diff --git a/_maps/RandomRuins/SpaceRuins/mrow_thats_right.dmm b/_maps/RandomRuins/SpaceRuins/mrow_thats_right.dmm
index 00ba365abf..0baac3252a 100644
--- a/_maps/RandomRuins/SpaceRuins/mrow_thats_right.dmm
+++ b/_maps/RandomRuins/SpaceRuins/mrow_thats_right.dmm
@@ -350,6 +350,7 @@
dir = 4
},
/obj/structure/closet/cabinet,
+/obj/item/pda/neko,
/obj/item/gps{
gpstag = "kitty"
},
diff --git a/code/__DEFINES/citadel_defines.dm b/code/__DEFINES/citadel_defines.dm
index c7daff49ed..82f38eb092 100644
--- a/code/__DEFINES/citadel_defines.dm
+++ b/code/__DEFINES/citadel_defines.dm
@@ -12,6 +12,7 @@
#define ui_boxcraft "EAST-4:22,SOUTH+1:6"
#define ui_boxarea "EAST-4:6,SOUTH+1:6"
#define ui_boxlang "EAST-5:22,SOUTH+1:6"
+#define ui_boxvore "EAST-4:22,SOUTH+1:6"
//Filters
#define CIT_FILTER_STAMINACRIT filter(type="drop_shadow", x=0, y=0, size=-3, border=0, color="#04080F")
@@ -130,5 +131,7 @@
//component stuff
#define COMSIG_COMBAT_TOGGLED "combatmode_toggled" //called by combat mode toggle on all equipped items. args: (mob/user, combatmode)
+#define COMSIG_VORE_TOGGLED "voremode_toggled" // totally not copypasta
+
//belly sound pref things
#define NORMIE_HEARCHECK 4
diff --git a/code/__DEFINES/time.dm b/code/__DEFINES/time.dm
index 27f6eae8b6..c5d6f3051e 100644
--- a/code/__DEFINES/time.dm
+++ b/code/__DEFINES/time.dm
@@ -25,3 +25,10 @@ When using time2text(), please use "DDD" to find the weekday. Refrain from using
#define DS2TICKS(DS) ((DS)/world.tick_lag)
#define TICKS2DS(T) ((T) TICKS)
+
+#define GAMETIMESTAMP(format, wtime) time2text(wtime, format)
+#define WORLDTIME2TEXT(format) GAMETIMESTAMP(format, world.time)
+#define WORLDTIMEOFDAY2TEXT(format) GAMETIMESTAMP(format, world.timeofday)
+#define TIME_STAMP(format, showds) showds ? "[WORLDTIMEOFDAY2TEXT(format)]:[world.timeofday % 10]" : WORLDTIMEOFDAY2TEXT(format)
+#define STATION_TIME(display_only) ((((world.time - SSticker.round_start_time) * SSticker.station_time_rate_multiplier) + SSticker.gametime_offset) % 864000) - (display_only? GLOB.timezoneOffset : 0)
+#define STATION_TIME_TIMESTAMP(format) time2text(STATION_TIME(TRUE), format)
\ No newline at end of file
diff --git a/code/__DEFINES/voreconstants.dm b/code/__DEFINES/voreconstants.dm
index a133297315..710b11ae16 100644
--- a/code/__DEFINES/voreconstants.dm
+++ b/code/__DEFINES/voreconstants.dm
@@ -18,39 +18,6 @@
/* // removing sizeplay again
GLOBAL_LIST_INIT(player_sizes_list, list("Macro" = SIZESCALE_HUGE, "Big" = SIZESCALE_BIG, "Normal" = SIZESCALE_NORMAL, "Small" = SIZESCALE_SMALL, "Tiny" = SIZESCALE_TINY))
// Edited to make the new travis check go away
-
-
-GLOBAL_LIST_INIT(digest_pred, list(
- 'sound/vore/pred/digest_01.ogg',
- 'sound/vore/pred/digest_02.ogg',
- 'sound/vore/pred/digest_03.ogg',
- 'sound/vore/pred/digest_04.ogg',
- 'sound/vore/pred/digest_05.ogg',
- 'sound/vore/pred/digest_06.ogg',
- 'sound/vore/pred/digest_07.ogg',
- 'sound/vore/pred/digest_08.ogg',
- 'sound/vore/pred/digest_09.ogg',
- 'sound/vore/pred/digest_10.ogg',
- 'sound/vore/pred/digest_11.ogg',
- 'sound/vore/pred/digest_12.ogg',
- 'sound/vore/pred/digest_13.ogg',
- 'sound/vore/pred/digest_14.ogg',
- 'sound/vore/pred/digest_15.ogg',
- 'sound/vore/pred/digest_16.ogg',
- 'sound/vore/pred/digest_17.ogg',
- 'sound/vore/pred/digest_18.ogg'))
-
-GLOBAL_LIST_INIT(death_pred, list(
- 'sound/vore/pred/death_01.ogg',
- 'sound/vore/pred/death_02.ogg',
- 'sound/vore/pred/death_03.ogg',
- 'sound/vore/pred/death_04.ogg',
- 'sound/vore/pred/death_05.ogg',
- 'sound/vore/pred/death_06.ogg',
- 'sound/vore/pred/death_07.ogg',
- 'sound/vore/pred/death_08.ogg',
- 'sound/vore/pred/death_09.ogg',
- 'sound/vore/pred/death_10.ogg'))
*/
GLOBAL_LIST_INIT(vore_sounds, list(
@@ -66,79 +33,21 @@ GLOBAL_LIST_INIT(vore_sounds, list(
"Squish3" = 'sound/vore/pred/squish_03.ogg',
"Squish4" = 'sound/vore/pred/squish_04.ogg',
"Rustle (cloth)" = 'sound/effects/rustle5.ogg',
- "rustle2(cloth)" = 'sound/effects/rustle2.ogg',
- "rustle3(cloth)" = 'sound/effects/rustle3.ogg',
- "rustle4(cloth)" = 'sound/effects/rustle4.ogg',
- "rustle5(cloth)" = 'sound/effects/rustle5.ogg',
- "None" = null))
-/*
-GLOBAL_LIST_INIT(pred_struggle_sounds, list(
- "Struggle1" = 'sound/vore/pred/struggle_01.ogg',
- "Struggle2" = 'sound/vore/pred/struggle_02.ogg',
- "Struggle3" = 'sound/vore/pred/struggle_03.ogg',
- "Struggle4" = 'sound/vore/pred/struggle_04.ogg',
- "Struggle5" = 'sound/vore/pred/struggle_05.ogg'))
-
-GLOBAL_LIST_INIT(prey_vore_sounds, list(
- "Gulp" = 'sound/vore/prey/swallow_01.ogg',
- "Swallow" = 'sound/vore/prey/swallow_02.ogg',
- "Insertion1" = 'sound/vore/prey/insertion_01.ogg',
- "Insertion2" = 'sound/vore/prey/insertion_02.ogg',
- "Tauric Swallow" = 'sound/vore/prey/taurswallow.ogg',
- "Schlorp" = 'sound/vore/prey/schlorp.ogg',
- "Squish1" = 'sound/vore/prey/squish_01.ogg',
- "Squish2" = 'sound/vore/prey/squish_02.ogg',
- "Squish3" = 'sound/vore/prey/squish_03.ogg',
- "Squish4" = 'sound/vore/prey/squish_04.ogg'))
-
-
-GLOBAL_LIST_INIT(prey_struggle_sounds, list(
- "Struggle1" = 'sound/vore/prey/struggle_01.ogg',
- "Struggle2" = 'sound/vore/prey/struggle_02.ogg',
- "Struggle3" = 'sound/vore/prey/struggle_03.ogg',
- "Struggle4" = 'sound/vore/prey/struggle_04.ogg',
- "Struggle5" = 'sound/vore/prey/struggle_05.ogg'))
-
-GLOBAL_LIST_INIT(digest_prey, list(
- "digest1" = 'sound/vore/prey/digest_01.ogg',
- "digest2" = 'sound/vore/prey/digest_02.ogg',
- "digest3" = 'sound/vore/prey/digest_03.ogg',
- "digest4" = 'sound/vore/prey/digest_04.ogg',
- "digest5" = 'sound/vore/prey/digest_05.ogg',
- "digest6" = 'sound/vore/prey/digest_06.ogg',
- "digest7" = 'sound/vore/prey/digest_07.ogg',
- "digest8" = 'sound/vore/prey/digest_08.ogg',
- "digest9" = 'sound/vore/prey/digest_09.ogg',
- "digest10" = 'sound/vore/prey/digest_10.ogg',
- "digest11" = 'sound/vore/prey/digest_11.ogg',
- "digest12" = 'sound/vore/prey/digest_12.ogg',
- "digest13" = 'sound/vore/prey/digest_13.ogg',
- "digest14" = 'sound/vore/prey/digest_14.ogg',
- "digest15" = 'sound/vore/prey/digest_15.ogg',
- "digest16" = 'sound/vore/prey/digest_16.ogg',
- "digest17" = 'sound/vore/prey/digest_17.ogg',
- "digest18" = 'sound/vore/prey/digest_18.ogg'))
-
-GLOBAL_LIST_INIT(death_prey, list(
- "death1" = 'sound/vore/prey/death_01.ogg',
- "death2" = 'sound/vore/prey/death_02.ogg',
- "death3" = 'sound/vore/prey/death_03.ogg',
- "death4" = 'sound/vore/prey/death_04.ogg',
- "death5" = 'sound/vore/prey/death_05.ogg',
- "death6" = 'sound/vore/prey/death_06.ogg',
- "death7" = 'sound/vore/prey/death_07.ogg',
- "death8" = 'sound/vore/prey/death_08.ogg',
- "death9" = 'sound/vore/prey/death_09.ogg',
- "death10" = 'sound/vore/prey/death_10.ogg'))
- */
+ "Rustle 2 (cloth)" = 'sound/effects/rustle2.ogg',
+ "Rustle 3 (cloth)" = 'sound/effects/rustle3.ogg',
+ "Rustle 4 (cloth)" = 'sound/effects/rustle4.ogg',
+ "Rustle 5 (cloth)" = 'sound/effects/rustle5.ogg',
+ "None" = null
+ ))
GLOBAL_LIST_INIT(release_sounds, list(
- "rustle (cloth)" = 'sound/effects/rustle1.ogg',
- "rustle2 (cloth)" = 'sound/effects/rustle2.ogg',
- "rustle3 (cloth)" = 'sound/effects/rustle3.ogg',
- "rustle4 (cloth)" = 'sound/effects/rustle4.ogg',
- "rustle5 (cloth)" = 'sound/effects/rustle5.ogg',
- "Stomach Move" = 'sound/vore/pred/stomachmove.ogg',
- "Pred Escape" = 'sound/vore/pred/escape.ogg',
- "Splatter" = 'sound/effects/splat.ogg',
- "None" = null))
\ No newline at end of file
+ "Rustle (cloth)" = 'sound/effects/rustle1.ogg',
+ "Rustle 2 (cloth)" = 'sound/effects/rustle2.ogg',
+ "Rustle 3 (cloth)" = 'sound/effects/rustle3.ogg',
+ "Rustle 4 (cloth)" = 'sound/effects/rustle4.ogg',
+ "Rustle 5 (cloth)" = 'sound/effects/rustle5.ogg',
+ "Stomach Move" = 'sound/vore/pred/stomachmove.ogg',
+ "Pred Escape" = 'sound/vore/pred/escape.ogg',
+ "Splatter" = 'sound/effects/splat.ogg',
+ "None" = null
+ ))
diff --git a/code/__HELPERS/_cit_helpers.dm b/code/__HELPERS/_cit_helpers.dm
index b6c968b163..eb2a564d1b 100644
--- a/code/__HELPERS/_cit_helpers.dm
+++ b/code/__HELPERS/_cit_helpers.dm
@@ -90,10 +90,6 @@ GLOBAL_LIST_INIT(dildo_colors, list(//mostly neon colors
"Purple" = "#e300ff"//purple
))
-//Looc stuff
-GLOBAL_VAR_INIT(looc_allowed, 1)
-GLOBAL_VAR_INIT(dlooc_allowed, 1)
-
//Crew objective and miscreants stuff
GLOBAL_VAR_INIT(miscreants_allowed, FALSE)
@@ -124,36 +120,6 @@ GLOBAL_VAR_INIT(miscreants_allowed, FALSE)
src << "You will [(prefs.chat_toggles & CHAT_LOOC) ? "now" : "no longer"] see messages on the LOOC channel."
SSblackbox.record_feedback("tally", "admin_verb", 1, "TLOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-/datum/admins/proc/togglelooc()
- set category = "Server"
- set desc="Fukken metagamers"
- set name="Toggle LOOC"
- toggle_looc()
- log_admin("[key_name(usr)] toggled LOOC.")
- message_admins("[key_name_admin(usr)] toggled LOOC.")
- SSblackbox.record_feedback("tally", "admin_verb", 1, "TLOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
-/proc/toggle_looc(toggle = null)
- if(toggle != null) //if we're specifically en/disabling ooc
- if(toggle != GLOB.looc_allowed)
- GLOB.looc_allowed = toggle
- else
- return
- else //otherwise just toggle it
- GLOB.looc_allowed = !GLOB.looc_allowed
- world << "The LOOC channel has been globally [GLOB.looc_allowed ? "enabled" : "disabled"]."
-
-/datum/admins/proc/toggleloocdead()
- set category = "Server"
- set desc="Toggle dis bitch"
- set name="Toggle Dead LOOC"
- GLOB.dlooc_allowed = !( GLOB.dlooc_allowed )
-
- log_admin("[key_name(usr)] toggled Dead LOOC.")
- message_admins("[key_name_admin(usr)] toggled Dead LOOC.")
- SSblackbox.record_feedback("tally", "admin_verb", 1, "TDLOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
-
/mob/living/carbon/proc/has_penis()
if(getorganslot("penis"))//slot shared with ovipositor
if(istype(getorganslot("penis"), /obj/item/organ/genital/penis))
diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm
index 7d578337bc..ad29125d95 100644
--- a/code/__HELPERS/game.dm
+++ b/code/__HELPERS/game.dm
@@ -413,7 +413,8 @@
var/list/candidates = list()
for(var/mob/dead/observer/G in GLOB.player_list)
- candidates += G
+ if(G.can_reenter_round)
+ candidates += G
return pollCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category, flashwindow, candidates)
diff --git a/code/__HELPERS/text_vr.dm b/code/__HELPERS/text_vr.dm
index fadc7b4513..64e13ef65b 100644
--- a/code/__HELPERS/text_vr.dm
+++ b/code/__HELPERS/text_vr.dm
@@ -24,8 +24,8 @@ GLOBAL_LIST_EMPTY(whitelisted_species_list)
/proc/log_mentor(text)
GLOB.mentorlog.Add(text)
- WRITE_FILE(GLOB.world_game_log, "\[[time_stamp()]]MENTOR: [text]")
+ WRITE_FILE(GLOB.world_game_log, "\[[TIME_STAMP("hh:mm:ss", FALSE)]]MENTOR: [text]")
/proc/log_looc(text)
if (CONFIG_GET(flag/log_ooc))
- WRITE_FILE(GLOB.world_game_log, "\[[time_stamp()]]LOOC: [text]")
\ No newline at end of file
+ WRITE_FILE(GLOB.world_game_log, "\[[TIME_STAMP("hh:mm:ss", FALSE)]]LOOC: [text]")
\ No newline at end of file
diff --git a/code/__HELPERS/time.dm b/code/__HELPERS/time.dm
index 5fe65af8f3..200e1f907f 100644
--- a/code/__HELPERS/time.dm
+++ b/code/__HELPERS/time.dm
@@ -1,22 +1,3 @@
-//Returns the world time in english
-/proc/worldtime2text()
- return gameTimestamp("hh:mm:ss", world.time)
-
-/proc/time_stamp(format = "hh:mm:ss", show_ds)
- var/time_string = time2text(world.timeofday, format)
- return show_ds ? "[time_string]:[world.timeofday % 10]" : time_string
-
-/proc/gameTimestamp(format = "hh:mm:ss", wtime=null)
- if(!wtime)
- wtime = world.time
- return time2text(wtime - GLOB.timezoneOffset, format)
-
-/proc/station_time(display_only = FALSE)
- return ((((world.time - SSticker.round_start_time) * SSticker.station_time_rate_multiplier) + SSticker.gametime_offset) % 864000) - (display_only? GLOB.timezoneOffset : 0)
-
-/proc/station_time_timestamp(format = "hh:mm:ss")
- return time2text(station_time(TRUE), format)
-
/proc/station_time_debug(force_set)
if(isnum(force_set))
SSticker.gametime_offset = force_set
diff --git a/code/_globalvars/configuration.dm b/code/_globalvars/configuration.dm
index fe83828e05..2334ce6bca 100644
--- a/code/_globalvars/configuration.dm
+++ b/code/_globalvars/configuration.dm
@@ -9,6 +9,7 @@ GLOBAL_VAR_INIT(changelog_hash, "")
GLOBAL_VAR_INIT(hub_visibility, FALSE)
GLOBAL_VAR_INIT(ooc_allowed, TRUE) // used with admin verbs to disable ooc - not a config option apparently
+GLOBAL_VAR_INIT(looc_allowed, TRUE)
GLOBAL_VAR_INIT(dooc_allowed, TRUE)
GLOBAL_VAR_INIT(aooc_allowed, FALSE)
GLOBAL_VAR_INIT(enter_allowed, TRUE)
diff --git a/code/_onclick/drag_drop.dm b/code/_onclick/drag_drop.dm
index cc13e262a8..e5c0ffc1ab 100644
--- a/code/_onclick/drag_drop.dm
+++ b/code/_onclick/drag_drop.dm
@@ -111,7 +111,7 @@
if(mob && LAZYLEN(mob.mousemove_intercept_objects))
for(var/datum/D in mob.mousemove_intercept_objects)
D.onMouseMove(object, location, control, params)
- if(mob) //CIT CHANGE - passes onmousemove() to mobs
+ if(!show_popup_menus && mob) //CIT CHANGE - passes onmousemove() to mobs
mob.onMouseMove(object, location, control, params) //CIT CHANGE - ditto
..()
diff --git a/code/_onclick/hud/_defines.dm b/code/_onclick/hud/_defines.dm
index b558e7c87c..a987f70d7b 100644
--- a/code/_onclick/hud/_defines.dm
+++ b/code/_onclick/hud/_defines.dm
@@ -85,6 +85,7 @@
#define ui_crafting "EAST-5:20,SOUTH:5"//CIT CHANGE - moves this over one tile to accommodate for combat mode toggle
#define ui_building "EAST-5:20,SOUTH:21"//CIT CHANGE - ditto
#define ui_language_menu "EAST-5:4,SOUTH:21"//CIT CHANGE - ditto
+#define ui_voremode "EAST-5:20,SOUTH:5"
#define ui_borg_pull "EAST-2:26,SOUTH+1:7"
#define ui_borg_radio "EAST-1:28,SOUTH+1:7"
diff --git a/code/_onclick/hud/human.dm b/code/_onclick/hud/human.dm
index 1106bee711..01e118aaed 100644
--- a/code/_onclick/hud/human.dm
+++ b/code/_onclick/hud/human.dm
@@ -109,6 +109,13 @@
using.screen_loc = ui_boxarea // CIT CHANGE
static_inventory += using
+ using = new /obj/screen/voretoggle() //We fancy Vore now
+ using.icon = tg_ui_icon_to_cit_ui(ui_style)
+ using.screen_loc = ui_voremode
+ if(!widescreenlayout)
+ using.screen_loc = ui_boxvore
+ static_inventory += using
+
action_intent = new /obj/screen/act_intent/segmented
action_intent.icon_state = mymob.a_intent
static_inventory += action_intent
diff --git a/code/controllers/subsystem/nightshift.dm b/code/controllers/subsystem/nightshift.dm
index 325ba212c6..df406eed3c 100644
--- a/code/controllers/subsystem/nightshift.dm
+++ b/code/controllers/subsystem/nightshift.dm
@@ -26,7 +26,7 @@ SUBSYSTEM_DEF(nightshift)
/datum/controller/subsystem/nightshift/proc/check_nightshift()
var/emergency = GLOB.security_level >= SEC_LEVEL_RED
var/announcing = TRUE
- var/time = station_time()
+ var/time = STATION_TIME(FALSE)
var/night_time = (time < nightshift_end_time) || (time > nightshift_start_time)
if(high_security_mode != emergency)
high_security_mode = emergency
diff --git a/code/controllers/subsystem/shuttle.dm b/code/controllers/subsystem/shuttle.dm
index e2e7c3d327..78cceddc8b 100644
--- a/code/controllers/subsystem/shuttle.dm
+++ b/code/controllers/subsystem/shuttle.dm
@@ -54,6 +54,7 @@ SUBSYSTEM_DEF(shuttle)
var/lockdown = FALSE //disallow transit after nuke goes off
var/auto_call = 72000 //CIT CHANGE - time before in deciseconds in which the shuttle is auto called. Default is 2ish hours plus 15 for the shuttle. So total is 3.
+ var/realtimeofstart = 0
/datum/controller/subsystem/shuttle/Initialize(timeofday)
ordernum = rand(1, 9000)
@@ -74,6 +75,7 @@ SUBSYSTEM_DEF(shuttle)
WARNING("No /obj/docking_port/mobile/emergency/backup placed on the map!")
if(!supply)
WARNING("No /obj/docking_port/mobile/supply placed on the map!")
+ realtimeofstart = world.realtime
return ..()
/datum/controller/subsystem/shuttle/proc/initial_load()
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index d54f6fc3ef..9a4cad3667 100755
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -304,6 +304,7 @@ SUBSYSTEM_DEF(ticker)
to_chat(world, "
[holiday.greet()]
")
PostSetup()
+ SSshuttle.realtimeofstart = world.realtime
return TRUE
@@ -655,7 +656,8 @@ SUBSYSTEM_DEF(ticker)
'sound/roundend/its_only_game.ogg',
'sound/roundend/yeehaw.ogg',
'sound/roundend/disappointed.ogg',
- 'sound/roundend/gondolabridge.ogg'\
+ 'sound/roundend/gondolabridge.ogg',
+ 'sound/roundend/haveabeautifultime.ogg'\
)
SEND_SOUND(world, sound(round_end_sound))
diff --git a/code/datums/components/forensics.dm b/code/datums/components/forensics.dm
index 9a67d01c5c..c7a1f7eba5 100644
--- a/code/datums/components/forensics.dm
+++ b/code/datums/components/forensics.dm
@@ -130,7 +130,7 @@
var/mob/living/carbon/human/H = M
if(H.gloves)
hasgloves = "(gloves)"
- var/current_time = time_stamp()
+ var/current_time = TIME_STAMP("hh:mm:ss", FALSE)
if(!LAZYACCESS(hiddenprints, M.key))
LAZYSET(hiddenprints, M.key, "First: [M.real_name]\[[current_time]\][hasgloves]. Ckey: [M.ckey]")
else
diff --git a/code/datums/components/storage/storage.dm b/code/datums/components/storage/storage.dm
index bb3c584cb9..dc25729e37 100644
--- a/code/datums/components/storage/storage.dm
+++ b/code/datums/components/storage/storage.dm
@@ -297,7 +297,7 @@
ND.number++
//This proc determines the size of the inventory to be displayed. Please touch it only if you know what you're doing.
-/datum/component/storage/proc/orient2hud()
+/datum/component/storage/proc/orient2hud(mob/user, maxcolumns)
var/atom/real_location = real_location()
var/adjusted_contents = real_location.contents.len
@@ -307,7 +307,7 @@
numbered_contents = _process_numerical_display()
adjusted_contents = numbered_contents.len
- var/columns = CLAMP(max_items, 1, screen_max_columns)
+ var/columns = CLAMP(max_items, 1, maxcolumns ? maxcolumns : screen_max_columns)
var/rows = CLAMP(CEILING(adjusted_contents / columns, 1), 1, screen_max_rows)
standard_orient_objs(rows, columns, numbered_contents)
@@ -351,6 +351,8 @@
/datum/component/storage/proc/show_to(mob/M)
if(!M.client)
return FALSE
+ var/list/cview = getviewsize(M.client.view)
+ var/maxallowedscreensize = cview[1]-8
var/atom/real_location = real_location()
if(M.active_storage != src && (M.stat == CONSCIOUS))
for(var/obj/item/I in real_location)
@@ -358,7 +360,7 @@
return FALSE
if(M.active_storage)
M.active_storage.hide_from(M)
- orient2hud()
+ orient2hud(M, (isliving(M) ? maxallowedscreensize : 7))
M.client.screen |= boxes
M.client.screen |= closer
M.client.screen |= real_location.contents
diff --git a/code/datums/explosion.dm b/code/datums/explosion.dm
index 0b3e431af6..fe16b9459f 100644
--- a/code/datums/explosion.dm
+++ b/code/datums/explosion.dm
@@ -92,7 +92,7 @@ GLOBAL_LIST_EMPTY(explosions)
var/y0 = epicenter.y
var/z0 = epicenter.z
var/area/areatype = get_area(epicenter)
- SSblackbox.record_feedback("associative", "explosion", 1, list("dev" = devastation_range, "heavy" = heavy_impact_range, "light" = light_impact_range, "flash" = flash_range, "flame" = flame_range, "orig_dev" = orig_dev_range, "orig_heavy" = orig_heavy_range, "orig_light" = orig_light_range, "x" = x0, "y" = y0, "z" = z0, "area" = areatype.type, "time" = time_stamp("YYYY-MM-DD hh:mm:ss", 1)))
+ SSblackbox.record_feedback("associative", "explosion", 1, list("dev" = devastation_range, "heavy" = heavy_impact_range, "light" = light_impact_range, "flash" = flash_range, "flame" = flame_range, "orig_dev" = orig_dev_range, "orig_heavy" = orig_heavy_range, "orig_light" = orig_light_range, "x" = x0, "y" = y0, "z" = z0, "area" = areatype.type, "time" = TIME_STAMP("YYYY-MM-DD hh:mm:ss", 1)))
// Play sounds; we want sounds to be different depending on distance so we will manually do it ourselves.
// Stereo users will also hear the direction of the explosion!
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index d467d8fd9d..90affe0228 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -752,6 +752,11 @@
for(var/X in spell_list)
var/obj/effect/proc_holder/spell/S = X
S.action.Grant(new_character)
+ var/datum/antagonist/changeling/changeling = new_character.mind.has_antag_datum(/datum/antagonist/changeling)
+ if(changeling &&(ishuman(new_character) || ismonkey(new_character)))
+ for(var/P in changeling.purchasedpowers)
+ var/obj/effect/proc_holder/changeling/I = P
+ I.action.Grant(new_character)
/datum/mind/proc/disrupt_spells(delay, list/exceptions = New())
for(var/X in spell_list)
diff --git a/code/datums/traits/good.dm b/code/datums/traits/good.dm
index bd55869c99..cfd8f95d7b 100644
--- a/code/datums/traits/good.dm
+++ b/code/datums/traits/good.dm
@@ -156,3 +156,17 @@
mob_trait = TRAIT_VORACIOUS
gain_text = "You feel HONGRY."
lose_text = "You no longer feel HONGRY."
+
+/datum/quirk/trandening
+ name = "High Luminosity Eyes"
+ desc = "When the next big fancy implant came out you had to buy one on impluse!"
+ value = 1
+ gain_text = "You have to keep up with the next big thing!."
+ lose_text = "High-tech gizmos are a scam..."
+
+/datum/quirk/trandening/on_spawn()
+ var/mob/living/carbon/human/H = quirk_holder
+ var/obj/item/autosurgeon/gloweyes = new(get_turf(H))
+ H.put_in_hands(gloweyes)
+ H.equip_to_slot(gloweyes, SLOT_IN_BACKPACK)
+ H.regenerate_icons()
diff --git a/code/datums/wires/airlock.dm b/code/datums/wires/airlock.dm
index 63140e6b25..26942a1ba6 100644
--- a/code/datums/wires/airlock.dm
+++ b/code/datums/wires/airlock.dm
@@ -79,7 +79,7 @@
if(!A.secondsElectrified)
A.set_electrified(30)
if(usr)
- LAZYADD(A.shockedby, text("\[[time_stamp()]\] [key_name(usr)]"))
+ LAZYADD(A.shockedby, text("\[[TIME_STAMP("hh:mm:ss", FALSE)]\] [key_name(usr)]"))
log_combat(usr, A, "electrified")
if(WIRE_SAFETY)
A.safe = !A.safe
@@ -134,7 +134,7 @@
if(A.secondsElectrified != -1)
A.set_electrified(-1)
if(usr)
- LAZYADD(A.shockedby, text("\[[time_stamp()]\] [key_name(usr)]"))
+ LAZYADD(A.shockedby, text("\[[TIME_STAMP("hh:mm:ss", FALSE)]\] [key_name(usr)]"))
log_combat(usr, A, "electrified")
if(WIRE_SAFETY) // Cut to disable safeties, mend to re-enable.
A.safe = mend
diff --git a/code/game/gamemodes/clock_cult/clock_cult.dm b/code/game/gamemodes/clock_cult/clock_cult.dm
index 06a365a82c..d6513b5d40 100644
--- a/code/game/gamemodes/clock_cult/clock_cult.dm
+++ b/code/game/gamemodes/clock_cult/clock_cult.dm
@@ -131,9 +131,9 @@ Credit where due:
config_tag = "clockwork_cult"
antag_flag = ROLE_SERVANT_OF_RATVAR
false_report_weight = 10
- required_players = 20
- required_enemies = 2
- recommended_enemies = 4
+ required_players = 30
+ required_enemies = 3
+ recommended_enemies = 5
enemy_minimum_age = 7
protected_jobs = list("AI", "Cyborg", "Security Officer", "Warden", "Detective", "Head of Security", "Captain") //Silicons can eventually be converted
restricted_jobs = list("Chaplain", "Captain")
diff --git a/code/game/gamemodes/cult/cult.dm b/code/game/gamemodes/cult/cult.dm
index 782ad54e4c..7b492e3a95 100644
--- a/code/game/gamemodes/cult/cult.dm
+++ b/code/game/gamemodes/cult/cult.dm
@@ -37,9 +37,9 @@
false_report_weight = 10
restricted_jobs = list("Chaplain","AI", "Cyborg", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel")
protected_jobs = list()
- required_players = 20
- required_enemies = 2
- recommended_enemies = 4
+ required_players = 30
+ required_enemies = 3
+ recommended_enemies = 5
enemy_minimum_age = 7
announce_span = "cult"
diff --git a/code/game/machinery/computer/apc_control.dm b/code/game/machinery/computer/apc_control.dm
index 8615af205b..e51d623c2f 100644
--- a/code/game/machinery/computer/apc_control.dm
+++ b/code/game/machinery/computer/apc_control.dm
@@ -197,7 +197,7 @@
/obj/machinery/computer/apc_control/proc/log_activity(log_text)
var/op_string = operator && !(obj_flags & EMAGGED) ? operator : "\[NULL OPERATOR\]"
- LAZYADD(logs, "([station_time_timestamp()]) [op_string] [log_text]")
+ LAZYADD(logs, "([STATION_TIME_TIMESTAMP("hh:mm:ss")]) [op_string] [log_text]")
/mob/proc/using_power_flow_console()
for(var/obj/machinery/computer/apc_control/A in range(1, src))
diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm
index 3b87c923d7..43f5b96fbd 100755
--- a/code/game/machinery/computer/communications.dm
+++ b/code/game/machinery/computer/communications.dm
@@ -202,7 +202,7 @@
state = STATE_CANCELSHUTTLE
if("cancelshuttle2")
if(authenticated)
- if(world.time > SSshuttle.auto_call) //Citadel Edit Removing auto_call caused recall.
+ if((world.realtime - SSshuttle.realtimeofstart) > SSshuttle.auto_call) //Citadel Edit Removing auto_call caused recall.
say("Warning: Emergency shuttle recalls have been blocked by Central Command due to ongoing crew transfer procedures.")
else
SSshuttle.cancelEvac(usr)
diff --git a/code/game/machinery/computer/medical.dm b/code/game/machinery/computer/medical.dm
index d65fb49161..8af83ae08e 100644
--- a/code/game/machinery/computer/medical.dm
+++ b/code/game/machinery/computer/medical.dm
@@ -493,7 +493,7 @@
var/counter = 1
while(src.active2.fields[text("com_[]", counter)])
counter++
- src.active2.fields[text("com_[]", counter)] = text("Made by [] ([]) on [] [], []
[]", src.authenticated, src.rank, station_time_timestamp(), time2text(world.realtime, "MMM DD"), GLOB.year_integer+540, t1)
+ src.active2.fields[text("com_[]", counter)] = text("Made by [] ([]) on [] [], []
[]", src.authenticated, src.rank, STATION_TIME_TIMESTAMP("hh:mm:ss"), time2text(world.realtime, "MMM DD"), GLOB.year_integer+540, t1)
else if(href_list["del_c"])
if((istype(src.active2, /datum/data/record) && src.active2.fields[text("com_[]", href_list["del_c"])]))
diff --git a/code/game/machinery/computer/security.dm b/code/game/machinery/computer/security.dm
index bacedad6c0..607f8dbe78 100644
--- a/code/game/machinery/computer/security.dm
+++ b/code/game/machinery/computer/security.dm
@@ -474,7 +474,7 @@ What a mess.*/
var/counter = 1
while(active2.fields[text("com_[]", counter)])
counter++
- active2.fields[text("com_[]", counter)] = text("Made by [] ([]) on [] [], []
[]", src.authenticated, src.rank, station_time_timestamp(), time2text(world.realtime, "MMM DD"), GLOB.year_integer+540, t1)
+ active2.fields[text("com_[]", counter)] = text("Made by [] ([]) on [] [], []
[]", src.authenticated, src.rank, STATION_TIME_TIMESTAMP("hh:mm:ss"), time2text(world.realtime, "MMM DD"), GLOB.year_integer+540, t1)
if("Delete Record (ALL)")
if(active1)
@@ -652,7 +652,7 @@ What a mess.*/
var/t2 = stripped_input(usr, "Please input minor crime details:", "Secure. records", "", null)
if(!canUseSecurityRecordsConsole(usr, t1, null, a2))
return
- var/crime = GLOB.data_core.createCrimeEntry(t1, t2, authenticated, station_time_timestamp())
+ var/crime = GLOB.data_core.createCrimeEntry(t1, t2, authenticated, STATION_TIME_TIMESTAMP("hh:mm:ss"))
GLOB.data_core.addMinorCrime(active1.fields["id"], crime)
investigate_log("New Minor Crime: [t1]: [t2] | Added to [active1.fields["name"]] by [key_name(usr)]", INVESTIGATE_RECORDS)
if("mi_crim_delete")
@@ -667,7 +667,7 @@ What a mess.*/
var/t2 = stripped_input(usr, "Please input major crime details:", "Secure. records", "", null)
if(!canUseSecurityRecordsConsole(usr, t1, null, a2))
return
- var/crime = GLOB.data_core.createCrimeEntry(t1, t2, authenticated, station_time_timestamp())
+ var/crime = GLOB.data_core.createCrimeEntry(t1, t2, authenticated, STATION_TIME_TIMESTAMP("hh:mm:ss"))
GLOB.data_core.addMajorCrime(active1.fields["id"], crime)
investigate_log("New Major Crime: [t1]: [t2] | Added to [active1.fields["name"]] by [key_name(usr)]", INVESTIGATE_RECORDS)
if("ma_crim_delete")
diff --git a/code/game/machinery/computer/telecrystalconsoles.dm b/code/game/machinery/computer/telecrystalconsoles.dm
index 43f84656f9..692bc8f038 100644
--- a/code/game/machinery/computer/telecrystalconsoles.dm
+++ b/code/game/machinery/computer/telecrystalconsoles.dm
@@ -136,7 +136,7 @@ GLOBAL_LIST_INIT(possible_uplinker_IDs, list("Alfa","Bravo","Charlie","Delta","E
var/list/transferlog = list()
/obj/machinery/computer/telecrystals/boss/proc/logTransfer(logmessage)
- transferlog += ("[station_time_timestamp()] [logmessage]")
+ transferlog += ("[STATION_TIME_TIMESTAMP("hh:mm:ss")] [logmessage]")
/obj/machinery/computer/telecrystals/boss/proc/scanUplinkers()
for(var/obj/machinery/computer/telecrystals/uplinker/A in urange(scanrange, src.loc))
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index 3d7e8dd00e..0183fcecd4 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -1327,7 +1327,7 @@
bolt() //Bolt it!
set_electrified(ELECTRIFIED_PERMANENT) //Shock it!
if(origin)
- LAZYADD(shockedby, "\[[time_stamp()]\] [key_name(origin)]")
+ LAZYADD(shockedby, "\[[TIME_STAMP("hh:mm:ss", FALSE)]\] [key_name(origin)]")
/obj/machinery/door/airlock/disable_lockdown()
@@ -1596,7 +1596,7 @@
if(wires.is_cut(WIRE_SHOCK))
to_chat(user, "The electrification wire has been cut")
else
- LAZYADD(shockedby, "\[[time_stamp()]\] [key_name(user)]")
+ LAZYADD(shockedby, "\[[TIME_STAMP("hh:mm:ss", FALSE)]\] [key_name(user)]")
log_combat(user, src, "electrified")
set_electrified(AI_ELECTRIFY_DOOR_TIME)
@@ -1606,7 +1606,7 @@
if(wires.is_cut(WIRE_SHOCK))
to_chat(user, "The electrification wire has been cut")
else
- LAZYADD(shockedby, text("\[[time_stamp()]\] [key_name(user)]"))
+ LAZYADD(shockedby, text("\[[TIME_STAMP("hh:mm:ss", FALSE)]\] [key_name(user)]"))
log_combat(user, src, "electrified")
set_electrified(ELECTRIFIED_PERMANENT)
diff --git a/code/game/machinery/doors/brigdoors.dm b/code/game/machinery/doors/brigdoors.dm
index 3542b4e8bd..5c8cd4ffb8 100644
--- a/code/game/machinery/doors/brigdoors.dm
+++ b/code/game/machinery/doors/brigdoors.dm
@@ -71,7 +71,7 @@
return
if(timing)
- if(world.time - activation_time >= timer_duration)
+ if(world.realtime - activation_time >= timer_duration)
timer_end() // open doors, reset timer, clear status screen
update_icon()
@@ -87,7 +87,7 @@
if(stat & (NOPOWER|BROKEN))
return 0
- activation_time = world.time
+ activation_time = world.realtime
timing = TRUE
for(var/obj/machinery/door/window/brigdoor/door in targets)
@@ -136,7 +136,7 @@
/obj/machinery/door_timer/proc/time_left(seconds = FALSE)
- . = max(0,timer_duration - (activation_time ? world.time - activation_time : 0))
+ . = max(0,timer_duration - (activation_time ? world.realtime - activation_time : 0))
if(seconds)
. /= 10
@@ -240,7 +240,7 @@
preset_time = PRESET_LONG
. = set_timer(preset_time)
if(timing)
- activation_time = world.time
+ activation_time = world.realtime
else
. = FALSE
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index 9c01c186e1..a2da7de29a 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -223,7 +223,7 @@
if(prob(severity*10 - 20))
if(secondsElectrified == 0)
secondsElectrified = -1
- LAZYADD(shockedby, "\[[time_stamp()]\]EM Pulse")
+ LAZYADD(shockedby, "\[[TIME_STAMP("hh:mm:ss", FALSE)]\]EM Pulse")
addtimer(CALLBACK(src, .proc/unelectrify), 300)
/obj/machinery/door/proc/unelectrify()
diff --git a/code/game/machinery/newscaster.dm b/code/game/machinery/newscaster.dm
index 86e8fc8af5..c37c607a6e 100644
--- a/code/game/machinery/newscaster.dm
+++ b/code/game/machinery/newscaster.dm
@@ -122,7 +122,7 @@ GLOBAL_LIST_EMPTY(allCasters)
var/datum/newscaster/feed_message/newMsg = new /datum/newscaster/feed_message
newMsg.author = author
newMsg.body = msg
- newMsg.time_stamp = "[station_time_timestamp()]"
+ newMsg.time_stamp = "[STATION_TIME_TIMESTAMP("hh:mm:ss")]"
newMsg.is_admin_message = adminMessage
newMsg.locked = !allow_comments
if(picture)
@@ -696,7 +696,7 @@ GLOBAL_LIST_EMPTY(allCasters)
var/datum/newscaster/feed_comment/FC = new/datum/newscaster/feed_comment
FC.author = scanned_user
FC.body = cominput
- FC.time_stamp = station_time_timestamp()
+ FC.time_stamp = STATION_TIME_TIMESTAMP("hh:mm:ss")
FM.comments += FC
usr.log_message("(as [scanned_user]) commented on message [FM.returnBody(-1)] -- [FC.body]", LOG_COMMENT)
updateUsrDialog()
diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm
index efa1b44445..d5de4a4898 100644
--- a/code/game/mecha/mecha.dm
+++ b/code/game/mecha/mecha.dm
@@ -1017,7 +1017,7 @@
/obj/mecha/log_message(message as text, message_type=LOG_GAME, color=null, log_globally)
log.len++
- log[log.len] = list("time"="[station_time_timestamp()]","date","year"="[GLOB.year_integer+540]","message"="[color?"":null][message][color?"":null]")
+ log[log.len] = list("time"="[STATION_TIME_TIMESTAMP("hh:mm:ss")]","date","year"="[GLOB.year_integer+540]","message"="[color?"":null][message][color?"":null]")
..()
return log.len
diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm
index ba4ae66d6a..9ff717816c 100644
--- a/code/game/objects/items/devices/PDA/PDA.dm
+++ b/code/game/objects/items/devices/PDA/PDA.dm
@@ -220,7 +220,7 @@ GLOBAL_LIST_EMPTY(PDAs)
dat += text("ID: [id ? "[id.registered_name], [id.assignment]" : "----------"]")
dat += text("
[id ? "Update PDA Info" : ""]
")
- dat += "[station_time_timestamp()]
" //:[world.time / 100 % 6][world.time / 100 % 10]"
+ dat += "[STATION_TIME_TIMESTAMP("hh:mm:ss")]
" //:[world.time / 100 % 6][world.time / 100 % 10]"
dat += "[time2text(world.realtime, "MMM DD")] [GLOB.year_integer+540]"
dat += "
"
@@ -391,6 +391,7 @@ GLOBAL_LIST_EMPTY(PDAs)
//BASIC FUNCTIONS===================================
if("Refresh")//Refresh, goes to the end of the proc.
+ playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
if ("Toggle_Font")
//CODE REVISION 2
@@ -405,12 +406,16 @@ GLOBAL_LIST_EMPTY(PDAs)
font_mode = FONT_ORBITRON
if (MODE_VT)
font_mode = FONT_VT
+ playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
+
if ("Change_Color")
var/new_color = input("Please enter a color name or hex value (Default is \'#808000\').",background_color)as color
background_color = new_color
+ playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
if ("Toggle_Underline")
underline_flag = !underline_flag
+ playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
if("Return")//Return
if(mode<=9)
@@ -419,13 +424,19 @@ GLOBAL_LIST_EMPTY(PDAs)
mode = round(mode/10)
if(mode==4 || mode == 5)//Fix for cartridges. Redirects to hub.
mode = 0
+ playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
+
if ("Authenticate")//Checks for ID
id_check(U)
+
if("UpdateInfo")
ownjob = id.assignment
if(istype(id, /obj/item/card/id/syndicate))
owner = id.registered_name
update_label()
+ playsound(src, 'sound/machines/terminal_processing.ogg', 50, 1)
+ addtimer(CALLBACK(GLOBAL_PROC, .proc/playsound, src, 'sound/machines/terminal_success.ogg', 50, 1), 13)
+
if("Eject")//Ejects the cart, only done from hub.
if (!isnull(cartridge))
U.put_in_hands(cartridge)
@@ -434,55 +445,74 @@ GLOBAL_LIST_EMPTY(PDAs)
cartridge.host_pda = null
cartridge = null
update_icon()
+ playsound(src, 'sound/machines/terminal_eject_disc.ogg', 50, 1)
//MENU FUNCTIONS===================================
if("0")//Hub
mode = 0
+ playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
if("1")//Notes
mode = 1
+ playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
if("2")//Messenger
mode = 2
+ playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
if("21")//Read messeges
mode = 21
+ playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
if("3")//Atmos scan
mode = 3
+ playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
if("4")//Redirects to hub
mode = 0
+ playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
//MAIN FUNCTIONS===================================
if("Light")
toggle_light()
+ playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
+
if("Medical Scan")
if(scanmode == PDA_SCANNER_MEDICAL)
scanmode = PDA_SCANNER_NONE
else if((!isnull(cartridge)) && (cartridge.access & CART_MEDICAL))
scanmode = PDA_SCANNER_MEDICAL
+ playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
+
if("Reagent Scan")
if(scanmode == PDA_SCANNER_REAGENT)
scanmode = PDA_SCANNER_NONE
else if((!isnull(cartridge)) && (cartridge.access & CART_REAGENT_SCANNER))
scanmode = PDA_SCANNER_REAGENT
+ playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
+
if("Halogen Counter")
if(scanmode == PDA_SCANNER_HALOGEN)
scanmode = PDA_SCANNER_NONE
else if((!isnull(cartridge)) && (cartridge.access & CART_ENGINE))
scanmode = PDA_SCANNER_HALOGEN
+ playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
+
if("Honk")
if ( !(last_noise && world.time < last_noise + 20) )
playsound(src, 'sound/items/bikehorn.ogg', 50, 1)
last_noise = world.time
+
if("Trombone")
if ( !(last_noise && world.time < last_noise + 20) )
playsound(src, 'sound/misc/sadtrombone.ogg', 50, 1)
last_noise = world.time
+
if("Gas Scan")
if(scanmode == PDA_SCANNER_GAS)
scanmode = PDA_SCANNER_NONE
else if((!isnull(cartridge)) && (cartridge.access & CART_ATMOS))
scanmode = PDA_SCANNER_GAS
+ playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
+
if("Drone Phone")
var/alert_s = input(U,"Alert severity level","Ping Drones",null) as null|anything in list("Low","Medium","High","Critical")
var/area/A = get_area(U)
@@ -490,6 +520,7 @@ GLOBAL_LIST_EMPTY(PDAs)
var/msg = "NON-DRONE PING: [U.name]: [alert_s] priority alert in [A.name]!"
_alert_drones(msg, TRUE, U)
to_chat(U, msg)
+ playsound(src, 'sound/machines/terminal_success.ogg', 50, 1)
//NOTEKEEPER FUNCTIONS===================================
@@ -641,6 +672,7 @@ GLOBAL_LIST_EMPTY(PDAs)
if (!signal.data["done"])
to_chat(user, "ERROR: Server isn't responding.")
return
+ playsound(src, 'sound/machines/terminal_error.ogg', 50, 1)
var/target_text = signal.format_target()
// Log it in our logs
@@ -653,6 +685,7 @@ GLOBAL_LIST_EMPTY(PDAs)
// Log in the talk log
user.log_talk(message, LOG_PDA, tag="PDA: [initial(name)] to [target_text]")
to_chat(user, "Message sent to [target_text]: \"[message]\"")
+ playsound(src, 'sound/machines/terminal_success.ogg', 50, 1)
// Reset the photo
picture = null
last_text = world.time
@@ -699,8 +732,10 @@ GLOBAL_LIST_EMPTY(PDAs)
if(id)
remove_id()
+ playsound(src, 'sound/machines/terminal_eject_disc.ogg', 50, 1)
else
remove_pen()
+ playsound(src, 'sound/machines/button4.ogg', 50, 1)
/obj/item/pda/CtrlClick()
..()
@@ -776,6 +811,7 @@ GLOBAL_LIST_EMPTY(PDAs)
if(old_id)
user.put_in_hands(old_id)
update_icon()
+ playsound(src, 'sound/machines/button.ogg', 50, 1)
return TRUE
// access to status display signals
@@ -787,17 +823,21 @@ GLOBAL_LIST_EMPTY(PDAs)
cartridge.host_pda = src
to_chat(user, "You insert [cartridge] into [src].")
update_icon()
+ playsound(src, 'sound/machines/button.ogg', 50, 1)
else if(istype(C, /obj/item/card/id))
var/obj/item/card/id/idcard = C
if(!idcard.registered_name)
to_chat(user, "\The [src] rejects the ID!")
return
+ playsound(src, 'sound/machines/terminal_error.ogg', 50, 1)
+
if(!owner)
owner = idcard.registered_name
ownjob = idcard.assignment
update_label()
to_chat(user, "Card scanned.")
+ playsound(src, 'sound/machines/terminal_success.ogg', 50, 1)
else
//Basic safety check. If either both objects are held by user or PDA is on ground and card is in hand.
if(((src in user.contents) || (isturf(loc) && in_range(src, user))) && (C in user.contents))
@@ -823,6 +863,8 @@ GLOBAL_LIST_EMPTY(PDAs)
to_chat(user, "You slide \the [C] into \the [src].")
inserted_item = C
update_icon()
+ playsound(src, 'sound/machines/button.ogg', 50, 1)
+
else if(istype(C, /obj/item/photo))
var/obj/item/photo/P = C
picture = P.picture
diff --git a/code/game/objects/items/devices/PDA/PDA_types.dm b/code/game/objects/items/devices/PDA/PDA_types.dm
index 089286efda..80d8cb5963 100644
--- a/code/game/objects/items/devices/PDA/PDA_types.dm
+++ b/code/game/objects/items/devices/PDA/PDA_types.dm
@@ -114,7 +114,7 @@
/obj/item/pda/heads/rd
name = "research director PDA"
default_cartridge = /obj/item/cartridge/rd
- inserted_item = /obj/item/pen/fountain
+ inserted_item = /obj/item/pen/fourcolor
icon_state = "pda-rd"
/obj/item/pda/captain
@@ -185,6 +185,12 @@
desc = "A portable microcomputer by Thinktronic Systems, LTD. This model is a special edition with a transparent case."
note = "Congratulations, you have chosen the Thinktronic 5230 Personal Data Assistant Deluxe Special Max Turbo Limited Edition!"
+/obj/item/pda/neko
+ name = "neko PDA"
+ icon_state = "pda-neko"
+ desc = "A portable microcomputer by Thinktronic Systems, LTD. This model is a special edition a feline fine case."
+ note = "Congratulations, you have chosen the Thinktronic 5230 Personal Data Assistant Deluxe Special Mew Turbo Limited Edition NYA~!"
+
/obj/item/pda/cook
name = "cook PDA"
icon_state = "pda-cook"
diff --git a/code/game/objects/items/devices/PDA/cart.dm b/code/game/objects/items/devices/PDA/cart.dm
index 421cc0989c..fc268e2b3e 100644
--- a/code/game/objects/items/devices/PDA/cart.dm
+++ b/code/game/objects/items/devices/PDA/cart.dm
@@ -580,6 +580,7 @@ Code:
host_pda.mode = 441
if(!active2)
active1 = null
+ playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
if("Security Records")
active1 = find_record("id", href_list["target"], GLOB.data_core.general)
@@ -588,19 +589,23 @@ Code:
host_pda.mode = 451
if(!active3)
active1 = null
+ playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
if("Send Signal")
INVOKE_ASYNC(radio, /obj/item/integrated_signaler.proc/send_activation)
+ playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
if("Signal Frequency")
var/new_frequency = sanitize_frequency(radio.frequency + text2num(href_list["sfreq"]))
radio.set_frequency(new_frequency)
+ playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
if("Signal Code")
radio.code += text2num(href_list["scode"])
radio.code = round(radio.code)
radio.code = min(100, radio.code)
radio.code = max(1, radio.code)
+ playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
if("Status")
switch(href_list["statdisp"])
@@ -616,16 +621,21 @@ Code:
updateSelfDialog()
else
post_status(href_list["statdisp"])
+ playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
+
if("Power Select")
var/pnum = text2num(href_list["target"])
powmonitor = powermonitors[pnum]
host_pda.mode = 433
+ playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
if("Supply Orders")
host_pda.mode =47
+ playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
if("Newscaster Access")
host_pda.mode = 53
+ playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
if("Newscaster Message")
var/host_pda_owner_name = host_pda.id ? "[host_pda.id.registered_name] ([host_pda.id.assignment])" : "Unknown"
@@ -641,11 +651,13 @@ Code:
GLOB.news_network.SubmitArticle(message,host_pda.owner,current_channel)
host_pda.Topic(null,list("choice"=num2text(host_pda.mode)))
return
+ playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
if("Newscaster Switch Channel")
current_channel = host_pda.msg_input()
host_pda.Topic(null,list("choice"=num2text(host_pda.mode)))
return
+ playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
//Bot control section! Viciously ripped from radios for being laggy and terrible.
if(href_list["op"])
@@ -656,10 +668,13 @@ Code:
if("botlist")
active_bot = null
+
if("summon") //Args are in the correct order, they are stated here just as an easy reminder.
active_bot.bot_control(command= "summon", user_turf= get_turf(usr), user_access= host_pda.GetAccess())
+
else //Forward all other bot commands to the bot itself!
active_bot.bot_control(command= href_list["op"], user= usr)
+ playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
if(href_list["mule"]) //MULEbots are special snowflakes, and need different args due to how they work.
diff --git a/code/game/objects/items/devices/forcefieldprojector.dm b/code/game/objects/items/devices/forcefieldprojector.dm
index 86e98aef93..de62869e7e 100644
--- a/code/game/objects/items/devices/forcefieldprojector.dm
+++ b/code/game/objects/items/devices/forcefieldprojector.dm
@@ -34,6 +34,10 @@
if(LAZYLEN(current_fields) >= max_fields)
to_chat(user, "[src] cannot sustain any more forcefields!")
return
+ var/obj/structure/projected_forcefield/same = locate() in T
+ if(same)
+ to_chat(user, "There is already a forcefield on [T]!")
+ return
playsound(src,'sound/weapons/resonator_fire.ogg',50,1)
user.visible_message("[user] projects a forcefield!","You project a forcefield.")
@@ -59,6 +63,8 @@
/obj/item/forcefield_projector/Destroy()
STOP_PROCESSING(SSobj, src)
+ for(var/i in current_fields)
+ qdel(i)
return ..()
/obj/item/forcefield_projector/process()
diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm
index 38cfdb95ae..d5806494e6 100644
--- a/code/game/objects/items/robot/robot_upgrades.dm
+++ b/code/game/objects/items/robot/robot_upgrades.dm
@@ -504,6 +504,31 @@
var/obj/item/surgical_processor/SP = locate() in R.module
R.module.remove_module(SP, TRUE)
+/obj/item/borg/upgrade/advhealth
+ name = "advanced cyborg health scanner"
+ desc = "An upgrade to the Medical modules, installing a built-in \
+ advanced health scanner, for better readings on patients."
+ icon_state = "cyborg_upgrade3"
+ require_module = 1
+ module_type = list(
+ /obj/item/robot_module/medical,
+ /obj/item/robot_module/syndicate_medical,
+ /obj/item/robot_module/medihound,
+ /obj/item/robot_module/borgi)
+
+/obj/item/borg/upgrade/advhealth/action(mob/living/silicon/robot/R, user = usr)
+ . = ..()
+ if(.)
+ var/obj/item/healthanalyzer/advanced/AH = new(R.module)
+ R.module.basic_modules += AH
+ R.module.add_module(AH, FALSE, TRUE)
+
+/obj/item/borg/upgrade/processor/deactivate(mob/living/silicon/robot/R, user = usr)
+ . = ..()
+ if (.)
+ var/obj/item/healthanalyzer/advanced/AH = locate() in R.module
+ R.module.remove_module(AH, TRUE)
+
/obj/item/borg/upgrade/ai
name = "B.O.R.I.S. module"
desc = "Bluespace Optimized Remote Intelligence Synchronization. An uplink device which takes the place of an MMI in cyborg endoskeletons, creating a robotic shell controlled by an AI."
@@ -638,4 +663,4 @@
name = "borg module picker (Clown)"
desc = "Allows you to to turn a cyborg into a clown, honk."
icon_state = "cyborg_upgrade3"
- new_module = /obj/item/robot_module/clown
\ No newline at end of file
+ new_module = /obj/item/robot_module/clown
diff --git a/code/game/objects/items/storage/bags.dm b/code/game/objects/items/storage/bags.dm
index aa0c504bb4..bd2bede87c 100644
--- a/code/game/objects/items/storage/bags.dm
+++ b/code/game/objects/items/storage/bags.dm
@@ -365,3 +365,15 @@
STR.max_items = 25
STR.insert_preposition = "in"
STR.can_hold = typecacheof(list(/obj/item/slime_extract, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/blood, /obj/item/reagent_containers/hypospray/medipen, /obj/item/reagent_containers/food/snacks/deadmouse, /obj/item/reagent_containers/food/snacks/monkeycube))
+
+/obj/item/storage/bag/bio/holding
+ name = "bio bag of holding"
+ icon = 'icons/obj/chemical.dmi'
+ icon_state = "bspace_biobag"
+ desc = "A bag for the safe transportation and disposal of biowaste and other biological materials."
+
+/obj/item/storage/bag/bio/holding/ComponentInitialize()
+ . = ..()
+ GET_COMPONENT(STR, /datum/component/storage)
+ STR.max_combined_w_class = INFINITY
+ STR.max_items = 100
diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm
index 7aaee7c5d4..eabd9f1968 100644
--- a/code/game/objects/items/toys.dm
+++ b/code/game/objects/items/toys.dm
@@ -1089,7 +1089,7 @@
/obj/item/toy/clockwork_watch/examine(mob/user)
..()
- to_chat(user, "Station Time: [station_time_timestamp()]")
+ to_chat(user, "Station Time: [STATION_TIME_TIMESTAMP("hh:mm:ss")]")
/*
* Toy Dagger
diff --git a/code/game/sound.dm b/code/game/sound.dm
index cceed31cfb..5503c6103d 100644
--- a/code/game/sound.dm
+++ b/code/game/sound.dm
@@ -213,6 +213,9 @@
'sound/vore/prey/death_04.ogg','sound/vore/prey/death_05.ogg','sound/vore/prey/death_06.ogg',
'sound/vore/prey/death_07.ogg','sound/vore/prey/death_08.ogg','sound/vore/prey/death_09.ogg',
'sound/vore/prey/death_10.ogg')
+ if("hunger_sounds")
+ soundin = pick( 'sound/vore/growl1.ogg','sound/vore/growl2.ogg','sound/vore/growl3.ogg','sound/vore/growl4.ogg',
+ 'sound/vore/growl5.ogg')
if("clang")
soundin = pick('sound/effects/clang1.ogg', 'sound/effects/clang2.ogg')
if("clangsmall")
diff --git a/code/game/world.dm b/code/game/world.dm
index d8576206dd..dedf822597 100644
--- a/code/game/world.dm
+++ b/code/game/world.dm
@@ -6,7 +6,7 @@ GLOBAL_VAR(restart_counter)
//So subsystems globals exist, but are not initialised
/world/New()
- log_world("World loaded at [time_stamp()]!")
+ log_world("World loaded at [TIME_STAMP("hh:mm:ss", FALSE)]!")
SetupExternalRSC()
@@ -90,7 +90,7 @@ GLOBAL_VAR(restart_counter)
GLOB.picture_logging_prefix += "R_[GLOB.round_id]_"
GLOB.picture_log_directory += "[GLOB.round_id]"
else
- var/timestamp = replacetext(time_stamp(), ":", ".")
+ var/timestamp = replacetext(TIME_STAMP("hh:mm:ss", FALSE), ":", ".")
GLOB.log_directory += "[timestamp]"
GLOB.picture_log_directory += "[timestamp]"
GLOB.picture_logging_prefix += "T_[timestamp]_"
@@ -226,11 +226,11 @@ GLOBAL_VAR(restart_counter)
do_hard_reboot = FALSE
if(do_hard_reboot)
- log_world("World hard rebooted at [time_stamp()]")
+ log_world("World hard rebooted at [TIME_STAMP("hh:mm:ss", FALSE)]")
shutdown_logging() // See comment below.
TgsEndProcess()
- log_world("World rebooted at [time_stamp()]")
+ log_world("World rebooted at [TIME_STAMP("hh:mm:ss", FALSE)]")
shutdown_logging() // Past this point, no logging procs can be used, at risk of data loss.
..()
diff --git a/code/modules/NTNet/network.dm b/code/modules/NTNet/network.dm
index ae8948dafb..645f05ac90 100644
--- a/code/modules/NTNet/network.dm
+++ b/code/modules/NTNet/network.dm
@@ -133,14 +133,14 @@
return FALSE
/datum/ntnet/proc/log_data_transfer(datum/netdata/data)
- logs += "[station_time_timestamp()] - [data.generate_netlog()]"
+ logs += "[STATION_TIME_TIMESTAMP("hh:mm:ss")] - [data.generate_netlog()]"
if(logs.len > setting_maxlogcount)
logs = logs.Copy(logs.len - setting_maxlogcount, 0)
return
// Simplified logging: Adds a log. log_string is mandatory parameter, source is optional.
/datum/ntnet/proc/add_log(log_string, obj/item/computer_hardware/network_card/source = null)
- var/log_text = "[station_time_timestamp()] - "
+ var/log_text = "[STATION_TIME_TIMESTAMP("hh:mm:ss")] - "
if(source)
log_text += "[source.get_network_tag()] - "
else
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index 1a398f56fc..b400f44b98 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -538,13 +538,22 @@
message_admins("[key_name_admin(usr)] toggled OOC.")
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle OOC", "[GLOB.ooc_allowed ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+/datum/admins/proc/toggleooclocal()
+ set category = "Server"
+ set desc="Toggle dat bitch"
+ set name="Toggle Local OOC"
+ toggle_looc()
+ log_admin("[key_name(usr)] toggled LOOC.")
+ message_admins("[key_name_admin(usr)] toggled LOOC.")
+ SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Local OOC", "[GLOB.ooc_allowed ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+
/datum/admins/proc/toggleoocdead()
set category = "Server"
set desc="Toggle dis bitch"
set name="Toggle Dead OOC"
toggle_dooc()
- log_admin("[key_name(usr)] toggled OOC.")
+ log_admin("[key_name(usr)] toggled Dead OOC.")
message_admins("[key_name_admin(usr)] toggled Dead OOC.")
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Dead OOC", "[GLOB.dooc_allowed ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
diff --git a/code/modules/admin/admin_investigate.dm b/code/modules/admin/admin_investigate.dm
index ebbef58cbb..8e1926115f 100644
--- a/code/modules/admin/admin_investigate.dm
+++ b/code/modules/admin/admin_investigate.dm
@@ -2,7 +2,7 @@
if(!message || !subject)
return
var/F = file("[GLOB.log_directory]/[subject].html")
- WRITE_FILE(F, "[time_stamp()] [REF(src)] ([x],[y],[z]) || [src] [message]
")
+ WRITE_FILE(F, "[TIME_STAMP("hh:mm:ss", FALSE)] [REF(src)] ([x],[y],[z]) || [src] [message]
")
/client/proc/investigate_show(subject in list("notes, memos, watchlist", INVESTIGATE_RESEARCH, INVESTIGATE_EXONET, INVESTIGATE_PORTAL, INVESTIGATE_SINGULO, INVESTIGATE_WIRES, INVESTIGATE_TELESCI, INVESTIGATE_GRAVITY, INVESTIGATE_RECORDS, INVESTIGATE_CARGO, INVESTIGATE_SUPERMATTER, INVESTIGATE_ATMOS, INVESTIGATE_EXPERIMENTOR, INVESTIGATE_BOTANY, INVESTIGATE_HALLUCINATIONS, INVESTIGATE_RADIATION, INVESTIGATE_CIRCUIT, INVESTIGATE_NANITES) )
set name = "Investigate"
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index e21c2b8873..ac1ae51f69 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -24,6 +24,7 @@ GLOBAL_LIST_INIT(admin_verbs_admin, world.AVerbsAdmin())
/client/proc/game_panel, /*game panel, allows to change game-mode etc*/
/client/proc/check_ai_laws, /*shows AI and borg laws*/
/datum/admins/proc/toggleooc, /*toggles ooc on/off for everyone*/
+ /datum/admins/proc/toggleooclocal, /*toggles looc on/off for everyone*/
/datum/admins/proc/toggleoocdead, /*toggles ooc on/off for everyone who is dead*/
/datum/admins/proc/toggleaooc, /*toggles antag ooc on/off*/
/datum/admins/proc/toggleenter, /*toggles whether people can join the current game*/
diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm
index c81c92bd0a..91fdc78d20 100644
--- a/code/modules/admin/verbs/adminhelp.dm
+++ b/code/modules/admin/verbs/adminhelp.dm
@@ -214,7 +214,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
if(heard_by_no_admins && usr && usr.ckey != initiator_ckey)
heard_by_no_admins = FALSE
send2irc(initiator_ckey, "Ticket #[id]: Answered by [key_name(usr)]")
- _interactions += "[time_stamp()]: [formatted_message]"
+ _interactions += "[TIME_STAMP("hh:mm:ss", FALSE)]: [formatted_message]"
//Removes the ahelp verb and returns it after 2 minutes
/datum/admin_help/proc/TimeoutVerb()
@@ -416,9 +416,9 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
dat += "[GLOB.TAB][TicketHref("Refresh", ref_src)][GLOB.TAB][TicketHref("Re-Title", ref_src, "retitle")]"
if(state != AHELP_ACTIVE)
dat += "[GLOB.TAB][TicketHref("Reopen", ref_src, "reopen")]"
- dat += "
Opened at: [gameTimestamp(wtime = opened_at)] (Approx [DisplayTimeText(world.time - opened_at)] ago)"
+ dat += "
Opened at: [GAMETIMESTAMP("hh:mm:ss", closed_at)] (Approx [DisplayTimeText(world.time - opened_at)] ago)"
if(closed_at)
- dat += "
Closed at: [gameTimestamp(wtime = closed_at)] (Approx [DisplayTimeText(world.time - closed_at)] ago)"
+ dat += "
Closed at: [GAMETIMESTAMP("hh:mm:ss", closed_at)] (Approx [DisplayTimeText(world.time - closed_at)] ago)"
dat += "
"
if(initiator)
dat += "Actions: [FullMonty(ref_src)]
"
diff --git a/code/modules/admin/verbs/panicbunker.dm b/code/modules/admin/verbs/panicbunker.dm
index ab3aeb4ba5..2897427d6f 100644
--- a/code/modules/admin/verbs/panicbunker.dm
+++ b/code/modules/admin/verbs/panicbunker.dm
@@ -13,6 +13,7 @@
if (new_pb && !SSdbcore.Connect())
message_admins("The Database is not connected! Panic bunker will not work until the connection is reestablished.")
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Panic Bunker", "[new_pb ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+ send2irc("Panic Bunker", "[key_name(usr)] has toggled the Panic Bunker, it is now [new_pb ? "enabled" : "disabled"].")
/client/proc/addbunkerbypass(ckeytobypass as text)
set category = "Special Verbs"
@@ -24,7 +25,8 @@
GLOB.bunker_passthrough |= ckey(ckeytobypass)
log_admin("[key_name(usr)] has added [ckeytobypass] to the current round's bunker bypass list.")
- message_admins("[key_name(usr)] has added [ckeytobypass] to the current round's bunker bypass list.")
+ message_admins("[key_name_admin(usr)] has added [ckeytobypass] to the current round's bunker bypass list.")
+ send2irc("Panic Bunker", "[key_name(usr)] has added [ckeytobypass] to the current round's bunker bypass list.")
/client/proc/revokebunkerbypass(ckeytobypass as text)
set category = "Special Verbs"
@@ -36,5 +38,5 @@
GLOB.bunker_passthrough -= ckey(ckeytobypass)
log_admin("[key_name(usr)] has removed [ckeytobypass] from the current round's bunker bypass list.")
- message_admins("[key_name(usr)] has removed [ckeytobypass] from the current round's bunker bypass list.")
-
+ message_admins("[key_name_admin(usr)] has removed [ckeytobypass] from the current round's bunker bypass list.")
+ send2irc("Panic Bunker", "[key_name(usr)] has removed [ckeytobypass] from the current round's bunker bypass list.")
diff --git a/code/modules/antagonists/changeling/cellular_emporium.dm b/code/modules/antagonists/changeling/cellular_emporium.dm
index 2a37a3885d..3cf0a3ee25 100644
--- a/code/modules/antagonists/changeling/cellular_emporium.dm
+++ b/code/modules/antagonists/changeling/cellular_emporium.dm
@@ -72,7 +72,7 @@
name = "Cellular Emporium"
icon_icon = 'icons/obj/drinks.dmi'
button_icon_state = "changelingsting"
- background_icon_state = "bg_alien"
+ background_icon_state = "bg_ling"
var/datum/cellular_emporium/cellular_emporium
/datum/action/innate/cellular_emporium/New(our_target)
diff --git a/code/modules/antagonists/changeling/changeling_power.dm b/code/modules/antagonists/changeling/changeling_power.dm
index c89dc50cec..1d4f15ec9d 100644
--- a/code/modules/antagonists/changeling/changeling_power.dm
+++ b/code/modules/antagonists/changeling/changeling_power.dm
@@ -20,10 +20,12 @@
/obj/effect/proc_holder/changeling/proc/on_purchase(mob/user, is_respec)
+ action.Grant(user)
if(!is_respec)
SSblackbox.record_feedback("tally", "changeling_power_purchase", 1, name)
/obj/effect/proc_holder/changeling/proc/on_refund(mob/user)
+ action.Remove(user)
return
/obj/effect/proc_holder/changeling/Click()
diff --git a/code/modules/antagonists/changeling/powers/absorb.dm b/code/modules/antagonists/changeling/powers/absorb.dm
index 2f8fc6943f..34a8231960 100644
--- a/code/modules/antagonists/changeling/powers/absorb.dm
+++ b/code/modules/antagonists/changeling/powers/absorb.dm
@@ -4,6 +4,9 @@
chemical_cost = 0
dna_cost = 0
req_human = 1
+ action_icon = 'icons/mob/actions/actions_changeling.dmi'
+ action_icon_state = "ling_absorb_dna"
+ action_background_icon_state = "bg_ling"
/obj/effect/proc_holder/changeling/absorbDNA/can_sting(mob/living/carbon/user)
if(!..())
diff --git a/code/modules/antagonists/changeling/powers/adrenaline.dm b/code/modules/antagonists/changeling/powers/adrenaline.dm
index 34d6e3605b..ede14a23c9 100644
--- a/code/modules/antagonists/changeling/powers/adrenaline.dm
+++ b/code/modules/antagonists/changeling/powers/adrenaline.dm
@@ -6,6 +6,9 @@
dna_cost = 2
req_human = 1
req_stat = UNCONSCIOUS
+ action_icon = 'icons/mob/actions/actions_changeling.dmi'
+ action_icon_state = "ling_adrenals"
+ action_background_icon_state = "bg_ling"
//Recover from stuns.
/obj/effect/proc_holder/changeling/adrenaline/sting_action(mob/living/user)
diff --git a/code/modules/antagonists/changeling/powers/augmented_eyesight.dm b/code/modules/antagonists/changeling/powers/augmented_eyesight.dm
index 5ec5440a2b..6eafeda63f 100644
--- a/code/modules/antagonists/changeling/powers/augmented_eyesight.dm
+++ b/code/modules/antagonists/changeling/powers/augmented_eyesight.dm
@@ -8,12 +8,16 @@
chemical_cost = 0
dna_cost = 2 //Would be 1 without thermal vision
active = FALSE
+ action_icon = 'icons/mob/actions/actions_changeling.dmi'
+ action_icon_state = "ling_augmented_eyesight"
+ action_background_icon_state = "bg_ling"
/obj/effect/proc_holder/changeling/augmented_eyesight/on_purchase(mob/user) //The ability starts inactive, so we should be protected from flashes.
var/obj/item/organ/eyes/E = user.getorganslot(ORGAN_SLOT_EYES)
if (E)
E.flash_protect = 2 //Adjust the user's eyes' flash protection
to_chat(user, "We adjust our eyes to protect them from bright lights.")
+ action.Grant(user)
else
to_chat(user, "We can't adjust our eyes if we don't have any!")
@@ -42,6 +46,7 @@
/obj/effect/proc_holder/changeling/augmented_eyesight/on_refund(mob/user) //Get rid of X-ray vision and flash protection when the user refunds this ability
+ action.Remove(user)
var/obj/item/organ/eyes/E = user.getorganslot(ORGAN_SLOT_EYES)
if(E)
if (active)
diff --git a/code/modules/antagonists/changeling/powers/biodegrade.dm b/code/modules/antagonists/changeling/powers/biodegrade.dm
index d1a2cc3891..f58b90d8e6 100644
--- a/code/modules/antagonists/changeling/powers/biodegrade.dm
+++ b/code/modules/antagonists/changeling/powers/biodegrade.dm
@@ -6,6 +6,9 @@
loudness = 1
dna_cost = 2
req_human = 1
+ action_icon = 'icons/mob/actions/actions_changeling.dmi'
+ action_icon_state = "ling_freedom"
+ action_background_icon_state = "bg_ling"
/obj/effect/proc_holder/changeling/biodegrade/sting_action(mob/living/carbon/human/user)
var/used = FALSE // only one form of shackles removed per use
diff --git a/code/modules/antagonists/changeling/powers/chameleon_skin.dm b/code/modules/antagonists/changeling/powers/chameleon_skin.dm
index 3be5103105..b7545353b2 100644
--- a/code/modules/antagonists/changeling/powers/chameleon_skin.dm
+++ b/code/modules/antagonists/changeling/powers/chameleon_skin.dm
@@ -5,6 +5,9 @@
dna_cost = 2
chemical_cost = 25
req_human = 1
+ action_icon = 'icons/mob/actions/actions_changeling.dmi'
+ action_icon_state = "ling_camouflage"
+ action_background_icon_state = "bg_ling"
/obj/effect/proc_holder/changeling/chameleon_skin/sting_action(mob/user)
var/mob/living/carbon/human/H = user //SHOULD always be human, because req_human = 1
@@ -18,6 +21,7 @@
return TRUE
/obj/effect/proc_holder/changeling/chameleon_skin/on_refund(mob/user)
+ action.Remove(user)
if(user.has_dna())
var/mob/living/carbon/C = user
var/datum/mutation/human/HM = GLOB.mutations_list[CHAMELEON]
diff --git a/code/modules/antagonists/changeling/powers/digitalcamo.dm b/code/modules/antagonists/changeling/powers/digitalcamo.dm
index e8bad0e215..6a0f78b532 100644
--- a/code/modules/antagonists/changeling/powers/digitalcamo.dm
+++ b/code/modules/antagonists/changeling/powers/digitalcamo.dm
@@ -4,6 +4,9 @@
helptext = "We cannot be tracked by camera or seen by AI units while using this skill. However, humans looking at us will find us... uncanny. This ability is somewhat loud, and carries a small risk of our blood gaining violent sensitivity to heat."
dna_cost = 1
loudness = 1
+ action_icon = 'icons/mob/actions/actions_changeling.dmi'
+ action_icon_state = "ling_digital_camo"
+ action_background_icon_state = "bg_ling"
//Prevents AIs tracking you but makes you easily detectable to the human-eye.
/obj/effect/proc_holder/changeling/digitalcamo/sting_action(mob/user)
@@ -19,5 +22,6 @@
return TRUE
/obj/effect/proc_holder/changeling/digitalcamo/on_refund(mob/user)
+ action.Remove(user)
user.digitalcamo = 0
user.digitalinvis = 0
\ No newline at end of file
diff --git a/code/modules/antagonists/changeling/powers/fakedeath.dm b/code/modules/antagonists/changeling/powers/fakedeath.dm
index 753d858cef..c19eb50e7f 100644
--- a/code/modules/antagonists/changeling/powers/fakedeath.dm
+++ b/code/modules/antagonists/changeling/powers/fakedeath.dm
@@ -6,13 +6,16 @@
req_dna = 1
req_stat = DEAD
ignores_fakedeath = TRUE
+ action_icon = 'icons/mob/actions/actions_changeling.dmi'
+ action_icon_state = "ling_regenerative_stasis"
+ action_background_icon_state = "bg_ling"
//Fake our own death and fully heal. You will appear to be dead but regenerate fully after a short delay.
/obj/effect/proc_holder/changeling/fakedeath/sting_action(mob/living/user)
to_chat(user, "We begin our stasis, preparing energy to arise once more.")
if(user.stat != DEAD)
user.emote("deathgasp")
- user.tod = station_time_timestamp()
+ user.tod = STATION_TIME_TIMESTAMP("hh:mm:ss")
user.fakedeath("changeling") //play dead
user.update_stat()
user.update_canmove()
@@ -25,7 +28,9 @@
var/datum/antagonist/changeling/C = user.mind.has_antag_datum(/datum/antagonist/changeling)
if(C && C.purchasedpowers)
to_chat(user, "We are ready to revive.")
- C.purchasedpowers += new /obj/effect/proc_holder/changeling/revive(null)
+ var/obj/effect/proc_holder/changeling/revive/RV = new /obj/effect/proc_holder/changeling/revive(null)
+ C.purchasedpowers += RV
+ RV.action.Grant(user)
/obj/effect/proc_holder/changeling/fakedeath/can_sting(mob/living/user)
if(user.has_trait(TRAIT_DEATHCOMA, "changeling"))
diff --git a/code/modules/antagonists/changeling/powers/fleshmend.dm b/code/modules/antagonists/changeling/powers/fleshmend.dm
index 930b756b65..9672d79ee4 100644
--- a/code/modules/antagonists/changeling/powers/fleshmend.dm
+++ b/code/modules/antagonists/changeling/powers/fleshmend.dm
@@ -5,6 +5,9 @@
chemical_cost = 20
dna_cost = 2
req_stat = UNCONSCIOUS
+ action_icon = 'icons/mob/actions/actions_changeling.dmi'
+ action_icon_state = "ling_fleshmend"
+ action_background_icon_state = "bg_ling"
//Starts healing you every second for 10 seconds.
//Can be used whilst unconscious.
diff --git a/code/modules/antagonists/changeling/powers/headcrab.dm b/code/modules/antagonists/changeling/powers/headcrab.dm
index 8a932dbd62..72d8a127ab 100644
--- a/code/modules/antagonists/changeling/powers/headcrab.dm
+++ b/code/modules/antagonists/changeling/powers/headcrab.dm
@@ -6,6 +6,9 @@
dna_cost = 1
loudness = 2
req_human = 1
+ action_icon = 'icons/mob/actions/actions_changeling.dmi'
+ action_icon_state = "ling_explode"
+ action_background_icon_state = "bg_ling"
/obj/effect/proc_holder/changeling/headcrab/sting_action(mob/user)
set waitfor = FALSE
diff --git a/code/modules/antagonists/changeling/powers/hivemind.dm b/code/modules/antagonists/changeling/powers/hivemind.dm
index 86926f51a9..b72123da6f 100644
--- a/code/modules/antagonists/changeling/powers/hivemind.dm
+++ b/code/modules/antagonists/changeling/powers/hivemind.dm
@@ -5,6 +5,16 @@
helptext = "We will be able to talk with other changelings with :g. Exchanged DNA do not count towards absorb objectives."
dna_cost = 1
chemical_cost = -1
+ action_icon = 'icons/mob/actions/actions_xeno.dmi'
+ action_icon_state = "alien_whisper"
+ action_background_icon_state = "bg_ling"
+
+/obj/effect/proc_holder/changeling/hivemind_comms/sting_action(var/mob/living/user)
+ if (user.has_trait(CHANGELING_HIVEMIND_MUTE))
+ to_chat(user, "The poison in the air hinders our ability to interact with the hivemind.")
+ return
+ var/input = stripped_input(usr, "Please choose a message to transmit.", "Changeling Hivemind", "")
+ user.say(".g[input]")
/obj/effect/proc_holder/changeling/hivemind_comms/on_purchase(mob/user, is_respec)
..()
@@ -14,12 +24,15 @@
var/obj/effect/proc_holder/changeling/hivemind_upload/S1 = new
if(!changeling.has_sting(S1))
changeling.purchasedpowers+=S1
+ S1.action.Grant(user)
var/obj/effect/proc_holder/changeling/hivemind_download/S2 = new
if(!changeling.has_sting(S2))
changeling.purchasedpowers+=S2
+ S2.action.Grant(user)
var/obj/effect/proc_holder/changeling/linglink/S3 = new
if(!changeling.has_sting(S3))
changeling.purchasedpowers+=S3
+ S3.action.Grant(user)
// HIVE MIND UPLOAD/DOWNLOAD DNA
GLOBAL_LIST_EMPTY(hivemind_bank)
@@ -29,6 +42,9 @@ GLOBAL_LIST_EMPTY(hivemind_bank)
desc = "Allows us to channel DNA in the airwaves to allow other changelings to absorb it."
chemical_cost = 10
dna_cost = -1
+ action_icon = 'icons/mob/actions/actions_changeling.dmi'
+ action_icon_state = "ling_upload"
+ action_background_icon_state = "bg_ling"
/obj/effect/proc_holder/changeling/hivemind_upload/sting_action(var/mob/living/user)
if (user.has_trait(CHANGELING_HIVEMIND_MUTE))
@@ -63,6 +79,9 @@ GLOBAL_LIST_EMPTY(hivemind_bank)
desc = "Allows us to absorb DNA that has been channeled to the airwaves. Does not count towards absorb objectives."
chemical_cost = 10
dna_cost = -1
+ action_icon = 'icons/mob/actions/actions_changeling.dmi'
+ action_icon_state = "ling_download"
+ action_background_icon_state = "bg_ling"
/obj/effect/proc_holder/changeling/hivemind_download/can_sting(mob/living/carbon/user)
if(!..())
diff --git a/code/modules/antagonists/changeling/powers/humanform.dm b/code/modules/antagonists/changeling/powers/humanform.dm
index e04f00308f..a6f7b0b6e9 100644
--- a/code/modules/antagonists/changeling/powers/humanform.dm
+++ b/code/modules/antagonists/changeling/powers/humanform.dm
@@ -3,6 +3,9 @@
desc = "We change into a human."
chemical_cost = 5
req_dna = 1
+ action_icon = 'icons/mob/actions/actions_changeling.dmi'
+ action_icon_state = "ling_human"
+ action_background_icon_state = "bg_ling"
//Transform into a human.
/obj/effect/proc_holder/changeling/humanform/sting_action(mob/living/carbon/user)
diff --git a/code/modules/antagonists/changeling/powers/lesserform.dm b/code/modules/antagonists/changeling/powers/lesserform.dm
index 24403b406c..1f9ca0b3ff 100644
--- a/code/modules/antagonists/changeling/powers/lesserform.dm
+++ b/code/modules/antagonists/changeling/powers/lesserform.dm
@@ -5,6 +5,9 @@
dna_cost = 1
loudness = 2
req_human = 1
+ action_icon = 'icons/mob/actions/actions_changeling.dmi'
+ action_icon_state = "ling_lesser"
+ action_background_icon_state = "bg_ling"
//Transform into a monkey.
/obj/effect/proc_holder/changeling/lesserform/sting_action(mob/living/carbon/human/user)
diff --git a/code/modules/antagonists/changeling/powers/linglink.dm b/code/modules/antagonists/changeling/powers/linglink.dm
index baa02ea7c8..70df78e3b4 100644
--- a/code/modules/antagonists/changeling/powers/linglink.dm
+++ b/code/modules/antagonists/changeling/powers/linglink.dm
@@ -4,6 +4,9 @@
chemical_cost = 0
dna_cost = -1
req_human = 1
+ action_icon = 'icons/mob/actions/actions_changeling.dmi'
+ action_icon_state = "ling_link"
+ action_background_icon_state = "bg_ling"
/obj/effect/proc_holder/changeling/linglink/can_sting(mob/living/carbon/user)
if(!..())
diff --git a/code/modules/antagonists/changeling/powers/mimic_voice.dm b/code/modules/antagonists/changeling/powers/mimic_voice.dm
index 6808ecd61a..170aea1e9c 100644
--- a/code/modules/antagonists/changeling/powers/mimic_voice.dm
+++ b/code/modules/antagonists/changeling/powers/mimic_voice.dm
@@ -5,6 +5,9 @@
chemical_cost = 0 //constant chemical drain hardcoded
dna_cost = 1
req_human = 1
+ action_icon = 'icons/mob/actions/actions_changeling.dmi'
+ action_icon_state = "ling_mimic_voice"
+ action_background_icon_state = "bg_ling"
// Fake Voice
diff --git a/code/modules/antagonists/changeling/powers/mutations.dm b/code/modules/antagonists/changeling/powers/mutations.dm
index 9e353a1855..3d6c766307 100644
--- a/code/modules/antagonists/changeling/powers/mutations.dm
+++ b/code/modules/antagonists/changeling/powers/mutations.dm
@@ -56,6 +56,7 @@
return W
/obj/effect/proc_holder/changeling/weapon/on_refund(mob/user)
+ action.Remove(user)
for(var/obj/item/I in user.held_items)
check_weapon(user, I)
@@ -105,6 +106,7 @@
/obj/effect/proc_holder/changeling/suit/on_refund(mob/user)
if(!ishuman(user))
return
+ action.Remove(user)
var/mob/living/carbon/human/H = user
check_suit(H)
@@ -141,6 +143,9 @@
req_human = 1
weapon_type = /obj/item/melee/arm_blade
weapon_name_simple = "blade"
+ action_icon = 'icons/mob/actions/actions_changeling.dmi'
+ action_icon_state = "ling_armblade"
+ action_background_icon_state = "bg_ling"
/obj/item/melee/arm_blade
name = "arm blade"
@@ -225,6 +230,9 @@
weapon_type = /obj/item/gun/magic/tentacle
weapon_name_simple = "tentacle"
silent = TRUE
+ action_icon = 'icons/mob/actions/actions_changeling.dmi'
+ action_icon_state = "ling_tentacle"
+ action_background_icon_state = "bg_ling"
/obj/item/gun/magic/tentacle
name = "tentacle"
@@ -401,6 +409,9 @@
dna_cost = 1
loudness = 1
req_human = 1
+ action_icon = 'icons/mob/actions/actions_changeling.dmi'
+ action_icon_state = "ling_shield"
+ action_background_icon_state = "bg_ling"
weapon_type = /obj/item/shield/changeling
weapon_name_simple = "shield"
@@ -454,6 +465,9 @@
dna_cost = 2
loudness = 1
req_human = 1
+ action_icon = 'icons/mob/actions/actions_changeling.dmi'
+ action_icon_state = "ling_space_suit"
+ action_background_icon_state = "bg_ling"
suit_type = /obj/item/clothing/suit/space/changeling
helmet_type = /obj/item/clothing/head/helmet/space/changeling
@@ -503,6 +517,9 @@
loudness = 2
req_human = 1
recharge_slowdown = 0.25
+ action_icon = 'icons/mob/actions/actions_changeling.dmi'
+ action_icon_state = "ling_armor"
+ action_background_icon_state = "bg_ling"
suit_type = /obj/item/clothing/suit/armor/changeling
helmet_type = /obj/item/clothing/head/helmet/changeling
diff --git a/code/modules/antagonists/changeling/powers/panacea.dm b/code/modules/antagonists/changeling/powers/panacea.dm
index cb5aba6c99..9f7a36f214 100644
--- a/code/modules/antagonists/changeling/powers/panacea.dm
+++ b/code/modules/antagonists/changeling/powers/panacea.dm
@@ -5,6 +5,9 @@
chemical_cost = 20
dna_cost = 1
req_stat = UNCONSCIOUS
+ action_icon = 'icons/mob/actions/actions_changeling.dmi'
+ action_icon_state = "ling_anatomic_panacea"
+ action_background_icon_state = "bg_ling"
//Heals the things that the other regenerative abilities don't.
/obj/effect/proc_holder/changeling/panacea/sting_action(mob/user)
diff --git a/code/modules/antagonists/changeling/powers/pheromone_receptors.dm b/code/modules/antagonists/changeling/powers/pheromone_receptors.dm
index c3fe3c9bc0..4995b27807 100644
--- a/code/modules/antagonists/changeling/powers/pheromone_receptors.dm
+++ b/code/modules/antagonists/changeling/powers/pheromone_receptors.dm
@@ -10,6 +10,9 @@
chemical_cost = 0 //Reduces regain rate while active.
dna_cost = 2
var/receptors_active = FALSE
+ action_icon = 'icons/mob/actions/actions_changeling.dmi'
+ action_icon_state = "ling_pheromone"
+ action_background_icon_state = "bg_ling"
/obj/effect/proc_holder/changeling/pheromone_receptors/sting_action(mob/living/carbon/user)
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
diff --git a/code/modules/antagonists/changeling/powers/regenerate.dm b/code/modules/antagonists/changeling/powers/regenerate.dm
index c86bf271d4..a1f8e1ef9b 100644
--- a/code/modules/antagonists/changeling/powers/regenerate.dm
+++ b/code/modules/antagonists/changeling/powers/regenerate.dm
@@ -8,6 +8,9 @@
chemical_cost = 10
dna_cost = 0
req_stat = UNCONSCIOUS
+ action_icon = 'icons/mob/actions/actions_changeling.dmi'
+ action_icon_state = "ling_regenerate"
+ action_background_icon_state = "bg_ling"
/obj/effect/proc_holder/changeling/regenerate/sting_action(mob/living/user)
to_chat(user, "You feel an itching, both inside and \
diff --git a/code/modules/antagonists/changeling/powers/revive.dm b/code/modules/antagonists/changeling/powers/revive.dm
index 937748a7ef..514b7603fd 100644
--- a/code/modules/antagonists/changeling/powers/revive.dm
+++ b/code/modules/antagonists/changeling/powers/revive.dm
@@ -5,6 +5,9 @@
req_stat = DEAD
always_keep = TRUE
ignores_fakedeath = TRUE
+ action_icon = 'icons/mob/actions/actions_changeling.dmi'
+ action_icon_state = "ling_revive"
+ action_background_icon_state = "bg_ling"
//Revive from revival stasis
/obj/effect/proc_holder/changeling/revive/sting_action(mob/living/carbon/user)
@@ -26,6 +29,7 @@
to_chat(user, "We have revived ourselves.")
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
changeling.purchasedpowers -= src
+ src.action.Remove(user)
return TRUE
/obj/effect/proc_holder/changeling/revive/can_be_used_by(mob/living/user)
diff --git a/code/modules/antagonists/changeling/powers/shriek.dm b/code/modules/antagonists/changeling/powers/shriek.dm
index f77624d072..65e58ae65b 100644
--- a/code/modules/antagonists/changeling/powers/shriek.dm
+++ b/code/modules/antagonists/changeling/powers/shriek.dm
@@ -6,6 +6,9 @@
dna_cost = 1
loudness = 1
req_human = 1
+ action_icon = 'icons/mob/actions/actions_changeling.dmi'
+ action_icon_state = "ling_resonant"
+ action_background_icon_state = "bg_ling"
//A flashy ability, good for crowd control and sewing chaos.
/obj/effect/proc_holder/changeling/resonant_shriek/sting_action(mob/user)
@@ -36,6 +39,9 @@
chemical_cost = 20
dna_cost = 1
loudness = 1
+ action_icon = 'icons/mob/actions/actions_changeling.dmi'
+ action_icon_state = "ling_dissonant"
+ action_background_icon_state = "bg_ling"
//A flashy ability, good for crowd control and sewing chaos.
/obj/effect/proc_holder/changeling/dissonant_shriek/sting_action(mob/user)
diff --git a/code/modules/antagonists/changeling/powers/spiders.dm b/code/modules/antagonists/changeling/powers/spiders.dm
index 2bd1bc8a35..489f646b39 100644
--- a/code/modules/antagonists/changeling/powers/spiders.dm
+++ b/code/modules/antagonists/changeling/powers/spiders.dm
@@ -6,6 +6,9 @@
dna_cost = 1
loudness = 4
req_absorbs = 3
+ action_icon = 'icons/effects/effects.dmi'
+ action_icon_state = "spiderling"
+ action_background_icon_state = "bg_ling"
//Makes some spiderlings. Good for setting traps and causing general trouble.
/obj/effect/proc_holder/changeling/spiders/sting_action(mob/user)
diff --git a/code/modules/antagonists/changeling/powers/strained_muscles.dm b/code/modules/antagonists/changeling/powers/strained_muscles.dm
index 832f6073b6..4e8b8adbd1 100644
--- a/code/modules/antagonists/changeling/powers/strained_muscles.dm
+++ b/code/modules/antagonists/changeling/powers/strained_muscles.dm
@@ -10,6 +10,9 @@
req_human = 1
var/stacks = 0 //Increments every 5 seconds; damage increases over time
active = 0 //Whether or not you are a hedgehog
+ action_icon = 'icons/obj/implants.dmi'
+ action_icon_state = "adrenal"
+ action_background_icon_state = "bg_ling"
/obj/effect/proc_holder/changeling/strained_muscles/sting_action(mob/living/carbon/user)
active = !active
diff --git a/code/modules/antagonists/changeling/powers/tiny_prick.dm b/code/modules/antagonists/changeling/powers/tiny_prick.dm
index 6c9e0c6599..b9abf76fd1 100644
--- a/code/modules/antagonists/changeling/powers/tiny_prick.dm
+++ b/code/modules/antagonists/changeling/powers/tiny_prick.dm
@@ -70,6 +70,9 @@
dna_cost = 3
loudness = 1
var/datum/changelingprofile/selected_dna = null
+ action_icon = 'icons/mob/actions/actions_changeling.dmi'
+ action_icon_state = "ling_sting_transform"
+ action_background_icon_state = "bg_ling"
/obj/effect/proc_holder/changeling/sting/transformation/Click()
var/mob/user = usr
@@ -117,6 +120,9 @@
chemical_cost = 20
dna_cost = 1
loudness = 1
+ action_icon = 'icons/mob/actions/actions_changeling.dmi'
+ action_icon_state = "ling_sting_fake"
+ action_background_icon_state = "bg_ling"
/obj/item/melee/arm_blade/false
desc = "A grotesque mass of flesh that used to be your arm. Although it looks dangerous at first, you can tell it's actually quite dull and useless."
@@ -169,6 +175,9 @@
sting_icon = "sting_extract"
chemical_cost = 25
dna_cost = 0
+ action_icon = 'icons/mob/actions/actions_changeling.dmi'
+ action_icon_state = "ling_sting_extract"
+ action_background_icon_state = "bg_ling"
/obj/effect/proc_holder/changeling/sting/extract_dna/can_sting(mob/user, mob/target)
if(..())
@@ -190,6 +199,9 @@
chemical_cost = 20
dna_cost = 2
loudness = 2
+ action_icon = 'icons/mob/actions/actions_changeling.dmi'
+ action_icon_state = "ling_sting_mute"
+ action_background_icon_state = "bg_ling"
/obj/effect/proc_holder/changeling/sting/mute/sting_action(mob/user, mob/living/carbon/target)
log_combat(user, target, "stung", "mute sting")
@@ -204,6 +216,9 @@
chemical_cost = 25
dna_cost = 1
loudness = 1
+ action_icon = 'icons/mob/actions/actions_changeling.dmi'
+ action_icon_state = "ling_sting_blind"
+ action_background_icon_state = "bg_ling"
/obj/effect/proc_holder/changeling/sting/blind/sting_action(mob/user, mob/living/carbon/target)
log_combat(user, target, "stung", "blind sting")
@@ -220,6 +235,9 @@
sting_icon = "sting_lsd"
chemical_cost = 10
dna_cost = 1
+ action_icon = 'icons/mob/actions/actions_changeling.dmi'
+ action_icon_state = "ling_sting_lsd"
+ action_background_icon_state = "bg_ling"
/obj/effect/proc_holder/changeling/sting/LSD/sting_action(mob/user, mob/living/carbon/target)
log_combat(user, target, "stung", "LSD sting")
@@ -238,6 +256,9 @@
chemical_cost = 15
dna_cost = 2
loudness = 1
+ action_icon = 'icons/mob/actions/actions_changeling.dmi'
+ action_icon_state = "ling_sting_cryo"
+ action_background_icon_state = "bg_ling"
/obj/effect/proc_holder/changeling/sting/cryo/sting_action(mob/user, mob/target)
log_combat(user, target, "stung", "cryo sting")
diff --git a/code/modules/antagonists/changeling/powers/transform.dm b/code/modules/antagonists/changeling/powers/transform.dm
index 1e2b22cdae..767c7d2621 100644
--- a/code/modules/antagonists/changeling/powers/transform.dm
+++ b/code/modules/antagonists/changeling/powers/transform.dm
@@ -5,6 +5,9 @@
dna_cost = 0
req_dna = 1
req_human = 1
+ action_icon = 'icons/mob/actions/actions_changeling.dmi'
+ action_icon_state = "ling_transform"
+ action_background_icon_state = "bg_ling"
/obj/item/clothing/glasses/changeling
name = "flesh"
diff --git a/code/modules/antagonists/cult/runes.dm b/code/modules/antagonists/cult/runes.dm
index f3975851ca..8a3f81fc01 100644
--- a/code/modules/antagonists/cult/runes.dm
+++ b/code/modules/antagonists/cult/runes.dm
@@ -112,7 +112,7 @@ structure_check() searches for nearby cultist structures required for the invoca
var/list/invokers = list() //people eligible to invoke the rune
if(user)
invokers += user
- if(req_cultists > 1 || istype(src, /obj/effect/rune/convert))
+ if(req_cultists > 1 || istype(src, /obj/effect/rune/narsie) || istype(src, /obj/effect/rune/convert))
var/list/things_in_range = range(1, src)
var/obj/item/toy/plush/narplush/plushsie = locate() in things_in_range
if(istype(plushsie) && plushsie.is_invoker)
diff --git a/code/modules/assembly/doorcontrol.dm b/code/modules/assembly/doorcontrol.dm
index aa1ee8adbc..04b4e3ed3c 100644
--- a/code/modules/assembly/doorcontrol.dm
+++ b/code/modules/assembly/doorcontrol.dm
@@ -55,7 +55,7 @@
if(specialfunctions & SHOCK)
if(D.secondsElectrified)
D.secondsElectrified = -1
- LAZYADD(D.shockedby, "\[[time_stamp()]\] [key_name(usr)]")
+ LAZYADD(D.shockedby, "\[[TIME_STAMP("hh:mm:ss", FALSE)]\] [key_name(usr)]")
log_combat(usr, D, "electrified")
else
D.secondsElectrified = 0
diff --git a/code/modules/atmospherics/gasmixtures/gas_mixture.dm b/code/modules/atmospherics/gasmixtures/gas_mixture.dm
index d07164ccd6..0304946111 100644
--- a/code/modules/atmospherics/gasmixtures/gas_mixture.dm
+++ b/code/modules/atmospherics/gasmixtures/gas_mixture.dm
@@ -29,6 +29,7 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
var/last_share = 0
var/list/reaction_results
var/list/analyzer_results //used for analyzer feedback - not initialized until its used
+ var/gc_share = FALSE // Whether to call garbage_collect() on the sharer during shares, used for immutable mixtures
/datum/gas_mixture/New(volume)
gases = new
@@ -143,9 +144,6 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
//Performs air sharing calculations between two gas_mixtures assuming only 1 boundary length
//Returns: amount of gas exchanged (+ if sharer received)
-/datum/gas_mixture/proc/after_share(datum/gas_mixture/sharer)
- //called on share's sharer to let it know it just got some gases
-
/datum/gas_mixture/proc/temperature_share(datum/gas_mixture/sharer, conduction_coefficient)
//Performs temperature sharing calculations (via conduction) between two gas_mixtures assuming only 1 boundary length
//Returns: new temperature of the sharer
@@ -343,7 +341,8 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
if(length(cached_gases ^ sharer_gases)) //if all gases were present in both mixtures, we know that no gases are 0
garbage_collect(cached_gases - sharer_gases) //any gases the sharer had, we are guaranteed to have. gases that it didn't have we are not.
sharer.garbage_collect(sharer_gases - cached_gases) //the reverse is equally true
- sharer.after_share(src, atmos_adjacent_turfs)
+ if (initial(sharer.gc_share))
+ sharer.garbage_collect()
if(temperature_delta > MINIMUM_TEMPERATURE_TO_MOVE || abs(moved_moles) > MINIMUM_MOLES_DELTA_TO_MOVE)
var/our_moles
TOTAL_MOLES(cached_gases,our_moles)
@@ -351,9 +350,6 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
TOTAL_MOLES(sharer_gases,their_moles)
return (temperature_archived*(our_moles + moved_moles) - sharer.temperature_archived*(their_moles - moved_moles)) * R_IDEAL_GAS_EQUATION / volume
-/datum/gas_mixture/after_share(datum/gas_mixture/sharer, atmos_adjacent_turfs = 4)
- return
-
/datum/gas_mixture/temperature_share(datum/gas_mixture/sharer, conduction_coefficient, sharer_temperature, sharer_heat_capacity)
//transfer of thermal energy (via conduction) between self and sharer
if(sharer)
diff --git a/code/modules/atmospherics/gasmixtures/immutable_mixtures.dm b/code/modules/atmospherics/gasmixtures/immutable_mixtures.dm
index 90b9550028..08f64b5f4a 100644
--- a/code/modules/atmospherics/gasmixtures/immutable_mixtures.dm
+++ b/code/modules/atmospherics/gasmixtures/immutable_mixtures.dm
@@ -1,73 +1,71 @@
-//"immutable" gas mixture used for immutable calculations
-//it can be changed, but any changes will ultimately be undone before they can have any effect
-
-/datum/gas_mixture/immutable
- var/initial_temperature
-
-/datum/gas_mixture/immutable/New()
- ..()
- garbage_collect()
-
-/datum/gas_mixture/immutable/garbage_collect()
- temperature = initial_temperature
- temperature_archived = initial_temperature
- gases.Cut()
-
-/datum/gas_mixture/immutable/archive()
- return 1 //nothing changes, so we do nothing and the archive is successful
-
-/datum/gas_mixture/immutable/merge()
- return 0 //we're immutable.
-
-/datum/gas_mixture/immutable/share(datum/gas_mixture/sharer, atmos_adjacent_turfs = 4)
- . = ..(sharer, 0)
- garbage_collect()
-
-/datum/gas_mixture/immutable/after_share()
- garbage_collect()
-
-/datum/gas_mixture/immutable/react()
- return 0 //we're immutable.
-
-/datum/gas_mixture/immutable/copy()
- return new type //we're immutable, so we can just return a new instance.
-
-/datum/gas_mixture/immutable/copy_from()
- return 0 //we're immutable.
-
-/datum/gas_mixture/immutable/copy_from_turf()
- return 0 //we're immutable.
-
-/datum/gas_mixture/immutable/parse_gas_string()
- return 0 //we're immutable.
-
-/datum/gas_mixture/immutable/temperature_share(datum/gas_mixture/sharer, conduction_coefficient, sharer_temperature, sharer_heat_capacity)
- . = ..()
- temperature = initial_temperature
-
-
-//used by space tiles
-/datum/gas_mixture/immutable/space
- initial_temperature = TCMB
-
-/datum/gas_mixture/immutable/space/heat_capacity()
- return HEAT_CAPACITY_VACUUM
-
-/datum/gas_mixture/immutable/space/remove()
- return copy() //we're always empty, so we can just return a copy.
-
-/datum/gas_mixture/immutable/space/remove_ratio()
- return copy() //we're always empty, so we can just return a copy.
-
-
-//used by cloners
-/datum/gas_mixture/immutable/cloner
- initial_temperature = T20C
-
-/datum/gas_mixture/immutable/cloner/garbage_collect()
- ..()
- ADD_GAS(/datum/gas/nitrogen, gases)
- gases[/datum/gas/nitrogen][MOLES] = MOLES_O2STANDARD + MOLES_N2STANDARD
-
-/datum/gas_mixture/immutable/cloner/heat_capacity()
- return (MOLES_O2STANDARD + MOLES_N2STANDARD)*20 //specific heat of nitrogen is 20
+//"immutable" gas mixture used for immutable calculations
+//it can be changed, but any changes will ultimately be undone before they can have any effect
+
+/datum/gas_mixture/immutable
+ var/initial_temperature
+ gc_share = TRUE
+
+/datum/gas_mixture/immutable/New()
+ ..()
+ garbage_collect()
+
+/datum/gas_mixture/immutable/garbage_collect()
+ temperature = initial_temperature
+ temperature_archived = initial_temperature
+ gases.Cut()
+
+/datum/gas_mixture/immutable/archive()
+ return 1 //nothing changes, so we do nothing and the archive is successful
+
+/datum/gas_mixture/immutable/merge()
+ return 0 //we're immutable.
+
+/datum/gas_mixture/immutable/share(datum/gas_mixture/sharer, atmos_adjacent_turfs = 4)
+ . = ..(sharer, 0)
+ garbage_collect()
+
+/datum/gas_mixture/immutable/react()
+ return 0 //we're immutable.
+
+/datum/gas_mixture/immutable/copy()
+ return new type //we're immutable, so we can just return a new instance.
+
+/datum/gas_mixture/immutable/copy_from()
+ return 0 //we're immutable.
+
+/datum/gas_mixture/immutable/copy_from_turf()
+ return 0 //we're immutable.
+
+/datum/gas_mixture/immutable/parse_gas_string()
+ return 0 //we're immutable.
+
+/datum/gas_mixture/immutable/temperature_share(datum/gas_mixture/sharer, conduction_coefficient, sharer_temperature, sharer_heat_capacity)
+ . = ..()
+ temperature = initial_temperature
+
+
+//used by space tiles
+/datum/gas_mixture/immutable/space
+ initial_temperature = TCMB
+
+/datum/gas_mixture/immutable/space/heat_capacity()
+ return HEAT_CAPACITY_VACUUM
+
+/datum/gas_mixture/immutable/space/remove()
+ return copy() //we're always empty, so we can just return a copy.
+
+/datum/gas_mixture/immutable/space/remove_ratio()
+ return copy() //we're always empty, so we can just return a copy.
+
+
+//used by cloners
+/datum/gas_mixture/immutable/cloner
+ initial_temperature = T20C
+
+/datum/gas_mixture/immutable/cloner/garbage_collect()
+ ..()
+ ADD_GAS(/datum/gas/nitrogen, gases)
+ gases[/datum/gas/nitrogen][MOLES] = MOLES_O2STANDARD + MOLES_N2STANDARD
+
+/datum/gas_mixture/immutable/cloner/heat_capacity()
+ return (MOLES_O2STANDARD + MOLES_N2STANDARD)*20 //specific heat of nitrogen is 20
diff --git a/code/modules/atmospherics/machinery/datum_pipeline.dm b/code/modules/atmospherics/machinery/datum_pipeline.dm
index cfaef38f6c..c4fa387ab0 100644
--- a/code/modules/atmospherics/machinery/datum_pipeline.dm
+++ b/code/modules/atmospherics/machinery/datum_pipeline.dm
@@ -221,13 +221,16 @@
if(!P)
continue
GL += P.return_air()
- for(var/obj/machinery/atmospherics/components/binary/valve/V in P.other_atmosmch)
- if(V.on)
- PL |= V.parents[1]
- PL |= V.parents[2]
- for(var/obj/machinery/atmospherics/components/unary/portables_connector/C in P.other_atmosmch)
- if(C.connected_device)
- GL += C.portableConnectorReturnAir()
+ for(var/atmosmch in P.other_atmosmch)
+ if (istype(atmosmch, /obj/machinery/atmospherics/components/binary/valve))
+ var/obj/machinery/atmospherics/components/binary/valve/V = atmosmch
+ if(V.on)
+ PL |= V.parents[1]
+ PL |= V.parents[2]
+ else if (istype(atmosmch, /obj/machinery/atmospherics/components/unary/portables_connector))
+ var/obj/machinery/atmospherics/components/unary/portables_connector/C = atmosmch
+ if(C.connected_device)
+ GL += C.portableConnectorReturnAir()
var/total_thermal_energy = 0
var/total_heat_capacity = 0
diff --git a/code/modules/awaymissions/corpse.dm b/code/modules/awaymissions/corpse.dm
index 276c488bce..7274f73965 100644
--- a/code/modules/awaymissions/corpse.dm
+++ b/code/modules/awaymissions/corpse.dm
@@ -41,6 +41,11 @@
return
if(QDELETED(src) || QDELETED(user))
return
+ if(isobserver(user))
+ var/mob/dead/observer/O = user
+ if(!O.can_reenter_round)
+ to_chat(user, "You are unable to reenter the round.")
+ return
var/ghost_role = alert(latejoinercalling ? "Latejoin as [mob_name]? (This is a ghost role, and as such, it's very likely to be off-station.)" : "Become [mob_name]? (Warning, You can no longer be cloned!)",,"Yes","No")
if(ghost_role == "No" || !loc)
return
diff --git a/code/modules/cargo/packs.dm b/code/modules/cargo/packs.dm
index a9972ca2d4..d76edf2e10 100644
--- a/code/modules/cargo/packs.dm
+++ b/code/modules/cargo/packs.dm
@@ -512,6 +512,14 @@
/obj/item/storage/belt/bandolier)
crate_name = "combat shotguns crate"
+/datum/supply_pack/security/armory/dragnetgun
+ name = "DRAGnet gun Crate"
+ desc = "Contains two DRAGnet gun. A Dynamic Rapid-Apprehension of the Guilty net the revolution in law enforcement technology that YOU Want! Requires Armory access to open."
+ cost = 3500
+ contains = list(/obj/item/gun/energy/e_gun/dragnet,
+ /obj/item/gun/energy/e_gun/dragnet)
+ crate_name = "anti riot net guns crate"
+
/datum/supply_pack/security/armory/energy
name = "Energy Guns Crate"
desc = "Contains three Energy Guns, capable of firing both nonlethal and lethal blasts of light. Requires Armory access to open."
@@ -545,7 +553,7 @@
/datum/supply_pack/security/armory/fire
name = "Incendiary Weapons Crate"
- desc = "Burn, baby burn. Contains three incendiary grenades, three plasma canisters, and a flamethrower. Requires Armory access to open."
+ desc = "Burn, baby burn. Contains three incendiary grenades, three plasma canisters, and a flamethrower. Requires Brige access to open."
cost = 1500
access = ACCESS_HEADS
contains = list(/obj/item/flamethrower/full,
@@ -559,6 +567,16 @@
crate_type = /obj/structure/closet/crate/secure/plasma
dangerous = TRUE
+/datum/supply_pack/security/armory/miniguns
+ name = "Personal Miniature Energy Guns"
+ desc = "Contains three miniature energy guns. Each gun has a disabler and a lethal option. Requires Armory access to open."
+ cost = 5000
+ contains = list(/obj/item/gun/energy/e_gun/mini,
+ /obj/item/gun/energy/e_gun/mini,
+ /obj/item/gun/energy/e_gun/mini)
+ crate_name = "personal energy guns crate"
+ crate_type = /obj/structure/closet/crate/secure/plasma
+
/datum/supply_pack/security/armory/laserarmor
name = "Reflector Vest Crate"
desc = "Contains two vests of highly reflective material. Each armor piece diffuses a laser's energy by over half, as well as offering a good chance to reflect the laser entirely. Requires Armory access to open."
@@ -620,6 +638,13 @@
/obj/item/clothing/gloves/combat)
crate_name = "swat crate"
+/datum/supply_pack/security/armory/swattasers //Lesser AEG tbh
+ name = "SWAT tatical tasers Crate"
+ desc = "Contains two tactical energy gun, these guns are able to tase, disable and lethal as well as hold a seclight. Requires Armory access to open."
+ cost = 8000
+ contains = list(/obj/item/gun/energy/e_gun/stun,
+ /obj/item/gun/energy/e_gun/stun)
+ crate_name = "swat taser crate"
/datum/supply_pack/security/armory/wt550
name = "WT-550 Semi-Auto Rifle Crate"
diff --git a/code/modules/client/verbs/looc.dm b/code/modules/client/verbs/looc.dm
index ccfcdae405..4049034203 100644
--- a/code/modules/client/verbs/looc.dm
+++ b/code/modules/client/verbs/looc.dm
@@ -24,11 +24,8 @@
return
if(!holder)
- if(!GLOB.ooc_allowed)
- to_chat(src, " OOC is globally muted")
- return
- if(!GLOB.dooc_allowed && (mob.stat == DEAD))
- to_chat(usr, " OOC for dead mobs has been turned off.")
+ if(!GLOB.looc_allowed)
+ to_chat(src, " LOOC is globally muted")
return
if(prefs.muted & MUTE_OOC)
to_chat(src, " You cannot use OOC (muted).")
diff --git a/code/modules/client/verbs/ooc.dm b/code/modules/client/verbs/ooc.dm
index 1d54b9f4a0..5df13a1ffe 100644
--- a/code/modules/client/verbs/ooc.dm
+++ b/code/modules/client/verbs/ooc.dm
@@ -80,6 +80,16 @@
GLOB.ooc_allowed = !GLOB.ooc_allowed
to_chat(world, "The OOC channel has been globally [GLOB.ooc_allowed ? "enabled" : "disabled"].")
+/proc/toggle_looc(toggle = null)
+ if(toggle != null)
+ if(toggle != GLOB.looc_allowed)
+ GLOB.looc_allowed = toggle
+ else
+ return
+ else
+ GLOB.looc_allowed = !GLOB.looc_allowed
+
+
/proc/toggle_dooc(toggle = null)
if(toggle != null)
if(toggle != GLOB.dooc_allowed)
diff --git a/code/modules/client/verbs/suicide.dm b/code/modules/client/verbs/suicide.dm
index 64d58cd40b..2e643cc05d 100644
--- a/code/modules/client/verbs/suicide.dm
+++ b/code/modules/client/verbs/suicide.dm
@@ -198,11 +198,13 @@
death(0)
-/mob/living/proc/suicide_log()
- log_game("[key_name(src)] committed suicide at [AREACOORD(src)] as [src.type].")
+/mob/living/proc/suicide_log(ghosting)
+ log_game("[key_name(src)] [ghosting ? "ghosted" : "committed suicide"] at [AREACOORD(src)] as [src.type].")
+ message_admins("[key_name(src)] [ghosting ? "ghosted" : "committed suicide"] at [AREACOORD(src)].")
-/mob/living/carbon/human/suicide_log()
- log_game("[key_name(src)] (job: [src.job ? "[src.job]" : "None"]) committed suicide at [AREACOORD(src)].")
+/mob/living/carbon/human/suicide_log(ghosting)
+ log_game("[key_name(src)] (job: [src.job ? "[src.job]" : "None"]) [is_special_character(src) ? "(ANTAG!) " : ""][ghosting ? "ghosted" : "committed suicide"] at [AREACOORD(src)].")
+ message_admins("[key_name(src)] (job: [src.job ? "[src.job]" : "None"]) [is_special_character(src) ? "(ANTAG!) " : ""][ghosting ? "ghosted" : "committed suicide"] at [AREACOORD(src)].")
/mob/living/proc/canSuicide()
switch(stat)
diff --git a/code/modules/clothing/spacesuits/hardsuit.dm b/code/modules/clothing/spacesuits/hardsuit.dm
index 974936ba30..c061d756b7 100644
--- a/code/modules/clothing/spacesuits/hardsuit.dm
+++ b/code/modules/clothing/spacesuits/hardsuit.dm
@@ -712,7 +712,10 @@
/obj/item/clothing/suit/space/hardsuit/shielded/worn_overlays(isinhands)
. = list()
if(!isinhands)
- . += mutable_appearance('icons/effects/effects.dmi', shield_state, MOB_LAYER + 0.01)
+ if(taurmode >= SNEK_TAURIC)
+ . += mutable_appearance('modular_citadel/icons/mob/64x32_effects.dmi', shield_state, MOB_LAYER + 0.01)
+ else
+ . += mutable_appearance('icons/effects/effects.dmi', shield_state, MOB_LAYER + 0.01)
/obj/item/clothing/head/helmet/space/hardsuit/shielded
resistance_flags = FIRE_PROOF | ACID_PROOF
diff --git a/code/modules/clothing/suits/_suits.dm b/code/modules/clothing/suits/_suits.dm
index ad0311f160..054154639c 100644
--- a/code/modules/clothing/suits/_suits.dm
+++ b/code/modules/clothing/suits/_suits.dm
@@ -36,7 +36,7 @@
if(tauric == TRUE)
center = TRUE
dimension_x = 64
- else if(H.dna.features["taur"] in list("Fox", "Wolf", "Otie", "Drake", "Lab", "Shepherd", "Husky", "Eevee", "Panther", "Tajaran", "Horse", "Cow"))
+ else if(H.dna.features["taur"] in list("Fox","Wolf","Otie","Drake","Lab","Shepherd","Husky","Eevee","Panther","Horse","Cow","Tiger"))
taurmode = PAW_TAURIC
if(tauric == TRUE)
center = TRUE
@@ -61,7 +61,10 @@
if(damaged_clothes)
. += mutable_appearance('icons/effects/item_damage.dmi', "damaged[blood_overlay_type]")
IF_HAS_BLOOD_DNA(src)
- . += mutable_appearance('icons/effects/blood.dmi', "[blood_overlay_type]blood")
+ if(taurmode >= SNEK_TAURIC)
+ . += mutable_appearance('modular_citadel/icons/mob/64x32_effects.dmi', "[blood_overlay_type]blood")
+ else
+ . += mutable_appearance('icons/effects/blood.dmi', "[blood_overlay_type]blood")
var/mob/living/carbon/human/M = loc
if(ishuman(M) && M.w_uniform)
var/obj/item/clothing/under/U = M.w_uniform
diff --git a/code/modules/detectivework/scanner.dm b/code/modules/detectivework/scanner.dm
index ca746e74fd..fd7591db81 100644
--- a/code/modules/detectivework/scanner.dm
+++ b/code/modules/detectivework/scanner.dm
@@ -116,7 +116,7 @@
// We gathered everything. Create a fork and slowly display the results to the holder of the scanner.
var/found_something = 0
- add_log("[station_time_timestamp()][get_timestamp()] - [target_name]", 0)
+ add_log("[STATION_TIME_TIMESTAMP("hh:mm:ss")][get_timestamp()] - [target_name]", 0)
// Fingerprints
if(length(fingerprints))
diff --git a/code/modules/error_handler/error_handler.dm b/code/modules/error_handler/error_handler.dm
index 1c26a09c30..8b410130cb 100644
--- a/code/modules/error_handler/error_handler.dm
+++ b/code/modules/error_handler/error_handler.dm
@@ -76,7 +76,7 @@ GLOBAL_VAR_INIT(total_runtimes_skipped, 0)
var/skipcount = abs(error_cooldown[erroruid]) - 1
error_cooldown[erroruid] = 0
if(skipcount > 0)
- SEND_TEXT(world.log, "\[[time_stamp()]] Skipped [skipcount] runtimes in [E.file],[E.line].")
+ SEND_TEXT(world.log, "\[[TIME_STAMP("hh:mm:ss", FALSE)]] Skipped [skipcount] runtimes in [E.file],[E.line].")
GLOB.error_cache.log_error(E, skip_count = skipcount)
error_last_seen[erroruid] = world.time
@@ -113,7 +113,7 @@ GLOBAL_VAR_INIT(total_runtimes_skipped, 0)
if(GLOB.error_cache)
GLOB.error_cache.log_error(E, desclines)
- var/main_line = "\[[time_stamp()]] Runtime in [E.file],[E.line]: [E]"
+ var/main_line = "\[[TIME_STAMP("hh:mm:ss", FALSE)]] Runtime in [E.file],[E.line]: [E]"
SEND_TEXT(world.log, main_line)
for(var/line in desclines)
SEND_TEXT(world.log, line)
diff --git a/code/modules/error_handler/error_viewer.dm b/code/modules/error_handler/error_viewer.dm
index adcbb8fb57..9ac65d26fb 100644
--- a/code/modules/error_handler/error_viewer.dm
+++ b/code/modules/error_handler/error_viewer.dm
@@ -131,10 +131,10 @@ GLOBAL_DATUM(error_cache, /datum/error_viewer/error_cache)
/datum/error_viewer/error_source/New(exception/e)
if (!istype(e))
- name = "\[[time_stamp()]] Uncaught exceptions"
+ name = "\[[TIME_STAMP("hh:mm:ss", FALSE)]] Uncaught exceptions"
return
- name = "\[[time_stamp()]] Runtime in [e.file], line [e.line]: [html_encode(e.name)]"
+ name = "\[[TIME_STAMP("hh:mm:ss", FALSE)]] Runtime in [e.file], line [e.line]: [html_encode(e.name)]"
/datum/error_viewer/error_source/show_to(user, datum/error_viewer/back_to, linear)
if (!istype(back_to))
@@ -156,15 +156,15 @@ GLOBAL_DATUM(error_cache, /datum/error_viewer/error_cache)
/datum/error_viewer/error_entry/New(exception/e, list/desclines, skip_count)
if (!istype(e))
- name = "\[[time_stamp()]] Uncaught exception: [html_encode(e.name)]"
+ name = "\[[TIME_STAMP("hh:mm:ss", FALSE)]] Uncaught exception: [html_encode(e.name)]"
return
if(skip_count)
- name = "\[[time_stamp()]] Skipped [skip_count] runtimes in [e.file],[e.line]."
+ name = "\[[TIME_STAMP("hh:mm:ss", FALSE)]] Skipped [skip_count] runtimes in [e.file],[e.line]."
is_skip_count = TRUE
return
- name = "\[[time_stamp()]] Runtime in [e.file], line [e.line]: [html_encode(e.name)]"
+ name = "\[[TIME_STAMP("hh:mm:ss", FALSE)]] Runtime in [e.file], line [e.line]: [html_encode(e.name)]"
exc = e
if (istype(desclines))
for (var/line in desclines)
diff --git a/code/modules/events/shuttle_loan.dm b/code/modules/events/shuttle_loan.dm
index cec897a61e..769e32275b 100644
--- a/code/modules/events/shuttle_loan.dm
+++ b/code/modules/events/shuttle_loan.dm
@@ -6,7 +6,7 @@
#define PIZZA_DELIVERY 6
#define ITS_HIP_TO 7
#define MY_GOD_JC 8
-
+#define DELTA_CRATES 9
/datum/round_event_control/shuttle_loan
name = "Shuttle Loan"
@@ -118,6 +118,18 @@
P.info = "Cargo: We have discovered an active Syndicate bomb near our VIP shuttle's fuel lines. If you feel up to the task, we will pay you for defusing it."
P.update_icon()
bonus_points = 45000 //If you mess up, people die and the shuttle gets turned into swiss cheese
+ if(DELTA_CRATES)
+ if(prob(50))
+ priority_announce("Cargo: We have discovered a warehouse of DELTA locked crates, we cant store any more of them at CC can you take them for us?.", "CentCom Security Division")
+ else
+ priority_announce("A report has been downloaded and printed out at all communications consoles.", "Incoming Classified Message", 'sound/ai/commandreport.ogg') // CITADEL EDIT metabreak
+ for(var/obj/machinery/computer/communications/C in GLOB.machines)
+ if(!(C.stat & (BROKEN|NOPOWER)) && is_station_level(C.z))
+ var/obj/item/paper/P = new(C.loc)
+ P.name = "Cargo Report"
+ P.info = "Cargo: We have discovered a warehouse of DELTA locked crates, we cant store any more of them at CC can you take them for us?."
+ P.update_icon()
+ bonus_points = 25000 //If you mess up, people die and the shuttle gets turned into swiss cheese
/datum/round_event/shuttle_loan/proc/loan_shuttle()
priority_announce(thanks_msg, "Cargo shuttle commandeered by CentCom.")
@@ -147,6 +159,8 @@
SSshuttle.centcom_message += "Biohazard cleanup incoming."
if(MY_GOD_JC)
SSshuttle.centcom_message += "Live explosive ordnance incoming. Exercise extreme caution."
+ if(DELTA_CRATES)
+ SSshuttle.centcom_message += "DELTA Locked crates incoming. Exercise extreme caution."
/datum/round_event/shuttle_loan/tick()
if(dispatched)
@@ -291,6 +305,15 @@
else
shuttle_spawns.Add(/obj/item/paper/fluff/cargo/bomb/allyourbase)
+ if(DELTA_CRATES) //Delta crates can stack on eacher, and are basicly a 1/3/5 bombs
+ for(var/i in 1 to 7) //7 seems fair
+ shuttle_spawns.Add(/obj/structure/closet/crate/secure/loot)
+
+ for(var/i in 1 to 5)
+ var/turf/T = pick_n_take(empty_shuttle_turfs)
+ new /obj/structure/spider/stickyweb(T)
+ new /obj/effect/decal/cleanable/ash(T)
+
var/false_positive = 0
while(shuttle_spawns.len && empty_shuttle_turfs.len)
var/turf/T = pick_n_take(empty_shuttle_turfs)
@@ -334,3 +357,4 @@
#undef PIZZA_DELIVERY
#undef ITS_HIP_TO
#undef MY_GOD_JC
+#undef DELTA_CRATES
diff --git a/code/modules/events/vent_clog.dm b/code/modules/events/vent_clog.dm
index 3945a12a40..86ede7f255 100644
--- a/code/modules/events/vent_clog.dm
+++ b/code/modules/events/vent_clog.dm
@@ -75,7 +75,7 @@
reagentsAmount = 250
/datum/round_event_control/vent_clog/beer
- name = "Foamy beer stationwide"
+ name = "Clogged Vents: Beer"
typepath = /datum/round_event/vent_clog/beer
max_occurrences = 0
@@ -83,12 +83,12 @@
reagentsAmount = 100
/datum/round_event_control/vent_clog/plasma_decon
- name = "Plasma decontamination"
+ name = "Anti-Plasma Flood"
typepath = /datum/round_event/vent_clog/plasma_decon
max_occurrences = 0
/datum/round_event_control/vent_clog/female
- name = "FemCum stationwide"
+ name = "Clogged Vents; Girlcum"
typepath = /datum/round_event/vent_clog/female
max_occurrences = 0
@@ -96,7 +96,7 @@
reagentsAmount = 100
/datum/round_event_control/vent_clog/male
- name = "Semen stationwide"
+ name = "Clogged Vents: Semen"
typepath = /datum/round_event/vent_clog/male
max_occurrences = 0
@@ -118,6 +118,9 @@
foam.start()
CHECK_TICK
+/datum/round_event/vent_clog/male/announce()
+ priority_announce("The scrubbers network is experiencing a backpressure surge. Some ejaculation of contents may occur.", "Atmospherics alert")
+
/datum/round_event/vent_clog/male/start()
for(var/obj/machinery/atmospherics/components/unary/vent in vents)
if(vent && vent.loc)
@@ -130,6 +133,9 @@
foam.start()
CHECK_TICK
+/datum/round_event/vent_clog/female/announce()
+ priority_announce("The scrubbers network is experiencing a backpressure squirt. Some ejection of contents may occur.", "Atmospherics alert")
+
/datum/round_event/vent_clog/female/start()
for(var/obj/machinery/atmospherics/components/unary/vent in vents)
if(vent && vent.loc)
diff --git a/code/modules/food_and_drinks/food/snacks_meat.dm b/code/modules/food_and_drinks/food/snacks_meat.dm
index c77242e69d..d3466caa5e 100644
--- a/code/modules/food_and_drinks/food/snacks_meat.dm
+++ b/code/modules/food_and_drinks/food/snacks_meat.dm
@@ -184,6 +184,15 @@
tastes = list("the jungle" = 1, "bananas" = 1)
foodtype = MEAT | SUGAR
+/obj/item/reagent_containers/food/snacks/monkeycube/On_Consume(mob/living/carbon/M)
+ if(iscarbon(M))
+ M.visible_message("[src] bursts out of [M]!")
+ M.emote("scream")
+ M.Knockdown(40)
+ M.adjustBruteLoss(60)
+ Expand()
+ return ..()
+
/obj/item/reagent_containers/food/snacks/monkeycube/proc/Expand()
var/mob/spammer = get_mob_by_key(fingerprintslast)
var/mob/living/carbon/monkey/bananas = new(drop_location(), TRUE, spammer)
diff --git a/code/modules/food_and_drinks/food/snacks_pizza.dm b/code/modules/food_and_drinks/food/snacks_pizza.dm
index 5bf5abacb0..403636db08 100644
--- a/code/modules/food_and_drinks/food/snacks_pizza.dm
+++ b/code/modules/food_and_drinks/food/snacks_pizza.dm
@@ -160,6 +160,15 @@
tastes = list("crust" = 1, "tomato" = 1, "cheese" = 1, "pineapple" = 6, "ham" = 2)
foodtype = PINEAPPLE //Over powering tast of gods fruit
+/obj/item/reagent_containers/food/snacks/pizza/pineapple/anomaly
+ desc = "A anomaly made pizza with pineapple..."
+ bonus_reagents = list("nutriment" = 16, "vitamin" = 16)
+ tastes = list("crust" = 1, "tomato" = 1, "cheese" = 1, "pineapple" = 6, "ham" = 2, "good" = 10)
+
+/obj/item/reagent_containers/food/snacks/pizzaslice/pineapple/anomaly
+ desc = "A slice of good tasting pizza. But has pineapple on it, what a anomaly!"
+ tastes = list("crust" = 1, "tomato" = 1, "cheese" = 1, "pineapple" = 6, "ham" = 2, "good" = 10)
+
/obj/item/reagent_containers/food/snacks/pizza/arnold
name = "\improper Arnold pizza"
desc = "Hello, you've reached Arnold's pizza shop. I'm not here now, I'm out killing pepperoni."
diff --git a/code/modules/food_and_drinks/recipes/drinks_recipes.dm b/code/modules/food_and_drinks/recipes/drinks_recipes.dm
index 02106e808c..f41f6cce80 100644
--- a/code/modules/food_and_drinks/recipes/drinks_recipes.dm
+++ b/code/modules/food_and_drinks/recipes/drinks_recipes.dm
@@ -692,3 +692,15 @@
id = "pwr_game"
results = list("pwr_game" = 5)
required_reagents = list("sodawater" = 1, "blackcrayonpowder" = 1, "sodiumchloride" = 1)
+
+/datum/chemical_reaction/pinkmilk
+ name = "Strawberry Milk"
+ id = "pinkmilk"
+ results = list("pinkmilk" = 5)
+ required_reagents = list("aphro+" = 1, "milk" = 1)
+
+/datum/chemical_reaction/pinktea
+ name = "Strawberry Tea"
+ id = "pinktea"
+ results = list("pinktea" = 5)
+ required_reagents = list("aphro" = 1, "arnold_palmer" = 1, "sugar" = 1)
diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_pizza.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_pizza.dm
index 70f4b50727..64447dd180 100644
--- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_pizza.dm
+++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_pizza.dm
@@ -101,3 +101,22 @@
)
result = /obj/item/reagent_containers/food/snacks/pizza/pineapple
subcategory = CAT_PIZZA
+
+/datum/crafting_recipe/food/pineapplepizza/anomaly
+ name = "Anomaly Hawaiian pizza"
+ reqs = list(
+ /obj/item/assembly/signaler/anomaly = 1,
+ /obj/item/reagent_containers/food/snacks/pizza/pineapple = 1
+ )
+ result = /obj/item/reagent_containers/food/snacks/pizza/pineapple/anomaly
+ subcategory = CAT_PIZZA
+
+/datum/crafting_recipe/food/anomalypizzaboy
+ name = "Anomaly pizza box"
+ reqs = list(
+ /obj/item/pizzabox = 5,
+ /obj/item/assembly/signaler/anomaly = 1
+ )
+ tools = list(TOOL_WELDER, TOOL_SCREWDRIVER, TOOL_WIRECUTTER)
+ result = /obj/item/pizzabox/infinite
+ subcategory = CAT_PIZZA
diff --git a/code/modules/goonchat/browserassets/js/browserOutput.js b/code/modules/goonchat/browserassets/js/browserOutput.js
index 15265b2a93..23a63d9708 100644
--- a/code/modules/goonchat/browserassets/js/browserOutput.js
+++ b/code/modules/goonchat/browserassets/js/browserOutput.js
@@ -245,96 +245,41 @@ function output(message, flag) {
message = byondDecode(message).trim();
- //The behemoth of filter-code (for Admin message filters)
- //Note: This is proooobably hella inefficient
- var filteredOut = false;
- if (opts.hasOwnProperty('showMessagesFilters') && !opts.showMessagesFilters['All'].show) {
- //Get this filter type (defined by class on message)
- var messageHtml = $.parseHTML(message),
- messageClasses;
- if (opts.hasOwnProperty('filterHideAll') && opts.filterHideAll) {
- var internal = false;
- messageClasses = (!!$(messageHtml).attr('class') ? $(messageHtml).attr('class').split(/\s+/) : false);
- if (messageClasses) {
- for (var i = 0; i < messageClasses.length; i++) { //Every class
- if (messageClasses[i] == 'internal') {
- internal = true;
- break;
- }
- }
- }
- if (!internal) {
- filteredOut = 'All';
- }
- } else {
- //If the element or it's child have any classes
- if (!!$(messageHtml).attr('class') || !!$(messageHtml).children().attr('class')) {
- messageClasses = $(messageHtml).attr('class').split(/\s+/);
- if (!!$(messageHtml).children().attr('class')) {
- messageClasses = messageClasses.concat($(messageHtml).children().attr('class').split(/\s+/));
- }
- var tempCount = 0;
- for (var i = 0; i < messageClasses.length; i++) { //Every class
- var thisClass = messageClasses[i];
- $.each(opts.showMessagesFilters, function(key, val) { //Every filter
- if (key !== 'All' && val.show === false && typeof val.match != 'undefined') {
- for (var i = 0; i < val.match.length; i++) {
- var matchClass = val.match[i];
- if (matchClass == thisClass) {
- filteredOut = key;
- break;
- }
- }
- }
- if (filteredOut) return false;
- });
- if (filteredOut) break;
- tempCount++;
- }
- } else {
- if (!opts.showMessagesFilters['Misc'].show) {
- filteredOut = 'Misc';
- }
- }
- }
- }
-
//Stuff we do along with appending a message
var atBottom = false;
- if (!filteredOut) {
- var bodyHeight = $('body').height();
- var messagesHeight = $messages.outerHeight();
- var scrollPos = $('body,html').scrollTop();
+ var bodyHeight = $('body').height();
+ var messagesHeight = $messages.outerHeight();
+ var scrollPos = $('body,html').scrollTop();
- //Should we snap the output to the bottom?
- if (bodyHeight + scrollPos >= messagesHeight - opts.scrollSnapTolerance) {
- atBottom = true;
- if ($('#newMessages').length) {
- $('#newMessages').remove();
+ //Should we snap the output to the bottom?
+ if (bodyHeight + scrollPos >= messagesHeight - opts.scrollSnapTolerance) {
+ atBottom = true;
+ if ($('#newMessages').length) {
+ $('#newMessages').remove();
+ }
+ //If not, put the new messages box in
+ } else {
+ if ($('#newMessages').length) {
+ var messages = $('#newMessages .number').text();
+ messages = parseInt(messages);
+ messages++;
+ $('#newMessages .number').text(messages);
+ if (messages == 2) {
+ $('#newMessages .messageWord').append('s');
}
- //If not, put the new messages box in
} else {
- if ($('#newMessages').length) {
- var messages = $('#newMessages .number').text();
- messages = parseInt(messages);
- messages++;
- $('#newMessages .number').text(messages);
- if (messages == 2) {
- $('#newMessages .messageWord').append('s');
- }
- } else {
- $messages.after('1 new message ');
- }
+ $messages.after('1 new message ');
}
}
+
opts.messageCount++;
//Pop the top message off if history limit reached
- if (opts.messageCount >= opts.messageLimit) {
- $messages.children('div.entry:first-child').remove();
- opts.messageCount--; //I guess the count should only ever equal the limit
- }
+ //if (opts.messageCount >= opts.messageLimit) {
+ //$messages.children('div.entry:first-child').remove();
+ //opts.messageCount--; //I guess the count should only ever equal the limit
+ //}
// Create the element - if combining is off, we use it, and if it's on, we
// might discard it bug need to check its text content. Some messages vary
@@ -372,11 +317,6 @@ function output(message, flag) {
//Actually append the message
entry.className = 'entry';
- if (filteredOut) {
- entry.className += ' hidden';
- entry.setAttribute('data-filter', filteredOut);
- }
-
$last_message = trimmed_message;
$messages[0].appendChild(entry);
$(entry).find("img.icon").error(iconError);
@@ -401,7 +341,7 @@ function output(message, flag) {
}
}
- if (!filteredOut && atBottom) {
+ if (atBottom) {
$('body,html').scrollTop($messages.outerHeight());
}
}
diff --git a/code/modules/integrated_electronics/_defines.dm b/code/modules/integrated_electronics/_defines.dm
new file mode 100644
index 0000000000..a2f4aa190a
--- /dev/null
+++ b/code/modules/integrated_electronics/_defines.dm
@@ -0,0 +1,5 @@
+#define IC_TOPIC_UNHANDLED 0
+#define IC_TOPIC_HANDLED 1
+#define IC_TOPIC_REFRESH 2
+#define IC_FLAG_ANCHORABLE 1
+#define IC_FLAG_CAN_FIRE 2
\ No newline at end of file
diff --git a/code/modules/integrated_electronics/subtypes/atmospherics.dm b/code/modules/integrated_electronics/subtypes/atmospherics.dm
new file mode 100644
index 0000000000..1715223fa2
--- /dev/null
+++ b/code/modules/integrated_electronics/subtypes/atmospherics.dm
@@ -0,0 +1,762 @@
+#define SOURCE_TO_TARGET 0
+#define TARGET_TO_SOURCE 1
+#define PUMP_EFFICIENCY 0.6
+#define TANK_FAILURE_PRESSURE (ONE_ATMOSPHERE*25)
+#define PUMP_MAX_PRESSURE (ONE_ATMOSPHERE*24)
+#define PUMP_MAX_VOLUME 100
+
+
+/obj/item/integrated_circuit/atmospherics
+ category_text = "Atmospherics"
+ cooldown_per_use = 2 SECONDS
+ complexity = 10
+ size = 7
+ outputs = list(
+ "self reference" = IC_PINTYPE_SELFREF,
+ "pressure" = IC_PINTYPE_NUMBER
+ )
+ var/datum/gas_mixture/air_contents
+ var/volume = 2 //Pretty small, I know
+
+/obj/item/integrated_circuit/atmospherics/Initialize()
+ air_contents = new(volume)
+ ..()
+
+/obj/item/integrated_circuit/atmospherics/return_air()
+ return air_contents
+
+//Check if the gas container is adjacent and of the right type
+/obj/item/integrated_circuit/atmospherics/proc/check_gassource(atom/gasholder)
+ if(!gasholder)
+ return FALSE
+ if(!gasholder.Adjacent(get_object()))
+ return FALSE
+ if(!istype(gasholder, /obj/item/tank) && !istype(gasholder, /obj/machinery/portable_atmospherics) && !istype(gasholder, /obj/item/integrated_circuit/atmospherics))
+ return FALSE
+ return TRUE
+
+//Needed in circuits where source and target types differ
+/obj/item/integrated_circuit/atmospherics/proc/check_gastarget(atom/gasholder)
+ return check_gassource(gasholder)
+
+
+// - gas pump - // **works**
+/obj/item/integrated_circuit/atmospherics/pump
+ name = "gas pump"
+ desc = "Somehow moves gases between two tanks, canisters, and other gas containers."
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ inputs = list(
+ "source" = IC_PINTYPE_REF,
+ "target" = IC_PINTYPE_REF,
+ "target pressure" = IC_PINTYPE_NUMBER
+ )
+ activators = list(
+ "transfer" = IC_PINTYPE_PULSE_IN,
+ "on transfer" = IC_PINTYPE_PULSE_OUT
+ )
+ var/direction = SOURCE_TO_TARGET
+ var/target_pressure = PUMP_MAX_PRESSURE
+ power_draw_per_use = 20
+
+/obj/item/integrated_circuit/atmospherics/pump/Initialize()
+ air_contents = new(volume)
+ extended_desc += " Use negative pressure to move air from target to source. \
+ Note that only part of the gas is moved on each transfer, \
+ so multiple activations will be necessary to achieve target pressure. \
+ The pressure limit for circuit pumps is [round(PUMP_MAX_PRESSURE)] kPa."
+ . = ..()
+
+// This proc gets the direction of the gas flow depending on its value, by calling update target
+/obj/item/integrated_circuit/atmospherics/pump/on_data_written()
+ var/amt = get_pin_data(IC_INPUT, 3)
+ update_target(amt)
+
+/obj/item/integrated_circuit/atmospherics/pump/proc/update_target(new_amount)
+ if(!isnum(new_amount))
+ new_amount = 0
+ // See in which direction the gas moves
+ if(new_amount < 0)
+ direction = TARGET_TO_SOURCE
+ else
+ direction = SOURCE_TO_TARGET
+ target_pressure = min(round(PUMP_MAX_PRESSURE),abs(new_amount))
+
+/obj/item/integrated_circuit/atmospherics/pump/do_work()
+ var/obj/source = get_pin_data_as_type(IC_INPUT, 1, /obj)
+ var/obj/target = get_pin_data_as_type(IC_INPUT, 2, /obj)
+ perform_magic(source, target)
+ activate_pin(2)
+
+/obj/item/integrated_circuit/atmospherics/pump/proc/perform_magic(atom/source, atom/target)
+ //Check if both atoms are of the right type: atmos circuits/gas tanks/canisters. If one is the same, use the circuit var
+ if(!check_gassource(source))
+ source = src
+
+ if(!check_gastarget(target))
+ target = src
+
+ // If both are the same, this whole proc would do nothing and just waste performance
+ if(source == target)
+ return
+
+ var/datum/gas_mixture/source_air = source.return_air()
+ var/datum/gas_mixture/target_air = target.return_air()
+
+ if(!source_air || !target_air)
+ return
+
+ // Swapping both source and target
+ if(direction == TARGET_TO_SOURCE)
+ var/temp = source_air
+ source_air = target_air
+ target_air = temp
+
+ // If what you are pumping is empty, use the circuit's storage
+ if(source_air.total_moles() <= 0)
+ source_air = air_contents
+
+ // Move gas from one place to another
+ move_gas(source_air, target_air)
+ air_update_turf()
+
+/obj/item/integrated_circuit/atmospherics/pump/proc/move_gas(datum/gas_mixture/source_air, datum/gas_mixture/target_air)
+
+ // No moles = nothing to pump
+ if(source_air.total_moles() <= 0 || target_air.return_pressure() >= PUMP_MAX_PRESSURE)
+ return
+
+ // Negative Kelvin temperatures should never happen and if they do, normalize them
+ if(source_air.temperature < TCMB)
+ source_air.temperature = TCMB
+
+ var/pressure_delta = target_pressure - target_air.return_pressure()
+ if(pressure_delta > 0.1)
+ var/transfer_moles = (pressure_delta*target_air.volume/(source_air.temperature * R_IDEAL_GAS_EQUATION))*PUMP_EFFICIENCY
+ var/datum/gas_mixture/removed = source_air.remove(transfer_moles)
+ target_air.merge(removed)
+
+
+// - volume pump - // **Works**
+/obj/item/integrated_circuit/atmospherics/pump/volume
+ name = "volume pump"
+ desc = "Moves gases between two tanks, canisters, and other gas containers by using their volume, up to 200 L/s."
+ extended_desc = " Use negative volume to move air from target to source. Note that only part of the gas is moved on each transfer. Its maximum pumping volume is capped at 1000kPa."
+
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ inputs = list(
+ "source" = IC_PINTYPE_REF,
+ "target" = IC_PINTYPE_REF,
+ "transfer volume" = IC_PINTYPE_NUMBER
+ )
+ activators = list(
+ "transfer" = IC_PINTYPE_PULSE_IN,
+ "on transfer" = IC_PINTYPE_PULSE_OUT
+ )
+ direction = SOURCE_TO_TARGET
+ var/transfer_rate = PUMP_MAX_VOLUME
+ power_draw_per_use = 20
+
+/obj/item/integrated_circuit/atmospherics/pump/volume/update_target(new_amount)
+ if(!isnum(new_amount))
+ new_amount = 0
+ // See in which direction the gas moves
+ if(new_amount < 0)
+ direction = TARGET_TO_SOURCE
+ else
+ direction = SOURCE_TO_TARGET
+ target_pressure = min(PUMP_MAX_VOLUME,abs(new_amount))
+
+/obj/item/integrated_circuit/atmospherics/pump/volume/move_gas(datum/gas_mixture/source_air, datum/gas_mixture/target_air)
+ // No moles = nothing to pump
+ if(source_air.total_moles() <= 0)
+ return
+
+ // Negative Kelvin temperatures should never happen and if they do, normalize them
+ if(source_air.temperature < TCMB)
+ source_air.temperature = TCMB
+
+ if((source_air.return_pressure() < 0.01) || (target_air.return_pressure() >= PUMP_MAX_PRESSURE))
+ return
+
+ //The second part of the min caps the pressure built by the volume pumps to the max pump pressure
+ var/transfer_ratio = min(transfer_rate,target_air.volume*PUMP_MAX_PRESSURE/source_air.return_pressure())/source_air.volume
+
+ var/datum/gas_mixture/removed = source_air.remove_ratio(transfer_ratio * PUMP_EFFICIENCY)
+
+ target_air.merge(removed)
+
+
+// - gas vent - // **works**
+/obj/item/integrated_circuit/atmospherics/pump/vent
+ name = "gas vent"
+ extended_desc = "Use negative volume to move air from target to environment. Note that only part of the gas is moved on each transfer. Unlike the gas pump, this one keeps pumping even further to pressures of 9000 pKa and it is not advised to use it on tank circuits."
+ desc = "Moves gases between the environment and adjacent gas containers."
+ inputs = list(
+ "container" = IC_PINTYPE_REF,
+ "target pressure" = IC_PINTYPE_NUMBER
+ )
+
+/obj/item/integrated_circuit/atmospherics/pump/vent/on_data_written()
+ var/amt = get_pin_data(IC_INPUT, 2)
+ update_target(amt)
+
+/obj/item/integrated_circuit/atmospherics/pump/vent/do_work()
+ var/turf/source = get_turf(src)
+ var/obj/target = get_pin_data_as_type(IC_INPUT, 1, /obj)
+ perform_magic(source, target)
+ activate_pin(2)
+
+/obj/item/integrated_circuit/atmospherics/pump/vent/check_gastarget(atom/gasholder)
+ if(!gasholder)
+ return FALSE
+ if(!gasholder.Adjacent(get_object()))
+ return FALSE
+ if(!istype(gasholder, /obj/item/tank) && !istype(gasholder, /obj/machinery/portable_atmospherics) && !istype(gasholder, /obj/item/integrated_circuit/atmospherics))
+ return FALSE
+ return TRUE
+
+
+/obj/item/integrated_circuit/atmospherics/pump/vent/check_gassource(atom/target)
+ if(!target)
+ return FALSE
+ if(!istype(target, /turf))
+ return FALSE
+ return TRUE
+
+
+// - integrated connector - // Can connect and disconnect properly
+/obj/item/integrated_circuit/atmospherics/connector
+ name = "integrated connector"
+ desc = "Creates an airtight seal with standard connectors found on the floor, \
+ allowing the assembly to exchange gases with a pipe network."
+ extended_desc = "This circuit will automatically attempt to locate and connect to ports on the floor beneath it when activated. \
+ You must set a target before connecting."
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ inputs = list(
+ "target" = IC_PINTYPE_REF
+ )
+ activators = list(
+ "toggle connection" = IC_PINTYPE_PULSE_IN,
+ "on connected" = IC_PINTYPE_PULSE_OUT,
+ "on connection failed" = IC_PINTYPE_PULSE_OUT,
+ "on disconnected" = IC_PINTYPE_PULSE_OUT
+ )
+
+ var/obj/machinery/atmospherics/components/unary/portables_connector/connector
+
+/obj/item/integrated_circuit/atmospherics/connector/Initialize()
+ air_contents = new(volume)
+ START_PROCESSING(SSobj, src)
+ . = ..()
+
+//Sucks up the gas from the connector
+/obj/item/integrated_circuit/atmospherics/connector/process()
+ set_pin_data(IC_OUTPUT, 2, air_contents.return_pressure())
+
+/obj/item/integrated_circuit/atmospherics/connector/check_gassource(atom/gasholder)
+ if(!gasholder)
+ return FALSE
+ if(!istype(gasholder,/obj/machinery/atmospherics/components/unary/portables_connector))
+ return FALSE
+ return TRUE
+
+//If the assembly containing this is moved from the tile the connector pipe is in, the connection breaks
+/obj/item/integrated_circuit/atmospherics/connector/ext_moved()
+ if(connector)
+ if(get_dist(get_object(), connector) > 0)
+ // The assembly is set as connected device and the connector handles the rest
+ connector.connected_device = null
+ connector = null
+ activate_pin(4)
+
+/obj/item/integrated_circuit/atmospherics/connector/do_work()
+ // If there is a connection, disconnect
+ if(connector)
+ connector.connected_device = null
+ connector = null
+ activate_pin(4)
+ return
+
+ var/obj/machinery/atmospherics/components/unary/portables_connector/PC = locate() in get_turf(src)
+ // If no connector can't connect
+ if(!PC)
+ activate_pin(3)
+ return
+ connector = PC
+ connector.connected_device = src
+ activate_pin(2)
+
+// Required for making the connector port script work
+obj/item/integrated_circuit/atmospherics/connector/portableConnectorReturnAir()
+ return air_contents
+
+
+// - gas filter - // **works**
+/obj/item/integrated_circuit/atmospherics/pump/filter
+ name = "gas filter"
+ desc = "Filters one gas out of a mixture."
+ complexity = 20
+ size = 8
+ spawn_flags = IC_SPAWN_RESEARCH
+ inputs = list(
+ "source" = IC_PINTYPE_REF,
+ "filtered output" = IC_PINTYPE_REF,
+ "contaminants output" = IC_PINTYPE_REF,
+ "wanted gases" = IC_PINTYPE_LIST,
+ "target pressure" = IC_PINTYPE_NUMBER
+ )
+ power_draw_per_use = 30
+
+/obj/item/integrated_circuit/atmospherics/pump/filter/on_data_written()
+ var/amt = get_pin_data(IC_INPUT, 5)
+ target_pressure = CLAMP(amt, 0, PUMP_MAX_PRESSURE)
+
+/obj/item/integrated_circuit/atmospherics/pump/filter/do_work()
+ activate_pin(2)
+ var/obj/source = get_pin_data_as_type(IC_INPUT, 1, /obj)
+ var/obj/filtered = get_pin_data_as_type(IC_INPUT, 2, /obj)
+ var/obj/contaminants = get_pin_data_as_type(IC_INPUT, 3, /obj)
+
+ var/wanted = get_pin_data(IC_INPUT, 4)
+
+ // If there is no filtered output, this whole thing makes no sense
+ if(!check_gassource(filtered))
+ return
+
+ var/datum/gas_mixture/filtered_air = filtered.return_air()
+ if(!filtered_air)
+ return
+
+ // If no source is set, the source is possibly this circuit's content
+ if(!check_gassource(source))
+ source = src
+ var/datum/gas_mixture/source_air = source.return_air()
+
+ //No source air: source is this circuit
+ if(!source_air)
+ source_air = air_contents
+
+ // If no filtering tank is set, filter through itself
+ if(!check_gassource(contaminants))
+ contaminants = src
+ var/datum/gas_mixture/contaminated_air = contaminants.return_air()
+
+ //If there is no gas mixture datum for unfiltered, pump the contaminants back into the circuit
+ if(!contaminated_air)
+ contaminated_air = air_contents
+
+ if(contaminated_air.return_pressure() >= PUMP_MAX_PRESSURE || filtered_air.return_pressure() >= PUMP_MAX_PRESSURE)
+ return
+
+ var/pressure_delta = target_pressure - contaminated_air.return_pressure()
+ var/transfer_moles
+
+ //Negative Kelvins are an anomaly and should be normalized if encountered
+ if(source_air.temperature < TCMB)
+ source_air.temperature = TCMB
+
+ transfer_moles = (pressure_delta*contaminated_air.volume/(source_air.temperature * R_IDEAL_GAS_EQUATION))*PUMP_EFFICIENCY
+
+ //If there is nothing to transfer, just return
+ if(transfer_moles <= 0)
+ return
+
+ //This is the var that holds the currently filtered part of the gas
+ var/datum/gas_mixture/removed = source_air.remove(transfer_moles)
+ if(!removed)
+ return
+
+ //This is the gas that will be moved from source to filtered
+ var/datum/gas_mixture/filtered_out = new
+
+ for(var/filtered_gas in removed.gases)
+ //Get the name of the gas and see if it is in the list
+ if(removed.gases[filtered_gas][GAS_META][META_GAS_NAME] in wanted)
+ //The gas that is put in all the filtered out gases
+ filtered_out.temperature = removed.temperature
+ filtered_out.add_gas(filtered_gas)
+ filtered_out.gases[filtered_gas][MOLES] = removed.gases[filtered_gas][MOLES]
+
+ //The filtered out gas is entirely removed from the currently filtered gases
+ removed.gases[filtered_gas][MOLES] = 0
+ removed.garbage_collect()
+
+ //Check if the pressure is high enough to put stuff in filtered, or else just put it back in the source
+ var/datum/gas_mixture/target = (filtered_air.return_pressure() < target_pressure ? filtered_air : source_air)
+ target.merge(filtered_out)
+ contaminated_air.merge(removed)
+
+
+/obj/item/integrated_circuit/atmospherics/pump/filter/Initialize()
+ air_contents = new(volume)
+ . = ..()
+ extended_desc = "Remember to properly spell and capitalize the filtered gas name. \
+ Note that only part of the gas is moved on each transfer, \
+ so multiple activations will be necessary to achieve target pressure. \
+ The pressure limit for circuit pumps is [round(PUMP_MAX_PRESSURE)] kPa."
+
+
+// - gas mixer - // **works**
+/obj/item/integrated_circuit/atmospherics/pump/mixer
+ name = "gas mixer"
+ desc = "Mixes 2 different types of gases."
+ complexity = 20
+ size = 8
+ spawn_flags = IC_SPAWN_RESEARCH
+ inputs = list(
+ "first source" = IC_PINTYPE_REF,
+ "second source" = IC_PINTYPE_REF,
+ "output" = IC_PINTYPE_REF,
+ "first source percentage" = IC_PINTYPE_NUMBER,
+ "target pressure" = IC_PINTYPE_NUMBER
+ )
+ power_draw_per_use = 30
+
+/obj/item/integrated_circuit/atmospherics/pump/mixer/do_work()
+ activate_pin(2)
+ var/obj/source_1 = get_pin_data(IC_INPUT, 1)
+ var/obj/source_2 = get_pin_data(IC_INPUT, 2)
+ var/obj/gas_output = get_pin_data(IC_INPUT, 3)
+ if(!check_gassource(source_1))
+ source_1 = src
+
+ if(!check_gassource(source_2))
+ source_2 = src
+
+ if(!check_gassource(gas_output))
+ gas_output = src
+
+ if(source_1 == gas_output || source_2 == gas_output)
+ return
+
+ var/datum/gas_mixture/source_1_gases = source_1.return_air()
+ var/datum/gas_mixture/source_2_gases = source_2.return_air()
+ var/datum/gas_mixture/output_gases = gas_output.return_air()
+
+ if(!source_1_gases || !source_2_gases || !output_gases)
+ return
+
+ if(output_gases.return_pressure() >= PUMP_MAX_PRESSURE)
+ return
+
+ if(source_1_gases.return_pressure() <= 0 || source_2_gases.return_pressure() <= 0)
+ return
+
+ //This calculates how much should be sent
+ var/gas_percentage = round(max(min(get_pin_data(IC_INPUT, 4),100),0) / 100)
+
+ //Basically: number of moles = percentage of pressure filled up * efficiency coefficient * (pressure from both gases * volume of output) / (R * Temperature)
+ var/transfer_moles = (get_pin_data(IC_INPUT, 5) / max(1,output_gases.return_pressure())) * PUMP_EFFICIENCY * (source_1_gases.return_pressure() * gas_percentage + source_2_gases.return_pressure() * (1 - gas_percentage)) * output_gases.volume/ (R_IDEAL_GAS_EQUATION * max(output_gases.temperature,TCMB))
+
+
+ if(transfer_moles <= 0)
+ return
+
+ var/datum/gas_mixture/mix = source_1_gases.remove(transfer_moles * gas_percentage)
+ output_gases.merge(mix)
+ mix = source_2_gases.remove(transfer_moles * (1-gas_percentage))
+ output_gases.merge(mix)
+
+
+// - integrated tank - // **works**
+/obj/item/integrated_circuit/atmospherics/tank
+ name = "integrated tank"
+ desc = "A small tank for the storage of gases."
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ size = 4
+ activators = list(
+ "push ref" = IC_PINTYPE_PULSE_IN
+ )
+ volume = 3 //emergency tank sized
+ var/broken = FALSE
+
+/obj/item/integrated_circuit/atmospherics/tank/Initialize()
+ air_contents = new(volume)
+ START_PROCESSING(SSobj, src)
+ extended_desc = "Take care not to pressurize it above [round(TANK_FAILURE_PRESSURE)] kPa, or else it will break."
+ . = ..()
+
+/obj/item/integrated_circuit/atmospherics/tank/Destroy()
+ STOP_PROCESSING(SSobj, src)
+ . = ..()
+
+/obj/item/integrated_circuit/atmospherics/tank/do_work()
+ set_pin_data(IC_OUTPUT, 1, WEAKREF(src))
+ push_data()
+
+/obj/item/integrated_circuit/atmospherics/tank/process()
+ var/tank_pressure = air_contents.return_pressure()
+ set_pin_data(IC_OUTPUT, 2, tank_pressure)
+ push_data()
+
+ //Check if tank broken
+ if(!broken && tank_pressure > TANK_FAILURE_PRESSURE)
+ broken = TRUE
+ to_chat(view(2),"The [name] ruptures, releasing its gases!")
+ if(broken)
+ release()
+
+/obj/item/integrated_circuit/atmospherics/tank/proc/release()
+ if(air_contents.total_moles() > 0)
+ playsound(loc, 'sound/effects/spray.ogg', 10, 1, -3)
+ var/datum/gas_mixture/expelled_gas = air_contents.remove(air_contents.total_moles())
+ var/turf/current_turf = get_turf(src)
+ var/datum/gas_mixture/exterior_gas
+ if(!current_turf)
+ return
+
+ exterior_gas = current_turf.return_air()
+ exterior_gas.merge(expelled_gas)
+
+
+// - large integrated tank - // **works**
+/obj/item/integrated_circuit/atmospherics/tank/large
+ name = "large integrated tank"
+ desc = "A less small tank for the storage of gases."
+ volume = 9
+ size = 12
+ spawn_flags = IC_SPAWN_RESEARCH
+
+
+// - freezer tank - // **works**
+/obj/item/integrated_circuit/atmospherics/tank/freezer
+ name = "freezer tank"
+ desc = "Cools the gas it contains to a preset temperature."
+ volume = 6
+ size = 8
+ inputs = list(
+ "target temperature" = IC_PINTYPE_NUMBER,
+ "on" = IC_PINTYPE_BOOLEAN
+ )
+ inputs_default = list("1" = 300)
+ spawn_flags = IC_SPAWN_RESEARCH
+ var/temperature = 293.15
+ var/heater_coefficient = 0.1
+
+/obj/item/integrated_circuit/atmospherics/tank/freezer/on_data_written()
+ temperature = max(73.15,min(293.15,get_pin_data(IC_INPUT, 1)))
+ if(get_pin_data(IC_INPUT, 2))
+ power_draw_idle = 30
+ else
+ power_draw_idle = 0
+
+/obj/item/integrated_circuit/atmospherics/tank/freezer/process()
+ var/tank_pressure = air_contents.return_pressure()
+ set_pin_data(IC_OUTPUT, 2, tank_pressure)
+ push_data()
+
+ //Cool the tank if the power is on and the temp is above
+ if(!power_draw_idle || air_contents.temperature < temperature)
+ return
+
+ air_contents.temperature = max(73.15,air_contents.temperature - (air_contents.temperature - temperature) * heater_coefficient)
+
+
+// - heater tank - // **works**
+/obj/item/integrated_circuit/atmospherics/tank/freezer/heater
+ name = "heater tank"
+ desc = "Heats the gas it contains to a preset temperature."
+ volume = 6
+ inputs = list(
+ "target temperature" = IC_PINTYPE_NUMBER,
+ "on" = IC_PINTYPE_BOOLEAN
+ )
+ spawn_flags = IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/atmospherics/tank/freezer/heater/on_data_written()
+ temperature = max(293.15,min(573.15,get_pin_data(IC_INPUT, 1)))
+ if(get_pin_data(IC_INPUT, 2))
+ power_draw_idle = 30
+ else
+ power_draw_idle = 0
+
+/obj/item/integrated_circuit/atmospherics/tank/freezer/heater/process()
+ var/tank_pressure = air_contents.return_pressure()
+ set_pin_data(IC_OUTPUT, 2, tank_pressure)
+ push_data()
+
+ //Heat the tank if the power is on or its temperature is below what is set
+ if(!power_draw_idle || air_contents.temperature > temperature)
+ return
+
+ air_contents.temperature = min(573.15,air_contents.temperature + (temperature - air_contents.temperature) * heater_coefficient)
+
+
+// - atmospheric cooler - // **works**
+/obj/item/integrated_circuit/atmospherics/cooler
+ name = "atmospheric cooler circuit"
+ desc = "Cools the air around it."
+ volume = 6
+ size = 13
+ spawn_flags = IC_SPAWN_RESEARCH
+ inputs = list(
+ "target temperature" = IC_PINTYPE_NUMBER,
+ "on" = IC_PINTYPE_BOOLEAN
+ )
+ var/temperature = 293.15
+ var/heater_coefficient = 0.1
+
+/obj/item/integrated_circuit/atmospherics/cooler/Initialize()
+ air_contents = new(volume)
+ START_PROCESSING(SSobj, src)
+ . = ..()
+
+/obj/item/integrated_circuit/atmospherics/cooler/Destroy()
+ STOP_PROCESSING(SSobj, src)
+ . = ..()
+
+/obj/item/integrated_circuit/atmospherics/cooler/on_data_written()
+ temperature = max(243.15,min(293.15,get_pin_data(IC_INPUT, 1)))
+ if(get_pin_data(IC_INPUT, 2))
+ power_draw_idle = 30
+ else
+ power_draw_idle = 0
+
+/obj/item/integrated_circuit/atmospherics/cooler/process()
+ set_pin_data(IC_OUTPUT, 2, air_contents.return_pressure())
+ push_data()
+
+
+ //Get the turf you're on and its gas mixture
+ var/turf/current_turf = get_turf(src)
+ if(!current_turf)
+ return
+
+ var/datum/gas_mixture/turf_air = current_turf.return_air()
+ if(!power_draw_idle || turf_air.temperature < temperature)
+ return
+
+ //Cool the gas
+ turf_air.temperature = max(243.15,turf_air.temperature - (turf_air.temperature - temperature) * heater_coefficient)
+
+
+// - atmospheric heater - // **works**
+/obj/item/integrated_circuit/atmospherics/cooler/heater
+ name = "atmospheric heater circuit"
+ desc = "Heats the air around it."
+
+/obj/item/integrated_circuit/atmospherics/cooler/heater/on_data_written()
+ temperature = max(293.15,min(323.15,get_pin_data(IC_INPUT, 1)))
+ if(get_pin_data(IC_INPUT, 2))
+ power_draw_idle = 30
+ else
+ power_draw_idle = 0
+
+/obj/item/integrated_circuit/atmospherics/cooler/heater/process()
+ set_pin_data(IC_OUTPUT, 2, air_contents.return_pressure())
+ push_data()
+
+ //Get the turf and its air mixture
+ var/turf/current_turf = get_turf(src)
+ if(!current_turf)
+ return
+
+ var/datum/gas_mixture/turf_air = current_turf.return_air()
+ if(!power_draw_idle || turf_air.temperature > temperature)
+ return
+
+ //Heat the gas
+ turf_air.temperature = min(323.15,turf_air.temperature + (temperature - turf_air.temperature) * heater_coefficient)
+
+
+// - tank slot - // **works**
+/obj/item/integrated_circuit/input/tank_slot
+ category_text = "Atmospherics"
+ cooldown_per_use = 1
+ name = "tank slot"
+ desc = "Lets you add a tank to your assembly and remove it even when the assembly is closed."
+ extended_desc = "It can help you extract gases easier."
+ complexity = 25
+ size = 30
+ inputs = list()
+ outputs = list(
+ "pressure used" = IC_PINTYPE_NUMBER,
+ "current tank" = IC_PINTYPE_REF
+ )
+ activators = list(
+ "push ref" = IC_PINTYPE_PULSE_IN,
+ "on insert" = IC_PINTYPE_PULSE_OUT,
+ "on remove" = IC_PINTYPE_PULSE_OUT
+ )
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+ can_be_asked_input = TRUE
+ demands_object_input = TRUE
+ can_input_object_when_closed = TRUE
+
+ var/obj/item/tank/internals/current_tank
+
+/obj/item/integrated_circuit/input/tank_slot/Initialize()
+ START_PROCESSING(SSobj, src)
+ . = ..()
+
+/obj/item/integrated_circuit/input/tank_slot/process()
+ push_pressure()
+
+/obj/item/integrated_circuit/input/tank_slot/attackby(var/obj/item/tank/internals/I, var/mob/living/user)
+ //Check if it truly is a tank
+ if(!istype(I,/obj/item/tank/internals))
+ to_chat(user,"The [I.name] doesn't seem to fit in here.")
+ return
+
+ //Check if there is no other tank already inside
+ if(current_tank)
+ to_chat(user,"There is already a gas tank inside.")
+ return
+
+ //The current tank is the one we just attached, its location is inside the circuit
+ current_tank = I
+ user.transferItemToLoc(I,src)
+ to_chat(user,"You put the [I.name] inside the tank slot.")
+
+ //Set the pin to a weak reference of the current tank
+ push_pressure()
+ set_pin_data(IC_OUTPUT, 2, WEAKREF(current_tank))
+ push_data()
+ do_work(1)
+
+
+/obj/item/integrated_circuit/input/tank_slot/ask_for_input(mob/user)
+ attack_self(user)
+
+/obj/item/integrated_circuit/input/tank_slot/attack_self(mob/user)
+ //Check if no tank attached
+ if(!current_tank)
+ to_chat(user, "There is currently no tank attached.")
+ return
+
+ //Remove tank and put in user's hands/location
+ to_chat(user, "You take [current_tank] out of the tank slot.")
+ user.put_in_hands(current_tank)
+ current_tank = null
+
+ //Remove tank reference
+ push_pressure()
+ set_pin_data(IC_OUTPUT, 2, null)
+ push_data()
+ do_work(2)
+
+/obj/item/integrated_circuit/input/tank_slot/do_work()
+ set_pin_data(IC_OUTPUT, 2, WEAKREF(current_tank))
+ push_data()
+
+/obj/item/integrated_circuit/input/tank_slot/proc/push_pressure()
+ if(!current_tank)
+ set_pin_data(IC_OUTPUT, 1, 0)
+ return
+
+ var/datum/gas_mixture/tank_air = current_tank.return_air()
+ if(!tank_air)
+ set_pin_data(IC_OUTPUT, 1, 0)
+ return
+
+ set_pin_data(IC_OUTPUT, 1, tank_air.return_pressure())
+ push_data()
+
+
+#undef SOURCE_TO_TARGET
+#undef TARGET_TO_SOURCE
+#undef PUMP_EFFICIENCY
+#undef TANK_FAILURE_PRESSURE
+#undef PUMP_MAX_PRESSURE
+#undef PUMP_MAX_VOLUME
diff --git a/code/modules/integrated_electronics/subtypes/input.dm b/code/modules/integrated_electronics/subtypes/input.dm
index 20d405b71d..6e8b0cb777 100644
--- a/code/modules/integrated_electronics/subtypes/input.dm
+++ b/code/modules/integrated_electronics/subtypes/input.dm
@@ -1218,4 +1218,95 @@
activate_pin(2)
else
return FALSE
- return TRUE
\ No newline at end of file
+ return TRUE
+
+
+//Hippie Ported Code--------------------------------------------------------------------------------------------------------
+
+
+ //Adding some color to cards aswell, because why not
+/obj/item/card/data/attackby(obj/item/I, mob/living/user)
+ if(istype(I, /obj/item/integrated_electronics/detailer))
+ var/obj/item/integrated_electronics/detailer/D = I
+ detail_color = D.detail_color
+ update_icon()
+ return ..()
+
+
+
+// -Inputlist- //
+/obj/item/integrated_circuit/input/selection
+ name = "selection circuit"
+ desc = "This circuit lets you choose between different strings from a selection."
+ extended_desc = "This circuit lets you choose between up to 4 different values from selection of up to 8 strings that you can set. Null values are ignored and the chosen value is put out in selected."
+ icon_state = "addition"
+ can_be_asked_input = 1
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ inputs = list(
+ "A" = IC_PINTYPE_STRING,
+ "B" = IC_PINTYPE_STRING,
+ "C" = IC_PINTYPE_STRING,
+ "D" = IC_PINTYPE_STRING,
+ "E" = IC_PINTYPE_STRING,
+ "F" = IC_PINTYPE_STRING,
+ "G" = IC_PINTYPE_STRING,
+ "H" = IC_PINTYPE_STRING
+ )
+ activators = list(
+ "on selected" = IC_PINTYPE_PULSE_OUT
+ )
+ outputs = list(
+ "selected" = IC_PINTYPE_STRING
+ )
+
+/obj/item/integrated_circuit/input/selection/ask_for_input(mob/user)
+ var/list/selection = list()
+ for(var/k in 1 to inputs.len)
+ var/I = get_pin_data(IC_INPUT, k)
+ if(istext(I))
+ selection.Add(I)
+ var/selected = input(user,"Choose input.","Selection") in selection
+ if(!selected)
+ return
+ set_pin_data(IC_OUTPUT, 1, selected)
+ push_data()
+ activate_pin(1)
+
+
+// -storage examiner- // **works**
+/obj/item/integrated_circuit/input/storage_examiner
+ name = "storage examiner circuit"
+ desc = "This circuit lets you scan a storage's content. (backpacks, toolboxes etc.)"
+ extended_desc = "The items are put out as reference, which makes it possible to interact with them. Additionally also gives the amount of items."
+ icon_state = "grabber"
+ can_be_asked_input = 1
+ complexity = 6
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ inputs = list(
+ "storage" = IC_PINTYPE_REF
+ )
+ activators = list(
+ "examine" = IC_PINTYPE_PULSE_IN,
+ "on examined" = IC_PINTYPE_PULSE_OUT
+ )
+ outputs = list(
+ "item amount" = IC_PINTYPE_NUMBER,
+ "item list" = IC_PINTYPE_LIST
+ )
+ power_draw_per_use = 85
+
+/obj/item/integrated_circuit/input/storage_examiner/do_work()
+ var/obj/item/storage = get_pin_data_as_type(IC_INPUT, 1, /obj/item)
+ if(!istype(storage,/obj/item/storage))
+ return
+
+ set_pin_data(IC_OUTPUT, 1, storage.contents.len)
+
+ var/list/regurgitated_contents = list()
+ for(var/obj/o in storage.contents)
+ regurgitated_contents.Add(WEAKREF(o))
+
+
+ set_pin_data(IC_OUTPUT, 2, regurgitated_contents)
+ push_data()
+ activate_pin(2)
\ No newline at end of file
diff --git a/code/modules/integrated_electronics/subtypes/manipulation.dm b/code/modules/integrated_electronics/subtypes/manipulation.dm
index 1c76c7648c..f63d58b707 100644
--- a/code/modules/integrated_electronics/subtypes/manipulation.dm
+++ b/code/modules/integrated_electronics/subtypes/manipulation.dm
@@ -677,3 +677,140 @@
GET_COMPONENT(materials, /datum/component/material_container)
materials.retrieve_all()
.=..()
+
+
+//Hippie Ported Code--------------------------------------------------------------------------------------------------------
+
+
+// - inserter circuit - //
+/obj/item/integrated_circuit/manipulation/inserter
+ name = "inserter"
+ desc = "A nimble circuit that puts stuff inside a storage like a backpack and can take it out aswell."
+ icon_state = "grabber"
+ extended_desc = "This circuit accepts a reference to an object to be inserted or extracted depending on mode. If a storage is given for extraction, the extracted item will be put in the new storage. Modes: 1 insert, 0 to extract."
+ w_class = WEIGHT_CLASS_SMALL
+ size = 3
+ cooldown_per_use = 5
+ complexity = 10
+ inputs = list("target object" = IC_PINTYPE_REF, "target container" = IC_PINTYPE_REF,"mode" = IC_PINTYPE_NUMBER)
+ activators = list("pulse in" = IC_PINTYPE_PULSE_IN,"pulse out" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_RESEARCH
+ action_flags = IC_ACTION_COMBAT
+ power_draw_per_use = 20
+ var/max_items = 10
+
+/obj/item/integrated_circuit/manipulation/inserter/do_work()
+ //There shouldn't be any target required to eject all contents
+ var/obj/item/target_obj = get_pin_data_as_type(IC_INPUT, 1, /obj/item)
+ if(!target_obj)
+ return
+
+ var/distance = get_dist(get_turf(src),get_turf(target_obj))
+ if(distance > 1 || distance < 0)
+ return
+
+ var/obj/item/storage/container = get_pin_data_as_type(IC_INPUT, 2, /obj/item)
+ var/mode = get_pin_data(IC_INPUT, 3)
+ switch(mode)
+ if(1) //Not working
+ if(!container || !istype(container,/obj/item/storage) || !Adjacent(container))
+ return
+
+ GET_COMPONENT_FROM(STR, /datum/component/storage, container)
+ if(!STR)
+ return
+
+ STR.attackby(src, target_obj)
+
+ else
+ GET_COMPONENT_FROM(STR, /datum/component/storage, target_obj.loc)
+ if(!STR)
+ return
+
+ if(!container || !istype(container,/obj/item/storage) || !Adjacent(container))
+ STR.remove_from_storage(target_obj,drop_location())
+ else
+ STR.remove_from_storage(target_obj,container)
+
+// Renamer circuit. Renames the assembly it is in. Useful in cooperation with telecomms-based circuits.
+/obj/item/integrated_circuit/manipulation/renamer
+ name = "renamer"
+ desc = "A small circuit that renames the assembly it is in. Useful paired with speech-based circuits."
+ icon_state = "internalbm"
+ extended_desc = "This circuit accepts a string as input, and can be pulsed to rewrite the current assembly's name with said string. On success, it pulses the default pulse-out wire."
+ inputs = list("name" = IC_PINTYPE_STRING)
+ outputs = list("current name" = IC_PINTYPE_STRING)
+ activators = list("rename" = IC_PINTYPE_PULSE_IN,"get name" = IC_PINTYPE_PULSE_IN,"pulse out" = IC_PINTYPE_PULSE_OUT)
+ power_draw_per_use = 1
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/manipulation/renamer/do_work(var/n)
+ if(!assembly)
+ return
+ switch(n)
+ if(1)
+ var/new_name = get_pin_data(IC_INPUT, 1)
+ if(new_name)
+ assembly.name = new_name
+
+ else
+ set_pin_data(IC_OUTPUT, 1, assembly.name)
+ push_data()
+
+ activate_pin(3)
+
+
+
+// - redescribing circuit - //
+/obj/item/integrated_circuit/manipulation/redescribe
+ name = "redescriber"
+ desc = "Takes any string as an input and will set it as the assembly's description."
+ extended_desc = "Strings should can be of any length."
+ icon_state = "speaker"
+ cooldown_per_use = 10
+ complexity = 3
+ inputs = list("text" = IC_PINTYPE_STRING)
+ outputs = list("description" = IC_PINTYPE_STRING)
+ activators = list("redescribe" = IC_PINTYPE_PULSE_IN,"get description" = IC_PINTYPE_PULSE_IN,"pulse out" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/manipulation/redescribe/do_work(var/n)
+ if(!assembly)
+ return
+
+ switch(n)
+ if(1)
+ assembly.desc = get_pin_data(IC_INPUT, 1)
+
+ else
+ set_pin_data(IC_OUTPUT, 1, assembly.desc)
+ push_data()
+
+ activate_pin(3)
+
+// - repainting circuit - //
+/obj/item/integrated_circuit/manipulation/repaint
+ name = "auto-repainter"
+ desc = "There's an oddly high amount of spraying cans fitted right inside this circuit."
+ extended_desc = "Takes a value in hexadecimal and uses it to repaint the assembly it is in."
+ cooldown_per_use = 10
+ complexity = 3
+ inputs = list("color" = IC_PINTYPE_COLOR)
+ outputs = list("current color" = IC_PINTYPE_COLOR)
+ activators = list("repaint" = IC_PINTYPE_PULSE_IN,"get color" = IC_PINTYPE_PULSE_IN,"pulse out" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/manipulation/repaint/do_work(var/n)
+ if(!assembly)
+ return
+
+ switch(n)
+ if(1)
+ assembly.detail_color = get_pin_data(IC_INPUT, 1)
+ assembly.update_icon()
+
+ else
+ set_pin_data(IC_OUTPUT, 1, assembly.detail_color)
+ push_data()
+
+ activate_pin(3)
\ No newline at end of file
diff --git a/code/modules/integrated_electronics/subtypes/output.dm b/code/modules/integrated_electronics/subtypes/output.dm
index 0ad2714e28..86efc4c74a 100644
--- a/code/modules/integrated_electronics/subtypes/output.dm
+++ b/code/modules/integrated_electronics/subtypes/output.dm
@@ -385,4 +385,30 @@
else
assembly.prefered_hud_icon = "hudstat"
//update the diagnostic hud
- assembly.diag_hud_set_circuitstat()
\ No newline at end of file
+ assembly.diag_hud_set_circuitstat()
+
+
+//Hippie Ported Code--------------------------------------------------------------------------------------------------------
+
+
+
+/obj/item/radio/headset/integrated
+
+/obj/item/integrated_circuit/output/screen/large
+ name = "medium screen"
+
+/obj/item/integrated_circuit/output/screen/extralarge // the subtype is called "extralarge" because tg brought back medium screens and they named the subtype /screen/large
+ name = "large screen"
+ desc = "Takes any data type as an input and displays it to the user upon examining, and to all nearby beings when pulsed."
+ icon_state = "screen_large"
+ power_draw_per_use = 40
+ cooldown_per_use = 10
+
+/obj/item/integrated_circuit/output/screen/extralarge/do_work()
+ ..()
+ var/obj/O = assembly ? get_turf(assembly) : loc
+ O.visible_message("[icon2html(O.icon, world, O.icon_state)] [stuff_to_display]")
+ if(assembly)
+ assembly.investigate_log("displayed \"[html_encode(stuff_to_display)]\" with [type].", INVESTIGATE_CIRCUIT)
+ else
+ investigate_log("displayed \"[html_encode(stuff_to_display)]\" as [type].", INVESTIGATE_CIRCUIT)
\ No newline at end of file
diff --git a/code/modules/integrated_electronics/subtypes/reagents.dm b/code/modules/integrated_electronics/subtypes/reagents.dm
index b8f326f389..0d8bf96afc 100644
--- a/code/modules/integrated_electronics/subtypes/reagents.dm
+++ b/code/modules/integrated_electronics/subtypes/reagents.dm
@@ -538,3 +538,250 @@
reagents.handle_reactions()
set_pin_data(IC_OUTPUT, 3, reagents.chem_temp)
push_data()
+
+
+//Hippie Ported Code--------------------------------------------------------------------------------------------------------
+
+
+/obj/item/integrated_circuit/reagent/smoke
+ name = "smoke generator"
+ desc = "Unlike most electronics, creating smoke is completely intentional."
+ icon_state = "smoke"
+ extended_desc = "This smoke generator creates clouds of smoke on command. It can also hold liquids inside, which will go \
+ into the smoke clouds when activated. The reagents are consumed when the smoke is made."
+ ext_cooldown = 1
+ volume = 100
+ complexity = 20
+ cooldown_per_use = 1 SECONDS
+ inputs = list()
+ outputs = list(
+ "volume used" = IC_PINTYPE_NUMBER,
+ "self reference" = IC_PINTYPE_SELFREF
+ )
+ activators = list(
+ "create smoke" = IC_PINTYPE_PULSE_IN,
+ "on smoked" = IC_PINTYPE_PULSE_OUT,
+ "push ref" = IC_PINTYPE_PULSE_IN
+ )
+ spawn_flags = IC_SPAWN_RESEARCH
+ power_draw_per_use = 20
+ var/smoke_radius = 5
+ var/notified = FALSE
+
+/obj/item/integrated_circuit/reagent/smoke/on_reagent_change(changetype)
+ //reset warning only if we have reagents now
+ if(changetype == ADD_REAGENT)
+ notified = FALSE
+ push_vol()
+/obj/item/integrated_circuit/reagent/smoke/do_work(ord)
+ switch(ord)
+ if(1)
+ if(!reagents || (reagents.total_volume < IC_SMOKE_REAGENTS_MINIMUM_UNITS))
+ return
+ var/location = get_turf(src)
+ var/datum/effect_system/smoke_spread/chem/S = new
+ S.attach(location)
+ playsound(location, 'sound/effects/smoke.ogg', 50, 1, -3)
+ if(S)
+ S.set_up(reagents, smoke_radius, location, notified)
+ if(!notified)
+ notified = TRUE
+ S.start()
+ reagents.clear_reagents()
+ activate_pin(2)
+ if(3)
+ set_pin_data(IC_OUTPUT, 2, WEAKREF(src))
+ push_data()
+
+// - Integrated extinguisher - //
+/obj/item/integrated_circuit/reagent/extinguisher
+ name = "integrated extinguisher"
+ desc = "This circuit sprays any of its contents out like an extinguisher."
+ icon_state = "injector"
+ extended_desc = "This circuit can hold up to 30 units of any given chemicals. On each use, it sprays these reagents like a fire extinguisher."
+
+ volume = 30
+
+ complexity = 20
+ cooldown_per_use = 6 SECONDS
+ inputs = list(
+ "target X rel" = IC_PINTYPE_NUMBER,
+ "target Y rel" = IC_PINTYPE_NUMBER
+ )
+ outputs = list(
+ "volume" = IC_PINTYPE_NUMBER,
+ "self reference" = IC_PINTYPE_SELFREF
+ )
+ activators = list(
+ "spray" = IC_PINTYPE_PULSE_IN,
+ "on sprayed" = IC_PINTYPE_PULSE_OUT,
+ "on fail" = IC_PINTYPE_PULSE_OUT
+ )
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ power_draw_per_use = 15
+ var/busy = FALSE
+
+/obj/item/integrated_circuit/reagent/extinguisher/Initialize()
+ .=..()
+ set_pin_data(IC_OUTPUT,2, src)
+
+/obj/item/integrated_circuit/reagent/extinguisher/on_reagent_change(changetype)
+ push_vol()
+
+/obj/item/integrated_circuit/reagent/extinguisher/do_work()
+ //Check if enough volume
+ set_pin_data(IC_OUTPUT, 1, reagents.total_volume)
+ if(!reagents || reagents.total_volume < 5 || busy)
+ push_data()
+ activate_pin(3)
+ return
+
+ playsound(loc, 'sound/effects/extinguish.ogg', 75, 1, -3)
+ //Get the tile on which the water particle spawns
+ var/turf/Spawnpoint = get_turf(src)
+ if(!Spawnpoint)
+ push_data()
+ activate_pin(3)
+ return
+
+ //Get direction and target turf for each water particle
+ var/turf/T = locate(Spawnpoint.x + get_pin_data(IC_INPUT, 1),Spawnpoint.y + get_pin_data(IC_INPUT, 2),Spawnpoint.z)
+ if(!T)
+ push_data()
+ activate_pin(3)
+ return
+ var/direction = get_dir(Spawnpoint, T)
+ var/turf/T1 = get_step(T,turn(direction, 90))
+ var/turf/T2 = get_step(T,turn(direction, -90))
+ var/list/the_targets = list(T,T1,T2)
+ busy = TRUE
+
+ // Create list with particles and their targets
+ var/list/water_particles=list()
+ for(var/a=0, a<5, a++)
+ var/obj/effect/particle_effect/water/W = new /obj/effect/particle_effect/water(get_turf(src))
+ water_particles[W] = pick(the_targets)
+ var/datum/reagents/R = new/datum/reagents(5)
+ W.reagents = R
+ R.my_atom = W
+ reagents.trans_to(W,1)
+
+ //Make em move dat ass, hun
+ addtimer(CALLBACK(src, /obj/item/integrated_circuit/reagent/extinguisher/proc/move_particles, water_particles), 2)
+
+//This whole proc is a loop
+/obj/item/integrated_circuit/reagent/extinguisher/proc/move_particles(var/list/particles, var/repetitions=0)
+ //Check if there's anything in here first
+ if(!particles || particles.len == 0)
+ return
+ // Second loop: Get all the water particles and make them move to their target
+ for(var/obj/effect/particle_effect/water/W in particles)
+ var/turf/my_target = particles[W]
+ if(!W)
+ continue
+ step_towards(W,my_target)
+ if(!W.reagents)
+ continue
+ W.reagents.reaction(get_turf(W))
+ for(var/A in get_turf(W))
+ W.reagents.reaction(A)
+ if(W.loc == my_target)
+ break
+ if(repetitions < 4)
+ repetitions++ //Can't have math operations in addtimer(CALLBACK())
+ addtimer(CALLBACK(src, /obj/item/integrated_circuit/reagent/extinguisher/proc/move_particles, particles, repetitions), 2)
+ else
+ push_data()
+ activate_pin(2)
+ busy = FALSE
+
+// - Beaker Connector - //
+/obj/item/integrated_circuit/input/beaker_connector
+ category_text = "Reagent"
+ cooldown_per_use = 1
+ name = "beaker slot"
+ desc = "Lets you add a beaker to your assembly and remove it even when the assembly is closed."
+ icon_state = "reagent_storage"
+ extended_desc = "It can help you extract reagents easier."
+ complexity = 4
+
+ inputs = list()
+ outputs = list(
+ "volume used" = IC_PINTYPE_NUMBER,
+ "current beaker" = IC_PINTYPE_REF
+ )
+ activators = list(
+ "on insert" = IC_PINTYPE_PULSE_OUT,
+ "on remove" = IC_PINTYPE_PULSE_OUT,
+ "push ref" = IC_PINTYPE_PULSE_OUT
+ )
+
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ can_be_asked_input = TRUE
+ demands_object_input = TRUE
+ can_input_object_when_closed = TRUE
+
+ var/obj/item/reagent_containers/glass/beaker/current_beaker
+
+
+/obj/item/integrated_circuit/input/beaker_connector/attackby(var/obj/item/reagent_containers/I, var/mob/living/user)
+ //Check if it truly is a reagent container
+ if(!istype(I,/obj/item/reagent_containers/glass/beaker))
+ to_chat(user,"The [I.name] doesn't seem to fit in here.")
+ return
+
+ //Check if there is no other beaker already inside
+ if(current_beaker)
+ to_chat(user,"There is already a reagent container inside.")
+ return
+
+ //The current beaker is the one we just attached, its location is inside the circuit
+ current_beaker = I
+ user.transferItemToLoc(I,src)
+
+ to_chat(user,"You put the [I.name] inside the beaker connector.")
+
+ //Set the pin to a weak reference of the current beaker
+ push_vol()
+ set_pin_data(IC_OUTPUT, 2, WEAKREF(current_beaker))
+ push_data()
+ activate_pin(1)
+ activate_pin(3)
+
+
+/obj/item/integrated_circuit/input/beaker_connector/ask_for_input(mob/user)
+ attack_self(user)
+
+
+/obj/item/integrated_circuit/input/beaker_connector/attack_self(mob/user)
+ //Check if no beaker attached
+ if(!current_beaker)
+ to_chat(user, "There is currently no beaker attached.")
+ return
+
+ //Remove beaker and put in user's hands/location
+ to_chat(user, "You take [current_beaker] out of the beaker connector.")
+ user.put_in_hands(current_beaker)
+ current_beaker = null
+ //Remove beaker reference
+ push_vol()
+ set_pin_data(IC_OUTPUT, 2, null)
+ push_data()
+ activate_pin(2)
+ activate_pin(3)
+
+
+/obj/item/integrated_circuit/input/beaker_connector/proc/push_vol()
+ var/beakerVolume = 0
+ if(current_beaker)
+ beakerVolume = current_beaker.reagents.total_volume
+
+ set_pin_data(IC_OUTPUT, 1, beakerVolume)
+ push_data()
+
+
+/obj/item/reagent_containers/glass/beaker/on_reagent_change()
+ ..()
+ if(istype(loc,/obj/item/integrated_circuit/input/beaker_connector))
+ var/obj/item/integrated_circuit/input/beaker_connector/current_circuit = loc
+ current_circuit.push_vol()
\ No newline at end of file
diff --git a/code/modules/integrated_electronics/subtypes/smart.dm b/code/modules/integrated_electronics/subtypes/smart.dm
index bd93ae8314..b9bd4627c4 100644
--- a/code/modules/integrated_electronics/subtypes/smart.dm
+++ b/code/modules/integrated_electronics/subtypes/smart.dm
@@ -114,3 +114,262 @@
set_pin_data(IC_OUTPUT, 2, Yn)
push_data()
activate_pin(2)
+
+
+//Hippie Ported Code--------------------------------------------------------------------------------------------------------
+
+
+
+// - MMI Tank - //
+/obj/item/integrated_circuit/input/mmi_tank
+ name = "man-machine interface tank"
+ desc = "This circuit is just a jar filled with an artificial liquid mimicking the cerebrospinal fluid."
+ extended_desc = "This jar can hold 1 man-machine interface and let it take control of some basic functions of the assembly."
+ complexity = 29
+ inputs = list("laws" = IC_PINTYPE_LIST)
+ outputs = list(
+ "man-machine interface" = IC_PINTYPE_REF,
+ "direction" = IC_PINTYPE_DIR,
+ "click target" = IC_PINTYPE_REF
+ )
+ activators = list(
+ "move" = IC_PINTYPE_PULSE_OUT,
+ "left" = IC_PINTYPE_PULSE_OUT,
+ "right" = IC_PINTYPE_PULSE_OUT,
+ "up" = IC_PINTYPE_PULSE_OUT,
+ "down" = IC_PINTYPE_PULSE_OUT,
+ "leftclick" = IC_PINTYPE_PULSE_OUT,
+ "shiftclick" = IC_PINTYPE_PULSE_OUT,
+ "altclick" = IC_PINTYPE_PULSE_OUT,
+ "ctrlclick" = IC_PINTYPE_PULSE_OUT
+ )
+ spawn_flags = IC_SPAWN_RESEARCH
+ power_draw_per_use = 150
+ can_be_asked_input = TRUE
+ demands_object_input = TRUE
+
+ var/obj/item/mmi/installed_brain
+
+/obj/item/integrated_circuit/input/mmi_tank/attackby(var/obj/item/mmi/O, var/mob/user)
+ if(!istype(O,/obj/item/mmi))
+ to_chat(user,"You can't put that inside.")
+ return
+ if(installed_brain)
+ to_chat(user,"There's already a brain inside.")
+ return
+ user.transferItemToLoc(O,src)
+ installed_brain = O
+ can_be_asked_input = FALSE
+ to_chat(user, "You gently place \the man-machine interface inside the tank.")
+ to_chat(O, "You are slowly being placed inside the man-machine-interface tank.")
+ O.brainmob.remote_control=src
+ set_pin_data(IC_OUTPUT, 1, O)
+
+/obj/item/integrated_circuit/input/mmi_tank/attack_self(var/mob/user)
+ if(installed_brain)
+ RemoveBrain()
+ to_chat(user, "You slowly lift [installed_brain] out of the MMI tank.")
+ playsound(src, 'sound/items/Crowbar.ogg', 50, 1)
+ installed_brain = null
+ push_data()
+ else
+ to_chat(user, "You don't see any brain swimming in the tank.")
+
+/obj/item/integrated_circuit/input/mmi_tank/Destroy()
+ RemoveBrain()
+ ..()
+
+/obj/item/integrated_circuit/input/mmi_tank/relaymove(var/n,var/dir)
+ set_pin_data(IC_OUTPUT, 2, dir)
+ do_work(1)
+ switch(dir)
+ if(8) activate_pin(2)
+ if(4) activate_pin(3)
+ if(1) activate_pin(4)
+ if(2) activate_pin(5)
+
+/obj/item/integrated_circuit/input/mmi_tank/do_work(var/n)
+ push_data()
+ activate_pin(n)
+
+/obj/item/integrated_circuit/input/mmi_tank/proc/RemoveBrain()
+ if(installed_brain)
+ can_be_asked_input = TRUE
+ installed_brain.forceMove(drop_location())
+ set_pin_data(IC_OUTPUT, 1, WEAKREF(null))
+ if(installed_brain.brainmob)
+ installed_brain.brainmob.remote_control = null
+ ..()
+
+
+//Brain changes
+/mob/living/brain/var/check_bot_self = FALSE
+
+/mob/living/brain/ClickOn(atom/A, params)
+ ..()
+ if(!istype(remote_control,/obj/item/integrated_circuit/input/mmi_tank))
+ return
+ var/obj/item/integrated_circuit/input/mmi_tank/brainholder=remote_control
+ brainholder.set_pin_data(IC_OUTPUT, 3, A)
+ var/list/modifiers = params2list(params)
+
+ if(modifiers["shift"])
+ brainholder.do_work(7)
+ return
+ if(modifiers["alt"])
+ brainholder.do_work(8)
+ return
+ if(modifiers["ctrl"])
+ brainholder.do_work(9)
+ return
+
+ if(istype(A,/obj/item/electronic_assembly))
+ var/obj/item/electronic_assembly/CheckedAssembly = A
+
+ if(brainholder in CheckedAssembly.assembly_components)
+ var/obj/item/electronic_assembly/holdingassembly=A
+ check_bot_self=TRUE
+
+ if(holdingassembly.opened)
+ holdingassembly.ui_interact(src)
+ holdingassembly.attack_self(src)
+ check_bot_self=FALSE
+ return
+
+ brainholder.do_work(6)
+
+/mob/living/brain/canUseTopic()
+ return check_bot_self
+
+/obj/item/integrated_circuit/smart/advanced_pathfinder/proc/hippie_xor_decrypt()
+ var/Ps = get_pin_data(IC_INPUT, 4)
+ if(!Ps)
+ return
+ var/list/Pl = json_decode(XorEncrypt(hextostr(Ps, TRUE), SScircuit.cipherkey))
+ if(Pl&&islist(Pl))
+ idc.access = Pl
+
+// - pAI connector circuit - //
+/obj/item/integrated_circuit/input/pAI_connector
+ name = "pAI connector circuit"
+ desc = "This circuit lets you fit in a personal artificial intelligence to give it some form of control over the bot."
+ extended_desc = "You can wire various functions to it."
+ complexity = 29
+ inputs = list("laws" = IC_PINTYPE_LIST)
+ outputs = list(
+ "personal artificial intelligence" = IC_PINTYPE_REF,
+ "direction" = IC_PINTYPE_DIR,
+ "click target" = IC_PINTYPE_REF
+ )
+ activators = list(
+ "move" = IC_PINTYPE_PULSE_OUT,
+ "left" = IC_PINTYPE_PULSE_OUT,
+ "right" = IC_PINTYPE_PULSE_OUT,
+ "up" = IC_PINTYPE_PULSE_OUT,
+ "down" = IC_PINTYPE_PULSE_OUT,
+ "leftclick" = IC_PINTYPE_PULSE_OUT,
+ "shiftclick" = IC_PINTYPE_PULSE_OUT,
+ "altclick" = IC_PINTYPE_PULSE_OUT,
+ "ctrlclick" = IC_PINTYPE_PULSE_OUT,
+ "shiftctrlclick" = IC_PINTYPE_PULSE_OUT
+ )
+ spawn_flags = IC_SPAWN_RESEARCH
+ power_draw_per_use = 150
+ can_be_asked_input = TRUE
+ demands_object_input = TRUE
+
+ var/obj/item/paicard/installed_pai
+
+/obj/item/integrated_circuit/input/pAI_connector/attackby(var/obj/item/paicard/O, var/mob/user)
+ if(!istype(O,/obj/item/paicard))
+ to_chat(user,"You can't put that inside.")
+ return
+ if(installed_pai)
+ to_chat(user,"There's already a pAI connected to this.")
+ return
+ user.transferItemToLoc(O,src)
+ installed_pai = O
+ can_be_asked_input = FALSE
+ to_chat(user, "You slowly connect the circuit's pins to the [installed_pai].")
+ to_chat(O, "You are slowly being connected to the pAI connector.")
+ O.pai.remote_control=src
+ set_pin_data(IC_OUTPUT, 1, O)
+
+/obj/item/integrated_circuit/input/pAI_connector/attack_self(var/mob/user)
+ if(installed_pai)
+ RemovepAI()
+ to_chat(user, "You slowly disconnect the circuit's pins from the [installed_pai].")
+ playsound(src, 'sound/items/Crowbar.ogg', 50, 1)
+ installed_pai = null
+ push_data()
+ else
+ to_chat(user, "The connection port is empty.")
+
+/obj/item/integrated_circuit/input/pAI_connector/relaymove(var/n,var/dir)
+ set_pin_data(IC_OUTPUT, 2, dir)
+ do_work(1)
+ switch(dir)
+ if(8) activate_pin(2)
+ if(4) activate_pin(3)
+ if(1) activate_pin(4)
+ if(2) activate_pin(5)
+
+/obj/item/integrated_circuit/input/pAI_connector/do_work(var/n)
+ push_data()
+ activate_pin(n)
+
+
+/obj/item/integrated_circuit/input/pAI_connector/Destroy()
+ RemovepAI()
+ ..()
+
+/obj/item/integrated_circuit/input/pAI_connector/proc/RemovepAI()
+ if(installed_pai)
+ can_be_asked_input = TRUE
+ installed_pai.forceMove(drop_location())
+ set_pin_data(IC_OUTPUT, 1, WEAKREF(null))
+ installed_pai.pai.remote_control = null
+ ..()
+
+
+//pAI changes
+/mob/living/silicon/pai/var/check_bot_self = FALSE
+
+/mob/living/silicon/pai/ClickOn(atom/A, params)
+ ..()
+ if(!istype(remote_control,/obj/item/integrated_circuit/input/pAI_connector))
+ return
+ var/obj/item/integrated_circuit/input/pAI_connector/paiholder=remote_control
+ paiholder.set_pin_data(IC_OUTPUT, 3, A)
+ var/list/modifiers = params2list(params)
+
+ if(modifiers["shift"] && modifiers["ctrl"])
+ paiholder.do_work(10)
+ return
+ if(modifiers["shift"])
+ paiholder.do_work(7)
+ return
+ if(modifiers["alt"])
+ paiholder.do_work(8)
+ return
+ if(modifiers["ctrl"])
+ paiholder.do_work(9)
+ return
+
+ if(istype(A,/obj/item/electronic_assembly))
+ var/obj/item/electronic_assembly/CheckedAssembly = A
+
+ if(paiholder in CheckedAssembly.assembly_components)
+ var/obj/item/electronic_assembly/holdingassembly=A
+ check_bot_self=TRUE
+
+ if(holdingassembly.opened)
+ holdingassembly.ui_interact(src)
+ holdingassembly.attack_self(src)
+ check_bot_self=FALSE
+ return
+
+ paiholder.do_work(6)
+
+/mob/living/silicon/pai/canUseTopic()
+ return check_bot_self
\ No newline at end of file
diff --git a/code/modules/integrated_electronics/subtypes/time.dm b/code/modules/integrated_electronics/subtypes/time.dm
index 9f3265ed40..edb9e1ede3 100644
--- a/code/modules/integrated_electronics/subtypes/time.dm
+++ b/code/modules/integrated_electronics/subtypes/time.dm
@@ -175,7 +175,7 @@
desc = "Tells you what the time is, in terms and adjusted for your local station or planet"
/obj/item/integrated_circuit/time/clock/station/get_time()
- return station_time()
+ return STATION_TIME(FALSE)
/obj/item/integrated_circuit/time/clock/bluespace
name = "integrated clock (Bluespace Absolute Time)"
diff --git a/code/modules/lighting/lighting_source.dm b/code/modules/lighting/lighting_source.dm
index 59ce11baeb..8f4aea3925 100644
--- a/code/modules/lighting/lighting_source.dm
+++ b/code/modules/lighting/lighting_source.dm
@@ -1,298 +1,311 @@
-// This is where the fun begins.
-// These are the main datums that emit light.
-
-/datum/light_source
- var/atom/top_atom // The atom we're emitting light from (for example a mob if we're from a flashlight that's being held).
- var/atom/source_atom // The atom that we belong to.
-
- var/turf/source_turf // The turf under the above.
- var/turf/pixel_turf // The turf the top_atom appears to over.
- var/light_power // Intensity of the emitter light.
- var/light_range // The range of the emitted light.
- var/light_color // The colour of the light, string, decomposed by parse_light_color()
-
- // Variables for keeping track of the colour.
- var/lum_r
- var/lum_g
- var/lum_b
-
- // The lumcount values used to apply the light.
- var/tmp/applied_lum_r
- var/tmp/applied_lum_g
- var/tmp/applied_lum_b
-
- var/list/datum/lighting_corner/effect_str // List used to store how much we're affecting corners.
- var/list/turf/affecting_turfs
-
- var/applied = FALSE // Whether we have applied our light yet or not.
-
- var/needs_update = LIGHTING_NO_UPDATE // Whether we are queued for an update.
-
-
-/datum/light_source/New(var/atom/owner, var/atom/top)
- source_atom = owner // Set our new owner.
- LAZYADD(source_atom.light_sources, src)
- top_atom = top
- if (top_atom != source_atom)
- LAZYADD(top_atom.light_sources, src)
-
- source_turf = top_atom
- pixel_turf = get_turf_pixel(top_atom) || source_turf
-
- light_power = source_atom.light_power
- light_range = source_atom.light_range
- light_color = source_atom.light_color
-
- parse_light_color()
-
- update()
-
- return ..()
-
-/datum/light_source/Destroy(force)
- remove_lum()
- if (source_atom)
- LAZYREMOVE(source_atom.light_sources, src)
-
- if (top_atom)
- LAZYREMOVE(top_atom.light_sources, src)
-
- if (needs_update)
- GLOB.lighting_update_lights -= src
-
- . = ..()
-
-// Yes this doesn't align correctly on anything other than 4 width tabs.
-// If you want it to go switch everybody to elastic tab stops.
-// Actually that'd be great if you could!
-#define EFFECT_UPDATE(level) \
- if (needs_update == LIGHTING_NO_UPDATE) \
- GLOB.lighting_update_lights += src; \
- if (needs_update < level) \
- needs_update = level; \
-
-
-// This proc will cause the light source to update the top atom, and add itself to the update queue.
-/datum/light_source/proc/update(var/atom/new_top_atom)
- // This top atom is different.
- if (new_top_atom && new_top_atom != top_atom)
- if(top_atom != source_atom && top_atom.light_sources) // Remove ourselves from the light sources of that top atom.
- LAZYREMOVE(top_atom.light_sources, src)
-
- top_atom = new_top_atom
-
- if (top_atom != source_atom)
- LAZYADD(top_atom.light_sources, src) // Add ourselves to the light sources of our new top atom.
-
- EFFECT_UPDATE(LIGHTING_CHECK_UPDATE)
-
-// Will force an update without checking if it's actually needed.
-/datum/light_source/proc/force_update()
- EFFECT_UPDATE(LIGHTING_FORCE_UPDATE)
-
-// Will cause the light source to recalculate turfs that were removed or added to visibility only.
-/datum/light_source/proc/vis_update()
- EFFECT_UPDATE(LIGHTING_VIS_UPDATE)
-
-// Decompile the hexadecimal colour into lumcounts of each perspective.
-/datum/light_source/proc/parse_light_color()
- if (light_color)
- lum_r = GetRedPart (light_color) / 255
- lum_g = GetGreenPart (light_color) / 255
- lum_b = GetBluePart (light_color) / 255
- else
- lum_r = 1
- lum_g = 1
- lum_b = 1
-
-// Macro that applies light to a new corner.
-// It is a macro in the interest of speed, yet not having to copy paste it.
-// If you're wondering what's with the backslashes, the backslashes cause BYOND to not automatically end the line.
-// As such this all gets counted as a single line.
-// The braces and semicolons are there to be able to do this on a single line.
-#define LUM_FALLOFF(C, T) (1 - CLAMP01(sqrt((C.x - T.x) ** 2 + (C.y - T.y) ** 2 + LIGHTING_HEIGHT) / max(1, light_range)))
-
-#define APPLY_CORNER(C) \
- . = LUM_FALLOFF(C, pixel_turf); \
- . *= light_power; \
- var/OLD = effect_str[C]; \
- effect_str[C] = .; \
- \
- C.update_lumcount \
- ( \
- (. * lum_r) - (OLD * applied_lum_r), \
- (. * lum_g) - (OLD * applied_lum_g), \
- (. * lum_b) - (OLD * applied_lum_b) \
- );
-
-#define REMOVE_CORNER(C) \
- . = -effect_str[C]; \
- C.update_lumcount \
- ( \
- . * applied_lum_r, \
- . * applied_lum_g, \
- . * applied_lum_b \
- );
-
-// This is the define used to calculate falloff.
-
-/datum/light_source/proc/remove_lum()
- applied = FALSE
- var/thing
- for (thing in affecting_turfs)
- var/turf/T = thing
- LAZYREMOVE(T.affecting_lights, src)
-
- affecting_turfs = null
-
- var/datum/lighting_corner/C
- for (thing in effect_str)
- C = thing
- REMOVE_CORNER(C)
-
- LAZYREMOVE(C.affecting, src)
-
- effect_str = null
-
-/datum/light_source/proc/recalc_corner(var/datum/lighting_corner/C)
- LAZYINITLIST(effect_str)
- if (effect_str[C]) // Already have one.
- REMOVE_CORNER(C)
- effect_str[C] = 0
-
- APPLY_CORNER(C)
- UNSETEMPTY(effect_str)
-
-/datum/light_source/proc/update_corners()
- var/update = FALSE
- var/atom/source_atom = src.source_atom
-
- if (QDELETED(source_atom))
- qdel(src)
- return
-
- if (source_atom.light_power != light_power)
- light_power = source_atom.light_power
- update = TRUE
-
- if (source_atom.light_range != light_range)
- light_range = source_atom.light_range
- update = TRUE
-
- if (!top_atom)
- top_atom = source_atom
- update = TRUE
-
- if (!light_range || !light_power)
- qdel(src)
- return
-
- if (isturf(top_atom))
- if (source_turf != top_atom)
- source_turf = top_atom
- pixel_turf = source_turf
- update = TRUE
- else if (top_atom.loc != source_turf)
- source_turf = top_atom.loc
- pixel_turf = get_turf_pixel(top_atom)
- update = TRUE
- else
- var/P = get_turf_pixel(top_atom)
- if (P != pixel_turf)
- pixel_turf = P
- update = TRUE
-
- if (!isturf(source_turf))
- if (applied)
- remove_lum()
- return
-
- if (light_range && light_power && !applied)
- update = TRUE
-
- if (source_atom.light_color != light_color)
- light_color = source_atom.light_color
- parse_light_color()
- update = TRUE
-
- else if (applied_lum_r != lum_r || applied_lum_g != lum_g || applied_lum_b != lum_b)
- update = TRUE
-
- if (update)
- needs_update = LIGHTING_CHECK_UPDATE
- applied = TRUE
- else if (needs_update == LIGHTING_CHECK_UPDATE)
- return //nothing's changed
-
- var/list/datum/lighting_corner/corners = list()
- var/list/turf/turfs = list()
- var/thing
- var/datum/lighting_corner/C
- var/turf/T
- if (source_turf)
- var/oldlum = source_turf.luminosity
- source_turf.luminosity = CEILING(light_range, 1)
- for(T in view(CEILING(light_range, 1), source_turf))
- for (thing in T.get_corners(source_turf))
- C = thing
- corners[C] = 0
- turfs += T
- source_turf.luminosity = oldlum
-
- LAZYINITLIST(affecting_turfs)
- var/list/L = turfs - affecting_turfs // New turfs, add us to the affecting lights of them.
- affecting_turfs += L
- for (thing in L)
- T = thing
- LAZYADD(T.affecting_lights, src)
-
- L = affecting_turfs - turfs // Now-gone turfs, remove us from the affecting lights.
- affecting_turfs -= L
- for (thing in L)
- T = thing
- LAZYREMOVE(T.affecting_lights, src)
-
- LAZYINITLIST(effect_str)
- if (needs_update == LIGHTING_VIS_UPDATE)
- for (thing in corners - effect_str) // New corners
- C = thing
- LAZYADD(C.affecting, src)
- if (!C.active)
- effect_str[C] = 0
- continue
- APPLY_CORNER(C)
- else
- L = corners - effect_str
- for (thing in L) // New corners
- C = thing
- LAZYADD(C.affecting, src)
- if (!C.active)
- effect_str[C] = 0
- continue
- APPLY_CORNER(C)
-
- for (thing in corners - L) // Existing corners
- C = thing
- if (!C.active)
- effect_str[C] = 0
- continue
- APPLY_CORNER(C)
-
- L = effect_str - corners
- for (thing in L) // Old, now gone, corners.
- C = thing
- REMOVE_CORNER(C)
- LAZYREMOVE(C.affecting, src)
- effect_str -= L
-
- applied_lum_r = lum_r
- applied_lum_g = lum_g
- applied_lum_b = lum_b
-
- UNSETEMPTY(effect_str)
- UNSETEMPTY(affecting_turfs)
-
-#undef EFFECT_UPDATE
-#undef LUM_FALLOFF
-#undef REMOVE_CORNER
-#undef APPLY_CORNER
+// This is where the fun begins.
+// These are the main datums that emit light.
+
+/datum/light_source
+ var/atom/top_atom // The atom we're emitting light from (for example a mob if we're from a flashlight that's being held).
+ var/atom/source_atom // The atom that we belong to.
+
+ var/turf/source_turf // The turf under the above.
+ var/turf/pixel_turf // The turf the top_atom appears to over.
+ var/light_power // Intensity of the emitter light.
+ var/light_range // The range of the emitted light.
+ var/light_color // The colour of the light, string, decomposed by parse_light_color()
+
+ // Variables for keeping track of the colour.
+ var/lum_r
+ var/lum_g
+ var/lum_b
+
+ // The lumcount values used to apply the light.
+ var/tmp/applied_lum_r
+ var/tmp/applied_lum_g
+ var/tmp/applied_lum_b
+
+ var/list/datum/lighting_corner/effect_str // List used to store how much we're affecting corners.
+ var/list/turf/affecting_turfs
+
+ var/applied = FALSE // Whether we have applied our light yet or not.
+
+ var/needs_update = LIGHTING_NO_UPDATE // Whether we are queued for an update.
+
+// Thanks to Lohikar for flinging this tiny bit of code at me, increasing my brain cell count from 1 to 2 in the process.
+// This macro will only offset up to 1 tile, but anything with a greater offset is an outlier and probably should handle its own lighting offsets.
+// Anything pixelshifted 16px or more will be considered on the next tile.
+#define GET_APPROXIMATE_PIXEL_DIR(PX, PY) ((!(PX) ? 0 : ((PX >= 16 ? EAST : (PX <= -16 ? WEST : 0)))) | (!PY ? 0 : (PY >= 16 ? NORTH : (PY <= -16 ? SOUTH : 0))))
+#define UPDATE_APPROXIMATE_PIXEL_TURF var/_mask = GET_APPROXIMATE_PIXEL_DIR(top_atom.pixel_x, top_atom.pixel_y); pixel_turf = _mask ? (get_step(source_turf, _mask) || source_turf) : source_turf
+
+/datum/light_source/New(var/atom/owner, var/atom/top)
+ source_atom = owner // Set our new owner.
+ LAZYADD(source_atom.light_sources, src)
+ top_atom = top
+ if (top_atom != source_atom)
+ LAZYADD(top_atom.light_sources, src)
+
+ source_turf = top_atom
+ UPDATE_APPROXIMATE_PIXEL_TURF
+
+ light_power = source_atom.light_power
+ light_range = source_atom.light_range
+ light_color = source_atom.light_color
+
+ parse_light_color()
+
+ update()
+
+ return ..()
+
+/datum/light_source/Destroy(force)
+ remove_lum()
+ if (source_atom)
+ LAZYREMOVE(source_atom.light_sources, src)
+
+ if (top_atom)
+ LAZYREMOVE(top_atom.light_sources, src)
+
+ if (needs_update)
+ GLOB.lighting_update_lights -= src
+
+ . = ..()
+
+// Yes this doesn't align correctly on anything other than 4 width tabs.
+// If you want it to go switch everybody to elastic tab stops.
+// Actually that'd be great if you could!
+#define EFFECT_UPDATE(level) \
+ if (needs_update == LIGHTING_NO_UPDATE) \
+ GLOB.lighting_update_lights += src; \
+ if (needs_update < level) \
+ needs_update = level; \
+
+
+// This proc will cause the light source to update the top atom, and add itself to the update queue.
+/datum/light_source/proc/update(var/atom/new_top_atom)
+ // This top atom is different.
+ if (new_top_atom && new_top_atom != top_atom)
+ if(top_atom != source_atom && top_atom.light_sources) // Remove ourselves from the light sources of that top atom.
+ LAZYREMOVE(top_atom.light_sources, src)
+
+ top_atom = new_top_atom
+
+ if (top_atom != source_atom)
+ LAZYADD(top_atom.light_sources, src) // Add ourselves to the light sources of our new top atom.
+
+ EFFECT_UPDATE(LIGHTING_CHECK_UPDATE)
+
+// Will force an update without checking if it's actually needed.
+/datum/light_source/proc/force_update()
+ EFFECT_UPDATE(LIGHTING_FORCE_UPDATE)
+
+// Will cause the light source to recalculate turfs that were removed or added to visibility only.
+/datum/light_source/proc/vis_update()
+ EFFECT_UPDATE(LIGHTING_VIS_UPDATE)
+
+// Decompile the hexadecimal colour into lumcounts of each perspective.
+/datum/light_source/proc/parse_light_color()
+ if (light_color)
+ lum_r = GetRedPart (light_color) / 255
+ lum_g = GetGreenPart (light_color) / 255
+ lum_b = GetBluePart (light_color) / 255
+ else
+ lum_r = 1
+ lum_g = 1
+ lum_b = 1
+
+// Macro that applies light to a new corner.
+// It is a macro in the interest of speed, yet not having to copy paste it.
+// If you're wondering what's with the backslashes, the backslashes cause BYOND to not automatically end the line.
+// As such this all gets counted as a single line.
+// The braces and semicolons are there to be able to do this on a single line.
+
+//Original lighting falloff calculation. This looks the best out of the three. However, this is also the most expensive.
+//#define LUM_FALLOFF(C, T) (1 - CLAMP01(sqrt((C.x - T.x) ** 2 + (C.y - T.y) ** 2 + LIGHTING_HEIGHT) / max(1, light_range)))
+
+//Cubic lighting falloff. This has the *exact* same range as the original lighting falloff calculation, down to the exact decimal, but it looks a little unnatural due to the harsher falloff and how it's generally brighter across the board.
+//#define LUM_FALLOFF(C, T) (1 - CLAMP01((((C.x - T.x) * (C.x - T.x)) + ((C.y - T.y) * (C.y - T.y)) + LIGHTING_HEIGHT) / max(1, light_range*light_range)))
+
+//Linear lighting falloff. This resembles the original lighting falloff calculation the best, but results in lights having a slightly larger range, which is most noticable with large light sources. This also results in lights being diamond-shaped, fuck. This looks the darkest out of the three due to how lights are brighter closer to the source compared to the original falloff algorithm. This falloff method also does not at all take into account lighting height, as it acts as a flat reduction to light range with this method.
+//#define LUM_FALLOFF(C, T) (1 - CLAMP01(((abs(C.x - T.x) + abs(C.y - T.y))) / max(1, light_range+1)))
+
+//Linear lighting falloff but with an octagonal shape in place of a diamond shape. Lummox JR please add pointer support.
+#define GET_LUM_DIST(DISTX, DISTY) (DISTX + DISTY + abs(DISTX - DISTY)*0.4)
+#define LUM_FALLOFF(C, T) (1 - CLAMP01(max(GET_LUM_DIST(abs(C.x - T.x), abs(C.y - T.y)),LIGHTING_HEIGHT) / max(1, light_range+1)))
+
+#define APPLY_CORNER(C) \
+ . = LUM_FALLOFF(C, pixel_turf); \
+ . *= light_power; \
+ var/OLD = effect_str[C]; \
+ effect_str[C] = .; \
+ \
+ C.update_lumcount \
+ ( \
+ (. * lum_r) - (OLD * applied_lum_r), \
+ (. * lum_g) - (OLD * applied_lum_g), \
+ (. * lum_b) - (OLD * applied_lum_b) \
+ );
+
+#define REMOVE_CORNER(C) \
+ . = -effect_str[C]; \
+ C.update_lumcount \
+ ( \
+ . * applied_lum_r, \
+ . * applied_lum_g, \
+ . * applied_lum_b \
+ );
+
+// This is the define used to calculate falloff.
+
+/datum/light_source/proc/remove_lum()
+ applied = FALSE
+ var/thing
+ for (thing in affecting_turfs)
+ var/turf/T = thing
+ LAZYREMOVE(T.affecting_lights, src)
+
+ affecting_turfs = null
+
+ var/datum/lighting_corner/C
+ for (thing in effect_str)
+ C = thing
+ REMOVE_CORNER(C)
+
+ LAZYREMOVE(C.affecting, src)
+
+ effect_str = null
+
+/datum/light_source/proc/recalc_corner(var/datum/lighting_corner/C)
+ LAZYINITLIST(effect_str)
+ if (effect_str[C]) // Already have one.
+ REMOVE_CORNER(C)
+ effect_str[C] = 0
+
+ APPLY_CORNER(C)
+ UNSETEMPTY(effect_str)
+
+/datum/light_source/proc/update_corners()
+ var/update = FALSE
+ var/atom/source_atom = src.source_atom
+
+ if (QDELETED(source_atom))
+ qdel(src)
+ return
+
+ if (source_atom.light_power != light_power)
+ light_power = source_atom.light_power
+ update = TRUE
+
+ if (source_atom.light_range != light_range)
+ light_range = source_atom.light_range
+ update = TRUE
+
+ if (!top_atom)
+ top_atom = source_atom
+ update = TRUE
+
+ if (!light_range || !light_power)
+ qdel(src)
+ return
+
+ if (isturf(top_atom))
+ if (source_turf != top_atom)
+ source_turf = top_atom
+ UPDATE_APPROXIMATE_PIXEL_TURF
+ update = TRUE
+ else if (top_atom.loc != source_turf)
+ source_turf = top_atom.loc
+ UPDATE_APPROXIMATE_PIXEL_TURF
+ update = TRUE
+
+ if (!isturf(source_turf))
+ if (applied)
+ remove_lum()
+ return
+
+ if (light_range && light_power && !applied)
+ update = TRUE
+
+ if (source_atom.light_color != light_color)
+ light_color = source_atom.light_color
+ parse_light_color()
+ update = TRUE
+
+ else if (applied_lum_r != lum_r || applied_lum_g != lum_g || applied_lum_b != lum_b)
+ update = TRUE
+
+ if (update)
+ needs_update = LIGHTING_CHECK_UPDATE
+ applied = TRUE
+ else if (needs_update == LIGHTING_CHECK_UPDATE)
+ return //nothing's changed
+
+ var/list/datum/lighting_corner/corners = list()
+ var/list/turf/turfs = list()
+ var/thing
+ var/datum/lighting_corner/C
+ var/turf/T
+ if (source_turf)
+ var/oldlum = source_turf.luminosity
+ source_turf.luminosity = CEILING(light_range, 1)
+ for(T in view(CEILING(light_range, 1), source_turf))
+ for (thing in T.get_corners(source_turf))
+ C = thing
+ corners[C] = 0
+ turfs += T
+ source_turf.luminosity = oldlum
+
+ LAZYINITLIST(affecting_turfs)
+ var/list/L = turfs - affecting_turfs // New turfs, add us to the affecting lights of them.
+ affecting_turfs += L
+ for (thing in L)
+ T = thing
+ LAZYADD(T.affecting_lights, src)
+
+ L = affecting_turfs - turfs // Now-gone turfs, remove us from the affecting lights.
+ affecting_turfs -= L
+ for (thing in L)
+ T = thing
+ LAZYREMOVE(T.affecting_lights, src)
+
+ LAZYINITLIST(effect_str)
+ if (needs_update == LIGHTING_VIS_UPDATE)
+ for (thing in corners - effect_str) // New corners
+ C = thing
+ LAZYADD(C.affecting, src)
+ if (!C.active)
+ effect_str[C] = 0
+ continue
+ APPLY_CORNER(C)
+ else
+ L = corners - effect_str
+ for (thing in L) // New corners
+ C = thing
+ LAZYADD(C.affecting, src)
+ if (!C.active)
+ effect_str[C] = 0
+ continue
+ APPLY_CORNER(C)
+
+ for (thing in corners - L) // Existing corners
+ C = thing
+ if (!C.active)
+ effect_str[C] = 0
+ continue
+ APPLY_CORNER(C)
+
+ L = effect_str - corners
+ for (thing in L) // Old, now gone, corners.
+ C = thing
+ REMOVE_CORNER(C)
+ LAZYREMOVE(C.affecting, src)
+ effect_str -= L
+
+ applied_lum_r = lum_r
+ applied_lum_g = lum_g
+ applied_lum_b = lum_b
+
+ UNSETEMPTY(effect_str)
+ UNSETEMPTY(affecting_turfs)
+
+#undef EFFECT_UPDATE
+#undef LUM_FALLOFF
+#undef GET_LUM_DIST
+#undef REMOVE_CORNER
+#undef APPLY_CORNER
diff --git a/code/modules/mining/machine_silo.dm b/code/modules/mining/machine_silo.dm
index bd1da0a90f..8fdc50d2ac 100644
--- a/code/modules/mining/machine_silo.dm
+++ b/code/modules/mining/machine_silo.dm
@@ -197,7 +197,7 @@ GLOBAL_LIST_EMPTY(silo_access_logs)
var/list/materials
/datum/ore_silo_log/New(obj/machinery/M, _action, _amount, _noun, list/mats=list())
- timestamp = station_time_timestamp()
+ timestamp = STATION_TIME_TIMESTAMP("hh:mm:ss")
machine_name = M.name
area_name = get_area_name(M, TRUE)
action = _action
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index 12fe984d6f..55b8891534 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -18,6 +18,7 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
invisibility = INVISIBILITY_OBSERVER
hud_type = /datum/hud/ghost
var/can_reenter_corpse
+ var/can_reenter_round = TRUE
var/datum/hud/living/carbon/hud = null // hud
var/bootime = 0
var/started_as_observer //This variable is set to 1 when you enter the game as an observer.
@@ -266,6 +267,7 @@ Works together with spawning an observer, noted above.
var/mob/dead/observer/ghost = new(src) // Transfer safety to observer spawning proc.
SStgui.on_transfer(src, ghost) // Transfer NanoUIs.
ghost.can_reenter_corpse = can_reenter_corpse
+ ghost.can_reenter_round = (can_reenter_corpse && !suiciding)
ghost.key = key
return ghost
@@ -280,7 +282,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
// CITADEL EDIT
if(istype(loc, /obj/machinery/cryopod))
- var/response = alert(src, "Are you -sure- you want to ghost?\n(You are alive. If you ghost whilst still alive you may not play again this round! You can't change your mind so choose wisely!!)","Are you sure you want to ghost?","Ghost","Stay in body")
+ var/response = alert(src, "Are you -sure- you want to ghost?\n(You are alive. If you ghost whilst still alive you won't be able to re-enter this round! You can't change your mind so choose wisely!!)","Are you sure you want to ghost?","Ghost","Stay in body")
if(response != "Ghost")//darn copypaste
return
var/obj/machinery/cryopod/C = loc
@@ -293,17 +295,18 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
if(stat == DEAD)
ghostize(1)
else
- var/response = alert(src, "Are you -sure- you want to ghost?\n(You are alive. If you ghost whilst still alive you may not play again this round! You can't change your mind so choose wisely!!)","Are you sure you want to ghost?","Ghost","Stay in body")
+ var/response = alert(src, "Are you -sure- you want to ghost?\n(You are alive. If you ghost whilst still alive you won't be able to re-enter this round! You can't change your mind so choose wisely!!)","Are you sure you want to ghost?","Ghost","Stay in body")
if(response != "Ghost")
return //didn't want to ghost after-all
ghostize(0) //0 parameter is so we can never re-enter our body, "Charlie, you can never come baaaack~" :3
+ suicide_log(TRUE)
/mob/camera/verb/ghost()
set category = "OOC"
set name = "Ghost"
set desc = "Relinquish your life and enter the land of the dead."
- var/response = alert(src, "Are you -sure- you want to ghost?\n(You are alive. If you ghost whilst still alive you may not play again this round! You can't change your mind so choose wisely!!)","Are you sure you want to ghost?","Ghost","Stay in body")
+ var/response = alert(src, "Are you -sure- you want to ghost?\n(You are alive. If you ghost whilst still alive you won't be able to re-enter this round! You can't change your mind so choose wisely!!)","Are you sure you want to ghost?","Ghost","Stay in body")
if(response != "Ghost")
return
ghostize(0)
@@ -617,6 +620,10 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
to_chat(src, "This isn't really a creature, now is it!")
return 0
+ if(!can_reenter_round)
+ to_chat(src, "You are unable to re-enter the round.")
+ return FALSE
+
if(can_reenter_corpse && mind && mind.current)
if(alert(src, "Your soul is still tied to your former life as [mind.current.name], if you go forward there is no going back to that life. Are you sure you wish to continue?", "Move On", "Yes", "No") == "No")
return 0
diff --git a/code/modules/mob/living/carbon/carbon_defines.dm b/code/modules/mob/living/carbon/carbon_defines.dm
index cc01a83a15..f782289e18 100644
--- a/code/modules/mob/living/carbon/carbon_defines.dm
+++ b/code/modules/mob/living/carbon/carbon_defines.dm
@@ -34,6 +34,7 @@
var/failed_last_breath = 0 //This is used to determine if the mob failed a breath. If they did fail a brath, they will attempt to breathe each tick, otherwise just once per 4 ticks.
var/co2overloadtime = null
+ var/o2overloadtime = null //for Ash walker's weaker lungs, and future atmosia hazards
var/temperature_resistance = T0C+75
var/obj/item/reagent_containers/food/snacks/meat/slab/type_of_meat = /obj/item/reagent_containers/food/snacks/meat/slab
diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm
index 77cf6fc4bb..c6ba900b13 100644
--- a/code/modules/mob/living/carbon/human/examine.dm
+++ b/code/modules/mob/living/carbon/human/examine.dm
@@ -341,8 +341,13 @@
else if(isobserver(user) && traitstring)
msg += "Traits: [traitstring]
"
- if(print_flavor_text() && get_visible_name() != "Unknown")//Are we sure we know who this is? Don't show flavor text unless we can recognize them. Prevents certain metagaming with impersonation.
- msg += "[print_flavor_text()]\n"
+ if(print_flavor_text())
+ if(get_visible_name() == "Unknown") //Are we sure we know who this is? Don't show flavor text unless we can recognize them. Prevents certain metagaming with impersonation.
+ msg += "...?
"
+ else if(skipface) //Sometimes we're not unknown, but impersonating someone in a hardsuit, let's not reveal our flavor text then either.
+ msg += "...?
"
+ else
+ msg += "[print_flavor_text()]\n"
msg += "*---------*"
to_chat(user, msg)
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index fa66daf143..49d2722323 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -84,7 +84,7 @@
var/obj/item/clothing/suit/space/space_ninja/SN = wear_suit
if(statpanel("SpiderOS"))
stat("SpiderOS Status:","[SN.s_initialized ? "Initialized" : "Disabled"]")
- stat("Current Time:", "[station_time_timestamp()]")
+ stat("Current Time:", "[STATION_TIME_TIMESTAMP("hh:mm:ss")]")
if(SN.s_initialized)
//Suit gear
stat("Energy Charge:", "[round(SN.cell.charge/100)]%")
@@ -438,7 +438,7 @@
return
else if(!istype(H.glasses, /obj/item/clothing/glasses/hud/security) && !istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud/security))
return
- var/crime = GLOB.data_core.createCrimeEntry(t1, t2, allowed_access, station_time_timestamp())
+ var/crime = GLOB.data_core.createCrimeEntry(t1, t2, allowed_access, STATION_TIME_TIMESTAMP("hh:mm:ss"))
GLOB.data_core.addMinorCrime(R.fields["id"], crime)
investigate_log("New Minor Crime: [t1]: [t2] | Added to [R.fields["name"]] by [key_name(usr)]", INVESTIGATE_RECORDS)
to_chat(usr, "Successfully added a minor crime.")
@@ -454,7 +454,7 @@
return
else if (!istype(H.glasses, /obj/item/clothing/glasses/hud/security) && !istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud/security))
return
- var/crime = GLOB.data_core.createCrimeEntry(t1, t2, allowed_access, station_time_timestamp())
+ var/crime = GLOB.data_core.createCrimeEntry(t1, t2, allowed_access, STATION_TIME_TIMESTAMP("hh:mm:ss"))
GLOB.data_core.addMajorCrime(R.fields["id"], crime)
investigate_log("New Major Crime: [t1]: [t2] | Added to [R.fields["name"]] by [key_name(usr)]", INVESTIGATE_RECORDS)
to_chat(usr, "Successfully added a major crime.")
@@ -487,7 +487,7 @@
var/counter = 1
while(R.fields[text("com_[]", counter)])
counter++
- R.fields[text("com_[]", counter)] = text("Made by [] on [] [], []
[]", allowed_access, station_time_timestamp(), time2text(world.realtime, "MMM DD"), GLOB.year_integer+540, t1)
+ R.fields[text("com_[]", counter)] = text("Made by [] on [] [], []
[]", allowed_access, STATION_TIME_TIMESTAMP("hh:mm:ss"), time2text(world.realtime, "MMM DD"), GLOB.year_integer+540, t1)
to_chat(usr, "Successfully added comment.")
return
to_chat(usr, "Unable to locate a data core entry for this person.")
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index ce906565d2..b66ebdb001 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -4,7 +4,9 @@
if(def_zone)
if(isbodypart(def_zone))
- return checkarmor(def_zone, type)
+ var/obj/item/bodypart/bp = def_zone
+ if(bp.body_part)
+ return checkarmor(def_zone, type)
var/obj/item/bodypart/affecting = get_bodypart(ran_zone(def_zone))
return checkarmor(affecting, type)
//If a specific bodypart is targetted, check how that bodypart is protected and return the value.
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index 0e5eef2ffb..3fd710fcc6 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -1143,7 +1143,15 @@ GLOBAL_LIST_EMPTY(roundstart_races)
H.add_trait(TRAIT_FAT, OBESITY)
H.update_inv_w_uniform()
H.update_inv_wear_suit()
-
+
+ if(H.noisy && H.nutrition <= NUTRITION_LEVEL_STARVING)
+ if(prob(10))
+ playsound(get_turf(H),"hunger_sounds",35,0,-5,1,ignore_walls = FALSE,channel=CHANNEL_PRED)
+
+ else if(H.noisy && H.nutrition <= NUTRITION_LEVEL_HUNGRY)
+ if(prob(10))
+ playsound(get_turf(H),"hunger_sounds",15,0,-5,1,ignore_walls = FALSE,channel=CHANNEL_PRED)
+
// nutrition decrease and satiety
if (H.nutrition > 0 && H.stat != DEAD && !H.has_trait(TRAIT_NOHUNGER))
// THEY HUNGER
@@ -1653,13 +1661,19 @@ GLOBAL_LIST_EMPTY(roundstart_races)
var/obj/item/bodypart/BP = null
if(isbodypart(def_zone))
- BP = def_zone
+ if(damagetype == STAMINA && istype(def_zone, /obj/item/bodypart/head))
+ BP = H.get_bodypart(check_zone(BODY_ZONE_CHEST))
+ else
+ BP = def_zone
else
if(!def_zone)
def_zone = ran_zone(def_zone)
+ if(damagetype == STAMINA && def_zone == BODY_ZONE_HEAD)
+ def_zone = BODY_ZONE_CHEST
BP = H.get_bodypart(check_zone(def_zone))
- if(!BP)
- BP = H.bodyparts[1]
+
+ if(!BP)
+ BP = H.bodyparts[1]
switch(damagetype)
if(BRUTE)
diff --git a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
index e3164e0dcb..b851552dc5 100644
--- a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
@@ -91,6 +91,7 @@
id = "ashlizard"
limbs_id = "lizard"
species_traits = list(MUTCOLORS,EYECOLOR,LIPS,DIGITIGRADE)
- inherent_traits = list(TRAIT_NOGUNS,TRAIT_NOBREATH)
+ inherent_traits = list(TRAIT_NOGUNS)
+ mutantlungs = /obj/item/organ/lungs/ashwalker
burnmod = 0.9
brutemod = 0.9
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm
index a2f6a469d9..48a401802b 100644
--- a/code/modules/mob/living/carbon/life.dm
+++ b/code/modules/mob/living/carbon/life.dm
@@ -141,6 +141,7 @@
return 0
var/safe_oxy_min = 16
+ var/safe_oxy_max = 50
var/safe_co2_max = 10
var/safe_tox_max = 0.05
var/SA_para_min = 1
@@ -156,6 +157,19 @@
//OXYGEN
+ if(O2_partialpressure > safe_oxy_max) // Too much Oxygen - blatant CO2 effect copy/pasta
+ if(!o2overloadtime)
+ o2overloadtime = world.time
+ else if(world.time - o2overloadtime > 120)
+ Dizzy(10) // better than a minute of you're fucked KO, but certainly a wake up call. Honk.
+ adjustOxyLoss(3)
+ if(world.time - o2overloadtime > 300)
+ adjustOxyLoss(8)
+ if(prob(20))
+ emote("cough")
+ throw_alert("too_much_oxy", /obj/screen/alert/too_much_oxy)
+ SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "suffocation", /datum/mood_event/suffocation)
+
if(O2_partialpressure < safe_oxy_min) //Not enough oxygen
if(prob(20))
emote("gasp")
@@ -172,6 +186,7 @@
else //Enough oxygen
failed_last_breath = 0
+ o2overloadtime = 0 //reset our counter for this too
if(health >= crit_threshold)
adjustOxyLoss(-5)
oxygen_used = breath_gases[/datum/gas/oxygen][MOLES]
diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm
index f6888a21ec..f16572c5d0 100644
--- a/code/modules/mob/living/death.dm
+++ b/code/modules/mob/living/death.dm
@@ -8,6 +8,8 @@
spill_organs(no_brain, no_organs, no_bodyparts)
+ release_vore_contents(silent = TRUE) // return of the bomb safe internals.
+
if(!no_bodyparts)
spread_bodyparts(no_brain, no_organs)
@@ -36,6 +38,7 @@
buckled.unbuckle_mob(src, force = TRUE)
dust_animation()
+ release_vore_contents(silent = TRUE) //technically grief protection, I guess? if they're SM'd it doesn't matter seconds after anyway.
spawn_dust(just_ash)
QDEL_IN(src,5) // since this is sometimes called in the middle of movement, allow half a second for movement to finish, ghosting to happen and animation to play. Looks much nicer and doesn't cause multiple runtimes.
@@ -50,7 +53,7 @@
stat = DEAD
unset_machine()
timeofdeath = world.time
- tod = station_time_timestamp()
+ tod = STATION_TIME_TIMESTAMP("hh:mm:ss")
var/turf/T = get_turf(src)
for(var/obj/item/I in contents)
I.on_mob_death(src, gibbed)
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 0e67ce7510..b9b6424092 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -312,7 +312,8 @@
return TRUE
/mob/living/verb/succumb(whispered as null)
- set hidden = TRUE
+ set name = "Succumb"
+ set category = "IC"
if (InCritical())
log_message("Has [whispered ? "whispered his final words" : "succumbed to death"] while in [InFullCritical() ? "hard":"soft"] critical with [round(health, 0.1)] points of health!", LOG_ATTACK)
adjustOxyLoss(health - HEALTH_THRESHOLD_DEAD)
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index f5b44db70f..643c91b95a 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -127,8 +127,18 @@
IgniteMob()
/mob/living/proc/grabbedby(mob/living/carbon/user, supress_message = 0)
- if(user == src || anchored || !isturf(user.loc))
+ if(user == anchored || !isturf(user.loc))
return FALSE
+
+ if(user.pulling && user.grab_state == GRAB_AGGRESSIVE && user.voremode)
+ if(ismob(user.pulling))
+ var/mob/P = user.pulling
+ user.vore_attack(user, P, src) // User, Pulled, Predator target (which can be user, pulling, or src)
+ return
+
+ if(user == src) //we want to be able to self click if we're voracious
+ return FALSE
+
if(!user.pulling || user.pulling != src)
user.start_pulling(src, supress_message)
return
@@ -163,6 +173,8 @@
return 0
if(!user.pulling || user.pulling != src || user.grab_state != old_grab_state || user.a_intent != INTENT_GRAB)
return 0
+ if(user.voremode && user.grab_state == GRAB_AGGRESSIVE)
+ return 0
user.grab_state++
switch(user.grab_state)
if(GRAB_AGGRESSIVE)
diff --git a/code/modules/mob/living/login.dm b/code/modules/mob/living/login.dm
index 611d9dbc4c..046d25a4b2 100644
--- a/code/modules/mob/living/login.dm
+++ b/code/modules/mob/living/login.dm
@@ -22,4 +22,6 @@
to_chat(src, "You can ventcrawl! Use alt+click on vents to quickly travel about the station.")
if(ranged_ability)
- ranged_ability.add_ranged_ability(src, "You currently have [ranged_ability] active!")
\ No newline at end of file
+ ranged_ability.add_ranged_ability(src, "You currently have [ranged_ability] active!")
+ if(vore_init && !vorepref_init) //Vore's been initialized, voreprefs haven't. If this triggers then that means that voreprefs failed to load due to the client being missing.
+ apply_vore_prefs()
diff --git a/code/modules/mob/living/simple_animal/friendly/mouse.dm b/code/modules/mob/living/simple_animal/friendly/mouse.dm
index 8c6300fab5..bf45c9cc73 100644
--- a/code/modules/mob/living/simple_animal/friendly/mouse.dm
+++ b/code/modules/mob/living/simple_animal/friendly/mouse.dm
@@ -63,6 +63,9 @@
..()
/mob/living/simple_animal/mouse/handle_automated_action()
+ if(isbelly(loc))
+ return
+
if(prob(chew_probability))
var/turf/open/floor/F = get_turf(src)
if(istype(F) && !F.intact)
diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm
index d8635060ee..941ba0ec69 100644
--- a/code/modules/mob/living/simple_animal/hostile/hostile.dm
+++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm
@@ -100,7 +100,7 @@
if(!target || !isturf(target.loc) || !isturf(loc) || stat == DEAD)
return
var/target_dir = get_dir(src,target)
-
+
var/static/list/cardinal_sidestep_directions = list(-90,-45,0,45,90)
var/static/list/diagonal_sidestep_directions = list(-45,0,45)
var/chosen_dir = 0
@@ -202,6 +202,8 @@
if(see_invisible < the_target.invisibility)//Target's invisible to us, forget it
return FALSE
+ if(isbelly(the_target.loc)) //Target's inside a gut, forget about it too
+ return FALSE
if(search_objects < 2)
if(isliving(the_target))
var/mob/living/L = the_target
@@ -301,7 +303,7 @@
if(target)
if(targets_from && isturf(targets_from.loc) && target.Adjacent(targets_from)) //If they're next to us, attack
MeleeAction()
- else
+ else
if(rapid_melee > 1 && target_distance <= melee_queue_distance)
MeleeAction(FALSE)
in_melee = FALSE //If we're just preparing to strike do not enter sidestep mode
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm
index 43bc2c26f8..92ac07b1a0 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm
@@ -83,18 +83,12 @@
if(L.stat != DEAD)
if(!client && ranged && ranged_cooldown <= world.time)
OpenFire()
-
- else if(L.stat >= SOFT_CRIT)
- if(vore_active == TRUE && L.devourable == TRUE)
- if(isliving(target) && !target.Adjacent(targets_from))
- return
- else
+ if(L.Adjacent(src) && (L.stat != CONSCIOUS))
+ if(vore_active && L.devourable == TRUE)
dragon_feeding(src,L)
- if(L.loc == src.contents)
- LoseTarget()
- return 0
- else
- devour(L)
+ LoseTarget()
+ else
+ devour(L)
/mob/living/simple_animal/hostile/megafauna/proc/devour(mob/living/L)
if(!L)
diff --git a/code/modules/mob/living/status_procs.dm b/code/modules/mob/living/status_procs.dm
index 43abe7c825..d1c72069ac 100644
--- a/code/modules/mob/living/status_procs.dm
+++ b/code/modules/mob/living/status_procs.dm
@@ -284,5 +284,5 @@
emote("deathgasp")
add_trait(TRAIT_FAKEDEATH, source)
add_trait(TRAIT_DEATHCOMA, source)
- tod = station_time_timestamp()
+ tod = STATION_TIME_TIMESTAMP("hh:mm:ss")
update_stat()
\ No newline at end of file
diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm
index 8d1b4b18d4..4d5d75c7e6 100644
--- a/code/modules/mob/login.dm
+++ b/code/modules/mob/login.dm
@@ -43,12 +43,13 @@
if(client)
client.change_view(CONFIG_GET(string/default_view)) // Resets the client.view in case it was changed.
- if(client.player_details.player_actions.len)
- for(var/datum/action/A in client.player_details.player_actions)
- A.Grant(src)
+ if(client.player_details && istype(client.player_details))
+ if(client.player_details.player_actions.len)
+ for(var/datum/action/A in client.player_details.player_actions)
+ A.Grant(src)
- for(var/foo in client.player_details.post_login_callbacks)
- var/datum/callback/CB = foo
- CB.Invoke()
+ for(var/foo in client.player_details.post_login_callbacks)
+ var/datum/callback/CB = foo
+ CB.Invoke()
log_message("Client [key_name(src)] has taken ownership of mob [src]([src.type])", LOG_OWNERSHIP)
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 30e58ebb44..9d662b1673 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -533,8 +533,8 @@
stat(null, "Next Map: [cached.map_name]")
stat(null, "Round ID: [GLOB.round_id ? GLOB.round_id : "NULL"]")
stat(null, "Server Time: [time2text(world.timeofday, "YYYY-MM-DD hh:mm:ss")]")
- stat(null, "Round Time: [worldtime2text()]")
- stat(null, "Station Time: [station_time_timestamp()]")
+ stat(null, "Round Time: [WORLDTIME2TEXT("hh:mm:ss")]")
+ stat(null, "Station Time: [STATION_TIME_TIMESTAMP("hh:mm:ss")]")
stat(null, "Time Dilation: [round(SStime_track.time_dilation_current,1)]% AVG:([round(SStime_track.time_dilation_avg_fast,1)]%, [round(SStime_track.time_dilation_avg,1)]%, [round(SStime_track.time_dilation_avg_slow,1)]%)")
if(SSshuttle.emergency)
var/ETA = SSshuttle.emergency.getModeStr()
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index fa908db729..35b48afa28 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -29,27 +29,10 @@
/proc/ran_zone(zone, probability = 80)
-
- zone = check_zone(zone)
-
if(prob(probability))
- return zone
-
- var/t = rand(1, 18) // randomly pick a different zone, or maybe the same one
- switch(t)
- if(1)
- return BODY_ZONE_HEAD
- if(2)
- return BODY_ZONE_CHEST
- if(3 to 6)
- return BODY_ZONE_L_ARM
- if(7 to 10)
- return BODY_ZONE_R_ARM
- if(11 to 14)
- return BODY_ZONE_L_LEG
- if(15 to 18)
- return BODY_ZONE_R_LEG
-
+ zone = check_zone(zone)
+ else
+ zone = pickweight(list(BODY_ZONE_HEAD = 6, BODY_ZONE_CHEST = 6, BODY_ZONE_L_ARM = 22, BODY_ZONE_R_ARM = 22, BODY_ZONE_L_LEG = 22, BODY_ZONE_R_LEG = 22))
return zone
/proc/above_neck(zone)
@@ -491,7 +474,7 @@ It's fairly easy to fix if dealing with single letters but not so much with comp
else
colored_message = "[message]"
- var/list/timestamped_message = list("[LAZYLEN(logging[smessage_type]) + 1]\[[time_stamp()]\] [key_name(src)] [loc_name(src)]" = colored_message)
+ var/list/timestamped_message = list("[LAZYLEN(logging[smessage_type]) + 1]\[[TIME_STAMP("hh:mm:ss", FALSE)]\] [key_name(src)] [loc_name(src)]" = colored_message)
logging[smessage_type] += timestamped_message
diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm
index 342ee386b6..5703e2190b 100644
--- a/code/modules/mob/transform_procs.dm
+++ b/code/modules/mob/transform_procs.dm
@@ -96,7 +96,9 @@
mind.transfer_to(O)
var/datum/antagonist/changeling/changeling = O.mind.has_antag_datum(/datum/antagonist/changeling)
if(changeling)
- changeling.purchasedpowers += new /obj/effect/proc_holder/changeling/humanform(null)
+ var/obj/effect/proc_holder/changeling/humanform/HF = new /obj/effect/proc_holder/changeling/humanform(null)
+ changeling.purchasedpowers += HF
+ HF.action.Grant(O)
for(var/X in internal_organs)
var/obj/item/organ/I = X
@@ -129,8 +131,9 @@
mind.transfer_to(O)
var/datum/antagonist/changeling/changeling = O.mind.has_antag_datum(/datum/antagonist/changeling)
if(changeling)
- changeling.purchasedpowers += new /obj/effect/proc_holder/changeling/humanform(null)
-
+ var/obj/effect/proc_holder/changeling/humanform/HF = new /obj/effect/proc_holder/changeling/humanform(null)
+ changeling.purchasedpowers += HF
+ HF.action.Grant(O)
if (tr_flags & TR_DEFAULTMSG)
to_chat(O, "You are now a monkey.")
diff --git a/code/modules/modular_computers/NTNet/NTNRC/conversation.dm b/code/modules/modular_computers/NTNet/NTNRC/conversation.dm
index 20ad05b8e3..7c4058ef39 100644
--- a/code/modules/modular_computers/NTNet/NTNRC/conversation.dm
+++ b/code/modules/modular_computers/NTNet/NTNRC/conversation.dm
@@ -19,12 +19,12 @@
return ..()
/datum/ntnet_conversation/proc/add_message(message, username)
- message = "[station_time_timestamp()] [username]: [message]"
+ message = "[STATION_TIME_TIMESTAMP("hh:mm:ss")] [username]: [message]"
messages.Add(message)
trim_message_list()
/datum/ntnet_conversation/proc/add_status_message(message)
- messages.Add("[station_time_timestamp()] -!- [message]")
+ messages.Add("[STATION_TIME_TIMESTAMP("hh:mm:ss")] -!- [message]")
trim_message_list()
/datum/ntnet_conversation/proc/trim_message_list()
diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm
index bc5c711e60..580374c5c0 100644
--- a/code/modules/modular_computers/computers/item/computer.dm
+++ b/code/modules/modular_computers/computers/item/computer.dm
@@ -333,7 +333,7 @@
data["PC_programheaders"] = program_headers
- data["PC_stationtime"] = station_time_timestamp()
+ data["PC_stationtime"] = STATION_TIME_TIMESTAMP("hh:mm:ss")
data["PC_hasheader"] = 1
data["PC_showexitprogram"] = active_program ? 1 : 0 // Hides "Exit Program" button on mainscreen
return data
diff --git a/code/modules/modular_computers/file_system/programs/card.dm b/code/modules/modular_computers/file_system/programs/card.dm
index def388c574..f8372dd2df 100644
--- a/code/modules/modular_computers/file_system/programs/card.dm
+++ b/code/modules/modular_computers/file_system/programs/card.dm
@@ -159,7 +159,7 @@
[GLOB.data_core ? GLOB.data_core.get_manifest(0) : ""]
"}
- if(!printer.print_text(contents,text("crew manifest ([])", station_time_timestamp())))
+ if(!printer.print_text(contents,text("crew manifest ([])", STATION_TIME_TIMESTAMP("hh:mm:ss"))))
to_chat(usr, "Hardware error: Printer was unable to print the file. It may be out of paper.")
return
else
diff --git a/code/modules/modular_computers/file_system/programs/file_browser.dm b/code/modules/modular_computers/file_system/programs/file_browser.dm
index fb90ecbfad..090d64e9d7 100644
--- a/code/modules/modular_computers/file_system/programs/file_browser.dm
+++ b/code/modules/modular_computers/file_system/programs/file_browser.dm
@@ -145,7 +145,7 @@
t = replacetext(t, "\[/i\]", "")
t = replacetext(t, "\[u\]", "")
t = replacetext(t, "\[/u\]", "")
- t = replacetext(t, "\[time\]", "[station_time_timestamp()]")
+ t = replacetext(t, "\[time\]", "[STATION_TIME_TIMESTAMP("hh:mm:ss")]")
t = replacetext(t, "\[date\]", "[time2text(world.realtime, "MMM DD")] [GLOB.year_integer+540]")
t = replacetext(t, "\[large\]", "")
t = replacetext(t, "\[/large\]", "")
diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm
index 1391e9ce42..efae090707 100644
--- a/code/modules/projectiles/projectile.dm
+++ b/code/modules/projectiles/projectile.dm
@@ -18,6 +18,7 @@
var/def_zone = "" //Aiming at
var/atom/movable/firer = null//Who shot it
var/suppressed = FALSE //Attack message
+ var/candink = FALSE //Can this projectile play the dink sound when hitting the head?
var/yo = null
var/xo = null
var/atom/original = null // the original target clicked
@@ -185,7 +186,7 @@
playsound(loc, hitsound, volume, 1, -1)
L.visible_message("[L] is hit by \a [src][organ_hit_text]!", \
"[L] is hit by \a [src][organ_hit_text]!", null, COMBAT_MESSAGE_RANGE)
- if(def_zone == BODY_ZONE_HEAD)
+ if(candink && def_zone == BODY_ZONE_HEAD)
playsound(src, 'sound/weapons/dink.ogg', 30, 1)
L.on_hit(src)
diff --git a/code/modules/projectiles/projectile/bullets.dm b/code/modules/projectiles/projectile/bullets.dm
index 725ef9baa6..7daeca91ea 100644
--- a/code/modules/projectiles/projectile/bullets.dm
+++ b/code/modules/projectiles/projectile/bullets.dm
@@ -4,6 +4,7 @@
damage = 60
damage_type = BRUTE
nodamage = FALSE
+ candink = TRUE
flag = "bullet"
hitsound_wall = "ricochet"
impact_effect_type = /obj/effect/temp_visual/impact_effect
diff --git a/code/modules/projectiles/projectile/bullets/special.dm b/code/modules/projectiles/projectile/bullets/special.dm
index 091dff454c..38a570c75f 100644
--- a/code/modules/projectiles/projectile/bullets/special.dm
+++ b/code/modules/projectiles/projectile/bullets/special.dm
@@ -5,6 +5,7 @@
knockdown = 60
forcedodge = TRUE
nodamage = TRUE
+ candink = FALSE
hitsound = 'sound/items/bikehorn.ogg'
icon = 'icons/obj/hydroponics/harvest.dmi'
icon_state = "banana"
diff --git a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm
index 8cb5f6254c..7339f850d8 100644
--- a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm
+++ b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm
@@ -49,14 +49,33 @@
"bromine",
"stable_plasma"
)
- var/list/emagged_reagents = list(
- "space_drugs",
- "morphine",
- "carpotoxin",
+//these become available once upgraded.
+ var/list/upgrade_reagents = list(
+ "oil",
+ "ammonia"
+ )
+
+ var/list/upgrade_reagents2 = list(
+ "acetone",
+ "phenol",
+ "diethylamine"
+ )
+
+ var/list/upgrade_reagents3 = list(
+ "glycerol",
"mine_salve",
"toxin"
)
+ var/list/emagged_reagents = list(
+ "space_drugs",
+ "plasma",
+ "frostoil",
+ "carpotoxin",
+ "histamine",
+ "morphine"
+ )
+
var/list/saved_recipes = list()
/obj/machinery/chem_dispenser/Initialize()
@@ -74,7 +93,17 @@
..()
if(panel_open)
to_chat(user, "[src]'s maintenance hatch is open!")
-
+ if(in_range(user, src) || isobserver(user))
+ to_chat(user, "The status display reads:
Recharging [recharge_amount] power units per interval.
Power efficiency increased by [(powerefficiency*1000)-100]%.")
+ switch(macrotier)
+ if(1)
+ to_chat(user, "Macro granularity at 5u.")
+ if(2)
+ to_chat(user, "Macro granularity at 3u.")
+ if(3)
+ to_chat(user, "Macro granularity at 2u.")
+ if(4)
+ to_chat(user, "Macro granularity at 1u.")
/obj/machinery/chem_dispenser/process()
if (recharge_counter >= 4)
if(!is_operational())
@@ -111,7 +140,6 @@
add_overlay(beaker_overlay)
-
/obj/machinery/chem_dispenser/emag_act(mob/user)
if(obj_flags & EMAGGED)
to_chat(user, "[src] has no functional safeties to emag.")
@@ -346,6 +374,12 @@
for(var/obj/item/stock_parts/manipulator/M in component_parts)
if (M.rating > macrotier)
macrotier = M.rating
+ if (M.rating > 1)
+ dispensable_reagents |= upgrade_reagents
+ if (M.rating > 2)
+ dispensable_reagents |= upgrade_reagents2
+ if (M.rating > 3)
+ dispensable_reagents |= upgrade_reagents3
powerefficiency = round(newpowereff, 0.01)
@@ -447,14 +481,19 @@
"tomatojuice",
"lemonjuice",
"menthol"
- )
+ ) //prevents the soda machine from obtaining chemical upgrades. .
+ upgrade_reagents = null
+ upgrade_reagents2 = null
+ upgrade_reagents3 = null
emagged_reagents = list(
"thirteenloko",
+ "changelingsting",
"whiskeycola",
"mindbreaker",
"tirizene"
)
+
/obj/machinery/chem_dispenser/drinks/fullupgrade //fully ugpraded stock parts, emagged
desc = "Contains a large reservoir of soft drinks. This model has had its safeties shorted out."
obj_flags = CAN_BE_HIT | EMAGGED
@@ -497,13 +536,20 @@
"creme_de_cacao",
"triple_sec",
"sake"
- )
+ )//prevents the booze machine from obtaining chemical upgrades.
+ upgrade_reagents = null
+ upgrade_reagents2 = null
+ upgrade_reagents3 = null
emagged_reagents = list(
"ethanol",
"iron",
+ "alexander",
+ "clownstears",
"minttoxin",
"atomicbomb",
- "fernet"
+ "fernet",
+ "aphro",
+ "aphro+"
)
/obj/machinery/chem_dispenser/drinks/beer/fullupgrade //fully ugpraded stock parts, emagged
@@ -528,6 +574,7 @@
name = "mutagen dispenser"
desc = "Creates and dispenses mutagen."
dispensable_reagents = list("mutagen")
+ upgrade_reagents = null
emagged_reagents = list("plasma")
@@ -550,7 +597,11 @@
"ammonia",
"ash",
"diethylamine")
-
+ //same as above.
+ upgrade_reagents = null
+ upgrade_reagents2 = null
+ upgrade_reagents3 = null
+
/obj/machinery/chem_dispenser/mutagensaltpeter/Initialize()
. = ..()
component_parts = list()
diff --git a/code/modules/reagents/chemistry/reagents/drink_reagents.dm b/code/modules/reagents/chemistry/reagents/drink_reagents.dm
index 0cbd1f2d85..6ec37f3ec4 100644
--- a/code/modules/reagents/chemistry/reagents/drink_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/drink_reagents.dm
@@ -745,3 +745,37 @@
taste_description = "sweet pomegranates"
glass_name = "glass of grenadine"
glass_desc = "Delicious flavored syrup."
+
+/datum/reagent/consumable/pinkmilk
+ name = "Strawberry Milk"
+ id = "pinkmilk"
+ description = "A drink of a bygone era of milk and artificial sweetener back on a rock."
+ color = "#f76aeb"//rgb(247, 106, 235)
+ glass_icon_state = "pinkmilk"
+ quality = DRINK_FANTASTIC //Love drink
+ taste_description = "sweet strawberry and milk cream"
+ glass_name = "tall glass of strawberry milk"
+ glass_desc = "Delicious flavored strawberry syrup mixed with milk."
+
+/datum/reagent/consumable/tea/pinkmilk/on_mob_life(mob/living/carbon/M)
+ if(prob(15))
+ to_chat(M, "[pick("You cant help to smile.","You feel nostalgia all of sudden.","You remember to relax.")]")
+ ..()
+ . = 1
+
+/datum/reagent/consumable/pinktea //Tiny Tim song
+ name = "Strawberry Tea"
+ id = "pinktea"
+ description = "A timeless classic!"
+ color = "#f76aeb"//rgb(247, 106, 235)
+ glass_icon_state = "pinktea"
+ quality = DRINK_FANTASTIC //Love drink
+ taste_description = "sweet tea with a hint of strawberry"
+ glass_name = "mug of strawberry tea"
+ glass_desc = "Delicious traditional tea flavored with strawberries."
+
+/datum/reagent/consumable/tea/pinktea/on_mob_life(mob/living/carbon/M)
+ if(prob(10))
+ to_chat(M, "[pick("Diamond skies where white deer fly.","Sipping strawberry tea.","Silver raindrops drift through timeless, Neverending June.","Crystal ... pearls free, with love!","Beaming love into me.")]")
+ ..()
+ . = 1
diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm
index 4b4f118a9c..15a2ef1cba 100644
--- a/code/modules/reagents/chemistry/reagents/other_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm
@@ -1251,7 +1251,7 @@
L.remove_trait(TRAIT_SLEEPIMMUNE, id)
..()
-/datum/reagent/stimulum/on_mob_life(mob/living/carbon/M)
+/datum/reagent/stimulum/on_mob_life(mob/living/carbon/M)
M.adjustStaminaLoss(-2*REM, 0)
current_cycle++
holder.remove_reagent(id, 0.99) //Gives time for the next tick of life().
diff --git a/code/modules/reagents/chemistry/recipes/pyrotechnics.dm b/code/modules/reagents/chemistry/recipes/pyrotechnics.dm
index 587d6c8b38..29863c2b90 100644
--- a/code/modules/reagents/chemistry/recipes/pyrotechnics.dm
+++ b/code/modules/reagents/chemistry/recipes/pyrotechnics.dm
@@ -3,23 +3,25 @@
id = "reagent_explosion"
var/strengthdiv = 10
var/modifier = 0
+ var/noexplosion = FALSE
-/datum/chemical_reaction/reagent_explosion/on_reaction(datum/reagents/holder, created_volume)
- var/turf/T = get_turf(holder.my_atom)
- var/inside_msg
- if(ismob(holder.my_atom))
- var/mob/M = holder.my_atom
- inside_msg = " inside [ADMIN_LOOKUPFLW(M)]"
- var/lastkey = holder.my_atom.fingerprintslast
- var/touch_msg = "N/A"
- if(lastkey)
- var/mob/toucher = get_mob_by_key(lastkey)
- touch_msg = "[ADMIN_LOOKUPFLW(toucher)]"
- message_admins("Reagent explosion reaction occurred at [ADMIN_VERBOSEJMP(T)][inside_msg]. Last Fingerprint: [touch_msg].")
- log_game("Reagent explosion reaction occurred at [AREACOORD(T)]. Last Fingerprint: [lastkey ? lastkey : "N/A"]." )
- var/datum/effect_system/reagents_explosion/e = new()
- e.set_up(modifier + round(created_volume/strengthdiv, 1), T, 0, 0)
- e.start()
+/datum/chemical_reaction/reagent_explosion/on_reaction(datum/reagents/holder, created_volume, turf/override)
+ if(!noexplosion)
+ var/turf/T = override || get_turf(holder.my_atom)
+ var/inside_msg
+ if(ismob(holder.my_atom))
+ var/mob/M = holder.my_atom
+ inside_msg = " inside [ADMIN_LOOKUPFLW(M)]"
+ var/lastkey = holder.my_atom.fingerprintslast
+ var/touch_msg = "N/A"
+ if(lastkey)
+ var/mob/toucher = get_mob_by_key(lastkey)
+ touch_msg = "[ADMIN_LOOKUPFLW(toucher)]"
+ message_admins("Reagent explosion reaction occurred at [ADMIN_VERBOSEJMP(T)][inside_msg]. Last Fingerprint: [touch_msg].")
+ log_game("Reagent explosion reaction occurred at [AREACOORD(T)]. Last Fingerprint: [lastkey ? lastkey : "N/A"]." )
+ var/datum/effect_system/reagents_explosion/e = new()
+ e.set_up(modifier + round(created_volume/strengthdiv, 1), T, 0, 0)
+ e.start()
holder.clear_reagents()
@@ -56,6 +58,7 @@
required_reagents = list("holywater" = 1, "potassium" = 1)
/datum/chemical_reaction/reagent_explosion/potassium_explosion/holyboom/on_reaction(datum/reagents/holder, created_volume)
+ var/turf/T = get_turf(holder.my_atom)
if(created_volume >= 150)
playsound(get_turf(holder.my_atom), 'sound/effects/pray.ogg', 80, 0, round(created_volume/48))
strengthdiv = 8
@@ -76,7 +79,7 @@
C.Knockdown(40)
C.adjust_fire_stacks(5)
C.IgniteMob()
- ..()
+ ..(holder, created_volume, T)
/datum/chemical_reaction/blackpowder
@@ -95,8 +98,9 @@
mix_message = "Sparks start flying around the black powder!"
/datum/chemical_reaction/reagent_explosion/blackpowder_explosion/on_reaction(datum/reagents/holder, created_volume)
+ var/turf/T = get_turf(holder.my_atom)
sleep(rand(50,100))
- ..()
+ ..(holder, created_volume, T)
/datum/chemical_reaction/thermite
name = "Thermite"
@@ -423,6 +427,7 @@
required_reagents = list("teslium" = 1, "water" = 1)
strengthdiv = 100
modifier = -100
+ noexplosion = TRUE
mix_message = "The teslium starts to spark as electricity arcs away from it!"
mix_sound = 'sound/machines/defib_zap.ogg'
var/tesla_flags = TESLA_MOB_DAMAGE | TESLA_OBJ_DAMAGE | TESLA_MOB_STUN
diff --git a/code/modules/research/designs/bluespace_designs.dm b/code/modules/research/designs/bluespace_designs.dm
index 40bd641478..2dc0ed77b3 100644
--- a/code/modules/research/designs/bluespace_designs.dm
+++ b/code/modules/research/designs/bluespace_designs.dm
@@ -24,6 +24,16 @@
dangerous_construction = TRUE
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
+/datum/design/biobag_holding
+ name = "Bio Bag of Holding"
+ desc = "A chemical holding thingy. Mostly used for xenobiology."
+ id = "biobag_holding"
+ build_type = PROTOLATHE
+ materials = list(MAT_GOLD = 1500, MAT_DIAMOND = 750, MAT_URANIUM = 250, MAT_BLUESPACE = 1000)
+ build_path = /obj/item/storage/bag/bio/holding
+ category = list("Bluespace Designs")
+ departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
+
/datum/design/bluespace_crystal
name = "Artificial Bluespace Crystal"
desc = "A small blue crystal with mystical properties."
diff --git a/code/modules/research/designs/mechfabricator_designs.dm b/code/modules/research/designs/mechfabricator_designs.dm
index 97c769170d..e8c8bab09f 100644
--- a/code/modules/research/designs/mechfabricator_designs.dm
+++ b/code/modules/research/designs/mechfabricator_designs.dm
@@ -768,6 +768,15 @@
construction_time = 120
category = list("Cyborg Upgrade Modules")
+/datum/design/borg_upgrade_advhealth
+ name = "Cyborg Upgrade (Advance Health Scanner)"
+ id = "borg_upgrade_advhealth"
+ build_type = MECHFAB
+ build_path = /obj/item/borg/upgrade/advhealth
+ materials = list(MAT_METAL=7500, MAT_GLASS=7500, MAT_SILVER=1000, MAT_GOLD=1000, MAT_TITANIUM=2000)
+ construction_time = 100
+ category = list("Cyborg Upgrade Modules")
+
//Misc
/datum/design/mecha_tracking
name = "Exosuit Tracking Beacon"
diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm
index fe7ee491c8..4d631691d0 100644
--- a/code/modules/research/techweb/all_nodes.dm
+++ b/code/modules/research/techweb/all_nodes.dm
@@ -228,7 +228,7 @@
display_name = "Applied Bluespace Research"
description = "Using bluespace to make things faster and better."
prereq_ids = list("bluespace_basic", "engineering")
- design_ids = list("bs_rped","minerbag_holding", "bluespacebeaker", "bluespacesyringe", "phasic_scanning", "roastingstick", "ore_silo")
+ design_ids = list("bs_rped","biobag_holding","minerbag_holding", "bluespacebeaker", "bluespacesyringe", "phasic_scanning", "roastingstick", "ore_silo")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 7500)
export_price = 5000
@@ -318,7 +318,7 @@
display_name = "Cyborg Upgrades: Medical"
description = "Medical upgrades for cyborgs."
prereq_ids = list("adv_biotech", "robotics")
- design_ids = list("borg_upgrade_defibrillator", "borg_upgrade_piercinghypospray", "borg_upgrade_highstrengthsynthesiser", "borg_upgrade_expandedsynthesiser", "borg_upgrade_pinpointer", "borg_upgrade_surgicalprocessor")
+ design_ids = list("borg_upgrade_defibrillator", "borg_upgrade_advhealth", "borg_upgrade_piercinghypospray", "borg_upgrade_highstrengthsynthesiser", "borg_upgrade_expandedsynthesiser", "borg_upgrade_pinpointer", "borg_upgrade_surgicalprocessor")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2000)
export_price = 5000
diff --git a/code/modules/surgery/advanced/revival.dm b/code/modules/surgery/advanced/revival.dm
index 8a791c850b..0cd7a64235 100644
--- a/code/modules/surgery/advanced/revival.dm
+++ b/code/modules/surgery/advanced/revival.dm
@@ -26,7 +26,7 @@
return TRUE
/datum/surgery_step/revive
- name = "repair body"
+ name = "electrically stimulate brain"
implements = list(/obj/item/twohanded/shockpaddles = 100, /obj/item/abductor/gizmo = 100, /obj/item/melee/baton = 75, /obj/item/organ/cyberimp/arm/baton = 75, /obj/item/organ/cyberimp/arm/gun/taser = 60, /obj/item/gun/energy/e_gun/advtaser = 60, /obj/item/gun/energy/taser = 60)
time = 120
diff --git a/code/modules/surgery/organs/autosurgeon.dm b/code/modules/surgery/organs/autosurgeon.dm
index cf22a633cf..787175e05d 100644
--- a/code/modules/surgery/organs/autosurgeon.dm
+++ b/code/modules/surgery/organs/autosurgeon.dm
@@ -82,6 +82,10 @@
uses = 1
starting_organ = /obj/item/organ/cyberimp/eyes/hud/medical
+/obj/item/autosurgeon/gloweyes
+ desc = "A single use autosurgeon that contains a set of Luminescent Eyes augments. A screwdriver can be used to remove it, but implants can't be placed back in."
+ uses = 1
+ starting_organ = /obj/item/organ/eyes/robotic/glow
/obj/item/autosurgeon/thermal_eyes
starting_organ = /obj/item/organ/eyes/robotic/thermals
diff --git a/code/modules/surgery/organs/lungs.dm b/code/modules/surgery/organs/lungs.dm
index 6ec1ea12d9..1e22796b1b 100644
--- a/code/modules/surgery/organs/lungs.dm
+++ b/code/modules/surgery/organs/lungs.dm
@@ -9,7 +9,7 @@
//Breath damage
var/safe_oxygen_min = 16 // Minimum safe partial pressure of O2, in kPa
- var/safe_oxygen_max = 0
+ var/safe_oxygen_max = 50 // Too much of a good thing, in kPa as well.
var/safe_nitro_min = 0
var/safe_nitro_max = 0
var/safe_co2_min = 0
@@ -97,11 +97,25 @@
//Too much oxygen! //Yes, some species may not like it.
if(safe_oxygen_max)
- if(O2_pp > safe_oxygen_max)
+ if((O2_pp > safe_oxygen_max) && safe_oxygen_max == 0) //I guess plasma men technically need to have a check.
var/ratio = (breath_gases[/datum/gas/oxygen][MOLES]/safe_oxygen_max) * 10
H.apply_damage_type(CLAMP(ratio, oxy_breath_dam_min, oxy_breath_dam_max), oxy_damage_type)
H.throw_alert("too_much_oxy", /obj/screen/alert/too_much_oxy)
+
+ else if((O2_pp > safe_oxygen_max) && !(safe_oxygen_max == 0)) //Why yes, this is like too much CO2 and spahget. Dirty lizards.
+ if(!H.o2overloadtime)
+ H.o2overloadtime = world.time
+ else if(world.time - H.o2overloadtime > 120)
+ H.Dizzy(10) // better than a minute of you're fucked KO, but certainly a wake up call. Honk.
+ H.adjustOxyLoss(3)
+ if(world.time - H.o2overloadtime > 300)
+ H.adjustOxyLoss(8)
+ if(prob(20))
+ H.emote("cough")
+ H.throw_alert("too_much_oxy", /obj/screen/alert/too_much_oxy)
+
else
+ H.o2overloadtime = 0
H.clear_alert("too_much_oxy")
//Too little oxygen!
@@ -129,6 +143,7 @@
var/ratio = (breath_gases[/datum/gas/nitrogen][MOLES]/safe_nitro_max) * 10
H.apply_damage_type(CLAMP(ratio, nitro_breath_dam_min, nitro_breath_dam_max), nitro_damage_type)
H.throw_alert("too_much_nitro", /obj/screen/alert/too_much_nitro)
+ H.losebreath += 2
else
H.clear_alert("too_much_nitro")
@@ -385,6 +400,7 @@
icon_state = "lungs-plasma"
safe_oxygen_min = 0 //We don't breath this
+ safe_oxygen_max = 0 // Like, at all.
safe_toxins_min = 16 //We breath THIS!
safe_toxins_max = 0
@@ -407,7 +423,24 @@
icon_state = "lungs-c-u"
safe_toxins_max = 20
safe_co2_max = 20
+ safe_oxygen_max = 250
cold_level_1_threshold = 200
cold_level_2_threshold = 140
cold_level_3_threshold = 100
+
+/obj/item/organ/lungs/ashwalker
+ name = "ash lungs"
+ desc = "blackened lungs identical from specimens recovered from lavaland, unsuited to higher air pressures."
+ icon_state = "lungs-ll"
+ safe_oxygen_min = 3 //able to handle much thinner oxygen, something something ash storm adaptation
+ safe_oxygen_max = 18 // Air standard is 22kpA of O2, LL is 14kpA
+ safe_nitro_max = 28 // Air standard is 82kpA of N2, LL is 23kpA
+
+ cold_level_1_threshold = 280 // Ash Lizards can't take the cold very well, station air is only just warm enough
+ cold_level_2_threshold = 240
+ cold_level_3_threshold = 200
+
+ heat_level_1_threshold = 400 // better adapted for heat, obv. Lavaland standard is 300
+ heat_level_2_threshold = 600 // up 200 from level 1, 1000 is silly but w/e for level 3
+
diff --git a/html/changelogs/AutoChangeLog-pr-8118.yml b/html/changelogs/AutoChangeLog-pr-8118.yml
new file mode 100644
index 0000000000..be5c0871ab
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-8118.yml
@@ -0,0 +1,6 @@
+author: "deathride58"
+delete-after: True
+changes:
+ - tweak: "Ghost role eligibility for both event and spawner ghost roles is now affected by DNR status. If you suicide, ghost, or cryo out, you will be unable to qualify for any ghost roles"
+ - tweak: "Cryo now applies DNR status no matter how long the round has been going on"
+ - tweak: "Suicide is now properly admin logged as it is supposed to be. Ghosting while alive is now also logged."
diff --git a/html/changelogs/AutoChangeLog-pr-8122.yml b/html/changelogs/AutoChangeLog-pr-8122.yml
new file mode 100644
index 0000000000..2b9d091601
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-8122.yml
@@ -0,0 +1,4 @@
+author: "deathride58"
+delete-after: True
+changes:
+ - bugfix: "Staminaloss targeted at the head now properly redirects to the chest."
diff --git a/html/changelogs/AutoChangeLog-pr-8123.yml b/html/changelogs/AutoChangeLog-pr-8123.yml
new file mode 100644
index 0000000000..56b2f82ad6
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-8123.yml
@@ -0,0 +1,4 @@
+author: "deathride58"
+delete-after: True
+changes:
+ - tweak: "Sprinting no longer takes stam when you're being pulled or when you're in zero gravity. Other sources of involuntary movement are not affected."
diff --git a/html/changelogs/AutoChangeLog-pr-8130.yml b/html/changelogs/AutoChangeLog-pr-8130.yml
new file mode 100644
index 0000000000..ac1dd56028
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-8130.yml
@@ -0,0 +1,4 @@
+author: "Poojawa"
+delete-after: True
+changes:
+ - bugfix: "fixed flavor text appearing when your face is hidden but you're not an Unknown"
diff --git a/html/changelogs/AutoChangeLog-pr-8132.yml b/html/changelogs/AutoChangeLog-pr-8132.yml
new file mode 100644
index 0000000000..05e27044be
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-8132.yml
@@ -0,0 +1,4 @@
+author: "Poojawa"
+delete-after: True
+changes:
+ - bugfix: "Tauric suits now apply an (placeholder) blood overlay, as well as their shield overlay."
diff --git a/html/changelogs/AutoChangeLog-pr-8140.yml b/html/changelogs/AutoChangeLog-pr-8140.yml
new file mode 100644
index 0000000000..ae21917771
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-8140.yml
@@ -0,0 +1,4 @@
+author: "deathride58"
+delete-after: True
+changes:
+ - code_imp: "In an attempt to improve performance during highpop, mouse movements will now only call onmousemove() while a user is in combat mode"
diff --git a/html/changelogs/AutoChangeLog-pr-8141.yml b/html/changelogs/AutoChangeLog-pr-8141.yml
new file mode 100644
index 0000000000..2a465b21e5
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-8141.yml
@@ -0,0 +1,4 @@
+author: "deathride58"
+delete-after: True
+changes:
+ - tweak: "Ballistic projectiles are now the only projectiles capable of emitting dinks."
diff --git a/html/changelogs/AutoChangeLog-pr-8145.yml b/html/changelogs/AutoChangeLog-pr-8145.yml
new file mode 100644
index 0000000000..55cd6c378b
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-8145.yml
@@ -0,0 +1,4 @@
+author: "deathride58"
+delete-after: True
+changes:
+ - admin: "Panic bunker toggling and bypassing is now logged in admin IRC"
diff --git a/html/changelogs/AutoChangeLog-pr-8146.yml b/html/changelogs/AutoChangeLog-pr-8146.yml
new file mode 100644
index 0000000000..566532fdd3
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-8146.yml
@@ -0,0 +1,5 @@
+author: "deathride58"
+delete-after: True
+changes:
+ - tweak: "OOC and LOOC now use separate toggles. You can now use LOOC while OOC is globally toggled off, and admins now have the option to toggle LOOC off separately from OOC"
+ - code_imp: "also revamped and reorganized relevant looc adminverb code"
diff --git a/html/changelogs/AutoChangeLog-pr-8159.yml b/html/changelogs/AutoChangeLog-pr-8159.yml
new file mode 100644
index 0000000000..da0f13e28a
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-8159.yml
@@ -0,0 +1,6 @@
+author: "Poojawa"
+delete-after: True
+changes:
+ - balance: "Ashwalkers now have lungs. They cannot breath station air without suffocation effects, but are completely fine on their homeworld."
+ - balance: "Carbon mobs now have a maximum tolerance to oxygen of 50kPa."
+ - balance: "Deluxe synthetic lungs have a very high bonus to O2 tolerance."
diff --git a/html/changelogs/AutoChangeLog-pr-8160.yml b/html/changelogs/AutoChangeLog-pr-8160.yml
new file mode 100644
index 0000000000..33d2445f48
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-8160.yml
@@ -0,0 +1,5 @@
+author: "Poojawa"
+delete-after: True
+changes:
+ - bugfix: "Digitigrade legs returned to normal, since digi leg markings overlay them anyway."
+ - bugfix: "commented out lizard mam_snout entries, because too many abominations"
diff --git a/html/changelogs/AutoChangeLog-pr-8164.yml b/html/changelogs/AutoChangeLog-pr-8164.yml
new file mode 100644
index 0000000000..5d00b5b9ba
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-8164.yml
@@ -0,0 +1,12 @@
+author: "Poojawa"
+delete-after: True
+changes:
+ - bugfix: "Vore Panel restored to have various interactions available again. Semi-untested but should work as advertised."
+ - tweak: "Vore Panel has more feedback."
+ - rscadd: "is_wet var to bellies, toggled in the panel, will remove flesh sounding struggles and the internal loop. JSON version updated."
+ - rscadd: "Feeding var. You will need to enable feeding to recieve any feed vore actions, but you can now feed yourself to mobs that have this set. TODO, Dogborg sleeper feeding."
+ - rscadd: "vore mode button now required to be enabled to perform vore actions. It's the mouth icon!"
+ - bugfix: "Ash Drake vore fixed for actual reals this time"
+ - bugfix: "Mobs shouldn't spam the released contents announcement anymore on qdel or death. Only if triggered"
+ - tweak: "Hostile mob code now properly ignores targets in bellies"
+ - rscadd: "Your belly can quietly growl if you're starving now."
diff --git a/html/changelogs/AutoChangeLog-pr-8167.yml b/html/changelogs/AutoChangeLog-pr-8167.yml
new file mode 100644
index 0000000000..105e5c34b8
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-8167.yml
@@ -0,0 +1,4 @@
+author: "deathride58"
+delete-after: True
+changes:
+ - bugfix: "The endgame narsie summon rune no longer requires 24 total sacrifices, and will now properly account for cultists that surround it"
diff --git a/html/changelogs/AutoChangeLog-pr-8169.yml b/html/changelogs/AutoChangeLog-pr-8169.yml
new file mode 100644
index 0000000000..5c50eac652
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-8169.yml
@@ -0,0 +1,4 @@
+author: "Poojawa"
+delete-after: True
+changes:
+ - bugfix: "Normal blood splattering isn't offset due to tauric mode"
diff --git a/html/changelogs/AutoChangeLog-pr-8172.yml b/html/changelogs/AutoChangeLog-pr-8172.yml
new file mode 100644
index 0000000000..87c4b5baf7
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-8172.yml
@@ -0,0 +1,4 @@
+author: "deathride58"
+delete-after: True
+changes:
+ - tweak: "The succumb verb is now available in the IC tab"
diff --git a/html/changelogs/AutoChangeLog-pr-8175.yml b/html/changelogs/AutoChangeLog-pr-8175.yml
new file mode 100644
index 0000000000..00e8ea4ee3
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-8175.yml
@@ -0,0 +1,4 @@
+author: "deathride58"
+delete-after: True
+changes:
+ - tweak: "Lighting now uses a linear algorithm to calculate falloff instead of an inverse-square algorithm."
diff --git a/html/changelogs/AutoChangeLog-pr-8183.yml b/html/changelogs/AutoChangeLog-pr-8183.yml
new file mode 100644
index 0000000000..01b3cd4b2b
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-8183.yml
@@ -0,0 +1,5 @@
+author: "bluespace bio bags"
+delete-after: True
+changes:
+ - rscadd: "Added bluespace bio bags and put it in the tech web, in the node applied bluespace"
+ - imageadd: "added a crappy icon for bluespace bio bags"
diff --git a/html/changelogs/AutoChangeLog-pr-8184.yml b/html/changelogs/AutoChangeLog-pr-8184.yml
new file mode 100644
index 0000000000..6a55b6468b
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-8184.yml
@@ -0,0 +1,4 @@
+author: "deathride58"
+delete-after: True
+changes:
+ - code_imp: "Goonchat's JS no longer contains checks related to a completely unused message filtering function, which should improve clientside performance quite a bit"
diff --git a/html/changelogs/AutoChangeLog-pr-8185.yml b/html/changelogs/AutoChangeLog-pr-8185.yml
new file mode 100644
index 0000000000..eec6e7c3b2
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-8185.yml
@@ -0,0 +1,4 @@
+author: "deathride58"
+delete-after: True
+changes:
+ - rscadd: "The storage hud now properly takes into account the viewer's view size, meaning storage items with a large amount of storage slots will properly stretch across the bottom of the screen when running in widescreen."
diff --git a/html/changelogs/AutoChangeLog-pr-8192.yml b/html/changelogs/AutoChangeLog-pr-8192.yml
new file mode 100644
index 0000000000..28d8f00814
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-8192.yml
@@ -0,0 +1,4 @@
+author: "deathride58"
+delete-after: True
+changes:
+ - code_imp: "The client update portion of /mob/Login() now double-checks to make sure client.player_details exists and is of the proper type. This should hopefully fix the \"cannot read null.player_details\" runtimes that can spontaneously cause clients to get kicked out and forced back to the lobby."
diff --git a/html/changelogs/AutoChangeLog-pr-8194.yml b/html/changelogs/AutoChangeLog-pr-8194.yml
new file mode 100644
index 0000000000..d7a8b0b540
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-8194.yml
@@ -0,0 +1,9 @@
+author: "Hippie Circuit Port"
+delete-after: True
+changes:
+ - rscadd: "Added all Atmospheric Circuits"
+ - rscadd: "Added the ability to color data disks"
+ - rscadd: "Added Selection and Storage Examiner Circuits"
+ - rscadd: "Added Smoke, Extinguisher, and Beaker Connector Circuits"
+ - rscadd: "Added Inserter, Renamer, Redescriber, and Repaint Circuits"
+ - rscadd: "Added MMI Tank and pAI Connector Circuits (The possibilities are endless!)"
diff --git a/html/changelogs/AutoChangeLog-pr-8196.yml b/html/changelogs/AutoChangeLog-pr-8196.yml
new file mode 100644
index 0000000000..631ccb6728
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-8196.yml
@@ -0,0 +1,4 @@
+author: "deathride58"
+delete-after: True
+changes:
+ - code_imp: "In an attempt to improve the performance of /mob/Stat(), various time-related procs now use defines instead of being procs that call procs that call other procs that call even more procs."
diff --git a/html/changelogs/AutoChangeLog-pr-8197.yml b/html/changelogs/AutoChangeLog-pr-8197.yml
new file mode 100644
index 0000000000..cdd2b4e877
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-8197.yml
@@ -0,0 +1,4 @@
+author: "Naksu"
+delete-after: True
+changes:
+ - code_imp: "squeezed a little bit more perf out of atmos"
diff --git a/html/changelogs/AutoChangeLog-pr-8199.yml b/html/changelogs/AutoChangeLog-pr-8199.yml
new file mode 100644
index 0000000000..d3bf5a07e5
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-8199.yml
@@ -0,0 +1,4 @@
+author: "Poojawa"
+delete-after: True
+changes:
+ - bugfix: "Hunger noises are now toggled per character"
diff --git a/html/changelogs/AutoChangeLog-pr-8207.yml b/html/changelogs/AutoChangeLog-pr-8207.yml
new file mode 100644
index 0000000000..22b31a834c
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-8207.yml
@@ -0,0 +1,4 @@
+author: "deathride58"
+delete-after: True
+changes:
+ - bugfix: "If a living mob has vore initialized but doesnt have voreprefs initialized, then client login will force voreprefs to load."
diff --git a/icons/mob/actions/actions_changeling.dmi b/icons/mob/actions/actions_changeling.dmi
new file mode 100644
index 0000000000..ce6b81e13b
Binary files /dev/null and b/icons/mob/actions/actions_changeling.dmi differ
diff --git a/icons/mob/actions/backgrounds.dmi b/icons/mob/actions/backgrounds.dmi
index 4303c6fff6..82303c5862 100644
Binary files a/icons/mob/actions/backgrounds.dmi and b/icons/mob/actions/backgrounds.dmi differ
diff --git a/icons/mob/human_parts_greyscale.dmi b/icons/mob/human_parts_greyscale.dmi
index 47ef8e816f..a8d2a2aef3 100644
Binary files a/icons/mob/human_parts_greyscale.dmi and b/icons/mob/human_parts_greyscale.dmi differ
diff --git a/icons/obj/chemical.dmi b/icons/obj/chemical.dmi
index efa7d91471..93daa6149e 100644
Binary files a/icons/obj/chemical.dmi and b/icons/obj/chemical.dmi differ
diff --git a/icons/obj/surgery.dmi b/icons/obj/surgery.dmi
index e3a0d595a6..4ab614cf83 100755
Binary files a/icons/obj/surgery.dmi and b/icons/obj/surgery.dmi differ
diff --git a/modular_citadel/code/_onclick/hud/screen_objects.dm b/modular_citadel/code/_onclick/hud/screen_objects.dm
index 5a193335f3..511627b81f 100644
--- a/modular_citadel/code/_onclick/hud/screen_objects.dm
+++ b/modular_citadel/code/_onclick/hud/screen_objects.dm
@@ -47,3 +47,21 @@
icon_state = "combat"
else
icon_state = "combat_off"
+
+/obj/screen/voretoggle
+ name = "toggle vore mode"
+ icon = 'modular_citadel/icons/ui/screen_midnight.dmi'
+ icon_state = "nom_off"
+
+/obj/screen/voretoggle/Click()
+ if(iscarbon(usr))
+ var/mob/living/carbon/C = usr
+ C.toggle_vore_mode()
+
+/obj/screen/voretoggle/proc/rebaseintomygut(mob/living/carbon/C)
+ if(!C)
+ return
+ if(C.voremode && !C.combatmode)
+ icon_state = "nom"
+ else
+ icon_state = "nom_off"
diff --git a/modular_citadel/code/controllers/subsystem/shuttle.dm b/modular_citadel/code/controllers/subsystem/shuttle.dm
index 4b9dd33d08..bb4592f819 100644
--- a/modular_citadel/code/controllers/subsystem/shuttle.dm
+++ b/modular_citadel/code/controllers/subsystem/shuttle.dm
@@ -1,5 +1,5 @@
/datum/controller/subsystem/shuttle/proc/autoEnd() //CIT CHANGE - allows shift to end after 3 hours has passed.
- if(world.time > auto_call && EMERGENCY_IDLE_OR_RECALLED) //3 hours
+ if((world.realtime - SSshuttle.realtimeofstart) > auto_call && EMERGENCY_IDLE_OR_RECALLED) //3 hours
SSshuttle.emergency.request()
priority_announce("The shift has come to an end and the shuttle called.")
log_game("Round time limit reached. Shuttle has been auto-called.")
diff --git a/modular_citadel/code/game/machinery/cryopod.dm b/modular_citadel/code/game/machinery/cryopod.dm
index 9369fa7677..1ff628bfbd 100644
--- a/modular_citadel/code/game/machinery/cryopod.dm
+++ b/modular_citadel/code/game/machinery/cryopod.dm
@@ -365,10 +365,8 @@
// Ghost and delete the mob.
if(!mob_occupant.get_ghost(1))
- if(world.time < 30 * 600)//before the 30 minute mark
- mob_occupant.ghostize(0) // Players despawned too early may not re-enter the game
- else
- mob_occupant.ghostize(1)
+ mob_occupant.ghostize(0) // Players who cryo out may not re-enter the round
+
QDEL_NULL(occupant)
open_machine()
name = initial(name)
diff --git a/modular_citadel/code/modules/client/loadout/__donator.dm b/modular_citadel/code/modules/client/loadout/__donator.dm
index 66245fbb8a..c0a4df2243 100644
--- a/modular_citadel/code/modules/client/loadout/__donator.dm
+++ b/modular_citadel/code/modules/client/loadout/__donator.dm
@@ -367,4 +367,10 @@ datum/gear/darksabresheath
name = "Soul Necklace"
category = SLOT_NECK
path = /obj/item/clothing/neck/undertale
- ckeywhitelist = list("twilightic")
\ No newline at end of file
+ ckeywhitelist = list("twilightic")
+
+/datum/gear/frenchberet
+ name = "French Beret"
+ category = SLOT_HEAD
+ path = /obj/item/clothing/head/frenchberet
+ ckeywhitelist = list("notazoltan")
\ No newline at end of file
diff --git a/modular_citadel/code/modules/client/loadout/backpack.dm b/modular_citadel/code/modules/client/loadout/backpack.dm
index 9f12dd8b03..bb12fc8ac9 100644
--- a/modular_citadel/code/modules/client/loadout/backpack.dm
+++ b/modular_citadel/code/modules/client/loadout/backpack.dm
@@ -72,3 +72,15 @@
name = "Box of crayons"
category = SLOT_IN_BACKPACK
path = /obj/item/storage/crayons
+
+/datum/gear/toykatana
+ name = "Toy Katana"
+ category = SLOT_IN_BACKPACK
+ path = /obj/item/toy/katana
+ cost = 3
+
+//datum/gear/lumeyes
+// name = "Luminescent eye auto surgeon"
+// category = SLOT_IN_BACKPACK
+// path = /obj/item/autosurgeon/gloweyes
+// cost = 4
diff --git a/modular_citadel/code/modules/mob/dead/new_player/sprite_accessories.dm b/modular_citadel/code/modules/mob/dead/new_player/sprite_accessories.dm
index bd301a2e85..ecb4a57407 100644
--- a/modular_citadel/code/modules/mob/dead/new_player/sprite_accessories.dm
+++ b/modular_citadel/code/modules/mob/dead/new_player/sprite_accessories.dm
@@ -36,7 +36,8 @@
color_src = MATRIXED
icon = 'modular_citadel/icons/mob/mam_tails.dmi'
-/************* Lizard compatable snoots ************/
+//christ this was a mistake, but it's here just in case someone wants to selectively fix
+/************* Lizard compatable snoots ***********
/datum/sprite_accessory/snouts/bird
name = "Beak"
icon_state = "bird"
@@ -155,6 +156,7 @@
icon_state = "toucan"
icon = 'modular_citadel/icons/mob/mam_snouts.dmi'
color_src = MATRIXED
+*/
/******************************************
************ Human Ears/Tails *************
diff --git a/modular_citadel/code/modules/mob/living/carbon/carbon.dm b/modular_citadel/code/modules/mob/living/carbon/carbon.dm
index d52cc6eabb..cd24bfc8a2 100644
--- a/modular_citadel/code/modules/mob/living/carbon/carbon.dm
+++ b/modular_citadel/code/modules/mob/living/carbon/carbon.dm
@@ -5,6 +5,9 @@
var/lastdirchange
var/combatmessagecooldown
+ //oh no vore time
+ var/voremode = FALSE
+
/mob/living/carbon/CanPass(atom/movable/mover, turf/target)
. = ..()
if(.)
@@ -19,6 +22,8 @@
if(recoveringstam)
return TRUE
combatmode = !combatmode
+ if(voremode)
+ toggle_vore_mode()
if(combatmode)
playsound_local(src, 'modular_citadel/sound/misc/ui_toggle.ogg', 50, FALSE, pressure_affected = FALSE) //Sound from interbay!
else
@@ -34,6 +39,16 @@
SEND_SIGNAL(src, COMSIG_COMBAT_TOGGLED, src, combatmode)
return TRUE
+mob/living/carbon/proc/toggle_vore_mode()
+ voremode = !voremode
+ if(hud_used && hud_used.static_inventory)
+ for(var/obj/screen/voretoggle/selector in hud_used.static_inventory)
+ selector.rebaseintomygut(src)
+ if(combatmode)
+ return FALSE //let's not override the main draw of the game these days
+ SEND_SIGNAL(src, COMSIG_VORE_TOGGLED, src, voremode)
+ return TRUE
+
/mob/living/carbon/Move(atom/newloc, direct = 0)
var/currentdirection = dir
. = ..()
diff --git a/modular_citadel/code/modules/mob/living/carbon/human/human_defense.dm b/modular_citadel/code/modules/mob/living/carbon/human/human_defense.dm
index c1fc6623de..bbff8ee750 100644
--- a/modular_citadel/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/modular_citadel/code/modules/mob/living/carbon/human/human_defense.dm
@@ -1,9 +1,3 @@
-/mob/living/carbon/human/grabbedby(mob/living/carbon/user, supress_message = 0)
- if(user == src && pulling && !pulling.anchored && grab_state >= GRAB_AGGRESSIVE && isliving(pulling))
- vore_attack(user, pulling)
- else
- ..()
-
/mob/living/carbon/human/alt_attack_hand(mob/user)
if(..())
return
diff --git a/modular_citadel/code/modules/mob/living/carbon/human/human_movement.dm b/modular_citadel/code/modules/mob/living/carbon/human/human_movement.dm
index c4449d33d7..2a1c790b2a 100644
--- a/modular_citadel/code/modules/mob/living/carbon/human/human_movement.dm
+++ b/modular_citadel/code/modules/mob/living/carbon/human/human_movement.dm
@@ -4,7 +4,7 @@
/mob/living/carbon/human/Move(NewLoc, direct)
var/oldpseudoheight = pseudo_z_axis
. = ..()
- if(. && sprinting && !(movement_type & FLYING) && canmove && !resting && m_intent == MOVE_INTENT_RUN)
+ if(. && sprinting && !(movement_type & FLYING) && canmove && !resting && m_intent == MOVE_INTENT_RUN && has_gravity(loc) && !pulledby)
adjustStaminaLossBuffered(0.3)
if((oldpseudoheight - pseudo_z_axis) >= 8)
to_chat(src, "You trip off of the elevated surface!")
diff --git a/modular_citadel/code/modules/mob/living/carbon/human/species_types/furrypeople.dm b/modular_citadel/code/modules/mob/living/carbon/human/species_types/furrypeople.dm
index edc36ae3af..c34146a90c 100644
--- a/modular_citadel/code/modules/mob/living/carbon/human/species_types/furrypeople.dm
+++ b/modular_citadel/code/modules/mob/living/carbon/human/species_types/furrypeople.dm
@@ -233,3 +233,5 @@
/mob/living/carbon/human/vore
devourable = TRUE
+ digestable = TRUE
+ feeding = TRUE
diff --git a/modular_citadel/code/modules/reagents/reagents/cit_reagents.dm b/modular_citadel/code/modules/reagents/reagents/cit_reagents.dm
index e5d2b49b37..ae77fbcfc0 100644
--- a/modular_citadel/code/modules/reagents/reagents/cit_reagents.dm
+++ b/modular_citadel/code/modules/reagents/reagents/cit_reagents.dm
@@ -37,11 +37,6 @@
..()
dir = pick(1,2,4,8)
-/datum/reagent/consumable/semen/reaction_turf(turf/T, reac_volume)
- if(!isspaceturf(T))
- var/obj/effect/decal/cleanable/reagentdecal = new/obj/effect/decal/cleanable/semen(T)
- reagentdecal.reagents.add_reagent("semen", reac_volume)
-
/datum/reagent/consumable/femcum
name = "Female Ejaculate"
id = "femcum"
diff --git a/modular_citadel/code/modules/vore/eating/belly_obj_vr.dm b/modular_citadel/code/modules/vore/eating/belly_obj_vr.dm
index 1aa8122be5..e931f37520 100644
--- a/modular_citadel/code/modules/vore/eating/belly_obj_vr.dm
+++ b/modular_citadel/code/modules/vore/eating/belly_obj_vr.dm
@@ -15,7 +15,7 @@
desc = "It's a belly! You're in it!" // Flavor text description of inside sight/sound/smells/feels.
var/vore_sound = "Gulp" // Sound when ingesting someone
var/vore_verb = "ingest" // Verb for eating with this in messages
- var/release_sound = "Splatter"
+ var/release_sound = "Splatter" // Sound for letting someone out.
var/human_prey_swallow_time = 100 // Time in deciseconds to swallow /mob/living/carbon/human
var/nonhuman_prey_swallow_time = 30 // Time in deciseconds to swallow anything else
var/emote_time = 60 SECONDS // How long between stomach emotes at prey
@@ -26,18 +26,18 @@
var/escapetime = 20 SECONDS // Deciseconds, how long to escape this belly
var/digestchance = 0 // % Chance of stomach beginning to digest if prey struggles
var/absorbchance = 0 // % Chance of stomach beginning to absorb if prey struggles
- var/escapechance = 100 // % Chance of prey beginning to escape if prey struggles.
+ var/escapechance = 0 // % Chance of prey beginning to escape if prey struggles.
var/can_taste = FALSE // If this belly prints the flavor of prey when it eats someone.
var/bulge_size = 0.25 // The minimum size the prey has to be in order to show up on examine.
// var/shrink_grow_size = 1 // This horribly named variable determines the minimum/maximum size it will shrink/grow prey to.
- var/silent = FALSE
- var/transferlocation = null // Location that the prey is released if they struggle and get dropped off.
+ var/transferlocation // Location that the prey is released if they struggle and get dropped off.
var/transferchance = 0 // % Chance of prey being transferred to transfer location when resisting
var/autotransferchance = 0 // % Chance of prey being autotransferred to transfer location
var/autotransferwait = 10 // Time between trying to transfer.
var/swallow_time = 10 SECONDS // for mob transfering automation
var/vore_capacity = 1 // simple animal nom capacity
+ var/is_wet = TRUE // Is this belly inside slimy parts?
//I don't think we've ever altered these lists. making them static until someone actually overrides them somewhere.
var/tmp/static/list/digest_modes = list(DM_HOLD,DM_DIGEST,DM_HEAL,DM_NOISY,DM_ABSORB,DM_UNABSORB) // Possible digest modes
@@ -126,7 +126,6 @@
"escapechance",
"can_taste",
"bulge_size",
- "silent",
"transferlocation",
"transferchance",
"autotransferchance",
@@ -138,7 +137,8 @@
"digest_messages_owner",
"digest_messages_prey",
"examine_messages",
- "emote_lists"
+ "emote_lists",
+ "is_wet"
)
//ommitted list
@@ -167,10 +167,11 @@
to_chat(owner,"[thing] slides into your [lowertext(name)].")
//Sound w/ antispam flag setting
- if(!silent && !recent_sound)
- for(var/mob/M in get_hearers_in_view(5, get_turf(owner)))
+ if(is_wet && !recent_sound)
+ for(var/mob/M in get_hearers_in_view(2, get_turf(owner)))
if(M.client && (M.client.prefs.cit_toggles & EATING_NOISES))
- playsound(get_turf(owner),"[src.vore_sound]",50,0,-5,0,ignore_walls = FALSE,channel=CHANNEL_PRED)
+ var/sound/devourments = GLOB.vore_sounds[vore_sound]
+ playsound(get_turf(owner),devourments,50,0,-5,0,ignore_walls = FALSE,channel=CHANNEL_PRED)
recent_sound = TRUE
//Messages if it's a mob
@@ -182,8 +183,12 @@
// Release all contents of this belly into the owning mob's location.
// If that location is another mob, contents are transferred into whichever of its bellies the owning mob is in.
// Returns the number of mobs so released.
-/obj/belly/proc/release_all_contents(var/include_absorbed = FALSE)
+/obj/belly/proc/release_all_contents(var/include_absorbed = FALSE, var/silent = FALSE)
var/atom/destination = drop_location()
+ //Don't bother if we don't have contents
+ if(!contents.len)
+ return 0
+
var/count = 0
for(var/thing in contents)
var/atom/movable/AM = thing
@@ -201,9 +206,16 @@
SEND_SIGNAL(L, COMSIG_ADD_MOOD_EVENT, "emptyprey", /datum/mood_event/emptyprey)
AM.forceMove(destination) // Move the belly contents into the same location as belly's owner.
count++
- for(var/mob/M in get_hearers_in_view(5, get_turf(owner)))
+ for(var/mob/M in get_hearers_in_view(2, get_turf(owner)))
if(M.client && (M.client.prefs.cit_toggles & EATING_NOISES))
- playsound(get_turf(owner),"[src.release_sound]",50,0,-5,0,ignore_walls = FALSE,channel=CHANNEL_PRED)
+ var/sound/releasement = GLOB.release_sounds[release_sound]
+ playsound(get_turf(owner),releasement,50,0,-5,0,ignore_walls = FALSE,channel=CHANNEL_PRED)
+
+ //Clean up our own business
+ items_preserved.Cut()
+ if(isanimal(owner))
+ owner.update_icons()
+
if(!silent)
owner.visible_message("[owner] expels everything from their [lowertext(name)]!")
items_preserved.Cut()
@@ -214,16 +226,16 @@
// Release a specific atom from the contents of this belly into the owning mob's location.
// If that location is another mob, the atom is transferred into whichever of its bellies the owning mob is in.
// Returns the number of atoms so released.
-/obj/belly/proc/release_specific_contents(var/atom/movable/M)
+/obj/belly/proc/release_specific_contents(var/atom/movable/M, var/silent = FALSE)
if (!(M in contents))
return FALSE // They weren't in this belly anyway
M.forceMove(drop_location()) // Move the belly contents into the same location as belly's owner.
items_preserved -= M
- if(release_sound)
- for(var/mob/H in get_hearers_in_view(5, get_turf(owner)))
- if(H.client && (H.client.prefs.cit_toggles & EATING_NOISES))
- playsound(get_turf(owner),"[src.release_sound]",50,0,-5,0,ignore_walls = FALSE,channel=CHANNEL_PRED)
+ for(var/mob/H in get_hearers_in_view(2, get_turf(owner)))
+ if(H.client && (H.client.prefs.cit_toggles & EATING_NOISES))
+ var/sound/releasement = GLOB.release_sounds[release_sound]
+ playsound(get_turf(owner),releasement,50,0,-5,0,ignore_walls = FALSE,channel=CHANNEL_PRED)
if(istype(M,/mob/living))
var/mob/living/ML = M
@@ -245,6 +257,11 @@
if(P.absorbed)
absorbed_count++
Pred.reagents.trans_to(Prey, Pred.reagents.total_volume / absorbed_count)
+
+ //Clean up our own business
+ if(isanimal(owner))
+ owner.update_icons()
+
if(!silent)
owner.visible_message("[owner] expels [M] from their [lowertext(name)]!")
owner.update_icons()
@@ -268,8 +285,10 @@
prey.forceMove(src)
var/sound/preyloop = sound('sound/vore/prey/loop.ogg', repeat = TRUE)
- if(!silent)
+
+ if(is_wet)
prey.playsound_local(loc,preyloop,70,0, channel = CHANNEL_PREYLOOP)
+
owner.updateVRPanel()
for(var/mob/living/M in contents)
@@ -299,7 +318,8 @@
if(!silent)
for(var/mob/M in get_hearers_in_view(5, get_turf(owner)))
if(M.client && (M.client.prefs.cit_toggles & EATING_NOISES))
- playsound(get_turf(owner),"[src.vore_sound]",50,0,-5,0,ignore_walls = FALSE,channel=CHANNEL_PRED)
+ var/sound/devourments = GLOB.vore_sounds[vore_sound]
+ playsound(get_turf(owner),devourments,50,0,-5,0,ignore_walls = FALSE,channel=CHANNEL_PRED)
owner.updateVRPanel()
for(var/mob/living/M in contents)
M.updateVRPanel()
@@ -461,6 +481,9 @@
//Yes, it's ""safe"" to drop items here
/obj/belly/AllowDrop()
return TRUE
+/*
+/obj/belly/onDropInto(var/atom/movable/AM)
+ return null */
//Handle a mob struggling
// Called from /mob/living/carbon/relaymove()
@@ -469,6 +492,23 @@
return // User is not in this belly
R.setClickCooldown(50)
+
+ if(owner.stat) //If owner is stat (dead, KO) we can actually escape
+ to_chat(R,"You attempt to climb out of \the [lowertext(name)]. (This will take around [escapetime/10] seconds.)")
+ to_chat(owner,"Someone is attempting to climb out of your [lowertext(name)]!")
+
+ if(do_after(R, owner, escapetime))
+ if((owner.stat || escapable) && (R.loc == src)) //Can still escape?
+ release_specific_contents(R)
+ return
+ else if(R.loc != src) //Aren't even in the belly. Quietly fail.
+ return
+ else //Belly became inescapable or mob revived
+ to_chat(R,"Your attempt to escape [lowertext(name)] has failed!")
+ to_chat(owner,"The attempt to escape from your [lowertext(name)] has failed!")
+ return
+ return
+
var/struggle_outer_message = pick(struggle_messages_outside)
var/struggle_user_message = pick(struggle_messages_inside)
@@ -483,41 +523,38 @@
struggle_outer_message = "" + struggle_outer_message + ""
struggle_user_message = "" + struggle_user_message + ""
- if((owner.stat || !owner.client) && (R.a_intent != INTENT_HELP)) //If owner is stat (dead, KO) we can actually escape
- to_chat(R,"You attempt to climb out of \the [lowertext(name)]. (This will take around 5 seconds.)")
- to_chat(owner,"Someone is attempting to climb out of your [lowertext(name)]!")
-
- if(!do_mob(R,owner,50))
- return
- if(!(R in contents)) //Aren't even in the belly. Quietly fail.
- return
- if(R.a_intent != INTENT_HELP) //still want to?
- release_specific_contents(R)
- return
- else //Belly became inescapable or mob revived
- to_chat(R,"Your attempt to escape [lowertext(name)] has failed!")
- to_chat(owner,"The attempt to escape from your [lowertext(name)] has failed!")
- return
- else if(R.a_intent != INTENT_HELP) //failsafe to make sure people are able to struggle out. friendly ERP should be on help intent.
- to_chat(R,"You attempt to climb out of [lowertext(name)]. (This will take around [escapetime] seconds.)")
- to_chat(owner,"Someone is attempting to climb out of your [lowertext(name)]!")
- if(!do_mob(R,owner,escapetime))
- return
- release_specific_contents(R)
- return
- else if (R.a_intent == INTENT_HELP)
- for(var/mob/M in get_hearers_in_view(3, get_turf(owner)))
- M.show_message(struggle_outer_message, 1) // visible
- to_chat(R,struggle_user_message)
-
- if(!silent)
- for(var/mob/M in get_hearers_in_view(5, get_turf(owner)))
+ if(is_wet)
+ for(var/mob/M in get_hearers_in_view(2, get_turf(owner)))
if(M.client && (M.client.prefs.cit_toggles & EATING_NOISES))
playsound(get_turf(owner),"struggle_sound",35,0,-5,1,ignore_walls = FALSE,channel=CHANNEL_PRED)
R.stop_sound_channel(CHANNEL_PRED)
var/sound/prey_struggle = sound(get_sfx("prey_struggle"))
R.playsound_local(get_turf(R),prey_struggle,45,0)
+ else
+ for(var/mob/M in get_hearers_in_view(2, get_turf(owner)))
+ if(M.client && (M.client.prefs.cit_toggles & EATING_NOISES))
+ playsound(get_turf(owner),"rustle",35,0,-5,1,ignore_walls = FALSE,channel=CHANNEL_PRED)
+
+ for(var/mob/M in get_hearers_in_view(3, get_turf(owner)))
+ if(M.client && (M.client.prefs.cit_toggles & EATING_NOISES)) //Might as well censor the normies here too.
+ M.show_message(struggle_outer_message, 1) // visible
+ to_chat(R,struggle_user_message)
+
+ if(escapable) //If the stomach has escapable enabled.
+ if(prob(escapechance)) //Let's have it check to see if the prey escapes first.
+ to_chat(R,"You start to climb out of \the [lowertext(name)].")
+ to_chat(owner,"Someone is attempting to climb out of your [lowertext(name)]!")
+ if(do_after(R, escapetime))
+ if((owner.stat || escapable) && (R.loc == src)) //Can still escape?
+ release_specific_contents(R)
+ return
+ else if(R.loc != src) //Aren't even in the belly. Quietly fail.
+ return
+ else //Belly became inescapable or mob revived
+ to_chat(R,"Your attempt to escape [lowertext(name)] has failed!")
+ to_chat(owner,"The attempt to escape from your [lowertext(name)] has failed!")
+ return
else if(prob(transferchance) && transferlocation) //Next, let's have it see if they end up getting into an even bigger mess then when they started.
var/obj/belly/dest_belly
for(var/belly in owner.vore_organs)
@@ -525,6 +562,7 @@
if(B.name == transferlocation)
dest_belly = B
break
+
if(!dest_belly)
to_chat(owner, "Something went wrong with your belly transfer settings. Your [lowertext(name)] has had it's transfer chance and transfer location cleared as a precaution.")
transferchance = 0
@@ -541,21 +579,10 @@
to_chat(owner,"You feel your [lowertext(name)] start to cling onto its contents...")
digest_mode = DM_ABSORB
return
-/*
- else if(prob(digestchance) && digest_mode != DM_ITEMWEAK && digest_mode != DM_DIGEST) //Finally, let's see if it should run the digest chance.
+
+ else if(prob(digestchance) && digest_mode != DM_DIGEST) //Finally, let's see if it should run the digest chance.
to_chat(R,"In response to your struggling, \the [lowertext(name)] begins to get more active...")
to_chat(owner,"You feel your [lowertext(name)] beginning to become active!")
- digest_mode = DM_ITEMWEAK
- return
-
- else if(prob(digestchance) && digest_mode == DM_ITEMWEAK) //Oh god it gets even worse if you fail twice!
- to_chat(R,"In response to your struggling, \the [lowertext(name)] begins to get even more active!")
- to_chat(owner,"You feel your [lowertext(name)] beginning to become even more active!")
- digest_mode = DM_DIGEST
- return */
- else if(prob(digestchance)) //Finally, let's see if it should run the digest chance.)
- to_chat(R, "In response to your struggling, \the [name] begins to get more active...")
- to_chat(owner, "You feel your [name] beginning to become active!")
digest_mode = DM_DIGEST
return
@@ -602,13 +629,13 @@
dupe.escapechance = escapechance
dupe.can_taste = can_taste
dupe.bulge_size = bulge_size
- dupe.silent = silent
dupe.transferlocation = transferlocation
dupe.transferchance = transferchance
dupe.autotransferchance = autotransferchance
dupe.autotransferwait = autotransferwait
dupe.swallow_time = swallow_time
dupe.vore_capacity = vore_capacity
+ dupe.is_wet = is_wet
//// Object-holding variables
//struggle_messages_outside - strings
diff --git a/modular_citadel/code/modules/vore/eating/bellymodes_vr.dm b/modular_citadel/code/modules/vore/eating/bellymodes_vr.dm
index 6c528f75a9..542241e9f2 100644
--- a/modular_citadel/code/modules/vore/eating/bellymodes_vr.dm
+++ b/modular_citadel/code/modules/vore/eating/bellymodes_vr.dm
@@ -25,7 +25,7 @@
for(var/mob/living/M in contents)
if(isbelly(M.loc))
if(world.time > M.next_preyloop)
- if(!silent)
+ if(is_wet)
M.stop_sound_channel(CHANNEL_PREYLOOP) // sanity just in case
var/sound/preyloop = sound('sound/vore/prey/loop.ogg', repeat = TRUE)
M.playsound_local(get_turf(src),preyloop,80,0, channel = CHANNEL_PREYLOOP)
diff --git a/modular_citadel/code/modules/vore/eating/living_vr.dm b/modular_citadel/code/modules/vore/eating/living_vr.dm
index 2e6018f112..cb194ddc65 100644
--- a/modular_citadel/code/modules/vore/eating/living_vr.dm
+++ b/modular_citadel/code/modules/vore/eating/living_vr.dm
@@ -1,16 +1,19 @@
///////////////////// Mob Living /////////////////////
/mob/living
var/digestable = FALSE // Can the mob be digested inside a belly?
- var/obj/belly/vore_selected // Default to no vore capability.
- var/list/vore_organs = list() // List of vore containers inside a mob
+ var/showvoreprefs = TRUE // Determines if the mechanical vore preferences button will be displayed on the mob or not.
+ var/obj/belly/vore_selected // Default to no vore capability.
+ var/list/vore_organs = list() // List of vore containers inside a mob
var/devourable = FALSE // Can the mob be vored at all?
-// var/feeding = FALSE // Are we going to feed someone else?
- var/vore_taste = null // What the character tastes like
+ var/feeding = FALSE // Are we going to feed someone else?
+ var/vore_taste = null // What the character tastes like
var/no_vore = FALSE // If the character/mob can vore.
- var/openpanel = 0 // Is the vore panel open?
- var/noisy = FALSE // tummies are rumbly?
- var/absorbed = FALSE //are we absorbed?
+ var/openpanel = 0 // Is the vore panel open?
+ var/noisy = FALSE // tummies are rumbly?
+ var/absorbed = FALSE //are we absorbed?
var/next_preyloop
+ var/vore_init = FALSE //Has this mob's vore been initialized yet?
+ var/vorepref_init = FALSE //Has this mob's voreprefs been initialized?
//
// Hook for generic creation of stuff on new creatures
@@ -26,12 +29,14 @@
//Tries to load prefs if a client is present otherwise gives freebie stomach
spawn(10 SECONDS) // long delay because the server delays in its startup. just on the safe side.
- M.init_vore()
+ if(M)
+ M.init_vore()
//Return 1 to hook-caller
return 1
/mob/living/proc/init_vore()
+ vore_init = TRUE
//Something else made organs, meanwhile.
if(LAZYLEN(vore_organs))
return TRUE
@@ -75,31 +80,42 @@
// Critical adjustments due to TG grab changes - Poojawa
-/mob/living/proc/vore_attack(var/mob/living/user, var/mob/living/prey)
- if(!user || !prey)
+/mob/living/proc/vore_attack(var/mob/living/user, var/mob/living/prey, var/mob/living/pred)
+ if(!user || !prey || !pred)
return
- if(prey == src && user.zone_selected == "mouth") //you click your target
-// if(!feeding(src))
-// return
- if(!is_vore_predator(prey))
+ if(!isliving(pred)) //no badmin, you can't feed people to ghosts or objects.
+ return
+
+ if(pred == prey) //you click your target
+ if(!pred.feeding)
+ to_chat(user, "They aren't able to be fed.")
+ to_chat(pred, "[user] tried to feed you themselves, but you aren't voracious enough to be fed.")
+ return
+ if(!is_vore_predator(pred))
to_chat(user, "They aren't voracious enough.")
return
- feed_self_to_grabbed(user, src)
+ feed_self_to_grabbed(user, pred)
- if(user == src) //you click yourself
+ if(pred == user) //you click yourself
if(!is_vore_predator(src))
to_chat(user, "You aren't voracious enough.")
return
- user.feed_grabbed_to_self(src, prey)
+ feed_grabbed_to_self(user, prey)
else // click someone other than you/prey
-// if(!feeding(src))
-// return
- if(!is_vore_predator(src))
+ if(!pred.feeding)
+ to_chat(user, "They aren't voracious enough to be fed.")
+ to_chat(pred, "[user] tried to feed you [prey], but you aren't voracious enough to be fed.")
+ return
+ if(!prey.feeding)
+ to_chat(user, "They aren't able to be fed to someone.")
+ to_chat(prey, "[user] tried to feed you to [pred], but you aren't able to be fed to them.")
+ return
+ if(!is_vore_predator(pred))
to_chat(user, "They aren't voracious enough.")
return
- feed_grabbed_to_other(user, prey, src)
+ feed_grabbed_to_other(user, prey, pred)
//
// Eating procs depending on who clicked what
//
@@ -120,7 +136,7 @@
return perform_the_nom(user, user, pred, belly)
/mob/living/proc/feed_grabbed_to_other(var/mob/living/user, var/mob/living/prey, var/mob/living/pred)
- return//disabled until I can make that toggle work
+// return//disabled until I can make that toggle work
var/belly = input("Choose Belly") in pred.vore_organs
return perform_the_nom(user, prey, pred, belly)
@@ -281,7 +297,7 @@
*/
//
-// Custom resist catches for /mob/living
+// Our custom resist catches for /mob/living
//
/mob/living/proc/vore_process_resist()
@@ -293,7 +309,7 @@
//Other overridden resists go here
- return FALSE
+ return 0
// internal slimy button in case the loop stops playing but the player wants to hear it
/mob/living/proc/preyloop_refresh()
@@ -320,9 +336,9 @@
return
//Actual escaping
forceMove(get_turf(src)) //Just move me up to the turf, let's not cascade through bellies, there's been a problem, let's just leave.
- if(is_blind(src) && !has_trait(TRAIT_BLIND))
- src.adjust_blindness(-1)
+ src.cure_blind("belly_[REF(src)]")
src.stop_sound_channel(CHANNEL_PREYLOOP)
+ SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "fedprey", /datum/mood_event/fedprey)
for(var/mob/living/simple_animal/SA in range(10))
SA.prey_excludes[src] = world.time
@@ -374,6 +390,8 @@
P.digestable = src.digestable
P.devourable = src.devourable
+ P.feeding = src.feeding
+ P.noisy = src.noisy
P.vore_taste = src.vore_taste
var/list/serialized = list()
@@ -392,11 +410,14 @@
if(!client || !client.prefs_vr)
to_chat(src,"You attempted to apply your vore prefs but somehow you're in this character without a client.prefs_vr variable. Tell a dev.")
return 0
+ vorepref_init = TRUE
var/datum/vore_preferences/P = client.prefs_vr
digestable = P.digestable
devourable = P.devourable
+ feeding = P.feeding
+ noisy = P.noisy
vore_taste = P.vore_taste
release_vore_contents(silent = TRUE)
diff --git a/modular_citadel/code/modules/vore/eating/vore_vr.dm b/modular_citadel/code/modules/vore/eating/vore_vr.dm
index 16366bbb14..64435619ae 100644
--- a/modular_citadel/code/modules/vore/eating/vore_vr.dm
+++ b/modular_citadel/code/modules/vore/eating/vore_vr.dm
@@ -28,7 +28,7 @@ V::::::V V::::::VO:::::::OOO:::::::ORR:::::R R:::::REE::::::EEEEEE
// The datum type bolted onto normal preferences datums for storing Vore stuff
//
-#define VORE_VERSION 2
+#define VORE_VERSION 3
GLOBAL_LIST_EMPTY(vore_preferences_datums)
@@ -39,7 +39,9 @@ GLOBAL_LIST_EMPTY(vore_preferences_datums)
//Actual preferences
var/digestable = FALSE
var/devourable = FALSE
+ var/feeding = FALSE
// var/allowmobvore = TRUE
+ var/noisy = FALSE
var/list/belly_prefs = list()
var/vore_taste = "nothing in particular"
// var/can_be_drop_prey = FALSE
@@ -105,6 +107,8 @@ GLOBAL_LIST_EMPTY(vore_preferences_datums)
digestable = json_from_file["digestable"]
devourable = json_from_file["devourable"]
+ feeding = json_from_file["feeding"]
+ noisy = json_from_file["noisy"]
vore_taste = json_from_file["vore_taste"]
belly_prefs = json_from_file["belly_prefs"]
@@ -113,6 +117,10 @@ GLOBAL_LIST_EMPTY(vore_preferences_datums)
digestable = FALSE
if(isnull(devourable))
devourable = FALSE
+ if(isnull(feeding))
+ feeding = FALSE
+ if(isnull(noisy))
+ noisy = FALSE
if(isnull(belly_prefs))
belly_prefs = list()
@@ -127,6 +135,8 @@ GLOBAL_LIST_EMPTY(vore_preferences_datums)
"version" = version,
"digestable" = digestable,
"devourable" = devourable,
+ "feeding" = feeding,
+ "noisy" = noisy,
"vore_taste" = vore_taste,
"belly_prefs" = belly_prefs,
)
diff --git a/modular_citadel/code/modules/vore/eating/vorepanel_vr.dm b/modular_citadel/code/modules/vore/eating/vorepanel_vr.dm
index 30209b6da2..4238ae4a7b 100644
--- a/modular_citadel/code/modules/vore/eating/vorepanel_vr.dm
+++ b/modular_citadel/code/modules/vore/eating/vorepanel_vr.dm
@@ -18,9 +18,10 @@
var/dat = picker_holder.gen_vui(src)
- picker_holder.popup = new(src, "insidePanel","Vore Panel", 400, 600, picker_holder)
+ picker_holder.popup = new(src, "insidePanel","Vore Panel", 450, 700, picker_holder)
picker_holder.popup.set_content(dat)
picker_holder.popup.open()
+ src.openpanel = 1
/mob/living/proc/updateVRPanel() //Panel popup update call from belly events.
if(src.openpanel == 1)
@@ -30,7 +31,7 @@
var/dat = picker_holder.gen_vui(src)
- picker_holder.popup = new(src, "insidePanel","Vore Panel", 400, 600, picker_holder)
+ picker_holder.popup = new(src, "insidePanel","Vore Panel", 450, 700, picker_holder)
picker_holder.popup.set_content(dat)
picker_holder.popup.open()
@@ -39,7 +40,7 @@
//
/datum/vore_look
var/obj/belly/selected
- var/show_interacts = TRUE
+ var/show_interacts = 0
var/datum/browser/popup
var/loop = null; // Magic self-reference to stop the handler from being GC'd before user takes action.
@@ -55,13 +56,14 @@
/datum/vore_look/proc/gen_vui(var/mob/living/user)
var/dat
-
+ dat += "Remember to toggle the vore mode, it's to the left of your combat toggle. Open mouth means you're voracious!
"
+ dat += "
"
var/atom/userloc = user.loc
if (isbelly(userloc))
var/obj/belly/inside_belly = userloc
var/mob/living/eater = inside_belly.owner
- //Don't display this part if we couldn't find the belly since could be held in hand.
+ //Don't display this part if we couldn't find the belly since could be held in hand.
if(inside_belly)
dat += "You are currently [user.absorbed ? "absorbed into " : "inside "] [eater]'s [inside_belly]!
"
@@ -102,7 +104,6 @@
dat += "[B.name]"
else
dat += "[B.name]"
-
var/spanstyle
switch(B.digest_mode)
if(DM_HOLD)
@@ -110,11 +111,13 @@
if(DM_DIGEST)
spanstyle = "color:red;"
if(DM_HEAL)
- spanstyle = "color:green;"
+ spanstyle = "color:darkgreen;"
if(DM_NOISY)
spanstyle = "color:purple;"
if(DM_ABSORB)
spanstyle = "color:purple;"
+ if(DM_DRAGON)
+ spanstyle = "color:blue;"
dat += " ([B.contents.len])"
@@ -156,6 +159,10 @@
dat += "Name:"
dat += " '[selected.name]'"
+ //Belly Type button
+ dat += "
Is Fleshy:"
+ dat += "[selected.is_wet ? "Yes" : "No"]"
+
//Digest Mode Button
dat += "
Belly Mode:"
dat += " [selected.digest_mode]"
@@ -169,11 +176,11 @@
dat += " '[selected.desc]'"
//Belly sound
- dat += "
Set Vore Sound"
+ dat += "
Vore Sound: [selected.vore_sound]"
dat += "Test"
//Release sound
- dat += "
Set Release Sound"
+ dat += "
Release Sound: [selected.release_sound]"
dat += "Test"
//Belly messages
@@ -201,10 +208,6 @@
dat += "
Set Belly Escape Time"
dat += " [selected.escapetime/10]s"
- dat += "
"
- dat += "
Set Belly Absorb Chance"
- dat += " [selected.absorbchance]%"
-
//Special
here to add a gap
dat += "
"
dat += "
Set Belly Transfer Chance"
@@ -215,14 +218,13 @@
//Special
here to add a gap
dat += "
"
+ dat += "
Set Belly Absorb Chance"
+ dat += " [selected.absorbchance]%"
+
dat += "
Set Belly Digest Chance"
dat += " [selected.digestchance]%"
dat += "
"
- // Belly Silence
- dat += "
Belly Silence (for not belly bellies):"
- dat += " [selected.silent ? "Yes" : "No"]"
-
//Delete button
dat += "
Delete Belly"
@@ -239,15 +241,21 @@
dat += "
"
switch(user.digestable)
if(TRUE)
- dat += "Toggle Digestable (Currently: ON)"
+ dat += "Toggle Digestable (Currently: ON)"
if(FALSE)
- dat += "Toggle Digestable (Currently: OFF)"
+ dat += "Toggle Digestable (Currently: OFF)"
switch(user.devourable)
if(TRUE)
- dat += "Toggle Devourable (Currently: ON)"
+ dat += "
Toggle Devourable (Currently: ON)"
if(FALSE)
- dat += "Toggle Devourable (Currently: OFF)"
+ dat += "
Toggle Devourable (Currently: OFF)"
+
+ switch(user.feeding)
+ if(TRUE)
+ dat += "
Toggle Feeding (Currently: ON)"
+ if(FALSE)
+ dat += "
Toggle Feeding (Currently: OFF)"
//Returns the dat html to the vore_look
return dat
@@ -258,25 +266,26 @@
if(href_list["close"])
qdel(src) // Cleanup
+ user.openpanel = 0
return
if(href_list["show_int"])
show_interacts = !show_interacts
- return TRUE //Force update
+ return 1 //Force update
if(href_list["int_help"])
- to_chat(usr,"These control how your belly responds to someone using 'resist' while inside you. The percent chance to trigger each is listed below, \
+ alert("These control how your belly responds to someone using 'resist' while inside you. The percent chance to trigger each is listed below, \
and you can change them to whatever you see fit. Setting them to 0% will disable the possibility of that interaction. \
- These only function as long as interactions are turned on in general. Keep in mind, the 'belly mode' interactions (digest) \
- will affect all prey in that belly, if one resists and triggers digestion. If multiple trigger at the same time, \
- only the first in the order of 'Escape > Transfer > Absorb > Digest' will occur.")
- return TRUE //Force update
+ These only function as long as interactions are turned on in general. Keep in mind, the 'belly mode' interactions (digest/absorb) \
+ will affect all prey in that belly, if one resists and triggers digestion/absorption. If multiple trigger at the same time, \
+ only the first in the order of 'Escape > Transfer > Absorb > Digest' will occur.","Interactions Help")
+ return 0 //Force update
if(href_list["outsidepick"])
var/atom/movable/tgt = locate(href_list["outsidepick"])
var/obj/belly/OB = locate(href_list["outsidebelly"])
if(!(tgt in OB)) //Aren't here anymore, need to update menu.
- return TRUE
+ return 1
var/intent = "Examine"
if(istype(tgt,/mob/living))
@@ -289,11 +298,11 @@
if("Help Out") //Help the inside-mob out
if(user.stat || user.absorbed || M.absorbed)
to_chat(user,"You can't do that in your state!")
- return
+ return 1
to_chat(user,"You begin to push [M] to freedom!")
to_chat(M,"[usr] begins to push you to freedom!")
- to_chat(OB.owner,"Someone is trying to escape from inside you!")
+ to_chat(M.loc,"Someone is trying to escape from inside you!")
sleep(50)
if(prob(33))
OB.release_specific_contents(M)
@@ -305,15 +314,14 @@
to_chat(M," Even with [user]'s help, you slip back inside again.")
to_chat(OB.owner,"Your body efficiently shoves [M] back where they belong.")
-
if("Devour") //Eat the inside mob
if(user.absorbed || user.stat)
to_chat(user,"You can't do that in your state!")
- return
+ return 1
if(!user.vore_selected)
to_chat(user,"Pick a belly on yourself first!")
- return
+ return 1
var/obj/belly/TB = user.vore_selected
to_chat(user,"You begin to [lowertext(TB.vore_verb)] [M] into your [lowertext(TB.name)]!")
@@ -329,9 +337,9 @@
else if(istype(tgt,/obj/item))
var/obj/item/T = tgt
- if(!(tgt in OB.contents))
+ if(!(tgt in OB))
//Doesn't exist anymore, update.
- return TRUE
+ return 1
intent = alert("What do you want to do to that?","Query","Examine","Use Hand")
switch(intent)
if("Examine")
@@ -339,8 +347,8 @@
if("Use Hand")
if(user.stat)
- to_chat(user, "You can't do that in your state!")
- return
+ to_chat(user,"You can't do that in your state!")
+ return 1
user.ClickOn(T)
sleep(5) //Seems to exit too fast for the panel to update
@@ -353,32 +361,31 @@
intent = alert("Eject all, Move all?","Query","Eject all","Cancel","Move all")
switch(intent)
if("Cancel")
- return
+ return 0
if("Eject all")
if(user.stat)
- to_chat(user, "You can't do that in your state!")
- return
+ to_chat(user,"You can't do that in your state!")
+ return 0
selected.release_all_contents()
- to_chat(user.loc,"Everything is released from [user]!")
if("Move all")
if(user.stat)
- to_chat(user, "You can't do that in your state!")
- return FALSE
+ to_chat(user,"You can't do that in your state!")
+ return 0
var/obj/belly/choice = input("Move all where?","Select Belly") as null|anything in user.vore_organs
if(!choice)
- return FALSE
+ return 0
for(var/atom/movable/tgt in selected)
- selected.transfer_contents(tgt, choice, 1)
to_chat(tgt,"You're squished from [user]'s [lowertext(selected)] to their [lowertext(choice.name)]!")
+ selected.transfer_contents(tgt, choice, 1)
var/atom/movable/tgt = locate(href_list["insidepick"])
if(!(tgt in selected)) //Old menu, needs updating because they aren't really there.
- return TRUE//Forces update
+ return 1 //Forces update
intent = "Examine"
intent = alert("Examine, Eject, Move? Examine if you want to leave this box.","Query","Examine","Eject","Move")
switch(intent)
@@ -387,11 +394,10 @@
if("Eject")
if(user.stat)
- to_chat(user, "You can't do that in your state!")
- return FALSE
+ to_chat(user,"You can't do that in your state!")
+ return 0
selected.release_specific_contents(tgt)
- user.loc << "[tgt] is released from [user]!"
if("Move")
if(user.stat)
@@ -457,6 +463,9 @@
selected.name = new_name
+ if(href_list["b_wetness"])
+ selected.is_wet = !selected.is_wet
+
if(href_list["b_mode"])
var/list/menu_list = selected.digest_modes
@@ -540,12 +549,12 @@
if(!choice)
return
- selected.release_sound = GLOB.release_sounds[choice]
+ selected.release_sound = choice
if(href_list["b_releasesoundtest"])
- var/soundfile = selected.release_sound
- if(soundfile)
- user << soundfile
+ var/sound/releasetest = GLOB.release_sounds[selected.release_sound]
+ if(releasetest)
+ user << releasetest
if(href_list["b_sound"])
var/choice = input(user,"Currently set to [selected.vore_sound]","Select Sound") as null|anything in GLOB.vore_sounds
@@ -553,12 +562,12 @@
if(!choice)
return
- selected.vore_sound = GLOB.vore_sounds[choice]
+ selected.vore_sound = choice
if(href_list["b_soundtest"])
- var/soundfile = selected.vore_sound
- if(soundfile)
- user << soundfile
+ var/sound/voretest = GLOB.vore_sounds[selected.vore_sound]
+ if(voretest)
+ user << voretest
if(href_list["b_tastes"])
selected.can_taste = !selected.can_taste
@@ -577,17 +586,17 @@
selected.bulge_size = (new_bulge/100)
if(href_list["b_escapable"])
- if(selected.escapable == FALSE) //Possibly escapable and special interactions.
- selected.escapable = TRUE
- to_chat(usr,"Prey now have special interactions with your [selected.name] depending on your settings.")
- else if(selected.escapable == TRUE) //Never escapable.
- selected.escapable = FALSE
- to_chat(usr,"Prey will not be able to have special interactions with your [selected.name].")
- show_interacts = FALSE //Force the hiding of the panel
+ if(selected.escapable == 0) //Possibly escapable and special interactions.
+ selected.escapable = 1
+ to_chat(usr,"Prey now have special interactions with your [lowertext(selected.name)] depending on your settings.")
+ else if(selected.escapable == 1) //Never escapable.
+ selected.escapable = 0
+ to_chat(usr,"Prey will not be able to have special interactions with your [lowertext(selected.name)].")
+ show_interacts = 0 //Force the hiding of the panel
else
- to_chat(usr,"Something went wrong. Your stomach will now not have special interactions. Press the button enable them again and tell a dev.") //If they somehow have a varable that's not 0 or 1
- selected.escapable = TRUE
- show_interacts = FALSE //Force the hiding of the panel
+ alert("Something went wrong. Your stomach will now not have special interactions. Press the button enable them again and tell a dev.","Error") //If they somehow have a varable that's not 0 or 1
+ selected.escapable = 0
+ show_interacts = 0 //Force the hiding of the panel
if(href_list["b_escapechance"])
var/escape_chance_input = input(user, "Set prey escape chance on resist (as %)", "Prey Escape Chance") as num|null
@@ -605,14 +614,14 @@
selected.transferchance = sanitize_integer(transfer_chance_input, 0, 100, initial(selected.transferchance))
if(href_list["b_transferlocation"])
- var/choice = input("Where do you want your [selected.name] to lead if prey resists?","Select Belly") as null|anything in (user.vore_organs + "None - Remove" - selected.name)
+ var/obj/belly/choice = input("Where do you want your [lowertext(selected.name)] to lead if prey resists?","Select Belly") as null|anything in (user.vore_organs + "None - Remove" - selected)
if(!choice) //They cancelled, no changes
- return FALSE
+ return 0
else if(choice == "None - Remove")
selected.transferlocation = null
else
- selected.transferlocation = user.vore_organs[choice]
+ selected.transferlocation = choice.name
if(href_list["b_absorbchance"])
var/absorb_chance_input = input(user, "Set belly absorb mode chance on resist (as %)", "Prey Absorb Chance") as num|null
@@ -624,13 +633,10 @@
if(!isnull(digest_chance_input))
selected.digestchance = sanitize_integer(digest_chance_input, 0, 100, initial(selected.digestchance))
- if(href_list["b_silent"])
- selected.silent = !selected.silent
-
if(href_list["b_del"])
var/alert = alert("Are you sure you want to delete your [lowertext(selected.name)]?","Confirmation","Delete","Cancel")
if(!alert == "Delete")
- return FALSE
+ return 0
var/failure_msg = ""
@@ -651,7 +657,7 @@
if(failure_msg)
alert(user,failure_msg,"Error!")
- return FALSE
+ return 0
qdel(selected)
selected = user.vore_organs[1]
@@ -689,7 +695,7 @@
var/choice = alert(user, "This button is for those who don't like being digested. It can make you undigestable to all mobs. Digesting you is currently: [user.digestable ? "Allowed" : "Prevented"]", "", "Allow Digestion", "Cancel", "Prevent Digestion")
switch(choice)
if("Cancel")
- return
+ return 0
if("Allow Digestion")
user.digestable = TRUE
if("Prevent Digestion")
@@ -702,7 +708,7 @@
var/choice = alert(user, "This button is for those who don't like vore at all. Devouring you is currently: [user.devourable ? "Allowed" : "Prevented"]", "", "Allow Devourment", "Cancel", "Prevent Devourment")
switch(choice)
if("Cancel")
- return
+ return 0
if("Allow Devourment")
user.devourable = TRUE
if("Prevent Devourment")
@@ -711,5 +717,28 @@
if(user.client.prefs_vr)
user.client.prefs_vr.devourable = user.devourable
+ if(href_list["toggledfeed"])
+ var/choice = alert(user, "This button is to toggle your ability to be fed to others. Feeding predators is currently: [user.feeding ? "Allowed" : "Prevented"]", "", "Allow Feeding", "Cancel", "Prevent Feeding")
+ switch(choice)
+ if("Cancel")
+ return 0
+ if("Allow Feeding")
+ user.feeding = TRUE
+ if("Prevent Feeding")
+ user.feeding = FALSE
+
+ if(user.client.prefs_vr)
+ user.client.prefs_vr.feeding = user.feeding
+
+ if(href_list["togglenoisy"])
+ var/choice = alert(user, "Toggle audible hunger noises. Currently: [user.noisy ? "Enabled" : "Disabled"]", "", "Enable audible hunger", "Cancel", "Disable audible hunger")
+ switch(choice)
+ if("Cancel")
+ return 0
+ if("Enable audible hunger")
+ user.noisy = TRUE
+ if("Disable audible hunger")
+ user.noisy = FALSE
+
//Refresh when interacted with, returning 1 makes vore_look.Topic update
return 1
\ No newline at end of file
diff --git a/modular_citadel/icons/mob/64x32_effects.dmi b/modular_citadel/icons/mob/64x32_effects.dmi
new file mode 100644
index 0000000000..7db65a8fc3
Binary files /dev/null and b/modular_citadel/icons/mob/64x32_effects.dmi differ
diff --git a/modular_citadel/icons/obj/drinks.dmi b/modular_citadel/icons/obj/drinks.dmi
index 0000af8b56..a7af4f23bd 100644
Binary files a/modular_citadel/icons/obj/drinks.dmi and b/modular_citadel/icons/obj/drinks.dmi differ
diff --git a/modular_citadel/icons/ui/screen_clockwork.dmi b/modular_citadel/icons/ui/screen_clockwork.dmi
index 499d2663b6..2b70d37f1e 100644
Binary files a/modular_citadel/icons/ui/screen_clockwork.dmi and b/modular_citadel/icons/ui/screen_clockwork.dmi differ
diff --git a/modular_citadel/icons/ui/screen_midnight.dmi b/modular_citadel/icons/ui/screen_midnight.dmi
index 38d96b86d1..396fd4a36e 100644
Binary files a/modular_citadel/icons/ui/screen_midnight.dmi and b/modular_citadel/icons/ui/screen_midnight.dmi differ
diff --git a/modular_citadel/icons/ui/screen_operative.dmi b/modular_citadel/icons/ui/screen_operative.dmi
index 7296db1f9c..a2c9420b77 100644
Binary files a/modular_citadel/icons/ui/screen_operative.dmi and b/modular_citadel/icons/ui/screen_operative.dmi differ
diff --git a/modular_citadel/icons/ui/screen_plasmafire.dmi b/modular_citadel/icons/ui/screen_plasmafire.dmi
index 2829b22d59..abbb8f0cd1 100644
Binary files a/modular_citadel/icons/ui/screen_plasmafire.dmi and b/modular_citadel/icons/ui/screen_plasmafire.dmi differ
diff --git a/modular_citadel/icons/ui/screen_slimecore.dmi b/modular_citadel/icons/ui/screen_slimecore.dmi
index 0f24033da6..15651808ec 100644
Binary files a/modular_citadel/icons/ui/screen_slimecore.dmi and b/modular_citadel/icons/ui/screen_slimecore.dmi differ
diff --git a/sound/machines/button.ogg b/sound/machines/button.ogg
new file mode 100644
index 0000000000..79b458317a
Binary files /dev/null and b/sound/machines/button.ogg differ
diff --git a/sound/machines/button1.ogg b/sound/machines/button1.ogg
new file mode 100644
index 0000000000..3499e2d172
Binary files /dev/null and b/sound/machines/button1.ogg differ
diff --git a/sound/machines/button2.ogg b/sound/machines/button2.ogg
new file mode 100644
index 0000000000..83970986e3
Binary files /dev/null and b/sound/machines/button2.ogg differ
diff --git a/sound/machines/button3.ogg b/sound/machines/button3.ogg
new file mode 100644
index 0000000000..a28da269ad
Binary files /dev/null and b/sound/machines/button3.ogg differ
diff --git a/sound/machines/button4.ogg b/sound/machines/button4.ogg
new file mode 100644
index 0000000000..9fceed1611
Binary files /dev/null and b/sound/machines/button4.ogg differ
diff --git a/sound/machines/terminal_eject_disc.ogg b/sound/machines/terminal_eject_disc.ogg
new file mode 100644
index 0000000000..357e667f94
Binary files /dev/null and b/sound/machines/terminal_eject_disc.ogg differ
diff --git a/sound/roundend/haveabeautifultime.ogg b/sound/roundend/haveabeautifultime.ogg
new file mode 100644
index 0000000000..0d5a5ed336
Binary files /dev/null and b/sound/roundend/haveabeautifultime.ogg differ
diff --git a/sound/vore/growl1.ogg b/sound/vore/growl1.ogg
new file mode 100644
index 0000000000..6a9cef8988
Binary files /dev/null and b/sound/vore/growl1.ogg differ
diff --git a/sound/vore/growl2.ogg b/sound/vore/growl2.ogg
new file mode 100644
index 0000000000..17a9a94559
Binary files /dev/null and b/sound/vore/growl2.ogg differ
diff --git a/sound/vore/growl3.ogg b/sound/vore/growl3.ogg
new file mode 100644
index 0000000000..f5a2f75434
Binary files /dev/null and b/sound/vore/growl3.ogg differ
diff --git a/sound/vore/growl4.ogg b/sound/vore/growl4.ogg
new file mode 100644
index 0000000000..5fb3c2db79
Binary files /dev/null and b/sound/vore/growl4.ogg differ
diff --git a/sound/vore/growl5.ogg b/sound/vore/growl5.ogg
new file mode 100644
index 0000000000..95ba675a75
Binary files /dev/null and b/sound/vore/growl5.ogg differ
diff --git a/tgstation.dme b/tgstation.dme
index c43b1a7019..1cdc8cb21a 100755
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -1711,6 +1711,7 @@
#include "code\modules\hydroponics\grown\tobacco.dm"
#include "code\modules\hydroponics\grown\tomato.dm"
#include "code\modules\hydroponics\grown\towercap.dm"
+#include "code\modules\integrated_electronics\_defines.dm"
#include "code\modules\integrated_electronics\core\analyzer.dm"
#include "code\modules\integrated_electronics\core\assemblies.dm"
#include "code\modules\integrated_electronics\core\debugger.dm"
@@ -1735,6 +1736,7 @@
#include "code\modules\integrated_electronics\passive\power.dm"
#include "code\modules\integrated_electronics\subtypes\access.dm"
#include "code\modules\integrated_electronics\subtypes\arithmetic.dm"
+#include "code\modules\integrated_electronics\subtypes\atmospherics.dm"
#include "code\modules\integrated_electronics\subtypes\converters.dm"
#include "code\modules\integrated_electronics\subtypes\data_transfer.dm"
#include "code\modules\integrated_electronics\subtypes\input.dm"