diff --git a/code/datums/mutations.dm b/code/datums/mutations.dm
index 6129bdce06e..2e5145df3e4 100644
--- a/code/datums/mutations.dm
+++ b/code/datums/mutations.dm
@@ -52,7 +52,7 @@
. = on_losing(owner)
/datum/mutation/human/proc/on_acquiring(mob/living/carbon/human/owner)
- if(!owner || !istype(owner) || (src in owner.dna.mutations))
+ if(!owner || !istype(owner) || owner.stat == DEAD || (src in owner.dna.mutations))
return 1
if(species_allowed.len && !species_allowed.Find(owner.dna.species.id))
return 1
@@ -87,7 +87,7 @@
/datum/mutation/human/proc/on_losing(mob/living/carbon/human/owner)
if(owner && istype(owner) && (owner.dna.mutations.Remove(src)))
- if(text_lose_indication)
+ if(text_lose_indication && owner.stat != DEAD)
owner << text_lose_indication
if(visual_indicators.len)
var/list/mut_overlay = list()
@@ -381,7 +381,7 @@
. = owner.monkeyize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPORGANS | TR_KEEPDAMAGE | TR_KEEPVIRUS | TR_KEEPSE)
/datum/mutation/human/race/on_losing(mob/living/carbon/monkey/owner)
- if(owner && istype(owner) && (owner.dna.mutations.Remove(src)))
+ if(owner && istype(owner) && owner.stat != DEAD && (owner.dna.mutations.Remove(src)))
. = owner.humanize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPORGANS | TR_KEEPDAMAGE | TR_KEEPVIRUS | TR_KEEPSE)
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 373d98c3b36..478f87827f4 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -280,22 +280,22 @@ var/list/blood_splatter_icons = list()
//returns 1 if made bloody, returns 0 otherwise
/atom/proc/add_blood(mob/living/carbon/M)
+ if(!M || !M.has_dna() || rejects_blood())
+ return 0
if(ishuman(M))
var/mob/living/carbon/human/H = M
if(NOBLOOD in H.dna.species.specflags)
return 0
- if(rejects_blood() || !M.has_dna())
- return 0
return 1
/obj/add_blood(mob/living/carbon/M)
- if(..() == 0)
+ if(!..())
return 0
return add_blood_list(M)
/obj/item/add_blood(mob/living/carbon/M)
- var/blood_count = blood_DNA == null ? 0 : blood_DNA.len
- if(..() == 0)
+ var/blood_count = !blood_DNA ? 0 : blood_DNA.len
+ if(!..())
return 0
//apply the blood-splatter overlay if it isn't already in there
if(!blood_count && initial(icon) && initial(icon_state))
@@ -312,14 +312,14 @@ var/list/blood_splatter_icons = list()
return 1 //we applied blood to the item
/obj/item/clothing/gloves/add_blood(mob/living/carbon/M)
- if(..() == 0)
+ if(!..())
return 0
transfer_blood = rand(2, 4)
bloody_hands_mob = M
return 1
/turf/simulated/add_blood(mob/living/carbon/human/M)
- if(..() == 0)
+ if(!..())
return 0
var/obj/effect/decal/cleanable/blood/B = locate() in contents //check for existing blood splatter
@@ -330,7 +330,7 @@ var/list/blood_splatter_icons = list()
return 1 //we bloodied the floor
/mob/living/carbon/human/add_blood(mob/living/carbon/M)
- if(..() == 0)
+ if(!..())
return 0
add_blood_list(M)
bloody_hands = rand(2, 4)
diff --git a/code/game/gamemodes/meteor/meteors.dm b/code/game/gamemodes/meteor/meteors.dm
index 2ddf8698ca5..0924648d33f 100644
--- a/code/game/gamemodes/meteor/meteors.dm
+++ b/code/game/gamemodes/meteor/meteors.dm
@@ -237,8 +237,7 @@
..(heavy)
explosion(src.loc, 0, 0, 4, 3, 0)
new /obj/effect/decal/cleanable/greenglow(get_turf(src))
- for(var/mob/living/L in view(5, src))
- L.irradiate(40)
+ radiation_pulse(get_turf(src), 2, 5, 50, 1)
//Meaty Ore
/obj/effect/meteor/meaty
diff --git a/code/game/machinery/computer/dna_console.dm b/code/game/machinery/computer/dna_console.dm
index 5de7d5b4adb..4f00df16887 100644
--- a/code/game/machinery/computer/dna_console.dm
+++ b/code/game/machinery/computer/dna_console.dm
@@ -84,7 +84,7 @@
switch(viable_occupant.stat)
if(CONSCIOUS) occupant_status += "Conscious"
if(UNCONSCIOUS) occupant_status += "Unconscious"
- else occupant_status += "DEAD - Cannot Operate"
+ else occupant_status += "DEAD"
occupant_status += ""
occupant_status += "
Health:
[viable_occupant.health] %
"
occupant_status += "Radiation Level:
[viable_occupant.radiation] %
"
@@ -95,8 +95,6 @@
else
viable_occupant = null
occupant_status += "Invalid DNA structure"
- if (viable_occupant && viable_occupant.stat == DEAD)
- viable_occupant = null // No editing the dead.
else
occupant_status += "No subject detected"
@@ -202,7 +200,7 @@
temp_html += "
\tUI: No Data"
if(se)
temp_html += "
\tSE: [se] "
- if(viable_occupant && viable_occupant.stat != DEAD) temp_html += "Occupant "
+ if(viable_occupant) temp_html += "Occupant "
else temp_html += "Occupant "
if(injectorready) temp_html += "Injector"
else temp_html += "Injector"
@@ -327,7 +325,7 @@
if(istype(buffer_slot))
buffer_slot.Cut()
if("transferbuffer")
- if(num && viable_occupant && viable_occupant.stat != DEAD)
+ if(num && viable_occupant)
num = Clamp(num, 1, NUMBER_OF_BUFFERS)
var/list/buffer_slot = buffer[num]
if(istype(buffer_slot)) //15 and 40 are just magic numbers that were here before so i didnt touch them, they are initial boundaries of damage
@@ -394,7 +392,7 @@
diskette.loc = get_turf(src)
diskette = null
if("pulseui","pulsese")
- if(num && viable_occupant && connected && viable_occupant.stat != DEAD)
+ if(num && viable_occupant && connected)
radduration = Wrap(radduration, 1, RADIATION_DURATION_MAX+1)
radstrength = Wrap(radstrength, 1, RADIATION_STRENGTH_MAX+1)
diff --git a/code/game/machinery/doors/airlock_types.dm b/code/game/machinery/doors/airlock_types.dm
index e703f41c727..ba606e037ec 100644
--- a/code/game/machinery/doors/airlock_types.dm
+++ b/code/game/machinery/doors/airlock_types.dm
@@ -146,8 +146,7 @@
..()
/obj/machinery/door/airlock/uranium/proc/radiate()
- for(var/mob/living/L in range (3,src))
- L.irradiate(15)
+ radiation_pulse(get_turf(src), 3, 3, 15, 0)
return
/obj/machinery/door/airlock/plasma
diff --git a/code/game/mecha/equipment/tools/other_tools.dm b/code/game/mecha/equipment/tools/other_tools.dm
index ac8c7e541d5..531f20faa01 100644
--- a/code/game/mecha/equipment/tools/other_tools.dm
+++ b/code/game/mecha/equipment/tools/other_tools.dm
@@ -500,10 +500,6 @@
/obj/item/mecha_parts/mecha_equipment/generator/nuclear/process()
if(..())
- for(var/mob/living/carbon/M in view(chassis))
- if(istype(M,/mob/living/carbon/human))
- M.irradiate(rad_per_cycle*3)
- else
- M.irradiate(rad_per_cycle)
+ radiation_pulse(get_turf(src), 2, 7, rad_per_cycle, 1)
diff --git a/code/game/mecha/mecha_control_console.dm b/code/game/mecha/mecha_control_console.dm
index cbd9075f608..ab7641dd681 100644
--- a/code/game/mecha/mecha_control_console.dm
+++ b/code/game/mecha/mecha_control_console.dm
@@ -77,7 +77,7 @@
Airtank: [M.return_pressure()]kPa
Pilot: [M.occupant||"None"]
Location: [get_area(M)||"Unknown"]
- Active equipment: [M.selected||"None"]"}
+ Active equipment: [M.selected||"None"]
"}
if(istype(M, /obj/mecha/working/ripley))
var/obj/mecha/working/ripley/RM = M
answer += "Used cargo space: [RM.cargo.len/RM.cargo_capacity*100]%
"
diff --git a/code/game/objects/items/devices/traitordevices.dm b/code/game/objects/items/devices/traitordevices.dm
index f77c8951f4f..3e081af2025 100644
--- a/code/game/objects/items/devices/traitordevices.dm
+++ b/code/game/objects/items/devices/traitordevices.dm
@@ -98,7 +98,7 @@ effective or pretty fucking useless.
if(M)
if(intensity >= 5)
M.apply_effect(round(intensity/1.5), PARALYZE)
- M.irradiate(intensity*10)
+ M.rad_act(intensity*10)
else
user << "The radioactive microlaser is still recharging."
diff --git a/code/game/objects/items/nuke_tools.dm b/code/game/objects/items/nuke_tools.dm
index ee10b83dbc9..f8a0a093a12 100644
--- a/code/game/objects/items/nuke_tools.dm
+++ b/code/game/objects/items/nuke_tools.dm
@@ -23,8 +23,7 @@
if(cooldown < world.time - 60)
cooldown = world.time
flick("plutonium_core_pulse", src)
- for(var/mob/living/L in range(4,get_turf(src)))
- L.irradiate(40)
+ radiation_pulse(get_turf(src), 1, 4, 40, 1)
//nuke core box, for carrying the core
/obj/item/nuke_core_container
diff --git a/code/game/objects/items/weapons/dna_injector.dm b/code/game/objects/items/weapons/dna_injector.dm
index 660a7dd078a..e94ac89b828 100644
--- a/code/game/objects/items/weapons/dna_injector.dm
+++ b/code/game/objects/items/weapons/dna_injector.dm
@@ -18,9 +18,6 @@
/obj/item/weapon/dnainjector/proc/inject(mob/living/carbon/M, mob/user)
if(M.has_dna() && !(M.disabilities & NOCLONE))
- if(M.stat == DEAD) //prevents dead people from having their DNA changed
- user << "You can't modify [M]'s DNA while \he's dead."
- return
M.radiation += rand(20/(damage_coeff ** 2),50/(damage_coeff ** 2))
var/log_msg = "[key_name(user)] injected [key_name(M)] with the [name]"
for(var/datum/mutation/human/HM in remove_mutations)
diff --git a/code/game/objects/radiation.dm b/code/game/objects/radiation.dm
new file mode 100644
index 00000000000..2498b6fe825
--- /dev/null
+++ b/code/game/objects/radiation.dm
@@ -0,0 +1,35 @@
+/proc/radiation_pulse(turf/epicenter, heavy_range, light_range, severity, log=0)
+ if(!epicenter) return
+
+ if(!istype(epicenter, /turf))
+ epicenter = get_turf(epicenter.loc)
+
+ if(log)
+ message_admins("Radiation pulse with size ([heavy_range], [light_range]) and severity [severity] in area [epicenter.loc.name] ")
+ log_game("Radiation pulse with size ([heavy_range], [light_range]) and severity [severity] in area [epicenter.loc.name] ")
+
+ if(heavy_range > light_range)
+ light_range = heavy_range
+
+ for(var/atom/T in range(light_range, epicenter))
+ var/distance = get_dist(epicenter, T)
+ if(distance < 0)
+ distance = 0
+ if(distance < heavy_range)
+ T.rad_act(severity)
+ else if(distance == heavy_range)
+ if(prob(50))
+ T.rad_act(severity)
+ else
+ T.rad_act(severity / 2)
+ else if(distance <= light_range)
+ T.rad_act(severity / 2)
+ return 1
+
+/atom/proc/rad_act(var/severity)
+ return 1
+
+/mob/living/rad_act(amount)
+ if(amount)
+ var/blocked = run_armor_check(null, "rad", "Your clothes feel warm.", "Your clothes feel warm.")
+ apply_effect(amount, IRRADIATE, blocked)
diff --git a/code/game/objects/structures/false_walls.dm b/code/game/objects/structures/false_walls.dm
index d5534842ddb..188911cc2a0 100644
--- a/code/game/objects/structures/false_walls.dm
+++ b/code/game/objects/structures/false_walls.dm
@@ -169,8 +169,7 @@
if(!active)
if(world.time > last_event+15)
active = 1
- for(var/mob/living/L in range(3,src))
- L.irradiate(4)
+ radiation_pulse(get_turf(src), 0, 3, 15, 1)
for(var/turf/simulated/wall/mineral/uranium/T in orange(1,src))
T.radiate()
last_event = world.time
diff --git a/code/game/objects/structures/statues.dm b/code/game/objects/structures/statues.dm
index 8eec60f7283..f54046e7065 100644
--- a/code/game/objects/structures/statues.dm
+++ b/code/game/objects/structures/statues.dm
@@ -176,8 +176,7 @@
if(!active)
if(world.time > last_event+15)
active = 1
- for(var/mob/living/L in range(3,src))
- L.irradiate(12)
+ radiation_pulse(get_turf(src), 3, 3, 12, 0)
last_event = world.time
active = null
return
diff --git a/code/game/turfs/simulated/floor/mineral_floor.dm b/code/game/turfs/simulated/floor/mineral_floor.dm
index b1b1a728cf4..612aec92013 100644
--- a/code/game/turfs/simulated/floor/mineral_floor.dm
+++ b/code/game/turfs/simulated/floor/mineral_floor.dm
@@ -163,8 +163,7 @@
if(!active)
if(world.time > last_event+15)
active = 1
- for(var/mob/living/L in range(3,src))
- L.irradiate(1)
+ radiation_pulse(get_turf(src), 3, 3, 1, 0)
for(var/turf/simulated/floor/mineral/uranium/T in orange(1,src))
T.radiate()
last_event = world.time
diff --git a/code/game/turfs/simulated/walls_mineral.dm b/code/game/turfs/simulated/walls_mineral.dm
index be9d5945c9d..84aae77504d 100644
--- a/code/game/turfs/simulated/walls_mineral.dm
+++ b/code/game/turfs/simulated/walls_mineral.dm
@@ -80,8 +80,7 @@
if(!active)
if(world.time > last_event+15)
active = 1
- for(var/mob/living/L in range(3,src))
- L.irradiate(4)
+ radiation_pulse(get_turf(src), 3, 3, 4, 0)
for(var/turf/simulated/wall/mineral/uranium/T in orange(1,src))
T.radiate()
last_event = world.time
diff --git a/code/modules/admin/IsBanned.dm b/code/modules/admin/IsBanned.dm
index 9687091df9a..19eff86457e 100644
--- a/code/modules/admin/IsBanned.dm
+++ b/code/modules/admin/IsBanned.dm
@@ -3,46 +3,11 @@
/world/IsBanned(key,address,computer_id)
if (!key || !address || !computer_id)
log_access("Failed Login (invalid data): [key] [address]-[computer_id]")
- return list("reason"="invalid login data", "desc"="Your computer provided invalid or blank information to the server on connection (byond username, IP, and Computer ID.) Provided information for reference: Username:'[key]' IP:'[address]' Computer ID:'[computer_id]' If you continue to get this error, please restart byond or contact byond support.")
- if(ckey(key) in admin_datums)
- //It has proven to be a bad idea to make admins completely immune to bans, making them have to wait for someone with daemon access
- //to add a daemon ban to finally stop them. Admin tempbans and admin permabans are special, high-level ban types, which are there to help
- //deal with rogue admins quicker. If admin tempbans or admin permabans are ever needed, it should be consider a big deal. The same applies if
- //admin bans are ever abused. This ban type does NOT check for IP or Computer ID. The reason for this is so a player cannot find/steal an admin's
- //computer id, set it on his computer, get himself banned, resulting in the admin getting banned aswell. - this happens to also be the reason why
- //admins were immune to bans in the first place.
- if(!config.ban_legacy_system)
- var/ckeytext = ckey(key)
-
- if(!establish_db_connection())
- world.log << "Ban database connection failure. Admin [ckeytext] not checked"
- diary << "Ban database connection failure. Admin [ckeytext] not checked"
- return
-
- var/DBQuery/query = dbcon.NewQuery("SELECT ckey, ip, computerid, a_ckey, reason, expiration_time, duration, bantime, bantype FROM [format_table_name("ban")] WHERE (ckey = '[ckeytext]') AND (bantype = 'ADMIN_PERMABAN' OR (bantype = 'ADMIN_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)")
-
- query.Execute()
-
- while(query.NextRow())
- var/pckey = query.item[1]
- //var/pip = query.item[2]
- //var/pcid = query.item[3]
- var/ackey = query.item[4]
- var/reason = query.item[5]
- var/expiration = query.item[6]
- var/duration = query.item[7]
- var/bantime = query.item[8]
- var/bantype = query.item[9]
-
- var/expires = ""
- if(text2num(duration) > 0)
- expires = " The ban is for [duration] minutes and expires on [expiration] (server time)."
-
- var/desc = "\nReason: You, or another user of this computer or connection ([pckey]) is banned from playing here. The ban reason is:\n[reason]\nThis ban was applied by [ackey] on [bantime], [expires]"
-
- return list("reason"="[bantype]", "desc"="[desc]")
-
- return ..()
+ return list("reason"="invalid login data", "desc"="Error: Could not check ban status, Please try again. Error message: Your computer provided invalid or blank information to the server on connection (byond username, IP, and Computer ID.) Provided information for reference: Username:'[key]' IP:'[address]' Computer ID:'[computer_id]'. (If you continue to get this error, please restart byond or contact byond support.)")
+ var/admin = 0
+ var/ckey = ckey(key)
+ if((ckey in admin_datums) || (ckey in deadmins))
+ admin = 1
//Guest Checking
if(IsGuestKey(key))
@@ -54,7 +19,7 @@
return list("reason"="guest", "desc"="\nReason: Sorry but the server is currently not accepting connections from never before seen players or guests. If you have played on this server with a byond account before, please log in to the byond account you have played from.")
//Population Cap Checking
- if(config.extreme_popcap && living_player_count() >= config.extreme_popcap && !(ckey(key) in admin_datums))
+ if(config.extreme_popcap && living_player_count() >= config.extreme_popcap && !admin)
log_access("Failed Login: [key] - Population cap reached")
return list("reason"="popcap", "desc"= "\nReason: [config.extreme_popcap_message]")
@@ -63,10 +28,13 @@
//Ban Checking
. = CheckBan( ckey(key), computer_id, address )
if(.)
- log_access("Failed Login: [key] [computer_id] [address] - Banned [.["reason"]]")
- return .
-
- return ..() //default pager ban stuff
+ if (admin)
+ log_admin("The admin [key] has been allowed to bypass a matching ban on [.["key"]]")
+ message_admins("The admin [key] has been allowed to bypass a matching ban on [.["key"]]")
+ addclientmessage(ckey,"You have been allowed to bypass a matching ban on [.["key"]]")
+ else
+ log_access("Failed Login: [key] [computer_id] [address] - Banned [.["reason"]]")
+ return .
else
@@ -85,7 +53,7 @@
if(computer_id)
cidquery = " OR computerid = '[computer_id]' "
- var/DBQuery/query = dbcon.NewQuery("SELECT ckey, ip, computerid, a_ckey, reason, expiration_time, duration, bantime, bantype FROM [format_table_name("ban")] WHERE (ckey = '[ckeytext]' [ipquery] [cidquery]) AND (bantype = 'PERMABAN' OR (bantype = 'TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)")
+ var/DBQuery/query = dbcon.NewQuery("SELECT ckey, ip, computerid, a_ckey, reason, expiration_time, duration, bantime, bantype FROM [format_table_name("ban")] WHERE (ckey = '[ckeytext]' [ipquery] [cidquery]) AND (bantype = 'PERMABAN' OR bantype = 'ADMIN_PERMABAN' OR ((bantype = 'TEMPBAN' OR bantype = 'ADMIN_TEMPBAN') AND expiration_time > Now())) AND isnull(unbanned)")
query.Execute()
@@ -99,12 +67,46 @@
var/duration = query.item[7]
var/bantime = query.item[8]
var/bantype = query.item[9]
-
+ if (bantype == "ADMIN_PERMABAN" || bantype == "ADMIN_TEMPBAN")
+ //admin bans MUST match on ckey to prevent cid-spoofing attacks
+ // as well as dynamic ip abuse
+ if (pckey != ckey)
+ continue
+ if (admin)
+ if (bantype == "ADMIN_PERMABAN" || bantype == "ADMIN_TEMPBAN")
+ log_admin("The admin [key] is admin banned, and has been disallowed access")
+ message_admins("The admin [key] is admin banned, and has been disallowed access")
+ else
+ log_admin("The admin [key] has been allowed to bypass a matching ban on [pckey]")
+ message_admins("The admin [key] has been allowed to bypass a matching ban on [pckey]")
+ addclientmessage(ckey,"You have been allowed to bypass a matching ban on [pckey]")
+ continue
var/expires = ""
if(text2num(duration) > 0)
expires = " The ban is for [duration] minutes and expires on [expiration] (server time)."
+ else
+ expires = " The is a permanent ban."
var/desc = "\nReason: You, or another user of this computer or connection ([pckey]) is banned from playing here. The ban reason is:\n[reason]\nThis ban was applied by [ackey] on [bantime], [expires]"
- return list("reason"="[bantype]", "desc"="[desc]")
- return ..() //default pager ban stuff
+ . = list("reason"="[bantype]", "desc"="[desc]")
+
+
+ log_access("Failed Login: [key] [computer_id] [address] - Banned [.["reason"]]")
+ return .
+
+
+ . = ..() //default pager ban stuff
+ if (.)
+ //byond will not trigger isbanned() for "global" host bans,
+ //ie, ones where the "apply to this game only" checkbox is not checked (defaults to not checked)
+ //So it's safe to let admins walk thru host/sticky bans here
+ if (admin)
+ log_admin("The admin [key] has been allowed to bypass a matching host/sticky ban")
+ message_admins("The admin [key] has been allowed to bypass a matching host/sticky ban")
+ addclientmessage(ckey,"You have been allowed to bypass a matching host/sticky ban")
+ return null
+ else
+ log_access("Failed Login: [key] [computer_id] [address] - Banned [.["message"]]")
+
+ return .
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 92ea19f1afc..f900be95527 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -66,7 +66,8 @@ var/list/admin_verbs_ban = list(
/client/proc/unban_panel,
/client/proc/jobbans,
/client/proc/unjobban_panel,
- /client/proc/DB_ban_panel
+ /client/proc/DB_ban_panel,
+ /client/proc/stickybanpanel
)
var/list/admin_verbs_sounds = list(
/client/proc/play_local_sound,
diff --git a/code/modules/admin/stickyban.dm b/code/modules/admin/stickyban.dm
new file mode 100644
index 00000000000..d3a198a52bb
--- /dev/null
+++ b/code/modules/admin/stickyban.dm
@@ -0,0 +1,204 @@
+/datum/admins/proc/stickyban(action,data)
+ if(!check_rights(R_BAN))
+ return
+ switch (action)
+ if ("show")
+ stickyban_show()
+ if ("add")
+ var/list/ban = list()
+ var/ckey
+ ban["admin"] = usr.key
+ ban["type"] = list("sticky")
+ ban["reason"] = "(InGameBan)([usr.key])" //this will be displayed in dd only
+
+ if (data["ckey"])
+ ckey = ckey(data["ckey"])
+ else
+ ckey = input(usr,"Ckey","Ckey","") as text|null
+ if (!ckey)
+ return
+ ckey = ckey(ckey)
+ if (get_stickyban_from_ckey(ckey))
+ usr << "Error: Can not add a stickyban: User already has a current sticky ban"
+
+ if (data["reason"])
+ ban["message"] = data["reason"]
+ else
+ var/reason = input(usr,"Reason","Reason","Ban Evasion") as text|null
+ if (!reason)
+ return
+ ban["message"] = "[reason]"
+
+ world.SetConfig("ban",ckey,list2stickyban(ban))
+
+ log_admin("[key_name(usr)] has stickybanned [ckey].\nReason: [ban["message"]]")
+ message_admins("[key_name_admin(usr)] has stickybanned [ckey].\nReason: [ban["message"]]")
+
+ if ("remove")
+ if (!data["ckey"])
+ return
+ var/ckey = data["ckey"]
+
+ var/ban = get_stickyban_from_ckey(ckey)
+ if (!ban)
+ usr << "Error: No sticky ban for [ckey] found!"
+ return
+ if (alert("Are you sure you want to remove the sticky ban on [ckey]?","Are you sure","Yes","No") == "No")
+ return
+ if (!get_stickyban_from_ckey(ckey))
+ usr << "Error: The ban disappeared."
+ return
+ world.SetConfig("ban",ckey, null)
+
+ log_admin("[key_name(usr)] removed [ckey]'s stickyban")
+ message_admins("[key_name_admin(usr)] removed [ckey]'s stickyban")
+
+ if ("remove_alt")
+ if (!data["ckey"])
+ return
+ var/ckey = data["ckey"]
+ if (!data["alt"])
+ return
+ var/alt = ckey(data["alt"])
+ var/ban = get_stickyban_from_ckey(ckey)
+ if (!ban)
+ usr << "Error: No sticky ban for [ckey] found!"
+ return
+
+ var/found = 0
+ //we have to do it this way because byond keeps the case in its sticky ban matches WHY!!!
+ for (var/key in ban["keys"])
+ if (ckey(key) == alt)
+ found = 1
+ break
+
+ if (!found)
+ usr << "Error: [alt] is not linked to [ckey]'s sticky ban!"
+ return
+
+ if (alert("Are you sure you want to disassociate [alt] from [ckey]'s sticky ban? \nNote: Nothing stops byond from re-linking them","Are you sure","Yes","No") == "No")
+ return
+
+ //we have to do this again incase something changes
+ ban = get_stickyban_from_ckey(ckey)
+ if (!ban)
+ usr << "Error: The ban disappeared."
+ return
+
+ found = 0
+ for (var/key in ban["keys"])
+ if (ckey(key) == alt)
+ ban["keys"] -= key
+ found = 1
+ break
+
+ if (!found)
+ usr << "Error: [alt] link to [ckey]'s sticky ban disappeared."
+ return
+
+ world.SetConfig("ban",ckey,list2stickyban(ban))
+
+ log_admin("[key_name(usr)] has disassociated [alt] from [ckey]'s sticky ban")
+ message_admins("[key_name_admin(usr)] has disassociated [alt] from [ckey]'s sticky ban")
+
+ if ("edit")
+ if (!data["ckey"])
+ return
+ var/ckey = data["ckey"]
+ var/ban = get_stickyban_from_ckey(ckey)
+ if (!ban)
+ usr << "Error: No sticky ban for [ckey] found!"
+ return
+ var/oldreason = ban["message"]
+ var/reason = input(usr,"Reason","Reason","[ban["message"]]") as text|null
+ if (!reason || reason == oldreason)
+ return
+ //we have to do this again incase something changed while we waited for input
+ ban = get_stickyban_from_ckey(ckey)
+ if (!ban)
+ usr << "Error: The ban disappeared."
+ return
+ ban["message"] = "[reason]"
+
+ world.SetConfig("ban",ckey,list2stickyban(ban))
+
+ log_admin("[key_name(usr)] has edited [ckey]'s sticky ban reason from [oldreason] to [reason]")
+ message_admins("[key_name_admin(usr)] has edited [ckey]'s sticky ban reason from [oldreason] to [reason]")
+
+/datum/admins/proc/stickyban_gethtml(ckey, ban)
+ . = "\[-\][ckey]
"
+ . += "[ban["message"]] \[Edit\]
"
+ if (ban["admin"])
+ . += "[ban["admin"]]
"
+ else
+ . += "LEGACY
"
+ . += "Caught keys
\n"
+ for (var/key in ban["keys"])
+ if (ckey(key) == ckey)
+ continue
+ . += "- \[-\][key]
"
+ . += "
\n"
+
+/datum/admins/proc/stickyban_show()
+ if(!check_rights(R_BAN))
+ return
+ var/list/bans = world.GetConfig("ban")
+ var/banhtml = ""
+ for(var/key in bans)
+ var/ckey = ckey(key)
+ var/ban = stickyban2list(world.GetConfig("ban",key))
+ banhtml += "
\n"
+ banhtml += stickyban_gethtml(ckey,ban)
+
+ var/html = {"
+
+ Sticky Bans
+
+
+ All Sticky Bans:
\[+\]
+ [banhtml]
+
+ "}
+ usr << browse(html,"window=stickybans;size=700x400")
+
+/proc/get_stickyban_from_ckey(var/ckey)
+ if (!ckey)
+ return null
+ ckey = ckey(ckey)
+ . = null
+ for (var/key in world.GetConfig("ban"))
+ if (ckey(key) == ckey)
+ . = stickyban2list(world.GetConfig("ban",key))
+ break
+
+/proc/stickyban2list(var/ban)
+ if (!ban)
+ return null
+ . = params2list(ban)
+ .["keys"] = text2list(.["keys"], ",")
+ .["type"] = text2list(.["type"], ",")
+ .["IP"] = text2list(.["IP"], ",")
+ .["computer_id"] = text2list(.["computer_id"], ",")
+
+
+/proc/list2stickyban(var/list/ban)
+ if (!ban || !islist(ban))
+ return null
+ . = ban.Copy()
+ if (.["keys"])
+ .["keys"] = list2text(.["keys"], ",")
+ if (.["type"])
+ .["type"] = list2text(.["type"], ",")
+ if (.["IP"])
+ .["IP"] = list2text(.["IP"], ",")
+ if (.["computer_id"])
+ .["computer_id"] = list2text(.["computer_id"], ",")
+ . = list2params(.)
+
+
+/client/proc/stickybanpanel()
+ set name = "Sticky Ban Panel"
+ set category = "Admin"
+ if (!holder)
+ return
+ holder.stickyban_show()
\ No newline at end of file
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index d832c960d13..495923b86d5 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -23,6 +23,8 @@
message_admins("[key_name_admin(usr)] Rejected [C.key]'s admin help. [C.key]'s Adminhelp verb has been returned to them")
log_admin("[key_name(usr)] Rejected [C.key]'s admin help")
+ else if(href_list["stickyban"])
+ stickyban(href_list["stickyban"],href_list)
else if(href_list["makeAntag"])
if (!ticker.mode)
@@ -2135,4 +2137,4 @@
O.ears = text2path(href_list["outfit_ears"])
custom_outfits.Add(O)
- message_admins("[key_name(usr)] created \"[O.name]\" outfit!")
\ No newline at end of file
+ message_admins("[key_name(usr)] created \"[O.name]\" outfit!")
diff --git a/code/modules/client/client defines.dm b/code/modules/client/client defines.dm
index 2e41bb6717e..7063b2fb8f9 100644
--- a/code/modules/client/client defines.dm
+++ b/code/modules/client/client defines.dm
@@ -1,3 +1,4 @@
+
/client
////////////////
//ADMIN THINGS//
diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm
index 5ecd635021b..70db67c27c5 100644
--- a/code/modules/client/client procs.dm
+++ b/code/modules/client/client procs.dm
@@ -175,13 +175,19 @@ var/next_external_rsc = 0
src.changes()
else
winset(src, "rpane.changelogb", "background-color=#eaeaea;font-style=bold")
+
+ if (ckey in clientmessages)
+ for (var/message in clientmessages[ckey])
+ src << message
+ clientmessages.Remove(ckey)
if (config && config.autoconvert_notes)
convert_notes_sql(ckey)
- //////////////
- //DISCONNECT//
- //////////////
+//////////////
+//DISCONNECT//
+//////////////
+
/client/Del()
if(holder)
adminGreet(1)
diff --git a/code/modules/client/message.dm b/code/modules/client/message.dm
new file mode 100644
index 00000000000..a18950fe8d1
--- /dev/null
+++ b/code/modules/client/message.dm
@@ -0,0 +1,9 @@
+var/list/clientmessages = list()
+
+proc/addclientmessage(var/ckey, var/message)
+ ckey = ckey(ckey)
+ if (!ckey || !message)
+ return
+ if (!(ckey in clientmessages))
+ clientmessages[ckey] = list()
+ clientmessages[ckey] += message
\ No newline at end of file
diff --git a/code/modules/events/radiation_storm.dm b/code/modules/events/radiation_storm.dm
index c83ea419df4..13c57a9a5a6 100644
--- a/code/modules/events/radiation_storm.dm
+++ b/code/modules/events/radiation_storm.dm
@@ -37,9 +37,9 @@
if(istype(C, /mob/living/carbon/human))
var/mob/living/carbon/human/H = C
if(prob(5))
- H.irradiate(rand(100, 160))
+ H.rad_act(rand(100, 160))
else
- H.irradiate(rand(15, 75))
+ H.rad_act(rand(15, 75))
if(prob(25))
if(prob(75))
randmutb(H)
@@ -49,7 +49,7 @@
else if(istype(C, /mob/living/carbon/monkey))
var/mob/living/carbon/monkey/M = C
- M.irradiate(rand(15, 75))
+ M.rad_act(rand(15, 75))
/datum/round_event/radiation_storm/end()
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 2b585fa1f89..4a15c82b74d 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -684,7 +684,7 @@
if(prob(current_size * 5) && hand.w_class >= ((11-current_size)/2) && unEquip(hand))
step_towards(hand, src)
src << "\The [S] pulls \the [hand] from your grip!"
- irradiate(current_size * 3)
+ rad_act(current_size * 3)
if(mob_negates_gravity())
return
..()
diff --git a/code/modules/mob/living/carbon/human/species_types.dm b/code/modules/mob/living/carbon/human/species_types.dm
index cd5c473014b..95105c87bdc 100644
--- a/code/modules/mob/living/carbon/human/species_types.dm
+++ b/code/modules/mob/living/carbon/human/species_types.dm
@@ -122,7 +122,7 @@ datum/species/human/spec_death(gibbed, mob/living/carbon/human/H)
switch(proj_type)
if(/obj/item/projectile/energy/floramut)
if(prob(15))
- H.irradiate(rand(30,80))
+ H.rad_act(rand(30,80))
H.Weaken(5)
H.visible_message("[H] writhes in pain as \his vacuoles boil.", "You writhe in pain as your vacuoles boil!", "You hear the crunching of leaves.")
if(prob(80))
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index c30c3527d60..a63a44b5a03 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -293,7 +293,4 @@
if(stat || paralysis || stunned || weakened || restrained())
return 1
-/mob/living/proc/irradiate(amount)
- if(amount)
- var/blocked = run_armor_check(null, "rad", "Your clothes feel warm", "Your clothes feel warm")
- apply_effect(amount, IRRADIATE, blocked)
+//Looking for irradiate()? It's been moved to radiation.dm under the rad_act() for mobs.
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index e39e59063dc..a8ff6fb6361 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -74,6 +74,7 @@ var/list/ai_list = list()
var/obj/machinery/camera/portable/builtInCamera
/mob/living/silicon/ai/New(loc, var/datum/ai_laws/L, var/obj/item/device/mmi/B, var/safety = 0)
+ ..()
rename_self("ai", 1)
name = real_name
anchored = 1
@@ -144,8 +145,7 @@ var/list/ai_list = list()
builtInCamera = new /obj/machinery/camera/portable(src)
builtInCamera.network = list("SS13")
- ..()
- return
+
/mob/living/silicon/ai/Destroy()
ai_list -= src
diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm
index 12a73e8edc4..7d87854a7ca 100644
--- a/code/modules/mob/living/simple_animal/hostile/hostile.dm
+++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm
@@ -94,14 +94,14 @@
return
/mob/living/simple_animal/hostile/proc/PickTarget(list/Targets)//Step 3, pick amongst the possible, attackable targets
- if(!Targets.len)//We didnt find nothin!
- return
if(target != null)//If we already have a target, but are told to pick again, calculate the lowest distance between all possible, and pick from the lowest distance targets
for(var/atom/A in Targets)
var/target_dist = get_dist(src, target)
var/possible_target_distance = get_dist(src, A)
if(target_dist < possible_target_distance)
Targets -= A
+ if(!Targets.len)//We didnt find nothin!
+ return
var/chosen_target = pick(Targets)//Pick the remaining targets (if any) at random
return chosen_target
diff --git a/code/modules/power/gravitygenerator.dm b/code/modules/power/gravitygenerator.dm
index 8eb7af7db8f..104a437c2f2 100644
--- a/code/modules/power/gravitygenerator.dm
+++ b/code/modules/power/gravitygenerator.dm
@@ -350,8 +350,7 @@ var/const/GRAV_NEEDS_WRENCH = 3
/obj/machinery/gravity_generator/main/proc/pulse_radiation()
- for(var/mob/living/L in view(7, src))
- L.irradiate(20)
+ radiation_pulse(get_turf(src), 3, 7, 20, 1)
// Shake everyone on the z level to let them know that gravity was enagaged/disenagaged.
/obj/machinery/gravity_generator/main/proc/shake_everyone()
diff --git a/code/modules/power/singularity/particle_accelerator/particle.dm b/code/modules/power/singularity/particle_accelerator/particle.dm
index a9ff8952161..889d9a3189b 100644
--- a/code/modules/power/singularity/particle_accelerator/particle.dm
+++ b/code/modules/power/singularity/particle_accelerator/particle.dm
@@ -56,7 +56,7 @@
/obj/effect/accelerated_particle/proc/toxmob(mob/living/M)
- M.irradiate(energy*6)
+ M.rad_act(energy*6)
M.updatehealth()
return
diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm
index b9578936e24..6223ff52e7c 100644
--- a/code/modules/power/singularity/singularity.dm
+++ b/code/modules/power/singularity/singularity.dm
@@ -364,13 +364,13 @@
radiation += round((energy-150)/10,1)
radiationmin = round((radiation/5),1)
for(var/mob/living/M in view(toxrange, src.loc))
- M.irradiate(rand(radiationmin,radiation))
+ M.rad_act(rand(radiationmin,radiation))
/obj/singularity/proc/combust_mobs()
for(var/mob/living/carbon/C in orange(20, src))
C.visible_message("[C]'s skin bursts into flame!", \
- "You feel an inner fire as your skin is suddenly covered in fire!")
+ "You feel an inner fire as your skin bursts into flames!")
C.adjust_fire_stacks(5)
C.IgniteMob()
return
diff --git a/code/modules/power/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm
index 9fb8bfb9d52..2d01b3cf8b3 100644
--- a/code/modules/power/supermatter/supermatter.dm
+++ b/code/modules/power/supermatter/supermatter.dm
@@ -119,7 +119,7 @@
var/mob/living/carbon/human/H = mob
H.hallucination += max(50, min(300, DETONATION_HALLUCINATION * sqrt(1 / (get_dist(mob, src) + 1)) ) )
var/rads = DETONATION_RADS * sqrt( 1 / (get_dist(mob, src) + 1) )
- mob.irradiate(rads)
+ mob.rad_act(rads)
explode()
@@ -181,7 +181,7 @@
for(var/mob/living/l in range(src, round((power / 100) ** 0.25)))
var/rads = (power / 10) * sqrt( 1 / max(get_dist(l, src),1) )
- l.irradiate(rads)
+ l.rad_act(rads)
power -= (power/500)**3
@@ -259,7 +259,7 @@
playsound(get_turf(src), 'sound/effects/supermatter.ogg', 50, 1)
- user.irradiate(150)
+ radiation_pulse(get_turf(src), 1, 1, 150, 1)
/obj/machinery/power/supermatter_shard/Bumped(atom/AM as mob|obj)
@@ -292,9 +292,8 @@
power += 200
//Some poor sod got eaten, go ahead and irradiate people nearby.
+ radiation_pulse(get_turf(src), 4, 10, 500, 1)
for(var/mob/living/L in range(10))
- var/rads = 500 * sqrt( 1 / (get_dist(L, src) + 1) )
- L.irradiate(rads)
investigate_log("has irradiated [L] after consuming [AM].", "supermatter")
if(L in view())
L.show_message("As \the [src] slowly stops resonating, you find your skin covered in new radiation burns.", 1,\
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index 9252eb3eccf..02729e68ac9 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -138,7 +138,8 @@
var/mob/living/M = user
if (M.disabilities & CLUMSY && prob(40))
user << "You shoot yourself in the foot with \the [src]!"
- process_fire(user,user,0,params)
+ var/shot_leg = pick("l_leg", "r_leg")
+ process_fire(user,user,0,params, zone_override = shot_leg)
M.drop_item()
return
@@ -182,7 +183,7 @@
return 0
-/obj/item/weapon/gun/proc/process_fire(atom/target as mob|obj|turf, mob/living/user as mob|obj, message = 1, params)
+/obj/item/weapon/gun/proc/process_fire(atom/target as mob|obj|turf, mob/living/user as mob|obj, message = 1, params, zone_override)
add_fingerprint(user)
if(semicd)
@@ -200,7 +201,7 @@
if( i>1 && !(src in get_both_hands(user))) //for burst firing
break
if(chambered)
- if(!chambered.fire(target, user, params, , suppressed))
+ if(!chambered.fire(target, user, params, , suppressed, zone_override))
shoot_with_empty_chamber(user)
break
else
@@ -216,7 +217,7 @@
sleep(fire_delay)
else
if(chambered)
- if(!chambered.fire(target, user, params, , suppressed))
+ if(!chambered.fire(target, user, params, , suppressed, zone_override))
shoot_with_empty_chamber(user)
return
else
diff --git a/code/modules/projectiles/guns/energy/nuclear.dm b/code/modules/projectiles/guns/energy/nuclear.dm
index a5752e23273..dbe0225dd79 100644
--- a/code/modules/projectiles/guns/energy/nuclear.dm
+++ b/code/modules/projectiles/guns/energy/nuclear.dm
@@ -90,11 +90,11 @@
switch(fail_tick)
if(0 to 200)
fail_tick += (2*(100-reliability))
- M.irradiate(40)
+ M.rad_act(40)
M << "Your [name] feels warmer."
if(201 to INFINITY)
SSobj.processing.Remove(src)
- M.irradiate(80)
+ M.rad_act(80)
crit_fail = 1
M << "Your [name]'s reactor overloads!"
diff --git a/code/modules/projectiles/guns/projectile/launchers.dm b/code/modules/projectiles/guns/projectile/launchers.dm
index 0d9220733f1..ecd46e53e3f 100644
--- a/code/modules/projectiles/guns/projectile/launchers.dm
+++ b/code/modules/projectiles/guns/projectile/launchers.dm
@@ -25,6 +25,7 @@
icon = 'icons/mecha/mecha_equipment.dmi'
icon_state = "mecha_grenadelnchr"
mag_type = /obj/item/ammo_box/magazine/internal/cylinder/grenademulti
+ pin = /obj/item/device/firing_pin
/obj/item/weapon/gun/projectile/revolver/grenadelauncher/cyborg/attack_self()
return
diff --git a/code/modules/research/experimentor.dm b/code/modules/research/experimentor.dm
index 436dde364d9..6cf59f10915 100644
--- a/code/modules/research/experimentor.dm
+++ b/code/modules/research/experimentor.dm
@@ -286,9 +286,7 @@
ejectItem()
if(prob(EFFECT_PROB_VERYLOW-badThingCoeff))
visible_message("[src] malfunctions, melting [exp_on] and leaking radiation!")
- for(var/mob/living/m in oview(1, src))
- m.irradiate(25)
- investigate_log("Experimentor has irradiated [m]", "experimentor") //One entry per person so we know what was irradiated.
+ radiation_pulse(get_turf(src), 1, 1, 25, 1)
ejectItem(TRUE)
if(prob(EFFECT_PROB_LOW-badThingCoeff))
visible_message("[src] malfunctions, spewing toxic waste!")
diff --git a/html/changelog.html b/html/changelog.html
index 02e025d64f0..02c2180dec9 100644
--- a/html/changelog.html
+++ b/html/changelog.html
@@ -55,6 +55,18 @@
-->
+
28 September 2015
+
Feemjmeem updated:
+
+ - Rechargers can now be wrenched and unwrenched by cyborgs.
+ - Rechargers no longer stop working forever if you move them from an unpowered area to a powered area, and now actually look powered off when they are.
+ - Guns and batons can no longer be placed in unwrenched chargers.
+
+
Razharas updated:
+
+ - Added button to preferences menu that kills all currently playing sounds when pressed, now you can kill midis and any other sounds for real.
+
+
26 September 2015
MMMiracles updated:
diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml
index 867ac88f9a9..18baf6d31f7 100644
--- a/html/changelogs/.all_changelog.yml
+++ b/html/changelogs/.all_changelog.yml
@@ -1942,3 +1942,12 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py.
WJohnston:
- tweak: Repiped the entire station. Atmosia and the disposals loop were not touched.
- tweak: Moved mulebot delivery from misc lab to RnD.
+2015-09-28:
+ Feemjmeem:
+ - bugfix: Rechargers can now be wrenched and unwrenched by cyborgs.
+ - bugfix: Rechargers no longer stop working forever if you move them from an unpowered
+ area to a powered area, and now actually look powered off when they are.
+ - bugfix: Guns and batons can no longer be placed in unwrenched chargers.
+ Razharas:
+ - rscadd: Added button to preferences menu that kills all currently playing sounds
+ when pressed, now you can kill midis and any other sounds for real.
diff --git a/html/changelogs/Razharas-PR-12019.yml b/html/changelogs/Razharas-PR-12019.yml
deleted file mode 100644
index 1d0ae023665..00000000000
--- a/html/changelogs/Razharas-PR-12019.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-author: Razharas
-
-delete-after: True
-
-changes:
- - rscadd: "Added button to preferences menu that kills all currently playing sounds when pressed, now you can kill midis and any other sounds for real."
diff --git a/html/changelogs/feemjmeem-rechargerfixes.yml b/html/changelogs/feemjmeem-rechargerfixes.yml
deleted file mode 100644
index f8edd0d178b..00000000000
--- a/html/changelogs/feemjmeem-rechargerfixes.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-author: Feemjmeem
-
-delete-after: True
-changes:
- - bugfix: "Rechargers can now be wrenched and unwrenched by cyborgs."
- - bugfix: "Rechargers no longer stop working forever if you move them from an unpowered area to a powered area, and now actually look powered off when they are."
- - bugfix: "Guns and batons can no longer be placed in unwrenched chargers."
diff --git a/html/changelogs/phil235-AddbloodRuntimeFix.yml b/html/changelogs/phil235-AddbloodRuntimeFix.yml
new file mode 100644
index 00000000000..da2c5a7018d
--- /dev/null
+++ b/html/changelogs/phil235-AddbloodRuntimeFix.yml
@@ -0,0 +1,8 @@
+
+author: phil235
+
+delete-after: True
+
+changes:
+ - bugfix: "Fixed the syndicate cyborg's grenade launcher."
+
diff --git a/html/changelogs/phil235-DeadDna.yml b/html/changelogs/phil235-DeadDna.yml
new file mode 100644
index 00000000000..d8edfe99c1e
--- /dev/null
+++ b/html/changelogs/phil235-DeadDna.yml
@@ -0,0 +1,8 @@
+
+author: phil235
+
+delete-after: True
+
+changes:
+ - tweak: "You can modify the dna of corpses again."
+
diff --git a/tgstation.dme b/tgstation.dme
index 32d682bff36..38771327bb6 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -544,6 +544,7 @@
#include "code\game\objects\explosion.dm"
#include "code\game\objects\items.dm"
#include "code\game\objects\objs.dm"
+#include "code\game\objects\radiation.dm"
#include "code\game\objects\structures.dm"
#include "code\game\objects\weapons.dm"
#include "code\game\objects\effects\aliens.dm"
@@ -833,8 +834,9 @@
#include "code\modules\admin\NewBan.dm"
#include "code\modules\admin\newbanjob.dm"
#include "code\modules\admin\player_panel.dm"
-#include "code\modules\admin\secrets.dm"
-#include "code\modules\admin\sql_notes.dm"
+#include "code\modules\admin\secrets.dm"
+#include "code\modules\admin\stickyban.dm"
+#include "code\modules\admin\sql_notes.dm"
#include "code\modules\admin\topic.dm"
#include "code\modules\admin\watchlist.dm"
#include "code\modules\admin\DB ban\functions.dm"
@@ -904,6 +906,7 @@
#include "code\modules\awaymissions\mission_code\wildwest.dm"
#include "code\modules\client\client defines.dm"
#include "code\modules\client\client procs.dm"
+#include "code\modules\client\message.dm"
#include "code\modules\client\preferences.dm"
#include "code\modules\client\preferences_savefile.dm"
#include "code\modules\client\preferences_toggles.dm"