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 += "