diff --git a/baystation12.dme b/baystation12.dme
index 1c47249d9b..d5cfe67d7e 100644
--- a/baystation12.dme
+++ b/baystation12.dme
@@ -1115,6 +1115,7 @@
#include "code\modules\mob\living\carbon\carbon_defines.dm"
#include "code\modules\mob\living\carbon\carbon_powers.dm"
#include "code\modules\mob\living\carbon\give.dm"
+#include "code\modules\mob\living\carbon\resist.dm"
#include "code\modules\mob\living\carbon\shock.dm"
#include "code\modules\mob\living\carbon\viruses.dm"
#include "code\modules\mob\living\carbon\alien\alien.dm"
diff --git a/code/ZAS/Atom.dm b/code/ZAS/Atom.dm
index 6f75f4ea29..e655295f78 100644
--- a/code/ZAS/Atom.dm
+++ b/code/ZAS/Atom.dm
@@ -50,7 +50,7 @@ atom/proc/c_airblock(turf/other)
#ifdef ZASDBG
ASSERT(isturf(other))
#endif
- return !CanPass(null, other, 0, 0) + 2*!CanPass(null, other, 1.5, 1)
+ return (AIR_BLOCKED*!CanPass(null, other, 0, 0))|(ZONE_BLOCKED*!CanPass(null, other, 1.5, 1))
turf/c_airblock(turf/other)
diff --git a/code/ZAS/Controller.dm b/code/ZAS/Controller.dm
index 4614b3a183..83d2a54ec3 100644
--- a/code/ZAS/Controller.dm
+++ b/code/ZAS/Controller.dm
@@ -154,7 +154,18 @@ Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_coun
#ifdef ZASDBG
var/updated = 0
#endif
+
+ //defer updating of self-zone-blocked turfs until after all other turfs have been updated.
+ //this hopefully ensures that non-self-zone-blocked turfs adjacent to self-zone-blocked ones
+ //have valid zones when the self-zone-blocked turfs update.
+ var/list/deferred = list()
+
for(var/turf/T in updating)
+ //check if the turf is self-zone-blocked
+ if(T.c_airblock(T) & ZONE_BLOCKED)
+ deferred += T
+ continue
+
T.update_air_properties()
T.post_update_air_properties()
T.needs_air_update = 0
@@ -164,6 +175,15 @@ Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_coun
#endif
//sleep(1)
+ for(var/turf/T in deferred)
+ T.update_air_properties()
+ T.post_update_air_properties()
+ T.needs_air_update = 0
+ #ifdef ZASDBG
+ T.overlays -= mark
+ updated++
+ #endif
+
#ifdef ZASDBG
if(updated != updating.len)
tick_progress = "[updating.len - updated] tiles left unupdated."
@@ -307,7 +327,7 @@ Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_coun
/datum/controller/air_system/proc/mark_edge_sleeping(connection_edge/E)
#ifdef ZASDBG
- ASSERT(istype(E)
+ ASSERT(istype(E))
#endif
if(E.sleeping) return
active_edges.Remove(E)
@@ -315,7 +335,7 @@ Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_coun
/datum/controller/air_system/proc/mark_edge_active(connection_edge/E)
#ifdef ZASDBG
- ASSERT(istype(E)
+ ASSERT(istype(E))
#endif
if(!E.sleeping) return
active_edges.Add(E)
diff --git a/code/ZAS/Turf.dm b/code/ZAS/Turf.dm
index 85d47d9f6f..331c00dd37 100644
--- a/code/ZAS/Turf.dm
+++ b/code/ZAS/Turf.dm
@@ -1,255 +1,259 @@
-/turf/simulated/var/zone/zone
-/turf/simulated/var/open_directions
-
-/turf/var/needs_air_update = 0
-/turf/var/datum/gas_mixture/air
-
-/turf/simulated/proc/update_graphic(list/graphic_add = null, list/graphic_remove = null)
- if(graphic_add && graphic_add.len)
- overlays += graphic_add
- if(graphic_remove && graphic_remove.len)
- overlays -= graphic_remove
-
-/turf/proc/update_air_properties()
- var/block = c_airblock(src)
- if(block & AIR_BLOCKED)
- //dbg(blocked)
- return 1
-
- #ifdef ZLEVELS
- for(var/d = 1, d < 64, d *= 2)
- #else
- for(var/d = 1, d < 16, d *= 2)
- #endif
-
- var/turf/unsim = get_step(src, d)
-
- if(!unsim)
- continue
-
- block = unsim.c_airblock(src)
-
- if(block & AIR_BLOCKED)
- //unsim.dbg(air_blocked, turn(180,d))
- continue
-
- var/r_block = c_airblock(unsim)
-
- if(r_block & AIR_BLOCKED)
- continue
-
- if(istype(unsim, /turf/simulated))
-
- var/turf/simulated/sim = unsim
- if(air_master.has_valid_zone(sim))
-
- air_master.connect(sim, src)
-
-/turf/simulated/update_air_properties()
- if(zone && zone.invalid)
- c_copy_air()
- zone = null //Easier than iterating through the list at the zone.
-
- var/s_block = c_airblock(src)
- if(s_block & AIR_BLOCKED)
- #ifdef ZASDBG
- if(verbose) world << "Self-blocked."
- //dbg(blocked)
- #endif
- if(zone)
- var/zone/z = zone
- if(locate(/obj/machinery/door/airlock) in src) //Hacky, but prevents normal airlocks from rebuilding zones all the time
- z.remove(src)
- else
- z.rebuild()
-
- return 1
-
- var/previously_open = open_directions
- open_directions = 0
-
- var/list/postponed
- #ifdef ZLEVELS
- for(var/d = 1, d < 64, d *= 2)
- #else
- for(var/d = 1, d < 16, d *= 2)
- #endif
-
- var/turf/unsim = get_step(src, d)
-
- if(!unsim) //edge of map
- continue
-
- var/block = unsim.c_airblock(src)
- if(block & AIR_BLOCKED)
-
- #ifdef ZASDBG
- if(verbose) world << "[d] is blocked."
- //unsim.dbg(air_blocked, turn(180,d))
- #endif
-
- continue
-
- var/r_block = c_airblock(unsim)
- if(r_block & AIR_BLOCKED)
-
- #ifdef ZASDBG
- if(verbose) world << "[d] is blocked."
- //dbg(air_blocked, d)
- #endif
-
- //Check that our zone hasn't been cut off recently.
- //This happens when windows move or are constructed. We need to rebuild.
- if((previously_open & d) && istype(unsim, /turf/simulated))
- var/turf/simulated/sim = unsim
- if(zone && sim.zone == zone)
- zone.rebuild()
- return
-
- continue
-
- open_directions |= d
-
- if(istype(unsim, /turf/simulated))
-
- var/turf/simulated/sim = unsim
- sim.open_directions |= reverse_dir[d]
-
- if(air_master.has_valid_zone(sim))
-
- //Might have assigned a zone, since this happens for each direction.
- if(!zone)
-
- //if((block & ZONE_BLOCKED) || (r_block & ZONE_BLOCKED && !(s_block & ZONE_BLOCKED)))
- if(((block & ZONE_BLOCKED) && !(r_block & ZONE_BLOCKED)) || (r_block & ZONE_BLOCKED && !(s_block & ZONE_BLOCKED)))
- #ifdef ZASDBG
- if(verbose) world << "[d] is zone blocked."
- //dbg(zone_blocked, d)
- #endif
-
- //Postpone this tile rather than exit, since a connection can still be made.
- if(!postponed) postponed = list()
- postponed.Add(sim)
-
- else
-
- sim.zone.add(src)
-
- #ifdef ZASDBG
- dbg(assigned)
- if(verbose) world << "Added to [zone]"
- #endif
-
- else if(sim.zone != zone)
-
- #ifdef ZASDBG
- if(verbose) world << "Connecting to [sim.zone]"
- #endif
-
- air_master.connect(src, sim)
-
-
- #ifdef ZASDBG
- else if(verbose) world << "[d] has same zone."
-
- else if(verbose) world << "[d] has invalid zone."
- #endif
-
- else
-
- //Postponing connections to tiles until a zone is assured.
- if(!postponed) postponed = list()
- postponed.Add(unsim)
-
- if(!air_master.has_valid_zone(src)) //Still no zone, make a new one.
- var/zone/newzone = new/zone()
- newzone.add(src)
-
- #ifdef ZASDBG
- dbg(created)
-
- ASSERT(zone)
- #endif
-
- //At this point, a zone should have happened. If it hasn't, don't add more checks, fix the bug.
-
- for(var/turf/T in postponed)
- air_master.connect(src, T)
-
-/turf/proc/post_update_air_properties()
- if(connections) connections.update_all()
-
-/turf/assume_air(datum/gas_mixture/giver) //use this for machines to adjust air
- return 0
-
-/turf/proc/assume_gas(gasid, moles, temp = 0)
- return 0
-
-/turf/return_air()
- //Create gas mixture to hold data for passing
- var/datum/gas_mixture/GM = new
-
- GM.adjust_multi("oxygen", oxygen, "carbon_dioxide", carbon_dioxide, "nitrogen", nitrogen, "phoron", phoron)
- GM.temperature = temperature
-
- return GM
-
-/turf/remove_air(amount as num)
- var/datum/gas_mixture/GM = new
-
- var/sum = oxygen + carbon_dioxide + nitrogen + phoron
- if(sum>0)
- GM.gas["oxygen"] = (oxygen/sum)*amount
- GM.gas["carbon_dioxide"] = (carbon_dioxide/sum)*amount
- GM.gas["nitrogen"] = (nitrogen/sum)*amount
- GM.gas["phoron"] = (phoron/sum)*amount
-
- GM.temperature = temperature
- GM.update_values()
-
- return GM
-
-/turf/simulated/assume_air(datum/gas_mixture/giver)
- var/datum/gas_mixture/my_air = return_air()
- my_air.merge(giver)
-
-/turf/simulated/assume_gas(gasid, moles, temp = null)
- var/datum/gas_mixture/my_air = return_air()
-
- if(isnull(temp))
- my_air.adjust_gas(gasid, moles)
- else
- my_air.adjust_gas_temp(gasid, moles, temp)
-
- return 1
-
-/turf/simulated/remove_air(amount as num)
- var/datum/gas_mixture/my_air = return_air()
- return my_air.remove(amount)
-
-/turf/simulated/return_air()
- if(zone)
- if(!zone.invalid)
- air_master.mark_zone_update(zone)
- return zone.air
- else
- if(!air)
- make_air()
- c_copy_air()
- return air
- else
- if(!air)
- make_air()
- return air
-
-/turf/proc/make_air()
- air = new/datum/gas_mixture
- air.temperature = temperature
- air.adjust_multi("oxygen", oxygen, "carbon_dioxide", carbon_dioxide, "nitrogen", nitrogen, "phoron", phoron)
- air.group_multiplier = 1
- air.volume = CELL_VOLUME
-
-/turf/simulated/proc/c_copy_air()
- if(!air) air = new/datum/gas_mixture
- air.copy_from(zone.air)
- air.group_multiplier = 1
+/turf/simulated/var/zone/zone
+/turf/simulated/var/open_directions
+
+/turf/var/needs_air_update = 0
+/turf/var/datum/gas_mixture/air
+
+/turf/simulated/proc/update_graphic(list/graphic_add = null, list/graphic_remove = null)
+ if(graphic_add && graphic_add.len)
+ overlays += graphic_add
+ if(graphic_remove && graphic_remove.len)
+ overlays -= graphic_remove
+
+/turf/proc/update_air_properties()
+ var/block = c_airblock(src)
+ if(block & AIR_BLOCKED)
+ //dbg(blocked)
+ return 1
+
+ #ifdef ZLEVELS
+ for(var/d = 1, d < 64, d *= 2)
+ #else
+ for(var/d = 1, d < 16, d *= 2)
+ #endif
+
+ var/turf/unsim = get_step(src, d)
+
+ if(!unsim)
+ continue
+
+ block = unsim.c_airblock(src)
+
+ if(block & AIR_BLOCKED)
+ //unsim.dbg(air_blocked, turn(180,d))
+ continue
+
+ var/r_block = c_airblock(unsim)
+
+ if(r_block & AIR_BLOCKED)
+ continue
+
+ if(istype(unsim, /turf/simulated))
+
+ var/turf/simulated/sim = unsim
+ if(air_master.has_valid_zone(sim))
+
+ air_master.connect(sim, src)
+
+/turf/simulated/update_air_properties()
+
+ if(zone && zone.invalid)
+ c_copy_air()
+ zone = null //Easier than iterating through the list at the zone.
+
+ var/s_block = c_airblock(src)
+ if(s_block & AIR_BLOCKED)
+ #ifdef ZASDBG
+ if(verbose) world << "Self-blocked."
+ //dbg(blocked)
+ #endif
+ if(zone)
+ var/zone/z = zone
+
+ if(s_block & ZONE_BLOCKED) //Hacky, but prevents normal airlocks from rebuilding zones all the time
+ z.remove(src)
+ else
+ z.rebuild()
+
+ return 1
+
+ var/previously_open = open_directions
+ open_directions = 0
+
+ var/list/postponed
+ #ifdef ZLEVELS
+ for(var/d = 1, d < 64, d *= 2)
+ #else
+ for(var/d = 1, d < 16, d *= 2)
+ #endif
+
+ var/turf/unsim = get_step(src, d)
+
+ if(!unsim) //edge of map
+ continue
+
+ var/block = unsim.c_airblock(src)
+ if(block & AIR_BLOCKED)
+
+ #ifdef ZASDBG
+ if(verbose) world << "[d] is blocked."
+ //unsim.dbg(air_blocked, turn(180,d))
+ #endif
+
+ continue
+
+ var/r_block = c_airblock(unsim)
+ if(r_block & AIR_BLOCKED)
+
+ #ifdef ZASDBG
+ if(verbose) world << "[d] is blocked."
+ //dbg(air_blocked, d)
+ #endif
+
+ //Check that our zone hasn't been cut off recently.
+ //This happens when windows move or are constructed. We need to rebuild.
+ if((previously_open & d) && istype(unsim, /turf/simulated))
+ var/turf/simulated/sim = unsim
+ if(zone && sim.zone == zone)
+ zone.rebuild()
+ return
+
+ continue
+
+ open_directions |= d
+
+ if(istype(unsim, /turf/simulated))
+
+ var/turf/simulated/sim = unsim
+ sim.open_directions |= reverse_dir[d]
+
+ if(air_master.has_valid_zone(sim))
+
+ //Might have assigned a zone, since this happens for each direction.
+ if(!zone)
+
+ //We do not merge if
+ // they are blocking us and we are not blocking them, or if
+ // we are blocking them and not blocking ourselves - this prevents tiny zones from forming on doorways.
+ if(((block & ZONE_BLOCKED) && !(r_block & ZONE_BLOCKED)) || ((r_block & ZONE_BLOCKED) && !(s_block & ZONE_BLOCKED)))
+ #ifdef ZASDBG
+ if(verbose) world << "[d] is zone blocked."
+ //dbg(zone_blocked, d)
+ #endif
+
+ //Postpone this tile rather than exit, since a connection can still be made.
+ if(!postponed) postponed = list()
+ postponed.Add(sim)
+
+ else
+
+ sim.zone.add(src)
+
+ #ifdef ZASDBG
+ dbg(assigned)
+ if(verbose) world << "Added to [zone]"
+ #endif
+
+ else if(sim.zone != zone)
+
+ #ifdef ZASDBG
+ if(verbose) world << "Connecting to [sim.zone]"
+ #endif
+
+ air_master.connect(src, sim)
+
+
+ #ifdef ZASDBG
+ else if(verbose) world << "[d] has same zone."
+
+ else if(verbose) world << "[d] has invalid zone."
+ #endif
+
+ else
+
+ //Postponing connections to tiles until a zone is assured.
+ if(!postponed) postponed = list()
+ postponed.Add(unsim)
+
+ if(!air_master.has_valid_zone(src)) //Still no zone, make a new one.
+ var/zone/newzone = new/zone()
+ newzone.add(src)
+
+ #ifdef ZASDBG
+ dbg(created)
+
+ ASSERT(zone)
+ #endif
+
+ //At this point, a zone should have happened. If it hasn't, don't add more checks, fix the bug.
+
+ for(var/turf/T in postponed)
+ air_master.connect(src, T)
+
+/turf/proc/post_update_air_properties()
+ if(connections) connections.update_all()
+
+/turf/assume_air(datum/gas_mixture/giver) //use this for machines to adjust air
+ return 0
+
+/turf/proc/assume_gas(gasid, moles, temp = 0)
+ return 0
+
+/turf/return_air()
+ //Create gas mixture to hold data for passing
+ var/datum/gas_mixture/GM = new
+
+ GM.adjust_multi("oxygen", oxygen, "carbon_dioxide", carbon_dioxide, "nitrogen", nitrogen, "phoron", phoron)
+ GM.temperature = temperature
+
+ return GM
+
+/turf/remove_air(amount as num)
+ var/datum/gas_mixture/GM = new
+
+ var/sum = oxygen + carbon_dioxide + nitrogen + phoron
+ if(sum>0)
+ GM.gas["oxygen"] = (oxygen/sum)*amount
+ GM.gas["carbon_dioxide"] = (carbon_dioxide/sum)*amount
+ GM.gas["nitrogen"] = (nitrogen/sum)*amount
+ GM.gas["phoron"] = (phoron/sum)*amount
+
+ GM.temperature = temperature
+ GM.update_values()
+
+ return GM
+
+/turf/simulated/assume_air(datum/gas_mixture/giver)
+ var/datum/gas_mixture/my_air = return_air()
+ my_air.merge(giver)
+
+/turf/simulated/assume_gas(gasid, moles, temp = null)
+ var/datum/gas_mixture/my_air = return_air()
+
+ if(isnull(temp))
+ my_air.adjust_gas(gasid, moles)
+ else
+ my_air.adjust_gas_temp(gasid, moles, temp)
+
+ return 1
+
+/turf/simulated/remove_air(amount as num)
+ var/datum/gas_mixture/my_air = return_air()
+ return my_air.remove(amount)
+
+/turf/simulated/return_air()
+ if(zone)
+ if(!zone.invalid)
+ air_master.mark_zone_update(zone)
+ return zone.air
+ else
+ if(!air)
+ make_air()
+ c_copy_air()
+ return air
+ else
+ if(!air)
+ make_air()
+ return air
+
+/turf/proc/make_air()
+ air = new/datum/gas_mixture
+ air.temperature = temperature
+ air.adjust_multi("oxygen", oxygen, "carbon_dioxide", carbon_dioxide, "nitrogen", nitrogen, "phoron", phoron)
+ air.group_multiplier = 1
+ air.volume = CELL_VOLUME
+
+/turf/simulated/proc/c_copy_air()
+ if(!air) air = new/datum/gas_mixture
+ air.copy_from(zone.air)
+ air.group_multiplier = 1
diff --git a/code/ZAS/Zone.dm b/code/ZAS/Zone.dm
index b7042191fc..9fcddc9246 100644
--- a/code/ZAS/Zone.dm
+++ b/code/ZAS/Zone.dm
@@ -111,6 +111,13 @@ Class Procs:
#ifdef ZASDBG
T.dbg(merged)
#endif
+
+ //rebuild the old zone's edges so that the will be possesed by the new zone
+ for(var/connection_edge/E in edges)
+ if(E.contains_zone(into))
+ continue //don't need to rebuild this edge
+ for(var/turf/T in E.connecting_turfs)
+ air_master.mark_for_update(T)
/zone/proc/c_invalidate()
invalid = 1
diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm
index 8895e67c2a..3564c5e15f 100644
--- a/code/__HELPERS/unsorted.dm
+++ b/code/__HELPERS/unsorted.dm
@@ -927,30 +927,6 @@ proc/anim(turf/location as turf,target as mob|obj,a_icon,a_icon_state as text,fl
refined_trg -= B
continue moving
- var/list/doors = new/list()
-
- if(toupdate.len)
- for(var/turf/simulated/T1 in toupdate)
- for(var/obj/machinery/door/D2 in T1)
- doors += D2
- /*if(T1.parent)
- air_master.groups_to_rebuild += T1.parent
- else
- air_master.tiles_to_update += T1*/
-
- if(fromupdate.len)
- for(var/turf/simulated/T2 in fromupdate)
- for(var/obj/machinery/door/D2 in T2)
- doors += D2
- /*if(T2.parent)
- air_master.groups_to_rebuild += T2.parent
- else
- air_master.tiles_to_update += T2*/
-
- for(var/obj/O in doors)
- O:update_nearby_tiles(1)
-
-
proc/DuplicateObject(obj/original, var/perfectcopy = 0 , var/sameloc = 0)
if(!original)
diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm
index f3cc2a4028..5e1a551cec 100644
--- a/code/game/objects/structures/crates_lockers/closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets.dm
@@ -10,7 +10,7 @@
var/welded = 0
var/wall_mounted = 0 //never solid (You can always pass over it)
var/health = 100
- var/lastbang
+ var/breakout = 0 //if someone is currently breaking out. mutex
var/storage_capacity = 30 //This is so that someone can't pack hundreds of items in a locker/crate
//then open it in a populated area to crash clients.
var/open_sound = 'sound/machines/click.ogg'
@@ -262,12 +262,6 @@
if(!src.open())
user << "It won't budge!"
- if(!lastbang)
- lastbang = 1
- for (var/mob/M in hearers(src, null))
- M << text("BANG, bang!", max(0, 5 - get_dist(src, M)))
- spawn(30)
- lastbang = 0
/obj/structure/closet/attack_hand(mob/user as mob)
src.add_fingerprint(user)
@@ -315,3 +309,64 @@
dump_contents()
spawn(1) qdel(src)
return 1
+
+/obj/structure/closet/proc/req_breakout()
+ if(breakout)
+ return 0 //Already breaking out.
+ if(opened)
+ return 0 //Door's open... wait, why are you in it's contents then?
+ if(!welded)
+ return 0 //closed but not welded...
+ return 1
+
+/obj/structure/closet/proc/mob_breakout(var/mob/living/escapee)
+ var/breakout_time = 2 //2 minutes by default
+
+ if(!req_breakout())
+ return
+
+ //okay, so the closet is either welded or locked... resist!!!
+ escapee.next_move = world.time + 100
+ escapee.last_special = world.time + 100
+ escapee << "You lean on the back of \the [src] and start pushing the door open. (this will take about [breakout_time] minutes)"
+
+ visible_message("The [src] begins to shake violently!")
+
+ breakout = 1 //can't think of a better way to do this right now.
+ for(var/i in 1 to (6*breakout_time * 2)) //minutes * 6 * 5seconds * 2
+ playsound(src.loc, 'sound/effects/grillehit.ogg', 100, 1)
+ animate_shake()
+
+ if(!do_after(escapee, 50)) //5 seconds
+ breakout = 0
+ return
+ if(!escapee || escapee.stat || escapee.loc != src)
+ breakout = 0
+ return //closet/user destroyed OR user dead/unconcious OR user no longer in closet OR closet opened
+ //Perform the same set of checks as above for weld and lock status to determine if there is even still a point in 'resisting'...
+ if(!req_breakout())
+ breakout = 0
+ return
+
+ //Well then break it!
+ breakout = 0
+ escapee << "You successfully break out!"
+ visible_message("\the [escapee] successfully broke out of \the [src]!")
+ playsound(src.loc, 'sound/effects/grillehit.ogg', 100, 1)
+ break_open()
+ animate_shake()
+
+/obj/structure/closet/proc/break_open()
+ welded = 0
+ update_icon()
+ //Do this to prevent contents from being opened into nullspace (read: bluespace)
+ if(istype(loc, /obj/structure/bigDelivery))
+ var/obj/structure/bigDelivery/BD = loc
+ BD.unwrap()
+ open()
+
+/obj/structure/closet/proc/animate_shake()
+ var/init_px = pixel_x
+ var/shake_dir = pick(-1, 1)
+ animate(src, transform=turn(matrix(), 8*shake_dir), pixel_x=init_px + 2*shake_dir, time=1)
+ animate(transform=null, pixel_x=init_px, time=6, easing=ELASTIC_EASING)
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm b/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm
index a3b9f39928..df2985c42e 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm
@@ -132,3 +132,25 @@
overlays += "welded"
else
icon_state = icon_opened
+
+
+/obj/structure/closet/secure_closet/req_breakout()
+ if(!opened && locked) return 1
+ return ..() //It's a secure closet, but isn't locked.
+
+/obj/structure/closet/secure_closet/break_open()
+ desc += " It appears to be broken."
+ icon_state = icon_off
+ spawn()
+ flick(icon_broken, src)
+ sleep(10)
+ flick(icon_broken, src)
+ sleep(10)
+ broken = 1
+ locked = 0
+ update_icon()
+ //Do this to prevent contents from being opened into nullspace (read: bluespace)
+ if(istype(loc, /obj/structure/bigDelivery))
+ var/obj/structure/bigDelivery/BD = loc
+ BD.unwrap()
+ open()
\ No newline at end of file
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index 7353531f59..882fa059c0 100644
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -233,12 +233,6 @@
if(S.zone) S.zone.rebuild()
if(ispath(N, /turf/simulated/floor))
- //if the old turf had a zone, connect the new turf to it as well - Cael
- //Adjusted by SkyMarshal 5/10/13 - The air master will handle the addition of the new turf.
- //if(zone)
- // zone.RemoveTurf(src)
- // if(!zone.CheckStatus())
- // zone.SetStatus(ZONE_ACTIVE)
var/turf/simulated/W = new N( locate(src.x, src.y, src.z) )
//W.Assimilate_Air()
@@ -258,7 +252,7 @@
universe.OnTurfChange(W)
if(air_master)
- air_master.mark_for_update(src)
+ air_master.mark_for_update(src) //handle the addition of the new turf.
for(var/turf/space/S in range(W,1))
S.update_starlight()
@@ -267,10 +261,6 @@
return W
else
- //if(zone)
- // zone.RemoveTurf(src)
- // if(!zone.CheckStatus())
- // zone.SetStatus(ZONE_ACTIVE)
var/turf/W = new N( locate(src.x, src.y, src.z) )
W.lighting_lumcount += old_lumcount
diff --git a/code/modules/mob/living/carbon/resist.dm b/code/modules/mob/living/carbon/resist.dm
new file mode 100644
index 0000000000..95ee36b1f6
--- /dev/null
+++ b/code/modules/mob/living/carbon/resist.dm
@@ -0,0 +1,170 @@
+
+/mob/living/carbon/process_resist()
+
+ //drop && roll
+ if(on_fire)
+ fire_stacks -= 2 //reduced
+ Weaken(3)
+ spin(32,2)
+ visible_message(
+ "[src] rolls on the floor, trying to put themselves out!",
+ "You stop, drop, and roll!"
+ )
+ sleep(30)
+ if(fire_stacks <= 0)
+ visible_message(
+ "[src] has successfully extinguished themselves!",
+ "You extinguish yourself."
+ )
+ ExtinguishMob()
+ return
+
+ if(handcuffed)
+ spawn() escape_handcuffs()
+ else if(legcuffed)
+ spawn() escape_legcuffs()
+
+ ..()
+
+/mob/living/carbon/proc/escape_handcuffs()
+ if(!(last_special <= world.time)) return
+
+ next_move = world.time + 100
+ last_special = world.time + 100
+
+ if(can_break_cuffs()) //Don't want to do a lot of logic gating here.
+ break_handcuffs()
+ return
+
+ var/obj/item/weapon/handcuffs/HC = handcuffed
+
+ //A default in case you are somehow handcuffed with something that isn't an obj/item/weapon/handcuffs type
+ var/breakouttime = 1200
+ var/displaytime = 2 //Minutes to display in the "this will take X minutes."
+ //If you are handcuffed with actual handcuffs... Well what do I know, maybe someone will want to handcuff you with toilet paper in the future...
+ if(istype(HC))
+ breakouttime = HC.breakouttime
+ displaytime = breakouttime / 600 //Minutes
+
+ visible_message(
+ "[src] attempts to remove \the [HC]!",
+ "You attempt to remove \the [HC]. (This will take around [displaytime] minutes and you need to stand still)"
+ )
+
+ if(do_after(src, breakouttime))
+ if(!handcuffed || buckled)
+ return
+ visible_message(
+ "[src] manages to remove \the [handcuffed]!",
+ "You successfully remove \the [handcuffed]."
+ )
+ drop_from_inventory(handcuffed)
+
+/mob/living/carbon/proc/escape_legcuffs()
+ if(!(last_special <= world.time)) return
+
+ next_move = world.time + 100
+ last_special = world.time + 100
+
+ if(can_break_cuffs()) //Don't want to do a lot of logic gating here.
+ break_legcuffs()
+ return
+
+ var/obj/item/weapon/legcuffs/HC = legcuffed
+
+ //A default in case you are somehow legcuffed with something that isn't an obj/item/weapon/legcuffs type
+ var/breakouttime = 1200
+ var/displaytime = 2 //Minutes to display in the "this will take X minutes."
+ //If you are legcuffed with actual legcuffs... Well what do I know, maybe someone will want to legcuff you with toilet paper in the future...
+ if(istype(HC))
+ breakouttime = HC.breakouttime
+ displaytime = breakouttime / 600 //Minutes
+
+ visible_message(
+ "[usr] attempts to remove \the [HC]!",
+ "You attempt to remove \the [HC]. (This will take around [displaytime] minutes and you need to stand still)"
+ )
+
+ if(do_after(src, breakouttime))
+ if(!legcuffed || buckled)
+ return
+ visible_message(
+ "[src] manages to remove \the [legcuffed]!",
+ "You successfully remove \the [legcuffed]."
+ )
+
+ drop_from_inventory(legcuffed)
+ legcuffed = null
+ update_inv_legcuffed()
+
+/mob/living/carbon/proc/can_break_cuffs()
+ if(HULK in mutations)
+ return 1
+
+/mob/living/carbon/proc/break_handcuffs()
+ visible_message(
+ "[src] is trying to break \the [handcuffed]!",
+ "You attempt to break your [handcuffed.name]. (This will take around 5 seconds and you need to stand still)"
+ )
+
+ if(do_after(src, 50))
+ if(!handcuffed || buckled)
+ return
+
+ visible_message(
+ "[src] manages to break \the [handcuffed]!",
+ "You successfully break your [handcuffed.name]."
+ )
+
+ say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" ))
+
+ del(handcuffed)
+ handcuffed = null
+ if(buckled && buckled.buckle_require_restraints)
+ buckled.unbuckle_mob()
+ update_inv_handcuffed()
+
+/mob/living/carbon/proc/break_legcuffs()
+ src << "You attempt to break your legcuffs. (This will take around 5 seconds and you need to stand still)"
+ visible_message("[src] is trying to break the legcuffs!")
+
+ if(do_after(src, 50))
+ if(!legcuffed || buckled)
+ return
+
+ visible_message(
+ "[src] manages to break the legcuffs!",
+ "You successfully break your legcuffs."
+ )
+
+ say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" ))
+
+ del(legcuffed)
+ legcuffed = null
+ update_inv_legcuffed()
+
+/mob/living/carbon/human/can_break_cuffs()
+ if(species.can_shred(src,1))
+ return 1
+ return ..()
+
+/mob/living/carbon/escape_buckle()
+ if(!buckled) return
+ if(!(last_special <= world.time)) return
+
+ if(!restrained())
+ ..()
+ else
+ next_move = world.time + 100
+ last_special = world.time + 100
+ visible_message(
+ "[usr] attempts to unbuckle themself!",
+ "You attempt to unbuckle yourself. (This will take around 2 minutes and you need to stand still)"
+ )
+
+ if(do_after(usr, 1200))
+ if(!buckled)
+ return
+ visible_message("[usr] manages to unbuckle themself!",
+ "You successfully unbuckle yourself.")
+ buckled.user_unbuckle_mob(src)
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 5f62018723..d723cb5556 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -571,284 +571,84 @@ default behaviour is:
set name = "Resist"
set category = "IC"
- if(usr.stat || !isliving(usr) || usr.next_move > world.time)
- return
+ if(can_resist())
+ next_move = world.time + 20
+ process_resist()
- usr.next_move = world.time + 20
-
- var/mob/living/L = usr
+/mob/living/proc/can_resist()
+ //need to allow !canmove, or otherwise neck grabs can't be resisted
+ //so just check weakened instead.
+ if(stat || weakened)
+ return 0
+ if(next_move > world.time)
+ return 0
+ return 1
+/mob/living/proc/process_resist()
//Getting out of someone's inventory.
- if(istype(src.loc,/obj/item/weapon/holder))
- var/obj/item/weapon/holder/H = src.loc //Get our item holder.
- var/mob/M = H.loc //Get our mob holder (if any).
-
- if(istype(M))
- M.drop_from_inventory(H)
- M << "[H] wriggles out of your grip!"
- src << "You wriggle out of [M]'s grip!"
- else if(istype(H.loc,/obj/item))
- src << "You struggle free of [H.loc]."
- H.loc = get_turf(H)
-
- if(istype(M))
- for(var/atom/A in M.contents)
- if(istype(A,/mob/living/simple_animal/borer) || istype(A,/obj/item/weapon/holder))
- return
-
- M.status_flags &= ~PASSEMOTES
+ if(istype(src.loc, /obj/item/weapon/holder))
+ escape_inventory(src.loc)
return
- //Resisting control by an alien mind.
- if(istype(src.loc,/mob/living/simple_animal/borer))
- var/mob/living/simple_animal/borer/B = src.loc
- var/mob/living/captive_brain/H = src
-
- H << "You begin doggedly resisting the parasite's control (this will take approximately sixty seconds)."
- B.host << "You feel the captive mind of [src] begin to resist your control."
-
- spawn(rand(200,250)+B.host.brainloss)
-
- if(!B || !B.controlling)
- return
-
- B.host.adjustBrainLoss(rand(5,10))
- H << "With an immense exertion of will, you regain control of your body!"
- B.host << "You feel control of the host brain ripped from your grasp, and retract your probosci before the wild neural impulses can damage you."
-
- B.detatch()
-
- verbs -= /mob/living/carbon/proc/release_control
- verbs -= /mob/living/carbon/proc/punish_host
- verbs -= /mob/living/carbon/proc/spawn_larvae
-
- return
-
//resisting grabs (as if it helps anyone...)
- if ((!( L.stat ) && !( L.restrained() )))
- var/resisting = 0
- for(var/obj/O in L.requests)
- L.requests.Remove(O)
- qdel(O)
- resisting++
- for(var/obj/item/weapon/grab/G in usr.grabbed_by)
- resisting++
- switch(G.state)
- if(GRAB_PASSIVE)
- qdel(G)
- if(GRAB_AGGRESSIVE)
- if(prob(60)) //same chance of breaking the grab as disarm
- L.visible_message("[L] has broken free of [G.assailant]'s grip!")
- qdel(G)
- if(GRAB_NECK)
- //If the you move when grabbing someone then it's easier for them to break free. Same if the affected mob is immune to stun.
- if (((world.time - G.assailant.l_move_time < 30 || !L.stunned) && prob(15)) || prob(3))
- L.visible_message("[L] has broken free of [G.assailant]'s headlock!")
- qdel(G)
- if(resisting)
- L.visible_message("[L] resists!")
-
+ if (!restrained())
+ resist_grab()
//unbuckling yourself
- if(L.buckled && (L.last_special <= world.time) )
- if(iscarbon(L))
- var/mob/living/carbon/C = L
- if( C.handcuffed )
- C.next_move = world.time + 100
- C.last_special = world.time + 100
- C << "\red You attempt to unbuckle yourself. (This will take around 2 minutes and you need to stand still)"
- for(var/mob/O in viewers(L))
- O.show_message("\red [usr] attempts to unbuckle themself!", 1)
- spawn(0)
- if(do_after(usr, 1200))
- if(!C.buckled)
- return
- for(var/mob/O in viewers(C))
- O.show_message("\red [usr] manages to unbuckle themself!", 1)
- C << "\blue You successfully unbuckle yourself."
- C.buckled.user_unbuckle_mob(C)
- else
- L.buckled.user_unbuckle_mob(L)
+ if(buckled)
+ spawn() escape_buckle()
//Breaking out of a locker?
- else if( src.loc && (istype(src.loc, /obj/structure/closet)) )
- var/breakout_time = 2 //2 minutes by default
+ if( src.loc && (istype(src.loc, /obj/structure/closet)) )
+ var/obj/structure/closet/C = loc
+ spawn() C.mob_breakout(src)
- var/obj/structure/closet/C = L.loc
- if(C.opened)
- return //Door's open... wait, why are you in it's contents then?
- if(istype(L.loc, /obj/structure/closet/secure_closet))
- var/obj/structure/closet/secure_closet/SC = L.loc
- if(!SC.locked && !SC.welded)
- return //It's a secure closet, but isn't locked. Easily escapable from, no need to 'resist'
- else
- if(!C.welded)
- return //closed but not welded...
- // else Meh, lets just keep it at 2 minutes for now
- // breakout_time++ //Harder to get out of welded lockers than locked lockers
+/mob/living/proc/escape_inventory(obj/item/weapon/holder/H)
+ if(H != src.loc) return
+
+ var/mob/M = H.loc //Get our mob holder (if any).
- //okay, so the closet is either welded or locked... resist!!!
- usr.next_move = world.time + 100
- L.last_special = world.time + 100
- L << "\red You lean on the back of \the [C] and start pushing the door open. (this will take about [breakout_time] minutes)"
- for(var/mob/O in viewers(usr.loc))
- O.show_message("\red The [L.loc] begins to shake violently!", 1)
+ if(istype(M))
+ M.drop_from_inventory(H)
+ M << "[H] wriggles out of your grip!"
+ src << "You wriggle out of [M]'s grip!"
+ else if(istype(H.loc,/obj/item))
+ src << "You struggle free of [H.loc]."
+ H.loc = get_turf(H)
+ if(istype(M))
+ for(var/atom/A in M.contents)
+ if(istype(A,/mob/living/simple_animal/borer) || istype(A,/obj/item/weapon/holder))
+ return
- spawn(0)
- if(do_after(usr,(breakout_time*60*10))) //minutes * 60seconds * 10deciseconds
- if(!C || !L || L.stat != CONSCIOUS || L.loc != C || C.opened) //closet/user destroyed OR user dead/unconcious OR user no longer in closet OR closet opened
- return
+ M.status_flags &= ~PASSEMOTES
- //Perform the same set of checks as above for weld and lock status to determine if there is even still a point in 'resisting'...
- if(istype(L.loc, /obj/structure/closet/secure_closet))
- var/obj/structure/closet/secure_closet/SC = L.loc
- if(!SC.locked && !SC.welded)
- return
- else
- if(!C.welded)
- return
+/mob/living/proc/escape_buckle()
+ if(buckled)
+ buckled.user_unbuckle_mob(src)
- //Well then break it!
- if(istype(usr.loc, /obj/structure/closet/secure_closet))
- var/obj/structure/closet/secure_closet/SC = L.loc
- SC.desc = "It appears to be broken."
- SC.icon_state = SC.icon_off
- flick(SC.icon_broken, SC)
- sleep(10)
- flick(SC.icon_broken, SC)
- sleep(10)
- SC.broken = 1
- SC.locked = 0
- SC.update_icon()
- usr << "\red You successfully break out!"
- for(var/mob/O in viewers(L.loc))
- O.show_message("\red \the [usr] successfully broke out of \the [SC]!", 1)
- if(istype(SC.loc, /obj/structure/bigDelivery)) //Do this to prevent contents from being opened into nullspace (read: bluespace)
- var/obj/structure/bigDelivery/BD = SC.loc
- BD.attack_hand(usr)
- SC.open()
- else
- C.welded = 0
- C.update_icon()
- usr << "\red You successfully break out!"
- for(var/mob/O in viewers(L.loc))
- O.show_message("\red \the [usr] successfully broke out of \the [C]!", 1)
- if(istype(C.loc, /obj/structure/bigDelivery)) //nullspace ect.. read the comment above
- var/obj/structure/bigDelivery/BD = C.loc
- BD.attack_hand(usr)
- C.open()
-
- //drop && roll or breaking out of handcuffs
- else if(iscarbon(L))
- var/mob/living/carbon/CM = L
- if(CM.on_fire && CM.canmove)
- CM.fire_stacks -= 5
- CM.Weaken(3)
- CM.spin(32,2)
- CM.visible_message("[CM] rolls on the floor, trying to put themselves out!", \
- "You stop, drop, and roll!")
- sleep(30)
- if(fire_stacks <= 0)
- CM.visible_message("[CM] has successfully extinguished themselves!", \
- "You extinguish yourself.")
- ExtinguishMob()
- return
- if(CM.handcuffed && CM.canmove && (CM.last_special <= world.time))
- CM.next_move = world.time + 100
- CM.last_special = world.time + 100
-
- var/can_break_cuffs
- if(HULK in usr.mutations)
- can_break_cuffs = 1
- else if(istype(CM,/mob/living/carbon/human))
- var/mob/living/carbon/human/H = CM
- if(H.species.can_shred(H,1))
- can_break_cuffs = 1
-
- if(can_break_cuffs) //Don't want to do a lot of logic gating here.
- usr << "\red You attempt to break your handcuffs. (This will take around 5 seconds and you need to stand still)"
- for(var/mob/O in viewers(CM))
- O.show_message(text("\red [] is trying to break the handcuffs!", CM), 1)
- spawn(0)
- if(do_after(CM, 50))
- if(!CM.handcuffed || CM.buckled)
- return
- for(var/mob/O in viewers(CM))
- O.show_message(text("\red [] manages to break the handcuffs!", CM), 1)
- CM << "\red You successfully break your handcuffs."
- CM.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" ))
- qdel(CM.handcuffed)
- CM.handcuffed = null
- if(buckled && buckled.buckle_require_restraints)
- buckled.unbuckle_mob()
- CM.update_inv_handcuffed()
- else
- var/obj/item/weapon/handcuffs/HC = CM.handcuffed
- var/breakouttime = 1200 //A default in case you are somehow handcuffed with something that isn't an obj/item/weapon/handcuffs type
- var/displaytime = 2 //Minutes to display in the "this will take X minutes."
- if(istype(HC)) //If you are handcuffed with actual handcuffs... Well what do I know, maybe someone will want to handcuff you with toilet paper in the future...
- breakouttime = HC.breakouttime
- displaytime = breakouttime / 600 //Minutes
- CM << "\red You attempt to remove \the [HC]. (This will take around [displaytime] minutes and you need to stand still)"
- for(var/mob/O in viewers(CM))
- O.show_message( "\red [usr] attempts to remove \the [HC]!", 1)
- spawn(0)
- if(do_after(CM, breakouttime))
- if(!CM.handcuffed || CM.buckled)
- return // time leniency for lag which also might make this whole thing pointless but the server
- for(var/mob/O in viewers(CM))// lags so hard that 40s isn't lenient enough - Quarxink
- O.show_message("\red [CM] manages to remove the handcuffs!", 1)
- CM << "\blue You successfully remove \the [CM.handcuffed]."
- CM.drop_from_inventory(CM.handcuffed)
-
- else if(CM.legcuffed && CM.canmove && (CM.last_special <= world.time))
- CM.next_move = world.time + 100
- CM.last_special = world.time + 100
-
- var/can_break_cuffs
- if(HULK in usr.mutations)
- can_break_cuffs = 1
- else if(istype(CM,/mob/living/carbon/human))
- var/mob/living/carbon/human/H = CM
- if(H.species.can_shred(H,1))
- can_break_cuffs = 1
-
- if(can_break_cuffs) //Don't want to do a lot of logic gating here.
- usr << "\red You attempt to break your legcuffs. (This will take around 5 seconds and you need to stand still)"
- for(var/mob/O in viewers(CM))
- O.show_message(text("\red [] is trying to break the legcuffs!", CM), 1)
- spawn(0)
- if(do_after(CM, 50))
- if(!CM.legcuffed || CM.buckled)
- return
- for(var/mob/O in viewers(CM))
- O.show_message(text("\red [] manages to break the legcuffs!", CM), 1)
- CM << "\red You successfully break your legcuffs."
- CM.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" ))
- qdel(CM.legcuffed)
- CM.legcuffed = null
- CM.update_inv_legcuffed()
- else
- var/obj/item/weapon/legcuffs/HC = CM.legcuffed
- var/breakouttime = 1200 //A default in case you are somehow legcuffed with something that isn't an obj/item/weapon/legcuffs type
- var/displaytime = 2 //Minutes to display in the "this will take X minutes."
- if(istype(HC)) //If you are legcuffed with actual legcuffs... Well what do I know, maybe someone will want to legcuff you with toilet paper in the future...
- breakouttime = HC.breakouttime
- displaytime = breakouttime / 600 //Minutes
- CM << "\red You attempt to remove \the [HC]. (This will take around [displaytime] minutes and you need to stand still)"
- for(var/mob/O in viewers(CM))
- O.show_message( "\red [usr] attempts to remove \the [HC]!", 1)
- spawn(0)
- if(do_after(CM, breakouttime))
- if(!CM.legcuffed || CM.buckled)
- return // time leniency for lag which also might make this whole thing pointless but the server
- for(var/mob/O in viewers(CM))// lags so hard that 40s isn't lenient enough - Quarxink
- O.show_message("\red [CM] manages to remove the legcuffs!", 1)
- CM << "\blue You successfully remove \the [CM.legcuffed]."
- CM.drop_from_inventory(CM.legcuffed)
- CM.legcuffed = null
- CM.update_inv_legcuffed()
+/mob/living/proc/resist_grab()
+ var/resisting = 0
+ for(var/obj/O in requests)
+ requests.Remove(O)
+ qdel(O)
+ resisting++
+ for(var/obj/item/weapon/grab/G in grabbed_by)
+ resisting++
+ switch(G.state)
+ if(GRAB_PASSIVE)
+ qdel(G)
+ if(GRAB_AGGRESSIVE)
+ if(prob(60)) //same chance of breaking the grab as disarm
+ visible_message("[src] has broken free of [G.assailant]'s grip!")
+ qdel(G)
+ if(GRAB_NECK)
+ //If the you move when grabbing someone then it's easier for them to break free. Same if the affected mob is immune to stun.
+ if (((world.time - G.assailant.l_move_time < 30 || !stunned) && prob(15)) || prob(3))
+ visible_message("[src] has broken free of [G.assailant]'s headlock!")
+ qdel(G)
+ if(resisting)
+ visible_message("[src] resists!")
/mob/living/verb/lay_down()
set name = "Rest"
diff --git a/code/modules/mob/living/simple_animal/borer/borer_captive.dm b/code/modules/mob/living/simple_animal/borer/borer_captive.dm
index 156d362773..c0b2999b2f 100644
--- a/code/modules/mob/living/simple_animal/borer/borer_captive.dm
+++ b/code/modules/mob/living/simple_animal/borer/borer_captive.dm
@@ -32,4 +32,31 @@
M << "The captive mind of [src] whispers, \"[message]\""
/mob/living/captive_brain/emote(var/message)
- return
\ No newline at end of file
+ return
+
+/mob/living/captive_brain/can_resist()
+ return !(stat || next_move > world.time)
+
+/mob/living/captive_brain/process_resist()
+ //Resisting control by an alien mind.
+ if(istype(src.loc,/mob/living/simple_animal/borer))
+ var/mob/living/simple_animal/borer/B = src.loc
+ var/mob/living/captive_brain/H = src
+
+ H << "You begin doggedly resisting the parasite's control (this will take approximately sixty seconds)."
+ B.host << "You feel the captive mind of [src] begin to resist your control."
+
+ spawn(rand(200,250)+B.host.brainloss)
+ if(!B || !B.controlling) return
+
+ B.host.adjustBrainLoss(rand(5,10))
+ H << "With an immense exertion of will, you regain control of your body!"
+ B.host << "You feel control of the host brain ripped from your grasp, and retract your probosci before the wild neural impulses can damage you."
+ B.detatch()
+ verbs -= /mob/living/carbon/proc/release_control
+ verbs -= /mob/living/carbon/proc/punish_host
+ verbs -= /mob/living/carbon/proc/spawn_larvae
+
+ return
+
+ ..()
diff --git a/code/modules/mob/living/simple_animal/friendly/mouse.dm b/code/modules/mob/living/simple_animal/friendly/mouse.dm
index 5b207ce2a7..ad41fc28aa 100644
--- a/code/modules/mob/living/simple_animal/friendly/mouse.dm
+++ b/code/modules/mob/living/simple_animal/friendly/mouse.dm
@@ -56,7 +56,10 @@
verbs += /mob/living/proc/ventcrawl
verbs += /mob/living/proc/hide
- name = "[name] ([rand(1, 1000)])"
+ if(name == initial(name))
+ name = "[name] ([rand(1, 1000)])"
+ real_name = name
+
if(!body_color)
body_color = pick( list("brown","gray","white") )
icon_state = "mouse_[body_color]"
@@ -112,5 +115,10 @@
name = "Tom"
desc = "Jerry the cat is not amused."
+/mob/living/simple_animal/mouse/brown/Tom/New()
+ ..()
+ // Change my name back, don't want to be named Tom (666)
+ name = initial(name)
+
/mob/living/simple_animal/mouse/can_use_vents()
- return
\ No newline at end of file
+ return
diff --git a/code/modules/mob/mob_grab.dm b/code/modules/mob/mob_grab.dm
index 36065960dc..73c5ee1de7 100644
--- a/code/modules/mob/mob_grab.dm
+++ b/code/modules/mob/mob_grab.dm
@@ -103,7 +103,7 @@
affecting.loc = assailant.loc
if(state >= GRAB_NECK)
- affecting.Stun(1)
+ affecting.Stun(3)
if(isliving(affecting))
var/mob/living/L = affecting
L.adjustOxyLoss(1)
diff --git a/code/modules/recycling/sortingmachinery.dm b/code/modules/recycling/sortingmachinery.dm
index 0b4cd1a608..eb76325832 100755
--- a/code/modules/recycling/sortingmachinery.dm
+++ b/code/modules/recycling/sortingmachinery.dm
@@ -15,13 +15,15 @@
var/tag_x
attack_hand(mob/user as mob)
+ unwrap()
+
+ proc/unwrap()
if(wrapped) //sometimes items can disappear. For example, bombs. --rastaf0
wrapped.loc = (get_turf(src.loc))
if(istype(wrapped, /obj/structure/closet))
var/obj/structure/closet/O = wrapped
O.welded = 0
qdel(src)
- return
attackby(obj/item/W as obj, mob/user as mob)
if(istype(W, /obj/item/device/destTagger))
diff --git a/html/changelogs/HarpyEagle-NeckgrabFix.yml b/html/changelogs/HarpyEagle-NeckgrabFix.yml
new file mode 100644
index 0000000000..6ff399b77e
--- /dev/null
+++ b/html/changelogs/HarpyEagle-NeckgrabFix.yml
@@ -0,0 +1,5 @@
+author: HarpyEagle
+delete-after: True
+
+changes:
+ - bugfix: "Neck-grabbing someone now stuns them properly."
diff --git a/html/changelogs/PsiOmegaDelta-OfMiceAndMen.yml b/html/changelogs/PsiOmegaDelta-OfMiceAndMen.yml
new file mode 100644
index 0000000000..0a994dfbc4
--- /dev/null
+++ b/html/changelogs/PsiOmegaDelta-OfMiceAndMen.yml
@@ -0,0 +1,5 @@
+author: PsiOmegaDelta
+delete-after: True
+
+changes:
+ - tweak: "Mice are now numbered to aid admins."
diff --git a/icons/mob/robots.dmi b/icons/mob/robots.dmi
index 5237308264..b81f428661 100644
Binary files a/icons/mob/robots.dmi and b/icons/mob/robots.dmi differ