diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm
index 8da220bf671..e2c04299f1c 100644
--- a/code/__HELPERS/game.dm
+++ b/code/__HELPERS/game.dm
@@ -142,7 +142,7 @@
if(ismob(A))
var/mob/M = A
if(client_check && !M.client)
- L = recursive_mob_check(A, L, recursion_limit - 1, client_check, sight_check, include_radio)
+ L |= recursive_mob_check(A, L, recursion_limit - 1, client_check, sight_check, include_radio)
continue
if(sight_check && !isInSight(A, O))
continue
@@ -155,7 +155,7 @@
L |= A
if(isobj(A) || ismob(A))
- L = recursive_mob_check(A, L, recursion_limit - 1, client_check, sight_check, include_radio)
+ L |= recursive_mob_check(A, L, recursion_limit - 1, client_check, sight_check, include_radio)
return L
// The old system would loop through lists for a total of 5000 per function call, in an empty server.
@@ -182,7 +182,7 @@
hear += A
if(isobj(A) || ismob(A))
- hear = recursive_mob_check(A, hear, 3, 1, 0, 1)
+ hear |= recursive_mob_check(A, hear, 3, 1, 0, 1)
return hear
@@ -209,8 +209,9 @@
if(M)
var/turf/ear = get_turf(M)
if(ear)
- if(speaker_coverage[ear])
- . |= M
+ // Ghostship is magic: Ghosts can hear radio chatter from anywhere
+ if(speaker_coverage[ear] || (istype(M, /mob/dead/observer) && (M.client) && (M.client.prefs.toggles & CHAT_GHOSTRADIO)))
+ . |= M // Since we're already looping through mobs, why bother using |= ? This only slows things down.
return .
#define SIGN(X) ((X<0)?-1:1)
diff --git a/code/_onclick/ai.dm b/code/_onclick/ai.dm
index cb840a1d90e..112c3431e52 100644
--- a/code/_onclick/ai.dm
+++ b/code/_onclick/ai.dm
@@ -55,6 +55,7 @@
RestrainedClickOn(A)
else
*/
+ A.add_hiddenprint(src)
A.attack_ai(src)
/*
diff --git a/code/_onclick/cyborg.dm b/code/_onclick/cyborg.dm
index 640a09d045f..d86faacbd1f 100644
--- a/code/_onclick/cyborg.dm
+++ b/code/_onclick/cyborg.dm
@@ -48,6 +48,7 @@
// Cyborgs have no range-checking unless there is item use
if(!W)
+ A.add_hiddenprint(src)
A.attack_robot(src)
return
diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm
index c9dfd7cf403..5f11d9f6230 100644
--- a/code/controllers/configuration.dm
+++ b/code/controllers/configuration.dm
@@ -18,6 +18,7 @@
var/log_adminwarn = 0 // log warnings admins get about bomb construction and such
var/log_pda = 0 // log pda messages
var/log_hrefs = 0 // logs all links clicked in-game. Could be used for debugging and tracking down exploits
+ var/log_runtime = 0 // logs world.log to a file
var/sql_enabled = 1 // for sql switching
var/allow_admin_ooccolor = 0 // Allows admins with relevant permissions to have their own ooc colour
var/allow_vote_restart = 0 // allow votes to restart
@@ -245,6 +246,9 @@
if ("log_hrefs")
config.log_hrefs = 1
+ if ("log_runtime")
+ config.log_runtime = 1
+
if("allow_admin_ooccolor")
config.allow_admin_ooccolor = 1
diff --git a/code/game/machinery/telecomms/broadcaster.dm b/code/game/machinery/telecomms/broadcaster.dm
index 155f6c64815..7775e3aa20d 100644
--- a/code/game/machinery/telecomms/broadcaster.dm
+++ b/code/game/machinery/telecomms/broadcaster.dm
@@ -288,7 +288,10 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
if(istype(R, /mob/new_player)) // we don't want new players to hear messages. rare but generates runtimes.
continue
-
+
+ // Ghosts hearing all radio chat don't want to hear syndicate intercepts, they're duplicates
+ if(data == 3 && istype(R, /mob/dead/observer) && R.client && (R.client.prefs.toggles & CHAT_GHOSTRADIO))
+ continue
// --- Check for compression ---
if(compression > 0)
diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm
index ad757325b95..ee73d537d8e 100644
--- a/code/game/objects/items/crayons.dm
+++ b/code/game/objects/items/crayons.dm
@@ -80,6 +80,7 @@
if(instant || do_after(user, 50))
new /obj/effect/decal/cleanable/crayon(target,colour,shadeColour,drawtype)
user << "You finish drawing."
+ target.add_fingerprint(user) // Adds their fingerprints to the floor the crayon is drawn on.
if(uses)
uses--
if(!uses)
@@ -97,4 +98,4 @@
user << "\red You ate your crayon!"
del(src)
else
- ..()
\ No newline at end of file
+ ..()
diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm
index ab8884d96c3..eb59003aced 100644
--- a/code/game/objects/structures/watercloset.dm
+++ b/code/game/objects/structures/watercloset.dm
@@ -163,6 +163,7 @@
if("boiling")
watertemp = "normal"
user.visible_message("[user] adjusts the shower with the [I].", "You adjust the shower with the [I].")
+ add_fingerprint(user)
/obj/machinery/shower/update_icon() //this is terribly unreadable, but basically it makes the shower mist up
overlays.Cut() //once it's been on for a while, in addition to handling the water overlay.
@@ -412,4 +413,4 @@
/obj/structure/sink/puddle/attackby(obj/item/O as obj, mob/user as mob)
icon_state = "puddle-splash"
..()
- icon_state = "puddle"
\ No newline at end of file
+ icon_state = "puddle"
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index 54a865955ad..b09712350c1 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -804,7 +804,11 @@
if (ismob(M))
if(!check_if_greater_rights_than(M.client))
return
- M << "\red You have been kicked from the server"
+ var/reason = input("Please enter reason")
+ if(!reason)
+ M << "\red You have been kicked from the server"
+ else
+ M << "\red You have been kicked from the server: [reason]"
log_admin("[key_name(usr)] booted [key_name(M)].")
message_admins("\blue [key_name_admin(usr)] booted [key_name_admin(M)].", 1)
//M.client = null
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index 304e96b0fad..0c8c0e0b4e9 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -250,6 +250,7 @@ datum/preferences
dat += "Play lobby music: [(toggles & SOUND_LOBBY) ? "Yes" : "No"]
"
dat += "Ghost ears: [(toggles & CHAT_GHOSTEARS) ? "Nearest Creatures" : "All Speech"]
"
dat += "Ghost sight: [(toggles & CHAT_GHOSTSIGHT) ? "Nearest Creatures" : "All Emotes"]
"
+ dat += "Ghost radio: [(toggles & CHAT_GHOSTRADIO) ? "Nearest Speakers" : "All Chatter"]
"
if(config.allow_Metadata)
dat += "OOC Notes: Edit
"
@@ -1159,6 +1160,9 @@ datum/preferences
if("ghost_sight")
toggles ^= CHAT_GHOSTSIGHT
+
+ if("ghost_radio")
+ toggles ^= CHAT_GHOSTRADIO
if("save")
save_preferences()
diff --git a/code/modules/client/preferences_toggles.dm b/code/modules/client/preferences_toggles.dm
index c1628c1f3fe..326921c1f6a 100644
--- a/code/modules/client/preferences_toggles.dm
+++ b/code/modules/client/preferences_toggles.dm
@@ -17,14 +17,23 @@
prefs.save_preferences()
feedback_add_details("admin_verb","TGS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+/client/verb/toggle_ghost_radio()
+ set name = "Enable/Disable GhostRadio"
+ set category = "Preferences"
+ set desc = ".Toggle between hearing all radio chatter, or only from nearby speakers"
+ prefs.toggles ^= CHAT_GHOSTRADIO
+ src << "As a ghost, you will now [(prefs.toggles & CHAT_GHOSTRADIO) ? "hear all radio chat in the world" : "only hear from nearby speakers"]."
+ prefs.save_preferences()
+ feedback_add_details("admin_verb","TGR")
+
/client/proc/toggle_hear_radio()
set name = "Show/Hide RadioChatter"
set category = "Preferences"
- set desc = "Toggle seeing radiochatter from nearby radios and speakers"
+ set desc = "Toggle seeing radiochatter from radios and speakers"
if(!holder) return
prefs.toggles ^= CHAT_RADIO
prefs.save_preferences()
- usr << "You will [(prefs.toggles & CHAT_RADIO) ? "now" : "no longer"] see radio chatter from nearby radios or speakers"
+ usr << "You will [(prefs.toggles & CHAT_RADIO) ? "now" : "no longer"] see radio chatter from radios or speakers"
feedback_add_details("admin_verb","THR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/toggleadminhelpsound()
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index 9627c55d824..2b5b5d75b7b 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -441,6 +441,58 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
set hidden = 1
src << "\red You are dead! You have no mind to store memory!"
+/mob/dead/observer/verb/analyze_air()
+ set name = "Analyze Air"
+ set category = "Ghost"
+
+ if(!istype(usr, /mob/dead/observer)) return
+
+ // Shamelessly copied from the Gas Analyzers
+ if (!( istype(usr.loc, /turf) ))
+ return
+
+ var/datum/gas_mixture/environment = usr.loc.return_air()
+
+ var/pressure = environment.return_pressure()
+ var/total_moles = environment.total_moles()
+
+ src << "\blue Results:"
+ if(abs(pressure - ONE_ATMOSPHERE) < 10)
+ src << "\blue Pressure: [round(pressure,0.1)] kPa"
+ else
+ src << "\red Pressure: [round(pressure,0.1)] kPa"
+ if(total_moles)
+ var/o2_concentration = environment.oxygen/total_moles
+ var/n2_concentration = environment.nitrogen/total_moles
+ var/co2_concentration = environment.carbon_dioxide/total_moles
+ var/plasma_concentration = environment.toxins/total_moles
+
+ var/unknown_concentration = 1-(o2_concentration+n2_concentration+co2_concentration+plasma_concentration)
+ if(abs(n2_concentration - N2STANDARD) < 20)
+ src << "\blue Nitrogen: [round(n2_concentration*100)]% ([round(environment.nitrogen,0.01)] moles)"
+ else
+ src << "\red Nitrogen: [round(n2_concentration*100)]% ([round(environment.nitrogen,0.01)] moles)"
+
+ if(abs(o2_concentration - O2STANDARD) < 2)
+ src << "\blue Oxygen: [round(o2_concentration*100)]% ([round(environment.oxygen,0.01)] moles)"
+ else
+ src << "\red Oxygen: [round(o2_concentration*100)]% ([round(environment.oxygen,0.01)] moles)"
+
+ if(co2_concentration > 0.01)
+ src << "\red CO2: [round(co2_concentration*100)]% ([round(environment.carbon_dioxide,0.01)] moles)"
+ else
+ src << "\blue CO2: [round(co2_concentration*100)]% ([round(environment.carbon_dioxide,0.01)] moles)"
+
+ if(plasma_concentration > 0.01)
+ src << "\red Plasma: [round(plasma_concentration*100)]% ([round(environment.toxins,0.01)] moles)"
+
+ if(unknown_concentration > 0.01)
+ src << "\red Unknown: [round(unknown_concentration*100)]% ([round(unknown_concentration*total_moles,0.01)] moles)"
+
+ src << "\blue Temperature: [round(environment.temperature-T0C,0.1)]°C"
+ src << "\blue Heat Capacity: [round(environment.heat_capacity(),0.1)]"
+
+
/mob/dead/observer/verb/toggle_darkness()
set name = "Toggle Darkness"
set category = "Ghost"
@@ -572,4 +624,4 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
W.update_icon()
W.message = message
W.add_hiddenprint(src)
- W.visible_message("\red Invisible fingers crudely paint something in blood on [T]...")
\ No newline at end of file
+ W.visible_message("\red Invisible fingers crudely paint something in blood on [T]...")
diff --git a/code/modules/mob/living/carbon/monkey/diona.dm b/code/modules/mob/living/carbon/monkey/diona.dm
index 442d33e2ace..3f98e3b6946 100644
--- a/code/modules/mob/living/carbon/monkey/diona.dm
+++ b/code/modules/mob/living/carbon/monkey/diona.dm
@@ -2,6 +2,38 @@
Tiny babby plant critter plus procs.
*/
+//Helper object for picking dionaea up.
+/obj/item/weapon/diona_holder
+
+ name = "diona nymph"
+ desc = "It's a tiny plant critter."
+ icon = 'icons/obj/objects.dmi'
+ icon_state = "nymph"
+ slot_flags = SLOT_HEAD
+ origin_tech = "magnets=3;biotech=5"
+
+/obj/item/weapon/diona_holder/New()
+ ..()
+ processing_objects.Add(src)
+
+
+/obj/item/weapon/diona_holder/Del()
+ processing_objects.Remove(src)
+ ..()
+
+/obj/item/weapon/diona_holder/process()
+ if(!loc) return
+
+ if(!istype(loc,/mob/living))
+ for(var/mob/M in contents)
+ M.loc = get_turf(src)
+ del(src)
+
+/obj/item/weapon/diona_holder/attackby(obj/item/weapon/W as obj, mob/user as mob)
+ for(var/mob/M in src.contents)
+ M.attackby(W,user)
+
+//Mob defines.
/mob/living/carbon/monkey/diona
name = "diona nymph"
voice_name = "diona nymph"
@@ -10,6 +42,20 @@
var/list/donors = list()
var/ready_evolve = 0
+/mob/living/carbon/monkey/diona/attack_hand(mob/living/carbon/human/M as mob)
+
+ //Let people pick the little buggers up.
+ if(M.a_intent == "help")
+ var/obj/item/weapon/diona_holder/D = new(loc)
+ src.loc = D
+ D.name = loc.name
+ D.attack_hand(M)
+ M << "You scoop up [src]."
+ src << "[M] scoops you up."
+ return
+
+ ..()
+
/mob/living/carbon/monkey/diona/New()
..()
@@ -64,6 +110,10 @@
set name = "Evolve"
set desc = "Grow to a more complex form."
+ if(!is_alien_whitelisted(src, "Diona") && config.usealienwhitelist)
+ src << alert("You are currently not whitelisted to play an adult Diona.")
+ return 0
+
if(donors.len < 5)
src << "You are not yet ready for your growth..."
return
@@ -73,8 +123,15 @@
return
src.visible_message("\red [src] begins to shift and quiver, and erupts in a shower of shed bark and twigs!","\red You begin to shift and quiver, then erupt in a shower of shed bark and twigs, attaining your adult form!")
- var/mob/living/carbon/human/adult = new(loc)
+
+ var/mob/living/carbon/human/adult = new(get_turf(src.loc))
adult.set_species("Diona")
+
+ if(istype(loc,/obj/item/weapon/diona_holder/))
+ var/obj/item/weapon/diona_holder/L = loc
+ src.loc = L.loc
+ del(L)
+
for(var/datum/language/L in languages)
adult.add_language(L.name)
adult.regenerate_icons()
diff --git a/code/modules/mob/living/carbon/monkey/life.dm b/code/modules/mob/living/carbon/monkey/life.dm
index 1b0d0e1064a..8b9f3009d14 100644
--- a/code/modules/mob/living/carbon/monkey/life.dm
+++ b/code/modules/mob/living/carbon/monkey/life.dm
@@ -22,7 +22,7 @@
if(loc)
environment = loc.return_air()
- if (stat != DEAD) //still breathing
+ if (stat != DEAD && !istype(src,/mob/living/carbon/monkey/diona)) //still breathing
//First, resolve location and get a breath
if(air_master.current_cycle%4==2)
//Only try to take a breath every 4 seconds, unless suffocating
diff --git a/code/setup.dm b/code/setup.dm
index 68b7552b40f..08fec4f49ae 100644
--- a/code/setup.dm
+++ b/code/setup.dm
@@ -639,6 +639,7 @@ var/list/TAGGERLOCATIONS = list("Disposals",
#define CHAT_ATTACKLOGS 1024
#define CHAT_DEBUGLOGS 2048
#define CHAT_LOOC 4096
+#define CHAT_GHOSTRADIO 8192
#define TOGGLES_DEFAULT (SOUND_ADMINHELP|SOUND_MIDI|SOUND_AMBIENCE|SOUND_LOBBY|CHAT_OOC|CHAT_DEAD|CHAT_GHOSTEARS|CHAT_GHOSTSIGHT|CHAT_PRAYER|CHAT_RADIO|CHAT_ATTACKLOGS|CHAT_LOOC)
diff --git a/code/world.dm b/code/world.dm
index 3d1ad20db98..de70b60fe65 100644
--- a/code/world.dm
+++ b/code/world.dm
@@ -11,7 +11,6 @@
/world/New()
//logs
var/date_string = time2text(world.realtime, "YYYY/MM-Month/DD-Day")
- log = file("data/logs/runtime/[time2text(world.realtime,"YYYY-MM")].log") //funtimelog
href_logfile = file("data/logs/[date_string] hrefs.htm")
diary = file("data/logs/[date_string].log")
diaryofmeanpeople = file("data/logs/[date_string] Attack.log")
@@ -28,6 +27,9 @@
// dumb and hardcoded but I don't care~
config.server_name += " #[(world.port % 1000) / 100]"
+ if(config && config.log_runtime)
+ log = file("data/logs/runtime/[time2text(world.realtime,"YYYY-MM-DD-(hh-mm-ss)")]-runtime.log")
+
callHook("startup")
//Emergency Fix
load_mods()
diff --git a/config/example/config.txt b/config/example/config.txt
index 1075a5ab677..783adb23afa 100644
--- a/config/example/config.txt
+++ b/config/example/config.txt
@@ -61,6 +61,9 @@ LOG_PDA
## log all Topic() calls (for use by coders in tracking down Topic issues)
# LOG_HREFS
+## log world.log and runtime errors to a file
+# LOG_RUNTIME
+
## log admin warning messages
##LOG_ADMINWARN ## Also duplicates a bunch of other messages.
@@ -234,4 +237,4 @@ USEALIENWHITELIST
ALLOW_CULT_GHOSTWRITER
## Sets the minimum number of cultists needed for ghosts to write in blood.
-REQ_CULT_GHOSTWRITER 6
\ No newline at end of file
+REQ_CULT_GHOSTWRITER 6
diff --git a/icons/mob/animal.dmi b/icons/mob/animal.dmi
index e269d169aa7..0fb7e01d0d7 100644
Binary files a/icons/mob/animal.dmi and b/icons/mob/animal.dmi differ
diff --git a/icons/mob/head.dmi b/icons/mob/head.dmi
index 3559623fd24..2e79482b4b2 100644
Binary files a/icons/mob/head.dmi and b/icons/mob/head.dmi differ
diff --git a/icons/mob/items_lefthand.dmi b/icons/mob/items_lefthand.dmi
index 2dc326dccb9..4e023c6bca3 100644
Binary files a/icons/mob/items_lefthand.dmi and b/icons/mob/items_lefthand.dmi differ
diff --git a/icons/mob/items_righthand.dmi b/icons/mob/items_righthand.dmi
index 2483a5c728c..392ac028231 100644
Binary files a/icons/mob/items_righthand.dmi and b/icons/mob/items_righthand.dmi differ
diff --git a/icons/obj/objects.dmi b/icons/obj/objects.dmi
index cbbef92efae..ae70160d50d 100644
Binary files a/icons/obj/objects.dmi and b/icons/obj/objects.dmi differ