Merge remote-tracking branch 'upstream/master' into air-injector-link-fix
@@ -1,50 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||
<CodeBlocks_project_file>
|
||||
<FileVersion major="1" minor="6" />
|
||||
<Project>
|
||||
<Option title="DLLSocket" />
|
||||
<Option pch_mode="2" />
|
||||
<Option compiler="gcc" />
|
||||
<Build>
|
||||
<Target title="Debug">
|
||||
<Option output="bin\Debug\DLLSocket" prefix_auto="1" extension_auto="1" />
|
||||
<Option object_output="obj\Debug\" />
|
||||
<Option type="3" />
|
||||
<Option compiler="gcc" />
|
||||
<Option createDefFile="1" />
|
||||
<Option createStaticLib="1" />
|
||||
<Compiler>
|
||||
<Add option="-Wall" />
|
||||
<Add option="-DBUILD_DLL" />
|
||||
<Add option="-g" />
|
||||
</Compiler>
|
||||
<Linker>
|
||||
<Add library="user32" />
|
||||
</Linker>
|
||||
</Target>
|
||||
<Target title="Release">
|
||||
<Option output="bin\Release\DLLSocket" prefix_auto="1" extension_auto="1" />
|
||||
<Option object_output="obj\Release\" />
|
||||
<Option type="3" />
|
||||
<Option compiler="gcc" />
|
||||
<Option createDefFile="1" />
|
||||
<Option createStaticLib="1" />
|
||||
<Compiler>
|
||||
<Add option="-Wall" />
|
||||
<Add option="-DBUILD_DLL" />
|
||||
<Add option="-O2" />
|
||||
</Compiler>
|
||||
<Linker>
|
||||
<Add option="-s" />
|
||||
<Add library="user32" />
|
||||
</Linker>
|
||||
</Target>
|
||||
</Build>
|
||||
<Unit filename="main.cpp" />
|
||||
<Unit filename="main.h" />
|
||||
<Extensions>
|
||||
<code_completion />
|
||||
<debugger />
|
||||
</Extensions>
|
||||
</Project>
|
||||
</CodeBlocks_project_file>
|
||||
@@ -1 +0,0 @@
|
||||
g++ -static -shared -O3 -fPIC main.cpp -o DLLSocket.so
|
||||
@@ -1,133 +0,0 @@
|
||||
// OS-specific networking includes
|
||||
// -------------------------------
|
||||
#ifdef __WIN32
|
||||
#include <winsock2.h>
|
||||
typedef int socklen_t;
|
||||
#else
|
||||
extern "C" {
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
}
|
||||
|
||||
typedef int SOCKET;
|
||||
typedef sockaddr_in SOCKADDR_IN;
|
||||
typedef sockaddr SOCKADDR;
|
||||
#define SOCKET_ERROR -1
|
||||
#endif
|
||||
|
||||
// Socket used for all communications
|
||||
SOCKET sock;
|
||||
|
||||
// Address of the remote server
|
||||
SOCKADDR_IN addr;
|
||||
|
||||
// Buffer used to return dynamic strings to the caller
|
||||
#define BUFFER_SIZE 1024
|
||||
char return_buffer[BUFFER_SIZE];
|
||||
|
||||
// exposed functions
|
||||
// ------------------------------
|
||||
|
||||
const char* SUCCESS = "1\0"; // string representing success
|
||||
|
||||
#ifdef __WIN32
|
||||
#define DLL_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define DLL_EXPORT __attribute__ ((visibility ("default")))
|
||||
#endif
|
||||
|
||||
// arg1: ip(in the xx.xx.xx.xx format)
|
||||
// arg2: port(a short)
|
||||
// return: NULL on failure, SUCCESS otherwise
|
||||
extern "C" DLL_EXPORT const char* establish_connection(int n, char *v[])
|
||||
{
|
||||
// extract args
|
||||
// ------------
|
||||
if(n < 2) return 0;
|
||||
const char* ip = v[0];
|
||||
const char* port_s = v[1];
|
||||
unsigned short port = atoi(port_s);
|
||||
|
||||
// set up network stuff
|
||||
// --------------------
|
||||
#ifdef __WIN32
|
||||
WSADATA wsa;
|
||||
WSAStartup(MAKEWORD(2,0),&wsa);
|
||||
#endif
|
||||
sock = socket(AF_INET,SOCK_DGRAM,0);
|
||||
|
||||
// make the socket non-blocking
|
||||
// ----------------------------
|
||||
#ifdef __WIN32
|
||||
unsigned long iMode=1;
|
||||
ioctlsocket(sock,FIONBIO,&iMode);
|
||||
#else
|
||||
fcntl(sock, F_SETFL, O_NONBLOCK);
|
||||
#endif
|
||||
|
||||
// establish a connection to the server
|
||||
// ------------------------------------
|
||||
memset(&addr,0,sizeof(SOCKADDR_IN));
|
||||
addr.sin_family=AF_INET;
|
||||
addr.sin_port=htons(port);
|
||||
|
||||
// convert the string representation of the ip to a byte representation
|
||||
addr.sin_addr.s_addr=inet_addr(ip);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
// arg1: string message to send
|
||||
// return: NULL on failure, SUCCESS otherwise
|
||||
extern "C" DLL_EXPORT const char* send_message(int n, char *v[])
|
||||
{
|
||||
// extract the args
|
||||
if(n < 1) return 0;
|
||||
const char* msg = v[0];
|
||||
|
||||
// send the message
|
||||
int rc = sendto(sock,msg,strlen(msg),0,(SOCKADDR*)&addr,sizeof(SOCKADDR));
|
||||
|
||||
// check for errors
|
||||
if (rc != -1) {
|
||||
return SUCCESS;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// no args
|
||||
// return: message if any received, NULL otherwise
|
||||
extern "C" DLL_EXPORT const char* recv_message(int n, char *v[])
|
||||
{
|
||||
SOCKADDR_IN sender; // we will store the sender address here
|
||||
|
||||
socklen_t sender_byte_length = sizeof(sender);
|
||||
|
||||
// Try receiving messages until we receive one that's valid, or there are no more messages
|
||||
while(1) {
|
||||
int rc = recvfrom(sock, return_buffer, BUFFER_SIZE,0,(SOCKADDR*) &sender,&sender_byte_length);
|
||||
if(rc > 0) {
|
||||
// we could read something
|
||||
|
||||
if(sender.sin_addr.s_addr != addr.sin_addr.s_addr) {
|
||||
continue; // not our connection, ignore and try again
|
||||
} else {
|
||||
return_buffer[rc] = 0; // 0-terminate the string
|
||||
return return_buffer;
|
||||
}
|
||||
}
|
||||
else {
|
||||
break; // no more messages, stop trying to receive
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
import subprocess
|
||||
import socket
|
||||
import urlparse
|
||||
|
||||
UDP_IP="127.0.0.1"
|
||||
UDP_PORT=8019
|
||||
|
||||
sock = socket.socket( socket.AF_INET, # Internet
|
||||
socket.SOCK_DGRAM ) # UDP
|
||||
sock.bind( (UDP_IP,UDP_PORT) )
|
||||
|
||||
last_ticker_state = None
|
||||
|
||||
def handle_message(data, addr):
|
||||
global last_ticker_state
|
||||
|
||||
params = urlparse.parse_qs(data)
|
||||
print(data)
|
||||
|
||||
try:
|
||||
if params["type"][0] == "log" and str(params["log"][0]) and str(params["message"][0]):
|
||||
open(params["log"][0],"a+").write(params["message"][0]+"\n")
|
||||
except IOError:
|
||||
pass
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
try:
|
||||
if params["type"][0] == "ticker_state" and str(params["message"][0]):
|
||||
last_ticker_state = str(params["message"][0])
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
try:
|
||||
if params["type"][0] == "startup" and last_ticker_state:
|
||||
open("crashlog.txt","a+").write("Server exited, last ticker state was: "+last_ticker_state+"\n")
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
sock.settimeout(60*6) # 10 minute timeout
|
||||
while True:
|
||||
try:
|
||||
data, addr = sock.recvfrom( 1024 ) # buffer size is 1024 bytes
|
||||
handle_message(data,addr)
|
||||
except socket.timeout:
|
||||
# try to start the server again
|
||||
print("Server timed out.. attempting restart.")
|
||||
if last_ticker_state:
|
||||
open("crashmsg.txt","a+").write("Server crashed, trying to reboot. last ticker state: "+last_ticker_state+"\n")
|
||||
subprocess.call("killall -9 DreamDaemon")
|
||||
subprocess.call("./start")
|
||||
@@ -1312,7 +1312,7 @@
|
||||
},
|
||||
/area/vox_station)
|
||||
"dA" = (
|
||||
/obj/vox/win_button,
|
||||
/obj/machinery/vox_win_button,
|
||||
/turf/unsimulated/floor{
|
||||
tag = "icon-light_on";
|
||||
icon_state = "light_on"
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
},
|
||||
/area/holodeck/source_wildlife)
|
||||
"ay" = (
|
||||
/obj/vox/win_button,
|
||||
/obj/machinery/vox_win_button,
|
||||
/turf/unsimulated/floor/vox{
|
||||
icon_state = "light_on"
|
||||
},
|
||||
|
||||
@@ -8,7 +8,6 @@ Pipelines and other atmospheric objects combine to form pipe_networks
|
||||
Pipes -> Pipelines
|
||||
Pipelines + Other Objects -> Pipe network
|
||||
*/
|
||||
GLOBAL_DATUM_INIT(pipe_icon_manager, /datum/pipe_icon_manager, new())
|
||||
/obj/machinery/atmospherics
|
||||
anchored = 1
|
||||
layer = GAS_PIPE_HIDDEN_LAYER //under wires
|
||||
@@ -51,6 +50,8 @@ GLOBAL_DATUM_INIT(pipe_icon_manager, /datum/pipe_icon_manager, new())
|
||||
/obj/machinery/atmospherics/proc/atmos_init()
|
||||
if(can_unwrench)
|
||||
stored = new(src, make_from = src)
|
||||
// Updates all pipe overlays and underlays
|
||||
update_underlays()
|
||||
|
||||
/obj/machinery/atmospherics/Destroy()
|
||||
QDEL_NULL(stored)
|
||||
@@ -80,14 +81,11 @@ GLOBAL_DATUM_INIT(pipe_icon_manager, /datum/pipe_icon_manager, new())
|
||||
pipe_image = image(src, loc, layer = ABOVE_HUD_LAYER, dir = dir) //the 20 puts it above Byond's darkness (not its opacity view)
|
||||
pipe_image.plane = HUD_PLANE
|
||||
|
||||
/obj/machinery/atmospherics/proc/check_icon_cache(var/safety = 0)
|
||||
if(!istype(GLOB.pipe_icon_manager))
|
||||
if(!safety) //to prevent infinite loops
|
||||
GLOB.pipe_icon_manager = new()
|
||||
check_icon_cache(1)
|
||||
return 0
|
||||
/obj/machinery/atmospherics/proc/check_icon_cache()
|
||||
if(!istype(SSair.icon_manager))
|
||||
return FALSE
|
||||
|
||||
return 1
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/atmospherics/proc/color_cache_name(var/obj/machinery/atmospherics/node)
|
||||
//Don't use this for standard pipes
|
||||
@@ -99,14 +97,14 @@ GLOBAL_DATUM_INIT(pipe_icon_manager, /datum/pipe_icon_manager, new())
|
||||
/obj/machinery/atmospherics/proc/add_underlay(var/turf/T, var/obj/machinery/atmospherics/node, var/direction, var/icon_connect_type)
|
||||
if(node)
|
||||
if(T.intact && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe))
|
||||
//underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay_down", direction, color_cache_name(node))
|
||||
underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "down" + icon_connect_type)
|
||||
//underlays += SSair.icon_manager.get_atmos_icon("underlay_down", direction, color_cache_name(node))
|
||||
underlays += SSair.icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "down" + icon_connect_type)
|
||||
else
|
||||
//underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay_intact", direction, color_cache_name(node))
|
||||
underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "intact" + icon_connect_type)
|
||||
//underlays += SSair.icon_manager.get_atmos_icon("underlay_intact", direction, color_cache_name(node))
|
||||
underlays += SSair.icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "intact" + icon_connect_type)
|
||||
else
|
||||
//underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay_exposed", direction, pipe_color)
|
||||
underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "exposed" + icon_connect_type)
|
||||
//underlays += SSair.icon_manager.get_atmos_icon("underlay_exposed", direction, pipe_color)
|
||||
underlays += SSair.icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "exposed" + icon_connect_type)
|
||||
|
||||
/obj/machinery/atmospherics/proc/update_underlays()
|
||||
if(check_icon_cache())
|
||||
@@ -343,11 +341,11 @@ GLOBAL_DATUM_INIT(pipe_icon_manager, /datum/pipe_icon_manager, new())
|
||||
/obj/machinery/atmospherics/proc/add_underlay_adapter(var/turf/T, var/obj/machinery/atmospherics/node, var/direction, var/icon_connect_type) //modified from add_underlay, does not make exposed underlays
|
||||
if(node)
|
||||
if(T.intact && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe))
|
||||
underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "down" + icon_connect_type)
|
||||
underlays += SSair.icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "down" + icon_connect_type)
|
||||
else
|
||||
underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "intact" + icon_connect_type)
|
||||
underlays += SSair.icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "intact" + icon_connect_type)
|
||||
else
|
||||
underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "retracted" + icon_connect_type)
|
||||
underlays += SSair.icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "retracted" + icon_connect_type)
|
||||
|
||||
/obj/machinery/atmospherics/singularity_pull(S, current_size)
|
||||
if(current_size >= STAGE_FIVE)
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
else
|
||||
vent_icon += "[on ? "[pump_direction ? "out" : "in"]" : "off"]"
|
||||
|
||||
overlays += GLOB.pipe_icon_manager.get_atmos_icon("device", , , vent_icon)
|
||||
overlays += SSair.icon_manager.get_atmos_icon("device", , , vent_icon)
|
||||
|
||||
/obj/machinery/atmospherics/binary/dp_vent_pump/update_underlays()
|
||||
if(..())
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
/// The amount of pressure the filter wants to operate at.
|
||||
var/target_pressure = ONE_ATMOSPHERE
|
||||
/// The type of gas we want to filter. Valid values that go here are from the `FILTER` defines at the top of the file.
|
||||
var/filter_type = FILTER_NOTHING
|
||||
var/filter_type = FILTER_TOXINS
|
||||
/// A list of available filter options. Used with `tgui_data`.
|
||||
var/list/filter_list = list(
|
||||
"Nothing" = FILTER_NOTHING,
|
||||
|
||||
@@ -102,7 +102,7 @@
|
||||
else
|
||||
vent_icon += "[on ? "[pump_direction ? "out" : "in"]" : "off"]"
|
||||
|
||||
overlays += GLOB.pipe_icon_manager.get_atmos_icon("device", , , vent_icon)
|
||||
overlays += SSair.icon_manager.get_atmos_icon("device", , , vent_icon)
|
||||
|
||||
update_pipe_image()
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@
|
||||
if(welded)
|
||||
scrubber_icon = "scrubberweld"
|
||||
|
||||
overlays += GLOB.pipe_icon_manager.get_atmos_icon("device", , , scrubber_icon)
|
||||
overlays += SSair.icon_manager.get_atmos_icon("device", , , scrubber_icon)
|
||||
update_pipe_image()
|
||||
|
||||
/obj/machinery/atmospherics/unary/vent_scrubber/update_underlays()
|
||||
|
||||
@@ -55,14 +55,14 @@
|
||||
|
||||
/obj/machinery/atmospherics/pipe/cap/update_icon(var/safety = 0)
|
||||
..()
|
||||
|
||||
|
||||
if(!check_icon_cache())
|
||||
return
|
||||
|
||||
alpha = 255
|
||||
|
||||
overlays.Cut()
|
||||
overlays += GLOB.pipe_icon_manager.get_atmos_icon("pipe", , pipe_color, "cap" + icon_connect_type)
|
||||
overlays += SSair.icon_manager.get_atmos_icon("pipe", , pipe_color, "cap" + icon_connect_type)
|
||||
|
||||
/obj/machinery/atmospherics/pipe/cap/atmos_init()
|
||||
..()
|
||||
|
||||
@@ -123,8 +123,8 @@
|
||||
alpha = 255
|
||||
|
||||
overlays.Cut()
|
||||
overlays += GLOB.pipe_icon_manager.get_atmos_icon("manifold", , pipe_color, "core" + icon_connect_type)
|
||||
overlays += GLOB.pipe_icon_manager.get_atmos_icon("manifold", , , "clamps" + icon_connect_type)
|
||||
overlays += SSair.icon_manager.get_atmos_icon("manifold", , pipe_color, "core" + icon_connect_type)
|
||||
overlays += SSair.icon_manager.get_atmos_icon("manifold", , , "clamps" + icon_connect_type)
|
||||
underlays.Cut()
|
||||
|
||||
var/turf/T = get_turf(src)
|
||||
|
||||
@@ -96,8 +96,8 @@
|
||||
|
||||
alpha = 255
|
||||
overlays.Cut()
|
||||
overlays += GLOB.pipe_icon_manager.get_atmos_icon("manifold", , pipe_color, "4way" + icon_connect_type)
|
||||
overlays += GLOB.pipe_icon_manager.get_atmos_icon("manifold", , , "clamps_4way" + icon_connect_type)
|
||||
overlays += SSair.icon_manager.get_atmos_icon("manifold", , pipe_color, "4way" + icon_connect_type)
|
||||
overlays += SSair.icon_manager.get_atmos_icon("manifold", , , "clamps_4way" + icon_connect_type)
|
||||
underlays.Cut()
|
||||
|
||||
var/turf/T = get_turf(src)
|
||||
|
||||
@@ -147,9 +147,9 @@
|
||||
overlays.Cut()
|
||||
|
||||
if(node1 && node2)
|
||||
overlays += GLOB.pipe_icon_manager.get_atmos_icon("pipe", , pipe_color, pipe_icon + "intact" + icon_connect_type)
|
||||
overlays += SSair.icon_manager.get_atmos_icon("pipe", , pipe_color, pipe_icon + "intact" + icon_connect_type)
|
||||
else
|
||||
overlays += GLOB.pipe_icon_manager.get_atmos_icon("pipe", , pipe_color, pipe_icon + "exposed[node1?1:0][node2?1:0]" + icon_connect_type)
|
||||
overlays += SSair.icon_manager.get_atmos_icon("pipe", , pipe_color, pipe_icon + "exposed[node1?1:0][node2?1:0]" + icon_connect_type)
|
||||
|
||||
// A check to make sure both nodes exist - self-delete if they aren't present
|
||||
/obj/machinery/atmospherics/pipe/simple/check_nodes_exist()
|
||||
|
||||
@@ -29,14 +29,14 @@
|
||||
|
||||
/obj/machinery/atmospherics/pipe/simple/hidden/universal/update_icon(var/safety = 0)
|
||||
..()
|
||||
|
||||
|
||||
if(!check_icon_cache())
|
||||
return
|
||||
|
||||
alpha = 255
|
||||
|
||||
overlays.Cut()
|
||||
overlays += GLOB.pipe_icon_manager.get_atmos_icon("pipe", , pipe_color, "universal")
|
||||
overlays += SSair.icon_manager.get_atmos_icon("pipe", , pipe_color, "universal")
|
||||
underlays.Cut()
|
||||
|
||||
if(node1)
|
||||
|
||||
@@ -40,14 +40,14 @@
|
||||
|
||||
/obj/machinery/atmospherics/pipe/simple/visible/universal/update_icon(var/safety = 0)
|
||||
..()
|
||||
|
||||
|
||||
if(!check_icon_cache())
|
||||
return
|
||||
|
||||
alpha = 255
|
||||
|
||||
overlays.Cut()
|
||||
overlays += GLOB.pipe_icon_manager.get_atmos_icon("pipe", , pipe_color, "universal")
|
||||
overlays += SSair.icon_manager.get_atmos_icon("pipe", , pipe_color, "universal")
|
||||
underlays.Cut()
|
||||
|
||||
if(node1)
|
||||
|
||||
@@ -429,10 +429,6 @@
|
||||
|
||||
// Filters
|
||||
#define FILTER_AMBIENT_OCCLUSION filter(type="drop_shadow", x=0, y=-2, size=4, color="#04080FAA")
|
||||
#define FILTER_EYE_BLUR filter(type="blur", size=0)
|
||||
|
||||
#define AMBIENT_OCCLUSION_FILTER_KEY "ambient occlusion"
|
||||
#define EYE_BLUR_FILTER_KEY "eye blur"
|
||||
|
||||
//Fullscreen overlay resolution in tiles.
|
||||
#define FULLSCREEN_OVERLAY_RESOLUTION_X 15
|
||||
|
||||
@@ -72,8 +72,6 @@
|
||||
to_chat(world, "<span class='warning'>pAI software module [P.name] has the same key as [O.name]!</span>")
|
||||
continue
|
||||
GLOB.pai_software_by_key[P.id] = P
|
||||
if(P.default)
|
||||
GLOB.default_pai_software[P.id] = P
|
||||
|
||||
// Setup loadout gear
|
||||
for(var/geartype in subtypesof(/datum/gear))
|
||||
|
||||
@@ -55,6 +55,4 @@ GLOBAL_LIST_INIT(cooking_recipes, list(RECIPE_MICROWAVE = list(), RECIPE_OVEN =
|
||||
GLOBAL_LIST_INIT(cooking_ingredients, list(RECIPE_MICROWAVE = list(), RECIPE_OVEN = list(), RECIPE_GRILL = list(), RECIPE_CANDY = list()))
|
||||
GLOBAL_LIST_INIT(cooking_reagents, list(RECIPE_MICROWAVE = list(), RECIPE_OVEN = list(), RECIPE_GRILL = list(), RECIPE_CANDY = list()))
|
||||
|
||||
GLOBAL_LIST(station_level_space_turfs)
|
||||
|
||||
#define EGG_LAYING_MESSAGES list("lays an egg.", "squats down and croons.", "begins making a huge racket.", "begins clucking raucously.")
|
||||
|
||||
@@ -99,6 +99,11 @@
|
||||
/obj/screen/fullscreen/impaired
|
||||
icon_state = "impairedoverlay"
|
||||
|
||||
/obj/screen/fullscreen/blurry
|
||||
icon = 'icons/mob/screen_gen.dmi'
|
||||
screen_loc = "WEST,SOUTH to EAST,NORTH"
|
||||
icon_state = "blurry"
|
||||
|
||||
/obj/screen/fullscreen/flash
|
||||
icon = 'icons/mob/screen_gen.dmi'
|
||||
screen_loc = "WEST,SOUTH to EAST,NORTH"
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
blend_mode = BLEND_OVERLAY
|
||||
var/show_alpha = 255
|
||||
var/hide_alpha = 0
|
||||
var/current_filters = list()
|
||||
|
||||
/obj/screen/plane_master/proc/Show(override)
|
||||
alpha = override || show_alpha
|
||||
@@ -13,21 +12,14 @@
|
||||
/obj/screen/plane_master/proc/Hide(override)
|
||||
alpha = override || hide_alpha
|
||||
|
||||
/obj/screen/plane_master/proc/add_filter(key, filter)
|
||||
if(current_filters[key])
|
||||
filters -= current_filters[key]
|
||||
/obj/screen/plane_master/proc/outline(_size, _color)
|
||||
filters += filter(type = "outline", size = _size, color = _color)
|
||||
|
||||
filters += filter
|
||||
current_filters[key] = filters[filters.len] //assigning `filter` will not allow it to work later while animating removal
|
||||
/obj/screen/plane_master/proc/shadow(_size, _border, _offset = 0, _x = 0, _y = 0, _color = "#04080FAA")
|
||||
filters += filter(type = "drop_shadow", x = _x, y = _y, color = _color, size = _size, offset = _offset)
|
||||
|
||||
/obj/screen/plane_master/proc/remove_filter(key)
|
||||
if(current_filters[key])
|
||||
animate(current_filters[key], size=0, time=5) //not all filters have a size param, but this is good enough for the general case
|
||||
addtimer(CALLBACK(src, .proc/remove_callback, key), 5)
|
||||
|
||||
/obj/screen/plane_master/proc/remove_callback(key)
|
||||
filters -= current_filters[key]
|
||||
current_filters -= key
|
||||
/obj/screen/plane_master/proc/clear_filters()
|
||||
filters = list()
|
||||
|
||||
//Why do plane masters need a backdrop sometimes? Read http://www.byond.com/forum/?post=2141928
|
||||
//Trust me, you need one. Period. If you don't think you do, you're doing something extremely wrong.
|
||||
@@ -46,8 +38,9 @@
|
||||
blend_mode = BLEND_OVERLAY
|
||||
|
||||
/obj/screen/plane_master/game_world/backdrop(mob/mymob)
|
||||
clear_filters()
|
||||
if(istype(mymob) && mymob.client && mymob.client.prefs && (mymob.client.prefs.toggles & PREFTOGGLE_AMBIENT_OCCLUSION))
|
||||
add_filter(AMBIENT_OCCLUSION_FILTER_KEY, FILTER_AMBIENT_OCCLUSION)
|
||||
filters += FILTER_AMBIENT_OCCLUSION
|
||||
|
||||
/obj/screen/plane_master/lighting
|
||||
name = "lighting plane master"
|
||||
|
||||
@@ -39,6 +39,9 @@ SUBSYSTEM_DEF(air)
|
||||
var/list/active_super_conductivity = list()
|
||||
var/list/high_pressure_delta = list()
|
||||
|
||||
/// Pipe overlay/underlay icon manager
|
||||
var/datum/pipe_icon_manager/icon_manager
|
||||
|
||||
|
||||
var/list/currentrun = list()
|
||||
var/currentpart = SSAIR_DEFERREDPIPENETS
|
||||
@@ -66,6 +69,7 @@ SUBSYSTEM_DEF(air)
|
||||
|
||||
/datum/controller/subsystem/air/Initialize(timeofday)
|
||||
setup_overlays() // Assign icons and such for gas-turf-overlays
|
||||
icon_manager = new() // Sets up icon manager for pipes
|
||||
setup_allturfs()
|
||||
setup_atmos_machinery(GLOB.machines)
|
||||
setup_pipenets(GLOB.machines)
|
||||
|
||||
@@ -245,7 +245,7 @@ SUBSYSTEM_DEF(changelog)
|
||||
if(config.wikiurl)
|
||||
if(alert("This will open the wiki in your browser. Are you sure?",,"Yes","No")=="No")
|
||||
return
|
||||
usr.client.wiki("") // Blank arg is important here
|
||||
usr.client.wiki()
|
||||
else
|
||||
to_chat(usr, "<span class='danger'>The Wiki URL is not set in the server configuration. Please inform the server host.</span>")
|
||||
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
var/icon_icon = 'icons/mob/actions/actions.dmi'
|
||||
var/button_icon_state = "default"
|
||||
var/mob/owner
|
||||
/// Whether to darken the button or not if [/datum/action/proc/IsAvailable] return FALSE.
|
||||
var/darken_when_unavailable = TRUE
|
||||
|
||||
/datum/action/New(var/Target)
|
||||
target = Target
|
||||
@@ -99,17 +97,18 @@
|
||||
|
||||
// If the action isn't available, darken the button
|
||||
if(!IsAvailable())
|
||||
if(!darken_when_unavailable)
|
||||
return
|
||||
var/image/img = image('icons/mob/screen_white.dmi', icon_state = "template")
|
||||
img.alpha = 200
|
||||
img.appearance_flags = RESET_COLOR | RESET_ALPHA
|
||||
img.color = "#000000"
|
||||
img.plane = FLOAT_PLANE + 1
|
||||
button.overlays += img
|
||||
apply_unavailable_effect()
|
||||
else
|
||||
return TRUE
|
||||
|
||||
/datum/action/proc/apply_unavailable_effect()
|
||||
var/image/img = image('icons/mob/screen_white.dmi', icon_state = "template")
|
||||
img.alpha = 200
|
||||
img.appearance_flags = RESET_COLOR | RESET_ALPHA
|
||||
img.color = "#000000"
|
||||
img.plane = FLOAT_PLANE + 1
|
||||
button.add_overlay(img)
|
||||
|
||||
/datum/action/proc/ApplyIcon(obj/screen/movable/action_button/current_button)
|
||||
current_button.cut_overlays()
|
||||
if(icon_icon && button_icon_state)
|
||||
@@ -117,7 +116,7 @@
|
||||
img.appearance_flags = RESET_COLOR | RESET_ALPHA
|
||||
img.pixel_x = 0
|
||||
img.pixel_y = 0
|
||||
current_button.overlays += img
|
||||
current_button.add_overlay(img)
|
||||
|
||||
//Presets for item actions
|
||||
/datum/action/item_action
|
||||
@@ -476,7 +475,6 @@
|
||||
/datum/action/spell_action
|
||||
check_flags = 0
|
||||
background_icon_state = "bg_spell"
|
||||
darken_when_unavailable = FALSE
|
||||
var/recharge_text_color = "#FFFFFF"
|
||||
|
||||
/datum/action/spell_action/New(Target)
|
||||
@@ -515,26 +513,28 @@
|
||||
return spell.can_cast(owner)
|
||||
return FALSE
|
||||
|
||||
/datum/action/spell_action/UpdateButtonIcon()
|
||||
if(button && !(. = ..()))
|
||||
var/obj/effect/proc_holder/spell/S = target
|
||||
if(!istype(S))
|
||||
return
|
||||
var/progress = S.get_availability_percentage()
|
||||
var/col_val_high = 72 * progress + 128
|
||||
var/col_val_low = 200 * progress
|
||||
button.maptext = "<div style=\"font-size:6pt;color:[recharge_text_color];font:'Small Fonts';text-align:center;\" valign=\"bottom\">[round_down(progress * 100)]%</div>"
|
||||
button.color = rgb(col_val_high, col_val_low, col_val_low, col_val_high)
|
||||
else
|
||||
button.maptext = null
|
||||
/datum/action/spell_action/apply_unavailable_effect()
|
||||
var/obj/effect/proc_holder/spell/S = target
|
||||
if(!istype(S))
|
||||
return ..()
|
||||
var/progress = S.get_availability_percentage()
|
||||
if(progress == 1)
|
||||
return ..() // This means that the spell is charged but unavailable due to something else
|
||||
|
||||
var/alpha = 220 - 140 * progress
|
||||
|
||||
var/image/img = image('icons/mob/screen_white.dmi', icon_state = "template")
|
||||
img.alpha = alpha
|
||||
img.appearance_flags = RESET_COLOR | RESET_ALPHA
|
||||
img.color = "#000000"
|
||||
img.plane = FLOAT_PLANE + 1
|
||||
button.add_overlay(img)
|
||||
// Make a holder for the charge text
|
||||
var/image/count_down_holder = image('icons/effects/effects.dmi', icon_state = "nothing")
|
||||
count_down_holder.plane = FLOAT_PLANE + 1.1
|
||||
count_down_holder.maptext = "<div style=\"font-size:6pt;color:[recharge_text_color];font:'Small Fonts';text-align:center;\" valign=\"bottom\">[round_down(progress * 100)]%</div>"
|
||||
button.add_overlay(count_down_holder)
|
||||
|
||||
/datum/action/spell_action/ApplyIcon(obj/screen/movable/action_button/current_button)
|
||||
current_button.cut_overlays()
|
||||
if(icon_icon && button_icon_state)
|
||||
var/image/img = image(icon_icon, current_button, button_icon_state)
|
||||
img.pixel_x = 0
|
||||
img.pixel_y = 0
|
||||
current_button.overlays += img
|
||||
/*
|
||||
/datum/action/spell_action/alien
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
GLOBAL_DATUM_INIT(air_alarm_repository, /datum/repository/air_alarm, new())
|
||||
|
||||
/datum/repository/air_alarm/proc/air_alarm_data(var/list/monitored_alarms, var/refresh = 0, var/obj/machinery/alarm/passed_alarm)
|
||||
var/alarms[0]
|
||||
/datum/repository/air_alarm/proc/air_alarm_data(list/monitored_alarms, refresh = FALSE, obj/machinery/alarm/passed_alarm)
|
||||
var/list/alarms = list()
|
||||
|
||||
var/datum/cache_entry/cache_entry = cache_data
|
||||
if(!cache_entry)
|
||||
@@ -16,20 +16,20 @@ GLOBAL_DATUM_INIT(air_alarm_repository, /datum/repository/air_alarm, new())
|
||||
if(SSticker && SSticker.current_state < GAME_STATE_PLAYING && istype(passed_alarm)) // Generating the list for the first time as the game hasn't started - no need to run through the machines list everything every time
|
||||
alarms = cache_entry.data // Don't deleate the list
|
||||
if(is_station_contact(passed_alarm.z) && passed_alarm.remote_control) // Still need sanity checks
|
||||
alarms[++alarms.len] = passed_alarm.get_nano_data_console()
|
||||
alarms[++alarms.len] = passed_alarm.get_console_data()
|
||||
else
|
||||
for(var/obj/machinery/alarm/alarm in (monitored_alarms ? monitored_alarms : GLOB.air_alarms)) // Generating the whole list again is a bad habit but I can't be bothered to fix it right now
|
||||
if(!monitored_alarms && !is_station_contact(alarm.z))
|
||||
continue
|
||||
if(!alarm.remote_control)
|
||||
continue
|
||||
alarms[++alarms.len] = alarm.get_nano_data_console()
|
||||
alarms[++alarms.len] = alarm.get_console_data()
|
||||
|
||||
cache_entry.timestamp = world.time //+ 10 SECONDS
|
||||
cache_entry.data = alarms
|
||||
return alarms
|
||||
|
||||
/datum/repository/air_alarm/proc/update_cache(var/obj/machinery/alarm/alarm)
|
||||
/datum/repository/air_alarm/proc/update_cache(obj/machinery/alarm/alarm)
|
||||
return air_alarm_data(refresh = 1, passed_alarm = alarm)
|
||||
|
||||
#undef AIR_ALARM_DATA_CACHE_DURATION
|
||||
|
||||
@@ -322,6 +322,8 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell))
|
||||
if("recharge")
|
||||
if(charge_counter == 0)
|
||||
return 0
|
||||
if(charge_max == 0)
|
||||
return 1
|
||||
return charge_counter / charge_max
|
||||
if("charges")
|
||||
if(charge_counter)
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
include_user = 1
|
||||
human_req = 1
|
||||
|
||||
action_icon_state = "mime"
|
||||
action_icon_state = "mime_silence"
|
||||
action_background_icon_state = "bg_mime"
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/mime/speak/Click()
|
||||
@@ -77,7 +77,7 @@
|
||||
range = -1
|
||||
include_user = TRUE
|
||||
|
||||
action_icon_state = "mime"
|
||||
action_icon_state = "mime_bigwall"
|
||||
action_background_icon_state = "bg_mime"
|
||||
large = TRUE
|
||||
|
||||
|
||||
@@ -219,13 +219,15 @@
|
||||
owner.adjustBruteLoss(-25)
|
||||
owner.adjustFireLoss(-25)
|
||||
owner.remove_CC()
|
||||
owner.bodytemperature = BODYTEMP_NORMAL
|
||||
if(ishuman(owner))
|
||||
var/mob/living/carbon/human/H = owner
|
||||
H.bodytemperature = H.dna.species.body_temperature
|
||||
for(var/thing in H.bodyparts)
|
||||
var/obj/item/organ/external/E = thing
|
||||
E.internal_bleeding = FALSE
|
||||
E.mend_fracture()
|
||||
else
|
||||
owner.bodytemperature = BODYTEMP_NORMAL
|
||||
return TRUE
|
||||
|
||||
/datum/status_effect/regenerative_core/on_remove()
|
||||
|
||||
@@ -1802,6 +1802,16 @@ GLOBAL_LIST_INIT(all_supply_groups, list(SUPPLY_EMERGENCY,SUPPLY_SECURITY,SUPPLY
|
||||
)
|
||||
containername = "bluespace artillery parts crate"
|
||||
|
||||
|
||||
/datum/supply_packs/misc/station_goal/bluespace_tap
|
||||
name = "Bluespace Harvester Parts"
|
||||
cost = 150
|
||||
contains = list(
|
||||
/obj/item/circuitboard/machine/bluespace_tap,
|
||||
/obj/item/paper/bluespace_tap
|
||||
)
|
||||
containername = "bluespace harvester parts crate"
|
||||
|
||||
/datum/supply_packs/misc/station_goal/dna_vault
|
||||
name = "DNA Vault Parts"
|
||||
cost = 120
|
||||
|
||||
@@ -1238,7 +1238,6 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
|
||||
/area/medical/psych
|
||||
name = "\improper Psych Room"
|
||||
icon_state = "medbaypsych"
|
||||
ambientsounds = list('sound/ambience/aurora_caelus_short.ogg')
|
||||
|
||||
/area/medical/medbreak
|
||||
name = "\improper Break Room"
|
||||
@@ -287,17 +287,17 @@ GLOBAL_LIST_EMPTY(cortical_stacks) //Stacks for 'leave nobody behind' objective.
|
||||
return ..()
|
||||
|
||||
|
||||
/obj/vox/win_button
|
||||
/obj/machinery/vox_win_button
|
||||
name = "shoal contact computer"
|
||||
desc = "Used to contact the Vox Shoal, generally to arrange for pickup."
|
||||
icon = 'icons/obj/computer.dmi'
|
||||
icon_state = "tcstation"
|
||||
|
||||
/obj/vox/win_button/New()
|
||||
/obj/machinery/vox_win_button/New()
|
||||
. = ..()
|
||||
overlays += icon('icons/obj/computer.dmi', "syndie")
|
||||
|
||||
/obj/vox/win_button/attack_hand(mob/user)
|
||||
/obj/machinery/vox_win_button/attack_hand(mob/user)
|
||||
if(!GAMEMODE_IS_HEIST || (world.time < 10 MINUTES)) //has to be heist, and at least ten minutes into the round
|
||||
to_chat(user, "<span class='warning'>\The [src] does not appear to have a connection.</span>")
|
||||
return 0
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
#define EMPOWERED_THRALL_LIMIT 5
|
||||
|
||||
/obj/effect/proc_holder/spell/proc/shadowling_check(var/mob/living/carbon/human/H)
|
||||
if(!H || !istype(H)) return
|
||||
if(!H || !istype(H))
|
||||
return
|
||||
if(H.incorporeal_move == 1)
|
||||
to_chat(usr, "<span class='warning'>You can't use abilities affecting others while you are traversing between worlds!</span>")
|
||||
to_chat(H, "<span class='warning'>You can't use abilities affecting others while you are traversing between worlds!</span>")
|
||||
return FALSE
|
||||
if(isshadowling(H) && is_shadow(H))
|
||||
return 1
|
||||
if(isshadowlinglesser(H) && is_thrall(H))
|
||||
return 1
|
||||
if(!is_shadow_or_thrall(usr))
|
||||
to_chat(usr, "<span class='warning'>You can't wrap your head around how to do this.</span>")
|
||||
else if(is_thrall(usr))
|
||||
to_chat(usr, "<span class='warning'>You aren't powerful enough to do this.</span>")
|
||||
else if(is_shadow(usr))
|
||||
to_chat(usr, "<span class='warning'>Your telepathic ability is suppressed. Hatch or use Rapid Re-Hatch first.</span>")
|
||||
if(!is_shadow_or_thrall(H))
|
||||
to_chat(H, "<span class='warning'>You can't wrap your head around how to do this.</span>")
|
||||
else if(is_thrall(H))
|
||||
to_chat(H, "<span class='warning'>You aren't powerful enough to do this.</span>")
|
||||
else if(is_shadow(H))
|
||||
to_chat(H, "<span class='warning'>Your telepathic ability is suppressed. Hatch or use Rapid Re-Hatch first.</span>")
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
@@ -44,12 +44,6 @@
|
||||
#define AALARM_PRESET_COLDROOM 3 // Kitchen coldroom
|
||||
#define AALARM_PRESET_SERVER 4 // Server coldroom
|
||||
|
||||
#define AALARM_SCREEN_MAIN 1
|
||||
#define AALARM_SCREEN_VENT 2
|
||||
#define AALARM_SCREEN_SCRUB 3
|
||||
#define AALARM_SCREEN_MODE 4
|
||||
#define AALARM_SCREEN_SENSORS 5
|
||||
|
||||
#define AALARM_REPORT_TIMEOUT 100
|
||||
|
||||
#define RCON_NO 1
|
||||
@@ -109,7 +103,6 @@
|
||||
|
||||
var/mode = AALARM_MODE_SCRUBBING
|
||||
var/preset = AALARM_PRESET_HUMAN
|
||||
var/screen = AALARM_SCREEN_MAIN
|
||||
var/area_uid
|
||||
var/area/alarm_area
|
||||
var/danger_level = ATMOS_ALARM_NONE
|
||||
@@ -375,7 +368,7 @@
|
||||
if(ATMOS_ALARM_DANGER)
|
||||
icon_state = "alarm1"
|
||||
|
||||
/obj/machinery/alarm/proc/register_env_machine(var/m_id, var/device_type)
|
||||
/obj/machinery/alarm/proc/register_env_machine(m_id, device_type)
|
||||
var/new_name
|
||||
if(device_type=="AVP")
|
||||
new_name = "[alarm_area.name] Vent Pump #[alarm_area.air_vent_names.len+1]"
|
||||
@@ -405,7 +398,7 @@
|
||||
frequency = new_frequency
|
||||
radio_connection = SSradio.add_object(src, frequency, RADIO_TO_AIRALARM)
|
||||
|
||||
/obj/machinery/alarm/proc/send_signal(var/target, var/list/command)//sends signal 'command' to 'target'. Returns 0 if no radio connection, 1 otherwise
|
||||
/obj/machinery/alarm/proc/send_signal(target, list/command)//sends signal 'command' to 'target'. Returns 0 if no radio connection, 1 otherwise
|
||||
if(!radio_connection)
|
||||
return 0
|
||||
|
||||
@@ -418,9 +411,7 @@
|
||||
signal.data["sigtype"] = "command"
|
||||
|
||||
radio_connection.post_signal(src, signal, RADIO_FROM_AIRALARM)
|
||||
// to_chat(world, text("Signal [] Broadcasted to []", command, target))
|
||||
|
||||
return 1
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/alarm/proc/apply_mode()
|
||||
switch(mode)
|
||||
@@ -571,10 +562,13 @@
|
||||
///////////////
|
||||
|
||||
/obj/machinery/alarm/attack_ai(mob/user)
|
||||
src.add_hiddenprint(user)
|
||||
return ui_interact(user)
|
||||
if(buildstage != 2)
|
||||
return
|
||||
|
||||
/obj/machinery/alarm/attack_ghost(user as mob)
|
||||
add_hiddenprint(user)
|
||||
return tgui_interact(user)
|
||||
|
||||
/obj/machinery/alarm/attack_ghost(mob/user)
|
||||
return interact(user)
|
||||
|
||||
/obj/machinery/alarm/attack_hand(mob/user)
|
||||
@@ -591,7 +585,7 @@
|
||||
wires.Interact(user)
|
||||
|
||||
if(!shorted)
|
||||
ui_interact(user)
|
||||
tgui_interact(user)
|
||||
|
||||
/obj/machinery/alarm/proc/ui_air_status()
|
||||
var/turf/location = get_turf(src)
|
||||
@@ -600,7 +594,7 @@
|
||||
|
||||
var/datum/gas_mixture/environment = location.return_air()
|
||||
var/total = environment.oxygen + environment.nitrogen + environment.carbon_dioxide + environment.toxins
|
||||
if(total==0)
|
||||
if(total == 0)
|
||||
return null
|
||||
|
||||
var/datum/tlv/cur_tlv
|
||||
@@ -633,12 +627,12 @@
|
||||
cur_tlv = TLV["temperature"]
|
||||
var/temperature_dangerlevel = cur_tlv.get_danger_level(environment.temperature)
|
||||
|
||||
var/data[0]
|
||||
var/list/data = list()
|
||||
data["pressure"] = environment_pressure
|
||||
data["temperature"] = environment.temperature
|
||||
data["temperature_c"] = round(environment.temperature - T0C, 0.1)
|
||||
|
||||
var/percentages[0]
|
||||
var/list/percentages = list()
|
||||
percentages["oxygen"] = oxygen_percent
|
||||
percentages["nitrogen"] = nitrogen_percent
|
||||
percentages["co2"] = co2_percent
|
||||
@@ -646,7 +640,7 @@
|
||||
percentages["other"] = other_moles
|
||||
data["contents"] = percentages
|
||||
|
||||
var/danger[0]
|
||||
var/list/danger = list()
|
||||
danger["pressure"] = pressure_dangerlevel
|
||||
danger["temperature"] = temperature_dangerlevel
|
||||
danger["oxygen"] = oxygen_dangerlevel
|
||||
@@ -658,13 +652,12 @@
|
||||
data["danger"] = danger
|
||||
return data
|
||||
|
||||
/obj/machinery/alarm/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
|
||||
var/data[0]
|
||||
/obj/machinery/alarm/proc/has_rcon_access(mob/user)
|
||||
return user && (isAI(user) || allowed(user) || emagged || rcon_setting == RCON_YES)
|
||||
|
||||
var/list/href_list = state.href_list(user)
|
||||
if(href_list)
|
||||
data["remote_connection"] = href_list["remote_connection"]
|
||||
data["remote_access"] = href_list["remote_access"]
|
||||
// Intentional nulls here
|
||||
/obj/machinery/alarm/tgui_data(mob/user)
|
||||
var/list/data = list()
|
||||
|
||||
data["name"] = sanitize(name)
|
||||
data["air"] = ui_air_status()
|
||||
@@ -674,70 +667,70 @@
|
||||
// Locked when:
|
||||
// Not sent from atmos console AND
|
||||
// Not silicon AND locked.
|
||||
data["locked"] = !is_authenticated(user, href_list)
|
||||
var/datum/tgui/active_ui = SStgui.get_open_ui(user, src, "main")
|
||||
data["locked"] = !is_authenticated(user, active_ui)
|
||||
data["rcon"] = rcon_setting
|
||||
data["target_temp"] = target_temperature - T0C
|
||||
data["atmos_alarm"] = alarm_area.atmosalm
|
||||
data["emagged"] = emagged
|
||||
data["modes"] = list(
|
||||
AALARM_MODE_SCRUBBING = list("name"="Filtering", "desc"="Scrubs out contaminants"),\
|
||||
AALARM_MODE_VENTING = list("name"="Draught", "desc"="Siphons out air while replacing"),\
|
||||
AALARM_MODE_PANIC = list("name"="Panic Siphon","desc"="Siphons air out of the room quickly"),\
|
||||
AALARM_MODE_REPLACEMENT = list("name"="Cycle", "desc"="Siphons air before replacing"),\
|
||||
AALARM_MODE_SIPHON = list("name"="Siphon", "desc"="Siphons air out of the room"),\
|
||||
AALARM_MODE_CONTAMINATED= list("name"="Contaminated","desc"="Scrubs out all contaminants quickly"),\
|
||||
AALARM_MODE_REFILL = list("name"="Refill", "desc"="Triples vent output"),\
|
||||
AALARM_MODE_OFF = list("name"="Off", "desc"="Shuts off vents and scrubbers"),\
|
||||
AALARM_MODE_FLOOD = list("name"="Flood", "desc"="Shuts off scrubbers and opens vents", "emagonly" = 1)
|
||||
AALARM_MODE_SCRUBBING = list("name"="Filtering", "desc"="Scrubs out contaminants", "id" = AALARM_MODE_SCRUBBING),\
|
||||
AALARM_MODE_VENTING = list("name"="Draught", "desc"="Siphons out air while replacing", "id" = AALARM_MODE_VENTING),\
|
||||
AALARM_MODE_PANIC = list("name"="Panic Siphon","desc"="Siphons air out of the room quickly", "id" = AALARM_MODE_PANIC),\
|
||||
AALARM_MODE_REPLACEMENT = list("name"="Cycle", "desc"="Siphons air before replacing", "id" = AALARM_MODE_REPLACEMENT),\
|
||||
AALARM_MODE_SIPHON = list("name"="Siphon", "desc"="Siphons air out of the room", "id" = AALARM_MODE_SIPHON),\
|
||||
AALARM_MODE_CONTAMINATED= list("name"="Contaminated","desc"="Scrubs out all contaminants quickly", "id" = AALARM_MODE_CONTAMINATED),\
|
||||
AALARM_MODE_REFILL = list("name"="Refill", "desc"="Triples vent output", "id" = AALARM_MODE_REFILL),\
|
||||
AALARM_MODE_OFF = list("name"="Off", "desc"="Shuts off vents and scrubbers", "id" = AALARM_MODE_OFF),\
|
||||
AALARM_MODE_FLOOD = list("name"="Flood", "desc"="Shuts off scrubbers and opens vents", "emagonly" = TRUE, "id" = AALARM_MODE_FLOOD)
|
||||
)
|
||||
data["mode"] = mode
|
||||
data["presets"] = list(
|
||||
AALARM_PRESET_HUMAN = list("name"="Human", "desc"="Checks for oxygen and nitrogen"),\
|
||||
AALARM_PRESET_VOX = list("name"="Vox", "desc"="Checks for nitrogen only"),\
|
||||
AALARM_PRESET_COLDROOM = list("name"="Coldroom", "desc"="For freezers"),\
|
||||
AALARM_PRESET_SERVER = list("name"="Server Room", "desc"="For server rooms")
|
||||
AALARM_PRESET_HUMAN = list("name"="Human", "desc"="Checks for oxygen and nitrogen", "id" = AALARM_PRESET_HUMAN),\
|
||||
AALARM_PRESET_VOX = list("name"="Vox", "desc"="Checks for nitrogen only", "id" = AALARM_PRESET_VOX),\
|
||||
AALARM_PRESET_COLDROOM = list("name"="Coldroom", "desc"="For freezers", "id" = AALARM_PRESET_COLDROOM),\
|
||||
AALARM_PRESET_SERVER = list("name"="Server Room", "desc"="For server rooms", "id" = AALARM_PRESET_SERVER)
|
||||
)
|
||||
data["preset"] = preset
|
||||
data["screen"] = screen
|
||||
|
||||
var/list/vents=list()
|
||||
var/list/vents = list()
|
||||
if(alarm_area.air_vent_names.len)
|
||||
for(var/id_tag in alarm_area.air_vent_names)
|
||||
var/vent_info[0]
|
||||
var/list/vent_info = list()
|
||||
var/long_name = alarm_area.air_vent_names[id_tag]
|
||||
var/list/vent_data = alarm_area.air_vent_info[id_tag]
|
||||
if(!vent_data)
|
||||
continue
|
||||
vent_info["id_tag"]=id_tag
|
||||
vent_info["name"]=sanitize(long_name)
|
||||
vent_info["id_tag"] = id_tag
|
||||
vent_info["name"] = sanitize(long_name)
|
||||
vent_info += vent_data
|
||||
vents+=list(vent_info)
|
||||
data["vents"]=vents
|
||||
vents += list(vent_info)
|
||||
data["vents"] = vents
|
||||
|
||||
var/list/scrubbers=list()
|
||||
var/list/scrubbers = list()
|
||||
if(alarm_area.air_scrub_names.len)
|
||||
for(var/id_tag in alarm_area.air_scrub_names)
|
||||
var/long_name = alarm_area.air_scrub_names[id_tag]
|
||||
var/list/scrubber_data = alarm_area.air_scrub_info[id_tag]
|
||||
if(!scrubber_data)
|
||||
continue
|
||||
scrubber_data["id_tag"]=id_tag
|
||||
scrubber_data["name"]=sanitize(long_name)
|
||||
scrubbers+=list(scrubber_data)
|
||||
data["scrubbers"]=scrubbers
|
||||
scrubber_data["id_tag"] = id_tag
|
||||
scrubber_data["name"] = sanitize(long_name)
|
||||
scrubbers += list(scrubber_data)
|
||||
data["scrubbers"] = scrubbers
|
||||
return data
|
||||
|
||||
/obj/machinery/alarm/proc/get_nano_data_console(mob/user)
|
||||
var/data[0]
|
||||
/obj/machinery/alarm/proc/get_console_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["name"] = sanitize(name)
|
||||
data["ref"] = "\ref[src]"
|
||||
data["danger"] = max(danger_level, alarm_area.atmosalm)
|
||||
var/area/Area = get_area(src)
|
||||
data["area"] = sanitize(Area.name)
|
||||
var/turf/pos = get_turf(src)
|
||||
data["x"] = pos.x
|
||||
data["y"] = pos.y
|
||||
data["z"] = pos.z
|
||||
var/area/A = get_area(src)
|
||||
data["area"] = sanitize(A.name)
|
||||
var/turf/T = get_turf(src)
|
||||
data["x"] = T.x
|
||||
data["y"] = T.y
|
||||
data["z"] = T.z
|
||||
return data
|
||||
|
||||
/obj/machinery/alarm/proc/generate_thresholds_menu()
|
||||
@@ -745,9 +738,9 @@
|
||||
var/list/thresholds = list()
|
||||
|
||||
var/list/gas_names = list(
|
||||
"oxygen" = "O<sub>2</sub>",
|
||||
"nitrogen" = "N<sub>2</sub>",
|
||||
"carbon dioxide" = "CO<sub>2</sub>",
|
||||
"oxygen" = "O2",
|
||||
"nitrogen" = "N2",
|
||||
"carbon dioxide" = "CO2",
|
||||
"plasma" = "Toxin",
|
||||
"other" = "Other")
|
||||
for(var/g in gas_names)
|
||||
@@ -774,28 +767,24 @@
|
||||
|
||||
return thresholds
|
||||
|
||||
/obj/machinery/alarm/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = GLOB.default_state)
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
/obj/machinery/alarm/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "air_alarm.tmpl", name, 570, 410, state = state)
|
||||
ui = new(user, src, ui_key, "AirAlarm", name, 570, 410, master_ui, state)
|
||||
ui.open()
|
||||
ui.set_auto_update(1)
|
||||
|
||||
/obj/machinery/alarm/proc/is_authenticated(var/mob/user, href_list)
|
||||
/obj/machinery/alarm/proc/is_authenticated(mob/user, datum/tgui/ui=null)
|
||||
// Return true if they are connecting with a remote console
|
||||
if(istype(ui?.master_ui?.src_object.type, /datum/tgui_module/atmos_control))
|
||||
return TRUE
|
||||
if(user.can_admin_interact())
|
||||
return 1
|
||||
else if(isAI(user) || isrobot(user) || emagged || is_auth_rcon(href_list))
|
||||
return 1
|
||||
return TRUE
|
||||
else if(isAI(user) || isrobot(user) || emagged)
|
||||
return TRUE
|
||||
else
|
||||
return !locked
|
||||
|
||||
/obj/machinery/alarm/proc/is_auth_rcon(href_list)
|
||||
if(href_list && href_list["remote_connection"] && href_list["remote_access"])
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
|
||||
/obj/machinery/alarm/CanUseTopic(var/mob/user, var/datum/topic_state/state, var/href_list = list())
|
||||
/obj/machinery/alarm/tgui_status(mob/user, datum/tgui_state/state)
|
||||
if(buildstage != 2)
|
||||
return STATUS_CLOSE
|
||||
|
||||
@@ -805,146 +794,138 @@
|
||||
|
||||
. = shorted ? STATUS_DISABLED : STATUS_INTERACTIVE
|
||||
|
||||
if(. == STATUS_INTERACTIVE)
|
||||
var/extra_href = state.href_list(usr)
|
||||
// Prevent remote users from altering RCON settings or activating atmos alarms unless they already have access
|
||||
if((href_list["atmos_alarm"] || href_list["rcon"]) && extra_href["remote_connection"] && !extra_href["remote_access"])
|
||||
. = STATUS_UPDATE
|
||||
return min(..(), .)
|
||||
|
||||
/obj/machinery/alarm/Topic(href, href_list, var/nowindow = 0, var/datum/topic_state/state)
|
||||
if(..(href, href_list, nowindow, state))
|
||||
return 1
|
||||
|
||||
var/state_href = state.href_list(usr)
|
||||
|
||||
if(href_list["rcon"])
|
||||
var/attempted_rcon_setting = text2num(href_list["rcon"])
|
||||
switch(attempted_rcon_setting)
|
||||
if(RCON_NO)
|
||||
rcon_setting = RCON_NO
|
||||
if(RCON_AUTO)
|
||||
rcon_setting = RCON_AUTO
|
||||
if(RCON_YES)
|
||||
rcon_setting = RCON_YES
|
||||
return 1
|
||||
// TODO: Refactor these utter pieces of garbage
|
||||
/obj/machinery/alarm/tgui_act(action, list/params)
|
||||
if(..())
|
||||
return
|
||||
|
||||
add_fingerprint(usr)
|
||||
|
||||
if(href_list["command"])
|
||||
if(!is_authenticated(usr, state_href))
|
||||
return
|
||||
. = TRUE
|
||||
|
||||
var/device_id = href_list["id_tag"]
|
||||
switch(href_list["command"])
|
||||
if( "power",
|
||||
"adjust_external_pressure",
|
||||
"set_external_pressure",
|
||||
"checks",
|
||||
"co2_scrub",
|
||||
"tox_scrub",
|
||||
"n2o_scrub",
|
||||
"n2_scrub",
|
||||
"o2_scrub",
|
||||
"widenet",
|
||||
"scrubbing",
|
||||
"direction")
|
||||
var/val
|
||||
if(href_list["val"])
|
||||
val=text2num(href_list["val"])
|
||||
else
|
||||
var/newval = input("Enter new value") as num|null
|
||||
if(isnull(newval))
|
||||
// Used for rcon auth
|
||||
var/datum/tgui/active_ui = SStgui.get_open_ui(usr, src, "main")
|
||||
|
||||
switch(action)
|
||||
if("set_rcon")
|
||||
var/attempted_rcon_setting = text2num(params["rcon"])
|
||||
switch(attempted_rcon_setting)
|
||||
if(RCON_NO)
|
||||
rcon_setting = RCON_NO
|
||||
if(RCON_AUTO)
|
||||
rcon_setting = RCON_AUTO
|
||||
if(RCON_YES)
|
||||
rcon_setting = RCON_YES
|
||||
|
||||
|
||||
if("command")
|
||||
if(!is_authenticated(usr, active_ui))
|
||||
return
|
||||
|
||||
var/device_id = params["id_tag"]
|
||||
switch(params["cmd"])
|
||||
if ("power",
|
||||
"adjust_external_pressure",
|
||||
"set_external_pressure",
|
||||
"checks",
|
||||
"co2_scrub",
|
||||
"tox_scrub",
|
||||
"n2o_scrub",
|
||||
"n2_scrub",
|
||||
"o2_scrub",
|
||||
"widenet",
|
||||
"scrubbing",
|
||||
"direction")
|
||||
var/val
|
||||
if(params["val"])
|
||||
val=text2num(params["val"])
|
||||
else
|
||||
var/newval = input("Enter new value") as num|null
|
||||
if(isnull(newval))
|
||||
return
|
||||
if(params["cmd"] == "set_external_pressure")
|
||||
if(newval > 1000 + ONE_ATMOSPHERE)
|
||||
newval = 1000 + ONE_ATMOSPHERE
|
||||
if(newval < 0)
|
||||
newval = 0
|
||||
val = newval
|
||||
|
||||
// For those who read this: This radio BS is what makes air alarms take 10 years to update in the UI
|
||||
send_signal(device_id, list(params["cmd"] = val))
|
||||
waiting_on_device = device_id
|
||||
|
||||
if("set_threshold")
|
||||
var/env = params["env"]
|
||||
var/varname = params["var"]
|
||||
var/datum/tlv/tlv = TLV[env]
|
||||
var/newval = input("Enter [varname] for [env]", "Alarm triggers", tlv.vars[varname]) as num|null
|
||||
|
||||
if(isnull(newval) || ..()) // No setting if you walked away
|
||||
return
|
||||
if(href_list["command"]=="set_external_pressure")
|
||||
if(newval>1000+ONE_ATMOSPHERE)
|
||||
newval = 1000+ONE_ATMOSPHERE
|
||||
if(newval<0)
|
||||
newval = 0
|
||||
val = newval
|
||||
if(newval < 0)
|
||||
tlv.vars[varname] = -1.0
|
||||
else if(env == "temperature" && newval > 5000)
|
||||
tlv.vars[varname] = 5000
|
||||
else if(env == "pressure" && newval > 50 * ONE_ATMOSPHERE)
|
||||
tlv.vars[varname] = 50 * ONE_ATMOSPHERE
|
||||
else if(env != "temperature" && env != "pressure" && newval > 200)
|
||||
tlv.vars[varname] = 200
|
||||
else
|
||||
newval = round(newval, 0.01)
|
||||
tlv.vars[varname] = newval
|
||||
|
||||
send_signal(device_id, list(href_list["command"] = val ) )
|
||||
waiting_on_device=device_id
|
||||
if("atmos_alarm")
|
||||
if(alarm_area.atmosalert(ATMOS_ALARM_DANGER, src))
|
||||
post_alert(ATMOS_ALARM_DANGER)
|
||||
alarmActivated = TRUE
|
||||
update_icon()
|
||||
|
||||
if("set_threshold")
|
||||
var/env = href_list["env"]
|
||||
var/varname = href_list["var"]
|
||||
var/datum/tlv/tlv = TLV[env]
|
||||
var/newval = input("Enter [varname] for [env]", "Alarm triggers", tlv.vars[varname]) as num|null
|
||||
if("atmos_reset")
|
||||
if(alarm_area.atmosalert(ATMOS_ALARM_NONE, src, TRUE))
|
||||
post_alert(ATMOS_ALARM_NONE)
|
||||
alarmActivated = FALSE
|
||||
update_icon()
|
||||
|
||||
if(isnull(newval) || ..(href, href_list, nowindow, state))
|
||||
return
|
||||
if(newval<0)
|
||||
tlv.vars[varname] = -1.0
|
||||
else if(env=="temperature" && newval>5000)
|
||||
tlv.vars[varname] = 5000
|
||||
else if(env=="pressure" && newval>50*ONE_ATMOSPHERE)
|
||||
tlv.vars[varname] = 50*ONE_ATMOSPHERE
|
||||
else if(env!="temperature" && env!="pressure" && newval>200)
|
||||
tlv.vars[varname] = 200
|
||||
else
|
||||
newval = round(newval,0.01)
|
||||
tlv.vars[varname] = newval
|
||||
if("mode")
|
||||
if(!is_authenticated(usr, active_ui))
|
||||
return
|
||||
|
||||
if(href_list["screen"])
|
||||
if(!is_authenticated(usr, state_href))
|
||||
return
|
||||
mode = text2num(params["mode"])
|
||||
apply_mode()
|
||||
|
||||
screen = text2num(href_list["screen"])
|
||||
return 1
|
||||
|
||||
if(href_list["atmos_alarm"])
|
||||
if(alarm_area.atmosalert(ATMOS_ALARM_DANGER, src))
|
||||
post_alert(ATMOS_ALARM_DANGER)
|
||||
alarmActivated = 1
|
||||
update_icon()
|
||||
return 1
|
||||
if("preset")
|
||||
if(!is_authenticated(usr, active_ui))
|
||||
return
|
||||
|
||||
if(href_list["atmos_reset"])
|
||||
if(alarm_area.atmosalert(ATMOS_ALARM_NONE, src, TRUE))
|
||||
post_alert(ATMOS_ALARM_NONE)
|
||||
alarmActivated = 0
|
||||
update_icon()
|
||||
return 1
|
||||
preset = text2num(params["preset"])
|
||||
apply_preset()
|
||||
|
||||
if(href_list["mode"])
|
||||
if(!is_authenticated(usr, state_href))
|
||||
return
|
||||
|
||||
mode = text2num(href_list["mode"])
|
||||
apply_mode()
|
||||
return 1
|
||||
if("temperature")
|
||||
var/datum/tlv/selected = TLV["temperature"]
|
||||
var/max_temperature = selected.max1 >= 0 ? min(selected.max1, MAX_TEMPERATURE) : max(selected.max1, MAX_TEMPERATURE)
|
||||
var/min_temperature = max(selected.min1, MIN_TEMPERATURE)
|
||||
var/max_temperature_c = max_temperature - T0C
|
||||
var/min_temperature_c = min_temperature - T0C
|
||||
var/input_temperature = input("What temperature would you like the system to maintain? (Capped between [min_temperature_c]C and [max_temperature_c]C)", "Thermostat Controls") as num|null
|
||||
if(isnull(input_temperature) || ..()) // No temp setting if you walked away
|
||||
return
|
||||
input_temperature = input_temperature + T0C
|
||||
if(input_temperature > max_temperature || input_temperature < min_temperature)
|
||||
to_chat(usr, "<span class='warning'>Temperature must be between [min_temperature_c]C and [max_temperature_c]C</span>")
|
||||
else
|
||||
target_temperature = input_temperature
|
||||
|
||||
if(href_list["preset"])
|
||||
if(!is_authenticated(usr, state_href))
|
||||
return
|
||||
|
||||
preset = text2num(href_list["preset"])
|
||||
apply_preset()
|
||||
return 1
|
||||
|
||||
if(href_list["temperature"])
|
||||
var/datum/tlv/selected = TLV["temperature"]
|
||||
var/max_temperature = selected.max1 >= 0 ? min(selected.max1, MAX_TEMPERATURE) : max(selected.max1, MAX_TEMPERATURE)
|
||||
var/min_temperature = max(selected.min1, MIN_TEMPERATURE)
|
||||
var/max_temperature_c = max_temperature - T0C
|
||||
var/min_temperature_c = min_temperature - T0C
|
||||
var/input_temperature = input("What temperature would you like the system to maintain? (Capped between [min_temperature_c]C and [max_temperature_c]C)", "Thermostat Controls") as num|null
|
||||
if(isnull(input_temperature) || ..(href, href_list, nowindow, state))
|
||||
return
|
||||
input_temperature = input_temperature + T0C
|
||||
if(input_temperature > max_temperature || input_temperature < min_temperature)
|
||||
to_chat(usr, "Temperature must be between [min_temperature_c]C and [max_temperature_c]C")
|
||||
else
|
||||
target_temperature = input_temperature
|
||||
return 1
|
||||
|
||||
/obj/machinery/alarm/emag_act(mob/user)
|
||||
if(!emagged)
|
||||
src.emagged = 1
|
||||
emagged = TRUE
|
||||
if(user)
|
||||
user.visible_message("<span class='warning'>Sparks fly out of the [src]!</span>", "<span class='notice'>You emag the [src], disabling its safeties.</span>")
|
||||
playsound(src.loc, 'sound/effects/sparks4.ogg', 50, 1)
|
||||
playsound(src.loc, 'sound/effects/sparks4.ogg', 50, TRUE)
|
||||
return
|
||||
|
||||
/obj/machinery/alarm/attackby(obj/item/I, mob/user, params)
|
||||
@@ -960,7 +941,7 @@
|
||||
if(allowed(usr) && !wires.is_cut(WIRE_IDSCAN))
|
||||
locked = !locked
|
||||
to_chat(user, "<span class='notice'>You [ locked ? "lock" : "unlock"] the Air Alarm interface.</span>")
|
||||
updateUsrDialog()
|
||||
SStgui.update_uis(src)
|
||||
else
|
||||
to_chat(user, "<span class='warning'>Access denied.</span>")
|
||||
return
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
var/list/being_built = list()
|
||||
var/datum/research/files
|
||||
var/list/imported = list() // /datum/design.id -> boolean
|
||||
var/list/datum/design/matching_designs
|
||||
var/temp_search
|
||||
var/selected_category
|
||||
@@ -108,12 +109,19 @@
|
||||
var/maxmult = 1
|
||||
if(ispath(D.build_path, /obj/item/stack))
|
||||
maxmult = D.maxstack
|
||||
|
||||
var/list/default_categories = D.category
|
||||
var/list/categories = istype(default_categories) ? default_categories.Copy() : list()
|
||||
|
||||
if(imported[D.id])
|
||||
categories |= "Imported"
|
||||
|
||||
recipes.Add(list(list(
|
||||
"name" = D.name,
|
||||
"category" = D.category,
|
||||
"category" = categories,
|
||||
"uid" = D.UID(),
|
||||
"requirements" = matreq,
|
||||
"hacked" = ("hacked" in D.category) ? TRUE : FALSE,
|
||||
"hacked" = ("hacked" in categories) ? TRUE : FALSE,
|
||||
"max_multiplier" = maxmult,
|
||||
"image" = "[icon2base64(icon(initial(I.icon), initial(I.icon_state), SOUTH, 1))]"
|
||||
)))
|
||||
@@ -162,8 +170,8 @@
|
||||
if(!istype(design_last_ordered))
|
||||
to_chat(usr, "<span class='warning'>Invalid design</span>")
|
||||
return
|
||||
if(!(design_last_ordered.build_type & AUTOLATHE))
|
||||
to_chat(usr, "<span class='warning'>Invalid design (not buildable in autolathe, report this error.)</span>")
|
||||
if(!(design_last_ordered.id in files.known_designs))
|
||||
to_chat(usr, "<span class='warning'>Invalid design (not in autolathe's known designs, report this error.)</span>")
|
||||
return
|
||||
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
|
||||
if(design_last_ordered.materials["$metal"] > materials.amount(MAT_METAL))
|
||||
@@ -243,7 +251,13 @@
|
||||
if(istype(O, /obj/item/disk/design_disk))
|
||||
var/obj/item/disk/design_disk/D = O
|
||||
if(D.blueprint)
|
||||
if(!(D.blueprint.build_type & AUTOLATHE)) // otherwise, would silently fail in AddDesign2Known
|
||||
var/datum/design/design = D.blueprint // READ ONLY!!
|
||||
|
||||
if(design.id in files.known_designs)
|
||||
to_chat(user, "<span class='warning'>This design has already been loaded into the autolathe.</span>")
|
||||
return 1
|
||||
|
||||
if(!files.CanAddDesign2Known(design))
|
||||
to_chat(user, "<span class='warning'>This design is not compatible with the autolathe.</span>")
|
||||
return 1
|
||||
user.visible_message("[user] begins to load \the [O] in \the [src]...",
|
||||
@@ -252,9 +266,8 @@
|
||||
playsound(get_turf(src), 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1)
|
||||
busy = TRUE
|
||||
if(do_after(user, 14.4, target = src))
|
||||
if(!("Imported" in D.blueprint.category)) // R&D should always ensure this is set on design disks, but it doesn't.
|
||||
D.blueprint.category += "Imported" // now it will actually show up in the list.
|
||||
files.AddDesign2Known(D.blueprint)
|
||||
imported[design.id] = TRUE
|
||||
files.AddDesign2Known(design)
|
||||
recipiecache = list()
|
||||
SStgui.close_uis(src) // forces all connected users to re-open the TGUI. Imported entries won't show otherwise due to static_data
|
||||
busy = FALSE
|
||||
|
||||
@@ -1,39 +1,30 @@
|
||||
/obj/machinery/computer/atmoscontrol
|
||||
name = "\improper Central Atmospherics Computer"
|
||||
name = "\improper central atmospherics computer"
|
||||
icon = 'icons/obj/computer.dmi'
|
||||
icon_keyboard = "atmos_key"
|
||||
icon_screen = "tank"
|
||||
light_color = LIGHT_COLOR_GREEN
|
||||
density = 1
|
||||
anchored = 1.0
|
||||
light_color = LIGHT_COLOR_CYAN
|
||||
circuit = /obj/item/circuitboard/atmoscontrol
|
||||
req_access = list(ACCESS_ATMOSPHERICS)
|
||||
var/list/monitored_alarm_ids = null
|
||||
var/datum/nano_module/atmos_control/atmos_control
|
||||
var/datum/tgui_module/atmos_control/atmos_control
|
||||
|
||||
/obj/machinery/computer/atmoscontrol/Initialize()
|
||||
. = ..()
|
||||
atmos_control = new(src)
|
||||
|
||||
/obj/machinery/computer/atmoscontrol/laptop
|
||||
name = "atmospherics laptop"
|
||||
desc = "Cheap Nanotrasen laptop."
|
||||
icon_state = "medlaptop"
|
||||
density = 0
|
||||
density = FALSE
|
||||
|
||||
/obj/machinery/computer/atmoscontrol/attack_ai(var/mob/user as mob)
|
||||
ui_interact(user)
|
||||
/obj/machinery/computer/atmoscontrol/attack_ai(mob/user)
|
||||
tgui_interact(user)
|
||||
|
||||
/obj/machinery/computer/atmoscontrol/attack_hand(mob/user)
|
||||
if(..())
|
||||
return 1
|
||||
ui_interact(user)
|
||||
return
|
||||
tgui_interact(user)
|
||||
|
||||
/obj/machinery/computer/atmoscontrol/emag_act(mob/user)
|
||||
if(!emagged)
|
||||
user.visible_message("<span class='warning'>\The [user] does something \the [src], causing the screen to flash!</span>",\
|
||||
"<span class='warning'>You cause the screen to flash as you gain full control.</span>",\
|
||||
"You hear an electronic warble.")
|
||||
atmos_control.emagged = 1
|
||||
return 1
|
||||
|
||||
/obj/machinery/computer/atmoscontrol/ui_interact(var/mob/user)
|
||||
if(!atmos_control)
|
||||
atmos_control = new(src, req_access, req_one_access, monitored_alarm_ids)
|
||||
atmos_control.ui_interact(user)
|
||||
/obj/machinery/computer/atmoscontrol/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
|
||||
atmos_control.tgui_interact(user, ui_key, ui, force_open)
|
||||
|
||||
@@ -121,8 +121,8 @@
|
||||
|
||||
var/list/visible_turfs = list()
|
||||
for(var/turf/T in (C.isXRay() \
|
||||
? range(C.view_range, C) \
|
||||
: view(C.view_range, C)))
|
||||
? range(C.view_range, get_turf(C)) \
|
||||
: view(C.view_range, get_turf(C))))
|
||||
visible_turfs += T
|
||||
|
||||
var/list/bbox = get_bbox_of_atoms(visible_turfs)
|
||||
@@ -163,8 +163,11 @@
|
||||
tgui_interact(user)
|
||||
|
||||
/obj/machinery/computer/security/attack_ai(mob/user)
|
||||
to_chat(user, "<span class='notice'>You realise its kind of stupid to access a camera console when you have the entire camera network at your metaphorical fingertips</span>")
|
||||
return
|
||||
if(isAI(user))
|
||||
to_chat(user, "<span class='notice'>You realise its kind of stupid to access a camera console when you have the entire camera network at your metaphorical fingertips</span>")
|
||||
return
|
||||
|
||||
tgui_interact(user)
|
||||
|
||||
|
||||
/obj/machinery/computer/security/proc/show_camera_static()
|
||||
|
||||
@@ -16,61 +16,6 @@
|
||||
..()
|
||||
program = new/datum/computer/file/embedded_program/airlock(src)
|
||||
|
||||
//Advanced airlock controller for when you want a more versatile airlock controller - useful for turning simple access control rooms into airlocks
|
||||
/obj/machinery/embedded_controller/radio/airlock/advanced_airlock_controller
|
||||
name = "Advanced Airlock Controller"
|
||||
|
||||
/obj/machinery/embedded_controller/radio/airlock/advanced_airlock_controller/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "advanced_airlock_console.tmpl", name, 470, 290)
|
||||
ui.open()
|
||||
ui.set_auto_update(1)
|
||||
|
||||
/obj/machinery/embedded_controller/radio/airlock/advanced_airlock_controller/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
|
||||
var/data[0]
|
||||
|
||||
data = list(
|
||||
"chamber_pressure" = round(program.memory["chamber_sensor_pressure"]),
|
||||
"external_pressure" = round(program.memory["external_sensor_pressure"]),
|
||||
"internal_pressure" = round(program.memory["internal_sensor_pressure"]),
|
||||
"processing" = program.memory["processing"],
|
||||
"purge" = program.memory["purge"],
|
||||
"secure" = program.memory["secure"]
|
||||
)
|
||||
|
||||
return data
|
||||
|
||||
/obj/machinery/embedded_controller/radio/airlock/advanced_airlock_controller/Topic(href, href_list)
|
||||
if(..())
|
||||
return 1
|
||||
|
||||
usr.set_machine(src)
|
||||
src.add_fingerprint(usr)
|
||||
|
||||
var/clean = 0
|
||||
switch(href_list["command"]) //anti-HTML-hacking checks
|
||||
if("cycle_ext")
|
||||
clean = 1
|
||||
if("cycle_int")
|
||||
clean = 1
|
||||
if("force_ext")
|
||||
clean = 1
|
||||
if("force_int")
|
||||
clean = 1
|
||||
if("abort")
|
||||
clean = 1
|
||||
if("purge")
|
||||
clean = 1
|
||||
if("secure")
|
||||
clean = 1
|
||||
|
||||
if(clean)
|
||||
program.receive_user_command(href_list["command"])
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
//Airlock controller for airlock control - most airlocks on the station use this
|
||||
/obj/machinery/embedded_controller/radio/airlock/airlock_controller
|
||||
name = "Airlock Controller"
|
||||
@@ -91,49 +36,34 @@
|
||||
tag_chamber_sensor = given_tag_chamber_sensor
|
||||
..()
|
||||
|
||||
/obj/machinery/embedded_controller/radio/airlock/airlock_controller/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
/obj/machinery/embedded_controller/radio/airlock/airlock_controller/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "simple_airlock_console.tmpl", name, 470, 290)
|
||||
ui = new(user, src, ui_key, "ExternalAirlockController", name, 470, 290, master_ui, state)
|
||||
ui.open()
|
||||
ui.set_auto_update(1)
|
||||
|
||||
/obj/machinery/embedded_controller/radio/airlock/airlock_controller/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
|
||||
var/data[0]
|
||||
/obj/machinery/embedded_controller/radio/airlock/airlock_controller/tgui_data(mob/user)
|
||||
var/list/data = list()
|
||||
|
||||
data = list(
|
||||
"chamber_pressure" = round(program.memory["chamber_sensor_pressure"]),
|
||||
"exterior_status" = program.memory["exterior_status"],
|
||||
"interior_status" = program.memory["interior_status"],
|
||||
"processing" = program.memory["processing"],
|
||||
)
|
||||
data["chamber_pressure"] = round(program.memory["chamber_sensor_pressure"])
|
||||
data["exterior_status"] = program.memory["exterior_status"]
|
||||
data["interior_status"] = program.memory["interior_status"]
|
||||
data["processing"] = program.memory["processing"]
|
||||
|
||||
return data
|
||||
|
||||
/obj/machinery/embedded_controller/radio/airlock/airlock_controller/Topic(href, href_list)
|
||||
|
||||
/obj/machinery/embedded_controller/radio/airlock/airlock_controller/tgui_act(action, params)
|
||||
if(..())
|
||||
return
|
||||
|
||||
usr.set_machine(src)
|
||||
src.add_fingerprint(usr)
|
||||
add_fingerprint(usr)
|
||||
|
||||
var/clean = 0
|
||||
switch(href_list["command"]) //anti-HTML-hacking checks
|
||||
if("cycle_ext")
|
||||
clean = 1
|
||||
if("cycle_int")
|
||||
clean = 1
|
||||
if("force_ext")
|
||||
clean = 1
|
||||
if("force_int")
|
||||
clean = 1
|
||||
if("abort")
|
||||
clean = 1
|
||||
var/list/allowed_actions = list("cycle_ext", "cycle_int", "force_ext", "force_int", "abort")
|
||||
if(action in allowed_actions) //anti-HTML-hacking checks
|
||||
program.receive_user_command(action)
|
||||
|
||||
if(clean)
|
||||
program.receive_user_command(href_list["command"])
|
||||
|
||||
return 1
|
||||
return TRUE
|
||||
|
||||
|
||||
//Access controller for door control - used in virology and the like
|
||||
@@ -153,45 +83,46 @@
|
||||
else
|
||||
icon_state = "access_control_off"
|
||||
|
||||
/obj/machinery/embedded_controller/radio/airlock/access_controller/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
/obj/machinery/embedded_controller/radio/airlock/access_controller/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "door_access_console.tmpl", name, 330, 220)
|
||||
ui = new(user, src, ui_key, "AirlockAccessController", name, 470, 290, master_ui, state)
|
||||
ui.open()
|
||||
ui.set_auto_update(1)
|
||||
|
||||
/obj/machinery/embedded_controller/radio/airlock/access_controller/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
|
||||
var/data[0]
|
||||
/obj/machinery/embedded_controller/radio/airlock/access_controller/tgui_data(mob/user)
|
||||
var/list/data = list()
|
||||
|
||||
data = list(
|
||||
"exterior_status" = program.memory["exterior_status"],
|
||||
"interior_status" = program.memory["interior_status"],
|
||||
"processing" = program.memory["processing"]
|
||||
)
|
||||
data["exterior_status"] = program.memory["exterior_status"]
|
||||
data["interior_status"] = program.memory["interior_status"]
|
||||
data["processing"] = program.memory["processing"]
|
||||
|
||||
return data
|
||||
|
||||
/obj/machinery/embedded_controller/radio/airlock/access_controller/Topic(href, href_list)
|
||||
/obj/machinery/embedded_controller/radio/airlock/access_controller/tgui_act(action, params)
|
||||
if(..())
|
||||
return
|
||||
|
||||
usr.set_machine(src)
|
||||
src.add_fingerprint(usr)
|
||||
add_fingerprint(usr)
|
||||
|
||||
var/clean = 0
|
||||
switch(href_list["command"]) //anti-HTML-hacking checks
|
||||
var/clean = FALSE
|
||||
|
||||
// Repeat the frontend check to make sure only allowed command are sent. You are only allowed to lock or cycle exterior if exterior is open
|
||||
// Vice versa for internal
|
||||
|
||||
switch(action)
|
||||
if("cycle_ext_door")
|
||||
clean = 1
|
||||
clean = TRUE
|
||||
if("cycle_int_door")
|
||||
clean = 1
|
||||
if("force_ext")
|
||||
if(program.memory["interior_status"]["state"] == "closed")
|
||||
clean = 1
|
||||
if("force_int")
|
||||
if(program.memory["exterior_status"]["state"] == "closed")
|
||||
clean = 1
|
||||
clean = TRUE
|
||||
if("force_ext") // Cannot force exterior if interior open
|
||||
if(program.memory["interior_status"]["state"] == "closed" && program.memory["exterior_status"]["state"] == "open")
|
||||
clean = TRUE
|
||||
if("force_int") // Cannot force interior if exterior open
|
||||
if(program.memory["exterior_status"]["state"] == "closed" && program.memory["interior_status"]["state"] == "open")
|
||||
clean = TRUE
|
||||
|
||||
if(clean)
|
||||
program.receive_user_command(href_list["command"])
|
||||
program.receive_user_command(action)
|
||||
|
||||
return TRUE
|
||||
|
||||
return 1
|
||||
|
||||
@@ -27,18 +27,15 @@
|
||||
src.updateDialog()
|
||||
|
||||
/obj/machinery/embedded_controller/attack_ghost(mob/user as mob)
|
||||
src.ui_interact(user)
|
||||
tgui_interact(user)
|
||||
|
||||
/obj/machinery/embedded_controller/attack_ai(mob/user as mob)
|
||||
src.ui_interact(user)
|
||||
tgui_interact(user)
|
||||
|
||||
/obj/machinery/embedded_controller/attack_hand(mob/user as mob)
|
||||
if(!user.IsAdvancedToolUser())
|
||||
return 0
|
||||
src.ui_interact(user)
|
||||
|
||||
/obj/machinery/embedded_controller/ui_interact()
|
||||
return
|
||||
return FALSE
|
||||
tgui_interact(user)
|
||||
|
||||
/obj/machinery/embedded_controller/radio
|
||||
icon = 'icons/obj/airlock_machines.dmi'
|
||||
|
||||
@@ -188,6 +188,9 @@ GLOBAL_LIST_INIT(sandbag_recipes, list ( \
|
||||
..()
|
||||
recipes = GLOB.diamond_recipes
|
||||
|
||||
/obj/item/stack/sheet/mineral/diamond/fifty
|
||||
amount = 50
|
||||
|
||||
/obj/item/stack/sheet/mineral/uranium
|
||||
name = "uranium"
|
||||
icon_state = "sheet-uranium"
|
||||
@@ -216,6 +219,9 @@ GLOBAL_LIST_INIT(sandbag_recipes, list ( \
|
||||
..()
|
||||
recipes = GLOB.plasma_recipes
|
||||
|
||||
/obj/item/stack/sheet/mineral/plasma/fifty
|
||||
amount = 50
|
||||
|
||||
/obj/item/stack/sheet/mineral/plasma/welder_act(mob/user, obj/item/I)
|
||||
if(I.use_tool(src, user, volume = I.tool_volume))
|
||||
log_and_set_aflame(user, I)
|
||||
@@ -275,6 +281,9 @@ GLOBAL_LIST_INIT(sandbag_recipes, list ( \
|
||||
materials = list(MAT_BANANIUM=MINERAL_MATERIAL_AMOUNT)
|
||||
point_value = 50
|
||||
|
||||
/obj/item/stack/sheet/mineral/bananium/fifty
|
||||
amount = 50
|
||||
|
||||
/obj/item/stack/sheet/mineral/bananium/New(loc, amount=null)
|
||||
..()
|
||||
recipes = GLOB.bananium_recipes
|
||||
@@ -289,6 +298,9 @@ GLOBAL_LIST_INIT(sandbag_recipes, list ( \
|
||||
wall_allowed = FALSE //no tranquilite walls in code
|
||||
point_value = 50
|
||||
|
||||
/obj/item/stack/sheet/mineral/tranquillite/fifty
|
||||
amount = 50
|
||||
|
||||
/obj/item/stack/sheet/mineral/tranquillite/New(loc, amount=null)
|
||||
..()
|
||||
recipes = GLOB.tranquillite_recipes
|
||||
@@ -367,6 +379,9 @@ GLOBAL_LIST_INIT(plastitanium_recipes, list(
|
||||
origin_tech = "materials=6;abductor=1"
|
||||
sheettype = "abductor"
|
||||
|
||||
/obj/item/stack/sheet/mineral/abductor/fifty
|
||||
amount = 50
|
||||
|
||||
/obj/item/stack/sheet/mineral/abductor/New(loc, amount=null)
|
||||
recipes = GLOB.abductor_recipes
|
||||
..()
|
||||
|
||||
@@ -24,27 +24,27 @@
|
||||
src.on_floor = on_floor
|
||||
src.window_checks = window_checks
|
||||
|
||||
/datum/stack_recipe/proc/post_build(var/obj/item/stack/S, var/obj/result)
|
||||
/datum/stack_recipe/proc/post_build(obj/item/stack/S, obj/result)
|
||||
return
|
||||
|
||||
/* Special Recipes */
|
||||
|
||||
/datum/stack_recipe/cable_restraints
|
||||
/datum/stack_recipe/cable_restraints/post_build(var/obj/item/stack/S, var/obj/result)
|
||||
/datum/stack_recipe/cable_restraints/post_build(obj/item/stack/S, obj/result)
|
||||
if(istype(result, /obj/item/restraints/handcuffs/cable))
|
||||
result.color = S.color
|
||||
..()
|
||||
|
||||
|
||||
/datum/stack_recipe/dangerous
|
||||
/datum/stack_recipe/dangerous/post_build(/obj/item/stack/S, /obj/result)
|
||||
/datum/stack_recipe/dangerous/post_build(obj/item/stack/S, obj/result)
|
||||
var/turf/targ = get_turf(usr)
|
||||
message_admins("[title] made by [key_name_admin(usr)](<A HREF='?_src_=holder;adminmoreinfo=\ref[usr]'>?</A>) in [get_area(usr)] [ADMIN_COORDJMP(targ)]!",0,1)
|
||||
log_game("[title] made by [key_name_admin(usr)] at [get_area(usr)] [targ.x], [targ.y], [targ.z].")
|
||||
..()
|
||||
|
||||
/datum/stack_recipe/rods
|
||||
/datum/stack_recipe/rods/post_build(var/obj/item/stack/S, var/obj/result)
|
||||
/datum/stack_recipe/rods/post_build(obj/item/stack/S, obj/result)
|
||||
if(istype(result, /obj/item/stack/rods))
|
||||
var/obj/item/stack/rods/R = result
|
||||
R.update_icon()
|
||||
|
||||
@@ -72,11 +72,17 @@
|
||||
turf_type = /turf/simulated/floor/carpet
|
||||
resistance_flags = FLAMMABLE
|
||||
|
||||
/obj/item/stack/tile/carpet/twenty
|
||||
amount = 20
|
||||
|
||||
/obj/item/stack/tile/carpet/black
|
||||
name = "black carpet"
|
||||
icon_state = "tile-carpet-black"
|
||||
turf_type = /turf/simulated/floor/carpet/black
|
||||
|
||||
/obj/item/stack/tile/carpet/black/twenty
|
||||
amount = 20
|
||||
|
||||
//Plasteel
|
||||
/obj/item/stack/tile/plasteel
|
||||
name = "floor tiles"
|
||||
|
||||
@@ -1,42 +1,20 @@
|
||||
/*
|
||||
CONTAINS:
|
||||
RCD // no fucking shit sherlock
|
||||
*/
|
||||
#define RCD_PAGE_MAIN 1
|
||||
#define RCD_PAGE_AIRLOCK 2
|
||||
|
||||
#define RCD_MODE_TURF "Turf"
|
||||
#define RCD_MODE_AIRLOCK "Airlock"
|
||||
#define RCD_MODE_DECON "Deconstruct"
|
||||
#define RCD_MODE_WINDOW "Windows"
|
||||
#define MATTER_100 100
|
||||
#define MATTER_500 500
|
||||
|
||||
GLOBAL_LIST_INIT(rcd_door_types, list(
|
||||
/obj/machinery/door/airlock = "Standard", /obj/machinery/door/airlock/glass = "Standard (Glass)",
|
||||
/obj/machinery/door/airlock/command = "Command", /obj/machinery/door/airlock/command/glass = "Command (Glass)",
|
||||
/obj/machinery/door/airlock/security = "Security", /obj/machinery/door/airlock/security/glass = "Security (Glass)",
|
||||
/obj/machinery/door/airlock/engineering = "Engineering", /obj/machinery/door/airlock/engineering/glass = "Engineering (Glass)",
|
||||
/obj/machinery/door/airlock/medical = "Medical", /obj/machinery/door/airlock/medical/glass = "Medical (Glass)",
|
||||
/obj/machinery/door/airlock/maintenance = "Maintenance", /obj/machinery/door/airlock/maintenance/glass = "Maintenance (Glass)",
|
||||
/obj/machinery/door/airlock/external = "External", /obj/machinery/door/airlock/external/glass = "External (Glass)",
|
||||
/obj/machinery/door/airlock/maintenance/external = "External Maintenance",
|
||||
/obj/machinery/door/airlock/maintenance/external/glass = "External Maintenance (Glass)",
|
||||
/obj/machinery/door/airlock/freezer = "Freezer",
|
||||
/obj/machinery/door/airlock/mining = "Mining", /obj/machinery/door/airlock/mining/glass = "Mining (Glass)",
|
||||
/obj/machinery/door/airlock/research = "Research", /obj/machinery/door/airlock/research/glass = "Research (Glass)",
|
||||
/obj/machinery/door/airlock/atmos = "Atmospherics", /obj/machinery/door/airlock/atmos/glass = "Atmospherics (Glass)",
|
||||
/obj/machinery/door/airlock/science = "Science", /obj/machinery/door/airlock/science/glass = "Science (Glass)",
|
||||
/obj/machinery/door/airlock/hatch = "Airtight Hatch",
|
||||
/obj/machinery/door/airlock/maintenance_hatch = "Maintenance Hatch"
|
||||
))
|
||||
#define TAB_AIRLOCK_TYPE 1
|
||||
#define TAB_AIRLOCK_ACCESS 2
|
||||
|
||||
#define MODE_TURF "Floors and Walls"
|
||||
#define MODE_AIRLOCK "Airlocks"
|
||||
#define MODE_WINDOW "Windows"
|
||||
#define MODE_DECON "Deconstruction"
|
||||
|
||||
/obj/item/rcd
|
||||
name = "rapid-construction-device (RCD)"
|
||||
desc = "A device used to rapidly build and deconstruct walls, floors and airlocks."
|
||||
icon = 'icons/obj/tools.dmi'
|
||||
icon_state = "rcd"
|
||||
opacity = 0
|
||||
density = 0
|
||||
anchored = 0
|
||||
flags = CONDUCT | NOBLUDGEON
|
||||
force = 0
|
||||
throwforce = 10
|
||||
@@ -51,24 +29,36 @@ GLOBAL_LIST_INIT(rcd_door_types, list(
|
||||
req_access = list(ACCESS_ENGINE)
|
||||
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50)
|
||||
resistance_flags = FIRE_PROOF
|
||||
// Important shit
|
||||
/// The spark system used to create sparks when the user interacts with the RCD.
|
||||
var/datum/effect_system/spark_spread/spark_system
|
||||
var/matter = 0
|
||||
var/max_matter = 100
|
||||
var/mode = RCD_MODE_TURF
|
||||
var/canRwall = 0
|
||||
|
||||
// UI shit
|
||||
var/menu = RCD_PAGE_MAIN
|
||||
/// The current amount of matter stored.
|
||||
var/matter = NONE
|
||||
/// The max amount of matter that can be stored.
|
||||
var/max_matter = MATTER_100
|
||||
/// The RCD's current build mode.
|
||||
var/mode = MODE_TURF
|
||||
/// If the RCD can deconstruct reinforced walls.
|
||||
var/canRwall = FALSE
|
||||
/// Is the RCD's airlock access selection menu locked?
|
||||
var/locked = TRUE
|
||||
/// The current airlock type that will be build.
|
||||
var/door_type = /obj/machinery/door/airlock
|
||||
/// The name that newly build airlocks will receive.
|
||||
var/door_name = "Airlock"
|
||||
var/list/door_accesses = list()
|
||||
var/list/door_accesses_list = list()
|
||||
var/one_access
|
||||
|
||||
// Stupid shit
|
||||
var/static/allowed_targets = list(/turf, /obj/structure/grille, /obj/structure/window, /obj/structure/lattice, /obj/machinery/door/airlock)
|
||||
/// If this is TRUE, any airlocks that gets built will require only ONE of the checked accesses. If FALSE, it will require ALL of them.
|
||||
var/one_access = TRUE
|
||||
/// Which airlock tab the UI is currently set to display.
|
||||
var/ui_tab = TAB_AIRLOCK_TYPE
|
||||
/// A list of access numbers which have been checked off by the user in the UI.
|
||||
var/list/selected_accesses = list()
|
||||
/// A list of valid atoms that RCDs can target. Clicking on an atom with an RCD which is not in this list, will do nothing.
|
||||
var/static/list/allowed_targets = list(/turf, /obj/structure/grille, /obj/structure/window, /obj/structure/lattice, /obj/machinery/door/airlock)
|
||||
/// An associative list of airlock type paths as keys, and their names as values.
|
||||
var/static/list/rcd_door_types = list()
|
||||
/// An associative list containing an airlock's name, type path, and image. For use with the UI.
|
||||
var/static/list/door_types_ui_list = list()
|
||||
/// An associative list containing all station accesses. Includes their name and access number. For use with the UI.
|
||||
var/static/list/door_accesses_list = list()
|
||||
|
||||
/obj/item/rcd/Initialize()
|
||||
. = ..()
|
||||
@@ -77,9 +67,49 @@ GLOBAL_LIST_INIT(rcd_door_types, list(
|
||||
spark_system.attach(src)
|
||||
GLOB.rcd_list += src
|
||||
|
||||
door_accesses_list = list()
|
||||
for(var/access in get_all_accesses())
|
||||
door_accesses_list[++door_accesses_list.len] = list("name" = get_access_desc(access), "id" = access, "enabled" = (access in door_accesses))
|
||||
if(!length(rcd_door_types))
|
||||
rcd_door_types = list(
|
||||
/obj/machinery/door/airlock = "Standard",
|
||||
/obj/machinery/door/airlock/glass = "Standard (Glass)",
|
||||
/obj/machinery/door/airlock/command = "Command",
|
||||
/obj/machinery/door/airlock/command/glass = "Command (Glass)",
|
||||
/obj/machinery/door/airlock/security = "Security",
|
||||
/obj/machinery/door/airlock/security/glass = "Security (Glass)",
|
||||
/obj/machinery/door/airlock/engineering = "Engineering",
|
||||
/obj/machinery/door/airlock/engineering/glass = "Engineering (Glass)",
|
||||
/obj/machinery/door/airlock/atmos = "Atmospherics",
|
||||
/obj/machinery/door/airlock/atmos/glass = "Atmospherics (Glass)",
|
||||
/obj/machinery/door/airlock/mining = "Mining",
|
||||
/obj/machinery/door/airlock/mining/glass = "Mining (Glass)",
|
||||
/obj/machinery/door/airlock/medical = "Medical",
|
||||
/obj/machinery/door/airlock/medical/glass = "Medical (Glass)",
|
||||
/obj/machinery/door/airlock/research = "Research",
|
||||
/obj/machinery/door/airlock/research/glass = "Research (Glass)",
|
||||
/obj/machinery/door/airlock/science = "Science",
|
||||
/obj/machinery/door/airlock/science/glass = "Science (Glass)",
|
||||
/obj/machinery/door/airlock/maintenance = "Maintenance",
|
||||
/obj/machinery/door/airlock/maintenance/glass = "Maintenance (Glass)",
|
||||
/obj/machinery/door/airlock/maintenance/external = "External Maintenance",
|
||||
/obj/machinery/door/airlock/maintenance/external/glass = "External Maint. (Glass)",
|
||||
/obj/machinery/door/airlock/external = "External",
|
||||
/obj/machinery/door/airlock/external/glass = "External (Glass)",
|
||||
/obj/machinery/door/airlock/hatch = "Airtight Hatch",
|
||||
/obj/machinery/door/airlock/maintenance_hatch = "Maintenance Hatch",
|
||||
/obj/machinery/door/airlock/freezer = "Freezer"
|
||||
)
|
||||
if(!length(door_types_ui_list))
|
||||
for(var/type in rcd_door_types)
|
||||
door_types_ui_list += list(list(
|
||||
"name" = rcd_door_types[type],
|
||||
"type" = type,
|
||||
"image" = get_airlock_image(type)
|
||||
))
|
||||
if(!length(door_accesses_list))
|
||||
for(var/access in get_all_accesses())
|
||||
door_accesses_list += list(list(
|
||||
"name" = get_access_desc(access),
|
||||
"id" = access
|
||||
))
|
||||
|
||||
/obj/item/rcd/examine(mob/user)
|
||||
. = ..()
|
||||
@@ -91,15 +121,29 @@ GLOBAL_LIST_INIT(rcd_door_types, list(
|
||||
GLOB.rcd_list -= src
|
||||
return ..()
|
||||
|
||||
/**
|
||||
* Creates and returns a base64 icon of the given `airlock_type`.
|
||||
*
|
||||
* This is used for airlock icon previews in the UI.
|
||||
*
|
||||
* Arugments:
|
||||
* * airlock_type - an airlock typepath.
|
||||
*/
|
||||
/obj/item/rcd/proc/get_airlock_image(airlock_type)
|
||||
var/obj/machinery/door/airlock/proto = airlock_type
|
||||
var/ic = initial(proto.icon)
|
||||
var/mutable_appearance/MA = mutable_appearance(ic, "closed")
|
||||
if(!initial(proto.glass))
|
||||
MA.overlays += "fill_closed"
|
||||
// Not scaling these down to button size because they look horrible then, instead just bumping up radius.
|
||||
return MA
|
||||
var/obj/machinery/door/airlock/proto = new airlock_type(null)
|
||||
proto.icon_state = "closed"
|
||||
if(!proto.glass)
|
||||
proto.add_overlay("fill_closed")
|
||||
var/icon/I = getFlatIcon(proto)
|
||||
qdel(proto)
|
||||
return "[icon2base64(I)]"
|
||||
|
||||
/**
|
||||
* Runs a series of pre-checks before opening the radial menu to the user.
|
||||
*
|
||||
* Arguments:
|
||||
* * user - the mob trying to open the radial menu.
|
||||
*/
|
||||
/obj/item/rcd/proc/check_menu(mob/living/user)
|
||||
if(!istype(user))
|
||||
return FALSE
|
||||
@@ -108,33 +152,41 @@ GLOBAL_LIST_INIT(rcd_door_types, list(
|
||||
return TRUE
|
||||
|
||||
/obj/item/rcd/attackby(obj/item/W, mob/user, params)
|
||||
..()
|
||||
if(!istype(W, /obj/item/rcd_ammo))
|
||||
return ..()
|
||||
|
||||
if(istype(W, /obj/item/rcd_ammo))
|
||||
var/obj/item/rcd_ammo/R = W
|
||||
if((matter + R.ammoamt) > max_matter)
|
||||
to_chat(user, "<span class='notice'>The RCD can't hold any more matter-units.</span>")
|
||||
return
|
||||
matter += R.ammoamt
|
||||
if(!user.unEquip(R))
|
||||
to_chat(user, "<span class='warning'>[R] is stuck to your hand!</span>")
|
||||
return
|
||||
qdel(R)
|
||||
playsound(loc, 'sound/machines/click.ogg', 50, 1)
|
||||
to_chat(user, "<span class='notice'>The RCD now holds [matter]/[max_matter] matter-units.</span>")
|
||||
SSnanoui.update_uis(src)
|
||||
var/obj/item/rcd_ammo/R = W
|
||||
if((matter + R.ammoamt) > max_matter)
|
||||
to_chat(user, "<span class='notice'>The RCD can't hold any more matter-units.</span>")
|
||||
return
|
||||
|
||||
if(!user.unEquip(R))
|
||||
to_chat(user, "<span class='warning'>[R] is stuck to your hand!</span>")
|
||||
return
|
||||
|
||||
matter += R.ammoamt
|
||||
qdel(R)
|
||||
playsound(loc, 'sound/machines/click.ogg', 50, 1)
|
||||
to_chat(user, "<span class='notice'>The RCD now holds [matter]/[max_matter] matter-units.</span>")
|
||||
SStgui.update_uis(src)
|
||||
|
||||
/**
|
||||
* Creates and displays a radial menu to a user when they trigger the `attack_self` of the RCD.
|
||||
*
|
||||
* Arguments:
|
||||
* * user - the mob trying to open the RCD radial.
|
||||
*/
|
||||
/obj/item/rcd/proc/radial_menu(mob/user)
|
||||
if(!check_menu(user))
|
||||
return
|
||||
var/list/choices = list(
|
||||
RCD_MODE_AIRLOCK = image(icon = 'icons/obj/interface.dmi', icon_state = "airlock"),
|
||||
RCD_MODE_DECON = image(icon = 'icons/obj/interface.dmi', icon_state = "delete"),
|
||||
RCD_MODE_WINDOW = image(icon = 'icons/obj/interface.dmi', icon_state = "grillewindow"),
|
||||
RCD_MODE_TURF = image(icon = 'icons/obj/interface.dmi', icon_state = "wallfloor"),
|
||||
MODE_AIRLOCK = image(icon = 'icons/obj/interface.dmi', icon_state = "airlock"),
|
||||
MODE_DECON = image(icon = 'icons/obj/interface.dmi', icon_state = "delete"),
|
||||
MODE_WINDOW = image(icon = 'icons/obj/interface.dmi', icon_state = "grillewindow"),
|
||||
MODE_TURF = image(icon = 'icons/obj/interface.dmi', icon_state = "wallfloor"),
|
||||
"UI" = image(icon = 'icons/obj/interface.dmi', icon_state = "ui_interact")
|
||||
)
|
||||
if(mode == RCD_MODE_AIRLOCK)
|
||||
if(mode == MODE_AIRLOCK)
|
||||
choices += list(
|
||||
"Change Access" = image(icon = 'icons/obj/interface.dmi', icon_state = "access"),
|
||||
"Change Airlock Type" = image(icon = 'icons/obj/interface.dmi', icon_state = "airlocktype")
|
||||
@@ -144,15 +196,18 @@ GLOBAL_LIST_INIT(rcd_door_types, list(
|
||||
if(!check_menu(user))
|
||||
return
|
||||
switch(choice)
|
||||
if(RCD_MODE_AIRLOCK, RCD_MODE_DECON, RCD_MODE_WINDOW, RCD_MODE_TURF)
|
||||
if(MODE_AIRLOCK, MODE_DECON, MODE_WINDOW, MODE_TURF)
|
||||
mode = choice
|
||||
if("UI")
|
||||
menu = RCD_PAGE_MAIN
|
||||
ui_interact(user)
|
||||
tgui_interact(user)
|
||||
return
|
||||
if("Change Access", "Change Airlock Type")
|
||||
menu = RCD_PAGE_AIRLOCK
|
||||
ui_interact(user)
|
||||
if("Change Access")
|
||||
ui_tab = TAB_AIRLOCK_ACCESS
|
||||
tgui_interact(user)
|
||||
return
|
||||
if("Change Airlock Type")
|
||||
ui_tab = TAB_AIRLOCK_TYPE
|
||||
tgui_interact(user)
|
||||
return
|
||||
else
|
||||
return
|
||||
@@ -167,87 +222,131 @@ GLOBAL_LIST_INIT(rcd_door_types, list(
|
||||
/obj/item/rcd/attack_self_tk(mob/user)
|
||||
radial_menu(user)
|
||||
|
||||
/obj/item/rcd/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.inventory_state)
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
/obj/item/rcd/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_inventory_state)
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "rcd.tmpl", "[name]", 450, 400, state = state)
|
||||
ui = new(user, src, ui_key, "RCD", "Rapid Construction Device", 471, 673, master_ui, state)
|
||||
ui.open()
|
||||
ui.set_auto_update(1)
|
||||
|
||||
/obj/item/rcd/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.inventory_state)
|
||||
var/data[0]
|
||||
data["mode"] = mode
|
||||
data["door_type"] = door_type
|
||||
data["door_name"] = door_name
|
||||
data["menu"] = menu
|
||||
data["matter"] = matter
|
||||
data["max_matter"] = max_matter
|
||||
data["one_access"] = one_access
|
||||
data["locked"] = locked
|
||||
|
||||
if(menu == RCD_PAGE_AIRLOCK)
|
||||
var/list/door_types_list = list()
|
||||
for(var/type in GLOB.rcd_door_types)
|
||||
door_types_list[++door_types_list.len] = list("name" = GLOB.rcd_door_types[type], "type" = type)
|
||||
data["allowed_door_types"] = door_types_list
|
||||
|
||||
data["door_accesses"] = door_accesses_list
|
||||
|
||||
/obj/item/rcd/tgui_data(mob/user)
|
||||
var/list/data = list(
|
||||
"tab" = ui_tab,
|
||||
"mode" = mode,
|
||||
"locked" = locked,
|
||||
"matter" = matter,
|
||||
"door_type" = door_type,
|
||||
"door_name" = door_name,
|
||||
"one_access" = one_access,
|
||||
"selected_accesses" = selected_accesses,
|
||||
"modal" = tgui_modal_data(src)
|
||||
)
|
||||
return data
|
||||
|
||||
/obj/item/rcd/Topic(href, href_list, nowindow, state)
|
||||
/obj/item/rcd/tgui_static_data(mob/user)
|
||||
var/list/data = list(
|
||||
"max_matter" = max_matter,
|
||||
"regions" = get_accesslist_static_data(REGION_GENERAL, REGION_COMMAND),
|
||||
"door_accesses_list" = door_accesses_list,
|
||||
"door_types_ui_list" = door_types_ui_list
|
||||
)
|
||||
return data
|
||||
|
||||
/obj/item/rcd/tgui_act(action, list/params)
|
||||
if(..())
|
||||
return 1
|
||||
return
|
||||
|
||||
if(prob(20))
|
||||
spark_system.start()
|
||||
|
||||
if(href_list["mode"])
|
||||
mode = href_list["mode"]
|
||||
. = 1
|
||||
if(tgui_act_modal(action, params))
|
||||
return TRUE
|
||||
|
||||
if(href_list["door_type"])
|
||||
var/new_door_type = text2path(href_list["door_type"])
|
||||
if(!(new_door_type in GLOB.rcd_door_types))
|
||||
message_admins("RCD Door HREF exploit attempted by [key_name(usr)]!")
|
||||
return
|
||||
door_type = new_door_type
|
||||
. = 1
|
||||
. = TRUE
|
||||
switch(action)
|
||||
if("set_tab")
|
||||
var/tab = text2num(params["tab"])
|
||||
if(!(tab in list(TAB_AIRLOCK_TYPE, TAB_AIRLOCK_ACCESS)))
|
||||
return FALSE
|
||||
ui_tab = tab
|
||||
|
||||
if(href_list["menu"])
|
||||
menu = text2num(href_list["menu"])
|
||||
. = 1
|
||||
if("mode")
|
||||
var/new_mode = params["mode"]
|
||||
if(!(new_mode in list(MODE_TURF, MODE_AIRLOCK, MODE_DECON, MODE_WINDOW)))
|
||||
return FALSE
|
||||
mode = new_mode
|
||||
|
||||
if(href_list["login"])
|
||||
if(allowed(usr))
|
||||
locked = FALSE
|
||||
. = 1
|
||||
if("door_type")
|
||||
var/new_door_type = text2path(params["door_type"])
|
||||
if(!(new_door_type in rcd_door_types))
|
||||
message_admins("RCD Door HREF exploit attempted by [key_name(usr)]!")
|
||||
return FALSE
|
||||
door_type = new_door_type
|
||||
|
||||
if(href_list["logout"])
|
||||
locked = TRUE
|
||||
. = 1
|
||||
if("set_lock")
|
||||
if(!allowed(usr))
|
||||
to_chat(usr, "<span class='warning'>Access denied.</span>")
|
||||
return FALSE
|
||||
locked = params["new_lock"] == "lock" ? TRUE : FALSE
|
||||
|
||||
if(!locked)
|
||||
if(href_list["toggle_one_access"])
|
||||
one_access = !one_access
|
||||
. = 1
|
||||
if("set_one_access")
|
||||
one_access = params["access"] == "one" ? TRUE : FALSE
|
||||
|
||||
if(href_list["toggle_access"])
|
||||
var/href_access = text2num(href_list["toggle_access"])
|
||||
if(href_access in door_accesses)
|
||||
door_accesses -= href_access
|
||||
if("set")
|
||||
var/access = text2num(params["access"])
|
||||
if(isnull(access))
|
||||
return
|
||||
if(access in selected_accesses)
|
||||
selected_accesses -= access
|
||||
else
|
||||
door_accesses += href_access
|
||||
door_accesses_list = list()
|
||||
for(var/access in get_all_accesses())
|
||||
door_accesses_list[++door_accesses_list.len] = list("name" = get_access_desc(access), "id" = access, "enabled" = (access in door_accesses))
|
||||
. = 1
|
||||
selected_accesses |= access
|
||||
|
||||
if(href_list["choice"])
|
||||
var/temp_t = sanitize(copytext(input("Enter a custom Airlock Name.", "Airlock Name"), 1, MAX_MESSAGE_LEN))
|
||||
if(temp_t)
|
||||
door_name = temp_t
|
||||
if("grant_region")
|
||||
var/region = text2num(params["region"])
|
||||
if(isnull(region) || region < REGION_GENERAL || region > REGION_COMMAND)
|
||||
return
|
||||
selected_accesses |= get_region_accesses(region)
|
||||
|
||||
if("deny_region")
|
||||
var/region = text2num(params["region"])
|
||||
if(isnull(region) || region < REGION_GENERAL || region > REGION_COMMAND)
|
||||
return
|
||||
selected_accesses -= get_region_accesses(region)
|
||||
|
||||
if("clear_all")
|
||||
selected_accesses = list()
|
||||
|
||||
if("grant_all")
|
||||
selected_accesses = get_all_accesses()
|
||||
|
||||
/**
|
||||
* Called in tgui_act() to process modal actions
|
||||
*
|
||||
* Arguments:
|
||||
* * action - The action passed by tgui
|
||||
* * params - The params passed by tgui
|
||||
*/
|
||||
/obj/item/rcd/proc/tgui_act_modal(action, list/params)
|
||||
. = TRUE
|
||||
switch(tgui_modal_act(src, action, params))
|
||||
if(TGUI_MODAL_OPEN)
|
||||
tgui_modal_input(src, "renameAirlock", "Enter a new name:", value = door_name, max_length = TGUI_MODAL_INPUT_MAX_LENGTH_NAME)
|
||||
if(TGUI_MODAL_ANSWER)
|
||||
var/answer = params["answer"]
|
||||
if(!answer)
|
||||
return
|
||||
door_name = sanitize(copytext(answer, 1, TGUI_MODAL_INPUT_MAX_LENGTH_NAME))
|
||||
else
|
||||
return FALSE
|
||||
|
||||
/**
|
||||
* Called in `afterattack()` if `mode` is set to `MODE_TURF`.
|
||||
*
|
||||
* Creates either a plating, or a wall, depending on the turf that already exists at the location.
|
||||
*
|
||||
* Arguments:
|
||||
* * A - the location we're trying to build at.
|
||||
* * user - the mob using the RCD.
|
||||
*/
|
||||
/obj/item/rcd/proc/mode_turf(atom/A, mob/user)
|
||||
if(isspaceturf(A) || istype(A, /obj/structure/lattice))
|
||||
if(useResource(1, user))
|
||||
@@ -279,6 +378,15 @@ GLOBAL_LIST_INIT(rcd_door_types, list(
|
||||
playsound(loc, 'sound/machines/click.ogg', 50, 1)
|
||||
return FALSE
|
||||
|
||||
/**
|
||||
* Called in `afterattack()` if `mode` is set to `MODE_AIRLOCK`.
|
||||
*
|
||||
* Creates an `door_type` airlock at the given location `A`, and assigns it accesses from `selected_accesses`.
|
||||
*
|
||||
* Arguments:
|
||||
* * A - the location we're trying to build at.
|
||||
* * user - the mob using the RCD.
|
||||
*/
|
||||
/obj/item/rcd/proc/mode_airlock(atom/A, mob/user)
|
||||
if(isfloorturf(A))
|
||||
if(checkResource(10, user))
|
||||
@@ -294,9 +402,9 @@ GLOBAL_LIST_INIT(rcd_door_types, list(
|
||||
T.name = door_name
|
||||
T.autoclose = TRUE
|
||||
if(one_access)
|
||||
T.req_one_access = door_accesses.Copy()
|
||||
T.req_one_access = selected_accesses.Copy()
|
||||
else
|
||||
T.req_access = door_accesses.Copy()
|
||||
T.req_access = selected_accesses.Copy()
|
||||
return FALSE
|
||||
return FALSE
|
||||
to_chat(user, "<span class='warning'>ERROR! Not enough matter in unit to construct this airlock!</span>")
|
||||
@@ -306,6 +414,16 @@ GLOBAL_LIST_INIT(rcd_door_types, list(
|
||||
playsound(loc, 'sound/machines/click.ogg', 50, 1)
|
||||
return FALSE
|
||||
|
||||
/**
|
||||
* Called in `afterattack()` if `mode` is set to `MODE_DECON`.
|
||||
*
|
||||
* Deconstrcts the target atom `A`.
|
||||
* Valid atoms are: basic walls, reinforced walls (if `canRwall` is `TRUE`), airlocks, and windows.
|
||||
*
|
||||
* Arguments:
|
||||
* * A - the location we're trying to build at.
|
||||
* * user - the mob using the RCD.
|
||||
*/
|
||||
/obj/item/rcd/proc/mode_decon(atom/A, mob/user)
|
||||
if(iswallturf(A))
|
||||
if(istype(A, /turf/simulated/wall/r_wall) && !canRwall)
|
||||
@@ -362,13 +480,13 @@ GLOBAL_LIST_INIT(rcd_door_types, list(
|
||||
if(!checkResource(2, user))
|
||||
to_chat(user, "<span class='warning'>ERROR! Not enough matter in unit to deconstruct this window!</span>")
|
||||
playsound(loc, 'sound/machines/click.ogg', 50, 1)
|
||||
return 0
|
||||
return FALSE
|
||||
to_chat(user, "Deconstructing window...")
|
||||
playsound(loc, 'sound/machines/click.ogg', 50, 1)
|
||||
if(!do_after(user, 20 * toolspeed, target = A))
|
||||
return 0
|
||||
return FALSE
|
||||
if(!useResource(2, user))
|
||||
return 0
|
||||
return FALSE
|
||||
playsound(loc, usesound, 50, 1)
|
||||
var/turf/T1 = get_turf(A)
|
||||
QDEL_NULL(A)
|
||||
@@ -388,22 +506,31 @@ GLOBAL_LIST_INIT(rcd_door_types, list(
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/**
|
||||
* Called in `afterattack()` if `mode` is set to `MODE_WINDOW`.
|
||||
*
|
||||
* Constructs a grille and 4 reinforced window panes at the given location `A`.
|
||||
*
|
||||
* Arguments:
|
||||
* * A - the location we're trying to build at.
|
||||
* * user - the mob using the RCD.
|
||||
*/
|
||||
/obj/item/rcd/proc/mode_window(atom/A, mob/user)
|
||||
if(isfloorturf(A))
|
||||
if(locate(/obj/structure/grille) in A)
|
||||
return 0 // We already have window
|
||||
return FALSE // We already have window
|
||||
if(!checkResource(2, user))
|
||||
to_chat(user, "<span class='warning'>ERROR! Not enough matter in unit to construct this window!</span>")
|
||||
playsound(loc, 'sound/machines/click.ogg', 50, 1)
|
||||
return 0
|
||||
return FALSE
|
||||
to_chat(user, "Constructing window...")
|
||||
playsound(loc, 'sound/machines/click.ogg', 50, 1)
|
||||
if(!do_after(user, 20 * toolspeed, target = A))
|
||||
return 0
|
||||
return FALSE
|
||||
if(locate(/obj/structure/grille) in A)
|
||||
return 0 // We already have window
|
||||
return FALSE // We already have window
|
||||
if(!useResource(2, user))
|
||||
return 0
|
||||
return FALSE
|
||||
playsound(loc, usesound, 50, 1)
|
||||
new /obj/structure/grille(A)
|
||||
for(var/obj/structure/window/W in A)
|
||||
@@ -419,10 +546,10 @@ GLOBAL_LIST_INIT(rcd_door_types, list(
|
||||
W.dir = cdir
|
||||
var/turf/AT = A
|
||||
AT.ChangeTurf(/turf/simulated/floor/plating) // Platings go under windows.
|
||||
return 1
|
||||
return TRUE
|
||||
to_chat(user, "<span class='warning'>ERROR! Location unsuitable for window construction!</span>")
|
||||
playsound(loc, 'sound/machines/click.ogg', 50, 1)
|
||||
return 0
|
||||
return FALSE
|
||||
|
||||
/obj/item/rcd/afterattack(atom/A, mob/user, proximity)
|
||||
if(!proximity)
|
||||
@@ -433,54 +560,77 @@ GLOBAL_LIST_INIT(rcd_door_types, list(
|
||||
return FALSE
|
||||
|
||||
switch(mode)
|
||||
if(RCD_MODE_TURF)
|
||||
if(MODE_TURF)
|
||||
. = mode_turf(A, user)
|
||||
if(RCD_MODE_AIRLOCK)
|
||||
if(MODE_AIRLOCK)
|
||||
. = mode_airlock(A, user)
|
||||
if(RCD_MODE_DECON)
|
||||
if(MODE_DECON)
|
||||
. = mode_decon(A, user)
|
||||
if(RCD_MODE_WINDOW)
|
||||
if(MODE_WINDOW)
|
||||
. = mode_window(A, user)
|
||||
else
|
||||
to_chat(user, "ERROR: RCD in MODE: [mode] attempted use by [user]. Send this text #coderbus or an admin.")
|
||||
. = 0
|
||||
|
||||
SSnanoui.update_uis(src)
|
||||
SStgui.update_uis(src)
|
||||
|
||||
/**
|
||||
* Called in each of the four build modes after an object is successfully built.
|
||||
*
|
||||
* Subtracts the amount of matter used from `matter`.
|
||||
*
|
||||
* Arguments:
|
||||
* * amount - the amount of matter that was used.
|
||||
*/
|
||||
/obj/item/rcd/proc/useResource(amount, mob/user)
|
||||
if(matter < amount)
|
||||
return 0
|
||||
return FALSE
|
||||
matter -= amount
|
||||
SSnanoui.update_uis(src)
|
||||
return 1
|
||||
SStgui.update_uis(src)
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* Called in each of the four build modes before an object gets build. Makes sure there is enough matter to build the object.
|
||||
*
|
||||
* Arguments:
|
||||
* * amount - an amount of matter to check for
|
||||
*/
|
||||
/obj/item/rcd/proc/checkResource(amount, mob/user)
|
||||
return matter >= amount
|
||||
|
||||
/obj/item/rcd/borg
|
||||
canRwall = 1
|
||||
var/use_multiplier = 160
|
||||
canRwall = TRUE
|
||||
/// A multipler which is applied to matter amount checks. A higher number means more power usage per RCD usage.
|
||||
var/power_use_multiplier = 160
|
||||
|
||||
/obj/item/rcd/borg/syndicate
|
||||
use_multiplier = 80
|
||||
power_use_multiplier = 80
|
||||
|
||||
/obj/item/rcd/borg/useResource(amount, mob/user)
|
||||
if(!isrobot(user))
|
||||
return 0
|
||||
return FALSE
|
||||
var/mob/living/silicon/robot/R = user
|
||||
return R.cell.use(amount * use_multiplier)
|
||||
return R.cell.use(amount * power_use_multiplier)
|
||||
|
||||
/obj/item/rcd/borg/checkResource(amount, mob/user)
|
||||
if(!isrobot(user))
|
||||
return 0
|
||||
return FALSE
|
||||
var/mob/living/silicon/robot/R = user
|
||||
return R.cell.charge >= (amount * use_multiplier)
|
||||
return R.cell.charge >= (amount * power_use_multiplier)
|
||||
|
||||
/**
|
||||
* Called from malf AI's "detonate RCD" ability.
|
||||
*
|
||||
* Creates a delayed explosion centered around the RCD.
|
||||
*/
|
||||
/obj/item/rcd/proc/detonate_pulse()
|
||||
audible_message("<span class='danger'><b>[src] begins to vibrate and buzz loudly!</b></span>", "<span class='danger'><b>[src] begins vibrating violently!</b></span>")
|
||||
// 5 seconds to get rid of it
|
||||
addtimer(CALLBACK(src, .proc/detonate_pulse_explode), 50)
|
||||
|
||||
/**
|
||||
* Called in `/obj/item/rcd/proc/detonate_pulse()` via callback.
|
||||
*/
|
||||
/obj/item/rcd/proc/detonate_pulse_explode()
|
||||
explosion(src, 0, 0, 3, 1, flame_range = 1)
|
||||
qdel(src)
|
||||
@@ -490,9 +640,9 @@ GLOBAL_LIST_INIT(rcd_door_types, list(
|
||||
|
||||
/obj/item/rcd/combat
|
||||
name = "combat RCD"
|
||||
max_matter = 500
|
||||
matter = 500
|
||||
canRwall = 1
|
||||
max_matter = MATTER_500
|
||||
matter = MATTER_500
|
||||
canRwall = TRUE
|
||||
|
||||
/obj/item/rcd_ammo
|
||||
name = "compressed matter cartridge"
|
||||
@@ -500,12 +650,15 @@ GLOBAL_LIST_INIT(rcd_door_types, list(
|
||||
icon = 'icons/obj/ammo.dmi'
|
||||
icon_state = "rcd"
|
||||
item_state = "rcdammo"
|
||||
opacity = 0
|
||||
density = 0
|
||||
anchored = 0.0
|
||||
opacity = FALSE
|
||||
density = FALSE
|
||||
anchored = FALSE
|
||||
origin_tech = "materials=3"
|
||||
materials = list(MAT_METAL=16000, MAT_GLASS=8000)
|
||||
var/ammoamt = 20
|
||||
|
||||
/obj/item/rcd_ammo/large
|
||||
ammoamt = 100
|
||||
|
||||
#undef MATTER_100
|
||||
#undef MATTER_500
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
/obj/item/firework
|
||||
name = "fireworks"
|
||||
icon = 'icons/obj/fireworks.dmi'
|
||||
icon_state = "rocket_0"
|
||||
var/litzor = 0
|
||||
var/datum/effect_system/sparkle_spread/S
|
||||
|
||||
/obj/item/firework/attackby(obj/item/W,mob/user, params)
|
||||
if(litzor)
|
||||
return
|
||||
if(is_hot(W))
|
||||
for(var/mob/M in viewers(user))
|
||||
to_chat(M, "[user] lits \the [src]")
|
||||
litzor = 1
|
||||
icon_state = "rocket_1"
|
||||
S = new()
|
||||
S.set_up(5,0,src.loc)
|
||||
sleep(30)
|
||||
if(ismob(src.loc) || isobj(src.loc))
|
||||
S.attach(src.loc)
|
||||
S.start()
|
||||
qdel(src)
|
||||
|
||||
/obj/item/sparkler
|
||||
name = "sparkler"
|
||||
icon = 'icons/obj/fireworks.dmi'
|
||||
icon_state = "sparkler_0"
|
||||
var/litzor = 0
|
||||
|
||||
/obj/item/sparkler/attackby(obj/item/W,mob/user, params)
|
||||
if(litzor)
|
||||
return
|
||||
if(is_hot(W))
|
||||
for(var/mob/M in viewers(user))
|
||||
to_chat(M, "[user] lits \the [src]")
|
||||
litzor = 1
|
||||
icon_state = "sparkler_1"
|
||||
var/b = rand(5,9)
|
||||
for(var/xy, xy<=b, xy++)
|
||||
do_sparks(1, 0, loc)
|
||||
sleep(10)
|
||||
qdel(src)
|
||||
|
||||
// TODO: Refactor this into a proper locker or something
|
||||
// Or just axe the system. This code is 7 years old
|
||||
/obj/crate/fireworks
|
||||
name = "Fireworks!"
|
||||
|
||||
/obj/crate/fireworks/New()
|
||||
new /obj/item/sparkler(src)
|
||||
new /obj/item/sparkler(src)
|
||||
new /obj/item/sparkler(src)
|
||||
new /obj/item/sparkler(src)
|
||||
new /obj/item/sparkler(src)
|
||||
new /obj/item/sparkler(src)
|
||||
new /obj/item/sparkler(src)
|
||||
new /obj/item/sparkler(src)
|
||||
new /obj/item/firework(src)
|
||||
new /obj/item/firework(src)
|
||||
new /obj/item/firework(src)
|
||||
new /obj/item/firework(src)
|
||||
new /obj/item/firework(src)
|
||||
new /obj/item/firework(src)
|
||||
new /obj/item/firework(src)
|
||||
new /obj/item/firework(src)
|
||||
new /obj/item/firework(src)
|
||||
new /obj/item/firework(src)
|
||||
@@ -17,6 +17,10 @@
|
||||
do_sparks(rand(5, 9), FALSE, src)
|
||||
playsound(T, 'sound/effects/bang.ogg', 100, TRUE)
|
||||
new /obj/effect/dummy/lighting_obj(T, light_color, range + 2, light_power, light_time)
|
||||
// Blob damage
|
||||
for(var/obj/structure/blob/B in hear(range + 1, T))
|
||||
var/damage = round(30 / (get_dist(B, T) + 1))
|
||||
B.take_damage(damage, BURN, "melee", FALSE)
|
||||
|
||||
// Stunning & damaging mechanic
|
||||
bang(T, src, range)
|
||||
@@ -25,7 +29,6 @@
|
||||
/**
|
||||
* Creates a flashing effect that blinds and deafens mobs within range
|
||||
*
|
||||
* Also damages blobs
|
||||
* Arguments:
|
||||
* * T - The turf to flash
|
||||
* * A - The flashing atom
|
||||
@@ -34,10 +37,6 @@
|
||||
* * bang - Whether to bang (deafen)
|
||||
*/
|
||||
/proc/bang(turf/T, atom/A, range = 7, flash = TRUE, bang = TRUE)
|
||||
// Blob damage
|
||||
for(var/obj/structure/blob/B in hear(range + 1, T))
|
||||
var/damage = round(30 / (get_dist(B, T) + 1))
|
||||
B.take_damage(damage, BURN, "melee", FALSE)
|
||||
|
||||
// Flashing mechanic
|
||||
var/source_turf = get_turf(A)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// TODO: Refactor these into spawners
|
||||
/obj/random
|
||||
name = "Random Object"
|
||||
desc = "This item type is used to spawn random objects at round-start"
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
icon_state = "spirit_board"
|
||||
density = 1
|
||||
anchored = 0
|
||||
var/virgin = 1
|
||||
var/used = FALSE
|
||||
var/cooldown = 0
|
||||
var/planchette = "A"
|
||||
var/lastuser = null
|
||||
@@ -28,8 +28,8 @@
|
||||
if(!spirit_board_checks(M))
|
||||
return 0
|
||||
|
||||
if(virgin)
|
||||
virgin = 0
|
||||
if(!used)
|
||||
used = TRUE
|
||||
notify_ghosts("Someone has begun playing with a [src.name] in [get_area(src)]!", source = src)
|
||||
|
||||
planchette = input("Choose the letter.", "Seance!") in list("A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z")
|
||||
|
||||
@@ -145,7 +145,11 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
|
||||
GLOB.LastAdminCalledTargetUID = target.UID()
|
||||
GLOB.AdminProcCaller = ckey //if this runtimes, too bad for you
|
||||
++GLOB.AdminProcCallCount
|
||||
. = world.WrapAdminProcCall(target, procname, arguments)
|
||||
try
|
||||
. = world.WrapAdminProcCall(target, procname, arguments)
|
||||
catch
|
||||
to_chat(usr, "<span class='adminnotice'>Your proc call failed to execute, likely from runtimes. You <i>should</i> be out of safety mode. If not, god help you.</span>")
|
||||
|
||||
if(--GLOB.AdminProcCallCount == 0)
|
||||
GLOB.AdminProcCaller = null
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ GLOBAL_VAR_INIT(vox_tick, 1)
|
||||
equip_to_slot_or_del(new /obj/item/clothing/glasses/thermal/monocle(src), slot_glasses) // REPLACE WITH CODED VOX ALTERNATIVE.
|
||||
equip_to_slot_or_del(new /obj/item/chameleon(src), slot_l_store)
|
||||
|
||||
var/obj/item/gun/projectile/automatic/spikethrower/W = new(src)
|
||||
var/obj/item/gun/energy/spikethrower/W = new(src)
|
||||
equip_to_slot_or_del(W, slot_r_hand)
|
||||
|
||||
|
||||
|
||||
@@ -88,8 +88,9 @@
|
||||
return 0
|
||||
while(possible_areas.len)
|
||||
//randomly select an area from our possible_areas list to try spawning in, then remove it from possible_areas so it won't get picked over and over forever.
|
||||
var/area/spawn_area = locate(pickweight(possible_areas))
|
||||
possible_areas -= spawn_area
|
||||
var/spawn_area_path = pickweight(possible_areas)
|
||||
var/area/spawn_area = locate(spawn_area_path)
|
||||
possible_areas -= spawn_area_path
|
||||
if(!spawn_area)
|
||||
break
|
||||
//clear and generate a fresh list of turfs in the selected area, weighted based on white/black lists
|
||||
|
||||
@@ -2104,9 +2104,9 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts
|
||||
toggles ^= PREFTOGGLE_AMBIENT_OCCLUSION
|
||||
if(parent && parent.screen && parent.screen.len)
|
||||
var/obj/screen/plane_master/game_world/PM = locate(/obj/screen/plane_master/game_world) in parent.screen
|
||||
PM.remove_filter(AMBIENT_OCCLUSION_FILTER_KEY)
|
||||
PM.filters -= FILTER_AMBIENT_OCCLUSION
|
||||
if(toggles & PREFTOGGLE_AMBIENT_OCCLUSION)
|
||||
PM.add_filter(AMBIENT_OCCLUSION_FILTER_KEY, FILTER_AMBIENT_OCCLUSION)
|
||||
PM.filters += FILTER_AMBIENT_OCCLUSION
|
||||
|
||||
if("parallax")
|
||||
var/parallax_styles = list(
|
||||
|
||||
@@ -200,13 +200,24 @@
|
||||
/obj/item/clothing/glasses/hud/skills
|
||||
name = "Skills HUD"
|
||||
desc = "A heads-up display capable of showing the employment history records of NT crew members."
|
||||
icon_state = "material"
|
||||
icon_state = "skill"
|
||||
item_state = "glasses"
|
||||
sprite_sheets = list(
|
||||
"Drask" = 'icons/mob/species/drask/eyes.dmi',
|
||||
"Grey" = 'icons/mob/species/grey/eyes.dmi',
|
||||
"Vox" = 'icons/mob/species/vox/eyes.dmi'
|
||||
)
|
||||
|
||||
/obj/item/clothing/glasses/hud/skills/sunglasses
|
||||
name = "Skills HUD Sunglasses"
|
||||
desc = "Sunglasses with a build-in skills HUD, showing the employment history of nearby NT crew members."
|
||||
icon_state = "sunhudskill"
|
||||
see_in_dark = 1 // None of these three can be converted to booleans. Do not try it.
|
||||
flash_protect = 1
|
||||
tint = 1
|
||||
prescription_upgradable = TRUE
|
||||
sprite_sheets = list(
|
||||
"Drask" = 'icons/mob/species/drask/eyes.dmi',
|
||||
"Grey" = 'icons/mob/species/grey/eyes.dmi',
|
||||
"Vox" = 'icons/mob/species/vox/eyes.dmi'
|
||||
)
|
||||
|
||||
@@ -1,537 +0,0 @@
|
||||
/************************
|
||||
* Point Of Sale
|
||||
*
|
||||
* Takes cash or credit.
|
||||
*************************/
|
||||
|
||||
/line_item
|
||||
parent_type = /datum
|
||||
|
||||
var/name = ""
|
||||
var/price = 0 // Per unit
|
||||
var/units = 0
|
||||
|
||||
GLOBAL_VAR_INIT(current_pos_id, 1)
|
||||
GLOBAL_VAR_INIT(pos_sales, 0)
|
||||
|
||||
#define RECEIPT_HEADER {"<html>
|
||||
<head>
|
||||
<style type="text/css">
|
||||
html {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
table {
|
||||
margin: auto;
|
||||
margin-top: 1em;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
th, td {
|
||||
padding: 1px;
|
||||
margin: 0;
|
||||
}
|
||||
td,
|
||||
tr.first th {
|
||||
border-left: 1px solid #ccc;
|
||||
}
|
||||
tr.first th.first {
|
||||
border-left: none;
|
||||
}
|
||||
tr.even td,
|
||||
tr.even th {
|
||||
background: #f0f0f0;
|
||||
}
|
||||
tr.calculated {
|
||||
font-style: italic;
|
||||
}
|
||||
tr.calculated td {
|
||||
border-top: 1px solid #000;
|
||||
border-left: 1px solid #ccc;
|
||||
background: #fefefe;
|
||||
}
|
||||
tr.total {
|
||||
font-weight: bold
|
||||
}
|
||||
tr.total td {
|
||||
border-top: 1px solid #000;
|
||||
background: #dfdfdf;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
"}
|
||||
#define POS_HEADER {"<html>
|
||||
<head>
|
||||
<style type="text/css">
|
||||
* {
|
||||
font-family: sans-serif;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
table {
|
||||
margin: auto;
|
||||
margin-top: 1em;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
th, td {
|
||||
padding: 1px;
|
||||
margin: 0;
|
||||
}
|
||||
td,
|
||||
tr.first th {
|
||||
border-left: 1px solid #ccc;
|
||||
}
|
||||
tr.first th.first {
|
||||
border-left: none;
|
||||
}
|
||||
tr.even td,
|
||||
tr.even th {
|
||||
background: #f0f0f0;
|
||||
}
|
||||
tr.calculated {
|
||||
font-style: italic;
|
||||
}
|
||||
tr.calculated td {
|
||||
border-top: 1px solid #000;
|
||||
border-left: 1px solid #ccc;
|
||||
background: #fefefe;
|
||||
}
|
||||
tr.total {
|
||||
font-weight: bold
|
||||
}
|
||||
tr.total td {
|
||||
border-top: 1px solid #000;
|
||||
background: #dfdfdf;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
"}
|
||||
|
||||
#define POS_TAX_RATE 0.10 // 10%
|
||||
|
||||
#define POS_SCREEN_LOGIN 0
|
||||
#define POS_SCREEN_ORDER 1
|
||||
#define POS_SCREEN_FINALIZE 2
|
||||
#define POS_SCREEN_PRODUCTS 3
|
||||
#define POS_SCREEN_IMPORT 4
|
||||
#define POS_SCREEN_EXPORT 5
|
||||
#define POS_SCREEN_SETTINGS 6
|
||||
/obj/machinery/pos
|
||||
icon = 'icons/obj/machines/pos.dmi'
|
||||
icon_state = "pos"
|
||||
density = 0
|
||||
name = "point of sale"
|
||||
desc = "Also known as a cash register, or, more commonly, \"robbery magnet\"."
|
||||
|
||||
var/id = 0
|
||||
var/sales = 0
|
||||
var/department
|
||||
var/mob/logged_in
|
||||
var/datum/money_account/linked_account
|
||||
|
||||
var/credits_held = 0
|
||||
var/credits_needed = 0
|
||||
|
||||
var/list/products = list() // name = /line_item
|
||||
var/list/line_items = list() // # = /line_item
|
||||
|
||||
var/screen=POS_SCREEN_LOGIN
|
||||
|
||||
/obj/machinery/pos/New()
|
||||
..()
|
||||
id = GLOB.current_pos_id++
|
||||
if(department)
|
||||
linked_account = GLOB.department_accounts[department]
|
||||
else
|
||||
linked_account = GLOB.station_account
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/pos/proc/AddToOrder(var/name, var/units)
|
||||
if(!(name in products))
|
||||
return 0
|
||||
var/line_item/LI = products[name]
|
||||
var/line_item/LIC = new
|
||||
LIC.name=LI.name
|
||||
LIC.price=LI.price
|
||||
LIC.units=units
|
||||
line_items.Add(LIC)
|
||||
|
||||
/obj/machinery/pos/proc/RemoveFromOrder(var/order_id)
|
||||
line_items.Cut(order_id,order_id+1)
|
||||
|
||||
/obj/machinery/pos/proc/NewOrder()
|
||||
line_items.Cut()
|
||||
|
||||
/obj/machinery/pos/proc/PrintReceipt(var/order_id)
|
||||
var/receipt = {"[RECEIPT_HEADER]<div>POINT OF SALE #[id]<br />
|
||||
Paying to: [linked_account.owner_name]<br />
|
||||
Cashier: [logged_in]<br />"}
|
||||
if(myArea)
|
||||
receipt += myArea.name
|
||||
receipt += "</div>"
|
||||
receipt += {"<br />
|
||||
<div>[station_time_timestamp()], [GLOB.current_date_string]</div>
|
||||
<table>
|
||||
<tr class=\"first\">
|
||||
<th class=\"first\">Item</th>
|
||||
<th>Amount</th>
|
||||
<th>Unit Price</th>
|
||||
<th>Line Total</th>
|
||||
</tr>"}
|
||||
var/subtotal=0
|
||||
for(var/i=1;i<=line_items.len;i++)
|
||||
var/line_item/LI = line_items[i]
|
||||
var/linetotal=LI.units*LI.price
|
||||
receipt += "<tr class=\"[(i%2)?"even":"odd"]\"><th>[LI.name]</th><td>[LI.units]</td><td>$[num2septext(LI.price)]</td><td>$[num2septext(linetotal)]</td></tr>"
|
||||
subtotal += linetotal
|
||||
var/taxes = POS_TAX_RATE*subtotal
|
||||
receipt += {"
|
||||
<tr class="calculated">
|
||||
<th colspan="3">SUBTOTAL</th><td>$[num2septext(subtotal)]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th colspan="3">TAXES</th><td>$[num2septext(taxes)]</td>
|
||||
</tr>"}
|
||||
receipt += {"
|
||||
<tr class="calculated total">
|
||||
<th colspan="3">TOTAL</th><td>$[num2septext(taxes+subtotal)]</th>
|
||||
</tr>"}
|
||||
receipt += "</table></body></html>"
|
||||
|
||||
playsound(loc, 'sound/goonstation/machines/printer_thermal.ogg', 50, 1)
|
||||
var/obj/item/paper/P = new(loc)
|
||||
P.name="Receipt #[id]-[++sales]"
|
||||
P.info=receipt
|
||||
|
||||
P = new(loc)
|
||||
P.name="Receipt #[id]-[sales] (Cashier Copy)"
|
||||
P.info=receipt
|
||||
|
||||
|
||||
/obj/machinery/pos/proc/LoginScreen()
|
||||
return "<center><b>Please swipe ID to log in.</b></center>"
|
||||
|
||||
/obj/machinery/pos/proc/OrderScreen()
|
||||
var/receipt = {"<fieldset>
|
||||
<legend>POS Info</legend>
|
||||
POINT OF SALE #[id]<br />
|
||||
Paying to: [linked_account.owner_name]<br />
|
||||
Cashier: [logged_in]<br />"}
|
||||
if(myArea)
|
||||
receipt += myArea.name
|
||||
receipt += "</fieldset>"
|
||||
receipt += {"<fieldset><legend>Order Data</legend>
|
||||
<form action="?src=[UID()]" method="get">
|
||||
<input type="hidden" name="src" value="[UID()]" />
|
||||
<table>
|
||||
<tr class=\"first\">
|
||||
<th class=\"first\">Item</th>
|
||||
<th>Amount</th>
|
||||
<th>Unit Price</th>
|
||||
<th>Line Total</th>
|
||||
<th>...</th>
|
||||
</tr>"}
|
||||
var/subtotal=0
|
||||
if(line_items.len>0)
|
||||
for(var/i=1;i<=line_items.len;i++)
|
||||
var/line_item/LI = line_items[i]
|
||||
var/linetotal=LI.units*LI.price
|
||||
receipt += {"<tr class=\"[(i%2)?"even":"odd"]\">
|
||||
<th>[LI.name]</th>
|
||||
<td><a href="?src=[UID()];setunits=[i]">[LI.units]</a></td>
|
||||
<td>$[num2septext(LI.price)]</td>
|
||||
<td>$[num2septext(linetotal)]</td>
|
||||
<td><a href="?src=[UID()];removefromorder=[i]" style="color:red;">×</a></td>
|
||||
</tr>"}
|
||||
subtotal += linetotal
|
||||
var/taxes = POS_TAX_RATE*subtotal
|
||||
var/presets = "<i>(No presets available)</i>"
|
||||
if(products.len>0)
|
||||
presets = {"<select name="preset">""}
|
||||
for(var/pid in products)
|
||||
var/line_item/product = products[pid]
|
||||
presets += {"<option value="[pid]">[product.name]</option>"}
|
||||
presets += "</select>"
|
||||
receipt += {"
|
||||
<tr>
|
||||
<td class="first">[presets]</td>
|
||||
<td><input type="textbox" name="units" value="1.0" /> units</td>
|
||||
<td colspan="2"><input type="submit" name="act" value="Add to Order" /></td>
|
||||
</tr>
|
||||
<tr class="calculated">
|
||||
<th colspan="3">SUBTOTAL</th><td>$[num2septext(subtotal)]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th colspan="3">TAXES</th><td>$[num2septext(taxes)]</td>
|
||||
</tr>"}
|
||||
receipt += {"
|
||||
<tr class="calculated total">
|
||||
<th colspan="3">TOTAL</th><td>$[num2septext(taxes+subtotal)]</th>
|
||||
</tr>"}
|
||||
receipt += {"</table>
|
||||
<input type="submit" name="act" value="Finalize Sale" />
|
||||
<input type="submit" name="act" value="Reset" />
|
||||
</form>
|
||||
</fieldset>"}
|
||||
return receipt
|
||||
|
||||
/obj/machinery/pos/proc/ProductsScreen()
|
||||
var/dat={"<fieldset><legend>Product List</legend>
|
||||
<form action="?src=[UID()]" method="get">
|
||||
<input type="hidden" name="src" value="[UID()]" />
|
||||
<table>
|
||||
<tr class=\"first\">
|
||||
<th class=\"first\">Item</th>
|
||||
<th>Unit Price</th>
|
||||
<th># Sold</th>
|
||||
<th>...</th>
|
||||
</tr>"}
|
||||
for(var/i in products)
|
||||
var/line_item/LI = products[i]
|
||||
dat += {"<tr class=\"[(i%2)?"even":"odd"]\">
|
||||
<th><a href="?src=[UID()];setpname=[i]">[LI.name]</a></th>
|
||||
<td><a href="?src=[UID()];setprice=[i]">$[num2septext(LI.price)]</a></td>
|
||||
<td>[LI.units]</td>
|
||||
<td><a href="?src=[UID()];rmproduct=[i]" style="color:red;">×</a></td>
|
||||
</tr>"}
|
||||
dat += {"</table>
|
||||
<b>New Product:</b><br />
|
||||
<label for="name">Name:</label> <input type="textbox" name="name" value=""/><br />
|
||||
<label for="name">Price:</label> $<input type="textbox" name="price" value="0.00" /><br />
|
||||
<input type="submit" name="act" value="Add Product" /><br />
|
||||
<a href="?src=[UID()];screen=[POS_SCREEN_IMPORT]">Import</a> | <a href="?src=[UID()];screen=[POS_SCREEN_EXPORT]">Export</a>
|
||||
</form>
|
||||
</fieldset>"}
|
||||
return dat
|
||||
|
||||
/obj/machinery/pos/proc/ExportScreen()
|
||||
var/dat={"<fieldset><legend>Export Products as CSV</legend>
|
||||
<textarea>"}
|
||||
for(var/i in products)
|
||||
var/line_item/LI = products[i]
|
||||
dat += "[LI.name],[LI.price]\n"
|
||||
dat += {"</textarea>
|
||||
<a href="?src=[UID()];screen=[POS_SCREEN_PRODUCTS]">OK</a>
|
||||
</fieldset>"}
|
||||
return dat
|
||||
|
||||
/obj/machinery/pos/proc/ImportScreen()
|
||||
var/dat={"<fieldset>
|
||||
<legend>Import Products as CSV</legend>
|
||||
<form action="?src=[UID()]" method="get">
|
||||
<input type="hidden" name="src" value="[UID()]" />
|
||||
<textarea name="csv"></textarea>
|
||||
<p>Data must be in the form of a CSV, with no headers or quotation marks.</p>
|
||||
<p>First column must be product names, second must be prices as an unformatted number (####.##)</p>
|
||||
<p>Deviations from this format will result in your import being rejected.</p>
|
||||
<input type="submit" name="act" value="Add Products" />
|
||||
</form>
|
||||
</fieldset>"}
|
||||
return dat
|
||||
|
||||
/obj/machinery/pos/proc/FinalizeScreen()
|
||||
return "<center><b>Waiting for Credit</b><br /><a href=\"?src=[UID()];act=Reset\">Cancel</a></center>"
|
||||
|
||||
/obj/machinery/pos/proc/SettingsScreen()
|
||||
var/dat={"<form action="?src=[UID()]" method="get">
|
||||
<input type="hidden" name="src" value="[UID()]" />
|
||||
<fieldset>
|
||||
<legend>Account Settings</legend>
|
||||
<div>
|
||||
<b>Payable Account:</b> <input type="textbox" name="payableto" value="[linked_account.account_number]" />
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>Locality Settings</legend>
|
||||
<div>
|
||||
<b>Tax Rate:</b> <input type="textbox" name="taxes" value="[POS_TAX_RATE*100]" disabled="disabled" />% (LOCKED)
|
||||
</div>
|
||||
</fieldset>
|
||||
<input type="submit" name="act" value="Save Settings" />
|
||||
</form>"}
|
||||
return dat
|
||||
|
||||
/obj/machinery/pos/update_icon()
|
||||
overlays = 0
|
||||
if(stat & (NOPOWER|BROKEN)) return
|
||||
if(logged_in)
|
||||
overlays += "pos-working"
|
||||
else
|
||||
overlays += "pos-standby"
|
||||
|
||||
/obj/machinery/pos/attack_hand(var/mob/user)
|
||||
user.set_machine(src)
|
||||
var/logindata=""
|
||||
if(logged_in)
|
||||
logindata={"<a href="?src=[UID()];logout=1">[logged_in.name]</a>"}
|
||||
var/dat = POS_HEADER + {"
|
||||
<div class="navbar">
|
||||
[station_time_timestamp()], [GLOB.current_date_string]<br />
|
||||
[logindata]
|
||||
<a href="?src=[UID()];screen=[POS_SCREEN_ORDER]">Order</a> |
|
||||
<a href="?src=[UID()];screen=[POS_SCREEN_PRODUCTS]">Products</a> |
|
||||
<a href="?src=[UID()];screen=[POS_SCREEN_SETTINGS]">Settings</a>
|
||||
</div>"}
|
||||
switch(screen)
|
||||
if(POS_SCREEN_LOGIN) dat += LoginScreen()
|
||||
if(POS_SCREEN_ORDER) dat += OrderScreen()
|
||||
if(POS_SCREEN_FINALIZE) dat += FinalizeScreen()
|
||||
if(POS_SCREEN_PRODUCTS) dat += ProductsScreen()
|
||||
if(POS_SCREEN_EXPORT) dat += ExportScreen()
|
||||
if(POS_SCREEN_IMPORT) dat += ImportScreen()
|
||||
if(POS_SCREEN_SETTINGS) dat += SettingsScreen()
|
||||
|
||||
dat += "</body></html>"
|
||||
// END AUTOFIX
|
||||
user << browse(dat, "window=pos")
|
||||
onclose(user, "pos")
|
||||
return
|
||||
|
||||
/obj/machinery/pos/proc/say(var/text)
|
||||
src.visible_message("[bicon(src)] <span class=\"notice\"><b>[name]</b> states, \"[text]\"</span>")
|
||||
|
||||
/obj/machinery/pos/Topic(var/href, var/list/href_list)
|
||||
if(..(href,href_list)) return
|
||||
if("logout" in href_list)
|
||||
if(alert(src, "You sure you want to log out?", "Confirm", "Yes", "No")!="Yes") return
|
||||
logged_in=null
|
||||
screen=POS_SCREEN_LOGIN
|
||||
update_icon()
|
||||
src.attack_hand(usr)
|
||||
return
|
||||
if(usr != logged_in)
|
||||
to_chat(usr, "<span class='warning'>[logged_in.name] is already logged in. You cannot use this machine until they log out.</span>")
|
||||
return
|
||||
if("act" in href_list)
|
||||
switch(href_list["act"])
|
||||
if("Reset")
|
||||
NewOrder()
|
||||
screen=POS_SCREEN_ORDER
|
||||
if("Finalize Sale")
|
||||
var/subtotal=0
|
||||
if(line_items.len>0)
|
||||
for(var/i=1;i<=line_items.len;i++)
|
||||
var/line_item/LI = line_items[i]
|
||||
subtotal += LI.units*LI.price
|
||||
var/taxes = POS_TAX_RATE*subtotal
|
||||
credits_needed=taxes+subtotal
|
||||
say("Your total is $[num2septext(credits_needed)]. Please insert credit chips or swipe your ID.")
|
||||
screen=POS_SCREEN_FINALIZE
|
||||
if("Add Product")
|
||||
var/line_item/LI = new
|
||||
LI.name=sanitize(href_list["name"])
|
||||
LI.price=text2num(href_list["price"])
|
||||
products["[products.len+1]"]=LI
|
||||
if("Add to Order")
|
||||
AddToOrder(href_list["preset"],text2num(href_list["units"]))
|
||||
if("Add Products")
|
||||
for(var/list/line in splittext(href_list["csv"],"\n"))
|
||||
var/list/cells = splittext(line,",")
|
||||
if(cells.len<2)
|
||||
to_chat(usr, "<span class='warning'>The CSV must have at least two columns: Product Name, followed by Price (as a number).</span>")
|
||||
src.attack_hand(usr)
|
||||
return
|
||||
var/line_item/LI = new
|
||||
LI.name=sanitize(cells[1])
|
||||
LI.price=text2num(cells[2])
|
||||
products["[products.len+1]"]=LI
|
||||
if("Export Products")
|
||||
screen=POS_SCREEN_EXPORT
|
||||
if("Import Products")
|
||||
screen=POS_SCREEN_IMPORT
|
||||
if("Save Settings")
|
||||
var/datum/money_account/new_linked_account = get_money_account(text2num(href_list["payableto"]),z)
|
||||
if(!new_linked_account)
|
||||
to_chat(usr, "<span class='warning'>Unable to link new account.</span>")
|
||||
else
|
||||
linked_account = new_linked_account
|
||||
screen=POS_SCREEN_SETTINGS
|
||||
else if("screen" in href_list)
|
||||
screen=text2num(href_list["screen"])
|
||||
else if("rmproduct" in href_list)
|
||||
products.Remove(href_list["rmproduct"])
|
||||
else if("removefromorder" in href_list)
|
||||
RemoveFromOrder(text2num(href_list["removefromorder"]))
|
||||
else if("setunits" in href_list)
|
||||
var/lid = text2num(href_list["setunits"])
|
||||
var/newunits = input(usr,"Enter the units sold.") as num
|
||||
if(!newunits) return
|
||||
var/line_item/LI = line_items[lid]
|
||||
LI.units = newunits
|
||||
line_items[lid]=LI
|
||||
else if("setpname" in href_list)
|
||||
var/newtext = sanitize(input(usr,"Enter the product's name."))
|
||||
if(!newtext) return
|
||||
var/pid = href_list["setpname"]
|
||||
var/line_item/LI = products[pid]
|
||||
LI.name = newtext
|
||||
products[pid]=LI
|
||||
else if("setprice" in href_list)
|
||||
var/newprice = input(usr,"Enter the product's price.") as num
|
||||
if(!newprice) return
|
||||
var/pid = href_list["setprice"]
|
||||
var/line_item/LI = products[pid]
|
||||
LI.price = newprice
|
||||
products[pid]=LI
|
||||
src.attack_hand(usr)
|
||||
|
||||
/obj/machinery/pos/attackby(var/atom/movable/A, var/mob/user, params)
|
||||
if(istype(A,/obj/item/card/id))
|
||||
var/obj/item/card/id/I = A
|
||||
if(!logged_in)
|
||||
user.visible_message("<span class='notice'>The machine beeps, and logs you in</span>","You hear a beep.")
|
||||
logged_in = user
|
||||
screen=POS_SCREEN_ORDER
|
||||
update_icon()
|
||||
src.attack_hand(user) //why'd you use usr nexis, why
|
||||
return
|
||||
else
|
||||
if(!linked_account)
|
||||
visible_message("<span class='warning'>The machine buzzes, and flashes \"NO LINKED ACCOUNT\" on the screen.</span>","You hear a buzz.")
|
||||
flick(src,"pos-error")
|
||||
return
|
||||
if(screen!=POS_SCREEN_FINALIZE)
|
||||
visible_message("<span class='notice'>The machine buzzes.</span>","<span class='warning'>You hear a buzz.</span>")
|
||||
flick(src,"pos-error")
|
||||
return
|
||||
var/datum/money_account/acct = get_card_account(I)
|
||||
if(!acct)
|
||||
visible_message("<span class='warning'>The machine buzzes, and flashes \"NO ACCOUNT\" on the screen.</span>","You hear a buzz.")
|
||||
flick(src,"pos-error")
|
||||
return
|
||||
if(credits_needed > acct.money)
|
||||
visible_message("<span class='warning'>The machine buzzes, and flashes \"NOT ENOUGH FUNDS\" on the screen.</span>","You hear a buzz.")
|
||||
flick(src,"pos-error")
|
||||
return
|
||||
visible_message("<span class='notice'>The machine beeps, and begins printing a receipt</span>","You hear a beep.")
|
||||
PrintReceipt()
|
||||
NewOrder()
|
||||
acct.charge(credits_needed,linked_account,"Purchase at POS #[id].")
|
||||
credits_needed=0
|
||||
screen=POS_SCREEN_ORDER
|
||||
else if(istype(A, /obj/item/stack/spacecash))
|
||||
if(!linked_account)
|
||||
visible_message("<span class='warning'>The machine buzzes, and flashes \"NO LINKED ACCOUNT\" on the screen.</span>","You hear a buzz.")
|
||||
flick(src,"pos-error")
|
||||
return
|
||||
if(!logged_in || screen!=POS_SCREEN_FINALIZE)
|
||||
visible_message("<span class='notice'>The machine buzzes.</span>","<span class='warning'>You hear a buzz.</span>")
|
||||
flick(src,"pos-error")
|
||||
return
|
||||
var/obj/item/stack/spacecash/C = A
|
||||
credits_held += C.amount
|
||||
if(credits_held >= credits_needed)
|
||||
visible_message("<span class='notice'>The machine beeps, and begins printing a receipt</span>","You hear a beep and the sound of paper being shredded.")
|
||||
PrintReceipt()
|
||||
NewOrder()
|
||||
credits_held -= credits_needed
|
||||
credits_needed=0
|
||||
screen=POS_SCREEN_ORDER
|
||||
if(credits_held)
|
||||
new /obj/item/stack/spacecash(loc, credits_held)
|
||||
credits_held=0
|
||||
return
|
||||
return ..()
|
||||
@@ -1,55 +0,0 @@
|
||||
/datum/event/aurora_caelus
|
||||
announceWhen = 5
|
||||
startWhen = 1
|
||||
endWhen = 50
|
||||
var/list/aurora_colors = list("#A2FF80", "#A2FF8B", "#A2FF96", "#A2FFA5", "#A2FFB6", "#A2FFC7", "#A2FFDE")
|
||||
var/aurora_progress = 0 //this cycles from 1 to 7, slowly changing colors from gentle green to gentle blue
|
||||
|
||||
/datum/event/aurora_caelus/announce()
|
||||
GLOB.event_announcement.Announce("[station_name()]: A harmless cloud of ions is approaching your station, and will exhaust their energy battering the hull. \
|
||||
Nanotrasen has approved a short break for all employees to relax and observe this very rare event. \
|
||||
During this time, starlight will be bright but gentle, shifting between quiet green and blue colors. \
|
||||
Any staff who would like to view these lights for themselves may proceed to the area nearest to them with viewing ports to open space. \
|
||||
We hope you enjoy the lights.", "Harmless ions approaching", new_sound = 'sound/misc/notice2.ogg', from = "Nanotrasen Meteorology Division")
|
||||
for(var/V in GLOB.player_list)
|
||||
var/mob/M = V
|
||||
if((M.client.prefs.toggles & SOUND_MIDI) && is_station_level(M.z))
|
||||
M.playsound_local(null, 'sound/ambience/aurora_caelus.ogg', 20, FALSE, pressure_affected = FALSE)
|
||||
|
||||
/datum/event/aurora_caelus/start()
|
||||
for(var/area in GLOB.all_areas)
|
||||
var/area/A = area
|
||||
if(initial(A.dynamic_lighting) == DYNAMIC_LIGHTING_IFSTARLIGHT)
|
||||
for(var/turf/space/S in A)
|
||||
S.set_light(S.light_range * 3, S.light_power * 0.5)
|
||||
|
||||
/datum/event/aurora_caelus/tick()
|
||||
if(aurora_progress >= aurora_colors.len)
|
||||
return
|
||||
if(activeFor % 5 == 0)
|
||||
aurora_progress++
|
||||
var/aurora_color = aurora_colors[aurora_progress]
|
||||
for(var/area in GLOB.all_areas)
|
||||
var/area/A = area
|
||||
if(initial(A.dynamic_lighting) == DYNAMIC_LIGHTING_IFSTARLIGHT)
|
||||
for(var/turf/space/S in A)
|
||||
S.set_light(l_color = aurora_color)
|
||||
|
||||
/datum/event/aurora_caelus/end()
|
||||
for(var/area in GLOB.all_areas)
|
||||
var/area/A = area
|
||||
if(initial(A.dynamic_lighting) == DYNAMIC_LIGHTING_IFSTARLIGHT)
|
||||
for(var/turf/space/S in A)
|
||||
fade_to_black(S)
|
||||
GLOB.event_announcement.Announce("The Aurora Caelus event is now ending. Starlight conditions will slowly return to normal. \
|
||||
When this has concluded, please return to your workplace and continue work as normal. \
|
||||
Have a pleasant shift, [station_name()], and thank you for watching with us.",
|
||||
"Harmless ions dissipating", new_sound = 'sound/misc/notice2.ogg', from = "Nanotrasen Meteorology Division")
|
||||
|
||||
/datum/event/aurora_caelus/proc/fade_to_black(turf/space/S)
|
||||
set waitfor = FALSE
|
||||
var/new_light = config.starlight
|
||||
while(S.light_range > new_light)
|
||||
S.set_light(S.light_range - 0.2)
|
||||
sleep(30)
|
||||
S.set_light(new_light, 1, l_color = "") // we should be able to use `, null` as the last arg but BYOND is a piece of FUCKING SHIT AND SET_LIGHT DOESN'T WORK THAT WAY DESPITE EVERY FUCKING THING ABOUT IT INDICATING THAT IT GODDAMN WELL SHOULD AAAAAAAAAAAAAAAAA
|
||||
@@ -139,7 +139,6 @@ GLOBAL_LIST_EMPTY(event_last_fired)
|
||||
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Vermin Infestation",/datum/event/infestation, 100, list(ASSIGNMENT_JANITOR = 100)),
|
||||
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Sentience", /datum/event/sentience, 50),
|
||||
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Wallrot", /datum/event/wallrot, 0, list(ASSIGNMENT_ENGINEER = 30, ASSIGNMENT_GARDENER = 50)),
|
||||
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Aurora Caelus", /datum/event/aurora_caelus, 15, is_one_shot = TRUE),
|
||||
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Koi School", /datum/event/carp_migration/koi, 80,)
|
||||
)
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
|
||||
|
||||
// Cannabis
|
||||
/obj/item/seeds/cannabis
|
||||
name = "pack of cannabis seeds"
|
||||
@@ -14,9 +16,7 @@
|
||||
icon_dead = "cannabis-dead" // Same for the dead icon
|
||||
genes = list(/datum/plant_gene/trait/repeated_harvest)
|
||||
mutatelist = list(/obj/item/seeds/cannabis/rainbow,
|
||||
/obj/item/seeds/cannabis/death,
|
||||
/obj/item/seeds/cannabis/white,
|
||||
/obj/item/seeds/cannabis/ultimate)
|
||||
/obj/item/seeds/cannabis/white)
|
||||
reagents_add = list("thc" = 0.15, "cbd" = 0.15)
|
||||
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
species = "megacannabis"
|
||||
plantname = "Rainbow Weed"
|
||||
product = /obj/item/reagent_containers/food/snacks/grown/cannabis/rainbow
|
||||
mutatelist = list()
|
||||
mutatelist = list(/obj/item/seeds/cannabis/death,
|
||||
/obj/item/seeds/cannabis/ultimate)
|
||||
reagents_add = list("lsd" = 0.15, "thc" = 0.15, "cbd" = 0.15)
|
||||
rarity = 40
|
||||
|
||||
@@ -49,7 +50,8 @@
|
||||
species = "whitecannabis"
|
||||
plantname = "Lifeweed"
|
||||
product = /obj/item/reagent_containers/food/snacks/grown/cannabis/white
|
||||
mutatelist = list()
|
||||
mutatelist = list(/obj/item/seeds/cannabis/death,
|
||||
/obj/item/seeds/cannabis/ultimate)
|
||||
reagents_add = list("omnizine" = 0.35, "thc" = 0.15, "cbd" = 0.15)
|
||||
rarity = 40
|
||||
|
||||
@@ -62,25 +64,24 @@
|
||||
plantname = "Omega Weed"
|
||||
product = /obj/item/reagent_containers/food/snacks/grown/cannabis/ultimate
|
||||
mutatelist = list()
|
||||
reagents_add = list("lsd" = 0.15,
|
||||
"suicider" = 0.15,
|
||||
"space_drugs" = 0.15,
|
||||
"mercury" = 0.15,
|
||||
"lithium" = 0.15,
|
||||
"atropine" = 0.15,
|
||||
"ephedrine" = 0.15,
|
||||
"haloperidol" = 0.15,
|
||||
"methamphetamine" = 0.15,
|
||||
"thc" = 0.15,
|
||||
"cbd" = 0.15,
|
||||
"psilocybin" = 0.15,
|
||||
"hairgrownium" = 0.15,
|
||||
"ectoplasm" = 0.15,
|
||||
"bath_salts" = 0.15,
|
||||
"itching_powder" = 0.15,
|
||||
"crank" = 0.15,
|
||||
"krokodil" = 0.15,
|
||||
"histamine" = 0.15)
|
||||
reagents_add = list("lsd" = 0.05,
|
||||
"suicider" = 0.05,
|
||||
"space_drugs" = 0.05,
|
||||
"mercury" = 0.05,
|
||||
"lithium" = 0.05,
|
||||
"ephedrine" = 0.05,
|
||||
"haloperidol" = 0.05,
|
||||
"methamphetamine" = 0.05,
|
||||
"thc" = 0.05,
|
||||
"cbd" = 0.05,
|
||||
"psilocybin" = 0.05,
|
||||
"hairgrownium" = 0.05,
|
||||
"ectoplasm" = 0.05,
|
||||
"bath_salts" = 0.05,
|
||||
"itching_powder" = 0.05,
|
||||
"crank" = 0.05,
|
||||
"krokodil" = 0.05,
|
||||
"histamine" = 0.05)
|
||||
rarity = 69
|
||||
|
||||
|
||||
@@ -124,5 +125,4 @@
|
||||
name = "omega cannibas leaf"
|
||||
desc = "You feel dizzy looking at it. What the fuck?"
|
||||
icon_state = "ocannabis"
|
||||
volume = 420
|
||||
wine_power = 0.9
|
||||
|
||||
@@ -336,6 +336,8 @@
|
||||
M.move_to_delay = initial(M.move_to_delay) + base_speed_add
|
||||
if(M.stored_gun)
|
||||
M.stored_gun.overheat_time += base_cooldown_add
|
||||
if(M.mind)
|
||||
M.mind.offstation_role = TRUE
|
||||
|
||||
/**********************Mining drone cube**********************/
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
/mob/living/carbon/human/proc/forcesay(list/append)
|
||||
if(stat == CONSCIOUS)
|
||||
if(client)
|
||||
var/virgin = 1 //has the text been modified yet?
|
||||
var/modified = FALSE //has the text been modified yet?
|
||||
var/temp = winget(client, "input", "text")
|
||||
if(findtextEx(temp, "Say \"", 1, 7) && length(temp) > 5) //case sensitive means
|
||||
|
||||
@@ -17,11 +17,11 @@
|
||||
|
||||
if(findtext(trim_left(temp), ":", 6, 7)) //dept radio
|
||||
temp = copytext(trim_left(temp), 8)
|
||||
virgin = 0
|
||||
modified = TRUE
|
||||
|
||||
if(virgin)
|
||||
if(!modified)
|
||||
temp = copytext(trim_left(temp), 6) //normal speech
|
||||
virgin = 0
|
||||
modified = TRUE
|
||||
|
||||
while(findtext(trim_left(temp), ":", 1, 2)) //dept radio again (necessary)
|
||||
temp = copytext(trim_left(temp), 3)
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
silence_time = null
|
||||
to_chat(src, "<font color=green>Communication circuit reinitialized. Speech and messaging functionality restored.</font>")
|
||||
|
||||
if(cable)
|
||||
if(get_dist(src, cable) > 1)
|
||||
var/turf/T = get_turf_or_move(loc)
|
||||
for(var/mob/M in viewers(T))
|
||||
M.show_message("<span class='warning'>The data cable rapidly retracts back into its spool.</span>", 3, "<span class='warning'>You hear a click and the sound of wire spooling rapidly.</span>", 2)
|
||||
QDEL_NULL(cable)
|
||||
if(installed_software["doorjack"])
|
||||
var/datum/pai_software/door_jack/DJ = installed_software["doorjack"]
|
||||
if(DJ.cable)
|
||||
if(get_dist(src, DJ.cable) > 1)
|
||||
visible_message("<span class='warning'>The data cable connected to [src] rapidly retracts back into its spool!</span>")
|
||||
QDEL_NULL(DJ.cable)
|
||||
|
||||
/mob/living/silicon/pai/updatehealth(reason = "none given")
|
||||
if(status_flags & GODMODE)
|
||||
|
||||
@@ -9,11 +9,8 @@
|
||||
pass_flags = PASSTABLE
|
||||
density = 0
|
||||
holder_type = /obj/item/holder/pai
|
||||
var/network = "SS13"
|
||||
var/obj/machinery/camera/current = null
|
||||
|
||||
var/ram = 100 // Used as currency to purchase different abilities
|
||||
var/list/software = list()
|
||||
var/userDNA // The DNA string of our assigned user
|
||||
var/obj/item/paicard/card // The card we inhabit
|
||||
var/obj/item/radio/radio // Our primary radio
|
||||
@@ -42,9 +39,6 @@
|
||||
)
|
||||
|
||||
|
||||
|
||||
var/obj/item/pai_cable/cable // The cable we produce and use when door or camera jacking
|
||||
|
||||
var/master // Name of the one who commands us
|
||||
var/master_dna // DNA string for owner verification
|
||||
// Keeping this separate from the laws var, it should be much more difficult to modify
|
||||
@@ -64,17 +58,11 @@
|
||||
var/secHUD = 0 // Toggles whether the Security HUD is active or not
|
||||
var/medHUD = 0 // Toggles whether the Medical HUD is active or not
|
||||
|
||||
var/medical_cannotfind = 0
|
||||
var/datum/data/record/medicalActive1 // Datacore record declarations for record software
|
||||
var/datum/data/record/medicalActive2
|
||||
/// Currently active software
|
||||
var/datum/pai_software/active_software
|
||||
|
||||
var/security_cannotfind = 0
|
||||
var/datum/data/record/securityActive1 // Could probably just combine all these into one
|
||||
var/datum/data/record/securityActive2
|
||||
|
||||
var/obj/machinery/door/hackdoor // The airlock being hacked
|
||||
var/hackprogress = 0 // Possible values: 0 - 100, >= 100 means the hack is complete and will be reset upon next check
|
||||
var/hack_aborted = 0
|
||||
/// List of all installed software
|
||||
var/list/datum/pai_software/installed_software = list()
|
||||
|
||||
var/obj/item/integrated_radio/signal/sradio // AI's signaller
|
||||
|
||||
@@ -109,20 +97,20 @@
|
||||
|
||||
//PDA
|
||||
pda = new(src)
|
||||
spawn(5)
|
||||
pda.ownjob = "Personal Assistant"
|
||||
pda.owner = text("[]", src)
|
||||
pda.name = pda.owner + " (" + pda.ownjob + ")"
|
||||
var/datum/data/pda/app/messenger/M = pda.find_program(/datum/data/pda/app/messenger)
|
||||
M.toff = 1
|
||||
..()
|
||||
pda.ownjob = "Personal Assistant"
|
||||
pda.owner = "[src]"
|
||||
pda.name = "[pda.owner] ([pda.ownjob])"
|
||||
var/datum/data/pda/app/messenger/M = pda.find_program(/datum/data/pda/app/messenger)
|
||||
M.toff = TRUE
|
||||
|
||||
/mob/living/silicon/pai/Destroy()
|
||||
medicalActive1 = null
|
||||
medicalActive2 = null
|
||||
securityActive1 = null
|
||||
securityActive2 = null
|
||||
return ..()
|
||||
// Software modules. No these var names have nothing to do with photoshop
|
||||
for(var/PS in subtypesof(/datum/pai_software))
|
||||
var/datum/pai_software/PSD = new PS(src)
|
||||
if(PSD.default)
|
||||
installed_software[PSD.id] = PSD
|
||||
|
||||
active_software = installed_software["mainmenu"] // Default us to the main menu
|
||||
..()
|
||||
|
||||
/mob/living/silicon/pai/can_unbuckle()
|
||||
return FALSE
|
||||
@@ -159,12 +147,6 @@
|
||||
for(var/obj/effect/proc_holder/P in proc_holder_list)
|
||||
statpanel("[P.panel]","",P)
|
||||
|
||||
/mob/living/silicon/pai/check_eye(var/mob/user as mob)
|
||||
if(!current)
|
||||
return null
|
||||
user.reset_perspective(current)
|
||||
return 1
|
||||
|
||||
/mob/living/silicon/pai/blob_act()
|
||||
if(stat != DEAD)
|
||||
adjustBruteLoss(60)
|
||||
@@ -229,7 +211,7 @@
|
||||
return
|
||||
|
||||
|
||||
// See software.dm for Topic()
|
||||
// See software.dm for tgui_act()
|
||||
|
||||
/mob/living/silicon/pai/attack_animal(mob/living/simple_animal/M)
|
||||
. = ..()
|
||||
@@ -238,78 +220,6 @@
|
||||
add_attack_logs(M, src, "Animal attacked for [damage] damage")
|
||||
adjustBruteLoss(damage)
|
||||
|
||||
/mob/living/silicon/pai/proc/switchCamera(var/obj/machinery/camera/C)
|
||||
usr:cameraFollow = null
|
||||
if(!C)
|
||||
unset_machine()
|
||||
reset_perspective(null)
|
||||
return 0
|
||||
if(stat == 2 || !C.status || !(network in C.network)) return 0
|
||||
|
||||
// ok, we're alive, camera is good and in our network...
|
||||
|
||||
set_machine(src)
|
||||
src:current = C
|
||||
reset_perspective(C)
|
||||
return 1
|
||||
|
||||
/mob/living/silicon/pai/verb/reset_record_view()
|
||||
set category = "pAI Commands"
|
||||
set name = "Reset Records Software"
|
||||
|
||||
securityActive1 = null
|
||||
securityActive2 = null
|
||||
security_cannotfind = 0
|
||||
medicalActive1 = null
|
||||
medicalActive2 = null
|
||||
medical_cannotfind = 0
|
||||
SSnanoui.update_uis(src)
|
||||
to_chat(usr, "<span class='notice'>You reset your record-viewing software.</span>")
|
||||
|
||||
/mob/living/silicon/pai/cancel_camera()
|
||||
set category = "pAI Commands"
|
||||
set name = "Cancel Camera View"
|
||||
reset_perspective(null)
|
||||
unset_machine()
|
||||
src:cameraFollow = null
|
||||
|
||||
//Addition by Mord_Sith to define AI's network change ability
|
||||
/*
|
||||
/mob/living/silicon/pai/proc/pai_network_change()
|
||||
set category = "pAI Commands"
|
||||
set name = "Change Camera Network"
|
||||
reset_perspective(null)
|
||||
unset_machine()
|
||||
src:cameraFollow = null
|
||||
var/cameralist[0]
|
||||
|
||||
if(usr.stat == 2)
|
||||
to_chat(usr, "You can't change your camera network because you are dead!")
|
||||
return
|
||||
|
||||
for(var/obj/machinery/camera/C in Cameras)
|
||||
if(!C.status)
|
||||
continue
|
||||
else
|
||||
if(C.network != "CREED" && C.network != "thunder" && C.network != "RD" && C.network != "toxins" && C.network != "Prison") COMPILE ERROR! This will have to be updated as camera.network is no longer a string, but a list instead
|
||||
cameralist[C.network] = C.network
|
||||
|
||||
network = input(usr, "Which network would you like to view?") as null|anything in cameralist
|
||||
to_chat(src, "<span class='notice'>Switched to [network] camera network.</span>")
|
||||
//End of code by Mord_Sith
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
// Debug command - Maybe should be added to admin verbs later
|
||||
/mob/verb/makePAI(var/turf/t in view())
|
||||
var/obj/item/paicard/card = new(t)
|
||||
var/mob/living/silicon/pai/pai = new(card)
|
||||
pai.key = key
|
||||
card.setPersonality(pai)
|
||||
|
||||
*/
|
||||
|
||||
// Procs/code after this point is used to convert the stationary pai item into a
|
||||
// mobile pai mob. This also includes handling some of the general shit that can occur
|
||||
// to it. Really this deserves its own file, but for the moment it can sit here. ~ Z
|
||||
|
||||
@@ -10,113 +10,41 @@ GLOBAL_LIST_INIT(pai_emotions, list(
|
||||
"What" = 9
|
||||
))
|
||||
|
||||
|
||||
GLOBAL_LIST_EMPTY(pai_software_by_key)
|
||||
GLOBAL_LIST_EMPTY(default_pai_software)
|
||||
|
||||
/mob/living/silicon/pai/New()
|
||||
..()
|
||||
software = GLOB.default_pai_software.Copy()
|
||||
|
||||
/mob/living/silicon/pai/verb/paiInterface()
|
||||
set category = "pAI Commands"
|
||||
set name = "Software Interface"
|
||||
|
||||
ui_interact(src)
|
||||
tgui_interact(src)
|
||||
|
||||
/mob/living/silicon/pai/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, datum/topic_state/state = GLOB.self_state)
|
||||
if(ui_key != "main")
|
||||
var/datum/pai_software/S = software[ui_key]
|
||||
if(S && !S.toggle)
|
||||
ui = SSnanoui.try_update_ui(user, src, S.id, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, S.id, S.template_file, S.ui_title, S.ui_width, S.ui_height, state = state)
|
||||
ui.open()
|
||||
if(S.autoupdate)
|
||||
ui.set_auto_update(1)
|
||||
else
|
||||
if(ui)
|
||||
ui.set_status(STATUS_CLOSE, 0)
|
||||
return
|
||||
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
/mob/living/silicon/pai/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_self_state)
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "pai_interface.tmpl", "pAI Software Interface", 450, 600, state = state)
|
||||
ui = new(user, src, ui_key, "PAI", name, 600, 650, master_ui, state)
|
||||
ui.open()
|
||||
ui.set_auto_update(1)
|
||||
|
||||
/mob/living/silicon/pai/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.self_state)
|
||||
var/data[0]
|
||||
|
||||
if(ui_key != "main")
|
||||
var/datum/pai_software/S = software[ui_key]
|
||||
if(S && !S.toggle)
|
||||
return S.on_ui_data(user, state)
|
||||
log_runtime(EXCEPTION("Unrecognized/invalid pAI UI state '[ui_key]'"), src)
|
||||
return
|
||||
// Software we have bought
|
||||
var/bought_software[0]
|
||||
// Software we have not bought
|
||||
var/not_bought_software[0]
|
||||
|
||||
for(var/key in GLOB.pai_software_by_key)
|
||||
var/datum/pai_software/S = GLOB.pai_software_by_key[key]
|
||||
var/software_data[0]
|
||||
software_data["name"] = S.name
|
||||
software_data["id"] = S.id
|
||||
if(key in software)
|
||||
software_data["on"] = S.is_active(src)
|
||||
bought_software[++bought_software.len] = software_data
|
||||
else
|
||||
software_data["ram"] = S.ram_cost
|
||||
not_bought_software[++not_bought_software.len] = software_data
|
||||
|
||||
data["bought"] = bought_software
|
||||
data["not_bought"] = not_bought_software
|
||||
data["available_ram"] = ram
|
||||
|
||||
// Emotions
|
||||
var/emotions[0]
|
||||
for(var/name in GLOB.pai_emotions)
|
||||
var/emote[0]
|
||||
emote["name"] = name
|
||||
emote["id"] = GLOB.pai_emotions[name]
|
||||
emotions[++emotions.len] = emote
|
||||
|
||||
data["emotions"] = emotions
|
||||
data["current_emotion"] = card.current_emotion
|
||||
/mob/living/silicon/pai/tgui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["app_template"] = active_software.template_file
|
||||
data["app_icon"] = active_software.ui_icon
|
||||
data["app_title"] = active_software.name
|
||||
data["app_data"] = active_software.get_app_data(user)
|
||||
|
||||
return data
|
||||
|
||||
/mob/living/silicon/pai/Topic(href, href_list)
|
||||
// Yes the stupid amount of args here is important, so we can proxy stuff to child UIs
|
||||
/mob/living/silicon/pai/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
|
||||
if(..())
|
||||
return 1
|
||||
return
|
||||
|
||||
if(href_list["software"])
|
||||
var/soft = href_list["software"]
|
||||
var/datum/pai_software/S = software[soft]
|
||||
if(S.toggle)
|
||||
S.toggle(src)
|
||||
. = TRUE
|
||||
|
||||
switch(action)
|
||||
// This call is global to all templates, hence the prefix
|
||||
if("MASTER_back")
|
||||
active_software = installed_software["mainmenu"]
|
||||
// Bail early
|
||||
return
|
||||
else
|
||||
ui_interact(src, ui_key = soft)
|
||||
return 1
|
||||
|
||||
else if(href_list["stopic"])
|
||||
var/soft = href_list["stopic"]
|
||||
var/datum/pai_software/S = software[soft]
|
||||
if(S)
|
||||
return S.Topic(href, href_list)
|
||||
|
||||
else if(href_list["purchase"])
|
||||
var/soft = href_list["purchase"]
|
||||
var/datum/pai_software/S = GLOB.pai_software_by_key[soft]
|
||||
if(S && (ram >= S.ram_cost))
|
||||
ram -= S.ram_cost
|
||||
software[S.id] = S
|
||||
return 1
|
||||
|
||||
else if(href_list["image"])
|
||||
var/img = text2num(href_list["image"])
|
||||
if(1 <= img && img <= 9)
|
||||
card.setEmotion(img)
|
||||
return 1
|
||||
active_software.tgui_act(action, params, ui, state)
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
/**
|
||||
* # pAI Software
|
||||
*
|
||||
* Datum module for pAI software
|
||||
*
|
||||
* Very similar to the PDA app datum, this determines what UI sub-template to use,
|
||||
* as well as the RAM cost, and if it is toggle software (not a UI app)
|
||||
*
|
||||
*/
|
||||
/datum/pai_software
|
||||
/// Name for the software. This is used as the button text when buying or opening/toggling the software
|
||||
var/name = "pAI software module"
|
||||
/// RAM cost; pAIs start with 100 RAM, spending it on programs
|
||||
var/ram_cost = 0
|
||||
/// ID for the software. This must be unique
|
||||
var/id
|
||||
// Toggled software should override toggle() and is_active()
|
||||
// Non-toggled software should override get_app_data() and Topic()
|
||||
/// Whether this software is a toggle or not
|
||||
var/toggle_software = FALSE
|
||||
/// Do we have this software installed by default
|
||||
var/default = FALSE
|
||||
/// Template for the TGUI file
|
||||
var/template_file = "oops"
|
||||
/// Icon for inside the UI
|
||||
var/ui_icon = "file-code"
|
||||
/// pAI which holds this software
|
||||
var/mob/living/silicon/pai/pai_holder
|
||||
|
||||
/**
|
||||
* New handler
|
||||
*
|
||||
* Ensures that the pai_holder var is set to the pAI itself
|
||||
* Arguments:
|
||||
* * user - The pAI that this softawre is held by
|
||||
*/
|
||||
/datum/pai_software/New(mob/living/silicon/pai/user)
|
||||
pai_holder = user
|
||||
..()
|
||||
|
||||
/**
|
||||
* Handler for the app's UI data
|
||||
*
|
||||
* This returns the list of the current app's data for the UI
|
||||
* This will then be injected as a variable on the TGUI data called "app_data"
|
||||
*
|
||||
* Arguments:
|
||||
* * user - The pAI that is using this app
|
||||
*/
|
||||
/datum/pai_software/proc/get_app_data(mob/living/silicon/pai/user)
|
||||
return list()
|
||||
|
||||
/**
|
||||
* Handler for toggling toggle apps on and off
|
||||
*
|
||||
* This is invoked whenever you toggle a toggleable function
|
||||
* Put your toggleable work in here
|
||||
*
|
||||
* Arguments:
|
||||
* * user - The pAI that is using this toggle
|
||||
*/
|
||||
/datum/pai_software/proc/toggle(mob/living/silicon/pai/user)
|
||||
return
|
||||
|
||||
/**
|
||||
* Helper for checking if a toggle is enabled or not
|
||||
*
|
||||
* Returns TRUE if the toggle software is active, FALSE if not
|
||||
*
|
||||
* Its like this instead of a simple `is_toggled` var because some toggles override eachother and this is easier
|
||||
*
|
||||
* Arguments:
|
||||
* * user - The pAI that is using this app
|
||||
*/
|
||||
/datum/pai_software/proc/is_active(mob/living/silicon/pai/user)
|
||||
return FALSE
|
||||
|
||||
/**
|
||||
* Helper proc so that pAIs can get the mob holding them
|
||||
*
|
||||
* This needs to exist because pAIs have many different locs
|
||||
* (Held card, mob itself, in pocket, etc)
|
||||
*
|
||||
* Arguments:
|
||||
* * inform - Boolean, should we inform the pAI if they fail to find a carrier
|
||||
*/
|
||||
/datum/pai_software/proc/get_holding_mob(inform = FALSE)
|
||||
var/mob/living/M = pai_holder.loc
|
||||
var/count = 0
|
||||
|
||||
// Find the carrier
|
||||
while(!istype(M, /mob/living))
|
||||
if(!M || !M.loc || count > 6)
|
||||
//For a runtime where M ends up in nullspace (similar to bluespace but less colourful)
|
||||
if(inform)
|
||||
to_chat(usr, "<span class='warning'>You are not being carried by anyone!</span>")
|
||||
return null
|
||||
M = M.loc
|
||||
count++
|
||||
|
||||
return M
|
||||
|
||||
/**
|
||||
* tgui_act sanity check helper
|
||||
*
|
||||
* Basically checks the existing href exploit stuff, as well as making sure the user using the UI is the pAI itself
|
||||
*/
|
||||
/datum/pai_software/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
|
||||
if(..())
|
||||
return TRUE
|
||||
|
||||
if(usr != pai_holder)
|
||||
return TRUE
|
||||
|
||||
return FALSE
|
||||
@@ -0,0 +1,388 @@
|
||||
// Main Menu //
|
||||
/datum/pai_software/main_menu
|
||||
name = "Main Menu"
|
||||
id = "mainmenu"
|
||||
default = TRUE
|
||||
template_file = "pai_main_menu"
|
||||
ui_icon = "home"
|
||||
|
||||
/datum/pai_software/main_menu/get_app_data(mob/living/silicon/pai/user)
|
||||
var/list/data = list()
|
||||
data["available_ram"] = user.ram
|
||||
|
||||
// Emotions
|
||||
var/list/emotions = list()
|
||||
for(var/name in GLOB.pai_emotions)
|
||||
var/list/emote = list()
|
||||
emote["name"] = name
|
||||
emote["id"] = GLOB.pai_emotions[name]
|
||||
emotions[++emotions.len] = emote
|
||||
|
||||
data["emotions"] = emotions
|
||||
data["current_emotion"] = user.card.current_emotion
|
||||
|
||||
var/list/available_s = list()
|
||||
for(var/s in GLOB.pai_software_by_key)
|
||||
var/datum/pai_software/PS = GLOB.pai_software_by_key[s]
|
||||
available_s += list(list("name" = PS.name, "key" = PS.id, "icon" = PS.ui_icon, "cost" = PS.ram_cost))
|
||||
|
||||
// Split to installed software and toggles for the UI
|
||||
var/list/installed_s = list()
|
||||
var/list/installed_t = list()
|
||||
for(var/s in pai_holder.installed_software)
|
||||
var/datum/pai_software/PS = pai_holder.installed_software[s]
|
||||
if(PS.toggle_software)
|
||||
installed_t += list(list("name" = PS.name, "key" = PS.id, "icon" = PS.ui_icon, "active" = PS.is_active(user)))
|
||||
else
|
||||
installed_s += list(list("name" = PS.name, "key" = PS.id, "icon" = PS.ui_icon))
|
||||
|
||||
data["available_software"] = available_s
|
||||
data["installed_software"] = installed_s
|
||||
data["installed_toggles"] = installed_t
|
||||
|
||||
return data
|
||||
|
||||
/datum/pai_software/main_menu/tgui_act(action, list/params)
|
||||
if(..())
|
||||
return
|
||||
|
||||
switch(action)
|
||||
if("purchaseSoftware")
|
||||
var/datum/pai_software/S = GLOB.pai_software_by_key[params["key"]]
|
||||
if(S && (pai_holder.ram >= S.ram_cost))
|
||||
var/datum/pai_software/newPS = new S.type(pai_holder)
|
||||
pai_holder.ram -= newPS.ram_cost
|
||||
pai_holder.installed_software[newPS.id] = newPS
|
||||
if("setEmotion")
|
||||
var/emotion = clamp(text2num(params["emotion"]), 1, 9)
|
||||
pai_holder.card.setEmotion(emotion)
|
||||
if("startSoftware")
|
||||
var/software_key = params["software_key"]
|
||||
if(pai_holder.installed_software[software_key])
|
||||
pai_holder.active_software = pai_holder.installed_software[software_key]
|
||||
if("setToggle")
|
||||
var/toggle_key = params["toggle_key"]
|
||||
if(pai_holder.installed_software[toggle_key])
|
||||
pai_holder.installed_software[toggle_key].toggle(pai_holder)
|
||||
|
||||
// Directives //
|
||||
/datum/pai_software/directives
|
||||
name = "Directives"
|
||||
id = "directives"
|
||||
default = TRUE
|
||||
template_file = "pai_directives"
|
||||
ui_icon = "clipboard-list"
|
||||
|
||||
/datum/pai_software/directives/get_app_data(mob/living/silicon/pai/user)
|
||||
var/list/data = list()
|
||||
|
||||
data["master"] = user.master
|
||||
data["dna"] = user.master_dna
|
||||
data["prime"] = user.pai_law0
|
||||
data["supplemental"] = user.pai_laws
|
||||
|
||||
return data
|
||||
|
||||
/datum/pai_software/directives/tgui_act(action, list/params)
|
||||
if(..())
|
||||
return
|
||||
|
||||
. = TRUE
|
||||
|
||||
switch(action)
|
||||
if("getdna")
|
||||
var/mob/living/M = get_holding_mob()
|
||||
if(!istype(M))
|
||||
return
|
||||
|
||||
// Check the carrier
|
||||
var/answer = alert(M, "[pai_holder] is requesting a DNA sample from you. Will you allow it to confirm your identity?", "[pai_holder] Check DNA", "Yes", "No")
|
||||
if(answer == "Yes")
|
||||
M.visible_message("<span class='notice'>[M] presses [M.p_their()] thumb against [pai_holder].</span>", "<span class='notice'>You press your thumb against [pai_holder].</span>")
|
||||
var/datum/dna/dna = M.dna
|
||||
to_chat(usr, "<span class='notice'>[M]'s UE string: [dna.unique_enzymes]</span>")
|
||||
if(dna.unique_enzymes == pai_holder.master_dna)
|
||||
to_chat(usr, "<span class='notice'>DNA is a match to stored Master DNA.</span>")
|
||||
else
|
||||
to_chat(usr, "<span class='warning'>DNA does not match stored Master DNA.</span>")
|
||||
else
|
||||
to_chat(usr, "<span class='warning'>[M] does not seem like [M.p_they()] [M.p_are()] going to provide a DNA sample willingly.</span>")
|
||||
|
||||
// Crew Manifest //
|
||||
/datum/pai_software/crew_manifest
|
||||
name = "Crew Manifest"
|
||||
ram_cost = 5
|
||||
id = "manifest"
|
||||
template_file = "pai_manifest"
|
||||
ui_icon = "users"
|
||||
|
||||
/datum/pai_software/crew_manifest/get_app_data(mob/living/silicon/pai/user)
|
||||
var/list/data = list()
|
||||
|
||||
GLOB.data_core.get_manifest_json()
|
||||
data["manifest"] = GLOB.PDA_Manifest
|
||||
|
||||
return data
|
||||
|
||||
// Med Records //
|
||||
/datum/pai_software/med_records
|
||||
name = "Medical Records"
|
||||
ram_cost = 15
|
||||
id = "med_records"
|
||||
template_file = "pai_medrecords"
|
||||
ui_icon = "heartbeat"
|
||||
/// Integrated medical records module to reduce duplicated code
|
||||
var/datum/data/pda/app/crew_records/medical/integrated_records = new
|
||||
|
||||
/datum/pai_software/med_records/get_app_data(mob/living/silicon/pai/user)
|
||||
var/list/data = list()
|
||||
// Just grab the stuff internally
|
||||
integrated_records.update_ui(user, data)
|
||||
return data
|
||||
|
||||
/datum/pai_software/med_records/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
|
||||
if(..())
|
||||
return
|
||||
// Double proxy here
|
||||
integrated_records.tgui_act(action, params, ui, state)
|
||||
|
||||
// Sec Records //
|
||||
/datum/pai_software/sec_records
|
||||
name = "Security Records"
|
||||
ram_cost = 15
|
||||
id = "sec_records"
|
||||
template_file = "pai_secrecords"
|
||||
ui_icon = "id-badge"
|
||||
/// Integrated security records module to reduce duplicated code
|
||||
var/datum/data/pda/app/crew_records/security/integrated_records = new
|
||||
|
||||
/datum/pai_software/sec_records/get_app_data(mob/living/silicon/pai/user)
|
||||
var/list/data = list()
|
||||
// Just grab the stuff internally
|
||||
integrated_records.update_ui(user, data)
|
||||
return data
|
||||
|
||||
/datum/pai_software/sec_records/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
|
||||
if(..())
|
||||
return
|
||||
// Double proxy here
|
||||
integrated_records.tgui_act(action, params, ui, state)
|
||||
|
||||
// Atmos Scan //
|
||||
/datum/pai_software/atmosphere_sensor
|
||||
name = "Atmosphere Sensor"
|
||||
ram_cost = 5
|
||||
id = "atmos_sense"
|
||||
template_file = "pai_atmosphere"
|
||||
ui_icon = "fire"
|
||||
/// Integrated PDA atmos scan module to reduce duplicated code
|
||||
var/datum/data/pda/app/atmos_scanner/scanner = new
|
||||
|
||||
/datum/pai_software/atmosphere_sensor/get_app_data(mob/living/silicon/pai/user)
|
||||
var/list/data = list()
|
||||
// Just grab the stuff internally
|
||||
scanner.update_ui(user, data)
|
||||
return data
|
||||
|
||||
// Messenger //
|
||||
/datum/pai_software/messenger
|
||||
name = "Digital Messenger"
|
||||
ram_cost = 5
|
||||
id = "messenger"
|
||||
template_file = "pai_messenger"
|
||||
ui_icon = "envelope"
|
||||
|
||||
/datum/pai_software/messenger/get_app_data(mob/living/silicon/pai/user)
|
||||
var/list/data = list()
|
||||
|
||||
// Some safety checks
|
||||
if(!user.pda)
|
||||
CRASH("pAI found without PDA.")
|
||||
|
||||
var/datum/data/pda/app/messenger/PM = user.pda.find_program(/datum/data/pda/app/messenger)
|
||||
if(!PM)
|
||||
CRASH("pAI PDA lacks a messenger program")
|
||||
|
||||
// Grab the internal data
|
||||
PM.update_ui(user, data)
|
||||
|
||||
return data
|
||||
|
||||
/datum/pai_software/messenger/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
|
||||
if(..())
|
||||
return
|
||||
|
||||
// Grab their messenger
|
||||
var/datum/data/pda/app/messenger/PM = pai_holder.pda.find_program(/datum/data/pda/app/messenger)
|
||||
// Double proxy here
|
||||
PM.tgui_act(action, params, ui, state)
|
||||
|
||||
// Radio
|
||||
/datum/pai_software/radio_config
|
||||
name = "Radio Configuration"
|
||||
id = "radio"
|
||||
default = TRUE
|
||||
template_file = "pai_radio"
|
||||
ui_icon = "broadcast-tower"
|
||||
|
||||
/datum/pai_software/radio_config/get_app_data(mob/living/silicon/pai/user)
|
||||
var/list/data = list()
|
||||
data["frequency"] = user.radio.frequency
|
||||
data["minFrequency"] = PUBLIC_LOW_FREQ
|
||||
data["maxFrequency"] = PUBLIC_HIGH_FREQ
|
||||
data["broadcasting"] = user.radio.broadcasting
|
||||
return data
|
||||
|
||||
/datum/pai_software/radio_config/tgui_act(action, list/params)
|
||||
if(..())
|
||||
return
|
||||
|
||||
switch(action)
|
||||
if("toggleBroadcast")
|
||||
// Just toggle it
|
||||
pai_holder.radio.broadcasting = !pai_holder.radio.broadcasting
|
||||
|
||||
if("freq")
|
||||
var/new_frequency = sanitize_frequency(text2num(params["freq"]) * 10)
|
||||
pai_holder.radio.set_frequency(new_frequency)
|
||||
|
||||
// Signaler //
|
||||
/datum/pai_software/signaler
|
||||
name = "Remote Signaler"
|
||||
ram_cost = 5
|
||||
id = "signaler"
|
||||
template_file = "pai_signaler"
|
||||
ui_icon = "rss"
|
||||
|
||||
/datum/pai_software/signaler/get_app_data(mob/living/silicon/pai/user)
|
||||
var/list/data = list()
|
||||
|
||||
data["frequency"] = user.sradio.frequency
|
||||
data["code"] = user.sradio.code
|
||||
data["minFrequency"] = PUBLIC_LOW_FREQ
|
||||
data["maxFrequency"] = PUBLIC_HIGH_FREQ
|
||||
|
||||
return data
|
||||
|
||||
/datum/pai_software/signaler/tgui_act(action, list/params)
|
||||
if(..())
|
||||
return
|
||||
|
||||
switch(action)
|
||||
if("signal")
|
||||
pai_holder.sradio.send_signal("ACTIVATE")
|
||||
|
||||
if("freq")
|
||||
var/new_frequency = sanitize_frequency(text2num(params["freq"]) * 10)
|
||||
pai_holder.sradio.set_frequency(new_frequency)
|
||||
|
||||
if("code")
|
||||
pai_holder.sradio.code = clamp(text2num(params["code"]), 1, 100)
|
||||
|
||||
// Door Jack //
|
||||
/datum/pai_software/door_jack
|
||||
name = "Door Jack"
|
||||
ram_cost = 30
|
||||
id = "door_jack"
|
||||
template_file = "pai_doorjack"
|
||||
ui_icon = "door-open"
|
||||
/// Progress on hacking the door
|
||||
var/progress = 0
|
||||
/// Are we hacking?
|
||||
var/hacking = FALSE
|
||||
/// The cable being plugged into a door
|
||||
var/obj/item/pai_cable/cable
|
||||
/// The door being hacked
|
||||
var/obj/machinery/door/hackdoor
|
||||
|
||||
/datum/pai_software/door_jack/get_app_data(mob/living/silicon/pai/user)
|
||||
var/list/data = list()
|
||||
|
||||
data["cable"] = (cable != null)
|
||||
data["machine"] = (cable?.machine != null)
|
||||
data["inprogress"] = (hackdoor != null)
|
||||
data["progress"] = progress
|
||||
|
||||
return data
|
||||
|
||||
/datum/pai_software/door_jack/tgui_act(action, list/params)
|
||||
if(..())
|
||||
return
|
||||
|
||||
switch(action)
|
||||
if("jack")
|
||||
if(cable && cable.machine)
|
||||
hackdoor = cable.machine
|
||||
if(hacking)
|
||||
to_chat(usr, "<span class='warning'>You are already hacking that door!</span>")
|
||||
else
|
||||
hacking = TRUE
|
||||
INVOKE_ASYNC(src, /datum/pai_software/door_jack/.proc/hackloop)
|
||||
if("cancel")
|
||||
hackdoor = null
|
||||
if("cable")
|
||||
if(cable)
|
||||
to_chat(usr, "<span class='warning'>You already have a cable deployed!</span>")
|
||||
return
|
||||
var/turf/T = get_turf(pai_holder)
|
||||
cable = new /obj/item/pai_cable(T)
|
||||
pai_holder.visible_message("<span class='warning'>A port on [pai_holder] opens to reveal [cable], which promptly falls to the floor.</span>")
|
||||
|
||||
/**
|
||||
* Door jack hack loop
|
||||
*
|
||||
* Self-contained proc for handling the hacking of a door.
|
||||
* Invoked asyncly, but will only allow one instance at a time
|
||||
*/
|
||||
/datum/pai_software/door_jack/proc/hackloop()
|
||||
var/obj/machinery/door/D = cable.machine
|
||||
if(!istype(D))
|
||||
cleanup_hack()
|
||||
return
|
||||
while(progress < 100)
|
||||
if(cable && cable.machine == D && cable.machine == hackdoor && get_dist(pai_holder, hackdoor) <= 1)
|
||||
progress = min(progress + rand(1, 20), 100)
|
||||
else
|
||||
cleanup_hack()
|
||||
return
|
||||
if(progress >= 100)
|
||||
D.open()
|
||||
cleanup_hack()
|
||||
return
|
||||
sleep(1 SECONDS) // Update every second
|
||||
|
||||
/**
|
||||
* Door jack cleanup proc
|
||||
*
|
||||
* Self-contained proc for cleaning up failed hack attempts
|
||||
*/
|
||||
/datum/pai_software/door_jack/proc/cleanup_hack()
|
||||
progress = 0
|
||||
hackdoor = null
|
||||
cable.machine = null
|
||||
QDEL_NULL(cable)
|
||||
hacking = FALSE
|
||||
|
||||
// Host Bioscan //
|
||||
/datum/pai_software/host_scan
|
||||
name = "Host Bioscan"
|
||||
ram_cost = 5
|
||||
id = "bioscan"
|
||||
template_file = "pai_bioscan"
|
||||
ui_icon = "heartbeat"
|
||||
|
||||
/datum/pai_software/host_scan/get_app_data(mob/living/silicon/pai/user)
|
||||
var/list/data = list()
|
||||
|
||||
var/mob/living/held = get_holding_mob(FALSE)
|
||||
|
||||
if(isliving(held))
|
||||
data["holder"] = held.name
|
||||
data["dead"] = (held.stat > UNCONSCIOUS)
|
||||
data["health"] = held.health
|
||||
data["brute"] = held.getBruteLoss()
|
||||
data["oxy"] = held.getOxyLoss()
|
||||
data["tox"] = held.getToxLoss()
|
||||
data["burn"] = held.getFireLoss()
|
||||
|
||||
return data
|
||||
@@ -0,0 +1,95 @@
|
||||
// Security Hud //
|
||||
/datum/pai_software/sec_hud
|
||||
name = "Security HUD"
|
||||
ram_cost = 20
|
||||
id = "sec_hud"
|
||||
ui_icon = "exclamation-triangle"
|
||||
toggle_software = TRUE
|
||||
|
||||
/datum/pai_software/sec_hud/toggle(mob/living/silicon/pai/user)
|
||||
user.secHUD = !user.secHUD
|
||||
user.remove_med_sec_hud()
|
||||
if(user.secHUD)
|
||||
user.add_sec_hud()
|
||||
|
||||
/datum/pai_software/sec_hud/is_active(mob/living/silicon/pai/user)
|
||||
return user.secHUD
|
||||
|
||||
// Medical Hud //
|
||||
/datum/pai_software/med_hud
|
||||
name = "Medical HUD"
|
||||
ram_cost = 20
|
||||
id = "med_hud"
|
||||
ui_icon = "first-aid"
|
||||
toggle_software = TRUE
|
||||
|
||||
/datum/pai_software/med_hud/toggle(mob/living/silicon/pai/user)
|
||||
user.medHUD = !user.medHUD
|
||||
user.remove_med_sec_hud()
|
||||
if(user.medHUD)
|
||||
user.add_med_hud()
|
||||
|
||||
/datum/pai_software/med_hud/is_active(mob/living/silicon/pai/user)
|
||||
return user.medHUD
|
||||
|
||||
// Universal Translator //
|
||||
/datum/pai_software/translator
|
||||
name = "Universal Translator"
|
||||
ram_cost = 35
|
||||
id = "translator"
|
||||
ui_icon = "language"
|
||||
toggle_software = TRUE
|
||||
|
||||
/datum/pai_software/translator/toggle(mob/living/silicon/pai/user)
|
||||
// Galactic Common, Sol Common, Tradeband, Gutter and Trinary are added with New() and are therefore the current default, always active languages
|
||||
user.translator_on = !user.translator_on
|
||||
if(user.translator_on)
|
||||
user.add_language("Sinta'unathi")
|
||||
user.add_language("Siik'tajr")
|
||||
user.add_language("Canilunzt")
|
||||
user.add_language("Skrellian")
|
||||
user.add_language("Vox-pidgin")
|
||||
user.add_language("Rootspeak")
|
||||
user.add_language("Chittin")
|
||||
user.add_language("Bubblish")
|
||||
user.add_language("Orluum")
|
||||
user.add_language("Clownish")
|
||||
user.add_language("Neo-Russkiya")
|
||||
else
|
||||
user.remove_language("Sinta'unathi")
|
||||
user.remove_language("Siik'tajr")
|
||||
user.remove_language("Canilunzt")
|
||||
user.remove_language("Skrellian")
|
||||
user.remove_language("Vox-pidgin")
|
||||
user.remove_language("Rootspeak")
|
||||
user.remove_language("Chittin")
|
||||
user.remove_language("Bubblish")
|
||||
user.remove_language("Orluum")
|
||||
user.remove_language("Clownish")
|
||||
user.remove_language("Neo-Russkiya")
|
||||
|
||||
/datum/pai_software/translator/is_active(mob/living/silicon/pai/user)
|
||||
return user.translator_on
|
||||
|
||||
// FLashlight //
|
||||
/datum/pai_software/flashlight
|
||||
name = "Flashlight"
|
||||
ram_cost = 5
|
||||
id = "flashlight"
|
||||
ui_icon = "lightbulb"
|
||||
toggle_software = TRUE
|
||||
|
||||
/datum/pai_software/flashlight/toggle(mob/living/silicon/pai/user)
|
||||
var/atom/movable/actual_location = istype(user.loc, /obj/item/paicard) ? user.loc : user
|
||||
if(!user.flashlight_on)
|
||||
actual_location.set_light(2)
|
||||
user.card.set_light(2)
|
||||
else
|
||||
actual_location.set_light(0)
|
||||
user.card.set_light(0)
|
||||
|
||||
user.flashlight_on = !user.flashlight_on
|
||||
|
||||
/datum/pai_software/flashlight/is_active(mob/living/silicon/pai/user)
|
||||
return user.flashlight_on
|
||||
|
||||
@@ -1,624 +0,0 @@
|
||||
/datum/pai_software
|
||||
// Name for the software. This is used as the button text when buying or opening/toggling the software
|
||||
var/name = "pAI software module"
|
||||
// RAM cost; pAIs start with 100 RAM, spending it on programs
|
||||
var/ram_cost = 0
|
||||
// ID for the software. This must be unique
|
||||
var/id = ""
|
||||
// Whether this software is a toggle or not
|
||||
// Toggled software should override toggle() and is_active()
|
||||
// Non-toggled software should override on_ui_data() and Topic()
|
||||
var/toggle = 1
|
||||
// Whether pAIs should automatically receive this module at no cost
|
||||
var/default = 0
|
||||
|
||||
// Vars for pAI nanoUI handling
|
||||
var/autoupdate = 0
|
||||
var/template_file = "oops"
|
||||
var/ui_title = "somebody forgot to set this"
|
||||
var/ui_width = 450
|
||||
var/ui_height = 600
|
||||
|
||||
/datum/pai_software/proc/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state)
|
||||
return list()
|
||||
|
||||
/datum/pai_software/proc/toggle(mob/living/silicon/pai/user)
|
||||
return
|
||||
|
||||
/datum/pai_software/proc/is_active(mob/living/silicon/pai/user)
|
||||
return 0
|
||||
|
||||
/datum/pai_software/directives
|
||||
name = "Directives"
|
||||
ram_cost = 0
|
||||
id = "directives"
|
||||
toggle = 0
|
||||
default = 1
|
||||
|
||||
template_file = "pai_directives.tmpl"
|
||||
ui_title = "pAI Directives"
|
||||
autoupdate = 1
|
||||
|
||||
/datum/pai_software/directives/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state)
|
||||
var/data[0]
|
||||
|
||||
data["master"] = user.master
|
||||
data["dna"] = user.master_dna
|
||||
data["prime"] = user.pai_law0
|
||||
data["supplemental"] = user.pai_laws
|
||||
|
||||
return data
|
||||
|
||||
/datum/pai_software/directives/Topic(href, href_list)
|
||||
var/mob/living/silicon/pai/P = usr
|
||||
if(!istype(P)) return
|
||||
|
||||
if(href_list["getdna"])
|
||||
var/mob/living/M = P.loc
|
||||
var/count = 0
|
||||
|
||||
// Find the carrier
|
||||
while(!istype(M, /mob/living))
|
||||
if(!M || !M.loc || count > 6)
|
||||
//For a runtime where M ends up in nullspace (similar to bluespace but less colourful)
|
||||
to_chat(P, "You are not being carried by anyone!")
|
||||
return 0
|
||||
M = M.loc
|
||||
count++
|
||||
|
||||
// Check the carrier
|
||||
var/answer = input(M, "[P] is requesting a DNA sample from you. Will you allow it to confirm your identity?", "[P] Check DNA", "No") in list("Yes", "No")
|
||||
if(answer == "Yes")
|
||||
var/turf/T = get_turf_or_move(P.loc)
|
||||
for(var/mob/v in viewers(T))
|
||||
v.show_message("<span class='notice'>[M] presses [M.p_their()] thumb against [P].</span>", 3, "<span class='notice'>[P] makes a sharp clicking sound as it extracts DNA material from [M].</span>", 2)
|
||||
var/datum/dna/dna = M.dna
|
||||
to_chat(P, "<font color = red><h3>[M]'s UE string : [dna.unique_enzymes]</h3></font>")
|
||||
if(dna.unique_enzymes == P.master_dna)
|
||||
to_chat(P, "<b>DNA is a match to stored Master DNA.</b>")
|
||||
else
|
||||
to_chat(P, "<b>DNA does not match stored Master DNA.</b>")
|
||||
else
|
||||
to_chat(P, "[M] does not seem like [M.p_they()] [M.p_are()] going to provide a DNA sample willingly.")
|
||||
return 1
|
||||
|
||||
/datum/pai_software/radio_config
|
||||
name = "Radio Configuration"
|
||||
ram_cost = 0
|
||||
id = "radio"
|
||||
toggle = 0
|
||||
default = 1
|
||||
|
||||
template_file = "pai_radio.tmpl"
|
||||
ui_title = "Radio Configuration"
|
||||
ui_width = 300
|
||||
ui_height = 150
|
||||
|
||||
/datum/pai_software/radio_config/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state)
|
||||
var/data[0]
|
||||
|
||||
data["listening"] = user.radio.broadcasting
|
||||
data["frequency"] = format_frequency(user.radio.frequency)
|
||||
var/channels[0]
|
||||
for(var/ch_name in user.radio.channels)
|
||||
var/ch_stat = user.radio.channels[ch_name]
|
||||
var/ch_dat[0]
|
||||
ch_dat["name"] = ch_name
|
||||
// FREQ_LISTENING is const in /obj/item/radio
|
||||
ch_dat["listening"] = !!(ch_stat & user.radio.FREQ_LISTENING)
|
||||
channels[++channels.len] = ch_dat
|
||||
|
||||
data["channels"] = channels
|
||||
|
||||
return data
|
||||
|
||||
/datum/pai_software/radio_config/Topic(href, href_list)
|
||||
var/mob/living/silicon/pai/P = usr
|
||||
if(!istype(P)) return
|
||||
|
||||
P.radio.Topic(href, href_list)
|
||||
return 1
|
||||
|
||||
/datum/pai_software/crew_manifest
|
||||
name = "Crew Manifest"
|
||||
ram_cost = 5
|
||||
id = "manifest"
|
||||
toggle = 0
|
||||
|
||||
autoupdate = 1
|
||||
template_file = "pai_manifest.tmpl"
|
||||
ui_title = "Crew Manifest"
|
||||
|
||||
/datum/pai_software/crew_manifest/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state)
|
||||
var/data[0]
|
||||
|
||||
GLOB.data_core.get_manifest_json()
|
||||
data["manifest"] = GLOB.PDA_Manifest
|
||||
|
||||
return data
|
||||
|
||||
/datum/pai_software/messenger
|
||||
name = "Digital Messenger"
|
||||
ram_cost = 5
|
||||
id = "messenger"
|
||||
toggle = 0
|
||||
|
||||
autoupdate = 1
|
||||
template_file = "pai_messenger.tmpl"
|
||||
ui_title = "Digital Messenger"
|
||||
|
||||
/datum/pai_software/messenger/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state)
|
||||
var/data[0]
|
||||
|
||||
if(!user.pda)
|
||||
log_runtime(EXCEPTION("pAI found without PDA."), user)
|
||||
return data
|
||||
var/datum/data/pda/app/messenger/M = user.pda.find_program(/datum/data/pda/app/messenger)
|
||||
if(!M)
|
||||
log_runtime(EXCEPTION("pAI PDA lacks a messenger program"), user)
|
||||
return data
|
||||
|
||||
data["receiver_off"] = M.toff
|
||||
data["ringer_off"] = M.notify_silent
|
||||
data["current_ref"] = null
|
||||
data["current_name"] = user.current_pda_messaging
|
||||
|
||||
var/pdas[0]
|
||||
if(!M.toff)
|
||||
for(var/obj/item/pda/P in GLOB.PDAs)
|
||||
var/datum/data/pda/app/messenger/PM = P.find_program(/datum/data/pda/app/messenger)
|
||||
|
||||
if(P == user.pda || !PM || !PM.can_receive())
|
||||
continue
|
||||
var/pda[0]
|
||||
pda["name"] = "[P]"
|
||||
pda["owner"] = "[P.owner]"
|
||||
pda["ref"] = "\ref[P]"
|
||||
if(P.owner == user.current_pda_messaging)
|
||||
data["current_ref"] = "\ref[P]"
|
||||
pdas[++pdas.len] = pda
|
||||
|
||||
data["pdas"] = pdas
|
||||
|
||||
var/messages[0]
|
||||
if(user.current_pda_messaging)
|
||||
for(var/index in M.tnote)
|
||||
if(index["owner"] != user.current_pda_messaging)
|
||||
continue
|
||||
var/msg[0]
|
||||
var/sent = index["sent"]
|
||||
msg["sent"] = sent ? 1 : 0
|
||||
msg["target"] = index["owner"]
|
||||
msg["message"] = index["message"]
|
||||
messages[++messages.len] = msg
|
||||
|
||||
data["messages"] = messages
|
||||
|
||||
return data
|
||||
|
||||
/datum/pai_software/messenger/Topic(href, href_list)
|
||||
var/mob/living/silicon/pai/P = usr
|
||||
if(!istype(P)) return
|
||||
|
||||
if(!isnull(P.pda))
|
||||
var/datum/data/pda/app/messenger/M = P.pda.find_program(/datum/data/pda/app/messenger)
|
||||
if(!M)
|
||||
return
|
||||
|
||||
if(href_list["toggler"])
|
||||
M.toff = href_list["toggler"] != "1"
|
||||
return 1
|
||||
else if(href_list["ringer"])
|
||||
M.notify_silent = href_list["ringer"] != "1"
|
||||
return 1
|
||||
else if(href_list["select"])
|
||||
var/s = href_list["select"]
|
||||
if(s == "*NONE*")
|
||||
P.current_pda_messaging = null
|
||||
else
|
||||
P.current_pda_messaging = s
|
||||
return 1
|
||||
else if(href_list["target"])
|
||||
if(P.silence_time)
|
||||
return alert("Communications circuits remain uninitialized.")
|
||||
|
||||
var/target = locate(href_list["target"])
|
||||
M.create_message(P, target, 1)
|
||||
return 1
|
||||
|
||||
/datum/pai_software/med_records
|
||||
name = "Medical Records"
|
||||
ram_cost = 15
|
||||
id = "med_records"
|
||||
toggle = 0
|
||||
|
||||
autoupdate = 1
|
||||
template_file = "pai_medrecords.tmpl"
|
||||
ui_title = "Medical Records"
|
||||
|
||||
|
||||
/datum/pai_software/med_records/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state)
|
||||
var/data[0]
|
||||
|
||||
var/records[0]
|
||||
for(var/datum/data/record/general in sortRecord(GLOB.data_core.general))
|
||||
var/record[0]
|
||||
record["name"] = general.fields["name"]
|
||||
record["ref"] = "\ref[general]"
|
||||
records[++records.len] = record
|
||||
|
||||
data["records"] = records
|
||||
|
||||
var/datum/data/record/G = user.medicalActive1
|
||||
var/datum/data/record/M = user.medicalActive2
|
||||
data["general"] = G ? G.fields : null
|
||||
data["medical"] = M ? M.fields : null
|
||||
data["could_not_find"] = user.medical_cannotfind
|
||||
|
||||
return data
|
||||
|
||||
/datum/pai_software/med_records/Topic(href, href_list)
|
||||
var/mob/living/silicon/pai/P = usr
|
||||
if(!istype(P)) return
|
||||
|
||||
if(href_list["select"])
|
||||
var/datum/data/record/record = locate(href_list["select"])
|
||||
if(record)
|
||||
var/datum/data/record/R = record
|
||||
var/datum/data/record/M = null
|
||||
if(!( GLOB.data_core.general.Find(R) ))
|
||||
P.medical_cannotfind = 1
|
||||
else
|
||||
P.medical_cannotfind = 0
|
||||
for(var/datum/data/record/E in GLOB.data_core.medical)
|
||||
if((E.fields["name"] == R.fields["name"] || E.fields["id"] == R.fields["id"]))
|
||||
M = E
|
||||
P.medicalActive1 = R
|
||||
P.medicalActive2 = M
|
||||
else
|
||||
P.medical_cannotfind = 1
|
||||
return 1
|
||||
|
||||
/datum/pai_software/sec_records
|
||||
name = "Security Records"
|
||||
ram_cost = 15
|
||||
id = "sec_records"
|
||||
toggle = 0
|
||||
|
||||
autoupdate = 1
|
||||
template_file = "pai_secrecords.tmpl"
|
||||
ui_title = "Security Records"
|
||||
|
||||
|
||||
/datum/pai_software/sec_records/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state)
|
||||
var/data[0]
|
||||
|
||||
var/records[0]
|
||||
for(var/datum/data/record/general in sortRecord(GLOB.data_core.general))
|
||||
var/record[0]
|
||||
record["name"] = general.fields["name"]
|
||||
record["ref"] = "\ref[general]"
|
||||
records[++records.len] = record
|
||||
|
||||
data["records"] = records
|
||||
|
||||
var/datum/data/record/G = user.securityActive1
|
||||
var/datum/data/record/S = user.securityActive2
|
||||
data["general"] = G ? G.fields : null
|
||||
data["security"] = S ? S.fields : null
|
||||
data["could_not_find"] = user.security_cannotfind
|
||||
|
||||
return data
|
||||
|
||||
/datum/pai_software/sec_records/Topic(href, href_list)
|
||||
var/mob/living/silicon/pai/P = usr
|
||||
if(!istype(P)) return
|
||||
|
||||
if(href_list["select"])
|
||||
var/datum/data/record/record = locate(href_list["select"])
|
||||
if(record)
|
||||
var/datum/data/record/R = record
|
||||
var/datum/data/record/S = null
|
||||
if(!( GLOB.data_core.general.Find(R) ))
|
||||
P.securityActive1 = null
|
||||
P.securityActive2 = null
|
||||
P.security_cannotfind = 1
|
||||
else
|
||||
P.security_cannotfind = 0
|
||||
for(var/datum/data/record/E in GLOB.data_core.security)
|
||||
if((E.fields["name"] == R.fields["name"] || E.fields["id"] == R.fields["id"]))
|
||||
S = E
|
||||
P.securityActive1 = R
|
||||
P.securityActive2 = S
|
||||
else
|
||||
P.securityActive1 = null
|
||||
P.securityActive2 = null
|
||||
P.security_cannotfind = 1
|
||||
return 1
|
||||
|
||||
/datum/pai_software/door_jack
|
||||
name = "Door Jack"
|
||||
ram_cost = 30
|
||||
id = "door_jack"
|
||||
toggle = 0
|
||||
|
||||
autoupdate = 1
|
||||
template_file = "pai_doorjack.tmpl"
|
||||
ui_title = "Door Jack"
|
||||
ui_width = 300
|
||||
ui_height = 150
|
||||
|
||||
/datum/pai_software/door_jack/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state)
|
||||
var/data[0]
|
||||
|
||||
data["cable"] = user.cable != null
|
||||
data["machine"] = user.cable && (user.cable.machine != null)
|
||||
data["inprogress"] = user.hackdoor != null
|
||||
data["progress_a"] = round(user.hackprogress / 10)
|
||||
data["progress_b"] = user.hackprogress % 10
|
||||
data["aborted"] = user.hack_aborted
|
||||
|
||||
return data
|
||||
|
||||
/datum/pai_software/door_jack/Topic(href, href_list)
|
||||
var/mob/living/silicon/pai/P = usr
|
||||
if(!istype(P)) return
|
||||
|
||||
if(href_list["jack"])
|
||||
if(P.cable && P.cable.machine)
|
||||
P.hackdoor = P.cable.machine
|
||||
P.hackloop()
|
||||
return 1
|
||||
else if(href_list["cancel"])
|
||||
P.hackdoor = null
|
||||
return 1
|
||||
else if(href_list["cable"])
|
||||
var/turf/T = get_turf_or_move(P.loc)
|
||||
P.hack_aborted = 0
|
||||
P.cable = new /obj/item/pai_cable(T)
|
||||
for(var/mob/M in viewers(T))
|
||||
M.show_message("<span class='warning'>A port on [P] opens to reveal [P.cable], which promptly falls to the floor.</span>", 3,
|
||||
"<span class='warning'>You hear the soft click of something light and hard falling to the ground.</span>", 2)
|
||||
return 1
|
||||
|
||||
/mob/living/silicon/pai/proc/hackloop()
|
||||
var/obj/machinery/door/D = cable.machine
|
||||
if(!istype(D))
|
||||
hack_aborted = 1
|
||||
hackprogress = 0
|
||||
cable.machine = null
|
||||
hackdoor = null
|
||||
return
|
||||
while(hackprogress < 1000)
|
||||
if(cable && cable.machine == D && cable.machine == hackdoor && get_dist(src, hackdoor) <= 1)
|
||||
hackprogress = min(hackprogress+rand(1, 20), 1000)
|
||||
else
|
||||
hack_aborted = 1
|
||||
hackprogress = 0
|
||||
hackdoor = null
|
||||
return
|
||||
if(hackprogress >= 1000)
|
||||
hackprogress = 0
|
||||
D.open()
|
||||
cable.machine = null
|
||||
return
|
||||
sleep(10) // Update every second
|
||||
|
||||
/datum/pai_software/atmosphere_sensor
|
||||
name = "Atmosphere Sensor"
|
||||
ram_cost = 5
|
||||
id = "atmos_sense"
|
||||
toggle = 0
|
||||
|
||||
template_file = "pai_atmosphere.tmpl"
|
||||
ui_title = "Atmosphere Sensor"
|
||||
ui_width = 350
|
||||
ui_height = 300
|
||||
|
||||
|
||||
/datum/pai_software/atmosphere_sensor/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state)
|
||||
var/data[0]
|
||||
|
||||
var/turf/T = get_turf_or_move(user.loc)
|
||||
if(!T)
|
||||
data["reading"] = 0
|
||||
data["pressure"] = 0
|
||||
data["temperature"] = 0
|
||||
data["temperatureC"] = 0
|
||||
data["gas"] = list()
|
||||
else
|
||||
var/datum/gas_mixture/env = T.return_air()
|
||||
data["reading"] = 1
|
||||
data["pressure"] = env.return_pressure()
|
||||
data["temperature"] = round(env.temperature)
|
||||
data["temperatureC"] = round(env.temperature-T0C)
|
||||
|
||||
var/t_moles = env.total_moles()
|
||||
var/gases[0]
|
||||
if(t_moles)
|
||||
var/n2[0]
|
||||
n2["name"] = "Nitrogen"
|
||||
n2["percent"] = round((env.nitrogen/t_moles)*100)
|
||||
var/o2[0]
|
||||
o2["name"] = "Oxygen"
|
||||
o2["percent"] = round((env.oxygen/t_moles)*100)
|
||||
var/co2[0]
|
||||
co2["name"] = "Carbon Dioxide"
|
||||
co2["percent"] = round((env.carbon_dioxide/t_moles)*100)
|
||||
var/plasma[0]
|
||||
plasma["name"] = "Plasma"
|
||||
plasma["percent"] = round((env.toxins/t_moles)*100)
|
||||
var/other[0]
|
||||
other["name"] = "Other"
|
||||
other["percent"] = round(1-((env.oxygen/t_moles)+(env.nitrogen/t_moles)+(env.carbon_dioxide/t_moles)+(env.toxins/t_moles)))
|
||||
gases[++gases.len] = n2
|
||||
gases[++gases.len] = o2
|
||||
gases[++gases.len] = co2
|
||||
gases[++gases.len] = plasma
|
||||
gases[++gases.len] = other
|
||||
data["gas"] = gases
|
||||
|
||||
return data
|
||||
|
||||
/datum/pai_software/sec_hud
|
||||
name = "Security HUD"
|
||||
ram_cost = 20
|
||||
id = "sec_hud"
|
||||
|
||||
/datum/pai_software/sec_hud/toggle(mob/living/silicon/pai/user)
|
||||
user.secHUD = !user.secHUD
|
||||
user.remove_med_sec_hud()
|
||||
if(user.secHUD)
|
||||
user.add_sec_hud()
|
||||
|
||||
/datum/pai_software/sec_hud/is_active(mob/living/silicon/pai/user)
|
||||
return user.secHUD
|
||||
|
||||
/datum/pai_software/med_hud
|
||||
name = "Medical HUD"
|
||||
ram_cost = 20
|
||||
id = "med_hud"
|
||||
|
||||
/datum/pai_software/med_hud/toggle(mob/living/silicon/pai/user)
|
||||
user.medHUD = !user.medHUD
|
||||
user.remove_med_sec_hud()
|
||||
if(user.medHUD)
|
||||
user.add_med_hud()
|
||||
|
||||
/datum/pai_software/med_hud/is_active(mob/living/silicon/pai/user)
|
||||
return user.medHUD
|
||||
|
||||
/datum/pai_software/translator
|
||||
name = "Universal Translator"
|
||||
ram_cost = 35
|
||||
id = "translator"
|
||||
|
||||
/datum/pai_software/translator/toggle(mob/living/silicon/pai/user)
|
||||
// Galactic Common, Sol Common, Tradeband, Gutter and Trinary are added with New() and are therefore the current default, always active languages
|
||||
user.translator_on = !user.translator_on
|
||||
if(user.translator_on)
|
||||
user.add_language("Sinta'unathi")
|
||||
user.add_language("Siik'tajr")
|
||||
user.add_language("Canilunzt")
|
||||
user.add_language("Skrellian")
|
||||
user.add_language("Vox-pidgin")
|
||||
user.add_language("Rootspeak")
|
||||
user.add_language("Chittin")
|
||||
user.add_language("Bubblish")
|
||||
user.add_language("Orluum")
|
||||
user.add_language("Clownish")
|
||||
user.add_language("Neo-Russkiya")
|
||||
else
|
||||
user.remove_language("Sinta'unathi")
|
||||
user.remove_language("Siik'tajr")
|
||||
user.remove_language("Canilunzt")
|
||||
user.remove_language("Skrellian")
|
||||
user.remove_language("Vox-pidgin")
|
||||
user.remove_language("Rootspeak")
|
||||
user.remove_language("Chittin")
|
||||
user.remove_language("Bubblish")
|
||||
user.remove_language("Orluum")
|
||||
user.remove_language("Clownish")
|
||||
user.remove_language("Neo-Russkiya")
|
||||
|
||||
/datum/pai_software/translator/is_active(mob/living/silicon/pai/user)
|
||||
return user.translator_on
|
||||
|
||||
/datum/pai_software/signaller
|
||||
name = "Remote Signaller"
|
||||
ram_cost = 5
|
||||
id = "signaller"
|
||||
toggle = 0
|
||||
|
||||
template_file = "pai_signaller.tmpl"
|
||||
ui_title = "Signaller"
|
||||
ui_width = 320
|
||||
ui_height = 150
|
||||
|
||||
/datum/pai_software/signaller/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state)
|
||||
var/data[0]
|
||||
|
||||
data["frequency"] = format_frequency(user.sradio.frequency)
|
||||
data["code"] = user.sradio.code
|
||||
|
||||
return data
|
||||
|
||||
/datum/pai_software/signaller/Topic(href, href_list)
|
||||
var/mob/living/silicon/pai/P = usr
|
||||
if(!istype(P)) return
|
||||
|
||||
if(href_list["send"])
|
||||
P.sradio.send_signal("ACTIVATE")
|
||||
for(var/mob/O in hearers(1, P.loc))
|
||||
O.show_message("[bicon(P)] *beep* *beep*", 3, "*beep* *beep*", 2)
|
||||
return 1
|
||||
|
||||
else if(href_list["freq"])
|
||||
var/new_frequency = (P.sradio.frequency + text2num(href_list["freq"]))
|
||||
if(new_frequency < PUBLIC_LOW_FREQ || new_frequency > PUBLIC_HIGH_FREQ)
|
||||
new_frequency = sanitize_frequency(new_frequency)
|
||||
P.sradio.set_frequency(new_frequency)
|
||||
return 1
|
||||
|
||||
else if(href_list["code"])
|
||||
P.sradio.code += text2num(href_list["code"])
|
||||
P.sradio.code = round(P.sradio.code)
|
||||
P.sradio.code = min(100, P.sradio.code)
|
||||
P.sradio.code = max(1, P.sradio.code)
|
||||
return 1
|
||||
|
||||
/datum/pai_software/host_scan
|
||||
name = "Host Bioscan"
|
||||
ram_cost = 5
|
||||
id = "bioscan"
|
||||
toggle = 0
|
||||
|
||||
template_file = "pai_bioscan.tmpl"
|
||||
ui_title = "Host Bioscan"
|
||||
ui_width = 400
|
||||
ui_height = 350
|
||||
|
||||
/datum/pai_software/host_scan/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state)
|
||||
var/data[0]
|
||||
var/mob/living/held = user.loc
|
||||
var/count = 0
|
||||
|
||||
// Find the carrier
|
||||
while(!isliving(held))
|
||||
if(!held || !held.loc || count > 6)
|
||||
//For a runtime where M ends up in nullspace (similar to bluespace but less colourful)
|
||||
to_chat(user, "You are not being carried by anyone!")
|
||||
return 0
|
||||
held = held.loc
|
||||
count++
|
||||
if(isliving(held))
|
||||
data["holder"] = held
|
||||
data["health"] = "[held.stat > 1 ? "dead" : "[held.health]% healthy"]"
|
||||
data["brute"] = "[held.getBruteLoss() > 50 ? "<font color=#FF5555>" : "<font color=#55FF55>"][held.getBruteLoss()]</font>"
|
||||
data["oxy"] = "[held.getOxyLoss() > 50 ? "<font color=#FF5555>" : "<font color=#55FF55>"][held.getOxyLoss()]</font>"
|
||||
data["tox"] = "[held.getToxLoss() > 50 ? "<font color=#FF5555>" : "<font color=#55FF55>"][held.getToxLoss()]</font>"
|
||||
data["burn"] = "[held.getFireLoss() > 50 ? "<font color=#FF5555>" : "<font color=#55FF55>"][held.getFireLoss()]</font>"
|
||||
data["temp"] = "[held.bodytemperature-T0C]°C ([held.bodytemperature*1.8-459.67]°F)"
|
||||
else
|
||||
data["holder"] = 0
|
||||
|
||||
return data
|
||||
|
||||
/datum/pai_software/flashlight
|
||||
name = "Flashlight"
|
||||
ram_cost = 5
|
||||
id = "flashlight"
|
||||
|
||||
/datum/pai_software/flashlight/toggle(mob/living/silicon/pai/user)
|
||||
var/atom/movable/actual_location = istype(user.loc, /obj/item/paicard) ? user.loc : user
|
||||
if(!user.flashlight_on)
|
||||
actual_location.set_light(2)
|
||||
user.card.set_light(2)
|
||||
else
|
||||
actual_location.set_light(0)
|
||||
user.card.set_light(0)
|
||||
|
||||
user.flashlight_on = !user.flashlight_on
|
||||
|
||||
/datum/pai_software/flashlight/is_active(mob/living/silicon/pai/user)
|
||||
return user.flashlight_on
|
||||
@@ -1,7 +1,6 @@
|
||||
/mob/living/silicon
|
||||
var/register_alarms = 1
|
||||
var/datum/nano_module/alarm_monitor/all/alarm_monitor
|
||||
var/datum/nano_module/atmos_control/atmos_control
|
||||
var/register_alarms = TRUE
|
||||
var/datum/tgui_module/atmos_control/atmos_control
|
||||
var/datum/tgui_module/crew_monitor/crew_monitor
|
||||
var/datum/nano_module/law_manager/law_manager
|
||||
var/datum/tgui_module/power_monitor/digital/power_monitor
|
||||
@@ -41,7 +40,7 @@
|
||||
set category = "Subsystems"
|
||||
set name = "Atmospherics Control"
|
||||
|
||||
atmos_control.ui_interact(usr, state = GLOB.self_state)
|
||||
atmos_control.tgui_interact(usr, state = GLOB.tgui_self_state)
|
||||
|
||||
/********************
|
||||
* Crew Monitor *
|
||||
|
||||
@@ -102,6 +102,8 @@
|
||||
var/mob/living/simple_animal/hostile/poison/terror_spider/T = thing
|
||||
if(!T.spider_myqueen)
|
||||
continue
|
||||
if(T == src)
|
||||
continue
|
||||
if(T.spider_myqueen != src)
|
||||
continue
|
||||
if(prob(50) || T.spider_tier >= spider_tier)
|
||||
|
||||
@@ -10,10 +10,10 @@
|
||||
|
||||
/mob/living/update_blurry_effects()
|
||||
if(eyes_blurred())
|
||||
add_eyeblur()
|
||||
overlay_fullscreen("blurry", /obj/screen/fullscreen/blurry)
|
||||
return 1
|
||||
else
|
||||
remove_eyeblur()
|
||||
clear_fullscreen("blurry")
|
||||
return 0
|
||||
|
||||
/mob/living/update_druggy_effects()
|
||||
@@ -133,19 +133,3 @@
|
||||
updatehealth("var edit")
|
||||
if("resize")
|
||||
update_transform()
|
||||
|
||||
/mob/proc/add_eyeblur()
|
||||
if(client?.screen)
|
||||
var/obj/screen/plane_master/game_world/GW = locate(/obj/screen/plane_master/game_world) in client.screen
|
||||
var/obj/screen/plane_master/floor/F = locate(/obj/screen/plane_master/floor) in client.screen
|
||||
GW.add_filter(EYE_BLUR_FILTER_KEY, FILTER_EYE_BLUR)
|
||||
F.add_filter(EYE_BLUR_FILTER_KEY, FILTER_EYE_BLUR)
|
||||
animate(GW.filters[GW.filters.len], size = 3, time = 5)
|
||||
animate(F.filters[F.filters.len], size = 3, time = 5)
|
||||
|
||||
/mob/proc/remove_eyeblur()
|
||||
if(client?.screen)
|
||||
var/obj/screen/plane_master/game_world/GW = locate(/obj/screen/plane_master/game_world) in client.screen
|
||||
var/obj/screen/plane_master/floor/F = locate(/obj/screen/plane_master/floor) in client.screen
|
||||
GW.remove_filter(EYE_BLUR_FILTER_KEY)
|
||||
F.remove_filter(EYE_BLUR_FILTER_KEY)
|
||||
|
||||
@@ -432,7 +432,7 @@
|
||||
spawn(30)
|
||||
for(var/C in GLOB.employmentCabinets)
|
||||
var/obj/structure/filingcabinet/employment/employmentCabinet = C
|
||||
if(!employmentCabinet.virgin)
|
||||
if(employmentCabinet.populated)
|
||||
employmentCabinet.addFile(employee)
|
||||
|
||||
/mob/new_player/proc/AnnounceCyborg(var/mob/living/character, var/rank, var/join_message)
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
/datum/nano_module/atmos_control
|
||||
name = "Atmospherics Control"
|
||||
var/obj/access = new()
|
||||
var/emagged = 0
|
||||
var/ui_ref
|
||||
var/list/monitored_alarms = null
|
||||
|
||||
/datum/nano_module/atmos_control/New(atmos_computer, req_access, req_one_access, monitored_alarm_ids)
|
||||
..()
|
||||
access.req_access = req_access
|
||||
access.req_one_access = req_one_access
|
||||
|
||||
if(monitored_alarm_ids)
|
||||
for(var/obj/machinery/alarm/alarm in GLOB.machines)
|
||||
if(alarm.alarm_id && (alarm.alarm_id in monitored_alarm_ids))
|
||||
monitored_alarms += alarm
|
||||
// machines may not yet be ordered at this point
|
||||
monitored_alarms = dd_sortedObjectList(monitored_alarms)
|
||||
|
||||
/datum/nano_module/atmos_control/Topic(href, href_list)
|
||||
if(..())
|
||||
return 1
|
||||
|
||||
if(href_list["alarm"])
|
||||
if(ui_ref)
|
||||
var/obj/machinery/alarm/alarm = locate(href_list["alarm"]) in (monitored_alarms ? monitored_alarms : GLOB.machines)
|
||||
if(alarm)
|
||||
var/datum/topic_state/air_alarm/TS = generate_state(alarm)
|
||||
alarm.ui_interact(usr, master_ui = ui_ref, state = TS)
|
||||
|
||||
/datum/nano_module/atmos_control/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/master_ui = null, var/datum/topic_state/state = GLOB.default_state)
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "atmos_control.tmpl", src.name, 900, 800, state = state)
|
||||
ui.add_template("mapContent", "atmos_control_map_content.tmpl")
|
||||
ui.add_template("mapHeader", "atmos_control_map_header.tmpl")
|
||||
|
||||
// Send nanomaps
|
||||
var/datum/asset/nanomaps = get_asset_datum(/datum/asset/simple/nanomaps)
|
||||
nanomaps.send(user)
|
||||
|
||||
ui.set_show_map(1)
|
||||
ui.open()
|
||||
ui.set_auto_update(1)
|
||||
ui_ref = ui
|
||||
|
||||
/datum/nano_module/atmos_control/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
|
||||
var/data[0]
|
||||
data["alarms"] = GLOB.air_alarm_repository.air_alarm_data(monitored_alarms)
|
||||
|
||||
return data
|
||||
|
||||
/datum/nano_module/atmos_control/proc/generate_state(air_alarm)
|
||||
var/datum/topic_state/air_alarm/state = new()
|
||||
state.atmos_control = src
|
||||
state.air_alarm = air_alarm
|
||||
return state
|
||||
|
||||
/datum/topic_state/air_alarm
|
||||
var/datum/nano_module/atmos_control/atmos_control = null
|
||||
var/obj/machinery/alarm/air_alarm = null
|
||||
|
||||
/datum/topic_state/air_alarm/can_use_topic(var/src_object, var/mob/user)
|
||||
if(!isAI(user) && !in_range(atmos_control.nano_host(), user))
|
||||
return STATUS_CLOSE
|
||||
if(has_access(user))
|
||||
return STATUS_INTERACTIVE
|
||||
return STATUS_UPDATE
|
||||
|
||||
/datum/topic_state/air_alarm/href_list(var/mob/user)
|
||||
var/list/extra_href = list()
|
||||
extra_href["remote_connection"] = TRUE
|
||||
extra_href["remote_access"] = has_access(user)
|
||||
|
||||
return extra_href
|
||||
|
||||
/datum/topic_state/air_alarm/proc/has_access(var/mob/user)
|
||||
return user && (isAI(user) || atmos_control.access.allowed(user) || atmos_control.emagged || air_alarm.rcon_setting == RCON_YES || air_alarm.emagged || (air_alarm.alarm_area.atmosalm && air_alarm.rcon_setting == RCON_AUTO))
|
||||
@@ -15,7 +15,7 @@
|
||||
icon = 'icons/obj/bureaucracy.dmi'
|
||||
icon_state = "filingcabinet"
|
||||
density = 1
|
||||
anchored = 1
|
||||
anchored = TRUE
|
||||
|
||||
|
||||
/obj/structure/filingcabinet/chestdrawer
|
||||
@@ -30,14 +30,14 @@
|
||||
icon_state = "tallcabinet"
|
||||
|
||||
|
||||
/obj/structure/filingcabinet/Initialize()
|
||||
/obj/structure/filingcabinet/Initialize(mapload)
|
||||
..()
|
||||
for(var/obj/item/I in loc)
|
||||
if(istype(I, /obj/item/paper) || istype(I, /obj/item/folder) || istype(I, /obj/item/photo))
|
||||
I.loc = src
|
||||
|
||||
|
||||
/obj/structure/filingcabinet/attackby(obj/item/P as obj, mob/user as mob, params)
|
||||
/obj/structure/filingcabinet/attackby(obj/item/P, mob/user, params)
|
||||
if(istype(P, /obj/item/paper) || istype(P, /obj/item/folder) || istype(P, /obj/item/photo) || istype(P, /obj/item/paper_bundle) || istype(P, /obj/item/documents))
|
||||
to_chat(user, "<span class='notice'>You put [P] in [src].</span>")
|
||||
user.drop_item()
|
||||
@@ -46,15 +46,15 @@
|
||||
sleep(5)
|
||||
icon_state = initial(icon_state)
|
||||
updateUsrDialog()
|
||||
else if(istype(P, /obj/item/wrench))
|
||||
playsound(loc, P.usesound, 50, 1)
|
||||
anchored = !anchored
|
||||
to_chat(user, "<span class='notice'>You [anchored ? "wrench" : "unwrench"] \the [src].</span>")
|
||||
else if(user.a_intent != INTENT_HARM)
|
||||
to_chat(user, "<span class='warning'>You can't put [P] in [src]!</span>")
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/structure/filingcabinet/wrench_act(mob/living/user, obj/item/I)
|
||||
. = TRUE
|
||||
default_unfasten_wrench(user, I)
|
||||
|
||||
/obj/structure/filingcabinet/deconstruct(disassembled = TRUE)
|
||||
if(!(flags & NODECONSTRUCT))
|
||||
new /obj/item/stack/sheet/metal(loc, 2)
|
||||
@@ -62,9 +62,9 @@
|
||||
I.forceMove(loc)
|
||||
qdel(src)
|
||||
|
||||
/obj/structure/filingcabinet/attack_hand(mob/user as mob)
|
||||
if(contents.len <= 0)
|
||||
to_chat(user, "<span class='notice'>\The [src] is empty.</span>")
|
||||
/obj/structure/filingcabinet/attack_hand(mob/user)
|
||||
if(!length(contents))
|
||||
to_chat(user, "<span class='notice'>[src] is empty.</span>")
|
||||
return
|
||||
|
||||
user.set_machine(src)
|
||||
@@ -74,7 +74,7 @@
|
||||
dat += "</table></center>"
|
||||
var/datum/browser/popup = new(user, "filingcabinet", name, 350, 300)
|
||||
popup.set_content(dat)
|
||||
popup.open(0)
|
||||
popup.open(FALSE)
|
||||
|
||||
return
|
||||
|
||||
@@ -85,8 +85,8 @@
|
||||
..()
|
||||
|
||||
/obj/structure/filingcabinet/attack_self_tk(mob/user)
|
||||
if(contents.len)
|
||||
if(prob(40 + contents.len * 5))
|
||||
if(length(contents))
|
||||
if(prob(40 + (length(contents) * 5)))
|
||||
var/obj/item/I = pick(contents)
|
||||
I.loc = loc
|
||||
if(prob(25))
|
||||
@@ -105,20 +105,19 @@
|
||||
usr.put_in_hands(P)
|
||||
updateUsrDialog()
|
||||
icon_state = "[initial(icon_state)]-open"
|
||||
spawn(0)
|
||||
sleep(5)
|
||||
icon_state = initial(icon_state)
|
||||
sleep(5)
|
||||
icon_state = initial(icon_state)
|
||||
|
||||
|
||||
/*
|
||||
* Security Record Cabinets
|
||||
*/
|
||||
/obj/structure/filingcabinet/security
|
||||
var/virgin = 1
|
||||
var/populated = FALSE
|
||||
|
||||
|
||||
/obj/structure/filingcabinet/security/proc/populate()
|
||||
if(virgin)
|
||||
if(!populated)
|
||||
for(var/datum/data/record/G in GLOB.data_core.general)
|
||||
var/datum/data/record/S
|
||||
for(var/datum/data/record/R in GLOB.data_core.security)
|
||||
@@ -133,7 +132,7 @@
|
||||
P.info += "[c]<BR>"
|
||||
P.info += "</TT>"
|
||||
P.name = "paper - '[G.fields["name"]]'"
|
||||
virgin = 0 //tabbing here is correct- it's possible for people to try and use it
|
||||
populated = TRUE //tabbing here is correct- it's possible for people to try and use it
|
||||
//before the records have been generated, so we do this inside the loop.
|
||||
|
||||
/obj/structure/filingcabinet/security/attack_hand()
|
||||
@@ -148,10 +147,10 @@
|
||||
* Medical Record Cabinets
|
||||
*/
|
||||
/obj/structure/filingcabinet/medical
|
||||
var/virgin = 1
|
||||
var/populated = FALSE
|
||||
|
||||
/obj/structure/filingcabinet/medical/proc/populate()
|
||||
if(virgin)
|
||||
if(!populated)
|
||||
for(var/datum/data/record/G in GLOB.data_core.general)
|
||||
var/datum/data/record/M
|
||||
for(var/datum/data/record/R in GLOB.data_core.medical)
|
||||
@@ -166,7 +165,7 @@
|
||||
P.info += "[c]<BR>"
|
||||
P.info += "</TT>"
|
||||
P.name = "paper - '[G.fields["name"]]'"
|
||||
virgin = 0 //tabbing here is correct- it's possible for people to try and use it
|
||||
populated = TRUE //tabbing here is correct- it's possible for people to try and use it
|
||||
//before the records have been generated, so we do this inside the loop.
|
||||
|
||||
/obj/structure/filingcabinet/medical/attack_hand()
|
||||
@@ -184,9 +183,9 @@
|
||||
GLOBAL_LIST_EMPTY(employmentCabinets)
|
||||
|
||||
/obj/structure/filingcabinet/employment
|
||||
var/cooldown = 0
|
||||
var/cooldown = FALSE // Only used for devils
|
||||
icon_state = "employmentcabinet"
|
||||
var/virgin = 1
|
||||
var/populated = FALSE
|
||||
|
||||
/obj/structure/filingcabinet/employment/New()
|
||||
GLOB.employmentCabinets += src
|
||||
@@ -210,23 +209,17 @@ GLOBAL_LIST_EMPTY(employmentCabinets)
|
||||
new /obj/item/paper/contract/employment(src, employee)
|
||||
|
||||
/obj/structure/filingcabinet/employment/attack_hand(mob/user)
|
||||
if(!cooldown)
|
||||
if(virgin)
|
||||
if(cooldown)
|
||||
to_chat(user, "<span class='warning'>[src] is jammed, give it a few seconds.</span>")
|
||||
else
|
||||
if(!populated)
|
||||
fillCurrent()
|
||||
virgin = 0
|
||||
cooldown = 1
|
||||
..()
|
||||
sleep(100) // prevents the devil from just instantly emptying the cabinet, ensuring an easy win.
|
||||
cooldown = 0
|
||||
else
|
||||
to_chat(user, "<span class='warning'>The [src] is jammed, give it a few seconds.</span>")
|
||||
populated = TRUE
|
||||
if(user.mind.special_role != "devil")
|
||||
return ..()
|
||||
|
||||
/obj/structure/filingcabinet/employment/attackby(obj/item/P, mob/user, params)
|
||||
if(istype(P, /obj/item/wrench))
|
||||
to_chat(user, "<span class='notice'>You begin to [anchored ? "wrench" : "unwrench"] [src].</span>")
|
||||
if (do_after(user,300,user))
|
||||
playsound(loc, 'sound/items/Ratchet.ogg', 50, 1)
|
||||
anchored = !anchored
|
||||
to_chat(user, "<span class='notice'>You successfully [anchored ? "wrench" : "unwrench"] [src].</span>")
|
||||
else
|
||||
return ..()
|
||||
else
|
||||
cooldown = TRUE
|
||||
..()
|
||||
sleep(10 SECONDS) // prevents the devil from just instantly emptying the cabinet, ensuring an easy win.
|
||||
cooldown = FALSE
|
||||
|
||||
@@ -97,8 +97,8 @@
|
||||
/obj/item/pda/silicon/pai/can_use()
|
||||
var/mob/living/silicon/pai/pAI = usr
|
||||
if(!istype(pAI))
|
||||
return 0
|
||||
if(!pAI.software["messenger"])
|
||||
return FALSE
|
||||
if(!pAI.installed_software["messenger"])
|
||||
to_chat(usr, "<span class='warning'>You have not purchased the digital messenger!</span>")
|
||||
return 0
|
||||
return FALSE
|
||||
return ..() && !pAI.silence_time
|
||||
|
||||
@@ -1,59 +1,30 @@
|
||||
/obj/item/gun/projectile/automatic/spikethrower
|
||||
/obj/item/gun/energy/spikethrower //It's like the cyborg LMG, uses energy to make spikes
|
||||
name = "\improper Vox spike thrower"
|
||||
desc = "A vicious alien projectile weapon. Parts of it quiver gelatinously, as though the thing is insectile and alive."
|
||||
icon = 'icons/obj/guns/projectile.dmi'
|
||||
icon_state = "spikethrower"
|
||||
item_state = "spikethrower"
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
fire_sound_text = "a strange noise"
|
||||
mag_type = /obj/item/ammo_box/magazine/internal/spikethrower
|
||||
burst_size = 2
|
||||
fire_delay = 3
|
||||
can_suppress = 0
|
||||
var/charge_tick = 0
|
||||
var/charge_delay = 15
|
||||
burst_size = 2 // burst has to be stored here
|
||||
can_charge = FALSE
|
||||
selfcharge = TRUE
|
||||
charge_delay = 10
|
||||
restricted_species = list(/datum/species/vox)
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/spike)
|
||||
|
||||
/obj/item/gun/projectile/automatic/spikethrower/New()
|
||||
..()
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
/obj/item/gun/projectile/automatic/spikethrower/Destroy()
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
return ..()
|
||||
|
||||
/obj/item/gun/projectile/automatic/spikethrower/update_icon()
|
||||
/obj/item/gun/energy/spikethrower/emp_act()
|
||||
return
|
||||
|
||||
/obj/item/gun/projectile/automatic/spikethrower/process()
|
||||
charge_tick++
|
||||
if(charge_tick < charge_delay || !magazine)
|
||||
return
|
||||
charge_tick = 0
|
||||
var/obj/item/ammo_casing/caseless/spike/S = new(get_turf(src))
|
||||
magazine.give_round(S)
|
||||
return 1
|
||||
|
||||
/obj/item/gun/projectile/automatic/spikethrower/attack_self()
|
||||
return
|
||||
|
||||
/obj/item/gun/projectile/automatic/spikethrower/process_chamber(eject_casing = 0, empty_chamber = 1)
|
||||
..()
|
||||
|
||||
/obj/item/ammo_box/magazine/internal/spikethrower
|
||||
name = "\improper Vox spikethrower internal magazine"
|
||||
ammo_type = /obj/item/ammo_casing/caseless/spike
|
||||
caliber = "spike"
|
||||
max_ammo = 10
|
||||
|
||||
/obj/item/ammo_casing/caseless/spike
|
||||
/obj/item/ammo_casing/energy/spike
|
||||
name = "alloy spike"
|
||||
desc = "A broadhead spike made out of a weird silvery metal."
|
||||
projectile_type = /obj/item/projectile/bullet/spike
|
||||
muzzle_flash_effect = null
|
||||
throwforce = 5
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
caliber = "spike"
|
||||
icon_state = "bolt"
|
||||
e_cost = 100
|
||||
delay = 3 //and delay has to be stored here on energy guns
|
||||
select_name = "spike"
|
||||
fire_sound = 'sound/weapons/bladeslice.ogg'
|
||||
|
||||
/obj/item/projectile/bullet/spike
|
||||
|
||||
@@ -318,6 +318,9 @@
|
||||
zoom_amt = 7 //Long range, enough to see in front of you, but no tiles behind you.
|
||||
shaded_charge = 1
|
||||
|
||||
/obj/item/gun/energy/sniperrifle/isHandgun()
|
||||
return FALSE // Makes it so no, you cant fit a massive, bulky, sniper under your arm
|
||||
|
||||
// Temperature Gun //
|
||||
/obj/item/gun/energy/temperature
|
||||
name = "temperature gun"
|
||||
|
||||
@@ -1001,19 +1001,14 @@
|
||||
reagent_state = LIQUID
|
||||
color = "#FFDCFF"
|
||||
taste_description = "stability"
|
||||
var/list/drug_list = list("crank","methamphetamine","space_drugs","psilocybin","ephedrine","epinephrine","stimulants","bath_salts","lsd","thc")
|
||||
|
||||
/datum/reagent/medicine/haloperidol/on_mob_life(mob/living/M)
|
||||
var/update_flags = STATUS_UPDATE_NONE
|
||||
M.reagents.remove_reagent("crank", 5)
|
||||
M.reagents.remove_reagent("methamphetamine", 5)
|
||||
M.reagents.remove_reagent("space_drugs", 5)
|
||||
M.reagents.remove_reagent("psilocybin", 5)
|
||||
M.reagents.remove_reagent("ephedrine", 5)
|
||||
M.reagents.remove_reagent("epinephrine", 5)
|
||||
M.reagents.remove_reagent("stimulants", 3)
|
||||
M.reagents.remove_reagent("bath_salts", 5)
|
||||
M.reagents.remove_reagent("lsd", 5)
|
||||
M.reagents.remove_reagent("thc", 5)
|
||||
for(var/I in M.reagents.reagent_list)
|
||||
var/datum/reagent/R = I
|
||||
if(drug_list.Find(R.id))
|
||||
M.reagents.remove_reagent(R.id, 5)
|
||||
update_flags |= M.AdjustDruggy(-5, FALSE)
|
||||
M.AdjustHallucinate(-5)
|
||||
M.AdjustJitter(-5)
|
||||
@@ -1241,9 +1236,15 @@
|
||||
can_synth = FALSE
|
||||
harmless = FALSE
|
||||
taste_description = "wholeness"
|
||||
var/list/stimulant_list = list("methamphetamine", "crank", "bath_salts", "stimulative_agent", "stimulants")
|
||||
|
||||
/datum/reagent/medicine/nanocalcium/on_mob_life(mob/living/carbon/human/M)
|
||||
var/update_flags = STATUS_UPDATE_NONE
|
||||
var/has_stimulant = FALSE
|
||||
for(var/I in M.reagents.reagent_list)
|
||||
var/datum/reagent/R = I
|
||||
if(stimulant_list.Find(R.id))
|
||||
has_stimulant = TRUE
|
||||
switch(current_cycle)
|
||||
if(1 to 19)
|
||||
M.AdjustJitter(4)
|
||||
@@ -1251,7 +1252,7 @@
|
||||
to_chat(M, "<span class='warning'>Your skin feels hot and your veins are on fire!</span>")
|
||||
if(20 to 43)
|
||||
//If they have stimulants or stimulant drugs then just apply toxin damage instead.
|
||||
if(M.reagents.has_reagent("methamphetamine") || M.reagents.has_reagent("crank") || M.reagents.has_reagent("bath_salts") || M.reagents.has_reagent("stimulative_agent") || M.reagents.has_reagent("stimulants"))
|
||||
if(has_stimulant == TRUE)
|
||||
update_flags |= M.adjustToxLoss(10, FALSE)
|
||||
else //apply debilitating effects
|
||||
if(prob(75))
|
||||
@@ -1262,7 +1263,7 @@
|
||||
to_chat(M, "<span class='warning'>Your body goes rigid, you cannot move at all!</span>")
|
||||
update_flags |= M.AdjustWeakened(15, FALSE)
|
||||
if(45 to INFINITY) // Start fixing bones | If they have stimulants or stimulant drugs in their system then the nanites won't work.
|
||||
if(M.reagents.has_reagent("methamphetamine") || M.reagents.has_reagent("crank") || M.reagents.has_reagent("bath_salts") || M.reagents.has_reagent("stimulative_agent") || M.reagents.has_reagent("stimulants"))
|
||||
if(has_stimulant == TRUE)
|
||||
return ..()
|
||||
else
|
||||
for(var/obj/item/organ/external/E in M.bodyparts)
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
/datum/reagent/space_cleaner/reaction_turf(turf/T, volume)
|
||||
if(volume >= 1)
|
||||
var/floor_only = TRUE
|
||||
for(var/obj/effect/decal/cleanable/C in src)
|
||||
for(var/obj/effect/decal/cleanable/C in T)
|
||||
var/obj/effect/decal/cleanable/blood/B = C
|
||||
if(istype(B) && B.off_floor)
|
||||
floor_only = FALSE
|
||||
|
||||
@@ -10,9 +10,7 @@ using metal and glass, it uses glass and reagents (usually sulfuric acis).
|
||||
icon_state = "circuit_imprinter"
|
||||
container_type = OPENCONTAINER
|
||||
|
||||
var/efficiency_coeff
|
||||
|
||||
var/list/categories = list(
|
||||
categories = list(
|
||||
"AI Modules",
|
||||
"Computer Boards",
|
||||
"Engineering Machinery",
|
||||
@@ -62,16 +60,17 @@ using metal and glass, it uses glass and reagents (usually sulfuric acis).
|
||||
var/T = 0
|
||||
for(var/obj/item/stock_parts/manipulator/M in component_parts)
|
||||
T += M.rating
|
||||
efficiency_coeff = 2 ** (T - 1) //Only 1 manipulator here, you're making runtimes Razharas
|
||||
T = clamp(T, 1, 4)
|
||||
efficiency_coeff = 1 / (2 ** (T - 1))
|
||||
|
||||
/obj/machinery/r_n_d/circuit_imprinter/proc/check_mat(datum/design/being_built, var/M)
|
||||
/obj/machinery/r_n_d/circuit_imprinter/check_mat(datum/design/being_built, var/M)
|
||||
var/list/all_materials = being_built.reagents_list + being_built.materials
|
||||
|
||||
var/A = materials.amount(M)
|
||||
if(!A)
|
||||
A = reagents.get_reagent_amount(M)
|
||||
|
||||
return round(A / max(1, (all_materials[M]/efficiency_coeff)))
|
||||
return round(A / max(1, (all_materials[M] * efficiency_coeff)))
|
||||
|
||||
/obj/machinery/r_n_d/circuit_imprinter/attackby(var/obj/item/O as obj, var/mob/user as mob, params)
|
||||
if(shocked)
|
||||
|
||||
@@ -13,9 +13,7 @@ Note: Must be placed west/left of and R&D console to function.
|
||||
icon_state = "protolathe"
|
||||
container_type = OPENCONTAINER
|
||||
|
||||
var/efficiency_coeff
|
||||
|
||||
var/list/categories = list(
|
||||
categories = list(
|
||||
"Bluespace",
|
||||
"Equipment",
|
||||
"Janitorial",
|
||||
@@ -70,7 +68,7 @@ Note: Must be placed west/left of and R&D console to function.
|
||||
T -= M.rating/10
|
||||
efficiency_coeff = min(max(0, T), 1)
|
||||
|
||||
/obj/machinery/r_n_d/protolathe/proc/check_mat(datum/design/being_built, var/M) // now returns how many times the item can be built with the material
|
||||
/obj/machinery/r_n_d/protolathe/check_mat(datum/design/being_built, var/M) // now returns how many times the item can be built with the material
|
||||
var/A = materials.amount(M)
|
||||
if(!A)
|
||||
A = reagents.get_reagent_amount(M)
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
var/obj/machinery/computer/rdconsole/linked_console
|
||||
var/obj/item/loaded_item = null
|
||||
var/datum/component/material_container/materials //Store for hyper speed!
|
||||
var/efficiency_coeff = 1
|
||||
var/list/categories = list()
|
||||
|
||||
/obj/machinery/r_n_d/New()
|
||||
materials = AddComponent(/datum/component/material_container, list(MAT_METAL, MAT_GLASS, MAT_SILVER, MAT_GOLD, MAT_DIAMOND, MAT_PLASMA, MAT_URANIUM, MAT_BANANIUM, MAT_TRANQUILLITE, MAT_TITANIUM, MAT_BLUESPACE, MAT_PLASTIC), 0, TRUE, /obj/item/stack, CALLBACK(src, .proc/is_insertion_ready), CALLBACK(src, .proc/AfterMaterialInsert))
|
||||
@@ -129,3 +131,6 @@
|
||||
overlays += "[initial(name)]_[stack_name]"
|
||||
sleep(10)
|
||||
overlays -= "[initial(name)]_[stack_name]"
|
||||
|
||||
/obj/machinery/r_n_d/proc/check_mat(datum/design/being_built, var/M)
|
||||
return 0 // number of copies of design beign_built you can make with material M
|
||||
|
||||
@@ -100,8 +100,13 @@ research holder datum.
|
||||
return
|
||||
known_tech[T.id] = T
|
||||
|
||||
/datum/research/proc/CanAddDesign2Known(var/datum/design/D)
|
||||
if (D.id in known_designs)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/research/proc/AddDesign2Known(var/datum/design/D)
|
||||
if(D.id in known_designs)
|
||||
if(!CanAddDesign2Known(D))
|
||||
return
|
||||
// Global datums make me nervous
|
||||
known_designs[D.id] = D
|
||||
@@ -163,10 +168,16 @@ research holder datum.
|
||||
/datum/research/autolathe/DesignHasReqs(var/datum/design/D)
|
||||
return D && (D.build_type & AUTOLATHE) && ("initial" in D.category)
|
||||
|
||||
/datum/research/autolathe/AddDesign2Known(var/datum/design/D)
|
||||
if(!(D.build_type & AUTOLATHE))
|
||||
return
|
||||
..()
|
||||
/datum/research/autolathe/CanAddDesign2Known(var/datum/design/design)
|
||||
// Specifically excludes circuit imprinter and mechfab
|
||||
if(design.locked || !(design.build_type & (AUTOLATHE|PROTOLATHE|CRAFTLATHE)))
|
||||
return FALSE
|
||||
|
||||
for(var/mat in design.materials)
|
||||
if(mat != MAT_METAL && mat != MAT_GLASS)
|
||||
return FALSE
|
||||
|
||||
return ..()
|
||||
|
||||
//Biogenerator files
|
||||
/datum/research/biogenerator/New()
|
||||
@@ -178,10 +189,10 @@ research holder datum.
|
||||
if((D.build_type & BIOGENERATOR) && ("initial" in D.category))
|
||||
AddDesign2Known(D)
|
||||
|
||||
/datum/research/biogenerator/AddDesign2Known(datum/design/D)
|
||||
/datum/research/biogenerator/CanAddDesign2Known(datum/design/D)
|
||||
if(!(D.build_type & BIOGENERATOR))
|
||||
return
|
||||
..()
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
//Smelter files
|
||||
/datum/research/smelter/New()
|
||||
@@ -193,10 +204,10 @@ research holder datum.
|
||||
if((D.build_type & SMELTER) && ("initial" in D.category))
|
||||
AddDesign2Known(D)
|
||||
|
||||
/datum/research/smelter/AddDesign2Known(datum/design/D)
|
||||
/datum/research/smelter/CanAddDesign2Known(datum/design/D)
|
||||
if(!(D.build_type & SMELTER))
|
||||
return
|
||||
..()
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/***************************************************************
|
||||
** Technology Datums **
|
||||
|
||||
@@ -0,0 +1,455 @@
|
||||
//Station goal stuff goes here
|
||||
/datum/station_goal/bluespace_tap
|
||||
name = "Bluespace Harvester"
|
||||
var/goal = 45000
|
||||
|
||||
/datum/station_goal/bluespace_tap/get_report()
|
||||
return {"<b>Bluespace Harvester Experiment</b><br>
|
||||
Another research station has developed a device called a Bluespace Harvester.
|
||||
It reaches through bluespace into other dimensions to shift through them for interesting objects.<br>
|
||||
Due to unforseen circumstances the large-scale test of the prototype could not be completed on the original research station. It will instead be carried out on your station.
|
||||
Acquire the circuit board, construct the device over a wire knot and feed it enough power to generate [goal] mining points by shift end.
|
||||
<br><br>
|
||||
Be advised that the device is experimental and might act in slightly unforseen ways if sufficiently powered.
|
||||
<br>
|
||||
Nanotrasen Science Directorate"}
|
||||
|
||||
/datum/station_goal/bluespace_tap/on_report()
|
||||
var/datum/supply_packs/misc/station_goal/bluespace_tap/P = SSshuttle.supply_packs["[/datum/supply_packs/misc/station_goal/bluespace_tap]"]
|
||||
P.special_enabled = TRUE
|
||||
|
||||
/datum/station_goal/bluespace_tap/check_completion()
|
||||
if(..())
|
||||
return TRUE
|
||||
var/highscore = 0
|
||||
for(var/obj/machinery/power/bluespace_tap/T in GLOB.machines)
|
||||
highscore = max(highscore, T.total_points)
|
||||
to_chat(world, "<b>Bluespace Harvester Highscore</b> : [highscore >= goal ? "<span class='greenannounce'>": "<span class='boldannounce'>"][highscore]</span>")
|
||||
if(highscore >= goal)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
//needed for the vending part of it
|
||||
/datum/data/bluespace_tap_product
|
||||
var/product_name = "generic"
|
||||
var/product_path = null
|
||||
var/product_cost = 100 //cost in mining points to generate
|
||||
|
||||
|
||||
/datum/data/bluespace_tap_product/New(name, path, cost)
|
||||
product_name = name
|
||||
product_path = path
|
||||
product_cost = cost
|
||||
|
||||
/obj/item/circuitboard/machine/bluespace_tap
|
||||
name = "Bluespace Harvester (Machine Board)"
|
||||
build_path = /obj/machinery/power/bluespace_tap
|
||||
origin_tech = "engineering=2;combat=2;bluespace=3"
|
||||
req_components = list(
|
||||
/obj/item/stock_parts/capacitor/quadratic = 5,//Probably okay, right?
|
||||
/obj/item/stack/ore/bluespace_crystal = 5)
|
||||
|
||||
/obj/effect/spawner/lootdrop/bluespace_tap
|
||||
name = "bluespace harvester reward spawner"
|
||||
lootcount = 1
|
||||
|
||||
/obj/effect/spawner/lootdrop/bluespace_tap/hat
|
||||
name = "exotic hat"
|
||||
loot = list(
|
||||
/obj/item/clothing/head/collectable/chef, //same weighing on all of them
|
||||
/obj/item/clothing/head/collectable/paper,
|
||||
/obj/item/clothing/head/collectable/tophat,
|
||||
/obj/item/clothing/head/collectable/captain,
|
||||
/obj/item/clothing/head/collectable/beret,
|
||||
/obj/item/clothing/head/collectable/welding,
|
||||
/obj/item/clothing/head/collectable/flatcap,
|
||||
/obj/item/clothing/head/collectable/pirate,
|
||||
/obj/item/clothing/head/collectable/kitty,
|
||||
/obj/item/clothing/head/crown/fancy,
|
||||
/obj/item/clothing/head/collectable/rabbitears,
|
||||
/obj/item/clothing/head/collectable/wizard,
|
||||
/obj/item/clothing/head/collectable/hardhat,
|
||||
/obj/item/clothing/head/collectable/HoS,
|
||||
/obj/item/clothing/head/collectable/thunderdome,
|
||||
/obj/item/clothing/head/collectable/swat,
|
||||
/obj/item/clothing/head/collectable/slime,
|
||||
/obj/item/clothing/head/collectable/police,
|
||||
/obj/item/clothing/head/collectable/slime,
|
||||
/obj/item/clothing/head/collectable/xenom,
|
||||
/obj/item/clothing/head/collectable/petehat
|
||||
)
|
||||
|
||||
|
||||
/obj/effect/spawner/lootdrop/bluespace_tap/cultural
|
||||
name = "cultural artifacts"
|
||||
loot = list(
|
||||
/obj/vehicle/space/speedbike/red = 10,
|
||||
/obj/item/grenade/clusterbuster/honk = 10,
|
||||
/obj/item/toy/katana = 10,
|
||||
/obj/item/stack/tile/brass/fifty = 20,
|
||||
/obj/item/stack/sheet/mineral/abductor/fifty = 20,
|
||||
/obj/item/sord = 20,
|
||||
/obj/item/toy/syndicateballoon = 15,
|
||||
/obj/item/lighter/zippo/gonzofist = 5,
|
||||
/obj/item/lighter/zippo/engraved = 5,
|
||||
/obj/item/lighter/zippo/nt_rep = 5,
|
||||
/obj/item/gun/projectile/automatic/c20r/toy = 1,
|
||||
/obj/item/gun/projectile/automatic/l6_saw/toy = 1,
|
||||
/obj/item/gun/projectile/automatic/toy/pistol = 2,
|
||||
/obj/item/gun/projectile/automatic/toy/pistol/enforcer = 1,
|
||||
/obj/item/gun/projectile/shotgun/toy = 1,
|
||||
/obj/item/gun/projectile/shotgun/toy/crossbow = 1,
|
||||
/obj/item/gun/projectile/shotgun/toy/tommygun = 1,
|
||||
/obj/item/gun/projectile/automatic/sniper_rifle/toy = 1,
|
||||
/obj/item/twohanded/dualsaber/toy = 5,
|
||||
/obj/machinery/snow_machine = 10,
|
||||
/obj/item/clothing/head/kitty = 5,
|
||||
/obj/item/coin/antagtoken = 5,
|
||||
/obj/item/toy/prizeball/figure = 15,
|
||||
/obj/item/toy/prizeball/therapy = 10,
|
||||
/obj/item/bedsheet/patriot = 2,
|
||||
/obj/item/bedsheet/rainbow = 2,
|
||||
/obj/item/bedsheet/captain = 2,
|
||||
/obj/item/bedsheet/centcom = 1, //mythic rare rarity
|
||||
/obj/item/bedsheet/syndie = 2,
|
||||
/obj/item/bedsheet/cult = 2,
|
||||
/obj/item/bedsheet/wiz = 2,
|
||||
/obj/item/stack/sheet/mineral/tranquillite/fifty = 3,
|
||||
/obj/item/clothing/gloves/combat = 5
|
||||
)
|
||||
|
||||
/obj/effect/spawner/lootdrop/bluespace_tap/organic
|
||||
name = "organic objects"
|
||||
loot = list(
|
||||
/obj/item/seeds/random/labelled = 50,
|
||||
/obj/item/guardiancreator/biological = 5,
|
||||
/obj/item/organ/internal/vocal_cords/adamantine = 15,
|
||||
/obj/item/storage/pill_bottle/random_meds/labelled = 25,
|
||||
/obj/item/reagent_containers/glass/bottle/reagent/omnizine = 15,
|
||||
/obj/item/dnainjector/telemut = 5,
|
||||
/obj/item/dnainjector/midgit = 5,
|
||||
/obj/item/dnainjector/morph = 5,
|
||||
/obj/item/dnainjector/regenerate = 5,
|
||||
/mob/living/simple_animal/pet/dog/corgi/ = 5,
|
||||
/mob/living/simple_animal/pet/cat = 5,
|
||||
/mob/living/simple_animal/pet/dog/fox/ = 5,
|
||||
/mob/living/simple_animal/pet/penguin = 5,
|
||||
/mob/living/simple_animal/pig = 5,
|
||||
/obj/item/slimepotion/sentience = 5,
|
||||
/obj/item/clothing/mask/cigarette/cigar/havana = 3,
|
||||
/obj/item/stack/sheet/mineral/bananium/fifty = 2, //bananas are organic, clearly.
|
||||
/obj/item/storage/box/monkeycubes = 5,
|
||||
/obj/item/stack/tile/carpet/twenty = 10,
|
||||
/obj/item/stack/tile/carpet/black/twenty = 10,
|
||||
/obj/item/soap/deluxe = 5
|
||||
)
|
||||
|
||||
/obj/effect/spawner/lootdrop/bluespace_tap/food
|
||||
name = "fancy food"
|
||||
lootcount = 3
|
||||
loot = list(
|
||||
/obj/item/reagent_containers/food/snacks/wingfangchu,
|
||||
/obj/item/reagent_containers/food/snacks/hotdog,
|
||||
/obj/item/reagent_containers/food/snacks/sliceable/turkey,
|
||||
/obj/item/reagent_containers/food/snacks/plumphelmetbiscuit,
|
||||
/obj/item/reagent_containers/food/snacks/appletart,
|
||||
/obj/item/reagent_containers/food/snacks/sliceable/cheesecake,
|
||||
/obj/item/reagent_containers/food/snacks/sliceable/bananacake,
|
||||
/obj/item/reagent_containers/food/snacks/sliceable/chocolatecake,
|
||||
/obj/item/reagent_containers/food/snacks/meatballsoup,
|
||||
/obj/item/reagent_containers/food/snacks/mysterysoup,
|
||||
/obj/item/reagent_containers/food/snacks/stew,
|
||||
/obj/item/reagent_containers/food/snacks/hotchili,
|
||||
/obj/item/reagent_containers/food/snacks/burrito,
|
||||
/obj/item/reagent_containers/food/snacks/fishburger,
|
||||
/obj/item/reagent_containers/food/snacks/cubancarp,
|
||||
/obj/item/reagent_containers/food/snacks/fishandchips,
|
||||
/obj/item/reagent_containers/food/snacks/meatpie,
|
||||
/obj/item/pizzabox/hawaiian, //it ONLY gives hawaiian. MUHAHAHA
|
||||
/obj/item/reagent_containers/food/snacks/sliceable/xenomeatbread //maybe add some dangerous/special food here, ie robobuger?
|
||||
)
|
||||
|
||||
#define kW *1000
|
||||
#define MW kW *1000
|
||||
#define GW MW *1000
|
||||
|
||||
/**
|
||||
* # Bluespace Harvester
|
||||
*
|
||||
* A station goal that consumes enormous amounts of power to generate (mostly fluff) rewards
|
||||
*
|
||||
* A machine that takes power each tick, generates points based on it
|
||||
* and lets you spend those points on rewards. A certain amount of points
|
||||
* has to be generated for the station goal to count as completed.
|
||||
*/
|
||||
/obj/machinery/power/bluespace_tap
|
||||
name = "Bluespace harvester"
|
||||
icon = 'icons/obj/machines/bluespace_tap.dmi'
|
||||
icon_state = "bluespace_tap" //sprites by Ionward
|
||||
max_integrity = 300
|
||||
pixel_x = -32 //shamelessly stolen from dna vault
|
||||
pixel_y = -64
|
||||
/// For faking having a big machine, dummy 'machines' that are hidden inside the large sprite and make certain tiles dense. See new and destroy.
|
||||
var/list/obj/structure/fillers = list()
|
||||
use_power = NO_POWER_USE // power usage is handelled manually
|
||||
density = TRUE
|
||||
interact_offline = TRUE
|
||||
luminosity = 1
|
||||
|
||||
/// Correspond to power required for a mining level, first entry for level 1, etc.
|
||||
var/list/power_needs = list(1 kW, 5 kW, 50 kW, 100 kW, 500 kW,
|
||||
1 MW, 2 MW, 5 MW, 10 MW, 25 MW,
|
||||
50 MW, 75 MW, 125 MW, 200 MW, 500 MW,
|
||||
1 GW, 5 GW, 15 GW, 45 GW, 500 GW)
|
||||
|
||||
/// list of possible products
|
||||
var/static/product_list = list(
|
||||
new /datum/data/bluespace_tap_product("Unknown Exotic Hat", /obj/effect/spawner/lootdrop/bluespace_tap/hat, 5000),
|
||||
new /datum/data/bluespace_tap_product("Unknown Snack", /obj/effect/spawner/lootdrop/bluespace_tap/food, 6000),
|
||||
new /datum/data/bluespace_tap_product("Unknown Cultural Artifact", /obj/effect/spawner/lootdrop/bluespace_tap/cultural, 15000),
|
||||
new /datum/data/bluespace_tap_product("Unknown Biological Artifact", /obj/effect/spawner/lootdrop/bluespace_tap/organic, 20000)
|
||||
)
|
||||
|
||||
/// The level the machine is currently mining at. 0 means off
|
||||
var/input_level = 0
|
||||
/// The machine you WANT the machine to mine at. It will try to match this.
|
||||
var/desired_level = 0
|
||||
/// Available mining points
|
||||
var/points = 0
|
||||
/// The total points earned by this machine so far, for tracking station goal and highscore
|
||||
var/total_points = 0
|
||||
/// How much power the machine needs per processing tick at the current level.
|
||||
var/actual_power_usage = 0
|
||||
|
||||
|
||||
// Tweak these and active_power_usage to balance power generation
|
||||
|
||||
/// Max power input level, I don't expect this to be ever reached
|
||||
var/max_level = 20
|
||||
/// Amount of points to give per mining level
|
||||
var/base_points = 4
|
||||
/// How high the machine can be run before it starts having a chance for dimension breaches.
|
||||
var/safe_levels = 10
|
||||
|
||||
|
||||
/obj/machinery/power/bluespace_tap/New()
|
||||
..()
|
||||
//more code stolen from dna vault, inculding comment below. Taking bets on that datum being made ever.
|
||||
//TODO: Replace this,bsa and gravgen with some big machinery datum
|
||||
var/list/occupied = list()
|
||||
for(var/direct in list(EAST, WEST, SOUTHEAST, SOUTHWEST))
|
||||
occupied += get_step(src, direct)
|
||||
occupied += locate(x + 1, y - 2, z)
|
||||
occupied += locate(x - 1, y - 2, z)
|
||||
|
||||
for(var/T in occupied)
|
||||
var/obj/structure/filler/F = new(T)
|
||||
F.parent = src
|
||||
fillers += F
|
||||
component_parts = list()
|
||||
component_parts += new /obj/item/circuitboard/machine/bluespace_tap(null)
|
||||
for(var/i = 1 to 5) //five of each
|
||||
component_parts += new /obj/item/stock_parts/capacitor/quadratic(null)
|
||||
component_parts += new /obj/item/stack/ore/bluespace_crystal(null)
|
||||
if(!powernet)
|
||||
connect_to_network()
|
||||
|
||||
/obj/machinery/power/bluespace_tap/Destroy()
|
||||
QDEL_LIST(fillers)
|
||||
return ..()
|
||||
|
||||
/**
|
||||
* Increases the desired mining level
|
||||
*
|
||||
* Increases the desired mining level, that
|
||||
* the machine tries to reach if there
|
||||
* is enough power for it. Note that it does
|
||||
* NOT increase the actual mining level directly.
|
||||
*/
|
||||
/obj/machinery/power/bluespace_tap/proc/increase_level()
|
||||
if(desired_level < max_level)
|
||||
desired_level++
|
||||
/**
|
||||
* Decreases the desired mining level
|
||||
*
|
||||
* Decreases the desired mining level, that
|
||||
* the machine tries to reach if there
|
||||
* is enough power for it. Note that it does
|
||||
* NOT decrease the actual mining level directly.
|
||||
*/
|
||||
/obj/machinery/power/bluespace_tap/proc/decrease_level()
|
||||
if(desired_level > 0)
|
||||
desired_level--
|
||||
|
||||
/**
|
||||
* Sets the desired mining level
|
||||
*
|
||||
* Sets the desired mining level, that
|
||||
* the machine tries to reach if there
|
||||
* is enough power for it. Note that it does
|
||||
* NOT change the actual mining level directly.
|
||||
* Arguments:
|
||||
* * t_level - The level we try to set it at, between 0 and max_level
|
||||
*/
|
||||
/obj/machinery/power/bluespace_tap/proc/set_level(t_level)
|
||||
if(t_level < 0)
|
||||
return
|
||||
if(t_level > max_level)
|
||||
return
|
||||
desired_level = t_level
|
||||
|
||||
/**
|
||||
* Gets the amount of power at a set input level
|
||||
*
|
||||
* Gets the amount of power (in W) a set input level needs.
|
||||
* Note that this is not necessarily the current power use.
|
||||
* * i_level - The hypothetical input level for which we want to know the power use.
|
||||
*/
|
||||
/obj/machinery/power/bluespace_tap/proc/get_power_use(i_level)
|
||||
if(!i_level)
|
||||
return 0
|
||||
return power_needs[i_level]
|
||||
|
||||
/obj/machinery/power/bluespace_tap/process()
|
||||
actual_power_usage = get_power_use(input_level)
|
||||
if(surplus() < actual_power_usage) //not enough power, so turn down a level
|
||||
input_level--
|
||||
return // and no mining gets done
|
||||
if(actual_power_usage)
|
||||
add_load(actual_power_usage)
|
||||
var/points_to_add = (input_level + emagged) * base_points
|
||||
points += points_to_add //point generation, emagging gets you 'free' points at the cost of higher anomaly chance
|
||||
total_points += points_to_add
|
||||
// actual input level changes slowly
|
||||
if(input_level < desired_level && (surplus() >= get_power_use(input_level + 1)))
|
||||
input_level++
|
||||
else if(input_level > desired_level)
|
||||
input_level--
|
||||
if(prob(input_level - safe_levels + (emagged * 5))) //at dangerous levels, start doing freaky shit. prob with values less than 0 treat it as 0
|
||||
GLOB.event_announcement.Announce("Unexpected power spike during Bluespace Harvester Operation. Extra-dimensional intruder alert. Expected location: [get_area(src).name]. [emagged ? "DANGER: Emergency shutdown failed! Please proceed with manual shutdown." : "Emergency shutdown initiated."]", "Bluespace Harvester Malfunction")
|
||||
if(!emagged)
|
||||
input_level = 0 //emergency shutdown unless we're sabotaged
|
||||
desired_level = 0
|
||||
for(var/i in 1 to rand(1, 3))
|
||||
var/turf/location = locate(x + rand(-5, 5), y + rand(-5, 5), z)
|
||||
new /obj/structure/spawner/nether/bluespace_tap(location)
|
||||
|
||||
|
||||
|
||||
/obj/machinery/power/bluespace_tap/tgui_data(mob/user)
|
||||
var/data[0]
|
||||
|
||||
data["desiredLevel"] = desired_level
|
||||
data["inputLevel"] = input_level
|
||||
data["points"] = points
|
||||
data["totalPoints"] = total_points
|
||||
data["powerUse"] = actual_power_usage
|
||||
data["availablePower"] = surplus()
|
||||
data["maxLevel"] = max_level
|
||||
data["emagged"] = emagged
|
||||
data["safeLevels"] = safe_levels
|
||||
data["nextLevelPower"] = get_power_use(input_level + 1)
|
||||
|
||||
/// A list of lists, each inner list equals a datum
|
||||
var/list/listed_items = list()
|
||||
for(var/key = 1 to length(product_list))
|
||||
var/datum/data/bluespace_tap_product/A = product_list[key]
|
||||
listed_items[++listed_items.len] = list(
|
||||
"key" = key,
|
||||
"name" = A.product_name,
|
||||
"price" = A.product_cost)
|
||||
data["product"] = listed_items
|
||||
return data
|
||||
|
||||
|
||||
/obj/machinery/power/bluespace_tap/attack_hand(mob/user)
|
||||
add_fingerprint(user)
|
||||
tgui_interact(user)
|
||||
|
||||
/obj/machinery/power/bluespace_tap/attack_ghost(mob/user)
|
||||
tgui_interact(user)
|
||||
|
||||
/obj/machinery/power/bluespace_tap/attack_ai(mob/user)
|
||||
tgui_interact(user)
|
||||
|
||||
/**
|
||||
* Produces the product with the desired key and increases product cost accordingly
|
||||
*/
|
||||
/obj/machinery/power/bluespace_tap/proc/produce(key)
|
||||
if(key <= 0 || key > length(product_list)) //invalid key
|
||||
return
|
||||
var/datum/data/bluespace_tap_product/A = product_list[key]
|
||||
if(!A)
|
||||
return
|
||||
if(A.product_cost > points)
|
||||
return
|
||||
points -= A.product_cost
|
||||
A.product_cost = round(1.2 * A.product_cost, 1)
|
||||
playsound(src, 'sound/magic/blink.ogg', 50)
|
||||
do_sparks(2, FALSE, src)
|
||||
new A.product_path(get_turf(src))
|
||||
|
||||
|
||||
|
||||
//UI stuff below
|
||||
|
||||
/obj/machinery/power/bluespace_tap/tgui_act(action, params)
|
||||
if(..())
|
||||
return
|
||||
. = TRUE // we want to refresh in all the cases below
|
||||
switch(action)
|
||||
if("decrease")
|
||||
decrease_level()
|
||||
if("increase")
|
||||
increase_level()
|
||||
if("set")
|
||||
set_level(text2num(params["set_level"]))
|
||||
if("vend")//it's not really vending as producing, but eh
|
||||
var/key = text2num(params["target"])
|
||||
produce(key)
|
||||
|
||||
/obj/machinery/power/bluespace_tap/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "BluespaceTap", name, 650, 400, master_ui, state)
|
||||
ui.open()
|
||||
|
||||
//emaging provides slightly more points but at much greater risk
|
||||
/obj/machinery/power/bluespace_tap/emag_act(mob/living/user as mob)
|
||||
if(emagged)
|
||||
return
|
||||
emagged = TRUE
|
||||
do_sparks(5, FALSE, src)
|
||||
if(user)
|
||||
user.visible_message("<span class='warning'>[user] overrides the safety protocols of [src].</span>", "<span class='warning'>You override the safety protocols.</span>")
|
||||
|
||||
/obj/structure/spawner/nether/bluespace_tap
|
||||
spawn_time = 30 SECONDS
|
||||
max_mobs = 5 //Dont' want them overrunning the station
|
||||
max_integrity = 250
|
||||
|
||||
/obj/structure/spawner/nether/bluespace_tap/deconstruct(disassembled)
|
||||
new /obj/item/stack/ore/bluespace_crystal(loc) //have a reward
|
||||
return ..()
|
||||
|
||||
/obj/item/paper/bluespace_tap
|
||||
name = "paper- 'The Experimental NT Bluespace Harvester - Mining other universes for science and profit!'"
|
||||
info = "<h1>Important Instructions!</h1>Please follow all setup instructions to ensure proper operation. <br>\
|
||||
1. Create a wire node with ample access to spare power. The device operates independently of APCs. <br>\
|
||||
2. Create a machine frame as normal on the wire node, taking into account the device's dimensions (3 by 3 meters). <br>\
|
||||
3. Insert wiring, circuit board and required components and finish construction according to NT engineering standards. <br>\
|
||||
4. Ensure the device is connected to the proper power network and the network contains sufficient power. <br>\
|
||||
5. Set machine to desired level. Check periodically on machine progress. <br>\
|
||||
6. Optionally, spend earned points on fun and exciting rewards. <br><hr>\
|
||||
<h2>Operational Principles</h2> \
|
||||
<p>The Bluespace Harvester is capable of accepting a nearly limitless amount of power to search other universes for valuables to recover. The speed of this search is controlled via the 'level' control of the device. \
|
||||
While it can be run on a low level by almost any power generation system, higher levels require work by a dedicated engineering team to power. \
|
||||
As we are interested in testing how the device performs under stress, we wish to encourage you to stress-test it and see how much power you can provide it. \
|
||||
For this reason, total shift point production will be calculated and announced at shift end. High totals may result in bonus payments to members of the Engineering department. <p>\
|
||||
<p>NT Science Directorate, Extradimensional Exploitation Research Group</p> \
|
||||
<p><small>Device highly experimental. Not for sale. Do not operate near small children or vital NT assets. Do not tamper with machine. In case of existential dread, stop machine immediately. \
|
||||
Please document any and all extradimensional incursions. In case of imminent death, please leave said documentation in plain sight for clean-up teams to recover.</small></p>"
|
||||
|
||||
#undef kW
|
||||
#undef MW
|
||||
#undef GW
|
||||
@@ -136,6 +136,10 @@ GLOBAL_LIST_INIT(non_simple_animals, typecacheof(list(/mob/living/carbon/human/m
|
||||
invisibility = 101
|
||||
var/obj/machinery/parent
|
||||
|
||||
/obj/structure/filler/Destroy()
|
||||
parent = null
|
||||
return ..()
|
||||
|
||||
/obj/structure/filler/ex_act()
|
||||
return
|
||||
|
||||
@@ -187,12 +191,8 @@ GLOBAL_LIST_INIT(non_simple_animals, typecacheof(list(/mob/living/carbon/human/m
|
||||
..()
|
||||
|
||||
/obj/machinery/dna_vault/Destroy()
|
||||
for(var/V in fillers)
|
||||
var/obj/structure/filler/filler = V
|
||||
filler.parent = null
|
||||
qdel(filler)
|
||||
fillers.Cut()
|
||||
. = ..()
|
||||
QDEL_LIST(fillers)
|
||||
return ..()
|
||||
|
||||
/obj/machinery/dna_vault/attack_ghost(mob/user)
|
||||
if(stat & (BROKEN|MAINT))
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/datum/tgui_module/atmos_control
|
||||
name = "Atmospherics Control"
|
||||
|
||||
/datum/tgui_module/atmos_control/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
|
||||
if(..())
|
||||
return
|
||||
|
||||
switch(action)
|
||||
if("open_alarm")
|
||||
var/obj/machinery/alarm/alarm = locate(params["aref"]) in GLOB.air_alarms
|
||||
if(alarm)
|
||||
alarm.tgui_interact(usr, master_ui = ui, state = GLOB.tgui_always_state) // ALWAYS is intentional here, as the master_ui pass will prevent fuckery
|
||||
|
||||
/datum/tgui_module/atmos_control/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "AtmosControl", name, 800, 600, master_ui, state)
|
||||
|
||||
// Send nanomaps
|
||||
var/datum/asset/nanomaps = get_asset_datum(/datum/asset/simple/nanomaps)
|
||||
nanomaps.send(user)
|
||||
|
||||
ui.open()
|
||||
|
||||
/datum/tgui_module/atmos_control/tgui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["alarms"] = GLOB.air_alarm_repository.air_alarm_data(GLOB.air_alarms)
|
||||
|
||||
return data
|
||||
@@ -1,6 +1,6 @@
|
||||
#Listing maps here will blacklist them from generating in space.
|
||||
#Maps must be the full path to them
|
||||
#A list of maps valid to blacklist can be found at _maps\map_files\RandomRuins\SpaceRuins\_maplisting.txt
|
||||
#Maps valid to be blacklisted can be found in _maps/map_files/RandomRuins/SpaceRuins
|
||||
#SPECIFYING AN INVALID MAP WILL RESULT IN RUNTIMES ON GAME START
|
||||
|
||||
#_maps/map_files/RandomRuins/SpaceRuins/way_home.dmm
|
||||
@@ -39,3 +39,7 @@
|
||||
# Its also important incase a white-ship console is ever built midround
|
||||
# DO NOT DISABLE THIS UNLESS YOU HAVE A GOOD REASON
|
||||
#_maps/map_files/RandomRuins/SpaceRuins/whiteship.dmm
|
||||
|
||||
# The following is a force-spawned ruin consisting mostly of empty space with a shuttle docking port for the free golem shuttle
|
||||
# Disabling it will lead to the free golem shuttle sometimes being stuck on lavaland.
|
||||
#_maps/map_files/RandomRuins/SpaceRuins/golemtarget.dmm
|
||||
|
||||
@@ -159,6 +159,11 @@ var/list/chatResources = list(
|
||||
return
|
||||
|
||||
if(cookie != "none")
|
||||
var/regex/crashy_thingy = new /regex("(\\\[ *){5}")
|
||||
if(crashy_thingy.Find(cookie))
|
||||
message_admins("[key_name(src.owner)] tried to crash the server using malformed JSON")
|
||||
log_admin("[key_name(owner)] tried to crash the server using malformed JSON")
|
||||
return
|
||||
var/list/connData = json_decode(cookie)
|
||||
if(connData && islist(connData) && connData.len > 0 && connData["connData"])
|
||||
connectionHistory = connData["connData"]
|
||||
|
||||
|
Before Width: | Height: | Size: 661 KiB After Width: | Height: | Size: 658 KiB |
|
Before Width: | Height: | Size: 121 KiB After Width: | Height: | Size: 121 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 9.6 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 12 KiB |