mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-27 17:41:50 +00:00
## About The Pull Request completes https://github.com/orgs/tgstation/projects/19/views/1?pane=issue&itemId=85235611 https://github.com/user-attachments/assets/087c88bd-9ddd-4e57-972d-fe66084d19bc ## Why It's Good For The Game maybe now someone will notice you wrote something ## Changelog 🆑 grungussuss sound: writing something now produces sound /🆑
1986 lines
76 KiB
Plaintext
1986 lines
76 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
|
|
|
|
/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 = 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
|
|
|
|
/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))
|
|
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)
|
|
return ..()
|
|
|
|
/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/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(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/dropped(mob/user)
|
|
UnregisterSignal(user, COMSIG_MOVABLE_POINTED)
|
|
return ..()
|
|
|
|
/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>(MINOR)</b>"
|
|
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"
|
|
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
|
|
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? [user.first_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 = registered_name ? "[registered_name]'s ID Card" : initial(name)
|
|
var/assignment_string
|
|
|
|
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])"
|
|
|
|
/// 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/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/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/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/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE)
|
|
. = ..()
|
|
|
|
//Old loc
|
|
if(istype(old_loc, /obj/item/storage/wallet))
|
|
UnregisterSignal(old_loc, list(COMSIG_ITEM_EQUIPPED, COMSIG_ITEM_DROPPED))
|
|
|
|
if(istype(old_loc, /obj/item/modular_computer))
|
|
UnregisterSignal(old_loc, list(COMSIG_ITEM_EQUIPPED, COMSIG_ITEM_DROPPED))
|
|
|
|
//New loc
|
|
if(istype(loc, /obj/item/storage/wallet))
|
|
RegisterSignal(loc, COMSIG_ITEM_EQUIPPED, PROC_REF(update_intern_status))
|
|
RegisterSignal(loc, COMSIG_ITEM_DROPPED, PROC_REF(remove_intern_status))
|
|
|
|
if(istype(loc, /obj/item/modular_computer))
|
|
RegisterSignal(loc, COMSIG_ITEM_EQUIPPED, PROC_REF(update_intern_status))
|
|
RegisterSignal(loc, COMSIG_ITEM_DROPPED, PROC_REF(remove_intern_status))
|
|
|
|
/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_from_chameleon_card(src)
|
|
else
|
|
SSid_access.apply_trim_to_chameleon_card(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
|
|
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
|
|
/// 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_from_chameleon_card(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_to_chameleon_card(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(!registered_account && ishuman(user))
|
|
var/mob/living/carbon/human/accountowner = user
|
|
|
|
var/datum/bank_account/account = SSeconomy.bank_accounts_by_id["[accountowner.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
|