(+kyix5!@`^4CoIZnNAWm!8A
z7)ZuPq0tVfxKB7g@4Rf*8Dfd%7^pj4i4p<
zOtLZ@W2NOv(!*J8Hta->V
z!=lmi3z%9pJ&sID=ab5wNYAM{y9%W0g(oRfyq?d2Lm_UAiJi*#o
z|FXW|br=MKoMPy=gzRh-GBW(9nvhviYYkH#MYyIxeIPv0e=t+q2GJim)l8^(h3gy2
z)P}3jI^?bs)0CmjgE1XIh*yy9U6C#Lw8Zwy;X7i(p!nnE#@8K+k=18?nGqsPwN4Ac*S
zUz%VLNX9WIbb$9j_!B2yBS#3R^|XjLFNFuR^C?{9lky8Ob&!=4uMBu~+~r}zbmYpz
zW^}0z4R`rFGv(WrvYAb(lqWgf0DVEy25-qR*qMOSEoW63wD;-4qli!8-D&{k;l1nk
zbo#{y2Tk@O{51U%PhwVmsFc1HBD(I>#BSg}Y{cL!vMb)PGpQ+KjOcYtNyd>Zf{J_6
zt)!jiuzSINsePXT;x}0uKxBlCREc6lm#uvaB_8*QUEnN7N=FgHf
zAP}9fdAP?iy-6VvN_!J&k2@Z=g%!d;4!)Ai?hU{-rm49e0Nv7YWL}+V*ZuM3R`g7=
zqU}fKhw_h$6~AYkJ&DeOszAL|_+(p&O02CFW}ty|7aIBmEVR5dJCK~*Ra)mBKS@mf
z{7@<6S@qGEYs7s2f>Pf>;UwF@Da}Oc!SVe{K}z1=Y_M_dnu}fRZrLoqgmkHipxN&q
zC5?Pgto37R_^8CkcF|K+I!2V}hCb?#yk%iu`>X@xr8^UYsVKXlM?5PMP(DIqI
zhOxp=Aazc*v*091t$q3%-IaPu5vR?C559llg0H@&j>{-Mt2tcS=f3jIF&2KaNp)0v
znQDmzI`I4{0M>IrZrZ)Kp}d`7Ibzan%g!`!r<|%)IyiH6#ekdzz8JVXuw7TK<&lxL
zGji37@OR1K^eC9;JHb~gyak%w{qJZmg2INp16lIOBRaK`hTD-9DUbVB-~@~^QLv2ODm%8-
z1Fw8?8(HTocr9f5x5nqc{0Y3tQvKETFWqlpAm8smfA?5Rrjy!wQ;3!Gx6vw})=mp%
z_b&DfjBP|KGNzyQsWfG-^z!3XS%$M*gknpnX0BiR*+*tT7+2W_Pr6I5)Kg~ljq=;roU!fmyTLcG~SXGRs
z3&tYxga0DzI(I~he{i~DnLy;9n}2nct-Qng>ulG%sZ;L;YO764
zpR692dEqu^AN9mv%+Ez_dp4jeTw0FyT^`&O)h?%I(`ft@EfoyiuOR2U(LwkF&MUwf
z@aUT);p~P}7BM>WA>sgAR#SB*
zggK~hO3i)j<9_|035$K)m@;n)5p~+aD60(nvdQe9^&hp=(}q8#muiv_N-;2rH`v?u;I-V&8Y7>>}SUI9|hub
zja$iK`)j1qP^XWDua+&ZR^Dq;y*bi>Ay}J?6PHgW5AsCr&Ln;k_%$+*JP{g3RLLqA
zj>Y`ByRG^S6#rygI|Ka^FR09`$
ZtIui%^a{qBbN1qA{C;W@b(1NVh>dE>`7
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
From 13ae2ee6e0f91f0c85d1c0a21c9c2d7f1e6463f3 Mon Sep 17 00:00:00 2001
From: dwasint <82520990+dwasint@users.noreply.github.com>
Date: Sun, 27 Aug 2023 00:04:02 -0400
Subject: [PATCH 06/41] Update armor.dm
---
code/modules/clothing/suits/armor.dm | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm
index 826ee05baceb..2fd8727e91a7 100644
--- a/code/modules/clothing/suits/armor.dm
+++ b/code/modules/clothing/suits/armor.dm
@@ -139,7 +139,7 @@
/obj/item/clothing/suit/armor/hos/trenchcoat/winter
name = "head of security's winter trenchcoat"
desc = "A trenchcoat enhanced with a special lightweight kevlar, padded with wool on the collar and inside. You feel strangely lonely wearing this coat."
- icon_state = "hoswinter"
+ icon_state = "dripcoat" // monkestation edit
min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT
/obj/item/clothing/suit/armor/hos/hos_formal
From 6fd8aee96570826f704ab3597174e58dfd7a4a04 Mon Sep 17 00:00:00 2001
From: dwasint <82520990+dwasint@users.noreply.github.com>
Date: Sun, 27 Aug 2023 12:51:43 -0400
Subject: [PATCH 07/41] Update armor.dm
---
code/modules/clothing/suits/armor.dm | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm
index 2fd8727e91a7..0dd8010e5f07 100644
--- a/code/modules/clothing/suits/armor.dm
+++ b/code/modules/clothing/suits/armor.dm
@@ -139,7 +139,7 @@
/obj/item/clothing/suit/armor/hos/trenchcoat/winter
name = "head of security's winter trenchcoat"
desc = "A trenchcoat enhanced with a special lightweight kevlar, padded with wool on the collar and inside. You feel strangely lonely wearing this coat."
- icon_state = "dripcoat" // monkestation edit
+ icon_state = "pimpcoat" // monkestation edit
min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT
/obj/item/clothing/suit/armor/hos/hos_formal
From 2268833ce355290d601cc7aac44fd514519c731c Mon Sep 17 00:00:00 2001
From: dwasint <82520990+dwasint@users.noreply.github.com>
Date: Sun, 27 Aug 2023 13:45:31 -0400
Subject: [PATCH 08/41] Create screenshot_humanoids__datum_species_goblin.png
---
...creenshot_humanoids__datum_species_goblin.png | Bin 0 -> 1016 bytes
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_goblin.png
diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_goblin.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_goblin.png
new file mode 100644
index 0000000000000000000000000000000000000000..22204deaabb0a385dfd88d9f4508b632b70f6196
GIT binary patch
literal 1016
zcmV-T;)pwg3PC0Kou>iHQKu0I;yI0Ga?bI6;n(lNuT!R$61Tv$J$}
zfOmU?QBPYD6ci#RCKwzXr>eHp*WZbYq9i0LEiE(4&C%}e?j0T^rKz@bae@Hs006)M
zvg&h100001bW%=J06^y0W&i*HWqMRtbVOxyV{&P5bZKvH004NLQ&wYE62mqS@7!En1VMG7`0?A24K~z|U?Uw6u8Ziuot#urdWqqxI
zq#MHR{ok=iUXpfV%XIkF$y6C&$tN9ah1Zf&-_3~Vem;QfK~uRB_%IA2R}TXjt+k=y
zLF5n+xl)g$SHnuvlfa)yBSTG((mgzn8Y}2gx=k82+xRT-wVFnaze!qvxn{nW72qJp
zLqQuY$1CgHIF8O)fqQ4!oR{K*jme~l&vtyUKiF(&D_Ey_fs8#nR^0I2ml|4Mb46yHa&j&^m+~~4
zug?RSiMWE6>qt|@vCT)?Yj-LA#qL5eeooPi>dJBhP#5nXKV
zlE32oTPLj2*d-qVg?9zAYHgBF0c4*Wd*B}uxZ!Y9Aj6io`}jP+z0Hc>=DYa*{e3?l
z)M4g}CO^!D&xa;g$Nm0zJgL)>FYe&q#^}a?aD$&oF9U!W&&`+4BEJl5a(A3`ak$6}
zoDr^9VhXgvfH0ip6sHRd41jU}2aAN0#
zhqbf71e`Js_hvjgH%{%&nYpxR@6qHN3W!JFj;^P`@i>;fZ787cdYoo1vxp?X!HZL<
zrW9^lU^!ulj)3zibEAiQok3~)?p6kFnG~uFY!PsrKvw`MxFYbi2;BVnymYq&9D9X&
zlH=x|c}!&|X)6~7r_3|LSBkP^3<<=&PuOb{
Date: Sun, 27 Aug 2023 18:21:51 -0400
Subject: [PATCH 09/41] replaces the starlight system with our weathers
daylight system
---
code/game/area/areas/misc.dm | 6 +-
code/game/area/areas/station.dm | 2 +
code/game/turfs/open/space/space.dm | 5 +-
.../outdoors/code/_onclick/hud/fullscreen.dm | 11 ++-
.../controllers/subsystem/outdoors_effects.dm | 70 +++++++++++--------
.../particle_weathers/_particle_weather.dm | 6 +-
6 files changed, 63 insertions(+), 37 deletions(-)
diff --git a/code/game/area/areas/misc.dm b/code/game/area/areas/misc.dm
index 53f30c563afd..f5a9643b4a03 100644
--- a/code/game/area/areas/misc.dm
+++ b/code/game/area/areas/misc.dm
@@ -6,8 +6,8 @@
always_unpowered = TRUE
static_lighting = FALSE
- base_lighting_alpha = 255
- base_lighting_color = "#FFFFFF"
+ //base_lighting_alpha = 255
+ //base_lighting_color = "#FFFFFF"
power_light = FALSE
power_equip = FALSE
@@ -19,9 +19,11 @@
sound_environment = SOUND_AREA_SPACE
ambient_buzz = null //Space is deafeningly quiet
+/*
/area/space/Initialize(mapload)
. = ..()
set_base_lighting(GLOB.starlight_color, alpha)
+*/
/area/space/nearstation
icon_state = "space_near"
diff --git a/code/game/area/areas/station.dm b/code/game/area/areas/station.dm
index 4d00873a9280..e00e3ce8f731 100644
--- a/code/game/area/areas/station.dm
+++ b/code/game/area/areas/station.dm
@@ -894,9 +894,11 @@
airlock_wires = /datum/wires/airlock/engineering
sound_environment = SOUND_AREA_SPACE
+/*
/area/station/solars/Initialize(mapload)
. = ..()
set_base_lighting(new_base_lighting_color = GLOB.starlight_color, new_alpha = 255)
+*/
/area/station/solars/fore
name = "\improper Fore Solar Array"
diff --git a/code/game/turfs/open/space/space.dm b/code/game/turfs/open/space/space.dm
index ab783a749c84..3819c6f8fa98 100644
--- a/code/game/turfs/open/space/space.dm
+++ b/code/game/turfs/open/space/space.dm
@@ -25,7 +25,7 @@ GLOBAL_VAR_INIT(starlight_color, pick(COLOR_TEAL, COLOR_GREEN, COLOR_CYAN, COLOR
light_inner_range = 0.1
light_outer_range = 4
light_falloff_curve = 5
- space_lit = TRUE
+ //space_lit = TRUE
bullet_bounce_sound = null
vis_flags = VIS_INHERIT_ID //when this be added to vis_contents of something it be associated with something on clicking, important for visualisation of turf in openspace and interraction with openspace that show you turf.
@@ -120,7 +120,8 @@ GLOBAL_VAR_INIT(starlight_color, pick(COLOR_TEAL, COLOR_GREEN, COLOR_CYAN, COLOR
/// Turns on the stars, if they aren't already
/turf/open/space/proc/enable_starlight()
- set_light(l_color = GLOB.starlight_color, l_on = TRUE)
+ //set_light(l_color = GLOB.starlight_color, l_on = TRUE)
+ return
/turf/open/space/attack_paw(mob/user, list/modifiers)
return attack_hand(user, modifiers)
diff --git a/monkestation/code/modules/outdoors/code/_onclick/hud/fullscreen.dm b/monkestation/code/modules/outdoors/code/_onclick/hud/fullscreen.dm
index 0425b18b6565..b2e1f11bd672 100644
--- a/monkestation/code/modules/outdoors/code/_onclick/hud/fullscreen.dm
+++ b/monkestation/code/modules/outdoors/code/_onclick/hud/fullscreen.dm
@@ -15,7 +15,16 @@
. = ..()
SSoutdoor_effects.sunlighting_planes |= src
color = SSoutdoor_effects.last_color
- SSoutdoor_effects.transition_sunlight_color(src)
+
+ var/daylight = FALSE
+ for (var/z in SSmapping.levels_by_trait(ZTRAIT_STATION))
+ if(SSmapping.level_trait(z, ZTRAIT_DAYCYCLE))
+ daylight = TRUE
+ continue
+ if(!daylight)
+ SSoutdoor_effects.transition_sunlight_color(src, 1)
+ else
+ SSoutdoor_effects.transition_sunlight_color(src)
/atom/movable/screen/fullscreen/lighting_backdrop/sunlight/Destroy()
. = ..()
diff --git a/monkestation/code/modules/outdoors/code/controllers/subsystem/outdoors_effects.dm b/monkestation/code/modules/outdoors/code/controllers/subsystem/outdoors_effects.dm
index 02f81daa81dc..bbbfc91a44d0 100644
--- a/monkestation/code/modules/outdoors/code/controllers/subsystem/outdoors_effects.dm
+++ b/monkestation/code/modules/outdoors/code/controllers/subsystem/outdoors_effects.dm
@@ -40,7 +40,6 @@
color = "#000032"
start = 20 HOURS //8:00:00 PM
-
GLOBAL_VAR_INIT(GLOBAL_LIGHT_RANGE, 5)
GLOBAL_LIST_EMPTY(SUNLIGHT_QUEUE_WORK) /* turfs to be stateChecked */
GLOBAL_LIST_EMPTY(SUNLIGHT_QUEUE_UPDATE) /* turfs to have their colors updated via corners (filter out the unroofed dudes) */
@@ -86,6 +85,17 @@ SUBSYSTEM_DEF(outdoor_effects)
InitializeTurfs()
initialized = TRUE
fire(FALSE, TRUE)
+
+ var/daylight = FALSE
+ for (var/z in SSmapping.levels_by_trait(ZTRAIT_STATION))
+ if(SSmapping.level_trait(z, ZTRAIT_DAYCYCLE))
+ daylight = TRUE
+ continue
+
+ if(!daylight)
+ for(var/datum/time_of_day/listed_time as anything in time_cycle_steps)
+ listed_time.color = GLOB.starlight_color
+
return SS_INIT_SUCCESS
/datum/controller/subsystem/outdoor_effects/proc/InitializeTurfs(list/targets)
@@ -226,11 +236,16 @@ SUBSYSTEM_DEF(outdoor_effects)
transition_sunlight_color(SP)
//Transition from our last color to our current color (i.e if it is going from daylight (white) to sunset (red), we transition to red in the first hour of sunset)
-/datum/controller/subsystem/outdoor_effects/proc/transition_sunlight_color(atom/movable/screen/fullscreen/lighting_backdrop/sunlight/SP)
+/datum/controller/subsystem/outdoor_effects/proc/transition_sunlight_color(atom/movable/screen/fullscreen/lighting_backdrop/sunlight/SP, time_given)
/* transistion in an hour or time diff from now to our next step, whichever is smaller */
+ if(!next_step_datum)
+ get_time_of_day()
+
if(!weather_light_affecting_event)
var/time = station_time()
- var/time_to_animate = min((1 HOURS / SSticker.station_time_rate_multiplier), daytimeDiff(time, next_step_datum.start))
+ var/time_to_animate = time_given
+ if(!time_given)
+ time_to_animate = min((1 HOURS / SSticker.station_time_rate_multiplier), daytimeDiff(time, next_step_datum.start))
var/blend_amount = (time - current_step_datum.start) / (next_step_datum.start - current_step_datum.start)
current_color = BlendRGB(current_step_datum.color, next_step_datum.color, blend_amount)
@@ -239,32 +254,29 @@ SUBSYSTEM_DEF(outdoor_effects)
// Updates overlays and vis_contents for outdoor effects
/datum/controller/subsystem/outdoor_effects/proc/update_outdoor_effect_overlays(atom/movable/outdoor_effect/OE)
var/turf/source = get_turf(OE)
- if(!SSmapping.level_trait(OE.z, ZTRAIT_DAYCYCLE))
- OE.overlays = OE.weatherproof ? list() : list(get_weather_overlay())
- else
- var/mutable_appearance/MA
- if (OE.state != SKY_BLOCKED)
- MA = get_sunlight_overlay(1,1,1,1, GET_TURF_PLANE_OFFSET(source)) /* fully lit */
- else //Indoor - do proper corner checks
- /* check if we are globally affected or not */
- var/static/datum/lighting_corner/dummy/dummy_lighting_corner = new
-
- var/datum/lighting_corner/cr = OE.source_turf.lighting_corner_SW || dummy_lighting_corner
- var/datum/lighting_corner/cg = OE.source_turf.lighting_corner_SE || dummy_lighting_corner
- var/datum/lighting_corner/cb = OE.source_turf.lighting_corner_NW || dummy_lighting_corner
- var/datum/lighting_corner/ca = OE.source_turf.lighting_corner_NE || dummy_lighting_corner
-
- var/fr = cr.sun_falloff
- var/fg = cg.sun_falloff
- var/fb = cb.sun_falloff
- var/fa = ca.sun_falloff
-
- MA = get_sunlight_overlay(fr, fg, fb, fa, GET_TURF_PLANE_OFFSET(source))
-
- OE.sunlight_overlay = MA
- //Get weather overlay if not weatherproof
- OE.overlays = OE.weatherproof ? list(OE.sunlight_overlay) : list(OE.sunlight_overlay, get_weather_overlay())
- OE.luminosity = MA.luminosity
+ var/mutable_appearance/MA
+ if (OE.state != SKY_BLOCKED)
+ MA = get_sunlight_overlay(1,1,1,1, GET_TURF_PLANE_OFFSET(source)) /* fully lit */
+ else //Indoor - do proper corner checks
+ /* check if we are globally affected or not */
+ var/static/datum/lighting_corner/dummy/dummy_lighting_corner = new
+
+ var/datum/lighting_corner/cr = OE.source_turf.lighting_corner_SW || dummy_lighting_corner
+ var/datum/lighting_corner/cg = OE.source_turf.lighting_corner_SE || dummy_lighting_corner
+ var/datum/lighting_corner/cb = OE.source_turf.lighting_corner_NW || dummy_lighting_corner
+ var/datum/lighting_corner/ca = OE.source_turf.lighting_corner_NE || dummy_lighting_corner
+
+ var/fr = cr.sun_falloff
+ var/fg = cg.sun_falloff
+ var/fb = cb.sun_falloff
+ var/fa = ca.sun_falloff
+
+ MA = get_sunlight_overlay(fr, fg, fb, fa, GET_TURF_PLANE_OFFSET(source))
+
+ OE.sunlight_overlay = MA
+ //Get weather overlay if not weatherproof
+ OE.overlays = OE.weatherproof ? list(OE.sunlight_overlay) : list(OE.sunlight_overlay, get_weather_overlay())
+ OE.luminosity = MA.luminosity
//Retrieve an overlay from the list - create if necessary
/datum/controller/subsystem/outdoor_effects/proc/get_sunlight_overlay(fr, fg, fb, fa, offset)
diff --git a/monkestation/code/modules/outdoors/code/datum/particle_weathers/_particle_weather.dm b/monkestation/code/modules/outdoors/code/datum/particle_weathers/_particle_weather.dm
index 1c7ef8778c7f..c621613858d6 100644
--- a/monkestation/code/modules/outdoors/code/datum/particle_weathers/_particle_weather.dm
+++ b/monkestation/code/modules/outdoors/code/datum/particle_weathers/_particle_weather.dm
@@ -214,7 +214,7 @@ GLOBAL_LIST_EMPTY(siren_objects)
COOLDOWN_START(src, time_left, weather_duration)
weather_start_time = world.time
running = TRUE
- addtimer(CALLBACK(src, .proc/wind_down), weather_duration)
+ addtimer(CALLBACK(src, PROC_REF(wind_down)), weather_duration)
weather_warnings()
if(particle_effect_type)
SSparticle_weather.set_particle_effect(new particle_effect_type);
@@ -247,7 +247,7 @@ GLOBAL_LIST_EMPTY(siren_objects)
messaged_mobs = list()
if(severity_steps_taken < severity_steps && as_step)
- addtimer(CALLBACK(src, .proc/change_severity), weather_duration / severity_steps)
+ addtimer(CALLBACK(src, PROC_REF(change_severity)), weather_duration / severity_steps)
/datum/particle_weather/proc/wind_down()
severity = 0
@@ -255,7 +255,7 @@ GLOBAL_LIST_EMPTY(siren_objects)
SSparticle_weather.particle_effect.animate_severity(severity_mod())
//Wait for the last particle to fade, then qdel yourself
- addtimer(CALLBACK(src, .proc/end), SSparticle_weather.particle_effect.lifespan + SSparticle_weather.particle_effect.fade)
+ addtimer(CALLBACK(src, PROC_REF(end)), SSparticle_weather.particle_effect.lifespan + SSparticle_weather.particle_effect.fade)
/datum/particle_weather/proc/end()
running = FALSE
From eeac9e9c0b8ba85a68ab527e509945283794b474 Mon Sep 17 00:00:00 2001
From: dwasint <82520990+dwasint@users.noreply.github.com>
Date: Sun, 27 Aug 2023 18:52:04 -0400
Subject: [PATCH 10/41] uses both systems to reduce the costs of sunlighting on
init
---
code/__DEFINES/maps.dm | 3 ++-
code/game/area/areas/misc.dm | 7 ++++---
code/game/area/areas/station.dm | 6 +++---
code/game/turfs/open/space/space.dm | 7 +++++--
.../code/controllers/subsystem/outdoors_effects.dm | 6 +++---
.../code/modules/outdoors/code/sunlight/sunlight_object.dm | 2 +-
6 files changed, 18 insertions(+), 13 deletions(-)
diff --git a/code/__DEFINES/maps.dm b/code/__DEFINES/maps.dm
index b09da50cfd4b..c74d0b739fc2 100644
--- a/code/__DEFINES/maps.dm
+++ b/code/__DEFINES/maps.dm
@@ -127,7 +127,7 @@ Always compile, always use that verb, and always make sure that it works for wha
///Z level traits for CentCom
#define ZTRAITS_CENTCOM list(ZTRAIT_CENTCOM = TRUE, ZTRAIT_NOPHASE = TRUE)
///Z level traits for Space Station 13
-#define ZTRAITS_STATION list(ZTRAIT_LINKAGE = CROSSLINKED, ZTRAIT_STATION = TRUE)
+#define ZTRAITS_STATION list(ZTRAIT_LINKAGE = CROSSLINKED, ZTRAIT_STATION = TRUE, ZTRAIT_STARLIGHT = TRUE)
///Z level traits for Deep Space
#define ZTRAITS_SPACE list(ZTRAIT_LINKAGE = CROSSLINKED, ZTRAIT_SPACE_RUINS = TRUE)
///Z level traits for Lavaland
@@ -234,6 +234,7 @@ Always compile, always use that verb, and always make sure that it works for wha
// boolean - particle weather types that occur on the level
#define WEATHER_MESSAGE_DELAY 30 SECONDS
#define ZTRAIT_DAYCYCLE "Daycycle"
+#define ZTRAIT_STARLIGHT "Starlight"
#define PARTICLEWEATHER_RAIN "weather_rain"
#define PARTICLEWEATHER_SNOW "weather_snow"
#define PARTICLEWEATHER_DUST "weather_dust"
diff --git a/code/game/area/areas/misc.dm b/code/game/area/areas/misc.dm
index f5a9643b4a03..3e4b32b3be76 100644
--- a/code/game/area/areas/misc.dm
+++ b/code/game/area/areas/misc.dm
@@ -19,11 +19,12 @@
sound_environment = SOUND_AREA_SPACE
ambient_buzz = null //Space is deafeningly quiet
-/*
+
/area/space/Initialize(mapload)
. = ..()
- set_base_lighting(GLOB.starlight_color, alpha)
-*/
+ if(!SSmapping.level_trait(src.z, ZTRAIT_STARLIGHT))
+ set_base_lighting(GLOB.starlight_color, alpha)
+
/area/space/nearstation
icon_state = "space_near"
diff --git a/code/game/area/areas/station.dm b/code/game/area/areas/station.dm
index e00e3ce8f731..f5ec05394281 100644
--- a/code/game/area/areas/station.dm
+++ b/code/game/area/areas/station.dm
@@ -893,12 +893,12 @@
ambience_index = AMBIENCE_ENGI
airlock_wires = /datum/wires/airlock/engineering
sound_environment = SOUND_AREA_SPACE
+ outdoors = TRUE
-/*
/area/station/solars/Initialize(mapload)
. = ..()
- set_base_lighting(new_base_lighting_color = GLOB.starlight_color, new_alpha = 255)
-*/
+ if(!SSmapping.level_trait(src.z, ZTRAIT_STARLIGHT))
+ set_base_lighting(new_base_lighting_color = GLOB.starlight_color, new_alpha = 255)
/area/station/solars/fore
name = "\improper Fore Solar Array"
diff --git a/code/game/turfs/open/space/space.dm b/code/game/turfs/open/space/space.dm
index 3819c6f8fa98..3c8e3060c1fd 100644
--- a/code/game/turfs/open/space/space.dm
+++ b/code/game/turfs/open/space/space.dm
@@ -62,6 +62,9 @@ GLOBAL_VAR_INIT(starlight_color, pick(COLOR_TEAL, COLOR_GREEN, COLOR_CYAN, COLOR
if(SSmapping.max_plane_offset)
plane = PLANE_SPACE - (PLANE_RANGE * SSmapping.z_level_to_plane_offset[z])
+ if(!SSmapping.level_trait(src.z, ZTRAIT_STARLIGHT))
+ space_lit = TRUE
+
var/area/our_area = loc
if(!our_area.area_has_base_lighting && space_lit) //Only provide your own lighting if the area doesn't for you
// Intentionally not add_overlay for performance reasons.
@@ -120,8 +123,8 @@ GLOBAL_VAR_INIT(starlight_color, pick(COLOR_TEAL, COLOR_GREEN, COLOR_CYAN, COLOR
/// Turns on the stars, if they aren't already
/turf/open/space/proc/enable_starlight()
- //set_light(l_color = GLOB.starlight_color, l_on = TRUE)
- return
+ if(space_lit)
+ set_light(l_color = GLOB.starlight_color, l_on = TRUE)
/turf/open/space/attack_paw(mob/user, list/modifiers)
return attack_hand(user, modifiers)
diff --git a/monkestation/code/modules/outdoors/code/controllers/subsystem/outdoors_effects.dm b/monkestation/code/modules/outdoors/code/controllers/subsystem/outdoors_effects.dm
index bbbfc91a44d0..f7c905d8a383 100644
--- a/monkestation/code/modules/outdoors/code/controllers/subsystem/outdoors_effects.dm
+++ b/monkestation/code/modules/outdoors/code/controllers/subsystem/outdoors_effects.dm
@@ -73,7 +73,7 @@ SUBSYSTEM_DEF(outdoor_effects)
return ..()
/datum/controller/subsystem/outdoor_effects/proc/fullPlonk()
- for (var/z in SSmapping.levels_by_trait(ZTRAIT_STATION))
+ for (var/z in (SSmapping.levels_by_trait(ZTRAIT_DAYCYCLE) + SSmapping.levels_by_trait(ZTRAIT_STARLIGHT)))
for (var/turf/T in block(locate(1,1,z), locate(world.maxx,world.maxy,z)))
var/area/TArea = T.loc
if (TArea.static_lighting)
@@ -87,7 +87,7 @@ SUBSYSTEM_DEF(outdoor_effects)
fire(FALSE, TRUE)
var/daylight = FALSE
- for (var/z in SSmapping.levels_by_trait(ZTRAIT_STATION))
+ for (var/z in (SSmapping.levels_by_trait(ZTRAIT_DAYCYCLE) + SSmapping.levels_by_trait(ZTRAIT_STARLIGHT)))
if(SSmapping.level_trait(z, ZTRAIT_DAYCYCLE))
daylight = TRUE
continue
@@ -99,7 +99,7 @@ SUBSYSTEM_DEF(outdoor_effects)
return SS_INIT_SUCCESS
/datum/controller/subsystem/outdoor_effects/proc/InitializeTurfs(list/targets)
- for (var/z in SSmapping.levels_by_trait(ZTRAIT_STATION))
+ for (var/z in (SSmapping.levels_by_trait(ZTRAIT_DAYCYCLE) + SSmapping.levels_by_trait(ZTRAIT_STARLIGHT)))
for (var/turf/T in block(locate(1,1,z), locate(world.maxx,world.maxy,z)))
var/area/TArea = T.loc
if (TArea.static_lighting || istype(TArea, /area/space))
diff --git a/monkestation/code/modules/outdoors/code/sunlight/sunlight_object.dm b/monkestation/code/modules/outdoors/code/sunlight/sunlight_object.dm
index 4420c7448f99..6e72ff093bcd 100644
--- a/monkestation/code/modules/outdoors/code/sunlight/sunlight_object.dm
+++ b/monkestation/code/modules/outdoors/code/sunlight/sunlight_object.dm
@@ -223,7 +223,7 @@ Sunlight System
.["WEATHERPROOF"] |= ceilingStat["WEATHERPROOF"]
var/area/turf_area = get_area(src)
- if((!isspaceturf(src) && !istype(src, /turf/open/floor/plating/ocean) && !above() && !SSmapping.level_trait(src.z, ZTRAIT_UP) && !turf_area.outdoors && !turf_area.false_outdoors) || !SSmapping.level_trait(src.z, ZTRAIT_STATION))
+ if((!isspaceturf(src) && !istype(src, /turf/open/floor/plating/ocean) && !above() && !SSmapping.level_trait(src.z, ZTRAIT_UP) && !turf_area.outdoors && !turf_area.false_outdoors) || (!SSmapping.level_trait(src.z, ZTRAIT_DAYCYCLE) && !SSmapping.level_trait(src.z, ZTRAIT_STARLIGHT)))
.["SKYVISIBLE"] = FALSE
.["WEATHERPROOF"] = TRUE
From 57eb5564476c6302cd7f99a1554d8829322713c0 Mon Sep 17 00:00:00 2001
From: dwasint <82520990+dwasint@users.noreply.github.com>
Date: Sun, 27 Aug 2023 20:57:04 -0400
Subject: [PATCH 11/41] ash walker stuff
---
code/modules/hydroponics/hydroponics.dm | 15 ++++++++++++---
.../modules/mob_spawn/ghost_roles/mining_roles.dm | 7 -------
2 files changed, 12 insertions(+), 10 deletions(-)
diff --git a/code/modules/hydroponics/hydroponics.dm b/code/modules/hydroponics/hydroponics.dm
index 62870ed310bb..abad7f3b0cf5 100644
--- a/code/modules/hydroponics/hydroponics.dm
+++ b/code/modules/hydroponics/hydroponics.dm
@@ -64,6 +64,8 @@
var/sustaining_precent = 0
///do we let self sustaining increase plant stats overtime?
var/self_growing = FALSE
+ ///the multi these get for exisitng
+ var/multi = 1
/obj/machinery/hydroponics/Initialize(mapload)
create_reagents(40)
@@ -251,11 +253,11 @@
needs_update = TRUE
growth += 3
if(self_sustaining && self_growing)
- if(myseed.potency < 50)
+ if(myseed.potency < 50 * multi)
myseed.adjust_potency(2)
- if(myseed.yield < 5)
+ if(myseed.yield < 5 * multi)
myseed.adjust_yield(1)
- if(myseed.lifespan < 70)
+ if(myseed.lifespan < 70 * multi)
myseed.adjust_lifespan(2)
/**
* Nutrients
@@ -1153,6 +1155,13 @@
self_sustaining_overlay_icon_state = null
maxnutri = 15
+/obj/machinery/hydroponics/soil/Initialize(mapload)
+ . = ..()
+ if(SSmapping.level_trait(src.z, ZTRAIT_MINING))
+ multi = 5
+ self_growing = TRUE
+ self_sustaining = TRUE
+
/obj/machinery/hydroponics/soil/update_icon(updates=ALL)
. = ..()
if(self_sustaining)
diff --git a/code/modules/mob_spawn/ghost_roles/mining_roles.dm b/code/modules/mob_spawn/ghost_roles/mining_roles.dm
index f7ae8f35c4fe..41b6ac0e87fd 100644
--- a/code/modules/mob_spawn/ghost_roles/mining_roles.dm
+++ b/code/modules/mob_spawn/ghost_roles/mining_roles.dm
@@ -225,13 +225,6 @@
eggshell = null
return ..()
-/obj/effect/mob_spawn/ghost_role/human/ash_walker/allow_spawn(mob/user, silent = FALSE)
- if(!(user.key in team.players_spawned))//one per person unless you get a bonus spawn
- return TRUE
- if(!silent)
- to_chat(user, span_warning("You have exhausted your usefulness to the Necropolis."))
- return FALSE
-
/obj/effect/mob_spawn/ghost_role/human/ash_walker/special(mob/living/carbon/human/spawned_human)
. = ..()
spawned_human.fully_replace_character_name(null,random_unique_lizard_name(gender))
From 28722da44a22e59087743fa9624d3004cb26369e Mon Sep 17 00:00:00 2001
From: dwasint <82520990+dwasint@users.noreply.github.com>
Date: Mon, 28 Aug 2023 12:43:26 -0400
Subject: [PATCH 12/41] cargo borgs
---
code/__DEFINES/~monkestation/robots.dm | 13 +
code/game/objects/items/stacks/wrap.dm | 11 +
.../modules/mob/living/silicon/robot/robot.dm | 45 +-
.../mob/living/silicon/robot/robot_model.dm | 7 +-
code/modules/paperwork/clipboard.dm | 2 +-
code/modules/paperwork/paperplane.dm | 26 +-
code/modules/research/techweb/all_nodes.dm | 1 +
.../modules/cargoborg/code/cargo_module.dm | 36 ++
.../cargoborg/code/cargo_teleporter.dm | 120 +++++
.../modules/cargoborg/code/cargoborg_items.dm | 492 ++++++++++++++++++
.../cargoborg/icons/cargo_teleporter.dmi | Bin 0 -> 726 bytes
.../modules/cargoborg/icons/robots_cargo.dmi | Bin 0 -> 37433 bytes
monkestation/icons/hud/screen_cyborg.dmi | Bin 0 -> 27042 bytes
monkestation/icons/mob/robots.dmi | Bin 0 -> 210198 bytes
tgstation.dme | 4 +
15 files changed, 732 insertions(+), 25 deletions(-)
create mode 100644 code/__DEFINES/~monkestation/robots.dm
create mode 100644 monkestation/code/modules/cargoborg/code/cargo_module.dm
create mode 100644 monkestation/code/modules/cargoborg/code/cargo_teleporter.dm
create mode 100644 monkestation/code/modules/cargoborg/code/cargoborg_items.dm
create mode 100644 monkestation/code/modules/cargoborg/icons/cargo_teleporter.dmi
create mode 100644 monkestation/code/modules/cargoborg/icons/robots_cargo.dmi
create mode 100644 monkestation/icons/hud/screen_cyborg.dmi
create mode 100644 monkestation/icons/mob/robots.dmi
diff --git a/code/__DEFINES/~monkestation/robots.dm b/code/__DEFINES/~monkestation/robots.dm
new file mode 100644
index 000000000000..f643f22343c7
--- /dev/null
+++ b/code/__DEFINES/~monkestation/robots.dm
@@ -0,0 +1,13 @@
+/// To store all the different cyborg models, instead of creating that for each cyborg.
+GLOBAL_LIST_EMPTY(cyborg_model_list)
+/// To store all of the different base cyborg model icons, instead of creating them every time the pick_module() proc is called.
+GLOBAL_LIST_EMPTY(cyborg_base_models_icon_list)
+/// To store all of the different cyborg model icons, instead of creating them every time the be_transformed_to() proc is called.
+GLOBAL_LIST_EMPTY(cyborg_all_models_icon_list)
+
+
+#define CYBORG_ICON_CARGO 'monkestation/code/modules/cargoborg/icons/robots_cargo.dmi'
+
+/// Module is compatible with Cargo Cyborg model
+#define BORG_MODEL_CARGO (BORG_MODEL_ENGINEERING<<1)
+#define RND_SUBCATEGORY_MECHFAB_CYBORG_MODULES_CARGO "/Cargo Cyborgs"
diff --git a/code/game/objects/items/stacks/wrap.dm b/code/game/objects/items/stacks/wrap.dm
index 88cf80bb74f3..43b7a497b767 100644
--- a/code/game/objects/items/stacks/wrap.dm
+++ b/code/game/objects/items/stacks/wrap.dm
@@ -83,6 +83,7 @@
parcel.base_icon_state = "deliverypackage5"
parcel.update_icon()
user.forceMove(parcel)
+ parcel.contains_mobs = TRUE //monkestation edit
parcel.add_fingerprint(user)
return OXYLOSS
else
@@ -109,6 +110,10 @@
return
if(target.anchored)
return
+ //monkestation edit start
+ if(!amount)
+ return
+ //monkestation edit end
if(isitem(target))
. |= AFTERATTACK_PROCESSED_ITEM
@@ -151,6 +156,12 @@
closet.forceMove(parcel)
parcel.add_fingerprint(user)
closet.add_fingerprint(user)
+ //monkestation edit start
+ for(var/item in closet.get_all_contents())
+ if(istype(item, /mob))
+ parcel.contains_mobs = TRUE
+ break
+ //monkestation edit end
else
balloon_alert(user, "not enough paper!")
return
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index ad9d2c6d14bd..540b4e2fdffb 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -186,31 +186,38 @@
to_chat(src,span_userdanger("ERROR: Lockdown is engaged. Please disengage lockdown to pick module."))
return
- var/list/model_list = list(
- "Engineering" = /obj/item/robot_model/engineering,
- "Medical" = /obj/item/robot_model/medical,
- "Miner" = /obj/item/robot_model/miner,
- "Janitor" = /obj/item/robot_model/janitor,
- "Service" = /obj/item/robot_model/service,
- )
- if(!CONFIG_GET(flag/disable_peaceborg))
- model_list["Peacekeeper"] = /obj/item/robot_model/peacekeeper
- if(!CONFIG_GET(flag/disable_secborg))
- model_list["Security"] = /obj/item/robot_model/security
+ if(!length(GLOB.cyborg_model_list))
+ GLOB.cyborg_model_list = list(
+ "Engineering" = /obj/item/robot_model/engineering,
+ "Medical" = /obj/item/robot_model/medical,
+ "Cargo" = /obj/item/robot_model/cargo, //monkestation edit
+ "Miner" = /obj/item/robot_model/miner,
+ "Janitor" = /obj/item/robot_model/janitor,
+ "Service" = /obj/item/robot_model/service,
+ )
+ if(!CONFIG_GET(flag/disable_peaceborg))
+ GLOB.cyborg_model_list["Peacekeeper"] = /obj/item/robot_model/peacekeeper
+ if(!CONFIG_GET(flag/disable_secborg))
+ GLOB.cyborg_model_list["Security"] = /obj/item/robot_model/security
+
+ //monkestation edit start
+ for(var/model in GLOB.cyborg_model_list)
+ // Creating the lists here since we know all the model icons will need them right after.
+ GLOB.cyborg_all_models_icon_list[model] = list()
// Create radial menu for choosing borg model
- var/list/model_icons = list()
- for(var/option in model_list)
- var/obj/item/robot_model/model = model_list[option]
- var/model_icon = initial(model.cyborg_base_icon)
- model_icons[option] = image(icon = 'icons/mob/silicon/robots.dmi', icon_state = model_icon)
+ if(!length(GLOB.cyborg_base_models_icon_list))
+ for(var/option in GLOB.cyborg_model_list)
+ var/obj/item/robot_model/model = GLOB.cyborg_model_list[option]
+ var/model_icon = initial(model.cyborg_base_icon)
+ GLOB.cyborg_base_models_icon_list[option] = image(icon = 'monkestation/icons/mob/robots.dmi', icon_state = model_icon)
- var/input_model = show_radial_menu(src, src, model_icons, radius = 42)
+ var/input_model = show_radial_menu(src, src, GLOB.cyborg_base_models_icon_list, radius = 42)
if(!input_model || model.type != /obj/item/robot_model)
return
- model.transform_to(model_list[input_model])
-
+ model.transform_to(GLOB.cyborg_model_list[input_model])
+ //monkestation edit end
/// Used to setup the a basic and (somewhat) unique name for the robot.
/mob/living/silicon/robot/proc/setup_default_name()
diff --git a/code/modules/mob/living/silicon/robot/robot_model.dm b/code/modules/mob/living/silicon/robot/robot_model.dm
index 0e975bc17d30..618a020e8c7e 100644
--- a/code/modules/mob/living/silicon/robot/robot_model.dm
+++ b/code/modules/mob/living/silicon/robot/robot_model.dm
@@ -170,7 +170,11 @@
var/obj/item/gun/energy/gun = module
if(!gun.chambered)
gun.recharge_newshot() //try to reload a new shot.
-
+ //monkestation ed start
+ else if(istype(module, /obj/item/hand_labeler/cyborg))
+ var/obj/item/hand_labeler/cyborg/labeler = module
+ labeler.labels_left = 30
+ //monkestation edit end
cyborg.toner = cyborg.tonermax
/**
@@ -255,7 +259,6 @@
cyborg_base_icon = details[SKIN_ICON_STATE]
if(!isnull(details[SKIN_ICON]))
cyborg.icon = details[SKIN_ICON]
- if(!isnull(details[SKIN_PIXEL_X]))
cyborg.base_pixel_x = details[SKIN_PIXEL_X]
if(!isnull(details[SKIN_PIXEL_Y]))
cyborg.base_pixel_y = details[SKIN_PIXEL_Y]
diff --git a/code/modules/paperwork/clipboard.dm b/code/modules/paperwork/clipboard.dm
index b773095e7990..95f1e34688c0 100644
--- a/code/modules/paperwork/clipboard.dm
+++ b/code/modules/paperwork/clipboard.dm
@@ -85,7 +85,7 @@
if(toppaper)
. += toppaper.icon_state
. += toppaper.overlays
- if(pen)
+ if(!integrated_pen && pen) //monkestation edit
. += "clipboard_pen"
. += "clipboard_over"
diff --git a/code/modules/paperwork/paperplane.dm b/code/modules/paperwork/paperplane.dm
index f83de3299584..ca409d31e185 100644
--- a/code/modules/paperwork/paperplane.dm
+++ b/code/modules/paperwork/paperplane.dm
@@ -14,6 +14,16 @@
var/hit_probability = 2 //%
var/obj/item/paper/internalPaper
+ //monkestation edit start
+ /// How long does getting shot in the eyes knock you down for?
+ var/knockdown_duration = 4 SECONDS
+ /// How much eye damage does it deal at minimum on eye impact?
+ var/impact_eye_damage_lower = 6
+ /// How much eye damage does it deal at maximum on eye impact?
+ var/impact_eye_damage_higher = 8
+ /// Does it get deleted when hitting anything or landing?
+ var/delete_on_impact = FALSE
+ //monkestation edit end
/obj/item/paperplane/syndicate
desc = "Paper, masterfully folded in the shape of a plane."
throwforce = 20 //same as throwing stars, but no chance of embedding.
@@ -51,7 +61,7 @@
user.visible_message(span_suicide("[user] jams [src] in [user.p_their()] nose. It looks like [user.p_theyre()] trying to commit suicide!"))
user.adjust_eye_blur(12 SECONDS)
if(eyes)
- eyes.apply_organ_damage(rand(6,8))
+ eyes.apply_organ_damage(rand(impact_eye_damage_lower, impact_eye_damage_higher)) //monkestation edit
sleep(1 SECONDS)
return BRUTELOSS
@@ -101,16 +111,26 @@
if(..() || !ishuman(hit_atom))//if the plane is caught or it hits a nonhuman
return
+ //monkestation edit
+ if(delete_on_impact)
+ qdel(src)
+ //monkestation edit end
var/mob/living/carbon/human/H = hit_atom
var/obj/item/organ/internal/eyes/eyes = H.get_organ_slot(ORGAN_SLOT_EYES)
if(prob(hit_probability))
if(H.is_eyes_covered())
return
+ //monkestation edit
+ if(delete_on_impact)
+ qdel(src)
+ //monkestation edit end
visible_message(span_danger("\The [src] hits [H] in the eye[eyes ? "" : " socket"]!"))
H.adjust_eye_blur(12 SECONDS)
- eyes?.apply_organ_damage(rand(6,8))
- H.Paralyze(40)
+ eyes?.apply_organ_damage(rand(impact_eye_damage_lower, impact_eye_damage_higher))
+ H.Knockdown(knockdown_duration)
H.emote("scream")
+ if(delete_on_impact)
+ qdel(src)
/obj/item/paper/examine(mob/user)
. = ..()
diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm
index c5ae05fe09f3..d083a01ed810 100644
--- a/code/modules/research/techweb/all_nodes.dm
+++ b/code/modules/research/techweb/all_nodes.dm
@@ -945,6 +945,7 @@
"borg_upgrade_selfrepair",
"borg_upgrade_thrusters",
"borg_upgrade_trashofholding",
+ "borg_upgrade_clamp", //monkestation edit
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2000)
diff --git a/monkestation/code/modules/cargoborg/code/cargo_module.dm b/monkestation/code/modules/cargoborg/code/cargo_module.dm
new file mode 100644
index 000000000000..673e66bb32a3
--- /dev/null
+++ b/monkestation/code/modules/cargoborg/code/cargo_module.dm
@@ -0,0 +1,36 @@
+/atom/movable/screen/robot
+ icon = 'monkestation/icons/hud/screen_cyborg.dmi'
+
+// CARGO
+/obj/item/robot_model/cargo
+ name = "Cargo"
+ basic_modules = list(
+ /obj/item/stamp,
+ /obj/item/stamp/denied,
+ /obj/item/pen/cyborg,
+ /obj/item/clipboard/cyborg,
+ /obj/item/stack/package_wrap/cyborg,
+ /obj/item/stack/wrapping_paper/xmas/cyborg,
+ /obj/item/assembly/flash/cyborg,
+ /obj/item/borg/hydraulic_clamp,
+ /obj/item/borg/hydraulic_clamp/mail,
+ /obj/item/hand_labeler/cyborg,
+ /obj/item/dest_tagger,
+ /obj/item/crowbar/cyborg,
+ /obj/item/extinguisher,
+ /obj/item/universal_scanner,
+ /obj/item/cargo_teleporter,
+ /obj/item/boxcutter,
+ )
+ radio_channels = list(RADIO_CHANNEL_SUPPLY)
+ emag_modules = list(
+ /obj/item/stamp/chameleon,
+ /obj/item/borg/paperplane_crossbow,
+ )
+ hat_offset = 0
+ cyborg_base_icon = "cargo"
+ model_select_icon = "cargo"
+ canDispose = TRUE
+ borg_skins = list(
+ "Technician" = list(SKIN_ICON_STATE = "cargoborg", SKIN_ICON = CYBORG_ICON_CARGO)
+ )
diff --git a/monkestation/code/modules/cargoborg/code/cargo_teleporter.dm b/monkestation/code/modules/cargoborg/code/cargo_teleporter.dm
new file mode 100644
index 000000000000..9ebba9234871
--- /dev/null
+++ b/monkestation/code/modules/cargoborg/code/cargo_teleporter.dm
@@ -0,0 +1,120 @@
+GLOBAL_LIST_EMPTY(cargo_marks)
+
+/obj/item/cargo_teleporter
+ name = "cargo teleporter"
+ desc = "An item that can set down a set number of markers, allowing them to teleport items within a tile to the set markers."
+ icon = 'monkestation/code/modules/cargoborg/icons/cargo_teleporter.dmi'
+ icon_state = "cargo_tele"
+ ///the list of markers spawned by this item
+ var/list/marker_children = list()
+
+ COOLDOWN_DECLARE(use_cooldown)
+
+/obj/item/cargo_teleporter/examine(mob/user)
+ . = ..()
+ . += span_notice("Attack itself to set down the markers!")
+ . += span_notice("ALT-CLICK to remove all markers!")
+
+/obj/item/cargo_teleporter/Destroy()
+ if(length(marker_children))
+ for(var/obj/effect/decal/cleanable/cargo_mark/destroy_children in marker_children)
+ destroy_children.parent_item = null
+ qdel(destroy_children)
+ return ..()
+
+/obj/item/cargo_teleporter/attack_self(mob/user, modifiers)
+ if(length(marker_children) >= 3)
+ to_chat(user, span_warning("You may only have three spawned markers from [src]!"))
+ return
+ to_chat(user, span_notice("You place a cargo marker below your feet."))
+ var/obj/effect/decal/cleanable/cargo_mark/spawned_marker = new /obj/effect/decal/cleanable/cargo_mark(get_turf(src))
+ playsound(src, 'sound/machines/click.ogg', 50)
+ spawned_marker.parent_item = src
+ marker_children += spawned_marker
+
+/obj/item/cargo_teleporter/AltClick(mob/user)
+ if(length(marker_children))
+ for(var/obj/effect/decal/cleanable/cargo_mark/destroy_children in marker_children)
+ qdel(destroy_children)
+
+/obj/item/cargo_teleporter/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
+ if(!proximity_flag)
+ return ..()
+ if(target == src)
+ return ..()
+ if(!COOLDOWN_FINISHED(src, use_cooldown))
+ to_chat(user, span_warning("[src] is still on cooldown!"))
+ return
+ var/choice = tgui_input_list(user, "Select which cargo mark to teleport the items to?", "Cargo Mark Selection", GLOB.cargo_marks)
+ if(!choice)
+ return ..()
+ if(get_dist(user, target) > 1)
+ return
+ var/turf/moving_turf = get_turf(choice)
+ var/turf/target_turf = get_turf(target)
+ for(var/check_content in target_turf.contents)
+ if(isobserver(check_content))
+ continue
+ if(!ismovable(check_content))
+ continue
+ var/atom/movable/movable_content = check_content
+ if(isliving(movable_content))
+ continue
+ if(length(movable_content.get_all_contents_type(/mob/living)))
+ continue
+ if(movable_content.anchored)
+ continue
+ do_teleport(movable_content, moving_turf, asoundout = 'sound/magic/Disable_Tech.ogg')
+ new /obj/effect/decal/cleanable/ash(target_turf)
+ COOLDOWN_START(src, use_cooldown, 8 SECONDS)
+
+/datum/design/cargo_teleporter
+ name = "Cargo Teleporter"
+ desc = "A wonderful item that can set markers and teleport things to those markers."
+ id = "cargotele"
+ build_type = PROTOLATHE | AWAY_LATHE
+ build_path = /obj/item/cargo_teleporter
+ materials = list(/datum/material/iron = 500, /datum/material/plastic = 500, /datum/material/uranium = 500)
+ category = list(RND_CATEGORY_TOOLS + RND_SUBCATEGORY_TOOLS_CARGO)
+ departmental_flags = DEPARTMENT_BITFLAG_CARGO
+
+/datum/techweb_node/cargo_teleporter
+ id = "cargoteleporter"
+ display_name = "Cargo Teleporter"
+ description = "We can teleport items across long distances, as long as they are not blocked."
+ prereq_ids = list("bluespace_basic", "engineering")
+ design_ids = list(
+ "cargotele",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 5000)
+
+/obj/effect/decal/cleanable/cargo_mark
+ name = "cargo mark"
+ desc = "A mark left behind by a cargo teleporter, which allows targeted teleportation. Can be removed by the cargo teleporter."
+ icon = 'monkestation/code/modules/cargoborg/icons/cargo_teleporter.dmi'
+ icon_state = "marker"
+ ///the reference to the item that spawned the cargo mark
+ var/obj/item/cargo_teleporter/parent_item
+
+ light_outer_range = 3
+ light_color = COLOR_VIVID_YELLOW
+
+/obj/effect/decal/cleanable/cargo_mark/attackby(obj/item/W, mob/user, params)
+ if(istype(W, /obj/item/cargo_teleporter))
+ to_chat(user, span_notice("You remove [src] using [W]."))
+ playsound(src, 'sound/machines/click.ogg', 50)
+ qdel(src)
+ return
+ return ..()
+
+/obj/effect/decal/cleanable/cargo_mark/Destroy()
+ if(parent_item)
+ parent_item.marker_children -= src
+ GLOB.cargo_marks -= src
+ return ..()
+
+/obj/effect/decal/cleanable/cargo_mark/Initialize(mapload, list/datum/disease/diseases)
+ . = ..()
+ var/area/src_area = get_area(src)
+ name = "[src_area.name] ([rand(100000,999999)])"
+ GLOB.cargo_marks += src
diff --git a/monkestation/code/modules/cargoborg/code/cargoborg_items.dm b/monkestation/code/modules/cargoborg/code/cargoborg_items.dm
new file mode 100644
index 000000000000..5c5fbf56dbf5
--- /dev/null
+++ b/monkestation/code/modules/cargoborg/code/cargoborg_items.dm
@@ -0,0 +1,492 @@
+/// CARGO BORGS ///
+#define CYBORG_FONT "Consolas"
+#define MAX_PAPER_INTEGRATED_CLIPBOARD 10
+
+/obj/item/pen/cyborg
+ name = "integrated pen"
+ font = CYBORG_FONT
+ desc = "You can almost hear the sound of gears grinding against one another as you write with this pen. Almost."
+
+
+/obj/item/clipboard/cyborg
+ name = "\improper integrated clipboard"
+ desc = "A clipboard which seems to come adapted with a paper synthetizer, carefully hidden in its paper clip."
+ integrated_pen = TRUE
+ /// When was the last time the printer was used?
+ COOLDOWN_DECLARE(printer_cooldown)
+ /// How long is the integrated printer's cooldown?
+ var/printer_cooldown_time = 10 SECONDS
+ /// How much charge is required to print a piece of paper?
+ var/paper_charge_cost = 50
+
+
+/obj/item/clipboard/cyborg/Initialize(mapload)
+ . = ..()
+ pen = new /obj/item/pen/cyborg
+
+
+/obj/item/clipboard/cyborg/examine()
+ . = ..()
+ . += "Alt-click to synthetize a piece of paper."
+ if(!COOLDOWN_FINISHED(src, printer_cooldown))
+ . += "Its integrated paper synthetizer seems to still be on cooldown."
+
+
+/obj/item/clipboard/cyborg/AltClick(mob/user)
+ if(!iscyborg(user))
+ to_chat(user, span_warning("You do not seem to understand how to use [src]."))
+ return
+ var/mob/living/silicon/robot/cyborg_user = user
+ // Not enough charge? Tough luck.
+ if(cyborg_user?.cell.charge < paper_charge_cost)
+ to_chat(user, span_warning("Your internal cell doesn't have enough charge left to use [src]'s integrated printer."))
+ return
+ // Check for cooldown to avoid paper spamming
+ if(COOLDOWN_FINISHED(src, printer_cooldown))
+ // If there's not too much paper already, let's go
+ if(!toppaper_ref || length(contents) < MAX_PAPER_INTEGRATED_CLIPBOARD)
+ cyborg_user.cell.use(paper_charge_cost)
+ COOLDOWN_START(src, printer_cooldown, printer_cooldown_time)
+ var/obj/item/paper/new_paper = new /obj/item/paper
+ new_paper.forceMove(src)
+ if(toppaper_ref)
+ var/obj/item/paper/toppaper = toppaper_ref?.resolve()
+ UnregisterSignal(toppaper, COMSIG_ATOM_UPDATED_ICON)
+ RegisterSignal(new_paper, COMSIG_ATOM_UPDATED_ICON, PROC_REF(on_top_paper_change))
+ toppaper_ref = WEAKREF(new_paper)
+ update_appearance()
+ to_chat(user, span_notice("[src]'s integrated printer whirs to life, spitting out a fresh piece of paper and clipping it into place."))
+ else
+ to_chat(user, span_warning("[src]'s integrated printer refuses to print more paper, as [src] already contains enough paper."))
+ else
+ to_chat(user, span_warning("[src]'s integrated printer refuses to print more paper, its bluespace paper synthetizer not having finished recovering from its last synthesis."))
+
+
+/obj/item/hand_labeler/cyborg
+ name = "integrated hand labeler"
+ labels_left = 9000 // I don't want to bother forcing them to recharge, honestly, that's a lot of code for a very niche functionality
+
+
+/// The clamps
+/obj/item/borg/hydraulic_clamp
+ name = "integrated hydraulic clamp"
+ desc = "A neat way to lift and move around few small packages for quick and painless deliveries!"
+ icon = 'icons/mecha/mecha_equipment.dmi' // Just some temporary sprites because I don't have any unique one yet
+ icon_state = "mecha_clamp"
+ /// How much power does it draw per operation?
+ var/charge_cost = 20
+ /// How many items can it hold at once in its internal storage?
+ var/storage_capacity = 5
+ /// Does it require the items it takes in to be wrapped in paper wrap? Can have unforeseen consequences, change to FALSE at your own risks.
+ var/whitelisted_contents = TRUE
+ /// What kind of wrapped item can it hold, if `whitelisted_contents` is set to true?
+ var/list/whitelisted_item_types = list(/obj/item/delivery/small, /obj/item/bounty_cube)
+ /// A short description used when the check to pick up something has failed.
+ var/whitelisted_item_description = "small wrapped packages"
+ /// Weight limit on the items it can hold. Leave as NONE if there isn't.
+ var/item_weight_limit = WEIGHT_CLASS_SMALL
+ /// Can it hold mobs? (Dangerous, it is recommended to leave this to FALSE)
+ var/can_hold_mobs = FALSE
+ /// Audio for using the hydraulic clamp.
+ var/clamp_sound = 'sound/mecha/hydraulic.ogg'
+ /// Volume of the clamp's loading and unloading noise.
+ var/clamp_sound_volume = 25
+ /// Cooldown for the clamp.
+ COOLDOWN_DECLARE(clamp_cooldown)
+ /// How long is the clamp on cooldown for after every usage?
+ var/cooldown_duration = 0.5 SECONDS
+ /// How long does it take to load in an item?
+ var/loading_time = 2 SECONDS
+ /// How long does it take to unload an item?
+ var/unloading_time = 1 SECONDS
+ /// Is it currently in use?
+ var/in_use = FALSE
+ /// Index of the item we want to take out of the clamp, 0 if nothing selected.
+ var/selected_item_index = 0
+ /// Weakref to the cyborg we're currently connected to.
+ var/datum/weakref/cyborg_holding_me
+
+
+/obj/item/borg/hydraulic_clamp/Initialize(mapload)
+ . = ..()
+ if(!istype(loc, /obj/item/robot_model))
+ return
+
+ var/obj/item/robot_model/holder_model = loc
+ cyborg_holding_me = WEAKREF(holder_model.robot)
+
+ RegisterSignal(holder_model.robot, COMSIG_LIVING_DEATH, PROC_REF(empty_contents))
+
+
+/obj/item/borg/hydraulic_clamp/Destroy()
+ var/mob/living/silicon/robot/robot_holder = cyborg_holding_me?.resolve()
+ if(robot_holder)
+ UnregisterSignal(robot_holder, COMSIG_LIVING_DEATH)
+ return ..()
+
+
+/obj/item/borg/hydraulic_clamp/examine(mob/user)
+ . = ..()
+ . += span_notice("It's cargo hold has a capacity of [storage_capacity] and is currently holding [contents.len ? contents.len : 0] items in it!")
+ if(storage_capacity > 1)
+ . += span_notice("Use in hand to select an item you want to prioritize taking out of the storage.")
+
+
+/// A simple proc to empty the contents of the hydraulic clamp, forcing them on the turf it's on. Also forces `selected_item_index` to 0, to avoid any possible issues resulting from it.
+/obj/item/borg/hydraulic_clamp/proc/empty_contents()
+ SIGNAL_HANDLER
+
+ selected_item_index = 0
+ var/spilled_amount = 0
+ var/turf/turf_of_clamp = get_turf(src)
+ for(var/atom/movable/item in contents)
+ item.forceMove(turf_of_clamp)
+ spilled_amount++
+
+ if(spilled_amount)
+ var/holder = cyborg_holding_me?.resolve()
+ if(holder)
+ visible_message(span_warning("[cyborg_holding_me?.resolve()] spills the content of [src]'s cargo hold all over the floor!"))
+
+
+/obj/item/borg/hydraulic_clamp/attack_self(mob/user, modifiers)
+ if(storage_capacity <= 1) // No need for selection if there's one or less item at maximum in the clamp.
+ return
+
+ selected_item_index = 0
+
+ if(contents.len <= 1)
+ to_chat(user, span_warning("There's currently [contents.len ? "only one item" : "nothing"] to take out of [src]'s cargo hold, no need to pick!"))
+ return
+
+ . = ..()
+
+ var/list/choices = list()
+ var/index = 1
+ for(var/item in contents)
+ choices[item] = index
+ index++
+
+ var/selection = tgui_input_list(user, "Which item would you like to prioritize?", "Choose an item to prioritize", choices)
+ if(!selection)
+ return
+
+ var/new_index = choices[selection]
+ if(!new_index)
+ return
+
+ selected_item_index = new_index
+ to_chat(user, span_notice("[src] will now prioritize unloading [selection]."))
+
+
+/obj/item/borg/hydraulic_clamp/emp_act(severity)
+ . = ..()
+ empty_contents()
+
+
+/obj/item/borg/hydraulic_clamp/pre_attack(atom/attacked_atom, mob/living/silicon/robot/user, params)
+ if(!istype(user) || !user.Adjacent(attacked_atom) || !COOLDOWN_FINISHED(src, clamp_cooldown) || in_use)
+ return
+
+ // Not enough charge? Tough luck.
+ if(user?.cell.charge < charge_cost)
+ to_chat(user, span_warning("Your internal cell doesn't have enough charge left to use [src]."))
+ return
+
+ user.cell.use(charge_cost)
+ in_use = TRUE
+ COOLDOWN_START(src, clamp_cooldown, cooldown_duration)
+
+ // We're trying to unload something from the clamp, only possible on the floor, tables and conveyors.
+ if(isturf(attacked_atom) || istype(attacked_atom, /obj/structure/table) || istype(attacked_atom, /obj/machinery/conveyor))
+ if(!contents.len)
+ in_use = FALSE
+ return
+
+ var/extraction_index = selected_item_index ? selected_item_index : contents.len
+ var/atom/movable/extracted_item = contents[extraction_index]
+ selected_item_index = 0
+
+ if(unloading_time > 0.5 SECONDS) // We don't want too much chat spam if the clamp works fast.
+ to_chat(user, span_notice("You start unloading something from [src]..."))
+ playsound(src, clamp_sound, clamp_sound_volume, FALSE, -5)
+ COOLDOWN_START(src, clamp_cooldown, cooldown_duration)
+
+ if(!do_after(user, unloading_time, attacked_atom))
+ in_use = FALSE
+ return
+
+ var/turf/extraction_turf = get_turf(attacked_atom)
+ extracted_item.forceMove(extraction_turf)
+ visible_message(span_notice("[src.loc] unloads [extracted_item] from [src]."))
+ log_silicon("[user] unloaded [extracted_item] onto [extraction_turf] ([AREACOORD(extraction_turf)]).")
+ in_use = FALSE
+ return
+
+ // We're trying to load something in the clamp
+ else
+ if(whitelisted_contents && !is_type_in_list(attacked_atom, whitelisted_item_types))
+ to_chat(user, span_warning("[src] can only pick up [whitelisted_item_description]!"))
+ in_use = FALSE
+ return
+
+ if(contents.len >= storage_capacity)
+ to_chat(user, span_warning("[src] is already at full capacity!"))
+ in_use = FALSE
+ return
+
+ if(item_weight_limit)
+ var/obj/item/to_lift = attacked_atom
+ if(!to_lift || to_lift.w_class > item_weight_limit)
+ to_chat(user, span_warning("[to_lift] is too big for [src]!"))
+ in_use = FALSE
+ return
+
+ var/atom/movable/lifting_up = attacked_atom
+
+ if(lifting_up.anchored)
+ to_chat(user, span_warning("[lifting_up] is firmly secured, it's not currently possible to move it into [src]!"))
+ in_use = FALSE
+ return
+
+ var/contains_mobs = FALSE
+
+ if(istype(lifting_up, /obj/item/delivery/big))
+ var/obj/item/delivery/big/parcel = lifting_up
+ if(parcel.contains_mobs)
+ if(!can_hold_mobs)
+ to_chat(user, span_warning("[src]'s warning light blinks red: There's something with the potential to be alive inside of [parcel]!"))
+ in_use = FALSE
+ return
+ contains_mobs = TRUE
+ parcel.set_anchored(TRUE)
+
+ lifting_up.add_fingerprint(user)
+
+ if(loading_time > 0.5 SECONDS) // We don't want too much chat spam if the clamp works fast.
+ to_chat(user, span_notice("You start loading [lifting_up] into [src]'s cargo hold..."))
+ playsound(src, clamp_sound, clamp_sound_volume, FALSE, -5)
+
+ if(!do_after(user, loading_time, lifting_up)) // It takes two seconds to put stuff into the clamp's cargo hold
+ lifting_up.set_anchored(initial(lifting_up.anchored))
+ in_use = FALSE
+ return
+
+ lifting_up.set_anchored(FALSE)
+ lifting_up.forceMove(src)
+ var/turf/lifting_up_from = get_turf(lifting_up.loc)
+ log_silicon("[user] loaded [lifting_up] (Contains mobs: [contains_mobs]) into [src] at ([AREACOORD(lifting_up_from)]).")
+ visible_message(span_notice("[src.loc] loads [lifting_up] into [src]'s cargo hold."))
+ in_use = FALSE
+
+
+/obj/item/borg/hydraulic_clamp/better
+ name = "improved integrated hydraulic clamp"
+ desc = "A neat way to lift and move around wrapped crates for quick and painless deliveries!"
+ storage_capacity = 2
+ whitelisted_item_types = list(/obj/item/delivery, /obj/item/bounty_cube) // If they want to carry a small package or a bounty cube instead, so be it, honestly.
+ whitelisted_item_description = "wrapped packages"
+ item_weight_limit = NONE
+ clamp_sound_volume = 50
+
+/obj/item/borg/hydraulic_clamp/better/examine(mob/user)
+ . = ..()
+ var/crate_count = contents.len
+ . += "There is currently [crate_count > 0 ? crate_count : "no"] crate[crate_count > 1 ? "s" : ""] stored in the clamp's internal storage."
+
+/obj/item/borg/hydraulic_clamp/mail
+ name = "integrated rapid mail delivery device"
+ desc = "Allows you to carry around a lot of mail, to distribute it around the station like the good little mailbot you are!"
+ icon = 'icons/obj/library.dmi'
+ icon_state = "bookbag"
+ storage_capacity = 100
+ loading_time = 0.25 SECONDS
+ unloading_time = 0.25 SECONDS
+ cooldown_duration = 0.25 SECONDS
+ whitelisted_item_types = list(/obj/item/mail)
+ whitelisted_item_description = "enveloppes"
+ item_weight_limit = WEIGHT_CLASS_NORMAL
+ clamp_sound_volume = 25
+ clamp_sound = 'sound/items/pshoom.ogg'
+
+
+
+/datum/design/borg_upgrade_clamp
+ name = "Improved Integrated Hydraulic Clamp Module"
+ id = "borg_upgrade_clamp"
+ build_type = MECHFAB
+ build_path = /obj/item/borg/upgrade/better_clamp
+ materials = list(/datum/material/titanium = 2000 * 2, /datum/material/gold = 1000, /datum/material/bluespace = 1000)
+ construction_time = 12 SECONDS
+ category = list(RND_CATEGORY_MECHFAB_CYBORG_MODULES + RND_SUBCATEGORY_MECHFAB_CYBORG_MODULES_CARGO)
+
+
+/obj/item/borg/upgrade/better_clamp
+ name = "improved integrated hydraulic clamp"
+ desc = "An improved hydraulic clamp that trades its storage quantity to allow for bigger packages to be picked up instead!"
+ icon_state = "cyborg_upgrade3"
+ require_model = TRUE
+ model_type = list(/obj/item/robot_model/cargo)
+ model_flags = BORG_MODEL_CARGO
+
+
+/obj/item/borg/upgrade/better_clamp/action(mob/living/silicon/robot/cyborg, user = usr)
+ . = ..()
+ if(!.)
+ return
+ var/obj/item/borg/hydraulic_clamp/better/big_clamp = locate() in cyborg.model.modules
+ if(big_clamp)
+ to_chat(user, span_warning("This cyborg is already equipped with an improved integrated hydraulic clamp!"))
+ return FALSE
+
+ big_clamp = new(cyborg.model)
+ cyborg.model.basic_modules += big_clamp
+ cyborg.model.add_module(big_clamp, FALSE, TRUE)
+
+
+/obj/item/borg/upgrade/better_clamp/deactivate(mob/living/silicon/robot/cyborg, user = usr)
+ . = ..()
+ if(!.)
+ return
+ var/obj/item/borg/hydraulic_clamp/better/big_clamp = locate() in cyborg.model.modules
+ if(big_clamp)
+ cyborg.model.remove_module(big_clamp, TRUE)
+
+
+
+/// The fabled paper plane crossbow and its hardlight paper planes.
+/obj/item/paperplane/syndicate/hardlight
+ name = "hardlight paper plane"
+ desc = "Hard enough to hurt, fickle enough to be impossible to pick up."
+ impact_eye_damage_lower = 10
+ impact_eye_damage_higher = 10
+ delete_on_impact = TRUE
+ /// Which color is the paper plane?
+ var/list/paper_colors = list(COLOR_CYAN, COLOR_BLUE_LIGHT, COLOR_BLUE)
+ alpha = 150 // It's hardlight, it's gotta be see-through.
+
+
+/obj/item/paperplane/syndicate/hardlight/Initialize(mapload)
+ . = ..()
+ color = color_hex2color_matrix(pick(paper_colors))
+ alpha = initial(alpha) // It's hardlight, it's gotta be see-through.
+
+
+/obj/item/borg/paperplane_crossbow
+ name = "paper plane crossbow"
+ desc = "Be careful, don't aim for the eyes- Who am I kidding, definitely aim for the eyes!"
+ icon = 'icons/obj/weapons/guns/energy.dmi'
+ icon_state = "crossbow"
+ /// How many planes does the crossbow currently have in its internal magazine?
+ var/planes = 4
+ /// Maximum of planes the crossbow can hold.
+ var/max_planes = 4
+ /// Time it takes to regenerate one plane
+ var/charge_delay = 1 SECONDS
+ /// Is the crossbow currently charging a new paper plane?
+ var/charging = FALSE
+ /// How long is the cooldown between shots?
+ var/shooting_delay = 0.5 SECONDS
+ /// Are we ready to fire again?
+ COOLDOWN_DECLARE(shooting_cooldown)
+
+
+/obj/item/borg/paperplane_crossbow/examine(mob/user)
+ . = ..()
+ . += span_notice("There is [planes] left inside of its internal magazine, out of [max_planes].")
+ var/charging_speed = 10 / charge_delay
+ . += span_notice("It recharges at a rate of [charging_speed] plane[charging_speed >= 2 ? "s" : ""] per second.")
+
+
+/obj/item/borg/paperplane_crossbow/equipped()
+ . = ..()
+ check_amount()
+
+
+/obj/item/borg/paperplane_crossbow/dropped()
+ . = ..()
+ check_amount()
+
+
+/// A simple proc to check if we're at the max amount of planes, if not, we keep on charging. Called by [/obj/item/borg/paperplane_crossbow/proc/charge_paper_planes()].
+/obj/item/borg/paperplane_crossbow/proc/check_amount()
+ if(!charging && planes < max_planes)
+ addtimer(CALLBACK(src, PROC_REF(charge_paper_planes)), charge_delay)
+ charging = TRUE
+
+
+/// A simple proc to charge paper planes, that then calls [/obj/item/borg/paperplane_crossbow/proc/check_amount()] to see if it should charge another one, over and over.
+/obj/item/borg/paperplane_crossbow/proc/charge_paper_planes()
+ planes++
+ charging = FALSE
+ check_amount()
+
+
+/// A proc for shooting a projectile at the target, it's just that simple, really.
+/obj/item/borg/paperplane_crossbow/proc/shoot(atom/target, mob/living/user, params)
+ if(!COOLDOWN_FINISHED(src, shooting_cooldown))
+ return
+ if(planes <= 0)
+ to_chat(user, span_warning("Not enough paper planes left!"))
+ return FALSE
+ planes--
+
+ var/obj/item/paperplane/syndicate/hardlight/plane_to_fire = new /obj/item/paperplane/syndicate/hardlight(get_turf(src.loc))
+
+ playsound(src.loc, 'sound/machines/click.ogg', 50, TRUE)
+ plane_to_fire.throw_at(target, plane_to_fire.throw_range, plane_to_fire.throw_speed, user)
+ COOLDOWN_START(src, shooting_cooldown, shooting_delay)
+ user.visible_message(span_warning("[user] shoots a paper plane at [target]!"))
+ check_amount()
+
+
+/obj/item/borg/paperplane_crossbow/afterattack(atom/target, mob/living/user, proximity, click_params)
+ . = ..()
+ check_amount()
+ if(iscyborg(user))
+ var/mob/living/silicon/robot/robot_user = user
+ if(!robot_user.cell.use(10))
+ to_chat(user, span_warning("Not enough power."))
+ return FALSE
+ shoot(target, user, click_params)
+
+
+/// Holders for the package wrap and the wrapping paper synthetizers.
+
+/datum/robot_energy_storage/package_wrap
+ name ="package wrapper synthetizer"
+ max_energy = 25
+ recharge_rate = 2
+
+
+/datum/robot_energy_storage/wrapping_paper
+ name ="wrapping paper synthetizer"
+ max_energy = 25
+ recharge_rate = 2
+
+
+/obj/item/stack/package_wrap/cyborg
+ name = "integrated package wrapper"
+ is_cyborg = TRUE
+ source = /datum/robot_energy_storage/package_wrap
+
+
+/obj/item/stack/wrapping_paper/xmas/cyborg
+ name = "integrated wrapping paper"
+ is_cyborg = TRUE
+ source = /datum/robot_energy_storage/wrapping_paper
+
+
+/obj/item/stack/wrapping_paper/xmas/cyborg/use(used, transfer, check = FALSE) // Check is set to FALSE here, so the stack istn't deleted.
+ . = ..()
+
+
+/// Some override that didn't belong anywhere else.
+
+/obj/item/delivery/big
+ /// Does this wrapped package contain at least one mob?
+ var/contains_mobs = FALSE
+
+// I did this out of sanity, I didn't want to make the clamp code more complex than necessary, and honestly I'm considering taking this upstream, it just feels awkward to PR just that.
+/obj/item/bounty_cube
+ w_class = WEIGHT_CLASS_SMALL
diff --git a/monkestation/code/modules/cargoborg/icons/cargo_teleporter.dmi b/monkestation/code/modules/cargoborg/icons/cargo_teleporter.dmi
new file mode 100644
index 0000000000000000000000000000000000000000..4676ce99ed9e70bacf21ca256fb04fd787187983
GIT binary patch
literal 726
zcmV;{0xA88P)V=-0C=2j%drZ=Fc1ddIrkJl?Ha_hu34l-GPJKC!JJL7wQvcFZ|~sX;`j|;
zi_>uS6Z+B9Q?%sm0f*ymPv(_lO>pQAWRm1L)h|j7Rp8KEgU;Kf$|4P!n6TOf80~M?ZS{0M)xIDP}Dd@Bjb;1W80eRCt{2nz3%e
zFc5|>Rn@9OfW)hi2jFd3QI)}O5C&cVcAkJ2U}8#_h@Fwm5@M;ukWNnB0QK3#K9bzG
zB#|8c|1J))a|ggM48t%C!!QifK_bkc#bQxl#wAG-0RUuKR>W}}RgbPFed-%cDe=*0
z^zS$+WqCfCOklg+ULAFlSbXchVoaFCZ;escn0qPpQ8U0b2>q!5*gyRc`luNorG$RJ
zUtXWjXS4~IphN1T%%y4i=RK5C!r>r{_pMgZ!@=Lv-|c3``!<^oqdsZ|oK7bg4u>G6
zEKMAb-%f%q+|nNmUarQi*YDQ)s036^=muOwU8w5!dT#*m_$)&wKsNypF7SK$b-i9z
z2~Z~H^Lg;#$@PU0g%AR@B5R{Q!!QiPFih(pThi)e%W=8Fi>F^+Jyo@=_X57q1J9P@
zr6-N4($Nz^jxrO%R7fqx24BsWsbg>gs0~oV37|GW4JUxw05zzkFm-=`PmBq$
zsn6?)Qs>oU9mUc*X+(d3PrU2~^E?LtD7!LTyF)_Ii2eYd7!!~rNtC8(p_isFmkaFo
zpT-}jD4^Qn+#jII(>irkY(wJQAD|i}d^JUc!nr@7>47U6*B?+fsHLzTaGQb$u$TdD
z3tZ9p%~0nPz&rz#g^{NzjRE|Y$3m3G6i@NbPFWoFsnrO60O8R>=JcFNod5s;07*qo
IM6N<$f{(^h%K!iX
literal 0
HcmV?d00001
diff --git a/monkestation/code/modules/cargoborg/icons/robots_cargo.dmi b/monkestation/code/modules/cargoborg/icons/robots_cargo.dmi
new file mode 100644
index 0000000000000000000000000000000000000000..ed7c2843b2481218331446f47ea673a124414fbd
GIT binary patch
literal 37433
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