mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-08-27 05:57:24 +01:00
Adds the Stealth implant, available to traitors (#21235)
* adds the stealth implant * changes - rename "implant" to "bio-chip" - removes the stealth implant box in favor of spawning implant directly for the traitor - makes the fake box not show up on right click. Makes the real box not show up on right click when invis * sirryan pt1 * lewcc review 1 * fix conflict * fix conflict * signal handler
This commit is contained in:
@@ -697,6 +697,8 @@
|
||||
#define COMSIG_HUMAN_CHANGE_HEAD_ACCESSORY "human_change_head_accessory"
|
||||
//sent from living mobs when they parry
|
||||
#define COMSIG_HUMAN_PARRY "human_parry"
|
||||
///From mob/living/carbon/human/do_suicide()
|
||||
#define COMSIG_HUMAN_SUICIDE_ACT "human_suicide_act"
|
||||
|
||||
// /datum/species signals
|
||||
|
||||
|
||||
+11
-5
@@ -1,8 +1,10 @@
|
||||
#define AB_CHECK_RESTRAINED 1
|
||||
#define AB_CHECK_STUNNED 2
|
||||
#define AB_CHECK_LYING 4
|
||||
#define AB_CHECK_CONSCIOUS 8
|
||||
#define AB_CHECK_TURF 16
|
||||
#define AB_CHECK_RESTRAINED (1<<0)
|
||||
#define AB_CHECK_STUNNED (1<<1)
|
||||
#define AB_CHECK_LYING (1<<2)
|
||||
#define AB_CHECK_CONSCIOUS (1<<3)
|
||||
#define AB_CHECK_TURF (1<<4)
|
||||
#define AB_CHECK_HANDS_BLOCKED (1<<5)
|
||||
#define AB_CHECK_IMMOBILE (1<<6)
|
||||
|
||||
|
||||
/datum/action
|
||||
@@ -72,6 +74,10 @@
|
||||
/datum/action/proc/IsAvailable()// returns 1 if all checks pass
|
||||
if(!owner)
|
||||
return FALSE
|
||||
if((check_flags & AB_CHECK_HANDS_BLOCKED) && HAS_TRAIT(owner, TRAIT_HANDS_BLOCKED))
|
||||
return FALSE
|
||||
if((check_flags & AB_CHECK_IMMOBILE) && HAS_TRAIT(owner, TRAIT_IMMOBILIZED))
|
||||
return FALSE
|
||||
if(check_flags & AB_CHECK_RESTRAINED)
|
||||
if(owner.restrained())
|
||||
return FALSE
|
||||
|
||||
@@ -672,6 +672,14 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
|
||||
item = /obj/item/implanter/adrenalin
|
||||
cost = 8
|
||||
|
||||
/datum/uplink_item/implants/stealthimplant
|
||||
name = "Stealth Bio-chip"
|
||||
desc = "This one-of-a-kind implant will make you almost invisible if you play your cards right. \
|
||||
On activation, it will conceal you inside a chameleon cardboard box that is only revealed once someone bumps into it."
|
||||
reference = "SI"
|
||||
item = /obj/item/implanter/stealth
|
||||
cost = 8
|
||||
|
||||
// POINTLESS BADASSERY
|
||||
|
||||
/datum/uplink_item/badass
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
/**
|
||||
* # Stealth Implant
|
||||
*
|
||||
* Implant which allows you to summon an MGS-style cardboard box that turns you invisble after a short delay.
|
||||
*/
|
||||
/obj/item/implant/stealth
|
||||
name = "S3 bio-chip"
|
||||
desc = "Allows you to be hidden in plain sight."
|
||||
implant_state = "implant-syndicate"
|
||||
implant_data = /datum/implant_fluff/stealth
|
||||
actions_types = list(/datum/action/item_action/agent_box)
|
||||
|
||||
/obj/item/implanter/stealth
|
||||
name = "bio-chip implanter (stealth)"
|
||||
implant_type = /obj/item/implant/stealth
|
||||
|
||||
/datum/action/item_action/agent_box
|
||||
name = "Deploy Box"
|
||||
desc = "Find inner peace, here, in the box."
|
||||
check_flags = AB_CHECK_HANDS_BLOCKED | AB_CHECK_IMMOBILE | AB_CHECK_CONSCIOUS | AB_CHECK_STUNNED
|
||||
background_icon_state = "bg_agent"
|
||||
button_icon_state = "deploy_box"
|
||||
use_itemicon = FALSE
|
||||
/// If TRUE, the box can't be deployed
|
||||
var/on_cooldown = FALSE
|
||||
|
||||
/datum/action/item_action/agent_box/Trigger(trigger_flags)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return FALSE
|
||||
if(istype(owner.loc, /obj/structure/closet/cardboard/agent))
|
||||
var/obj/structure/closet/cardboard/agent/box = owner.loc
|
||||
if(box.open())
|
||||
owner.playsound_local(box, 'sound/misc/box_deploy.ogg', 50, TRUE)
|
||||
recall_box_animation()
|
||||
return
|
||||
// Box closing from here on out.
|
||||
if(on_cooldown)
|
||||
return FALSE
|
||||
if(!isturf(owner.loc)) //Don't let the player use this to escape mechs/welded closets.
|
||||
to_chat(owner, "<span class='warning'>You need more space to activate this implant!</span>")
|
||||
return
|
||||
addtimer(VARSET_CALLBACK(src, on_cooldown, FALSE), 10 SECONDS)
|
||||
on_cooldown = TRUE
|
||||
owner.playsound_local(owner, 'sound/misc/box_deploy.ogg', 50, TRUE)
|
||||
spawn_box()
|
||||
|
||||
/datum/action/item_action/agent_box/proc/spawn_box()
|
||||
// Do the box's fade in spawn animation with an image so it follows the owner.
|
||||
var/image/fake_box = image('icons/obj/cardboard_boxes.dmi', owner, "agentbox", ABOVE_MOB_LAYER)
|
||||
flick_overlay_view(fake_box, owner, 0.4 SECONDS)
|
||||
fake_box.alpha = 0
|
||||
fake_box.pixel_z = 30
|
||||
animate(fake_box, pixel_z = fake_box.pixel_z - 30, alpha = fake_box.alpha + 255, time = 3, loop = 1)
|
||||
sleep(3)
|
||||
|
||||
// Spawn the actual box
|
||||
var/obj/structure/closet/cardboard/agent/box = new(get_turf(owner), owner)
|
||||
box.implant_user_UID = owner.UID()
|
||||
// Slightly shorter time since we needed 0.3s to to do the spawn animation.
|
||||
INVOKE_ASYNC(box, TYPE_PROC_REF(/obj/structure/closet/cardboard/agent, go_invisible), 1.7 SECONDS)
|
||||
box.create_fake_box()
|
||||
owner.forceMove(box)
|
||||
|
||||
/datum/action/item_action/agent_box/proc/recall_box_animation()
|
||||
var/image/fake_box = image('icons/obj/cardboard_boxes.dmi', owner, "agentbox", ABOVE_MOB_LAYER)
|
||||
flick_overlay_view(fake_box, owner, 0.4 SECONDS)
|
||||
animate(fake_box, pixel_z = fake_box.pixel_z + 30, alpha = fake_box.alpha - 255, time = 3, loop = 1)
|
||||
|
||||
/datum/action/item_action/agent_box/Grant(mob/grant_to)
|
||||
. = ..()
|
||||
if(owner)
|
||||
RegisterSignal(owner, COMSIG_HUMAN_SUICIDE_ACT, PROC_REF(suicide_act))
|
||||
|
||||
/datum/action/item_action/agent_box/Remove(mob/M)
|
||||
if(owner)
|
||||
UnregisterSignal(owner, COMSIG_HUMAN_SUICIDE_ACT)
|
||||
return ..()
|
||||
|
||||
/datum/action/item_action/agent_box/proc/suicide_act(datum/source)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(!istype(owner.loc, /obj/structure/closet/cardboard/agent))
|
||||
return
|
||||
var/obj/structure/closet/cardboard/agent/box = owner.loc
|
||||
owner.visible_message("<span class='suicide'>[owner] falls out of [box]! It looks like [owner.p_they()] committed suicide!</span>")
|
||||
owner.playsound_local(box, 'sound/misc/box_deploy.ogg', 50, TRUE)
|
||||
INVOKE_ASYNC(box, TYPE_PROC_REF(/obj/structure/closet/cardboard/agent, open))
|
||||
INVOKE_ASYNC(owner, TYPE_PROC_REF(/atom/movable, throw_at), get_turf(owner))
|
||||
return OXYLOSS
|
||||
|
||||
// Stealth implant box
|
||||
/obj/structure/closet/cardboard/agent
|
||||
name = "inconspicious box"
|
||||
desc = "It's so normal that you didn't notice it before."
|
||||
icon_state = "agentbox"
|
||||
max_integrity = 1
|
||||
move_speed_multiplier = 0.5 // You can move at normal walk speed while in this box.
|
||||
/// UID of the person who summoned this box with an implant.
|
||||
var/implant_user_UID
|
||||
// This has to be a separate object and not just an image because the image will inherit the box's 0 alpha while it is stealthed.
|
||||
/// A holder effect which follows the src box so we can display an image to the person inside the box.
|
||||
var/obj/effect/fake_box
|
||||
/// The box image attached to the `fake_box` object.
|
||||
var/image/box_img
|
||||
|
||||
/obj/structure/closet/cardboard/agent/Destroy()
|
||||
var/mob/living/implant_user = locateUID(implant_user_UID)
|
||||
implant_user?.client?.images -= box_img
|
||||
QDEL_NULL(fake_box)
|
||||
QDEL_NULL(box_img)
|
||||
return ..()
|
||||
|
||||
/obj/structure/closet/cardboard/agent/open()
|
||||
. = ..()
|
||||
if(!.)
|
||||
return FALSE
|
||||
qdel(src)
|
||||
|
||||
// When the box is opened, it's deleted, so we never need to update this.
|
||||
/obj/structure/closet/cardboard/agent/update_icon_state()
|
||||
return
|
||||
|
||||
/obj/structure/closet/cardboard/agent/proc/create_fake_box()
|
||||
if(fake_box)
|
||||
return
|
||||
fake_box = new(get_turf(src))
|
||||
fake_box.mouse_opacity = MOUSE_OPACITY_TRANSPARENT // This object should be completely invisible.
|
||||
box_img = image(icon, fake_box, icon_state, ABOVE_MOB_LAYER)
|
||||
box_img.alpha = 128
|
||||
RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(move_fake_box))
|
||||
var/mob/living/implant_user = locateUID(implant_user_UID)
|
||||
implant_user.client?.images += box_img
|
||||
|
||||
/obj/structure/closet/cardboard/agent/proc/move_fake_box(datum/source, oldloc, move_dir)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
// For non-standard movement such as teleports.
|
||||
if(!move_dir)
|
||||
fake_box.loc = get_turf(src)
|
||||
return
|
||||
// For basic 8-directional movement.
|
||||
fake_box.loc = get_step(fake_box, move_dir)
|
||||
|
||||
/obj/structure/closet/cardboard/agent/proc/go_invisible(invis_time = 2 SECONDS)
|
||||
animate(src, alpha = 0, time = invis_time)
|
||||
sleep(invis_time)
|
||||
// This is so people can't locate the box by spamming right click everywhere.
|
||||
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
|
||||
/obj/structure/closet/cardboard/agent/proc/reveal()
|
||||
alpha = 255
|
||||
mouse_opacity = MOUSE_OPACITY_OPAQUE
|
||||
addtimer(CALLBACK(src, PROC_REF(go_invisible)), 1 SECONDS, TIMER_OVERRIDE|TIMER_UNIQUE)
|
||||
|
||||
/obj/structure/closet/cardboard/agent/Bump(atom/A)
|
||||
. = ..()
|
||||
if(isliving(A))
|
||||
reveal()
|
||||
|
||||
/obj/structure/closet/cardboard/agent/Bumped(atom/movable/A)
|
||||
. = ..()
|
||||
if(isliving(A))
|
||||
reveal()
|
||||
@@ -116,3 +116,9 @@
|
||||
life = "Unknown, no collected sample has been active long enough to determine lifespan."
|
||||
notes = "An experimental serum which causes rapid muscular growth in Hominidae. Side-affects may include hypertrichosis, violent outbursts, and an unending affinity for bananas."
|
||||
function = "Allows the user to transform into an angry fast and robust gorilla. Very deadly in close quarters."
|
||||
|
||||
/datum/implant_fluff/stealth
|
||||
name = "Syndicate S3 \"Stealth\" Bio-chip"
|
||||
life = "Unknown. No collected sample has been active long enough to determine lifespan."
|
||||
notes = "Manually activated by the user."
|
||||
function = "Allows the user to summon a box from a bluespace pocket located inside the implant. The exterior of this box is lined with experimental cloaking panels which render the box invisible to the naked eye."
|
||||
|
||||
@@ -12,8 +12,10 @@
|
||||
open_sound_volume = 35
|
||||
close_sound_volume = 35
|
||||
material_drop = /obj/item/stack/sheet/cardboard
|
||||
/// How fast a mob can move inside this box.
|
||||
var/move_speed_multiplier = 1
|
||||
var/amt = 4
|
||||
var/move_delay = 0
|
||||
var/move_delay = FALSE
|
||||
var/egged = 0
|
||||
|
||||
/obj/structure/closet/cardboard/relaymove(mob/living/user, direction)
|
||||
@@ -22,8 +24,12 @@
|
||||
move_delay = TRUE
|
||||
var/oldloc = loc
|
||||
step(src, direction)
|
||||
// By default, while inside a box, we move at walk speed times the speed multipler of the box.
|
||||
var/delay = GLOB.configuration.movement.base_walk_speed * move_speed_multiplier
|
||||
if(IS_DIR_DIAGONAL(direction))
|
||||
delay *= SQRT_2 // Moving diagonal counts as moving 2 tiles, we need to slow them down accordingly.
|
||||
if(oldloc != loc)
|
||||
addtimer(CALLBACK(src, PROC_REF(ResetMoveDelay)), GLOB.configuration.movement.base_walk_speed)
|
||||
addtimer(CALLBACK(src, PROC_REF(ResetMoveDelay)), delay)
|
||||
else
|
||||
move_delay = FALSE
|
||||
|
||||
@@ -46,7 +52,7 @@
|
||||
L.do_alert_animation(L)
|
||||
egged = 1
|
||||
alerted << sound('sound/machines/chime.ogg')
|
||||
..()
|
||||
return ..()
|
||||
|
||||
/mob/living/proc/do_alert_animation(atom/A)
|
||||
var/image/I
|
||||
|
||||
+12
-11
@@ -76,21 +76,22 @@
|
||||
|
||||
/mob/living/carbon/human/do_suicide()
|
||||
var/obj/item/held_item = get_active_hand()
|
||||
if(held_item)
|
||||
var/damagetype = held_item.suicide_act(src)
|
||||
if(damagetype)
|
||||
if(damagetype & SHAME)
|
||||
adjustStaminaLoss(200)
|
||||
suiciding = FALSE
|
||||
return
|
||||
if(damagetype & OBLITERATION) // Does it gib or something? Don't deal damage
|
||||
return
|
||||
human_suicide(damagetype, held_item)
|
||||
var/damagetype = SEND_SIGNAL(src, COMSIG_HUMAN_SUICIDE_ACT) || held_item?.suicide_act(src)
|
||||
|
||||
if(damagetype)
|
||||
if(damagetype & SHAME)
|
||||
adjustStaminaLoss(200)
|
||||
suiciding = FALSE
|
||||
return
|
||||
if(damagetype & OBLITERATION) // Does it gib or something? Don't deal damage
|
||||
return
|
||||
human_suicide(damagetype, held_item)
|
||||
return
|
||||
|
||||
for(var/obj/O in orange(1, src))
|
||||
if(O.suicidal_hands)
|
||||
continue
|
||||
var/damagetype = O.suicide_act(src)
|
||||
damagetype = O.suicide_act(src)
|
||||
if(damagetype)
|
||||
if(damagetype & SHAME)
|
||||
adjustStaminaLoss(200)
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 139 KiB After Width: | Height: | Size: 142 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
@@ -1078,6 +1078,7 @@
|
||||
#include "code\game\objects\items\weapons\implants\implant_krav_maga.dm"
|
||||
#include "code\game\objects\items\weapons\implants\implant_mindshield.dm"
|
||||
#include "code\game\objects\items\weapons\implants\implant_sad_trombone.dm"
|
||||
#include "code\game\objects\items\weapons\implants\implant_stealth.dm"
|
||||
#include "code\game\objects\items\weapons\implants\implant_storage.dm"
|
||||
#include "code\game\objects\items\weapons\implants\implant_tracking.dm"
|
||||
#include "code\game\objects\items\weapons\implants\implant_traitor.dm"
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user