diff --git a/code/game/objects/items/RCD.dm b/code/game/objects/items/RCD.dm index a1f663986577..8e53ec65c58f 100644 --- a/code/game/objects/items/RCD.dm +++ b/code/game/objects/items/RCD.dm @@ -413,6 +413,7 @@ RLD no_ammo_message = "Insufficient charge." desc = "A device used to rapidly build walls and floors." canRturf = TRUE + var/energyfactor = 72 /obj/item/construction/rcd/borg/useResource(amount, mob/user) @@ -423,7 +424,7 @@ RLD if(user) to_chat(user, no_ammo_message) return 0 - . = borgy.cell.use(amount * 72) //borgs get 1.3x the use of their RCDs + . = borgy.cell.use(amount * energyfactor) //borgs get 1.3x the use of their RCDs if(!. && user) to_chat(user, no_ammo_message) return . @@ -436,11 +437,16 @@ RLD if(user) to_chat(user, no_ammo_message) return 0 - . = borgy.cell.charge >= (amount * 72) + . = borgy.cell.charge >= (amount * energyfactor) if(!. && user) to_chat(user, no_ammo_message) return . +/obj/item/construction/rcd/borg/syndicate + icon_state = "ircd" + item_state = "ircd" + energyfactor = 66 + /obj/item/construction/rcd/loaded matter = 160 diff --git a/code/modules/antagonists/_common/antag_spawner.dm b/code/modules/antagonists/_common/antag_spawner.dm index a5cb83c3a93c..5a42b8504c40 100644 --- a/code/modules/antagonists/_common/antag_spawner.dm +++ b/code/modules/antagonists/_common/antag_spawner.dm @@ -182,6 +182,10 @@ name = "syndicate medical teleporter" borg_to_spawn = "Medical" +/obj/item/antag_spawner/nuke_ops/borg_tele/saboteur + name = "syndicate saboteur teleporter" + borg_to_spawn = "Saboteur" + /obj/item/antag_spawner/nuke_ops/borg_tele/spawn_antag(client/C, turf/T, kind, datum/mind/user) var/mob/living/silicon/robot/R var/datum/antagonist/nukeop/creator_op = user.has_antag_datum(/datum/antagonist/nukeop,TRUE) @@ -191,6 +195,8 @@ switch(borg_to_spawn) if("Medical") R = new /mob/living/silicon/robot/modules/syndicate/medical(T) + if("Saboteur") + R = new /mob/living/silicon/robot/modules/syndicate/saboteur(T) else R = new /mob/living/silicon/robot/modules/syndicate(T) //Assault borg by default diff --git a/code/modules/antagonists/nukeop/equipment/borgchameleon.dm b/code/modules/antagonists/nukeop/equipment/borgchameleon.dm new file mode 100644 index 000000000000..01e18c752bcf --- /dev/null +++ b/code/modules/antagonists/nukeop/equipment/borgchameleon.dm @@ -0,0 +1,103 @@ +/obj/item/borg_chameleon + name = "cyborg chameleon projector" + icon = 'icons/obj/device.dmi' + icon_state = "shield0" + flags_1 = CONDUCT_1 + item_flags = NOBLUDGEON + item_state = "electronic" + lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' + righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' + w_class = WEIGHT_CLASS_SMALL + var/friendlyName + var/savedName + var/active = FALSE + var/activationCost = 300 + var/activationUpkeep = 50 + var/disguise = "engineer" + var/datum/component/mobhook // need this to deal with unregistration properly + var/mob/living/silicon/robot/user // needed for process() + +/obj/item/borg_chameleon/Initialize() + . = ..() + friendlyName = pick(GLOB.ai_names) + +/obj/item/borg_chameleon/Destroy() + QDEL_NULL(mobhook) + return ..() + +/obj/item/borg_chameleon/dropped(mob/user) + . = ..() + disrupt(user) + +/obj/item/borg_chameleon/equipped(mob/user) + . = ..() + disrupt(user) + +/obj/item/borg_chameleon/attack_self(mob/living/silicon/robot/user) + if (user && user.cell && user.cell.charge > activationCost) + if (isturf(user.loc)) + toggle(user) + else + to_chat(user, "You can't use [src] while inside something!") + else + to_chat(user, "You need at least [activationCost] charge in your cell to use [src]!") + +/obj/item/borg_chameleon/proc/toggle(mob/living/silicon/robot/user) + if(active) + playsound(src, 'sound/effects/pop.ogg', 100, 1, -6) + to_chat(user, "You deactivate \the [src].") + deactivate(user) + else + to_chat(user, "You activate \the [src].") + playsound(src, 'sound/effects/seedling_chargeup.ogg', 100, 1, -6) + if (do_after(user, 50, target=user) && user.cell.use(activationCost)) + playsound(src, 'sound/effects/bamf.ogg', 100, 1, -6) + to_chat(user, "You are now disguised as the Nanotrasen engineering borg \"[friendlyName]\".") + activate(user) + else + to_chat(user, "The chameleon field fizzles.") + do_sparks(3, FALSE, user) + +/obj/item/borg_chameleon/process() + if (user) + if (!user.cell || !user.cell.use(activationUpkeep)) + disrupt(user) + else + return PROCESS_KILL + +/obj/item/borg_chameleon/proc/activate(mob/living/silicon/robot/user) + START_PROCESSING(SSobj, src) + src.user = user + savedName = user.name + user.name = friendlyName + user.module.cyborg_base_icon = disguise + active = TRUE + if (mobhook && mobhook.parent != user) + QDEL_NULL(mobhook) + if (!mobhook) + var/callback = CALLBACK(src, .proc/disrupt, user) // push user into the callback so that it's guaranteed to be the first arg + mobhook = user.AddComponent(/datum/component/redirect, list( // list here all signals that should break the camouflage + COMSIG_PARENT_ATTACKBY = callback, + COMSIG_ATOM_ATTACK_HAND = callback, + COMSIG_MOVABLE_IMPACT_ZONE = callback, + COMSIG_ATOM_BULLET_ACT = callback, + COMSIG_ATOM_EX_ACT = callback, + COMSIG_ATOM_FIRE_ACT = callback, + COMSIG_ATOM_EMP_ACT = callback, + )) + user.update_icons() + +/obj/item/borg_chameleon/proc/deactivate(mob/living/silicon/robot/user) + STOP_PROCESSING(SSobj, src) + QDEL_NULL(mobhook) + do_sparks(5, FALSE, user) + user.name = savedName + user.module.cyborg_base_icon = initial(user.module.cyborg_base_icon) + active = FALSE + user.update_icons() + src.user = user + +/obj/item/borg_chameleon/proc/disrupt(mob/living/silicon/robot/user) + if(active) + to_chat(user, "Your chameleon field deactivates.") + deactivate(user) diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 86fb68807f2c..846768c3ab73 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -853,6 +853,17 @@ Help the operatives secure the disk at all costs!" set_module = /obj/item/robot_module/syndicate_medical +/mob/living/silicon/robot/modules/syndicate/saboteur + icon_state = "synd_engi" + playstyle_string = "You are a Syndicate saboteur cyborg!
\ + You are armed with robust engineering tools to aid you in your mission: help the operatives secure the nuclear authentication disk. \ + Your destination tagger will allow you to stealthily traverse the disposal network across the station \ + Your welder will allow you to repair the operatives' exosuits, but also yourself and your fellow cyborgs \ + Your cyborg chameleon projector allows you to assume the appearance and registered name of a Nanotrasen engineering borg, and undertake covert actions on the station \ + Be aware that almost any physical contact or incidental damage will break your camouflage \ + Help the operatives secure the disk at all costs!" + set_module = /obj/item/robot_module/saboteur + /mob/living/silicon/robot/proc/notify_ai(notifytype, oldname, newname) if(!connected_ai) return diff --git a/code/modules/mob/living/silicon/robot/robot_defense.dm b/code/modules/mob/living/silicon/robot/robot_defense.dm index c90c719e8a85..037b2819e993 100644 --- a/code/modules/mob/living/silicon/robot/robot_defense.dm +++ b/code/modules/mob/living/silicon/robot/robot_defense.dm @@ -177,8 +177,8 @@ if (stat != DEAD) adjustBruteLoss(30) -/mob/living/silicon/robot/bullet_act(var/obj/item/projectile/Proj) - ..(Proj) +/mob/living/silicon/robot/bullet_act(var/obj/item/projectile/Proj, def_zone) + ..() updatehealth() if(prob(75) && Proj.damage > 0) spark_system.start() diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm index 3f3071bf0c20..b57fa101b0e3 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules.dm @@ -32,6 +32,7 @@ var/list/ride_offset_y = list("north" = 4, "south" = 4, "east" = 3, "west" = 3) var/ride_allow_incapacitated = FALSE var/allow_riding = TRUE + var/canDispose = FALSE // Whether the borg can stuff itself into disposal /obj/item/robot_module/Initialize() . = ..() @@ -596,6 +597,40 @@ can_be_pushed = FALSE hat_offset = 3 +/obj/item/robot_module/saboteur + name = "Syndicate Saboteur" + basic_modules = list( + /obj/item/assembly/flash/cyborg, + /obj/item/borg/sight/thermal, + /obj/item/construction/rcd/borg/syndicate, + /obj/item/pipe_dispenser, + /obj/item/extinguisher, + /obj/item/weldingtool/largetank/cyborg, + /obj/item/screwdriver/nuke, + /obj/item/wrench/cyborg, + /obj/item/crowbar/cyborg, + /obj/item/wirecutters/cyborg, + /obj/item/multitool/cyborg, + /obj/item/stack/sheet/metal/cyborg, + /obj/item/stack/sheet/glass/cyborg, + /obj/item/stack/sheet/rglass/cyborg, + /obj/item/stack/rods/cyborg, + /obj/item/stack/tile/plasteel/cyborg, + /obj/item/destTagger/borg, + /obj/item/stack/cable_coil/cyborg, + /obj/item/pinpointer/syndicate_cyborg, + /obj/item/borg_chameleon, + ) + ratvar_modules = list( + /obj/item/clockwork/slab/cyborg/engineer, + /obj/item/clockwork/replica_fabricator/cyborg) + cyborg_base_icon = "synd_engi" + moduleselect_icon = "malf" + can_be_pushed = FALSE + magpulsing = TRUE + hat_offset = -4 + canDispose = TRUE + /datum/robot_energy_storage var/name = "Generic energy storage" var/max_energy = 30000 diff --git a/code/modules/mob/living/silicon/silicon_defense.dm b/code/modules/mob/living/silicon/silicon_defense.dm index 073a2eec2b28..d898fadc5fcc 100644 --- a/code/modules/mob/living/silicon/silicon_defense.dm +++ b/code/modules/mob/living/silicon/silicon_defense.dm @@ -65,6 +65,9 @@ //ATTACK HAND IGNORING PARENT RETURN VALUE /mob/living/silicon/attack_hand(mob/living/carbon/human/M) + . = FALSE + if(SEND_SIGNAL(src, COMSIG_ATOM_ATTACK_HAND, M) & COMPONENT_NO_ATTACK_HAND) + . = TRUE switch(M.a_intent) if ("help") M.visible_message("[M] pets [src].", \ @@ -76,7 +79,6 @@ playsound(src.loc, 'sound/effects/bang.ogg', 10, 1) visible_message("[M] punches [src], but doesn't leave a dent.", \ "[M] punches [src], but doesn't leave a dent.", null, COMBAT_MESSAGE_RANGE) - return 0 /mob/living/silicon/attack_drone(mob/living/simple_animal/drone/M) if(M.a_intent == INTENT_HARM) @@ -108,7 +110,8 @@ M.visible_message("[M] is thrown off of [src]!") flash_act(affect_silicon = 1) -/mob/living/silicon/bullet_act(obj/item/projectile/Proj) +/mob/living/silicon/bullet_act(obj/item/projectile/Proj, def_zone) + SEND_SIGNAL(src, COMSIG_ATOM_BULLET_ACT, Proj, def_zone) if((Proj.damage_type == BRUTE || Proj.damage_type == BURN)) adjustBruteLoss(Proj.damage) if(prob(Proj.damage*1.5)) diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm index 8c9769f27852..3ac50c8e721e 100644 --- a/code/modules/projectiles/projectile/magic.dm +++ b/code/modules/projectiles/projectile/magic.dm @@ -153,6 +153,7 @@ var/robot = pick(200;/mob/living/silicon/robot, /mob/living/silicon/robot/modules/syndicate, /mob/living/silicon/robot/modules/syndicate/medical, + /mob/living/silicon/robot/modules/syndicate/saboteur, 200;/mob/living/simple_animal/drone/polymorphed) new_mob = new robot(M.loc) if(issilicon(new_mob)) @@ -510,5 +511,3 @@ var/turf/T = get_turf(target) for(var/i=0, i<50, i+=10) addtimer(CALLBACK(GLOBAL_PROC, .proc/explosion, T, -1, exp_heavy, exp_light, exp_flash, FALSE, FALSE, exp_fire), i) - - diff --git a/code/modules/recycling/disposal/bin.dm b/code/modules/recycling/disposal/bin.dm index fd98c54b81cf..6ac9ac19c092 100644 --- a/code/modules/recycling/disposal/bin.dm +++ b/code/modules/recycling/disposal/bin.dm @@ -115,7 +115,12 @@ /obj/machinery/disposal/proc/stuff_mob_in(mob/living/target, mob/living/user) if(!iscarbon(user) && !user.ventcrawler) //only carbon and ventcrawlers can climb into disposal by themselves. - return + if (iscyborg(user)) + var/mob/living/silicon/robot/borg = user + if (!borg.module || !borg.module.canDispose) + return + else + return if(!isturf(user.loc)) //No magically doing it from inside closets return if(target.buckled || target.has_buckled_mobs()) diff --git a/code/modules/recycling/disposal/holder.dm b/code/modules/recycling/disposal/holder.dm index 2c0a6aa646b6..639df2e36406 100644 --- a/code/modules/recycling/disposal/holder.dm +++ b/code/modules/recycling/disposal/holder.dm @@ -52,9 +52,13 @@ if(istype(AM, /obj/structure/bigDelivery) && !hasmob) var/obj/structure/bigDelivery/T = AM src.destinationTag = T.sortTag - if(istype(AM, /obj/item/smallDelivery) && !hasmob) + else if(istype(AM, /obj/item/smallDelivery) && !hasmob) var/obj/item/smallDelivery/T = AM src.destinationTag = T.sortTag + else if(istype(AM, /mob/living/silicon/robot)) + var/obj/item/destTagger/borg/tagger = locate() in AM + if (tagger) + src.destinationTag = tagger.currTag // start the movement process diff --git a/code/modules/recycling/sortingmachinery.dm b/code/modules/recycling/sortingmachinery.dm index fa2eaa22d4bf..052d12407046 100644 --- a/code/modules/recycling/sortingmachinery.dm +++ b/code/modules/recycling/sortingmachinery.dm @@ -154,6 +154,10 @@ flags_1 = CONDUCT_1 slot_flags = ITEM_SLOT_BELT +/obj/item/destTagger/borg + name = "cyborg destination tagger" + desc = "Used to fool the disposal mail network into thinking that you're a harmless parcel. Does actually work as a regular destination tagger as well." + /obj/item/destTagger/suicide_act(mob/living/user) user.visible_message("[user] begins tagging [user.p_their()] final destination! It looks like [user.p_theyre()] trying to commit suicide!") if (islizard(user)) diff --git a/code/modules/uplink/uplink_items.dm b/code/modules/uplink/uplink_items.dm index 8077e59b4de4..6d47a86b81c1 100644 --- a/code/modules/uplink/uplink_items.dm +++ b/code/modules/uplink/uplink_items.dm @@ -201,7 +201,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) cost = 10 surplus = 40 include_modes = list(/datum/game_mode/nuclear) - + /datum/uplink_item/dangerous/carbine name = "M-90gl Carbine" desc = "A fully-loaded, specialized three-round burst carbine that fires 5.56mm ammunition from a 30 round magazine \ @@ -526,7 +526,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/storage/backpack/duffelbag/syndie/ammo/smg cost = 20 include_modes = list(/datum/game_mode/nuclear) - + /datum/uplink_item/ammo/carbine name = "5.56mm Toploader Magazine" desc = "An additional 30-round 5.56mm magazine; suitable for use with the M-90gl carbine. \ @@ -534,7 +534,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/ammo_box/magazine/m556 cost = 4 include_modes = list(/datum/game_mode/nuclear) - + /datum/uplink_item/ammo/a40mm name = "40mm Grenade" desc = "A 40mm HE grenade for use with the M-90gl's under-barrel grenade launcher. \ @@ -642,6 +642,14 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) cost = 35 restricted = TRUE +/datum/uplink_item/support/reinforcement/saboteur_borg + name = "Syndicate Saboteur Cyborg" + desc = "A streamlined engineering cyborg, equipped with thermal vision and the ability to traverse Nanotrasen's disposal network. Also incapable of leaving the welder in the shuttle." + item = /obj/item/antag_spawner/nuke_ops/borg_tele/saboteur + refundable = TRUE + cost = 35 + restricted = TRUE + /datum/uplink_item/support/gygax name = "Gygax Exosuit" desc = "A lightweight exosuit, painted in a dark scheme. Its speed and equipment selection make it excellent \ diff --git a/icons/mob/robots.dmi b/icons/mob/robots.dmi index ea0d10e67d64..1ead2a2afad2 100644 Binary files a/icons/mob/robots.dmi and b/icons/mob/robots.dmi differ diff --git a/tgstation.dme b/tgstation.dme index 2b2018dc3f10..128b5605dcbc 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1254,6 +1254,7 @@ #include "code\modules\antagonists\ninja\ninja.dm" #include "code\modules\antagonists\nukeop\clownop.dm" #include "code\modules\antagonists\nukeop\nukeop.dm" +#include "code\modules\antagonists\nukeop\equipment\borgchameleon.dm" #include "code\modules\antagonists\nukeop\equipment\nuclear_challenge.dm" #include "code\modules\antagonists\nukeop\equipment\nuclearbomb.dm" #include "code\modules\antagonists\nukeop\equipment\pinpointer.dm"