diff --git a/code/game/objects/items/weapons/chrono_eraser.dm b/code/game/objects/items/weapons/chrono_eraser.dm new file mode 100644 index 00000000000..a3b92ee4dbd --- /dev/null +++ b/code/game/objects/items/weapons/chrono_eraser.dm @@ -0,0 +1,258 @@ +#define CHRONO_BEAM_RANGE 3 +#define CHRONO_FRAME_COUNT 22 +/obj/item/weapon/chrono_eraser + name = "Timestream Eradication Device" + desc = "The result of outlawed time-bluespace research, this device is capable of wiping a being from the timestream. They never are, they never were, they never will be." + icon = 'icons/obj/chronos.dmi' + icon_state = "chronobackpack" + item_state = "backpack" + w_class = 4.0 + slot_flags = SLOT_BACK + slowdown = 1 + action_button_name = "Equip/Unequip TED Gun" + icon_action_button = "action_chronobackpack" + var/obj/item/weapon/gun/energy/chrono_gun/PA = null + var/list/erased_minds = list() //a collection of minds from the dead + +/obj/item/weapon/chrono_eraser/proc/pass_mind(var/datum/mind/M) + erased_minds += M + +/obj/item/weapon/chrono_eraser/dropped() + if(PA) + del(PA) + +/obj/item/weapon/chrono_eraser/Destroy() + dropped() + ..() + +/obj/item/weapon/chrono_eraser/ui_action_click() + var/mob/living/carbon/user = src.loc + if(iscarbon(user) && (user.back == src)) + if(PA) + del(PA) + else + PA = new(src) + user.put_in_hands(PA) + + + +/obj/item/weapon/gun/energy/chrono_gun + name = "T.E.D. Projection Apparatus" + desc = "It's as if they never existed in the first place." + icon = 'icons/obj/chronos.dmi' + icon_state = "chronogun" + item_state = "chronogun" + icon_override = 'icons/mob/in-hand/guns.dmi' + w_class = 3.0 + canremove = 0 + projectile_type = "/obj/item/projectile/energy/chrono_beam" + fire_sound = 'sound/weapons/Laser.ogg' + charge_cost = 0 + fire_delay = 50 + var/obj/item/weapon/chrono_eraser/TED = null + var/obj/effect/chrono_field/field = null + var/turf/startpos = null + +/obj/item/weapon/gun/energy/chrono_gun/New(var/obj/item/weapon/chrono_eraser/T) + . = ..() + if(istype(T)) + TED = T + else //admin must have spawned it + TED = new(src.loc) + del(src) + +/obj/item/weapon/gun/energy/chrono_gun/dropped() + del(src) + +/obj/item/weapon/gun/energy/chrono_gun/update_icon() + return + +/obj/item/weapon/gun/energy/chrono_gun/Fire() + if(field) + field_disconnect(field) + ..() + +/obj/item/weapon/gun/energy/chrono_gun/Destroy() + if(TED) + TED.PA = null + TED = null + if(field) + field_disconnect(field) + ..() + +/obj/item/weapon/gun/energy/chrono_gun/proc/field_connect(var/obj/effect/chrono_field/F) + var/mob/living/user = src.loc + if(F.gun) + if(isliving(user) && F.captured) + user << "FAIL: [F.captured] already has an existing connection." + src.field_disconnect(F) + else + startpos = get_turf(src) + field = F + F.gun = src + if(isliving(user) && F.captured) + user << "Connection established with target: [F.captured]" + + +/obj/item/weapon/gun/energy/chrono_gun/proc/field_disconnect(var/obj/effect/chrono_field/F) + if(F && field == F) + var/mob/living/user = src.loc + if(F.gun == src) + F.gun = null + if(isliving(user) && F.captured) + user << "Disconnected from target: [F.captured]" + field = null + startpos = null + +/obj/item/weapon/gun/energy/chrono_gun/proc/field_check(var/obj/effect/chrono_field/F) + if(F) + if(field == F) + var/turf/currentpos = get_turf(src) + var/mob/living/user = src.loc + if((currentpos == startpos) && (field in view(CHRONO_BEAM_RANGE, currentpos)) && !user.lying && (user.stat == CONSCIOUS)) + return 1 + field_disconnect(F) + return 0 + +/obj/item/weapon/gun/energy/chrono_gun/proc/pass_mind(var/datum/mind/M) + if(TED) + TED.pass_mind(M) + + +/obj/item/projectile/energy/chrono_beam + name = "eradication beam" + icon_state = "chronobolt" + range = CHRONO_BEAM_RANGE + color = null + nodamage = 1 + var/obj/item/weapon/gun/energy/chrono_gun/gun = null + +/obj/item/projectile/energy/chrono_beam/process() + gun = firer.get_active_hand() + if(istype(gun)) + return ..() + else + return 0 + +/obj/item/projectile/energy/chrono_beam/on_hit(var/atom/target) + if(target && gun && isliving(target)) + var/obj/effect/chrono_field/F = new(target.loc, target, gun) + gun.field_connect(F) + + +/obj/effect/chrono_field + name = "eradication field" + desc = "An aura of time-bluespace energy." + icon = 'icons/effects/effects.dmi' + icon_state = "chronofield" + density = 1 + anchored = 1 + unacidable = 1 + blend_mode = BLEND_MULTIPLY + var/mob/living/captured = null + var/obj/item/weapon/gun/energy/chrono_gun/gun = null + var/tickstokill = 30 + var/image/mob_underlay = null + var/preloaded = 0 + var/RPpos = null + +/obj/effect/chrono_field/New(loc, var/mob/living/target, var/obj/item/weapon/gun/energy/chrono_gun/G) + if(target && isliving(target) && G) + target.loc = src + src.captured = target + var/icon/mob_snapshot = getFlatIcon(target) + var/icon/cached_icon = new() + + for(var/i=1, i<=CHRONO_FRAME_COUNT, i++) + var/icon/removing_frame = icon('icons/obj/chronos.dmi', "erasing", SOUTH, i) + var/icon/mob_icon = icon(mob_snapshot) + mob_icon.Blend(removing_frame, ICON_MULTIPLY) + cached_icon.Insert(mob_icon, "frame[i]") + + mob_underlay = new(cached_icon, "frame1") + update_icon() + + desc = initial(desc) + "
It appears to contain [target.name]." + processing_objects.Add(src) + +/obj/effect/chrono_field/Destroy() + if(gun && gun.field_check(src)) + gun.field_disconnect(src) + ..() + +/obj/effect/chrono_field/update_icon() + var/ttk_frame = 1 - (tickstokill / initial(tickstokill)) + ttk_frame = Clamp(Ceiling(ttk_frame * CHRONO_FRAME_COUNT), 1, CHRONO_FRAME_COUNT) + if(ttk_frame != RPpos) + RPpos = ttk_frame + mob_underlay.icon_state = "frame[RPpos]" + underlays = list() //hack: BYOND refuses to update the underlay to match the icon_state otherwise + underlays += mob_underlay + +/obj/effect/chrono_field/process() + if(captured) + if(tickstokill > initial(tickstokill)) + for(var/atom/movable/AM in contents) + AM.loc = loc + del(src) + else if(tickstokill <= 0) + captured << "As the last essence of your being is erased from time, you begin to re-experience your most enjoyable memory. You feel happy..." + var/mob/dead/observer/ghost = captured.ghostize(1) + if(captured.mind) + if(ghost) + ghost.mind = null + if(gun) + gun.pass_mind(captured.mind) + del(captured) + del(src) + else + captured.Paralyse(4) + if(captured.reagents) + captured.reagents.del_reagent("synaptizine") //you pesky thing you + if(captured.loc != src) //If they manage to escape, immediately kill them, this is so that even if there IS a way to get out, they won't use it + captured.loc = src + tickstokill = 0 + return .() + update_icon() + if(gun) + if(gun.field_check(src)) + tickstokill-- + else + gun = null + return .() + else + tickstokill++ + else + del(src) + +/obj/effect/chrono_field/bullet_act(var/obj/item/projectile/P) + if(istype(P, /obj/item/projectile/energy/chrono_beam)) + var/obj/item/projectile/energy/chrono_beam/beam = P + var/obj/item/weapon/gun/energy/chrono_gun/Pgun = beam.gun + if(Pgun && istype(Pgun)) + Pgun.field_connect(src) + else + return 0 + +/obj/effect/chrono_field/assume_air() + return 0 + +/obj/effect/chrono_field/return_air() //we always have nominal air and temperature + var/datum/gas_mixture/GM = new + GM.oxygen = MOLES_O2STANDARD + GM.nitrogen = MOLES_N2STANDARD + GM.temperature = T20C + return GM + +/obj/effect/chrono_field/Move() + return + +/obj/effect/chrono_field/ex_act() + return + +/obj/effect/chrono_field/blob_act() + return + + +#undef CHRONO_BEAM_RANGE +#undef CHRONO_FRAME_COUNT \ No newline at end of file diff --git a/code/modules/clothing/spacesuits/chronosuit.dm b/code/modules/clothing/spacesuits/chronosuit.dm new file mode 100644 index 00000000000..f1875e34de1 --- /dev/null +++ b/code/modules/clothing/spacesuits/chronosuit.dm @@ -0,0 +1,213 @@ +/obj/item/clothing/head/helmet/space/chronos + name = "Chronosuit Helmet" + desc = "A white helmet with an opaque blue visor." + icon_state = "chronohelmet" + item_state = "chronohelmet" + slowdown = 1 + armor = list(melee = 60, bullet = 30/*bullet through the visor*/, laser = 60, energy = 60, bomb = 30, bio = 90, rad = 90) + var/obj/item/clothing/suit/space/chronos/suit = null + +/obj/item/clothing/head/helmet/space/chronos/dropped() + if(suit) + suit.deactivate() + ..() + +/obj/item/clothing/head/helmet/space/chronos/Destroy() + dropped() + ..() + + +/obj/item/clothing/suit/space/chronos + name = "Chronosuit" + desc = "An advanced spacesuit equipped with teleportation and anti-compression technology" + icon_state = "chronosuit" + item_state = "chronosuit" + action_button_name = "Toggle Chronosuit" + icon_action_button = "action_chronosuit" + slowdown = 2 + armor = list(melee = 60, bullet = 60, laser = 60, energy = 60, bomb = 30, bio = 90, rad = 90) + var/obj/item/clothing/head/helmet/space/chronos/helmet = null + var/obj/effect/chronos_cam/camera = null + var/activating = 0 + var/activated = 0 + var/cooldowntime = 50 //deciseconds + var/cooldown = 0 + var/teleporting = 0 + + +/obj/item/clothing/suit/space/chronos/proc/new_camera(var/mob/user) + if(camera) + del(camera) + camera = new /obj/effect/chronos_cam(get_turf(user)) + camera.holder = user + user.remote_control = camera + +/obj/item/clothing/suit/space/chronos/ui_action_click() + if((cooldown <= world.time) && !teleporting && !activating) + if(!activated) + activate() + else + deactivate() + +/obj/item/clothing/suit/space/chronos/dropped() + if(activated) + deactivate() + ..() + +/obj/item/clothing/suit/space/chronos/Destroy() + dropped() + ..() + +/obj/item/clothing/suit/space/chronos/emp_act(severity) + var/mob/living/carbon/human/user = src.loc + switch(severity) + if(1) + if(user && ishuman(user) && (user.wear_suit == src)) + user << "Elecrtromagnetic pulse detected, shutting down systems to preserve integrity..." + deactivate() + +/obj/item/clothing/suit/space/chronos/proc/chronowalk(var/mob/living/carbon/human/user) + if(!teleporting && user && (user.stat == CONSCIOUS)) + teleporting = 1 + var/turf/from_turf = get_turf(user) + if(!from_turf) //sanity, things happen + teleporting = 0 + return + var/turf/to_turf = from_turf + var/atom/movable/overlay/phaseanim = new(from_turf) + var/obj/holder = new(camera) + phaseanim.name = "phasing [user.name]" + phaseanim.icon = 'icons/mob/mob.dmi' + phaseanim.icon_state = "chronostuck" + phaseanim.density = 1 + phaseanim.layer = FLY_LAYER + phaseanim.master = user + user.ExtinguishMob() + if(user.buckled) + user.buckled.unbuckle() + user.loc = holder + flick("chronophase", phaseanim) + spawn(7) + if(user) + if(phaseanim) + if(camera && camera.loc) + to_turf = camera.loc + flick("chronounphase", phaseanim) + else + flick("chronostuck", phaseanim) + phaseanim.loc = to_turf + sleep(7) + if(holder) + if(user && user in holder.contents) + user.loc = to_turf + if(user.client) + if(camera) + user.client.eye = camera + else + user.client.eye = user + del(holder) + else if(user) + user.loc = from_turf + if(phaseanim) + del(phaseanim) + teleporting = 0 + if(user && !user.loc) //ubersanity + user.loc = locate(0,0,1) + user.gib() + +/obj/item/clothing/suit/space/chronos/process() + if(activated) + var/mob/living/carbon/human/user = src.loc + if(user && ishuman(user) && (user.wear_suit == src)) + if(camera && (user.remote_control == camera)) + if(!teleporting && !((camera.x == user.x) && (camera.y == user.y) && (camera.z == user.z))) //cheaper than a couple get_turf calls??? + chronowalk(user) + else + new_camera(user) + else + processing_objects.Remove(src) + +/obj/item/clothing/suit/space/chronos/proc/activate() + if(!activating && !activated && !teleporting) + activating = 1 + var/mob/living/carbon/human/user = src.loc + if(user && ishuman(user)) + if(user.wear_suit == src) + user << "\nChronosuitMK4 login: root" + user << "Password:\n" + user << "root@ChronosuitMK4# chronowalk4 --start\n" + if(user.head && istype(user.head, /obj/item/clothing/head/helmet/space/chronos)) + user << "\[ ok \] Mounting /dev/helmet" + helmet = user.head + helmet.canremove = 0 + helmet.suit = src + src.canremove = 0 + user << "\[ ok \] Starting brainwave scanner" + user << "\[ ok \] Starting ui display driver" + user << "\[ ok \] Initializing chronowalk4-view" + new_camera(user) + processing_objects.Add(src) + activated = 1 + else + user << "\[ fail \] Mounting /dev/helmet" + user << "FATAL: Unable to locate /dev/helmet. Aborting..." + cooldown = world.time + cooldowntime + activating = 0 + return 0 + +/obj/item/clothing/suit/space/chronos/proc/deactivate() + if(activated) + activating = 1 + var/mob/living/carbon/human/user = src.loc + if(user && ishuman(user)) + if(user.wear_suit == src) + user << "\nroot@ChronosuitMK4# chronowalk4 --stop\n" + if(camera) + user << "\[ ok \] Sending TERM signal to chronowalk4-view" //yes I know they aren't a different color when shutting down, but they were too similar at a glance + del(camera) + if(helmet) + user << "\[ ok \] Stopping ui display driver" + user << "\[ ok \] Stopping brainwave scanner" + user << "\[ ok \] Unmounting /dev/helmet" + helmet.canremove = 1 + helmet.suit = null + helmet = null + user << "logout" + src.canremove = 1 + cooldown = world.time + cooldowntime * 1.5 + activated = 0 + activating = 0 + + +/obj/effect/chronos_cam + name = "Chronosuit View" + density = 0 + anchored = 1 + invisibility = 101 + opacity = 0 + mouse_opacity = 0 + var/mob/holder = null + +/obj/effect/chronos_cam/relaymove(var/mob/user, direction) + if(holder) + if(user == holder) + if(user.client && user.client.eye != src) + src.loc = get_turf(user) + user.client.eye = src + var/step = get_step(src, direction) + if(step) + if(istype(step, /turf/space)) + if(!src.Move(step)) + src.loc = step + else + src.loc = step + else + del(src) + +/obj/effect/chronos_cam/Destroy() + if(holder) + if(holder.remote_control == src) + holder.remote_control = null + if(holder.client && (holder.client.eye == src)) + holder.client.eye = holder + ..() \ No newline at end of file diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 3a5d8e7d961..b91583758af 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -203,6 +203,7 @@ var/list/radar_blips = list() // list of screen objects, radar blips var/radar_open = 0 // nonzero is radar is open + var/atom/movable/remote_control //Calls relaymove() to whatever it is var/obj/control_object //Used by admins to possess objects. All mobs should have this var var/datum/visibility_interface/visibility_interface = null // used by the visibility system to provide an interface for the visibility networks diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index d064544d060..0f9035003ca 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -207,6 +207,9 @@ if(Process_Grab()) return + if(mob.remote_control) //we're controlling something, our movement is relayed to it + return mob.remote_control.relaymove(mob, direct) + if(!mob.canmove) return diff --git a/icons/Chrono_Legionairre.dmi b/icons/Chrono_Legionairre.dmi new file mode 100644 index 00000000000..77f8bdfeeeb Binary files /dev/null and b/icons/Chrono_Legionairre.dmi differ diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi index 68d902e656e..17667aab4e9 100644 Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ diff --git a/icons/mob/back.dmi b/icons/mob/back.dmi index bbc484bc8a7..eb984038398 100644 Binary files a/icons/mob/back.dmi and b/icons/mob/back.dmi differ diff --git a/icons/mob/head.dmi b/icons/mob/head.dmi index f0cc991cef5..435f3ae32f1 100644 Binary files a/icons/mob/head.dmi and b/icons/mob/head.dmi differ diff --git a/icons/mob/in-hand/guns.dmi b/icons/mob/in-hand/guns.dmi index 9609c6d23f7..74b5aad9402 100644 Binary files a/icons/mob/in-hand/guns.dmi and b/icons/mob/in-hand/guns.dmi differ diff --git a/icons/mob/mob.dmi b/icons/mob/mob.dmi index 8bd313b7ef7..44b9b3a3043 100644 Binary files a/icons/mob/mob.dmi and b/icons/mob/mob.dmi differ diff --git a/icons/mob/suit.dmi b/icons/mob/suit.dmi index 254c1abce75..bfcb7ce190f 100644 Binary files a/icons/mob/suit.dmi and b/icons/mob/suit.dmi differ diff --git a/icons/obj/chronos.dmi b/icons/obj/chronos.dmi new file mode 100644 index 00000000000..e008e609ff1 Binary files /dev/null and b/icons/obj/chronos.dmi differ diff --git a/icons/obj/clothing/hats.dmi b/icons/obj/clothing/hats.dmi index d8af262681a..1c71d6d674a 100644 Binary files a/icons/obj/clothing/hats.dmi and b/icons/obj/clothing/hats.dmi differ diff --git a/icons/obj/clothing/suits.dmi b/icons/obj/clothing/suits.dmi index f59d96322a0..c8a10b1c4da 100644 Binary files a/icons/obj/clothing/suits.dmi and b/icons/obj/clothing/suits.dmi differ diff --git a/icons/obj/projectiles.dmi b/icons/obj/projectiles.dmi index c4ecd1b70f3..57f75b0f54e 100644 Binary files a/icons/obj/projectiles.dmi and b/icons/obj/projectiles.dmi differ diff --git a/paradise.dme b/paradise.dme index 94264ed7bba..74f82e2588c 100644 --- a/paradise.dme +++ b/paradise.dme @@ -662,6 +662,7 @@ #include "code\game\objects\items\weapons\alien_specific.dm" #include "code\game\objects\items\weapons\cards_ids.dm" #include "code\game\objects\items\weapons\cash.dm" +#include "code\game\objects\items\weapons\chrono_eraser.dm" #include "code\game\objects\items\weapons\cigs_lighters.dm" #include "code\game\objects\items\weapons\clown_items.dm" #include "code\game\objects\items\weapons\cosmetics.dm" @@ -945,6 +946,7 @@ #include "code\modules\clothing\shoes\miscellaneous.dm" #include "code\modules\clothing\spacesuits\alien.dm" #include "code\modules\clothing\spacesuits\breaches.dm" +#include "code\modules\clothing\spacesuits\chronosuit.dm" #include "code\modules\clothing\spacesuits\ert.dm" #include "code\modules\clothing\spacesuits\miscellaneous.dm" #include "code\modules\clothing\spacesuits\ninja.dm" diff --git a/sound/weapons/Laser.ogg b/sound/weapons/Laser.ogg index 4e566c1cf59..0fbb611f9cd 100644 Binary files a/sound/weapons/Laser.ogg and b/sound/weapons/Laser.ogg differ