Merge remote-tracking branch 'upstream/master' into air-injector-link-fix

This commit is contained in:
SteelSlayer
2020-10-17 11:38:41 -05:00
210 changed files with 8872 additions and 10310 deletions
-50
View File
@@ -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
View File
@@ -1 +0,0 @@
g++ -static -shared -O3 -fPIC main.cpp -o DLLSocket.so
-133
View File
@@ -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;
}
-51
View File
@@ -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")
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -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"
+1 -1
View File
@@ -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"
},
+15 -17
View File
@@ -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(..())
+1 -1
View File
@@ -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()
+2 -2
View File
@@ -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()
..()
+2 -2
View File
@@ -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)
+2 -2
View File
@@ -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)
-4
View File
@@ -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
-2
View File
@@ -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))
-2
View File
@@ -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.")
+5
View File
@@ -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"
+8 -15
View File
@@ -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"
+4
View File
@@ -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)
+1 -1
View File
@@ -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>")
+31 -31
View File
@@ -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
+5 -5
View File
@@ -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
+2
View File
@@ -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)
+2 -2
View File
@@ -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
+3 -1
View File
@@ -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()
+10
View File
@@ -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"
+3 -3
View File
@@ -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
+178 -197
View File
@@ -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
+21 -8
View File
@@ -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
+14 -23
View File
@@ -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)
+7 -4
View File
@@ -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"
+328 -175
View File
@@ -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
View File
@@ -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"
+3 -3
View File
@@ -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")
+5 -1
View File
@@ -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
+1 -1
View File
@@ -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)
+3 -2
View File
@@ -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(
+12 -1
View File
@@ -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'
)
-537
View File
@@ -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;">&times;</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;">&times;</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 ..()
-55
View File
@@ -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
-1
View File
@@ -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,)
)
+25 -25
View File
@@ -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
+2
View File
@@ -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**********************/
+4 -4
View File
@@ -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)
+6 -6
View File
@@ -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)
+18 -108
View File
@@ -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
+22 -94
View File
@@ -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]&deg;C ([held.bodytemperature*1.8-459.67]&deg;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)
+2 -18
View File
@@ -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)
+1 -1
View File
@@ -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))
+35 -42
View File
@@ -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
+3 -3
View File
@@ -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
+12 -41
View File
@@ -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
+5 -6
View File
@@ -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)
+2 -4
View File
@@ -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)
File diff suppressed because it is too large Load Diff
+5
View File
@@ -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
+22 -11
View File
@@ -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 **
+455
View File
@@ -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
+6 -6
View File
@@ -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
+5 -1
View File
@@ -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
+5
View File
@@ -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"]
Binary file not shown.

Before

Width:  |  Height:  |  Size: 661 KiB

After

Width:  |  Height:  |  Size: 658 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 121 KiB

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Some files were not shown because too many files have changed in this diff Show More