Merge remote-tracking branch 'origin/master' into TGUI-3.0

This commit is contained in:
Artur
2020-06-09 11:07:39 +03:00
177 changed files with 249863 additions and 2481 deletions
+74 -63
View File
@@ -23380,7 +23380,7 @@
/obj/structure/extinguisher_cabinet{
pixel_y = -32
},
/obj/item/paper/guides/cogstation/letter_sec,
/obj/item/paper/fluff/cogstation/secsolars,
/turf/open/floor/plasteel,
/area/security/main)
"aYJ" = (
@@ -27342,13 +27342,16 @@
/turf/open/floor/plasteel,
/area/security/courtroom)
"bhA" = (
/obj/machinery/disposal/bin,
/obj/effect/turf_decal/delivery,
/obj/structure/disposalpipe/trunk{
dir = 4
/obj/item/stack/cable_coil/red,
/obj/item/multitool,
/obj/item/wrench,
/obj/item/stack/sheet/metal/five{
pixel_x = 6;
pixel_y = -6
},
/turf/open/floor/plasteel,
/area/quartermaster/sorting)
/obj/item/paper/fluff/cogstation/letter_arrd,
/turf/open/floor/plating,
/area/maintenance/disposal)
"bhB" = (
/obj/structure/table/wood,
/obj/item/stack/packageWrap,
@@ -28343,6 +28346,7 @@
/obj/effect/turf_decal/tile/yellow{
dir = 4
},
/obj/item/paper/guides/cogstation/letter_atmos,
/turf/open/floor/plasteel/dark/corner{
dir = 1
},
@@ -34940,15 +34944,19 @@
},
/area/maintenance/disposal)
"bxz" = (
/obj/item/stack/cable_coil/red,
/obj/item/multitool,
/obj/item/wrench,
/obj/item/stack/sheet/metal/five{
pixel_x = 6;
pixel_y = -6
/obj/effect/turf_decal/tile/yellow{
dir = 4
},
/turf/open/floor/plating,
/area/maintenance/disposal)
/obj/machinery/computer/station_alert,
/obj/structure/sign/poster/official/safety_eye_protection{
pixel_y = 32
},
/obj/effect/turf_decal/tile/red{
dir = 1
},
/obj/item/paper/guides/cogstation/letter_eng,
/turf/open/floor/plasteel,
/area/engine/break_room)
"bxA" = (
/obj/machinery/announcement_system,
/turf/open/floor/plasteel/dark,
@@ -43875,14 +43883,19 @@
/turf/open/floor/plasteel/dark,
/area/science/robotics/lab)
"bQq" = (
/obj/machinery/atmospherics/components/unary/vent_pump/on,
/obj/structure/cable{
icon_state = "4-8"
/obj/effect/turf_decal/tile/yellow{
dir = 1
},
/obj/effect/landmark/event_spawn,
/obj/machinery/atmospherics/pipe/simple/orange/hidden{
dir = 8
/obj/effect/turf_decal/tile/yellow,
/obj/structure/table/wood,
/obj/machinery/airalarm{
pixel_y = 24
},
/obj/item/modular_computer/laptop/preset/civilian,
/obj/effect/turf_decal/tile/blue{
dir = 4
},
/obj/item/paper/guides/cogstation/letter_atmos,
/turf/open/floor/plasteel,
/area/engine/break_room)
"bQr" = (
@@ -51737,27 +51750,17 @@
/turf/open/floor/plasteel,
/area/engine/atmos)
"cfU" = (
/obj/structure/table,
/obj/effect/turf_decal/tile/purple{
dir = 4
/obj/machinery/atmospherics/components/unary/vent_pump/on,
/obj/structure/cable{
icon_state = "4-8"
},
/obj/effect/turf_decal/tile/purple{
/obj/effect/landmark/event_spawn,
/obj/machinery/atmospherics/pipe/simple/orange/hidden{
dir = 8
},
/obj/effect/turf_decal/tile/yellow{
dir = 1
},
/obj/effect/turf_decal/tile/yellow,
/obj/item/clothing/gloves/color/latex,
/obj/item/book/manual/wiki/cit/chem_recipies,
/obj/item/book/manual/wiki/cit/chemistry{
pixel_x = 2;
pixel_y = -3
},
/obj/item/reagent_containers/dropper,
/obj/machinery/atmospherics/pipe/manifold/orange/hidden,
/turf/open/floor/plasteel/white,
/area/medical/chemistry)
/obj/item/beacon,
/turf/open/floor/plasteel,
/area/engine/break_room)
"cfV" = (
/obj/machinery/door/airlock/external{
name = "Atmospherics External Airlock";
@@ -57026,18 +57029,18 @@
/turf/open/floor/plasteel,
/area/engine/break_room)
"cqt" = (
/obj/effect/turf_decal/tile/yellow{
/obj/machinery/disposal/bin,
/obj/effect/turf_decal/delivery,
/obj/structure/disposalpipe/trunk{
dir = 4
},
/obj/machinery/computer/station_alert,
/obj/structure/sign/poster/official/safety_eye_protection{
pixel_y = 32
},
/obj/effect/turf_decal/tile/red{
dir = 1
/obj/structure/noticeboard/qm{
dir = 4;
icon_state = "nboard00";
pixel_x = -32
},
/turf/open/floor/plasteel,
/area/engine/break_room)
/area/quartermaster/sorting)
"cqu" = (
/obj/effect/turf_decal/tile/yellow{
dir = 4
@@ -57106,20 +57109,28 @@
/turf/open/floor/plasteel,
/area/engine/break_room)
"cqz" = (
/obj/structure/table,
/obj/effect/turf_decal/tile/purple{
dir = 4
},
/obj/effect/turf_decal/tile/purple{
dir = 8
},
/obj/effect/turf_decal/tile/yellow{
dir = 1
},
/obj/effect/turf_decal/tile/yellow,
/obj/structure/table/wood,
/obj/machinery/airalarm{
pixel_y = 24
/obj/item/clothing/gloves/color/latex,
/obj/item/book/manual/wiki/cit/chem_recipies,
/obj/item/book/manual/wiki/cit/chemistry{
pixel_x = 2;
pixel_y = -3
},
/obj/item/modular_computer/laptop/preset/civilian,
/obj/effect/turf_decal/tile/blue{
dir = 4
},
/turf/open/floor/plasteel,
/area/engine/break_room)
/obj/machinery/atmospherics/pipe/manifold/orange/hidden,
/obj/item/paper/fluff/cogstation/chemists,
/obj/item/reagent_containers/dropper,
/turf/open/floor/plasteel/white,
/area/medical/chemistry)
"cqA" = (
/obj/structure/cable{
icon_state = "1-2"
@@ -96465,7 +96476,7 @@ bBQ
csV
bIe
bYB
cfU
cqz
bIM
cvt
cmS
@@ -109285,7 +109296,7 @@ asR
aSK
aSr
aeI
cqt
bxz
bwP
cBn
cBH
@@ -110315,7 +110326,7 @@ aUf
aeI
bnp
cmO
bQq
cfU
cmm
cri
cdH
@@ -111598,7 +111609,7 @@ aRR
cpj
aSr
aeI
cqz
bQq
cqI
bQM
cdl
@@ -114945,7 +114956,7 @@ bbV
bjf
bkh
blu
bbV
ciI
ccZ
cgs
cxo
@@ -116742,7 +116753,7 @@ cPH
bfU
bFD
bgO
bhA
cqt
bUQ
cvj
cDg
@@ -117990,7 +118001,7 @@ bwb
aMs
cAb
cAl
bxz
bhA
bgP
bzO
caD
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+224
View File
@@ -0,0 +1,224 @@
//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
"a" = (
/turf/template_noop,
/area/template_noop)
"b" = (
/turf/closed/wall/mineral/titanium,
/area/shuttle/arrival)
"c" = (
/obj/machinery/door/airlock/titanium{
name = "Arrivals Shuttle Airlock"
},
/obj/structure/fans/tiny,
/turf/open/floor/mineral/titanium/blue,
/area/shuttle/arrival)
"d" = (
/obj/effect/spawner/structure/window/shuttle,
/turf/open/floor/plating,
/area/shuttle/arrival)
"e" = (
/obj/structure/closet/wardrobe/black,
/turf/open/floor/mineral/titanium/blue,
/area/shuttle/arrival)
"f" = (
/turf/open/floor/mineral/titanium/blue,
/area/shuttle/arrival)
"g" = (
/obj/machinery/computer/arcade,
/turf/open/floor/mineral/titanium/blue,
/area/shuttle/arrival)
"h" = (
/obj/structure/closet/wardrobe/grey,
/obj/machinery/light/small{
dir = 1
},
/turf/open/floor/mineral/titanium/blue,
/area/shuttle/arrival)
"i" = (
/obj/structure/closet/wardrobe/green,
/turf/open/floor/mineral/titanium/blue,
/area/shuttle/arrival)
"j" = (
/obj/machinery/requests_console{
department = "Arrival shuttle";
name = "Arrivals Shuttle console";
pixel_y = 30
},
/obj/machinery/light{
dir = 1
},
/turf/open/floor/mineral/titanium/blue,
/area/shuttle/arrival)
"k" = (
/obj/structure/shuttle/engine/heater{
dir = 4
},
/obj/structure/window/reinforced{
dir = 8
},
/turf/open/floor/plating/airless,
/area/shuttle/arrival)
"m" = (
/obj/machinery/light{
dir = 1
},
/obj/structure/closet/wardrobe/mixed,
/turf/open/floor/mineral/titanium/blue,
/area/shuttle/arrival)
"n" = (
/obj/docking_port/mobile/arrivals{
name = "Snaxi Arrivals Shuttle"
},
/obj/structure/shuttle/engine/propulsion/burst/right{
dir = 4
},
/turf/open/floor/plating/airless,
/area/shuttle/arrival)
"o" = (
/obj/structure/table,
/obj/item/storage/firstaid/regular,
/obj/machinery/light/small,
/turf/open/floor/mineral/titanium/blue,
/area/shuttle/arrival)
"p" = (
/obj/structure/table,
/obj/item/storage/toolbox/emergency,
/turf/open/floor/mineral/titanium/blue,
/area/shuttle/arrival)
"q" = (
/obj/machinery/light,
/turf/open/floor/mineral/titanium/blue,
/area/shuttle/arrival)
"r" = (
/obj/structure/chair/comfy/shuttle{
dir = 8
},
/turf/open/floor/mineral/titanium,
/area/shuttle/arrival)
"s" = (
/obj/item/radio/intercom{
name = "Station Intercom (General)";
pixel_y = -29
},
/obj/machinery/light,
/turf/open/floor/mineral/titanium/blue,
/area/shuttle/arrival)
"x" = (
/obj/structure/closet/emcloset,
/turf/open/floor/mineral/titanium/blue,
/area/shuttle/arrival)
"z" = (
/obj/structure/shuttle/engine/propulsion/burst/right{
dir = 4
},
/turf/open/floor/plating/airless,
/area/shuttle/arrival)
(1,1,1) = {"
a
b
d
d
d
b
a
"}
(2,1,1) = {"
b
b
r
r
r
b
b
"}
(3,1,1) = {"
b
h
f
f
f
o
b
"}
(4,1,1) = {"
b
i
r
r
r
p
b
"}
(5,1,1) = {"
b
e
f
f
f
x
b
"}
(6,1,1) = {"
b
m
r
r
r
q
b
"}
(7,1,1) = {"
d
g
f
f
f
f
d
"}
(8,1,1) = {"
c
f
r
r
r
f
c
"}
(9,1,1) = {"
c
f
f
f
f
f
c
"}
(10,1,1) = {"
b
j
r
r
r
s
b
"}
(11,1,1) = {"
b
k
k
k
k
k
b
"}
(12,1,1) = {"
b
z
z
n
z
z
b
"}
+39 -13
View File
@@ -342,11 +342,6 @@
},
/turf/open/floor/plasteel/dark,
/area/shuttle/syndicate/eva)
"aN" = (
/obj/machinery/portable_atmospherics/canister/oxygen,
/obj/effect/turf_decal/bot_white,
/turf/open/floor/plasteel/dark,
/area/shuttle/syndicate/eva)
"aO" = (
/obj/structure/chair/comfy/shuttle{
dir = 4;
@@ -385,8 +380,9 @@
/turf/open/floor/mineral/plastitanium,
/area/shuttle/syndicate/eva)
"aS" = (
/obj/structure/tank_dispenser/oxygen,
/obj/effect/turf_decal/bot_white,
/obj/vehicle/ridden/atv/snowmobile,
/obj/item/key,
/turf/open/floor/plasteel/dark,
/area/shuttle/syndicate/eva)
"aT" = (
@@ -1172,15 +1168,37 @@
},
/turf/closed/wall/r_wall/syndicate,
/area/shuttle/syndicate/armory)
"gZ" = (
/obj/effect/turf_decal/bot_white,
/obj/vehicle/ridden/atv/snowmobile,
/obj/item/key,
/turf/open/floor/plasteel/dark,
/area/shuttle/syndicate/airlock)
"pd" = (
/obj/machinery/porta_turret/syndicate/shuttle{
dir = 5
},
/turf/closed/wall/r_wall/syndicate,
/area/shuttle/syndicate/bridge)
"vv" = (
/obj/structure/reagent_dispensers/fueltank,
"qk" = (
/obj/effect/turf_decal/tile/neutral{
dir = 1
},
/obj/effect/turf_decal/tile/neutral,
/obj/effect/turf_decal/tile/neutral{
dir = 4
},
/obj/effect/turf_decal/tile/neutral{
dir = 8
},
/obj/effect/turf_decal/bot_white,
/obj/vehicle/ridden/atv/snowmobile,
/obj/item/key,
/turf/open/floor/plasteel/dark,
/area/shuttle/syndicate/airlock)
"vv" = (
/obj/effect/turf_decal/bot_white,
/obj/structure/tank_dispenser/oxygen,
/turf/open/floor/plasteel/dark,
/area/shuttle/syndicate/eva)
"In" = (
@@ -1195,6 +1213,14 @@
},
/turf/closed/wall/r_wall/syndicate,
/area/shuttle/syndicate/medical)
"Sz" = (
/obj/effect/turf_decal/tile/neutral,
/obj/effect/turf_decal/tile/neutral{
dir = 8
},
/obj/structure/reagent_dispensers/fueltank,
/turf/open/floor/plasteel/dark,
/area/shuttle/syndicate/armory)
(1,1,1) = {"
aa
@@ -1330,7 +1356,7 @@ ac
ad
aa
ax
aN
aS
aS
aU
aZ
@@ -1480,7 +1506,7 @@ ac
In
aa
aB
aO
gZ
aO
aX
bc
@@ -1505,7 +1531,7 @@ ac
aa
aa
aB
aP
qk
aP
aP
aP
@@ -1530,7 +1556,7 @@ aa
aa
aa
aB
aP
qk
aP
aP
aP
@@ -1587,7 +1613,7 @@ bd
aB
bm
bB
bG
Sz
bQ
bX
ci
+116
View File
@@ -0,0 +1,116 @@
//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
"a" = (
/turf/closed/wall/mineral/titanium,
/area/shuttle/snowtaxi)
"b" = (
/obj/structure/shuttle/engine/propulsion/burst{
dir = 4
},
/obj/structure/window/reinforced{
dir = 8
},
/obj/structure/fans/tiny/invisible,
/turf/open/floor/plating,
/area/shuttle/snowtaxi)
"c" = (
/obj/machinery/door/airlock/titanium{
name = "Mining Shuttle Airlock"
},
/obj/effect/mapping_helpers/airlock/cyclelink_helper,
/obj/structure/fans/tiny,
/turf/open/floor/plating,
/area/shuttle/snowtaxi)
"e" = (
/obj/effect/spawner/structure/window/shuttle,
/obj/structure/fans/tiny/invisible,
/turf/open/floor/plating,
/area/shuttle/snowtaxi)
"f" = (
/obj/machinery/light{
dir = 1
},
/obj/structure/chair/comfy/shuttle{
dir = 8
},
/turf/open/floor/mineral/titanium,
/area/shuttle/snowtaxi)
"g" = (
/obj/machinery/door/airlock/titanium{
name = "Mining Shuttle Airlock"
},
/obj/docking_port/mobile{
dwidth = 3;
height = 4;
id = "snow_taxi";
name = "snow taxi";
port_direction = 8;
width = 6
},
/obj/effect/mapping_helpers/airlock/cyclelink_helper{
dir = 1
},
/obj/structure/fans/tiny,
/turf/open/floor/plating,
/area/shuttle/snowtaxi)
"i" = (
/obj/machinery/light,
/obj/machinery/computer/shuttle/snow_taxi{
dir = 4
},
/turf/open/floor/mineral/titanium,
/area/shuttle/snowtaxi)
"j" = (
/obj/structure/chair/comfy/shuttle{
dir = 1
},
/turf/open/floor/mineral/titanium,
/area/shuttle/snowtaxi)
"k" = (
/turf/open/floor/mineral/titanium,
/area/shuttle/snowtaxi)
"l" = (
/obj/effect/spawner/structure/window/shuttle,
/obj/structure/fans/tiny/invisible,
/turf/open/floor/plating/airless,
/area/shuttle/snowtaxi)
"u" = (
/obj/structure/chair/comfy/shuttle,
/turf/open/floor/mineral/titanium,
/area/shuttle/snowtaxi)
(1,1,1) = {"
a
e
e
a
"}
(2,1,1) = {"
a
f
i
a
"}
(3,1,1) = {"
e
u
j
l
"}
(4,1,1) = {"
c
k
k
g
"}
(5,1,1) = {"
a
u
j
a
"}
(6,1,1) = {"
a
b
b
a
"}
+1
View File
@@ -0,0 +1 @@
#define FORCE_MAP "_maps/snaxi.json"
+49
View File
@@ -0,0 +1,49 @@
{
"map_name":"Snow Taxi",
"map_path":"map_files/Snaxi",
"map_file":[
"IcemoonUnderground_Below.dmm",
"IcemoonUnderground_Above.dmm",
"Snaxi.dmm"
],
"station_ruin_budget":0,
"space_ruin_levels":0,
"space_empty_levels":0,
"shuttles":{
"cargo":"cargo_box",
"ferry":"ferry_fancy",
"whiteship":"whiteship_box",
"emergency":"emergency_box"
},
"traits":[
{
"Up":1,
"Station":0,
"Mining":true,
"Linkage":null,
"Gravity":true,
"Ice Ruins Underground":true,
"Baseturf":"/turf/open/lava/plasma/ice_moon"
},
{
"Down":-1,
"Up":1,
"Station":0,
"Mining":true,
"Linkage":null,
"Gravity":true,
"Ice Ruins Underground":true,
"Baseturf":"/turf/open/openspace/icemoon"
},
{
"Down":-1,
"Mining":true,
"Linkage":null,
"Gravity":true,
"Ice Ruins":true,
"Weather_Snowstorm":true,
"Baseturf":"/turf/open/floor/plating/asteroid/snow/icemoon"
}
],
"minetype":"none"
}
+30 -4
View File
@@ -61,10 +61,18 @@
#define CHECK_STAMCRIT(mob) ((mob.combat_flags & COMBAT_FLAG_HARD_STAMCRIT)? HARD_STAMCRIT : ((mob.combat_flags & COMBAT_FLAG_SOFT_STAMCRIT)? SOFT_STAMCRIT : NOT_STAMCRIT))
//stamina stuff
#define STAMINA_SOFTCRIT 100 //softcrit for stamina damage. prevents standing up, prevents performing actions that cost stamina, etc, but doesn't force a rest or stop movement
#define STAMINA_CRIT 140 //crit for stamina damage. forces a rest, and stops movement until stamina goes back to stamina softcrit
#define STAMINA_SOFTCRIT_TRADITIONAL 0 //same as STAMINA_SOFTCRIT except for the more traditional health calculations
#define STAMINA_CRIT_TRADITIONAL -40 //ditto, but for STAMINA_CRIT
///Threshold over which attacks start being hindered.
#define STAMINA_NEAR_SOFTCRIT 90
///softcrit for stamina damage. prevents standing up, some actions that cost stamina, etc, but doesn't force a rest or stop movement
#define STAMINA_SOFTCRIT 100
///sanity cap to prevent stamina actions (that are still performable) from sending you into crit.
#define STAMINA_NEAR_CRIT 130
///crit for stamina damage. forces a rest, and stops movement until stamina goes back to stamina softcrit
#define STAMINA_CRIT 140
///same as STAMINA_SOFTCRIT except for the more traditional health calculations
#define STAMINA_SOFTCRIT_TRADITIONAL 0
///ditto, but for STAMINA_CRIT
#define STAMINA_CRIT_TRADITIONAL -40
#define CRAWLUNDER_DELAY 30 //Delay for crawling under a standing mob
@@ -182,6 +190,9 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list(
#define EGUN_SELFCHARGE 1
#define EGUN_SELFCHARGE_BORG 2
///Time to spend without clicking on other things required for your shots to become accurate.
#define GUN_AIMING_TIME (2 SECONDS)
//Object/Item sharpness
#define IS_BLUNT 0
#define IS_SHARP 1
@@ -249,6 +260,21 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list(
#define STAM_COST_W_CLASS_MULT 1.25
#define STAM_COST_THROW_MULT 2
///Multiplier of the (STAMINA_NEAR_CRIT - user current stamina loss) : (STAMINA_NEAR_CRIT - STAMINA_SOFTCRIT) ratio used in damage penalties when stam soft-critted.
#define STAM_CRIT_ITEM_ATTACK_PENALTY 0.66
/// changeNext_move penalty multiplier of the above.
#define STAM_CRIT_ITEM_ATTACK_DELAY 1.75
/// Damage penalty when fighting prone.
#define LYING_DAMAGE_PENALTY 0.5
/// Added delay when firing guns stam-softcritted. Summed with a hardset CLICK_CD_RANGE delay, similar to STAM_CRIT_DAMAGE_DELAY otherwise.
#define STAM_CRIT_GUN_DELAY 2.75
/**
* should the current-attack-damage be lower than the item force multiplied by this value,
* a "inefficiently" prefix will be added to the message.
*/
#define INEFFICIENT_ATTACK_MSG_THRESHOLD 0.7
//bullet_act() return values
#define BULLET_ACT_HIT "HIT" //It's a successful hit, whatever that means in the context of the thing it's hitting.
+25
View File
@@ -88,3 +88,28 @@
#define EMISSIVE_BLOCK_GENERIC 1
/// Uses a dedicated render_target object to copy the entire appearance in real time to the blocking layer. For things that can change in appearance a lot from the base state, like humans.
#define EMISSIVE_BLOCK_UNIQUE 2
/// Returns the red part of a #RRGGBB hex sequence as number
#define GETREDPART(hexa) hex2num(copytext(hexa, 2, 4))
/// Returns the green part of a #RRGGBB hex sequence as number
#define GETGREENPART(hexa) hex2num(copytext(hexa, 4, 6))
/// Returns the blue part of a #RRGGBB hex sequence as number
#define GETBLUEPART(hexa) hex2num(copytext(hexa, 6, 8))
/// Parse the hexadecimal color into lumcounts of each perspective.
#define PARSE_LIGHT_COLOR(source) \
do { \
if (source.light_color) { \
var/__light_color = source.light_color; \
source.lum_r = GETREDPART(__light_color) / 255; \
source.lum_g = GETGREENPART(__light_color) / 255; \
source.lum_b = GETBLUEPART(__light_color) / 255; \
} else { \
source.lum_r = 1; \
source.lum_g = 1; \
source.lum_b = 1; \
}; \
} while (FALSE)
+22 -11
View File
@@ -18,18 +18,29 @@
// Flags for the item_flags var on /obj/item
#define BEING_REMOVED (1<<0)
#define IN_INVENTORY (1<<1) //is this item equipped into an inventory slot or hand of a mob? used for tooltips
#define FORCE_STRING_OVERRIDE (1<<2) //used for tooltips
#define NEEDS_PERMIT (1<<3) //Used by security bots to determine if this item is safe for public use.
///is this item equipped into an inventory slot or hand of a mob? used for tooltips
#define IN_INVENTORY (1<<1)
///used for tooltips
#define FORCE_STRING_OVERRIDE (1<<2)
///Used by security bots to determine if this item is safe for public use.
#define NEEDS_PERMIT (1<<3)
#define SLOWS_WHILE_IN_HAND (1<<4)
#define NO_MAT_REDEMPTION (1<<5) //Stops you from putting things like an RCD or other items into an ORM or protolathe for materials.
#define DROPDEL (1<<6) //When dropped, it calls qdel on itself
#define NOBLUDGEON (1<<7) //when an item has this it produces no "X has been hit by Y with Z" message in the default attackby()
#define ABSTRACT (1<<8) //for all things that are technically items but used for various different stuff
#define IMMUTABLE_SLOW (1<<9) //When players should not be able to change the slowdown of the item (Speed potions, ect)
#define SURGICAL_TOOL (1<<10) //Tool commonly used for surgery: won't attack targets in an active surgical operation on help intent (in case of mistakes)
#define NO_UNIFORM_REQUIRED (1<<11) //Can be worn on certain slots (currently belt and id) that would otherwise require an uniform.
#define NO_ATTACK_CHAIN_SOFT_STAMCRIT (1<<12) //Entirely blocks melee_attack_chain() if user is soft stamcritted. Uses getStaminaLoss() to check at this point in time. THIS DOES NOT BLOCK RANGED AFTERATTACK()S, ONLY MELEE RANGE AFTERATTACK()S.
///Stops you from putting things like an RCD or other items into an ORM or protolathe for materials.
#define NO_MAT_REDEMPTION (1<<5)
///When dropped, it calls qdel on itself
#define DROPDEL (1<<6)
///when an item has this it produces no "X has been hit by Y with Z" message in the default attackby()
#define NOBLUDGEON (1<<7)
///for all things that are technically items but used for various different stuff
#define ABSTRACT (1<<8)
///When players should not be able to change the slowdown of the item (Speed potions, ect)
#define IMMUTABLE_SLOW (1<<9)
///Tool commonly used for surgery: won't attack targets in an active surgical operation on help intent (in case of mistakes)
#define SURGICAL_TOOL (1<<10)
///Can be worn on certain slots (currently belt and id) that would otherwise require an uniform.
#define NO_UNIFORM_REQUIRED (1<<11)
///Damage when attacking people is not affected by combat mode.
#define NO_COMBAT_MODE_FORCE_MODIFIER (1<<12)
// Flags for the clothing_flags var on /obj/item/clothing
+2 -1
View File
@@ -36,4 +36,5 @@ GLOBAL_LIST_INIT(default_weight_class_to_volume, list(
#define STORAGE_VOLUME_BACKPACK (DEFAULT_VOLUME_NORMAL * 7)
#define STORAGE_VOLUME_DUFFLEBAG (DEFAULT_VOLUME_NORMAL * 10)
#define STORAGE_VOLUME_BAG_OF_HOLDING (DEFAULT_VOLUME_NORMAL * 20)
#define STORAGE_VOLUME_CHEMISTRY_BAG (DEFAULT_VOLUME_TINY * 50)
#define STORAGE_VOLUME_PILL_BOTTLE (DEFAULT_VOLUME_TINY * 7)
+1
View File
@@ -18,6 +18,7 @@
#define VALENTINES "Valentine's Day"
#define APRIL_FOOLS "April Fool's Day"
#define EASTER "Easter"
#define PRIDE_MONTH "Pride Month"
#define HALLOWEEN "Halloween"
#define CHRISTMAS "Christmas"
#define FESTIVE_SEASON "Festive Season"
+1
View File
@@ -170,6 +170,7 @@ GLOBAL_LIST_INIT(typecache_powerfailure_safe_areas, typecacheof(/area/engine/eng
newA.setup(str)
newA.set_dynamic_lighting()
newA.has_gravity = oldA.has_gravity
newA.noteleport = oldA.noteleport
else
newA = area_choice
-16
View File
@@ -565,22 +565,6 @@
var/obj/machinery/announcement_system/announcer = pick(GLOB.announcement_systems)
announcer.announce("ARRIVAL", character.real_name, rank, list()) //make the list empty to make it announce it in common
/proc/GetHexColors(const/hexa)
return list(
GetRedPart(hexa)/ 255,
GetGreenPart(hexa)/ 255,
GetBluePart(hexa)/ 255
)
/proc/GetRedPart(const/hexa)
return hex2num(copytext(hexa, 2, 4))
/proc/GetGreenPart(const/hexa)
return hex2num(copytext(hexa, 4, 6))
/proc/GetBluePart(const/hexa)
return hex2num(copytext(hexa, 6, 8))
/proc/lavaland_equipment_pressure_check(turf/T)
. = FALSE
if(!istype(T))
+1
View File
@@ -216,6 +216,7 @@
"ipc_screen" = snowflake_ipc_antenna_list ? pick(snowflake_ipc_antenna_list) : "None",
"ipc_antenna" = "None",
"flavor_text" = "",
"silicon_flavor_text" = "",
"meat_type" = "Mammalian",
"body_model" = body_model,
"body_size" = RESIZE_DEFAULT_SIZE
+1
View File
@@ -21,6 +21,7 @@
next_move = world.time + ((num+next_move_adjust)*next_move_modifier)
/mob/living/changeNext_move(num)
last_click_move = next_move
var/mod = next_move_modifier
var/adj = next_move_adjust
for(var/i in status_effects)
+56 -30
View File
@@ -10,10 +10,6 @@
/obj/item/proc/melee_attack_chain(mob/user, atom/target, params)
if(isliving(user))
var/mob/living/L = user
if(item_flags & NO_ATTACK_CHAIN_SOFT_STAMCRIT)
if(IS_STAMCRIT(L))
to_chat(L, "<span class='warning'>You are too exhausted to swing [src]!</span>")
return
if(!CHECK_MOBILITY(L, MOBILITY_USE))
to_chat(L, "<span class='warning'>You are unable to swing [src] right now!</span>")
return
@@ -59,8 +55,10 @@
/mob/living/attackby(obj/item/I, mob/living/user, params)
if(..())
return TRUE
user.changeNext_move(CLICK_CD_MELEE)
return I.attack(src, user)
I.attack_delay_done = FALSE //Should be set TRUE in pre_attacked_by()
. = I.attack(src, user)
if(!I.attack_delay_done) //Otherwise, pre_attacked_by() should handle it.
user.changeNext_move(I.click_delay)
/obj/item/proc/attack(mob/living/M, mob/living/user)
if(SEND_SIGNAL(src, COMSIG_ITEM_ATTACK, M, user) & COMPONENT_ITEM_NO_ATTACK)
@@ -68,11 +66,6 @@
SEND_SIGNAL(user, COMSIG_MOB_ITEM_ATTACK, M, user)
if(item_flags & NOBLUDGEON)
return
if(IS_STAMCRIT(user)) // CIT CHANGE - makes it impossible to attack in stamina softcrit
to_chat(user, "<span class='warning'>You're too exhausted.</span>") // CIT CHANGE - ditto
return // CIT CHANGE - ditto
if(force && damtype != STAMINA && HAS_TRAIT(user, TRAIT_PACIFISM))
to_chat(user, "<span class='warning'>You don't want to harm other living beings!</span>")
return
@@ -91,7 +84,9 @@
log_combat(user, M, "attacked", src.name, "(INTENT: [uppertext(user.a_intent)]) (DAMTYPE: [uppertext(damtype)])")
add_fingerprint(user)
user.adjustStaminaLossBuffered(getweight(user, STAM_COST_ATTACK_MOB_MULT))//CIT CHANGE - makes attacking things cause stamina loss
var/weight = getweight(user, STAM_COST_ATTACK_MOB_MULT) //CIT CHANGE - makes attacking things cause stamina loss
if(weight)
user.adjustStaminaLossBuffered(weight)
//the equivalent of the standard version of attack() but for object targets.
/obj/item/proc/attack_obj(obj/O, mob/living/user)
@@ -99,13 +94,12 @@
return
if(item_flags & NOBLUDGEON)
return
if(IS_STAMCRIT(user)) // CIT CHANGE - makes it impossible to attack in stamina softcrit
to_chat(user, "<span class='warning'>You're too exhausted.</span>") // CIT CHANGE - ditto
return // CIT CHANGE - ditto
user.adjustStaminaLossBuffered(getweight(user, STAM_COST_ATTACK_OBJ_MULT))//CIT CHANGE - makes attacking things cause stamina loss
user.changeNext_move(CLICK_CD_MELEE)
user.do_attack_animation(O)
O.attacked_by(src, user)
if(!O.attacked_by(src, user))
user.changeNext_move(click_delay)
var/weight = getweight(user, STAM_COST_ATTACK_OBJ_MULT)
if(weight)
user.adjustStaminaLossBuffered(weight)//CIT CHANGE - makes attacking things cause stamina loss
/atom/movable/proc/attacked_by()
return
@@ -113,9 +107,18 @@
/obj/attacked_by(obj/item/I, mob/living/user)
var/totitemdamage = I.force
var/bad_trait
if(!SEND_SIGNAL(user, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE))
totitemdamage *= 0.5
var/stamloss = user.getStaminaLoss()
var/next_move_mult = 1
if(stamloss > STAMINA_NEAR_SOFTCRIT) //The more tired you are, the less damage you do.
var/penalty = (stamloss - STAMINA_NEAR_SOFTCRIT)/(STAMINA_NEAR_CRIT - STAMINA_NEAR_SOFTCRIT)*STAM_CRIT_ITEM_ATTACK_PENALTY
totitemdamage *= 1 - penalty
next_move_mult += penalty*STAM_CRIT_ITEM_ATTACK_DELAY
user.changeNext_move(I.click_delay*next_move_mult)
if(SEND_SIGNAL(user, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE))
bad_trait = SKILL_COMBAT_MODE //blacklist combat skills.
if(I.used_skills && user.mind)
if(totitemdamage)
totitemdamage = user.mind.item_action_skills_mod(I, totitemdamage, I.skill_difficulty, SKILL_ATTACK_OBJ, bad_trait)
@@ -123,17 +126,19 @@
if(!(I.used_skills[skill] & SKILL_TRAIN_ATTACK_OBJ))
continue
user.mind.auto_gain_experience(skill, I.skill_gain)
if(totitemdamage)
visible_message("<span class='danger'>[user] has hit [src] with [I]!</span>", null, null, COMBAT_MESSAGE_RANGE)
//only witnesses close by and the victim see a hit message.
log_combat(user, src, "attacked", I)
take_damage(totitemdamage, I.damtype, "melee", 1)
return TRUE
/mob/living/attacked_by(obj/item/I, mob/living/user)
var/totitemdamage = pre_attacked_by(I, user)
if((user != src) && mob_run_block(I, totitemdamage, "the [I.name]", ATTACK_TYPE_MELEE, I.armour_penetration, user, null, null) & BLOCK_SUCCESS)
return FALSE
send_item_attack_message(I, user)
send_item_attack_message(I, user, null, totitemdamage)
I.do_stagger_action(src, user, totitemdamage)
if(I.force)
apply_damage(totitemdamage, I.damtype)
@@ -149,19 +154,37 @@
/mob/living/simple_animal/attacked_by(obj/item/I, mob/living/user)
if(I.force < force_threshold || I.damtype == STAMINA)
playsound(loc, 'sound/weapons/tap.ogg', I.get_clamped_volume(), 1, -1)
user.changeNext_move(I.click_delay) //pre_attacked_by not called
else
return ..()
/mob/living/proc/pre_attacked_by(obj/item/I, mob/living/user)
. = I.force
if(!.)
return
var/stamloss = user.getStaminaLoss()
var/stam_mobility_mult = 1
var/next_move_mult = 1
if(stamloss > STAMINA_NEAR_SOFTCRIT) //The more tired you are, the less damage you do.
var/penalty = (stamloss - STAMINA_NEAR_SOFTCRIT)/(STAMINA_NEAR_CRIT - STAMINA_NEAR_SOFTCRIT)*STAM_CRIT_ITEM_ATTACK_PENALTY
stam_mobility_mult -= penalty
next_move_mult += penalty*STAM_CRIT_ITEM_ATTACK_DELAY
if(stam_mobility_mult > LYING_DAMAGE_PENALTY && !CHECK_MOBILITY(user, MOBILITY_STAND)) //damage penalty for fighting prone, doesn't stack with the above.
stam_mobility_mult = LYING_DAMAGE_PENALTY
. *= stam_mobility_mult
user.changeNext_move(I.click_delay*next_move_mult)
I.attack_delay_done = TRUE
var/bad_trait
if(SEND_SIGNAL(user, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE))
. *= 0.5
bad_trait = SKILL_COMBAT_MODE //blacklist combat skills.
if(SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE))
. *= 1.5
if(!CHECK_MOBILITY(user, MOBILITY_STAND))
. *= 0.5
if(!(I.item_flags & NO_COMBAT_MODE_FORCE_MODIFIER))
if(SEND_SIGNAL(user, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE))
bad_trait = SKILL_COMBAT_MODE //blacklist combat skills.
if(SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE))
. *= 0.5
else if(SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE))
. *= 1.5
if(!user.mind || !I.used_skills)
return
if(.)
@@ -171,7 +194,6 @@
continue
user.mind.auto_gain_experience(skill, I.skill_gain)
// Proximity_flag is 1 if this afterattack was called on something adjacent, in your square, or on your person.
// Click parameters is the params string from byond Click() code, see that documentation.
/obj/item/proc/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
@@ -185,10 +207,12 @@
else
return clamp(w_class * 6, 10, 100) // Multiply the item's weight class by 6, then clamp the value between 10 and 100
/mob/living/proc/send_item_attack_message(obj/item/I, mob/living/user, hit_area)
/mob/living/proc/send_item_attack_message(obj/item/I, mob/living/user, hit_area, current_force)
var/message_verb = "attacked"
if(I.attack_verb && I.attack_verb.len)
message_verb = "[pick(I.attack_verb)]"
if(current_force < I.force * INEFFICIENT_ATTACK_MSG_THRESHOLD)
message_verb = "inefficiently [message_verb]"
else if(!I.force)
return
var/message_hit_area = ""
@@ -215,6 +239,8 @@
bad_trait = SKILL_COMBAT_MODE
if(used_skills && user.mind)
. = user.mind.item_action_skills_mod(src, ., skill_difficulty, trait, bad_trait, FALSE)
var/total_health = user.getStaminaLoss()
. = clamp(., 0, STAMINA_NEAR_CRIT - total_health)
/// How long this staggers for. 0 and negatives supported.
/obj/item/proc/melee_stagger_duration(force_override)
+5 -1
View File
@@ -307,7 +307,11 @@ SUBSYSTEM_DEF(research)
for(var/A in subtypesof(/obj/item/seeds))
var/obj/item/seeds/S = A
var/list/L = list()
L[TECHWEB_POINT_TYPE_GENERIC] = 50 + initial(S.rarity) * 2
//First we get are yield and rarity and times it by two
//Then we subtract production and maturation, making it so faster growing plants are better for RnD
//Then we add in lifespan and potency,
//A basic seed can be worth 268 points if its the best it can be.
L[TECHWEB_POINT_TYPE_GENERIC] = 50 + initial(S.rarity) * 2 + initial(S.yield) * 2 - initial(S.maturation) - initial(S.production) + initial(S.lifespan) + initial(S.potency)
techweb_point_items[S] = L
return ..()
-77
View File
@@ -1,77 +0,0 @@
/datum/component/decal
dupe_mode = COMPONENT_DUPE_ALLOWED
can_transfer = TRUE
var/cleanable
var/description
var/mutable_appearance/pic
var/first_dir // This only stores the dir arg from init
/datum/component/decal/Initialize(_icon, _icon_state, _dir, _cleanable=CLEAN_GOD, _color, _layer=TURF_LAYER, _description, _alpha=255)
if(!isatom(parent) || !generate_appearance(_icon, _icon_state, _dir, _layer, _color, _alpha))
return COMPONENT_INCOMPATIBLE
first_dir = _dir
description = _description
cleanable = _cleanable
apply()
/datum/component/decal/RegisterWithParent()
. = ..()
if(first_dir)
RegisterSignal(parent, COMSIG_ATOM_DIR_CHANGE, .proc/rotate_react)
if(cleanable)
RegisterSignal(parent, COMSIG_COMPONENT_CLEAN_ACT, .proc/clean_react)
if(description)
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, .proc/examine)
/datum/component/decal/UnregisterFromParent()
. = ..()
UnregisterSignal(parent, list(COMSIG_ATOM_DIR_CHANGE, COMSIG_COMPONENT_CLEAN_ACT, COMSIG_PARENT_EXAMINE))
/datum/component/decal/Destroy()
remove()
return ..()
/datum/component/decal/PreTransfer()
remove()
/datum/component/decal/PostTransfer()
remove()
apply()
/datum/component/decal/proc/generate_appearance(_icon, _icon_state, _dir, _layer, _color, _alpha)
if(!_icon || !_icon_state)
return FALSE
// It has to be made from an image or dir breaks because of a byond bug
var/temp_image = image(_icon, null, _icon_state, _layer, _dir)
pic = new(temp_image)
pic.color = _color
pic.alpha = _alpha
return TRUE
/datum/component/decal/proc/apply(atom/thing)
var/atom/master = thing || parent
master.add_overlay(pic, TRUE)
if(isitem(master))
addtimer(CALLBACK(master, /obj/item/.proc/update_slot_icon), 0, TIMER_UNIQUE)
/datum/component/decal/proc/remove(atom/thing)
var/atom/master = thing || parent
master.cut_overlay(pic, TRUE)
if(isitem(master))
addtimer(CALLBACK(master, /obj/item/.proc/update_slot_icon), 0, TIMER_UNIQUE)
/datum/component/decal/proc/rotate_react(datum/source, old_dir, new_dir)
if(old_dir == new_dir)
return
remove()
pic.dir = turn(pic.dir, dir2angle(old_dir) - dir2angle(new_dir))
apply()
/datum/component/decal/proc/clean_react(datum/source, strength)
if(strength >= cleanable)
qdel(src)
/datum/component/decal/proc/examine(datum/source, mob/user, list/examine_list)
examine_list += description
-13
View File
@@ -1,13 +0,0 @@
/datum/component/decal/blood
dupe_mode = COMPONENT_DUPE_UNIQUE
/datum/component/decal/blood/Initialize(_icon, _icon_state, _dir, _cleanable=CLEAN_STRENGTH_BLOOD, _color, _layer=ABOVE_OBJ_LAYER)
if(!isitem(parent))
return COMPONENT_INCOMPATIBLE
. = ..()
RegisterSignal(parent, COMSIG_ATOM_GET_EXAMINE_NAME, .proc/get_examine_name)
/datum/component/decal/blood/proc/get_examine_name(datum/source, mob/user, list/override)
var/atom/A = parent
return COMPONENT_EXNAME_CHANGED
+1 -1
View File
@@ -73,7 +73,7 @@
if(!material_amount)
to_chat(user, "<span class='warning'>[I] does not contain sufficient materials to be accepted by [parent].</span>")
return
if((!precise_insertion || !GLOB.typecache_stack[I]) && !has_space(material_amount))
if((!precise_insertion || !GLOB.typecache_stack[I.type]) && !has_space(material_amount))
to_chat(user, "<span class='warning'>[parent] has not enough space. Please remove materials from [parent] in order to insert more.</span>")
return
user_insert(I, user)
+5 -5
View File
@@ -25,7 +25,7 @@
var/mob/living/owner = parent
if(owner.stat != DEAD)
START_PROCESSING(SSdcs, src)
START_PROCESSING(SSobj, src)
RegisterSignal(parent, COMSIG_ADD_MOOD_EVENT, .proc/add_event)
RegisterSignal(parent, COMSIG_CLEAR_MOOD_EVENT, .proc/clear_event)
@@ -40,12 +40,12 @@
hud.show_hud(hud.hud_version)
/datum/component/mood/Destroy()
STOP_PROCESSING(SSdcs, src)
STOP_PROCESSING(SSobj, src)
unmodify_hud()
return ..()
/datum/component/mood/proc/stop_processing()
STOP_PROCESSING(SSdcs, src)
STOP_PROCESSING(SSobj, src)
/datum/component/mood/proc/print_mood(mob/user)
var/msg = "<span class='info'>*---------*\n<EM>Your current mood</EM>\n"
@@ -138,7 +138,7 @@
else
screen_obj.icon_state = "mood[mood_level]"
/datum/component/mood/process() //Called on SSdcs process
/datum/component/mood/process() //Called on SSobj process
if(QDELETED(parent)) // workaround to an obnoxious sneaky periodical runtime.
qdel(src)
return
@@ -345,7 +345,7 @@
///Called when parent is revived.
/datum/component/mood/proc/on_revive(datum/source, full_heal)
START_PROCESSING(SSdcs, src)
START_PROCESSING(SSobj, src)
if(!full_heal)
return
remove_temp_moods()
+4 -2
View File
@@ -62,7 +62,9 @@
orbiters[orbiter] = TRUE
orbiter.orbiting = src
RegisterSignal(orbiter, COMSIG_MOVABLE_MOVED, .proc/orbiter_move_react)
var/matrix/initial_transform = matrix(orbiter.transform)
orbiters[orbiter] = initial_transform
// Head first!
if(pre_rotation)
@@ -79,8 +81,6 @@
orbiter.SpinAnimation(rotation_speed, -1, clockwise, rotation_segments, parallel = FALSE)
//we stack the orbits up client side, so we can assign this back to normal server side without it breaking the orbit
orbiter.transform = initial_transform
orbiter.forceMove(get_turf(parent))
to_chat(orbiter, "<span class='notice'>Now orbiting [parent].</span>")
@@ -89,6 +89,8 @@
return
UnregisterSignal(orbiter, COMSIG_MOVABLE_MOVED)
orbiter.SpinAnimation(0, 0)
if(istype(orbiters[orbiter],/matrix)) //This is ugly.
orbiter.transform = orbiters[orbiter]
orbiters -= orbiter
orbiter.stop_orbit(src)
orbiter.orbiting = null
@@ -57,7 +57,7 @@
/obj/item/reagent_containers/hypospray/medipen, /obj/item/reagent_containers/dropper,
/obj/item/implanter, /obj/item/screwdriver, /obj/item/weldingtool/mini,
/obj/item/firing_pin, /obj/item/gun/ballistic/automatic/pistol, /obj/item/gun/ballistic/automatic/magrifle/pistol,
/obj/item/toy/plush/snakeplushie
/obj/item/toy/plush/snakeplushie, /obj/item/gun/energy/e_gun/mini
))
/datum/component/storage/concrete/pockets/shoes/clown/Initialize()
@@ -68,7 +68,7 @@
/obj/item/scalpel, /obj/item/reagent_containers/syringe, /obj/item/dnainjector,
/obj/item/reagent_containers/hypospray/medipen, /obj/item/reagent_containers/dropper,
/obj/item/implanter, /obj/item/screwdriver, /obj/item/weldingtool/mini,
/obj/item/firing_pin, /obj/item/bikehorn, /obj/item/gun/ballistic/automatic/pistol))
/obj/item/firing_pin, /obj/item/bikehorn, /obj/item/gun/ballistic/automatic/pistol, /obj/item/gun/energy/e_gun/mini))
/datum/component/storage/concrete/pockets/pocketprotector
max_items = 3
+10 -10
View File
@@ -163,24 +163,24 @@
if(-4 to -2) // glancing blow at best
user.visible_message("<span class='warning'>[user] lands a weak [tackle_word] on [target], briefly knocking [target.p_them()] off-balance!</span>", "<span class='userdanger'>You land a weak [tackle_word] on [target], briefly knocking [target.p_them()] off-balance!</span>", target)
to_chat(target, "<span class='userdanger'>[user] lands a weak [tackle_word] on you, briefly knocking you off-balance!</span>")
user.Knockdown(30)
target.adjustStaminaLoss(15)
target.apply_status_effect(STATUS_EFFECT_TASED_WEAK, 6 SECONDS)
if(-1 to 0) // decent hit, both parties are about equally inconvenienced
user.visible_message("<span class='warning'>[user] lands a passable [tackle_word] on [target], sending them both tumbling!</span>", "<span class='userdanger'>You land a passable [tackle_word] on [target], sending you both tumbling!</span>", target)
to_chat(target, "<span class='userdanger'>[user] lands a passable [tackle_word] on you, sending you both tumbling!</span>")
target.adjustStaminaLoss(stamina_cost)
target.adjustStaminaLoss(stamina_cost * 1.5)
target.Paralyze(5)
user.Knockdown(20)
target.Knockdown(25)
target.Knockdown(30)
if(1 to 2) // solid hit, tackler has a slight advantage
user.visible_message("<span class='warning'>[user] lands a solid [tackle_word] on [target], knocking them both down hard!</span>", "<span class='userdanger'>You land a solid [tackle_word] on [target], knocking you both down hard!</span>", target)
to_chat(target, "<span class='userdanger'>[user] lands a solid [tackle_word] on you, knocking you both down hard!</span>")
target.adjustStaminaLoss(30)
target.adjustStaminaLoss(40)
target.Paralyze(5)
user.Knockdown(10)
target.Knockdown(20)
@@ -192,9 +192,9 @@
user.SetKnockdown(0)
user.set_resting(FALSE, TRUE, FALSE)
user.forceMove(get_turf(target))
target.adjustStaminaLoss(40)
target.Paralyze(5)
target.Knockdown(30)
target.adjustStaminaLoss(50)
target.Paralyze(3) //Otherwise the victim can just instantly get out of the grab.
target.DefaultCombatKnockdown(20) //So they cant get up instantly.
if(ishuman(target) && iscarbon(user))
target.grabbedby(user)
@@ -205,9 +205,9 @@
user.SetKnockdown(0)
user.set_resting(FALSE, TRUE, FALSE)
user.forceMove(get_turf(target))
target.adjustStaminaLoss(40)
target.Paralyze(5)
target.Knockdown(30)
target.adjustStaminaLoss(65)
target.Paralyze(10)
target.DefaultCombatKnockdown(20)
if(ishuman(target) && iscarbon(user))
target.grabbedby(user)
target.grippedby(user, instant = TRUE)
-5
View File
@@ -175,11 +175,6 @@
name = "Trooper REAL_NAME"
desc = "That's not red paint. That's real corgi blood."
/datum/dog_fashion/head/colour
name = "Stylish REAL_NAME"
desc = "From the tips of their paws to the top of their head, they look like a made bed."
emote_see = list("tries to tap dances.","looks sadly at others outfits...","barks at bad fashion!")
/datum/dog_fashion/head/telegram
name = "Messenger REAL_NAME"
desc = "Dont shoot the messenger..."
+75
View File
@@ -0,0 +1,75 @@
/datum/element/decal
element_flags = ELEMENT_BESPOKE|ELEMENT_DETACH
id_arg_index = 2
var/cleanable
var/description
var/mutable_appearance/pic
var/list/num_decals_per_atom
var/first_dir // This stores the direction of the decal compared to the parent facing NORTH
/datum/element/decal/Attach(datum/target, _icon, _icon_state, _dir, _cleanable=CLEAN_GOD, _color, _layer=TURF_LAYER, _description, _alpha=255)
. = ..()
if(. == ELEMENT_INCOMPATIBLE || !_icon || !_icon_state || !isatom(target))
return ELEMENT_INCOMPATIBLE
var/atom/A = target
if(!pic)
// It has to be made from an image or dir breaks because of a byond bug
var/temp_image = image(_icon, null, _icon_state, _layer, _dir)
pic = new(temp_image)
pic.color = _color
pic.alpha = _alpha
first_dir = _dir
description = _description
cleanable = _cleanable
LAZYINITLIST(num_decals_per_atom)
if(!num_decals_per_atom[A])
if(first_dir)
RegisterSignal(A, COMSIG_ATOM_DIR_CHANGE, .proc/rotate_react)
if(cleanable)
RegisterSignal(A, COMSIG_COMPONENT_CLEAN_ACT, .proc/clean_react)
if(description)
RegisterSignal(A, COMSIG_PARENT_EXAMINE, .proc/examine)
apply(A, TRUE)
num_decals_per_atom[A]++
/datum/element/decal/Detach(datum/target)
var/atom/A = target
remove(A, A.dir)
UnregisterSignal(A, list(COMSIG_ATOM_DIR_CHANGE, COMSIG_COMPONENT_CLEAN_ACT, COMSIG_PARENT_EXAMINE))
LAZYREMOVE(num_decals_per_atom, A)
return ..()
/datum/element/decal/proc/remove(atom/target, old_dir)
pic.dir = first_dir == NORTH ? target.dir : turn(first_dir, dir2angle(old_dir))
for(var/i in 1 to num_decals_per_atom[target])
target.cut_overlay(pic, TRUE)
if(isitem(target))
addtimer(CALLBACK(target, /obj/item/.proc/update_slot_icon), 0, TIMER_UNIQUE)
/datum/element/decal/proc/apply(atom/target, init = FALSE)
pic.dir = first_dir == NORTH ? target.dir : turn(first_dir, dir2angle(target.dir))
if(init)
target.add_overlay(pic, TRUE)
else
for(var/i in 1 to num_decals_per_atom[target])
target.add_overlay(pic, TRUE)
if(isitem(target))
addtimer(CALLBACK(target, /obj/item/.proc/update_slot_icon), 0, TIMER_UNIQUE)
/datum/element/decal/proc/rotate_react(datum/source, old_dir, new_dir)
if(old_dir == new_dir)
return
remove(source, old_dir)
apply(source)
/datum/element/decal/proc/clean_react(datum/source, strength)
if(strength >= cleanable)
Detach(source)
/datum/element/decal/proc/examine(datum/source, mob/user, list/examine_list)
examine_list += description
+1 -1
View File
@@ -4,7 +4,7 @@
var/list/user_by_item = list()
/datum/element/earhealing/New()
START_PROCESSING(SSdcs, src)
START_PROCESSING(SSobj, src)
/datum/element/earhealing/Attach(datum/target)
. = ..()
+81
View File
@@ -0,0 +1,81 @@
/datum/element/photosynthesis
element_flags = ELEMENT_BESPOKE|ELEMENT_DETACH
id_arg_index = 2
///how much brute damage (or integrity, for objects) is healed (taken if positive) at maximum luminosity. (if lum_minus were 0)
var/light_bruteheal = -1
///how much burn damage is restored/taken at maximum luminosity. Mobs only.
var/light_burnheal = -1
///how much tox damage is restored/taken at maximum luminosity. Mobs only.
var/light_toxheal = -1
///how much oxy damage is restored/taken at maximum luminosity. Mobs only.
var/light_oxyheal = -1
///how nutrition recovery/expenses factor, not affected by bonus_lum and malus_lum. Mobs only.
var/light_nutrition_gain = 4
///A value subtracted to the lum count, which allows targets to wilt or heal in the darkness.
var/lum_minus = 0.5
///the minimum lum count over which where the target damage is adjusted.
var/bonus_lum = 0.2
///the maximum lum count under which the target damage is inversely adjusted.
var/malus_lum = 0
///List of atoms this element is attached to. Doubles as a multiplier if the same element is attached multiple times to a target multiple times.
var/list/attached_atoms
/datum/element/photosynthesis/Attach(datum/target, brute = -1, burn = -1, tox = -1, oxy = -1, nutri = 4, minus = 0.2, bonus = 0.3, malus = -0.1)
. = ..()
if(. == ELEMENT_INCOMPATIBLE || !(isliving(target) || (isobj(target) && light_bruteheal)))
return ELEMENT_INCOMPATIBLE
light_bruteheal = brute
light_burnheal = burn
light_toxheal = tox
light_oxyheal = oxy
light_nutrition_gain = nutri
lum_minus = minus
bonus_lum = bonus
malus_lum = malus
if(!attached_atoms)
attached_atoms = list()
START_PROCESSING(SSobj, src)
attached_atoms[target]++
/datum/element/photosynthesis/Detach(datum/target)
attached_atoms[target]--
if(!attached_atoms[target])
attached_atoms -= target
if(!length(attached_atoms))
STOP_PROCESSING(SSobj, src)
attached_atoms = null
return ..()
/datum/element/photosynthesis/process()
for(var/A in attached_atoms)
var/atom/movable/AM = A
var/light_amount = 0 //how much light there is in the place, affects receiving nutrition and healing
if(isturf(AM.loc)) //else, there's considered to be no light
var/turf/T = AM.loc
light_amount = (T.get_lumcount() - lum_minus)
if(isliving(AM))
var/mob/living/L = AM
if(L.stat == DEAD)
continue
if(light_nutrition_gain)
L.adjust_nutrition(light_amount * light_nutrition_gain * attached_atoms[AM], NUTRITION_LEVEL_FULL)
if(light_amount > bonus_lum || light_amount < malus_lum)
var/mult = ((light_amount > bonus_lum) ? 1 : -1) * attached_atoms[AM]
if(light_bruteheal)
L.adjustBruteLoss(light_bruteheal * mult)
if(light_burnheal)
L.adjustFireLoss(light_burnheal * mult)
if(light_toxheal)
L.adjustToxLoss(light_toxheal * mult)
if(light_oxyheal)
L.adjustOxyLoss(light_oxyheal * mult)
else if(light_amount > bonus_lum || light_amount < malus_lum)
var/obj/O = AM
var/damage = light_bruteheal * ((light_amount > bonus_lum) ? 1 : -1) * attached_atoms[AM]
if(damage < 0 && O.obj_integrity < O.max_integrity)
O.obj_integrity = min(O.obj_integrity + damage, O.max_integrity) //Till we get a obj heal proc...
else
O.take_damage(damage, BRUTE, FALSE, FALSE, null, 100)
+8 -2
View File
@@ -331,14 +331,20 @@
/datum/mind/proc/enslave_mind_to_creator(mob/living/creator)
if(iscultist(creator))
SSticker.mode.add_cultist(src)
if(iscultist(creator, TRUE))
SSticker.mode.add_cultist(src)
else
src.add_antag_datum(/datum/antagonist/cult/neutered/traitor)
else if(is_revolutionary(creator))
var/datum/antagonist/rev/converter = creator.mind.has_antag_datum(/datum/antagonist/rev,TRUE)
converter.add_revolutionary(src,FALSE)
else if(is_servant_of_ratvar(creator))
add_servant_of_ratvar(current)
if(is_servant_of_ratvar(creator, TRUE))
add_servant_of_ratvar(current)
else
add_servant_of_ratvar(current, FALSE, FALSE, /datum/antagonist/clockcult/neutered/traitor)
else if(is_nuclear_operative(creator))
var/datum/antagonist/nukeop/converter = creator.mind.has_antag_datum(/datum/antagonist/nukeop,TRUE)
+9
View File
@@ -173,6 +173,11 @@
port_id = "snowdin"
can_be_bought = FALSE
/datum/map_template/shuttle/snow_taxi
port_id = "snow"
can_be_bought = FALSE
suffix = "taxi"
// Shuttles start here:
/datum/map_template/shuttle/emergency/backup
@@ -560,6 +565,10 @@
suffix = "cog"
name = "arrival shuttle (Cog)"
/datum/map_template/shuttle/arrival/snaxi
suffix = "snaxi"
name = "arrival shuttle (Snaxi)"
/datum/map_template/shuttle/aux_base/default
suffix = "default"
name = "auxilliary base (Default)"
+1 -1
View File
@@ -191,7 +191,7 @@
/datum/mind/proc/skill_html_readout()
var/list/out = list("<center><h1>Skills</h1></center><hr>")
out += "<table style=\"width:100%\"><tr><th><b>Skill</b><th><b>Value</b></tr>"
for(var/path in skill_holder.skills|skill_holder.skill_value_mods|skill_holder.skill_level_mods)
for(var/path in GLOB.skill_datums)
var/datum/skill/S = GLOB.skill_datums[path]
var/skill_value = get_skill_value(path)
var/skill_level = get_skill_level(path, round = TRUE)
@@ -18,7 +18,7 @@
area_type = /area
protected_areas = list(/area/maintenance, /area/ai_monitored/turret_protected/ai_upload, /area/ai_monitored/turret_protected/ai_upload_foyer,
/area/ai_monitored/turret_protected/ai, /area/storage/emergency/starboard, /area/storage/emergency/port, /area/shuttle, /area/security/prison, /area/ruin, /area/space/nearstation)
/area/ai_monitored/turret_protected/ai, /area/storage/emergency/starboard, /area/storage/emergency/port, /area/shuttle, /area/security/prison, /area/ruin, /area/space/nearstation, /area/icemoon)
target_trait = ZTRAIT_STATION
immunity_type = "rad"
+4 -1
View File
@@ -200,4 +200,7 @@
name = "Tiny Freighter"
/area/shuttle/caravan/freighter3
name = "Tiny Freighter"
name = "Tiny Freighter"
/area/shuttle/snowtaxi
name = "Snow Taxi"
+1 -1
View File
@@ -147,7 +147,7 @@
/obj/machinery/dominator/attacked_by(obj/item/I, mob/living/user)
add_fingerprint(user)
..()
return ..()
/obj/machinery/dominator/attack_hand(mob/user)
if(operating || (stat & BROKEN))
+1 -1
View File
@@ -36,7 +36,7 @@
targetitem = /obj/item/gun/energy/e_gun/hos
difficulty = 10
excludefromjob = list("Head Of Security")
altitems = list(/obj/item/gun/ballistic/revolver/mws)
altitems = list(/obj/item/gun/ballistic/revolver/mws, /obj/item/choice_beacon/hosgun) //We now look for eather the alt verson of the hos gun or the beacon picker.
/datum/objective_item/steal/handtele
name = "a hand teleporter."
@@ -71,7 +71,7 @@
return ..()
/obj/machinery/porta_turret_cover/attacked_by(obj/item/I, mob/user)
parent_turret.attacked_by(I, user)
return parent_turret.attacked_by(I, user)
/obj/machinery/porta_turret_cover/attack_alien(mob/living/carbon/alien/humanoid/user)
parent_turret.attack_alien(user)
+30 -12
View File
@@ -100,44 +100,61 @@
eat(AM)
. = ..()
/obj/machinery/recycler/proc/eat(atom/AM0, sound=TRUE)
/obj/machinery/recycler/proc/eat(atom/AM0)
if(stat & (BROKEN|NOPOWER) || safety_mode)
return
var/list/to_eat
to_eat = AM0.GetAllContentsIgnoring(GLOB.typecache_mob)
to_eat = list(AM0)
var/items_recycled = 0
var/buzz = FALSE
for(var/i in to_eat)
var/atom/movable/AM = i
if(QDELETED(AM))
continue
var/obj/item/bodypart/head/as_head = AM
var/obj/item/mmi/as_mmi = AM
var/brain_holder = istype(AM, /obj/item/organ/brain) || (istype(as_head) && as_head.brain) || (istype(as_mmi) && as_mmi.brain) || isbrain(AM) || istype(AM, /obj/item/dullahan_relay)
var/brain_holder = istype(AM, /obj/item/organ/brain) || (istype(as_head) && as_head.brain) || (istype(as_mmi) && as_mmi.brain) || istype(AM, /obj/item/dullahan_relay)
if(brain_holder)
emergency_stop(AM)
else if(isliving(AM))
if((obj_flags & EMAGGED)||((!allowed(AM))&&(!ishuman(AM))))
crush_living(AM)
if(obj_flags & EMAGGED)
continue
else
emergency_stop(AM)
return
else if(isliving(AM))
if((obj_flags & EMAGGED)||((!allowed(AM))&&(!ishuman(AM))))
to_eat += crush_living(AM)
else
emergency_stop(AM)
return
else if(isitem(AM))
var/obj/O = AM
if(O.resistance_flags & INDESTRUCTIBLE)
playsound(src, 'sound/machines/buzz-sigh.ogg', 50, 0)
buzz = TRUE
O.forceMove(loc)
else
recycle_item(AM)
to_eat += recycle_item(AM)
items_recycled++
else
playsound(src, 'sound/machines/buzz-sigh.ogg', 50, 0)
buzz = TRUE
AM.forceMove(loc)
if(items_recycled && sound)
if(items_recycled)
playsound(src, item_recycle_sound, 50, 1)
if(buzz)
playsound(src, 'sound/machines/buzz-sigh.ogg', 50, 0)
/obj/machinery/recycler/proc/recycle_item(obj/item/I)
. = list()
for(var/A in I)
var/atom/movable/AM = A
AM.forceMove(loc)
if(AM.loc == loc)
. += AM
I.forceMove(loc)
var/obj/item/grown/log/L = I
if(istype(L))
@@ -172,6 +189,7 @@
/obj/machinery/recycler/proc/crush_living(mob/living/L)
. = list()
L.forceMove(loc)
if(issilicon(L))
@@ -193,7 +211,7 @@
if(eat_victim_items)
for(var/obj/item/I in L.get_equipped_items(TRUE))
if(L.dropItemToGround(I))
eat(I, sound=FALSE)
. += I
// Instantly lie down, also go unconscious from the pain, before you die.
L.Unconscious(100)
+249 -227
View File
@@ -9,7 +9,7 @@ GLOBAL_LIST_EMPTY(allConsoles)
#define NO_NEW_MESSAGE 0
#define NORMAL_MESSAGE_PRIORITY 1
#define HIGH_MESSAGE_PRIORITY 2
#define EXTREME_MESSAGE_PRIORITY 3 // not implemented, will probably require some hacking... everything needs to have a hidden feature in this game.
#define EXTREME_MESSAGE_PRIORITY 3 // is implimented, does require hacking. everything needs to have a hidden feature in this game.
/obj/machinery/requests_console
name = "requests console"
@@ -48,9 +48,9 @@ GLOBAL_LIST_EMPTY(allConsoles)
var/announceAuth = FALSE //Will be set to 1 when you authenticate yourself for announcements
var/msgVerified = "" //Will contain the name of the person who verified it
var/msgStamped = "" //If a message is stamped, this will contain the stamp name
var/message = "";
var/dpt = ""; //the department which will be receiving the message
var/priority = -1 ; //Priority of the message being sent
var/message = ""
var/dpt = "" //the department which will be receiving the message
var/priority = NORMAL_MESSAGE_PRIORITY //Priority of the message being sent. why is the default -1??
var/obj/item/radio/Radio
var/emergency //If an emergency has been called by this device. Acts as both a cooldown and lets the responder know where it the emergency was triggered from
var/receive_ore_updates = FALSE //If ore redemption machines will send an update when it receives new ores.
@@ -62,16 +62,17 @@ GLOBAL_LIST_EMPTY(allConsoles)
update_icon()
/obj/machinery/requests_console/update_icon_state()
if(stat & NOPOWER)
if(CHECK_BITFIELD(stat, NOPOWER))
set_light(0)
else
set_light(1.4,0.7,"#34D352")//green light
set_light(1.4, 0.7, "#34D352")//green light
if(open)
if(!hackState)
icon_state="req_comp_open"
else
icon_state="req_comp_rewired"
else if(stat & NOPOWER)
else if(CHECK_BITFIELD(stat, NOPOWER))
if(icon_state != "req_comp_off")
icon_state = "req_comp_off"
else
@@ -122,7 +123,7 @@ GLOBAL_LIST_EMPTY(allConsoles)
GLOB.req_console_information += department
Radio = new /obj/item/radio(src)
Radio.listening = 0
Radio.listening = FALSE
/obj/machinery/requests_console/Destroy()
QDEL_NULL(Radio)
@@ -131,164 +132,173 @@ GLOBAL_LIST_EMPTY(allConsoles)
/obj/machinery/requests_console/ui_interact(mob/user)
. = ..()
if(open) //no.
return
var/dat = ""
if(!open)
switch(screen)
if(1) //req. assistance
dat += "Which department do you need assistance from?<BR><BR>"
dat += "<table width='100%'>"
for(var/dpt in GLOB.req_console_assistance)
if (dpt != department)
dat += "<tr>"
dat += "<td width='55%'>[dpt]</td>"
dat += "<td width='45%'><A href='?src=[REF(src)];write=[ckey(dpt)]'>Normal</A> <A href='?src=[REF(src)];write=[ckey(dpt)];priority=2'>High</A>"
if(hackState)
dat += "<A href='?src=[REF(src)];write=[ckey(dpt)];priority=3'>EXTREME</A>"
dat += "</td>"
dat += "</tr>"
dat += "</table>"
dat += "<BR><A href='?src=[REF(src)];setScreen=0'><< Back</A><BR>"
switch(screen)
if(1) //req. assistance
dat += "Which department do you need assistance from?<br><br>"
dat += "<table width='100%'>"
for(var/dpt in GLOB.req_console_assistance)
if(dpt == department)
continue
dat += "<tr>"
dat += "<td width='55%'>[dpt]</td>"
dat += "<td width='45%'>"
dat += "<a href='?src=[REF(src)];write=[ckey(dpt)];priority=1'>Normal</a>"
dat += "<a href='?src=[REF(src)];write=[ckey(dpt)];priority=2'>High</a>"
if(hackState)
dat += "<a href='?src=[REF(src)];write=[ckey(dpt)];priority=3'>EXTREME</a>"
dat += "</td>"
dat += "</tr>"
dat += "</table>"
dat += "<br><A href='?src=[REF(src)];setScreen=0'><< Back</A><br>"
if(2) //req. supplies
dat += "Which department do you need supplies from?<BR><BR>"
dat += "<table width='100%'>"
for(var/dpt in GLOB.req_console_supplies)
if (dpt != department)
dat += "<tr>"
dat += "<td width='55%'>[dpt]</td>"
dat += "<td width='45%'><A href='?src=[REF(src)];write=[ckey(dpt)]'>Normal</A> <A href='?src=[REF(src)];write=[ckey(dpt)];priority=2'>High</A>"
if(hackState)
dat += "<A href='?src=[REF(src)];write=[ckey(dpt)];priority=3'>EXTREME</A>"
dat += "</td>"
dat += "</tr>"
dat += "</table>"
dat += "<BR><A href='?src=[REF(src)];setScreen=0'><< Back</A><BR>"
if(2) //req. supplies
dat += "Which department do you need supplies from?<br><br>"
dat += "<table width='100%'>"
for(var/dpt in GLOB.req_console_supplies)
if(dpt == department)
continue
if(3) //relay information
dat += "Which department would you like to send information to?<BR><BR>"
dat += "<table width='100%'>"
for(var/dpt in GLOB.req_console_information)
if (dpt != department)
dat += "<tr>"
dat += "<td width='55%'>[dpt]</td>"
dat += "<td width='45%'><A href='?src=[REF(src)];write=[ckey(dpt)]'>Normal</A> <A href='?src=[REF(src)];write=[ckey(dpt)];priority=2'>High</A>"
if(hackState)
dat += "<A href='?src=[REF(src)];write=[ckey(dpt)];priority=3'>EXTREME</A>"
dat += "</td>"
dat += "</tr>"
dat += "</table>"
dat += "<BR><A href='?src=[REF(src)];setScreen=0'><< Back</A><BR>"
dat += "<tr>"
dat += "<td width='55%'>[dpt]</td>"
dat += "<td width='45%'>"
dat += "<a href='?src=[REF(src)];write=[ckey(dpt)];priority=1'>Normal</a>"
dat += "<a href='?src=[REF(src)];write=[ckey(dpt)];priority=2'>High</a>"
if(hackState)
dat += "<a href='?src=[REF(src)];write=[ckey(dpt)];priority=3'>EXTREME</a>"
dat += "</td>"
dat += "</tr>"
dat += "</table>"
dat += "<br><A href='?src=[REF(src)];setScreen=0'><< Back</A><br>"
if(6) //sent successfully
dat += "<span class='good'>Message sent.</span><BR><BR>"
dat += "<A href='?src=[REF(src)];setScreen=0'>Continue</A><BR>"
if(3) //relay information
dat += "Which department would you like to send information to?<br><br>"
dat += "<table width='100%'>"
for(var/dpt in GLOB.req_console_information)
if(dpt == department)
continue
dat += "<tr>"
dat += "<td width='55%'>[dpt]</td>"
dat += "<td width='45%'>"
dat += "<a href='?src=[REF(src)];write=[ckey(dpt)];priority=1'>Normal</a>"
dat += "<a href='?src=[REF(src)];write=[ckey(dpt)];priority=2'>High</a>"
if(hackState)
dat += "<a href='?src=[REF(src)];write=[ckey(dpt)];priority=3'>EXTREME</a>"
dat += "</td>"
dat += "</tr>"
dat += "</table>"
dat += "<br><a href='?src=[REF(src)];setScreen=0'><< Back</a><br>"
if(7) //unsuccessful; not sent
dat += "<span class='bad'>An error occurred.</span><BR><BR>"
dat += "<A href='?src=[REF(src)];setScreen=0'>Continue</A><BR>"
if(6) //sent successfully
dat += "<span class='good'>Message sent.</span><br><br>"
dat += "<a href='?src=[REF(src)];setScreen=0'>Continue</a><br>"
if(8) //view messages
for (var/obj/machinery/requests_console/Console in GLOB.allConsoles)
if (Console.department == department)
Console.newmessagepriority = NO_NEW_MESSAGE
Console.update_icon()
if(7) //unsuccessful; not sent
dat += "<span class='bad'>An error occurred.</span><br><br>"
dat += "<a href='?src=[REF(src)];setScreen=0'>Continue</a><br>"
newmessagepriority = NO_NEW_MESSAGE
update_icon()
var/messageComposite = ""
for(var/msg in messages) // This puts more recent messages at the *top*, where they belong.
messageComposite = "<div class='block'>[msg]</div>" + messageComposite
dat += messageComposite
dat += "<BR><A href='?src=[REF(src)];setScreen=0'><< Back to Main Menu</A><BR>"
if(8) //view messages
for(var/obj/machinery/requests_console/Console in GLOB.allConsoles)
if(Console.department == department)
Console.newmessagepriority = NO_NEW_MESSAGE
Console.update_icon()
if(9) //authentication before sending
dat += "<B>Message Authentication</B><BR><BR>"
dat += "<b>Message for [dpt]: </b>[message]<BR><BR>"
dat += "<div class='notice'>You may authenticate your message now by scanning your ID or your stamp</div><BR>"
dat += "<b>Validated by:</b> [msgVerified ? msgVerified : "<i>Not Validated</i>"]<br>"
dat += "<b>Stamped by:</b> [msgStamped ? msgStamped : "<i>Not Stamped</i>"]<br><br>"
dat += "<A href='?src=[REF(src)];department=[dpt]'>Send Message</A><BR>"
dat += "<BR><A href='?src=[REF(src)];setScreen=0'><< Discard Message</A><BR>"
newmessagepriority = NO_NEW_MESSAGE
update_icon()
var/messageComposite = ""
for(var/msg in messages) // This puts more recent messages at the *top*, where they belong.
messageComposite = "<div class='block'>[msg]</div>" + messageComposite
dat += messageComposite
dat += "<br><a href='?src=[REF(src)];setScreen=0'><< Back to Main Menu</a><br>"
if(10) //send announcement
dat += "<h3>Station-wide Announcement</h3>"
if(announceAuth)
dat += "<div class='notice'>Authentication accepted</div><BR>"
else
dat += "<div class='notice'>Swipe your card to authenticate yourself</div><BR>"
dat += "<b>Message: </b>[message ? message : "<i>No Message</i>"]<BR>"
dat += "<A href='?src=[REF(src)];writeAnnouncement=1'>[message ? "Edit" : "Write"] Message</A><BR><BR>"
if ((announceAuth || IsAdminGhost(user)) && message)
dat += "<A href='?src=[REF(src)];sendAnnouncement=1'>Announce Message</A><BR>"
else
dat += "<span class='linkOff'>Announce Message</span><BR>"
dat += "<BR><A href='?src=[REF(src)];setScreen=0'><< Back</A><BR>"
if(9) //authentication before sending
dat += "<b>Message Authentication</b> <br><br>"
dat += "<b>Message for [dpt]:</b> [message] <br><br>"
dat += "<div class='notice'>You may authenticate your message now by scanning your ID or your stamp</div> <br>"
dat += "<b>Validated by:</b> [msgVerified ? "<span class='good'><b>[msgVerified]</b></span>" : "<i>Not Validated</i>"] <br>"
dat += "<b>Stamped by:</b> [msgStamped ? "<span class='boldnotice'>[msgStamped]</span>" : "<i>Not Stamped</i>"] <br><br>"
dat += "<a href='?src=[REF(src)];department=[dpt]'>Send Message</a> <br><br>"
dat += "<a href='?src=[REF(src)];setScreen=0'><< Discard Message</a> <br>"
else //main menu
screen = 0
announceAuth = FALSE
if (newmessagepriority == NORMAL_MESSAGE_PRIORITY)
dat += "<div class='notice'>There are new messages</div><BR>"
if (newmessagepriority == HIGH_MESSAGE_PRIORITY)
dat += "<div class='notice'>There are new <b>PRIORITY</b> messages</div><BR>"
if (newmessagepriority == EXTREME_MESSAGE_PRIORITY)
dat += "<div class='notice'>There are new <b>EXTREME PRIORITY</b> messages</div><BR>"
dat += "<A href='?src=[REF(src)];setScreen=8'>View Messages</A><BR><BR>"
if(10) //send announcement
dat += "<h3>Station-wide Announcement</h3>"
if(announceAuth)
dat += "<div class='notice'>Authentication accepted</div><br>"
else
dat += "<div class='notice'>Swipe your card to authenticate yourself</div> <br>"
dat += "<b>Message: </b>[message ? message : "<i>No Message</i>"] <br>"
dat += "<a href='?src=[REF(src)];writeAnnouncement=1'>[message ? "Edit" : "Write"] Message</a> <br><br>"
if((announceAuth || IsAdminGhost(user)) && message)
dat += "<a href='?src=[REF(src)];sendAnnouncement=1'>Announce Message</a> <br>"
else
dat += "<span class='linkOff'>Announce Message</span> <br>"
dat += "<br><a href='?src=[REF(src)];setScreen=0'><< Back</a> <br>"
dat += "<A href='?src=[REF(src)];setScreen=1'>Request Assistance</A><BR>"
dat += "<A href='?src=[REF(src)];setScreen=2'>Request Supplies</A><BR>"
dat += "<A href='?src=[REF(src)];setScreen=3'>Relay Anonymous Information</A><BR><BR>"
else //main menu
screen = 0
announceAuth = FALSE
if(newmessagepriority == NORMAL_MESSAGE_PRIORITY)
dat += "<div class='notice'>There are new messages</div><br>"
if(newmessagepriority == HIGH_MESSAGE_PRIORITY)
dat += "<div class='notice'>There are new <b>PRIORITY</b> messages</div><br>"
if(newmessagepriority == EXTREME_MESSAGE_PRIORITY)
dat += "<div class='notice'>There are new <b>EXTREME PRIORITY</b> messages</div><br>"
if(!emergency)
dat += "<A href='?src=[REF(src)];emergency=1'>Emergency: Security</A><BR>"
dat += "<A href='?src=[REF(src)];emergency=2'>Emergency: Engineering</A><BR>"
dat += "<A href='?src=[REF(src)];emergency=3'>Emergency: Medical</A><BR><BR>"
else
dat += "<B><font color='red'>[emergency] has been dispatched to this location.</font></B><BR><BR>"
dat += "<a href='?src=[REF(src)];setScreen=8'>View Messages</a> <br><br>"
if(announcementConsole)
dat += "<A href='?src=[REF(src)];setScreen=10'>Send Station-wide Announcement</A><BR><BR>"
if (silent)
dat += "Speaker <A href='?src=[REF(src)];setSilent=0'>OFF</A>"
else
dat += "Speaker <A href='?src=[REF(src)];setSilent=1'>ON</A>"
var/datum/browser/popup = new(user, "req_console", "[department] Requests Console", 450, 440)
popup.set_content(dat)
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
popup.open()
return
dat += "<a href='?src=[REF(src)];setScreen=1'>Request Assistance</a> <br>"
dat += "<a href='?src=[REF(src)];setScreen=2'>Request Supplies</a> <br>"
dat += "<a href='?src=[REF(src)];setScreen=3'>Relay Anonymous Information</a> <br><br>"
if(!emergency)
dat += "<a href='?src=[REF(src)];emergency=1'>Emergency: Security</a> <br>"
dat += "<a href='?src=[REF(src)];emergency=2'>Emergency: Engineering</a> <br>"
dat += "<a href='?src=[REF(src)];emergency=3'>Emergency: Medical</a> <br><br>"
else
dat += "<b><div class='bad'>[emergency] has been dispatched to this location.</div></b> <br><br>"
if(announcementConsole)
dat += "<a href='?src=[REF(src)];setScreen=10'>Send Station-wide Announcement</a> <br><br>"
if(silent)
dat += "Speaker <a href='?src=[REF(src)];setSilent=0'>OFF</a>"
else
dat += "Speaker <a href='?src=[REF(src)];setSilent=1'>ON</a>"
var/datum/browser/popup = new(user, "req_console", "[department] Requests Console", 450, 440)
popup.set_content(dat)
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
popup.open()
/obj/machinery/requests_console/Topic(href, href_list)
if(..())
return
usr.set_machine(src)
add_fingerprint(usr)
if(href_list["write"])
dpt = ckey(reject_bad_text(href_list["write"])) //write contains the string of the receiving department's name
var/new_message = stripped_input(usr, "Write your message:", "Awaiting Input", "", MAX_MESSAGE_LEN)
if(new_message)
message = new_message
screen = 9
if (text2num(href_list["priority"]) < 2)
priority = -1
else
priority = text2num(href_list["priority"])
priority = text2num(href_list["priority"])
else
dpt = "";
msgVerified = ""
msgStamped = ""
screen = 0
priority = -1
priority = NORMAL_MESSAGE_PRIORITY //:salt:
if(href_list["writeAnnouncement"])
var/new_message = reject_bad_text(stripped_input(usr, "Write your message:", "Awaiting Input", "", MAX_MESSAGE_LEN))
if(new_message)
message = new_message
if (text2num(href_list["priority"]) < 2)
priority = -1
else
priority = text2num(href_list["priority"])
priority = text2num(href_list["priority"])
else
message = ""
announceAuth = FALSE
@@ -296,106 +306,116 @@ GLOBAL_LIST_EMPTY(allConsoles)
if(href_list["sendAnnouncement"])
if(!announcementConsole)
updateUsrDialog()
return
if(isliving(usr))
var/mob/living/L = usr
message = L.treat_message(message)
minor_announce(message, "[department] Announcement:")
GLOB.news_network.SubmitArticle(message, department, "Station Announcements", null)
usr.log_talk(message, LOG_SAY, tag="station announcement from [src]")
usr.log_talk(message, LOG_SAY, tag = "station announcement from [src]")
message_admins("[ADMIN_LOOKUPFLW(usr)] has made a station announcement from [src] at [AREACOORD(usr)].")
announceAuth = FALSE
message = ""
screen = 0
if(href_list["emergency"])
if(!emergency)
var/radio_freq
switch(text2num(href_list["emergency"]))
if(1) //Security
radio_freq = FREQ_SECURITY
emergency = "Security"
if(2) //Engineering
radio_freq = FREQ_ENGINEERING
emergency = "Engineering"
if(3) //Medical
radio_freq = FREQ_MEDICAL
emergency = "Medical"
if(radio_freq)
Radio.set_frequency(radio_freq)
Radio.talk_into(src,"[emergency] emergency in [department]!!",radio_freq)
update_icon()
addtimer(CALLBACK(src, .proc/clear_emergency), 3000)
if(emergency) //already has an emergency? do not continue
updateUsrDialog()
return
if( href_list["department"] && message )
var/log_msg = message
var/radio_freq
switch(text2num(href_list["emergency"]))
if(1) //Security
radio_freq = FREQ_SECURITY
emergency = "Security"
if(2) //Engineering
radio_freq = FREQ_ENGINEERING
emergency = "Engineering"
if(3) //Medical
radio_freq = FREQ_MEDICAL
emergency = "Medical"
if(radio_freq)
Radio.set_frequency(radio_freq)
Radio.talk_into(src, "[emergency] emergency in [department]!!", radio_freq)
update_icon()
addtimer(CALLBACK(src, .proc/clear_emergency), 5 MINUTES)
if(href_list["department"] && message)
var/sending = message
sending += "<br>"
if (msgVerified)
sending += msgVerified
sending += "<br>"
if (msgStamped)
sending += msgStamped
sending += "<br>"
screen = 7 //if it's successful, this will get overrwritten (7 = unsufccessfull, 6 = successfull)
if (sending)
var/pass = FALSE
var/datum/data_rc_msg/log = new(href_list["department"], department, log_msg, msgStamped, msgVerified, priority)
for (var/obj/machinery/telecomms/message_server/MS in GLOB.telecomms_list)
if (MS.toggled)
MS.rc_msgs += log
pass = TRUE
if(msgVerified)
sending += "<span class='good'><b>[msgVerified]</b></span> <br>"
if(msgStamped)
sending += "<span class='boldnotice'>[msgStamped]</span> <br>"
//so you're telling me is you cheated, by making fail happen, then quickly replacing it with 6
if(pass)
var/radio_freq = 0
switch(href_list["department"])
if("bridge")
radio_freq = FREQ_COMMAND
if("medbay")
radio_freq = FREQ_MEDICAL
if("science")
radio_freq = FREQ_SCIENCE
if("engineering")
radio_freq = FREQ_ENGINEERING
if("security")
radio_freq = FREQ_SECURITY
if("cargobay" || "mining")
radio_freq = FREQ_SUPPLY
Radio.set_frequency(radio_freq)
var/authentic
if(msgVerified || msgStamped)
authentic = " (Authenticated)"
var/workingServer = FALSE
var/datum/data_rc_msg/log = new(href_list["department"], department, message, msgStamped, msgVerified, priority)
for(var/obj/machinery/telecomms/message_server/MS in GLOB.telecomms_list)
if(MS.on) //on does the calculations. why would this server still work even though the apc is off??
LAZYADD(MS.rc_msgs, log)
workingServer = TRUE
var/alert = ""
for (var/obj/machinery/requests_console/Console in GLOB.allConsoles)
if (ckey(Console.department) == ckey(href_list["department"]))
switch(priority)
if(2) //High priority
alert = "PRIORITY Alert in [department][authentic]"
Console.createmessage(src, alert, sending, 2, 1)
if(3) // Extreme Priority
alert = "EXTREME PRIORITY Alert from [department][authentic]"
Console.createmessage(src, alert , sending, 3, 1)
else // Normal priority
alert = "Message from [department][authentic]"
Console.createmessage(src, alert , sending, 1, 1)
screen = 6
if(!workingServer)
screen = 7
say("NOTICE: No server detected! Please contact your local engineering team.")
updateUsrDialog()
return
if(radio_freq)
Radio.talk_into(src, "[alert]: <i>[message]</i>", radio_freq)
var/radio_freq = 0
switch(href_list["department"])
if("bridge")
radio_freq = FREQ_COMMAND
if("medbay")
radio_freq = FREQ_MEDICAL
if("science")
radio_freq = FREQ_SCIENCE
if("engineering")
radio_freq = FREQ_ENGINEERING
if("security")
radio_freq = FREQ_SECURITY
if("cargobay" || "mining")
radio_freq = FREQ_SUPPLY
Radio.set_frequency(radio_freq)
switch(priority)
if(2)
messages += "<span class='bad'>High Priority</span><BR><b>To:</b> [dpt]<BR>[sending]"
else
messages += "<b>To: [dpt]</b><BR>[sending]"
var/authentic = ""
if(msgVerified || msgStamped)
authentic = " (Authenticated)"
var/alert = ""
for(var/obj/machinery/requests_console/C in GLOB.allConsoles)
if(ckey(C.department) != ckey(href_list["department"]))
continue
switch(priority)
if(HIGH_MESSAGE_PRIORITY) //High priority
alert = "PRIORITY Alert from [department][authentic]"
C.createmessage(src, alert, sending, HIGH_MESSAGE_PRIORITY)
if(EXTREME_MESSAGE_PRIORITY) // Extreme Priority
alert = "EXTREME PRIORITY Alert from [department][authentic]"
C.createmessage(src, alert, sending, EXTREME_MESSAGE_PRIORITY)
else // Normal priority
alert = "Message from [department][authentic]"
C.createmessage(src, alert, sending, NORMAL_MESSAGE_PRIORITY)
screen = 6 //if it ever gets here that means (c.department == href_ls["dept"])
if(radio_freq)
Radio.talk_into(src, "[alert]: <i>[message]</i>", radio_freq)
//log to (this)
switch(priority)
if(HIGH_MESSAGE_PRIORITY)
messages += "<span class='bad'>High Priority</span><br><b>To:</b> [dpt]<br>[sending]"
if(EXTREME_MESSAGE_PRIORITY)
messages += "<span class='bad'>!!!Extreme Priority!!!</span><br><b>To:</b> [dpt]<br>[sending]"
else
say("NOTICE: No server detected!")
messages += "<b>To:</b> [dpt]<br>[sending]"
//Handle screen switching
switch(text2num(href_list["setScreen"]))
if(null) //skip
updateUsrDialog()
return
if(1) //req. assistance
screen = 1
if(2) //req. supplies
@@ -423,16 +443,14 @@ GLOBAL_LIST_EMPTY(allConsoles)
msgVerified = ""
msgStamped = ""
message = ""
priority = -1
priority = NORMAL_MESSAGE_PRIORITY // :salt:
screen = 0
//Handle silencing the console
switch( href_list["setSilent"] )
if(null) //skip
if("1")
silent = TRUE
else
silent = FALSE
if(href_list["setSilent"] == "1")
silent = TRUE
else
silent = FALSE
updateUsrDialog()
return
@@ -457,31 +475,32 @@ GLOBAL_LIST_EMPTY(allConsoles)
linkedsender = source
capitalize(title)
switch(priority)
if(2) //High priority
if(HIGH_MESSAGE_PRIORITY) //High priority
if(newmessagepriority < HIGH_MESSAGE_PRIORITY)
newmessagepriority = HIGH_MESSAGE_PRIORITY
update_icon()
if(!silent)
playsound(src, 'sound/machines/twobeep.ogg', 50, 1)
say(title)
messages += "<span class='bad'>High Priority</span><BR><b>From:</b> [linkedsender]<BR>[message]"
messages += "<span class='bad'>High Priority</span><br><b>From:</b> [linkedsender]<br>[message]" //the fuck is this not being sent
if(3) // Extreme Priority
if(EXTREME_MESSAGE_PRIORITY) // Extreme Priority
if(newmessagepriority < EXTREME_MESSAGE_PRIORITY)
newmessagepriority = EXTREME_MESSAGE_PRIORITY
update_icon()
playsound(src, 'sound/machines/twobeep.ogg', 50, 1)
say(title)
messages += "<span class='bad'>!!!Extreme Priority!!!</span><BR><b>From:</b> [linkedsender]<BR>[message]"
//we ignore the silent option because this is !!!IMPORTANT!!!
playsound(src, 'sound/machines/twobeep.ogg', 50, 1)
say(title)
messages += "<span class='bad'><b>!!!Extreme Priority!!!</span></b><br><b>From:</b> [linkedsender]<br>[message]"
else // Normal priority
if(newmessagepriority < NORMAL_MESSAGE_PRIORITY)
newmessagepriority = NORMAL_MESSAGE_PRIORITY
update_icon()
if(!src.silent)
if(!silent)
playsound(src, 'sound/machines/twobeep.ogg', 50, 1)
say(title)
messages += "<b>From:</b> [linkedsender]<BR>[message]"
messages += "<b>From:</b> [linkedsender]<br>[message]"
/obj/machinery/requests_console/attackby(obj/item/O, mob/user, params)
if(istype(O, /obj/item/crowbar))
@@ -505,23 +524,26 @@ GLOBAL_LIST_EMPTY(allConsoles)
to_chat(user, "<span class='warning'>You must open the maintenance panel first!</span>")
return
var/obj/item/card/id/ID = O.GetID()
if(ID)
if(istype(O, /obj/item/card/id))
var/obj/item/card/id/ID = O.GetID()
if(!ID)
return
if(screen == 9)
msgVerified = "<font color='green'><b>Verified by [ID.registered_name] ([ID.assignment])</b></font>"
msgVerified = "Verified by [ID.registered_name] ([ID.assignment])"
updateUsrDialog()
if(screen == 10)
if (ACCESS_RC_ANNOUNCE in ID.access)
if(ACCESS_RC_ANNOUNCE in ID.access)
announceAuth = TRUE
else
announceAuth = FALSE
to_chat(user, "<span class='warning'>You are not authorized to send announcements!</span>")
updateUsrDialog()
return
if (istype(O, /obj/item/stamp))
if(istype(O, /obj/item/stamp))
if(screen == 9)
var/obj/item/stamp/T = O
msgStamped = "<span class='boldnotice'>Stamped with the [T.name]</span>"
msgStamped = "Stamped with the [T.name]"
updateUsrDialog()
return
return ..()
@@ -1,215 +1,164 @@
/*
The log console for viewing the entire telecomms
network log
*/
/obj/machinery/computer/telecomms/server
name = "telecommunications server monitoring console"
icon_screen = "comm_logs"
desc = "Has full access to all details and record of the telecommunications network it's monitoring."
circuit = /obj/item/circuitboard/computer/comm_server
req_access = list(ACCESS_TCOMSAT)
var/screen = 0 // the screen number:
var/list/servers = list() // the servers located by the computer
var/obj/machinery/telecomms/server/SelectedServer
var/list/machinelist = list() // the servers located by the computer
var/obj/machinery/telecomms/server/SelectedMachine = null
var/network = "NULL" // the network to probe
var/temp = "" // temporary feedback messages
var/notice = ""
var/universal_translate = FALSE // set to TRUE(1) if it can translate nonhuman speech
var/universal_translate = 0 // set to 1 if it can translate nonhuman speech
/obj/machinery/computer/telecomms/server/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "tcommsserver", "Telecomms Server Monitor", 575, 400, master_ui, state)
ui.open()
req_access = list(ACCESS_TCOMSAT)
circuit = /obj/item/circuitboard/computer/comm_server
/obj/machinery/computer/telecomms/server/ui_data(mob/user)
var/list/data_out = list()
data_out["network"] = network
data_out["notice"] = notice
/obj/machinery/computer/telecomms/server/ui_interact(mob/user)
. = ..()
var/dat = "<TITLE>Telecommunication Server Monitor</TITLE><center><b>Telecommunications Server Monitor</b></center>"
data_out["servers"] = list()
for(var/obj/machinery/telecomms/server/T in machinelist)
var/list/data = list(
name = T.name,
id = T.id,
ref = REF(T)
)
data_out["servers"] += list(data)
data_out["servers"] = sortList(data_out["servers"]) //a-z sort
switch(screen)
if(!SelectedMachine) //null is bad.
data_out["selected"] = null //but in js, null is good.
return data_out
data_out["selected"] = list(
name = SelectedMachine.name,
id = SelectedMachine.id,
status = SelectedMachine.on,
traffic = SelectedMachine.totaltraffic, //note: total traffic, not traffic!
ref = REF(SelectedMachine)
)
data_out["selected_logs"] = list()
// --- Main Menu ---
if(!LAZYLEN(SelectedMachine.log_entries))
return data_out
for(var/datum/comm_log_entry/C in SelectedMachine.log_entries)
var/list/data = list()
data["name"] = C.name //name of the file
data["ref"] = REF(C)
data["input_type"] = C.input_type //type of input ("Speech File" | "Execution Error").
if(0)
dat += "<br>[temp]<br>"
dat += "<br>Current Network: <a href='?src=[REF(src)];network=1'>[network]</a><br>"
if(servers.len)
dat += "<br>Detected Telecommunication Servers:<ul>"
for(var/obj/machinery/telecomms/T in servers)
dat += "<li><a href='?src=[REF(src)];viewserver=[T.id]'>[REF(T)] [T.name]</a> ([T.id])</li>"
dat += "</ul>"
dat += "<br><a href='?src=[REF(src)];operation=release'>\[Flush Buffer\]</a>"
if(C.input_type == "Speech File") //there is a reason why this is not a switch.
data["source"] = list(
name = C.parameters["name"], //name of the mob | obj
job = C.parameters["job"] //job of the mob | obj
)
// -- Determine race of orator --
var/mobtype = C.parameters["mobtype"]
var/race // The actual race of the mob
if(ispath(mobtype, /mob/living/carbon/human) || ispath(mobtype, /mob/living/brain))
race = "Humanoid"
else if(ispath(mobtype, /mob/living/simple_animal/slime))
race = "Slime" // NT knows a lot about slimes, but not aliens. Can identify slimes
else if(ispath(mobtype, /mob/living/carbon/monkey))
race = "Monkey"
else if(ispath(mobtype, /mob/living/silicon) || C.parameters["job"] == "AI")
race = "Artificial Life" // sometimes M gets deleted prematurely for AIs... just check the job
else if(isobj(mobtype))
race = "Machinery"
else if(ispath(mobtype, /mob/living/simple_animal))
race = "Domestic Animal"
else
dat += "<br>No servers detected. Scan for servers: <a href='?src=[REF(src)];operation=scan'>\[Scan\]</a>"
race = "Unidentifiable"
data["race"] = race
// based on [/atom/movable/proc/lang_treat]
var/message = C.parameters["message"]
var/language = C.parameters["language"]
// --- Viewing Server ---
if(universal_translate || user.has_language(language))
message = message
else if(!user.has_language(language))
var/datum/language/D = GLOB.language_datum_instances[language]
message = D.scramble(message)
else if(language)
message = "(unintelligible)"
if(1)
dat += "<br>[temp]<br>"
dat += "<center><a href='?src=[REF(src)];operation=mainmenu'>\[Main Menu\]</a> <a href='?src=[REF(src)];operation=refresh'>\[Refresh\]</a></center>"
dat += "<br>Current Network: [network]"
dat += "<br>Selected Server: [SelectedServer.id]"
data["message"] = message
if(SelectedServer.totaltraffic >= 1024)
dat += "<br>Total recorded traffic: [round(SelectedServer.totaltraffic / 1024)] Terrabytes<br><br>"
else
dat += "<br>Total recorded traffic: [SelectedServer.totaltraffic] Gigabytes<br><br>"
else if(C.input_type == "Execution Error")
data["message"] = C.parameters["message"]
else
data["message"] = "(unintelligible)"
data_out["selected_logs"] += list(data)
return data_out
dat += "Stored Logs: <ol>"
var/i = 0
for(var/datum/comm_log_entry/C in SelectedServer.log_entries)
i++
// If the log is a speech file
if(C.input_type == "Speech File")
dat += "<li><font color = #008F00>[C.name]</font> <font color = #FF0000><a href='?src=[REF(src)];delete=[i]'>\[X\]</a></font><br>"
// -- Determine race of orator --
var/mobtype = C.parameters["mobtype"]
var/race // The actual race of the mob
if(ispath(mobtype, /mob/living/carbon/human) || ispath(mobtype, /mob/living/brain))
race = "Humanoid"
// NT knows a lot about slimes, but not aliens. Can identify slimes
else if(ispath(mobtype, /mob/living/simple_animal/slime))
race = "Slime"
else if(ispath(mobtype, /mob/living/carbon/monkey))
race = "Monkey"
// sometimes M gets deleted prematurely for AIs... just check the job
else if(ispath(mobtype, /mob/living/silicon) || C.parameters["job"] == "AI")
race = "Artificial Life"
else if(isobj(mobtype))
race = "Machinery"
else if(ispath(mobtype, /mob/living/simple_animal))
race = "Domestic Animal"
else
race = "<i>Unidentifiable</i>"
dat += "<u><font color = #18743E>Data type</font></u>: [C.input_type]<br>"
dat += "<u><font color = #18743E>Source</font></u>: [C.parameters["name"]] (Job: [C.parameters["job"]])<br>"
dat += "<u><font color = #18743E>Class</font></u>: [race]<br>"
var/message = C.parameters["message"]
var/language = C.parameters["language"]
// based on [/atom/movable/proc/lang_treat]
if (universal_translate || user.has_language(language))
message = "\"[message]\""
else if (!user.has_language(language))
var/datum/language/D = GLOB.language_datum_instances[language]
message = "\"[D.scramble(message)]\""
else if (language)
message = "<i>(unintelligible)</i>"
dat += "<u><font color = #18743E>Contents</font></u>: [message]<br>"
dat += "</li><br>"
else if(C.input_type == "Execution Error")
dat += "<li><font color = #990000>[C.name]</font> <a href='?src=[REF(src)];delete=[i]'>\[X\]</a><br>"
dat += "<u><font color = #787700>Error</font></u>: \"[C.parameters["message"]]\"<br>"
dat += "</li><br>"
else
dat += "<li><font color = #000099>[C.name]</font> <a href='?src=[REF(src)];delete=[i]'>\[X\]</a><br>"
dat += "<u><font color = #18743E>Data type</font></u>: [C.input_type]<br>"
dat += "<u><font color = #18743E>Contents</font></u>: <i>(unintelligible)</i><br>"
dat += "</li><br>"
dat += "</ol>"
user << browse(dat, "window=comm_monitor;size=575x400")
onclose(user, "server_control")
temp = ""
return
/obj/machinery/computer/telecomms/server/Topic(href, href_list)
/obj/machinery/computer/telecomms/server/ui_act(action, params)
if(..())
return
add_fingerprint(usr)
usr.set_machine(src)
if(href_list["viewserver"])
screen = 1
for(var/obj/machinery/telecomms/T in servers)
if(T.id == href_list["viewserver"])
SelectedServer = T
break
if(href_list["operation"])
switch(href_list["operation"])
if("release")
servers = list()
screen = 0
if("mainmenu")
screen = 0
if("scan")
if(servers.len > 0)
temp = "<font color = #D70B00>- FAILED: CANNOT PROBE WHEN BUFFER FULL -</font color>"
else
for(var/obj/machinery/telecomms/server/T in urange(25, src))
if(T.network == network)
servers.Add(T)
if(!servers.len)
temp = "<font color = #D70B00>- FAILED: UNABLE TO LOCATE SERVERS IN \[[network]\] -</font color>"
else
temp = "<font color = #336699>- [servers.len] SERVERS PROBED & BUFFERED -</font color>"
screen = 0
if(href_list["delete"])
if(!src.allowed(usr) && !(obj_flags & EMAGGED))
to_chat(usr, "<span class='danger'>ACCESS DENIED.</span>")
switch(action)
if("mainmenu")
SelectedMachine = null
notice = ""
return
if("release")
machinelist = list()
notice = ""
return
if("network") //network change, flush the selected machine and buffer
var/newnet = sanitize(sanitize_text(params["value"], network))
if(length(newnet) > 15) //i'm looking at you, you href fuckers
notice = "FAILED: Network tag string too lengthy"
return
network = newnet
SelectedMachine = null
machinelist = list()
return
if("probe")
if(LAZYLEN(machinelist) > 0)
notice = "FAILED: Cannot probe when buffer full"
return
for(var/obj/machinery/telecomms/T in GLOB.telecomms_list) //telecomms just went global!
if(T.network == network)
LAZYADD(machinelist, T)
if(SelectedServer)
if(!LAZYLEN(machinelist))
notice = "FAILED: Unable to locate network entities in \[[network]\]"
return
if("viewmachine")
for(var/obj/machinery/telecomms/T in machinelist)
if(T.id == params["value"])
SelectedMachine = T
break
if("delete")
if(!src.allowed(usr) && !CHECK_BITFIELD(obj_flags, EMAGGED))
to_chat(usr, "<span class='danger'>ACCESS DENIED.</span>")
return
var/datum/comm_log_entry/D = SelectedServer.log_entries[text2num(href_list["delete"])]
temp = "<font color = #336699>- DELETED ENTRY: [D.name] -</font color>"
SelectedServer.log_entries.Remove(D)
if(!SelectedMachine)
notice = "ALERT: No server detected. Server may be nonresponsive."
return
var/datum/comm_log_entry/D = locate(params["value"])
if(!istype(D))
notice = "NOTICE: Object not found"
return
notice = "Deleted entry: [D.name]"
LAZYREMOVE(SelectedMachine.log_entries, D)
qdel(D)
else
temp = "<font color = #D70B00>- FAILED: NO SELECTED MACHINE -</font color>"
if(href_list["network"])
var/newnet = stripped_input(usr, "Which network do you want to view?", "Comm Monitor", network)
if(newnet && ((usr in range(1, src)) || hasSiliconAccessInArea(usr)))
if(length(newnet) > 15)
temp = "<font color = #D70B00>- FAILED: NETWORK TAG STRING TOO LENGHTLY -</font color>"
else
network = newnet
screen = 0
servers = list()
temp = "<font color = #336699>- NEW NETWORK TAG SET IN ADDRESS \[[network]\] -</font color>"
updateUsrDialog()
return
/obj/machinery/computer/telecomms/server/attackby()
. = ..()
updateUsrDialog()
+315 -382
View File
@@ -3,241 +3,360 @@
Lets you read PDA and request console messages.
*/
#define LINKED_SERVER_NONRESPONSIVE (!linkedServer || (linkedServer.stat & (NOPOWER|BROKEN)))
// The monitor itself.
/obj/machinery/computer/message_monitor
name = "message monitor console"
desc = "Used to monitor the crew's PDA messages, as well as request console messages."
icon_screen = "comm_logs"
circuit = /obj/item/circuitboard/computer/message_monitor
//Server linked to.
//Servers, and server linked to.
var/network = "tcommsat" // the network to probe
var/list/machinelist = list() // the servers located by the computer
var/obj/machinery/telecomms/message_server/linkedServer = null
//Sparks effect - For emag
var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread
//Messages - Saves me time if I want to change something.
var/noserver = "<span class='alert'>ALERT: No server detected.</span>"
var/incorrectkey = "<span class='warning'>ALERT: Incorrect decryption key!</span>"
var/defaultmsg = "<span class='notice'>Welcome. Please select an option.</span>"
var/rebootmsg = "<span class='warning'>%$&(: Critical %$$@ Error // !RestArting! <lOadiNg backUp iNput ouTput> - ?pLeaSe wAit!</span>"
var/noserver = "ALERT: No server detected. Server may be nonresponsive."
var/incorrectkey = "ALERT: Incorrect decryption key!"
var/rebootmsg = "%$(:SYS&EM INTRN@L ACfES VIOLTIa█ DEtE₡TED! Ree3ARcinG A█ BAaKUP RdSTRE PbINT \[0xcff32ca/ - PLfASE aAIT"
//Computer properties
var/screen = 0 // 0 = Main menu, 1 = Message Logs, 2 = Hacked screen, 3 = Custom Message
var/hacking = FALSE // Is it being hacked into by the AI/Cyborg
var/message = "<span class='notice'>System bootup complete. Please select an option.</span>" // The message that shows on the main menu.
var/auth = FALSE // Are they authenticated?
var/optioncount = 7
var/message = "" // The message that shows on the main menu.
var/auth = FALSE // Are they authenticated?
// Custom Message Properties
var/customsender = "System Administrator"
var/obj/item/pda/customrecepient = null
var/customsender = "System Administrator"
var/customjob = "Admin"
var/custommessage = "This is a test, please ignore."
light_color = LIGHT_COLOR_GREEN
/obj/machinery/computer/message_monitor/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE,\
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "telepdalog", name, 727, 510, master_ui, state)
ui.open()
/obj/machinery/computer/message_monitor/ui_static_data(mob/user)
var/list/data_out = list()
if(!linkedServer || !auth) // no need building this if the usr isn't authenticated
return data_out
data_out["recon_logs"] = list()
var/i1 = 0
for(var/datum/data_rc_msg/rc in linkedServer.rc_msgs)
i1++
if(i1 > 3000)
break
var/list/data = list(
sender = rc.send_dpt,
recipient = rc.rec_dpt,
message = rc.message,
stamp = rc.stamp,
auth = rc.id_auth,
priority = rc.priority,
ref = REF(rc)
)
data_out["recon_logs"] += list(data)
data_out["message_logs"] = list()
var/i2 = 0
for(var/datum/data_pda_msg/pda in linkedServer.pda_msgs)
i2++
if(i2 > 3000)
break
var/list/data = list(
sender = pda.sender,
recipient = pda.recipient,
message = pda.message,
picture = pda.picture ? TRUE : FALSE,
ref = REF(pda)
)
data_out["message_logs"] += list(data)
return data_out
/obj/machinery/computer/message_monitor/ui_data(mob/user)
var/list/data_out = list()
data_out["notice"] = message
data_out["authenticated"] = auth
data_out["network"] = network
var/mob/living/silicon/S = user
if(istype(S) && S.hack_software)
data_out["canhack"] = TRUE
data_out["hacking"] = (hacking || CHECK_BITFIELD(obj_flags, EMAGGED))
if(hacking)
data_out["borg"] = ((isAI(user) || iscyborg(user)) && !CHECK_BITFIELD(obj_flags, EMAGGED)) //even borgs can't read emag
return data_out
data_out["servers"] = list()
for(var/obj/machinery/telecomms/message_server/T in machinelist)
var/list/data = list(
name = T.name,
id = T.id,
ref = REF(T)
)
data_out["servers"] += list(data) // This /might/ cause an oom. Too bad!
data_out["servers"] = sortList(data_out["servers"]) //a-z sort
data_out["fake_message"] = list(
sender = customsender,
job = customjob,
message = custommessage,
recepient = (customrecepient ? "[customrecepient.owner] ([customrecepient.ownjob])" : null)
)
if(!linkedServer)
data_out["selected"] = null
return data_out
data_out["selected"] = list(
name = linkedServer.name,
id = linkedServer.id,
ref = REF(linkedServer),
status = (linkedServer.on && (linkedServer.toggled != FALSE)) // returns true if server is running
)
return data_out
/obj/machinery/computer/message_monitor/ui_act(action, params)
if(..())
return
switch(action)
if("mainmenu") //deselect
linkedServer = null
auth = FALSE
message = ""
return
if("release") //release server listing
machinelist = list()
message = ""
return
if("network") //network change, flush the selected machine and buffer, and de-auth them, if blank, return default
var/newnet = sanitize(sanitize_text(params["value"], network))
if(length(newnet) > 15) //i'm looking at you, you href fuckers
message = "FAILED: Network tag string too lengthy"
return
network = newnet
linkedServer = null
machinelist = list()
auth = FALSE
message = "NOTICE: Network change detected. Server disconnected, please re-authenticate."
return
if("probe") //probe network for the pda serbs
if(LAZYLEN(machinelist) > 0)
message = "FAILED: Cannot probe when buffer full"
return
for(var/obj/machinery/telecomms/message_server/T in GLOB.telecomms_list)
if(T.network == network)
LAZYADD(machinelist, T)
if(!LAZYLEN(machinelist))
message = "FAILED: Unable to locate network entities in \[[network]\]"
return
if("viewmachine") //selected but not authorized
for(var/obj/machinery/telecomms/message_server/T in machinelist)
if(T.id == params["value"])
linkedServer = T
break
if("auth")
if(!(linkedServer.on && (linkedServer.toggled != FALSE)))
message = noserver
return
if(auth)
auth = FALSE
update_static_data(usr) //make sure it's cleared!
return
var/dkey = stripped_input(usr, "Please enter the decryption key.")
if(dkey && dkey == "")
return
if(linkedServer.decryptkey == dkey)
auth = TRUE
else
message = incorrectkey
update_static_data(usr)
if("change_auth")
if(!auth)
message = "WARNING: Auth failed! Please log in to change the password!"
return
else if(!(linkedServer.on && (linkedServer.toggled != FALSE)))
message = noserver
return
var/dkey = stripped_input(usr, "Please enter the old decryption key.")
if(dkey && dkey != "")
if(linkedServer.decryptkey == dkey)
var/newkey = stripped_input(usr, "Please enter the new key (3 - 20 characters max):")
if(!ISINRANGE(length(newkey), 3, 20))
message = "NOTICE: Decryption key length too long/short!"
return
if(newkey && newkey != "")
linkedServer.decryptkey = newkey
message = "NOTICE: Decryption key set."
return
message = incorrectkey
if("hack")
if(!(linkedServer.on && (linkedServer.toggled != FALSE)))
message = noserver
return
var/mob/living/silicon/S = usr
if(istype(S) && S.hack_software)
hacking = TRUE
//Time it takes to bruteforce is dependant on the password length.
addtimer(CALLBACK(src, .proc/BruteForce, usr), (10 SECONDS) * length(linkedServer.decryptkey))
if("del_log")
if(!auth)
message = "WARNING: Auth failed! Delete aborted!"
return
else if(!(linkedServer.on && (linkedServer.toggled != FALSE)))
message = noserver
return
var/datum/data_ref = locate(params["ref"])
if(istype(data_ref, /datum/data_rc_msg))
LAZYREMOVE(linkedServer.rc_msgs, data_ref)
message = "NOTICE: Log Deleted!"
else if(istype(data_ref, /datum/data_pda_msg))
LAZYREMOVE(linkedServer.pda_msgs, data_ref)
message = "NOTICE: Log Deleted!"
else
message = "NOTICE: Log not found! It may have already been deleted"
update_static_data(usr)
if("clear_log")
if(!auth)
message = "WARNING: Auth failed! Delete aborted!"
return
else if(!(linkedServer.on && (linkedServer.toggled != FALSE)))
message = noserver
return
var/what = params["value"]
if(what == "pda_logs")
linkedServer.pda_msgs = list()
if(what == "rc_msgs")
linkedServer.rc_msgs = list()
update_static_data(usr)
if("fake")
if(!auth)
message = "WARNING: Auth failed!"
return
else if(!(linkedServer.on && (linkedServer.toggled != FALSE)))
message = noserver
return
if("reset" in params)
ResetMessage()
return
if("send" in params)
if(isnull(customrecepient))
message = "NOTICE: No recepient selected!"
return
if(length(custommessage) <= 0 || custommessage == "")
message = "NOTICE: No message entered!"
return
if(length(customjob) <= 0 || customjob == "")
customjob = "Admin"
return
if(length(customsender) <= 0 || customsender == "")
customsender = "UNKNOWN"
//sanitize text!!!
var/datum/signal/subspace/pda/signal = new(src, list(
"name" = sanitize(customsender),
"job" = sanitize(customjob),
"message" = sanitize(custommessage),
"emojis" = TRUE,
"targets" = list("[customrecepient.owner] ([customrecepient.ownjob])")
))
// this will log the signal and transmit it to the target
linkedServer.receive_information(signal, null)
usr.log_message("(PDA: [name] | [usr.real_name]) sent \"[sanitize(custommessage)]\" to [signal.format_target()]", LOG_PDA)
message = ""
return
// Do not check if it's blank yet
// But do check if it's above our set limit (for people who manualy send hrefs at us!)
if("sender" in params)
var/S = params["sender"]
if(length(S) > MAX_NAME_LEN)
message = "FAILED: Job string too lengthy"
return
customsender = S
return
if("job" in params)
var/J = params["job"]
if(length(J) > 100)
message = "FAILED: Job string too lengthy"
return
customjob = J
return
if("message" in params)
var/M = params["message"]
if(length(M) > MAX_MESSAGE_LEN)
message = "FAILED: Message string too lengthy"
return
custommessage = M
return
if("recepient" in params)
// Get out list of viable PDAs
var/list/obj/item/pda/sendPDAs = get_viewable_pdas()
if(GLOB.PDAs && LAZYLEN(GLOB.PDAs) > 0)
customrecepient = input(usr, "Select a PDA from the list.") as null|anything in sortNames(sendPDAs)
else
customrecepient = null
return
if("refresh")
update_static_data(usr)
/obj/machinery/computer/message_monitor/attackby(obj/item/O, mob/living/user, params)
if(istype(O, /obj/item/screwdriver) && (obj_flags & EMAGGED))
//Stops people from just unscrewing the monitor and putting it back to get the console working again.
if(istype(O, /obj/item/screwdriver) && CHECK_BITFIELD(obj_flags, EMAGGED))
//Stops people from just unscrewing the monitor and putting it back to get the console working again.
//Why this though, you should make it emag to a board level. (i wont do it)
to_chat(user, "<span class='warning'>It is too hot to mess with!</span>")
else
return ..()
/obj/machinery/computer/message_monitor/emag_act(mob/user)
. = ..()
if(obj_flags & EMAGGED)
if(CHECK_BITFIELD(obj_flags, EMAGGED))
return
if(isnull(linkedServer))
to_chat(user, "<span class='notice'>A no server error appears on the screen.</span>")
return
obj_flags |= EMAGGED
screen = 2
ENABLE_BITFIELD(obj_flags, EMAGGED)
spark_system.set_up(5, 0, src)
spark_system.start()
var/obj/item/paper/monitorkey/MK = new(loc, linkedServer)
// Will help make emagging the console not so easy to get away with.
MK.info += "<br><br><font color='red'>%@%(*$%&(&?*(%&/{}</font>"
var/time = 100 * length(linkedServer.decryptkey)
addtimer(CALLBACK(src, .proc/UnmagConsole), time)
message = rebootmsg
addtimer(CALLBACK(src, .proc/UnmagConsole), (10 SECONDS) * length(linkedServer.decryptkey))
//message = rebootmsg
return TRUE
/obj/machinery/computer/message_monitor/New()
. = ..()
GLOB.telecomms_list += src
/obj/machinery/computer/message_monitor/Initialize()
..()
return INITIALIZE_HINT_LATELOAD
/obj/machinery/computer/message_monitor/LateInitialize()
//Is the server isn't linked to a server, and there's a server available, default it to the first one in the list.
if(!linkedServer)
for(var/obj/machinery/telecomms/message_server/S in GLOB.telecomms_list)
linkedServer = S
break
/obj/machinery/computer/message_monitor/Destroy()
GLOB.telecomms_list -= src
. = ..()
/obj/machinery/computer/message_monitor/ui_interact(mob/living/user)
. = ..()
//If the computer is being hacked or is emagged, display the reboot message.
if(hacking || (obj_flags & EMAGGED))
message = rebootmsg
var/dat = "<center><font color='blue'[message]</font></center>"
if(auth)
dat += "<h4><dd><A href='?src=[REF(src)];auth=1'>&#09;<font color='green'>\[Authenticated\]</font></a>&#09;/"
dat += " Server Power: <A href='?src=[REF(src)];active=1'>[linkedServer && linkedServer.toggled ? "<font color='green'>\[On\]</font>":"<font color='red'>\[Off\]</font>"]</a></h4>"
else
dat += "<h4><dd><A href='?src=[REF(src)];auth=1'>&#09;<font color='red'>\[Unauthenticated\]</font></a>&#09;/"
dat += " Server Power: <u>[linkedServer && linkedServer.toggled ? "<font color='green'>\[On\]</font>":"<font color='red'>\[Off\]</font>"]</u></h4>"
if(hacking || (obj_flags & EMAGGED))
screen = 2
else if(!auth || LINKED_SERVER_NONRESPONSIVE)
if(LINKED_SERVER_NONRESPONSIVE)
message = noserver
screen = 0
switch(screen)
//Main menu
if(0)
//&#09; = TAB
var/i = 0
dat += "<dd><A href='?src=[REF(src)];find=1'>&#09;[++i]. Link To A Server</a></dd>"
if(auth)
if(LINKED_SERVER_NONRESPONSIVE)
dat += "<dd><A>&#09;ERROR: Server not found!</A><br></dd>"
else
dat += "<dd><A href='?src=[REF(src)];view_logs=1'>&#09;[++i]. View Message Logs </a><br></dd>"
dat += "<dd><A href='?src=[REF(src)];view_requests=1'>&#09;[++i]. View Request Console Logs </a></br></dd>"
dat += "<dd><A href='?src=[REF(src)];clear_logs=1'>&#09;[++i]. Clear Message Logs</a><br></dd>"
dat += "<dd><A href='?src=[REF(src)];clear_requests=1'>&#09;[++i]. Clear Request Console Logs</a><br></dd>"
dat += "<dd><A href='?src=[REF(src)];pass=1'>&#09;[++i]. Set Custom Key</a><br></dd>"
dat += "<dd><A href='?src=[REF(src)];msg=1'>&#09;[++i]. Send Admin Message</a><br></dd>"
else
for(var/n = ++i; n <= optioncount; n++)
dat += "<dd><font color='blue'>&#09;[n]. ---------------</font><br></dd>"
var/mob/living/silicon/S = usr
if(istype(S) && S.hack_software)
//Malf/Traitor AIs can bruteforce into the system to gain the Key.
dat += "<dd><A href='?src=[REF(src)];hack=1'><i><font color='Red'>*&@#. Bruteforce Key</font></i></font></a><br></dd>"
else
dat += "<br>"
//Bottom message
if(!auth)
dat += "<br><hr><dd><span class='notice'>Please authenticate with the server in order to show additional options.</span>"
else
dat += "<br><hr><dd><span class='warning'>Reg, #514 forbids sending messages to a Head of Staff containing Erotic Rendering Properties.</span>"
//Message Logs
if(1)
var/index = 0
dat += "<center><A href='?src=[REF(src)];back=1'>Back</a> - <A href='?src=[REF(src)];refresh=1'>Refresh</a></center><hr>"
dat += "<table border='1' width='100%'><tr><th width = '5%'>X</th><th width='15%'>Sender</th><th width='15%'>Recipient</th><th width='300px' word-wrap: break-word>Message</th></tr>"
for(var/datum/data_pda_msg/pda in linkedServer.pda_msgs)
index++
if(index > 3000)
break
// Del - Sender - Recepient - Message
// X - Al Green - Your Mom - WHAT UP!?
dat += "<tr><td width = '5%'><center><A href='?src=[REF(src)];delete_logs=[REF(pda)]' style='color: rgb(255,0,0)'>X</a></center></td><td width='15%'>[pda.sender]</td><td width='15%'>[pda.recipient]</td><td width='300px'>[pda.message][pda.picture ? " <a href='byond://?src=[REF(pda)];photo=1'>(Photo)</a>":""]</td></tr>"
dat += "</table>"
//Hacking screen.
if(2)
if(isAI(user) || iscyborg(user))
dat += "Brute-forcing for server key.<br> It will take 20 seconds for every character that the password has."
dat += "In the meantime, this console can reveal your true intentions if you let someone access it. Make sure no humans enter the room during that time."
else
//It's the same message as the one above but in binary. Because robots understand binary and humans don't... well I thought it was clever.
dat += {"01000010011100100111010101110100011001010010110<br>
10110011001101111011100100110001101101001011011100110011<br>
10010000001100110011011110111001000100000011100110110010<br>
10111001001110110011001010111001000100000011010110110010<br>
10111100100101110001000000100100101110100001000000111011<br>
10110100101101100011011000010000001110100011000010110101<br>
10110010100100000001100100011000000100000011100110110010<br>
10110001101101111011011100110010001110011001000000110011<br>
00110111101110010001000000110010101110110011001010111001<br>
00111100100100000011000110110100001100001011100100110000<br>
10110001101110100011001010111001000100000011101000110100<br>
00110000101110100001000000111010001101000011001010010000<br>
00111000001100001011100110111001101110111011011110111001<br>
00110010000100000011010000110000101110011001011100010000<br>
00100100101101110001000000111010001101000011001010010000<br>
00110110101100101011000010110111001110100011010010110110<br>
10110010100101100001000000111010001101000011010010111001<br>
10010000001100011011011110110111001110011011011110110110<br>
00110010100100000011000110110000101101110001000000111001<br>
00110010101110110011001010110000101101100001000000111100<br>
10110111101110101011100100010000001110100011100100111010<br>
10110010100100000011010010110111001110100011001010110111<br>
00111010001101001011011110110111001110011001000000110100<br>
10110011000100000011110010110111101110101001000000110110<br>
00110010101110100001000000111001101101111011011010110010<br>
10110111101101110011001010010000001100001011000110110001<br>
10110010101110011011100110010000001101001011101000010111<br>
00010000001001101011000010110101101100101001000000111001<br>
10111010101110010011001010010000001101110011011110010000<br>
00110100001110101011011010110000101101110011100110010000<br>
00110010101101110011101000110010101110010001000000111010<br>
00110100001100101001000000111001001101111011011110110110<br>
10010000001100100011101010111001001101001011011100110011<br>
10010000001110100011010000110000101110100001000000111010<br>
001101001011011010110010100101110"}
//Fake messages
if(3)
dat += "<center><A href='?src=[REF(src)];back=1'>Back</a> - <A href='?src=[REF(src)];Reset=1'>Reset</a></center><hr>"
dat += {"<table border='1' width='100%'>
<tr><td width='20%'><A href='?src=[REF(src)];select=Sender'>Sender</a></td>
<td width='20%'><A href='?src=[REF(src)];select=RecJob'>Sender's Job</a></td>
<td width='20%'><A href='?src=[REF(src)];select=Recepient'>Recipient</a></td>
<td width='300px' word-wrap: break-word><A href='?src=[REF(src)];select=Message'>Message</a></td></tr>"}
//Sender - Sender's Job - Recepient - Message
//Al Green- Your Dad - Your Mom - WHAT UP!?
dat += {"<tr><td width='20%'>[customsender]</td>
<td width='20%'>[customjob]</td>
<td width='20%'>[customrecepient ? customrecepient.owner : "NONE"]</td>
<td width='300px'>[custommessage]</td></tr>"}
dat += "</table><br><center><A href='?src=[REF(src)];select=Send'>Send</a>"
//Request Console Logs
if(4)
var/index = 0
/* data_rc_msg
X - 5%
var/rec_dpt = "Unspecified" //name of the person - 15%
var/send_dpt = "Unspecified" //name of the sender- 15%
var/message = "Blank" //transferred message - 300px
var/stamp = "Unstamped" - 15%
var/id_auth = "Unauthenticated" - 15%
var/priority = "Normal" - 10%
*/
dat += "<center><A href='?src=[REF(src)];back=1'>Back</a> - <A href='?src=[REF(src)];refresh=1'>Refresh</a></center><hr>"
dat += {"<table border='1' width='100%'><tr><th width = '5%'>X</th><th width='15%'>Sending Dep.</th><th width='15%'>Receiving Dep.</th>
<th width='300px' word-wrap: break-word>Message</th><th width='15%'>Stamp</th><th width='15%'>ID Auth.</th><th width='15%'>Priority.</th></tr>"}
for(var/datum/data_rc_msg/rc in linkedServer.rc_msgs)
index++
if(index > 3000)
break
// Del - Sender - Recepient - Message
// X - Al Green - Your Mom - WHAT UP!?
dat += {"<tr><td width = '5%'><center><A href='?src=[REF(src)];delete_requests=[REF(rc)]' style='color: rgb(255,0,0)'>X</a></center></td><td width='15%'>[rc.send_dpt]</td>
<td width='15%'>[rc.rec_dpt]</td><td width='300px'>[rc.message]</td><td width='15%'>[rc.stamp]</td><td width='15%'>[rc.id_auth]</td><td width='15%'>[rc.priority]</td></tr>"}
dat += "</table>"
message = defaultmsg
var/datum/browser/popup = new(user, "hologram_console", name, 700, 700)
popup.set_content(dat)
popup.set_title_image(user.browse_rsc_icon(icon, icon_state))
popup.open()
/obj/machinery/computer/message_monitor/proc/BruteForce(mob/user)
if(isnull(linkedServer))
to_chat(user, "<span class='warning'>Could not complete brute-force: Linked Server Disconnected!</span>")
@@ -245,10 +364,11 @@
var/currentKey = linkedServer.decryptkey
to_chat(user, "<span class='warning'>Brute-force completed! The key is '[currentKey]'.</span>")
hacking = FALSE
screen = 0 // Return the screen back to normal
message = ""
/obj/machinery/computer/message_monitor/proc/UnmagConsole()
obj_flags &= ~EMAGGED
DISABLE_BITFIELD(obj_flags, EMAGGED)
message = ""
/obj/machinery/computer/message_monitor/proc/ResetMessage()
customsender = "System Administrator"
@@ -256,199 +376,12 @@
custommessage = "This is a test, please ignore."
customjob = "Admin"
/obj/machinery/computer/message_monitor/Topic(href, href_list)
if(..())
return
if(usr.contents.Find(src) || (in_range(src, usr) && isturf(loc)) || hasSiliconAccessInArea(usr))
//Authenticate
if (href_list["auth"])
if(LINKED_SERVER_NONRESPONSIVE)
message = noserver
else if(auth)
auth = FALSE
screen = 0
else
var/dkey = trim(input(usr, "Please enter the decryption key.") as text|null)
if(dkey && dkey != "")
if(linkedServer.decryptkey == dkey)
auth = TRUE
else
message = incorrectkey
//Turn the server on/off.
if (href_list["active"])
if(LINKED_SERVER_NONRESPONSIVE)
message = noserver
else if(auth)
linkedServer.toggled = !linkedServer.toggled
//Find a server
if (href_list["find"])
var/list/message_servers = list()
for (var/obj/machinery/telecomms/message_server/M in GLOB.telecomms_list)
message_servers += M
if(message_servers.len > 1)
linkedServer = input(usr, "Please select a server.", "Select a server.", null) as null|anything in message_servers
message = "<span class='alert'>NOTICE: Server selected.</span>"
else if(message_servers.len > 0)
linkedServer = message_servers[1]
message = "<span class='notice'>NOTICE: Only Single Server Detected - Server selected.</span>"
else
message = noserver
//View the logs - KEY REQUIRED
if (href_list["view_logs"])
if(LINKED_SERVER_NONRESPONSIVE)
message = noserver
else if(auth)
screen = 1
//Clears the logs - KEY REQUIRED
if (href_list["clear_logs"])
if(LINKED_SERVER_NONRESPONSIVE)
message = noserver
else if(auth)
linkedServer.pda_msgs = list()
message = "<span class='notice'>NOTICE: Logs cleared.</span>"
//Clears the request console logs - KEY REQUIRED
if (href_list["clear_requests"])
if(LINKED_SERVER_NONRESPONSIVE)
message = noserver
else if(auth)
linkedServer.rc_msgs = list()
message = "<span class='notice'>NOTICE: Logs cleared.</span>"
//Change the password - KEY REQUIRED
if (href_list["pass"])
if(LINKED_SERVER_NONRESPONSIVE)
message = noserver
else if(auth)
var/dkey = stripped_input(usr, "Please enter the decryption key.")
if(dkey && dkey != "")
if(linkedServer.decryptkey == dkey)
var/newkey = trim(input(usr,"Please enter the new key (3 - 16 characters max):"))
if(length(newkey) <= 3)
message = "<span class='notice'>NOTICE: Decryption key too short!</span>"
else if(length(newkey) > 16)
message = "<span class='notice'>NOTICE: Decryption key too long!</span>"
else if(newkey && newkey != "")
linkedServer.decryptkey = newkey
message = "<span class='notice'>NOTICE: Decryption key set.</span>"
else
message = incorrectkey
//Hack the Console to get the password
if (href_list["hack"])
var/mob/living/silicon/S = usr
if(istype(S) && S.hack_software)
hacking = TRUE
screen = 2
//Time it takes to bruteforce is dependant on the password length.
spawn(100*length(linkedServer.decryptkey))
if(src && linkedServer && usr)
BruteForce(usr)
//Delete the log.
if (href_list["delete_logs"])
//Are they on the view logs screen?
if(screen == 1)
if(LINKED_SERVER_NONRESPONSIVE)
message = noserver
else //if(istype(href_list["delete_logs"], /datum/data_pda_msg))
linkedServer.pda_msgs -= locate(href_list["delete_logs"])
message = "<span class='notice'>NOTICE: Log Deleted!</span>"
//Delete the request console log.
if (href_list["delete_requests"])
//Are they on the view logs screen?
if(screen == 4)
if(LINKED_SERVER_NONRESPONSIVE)
message = noserver
else //if(istype(href_list["delete_logs"], /datum/data_pda_msg))
linkedServer.rc_msgs -= locate(href_list["delete_requests"])
message = "<span class='notice'>NOTICE: Log Deleted!</span>"
//Create a custom message
if (href_list["msg"])
if(LINKED_SERVER_NONRESPONSIVE)
message = noserver
else if(auth)
screen = 3
//Fake messaging selection - KEY REQUIRED
if (href_list["select"])
if(LINKED_SERVER_NONRESPONSIVE)
message = noserver
screen = 0
else
switch(href_list["select"])
//Reset
if("Reset")
ResetMessage()
//Select Your Name
if("Sender")
customsender = stripped_input(usr, "Please enter the sender's name.") || customsender
//Select Receiver
if("Recepient")
//Get out list of viable PDAs
var/list/obj/item/pda/sendPDAs = get_viewable_pdas()
if(GLOB.PDAs && GLOB.PDAs.len > 0)
customrecepient = input(usr, "Select a PDA from the list.") as null|anything in sortNames(sendPDAs)
else
customrecepient = null
//Enter custom job
if("RecJob")
customjob = stripped_input(usr, "Please enter the sender's job.") || customjob
//Enter message
if("Message")
custommessage = stripped_input(usr, "Please enter your message.") || custommessage
//Send message
if("Send")
if(isnull(customsender) || customsender == "")
customsender = "UNKNOWN"
if(isnull(customrecepient))
message = "<span class='notice'>NOTICE: No recepient selected!</span>"
return attack_hand(usr)
if(isnull(custommessage) || custommessage == "")
message = "<span class='notice'>NOTICE: No message entered!</span>"
return attack_hand(usr)
var/datum/signal/subspace/pda/signal = new(src, list(
"name" = "[customsender]",
"job" = "[customjob]",
"message" = custommessage,
"emojis" = TRUE,
"targets" = list("[customrecepient.owner] ([customrecepient.ownjob])")
))
// this will log the signal and transmit it to the target
linkedServer.receive_information(signal, null)
usr.log_message("(PDA: [name] | [usr.real_name]) sent \"[custommessage]\" to [signal.format_target()]", LOG_PDA)
//Request Console Logs - KEY REQUIRED
if(href_list["view_requests"])
if(LINKED_SERVER_NONRESPONSIVE)
message = noserver
else if(auth)
screen = 4
if (href_list["back"])
screen = 0
return attack_hand(usr)
#undef LINKED_SERVER_NONRESPONSIVE
/obj/item/paper/monitorkey
name = "monitor decryption key"
/obj/item/paper/monitorkey/Initialize(mapload, obj/machinery/telecomms/message_server/server)
..()
if (server)
if(server)
print(server)
return INITIALIZE_HINT_NORMAL
else
@@ -459,7 +392,7 @@
add_overlay("paper_words")
/obj/item/paper/monitorkey/LateInitialize()
for (var/obj/machinery/telecomms/message_server/server in GLOB.telecomms_list)
if (server.decryptkey)
for(var/obj/machinery/telecomms/message_server/server in GLOB.telecomms_list)
if(server.decryptkey)
print(server)
break
@@ -1,4 +1,3 @@
/*
Telecomms monitor tracks the overall trafficing of a telecommunications network
and displays a heirarchy of linked machines.
@@ -10,117 +9,98 @@
icon_screen = "comm_monitor"
desc = "Monitors the details of the telecommunications network it's synced with."
var/screen = 0 // the screen number:
var/list/machinelist = list() // the machines located by the computer
var/obj/machinery/telecomms/SelectedMachine
var/obj/machinery/telecomms/SelectedMachine = null
var/network = "NULL" // the network to probe
var/notice = ""
var/temp = "" // temporary feedback messages
circuit = /obj/item/circuitboard/computer/comm_monitor
/obj/machinery/computer/telecomms/monitor/ui_interact(mob/user)
. = ..()
var/dat = "<TITLE>Telecommunications Monitor</TITLE><center><b>Telecommunications Monitor</b></center>"
/obj/machinery/computer/telecomms/monitor/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE,\
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
switch(screen)
if(!ui)
ui = new(user, src, ui_key, "telemonitor", name, 575, 400, master_ui, state)
ui.open()
/obj/machinery/computer/telecomms/monitor/ui_data(mob/user)
var/list/data_out = list()
data_out["network"] = network
data_out["notice"] = notice
// --- Main Menu ---
data_out["servers"] = list()
for(var/obj/machinery/telecomms/T in machinelist)
var/list/data = list(
name = T.name,
id = T.id,
ref = REF(T)
)
data_out["servers"] += list(data)
data_out["servers"] = sortList(data_out["servers"])
if(0)
dat += "<br>[temp]<br><br>"
dat += "<br>Current Network: <a href='?src=[REF(src)];network=1'>[network]</a><br>"
if(machinelist.len)
dat += "<br>Detected Network Entities:<ul>"
for(var/obj/machinery/telecomms/T in machinelist)
dat += "<li><a href='?src=[REF(src)];viewmachine=[T.id]'>[REF(T)] [T.name]</a> ([T.id])</li>"
dat += "</ul>"
dat += "<br><a href='?src=[REF(src)];operation=release'>\[Flush Buffer\]</a>"
else
dat += "<a href='?src=[REF(src)];operation=probe'>\[Probe Network\]</a>"
if(!SelectedMachine) //null is bad.
data_out["selected"] = null //but in js, null is good.
return data_out
data_out["selected"] = list(
name = SelectedMachine.name,
id = SelectedMachine.id,
status = SelectedMachine.on,
traffic = SelectedMachine.traffic,
netspeed = SelectedMachine.netspeed,
freq_listening = SelectedMachine.freq_listening,
long_range_link = SelectedMachine.long_range_link,
ref = REF(SelectedMachine)
)
data_out["selected_servers"] = list()
for(var/obj/machinery/telecomms/T in SelectedMachine.links)
if(!T.hide)
var/list/data = list(
name = T.name,
id = T.id,
ref = REF(T)
)
data_out["selected_servers"] += list(data)
return data_out
// --- Viewing Machine ---
if(1)
dat += "<br>[temp]<br>"
dat += "<center><a href='?src=[REF(src)];operation=mainmenu'>\[Main Menu\]</a></center>"
dat += "<br>Current Network: [network]<br>"
dat += "Selected Network Entity: [SelectedMachine.name] ([SelectedMachine.id])<br>"
dat += "Linked Entities: <ol>"
for(var/obj/machinery/telecomms/T in SelectedMachine.links)
if(!T.hide)
dat += "<li><a href='?src=[REF(src)];viewmachine=[T.id]'>[REF(T.id)] [T.name]</a> ([T.id])</li>"
dat += "</ol>"
user << browse(dat, "window=comm_monitor;size=575x400")
onclose(user, "server_control")
temp = ""
return
/obj/machinery/computer/telecomms/monitor/Topic(href, href_list)
/obj/machinery/computer/telecomms/monitor/ui_act(action, params)
if(..())
return
switch(action)
if("mainmenu")
SelectedMachine = null
notice = ""
return
if("release")
machinelist = list()
notice = ""
return
if("network") //network change, flush the selected machine and buffer
var/newnet = sanitize(sanitize_text(params["value"], network))
if(length(newnet) > 15) //i'm looking at you, you href fuckers
notice = "FAILED: Network tag string too lengthy"
return
network = newnet
SelectedMachine = null
machinelist = list()
return
if("probe")
if(LAZYLEN(machinelist) > 0)
notice = "FAILED: Cannot probe when buffer full"
return
for(var/obj/machinery/telecomms/T in GLOB.telecomms_list)
if(T.network == network)
LAZYADD(machinelist, T)
add_fingerprint(usr)
usr.set_machine(src)
if(href_list["viewmachine"])
screen = 1
for(var/obj/machinery/telecomms/T in machinelist)
if(T.id == href_list["viewmachine"])
SelectedMachine = T
break
if(href_list["operation"])
switch(href_list["operation"])
if("release")
machinelist = list()
screen = 0
if("mainmenu")
screen = 0
if("probe")
if(machinelist.len > 0)
temp = "<font color = #D70B00>- FAILED: CANNOT PROBE WHEN BUFFER FULL -</font color>"
else
for(var/obj/machinery/telecomms/T in urange(25, src))
if(T.network == network)
machinelist.Add(T)
if(!machinelist.len)
temp = "<font color = #D70B00>- FAILED: UNABLE TO LOCATE NETWORK ENTITIES IN \[[network]\] -</font color>"
else
temp = "<font color = #336699>- [machinelist.len] ENTITIES LOCATED & BUFFERED -</font color>"
screen = 0
if(href_list["network"])
var/newnet = stripped_input(usr, "Which network do you want to view?", "Comm Monitor", network)
if(newnet && ((usr in range(1, src)) || hasSiliconAccessInArea(usr)))
if(length(newnet) > 15)
temp = "<font color = #D70B00>- FAILED: NETWORK TAG STRING TOO LENGHTLY -</font color>"
else
network = newnet
screen = 0
machinelist = list()
temp = "<font color = #336699>- NEW NETWORK TAG SET IN ADDRESS \[[network]\] -</font color>"
updateUsrDialog()
return
/obj/machinery/computer/telecomms/monitor/attackby()
. = ..()
updateUsrDialog()
if(!LAZYLEN(machinelist))
notice = "FAILED: Unable to locate network entities in \[[network]\]"
return
if("viewmachine")
for(var/obj/machinery/telecomms/T in machinelist)
if(T.id == params["value"])
SelectedMachine = T
break
@@ -9,9 +9,9 @@
var/temp = "" // output message
/obj/machinery/telecomms/attackby(obj/item/P, mob/user, params)
var/icon_closed = initial(icon_state)
var/icon_open = "[initial(icon_state)]_o"
if(!on)
icon_closed = "[initial(icon_state)]_off"
icon_open = "[initial(icon_state)]_o_off"
@@ -27,78 +27,240 @@
else
return ..()
/obj/machinery/telecomms/ui_interact(mob/user)
. = ..()
// You need a multitool to use this, or be silicon
if(!hasSiliconAccessInArea(user))
// istype returns false if the value is null
if(!istype(user.get_active_held_item(), /obj/item/multitool))
return
/obj/machinery/telecomms/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE,\
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
if(!canInteract(user))
if(ui)
ui.close() //haha no.
return
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "teleinteract", "[name] Access", 520, 500, master_ui, state)
ui.open()
/obj/machinery/telecomms/ui_data(mob/user)
. = list() //cpypaste from the vending bus
.["notice"] = temp
.["multitool"] = FALSE
var/obj/item/multitool/P = get_multitool(user)
var/dat
dat = "<font face = \"Courier\"><HEAD><TITLE>[name]</TITLE></HEAD><center><H3>[name] Access</H3></center>"
dat += "<br>[temp]<br>"
dat += "<br>Power Status: <a href='?src=[REF(src)];input=toggle'>[toggled ? "On" : "Off"]</a>"
if(on && toggled)
if(id != "" && id)
dat += "<br>Identification String: <a href='?src=[REF(src)];input=id'>[id]</a>"
else
dat += "<br>Identification String: <a href='?src=[REF(src)];input=id'>NULL</a>"
dat += "<br>Network: <a href='?src=[REF(src)];input=network'>[network]</a>"
dat += "<br>Prefabrication: [autolinkers.len ? "TRUE" : "FALSE"]"
if(hide)
dat += "<br>Shadow Link: ACTIVE</a>"
if(P)
.["multitool"] = TRUE
.["multitool_buf"] = null //to clean the list!
var/obj/machinery/telecomms/T = P.buffer
if(istype(T))
.["multitool_buf"] = list(
name = T.name,
id = T.id
)
//Show additional options for certain machines.
dat += Options_Menu()
.["machine"] = list()
.["machine"]["power"] = toggled
.["machine"]["id"] = id
.["machine"]["network"] = network
.["machine"]["prefab"] = LAZYLEN(autolinkers) ? TRUE : FALSE
.["machine"]["hidden"] = hide
dat += "<br>Linked Network Entities: <ol>"
.["links"] = list()
for(var/obj/machinery/telecomms/T in links)
if(T.hide && !hide)
continue
var/list/data = list(
name = T.name,
id = T.id,
ref = REF(T)
)
.["links"] += list(data)
var/i = 0
for(var/obj/machinery/telecomms/T in links)
i++
if(T.hide && !hide)
continue
dat += "<li>[REF(T)] [T.name] ([T.id]) <a href='?src=[REF(src)];unlink=[i]'>\[X\]</a></li>"
dat += "</ol>"
.["freq_listening"] = freq_listening
dat += "<br>Filtering Frequencies: "
/obj/machinery/telecomms/relay/ui_data(mob/user)
. = ..()
.["machine"]["isrelay"] = TRUE
.["machine"]["broadcast"] = broadcasting
.["machine"]["receiving"] = receiving
i = 0
if(length(freq_listening))
for(var/x in freq_listening)
i++
if(i < length(freq_listening))
dat += "[format_frequency(x)] GHz<a href='?src=[REF(src)];delete=[x]'>\[X\]</a>; "
else
dat += "[format_frequency(x)] GHz<a href='?src=[REF(src)];delete=[x]'>\[X\]</a>"
else
dat += "NONE"
/obj/machinery/telecomms/bus/ui_data(mob/user)
. = ..()
.["machine"]["isbus"] = TRUE
.["machine"]["chang_frequency"] = change_frequency
.["machine"]["chang_freq_value"] = change_freq_value
dat += "<br> <a href='?src=[REF(src)];input=freq'>\[Add Filter\]</a>"
dat += "<hr>"
if(P)
var/obj/machinery/telecomms/T = P.buffer
if(istype(T))
dat += "<br><br>MULTITOOL BUFFER: [T] ([T.id]) <a href='?src=[REF(src)];link=1'>\[Link\]</a> <a href='?src=[REF(src)];flush=1'>\[Flush\]"
else
dat += "<br><br>MULTITOOL BUFFER: <a href='?src=[REF(src)];buffer=1'>\[Add Machine\]</a>"
dat += "</font>"
/obj/machinery/telecomms/ui_act(action, params, datum/tgui/ui)
if(!canInteract(usr))
if(ui)
ui.close() //haha no.
return
temp = ""
user << browse(dat, "window=tcommachine;size=520x500;can_resize=0")
onclose(user, "tcommachine")
return TRUE
switch(action)
if("toggle")
toggled = !toggled
temp = "-% [src.name] has been [toggled ? "activated" : "deactivated"]. %-"
update_power()
return
if("machine")
if("id" in params)
if(!canAccess(usr))
return
//if the text is blank, return the id it was using
var/newid = sanitize_text(reject_bad_text(params["id"]), id) // reject_bad_text can return null!
if(length(newid) > 255)
temp = "-% Too many characters in new id tag. %-"
return
temp = "-% New ID assigned: \"[newid]\". %-"
id = newid
return
if("network" in params)
if(!canAccess(usr))
return
var/newnet = sanitize(sanitize_text(params["network"], network))
if(length(newnet) > 15)
temp = "-% Too many characters in new network tag. %-"
return
network = newnet
links = list()
temp = "-% New network tag assigned: \"[network]\" %-"
return
if("multitool")
var/obj/item/multitool/P = get_multitool(usr)
if("Link" in params)
if(!canAccess(usr))
return
if(!istype(P))
temp = "-% Unable to acquire buffer %-"
return
var/obj/machinery/telecomms/T = P.buffer
if(!istype(T) || T == src)
temp = "-% Unable to acquire buffer %-"
return
if(!(src in T.links))
LAZYADD(T.links, src)
if(!(T in links))
LAZYADD(links, T)
temp = "-% Successfully linked with [REF(T)] [T.name] %-"
if("Flush" in params)
if(!istype(P))
temp = "-% Unable to acquire multitool %-"
return
temp = "-% Buffer successfully flushed. %-"
P.buffer = null
if("Add" in params)
if(!canAccess(usr))
return
if(!istype(P))
temp = "-% Unable to acquire multitool %-"
return
P.buffer = src
temp = "% Successfully stored [REF(P.buffer)] [P.buffer.name] in buffer %-"
if("unlink")
var/obj/machinery/telecomms/T = locate(params["value"])
if(!canAccess(usr))
return
if(!istype(T))
temp = "-% Unable to locate machine to unlink from, try again. %-"
return
temp = "-% Removed [REF(T)] [T.name] from linked entities. %-"
if(T.links) //lazyrem makes blank list null, which is good but some might cause runtime ee's
T.links.Remove(src)
links.Remove(T)
if("freq")
if("add" in params)
var/newfreq = input("Specify a new frequency to filter (GHz). Decimals assigned automatically.", src.name, null) as null|num
if(!canAccess(usr) || !newfreq || isnull(newfreq))
return
if(findtext(num2text(newfreq), ".")) // did they not read the text?
newfreq *= 10 // shift the decimal one place
newfreq = sanitize_frequency(newfreq, TRUE) //sanitize
if(newfreq == FREQ_SYNDICATE)
temp = "-% Error: Interference preventing filtering frequency: \"[newfreq] GHz\" %-"
return
if(newfreq in freq_listening)
temp = "-% Error: Frequency already filtered %-"
return
LAZYADD(freq_listening, newfreq)
temp = "-% New frequency filter assigned: \"[newfreq] GHz\" %-"
if("remove" in params)
if(!canAccess(usr))
return
var/x = text2num(params["remove"])
temp = "-% Removed frequency filter [x] %-"
freq_listening.Remove(x)
/obj/machinery/telecomms/relay/ui_act(action, params)
..()
switch(action)
if("relay")
if("broadcast" in params)
if(!canAccess(usr))
return
broadcasting = !broadcasting
temp = "-% Broadcasting mode changed. %-"
return
if("receiving" in params)
if(!canAccess(usr))
return
receiving = !receiving
temp = "-% Receiving mode changed. %-"
/obj/machinery/telecomms/bus/ui_act(action, params)
..()
switch(action)
if("frequency")
if("toggle" in params)
if(!canAccess(usr))
return
change_frequency = !change_frequency
return
if("adjust" in params)
var/newfreq = text2num(params["adjust"])
if(!canAccess(usr) || !newfreq)
return
// this should return true, unless the href is handcrafted
if(findtext(num2text(newfreq), "."))
newfreq *= 10 // shift the decimal one place
newfreq = sanitize_frequency(newfreq, TRUE)
if(newfreq == FREQ_SYNDICATE)
temp = "-% Error: Interference preventing filtering frequency: \"[newfreq] GHz\" %-"
return
change_freq_value = newfreq
temp = "-% New frequency to change to assigned: \"[newfreq] GHz\" %-"
return
// Check if the user can use it.
/obj/machinery/telecomms/proc/canInteract(mob/user)
if(hasSiliconAccessInArea(user) || istype(user.get_active_held_item(), /obj/item/multitool))
return TRUE
return FALSE
// Check if the user is nearby and has a multitool.
/obj/machinery/telecomms/proc/canAccess(mob/user)
if((canInteract(user) && in_range(user, src)) || hasSiliconAccessInArea(user))
return TRUE
return FALSE
// Returns a multitool from a user depending on their mobtype.
/obj/machinery/telecomms/proc/get_multitool(mob/user)
var/obj/item/multitool/P = null
// Let's double check
if(!hasSiliconAccessInArea(user) && istype(user.get_active_held_item(), /obj/item/multitool))
P = user.get_active_held_item()
if(!canInteract(user))
return null
var/obj/item/multitool/P = user.get_active_held_item()
// Is the ref not a null? and is it the actual type?
if(istype(P))
return P
else if(isAI(user))
var/mob/living/silicon/ai/U = user
P = U.aiMulti
@@ -106,170 +268,3 @@
if(istype(user.get_active_held_item(), /obj/item/multitool))
P = user.get_active_held_item()
return P
// Additional Options for certain machines. Use this when you want to add an option to a specific machine.
// Example of how to use below.
/obj/machinery/telecomms/proc/Options_Menu()
return ""
// The topic for Additional Options. Use this for checking href links for your specific option.
// Example of how to use below.
/obj/machinery/telecomms/proc/Options_Topic(href, href_list)
return
// RELAY
/obj/machinery/telecomms/relay/Options_Menu()
var/dat = ""
dat += "<br>Broadcasting: <A href='?src=[REF(src)];broadcast=1'>[broadcasting ? "YES" : "NO"]</a>"
dat += "<br>Receiving: <A href='?src=[REF(src)];receive=1'>[receiving ? "YES" : "NO"]</a>"
return dat
/obj/machinery/telecomms/relay/Options_Topic(href, href_list)
if(href_list["receive"])
receiving = !receiving
temp = "<font color = #666633>-% Receiving mode changed. %-</font color>"
if(href_list["broadcast"])
broadcasting = !broadcasting
temp = "<font color = #666633>-% Broadcasting mode changed. %-</font color>"
// BUS
/obj/machinery/telecomms/bus/Options_Menu()
var/dat = "<br>Change Signal Frequency: <A href='?src=[REF(src)];change_freq=1'>[change_frequency ? "YES ([change_frequency])" : "NO"]</a>"
return dat
/obj/machinery/telecomms/bus/Options_Topic(href, href_list)
if(href_list["change_freq"])
var/newfreq = input(usr, "Specify a new frequency for new signals to change to. Enter null to turn off frequency changing. Decimals assigned automatically.", src, network) as null|num
if(canAccess(usr))
if(newfreq)
if(findtext(num2text(newfreq), "."))
newfreq *= 10 // shift the decimal one place
if(newfreq < 10000)
change_frequency = newfreq
temp = "<font color = #666633>-% New frequency to change to assigned: \"[newfreq] GHz\" %-</font color>"
else
change_frequency = 0
temp = "<font color = #666633>-% Frequency changing deactivated %-</font color>"
/obj/machinery/telecomms/Topic(href, href_list)
if(..())
return
if(!hasSiliconAccessInArea(usr))
if(!istype(usr.get_active_held_item(), /obj/item/multitool))
return
var/obj/item/multitool/P = get_multitool(usr)
if(href_list["input"])
switch(href_list["input"])
if("toggle")
toggled = !toggled
temp = "<font color = #666633>-% [src] has been [toggled ? "activated" : "deactivated"].</font color>"
update_power()
if("id")
var/newid = reject_bad_text(stripped_input(usr, "Specify the new ID for this machine", src, id, MAX_MESSAGE_LEN))
if(newid && canAccess(usr))
id = newid
temp = "<font color = #666633>-% New ID assigned: \"[id]\" %-</font color>"
if("network")
var/newnet = stripped_input(usr, "Specify the new network for this machine. This will break all current links.", src, network)
if(newnet && canAccess(usr))
if(length(newnet) > 15)
temp = "<font color = #666633>-% Too many characters in new network tag %-</font color>"
else
for(var/obj/machinery/telecomms/T in links)
T.links.Remove(src)
network = newnet
links = list()
temp = "<font color = #666633>-% New network tag assigned: \"[network]\" %-</font color>"
if("freq")
var/newfreq = input(usr, "Specify a new frequency to filter (GHz). Decimals assigned automatically.", src, network) as null|num
if(newfreq && canAccess(usr))
if(findtext(num2text(newfreq), "."))
newfreq *= 10 // shift the decimal one place
if(newfreq == FREQ_SYNDICATE)
temp = "<font color = #FF0000>-% Error: Interference preventing filtering frequency: \"[newfreq] GHz\" %-</font color>"
else
if(!(newfreq in freq_listening) && newfreq < 10000)
freq_listening.Add(newfreq)
temp = "<font color = #666633>-% New frequency filter assigned: \"[newfreq] GHz\" %-</font color>"
if(href_list["delete"])
// changed the layout about to workaround a pesky runtime -- Doohl
var/x = text2num(href_list["delete"])
temp = "<font color = #666633>-% Removed frequency filter [x] %-</font color>"
freq_listening.Remove(x)
if(href_list["unlink"])
if(text2num(href_list["unlink"]) <= length(links))
var/obj/machinery/telecomms/T = links[text2num(href_list["unlink"])]
if(T)
temp = "<font color = #666633>-% Removed [REF(T)] [T.name] from linked entities. %-</font color>"
// Remove link entries from both T and src.
if(T.links)
T.links.Remove(src)
links.Remove(T)
else
temp = "<font color = #666633>-% Unable to locate machine to unlink from, try again. %-</font color>"
if(href_list["link"])
if(P)
var/obj/machinery/telecomms/T = P.buffer
if(istype(T) && T != src)
if(!(src in T.links))
T.links += src
if(!(T in links))
links += T
temp = "<font color = #666633>-% Successfully linked with [REF(T)] [T.name] %-</font color>"
else
temp = "<font color = #666633>-% Unable to acquire buffer %-</font color>"
if(href_list["buffer"])
P.buffer = src
temp = "<font color = #666633>-% Successfully stored [REF(P.buffer)] [P.buffer.name] in buffer %-</font color>"
if(href_list["flush"])
temp = "<font color = #666633>-% Buffer successfully flushed. %-</font color>"
P.buffer = null
Options_Topic(href, href_list)
usr.set_machine(src)
updateUsrDialog()
/obj/machinery/telecomms/proc/canAccess(mob/user)
if(hasSiliconAccessInArea(user) || in_range(user, src))
return TRUE
return FALSE
@@ -17,7 +17,8 @@
idle_power_usage = 50
netspeed = 40
circuit = /obj/item/circuitboard/machine/telecomms/bus
var/change_frequency = 0
var/change_frequency = FALSE
var/change_freq_value = 0
/obj/machinery/telecomms/bus/RefreshParts()
idle_power_usage = 50
@@ -28,8 +29,8 @@
if(!istype(signal) || !is_freq_listening(signal))
return
if(change_frequency && signal.frequency != FREQ_SYNDICATE)
signal.frequency = change_frequency
if(change_frequency && (change_freq_value && signal.frequency != FREQ_SYNDICATE))
signal.frequency = change_freq_value
if(!istype(machine_from, /obj/machinery/telecomms/processor) && machine_from != src) // Signal must be ready (stupid assuming machine), let's send it
// send to one linked processor unit
@@ -140,11 +140,16 @@
..()
if(href_list["photo"])
var/mob/M = usr
M << browse_rsc(picture.picture_image, "pda_photo.png")
M << browse("<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><title>PDA Photo</title></head>" \
+ "<body style='overflow:hidden;margin:0;text-align:center'>" \
+ "<img src='pda_photo.png' width='192' style='-ms-interpolation-mode:nearest-neighbor' />" \
+ "</body></html>", "window=pdaphoto;size=[picture.psize_x]x[picture.psize_y];can-close=true")
M << browse_rsc(picture.picture_image, "pda_photo.png")
var/dat = "<div style='overflow: hidden; margin :0; text-align: center'>"
dat += "<img src='pda_photo.png' width='192' style='-ms-interpolation-mode:nearest-neighbor' />"
dat += "</div>"
var/datum/browser/popup = new(M, "pdaphoto", "PDA Photo", picture.psize_x, picture.psize_y)
popup.set_content(dat)
popup.open()
onclose(M, "pdaphoto")
/datum/data_rc_msg
@@ -33,7 +33,7 @@
totaltraffic += traffic // add current traffic to total traffic
// Delete particularly old logs
if (log_entries.len >= 400)
if(LAZYLEN(log_entries) >= 400) //[list].len is not safe
log_entries.Cut(1, 2)
var/datum/comm_log_entry/log = new
@@ -117,9 +117,9 @@ GLOBAL_LIST_EMPTY(telecomms_list)
icon_state = "[initial(icon_state)]_off"
/obj/machinery/telecomms/proc/update_power()
if(toggled)
if(stat & (BROKEN|NOPOWER|EMPED)) // if powered, on. if not powered, off. if too damaged, off
// if powered, on. if not powered, off. if too damaged, off
if(CHECK_BITFIELD(stat, (BROKEN | NOPOWER | EMPED)))
on = FALSE
else
on = TRUE
@@ -137,11 +137,11 @@ GLOBAL_LIST_EMPTY(telecomms_list)
/obj/machinery/telecomms/emp_act(severity)
. = ..()
if(. & EMP_PROTECT_SELF)
if(CHECK_BITFIELD(., EMP_PROTECT_SELF))
return
if(prob(100/severity))
if(!(stat & EMPED))
stat |= EMPED
var/duration = (300 * 10)/severity
if(prob(100 / severity))
if(!CHECK_BITFIELD(stat, EMPED))
ENABLE_BITFIELD(stat, EMPED)
var/duration = (300 * 10) / severity
spawn(rand(duration - 20, duration + 20)) // Takes a long time for the machines to reboot.
stat &= ~EMPED
DISABLE_BITFIELD(stat, EMPED)
+17 -19
View File
@@ -5,44 +5,42 @@
var/base_icon
var/looky_helpy = TRUE
/datum/component/construction/mecha/examine(mob/user)
/datum/component/construction/mecha/examine(datum/source, mob/user, list/examine_list)
. = ..()
if(looky_helpy)
switch(steps[index]["key"])
if(TOOL_WRENCH)
. += "<span class='notice'>The mech could be <b>wrenched</b> into place.</span>"
examine_list += "<span class='notice'>The mech could be <b>wrenched</b> into place.</span>"
if(TOOL_SCREWDRIVER)
. += "<span class='notice'>The mech could be <b>screwed</b> into place.</span>"
examine_list += "<span class='notice'>The mech could be <b>screwed</b> into place.</span>"
if(TOOL_WIRECUTTER)
. += "<span class='notice'>The mech wires could be <b>trimmed</b> into place.</span>"
examine_list += "<span class='notice'>The mech wires could be <b>trimmed</b> into place.</span>"
if(/obj/item/stack/cable_coil)
. += "<span class='notice'>The mech could use some <b>wiring</b>.</span>"
examine_list += "<span class='notice'>The mech could use some <b>wiring</b>.</span>"
if(/obj/item/circuitboard)
. += "<span class='notice'>The mech could use a type of<b>circuitboard</b>.</span>"
examine_list += "<span class='notice'>The mech could use a type of<b>circuitboard</b>.</span>"
if(/obj/item/stock_parts/scanning_module)
. += "<span class='notice'>The mech could use a <b>scanning stock part</b>.</span>"
examine_list += "<span class='notice'>The mech could use a <b>scanning stock part</b>.</span>"
if(/obj/item/stock_parts/capacitor)
. += "<span class='notice'>The mech could use a <b>power based stock part</b>.</span>"
examine_list += "<span class='notice'>The mech could use a <b>power based stock part</b>.</span>"
if(/obj/item/stock_parts/cell)
. += "<span class='notice'>The mech could use a <b>power source</b>.</span>"
examine_list += "<span class='notice'>The mech could use a <b>power source</b>.</span>"
if(/obj/item/stack/sheet/metal)
. += "<span class='notice'>The mech could use some <b>sheets of metal</b>.</span>"
examine_list += "<span class='notice'>The mech could use some <b>sheets of metal</b>.</span>"
if(/obj/item/stack/sheet/plasteel)
. += "<span class='notice'>The mech could use some <b>sheets of strong steel</b>.</span>"
examine_list += "<span class='notice'>The mech could use some <b>sheets of strong steel</b>.</span>"
if(/obj/item/bikehorn)
. += "<span class='notice'>HONK IT!.</span>"
examine_list += "<span class='notice'>HONK IT!.</span>"
if(/obj/item/clothing/mask/gas/clown_hat)
. += "<span class='notice'>GIVE IT CLOWN MAKEUP HONK!.</span>"
examine_list += "<span class='notice'>GIVE IT CLOWN MAKEUP HONK!.</span>"
if(/obj/item/clothing/shoes/clown_shoes)
. += "<span class='notice'>GIVE IT GOOFY SHOES HONK HONK!.</span>"
examine_list += "<span class='notice'>GIVE IT GOOFY SHOES HONK HONK!.</span>"
if(/obj/item/mecha_parts/part)
. += "<span class='notice'>The mech could use a mech <b>part</b>.</span>"
examine_list += "<span class='notice'>The mech could use a mech <b>part</b>.</span>"
if(/obj/item/stack/ore/bluespace_crystal)
. += "<span class='notice'>The mech could use a <b>crystal</b> of sorts.</span>"
examine_list += "<span class='notice'>The mech could use a <b>crystal</b> of sorts.</span>"
if(/obj/item/assembly/signaler/anomaly)
. += "<span class='notice'>The mech could use a <b>anomaly</b> of sorts.</span>"
else
return
examine_list += "<span class='notice'>The mech could use a <b>anomaly</b> of sorts.</span>"
/datum/component/construction/mecha/spawn_result()
if(!result)
+1 -1
View File
@@ -284,7 +284,7 @@
/obj/mecha/attacked_by(obj/item/I, mob/living/user)
mecha_log_message("Attacked by [I]. Attacker - [user]")
..()
return ..()
/obj/mecha/proc/mech_toxin_damage(mob/living/target)
playsound(src, 'sound/effects/spray2.ogg', 50, 1)
+1 -1
View File
@@ -46,4 +46,4 @@
var/turf/T = loc
if(!istype(T)) //you know this will happen somehow
CRASH("Turf decal initialized in an object/nullspace")
T.AddComponent(/datum/component/decal, icon, icon_state, dir, CLEAN_GOD, color, null, null, alpha)
T.AddElement(/datum/element/decal, icon, icon_state, turn(dir, -dir2angle(T.dir)), CLEAN_GOD, color, null, null, alpha)
@@ -95,6 +95,56 @@
icon_state = "warden_gaze"
duration = 3
/obj/effect/temp_visual/ratvar/volt_hit
name = "volt blast"
layer = ABOVE_MOB_LAYER
duration = 8
icon_state = "volt_hit"
light_range = 1.5
light_power = 2
light_color = LIGHT_COLOR_ORANGE
var/mob/user
var/damage = 20
/obj/effect/temp_visual/ratvar/volt_hit/Initialize(mapload, caster)
. = ..()
user = caster
if(user)
var/matrix/M = new
M.Turn(Get_Angle(src, user))
transform = M
INVOKE_ASYNC(src, .proc/volthit)
/obj/effect/temp_visual/ratvar/volt_hit/proc/volthit()
if(user)
Beam(get_turf(user), "volt_ray", time=duration, maxdistance=8, beam_type=/obj/effect/ebeam/volt_ray)
var/hit_amount = 0
var/turf/T = get_turf(src)
for(var/mob/living/L in T)
if(is_servant_of_ratvar(L))
continue
var/obj/item/I = L.anti_magic_check()
if(I)
L.visible_message("<span class='warning'>Strange energy flows into [L]'s [I.name]!</span>", \
"<span class='userdanger'>Your [I.name] shields you from [src]!</span>")
continue
L.visible_message("<span class='warning'>[L] is struck by a [name]!</span>", "<span class='userdanger'>You're struck by a [name]!</span>")
L.apply_damage(damage, BURN, "chest", L.run_armor_check("chest", "laser", "Your armor absorbs [src]!", "Your armor blocks part of [src]!", 0, "Your armor was penetrated by [src]!"))
log_combat(user, L, "struck with a volt blast")
hit_amount++
for(var/obj/mecha/M in T)
if(M.occupant)
if(is_servant_of_ratvar(M.occupant))
continue
to_chat(M.occupant, "<span class='userdanger'>Your [M.name] is struck by a [name]!</span>")
M.visible_message("<span class='warning'>[M] is struck by a [name]!</span>")
M.take_damage(damage, BURN, 0, 0)
hit_amount++
if(hit_amount)
playsound(src, 'sound/machines/defib_zap.ogg', damage*hit_amount, 1, -1)
else
playsound(src, "sparks", 50, 1)
/obj/effect/temp_visual/ratvar/ocular_warden/Initialize()
. = ..()
pixel_x = rand(-8, 8)
+9
View File
@@ -55,6 +55,15 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
/// How long, in deciseconds, this staggers for, if null it will autocalculate from w_class and force. Unlike total mass this supports 0 and negatives.
var/stagger_force
/**
* Set FALSE and then checked at the end of on mob/living/attackby(), set TRUE on living/pre_attacked_by().
* Should it be FALSE by the end of the item/attack(), that means the item overrode the standard attack behaviour
* and the user still needs the delay applied. We can't be using return values since that'll stop afterattack() from being triggered.
*/
var/attack_delay_done = FALSE
///next_move click/attack delay of this item.
var/click_delay = CLICK_CD_MELEE
var/slot_flags = 0 //This is used to determine on which slots an item can fit.
pass_flags = PASSTABLE
pressure_resistance = 4
+1 -1
View File
@@ -157,7 +157,7 @@
to_chat(user, "<span class='notice'>You need to get closer!</span>")
return
if(use_paint(user) && isturf(F))
F.AddComponent(/datum/component/decal, 'icons/turf/decals.dmi', stored_decal_total, stored_dir, CLEAN_STRONG, color, null, null, alpha)
F.AddElement(/datum/element/decal, 'icons/turf/decals.dmi', stored_decal_total, turn(stored_dir, -dir2angle(F.dir)), CLEAN_STRONG, color, null, null, alpha)
/obj/item/airlock_painter/decal/attack_self(mob/user)
if((ink) && (ink.charges >= 1))
+1 -1
View File
@@ -65,7 +65,7 @@
. = ..()
AddElement(/datum/element/update_icon_blocker)
/obj/item/gun/energy/chrono_gun/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0)
/obj/item/gun/energy/chrono_gun/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0, stam_cost = 0)
if(field)
field_disconnect(field)
..()
@@ -293,6 +293,10 @@
name = "Mining Shuttle (Computer Board)"
build_path = /obj/machinery/computer/shuttle/mining
/obj/item/circuitboard/computer/snow_taxi
name = "Snow Taxi (Computer Board)"
build_path = /obj/machinery/computer/shuttle/snow_taxi
/obj/item/circuitboard/computer/white_ship
name = "White Ship (Computer Board)"
build_path = /obj/machinery/computer/shuttle/white_ship
+5
View File
@@ -273,6 +273,10 @@
light_color = "#37FFF7"
actions_types = list()
/obj/item/melee/transforming/energy/sword/cx/Initialize()
icon_state_on = icon_state
return ..()
/obj/item/melee/transforming/energy/sword/cx/ComponentInitialize()
. = ..()
AddElement(/datum/element/update_icon_updates_onmob)
@@ -290,6 +294,7 @@
if(!supress_message_text)
to_chat(user, "<span class='notice'>[src] [active ? "is now active":"can now be concealed"].</span>")
/obj/item/melee/transforming/energy/sword/cx/update_overlays()
. = ..()
var/mutable_appearance/blade_overlay = mutable_appearance(icon, "cxsword_blade")
+42 -37
View File
@@ -6,6 +6,7 @@
lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
flags_1 = CONDUCT_1
item_flags = NEEDS_PERMIT | NO_COMBAT_MODE_FORCE_MODIFIER //To avoid ambushing and oneshotting healthy crewmembers on force setting 3.
attack_verb = list("whacked", "fisted", "power-punched")
force = 20
throwforce = 10
@@ -13,7 +14,7 @@
w_class = WEIGHT_CLASS_NORMAL
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 40)
resistance_flags = FIRE_PROOF
var/click_delay = 1.5
click_delay = CLICK_CD_MELEE * 1.5
var/fisto_setting = 1
var/gasperfist = 3
var/obj/item/tank/internals/tank = null //Tank used for the gauntlet's piston-ram.
@@ -70,42 +71,46 @@
/obj/item/melee/powerfist/attack(mob/living/target, mob/living/user)
if(!tank)
to_chat(user, "<span class='warning'>\The [src] can't operate without a source of gas!</span>")
return
var/datum/gas_mixture/gasused = tank.air_contents.remove(gasperfist * fisto_setting)
var/turf/T = get_turf(src)
if(!T)
return
T.assume_air(gasused)
T.air_update_turf()
if(!gasused)
to_chat(user, "<span class='warning'>\The [src]'s tank is empty!</span>")
target.apply_damage((force / 5), BRUTE)
playsound(loc, 'sound/weapons/punch1.ogg', 50, 1)
target.visible_message("<span class='danger'>[user]'s powerfist lets out a dull thunk as [user.p_they()] punch[user.p_es()] [target.name]!</span>", \
"<span class='userdanger'>[user]'s punches you!</span>")
return
if(gasused.total_moles() < gasperfist * fisto_setting)
to_chat(user, "<span class='warning'>\The [src]'s piston-ram lets out a weak hiss, it needs more gas!</span>")
playsound(loc, 'sound/weapons/punch4.ogg', 50, 1)
target.apply_damage((force / 2), BRUTE)
target.visible_message("<span class='danger'>[user]'s powerfist lets out a weak hiss as [user.p_they()] punch[user.p_es()] [target.name]!</span>", \
"<span class='userdanger'>[user]'s punch strikes with force!</span>")
return
target.apply_damage(force * fisto_setting, BRUTE)
target.visible_message("<span class='danger'>[user]'s powerfist lets out a loud hiss as [user.p_they()] punch[user.p_es()] [target.name]!</span>", \
"<span class='userdanger'>You cry out in pain as [user]'s punch flings you backwards!</span>")
new /obj/effect/temp_visual/kinetic_blast(target.loc)
playsound(loc, 'sound/weapons/resonator_blast.ogg', 50, 1)
playsound(loc, 'sound/weapons/genhit2.ogg', 50, 1)
if(HAS_TRAIT(user, TRAIT_PACIFISM))
to_chat(user, "<span class='warning'>You don't want to harm other living beings!</span>")
return FALSE
if(!tank)
to_chat(user, "<span class='warning'>\The [src] can't operate without a source of gas!</span>")
return FALSE
var/datum/gas_mixture/gasused = tank.air_contents.remove(gasperfist * fisto_setting)
var/turf/T = get_turf(src)
if(!T)
return FALSE
var/totalitemdamage = target.pre_attacked_by(src, user)
T.assume_air(gasused)
T.air_update_turf()
if(!gasused)
to_chat(user, "<span class='warning'>\The [src]'s tank is empty!</span>")
target.apply_damage((totalitemdamage / 5), BRUTE)
playsound(loc, 'sound/weapons/punch1.ogg', 50, 1)
target.visible_message("<span class='danger'>[user]'s powerfist lets out a dull thunk as [user.p_they()] punch[user.p_es()] [target.name]!</span>", \
"<span class='userdanger'>[user]'s punches you!</span>")
return
if(gasused.total_moles() < gasperfist * fisto_setting)
to_chat(user, "<span class='warning'>\The [src]'s piston-ram lets out a weak hiss, it needs more gas!</span>")
playsound(loc, 'sound/weapons/punch4.ogg', 50, 1)
target.apply_damage((totalitemdamage / 2), BRUTE)
target.visible_message("<span class='danger'>[user]'s powerfist lets out a weak hiss as [user.p_they()] punch[user.p_es()] [target.name]!</span>", \
"<span class='userdanger'>[user]'s punch strikes with force!</span>")
return
target.apply_damage(totalitemdamage * fisto_setting, BRUTE)
target.visible_message("<span class='danger'>[user]'s powerfist lets out a loud hiss as [user.p_they()] punch[user.p_es()] [target.name]!</span>", \
"<span class='userdanger'>You cry out in pain as [user]'s punch flings you backwards!</span>")
new /obj/effect/temp_visual/kinetic_blast(target.loc)
playsound(loc, 'sound/weapons/resonator_blast.ogg', 50, 1)
playsound(loc, 'sound/weapons/genhit2.ogg', 50, 1)
var/atom/throw_target = get_edge_target_turf(target, get_dir(src, get_step_away(target, src)))
var/atom/throw_target = get_edge_target_turf(target, get_dir(src, get_step_away(target, src)))
target.throw_at(throw_target, 5 * fisto_setting, 0.5 + (fisto_setting / 2))
target.throw_at(throw_target, 5 * fisto_setting, 0.5 + (fisto_setting / 2))
log_combat(user, target, "power fisted", src)
log_combat(user, target, "power fisted", src)
user.changeNext_move(CLICK_CD_MELEE * click_delay)
return
var/weight = getweight(user, STAM_COST_ATTACK_MOB_MULT)
if(weight)
user.adjustStaminaLossBuffered(weight)
return TRUE
+2 -1
View File
@@ -215,8 +215,9 @@
/obj/item/clothing/head/helmet/plate/crusader/prophet
name = "Prophet's Hat"
desc = "A religious-looking hat."
icon_state = "prophet"
mob_overlay_icon = 'icons/mob/large-worn-icons/64x64/head.dmi'
flags_1 = 0
flags_1 = NONE
armor = list("melee" = 60, "bullet" = 60, "laser" = 60, "energy" = 50, "bomb" = 70, "bio" = 50, "rad" = 50, "fire" = 60, "acid" = 60) //religion protects you from disease and radiation, honk.
worn_x_dimension = 64
worn_y_dimension = 64
+4 -4
View File
@@ -382,14 +382,14 @@
icon = 'icons/obj/chemical.dmi'
icon_state = "bag"
desc = "A bag for storing pills, patches, and bottles."
w_class = WEIGHT_CLASS_TINY
slot_flags = ITEM_SLOT_BELT|ITEM_SLOT_POCKET
resistance_flags = FLAMMABLE
/obj/item/storage/bag/chemistry/ComponentInitialize()
. = ..()
var/datum/component/storage/STR = GetComponent(/datum/component/storage)
STR.max_combined_w_class = 200
STR.max_items = 50
STR.storage_flags = STORAGE_FLAGS_VOLUME_DEFAULT
STR.max_volume = STORAGE_VOLUME_CHEMISTRY_BAG
STR.insert_preposition = "in"
STR.can_hold = typecacheof(list(/obj/item/reagent_containers/pill, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/syringe/dart))
@@ -402,7 +402,7 @@
icon = 'icons/obj/chemical.dmi'
icon_state = "biobag"
desc = "A bag for the safe transportation and disposal of biowaste and other biological materials."
w_class = WEIGHT_CLASS_TINY
slot_flags = ITEM_SLOT_BELT|ITEM_SLOT_POCKET
resistance_flags = FLAMMABLE
/obj/item/storage/bag/bio/ComponentInitialize()
+1 -1
View File
@@ -224,7 +224,7 @@
. = ..()
var/datum/component/storage/STR = GetComponent(/datum/component/storage)
STR.storage_flags = STORAGE_FLAGS_VOLUME_DEFAULT
STR.max_volume = 14
STR.max_volume = STORAGE_VOLUME_PILL_BOTTLE
STR.allow_quick_gather = TRUE
STR.click_gather = TRUE
STR.can_hold = typecacheof(list(/obj/item/reagent_containers/pill, /obj/item/dice))
+2 -2
View File
@@ -174,7 +174,7 @@
name = "\improper X-01 Multiphase energy gun box"
desc = "A storage case for a high-tech energy firearm."
/obj/item/storage/secure/briefcase/mws_pack_hos/PopulateContents()
/obj/item/storage/secure/briefcase/hos/multiphase_box/PopulateContents()
new /obj/item/gun/energy/e_gun/hos(src)
// -----------------------------
@@ -212,4 +212,4 @@
return attack_self(user)
/obj/item/storage/secure/safe/HoS
name = "head of security's safe"
name = "head of security's safe"
+2 -2
View File
@@ -141,7 +141,7 @@
/obj/item/melee/baton/attack(mob/M, mob/living/carbon/human/user)
var/interrupt = common_baton_melee(M, user, FALSE)
if(!interrupt)
..()
return ..()
/obj/item/melee/baton/alt_pre_attack(atom/A, mob/living/user, params)
. = common_baton_melee(A, user, TRUE) //return true (attackchain interrupt) if this also returns true. no harm-disarming.
@@ -154,7 +154,7 @@
if(turned_on && HAS_TRAIT(user, TRAIT_CLUMSY) && prob(50))
clowning_around(user)
if(IS_STAMCRIT(user)) //CIT CHANGE - makes it impossible to baton in stamina softcrit
to_chat(user, "<span class='danger'>You're too exhausted for that.</span>")
to_chat(user, "<span class='danger'>You're too exhausted to use [src] properly.</span>")
return TRUE
if(ishuman(M))
var/mob/living/carbon/human/L = M
+4 -1
View File
@@ -93,11 +93,13 @@
F.update_icon()
else
return ..()
//Makes empty oxygen tanks spawn without gas
/obj/item/tank/internals/plasma/empty/populate_gas()
return
/obj/item/tank/internals/plasma/full/populate_gas()
air_contents.gases[/datum/gas/plasma] = (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)
/*
* Plasmaman Plasma Tank
*/
@@ -130,6 +132,7 @@
air_contents.gases[/datum/gas/plasma] = (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)
return
//makes empty plasma tanks spawn without gas.
/obj/item/tank/internals/plasmaman/belt/empty/populate_gas()
return
+3 -3
View File
@@ -835,11 +835,11 @@
/obj/item/toy/cards/deck/update_icon_state()
switch(cards.len)
if(INFINITY to original_size/2)
if(original_size*0.5 to INFINITY)
icon_state = "deck_[deckstyle]_full"
if(original_size/2 to original_size/4)
if(original_size*0.25 to original_size*0.5)
icon_state = "deck_[deckstyle]_half"
if(original_size/4 to 1)
if(1 to original_size*0.25)
icon_state = "deck_[deckstyle]_low"
else
icon_state = "deck_[deckstyle]_empty"
+5 -4
View File
@@ -582,7 +582,7 @@
force_wielded = 18
throwforce = 20
throw_speed = 4
embedding = list("embedded_impact_pain_multiplier" = 3, "embed_chance" = 90)
embedding = list("embedded_impact_pain_multiplier" = 1.5, "embed_chance" = 65)
armour_penetration = 10
custom_materials = list(/datum/material/iron=1150, /datum/material/glass=2075)
hitsound = 'sound/weapons/bladeslice.ogg'
@@ -667,6 +667,7 @@
force_wielded = 19
force_unwielded = 11
throwforce = 21
embedding = getEmbeddingBehavior(embed_chance = 75, embedded_pain_multiplier = 1.5) //plasmaglass spears are sharper
icon_prefix = "spearplasma"
qdel(tip)
var/obj/item/twohanded/spear/S = locate() in parts_list
@@ -681,6 +682,7 @@
if(G)
explosive = G
name = "explosive lance"
embedding = getEmbeddingBehavior(embed_chance = 0, embedded_pain_multiplier = 1)//elances should not be embeddable
desc = "A makeshift spear with [G] attached to it."
update_icon()
@@ -1174,14 +1176,13 @@
/obj/item/twohanded/electrostaff/attack(mob/living/target, mob/living/user)
if(IS_STAMCRIT(user))//CIT CHANGE - makes it impossible to baton in stamina softcrit
to_chat(user, "<span class='danger'>You're too exhausted for that.</span>")//CIT CHANGE - ditto
to_chat(user, "<span class='danger'>You're too exhausted to use [src] properly.</span>")//CIT CHANGE - ditto
return //CIT CHANGE - ditto
if(on && HAS_TRAIT(user, TRAIT_CLUMSY) && prob(50))
clowning_around(user) //ouch!
return
if(iscyborg(target))
..()
return
return ..()
if(target.mob_run_block(src, 0, "[user]'s [name]", ATTACK_TYPE_MELEE, 0, user, null, null) & BLOCK_SUCCESS) //No message; run_block() handles that
playsound(target, 'sound/weapons/genhit.ogg', 50, 1)
return FALSE
+1 -1
View File
@@ -675,7 +675,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
to_chat(user, "<span class='warning'>You easily land a critical blow on the [target].</span>")
if(istype(target, /mob/living/))
var/mob/living/bug = target
bug.adjustBruteLoss(-35) //What kinda mad man would go into melee with a spider?!
bug.adjustBruteLoss(35) //What kinda mad man would go into melee with a spider?!
else
qdel(target)
+2 -2
View File
@@ -41,7 +41,7 @@ LINEN BINS
return
/obj/item/bedsheet/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/wirecutters) || I.get_sharpness())
if(!(flags_1 & HOLOGRAM_1) && (istype(I, /obj/item/wirecutters) || I.get_sharpness()))
var/obj/item/stack/sheet/cloth/C = new (get_turf(src), 3)
transfer_fingerprints_to(C)
C.add_fingerprint(user)
@@ -369,4 +369,4 @@ LINEN BINS
/obj/structure/bedsheetbin/color
sheet_types = list(/obj/item/bedsheet, /obj/item/bedsheet/blue, /obj/item/bedsheet/green, /obj/item/bedsheet/orange, \
/obj/item/bedsheet/purple, /obj/item/bedsheet/red, /obj/item/bedsheet/yellow, /obj/item/bedsheet/brown, \
/obj/item/bedsheet/black)
/obj/item/bedsheet/black)
@@ -51,7 +51,6 @@
new /obj/item/storage/backpack/satchel/leather/withwallet( src )
new /obj/item/instrument/piano_synth(src)
new /obj/item/radio/headset( src )
new /obj/item/clothing/head/colour(src)
/obj/structure/closet/secure_closet/personal/attackby(obj/item/W, mob/user, params)
var/obj/item/card/id/I = W.GetID()
@@ -694,15 +694,12 @@
..()
var/suited = !preference_source || preference_source.prefs.jumpsuit_style == PREF_SUIT
if (CONFIG_GET(flag/grey_assistants))
if(suited)
uniform = /obj/item/clothing/under/color/grey
else
uniform = /obj/item/clothing/under/color/jumpskirt/grey
uniform = suited ? /obj/item/clothing/under/color/grey : /obj/item/clothing/under/color/jumpskirt/grey
else
if(suited)
uniform = /obj/item/clothing/under/color/random
if(SSevents.holidays && SSevents.holidays[PRIDE_MONTH])
uniform = suited ? /obj/item/clothing/under/color/rainbow : /obj/item/clothing/under/color/jumpskirt/rainbow
else
uniform = /obj/item/clothing/under/color/jumpskirt/random
uniform = suited ? /obj/item/clothing/under/color/random : /obj/item/clothing/under/color/jumpskirt/random
/obj/item/storage/box/syndie_kit/chameleon/ghostcafe
name = "ghost cafe costuming kit"
+13
View File
@@ -185,3 +185,16 @@
desc = "A wall made out of a strange metal. The squares on it pulse in a predictable pattern."
icon = 'icons/turf/walls/hierophant_wall.dmi'
icon_state = "wall"
/turf/closed/indestructible/rock/glacierrock
name = "unaturally hard ice wall"
desc = "Ice, hardened over thousands of years, you're not breaking through this."
icon = 'icons/turf/walls.dmi'
icon_state = "snow_rock"
/turf/closed/indestructible/rock/glacierrock/blue
name = "blue ice wall"
desc = "The incredible compressive forces that formed this sturdy ice wall gave it a blue color."
icon = 'icons/turf/walls.dmi'
icon_state = "ice"
canSmoothWith = list(/turf/closed/indestructible/rock/glacierrock/blue)
@@ -157,7 +157,7 @@
planetary_atmos = TRUE
floor_tile = null
initial_gas_mix = FROZEN_ATMOS
slowdown = 2
slowdown = 1.5 //So digging it out paths are usefull.
bullet_sizzle = TRUE
footstep = FOOTSTEP_SAND
barefootstep = FOOTSTEP_SAND
@@ -146,95 +146,3 @@
/turf/open/floor/plasteel/sepia
icon_state = "sepia"
///////////////////////////////
// Pre-Applied Decal Floors //
//////////////////////////////
// Neutral
/turf/open/floor/plasteel/neutral
icon_state = "neutral_full"
/turf/open/floor/plasteel/neutral/side
icon_state = "neutral"
/turf/open/floor/plasteel/neutral/corner
icon_state = "neutral_corner"
// Dark Neutral
/turf/open/floor/plasteel/dark/neutral
icon_state = "dark_neutral_full"
/turf/open/floor/plasteel/dark/neutral/checker
icon_state = "dark_neutral_checker"
/turf/open/floor/plasteel/dark/neutral/side
icon_state = "dark_neutral"
/turf/open/floor/plasteel/dark/neutral/corner
icon_state = "dark_neutral_corner"
// Dark Security
/turf/open/floor/plasteel/dark/security
icon_state = "dark_red_full"
/turf/open/floor/plasteel/dark/security/checker
icon_state = "dark_red_checker"
/turf/open/floor/plasteel/dark/security/side
icon_state = "dark_red"
/turf/open/floor/plasteel/dark/security/corner
icon_state = "dark_red_corner"
// Engineering
/turf/open/floor/plasteel/engineering
icon_state = "engineering_full"
/turf/open/floor/plasteel/engineering/side
icon_state = "engineering"
/turf/open/floor/plasteel/engineering/corner
icon_state = "engineering_corner"
// Atmospherics
/turf/open/floor/plasteel/atmospherics
icon_state = "atmospherics_full"
/turf/open/floor/plasteel/atmospherics/side
icon_state = "atmospherics"
/turf/open/floor/plasteel/atmospherics/corner
icon_state = "atmospherics_corner"
// Command
/turf/open/floor/plasteel/command
icon_state = "command_full"
/turf/open/floor/plasteel/command/side
icon_state = "command"
/turf/open/floor/plasteel/command/corner
icon_state = "command_corner"
// Medical
/turf/open/floor/plasteel/medical
icon_state = "medical_full"
/turf/open/floor/plasteel/medical/alt
icon_state = "medical_alt"
/turf/open/floor/plasteel/medical/side
icon_state = "medical"
/turf/open/floor/plasteel/medical/corner
icon_state = "medical_corner"
// Security
/turf/open/floor/plasteel/security
icon_state = "security_full"
/turf/open/floor/plasteel/security/side
icon_state = "security"
/turf/open/floor/plasteel/security/corner
icon_state = "security_corner"
// Cargo
/turf/open/floor/plasteel/cargo
icon_state = "cargo_full"
/turf/open/floor/plasteel/cargo/side
icon_state = "cargo"
/turf/open/floor/plasteel/cargo/corner
icon_state = "cargo_corner"
// Misc
/turf/open/floor/plasteel/showroomfloor/shower
icon_state = "shower"
/turf/open/floor/plasteel/goonplaque/alien
icon_state = "plaque1"
desc = "\"This is a plaque is a collaboration of iconography celebrating the peaceful collaboration between the people of Earth and distant alien species."
/turf/open/floor/plasteel/goonplaque/charter
icon_state = "plaque2"
desc = "\"A golden plaque. Etched into it is the introductory article for a cross-species interplanetary constitution, guaranteeing equal rights between species that Nanotrasen relunctantly agreed to."
@@ -1,4 +1,5 @@
#define VV_HTML_ENCODE(thing) ( sanitize ? html_encode(thing) : thing )
/// Get displayed variable in VV variable list
/proc/debug_variable(name, value, level, datum/D, sanitize = TRUE) //if D is a list, name will be index, and value will be assoc value.
var/header
if(D)
@@ -35,6 +36,19 @@
else if (isfile(value))
item = "[VV_HTML_ENCODE(name)] = <span class='value'>'[value]'</span>"
else if(istype(value, /matrix)) // Needs to be before datum
var/matrix/M = value
item = {"[VV_HTML_ENCODE(name)] = <span class='value'>
<table class='matrixbrak'><tbody><tr>
<td class='lbrak'>&nbsp;</td>
<td><table class='matrix'><tbody>
<tr><td>[M.a]</td><td>[M.d]</td><td>0</td></tr>
<tr><td>[M.b]</td><td>[M.e]</td><td>0</td></tr>
<tr><td>[M.c]</td><td>[M.f]</td><td>1</td></tr>
</tbody></table></td>
<td class='rbrak'>&nbsp;</td>
</tr></tbody></table></span>"} //TODO link to modify_transform wrapper for all matrices
else if (istype(value, /datum))
var/datum/DV = value
if ("[DV]" != "[DV.type]") //if the thing as a name var, lets use it.
@@ -96,16 +96,7 @@
<head>
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>
<title>[title]</title>
<style>
body {
font-family: Verdana, sans-serif;
font-size: 9pt;
}
.value {
font-family: "Courier New", monospace;
font-size: 8pt;
}
</style>
<link rel="stylesheet" type="text/css" href="view_variables.css">
</head>
<body onload='selectTextField()' onkeydown='return handle_keydown()' onkeyup='handle_keyup()'>
<script type="text/javascript">
@@ -91,6 +91,7 @@
B.organ_flags |= ORGAN_VITAL
B.decoy_override = FALSE
remove_changeling_powers()
owner.special_role = null
. = ..()
/datum/antagonist/changeling/proc/remove_clownmut()
@@ -1,21 +1,31 @@
//horrifying power drain proc made for clockcult's power drain in lieu of six istypes or six for(x in view) loops
/atom/movable/proc/power_drain(clockcult_user)
/atom/movable/proc/power_drain(clockcult_user, drain_weapons = FALSE) //This proc as of now is only in use for void volt
var/obj/item/stock_parts/cell/cell = get_cell()
if(cell)
return cell.power_drain(clockcult_user)
return 0
return 0 //Returns 0 instead of FALSE to symbolise it returning the power amount in other cases, not TRUE aka 1
/obj/item/melee/baton/power_drain(clockcult_user) //balance memes
return 0
/obj/item/melee/baton/power_drain(clockcult_user, drain_weapons = FALSE) //balance memes
if(!drain_weapons)
return 0
return ..()
/obj/item/gun/power_drain(clockcult_user) //balance memes
return 0
/obj/item/gun/power_drain(clockcult_user, drain_weapons = FALSE) //balance memes
if(!drain_weapons)
return 0
var/obj/item/stock_parts/cell/cell = get_cell()
if(!cell)
return 0
if(cell.charge)
. = min(cell.charge, MIN_CLOCKCULT_POWER*4) //Done snowflakey because guns have far smaller cells than batons / other equipment
cell.use(.)
update_icon()
/obj/machinery/power/apc/power_drain(clockcult_user)
/obj/machinery/power/apc/power_drain(clockcult_user, drain_weapons = FALSE)
if(cell && cell.charge)
playsound(src, "sparks", 50, 1)
flick("apc-spark", src)
. = min(cell.charge, MIN_CLOCKCULT_POWER*3)
. = min(cell.charge, MIN_CLOCKCULT_POWER*4)
cell.use(.) //Better than a power sink!
if(!cell.charge && !shorted)
shorted = 1
@@ -23,9 +33,9 @@
update()
update_icon()
/obj/machinery/power/smes/power_drain(clockcult_user)
/obj/machinery/power/smes/power_drain(clockcult_user, drain_weapons = FALSE)
if(charge)
. = min(charge, MIN_CLOCKCULT_POWER*3)
. = min(charge, MIN_CLOCKCULT_POWER*4)
charge -= . * 50
if(!charge && !panel_open)
panel_open = TRUE
@@ -34,19 +44,19 @@
visible_message("<span class='warning'>[src]'s panel flies open with a flurry of sparks!</span>")
update_icon()
/obj/item/stock_parts/cell/power_drain(clockcult_user)
/obj/item/stock_parts/cell/power_drain(clockcult_user, drain_weapons = FALSE)
if(charge)
. = min(charge, MIN_CLOCKCULT_POWER*3)
charge = use(.)
. = min(charge, MIN_CLOCKCULT_POWER * 4) //Done like this because normal cells are usually quite a bit bigger than the ones used in guns / APCs
use(min(charge, . * 10)) //Usually cell-powered equipment that is not a gun has at least ten times the capacity of a gun / 5 times the amount of an APC. This adjusts the drain to account for that.
update_icon()
/mob/living/silicon/robot/power_drain(clockcult_user)
/mob/living/silicon/robot/power_drain(clockcult_user, drain_weapons = FALSE)
if((!clockcult_user || !is_servant_of_ratvar(src)) && cell && cell.charge)
. = min(cell.charge, MIN_CLOCKCULT_POWER*4)
cell.use(.)
spark_system.start()
/obj/mecha/power_drain(clockcult_user)
/obj/mecha/power_drain(clockcult_user, drain_weapons = FALSE)
if((!clockcult_user || (occupant && !is_servant_of_ratvar(occupant))) && cell && cell.charge)
. = min(cell.charge, MIN_CLOCKCULT_POWER*4)
cell.use(.)
@@ -135,6 +135,29 @@
return TRUE
//For the Volt Void scripture, fires a ray of energy at a target location
/obj/effect/proc_holder/slab/volt
ranged_mousepointer = 'icons/effects/volt_target.dmi'
/obj/effect/proc_holder/slab/volt/InterceptClickOn(mob/living/caller, params, atom/target)
if(target == slab || ..()) //we can't cancel
return TRUE
var/turf/T = ranged_ability_user.loc
if(!isturf(T))
return TRUE
if(target in view(7, get_turf(ranged_ability_user)))
successful = TRUE
ranged_ability_user.visible_message("<span class='warning'>[ranged_ability_user] fires a ray of energy at [target]!</span>", "<span class='nzcrentr'>You fire a volt ray at [target].</span>")
playsound(ranged_ability_user, 'sound/effects/light_flicker.ogg', 50, 1)
T = get_turf(target)
new/obj/effect/temp_visual/ratvar/volt_hit(T, ranged_ability_user)
log_combat(ranged_ability_user, T, "fired a volt ray")
remove_ranged_ability()
return TRUE
//For the Kindle scripture; stuns and mutes a target non-servant.
/obj/effect/proc_holder/slab/kindle
ranged_mousepointer = 'icons/effects/volt_target.dmi'
@@ -203,6 +203,10 @@ Applications: 8 servants, 3 caches, and 100 CV
if(!do_after(invoker, chant_interval, target = invoker, extra_checks = CALLBACK(src, .proc/can_recite)))
break
clockwork_say(invoker, text2ratvar(pick(chant_invocations)), whispered)
if(multiple_invokers_used)
for(var/mob/living/L in range(1, get_turf(invoker)))
if(can_recite_scripture(L) && L != invoker)
clockwork_say(L, text2ratvar(pick(chant_invocations)), whispered)
if(!chant_effects(i))
break
if(invoker && slab)
@@ -115,7 +115,7 @@
tier = SCRIPTURE_SCRIPT
space_allowed = TRUE
primary_component = VANGUARD_COGWHEEL
sort_priority = 5
sort_priority = 6
quickbind = TRUE
quickbind_desc = "Creates a Ratvarian shield, which can absorb energy from attacks for use in powerful bashes."
@@ -131,7 +131,7 @@
usage_tip = "Throwing the spear at a mob will do massive damage and knock them down, but break the spear. You will need to wait for 30 seconds before resummoning it."
tier = SCRIPTURE_SCRIPT
primary_component = VANGUARD_COGWHEEL
sort_priority = 6
sort_priority = 7
important = TRUE
quickbind = TRUE
quickbind_desc = "Permanently binds clockwork armor and a Ratvarian spear to you."
@@ -231,7 +231,7 @@
usage_tip = "This gateway is strictly one-way and will only allow things through the invoker's portal."
tier = SCRIPTURE_SCRIPT
primary_component = GEIS_CAPACITOR
sort_priority = 7
sort_priority = 9
quickbind = TRUE
quickbind_desc = "Allows you to create a one-way Spatial Gateway to a living Servant or Clockwork Obelisk."
@@ -263,3 +263,227 @@
duration = max(duration, 100)
return slab.procure_gateway(invoker, duration, portal_uses)
//Mending Mantra: Channeled for up to ten times over twenty seconds to repair structures and heal allies
/datum/clockwork_scripture/channeled/mending_mantra
descname = "Channeled, Area Healing and Repair"
name = "Mending Mantra"
desc = "Repairs nearby structures and constructs. Servants wearing clockwork armor will also be healed. Channeled every two seconds for a maximum of twenty seconds."
chant_invocations = list("Mend our dents!", "Heal our scratches!", "Repair our gears!")
chant_amount = 10
chant_interval = 20
power_cost = 400
usage_tip = "This is a very effective way to rapidly reinforce a base after an attack."
tier = SCRIPTURE_SCRIPT
primary_component = VANGUARD_COGWHEEL
sort_priority = 8
quickbind = TRUE
quickbind_desc = "Repairs nearby structures and constructs. Servants wearing clockwork armor will also be healed.<br><b>Maximum 10 chants.</b>"
var/heal_attempts = 4
var/heal_amount = 2.5
var/static/list/damage_heal_order = list(BRUTE, BURN, OXY)
var/static/list/heal_finish_messages = list("There, all mended!", "Try not to get too damaged.", "No more dents and scratches for you!", "Champions never die.", "All patched up.", \
"Ah, child, it's okay now.", "Pain is temporary.", "What you do for the Justiciar is eternal.", "Bear this for me.", "Be strong, child.", "Please, be careful!", \
"If you die, you will be remembered.")
var/static/list/heal_target_typecache = typecacheof(list(
/obj/structure/destructible/clockwork,
/obj/machinery/door/airlock/clockwork,
/obj/machinery/door/window/clockwork,
/obj/structure/window/reinforced/clockwork,
/obj/structure/table/reinforced/brass))
var/static/list/ratvarian_armor_typecache = typecacheof(list(
/obj/item/clothing/suit/armor/clockwork,
/obj/item/clothing/head/helmet/clockwork,
/obj/item/clothing/gloves/clockwork,
/obj/item/clothing/shoes/clockwork))
/datum/clockwork_scripture/channeled/mending_mantra/chant_effects(chant_number)
var/turf/T
for(var/atom/movable/M in range(7, invoker))
if(isliving(M))
if(isclockmob(M) || istype(M, /mob/living/simple_animal/drone/cogscarab))
var/mob/living/simple_animal/S = M
if(S.health == S.maxHealth || S.stat == DEAD)
continue
T = get_turf(M)
for(var/i in 1 to heal_attempts)
if(S.health < S.maxHealth)
S.adjustHealth(-heal_amount)
new /obj/effect/temp_visual/heal(T, "#1E8CE1")
if(i == heal_attempts && S.health >= S.maxHealth) //we finished healing on the last tick, give them the message
to_chat(S, "<span class='inathneq'>\"[text2ratvar(pick(heal_finish_messages))]\"</span>")
break
else
to_chat(S, "<span class='inathneq'>\"[text2ratvar(pick(heal_finish_messages))]\"</span>")
break
else if(issilicon(M))
var/mob/living/silicon/S = M
if(S.health == S.maxHealth || S.stat == DEAD || !is_servant_of_ratvar(S))
continue
T = get_turf(M)
for(var/i in 1 to heal_attempts)
if(S.health < S.maxHealth)
S.heal_ordered_damage(heal_amount, damage_heal_order)
new /obj/effect/temp_visual/heal(T, "#1E8CE1")
if(i == heal_attempts && S.health >= S.maxHealth)
to_chat(S, "<span class='inathneq'>\"[text2ratvar(pick(heal_finish_messages))]\"</span>")
break
else
to_chat(S, "<span class='inathneq'>\"[text2ratvar(pick(heal_finish_messages))]\"</span>")
break
else if(ishuman(M))
var/mob/living/carbon/human/H = M
if(H.health == H.maxHealth || H.stat == DEAD || !is_servant_of_ratvar(H))
continue
T = get_turf(M)
var/heal_ticks = 0 //one heal tick for each piece of ratvarian armor worn
var/obj/item/I = H.get_item_by_slot(SLOT_WEAR_SUIT)
if(is_type_in_typecache(I, ratvarian_armor_typecache))
heal_ticks++
I = H.get_item_by_slot(SLOT_HEAD)
if(is_type_in_typecache(I, ratvarian_armor_typecache))
heal_ticks++
I = H.get_item_by_slot(SLOT_GLOVES)
if(is_type_in_typecache(I, ratvarian_armor_typecache))
heal_ticks++
I = H.get_item_by_slot(SLOT_SHOES)
if(is_type_in_typecache(I, ratvarian_armor_typecache))
heal_ticks++
if(heal_ticks)
for(var/i in 1 to heal_ticks)
if(H.health < H.maxHealth)
H.heal_ordered_damage(heal_amount, damage_heal_order)
new /obj/effect/temp_visual/heal(T, "#1E8CE1")
if(i == heal_ticks && H.health >= H.maxHealth)
to_chat(H, "<span class='inathneq'>\"[text2ratvar(pick(heal_finish_messages))]\"</span>")
break
else
to_chat(H, "<span class='inathneq'>\"[text2ratvar(pick(heal_finish_messages))]\"</span>")
break
else if(is_type_in_typecache(M, heal_target_typecache))
var/obj/structure/destructible/clockwork/C = M
if(C.obj_integrity == C.max_integrity || (istype(C) && !C.can_be_repaired))
continue
T = get_turf(M)
for(var/i in 1 to heal_attempts)
if(C.obj_integrity < C.max_integrity)
C.obj_integrity = min(C.obj_integrity + 5, C.max_integrity)
C.update_icon()
new /obj/effect/temp_visual/heal(T, "#1E8CE1")
else
break
new /obj/effect/temp_visual/ratvar/mending_mantra(get_turf(invoker))
return TRUE
//Volt Blaster: Channeled for up to five times over ten seconds to fire up to five rays of energy at target locations.
/datum/clockwork_scripture/channeled/volt_blaster
descname = "Channeled, Targeted Energy Blasts"
name = "Volt Blaster"
desc = "Allows you to fire five energy rays at target locations. Channeled every fourth of a second for a maximum of ten seconds."
channel_time = 30
invocations = list("Amperage...", "...grant me your power!")
chant_invocations = list("Use charge to kill!", "Slay with power!", "Hunt with energy!")
chant_amount = 5
chant_interval = 4
power_cost = 500
usage_tip = "Though it requires you to stand still, this scripture can do massive damage."
tier = SCRIPTURE_SCRIPT
primary_component = BELLIGERENT_EYE
sort_priority = 5
quickbind = TRUE
quickbind_desc = "Allows you to fire energy rays at target locations.<br><b>Maximum 5 chants.</b>"
var/static/list/nzcrentr_insults = list("You're not very good at aiming.", "You hunt badly.", "What a waste of energy.", "Almost funny to watch.",
"Boss says </span><span class='heavy_brass'>\"Click something, you idiot!\"</span><span class='nzcrentr'>.", "Stop wasting power if you can't aim.")
/datum/clockwork_scripture/channeled/volt_blaster/chant_effects(chant_number)
slab.busy = null
var/datum/clockwork_scripture/ranged_ability/volt_ray/ray = new
ray.slab = slab
ray.invoker = invoker
var/turf/T = get_turf(invoker)
if(!ray.run_scripture() && slab && invoker)
if(can_recite() && T == get_turf(invoker))
to_chat(invoker, "<span class='nzcrentr'>\"[text2ratvar(pick(nzcrentr_insults))]\"</span>")
else
return FALSE
return TRUE
/obj/effect/ebeam/volt_ray
name = "volt_ray"
layer = LYING_MOB_LAYER
/datum/clockwork_scripture/ranged_ability/volt_ray
name = "Volt Ray"
slab_overlay = "volt"
allow_mobility = FALSE
ranged_type = /obj/effect/proc_holder/slab/volt
ranged_message = "<span class='nzcrentr_small'><i>You charge the clockwork slab with shocking might.</i>\n\
<b>Left-click a target to fire, quickly!</b></span>"
timeout_time = 20
/datum/clockwork_scripture/channeled/void_volt
descname = "Channeled, Power Drain"
name = "Void Volt"
desc = "A channeled spell that quickly drains any powercells in a radius of eight tiles, but burns the invoker. \
Can be channeled with more cultists to increase range and split the caused damage evenly over all invokers. \
Also charges clockwork power by a small percentage of the drained power amount, which can help offset this scriptures powercost."
invocations = list("Channel their energy through my body... ", "... so it may fuel Engine!")
chant_invocations = list("Make their lights fall dark!", "They shall be powerless!", "Rob them of their power!")
chant_amount = 20
chant_interval = 10 //100KW drain per pulse for guns / APCs / 1MW for other cells = 10 chants / 100ds / 10s to drain a charged weapon or a baton with a nonupgraded cell
channel_time = 50
power_cost = 300
multiple_invokers_used = TRUE
multiple_invokers_optional = TRUE
usage_tip = "It may be useful to end channelling early if the burning becomes too much to handle.."
tier = SCRIPTURE_SCRIPT
primary_component = GEIS_CAPACITOR
sort_priority = 10
quickbind = TRUE
quickbind_desc = "Quickly drains power in an area around the invoker, causing burns proportional to the amount of energy drained.<br><b>Maximum of 20 chants.</b>"
/datum/clockwork_scripture/channeled/void_volt/scripture_effects()
invoker.visible_message("<span class='warning'>[invoker] glows in a brilliant golden light!</span>")
invoker.add_atom_colour("#FFD700", ADMIN_COLOUR_PRIORITY)
invoker.light_power = 2
invoker.light_range = 4
invoker.light_color = LIGHT_COLOR_FIRE
invoker.update_light()
return ..()
/datum/clockwork_scripture/channeled/void_volt/chant_effects(chant_number)
var/power_drained = 0
var/power_mod = 0.005 //Amount of power drained (generally) is multiplied with this, and subsequently dealt in damage to the invoker, then 15 times that is added to the clockwork cult's power reserves.
var/drain_range = 8
var/additional_chanters = 0
var/list/chanters = list()
chanters += invoker
for(var/mob/living/L in range(1, invoker))
if(!L.stat && is_servant_of_ratvar(L))
additional_chanters++
chanters += L
drain_range = min(drain_range + 2 * additional_chanters, drain_range * 2) //s u c c
for(var/t in spiral_range_turfs(drain_range, invoker))
var/turf/T = t
for(var/M in T)
var/atom/movable/A = M
power_drained += A.power_drain(TRUE, TRUE) //Yes, this absolutely does drain weaponry. 10 pulses to drain guns / batons, though of course they can just be recharged.
new /obj/effect/temp_visual/ratvar/sigil/transgression(invoker.loc, 1 + (power_drained * power_mod))
var/datum/effect_system/spark_spread/S = new
S.set_up(round(1 + (power_drained * power_mod), 1), 0, get_turf(invoker))
S.start()
adjust_clockwork_power(power_drained * power_mod * 15)
for(var/mob/living/L in chanters)
L.adjustFireLoss(round(clamp(power_drained * power_mod / (1 + additional_chanters), 0, 20), 0.1)) //No you won't just immediately melt if you do this in a very power-rich area
return TRUE
/datum/clockwork_scripture/channeled/void_volt/chant_end_effects()
invoker.visible_message("<span class='warning'>[invoker] stops glowing...</span>")
invoker.remove_atom_colour(ADMIN_COLOUR_PRIORITY)
invoker.light_power = 0
invoker.light_range = 0
invoker.update_light()
return ..()
+1 -1
View File
@@ -343,7 +343,7 @@
icon = 'icons/obj/items_and_weapons.dmi'
icon_state = "disintegrate"
item_state = null
item_flags = NEEDS_PERMIT | ABSTRACT | DROPDEL | NO_ATTACK_CHAIN_SOFT_STAMCRIT
item_flags = NEEDS_PERMIT | ABSTRACT | DROPDEL
w_class = WEIGHT_CLASS_HUGE
throwforce = 0
@@ -118,22 +118,10 @@
/mob/living/carbon/true_devil/attacked_by(obj/item/I, mob/living/user, def_zone)
var/weakness = check_weakness(I, user)
apply_damage(I.force * weakness, I.damtype, def_zone)
var/message_verb = ""
if(I.attack_verb && I.attack_verb.len)
message_verb = "[pick(I.attack_verb)]"
else if(I.force)
message_verb = "attacked"
var/attack_message = "[src] has been [message_verb] with [I]."
if(user)
user.do_attack_animation(src)
if(user in viewers(src, null))
attack_message = "[user] has [message_verb] [src] with [I]!"
if(message_verb)
visible_message("<span class='danger'>[attack_message]</span>",
"<span class='userdanger'>[attack_message]</span>", null, COMBAT_MESSAGE_RANGE)
var/totitemdamage = pre_attacked_by(I, user)
totitemdamage *= check_weakness(I, user)
apply_damage(totitemdamage, I.damtype, def_zone)
send_item_attack_message(I, user, null, totitemdamage)
return TRUE
/mob/living/carbon/true_devil/singularity_act()
+2 -2
View File
@@ -171,10 +171,10 @@
var/worth = 10
var/gases = C.air_contents.gases
worth += gases[/datum/gas/bz]*4
worth += gases[/datum/gas/bz]*3
worth += gases[/datum/gas/stimulum]*25
worth += gases[/datum/gas/hypernoblium]*1000
worth += gases[/datum/gas/miasma]*4
worth += gases[/datum/gas/miasma]*2
worth += gases[/datum/gas/tritium]*7
worth += gases[/datum/gas/pluoxium]*6
worth += gases[/datum/gas/nitryl]*30
+10
View File
@@ -66,6 +66,16 @@
crate_name = "shaft miner starter kit"
crate_type = /obj/structure/closet/crate/secure
/datum/supply_pack/service/snowmobile
name = "Snowmobile kit"
desc = "trapped on a frigid wasteland? need to get around fast? purchase a refurbished snowmobile, with a FREE 10 microsecond warranty!"
cost = 1500 // 1000 points cheaper than ATV
contains = list(/obj/vehicle/ridden/atv/snowmobile = 1,
/obj/item/key = 1,
/obj/item/clothing/mask/gas/explorer = 1)
crate_name = "Snowmobile kit"
crate_type = /obj/structure/closet/crate/large
//////////////////////////////////////////////////////////////////////////////
/////////////////////// Chef, Botanist, Bartender ////////////////////////////
//////////////////////////////////////////////////////////////////////////////
+751
View File
@@ -0,0 +1,751 @@
/*
Asset cache quick users guide:
Make a datum at the bottom of this file with your assets for your thing.
The simple subsystem will most like be of use for most cases.
Then call get_asset_datum() with the type of the datum you created and store the return
Then call .send(client) on that stored return value.
You can set verify to TRUE if you want send() to sleep until the client has the assets.
*/
// Amount of time(ds) MAX to send per asset, if this get exceeded we cancel the sleeping.
// This is doubled for the first asset, then added per asset after
#define ASSET_CACHE_SEND_TIMEOUT 7
//When sending mutiple assets, how many before we give the client a quaint little sending resources message
#define ASSET_CACHE_TELL_CLIENT_AMOUNT 8
//When passively preloading assets, how many to send at once? Too high creates noticable lag where as too low can flood the client's cache with "verify" files
#define ASSET_CACHE_PRELOAD_CONCURRENT 3
/client
var/list/cache = list() // List of all assets sent to this client by the asset cache.
var/list/completed_asset_jobs = list() // List of all completed jobs, awaiting acknowledgement.
var/list/sending = list()
var/last_asset_job = 0 // Last job done.
//This proc sends the asset to the client, but only if it needs it.
//This proc blocks(sleeps) unless verify is set to false
/proc/send_asset(client/client, asset_name, verify = TRUE)
if(!istype(client))
if(ismob(client))
var/mob/M = client
if(M.client)
client = M.client
else
return 0
else
return 0
if(client.cache.Find(asset_name) || client.sending.Find(asset_name))
return 0
client << browse_rsc(SSassets.cache[asset_name], asset_name)
if(!verify)
client.cache += asset_name
return 1
client.sending |= asset_name
var/job = ++client.last_asset_job
client << browse({"
<script>
window.location.href="?asset_cache_confirm_arrival=[job]"
</script>
"}, "window=asset_cache_browser")
var/t = 0
var/timeout_time = (ASSET_CACHE_SEND_TIMEOUT * client.sending.len) + ASSET_CACHE_SEND_TIMEOUT
while(client && !client.completed_asset_jobs.Find(job) && t < timeout_time) // Reception is handled in Topic()
stoplag(1) // Lock up the caller until this is received.
t++
if(client)
client.sending -= asset_name
client.cache |= asset_name
client.completed_asset_jobs -= job
return 1
//This proc blocks(sleeps) unless verify is set to false
/proc/send_asset_list(client/client, list/asset_list, verify = TRUE)
if(!istype(client))
if(ismob(client))
var/mob/M = client
if(M.client)
client = M.client
else
return 0
else
return 0
var/list/unreceived = asset_list - (client.cache + client.sending)
if(!unreceived || !unreceived.len)
return 0
if (unreceived.len >= ASSET_CACHE_TELL_CLIENT_AMOUNT)
to_chat(client, "Sending Resources...")
for(var/asset in unreceived)
if (asset in SSassets.cache)
client << browse_rsc(SSassets.cache[asset], asset)
if(!verify) // Can't access the asset cache browser, rip.
client.cache += unreceived
return 1
client.sending |= unreceived
var/job = ++client.last_asset_job
client << browse({"
<script>
window.location.href="?asset_cache_confirm_arrival=[job]"
</script>
"}, "window=asset_cache_browser")
var/t = 0
var/timeout_time = ASSET_CACHE_SEND_TIMEOUT * client.sending.len
while(client && !client.completed_asset_jobs.Find(job) && t < timeout_time) // Reception is handled in Topic()
stoplag(1) // Lock up the caller until this is received.
t++
if(client)
client.sending -= unreceived
client.cache |= unreceived
client.completed_asset_jobs -= job
return 1
//This proc will download the files without clogging up the browse() queue, used for passively sending files on connection start.
//The proc calls procs that sleep for long times.
/proc/getFilesSlow(client/client, list/files, register_asset = TRUE)
var/concurrent_tracker = 1
for(var/file in files)
if (!client)
break
if (register_asset)
register_asset(file, files[file])
if (concurrent_tracker >= ASSET_CACHE_PRELOAD_CONCURRENT)
concurrent_tracker = 1
send_asset(client, file)
else
concurrent_tracker++
send_asset(client, file, verify=FALSE)
stoplag(0) //queuing calls like this too quickly can cause issues in some client versions
//This proc "registers" an asset, it adds it to the cache for further use, you cannot touch it from this point on or you'll fuck things up.
//if it's an icon or something be careful, you'll have to copy it before further use.
/proc/register_asset(asset_name, asset)
SSassets.cache[asset_name] = asset
//Generated names do not include file extention.
//Used mainly for code that deals with assets in a generic way
//The same asset will always lead to the same asset name
/proc/generate_asset_name(file)
return "asset.[md5(fcopy_rsc(file))]"
//These datums are used to populate the asset cache, the proc "register()" does this.
//all of our asset datums, used for referring to these later
GLOBAL_LIST_EMPTY(asset_datums)
//get an assetdatum or make a new one
/proc/get_asset_datum(type)
return GLOB.asset_datums[type] || new type()
/datum/asset
var/_abstract = /datum/asset
/datum/asset/New()
GLOB.asset_datums[type] = src
register()
/datum/asset/proc/register()
return
/datum/asset/proc/send(client)
return
//If you don't need anything complicated.
/datum/asset/simple
_abstract = /datum/asset/simple
var/assets = list()
var/verify = FALSE
/datum/asset/simple/register()
for(var/asset_name in assets)
register_asset(asset_name, assets[asset_name])
/datum/asset/simple/send(client)
send_asset_list(client,assets,verify)
// For registering or sending multiple others at once
/datum/asset/group
_abstract = /datum/asset/group
var/list/children
/datum/asset/group/register()
for(var/type in children)
get_asset_datum(type)
/datum/asset/group/send(client/C)
for(var/type in children)
var/datum/asset/A = get_asset_datum(type)
A.send(C)
// spritesheet implementation - coalesces various icons into a single .png file
// and uses CSS to select icons out of that file - saves on transferring some
// 1400-odd individual PNG files
#define SPR_SIZE 1
#define SPR_IDX 2
#define SPRSZ_COUNT 1
#define SPRSZ_ICON 2
#define SPRSZ_STRIPPED 3
/datum/asset/spritesheet
_abstract = /datum/asset/spritesheet
var/name
var/list/sizes = list() // "32x32" -> list(10, icon/normal, icon/stripped)
var/list/sprites = list() // "foo_bar" -> list("32x32", 5)
var/verify = FALSE
/datum/asset/spritesheet/register()
if (!name)
CRASH("spritesheet [type] cannot register without a name")
ensure_stripped()
var/res_name = "spritesheet_[name].css"
var/fname = "data/spritesheets/[res_name]"
fdel(fname)
text2file(generate_css(), fname)
register_asset(res_name, fcopy_rsc(fname))
fdel(fname)
for(var/size_id in sizes)
var/size = sizes[size_id]
register_asset("[name]_[size_id].png", size[SPRSZ_STRIPPED])
/datum/asset/spritesheet/send(client/C)
if (!name)
return
var/all = list("spritesheet_[name].css")
for(var/size_id in sizes)
all += "[name]_[size_id].png"
send_asset_list(C, all, verify)
/datum/asset/spritesheet/proc/ensure_stripped(sizes_to_strip = sizes)
for(var/size_id in sizes_to_strip)
var/size = sizes[size_id]
if (size[SPRSZ_STRIPPED])
continue
// save flattened version
var/fname = "data/spritesheets/[name]_[size_id].png"
fcopy(size[SPRSZ_ICON], fname)
var/error = rustg_dmi_strip_metadata(fname)
if(length(error))
stack_trace("Failed to strip [name]_[size_id].png: [error]")
size[SPRSZ_STRIPPED] = icon(fname)
fdel(fname)
/datum/asset/spritesheet/proc/generate_css()
var/list/out = list()
for (var/size_id in sizes)
var/size = sizes[size_id]
var/icon/tiny = size[SPRSZ_ICON]
out += ".[name][size_id]{display:inline-block;width:[tiny.Width()]px;height:[tiny.Height()]px;background:url('[name]_[size_id].png') no-repeat;}"
for (var/sprite_id in sprites)
var/sprite = sprites[sprite_id]
var/size_id = sprite[SPR_SIZE]
var/idx = sprite[SPR_IDX]
var/size = sizes[size_id]
var/icon/tiny = size[SPRSZ_ICON]
var/icon/big = size[SPRSZ_STRIPPED]
var/per_line = big.Width() / tiny.Width()
var/x = (idx % per_line) * tiny.Width()
var/y = round(idx / per_line) * tiny.Height()
out += ".[name][size_id].[sprite_id]{background-position:-[x]px -[y]px;}"
return out.Join("\n")
/datum/asset/spritesheet/proc/Insert(sprite_name, icon/I, icon_state="", dir=SOUTH, frame=1, moving=FALSE)
I = icon(I, icon_state=icon_state, dir=dir, frame=frame, moving=moving)
if (!I || !length(icon_states(I))) // that direction or state doesn't exist
return
var/size_id = "[I.Width()]x[I.Height()]"
var/size = sizes[size_id]
if (sprites[sprite_name])
CRASH("duplicate sprite \"[sprite_name]\" in sheet [name] ([type])")
if (size)
var/position = size[SPRSZ_COUNT]++
var/icon/sheet = size[SPRSZ_ICON]
size[SPRSZ_STRIPPED] = null
sheet.Insert(I, icon_state=sprite_name)
sprites[sprite_name] = list(size_id, position)
else
sizes[size_id] = size = list(1, I, null)
sprites[sprite_name] = list(size_id, 0)
/datum/asset/spritesheet/proc/InsertAll(prefix, icon/I, list/directions)
if (length(prefix))
prefix = "[prefix]-"
if (!directions)
directions = list(SOUTH)
for (var/icon_state_name in icon_states(I))
for (var/direction in directions)
var/prefix2 = (directions.len > 1) ? "[dir2text(direction)]-" : ""
Insert("[prefix][prefix2][icon_state_name]", I, icon_state=icon_state_name, dir=direction)
/datum/asset/spritesheet/proc/css_tag()
return {"<link rel="stylesheet" href="spritesheet_[name].css" />"}
/datum/asset/spritesheet/proc/icon_tag(sprite_name)
var/sprite = sprites[sprite_name]
if (!sprite)
return null
var/size_id = sprite[SPR_SIZE]
return {"<span class="[name][size_id] [sprite_name]"></span>"}
/datum/asset/spritesheet/proc/icon_class_name(sprite_name)
var/sprite = sprites[sprite_name]
if (!sprite)
return null
var/size_id = sprite[SPR_SIZE]
return {"[name][size_id] [sprite_name]"}
#undef SPR_SIZE
#undef SPR_IDX
#undef SPRSZ_COUNT
#undef SPRSZ_ICON
#undef SPRSZ_STRIPPED
/datum/asset/spritesheet/simple
_abstract = /datum/asset/spritesheet/simple
var/list/assets
/datum/asset/spritesheet/simple/register()
for (var/key in assets)
Insert(key, assets[key])
..()
//Generates assets based on iconstates of a single icon
/datum/asset/simple/icon_states
_abstract = /datum/asset/simple/icon_states
var/icon
var/list/directions = list(SOUTH)
var/frame = 1
var/movement_states = FALSE
var/prefix = "default" //asset_name = "[prefix].[icon_state_name].png"
var/generic_icon_names = FALSE //generate icon filenames using generate_asset_name() instead the above format
verify = FALSE
/datum/asset/simple/icon_states/register(_icon = icon)
for(var/icon_state_name in icon_states(_icon))
for(var/direction in directions)
var/asset = icon(_icon, icon_state_name, direction, frame, movement_states)
if (!asset)
continue
asset = fcopy_rsc(asset) //dedupe
var/prefix2 = (directions.len > 1) ? "[dir2text(direction)]." : ""
var/asset_name = sanitize_filename("[prefix].[prefix2][icon_state_name].png")
if (generic_icon_names)
asset_name = "[generate_asset_name(asset)].png"
register_asset(asset_name, asset)
/datum/asset/simple/icon_states/multiple_icons
_abstract = /datum/asset/simple/icon_states/multiple_icons
var/list/icons
/datum/asset/simple/icon_states/multiple_icons/register()
for(var/i in icons)
..(i)
//DEFINITIONS FOR ASSET DATUMS START HERE.
/datum/asset/simple/tgui
assets = list(
// tgui
"tgui.css" = 'tgui/assets/tgui.css',
"tgui.js" = 'tgui/assets/tgui.js',
// tgui-next
"tgui-main.html" = 'tgui-next/packages/tgui/public/tgui-main.html',
"tgui-fallback.html" = 'tgui-next/packages/tgui/public/tgui-fallback.html',
"tgui.bundle.js" = 'tgui-next/packages/tgui/public/tgui.bundle.js',
"tgui.bundle.css" = 'tgui-next/packages/tgui/public/tgui.bundle.css',
"shim-html5shiv.js" = 'tgui-next/packages/tgui/public/shim-html5shiv.js',
"shim-ie8.js" = 'tgui-next/packages/tgui/public/shim-ie8.js',
"shim-dom4.js" = 'tgui-next/packages/tgui/public/shim-dom4.js',
"shim-css-om.js" = 'tgui-next/packages/tgui/public/shim-css-om.js',
)
/datum/asset/group/tgui
children = list(
/datum/asset/simple/tgui,
/datum/asset/simple/fontawesome
)
/datum/asset/simple/headers
assets = list(
"alarm_green.gif" = 'icons/program_icons/alarm_green.gif',
"alarm_red.gif" = 'icons/program_icons/alarm_red.gif',
"batt_5.gif" = 'icons/program_icons/batt_5.gif',
"batt_20.gif" = 'icons/program_icons/batt_20.gif',
"batt_40.gif" = 'icons/program_icons/batt_40.gif',
"batt_60.gif" = 'icons/program_icons/batt_60.gif',
"batt_80.gif" = 'icons/program_icons/batt_80.gif',
"batt_100.gif" = 'icons/program_icons/batt_100.gif',
"charging.gif" = 'icons/program_icons/charging.gif',
"downloader_finished.gif" = 'icons/program_icons/downloader_finished.gif',
"downloader_running.gif" = 'icons/program_icons/downloader_running.gif',
"ntnrc_idle.gif" = 'icons/program_icons/ntnrc_idle.gif',
"ntnrc_new.gif" = 'icons/program_icons/ntnrc_new.gif',
"power_norm.gif" = 'icons/program_icons/power_norm.gif',
"power_warn.gif" = 'icons/program_icons/power_warn.gif',
"sig_high.gif" = 'icons/program_icons/sig_high.gif',
"sig_low.gif" = 'icons/program_icons/sig_low.gif',
"sig_lan.gif" = 'icons/program_icons/sig_lan.gif',
"sig_none.gif" = 'icons/program_icons/sig_none.gif',
"smmon_0.gif" = 'icons/program_icons/smmon_0.gif',
"smmon_1.gif" = 'icons/program_icons/smmon_1.gif',
"smmon_2.gif" = 'icons/program_icons/smmon_2.gif',
"smmon_3.gif" = 'icons/program_icons/smmon_3.gif',
"smmon_4.gif" = 'icons/program_icons/smmon_4.gif',
"smmon_5.gif" = 'icons/program_icons/smmon_5.gif',
"smmon_6.gif" = 'icons/program_icons/smmon_6.gif'
)
/datum/asset/spritesheet/simple/pda
name = "pda"
assets = list(
"atmos" = 'icons/pda_icons/pda_atmos.png',
"back" = 'icons/pda_icons/pda_back.png',
"bell" = 'icons/pda_icons/pda_bell.png',
"blank" = 'icons/pda_icons/pda_blank.png',
"boom" = 'icons/pda_icons/pda_boom.png',
"bucket" = 'icons/pda_icons/pda_bucket.png',
"medbot" = 'icons/pda_icons/pda_medbot.png',
"floorbot" = 'icons/pda_icons/pda_floorbot.png',
"cleanbot" = 'icons/pda_icons/pda_cleanbot.png',
"crate" = 'icons/pda_icons/pda_crate.png',
"cuffs" = 'icons/pda_icons/pda_cuffs.png',
"eject" = 'icons/pda_icons/pda_eject.png',
"flashlight" = 'icons/pda_icons/pda_flashlight.png',
"honk" = 'icons/pda_icons/pda_honk.png',
"mail" = 'icons/pda_icons/pda_mail.png',
"medical" = 'icons/pda_icons/pda_medical.png',
"menu" = 'icons/pda_icons/pda_menu.png',
"mule" = 'icons/pda_icons/pda_mule.png',
"notes" = 'icons/pda_icons/pda_notes.png',
"power" = 'icons/pda_icons/pda_power.png',
"rdoor" = 'icons/pda_icons/pda_rdoor.png',
"reagent" = 'icons/pda_icons/pda_reagent.png',
"refresh" = 'icons/pda_icons/pda_refresh.png',
"scanner" = 'icons/pda_icons/pda_scanner.png',
"signaler" = 'icons/pda_icons/pda_signaler.png',
"status" = 'icons/pda_icons/pda_status.png',
"dronephone" = 'icons/pda_icons/pda_dronephone.png',
"emoji" = 'icons/pda_icons/pda_emoji.png'
)
/datum/asset/spritesheet/simple/paper
name = "paper"
assets = list(
"stamp-clown" = 'icons/stamp_icons/large_stamp-clown.png',
"stamp-deny" = 'icons/stamp_icons/large_stamp-deny.png',
"stamp-ok" = 'icons/stamp_icons/large_stamp-ok.png',
"stamp-hop" = 'icons/stamp_icons/large_stamp-hop.png',
"stamp-cmo" = 'icons/stamp_icons/large_stamp-cmo.png',
"stamp-ce" = 'icons/stamp_icons/large_stamp-ce.png',
"stamp-hos" = 'icons/stamp_icons/large_stamp-hos.png',
"stamp-rd" = 'icons/stamp_icons/large_stamp-rd.png',
"stamp-cap" = 'icons/stamp_icons/large_stamp-cap.png',
"stamp-qm" = 'icons/stamp_icons/large_stamp-qm.png',
"stamp-law" = 'icons/stamp_icons/large_stamp-law.png'
)
/datum/asset/spritesheet/simple/minesweeper
name = "minesweeper"
assets = list(
"1" = 'icons/UI_Icons/minesweeper_tiles/one.png',
"2" = 'icons/UI_Icons/minesweeper_tiles/two.png',
"3" = 'icons/UI_Icons/minesweeper_tiles/three.png',
"4" = 'icons/UI_Icons/minesweeper_tiles/four.png',
"5" = 'icons/UI_Icons/minesweeper_tiles/five.png',
"6" = 'icons/UI_Icons/minesweeper_tiles/six.png',
"7" = 'icons/UI_Icons/minesweeper_tiles/seven.png',
"8" = 'icons/UI_Icons/minesweeper_tiles/eight.png',
"empty" = 'icons/UI_Icons/minesweeper_tiles/empty.png',
"flag" = 'icons/UI_Icons/minesweeper_tiles/flag.png',
"hidden" = 'icons/UI_Icons/minesweeper_tiles/hidden.png',
"mine" = 'icons/UI_Icons/minesweeper_tiles/mine.png',
"minehit" = 'icons/UI_Icons/minesweeper_tiles/minehit.png'
)
/datum/asset/spritesheet/simple/pills
name = "pills"
assets = list(
"pill1" = 'icons/UI_Icons/Pills/pill1.png',
"pill2" = 'icons/UI_Icons/Pills/pill2.png',
"pill3" = 'icons/UI_Icons/Pills/pill3.png',
"pill4" = 'icons/UI_Icons/Pills/pill4.png',
"pill5" = 'icons/UI_Icons/Pills/pill5.png',
"pill6" = 'icons/UI_Icons/Pills/pill6.png',
"pill7" = 'icons/UI_Icons/Pills/pill7.png',
"pill8" = 'icons/UI_Icons/Pills/pill8.png',
"pill9" = 'icons/UI_Icons/Pills/pill9.png',
"pill10" = 'icons/UI_Icons/Pills/pill10.png',
"pill11" = 'icons/UI_Icons/Pills/pill11.png',
"pill12" = 'icons/UI_Icons/Pills/pill12.png',
"pill13" = 'icons/UI_Icons/Pills/pill13.png',
"pill14" = 'icons/UI_Icons/Pills/pill14.png',
"pill15" = 'icons/UI_Icons/Pills/pill15.png',
"pill16" = 'icons/UI_Icons/Pills/pill16.png',
"pill17" = 'icons/UI_Icons/Pills/pill17.png',
"pill18" = 'icons/UI_Icons/Pills/pill18.png',
"pill19" = 'icons/UI_Icons/Pills/pill19.png',
"pill20" = 'icons/UI_Icons/Pills/pill20.png',
"pill21" = 'icons/UI_Icons/Pills/pill21.png',
"pill22" = 'icons/UI_Icons/Pills/pill22.png',
)
/datum/asset/simple/IRV
assets = list(
"jquery-ui.custom-core-widgit-mouse-sortable-min.js" = 'html/IRV/jquery-ui.custom-core-widgit-mouse-sortable-min.js',
)
/datum/asset/group/IRV
children = list(
/datum/asset/simple/jquery,
/datum/asset/simple/IRV
)
/datum/asset/simple/changelog
assets = list(
"88x31.png" = 'html/88x31.png',
"bug-minus.png" = 'html/bug-minus.png',
"cross-circle.png" = 'html/cross-circle.png',
"hard-hat-exclamation.png" = 'html/hard-hat-exclamation.png',
"image-minus.png" = 'html/image-minus.png',
"image-plus.png" = 'html/image-plus.png',
"music-minus.png" = 'html/music-minus.png',
"music-plus.png" = 'html/music-plus.png',
"tick-circle.png" = 'html/tick-circle.png',
"wrench-screwdriver.png" = 'html/wrench-screwdriver.png',
"spell-check.png" = 'html/spell-check.png',
"burn-exclamation.png" = 'html/burn-exclamation.png',
"chevron.png" = 'html/chevron.png',
"chevron-expand.png" = 'html/chevron-expand.png',
"scales.png" = 'html/scales.png',
"coding.png" = 'html/coding.png',
"ban.png" = 'html/ban.png',
"chrome-wrench.png" = 'html/chrome-wrench.png',
"changelog.css" = 'html/changelog.css'
)
/datum/asset/group/goonchat
children = list(
/datum/asset/simple/jquery,
/datum/asset/simple/goonchat,
/datum/asset/spritesheet/goonchat,
/datum/asset/simple/fontawesome
)
/datum/asset/simple/jquery
verify = FALSE
assets = list(
"jquery.min.js" = 'code/modules/goonchat/browserassets/js/jquery.min.js',
)
/datum/asset/simple/goonchat
verify = FALSE
assets = list(
"json2.min.js" = 'code/modules/goonchat/browserassets/js/json2.min.js',
"browserOutput.js" = 'code/modules/goonchat/browserassets/js/browserOutput.js',
"browserOutput.css" = 'code/modules/goonchat/browserassets/css/browserOutput.css',
"browserOutput_dark.css" = 'code/modules/goonchat/browserassets/css/browserOutput_dark.css',
"browserOutput_light.css" = 'code/modules/goonchat/browserassets/css/browserOutput_light.css'
)
/datum/asset/simple/fontawesome
verify = FALSE
assets = list(
"fa-regular-400.eot" = 'html/font-awesome/webfonts/fa-regular-400.eot',
"fa-regular-400.woff" = 'html/font-awesome/webfonts/fa-regular-400.woff',
"fa-solid-900.eot" = 'html/font-awesome/webfonts/fa-solid-900.eot',
"fa-solid-900.woff" = 'html/font-awesome/webfonts/fa-solid-900.woff',
"font-awesome.css" = 'html/font-awesome/css/all.min.css',
"v4shim.css" = 'html/font-awesome/css/v4-shims.min.css'
)
/datum/asset/spritesheet/goonchat
name = "chat"
/datum/asset/spritesheet/goonchat/register()
InsertAll("emoji", 'icons/emoji.dmi')
// pre-loading all lanugage icons also helps to avoid meta
InsertAll("language", 'icons/misc/language.dmi')
// catch languages which are pulling icons from another file
for(var/path in typesof(/datum/language))
var/datum/language/L = path
var/icon = initial(L.icon)
if (icon != 'icons/misc/language.dmi')
var/icon_state = initial(L.icon_state)
Insert("language-[icon_state]", icon, icon_state=icon_state)
..()
/datum/asset/simple/permissions
assets = list(
"padlock.png" = 'html/padlock.png'
)
/datum/asset/simple/notes
assets = list(
"high_button.png" = 'html/high_button.png',
"medium_button.png" = 'html/medium_button.png',
"minor_button.png" = 'html/minor_button.png',
"none_button.png" = 'html/none_button.png',
)
//this exists purely to avoid meta by pre-loading all language icons.
/datum/asset/language/register()
for(var/path in typesof(/datum/language))
set waitfor = FALSE
var/datum/language/L = new path ()
L.get_icon()
/datum/asset/spritesheet/pipes
name = "pipes"
/datum/asset/spritesheet/pipes/register()
for (var/each in list('icons/obj/atmospherics/pipes/pipe_item.dmi', 'icons/obj/atmospherics/pipes/disposal.dmi', 'icons/obj/atmospherics/pipes/transit_tube.dmi'))
InsertAll("", each, GLOB.alldirs)
..()
// Representative icons for each research design
/datum/asset/spritesheet/research_designs
name = "design"
/datum/asset/spritesheet/research_designs/register()
for (var/path in subtypesof(/datum/design))
var/datum/design/D = path
var/icon_file
var/icon_state
var/icon/I
if(initial(D.research_icon) && initial(D.research_icon_state)) //If the design has an icon replacement skip the rest
icon_file = initial(D.research_icon)
icon_state = initial(D.research_icon_state)
if(!(icon_state in icon_states(icon_file)))
warning("design [D] with icon '[icon_file]' missing state '[icon_state]'")
continue
I = icon(icon_file, icon_state, SOUTH)
else
// construct the icon and slap it into the resource cache
var/atom/item = initial(D.build_path)
if (!ispath(item, /atom))
// biogenerator outputs to beakers by default
if (initial(D.build_type) & BIOGENERATOR)
item = /obj/item/reagent_containers/glass/beaker/large
else
continue // shouldn't happen, but just in case
// circuit boards become their resulting machines or computers
if (ispath(item, /obj/item/circuitboard))
var/obj/item/circuitboard/C = item
var/machine = initial(C.build_path)
if (machine)
item = machine
icon_file = initial(item.icon)
icon_state = initial(item.icon_state)
if(!(icon_state in icon_states(icon_file)))
warning("design [D] with icon '[icon_file]' missing state '[icon_state]'")
continue
I = icon(icon_file, icon_state, SOUTH)
// computers (and snowflakes) get their screen and keyboard sprites
if (ispath(item, /obj/machinery/computer) || ispath(item, /obj/machinery/power/solar_control))
var/obj/machinery/computer/C = item
var/screen = initial(C.icon_screen)
var/keyboard = initial(C.icon_keyboard)
var/all_states = icon_states(icon_file)
if (screen && (screen in all_states))
I.Blend(icon(icon_file, screen, SOUTH), ICON_OVERLAY)
if (keyboard && (keyboard in all_states))
I.Blend(icon(icon_file, keyboard, SOUTH), ICON_OVERLAY)
Insert(initial(D.id), I)
return ..()
/datum/asset/spritesheet/vending
name = "vending"
/datum/asset/spritesheet/vending/register()
for(var/k in GLOB.vending_products)
var/atom/item = k
if(!ispath(item, /atom))
continue
var/icon_file = initial(item.icon)
var/icon_state = initial(item.icon_state)
var/icon/I
var/icon_states_list = icon_states(icon_file)
if(icon_state in icon_states_list)
I = icon(icon_file, icon_state, SOUTH)
var/c = initial(item.color)
if(!isnull(c) && c != "#FFFFFF")
I.Blend(c, ICON_MULTIPLY)
else
var/icon_states_string
for(var/an_icon_state in icon_states_list)
if(!icon_states_string)
icon_states_string = "[json_encode(an_icon_state)](\ref[an_icon_state])"
else
icon_states_string += ", [json_encode(an_icon_state)](\ref[an_icon_state])"
stack_trace("[item] does not have a valid icon state, icon=[icon_file], icon_state=[json_encode(icon_state)](\ref[icon_state]), icon_states=[icon_states_string]")
I = icon('icons/turf/floors.dmi', "", SOUTH)
var/imgid = replacetext(replacetext("[item]", "/obj/item/", ""), "/", "-")
Insert(imgid, I)
return ..()
/datum/asset/simple/genetics
assets = list(
"dna_discovered.gif" = 'html/dna_discovered.gif',
"dna_undiscovered.gif" = 'html/dna_undiscovered.gif',
"dna_extra.gif" = 'html/dna_extra.gif'
)
/datum/asset/simple/vv
assets = list(
"view_variables.css" = 'html/admin/view_variables.css'
)
+62 -23
View File
@@ -50,6 +50,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
/// Custom Keybindings
var/list/key_bindings = list()
/// List with a key string associated to a list of keybindings. Unlike key_bindings, this one operates on raw key, allowing for binding a key that triggers regardless of if a modifier is depressed as long as the raw key is sent.
var/list/modless_key_bindings = list()
var/tgui_fancy = TRUE
@@ -154,6 +156,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
"ipc_screen" = "Sunburst",
"ipc_antenna" = "None",
"flavor_text" = "",
"silicon_flavor_text" = "",
"ooc_notes" = "",
"meat_type" = "Mammalian",
"body_model" = MALE,
@@ -367,6 +370,15 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "[features["flavor_text"]]"
else
dat += "[TextPreview(features["flavor_text"])]...<BR>"
dat += "<h2>Silicon Flavor Text</h2>"
dat += "<a href='?_src_=prefs;preference=silicon_flavor_text;task=input'><b>Set Silicon Examine Text</b></a><br>"
if(length(features["silicon_flavor_text"]) <= 40)
if(!length(features["silicon_flavor_text"]))
dat += "\[...\]"
else
dat += "[features["silicon_flavor_text"]]"
else
dat += "[TextPreview(features["silicon_flavor_text"])]...<BR>"
dat += "<h2>OOC notes</h2>"
dat += "<a href='?_src_=prefs;preference=ooc_notes;task=input'><b>Set OOC notes</b></a><br>"
var/ooc_notes_len = length(features["ooc_notes"])
@@ -1088,11 +1100,16 @@ GLOBAL_LIST_EMPTY(preferences_datums)
Input mode is the closest thing to the old input system.<br>\
<b>IMPORTANT:</b> While in input mode's non hotkey setting (tab toggled), Ctrl + KEY will send KEY to the keybind system as the key itself, not as Ctrl + KEY. This means Ctrl + T/W/A/S/D/all your familiar stuff still works, but you \
won't be able to access any regular Ctrl binds.<br>"
dat += "<br><b>Modifier-Independent binding</b> - This is a singular bind that works regardless of if Ctrl/Shift/Alt are held down. For example, if combat mode is bound to C in modifier-independent binds, it'll trigger regardless of if you are \
holding down shift for sprint. <b>Each keybind can only have one independent binding, and each key can only have one keybind independently bound to it.</b>"
// Create an inverted list of keybindings -> key
var/list/user_binds = list()
var/list/user_modless_binds = list()
for (var/key in key_bindings)
for(var/kb_name in key_bindings[key])
user_binds[kb_name] += list(key)
for (var/key in modless_key_bindings)
user_modless_binds[modless_key_bindings[key]] = key
var/list/kb_categories = list()
// Group keybinds by category
@@ -1100,21 +1117,29 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/datum/keybinding/kb = GLOB.keybindings_by_name[name]
kb_categories[kb.category] += list(kb)
dat += "<style>label { display: inline-block; width: 200px; }</style><body>"
dat += {"
<style>
span.bindname { display: inline-block; position: absolute; width: 20% ; left: 5px; padding: 5px; } \
span.bindings { display: inline-block; position: relative; width: auto; left: 20%; width: auto; right: 20%; padding: 5px; } \
span.independent { display: inline-block; position: absolute; width: 20%; right: 5px; padding: 5px; } \
</style><body>
"}
for (var/category in kb_categories)
dat += "<h3>[category]</h3>"
for (var/i in kb_categories[category])
var/datum/keybinding/kb = i
var/current_independent_binding = user_modless_binds[kb.name] || "Unbound"
if(!length(user_binds[kb.name]))
dat += "<label>[kb.full_name]</label> <a href ='?_src_=prefs;preference=keybindings_capture;keybinding=[kb.name];old_key=["Unbound"]'>Unbound</a>"
dat += "<span class='bindname'>[kb.full_name]</span><span class='bindings'><a href ='?_src_=prefs;preference=keybindings_capture;keybinding=[kb.name];old_key=["Unbound"]'>Unbound</a>"
var/list/default_keys = hotkeys ? kb.hotkey_keys : kb.classic_keys
if(LAZYLEN(default_keys))
dat += "| Default: [default_keys.Join(", ")]"
dat += "</span><span class='independent'>Independent Binding: <a href='?_src_=prefs;preference=keybindings_capture;keybinding=[kb.name];old_key=[current_independent_binding];independent=1'>[current_independent_binding]</a></span>"
dat += "<br>"
else
var/bound_key = user_binds[kb.name][1]
dat += "<label>[kb.full_name]</label> <a href ='?_src_=prefs;preference=keybindings_capture;keybinding=[kb.name];old_key=[bound_key]'>[bound_key]</a>"
dat += "<span class='bindname'l>[kb.full_name]</span><span class='bindings'><a href ='?_src_=prefs;preference=keybindings_capture;keybinding=[kb.name];old_key=[bound_key]'>[bound_key]</a>"
for(var/bound_key_index in 2 to length(user_binds[kb.name]))
bound_key = user_binds[kb.name][bound_key_index]
dat += " | <a href ='?_src_=prefs;preference=keybindings_capture;keybinding=[kb.name];old_key=[bound_key]'>[bound_key]</a>"
@@ -1123,6 +1148,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/list/default_keys = hotkeys ? kb.classic_keys : kb.hotkey_keys
if(LAZYLEN(default_keys))
dat += "| Default: [default_keys.Join(", ")]"
dat += "</span><span class='independent'>Independent Binding: <a href='?_src_=prefs;preference=keybindings_capture;keybinding=[kb.name];old_key=[current_independent_binding];independent=1'>[current_independent_binding]</a></span>"
dat += "<br>"
dat += "<br><br>"
@@ -1148,7 +1174,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
#undef APPEARANCE_CATEGORY_COLUMN
#undef MAX_MUTANT_ROWS
/datum/preferences/proc/CaptureKeybinding(mob/user, datum/keybinding/kb, var/old_key)
/datum/preferences/proc/CaptureKeybinding(mob/user, datum/keybinding/kb, old_key, independent = FALSE)
var/HTML = {"
<div id='focus' style="outline: 0;" tabindex=0>Keybinding: [kb.full_name]<br>[kb.description]<br><br><b>Press any key to change<br>Press ESC to clear</b></div>
<script>
@@ -1160,7 +1186,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var shift = e.shiftKey ? 1 : 0;
var numpad = (95 < e.keyCode && e.keyCode < 112) ? 1 : 0;
var escPressed = e.keyCode == 27 ? 1 : 0;
var url = 'byond://?_src_=prefs;preference=keybindings_set;keybinding=[kb.name];old_key=[old_key];clear_key='+escPressed+';key='+e.key+';alt='+alt+';ctrl='+ctrl+';shift='+shift+';numpad='+numpad+';key_code='+e.keyCode;
var url = 'byond://?_src_=prefs;preference=keybindings_set;keybinding=[kb.name];old_key=[old_key];[independent?"independent=1":""];clear_key='+escPressed+';key='+e.key+';alt='+alt+';ctrl='+ctrl+';shift='+shift+';numpad='+numpad+';key_code='+e.keyCode;
window.location=url;
deedDone = true;
}
@@ -1616,6 +1642,11 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(!isnull(msg))
features["flavor_text"] = html_decode(msg)
if("silicon_flavor_text")
var/msg = stripped_multiline_input(usr, "Set the silicon flavor text in your 'examine' verb. This can also be used for OOC notes and preferences!", "Silicon Flavor Text", features["silicon_flavor_text"], MAX_FLAVOR_LEN, TRUE)
if(!isnull(msg))
features["silicon_flavor_text"] = html_decode(msg)
if("ooc_notes")
var/msg = stripped_multiline_input(usr, "Set always-visible OOC notes related to content preferences. THIS IS NOT FOR CHARACTER DESCRIPTIONS!", "OOC notes", features["ooc_notes"], MAX_FLAVOR_LEN, TRUE)
if(!isnull(msg))
@@ -2357,8 +2388,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if("keybindings_capture")
var/datum/keybinding/kb = GLOB.keybindings_by_name[href_list["keybinding"]]
var/old_key = href_list["old_key"]
CaptureKeybinding(user, kb, old_key)
CaptureKeybinding(user, kb, href_list["old_key"], text2num(href_list["independent"]))
return
if("keybindings_set")
@@ -2368,13 +2398,18 @@ GLOBAL_LIST_EMPTY(preferences_datums)
ShowChoices(user)
return
var/independent = href_list["independent"]
var/clear_key = text2num(href_list["clear_key"])
var/old_key = href_list["old_key"]
if(clear_key)
if(key_bindings[old_key])
key_bindings[old_key] -= kb_name
if(!length(key_bindings[old_key]))
key_bindings -= old_key
if(independent)
modless_key_bindings -= old_key
else
if(key_bindings[old_key])
key_bindings[old_key] -= kb_name
if(!length(key_bindings[old_key]))
key_bindings -= old_key
user << browse(null, "window=capturekeypress")
save_preferences()
ShowChoices(user)
@@ -2400,15 +2435,18 @@ GLOBAL_LIST_EMPTY(preferences_datums)
full_key = "[AltMod][CtrlMod][new_key]"
else
full_key = "[AltMod][CtrlMod][ShiftMod][numpad][new_key]"
if(key_bindings[old_key])
key_bindings[old_key] -= kb_name
if(!length(key_bindings[old_key]))
key_bindings -= old_key
key_bindings[full_key] += list(kb_name)
key_bindings[full_key] = sortList(key_bindings[full_key])
user << browse(null, "window=capturekeypress")
if(independent)
modless_key_bindings -= old_key
modless_key_bindings[full_key] = kb_name
else
if(key_bindings[old_key])
key_bindings[old_key] -= kb_name
if(!length(key_bindings[old_key]))
key_bindings -= old_key
key_bindings[full_key] += list(kb_name)
key_bindings[full_key] = sortList(key_bindings[full_key])
user.client.update_movement_keys()
user << browse(null, "window=capturekeypress")
save_preferences()
if("keybindings_reset")
@@ -2418,6 +2456,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
return
hotkeys = (choice == "Hotkey")
key_bindings = (hotkeys) ? deepCopyList(GLOB.hotkey_keybinding_list_by_key) : deepCopyList(GLOB.classic_keybinding_list_by_key)
modless_key_bindings = list()
user.client.update_movement_keys()
if("chat_on_map")
@@ -2572,8 +2611,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
current_tab = text2num(href_list["tab"])
if(href_list["preference"] == "gear")
if(href_list["clear_loadout"])
LAZYCLEARLIST(chosen_gear)
gear_points = initial(gear_points)
chosen_gear = list()
gear_points = CONFIG_GET(number/initial_gear_points)
save_preferences()
if(href_list["select_category"])
for(var/i in GLOB.loadout_items)
@@ -2585,7 +2624,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
return
var/toggle = text2num(href_list["toggle_gear"])
if(!toggle && (G.type in chosen_gear))//toggling off and the item effectively is in chosen gear)
LAZYREMOVE(chosen_gear, G.type)
chosen_gear -= G.type
gear_points += initial(G.cost)
else if(toggle && (!(is_type_in_ref_list(G, chosen_gear))))
if(!is_loadout_slot_available(G.category))
@@ -2595,7 +2634,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
to_chat(user, "<span class='danger'>This is an item intended for donator use only. You are not authorized to use this item.</span>")
return
if(gear_points >= initial(G.cost))
LAZYADD(chosen_gear, G.type)
chosen_gear += G.type
gear_points -= initial(G.cost)
ShowChoices(user)
+14 -1
View File
@@ -262,6 +262,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
// Custom hotkeys
S["key_bindings"] >> key_bindings
S["modless_key_bindings"] >> modless_key_bindings
//citadel code
S["arousable"] >> arousable
@@ -315,7 +316,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
cit_toggles = sanitize_integer(cit_toggles, 0, 16777215, initial(cit_toggles))
auto_ooc = sanitize_integer(auto_ooc, 0, 1, initial(auto_ooc))
no_tetris_storage = sanitize_integer(no_tetris_storage, 0, 1, initial(no_tetris_storage))
key_bindings = sanitize_islist(key_bindings, list())
key_bindings = sanitize_islist(key_bindings, list())
modless_key_bindings = sanitize_islist(modless_key_bindings, list())
verify_keybindings_valid() // one of these days this will runtime and you'll be glad that i put it in a different proc so no one gets their saves wiped
@@ -333,6 +335,11 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
if(!length(binds))
key_bindings -= key
// End
// I hate copypaste but let's do it again but for modless ones
for(var/key in modless_key_bindings)
var/bindname = modless_key_bindings[key]
if(!GLOB.keybindings_by_name[bindname])
modless_key_bindings -= key
/datum/preferences/proc/save_preferences()
if(!path)
@@ -387,6 +394,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
WRITE_FILE(S["pda_color"], pda_color)
WRITE_FILE(S["pda_skin"], pda_skin)
WRITE_FILE(S["key_bindings"], key_bindings)
WRITE_FILE(S["modless_key_bindings"], modless_key_bindings)
//citadel code
WRITE_FILE(S["screenshake"], screenshake)
@@ -553,7 +561,11 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
else //We have no old flavortext, default to new
S["feature_flavor_text"] >> features["flavor_text"]
S["silicon_feature_flavor_text"] >> features["silicon_flavor_text"]
S["feature_ooc_notes"] >> features["ooc_notes"]
S["silicon_flavor_text"] >> features["silicon_flavor_text"]
S["vore_flags"] >> vore_flags
S["vore_taste"] >> vore_taste
@@ -678,6 +690,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
features["flavor_text"] = copytext(features["flavor_text"], 1, MAX_FLAVOR_LEN)
features["silicon_flavor_text"] = copytext(features["silicon_flavor_text"], 1, MAX_FLAVOR_LEN)
features["ooc_notes"] = copytext(features["ooc_notes"], 1, MAX_FLAVOR_LEN)
joblessrole = sanitize_integer(joblessrole, 1, 3, initial(joblessrole))
-6
View File
@@ -1,6 +0,0 @@
/obj/item/clothing/head/hunter
name = "hunter"
desc = "A basic hat for hunting things."
icon = 'modular_citadel/icons/obj/clothing/cit_hats.dmi'
icon_state = "hunter"
item_state = "hunter_worn"
+1 -6
View File
@@ -375,12 +375,6 @@
icon_state = "telegram"
dog_fashion = /datum/dog_fashion/head/telegram
/obj/item/clothing/head/colour
name = "Singer cap"
desc = "A light white hat that has bands of color. Just makes you want to sing and dance!"
icon_state = "colour"
dog_fashion = /datum/dog_fashion/head/colour
/obj/item/clothing/head/christmashat
name = "red santa hat"
desc = "A red Christmas Hat! How festive!"
@@ -434,3 +428,4 @@
desc = "A symbol of discipline, honor, and lots and lots of removal of some type of skewered food."
icon_state = "russobluecamohat"
item_state = "russobluecamohat"
dynamic_hair_suffix = ""
@@ -114,6 +114,7 @@
desc = "A jack o' lantern! Believed to ward off evil spirits."
icon_state = "hardhat0_pumpkin"
item_state = "hardhat0_pumpkin"
hat_type = "pumpkin"
flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR|HIDESNOUT
armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0)
brightness_on = 2 //luminosity when on
@@ -150,6 +151,7 @@
desc = "Some fake antlers and a very fake red nose."
icon_state = "hardhat0_reindeer"
item_state = "hardhat0_reindeer"
hat_type = "reindeer"
flags_inv = 0
armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0)
brightness_on = 1 //luminosity when on
+1 -1
View File
@@ -134,7 +134,7 @@
name = "baseball cap"
desc = "It's a robust baseball hat, this one belongs to syndicate major league team."
icon_state = "baseballsoft"
soft_type = "baseballsoft"
soft_type = "baseball"
item_state = "baseballsoft"
flags_inv = HIDEEYES|HIDEFACE
armor = list("melee" = 35, "bullet" = 35, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 90)
+42
View File
@@ -97,6 +97,48 @@
icon_state = "trek_ds9_medsci"
item_state = "b_suit"
//Orvilike (Orville-inspired clothing with TOS-like color code)
/obj/item/clothing/under/trek/command/orv
desc = "An uniform worn by command officers since 2420s."
icon_state = "orv_com"
/obj/item/clothing/under/trek/engsec/orv
desc = "An uniform worn by operations officers since 2420s."
icon_state = "orv_ops"
/obj/item/clothing/under/trek/medsci/orv
desc = "An uniform worn by medsci officers since 2420s."
icon_state = "orv_medsci"
//Orvilike Extra (Ditto, but expands it for Civilian department with SS13 colors and gives specified command uniform)
//honestly no idea why i added specified comm. uniforms but w/e
/obj/item/clothing/under/trek/command/orv/captain
name = "captain uniform"
desc = "An uniform worn by captains since 2550s."
icon_state = "orv_com_capt"
/obj/item/clothing/under/trek/command/orv/engsec
name = "operations command uniform"
desc = "An uniform worn by operations command officers since 2550s."
icon_state = "orv_com_ops"
/obj/item/clothing/under/trek/command/orv/medsci
name = "medsci command uniform"
desc = "An uniform worn by medsci command officers since 2550s."
icon_state = "orv_com_medsci"
/obj/item/clothing/under/trek/orv
name = "adjutant uniform"
desc = "An uniform worn by adjutants <i>(assistants)</i> since 2550s."
icon_state = "orv_ass"
item_state = "gy_suit"
/obj/item/clothing/under/trek/orv/service
name = "service uniform"
desc = "An uniform worn by service officers since 2550s."
icon_state = "orv_srv"
item_state = "g_suit"
//The Motion Picture
/obj/item/clothing/under/trek/fedutil
name = "federation utility uniform"
+1
View File
@@ -383,6 +383,7 @@
/datum/spacevine_controller/New(turf/location, list/muts, potency, production, datum/round_event/event = null)
vines = list()
growth_queue = list()
spawn_spacevine_piece(location, null, muts)
START_PROCESSING(SSobj, src)
vine_mutations_list = list()
init_subtypes(/datum/spacevine_mutation/, vine_mutations_list)
+9
View File
@@ -412,6 +412,15 @@
begin_month = JUNE
begin_weekday = SUNDAY
/datum/holiday/pride
name = PRIDE_MONTH
begin_day = 1
begin_month = JUNE
end_day = 30
/datum/holiday/pride/getStationPrefix()
return pick("Pride", "Gay", "Bi", "Trans", "Lesbian", "Ace", "Aro", "Agender", pick("Enby", "Enbie"), "Pan", "Intersex", "Demi", "Poly", "Closeted", "Genderfluid")
/datum/holiday/moth
name = "Moth Week"
-3
View File
@@ -40,9 +40,6 @@
return ..()
return ..()
/obj/item/holo/esword/attack(target as mob, mob/user as mob)
..()
/obj/item/holo/esword/Initialize()
. = ..()
saber_color = pick("red","blue","green","purple")
+8 -3
View File
@@ -205,6 +205,11 @@
popup.set_content(dat)
popup.open()
/obj/machinery/biogenerator/AltClick(mob/living/user)
. = ..()
if(istype(user) && user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
detach(user)
/obj/machinery/biogenerator/proc/activate()
if (usr.stat != CONSCIOUS)
return
@@ -293,9 +298,9 @@
update_icon()
return .
/obj/machinery/biogenerator/proc/detach()
/obj/machinery/biogenerator/proc/detach(mob/living/user)
if(beaker)
beaker.forceMove(drop_location())
user.put_in_hands(beaker)
beaker = null
update_icon()
@@ -310,7 +315,7 @@
updateUsrDialog()
else if(href_list["detach"])
detach()
detach(usr)
updateUsrDialog()
else if(href_list["create"])
+1 -1
View File
@@ -69,7 +69,7 @@
/obj/item/instrument/dropped(mob/user)
. = ..()
if((loc != user) && (user.machine == src))
user.set_machine(null)
user.unset_machine()
/obj/item/instrument/interact(mob/user)
ui_interact(user)
@@ -24,6 +24,7 @@
/obj/structure/musician/ui_interact(mob/user)
. = ..()
user.set_machine(src)
song.ui_interact(user)
/obj/structure/musician/wrench_act(mob/living/user, obj/item/I)

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