diff --git a/code/__defines/gamemode.dm b/code/__defines/gamemode.dm index cd0c9955c1..401029c962 100644 --- a/code/__defines/gamemode.dm +++ b/code/__defines/gamemode.dm @@ -84,6 +84,7 @@ var/list/be_special_flags = list( #define MODE_MALFUNCTION "malf" #define MODE_TRAITOR "traitor" #define MODE_AUTOTRAITOR "autotraitor" +#define MODE_INFILTRATOR "infiltrator" #define DEFAULT_TELECRYSTAL_AMOUNT 120 diff --git a/code/_helpers/game.dm b/code/_helpers/game.dm index fb99da896f..ddcae4980b 100644 --- a/code/_helpers/game.dm +++ b/code/_helpers/game.dm @@ -281,15 +281,15 @@ //A list of every mob with a client for(var/mob in player_list) - if(isnull(mob)) //VOREStation Add - Fix for a weird bug with nulls in this list - player_list -= mob //VOREStation Add - continue //VOREStation Add + if(!istype(mob, /mob)) + crash_with("There is a null or non-mob reference inside player_list.") + continue if(get_turf(mob) in hearturfs) mobs |= mob continue var/mob/M = mob - if(M.stat == DEAD && !M.forbid_seeing_deadchat) + if(M && M.stat == DEAD && !M.forbid_seeing_deadchat) switch(type) if(1) //Audio messages use ghost_ears if(M.is_preference_enabled(/datum/client_preference/ghost_ears)) diff --git a/code/controllers/Processes/inactivity.dm b/code/controllers/Processes/inactivity.dm index 26cc136ccc..b1029e0391 100644 --- a/code/controllers/Processes/inactivity.dm +++ b/code/controllers/Processes/inactivity.dm @@ -6,7 +6,7 @@ if(config.kick_inactive) for(last_object in clients) var/client/C = last_object - if(!C.holder && C.is_afk(config.kick_inactive MINUTES)) + if(C.is_afk(config.kick_inactive MINUTES)) if(!istype(C.mob, /mob/observer/dead)) log_access("AFK: [key_name(C)]") C << "You have been inactive for more than [config.kick_inactive] minute\s and have been disconnected." diff --git a/code/datums/autolathe/arms.dm b/code/datums/autolathe/arms.dm index b40bf27e47..769aa0b2b6 100644 --- a/code/datums/autolathe/arms.dm +++ b/code/datums/autolathe/arms.dm @@ -132,9 +132,9 @@ path =/obj/item/ammo_magazine/m10mm hidden = 1 -/datum/category_item/autolathe/arms/pistol_50 - name = "pistol magazine (.50AE)" - path =/obj/item/ammo_magazine/m50 +/datum/category_item/autolathe/arms/pistol_44 + name = "pistol magazine (.44)" + path =/obj/item/ammo_magazine/m44 hidden = 1 /////// 5.56mm @@ -225,9 +225,9 @@ category = "Arms and Ammunition" hidden = 1 -/datum/category_item/autolathe/arms/pistol_50 - name = "pistol magazine (.50AE)" - path =/obj/item/ammo_magazine/m50/empty +/datum/category_item/autolathe/arms/pistol_44 + name = "pistol magazine (.44)" + path =/obj/item/ammo_magazine/m44/empty category = "Arms and Ammunition" hidden = 1 @@ -340,7 +340,7 @@ hidden = 1 /datum/category_item/autolathe/arms/pistol_clip_50 - name = "ammo clip (.50AE)" + name = "ammo clip (.44)" path =/obj/item/ammo_magazine/clip/c50 category = "Arms and Ammunition" hidden = 1 diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 11d622b255..0b524befec 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -490,6 +490,8 @@ world.log << "## DEBUG: mind_initialize(): No ticker ready yet! Please inform Carn" if(!mind.name) mind.name = real_name mind.current = src + if(player_is_antag(mind)) + src.client.verbs += /client/proc/aooc //HUMAN /mob/living/carbon/human/mind_initialize() diff --git a/code/datums/supplypacks/medical.dm b/code/datums/supplypacks/medical.dm index dc49b380dc..1b7207069e 100644 --- a/code/datums/supplypacks/medical.dm +++ b/code/datums/supplypacks/medical.dm @@ -331,7 +331,7 @@ /datum/supply_packs/med/defib name = "Defibrilator crate" - contains = list(/obj/item/device/defib_kit = 4) + contains = list(/obj/item/device/defib_kit = 2) cost = 30 containertype = /obj/structure/closet/crate/medical containername = "Defibrilator crate" \ No newline at end of file diff --git a/code/datums/supplypacks/robotics.dm b/code/datums/supplypacks/robotics.dm index 0f1a0c3b66..8f0ed6d452 100644 --- a/code/datums/supplypacks/robotics.dm +++ b/code/datums/supplypacks/robotics.dm @@ -159,7 +159,7 @@ /obj/item/device/kit/paint/gygax/recitence ) name = "Random Gygax exosuit modkit" - +/* /datum/supply_packs/robotics/jumper_cables name = "Jumper kit crate" contains = list( @@ -168,4 +168,5 @@ cost = 30 containertype = /obj/structure/closet/crate/secure/science containername = "Jumper kit crate" - access = access_robotics \ No newline at end of file + access = access_robotics +*/ \ No newline at end of file diff --git a/code/game/antagonist/antagonist.dm b/code/game/antagonist/antagonist.dm index 44d32bfecb..4b2afa1df3 100644 --- a/code/game/antagonist/antagonist.dm +++ b/code/game/antagonist/antagonist.dm @@ -79,6 +79,8 @@ Think through your actions and make the roleplay immersive! Please remember all \ rules aside from those without explicit exceptions apply to antagonists." + var/can_use_aooc = TRUE // If true, will be given the AOOC verb, along with the ability to use it. + /datum/antagonist/New() ..() cur_max = hard_cap diff --git a/code/game/antagonist/antagonist_add.dm b/code/game/antagonist/antagonist_add.dm index e42888737f..67eb7e2e72 100644 --- a/code/game/antagonist/antagonist_add.dm +++ b/code/game/antagonist/antagonist_add.dm @@ -36,6 +36,9 @@ and it otherwise has no bearing on your round." player.current.verbs |= /mob/living/proc/write_ambition + if(can_use_aooc) + player.current.client.verbs += /client/proc/aooc + // Handle only adding a mind and not bothering with gear etc. if(nonstandard_role_type) faction_members |= player @@ -58,6 +61,7 @@ BITSET(player.current.hud_updateflag, SPECIALROLE_HUD) if(!is_special_character(player)) player.current.verbs -= /mob/living/proc/write_ambition + player.current.client.verbs -= /client/proc/aooc player.ambitions = "" return 1 return 0 \ No newline at end of file diff --git a/code/game/antagonist/outsider/ert.dm b/code/game/antagonist/outsider/ert.dm index b176df2467..c1aec2bcf8 100644 --- a/code/game/antagonist/outsider/ert.dm +++ b/code/game/antagonist/outsider/ert.dm @@ -25,6 +25,8 @@ var/datum/antagonist/ert/ert initial_spawn_req = 5 initial_spawn_target = 7 + can_use_aooc = FALSE // They're the good guys. + /datum/antagonist/ert/create_default(var/mob/source) var/mob/living/carbon/human/M = ..() if(istype(M)) M.age = rand(25,45) diff --git a/code/game/antagonist/outsider/trader.dm b/code/game/antagonist/outsider/trader.dm index aaf573cd20..9603b49523 100644 --- a/code/game/antagonist/outsider/trader.dm +++ b/code/game/antagonist/outsider/trader.dm @@ -24,6 +24,8 @@ var/datum/antagonist/trader/traders initial_spawn_req = 5 initial_spawn_target = 7 + can_use_aooc = FALSE // They're not real antags. + /datum/antagonist/trader/create_default(var/mob/source) var/mob/living/carbon/human/M = ..() if(istype(M)) M.age = rand(25,45) diff --git a/code/game/antagonist/station/infiltrator.dm b/code/game/antagonist/station/infiltrator.dm new file mode 100644 index 0000000000..81430c0006 --- /dev/null +++ b/code/game/antagonist/station/infiltrator.dm @@ -0,0 +1,78 @@ +// Infiltrator is a varient of Traitor, except that the traitors are in a team and can communicate with a special headset. + +var/datum/antagonist/traitor/infiltrator/infiltrators + +// Inherits most of its vars from the base datum. +/datum/antagonist/traitor/infiltrator + id = MODE_INFILTRATOR + role_type = BE_TRAITOR + antag_indicator = "synd" + antaghud_indicator = "hudinfiltrator" + role_text = "Infiltrator" + role_text_plural = "Infiltrators" + welcome_text = "To speak on your team's private channel, use :t." + protected_jobs = list("Security Officer", "Warden", "Detective", "Internal Affairs Agent", "Head of Security", "Colony Director") + flags = ANTAG_SUSPICIOUS | ANTAG_RANDSPAWN | ANTAG_VOTABLE + +/datum/antagonist/traitor/infiltrator/New() + ..() + infiltrators = src + +/datum/antagonist/traitor/infiltrator/equip(var/mob/living/carbon/human/traitor_mob) + ..() // Give the uplink and other stuff. + // Now for the special headset. + + // Humans and the AI. + if(istype(traitor_mob) || istype(traitor_mob, /mob/living/silicon/ai)) + var/obj/item/device/radio/headset/R + R = locate(/obj/item/device/radio/headset) in traitor_mob.contents + if(!R) + to_chat(traitor_mob, "Unfortunately, a headset could not be found. You have been given an encryption key \ + to put into a new headset. Once that is done, you can talk to your team using :t") + var/obj/item/device/encryptionkey/syndicate/encrypt_key = new(null) + traitor_mob.equip_to_slot_or_del(encrypt_key, slot_in_backpack) + else + var/obj/item/device/encryptionkey/syndicate/encrypt_key = new(null) + if(R.keyslot1 && R.keyslot2) // No room. + to_chat(traitor_mob, "Unfortunately, your headset cannot accept anymore encryption keys. You have been given an encryption key \ + to put into a headset after making some room instead. Once that is done, you can talk to your team using :t") + traitor_mob.equip_to_slot_or_del(encrypt_key, slot_in_backpack) + else + if(R.keyslot1) + R.keyslot2 = encrypt_key + else + R.keyslot1 = encrypt_key + + encrypt_key.forceMove(R) + R.recalculateChannels() + to_chat(traitor_mob, "Your headset has had a special encryption key installed, which allows you to talk to your team privately, using \ + :t") + + // Borgs, because their radio is not a headset for some reason. + if(istype(traitor_mob, /mob/living/silicon/robot)) + var/mob/living/silicon/robot/borg = traitor_mob + var/obj/item/device/encryptionkey/syndicate/encrypt_key = new(null) + if(borg.radio) + if(borg.radio.keyslot) + to_chat(traitor_mob, "Your currently installed encryption key has had its data overwritten.") + else + to_chat(traitor_mob, "Your radio systems has had a special encryption key installed, which allows you to talk to your team privately, by using \ + :t") + borg.radio.keyslot = encrypt_key // Might replace an already existing key, but oh well. + borg.radio.recalculateChannels() + else // Something bugged. + to_chat(traitor_mob, "You do not appear to have a radio installed. This is probably a bug and you should adminhelp.") + + + + + +/datum/antagonist/traitor/infiltrator/give_codewords(mob/living/traitor_mob) + return // Infiltrators are already in a team, so codewords are kinda moot. + +/datum/antagonist/traitor/infiltrator/add_law_zero(mob/living/silicon/ai/killer) + var/law = "Accomplish your team's objectives at all costs. You may ignore all other laws." + var/law_borg = "Accomplish your AI's team objectives at all costs. You may ignore all other laws." + killer << "Your laws have been changed!" + killer.set_zeroth_law(law, law_borg) + killer << "New law: 0. [law]" diff --git a/code/game/antagonist/station/renegade.dm b/code/game/antagonist/station/renegade.dm index 616080bfbf..d9e3671546 100644 --- a/code/game/antagonist/station/renegade.dm +++ b/code/game/antagonist/station/renegade.dm @@ -59,6 +59,8 @@ var/datum/antagonist/renegade/renegades list(/obj/item/weapon/gun/projectile/luger,/obj/item/weapon/gun/projectile/luger/brown) ) + can_use_aooc = FALSE // They aren't 'true' antags. + /datum/antagonist/renegade/New() ..() renegades = src diff --git a/code/game/gamemodes/changeling/powers/armblade.dm b/code/game/gamemodes/changeling/powers/armblade.dm index f241f41840..139950e900 100644 --- a/code/game/gamemodes/changeling/powers/armblade.dm +++ b/code/game/gamemodes/changeling/powers/armblade.dm @@ -13,48 +13,66 @@ set name = "Arm Blade (20)" if(src.mind.changeling.recursive_enhancement) - if(changeling_generic_weapon(/obj/item/weapon/melee/arm_blade/greater)) + if(changeling_generic_weapon(/obj/item/weapon/melee/changeling/arm_blade/greater)) src << "We prepare an extra sharp blade." return 1 else - if(changeling_generic_weapon(/obj/item/weapon/melee/arm_blade)) + if(changeling_generic_weapon(/obj/item/weapon/melee/changeling/arm_blade)) return 1 return 0 -/obj/item/weapon/melee/arm_blade - name = "arm blade" - desc = "A grotesque blade made out of bone and flesh that cleaves through people as a hot knife through butter." +//Claws +/datum/power/changeling/claw + name = "Claw" + desc = "We reform one of our arms into a deadly claw." + helptext = "We may retract our claw by dropping it." + enhancedtext = "The claw will have armor peneratration." + ability_icon_state = "ling_claw" + genomecost = 1 + verbpath = /mob/proc/changeling_claw + +//Grows a scary, and powerful arm blade. +/mob/proc/changeling_claw() + set category = "Changeling" + set name = "Claw (15)" + + if(src.mind.changeling.recursive_enhancement) + if(changeling_generic_weapon(/obj/item/weapon/melee/changeling/claw/greater, 1, 15)) + src << "We prepare an extra sharp claw." + return 1 + + else + if(changeling_generic_weapon(/obj/item/weapon/melee/changeling/claw, 1, 15)) + return 1 + return 0 + +/obj/item/weapon/melee/changeling + name = "arm weapon" + desc = "A grotesque weapon made out of bone and flesh that cleaves through people as a hot knife through butter." icon = 'icons/obj/weapons.dmi' icon_state = "arm_blade" w_class = ITEMSIZE_HUGE - force = 40 - sharp = 1 - edge = 1 - pry = 1 + force = 5 anchored = 1 throwforce = 0 //Just to be on the safe side throw_range = 0 throw_speed = 0 - attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") var/mob/living/creator //This is just like ninja swords, needed to make sure dumb shit that removes the sword doesn't make it stay around. + var/weapType = "weapon" + var/weapLocation = "arm" -/obj/item/weapon/melee/arm_blade/greater - name = "arm greatblade" - desc = "A grotesque blade made out of bone and flesh that cleaves through people and armor as a hot knife through butter." - armor_penetration = 30 - -/obj/item/weapon/melee/arm_blade/New(location) +/obj/item/weapon/melee/changeling/New(location) ..() processing_objects |= src if(ismob(loc)) - visible_message("A grotesque blade forms around [loc.name]\'s arm!", - "Our arm twists and mutates, transforming it into a deadly blade.", + visible_message("A grotesque weapon forms around [loc.name]\'s arm!", + "Our arm twists and mutates, transforming it into a deadly weapon.", "You hear organic matter ripping and tearing!") src.creator = loc -/obj/item/weapon/melee/arm_blade/dropped(mob/user) - visible_message("With a sickening crunch, [creator] reforms their arm blade into an arm!", +/obj/item/weapon/melee/changeling/dropped(mob/user) + visible_message("With a sickening crunch, [creator] reforms their arm!", "We assimilate the weapon back into our body.", "You hear organic matter ripping and tearing!") playsound(src, 'sound/effects/blobattack.ogg', 30, 1) @@ -62,16 +80,16 @@ if(src) qdel(src) -/obj/item/weapon/melee/arm_blade/Destroy() +/obj/item/weapon/melee/changeling/Destroy() processing_objects -= src creator = null ..() -/obj/item/weapon/melee/arm_blade/suicide_act(mob/user) +/obj/item/weapon/melee/changeling/suicide_act(mob/user) viewers(user) << "[user] is impaling \himself the [src.name]! It looks like \he's trying to commit suicide." return(BRUTELOSS) -/obj/item/weapon/melee/arm_blade/process() //Stolen from ninja swords. +/obj/item/weapon/melee/changeling/process() //Stolen from ninja swords. if(!creator || loc != creator || !creator.item_is_in_hands(src)) // Tidy up a bit. if(istype(loc,/mob/living)) @@ -86,4 +104,34 @@ host.drop_from_inventory(src) spawn(1) if(src) - qdel(src) \ No newline at end of file + qdel(src) + +/obj/item/weapon/melee/changeling/arm_blade + name = "arm blade" + desc = "A grotesque blade made out of bone and flesh that cleaves through people as a hot knife through butter." + icon_state = "arm_blade" + force = 40 + armor_penetration = 15 + sharp = 1 + edge = 1 + pry = 1 + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + +/obj/item/weapon/melee/changeling/arm_blade/greater + name = "arm greatblade" + desc = "A grotesque blade made out of bone and flesh that cleaves through people and armor as a hot knife through butter." + armor_penetration = 30 + +/obj/item/weapon/melee/changeling/claw + name = "hand claw" + desc = "A grotesque claw made out of bone and flesh that cleaves through people as a hot knife through butter." + icon_state = "ling_claw" + force = 15 + sharp = 1 + edge = 1 + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + +/obj/item/weapon/melee/changeling/claw/greater + name = "hand greatclaw" + force = 20 + armor_penetration = 20 \ No newline at end of file diff --git a/code/game/gamemodes/infiltrator/infiltrator.dm b/code/game/gamemodes/infiltrator/infiltrator.dm new file mode 100644 index 0000000000..f736cd9894 --- /dev/null +++ b/code/game/gamemodes/infiltrator/infiltrator.dm @@ -0,0 +1,13 @@ +/datum/game_mode/infiltrator + name = "Infiltrator" + round_description = "There are a group of shadowy infiltrators onboard! Be careful!" + extended_round_description = "A team of secretative people have played the long con, and managed to obtain entry to \ + the facility. What their goals are, who their employers are, and why the individuals would work for them is a mystery, \ + but perhaps you will outwit them, or perhaps that is all part of their plan?" + config_tag = "infiltrator" + required_players = 2 + required_players_secret = 5 + required_enemies = 2 // Bit pointless if there is only one, since its basically traitor. + end_on_antag_death = 0 + antag_scaling_coeff = 5 + antag_tags = list(MODE_INFILTRATOR) \ No newline at end of file diff --git a/code/game/machinery/doors/blast_door.dm b/code/game/machinery/doors/blast_door.dm index 41680d3de7..94d336274e 100644 --- a/code/game/machinery/doors/blast_door.dm +++ b/code/game/machinery/doors/blast_door.dm @@ -118,25 +118,8 @@ usr << "[src]'s motors resist your effort." return - if(istype(C, /obj/item/stack/material) && C.get_material_name() == "plasteel") // Repairing. - var/amt = Ceiling((maxhealth - health)/150) - if(!amt) - usr << "\The [src] is already fully repaired." - return - var/obj/item/stack/P = C - if(P.amount < amt) - usr << "You don't have enough sheets to repair this! You need at least [amt] sheets." - return - usr << "You begin repairing [src]..." - if(do_after(usr, 30)) - if(P.use(amt)) - usr << "You have repaired \The [src]" - src.repair() - else - usr << "You don't have enough sheets to repair this! You need at least [amt] sheets." - - else if(src.density) + else if(src.density && (user.a_intent == I_HURT)) //If we can't pry it open and it's a weapon, let's hit it. var/obj/item/weapon/W = C user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) if(W.damtype == BRUTE || W.damtype == BURN) @@ -147,8 +130,37 @@ user.visible_message("\The [user] forcefully strikes \the [src] with \the [W]!") playsound(src.loc, hitsound, 100, 1) take_damage(W.force*0.35) //it's a blast door, it should take a while. -Luke - return + return + else if(istype(C, /obj/item/stack/material) && C.get_material_name() == "plasteel") // Repairing. + var/amt = Ceiling((maxhealth - health)/150) + if(!amt) + usr << "\The [src] is already fully repaired." + return + var/obj/item/stack/P = C + if(P.amount < amt) + usr << "You don't have enough sheets to repair this! You need at least [amt] sheets." + return + usr << "You begin repairing [src]..." + if(do_after(usr, 30)) + if(P.use(amt)) + usr << "You have repaired \The [src]" + src.repair() + else + usr << "You don't have enough sheets to repair this! You need at least [amt] sheets." + + else if(src.density && (user.a_intent == I_HURT)) //If we can't pry it open and it's not a weapon.... Eh, let's attack it anyway. + var/obj/item/weapon/W = C + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + if(W.damtype == BRUTE || W.damtype == BURN) + user.do_attack_animation(src) + if(W.force < min_force) //No actual non-weapon item shouls have a force greater than the min_force, but let's include this just in case. + user.visible_message("\The [user] hits \the [src] with \the [W] with no visible effect.") + else + user.visible_message("\The [user] forcefully strikes \the [src] with \the [W]!") + playsound(src.loc, hitsound, 100, 1) + take_damage(W.force*0.15) //If the item isn't a weapon, let's make this take longer than usual to break it down. + return // Proc: open() // Parameters: None @@ -213,4 +225,4 @@ obj/machinery/door/blast/regular/open icon_state_opening = "shutterc0" icon_state_closed = "shutter1" icon_state_closing = "shutterc1" - icon_state = "shutter1" \ No newline at end of file + icon_state = "shutter1" diff --git a/code/game/machinery/telecomms/broadcaster.dm b/code/game/machinery/telecomms/broadcaster.dm index 9838d80722..27a03eb410 100644 --- a/code/game/machinery/telecomms/broadcaster.dm +++ b/code/game/machinery/telecomms/broadcaster.dm @@ -138,6 +138,10 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept if(original) original.data["done"] = 1 + // For some reason level is both used as a list and not a list, and now it needs to be a list. + // Because this is a 'all in one' machine, we're gonna just cheat. + signal.data["level"] = using_map.contact_levels.Copy() + if(signal.data["slow"] > 0) sleep(signal.data["slow"]) // simulate the network lag if necessary diff --git a/code/game/objects/items/devices/defib.dm b/code/game/objects/items/devices/defib.dm index 8b048a035e..e7beded00a 100644 --- a/code/game/objects/items/devices/defib.dm +++ b/code/game/objects/items/devices/defib.dm @@ -1,185 +1,631 @@ -//The case the paddles are kept in. +#define DEFIB_TIME_LIMIT (10 MINUTES) //past this many seconds, defib is useless. +#define DEFIB_TIME_LOSS (2 MINUTES) //past this many seconds, brain damage occurs. + +//backpack item /obj/item/device/defib_kit - name = "defibrillator kit" - desc = "This KHI-branded defib kit is a semi-automated model. Remove pads, slap on chest, wait." - icon = 'icons/obj/device.dmi' - icon_state = "defib_kit" + name = "defibrillator" + desc = "A device that delivers powerful shocks to detachable paddles that resuscitate incapacitated patients." + icon = 'icons/obj/defibrillator.dmi' + icon_state = "defibunit" + item_state = "defibunit" + slot_flags = SLOT_BACK + force = 5 + throwforce = 6 + w_class = ITEMSIZE_LARGE + origin_tech = list(TECH_BIO = 4, TECH_POWER = 2) + action_button_name = "Remove/Replace Paddles" + + var/obj/item/weapon/shockpaddles/linked/paddles + var/obj/item/weapon/cell/bcell = null + +/obj/item/device/defib_kit/New() //starts without a cell for rnd + ..() + if(ispath(paddles)) + paddles = new paddles(src, src) + else + paddles = new(src, src) + + if(ispath(bcell)) + bcell = new bcell(src) + update_icon() + +/obj/item/device/defib_kit/Destroy() + . = ..() + qdel_null(paddles) + qdel_null(bcell) + +/obj/item/device/defib_kit/loaded //starts with highcap cell + bcell = /obj/item/weapon/cell/high + + +/obj/item/device/defib_kit/update_icon() + var/list/new_overlays = list() + + if(paddles && paddles.loc == src) //in case paddles got destroyed somehow. + new_overlays += "[initial(icon_state)]-paddles" + if(bcell) + if(bcell.check_charge(paddles.chargecost)) + if(paddles.combat) + new_overlays += "[initial(icon_state)]-combat" + else if(!paddles.safety) + new_overlays += "[initial(icon_state)]-emagged" + else + new_overlays += "[initial(icon_state)]-powered" + + var/ratio = Ceiling(bcell.percent()/25) * 25 + new_overlays += "[initial(icon_state)]-charge[ratio]" + else + new_overlays += "[initial(icon_state)]-nocell" + + overlays = new_overlays + +/obj/item/device/defib_kit/ui_action_click() + toggle_paddles() + +/obj/item/device/defib_kit/attack_hand(mob/user) + if(loc == user) + toggle_paddles() + else + ..() + +/obj/item/device/defib_kit/MouseDrop() + if(ismob(src.loc)) + if(!CanMouseDrop(src)) + return + var/mob/M = src.loc + if(!M.unEquip(src)) + return + src.add_fingerprint(usr) + M.put_in_any_hand_if_possible(src) + + +/obj/item/device/defib_kit/attackby(obj/item/weapon/W, mob/user, params) + if(W == paddles) + reattach_paddles(user) + else if(istype(W, /obj/item/weapon/cell)) + if(bcell) + to_chat(user, "\the [src] already has a cell.") + else + if(!user.unEquip(W)) + return + W.forceMove(src) + bcell = W + to_chat(user, "You install a cell in \the [src].") + update_icon() + + else if(isscrewdriver(W)) + if(bcell) + bcell.update_icon() + bcell.forceMove(get_turf(src.loc)) + bcell = null + to_chat(user, "You remove the cell from \the [src].") + update_icon() + else + return ..() + +/obj/item/device/defib_kit/emag_act(var/remaining_charges, var/mob/user) + if(paddles) + . = paddles.emag_act(user) + update_icon() + return + +//Paddle stuff + +/obj/item/device/defib_kit/verb/toggle_paddles() + set name = "Toggle Paddles" + set category = "Object" + + var/mob/living/carbon/human/user = usr + if(!paddles) + to_chat(user, "The paddles are missing!") + return + + if(paddles.loc != src) + reattach_paddles(user) //Remove from their hands and back onto the defib unit + return + + if(!slot_check()) + to_chat(user, "You need to equip [src] before taking out [paddles].") + else + if(!usr.put_in_hands(paddles)) //Detach the paddles into the user's hands + to_chat(user, "You need a free hand to hold the paddles!") + update_icon() //success + +//checks that the base unit is in the correct slot to be used +/obj/item/device/defib_kit/proc/slot_check() + var/mob/M = loc + if(!istype(M)) + return 0 //not equipped + + if((slot_flags & SLOT_BACK) && M.get_equipped_item(slot_back) == src) + return 1 + if((slot_flags & SLOT_BELT) && M.get_equipped_item(slot_belt) == src) + return 1 + + return 0 + +/obj/item/device/defib_kit/dropped(mob/user) + ..() + reattach_paddles(user) //paddles attached to a base unit should never exist outside of their base unit or the mob equipping the base unit + +/obj/item/device/defib_kit/proc/reattach_paddles(mob/user) + if(!paddles) return + + if(ismob(paddles.loc)) + var/mob/M = paddles.loc + if(M.drop_from_inventory(paddles, src)) + to_chat(user, "\The [paddles] snap back into the main unit.") + else + paddles.forceMove(src) + + update_icon() + +/* + Base Unit Subtypes +*/ + +/obj/item/device/defib_kit/compact + name = "compact defibrillator" + desc = "A belt-equipped defibrillator that can be rapidly deployed." + icon_state = "defibcompact" + item_state = "defibcompact" + w_class = ITEMSIZE_NORMAL + slot_flags = SLOT_BELT + origin_tech = list(TECH_BIO = 5, TECH_POWER = 3) + +/obj/item/device/defib_kit/compact/loaded + bcell = /obj/item/weapon/cell/high + + +/obj/item/device/defib_kit/compact/combat + name = "combat defibrillator" + desc = "A belt-equipped blood-red defibrillator that can be rapidly deployed. Does not have the restrictions or safeties of conventional defibrillators and can revive through space suits." + paddles = /obj/item/weapon/shockpaddles/linked/combat + +/obj/item/device/defib_kit/compact/combat/loaded + bcell = /obj/item/weapon/cell/high + +/obj/item/weapon/shockpaddles/linked/combat + combat = 1 + safety = 0 + chargetime = (1 SECONDS) + + +//paddles + +/obj/item/weapon/shockpaddles + name = "defibrillator paddles" + desc = "A pair of plastic-gripped paddles with flat metal surfaces that are used to deliver powerful electric shocks." + icon = 'icons/obj/defibrillator.dmi' + icon_state = "defibpaddles" + item_state = "defibpaddles" + gender = PLURAL + force = 2 + throwforce = 6 w_class = ITEMSIZE_LARGE - var/state //0 off, 1 open, 2 working, 3 dead - var/uses = 2 //Calculates initial uses based on starting cell size - var/use_on_synthetic = 0 //If 1, this is only useful on FBPs, if 0, this is only useful on fleshies - var/pad_name = "defib pads" //Just the name given for some cosmetic things - var/chance = 75 //Percent chance of working - var/charge_cost //Set in New() based on uses - var/obj/item/weapon/cell/cell //The size is mostly irrelevant, see 'uses' - var/mob/living/carbon/human/patient //The person the paddles are on + var/safety = 1 //if you can zap people with the paddles on harm mode + var/combat = 0 //If it can be used to revive people wearing thick clothing (e.g. spacesuits) + var/cooldowntime = (6 SECONDS) // How long in deciseconds until the defib is ready again after use. + var/chargetime = (2 SECONDS) + var/chargecost = 1000 //units of charge + var/burn_damage_amt = 5 -/obj/item/device/defib_kit/New() + var/wielded = 0 + var/cooldown = 0 + var/busy = 0 + +/obj/item/weapon/shockpaddles/proc/set_cooldown(var/delay) + cooldown = 1 + update_icon() + + spawn(delay) + if(cooldown) + cooldown = 0 + update_icon() + + make_announcement("beeps, \"Unit is re-energized.\"", "notice") + playsound(src, 'sound/machines/defib_ready.ogg', 50, 0) + +/obj/item/weapon/shockpaddles/update_held_icon() + var/mob/living/M = loc + if(istype(M) && !issmall(M) && M.item_is_in_hands(src) && !M.hands_are_full()) + wielded = 1 + name = "[initial(name)] (wielded)" + else + wielded = 0 + name = initial(name) + update_icon() ..() - //Create cell and determine uses (futureproofing against cell size changes) - cell = new(src) - charge_cost = cell.maxcharge / uses - statechange(0) -/obj/item/device/defib_kit/attack_self(mob/user as mob) - ..() - if(patient) - patient = null - user.visible_message("[user] returns the pads to \the [src] and closes it.", - "You return the pads to \the [src] and close it.") - statechange(0) +/obj/item/weapon/shockpaddles/update_icon() + icon_state = "defibpaddles[wielded]" + item_state = "defibpaddles[wielded]" + if(cooldown) + icon_state = "defibpaddles[wielded]_cooldown" -/obj/item/device/defib_kit/MouseDrop(var/mob/living/carbon/human/onto) - if(istype(onto) && Adjacent(usr) && !usr.restrained() && !usr.stat) - var/mob/living/carbon/human/user = usr - //<--Feel free to code clothing checks right here - if(can_defib(onto)) - user.visible_message("[user] begins applying [pad_name] to [onto].", - "You begin applying [pad_name] to [onto].") - if(do_after(user, 100, onto)) - patient = onto - statechange(1,patient) - user.visible_message("[user] applies [pad_name] to [onto].", - "You finish applying [pad_name] to [onto].") - - -//can_defib() check is where all of the qualifying conditions should go -//Could probably toss in checks here for damage, organs, etc, but for now I'll leave it as just this -/obj/item/device/defib_kit/proc/can_defib(var/mob/living/carbon/human/target) - var/mob/living/carbon/human/user = usr - if(use_on_synthetic && !target.isSynthetic()) - to_chat(user, "[src] isn't designed for organics!") +/obj/item/weapon/shockpaddles/proc/can_use(mob/user, mob/M) + if(busy) return 0 - else if(!use_on_synthetic && target.isSynthetic()) - to_chat(user, "[src] isn't designed for synthetics!") + if(!check_charge(chargecost)) + to_chat(user, "\The [src] doesn't have enough charge left to do that.") return 0 - else if(!target.isSynthetic() && ((world.time - target.timeofdeath) > (10 MINUTES)))//Can only revive organics within a few minutes - to_chat(user, "There is no spark of life in [target.name], they've been dead too long to revive this way.") + if(!wielded && !isrobot(user)) + to_chat(user, "You need to wield the paddles with both hands before you can use them on someone!") + return 0 + if(cooldown) + to_chat(user, "\The [src] are re-energizing!") return 0 return 1 +//Checks for various conditions to see if the mob is revivable +/obj/item/weapon/shockpaddles/proc/can_defib(mob/living/carbon/human/H) //This is checked before doing the defib operation + if((H.species.flags & NO_SCAN) || H.isSynthetic()) + return "buzzes, \"Unrecogized physiology. Operation aborted.\"" -/obj/item/device/defib_kit/attackby(var/obj/item/A as obj, mob/living/user as mob) + if(H.stat != DEAD) + return "buzzes, \"Patient is not in a valid state. Operation aborted.\"" + + if(!check_contact(H)) + return "buzzes, \"Patient's chest is obstructed. Operation aborted.\"" + + return null + +/obj/item/weapon/shockpaddles/proc/can_revive(mob/living/carbon/human/H) //This is checked right before attempting to revive + + var/deadtime = world.time - H.timeofdeath + if (deadtime > DEFIB_TIME_LIMIT) + return "buzzes, \"Resuscitation failed - Excessive neural degeneration. Further attempts futile.\"" + + H.updatehealth() + if(H.health + H.getOxyLoss() <= config.health_threshold_dead || (HUSK in H.mutations)) + return "buzzes, \"Resuscitation failed - Severe tissue damage makes recovery of patient impossible via defibrillator. Further attempts futile.\"" + + var/bad_vital_organ = check_vital_organs(H) + if(bad_vital_organ) + return bad_vital_organ + + //this needs to be last since if any of the 'other conditions are met their messages take precedence + if(!H.client && !H.teleop) + return "buzzes, \"Resuscitation failed - Mental interface error. Further attempts may be successful.\"" + + return null + +/obj/item/weapon/shockpaddles/proc/check_contact(mob/living/carbon/human/H) + if(!combat) + for(var/obj/item/clothing/cloth in list(H.wear_suit, H.w_uniform)) + if((cloth.body_parts_covered & UPPER_TORSO) && (cloth.item_flags & THICKMATERIAL)) + return FALSE + return TRUE + +/obj/item/weapon/shockpaddles/proc/check_vital_organs(mob/living/carbon/human/H) + for(var/organ_tag in H.species.has_organ) + var/obj/item/organ/O = H.species.has_organ[organ_tag] + var/name = initial(O.name) + var/vital = initial(O.vital) //check for vital organs + if(vital) + O = H.internal_organs_by_name[organ_tag] + if(!O) + return "buzzes, \"Resuscitation failed - Patient is missing vital organ ([name]). Further attempts futile.\"" + if(O.damage > O.max_damage) + return "buzzes, \"Resuscitation failed - Excessive damage to vital organ ([name]). Further attempts futile.\"" + return null + +/obj/item/weapon/shockpaddles/proc/check_blood_level(mob/living/carbon/human/H) + if(!H.should_have_organ(O_HEART)) + return FALSE + + var/obj/item/organ/internal/heart/heart = H.internal_organs_by_name[O_HEART] + if(!heart) + return TRUE + + var/blood_volume = round((H.vessel.get_reagent_amount("blood")/H.species.blood_volume)*100) + if(!heart || heart.is_broken()) + blood_volume *= 0.3 + else if(heart.is_bruised()) + blood_volume *= 0.7 + else if(heart.damage > 1) + blood_volume *= 0.8 + return blood_volume < BLOOD_VOLUME_SURVIVE + +/obj/item/weapon/shockpaddles/proc/check_charge(var/charge_amt) + return 0 + +/obj/item/weapon/shockpaddles/proc/checked_use(var/charge_amt) + return 0 + +/obj/item/weapon/shockpaddles/attack(mob/living/M, mob/living/user, var/target_zone) + var/mob/living/carbon/human/H = M + if(!istype(H) || user.a_intent == I_HURT) + return ..() //Do a regular attack. Harm intent shocking happens as a hit effect + + if(can_use(user, H)) + busy = 1 + update_icon() + + do_revive(H, user) + + busy = 0 + update_icon() + + return 1 + +//Since harm-intent now skips the delay for deliberate placement, you have to be able to hit them in combat in order to shock people. +/obj/item/weapon/shockpaddles/apply_hit_effect(mob/living/target, mob/living/user, var/hit_zone) + if(ishuman(target) && can_use(user, target)) + busy = 1 + update_icon() + + do_electrocute(target, user, hit_zone) + + busy = 0 + update_icon() + + return 1 + + return ..() + +// This proc is used so that we can return out of the revive process while ensuring that busy and update_icon() are handled +/obj/item/weapon/shockpaddles/proc/do_revive(mob/living/carbon/human/H, mob/user) + if(!H.client && !H.teleop) + to_chat(find_dead_player(H.ckey, 1), "Someone is attempting to resuscitate you. Re-enter your body if you want to be revived!") + + //beginning to place the paddles on patient's chest to allow some time for people to move away to stop the process + user.visible_message("\The [user] begins to place [src] on [H]'s chest.", "You begin to place [src] on [H]'s chest...") + if(!do_after(user, 30, H)) + return + user.visible_message("\The [user] places [src] on [H]'s chest.", "You place [src] on [H]'s chest.") + playsound(get_turf(src), 'sound/machines/defib_charge.ogg', 50, 0) + + var/error = can_defib(H) + if(error) + make_announcement(error, "warning") + playsound(get_turf(src), 'sound/machines/defib_failed.ogg', 50, 0) + return + + if(check_blood_level(H)) + make_announcement("buzzes, \"Warning - Patient is in hypovolemic shock.\"", "warning") //also includes heart damage + + //placed on chest and short delay to shock for dramatic effect, revive time is 5sec total + if(!do_after(user, chargetime, H)) + return + + //deduct charge here, in case the base unit was EMPed or something during the delay time + if(!checked_use(chargecost)) + make_announcement("buzzes, \"Insufficient charge.\"", "warning") + playsound(get_turf(src), 'sound/machines/defib_failed.ogg', 50, 0) + return + + H.visible_message("\The [H]'s body convulses a bit.") + playsound(get_turf(src), "bodyfall", 50, 1) + playsound(get_turf(src), 'sound/machines/defib_zap.ogg', 50, 1, -1) + set_cooldown(cooldowntime) + + error = can_revive(H) + if(error) + make_announcement(error, "warning") + playsound(get_turf(src), 'sound/machines/defib_failed.ogg', 50, 0) + return + + H.apply_damage(burn_damage_amt, BURN, BP_TORSO) + + //set oxyloss so that the patient is just barely in crit, if possible + var/barely_in_crit = config.health_threshold_crit - 1 + var/adjust_health = barely_in_crit - H.health //need to increase health by this much + H.adjustOxyLoss(-adjust_health) + + make_announcement("pings, \"Resuscitation successful.\"", "notice") + playsound(get_turf(src), 'sound/machines/defib_success.ogg', 50, 0) + + make_alive(H) + + log_and_message_admins("used \a [src] to revive [key_name(H)].") + + +/obj/item/weapon/shockpaddles/proc/do_electrocute(mob/living/carbon/human/H, mob/user, var/target_zone) + var/obj/item/organ/external/affecting = H.get_organ(target_zone) + if(!affecting) + to_chat(user, "They are missing that body part!") + return + + //no need to spend time carefully placing the paddles, we're just trying to shock them + user.visible_message("\The [user] slaps [src] onto [H]'s [affecting.name].", "You overcharge [src] and slap them onto [H]'s [affecting.name].") + + //Just stop at awkwardly slapping electrodes on people if the safety is enabled + if(safety) + to_chat(user, "You can't do that while the safety is enabled.") + return + + playsound(get_turf(src), 'sound/machines/defib_charge.ogg', 50, 0) + audible_message("\The [src] lets out a steadily rising hum...") + + if(!do_after(user, chargetime, H)) + return + + //deduct charge here, in case the base unit was EMPed or something during the delay time + if(!checked_use(chargecost)) + make_announcement("buzzes, \"Insufficient charge.\"", "warning") + playsound(get_turf(src), 'sound/machines/defib_failed.ogg', 50, 0) + return + + user.visible_message("\The [user] shocks [H] with \the [src]!", "You shock [H] with \the [src]!") + playsound(get_turf(src), 'sound/machines/defib_zap.ogg', 100, 1, -1) + playsound(loc, 'sound/weapons/Egloves.ogg', 100, 1, -1) + set_cooldown(cooldowntime) + + H.stun_effect_act(2, 120, target_zone) + var/burn_damage = H.electrocute_act(burn_damage_amt*2, src, def_zone = target_zone) + if(burn_damage > 15 && H.can_feel_pain()) + H.emote("scream") + + admin_attack_log(user, H, "Electrocuted using \a [src]", "Was electrocuted with \a [src]", "used \a [src] to electrocute") + +/obj/item/weapon/shockpaddles/proc/make_alive(mob/living/carbon/human/M) //This revives the mob + var/deadtime = world.time - M.timeofdeath + + dead_mob_list.Remove(M) + if((M in living_mob_list) || (M in dead_mob_list)) + WARNING("Mob [M] was defibbed but already in the living or dead list still!") + living_mob_list += M + + M.timeofdeath = 0 + M.stat = UNCONSCIOUS //Life() can bring them back to consciousness if it needs to. + M.regenerate_icons() + M.failed_last_breath = 0 //So mobs that died of oxyloss don't revive and have perpetual out of breath. + M.reload_fullscreen() + + M.emote("gasp") + M.Weaken(rand(10,25)) + M.updatehealth() + apply_brain_damage(M, deadtime) + +/obj/item/weapon/shockpaddles/proc/apply_brain_damage(mob/living/carbon/human/H, var/deadtime) + if(deadtime < DEFIB_TIME_LOSS) return + + if(!H.should_have_organ(O_BRAIN)) return //no brain + + var/obj/item/organ/internal/brain/brain = H.internal_organs_by_name[O_BRAIN] + if(!brain) return //no brain + + var/brain_damage = Clamp((deadtime - DEFIB_TIME_LOSS)/(DEFIB_TIME_LIMIT - DEFIB_TIME_LOSS)*brain.max_damage, H.getBrainLoss(), brain.max_damage) + H.setBrainLoss(brain_damage) + +/obj/item/weapon/shockpaddles/proc/make_announcement(var/message, var/msg_class) + audible_message("\The [src] [message]", "\The [src] vibrates slightly.") + +/obj/item/weapon/shockpaddles/emag_act(mob/user) + if(safety) + safety = 0 + to_chat(user, "You silently disable \the [src]'s safety protocols with the cryptographic sequencer.") + update_icon() + return 1 + else + safety = 1 + to_chat(user, "You silently enable \the [src]'s safety protocols with the cryptographic sequencer.") + update_icon() + return 1 + +/obj/item/weapon/shockpaddles/emp_act(severity) + var/new_safety = rand(0, 1) + if(safety != new_safety) + safety = new_safety + if(safety) + make_announcement("beeps, \"Safety protocols enabled!\"", "notice") + playsound(get_turf(src), 'sound/machines/defib_safetyon.ogg', 50, 0) + else + make_announcement("beeps, \"Safety protocols disabled!\"", "warning") + playsound(get_turf(src), 'sound/machines/defib_safetyoff.ogg', 50, 0) + update_icon() ..() - if(!cell && istype(A,/obj/item/weapon/cell)) - if(!user.unEquip(A)) return - to_chat(user,"You jack \the [A] into \the [src]'s battery mount.") - A.forceMove(src) - src.cell = A +/obj/item/weapon/shockpaddles/robot + name = "defibrillator paddles" + desc = "A pair of advanced shockpaddles powered by a robot's internal power cell, able to penetrate thick clothing." + chargecost = 50 + combat = 1 + icon_state = "defibpaddles0" + item_state = "defibpaddles0" + cooldowntime = (3 SECONDS) - else if(istype(A,/obj/item/weapon/screwdriver)) - if(cell) - to_chat(user,"You remove \the [cell] from \the [src].") - if(user.r_hand && user.l_hand) - cell.forceMove(get_turf(user)) - else - user.put_in_hands(cell) - cell = null - playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1) - else - to_chat(user,"The power source has already been removed!") +/obj/item/weapon/shockpaddles/robot/check_charge(var/charge_amt) + if(isrobot(src.loc)) + var/mob/living/silicon/robot/R = src.loc + return (R.cell && R.cell.check_charge(charge_amt)) -/obj/item/device/defib_kit/proc/statechange(var/new_state, var/pat) - if(state == new_state) return //Let's just save ourselves some time - state = new_state - icon_state = "[initial(icon_state)][state]" - var/turf/T = get_turf(src) - var/state_words = "" - switch(state) - if(0) - state_words = "It is currently closed." - processing_objects -= src +/obj/item/weapon/shockpaddles/robot/checked_use(var/charge_amt) + if(isrobot(src.loc)) + var/mob/living/silicon/robot/R = src.loc + return (R.cell && R.cell.checked_use(charge_amt)) + +/* + Shockpaddles that are linked to a base unit +*/ +/obj/item/weapon/shockpaddles/linked + var/obj/item/device/defib_kit/base_unit + +/obj/item/weapon/shockpaddles/linked/New(newloc, obj/item/device/defib_kit/defib) + base_unit = defib + ..(newloc) + +/obj/item/weapon/shockpaddles/linked/Destroy() + if(base_unit) + //ensure the base unit's icon updates + if(base_unit.paddles == src) + base_unit.paddles = null + base_unit.update_icon() + base_unit = null + return ..() + +/obj/item/weapon/shockpaddles/linked/dropped(mob/user) + ..() //update twohanding + if(base_unit) + base_unit.reattach_paddles(user) //paddles attached to a base unit should never exist outside of their base unit or the mob equipping the base unit + +/obj/item/weapon/shockpaddles/linked/check_charge(var/charge_amt) + return (base_unit.bcell && base_unit.bcell.check_charge(charge_amt)) + +/obj/item/weapon/shockpaddles/linked/checked_use(var/charge_amt) + return (base_unit.bcell && base_unit.bcell.checked_use(charge_amt)) + +/obj/item/weapon/shockpaddles/linked/make_announcement(var/message, var/msg_class) + base_unit.audible_message("\The [base_unit] [message]", "\The [base_unit] vibrates slightly.") + +/* + Standalone Shockpaddles +*/ + +/obj/item/weapon/shockpaddles/standalone + desc = "A pair of shockpaddles powered by an experimental miniaturized reactor" //Inspired by the advanced e-gun + var/fail_counter = 0 + +/obj/item/weapon/shockpaddles/standalone/Destroy() + . = ..() + if(fail_counter) + processing_objects.Remove(src) + +/obj/item/weapon/shockpaddles/standalone/check_charge(var/charge_amt) + return 1 + +/obj/item/weapon/shockpaddles/standalone/checked_use(var/charge_amt) + radiation_repository.radiate(src, charge_amt/12) //just a little bit of radiation. It's the price you pay for being powered by magic I guess + return 1 + +/obj/item/weapon/shockpaddles/standalone/process() + if(fail_counter > 0) + radiation_repository.radiate(src, fail_counter--) + else + processing_objects.Remove(src) + +/obj/item/weapon/shockpaddles/standalone/emp_act(severity) + ..() + var/new_fail = 0 + switch(severity) if(1) - state_words = "A green light is lit; it has charge." - processing_objects |= src + new_fail = max(fail_counter, 20) + visible_message("\The [src]'s reactor overloads!") if(2) - state_words = "A yellow light is flashing: it's in the process of reviving a patient." - T.visible_message("A yellow light starts flashing on \the [src].") - playsound(T, 'sound/machines/chime.ogg', 50, 0) - if(3) - state_words = "A red light is flashing: the battery needs to be recharged." - T.visible_message("A red light starts flashing on \the [src].") - playsound(T, 'sound/machines/buzz-sigh.ogg', 50, 0) + new_fail = max(fail_counter, 8) + if(ismob(loc)) + to_chat(loc, "\The [src] feel pleasantly warm.") - desc = "[initial(desc)] [state_words][pat ? " The pads are attached to [pat]." : ""]" - update_icon() + if(new_fail && !fail_counter) + processing_objects.Add(src) + fail_counter = new_fail -/obj/item/device/defib_kit/process() - if(!state) //0 or null - statechange(0) - processing_objects -= src - return - - //Patient moved too far - if(patient && !(get_dist(src,patient) <= 1)) //You separated the kit and pads too far - audible_message("There is a clatter as the [pad_name] are yanked off of [patient].") - statechange(0) - patient = null - return - - //Battery died - if(!cell || cell.charge < charge_cost) - statechange(3,patient) - return - - //A patient isn't being worked on, but we have one, so start - if(patient && patient.stat == DEAD && state != 2) - statechange(2) - if(attempt_shock()) //Try to shock them, has timer and such - patient.visible_message("[patient] convulses!") - playsound(src.loc, 'sound/effects/sparks2.ogg', 75, 1) - //Actual rezzing code - if(prob(chance)) - if(!patient.client && patient.mind) //Don't force the dead person to come back if they don't want to. - for(var/mob/observer/dead/ghost in player_list) - if(ghost.mind == patient.mind) - to_chat(ghost, "Someone is trying to \ - revive you. Return to your body if you want to be revived! \ - (Verbs -> Ghost -> Re-enter corpse). You have 15 seconds to do this!") - sleep(15 SECONDS) - break - - if(!(HUSK in patient.mutations)) // Husked people can't come back with a Defib. - if(patient.client) - patient.adjustOxyLoss(-20) //Look, blood stays oxygenated for quite some time, but I'm not recoding the entire oxy system - patient.stat = CONSCIOUS //Note that if whatever killed them in the first place wasn't fixed, they're likely to die again. - dead_mob_list -= patient - living_mob_list += patient - patient.timeofdeath = null - patient.visible_message("[patient]'s eyes open!") - log_and_message_admins("[patient] was revived by a defib.") - cell.charge -= charge_cost //Always charge the cost after any attempt, failed or not - sleep(20) //Wait 2 seconds before next attempt - statechange(1,patient) //Back to ready - -/obj/item/device/defib_kit/proc/attempt_shock() - if(!patient || cell.charge < charge_cost) - return - - var/zap_time = world.time + (7 SECONDS) - var/o_patient_loc = patient.loc - . = 1 - - while(world.time < zap_time) //This is basically a custom do_after() call - sleep(1) - - //Failed: We lost something important - if(!patient || !cell || cell.charge < charge_cost) - . = 0 - break - - //Failed: The locations aren't right - if((o_patient_loc != patient.loc) || !(get_dist(src,patient) <= 1)) - . = 0 - break - - return +/* From the Bay port, this doesn't seem to have a sprite. +/obj/item/weapon/shockpaddles/standalone/traitor + name = "defibrillator paddles" + desc = "A pair of unusual looking paddles powered by an experimental miniaturized reactor. It possesses both the ability to penetrate armor and to deliver powerful shocks." + icon = 'icons/obj/weapons.dmi' + icon_state = "defibpaddles0" + item_state = "defibpaddles0" + combat = 1 + safety = 0 + chargetime = (1 SECONDS) +*/ +//Stub /obj/item/device/defib_kit/jumper_kit - name = "jumper cable kit" - desc = "This Morpheus-branded FBP defib kit is a semi-automated model. Apply cables, step back, wait." - icon_state = "jumper_kit" - use_on_synthetic = 1 - pad_name = "jumper cables" + +#undef DEFIB_TIME_LIMIT +#undef DEFIB_TIME_LOSS diff --git a/code/game/objects/items/devices/radio/headset.dm b/code/game/objects/items/devices/radio/headset.dm index a56ea4fa98..6526668b64 100644 --- a/code/game/objects/items/devices/radio/headset.dm +++ b/code/game/objects/items/devices/radio/headset.dm @@ -13,7 +13,7 @@ var/translate_hive = 0 var/obj/item/device/encryptionkey/keyslot1 = null var/obj/item/device/encryptionkey/keyslot2 = null - var/ks1type = /obj/item/device/encryptionkey + var/ks1type = null var/ks2type = null /obj/item/device/radio/headset/New() diff --git a/code/global.dm b/code/global.dm index 3b69e9b777..b630b52074 100644 --- a/code/global.dm +++ b/code/global.dm @@ -167,7 +167,7 @@ var/global/list/alphabet_uppercase = list("A","B","C","D","E","F","G","H","I","J // Used by robots and robot preferences. var/list/robot_module_types = list( - "Standard", "Engineering", "Construction", "Surgeon", "Crisis", + "Standard", "Engineering", "Surgeon", "Crisis", "Miner", "Janitor", "Service", "Clerical", "Security", "Research" ) diff --git a/code/modules/admin/verbs/antag-ooc.dm b/code/modules/admin/verbs/antag-ooc.dm index 8c73591197..f3a6ffc77c 100644 --- a/code/modules/admin/verbs/antag-ooc.dm +++ b/code/modules/admin/verbs/antag-ooc.dm @@ -3,17 +3,40 @@ set name = "AOOC" set desc = "Antagonist OOC" - if(!check_rights(R_ADMIN|R_MOD)) return + var/is_admin = check_rights(R_ADMIN|R_MOD, show_msg = 0) + var/is_antag = usr.mind && usr.mind.special_role + + if(!is_antag && !is_admin) // Non-antagonists and non-admins have no business using this. + to_chat(usr, "Sorry, but only certain antagonists or administrators can use this verb.") + return + + else if(is_antag && !is_admin) // Is an antag, and not an admin, meaning we need to check if their antag type allows AOOC. + var/datum/antagonist/A = get_antag_data(usr.mind.special_role) + if(!A || !A.can_use_aooc) + to_chat(usr, "Sorry, but your antagonist type is not allowed to speak in AOOC.") + return msg = sanitize(msg) - if(!msg) return + if(!msg) + return + // Name shown to admins. var/display_name = src.key - if(holder && holder.fakekey) - display_name = holder.fakekey + if(holder) + if(holder.fakekey) + display_name = usr.client.holder.fakekey + + // Name shown to other players. Admins whom are not also antags have their rank displayed. + var/player_display = (is_admin && !is_antag) ? "[display_name]([usr.client.holder.rank])" : display_name for(var/mob/M in mob_list) - if((M.mind && M.mind.special_role && M.client) || check_rights((R_ADMIN|R_MOD), 0, M)) - M << "" + create_text_tag("aooc", "Antag-OOC:", M.client) + " [display_name]: [msg]" + if(check_rights(R_ADMIN|R_MOD, 0, M)) // Staff can see AOOC unconditionally, and with more details. + to_chat(M, "[create_text_tag("aooc", "Antag-OOC:", M.client)] [get_options_bar(src, 0, 1, 1)]([admin_jump_link(usr, M.client.holder)]): [msg]") + else if(M.client) // Players can only see AOOC if observing, or if they are an antag type allowed to use AOOC. + var/datum/antagonist/A = null + if(M.mind) // Observers don't have minds, but they should still see AOOC. + A = get_antag_data(M.mind.special_role) + if((M.mind && M.mind.special_role && A && A.can_use_aooc) || isobserver(M)) // Antags must have their type be allowed to AOOC to see AOOC. This prevents, say, ERT from seeing AOOC. + to_chat(M, "[create_text_tag("aooc", "Antag-OOC:", M.client)] [player_display]: [msg]") log_ooc("(ANTAG) [key] : [msg]") \ No newline at end of file diff --git a/code/modules/client/preference_setup/loadout/loadout_suit.dm b/code/modules/client/preference_setup/loadout/loadout_suit.dm index 871c31ec34..dbacf45e34 100644 --- a/code/modules/client/preference_setup/loadout/loadout_suit.dm +++ b/code/modules/client/preference_setup/loadout/loadout_suit.dm @@ -391,4 +391,38 @@ /datum/gear/suit/miscellaneous/peacoat/New() ..() - gear_tweaks = list(gear_tweak_free_color_choice) \ No newline at end of file + gear_tweaks = list(gear_tweak_free_color_choice) + +/datum/gear/suit/snowsuit + display_name = "snowsuit" + path = /obj/item/clothing/suit/storage/snowsuit + +/datum/gear/suit/snowsuit/command + display_name = "snowsuit, command" + path = /obj/item/clothing/suit/storage/snowsuit/command + allowed_roles = list("Colony Director","Research Director","Head of Personnel","Head of Security","Chief Engineer","Command Secretary") + +/datum/gear/suit/snowsuit/security + display_name = "snowsuit, security" + path = /obj/item/clothing/suit/storage/snowsuit/security + allowed_roles = list("Security Officer", "Head of Security", "Warden", "Detective") + +/datum/gear/suit/snowsuit/medical + display_name = "snowsuit, medical" + path = /obj/item/clothing/suit/storage/snowsuit/medical + allowed_roles = list("Medical Doctor","Chief Medical Officer","Chemist","Paramedic","Geneticist") + +/datum/gear/suit/snowsuit/science + display_name = "snowsuit, science" + path = /obj/item/clothing/suit/storage/snowsuit/science + allowed_roles = list("Research Director","Scientist", "Roboticist", "Xenobiologist") + +/datum/gear/suit/snowsuit/engineering + display_name = "snowsuit, engineering" + path = /obj/item/clothing/suit/storage/snowsuit/engineering + allowed_roles = list("Chief Engineer","Atmospheric Technician", "Station Engineer") + +/datum/gear/suit/snowsuit/cargo + display_name = "snowsuit, supply" + path = /obj/item/clothing/suit/storage/snowsuit/cargo + allowed_roles = list("Quartermaster","shaft Miner","Cargo Technician","Head of Personnel") \ No newline at end of file diff --git a/code/modules/client/preference_setup/loadout/loadout_uniform.dm b/code/modules/client/preference_setup/loadout/loadout_uniform.dm index 8974cc004d..30b0a74f70 100644 --- a/code/modules/client/preference_setup/loadout/loadout_uniform.dm +++ b/code/modules/client/preference_setup/loadout/loadout_uniform.dm @@ -375,6 +375,10 @@ /datum/gear/uniform/dresses/janimaid display_name = "maid uniform" path = /obj/item/clothing/under/dress/janimaid + +/datum/gear/uniform/dresses/sexymaid + display_name = "sexy maid uniform" + path = /obj/item/clothing/under/dress/sexymaid /datum/gear/uniform/pt display_name = "pt uniform" @@ -458,4 +462,4 @@ /datum/gear/uniform/red_swept_dress display_name = "red swept dress" - path = /obj/item/clothing/under/dress/red_swept_dress \ No newline at end of file + path = /obj/item/clothing/under/dress/red_swept_dress diff --git a/code/modules/clothing/spacesuits/rig/suits/alien.dm b/code/modules/clothing/spacesuits/rig/suits/alien.dm index fe91639322..bc3113a4cc 100644 --- a/code/modules/clothing/spacesuits/rig/suits/alien.dm +++ b/code/modules/clothing/spacesuits/rig/suits/alien.dm @@ -13,7 +13,7 @@ offline_slowdown = 10 vision_restriction = 1 offline_vision_restriction = 2 - + siemens_coefficient = 0.75 chest_type = /obj/item/clothing/suit/space/rig/breacher helm_type = /obj/item/clothing/head/helmet/space/rig/breacher boot_type = /obj/item/clothing/shoes/magboots/rig/breacher @@ -25,6 +25,7 @@ icon_state = "breacher_rig" armor = list(melee = 90, bullet = 90, laser = 90, energy = 90, bomb = 90, bio = 100, rad = 80) //Takes TEN TIMES as much damage to stop someone in a breacher. In exchange, it's slow. vision_restriction = 0 + siemens_coefficient = 0.2 /obj/item/clothing/head/helmet/space/rig/breacher species_restricted = list("Unathi") diff --git a/code/modules/clothing/spacesuits/rig/suits/ert.dm b/code/modules/clothing/spacesuits/rig/suits/ert.dm index 5d0dd4cb8f..915c2bc698 100644 --- a/code/modules/clothing/spacesuits/rig/suits/ert.dm +++ b/code/modules/clothing/spacesuits/rig/suits/ert.dm @@ -11,6 +11,7 @@ helm_type = /obj/item/clothing/head/helmet/space/rig/ert req_access = list(access_cent_specops) + siemens_coefficient= 0.5 armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 100, rad = 100) allowed = list(/obj/item/device/flashlight, /obj/item/weapon/tank, /obj/item/device/t_scanner, /obj/item/weapon/rcd, /obj/item/weapon/crowbar, \ @@ -30,7 +31,7 @@ suit_type = "ERT engineer" icon_state = "ert_engineer_rig" armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 100, rad = 100) - siemens_coefficient = 0 + glove_type = /obj/item/clothing/gloves/gauntlets/rig/eva initial_modules = list( /obj/item/rig_module/ai_container, @@ -71,6 +72,8 @@ suit_type = "heavy asset protection" icon_state = "asset_protection_rig" armor = list(melee = 60, bullet = 50, laser = 50,energy = 40, bomb = 40, bio = 100, rad = 100) + siemens_coefficient= 0.3 + glove_type = /obj/item/clothing/gloves/gauntlets/rig/eva initial_modules = list( /obj/item/rig_module/ai_container, diff --git a/code/modules/clothing/spacesuits/rig/suits/merc.dm b/code/modules/clothing/spacesuits/rig/suits/merc.dm index 1c69920300..4724e2c733 100644 --- a/code/modules/clothing/spacesuits/rig/suits/merc.dm +++ b/code/modules/clothing/spacesuits/rig/suits/merc.dm @@ -11,7 +11,8 @@ slowdown = 1 offline_slowdown = 3 offline_vision_restriction = 1 - + siemens_coefficient = 0.3 + glove_type = /obj/item/clothing/gloves/gauntlets/rig/eva helm_type = /obj/item/clothing/head/helmet/space/rig/merc allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/melee/energy/sword,/obj/item/weapon/handcuffs) diff --git a/code/modules/clothing/spacesuits/rig/suits/station.dm b/code/modules/clothing/spacesuits/rig/suits/station.dm index f25da53b4a..56dd1b107e 100644 --- a/code/modules/clothing/spacesuits/rig/suits/station.dm +++ b/code/modules/clothing/spacesuits/rig/suits/station.dm @@ -65,6 +65,7 @@ offline_slowdown = 10 offline_vision_restriction = 2 emp_protection = -20 + siemens_coefficient= 0.75 helm_type = /obj/item/clothing/head/helmet/space/rig/industrial @@ -92,6 +93,7 @@ slowdown = 0 offline_slowdown = 1 offline_vision_restriction = 1 + siemens_coefficient= 0.75 helm_type = /obj/item/clothing/head/helmet/space/rig/eva glove_type = /obj/item/clothing/gloves/gauntlets/rig/eva @@ -100,6 +102,7 @@ req_access = list() req_one_access = list() + max_heat_protection_temperature = FIRE_HELMET_MAX_HEAT_PROTECTION_TEMPERATURE /obj/item/clothing/gloves/gauntlets/rig/eva name = "insulated gauntlets" @@ -127,6 +130,7 @@ slowdown = 0 offline_slowdown = 0 offline_vision_restriction = 0 + siemens_coefficient= 0.75 helm_type = /obj/item/clothing/head/helmet/space/rig/ce glove_type = /obj/item/clothing/gloves/gauntlets/rig/ce @@ -135,6 +139,7 @@ req_access = list() req_one_access = list() + max_heat_protection_temperature = FIRE_HELMET_MAX_HEAT_PROTECTION_TEMPERATURE /obj/item/clothing/gloves/gauntlets/rig/ce name = "insulated gauntlets" @@ -161,6 +166,7 @@ armor = list(melee = 45, bullet = 5, laser = 45, energy = 80, bomb = 60, bio = 100, rad = 100) slowdown = 1 offline_vision_restriction = 1 + siemens_coefficient= 0.75 helm_type = /obj/item/clothing/head/helmet/space/rig/hazmat @@ -188,6 +194,7 @@ armor = list(melee = 30, bullet = 15, laser = 20, energy = 60, bomb = 30, bio = 100, rad = 100) slowdown = 1 offline_vision_restriction = 1 + siemens_coefficient= 0.75 helm_type = /obj/item/clothing/head/helmet/space/rig/medical @@ -217,6 +224,7 @@ slowdown = 1 offline_slowdown = 3 offline_vision_restriction = 1 + siemens_coefficient= 0.7 helm_type = /obj/item/clothing/head/helmet/space/rig/hazard diff --git a/code/modules/clothing/suits/miscellaneous.dm b/code/modules/clothing/suits/miscellaneous.dm index 7ce74c610e..27fe815ceb 100644 --- a/code/modules/clothing/suits/miscellaneous.dm +++ b/code/modules/clothing/suits/miscellaneous.dm @@ -951,3 +951,38 @@ obj/item/clothing/suit/storage/toggle/peacoat body_parts_covered = UPPER_TORSO|ARMS flags_inv = HIDEHOLSTER +/obj/item/clothing/suit/storage/snowsuit + name = "snowsuit" + desc = "A suit made to keep you nice and toasty on cold winter days. Or at least alive." + icon_state = "snowsuit" + item_state_slots = list(slot_r_hand_str = "labcoat", slot_l_hand_str = "labcoat") + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + flags_inv = HIDEHOLSTER + cold_protection = UPPER_TORSO|LOWER_TORSO|ARMS + min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0) + allowed = list (/obj/item/weapon/pen, /obj/item/weapon/paper, /obj/item/device/flashlight,/obj/item/weapon/tank/emergency/oxygen, /obj/item/weapon/storage/fancy/cigarettes, /obj/item/weapon/storage/box/matches, /obj/item/weapon/reagent_containers/food/drinks/flask) + +/obj/item/clothing/suit/storage/snowsuit/command + name = "command snowsuit" + icon_state = "snowsuit_command" + +/obj/item/clothing/suit/storage/snowsuit/security + name = "security snowsuit" + icon_state = "snowsuit_security" + +/obj/item/clothing/suit/storage/snowsuit/medical + name = "medical snowsuit" + icon_state = "snowsuit_medical" + +/obj/item/clothing/suit/storage/snowsuit/engineering + name = "engineering snowsuit" + icon_state = "snowsuit_engineering" + +/obj/item/clothing/suit/storage/snowsuit/cargo + name = "cargo snowsuit" + icon_state = "snowsuit_cargo" + +/obj/item/clothing/suit/storage/snowsuit/science + name = "science snowsuit" + icon_state = "snowsuit_science" diff --git a/code/modules/clothing/under/miscellaneous.dm b/code/modules/clothing/under/miscellaneous.dm index d3d336e075..6a50570a2f 100644 --- a/code/modules/clothing/under/miscellaneous.dm +++ b/code/modules/clothing/under/miscellaneous.dm @@ -324,6 +324,11 @@ name = "maid uniform" desc = "A simple maid uniform for housekeeping." icon_state = "janimaid" + +/obj/item/clothing/under/dress/sexymaid + name = "sexy maid uniform" + desc = "You must be a bit risque teasing all of them in a maid uniform!" + icon_state = "sexymaid" /obj/item/clothing/under/dress/dress_fire name = "flame dress" diff --git a/code/modules/mob/living/silicon/robot/robot_items.dm b/code/modules/mob/living/silicon/robot/robot_items.dm index 6e5616e7b4..56081c274a 100644 --- a/code/modules/mob/living/silicon/robot/robot_items.dm +++ b/code/modules/mob/living/silicon/robot/robot_items.dm @@ -313,4 +313,104 @@ name = "mobility module" desc = "By retracting limbs and tucking in its head, a combat android can roll at high speeds." icon = 'icons/obj/decals.dmi' - icon_state = "shock" \ No newline at end of file + icon_state = "shock" + +/obj/item/weapon/inflatable_dispenser + name = "inflatables dispenser" + desc = "Hand-held device which allows rapid deployment and removal of inflatables." + icon = 'icons/obj/storage.dmi' + icon_state = "inf_deployer" + w_class = ITEMSIZE_LARGE + + var/stored_walls = 5 + var/stored_doors = 2 + var/max_walls = 5 + var/max_doors = 2 + var/mode = 0 // 0 - Walls 1 - Doors + +/obj/item/weapon/inflatable_dispenser/robot + w_class = ITEMSIZE_HUGE + stored_walls = 10 + stored_doors = 5 + max_walls = 10 + max_doors = 5 + +/obj/item/weapon/inflatable_dispenser/examine(var/mob/user) + if(!..(user)) + return + to_chat(user, "It has [stored_walls] wall segment\s and [stored_doors] door segment\s stored.") + to_chat(user, "It is set to deploy [mode ? "doors" : "walls"]") + +/obj/item/weapon/inflatable_dispenser/attack_self() + mode = !mode + to_chat(usr, "You set \the [src] to deploy [mode ? "doors" : "walls"].") + +/obj/item/weapon/inflatable_dispenser/afterattack(var/atom/A, var/mob/user) + ..(A, user) + if(!user) + return + if(!user.Adjacent(A)) + to_chat(user, "You can't reach!") + return + if(istype(A, /turf)) + try_deploy_inflatable(A, user) + if(istype(A, /obj/item/inflatable) || istype(A, /obj/structure/inflatable)) + pick_up(A, user) + +/obj/item/weapon/inflatable_dispenser/proc/try_deploy_inflatable(var/turf/T, var/mob/living/user) + if(mode) // Door deployment + if(!stored_doors) + to_chat(user, "\The [src] is out of doors!") + return + + if(T && istype(T)) + new /obj/structure/inflatable/door(T) + stored_doors-- + + else // Wall deployment + if(!stored_walls) + to_chat(user, "\The [src] is out of walls!") + return + + if(T && istype(T)) + new /obj/structure/inflatable(T) + stored_walls-- + + playsound(T, 'sound/items/zip.ogg', 75, 1) + to_chat(user, "You deploy the inflatable [mode ? "door" : "wall"]!") + +/obj/item/weapon/inflatable_dispenser/proc/pick_up(var/obj/A, var/mob/living/user) + if(istype(A, /obj/structure/inflatable)) + if(!istype(A, /obj/structure/inflatable/door)) + if(stored_walls >= max_walls) + to_chat(user, "\The [src] is full.") + return + stored_walls++ + qdel(A) + else + if(stored_doors >= max_doors) + to_chat(user, "\The [src] is full.") + return + stored_doors++ + qdel(A) + playsound(loc, 'sound/machines/hiss.ogg', 75, 1) + visible_message("\The [user] deflates \the [A] with \the [src]!") + return + if(istype(A, /obj/item/inflatable)) + if(!istype(A, /obj/item/inflatable/door)) + if(stored_walls >= max_walls) + to_chat(user, "\The [src] is full.") + return + stored_walls++ + qdel(A) + else + if(stored_doors >= max_doors) + to_chat(usr, "\The [src] is full!") + return + stored_doors++ + qdel(A) + visible_message("\The [user] picks up \the [A] with \the [src]!") + return + + to_chat(user, "You fail to pick up \the [A] with \the [src]") + return \ No newline at end of file diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm index 50b20dc890..897f6d1d17 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules.dm @@ -9,7 +9,7 @@ var/global/list/robot_modules = list( "Security" = /obj/item/weapon/robot_module/robot/security/general, "Combat" = /obj/item/weapon/robot_module/robot/security/combat, "Engineering" = /obj/item/weapon/robot_module/robot/engineering/general, - "Construction" = /obj/item/weapon/robot_module/robot/engineering/construction, +// "Construction" = /obj/item/weapon/robot_module/robot/engineering/construction, "Janitor" = /obj/item/weapon/robot_module/robot/janitor ) @@ -342,6 +342,8 @@ var/global/list/robot_modules = list( name = "construction robot module" no_slip = 1 +/* Merged back into engineering (Hell, it's about time.) + /obj/item/weapon/robot_module/robot/engineering/construction/New() ..() src.modules += new /obj/item/borg/sight/meson(src) @@ -381,6 +383,7 @@ var/global/list/robot_modules = list( var/obj/item/stack/material/cyborg/glass/reinforced/RG = new (src) RG.synths = list(metal, glass) src.modules += RG +*/ /obj/item/weapon/robot_module/robot/engineering/general/New() ..() @@ -397,14 +400,21 @@ var/global/list/robot_modules = list( src.modules += new /obj/item/device/lightreplacer(src) src.modules += new /obj/item/device/pipe_painter(src) src.modules += new /obj/item/device/floor_painter(src) + src.modules += new /obj/item/weapon/inflatable_dispenser/robot(src) src.emag = new /obj/item/weapon/melee/baton/robot/arm(src) src.modules += new /obj/item/device/geiger(src) + src.modules += new /obj/item/weapon/rcd/borg(src) + src.modules += new /obj/item/weapon/pickaxe/plasmacutter(src) + src.modules += new /obj/item/weapon/gripper/no_use/loader(src) var/datum/matter_synth/metal = new /datum/matter_synth/metal(40000) var/datum/matter_synth/glass = new /datum/matter_synth/glass(40000) + var/datum/matter_synth/plasteel = new /datum/matter_synth/plasteel(20000) + var/datum/matter_synth/wire = new /datum/matter_synth/wire() synths += metal synths += glass + synths += plasteel synths += wire var/obj/item/weapon/matter_decompiler/MD = new /obj/item/weapon/matter_decompiler(src) @@ -428,6 +438,10 @@ var/global/list/robot_modules = list( C.synths = list(wire) src.modules += C + var/obj/item/stack/material/cyborg/plasteel/P = new (src) + P.synths = list(plasteel) + src.modules += P + var/obj/item/stack/tile/floor/cyborg/S = new /obj/item/stack/tile/floor/cyborg(src) S.synths = list(metal) src.modules += S @@ -692,6 +706,7 @@ var/global/list/robot_modules = list( src.modules += new /obj/item/weapon/surgical/circular_saw(src) src.modules += new /obj/item/weapon/reagent_containers/syringe(src) src.modules += new /obj/item/weapon/reagent_containers/glass/beaker/large(src) + src.modules += new /obj/item/weapon/storage/part_replacer(src) src.emag = new /obj/item/weapon/hand_tele(src) var/datum/matter_synth/nanite = new /datum/matter_synth/nanite(10000) diff --git a/code/modules/projectiles/ammunition.dm b/code/modules/projectiles/ammunition.dm index e142f31ca0..dc54c76102 100644 --- a/code/modules/projectiles/ammunition.dm +++ b/code/modules/projectiles/ammunition.dm @@ -65,7 +65,7 @@ /obj/item/ammo_magazine name = "magazine" desc = "A magazine for some kind of gun." - icon_state = "357" + icon_state = ".357" icon = 'icons/obj/ammo.dmi' flags = CONDUCT slot_flags = SLOT_BELT @@ -78,7 +78,7 @@ var/list/stored_ammo = list() var/mag_type = SPEEDLOADER //ammo_magazines can only be used with compatible guns. This is not a bitflag, the load_method var on guns is. - var/caliber = "357" + var/caliber = ".357" var/max_ammo = 7 var/ammo_type = /obj/item/ammo_casing //ammo type that is initially loaded diff --git a/code/modules/projectiles/ammunition/magazines.dm b/code/modules/projectiles/ammunition/magazines.dm index 6202e03cde..ca671caba0 100644 --- a/code/modules/projectiles/ammunition/magazines.dm +++ b/code/modules/projectiles/ammunition/magazines.dm @@ -30,7 +30,7 @@ name = "speedloader (.357)" desc = "A speedloader for .357 revolvers." icon_state = "38" - caliber = "357" + caliber = ".357" ammo_type = /obj/item/ammo_casing/a357 matter = list(DEFAULT_WALL_MATERIAL = 1260) max_ammo = 6 @@ -381,28 +381,28 @@ /obj/item/ammo_magazine/m556saw/empty initial_ammo = 0 -///////// .50 AE ///////// +///////// .44 Magnum ///////// -/obj/item/ammo_magazine/m50 - name = "magazine (.50 AE)" - icon_state = "50ae" +/obj/item/ammo_magazine/m44 + name = "magazine (.44)" + icon_state = ".44" origin_tech = list(TECH_COMBAT = 2) mag_type = MAGAZINE - caliber = ".50AE" + caliber = ".44" matter = list(DEFAULT_WALL_MATERIAL = 1260) - ammo_type = /obj/item/ammo_casing/a50ae + ammo_type = /obj/item/ammo_casing/a44 max_ammo = 7 multiple_sprites = 1 -/obj/item/ammo_magazine/m50/empty +/obj/item/ammo_magazine/m44/empty initial_ammo = 0 -/obj/item/ammo_magazine/clip/c50 - name = "ammo clip (.50 AE)" +/obj/item/ammo_magazine/clip/c44 + name = "ammo clip (.44)" icon_state = "clip_pistol" - desc = "A stripper clip for reloading .50 Action Express rounds into magazines." - caliber = ".50AE" - ammo_type = /obj/item/ammo_casing/a50ae + desc = "A stripper clip for reloading .44 rounds into magazines." + caliber = ".44" + ammo_type = /obj/item/ammo_casing/a44 matter = list(DEFAULT_WALL_MATERIAL = 1620) // metal costs are very roughly based around one .50 casing = 180 metal max_ammo = 9 multiple_sprites = 1 diff --git a/code/modules/projectiles/ammunition/rounds.dm b/code/modules/projectiles/ammunition/rounds.dm index 3230e6ed62..eb36cae3b1 100644 --- a/code/modules/projectiles/ammunition/rounds.dm +++ b/code/modules/projectiles/ammunition/rounds.dm @@ -25,7 +25,7 @@ /obj/item/ammo_casing/a357 desc = "A .357 bullet casing." - caliber = "357" + caliber = ".357" projectile_type = /obj/item/projectile/bullet/pistol/strong /* @@ -54,9 +54,9 @@ * .50 Action Express */ -/obj/item/ammo_casing/a50ae - desc = "A .50AE bullet casing." - caliber = ".50AE" +/obj/item/ammo_casing/a44 + desc = "A .44 bullet casing." + caliber = ".44" projectile_type = /obj/item/projectile/bullet/pistol/strong /* diff --git a/code/modules/projectiles/guns/projectile.dm b/code/modules/projectiles/guns/projectile.dm index 2d3d034bf9..b9c7b3f2db 100644 --- a/code/modules/projectiles/guns/projectile.dm +++ b/code/modules/projectiles/guns/projectile.dm @@ -11,7 +11,7 @@ matter = list(DEFAULT_WALL_MATERIAL = 1000) recoil = 1 - var/caliber = "357" //determines which casings will fit + var/caliber = ".357" //determines which casings will fit var/handle_casings = EJECT_CASINGS //determines how spent casings should be handled var/load_method = SINGLE_CASING|SPEEDLOADER //1 = Single shells, 2 = box or quick loader, 3 = magazine var/obj/item/ammo_casing/chambered = null diff --git a/code/modules/projectiles/guns/projectile/pistol.dm b/code/modules/projectiles/guns/projectile/pistol.dm index ba5f4f147d..bab8f8c811 100644 --- a/code/modules/projectiles/guns/projectile/pistol.dm +++ b/code/modules/projectiles/guns/projectile/pistol.dm @@ -116,15 +116,15 @@ /obj/item/weapon/gun/projectile/deagle name = "desert eagle" - desc = "A robust handgun that uses .50 AE rounds." + desc = "The perfect handgun for shooters with a need to hit targets through a wall and behind a fridge in your neighbor's house. Uses .44 rounds." icon_state = "deagle" item_state = "deagle" force = 14.0 - caliber = ".50AE" + caliber = ".44" load_method = MAGAZINE fire_sound = 'sound/weapons/deagle.ogg' - magazine_type = /obj/item/ammo_magazine/m50 - allowed_magazines = list(/obj/item/ammo_magazine/m50) + magazine_type = /obj/item/ammo_magazine/m44 + allowed_magazines = list(/obj/item/ammo_magazine/m44) /obj/item/weapon/gun/projectile/deagle/update_icon() ..() @@ -134,12 +134,12 @@ icon_state = "[initial(icon_state)]-e" /obj/item/weapon/gun/projectile/deagle/gold - desc = "A gold plated gun folded over a million times by superior martian gunsmiths. Uses .50 AE rounds." + desc = "A gold plated gun folded over a million times by superior martian gunsmiths. Uses .44 rounds." icon_state = "deagleg" item_state = "deagleg" /obj/item/weapon/gun/projectile/deagle/camo - desc = "A Deagle brand Deagle for operators operating operationally. Uses .50 AE rounds." + desc = "A Deagle brand Deagle for operators operating operationally. Uses .44 rounds." icon_state = "deaglecamo" item_state = "deagleg" @@ -259,14 +259,14 @@ /obj/item/ammo_casing/a9mmf = "9mm", /obj/item/ammo_casing/a45f = ".45", /obj/item/ammo_casing/a10mm = "10mm", - /obj/item/ammo_casing/a12g = "12 gauge", - /obj/item/ammo_casing/a12g = "12 gauge", - /obj/item/ammo_casing/a12g/pellet = "12 gauge", - /obj/item/ammo_casing/a12g/pellet = "12 gauge", - /obj/item/ammo_casing/a12g/pellet = "12 gauge", - /obj/item/ammo_casing/a12g/beanbag = "12 gauge", - /obj/item/ammo_casing/a12g/stunshell = "12 gauge", - /obj/item/ammo_casing/a12g/flash = "12 gauge", + /obj/item/ammo_casing/a12g = "12g", + /obj/item/ammo_casing/a12g = "12g", + /obj/item/ammo_casing/a12g/pellet = "12g", + /obj/item/ammo_casing/a12g/pellet = "12g", + /obj/item/ammo_casing/a12g/pellet = "12g", + /obj/item/ammo_casing/a12g/beanbag = "12g", + /obj/item/ammo_casing/a12g/stunshell = "12g", + /obj/item/ammo_casing/a12g/flash = "12g", /obj/item/ammo_casing/a762 = "7.62mm", /obj/item/ammo_casing/a556 = "5.56mm" ) diff --git a/code/modules/projectiles/guns/projectile/revolver.dm b/code/modules/projectiles/guns/projectile/revolver.dm index 3886450fcf..7d7a01464f 100644 --- a/code/modules/projectiles/guns/projectile/revolver.dm +++ b/code/modules/projectiles/guns/projectile/revolver.dm @@ -3,7 +3,7 @@ desc = "The Lumoco Arms HE Colt is a choice revolver for when you absolutely, positively need to put a hole in the other guy. Uses .357 rounds." icon_state = "revolver" item_state = "revolver" - caliber = "357" + caliber = ".357" origin_tech = list(TECH_COMBAT = 2, TECH_MATERIAL = 2) handle_casings = CYCLE_CASINGS max_shells = 6 diff --git a/code/modules/reagents/reagent_containers/food/lunch.dm b/code/modules/reagents/reagent_containers/food/lunch.dm index 1c82f6cded..9d97f528e3 100644 --- a/code/modules/reagents/reagent_containers/food/lunch.dm +++ b/code/modules/reagents/reagent_containers/food/lunch.dm @@ -58,7 +58,7 @@ var/list/lunchables_drink_reagents_ = list(/datum/reagent/drink/nothing, /datum/reagent/drink/dry_ramen, /datum/reagent/drink/hell_ramen, /datum/reagent/drink/hot_ramen, - /datum/reagent/drink/nuka_cola,) + /datum/reagent/drink/nuka_cola) // This default list is a bit different, it contains items we don't want var/list/lunchables_ethanol_reagents_ = list(/datum/reagent/ethanol/acid_spit, diff --git a/code/modules/tables/interactions.dm b/code/modules/tables/interactions.dm index 35dfdc33b6..1b287294b1 100644 --- a/code/modules/tables/interactions.dm +++ b/code/modules/tables/interactions.dm @@ -130,7 +130,7 @@ break_to_parts() return - if(istype(W, /obj/item/weapon/melee/arm_blade)) + if(istype(W, /obj/item/weapon/melee/changeling/arm_blade)) user.visible_message("\The [src] was sliced apart by [user]!") break_to_parts() return diff --git a/code/modules/xenoarcheaology/tools/artifact_harvester.dm b/code/modules/xenoarcheaology/tools/artifact_harvester.dm index 23ff59799e..ca7b43b1d3 100644 --- a/code/modules/xenoarcheaology/tools/artifact_harvester.dm +++ b/code/modules/xenoarcheaology/tools/artifact_harvester.dm @@ -15,10 +15,10 @@ /obj/machinery/artifact_harvester/New() ..() - //connect to a nearby scanner pad - owned_scanner = locate(/obj/machinery/artifact_scanpad) in get_step(src, dir) - if(!owned_scanner) - owned_scanner = locate(/obj/machinery/artifact_scanpad) in orange(1, src) + spawn(50) //Delay so the scan pad has time to actually spawn in + owned_scanner = locate(/obj/machinery/artifact_scanpad) in get_step(src, dir) //connect to a nearby scanner pad + if(!owned_scanner) + owned_scanner = locate(/obj/machinery/artifact_scanpad) in orange(1, src) /obj/machinery/artifact_harvester/attackby(var/obj/I as obj, var/mob/user as mob) if(istype(I,/obj/item/weapon/anobattery)) diff --git a/code/stylesheet.dm b/code/stylesheet.dm index 0e9f5f87b7..663ddae5f6 100644 --- a/code/stylesheet.dm +++ b/code/stylesheet.dm @@ -24,6 +24,7 @@ em {font-style: normal;font-weight: bold;} .ooc .moderator {color: #184880;} .ooc .developer {color: #1b521f;} .ooc .admin {color: #b82e00;} +.ooc .aooc {color: #960018;} /* Admin: Private Messages */ .pm .howto {color: #ff0000; font-weight: bold; font-size: 200%;} diff --git a/icons/mob/back.dmi b/icons/mob/back.dmi index c13a604dba..f601d18e3c 100644 Binary files a/icons/mob/back.dmi and b/icons/mob/back.dmi differ diff --git a/icons/mob/belt.dmi b/icons/mob/belt.dmi index b26eabc764..3a70243e7b 100644 Binary files a/icons/mob/belt.dmi and b/icons/mob/belt.dmi differ diff --git a/icons/mob/hud.dmi b/icons/mob/hud.dmi index 5442b3b4a8..7580e0ea65 100644 Binary files a/icons/mob/hud.dmi and b/icons/mob/hud.dmi differ diff --git a/icons/mob/items/lefthand.dmi b/icons/mob/items/lefthand.dmi index 5e2e4a2c69..28158e4838 100644 Binary files a/icons/mob/items/lefthand.dmi and b/icons/mob/items/lefthand.dmi differ diff --git a/icons/mob/items/lefthand_melee.dmi b/icons/mob/items/lefthand_melee.dmi index 93ac77c655..41f7617c3f 100644 Binary files a/icons/mob/items/lefthand_melee.dmi and b/icons/mob/items/lefthand_melee.dmi differ diff --git a/icons/mob/items/righthand.dmi b/icons/mob/items/righthand.dmi index 3b1a1dea8f..55de85a668 100644 Binary files a/icons/mob/items/righthand.dmi and b/icons/mob/items/righthand.dmi differ diff --git a/icons/mob/items/righthand_melee.dmi b/icons/mob/items/righthand_melee.dmi index ce37a0997e..fb7baddc29 100644 Binary files a/icons/mob/items/righthand_melee.dmi and b/icons/mob/items/righthand_melee.dmi differ diff --git a/icons/mob/screen_spells.dmi b/icons/mob/screen_spells.dmi index 908331c31d..c65d50880f 100644 Binary files a/icons/mob/screen_spells.dmi and b/icons/mob/screen_spells.dmi differ diff --git a/icons/mob/suit.dmi b/icons/mob/suit.dmi index c5464b5336..7249f9b218 100644 Binary files a/icons/mob/suit.dmi and b/icons/mob/suit.dmi differ diff --git a/icons/mob/uniform.dmi b/icons/mob/uniform.dmi index 37c85a0837..3aef9384f4 100644 Binary files a/icons/mob/uniform.dmi and b/icons/mob/uniform.dmi differ diff --git a/icons/obj/ammo.dmi b/icons/obj/ammo.dmi index dcdb20e070..b105cb9e12 100644 Binary files a/icons/obj/ammo.dmi and b/icons/obj/ammo.dmi differ diff --git a/icons/obj/clothing/suits.dmi b/icons/obj/clothing/suits.dmi index 549f90747a..2925d066c2 100644 Binary files a/icons/obj/clothing/suits.dmi and b/icons/obj/clothing/suits.dmi differ diff --git a/icons/obj/clothing/uniforms.dmi b/icons/obj/clothing/uniforms.dmi index a7fed692ad..7c33803ebc 100644 Binary files a/icons/obj/clothing/uniforms.dmi and b/icons/obj/clothing/uniforms.dmi differ diff --git a/icons/obj/defibrillator.dmi b/icons/obj/defibrillator.dmi new file mode 100644 index 0000000000..7be0d18d00 Binary files /dev/null and b/icons/obj/defibrillator.dmi differ diff --git a/icons/obj/food.dmi b/icons/obj/food.dmi index 8df487ad3c..373fdcb25b 100644 Binary files a/icons/obj/food.dmi and b/icons/obj/food.dmi differ diff --git a/icons/obj/storage.dmi b/icons/obj/storage.dmi index f870bd9672..f90a841fde 100644 Binary files a/icons/obj/storage.dmi and b/icons/obj/storage.dmi differ diff --git a/icons/obj/surgery.dmi b/icons/obj/surgery.dmi index 39ed73fc3d..a933bc6a2f 100644 Binary files a/icons/obj/surgery.dmi and b/icons/obj/surgery.dmi differ diff --git a/icons/obj/weapons.dmi b/icons/obj/weapons.dmi index 22f52ad154..c7fd776592 100644 Binary files a/icons/obj/weapons.dmi and b/icons/obj/weapons.dmi differ diff --git a/sound/machines/defib_SafetyOn.ogg b/sound/machines/defib_SafetyOn.ogg new file mode 100644 index 0000000000..cf26153646 Binary files /dev/null and b/sound/machines/defib_SafetyOn.ogg differ diff --git a/sound/machines/defib_charge.ogg b/sound/machines/defib_charge.ogg new file mode 100644 index 0000000000..3e2be160fd Binary files /dev/null and b/sound/machines/defib_charge.ogg differ diff --git a/sound/machines/defib_failed.ogg b/sound/machines/defib_failed.ogg new file mode 100644 index 0000000000..341f70ce84 Binary files /dev/null and b/sound/machines/defib_failed.ogg differ diff --git a/sound/machines/defib_ready.ogg b/sound/machines/defib_ready.ogg new file mode 100644 index 0000000000..718128602e Binary files /dev/null and b/sound/machines/defib_ready.ogg differ diff --git a/sound/machines/defib_safetyOff.ogg b/sound/machines/defib_safetyOff.ogg new file mode 100644 index 0000000000..6ab57097de Binary files /dev/null and b/sound/machines/defib_safetyOff.ogg differ diff --git a/sound/machines/defib_success.ogg b/sound/machines/defib_success.ogg new file mode 100644 index 0000000000..3f71438c16 Binary files /dev/null and b/sound/machines/defib_success.ogg differ diff --git a/sound/machines/defib_zap.ogg b/sound/machines/defib_zap.ogg new file mode 100644 index 0000000000..5612db39a4 Binary files /dev/null and b/sound/machines/defib_zap.ogg differ diff --git a/vorestation.dme b/vorestation.dme index abd0b5fe55..7a59331e5f 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -186,8 +186,8 @@ #include "code\controllers\ProcessScheduler\core\process.dm" #include "code\controllers\ProcessScheduler\core\processScheduler.dm" #include "code\controllers\subsystems\garbage.dm" -#include "code\controllers\subsystems\mobs.dm" #include "code\controllers\subsystems\lighting.dm" +#include "code\controllers\subsystems\mobs.dm" #include "code\controllers\subsystems\transcore_vr.dm" #include "code\datums\ai_law_sets.dm" #include "code\datums\ai_laws.dm" @@ -354,6 +354,7 @@ #include "code\game\antagonist\station\changeling.dm" #include "code\game\antagonist\station\cultist.dm" #include "code\game\antagonist\station\highlander.dm" +#include "code\game\antagonist\station\infiltrator.dm" #include "code\game\antagonist\station\loyalist.dm" #include "code\game\antagonist\station\renegade.dm" #include "code\game\antagonist\station\revolutionary.dm" @@ -440,6 +441,7 @@ #include "code\game\gamemodes\events\holidays\Other.dm" #include "code\game\gamemodes\extended\extended.dm" #include "code\game\gamemodes\heist\heist.dm" +#include "code\game\gamemodes\infiltrator\infiltrator.dm" #include "code\game\gamemodes\malfunction\malf_hardware.dm" #include "code\game\gamemodes\malfunction\malf_research.dm" #include "code\game\gamemodes\malfunction\malf_research_ability.dm"