Merge remote-tracking branch 'upstream/master' into borger-tweaks

This commit is contained in:
SteelSlayer
2020-08-18 11:09:48 -05:00
371 changed files with 4484 additions and 8606 deletions
-1
View File
@@ -2,7 +2,6 @@
[![Build Status](https://travis-ci.org/ParadiseSS13/Paradise.svg?branch=master)](https://travis-ci.org/ParadiseSS13/Paradise)
[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/paradisess13/paradise.svg)](http://isitmaintained.com/project/paradisess13/paradise "Average time to resolve an issue")
[![Percentage of issues still open](http://isitmaintained.com/badge/open/paradisess13/paradise.svg)](http://isitmaintained.com/project/paradisess13/paradise "Percentage of issues still open")
[![Krihelimeter](http://www.krihelinator.xyz/badge/paradisess13/paradise)](http://www.krihelinator.xyz)
![Render Nanomaps](https://github.com/ParadiseSS13/Paradise/workflows/Render%20Nanomaps/badge.svg)
[![forthebadge](http://forthebadge.com/images/badges/60-percent-of-the-time-works-every-time.svg)](http://forthebadge.com)
+1 -1
View File
@@ -17,7 +17,7 @@ z4 = lavaland
#define MAP_TRANSITION_CONFIG list(\
DECLARE_LEVEL(MAIN_STATION, CROSSLINKED, list(STATION_LEVEL, STATION_CONTACT, REACHABLE, AI_OK)),\
DECLARE_LEVEL(CENTCOMM, SELFLOOPING, list(ADMIN_LEVEL, BLOCK_TELEPORT, IMPEDES_MAGIC)),\
DECLARE_LEVEL(MINING, SELFLOOPING, list(REACHABLE, STATION_CONTACT, HAS_WEATHER, ORE_LEVEL, AI_OK)))
DECLARE_LEVEL(MINING, SELFLOOPING, list(ORE_LEVEL, REACHABLE, STATION_CONTACT, HAS_WEATHER, AI_OK)))
#define USING_MAP_DATUM /datum/map/cyberiad
#elif !defined(MAP_OVERRIDE)
+1 -1
View File
@@ -22,7 +22,7 @@ Lovingly ported by Purpose2 to Paradise
#define MAP_TRANSITION_CONFIG list(\
DECLARE_LEVEL(MAIN_STATION, CROSSLINKED, list(STATION_LEVEL, STATION_CONTACT, REACHABLE, AI_OK)),\
DECLARE_LEVEL(CENTCOMM, SELFLOOPING, list(ADMIN_LEVEL, BLOCK_TELEPORT, IMPEDES_MAGIC)),\
DECLARE_LEVEL(MINING, SELFLOOPING, list(REACHABLE, STATION_CONTACT, HAS_WEATHER, ORE_LEVEL, AI_OK)))
DECLARE_LEVEL(MINING, SELFLOOPING, list(ORE_LEVEL, REACHABLE, STATION_CONTACT, HAS_WEATHER, AI_OK)))
#define USING_MAP_DATUM /datum/map/delta
@@ -1205,9 +1205,9 @@
/turf/simulated/floor/plating/asteroid/basalt/lava_land_surface,
/area/lavaland/surface/outdoors)
"dg" = (
/obj/structure/bonfire/dense,
/obj/structure/stone_tile/center,
/obj/effect/mapping_helpers/no_lava,
/obj/structure/bonfire,
/turf/simulated/floor/plating/asteroid/basalt/lava_land_surface,
/area/lavaland/surface/outdoors)
"di" = (
@@ -1,7 +1,7 @@
//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
"a" = (
/turf/space,
/area/space)
/turf/template_noop,
/area/template_noop)
"b" = (
/turf/simulated/floor/plating/asteroid/airless,
/area/ruin/unpowered)
@@ -1,7 +1,7 @@
//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
"a" = (
/turf/space,
/area/space)
/turf/template_noop,
/area/template_noop)
"b" = (
/turf/simulated/floor/plating/asteroid/airless,
/area/ruin/unpowered)
@@ -1,7 +1,7 @@
//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
"a" = (
/turf/space,
/area/space)
/turf/template_noop,
/area/template_noop)
"b" = (
/turf/simulated/floor/plating/asteroid/airless,
/area/ruin/unpowered)
@@ -1,7 +1,7 @@
//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
"a" = (
/turf/space,
/area/space)
/turf/template_noop,
/area/template_noop)
"b" = (
/turf/simulated/mineral,
/area/ruin/unpowered)
@@ -1,7 +1,7 @@
//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
"a" = (
/turf/space,
/area/space)
/turf/template_noop,
/area/template_noop)
"b" = (
/turf/simulated/mineral,
/area/ruin/unpowered)
@@ -1,7 +1,7 @@
//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
"aa" = (
/turf/space,
/area/space)
/turf/template_noop,
/area/template_noop)
"ab" = (
/turf/simulated/mineral/random/low_chance,
/area/ruin/unpowered)
@@ -1,11 +1,11 @@
//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
"a" = (
/turf/space,
/area/space)
/turf/template_noop,
/area/template_noop)
"b" = (
/obj/structure/lattice,
/turf/space,
/area/space)
/turf/template_noop,
/area/template_noop)
"c" = (
/turf/simulated/wall,
/area/ruin/unpowered)
@@ -1,14 +1,14 @@
//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
"a" = (
/turf/space,
/area/space)
/turf/template_noop,
/area/template_noop)
"b" = (
/obj/structure/window/reinforced{
tag = "icon-rwindow (EAST)";
icon_state = "rwindow";
dir = 4
},
/turf/space,
/turf/template_noop,
/area/space/nearstation)
"c" = (
/obj/machinery/door/airlock/external,
@@ -20,7 +20,7 @@
icon_state = "rwindow";
dir = 8
},
/turf/space,
/turf/template_noop,
/area/space/nearstation)
"e" = (
/obj/structure/window/reinforced{
@@ -64,7 +64,7 @@
/area/ruin/powered)
"k" = (
/obj/structure/window/reinforced,
/turf/space,
/turf/template_noop,
/area/space/nearstation)
"l" = (
/obj/structure/window/reinforced,
@@ -112,7 +112,7 @@
icon_state = "rwindow";
dir = 1
},
/turf/space,
/turf/template_noop,
/area/space/nearstation)
"q" = (
/obj/machinery/light/small{
@@ -1,7 +1,7 @@
//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
"a" = (
/turf/space,
/area/space)
/turf/template_noop,
/area/template_noop)
"b" = (
/turf/simulated/floor/plating/airless,
/area/ruin/unpowered)
@@ -10,12 +10,12 @@
/area/ruin/unpowered)
"d" = (
/obj/structure/lattice,
/turf/space,
/turf/template_noop,
/area/space/nearstation)
"e" = (
/obj/structure/lattice,
/obj/structure/lattice,
/turf/space,
/turf/template_noop,
/area/space/nearstation)
(1,1,1) = {"
@@ -1,7 +1,7 @@
//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
"a" = (
/turf/space,
/area/space)
/turf/template_noop,
/area/template_noop)
"b" = (
/turf/simulated/mineral,
/area/ruin/unpowered)
@@ -1,7 +1,7 @@
//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
"a" = (
/turf/space,
/area/space)
/turf/template_noop,
/area/template_noop)
"b" = (
/turf/simulated/mineral,
/area/ruin/unpowered)
@@ -1,11 +1,11 @@
//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
"a" = (
/turf/space,
/area/space)
/turf/template_noop,
/area/template_noop)
"b" = (
/obj/structure/lattice,
/turf/space,
/area/space)
/turf/template_noop,
/area/template_noop)
"c" = (
/turf/simulated/wall/r_wall,
/area/ruin/unpowered)
@@ -1,11 +1,11 @@
//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
"a" = (
/turf/space,
/area/space)
/turf/template_noop,
/area/template_noop)
"b" = (
/obj/structure/lattice,
/turf/space,
/area/space)
/turf/template_noop,
/area/template_noop)
"c" = (
/turf/simulated/wall,
/area/ruin/unpowered)
@@ -1,9 +1,9 @@
//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
"a" = (
/turf/space,
/area/space)
/turf/template_noop,
/area/template_noop)
"b" = (
/turf/space,
/turf/template_noop,
/turf/simulated/shuttle/wall{
dir = 8;
icon_state = "diagonalWall3"
@@ -15,7 +15,7 @@
},
/area/ruin/powered)
"d" = (
/turf/space,
/turf/template_noop,
/turf/simulated/shuttle/wall{
dir = 2;
icon_state = "diagonalWall3"
@@ -198,7 +198,7 @@
},
/area/ruin/powered)
"A" = (
/turf/space,
/turf/template_noop,
/turf/simulated/shuttle/wall{
dir = 1;
icon_state = "diagonalWall3"
@@ -212,7 +212,7 @@
},
/area/ruin/powered)
"C" = (
/turf/space,
/turf/template_noop,
/turf/simulated/shuttle/wall{
dir = 4;
icon_state = "diagonalWall3"
@@ -1,7 +1,7 @@
//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
"a" = (
/turf/space,
/area/space)
/turf/template_noop,
/area/template_noop)
"b" = (
/turf/simulated/mineral,
/area/ruin/powered)
@@ -73,8 +73,7 @@
},
/mob/living/simple_animal/hostile/syndicate{
desc = "A weary looking syndicate operative.";
environment_smash = 0;
faction = "syndicate"
environment_smash = 0
},
/turf/simulated/floor/plasteel,
/area/ruin/powered)
@@ -1,7 +1,7 @@
//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
"a" = (
/turf/space,
/area/space)
/turf/template_noop,
/area/template_noop)
"b" = (
/turf/simulated/shuttle/wall{
tag = "icon-swall_s6";
@@ -168,7 +168,7 @@
/area/ruin/powered)
"D" = (
/obj/structure/lattice,
/turf/space,
/turf/template_noop,
/area/space/nearstation)
"E" = (
/obj/item/stack/rods,
@@ -202,7 +202,7 @@
/area/ruin/powered)
"K" = (
/obj/item/stack/sheet/metal,
/turf/space,
/turf/template_noop,
/area/space/nearstation)
"L" = (
/obj/structure/mecha_wreckage/gygax,
@@ -228,7 +228,7 @@
/area/ruin/powered)
"P" = (
/obj/item/stack/rods,
/turf/space,
/turf/template_noop,
/area/space/nearstation)
"Q" = (
/turf/simulated/shuttle/wall{
@@ -1,7 +1,7 @@
//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
"aa" = (
/turf/space,
/area/space)
/turf/template_noop,
/area/template_noop)
"ab" = (
/obj/structure/lattice,
/obj/structure/cable{
@@ -10,7 +10,7 @@
icon_state = "2-4";
tag = ""
},
/turf/space,
/turf/template_noop,
/area/space/nearstation)
"ac" = (
/obj/structure/lattice,
@@ -20,7 +20,7 @@
icon_state = "4-8";
tag = ""
},
/turf/space,
/turf/template_noop,
/area/space/nearstation)
"ad" = (
/obj/structure/lattice,
@@ -29,7 +29,7 @@
d2 = 8;
icon_state = "2-8"
},
/turf/space,
/turf/template_noop,
/area/space/nearstation)
"ae" = (
/obj/structure/lattice,
@@ -38,7 +38,7 @@
dir = 2;
icon_state = "coil_red2"
},
/turf/space,
/turf/template_noop,
/area/space/nearstation)
"af" = (
/obj/structure/lattice,
@@ -48,7 +48,7 @@
icon_state = "1-8";
tag = ""
},
/turf/space,
/turf/template_noop,
/area/space/nearstation)
"ag" = (
/turf/simulated/wall,
@@ -65,7 +65,7 @@
d2 = 2;
icon_state = "1-2"
},
/turf/space,
/turf/template_noop,
/area/space/nearstation)
"aj" = (
/turf/unsimulated/floor/plating/airless,
@@ -154,11 +154,11 @@
/area/ruin/onehalf/drone_bay)
"aw" = (
/obj/structure/lattice,
/turf/space,
/turf/template_noop,
/area/space/nearstation)
"ax" = (
/obj/structure/lattice,
/turf/space,
/turf/template_noop,
/area/ruin/onehalf/hallway)
"ay" = (
/obj/structure/table_frame,
@@ -224,7 +224,7 @@
/area/ruin/onehalf/drone_bay)
"aH" = (
/obj/item/stack/rods,
/turf/space,
/turf/template_noop,
/area/ruin/onehalf/hallway)
"aI" = (
/obj/effect/landmark/damageturf,
@@ -805,7 +805,7 @@
/area/ruin/onehalf/bridge)
"bU" = (
/obj/item/stack/sheet/metal,
/turf/space,
/turf/template_noop,
/area/space/nearstation)
"bV" = (
/obj/structure/disposalpipe/segment,
@@ -914,7 +914,7 @@
dir = 4
},
/obj/item/stack/rods,
/turf/space,
/turf/template_noop,
/area/ruin/onehalf/hallway)
"cn" = (
/obj/structure/lattice,
@@ -925,7 +925,7 @@
tag = "icon-small";
icon_state = "small"
},
/turf/space,
/turf/template_noop,
/area/space/nearstation)
"co" = (
/obj/structure/grille,
@@ -1008,7 +1008,7 @@
/area/ruin/onehalf/bridge)
"cv" = (
/obj/item/stack/rods,
/turf/space,
/turf/template_noop,
/area/space/nearstation)
"cw" = (
/turf/unsimulated/floor/plating/airless,
@@ -1016,7 +1016,7 @@
"cx" = (
/obj/structure/lattice,
/obj/item/stack/sheet/plasteel,
/turf/space,
/turf/template_noop,
/area/space/nearstation)
"cy" = (
/obj/structure/lattice,
@@ -1024,7 +1024,7 @@
tag = "icon-medium";
icon_state = "medium"
},
/turf/space,
/turf/template_noop,
/area/ruin/onehalf/hallway)
"cz" = (
/obj/structure/grille/broken,
@@ -1071,10 +1071,10 @@
/area/ruin/onehalf/bridge)
"cE" = (
/obj/item/stack/tile/wood,
/turf/space,
/turf/template_noop,
/area/space/nearstation)
"cF" = (
/turf/space,
/turf/template_noop,
/area/ruin/onehalf/hallway)
"cG" = (
/obj/structure/lattice,
@@ -1083,7 +1083,7 @@
d2 = 8;
icon_state = "2-8"
},
/turf/space,
/turf/template_noop,
/area/ruin/onehalf/hallway)
"cH" = (
/obj/structure/lattice,
@@ -1097,7 +1097,7 @@
icon_state = "pipe-b";
dir = 8
},
/turf/space,
/turf/template_noop,
/area/ruin/onehalf/hallway)
"cI" = (
/obj/machinery/door/airlock/command/glass{
@@ -1149,7 +1149,7 @@
dir = 2;
icon_state = "coil_red2"
},
/turf/space,
/turf/template_noop,
/area/ruin/onehalf/hallway)
"cQ" = (
/obj/machinery/light{
@@ -1160,7 +1160,7 @@
/area/ruin/onehalf/bridge)
"cR" = (
/obj/item/stack/sheet/plasteel,
/turf/space,
/turf/template_noop,
/area/space/nearstation)
"cS" = (
/obj/structure/lattice,
@@ -1169,7 +1169,7 @@
d2 = 2;
icon_state = "1-2"
},
/turf/space,
/turf/template_noop,
/area/ruin/onehalf/hallway)
"cU" = (
/obj/structure/grille,
@@ -1221,7 +1221,7 @@
d2 = 8;
icon_state = "2-8"
},
/turf/space,
/turf/template_noop,
/area/space/nearstation)
"db" = (
/obj/structure/table,
@@ -1256,7 +1256,7 @@
d2 = 2;
icon_state = "1-2"
},
/turf/space,
/turf/template_noop,
/area/space/nearstation)
"dg" = (
/obj/structure/girder/reinforced,
@@ -1350,7 +1350,7 @@
tag = "icon-medium";
icon_state = "medium"
},
/turf/space,
/turf/template_noop,
/area/space/nearstation)
"dp" = (
/obj/structure/lattice,
@@ -1366,7 +1366,7 @@
icon_state = "1-4"
},
/obj/item/stack/rods,
/turf/space,
/turf/template_noop,
/area/space/nearstation)
"dq" = (
/obj/structure/lattice,
@@ -1382,7 +1382,7 @@
icon_state = "1-8";
tag = ""
},
/turf/space,
/turf/template_noop,
/area/space/nearstation)
(1,1,1) = {"
@@ -1,7 +1,7 @@
//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
"aa" = (
/turf/space,
/area/space)
/turf/template_noop,
/area/template_noop)
"ab" = (
/turf/simulated/mineral,
/area/space/nearstation)
@@ -1,10 +1,10 @@
//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
"a" = (
/turf/space,
/area/space)
/turf/template_noop,
/area/template_noop)
"b" = (
/obj/structure/lattice,
/turf/space,
/turf/template_noop,
/area/space/nearstation)
"c" = (
/obj/structure/grille,
@@ -1,7 +1,7 @@
//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
"a" = (
/turf/space,
/area/space)
/turf/template_noop,
/area/template_noop)
"b" = (
/turf/simulated/mineral/random,
/area/ruin/unpowered/no_grav/way_home)
@@ -1,7 +1,7 @@
//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
"aa" = (
/turf/space,
/area/space)
/turf/template_noop,
/area/template_noop)
"ab" = (
/turf/simulated/floor/plating/asteroid/airless,
/area/ruin/unpowered)
+28 -25
View File
@@ -681,7 +681,7 @@
req_access_txt = "2"
},
/obj/structure/cable{
icon_state = "1-10"
icon_state = "1-2"
},
/turf/simulated/floor/plating,
/area/mine/laborcamp)
@@ -858,18 +858,11 @@
/turf/simulated/floor/plasteel,
/area/mine/laborcamp/security)
"ce" = (
/obj/structure/cable{
icon_state = "2-4"
/obj/machinery/power/smes{
charge = 5e+006
},
/obj/structure/cable{
icon_state = "5-6"
},
/turf/simulated/floor/plating,
/area/mine/laborcamp)
"cf" = (
/obj/machinery/power/terminal,
/obj/structure/cable{
icon_state = "0-8"
icon_state = "0-4"
},
/turf/simulated/floor/plating,
/area/mine/laborcamp)
@@ -880,6 +873,9 @@
/turf/simulated/floor/plating,
/area/mine/laborcamp)
"ch" = (
/obj/structure/cable{
icon_state = "1-8"
},
/turf/simulated/floor/plating,
/area/mine/laborcamp)
"ci" = (
@@ -1033,21 +1029,22 @@
/turf/simulated/floor/plasteel,
/area/mine/laborcamp)
"cz" = (
/obj/machinery/power/port_gen/pacman{
anchored = 1
},
/obj/structure/cable,
/turf/simulated/floor/plating,
/area/mine/laborcamp)
"cA" = (
/obj/machinery/power/smes{
charge = 5e+006
/obj/machinery/power/terminal{
dir = 1
},
/obj/structure/cable{
icon_state = "0-4"
},
/obj/structure/reagent_dispensers/fueltank,
/turf/simulated/floor/plating,
/area/mine/laborcamp)
"cA" = (
/obj/structure/cable{
icon_state = "0-9"
d1 = 4;
d2 = 8;
icon_state = "4-8";
pixel_x = 0;
tag = ""
},
/turf/simulated/floor/plating,
/area/mine/laborcamp)
@@ -1056,7 +1053,13 @@
/turf/simulated/floor/plating,
/area/mine/laborcamp)
"cC" = (
/obj/structure/reagent_dispensers/fueltank,
/obj/machinery/power/port_gen/pacman{
anchored = 1
},
/obj/structure/cable{
d2 = 8;
icon_state = "0-8"
},
/turf/simulated/floor/plating,
/area/mine/laborcamp)
"cD" = (
@@ -11202,7 +11205,7 @@ bm
aq
bB
bM
cf
ch
cA
aq
aj
@@ -11460,7 +11463,7 @@ aq
aq
aq
cg
cB
cA
aq
aj
cQ
@@ -11716,7 +11719,7 @@ aD
aD
aD
aq
ch
cB
cC
aq
aj
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -23,7 +23,7 @@ z7 = empty space
#define MAP_TRANSITION_CONFIG list(\
DECLARE_LEVEL(MAIN_STATION, CROSSLINKED, list(STATION_LEVEL, STATION_CONTACT, REACHABLE, AI_OK)),\
DECLARE_LEVEL(CENTCOMM, SELFLOOPING, list(ADMIN_LEVEL, BLOCK_TELEPORT, IMPEDES_MAGIC)),\
DECLARE_LEVEL(MINING, SELFLOOPING, list(REACHABLE, STATION_CONTACT, HAS_WEATHER, ORE_LEVEL, AI_OK)))
DECLARE_LEVEL(MINING, SELFLOOPING, list(ORE_LEVEL, REACHABLE, STATION_CONTACT, HAS_WEATHER, AI_OK)))
#define USING_MAP_DATUM /datum/map/metastation
-56
View File
@@ -1,56 +0,0 @@
/datum/stack
var/list/stack = list()
var/max_elements = 0
/datum/stack/New(list/elements,max)
..()
if(elements)
stack = elements.Copy()
if(max)
max_elements = max
/datum/stack/proc/Pop()
if(is_empty())
return null
. = stack[stack.len]
stack.Cut(stack.len,0)
/datum/stack/proc/Push(element)
if(max_elements && (stack.len+1 > max_elements))
return null
stack += element
/datum/stack/proc/Top()
if(is_empty())
return null
. = stack[stack.len]
/datum/stack/proc/is_empty()
. = stack.len ? 0 : 1
//Rotate entire stack left with the leftmost looping around to the right
/datum/stack/proc/RotateLeft()
if(is_empty())
return 0
. = stack[1]
stack.Cut(1,2)
Push(.)
//Rotate entire stack to the right with the rightmost looping around to the left
/datum/stack/proc/RotateRight()
if(is_empty())
return 0
. = stack[stack.len]
stack.Cut(stack.len,0)
stack.Insert(1,.)
/datum/stack/proc/Copy()
var/datum/stack/S=new()
S.stack = stack.Copy()
S.max_elements = max_elements
return S
/datum/stack/proc/Clear()
stack.Cut()
+1
View File
@@ -7,6 +7,7 @@
#define PLANE_SPACE_PARALLAX -90
#define FLOOR_PLANE -2
#define FLOOR_OVERLAY_PLANE -1.5
#define GAME_PLANE -1
#define BLACKNESS_PLANE 0 //To keep from conflicts with SEE_BLACKNESS internals
+5
View File
@@ -4,3 +4,8 @@
#define SAY_LOG "Say"
#define EMOTE_LOG "Emote"
#define MISC_LOG "Misc"
#define DEADCHAT_LOG "Deadchat"
#define OOC_LOG "OOC"
#define LOOC_LOG "LOOC"
#define ALL_LOGS list(ATTACK_LOG, DEFENSE_LOG, CONVERSION_LOG, SAY_LOG, EMOTE_LOG, DEADCHAT_LOG, OOC_LOG, LOOC_LOG, MISC_LOG)
+2 -60
View File
@@ -425,66 +425,8 @@
if(pressure <= LAVALAND_EQUIPMENT_EFFECT_PRESSURE)
. = TRUE
proc/pollCandidates(Question, be_special_type, antag_age_check = FALSE, poll_time = 300, ignore_respawnability = FALSE, min_hours = 0, flashwindow = TRUE, check_antaghud = TRUE)
var/roletext = be_special_type ? get_roletext(be_special_type) : null
var/list/mob/dead/observer/candidates = list()
var/time_passed = world.time
if(!Question)
Question = "Would you like to be a special role?"
for(var/mob/dead/observer/G in (ignore_respawnability ? GLOB.player_list : GLOB.respawnable_list))
if(!G.key || !G.client)
continue
if(be_special_type)
if(!(be_special_type in G.client.prefs.be_special))
continue
if(antag_age_check)
if(!player_old_enough_antag(G.client, be_special_type))
continue
if(roletext)
if(jobban_isbanned(G, roletext) || jobban_isbanned(G, "Syndicate"))
continue
if(config.use_exp_restrictions && min_hours)
if(G.client.get_exp_type_num(EXP_TYPE_LIVING) < min_hours * 60)
continue
if(check_antaghud && cannotPossess(G))
continue
spawn(0)
G << 'sound/misc/notice2.ogg'//Alerting them to their consideration
if(flashwindow)
window_flash(G.client)
var/ans = alert(G,Question,"Please answer in [poll_time/10] seconds!","No","Yes","Not This Round")
if(!G?.client)
return
switch(ans)
if("Yes")
to_chat(G, "<span class='notice'>Choice registered: Yes.</span>")
if((world.time-time_passed)>poll_time)//If more than 30 game seconds passed.
to_chat(G, "<span class='danger'>Sorry, you were too late for the consideration!</span>")
G << 'sound/machines/buzz-sigh.ogg'
return
candidates += G
if("No")
to_chat(G, "<span class='danger'>Choice registered: No.</span>")
return
if("Not This Round")
to_chat(G, "<span class='danger'>Choice registered: No.</span>")
to_chat(G, "<span class='notice'>You will no longer receive notifications for the role '[roletext]' for the rest of the round.</span>")
G.client.prefs.be_special -= be_special_type
return
else
return
sleep(poll_time)
//Check all our candidates, to make sure they didn't log off during the 30 second wait period.
for(var/mob/dead/observer/G in candidates)
if(!G.key || !G.client)
candidates.Remove(G)
return candidates
/proc/pollCandidatesWithVeto(adminclient, adminusr, max_slots, Question, be_special_type, antag_age_check = 0, poll_time = 300, ignore_respawnability = 0, min_hours = 0, flashwindow = TRUE, check_antaghud = TRUE)
var/list/willing_ghosts = pollCandidates(Question, be_special_type, antag_age_check, poll_time, ignore_respawnability, min_hours, flashwindow, check_antaghud)
/proc/pollCandidatesWithVeto(adminclient, adminusr, max_slots, Question, be_special_type, antag_age_check = FALSE, poll_time = 300, ignore_respawnability = FALSE, min_hours = FALSE, flashwindow = TRUE, check_antaghud = TRUE, source)
var/list/willing_ghosts = SSghost_spawns.poll_candidates(Question, be_special_type, antag_age_check, poll_time, ignore_respawnability, min_hours, flashwindow, check_antaghud, source)
var/list/selected_ghosts = list()
if(!willing_ghosts.len)
return selected_ghosts
@@ -1,28 +1,28 @@
//////////////////////
//Heap object
//datum/heap object
//////////////////////
/Heap
/datum/heap
var/list/L
var/cmp
/Heap/New(compare)
/datum/heap/New(compare)
L = new()
cmp = compare
/Heap/proc/IsEmpty()
/datum/heap/proc/IsEmpty()
return !L.len
//Insert and place at its position a new node in the heap
/Heap/proc/Insert(atom/A)
/datum/heap/proc/Insert(atom/A)
L.Add(A)
Swim(L.len)
//removes and returns the first element of the heap
//(i.e the max or the min dependant on the comparison function)
/Heap/proc/Pop()
/datum/heap/proc/Pop()
if(!L.len)
return 0
. = L[1]
@@ -33,7 +33,7 @@
Sink(1)
//Get a node up to its right position in the heap
/Heap/proc/Swim(var/index)
/datum/heap/proc/Swim(var/index)
var/parent = round(index * 0.5)
while(parent > 0 && (call(cmp)(L[index],L[parent]) > 0))
@@ -42,7 +42,7 @@
parent = round(index * 0.5)
//Get a node down to its right position in the heap
/Heap/proc/Sink(var/index)
/datum/heap/proc/Sink(var/index)
var/g_child = GetGreaterChild(index)
while(g_child > 0 && (call(cmp)(L[index],L[g_child]) < 0))
@@ -52,7 +52,7 @@
//Returns the greater (relative to the comparison proc) of a node children
//or 0 if there's no child
/Heap/proc/GetGreaterChild(var/index)
/datum/heap/proc/GetGreaterChild(var/index)
if(index * 2 > L.len)
return 0
@@ -65,11 +65,11 @@
return index * 2
//Replaces a given node so it verify the heap condition
/Heap/proc/ReSort(atom/A)
/datum/heap/proc/ReSort(atom/A)
var/index = L.Find(A)
Swim(index)
Sink(index)
/Heap/proc/List()
/datum/heap/proc/List()
. = L.Copy()
+3
View File
@@ -693,6 +693,9 @@ proc/dd_sortedObjectList(list/incoming)
// Lazying Episode 3
#define LAZYSET(L, K, V) LAZYINITLIST(L); L[K] = V;
/// Returns whether a numerical index is within a given list's bounds. Faster than isnull(LAZYACCESS(L, I)).
#define ISINDEXSAFE(L, I) (I >= 1 && I <= length(L))
//same, but returns nothing and acts on list in place
/proc/shuffle_inplace(list/L)
if(!L)
+9 -2
View File
@@ -289,11 +289,14 @@ This is always put in the attack log.
var/user_str = key_name_log(user) + COORD(user)
var/target_str
var/target_info
if(isatom(target))
var/atom/AT = target
target_str = key_name_log(AT) + COORD(AT)
target_info = key_name_admin(target)
else
target_str = target
target_info = target
var/mob/MU = user
var/mob/MT = target
if(istype(MU))
@@ -317,10 +320,13 @@ This is always put in the attack log.
var/area/A = get_area(MT)
if(A && A.hide_attacklogs)
loglevel = ATKLOG_ALMOSTALL
else
loglevel = ATKLOG_ALL // Hitting an object. Not a mob
if(isLivingSSD(target)) // Attacks on SSDs are shown to admins with any log level except ATKLOG_NONE. Overrides custom level
loglevel = ATKLOG_FEW
msg_admin_attack("[key_name_admin(user)] vs [key_name_admin(target)]: [what_done]", loglevel)
msg_admin_attack("[key_name_admin(user)] vs [target_info]: [what_done]", loglevel)
/proc/do_mob(mob/user, mob/target, time = 30, uninterruptible = 0, progress = 1, list/extra_checks = list())
if(!user || !target)
@@ -579,7 +585,8 @@ GLOBAL_LIST_INIT(do_after_once_tracker, list())
LogMouseMacro(".mouse", params)
/proc/update_all_mob_security_hud()
for(var/mob/living/carbon/human/H in GLOB.mob_list)
for(var/thing in GLOB.human_list)
var/mob/living/carbon/human/H = thing
H.sec_hud_set_security_status()
/proc/getviewsize(view)
+1 -1
View File
@@ -533,7 +533,7 @@ proc/checkhtml(var/t)
text = replacetext(text, "\[/grid\]", "</td></tr></table>")
text = replacetext(text, "\[row\]", "</td><tr>")
text = replacetext(text, "\[cell\]", "<td>")
text = replacetext(text, "\[logo\]", "<img src = ntlogo.png>")
text = replacetext(text, "\[logo\]", "&ZeroWidthSpace;<img src = ntlogo.png>")
text = replacetext(text, "\[time\]", "[station_time_timestamp()]") // TO DO
if(!no_font)
if(P)
+5
View File
@@ -2017,5 +2017,10 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
set waitfor = FALSE
return call(source, proctype)(arglist(arguments))
/proc/IsFrozen(atom/A)
if(A in GLOB.frozen_atom_list)
return TRUE
return FALSE
/// Waits at a line of code until X is true
#define UNTIL(X) while(!(X)) stoplag()
+2
View File
@@ -34,4 +34,6 @@ GLOBAL_PROTECT(IClog)
GLOBAL_LIST_EMPTY(OOClog)
GLOBAL_PROTECT(OOClog)
GLOBAL_DATUM_INIT(logging, /datum/logging, new /datum/logging())
GLOBAL_LIST_INIT(investigate_log_subjects, list("notes", "watchlist", "hrefs"))
+3 -1
View File
@@ -73,7 +73,9 @@
var/dragged = modifiers["drag"]
if(dragged && !modifiers[dragged])
return
if(IsFrozen(A) && !is_admin(usr))
to_chat(usr, "<span class='boldannounce'>Interacting with admin-frozen players is not permitted.</span>")
return
if(modifiers["middle"] && modifiers["shift"] && modifiers["ctrl"])
MiddleShiftControlClickOn(A)
return
+83 -20
View File
@@ -3,7 +3,7 @@
//PUBLIC - call these wherever you want
/mob/proc/throw_alert(category, type, severity, obj/new_master, override = FALSE)
/mob/proc/throw_alert(category, type, severity, obj/new_master, override = FALSE, timeout_override, no_anim)
/*
Proc to create or update an alert. Returns the alert if the alert is new or updated, 0 if it was thrown already
@@ -59,14 +59,16 @@
LAZYSET(alerts, category, alert) // This also creates the list if it doesn't exist
if(client && hud_used)
hud_used.reorganize_alerts()
alert.transform = matrix(32, 6, MATRIX_TRANSLATE)
animate(alert, transform = matrix(), time = 2.5, easing = CUBIC_EASING)
if(alert.timeout)
spawn(alert.timeout)
if(alert.timeout && alerts[category] == alert && world.time >= alert.timeout)
clear_alert(category)
alert.timeout = world.time + alert.timeout - world.tick_lag
if(!no_anim)
alert.transform = matrix(32, 6, MATRIX_TRANSLATE)
animate(alert, transform = matrix(), time = 2.5, easing = CUBIC_EASING)
var/timeout = timeout_override || alert.timeout
if(timeout)
addtimer(CALLBACK(alert, /obj/screen/alert/.proc/do_timeout, src, category), timeout)
alert.timeout = world.time + timeout - world.tick_lag
return alert
// Proc to clear an existing alert.
@@ -94,7 +96,6 @@
var/alerttooltipstyle = ""
var/override_alerts = FALSE //If it is overriding other alerts of the same type
/obj/screen/alert/MouseEntered(location,control,params)
openToolTip(usr, src, params, title = name, content = desc, theme = alerttooltipstyle)
@@ -102,6 +103,12 @@
/obj/screen/alert/MouseExited()
closeToolTip(usr)
/obj/screen/alert/proc/do_timeout(mob/M, category)
if(!M || !M.alerts)
return
if(timeout && M.alerts[category] == src && world.time >= timeout)
M.clear_alert(category)
//Gas alerts
/obj/screen/alert/not_enough_oxy
@@ -507,6 +514,34 @@ so as to remain in compliance with the most up-to-date laws."
timeout = 300
var/atom/target = null
var/action = NOTIFY_JUMP
var/show_time_left = FALSE // If true you need to call START_PROCESSING manually
var/image/time_left_overlay // The last image showing the time left
var/datum/candidate_poll/poll // If set, on Click() it'll register the player as a candidate
/obj/screen/alert/notify_action/process()
if(show_time_left)
var/timeleft = timeout - world.time
if(timeleft <= 0)
return PROCESS_KILL
if(time_left_overlay)
overlays -= time_left_overlay
var/obj/O = new
O.maptext = "<span style='font-family: \"Small Fonts\"; font-weight: bold; font-size: 32px; color: [(timeleft <= 10 SECONDS) ? "red" : "white"];'>[CEILING(timeleft / 10, 1)]</span>"
O.maptext_width = O.maptext_height = 128
var/matrix/M = new
M.Translate(4, 16)
O.transform = M
var/image/I = image(O)
I.layer = FLOAT_LAYER
I.plane = FLOAT_PLANE + 1
overlays += I
time_left_overlay = I
qdel(O)
..()
/obj/screen/alert/notify_action/Destroy()
target = null
@@ -515,20 +550,48 @@ so as to remain in compliance with the most up-to-date laws."
/obj/screen/alert/notify_action/Click()
if(!usr || !usr.client)
return
if(!target)
return
var/mob/dead/observer/G = usr
if(!istype(G))
return
switch(action)
if(NOTIFY_ATTACK)
target.attack_ghost(G)
if(NOTIFY_JUMP)
var/turf/T = get_turf(target)
if(T && isturf(T))
G.loc = T
if(NOTIFY_FOLLOW)
G.ManualFollow(target)
if(poll)
if(poll.sign_up(G))
// Add a small overlay to indicate we've signed up
display_signed_up()
else if(target)
switch(action)
if(NOTIFY_ATTACK)
target.attack_ghost(G)
if(NOTIFY_JUMP)
var/turf/T = get_turf(target)
if(T && isturf(T))
G.loc = T
if(NOTIFY_FOLLOW)
G.ManualFollow(target)
/obj/screen/alert/notify_action/proc/display_signed_up()
var/image/I = image('icons/mob/screen_gen.dmi', icon_state = "selector")
I.layer = FLOAT_LAYER
I.plane = FLOAT_PLANE + 2
overlays += I
/obj/screen/alert/notify_action/proc/display_stacks(stacks = 1)
if(stacks <= 1)
return
var/obj/O = new
O.maptext = "<span style='font-family: \"Small Fonts\"; font-size: 32px; color: yellow;'>[stacks]x</span>"
O.maptext_width = O.maptext_height = 128
var/matrix/M = new
M.Translate(4, 2)
O.transform = M
var/image/I = image(O)
I.layer = FLOAT_LAYER
I.plane = FLOAT_PLANE + 1
overlays += I
qdel(O)
/obj/screen/alert/notify_soulstone
name = "Soul Stone"
+1 -1
View File
@@ -81,7 +81,7 @@
user.do_attack_animation(M)
. = M.attacked_by(src, user, def_zone)
add_attack_logs(user, M, "Attacked with [name] (INTENT: [uppertext(user.a_intent)]) (DAMTYPE: [uppertext(damtype)])", (M.ckey && force > 0 && damtype != STAMINA) ? null : ATKLOG_ALMOSTALL)
add_attack_logs(user, M, "Attacked with [name] ([uppertext(user.a_intent)]) ([uppertext(damtype)])", (M.ckey && force > 0 && damtype != STAMINA) ? null : ATKLOG_ALMOSTALL)
add_fingerprint(user)
-84
View File
@@ -1,84 +0,0 @@
#define MIDDLE_CLICK 0
#define ALT_CLICK 1
#define CTRL_CLICK 2
#define MAX_HARDSUIT_CLICK_MODE 2
/client
var/hardsuit_click_mode = MIDDLE_CLICK
/client/verb/toggle_hardsuit_mode()
set name = "Toggle Hardsuit Activation Mode"
set desc = "Switch between hardsuit activation modes."
set category = "OOC"
hardsuit_click_mode++
if(hardsuit_click_mode > MAX_HARDSUIT_CLICK_MODE)
hardsuit_click_mode = 0
switch(hardsuit_click_mode)
if(MIDDLE_CLICK)
to_chat(src, "Hardsuit activation mode set to middle-click.")
if(ALT_CLICK)
to_chat(src, "Hardsuit activation mode set to alt-click.")
if(CTRL_CLICK)
to_chat(src, "Hardsuit activation mode set to control-click.")
else
// should never get here, but just in case:
log_runtime(EXCEPTION("Bad hardsuit click mode: [hardsuit_click_mode] - expected 0 to [MAX_HARDSUIT_CLICK_MODE]"), src)
to_chat(src, "Somehow you bugged the system. Setting your hardsuit mode to middle-click.")
hardsuit_click_mode = MIDDLE_CLICK
/mob/living/MiddleClickOn(atom/A)
if(client && client.hardsuit_click_mode == MIDDLE_CLICK)
if(HardsuitClickOn(A))
return
..()
/mob/living/AltClickOn(atom/A)
if(client && client.hardsuit_click_mode == ALT_CLICK)
if(HardsuitClickOn(A))
return
..()
/mob/living/CtrlClickOn(atom/A)
if(client && client.hardsuit_click_mode == CTRL_CLICK)
if(HardsuitClickOn(A))
return
..()
/mob/living/proc/can_use_rig()
return 0
/mob/living/carbon/human/can_use_rig()
return 1
/mob/living/carbon/brain/can_use_rig()
return istype(loc, /obj/item/mmi)
/mob/living/silicon/ai/can_use_rig()
return istype(loc, /obj/item/aicard)
/mob/living/silicon/pai/can_use_rig()
return loc == card
/mob/living/proc/HardsuitClickOn(var/atom/A, var/alert_ai = 0)
if(!can_use_rig() || (next_move > world.time))
return 0
var/obj/item/rig/rig = get_rig()
if(istype(rig) && !rig.offline && rig.selected_module)
if(src != rig.wearer)
if(rig.ai_can_move_suit(src, check_user_module = 1))
message_admins("[key_name_admin(src)] is trying to force \the [key_name_admin(rig.wearer)] to use a hardsuit module.")
else
return 0
rig.selected_module.engage(A, alert_ai)
if(ismob(A)) // No instant mob attacking - though modules have their own cooldowns
changeNext_move(CLICK_CD_MELEE)
return 1
return 0
#undef MIDDLE_CLICK
#undef ALT_CLICK
#undef CTRL_CLICK
#undef MAX_HARDSUIT_CLICK_MODE
-4
View File
@@ -61,7 +61,6 @@
var/usewhitelist = 0
var/mods_are_mentors = 0
var/load_jobs_from_txt = 0
var/ToRban = 0
var/automute_on = 0 //enables automuting/spam prevention
var/jobs_have_minimal_access = 0 //determines whether jobs use minimal access or expanded access.
var/round_abandon_penalty_period = 30 MINUTES // Time from round start during which ghosting out is penalized
@@ -578,9 +577,6 @@
if("humans_need_surnames")
humans_need_surnames = 1
if("tor_ban")
ToRban = 1
if("automute_on")
automute_on = 1
+1 -1
View File
@@ -35,7 +35,7 @@
var/static/list/failure_strikes //How many times we suspect a subsystem type has crashed the MC, 3 strikes and you're out!
var/offline_implications = "None" // What are the implications of this SS being offlined?
var/offline_implications = "None. No immediate action is needed." // What are the implications of this SS being offlined?
//Do not override
///datum/controller/subsystem/New()
+2 -1
View File
@@ -21,7 +21,8 @@ SUBSYSTEM_DEF(afk)
/datum/controller/subsystem/afk/fire()
var/list/toRemove = list()
for(var/mob/living/carbon/human/H in GLOB.alive_mob_list)
for(var/thing in GLOB.human_list)
var/mob/living/carbon/human/H = thing
if(!H?.ckey) // Useless non ckey creatures
continue
+1 -1
View File
@@ -4,7 +4,7 @@ SUBSYSTEM_DEF(garbage)
wait = 2 SECONDS
flags = SS_POST_FIRE_TIMING|SS_BACKGROUND|SS_NO_INIT
runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY
init_order = INIT_ORDER_GARBAGE
init_order = INIT_ORDER_GARBAGE // Why does this have an init order if it has SS_NO_INIT?
offline_implications = "Garbage collection is no longer functional, and objects will not be qdel'd. Immediate server restart recommended."
var/list/collection_timeout = list(2 MINUTES, 10 SECONDS) // deciseconds to wait before moving something up in the queue to the next level
+268
View File
@@ -0,0 +1,268 @@
SUBSYSTEM_DEF(ghost_spawns)
name = "Ghost Spawns"
init_order = INIT_ORDER_EVENTS
flags = SS_BACKGROUND
wait = 1 SECONDS
runlevels = RUNLEVEL_GAME
offline_implications = "Ghosts will no longer be able to respawn as event mobs (Blob, etc..). Shuttle call recommended."
/// List of polls currently ongoing, to be checked on next fire()
var/list/datum/candidate_poll/currently_polling
/// Whether there are active polls or not
var/polls_active = FALSE
/// Number of polls performed since the start
var/total_polls = 0
/// The poll that's closest to finishing
var/datum/candidate_poll/next_poll_to_finish
/datum/controller/subsystem/ghost_spawns/fire()
if(!polls_active)
return
if(!currently_polling) // if polls_active is TRUE then this shouldn't happen, but still..
currently_polling = list()
for(var/poll in currently_polling)
var/datum/candidate_poll/P = poll
if(P.time_left() <= 0)
polling_finished(P)
/**
* Polls for candidates with a question and a preview of the role
*
* This proc replaces /proc/pollCandidates.
* Should NEVER be used in a proc that has waitfor set to FALSE/0 (due to #define UNTIL)
* Arguments:
* * question - The question to ask to potential candidates
* * role - The role to poll for. Should be a ROLE_x enum. If set, potential candidates who aren't eligible will be ignored
* * antag_age_check - Whether to filter out potential candidates who don't have an old enough account
* * poll_time - How long to poll for in deciseconds
* * ignore_respawnability - Whether to ignore the player's respawnability
* * min_hours - The amount of hours needed for a potential candidate to be eligible
* * flash_window - Whether the poll should flash a potential candidate's game window
* * check_antaghud - Whether to filter out potential candidates who enabled AntagHUD
* * source - The atom, atom prototype, icon or mutable appearance to display as an icon in the alert
*/
/datum/controller/subsystem/ghost_spawns/proc/poll_candidates(question = "Would you like to play a special role?", role, antag_age_check = FALSE, poll_time = 30 SECONDS, ignore_respawnability = FALSE, min_hours = 0, flash_window = TRUE, check_antaghud = TRUE, source)
log_debug("Polling candidates [role ? "for [get_roletext(role)]" : "\"[question]\""] for [poll_time / 10] seconds")
// Start firing
polls_active = TRUE
total_polls++
var/datum/candidate_poll/P = new(role, question, poll_time)
LAZYADD(currently_polling, P)
// We're the poll closest to completion
if(!next_poll_to_finish || poll_time < next_poll_to_finish.time_left())
next_poll_to_finish = P
var/category = "[P.hash]_notify_action"
for(var/mob/dead/observer/M in (ignore_respawnability ? GLOB.player_list : GLOB.respawnable_list))
if(!is_eligible(M))
continue
SEND_SOUND(M, 'sound/misc/notice2.ogg')
if(flash_window)
window_flash(M.client)
// If we somehow send two polls for the same mob type, but with a duration on the second one shorter than the time left on the first one,
// we need to keep the first one's timeout rather than use the shorter one
var/obj/screen/alert/notify_action/current_alert = LAZYACCESS(M.alerts, category)
var/alert_time = poll_time
var/alert_poll = P
if(current_alert && current_alert.timeout > (world.time + poll_time - world.tick_lag))
alert_time = current_alert.timeout - world.time + world.tick_lag
alert_poll = current_alert.poll
// Send them an on-screen alert
var/obj/screen/alert/notify_action/A = M.throw_alert(category, /obj/screen/alert/notify_action, timeout_override = alert_time, no_anim = TRUE)
if(!A)
continue
A.icon = ui_style2icon(M.client?.prefs.UI_style)
A.name = "Looking for candidates"
A.desc = "[question]\n\n(expires in [poll_time / 10] seconds)"
A.show_time_left = TRUE
A.poll = alert_poll
// Sign up inheritance and stacking
var/inherited_sign_up = FALSE
var/num_stack = 1
for(var/existing_poll in currently_polling)
var/datum/candidate_poll/P2 = existing_poll
if(P != P2 && P.hash == P2.hash)
// If there's already a poll for an identical mob type ongoing and the client is signed up for it, sign them up for this one
if(!inherited_sign_up && (M in P2.signed_up) && P.sign_up(M, TRUE))
A.display_signed_up()
inherited_sign_up = TRUE
// This number is used to display the number of polls the alert regroups
num_stack++
if(num_stack > 1)
A.display_stacks(num_stack)
// Image to display
var/image/I
if(source)
if(!ispath(source))
var/atom/S = source
var/old_layer = S.layer
var/old_plane = S.plane
S.layer = FLOAT_LAYER
S.plane = FLOAT_PLANE
A.overlays += S
S.layer = old_layer
S.plane = old_plane
else
I = image(source, layer = FLOAT_LAYER, dir = SOUTH)
else
// Just use a generic image
I = image('icons/effects/effects.dmi', icon_state = "static", layer = FLOAT_LAYER, dir = SOUTH)
if(I)
I.layer = FLOAT_LAYER
I.plane = FLOAT_PLANE
A.overlays += I
// Start processing it so it updates visually the timer
START_PROCESSING(SSprocessing, A)
A.process()
// Sleep until the time is up
UNTIL(P.finished)
return P.signed_up
/**
* Returns whether an observer is eligible to be an event mob
*
* Arguments:
* * M - The mob to check eligibility
* * role - The role to check eligibility for. Checks 1. the client has enabled the role 2. the account's age for this role if antag_age_check is TRUE
* * antag_age_check - Whether to check the account's age or not for the given role.
* * role_text - The role's clean text. Used for checking job bans to determine eligibility
* * min_hours - The amount of minimum hours the client needs before being eligible
* * check_antaghud - Whether to consider a client who enabled AntagHUD ineligible or not
*/
/datum/controller/subsystem/ghost_spawns/proc/is_eligible(mob/M, role, antag_age_check, role_text, min_hours, check_antaghud)
. = FALSE
if(!M.key || !M.client)
return
if(role)
if(!(role in M.client.prefs.be_special))
return
if(antag_age_check)
if(!player_old_enough_antag(M.client, role))
return
if(role_text)
if(jobban_isbanned(M, role_text) || jobban_isbanned(M, "Syndicate"))
return
if(config.use_exp_restrictions && min_hours)
if(M.client.get_exp_type_num(EXP_TYPE_LIVING) < min_hours * 60)
return
if(check_antaghud && cannotPossess(M))
return
return TRUE
/**
* Called by the subsystem when a poll's timer runs out
*
* Can be called manually to finish a poll prematurely
* Arguments:
* * P - The poll to finish
*/
/datum/controller/subsystem/ghost_spawns/proc/polling_finished(datum/candidate_poll/P)
// Trim players who aren't eligible anymore
var/len_pre_trim = length(P.signed_up)
P.trim_candidates()
log_debug("Candidate poll [P.role ? "for [get_roletext(P.role)]" : "\"[P.question]\""] finished. [len_pre_trim] players signed up, [length(P.signed_up)] after trimming")
P.finished = TRUE
currently_polling -= P
// Determine which is the next poll closest the completion or "disable" firing if there's none
if(!length(currently_polling))
polls_active = FALSE
next_poll_to_finish = null
else if(P == next_poll_to_finish)
next_poll_to_finish = null
for(var/poll in currently_polling)
var/datum/candidate_poll/P2 = poll
if(!next_poll_to_finish || P2.time_left() < next_poll_to_finish.time_left())
next_poll_to_finish = P2
/datum/controller/subsystem/ghost_spawns/stat_entry(msg)
msg += "Active: [length(currently_polling)] | Total: [total_polls]"
if(next_poll_to_finish)
msg += " | Next: [DisplayTimeText(next_poll_to_finish.time_left())] ([length(next_poll_to_finish.signed_up)] candidates)"
..(msg)
// The datum that describes one instance of candidate polling
/datum/candidate_poll
var/role // The role the poll is for
var/question // The question asked to observers
var/duration // The duration of the poll
var/list/mob/dead/observer/signed_up // The players who signed up to this poll
var/time_started // The world.time at which the poll was created
var/finished = FALSE // Whether the polling is finished
var/hash // Used to categorize in the alerts system
/datum/candidate_poll/New(polled_role, polled_question, poll_duration)
role = polled_role
question = polled_question
duration = poll_duration
signed_up = list()
time_started = world.time
hash = copytext(md5("[question]_[role ? role : "0"]"), 1, 7)
return ..()
/**
* Attempts to sign a (controlled) mob up
*
* Will fail if the mob is already signed up or the poll's timer ran out.
* Does not check for eligibility
* Arguments:
* * M - The (controlled) mob to sign up
* * silent - Whether no messages should appear or not. If not TRUE, signing up to this poll will also sign the mob up for identical polls
*/
/datum/candidate_poll/proc/sign_up(mob/dead/observer/M, silent = FALSE)
. = FALSE
if(!istype(M) || !M.key || !M.client)
return
if(M in signed_up)
if(!silent)
to_chat(M, "<span class='warning'>You have already signed up for this!</span>")
return
if(time_left() <= 0)
if(!silent)
to_chat(M, "<span class='danger'>Sorry, you were too late for the consideration!</span>")
SEND_SOUND(M, 'sound/machines/buzz-sigh.ogg')
return
signed_up += M
if(!silent)
to_chat(M, "<span class='notice'>You have signed up for this role! A candidate will be picked randomly soon..</span>")
// Sign them up for any other polls with the same mob type
for(var/existing_poll in SSghost_spawns.currently_polling)
var/datum/candidate_poll/P = existing_poll
if(src != P && hash == P.hash && !(M in P.signed_up))
P.sign_up(M, TRUE)
return TRUE
/**
* Deletes any candidates who may have disconnected from the list
*/
/datum/candidate_poll/proc/trim_candidates()
listclearnulls(signed_up)
for(var/mob in signed_up)
var/mob/M = mob
if(!M.key || !M.client)
signed_up -= M
/**
* Returns the time left for a poll
*/
/datum/candidate_poll/proc/time_left()
return duration - (world.time - time_started)
+22 -1
View File
@@ -27,7 +27,7 @@ SUBSYSTEM_DEF(jobs)
/datum/controller/subsystem/jobs/fire()
if(!config.sql_enabled || !config.use_exp_tracking)
return
update_exp(5,0)
INVOKE_ASYNC(GLOBAL_PROC, /.proc/update_exp, 5, 0)
/datum/controller/subsystem/jobs/proc/SetupOccupations(var/list/faction = list("Station"))
occupations = list()
@@ -644,6 +644,27 @@ SUBSYSTEM_DEF(jobs)
oldjobdatum.current_positions--
newjobdatum.current_positions++
/datum/controller/subsystem/jobs/proc/notify_dept_head(jobtitle, antext)
// Used to notify the department head of jobtitle X that their employee was brigged, demoted or terminated
if(!jobtitle || !antext)
return
var/datum/job/tgt_job = GetJob(jobtitle)
if(!tgt_job)
return
if(!tgt_job.department_head[1])
return
var/boss_title = tgt_job.department_head[1]
var/obj/item/pda/target_pda
for(var/obj/item/pda/check_pda in GLOB.PDAs)
if(check_pda.ownrank == boss_title)
target_pda = check_pda
break
if(!target_pda)
return
var/datum/data/pda/app/messenger/PM = target_pda.find_program(/datum/data/pda/app/messenger)
if(PM && PM.can_receive())
PM.notify("<b>Automated Notification: </b>\"[antext]\" (Unable to Reply)")
/datum/controller/subsystem/jobs/proc/fetch_transfer_record_html(var/centcom)
var/record_html = "<TABLE border=\"1\">"
@@ -2,6 +2,7 @@ SUBSYSTEM_DEF(mob_hunt)
name = "Nano-Mob Hunter GO Server"
init_order = INIT_ORDER_NANOMOB
priority = FIRE_PRIORITY_NANOMOB // Low priority, no need for MC_TICK_CHECK due to extremely low performance impact.
flags = SS_NO_INIT
offline_implications = "Nano-Mob Hunter will no longer spawn mobs. No immediate action is needed."
var/max_normal_spawns = 15 //change this to adjust the number of normal spawns that can exist at one time. trapped spawns (from traitors) don't count towards this
var/list/normal_spawns = list()
@@ -3,6 +3,8 @@ PROCESSING_SUBSYSTEM_DEF(dcs)
flags = SS_NO_INIT
var/list/elements_by_type = list()
// Update this if you add in components which actually use this as a processor
offline_implications = "This SS doesnt actually process anything yet. No immediate action is needed."
/datum/controller/subsystem/processing/dcs/Recover()
comp_lookup = SSdcs.comp_lookup
@@ -4,3 +4,4 @@ PROCESSING_SUBSYSTEM_DEF(fastprocess)
name = "Fast Processing"
wait = 2
stat_tag = "FP"
offline_implications = "Objects using the 'Fast Processing' processor will no longer process. Shuttle call recommended."
@@ -3,3 +3,4 @@ PROCESSING_SUBSYSTEM_DEF(obj)
priority = FIRE_PRIORITY_OBJ
flags = SS_NO_INIT
wait = 20
offline_implications = "Objects using the 'Objects' processor will no longer process. Shuttle call recommended."
@@ -9,6 +9,7 @@ SUBSYSTEM_DEF(processing)
var/stat_tag = "P" //Used for logging
var/list/processing = list()
var/list/currentrun = list()
offline_implications = "Objects using the default processor will no longer process. Shuttle call recommended."
/datum/controller/subsystem/processing/stat_entry()
..("[stat_tag]:[processing.len]")
@@ -6,16 +6,23 @@ GLOBAL_REAL(SSmentor_tickets, /datum/controller/subsystem/tickets/mentor_tickets
/datum/controller/subsystem/tickets/mentor_tickets
name = "Mentor Tickets"
offline_implications = "Mentor tickets will no longer be marked as stale. No immediate action is needed."
ticket_system_name = "Mentor Tickets"
ticket_name = "Mentor Ticket"
span_class = "mentorhelp"
other_ticket_name = "Admin"
other_ticket_permission = R_ADMIN
close_rights = R_MENTOR | R_ADMIN
/datum/controller/subsystem/tickets/mentor_tickets/message_staff(var/msg)
message_mentorTicket(msg)
rights_needed = R_MENTOR | R_ADMIN | R_MOD
/datum/controller/subsystem/tickets/mentor_tickets/Initialize()
close_messages = list("<font color='red' size='3'><b>- [ticket_name] Closed -</b></font>",
"<span class='boldmessage'>Please try to be as descriptive as possible in mentor helps. Mentors do not know the full situation you're in and need more information to give you a helpful response.</span>",
"<span class='[span_class]'>Your [ticket_name] has now been closed.</span>")
return ..()
/datum/controller/subsystem/tickets/mentor_tickets/message_staff(msg)
message_mentorTicket(msg)
/datum/controller/subsystem/tickets/mentor_tickets/create_other_system_ticket(datum/ticket/T)
SStickets.newTicket(T.clientName, T.content, T.title)
+52 -10
View File
@@ -12,17 +12,23 @@
SUBSYSTEM_DEF(tickets)
name = "Admin Tickets"
init_order = INIT_ORDER_TICKETS
wait = 300
priority = FIRE_PRIORITY_TICKETS
offline_implications = "Admin tickets will no longer be marked as stale. No immediate action is needed."
flags = SS_BACKGROUND
var/span_class = "adminticket"
var/ticket_system_name = "Admin Tickets"
var/ticket_name = "Admin Ticket"
var/close_rights = R_ADMIN
var/rights_needed = R_ADMIN | R_MOD
/// The name of the other ticket type to convert to
var/other_ticket_name = "Mentor"
/// Which permission to look for when seeing if there is staff available for the other ticket type
var/other_ticket_permission = R_MENTOR
var/list/close_messages
init_order = INIT_ORDER_TICKETS
wait = 300
priority = FIRE_PRIORITY_TICKETS
flags = SS_BACKGROUND
var/list/allTickets = list() //make it here because someone might ahelp before the system has initialized
var/ticketCounter = 1
@@ -113,9 +119,37 @@ SUBSYSTEM_DEF(tickets)
to_chat_safe(returnClient(N), "<span class='[span_class]'>Your [ticket_name] has now been resolved.</span>")
return TRUE
/datum/controller/subsystem/tickets/proc/convert_to_other_ticket(ticketId)
if(!check_rights(rights_needed))
return
if(alert("Are you sure to convert this ticket to an '[other_ticket_name]' ticket?",,"Yes","No") != "Yes")
return
if(!other_ticket_system_staff_check())
return
var/datum/ticket/T = allTickets[ticketId]
convert_ticket(T)
/datum/controller/subsystem/tickets/proc/other_ticket_system_staff_check()
var/list/staff = staff_countup(other_ticket_permission)
if(!staff[1])
if(alert("No active staff online to answer the ticket. Are you sure you want to convert the ticket?",, "No", "Yes") != "Yes")
return FALSE
return TRUE
/datum/controller/subsystem/tickets/proc/convert_ticket(datum/ticket/T)
T.ticketState = TICKET_CLOSED
var/client/C = usr.client
to_chat_safe(T.clientName, list("<span class='[span_class]'>[key_name_hidden(C)] has converted your ticket to a [other_ticket_name] ticket.</span>",\
"<span class='[span_class]'>Be sure to use the correct type of help next time!</span>"))
message_staff("<span class='[span_class]'>[C] has converted ticket number [T.ticketNum] to a [other_ticket_name] ticket.</span>")
log_game("[C] has converted ticket number [T.ticketNum] to a [other_ticket_name] ticket.")
create_other_system_ticket(T)
/datum/controller/subsystem/tickets/proc/create_other_system_ticket(datum/ticket/T)
SSmentor_tickets.newTicket(T.clientName, T.content, T.title)
/datum/controller/subsystem/tickets/proc/autoRespond(N)
if(!check_rights(R_ADMIN|R_MOD))
if(!check_rights(rights_needed))
return
var/datum/ticket/T = allTickets[N]
@@ -130,6 +164,7 @@ SUBSYSTEM_DEF(tickets)
"Already Resolved" = "The problem has been resolved already.",
"Mentorhelp" = "Please redirect your question to Mentorhelp, as they are better experienced with these types of questions.",
"Happens Again" = "Thanks, let us know if it continues to happen.",
"Github Issue Report" = "To report a bug, please go to our <a href='[config.githuburl]'>Github page</a>. Then go to 'Issues'. Then 'New Issue'. Then fill out the report form. If the report would reveal current-round information, file it after the round ends.",
"Clear Cache" = "To fix a blank screen, go to the 'Special Verbs' tab and press 'Reload UI Resources'. If that fails, clear your BYOND cache (instructions provided with 'Reload UI Resources'). If that still fails, please adminhelp again, stating you have already done the following." ,
"IC Issue" = "This is an In Character (IC) issue and will not be handled by admins. You could speak to Security, Internal Affairs, a Departmental Head, Nanotrasen Representetive, or any other relevant authority currently on station.",
"Reject" = "Reject",
@@ -156,14 +191,17 @@ SUBSYSTEM_DEF(tickets)
resolveTicket(N)
message_staff("[C] has auto responded to [T.clientName]\'s adminhelp with:<span class='adminticketalt'> [message_key] </span>")
log_game("[C] has auto responded to [T.clientName]\'s adminhelp with: [response_phrases[message_key]]")
if("Mentorhelp")
convert_ticket(T)
else
var/msg_sound = sound('sound/effects/adminhelp.ogg')
SEND_SOUND(returnClient(N), msg_sound)
to_chat(returnClient(N), "<span class='[span_class]'>[key_name_hidden(C)] is autoresponding with: <span/> <span class='adminticketalt'>[response_phrases[message_key]]</span>")//for this we want the full value of whatever key this is to tell the player so we do response_phrases[message_key]
to_chat_safe(returnClient(N), "<span class='[span_class]'>[key_name_hidden(C)] is autoresponding with: <span/> <span class='adminticketalt'>[response_phrases[message_key]]</span>")//for this we want the full value of whatever key this is to tell the player so we do response_phrases[message_key]
message_staff("[C] has auto responded to [T.clientName]\'s adminhelp with:<span class='adminticketalt'> [message_key] </span>") //we want to use the short named keys for this instead of the full sentence which is why we just do message_key
T.lastStaffResponse = "Autoresponse: [message_key]"
resolveTicket(N)
log_game("[C] has auto responded to [T.clientName]\'s adminhelp with: [response_phrases[message_key]]")
//Set ticket state with key N to closed
/datum/controller/subsystem/tickets/proc/closeTicket(N)
var/datum/ticket/T = allTickets[N]
@@ -351,7 +389,7 @@ UI STUFF
dat += "<tr><td>[T.content[i]]</td></tr>"
dat += "</table><br /><br />"
dat += "<a href='?src=[UID()];detailreopen=[T.ticketNum]'>Re-Open</a>[check_rights(R_ADMIN|R_MOD, 0) ? "<a href='?src=[UID()];autorespond=[T.ticketNum]'>Auto</a>": ""]<a href='?src=[UID()];detailresolve=[T.ticketNum]'>Resolve</a><br /><br />"
dat += "<a href='?src=[UID()];detailreopen=[T.ticketNum]'>Re-Open</a>[check_rights(rights_needed, 0) ? "<a href='?src=[UID()];autorespond=[T.ticketNum]'>Auto</a>": ""]<a href='?src=[UID()];detailresolve=[T.ticketNum]'>Resolve</a><br /><br />"
if(!T.staffAssigned)
dat += "No staff member assigned to this [ticket_name] - <a href='?src=[UID()];assignstaff=[T.ticketNum]'>Take Ticket</a><br />"
@@ -366,6 +404,7 @@ UI STUFF
dat += "<br /><br />"
dat += "<a href='?src=[UID()];detailclose=[T.ticketNum]'>Close Ticket</a>"
dat += "<a href='?src=[UID()];convert_ticket=[T.ticketNum]'>Convert Ticket</a>"
var/datum/browser/popup = new(user, "[ticket_system_name]detail", "[ticket_system_name] #[T.ticketNum]", 1000, 600)
popup.set_content(dat)
@@ -448,7 +487,6 @@ UI STUFF
if(closeTicket(indexNum))
showDetailUI(usr, indexNum)
if(href_list["detailreopen"])
var/indexNum = text2num(href_list["detailreopen"])
if(openTicket(indexNum))
@@ -468,6 +506,10 @@ UI STUFF
var/indexNum = text2num(href_list["autorespond"])
autoRespond(indexNum)
if(href_list["convert_ticket"])
var/indexNum = text2num(href_list["convert_ticket"])
convert_to_other_ticket(indexNum)
if(href_list["resolveall"])
if(ticket_system_name == "Mentor Tickets")
usr.client.resolveAllMentorTickets()
+3 -2
View File
@@ -7,7 +7,7 @@
set name = "Restart Controller"
set desc = "Restart one of the various periodic loop controllers for the game (be careful!)"
if(!holder)
if(!check_rights(R_DEBUG))
return
switch(controller)
if("Master")
@@ -26,7 +26,8 @@
set name = "Debug Controller"
set desc = "Debug the various periodic loop controllers for the game (be careful!)"
if(!holder) return
if(!check_rights(R_DEBUG))
return
switch(controller)
if("failsafe")
debug_variables(Failsafe)
+1 -2
View File
@@ -131,6 +131,5 @@
/atom/proc/Beam(atom/BeamTarget,icon_state="b_beam",icon='icons/effects/beam.dmi',time=50, maxdistance=10,beam_type=/obj/effect/ebeam,beam_sleep_time=3)
var/datum/beam/newbeam = new(src,BeamTarget,icon,icon_state,time,maxdistance,beam_type,beam_sleep_time)
spawn(0)
newbeam.Start()
INVOKE_ASYNC(newbeam, /datum/beam.proc/Start)
return newbeam
+3 -2
View File
@@ -395,8 +395,9 @@ GLOBAL_LIST_INIT(advance_cures, list(
for(var/datum/disease/advance/AD in GLOB.active_diseases)
AD.Refresh()
for(var/mob/living/carbon/human/H in shuffle(GLOB.alive_mob_list))
if(!is_station_level(H.z))
for(var/thing in shuffle(GLOB.human_list))
var/mob/living/carbon/human/H = thing
if(H.stat == DEAD || !is_station_level(H.z))
continue
if(!H.HasDisease(D))
H.ForceContractDisease(D)
+3
View File
@@ -57,6 +57,9 @@
W.plane = initial(W.plane)
W.loc = affected_mob.loc
W.dropped(affected_mob)
if(isobj(affected_mob.loc))
var/obj/O = affected_mob.loc
O.force_eject_occupant()
var/mob/living/new_mob = new new_form(affected_mob.loc)
if(istype(new_mob))
new_mob.a_intent = "harm"
+24 -9
View File
@@ -8,10 +8,10 @@
/datum/log_record/New(_log_type, _who, _what, _target, _where, _raw_time)
log_type = _log_type
who = get_subject_text(_who)
who = get_subject_text(_who, _log_type)
what = _what
target = get_subject_text(_target)
target = get_subject_text(_target, _log_type)
if(!_where)
_where = get_turf(_who)
where = _where
@@ -19,16 +19,31 @@
_raw_time = world.time
raw_time = _raw_time
/datum/log_record/proc/get_subject_text(subject)
/datum/log_record/proc/get_subject_text(subject, log_type)
if(ismob(subject) || isclient(subject) || istype(subject, /datum/mind))
return key_name_admin(subject)
if(isatom(subject))
. = key_name_admin(subject)
if(should_log_health(log_type) && isliving(subject))
. += get_health_string(subject)
else if(isatom(subject))
var/atom/A = subject
return A.name
if(istype(subject, /datum))
. = A.name
else if(istype(subject, /datum))
var/datum/D = subject
return D.type
return subject
else
. = subject
/datum/log_record/proc/get_health_string(var/mob/living/L)
var/OX = L.getOxyLoss() > 50 ? "<b>[L.getOxyLoss()]</b>" : L.getOxyLoss()
var/TX = L.getToxLoss() > 50 ? "<b>[L.getToxLoss()]</b>" : L.getToxLoss()
var/BU = L.getFireLoss() > 50 ? "<b>[L.getFireLoss()]</b>" : L.getFireLoss()
var/BR = L.getBruteLoss() > 50 ? "<b>[L.getBruteLoss()]</b>" : L.getBruteLoss()
return " ([L.health]: <font color='deepskyblue'>[OX]</font> - <font color='green'>[TX]</font> - <font color='#FFA500'>[BU]</font> - <font color='red'>[BR]</font>)"
/datum/log_record/proc/should_log_health(log_type)
if(log_type == ATTACK_LOG || log_type == DEFENSE_LOG)
return TRUE
return FALSE
/proc/compare_log_record(datum/log_record/A, datum/log_record/B)
var/time_diff = A.raw_time - B.raw_time
+84 -17
View File
@@ -1,16 +1,23 @@
#define ALL_LOGS list(ATTACK_LOG, DEFENSE_LOG, CONVERSION_LOG, SAY_LOG, EMOTE_LOG, MISC_LOG)
#define UPDATE_CKEY_MOB(__ckey) var/mob/result = selected_ckeys_mobs[__ckey];\
if(!result || result.ckey != __ckey){\
result = get_mob_by_ckey(__ckey);\
selected_ckeys_mobs[__ckey] = result;\
}
/datum/log_viewer
var/time_from = 0
var/time_to = 4 HOURS // 4 Hours should be enough. INFINITY would screw the UI up
var/list/selected_mobs = list() // The mobs in question
var/list/selected_log_types = list() // The log types being searched for
var/list/selected_mobs = list() // The mobs in question.
var/list/selected_ckeys = list() // The ckeys selected to search for. Will show all mobs the ckey is attached to
var/list/mob/selected_ckeys_mobs = list()
var/list/selected_log_types = ALL_LOGS // The log types being searched for
var/list/log_records = list() // Found and sorted records
/datum/log_viewer/proc/clear_all()
selected_mobs.Cut()
selected_log_types.Cut()
selected_log_types = ALL_LOGS
selected_ckeys.Cut()
selected_ckeys_mobs.Cut()
time_from = initial(time_from)
time_to = initial(time_to)
log_records.Cut()
@@ -19,13 +26,17 @@
/datum/log_viewer/proc/search()
log_records.Cut() // Empty the old results
var/list/invalid_mobs = list()
var/list/ckeys = selected_ckeys.Copy()
for(var/i in selected_mobs)
var/mob/M = i
if(!M || QDELETED(M))
if(!M || QDELETED(M) || !M.last_known_ckey)
invalid_mobs |= M
continue
ckeys |= M.last_known_ckey
for(var/ckey in ckeys)
for(var/log_type in selected_log_types)
var/list/logs = M.logs[log_type]
var/list/logs = GLOB.logging.get_logs_by_type(ckey, log_type)
var/len_logs = length(logs)
if(len_logs)
var/start_index = get_earliest_log_index(logs)
@@ -91,9 +102,23 @@
return start
return 0
/datum/log_viewer/proc/add_mob(mob/user, mob/M)
/datum/log_viewer/proc/add_mobs(list/mob/mobs)
if(!mobs?.len)
return
for(var/i in mobs)
add_mob(usr, i, FALSE)
/datum/log_viewer/proc/add_ckey(mob/user, ckey)
if(!user || !user)
return
selected_ckeys |= ckey
UPDATE_CKEY_MOB(ckey)
show_ui(user)
/datum/log_viewer/proc/add_mob(mob/user, mob/M, show_the_ui = TRUE)
if(!M || !user)
return
selected_mobs |= M
show_ui(user)
@@ -103,8 +128,8 @@
var/trStyleTop = "border-top:2px solid; border-bottom:2px solid; padding-top: 5px; padding-bottom: 5px;"
var/trStyle = "border-top:1px solid; border-bottom:1px solid; padding-top: 5px; padding-bottom: 5px;"
var/dat
dat += "<head><style>.adminticket{border:2px solid} td{border:1px solid grey;} th{border:1px solid grey;} span{float:left;width:150px;}</style></head>"
dat += "<div style='height:15vh'>"
dat += "<head><meta http-equiv='X-UA-Compatible' content='IE=edge'><style>.adminticket{border:2px solid} td{border:1px solid grey;} th{border:1px solid grey;} span{float:left;width:150px;}</style></head>"
dat += "<div style='min-height:100px'>"
dat += "<span>Time Search Range:</span> <a href='?src=[UID()];start_time=1'>[gameTimestamp(wtime = time_from)]</a>"
dat += " To: <a href='?src=[UID()];end_time=1'>[gameTimestamp(wtime = time_to)]</a>"
dat += "<BR>"
@@ -115,20 +140,26 @@
if(QDELETED(M))
selected_mobs -= i
continue
dat += "<a href='?src=[UID()];remove_mob=\ref[M]'>[M.name]</a>"
dat += "<a href='?src=[UID()];remove_mob=\ref[M]'>[get_display_name(M)]</a>"
dat += "<a href='?src=[UID()];add_mob=1'>Add Mob</a>"
dat += "<a href='?src=[UID()];clear_mobs=1'>Clear All Mobs</a>"
dat += "<BR>"
dat += "<span>Ckeys being used:</span>"
for(var/ckey in selected_ckeys)
dat += "<a href='?src=[UID()];remove_ckey=[ckey]'>[get_ckey_name(ckey)]</a>"
dat += "<a href='?src=[UID()];add_ckey=1'>Add ckey</a>"
dat += "<a href='?src=[UID()];clear_ckeys=1'>Clear All ckeys</a>"
dat += "<BR>"
dat += "<span>Log Types:</span>"
for(var/i in all_log_types)
var/log_type = i
for(var/log_type in all_log_types)
var/enabled = (log_type in selected_log_types)
var/text
var/style
if(enabled)
text = "<b>[log_type]</b>"
style = "background: [get_logtype_color(i)]"
style = "background: [get_logtype_color(log_type)]"
else
text = log_type
@@ -142,9 +173,9 @@
// Search results
var/tdStyleTime = "width:80px; text-align:center;"
var/tdStyleType = "width:80px; text-align:center;"
var/tdStyleWho = "width:300px; text-align:center;"
var/tdStyleWho = "width:400px; text-align:center;"
var/tdStyleWhere = "width:150px; text-align:center;"
dat += "<div style='overflow-y: auto; max-height:76vh;'>"
dat += "<div style='overflow-y: auto; max-height:calc(100vh - 150px);'>"
dat += "<table style='width:100%; border: 1px solid;'>"
dat += "<tr style='[trStyleTop]'><th style='[tdStyleTime]'>When</th><th style='[tdStyleType]'>Type</th><th style='[tdStyleWho]'>Who</th><th>What</th><th>Target</th><th style='[tdStyleWhere]'>Where</th></tr>"
for(var/i in log_records)
@@ -158,7 +189,7 @@
dat += "</table>"
dat += "</div>"
var/datum/browser/popup = new(user, "Log viewer", "Log viewer", 1400, 600)
var/datum/browser/popup = new(user, "Log Viewer", "Log Viewer", 1500, 600)
popup.set_content(dat)
popup.open()
@@ -198,17 +229,31 @@
selected_mobs.Cut()
show_ui(usr)
return
if(href_list["clear_ckeys"])
selected_ckeys.Cut()
selected_ckeys_mobs.Cut()
show_ui(usr)
return
if(href_list["add_mob"])
var/list/mobs = getpois(TRUE, TRUE)
var/datum/async_input/A = input_autocomplete_async(usr, "Please, select a mob: ", mobs)
A.on_close(CALLBACK(src, .proc/add_mob, usr))
return
if(href_list["add_ckey"])
var/list/ckeys = GLOB.logging.get_ckeys_logged()
var/datum/async_input/A = input_autocomplete_async(usr, "Please, select a ckey: ", ckeys)
A.on_close(CALLBACK(src, .proc/add_ckey, usr))
return
if(href_list["remove_mob"])
var/mob/M = locate(href_list["remove_mob"])
if(M)
selected_mobs -= M
show_ui(usr)
return
if(href_list["remove_ckey"])
selected_ckeys -= href_list["remove_ckey"]
show_ui(usr)
return
if(href_list["toggle_log_type"])
var/log_type = href_list["toggle_log_type"]
if(log_type in selected_log_types)
@@ -232,4 +277,26 @@
return "deepskyblue"
if(MISC_LOG)
return "gray"
if(DEADCHAT_LOG)
return "#cc00c6"
if(OOC_LOG)
return "#002eb8"
if(LOOC_LOG)
return "#6699CC"
return "slategray"
/datum/log_viewer/proc/get_display_name(mob/M)
var/name = M.name
if(M.name != M.real_name)
name = "[name] ([M.real_name])"
if(isobserver(M))
name = "[name] (DEAD)"
return "\[[M.last_known_ckey]\] [name]"
/datum/log_viewer/proc/get_ckey_name(ckey)
UPDATE_CKEY_MOB(ckey)
var/mob/M = selected_ckeys_mobs[ckey]
return get_display_name(M)
#undef UPDATE_CKEY_MOB
+47
View File
@@ -0,0 +1,47 @@
/datum/logging
var/list/datum/log_record/logs = list() // Assoc list of assoc lists (ckey, (log_type, list/logs))
/datum/logging/proc/add_log(ckey, datum/log_record/log)
if(!ckey)
log_debug("GLOB.logging.add_log called with an invalid ckey")
return
if(!logs[ckey])
logs[ckey] = list()
var/list/log_types_list = logs[ckey]
if(!log_types_list[log.log_type])
log_types_list[log.log_type] = list()
var/list/datum/log_record/log_records = log_types_list[log.log_type]
log_records.Add(log)
/datum/logging/proc/get_ckeys_logged()
var/list/ckeys = list()
for(var/ckey in logs)
ckeys.Add(ckey)
return ckeys
/* Returns the logs of a given ckey and log_type
* If no logs exist it will return an empty list
*/
/datum/logging/proc/get_logs_by_type(ckey, log_type)
if(!ckey)
log_debug("GLOB.logging.get_logs_by_type called with an invalid ckey")
return
if(!log_type || !(log_type in ALL_LOGS))
log_debug("GLOB.logging.get_logs_by_type called with an invalid log_type '[log_type]'")
return
var/list/log_types_list = logs[ckey]
// Check if logs exist for the ckey
if(!length(log_types_list))
return list()
var/list/datum/log_record/log_records = log_types_list[log_type]
// Check if logs exist for this type
if(!log_records)
return list()
return log_records
+4 -11
View File
@@ -100,14 +100,6 @@
current.mind = null
leave_all_huds() //leave all the huds in the old body, so it won't get huds if somebody else enters it
for(var/log_type in current.logs) // Copy the old logs
var/list/logs = current.logs[log_type]
if(new_character.logs[log_type])
new_character.logs[log_type] += logs.Copy() // Append the old ones
new_character.logs[log_type] = sortTim(new_character.logs[log_type], /proc/compare_log_record) // Sort them on time
else
new_character.logs[log_type] = logs.Copy() // Just copy them
SSnanoui.user_transferred(current, new_character)
if(new_character.mind) //remove any mind currently in our new body's mind variable
@@ -1419,9 +1411,10 @@
return A
/datum/mind/proc/announce_objectives()
to_chat(current, "<span class='notice'>Your current objectives:</span>")
for(var/line in splittext(gen_objective_text(), "<br>"))
to_chat(current, line)
if(current)
to_chat(current, "<span class='notice'>Your current objectives:</span>")
for(var/line in splittext(gen_objective_text(), "<br>"))
to_chat(current, line)
/datum/mind/proc/find_syndicate_uplink()
var/list/L = current.get_contents()
+3 -2
View File
@@ -9,8 +9,9 @@
var/admin_notes
/datum/map_template/shuttle/New()
shuttle_id = "[port_id]_[suffix]"
mappath = "[prefix][shuttle_id].dmm"
if(port_id && suffix)
shuttle_id = "[port_id]_[suffix]"
mappath = "[prefix][shuttle_id].dmm"
. = ..()
/datum/map_template/shuttle/emergency
+5
View File
@@ -444,6 +444,11 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell))
return
// Normally, AoE spells will generate an attack log for every turf they loop over, while searching for targets.
// With this override, all /aoe_turf type spells will only generate 1 log, saying that the user has cast the spell.
/obj/effect/proc_holder/spell/aoe_turf/perform(list/targets, recharge, mob/user, make_attack_logs)
add_attack_logs(user, null, "Cast the AoE spell [name]", ATKLOG_ALL)
return ..(targets, recharge, user, FALSE)
/obj/effect/proc_holder/spell/targeted/proc/los_check(mob/A,mob/B)
//Checks for obstacles from A to B
-5
View File
@@ -13,11 +13,6 @@
action_icon_state = "knock"
sound = 'sound/magic/knock.ogg'
// Knock doesn't need to generate an attack log for every turf, set `make_attack_logs` to FALSE and just create a custom one.
/obj/effect/proc_holder/spell/aoe_turf/knock/perform(list/targets, recharge, mob/user)
add_attack_logs(user, user, "cast the spell [name]", ATKLOG_ALL)
return ..(targets, recharge, user, make_attack_logs = FALSE)
/obj/effect/proc_holder/spell/aoe_turf/knock/cast(list/targets, mob/user = usr)
for(var/turf/T in targets)
for(var/obj/machinery/door/door in T.contents)
+12 -3
View File
@@ -633,7 +633,6 @@ GLOBAL_LIST_INIT(all_supply_groups, list(SUPPLY_EMERGENCY,SUPPLY_SECURITY,SUPPLY
contains = list(/obj/machinery/power/emitter,
/obj/machinery/power/emitter)
cost = 10
containertype = /obj/structure/closet/crate/secure
containername = "emitter crate"
access = ACCESS_CE
containertype = /obj/structure/closet/crate/secure/engineering
@@ -717,7 +716,7 @@ GLOBAL_LIST_INIT(all_supply_groups, list(SUPPLY_EMERGENCY,SUPPLY_SECURITY,SUPPLY
name = "Supermatter Shard Crate"
contains = list(/obj/machinery/power/supermatter_shard)
cost = 50 //So cargo thinks twice before killing themselves with it
containertype = /obj/structure/closet/crate/secure
containertype = /obj/structure/closet/crate/secure/engineering
containername = "supermatter shard crate"
access = ACCESS_CE
@@ -728,7 +727,7 @@ GLOBAL_LIST_INIT(all_supply_groups, list(SUPPLY_EMERGENCY,SUPPLY_SECURITY,SUPPLY
/obj/item/pipe/circulator,
/obj/item/pipe/circulator)
cost = 25
containertype = /obj/structure/closet/crate/secure
containertype = /obj/structure/closet/crate/secure/engineering
containername = "thermo-electric generator crate"
access = ACCESS_CE
announce_beacons = list("Engineering" = list("Chief Engineer's Desk", "Atmospherics"))
@@ -1767,6 +1766,16 @@ GLOBAL_LIST_INIT(all_supply_groups, list(SUPPLY_EMERGENCY,SUPPLY_SECURITY,SUPPLY
cost = 20
containername = "polo supply crate"
/datum/supply_packs/misc/boxing //For non log spamming cargo brawls!
name = "Boxing Supply Crate"
// 4 boxing gloves
contains = list(/obj/item/clothing/gloves/boxing/blue,
/obj/item/clothing/gloves/boxing/green,
/obj/item/clothing/gloves/boxing/yellow,
/obj/item/clothing/gloves/boxing)
cost = 15
containername = "boxing supply crate"
///////////// Station Goals
/datum/supply_packs/misc/station_goal
+1 -1
View File
@@ -194,7 +194,7 @@ GLOBAL_LIST_INIT(wireColours, list("red", "blue", "green", "black", "orange", "b
return 1
/datum/wires/CanUseTopic(mob/user, datum/topic_state/state)
if(!CanUse(user))
if(!holder || !CanUse(user))
return STATUS_CLOSE
return ..()
+11 -11
View File
@@ -29,15 +29,15 @@ Actual Adjacent procs :
//////////////////////
//A* nodes variables
/PathNode
/datum/pathnode
var/turf/source //turf associated with the PathNode
var/PathNode/prevNode //link to the parent PathNode
var/datum/pathnode/prevNode //link to the parent PathNode
var/f //A* Node weight (f = g + h)
var/g //A* movement cost variable
var/h //A* heuristic variable
var/nt //count the number of Nodes traversed
/PathNode/New(s,p,pg,ph,pnt)
/datum/pathnode/New(s,p,pg,ph,pnt)
source = s
prevNode = p
g = pg
@@ -46,7 +46,7 @@ Actual Adjacent procs :
source.PNode = src
nt = pnt
/PathNode/proc/calc_f()
/datum/pathnode/proc/calc_f()
f = g + h
//////////////////////
@@ -54,11 +54,11 @@ Actual Adjacent procs :
//////////////////////
//the weighting function, used in the A* algorithm
/proc/PathWeightCompare(PathNode/a, PathNode/b)
/proc/PathWeightCompare(datum/pathnode/a, datum/pathnode/b)
return a.f - b.f
//reversed so that the Heap is a MinHeap rather than a MaxHeap
/proc/HeapPathWeightCompare(PathNode/a, PathNode/b)
/proc/HeapPathWeightCompare(datum/pathnode/a, datum/pathnode/b)
return b.f - a.f
//wrapper that returns an empty list if A* failed to find a path
@@ -82,13 +82,13 @@ Actual Adjacent procs :
return 0
maxnodedepth = maxnodes //no need to consider path longer than maxnodes
var/Heap/open = new /Heap(/proc/HeapPathWeightCompare) //the open list
var/datum/heap/open = new /datum/heap(/proc/HeapPathWeightCompare) //the open list
var/list/closed = new() //the closed list
var/list/path = null //the returned path, if any
var/PathNode/cur //current processed turf
var/datum/pathnode/cur //current processed turf
//initialization
open.Insert(new /PathNode(start,null,0,call(start,dist)(end),0))
open.Insert(new /datum/pathnode(start,null,0,call(start,dist)(end),0))
//then run the main loop
while(!open.IsEmpty() && !path)
@@ -125,7 +125,7 @@ Actual Adjacent procs :
var/newg = cur.g + call(cur.source,dist)(T)
if(!T.PNode) //is not already in open list, so add it
open.Insert(new /PathNode(T,cur,newg,call(T,dist)(end),cur.nt+1))
open.Insert(new /datum/pathnode(T,cur,newg,call(T,dist)(end),cur.nt+1))
else //is already in open list, check if it's a better way from the current turf
if(newg < T.PNode.g)
T.PNode.prevNode = cur
@@ -137,7 +137,7 @@ Actual Adjacent procs :
}
//cleaning after us
for(var/PathNode/PN in open.L)
for(var/datum/pathnode/PN in open.L)
PN.source.PNode = null
for(var/turf/T in closed)
T.PNode = null
+13 -4
View File
@@ -61,7 +61,7 @@
/atom/movable/proc/get_cell()
return
/atom/movable/proc/start_pulling(atom/movable/AM, state, force = move_force, supress_message = FALSE)
/atom/movable/proc/start_pulling(atom/movable/AM, state, force = pull_force, show_message = FALSE)
if(QDELETED(AM))
return FALSE
if(!(AM.can_be_pulled(src, state, force)))
@@ -87,7 +87,7 @@
if(ismob(AM))
var/mob/M = AM
add_attack_logs(src, M, "passively grabbed", ATKLOG_ALMOSTALL)
if(!supress_message)
if(show_message)
visible_message("<span class='warning'>[src] has grabbed [M] passively!</span>")
return TRUE
@@ -120,12 +120,18 @@
if(pulledby && moving_diagonally != FIRST_DIAG_STEP && get_dist(src, pulledby) > 1) //separated from our puller and not in the middle of a diagonal move.
pulledby.stop_pulling()
/atom/movable/proc/can_be_pulled(user, grab_state, force)
/atom/movable/proc/can_be_pulled(user, grab_state, force, show_message = FALSE)
if(src == user || !isturf(loc))
return FALSE
if(anchored || throwing)
if(anchored || move_resist == INFINITY)
if(show_message)
to_chat(user, "<span class='warning'>[src] appears to be anchored to the ground!</span>")
return FALSE
if(throwing)
return FALSE
if(force < (move_resist * MOVE_FORCE_PULL_RATIO))
if(show_message)
to_chat(user, "<span class='warning'>[src] is too heavy to pull!</span>")
return FALSE
return TRUE
@@ -567,3 +573,6 @@
/atom/movable/proc/portal_destroyed(obj/effect/portal/P)
return
/atom/movable/proc/decompile_act(obj/item/matter_decompiler/C, mob/user) // For drones to decompile mobs and objs. See drone for an example.
return FALSE
+3
View File
@@ -282,6 +282,9 @@
occupant = null
icon_state = "scanner_open"
/obj/machinery/dna_scannernew/force_eject_occupant()
go_out(null, TRUE)
/obj/machinery/dna_scannernew/ex_act(severity)
if(occupant)
occupant.ex_act(severity)
+1 -1
View File
@@ -18,7 +18,7 @@
intercepttext += "<BR>Note in the event of a quarantine breach or uncontrolled spread of the biohazard, the directive 7-10 may be upgraded to a directive 7-12.<BR>"
intercepttext += "Message ends."
if(2)
var/nukecode = rand(10000, 99999)
var/nukecode = "[rand(10000, 99999)]"
for(var/obj/machinery/nuclearbomb/bomb in GLOB.machines)
if(bomb && bomb.r_code)
if(is_station_level(bomb.z))
+10 -2
View File
@@ -104,10 +104,18 @@
var/mob/C = null
var/list/candidates = list()
if(!new_overmind)
// Create icon
var/mutable_appearance/MA = new
var/mutable_appearance/MA1 = new(icon, "blob")
MA1.color = overmind ? overmind.blob_reagent_datum.color : color
MA.overlays += MA1
var/mutable_appearance/MA2 = new(icon, "blob_core_overlay")
MA.overlays += MA2
// sendit
if(is_offspring)
candidates = pollCandidates("Do you want to play as a blob offspring?", ROLE_BLOB, 1)
candidates = SSghost_spawns.poll_candidates("Do you want to play as a blob offspring?", ROLE_BLOB, TRUE, source = MA)
else
candidates = pollCandidates("Do you want to play as a blob?", ROLE_BLOB, 1)
candidates = SSghost_spawns.poll_candidates("Do you want to play as a blob?", ROLE_BLOB, TRUE, source = MA)
if(length(candidates))
C = pick(candidates)
+1 -1
View File
@@ -237,7 +237,7 @@
blobber.AIStatus = AI_OFF
blobber.LoseTarget()
spawn()
var/list/candidates = pollCandidates("Do you want to play as a blobbernaut?", ROLE_BLOB, 1, 100)
var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a blobbernaut?", ROLE_BLOB, TRUE, 10 SECONDS, source = blobber)
if(candidates.len)
var/mob/C = pick(candidates)
if(C)
+4 -2
View File
@@ -20,8 +20,8 @@ GLOBAL_LIST_EMPTY(all_cults)
var/mob/living/carbon/human/H = mind.current
if(ismindshielded(H)) //mindshield protects against conversions unless removed
return FALSE
// if(mind.offstation_role) cant convert offstation roles such as ghost spawns
// return FALSE Commented out until we can figure out why offstation_role is getting set to TRUE on normal crew
if(mind.offstation_role)
return FALSE
if(issilicon(mind.current))
return FALSE //can't convert machines, that's ratvar's thing
if(isguardian(mind.current))
@@ -231,6 +231,8 @@ GLOBAL_LIST_EMPTY(all_cults)
/datum/game_mode/cult/proc/get_unconvertables()
var/list/ucs = list()
for(var/mob/living/carbon/human/player in GLOB.player_list)
if(player.mind && player.mind.offstation_role)
continue
if(!is_convertable_to_cult(player.mind))
ucs += player.mind
return ucs
+1 -1
View File
@@ -4,7 +4,7 @@
var/cultist_name = "talisman"
var/cultist_desc = "A basic talisman. It serves no purpose."
var/invocation = "Naise meam!"
info = "<center><img src='talisman.png'></center><br/><br/>"
info = "<center>&ZeroWidthSpace;<img src='talisman.png'></center><br/><br/>"
var/uses = 1
var/health_cost = 0 //The amount of health taken from the user when invoking the talisman
+8 -5
View File
@@ -291,7 +291,8 @@
///////////////////////////////////
/datum/game_mode/proc/get_living_heads()
. = list()
for(var/mob/living/carbon/human/player in GLOB.mob_list)
for(var/thing in GLOB.human_list)
var/mob/living/carbon/human/player = thing
var/list/real_command_positions = GLOB.command_positions.Copy() - "Nanotrasen Representative"
if(player.stat != DEAD && player.mind && (player.mind.assigned_role in real_command_positions))
. |= player.mind
@@ -312,7 +313,8 @@
//////////////////////////////////////////////
/datum/game_mode/proc/get_living_sec()
. = list()
for(var/mob/living/carbon/human/player in GLOB.mob_list)
for(var/thing in GLOB.human_list)
var/mob/living/carbon/human/player = thing
if(player.stat != DEAD && player.mind && (player.mind.assigned_role in GLOB.security_positions))
. |= player.mind
@@ -321,7 +323,8 @@
////////////////////////////////////////
/datum/game_mode/proc/get_all_sec()
. = list()
for(var/mob/living/carbon/human/player in GLOB.mob_list)
for(var/thing in GLOB.human_list)
var/mob/living/carbon/human/player = thing
if(player.mind && (player.mind.assigned_role in GLOB.security_positions))
. |= player.mind
@@ -421,9 +424,9 @@ proc/display_roundstart_logout_report()
return nukecode
/datum/game_mode/proc/replace_jobbanned_player(mob/living/M, role_type)
var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as a [role_type]?", role_type, 0, 100)
var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as a [role_type]?", role_type, FALSE, 10 SECONDS)
var/mob/dead/observer/theghost = null
if(candidates.len)
if(length(candidates))
theghost = pick(candidates)
to_chat(M, "<span class='userdanger'>Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!</span>")
message_admins("[key_name_admin(theghost)] has taken control of ([key_name_admin(M)]) to replace a jobbanned player.")
@@ -83,6 +83,36 @@
else
add_ranged_ability(user, enable_text)
/datum/action/innate/ai/choose_modules
name = "Choose Modules"
desc = "Spend your processing time to gain a variety of different abilities."
button_icon_state = "choose_module"
auto_use_uses = FALSE // This is an infinite ability.
/datum/action/innate/ai/choose_modules/Grant(mob/living/L)
. = ..()
owner_AI.malf_picker = new /datum/module_picker
/datum/action/innate/ai/choose_modules/Trigger()
. = ..()
owner_AI.malf_picker.use(owner_AI)
/datum/action/innate/ai/return_to_core
name = "Return to Main Core"
desc = "Leave the APC you are shunted to, and return to your core."
icon_icon = 'icons/obj/power.dmi'
button_icon_state = "apcemag"
auto_use_uses = FALSE // Here just to prevent the "You have X uses remaining" from popping up.
/datum/action/innate/ai/return_to_core/Trigger()
. = ..()
var/obj/machinery/power/apc/apc = owner_AI.loc
if(!istype(apc)) // This shouldn't happen but here for safety.
to_chat(src, "<span class='notice'>You are already in your Main Core.</span>")
return
apc.malfvacate()
qdel(src)
//The datum and interface for the malf unlock menu, which lets them choose actions to unlock.
/datum/module_picker
var/temp
@@ -96,13 +126,7 @@
if((AM.power_type && AM.power_type != /datum/action/innate/ai) || AM.upgrade)
possible_modules += AM
/datum/module_picker/proc/remove_malf_verbs(mob/living/silicon/ai/AI) //Removes all malfunction-related abilities from the target AI.
for(var/datum/AI_Module/AM in possible_modules)
for(var/datum/action/A in AI.actions)
if(istype(A, initial(AM.power_type)))
qdel(A)
/datum/module_picker/proc/use(user as mob)
/datum/module_picker/proc/use(mob/user)
var/dat
dat += {"<B>Select use of processing time: (currently #[processing_time] left.)</B><BR>
<HR>
@@ -394,7 +394,7 @@
to_chat(src, "<font color='blue'> You are feeling far too docile to do that.</font>")
return
var content = ""
var/content = ""
content += "<table>"
@@ -246,7 +246,7 @@
src.verbs -= /mob/living/proc/guardian_reset
for(var/mob/living/simple_animal/hostile/guardian/G in GLOB.mob_list)
if(G.summoner == src)
var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as [G.real_name]?", ROLE_GUARDIAN, 0, 100)
var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as [G.real_name]?", ROLE_GUARDIAN, FALSE, 10 SECONDS, source = G)
var/mob/dead/observer/new_stand = null
if(candidates.len)
new_stand = pick(candidates)
@@ -312,7 +312,7 @@
used = FALSE
return
to_chat(user, "[use_message]")
var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as the [mob_name] of [user.real_name]?", ROLE_GUARDIAN, 0, 100)
var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as the [mob_name] of [user.real_name]?", ROLE_GUARDIAN, FALSE, 10 SECONDS, source = /mob/living/simple_animal/hostile/guardian)
var/mob/dead/observer/theghost = null
if(candidates.len)
@@ -1,7 +1,7 @@
/obj/item/projectile/guardian
name = "crystal spray"
icon_state = "guardian"
damage = 5
damage = 25
damage_type = BRUTE
armour_penetration = 100
@@ -11,7 +11,7 @@
melee_damage_upper = 10
damage_transfer = 0.9
projectiletype = /obj/item/projectile/guardian
ranged_cooldown_time = 1 //fast!
ranged_cooldown_time = 5 //fast!
projectilesound = 'sound/effects/hit_on_shattered_glass.ogg'
ranged = 1
range = 13
@@ -3,7 +3,7 @@
/datum/event/spawn_morph/proc/get_morph()
spawn()
var/list/candidates = pollCandidates("Do you want to play as a morph?", ROLE_MORPH, 1)
var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a morph?", ROLE_MORPH, TRUE, source = /mob/living/simple_animal/hostile/morph)
if(!candidates.len)
key_of_morph = null
return kill()
@@ -16,6 +16,7 @@
var/icon_stun = "revenant_stun"
var/icon_drain = "revenant_draining"
incorporeal_move = 3
see_invisible = INVISIBILITY_REVENANT
invisibility = INVISIBILITY_REVENANT
health = INFINITY //Revenants don't use health, they use essence instead
maxHealth = INFINITY
@@ -140,7 +141,7 @@
giveObjectivesandGoals()
giveSpells()
else
var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as a revenant?", poll_time = 15 SECONDS)
var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as a revenant?", poll_time = 15 SECONDS, source = /mob/living/simple_animal/revenant)
var/mob/dead/observer/theghost = null
if(candidates.len)
theghost = pick(candidates)
@@ -396,7 +397,7 @@
spawn()
if(!key_of_revenant)
message_admins("The new revenant's old client either could not be found or is in a new, living mob - grabbing a random candidate instead...")
var/list/candidates = pollCandidates("Do you want to play as a revenant?", ROLE_REVENANT, 1)
var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a revenant?", ROLE_REVENANT, TRUE, source = /mob/living/simple_animal/revenant)
if(!candidates.len)
qdel(R)
message_admins("No candidates were found for the new revenant. Oh well!")
@@ -13,7 +13,7 @@
return
spawn()
var/list/candidates = pollCandidates("Do you want to play as a revenant?", ROLE_REVENANT, 1)
var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a revenant?", ROLE_REVENANT, TRUE, source = /mob/living/simple_animal/revenant)
if(!candidates.len)
key_of_revenant = null
return kill()
@@ -157,7 +157,7 @@
/mob/living/simple_animal/slaughter/cult/New()
..()
spawn(5)
var/list/demon_candidates = pollCandidates("Do you want to play as a slaughter demon?", ROLE_DEMON, 1, 100)
var/list/demon_candidates = SSghost_spawns.poll_candidates("Do you want to play as a slaughter demon?", ROLE_DEMON, TRUE, 10 SECONDS, source = /mob/living/simple_animal/slaughter/cult)
if(!demon_candidates.len)
visible_message("<span class='warning'>[src] disappears in a flash of red light!</span>")
qdel(src)
+1 -2
View File
@@ -53,7 +53,6 @@ proc/issyndicate(mob/living/M as mob)
for(var/datum/mind/synd_mind in syndicates)
synd_mind.assigned_role = SPECIAL_ROLE_NUKEOPS //So they aren't chosen for other jobs.
synd_mind.special_role = SPECIAL_ROLE_NUKEOPS
synd_mind.offstation_role = TRUE
return 1
@@ -113,7 +112,7 @@ proc/issyndicate(mob/living/M as mob)
if(spawnpos > synd_spawn.len)
spawnpos = 2
synd_mind.current.loc = synd_spawn[spawnpos]
synd_mind.offstation_role = TRUE
forge_syndicate_objectives(synd_mind)
create_syndicate(synd_mind)
greet_syndicate(synd_mind)
+2 -1
View File
@@ -357,7 +357,8 @@
var/list/name_counts = list()
var/list/names = list()
for(var/mob/living/carbon/human/H in GLOB.mob_list)
for(var/thing in GLOB.human_list)
var/mob/living/carbon/human/H = thing
if(!trackable(H))
continue
+4 -2
View File
@@ -385,7 +385,8 @@
if(foecount == GLOB.score_arrested)
GLOB.score_allarrested = 1
for(var/mob/living/carbon/human/player in GLOB.mob_living_list)
for(var/thing in GLOB.human_list)
var/mob/living/carbon/human/player = thing
if(player.mind)
var/role = player.mind.assigned_role
if(role in list("Captain", "Head of Security", "Head of Personnel", "Chief Engineer", "Research Director"))
@@ -415,7 +416,8 @@
for(var/datum/mind/M in SSticker.mode:revolutionaries)
if(M.current && M.current.stat != DEAD)
revcount++
for(var/mob/living/carbon/human/player in GLOB.mob_living_list)
for(var/thing in GLOB.human_list)
var/mob/living/carbon/human/player = thing
if(player.mind)
var/role = player.mind.assigned_role
if(role in list("Captain", "Head of Security", "Head of Personnel", "Chief Engineer", "Research Director"))
+4 -2
View File
@@ -41,7 +41,8 @@
GLOB.score_deadaipenalty++
GLOB.score_deadcrew++
for(var/mob/living/carbon/human/I in GLOB.mob_list)
for(var/thing in GLOB.human_list)
var/mob/living/carbon/human/I = thing
if(I.stat == DEAD && is_station_level(I.z))
GLOB.score_deadcrew++
@@ -60,7 +61,8 @@
var/dmg_score = 0
if(SSshuttle.emergency.mode >= SHUTTLE_ENDGAME)
for(var/mob/living/carbon/human/E in GLOB.mob_list)
for(var/thing in GLOB.human_list)
var/mob/living/carbon/human/E = thing
cash_score = 0
dmg_score = 0
var/turf/location = get_turf(E.loc)
+1 -1
View File
@@ -74,7 +74,7 @@ Made by Xhuis
required_enemies = 2
recommended_enemies = 2
restricted_jobs = list("AI", "Cyborg")
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Security Pod Pilot", "Magistrate", "Brig Physician", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer")
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Head of Personnel", "Captain", "Blueshield", "Nanotrasen Representative", "Security Pod Pilot", "Magistrate", "Brig Physician", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer")
/datum/game_mode/shadowling/announce()
to_chat(world, "<b>The current game mode is - Shadowling!</b>")
@@ -145,7 +145,7 @@
/obj/effect/proc_holder/spell/vampire/self/rejuvenate
name = "Rejuvenate"
desc= "Flush your system with spare blood to remove any incapacitating effects."
desc= "Use reserve blood to enliven your body, removing any incapacitating effects."
action_icon_state = "vampire_rejuvinate"
charge_max = 200
stat_allowed = 1
@@ -158,7 +158,7 @@
user.SetParalysis(0)
user.SetSleeping(0)
U.adjustStaminaLoss(-75)
to_chat(user, "<span class='notice'>You flush your system with clean blood and remove any incapacitating effects.</span>")
to_chat(user, "<span class='notice'>You instill your body with clean blood and remove any incapacitating effects.</span>")
spawn(1)
if(usr.mind.vampire.get_ability(/datum/vampire_passive/regen))
for(var/i = 1 to 5)
@@ -354,10 +354,10 @@
var/datum/objective/protect/serve_objective = new
serve_objective.owner = user.mind
serve_objective.target = H.mind
serve_objective.explanation_text = "You have been Enthralled by [user]. Follow [user.p_their()] every command."
serve_objective.explanation_text = "You have been Enthralled by [user.real_name]. Follow [user.p_their()] every command."
H.mind.objectives += serve_objective
to_chat(H, "<span class='biggerdanger'>You have been Enthralled by [user]. Follow [user.p_their()] every command.</span>")
to_chat(H, "<span class='biggerdanger'>You have been Enthralled by [user.real_name]. Follow [user.p_their()] every command.</span>")
to_chat(user, "<span class='warning'>You have successfully Enthralled [H]. <i>If [H.p_they()] refuse[H.p_s()] to do as you say just adminhelp.</i></span>")
H.Stun(2)
add_attack_logs(user, H, "Vampire-thralled")
+7 -4
View File
@@ -50,7 +50,8 @@
to_chat(H, "You already used this contract!")
return
used = 1
var/list/candidates = pollCandidates("Do you want to play as the wizard apprentice of [H.real_name]?", ROLE_WIZARD, 1)
var/mutable_appearance/source = new('icons/obj/cardboard_cutout.dmi', "cutout_wizard")
var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as the wizard apprentice of [H.real_name]?", ROLE_WIZARD, TRUE, source = source)
if(candidates.len)
var/mob/C = pick(candidates)
new /obj/effect/particle_effect/smoke(H.loc)
@@ -307,7 +308,8 @@ GLOBAL_LIST_EMPTY(multiverse)
if(M.assigned == assigned)
M.cooldown = cooldown
var/list/candidates = pollCandidates("Do you want to play as the wizard apprentice of [user.real_name]?", ROLE_WIZARD, 1, 100)
var/mutable_appearance/source = new('icons/obj/cardboard_cutout.dmi', "cutout_wizard")
var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as the wizard apprentice of [user.real_name]?", ROLE_WIZARD, TRUE, 10 SECONDS, source = source)
if(candidates.len)
var/mob/C = pick(candidates)
spawn_copy(C.client, get_turf(user.loc), user)
@@ -866,8 +868,9 @@ GLOBAL_LIST_EMPTY(multiverse)
possible = list()
if(!link)
return
for(var/mob/living/carbon/human/H in GLOB.alive_mob_list)
if(md5(H.dna.uni_identity) in link.fingerprints)
for(var/thing in GLOB.human_list)
var/mob/living/carbon/human/H = thing
if(H.stat != DEAD && (md5(H.dna.uni_identity) in link.fingerprints))
possible |= H
/obj/item/voodoo/proc/GiveHint(mob/victim,force=0)
+2 -1
View File
@@ -118,7 +118,8 @@
return FALSE
making_mage = TRUE
var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as a raging Space Wizard?", ROLE_WIZARD, TRUE, poll_time = 20 SECONDS)
var/mutable_appearance/source = new('icons/obj/cardboard_cutout.dmi', "cutout_wizard")
var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as a raging Space Wizard?", ROLE_WIZARD, TRUE, poll_time = 20 SECONDS, source = source)
var/mob/dead/observer/harry = null
message_admins("SWF is still pissed, sending another wizard - [max_mages - mages_made] left.")
+1 -1
View File
@@ -363,7 +363,7 @@
break
if(!chosen_ghost) //Failing that, we grab a ghost
var/list/consenting_candidates = pollCandidates("Would you like to play as a Shade?", ROLE_CULTIST, FALSE, poll_time = 100)
var/list/consenting_candidates = SSghost_spawns.poll_candidates("Would you like to play as a Shade?", ROLE_CULTIST, FALSE, poll_time = 10 SECONDS, source = /mob/living/simple_animal/shade)
if(consenting_candidates.len)
chosen_ghost = pick(consenting_candidates)
if(!T)
+1 -1
View File
@@ -610,7 +610,7 @@
var/entry_types = subtypesof(/datum/spellbook_entry) - /datum/spellbook_entry/item - /datum/spellbook_entry/summon - /datum/spellbook_entry/loadout
for(var/T in entry_types)
var/datum/spellbook_entry/E = new T
if(GAMEMODE_IS_WIZARD && E.is_ragin_restricted)
if(GAMEMODE_IS_RAGIN_MAGES && E.is_ragin_restricted)
qdel(E)
continue
entries |= E
+1 -1
View File
@@ -32,7 +32,7 @@
minimal_player_age = 21
exp_requirements = 300
exp_type = EXP_TYPE_CREW
alt_titles = list("Android", "Robot")
alt_titles = list("Robot")
/datum/job/cyborg/equip(mob/living/carbon/human/H)
if(!H)
+10 -12
View File
@@ -224,19 +224,17 @@ GLOBAL_LIST_INIT(role_playtime_requirements, list(
else
return "none"
/proc/update_exp(var/mins, var/ann = 0)
if(!establish_db_connection())
return -1
spawn(0)
for(var/client/L in GLOB.clients)
if(L.inactivity >= (10 MINUTES))
continue
spawn(0)
L.update_exp_client(mins, ann)
sleep(10)
/proc/update_exp(mins = 0, ann = 0)
if(!GLOB.dbcon.IsConnected())
return
for(var/client/L in GLOB.clients)
if(L.inactivity >= (10 MINUTES))
continue
L.update_exp_client(mins, ann)
CHECK_TICK
/client/proc/update_exp_client(var/minutes, var/announce_changes = 0)
if(!src ||!ckey)
/client/proc/update_exp_client(minutes = 0, announce_changes = 0)
if(!src || !ckey || !GLOB.dbcon.IsConnected())
return
var/DBQuery/exp_read = GLOB.dbcon.NewQuery("SELECT exp FROM [format_table_name("player")] WHERE ckey='[ckey]'")
if(!exp_read.Execute())
+3
View File
@@ -411,6 +411,9 @@
for(var/atom/movable/A in contents - component_parts - list(beaker))
A.forceMove(loc)
/obj/machinery/sleeper/force_eject_occupant()
go_out()
/obj/machinery/sleeper/proc/inject_chemical(mob/living/user as mob, chemical, amount)
if(!(chemical in possible_chems))
to_chat(user, "<span class='notice'>The sleeper does not offer that chemical!</span>")
+3
View File
@@ -172,6 +172,9 @@
for(var/atom/movable/A in contents - component_parts)
A.forceMove(loc)
/obj/machinery/bodyscanner/force_eject_occupant()
go_out()
/obj/machinery/bodyscanner/ex_act(severity)
if(occupant)
occupant.ex_act(severity)
+24 -10
View File
@@ -390,7 +390,7 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
if(!job_in_department(SSjobs.GetJob(t1)))
return 0
if(t1 == "Custom")
var/temp_t = sanitize(copytext(input("Enter a custom job assignment.","Assignment"),1,MAX_MESSAGE_LEN))
var/temp_t = sanitize(reject_bad_name(copytext(input("Enter a custom job assignment.", "Assignment"), 1, MAX_MESSAGE_LEN), TRUE))
//let custom jobs function as an impromptu alt title, mainly for sechuds
if(temp_t && modify)
SSjobs.log_job_transfer(modify.registered_name, modify.getRankAndAssignment(), temp_t, scan.registered_name)
@@ -419,7 +419,8 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
message_admins("[key_name_admin(usr)] has reassigned \"[modify.registered_name]\" from \"[jobnamedata]\" to \"[t1]\".")
SSjobs.log_job_transfer(modify.registered_name, jobnamedata, t1, scan.registered_name)
SSjobs.slot_job_transfer(modify.rank, t1)
if(modify.owner_uid)
SSjobs.slot_job_transfer(modify.rank, t1)
var/mob/living/carbon/human/H = modify.getPlayer()
if(istype(H))
@@ -436,7 +437,7 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
if(is_authenticated(usr) && !target_dept)
var/t2 = modify
if((modify == t2 && (in_range(src, usr) || (istype(usr, /mob/living/silicon))) && istype(loc, /turf)))
var/temp_name = reject_bad_name(href_list["reg"])
var/temp_name = reject_bad_name(href_list["reg"], TRUE)
if(temp_name)
modify.registered_name = temp_name
else
@@ -465,6 +466,8 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
if(is_authenticated(usr) && !target_dept)
var/delcount = SSjobs.delete_log_records(scan.registered_name, TRUE)
if(delcount)
message_admins("[key_name_admin(usr)] has wiped all ID computer logs.")
usr.create_log(MISC_LOG, "wiped all ID computer logs.")
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
SSnanoui.update_uis(src)
@@ -504,9 +507,16 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
if("terminate")
if(is_authenticated(usr) && !target_dept)
var/jobnamedata = modify.getRankAndAssignment()
log_game("[key_name(usr)] has terminated the employment of \"[modify.registered_name]\" the \"[jobnamedata]\".")
message_admins("[key_name_admin(usr)] has terminated the employment of \"[modify.registered_name]\" the \"[jobnamedata]\".")
var/reason = sanitize(copytext(input("Enter legal reason for termination. Enter nothing to cancel.", "Employment Termination"), 1, MAX_MESSAGE_LEN))
if(!reason || !is_authenticated(usr) || !modify)
return FALSE
var/m_ckey = modify.getPlayerCkey()
var/m_ckey_text = m_ckey ? "([m_ckey])" : "(no ckey)"
log_game("[key_name(usr)] has terminated the employment of \"[modify.registered_name]\" [m_ckey_text] the \"[jobnamedata]\" for: \"[reason]\".")
message_admins("[key_name_admin(usr)] has terminated the employment of \"[modify.registered_name]\" [m_ckey_text] the \"[jobnamedata]\" for: \"[reason]\".")
usr.create_log(MISC_LOG, "terminated the employment of \"[modify.registered_name]\" [m_ckey_text] the \"[jobnamedata]\"")
SSjobs.log_job_transfer(modify.registered_name, jobnamedata, "Terminated", scan.registered_name)
SSjobs.notify_dept_head(modify.rank, "[scan.registered_name] has terminated the employment of \"[modify.registered_name]\" the \"[jobnamedata]\" for \"[reason]\".")
modify.assignment = "Terminated"
modify.access = list()
@@ -518,16 +528,20 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
if(!job_in_department(SSjobs.GetJob(modify.rank), FALSE))
visible_message("<span class='notice'>[src]: Heads may only demote members of their own department.</span>")
return 0
var/reason = sanitize(copytext(input("Enter legal reason for demotion. Enter nothing to cancel.","Legal Demotion"),1,MAX_MESSAGE_LEN))
if(!reason || !is_authenticated(usr) || !modify)
return 0
var/list/access = list()
var/datum/job/jobdatum = new /datum/job/civilian
access = jobdatum.get_access()
var/jobnamedata = modify.getRankAndAssignment()
log_game("[key_name(usr)] has demoted \"[modify.registered_name]\" the \"[jobnamedata]\" to \"Civilian (Demoted)\".")
message_admins("[key_name_admin(usr)] has demoted \"[modify.registered_name]\" the \"[jobnamedata]\" to \"Civilian (Demoted)\".")
var/m_ckey = modify.getPlayerCkey()
var/m_ckey_text = m_ckey ? "([m_ckey])" : "(no ckey)"
log_game("[key_name(usr)] has demoted \"[modify.registered_name]\" the \"[jobnamedata]\" [m_ckey_text] to \"Civilian (Demoted)\" for: \"[reason]\".")
message_admins("[key_name_admin(usr)] has demoted \"[modify.registered_name]\" the \"[jobnamedata]\" [m_ckey_text] to \"Civilian (Demoted)\" for: \"[reason]\".")
usr.create_log(MISC_LOG, "demoted \"[modify.registered_name]\" [m_ckey_text] the \"[jobnamedata]\"")
SSjobs.log_job_transfer(modify.registered_name, jobnamedata, "Demoted", scan.registered_name)
SSjobs.notify_dept_head(modify.rank, "[scan.registered_name] has demoted \"[modify.registered_name]\" the \"[jobnamedata]\" for \"[reason]\".")
modify.access = access
modify.rank = "Civilian"
modify.assignment = "Demoted"

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