mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-09 16:05:07 +00:00
## About The Pull Request This PR tackles our piss-poor item action handling. Currently in order to make an item only have actions when its equipped to a certain slot you need to override a proc, which I've changed by introducing an action_slots variable. I've also cleaned up a ton of action code, and most importantly moved a lot of Trigger effects on items to do_effect, which allows actions to not call ui_action_click or attack_self on an item without bypassing IsAvailible and comsigs that parent Trigger has. This resolves issues like jump boots being usable from your hands, HUDs being toggleable out of your pockets, etc. Also moved a few actions from relying on attack_self to individual handling on their side. This also stops welding masks/hardhats from showing their action while you hold them, this part of the change is just something I thought didn't make much sense - you can use their action by using them in-hand, and flickering on your action bar can be annoying when reshuffling your backpack. Closes #89653 ## Why It's Good For The Game Makes action handling significantly less ass, allows us to avoid code like this ```js /obj/item/clothing/mask/gas/sechailer/ui_action_click(mob/user, action) if(istype(action, /datum/action/item_action/halt)) halt() else adjust_visor(user) ```
2118 lines
81 KiB
Plaintext
2118 lines
81 KiB
Plaintext
/// Fallback time if none of the config entries are set for USE_LOW_LIVING_HOUR_INTERN
|
|
#define INTERN_THRESHOLD_FALLBACK_HOURS 15
|
|
|
|
/// Max time interval between projecting holopays
|
|
#define HOLOPAY_PROJECTION_INTERVAL (7 SECONDS)
|
|
|
|
/* Cards
|
|
* Contains:
|
|
* DATA CARD
|
|
* ID CARD
|
|
* FINGERPRINT CARD HOLDER
|
|
* FINGERPRINT CARD
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
* DATA CARDS - Used for the IC data card reader
|
|
*/
|
|
|
|
/obj/item/card
|
|
name = "card"
|
|
desc = "Does card things."
|
|
icon = 'icons/obj/card.dmi'
|
|
inhand_icon_state = "card-id"
|
|
lefthand_file = 'icons/mob/inhands/equipment/idcards_lefthand.dmi'
|
|
righthand_file = 'icons/mob/inhands/equipment/idcards_righthand.dmi'
|
|
w_class = WEIGHT_CLASS_TINY
|
|
pickup_sound = 'sound/items/handling/id_card/id_card_pickup1.ogg'
|
|
drop_sound = 'sound/items/handling/id_card/id_card_drop1.ogg'
|
|
sound_vary = TRUE
|
|
|
|
/// Cached icon that has been built for this card. Intended to be displayed in chat. Cardboards IDs and actual IDs use it.
|
|
var/icon/cached_flat_icon
|
|
///What is our honorific name/title combo to be displayed?
|
|
var/honorific_title
|
|
|
|
/obj/item/card/suicide_act(mob/living/carbon/user)
|
|
user.visible_message(span_suicide("[user] begins to swipe [user.p_their()] neck with \the [src]! It looks like [user.p_theyre()] trying to commit suicide!"))
|
|
return BRUTELOSS
|
|
|
|
/obj/item/card/update_overlays()
|
|
. = ..()
|
|
cached_flat_icon = null
|
|
|
|
/// If no cached_flat_icon exists, this proc creates it and crops it. This proc then returns the cached_flat_icon. Intended for use displaying ID card icons in chat.
|
|
/obj/item/card/proc/get_cached_flat_icon()
|
|
if(!cached_flat_icon)
|
|
cached_flat_icon = getFlatIcon(src)
|
|
cached_flat_icon.Crop(ID_ICON_BORDERS)
|
|
return cached_flat_icon
|
|
|
|
/*
|
|
* ID CARDS
|
|
*/
|
|
|
|
/// "Retro" ID card that renders itself as the icon state with no overlays.
|
|
/obj/item/card/id
|
|
name = "retro identification card"
|
|
desc = "A card used to provide ID and determine access across the station."
|
|
icon_state = "card_grey"
|
|
worn_icon_state = "nothing"
|
|
slot_flags = ITEM_SLOT_ID
|
|
interaction_flags_click = FORBID_TELEKINESIS_REACH
|
|
armor_type = /datum/armor/card_id
|
|
resistance_flags = FIRE_PROOF | ACID_PROOF
|
|
|
|
/// The name registered on the card (for example: Dr Bryan See)
|
|
var/registered_name = null
|
|
/// Linked bank account.
|
|
var/datum/bank_account/registered_account
|
|
|
|
/// Linked holopay.
|
|
var/obj/structure/holopay/my_store
|
|
/// Cooldown between projecting holopays
|
|
COOLDOWN_DECLARE(last_holopay_projection)
|
|
/// List of logos available for holopay customization - via font awesome 5
|
|
var/static/list/available_logos = list("angry", "ankh", "bacon", "band-aid", "cannabis", "cat", "cocktail", "coins", "comments-dollar",
|
|
"cross", "cut", "dog", "donate", "dna", "fist-raised", "flask", "glass-cheers", "glass-martini-alt", "hamburger", "hand-holding-usd",
|
|
"hat-wizard", "head-side-cough-slash", "heart", "heart-broken", "laugh-beam", "leaf", "money-check-alt", "music", "piggy-bank",
|
|
"pizza-slice", "prescription-bottle-alt", "radiation", "robot", "smile", "skull-crossbones", "smoking", "space-shuttle", "tram",
|
|
"trash", "user-ninja", "utensils", "wrench")
|
|
/// Replaces the "pay whatever" functionality with a set amount when non-zero.
|
|
var/holopay_fee = 0
|
|
/// The holopay icon chosen by the user
|
|
var/holopay_logo = "donate"
|
|
/// Maximum forced fee. It's unlikely for a user to encounter this type of money, much less pay it willingly.
|
|
var/holopay_max_fee = 5000
|
|
/// Minimum forced fee for holopay stations. Registers as "pay what you want."
|
|
var/holopay_min_fee = 0
|
|
/// The holopay name chosen by the user
|
|
var/holopay_name = "holographic pay stand"
|
|
|
|
/// Registered owner's age.
|
|
var/registered_age = 18 //SKYRAT EDIT - ORIGINAL (30)
|
|
|
|
/// The job name registered on the card (for example: Assistant).
|
|
var/assignment
|
|
|
|
/// Trim datum associated with the card. Controls which job icon is displayed on the card and which accesses do not require wildcards.
|
|
var/datum/id_trim/trim
|
|
|
|
/// Access levels held by this card.
|
|
var/list/access = list()
|
|
|
|
/// List of wildcard slot names as keys with lists of wildcard data as values.
|
|
var/list/wildcard_slots = list()
|
|
|
|
/// Boolean value. If TRUE, the [Intern] tag gets prepended to this ID card when the label is updated.
|
|
var/is_intern = FALSE
|
|
|
|
///If true, the wearer will have bigger arrow when pointing at things. Passed down by trims.
|
|
var/big_pointer = FALSE
|
|
///If set, the arrow will have a different color.
|
|
var/pointer_color
|
|
/// Will this ID card use the first or last name as the name displayed with the honorific?
|
|
var/honorific_position = HONORIFIC_POSITION_NONE
|
|
/// What is our selected honorific?
|
|
var/chosen_honorific
|
|
|
|
|
|
/datum/armor/card_id
|
|
fire = 100
|
|
acid = 100
|
|
|
|
/obj/item/card/id/apply_fantasy_bonuses(bonus)
|
|
. = ..()
|
|
if(bonus >= 15)
|
|
add_access(SSid_access.get_region_access_list(list(REGION_ALL_GLOBAL)), mode = FORCE_ADD_ALL)
|
|
else if(bonus >= 10)
|
|
add_access(SSid_access.get_region_access_list(list(REGION_ALL_STATION)), mode = FORCE_ADD_ALL)
|
|
else if(bonus <= -10)
|
|
clear_access()
|
|
|
|
/obj/item/card/id/Initialize(mapload)
|
|
. = ..()
|
|
|
|
var/datum/bank_account/blank_bank_account = new("Unassigned", SSjob.get_job_type(/datum/job/unassigned), player_account = FALSE)
|
|
registered_account = blank_bank_account
|
|
registered_account.replaceable = TRUE
|
|
|
|
// Applying the trim updates the label and icon, so don't do this twice.
|
|
if(ispath(trim))
|
|
SSid_access.apply_trim_to_card(src, trim)
|
|
else
|
|
update_label()
|
|
update_icon()
|
|
|
|
register_context()
|
|
|
|
RegisterSignal(src, COMSIG_ATOM_UPDATED_ICON, PROC_REF(update_in_wallet))
|
|
RegisterSignal(src, COMSIG_ID_GET_HONORIFIC, PROC_REF(return_message_name_part))
|
|
if(prob(1))
|
|
ADD_TRAIT(src, TRAIT_TASTEFULLY_THICK_ID_CARD, ROUNDSTART_TRAIT)
|
|
|
|
/obj/item/card/id/Destroy()
|
|
if (registered_account)
|
|
registered_account.bank_cards -= src
|
|
if (my_store)
|
|
QDEL_NULL(my_store)
|
|
if (isitem(loc))
|
|
UnregisterSignal(loc, list(COMSIG_ITEM_EQUIPPED, COMSIG_ITEM_DROPPED))
|
|
return ..()
|
|
|
|
/obj/item/card/id/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change)
|
|
if (isitem(old_loc))
|
|
UnregisterSignal(old_loc, list(COMSIG_ITEM_EQUIPPED, COMSIG_ITEM_DROPPED))
|
|
if (ismob(old_loc.loc))
|
|
UnregisterSignal(old_loc.loc, COMSIG_MOVABLE_POINTED)
|
|
. = ..()
|
|
if (isitem(loc))
|
|
RegisterSignal(loc, COMSIG_ITEM_EQUIPPED, PROC_REF(on_loc_equipped))
|
|
RegisterSignal(loc, COMSIG_ITEM_DROPPED, PROC_REF(on_loc_dropped))
|
|
|
|
/obj/item/card/id/equipped(mob/user, slot)
|
|
. = ..()
|
|
if (slot == ITEM_SLOT_ID)
|
|
RegisterSignal(user, COMSIG_MOVABLE_POINTED, PROC_REF(on_pointed))
|
|
|
|
/obj/item/card/id/dropped(mob/user)
|
|
UnregisterSignal(user, COMSIG_MOVABLE_POINTED)
|
|
return ..()
|
|
|
|
/obj/item/card/id/proc/return_message_name_part(datum/source, list/stored_name, mob/living/carbon/carbon_human)
|
|
SIGNAL_HANDLER
|
|
var/voice_name = carbon_human.GetVoice()
|
|
var/end_string = ""
|
|
var/return_string = ""
|
|
if(carbon_human.name != voice_name)
|
|
end_string += " (as [registered_name])"
|
|
if(trim && honorific_position != HONORIFIC_POSITION_NONE && (carbon_human.name == voice_name)) //The voice and name are the same, so we display the title.
|
|
return_string += honorific_title
|
|
else
|
|
return_string += voice_name //Name on the ID ain't the same as the speaker, so we display their real name with no title.
|
|
return_string += end_string
|
|
stored_name[NAME_PART_INDEX] = return_string
|
|
|
|
/obj/item/card/id/proc/on_loc_equipped(datum/source, mob/equipper, slot)
|
|
SIGNAL_HANDLER
|
|
|
|
if (slot == ITEM_SLOT_ID)
|
|
RegisterSignal(equipper, COMSIG_MOVABLE_POINTED, PROC_REF(on_pointed))
|
|
|
|
/obj/item/card/id/proc/on_loc_dropped(datum/source, mob/dropper)
|
|
SIGNAL_HANDLER
|
|
UnregisterSignal(dropper, COMSIG_MOVABLE_POINTED)
|
|
|
|
/obj/item/card/id/proc/on_pointed(mob/living/user, atom/pointed, obj/effect/temp_visual/point/point)
|
|
SIGNAL_HANDLER
|
|
if ((!big_pointer && !pointer_color) || HAS_TRAIT(user, TRAIT_UNKNOWN))
|
|
return
|
|
if (point.icon_state != /obj/effect/temp_visual/point::icon_state) //it differs from the original icon_state already.
|
|
return
|
|
if (loc != user)
|
|
if (!isitem(loc))
|
|
return
|
|
var/obj/item/as_item = loc
|
|
if (as_item.GetID() != src)
|
|
return
|
|
if (big_pointer)
|
|
point.icon_state = "arrow_large"
|
|
if (pointer_color)
|
|
point.icon_state = "[point.icon_state]_white"
|
|
point.color = pointer_color
|
|
var/mutable_appearance/highlight = mutable_appearance(point.icon, "[point.icon_state]_highlights", appearance_flags = RESET_COLOR)
|
|
point.add_overlay(highlight)
|
|
|
|
/obj/item/card/id/get_id_examine_strings(mob/user)
|
|
. = ..()
|
|
. += list("[icon2html(get_cached_flat_icon(), user, extra_classes = "hugeicon")]")
|
|
|
|
/obj/item/card/id/get_examine_icon(mob/user)
|
|
return icon2html(get_cached_flat_icon(), user)
|
|
|
|
/**
|
|
* Helper proc, checks whether the ID card can hold any given set of wildcards.
|
|
*
|
|
* Returns TRUE if the card can hold the wildcards, FALSE otherwise.
|
|
* Arguments:
|
|
* * wildcard_list - List of accesses to check.
|
|
* * try_wildcard - If not null, will attempt to add wildcards for this wildcard specifically and will return FALSE if the card cannot hold all wildcards in this slot.
|
|
*/
|
|
/obj/item/card/id/proc/can_add_wildcards(list/wildcard_list, try_wildcard = null)
|
|
if(!length(wildcard_list))
|
|
return TRUE
|
|
|
|
var/list/new_wildcard_limits = list()
|
|
|
|
for(var/flag_name in wildcard_slots)
|
|
if(try_wildcard && !(flag_name == try_wildcard))
|
|
continue
|
|
var/list/wildcard_info = wildcard_slots[flag_name]
|
|
new_wildcard_limits[flag_name] = wildcard_info["limit"] - length(wildcard_info["usage"])
|
|
|
|
if(!length(new_wildcard_limits))
|
|
return FALSE
|
|
|
|
var/wildcard_allocated
|
|
for(var/wildcard in wildcard_list)
|
|
var/wildcard_flag = SSid_access.get_access_flag(wildcard)
|
|
wildcard_allocated = FALSE
|
|
for(var/flag_name in new_wildcard_limits)
|
|
var/limit_flags = SSid_access.wildcard_flags_by_wildcard[flag_name]
|
|
if(!(wildcard_flag & limit_flags))
|
|
continue
|
|
// Negative limits mean infinite slots. Positive limits mean limited slots still available. 0 slots means no slots.
|
|
if(new_wildcard_limits[flag_name] == 0)
|
|
continue
|
|
new_wildcard_limits[flag_name]--
|
|
wildcard_allocated = TRUE
|
|
break
|
|
if(!wildcard_allocated)
|
|
return FALSE
|
|
|
|
return TRUE
|
|
|
|
/**
|
|
* Attempts to add the given wildcards to the ID card.
|
|
*
|
|
* Arguments:
|
|
* * wildcard_list - List of accesses to add.
|
|
* * try_wildcard - If not null, will attempt to add all wildcards to this wildcard slot only.
|
|
* * mode - The method to use when adding wildcards. See define for ERROR_ON_FAIL
|
|
*/
|
|
/obj/item/card/id/proc/add_wildcards(list/wildcard_list, try_wildcard = null, mode = ERROR_ON_FAIL)
|
|
var/wildcard_allocated
|
|
// Iterate through each wildcard in our list. Get its access flag. Then iterate over wildcard slots and try to fit it in.
|
|
for(var/wildcard in wildcard_list)
|
|
var/wildcard_flag = SSid_access.get_access_flag(wildcard)
|
|
wildcard_allocated = FALSE
|
|
for(var/flag_name in wildcard_slots)
|
|
if(flag_name == WILDCARD_NAME_FORCED)
|
|
continue
|
|
|
|
if(try_wildcard && !(flag_name == try_wildcard))
|
|
continue
|
|
|
|
var/limit_flags = SSid_access.wildcard_flags_by_wildcard[flag_name]
|
|
|
|
if(!(wildcard_flag & limit_flags))
|
|
continue
|
|
|
|
var/list/wildcard_info = wildcard_slots[flag_name]
|
|
var/wildcard_limit = wildcard_info["limit"]
|
|
var/list/wildcard_usage = wildcard_info["usage"]
|
|
|
|
var/wildcard_count = wildcard_limit - length(wildcard_usage)
|
|
|
|
// Negative limits mean infinite slots. Positive limits mean limited slots still available. 0 slots means no slots.
|
|
if(wildcard_count == 0)
|
|
continue
|
|
|
|
wildcard_usage |= wildcard
|
|
access |= wildcard
|
|
wildcard_allocated = TRUE
|
|
break
|
|
// Fallback for if we couldn't allocate the wildcard for some reason.
|
|
if(!wildcard_allocated)
|
|
if(mode == ERROR_ON_FAIL)
|
|
CRASH("Wildcard ([wildcard]) could not be added to [src].")
|
|
|
|
if(mode == TRY_ADD_ALL)
|
|
continue
|
|
|
|
// If the card has no info for historic forced wildcards, create the list.
|
|
if(!wildcard_slots[WILDCARD_NAME_FORCED])
|
|
wildcard_slots[WILDCARD_NAME_FORCED] = list(limit = 0, usage = list())
|
|
|
|
var/list/wildcard_info = wildcard_slots[WILDCARD_NAME_FORCED]
|
|
var/list/wildcard_usage = wildcard_info["usage"]
|
|
wildcard_usage |= wildcard
|
|
access |= wildcard
|
|
wildcard_info["limit"] = length(wildcard_usage)
|
|
|
|
/**
|
|
* Removes wildcards from the ID card.
|
|
*
|
|
* Arguments:
|
|
* * wildcard_list - List of accesses to remove.
|
|
*/
|
|
/obj/item/card/id/proc/remove_wildcards(list/wildcard_list)
|
|
var/wildcard_removed
|
|
// Iterate through each wildcard in our list. Get its access flag. Then iterate over wildcard slots and try to remove it.
|
|
for(var/wildcard in wildcard_list)
|
|
wildcard_removed = FALSE
|
|
for(var/flag_name in wildcard_slots)
|
|
if(flag_name == WILDCARD_NAME_FORCED)
|
|
continue
|
|
|
|
var/list/wildcard_info = wildcard_slots[flag_name]
|
|
var/wildcard_usage = wildcard_info["usage"]
|
|
|
|
if(!(wildcard in wildcard_usage))
|
|
continue
|
|
|
|
wildcard_usage -= wildcard
|
|
access -= wildcard
|
|
wildcard_removed = TRUE
|
|
break
|
|
// Fallback to see if this was a force-added wildcard.
|
|
if(!wildcard_removed)
|
|
// If the card has no info for historic forced wildcards, that's an error state.
|
|
if(!wildcard_slots[WILDCARD_NAME_FORCED])
|
|
stack_trace("Wildcard ([wildcard]) could not be removed from [src]. This card has no forced wildcard data and the wildcard is not in this card's wildcard lists.")
|
|
|
|
var/list/wildcard_info = wildcard_slots[WILDCARD_NAME_FORCED]
|
|
var/wildcard_usage = wildcard_info["usage"]
|
|
|
|
if(!(wildcard in wildcard_usage))
|
|
stack_trace("Wildcard ([wildcard]) could not be removed from [src]. This access is not a wildcard on this card.")
|
|
|
|
wildcard_usage -= wildcard
|
|
access -= wildcard
|
|
wildcard_info["limit"] = length(wildcard_usage)
|
|
|
|
if(!wildcard_info["limit"])
|
|
wildcard_slots -= WILDCARD_NAME_FORCED
|
|
|
|
/**
|
|
* Attempts to add the given accesses to the ID card as non-wildcards.
|
|
*
|
|
* Depending on the mode, may add accesses as wildcards or error if it can't add them as non-wildcards.
|
|
* Arguments:
|
|
* * add_accesses - List of accesses to check.
|
|
* * try_wildcard - If not null, will attempt to add all accesses that require wildcard slots to this wildcard slot only.
|
|
* * mode - The method to use when adding accesses. See define for ERROR_ON_FAIL
|
|
*/
|
|
/obj/item/card/id/proc/add_access(list/add_accesses, try_wildcard = null, mode = ERROR_ON_FAIL)
|
|
var/list/wildcard_access = list()
|
|
var/list/normal_access = list()
|
|
|
|
build_access_lists(add_accesses, normal_access, wildcard_access)
|
|
|
|
// Check if we can add the wildcards.
|
|
if(mode == ERROR_ON_FAIL)
|
|
if(!can_add_wildcards(wildcard_access, try_wildcard))
|
|
CRASH("Cannot add wildcards from \[[add_accesses.Join(",")]\] to [src]")
|
|
|
|
// All clear to add the accesses.
|
|
access |= normal_access
|
|
if(mode != TRY_ADD_ALL_NO_WILDCARD)
|
|
add_wildcards(wildcard_access, try_wildcard, mode = mode)
|
|
|
|
return TRUE
|
|
|
|
/**
|
|
* Removes the given accesses from the ID Card.
|
|
*
|
|
* Will remove the wildcards if the accesses given are on the card as wildcard accesses.
|
|
* Arguments:
|
|
* * rem_accesses - List of accesses to remove.
|
|
*/
|
|
/obj/item/card/id/proc/remove_access(list/rem_accesses)
|
|
var/list/wildcard_access = list()
|
|
var/list/normal_access = list()
|
|
|
|
build_access_lists(rem_accesses, normal_access, wildcard_access)
|
|
|
|
access -= normal_access
|
|
remove_wildcards(wildcard_access)
|
|
|
|
/**
|
|
* Attempts to set the card's accesses to the given accesses, clearing all accesses not in the given list.
|
|
*
|
|
* Depending on the mode, may add accesses as wildcards or error if it can't add them as non-wildcards.
|
|
* Arguments:
|
|
* * new_access_list - List of all accesses that this card should hold exclusively.
|
|
* * mode - The method to use when setting accesses. See define for ERROR_ON_FAIL
|
|
*/
|
|
/obj/item/card/id/proc/set_access(list/new_access_list, mode = ERROR_ON_FAIL)
|
|
var/list/wildcard_access = list()
|
|
var/list/normal_access = list()
|
|
|
|
if(length(new_access_list))
|
|
build_access_lists(new_access_list, normal_access, wildcard_access)
|
|
|
|
// Check if we can add the wildcards.
|
|
if(mode == ERROR_ON_FAIL)
|
|
if(!can_add_wildcards(wildcard_access))
|
|
CRASH("Cannot add wildcards from \[[new_access_list.Join(",")]\] to [src]")
|
|
|
|
clear_access()
|
|
|
|
access = normal_access.Copy()
|
|
|
|
if(mode != TRY_ADD_ALL_NO_WILDCARD)
|
|
add_wildcards(wildcard_access, mode = mode)
|
|
|
|
return TRUE
|
|
|
|
/// Clears all accesses from the ID card - both wildcard and normal.
|
|
/obj/item/card/id/proc/clear_access()
|
|
// Go through the wildcards and reset them.
|
|
for(var/flag_name in wildcard_slots)
|
|
var/list/wildcard_info = wildcard_slots[flag_name]
|
|
var/list/wildcard_usage = wildcard_info["usage"]
|
|
wildcard_usage.Cut()
|
|
|
|
// Hard reset access
|
|
access.Cut()
|
|
|
|
/// Clears the economy account from the ID card.
|
|
/obj/item/card/id/proc/clear_account()
|
|
registered_account = null
|
|
|
|
|
|
/**
|
|
* Helper proc. Creates access lists for the access procs.
|
|
*
|
|
* Takes the accesses list and compares it with the trim. Any basic accesses that match the trim are
|
|
* added to basic_access_list and the rest are added to wildcard_access_list.
|
|
|
|
* This proc directly modifies the lists passed in as args. It expects these lists to be instantiated.
|
|
* There is no return value.
|
|
* Arguments:
|
|
* * accesses - List of accesses you want to stort into basic_access_list and wildcard_access_list. Should not be null.
|
|
* * basic_access_list - Mandatory argument. The proc modifies the list passed in this argument and adds accesses the trim supports to it.
|
|
* * wildcard_access_list - Mandatory argument. The proc modifies the list passed in this argument and adds accesses the trim does not support to it.
|
|
*/
|
|
/obj/item/card/id/proc/build_access_lists(list/accesses, list/basic_access_list, list/wildcard_access_list)
|
|
if(!length(accesses) || isnull(basic_access_list) || isnull(wildcard_access_list))
|
|
CRASH("Invalid parameters passed to build_access_lists")
|
|
|
|
var/list/trim_accesses = trim?.access
|
|
|
|
// Populate the lists.
|
|
for(var/new_access in accesses)
|
|
if(new_access in trim_accesses)
|
|
basic_access_list |= new_access
|
|
continue
|
|
|
|
wildcard_access_list |= new_access
|
|
|
|
/// Helper proc that determines if a card can be used in certain types of payment transactions.
|
|
/obj/item/card/id/proc/can_be_used_in_payment(mob/living/user)
|
|
if(QDELETED(src) || isnull(registered_account?.account_job) || !isliving(user))
|
|
return FALSE
|
|
|
|
return TRUE
|
|
|
|
/obj/item/card/id/attack_self(mob/user)
|
|
if(Adjacent(user))
|
|
var/minor
|
|
if(registered_name && registered_age && registered_age < AGE_MINOR)
|
|
minor = " <b>[registered_age]</b>" //SKYRAT EDIT CHANGE
|
|
user.visible_message(span_notice("[user] shows you: [icon2html(src, viewers(user))] [src.name][minor]."), span_notice("You show \the [src.name][minor]."))
|
|
add_fingerprint(user)
|
|
|
|
/obj/item/card/id/interact_with_atom_secondary(atom/interacting_with, mob/living/user, list/modifiers)
|
|
if(!check_allowed_items(interacting_with) || !isfloorturf(interacting_with))
|
|
return NONE
|
|
try_project_paystand(user, interacting_with)
|
|
return ITEM_INTERACT_SUCCESS
|
|
|
|
/obj/item/card/id/attack_self_secondary(mob/user, modifiers)
|
|
. = ..()
|
|
if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
|
|
return
|
|
try_project_paystand(user)
|
|
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
|
|
|
|
/obj/item/card/id/add_context(atom/source, list/context, obj/item/held_item, mob/user)
|
|
. = ..()
|
|
|
|
context[SCREENTIP_CONTEXT_LMB] = "Show ID"
|
|
context[SCREENTIP_CONTEXT_RMB] = "Project pay stand"
|
|
if(isnull(registered_account) || registered_account.replaceable) //Same check we use when we check if we can assign an account
|
|
context[SCREENTIP_CONTEXT_ALT_RMB] = "Assign account"
|
|
else if(registered_account.account_balance > 0)
|
|
context[SCREENTIP_CONTEXT_ALT_LMB] = "Withdraw credits"
|
|
if(trim && length(trim.honorifics))
|
|
context[SCREENTIP_CONTEXT_CTRL_LMB] = "Toggle honorific"
|
|
return CONTEXTUAL_SCREENTIP_SET
|
|
|
|
/obj/item/card/id/proc/try_project_paystand(mob/user, turf/target)
|
|
if(!COOLDOWN_FINISHED(src, last_holopay_projection))
|
|
balloon_alert(user, "still recharging")
|
|
return
|
|
if(!can_be_used_in_payment(user))
|
|
balloon_alert(user, "no account!")
|
|
to_chat(user, span_warning("You need a valid bank account to do this."))
|
|
return
|
|
/// Determines where the holopay will be placed based on tile contents
|
|
var/turf/projection
|
|
var/turf/step_ahead = get_step(user, user.dir)
|
|
var/turf/user_loc = user.loc
|
|
if(target && can_proj_holopay(target))
|
|
projection = target
|
|
else if(can_proj_holopay(step_ahead))
|
|
projection = step_ahead
|
|
else if(can_proj_holopay(user_loc))
|
|
projection = user_loc
|
|
if(!projection)
|
|
balloon_alert(user, "no space")
|
|
to_chat(user, span_warning("You need to be standing on or near an open tile to do this."))
|
|
return
|
|
/// Success: Valid tile for holopay placement
|
|
if(my_store)
|
|
my_store.dissipate()
|
|
var/obj/structure/holopay/new_store = new(projection)
|
|
if(new_store?.assign_card(projection, src))
|
|
COOLDOWN_START(src, last_holopay_projection, HOLOPAY_PROJECTION_INTERVAL)
|
|
playsound(projection, 'sound/effects/empulse.ogg', 40, TRUE)
|
|
my_store = new_store
|
|
|
|
/**
|
|
* Determines whether a new holopay can be placed on the given turf.
|
|
* Checks if there are dense contents, too many contents, or another
|
|
* holopay already exists on the turf.
|
|
*
|
|
* Arguments:
|
|
* * turf/target - The target turf to be checked for dense contents
|
|
* Returns:
|
|
* * TRUE if the target is a valid holopay location, FALSE otherwise.
|
|
*/
|
|
/obj/item/card/id/proc/can_proj_holopay(turf/target)
|
|
if(!isfloorturf(target))
|
|
return FALSE
|
|
if(target.density)
|
|
return FALSE
|
|
if(length(target.contents) > 5)
|
|
return FALSE
|
|
for(var/obj/checked_obj in target.contents)
|
|
if(checked_obj.density)
|
|
return FALSE
|
|
if(istype(checked_obj, /obj/structure/holopay))
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/**
|
|
* Setter for the shop logo on linked holopays
|
|
*
|
|
* Arguments:
|
|
* * new_logo - The new logo to be set.
|
|
*/
|
|
/obj/item/card/id/proc/set_holopay_logo(new_logo)
|
|
if(!available_logos.Find(new_logo))
|
|
CRASH("User input a holopay shop logo that didn't exist.")
|
|
holopay_logo = new_logo
|
|
|
|
/**
|
|
* Setter for changing the force fee on a holopay.
|
|
*
|
|
* Arguments:
|
|
* * new_fee - The new fee to be set.
|
|
*/
|
|
/obj/item/card/id/proc/set_holopay_fee(new_fee)
|
|
if(!isnum(new_fee))
|
|
CRASH("User input a non number into the holopay fee field.")
|
|
if(new_fee < holopay_min_fee || new_fee > holopay_max_fee)
|
|
CRASH("User input a number outside of the valid range into the holopay fee field.")
|
|
holopay_fee = new_fee
|
|
|
|
/**
|
|
* Setter for changing the holopay name.
|
|
*
|
|
* Arguments:
|
|
* * new_name - The new name to be set.
|
|
*/
|
|
/obj/item/card/id/proc/set_holopay_name(name)
|
|
if(length(name) < 3 || length(name) > MAX_NAME_LEN)
|
|
to_chat(usr, span_warning("Must be between 3 - 42 characters."))
|
|
else
|
|
holopay_name = html_encode(trim(name, MAX_NAME_LEN))
|
|
|
|
|
|
/obj/item/card/id/vv_edit_var(var_name, var_value)
|
|
. = ..()
|
|
if(.)
|
|
switch(var_name)
|
|
if(NAMEOF(src, assignment), NAMEOF(src, registered_name), NAMEOF(src, registered_age))
|
|
update_label()
|
|
update_icon()
|
|
if(NAMEOF(src, trim))
|
|
if(ispath(trim))
|
|
SSid_access.apply_trim_to_card(src, trim)
|
|
|
|
/obj/item/card/id/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
|
|
if(istype(tool, /obj/item/rupee))
|
|
to_chat(user, span_warning("Your ID smartly rejects the strange shard of glass. Who knew, apparently it's not ACTUALLY valuable!"))
|
|
return ITEM_INTERACT_BLOCKING
|
|
else if(iscash(tool))
|
|
return insert_money(tool, user) ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_BLOCKING
|
|
else if(istype(tool, /obj/item/storage/bag/money))
|
|
var/obj/item/storage/bag/money/money_bag = tool
|
|
var/list/money_contained = money_bag.contents
|
|
var/money_added = mass_insert_money(money_contained, user)
|
|
if(!money_added)
|
|
return ITEM_INTERACT_BLOCKING
|
|
to_chat(user, span_notice("You stuff the contents into the card! They disappear in a puff of bluespace smoke, adding [money_added] worth of credits to the linked account."))
|
|
return ITEM_INTERACT_SUCCESS
|
|
/// SKYRAT EDIT BEGINS - Trim Tokens - Proc defined in modular_skyrat/modules/trim_tokens/code/cards_id.dm
|
|
else if(istype(tool, /obj/item/trim_token))
|
|
apply_token(tool, user)
|
|
return
|
|
/// SKYRAT EDIT ENDS
|
|
return NONE
|
|
|
|
/**
|
|
* Insert credits or coins into the ID card and add their value to the associated bank account.
|
|
*
|
|
* Returns TRUE if the money was successfully inserted, FALSE otherwise.
|
|
* Arguments:
|
|
* money - The item to attempt to convert to credits and insert into the card.
|
|
* user - The user inserting the item.
|
|
* physical_currency - Boolean, whether this is a physical currency such as a coin and not a holochip.
|
|
*/
|
|
/obj/item/card/id/proc/insert_money(obj/item/money, mob/user)
|
|
var/physical_currency
|
|
if(istype(money, /obj/item/stack/spacecash) || istype(money, /obj/item/coin))
|
|
physical_currency = TRUE
|
|
|
|
if(!registered_account)
|
|
to_chat(user, span_warning("[src] doesn't have a linked account to deposit [money] into!"))
|
|
return FALSE
|
|
var/cash_money = money.get_item_credit_value()
|
|
if(!cash_money)
|
|
to_chat(user, span_warning("[money] doesn't seem to be worth anything!"))
|
|
return FALSE
|
|
registered_account.adjust_money(cash_money, "System: Deposit")
|
|
SSblackbox.record_feedback("amount", "credits_inserted", cash_money)
|
|
log_econ("[cash_money] credits were inserted into [src] owned by [src.registered_name]")
|
|
if(physical_currency)
|
|
to_chat(user, span_notice("You stuff [money] into [src]. It disappears in a small puff of bluespace smoke, adding [cash_money] credits to the linked account."))
|
|
else
|
|
to_chat(user, span_notice("You insert [money] into [src], adding [cash_money] credits to the linked account."))
|
|
|
|
to_chat(user, span_notice("The linked account now reports a balance of [registered_account.account_balance] cr."))
|
|
qdel(money)
|
|
return TRUE
|
|
|
|
/**
|
|
* Insert multiple money or money-equivalent items at once.
|
|
*
|
|
* Arguments:
|
|
* money - List of items to attempt to convert to credits and insert into the card.
|
|
* user - The user inserting the items.
|
|
*/
|
|
/obj/item/card/id/proc/mass_insert_money(list/money, mob/user)
|
|
if(!registered_account)
|
|
to_chat(user, span_warning("[src] doesn't have a linked account to deposit into!"))
|
|
return FALSE
|
|
|
|
if (!money || !length(money))
|
|
return FALSE
|
|
|
|
var/total = 0
|
|
|
|
for (var/obj/item/physical_money in money)
|
|
total += physical_money.get_item_credit_value()
|
|
CHECK_TICK
|
|
|
|
registered_account.adjust_money(total, "System: Deposit")
|
|
SSblackbox.record_feedback("amount", "credits_inserted", total)
|
|
log_econ("[total] credits were inserted into [src] owned by [src.registered_name]")
|
|
QDEL_LIST(money)
|
|
|
|
return total
|
|
|
|
/// Helper proc. Can the user alt-click the ID?
|
|
/obj/item/card/id/proc/alt_click_can_use_id(mob/living/user)
|
|
if(!isliving(user))
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/// Attempts to set a new bank account on the ID card.
|
|
/obj/item/card/id/proc/set_new_account(mob/living/user)
|
|
. = FALSE
|
|
var/datum/bank_account/old_account = registered_account
|
|
if(loc != user)
|
|
to_chat(user, span_warning("You must be holding the ID to continue!"))
|
|
return FALSE
|
|
var/list/user_memories = user.mind.memories
|
|
var/datum/memory/key/account/user_key = user_memories[/datum/memory/key/account]
|
|
var/default_account = (istype(user_key) && user_key.remembered_id) || 11111
|
|
var/new_bank_id = tgui_input_number(user, "Enter the account ID to associate with this card.", "Link Bank Account", default_account, 999999, 111111)
|
|
if(!new_bank_id || QDELETED(user) || QDELETED(src) || issilicon(user) || !alt_click_can_use_id(user) || loc != user)
|
|
return FALSE
|
|
if(registered_account?.account_id == new_bank_id)
|
|
to_chat(user, span_warning("The account ID was already assigned to this card."))
|
|
return FALSE
|
|
var/datum/bank_account/account = SSeconomy.bank_accounts_by_id["[new_bank_id]"]
|
|
if(isnull(account))
|
|
to_chat(user, span_warning("The account ID number provided is invalid."))
|
|
return FALSE
|
|
if(old_account)
|
|
old_account.bank_cards -= src
|
|
account.account_balance += old_account.account_balance
|
|
account.bank_cards += src
|
|
registered_account = account
|
|
to_chat(user, span_notice("The provided account has been linked to this ID card. It contains [account.account_balance] credits."))
|
|
return TRUE
|
|
|
|
/obj/item/card/id/click_alt(mob/living/user)
|
|
if(!alt_click_can_use_id(user))
|
|
return NONE
|
|
if(registered_account.account_debt)
|
|
var/choice = tgui_alert(user, "Choose An Action", "Bank Account", list("Withdraw", "Pay Debt"))
|
|
if(!choice || QDELETED(user) || QDELETED(src) || !alt_click_can_use_id(user) || loc != user)
|
|
return CLICK_ACTION_BLOCKING
|
|
if(choice == "Pay Debt")
|
|
pay_debt(user)
|
|
return CLICK_ACTION_SUCCESS
|
|
if (registered_account.being_dumped)
|
|
registered_account.bank_card_talk(span_warning("内部服务器错误"), TRUE)
|
|
return CLICK_ACTION_SUCCESS
|
|
if(loc != user)
|
|
to_chat(user, span_warning("You must be holding the ID to continue!"))
|
|
return CLICK_ACTION_BLOCKING
|
|
if(registered_account.replaceable && !registered_account.account_balance)
|
|
var/choice = tgui_alert(user, "This card's account is unassigned. Would you like to link a bank account?", "Bank Account", list("Link Account", "Leave Unassigned"))
|
|
if(!choice || QDELETED(user) || QDELETED(src) || !alt_click_can_use_id(user) || loc != user)
|
|
return CLICK_ACTION_BLOCKING
|
|
if(choice == "Link Account")
|
|
set_new_account(user)
|
|
return CLICK_ACTION_SUCCESS
|
|
var/amount_to_remove = tgui_input_number(user, "How much do you want to withdraw? (Max: [registered_account.account_balance] cr)", "Withdraw Funds", max_value = registered_account.account_balance)
|
|
if(!amount_to_remove || QDELETED(user) || QDELETED(src) || issilicon(user) || loc != user)
|
|
return CLICK_ACTION_BLOCKING
|
|
if(!alt_click_can_use_id(user))
|
|
return CLICK_ACTION_BLOCKING
|
|
if(registered_account.adjust_money(-amount_to_remove, "System: Withdrawal"))
|
|
var/obj/item/holochip/holochip = new (user.drop_location(), amount_to_remove)
|
|
user.put_in_hands(holochip)
|
|
to_chat(user, span_notice("You withdraw [amount_to_remove] credits into a holochip."))
|
|
SSblackbox.record_feedback("amount", "credits_removed", amount_to_remove)
|
|
log_econ("[amount_to_remove] credits were removed from [src] owned by [src.registered_name]")
|
|
return CLICK_ACTION_SUCCESS
|
|
else
|
|
var/difference = amount_to_remove - registered_account.account_balance
|
|
registered_account.bank_card_talk(span_warning("ERROR: The linked account requires [difference] more credit\s to perform that withdrawal."), TRUE)
|
|
return CLICK_ACTION_BLOCKING
|
|
|
|
/obj/item/card/id/click_alt_secondary(mob/user)
|
|
if(!alt_click_can_use_id(user))
|
|
return
|
|
if(!registered_account || registered_account.replaceable)
|
|
set_new_account(user)
|
|
|
|
/obj/item/card/id/proc/pay_debt(user)
|
|
var/amount_to_pay = tgui_input_number(user, "How much do you want to pay? (Max: [registered_account.account_balance] cr)", "Debt Payment", max_value = min(registered_account.account_balance, registered_account.account_debt))
|
|
if(!amount_to_pay || QDELETED(src) || loc != user || !alt_click_can_use_id(user))
|
|
return
|
|
var/prev_debt = registered_account.account_debt
|
|
var/amount_paid = registered_account.pay_debt(amount_to_pay)
|
|
if(amount_paid)
|
|
var/message = span_notice("You pay [amount_to_pay] credits of a [prev_debt] cr debt. [registered_account.account_debt] cr to go.")
|
|
if(!registered_account.account_debt)
|
|
message = span_nicegreen("You pay the last [amount_to_pay] credits of your debt, extinguishing it. Congratulations!")
|
|
to_chat(user, message)
|
|
|
|
/obj/item/card/id/examine(mob/user)
|
|
. = ..()
|
|
if(!user.can_read(src))
|
|
return
|
|
|
|
if(registered_account && !isnull(registered_account.account_id))
|
|
. += "The account linked to the ID belongs to '[registered_account.account_holder]' and reports a balance of [registered_account.account_balance] cr."
|
|
if(ACCESS_COMMAND in access)
|
|
var/datum/bank_account/linked_dept = SSeconomy.get_dep_account(registered_account.account_job.paycheck_department)
|
|
. += "The [linked_dept.account_holder] linked to the ID reports a balance of [linked_dept.account_balance] cr."
|
|
else
|
|
. += span_notice("Alt-Right-Click the ID to set the linked bank account.")
|
|
|
|
if(HAS_TRAIT(user, TRAIT_ID_APPRAISER))
|
|
. += HAS_TRAIT(src, TRAIT_JOB_FIRST_ID_CARD) ? span_boldnotice("Hmm... yes, this ID was issued from Central Command!") : span_boldnotice("This ID was created in this sector, not by Central Command.")
|
|
if(HAS_TRAIT(src, TRAIT_TASTEFULLY_THICK_ID_CARD) && (user.is_holding(src) || (user.CanReach(src) && user.put_in_hands(src, ignore_animation = FALSE))))
|
|
ADD_TRAIT(src, TRAIT_NODROP, "psycho")
|
|
. += span_hypnophrase("Look at that subtle coloring... The tasteful thickness of it. Oh my God, it even has a watermark...")
|
|
var/sound/slowbeat = sound('sound/effects/health/slowbeat.ogg', repeat = TRUE)
|
|
user.playsound_local(get_turf(src), slowbeat, 40, 0, channel = CHANNEL_HEARTBEAT, use_reverb = FALSE)
|
|
if(isliving(user))
|
|
var/mob/living/living_user = user
|
|
living_user.adjust_jitter(10 SECONDS)
|
|
addtimer(CALLBACK(src, PROC_REF(drop_card), user), 10 SECONDS)
|
|
. += span_notice("<i>There's more information below, you can look again to take a closer look...</i>")
|
|
|
|
/obj/item/card/id/proc/drop_card(mob/user)
|
|
user.stop_sound_channel(CHANNEL_HEARTBEAT)
|
|
REMOVE_TRAIT(src, TRAIT_NODROP, "psycho")
|
|
if(user.is_holding(src))
|
|
user.dropItemToGround(src)
|
|
for(var/mob/living/carbon/human/viewing_mob in viewers(user, 2))
|
|
if(viewing_mob.stat || viewing_mob == user)
|
|
continue
|
|
viewing_mob.say("Is something wrong? [first_name(user.name)]... you're sweating.", forced = "psycho")
|
|
break
|
|
|
|
/obj/item/card/id/examine_more(mob/user)
|
|
. = ..()
|
|
if(!user.can_read(src))
|
|
return
|
|
|
|
. += span_notice("<i>You examine [src] closer, and note the following...</i>")
|
|
|
|
if(registered_age)
|
|
. += "The card indicates that the holder is [registered_age] years old. [(registered_age < AGE_MINOR) ? "There's a holographic stripe that reads <b>[span_danger("'MINOR: DO NOT SERVE ALCOHOL OR TOBACCO'")]</b> along the bottom of the card." : ""]"
|
|
if(registered_account)
|
|
if(registered_account.mining_points)
|
|
. += "There's [registered_account.mining_points] mining point\s loaded onto the card's bank account."
|
|
. += "The account linked to the ID belongs to '[registered_account.account_holder]' and reports a balance of [registered_account.account_balance] cr."
|
|
if(registered_account.account_debt)
|
|
. += span_warning("The account is currently indebted for [registered_account.account_debt] cr. [100*DEBT_COLLECTION_COEFF]% of all earnings will go towards extinguishing it.")
|
|
if(registered_account.account_job)
|
|
var/datum/bank_account/D = SSeconomy.get_dep_account(registered_account.account_job.paycheck_department)
|
|
if(D)
|
|
. += "The [D.account_holder] reports a balance of [D.account_balance] cr."
|
|
. += span_info("Alt-Click the ID to pull money from the linked account in the form of holochips.")
|
|
. += span_info("You can insert credits into the linked account by pressing holochips, cash, or coins against the ID.")
|
|
if(registered_account.replaceable)
|
|
. += span_info("Alt-Right-Click the ID to change the linked bank account.")
|
|
if(registered_account.civilian_bounty)
|
|
. += span_info("<b>There is an active civilian bounty.</b>")
|
|
. += span_info("<i>[registered_account.bounty_text()]</i>")
|
|
. += span_info("Quantity: [registered_account.bounty_num()]")
|
|
. += span_info("Reward: [registered_account.bounty_value()]")
|
|
if(registered_account.account_holder == user.real_name)
|
|
. += span_boldnotice("If you lose this ID card, you can reclaim your account by Alt-Clicking a blank ID card while holding it and entering your account ID number.")
|
|
else
|
|
. += span_info("There is no registered account linked to this card. Alt-Click to add one.")
|
|
|
|
return .
|
|
|
|
/obj/item/card/id/GetAccess()
|
|
return access.Copy()
|
|
|
|
/obj/item/card/id/GetID()
|
|
return src
|
|
|
|
/obj/item/card/id/RemoveID()
|
|
return src
|
|
|
|
/// Called on COMSIG_ATOM_UPDATED_ICON. Updates the visuals of the wallet this card is in.
|
|
/obj/item/card/id/proc/update_in_wallet()
|
|
SIGNAL_HANDLER
|
|
|
|
if(istype(loc, /obj/item/storage/wallet))
|
|
var/obj/item/storage/wallet/powergaming = loc
|
|
if(powergaming.front_id == src)
|
|
powergaming.update_label()
|
|
powergaming.update_appearance()
|
|
|
|
/// Updates the name based on the card's vars and state.
|
|
/obj/item/card/id/proc/update_label()
|
|
var/name_string
|
|
if(registered_name)
|
|
if(trim && (honorific_position & ~HONORIFIC_POSITION_NONE))
|
|
name_string = "[update_honorific()]'s ID Card"
|
|
else
|
|
name_string = "[registered_name]'s ID Card"
|
|
else
|
|
name_string = initial(name)
|
|
|
|
var/assignment_string= get_job_title() // BUBBER EDIT: Adds a new proc which is used to get a job title
|
|
|
|
/* BUBBER EDIT: Moved to `get_job_title()`
|
|
if(is_intern)
|
|
if(assignment)
|
|
assignment_string = trim?.intern_alt_name || "Intern [assignment]"
|
|
else
|
|
assignment_string = "Intern"
|
|
else
|
|
assignment_string = assignment
|
|
*/
|
|
|
|
name = "[name_string] ([assignment_string])"
|
|
|
|
/// Re-generates the honorific title. Returns the compiled honorific_title value
|
|
/obj/item/card/id/proc/update_honorific()
|
|
switch(honorific_position)
|
|
if(HONORIFIC_POSITION_FIRST)
|
|
honorific_title = "[chosen_honorific] [first_name(registered_name)]"
|
|
if(HONORIFIC_POSITION_LAST)
|
|
honorific_title = "[chosen_honorific] [last_name(registered_name)]"
|
|
if(HONORIFIC_POSITION_FIRST_FULL)
|
|
honorific_title = "[chosen_honorific] [registered_name]"
|
|
if(HONORIFIC_POSITION_LAST_FULL)
|
|
honorific_title = "[registered_name][chosen_honorific]"
|
|
return honorific_title
|
|
|
|
/// Returns the trim assignment name.
|
|
/obj/item/card/id/proc/get_trim_assignment()
|
|
return trim?.assignment || assignment
|
|
|
|
/// Returns the trim sechud icon state.
|
|
/obj/item/card/id/proc/get_trim_sechud_icon_state()
|
|
return trim?.sechud_icon_state || SECHUD_UNKNOWN
|
|
|
|
/obj/item/card/id/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
|
|
if(iscash(interacting_with))
|
|
return insert_money(interacting_with, user) ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_BLOCKING
|
|
return NONE
|
|
|
|
/obj/item/card/id/item_ctrl_click(mob/user)
|
|
if(!in_contents_of(user) || user.incapacitated) //Check if the ID is in the ID slot, so it can be changed from there too.
|
|
return
|
|
|
|
if(!trim)
|
|
balloon_alert(user, "card has no trim!")
|
|
return
|
|
|
|
if(!length(trim.honorifics))
|
|
balloon_alert(user, "card has no honorific to use!")
|
|
return
|
|
|
|
var/list/choices = list()
|
|
var/list/readable_names = HONORIFIC_POSITION_BITFIELDS()
|
|
for(var/i in readable_names) //Filter out the options you don't have on your ID.
|
|
if(trim.honorific_positions & readable_names[i]) //If the positions list has the same bit value as the readable list.
|
|
choices += i
|
|
|
|
var/chosen_position = tgui_input_list(user, "What position do you want your honorific in?", "Flair!", choices)
|
|
if(user.incapacitated || !in_contents_of(user))
|
|
return
|
|
var/honorific_position_to_use = readable_names[chosen_position]
|
|
|
|
honorific_position = initial(honorific_position) //In case you want to force an honorific on an ID, set a default that won't always be NONE.
|
|
honorific_title = null //We reset this regardless so that we don't stack titles on accident.
|
|
|
|
if(honorific_position_to_use & HONORIFIC_POSITION_NONE)
|
|
balloon_alert(user, "honorific disabled")
|
|
else
|
|
var/new_honorific = tgui_input_list(user, "What honorific do you want to use?", "Flair!!!", trim.honorifics)
|
|
if(!new_honorific || user.incapacitated || !in_contents_of(user))
|
|
return
|
|
chosen_honorific = new_honorific
|
|
switch(honorific_position_to_use)
|
|
if(HONORIFIC_POSITION_FIRST)
|
|
honorific_position = HONORIFIC_POSITION_FIRST
|
|
balloon_alert(user, "honorific set: display first name")
|
|
if(HONORIFIC_POSITION_LAST)
|
|
honorific_position = HONORIFIC_POSITION_LAST
|
|
balloon_alert(user, "honorific set: display last name")
|
|
if(HONORIFIC_POSITION_FIRST_FULL)
|
|
honorific_position = HONORIFIC_POSITION_FIRST_FULL
|
|
balloon_alert(user, "honorific set: start of full name")
|
|
if(HONORIFIC_POSITION_LAST_FULL)
|
|
honorific_position = HONORIFIC_POSITION_LAST_FULL
|
|
balloon_alert(user, "honorific set: end of full name")
|
|
|
|
update_label()
|
|
|
|
/obj/item/card/id/away
|
|
name = "\proper a perfectly generic identification card"
|
|
desc = "A perfectly generic identification card. Looks like it could use some flavor."
|
|
trim = /datum/id_trim/away
|
|
icon_state = "retro"
|
|
registered_age = null
|
|
|
|
/obj/item/card/id/away/hotel
|
|
name = "Staff ID"
|
|
desc = "A staff ID used to access the hotel's doors."
|
|
trim = /datum/id_trim/away/hotel
|
|
|
|
/obj/item/card/id/away/hotel/security
|
|
name = "Officer ID"
|
|
trim = /datum/id_trim/away/hotel/security
|
|
|
|
/obj/item/card/id/away/old
|
|
name = "\proper a perfectly generic identification card"
|
|
desc = "A perfectly generic identification card. Looks like it could use some flavor."
|
|
|
|
/obj/item/card/id/away/old/sec
|
|
name = "Charlie Station Security Officer's ID card"
|
|
desc = "A faded Charlie Station ID card. You can make out the rank \"Security Officer\"."
|
|
trim = /datum/id_trim/away/old/sec
|
|
|
|
/obj/item/card/id/away/old/sci
|
|
name = "Charlie Station Scientist's ID card"
|
|
desc = "A faded Charlie Station ID card. You can make out the rank \"Scientist\"."
|
|
trim = /datum/id_trim/away/old/sci
|
|
|
|
/obj/item/card/id/away/old/eng
|
|
name = "Charlie Station Engineer's ID card"
|
|
desc = "A faded Charlie Station ID card. You can make out the rank \"Station Engineer\"."
|
|
trim = /datum/id_trim/away/old/eng
|
|
|
|
/obj/item/card/id/away/old/equipment
|
|
name = "Engineering Equipment Access"
|
|
desc = "A special ID card that allows access to engineering equipment."
|
|
trim = /datum/id_trim/away/old/equipment
|
|
|
|
/obj/item/card/id/away/old/robo
|
|
name = "Delta Station Roboticist's ID card"
|
|
desc = "An ID card that allows access to bots maintenance protocols."
|
|
trim = /datum/id_trim/away/old/robo
|
|
|
|
/obj/item/card/id/away/deep_storage //deepstorage.dmm space ruin
|
|
name = "bunker access ID"
|
|
|
|
/obj/item/card/id/away/filmstudio
|
|
name = "Film Studio ID"
|
|
desc = "An ID card that allows access to the variety of airlocks present in the film studio"
|
|
|
|
/obj/item/card/id/departmental_budget
|
|
name = "departmental card (ERROR)"
|
|
desc = "Provides access to the departmental budget."
|
|
icon_state = "budgetcard"
|
|
var/department_ID = ACCOUNT_CIV
|
|
var/department_name = ACCOUNT_CIV_NAME
|
|
registered_age = null
|
|
|
|
/obj/item/card/id/departmental_budget/Initialize(mapload)
|
|
. = ..()
|
|
var/datum/bank_account/B = SSeconomy.get_dep_account(department_ID)
|
|
if(B)
|
|
registered_account = B
|
|
if(!B.bank_cards.Find(src))
|
|
B.bank_cards += src
|
|
name = "departmental card ([department_name])"
|
|
desc = "Provides access to the [department_name]."
|
|
SSeconomy.dep_cards += src
|
|
|
|
/obj/item/card/id/departmental_budget/Destroy()
|
|
SSeconomy.dep_cards -= src
|
|
return ..()
|
|
|
|
/obj/item/card/id/departmental_budget/update_label()
|
|
return
|
|
|
|
/obj/item/card/id/departmental_budget/car
|
|
department_ID = ACCOUNT_CAR
|
|
department_name = ACCOUNT_CAR_NAME
|
|
icon_state = "car_budget" //saving up for a new tesla
|
|
|
|
/obj/item/card/id/departmental_budget/click_alt(mob/living/user)
|
|
registered_account.bank_card_talk(span_warning("Withdrawing is not compatible with this card design."), TRUE) //prevents the vault bank machine being useless and putting money from the budget to your card to go over personal crates
|
|
return CLICK_ACTION_BLOCKING
|
|
|
|
/obj/item/card/id/advanced
|
|
name = "identification card"
|
|
desc = "A card used to provide ID and determine access across the station. Has an integrated digital display and advanced microchips."
|
|
icon_state = "card_grey"
|
|
|
|
wildcard_slots = WILDCARD_LIMIT_GREY
|
|
flags_1 = UNPAINTABLE_1
|
|
|
|
/// An overlay icon state for when the card is assigned to a name. Usually manifests itself as a little scribble to the right of the job icon.
|
|
var/assigned_icon_state = "assigned"
|
|
|
|
/// If this is set, will manually override the icon file for the trim. Intended for admins to VV edit and chameleon ID cards.
|
|
var/trim_icon_override
|
|
/// If this is set, will manually override the icon state for the trim. Intended for admins to VV edit and chameleon ID cards.
|
|
var/trim_state_override
|
|
/// If this is set, will manually override the department color for this trim. Intended for admins to VV edit and chameleon ID cards.
|
|
var/department_color_override
|
|
/// If this is set, will manually override the department icon state for the trim. Intended for admins to VV edit and chameleon ID cards.
|
|
var/department_state_override
|
|
/// If this is set, will manually override the subdepartment color for this trim. Intended for admins to VV edit and chameleon ID cards.
|
|
var/subdepartment_color_override
|
|
/// If this is set, will manually override the trim's assignmment as it appears in the crew monitor and elsewhere. Intended for admins to VV edit and chameleon ID cards.
|
|
var/trim_assignment_override
|
|
/// If this is set, will manually override the trim shown for SecHUDs. Intended for admins to VV edit and chameleon ID cards.
|
|
var/sechud_icon_state_override = null
|
|
|
|
/obj/item/card/id/advanced/Initialize(mapload)
|
|
. = ..()
|
|
RegisterSignal(src, COMSIG_ITEM_EQUIPPED, PROC_REF(update_intern_status))
|
|
RegisterSignal(src, COMSIG_ITEM_DROPPED, PROC_REF(remove_intern_status))
|
|
|
|
/obj/item/card/id/advanced/Destroy()
|
|
UnregisterSignal(src, list(COMSIG_ITEM_EQUIPPED, COMSIG_ITEM_DROPPED))
|
|
|
|
return ..()
|
|
|
|
/obj/item/card/id/advanced/proc/after_input_check(mob/user)
|
|
if(QDELETED(user) || QDELETED(src) || !user.client || !user.can_perform_action(src, NEED_DEXTERITY|FORBID_TELEKINESIS_REACH))
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/obj/item/card/id/advanced/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
|
|
. = ..()
|
|
if(.)
|
|
return .
|
|
|
|
if(istype(tool, /obj/item/toy/crayon))
|
|
return recolor_id(user, tool)
|
|
|
|
/obj/item/card/id/advanced/proc/recolor_id(mob/living/user, obj/item/toy/crayon/our_crayon)
|
|
if(our_crayon.is_capped)
|
|
balloon_alert(user, "take the cap off first!")
|
|
return ITEM_INTERACT_BLOCKING
|
|
var/choice = tgui_alert(usr, "Recolor Department or Subdepartment?", "Recoloring ID...", list("Department", "Subdepartment"))
|
|
if(isnull(choice) \
|
|
|| QDELETED(user) \
|
|
|| QDELETED(src) \
|
|
|| QDELETED(our_crayon) \
|
|
|| !usr.can_perform_action(src, ALLOW_RESTING) \
|
|
|| !usr.can_perform_action(our_crayon, ALLOW_RESTING) \
|
|
)
|
|
return ITEM_INTERACT_BLOCKING
|
|
|
|
switch(choice)
|
|
if("Department")
|
|
if(!do_after(user, 2 SECONDS))
|
|
return ITEM_INTERACT_BLOCKING
|
|
department_color_override = our_crayon.paint_color
|
|
balloon_alert(user, "recolored")
|
|
if("Subdepartment")
|
|
if(!do_after(user, 1 SECONDS))
|
|
return ITEM_INTERACT_BLOCKING
|
|
subdepartment_color_override = our_crayon.paint_color
|
|
balloon_alert(user, "recolored")
|
|
update_icon()
|
|
return ITEM_INTERACT_SUCCESS
|
|
|
|
/obj/item/card/id/advanced/on_loc_equipped(datum/source, mob/equipper, slot)
|
|
. = ..()
|
|
if(istype(loc, /obj/item/storage/wallet) || istype(loc, /obj/item/modular_computer))
|
|
update_intern_status(source, equipper, slot)
|
|
|
|
/obj/item/card/id/advanced/on_loc_dropped(datum/source, mob/dropper)
|
|
. = ..()
|
|
if(istype(loc, /obj/item/storage/wallet) || istype(loc, /obj/item/modular_computer))
|
|
remove_intern_status(source, dropper)
|
|
|
|
/obj/item/card/id/advanced/proc/update_intern_status(datum/source, mob/user, slot)
|
|
SIGNAL_HANDLER
|
|
|
|
if(!user?.client)
|
|
return
|
|
if(!CONFIG_GET(flag/use_exp_tracking))
|
|
return
|
|
if(!CONFIG_GET(flag/use_low_living_hour_intern))
|
|
return
|
|
if(!SSdbcore.Connect())
|
|
return
|
|
|
|
var/intern_threshold = (CONFIG_GET(number/use_low_living_hour_intern_hours) * 60) || (CONFIG_GET(number/use_exp_restrictions_heads_hours) * 60) || INTERN_THRESHOLD_FALLBACK_HOURS * 60
|
|
var/playtime = user.client.get_exp_living(pure_numeric = TRUE)
|
|
|
|
if((intern_threshold >= playtime) && (user.mind?.assigned_role.job_flags & JOB_CAN_BE_INTERN))
|
|
is_intern = TRUE
|
|
update_label()
|
|
return
|
|
|
|
if(!is_intern)
|
|
return
|
|
|
|
is_intern = FALSE
|
|
update_label()
|
|
|
|
/obj/item/card/id/advanced/proc/remove_intern_status(datum/source, mob/user)
|
|
SIGNAL_HANDLER
|
|
|
|
if(!is_intern)
|
|
return
|
|
|
|
is_intern = FALSE
|
|
update_label()
|
|
|
|
/obj/item/card/id/advanced/update_overlays()
|
|
. = ..()
|
|
|
|
if(registered_name && registered_name != "Captain")
|
|
. += mutable_appearance(icon, assigned_icon_state)
|
|
|
|
var/trim_icon_file = trim_icon_override ? trim_icon_override : trim?.trim_icon
|
|
var/trim_icon_state = trim_state_override ? trim_state_override : trim?.trim_state
|
|
var/trim_department_color = department_color_override ? department_color_override : trim?.department_color
|
|
var/trim_department_state = department_state_override ? department_state_override : trim?.department_state
|
|
var/trim_subdepartment_color = subdepartment_color_override ? subdepartment_color_override : trim?.subdepartment_color
|
|
|
|
if(!trim_icon_file || !trim_icon_state || !trim_department_color || !trim_subdepartment_color || !trim_department_state)
|
|
return
|
|
|
|
/// We handle department and subdepartment overlays first, so the job icon is always on top.
|
|
var/mutable_appearance/department_overlay = mutable_appearance(trim_icon_file, trim_department_state)
|
|
department_overlay.color = trim_department_color
|
|
. += department_overlay
|
|
|
|
var/mutable_appearance/subdepartment_overlay = mutable_appearance(trim_icon_file, "subdepartment")
|
|
subdepartment_overlay.color = trim_subdepartment_color
|
|
. += subdepartment_overlay
|
|
|
|
/// Then we handle the job's icon here.
|
|
. += mutable_appearance(trim_icon_file, trim_icon_state)
|
|
|
|
/obj/item/card/id/advanced/get_trim_assignment()
|
|
if(trim_assignment_override)
|
|
return trim_assignment_override
|
|
|
|
if(ispath(trim))
|
|
var/datum/id_trim/trim_singleton = SSid_access.trim_singletons_by_path[trim]
|
|
return trim_singleton.assignment
|
|
|
|
return ..()
|
|
|
|
/// Returns the trim sechud icon state.
|
|
/obj/item/card/id/advanced/get_trim_sechud_icon_state()
|
|
return sechud_icon_state_override || ..()
|
|
|
|
/obj/item/card/id/advanced/rainbow
|
|
name = "rainbow identification card"
|
|
desc = "A rainbow card, promoting fun in a 'business proper' sense!"
|
|
icon_state = "card_rainbow"
|
|
|
|
/obj/item/card/id/advanced/silver
|
|
name = "silver identification card"
|
|
desc = "A silver card which shows honour and dedication."
|
|
icon_state = "card_silver"
|
|
inhand_icon_state = "silver_id"
|
|
assigned_icon_state = "assigned_silver"
|
|
wildcard_slots = WILDCARD_LIMIT_SILVER
|
|
|
|
/obj/item/card/id/advanced/robotic
|
|
name = "magnetic identification card"
|
|
desc = "An integrated card which shows the work poured into opening doors."
|
|
icon_state = "card_carp" //im not a spriter
|
|
inhand_icon_state = "silver_id"
|
|
assigned_icon_state = "assigned_silver"
|
|
wildcard_slots = WILDCARD_LIMIT_GREY
|
|
|
|
/datum/id_trim/maint_reaper
|
|
access = list(ACCESS_MAINT_TUNNELS)
|
|
trim_state = "trim_janitor"
|
|
assignment = "Reaper"
|
|
|
|
/obj/item/card/id/advanced/silver/reaper
|
|
name = "Thirteen's ID Card (Reaper)"
|
|
trim = /datum/id_trim/maint_reaper
|
|
registered_name = "Thirteen"
|
|
|
|
/obj/item/card/id/advanced/gold
|
|
name = "gold identification card"
|
|
desc = "A golden card which shows power and might."
|
|
icon_state = "card_gold"
|
|
inhand_icon_state = "gold_id"
|
|
assigned_icon_state = "assigned_gold"
|
|
wildcard_slots = WILDCARD_LIMIT_GOLD
|
|
|
|
/obj/item/card/id/advanced/gold/Initialize(mapload)
|
|
. = ..()
|
|
ADD_TRAIT(src, TRAIT_TASTEFULLY_THICK_ID_CARD, ROUNDSTART_TRAIT)
|
|
|
|
/obj/item/card/id/advanced/gold/captains_spare
|
|
name = "captain's spare ID"
|
|
desc = "The spare ID of the High Lord himself."
|
|
registered_name = "Captain"
|
|
trim = /datum/id_trim/job/captain
|
|
registered_age = null
|
|
|
|
/obj/item/card/id/advanced/gold/captains_spare/update_label() //so it doesn't change to Captain's ID card (Captain) on a sneeze
|
|
if(registered_name == "Captain")
|
|
name = "[initial(name)][(!assignment || assignment == "Captain") ? "" : " ([assignment])"]"
|
|
update_appearance(UPDATE_ICON)
|
|
else
|
|
..()
|
|
|
|
/obj/item/card/id/advanced/centcom
|
|
name = "\improper CentCom ID"
|
|
desc = "An ID straight from Central Command."
|
|
icon_state = "card_centcom"
|
|
assigned_icon_state = "assigned_centcom"
|
|
registered_name = JOB_CENTCOM
|
|
registered_age = null
|
|
trim = /datum/id_trim/centcom
|
|
wildcard_slots = WILDCARD_LIMIT_CENTCOM
|
|
|
|
/obj/item/card/id/advanced/centcom/ert
|
|
name = "\improper CentCom ID"
|
|
desc = "An ERT ID card."
|
|
registered_age = null
|
|
registered_name = "Emergency Response Intern"
|
|
trim = /datum/id_trim/centcom/ert
|
|
|
|
/obj/item/card/id/advanced/centcom/ert
|
|
registered_name = JOB_ERT_COMMANDER
|
|
trim = /datum/id_trim/centcom/ert/commander
|
|
|
|
/obj/item/card/id/advanced/centcom/ert/security
|
|
registered_name = JOB_ERT_OFFICER
|
|
trim = /datum/id_trim/centcom/ert/security
|
|
|
|
/obj/item/card/id/advanced/centcom/ert/engineer
|
|
registered_name = JOB_ERT_ENGINEER
|
|
trim = /datum/id_trim/centcom/ert/engineer
|
|
|
|
/obj/item/card/id/advanced/centcom/ert/medical
|
|
registered_name = JOB_ERT_MEDICAL_DOCTOR
|
|
trim = /datum/id_trim/centcom/ert/medical
|
|
|
|
/obj/item/card/id/advanced/centcom/ert/chaplain
|
|
registered_name = JOB_ERT_CHAPLAIN
|
|
trim = /datum/id_trim/centcom/ert/chaplain
|
|
|
|
/obj/item/card/id/advanced/centcom/ert/janitor
|
|
registered_name = JOB_ERT_JANITOR
|
|
trim = /datum/id_trim/centcom/ert/janitor
|
|
|
|
/obj/item/card/id/advanced/centcom/ert/clown
|
|
registered_name = JOB_ERT_CLOWN
|
|
trim = /datum/id_trim/centcom/ert/clown
|
|
|
|
/obj/item/card/id/advanced/centcom/ert/militia
|
|
registered_name = "Frontier Militia"
|
|
trim = /datum/id_trim/centcom/ert/militia
|
|
|
|
/obj/item/card/id/advanced/centcom/ert/militia/general
|
|
registered_name = "Frontier Militia General"
|
|
trim = /datum/id_trim/centcom/ert/militia/general
|
|
|
|
/obj/item/card/id/advanced/black
|
|
name = "black identification card"
|
|
desc = "This card is telling you one thing and one thing alone. The person holding this card is an utter badass."
|
|
icon_state = "card_black"
|
|
assigned_icon_state = "assigned_syndicate"
|
|
wildcard_slots = WILDCARD_LIMIT_GOLD
|
|
|
|
/obj/item/card/id/advanced/black/deathsquad
|
|
name = "\improper Death Squad ID"
|
|
desc = "A Death Squad ID card."
|
|
registered_name = JOB_ERT_DEATHSQUAD
|
|
trim = /datum/id_trim/centcom/deathsquad
|
|
wildcard_slots = WILDCARD_LIMIT_DEATHSQUAD
|
|
|
|
/obj/item/card/id/advanced/black/syndicate_command
|
|
name = "syndicate ID card"
|
|
desc = "An ID straight from the Syndicate."
|
|
registered_name = "Syndicate"
|
|
registered_age = null
|
|
trim = /datum/id_trim/syndicom
|
|
wildcard_slots = WILDCARD_LIMIT_SYNDICATE
|
|
|
|
/obj/item/card/id/advanced/black/syndicate_command/crew_id
|
|
name = "syndicate ID card"
|
|
desc = "An ID straight from the Syndicate."
|
|
registered_name = "Syndicate"
|
|
trim = /datum/id_trim/syndicom/crew
|
|
|
|
/obj/item/card/id/advanced/black/syndicate_command/captain_id
|
|
name = "syndicate captain ID card"
|
|
desc = "An ID straight from the Syndicate."
|
|
registered_name = "Syndicate"
|
|
trim = /datum/id_trim/syndicom/captain
|
|
|
|
|
|
/obj/item/card/id/advanced/black/syndicate_command/captain_id/syndie_spare
|
|
name = "syndicate captain's spare ID"
|
|
desc = "The spare ID of the Dark Lord himself."
|
|
registered_name = "Captain"
|
|
registered_age = null
|
|
|
|
/obj/item/card/id/advanced/black/syndicate_command/captain_id/syndie_spare/update_label()
|
|
if(registered_name == "Captain")
|
|
name = "[initial(name)][(!assignment || assignment == "Captain") ? "" : " ([assignment])"]"
|
|
update_appearance(UPDATE_ICON)
|
|
return
|
|
|
|
return ..()
|
|
|
|
/obj/item/card/id/advanced/debug
|
|
name = "\improper Debug ID"
|
|
desc = "A debug ID card. Has ALL the all access and a boatload of money, you really shouldn't have this."
|
|
icon_state = "card_centcom"
|
|
assigned_icon_state = "assigned_centcom"
|
|
trim = /datum/id_trim/admin
|
|
wildcard_slots = WILDCARD_LIMIT_ADMIN
|
|
|
|
/obj/item/card/id/advanced/debug/Initialize(mapload)
|
|
. = ..()
|
|
registered_account = new(player_account = FALSE)
|
|
registered_account.account_id = ADMIN_ACCOUNT_ID // this is so bank_card_talk() can work.
|
|
registered_account.account_job = SSjob.get_job_type(/datum/job/admin)
|
|
registered_account.account_balance += 999999 // MONEY! We add more money to the account every time we spawn because it's a debug item and infinite money whoopie
|
|
|
|
/obj/item/card/id/advanced/debug/alt_click_can_use_id(mob/living/user)
|
|
. = ..()
|
|
if(!. || isnull(user.client?.holder)) // admins only as a safety so people don't steal all the dollars. spawn in a holochip if you want them to get some dosh
|
|
registered_account.bank_card_talk(span_warning("Only authorized representatives of Nanotrasen may use this card."), force = TRUE)
|
|
return FALSE
|
|
|
|
return TRUE
|
|
|
|
/obj/item/card/id/advanced/debug/can_be_used_in_payment(mob/living/user)
|
|
. = ..()
|
|
if(!. || isnull(user.client?.holder))
|
|
registered_account.bank_card_talk(span_warning("Only authorized representatives of Nanotrasen may use this card."), force = TRUE)
|
|
return FALSE
|
|
|
|
return TRUE
|
|
|
|
/obj/item/card/id/advanced/prisoner
|
|
name = "prisoner ID card"
|
|
desc = "You are a number, you are not a free man."
|
|
icon_state = "card_prisoner"
|
|
inhand_icon_state = "orange-id"
|
|
registered_name = "Scum"
|
|
registered_age = null
|
|
trim = /datum/id_trim/job/prisoner
|
|
|
|
wildcard_slots = WILDCARD_LIMIT_PRISONER
|
|
|
|
/// Number of gulag points required to earn freedom.
|
|
var/goal = 0
|
|
/// Number of gulag points earned.
|
|
var/points = 0
|
|
/// If the card has a timer set on it for temporary stay.
|
|
var/timed = FALSE
|
|
/// Time to assign to the card when they pass through the security gate.
|
|
var/time_to_assign
|
|
/// Time left on a card till they can leave.
|
|
var/time_left = 0
|
|
|
|
/obj/item/card/id/advanced/prisoner/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
|
|
. = ..()
|
|
if(.)
|
|
return .
|
|
|
|
if(isidcard(tool))
|
|
return set_sentence_time(user, tool)
|
|
|
|
/obj/item/card/id/advanced/prisoner/proc/set_sentence_time(mob/living/user, obj/item/card/id/our_card)
|
|
var/list/id_access = our_card.GetAccess()
|
|
if(!(ACCESS_BRIG in id_access))
|
|
balloon_alert(user, "access denied!")
|
|
return ITEM_INTERACT_BLOCKING
|
|
if(!user.is_holding(src))
|
|
to_chat(user, span_warning("You must be holding the ID to continue!"))
|
|
return ITEM_INTERACT_BLOCKING
|
|
|
|
if(timed) // If we already have a time set, reset the card
|
|
timed = FALSE
|
|
time_to_assign = initial(time_to_assign)
|
|
registered_name = initial(registered_name)
|
|
STOP_PROCESSING(SSobj, src)
|
|
to_chat(user, "Resetting prisoner ID to default parameters.")
|
|
return ITEM_INTERACT_SUCCESS
|
|
|
|
var/choice = tgui_input_number(user, "Sentence time in seconds", "Sentencing")
|
|
if(isnull(choice) || QDELETED(user) || QDELETED(src) || !user.can_perform_action(src, FORBID_TELEKINESIS_REACH) || !user.is_holding(src))
|
|
return ITEM_INTERACT_BLOCKING
|
|
time_to_assign = choice
|
|
to_chat(user, "You set the sentence time to [DisplayTimeText(time_to_assign * 10)].")
|
|
timed = TRUE
|
|
return ITEM_INTERACT_SUCCESS
|
|
|
|
/obj/item/card/id/advanced/prisoner/proc/start_timer()
|
|
say("Sentence started, welcome to the corporate rehabilitation center!")
|
|
START_PROCESSING(SSobj, src)
|
|
|
|
/obj/item/card/id/advanced/prisoner/examine(mob/user)
|
|
. = ..()
|
|
if(!.)
|
|
return
|
|
|
|
if(timed)
|
|
if(time_to_assign > 0)
|
|
. += span_notice("The digital timer on the card is set to [DisplayTimeText(time_to_assign * 10)]. The timer will start once the prisoner passes through the prison gate scanners.")
|
|
else if(time_left <= 0)
|
|
. += span_notice("The digital timer on the card has zero seconds remaining. You leave a changed man, but a free man nonetheless.")
|
|
else
|
|
. += span_notice("The digital timer on the card has [DisplayTimeText(time_left * 10)] remaining. Don't do the crime if you can't do the time.")
|
|
|
|
. += span_notice("[EXAMINE_HINT("Swipe")] a security ID on the card to [timed ? "re" : ""]set the genpop sentence time.")
|
|
. += span_notice("Remember to [EXAMINE_HINT("swipe")] the card on a genpop locker to link it.")
|
|
|
|
/obj/item/card/id/advanced/prisoner/process(seconds_per_tick)
|
|
if(!timed)
|
|
return
|
|
time_left -= seconds_per_tick
|
|
if(time_left <= 0)
|
|
say("Sentence time has been served. Thank you for your cooperation in our corporate rehabilitation program!")
|
|
STOP_PROCESSING(SSobj, src)
|
|
|
|
/obj/item/card/id/advanced/prisoner/attack_self(mob/user)
|
|
to_chat(usr, span_notice("You have accumulated [points] out of the [goal] points you need for freedom."))
|
|
|
|
/obj/item/card/id/advanced/prisoner/one
|
|
name = "Prisoner #13-001"
|
|
registered_name = "Prisoner #13-001"
|
|
trim = /datum/id_trim/job/prisoner/one
|
|
|
|
/obj/item/card/id/advanced/prisoner/two
|
|
name = "Prisoner #13-002"
|
|
registered_name = "Prisoner #13-002"
|
|
trim = /datum/id_trim/job/prisoner/two
|
|
|
|
/obj/item/card/id/advanced/prisoner/three
|
|
name = "Prisoner #13-003"
|
|
registered_name = "Prisoner #13-003"
|
|
trim = /datum/id_trim/job/prisoner/three
|
|
|
|
/obj/item/card/id/advanced/prisoner/four
|
|
name = "Prisoner #13-004"
|
|
registered_name = "Prisoner #13-004"
|
|
trim = /datum/id_trim/job/prisoner/four
|
|
|
|
/obj/item/card/id/advanced/prisoner/five
|
|
name = "Prisoner #13-005"
|
|
registered_name = "Prisoner #13-005"
|
|
trim = /datum/id_trim/job/prisoner/five
|
|
|
|
/obj/item/card/id/advanced/prisoner/six
|
|
name = "Prisoner #13-006"
|
|
registered_name = "Prisoner #13-006"
|
|
trim = /datum/id_trim/job/prisoner/six
|
|
|
|
/obj/item/card/id/advanced/prisoner/seven
|
|
name = "Prisoner #13-007"
|
|
registered_name = "Prisoner #13-007"
|
|
trim = /datum/id_trim/job/prisoner/seven
|
|
|
|
/obj/item/card/id/advanced/mining
|
|
name = "mining ID"
|
|
trim = /datum/id_trim/job/shaft_miner/spare
|
|
|
|
/obj/item/card/id/advanced/highlander
|
|
name = "highlander ID"
|
|
registered_name = "Highlander"
|
|
desc = "There can be only one!"
|
|
icon_state = "card_black"
|
|
assigned_icon_state = "assigned_syndicate"
|
|
trim = /datum/id_trim/highlander
|
|
wildcard_slots = WILDCARD_LIMIT_ADMIN
|
|
|
|
/// An ID that you can flip with attack_self_secondary, overriding the appearance of the ID (useful for plainclothes detectives for example).
|
|
/obj/item/card/id/advanced/plainclothes
|
|
name = "Plainclothes ID"
|
|
///The trim that we use as plainclothes identity
|
|
var/alt_trim = /datum/id_trim/job/assistant
|
|
|
|
/obj/item/card/id/advanced/plainclothes/add_context(atom/source, list/context, obj/item/held_item, mob/user)
|
|
. = ..()
|
|
context[SCREENTIP_CONTEXT_LMB] = "Show/Flip ID"
|
|
|
|
/obj/item/card/id/advanced/plainclothes/examine(mob/user)
|
|
. = ..()
|
|
if(trim_assignment_override)
|
|
. += span_smallnotice("it's currently under plainclothes identity.")
|
|
else
|
|
. += span_smallnotice("flip it to switch to the plainclothes identity.")
|
|
|
|
/obj/item/card/id/advanced/plainclothes/attack_self(mob/user)
|
|
var/popup_input = tgui_input_list(user, "Choose Action", "Two-Sided ID", list("Show", "Flip"))
|
|
if(!popup_input || !after_input_check(user))
|
|
return TRUE
|
|
if(popup_input == "Show")
|
|
return ..()
|
|
balloon_alert(user, "flipped")
|
|
if(trim_assignment_override)
|
|
SSid_access.remove_trim_override(src)
|
|
else
|
|
SSid_access.apply_trim_override(src, alt_trim)
|
|
update_label()
|
|
update_appearance()
|
|
|
|
/obj/item/card/id/advanced/plainclothes/update_label()
|
|
if(!trim_assignment_override)
|
|
return ..()
|
|
var/name_string = registered_name ? "[registered_name]'s ID Card" : initial(name)
|
|
var/datum/id_trim/fake = SSid_access.trim_singletons_by_path[alt_trim]
|
|
name = "[name_string] ([fake.assignment])"
|
|
|
|
/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."
|
|
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)
|
|
action_slots = ALL
|
|
|
|
/// Have we set a custom name and job assignment, or will we use what we're given when we chameleon change?
|
|
var/forged = FALSE
|
|
/// Anti-metagaming protections. If TRUE, anyone can change the ID card's details. If FALSE, only syndicate agents can.
|
|
var/anyone = FALSE
|
|
/// Weak ref to the ID card we're currently attempting to steal access from.
|
|
var/datum/weakref/theft_target
|
|
|
|
/obj/item/card/id/advanced/chameleon/Initialize(mapload)
|
|
. = ..()
|
|
register_item_context()
|
|
|
|
/obj/item/card/id/advanced/chameleon/Destroy()
|
|
theft_target = null
|
|
return ..()
|
|
|
|
/obj/item/card/id/advanced/chameleon/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
|
|
if(isidcard(interacting_with))
|
|
theft_target = WEAKREF(interacting_with)
|
|
ui_interact(user)
|
|
return ITEM_INTERACT_SUCCESS
|
|
return ..()
|
|
|
|
/obj/item/card/id/advanced/chameleon/interact_with_atom_secondary(atom/interacting_with, mob/living/user, list/modifiers)
|
|
// 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 ITEM_INTERACT_BLOCKING to cancel any part of the following the attack chain.
|
|
if(ishuman(interacting_with))
|
|
interacting_with.balloon_alert(user, "scanning ID card...")
|
|
|
|
if(!do_after(user, 2 SECONDS, interacting_with))
|
|
interacting_with.balloon_alert(user, "interrupted!")
|
|
return ITEM_INTERACT_BLOCKING
|
|
|
|
var/mob/living/carbon/human/human_target = interacting_with
|
|
var/list/target_id_cards = human_target.get_all_contents_type(/obj/item/card/id)
|
|
|
|
if(!length(target_id_cards))
|
|
interacting_with.balloon_alert(user, "no IDs!")
|
|
return ITEM_INTERACT_BLOCKING
|
|
|
|
var/selected_id = pick(target_id_cards)
|
|
interacting_with.balloon_alert(user, UNLINT("IDs synced"))
|
|
theft_target = WEAKREF(selected_id)
|
|
ui_interact(user)
|
|
return ITEM_INTERACT_SUCCESS
|
|
|
|
if(isitem(interacting_with))
|
|
var/obj/item/target_item = interacting_with
|
|
|
|
interacting_with.balloon_alert(user, "scanning ID card...")
|
|
|
|
var/list/target_id_cards = target_item.get_all_contents_type(/obj/item/card/id)
|
|
var/target_item_id = target_item.GetID()
|
|
|
|
if(target_item_id)
|
|
target_id_cards |= target_item_id
|
|
|
|
if(!length(target_id_cards))
|
|
interacting_with.balloon_alert(user, "no IDs!")
|
|
return ITEM_INTERACT_BLOCKING
|
|
|
|
var/selected_id = pick(target_id_cards)
|
|
interacting_with.balloon_alert(user, UNLINT("IDs synced"))
|
|
theft_target = WEAKREF(selected_id)
|
|
ui_interact(user)
|
|
return ITEM_INTERACT_SUCCESS
|
|
|
|
return NONE
|
|
|
|
/obj/item/card/id/advanced/chameleon/ui_interact(mob/user, datum/tgui/ui)
|
|
ui = SStgui.try_update_ui(user, src, ui)
|
|
if(!ui)
|
|
ui = new(user, src, "ChameleonCard", name)
|
|
ui.open()
|
|
|
|
/obj/item/card/id/advanced/chameleon/ui_static_data(mob/user)
|
|
var/list/data = list()
|
|
data["wildcardFlags"] = SSid_access.wildcard_flags_by_wildcard
|
|
data["accessFlagNames"] = SSid_access.access_flag_string_by_flag
|
|
data["accessFlags"] = SSid_access.flags_by_access
|
|
return data
|
|
|
|
/obj/item/card/id/advanced/chameleon/ui_host(mob/user)
|
|
// Hook our UI to the theft target ID card for UI state checks.
|
|
return theft_target?.resolve()
|
|
|
|
/obj/item/card/id/advanced/chameleon/ui_state(mob/user)
|
|
return GLOB.always_state
|
|
|
|
/obj/item/card/id/advanced/chameleon/ui_status(mob/user, datum/ui_state/state)
|
|
var/target = theft_target?.resolve()
|
|
|
|
if(!target)
|
|
return UI_CLOSE
|
|
|
|
var/status = min(
|
|
ui_status_user_strictly_adjacent(user, target),
|
|
ui_status_user_is_advanced_tool_user(user),
|
|
max(
|
|
ui_status_user_is_conscious_and_lying_down(user),
|
|
ui_status_user_is_abled(user, target),
|
|
),
|
|
)
|
|
|
|
if(status < UI_INTERACTIVE)
|
|
return UI_CLOSE
|
|
|
|
return status
|
|
|
|
/obj/item/card/id/advanced/chameleon/ui_data(mob/user)
|
|
var/list/data = list()
|
|
|
|
data["showBasic"] = FALSE
|
|
|
|
var/list/regions = list()
|
|
|
|
var/obj/item/card/id/target_card = theft_target.resolve()
|
|
if(target_card)
|
|
var/list/tgui_region_data = SSid_access.all_region_access_tgui
|
|
for(var/region in SSid_access.station_regions)
|
|
regions += tgui_region_data[region]
|
|
|
|
data["accesses"] = regions
|
|
data["ourAccess"] = access
|
|
data["ourTrimAccess"] = trim ? trim.access : list()
|
|
data["theftAccess"] = target_card.access.Copy()
|
|
data["wildcardSlots"] = wildcard_slots
|
|
data["selectedList"] = access
|
|
data["trimAccess"] = list()
|
|
|
|
return data
|
|
|
|
/obj/item/card/id/advanced/chameleon/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
|
. = ..()
|
|
if(.)
|
|
return
|
|
|
|
var/obj/item/card/id/target_card = theft_target?.resolve()
|
|
if(QDELETED(target_card))
|
|
to_chat(usr, span_notice("The ID card you were attempting to scan is no longer in range."))
|
|
target_card = null
|
|
return TRUE
|
|
|
|
// Wireless ID theft!
|
|
var/turf/our_turf = get_turf(src)
|
|
var/turf/target_turf = get_turf(target_card)
|
|
if(!our_turf.Adjacent(target_turf))
|
|
to_chat(usr, span_notice("The ID card you were attempting to scan is no longer in range."))
|
|
target_card = null
|
|
return TRUE
|
|
|
|
switch(action)
|
|
if("mod_access")
|
|
var/access_type = params["access_target"]
|
|
var/try_wildcard = params["access_wildcard"]
|
|
if(access_type in access)
|
|
remove_access(list(access_type))
|
|
LOG_ID_ACCESS_CHANGE(usr, src, "removed [SSid_access.get_access_desc(access_type)]")
|
|
return TRUE
|
|
|
|
if(!(access_type in target_card.access))
|
|
to_chat(usr, span_notice("ID error: ID card rejected your attempted access modification."))
|
|
LOG_ID_ACCESS_CHANGE(usr, src, "failed to add [SSid_access.get_access_desc(access_type)][try_wildcard ? " with wildcard [try_wildcard]" : ""]")
|
|
return TRUE
|
|
|
|
if(!can_add_wildcards(list(access_type), try_wildcard))
|
|
to_chat(usr, span_notice("ID error: ID card rejected your attempted access modification."))
|
|
LOG_ID_ACCESS_CHANGE(usr, src, "failed to add [SSid_access.get_access_desc(access_type)][try_wildcard ? " with wildcard [try_wildcard]" : ""]")
|
|
return TRUE
|
|
|
|
if(!add_access(list(access_type), try_wildcard))
|
|
to_chat(usr, span_notice("ID error: ID card rejected your attempted access modification."))
|
|
LOG_ID_ACCESS_CHANGE(usr, src, "failed to add [SSid_access.get_access_desc(access_type)][try_wildcard ? " with wildcard [try_wildcard]" : ""]")
|
|
return TRUE
|
|
|
|
if(access_type in ACCESS_ALERT_ADMINS)
|
|
message_admins("[ADMIN_LOOKUPFLW(usr)] just added [SSid_access.get_access_desc(access_type)] to an ID card [ADMIN_VV(src)] [(registered_name) ? "belonging to [registered_name]." : "with no registered name."]")
|
|
LOG_ID_ACCESS_CHANGE(usr, src, "added [SSid_access.get_access_desc(access_type)]")
|
|
return TRUE
|
|
|
|
/obj/item/card/id/advanced/chameleon/attack_self(mob/user)
|
|
if(!user.can_perform_action(user, NEED_DEXTERITY| FORBID_TELEKINESIS_REACH))
|
|
return ..()
|
|
var/popup_input = tgui_input_list(user, "Choose Action", "Agent ID", list("Show", "Forge/Reset", "Change Account ID"))
|
|
if(!popup_input || !after_input_check(user))
|
|
return TRUE
|
|
switch(popup_input)
|
|
if ("Change Account ID")
|
|
set_new_account(user)
|
|
return
|
|
if("Show")
|
|
return ..()
|
|
|
|
///"Forge/Reset", kept outside the switch() statement to reduce indentation.
|
|
if(forged) //reset the ID if forged
|
|
registered_name = initial(registered_name)
|
|
assignment = initial(assignment)
|
|
SSid_access.remove_trim_override(src)
|
|
REMOVE_TRAIT(src, TRAIT_MAGNETIC_ID_CARD, CHAMELEON_ITEM_TRAIT)
|
|
user.log_message("reset \the [initial(name)] named \"[src]\" to default.", LOG_GAME)
|
|
update_label()
|
|
update_icon()
|
|
forged = FALSE
|
|
to_chat(user, span_notice("You successfully reset the ID card."))
|
|
return
|
|
|
|
///forge the ID if not forged.s
|
|
var/input_name = tgui_input_text(user, "What name would you like to put on this card? Leave blank to randomise.", "Agent card name", registered_name ? registered_name : (ishuman(user) ? user.real_name : user.name), max_length = MAX_NAME_LEN, encode = FALSE)
|
|
|
|
if(!after_input_check(user))
|
|
return TRUE
|
|
if(input_name)
|
|
input_name = sanitize_name(input_name, allow_numbers = TRUE)
|
|
if(!input_name)
|
|
// Invalid/blank names give a randomly generated one.
|
|
if(user.gender == MALE)
|
|
input_name = "[pick(GLOB.first_names_male)] [pick(GLOB.last_names)]"
|
|
else if(user.gender == FEMALE)
|
|
input_name = "[pick(GLOB.first_names_female)] [pick(GLOB.last_names)]"
|
|
else
|
|
input_name = "[pick(GLOB.first_names)] [pick(GLOB.last_names)]"
|
|
|
|
var/change_trim = tgui_alert(user, "Adjust the appearance of your card's trim?", "Modify Trim", list("Yes", "No"))
|
|
if(!after_input_check(user))
|
|
return TRUE
|
|
var/selected_trim_path
|
|
var/static/list/trim_list
|
|
if(change_trim == "Yes")
|
|
trim_list = list()
|
|
for(var/trim_path in typesof(/datum/id_trim))
|
|
var/datum/id_trim/trim = SSid_access.trim_singletons_by_path[trim_path]
|
|
if(trim && trim.trim_state && trim.assignment)
|
|
var/fake_trim_name = "[trim.assignment] ([trim.trim_state])"
|
|
trim_list[fake_trim_name] = trim_path
|
|
selected_trim_path = tgui_input_list(user, "Select trim to apply to your card.\nNote: This will not grant any trim accesses.", "Forge Trim", sort_list(trim_list, GLOBAL_PROC_REF(cmp_typepaths_asc)))
|
|
if(!after_input_check(user))
|
|
return TRUE
|
|
|
|
var/target_occupation = tgui_input_text(user, "What occupation would you like to put on this card?\nNote: This will not grant any access levels.", "Agent card job assignment", assignment ? assignment : "Assistant", max_length = MAX_NAME_LEN)
|
|
if(!after_input_check(user))
|
|
return TRUE
|
|
|
|
var/new_age = tgui_input_number(user, "Choose the ID's age", "Agent card age", AGE_MIN, AGE_MAX, AGE_MIN)
|
|
if(!after_input_check(user))
|
|
return TRUE
|
|
|
|
var/wallet_spoofing = tgui_alert(user, "Activate wallet ID spoofing, allowing this card to force itself to occupy the visible ID slot in wallets?", "Wallet ID Spoofing", list("Yes", "No"))
|
|
if(!after_input_check(user))
|
|
return
|
|
|
|
registered_name = input_name
|
|
if(selected_trim_path)
|
|
SSid_access.apply_trim_override(src, trim_list[selected_trim_path])
|
|
if(target_occupation)
|
|
assignment = sanitize(target_occupation)
|
|
if(new_age)
|
|
registered_age = new_age
|
|
if(wallet_spoofing == "Yes")
|
|
ADD_TRAIT(src, TRAIT_MAGNETIC_ID_CARD, CHAMELEON_ITEM_TRAIT)
|
|
|
|
update_label()
|
|
update_icon()
|
|
forged = TRUE
|
|
to_chat(user, span_notice("You successfully forge the ID card."))
|
|
user.log_message("forged \the [initial(name)] with name \"[registered_name]\", occupation \"[assignment]\" and trim \"[trim?.assignment]\".", LOG_GAME)
|
|
|
|
if(!ishuman(user))
|
|
return
|
|
|
|
var/mob/living/carbon/human/owner = user
|
|
if (!selected_trim_path) // Ensure that even without a trim update, we update user's sechud
|
|
owner.sec_hud_set_ID()
|
|
|
|
if (registered_account)
|
|
return
|
|
|
|
var/datum/bank_account/account = SSeconomy.bank_accounts_by_id["[owner.account_id]"]
|
|
if(account)
|
|
account.bank_cards += src
|
|
registered_account = account
|
|
to_chat(user, span_notice("Your account number has been automatically assigned."))
|
|
|
|
/obj/item/card/id/advanced/chameleon/add_item_context(obj/item/source, list/context, atom/target, mob/living/user,)
|
|
. = ..()
|
|
|
|
if(!in_range(user, target))
|
|
return .
|
|
if(ishuman(target))
|
|
context[SCREENTIP_CONTEXT_RMB] = "Copy access"
|
|
return CONTEXTUAL_SCREENTIP_SET
|
|
if(isitem(target))
|
|
context[SCREENTIP_CONTEXT_RMB] = "Scan for access"
|
|
return CONTEXTUAL_SCREENTIP_SET
|
|
return .
|
|
|
|
/// A special variant of the classic chameleon ID card which accepts all access.
|
|
/obj/item/card/id/advanced/chameleon/black
|
|
icon_state = "card_black"
|
|
assigned_icon_state = "assigned_syndicate"
|
|
wildcard_slots = WILDCARD_LIMIT_GOLD
|
|
|
|
/obj/item/card/id/advanced/engioutpost
|
|
registered_name = "George 'Plastic' Miller"
|
|
desc = "A card used to provide ID and determine access across the station. There's blood dripping from the corner. Ew."
|
|
trim = /datum/id_trim/engioutpost
|
|
registered_age = 47
|
|
|
|
/obj/item/card/id/advanced/simple_bot
|
|
name = "simple bot ID card"
|
|
desc = "An internal ID card used by the station's non-sentient bots. You should report this to a coder if you're holding it."
|
|
wildcard_slots = WILDCARD_LIMIT_ADMIN
|
|
|
|
/obj/item/card/id/red
|
|
name = "Red Team identification card"
|
|
desc = "A card used to identify members of the red team for CTF"
|
|
icon_state = "ctf_red"
|
|
|
|
/obj/item/card/id/blue
|
|
name = "Blue Team identification card"
|
|
desc = "A card used to identify members of the blue team for CTF"
|
|
icon_state = "ctf_blue"
|
|
|
|
/obj/item/card/id/yellow
|
|
name = "Yellow Team identification card"
|
|
desc = "A card used to identify members of the yellow team for CTF"
|
|
icon_state = "ctf_yellow"
|
|
|
|
/obj/item/card/id/green
|
|
name = "Green Team identification card"
|
|
desc = "A card used to identify members of the green team for CTF"
|
|
icon_state = "ctf_green"
|
|
|
|
#undef INTERN_THRESHOLD_FALLBACK_HOURS
|
|
#undef HOLOPAY_PROJECTION_INTERVAL
|
|
|
|
#define INDEX_NAME_COLOR 1
|
|
#define INDEX_ASSIGNMENT_COLOR 2
|
|
#define INDEX_TRIM_COLOR 3
|
|
|
|
/**
|
|
* A fake ID card any silly-willy can craft with wirecutters, cardboard and a writing utensil
|
|
* Beside the gimmick of changing the visible name when worn, they do nothing. They cannot have an account.
|
|
* They don't fit in PDAs nor wallets, They have no access. They won't trick securitrons. They won't work with chameleon masks.
|
|
* Etcetera etcetera. Furthermore, talking, or getting examined on will pretty much give it away.
|
|
*/
|
|
/obj/item/card/cardboard
|
|
name = "cardboard identification card"
|
|
desc = "A card used to provide ID and det- Heeeey, wait a second, this is just a piece of cut cardboard!"
|
|
icon_state = "cardboard_id"
|
|
inhand_icon_state = "cardboard-id"
|
|
worn_icon_state = "nothing"
|
|
resistance_flags = FLAMMABLE
|
|
slot_flags = ITEM_SLOT_ID
|
|
///The "name" of the "owner" of this "ID"
|
|
var/scribbled_name
|
|
///The assignment written on this card.
|
|
var/scribbled_assignment
|
|
///An icon state used as trim.
|
|
var/scribbled_trim
|
|
///The colors for each of the above variables, for when overlays are updated.
|
|
var/details_colors = list(COLOR_BLACK, COLOR_BLACK, COLOR_BLACK)
|
|
pickup_sound = 'sound/items/handling/materials/cardboard_pick_up.ogg'
|
|
drop_sound = 'sound/items/handling/materials/cardboard_drop.ogg'
|
|
|
|
/obj/item/card/cardboard/equipped(mob/user, slot, initial = FALSE)
|
|
. = ..()
|
|
if(slot == ITEM_SLOT_ID)
|
|
RegisterSignal(user, COMSIG_HUMAN_GET_VISIBLE_NAME, PROC_REF(return_visible_name))
|
|
RegisterSignal(user, COMSIG_MOVABLE_MESSAGE_GET_NAME_PART, PROC_REF(return_message_name_part))
|
|
|
|
/obj/item/card/cardboard/dropped(mob/user, silent = FALSE)
|
|
. = ..()
|
|
UnregisterSignal(user, list(COMSIG_HUMAN_GET_VISIBLE_NAME, COMSIG_MOVABLE_MESSAGE_GET_NAME_PART))
|
|
|
|
/obj/item/card/cardboard/proc/return_visible_name(mob/living/carbon/human/source, list/identity)
|
|
SIGNAL_HANDLER
|
|
identity[VISIBLE_NAME_ID] = scribbled_name
|
|
|
|
/obj/item/card/cardboard/proc/return_message_name_part(mob/living/carbon/human/source, list/stored_name, visible_name)
|
|
SIGNAL_HANDLER
|
|
if(visible_name)
|
|
return
|
|
var/voice_name = source.GetVoice()
|
|
if(source.name != voice_name)
|
|
voice_name += " (as [scribbled_name])"
|
|
stored_name[NAME_PART_INDEX] = voice_name
|
|
|
|
/obj/item/card/cardboard/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
|
|
if(user.can_write(tool, TRUE))
|
|
INVOKE_ASYNC(src, PROC_REF(modify_card), user, tool)
|
|
return ITEM_INTERACT_SUCCESS
|
|
|
|
///Lets the user write a name, assignment or trim on the card, or reset it. Only the name is important for the component.
|
|
/obj/item/card/cardboard/proc/modify_card(mob/living/user, obj/item/item)
|
|
if(!user.mind)
|
|
return
|
|
var/popup_input = tgui_input_list(user, "What To Change", "Cardboard ID", list("Name", "Assignment", "Trim", "Reset"))
|
|
if(!after_input_check(user, item, popup_input))
|
|
return
|
|
switch(popup_input)
|
|
if("Name")
|
|
var/raw_input = tgui_input_text(user, "What name would you like to put on this card?", "Cardboard card name", scribbled_name || (ishuman(user) ? user.real_name : user.name), max_length = MAX_NAME_LEN)
|
|
var/input_name = sanitize_name(raw_input, allow_numbers = TRUE)
|
|
if(!after_input_check(user, item, input_name, scribbled_name))
|
|
return
|
|
playsound(src, SFX_WRITING_PEN, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, SOUND_FALLOFF_EXPONENT + 3, ignore_walls = FALSE)
|
|
scribbled_name = input_name
|
|
var/list/details = item.get_writing_implement_details()
|
|
details_colors[INDEX_NAME_COLOR] = details["color"] || COLOR_BLACK
|
|
if("Assignment")
|
|
var/input_assignment = tgui_input_text(user, "What assignment would you like to put on this card?", "Cardboard card job ssignment", scribbled_assignment || "Assistant", max_length = MAX_NAME_LEN)
|
|
if(!after_input_check(user, item, input_assignment, scribbled_assignment))
|
|
return
|
|
playsound(src, SFX_WRITING_PEN, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, SOUND_FALLOFF_EXPONENT + 3, ignore_walls = FALSE)
|
|
scribbled_assignment = sanitize(input_assignment)
|
|
var/list/details = item.get_writing_implement_details()
|
|
details_colors[INDEX_ASSIGNMENT_COLOR] = details["color"] || COLOR_BLACK
|
|
if("Trim")
|
|
var/static/list/possible_trims
|
|
if(!possible_trims)
|
|
possible_trims = list()
|
|
for(var/trim_path in typesof(/datum/id_trim))
|
|
var/datum/id_trim/trim = SSid_access.trim_singletons_by_path[trim_path]
|
|
if(trim?.trim_state && trim.assignment)
|
|
possible_trims |= replacetext(trim.trim_state, "trim_", "")
|
|
sortTim(possible_trims, GLOBAL_PROC_REF(cmp_typepaths_asc))
|
|
var/input_trim = tgui_input_list(user, "Select trim to apply to your card.\nNote: This will not grant any trim accesses.", "Forge Trim", possible_trims)
|
|
if(!input_trim || !after_input_check(user, item, input_trim, scribbled_trim))
|
|
return
|
|
playsound(src, SFX_WRITING_PEN, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, SOUND_FALLOFF_EXPONENT + 3, ignore_walls = FALSE)
|
|
scribbled_trim = "cardboard_[input_trim]"
|
|
var/list/details = item.get_writing_implement_details()
|
|
details_colors[INDEX_TRIM_COLOR] = details["color"] || COLOR_BLACK
|
|
if("Reset")
|
|
scribbled_name = null
|
|
scribbled_assignment = null
|
|
scribbled_trim = null
|
|
details_colors = list(COLOR_BLACK, COLOR_BLACK, COLOR_BLACK)
|
|
|
|
update_appearance()
|
|
|
|
///Checks that the conditions to be able to modify the cardboard card are still present after user input calls.
|
|
/obj/item/card/cardboard/proc/after_input_check(mob/living/user, obj/item/item, input, value)
|
|
if(!input || (value && input == value))
|
|
return FALSE
|
|
if(QDELETED(user) || QDELETED(item) || QDELETED(src) || user.incapacitated || !user.is_holding(item) || !user.CanReach(src) || !user.can_write(item))
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/obj/item/card/cardboard/attack_self(mob/user)
|
|
if(!Adjacent(user))
|
|
return
|
|
user.visible_message(span_notice("[user] shows you: [icon2html(src, viewers(user))] [name]."), span_notice("You show \the [name]."))
|
|
add_fingerprint(user)
|
|
|
|
/obj/item/card/cardboard/update_name()
|
|
. = ..()
|
|
if(!scribbled_name)
|
|
name = initial(name)
|
|
return
|
|
name = "[scribbled_name]'s ID Card ([scribbled_assignment])"
|
|
|
|
/obj/item/card/cardboard/update_overlays()
|
|
. = ..()
|
|
if(scribbled_name)
|
|
var/mutable_appearance/name_overlay = mutable_appearance(icon, "cardboard_name")
|
|
name_overlay.color = details_colors[INDEX_NAME_COLOR]
|
|
. += name_overlay
|
|
if(scribbled_assignment)
|
|
var/mutable_appearance/assignment_overlay = mutable_appearance(icon, "cardboard_assignment")
|
|
assignment_overlay.color = details_colors[INDEX_ASSIGNMENT_COLOR]
|
|
. += assignment_overlay
|
|
if(scribbled_trim)
|
|
var/mutable_appearance/frame_overlay = mutable_appearance(icon, "cardboard_frame")
|
|
frame_overlay.color = details_colors[INDEX_TRIM_COLOR]
|
|
. += frame_overlay
|
|
var/mutable_appearance/trim_overlay = mutable_appearance(icon, scribbled_trim)
|
|
trim_overlay.color = details_colors[INDEX_TRIM_COLOR]
|
|
. += trim_overlay
|
|
|
|
/obj/item/card/cardboard/get_id_examine_strings(mob/user)
|
|
. = ..()
|
|
. += list("[icon2html(get_cached_flat_icon(), user, extra_classes = "hugeicon")]")
|
|
|
|
/obj/item/card/cardboard/get_examine_icon(mob/user)
|
|
return icon2html(get_cached_flat_icon(), user)
|
|
|
|
/obj/item/card/cardboard/examine(mob/user)
|
|
. = ..()
|
|
. += span_notice("You could use a pen or crayon to forge a name, assignment or trim.")
|
|
|
|
#undef INDEX_NAME_COLOR
|
|
#undef INDEX_ASSIGNMENT_COLOR
|
|
#undef INDEX_TRIM_COLOR
|