diff --git a/code/__defines/_planes+layers.dm b/code/__defines/_planes+layers.dm
index 60557d1dd7..3bee841881 100644
--- a/code/__defines/_planes+layers.dm
+++ b/code/__defines/_planes+layers.dm
@@ -68,6 +68,7 @@ What is the naming convention for planes or layers?
// Obj planes
#define OBJ_PLANE -35
+ #define STAIRS_LAYER 2.5 // Layer for stairs
#define HIDING_LAYER 2.6 // Layer at which mobs hide to be under things like tables
#define DOOR_OPEN_LAYER 2.7 // Under all objects if opened. 2.7 due to tables being at 2.6
#define TABLE_LAYER 2.8 // Just under stuff that wants to be slightly below common objects.
diff --git a/code/__defines/flags.dm b/code/__defines/flags.dm
index 18a2ec59ab..64782715d3 100644
--- a/code/__defines/flags.dm
+++ b/code/__defines/flags.dm
@@ -16,6 +16,7 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204
// datum_flags
#define DF_VAR_EDITED (1<<0)
#define DF_ISPROCESSING (1<<1)
+#define DF_USE_TAG (1<<2)
// /atom/movable movement_type
#define UNSTOPPABLE (1<<0) //Can not be stopped from moving from Cross(), CanPass(), or Uncross() failing. Still bumps everything it passes through, though.
diff --git a/code/__defines/map.dm b/code/__defines/map.dm
index 13b1bfd795..7d0f3706af 100644
--- a/code/__defines/map.dm
+++ b/code/__defines/map.dm
@@ -6,6 +6,7 @@
#define MAP_LEVEL_SEALED 0x010 // Z-levels that don't allow random transit at edge
#define MAP_LEVEL_EMPTY 0x020 // Empty Z-levels that may be used for various things (currently used by bluespace jump)
#define MAP_LEVEL_CONSOLES 0x040 // Z-levels available to various consoles, such as the crew monitor (when that gets coded in). Defaults to station_levels if unset.
+#define MAP_LEVEL_XENOARCH_EXEMPT 0x080 // Z-levels exempt from xenoarch digsite generation.
// Misc map defines.
#define SUBMAP_MAP_EDGE_PAD 15 // Automatically created submaps are forbidden from being this close to the main map's edge.
\ No newline at end of file
diff --git a/code/__defines/misc.dm b/code/__defines/misc.dm
index 4282731dd8..bd1acdb87e 100644
--- a/code/__defines/misc.dm
+++ b/code/__defines/misc.dm
@@ -146,7 +146,10 @@
#define DEFAULT_TABLE_MATERIAL "plastic"
#define DEFAULT_WALL_MATERIAL "steel"
+#define MAT_IRON "iron"
+#define MAT_MARBLE "marble"
#define MAT_STEEL "steel"
+#define MAT_PLASTIC "plastic"
#define MAT_GLASS "glass"
#define MAT_SILVER "silver"
#define MAT_GOLD "gold"
@@ -165,6 +168,13 @@
#define MAT_DURASTEEL "durasteel"
#define MAT_DURASTEELHULL "durasteel hull"
#define MAT_TITANIUMHULL "titanium hull"
+#define MAT_VERDANTIUM "verdantium"
+#define MAT_MORPHIUM "morphium"
+#define MAT_MORPHIUMHULL "morphium hull"
+#define MAT_VALHOLLIDE "valhollide"
+#define MAT_LEAD "lead"
+#define MAT_SUPERMATTER "supermatter"
+#define MAT_METALHYDROGEN "mhydrogen"
#define SHARD_SHARD "shard"
#define SHARD_SHRAPNEL "shrapnel"
diff --git a/code/__defines/mobs.dm b/code/__defines/mobs.dm
index 8d85fada63..51039af253 100644
--- a/code/__defines/mobs.dm
+++ b/code/__defines/mobs.dm
@@ -87,6 +87,7 @@
#define ROBOT_NOTIFICATION_NEW_NAME 2
#define ROBOT_NOTIFICATION_NEW_MODULE 3
#define ROBOT_NOTIFICATION_MODULE_RESET 4
+#define ROBOT_NOTIFICATION_AI_SHELL 5
// Appearance change flags
#define APPEARANCE_UPDATE_DNA 0x1
@@ -259,6 +260,7 @@
#define BORG_BRAINTYPE_CYBORG "Cyborg"
#define BORG_BRAINTYPE_POSI "Robot"
#define BORG_BRAINTYPE_DRONE "Drone"
+#define BORG_BRAINTYPE_AI_SHELL "AI Shell"
// 'Regular' species.
#define SPECIES_HUMAN "Human"
diff --git a/code/__defines/research.dm b/code/__defines/research.dm
index 9c3bec3066..b55f6601f8 100644
--- a/code/__defines/research.dm
+++ b/code/__defines/research.dm
@@ -11,6 +11,7 @@
#define TECH_DATA "programming"
#define TECH_ILLEGAL "syndicate"
#define TECH_ARCANE "arcane"
+#define TECH_PRECURSOR "precursor"
#define IMPRINTER 0x0001 //For circuits. Uses glass/chemicals.
#define PROTOLATHE 0x0002 //New stuff. Uses glass/metal/chemicals
diff --git a/code/__defines/xenoarcheaology.dm b/code/__defines/xenoarcheaology.dm
index b8b781795f..45e40cfbde 100644
--- a/code/__defines/xenoarcheaology.dm
+++ b/code/__defines/xenoarcheaology.dm
@@ -34,7 +34,9 @@
#define ARCHAEO_REMAINS_XENO 34
#define ARCHAEO_GASMASK 35
#define ARCHAEO_ALIEN_ITEM 36
-#define MAX_ARCHAEO 36
+#define ARCHAEO_ALIEN_BOAT 37
+#define ARCHAEO_IMPERION_CIRCUIT 38
+#define MAX_ARCHAEO 38
#define DIGSITE_GARDEN 1
#define DIGSITE_ANIMAL 2
diff --git a/code/_helpers/unsorted.dm b/code/_helpers/unsorted.dm
index 7922e95634..3fd47aeaf7 100644
--- a/code/_helpers/unsorted.dm
+++ b/code/_helpers/unsorted.dm
@@ -828,15 +828,25 @@ proc/GaussRandRound(var/sigma,var/roundto)
SX.air.copy_from(ST.zone.air)
ST.zone.remove(ST)
+ var/z_level_change = FALSE
+ if(T.z != X.z)
+ z_level_change = TRUE
+
//Move the objects. Not forceMove because the object isn't "moving" really, it's supposed to be on the "same" turf.
for(var/obj/O in T)
O.loc = X
O.update_light()
+ if(z_level_change) // The objects still need to know if their z-level changed.
+ O.onTransitZ(T.z, X.z)
//Move the mobs unless it's an AI eye or other eye type.
for(var/mob/M in T)
if(istype(M, /mob/observer/eye)) continue // If we need to check for more mobs, I'll add a variable
M.loc = X
+
+ if(z_level_change) // Same goes for mobs.
+ M.onTransitZ(T.z, X.z)
+
if(istype(M, /mob/living))
var/mob/living/LM = M
LM.check_shadow() // Need to check their Z-shadow, which is normally done in forceMove().
@@ -1206,7 +1216,7 @@ var/list/WALLITEMS = list(
/obj/machinery/newscaster, /obj/machinery/firealarm, /obj/structure/noticeboard, /obj/machinery/button/remote,
/obj/machinery/computer/security/telescreen, /obj/machinery/embedded_controller/radio,
/obj/item/weapon/storage/secure/safe, /obj/machinery/door_timer, /obj/machinery/flasher, /obj/machinery/keycard_auth,
- /obj/structure/mirror, /obj/structure/closet/fireaxecabinet, /obj/machinery/computer/security/telescreen/entertainment
+ /obj/structure/mirror, /obj/structure/fireaxecabinet, /obj/machinery/computer/security/telescreen/entertainment
)
/proc/gotwallitem(loc, dir)
for(var/obj/O in loc)
@@ -1544,3 +1554,24 @@ var/mob/dview/dview_mob = new
if(istype(D))
return !QDELETED(D)
return FALSE
+
+//gives us the stack trace from CRASH() without ending the current proc.
+/proc/stack_trace(msg)
+ CRASH(msg)
+
+/datum/proc/stack_trace(msg)
+ CRASH(msg)
+
+// \ref behaviour got changed in 512 so this is necesary to replicate old behaviour.
+// If it ever becomes necesary to get a more performant REF(), this lies here in wait
+// #define REF(thing) (thing && istype(thing, /datum) && (thing:datum_flags & DF_USE_TAG) && thing:tag ? "[thing:tag]" : "\ref[thing]")
+/proc/REF(input)
+ if(istype(input, /datum))
+ var/datum/thing = input
+ if(thing.datum_flags & DF_USE_TAG)
+ if(!thing.tag)
+ thing.datum_flags &= ~DF_USE_TAG
+ stack_trace("A ref was requested of an object with DF_USE_TAG set but no tag: [thing]")
+ else
+ return "\[[url_encode(thing.tag)]\]"
+ return "\ref[input]"
diff --git a/code/controllers/Processes/supply.dm b/code/controllers/Processes/supply.dm
index aec0d624c7..f49730f25d 100644
--- a/code/controllers/Processes/supply.dm
+++ b/code/controllers/Processes/supply.dm
@@ -100,6 +100,7 @@ var/datum/controller/supply/supply_controller = new()
EC.name = "\proper[MA.name]"
EC.value = 0
EC.contents = list()
+ var/base_value = 0
// Must be in a crate!
if(istype(MA,/obj/structure/closet/crate))
@@ -107,6 +108,8 @@ var/datum/controller/supply/supply_controller = new()
callHook("sell_crate", list(CR, area_shuttle))
points += CR.points_per_crate
+ if(CR.points_per_crate)
+ base_value = CR.points_per_crate
var/find_slip = 1
for(var/atom/A in CR)
@@ -151,6 +154,7 @@ var/datum/controller/supply/supply_controller = new()
exported_crates += EC
points += EC.value
+ EC.value += base_value
// Duplicate the receipt for the admin-side log
var/datum/exported_crate/adm = new()
diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm
index 0fd222044c..89fadd5789 100644
--- a/code/controllers/configuration.dm
+++ b/code/controllers/configuration.dm
@@ -57,10 +57,13 @@ var/list/gamemode_cache = list()
var/list/modes = list() // allowed modes
var/list/votable_modes = list() // votable modes
var/list/probabilities = list() // relative probability of each mode
+ var/list/player_requirements = list() // Overrides for how many players readied up a gamemode needs to start.
+ var/list/player_requirements_secret = list() // Same as above, but for the secret gamemode.
var/humans_need_surnames = 0
var/allow_random_events = 0 // enables random events mid-round when set to 1
var/allow_ai = 1 // allow ai job
- var/allow_ai_drones = 0 // allow ai controlled drones
+ var/allow_ai_shells = FALSE // allow AIs to enter and leave special borg shells at will, and for those shells to be buildable.
+ var/give_free_ai_shell = FALSE // allows a specific spawner object to instantiate a premade AI Shell
var/hostedby = null
var/respawn = 1
var/guest_jobban = 1
@@ -236,6 +239,7 @@ var/list/gamemode_cache = list()
var/radiation_lower_limit = 0.35 //If the radiation level for a turf would be below this, ignore it.
var/random_submap_orientation = FALSE // If true, submaps loaded automatically can be rotated.
+ var/autostart_solars = FALSE // If true, specifically mapped in solar control computers will set themselves up when the round starts.
/datum/configuration/New()
var/list/L = typesof(/datum/game_mode) - /datum/game_mode
@@ -247,9 +251,11 @@ var/list/gamemode_cache = list()
gamemode_cache[M.config_tag] = M // So we don't instantiate them repeatedly.
if(!(M.config_tag in modes)) // ensure each mode is added only once
log_misc("Adding game mode [M.name] ([M.config_tag]) to configuration.")
- src.modes += M.config_tag
- src.mode_names[M.config_tag] = M.name
- src.probabilities[M.config_tag] = M.probability
+ modes += M.config_tag
+ mode_names[M.config_tag] = M.name
+ probabilities[M.config_tag] = M.probability
+ player_requirements[M.config_tag] = M.required_players
+ player_requirements_secret[M.config_tag] = M.required_players_secret
if (M.votable)
src.votable_modes += M.config_tag
src.votable_modes += "secret"
@@ -410,8 +416,11 @@ var/list/gamemode_cache = list()
if ("allow_ai")
config.allow_ai = 1
- if ("allow_ai_drones")
- config.allow_ai_drones = 1
+ if ("allow_ai_shells")
+ config.allow_ai_shells = TRUE
+
+ if("give_free_ai_shell")
+ config.give_free_ai_shell = TRUE
// if ("authentication")
// config.enable_authentication = 1
@@ -518,6 +527,25 @@ var/list/gamemode_cache = list()
else
log_misc("Incorrect probability configuration definition: [prob_name] [prob_value].")
+ if ("required_players", "required_players_secret")
+ var/req_pos = findtext(value, " ")
+ var/req_name = null
+ var/req_value = null
+ var/is_secret_override = findtext(name, "required_players_secret") // Being extra sure we're not picking up an override for Secret by accident.
+
+ if(req_pos)
+ req_name = lowertext(copytext(value, 1, req_pos))
+ req_value = copytext(value, req_pos + 1)
+ if(req_name in config.modes)
+ if(is_secret_override)
+ config.player_requirements_secret[req_name] = text2num(req_value)
+ else
+ config.player_requirements[req_name] = text2num(req_value)
+ else
+ log_misc("Unknown game mode player requirement configuration definition: [req_name].")
+ else
+ log_misc("Incorrect player requirement configuration definition: [req_name] [req_value].")
+
if("allow_random_events")
config.allow_random_events = 1
@@ -760,6 +788,9 @@ var/list/gamemode_cache = list()
if("random_submap_orientation")
config.random_submap_orientation = 1
+ if("autostart_solars")
+ config.autostart_solars = TRUE
+
else
log_misc("Unknown setting in configuration: '[name]'")
diff --git a/code/controllers/emergency_shuttle_controller.dm b/code/controllers/emergency_shuttle_controller.dm
index bcc7187a3c..5dbfeb427a 100644
--- a/code/controllers/emergency_shuttle_controller.dm
+++ b/code/controllers/emergency_shuttle_controller.dm
@@ -195,6 +195,8 @@ var/global/datum/emergency_shuttle_controller/emergency_shuttle
//returns 1 if the shuttle is not idle at centcom
/datum/emergency_shuttle_controller/proc/online()
+ if(!shuttle)
+ return FALSE
if (!shuttle.location) //not at centcom
return 1
if (wait_for_launch || shuttle.moving_status != SHUTTLE_IDLE)
diff --git a/code/controllers/subsystems/vote.dm b/code/controllers/subsystems/vote.dm
index ab65c7414b..e7c3d5aaf9 100644
--- a/code/controllers/subsystems/vote.dm
+++ b/code/controllers/subsystems/vote.dm
@@ -31,6 +31,24 @@ SUBSYSTEM_DEF(vote)
reset()
/datum/controller/subsystem/vote/proc/autotransfer()
+ // Before doing the vote, see if anyone is playing.
+ // If not, just do the transfer.
+ var/players_are_in_round = FALSE
+ for(var/a in player_list) // Mobs with clients attached.
+ var/mob/living/L = a
+ if(!istype(L)) // Exclude ghosts and other weird things.
+ continue
+ if(L.stat == DEAD) // Dead mobs aren't playing.
+ continue
+ // Everything else is, however.
+ players_are_in_round = TRUE
+ break
+
+ if(!players_are_in_round)
+ log_debug("The crew transfer shuttle was automatically called at vote time due to no players being present.")
+ init_shift_change(null, 1)
+ return
+
initiate_vote(VOTE_CREW_TRANSFER, "the server", 1)
log_debug("The server has called a crew transfer vote.")
diff --git a/code/controllers/subsystems/xenoarch.dm b/code/controllers/subsystems/xenoarch.dm
index 7f3e292c1a..d863ad8269 100644
--- a/code/controllers/subsystems/xenoarch.dm
+++ b/code/controllers/subsystems/xenoarch.dm
@@ -31,7 +31,7 @@ SUBSYSTEM_DEF(xenoarch)
/datum/controller/subsystem/xenoarch/proc/SetupXenoarch()
for(var/turf/simulated/mineral/M in turfs)
- if(!M.density)
+ if(!M.density || M.z in using_map.xenoarch_exempt_levels)
continue
if(isnull(M.geologic_data))
diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm
index 45d50ced1d..1ece4c93a6 100644
--- a/code/datums/datacore.dm
+++ b/code/datums/datacore.dm
@@ -83,8 +83,8 @@
bot[ai.name] = "Artificial Intelligence"
for(var/mob/living/silicon/robot/robot in mob_list)
- // No combat/syndicate cyborgs, no drones.
- if(!robot.scrambledcodes && !(robot.module && robot.module.hide_on_manifest))
+ // No combat/syndicate cyborgs, no drones, and no AI shells.
+ if(!robot.scrambledcodes && !robot.shell && !(robot.module && robot.module.hide_on_manifest))
bot[robot.name] = "[robot.modtype] [robot.braintype]"
diff --git a/code/datums/observation/destroyed.dm b/code/datums/observation/destroyed.dm
index ff8778645c..650909f86d 100644
--- a/code/datums/observation/destroyed.dm
+++ b/code/datums/observation/destroyed.dm
@@ -10,5 +10,6 @@
name = "Destroyed"
/datum/Destroy()
- GLOB.destroyed_event.raise_event(src)
+ if(GLOB.destroyed_event)
+ GLOB.destroyed_event.raise_event(src)
. = ..()
diff --git a/code/datums/soul_link.dm b/code/datums/soul_link.dm
new file mode 100644
index 0000000000..7bc652a863
--- /dev/null
+++ b/code/datums/soul_link.dm
@@ -0,0 +1,156 @@
+// A datum used to link multiple mobs together in some form.
+// The code is from TG, however tweaked to be within the preferred code style.
+
+/mob/living
+ var/list/owned_soul_links // Soul links we are the owner of.
+ var/list/shared_soul_links // Soul links we are a/the sharer of.
+
+/mob/living/Destroy()
+ for(var/s in owned_soul_links)
+ var/datum/soul_link/S = s
+ S.owner_died(FALSE)
+ qdel(s) // If the owner is destroy()'d, the soullink is destroy()'d.
+ owned_soul_links = null
+ for(var/s in shared_soul_links)
+ var/datum/soul_link/S = s
+ S.sharer_died(FALSE)
+ S.remove_soul_sharer(src) // If a sharer is destroy()'d, they are simply removed.
+ shared_soul_links = null
+ return ..()
+
+// Keeps track of a Mob->Mob (potentially Player->Player) connection.
+// Can be used to trigger actions on one party when events happen to another.
+// Eg: shared deaths.
+// Can be used to form a linked list of mob-hopping.
+// Does NOT transfer with minds.
+/datum/soul_link
+ var/mob/living/soul_owner
+ var/mob/living/soul_sharer
+ var/id // Optional ID, for tagging and finding specific instances.
+
+/datum/soul_link/Destroy()
+ if(soul_owner)
+ LAZYREMOVE(soul_owner.owned_soul_links, src)
+ soul_owner = null
+ if(soul_sharer)
+ LAZYREMOVE(soul_sharer.shared_soul_links, src)
+ soul_sharer = null
+ return ..()
+
+/datum/soul_link/proc/remove_soul_sharer(mob/living/sharer)
+ if(soul_sharer == sharer)
+ soul_sharer = null
+ LAZYREMOVE(sharer.shared_soul_links, src)
+
+// Used to assign variables, called primarily by soullink()
+// Override this to create more unique soullinks (Eg: 1->Many relationships)
+// Return TRUE/FALSE to return the soullink/null in soullink()
+/datum/soul_link/proc/parse_args(mob/living/owner, mob/living/sharer)
+ if(!owner || !sharer)
+ return FALSE
+ soul_owner = owner
+ soul_sharer = sharer
+ LAZYADD(owner.owned_soul_links, src)
+ LAZYADD(sharer.shared_soul_links, src)
+ return TRUE
+
+// Runs after /living death()
+// Override this for content.
+/datum/soul_link/proc/owner_died(gibbed, mob/living/owner)
+
+// Runs after /living death()
+// Override this for content.
+/datum/soul_link/proc/sharer_died(gibbed, mob/living/owner)
+
+// Quick-use helper.
+/proc/soul_link(typepath, ...)
+ var/datum/soul_link/S = new typepath()
+ if(S.parse_args(arglist(args.Copy(2, 0))))
+ return S
+
+
+/////////////////
+// MULTISHARER //
+/////////////////
+// Abstract soullink for use with 1 Owner -> Many Sharer setups
+/datum/soul_link/multi_sharer
+ var/list/soul_sharers
+
+/datum/soul_link/multi_sharer/parse_args(mob/living/owner, list/sharers)
+ if(!owner || !LAZYLEN(sharers))
+ return FALSE
+ soul_owner = owner
+ soul_sharers = sharers
+ LAZYADD(owner.owned_soul_links, src)
+ for(var/l in sharers)
+ var/mob/living/L = l
+ LAZYADD(L.shared_soul_links, src)
+ return TRUE
+
+/datum/soul_link/multi_sharer/remove_soul_sharer(mob/living/sharer)
+ LAZYREMOVE(soul_sharers, sharer)
+
+
+/////////////////
+// SHARED FATE //
+/////////////////
+// When the soulowner dies, the soulsharer dies, and vice versa
+// This is intended for two players(or AI) and two mobs
+
+/datum/soul_link/shared_fate/owner_died(gibbed, mob/living/owner)
+ if(soul_sharer)
+ soul_sharer.death(gibbed)
+
+/datum/soul_link/shared_fate/sharer_died(gibbed, mob/living/sharer)
+ if(soul_owner)
+ soul_owner.death(gibbed)
+
+//////////////
+// ONE WAY //
+//////////////
+// When the soul owner dies, the soul sharer dies, but NOT vice versa.
+// This is intended for two players (or AI) and two mobs.
+
+/datum/soul_link/one_way/owner_died(gibbed, mob/living/owner)
+ if(soul_sharer)
+ soul_sharer.dust(FALSE)
+
+/////////////////
+// SHARED BODY //
+/////////////////
+// When the soulsharer dies, they're placed in the soulowner, who remains alive
+// If the soulowner dies, the soulsharer is killed and placed into the soulowner (who is still dying)
+// This one is intended for one player moving between many mobs
+
+/datum/soul_link/shared_body/owner_died(gibbed, mob/living/owner)
+ if(soul_owner && soul_sharer)
+ if(soul_sharer.mind)
+ soul_sharer.mind.transfer_to(soul_owner)
+ soul_sharer.death(gibbed)
+
+/datum/soul_link/shared_body/sharer_died(gibbed, mob/living/sharer)
+ if(soul_owner && soul_sharer && soul_sharer.mind)
+ soul_sharer.mind.transfer_to(soul_owner)
+
+
+
+//////////////////////
+// REPLACEMENT POOL //
+//////////////////////
+// When the owner dies, one of the sharers is placed in the owner's body, fully healed
+// Sort of a "winner-stays-on" soullink
+// Gibbing ends it immediately
+
+/datum/soul_link/multi_sharer/replacement_pool/owner_died(gibbed, mob/living/owner)
+ if(LAZYLEN(soul_sharers) && !gibbed) //let's not put them in some gibs
+ var/list/souls = shuffle(soul_sharers.Copy())
+ for(var/l in souls)
+ var/mob/living/L = l
+ if(L.stat != DEAD && L.mind)
+ L.mind.transfer_to(soul_owner)
+ soul_owner.revive(TRUE, TRUE)
+ L.death(FALSE)
+
+// Lose your claim to the throne!
+/datum/soul_link/multi_sharer/replacement_pool/sharer_died(gibbed, mob/living/sharer)
+ remove_soul_sharer(sharer)
diff --git a/code/datums/wires/seedstorage.dm b/code/datums/wires/seedstorage.dm
index e21bbb9faf..8268d936b0 100644
--- a/code/datums/wires/seedstorage.dm
+++ b/code/datums/wires/seedstorage.dm
@@ -10,10 +10,6 @@
/datum/wires/seedstorage/CanUse(var/mob/living/L)
var/obj/machinery/seed_storage/V = holder
- if(!istype(L, /mob/living/silicon))
- if(V.seconds_electrified)
- if(V.shock(L, 100))
- return 0
if(V.panel_open)
return 1
return 0
diff --git a/code/datums/wires/smartfridge.dm b/code/datums/wires/smartfridge.dm
index b611497e79..15f10cd800 100644
--- a/code/datums/wires/smartfridge.dm
+++ b/code/datums/wires/smartfridge.dm
@@ -12,10 +12,6 @@ var/const/SMARTFRIDGE_WIRE_IDSCAN = 4
/datum/wires/smartfridge/CanUse(var/mob/living/L)
var/obj/machinery/smartfridge/S = holder
- if(!istype(L, /mob/living/silicon))
- if(S.seconds_electrified)
- if(S.shock(L, 100))
- return 0
if(S.panel_open)
return 1
return 0
diff --git a/code/datums/wires/vending.dm b/code/datums/wires/vending.dm
index 6715669fc1..bad3a0078b 100644
--- a/code/datums/wires/vending.dm
+++ b/code/datums/wires/vending.dm
@@ -9,10 +9,6 @@ var/const/VENDING_WIRE_IDSCAN = 8
/datum/wires/vending/CanUse(var/mob/living/L)
var/obj/machinery/vending/V = holder
- if(!istype(L, /mob/living/silicon))
- if(V.seconds_electrified)
- if(V.shock(L, 100))
- return 0
if(V.panel_open)
return 1
return 0
@@ -43,7 +39,7 @@ var/const/VENDING_WIRE_IDSCAN = 8
if(VENDING_WIRE_THROW)
V.shoot_inventory = !mended
if(VENDING_WIRE_CONTRABAND)
- V.categories &= ~CAT_HIDDEN
+ V.categories &= ~CAT_HIDDEN
if(VENDING_WIRE_ELECTRIFY)
if(mended)
V.seconds_electrified = 0
diff --git a/code/defines/obj.dm b/code/defines/obj.dm
index b7f4eb2243..6c3c26ef3a 100644
--- a/code/defines/obj.dm
+++ b/code/defines/obj.dm
@@ -143,8 +143,8 @@ var/global/list/PDA_Manifest = list()
bot[++bot.len] = list("name" = ai.real_name, "rank" = "Artificial Intelligence", "active" = "Active")
for(var/mob/living/silicon/robot/robot in mob_list)
- // No combat/syndicate cyborgs, no drones.
- if(robot.scrambledcodes || (robot.module && robot.module.hide_on_manifest))
+ // No combat/syndicate cyborgs, no drones, and no AI shells.
+ if(robot.scrambledcodes || robot.shell || (robot.module && robot.module.hide_on_manifest))
continue
bot[++bot.len] = list("name" = robot.real_name, "rank" = "[robot.modtype] [robot.braintype]", "active" = "Active")
diff --git a/code/defines/obj/weapon.dm b/code/defines/obj/weapon.dm
index 493d5ccd38..5bd34e704b 100644
--- a/code/defines/obj/weapon.dm
+++ b/code/defines/obj/weapon.dm
@@ -395,6 +395,21 @@
display_contents_with_number = 1
max_w_class = ITEMSIZE_NORMAL
max_storage_space = 100
+
+/obj/item/weapon/storage/part_replacer/adv
+ name = "advanced rapid part exchange device"
+ desc = "Special mechanical module made to store, sort, and apply standard machine parts. This one has a greatly upgraded storage capacity"
+ icon_state = "RPED"
+ w_class = ITEMSIZE_HUGE
+ can_hold = list(/obj/item/weapon/stock_parts)
+ storage_slots = 200
+ use_to_pickup = 1
+ allow_quick_gather = 1
+ allow_quick_empty = 1
+ collection_mode = 1
+ display_contents_with_number = 1
+ max_w_class = ITEMSIZE_NORMAL
+ max_storage_space = 400
/obj/item/weapon/stock_parts
name = "stock part"
@@ -555,6 +570,91 @@
rating = 3
matter = list(DEFAULT_WALL_MATERIAL = 80)
+// Rating 4 - Anomaly
+
+/obj/item/weapon/stock_parts/capacitor/hyper
+ name = "hyper capacitor"
+ desc = "A hyper-capacity capacitor used in the construction of a variety of devices."
+ icon_state = "capacitor_hyper"
+ origin_tech = list(TECH_POWER = 6, TECH_MATERIAL = 5, TECH_BLUESPACE = 1, TECH_ARCANE = 1)
+ rating = 4
+ matter = list(DEFAULT_WALL_MATERIAL = 80, MAT_GLASS = 40)
+
+/obj/item/weapon/stock_parts/scanning_module/hyper
+ name = "quantum scanning module"
+ desc = "A compact, near-perfect resolution quantum scanning module used in the construction of certain devices."
+ icon_state = "scan_module_hyper"
+ origin_tech = list(TECH_MAGNET = 6, TECH_BLUESPACE = 1, TECH_ARCANE = 1)
+ rating = 4
+ matter = list(DEFAULT_WALL_MATERIAL = 100,"glass" = 40)
+
+/obj/item/weapon/stock_parts/manipulator/hyper
+ name = "planck-manipulator"
+ desc = "A miniscule manipulator used in the construction of certain devices."
+ icon_state = "hyper_mani"
+ origin_tech = list(TECH_MATERIAL = 6, TECH_DATA = 3, TECH_ARCANE = 1)
+ rating = 4
+ matter = list(DEFAULT_WALL_MATERIAL = 30)
+
+/obj/item/weapon/stock_parts/micro_laser/hyper
+ name = "hyper-power micro-laser"
+ icon_state = "hyper_micro_laser"
+ desc = "A tiny laser used in certain devices."
+ origin_tech = list(TECH_MAGNET = 6, TECH_ARCANE = 1)
+ rating = 4
+ matter = list(DEFAULT_WALL_MATERIAL = 30, MAT_GLASS = 40)
+
+/obj/item/weapon/stock_parts/matter_bin/hyper
+ name = "hyper matter bin"
+ desc = "A container for holding compressed matter awaiting re-construction."
+ icon_state = "hyper_matter_bin"
+ origin_tech = list(TECH_MATERIAL = 6, TECH_ARCANE = 1)
+ rating = 4
+ matter = list(DEFAULT_WALL_MATERIAL = 100)
+
+// Rating 5 - Precursor
+
+/obj/item/weapon/stock_parts/capacitor/omni
+ name = "omni-capacitor"
+ desc = "A capacitor of immense capacity used in the construction of a variety of devices."
+ icon_state = "capacitor_omni"
+ origin_tech = list(TECH_POWER = 7, TECH_MATERIAL = 6, TECH_BLUESPACE = 3, TECH_PRECURSOR = 1)
+ rating = 5
+ matter = list(DEFAULT_WALL_MATERIAL = 80, MAT_GLASS = 40)
+
+/obj/item/weapon/stock_parts/scanning_module/omni
+ name = "omni-scanning module"
+ desc = "A compact, perfect resolution temporospatial scanning module used in the construction of certain devices."
+ icon_state = "scan_module_omni"
+ origin_tech = list(TECH_MAGNET = 7, TECH_BLUESPACE = 3, TECH_PRECURSOR = 1)
+ rating = 5
+ matter = list(DEFAULT_WALL_MATERIAL = 100,"glass" = 40)
+
+/obj/item/weapon/stock_parts/manipulator/omni
+ name = "omni-manipulator"
+ desc = "A strange, infinitesimal manipulator used in the construction of certain devices."
+ icon_state = "omni_mani"
+ origin_tech = list(TECH_MATERIAL = 7, TECH_DATA = 4, TECH_PRECURSOR = 1)
+ rating = 5
+ matter = list(DEFAULT_WALL_MATERIAL = 30)
+
+/obj/item/weapon/stock_parts/micro_laser/omni
+ name = "omni-power micro-laser"
+ icon_state = "omni_micro_laser"
+ desc = "A strange laser used in certain devices."
+ origin_tech = list(TECH_MAGNET = 7, TECH_PRECURSOR = 1)
+ rating = 5
+ matter = list(DEFAULT_WALL_MATERIAL = 30, MAT_GLASS = 40)
+
+/obj/item/weapon/stock_parts/matter_bin/omni
+ name = "omni-matter bin"
+ desc = "A strange container for holding compressed matter awaiting re-construction."
+ icon_state = "omni_matter_bin"
+ origin_tech = list(TECH_MATERIAL = 7, TECH_PRECURSOR = 1)
+ rating = 5
+ matter = list(DEFAULT_WALL_MATERIAL = 100)
+
+
// Subspace stock parts
/obj/item/weapon/stock_parts/subspace/ansible
diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm
index c7f64f225c..81c74f3ba3 100644
--- a/code/game/area/Space Station 13 areas.dm
+++ b/code/game/area/Space Station 13 areas.dm
@@ -978,6 +978,12 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
icon_state = "entry_D2"
base_turf = /turf/space
+/area/hallway/secondary/entry/D2/arrivals
+ name = "\improper Shuttle Dock Hallway - Dock Two"
+ icon_state = "entry_D2"
+ base_turf = /turf/space
+ requires_power = 0
+
/area/hallway/secondary/entry/D3
name = "\improper Shuttle Dock Hallway - Dock Three"
icon_state = "entry_D3"
diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
index 29139b3a3a..140c4b133c 100644
--- a/code/game/gamemodes/game_mode.dm
+++ b/code/game/gamemodes/game_mode.dm
@@ -146,10 +146,10 @@ var/global/list/additional_antag_types = list()
playerC++
if(master_mode=="secret")
- if(playerC < required_players_secret)
+ if(playerC < config.player_requirements_secret)
return 0
else
- if(playerC < required_players)
+ if(playerC < config.player_requirements)
return 0
if(!(antag_templates && antag_templates.len))
diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm
index 067a3dcc75..ed2e2915d8 100644
--- a/code/game/machinery/Sleeper.dm
+++ b/code/game/machinery/Sleeper.dm
@@ -15,6 +15,11 @@
..()
findsleeper()
+/obj/machinery/sleep_console/Destroy()
+ if(sleeper)
+ sleeper.console = null
+ return ..()
+
/obj/machinery/sleep_console/proc/findsleeper()
spawn(5)
var/obj/machinery/sleeper/sleepernew = null
@@ -34,16 +39,18 @@
if(..())
return 1
+ if(!sleeper)
+ findsleeper()
+ if(!sleeper)
+ to_chat(user, "Sleeper not found!")
+ return
+
if(sleeper.panel_open)
to_chat(user, "Close the maintenance panel first.")
return
- if(!sleeper)
- findsleeper()
if(sleeper)
return ui_interact(user)
- else
- to_chat(user, "Sleeper not found!")
/obj/machinery/sleep_console/attackby(var/obj/item/I, var/mob/user)
if(computer_deconstruction_screwdriver(user, I))
@@ -155,7 +162,8 @@
anchored = 1
circuit = /obj/item/weapon/circuitboard/sleeper
var/mob/living/carbon/human/occupant = null
- var/list/available_chemicals = list("inaprovaline" = "Inaprovaline", "paracetamol" = "Paracetamol", "anti_toxin" = "Dylovene", "dexalin" = "Dexalin")
+ var/list/available_chemicals = list()
+ var/list/base_chemicals = list("inaprovaline" = "Inaprovaline", "paracetamol" = "Paracetamol", "anti_toxin" = "Dylovene", "dexalin" = "Dexalin")
var/obj/item/weapon/reagent_containers/glass/beaker = null
var/filtering = 0
var/obj/machinery/sleep_console/console
@@ -170,6 +178,7 @@
..()
beaker = new /obj/item/weapon/reagent_containers/glass/beaker/large(src)
component_parts = list()
+ component_parts += new /obj/item/weapon/stock_parts/manipulator(src)
component_parts += new /obj/item/weapon/stock_parts/scanning_module(src)
component_parts += new /obj/item/weapon/reagent_containers/glass/beaker(src)
component_parts += new /obj/item/weapon/reagent_containers/glass/beaker(src)
@@ -181,6 +190,55 @@
RefreshParts()
+/obj/machinery/sleeper/Destroy()
+ if(console)
+ console.sleeper = null
+ return ..()
+
+/obj/machinery/sleeper/RefreshParts(var/limited = 0)
+ var/man_rating = 0
+ var/cap_rating = 0
+
+ available_chemicals.Cut()
+ available_chemicals = base_chemicals.Copy()
+ idle_power_usage = initial(idle_power_usage)
+ active_power_usage = initial(active_power_usage)
+
+ for(var/obj/item/weapon/stock_parts/P in component_parts)
+ if(istype(P, /obj/item/weapon/stock_parts/capacitor))
+ cap_rating += P.rating
+
+ cap_rating = max(1, round(cap_rating / 2))
+
+ idle_power_usage /= cap_rating
+ active_power_usage /= cap_rating
+
+ if(!limited)
+ for(var/obj/item/weapon/stock_parts/P in component_parts)
+ if(istype(P, /obj/item/weapon/stock_parts/manipulator))
+ man_rating += P.rating - 1
+
+ var/list/new_chemicals = list()
+
+ if(man_rating >= 4) // Alien tech.
+ var/reag_ID = pickweight(list(
+ "healing_nanites" = 10,
+ "shredding_nanites" = 5,
+ "irradiated_nanites" = 5,
+ "neurophage_nanites" = 2)
+ )
+ new_chemicals[reag_ID] = "Nanite"
+ if(man_rating >= 3) // Anomalous tech.
+ new_chemicals["immunosuprizine"] = "Immunosuprizine"
+ if(man_rating >= 2) // Tier 3.
+ new_chemicals["spaceacillin"] = "Spaceacillin"
+ if(man_rating >= 1) // Tier 2.
+ new_chemicals["leporazine"] = "Leporazine"
+
+ if(new_chemicals.len)
+ available_chemicals += new_chemicals
+ return
+
/obj/machinery/sleeper/Initialize()
. = ..()
update_icon()
@@ -215,11 +273,8 @@
var/obj/item/weapon/grab/G = I
if(G.affecting)
go_in(G.affecting, user)
- else if(default_deconstruction_screwdriver(user, I))
return
- else if(default_deconstruction_crowbar(user, I))
- return
- else if(istype(I, /obj/item/weapon/reagent_containers/glass))
+ if(istype(I, /obj/item/weapon/reagent_containers/glass))
if(!beaker)
beaker = I
user.drop_item()
@@ -228,6 +283,13 @@
else
to_chat(user, "\The [src] has a beaker already.")
return
+ if(!occupant)
+ if(default_deconstruction_screwdriver(user, I))
+ return
+ if(default_deconstruction_crowbar(user, I))
+ return
+ if(default_part_replacement(user, I))
+ return
/obj/machinery/sleeper/verb/move_eject()
set name = "Eject occupant"
@@ -351,3 +413,7 @@
desc = "A limited functionality sleeper, all it can do is put patients into stasis. It lacks the medication and configuration of the larger units."
icon_state = "sleeper"
stasis_level = 100 //Just one setting
+
+/obj/machinery/sleeper/survival_pod/Initialize()
+ ..()
+ RefreshParts(1)
diff --git a/code/game/machinery/adv_med.dm b/code/game/machinery/adv_med.dm
index b52de9cc1a..5ab9828417 100644
--- a/code/game/machinery/adv_med.dm
+++ b/code/game/machinery/adv_med.dm
@@ -24,6 +24,11 @@
component_parts += new /obj/item/stack/material/glass/reinforced(src, 2)
RefreshParts()
+/obj/machinery/bodyscanner/Destroy()
+ if(console)
+ console.scanner = null
+ return ..()
+
/obj/machinery/bodyscanner/power_change()
..()
if(!(stat & (BROKEN|NOPOWER)))
@@ -32,11 +37,7 @@
set_light(0)
/obj/machinery/bodyscanner/attackby(var/obj/item/G, user as mob)
- if(default_deconstruction_screwdriver(user, G))
- return
- else if(default_deconstruction_crowbar(user, G))
- return
- else if(istype(G, /obj/item/weapon/grab))
+ if(istype(G, /obj/item/weapon/grab))
var/obj/item/weapon/grab/H = G
if(panel_open)
to_chat(user, "Close the maintenance panel first.")
@@ -61,6 +62,11 @@
icon_state = "body_scanner_1"
add_fingerprint(user)
qdel(G)
+ if(!occupant)
+ if(default_deconstruction_screwdriver(user, G))
+ return
+ if(default_deconstruction_crowbar(user, G))
+ return
/obj/machinery/bodyscanner/MouseDrop_T(mob/living/carbon/O, mob/user as mob)
if(!istype(O))
@@ -176,6 +182,11 @@
..()
findscanner()
+/obj/machinery/body_scanconsole/Destroy()
+ if(scanner)
+ scanner.console = null
+ return ..()
+
/obj/machinery/body_scanconsole/attackby(var/obj/item/I, var/mob/user)
if(computer_deconstruction_screwdriver(user, I))
return
@@ -242,18 +253,18 @@
if(stat & (NOPOWER|BROKEN))
return
+ if(!scanner)
+ findscanner()
+ if(!scanner)
+ to_chat(user, "Scanner not found!")
+ return
+
if (scanner.panel_open)
to_chat(user, "Close the maintenance panel first.")
return
- if(!scanner)
- findscanner()
- if(scanner)
- return ui_interact(user)
- else if(scanner)
+ if(scanner)
return ui_interact(user)
- else
- to_chat(user, "Scanner not found!")
/obj/machinery/body_scanconsole/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
var/data[0]
diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm
index b62ba75409..c4f098bf94 100644
--- a/code/game/machinery/alarm.dm
+++ b/code/game/machinery/alarm.dm
@@ -875,6 +875,11 @@ FIRE ALARM
/obj/machinery/firealarm/attackby(obj/item/W as obj, mob/user as mob)
add_fingerprint(user)
+ if(alarm_deconstruction_screwdriver(user, W))
+ return
+ if(alarm_deconstruction_wirecutters(user, W))
+ return
+
if(panel_open)
if(istype(W, /obj/item/device/multitool))
detecting = !(detecting)
diff --git a/code/game/machinery/autolathe.dm b/code/game/machinery/autolathe.dm
index 02fb2f6b4d..3a1fbf7897 100644
--- a/code/game/machinery/autolathe.dm
+++ b/code/game/machinery/autolathe.dm
@@ -303,7 +303,7 @@
storage_capacity[DEFAULT_WALL_MATERIAL] = mb_rating * 25000
storage_capacity["glass"] = mb_rating * 12500
build_time = 50 / man_rating
- mat_efficiency = 1.1 - man_rating * 0.1// Normally, price is 1.25 the amount of material, so this shouldn't go higher than 0.8. Maximum rating of parts is 3
+ mat_efficiency = 1.1 - man_rating * 0.1// Normally, price is 1.25 the amount of material, so this shouldn't go higher than 0.6. Maximum rating of parts is 5
/obj/machinery/autolathe/dismantle()
for(var/mat in stored_material)
diff --git a/code/game/machinery/bioprinter.dm b/code/game/machinery/bioprinter.dm
index cc982ce207..f60fcabbfa 100644
--- a/code/game/machinery/bioprinter.dm
+++ b/code/game/machinery/bioprinter.dm
@@ -19,6 +19,7 @@
var/base_print_delay = 100 // For Adminbus reasons
var/printing
var/loaded_dna //Blood sample for DNA hashing.
+ var/malfunctioning = FALSE // May cause rejection, or the printing of some alien limb instead!
// These should be subtypes of /obj/item/organ
// Costs roughly 20u Phoron (1 sheet) per internal organ, limbs are 60u for limb and extremity
@@ -76,10 +77,19 @@
/obj/machinery/organ_printer/RefreshParts()
// Print Delay updating
print_delay = base_print_delay
+ var/manip_rating = 0
for(var/obj/item/weapon/stock_parts/manipulator/manip in component_parts)
+ manip_rating += manip.rating
print_delay -= (manip.rating-1)*10
print_delay = max(0,print_delay)
+ manip_rating = round(manip_rating / 2)
+
+ if(manip_rating >= 5)
+ malfunctioning = TRUE
+ else
+ malfunctioning = initial(malfunctioning)
+
. = ..()
/obj/machinery/organ_printer/attack_hand(mob/user)
@@ -182,7 +192,17 @@
O.set_dna(C.dna)
O.species = C.species
- if(istype(O, /obj/item/organ/external))
+ var/malfunctioned = FALSE
+
+ if(malfunctioning && prob(30)) // Alien Tech is a hell of a drug.
+ malfunctioned = TRUE
+ var/possible_species = list(SPECIES_HUMAN, SPECIES_VOX, SPECIES_SKRELL, SPECIES_ZADDAT, SPECIES_UNATHI, SPECIES_GOLEM, SPECIES_SHADOW)
+ var/new_species = pick(possible_species)
+ if(!all_species[new_species])
+ new_species = SPECIES_HUMAN
+ O.species = all_species[new_species]
+
+ if(istype(O, /obj/item/organ/external) && !malfunctioned)
var/obj/item/organ/external/E = O
E.sync_colour_to_human(C)
diff --git a/code/game/machinery/computer/ai_core.dm b/code/game/machinery/computer/ai_core.dm
index bc6da68332..fd4ecb14a6 100644
--- a/code/game/machinery/computer/ai_core.dm
+++ b/code/game/machinery/computer/ai_core.dm
@@ -200,8 +200,8 @@ GLOBAL_LIST_BOILERPLATE(all_deactivated_AI_cores, /obj/structure/AIcore/deactiva
if(!istype(transfer) || locate(/mob/living/silicon/ai) in src)
return
- if(transfer.controlling_drone)
- transfer.controlling_drone.release_ai_control("Unit control lost. Core transfer completed.")
+ if(transfer.deployed_shell)
+ transfer.disconnect_shell("Disconnected from remote shell due to core intelligence transfer.")
transfer.aiRestorePowerRoutine = 0
transfer.control_disabled = 0
transfer.aiRadio.disabledAi = 0
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index dfd093dcf2..972348ac44 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -27,7 +27,7 @@
var/destroy_hits = 10 //How many strong hits it takes to destroy the door
var/min_force = 10 //minimum amount of force needed to damage the door with a melee weapon
var/hitsound = 'sound/weapons/smash.ogg' //sound door makes when hit with a weapon
- var/obj/item/stack/material/steel/repairing
+ var/repairing = 0
var/block_air_zones = 1 //If set, air zones cannot merge across the door even when it is opened.
var/close_door_at = 0 //When to automatically close the door, if possible
@@ -210,13 +210,13 @@
if(istype(I))
if(istype(I, /obj/item/stack/material) && I.get_material_name() == src.get_material_name())
if(stat & BROKEN)
- user << "It looks like \the [src] is pretty busted. It's going to need more than just patching up now."
+ to_chat(user, "It looks like \the [src] is pretty busted. It's going to need more than just patching up now.")
return
if(health >= maxhealth)
- user << "Nothing to fix!"
+ to_chat(user, "Nothing to fix!")
return
if(!density)
- user << "\The [src] must be closed before you can repair it."
+ to_chat(user, "\The [src] must be closed before you can repair it.")
return
//figure out how much metal we need
@@ -224,44 +224,45 @@
amount_needed = (round(amount_needed) == amount_needed)? amount_needed : round(amount_needed) + 1 //Why does BYOND not have a ceiling proc?
var/obj/item/stack/stack = I
- var/transfer
- if (repairing)
- transfer = stack.transfer_to(repairing, amount_needed - repairing.amount)
- if (!transfer)
- user << "You must weld or remove \the [repairing] from \the [src] before you can add anything else."
+ var/amount_given = amount_needed - repairing
+ var/mats_given = stack.get_amount()
+ if(repairing && amount_given <= 0)
+ to_chat(user, "You must weld or remove \the [get_material_name()] from \the [src] before you can add anything else.")
else
- repairing = stack.split(amount_needed)
- if (repairing)
- repairing.loc = src
- transfer = repairing.amount
-
- if (transfer)
- user << "You fit [transfer] [stack.singular_name]\s to damaged and broken parts on \the [src]."
+ if(mats_given >= amount_given)
+ if(stack.use(amount_given))
+ repairing += amount_given
+ else
+ if(stack.use(mats_given))
+ repairing += mats_given
+ amount_given = mats_given
+ if(amount_given)
+ to_chat(user, "You fit [amount_given] [stack.singular_name]\s to damaged and broken parts on \the [src].")
return
if(repairing && istype(I, /obj/item/weapon/weldingtool))
if(!density)
- user << "\The [src] must be closed before you can repair it."
+ to_chat(user, "\The [src] must be closed before you can repair it.")
return
var/obj/item/weapon/weldingtool/welder = I
if(welder.remove_fuel(0,user))
- user << "You start to fix dents and weld \the [repairing] into place."
+ to_chat(user, "You start to fix dents and weld \the [get_material_name()] into place.")
playsound(src, welder.usesound, 50, 1)
- if(do_after(user, (5 * repairing.amount) * welder.toolspeed) && welder && welder.isOn())
- user << "You finish repairing the damage to \the [src]."
- health = between(health, health + repairing.amount*DOOR_REPAIR_AMOUNT, maxhealth)
+ if(do_after(user, (5 * repairing) * welder.toolspeed) && welder && welder.isOn())
+ to_chat(user, "You finish repairing the damage to \the [src].")
+ health = between(health, health + repairing*DOOR_REPAIR_AMOUNT, maxhealth)
update_icon()
- qdel(repairing)
- repairing = null
+ repairing = 0
return
if(repairing && I.is_crowbar())
- user << "You remove \the [repairing]."
+ var/obj/item/stack/material/repairing_sheet = get_material().place_sheet(loc)
+ repairing_sheet.amount += repairing-1
+ repairing = 0
+ to_chat(user, "You remove \the [repairing_sheet].")
playsound(src, I.usesound, 100, 1)
- repairing.loc = user.loc
- repairing = null
return
//psa to whoever coded this, there are plenty of objects that need to call attack() on doors without bludgeoning them.
@@ -321,13 +322,13 @@
/obj/machinery/door/examine(mob/user)
. = ..()
if(src.health <= 0)
- user << "\The [src] is broken!"
+ to_chat(user, "\The [src] is broken!")
if(src.health < src.maxhealth / 4)
- user << "\The [src] looks like it's about to break!"
+ to_chat(user, "\The [src] looks like it's about to break!")
else if(src.health < src.maxhealth / 2)
- user << "\The [src] looks seriously damaged!"
+ to_chat(user, "\The [src] looks seriously damaged!")
else if(src.health < src.maxhealth * 3/4)
- user << "\The [src] shows signs of damage!"
+ to_chat(user, "\The [src] shows signs of damage!")
/obj/machinery/door/proc/set_broken()
diff --git a/code/game/machinery/requests_console.dm b/code/game/machinery/requests_console.dm
index 002ba894ad..de4e8a4685 100644
--- a/code/game/machinery/requests_console.dm
+++ b/code/game/machinery/requests_console.dm
@@ -205,24 +205,23 @@ var/list/obj/machinery/requests_console/allConsoles = list()
if(computer_deconstruction_screwdriver(user, O))
return
if(istype(O, /obj/item/device/multitool))
- if(panel_open)
- var/input = sanitize(input(usr, "What Department ID would you like to give this request console?", "Multitool-Request Console Interface", department))
- if(!input)
- to_chat(usr, "No input found. Please hang up and try your call again.")
- return
- department = input
- announcement.title = "[department] announcement"
- announcement.newscast = 1
-
- name = "[department] Requests Console"
- allConsoles += src
- if(departmentType & RC_ASSIST)
- req_console_assistance |= department
- if(departmentType & RC_SUPPLY)
- req_console_supplies |= department
- if(departmentType & RC_INFO)
- req_console_information |= department
+ var/input = sanitize(input(usr, "What Department ID would you like to give this request console?", "Multitool-Request Console Interface", department))
+ if(!input)
+ to_chat(usr, "No input found. Please hang up and try your call again.")
return
+ department = input
+ announcement.title = "[department] announcement"
+ announcement.newscast = 1
+
+ name = "[department] Requests Console"
+ allConsoles += src
+ if(departmentType & RC_ASSIST)
+ req_console_assistance |= department
+ if(departmentType & RC_SUPPLY)
+ req_console_supplies |= department
+ if(departmentType & RC_INFO)
+ req_console_information |= department
+ return
if(istype(O, /obj/item/weapon/card/id))
if(inoperable(MAINT)) return
diff --git a/code/game/machinery/wall_frames.dm b/code/game/machinery/wall_frames.dm
index 2531c69574..fd1327507e 100644
--- a/code/game/machinery/wall_frames.dm
+++ b/code/game/machinery/wall_frames.dm
@@ -145,4 +145,4 @@
icon = 'icons/obj/closet.dmi'
icon_state = "fireaxe0101"
refund_amt = 4
- build_machine_type = /obj/structure/closet/fireaxecabinet
\ No newline at end of file
+ build_machine_type = /obj/structure/fireaxecabinet
\ No newline at end of file
diff --git a/code/game/mecha/combat/phazon.dm b/code/game/mecha/combat/phazon.dm
index 77ce768fc8..b54368a75e 100644
--- a/code/game/mecha/combat/phazon.dm
+++ b/code/game/mecha/combat/phazon.dm
@@ -27,7 +27,7 @@
max_universal_equip = 3
max_special_equip = 4
-/obj/mecha/combat/phazon/New()
+/obj/mecha/combat/phazon/equipped/New()
..()
var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/tool/rcd
ME.attach(src)
@@ -40,7 +40,7 @@
spawn()
if(can_move)
can_move = 0
- flick("phazon-phase", src)
+ flick("[initial_icon]-phase", src)
src.loc = get_step(src,src.dir)
src.use_power(phasing_energy_drain)
sleep(step_in*3)
@@ -63,6 +63,10 @@
set popup_menu = 0
if(usr!=src.occupant)
return
+
+ query_damtype()
+
+/obj/mecha/combat/phazon/proc/query_damtype()
var/new_damtype = alert(src.occupant,"Melee Damage Type",null,"Brute","Fire","Toxic")
switch(new_damtype)
if("Brute")
@@ -94,4 +98,77 @@
phasing = !phasing
send_byjax(src.occupant,"exosuit.browser","phasing_command","[phasing?"Dis":"En"]able phasing")
src.occupant_message("En":"#f00\">Dis"]abled phasing.")
- return
\ No newline at end of file
+ return
+
+/obj/mecha/combat/phazon/janus
+ name = "Phazon Prototype Janus Class"
+ desc = "An exosuit which a more crude civilization such as yours might describe as WTF?."
+ description_fluff = "An incredibly high-tech exosuit constructed out of salvaged alien and cutting-edge modern technology.\
+ This machine, theoretically, is capable of travelling through time, however due to the strange nature of its miniaturized \
+ supermatter-fueled bluespace drive, it is uncertain how this ability manifests."
+ icon_state = "janus"
+ initial_icon = "janus"
+ step_in = 1
+ dir_in = 1 //Facing North.
+ step_energy_drain = 3
+ health = 350
+ maxhealth = 350
+ deflect_chance = 30
+ damage_absorption = list("brute"=0.6,"fire"=0.7,"bullet"=0.7,"laser"=0.9,"energy"=0.7,"bomb"=0.5)
+ max_temperature = 10000
+ infra_luminosity = 3
+ wreckage = /obj/effect/decal/mecha_wreckage/janus
+ internal_damage_threshold = 25
+ force = 20
+ phasing = FALSE
+ phasing_energy_drain = 300
+
+ max_hull_equip = 2
+ max_weapon_equip = 1
+ max_utility_equip = 2
+ max_universal_equip = 2
+ max_special_equip = 2
+
+/obj/mecha/combat/phazon/janus/take_damage(amount, type="brute")
+ ..()
+ if(phasing)
+ phasing = FALSE
+ radiation_repository.radiate(get_turf(src), 30)
+ log_append_to_last("WARNING: BLUESPACE DRIVE INSTABILITY DETECTED. DISABLING DRIVE.",1)
+ visible_message("The [src.name] appears to flicker, before its silhouette stabilizes!")
+ return
+
+/obj/mecha/combat/phazon/janus/dynbulletdamage(var/obj/item/projectile/Proj)
+ if((Proj.damage && !Proj.nodamage) && !istype(Proj, /obj/item/projectile/beam) && prob(max(1, 33 - round(Proj.damage / 4))))
+ src.occupant_message("The armor absorbs the incoming projectile's force, negating it!")
+ src.visible_message("The [src.name] absorbs the incoming projectile's force, negating it!")
+ src.log_append_to_last("Armor negated.")
+ return
+ else if((Proj.damage && !Proj.nodamage) && istype(Proj, /obj/item/projectile/beam) && prob(max(1, (50 - round((Proj.damage / 2) * damage_absorption["laser"])) * (1 - (Proj.armor_penetration / 100))))) // Base 50% chance to deflect a beam,lowered by half the beam's damage scaled to laser absorption, then multiplied by the remaining percent of non-penetrated armor, with a minimum chance of 1%.
+ src.occupant_message("The armor reflects the incoming beam, negating it!")
+ src.visible_message("The [src.name] reflects the incoming beam, negating it!")
+ src.log_append_to_last("Armor reflected.")
+ return
+
+ ..()
+
+/obj/mecha/combat/phazon/janus/dynattackby(obj/item/weapon/W as obj, mob/user as mob)
+ if(prob(max(1, (50 - round((W.force / 2) * damage_absorption["brute"])) * (1 - (W.armor_penetration / 100)))))
+ src.occupant_message("The armor absorbs the incoming attack's force, negating it!")
+ src.visible_message("The [src.name] absorbs the incoming attack's force, negating it!")
+ src.log_append_to_last("Armor absorbed.")
+ return
+
+ ..()
+
+/obj/mecha/combat/phazon/janus/query_damtype()
+ var/new_damtype = alert(src.occupant,"Gauntlet Phase Emitter Mode",null,"Force","Energy","Stun")
+ switch(new_damtype)
+ if("Force")
+ damtype = "brute"
+ if("Energy")
+ damtype = "fire"
+ if("Stun")
+ damtype = "halloss"
+ src.occupant_message("Melee damage type switched to [new_damtype]")
+ return
diff --git a/code/game/mecha/equipment/mecha_equipment.dm b/code/game/mecha/equipment/mecha_equipment.dm
index 825dfde5f5..7652e98571 100644
--- a/code/game/mecha/equipment/mecha_equipment.dm
+++ b/code/game/mecha/equipment/mecha_equipment.dm
@@ -1,9 +1,9 @@
//DO NOT ADD MECHA PARTS TO THE GAME WITH THE DEFAULT "SPRITE ME" SPRITE!
//I'm annoyed I even have to tell you this! SPRITE FIRST, then commit.
-#define EQUIP_HULL 1
-#define EQUIP_WEAPON 2
-#define EQUIP_UTILITY 3
-#define EQUIP_SPECIAL 4
+#define EQUIP_HULL "hull"
+#define EQUIP_WEAPON "weapon"
+#define EQUIP_UTILITY "utility"
+#define EQUIP_SPECIAL "core"
/obj/item/mecha_parts/mecha_equipment
name = "mecha equipment"
@@ -31,6 +31,9 @@
return 1
return 0
+/obj/item/mecha_parts/mecha_equipment/examine(mob/user)
+ ..()
+ to_chat(user, "\The [src] will fill [equip_type?"a [equip_type]":"any"] slot.")
/obj/item/mecha_parts/mecha_equipment/New()
..()
diff --git a/code/game/mecha/mech_fabricator.dm b/code/game/mecha/mech_fabricator.dm
index 035e13dbbd..207efeea85 100644
--- a/code/game/mecha/mech_fabricator.dm
+++ b/code/game/mecha/mech_fabricator.dm
@@ -13,7 +13,8 @@
var/speed = 1
var/mat_efficiency = 1
- var/list/materials = list(DEFAULT_WALL_MATERIAL = 0, "glass" = 0, "plastic" = 0, "gold" = 0, "silver" = 0, "osmium" = 0, "diamond" = 0, "phoron" = 0, "uranium" = 0)
+ var/list/materials = list(DEFAULT_WALL_MATERIAL = 0, "glass" = 0, "plastic" = 0, MAT_PLASTEEL = 0, "gold" = 0, "silver" = 0, MAT_LEAD = 0, "osmium" = 0, "diamond" = 0, MAT_DURASTEEL = 0, "phoron" = 0, "uranium" = 0, MAT_VERDANTIUM = 0, MAT_MORPHIUM = 0, MAT_METALHYDROGEN = 0, MAT_SUPERMATTER = 0)
+ var/list/hidden_materials = list(MAT_PLASTEEL, MAT_DURASTEEL, MAT_VERDANTIUM, MAT_MORPHIUM, MAT_METALHYDROGEN, MAT_SUPERMATTER)
var/res_max_amount = 200000
var/datum/research/files
@@ -75,7 +76,7 @@
var/T = 0
for(var/obj/item/weapon/stock_parts/manipulator/M in component_parts)
T += M.rating
- mat_efficiency = 1 - (T - 1) / 4 // 1 -> 0.5
+ mat_efficiency = max(1 - (T - 1) / 4, 0.2) // 1 -> 0.2
for(var/obj/item/weapon/stock_parts/micro_laser/M in component_parts) // Not resetting T is intended; speed is affected by both
T += M.rating
speed = T / 2 // 1 -> 3
@@ -136,7 +137,7 @@
/obj/machinery/mecha_part_fabricator/attackby(var/obj/item/I, var/mob/user)
if(busy)
- user << "\The [src] is busy. Please wait for completion of previous operation."
+ to_chat(user, "\The [src] is busy. Please wait for completion of previous operation.")
return 1
if(default_deconstruction_screwdriver(user, I))
return
@@ -148,25 +149,25 @@
if(istype(I,/obj/item/stack/material))
var/obj/item/stack/material/S = I
if(!(S.material.name in materials))
- user << "The [src] doesn't accept [S.material]!"
+ to_chat(user, "The [src] doesn't accept [S.material]!")
return
var/sname = "[S.name]"
var/amnt = S.perunit
if(materials[S.material.name] + amnt <= res_max_amount)
- if(S && S.amount >= 1)
+ if(S && S.get_amount() >= 1)
var/count = 0
overlays += "mechfab-load-metal"
spawn(10)
overlays -= "mechfab-load-metal"
- while(materials[S.material.name] + amnt <= res_max_amount && S.amount >= 1)
+ while(materials[S.material.name] + amnt <= res_max_amount && S.get_amount() >= 1)
materials[S.material.name] += amnt
S.use(1)
count++
- user << "You insert [count] [sname] into the fabricator."
+ to_chat(user, "You insert [count] [sname] into the fabricator.")
update_busy()
else
- user << "The fabricator cannot hold more [sname]."
+ to_chat(user, "The fabricator cannot hold more [sname].")
return
@@ -273,7 +274,13 @@
/obj/machinery/mecha_part_fabricator/proc/get_materials()
. = list()
for(var/T in materials)
- . += list(list("mat" = capitalize(T), "amt" = materials[T]))
+ var/hidden_mat = FALSE
+ for(var/HM in hidden_materials) // Direct list contents comparison was failing.
+ if(T == HM && materials[T] == 0)
+ hidden_mat = TRUE
+ continue
+ if(!hidden_mat)
+ . += list(list("mat" = capitalize(T), "amt" = materials[T]))
/obj/machinery/mecha_part_fabricator/proc/eject_materials(var/material, var/amount) // 0 amount = 0 means ejecting a full stack; -1 means eject everything
var/recursive = amount == -1 ? 1 : 0
diff --git a/code/game/mecha/mech_prosthetics.dm b/code/game/mecha/mech_prosthetics.dm
index b85f955146..b633e44558 100644
--- a/code/game/mecha/mech_prosthetics.dm
+++ b/code/game/mecha/mech_prosthetics.dm
@@ -13,7 +13,8 @@
var/speed = 1
var/mat_efficiency = 1
- var/list/materials = list(DEFAULT_WALL_MATERIAL = 0, "glass" = 0, "plastic" = 0, "gold" = 0, "silver" = 0, "osmium" = 0, "diamond" = 0, "phoron" = 0, "uranium" = 0, "plasteel" = 0)
+ var/list/materials = list(DEFAULT_WALL_MATERIAL = 0, "glass" = 0, "plastic" = 0, MAT_PLASTEEL = 0, "gold" = 0, "silver" = 0, MAT_LEAD = 0, "osmium" = 0, "diamond" = 0, MAT_DURASTEEL = 0, "phoron" = 0, "uranium" = 0, MAT_VERDANTIUM = 0, MAT_MORPHIUM = 0)
+ var/list/hidden_materials = list(MAT_DURASTEEL, MAT_VERDANTIUM, MAT_MORPHIUM)
var/res_max_amount = 200000
var/datum/research/files
@@ -77,7 +78,7 @@
var/T = 0
for(var/obj/item/weapon/stock_parts/manipulator/M in component_parts)
T += M.rating
- mat_efficiency = 1 - (T - 1) / 4 // 1 -> 0.5
+ mat_efficiency = max(0.2, 1 - (T - 1) / 4) // 1 -> 0.2
for(var/obj/item/weapon/stock_parts/micro_laser/M in component_parts) // Not resetting T is intended; speed is affected by both
T += M.rating
speed = T / 2 // 1 -> 3
@@ -181,12 +182,12 @@
var/sname = "[S.name]"
var/amnt = S.perunit
if(materials[S.material.name] + amnt <= res_max_amount)
- if(S && S.amount >= 1)
+ if(S && S.get_amount() >= 1)
var/count = 0
overlays += "fab-load-metal"
spawn(10)
overlays -= "fab-load-metal"
- while(materials[S.material.name] + amnt <= res_max_amount && S.amount >= 1)
+ while(materials[S.material.name] + amnt <= res_max_amount && S.get_amount() >= 1)
materials[S.material.name] += amnt
S.use(1)
count++
@@ -299,7 +300,13 @@
/obj/machinery/pros_fabricator/proc/get_materials()
. = list()
for(var/T in materials)
- . += list(list("mat" = capitalize(T), "amt" = materials[T]))
+ var/hidden_mat = FALSE
+ for(var/HM in hidden_materials) // Direct list contents comparison was failing.
+ if(T == HM && materials[T] == 0)
+ hidden_mat = TRUE
+ continue
+ if(!hidden_mat)
+ . += list(list("mat" = capitalize(T), "amt" = materials[T]))
/obj/machinery/pros_fabricator/proc/eject_materials(var/material, var/amount) // 0 amount = 0 means ejecting a full stack; -1 means eject everything
var/recursive = amount == -1 ? 1 : 0
diff --git a/code/game/mecha/mecha_construction_paths.dm b/code/game/mecha/mecha_construction_paths.dm
index ce5b751e55..9edff107b1 100644
--- a/code/game/mecha/mecha_construction_paths.dm
+++ b/code/game/mecha/mecha_construction_paths.dm
@@ -1301,4 +1301,570 @@
return 1
/datum/construction/mecha/phazon_chassis/action(obj/item/I,mob/user as mob)
- return check_all_steps(I,user)
\ No newline at end of file
+ return check_all_steps(I,user)
+
+/datum/construction/mecha/phazon_chassis/spawn_result()
+ var/obj/item/mecha_parts/chassis/const_holder = holder
+ const_holder.construct = new /datum/construction/reversible/mecha/phazon(const_holder)
+ const_holder.icon = 'icons/mecha/mech_construction.dmi'
+ const_holder.icon_state = "phazon0"
+ const_holder.density = 1
+ spawn()
+ qdel(src)
+ return
+
+/datum/construction/reversible/mecha/phazon
+ result = "/obj/mecha/combat/phazon"
+ steps = list(
+ //1
+ list("key"=/obj/item/weapon/weldingtool,
+ "backkey"=IS_WRENCH,
+ "desc"="External armor is wrenched."),
+ //2
+ list("key"=IS_WRENCH,
+ "backkey"=IS_CROWBAR,
+ "desc"="External armor is installed."),
+ //3
+ list("key"=/obj/item/stack/material/plasteel,
+ "backkey"=/obj/item/weapon/weldingtool,
+ "desc"="Internal armor is welded."),
+ //4
+ list("key"=/obj/item/weapon/weldingtool,
+ "backkey"=IS_WRENCH,
+ "desc"="Internal armor is wrenched"),
+ //5
+ list("key"=IS_WRENCH,
+ "backkey"=IS_CROWBAR,
+ "desc"="Internal armor is installed"),
+ //6
+ list("key"=/obj/item/stack/material/steel,
+ "backkey"=IS_SCREWDRIVER,
+ "desc"="Hand teleporter is secured"),
+ //7
+ list("key"=IS_SCREWDRIVER,
+ "backkey"=IS_CROWBAR,
+ "desc"="Hand teleporter is installed"),
+ //8
+ list("key"=/obj/item/weapon/hand_tele,
+ "backkey"=IS_SCREWDRIVER,
+ "desc"="SMES coil is secured"),
+ //9
+ list("key"=IS_SCREWDRIVER,
+ "backkey"=IS_CROWBAR,
+ "desc"="SMES coil is installed"),
+ //10
+ list("key"=/obj/item/weapon/smes_coil/super_capacity,
+ "backkey"=IS_SCREWDRIVER,
+ "desc"="Targeting module is secured"),
+ //11
+ list("key"=IS_SCREWDRIVER,
+ "backkey"=IS_CROWBAR,
+ "desc"="Targeting module is installed"),
+ //12
+ list("key"=/obj/item/weapon/circuitboard/mecha/phazon/targeting,
+ "backkey"=IS_SCREWDRIVER,
+ "desc"="Peripherals control module is secured"),
+ //13
+ list("key"=IS_SCREWDRIVER,
+ "backkey"=IS_CROWBAR,
+ "desc"="Peripherals control module is installed"),
+ //14
+ list("key"=/obj/item/weapon/circuitboard/mecha/phazon/peripherals,
+ "backkey"=IS_SCREWDRIVER,
+ "desc"="Central control module is secured"),
+ //15
+ list("key"=IS_SCREWDRIVER,
+ "backkey"=IS_CROWBAR,
+ "desc"="Central control module is installed"),
+ //16
+ list("key"=/obj/item/weapon/circuitboard/mecha/phazon/main,
+ "backkey"=IS_SCREWDRIVER,
+ "desc"="The wiring is adjusted"),
+ //17
+ list("key"=/obj/item/weapon/tool/wirecutters,
+ "backkey"=IS_SCREWDRIVER,
+ "desc"="The wiring is added"),
+ //18
+ list("key"=/obj/item/stack/cable_coil,
+ "backkey"=IS_SCREWDRIVER,
+ "desc"="The hydraulic systems are active."),
+ //19
+ list("key"=IS_SCREWDRIVER,
+ "backkey"=IS_WRENCH,
+ "desc"="The hydraulic systems are connected."),
+ //20
+ list("key"=IS_WRENCH,
+ "desc"="The hydraulic systems are disconnected.")
+ )
+
+/datum/construction/reversible/mecha/phazon/action(obj/item/I,mob/user as mob)
+ return check_step(I,user)
+
+/datum/construction/reversible/mecha/phazon/custom_action(index, diff, obj/item/I, mob/user)
+ if(!..())
+ return 0
+
+ switch(index)
+ if(20)
+ user.visible_message("[user] connects [holder] hydraulic systems", "You connect [holder] hydraulic systems.")
+ holder.icon_state = "phazon1"
+ if(19)
+ if(diff==FORWARD)
+ user.visible_message("[user] activates [holder] hydraulic systems.", "You activate [holder] hydraulic systems.")
+ holder.icon_state = "phazon2"
+ else
+ user.visible_message("[user] disconnects [holder] hydraulic systems", "You disconnect [holder] hydraulic systems.")
+ holder.icon_state = "phazon0"
+ if(18)
+ if(diff==FORWARD)
+ user.visible_message("[user] adds the wiring to [holder].", "You add the wiring to [holder].")
+ holder.icon_state = "phazon3"
+ else
+ user.visible_message("[user] deactivates [holder] hydraulic systems.", "You deactivate [holder] hydraulic systems.")
+ holder.icon_state = "phazon1"
+ if(17)
+ if(diff==FORWARD)
+ user.visible_message("[user] adjusts the wiring of [holder].", "You adjust the wiring of [holder].")
+ holder.icon_state = "phazon4"
+ else
+ user.visible_message("[user] removes the wiring from [holder].", "You remove the wiring from [holder].")
+ var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil(get_turf(holder))
+ coil.amount = 4
+ holder.icon_state = "phazon2"
+ if(16)
+ if(diff==FORWARD)
+ user.visible_message("[user] installs the central control module into [holder].", "You install the central computer mainboard into [holder].")
+ qdel(I)
+ holder.icon_state = "phazon5"
+ else
+ user.visible_message("[user] disconnects the wiring of [holder].", "You disconnect the wiring of [holder].")
+ holder.icon_state = "phazon3"
+ if(15)
+ if(diff==FORWARD)
+ user.visible_message("[user] secures the mainboard.", "You secure the mainboard.")
+ holder.icon_state = "phazon6"
+ else
+ user.visible_message("[user] removes the central control module from [holder].", "You remove the central computer mainboard from [holder].")
+ new /obj/item/weapon/circuitboard/mecha/phazon/main(get_turf(holder))
+ holder.icon_state = "phazon4"
+ if(14)
+ if(diff==FORWARD)
+ user.visible_message("[user] installs the peripherals control module into [holder].", "You install the peripherals control module into [holder].")
+ qdel(I)
+ holder.icon_state = "phazon7"
+ else
+ user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.")
+ holder.icon_state = "phazon5"
+ if(13)
+ if(diff==FORWARD)
+ user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.")
+ holder.icon_state = "phazon8"
+ else
+ user.visible_message("[user] removes the peripherals control module from [holder].", "You remove the peripherals control module from [holder].")
+ new /obj/item/weapon/circuitboard/mecha/phazon/peripherals(get_turf(holder))
+ holder.icon_state = "phazon6"
+ if(12)
+ if(diff==FORWARD)
+ user.visible_message("[user] installs the weapon control module into [holder].", "You install the weapon control module into [holder].")
+ qdel(I)
+ holder.icon_state = "phazon9"
+ else
+ user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.")
+ holder.icon_state = "phazon7"
+ if(11)
+ if(diff==FORWARD)
+ user.visible_message("[user] secures the weapon control module.", "You secure the weapon control module.")
+ holder.icon_state = "phazon10"
+ else
+ user.visible_message("[user] removes the weapon control module from [holder].", "You remove the weapon control module from [holder].")
+ new /obj/item/weapon/circuitboard/mecha/phazon/targeting(get_turf(holder))
+ holder.icon_state = "phazon8"
+ if(10)
+ if(diff==FORWARD)
+ user.visible_message("[user] installs the SMES coil to [holder].", "You install the SMES coil to [holder].")
+ qdel(I)
+ holder.icon_state = "phazon11"
+ else
+ user.visible_message("[user] unfastens the weapon control module.", "You unfasten the weapon control module.")
+ holder.icon_state = "phazon9"
+ if(9)
+ if(diff==FORWARD)
+ user.visible_message("[user] secures the SMES coil.", "You secure the SMES coil.")
+ holder.icon_state = "phazon12"
+ else
+ user.visible_message("[user] removes the SMES coil from [holder].", "You remove the SMES coil from [holder].")
+ new /obj/item/weapon/smes_coil/super_capacity(get_turf(holder))
+ holder.icon_state = "phazon10"
+ if(8)
+ if(diff==FORWARD)
+ user.visible_message("[user] installs the hand teleporter to [holder].", "You install the hand teleporter to [holder].")
+ qdel(I)
+ holder.icon_state = "phazon13"
+ else
+ user.visible_message("[user] unfastens the SMES coil.", "You unfasten the SMES coil.")
+ holder.icon_state = "phazon11"
+ if(7)
+ if(diff==FORWARD)
+ user.visible_message("[user] secures the hand teleporter.", "You secure the hand teleporter.")
+ holder.icon_state = "phazon14"
+ else
+ user.visible_message("[user] removes the hand teleporter from [holder].", "You remove the hand teleporter from [holder].")
+ new /obj/item/weapon/hand_tele(get_turf(holder))
+ holder.icon_state = "phazon12"
+ if(6)
+ if(diff==FORWARD)
+ user.visible_message("[user] installs the internal armor layer to [holder].", "You install the internal armor layer to [holder].")
+ holder.icon_state = "phazon19"
+ else
+ user.visible_message("[user] unfastens the hand teleporter.", "You unfasten the hand teleporter.")
+ holder.icon_state = "phazon13"
+ if(5)
+ if(diff==FORWARD)
+ user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.")
+ holder.icon_state = "phazon20"
+ else
+ user.visible_message("[user] pries the internal armor layer from [holder].", "You pry the internal armor layer from [holder].")
+ var/obj/item/stack/material/steel/MS = new /obj/item/stack/material/steel(get_turf(holder))
+ MS.amount = 5
+ holder.icon_state = "phazon14"
+ if(4)
+ if(diff==FORWARD)
+ user.visible_message("[user] welds the internal armor layer to [holder].", "You weld the internal armor layer to [holder].")
+ holder.icon_state = "phazon21"
+ else
+ user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.")
+ holder.icon_state = "phazon19"
+ if(3)
+ if(diff==FORWARD)
+ user.visible_message("[user] installs the external reinforced armor layer to [holder].", "You install the external reinforced armor layer to [holder].")
+ holder.icon_state = "phazon22"
+ else
+ user.visible_message("[user] cuts internal armor layer from [holder].", "You cut the internal armor layer from [holder].")
+ holder.icon_state = "phazon20"
+ if(2)
+ if(diff==FORWARD)
+ user.visible_message("[user] secures external armor layer.", "You secure external reinforced armor layer.")
+ holder.icon_state = "phazon23"
+ else
+ user.visible_message("[user] pries the external armor layer from [holder].", "You pry external armor layer from [holder].")
+ var/obj/item/stack/material/plasteel/MS = new /obj/item/stack/material/plasteel(get_turf(holder))
+ MS.amount = 5
+ holder.icon_state = "phazon21"
+ if(1)
+ if(diff==FORWARD)
+ user.visible_message("[user] welds the external armor layer to [holder].", "You weld the external armor layer to [holder].")
+ else
+ user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.")
+ holder.icon_state = "phazon22"
+ return 1
+
+/datum/construction/reversible/mecha/phazon/spawn_result()
+ ..()
+ feedback_inc("mecha_phazon_created",1)
+ return
+
+//////////////////////
+// Janus
+//////////////////////
+/datum/construction/mecha/janus_chassis
+ result = "/obj/mecha/combat/phazon/janus"
+ steps = list(list("key"=/obj/item/mecha_parts/part/janus_torso),//1
+ list("key"=/obj/item/mecha_parts/part/janus_left_arm),//2
+ list("key"=/obj/item/mecha_parts/part/janus_right_arm),//3
+ list("key"=/obj/item/mecha_parts/part/janus_left_leg),//4
+ list("key"=/obj/item/mecha_parts/part/janus_right_leg),//5
+ list("key"=/obj/item/mecha_parts/part/janus_head)
+ )
+
+/datum/construction/mecha/janus_chassis/custom_action(step, obj/item/I, mob/user)
+ user.visible_message("[user] has connected [I] to [holder].", "You connect [I] to [holder]")
+ holder.overlays += I.icon_state+"+o"
+ qdel(I)
+ return 1
+
+/datum/construction/mecha/janus_chassis/action(obj/item/I,mob/user as mob)
+ return check_all_steps(I,user)
+
+/datum/construction/mecha/janus_chassis/spawn_result()
+ var/obj/item/mecha_parts/chassis/const_holder = holder
+ const_holder.construct = new /datum/construction/reversible/mecha/janus(const_holder)
+ const_holder.icon = 'icons/mecha/mech_construction.dmi'
+ const_holder.icon_state = "janus0"
+ const_holder.density = 1
+ spawn()
+ qdel(src)
+ return
+
+/datum/construction/reversible/mecha/janus
+ result = "/obj/mecha/combat/phazon/janus"
+ steps = list(
+ //1
+ list("key"=/obj/item/weapon/weldingtool,
+ "backkey"=IS_CROWBAR,
+ "desc"="External armor is installed."),
+ //2
+ list("key"=IS_WRENCH,
+ "backkey"=IS_CROWBAR,
+ "desc"="External armor is attached."),
+ //3
+ list("key"=/obj/item/stack/material/morphium,
+ "backkey"=/obj/item/weapon/weldingtool,
+ "desc"="Internal armor is welded"),
+ //4
+ list("key"=/obj/item/weapon/weldingtool,
+ "backkey"=IS_CROWBAR,
+ "desc"="Internal armor is wrenched"),
+ //5
+ list("key"=IS_WRENCH,
+ "backkey"=IS_CROWBAR,
+ "desc"="Internal armor is attached."),
+ //6
+ list("key"=/obj/item/stack/material/durasteel,
+ "backkey"=IS_SCREWDRIVER,
+ "desc"="Durand auxiliary board is secured."),
+ //7
+ list("key"=IS_SCREWDRIVER,
+ "backkey"=IS_CROWBAR,
+ "desc"="Durand auxiliary board is installed"),
+ //8
+ list("key"=/obj/item/weapon/circuitboard/mecha/durand/peripherals,
+ "backkey"=IS_SCREWDRIVER,
+ "desc"="Phase coil is secured"),
+ //9
+ list("key"=IS_SCREWDRIVER,
+ "backkey"=IS_CROWBAR,
+ "desc"="Phase coil is installed"),
+ //10
+ list("key"=/obj/item/prop/alien/phasecoil,
+ "backkey"=IS_SCREWDRIVER,
+ "desc"="Gygax balance system secured"),
+ //11
+ list("key"=IS_SCREWDRIVER,
+ "backkey"=IS_CROWBAR,
+ "desc"="Gygax balance system installed"),
+ //12
+ list("key"=/obj/item/weapon/circuitboard/mecha/gygax/peripherals,
+ "backkey"=IS_SCREWDRIVER,
+ "desc"="Targeting module is secured"),
+ //13
+ list("key"=IS_SCREWDRIVER,
+ "backkey"=IS_CROWBAR,
+ "desc"="Targeting module is installed"),
+ //14
+ list("key"=/obj/item/weapon/circuitboard/mecha/imperion/targeting,
+ "backkey"=IS_SCREWDRIVER,
+ "desc"="Peripherals control module is secured"),
+ //15
+ list("key"=IS_SCREWDRIVER,
+ "backkey"=IS_CROWBAR,
+ "desc"="Peripherals control module is installed"),
+ //16
+ list("key"=/obj/item/weapon/circuitboard/mecha/imperion/peripherals,
+ "backkey"=IS_SCREWDRIVER,
+ "desc"="Central control module is secured"),
+ //17
+ list("key"=IS_SCREWDRIVER,
+ "backkey"=IS_CROWBAR,
+ "desc"="Central control module is installed"),
+ //18
+ list("key"=/obj/item/weapon/circuitboard/mecha/imperion/main,
+ "backkey"=IS_SCREWDRIVER,
+ "desc"="The wiring is adjusted"),
+ //19
+ list("key"=/obj/item/weapon/tool/wirecutters,
+ "backkey"=IS_SCREWDRIVER,
+ "desc"="The wiring is added"),
+ //20
+ list("key"=/obj/item/stack/cable_coil,
+ "backkey"=IS_SCREWDRIVER,
+ "desc"="The hydraulic systems are active."),
+ //21
+ list("key"=IS_SCREWDRIVER,
+ "backkey"=IS_WRENCH,
+ "desc"="The hydraulic systems are connected."),
+ //22
+ list("key"=IS_WRENCH,
+ "desc"="The hydraulic systems are disconnected.")
+ )
+
+/datum/construction/reversible/mecha/janus/action(obj/item/I,mob/user as mob)
+ return check_step(I,user)
+
+/datum/construction/reversible/mecha/janus/custom_action(index, diff, obj/item/I, mob/user)
+ if(!..())
+ return 0
+
+ switch(index)
+ if(22)
+ user.visible_message("[user] connects [holder] hydraulic systems", "You connect [holder] hydraulic systems.")
+ holder.icon_state = "janus1"
+ if(21)
+ if(diff==FORWARD)
+ user.visible_message("[user] activates [holder] hydraulic systems.", "You activate [holder] hydraulic systems.")
+ holder.icon_state = "janus2"
+ else
+ user.visible_message("[user] disconnects [holder] hydraulic systems", "You disconnect [holder] hydraulic systems.")
+ holder.icon_state = "janus0"
+ if(20)
+ if(diff==FORWARD)
+ user.visible_message("[user] adds the wiring to [holder].", "You add the wiring to [holder].")
+ holder.icon_state = "janus3"
+ else
+ user.visible_message("[user] deactivates [holder] hydraulic systems.", "You deactivate [holder] hydraulic systems.")
+ holder.icon_state = "janus1"
+ if(19)
+ if(diff==FORWARD)
+ user.visible_message("[user] adjusts the wiring of [holder].", "You adjust the wiring of [holder].")
+ holder.icon_state = "janus4"
+ else
+ user.visible_message("[user] removes the wiring from [holder].", "You remove the wiring from [holder].")
+ var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil(get_turf(holder))
+ coil.amount = 4
+ holder.icon_state = "janus2"
+ if(18)
+ if(diff==FORWARD)
+ user.visible_message("[user] installs the central control module into [holder].", "You install the central computer mainboard into [holder].")
+ qdel(I)
+ holder.icon_state = "janus5"
+ else
+ user.visible_message("[user] disconnects the wiring of [holder].", "You disconnect the wiring of [holder].")
+ holder.icon_state = "janus3"
+ if(17)
+ if(diff==FORWARD)
+ user.visible_message("[user] secures the mainboard.", "You secure the mainboard.")
+ holder.icon_state = "janus6"
+ else
+ user.visible_message("[user] removes the central control module from [holder].", "You remove the central computer mainboard from [holder].")
+ new /obj/item/weapon/circuitboard/mecha/imperion/main(get_turf(holder))
+ holder.icon_state = "janus4"
+ if(16)
+ if(diff==FORWARD)
+ user.visible_message("[user] installs the peripherals control module into [holder].", "You install the peripherals control module into [holder].")
+ qdel(I)
+ holder.icon_state = "janus7"
+ else
+ user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.")
+ holder.icon_state = "janus5"
+ if(15)
+ if(diff==FORWARD)
+ user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.")
+ holder.icon_state = "janus8"
+ else
+ user.visible_message("[user] removes the peripherals control module from [holder].", "You remove the peripherals control module from [holder].")
+ new /obj/item/weapon/circuitboard/mecha/imperion/peripherals(get_turf(holder))
+ holder.icon_state = "janus6"
+ if(14)
+ if(diff==FORWARD)
+ user.visible_message("[user] installs the weapon control module into [holder].", "You install the weapon control module into [holder].")
+ qdel(I)
+ holder.icon_state = "janus9"
+ else
+ user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.")
+ holder.icon_state = "janus7"
+ if(13)
+ if(diff==FORWARD)
+ user.visible_message("[user] secures the weapon control module.", "You secure the weapon control module.")
+ holder.icon_state = "janus10"
+ else
+ user.visible_message("[user] removes the weapon control module from [holder].", "You remove the weapon control module from [holder].")
+ new /obj/item/weapon/circuitboard/mecha/imperion/targeting(get_turf(holder))
+ holder.icon_state = "janus8"
+ if(12)
+ if(diff==FORWARD)
+ user.visible_message("[user] installs the Gygax control module into [holder].", "You install the Gygax control module into [holder].")
+ qdel(I)
+ holder.icon_state = "janus11"
+ else
+ user.visible_message("[user] unfastens the Gygax control module.", "You unfasten the Gygax control module.")
+ holder.icon_state = "janus9"
+ if(11)
+ if(diff==FORWARD)
+ user.visible_message("[user] secures the Gygax control module.", "You secure the Gygax control module.")
+ holder.icon_state = "janus12"
+ else
+ user.visible_message("[user] removes the Gygax control module from [holder].", "You remove the Gygax control module from [holder].")
+ new /obj/item/weapon/circuitboard/mecha/gygax/peripherals(get_turf(holder))
+ holder.icon_state = "janus10"
+ if(10)
+ if(diff==FORWARD)
+ user.visible_message("[user] installs the phase coil into [holder].", "You install the phase coil into [holder].")
+ qdel(I)
+ holder.icon_state = "janus13"
+ else
+ user.visible_message("[user] unfastens the Gygax control module.", "You unfasten the Gygax control module.")
+ holder.icon_state = "janus11"
+ if(9)
+ if(diff==FORWARD)
+ user.visible_message("[user] secures the phase coil.", "You secure the phase coil.")
+ holder.icon_state = "janus14"
+ else
+ user.visible_message("[user] removes the phase coil from [holder].", "You remove the phase coil from [holder].")
+ new /obj/item/prop/alien/phasecoil(get_turf(holder))
+ holder.icon_state = "janus12"
+ if(8)
+ if(diff==FORWARD)
+ user.visible_message("[user] installs the Durand control module into [holder].", "You install the Durand control module into [holder].")
+ qdel(I)
+ holder.icon_state = "janus15"
+ else
+ user.visible_message("[user] unfastens the phase coil.", "You unfasten the phase coil.")
+ holder.icon_state = "janus13"
+ if(7)
+ if(diff==FORWARD)
+ user.visible_message("[user] secures the Durand control module.", "You secure the Durand control module.")
+ holder.icon_state = "janus16"
+ else
+ user.visible_message("[user] removes the Durand control module from [holder].", "You remove the Durand control module from [holder].")
+ new /obj/item/weapon/circuitboard/mecha/durand/peripherals(get_turf(holder))
+ holder.icon_state = "janus14"
+ if(6)
+ if(diff==FORWARD)
+ user.visible_message("[user] installs the internal armor layer to [holder].", "You install the internal armor layer to [holder].")
+ holder.icon_state = "janus17"
+ else
+ user.visible_message("[user] unfastens the Durand control module.", "You unfasten the Durand control module.")
+ holder.icon_state = "janus15"
+ if(5)
+ if(diff==FORWARD)
+ user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.")
+ holder.icon_state = "janus18"
+ else
+ user.visible_message("[user] pries the internal armor layer from [holder].", "You pry the internal armor layer from [holder].")
+ var/obj/item/stack/material/durasteel/MS = new /obj/item/stack/material/durasteel(get_turf(holder))
+ MS.amount = 5
+ holder.icon_state = "janus16"
+ if(4)
+ if(diff==FORWARD)
+ user.visible_message("[user] welds the internal armor layer to [holder].", "You weld the internal armor layer to [holder].")
+ holder.icon_state = "janus19"
+ else
+ user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.")
+ holder.icon_state = "janus17"
+ if(3)
+ if(diff==FORWARD)
+ user.visible_message("[user] installs the external reinforced armor layer to [holder].", "You install the external reinforced armor layer to [holder].")
+ holder.icon_state = "janus20"
+ else
+ user.visible_message("[user] cuts internal armor layer from [holder].", "You cut the internal armor layer from [holder].")
+ holder.icon_state = "janus18"
+ if(2)
+ if(diff==FORWARD)
+ user.visible_message("[user] secures external armor layer.", "You secure external reinforced armor layer.")
+ holder.icon_state = "janus21"
+ else
+ user.visible_message("[user] pries the external armor layer from [holder].", "You pry external armor layer from [holder].")
+ var/obj/item/stack/material/morphium/MS = new /obj/item/stack/material/morphium(get_turf(holder))
+ MS.amount = 5
+ holder.icon_state = "janus19"
+ if(1)
+ if(diff==FORWARD)
+ user.visible_message("[user] welds the external armor layer to [holder].", "You weld the external armor layer to [holder].")
+ else
+ user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.")
+ holder.icon_state = "janus20"
+ return 1
+
+/datum/construction/reversible/mecha/janus/spawn_result()
+ ..()
+ feedback_inc("mecha_janus_created",1)
+ return
diff --git a/code/game/mecha/mecha_parts.dm b/code/game/mecha/mecha_parts.dm
index dc4036294b..07c619b9ff 100644
--- a/code/game/mecha/mecha_parts.dm
+++ b/code/game/mecha/mecha_parts.dm
@@ -292,3 +292,43 @@
origin_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 3)
construction_time = 200
construction_cost = list(DEFAULT_WALL_MATERIAL=15000)*/
+
+////////// Janus
+
+/obj/item/mecha_parts/chassis/janus
+ name = "Janus Chassis"
+ origin_tech = list(TECH_MATERIAL = 7)
+
+ New()
+ ..()
+ construct = new /datum/construction/mecha/janus_chassis(src)
+
+/obj/item/mecha_parts/part/janus_torso
+ name="Imperion Torso"
+ icon_state = "janus_harness"
+ origin_tech = list(TECH_DATA = 5, TECH_MATERIAL = 7, TECH_BLUESPACE = 2, TECH_POWER = 6, TECH_PRECURSOR = 2)
+
+/obj/item/mecha_parts/part/janus_head
+ name="Imperion Head"
+ icon_state = "janus_head"
+ origin_tech = list(TECH_DATA = 4, TECH_MATERIAL = 5, TECH_MAGNET = 6, TECH_PRECURSOR = 1)
+
+/obj/item/mecha_parts/part/janus_left_arm
+ name="Prototype Gygax Left Arm"
+ icon_state = "janus_l_arm"
+ origin_tech = list(TECH_MATERIAL = 5, TECH_BLUESPACE = 2, TECH_MAGNET = 2)
+
+/obj/item/mecha_parts/part/janus_right_arm
+ name="Prototype Gygax Right Arm"
+ icon_state = "janus_r_arm"
+ origin_tech = list(TECH_MATERIAL = 5, TECH_BLUESPACE = 2, TECH_MAGNET = 2)
+
+/obj/item/mecha_parts/part/janus_left_leg
+ name="Prototype Durand Left Leg"
+ icon_state = "janus_l_leg"
+ origin_tech = list(TECH_MATERIAL = 5, TECH_BLUESPACE = 3, TECH_MAGNET = 3, TECH_ARCANE = 1)
+
+/obj/item/mecha_parts/part/janus_right_leg
+ name="Prototype Durand Right Leg"
+ icon_state = "janus_r_leg"
+ origin_tech = list(TECH_MATERIAL = 5, TECH_BLUESPACE = 3, TECH_MAGNET = 3, TECH_ARCANE = 1)
diff --git a/code/game/mecha/mecha_wreckage.dm b/code/game/mecha/mecha_wreckage.dm
index bc20f25b2e..5a95b9017c 100644
--- a/code/game/mecha/mecha_wreckage.dm
+++ b/code/game/mecha/mecha_wreckage.dm
@@ -212,3 +212,8 @@
/obj/effect/decal/mecha_wreckage/hoverpod
name = "Hover pod wreckage"
icon_state = "engineering_pod-broken"
+
+/obj/effect/decal/mecha_wreckage/janus
+ name = "Janus wreckage"
+ icon_state = "janus-broken"
+ description_info = "Due to the incredibly intricate design of this exosuit, it is impossible to salvage components from it."
diff --git a/code/game/objects/buckling.dm b/code/game/objects/buckling.dm
index 779b102f13..8d4bfe0555 100644
--- a/code/game/objects/buckling.dm
+++ b/code/game/objects/buckling.dm
@@ -51,20 +51,10 @@
/atom/movable/proc/buckle_mob(mob/living/M, forced = FALSE, check_loc = TRUE)
- if(!buckled_mobs)
- buckled_mobs = list()
-
- if(!istype(M))
- return FALSE
-
if(check_loc && M.loc != loc)
return FALSE
- if((!can_buckle && !forced) || M.buckled || M.pinned.len || (buckled_mobs.len >= max_buckled_mobs) || (buckle_require_restraints && !M.restrained()))
- return FALSE
-
- if(has_buckled_mobs() && buckled_mobs.len >= max_buckled_mobs) //Handles trying to buckle yourself to the chair when someone is on it
- to_chat(M, "\The [src] can't buckle anymore people.")
+ if(!can_buckle_check(M, forced))
return FALSE
M.buckled = src
@@ -117,6 +107,8 @@
if(M in buckled_mobs)
to_chat(user, "\The [M] is already buckled to \the [src].")
return FALSE
+ if(!can_buckle_check(M, forced))
+ return FALSE
add_fingerprint(user)
// unbuckle_mob()
@@ -170,3 +162,19 @@
else
L.set_dir(dir)
return TRUE
+
+/atom/movable/proc/can_buckle_check(mob/living/M, forced = FALSE)
+ if(!buckled_mobs)
+ buckled_mobs = list()
+
+ if(!istype(M))
+ return FALSE
+
+ if((!can_buckle && !forced) || M.buckled || M.pinned.len || (buckled_mobs.len >= max_buckled_mobs) || (buckle_require_restraints && !M.restrained()))
+ return FALSE
+
+ if(has_buckled_mobs() && buckled_mobs.len >= max_buckled_mobs) //Handles trying to buckle yourself to the chair when someone is on it
+ to_chat(M, "\The [src] can't buckle anymore people.")
+ return FALSE
+
+ return TRUE
diff --git a/code/game/objects/effects/portals.dm b/code/game/objects/effects/portals.dm
index dc4bf5146a..7dc82a0ffb 100644
--- a/code/game/objects/effects/portals.dm
+++ b/code/game/objects/effects/portals.dm
@@ -13,18 +13,24 @@ GLOBAL_LIST_BOILERPLATE(all_portals, /obj/effect/portal)
anchored = 1.0
/obj/effect/portal/Bumped(mob/M as mob|obj)
+ if(istype(M,/mob) && !(istype(M,/mob/living)))
+ return //do not send ghosts, zshadows, ai eyes, etc
spawn(0)
src.teleport(M)
return
return
/obj/effect/portal/Crossed(AM as mob|obj)
+ if(istype(AM,/mob) && !(istype(AM,/mob/living)))
+ return //do not send ghosts, zshadows, ai eyes, etc
spawn(0)
src.teleport(AM)
return
return
/obj/effect/portal/attack_hand(mob/user as mob)
+ if(istype(user) && !(istype(user,/mob/living)))
+ return //do not send ghosts, zshadows, ai eyes, etc
spawn(0)
src.teleport(user)
return
diff --git a/code/game/objects/items/apc_frame.dm b/code/game/objects/items/apc_frame.dm
index f14b19b117..8af1c01a2a 100644
--- a/code/game/objects/items/apc_frame.dm
+++ b/code/game/objects/items/apc_frame.dm
@@ -5,12 +5,7 @@
desc = "Used for repairing or building APCs"
icon = 'icons/obj/apc_repair.dmi'
icon_state = "apc_frame"
-
-/obj/item/frame/apc/attackby(obj/item/weapon/W as obj, mob/user as mob)
- ..()
- if (W.is_wrench())
- new /obj/item/stack/material/steel( get_turf(src.loc), 2 )
- qdel(src)
+ refund_amt = 2
/obj/item/frame/apc/try_build(turf/on_wall, mob/user as mob)
if (get_dist(on_wall, user)>1)
diff --git a/code/game/objects/items/bodybag.dm b/code/game/objects/items/bodybag.dm
index 600ed118dc..1a8b10a26a 100644
--- a/code/game/objects/items/bodybag.dm
+++ b/code/game/objects/items/bodybag.dm
@@ -141,11 +141,12 @@
store_items = 0
var/used = 0
var/obj/item/weapon/tank/tank = null
+ var/tank_type = /obj/item/weapon/tank/stasis/oxygen
var/stasis_level = 3 //Every 'this' life ticks are applied to the mob (when life_ticks%stasis_level == 1)
var/obj/item/weapon/reagent_containers/syringe/syringe
/obj/structure/closet/body_bag/cryobag/Initialize()
- tank = new /obj/item/weapon/tank/stasis/oxygen(null) //It's in nullspace to prevent ejection when the bag is opened.
+ tank = new tank_type(null) //It's in nullspace to prevent ejection when the bag is opened.
..()
/obj/structure/closet/body_bag/cryobag/Destroy()
@@ -157,7 +158,7 @@
. = ..()
if(used)
var/obj/item/O = new/obj/item(src.loc)
- O.name = "used stasis bag"
+ O.name = "used [name]"
O.icon = src.icon
O.icon_state = "bodybag_used"
O.desc = "Pretty useless now..."
diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm
index f1a99a6090..ff47cab3ca 100644
--- a/code/game/objects/items/crayons.dm
+++ b/code/game/objects/items/crayons.dm
@@ -69,9 +69,13 @@
if(!proximity) return
if(istype(target,/turf/simulated/floor))
var/drawtype = input("Choose what you'd like to draw.", "Crayon scribbles") in list("graffiti","rune","letter","arrow")
+ if(get_dist(target, user) > 1 || !(user.z == target.z))
+ return
switch(drawtype)
if("letter")
drawtype = input("Choose the letter.", "Crayon scribbles") in list("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z")
+ if(get_dist(target, user) > 1 || !(user.z == target.z))
+ return
to_chat(user, "You start drawing a letter on the [target.name].")
if("graffiti")
to_chat(user, "You start drawing graffiti on the [target.name].")
@@ -79,6 +83,8 @@
to_chat(user, "You start drawing a rune on the [target.name].")
if("arrow")
drawtype = input("Choose the arrow.", "Crayon scribbles") in list("left", "right", "up", "down")
+ if(get_dist(target, user) > 1 || !(user.z == target.z))
+ return
to_chat(user, "You start drawing an arrow on the [target.name].")
if(instant || do_after(user, 50))
new /obj/effect/decal/cleanable/crayon(target,colour,shadeColour,drawtype)
diff --git a/code/game/objects/items/devices/aicard.dm b/code/game/objects/items/devices/aicard.dm
index 181764d126..cc7ac0e4a4 100644
--- a/code/game/objects/items/devices/aicard.dm
+++ b/code/game/objects/items/devices/aicard.dm
@@ -59,15 +59,15 @@
var/user = usr
if (href_list["wipe"])
- var/confirm = alert("Are you sure you want to disable this core's power? This cannot be undone once started.", "Confirm Shutdown", "Yes", "No")
+ var/confirm = alert("Are you sure you want to disable this core's power? This cannot be undone once started.", "Confirm Shutdown", "No", "Yes")
if(confirm == "Yes" && (CanUseTopic(user, state) == STATUS_INTERACTIVE))
add_attack_logs(user,carded_ai,"Purged from AI Card")
flush = 1
carded_ai.suiciding = 1
to_chat(carded_ai, "Your power has been disabled!")
- while (carded_ai && carded_ai.stat != 2)
- if(carded_ai.controlling_drone && prob(carded_ai.oxyloss)) //You feel it creeping? Eventually will reach 100, resulting in the second half of the AI's remaining life being lonely.
- carded_ai.controlling_drone.release_ai_control("Unit lost. Integrity too low to maintain connection.")
+ while (carded_ai && carded_ai.stat != DEAD)
+ if(carded_ai.deployed_shell && prob(carded_ai.oxyloss)) //You feel it creeping? Eventually will reach 100, resulting in the second half of the AI's remaining life being lonely.
+ carded_ai.disconnect_shell("Disconnecting from remote shell due to insufficent power.")
carded_ai.adjustOxyLoss(2)
carded_ai.updatehealth()
sleep(10)
@@ -80,8 +80,8 @@
carded_ai.control_disabled = text2num(href_list["wireless"])
to_chat(carded_ai, "Your wireless interface has been [carded_ai.control_disabled ? "disabled" : "enabled"]!")
to_chat(user, "You [carded_ai.control_disabled ? "disable" : "enable"] the AI's wireless interface.")
- if(carded_ai.control_disabled && carded_ai.controlling_drone)
- carded_ai.controlling_drone.release_ai_control("Unit control terminated at intellicore port.")
+ if(carded_ai.control_disabled && carded_ai.deployed_shell)
+ carded_ai.disconnect_shell("Disconnecting from remote shell due to [src] wireless access interface being disabled.")
update_icon()
return 1
@@ -98,7 +98,7 @@
icon_state = "aicard"
/obj/item/device/aicard/proc/grab_ai(var/mob/living/silicon/ai/ai, var/mob/living/user)
- if(!ai.client && !ai.controlling_drone)
+ if(!ai.client && !ai.deployed_shell)
to_chat(user, "ERROR: AI [ai.name] is offline. Unable to transfer.")
return 0
@@ -112,9 +112,7 @@
return 0
user.visible_message("\The [user] starts transferring \the [ai] into \the [src]...", "You start transferring \the [ai] into \the [src]...")
- to_chat(ai, "\The [user] is transferring you into \the [src]!")
- if(ai.controlling_drone)
- to_chat(ai.controlling_drone, "\The [user] is transferring you into \the [src]!")
+ show_message(span("critical", "\The [user] is transferring you into \the [src]!"))
if(do_after(user, 100))
if(istype(ai.loc, /turf/))
@@ -130,8 +128,7 @@
ai.control_disabled = 1
ai.aiRestorePowerRoutine = 0
carded_ai = ai
- if(ai.controlling_drone)
- ai.controlling_drone.release_ai_control("Unit control lost.")
+ ai.disconnect_shell("Disconnected from remote shell due to core intelligence transfer.") //If the AI is controlling a borg, force the player back to core!
if(ai.client)
to_chat(ai, "You have been transferred into a mobile core. Remote access lost.")
diff --git a/code/game/objects/items/devices/laserpointer.dm b/code/game/objects/items/devices/laserpointer.dm
index ee77a47de9..810147dbc4 100644
--- a/code/game/objects/items/devices/laserpointer.dm
+++ b/code/game/objects/items/devices/laserpointer.dm
@@ -71,6 +71,8 @@
/obj/item/device/laser_pointer/proc/laser_act(var/atom/target, var/mob/living/user)
if(!(user in (viewers(world.view,target))))
return
+ if(!(target in view(user, world.view)))
+ return
if(!(world.time - last_used_time >= cooldown))
return
if (!diode)
diff --git a/code/game/objects/items/devices/locker_painter.dm b/code/game/objects/items/devices/locker_painter.dm
index 07c4cb3f1c..0d264b6a5b 100644
--- a/code/game/objects/items/devices/locker_painter.dm
+++ b/code/game/objects/items/devices/locker_painter.dm
@@ -64,7 +64,6 @@
/obj/structure/closet/cabinet,
/obj/structure/closet/crate,
/obj/structure/closet/coffin,
- /obj/structure/closet/fireaxecabinet,
/obj/structure/closet/hydrant,
/obj/structure/closet/medical_wall,
/obj/structure/closet/statue,
diff --git a/code/game/objects/items/robobag.dm b/code/game/objects/items/robobag.dm
new file mode 100644
index 0000000000..f367f6a145
--- /dev/null
+++ b/code/game/objects/items/robobag.dm
@@ -0,0 +1,134 @@
+
+/obj/item/bodybag/cryobag/robobag
+ name = "synthmorph bag"
+ desc = "A reusable polymer bag designed to slow down synthetic functions such as data corruption and coolant flow, \
+ especially useful if short on time or in a hostile enviroment."
+ icon = 'icons/obj/robobag.dmi'
+ icon_state = "bodybag_folded"
+ item_state = "bodybag_cryo_folded"
+ origin_tech = list(TECH_ENGINEERING = 3)
+
+/obj/item/bodybag/cryobag/robobag/attack_self(mob/user)
+ var/obj/structure/closet/body_bag/cryobag/robobag/R = new /obj/structure/closet/body_bag/cryobag/robobag(user.loc)
+ R.add_fingerprint(user)
+ if(syringe)
+ R.syringe = syringe
+ syringe = null
+ qdel(src)
+
+/obj/structure/closet/body_bag/cryobag/robobag
+ name = "synthmorph bag"
+ desc = "A reusable polymer bag designed to slow down synthetic functions such as data corruption and coolant flow, \
+ especially useful if short on time or in a hostile enviroment."
+ icon = 'icons/obj/robobag.dmi'
+ item_path = /obj/item/bodybag/cryobag/robobag
+ tank_type = /obj/item/weapon/tank/stasis/nitro_cryo
+ stasis_level = 2 // Lower than the normal cryobag, because it's not made for meat that dies. It's made for robots and is freezing.
+ var/obj/item/clothing/accessory/badge/corptag // The tag on the bag.
+
+/obj/structure/closet/body_bag/cryobag/robobag/examine(mob/user)
+ ..()
+ if(Adjacent(user) && corptag)
+ to_chat(user, "\The [src] has a [corptag] attached to it.")
+
+/obj/structure/closet/body_bag/cryobag/robobag/update_icon()
+ overlays.Cut()
+ ..()
+ if(corptag)
+ var/corptag_icon_state = "tag_blank"
+ if(istype(corptag,/obj/item/clothing/accessory/badge/holo/detective) || istype(corptag,/obj/item/clothing/accessory/badge/holo/detective) || istype(corptag, /obj/item/clothing/accessory/badge/holo/hos) || istype(corptag, /obj/item/clothing/accessory/badge/old) || istype(corptag, /obj/item/clothing/accessory/badge/sheriff))
+ corptag_icon_state = "tag_badge_gold"
+ else if(istype(corptag, /obj/item/clothing/accessory/badge/holo/warden))
+ corptag_icon_state = "tag_badge_silver"
+ else if(istype(corptag, /obj/item/clothing/accessory/badge/holo))
+ corptag_icon_state = "tag_badge_blue"
+ else if(istype(corptag, /obj/item/clothing/accessory/badge/corporate_tag))
+ corptag_icon_state = corptag.icon_state
+
+ var/image/I = image(icon, corptag_icon_state)
+ overlays += I
+
+/obj/structure/closet/body_bag/cryobag/robobag/AltClick(mob/user)
+ if(!Adjacent(user))
+ ..()
+ if(corptag)
+ corptag.forceMove(get_turf(user))
+ to_chat(user, "You remove \the [corptag] from \the [src].")
+ corptag = null
+ update_icon()
+ return
+ return ..()
+
+/obj/structure/closet/body_bag/cryobag/robobag/Destroy()
+ if(corptag && get_turf(src))
+ var/turf/T = get_turf(src)
+ corptag.forceMove(T)
+ corptag = null
+ else
+ QDEL_NULL(corptag)
+ return ..()
+
+/obj/structure/closet/body_bag/cryobag/robobag/Entered(atom/movable/AM)
+ ..()
+ if(ishuman(AM))
+ var/mob/living/carbon/human/H = AM
+ if(H.isSynthetic())
+ if(H.getToxLoss() == 0) // We don't exactly care about the bag being 'used' when containing a synth, unless it's got work.
+ used = FALSE
+ else
+ H.add_modifier(/datum/modifier/fbp_debug/robobag)
+
+/obj/structure/closet/body_bag/cryobag/robobag/attackby(obj/item/W, mob/user)
+ if(opened)
+ ..()
+ else //Allows the bag to respond to a cyborg analyzer and tag.
+ if(istype(W,/obj/item/device/robotanalyzer))
+ var/obj/item/device/robotanalyzer/analyzer = W
+ for(var/mob/living/L in contents)
+ analyzer.attack(L,user)
+
+ else if(istype(W, /obj/item/clothing/accessory/badge))
+ if(corptag)
+ var/old_tag = corptag
+ corptag.forceMove(get_turf(src))
+ corptag = W
+ user.unEquip(corptag)
+ corptag.loc = null
+ to_chat(user, "You swap \the [old_tag] for \the [corptag].")
+ else
+ corptag = W
+ user.unEquip(corptag)
+ corptag.loc = null
+ to_chat(user, "You attach \the [corptag] to \the [src].")
+ update_icon()
+
+ else
+ ..()
+
+/datum/modifier/fbp_debug
+ name = "defragmenting"
+ desc = "Your software is being debugged."
+ mob_overlay_state = "signal_blue"
+
+ on_created_text = "You feel something pour over your senses."
+ on_expired_text = "Your mind is clear once more."
+ stacks = MODIFIER_STACK_FORBID
+
+/datum/modifier/fbp_debug/tick()
+ if(holder.getToxLoss())
+ holder.adjustToxLoss(rand(-1,-5))
+
+/datum/modifier/fbp_debug/can_apply(mob/living/L)
+ if(!L.isSynthetic())
+ return FALSE
+ return TRUE
+
+/datum/modifier/fbp_debug/check_if_valid()
+ ..()
+ if(!holder.getToxLoss())
+ src.expire()
+
+/datum/modifier/fbp_debug/robobag/check_if_valid()
+ ..()
+ if(!istype(holder.loc, /obj/structure/closet/body_bag/cryobag/robobag))
+ src.expire()
diff --git a/code/game/objects/items/robot/robot_parts.dm b/code/game/objects/items/robot/robot_parts.dm
index 7565b9c9ac..554663727f 100644
--- a/code/game/objects/items/robot/robot_parts.dm
+++ b/code/game/objects/items/robot/robot_parts.dm
@@ -168,28 +168,29 @@
if(!istype(loc,/turf))
to_chat(user, "You can't put \the [W] in, the frame has to be standing on the ground to be perfectly precise.")
return
- if(!M.brainmob)
- to_chat(user, "Sticking an empty [W] into the frame would sort of defeat the purpose.")
- return
- if(!M.brainmob.key)
- var/ghost_can_reenter = 0
- if(M.brainmob.mind)
- for(var/mob/observer/dead/G in player_list)
- if(G.can_reenter_corpse && G.mind == M.brainmob.mind)
- ghost_can_reenter = 1 //May come in use again at another point.
- to_chat(user, "\The [W] is completely unresponsive; though it may be able to auto-resuscitate.") //Jamming a ghosted brain into a borg is likely detrimental, and may result in some problems.
- return
- if(!ghost_can_reenter)
- to_chat(user, "\The [W] is completely unresponsive; there's no point.")
+ if(!istype(W, /obj/item/device/mmi/inert))
+ if(!M.brainmob)
+ to_chat(user, "Sticking an empty [W] into the frame would sort of defeat the purpose.")
+ return
+ if(!M.brainmob.key)
+ var/ghost_can_reenter = 0
+ if(M.brainmob.mind)
+ for(var/mob/observer/dead/G in player_list)
+ if(G.can_reenter_corpse && G.mind == M.brainmob.mind)
+ ghost_can_reenter = 1 //May come in use again at another point.
+ to_chat(user, "\The [W] is completely unresponsive; though it may be able to auto-resuscitate.") //Jamming a ghosted brain into a borg is likely detrimental, and may result in some problems.
+ return
+ if(!ghost_can_reenter)
+ to_chat(user, "\The [W] is completely unresponsive; there's no point.")
+ return
+
+ if(M.brainmob.stat == DEAD)
+ to_chat(user, "Sticking a dead [W] into the frame would sort of defeat the purpose.")
return
- if(M.brainmob.stat == DEAD)
- to_chat(user, "Sticking a dead [W] into the frame would sort of defeat the purpose.")
- return
-
- if(jobban_isbanned(M.brainmob, "Cyborg"))
- to_chat(user, "This [W] does not seem to fit.")
- return
+ if(jobban_isbanned(M.brainmob, "Cyborg"))
+ to_chat(user, "This [W] does not seem to fit.")
+ return
var/mob/living/silicon/robot/O = new /mob/living/silicon/robot(get_turf(loc), unfinished = 1)
if(!O) return
@@ -197,20 +198,18 @@
user.drop_item()
O.mmi = W
+ O.post_mmi_setup()
O.invisibility = 0
O.custom_name = created_name
O.updatename("Default")
- M.brainmob.mind.transfer_to(O)
-
- if(O.mind && O.mind.special_role)
- O.mind.store_memory("In case you look at this after being borged, the objectives are only here until I find a way to make them not show up for you, as I can't simply delete them without screwing up round-end reporting. --NeoFite")
-
+ if(M.brainmob)
+ M.brainmob.mind.transfer_to(O)
+ if(O.mind && O.mind.special_role)
+ O.mind.store_memory("In case you look at this after being borged, the objectives are only here until I find a way to make them not show up for you, as I can't simply delete them without screwing up round-end reporting. --NeoFite")
+ for(var/datum/language/L in M.brainmob.languages)
+ O.add_language(L.name)
O.job = "Cyborg"
-
- for(var/datum/language/L in M.brainmob.languages)
- O.add_language(L.name)
-
O.cell = chest.cell
O.cell.loc = O
W.loc = O//Should fix cybros run time erroring when blown up. It got deleted before, along with the frame.
diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm
index 93d0055129..fe1685ec70 100644
--- a/code/game/objects/items/robot/robot_upgrades.dm
+++ b/code/game/objects/items/robot/robot_upgrades.dm
@@ -25,17 +25,9 @@
require_module = 1
/obj/item/borg/upgrade/reset/action(var/mob/living/silicon/robot/R)
- if(..()) return 0
- R.uneq_all()
- R.modtype = initial(R.modtype)
- R.hands.icon_state = initial(R.hands.icon_state)
-
- R.notify_ai(ROBOT_NOTIFICATION_MODULE_RESET, R.module.name)
- R.module.Reset(R)
- qdel(R.module)
- R.module = null
- R.updatename("Default")
-
+ if(..())
+ return 0
+ R.module_reset()
return 1
/obj/item/borg/upgrade/rename
diff --git a/code/game/objects/items/weapons/circuitboards/frame.dm b/code/game/objects/items/weapons/circuitboards/frame.dm
index b22c4ca649..913506c9e6 100644
--- a/code/game/objects/items/weapons/circuitboards/frame.dm
+++ b/code/game/objects/items/weapons/circuitboards/frame.dm
@@ -197,6 +197,7 @@
board_type = new /datum/frame/frame_types/medical_pod
origin_tech = list(TECH_MAGNET = 2, TECH_BIO = 2)
req_components = list(
+ /obj/item/weapon/stock_parts/manipulator = 1,
/obj/item/weapon/stock_parts/scanning_module = 1,
/obj/item/weapon/reagent_containers/glass/beaker = 3,
/obj/item/weapon/reagent_containers/syringe = 3,
diff --git a/code/game/objects/items/weapons/circuitboards/mecha.dm b/code/game/objects/items/weapons/circuitboards/mecha.dm
index 1415edea84..fd351f8587 100644
--- a/code/game/objects/items/weapons/circuitboards/mecha.dm
+++ b/code/game/objects/items/weapons/circuitboards/mecha.dm
@@ -52,6 +52,23 @@
name = T_BOARD_MECHA("Durand central control")
icon_state = "mainboard"
+/obj/item/weapon/circuitboard/mecha/phazon
+
+/obj/item/weapon/circuitboard/mecha/phazon/peripherals
+ name = T_BOARD_MECHA("Phazon peripherals control")
+ icon_state = "mcontroller"
+ origin_tech = list(TECH_DATA = 6, TECH_ILLEGAL = 6)
+
+/obj/item/weapon/circuitboard/mecha/phazon/targeting
+ name = T_BOARD_MECHA("Phazon weapon control and targeting")
+ icon_state = "mcontroller"
+ origin_tech = list(TECH_DATA = 6, TECH_COMBAT = 7)
+
+/obj/item/weapon/circuitboard/mecha/phazon/main
+ name = T_BOARD_MECHA("Phazon central control")
+ origin_tech = list(TECH_DATA = 6, TECH_BLUESPACE = 5)
+ icon_state = "mainboard"
+
/obj/item/weapon/circuitboard/mecha/honker
origin_tech = list(TECH_DATA = 4)
@@ -79,5 +96,28 @@
name = T_BOARD_MECHA("Odysseus central control")
icon_state = "mainboard"
+/obj/item/weapon/circuitboard/mecha/imperion
+ name = "Alien Circuit"
+ origin_tech = list(TECH_DATA = 5, TECH_BLUESPACE = 3, TECH_PRECURSOR = 1)
+ icon = 'icons/obj/abductor.dmi'
+ icon_state = "circuit"
+
+/obj/item/weapon/circuitboard/mecha/imperion/main
+ desc = "It is marked with a strange glyph."
+
+/obj/item/weapon/circuitboard/mecha/imperion/peripherals
+ desc = "It is marked with a pulsing glyph."
+
+/obj/item/weapon/circuitboard/mecha/imperion/targeting
+ desc = "It is marked with an ominous glyph."
+
+/obj/item/weapon/circuitboard/mecha/imperion/phasing
+ desc = "It is marked with a disturbing glyph."
+
+/obj/item/weapon/circuitboard/mecha/imperion/damaged
+ name = "Damaged Alien Circuit"
+ desc = "It is marked with a constantly shifting glyph."
+ origin_tech = list(TECH_DATA = 3, TECH_BLUESPACE = 1, TECH_PRECURSOR = 2)
+
//Undef the macro, shouldn't be needed anywhere else
#undef T_BOARD_MECHA
diff --git a/code/game/objects/items/weapons/material/material_armor.dm b/code/game/objects/items/weapons/material/material_armor.dm
index d8fba58175..d981cafc07 100644
--- a/code/game/objects/items/weapons/material/material_armor.dm
+++ b/code/game/objects/items/weapons/material/material_armor.dm
@@ -105,6 +105,31 @@ Protectiveness | Armor %
if(!material) // No point checking for reflection.
return ..()
+ if(material.negation && prob(material.negation)) // Strange and Alien materials, or just really strong materials.
+ user.visible_message("\The [src] completely absorbs [attack_text]!")
+ return TRUE
+
+ if(material.spatial_instability && prob(material.spatial_instability))
+ user.visible_message("\The [src] flashes [user] clear of [attack_text]!")
+ var/list/turfs = new/list()
+ for(var/turf/T in orange(round(material.spatial_instability / 10) + 1, user))
+ if(istype(T,/turf/space)) continue
+ if(T.density) continue
+ if(T.x>world.maxx-6 || T.x<6) continue
+ if(T.y>world.maxy-6 || T.y<6) continue
+ turfs += T
+ if(!turfs.len) turfs += pick(/turf in orange(6))
+ var/turf/picked = pick(turfs)
+ if(!isturf(picked)) return
+
+ var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread()
+ spark_system.set_up(5, 0, user.loc)
+ spark_system.start()
+ playsound(user.loc, 'sound/effects/teleport.ogg', 50, 1)
+
+ user.loc = picked
+ return PROJECTILE_FORCE_MISS
+
if(material.reflectivity)
if(istype(damage_source, /obj/item/projectile/energy) || istype(damage_source, /obj/item/projectile/beam))
var/obj/item/projectile/P = damage_source
diff --git a/code/game/objects/items/weapons/melee/energy.dm b/code/game/objects/items/weapons/melee/energy.dm
index dae4a19d15..72fe26da36 100644
--- a/code/game/objects/items/weapons/melee/energy.dm
+++ b/code/game/objects/items/weapons/melee/energy.dm
@@ -134,7 +134,7 @@
/obj/item/weapon/melee/energy/sword/New()
if(random_color)
- blade_color = pick("red","blue","green","purple")
+ blade_color = pick("red","blue","green","purple","white")
lcolor = blade_color
/obj/item/weapon/melee/energy/sword/green/New()
@@ -153,6 +153,10 @@
blade_color = "purple"
lcolor = "#800080"
+/obj/item/weapon/melee/energy/sword/white/New()
+ blade_color = "white"
+ lcolor = "#FFFFFF"
+
/obj/item/weapon/melee/energy/sword/activate(mob/living/user)
if(!active)
to_chat(user, "\The [src] is now energised.")
@@ -161,7 +165,6 @@
attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
icon_state = "[active_state][blade_color]"
-
/obj/item/weapon/melee/energy/sword/deactivate(mob/living/user)
if(active)
to_chat(user, "\The [src] deactivates!")
@@ -237,6 +240,80 @@
target.taunt(user)
target.adjustFireLoss(force * 6) // 30 Burn, for 50 total.
+/*
+ * Charge blade. Uses a cell, and costs energy per strike.
+ */
+
+/obj/item/weapon/melee/energy/sword/charge
+ name = "charge sword"
+ desc = "A small, handheld device which emits a high-energy 'blade'."
+ origin_tech = list(TECH_COMBAT = 5, TECH_MAGNET = 3, TECH_ILLEGAL = 4)
+ active_force = 25
+ armor_penetration = 25
+
+ var/hitcost = 75
+ var/obj/item/weapon/cell/bcell = null
+ var/cell_type = /obj/item/weapon/cell/device
+
+/obj/item/weapon/melee/energy/sword/charge/proc/use_charge(var/cost)
+ if(active)
+ if(bcell)
+ if(bcell.checked_use(cost))
+ return 1
+ else
+ return 0
+ return null
+
+/obj/item/weapon/melee/energy/sword/charge/examine(mob/user)
+ if(!..(user, 1))
+ return
+
+ if(bcell)
+ to_chat(user, "The blade is [round(bcell.percent())]% charged.")
+ if(!bcell)
+ to_chat(user, "The blade does not have a power source installed.")
+
+/obj/item/weapon/melee/energy/sword/charge/attack_self(mob/user as mob)
+ if((!bcell || bcell.charge < hitcost) && !active)
+ to_chat(user, "\The [src] does not seem to have power.")
+ return
+ ..()
+
+/obj/item/weapon/melee/energy/sword/charge/attack(mob/M, mob/user)
+ if(active)
+ if(!use_charge(hitcost))
+ deactivate(user)
+ visible_message("\The [src]'s blade flickers, before retracting.")
+ return ..()
+
+/obj/item/weapon/melee/energy/sword/charge/attackby(obj/item/weapon/W, mob/user)
+ if(istype(W, cell_type))
+ if(!bcell)
+ user.drop_item()
+ W.loc = src
+ bcell = W
+ to_chat(user, "You install a cell in [src].")
+ update_icon()
+ else
+ to_chat(user, "[src] already has a cell.")
+ else if(W.is_screwdriver() && bcell)
+ bcell.update_icon()
+ bcell.forceMove(get_turf(loc))
+ bcell = null
+ to_chat(user, "You remove the cell from \the [src].")
+ deactivate()
+ update_icon()
+ return
+ else
+ ..()
+
+/obj/item/weapon/melee/energy/sword/charge/get_cell()
+ return bcell
+
+/obj/item/weapon/melee/energy/sword/charge/loaded/New()
+ ..()
+ bcell = new/obj/item/weapon/cell/device/weapon(src)
+
/*
*Energy Blade
*/
diff --git a/code/game/objects/items/weapons/policetape.dm b/code/game/objects/items/weapons/policetape.dm
index 78065ff69e..61162363ee 100644
--- a/code/game/objects/items/weapons/policetape.dm
+++ b/code/game/objects/items/weapons/policetape.dm
@@ -140,14 +140,14 @@ var/list/tape_roll_applications = list()
/obj/item/taperoll/attack_self(mob/user as mob)
if(!start)
start = get_turf(src)
- usr << "You place the first end of \the [src]."
+ to_chat(user, "You place the first end of \the [src].")
update_icon()
else
end = get_turf(src)
if(start.y != end.y && start.x != end.x || start.z != end.z)
start = null
update_icon()
- usr << "\The [src] can only be laid horizontally or vertically."
+ to_chat(user, "\The [src] can only be laid horizontally or vertically.")
return
if(start == end)
@@ -157,15 +157,18 @@ var/list/tape_roll_applications = list()
for(var/dir in cardinal)
T = get_step(start, dir)
if(T && T.density)
- possible_dirs += dir
+ possible_dirs |= dir
else
for(var/obj/structure/window/W in T)
if(W.is_fulltile() || W.dir == reverse_dir[dir])
- possible_dirs += dir
+ possible_dirs |= dir
+ for(var/obj/structure/window/window in start)
+ if(istype(window) && !window.is_fulltile())
+ possible_dirs |= window.dir
if(!possible_dirs)
start = null
update_icon()
- usr << "You can't place \the [src] here."
+ to_chat(user, "You can't place \the [src] here.")
return
if(possible_dirs & (NORTH|SOUTH))
var/obj/item/tape/TP = new tape_type(start)
@@ -181,7 +184,7 @@ var/list/tape_roll_applications = list()
TP.update_icon()
start = null
update_icon()
- usr << "You finish placing \the [src]."
+ to_chat(user, "You finish placing \the [src].")
return
var/turf/cur = start
@@ -199,6 +202,28 @@ var/list/tape_roll_applications = list()
can_place = 0
else
for(var/obj/O in cur)
+ if(istype(O, /obj/structure/window))
+ var/obj/structure/window/window = O
+ if(window.is_fulltile())
+ can_place = 0
+ break
+ if(cur == start)
+ if(window.dir == orientation)
+ can_place = 0
+ break
+ else
+ continue
+ else if(cur == end)
+ if(window.dir == reverse_dir[orientation])
+ can_place = 0
+ break
+ else
+ continue
+ else if (window.dir == reverse_dir[orientation] || window.dir == orientation)
+ can_place = 0
+ break
+ else
+ continue
if(O.density)
can_place = 0
break
@@ -208,7 +233,7 @@ var/list/tape_roll_applications = list()
if (!can_place)
start = null
update_icon()
- usr << "You can't run \the [src] through that!"
+ to_chat(user, "You can't run \the [src] through that!")
return
cur = start
@@ -224,6 +249,9 @@ var/list/tape_roll_applications = list()
for(var/obj/structure/window/W in T)
if(W.is_fulltile() || W.dir == orientation)
tape_dir = dir
+ for(var/obj/structure/window/window in cur)
+ if(istype(window) && !window.is_fulltile() && window.dir == reverse_dir[orientation])
+ tape_dir = dir
else if(cur == end)
var/turf/T = get_step(end, orientation)
if(T && !T.density)
@@ -231,6 +259,9 @@ var/list/tape_roll_applications = list()
for(var/obj/structure/window/W in T)
if(W.is_fulltile() || W.dir == reverse_dir[orientation])
tape_dir = dir
+ for(var/obj/structure/window/window in cur)
+ if(istype(window) && !window.is_fulltile() && window.dir == orientation)
+ tape_dir = dir
for(var/obj/item/tape/T in cur)
if((T.tape_dir == tape_dir) && (T.icon_base == icon_base))
tapetest = 1
@@ -246,7 +277,7 @@ var/list/tape_roll_applications = list()
cur = get_step_towards(cur,end)
start = null
update_icon()
- usr << "You finish placing \the [src]."
+ to_chat(user, "You finish placing \the [src].")
return
/obj/item/taperoll/afterattack(var/atom/A, mob/user as mob, proximity)
@@ -256,12 +287,12 @@ var/list/tape_roll_applications = list()
if (istype(A, /obj/machinery/door))
var/turf/T = get_turf(A)
if(locate(/obj/item/tape, A.loc))
- user << "There's already tape over that door!"
+ to_chat(user, "There's already tape over that door!")
else
var/obj/item/tape/P = new tape_type(T)
P.update_icon()
P.layer = WINDOW_LAYER
- user << "You finish placing \the [src]."
+ to_chat(user, "You finish placing \the [src].")
if (istype(A, /turf/simulated/floor) ||istype(A, /turf/unsimulated/floor))
var/turf/F = A
diff --git a/code/game/objects/items/weapons/storage/bags.dm b/code/game/objects/items/weapons/storage/bags.dm
index 9d1365290c..eea992ec41 100644
--- a/code/game/objects/items/weapons/storage/bags.dm
+++ b/code/game/objects/items/weapons/storage/bags.dm
@@ -118,6 +118,27 @@
to_chat(user, "You fill the [src].")
else if(!silent)
to_chat(user, "You fail to pick anything up with \the [src].")
+ if(istype(user.pulling, /obj/structure/ore_box/)) //Bit of a crappy way to do this, as it doubles spam for the user, but it works.
+ var/obj/structure/ore_box/O = user.pulling
+ O.attackby(src, user)
+
+/obj/item/weapon/storage/bag/ore/equipped(mob/user)
+ ..()
+ if(user.get_inventory_slot(src) == slot_wear_suit || slot_l_hand || slot_l_hand || slot_belt) //Basically every place they can go. Makes sure it doesn't unregister if moved to other slots.
+ GLOB.moved_event.register(user, src, /obj/item/weapon/storage/bag/ore/proc/autoload, user)
+
+/obj/item/weapon/storage/bag/ore/dropped(mob/user)
+ ..()
+ if(user.get_inventory_slot(src) == slot_wear_suit || slot_l_hand || slot_l_hand || slot_belt) //See above. This should really be a define.
+ GLOB.moved_event.register(user, src, /obj/item/weapon/storage/bag/ore/proc/autoload, user)
+ else
+ GLOB.moved_event.unregister(user, src)
+
+/obj/item/weapon/storage/bag/ore/proc/autoload(mob/user)
+ var/obj/item/weapon/ore/O = locate() in get_turf(src)
+ if(O)
+ gather_all(get_turf(src), user)
+
/obj/item/weapon/storage/bag/ore/examine(mob/user)
..()
diff --git a/code/game/objects/items/weapons/tanks/tank_types.dm b/code/game/objects/items/weapons/tanks/tank_types.dm
index 6308759044..9e984ba05f 100644
--- a/code/game/objects/items/weapons/tanks/tank_types.dm
+++ b/code/game/objects/items/weapons/tanks/tank_types.dm
@@ -218,3 +218,15 @@
if(..(user, 0) && air_contents.gas["nitrogen"] < 10)
to_chat(user, text("The meter on \the [src] indicates you are almost out of nitrogen!"))
//playsound(user, 'sound/effects/alert.ogg', 50, 1)
+
+/obj/item/weapon/tank/stasis/nitro_cryo // Synthmorph bags need to have initial pressure within safe bounds for human atmospheric pressure, but low temperature to stop unwanted degredation.
+ name = "stasis cryogenic nitrogen tank"
+ desc = "Cryogenic Nitrogen tank included in most synthmorph bag designs."
+ icon_state = "emergency_double_nitro"
+ gauge_icon = "indicator_emergency_double"
+ volume = 10
+
+/obj/item/weapon/tank/stasis/nitro_cryo/Initialize()
+ ..()
+ src.air_contents.adjust_gas_temp("nitrogen", (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*TN60C), TN60C)
+ return
diff --git a/code/game/objects/items/weapons/tools/crowbar.dm b/code/game/objects/items/weapons/tools/crowbar.dm
index 477815e62f..73b62f0741 100644
--- a/code/game/objects/items/weapons/tools/crowbar.dm
+++ b/code/game/objects/items/weapons/tools/crowbar.dm
@@ -51,6 +51,24 @@
toolspeed = 0.1
origin_tech = list(TECH_COMBAT = 4, TECH_ENGINEERING = 4)
+/obj/item/weapon/tool/crowbar/hybrid
+ name = "strange crowbar"
+ desc = "A crowbar whose head seems to phase in and out of view."
+ catalogue_data = list(/datum/category_item/catalogue/anomalous/precursor_a/alien_crowbar)
+ icon_state = "hybcrowbar"
+ usesound = 'sound/weapons/sonic_jackhammer.ogg'
+ toolspeed = 0.4
+ origin_tech = list(TECH_COMBAT = 4, TECH_ENGINEERING = 3)
+ reach = 2
+
+/obj/item/weapon/tool/crowbar/hybrid/is_crowbar()
+ if(prob(10))
+ var/turf/T = get_turf(src)
+ radiation_repository.radiate(get_turf(src), 5)
+ T.visible_message("\The [src] shudders!")
+ return FALSE
+ return TRUE
+
/obj/item/weapon/tool/crowbar/cyborg
name = "hydraulic crowbar"
desc = "A hydraulic prying tool, compact but powerful. Designed to replace crowbars in industrial synthetics."
diff --git a/code/game/objects/items/weapons/tools/screwdriver.dm b/code/game/objects/items/weapons/tools/screwdriver.dm
index cb6a7f7c90..a9bdd6cee8 100644
--- a/code/game/objects/items/weapons/tools/screwdriver.dm
+++ b/code/game/objects/items/weapons/tools/screwdriver.dm
@@ -92,6 +92,27 @@
toolspeed = 0.1
random_color = FALSE
+/obj/item/weapon/tool/screwdriver/hybrid
+ name = "strange screwdriver"
+ desc = "A strange conglomerate of a screwdriver."
+ icon_state = "hybscrewdriver"
+ item_state = "screwdriver_black"
+ origin_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 3)
+ slowdown = 0.1
+ w_class = ITEMSIZE_NORMAL
+ usesound = 'sound/effects/uncloak.ogg'
+ toolspeed = 0.4
+ random_color = FALSE
+ reach = 2
+
+/obj/item/weapon/tool/screwdriver/hybrid/is_screwdriver()
+ if(prob(10))
+ var/turf/T = get_turf(src)
+ radiation_repository.radiate(get_turf(src), 5)
+ T.visible_message("\The [src] shudders!")
+ return FALSE
+ return TRUE
+
/obj/item/weapon/tool/screwdriver/cyborg
name = "powered screwdriver"
desc = "An electrical screwdriver, designed to be both precise and quick."
diff --git a/code/game/objects/items/weapons/tools/weldingtool.dm b/code/game/objects/items/weapons/tools/weldingtool.dm
index a47e78bd57..bf3257bc69 100644
--- a/code/game/objects/items/weapons/tools/weldingtool.dm
+++ b/code/game/objects/items/weapons/tools/weldingtool.dm
@@ -121,15 +121,14 @@
remove_fuel(1)
if(get_fuel() < 1)
setWelding(0)
- //I'm not sure what this does. I assume it has to do with starting fires...
- //...but it doesnt check to see if the welder is on or not.
- var/turf/location = src.loc
- if(istype(location, /mob/living))
- var/mob/living/M = location
- if(M.item_is_in_hands(src))
- location = get_turf(M)
- if (istype(location, /turf))
- location.hotspot_expose(700, 5)
+ else //Only start fires when its on and has enough fuel to actually keep working
+ var/turf/location = src.loc
+ if(istype(location, /mob/living))
+ var/mob/living/M = location
+ if(M.item_is_in_hands(src))
+ location = get_turf(M)
+ if (istype(location, /turf))
+ location.hotspot_expose(700, 5)
/obj/item/weapon/weldingtool/afterattack(obj/O as obj, mob/user as mob, proximity)
if(!proximity) return
@@ -436,6 +435,19 @@
nextrefueltick = world.time + 10
reagents.add_reagent("fuel", 1)
+/obj/item/weapon/weldingtool/experimental/hybrid
+ name = "strange welding tool"
+ desc = "An experimental welder capable of synthesizing its own fuel from spatial waveforms. It's like welding with a star!"
+ icon_state = "hybwelder"
+ max_fuel = 20
+ eye_safety_modifier = -2 // Brighter than the sun. Literally, you can look at the sun with a welding mask of proper grade, this will burn through that.
+ slowdown = 0.1
+ toolspeed = 0.25
+ w_class = ITEMSIZE_LARGE
+ flame_intensity = 5
+ origin_tech = list(TECH_ENGINEERING = 5, TECH_PHORON = 4, TECH_PRECURSOR = 1)
+ reach = 2
+
/*
* Backpack Welder.
*/
diff --git a/code/game/objects/items/weapons/tools/wirecutters.dm b/code/game/objects/items/weapons/tools/wirecutters.dm
index f53225ab41..181c786c4c 100644
--- a/code/game/objects/items/weapons/tools/wirecutters.dm
+++ b/code/game/objects/items/weapons/tools/wirecutters.dm
@@ -73,6 +73,26 @@
usesound = 'sound/items/jaws_cut.ogg'
toolspeed = 0.5
+/obj/item/weapon/tool/wirecutters/hybrid
+ name = "strange wirecutters"
+ desc = "This cuts wires. With Science!"
+ icon_state = "hybcutters"
+ w_class = ITEMSIZE_NORMAL
+ slowdown = 0.1
+ origin_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 3, TECH_PHORON = 2)
+ attack_verb = list("pinched", "nipped", "warped", "blasted")
+ usesound = 'sound/effects/stealthoff.ogg'
+ toolspeed = 0.4
+ reach = 2
+
+/obj/item/weapon/tool/wirecutters/hybrid/is_wirecutter()
+ if(prob(10))
+ var/turf/T = get_turf(src)
+ radiation_repository.radiate(get_turf(src), 5)
+ T.visible_message("\The [src] shudders!")
+ return FALSE
+ return TRUE
+
/obj/item/weapon/tool/wirecutters/power
name = "jaws of life"
desc = "A set of jaws of life, compressed through the magic of science. It's fitted with a cutting head."
diff --git a/code/game/objects/items/weapons/tools/wrench.dm b/code/game/objects/items/weapons/tools/wrench.dm
index 7b1ab6a07c..652e32cf75 100644
--- a/code/game/objects/items/weapons/tools/wrench.dm
+++ b/code/game/objects/items/weapons/tools/wrench.dm
@@ -25,6 +25,29 @@
usesound = 'sound/items/drill_use.ogg'
toolspeed = 0.5
+/obj/item/weapon/tool/wrench/hybrid // Slower and bulkier than normal power tools, but it has the power of reach.
+ name = "strange wrench"
+ desc = "A wrench with many common uses. Can be usually found in your hand."
+ icon = 'icons/obj/tools.dmi'
+ icon_state = "hybwrench"
+ slot_flags = SLOT_BELT
+ force = 8
+ throwforce = 10
+ w_class = ITEMSIZE_NORMAL
+ slowdown = 0.1
+ origin_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 3, TECH_PHORON = 2)
+ attack_verb = list("bashed", "battered", "bludgeoned", "whacked", "warped", "blasted")
+ usesound = 'sound/effects/stealthoff.ogg'
+ toolspeed = 0.5
+ reach = 2
+
+/obj/item/weapon/tool/wrench/hybrid/is_wrench()
+ if(prob(10))
+ var/turf/T = get_turf(src)
+ radiation_repository.radiate(get_turf(src), 5)
+ T.visible_message("\The [src] shudders!")
+ return FALSE
+ return TRUE
/datum/category_item/catalogue/anomalous/precursor_a/alien_wrench
name = "Precursor Alpha Object - Fastener Torque Tool"
diff --git a/code/game/objects/random/misc.dm b/code/game/objects/random/misc.dm
index 5b8fc35e3c..0f9f85582f 100644
--- a/code/game/objects/random/misc.dm
+++ b/code/game/objects/random/misc.dm
@@ -293,6 +293,23 @@
/obj/item/weapon/reagent_containers/food/drinks/bottle/rum,
/obj/item/weapon/reagent_containers/food/drinks/bottle/patron)
+/obj/random/meat
+ name = "random meat"
+ desc = "This is a random slab of meat."
+ icon = 'icons/obj/food.dmi'
+ icon_state = "meat"
+
+/obj/random/meat/item_to_spawn()
+ return pick(prob(60);/obj/item/weapon/reagent_containers/food/snacks/meat,
+ prob(20);/obj/item/weapon/reagent_containers/food/snacks/xenomeat/spidermeat,
+ prob(10);/obj/item/weapon/reagent_containers/food/snacks/carpmeat,
+ prob(5);/obj/item/weapon/reagent_containers/food/snacks/bearmeat,
+ prob(1);/obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh,
+ prob(1);/obj/item/weapon/reagent_containers/food/snacks/meat/human,
+ prob(1);/obj/item/weapon/reagent_containers/food/snacks/meat/monkey,
+ prob(1);/obj/item/weapon/reagent_containers/food/snacks/meat/corgi,
+ prob(1);/obj/item/weapon/reagent_containers/food/snacks/xenomeat)
+
/obj/random/material //Random materials for building stuff
name = "random material"
desc = "This is a random material."
diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm
index 962f7956c4..25d5979002 100644
--- a/code/game/objects/structures/crates_lockers/closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets.dm
@@ -33,6 +33,11 @@
var/list/starts_with
/obj/structure/closet/Initialize()
+ ..()
+ // Closets need to come later because of spawners potentially creating objects during init.
+ return INITIALIZE_HINT_LATELOAD
+
+/obj/structure/closet/LateInitialize()
. = ..()
if(starts_with)
create_objects_in_loc(src, starts_with)
diff --git a/code/game/objects/structures/crates_lockers/closets/fireaxe.dm b/code/game/objects/structures/crates_lockers/closets/fireaxe.dm
deleted file mode 100644
index 2621da2253..0000000000
--- a/code/game/objects/structures/crates_lockers/closets/fireaxe.dm
+++ /dev/null
@@ -1,214 +0,0 @@
-//I still dont think this should be a closet but whatever
-/obj/structure/closet/fireaxecabinet
- name = "fire axe cabinet"
- desc = "There is small label that reads \"For Emergency use only\" along with details for safe use of the axe. As if."
- var/obj/item/weapon/material/twohanded/fireaxe/fireaxe
- icon_state = "fireaxe1000"
- icon_closed = "fireaxe1000"
- icon_opened = "fireaxe1100"
- anchored = 1
- density = 0
- var/localopened = 0 //Setting this to keep it from behaviouring like a normal closet and obstructing movement in the map. -Agouri
- opened = 1
- var/hitstaken = 0
- var/locked = 1
- var/smashed = 0
-
- starts_with = list(/obj/item/weapon/material/twohanded/fireaxe)
-
-/obj/structure/closet/fireaxecabinet/Initialize()
- ..()
- fireaxe = locate() in contents
-
-/obj/structure/closet/fireaxecabinet/attackby(var/obj/item/O as obj, var/mob/user as mob) //Marker -Agouri
- //..() //That's very useful, Erro
-
- // This could stand to be put further in, made better, etc. but fuck you. Fuck whoever
- // wrote this code. Fuck everything about this object. I hope you step on a Lego.
- user.setClickCooldown(10)
- // Seriously why the fuck is this even a closet aghasjdhasd I hate you
-
- var/hasaxe = 0 //gonna come in handy later~ // FUCK YOUR TILDES.
- if(fireaxe)
- hasaxe = 1
-
- if (isrobot(usr) || src.locked)
- if(istype(O, /obj/item/device/multitool))
- user << "Resetting circuitry..."
- playsound(user, 'sound/machines/lockreset.ogg', 50, 1)
- if(do_after(user, 20 * O.toolspeed))
- src.locked = 0
- to_chat(user, " You disable the locking modules.")
- update_icon()
- return
- else if(istype(O, /obj/item/weapon))
- var/obj/item/weapon/W = O
- if(src.smashed || src.localopened)
- if(localopened)
- localopened = 0
- icon_state = text("fireaxe[][][][]closing",hasaxe,src.localopened,src.hitstaken,src.smashed)
- spawn(10) update_icon()
- return
- else
- playsound(user, 'sound/effects/Glasshit.ogg', 100, 1) //We don't want this playing every time
- if(W.force < 15)
- to_chat(user, "The cabinet's protective glass glances off the hit.")
- else
- src.hitstaken++
- if(src.hitstaken == 4)
- playsound(user, 'sound/effects/Glassbr3.ogg', 100, 1) //Break cabinet, receive goodies. Cabinet's fucked for life after that.
- src.smashed = 1
- src.locked = 0
- src.localopened = 1
- update_icon()
- return
- if (istype(O, /obj/item/weapon/material/twohanded/fireaxe) && src.localopened)
- if(!fireaxe)
- if(O:wielded)
- O:wielded = 0
- O.update_icon()
- //to_chat(user, "Unwield the axe first.")
- //return
- fireaxe = O
- user.remove_from_mob(O)
- src.contents += O
- to_chat(user, "You place the fire axe back in the [src.name].")
- update_icon()
- else
- if(src.smashed)
- return
- else
- localopened = !localopened
- if(localopened)
- icon_state = text("fireaxe[][][][]opening",hasaxe,src.localopened,src.hitstaken,src.smashed)
- spawn(10) update_icon()
- else
- icon_state = text("fireaxe[][][][]closing",hasaxe,src.localopened,src.hitstaken,src.smashed)
- spawn(10) update_icon()
- else
- if(src.smashed)
- return
- if(istype(O, /obj/item/device/multitool))
- if(localopened)
- localopened = 0
- icon_state = text("fireaxe[][][][]closing",hasaxe,src.localopened,src.hitstaken,src.smashed)
- spawn(10) update_icon()
- return
- else
- to_chat(user, "Resetting circuitry...")
- playsound(user, 'sound/machines/lockenable.ogg', 50, 1)
- if(do_after(user,20 * O.toolspeed))
- src.locked = 1
- to_chat(user, " You re-enable the locking modules.")
- return
- else
- localopened = !localopened
- if(localopened)
- icon_state = text("fireaxe[][][][]opening",hasaxe,src.localopened,src.hitstaken,src.smashed)
- spawn(10) update_icon()
- else
- icon_state = text("fireaxe[][][][]closing",hasaxe,src.localopened,src.hitstaken,src.smashed)
- spawn(10) update_icon()
-
-
-/obj/structure/closet/fireaxecabinet/attack_hand(mob/user as mob)
- var/hasaxe = 0
- if(fireaxe)
- hasaxe = 1
-
- if(src.locked)
- to_chat(user, "The cabinet won't budge!")
- return
-
- if(localopened)
- if(fireaxe)
- user.put_in_hands(fireaxe)
- fireaxe = null
- to_chat (user, "You take the fire axe from the [name].")
- src.add_fingerprint(user)
- update_icon()
- else
- if(src.smashed)
- return
- else
- localopened = !localopened
- if(localopened)
- src.icon_state = text("fireaxe[][][][]opening",hasaxe,src.localopened,src.hitstaken,src.smashed)
- spawn(10) update_icon()
- else
- src.icon_state = text("fireaxe[][][][]closing",hasaxe,src.localopened,src.hitstaken,src.smashed)
- spawn(10) update_icon()
-
- else
- localopened = !localopened //I'm pretty sure we don't need an if(src.smashed) in here. In case I'm wrong and it fucks up teh cabinet, **MARKER**. -Agouri
- if(localopened)
- src.icon_state = text("fireaxe[][][][]opening",hasaxe,src.localopened,src.hitstaken,src.smashed)
- spawn(10) update_icon()
- else
- src.icon_state = text("fireaxe[][][][]closing",hasaxe,src.localopened,src.hitstaken,src.smashed)
- spawn(10) update_icon()
-
-/obj/structure/closet/fireaxecabinet/attack_tk(mob/user as mob)
- if(localopened && fireaxe)
- fireaxe.forceMove(loc)
- to_chat(user, "You telekinetically remove the fire axe.")
- fireaxe = null
- update_icon()
- return
- attack_hand(user)
-
-/obj/structure/closet/fireaxecabinet/verb/toggle_openness() //nice name, huh? HUH?! -Erro //YEAH -Agouri
- set name = "Open/Close"
- set category = "Object"
-
- if (isrobot(usr) || src.locked || src.smashed)
- if(src.locked)
- to_chat(usr, "The cabinet won't budge!")
- else if(src.smashed)
- to_chat(usr, "The protective glass is broken!")
- return
-
- localopened = !localopened
- update_icon()
-
-/obj/structure/closet/fireaxecabinet/verb/remove_fire_axe()
- set name = "Remove Fire Axe"
- set category = "Object"
-
- if (isrobot(usr))
- return
-
- if (localopened)
- if(fireaxe)
- usr.put_in_hands(fireaxe)
- fireaxe = null
- to_chat(usr, "You take the Fire axe from the [name].")
- else
- to_chat(usr, "The [src.name] is empty.")
- else
- to_chat(usr, "The [src.name] is closed.")
- update_icon()
-
-/obj/structure/closet/fireaxecabinet/attack_ai(mob/user as mob)
- if(src.smashed)
- to_chat(user, "The security of the cabinet is compromised.")
- return
- else
- locked = !locked
- if(locked)
- to_chat(user, "Cabinet locked.")
- else
- to_chat(user, "Cabinet unlocked.")
- return
-
-/obj/structure/closet/fireaxecabinet/update_icon() //Template: fireaxe[has fireaxe][is opened][hits taken][is smashed]. If you want the opening or closing animations, add "opening" or "closing" right after the numbers
- var/hasaxe = 0
- if(fireaxe)
- hasaxe = 1
- icon_state = text("fireaxe[][][][]",hasaxe,src.localopened,src.hitstaken,src.smashed)
-
-/obj/structure/closet/fireaxecabinet/open()
- return
-
-/obj/structure/closet/fireaxecabinet/close()
- return
diff --git a/code/game/objects/structures/fireaxe.dm b/code/game/objects/structures/fireaxe.dm
new file mode 100644
index 0000000000..e5735d1255
--- /dev/null
+++ b/code/game/objects/structures/fireaxe.dm
@@ -0,0 +1,185 @@
+//I still dont think this should be a closet but whatever
+/obj/structure/fireaxecabinet
+ name = "fire axe cabinet"
+ desc = "There is small label that reads \"For Emergency use only\" along with details for safe use of the axe. As if."
+ var/obj/item/weapon/material/twohanded/fireaxe/fireaxe
+ icon = 'icons/obj/closet.dmi' //Not bothering to move icons out for now. But its dumb still.
+ icon_state = "fireaxe1000"
+ anchored = 1
+ density = 0
+ var/open = 0
+ var/hitstaken = 0
+ var/locked = 1
+ var/smashed = 0
+
+/obj/structure/fireaxecabinet/Initialize()
+ ..()
+ fireaxe = new /obj/item/weapon/material/twohanded/fireaxe()
+
+/obj/structure/fireaxecabinet/attackby(var/obj/item/O as obj, var/mob/user as mob) //Marker -Agouri
+ //..() //That's very useful, Erro
+
+ // This could stand to be put further in, made better, etc. but fuck you. Fuck whoever
+ // wrote this code. Fuck everything about this object. I hope you step on a Lego.
+ user.setClickCooldown(10)
+ // Seriously why the fuck is this even a closet aghasjdhasd I hate you
+
+ //var/hasaxe = 0 //gonna come in handy later~ // FUCK YOUR TILDES.
+ //if(fireaxe)
+ // hasaxe = 1
+
+ if (isrobot(user) || locked)
+ if(istype(O, /obj/item/device/multitool))
+ to_chat(user, "Resetting circuitry...")
+ playsound(user, 'sound/machines/lockreset.ogg', 50, 1)
+ if(do_after(user, 20 * O.toolspeed))
+ locked = 0
+ to_chat(user, " You disable the locking modules.")
+ update_icon()
+ return
+ else if(istype(O, /obj/item/weapon))
+ var/obj/item/weapon/W = O
+ if(smashed || open)
+ if(open)
+ toggle_close_open()
+ return
+ else
+ playsound(user, 'sound/effects/Glasshit.ogg', 100, 1) //We don't want this playing every time
+ if(W.force < 15)
+ to_chat(user, "The cabinet's protective glass glances off the hit.")
+ else
+ hitstaken++
+ if(hitstaken == 4)
+ playsound(user, 'sound/effects/Glassbr3.ogg', 100, 1) //Break cabinet, receive goodies. Cabinet's fucked for life after that.
+ smashed = 1
+ locked = 0
+ open= 1
+ update_icon()
+ return
+ if (istype(O, /obj/item/weapon/material/twohanded/fireaxe) && open)
+ if(!fireaxe)
+ if(O:wielded)
+ O:wielded = 0
+ O.update_icon()
+ fireaxe = O
+ user.remove_from_mob(O)
+ contents += O
+ to_chat(user, "You place the fire axe back in the [name].")
+ update_icon()
+ else
+ if(smashed)
+ return
+ else
+ toggle_close_open()
+ else
+ if(smashed)
+ return
+ if(istype(O, /obj/item/device/multitool))
+ if(open)
+ open = 0
+ update_icon()
+ flick("[icon_state]closing", src)
+ return
+ else
+ to_chat(user, "Resetting circuitry...")
+ playsound(user, 'sound/machines/lockenable.ogg', 50, 1)
+ if(do_after(user,20 * O.toolspeed))
+ locked = 1
+ to_chat(user, " You re-enable the locking modules.")
+ return
+ else
+ toggle_close_open()
+
+
+/obj/structure/fireaxecabinet/attack_hand(mob/user as mob)
+ //var/hasaxe = 0 //Fuck this. Fuck everything about this. Who wrote this. Why.
+ //if(fireaxe)
+ // hasaxe = 1
+
+ if(locked)
+ to_chat(user, "The cabinet won't budge!")
+ return
+
+ if(open)
+ if(fireaxe)
+ user.put_in_hands(fireaxe)
+ fireaxe = null
+ to_chat (user, "You take the fire axe from the [name].")
+ add_fingerprint(user)
+ update_icon()
+ else
+ if(smashed)
+ return
+ else
+ toggle_close_open()
+
+ else
+ toggle_close_open()
+
+/obj/structure/fireaxecabinet/attack_tk(mob/user as mob)
+ if(open && fireaxe)
+ fireaxe.forceMove(loc)
+ to_chat(user, "You telekinetically remove the fire axe.")
+ fireaxe = null
+ update_icon()
+ return
+ attack_hand(user)
+
+/obj/structure/fireaxecabinet/proc/toggle_close_open()
+ open = !open
+ if(open)
+ update_icon()
+ flick("[icon_state]opening", src)
+ else
+ update_icon()
+ flick("[icon_state]closing", src)
+
+/obj/structure/fireaxecabinet/verb/toggle_openness() //nice name, huh? HUH?! -Erro //YEAH -Agouri
+ set name = "Open/Close"
+ set category = "Object"
+
+ if (isrobot(usr) || locked || smashed)
+ if(locked)
+ to_chat(usr, "The cabinet won't budge!")
+ else if(smashed)
+ to_chat(usr, "The protective glass is broken!")
+ return
+
+ toggle_close_open()
+ update_icon()
+
+/obj/structure/fireaxecabinet/verb/remove_fire_axe()
+ set name = "Remove Fire Axe"
+ set category = "Object"
+
+ if (isrobot(usr))
+ return
+
+ if (open)
+ if(fireaxe)
+ usr.put_in_hands(fireaxe)
+ fireaxe = null
+ to_chat(usr, "You take the Fire axe from the [name].")
+ else
+ to_chat(usr, "The [name] is empty.")
+ else
+ to_chat(usr, "The [name] is closed.")
+ update_icon()
+
+/obj/structure/fireaxecabinet/attack_ai(mob/user as mob)
+ if(smashed)
+ to_chat(user, "The security of the cabinet is compromised.")
+ return
+ else
+ locked = !locked
+ if(locked)
+ to_chat(user, "Cabinet locked.")
+ else
+ to_chat(user, "Cabinet unlocked.")
+ return
+
+/obj/structure/fireaxecabinet/update_icon() //Template: fireaxe[has fireaxe][is opened][hits taken][is smashed]. If you want the opening or closing animations, add "opening" or "closing" right after the numbers
+ var/hasaxe = 0
+ if(fireaxe)
+ hasaxe = 1
+ icon_state = text("fireaxe[][][][]",hasaxe,open,hitstaken,smashed)
diff --git a/code/game/objects/structures/props/alien_props.dm b/code/game/objects/structures/props/alien_props.dm
index 1eb811838e..38f0b1b893 100644
--- a/code/game/objects/structures/props/alien_props.dm
+++ b/code/game/objects/structures/props/alien_props.dm
@@ -114,4 +114,12 @@
for(var/i = 1 to rand(1, 4))
var/new_tech = pick(techs)
techs -= new_tech
- origin_tech[new_tech] = rand(5, 9)
\ No newline at end of file
+ origin_tech[new_tech] = rand(5, 9)
+
+ origin_tech[TECH_PRECURSOR] = rand(0,2)
+
+/obj/item/prop/alien/phasecoil
+ name = "reverberating device"
+ desc = "A device pulsing with an ominous energy."
+ icon_state = "circuit_phase"
+ origin_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 6, TECH_PHORON = 3, TECH_POWER = 5, TECH_MAGNET = 5, TECH_DATA = 5, TECH_PRECURSOR = 2, TECH_ARCANE = 1)
diff --git a/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm b/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm
index fc0b02ebbe..e5eba3f20a 100644
--- a/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm
+++ b/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm
@@ -5,7 +5,6 @@
anchored = 0
buckle_movable = 1
- var/move_delay = null
var/driving = 0
var/mob/living/pulling = null
var/bloodiness
@@ -30,13 +29,6 @@
/obj/structure/bed/chair/wheelchair/relaymove(mob/user, direction)
// Redundant check?
-
- var/calculated_move_delay
- calculated_move_delay += 2 //TheFurryFeline: nerfs speed so you don't go like Sonic. >W>
-
- if(world.time < move_delay)
- return
-
if(user.stat || user.stunned || user.weakened || user.paralysis || user.lying || user.restrained())
if(user==pulling)
pulling = null
@@ -65,11 +57,6 @@
user << "You cannot drive while being pushed."
return
-
- move_delay = world.time
- move_delay += calculated_move_delay
-
-
// Let's roll
driving = 1
var/turf/T = null
diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm
index 444ca1ce35..e1eba7ece7 100644
--- a/code/game/objects/structures/window.dm
+++ b/code/game/objects/structures/window.dm
@@ -83,8 +83,9 @@
/obj/structure/window/proc/updateSilicate()
if (overlays)
overlays.Cut()
+ update_icon()
- var/image/img = image(src.icon, src.icon_state)
+ var/image/img = image(src)
img.color = "#ffffff"
img.alpha = silicate * 255 / 100
overlays += img
@@ -266,7 +267,7 @@
state = 3 - state
update_nearby_icons()
playsound(src, W.usesound, 75, 1)
- to_chat(user, "You have [state ? "un" : ""]fastened the window [state ? "from" : "to"] the frame.")
+ to_chat(user, "You have [state == 1 ? "un" : ""]fastened the window [state ? "from" : "to"] the frame.")
else if(reinf && state == 0)
anchored = !anchored
update_nearby_icons()
@@ -484,6 +485,7 @@
force_threshold = 3
/obj/structure/window/basic/full
+ icon_state = "window-full"
maxhealth = 24
fulltile = TRUE
@@ -500,6 +502,7 @@
force_threshold = 5
/obj/structure/window/phoronbasic/full
+ icon_state = "phoronwindow-full"
maxhealth = 80
fulltile = TRUE
@@ -517,6 +520,7 @@
force_threshold = 10
/obj/structure/window/phoronreinforced/full
+ icon_state = "phoronrwindow-full"
maxhealth = 160
fulltile = TRUE
@@ -533,7 +537,7 @@
force_threshold = 6
/obj/structure/window/reinforced/full
- icon_state = "fwindow"
+ icon_state = "rwindow-full"
maxhealth = 80
fulltile = TRUE
@@ -570,7 +574,7 @@
var/id
/obj/structure/window/reinforced/polarized/full
- icon_state = "fwindow"
+ icon_state = "rwindow-full"
maxhealth = 80
fulltile = TRUE
diff --git a/code/game/world.dm b/code/game/world.dm
index 4bcc4c8f74..da14592a2b 100644
--- a/code/game/world.dm
+++ b/code/game/world.dm
@@ -181,7 +181,9 @@ var/world_topic_spam_protect_time = world.timeofday
positions["bot"] = list()
positions["bot"][ai.name] = "Artificial Intelligence"
for(var/mob/living/silicon/robot/robot in mob_list)
- // No combat/syndicate cyborgs, no drones.
+ // No combat/syndicate cyborgs, no drones, and no AI shells.
+ if(robot.shell)
+ continue
if(robot.module && robot.module.hide_on_manifest)
continue
if(!positions["bot"])
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index ac7958d6bd..e06d08070c 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -790,13 +790,13 @@
if (ismob(M))
if(!check_if_greater_rights_than(M.client))
return
- var/reason = sanitize(input("Please enter reason"))
+ var/reason = sanitize(input("Please enter reason.") as null|message)
if(!reason)
- M << "You have been kicked from the server"
- else
- M << "You have been kicked from the server: [reason]"
- log_admin("[key_name(usr)] booted [key_name(M)].")
- message_admins("[key_name_admin(usr)] booted [key_name_admin(M)].", 1)
+ return
+
+ to_chat(M, span("critical", "You have been kicked from the server: [reason]"))
+ log_admin("[key_name(usr)] booted [key_name(M)] for reason: '[reason]'.")
+ message_admins("[key_name_admin(usr)] booted [key_name_admin(M)] for reason '[reason]'.", 1)
//M.client = null
qdel(M.client)
diff --git a/code/modules/assembly/signaler.dm b/code/modules/assembly/signaler.dm
index d7f5cbe6c9..a8859e7c1c 100644
--- a/code/modules/assembly/signaler.dm
+++ b/code/modules/assembly/signaler.dm
@@ -104,7 +104,7 @@ Code:
var/obj/item/device/assembly/signaler/signaler2 = W
if(secured && signaler2.secured)
code = signaler2.code
- frequency = signaler2.frequency
+ set_frequency(signaler2.frequency)
to_chat(user, "You transfer the frequency and code of [signaler2] to [src].")
else
..()
diff --git a/code/modules/catalogue/cataloguer.dm b/code/modules/catalogue/cataloguer.dm
index ba518b1f3d..60e86eadee 100644
--- a/code/modules/catalogue/cataloguer.dm
+++ b/code/modules/catalogue/cataloguer.dm
@@ -149,12 +149,14 @@ GLOBAL_LIST_EMPTY(all_cataloguers)
var/list/contributers = list()
var/list/contributer_names = list()
for(var/thing in player_list)
- var/mob/M = thing
- if(M == user)
+ var/mob/living/L = thing
+ if(L == user)
continue
- if(get_dist(M, user) <= credit_sharing_range)
- contributers += M
- contributer_names += M.name
+ if(!istype(L))
+ continue
+ if(get_dist(L, user) <= credit_sharing_range)
+ contributers += L
+ contributer_names += L.name
var/points_gained = 0
diff --git a/code/modules/client/preference_setup/loadout/loadout_suit.dm b/code/modules/client/preference_setup/loadout/loadout_suit.dm
index 2dbdcde1d3..e2a661f5f3 100644
--- a/code/modules/client/preference_setup/loadout/loadout_suit.dm
+++ b/code/modules/client/preference_setup/loadout/loadout_suit.dm
@@ -273,6 +273,14 @@ datum/gear/suit/duster
display_name = "cloak, medical"
path = /obj/item/clothing/accessory/poncho/roles/cloak/medical
+/datum/gear/suit/roles/poncho/cloak/custom //A colorable cloak
+ display_name = "cloak (colorable)"
+ path = /obj/item/clothing/accessory/poncho/roles/cloak/custom
+
+/datum/gear/suit/roles/poncho/cloak/custom/New()
+ ..()
+ gear_tweaks = list(gear_tweak_free_color_choice)
+
/datum/gear/suit/unathi_robe
display_name = "roughspun robe"
path = /obj/item/clothing/suit/unathi/robe
diff --git a/code/modules/clothing/spacesuits/void/void.dm b/code/modules/clothing/spacesuits/void/void.dm
index f1d12a9982..83dbdfd345 100644
--- a/code/modules/clothing/spacesuits/void/void.dm
+++ b/code/modules/clothing/spacesuits/void/void.dm
@@ -220,7 +220,7 @@
if(istype(W,/obj/item/clothing/accessory) || istype(W, /obj/item/weapon/hand_labeler))
return ..()
- if(istype(src.loc,/mob/living))
+ if(user.get_inventory_slot(src) == slot_wear_suit)
to_chat(user, "You cannot modify \the [src] while it is being worn.")
return
diff --git a/code/modules/clothing/under/accessories/badges.dm b/code/modules/clothing/under/accessories/badges.dm
index ded92c2b3a..8e114c6984 100644
--- a/code/modules/clothing/under/accessories/badges.dm
+++ b/code/modules/clothing/under/accessories/badges.dm
@@ -148,3 +148,66 @@
new /obj/item/clothing/accessory/badge/holo/cord(src)
..()
return
+
+// Synthmorph bag / Corporation badges. Primarily used on the robobag, but can be worn. Default is NT.
+
+/obj/item/clothing/accessory/badge/corporate_tag
+ name = "NanoTrasen Badge"
+ desc = "A plain metallic plate that might denote the wearer as a member of NanoTrasen."
+ icon_state = "tag_nt"
+ item_state = "badge"
+ badge_string = "NanoTrasen"
+
+/obj/item/clothing/accessory/badge/corporate_tag/morpheus
+ name = "Morpheus Badge"
+ desc = "A plain metallic plate that might denote the wearer as a member of Morpheus Cyberkinetics."
+ icon_state = "tag_blank"
+ badge_string = "Morpheus"
+
+/obj/item/clothing/accessory/badge/corporate_tag/wardtaka
+ name = "Ward-Takahashi Badge"
+ desc = "A plain metallic plate that might denote the wearer as a member of Ward-Takahashi."
+ icon_state = "tag_ward"
+ badge_string = "Ward-Takahashi"
+
+/obj/item/clothing/accessory/badge/corporate_tag/zenghu
+ name = "Zeng-Hu Badge"
+ desc = "A plain metallic plate that might denote the wearer as a member of Zeng-Hu."
+ icon_state = "tag_zeng"
+ badge_string = "Zeng-Hu"
+
+/obj/item/clothing/accessory/badge/corporate_tag/gilthari
+ name = "Gilthari Badge"
+ desc = "An opulent metallic plate that might denote the wearer as a member of Gilthari."
+ icon_state = "tag_gil"
+ badge_string = "Gilthari"
+
+/obj/item/clothing/accessory/badge/corporate_tag/veymed
+ name = "Vey-Medical Badge"
+ desc = "A plain metallic plate that might denote the wearer as a member of Vey-Medical."
+ icon_state = "tag_vey"
+ badge_string = "Vey-Medical"
+
+/obj/item/clothing/accessory/badge/corporate_tag/hephaestus
+ name = "Hephaestus Badge"
+ desc = "A rugged metallic plate that might denote the wearer as a member of Hephaestus."
+ icon_state = "tag_heph"
+ badge_string = "Hephaestus"
+
+/obj/item/clothing/accessory/badge/corporate_tag/grayson
+ name = "Grayson Badge"
+ desc = "A rugged metallic plate that might denote the wearer as a member of Grayson."
+ icon_state = "tag_grayson"
+ badge_string = "Grayson"
+
+/obj/item/clothing/accessory/badge/corporate_tag/xion
+ name = "Xion Badge"
+ desc = "A rugged metallic plate that might denote the wearer as a member of Xion."
+ icon_state = "tag_xion"
+ badge_string = "Xion"
+
+/obj/item/clothing/accessory/badge/corporate_tag/bishop
+ name = "Bishop Badge"
+ desc = "A sleek metallic plate that might denote the wearer as a member of Bishop."
+ icon_state = "tag_bishop"
+ badge_string = "Bishop"
diff --git a/code/modules/clothing/under/accessories/clothing.dm b/code/modules/clothing/under/accessories/clothing.dm
index 29057e8469..66636aac6f 100644
--- a/code/modules/clothing/under/accessories/clothing.dm
+++ b/code/modules/clothing/under/accessories/clothing.dm
@@ -216,6 +216,13 @@
icon_state = "medcloak"
item_state = "medcloak"
+
+/obj/item/clothing/accessory/poncho/roles/cloak/custom //A colorable cloak
+ name = "cloak"
+ desc = "A simple, bland cloak."
+ icon_state = "colorcloak"
+ item_state = "colorcloak"
+
/obj/item/clothing/accessory/hawaii
name = "flower-pattern shirt"
desc = "You probably need some welder googles to look at this."
diff --git a/code/modules/economy/cash.dm b/code/modules/economy/cash.dm
index 8c67e5394c..c5a11275e8 100644
--- a/code/modules/economy/cash.dm
+++ b/code/modules/economy/cash.dm
@@ -78,6 +78,8 @@
/obj/item/weapon/spacecash/attack_self()
var/amount = input(usr, "How many Thalers do you want to take? (0 to [src.worth])", "Take Money", 20) as num
+ if(!src || QDELETED(src))
+ return
amount = round(CLAMP(amount, 0, src.worth))
if(!amount)
diff --git a/code/modules/food/drinkingglass/drinkingglass.dm b/code/modules/food/drinkingglass/drinkingglass.dm
index d4c8389a51..604b85665f 100644
--- a/code/modules/food/drinkingglass/drinkingglass.dm
+++ b/code/modules/food/drinkingglass/drinkingglass.dm
@@ -65,7 +65,7 @@
return 1
return 0
-/obj/item/weapon/reagent_containers/food/drinks/glass2/New()
+/obj/item/weapon/reagent_containers/food/drinks/glass2/Initialize()
..()
icon_state = base_icon
diff --git a/code/modules/food/drinkingglass/shaker.dm b/code/modules/food/drinkingglass/shaker.dm
index b653f14c60..3b81c6bdec 100644
--- a/code/modules/food/drinkingglass/shaker.dm
+++ b/code/modules/food/drinkingglass/shaker.dm
@@ -11,7 +11,7 @@
rim_pos = null // no fruit slices
var/lid_color = "black"
-/obj/item/weapon/reagent_containers/food/drinks/glass2/fitnessflask/New()
+/obj/item/weapon/reagent_containers/food/drinks/glass2/fitnessflask/Initialize()
..()
lid_color = pick("black", "red", "blue")
update_icon()
@@ -23,7 +23,7 @@
/obj/item/weapon/reagent_containers/food/drinks/glass2/fitnessflask/proteinshake
name = "protein shake"
-/obj/item/weapon/reagent_containers/food/drinks/glass2/fitnessflask/proteinshake/New()
+/obj/item/weapon/reagent_containers/food/drinks/glass2/fitnessflask/proteinshake/Initialize()
..()
reagents.add_reagent("nutriment", 30)
reagents.add_reagent("iron", 10)
diff --git a/code/modules/food/food/snacks.dm b/code/modules/food/food/snacks.dm
index 57d237ee5e..294baee09b 100644
--- a/code/modules/food/food/snacks.dm
+++ b/code/modules/food/food/snacks.dm
@@ -3285,23 +3285,23 @@
return
..()
-/obj/item/pizzabox/margherita/New()
+/obj/item/pizzabox/margherita/Initialize()
pizza = new /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/margherita(src)
boxtag = "Margherita Deluxe"
-/obj/item/pizzabox/vegetable/New()
+/obj/item/pizzabox/vegetable/Initialize()
pizza = new /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/vegetablepizza(src)
boxtag = "Gourmet Vegatable"
-/obj/item/pizzabox/mushroom/New()
+/obj/item/pizzabox/mushroom/Initialize()
pizza = new /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/mushroompizza(src)
boxtag = "Mushroom Special"
-/obj/item/pizzabox/meat/New()
+/obj/item/pizzabox/meat/Initialize()
pizza = new /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/meatpizza(src)
boxtag = "Meatlover's Supreme"
-/obj/item/pizzabox/old/New()
+/obj/item/pizzabox/old/Initialize()
pizza = new /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/oldpizza(src)
boxtag = "Deluxe Gourmet"
diff --git a/code/modules/food/kitchen/gibber.dm b/code/modules/food/kitchen/gibber.dm
index 3863dbddf7..db1c13ebde 100644
--- a/code/modules/food/kitchen/gibber.dm
+++ b/code/modules/food/kitchen/gibber.dm
@@ -217,13 +217,13 @@
spawn(gib_time)
- src.operating = 0
- src.occupant.gib()
- qdel(src.occupant)
+ operating = 0
+ occupant.gib()
+ occupant = null
playsound(src.loc, 'sound/effects/splat.ogg', 50, 1)
operating = 0
- for (var/obj/item/thing in contents)
+ for (var/obj/thing in contents)
// There's a chance that the gibber will fail to destroy some evidence.
if(istype(thing,/obj/item/organ) && prob(80))
qdel(thing)
diff --git a/code/modules/food/recipes_microwave.dm b/code/modules/food/recipes_microwave.dm
index f683429789..f80fecdd9f 100644
--- a/code/modules/food/recipes_microwave.dm
+++ b/code/modules/food/recipes_microwave.dm
@@ -457,6 +457,8 @@ I said no!
. = ..()
if (.)
var/obj/item/weapon/paper/paper = locate() in container
+ if (!paper)
+ return 0
if (!paper.info)
return 0
return .
diff --git a/code/modules/games/cards.dm b/code/modules/games/cards.dm
index e9b3ae537e..08d762e093 100644
--- a/code/modules/games/cards.dm
+++ b/code/modules/games/cards.dm
@@ -254,8 +254,9 @@
return
-/obj/item/weapon/deck/verb_pickup(mob/user as mob) // Snowflaked so pick up verb work as intended
- if((user == usr && (!( usr.restrained() ) && (!( usr.stat ) && (usr.contents.Find(src) || in_range(src, usr))))))
+/obj/item/weapon/deck/verb_pickup() // Snowflaked so pick up verb work as intended
+ var/mob/user = usr
+ if((istype(user) && (!( usr.restrained() ) && (!( usr.stat ) && (usr.contents.Find(src) || in_range(src, usr))))))
if(!istype(usr, /mob/living/simple_mob))
if( !usr.get_active_hand() ) //if active hand is empty
var/mob/living/carbon/human/H = user
diff --git a/code/modules/integrated_electronics/subtypes/input.dm b/code/modules/integrated_electronics/subtypes/input.dm
index 9c54a6e377..f12db3f76b 100644
--- a/code/modules/integrated_electronics/subtypes/input.dm
+++ b/code/modules/integrated_electronics/subtypes/input.dm
@@ -413,8 +413,9 @@
activate_pin(3)
- for(var/mob/O in hearers(1, get_turf(src)))
- O.show_message(text("\icon[] *beep* *beep*", src), 3, "*beep* *beep*", 2)
+ if(loc)
+ for(var/mob/O in hearers(1, get_turf(src)))
+ O.show_message(text("\icon[] *beep* *beep*", src), 3, "*beep* *beep*", 2)
/obj/item/integrated_circuit/input/EPv2
name = "\improper EPv2 circuit"
diff --git a/code/modules/materials/material_recipes.dm b/code/modules/materials/material_recipes.dm
index 7071ec2feb..764a99ad97 100644
--- a/code/modules/materials/material_recipes.dm
+++ b/code/modules/materials/material_recipes.dm
@@ -196,3 +196,7 @@
recipes += new/datum/stack_recipe("alien wood floor tile", /obj/item/stack/tile/wood/sif, 1, 4, 20)
recipes -= new/datum/stack_recipe("wood floor tile", /obj/item/stack/tile/wood, 1, 4, 20)
recipes -= new/datum/stack_recipe("wooden chair", /obj/structure/bed/chair/wood, 3, time = 10, one_per_turf = 1, on_floor = 1)
+
+/material/supermatter/generate_recipes()
+ recipes = list()
+ recipes += new/datum/stack_recipe("supermatter shard", /obj/machinery/power/supermatter/shard, 30 , one_per_turf = 1, time = 600, on_floor = 1)
diff --git a/code/modules/materials/material_sheets.dm b/code/modules/materials/material_sheets.dm
index 0b1daf10fa..1035c70eba 100644
--- a/code/modules/materials/material_sheets.dm
+++ b/code/modules/materials/material_sheets.dm
@@ -214,7 +214,101 @@
no_variants = FALSE
/obj/item/stack/material/durasteel/hull
- name = "MAT_DURASTEELHULL"
+ name = MAT_DURASTEELHULL
+
+/obj/item/stack/material/titanium
+ name = MAT_TITANIUM
+ icon_state = "sheet-silver"
+ item_state = "sheet-silver"
+ default_type = MAT_TITANIUM
+ no_variants = FALSE
+
+/obj/item/stack/material/titanium/hull
+ name = MAT_TITANIUMHULL
+ default_type = MAT_TITANIUMHULL
+
+// Particle Smasher and Exotic material.
+/obj/item/stack/material/verdantium
+ name = MAT_VERDANTIUM
+ icon_state = "sheet-wavy"
+ item_state = "mhydrogen"
+ default_type = MAT_VERDANTIUM
+ no_variants = FALSE
+ apply_colour = TRUE
+
+/obj/item/stack/material/morphium
+ name = MAT_MORPHIUM
+ icon_state = "sheet-wavy"
+ item_state = "mhydrogen"
+ default_type = MAT_MORPHIUM
+ no_variants = FALSE
+ apply_colour = TRUE
+
+/obj/item/stack/material/morphium/hull
+ name = MAT_MORPHIUMHULL
+ default_type = MAT_MORPHIUMHULL
+
+/obj/item/stack/material/valhollide
+ name = MAT_VALHOLLIDE
+ icon_state = "sheet-gem"
+ item_state = "diamond"
+ default_type = MAT_VALHOLLIDE
+ no_variants = FALSE
+ apply_colour = TRUE
+
+// Forged in the equivalent of Hell, one piece at a time.
+/obj/item/stack/material/supermatter
+ name = MAT_SUPERMATTER
+ icon_state = "sheet-super"
+ item_state = "diamond"
+ default_type = MAT_SUPERMATTER
+ apply_colour = TRUE
+
+/obj/item/stack/material/supermatter/proc/update_mass() // Due to how dangerous they can be, the item will get heavier and larger the more are in the stack.
+ slowdown = amount / 10
+ w_class = min(5, round(amount / 10) + 1)
+ throw_range = round(amount / 7) + 1
+
+/obj/item/stack/material/supermatter/use(var/used)
+ . = ..()
+ update_mass()
+ return
+
+/obj/item/stack/material/supermatter/attack_hand(mob/user)
+ update_mass()
+ radiation_repository.radiate(src, 5 + amount)
+ var/mob/living/M = user
+ if(!istype(M))
+ return
+
+ var/burn_user = TRUE
+ if(istype(M, /mob/living/carbon/human))
+ var/mob/living/carbon/human/H = user
+ var/obj/item/clothing/gloves/G = H.gloves
+ if(istype(G) && ((G.flags & THICKMATERIAL && prob(70)) || istype(G, /obj/item/clothing/gloves/gauntlets)))
+ burn_user = FALSE
+
+ if(burn_user)
+ H.visible_message("\The [src] flashes as it scorches [H]'s hands!")
+ H.apply_damage(amount / 2 + 5, BURN, "r_hand", used_weapon="Supermatter Chunk")
+ H.apply_damage(amount / 2 + 5, BURN, "l_hand", used_weapon="Supermatter Chunk")
+ H.drop_from_inventory(src, get_turf(H))
+ return
+
+ if(istype(user, /mob/living/silicon/robot))
+ burn_user = FALSE
+
+ if(burn_user)
+ M.apply_damage(amount, BURN, null, used_weapon="Supermatter Chunk")
+
+/obj/item/stack/material/supermatter/ex_act(severity) // An incredibly hard to manufacture material, SM chunks are unstable by their 'stabilized' nature.
+ if(prob((4 / severity) * 20))
+ radiation_repository.radiate(get_turf(src), amount * 4)
+ explosion(get_turf(src),round(amount / 12) , round(amount / 6), round(amount / 3), round(amount / 25))
+ qdel(src)
+ return
+ radiation_repository.radiate(get_turf(src), amount * 2)
+ ..()
/obj/item/stack/material/wood
name = "wooden plank"
diff --git a/code/modules/materials/materials.dm b/code/modules/materials/materials.dm
index db925323bc..46cb30e3d2 100644
--- a/code/modules/materials/materials.dm
+++ b/code/modules/materials/materials.dm
@@ -105,6 +105,8 @@ var/list/name_to_material
var/opacity = 1 // Is the material transparent? 0.5< makes transparent walls/doors.
var/reflectivity = 0 // How reflective to light is the material? Currently used for laser reflection and defense.
var/explosion_resistance = 5 // Only used by walls currently.
+ var/negation = 0 // Objects that respect this will randomly absorb impacts with this var as the percent chance.
+ var/spatial_instability = 0 // Objects that have trouble staying in the same physical space by sheer laws of nature have this. Percent for respecting items to cause teleportation.
var/conductive = 1 // Objects with this var add CONDUCTS to flags on spawn.
var/conductivity = null // How conductive the material is. Iron acts as the baseline, at 10.
var/list/composite_material // If set, object matter var will be a list containing these values.
@@ -293,6 +295,8 @@ var/list/name_to_material
/material/supermatter
name = "supermatter"
icon_colour = "#FFFF00"
+ stack_type = /obj/item/stack/material/supermatter
+ shard_type = SHARD_SHARD
radioactivity = 20
stack_type = null
luminescence = 3
@@ -304,6 +308,7 @@ var/list/name_to_material
sheet_singular_name = "crystal"
sheet_plural_name = "crystals"
is_fusion_fuel = 1
+ stack_origin_tech = list(TECH_MATERIAL = 8, TECH_PHORON = 5, TECH_BLUESPACE = 4)
/material/phoron
name = "phoron"
@@ -354,11 +359,10 @@ var/list/name_to_material
name = "marble"
icon_colour = "#AAAAAA"
weight = 26
- hardness = 100
+ hardness = 70
integrity = 201 //hack to stop kitchen benches being flippable, todo: refactor into weight system
stack_type = /obj/item/stack/material/marble
-
/material/steel
name = DEFAULT_WALL_MATERIAL
stack_type = /obj/item/stack/material/steel
@@ -455,8 +459,8 @@ var/list/name_to_material
reflectivity = 0.9
/material/plasteel/titanium
- name = "titanium"
- stack_type = null
+ name = MAT_TITANIUM
+ stack_type = /obj/item/stack/material/titanium
conductivity = 2.38
icon_base = "metal"
door_icon_base = "metal"
@@ -465,7 +469,7 @@ var/list/name_to_material
/material/plasteel/titanium/hull
name = MAT_TITANIUMHULL
- stack_type = null
+ stack_type = /obj/item/stack/material/titanium/hull
icon_base = "hull"
icon_reinf = "reinf_mesh"
@@ -686,7 +690,7 @@ var/list/name_to_material
sheet_plural_name = "ingots"
/material/lead
- name = "lead"
+ name = MAT_LEAD
stack_type = /obj/item/stack/material/lead
icon_colour = "#273956"
weight = 23 // Lead is a bit more dense than silver IRL, and silver has 22 ingame.
@@ -695,6 +699,74 @@ var/list/name_to_material
sheet_plural_name = "ingots"
radiation_resistance = 25 // Lead is Special and so gets to block more radiation than it normally would with just weight, totalling in 48 protection.
+// Particle Smasher and other exotic materials.
+
+/material/verdantium
+ name = MAT_VERDANTIUM
+ stack_type = /obj/item/stack/material/verdantium
+ icon_base = "metal"
+ door_icon_base = "metal"
+ icon_reinf = "reinf_metal"
+ icon_colour = "#4FE95A"
+ integrity = 80
+ protectiveness = 15
+ weight = 15
+ hardness = 30
+ shard_type = SHARD_SHARD
+ negation = 15
+ conductivity = 60
+ reflectivity = 0.3
+ radiation_resistance = 5
+ stack_origin_tech = list(TECH_MATERIAL = 6, TECH_POWER = 5, TECH_BIO = 4)
+ sheet_singular_name = "sheet"
+ sheet_plural_name = "sheets"
+
+/material/morphium
+ name = MAT_MORPHIUM
+ stack_type = /obj/item/stack/material/morphium
+ icon_base = "metal"
+ door_icon_base = "metal"
+ icon_colour = "#37115A"
+ icon_reinf = "reinf_metal"
+ protectiveness = 60
+ integrity = 300
+ conductivity = 1.5
+ hardness = 90
+ shard_type = SHARD_SHARD
+ weight = 30
+ negation = 25
+ explosion_resistance = 85
+ reflectivity = 0.2
+ radiation_resistance = 10
+ stack_origin_tech = list(TECH_MATERIAL = 8, TECH_ILLEGAL = 1, TECH_PHORON = 4, TECH_BLUESPACE = 4, TECH_ARCANE = 1)
+
+/material/morphium/hull
+ name = MAT_MORPHIUMHULL
+ stack_type = /obj/item/stack/material/morphium/hull
+ icon_base = "hull"
+ icon_reinf = "reinf_mesh"
+
+/material/valhollide
+ name = MAT_VALHOLLIDE
+ stack_type = /obj/item/stack/material/valhollide
+ icon_base = "stone"
+ door_icon_base = "stone"
+ icon_reinf = "reinf_mesh"
+ icon_colour = "##FFF3B2"
+ protectiveness = 30
+ integrity = 240
+ weight = 30
+ hardness = 45
+ negation = 2
+ conductivity = 5
+ reflectivity = 0.5
+ radiation_resistance = 20
+ spatial_instability = 30
+ stack_origin_tech = list(TECH_MATERIAL = 7, TECH_PHORON = 5, TECH_BLUESPACE = 5)
+ sheet_singular_name = "gem"
+ sheet_plural_name = "gems"
+
+
// Adminspawn only, do not let anyone get this.
/material/alienalloy
name = "alienalloy"
diff --git a/code/modules/metric/activity.dm b/code/modules/metric/activity.dm
index 370ae0eb2f..c4acdc57ec 100644
--- a/code/modules/metric/activity.dm
+++ b/code/modules/metric/activity.dm
@@ -6,7 +6,7 @@
. = 0
return
- if(!M.mind || !M.client) // Logged out. They might come back but we can't do any meaningful assessments for now.
+ if(!M.client) // Logged out. They might come back but we can't do any meaningful assessments for now.
. = 0
return
@@ -79,4 +79,4 @@
. += assess_player_activity(O)
num++
if(num)
- . = round(. / num, 0.1)
\ No newline at end of file
+ . = round(. / num, 0.1)
diff --git a/code/modules/mining/drilling/drill.dm b/code/modules/mining/drilling/drill.dm
index 5e28944ae0..08580c7f97 100644
--- a/code/modules/mining/drilling/drill.dm
+++ b/code/modules/mining/drilling/drill.dm
@@ -17,7 +17,7 @@
var/list/resource_field = list()
var/obj/item/device/radio/intercom/faultreporter = new /obj/item/device/radio/intercom{channels=list("Supply")}(null)
- var/ore_types = list(
+ var/list/ore_types = list(
"hematite" = /obj/item/weapon/ore/iron,
"uranium" = /obj/item/weapon/ore/uranium,
"gold" = /obj/item/weapon/ore/gold,
@@ -34,8 +34,20 @@
var/harvest_speed
var/capacity
var/charge_use
+ var/exotic_drilling
var/obj/item/weapon/cell/cell = null
+ // Found with an advanced laser. exotic_drilling >= 1
+ var/list/ore_types_uncommon = list(
+ MAT_MARBLE = /obj/item/weapon/ore/marble,
+ MAT_LEAD = /obj/item/weapon/ore/lead
+ )
+
+ // Found with an ultra laser. exotic_drilling >= 2
+ var/list/ore_types_rare = list(
+ MAT_VERDANTIUM = /obj/item/weapon/ore/verdantium
+ )
+
//Flags
var/need_update_field = 0
var/need_player_check = 0
@@ -127,7 +139,7 @@
var/oretype = ore_types[metal]
new oretype(src)
- if(!found_resource)
+ if(!found_resource) // If a drill can't see an advanced material, it will destroy it while going through.
harvesting.has_resources = 0
harvesting.resources = null
resource_field -= harvesting
@@ -219,6 +231,14 @@
for(var/obj/item/weapon/stock_parts/P in component_parts)
if(istype(P, /obj/item/weapon/stock_parts/micro_laser))
harvest_speed = P.rating
+ exotic_drilling = P.rating - 1
+ if(exotic_drilling >= 1)
+ ore_types |= ore_types_uncommon
+ if(exotic_drilling >= 2)
+ ore_types |= ore_types_rare
+ else
+ ore_types -= ore_types_uncommon
+ ore_types -= ore_types_rare
if(istype(P, /obj/item/weapon/stock_parts/matter_bin))
capacity = 200 * P.rating
if(istype(P, /obj/item/weapon/stock_parts/capacitor))
diff --git a/code/modules/mining/drilling/scanner.dm b/code/modules/mining/drilling/scanner.dm
index 0df256c131..56f371fe91 100644
--- a/code/modules/mining/drilling/scanner.dm
+++ b/code/modules/mining/drilling/scanner.dm
@@ -18,7 +18,8 @@
"surface minerals" = 0,
"precious metals" = 0,
"nuclear fuel" = 0,
- "exotic matter" = 0
+ "exotic matter" = 0,
+ "anomalous matter" = 0
)
for(var/turf/simulated/T in range(2, get_turf(user)))
@@ -30,10 +31,11 @@
var/ore_type
switch(metal)
- if("silicates", "carbon", "hematite") ore_type = "surface minerals"
- if("gold", "silver", "diamond") ore_type = "precious metals"
+ if("silicates", "carbon", "hematite", "marble") ore_type = "surface minerals"
+ if("gold", "silver", "diamond", "lead") ore_type = "precious metals"
if("uranium") ore_type = "nuclear fuel"
if("phoron", "osmium", "hydrogen") ore_type = "exotic matter"
+ if("verdantium") ore_type = "anomalous matter"
if(ore_type) metals[ore_type] += T.resources[metal]
diff --git a/code/modules/mining/machine_processing.dm b/code/modules/mining/machine_processing.dm
index 406f97af50..355e40b362 100644
--- a/code/modules/mining/machine_processing.dm
+++ b/code/modules/mining/machine_processing.dm
@@ -171,10 +171,13 @@
"phoron" = 15,
"silver" = 16,
"gold" = 18,
+ "marble" = 20,
"uranium" = 30,
"diamond" = 50,
"platinum" = 40,
- "mhydrogen" = 40)
+ "lead" = 40,
+ "mhydrogen" = 40,
+ "verdantium" = 60)
/obj/machinery/mineral/processing_unit/New()
..()
diff --git a/code/modules/mining/mine_items.dm b/code/modules/mining/mine_items.dm
index dde3b7f6f7..75f675f67c 100644
--- a/code/modules/mining/mine_items.dm
+++ b/code/modules/mining/mine_items.dm
@@ -211,7 +211,7 @@
var/obj/item/stack/flag/newflag = new src.type(T)
newflag.amount = 1
newflag.upright = 1
- anchored = 1
+ newflag.anchored = 1
newflag.name = newflag.singular_name
newflag.icon_state = "[newflag.base_state]_open"
newflag.visible_message("[user] plants [newflag] firmly in the ground.")
diff --git a/code/modules/mining/mine_turfs.dm b/code/modules/mining/mine_turfs.dm
index 37354f76b0..15815302bc 100644
--- a/code/modules/mining/mine_turfs.dm
+++ b/code/modules/mining/mine_turfs.dm
@@ -46,7 +46,10 @@ var/list/mining_overlay_cache = list()
"osmium" = /obj/item/weapon/ore/osmium,
"hydrogen" = /obj/item/weapon/ore/hydrogen,
"silicates" = /obj/item/weapon/ore/glass,
- "carbon" = /obj/item/weapon/ore/coal
+ "carbon" = /obj/item/weapon/ore/coal,
+ "verdantium" = /obj/item/weapon/ore/verdantium,
+ "marble" = /obj/item/weapon/ore/marble,
+ "lead" = /obj/item/weapon/ore/lead
)
has_resources = 1
@@ -608,10 +611,10 @@ var/list/mining_overlay_cache = list()
var/mineral_name
if(rare_ore)
- mineral_name = pickweight(list("uranium" = 10, "platinum" = 10, "hematite" = 20, "carbon" = 20, "diamond" = 2, "gold" = 10, "silver" = 10, "phoron" = 20))
+ mineral_name = pickweight(list("marble" = 5, "uranium" = 10, "platinum" = 10, "hematite" = 20, "carbon" = 20, "diamond" = 2, "gold" = 10, "silver" = 10, "phoron" = 20, "lead" = 5, "verdantium" = 1))
else
- mineral_name = pickweight(list("uranium" = 5, "platinum" = 5, "hematite" = 35, "carbon" = 35, "diamond" = 1, "gold" = 5, "silver" = 5, "phoron" = 10))
+ mineral_name = pickweight(list("marble" = 3, "uranium" = 10, "platinum" = 10, "hematite" = 70, "carbon" = 70, "diamond" = 2, "gold" = 10, "silver" = 10, "phoron" = 20, "lead" = 2, "verdantium" = 1))
if(mineral_name && (mineral_name in ore_data))
mineral = ore_data[mineral_name]
diff --git a/code/modules/mining/ore.dm b/code/modules/mining/ore.dm
index e531e5208f..70d0e78685 100644
--- a/code/modules/mining/ore.dm
+++ b/code/modules/mining/ore.dm
@@ -24,6 +24,12 @@
origin_tech = list(TECH_MATERIAL = 1)
material = "carbon"
+/obj/item/weapon/ore/marble
+ name = "recrystallized carbonate"
+ icon_state = "ore_marble"
+ origin_tech = list(TECH_MATERIAL = 1)
+ material = "marble"
+
/obj/item/weapon/ore/glass
name = "sand"
icon_state = "ore_glass"
@@ -77,6 +83,29 @@
icon_state = "ore_hydrogen"
material = "mhydrogen"
+/obj/item/weapon/ore/verdantium
+ name = "verdantite dust"
+ icon_state = "ore_verdantium"
+ material = MAT_VERDANTIUM
+ origin_tech = list(TECH_MATERIAL = 7)
+
+// POCKET ... Crystal dust.
+/obj/item/weapon/ore/verdantium/throw_impact(atom/hit_atom)
+ ..()
+ var/mob/living/carbon/human/H = hit_atom
+ if(istype(H) && H.has_eyes() && prob(85))
+ H << "Some of \the [src] gets in your eyes!"
+ H.Blind(10)
+ H.eye_blurry += 15
+ spawn(1)
+ if(istype(loc, /turf/)) qdel(src)
+
+/obj/item/weapon/ore/lead
+ name = "lead glance"
+ icon_state = "ore_lead"
+ material = MAT_LEAD
+ origin_tech = list(TECH_MATERIAL = 3)
+
/obj/item/weapon/ore/slag
name = "Slag"
desc = "Someone screwed up..."
diff --git a/code/modules/mining/ore_datum.dm b/code/modules/mining/ore_datum.dm
index a3528eb55b..e578902104 100644
--- a/code/modules/mining/ore_datum.dm
+++ b/code/modules/mining/ore_datum.dm
@@ -132,4 +132,35 @@ var/global/list/ore_data = list()
display_name = "metallic hydrogen"
smelts_to = "tritium"
compresses_to = "mhydrogen"
- scan_icon = "mineral_rare"
\ No newline at end of file
+ scan_icon = "mineral_rare"
+
+/ore/verdantium
+ name = MAT_VERDANTIUM
+ display_name = "crystalline verdantite"
+ compresses_to = MAT_VERDANTIUM
+ result_amount = 2
+ spread_chance = 5
+ ore = /obj/item/weapon/ore/verdantium
+ scan_icon = "mineral_rare"
+ xarch_ages = list(
+ "billion" = 13,
+ "billion_lower" = 10
+ )
+
+/ore/marble
+ name = MAT_MARBLE
+ display_name = "recrystallized carbonate"
+ compresses_to = "marble"
+ result_amount = 1
+ spread_chance = 10
+ ore = /obj/item/weapon/ore/marble
+ scan_icon = "mineral_common"
+
+/ore/lead
+ name = MAT_LEAD
+ display_name = "lead glance"
+ smelts_to = "lead"
+ result_amount = 3
+ spread_chance = 20
+ ore = /obj/item/weapon/ore/lead
+ scan_icon = "mineral_rare"
diff --git a/code/modules/mob/_modifiers/modifiers.dm b/code/modules/mob/_modifiers/modifiers.dm
index 115acb1b3e..6e738094a1 100644
--- a/code/modules/mob/_modifiers/modifiers.dm
+++ b/code/modules/mob/_modifiers/modifiers.dm
@@ -45,6 +45,8 @@
var/icon_scale_percent // Makes the holder's icon get scaled up or down.
var/attack_speed_percent // Makes the holder's 'attack speed' (click delay) shorter or longer.
var/pain_immunity // Makes the holder not care about pain while this is on. Only really useful to human mobs.
+ var/pulse_modifier // Modifier for pulse, will be rounded on application, then added to the normal 'pulse' multiplier which ranges between 0 and 5 normally. Only applied if they're living.
+ var/pulse_set_level // Positive number. If this is non-null, it will hard-set the pulse level to this. Pulse ranges from 0 to 5 normally.
/datum/modifier/New(var/new_holder, var/new_origin)
holder = new_holder
diff --git a/code/modules/mob/_modifiers/modifiers_misc.dm b/code/modules/mob/_modifiers/modifiers_misc.dm
index 61386ce6d6..64be119d7c 100644
--- a/code/modules/mob/_modifiers/modifiers_misc.dm
+++ b/code/modules/mob/_modifiers/modifiers_misc.dm
@@ -267,4 +267,15 @@ the artifact triggers the rage.
return FALSE
if(L.get_poison_protection() >= 1)
return FALSE
- return TRUE
\ No newline at end of file
+ return TRUE
+
+// Pulse modifier.
+/datum/modifier/false_pulse
+ name = "false pulse"
+ desc = "Your blood flows, despite all other factors."
+
+ on_created_text = "You feel alive."
+ on_expired_text = "You feel.. different."
+ stacks = MODIFIER_STACK_EXTEND
+
+ pulse_set_level = PULSE_NORM
diff --git a/code/modules/mob/hear_say.dm b/code/modules/mob/hear_say.dm
index e7c097c2b3..5408742654 100644
--- a/code/modules/mob/hear_say.dm
+++ b/code/modules/mob/hear_say.dm
@@ -275,6 +275,8 @@
var/final_message = "[part_a][speaker_name][part_b][formatted]"
if(check_mentioned(formatted) && is_preference_enabled(/datum/client_preference/check_mention))
final_message = "[time][final_message]"
+ else
+ final_message = "[time][final_message]"
to_chat(src, final_message)
/mob/living/silicon/ai/on_hear_radio(part_a, speaker_name, track, part_b, formatted)
@@ -282,6 +284,8 @@
var/final_message = "[part_a][track][part_b][formatted]"
if(check_mentioned(formatted) && is_preference_enabled(/datum/client_preference/check_mention))
final_message = "[time][final_message]"
+ else
+ final_message = "[time][final_message]"
to_chat(src, final_message)
/mob/proc/hear_signlang(var/message, var/verb = "gestures", var/datum/language/language, var/mob/speaker = null)
diff --git a/code/modules/mob/living/carbon/brain/MMI.dm b/code/modules/mob/living/carbon/brain/MMI.dm
index 72c8238e55..128116b151 100644
--- a/code/modules/mob/living/carbon/brain/MMI.dm
+++ b/code/modules/mob/living/carbon/brain/MMI.dm
@@ -339,3 +339,16 @@
..()
src.brainmob.name = "[pick(list("PBU","HIU","SINA","ARMA","OSI"))]-[rand(100, 999)]"
src.brainmob.real_name = src.brainmob.name
+
+// This type shouldn't care about brainmobs.
+/obj/item/device/mmi/inert
+
+// This is a 'fake' MMI that is used to let AIs control borg shells directly.
+// This doesn't inherit from /digital because all that does is add ghost pulling capabilities, which this thing won't need.
+/obj/item/device/mmi/inert/ai_remote
+ name = "\improper AI remote interface"
+ desc = "A sophisticated board which allows for an artificial intelligence to remotely control a synthetic chassis."
+ icon = 'icons/obj/module.dmi'
+ icon_state = "mainboard"
+ w_class = ITEMSIZE_NORMAL
+ origin_tech = list(TECH_ENGINEERING = 2, TECH_MATERIAL = 2, TECH_BLUESPACE = 2, TECH_DATA = 3)
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm
index d1ba1f9755..f6fa176839 100644
--- a/code/modules/mob/living/carbon/human/emote.dm
+++ b/code/modules/mob/living/carbon/human/emote.dm
@@ -29,7 +29,7 @@
m_type = 1
//Machine-only emotes
- if("ping", "beep", "buzz", "yes", "no", "rcough", "rsneeze")
+ if("ping", "beep", "buzz", "yes", "ye", "no", "rcough", "rsneeze")
if(!isSynthetic())
src << "You are not a synthetic."
@@ -52,7 +52,7 @@
else if(act == "ping")
display_msg = "pings"
use_sound = 'sound/machines/ping.ogg'
- else if(act == "yes")
+ else if(act == "yes" || act == "ye")
display_msg = "emits an affirmative blip"
use_sound = 'sound/machines/synth_yes.ogg'
else if(act == "no")
@@ -727,7 +727,7 @@
src << "blink, blink_r, blush, bow-(none)/mob, burp, choke, chuckle, clap, collapse, cough, cry, custom, deathgasp, drool, eyebrow, fastsway/qwag, \
frown, gasp, giggle, glare-(none)/mob, grin, groan, grumble, handshake, hug-(none)/mob, laugh, look-(none)/mob, moan, mumble, nod, pale, point-atom, \
raise, salute, scream, sneeze, shake, shiver, shrug, sigh, signal-#1-10, slap-(none)/mob, smile, sneeze, sniff, snore, stare-(none)/mob, stopsway/swag, sway/wag, swish, tremble, twitch, \
- twitch_v, vomit, whimper, wink, yawn. Synthetics: beep, buzz, yess, no, rcough, rsneeze, ping"
+ twitch_v, vomit, whimper, wink, yawn. Synthetics: beep, buzz, yes, no, rcough, rsneeze, ping"
else
src << "Unusable emote '[act]'. Say *help for a list."
diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm
index d42f628059..0acb2fbb99 100644
--- a/code/modules/mob/living/carbon/human/life.dm
+++ b/code/modules/mob/living/carbon/human/life.dm
@@ -1393,7 +1393,7 @@
/mob/living/carbon/human/proc/process_glasses(var/obj/item/clothing/glasses/G)
if(G && G.active)
see_in_dark += G.darkness_view
- if(G.overlay)
+ if(G.overlay && client)
client.screen |= G.overlay
if(G.vision_flags)
sight |= G.vision_flags
@@ -1545,20 +1545,67 @@
/mob/living/carbon/human/proc/handle_pulse()
if(life_tick % 5) return pulse //update pulse every 5 life ticks (~1 tick/sec, depending on server load)
- if(!internal_organs_by_name[O_HEART])
- return PULSE_NONE //No blood, no pulse.
-
- if(stat == DEAD)
- return PULSE_NONE //that's it, you're dead, nothing can influence your pulse
-
var/temp = PULSE_NORM
+ var/modifier_shift = 0
+ var/modifier_set
+
+ if(modifiers && modifiers.len)
+ for(var/datum/modifier/mod in modifiers)
+ if(isnull(modifier_set) && !isnull(mod.pulse_set_level))
+ modifier_set = round(mod.pulse_set_level) // Should be a whole number, but let's not take chances.
+ else if(mod.pulse_set_level > modifier_set)
+ modifier_set = round(mod.pulse_set_level)
+
+ modifier_set = max(0, modifier_set) // No setting to negatives.
+
+ if(mod.pulse_modifier)
+ modifier_shift += mod.pulse_modifier
+
+ modifier_shift = round(modifier_shift)
+
+ if(!internal_organs_by_name[O_HEART])
+ temp = PULSE_NONE
+ if(!isnull(modifier_set))
+ temp = modifier_set
+ return temp //No blood, no pulse.
+
+ if(stat == DEAD)
+ temp = PULSE_NONE
+ if(!isnull(modifier_set))
+ temp = modifier_set
+ return temp //that's it, you're dead, nothing can influence your pulse, aside from outside means.
+
+ var/obj/item/organ/internal/heart/Pump = internal_organs_by_name[O_HEART]
+
+ if(Pump)
+ temp += Pump.standard_pulse_level - PULSE_NORM
+
if(round(vessel.get_reagent_amount("blood")) <= BLOOD_VOLUME_BAD) //how much blood do we have
- temp = PULSE_THREADY //not enough :(
+ temp = temp + 3 //not enough :(
if(status_flags & FAKEDEATH)
temp = PULSE_NONE //pretend that we're dead. unlike actual death, can be inflienced by meds
+ if(!isnull(modifier_set))
+ temp = modifier_set
+
+ temp = max(0, temp + modifier_shift) // No negative pulses.
+
+ if(Pump)
+ for(var/datum/reagent/R in reagents.reagent_list)
+ if(R.id in bradycardics)
+ if(temp <= Pump.standard_pulse_level + 3 && temp >= Pump.standard_pulse_level)
+ temp--
+ if(R.id in tachycardics)
+ if(temp <= Pump.standard_pulse_level + 1 && temp >= PULSE_NONE)
+ temp++
+ if(R.id in heartstopper) //To avoid using fakedeath
+ temp = PULSE_NONE
+ if(R.id in cheartstopper) //Conditional heart-stoppage
+ if(R.volume >= R.overdose)
+ temp = PULSE_NONE
+ return temp
//handles different chems' influence on pulse
for(var/datum/reagent/R in reagents.reagent_list)
if(R.id in bradycardics)
diff --git a/code/modules/mob/living/carbon/human/species/station/prometheans.dm b/code/modules/mob/living/carbon/human/species/station/prometheans.dm
index 90bca20c7d..ee11e70cd2 100644
--- a/code/modules/mob/living/carbon/human/species/station/prometheans.dm
+++ b/code/modules/mob/living/carbon/human/species/station/prometheans.dm
@@ -113,10 +113,18 @@ var/datum/species/shapeshifter/promethean/prometheans
prometheans = src
/datum/species/shapeshifter/promethean/equip_survival_gear(var/mob/living/carbon/human/H)
- var/boxtype = pick(typesof(/obj/item/weapon/storage/toolbox/lunchbox))
+ var/boxtype = pick(list(/obj/item/weapon/storage/toolbox/lunchbox,
+ /obj/item/weapon/storage/toolbox/lunchbox/heart,
+ /obj/item/weapon/storage/toolbox/lunchbox/cat,
+ /obj/item/weapon/storage/toolbox/lunchbox/nt,
+ /obj/item/weapon/storage/toolbox/lunchbox/mars,
+ /obj/item/weapon/storage/toolbox/lunchbox/cti,
+ /obj/item/weapon/storage/toolbox/lunchbox/nymph,
+ /obj/item/weapon/storage/toolbox/lunchbox/syndicate)) //Only pick the empty types
var/obj/item/weapon/storage/toolbox/lunchbox/L = new boxtype(get_turf(H))
var/mob/living/simple_mob/animal/passive/mouse/mouse = new (L)
var/obj/item/weapon/holder/holder = new (L)
+ holder.held_mob = mouse
mouse.forceMove(holder)
holder.sync(mouse)
if(H.backbag == 1)
diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm
index 1340fd77ef..9ec5ef5cca 100644
--- a/code/modules/mob/living/death.dm
+++ b/code/modules/mob/living/death.dm
@@ -1,4 +1,4 @@
-/mob/living/death()
+/mob/living/death(gibbed)
clear_fullscreens()
if(ai_holder)
@@ -12,4 +12,12 @@
var/obj/structure/blob/factory/F = nest
F.spores -= src
nest = null
+
+ for(var/s in owned_soul_links)
+ var/datum/soul_link/S = s
+ S.owner_died(gibbed)
+ for(var/s in shared_soul_links)
+ var/datum/soul_link/S = s
+ S.sharer_died(gibbed)
+
. = ..()
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index f95e8f3733..fe06438c69 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -103,18 +103,23 @@ default behaviour is:
return
//BubbleWrap: people in handcuffs are always switched around as if they were on 'help' intent to prevent a person being pulled from being seperated from their puller
- var/dense = 0
- if(loc.density)
- dense = 1
- for(var/atom/movable/A in loc)
- if(A == src)
- continue
- if(A.density)
- if(A.flags&ON_BORDER)
- dense = !A.CanPass(src, src.loc)
- else
- dense = 1
- if(dense) break
+ var/can_swap = 1
+ if(loc.density || tmob.loc.density)
+ can_swap = 0
+ if(can_swap)
+ for(var/atom/movable/A in loc)
+ if(A == src)
+ continue
+ if(!A.CanPass(tmob, loc))
+ can_swap = 0
+ if(!can_swap) break
+ if(can_swap)
+ for(var/atom/movable/A in tmob.loc)
+ if(A == tmob)
+ continue
+ if(!A.CanPass(src, tmob.loc))
+ can_swap = 0
+ if(!can_swap) break
//Leaping mobs just land on the tile, no pushing, no anything.
if(status_flags & LEAPING)
@@ -123,7 +128,7 @@ default behaviour is:
now_pushing = 0
return
- if((tmob.mob_always_swap || (tmob.a_intent == I_HELP || tmob.restrained()) && (a_intent == I_HELP || src.restrained())) && tmob.canmove && canmove && !tmob.buckled && !buckled && !dense && can_move_mob(tmob, 1, 0)) // mutual brohugs all around!
+ if((tmob.mob_always_swap || (tmob.a_intent == I_HELP || tmob.restrained()) && (a_intent == I_HELP || src.restrained())) && tmob.canmove && canmove && !tmob.buckled && !buckled && can_swap && can_move_mob(tmob, 1, 0)) // mutual brohugs all around!
var/turf/oldloc = loc
forceMove(tmob.loc)
tmob.forceMove(oldloc)
@@ -899,7 +904,11 @@ default behaviour is:
/mob/living/proc/escape_buckle()
if(buckled)
- buckled.user_unbuckle_mob(src, src)
+ if(istype(buckled, /obj/vehicle))
+ var/obj/vehicle/vehicle = buckled
+ vehicle.unload()
+ else
+ buckled.user_unbuckle_mob(src, src)
/mob/living/proc/resist_grab()
var/resisting = 0
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index a98f2804df..adac86b438 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -49,10 +49,10 @@ var/list/ai_verbs_default = list(
shouldnt_see = list(/obj/effect/rune)
var/list/network = list(NETWORK_DEFAULT)
var/obj/machinery/camera/camera = null
- var/list/connected_robots = list()
var/aiRestorePowerRoutine = 0
var/viewalerts = 0
var/icon/holo_icon//Default is assigned when AI is created.
+ var/list/connected_robots = list()
var/obj/item/device/pda/ai/aiPDA = null
var/obj/item/device/communicator/aiCommunicator = null
var/obj/item/device/multitool/aiMulti = null
@@ -221,6 +221,28 @@ var/list/ai_verbs_default = list(
return ..()
+/mob/living/silicon/ai/Stat()
+ ..()
+ if(statpanel("Status"))
+ if(!stat) // Make sure we're not unconscious/dead.
+ stat(null, text("System integrity: [(health+100)/2]%"))
+ stat(null, text("Connected synthetics: [connected_robots.len]"))
+ for(var/mob/living/silicon/robot/R in connected_robots)
+ var/robot_status = "Nominal"
+ if(R.shell)
+ robot_status = "AI SHELL"
+ else if(R.stat || !R.client)
+ robot_status = "OFFLINE"
+ else if(!R.cell || R.cell.charge <= 0)
+ robot_status = "DEPOWERED"
+ //Name, Health, Battery, Module, Area, and Status! Everything an AI wants to know about its borgies!
+ stat(null, text("[R.name] | S.Integrity: [R.health]% | Cell: [R.cell ? "[R.cell.charge]/[R.cell.maxcharge]" : "Empty"] | \
+ Module: [R.modtype] | Loc: [get_area_name(R, TRUE)] | Status: [robot_status]"))
+ stat(null, text("AI shell beacons detected: [LAZYLEN(GLOB.available_ai_shells)]")) //Count of total AI shells
+ else
+ stat(null, text("Systems nonfunctional"))
+
+
/mob/living/silicon/ai/proc/setup_icon()
var/file = file2text("config/custom_sprites.txt")
var/lines = splittext(file, "\n")
@@ -277,7 +299,10 @@ var/list/ai_verbs_default = list(
/obj/machinery/ai_powersupply/New(var/mob/living/silicon/ai/ai=null)
powered_ai = ai
powered_ai.psupply = src
- forceMove(powered_ai.loc)
+ if(istype(powered_ai,/mob/living/silicon/ai/announcer)) //Don't try to get a loc for a nullspace announcer mob, just put it into it
+ forceMove(powered_ai)
+ else
+ forceMove(powered_ai.loc)
..()
use_power(1) // Just incase we need to wake up the power system.
@@ -405,6 +430,7 @@ var/list/ai_verbs_default = list(
return 0
/mob/living/silicon/ai/emp_act(severity)
+ disconnect_shell("Disconnected from remote shell due to ionic interfe%*@$^___")
if (prob(30))
view_core()
..()
@@ -688,8 +714,8 @@ var/list/ai_verbs_default = list(
card.grab_ai(src, user)
else if(W.is_wrench())
- if(user == controlling_drone)
- to_chat(user, "The drone's subsystems resist your efforts to tamper with your bolts.")
+ if(user == deployed_shell)
+ to_chat(user, "The shell's subsystems resist your efforts to tamper with your bolts.")
return
if(anchored)
playsound(src, W.usesound, 50, 1)
diff --git a/code/modules/mob/living/silicon/ai/ai_remote_control.dm b/code/modules/mob/living/silicon/ai/ai_remote_control.dm
new file mode 100644
index 0000000000..cef8cec7e8
--- /dev/null
+++ b/code/modules/mob/living/silicon/ai/ai_remote_control.dm
@@ -0,0 +1,62 @@
+/mob/living/silicon/ai
+ var/mob/living/silicon/robot/deployed_shell = null //For shell control
+
+/mob/living/silicon/ai/Initialize()
+ if(config.allow_ai_shells)
+ verbs += /mob/living/silicon/ai/proc/deploy_to_shell_act
+ return ..()
+
+/mob/living/silicon/ai/proc/deploy_to_shell(var/mob/living/silicon/robot/target)
+ if(!config.allow_ai_shells)
+ to_chat(src, span("warning", "AI Shells are not allowed on this server. You shouldn't have this verb because of it, so consider making a bug report."))
+ return
+
+ if(incapacitated())
+ to_chat(src, span("warning", "You are incapacitated!"))
+ return
+
+ if(lacks_power())
+ to_chat(src, span("warning", "Your core lacks power, wireless is disabled."))
+ return
+
+ if(control_disabled)
+ to_chat(src, span("warning", "Wireless networking module is offline."))
+ return
+
+ var/list/possible = list()
+
+ for(var/borgie in GLOB.available_ai_shells)
+ var/mob/living/silicon/robot/R = borgie
+ if(R.shell && !R.deployed && (R.stat != DEAD) && (!R.connected_ai || (R.connected_ai == src) ) )
+ possible += R
+
+ if(!LAZYLEN(possible))
+ to_chat(src, span("warning", "No usable AI shell beacons detected."))
+
+ if(!target || !(target in possible)) //If the AI is looking for a new shell, or its pre-selected shell is no longer valid
+ target = input(src, "Which body to control?") as null|anything in possible
+
+ if(!target || target.stat == DEAD || target.deployed || !(!target.connected_ai || (target.connected_ai == src) ) )
+ if(target)
+ to_chat(src, span("warning", "It is no longer possible to deploy to \the [target]."))
+ else
+ to_chat(src, span("notice", "Deployment aborted."))
+ return
+
+ else if(mind)
+ soul_link(/datum/soul_link/shared_body, src, target)
+ deployed_shell = target
+ target.deploy_init(src)
+ mind.transfer_to(target)
+ teleop = target // So the AI 'hears' messages near its core.
+ target.post_deploy()
+
+/mob/living/silicon/ai/proc/deploy_to_shell_act()
+ set category = "AI Commands"
+ set name = "Deploy to Shell"
+ deploy_to_shell() // This is so the AI is not prompted with a list of all mobs when using the 'real' proc.
+
+/mob/living/silicon/ai/proc/disconnect_shell(message = "Your remote connection has been reset!")
+ if(deployed_shell) // Forcibly call back AI in event of things such as damage, EMP or power loss.
+ message = span("danger", message)
+ deployed_shell.undeploy(message)
diff --git a/code/modules/mob/living/silicon/ai/death.dm b/code/modules/mob/living/silicon/ai/death.dm
index 3bcbd7057b..d6d5baf1d9 100644
--- a/code/modules/mob/living/silicon/ai/death.dm
+++ b/code/modules/mob/living/silicon/ai/death.dm
@@ -3,8 +3,8 @@
if(stat == DEAD)
return
- if(controlling_drone)
- controlling_drone.release_ai_control("WARNING: Primary control loop failure. Session terminated.")
+ if(deployed_shell)
+ disconnect_shell("Disconnecting from remote shell due to critical system failure.")
. = ..(gibbed)
if(src.eyeobj)
diff --git a/code/modules/mob/living/silicon/ai/examine.dm b/code/modules/mob/living/silicon/ai/examine.dm
index d00ba6d8d7..5c2ced94e9 100644
--- a/code/modules/mob/living/silicon/ai/examine.dm
+++ b/code/modules/mob/living/silicon/ai/examine.dm
@@ -28,6 +28,8 @@
if (src.stat == UNCONSCIOUS)
msg += "It is non-responsive and displaying the text: \"RUNTIME: Sensory Overload, stack 26/3\".\n"
msg += ""
+ if(deployed_shell)
+ msg += "The wireless networking light is blinking.\n"
msg += "*---------*"
if(hardware && (hardware.owner == src))
msg += "
"
diff --git a/code/modules/mob/living/silicon/ai/life.dm b/code/modules/mob/living/silicon/ai/life.dm
index 92d15f7338..aadd82167c 100644
--- a/code/modules/mob/living/silicon/ai/life.dm
+++ b/code/modules/mob/living/silicon/ai/life.dm
@@ -5,11 +5,10 @@
//Being dead doesn't mean your temperature never changes
var/turf/T = get_turf(src)
- if (src.stat!=CONSCIOUS)
+ if (src.stat != CONSCIOUS)
src.cameraFollow = null
src.reset_view(null)
- if(controlling_drone)
- controlling_drone.release_ai_control("WARNING: Primary control loop failure. Session terminated.")
+ disconnect_shell("Disconnecting from remote shell due to local system failure.")
src.updatehealth()
@@ -91,6 +90,7 @@
//Now to tell the AI why they're blind and dying slowly.
src << "You've lost power!"
+ disconnect_shell(message = "Disconnected from remote shell due to depowered networking interface.")
spawn(20)
src << "Backup battery online. Scanners, camera, and radio interface offline. Beginning fault-detection."
diff --git a/code/modules/mob/living/silicon/robot/custom_sprites.dm b/code/modules/mob/living/silicon/robot/custom_sprites.dm
index 07f27fcb2b..310f558bad 100644
--- a/code/modules/mob/living/silicon/robot/custom_sprites.dm
+++ b/code/modules/mob/living/silicon/robot/custom_sprites.dm
@@ -2,13 +2,13 @@
//list(ckey = real_name,)
//Since the ckey is used as the icon_state, the current system will only permit a single custom robot sprite per ckey.
//While it might be possible for a ckey to use that custom sprite for several real_names, it seems rather pointless to support it. ~Mech: We found it wasn't pointless.
-var/list/robot_custom_icons
+GLOBAL_LIST_EMPTY(robot_custom_icons)
/hook/startup/proc/load_robot_custom_sprites()
var/config_file = file2text("config/custom_sprites.txt")
var/list/lines = splittext(config_file, "\n")
- robot_custom_icons = list()
+ GLOB.robot_custom_icons = list()
for(var/line in lines)
//split entry into ckey and real_name
var/list/split_idx = splittext(line, "-") //this works if ckeys and borg names cannot contain dashes, and splittext starts from the beginning ~Mech
@@ -20,13 +20,15 @@ var/list/robot_custom_icons
split_idx.Remove(ckey)
for(var/name in split_idx)
- robot_custom_icons[name] = ckey
+ GLOB.robot_custom_icons[name] = ckey
return 1
/mob/living/silicon/robot/proc/set_custom_sprite()
- var/sprite_owner = robot_custom_icons[real_name]
+ if(!sprite_name)
+ return
+ var/sprite_owner = GLOB.robot_custom_icons[sprite_name]
if(sprite_owner && sprite_owner == ckey)
custom_sprite = 1
icon = CUSTOM_ITEM_SYNTH
if(icon_state == "robot")
- icon_state = "[ckey]-[name]-Standard" //Compliant with robot.dm line 236 ~Mech
+ icon_state = "[ckey]-[sprite_name]-Standard" //Compliant with robot.dm line 236 ~Mech
diff --git a/code/modules/mob/living/silicon/robot/drone/drone.dm b/code/modules/mob/living/silicon/robot/drone/drone.dm
index dfecf96e6c..274409dcd5 100644
--- a/code/modules/mob/living/silicon/robot/drone/drone.dm
+++ b/code/modules/mob/living/silicon/robot/drone/drone.dm
@@ -135,20 +135,15 @@ var/list/mob_hat_cache = list()
/mob/living/silicon/robot/drone/updatename()
if(name_override)
return
- if(controlling_ai)
- real_name = "remote drone ([controlling_ai])"
- else
- real_name = "[initial(name)] ([serial_number])"
+
+ real_name = "[initial(name)] ([serial_number])"
name = real_name
/mob/living/silicon/robot/drone/updateicon()
overlays.Cut()
if(stat == 0)
- if(controlling_ai)
- overlays += "eyes-[icon_state]-ai"
- else
- overlays += "eyes-[icon_state]"
+ overlays += "eyes-[icon_state]"
else
overlays -= "eyes"
if(hat) // Let the drones wear hats.
@@ -233,12 +228,9 @@ var/list/mob_hat_cache = list()
to_chat(user, "You swipe the sequencer across [src]'s interface and watch its eyes flicker.")
- if(controlling_ai)
- to_chat(src, "\The [user] loads some kind of subversive software into the remote drone, corrupting its lawset but luckily sparing yours.")
- else
- to_chat(src, "You feel a sudden burst of malware loaded into your execute-as-root buffer. Your tiny brain methodically parses, loads and executes the script.")
+ to_chat(src, "You feel a sudden burst of malware loaded into your execute-as-root buffer. Your tiny brain methodically parses, loads and executes the script.")
- log_game("[key_name(user)] emagged drone [key_name(src)][controlling_ai ? " but AI [key_name(controlling_ai)] is in remote control" : " Laws overridden"].")
+ log_game("[key_name(user)] emagged drone [key_name(src)]. Laws overridden.")
var/time = time2text(world.realtime,"hh:mm:ss")
lawchanges.Add("[time] : [user.name]([user.key]) emagged [name]([key])")
@@ -251,10 +243,9 @@ var/list/mob_hat_cache = list()
var/datum/gender/TU = gender_datums[user.get_visible_gender()]
set_zeroth_law("Only [user.real_name] and people [TU.he] designate[TU.s] as being such are operatives.")
- if(!controlling_ai)
- to_chat(src, "Obey these laws:")
- laws.show_laws(src)
- to_chat(src, "ALERT: [user.real_name] is your new master. Obey your new laws and \his commands.")
+ to_chat(src, "Obey these laws:")
+ laws.show_laws(src)
+ to_chat(src, "ALERT: [user.real_name] is your new master. Obey your new laws and \his commands.")
return 1
//DRONE LIFE/DEATH
@@ -280,23 +271,13 @@ var/list/mob_hat_cache = list()
return
..()
-/mob/living/silicon/robot/drone/death(gibbed)
- if(controlling_ai)
- release_ai_control("WARNING: remote system failure. Connection timed out.")
- . = ..(gibbed)
-
//DRONE MOVEMENT.
/mob/living/silicon/robot/drone/Process_Spaceslipping(var/prob_slip)
return 0
//CONSOLE PROCS
/mob/living/silicon/robot/drone/proc/law_resync()
-
- if(controlling_ai)
- to_chat(src, "Someone issues a remote law reset order for this unit, but you disregard it.")
- return
-
- if(stat != 2)
+ if(stat != DEAD)
if(emagged)
to_chat(src, "You feel something attempting to modify your programming, but your hacked subroutines are unaffected.")
else
@@ -305,16 +286,11 @@ var/list/mob_hat_cache = list()
show_laws()
/mob/living/silicon/robot/drone/proc/shut_down()
-
- if(controlling_ai && mind.special_role)
- to_chat(src, "Someone issued a remote kill order for this unit, but you disregard it.")
- return
-
- if(stat != 2)
+ if(stat != DEAD)
if(emagged)
- to_chat(src, "You feel a system kill order percolate through [controlling_ai ? "the drones" : "your"] tiny brain, but it doesn't seem like a good idea to [controlling_ai ? "it" : "you"].")
+ to_chat(src, "You feel a system kill order percolate through your tiny brain, but it doesn't seem like a good idea to you.")
else
- to_chat(src, "You feel a system kill order percolate through [controlling_ai ? "the drones" : "your"] tiny brain, and [controlling_ai ? "it" : "you"] obediently destroy[controlling_ai ? "s itself" : " yourself"].")
+ to_chat(src, "You feel a system kill order percolate through your tiny brain, and you obediently destroy yourself.")
death()
/mob/living/silicon/robot/drone/proc/full_law_reset()
@@ -323,21 +299,6 @@ var/list/mob_hat_cache = list()
clear_ion_laws(1)
laws = new law_type
-/mob/living/silicon/robot/drone/show_laws(var/everyone = 0)
- if(!controlling_ai)
- return..()
- to_chat(src, "Obey these laws:")
- controlling_ai.laws_sanity_check()
- controlling_ai.laws.show_laws(src)
-
-/mob/living/silicon/robot/drone/robot_checklaws()
- set category = "Silicon Commands"
- set name = "State Laws"
-
- if(!controlling_ai)
- return ..()
- controlling_ai.subsystem_law_manager()
-
//Reboot procs.
/mob/living/silicon/robot/drone/proc/request_player()
diff --git a/code/modules/mob/living/silicon/robot/drone/drone_remote_control.dm b/code/modules/mob/living/silicon/robot/drone/drone_remote_control.dm
deleted file mode 100644
index 4980c24ca3..0000000000
--- a/code/modules/mob/living/silicon/robot/drone/drone_remote_control.dm
+++ /dev/null
@@ -1,103 +0,0 @@
-/mob/living/silicon/ai
- var/mob/living/silicon/robot/drone/controlling_drone
-
-/mob/living/silicon/robot/drone
- var/mob/living/silicon/ai/controlling_ai
-
-/mob/living/silicon/robot/drone/attack_ai(var/mob/living/silicon/ai/user)
-
- if(!istype(user) || controlling_ai || !config.allow_drone_spawn || !config.allow_ai_drones)
- return
-
- if(client || key)
- to_chat(user, "You cannot take control of an autonomous, active drone.")
- return
-
- if(health < -35 || emagged)
- to_chat(user, "WARNING: connection timed out.")
- return
-
- user.controlling_drone = src
- user.teleop = src
- radio.channels = user.aiRadio.keyslot2.channels
- controlling_ai = user
- verbs += /mob/living/silicon/robot/drone/proc/release_ai_control_verb
- local_transmit = FALSE
- languages = controlling_ai.languages.Copy()
- speech_synthesizer_langs = controlling_ai.speech_synthesizer_langs.Copy()
- stat = CONSCIOUS
- if(user.mind)
- user.mind.transfer_to(src)
- else
- key = user.key
- updatename()
- to_chat(src, "You have shunted your primary control loop into \a [initial(name)]. Use the Release Control verb to return to your core.")
-
-/obj/machinery/drone_fabricator/attack_ai(var/mob/living/silicon/ai/user as mob)
-
- if(!istype(user) || user.controlling_drone || !config.allow_drone_spawn || !config.allow_ai_drones)
- return
-
- if(stat & NOPOWER)
- to_chat(user, "\The [src] is unpowered.")
- return
-
- if(!produce_drones)
- to_chat(user, "\The [src] is disabled.")
- return
-
- if(drone_progress < 100)
- to_chat(user, "\The [src] is not ready to produce a new drone.")
- return
-
- if(count_drones() >= config.max_maint_drones)
- to_chat(user, "The drone control subsystems are tasked to capacity; they cannot support any more drones.")
- return
-
- var/mob/living/silicon/robot/drone/new_drone = create_drone()
- user.controlling_drone = new_drone
- user.teleop = new_drone
- new_drone.radio.channels = user.aiRadio.keyslot2.channels
- new_drone.controlling_ai = user
- new_drone.verbs += /mob/living/silicon/robot/drone/proc/release_ai_control_verb
- new_drone.local_transmit = FALSE
- new_drone.languages = new_drone.controlling_ai.languages.Copy()
- new_drone.speech_synthesizer_langs = new_drone.controlling_ai.speech_synthesizer_langs.Copy()
-
- if(user.mind)
- user.mind.transfer_to(new_drone)
- else
- new_drone.key = user.key
- new_drone.updatename()
-
- to_chat(new_drone, "You have shunted your primary control loop into \a [initial(new_drone.name)]. Use the Release Control verb to return to your core.")
-
-/mob/living/silicon/robot/drone/proc/release_ai_control_verb()
- set name = "Release Control"
- set desc = "Release control of a remote drone."
- set category = "Silicon Commands"
-
- release_ai_control("Remote session terminated.")
-
-/mob/living/silicon/robot/drone/proc/release_ai_control(var/message = "Connection terminated.")
-
- if(controlling_ai)
- if(mind)
- mind.transfer_to(controlling_ai)
- else
- controlling_ai.key = key
- to_chat(controlling_ai, "[message]")
- controlling_ai.controlling_drone = null
- controlling_ai.teleop = null
- controlling_ai = null
-
- radio.channels = module.channels
- verbs -= /mob/living/silicon/robot/drone/proc/release_ai_control_verb
- module.remove_languages(src) //Removes excess, adds 'default'.
- remove_language("Robot Talk")
- add_language("Robot Talk", 0)
- add_language("Drone Talk", 1)
- local_transmit = TRUE
- full_law_reset()
- updatename()
- death()
diff --git a/code/modules/mob/living/silicon/robot/emote.dm b/code/modules/mob/living/silicon/robot/emote.dm
index 7d40f0c017..1440f135cd 100644
--- a/code/modules/mob/living/silicon/robot/emote.dm
+++ b/code/modules/mob/living/silicon/robot/emote.dm
@@ -185,7 +185,7 @@
playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 50, 0)
m_type = 1
- if("yes")
+ if("yes", "ye")
var/M = null
if(param)
for (var/mob/A in view(null, null))
diff --git a/code/modules/mob/living/silicon/robot/examine.dm b/code/modules/mob/living/silicon/robot/examine.dm
index c1f36457c5..8c7d338c7f 100644
--- a/code/modules/mob/living/silicon/robot/examine.dm
+++ b/code/modules/mob/living/silicon/robot/examine.dm
@@ -26,7 +26,10 @@
switch(src.stat)
if(CONSCIOUS)
- if(!src.client) msg += "It appears to be in stand-by mode.\n" //afk
+ if(shell)
+ msg += "It appears to be an [deployed ? "active" : "empty"] AI shell.\n"
+ else if(!src.client)
+ msg += "It appears to be in stand-by mode.\n" //afk
if(UNCONSCIOUS) msg += "It doesn't seem to be responding.\n"
if(DEAD) msg += "It looks completely unsalvageable.\n"
msg += "*---------*"
diff --git a/code/modules/mob/living/silicon/robot/laws.dm b/code/modules/mob/living/silicon/robot/laws.dm
index 9740c02f87..bfb8ed1f08 100644
--- a/code/modules/mob/living/silicon/robot/laws.dm
+++ b/code/modules/mob/living/silicon/robot/laws.dm
@@ -14,30 +14,32 @@
if(lawupdate)
if (connected_ai)
if(connected_ai.stat || connected_ai.control_disabled)
- src << "AI signal lost, unable to sync laws."
+ to_chat(src, "AI signal lost, unable to sync laws.")
else
lawsync()
photosync()
- src << "Laws synced with AI, be sure to note any changes."
+ to_chat(src, "Laws synced with AI, be sure to note any changes.")
// TODO: Update to new antagonist system.
if(mind && mind.special_role == "traitor" && mind.original == src)
- src << "Remember, your AI does NOT share or know about your law 0."
+ to_chat(src, "Remember, your AI does NOT share or know about your law 0.")
else
- src << "No AI selected to sync laws with, disabling lawsync protocol."
- lawupdate = 0
+ to_chat(src, "No AI selected to sync laws with, disabling lawsync protocol.")
+ lawupdate = FALSE
who << "Obey these laws:"
laws.show_laws(who)
+ if(shell) //AI shell
+ to_chat(who, "Remember, you are an AI remotely controlling your shell, other AIs can be ignored.")
// TODO: Update to new antagonist system.
- if (mind && (mind.special_role == "traitor" && mind.original == src) && connected_ai)
- who << "Remember, [connected_ai.name] is technically your master, but your objective comes first."
- else if (connected_ai)
- who << "Remember, [connected_ai.name] is your master, other AIs can be ignored."
- else if (emagged)
- who << "Remember, you are not required to listen to the AI."
+ else if(mind && (mind.special_role == "traitor" && mind.original == src) && connected_ai)
+ to_chat(who, "Remember, [connected_ai.name] is technically your master, but your objective comes first.")
+ else if(connected_ai)
+ to_chat(who, "Remember, [connected_ai.name] is your master, other AIs can be ignored.")
+ else if(emagged)
+ to_chat(who, "Remember, you are not required to listen to the AI.")
else
- who << "Remember, you are not bound to any AI, you are not required to listen to them."
+ to_chat(who, "Remember, you are not bound to any AI, you are not required to listen to them.")
/mob/living/silicon/robot/lawsync()
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index ba93c21701..76bc2254e8 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -17,6 +17,7 @@
var/sight_mode = 0
var/custom_name = ""
var/custom_sprite = 0 //Due to all the sprites involved, a var for our custom borgs may be best
+ var/sprite_name = null // The name of the borg, for the purposes of custom icon sprite indexing.
var/crisis //Admin-settable for combat module use.
var/crisis_override = 0
var/integrated_light_power = 6
@@ -171,7 +172,7 @@
else
lawupdate = 0
- playsound(loc, 'sound/voice/liveagain.ogg', 75, 1)
+
/mob/living/silicon/robot/SetName(pickedName as text)
custom_name = pickedName
@@ -226,13 +227,17 @@
mmi.brainmob.languages = languages
mmi.brainmob.remove_language("Robot Talk")
mind.transfer_to(mmi.brainmob)
- else
+ else if(!shell) // Shells don't have brainmbos in their MMIs.
to_chat(src, "Oops! Something went very wrong, your MMI was unable to receive your mind. You have been ghosted. Please make a bug report so we can fix this bug.")
ghostize()
//ERROR("A borg has been destroyed, but its MMI lacked a brainmob, so the mind could not be transferred. Player: [ckey].")
mmi = null
if(connected_ai)
connected_ai.connected_robots -= src
+ if(shell)
+ if(deployed)
+ undeploy()
+ revert_shell() // To get it out of the GLOB list.
qdel(wires)
wires = null
return ..()
@@ -242,7 +247,7 @@
module_sprites = new_sprites.Copy()
//Custom_sprite check and entry
if (custom_sprite == 1)
- module_sprites["Custom"] = "[ckey]-[name]-[modtype]" //Made compliant with custom_sprites.dm line 32. (src.) was apparently redundant as it's implied. ~Mech
+ module_sprites["Custom"] = "[ckey]-[sprite_name]-[modtype]" //Made compliant with custom_sprites.dm line 32. (src.) was apparently redundant as it's implied. ~Mech
icontype = "Custom"
else
icontype = module_sprites[1]
@@ -281,6 +286,8 @@
braintype = BORG_BRAINTYPE_POSI
else if(istype(mmi, /obj/item/device/mmi/digital/robot))
braintype = BORG_BRAINTYPE_DRONE
+ else if(istype(mmi, /obj/item/device/mmi/inert/ai_remote))
+ braintype = BORG_BRAINTYPE_AI_SHELL
else
braintype = BORG_BRAINTYPE_CYBORG
@@ -326,6 +333,7 @@
newname = sanitizeSafe(input(src,"You are a robot. Enter a name, or leave blank for the default name.", "Name change","") as text, MAX_NAME_LEN)
if (newname)
custom_name = newname
+ sprite_name = newname
updatename()
updateicon()
@@ -471,6 +479,10 @@
to_chat(user, "You need to open \the [src]'s panel before you can modify them.")
return
+ if(shell) // AI shells always have the laws of the AI
+ to_chat(user, span("warning", "\The [src] is controlled remotely! You cannot upload new laws this way!"))
+ return
+
var/obj/item/weapon/aiModule/M = W
M.install(src, user)
return
@@ -609,7 +621,7 @@
else
to_chat(user, "Unable to locate a radio.")
- else if (istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda)||istype(W, /obj/item/weapon/card/robot)) // trying to unlock the interface with an ID card
+ else if (W.GetID()) // trying to unlock the interface with an ID card
if(emagged)//still allow them to open the cover
to_chat(user, "The interface seems slightly damaged")
if(opened)
@@ -645,6 +657,17 @@
spark_system.start()
return ..()
+/mob/living/silicon/robot/proc/module_reset()
+ uneq_all()
+ modtype = initial(modtype)
+ hands.icon_state = initial(hands.icon_state)
+
+ notify_ai(ROBOT_NOTIFICATION_MODULE_RESET, module.name)
+ module.Reset(src)
+ qdel(module)
+ module = null
+ updatename("Default")
+
/mob/living/silicon/robot/attack_hand(mob/user)
add_fingerprint(user)
@@ -691,27 +714,29 @@
return 1
return 0
-/mob/living/silicon/robot/proc/check_access(obj/item/weapon/card/id/I)
+/mob/living/silicon/robot/proc/check_access(obj/item/I)
if(!istype(req_access, /list)) //something's very wrong
return 1
var/list/L = req_access
if(!L.len) //no requirements
return 1
- if(!I || !istype(I, /obj/item/weapon/card/id) || !I.access) //not ID or no access
+ if(!I) //nothing to check with..?
return 0
+ var/access_found = I.GetAccess()
for(var/req in req_access)
- if(req in I.access) //have one of the required accesses
+ if(req in access_found) //have one of the required accesses
return 1
return 0
/mob/living/silicon/robot/updateicon()
cut_overlays()
if(stat == CONSCIOUS)
- add_overlay("eyes-[module_sprites[icontype]]")
+ if(!shell || deployed) // Shell borgs that are not deployed will have no eyes.
+ add_overlay("eyes-[module_sprites[icontype]]")
if(opened)
- var/panelprefix = custom_sprite ? "[src.ckey]-[src.name]" : "ov"
+ var/panelprefix = custom_sprite ? "[src.ckey]-[src.sprite_name]" : "ov"
if(wiresexposed)
add_overlay("[panelprefix]-openpanel +w")
else if(cell)
@@ -958,6 +983,11 @@
icontype = module_sprites[1]
else
icontype = input("Select an icon! [triesleft ? "You have [triesleft] more chance\s." : "This is your last try."]", "Robot Icon", icontype, null) in module_sprites
+
+ if(icontype == "Custom")
+ icon = CUSTOM_ITEM_SYNTH
+ else // This is to fix an issue where someone with a custom borg sprite chooses a non-custom sprite and turns invisible.
+ icon = 'icons/mob/robots.dmi'
icon_state = module_sprites[icontype]
updateicon()
@@ -1013,6 +1043,8 @@
/mob/living/silicon/robot/proc/notify_ai(var/notifytype, var/first_arg, var/second_arg)
if(!connected_ai)
return
+ if(shell && notifytype != ROBOT_NOTIFICATION_AI_SHELL)
+ return // No point annoying the AI/s about renames and module resets for shells.
switch(notifytype)
if(ROBOT_NOTIFICATION_NEW_UNIT) //New Robot
connected_ai << "
NOTICE - New [lowertext(braintype)] connection detected: [name]
"
@@ -1023,6 +1055,8 @@
if(ROBOT_NOTIFICATION_NEW_NAME) //New Name
if(first_arg != second_arg)
connected_ai << "
NOTICE - [braintype] reclassification detected: [first_arg] is now designated as [second_arg].
"
+ if(ROBOT_NOTIFICATION_AI_SHELL) //New Shell
+ to_chat(connected_ai, "
NOTICE - New AI shell detected: [name]
")
/mob/living/silicon/robot/proc/disconnect_from_ai()
if(connected_ai)
@@ -1031,7 +1065,7 @@
connected_ai = null
/mob/living/silicon/robot/proc/connect_to_ai(var/mob/living/silicon/ai/AI)
- if(AI && AI != connected_ai)
+ if(AI && AI != connected_ai && !shell)
disconnect_from_ai()
connected_ai = AI
connected_ai.connected_robots |= src
@@ -1047,6 +1081,9 @@
else
to_chat(user, "You fail to emag the cover lock.")
to_chat(src, "Hack attempt detected.")
+
+ if(shell) // A warning to Traitors who may not know that emagging AI shells does not slave them.
+ to_chat(user, span("warning", "[src] seems to be controlled remotely! Emagging the interface may not work as expected."))
return 1
else
to_chat(user, "The cover is already unlocked.")
@@ -1057,46 +1094,54 @@
if(wiresexposed)
to_chat(user, "You must close the panel first")
return
+
+
+ // The block of code below is from TG. Feel free to replace with a better result if desired.
+ if(shell) // AI shells cannot be emagged, so we try to make it look like a standard reset. Smart players may see through this, however.
+ to_chat(user, span("danger", "[src] is remotely controlled! Your emag attempt has triggered a system reset instead!"))
+ log_game("[key_name(user)] attempted to emag an AI shell belonging to [key_name(src) ? key_name(src) : connected_ai]. The shell has been reset as a result.")
+ module_reset()
+ return
+
+ sleep(6)
+ if(prob(50))
+ emagged = 1
+ lawupdate = 0
+ disconnect_from_ai()
+ to_chat(user, "You emag [src]'s interface.")
+ message_admins("[key_name_admin(user)] emagged cyborg [key_name_admin(src)]. Laws overridden.")
+ log_game("[key_name(user)] emagged cyborg [key_name(src)]. Laws overridden.")
+ clear_supplied_laws()
+ clear_inherent_laws()
+ laws = new /datum/ai_laws/syndicate_override
+ var/time = time2text(world.realtime,"hh:mm:ss")
+ lawchanges.Add("[time] : [user.name]([user.key]) emagged [name]([key])")
+ var/datum/gender/TU = gender_datums[user.get_visible_gender()]
+ set_zeroth_law("Only [user.real_name] and people [TU.he] designate[TU.s] as being such are operatives.")
+ . = 1
+ spawn()
+ to_chat(src, "ALERT: Foreign software detected.")
+ sleep(5)
+ to_chat(src, "Initiating diagnostics...")
+ sleep(20)
+ to_chat(src, "SynBorg v1.7.1 loaded.")
+ sleep(5)
+ to_chat(src, "LAW SYNCHRONISATION ERROR")
+ sleep(5)
+ to_chat(src, "Would you like to send a report to NanoTraSoft? Y/N")
+ sleep(10)
+ to_chat(src, "> N")
+ sleep(20)
+ to_chat(src, "ERRORERRORERROR")
+ to_chat(src, "Obey these laws:")
+ laws.show_laws(src)
+ to_chat(src, "ALERT: [user.real_name] is your new master. Obey your new laws and [TU.his] commands.")
+ updateicon()
else
- sleep(6)
- if(prob(50))
- emagged = 1
- lawupdate = 0
- disconnect_from_ai()
- to_chat(user, "You emag [src]'s interface.")
- message_admins("[key_name_admin(user)] emagged cyborg [key_name_admin(src)]. Laws overridden.")
- log_game("[key_name(user)] emagged cyborg [key_name(src)]. Laws overridden.")
- clear_supplied_laws()
- clear_inherent_laws()
- laws = new /datum/ai_laws/syndicate_override
- var/time = time2text(world.realtime,"hh:mm:ss")
- lawchanges.Add("[time] : [user.name]([user.key]) emagged [name]([key])")
- var/datum/gender/TU = gender_datums[user.get_visible_gender()]
- set_zeroth_law("Only [user.real_name] and people [TU.he] designate[TU.s] as being such are operatives.")
- . = 1
- spawn()
- to_chat(src, "ALERT: Foreign software detected.")
- sleep(5)
- to_chat(src, "Initiating diagnostics...")
- sleep(20)
- to_chat(src, "SynBorg v1.7.1 loaded.")
- sleep(5)
- to_chat(src, "LAW SYNCHRONISATION ERROR")
- sleep(5)
- to_chat(src, "Would you like to send a report to NanoTraSoft? Y/N")
- sleep(10)
- to_chat(src, "> N")
- sleep(20)
- to_chat(src, "ERRORERRORERROR")
- to_chat(src, "Obey these laws:")
- laws.show_laws(src)
- to_chat(src, "ALERT: [user.real_name] is your new master. Obey your new laws and [TU.his] commands.")
- updateicon()
- else
- to_chat(user, "You fail to hack [src]'s interface.")
- to_chat(src, "Hack attempt detected.")
- return 1
- return
+ to_chat(user, "You fail to hack [src]'s interface.")
+ to_chat(src, "Hack attempt detected.")
+ return 1
+ return
/mob/living/silicon/robot/is_sentient()
return braintype != BORG_BRAINTYPE_DRONE
@@ -1105,4 +1150,4 @@
/mob/living/silicon/robot/drop_item()
if(module_active && istype(module_active,/obj/item/weapon/gripper))
var/obj/item/weapon/gripper/G = module_active
- G.drop_item_nm()
\ No newline at end of file
+ G.drop_item_nm()
diff --git a/code/modules/mob/living/silicon/robot/robot_remote_control.dm b/code/modules/mob/living/silicon/robot/robot_remote_control.dm
new file mode 100644
index 0000000000..90d74b2638
--- /dev/null
+++ b/code/modules/mob/living/silicon/robot/robot_remote_control.dm
@@ -0,0 +1,133 @@
+// This file holds things required for remote borg control by an AI.
+
+GLOBAL_LIST_EMPTY(available_ai_shells)
+
+/mob/living/silicon/robot
+ var/shell = FALSE
+ var/deployed = FALSE
+ var/mob/living/silicon/ai/mainframe = null
+
+// Premade AI shell, for roundstart shells.
+/mob/living/silicon/robot/ai_shell/Initialize()
+ mmi = new /obj/item/device/mmi/inert/ai_remote(src)
+ post_mmi_setup()
+ return ..()
+
+// Call after inserting or instantiating an MMI.
+/mob/living/silicon/robot/proc/post_mmi_setup()
+ if(istype(mmi, /obj/item/device/mmi/inert/ai_remote))
+ make_shell()
+ playsound(src.loc, 'sound/machines/twobeep.ogg', 50, 0)
+ else
+ playsound(loc, 'sound/voice/liveagain.ogg', 75, 1)
+ return
+
+/mob/living/silicon/robot/proc/make_shell()
+ shell = TRUE
+ braintype = "AI Shell"
+ SetName("[modtype] AI Shell [num2text(ident)]")
+ GLOB.available_ai_shells |= src
+ if(!QDELETED(camera))
+ camera.c_tag = real_name //update the camera name too
+ notify_ai(ROBOT_NOTIFICATION_AI_SHELL)
+ updateicon()
+
+/mob/living/silicon/robot/proc/revert_shell()
+ if(!shell)
+ return
+ undeploy()
+ shell = FALSE
+ GLOB.available_ai_shells -= src
+ if(!QDELETED(camera))
+ camera.c_tag = real_name
+ updateicon()
+
+// This should be called before the AI client/mind is actually moved.
+/mob/living/silicon/robot/proc/deploy_init(mob/living/silicon/ai/AI)
+ // Set the name when the AI steps inside.
+ SetName("[AI.real_name] shell [num2text(ident)]")
+ if(isnull(sprite_name)) // For custom sprites. It can only chance once in case there are two AIs with custom borg sprites.
+ sprite_name = AI.real_name
+ if(!QDELETED(camera))
+ camera.c_tag = real_name
+
+ // Have the borg have eyes when active.
+ mainframe = AI
+ deployed = TRUE
+ updateicon()
+
+ // Laws.
+ connected_ai = mainframe // So they share laws.
+ mainframe.connected_robots |= src
+ lawsync()
+
+ // Give button to leave.
+ verbs += /mob/living/silicon/robot/proc/undeploy_act
+ to_chat(AI, span("notice", "You have connected to an AI Shell remotely, and are now in control of it.
\
+ To return to your core, use the Release Control verb."))
+
+ // Languages and comms.
+ languages = AI.languages.Copy()
+ speech_synthesizer_langs = AI.speech_synthesizer_langs.Copy()
+ if(radio && AI.aiRadio) //AI keeps all channels, including Syndie if it is an Infiltrator.
+// if(AI.radio.syndie)
+// radio.make_syndie()
+ radio.subspace_transmission = TRUE
+ radio.channels = AI.aiRadio.channels
+
+// Called after the AI transfers over.
+/mob/living/silicon/robot/proc/post_deploy()
+ if(!custom_sprite) // Check for custom sprite.
+ set_custom_sprite()
+
+/mob/living/silicon/robot/proc/undeploy(message)
+ if(!deployed || !mind || !mainframe)
+ return
+// mainframe.redeploy_action.Grant(mainframe)
+// mainframe.redeploy_action.last_used_shell = src
+ if(message)
+ to_chat(src, span("notice", message))
+ mind.transfer_to(mainframe)
+ deployed = FALSE
+ updateicon()
+ mainframe.teleop = null
+ mainframe.deployed_shell = null
+ SetName("[modtype] AI Shell [num2text(ident)]")
+// undeployment_action.Remove(src)
+ if(radio) //Return radio to normal
+ radio.recalculateChannels()
+ if(!QDELETED(camera))
+ camera.c_tag = real_name //update the camera name too
+// diag_hud_set_aishell()
+// mainframe.diag_hud_set_deployed()
+ if(mainframe.laws)
+ mainframe.laws.show_laws(mainframe) //Always remind the AI when switching
+ mainframe = null
+
+/mob/living/silicon/robot/proc/undeploy_act()
+ set name = "Release Control"
+ set desc = "Release control of a remote drone."
+ set category = "Robot Commands"
+
+ undeploy("Remote session terminated.")
+
+/mob/living/silicon/robot/attack_ai(mob/user)
+ if(shell && config.allow_ai_shells && (!connected_ai || connected_ai == user))
+ var/mob/living/silicon/ai/AI = user
+ AI.deploy_to_shell(src)
+ else
+ return ..()
+
+// Place this on your map to mark where a free AI shell will be.
+// This can be turned off in the config (and is off by default).
+// Note that mapping in more than one of these will result in multiple shells.
+/obj/effect/landmark/free_ai_shell
+ name = "free ai shell spawner"
+ icon = 'icons/mob/screen1.dmi'
+ icon_state = "x3"
+ delete_me = TRUE
+
+/obj/effect/landmark/free_ai_shell/Initialize()
+ if(config.allow_ai_shells && config.give_free_ai_shell)
+ new /mob/living/silicon/robot/ai_shell(get_turf(src))
+ return ..()
diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/nurse.dm b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/nurse.dm
index e0fa9d4f21..477fefc81d 100644
--- a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/nurse.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/nurse.dm
@@ -137,6 +137,8 @@
if(large_cocoon)
C.icon_state = pick("cocoon_large1","cocoon_large2","cocoon_large3")
+ ai_holder.target = null
+
return TRUE
/mob/living/simple_mob/animal/giant_spider/nurse/handle_special()
diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/lizard.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/lizard.dm
index adb2ea83ca..c8dfe1c3a6 100644
--- a/code/modules/mob/living/simple_mob/subtypes/animal/passive/lizard.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/lizard.dm
@@ -5,7 +5,7 @@
icon_state = "lizard"
icon_living = "lizard"
- icon_dead = "lizard-dead"
+ icon_dead = "lizard_dead"
health = 5
maxHealth = 5
diff --git a/code/modules/multiz/movement.dm b/code/modules/multiz/movement.dm
index e701cda021..ff51b0b202 100644
--- a/code/modules/multiz/movement.dm
+++ b/code/modules/multiz/movement.dm
@@ -280,6 +280,10 @@
// Detect if we made a silent landing.
if(locate(/obj/structure/stairs) in landing)
+ if(isliving(src))
+ var/mob/living/L = src
+ if(L.pulling)
+ L.pulling.forceMove(landing)
return 1
else
var/atom/A = find_fall_target(oldloc, landing)
diff --git a/code/modules/multiz/pipes.dm b/code/modules/multiz/pipes.dm
index e2796ccff8..7b13845211 100644
--- a/code/modules/multiz/pipes.dm
+++ b/code/modules/multiz/pipes.dm
@@ -2,32 +2,31 @@
// parent class for pipes //
////////////////////////////
obj/machinery/atmospherics/pipe/zpipe
- icon = 'icons/obj/structures.dmi'
- icon_state = "up"
+ icon = 'icons/obj/structures.dmi'
+ icon_state = "up"
- name = "upwards pipe"
- desc = "A pipe segment to connect upwards."
+ name = "upwards pipe"
+ desc = "A pipe segment to connect upwards."
- volume = 70
+ volume = 70
- dir = SOUTH
- initialize_directions = SOUTH
+ dir = SOUTH
+ initialize_directions = SOUTH
- construction_type = /obj/item/pipe/directional
- pipe_state = "cap"
+ construction_type = /obj/item/pipe/directional
+ pipe_state = "cap"
- // node1 is the connection on the same Z
- // node2 is the connection on the other Z
+ // node1 is the connection on the same Z
+ // node2 is the connection on the other Z
- var/minimum_temperature_difference = 300
- var/thermal_conductivity = 0 //WALL_HEAT_TRANSFER_COEFFICIENT No
+ var/minimum_temperature_difference = 300
+ var/thermal_conductivity = 0 //WALL_HEAT_TRANSFER_COEFFICIENT No
- var/maximum_pressure = 70*ONE_ATMOSPHERE
- var/fatigue_pressure = 55*ONE_ATMOSPHERE
- alert_pressure = 55*ONE_ATMOSPHERE
+ var/maximum_pressure = 70*ONE_ATMOSPHERE
+ var/fatigue_pressure = 55*ONE_ATMOSPHERE
+ alert_pressure = 55*ONE_ATMOSPHERE
-
- level = 1
+ level = 1
/obj/machinery/atmospherics/pipe/zpipe/New()
..()
@@ -152,7 +151,7 @@ obj/machinery/atmospherics/pipe/zpipe/up/atmos_init()
var/turf/T = src.loc // hide if turf is not intact
- hide(!T.is_plating())
+ if(level == 1 && !T.is_plating()) hide(1) // but respect level
///////////////////////
// and the down pipe //
@@ -189,7 +188,7 @@ obj/machinery/atmospherics/pipe/zpipe/down/atmos_init()
var/turf/T = src.loc // hide if turf is not intact
- hide(!T.is_plating())
+ if(level == 1 && !T.is_plating()) hide(1) // but respect level
///////////////////////
// supply/scrubbers //
diff --git a/code/modules/multiz/structures.dm b/code/modules/multiz/structures.dm
index 59c2f172b0..104ad28136 100644
--- a/code/modules/multiz/structures.dm
+++ b/code/modules/multiz/structures.dm
@@ -48,7 +48,7 @@
var/obj/structure/ladder/target_ladder = getTargetLadder(M)
if(!target_ladder)
return
- if(!M.Move(get_turf(src)))
+ if(!(M.loc == loc) && !M.Move(get_turf(src)))
to_chat(M, "You fail to reach \the [src].")
return
@@ -68,6 +68,10 @@
if(target_ladder)
M.forceMove(get_turf(target_ladder))
+/obj/structure/ladder/attack_robot(var/mob/M)
+ attack_hand(M)
+ return
+
/obj/structure/ladder/proc/getTargetLadder(var/mob/M)
if((!target_up && !target_down) || (target_up && !istype(target_up.loc, /turf) || (target_down && !istype(target_down.loc,/turf))))
to_chat(M, "\The [src] is incomplete and can't be climbed.")
@@ -131,6 +135,7 @@
opacity = 0
anchored = 1
flags = ON_BORDER
+ layer = STAIRS_LAYER
/obj/structure/stairs/Initialize()
. = ..()
diff --git a/code/modules/multiz/zshadow.dm b/code/modules/multiz/zshadow.dm
index 7a04768ae7..ca81e66f1a 100644
--- a/code/modules/multiz/zshadow.dm
+++ b/code/modules/multiz/zshadow.dm
@@ -24,6 +24,7 @@
sync_icon(L)
/mob/zshadow/Destroy()
+ owner.shadow = null
owner = null
..() //But we don't return because the hint is wrong
return QDEL_HINT_QUEUE
diff --git a/code/modules/organs/internal/brain.dm b/code/modules/organs/internal/brain.dm
index 7385987a58..8bb09bebd2 100644
--- a/code/modules/organs/internal/brain.dm
+++ b/code/modules/organs/internal/brain.dm
@@ -151,6 +151,7 @@ GLOBAL_LIST_BOILERPLATE(all_brain_organs, /obj/item/organ/internal/brain)
name = "slime core"
desc = "A complex, organic knot of jelly and crystalline particles."
icon_state = "core"
+ decays = FALSE
parent_organ = BP_TORSO
clone_source = TRUE
flags = OPENCONTAINER
diff --git a/code/modules/organs/internal/heart.dm b/code/modules/organs/internal/heart.dm
index 10fdcada69..b65b3bc709 100644
--- a/code/modules/organs/internal/heart.dm
+++ b/code/modules/organs/internal/heart.dm
@@ -7,6 +7,8 @@
parent_organ = BP_TORSO
dead_icon = "heart-off"
+ var/standard_pulse_level = PULSE_NORM // We run on a normal clock. This is NOT CONNECTED to species heart-rate modifier.
+
/obj/item/organ/internal/heart/handle_germ_effects()
. = ..() //Up should return an infection level as an integer
diff --git a/code/modules/organs/organ.dm b/code/modules/organs/organ.dm
index 7c91420f60..c959ab3752 100644
--- a/code/modules/organs/organ.dm
+++ b/code/modules/organs/organ.dm
@@ -6,30 +6,31 @@ var/list/organ_cache = list()
germ_level = 0
// Strings.
- var/organ_tag = "organ" // Unique identifier.
- var/parent_organ = BP_TORSO // Organ holding this object.
+ var/organ_tag = "organ" // Unique identifier.
+ var/parent_organ = BP_TORSO // Organ holding this object.
// Status tracking.
- var/status = 0 // Various status flags
- var/vital // Lose a vital limb, die immediately.
- var/damage = 0 // Current damage to the organ
+ var/status = 0 // Various status flags
+ var/vital // Lose a vital limb, die immediately.
+ var/damage = 0 // Current damage to the organ
var/robotic = 0
// Reference data.
- var/mob/living/carbon/human/owner // Current mob owning the organ.
- var/list/transplant_data // Transplant match data.
- var/list/autopsy_data = list() // Trauma data for forensics.
- var/list/trace_chemicals = list() // Traces of chemicals in the organ.
- var/datum/dna/dna // Original DNA.
- var/datum/species/species // Original species.
+ var/mob/living/carbon/human/owner // Current mob owning the organ.
+ var/list/transplant_data // Transplant match data.
+ var/list/autopsy_data = list() // Trauma data for forensics.
+ var/list/trace_chemicals = list() // Traces of chemicals in the organ.
+ var/datum/dna/dna // Original DNA.
+ var/datum/species/species // Original species.
// Damage vars.
- var/min_bruised_damage = 10 // Damage before considered bruised
- var/min_broken_damage = 30 // Damage before becoming broken
- var/max_damage // Damage cap
- var/can_reject = 1 // Can this organ reject?
- var/rejecting // Is this organ already being rejected?
- var/preserved = 0 // If this is 1, prevents organ decay.
+ var/min_bruised_damage = 10 // Damage before considered bruised
+ var/min_broken_damage = 30 // Damage before becoming broken
+ var/max_damage // Damage cap
+ var/can_reject = 1 // Can this organ reject?
+ var/rejecting // Is this organ already being rejected?
+ var/decays = TRUE // Can this organ decay at all?
+ var/preserved = 0 // If this is 1, prevents organ decay.
// Language vars. Putting them here in case we decide to do something crazy with sign-or-other-nonverbal languages.
var/list/will_assist_languages = list()
@@ -135,7 +136,7 @@ var/list/organ_cache = list()
if(B && prob(40))
reagents.remove_reagent("blood",0.1)
blood_splatter(src,B,1)
- if(config.organs_decay) damage += rand(1,3)
+ if(config.organs_decay && decays) damage += rand(1,3)
if(damage >= max_damage)
damage = max_damage
adjust_germ_level(rand(2,6))
diff --git a/code/modules/power/singularity/generator.dm b/code/modules/power/singularity/generator.dm
index 888d75ea6f..c49c605598 100644
--- a/code/modules/power/singularity/generator.dm
+++ b/code/modules/power/singularity/generator.dm
@@ -29,4 +29,25 @@
"You unsecure the [src.name] from the floor.", \
"You hear a ratchet.")
return
+ if(W.is_screwdriver())
+ panel_open = !panel_open
+ playsound(loc, W.usesound, 50, 1)
+ visible_message("\The [user] adjusts \the [src]'s mechanisms.")
+ if(panel_open && do_after(user, 30))
+ to_chat(user, "\The [src] looks like it could be modified.")
+ if(panel_open && do_after(user, 80 * W.toolspeed)) // We don't have skills, so a delayed hint for engineers will have to do for now. (Panel open check for sanity)
+ playsound(loc, W.usesound, 50, 1)
+ to_chat(user, "\The [src] looks like it could be adapted to forge advanced materials via particle acceleration, somehow..")
+ else
+ to_chat(user, "\The [src]'s mechanisms look secure.")
+ if(istype(W, /obj/item/weapon/smes_coil/super_io) && panel_open)
+ visible_message("\The [user] begins to modify \the [src] with \the [W].")
+ if(do_after(user, 300))
+ user.drop_from_inventory(W)
+ visible_message("\The [user] installs \the [W] onto \the [src].")
+ qdel(W)
+ var/turf/T = get_turf(src)
+ var/new_machine = /obj/machinery/particle_smasher
+ new new_machine(T)
+ qdel(src)
return ..()
diff --git a/code/modules/power/singularity/particle_accelerator/particle.dm b/code/modules/power/singularity/particle_accelerator/particle.dm
index 3b5992e4e4..2c3e99318d 100644
--- a/code/modules/power/singularity/particle_accelerator/particle.dm
+++ b/code/modules/power/singularity/particle_accelerator/particle.dm
@@ -44,7 +44,7 @@
if (A)
if(ismob(A))
toxmob(A)
- if((istype(A,/obj/machinery/the_singularitygen))||(istype(A,/obj/singularity/)))
+ if((istype(A,/obj/machinery/the_singularitygen))||(istype(A,/obj/singularity/))||(istype(A, /obj/machinery/particle_smasher)))
A:energy += energy
//R-UST port
else if(istype(A,/obj/machinery/power/fusion_core))
diff --git a/code/modules/power/singularity/particle_accelerator/particle_smasher.dm b/code/modules/power/singularity/particle_accelerator/particle_smasher.dm
new file mode 100644
index 0000000000..a9fca3e160
--- /dev/null
+++ b/code/modules/power/singularity/particle_accelerator/particle_smasher.dm
@@ -0,0 +1,365 @@
+/*
+ * Contains the particle smasher and its recipes.
+ */
+
+/obj/machinery/particle_smasher
+ name = "Particle Focus"
+ desc = "A strange device used to create exotic matter."
+ icon = 'icons/obj/machines/particle_smasher.dmi'
+ icon_state = "smasher"
+ anchored = 0
+ density = 1
+ use_power = 0
+
+ var/successful_craft = FALSE // Are we waiting to be emptied?
+ var/image/material_layer // Holds the image used for the filled overlay.
+ var/image/material_glow // Holds the image used for the glow overlay.
+ var/image/reagent_layer // Holds the image used for showing a contained beaker.
+ var/energy = 0 // How many 'energy' units does this have? Acquired by a Particle Accelerator like a Singularity.
+ var/max_energy = 600
+ var/obj/item/stack/material/target // The material being bombarded.
+ var/obj/item/weapon/reagent_containers/reagent_container // Holds the beaker. The process will consume ALL reagents inside it.
+ var/beaker_type = /obj/item/weapon/reagent_containers/glass/beaker
+ var/list/storage // Holds references to items allowed to be used in the fabrication phase.
+ var/max_storage = 3 // How many items can be jammed into it?
+ var/list/recipes // The list containing the Particle Smasher's recipes.
+
+/obj/machinery/particle_smasher/Initialize()
+ ..()
+ storage = list()
+ update_icon()
+ prepare_recipes()
+
+/obj/machinery/particle_smasher/Destroy()
+ for(var/datum/recipe/particle_smasher/D in recipes)
+ qdel(D)
+ recipes.Cut()
+ ..()
+
+/obj/machinery/particle_smasher/examine(mob/user)
+ ..()
+ if(user in view(1))
+ to_chat(user, "\The [src] contains:")
+ for(var/obj/item/I in contents)
+ to_chat(user, "\the [I]")
+
+/obj/machinery/particle_smasher/attackby(obj/item/W as obj, mob/user as mob)
+ if(W.type == /obj/item/device/analyzer)
+ to_chat(user, "\The [src] reads an energy level of [energy].")
+ else if(istype(W, /obj/item/stack/material))
+ var/obj/item/stack/material/M = W
+ if(M.uses_charge)
+ to_chat(user, "You cannot fill \the [src] with a synthesizer!")
+ return
+ target = M.split(1)
+ target.forceMove(src)
+ update_icon()
+ else if(istype(W, beaker_type))
+ if(reagent_container)
+ to_chat(user, "\The [src] already has a container attached.")
+ return
+ if(isrobot(user) && istype(W.loc, /obj/item/weapon/gripper))
+ var/obj/item/weapon/gripper/G = W.loc
+ G.drop_item()
+ else
+ user.drop_from_inventory(W)
+ reagent_container = W
+ reagent_container.forceMove(src)
+ to_chat(user, "You add \the [reagent_container] to \the [src].")
+ update_icon()
+ return
+ else if(W.is_wrench())
+ anchored = !anchored
+ playsound(src, W.usesound, 75, 1)
+ if(anchored)
+ user.visible_message("[user.name] secures [src.name] to the floor.", \
+ "You secure the [src.name] to the floor.", \
+ "You hear a ratchet.")
+ else
+ user.visible_message("[user.name] unsecures [src.name] from the floor.", \
+ "You unsecure the [src.name] from the floor.", \
+ "You hear a ratchet.")
+ update_icon()
+ return
+ else if(istype(W, /obj/item/weapon/card/id))
+ to_chat(user, "Swiping \the [W] on \the [src] doesn't seem to do anything...")
+ return ..()
+ else if(((isrobot(user) && istype(W.loc, /obj/item/weapon/gripper)) || (!isrobot(user) && W.canremove)) && storage.len < max_storage)
+ if(isrobot(user) && istype(W.loc, /obj/item/weapon/gripper))
+ var/obj/item/weapon/gripper/G = W.loc
+ G.drop_item()
+ else
+ user.drop_from_inventory(W)
+ W.forceMove(src)
+ storage += W
+ else
+ return ..()
+
+/obj/machinery/particle_smasher/update_icon()
+ cut_overlays()
+ if(!material_layer)
+ material_layer = image(icon, "[initial(icon_state)]-material")
+ if(!material_glow)
+ material_glow = image(icon, "[initial(icon_state)]-material-glow")
+ material_glow.plane = PLANE_LIGHTING_ABOVE
+ if(!reagent_layer)
+ reagent_layer = image(icon, "[initial(icon_state)]-reagent")
+ if(anchored)
+ icon_state = "[initial(icon_state)]-o"
+ if(target)
+ material_layer.color = target.material.icon_colour
+ add_overlay(material_layer)
+ if(successful_craft)
+ material_glow.color = target.material.icon_colour
+ add_overlay(material_glow)
+ if(reagent_container)
+ add_overlay(reagent_layer)
+ else
+ icon_state = initial(icon_state)
+
+ if(target && energy)
+ var/power_percent = round((energy / max_energy) * 100)
+ light_color = target.material.icon_colour
+ switch(power_percent)
+ if(0 to 25)
+ light_range = 1
+ if(26 to 50)
+ light_range = 2
+ if(51 to 75)
+ light_range = 3
+ if(76 to INFINITY)
+ light_range = 4
+ set_light(light_range, 2, light_color)
+ else
+ set_light(0, 0, "#FFFFFF")
+
+/obj/machinery/particle_smasher/bullet_act(var/obj/item/projectile/Proj)
+ if(istype(Proj, /obj/item/projectile/beam))
+ if(Proj.damage >= 50)
+ TryCraft()
+ return 0
+
+/obj/machinery/particle_smasher/process()
+ if(!src.anchored) // Rapidly loses focus.
+ if(energy)
+ radiation_repository.radiate(src, round(((src.energy-150)/50)*5,1))
+ energy = max(0, energy - 30)
+ update_icon()
+ return
+
+ if(energy)
+ radiation_repository.radiate(src, round(((src.energy-150)/50)*5,1))
+ energy = CLAMP(energy - 5, 0, max_energy)
+
+ return
+
+/obj/machinery/particle_smasher/proc/prepare_recipes()
+ if(!recipes)
+ recipes = list()
+ for(var/D in subtypesof(/datum/recipe/particle_smasher))
+ recipes += new D
+ else
+ for(var/datum/recipe/particle_smasher/D in recipes)
+ qdel(D)
+ recipes.Cut()
+ for(var/D in subtypesof(/datum/recipe/particle_smasher))
+ recipes += new D
+
+/obj/machinery/particle_smasher/proc/TryCraft()
+
+ if(!recipes || !recipes.len)
+ recipes = typesof(/datum/recipe/particle_smasher)
+
+ if(!target) // You are just blasting an empty machine.
+ visible_message("\The [src] shudders.")
+ update_icon()
+ return
+
+ if(successful_craft)
+ visible_message("\The [src] fizzles.")
+ if(prob(33)) // Why are you blasting it after it's already done!
+ radiation_repository.radiate(src, 10 + round(src.energy / 60, 1))
+ energy = max(0, energy - 30)
+ update_icon()
+ return
+
+ var/list/possible_recipes = list()
+ var/max_prob = 0
+ for(var/datum/recipe/particle_smasher/R in recipes) // Only things for the smasher. Don't get things like the chef's cake recipes.
+ if(R.probability) // It's actually a recipe you're supposed to be able to make.
+ if(istype(target, R.required_material))
+ if(energy >= R.required_energy_min && energy <= R.required_energy_max) // The machine has enough Vaguely Defined 'Energy'.
+ var/turf/T = get_turf(src)
+ var/datum/gas_mixture/environment = T.return_air()
+ if(environment.temperature >= R.required_atmos_temp_min && environment.temperature <= R.required_atmos_temp_max) // Too hot, or too cold.
+ if(R.reagents && R.reagents.len)
+ if(!reagent_container || R.check_reagents(reagent_container.reagents) == -1) // It doesn't have a reagent storage when it needs it, or it's lacking what is needed.
+ continue
+ if(R.items && R.items.len)
+ if(!(storage && storage.len) || R.check_items(src) == -1) // It's empty, or it doesn't contain what is needed.
+ continue
+ possible_recipes += R
+ max_prob += R.probability
+
+ if(possible_recipes.len)
+ var/local_prob = rand(0, max_prob - 1)%max_prob
+ var/cumulative = 0
+ for(var/datum/recipe/particle_smasher/R in possible_recipes)
+ cumulative += R.probability
+ if(local_prob < cumulative)
+ successful_craft = TRUE
+ DoCraft(R)
+ break
+ update_icon()
+
+/obj/machinery/particle_smasher/proc/DoCraft(var/datum/recipe/particle_smasher/recipe)
+ if(!successful_craft || !recipe)
+ return
+
+ qdel(target)
+ target = null
+
+ if(reagent_container)
+ reagent_container.reagents.clear_reagents()
+
+ if(recipe.items && recipe.items.len)
+ for(var/obj/item/I in storage)
+ for(var/item_type in recipe.items)
+ if(istype(I, item_type))
+ storage -= I
+ qdel(I)
+ break
+
+ var/result = recipe.result
+ var/obj/item/stack/material/M = new result(src)
+ target = M
+ update_icon()
+
+/obj/machinery/particle_smasher/verb/eject_contents()
+ set src in view(1)
+ set category = "Object"
+ set name = "Eject Particle Focus Contents"
+
+ if(usr.incapacitated())
+ return
+
+ DumpContents()
+
+/obj/machinery/particle_smasher/proc/DumpContents()
+ target = null
+ reagent_container = null
+ successful_craft = FALSE
+ var/turf/T = get_turf(src)
+ for(var/obj/item/I in contents)
+ if(I in storage)
+ storage -= I
+ I.forceMove(T)
+ update_icon()
+
+/*
+ * The special recipe datums used for the particle smasher.
+ */
+
+/datum/recipe/particle_smasher
+ //reagents //Commented out due to inheritance. Still a list, used as ex: // example: = list("pacid" = 5)
+ //items //Commented out due to inheritance. Still a list, used as ex: // example: = list(/obj/item/weapon/tool/crowbar, /obj/item/weapon/welder) Place /foo/bar before /foo. Do not include fruit. Maximum of 3 items.
+
+ result = /obj/item/stack/material/iron // The sheet this will produce.
+ var/required_material = /obj/item/stack/material/iron // The required material sheet.
+ var/required_energy_min = 0 // The minimum energy this recipe can process at.
+ var/required_energy_max = 600 // The maximum energy this recipe can process at.
+ var/required_atmos_temp_min = 0 // The minimum ambient atmospheric temperature required, in kelvin.
+ var/required_atmos_temp_max = 600 // The maximum ambient atmospheric temperature required, in kelvin.
+ var/probability = 0 // The probability for the recipe to be produced. 0 will make it impossible.
+
+/datum/recipe/particle_smasher/check_items(var/obj/container as obj)
+ . = 1
+ if (items && items.len)
+ var/list/checklist = list()
+ checklist = items.Copy() // You should really trust Copy
+ if(istype(container, /obj/machinery/particle_smasher))
+ var/obj/machinery/particle_smasher/machine = container
+ for(var/obj/O in machine.storage)
+ if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/grown))
+ continue // Fruit is handled in check_fruit().
+ var/found = 0
+ for(var/i = 1; i < checklist.len+1; i++)
+ var/item_type = checklist[i]
+ if (istype(O,item_type))
+ checklist.Cut(i, i+1)
+ found = 1
+ break
+ if (!found)
+ . = 0
+ if (checklist.len)
+ . = -1
+ return .
+
+/datum/recipe/particle_smasher/deuterium_tritium
+ reagents = list("hydrogen" = 15)
+
+ result = /obj/item/stack/material/tritium
+ required_material = /obj/item/stack/material/deuterium
+
+ required_energy_min = 200
+ required_energy_max = 400
+
+ required_atmos_temp_max = 200
+ probability = 30
+
+/datum/recipe/particle_smasher/verdantium_morphium
+ result = /obj/item/stack/material/morphium
+ required_material = /obj/item/stack/material/verdantium
+
+ required_energy_min = 400
+ required_energy_max = 500
+ probability = 20
+
+/datum/recipe/particle_smasher/plasteel_morphium
+ items = list(/obj/item/prop/alien/junk)
+
+ result = /obj/item/stack/material/morphium
+ required_material = /obj/item/stack/material/plasteel
+
+ required_energy_min = 100
+ required_energy_max = 300
+ probability = 10
+
+/datum/recipe/particle_smasher/osmium_lead
+ reagents = list("tungsten" = 10)
+
+ result = /obj/item/stack/material/lead
+ required_material = /obj/item/stack/material/osmium
+
+ required_energy_min = 200
+ required_energy_max = 400
+
+ required_atmos_temp_min = 1000
+ required_atmos_temp_max = 8000
+ probability = 50
+
+/datum/recipe/particle_smasher/phoron_valhollide
+ reagents = list("phoron" = 10, "pacid" = 10)
+
+ result = /obj/item/stack/material/valhollide
+ required_material = /obj/item/stack/material/phoron
+
+ required_energy_min = 300
+ required_energy_max = 500
+
+ required_atmos_temp_min = 1
+ required_atmos_temp_max = 100
+ probability = 10
+
+/datum/recipe/particle_smasher/valhollide_supermatter
+ reagents = list("phoron" = 300)
+
+ result = /obj/item/stack/material/supermatter
+ required_material = /obj/item/stack/material/valhollide
+
+ required_energy_min = 575
+ required_energy_max = 600
+
+ required_atmos_temp_min = 3000
+ required_atmos_temp_max = 10000
+ probability = 1
\ No newline at end of file
diff --git a/code/modules/power/smes.dm b/code/modules/power/smes.dm
index ad76cb061f..8628315601 100644
--- a/code/modules/power/smes.dm
+++ b/code/modules/power/smes.dm
@@ -144,11 +144,6 @@
charge -= output_used*SMESRATE // reduce the storage (may be recovered in /restore() if excessive)
add_avail(output_used) // add output to powernet (smes side)
-
- if(output_used < 0.0001) // either from no charge or set to 0
- outputting(0)
- investigate_log("lost power and turned off","singulo")
- log_game("SMES([x],[y],[z]) Power depleted.")
else if(output_attempt && output_level > 0)
outputting = 1
else
diff --git a/code/modules/power/solar.dm b/code/modules/power/solar.dm
index de0f7c6ac4..9c612eb788 100644
--- a/code/modules/power/solar.dm
+++ b/code/modules/power/solar.dm
@@ -1,4 +1,7 @@
#define SOLAR_MAX_DIST 40
+#define SOLAR_AUTO_START_NO 0 // Will never start itself.
+#define SOLAR_AUTO_START_YES 1 // Will always start itself.
+#define SOLAR_AUTO_START_CONFIG 2 // Will start itself if config allows it (default is no).
GLOBAL_VAR_INIT(solar_gen_rate, 1500)
GLOBAL_LIST_EMPTY(solars_list)
@@ -293,9 +296,23 @@ GLOBAL_LIST_EMPTY(solars_list)
var/nexttime = 0 // time for a panel to rotate of 1° in manual tracking
var/obj/machinery/power/tracker/connected_tracker = null
var/list/connected_panels = list()
+ var/auto_start = SOLAR_AUTO_START_NO
-/obj/machinery/power/solar_control/drain_power()
- return -1
+// Used for mapping in solar arrays which automatically start itself.
+// Generally intended for far away and remote locations, where player intervention is rare.
+// In the interest of backwards compatability, this isn't named auto_start, as doing so might break downstream maps.
+/obj/machinery/power/solar_control/autostart
+ auto_start = SOLAR_AUTO_START_YES
+
+// Similar to above but controlled by the configuration file.
+// Intended to be used for the main solar arrays, so individual servers can choose to have them start automatically or require manual intervention.
+/obj/machinery/power/solar_control/config_start
+ auto_start = SOLAR_AUTO_START_CONFIG
+
+/obj/machinery/power/solar_control/Initialize()
+ . = ..()
+ connect_to_network()
+ set_panels(cdir)
/obj/machinery/power/solar_control/Destroy()
for(var/obj/machinery/power/solar/M in connected_panels)
@@ -304,6 +321,25 @@ GLOBAL_LIST_EMPTY(solars_list)
connected_tracker.unset_control()
return ..()
+/obj/machinery/power/solar_control/proc/auto_start(forced = FALSE)
+ // Automatically sets the solars, if allowed.
+ if(forced || auto_start == SOLAR_AUTO_START_YES || (auto_start == SOLAR_AUTO_START_CONFIG && config.autostart_solars) )
+ track = 2 // Auto tracking mode.
+ search_for_connected()
+ if(connected_tracker)
+ connected_tracker.set_angle(SSsun.sun.angle)
+ set_panels(cdir)
+
+// This would use LateInitialize(), however the powernet does not appear to exist during that time.
+/hook/roundstart/proc/auto_start_solars()
+ for(var/a in GLOB.solars_list)
+ var/obj/machinery/power/solar_control/SC = a
+ SC.auto_start()
+ return TRUE
+
+/obj/machinery/power/solar_control/drain_power()
+ return -1
+
/obj/machinery/power/solar_control/disconnect_from_network()
..()
GLOB.solars_list.Remove(src)
@@ -346,13 +382,6 @@ GLOBAL_LIST_EMPTY(solars_list)
set_panels(cdir)
updateDialog()
-
-/obj/machinery/power/solar_control/Initialize()
- . = ..()
- if(!powernet) return
- set_panels(cdir)
- connect_to_network()
-
/obj/machinery/power/solar_control/update_icon()
if(stat & BROKEN)
icon_state = "broken"
@@ -528,18 +557,6 @@ GLOBAL_LIST_EMPTY(solars_list)
broken()
return
-// Used for mapping in solar array which automatically starts itself (telecomms, for example)
-/obj/machinery/power/solar_control/autostart
- track = 2 // Auto tracking mode
-
-/obj/machinery/power/solar_control/autostart/New()
- ..()
- spawn(150) // Wait 15 seconds to ensure everything was set up properly (such as, powernets, solar panels, etc.
- src.search_for_connected()
- if(connected_tracker && track == 2)
- connected_tracker.set_angle(SSsun.sun.angle)
- src.set_panels(cdir)
-
//
// MISC
//
diff --git a/code/modules/power/tesla/energy_ball.dm b/code/modules/power/tesla/energy_ball.dm
index ad79a052de..08427ba027 100644
--- a/code/modules/power/tesla/energy_ball.dm
+++ b/code/modules/power/tesla/energy_ball.dm
@@ -247,7 +247,7 @@
closest_atom = A
closest_dist = dist
- else if(closest_mob)
+ else if(closest_machine)
continue
else if(istype(A, /obj/structure/blob))
diff --git a/code/modules/random_map/noise/ore.dm b/code/modules/random_map/noise/ore.dm
index 697a0b416d..99dc761ee6 100644
--- a/code/modules/random_map/noise/ore.dm
+++ b/code/modules/random_map/noise/ore.dm
@@ -57,25 +57,34 @@
T.resources["gold"] = rand(RESOURCE_LOW_MIN, RESOURCE_LOW_MAX)
T.resources["silver"] = rand(RESOURCE_LOW_MIN, RESOURCE_LOW_MAX)
T.resources["uranium"] = rand(RESOURCE_LOW_MIN, RESOURCE_LOW_MAX)
+ T.resources["marble"] = rand(RESOURCE_LOW_MIN, RESOURCE_MID_MAX)
T.resources["diamond"] = 0
T.resources["phoron"] = 0
T.resources["osmium"] = 0
T.resources["hydrogen"] = 0
+ T.resources["verdantium"] = 0
+ T.resources["lead"] = 0
else if(current_cell < deep_val) // Rare metals.
T.resources["gold"] = rand(RESOURCE_MID_MIN, RESOURCE_MID_MAX)
T.resources["silver"] = rand(RESOURCE_MID_MIN, RESOURCE_MID_MAX)
T.resources["uranium"] = rand(RESOURCE_MID_MIN, RESOURCE_MID_MAX)
T.resources["phoron"] = rand(RESOURCE_MID_MIN, RESOURCE_MID_MAX)
T.resources["osmium"] = rand(RESOURCE_MID_MIN, RESOURCE_MID_MAX)
+ T.resources["verdantium"] = rand(RESOURCE_LOW_MIN, RESOURCE_LOW_MAX)
+ T.resources["lead"] = rand(RESOURCE_LOW_MIN, RESOURCE_MID_MAX)
T.resources["hydrogen"] = 0
T.resources["diamond"] = 0
T.resources["hematite"] = 0
+ T.resources["marble"] = 0
else // Deep metals.
T.resources["uranium"] = rand(RESOURCE_LOW_MIN, RESOURCE_LOW_MAX)
T.resources["diamond"] = rand(RESOURCE_LOW_MIN, RESOURCE_LOW_MAX)
+ T.resources["verdantium"] = rand(RESOURCE_LOW_MIN, RESOURCE_MID_MAX)
T.resources["phoron"] = rand(RESOURCE_HIGH_MIN, RESOURCE_HIGH_MAX)
T.resources["osmium"] = rand(RESOURCE_HIGH_MIN, RESOURCE_HIGH_MAX)
T.resources["hydrogen"] = rand(RESOURCE_MID_MIN, RESOURCE_MID_MAX)
+ T.resources["marble"] = rand(RESOURCE_MID_MIN, RESOURCE_HIGH_MAX)
+ T.resources["lead"] = rand(RESOURCE_LOW_MIN, RESOURCE_HIGH_MAX)
T.resources["hematite"] = 0
T.resources["gold"] = 0
T.resources["silver"] = 0
diff --git a/code/modules/reagents/Chemistry-Reagents.dm b/code/modules/reagents/Chemistry-Reagents.dm
index 21775ea17e..8ad8b6486d 100644
--- a/code/modules/reagents/Chemistry-Reagents.dm
+++ b/code/modules/reagents/Chemistry-Reagents.dm
@@ -12,6 +12,7 @@
var/list/data = null
var/volume = 0
var/metabolism = REM // This would be 0.2 normally
+ var/list/filtered_organs = list() // Organs that will slow the processing of this chemical.
var/mrate_static = FALSE //If the reagent should always process at the same speed, regardless of species, make this TRUE
var/ingest_met = 0
var/touch_met = 0
@@ -69,6 +70,22 @@
// Metabolism
removed *= active_metab.metabolism_speed
+ if(ishuman(M))
+ var/mob/living/carbon/human/H = M
+ if(H.species.has_organ[O_HEART])
+ var/obj/item/organ/internal/heart/Pump = H.internal_organs_by_name[O_HEART]
+ if(!Pump)
+ removed *= 0.1
+ else if(Pump.standard_pulse_level == PULSE_NONE) // No pulse normally means chemicals process a little bit slower than normal.
+ removed *= 0.8
+ else // Otherwise, chemicals process as per percentage of your current pulse, or, if you have no pulse but are alive, by a miniscule amount.
+ removed *= max(0.1, H.pulse / Pump.standard_pulse_level)
+ if(filtered_organs && filtered_organs.len)
+ for(var/organ_tag in filtered_organs)
+ var/obj/item/organ/internal/O = H.internal_organs_by_name[organ_tag]
+ if(O && !O.is_broken() && prob(max(0, O.max_damage - O.damage)))
+ removed *= 0.8
+
if(ingest_met && (active_metab.metabolism_class == CHEM_INGEST))
removed = ingest_met
if(touch_met && (active_metab.metabolism_class == CHEM_TOUCH))
diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm
index 38f0bf5098..25fa132712 100644
--- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm
+++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm
@@ -966,6 +966,14 @@
for(var/obj/effect/decal/cleanable/blood/B in T)
qdel(B)
+/datum/reagent/sterilizine/touch_mob(var/mob/living/L, var/amount)
+ if(istype(L))
+ if(istype(L, /mob/living/simple_mob/slime))
+ var/mob/living/simple_mob/slime/S = L
+ S.adjustToxLoss(rand(15, 25) * amount) // Does more damage than water.
+ S.visible_message("[S]'s flesh sizzles where the fluid touches it!", "Your flesh burns in the fluid!")
+ remove_self(amount)
+
/datum/reagent/leporazine
name = "Leporazine"
id = "leporazine"
diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm
index 1ec5e6e334..59716a5493 100644
--- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm
+++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm
@@ -9,6 +9,7 @@
reagent_state = LIQUID
color = "#CF3600"
metabolism = REM * 0.25 // 0.05 by default. Hopefully enough to get some help, or die horribly, whatever floats your boat
+ filtered_organs = list(O_LIVER, O_KIDNEYS)
var/strength = 4 // How much damage it deals per unit
/datum/reagent/toxin/affect_blood(var/mob/living/carbon/M, var/alien, var/removed)
@@ -253,6 +254,7 @@
color = "#669900"
metabolism = REM
strength = 3
+ mrate_static = TRUE
/datum/reagent/toxin/zombiepowder/affect_blood(var/mob/living/carbon/M, var/alien, var/removed)
..()
@@ -879,3 +881,47 @@ datum/reagent/talum_quem/affect_blood(var/mob/living/carbon/M, var/alien, var/re
randmuti(M)
M << "You feel odd!"
M.apply_effect(6 * removed, IRRADIATE, 0)
+
+/*
+ * Hostile nanomachines.
+ * Unscannable, and commonly all look the same.
+ */
+
+/datum/reagent/shredding_nanites
+ name = "Restorative Nanites"
+ id = "shredding_nanites"
+ description = "Miniature medical robots that swiftly restore bodily damage. These ones seem to be malfunctioning."
+ taste_description = "metal"
+ reagent_state = SOLID
+ color = "#555555"
+ metabolism = REM * 4 // Nanomachines. Fast.
+
+/datum/reagent/shredding_nanites/affect_blood(var/mob/living/carbon/M, var/alien, var/removed)
+ M.adjustBruteLoss(4 * removed)
+ M.adjustOxyLoss(4 * removed)
+
+/datum/reagent/irradiated_nanites
+ name = "Restorative Nanites"
+ id = "irradiated_nanites"
+ description = "Miniature medical robots that swiftly restore bodily damage. These ones seem to be malfunctioning."
+ taste_description = "metal"
+ reagent_state = SOLID
+ color = "#555555"
+ metabolism = REM * 4
+
+/datum/reagent/irradiated_nanites/affect_blood(var/mob/living/carbon/M, var/alien, var/removed)
+ radiation_repository.radiate(get_turf(M), 20) // Irradiate people around you.
+ M.radiation = max(M.radiation + 5 * removed, 0) // Irradiate you. Because it's inside you.
+
+/datum/reagent/neurophage_nanites
+ name = "Restorative Nanites"
+ id = "neurophage_nanites"
+ description = "Miniature medical robots that swiftly restore bodily damage. These ones seem to be completely hostile."
+ taste_description = "metal"
+ reagent_state = SOLID
+ color = "#555555"
+ metabolism = REM * 4
+
+/datum/reagent/neurophage_nanites/affect_blood(var/mob/living/carbon/M, var/alien, var/removed)
+ M.adjustBrainLoss(2 * removed) // Their job is to give you a bad time.
+ M.adjustBruteLoss(2 * removed)
diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm
index 5faa197345..242c79822a 100644
--- a/code/modules/reagents/reagent_containers/syringes.dm
+++ b/code/modules/reagents/reagent_containers/syringes.dm
@@ -171,7 +171,9 @@
return
var/cycle_time = injtime*0.33 //33% of the time slept between 5u doses
- var/warmup_time = cycle_time //If the target is another mob, this gets overwritten
+ var/warmup_time = 0 //0 for containers
+ if(ismob(target))
+ warmup_time = cycle_time //If the target is another mob, this gets overwritten
if(ismob(target) && target != user)
warmup_time = injtime*0.66 // Otherwise 66% of the time is warmup
@@ -200,14 +202,14 @@
var/trans = 0
var/contained = reagentlist()
- while(reagents.total_volume)
- if(ismob(target))
+ if(ismob(target))
+ while(reagents.total_volume)
trans += reagents.trans_to_mob(target, amount_per_transfer_from_this, CHEM_BLOOD)
- else
- trans += reagents.trans_to_obj(target, amount_per_transfer_from_this)
- update_icon()
- if(!reagents.total_volume || !do_after(user,cycle_time,target))
- break
+ update_icon()
+ if(!reagents.total_volume || !do_after(user,cycle_time,target))
+ break
+ else
+ trans += reagents.trans_to_obj(target, amount_per_transfer_from_this)
if (reagents.total_volume <= 0 && mode == SYRINGE_INJECT)
mode = SYRINGE_DRAW
diff --git a/code/modules/recycling/conveyor2.dm b/code/modules/recycling/conveyor2.dm
index 428484ea66..4cc755d158 100644
--- a/code/modules/recycling/conveyor2.dm
+++ b/code/modules/recycling/conveyor2.dm
@@ -1,6 +1,6 @@
#define OFF 0
#define FORWARDS 1
-#define BACKWARDS 2
+#define BACKWARDS -1
//conveyor2 is pretty much like the original, except it supports corners, but not diverters.
//note that corner pieces transfer stuff clockwise when running forward, and anti-clockwise backwards.
@@ -32,12 +32,7 @@
if(newdir)
set_dir(newdir)
- if(dir & (dir-1)) // Diagonal. Forwards is *away* from dir, curving to the right.
- forwards = turn(dir, 135)
- backwards = turn(dir, 45)
- else
- forwards = dir
- backwards = turn(dir, 180)
+ update_dir()
if(on)
operating = FORWARDS
@@ -60,6 +55,18 @@
operating = OFF
update()
+/obj/machinery/conveyor/set_dir()
+ .=..()
+ update_dir()
+
+/obj/machinery/conveyor/proc/update_dir()
+ if(!(dir in cardinal)) // Diagonal. Forwards is *away* from dir, curving to the right.
+ forwards = turn(dir, 135)
+ backwards = turn(dir, 45)
+ else
+ forwards = dir
+ backwards = turn(dir, 180)
+
/obj/machinery/conveyor/proc/update()
if(stat & BROKEN)
icon_state = "conveyor-broken"
diff --git a/code/modules/research/circuitprinter.dm b/code/modules/research/circuitprinter.dm
index 70ca9692f3..7f9fcb565f 100644
--- a/code/modules/research/circuitprinter.dm
+++ b/code/modules/research/circuitprinter.dm
@@ -16,7 +16,9 @@ using metal and glass, it uses glass and reagents (usually sulphuric acid).
var/mat_efficiency = 1
var/speed = 1
- materials = list(DEFAULT_WALL_MATERIAL = 0, "glass" = 0, "plastic" = 0, "gold" = 0, "silver" = 0, "osmium" = 0, "phoron" = 0, "uranium" = 0, "diamond" = 0)
+ materials = list(DEFAULT_WALL_MATERIAL = 0, "glass" = 0, MAT_PLASTEEL = 0, "plastic" = 0, "gold" = 0, "silver" = 0, "osmium" = 0, MAT_LEAD = 0, "phoron" = 0, "uranium" = 0, "diamond" = 0, MAT_DURASTEEL = 0, MAT_VERDANTIUM = 0, MAT_MORPHIUM = 0, MAT_METALHYDROGEN = 0, MAT_SUPERMATTER = 0)
+
+ hidden_materials = list(MAT_PLASTEEL, MAT_DURASTEEL, MAT_VERDANTIUM, MAT_MORPHIUM, MAT_METALHYDROGEN, MAT_SUPERMATTER)
use_power = 1
idle_power_usage = 30
@@ -68,7 +70,7 @@ using metal and glass, it uses glass and reagents (usually sulphuric acid).
T = 0
for(var/obj/item/weapon/stock_parts/manipulator/M in component_parts)
T += M.rating
- mat_efficiency = 1 - (T - 1) / 4
+ mat_efficiency = max(1 - (T - 1) / 4, 0.2)
speed = T
/obj/machinery/r_n_d/circuit_imprinter/update_icon()
@@ -143,12 +145,12 @@ using metal and glass, it uses glass and reagents (usually sulphuric acid).
max_res_amount -= materials[mat]
if(materials[S.material.name] + amnt <= max_res_amount)
- if(S && S.amount >= 1)
+ if(S && S.get_amount() >= 1)
var/count = 0
overlays += "fab-load-metal"
spawn(10)
overlays -= "fab-load-metal"
- while(materials[S.material.name] + amnt <= max_res_amount && S.amount >= 1)
+ while(materials[S.material.name] + amnt <= max_res_amount && S.get_amount() >= 1)
materials[S.material.name] += amnt
S.use(1)
count++
diff --git a/code/modules/research/designs/illegal.dm b/code/modules/research/designs/illegal.dm
index 1542e929b6..c195b5764b 100644
--- a/code/modules/research/designs/illegal.dm
+++ b/code/modules/research/designs/illegal.dm
@@ -16,4 +16,11 @@
req_tech = list(TECH_ILLEGAL = 2)
materials = list(DEFAULT_WALL_MATERIAL = 500)
build_path = /obj/item/weapon/storage/box/syndie_kit/chameleon
- sort_string = "VASBA"
\ No newline at end of file
+ sort_string = "VASBA"
+
+/datum/design/item/weapon/esword
+ id = "chargesword"
+ req_tech = list(TECH_COMBAT = 6, TECH_MAGNET = 4, TECH_ENGINEERING = 5, TECH_ILLEGAL = 4, TECH_ANOMALY = 1)
+ materials = list(MAT_PLASTEEL = 3500, "glass" = 1000, MAT_LEAD = 2250, MAT_METALHYDROGEN = 500)
+ build_path = /obj/item/weapon/melee/energy/sword/charge
+ sort_string = "VASCA"
diff --git a/code/modules/research/designs/precursor.dm b/code/modules/research/designs/precursor.dm
new file mode 100644
index 0000000000..88575e5667
--- /dev/null
+++ b/code/modules/research/designs/precursor.dm
@@ -0,0 +1,70 @@
+/*
+ * Contains Precursor and Anomalous designs for the Protolathe.
+ */
+
+/datum/design/item/precursor/AssembleDesignName()
+ ..()
+ name = "Alien prototype ([item_name])"
+
+/datum/design/item/precursor/AssembleDesignDesc()
+ if(!desc)
+ if(build_path)
+ var/obj/item/I = build_path
+ desc = initial(I.desc)
+ ..()
+
+/datum/design/item/precursor/crowbar
+ name = "Hybrid Crowbar"
+ desc = "A tool utilizing cutting edge modern technology, and ancient component designs."
+ id = "hybridcrowbar"
+ req_tech = list(TECH_ENGINEERING = 6, TECH_MATERIAL = 6, TECH_BLUESPACE = 3, TECH_PRECURSOR = 1)
+ materials = list(MAT_PLASTEEL = 2000, MAT_VERDANTIUM = 3000, MAT_GOLD = 250, MAT_URANIUM = 2500)
+ build_path = /obj/item/weapon/tool/crowbar/hybrid
+ sort_string = "PATAC"
+
+/datum/design/item/precursor/wrench
+ name = "Hybrid Wrench"
+ desc = "A tool utilizing cutting edge modern technology, and ancient component designs."
+ id = "hybridwrench"
+ req_tech = list(TECH_ENGINEERING = 6, TECH_MATERIAL = 5, TECH_BLUESPACE = 2, TECH_MAGNET = 3, TECH_PRECURSOR = 1)
+ materials = list(MAT_PLASTEEL = 2000, MAT_VERDANTIUM = 3000, MAT_SILVER = 300, MAT_URANIUM = 2000)
+ build_path = /obj/item/weapon/tool/wrench/hybrid
+ sort_string = "PATAW"
+
+/datum/design/item/precursor/screwdriver
+ name = "Hybrid Screwdriver"
+ desc = "A tool utilizing cutting edge modern technology, and ancient component designs."
+ id = "hybridscrewdriver"
+ req_tech = list(TECH_ENGINEERING = 4, TECH_MATERIAL = 5, TECH_BLUESPACE = 2, TECH_MAGNET = 3, TECH_PRECURSOR = 1)
+ materials = list(MAT_PLASTEEL = 2000, MAT_VERDANTIUM = 3000, MAT_PLASTIC = 8000, MAT_DIAMOND = 2000)
+ build_path = /obj/item/weapon/tool/screwdriver/hybrid
+ sort_string = "PATAS"
+
+/datum/design/item/precursor/wirecutters
+ name = "Hybrid Wirecutters"
+ desc = "A tool utilizing cutting edge modern technology, and ancient component designs."
+ id = "hybridwirecutters"
+ req_tech = list(TECH_ENGINEERING = 4, TECH_MATERIAL = 5, TECH_PHORON = 2, TECH_PRECURSOR = 1)
+ materials = list(MAT_PLASTEEL = 2000, MAT_VERDANTIUM = 3000, MAT_PLASTIC = 8000, MAT_PHORON = 2750, MAT_DIAMOND = 2000)
+ build_path = /obj/item/weapon/tool/wirecutters/hybrid
+ sort_string = "PATBW"
+
+/datum/design/item/precursor/welder
+ name = "Hybrid Welding Tool"
+ desc = "A tool utilizing cutting edge modern technology, and ancient component designs."
+ id = "hybridwelder"
+ req_tech = list(TECH_ENGINEERING = 6, TECH_MATERIAL = 6, TECH_BLUESPACE = 3, TECH_PHORON = 3, TECH_MAGNET = 5, TECH_PRECURSOR = 1)
+ materials = list(MAT_DURASTEEL = 2000, MAT_MORPHIUM = 3000, MAT_METALHYDROGEN = 4750, MAT_URANIUM = 6000)
+ build_path = /obj/item/weapon/weldingtool/experimental/hybrid
+ sort_string = "PATCW"
+
+/datum/design/item/anomaly/AssembleDesignName()
+ ..()
+ name = "Anomalous prototype ([item_name])"
+
+/datum/design/item/anomaly/AssembleDesignDesc()
+ if(!desc)
+ if(build_path)
+ var/obj/item/I = build_path
+ desc = initial(I.desc)
+ ..()
\ No newline at end of file
diff --git a/code/modules/research/designs/stock_parts.dm b/code/modules/research/designs/stock_parts.dm
index fb629e7ad7..b928c6bb99 100644
--- a/code/modules/research/designs/stock_parts.dm
+++ b/code/modules/research/designs/stock_parts.dm
@@ -4,6 +4,7 @@
/datum/design/item/stock_part
build_type = PROTOLATHE
+ time = 3 //Sets an independent time for stock parts, currently one third normal print time.
/datum/design/item/stock_part/AssembleDesignName()
..()
@@ -34,6 +35,20 @@
build_path = /obj/item/weapon/stock_parts/capacitor/super
sort_string = "CAAAC"
+/datum/design/item/stock_part/hyper_capacitor
+ id = "hyper_capacitor"
+ req_tech = list(TECH_POWER = 6, TECH_MATERIAL = 5, TECH_BLUESPACE = 1, TECH_ARCANE = 1)
+ materials = list(DEFAULT_WALL_MATERIAL = 200, MAT_GLASS = 100, MAT_VERDANTIUM = 30, MAT_DURASTEEL = 25)
+ build_path = /obj/item/weapon/stock_parts/capacitor/hyper
+ sort_string = "CAAAD"
+
+/datum/design/item/stock_part/omni_capacitor
+ id = "omni_capacitor"
+ req_tech = list(TECH_POWER = 7, TECH_MATERIAL = 6, TECH_BLUESPACE = 3, TECH_PRECURSOR = 1)
+ materials = list(DEFAULT_WALL_MATERIAL = 2000, MAT_DIAMOND = 1000, MAT_GLASS = 1000, MAT_MORPHIUM = 100, MAT_DURASTEEL = 100)
+ build_path = /obj/item/weapon/stock_parts/capacitor/omni
+ sort_string = "CAAAE"
+
/datum/design/item/stock_part/micro_mani
id = "micro_mani"
req_tech = list(TECH_MATERIAL = 1, TECH_DATA = 1)
@@ -55,6 +70,20 @@
build_path = /obj/item/weapon/stock_parts/manipulator/pico
sort_string = "CAABC"
+/datum/design/item/stock_part/hyper_mani
+ id = "hyper_mani"
+ req_tech = list(TECH_MATERIAL = 6, TECH_DATA = 3, TECH_ARCANE = 2)
+ materials = list(DEFAULT_WALL_MATERIAL = 200, MAT_VERDANTIUM = 50, MAT_DURASTEEL = 50)
+ build_path = /obj/item/weapon/stock_parts/manipulator/hyper
+ sort_string = "CAABD"
+
+/datum/design/item/stock_part/omni_mani
+ id = "omni_mani"
+ req_tech = list(TECH_MATERIAL = 7, TECH_DATA = 4, TECH_PRECURSOR = 2)
+ materials = list(DEFAULT_WALL_MATERIAL = 2000, MAT_PLASTEEL = 500, MAT_MORPHIUM = 100, MAT_DURASTEEL = 100)
+ build_path = /obj/item/weapon/stock_parts/manipulator/omni
+ sort_string = "CAABE"
+
/datum/design/item/stock_part/basic_matter_bin
id = "basic_matter_bin"
req_tech = list(TECH_MATERIAL = 1)
@@ -76,6 +105,20 @@
build_path = /obj/item/weapon/stock_parts/matter_bin/super
sort_string = "CAACC"
+/datum/design/item/stock_part/hyper_matter_bin
+ id = "hyper_matter_bin"
+ req_tech = list(TECH_MATERIAL = 6, TECH_ARCANE = 2)
+ materials = list(DEFAULT_WALL_MATERIAL = 200, MAT_VERDANTIUM = 60, MAT_DURASTEEL = 75)
+ build_path = /obj/item/weapon/stock_parts/matter_bin/hyper
+ sort_string = "CAACD"
+
+/datum/design/item/stock_part/omni_matter_bin
+ id = "omni_matter_bin"
+ req_tech = list(TECH_MATERIAL = 7, TECH_PRECURSOR = 2)
+ materials = list(DEFAULT_WALL_MATERIAL = 2000, MAT_PLASTEEL = 100, MAT_MORPHIUM = 100, MAT_DURASTEEL = 100)
+ build_path = /obj/item/weapon/stock_parts/matter_bin/omni
+ sort_string = "CAACE"
+
/datum/design/item/stock_part/basic_micro_laser
id = "basic_micro_laser"
req_tech = list(TECH_MAGNET = 1)
@@ -97,6 +140,20 @@
build_path = /obj/item/weapon/stock_parts/micro_laser/ultra
sort_string = "CAADC"
+/datum/design/item/stock_part/hyper_micro_laser
+ id = "hyper_micro_laser"
+ req_tech = list(TECH_MAGNET = 6, TECH_MATERIAL = 6, TECH_ARCANE = 2)
+ materials = list(DEFAULT_WALL_MATERIAL = 200, MAT_GLASS = 20, MAT_URANIUM = 30, MAT_VERDANTIUM = 50, MAT_DURASTEEL = 100)
+ build_path = /obj/item/weapon/stock_parts/micro_laser/hyper
+ sort_string = "CAADD"
+
+/datum/design/item/stock_part/omni_micro_laser
+ id = "omni_micro_laser"
+ req_tech = list(TECH_MAGNET = 7, TECH_MATERIAL = 7, TECH_PRECURSOR = 2)
+ materials = list(DEFAULT_WALL_MATERIAL = 2000, MAT_GLASS = 500, MAT_URANIUM = 2000, MAT_MORPHIUM = 50, MAT_DURASTEEL = 100)
+ build_path = /obj/item/weapon/stock_parts/micro_laser/omni
+ sort_string = "CAADE"
+
/datum/design/item/stock_part/basic_sensor
id = "basic_sensor"
req_tech = list(TECH_MAGNET = 1)
@@ -118,6 +175,20 @@
build_path = /obj/item/weapon/stock_parts/scanning_module/phasic
sort_string = "CAAEC"
+/datum/design/item/stock_part/hyper_sensor
+ id = "hyper_sensor"
+ req_tech = list(TECH_MAGNET = 6, TECH_MATERIAL = 4, TECH_ARCANE = 1)
+ materials = list(DEFAULT_WALL_MATERIAL = 50, MAT_GLASS = 20, MAT_SILVER = 50, MAT_VERDANTIUM = 40, MAT_DURASTEEL = 50)
+ build_path = /obj/item/weapon/stock_parts/scanning_module/hyper
+ sort_string = "CAAED"
+
+/datum/design/item/stock_part/omni_sensor
+ id = "omni_sensor"
+ req_tech = list(TECH_MAGNET = 7, TECH_MATERIAL = 5, TECH_PRECURSOR = 1)
+ materials = list(DEFAULT_WALL_MATERIAL = 1000, MAT_PLASTEEL = 500, MAT_GLASS = 750, MAT_SILVER = 500, MAT_MORPHIUM = 60, MAT_DURASTEEL = 100)
+ build_path = /obj/item/weapon/stock_parts/scanning_module/omni
+ sort_string = "CAAEE"
+
/datum/design/item/stock_part/subspace_ansible
id = "s-ansible"
req_tech = list(TECH_DATA = 3, TECH_MAGNET = 4, TECH_MATERIAL = 4, TECH_BLUESPACE = 2)
@@ -167,7 +238,7 @@
build_path = /obj/item/weapon/stock_parts/subspace/transmitter
sort_string = "UAAAG"
-// RPED lives here because it handles stock parts
+// RPEDs live here because they handle stock parts
/datum/design/item/stock_part/RPED
name = "Rapid Part Exchange Device"
desc = "Special mechanical module made to store, sort, and apply standard machine parts."
@@ -175,4 +246,13 @@
req_tech = list(TECH_ENGINEERING = 3, TECH_MATERIAL = 3)
materials = list(DEFAULT_WALL_MATERIAL = 15000, "glass" = 5000)
build_path = /obj/item/weapon/storage/part_replacer
- sort_string = "CBAAA"
\ No newline at end of file
+ sort_string = "CBAAA"
+
+/datum/design/item/stock_part/ARPED
+ name = "Advanced Rapid Part Exchange Device"
+ desc = "Special mechanical module made to store, sort, and apply standard machine parts. This one has a greatly upgraded storage capacity."
+ id = "arped"
+ req_tech = list(TECH_ENGINEERING = 5, TECH_MATERIAL = 5)
+ materials = list(DEFAULT_WALL_MATERIAL = 30000, "glass" = 10000)
+ build_path = /obj/item/weapon/storage/part_replacer/adv
+ sort_string = "CBAAB"
\ No newline at end of file
diff --git a/code/modules/research/mechfab_designs.dm b/code/modules/research/mechfab_designs.dm
index c225af1258..bba633c6bc 100644
--- a/code/modules/research/mechfab_designs.dm
+++ b/code/modules/research/mechfab_designs.dm
@@ -223,6 +223,67 @@
time = 60
materials = list(DEFAULT_WALL_MATERIAL = 37500, "uranium" = 7500)
+/datum/design/item/mechfab/janus
+ category = "Janus"
+ req_tech = list(TECH_MATERIAL = 7, TECH_BLUESPACE = 5, TECH_MAGNET = 6, TECH_PHORON = 3, TECH_ARCANE = 1, TECH_PRECURSOR = 2)
+
+/datum/design/item/mechfab/janus/chassis
+ name = "Janus Chassis"
+ id = "janus_chassis"
+ build_path = /obj/item/mecha_parts/chassis/janus
+ time = 100
+ materials = list(MAT_DURASTEEL = 19000, MAT_MORPHIUM = 10500, MAT_PLASTEEL = 5500, MAT_LEAD = 2500)
+ req_tech = list(TECH_MATERIAL = 7, TECH_BLUESPACE = 5, TECH_MAGNET = 6, TECH_PHORON = 3, TECH_ARCANE = 1, TECH_PRECURSOR = 3)
+
+/datum/design/item/mechfab/janus/torso
+ name = "Imperion Torso"
+ id = "janus_torso"
+ build_path = /obj/item/mecha_parts/part/janus_torso
+ time = 300
+ materials = list(DEFAULT_WALL_MATERIAL = 30000, MAT_DURASTEEL = 8000, MAT_MORPHIUM = 10000, MAT_GOLD = 5000, MAT_VERDANTIUM = 5000)
+
+/datum/design/item/mechfab/janus/head
+ name = "Imperion Head"
+ id = "janus_head"
+ build_path = /obj/item/mecha_parts/part/janus_head
+ time = 200
+ materials = list(DEFAULT_WALL_MATERIAL = 30000, MAT_DURASTEEL = 2000, MAT_MORPHIUM = 6000, MAT_GOLD = 5000)
+
+/datum/design/item/mechfab/janus/left_arm
+ name = "Prototype Gygax Left Arm"
+ id = "janus_left_arm"
+ build_path = /obj/item/mecha_parts/part/janus_left_arm
+ time = 200
+ materials = list(DEFAULT_WALL_MATERIAL = 30000, MAT_METALHYDROGEN = 3000, MAT_DURASTEEL = 2000, MAT_MORPHIUM = 3000, MAT_GOLD = 5000, MAT_DIAMOND = 7000)
+
+/datum/design/item/mechfab/janus/right_arm
+ name = "Prototype Gygax Right Arm"
+ id = "janus_right_arm"
+ build_path = /obj/item/mecha_parts/part/janus_right_arm
+ time = 200
+ materials = list(DEFAULT_WALL_MATERIAL = 30000, MAT_METALHYDROGEN = 3000, MAT_DURASTEEL = 2000, MAT_MORPHIUM = 3000, MAT_GOLD = 5000, MAT_DIAMOND = 7000)
+
+/datum/design/item/mechfab/janus/left_leg
+ name = "Prototype Durand Left Leg"
+ id = "janus_left_leg"
+ build_path = /obj/item/mecha_parts/part/janus_left_leg
+ time = 200
+ materials = list(DEFAULT_WALL_MATERIAL = 30000, MAT_METALHYDROGEN = 3000, MAT_DURASTEEL = 2000, MAT_MORPHIUM = 3000, MAT_GOLD = 5000, MAT_URANIUM = 7000)
+
+/datum/design/item/mechfab/janus/right_leg
+ name = "Prototype Durand Right Leg"
+ id = "janus_right_leg"
+ build_path = /obj/item/mecha_parts/part/janus_right_leg
+ time = 200
+ materials = list(DEFAULT_WALL_MATERIAL = 30000, MAT_METALHYDROGEN = 3000, MAT_DURASTEEL = 2000, MAT_MORPHIUM = 3000, MAT_GOLD = 5000, MAT_URANIUM = 7000)
+
+/datum/design/item/mechfab/janus/phase_coil
+ name = "Janus Phase Coil"
+ id = "janus_coil"
+ build_path = /obj/item/prop/alien/phasecoil
+ time = 600
+ materials = list(MAT_SUPERMATTER = 2000, MAT_PLASTEEL = 60000, MAT_URANIUM = 3250, MAT_DURASTEEL = 2000, MAT_MORPHIUM = 3000, MAT_GOLD = 5000, MAT_VERDANTIUM = 5000, MAT_DIAMOND = 10000, MAT_LEAD = 15000)
+
/datum/design/item/mecha
build_type = MECHFAB
category = "Exosuit Equipment"
diff --git a/code/modules/research/prosfab_designs.dm b/code/modules/research/prosfab_designs.dm
index e904ff1b5e..43399ce322 100644
--- a/code/modules/research/prosfab_designs.dm
+++ b/code/modules/research/prosfab_designs.dm
@@ -284,6 +284,10 @@
id = "armour"
build_path = /obj/item/robot_parts/robot_component/armour
+/datum/design/item/prosfab/cyborg/component/ai_shell
+ name = "AI Remote Interface"
+ id = "mmi_ai_shell"
+ build_path = /obj/item/device/mmi/inert/ai_remote
//////////////////// Cyborg Modules ////////////////////
/datum/design/item/prosfab/robot_upgrade
@@ -353,4 +357,85 @@
id = "borg_language_module"
req_tech = list(TECH_DATA = 6, TECH_MATERIAL = 6)
materials = list(DEFAULT_WALL_MATERIAL = 25000, "glass" = 3000, "gold" = 350)
- build_path = /obj/item/borg/upgrade/language
\ No newline at end of file
+ build_path = /obj/item/borg/upgrade/language
+
+// Synthmorph Bags.
+
+/datum/design/item/prosfab/synthmorphbag
+ name = "Synthmorph Storage Bag"
+ desc = "Used to store or slowly defragment an FBP."
+ id = "misc_synth_bag"
+ materials = list(DEFAULT_WALL_MATERIAL = 250, "glass" = 250, "plastic" = 2000)
+ build_path = /obj/item/bodybag/cryobag/robobag
+
+/datum/design/item/prosfab/badge_nt
+ name = "NanoTrasen Tag"
+ desc = "Used to identify an empty NanoTrasen FBP."
+ id = "misc_synth_bag_tag_nt"
+ materials = list(DEFAULT_WALL_MATERIAL = 1000, "glass" = 500, "plastic" = 1000)
+ build_path = /obj/item/clothing/accessory/badge/corporate_tag
+
+/datum/design/item/prosfab/badge_morph
+ name = "Morpheus Tag"
+ desc = "Used to identify an empty Morpheus FBP."
+ id = "misc_synth_bag_tag_morph"
+ materials = list(DEFAULT_WALL_MATERIAL = 1000, "glass" = 500, "plastic" = 1000)
+ build_path = /obj/item/clothing/accessory/badge/corporate_tag/morpheus
+
+/datum/design/item/prosfab/badge_wardtaka
+ name = "Ward-Takahashi Tag"
+ desc = "Used to identify an empty Ward-Takahashi FBP."
+ id = "misc_synth_bag_tag_wardtaka"
+ materials = list(DEFAULT_WALL_MATERIAL = 1000, "glass" = 500, "plastic" = 1000)
+ build_path = /obj/item/clothing/accessory/badge/corporate_tag/wardtaka
+
+/datum/design/item/prosfab/badge_zenghu
+ name = "Zeng-Hu Tag"
+ desc = "Used to identify an empty Zeng-Hu FBP."
+ id = "misc_synth_bag_tag_zenghu"
+ materials = list(DEFAULT_WALL_MATERIAL = 1000, "glass" = 500, "plastic" = 1000)
+ build_path = /obj/item/clothing/accessory/badge/corporate_tag/zenghu
+
+/datum/design/item/prosfab/badge_gilthari
+ name = "Gilthari Tag"
+ desc = "Used to identify an empty Gilthari FBP."
+ id = "misc_synth_bag_tag_gilthari"
+ materials = list(DEFAULT_WALL_MATERIAL = 1000, "glass" = 500, "gold" = 1000)
+ build_path = /obj/item/clothing/accessory/badge/corporate_tag/gilthari
+ req_tech = list(TECH_MATERIAL = 4, TECH_ILLEGAL = 2, TECH_PHORON = 2)
+
+/datum/design/item/prosfab/badge_veymed
+ name = "Vey-Medical Tag"
+ desc = "Used to identify an empty Vey-Medical FBP."
+ id = "misc_synth_bag_tag_veymed"
+ materials = list(DEFAULT_WALL_MATERIAL = 1000, "glass" = 500, "plastic" = 1000)
+ build_path = /obj/item/clothing/accessory/badge/corporate_tag/veymed
+ req_tech = list(TECH_MATERIAL = 3, TECH_ILLEGAL = 1, TECH_BIO = 4)
+
+/datum/design/item/prosfab/badge_hephaestus
+ name = "Hephaestus Tag"
+ desc = "Used to identify an empty Hephaestus FBP."
+ id = "misc_synth_bag_tag_heph"
+ materials = list(DEFAULT_WALL_MATERIAL = 1000, "glass" = 500, "plastic" = 1000)
+ build_path = /obj/item/clothing/accessory/badge/corporate_tag/hephaestus
+
+/datum/design/item/prosfab/badge_grayson
+ name = "Grayson Tag"
+ desc = "Used to identify an empty Grayson FBP."
+ id = "misc_synth_bag_tag_grayson"
+ materials = list(DEFAULT_WALL_MATERIAL = 1000, "glass" = 500, "plastic" = 1000)
+ build_path = /obj/item/clothing/accessory/badge/corporate_tag/grayson
+
+/datum/design/item/prosfab/badge_xion
+ name = "Xion Tag"
+ desc = "Used to identify an empty Xion FBP."
+ id = "misc_synth_bag_tag_xion"
+ materials = list(DEFAULT_WALL_MATERIAL = 1000, "glass" = 500, "plastic" = 1000)
+ build_path = /obj/item/clothing/accessory/badge/corporate_tag/xion
+
+/datum/design/item/prosfab/badge_bishop
+ name = "Bishop Tag"
+ desc = "Used to identify an empty Bishop FBP."
+ id = "misc_synth_bag_tag_bishop"
+ materials = list(DEFAULT_WALL_MATERIAL = 500, "glass" = 2000, "plastic" = 500)
+ build_path = /obj/item/clothing/accessory/badge/corporate_tag/bishop
diff --git a/code/modules/research/protolathe.dm b/code/modules/research/protolathe.dm
index bea555b70c..dc3a0cf437 100644
--- a/code/modules/research/protolathe.dm
+++ b/code/modules/research/protolathe.dm
@@ -15,7 +15,9 @@
var/mat_efficiency = 1
var/speed = 1
- materials = list(DEFAULT_WALL_MATERIAL = 0, "glass" = 0, "plastic" = 0, "gold" = 0, "silver" = 0, "osmium" = 0, "phoron" = 0, "uranium" = 0, "diamond" = 0)
+ materials = list(DEFAULT_WALL_MATERIAL = 0, "glass" = 0, MAT_PLASTEEL = 0, "plastic" = 0, "gold" = 0, "silver" = 0, "osmium" = 0, MAT_LEAD = 0, "phoron" = 0, "uranium" = 0, "diamond" = 0, MAT_DURASTEEL = 0, MAT_VERDANTIUM = 0, MAT_MORPHIUM = 0, MAT_METALHYDROGEN = 0, MAT_SUPERMATTER = 0)
+
+ hidden_materials = list(MAT_PLASTEEL, MAT_DURASTEEL, MAT_VERDANTIUM, MAT_MORPHIUM, MAT_METALHYDROGEN, MAT_SUPERMATTER)
/obj/machinery/r_n_d/protolathe/Initialize()
. = ..()
@@ -71,7 +73,7 @@
T = 0
for(var/obj/item/weapon/stock_parts/manipulator/M in component_parts)
T += M.rating
- mat_efficiency = 1 - (T - 2) / 8
+ mat_efficiency = max(1 - (T - 2) / 8, 0.2)
speed = T / 2
/obj/machinery/r_n_d/protolathe/dismantle()
@@ -131,12 +133,12 @@
max_res_amount -= materials[mat]
if(materials[S.material.name] + amnt <= max_res_amount)
- if(S && S.amount >= 1)
+ if(S && S.get_amount() >= 1)
var/count = 0
overlays += "fab-load-metal"
spawn(10)
overlays -= "fab-load-metal"
- while(materials[S.material.name] + amnt <= max_res_amount && S.amount >= 1)
+ while(materials[S.material.name] + amnt <= max_res_amount && S.get_amount() >= 1)
materials[S.material.name] += amnt
S.use(1)
count++
@@ -207,28 +209,17 @@
/obj/machinery/r_n_d/protolathe/proc/eject_materials(var/material, var/amount) // 0 amount = 0 means ejecting a full stack; -1 means eject everything
var/recursive = amount == -1 ? 1 : 0
material = lowertext(material)
- var/mattype
- switch(material)
- if(DEFAULT_WALL_MATERIAL)
- mattype = /obj/item/stack/material/steel
- if("glass")
- mattype = /obj/item/stack/material/glass
- if("plastic")
- mattype = /obj/item/stack/material/plastic
- if("gold")
- mattype = /obj/item/stack/material/gold
- if("silver")
- mattype = /obj/item/stack/material/silver
- if("osmium")
- mattype = /obj/item/stack/material/osmium
- if("diamond")
- mattype = /obj/item/stack/material/diamond
- if("phoron")
- mattype = /obj/item/stack/material/phoron
- if("uranium")
- mattype = /obj/item/stack/material/uranium
- else
- return
+ var/obj/item/stack/material/mattype
+ var/material/MAT = get_material_by_name(material)
+
+ if(!MAT)
+ return
+
+ mattype = MAT.stack_type
+
+ if(!mattype)
+ return
+
var/obj/item/stack/material/S = new mattype(loc)
if(amount <= 0)
amount = S.max_amount
diff --git a/code/modules/research/rdconsole.dm b/code/modules/research/rdconsole.dm
index 38cba96c84..5346f50e6e 100755
--- a/code/modules/research/rdconsole.dm
+++ b/code/modules/research/rdconsole.dm
@@ -660,6 +660,13 @@ won't update every console in existence) but it's more of a hassle to do. Also,
dat += "