mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-30 08:37:43 +01:00
[MIRROR] Refactors chameleon actions, Adds chameleon outfit saving, Adds chameleon scanner [MDB IGNORE] (#22773)
* Refactors chameleon actions, Adds chameleon outfit saving, Adds chameleon scanner * Update cards_ids.dm * Update _glasses.dm * Delete chameleon.dm * Update _glasses.dm * Update _glasses.dm --------- Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Co-authored-by: Bloop <13398309+vinylspiders@users.noreply.github.com>
This commit is contained in:
co-authored by
MrMelbert
Bloop
parent
7b2447ea08
commit
598952c1c2
@@ -4,7 +4,9 @@
|
||||
|
||||
///from the [EX_ACT] wrapper macro: (severity, target)
|
||||
#define COMSIG_ATOM_EX_ACT "atom_ex_act"
|
||||
///from base of atom/emp_act(): (severity)
|
||||
///from base of atom/emp_act(): (severity). return EMP protection flags
|
||||
#define COMSIG_ATOM_PRE_EMP_ACT "atom_emp_act"
|
||||
///from base of atom/emp_act(): (severity, protection)
|
||||
#define COMSIG_ATOM_EMP_ACT "atom_emp_act"
|
||||
///from base of atom/fire_act(): (exposed_temperature, exposed_volume)
|
||||
#define COMSIG_ATOM_FIRE_ACT "atom_fire_act"
|
||||
|
||||
@@ -66,9 +66,10 @@ GLOBAL_LIST_EMPTY(GPS_list)
|
||||
examine_list += span_notice("Alt-click to switch it [tracking ? "off":"on"].")
|
||||
|
||||
///Called on COMSIG_ATOM_EMP_ACT
|
||||
/datum/component/gps/item/proc/on_emp_act(datum/source, severity)
|
||||
/datum/component/gps/item/proc/on_emp_act(datum/source, severity, protection)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(protection & EMP_PROTECT_SELF)
|
||||
return
|
||||
emped = TRUE
|
||||
var/atom/A = parent
|
||||
A.cut_overlay("working")
|
||||
|
||||
@@ -8,10 +8,10 @@
|
||||
if(. == ELEMENT_INCOMPATIBLE || !isatom(target))
|
||||
return ELEMENT_INCOMPATIBLE
|
||||
flags = _flags
|
||||
RegisterSignal(target, COMSIG_ATOM_EMP_ACT, PROC_REF(getEmpFlags))
|
||||
RegisterSignal(target, COMSIG_ATOM_PRE_EMP_ACT, PROC_REF(getEmpFlags))
|
||||
|
||||
/datum/element/empprotection/Detach(atom/target)
|
||||
UnregisterSignal(target, COMSIG_ATOM_EMP_ACT)
|
||||
UnregisterSignal(target, COMSIG_ATOM_PRE_EMP_ACT)
|
||||
return ..()
|
||||
|
||||
/datum/element/empprotection/proc/getEmpFlags(datum/source, severity)
|
||||
|
||||
+5
-3
@@ -632,17 +632,19 @@
|
||||
/**
|
||||
* React to an EMP of the given severity
|
||||
*
|
||||
* Default behaviour is to send the [COMSIG_ATOM_EMP_ACT] signal
|
||||
* Default behaviour is to send the [COMSIG_ATOM_PRE_EMP_ACT] and [COMSIG_ATOM_EMP_ACT] signal
|
||||
*
|
||||
* If the signal does not return protection, and there are attached wires then we call
|
||||
* If the pre-signal does not return protection, and there are attached wires then we call
|
||||
* [emp_pulse][/datum/wires/proc/emp_pulse] on the wires
|
||||
*
|
||||
* We then return the protection value
|
||||
*/
|
||||
/atom/proc/emp_act(severity)
|
||||
var/protection = SEND_SIGNAL(src, COMSIG_ATOM_EMP_ACT, severity)
|
||||
var/protection = SEND_SIGNAL(src, COMSIG_ATOM_PRE_EMP_ACT, severity)
|
||||
if(!(protection & EMP_PROTECT_WIRES) && istype(wires))
|
||||
wires.emp_pulse()
|
||||
|
||||
SEND_SIGNAL(src, COMSIG_ATOM_EMP_ACT, severity, protection)
|
||||
return protection // Pass the protection value collected here upwards
|
||||
|
||||
/**
|
||||
|
||||
@@ -1313,9 +1313,11 @@
|
||||
|
||||
/obj/item/card/id/advanced/chameleon
|
||||
name = "agent card"
|
||||
desc = "A highly advanced chameleon ID card. Touch this card on another ID card or player to choose which accesses to copy. Has special magnetic properties which force it to the front of wallets."
|
||||
desc = "A highly advanced chameleon ID card. Touch this card on another ID card or player to choose which accesses to copy. \
|
||||
Has special magnetic properties which force it to the front of wallets."
|
||||
trim = /datum/id_trim/chameleon
|
||||
wildcard_slots = WILDCARD_LIMIT_CHAMELEON_PLUS // SKYRAT EDIT - Original WILDCARD_LIMIT_CHAMELEON
|
||||
actions_types = list(/datum/action/item_action/chameleon/change/id, /datum/action/item_action/chameleon/change/id_trim)
|
||||
|
||||
/// Have we set a custom name and job assignment, or will we use what we're given when we chameleon change?
|
||||
var/forged = FALSE
|
||||
@@ -1326,38 +1328,35 @@
|
||||
|
||||
/obj/item/card/id/advanced/chameleon/Initialize(mapload)
|
||||
. = ..()
|
||||
|
||||
var/datum/action/item_action/chameleon/change/id/chameleon_card_action = new(src)
|
||||
chameleon_card_action.chameleon_type = /obj/item/card/id/advanced
|
||||
chameleon_card_action.chameleon_name = "ID Card"
|
||||
chameleon_card_action.initialize_disguises()
|
||||
add_item_action(chameleon_card_action)
|
||||
register_item_context()
|
||||
|
||||
/obj/item/card/id/advanced/chameleon/Destroy()
|
||||
theft_target = null
|
||||
. = ..()
|
||||
return ..()
|
||||
|
||||
/obj/item/card/id/advanced/chameleon/afterattack(atom/target, mob/user, proximity)
|
||||
/obj/item/card/id/advanced/chameleon/afterattack(atom/target, mob/user, proximity, click_parameters)
|
||||
. = ..()
|
||||
if(!proximity)
|
||||
return
|
||||
|
||||
if(isidcard(target))
|
||||
theft_target = WEAKREF(target)
|
||||
ui_interact(user)
|
||||
return AFTERATTACK_PROCESSED_ITEM
|
||||
|
||||
return ..()
|
||||
return . | AFTERATTACK_PROCESSED_ITEM
|
||||
|
||||
/obj/item/card/id/advanced/chameleon/pre_attack_secondary(atom/target, mob/living/user, params)
|
||||
. = ..()
|
||||
if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
|
||||
return .
|
||||
|
||||
// If we're attacking a human, we want it to be covert. We're not ATTACKING them, we're trying
|
||||
// to sneakily steal their accesses by swiping our agent ID card near them. As a result, we
|
||||
// return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN to cancel any part of the following the attack chain.
|
||||
if(ishuman(target))
|
||||
to_chat(user, "<span class='notice'>You covertly start to scan [target] with \the [src], hoping to pick up a wireless ID card signal...</span>")
|
||||
target.balloon_alert(user, "scanning ID card...")
|
||||
|
||||
if(!do_after(user, 2 SECONDS, target))
|
||||
to_chat(user, "<span class='notice'>The scan was interrupted.</span>")
|
||||
target.balloon_alert(user, "interrupted!")
|
||||
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
|
||||
|
||||
var/mob/living/carbon/human/human_target = target
|
||||
@@ -1365,11 +1364,11 @@
|
||||
var/list/target_id_cards = human_target.get_all_contents_type(/obj/item/card/id)
|
||||
|
||||
if(!length(target_id_cards))
|
||||
to_chat(user, "<span class='notice'>The scan failed to locate any ID cards.</span>")
|
||||
target.balloon_alert(user, "no IDs!")
|
||||
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
|
||||
|
||||
var/selected_id = pick(target_id_cards)
|
||||
to_chat(user, "<span class='notice'>You successfully sync your [src] with \the [selected_id].</span>")
|
||||
target.balloon_alert(user, UNLINT("IDs synced"))
|
||||
theft_target = WEAKREF(selected_id)
|
||||
ui_interact(user)
|
||||
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
|
||||
@@ -1377,7 +1376,7 @@
|
||||
if(isitem(target))
|
||||
var/obj/item/target_item = target
|
||||
|
||||
to_chat(user, "<span class='notice'>You covertly start to scan [target] with your [src], hoping to pick up a wireless ID card signal...</span>")
|
||||
target.balloon_alert(user, "scanning ID card...")
|
||||
|
||||
var/list/target_id_cards = target_item.get_all_contents_type(/obj/item/card/id)
|
||||
|
||||
@@ -1387,16 +1386,16 @@
|
||||
target_id_cards |= target_item_id
|
||||
|
||||
if(!length(target_id_cards))
|
||||
to_chat(user, "<span class='notice'>The scan failed to locate any ID cards.</span>")
|
||||
target.balloon_alert(user, "no IDs!")
|
||||
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
|
||||
|
||||
var/selected_id = pick(target_id_cards)
|
||||
to_chat(user, "<span class='notice'>You successfully sync your [src] with \the [selected_id].</span>")
|
||||
target.balloon_alert(user, UNLINT("IDs synced"))
|
||||
theft_target = WEAKREF(selected_id)
|
||||
ui_interact(user)
|
||||
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
|
||||
|
||||
return ..()
|
||||
return .
|
||||
|
||||
/obj/item/card/id/advanced/chameleon/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
|
||||
@@ -129,7 +129,7 @@
|
||||
inhand_icon_state = "syndicate_holster"
|
||||
worn_icon_state = "syndicate_holster"
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
var/datum/action/item_action/chameleon/change/chameleon_action
|
||||
actions_types = list(/datum/action/item_action/chameleon/change/belt)
|
||||
|
||||
/obj/item/storage/belt/holster/chameleon/Initialize(mapload)
|
||||
. = ..()
|
||||
@@ -155,22 +155,6 @@
|
||||
|
||||
atom_storage.silent = TRUE
|
||||
|
||||
chameleon_action = new(src)
|
||||
chameleon_action.chameleon_type = /obj/item/storage/belt
|
||||
chameleon_action.chameleon_name = "Belt"
|
||||
chameleon_action.initialize_disguises()
|
||||
add_item_action(chameleon_action)
|
||||
|
||||
/obj/item/storage/belt/holster/chameleon/emp_act(severity)
|
||||
. = ..()
|
||||
if(. & EMP_PROTECT_SELF)
|
||||
return
|
||||
chameleon_action.emp_randomise()
|
||||
|
||||
/obj/item/storage/belt/holster/chameleon/broken/Initialize(mapload)
|
||||
. = ..()
|
||||
chameleon_action.emp_randomise(INFINITY)
|
||||
|
||||
/obj/item/storage/belt/holster/nukie
|
||||
name = "operative holster"
|
||||
desc = "A deep shoulder holster capable of holding almost any form of firearm and its ammo."
|
||||
|
||||
@@ -504,6 +504,7 @@
|
||||
new /obj/item/stamp/chameleon(src)
|
||||
new /obj/item/modular_computer/pda/chameleon(src)
|
||||
new /obj/item/gun/energy/laser/chameleon(src)
|
||||
new /obj/item/chameleon_scanner(src)
|
||||
|
||||
//5*(2*4) = 5*8 = 45, 45 damage if you hit one person with all 5 stars.
|
||||
//Not counting the damage it will do while embedded (2*4 = 8, at 15% chance)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,222 @@
|
||||
/// Default duration of an EMP randomisation on a chameleon item
|
||||
#define EMP_RANDOMISE_TIME 30 SECONDS
|
||||
|
||||
/datum/action/item_action/chameleon
|
||||
|
||||
/datum/action/item_action/chameleon/change
|
||||
name = "Chameleon Change"
|
||||
check_flags = AB_CHECK_CONSCIOUS|AB_CHECK_INCAPACITATED|AB_CHECK_HANDS_BLOCKED
|
||||
/// Typecache of all item types we explicitly cannot pick
|
||||
/// Note that abstract items are already excluded
|
||||
VAR_FINAL/list/chameleon_blacklist = list()
|
||||
/// Typecache of typepaths we can turn into
|
||||
VAR_FINAL/list/chameleon_typecache
|
||||
/// Assoc list of item name + icon state to item typepath
|
||||
/// This is passed to the list input
|
||||
VAR_FINAL/list/chameleon_list
|
||||
/// The prime typepath of what class of item we're allowed to pick from
|
||||
var/chameleon_type
|
||||
/// Used in the action button to describe what we're changing into
|
||||
var/chameleon_name = "Item"
|
||||
/// What chameleon is active right now?
|
||||
/// Can be set in the declaration to update in init
|
||||
var/active_type
|
||||
/// Cooldown from when we started being EMP'd
|
||||
COOLDOWN_DECLARE(emp_timer)
|
||||
|
||||
/datum/action/item_action/chameleon/change/New(Target)
|
||||
. = ..()
|
||||
if(!isitem(target))
|
||||
stack_trace("Adding chameleon action to non-item ([target])")
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
initialize_blacklist()
|
||||
initialize_disguises()
|
||||
if(active_type)
|
||||
if(chameleon_blacklist[active_type])
|
||||
stack_trace("[type] has an active type defined in init which is blacklisted ([active_type])")
|
||||
active_type = null
|
||||
else
|
||||
update_look(active_type)
|
||||
|
||||
RegisterSignal(target, COMSIG_ATOM_EMP_ACT, PROC_REF(on_emp))
|
||||
|
||||
/datum/action/item_action/chameleon/change/Destroy()
|
||||
STOP_PROCESSING(SSprocessing, src)
|
||||
return ..()
|
||||
|
||||
/datum/action/item_action/chameleon/change/proc/on_emp(datum/source, severity, protection)
|
||||
SIGNAL_HANDLER
|
||||
if(protection & EMP_PROTECT_SELF)
|
||||
return
|
||||
emp_randomise()
|
||||
|
||||
/datum/action/item_action/chameleon/change/Grant(mob/grant_to)
|
||||
. = ..()
|
||||
if(isnull(owner))
|
||||
return
|
||||
|
||||
// Whenever a mob gains their first cham change action, they need to also gain the outfit action
|
||||
if(locate(/datum/action/chameleon_outfit) in grant_to.actions)
|
||||
return
|
||||
|
||||
var/datum/action/chameleon_outfit/outfit_action = new(owner)
|
||||
outfit_action.Grant(owner)
|
||||
|
||||
/datum/action/item_action/chameleon/change/Remove(mob/remove_from)
|
||||
. = ..()
|
||||
// Likewise when the mob loses the cham change action, if they have no others, they need to lose the outfit action
|
||||
if(locate(/datum/action/item_action/chameleon/change) in remove_from.actions)
|
||||
return
|
||||
|
||||
var/datum/action/chameleon_outfit/outfit_action = locate() in remove_from.actions
|
||||
QDEL_NULL(outfit_action)
|
||||
|
||||
/datum/action/item_action/chameleon/change/proc/initialize_blacklist()
|
||||
chameleon_blacklist |= typecacheof(target.type)
|
||||
|
||||
/datum/action/item_action/chameleon/change/proc/initialize_disguises()
|
||||
name = "Change [chameleon_name] Appearance"
|
||||
build_all_button_icons()
|
||||
|
||||
LAZYINITLIST(chameleon_typecache)
|
||||
LAZYINITLIST(chameleon_list)
|
||||
|
||||
if(!ispath(chameleon_type, /obj/item))
|
||||
stack_trace("Non-item chameleon type defined on [type] ([chameleon_type])")
|
||||
return
|
||||
|
||||
add_chameleon_items(chameleon_type)
|
||||
|
||||
/datum/action/item_action/chameleon/change/proc/add_chameleon_items(type_to_add)
|
||||
|
||||
chameleon_typecache |= typecacheof(type_to_add)
|
||||
for(var/obj/item/item_type as anything in chameleon_typecache)
|
||||
if(chameleon_blacklist[item_type] || (initial(item_type.item_flags) & ABSTRACT) || !initial(item_type.icon_state))
|
||||
continue
|
||||
var/chameleon_item_name = "[initial(item_type.name)] ([initial(item_type.icon_state)])"
|
||||
chameleon_list[chameleon_item_name] = item_type
|
||||
|
||||
|
||||
/datum/action/item_action/chameleon/change/proc/select_look(mob/user)
|
||||
var/picked_name = tgui_input_list(user, "Select [chameleon_name] to change into", "Chameleon Settings", sort_list(chameleon_list, GLOBAL_PROC_REF(cmp_typepaths_asc)))
|
||||
if(isnull(picked_name) || isnull(chameleon_list[picked_name]) || QDELETED(src) || QDELETED(user) || QDELETED(owner) || !IsAvailable(feedback = TRUE))
|
||||
return
|
||||
var/obj/item/picked_item = chameleon_list[picked_name]
|
||||
update_look(picked_item)
|
||||
|
||||
/datum/action/item_action/chameleon/change/proc/random_look()
|
||||
var/picked_name = pick(chameleon_list)
|
||||
update_look(chameleon_list[picked_name])
|
||||
|
||||
/datum/action/item_action/chameleon/change/proc/update_look(obj/item/picked_item)
|
||||
var/obj/item/chameleon_item = target
|
||||
|
||||
update_item(picked_item)
|
||||
build_all_button_icons()
|
||||
active_type = picked_item
|
||||
|
||||
if(ismob(chameleon_item.loc))
|
||||
var/mob/wearer = chameleon_item.loc
|
||||
wearer.update_clothing(chameleon_item.slot_flags | ITEM_SLOT_HANDS)
|
||||
|
||||
/datum/action/item_action/chameleon/change/proc/update_item(obj/item/picked_item)
|
||||
PROTECTED_PROC(TRUE) // Call update_look, not this!
|
||||
|
||||
var/atom/atom_target = target
|
||||
atom_target.name = initial(picked_item.name)
|
||||
atom_target.desc = initial(picked_item.desc)
|
||||
atom_target.icon_state = initial(picked_item.icon_state)
|
||||
|
||||
if(isitem(atom_target))
|
||||
var/obj/item/item_target = target
|
||||
item_target.worn_icon = initial(picked_item.worn_icon)
|
||||
item_target.lefthand_file = initial(picked_item.lefthand_file)
|
||||
item_target.righthand_file = initial(picked_item.righthand_file)
|
||||
|
||||
item_target.worn_icon_state = initial(picked_item.worn_icon_state)
|
||||
item_target.inhand_icon_state = initial(picked_item.inhand_icon_state)
|
||||
|
||||
if(initial(picked_item.greyscale_colors))
|
||||
if(initial(picked_item.greyscale_config_worn))
|
||||
item_target.worn_icon = SSgreyscale.GetColoredIconByType(
|
||||
initial(picked_item.greyscale_config_worn),
|
||||
initial(picked_item.greyscale_colors),
|
||||
)
|
||||
if(initial(picked_item.greyscale_config_inhand_left))
|
||||
item_target.lefthand_file = SSgreyscale.GetColoredIconByType(
|
||||
initial(picked_item.greyscale_config_inhand_left),
|
||||
initial(picked_item.greyscale_colors),
|
||||
)
|
||||
if(initial(picked_item.greyscale_config_inhand_right))
|
||||
item_target.righthand_file = SSgreyscale.GetColoredIconByType(
|
||||
initial(picked_item.greyscale_config_inhand_right),
|
||||
initial(picked_item.greyscale_colors),
|
||||
)
|
||||
|
||||
item_target.flags_inv = initial(picked_item.flags_inv)
|
||||
item_target.transparent_protection = initial(picked_item.transparent_protection)
|
||||
if(isclothing(item_target) && ispath(picked_item, /obj/item/clothing))
|
||||
var/obj/item/clothing/clothing_target = item_target
|
||||
var/obj/item/clothing/picked_clothing = picked_item
|
||||
clothing_target.flags_cover = initial(picked_clothing.flags_cover)
|
||||
|
||||
|
||||
if(initial(picked_item.greyscale_config) && initial(picked_item.greyscale_colors))
|
||||
atom_target.icon = SSgreyscale.GetColoredIconByType(
|
||||
initial(picked_item.greyscale_config),
|
||||
initial(picked_item.greyscale_colors),
|
||||
)
|
||||
|
||||
else
|
||||
atom_target.icon = initial(picked_item.icon)
|
||||
|
||||
/datum/action/item_action/chameleon/change/Trigger(trigger_flags)
|
||||
if(!IsAvailable(feedback = TRUE))
|
||||
return FALSE
|
||||
|
||||
select_look(owner)
|
||||
return TRUE
|
||||
|
||||
/datum/action/item_action/chameleon/change/proc/emp_randomise(amount = EMP_RANDOMISE_TIME)
|
||||
START_PROCESSING(SSprocessing, src)
|
||||
random_look()
|
||||
|
||||
COOLDOWN_START(src, emp_timer, amount)
|
||||
|
||||
/datum/action/item_action/chameleon/change/process()
|
||||
if(COOLDOWN_FINISHED(src, emp_timer))
|
||||
STOP_PROCESSING(SSprocessing, src)
|
||||
return
|
||||
random_look()
|
||||
|
||||
/datum/action/item_action/chameleon/change/proc/apply_outfit(datum/outfit/applying_from, list/all_items_to_apply)
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
|
||||
var/using_item_type
|
||||
for(var/item_type in all_items_to_apply)
|
||||
if(!ispath(item_type, /obj/item))
|
||||
stack_trace("Invalid item type passed to apply_outfit ([item_type])")
|
||||
continue
|
||||
if(chameleon_typecache[item_type])
|
||||
using_item_type = item_type
|
||||
break
|
||||
|
||||
if(isnull(using_item_type))
|
||||
return FALSE
|
||||
|
||||
if(istype(applying_from, /datum/outfit/job))
|
||||
var/datum/outfit/job/job_outfit = applying_from
|
||||
var/datum/job/job_datum = SSjob.GetJobType(job_outfit.jobtype)
|
||||
apply_job_data(job_datum)
|
||||
|
||||
update_look(using_item_type)
|
||||
all_items_to_apply -= using_item_type
|
||||
return TRUE
|
||||
|
||||
/// Used when applying this cham item via a job datum (from an outfit selection)
|
||||
/datum/action/item_action/chameleon/change/proc/apply_job_data(datum/job/job_datum)
|
||||
return
|
||||
|
||||
#undef EMP_RANDOMISE_TIME
|
||||
@@ -0,0 +1,119 @@
|
||||
/datum/action/chameleon_outfit
|
||||
name = "Select Chameleon Outfit"
|
||||
desc = "LMB: Select a job to update all of your chameleon items to.<br>\
|
||||
RMB: Save your current chameleon setup as a custom outfit."
|
||||
button_icon_state = "chameleon_outfit"
|
||||
check_flags = AB_CHECK_CONSCIOUS|AB_CHECK_INCAPACITATED|AB_CHECK_HANDS_BLOCKED
|
||||
|
||||
/// Determines when we're in use
|
||||
var/applying = FALSE
|
||||
/// Cached assoc list of job outfit datums by their names that we can select
|
||||
/// If you intend on editing this, ensure you are copying it first
|
||||
var/list/outfit_options
|
||||
/// Assoc list of custom outfit names ("Custom outfit 1", "Custom outfit 2", etc) to list of all item typepaths saved in that outfit
|
||||
var/list/custom_outfits
|
||||
/// Cap on how many custom outfits we can save
|
||||
var/max_custom_oufits = 10
|
||||
/// Used to generate the next custom outfit name, also to track which next to override if we've passed the limit
|
||||
var/next_custom_outfit = 1
|
||||
|
||||
/datum/action/chameleon_outfit/New()
|
||||
. = ..()
|
||||
outfit_options = get_initial_outfits()
|
||||
|
||||
/datum/action/chameleon_outfit/proc/get_initial_outfits()
|
||||
var/static/list/standard_outfit_options
|
||||
if(!standard_outfit_options)
|
||||
standard_outfit_options = list()
|
||||
for(var/datum/outfit/found_outfit as anything in subtypesof(/datum/outfit/job))
|
||||
standard_outfit_options[initial(found_outfit.name)] = new found_outfit()
|
||||
sortTim(standard_outfit_options, GLOBAL_PROC_REF(cmp_text_asc))
|
||||
|
||||
return standard_outfit_options
|
||||
|
||||
/datum/action/chameleon_outfit/Trigger(trigger_flags)
|
||||
. = ..()
|
||||
if(!. || applying)
|
||||
return
|
||||
if(usr != owner) // i guess
|
||||
return
|
||||
applying = TRUE
|
||||
|
||||
if(trigger_flags & TRIGGER_SECONDARY_ACTION)
|
||||
. = save_current_outfit(usr)
|
||||
else
|
||||
. = select_outfit(usr)
|
||||
|
||||
applying = FALSE
|
||||
return .
|
||||
|
||||
/datum/action/chameleon_outfit/proc/save_current_outfit(mob/user)
|
||||
var/list/saved_paths = list()
|
||||
for(var/datum/action/item_action/chameleon/change/change_action in owner.actions)
|
||||
if(change_action.active_type)
|
||||
saved_paths |= change_action.active_type
|
||||
|
||||
save_outfit(user, saved_paths)
|
||||
|
||||
/datum/action/chameleon_outfit/proc/save_outfit(mob/user, list/saved_paths)
|
||||
if(!length(saved_paths))
|
||||
owner.balloon_alert(user, "no outfit saved!")
|
||||
return
|
||||
|
||||
for(var/existing_outfit in custom_outfits)
|
||||
if(custom_outfits[existing_outfit] ~= saved_paths)
|
||||
owner.balloon_alert(user, "outfit already saved!")
|
||||
return
|
||||
|
||||
if(next_custom_outfit > max_custom_oufits)
|
||||
next_custom_outfit = 1
|
||||
|
||||
LAZYSET(custom_outfits, "Custom Outfit #[next_custom_outfit]", saved_paths)
|
||||
owner.balloon_alert(user, "outfit saved in slot [next_custom_outfit]")
|
||||
next_custom_outfit += 1
|
||||
|
||||
/datum/action/chameleon_outfit/proc/select_outfit(mob/user)
|
||||
var/list/all_options = list()
|
||||
if(LAZYLEN(custom_outfits))
|
||||
all_options += "--- Custom outfits ---"
|
||||
all_options += custom_outfits
|
||||
all_options += "--- Job outfits ---"
|
||||
all_options += outfit_options
|
||||
|
||||
var/selected = tgui_input_list(user, "Select outfit to change into", "Chameleon Outfit", all_options)
|
||||
if(isnull(selected) || QDELETED(src) || QDELETED(user) || QDELETED(owner) || !IsAvailable(feedback = TRUE))
|
||||
return FALSE
|
||||
|
||||
var/selected_outfit = all_options[selected]
|
||||
if(islist(selected_outfit))
|
||||
var/list/selected_custom_outfit = selected_outfit
|
||||
var/datum/outfit/empty_outfit = new()
|
||||
apply_outfit(empty_outfit, selected_custom_outfit.Copy())
|
||||
qdel(empty_outfit)
|
||||
return TRUE
|
||||
|
||||
if(istype(selected_outfit, /datum/outfit))
|
||||
apply_outfit(selected_outfit)
|
||||
return TRUE
|
||||
|
||||
return FALSE
|
||||
|
||||
/**
|
||||
* Applies the given outfit to all chameleon actions the owner has
|
||||
*
|
||||
* * outfit - what outfit to apply
|
||||
* * outfit_types - optinal, list of typepaths to apply. If null, defaults to all items in the passed outfit. This list is mutated!
|
||||
*/
|
||||
/datum/action/chameleon_outfit/proc/apply_outfit(datum/outfit/outfit, list/outfit_types)
|
||||
if(isnull(outfit_types))
|
||||
outfit_types = outfit.get_chameleon_disguise_info()
|
||||
|
||||
for(var/datum/action/item_action/chameleon/change/change_action in owner.actions)
|
||||
change_action.apply_outfit(outfit, outfit_types)
|
||||
if(!isitem(change_action.target))
|
||||
continue
|
||||
var/obj/item/cham_item = change_action.target
|
||||
if(!cham_item.slot_flags)
|
||||
continue
|
||||
// Clothing items get a small delay to create a visual effect and also introduce some counterplay and also distinguish from lings
|
||||
stoplag(0.1 SECONDS)
|
||||
@@ -0,0 +1,299 @@
|
||||
/datum/action/item_action/chameleon/change/neck
|
||||
chameleon_type = /obj/item/clothing/neck
|
||||
chameleon_name = "Neck Accessory"
|
||||
active_type = /obj/item/clothing/neck/tie/black
|
||||
|
||||
/datum/action/item_action/chameleon/change/neck/initialize_blacklist()
|
||||
. = ..()
|
||||
chameleon_blacklist |= typecacheof(/obj/item/clothing/neck/cloak/skill_reward)
|
||||
|
||||
/datum/action/item_action/chameleon/change/stamp
|
||||
chameleon_type = /obj/item/stamp
|
||||
chameleon_name = "Stamp"
|
||||
|
||||
/datum/action/item_action/chameleon/change/tablet
|
||||
chameleon_type = /obj/item/modular_computer/pda
|
||||
chameleon_name = "Tablet"
|
||||
|
||||
/datum/action/item_action/chameleon/change/tablet/initialize_blacklist()
|
||||
. = ..()
|
||||
chameleon_blacklist |= typecacheof(list(/obj/item/modular_computer/pda/heads), only_root_path = TRUE)
|
||||
|
||||
/datum/action/item_action/chameleon/change/tablet/update_item(obj/item/picked_item)
|
||||
..()
|
||||
var/obj/item/modular_computer/pda/agent_pda = target
|
||||
if(istype(agent_pda))
|
||||
agent_pda.update_appearance()
|
||||
|
||||
/datum/action/item_action/chameleon/change/tablet/apply_job_data(datum/job/job_datum)
|
||||
..()
|
||||
var/obj/item/modular_computer/pda/agent_pda = target
|
||||
if(istype(agent_pda) && istype(job_datum))
|
||||
agent_pda.saved_job = job_datum.title
|
||||
|
||||
/datum/action/item_action/chameleon/change/headset
|
||||
chameleon_type = /obj/item/radio/headset
|
||||
chameleon_name = "Headset"
|
||||
active_type = /obj/item/radio/headset
|
||||
|
||||
/datum/action/item_action/chameleon/change/belt
|
||||
chameleon_type = /obj/item/storage/belt
|
||||
chameleon_name = "Belt"
|
||||
active_type = /obj/item/storage/belt/utility
|
||||
|
||||
/datum/action/item_action/chameleon/change/backpack
|
||||
chameleon_type = /obj/item/storage/backpack
|
||||
chameleon_name = "Backpack"
|
||||
active_type = /obj/item/storage/backpack
|
||||
|
||||
/datum/action/item_action/chameleon/change/shoes
|
||||
chameleon_type = /obj/item/clothing/shoes
|
||||
chameleon_name = "Shoes"
|
||||
active_type = /obj/item/clothing/shoes/sneakers/black
|
||||
|
||||
/datum/action/item_action/chameleon/change/shoes/initialize_blacklist()
|
||||
. = ..()
|
||||
chameleon_blacklist |= typecacheof(/obj/item/clothing/shoes/changeling, only_root_path = TRUE)
|
||||
|
||||
/datum/action/item_action/chameleon/change/mask
|
||||
chameleon_type = /obj/item/clothing/mask
|
||||
chameleon_name = "Mask"
|
||||
active_type = /obj/item/clothing/mask/gas
|
||||
|
||||
/datum/action/item_action/chameleon/change/mask/initialize_blacklist()
|
||||
. = ..()
|
||||
chameleon_blacklist |= typecacheof(/obj/item/clothing/mask/changeling, only_root_path = TRUE)
|
||||
|
||||
/datum/action/item_action/chameleon/change/hat
|
||||
chameleon_type = /obj/item/clothing/head
|
||||
chameleon_name = "Hat"
|
||||
active_type = /obj/item/clothing/head/soft/black
|
||||
|
||||
/datum/action/item_action/chameleon/change/hat/initialize_blacklist()
|
||||
. = ..()
|
||||
chameleon_blacklist |= typecacheof(/obj/item/clothing/head/changeling, only_root_path = TRUE)
|
||||
|
||||
/datum/action/item_action/chameleon/change/gloves
|
||||
chameleon_type = /obj/item/clothing/gloves
|
||||
chameleon_name = "Gloves"
|
||||
active_type = /obj/item/clothing/gloves/color/yellow
|
||||
|
||||
/datum/action/item_action/chameleon/change/gloves/initialize_blacklist()
|
||||
. = ..()
|
||||
chameleon_blacklist |= typecacheof(list(/obj/item/clothing/gloves, /obj/item/clothing/gloves/color, /obj/item/clothing/gloves/changeling), only_root_path = TRUE)
|
||||
|
||||
/datum/action/item_action/chameleon/change/glasses
|
||||
chameleon_type = /obj/item/clothing/glasses
|
||||
chameleon_name = "Glasses"
|
||||
active_type = /obj/item/clothing/glasses/meson
|
||||
|
||||
/datum/action/item_action/chameleon/change/glasses/initialize_blacklist()
|
||||
. = ..()
|
||||
chameleon_blacklist |= typecacheof(list(/obj/item/clothing/glasses/changeling, /obj/item/clothing/glasses/hud/security/chameleon, /obj/item/clothing/glasses/thermal/syndi), only_root_path = TRUE)
|
||||
|
||||
/datum/action/item_action/chameleon/change/glasses/no_preset
|
||||
active_type = null
|
||||
|
||||
/datum/action/item_action/chameleon/change/suit
|
||||
chameleon_type = /obj/item/clothing/suit
|
||||
chameleon_name = "Suit"
|
||||
active_type = /obj/item/clothing/suit/armor/vest
|
||||
|
||||
/datum/action/item_action/chameleon/change/suit/initialize_blacklist()
|
||||
. = ..()
|
||||
chameleon_blacklist |= typecacheof(list(/obj/item/clothing/suit/armor/abductor, /obj/item/clothing/suit/changeling), only_root_path = TRUE)
|
||||
|
||||
/datum/action/item_action/chameleon/change/suit/apply_outfit(mob/user, datum/outfit/applying_from, list/all_items_to_apply)
|
||||
. = ..()
|
||||
if(!. || !ispath(applying_from.suit, /obj/item/clothing/suit/hooded))
|
||||
return
|
||||
// If we're appling a hooded suit, and wearing a cham hat, make it a hood
|
||||
var/obj/item/clothing/suit/hooded/hooded = applying_from.suit
|
||||
var/datum/action/item_action/chameleon/change/hat/hood_action = locate() in user.actions
|
||||
hood_action?.update_look(user, initial(hooded.hoodtype))
|
||||
|
||||
/datum/action/item_action/chameleon/change/jumpsuit
|
||||
chameleon_type = /obj/item/clothing/under
|
||||
chameleon_name = "Jumpsuit"
|
||||
active_type = /obj/item/clothing/under/color/black
|
||||
|
||||
/datum/action/item_action/chameleon/change/jumpsuit/initialize_blacklist()
|
||||
. = ..()
|
||||
chameleon_blacklist |= typecacheof(list(/obj/item/clothing/under, /obj/item/clothing/under/color, /obj/item/clothing/under/rank, /obj/item/clothing/under/changeling), only_root_path = TRUE)
|
||||
|
||||
/datum/action/item_action/chameleon/change/id
|
||||
chameleon_type = /obj/item/card/id/advanced
|
||||
chameleon_name = "ID Card"
|
||||
|
||||
/datum/action/item_action/chameleon/change/id/New(Target)
|
||||
. = ..()
|
||||
if(!istype(target, /obj/item/card/id/advanced/chameleon))
|
||||
stack_trace("Adding chameleon ID action to non-chameleon id ([target])")
|
||||
qdel(src)
|
||||
|
||||
/datum/action/item_action/chameleon/change/id/update_item(obj/item/picked_item)
|
||||
. = ..()
|
||||
var/obj/item/card/id/advanced/chameleon/agent_card = target
|
||||
var/obj/item/card/id/copied_card = picked_item
|
||||
|
||||
// If the outfit comes with a special trim override, we'll steal some stuff from that.
|
||||
var/new_trim = initial(copied_card.trim)
|
||||
|
||||
if(new_trim)
|
||||
SSid_access.apply_trim_to_chameleon_card(agent_card, new_trim, TRUE)
|
||||
|
||||
// If the ID card hasn't been forged, we'll check if there has been an assignment set already by any new trim.
|
||||
// If there has not, we set the assignment to the copied card's default as well as copying over the the
|
||||
// default registered name from the copied card.
|
||||
if(!agent_card.forged)
|
||||
if(!agent_card.assignment)
|
||||
agent_card.assignment = initial(copied_card.assignment)
|
||||
|
||||
agent_card.registered_name = initial(copied_card.registered_name)
|
||||
|
||||
agent_card.icon_state = initial(copied_card.icon_state)
|
||||
if(ispath(copied_card, /obj/item/card/id/advanced))
|
||||
var/obj/item/card/id/advanced/copied_advanced_card = copied_card
|
||||
agent_card.assigned_icon_state = initial(copied_advanced_card.assigned_icon_state)
|
||||
|
||||
agent_card.update_label()
|
||||
agent_card.update_appearance(UPDATE_ICON)
|
||||
|
||||
/datum/action/item_action/chameleon/change/id/apply_job_data(datum/job/job_datum)
|
||||
var/obj/item/card/id/advanced/chameleon/agent_card = target
|
||||
agent_card.forged = TRUE
|
||||
|
||||
// job_outfit is going to be a path.
|
||||
var/datum/outfit/job/job_outfit = job_datum.outfit
|
||||
if(isnull(job_outfit))
|
||||
return
|
||||
|
||||
// copied_card is also going to be a path.
|
||||
var/obj/item/card/id/copied_card = initial(job_outfit.id)
|
||||
if(isnull(copied_card))
|
||||
return
|
||||
|
||||
// If the outfit comes with a special trim override, we'll use that. Otherwise, use the card's default trim. Failing that, no trim at all.
|
||||
var/new_trim = initial(job_outfit.id_trim) ? initial(job_outfit.id_trim) : initial(copied_card.trim)
|
||||
|
||||
if(new_trim)
|
||||
SSid_access.apply_trim_to_chameleon_card(agent_card, new_trim, FALSE)
|
||||
else
|
||||
agent_card.assignment = job_datum.title
|
||||
|
||||
agent_card.icon_state = initial(copied_card.icon_state)
|
||||
if(ispath(copied_card, /obj/item/card/id/advanced))
|
||||
var/obj/item/card/id/advanced/copied_advanced_card = copied_card
|
||||
agent_card.assigned_icon_state = initial(copied_advanced_card.assigned_icon_state)
|
||||
|
||||
agent_card.update_label()
|
||||
agent_card.update_appearance(UPDATE_ICON)
|
||||
|
||||
/datum/action/item_action/chameleon/change/id_trim
|
||||
chameleon_type = /datum/id_trim
|
||||
chameleon_name = "ID Trim"
|
||||
|
||||
/datum/action/item_action/chameleon/change/id_trim/New(Target)
|
||||
. = ..()
|
||||
if(!istype(target, /obj/item/card/id/advanced/chameleon))
|
||||
stack_trace("Adding chameleon ID trim action to non-chameleon id ([target])")
|
||||
qdel(src)
|
||||
|
||||
/datum/action/item_action/chameleon/change/id_trim/initialize_blacklist()
|
||||
return
|
||||
|
||||
/datum/action/item_action/chameleon/change/id_trim/initialize_disguises()
|
||||
// Little bit of copypasta but we only use trim datums rather than item paths
|
||||
name = "Change [chameleon_name] Appearance"
|
||||
build_all_button_icons()
|
||||
|
||||
LAZYINITLIST(chameleon_typecache)
|
||||
LAZYINITLIST(chameleon_list)
|
||||
|
||||
for(var/datum/id_trim/trim_path as anything in typesof(/datum/id_trim))
|
||||
if(chameleon_blacklist[trim_path])
|
||||
continue
|
||||
|
||||
var/datum/id_trim/trim = SSid_access.trim_singletons_by_path[trim_path]
|
||||
|
||||
if(trim && trim.trim_state && trim.assignment)
|
||||
var/chameleon_item_name = "[trim.assignment] ([trim.trim_state])"
|
||||
chameleon_list[chameleon_item_name] = trim_path
|
||||
chameleon_typecache[trim_path] = TRUE
|
||||
|
||||
/datum/action/item_action/chameleon/change/id_trim/update_item(picked_trim_path)
|
||||
var/obj/item/card/id/advanced/chameleon/agent_card = target
|
||||
|
||||
SSid_access.apply_trim_to_chameleon_card(agent_card, picked_trim_path, TRUE)
|
||||
|
||||
agent_card.update_label()
|
||||
agent_card.update_appearance(UPDATE_ICON)
|
||||
|
||||
/datum/action/item_action/chameleon/change/gun
|
||||
chameleon_type = /obj/item/gun
|
||||
chameleon_name = "Gun"
|
||||
|
||||
/datum/action/item_action/chameleon/change/gun/New(Target)
|
||||
. = ..()
|
||||
if(!istype(target, /obj/item/gun/energy/laser/chameleon))
|
||||
stack_trace("Adding chameleon gun action to non-chameleon gun ([target])")
|
||||
qdel(src)
|
||||
|
||||
/datum/action/item_action/chameleon/change/gun/initialize_blacklist()
|
||||
. = ..()
|
||||
chameleon_blacklist |= typecacheof(/obj/item/gun/energy/minigun)
|
||||
|
||||
/datum/action/item_action/chameleon/change/gun/update_look(obj/item/picked_item)
|
||||
var/obj/item/gun/energy/laser/chameleon/chameleon_gun = target
|
||||
chameleon_gun.set_chameleon_disguise(picked_item)
|
||||
return ..()
|
||||
|
||||
/datum/action/item_action/chameleon/change/scanner
|
||||
chameleon_type = /obj/item/pen
|
||||
chameleon_name = "Chameleon Scanner"
|
||||
active_type = /obj/item/storage/fancy/cigarettes/cigpack_robustgold
|
||||
/// Other types the chameleon scanner can swap into in addition to the chameleon type
|
||||
var/static/list/other_cham_types = list(
|
||||
/obj/item/analyzer,
|
||||
/obj/item/assembly/flash/handheld,
|
||||
/obj/item/assembly/signaler,
|
||||
/obj/item/autopsy_scanner,
|
||||
/obj/item/camera,
|
||||
/obj/item/cane,
|
||||
/obj/item/detective_scanner,
|
||||
/obj/item/door_remote,
|
||||
/obj/item/flashlight,
|
||||
/obj/item/geiger_counter,
|
||||
/obj/item/healthanalyzer,
|
||||
/obj/item/mining_scanner,
|
||||
/obj/item/multitool,
|
||||
/obj/item/plant_analyzer,
|
||||
/obj/item/sensor_device,
|
||||
/obj/item/storage/fancy/cigarettes, // Fanservice
|
||||
/obj/item/taperecorder,
|
||||
/obj/item/toy/crayon,
|
||||
)
|
||||
// (Other ideas include: GPSs, PDAs, station bounced radios, holosign creators, etc. But this is good enough)
|
||||
|
||||
/datum/action/item_action/chameleon/change/scanner/initialize_blacklist()
|
||||
. = ..()
|
||||
chameleon_blacklist |= typecacheof(list(
|
||||
/obj/item/assembly/signaler/anomaly,
|
||||
/obj/item/camera/siliconcam,
|
||||
/obj/item/door_remote/omni,
|
||||
/obj/item/flashlight/emp/debug,
|
||||
/obj/item/flashlight/eyelight/adapted,
|
||||
/obj/item/flashlight/flare,
|
||||
/obj/item/flashlight/lamp,
|
||||
/obj/item/healthanalyzer/rad_laser,
|
||||
/obj/item/multitool/ai_detect,
|
||||
/obj/item/multitool/cyborg,
|
||||
/obj/item/multitool/drone,
|
||||
/obj/item/multitool/field_debug,
|
||||
/obj/item/storage/fancy/cigarettes/cigars,
|
||||
))
|
||||
|
||||
/datum/action/item_action/chameleon/change/scanner/initialize_disguises()
|
||||
. = ..()
|
||||
for(var/other_type in other_cham_types)
|
||||
add_chameleon_items(other_type)
|
||||
@@ -0,0 +1,52 @@
|
||||
/datum/action/item_action/chameleon/drone/randomise
|
||||
name = "Randomise Headgear"
|
||||
button_icon = 'icons/mob/actions/actions_items.dmi'
|
||||
button_icon_state = "random"
|
||||
|
||||
/datum/action/item_action/chameleon/drone/randomise/Trigger(trigger_flags)
|
||||
if(!IsAvailable(feedback = TRUE))
|
||||
return FALSE
|
||||
|
||||
for(var/datum/action/item_action/chameleon/change/to_randomize in owner.actions)
|
||||
to_randomize.random_look()
|
||||
return TRUE
|
||||
|
||||
// Allows a drone to turn their hat into a mask
|
||||
// This action's existence is very silly can be replaced with just, a hat with a chameleon action that can be both hats and masks.
|
||||
/datum/action/item_action/chameleon/drone/togglehatmask
|
||||
name = "Toggle Headgear Mode"
|
||||
button_icon = 'icons/mob/actions/actions_silicon.dmi'
|
||||
button_icon_state = "drone_camogear_helm"
|
||||
|
||||
/datum/action/item_action/chameleon/drone/togglehatmask/New(Target)
|
||||
if (istype(Target, /obj/item/clothing/head/chameleon/drone))
|
||||
button_icon_state = "drone_camogear_helm"
|
||||
if (istype(Target, /obj/item/clothing/mask/chameleon/drone))
|
||||
button_icon_state = "drone_camogear_mask"
|
||||
return ..()
|
||||
|
||||
/datum/action/item_action/chameleon/drone/togglehatmask/IsAvailable(feedback)
|
||||
return ..() && isdrone(owner)
|
||||
|
||||
/datum/action/item_action/chameleon/drone/togglehatmask/Trigger(trigger_flags)
|
||||
if(!IsAvailable(feedback = TRUE))
|
||||
return FALSE
|
||||
|
||||
var/mob/living/simple_animal/drone/droney = owner
|
||||
|
||||
// The drone unEquip() proc sets head to null after dropping
|
||||
// an item, so we need to keep a reference to our old headgear
|
||||
// to make sure it's deleted.
|
||||
var/obj/old_headgear = target
|
||||
var/obj/new_headgear
|
||||
|
||||
if(istype(old_headgear, /obj/item/clothing/head/chameleon/drone))
|
||||
new_headgear = new /obj/item/clothing/mask/chameleon/drone(droney)
|
||||
else if(istype(old_headgear, /obj/item/clothing/mask/chameleon/drone))
|
||||
new_headgear = new /obj/item/clothing/head/chameleon/drone(droney)
|
||||
else
|
||||
to_chat(owner, span_warning("You shouldn't be able to toggle a camogear helmetmask if you're not wearing it."))
|
||||
return FALSE
|
||||
droney.dropItemToGround(target, force = TRUE)
|
||||
droney.equip_to_slot_or_del(new_headgear, ITEM_SLOT_HEAD)
|
||||
return TRUE
|
||||
@@ -0,0 +1,157 @@
|
||||
|
||||
/obj/item/gun/energy/laser/chameleon
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/chameleon)
|
||||
pin = /obj/item/firing_pin
|
||||
automatic_charge_overlays = FALSE
|
||||
can_select = FALSE
|
||||
actions_types = list(/datum/action/item_action/chameleon/change/gun)
|
||||
|
||||
/// The vars copied over to our projectile on fire.
|
||||
var/list/chameleon_projectile_vars
|
||||
|
||||
/// The badmin mode. Makes your projectiles act like the real deal.
|
||||
var/real_hits = FALSE
|
||||
|
||||
/obj/item/gun/energy/laser/chameleon/Initialize(mapload)
|
||||
. = ..()
|
||||
recharge_newshot()
|
||||
AddElement(/datum/element/empprotection, EMP_PROTECT_SELF|EMP_PROTECT_CONTENTS)
|
||||
// Init order shenanigans dictate we have to do this last so we can't just use `active_type`
|
||||
var/datum/action/item_action/chameleon/change/gun/gun_action = locate() in actions
|
||||
gun_action?.update_look(/obj/item/gun/energy/laser)
|
||||
|
||||
/**
|
||||
* Description: Resets the currently loaded chameleon variables, essentially resetting it to brand new.
|
||||
* Arguments: []
|
||||
*/
|
||||
/obj/item/gun/energy/laser/chameleon/proc/reset_chameleon_vars()
|
||||
chameleon_projectile_vars = list()
|
||||
|
||||
if(chambered)
|
||||
chambered.firing_effect_type = initial(chambered.firing_effect_type)
|
||||
|
||||
fire_sound = initial(fire_sound)
|
||||
burst_size = initial(burst_size)
|
||||
fire_delay = initial(fire_delay)
|
||||
inhand_x_dimension = initial(inhand_x_dimension)
|
||||
inhand_y_dimension = initial(inhand_y_dimension)
|
||||
|
||||
QDEL_NULL(chambered.loaded_projectile)
|
||||
chambered.newshot()
|
||||
|
||||
/**
|
||||
* Description: Sets what gun we should be mimicking.
|
||||
* Arguments: [obj/item/gun/gun_to_set (the gun we're trying to mimic)]
|
||||
*/
|
||||
/obj/item/gun/energy/laser/chameleon/proc/set_chameleon_gun(obj/item/gun/gun_to_set)
|
||||
if(!istype(gun_to_set))
|
||||
stack_trace("[gun_to_set] is not a valid gun.")
|
||||
return FALSE
|
||||
|
||||
fire_sound = gun_to_set.fire_sound
|
||||
burst_size = gun_to_set.burst_size
|
||||
fire_delay = gun_to_set.fire_delay
|
||||
inhand_x_dimension = gun_to_set.inhand_x_dimension
|
||||
inhand_y_dimension = gun_to_set.inhand_y_dimension
|
||||
|
||||
if(istype(gun_to_set, /obj/item/gun/ballistic))
|
||||
var/obj/item/gun/ballistic/ball_gun = gun_to_set
|
||||
var/obj/item/ammo_box/ball_ammo = new ball_gun.spawn_magazine_type(gun_to_set)
|
||||
qdel(ball_gun)
|
||||
|
||||
if(!istype(ball_ammo) || !ball_ammo.ammo_type)
|
||||
qdel(ball_ammo)
|
||||
return FALSE
|
||||
|
||||
var/obj/item/ammo_casing/ball_cartridge = new ball_ammo.ammo_type(gun_to_set)
|
||||
set_chameleon_ammo(ball_cartridge)
|
||||
|
||||
else if(istype(gun_to_set, /obj/item/gun/magic))
|
||||
var/obj/item/gun/magic/magic_gun = gun_to_set
|
||||
var/obj/item/ammo_casing/magic_cartridge = new magic_gun.ammo_type(gun_to_set)
|
||||
set_chameleon_ammo(magic_cartridge)
|
||||
|
||||
else if(istype(gun_to_set, /obj/item/gun/energy))
|
||||
var/obj/item/gun/energy/energy_gun = gun_to_set
|
||||
if(islist(energy_gun.ammo_type) && energy_gun.ammo_type.len)
|
||||
var/obj/item/ammo_casing/energy_cartridge = energy_gun.ammo_type[1]
|
||||
set_chameleon_ammo(energy_cartridge)
|
||||
|
||||
else if(istype(gun_to_set, /obj/item/gun/syringe))
|
||||
var/obj/item/ammo_casing/syringe_cartridge = new /obj/item/ammo_casing/syringegun(src)
|
||||
set_chameleon_ammo(syringe_cartridge)
|
||||
|
||||
else
|
||||
var/obj/item/ammo_casing/default_cartridge = new /obj/item/ammo_casing(src)
|
||||
set_chameleon_ammo(default_cartridge)
|
||||
|
||||
/**
|
||||
* Description: Sets the ammo type our gun should have.
|
||||
* Arguments: [obj/item/ammo_casing/cartridge (the ammo_casing we're trying to copy)]
|
||||
*/
|
||||
/obj/item/gun/energy/laser/chameleon/proc/set_chameleon_ammo(obj/item/ammo_casing/cartridge)
|
||||
if(!istype(cartridge))
|
||||
stack_trace("[cartridge] is not a valid ammo casing.")
|
||||
return FALSE
|
||||
|
||||
var/obj/projectile/projectile = cartridge.loaded_projectile
|
||||
set_chameleon_projectile(projectile)
|
||||
|
||||
/**
|
||||
* Description: Sets the current projectile variables for our chameleon gun.
|
||||
* Arguments: [obj/projectile/template_projectile (the projectile we're trying to copy)]
|
||||
*/
|
||||
/obj/item/gun/energy/laser/chameleon/proc/set_chameleon_projectile(obj/projectile/template_projectile)
|
||||
if(!istype(template_projectile))
|
||||
stack_trace("[template_projectile] is not a valid projectile.")
|
||||
return FALSE
|
||||
|
||||
chameleon_projectile_vars = list("name" = "practice laser", "icon" = 'icons/obj/weapons/guns/projectiles.dmi', "icon_state" = "laser")
|
||||
|
||||
var/default_state = isnull(template_projectile.icon_state) ? "laser" : template_projectile.icon_state
|
||||
|
||||
chameleon_projectile_vars["name"] = template_projectile.name
|
||||
chameleon_projectile_vars["icon"] = template_projectile.icon
|
||||
chameleon_projectile_vars["icon_state"] = default_state
|
||||
chameleon_projectile_vars["speed"] = template_projectile.speed
|
||||
chameleon_projectile_vars["color"] = template_projectile.color
|
||||
chameleon_projectile_vars["hitsound"] = template_projectile.hitsound
|
||||
chameleon_projectile_vars["impact_effect_type"] = template_projectile.impact_effect_type
|
||||
chameleon_projectile_vars["range"] = template_projectile.range
|
||||
chameleon_projectile_vars["suppressed"] = template_projectile.suppressed
|
||||
chameleon_projectile_vars["hitsound_wall"] = template_projectile.hitsound_wall
|
||||
chameleon_projectile_vars["pass_flags"] = template_projectile.pass_flags
|
||||
|
||||
if(istype(chambered, /obj/item/ammo_casing/energy/chameleon))
|
||||
var/obj/item/ammo_casing/energy/chameleon/cartridge = chambered
|
||||
|
||||
cartridge.loaded_projectile.name = template_projectile.name
|
||||
cartridge.loaded_projectile.icon = template_projectile.icon
|
||||
cartridge.loaded_projectile.icon_state = default_state
|
||||
cartridge.loaded_projectile.speed = template_projectile.speed
|
||||
cartridge.loaded_projectile.color = template_projectile.color
|
||||
cartridge.loaded_projectile.hitsound = template_projectile.hitsound
|
||||
cartridge.loaded_projectile.impact_effect_type = template_projectile.impact_effect_type
|
||||
cartridge.loaded_projectile.range = template_projectile.range
|
||||
cartridge.loaded_projectile.suppressed = template_projectile.suppressed
|
||||
cartridge.loaded_projectile.hitsound_wall = template_projectile.hitsound_wall
|
||||
cartridge.loaded_projectile.pass_flags = template_projectile.pass_flags
|
||||
|
||||
cartridge.projectile_vars = chameleon_projectile_vars.Copy()
|
||||
|
||||
if(real_hits)
|
||||
qdel(chambered.loaded_projectile)
|
||||
chambered.projectile_type = template_projectile.type
|
||||
|
||||
qdel(template_projectile)
|
||||
|
||||
|
||||
/**
|
||||
* Description: Resets our chameleon variables, then resets the entire gun to mimic the given guntype.
|
||||
* Arguments: [guntype (the gun we're copying, pathtyped to obj/item/gun)]
|
||||
*/
|
||||
/obj/item/gun/energy/laser/chameleon/proc/set_chameleon_disguise(guntype)
|
||||
reset_chameleon_vars()
|
||||
var/obj/item/gun/new_gun = new guntype(src)
|
||||
set_chameleon_gun(new_gun)
|
||||
qdel(new_gun)
|
||||
@@ -0,0 +1,116 @@
|
||||
/// Small handheld chameleon item that allows a user to mimic the outfit of another person quickly.
|
||||
/obj/item/chameleon_scanner
|
||||
// No name or desc by default, set up by the cham action
|
||||
flags_1 = CONDUCT_1
|
||||
item_flags = NOBLUDGEON
|
||||
slot_flags = ITEM_SLOT_BELT
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
actions_types = list(/datum/action/item_action/chameleon/change/scanner)
|
||||
throw_speed = 3
|
||||
/// Range that we can scan people
|
||||
var/scan_range = 5
|
||||
/// Cooldown between scans.
|
||||
/// Not entirely intended to be a balance knob, but rather intended to prevent accidentally scanning the same person in quick succession.
|
||||
COOLDOWN_DECLARE(scan_cooldown)
|
||||
|
||||
/obj/item/chameleon_scanner/Initialize(mapload)
|
||||
. = ..()
|
||||
register_item_context()
|
||||
|
||||
/obj/item/chameleon_scanner/add_item_context(
|
||||
obj/item/source,
|
||||
list/context,
|
||||
atom/target,
|
||||
mob/living/user,
|
||||
)
|
||||
if(ishuman(target) && IS_TRAITOR(user))
|
||||
// probably don't want to give out the context to any old crewmember -
|
||||
// even though anyone can use it, it kind of reveals the fact that it's disguised
|
||||
// (though it's trivial to find that out anyway)
|
||||
context[SCREENTIP_CONTEXT_LMB] = "Scan target outfit"
|
||||
context[SCREENTIP_CONTEXT_RMB] = "Scan target outfit and equip"
|
||||
return CONTEXTUAL_SCREENTIP_SET
|
||||
|
||||
return NONE
|
||||
|
||||
/obj/item/chameleon_scanner/examine(mob/user)
|
||||
. = ..()
|
||||
if(!IS_TRAITOR(user))
|
||||
return
|
||||
// similar to context, we don't want a bunch of text revealing "THIS IS A DISGUISED ITEM" to everyone on examine.
|
||||
// despite the fact that anyone can use it, we'll only show it to traitors, everyone else just has to figure it out.
|
||||
. += span_red("There's a small button on the bottom side of it. You recognize this as a hidden <i>Chameleon Scanner 6000</i>.")
|
||||
. += span_red("<b>Left click</b> will stealthily scan a target up to [scan_range] meters away and upload their getup as a custom outfit for you to use.")
|
||||
. += span_red("<b>Right click</b> will do the same, but instantly equip the outfit you obtain.")
|
||||
|
||||
/obj/item/chameleon_scanner/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
|
||||
. = ..()
|
||||
if(scan_target(target, user))
|
||||
. |= AFTERATTACK_PROCESSED_ITEM
|
||||
return .
|
||||
|
||||
/obj/item/chameleon_scanner/afterattack_secondary(atom/target, mob/user, proximity_flag, click_parameters)
|
||||
. = ..()
|
||||
if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
|
||||
return .
|
||||
|
||||
var/list/scanned_outfit = scan_target(target, user)
|
||||
if(length(scanned_outfit))
|
||||
var/datum/outfit/empty_outfit = new()
|
||||
var/datum/action/chameleon_outfit/outfit_action = locate() in user.actions
|
||||
outfit_action?.apply_outfit(empty_outfit, scanned_outfit.Copy())
|
||||
qdel(empty_outfit)
|
||||
|
||||
return SECONDARY_ATTACK_CONTINUE_CHAIN // no normal afterattack
|
||||
|
||||
/**
|
||||
* Attempts to scan a human's outfit
|
||||
*
|
||||
* * scanned - the atom being scanned.
|
||||
* * scanner - the mob doing the scanning
|
||||
*
|
||||
* Returns null or a list of paths scanned. Will not return an empty list.
|
||||
*/
|
||||
/obj/item/chameleon_scanner/proc/scan_target(atom/scanned, mob/scanner)
|
||||
|
||||
var/mob/living/carbon/human/mob_copying
|
||||
if(ishuman(scanned))
|
||||
mob_copying = scanned
|
||||
else if(isturf(scanned))
|
||||
mob_copying = locate() in scanned // Aim assist
|
||||
|
||||
if(isnull(mob_copying))
|
||||
return
|
||||
|
||||
if(!COOLDOWN_FINISHED(src, scan_cooldown))
|
||||
balloon_alert(scanner, "not ready yet!")
|
||||
return
|
||||
if(get_dist(scanner, mob_copying) > scan_range)
|
||||
balloon_alert(scanner, "too far away!")
|
||||
return
|
||||
// Very short scan timer, keep you on your toes
|
||||
if(!do_after(scanner, 0.5 SECONDS, scanned))
|
||||
return
|
||||
|
||||
var/list/all_scanned_items = list()
|
||||
for(var/obj/item/thing in mob_copying.get_equipped_items())
|
||||
var/datum/action/item_action/chameleon/change/counter_chameleon = locate() in thing.actions
|
||||
if(counter_chameleon?.active_type)
|
||||
// Prevent counter spying
|
||||
all_scanned_items |= counter_chameleon.active_type
|
||||
else
|
||||
// I guess this technically serves as a way to discover changling clothes at a distance...??
|
||||
// Not sure if that's a good thing or not.
|
||||
all_scanned_items |= thing.type
|
||||
|
||||
if(!length(all_scanned_items))
|
||||
scanned.balloon_alert(scanner, "nothing to scan!")
|
||||
return
|
||||
|
||||
playsound(src, 'sound/machines/ping.ogg', vol = 30, vary = TRUE, extrarange = SILENCED_SOUND_EXTRARANGE)
|
||||
COOLDOWN_START(src, scan_cooldown, 5 SECONDS)
|
||||
|
||||
var/datum/action/chameleon_outfit/outfit_action = locate() in scanner.actions
|
||||
outfit_action?.save_outfit(scanner, all_scanned_items)
|
||||
|
||||
return all_scanned_items
|
||||
@@ -0,0 +1,326 @@
|
||||
|
||||
#define BREAK_CHAMELEON_ACTION(item) \
|
||||
do { \
|
||||
var/datum/action/item_action/chameleon/change/_action = locate() in item.actions; \
|
||||
_action?.emp_randomise(INFINITY); \
|
||||
} while(FALSE)
|
||||
|
||||
// Cham jumpsuit
|
||||
/datum/armor/clothing_under/chameleon
|
||||
melee = 10
|
||||
bullet = 10
|
||||
laser = 10
|
||||
fire = 50
|
||||
acid = 50
|
||||
wound = 10
|
||||
|
||||
/obj/item/clothing/under/chameleon
|
||||
name = "black jumpsuit"
|
||||
desc = "It's a plain jumpsuit. It has a small dial on the wrist."
|
||||
icon_state = "jumpsuit"
|
||||
greyscale_colors = "#3f3f3f"
|
||||
greyscale_config = /datum/greyscale_config/jumpsuit
|
||||
greyscale_config_worn = /datum/greyscale_config/jumpsuit/worn
|
||||
greyscale_config_inhand_left = /datum/greyscale_config/jumpsuit/inhand_left
|
||||
greyscale_config_inhand_right = /datum/greyscale_config/jumpsuit/inhand_right
|
||||
sensor_mode = SENSOR_OFF //Hey who's this guy on the Syndicate Shuttle??
|
||||
random_sensor = FALSE
|
||||
resistance_flags = NONE
|
||||
can_adjust = FALSE
|
||||
armor_type = /datum/armor/clothing_under/chameleon
|
||||
actions_types = list(/datum/action/item_action/chameleon/change/jumpsuit)
|
||||
|
||||
/obj/item/clothing/under/chameleon/broken
|
||||
|
||||
/obj/item/clothing/under/chameleon/broken/Initialize(mapload)
|
||||
. = ..()
|
||||
BREAK_CHAMELEON_ACTION(src)
|
||||
|
||||
// Cham suit / armor
|
||||
/datum/armor/suit_chameleon
|
||||
melee = 10
|
||||
bullet = 10
|
||||
laser = 10
|
||||
fire = 50
|
||||
acid = 50
|
||||
|
||||
/obj/item/clothing/suit/chameleon
|
||||
name = "armor"
|
||||
desc = "A slim armored vest that protects against most types of damage."
|
||||
icon_state = "armor"
|
||||
icon = 'icons/obj/clothing/suits/armor.dmi'
|
||||
worn_icon = 'icons/mob/clothing/suits/armor.dmi'
|
||||
inhand_icon_state = "armor"
|
||||
blood_overlay_type = "armor"
|
||||
resistance_flags = NONE
|
||||
armor_type = /datum/armor/suit_chameleon
|
||||
actions_types = list(/datum/action/item_action/chameleon/change/suit)
|
||||
|
||||
/obj/item/clothing/suit/chameleon/Initialize(mapload)
|
||||
. = ..()
|
||||
allowed = GLOB.security_vest_allowed //should at least act like a vest
|
||||
|
||||
/obj/item/clothing/suit/chameleon/broken
|
||||
|
||||
/obj/item/clothing/suit/chameleon/broken/Initialize(mapload)
|
||||
. = ..()
|
||||
BREAK_CHAMELEON_ACTION(src)
|
||||
|
||||
// Cham glasses
|
||||
/datum/armor/glasses_chameleon
|
||||
melee = 10
|
||||
bullet = 10
|
||||
laser = 10
|
||||
fire = 50
|
||||
acid = 50
|
||||
|
||||
/obj/item/clothing/glasses/chameleon
|
||||
name = "Optical Meson Scanner"
|
||||
desc = "Used by engineering and mining staff to see basic structural and terrain layouts through walls, regardless of lighting condition."
|
||||
icon_state = "meson"
|
||||
inhand_icon_state = "meson"
|
||||
resistance_flags = NONE
|
||||
armor_type = /datum/armor/glasses_chameleon
|
||||
actions_types = list(/datum/action/item_action/chameleon/change/glasses)
|
||||
|
||||
/obj/item/clothing/glasses/chameleon/broken
|
||||
|
||||
/obj/item/clothing/glasses/chameleon/broken/Initialize(mapload)
|
||||
. = ..()
|
||||
BREAK_CHAMELEON_ACTION(src)
|
||||
|
||||
// Cham gloves
|
||||
/datum/armor/gloves_chameleon
|
||||
melee = 10
|
||||
bullet = 10
|
||||
laser = 10
|
||||
fire = 50
|
||||
acid = 50
|
||||
|
||||
/obj/item/clothing/gloves/chameleon
|
||||
desc = "These gloves provide protection against electric shock."
|
||||
name = "insulated gloves"
|
||||
icon_state = "yellow"
|
||||
inhand_icon_state = "ygloves"
|
||||
greyscale_colors = null
|
||||
|
||||
resistance_flags = NONE
|
||||
armor_type = /datum/armor/gloves_chameleon
|
||||
actions_types = list(/datum/action/item_action/chameleon/change/gloves)
|
||||
|
||||
/obj/item/clothing/gloves/chameleon/broken
|
||||
|
||||
/obj/item/clothing/gloves/chameleon/broken/Initialize(mapload)
|
||||
. = ..()
|
||||
BREAK_CHAMELEON_ACTION(src)
|
||||
|
||||
// Cham hat
|
||||
/datum/armor/head_chameleon
|
||||
melee = 5
|
||||
bullet = 5
|
||||
laser = 5
|
||||
fire = 50
|
||||
acid = 50
|
||||
|
||||
/obj/item/clothing/head/chameleon
|
||||
name = "grey cap"
|
||||
desc = "It's a baseball hat in a tasteful grey colour."
|
||||
icon = 'icons/obj/clothing/head/hats.dmi'
|
||||
worn_icon = 'icons/mob/clothing/head/hats.dmi'
|
||||
icon_state = "greysoft"
|
||||
resistance_flags = NONE
|
||||
armor_type = /datum/armor/head_chameleon
|
||||
actions_types = list(/datum/action/item_action/chameleon/change/hat)
|
||||
|
||||
/obj/item/clothing/head/chameleon/broken
|
||||
|
||||
/obj/item/clothing/head/chameleon/broken/Initialize(mapload)
|
||||
. = ..()
|
||||
BREAK_CHAMELEON_ACTION(src)
|
||||
|
||||
/obj/item/clothing/head/chameleon/drone
|
||||
actions_types = list(/datum/action/item_action/chameleon/change/hat, /datum/action/item_action/chameleon/drone/togglehatmask, /datum/action/item_action/chameleon/drone/randomise)
|
||||
item_flags = DROPDEL
|
||||
// The camohat, I mean, holographic hat projection, is part of the drone itself.
|
||||
armor_type = /datum/armor/none
|
||||
// which means it offers no protection, it's just air and light
|
||||
|
||||
/obj/item/clothing/head/chameleon/drone/Initialize(mapload)
|
||||
. = ..()
|
||||
ADD_TRAIT(src, TRAIT_NODROP, ABSTRACT_ITEM_TRAIT)
|
||||
var/datum/action/item_action/chameleon/change/hat/hat = locate() in actions
|
||||
hat?.random_look()
|
||||
|
||||
// Cham mask, voice changer included
|
||||
/datum/armor/mask_chameleon
|
||||
melee = 5
|
||||
bullet = 5
|
||||
laser = 5
|
||||
bio = 100
|
||||
fire = 50
|
||||
acid = 50
|
||||
|
||||
/obj/item/clothing/mask/chameleon
|
||||
name = "gas mask"
|
||||
desc = "A face-covering mask that can be connected to an air supply. While good for concealing your identity, it isn't good for blocking gas flow." //More accurate
|
||||
icon_state = "gas_alt"
|
||||
inhand_icon_state = "gas_alt"
|
||||
resistance_flags = NONE
|
||||
armor_type = /datum/armor/mask_chameleon
|
||||
clothing_flags = BLOCK_GAS_SMOKE_EFFECT | MASKINTERNALS
|
||||
flags_inv = HIDEEARS|HIDEEYES|HIDEFACE|HIDEFACIALHAIR|HIDESNOUT
|
||||
flags_cover = MASKCOVERSEYES | MASKCOVERSMOUTH
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
actions_types = list(/datum/action/item_action/chameleon/change/mask)
|
||||
/// Is our voice changer enabled or disabled?
|
||||
var/voice_change = TRUE
|
||||
|
||||
/obj/item/clothing/mask/chameleon/attack_self(mob/user)
|
||||
voice_change = !voice_change
|
||||
to_chat(user, span_notice("The voice changer is now [voice_change ? "on" : "off"]!"))
|
||||
|
||||
/obj/item/clothing/mask/chameleon/broken
|
||||
|
||||
/obj/item/clothing/mask/chameleon/broken/Initialize(mapload)
|
||||
. = ..()
|
||||
BREAK_CHAMELEON_ACTION(src)
|
||||
|
||||
/obj/item/clothing/mask/chameleon/drone
|
||||
actions_types = list(/datum/action/item_action/chameleon/change/mask, /datum/action/item_action/chameleon/drone/togglehatmask, /datum/action/item_action/chameleon/drone/randomise)
|
||||
item_flags = DROPDEL
|
||||
//Same as the drone chameleon hat, undroppable and no protection
|
||||
armor_type = /datum/armor/none
|
||||
voice_change = FALSE
|
||||
|
||||
/obj/item/clothing/mask/chameleon/drone/Initialize(mapload)
|
||||
. = ..()
|
||||
ADD_TRAIT(src, TRAIT_NODROP, ABSTRACT_ITEM_TRAIT)
|
||||
var/datum/action/item_action/chameleon/change/mask/mask = locate() in actions
|
||||
mask?.random_look()
|
||||
|
||||
/obj/item/clothing/mask/chameleon/drone/attack_self(mob/user)
|
||||
to_chat(user, span_notice("[src] does not have a voice changer."))
|
||||
|
||||
// Cham shoes, including chameleon noslips
|
||||
/datum/armor/shoes_chameleon
|
||||
melee = 10
|
||||
bullet = 10
|
||||
laser = 10
|
||||
bio = 90
|
||||
fire = 50
|
||||
acid = 50
|
||||
|
||||
/obj/item/clothing/shoes/chameleon
|
||||
name = "black shoes"
|
||||
desc = "A pair of black shoes."
|
||||
icon_state = "sneakers"
|
||||
inhand_icon_state = "sneakers_back"
|
||||
greyscale_colors = "#545454#ffffff"
|
||||
greyscale_config = /datum/greyscale_config/sneakers
|
||||
greyscale_config_worn = /datum/greyscale_config/sneakers/worn
|
||||
greyscale_config_inhand_left = /datum/greyscale_config/sneakers/inhand_left
|
||||
greyscale_config_inhand_right = /datum/greyscale_config/sneakers/inhand_right
|
||||
resistance_flags = NONE
|
||||
armor_type = /datum/armor/shoes_chameleon
|
||||
actions_types = list(/datum/action/item_action/chameleon/change/shoes)
|
||||
|
||||
/obj/item/clothing/shoes/chameleon/Initialize(mapload)
|
||||
. = ..()
|
||||
create_storage(storage_type = /datum/storage/pockets/shoes)
|
||||
|
||||
/obj/item/clothing/shoes/chameleon/broken
|
||||
|
||||
/obj/item/clothing/shoes/chameleon/broken/Initialize(mapload)
|
||||
. = ..()
|
||||
BREAK_CHAMELEON_ACTION(src)
|
||||
|
||||
/obj/item/clothing/shoes/chameleon/noslip
|
||||
clothing_traits = list(TRAIT_NO_SLIP_WATER)
|
||||
can_be_bloody = FALSE
|
||||
|
||||
/obj/item/clothing/shoes/chameleon/noslip/broken
|
||||
|
||||
/obj/item/clothing/shoes/chameleon/noslip/broken/Initialize(mapload)
|
||||
. = ..()
|
||||
BREAK_CHAMELEON_ACTION(src)
|
||||
|
||||
// Cham backpack
|
||||
/obj/item/storage/backpack/chameleon
|
||||
name = "backpack"
|
||||
actions_types = list(/datum/action/item_action/chameleon/change/backpack)
|
||||
|
||||
/obj/item/storage/backpack/chameleon/broken
|
||||
|
||||
/obj/item/storage/backpack/chameleon/broken/Initialize(mapload)
|
||||
. = ..()
|
||||
BREAK_CHAMELEON_ACTION(src)
|
||||
|
||||
// Cham belt (bonus: is silent)
|
||||
/obj/item/storage/belt/chameleon
|
||||
name = "toolbelt"
|
||||
desc = "Holds tools."
|
||||
actions_types = list(/datum/action/item_action/chameleon/change/belt)
|
||||
|
||||
/obj/item/storage/belt/chameleon/Initialize(mapload)
|
||||
. = ..()
|
||||
atom_storage.silent = TRUE
|
||||
|
||||
/obj/item/storage/belt/chameleon/broken
|
||||
|
||||
/obj/item/storage/belt/chameleon/broken/Initialize(mapload)
|
||||
. = ..()
|
||||
BREAK_CHAMELEON_ACTION(src)
|
||||
|
||||
// Cham headset
|
||||
/obj/item/radio/headset/chameleon
|
||||
name = "radio headset"
|
||||
actions_types = list(/datum/action/item_action/chameleon/change/headset)
|
||||
|
||||
/obj/item/radio/headset/chameleon/broken
|
||||
|
||||
/obj/item/radio/headset/chameleon/broken/Initialize(mapload)
|
||||
. = ..()
|
||||
BREAK_CHAMELEON_ACTION(src)
|
||||
|
||||
// Cham PDA
|
||||
/obj/item/modular_computer/pda/chameleon
|
||||
name = "tablet"
|
||||
actions_types = list(/datum/action/item_action/chameleon/change/tablet)
|
||||
|
||||
/obj/item/modular_computer/pda/chameleon/broken
|
||||
|
||||
/obj/item/modular_computer/pda/chameleon/broken/Initialize(mapload)
|
||||
. = ..()
|
||||
BREAK_CHAMELEON_ACTION(src)
|
||||
|
||||
// Cham Stamp
|
||||
/obj/item/stamp/chameleon
|
||||
actions_types = list(/datum/action/item_action/chameleon/change/stamp)
|
||||
|
||||
/obj/item/stamp/chameleon/broken
|
||||
|
||||
/obj/item/stamp/chameleon/broken/Initialize(mapload)
|
||||
. = ..()
|
||||
BREAK_CHAMELEON_ACTION(src)
|
||||
|
||||
// Cham neck-thing
|
||||
/datum/armor/neck_chameleon
|
||||
fire = 50
|
||||
acid = 50
|
||||
|
||||
/obj/item/clothing/neck/chameleon
|
||||
name = "black tie"
|
||||
desc = "A neosilk clip-on tie."
|
||||
icon_state = "detective" //we use this icon_state since the other ones are all generated by GAGS.
|
||||
resistance_flags = NONE
|
||||
armor_type = /datum/armor/neck_chameleon
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
actions_types = list(/datum/action/item_action/chameleon/change/neck)
|
||||
|
||||
/obj/item/clothing/neck/chameleon/broken
|
||||
|
||||
/obj/item/clothing/neck/chameleon/broken/Initialize(mapload)
|
||||
. = ..()
|
||||
BREAK_CHAMELEON_ACTION(src)
|
||||
|
||||
#undef BREAK_CHAMELEON_ACTION
|
||||
@@ -524,28 +524,12 @@
|
||||
. = ..()
|
||||
REMOVE_TRAIT(user, TRAIT_XRAY_VISION, GLASSES_TRAIT)
|
||||
|
||||
/obj/item/clothing/glasses/thermal/syndi //These are now a traitor item, concealed as mesons. -Pete
|
||||
/obj/item/clothing/glasses/thermal/syndi
|
||||
name = "chameleon thermals"
|
||||
desc = "A pair of thermal optic goggles with an onboard chameleon generator."
|
||||
special_desc_requirement = EXAMINE_CHECK_SYNDICATE // SKYRAT EDIT
|
||||
special_desc = "Chameleon thermal goggles employed by the Syndicate in infiltration operations." //SKYRAT EDIT, I don't think the regular description persists through chameleon changes.
|
||||
|
||||
var/datum/action/item_action/chameleon/change/chameleon_action
|
||||
|
||||
/obj/item/clothing/glasses/thermal/syndi/Initialize(mapload)
|
||||
. = ..()
|
||||
chameleon_action = new(src)
|
||||
chameleon_action.chameleon_type = /obj/item/clothing/glasses
|
||||
chameleon_action.chameleon_name = "Glasses"
|
||||
chameleon_action.chameleon_blacklist = typecacheof(/obj/item/clothing/glasses/changeling, only_root_path = TRUE)
|
||||
chameleon_action.initialize_disguises()
|
||||
add_item_action(chameleon_action)
|
||||
|
||||
/obj/item/clothing/glasses/thermal/syndi/emp_act(severity)
|
||||
. = ..()
|
||||
if(. & EMP_PROTECT_SELF)
|
||||
return
|
||||
chameleon_action.emp_randomise()
|
||||
actions_types = list(/datum/action/item_action/chameleon/change/glasses/no_preset)
|
||||
special_desc_requirement = EXAMINE_CHECK_SYNDICATE // SKYRAT EDIT ADDITION
|
||||
special_desc = "Chameleon thermal goggles employed by the Syndicate in infiltration operations." //SKYRAT EDIT ADDITION, I don't think the regular description persists through chameleon changes.
|
||||
|
||||
/obj/item/clothing/glasses/thermal/monocle
|
||||
name = "thermoncle"
|
||||
|
||||
@@ -130,26 +130,7 @@
|
||||
name = "chameleon security HUD"
|
||||
desc = "A stolen security HUD integrated with Syndicate chameleon technology. Provides flash protection."
|
||||
flash_protect = FLASH_PROTECTION_FLASH
|
||||
|
||||
// Yes this code is the same as normal chameleon glasses, but we don't
|
||||
// have multiple inheritance, okay?
|
||||
var/datum/action/item_action/chameleon/change/chameleon_action
|
||||
|
||||
/obj/item/clothing/glasses/hud/security/chameleon/Initialize(mapload)
|
||||
. = ..()
|
||||
chameleon_action = new(src)
|
||||
chameleon_action.chameleon_type = /obj/item/clothing/glasses
|
||||
chameleon_action.chameleon_name = "Glasses"
|
||||
chameleon_action.chameleon_blacklist = typecacheof(/obj/item/clothing/glasses/changeling, only_root_path = TRUE)
|
||||
chameleon_action.initialize_disguises()
|
||||
add_item_action(chameleon_action)
|
||||
|
||||
/obj/item/clothing/glasses/hud/security/chameleon/emp_act(severity)
|
||||
. = ..()
|
||||
if(. & EMP_PROTECT_SELF)
|
||||
return
|
||||
chameleon_action.emp_randomise()
|
||||
|
||||
actions_types = list(/datum/action/item_action/chameleon/change/glasses/no_preset)
|
||||
|
||||
/obj/item/clothing/glasses/hud/security/sunglasses/eyepatch
|
||||
name = "eyepatch HUD"
|
||||
|
||||
@@ -42,7 +42,9 @@
|
||||
var/cooldown = 5 MINUTES
|
||||
/// Cooldown for chip actions.
|
||||
COOLDOWN_DECLARE(chip_cooldown)
|
||||
/// Used to determine if this is an abstract type or not. If this is meant to be an abstract type, set it to the type's path. Will be overridden by subsequent abstract parents. See /datum/action/item_action/chameleon/change/skillchip/initialize_disguises()
|
||||
/// Used to determine if this is an abstract type or not.
|
||||
/// If this is meant to be an abstract type, set it to the type's path.
|
||||
/// Will be overridden by subsequent abstract parents.
|
||||
var/abstract_parent_type = /obj/item/skillchip
|
||||
/// Set to TRUE when the skill chip's effects are applied. Set to FALSE when they're not.
|
||||
var/active = FALSE
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
update_suit_storage()
|
||||
if(slot_flags & (ITEM_SLOT_LPOCKET|ITEM_SLOT_RPOCKET))
|
||||
update_pockets()
|
||||
if(slot_flags & ITEM_SLOT_HANDS)
|
||||
update_held_items()
|
||||
|
||||
/// Updates features and clothing attached to a specific limb with limb-specific offsets
|
||||
/mob/living/carbon/proc/update_features(feature_key)
|
||||
|
||||
@@ -134,8 +134,10 @@
|
||||
ethereal.set_facial_haircolor(dead_color, override = TRUE, update = FALSE)
|
||||
ethereal.set_haircolor(dead_color, override = TRUE, update = TRUE)
|
||||
|
||||
/datum/species/ethereal/proc/on_emp_act(mob/living/carbon/human/H, severity)
|
||||
/datum/species/ethereal/proc/on_emp_act(mob/living/carbon/human/H, severity, protection)
|
||||
SIGNAL_HANDLER
|
||||
if(protection & EMP_PROTECT_SELF)
|
||||
return
|
||||
EMPeffect = TRUE
|
||||
spec_updatehealth(H)
|
||||
to_chat(H, span_notice("You feel the light of your body leave you."))
|
||||
|
||||
@@ -17,6 +17,15 @@
|
||||
cut_overlay(I)
|
||||
drone_overlays[cache_index] = null
|
||||
|
||||
/mob/living/simple_animal/drone/update_clothing(slot_flags)
|
||||
if(slot_flags & ITEM_SLOT_HEAD)
|
||||
update_worn_head()
|
||||
if(slot_flags & ITEM_SLOT_MASK)
|
||||
update_worn_mask()
|
||||
if(slot_flags & ITEM_SLOT_HANDS)
|
||||
update_held_items()
|
||||
if(slot_flags & (ITEM_SLOT_HANDS|ITEM_SLOT_BACKPACK|ITEM_SLOT_DEX_STORAGE))
|
||||
update_inv_internal_storage()
|
||||
|
||||
/mob/living/simple_animal/drone/update_held_items()
|
||||
remove_overlay(DRONE_HANDS_LAYER)
|
||||
|
||||
@@ -59,9 +59,6 @@
|
||||
var/cached_multiplicative_actions_slowdown
|
||||
/// List of action hud items the user has
|
||||
var/list/datum/action/actions
|
||||
/// A list of chameleon actions we have specifically
|
||||
/// This can be unified with the actions list
|
||||
var/list/datum/action/item_action/chameleon/chameleon_item_actions
|
||||
///Cursor icon used when holding shift over things
|
||||
var/examine_cursor_icon = 'icons/effects/mouse_pointers/examine_pointer.dmi'
|
||||
|
||||
|
||||
@@ -504,9 +504,7 @@
|
||||
|
||||
/obj/item/mod/module/dna_lock/emp_act(severity)
|
||||
. = ..()
|
||||
if(. & EMP_PROTECT_SELF)
|
||||
return
|
||||
on_emp(src, severity)
|
||||
on_emp(src, severity, .)
|
||||
|
||||
/obj/item/mod/module/dna_lock/emag_act(mob/user, obj/item/card/emag/emag_card)
|
||||
. = ..()
|
||||
@@ -521,9 +519,10 @@
|
||||
balloon_alert(user, "dna locked!")
|
||||
return FALSE
|
||||
|
||||
/obj/item/mod/module/dna_lock/proc/on_emp(datum/source, severity)
|
||||
/obj/item/mod/module/dna_lock/proc/on_emp(datum/source, severity, protection)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(protection & EMP_PROTECT_SELF)
|
||||
return
|
||||
dna = null
|
||||
|
||||
/obj/item/mod/module/dna_lock/proc/on_emag(datum/source, mob/user, obj/item/card/emag/emag_card)
|
||||
|
||||
@@ -41,20 +41,25 @@
|
||||
|
||||
/obj/item/paperwork/attackby(obj/item/attacking_item, mob/user, params)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
|
||||
if(!stamped)
|
||||
if(istype(attacking_item, /obj/item/stamp))
|
||||
if(istype(attacking_item, stamp_requested)) //chameleon stamp does not work, this is a CRITICAL issue
|
||||
add_stamp()
|
||||
to_chat(user, span_notice("You skim through the papers until you find a field reading 'STAMP HERE', and complete the paperwork."))
|
||||
else
|
||||
if(istype(attacking_item, /obj/item/stamp/chameleon))
|
||||
var/obj/item/stamp/chameleon/chameleon_stamp = attacking_item
|
||||
to_chat(user, span_notice("[chameleon_stamp] morphs into the appropriate stamp, which you use to complete the paperwork."))
|
||||
chameleon_stamp.chameleon_action.update_item(stamp_requested)
|
||||
add_stamp()
|
||||
else
|
||||
to_chat(user, span_warning("You hunt through the papers for somewhere to use the [attacking_item], but can't find anything."))
|
||||
if(stamped || istype(attacking_item, /obj/item/stamp))
|
||||
return
|
||||
|
||||
if(istype(attacking_item, stamp_requested))
|
||||
add_stamp()
|
||||
to_chat(user, span_notice("You skim through the papers until you find a field reading 'STAMP HERE', and complete the paperwork."))
|
||||
return TRUE
|
||||
var/datum/action/item_action/chameleon/change/stamp/stamp_action = locate() in attacking_item.actions
|
||||
if(isnull(stamp_action))
|
||||
to_chat(user, span_warning("You hunt through the papers for somewhere to use [attacking_item], but can't find anything."))
|
||||
return TRUE
|
||||
|
||||
to_chat(user, span_notice("[attacking_item] morphs into the appropriate stamp, which you use to complete the paperwork."))
|
||||
stamp_action.update_look(stamp_requested)
|
||||
add_stamp()
|
||||
return TRUE
|
||||
|
||||
/obj/item/paperwork/examine_more(mob/user)
|
||||
. = ..()
|
||||
|
||||
+7
-1
@@ -3299,8 +3299,14 @@
|
||||
#include "code\modules\client\verbs\ping.dm"
|
||||
#include "code\modules\client\verbs\suicide.dm"
|
||||
#include "code\modules\client\verbs\who.dm"
|
||||
#include "code\modules\clothing\chameleon.dm"
|
||||
#include "code\modules\clothing\clothing.dm"
|
||||
#include "code\modules\clothing\chameleon\_chameleon_action.dm"
|
||||
#include "code\modules\clothing\chameleon\_chameleon_outfit.dm"
|
||||
#include "code\modules\clothing\chameleon\chameleon_action_subtypes.dm"
|
||||
#include "code\modules\clothing\chameleon\chameleon_drone.dm"
|
||||
#include "code\modules\clothing\chameleon\chameleon_gun.dm"
|
||||
#include "code\modules\clothing\chameleon\chameleon_scanner.dm"
|
||||
#include "code\modules\clothing\chameleon\generic_chameleon_clothing.dm"
|
||||
#include "code\modules\clothing\ears\_ears.dm"
|
||||
#include "code\modules\clothing\glasses\_glasses.dm"
|
||||
#include "code\modules\clothing\glasses\engine_goggles.dm"
|
||||
|
||||
Reference in New Issue
Block a user