Files
WildkinsandGitHub e4192f9b8f Lag War Day 5.5: are you serious dude (#21889)
we have a gorillion hard deletions and it turns out half of them are
because we set the "ALWAYS HARD DELETE THIS" flag rather than just like.
fixing the hard delete.

cool
2026-02-21 01:41:50 +00:00

687 lines
21 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* General outfit abstraction.
* To be used for both a mob's outfit (for a ghostspawner, ship/station job, corpse),
* where the outfit is applied to the mob.
* But also for "standalone" outfits (just items spawned in a locker, on the floor),
* where the outfit items are "spilled" onto the floor (not all items, does not spawn IDs for example).
*/
/obj/outfit
name = "Naked"
icon = 'icons/effects/map_effects.dmi'
icon_state = "outfit"
/// If spilling onto the floor, prob chance for the item to spill.
var/spill_prob = 98
/// If spilling onto the floor, if true, items to spill will be shuffled.
var/spill_shuffle = TRUE
var/collect_not_del = FALSE
// The following vars can either be a path or a list of paths.
/// If a list of paths is supplied a random item from that list is selected.
var/uniform = null
var/suit = null
/// Mutually exclusive with and will override backpack choices below. Use for RIGs, tanks, etc.
var/back = null
var/belt = null
var/gloves = null
var/wrist = null
var/pants = null
var/shoes = null
var/head = null
var/mask = null
var/l_ear = null
var/r_ear = null
var/glasses = null
var/l_pocket = null
var/r_pocket = null
var/suit_store = null
var/accessory = null
var/suit_accessory = null
/**
* species specific item paths, in the form of
* thing = list(SPECIES_NAME = /type/path/here)
* if no path is found, the default fallback (var without the species_ prefix) will be used.
*/
var/list/species_head
var/list/species_suit
var/list/species_gloves
var/list/species_shoes
/// The following vars must be paths.
var/l_hand = null
var/r_hand = null
var/id = null
var/pda = null
var/radio = null
/// Must be paths, used to allow player-pref backpack choice.
var/allow_backbag_choice = FALSE
var/backpack = /obj/item/storage/backpack
var/backpack_faction
var/satchel = /obj/item/storage/backpack/satchel
var/satchel_faction
var/satchel_alt = /obj/item/storage/backpack/satchel/leather
var/satchel_alt_faction
var/dufflebag = /obj/item/storage/backpack/duffel
var/dufflebag_faction
var/messengerbag = /obj/item/storage/backpack/messenger
var/messengerbag_faction
var/rucksack = /obj/item/storage/backpack/rucksack
var/rucksack_faction
var/pocketbook = /obj/item/storage/backpack/satchel/pocketbook
var/pocketbook_faction
var/chestpouch = /obj/item/storage/backpack/chestpouch
var/chestpouch_faction
var/allow_pda_choice = FALSE
var/tab_pda = /obj/item/modular_computer/handheld/pda/civilian
var/tablet = /obj/item/modular_computer/handheld/preset/civilian
var/wristbound = /obj/item/modular_computer/handheld/wristbound/preset/pda/civilian
var/allow_headset_choice = FALSE
var/headset = /obj/item/radio/headset
var/bowman = /obj/item/radio/headset/alt
var/double_headset = /obj/item/radio/headset/alt/double
var/wrist_radio = /obj/item/radio/headset/wrist
var/clipon_radio = /obj/item/radio/headset/wrist/clip
/// When spawning in, the ID will be set to this iff, preventing friendly fire.
var/id_iff = IFF_DEFAULT
/// ID of slot containing a gas tank.
var/internals_slot = null
/// In the list(path=count,otherpath=count) format.
var/list/backpack_contents = list()
var/list/accessory_contents = list()
/// In the list(path=count,otherpath=count) format.
var/list/belt_contents = list()
/// A list of implants that should be implanted.
var/list/implants = null
/// A list of spells to grant.
var/list/spells = list()
/obj/outfit/Initialize(mapload, ...)
. = ..()
// if loc is not null, means the outfit was mapped in or spawned manually
if(loc!=null)
spill()
/**
* Spawn the items on the loc turf.
* Delete self later.
*/
/obj/outfit/proc/spill()
// Get a list of item types to spawn.
var/list/items = list(
uniform,
suit,
back,
belt,
gloves,
wrist,
pants,
shoes,
head,
mask,
l_ear,
r_ear,
glasses,
l_pocket,
r_pocket,
suit_store,
accessory,
suit_accessory,
l_hand,
r_hand,
pda,
radio,
backpack,
)
// add contents to the list
for(var/c in backpack_contents)
items += c
for(var/c in accessory_contents)
items += c
for(var/c in belt_contents)
items += c
// shuffle
if(spill_shuffle)
items = shuffle(items)
// go over each item
for(var/i in items)
if(i && prob(spill_prob))
spill_item(i)
// and finally delete self
qdel(src)
/obj/outfit/proc/spill_item(var/path)
if(islist(path))
path = pick(path)
if(path && ispath(path))
new path(loc)
/obj/outfit/proc/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
// To be overriden for customization depending on client prefs, species etc
if(allow_backbag_choice)
switch(H.backbag)
if (OUTFIT_NOTHING)
back = null
if (OUTFIT_BACKPACK)
switch(H.backbag_style)
if (OUTFIT_JOBSPECIFIC)
back = backpack
if (OUTFIT_GENERIC)
back = /obj/item/storage/backpack
// Some may not have faction-specifics; fall back on job backpack if needed
if (OUTFIT_FACTIONSPECIFIC)
back = backpack_faction ? backpack_faction : backpack
if (OUTFIT_SATCHEL)
switch(H.backbag_style)
if (OUTFIT_JOBSPECIFIC)
back = satchel
if (OUTFIT_GENERIC)
back = /obj/item/storage/backpack/satchel
if (OUTFIT_FACTIONSPECIFIC)
back = satchel_faction ? satchel_faction : satchel
// Leather Satchel
if (OUTFIT_SATCHEL_ALT)
switch(H.backbag_style)
if (OUTFIT_JOBSPECIFIC)
back = satchel_alt
if (OUTFIT_GENERIC)
back = /obj/item/storage/backpack/satchel/leather
if (OUTFIT_FACTIONSPECIFIC)
back = satchel_alt_faction ? satchel_alt_faction : satchel_alt
if (OUTFIT_DUFFELBAG)
switch(H.backbag_style)
if (OUTFIT_JOBSPECIFIC)
back = dufflebag
if (OUTFIT_GENERIC)
back = /obj/item/storage/backpack/duffel
if (OUTFIT_FACTIONSPECIFIC)
back = dufflebag_faction ? dufflebag_faction : dufflebag
if (OUTFIT_MESSENGERBAG)
switch(H.backbag_style)
if (OUTFIT_JOBSPECIFIC)
back = messengerbag
if (OUTFIT_GENERIC)
back = /obj/item/storage/backpack/messenger
if (OUTFIT_FACTIONSPECIFIC)
back = messengerbag_faction ? messengerbag_faction : messengerbag
if (OUTFIT_RUCKSACK)
switch(H.backbag_style)
if (OUTFIT_JOBSPECIFIC)
back = rucksack
if (OUTFIT_GENERIC)
back = /obj/item/storage/backpack/rucksack
if (OUTFIT_FACTIONSPECIFIC)
back = rucksack_faction ? rucksack_faction : rucksack
if (OUTFIT_POCKETBOOK)
switch(H.backbag_style)
if (OUTFIT_JOBSPECIFIC)
back = pocketbook
if (OUTFIT_GENERIC)
back = /obj/item/storage/backpack/satchel/pocketbook
if (OUTFIT_FACTIONSPECIFIC)
back = pocketbook_faction ? pocketbook_faction : pocketbook
if (OUTFIT_CHESTPOUCH)
switch(H.backbag_style)
if (OUTFIT_JOBSPECIFIC)
back = chestpouch
if (OUTFIT_GENERIC)
back = /obj/item/storage/backpack/chestpouch
if (OUTFIT_FACTIONSPECIFIC)
back = chestpouch_faction ? chestpouch_faction : chestpouch
else
// Department backpack
back = backpack
// If theres a color switch em out for a recolorable one
if (H.backbag_color >= 2)
switch (H.backbag)
if (OUTFIT_POCKETBOOK)
back = /obj/item/storage/backpack/satchel/pocketbook/recolorable
if (OUTFIT_RUCKSACK)
back = /obj/item/storage/backpack/rucksack/recolorable
if (OUTFIT_SATCHEL_ALT)
back = /obj/item/storage/backpack/satchel/leather/recolorable
if(back)
if(islist(back))
back = pick(back)
var/obj/item/storage/backpack/B = new back(H)
if (H.backbag == OUTFIT_SATCHEL_ALT || H.backbag == OUTFIT_RUCKSACK || H.backbag == OUTFIT_POCKETBOOK)
switch (H.backbag_color)
if (OUTFIT_NOTHING)
B.color = null
if (OUTFIT_BLUE)
B.color = "#2f4f81"
if (OUTFIT_GREEN)
B.color = "#353727"
if (OUTFIT_NAVY)
B.color = "#2a303b"
if (OUTFIT_TAN)
B.color = "#524a3e"
if (OUTFIT_KHAKI)
B.color = "#baa481"
if (OUTFIT_BLACK)
B.color = "#212121"
if (OUTFIT_OLIVE)
B.color = "#544f3d"
if (OUTFIT_AUBURN)
B.color = "#512828"
if (OUTFIT_BROWN)
B.color = "#3d2711"
else
B.color = null
switch(H.backbag_strap)
if(OUTFIT_NOTHING)
B.alpha_mask = "hidden"
if(OUTFIT_THIN)
B.alpha_mask = "thin"
if(OUTFIT_NORMAL)
B.alpha_mask = "normal"
if(OUTFIT_THICK)
B.alpha_mask = null
if(isvaurca(H, TRUE))
H.equip_or_collect(B, slot_r_hand)
else
H.equip_or_collect(B, slot_back)
var/datum/callback/radio_callback
if(allow_headset_choice)
switch(H.headset_choice)
if (OUTFIT_NOTHING)
l_ear = null
if (OUTFIT_BOWMAN)
l_ear = bowman
if (OUTFIT_DOUBLE)
l_ear = double_headset
if (OUTFIT_WRISTRAD, OUTFIT_THIN_WRISTRAD)
l_ear = null
wrist = wrist_radio
if(H.headset_choice == OUTFIT_THIN_WRISTRAD)
radio_callback = CALLBACK(src, PROC_REF(turn_into_thinset))
if(OUTFIT_CLIPON)
l_ear = null
wrist = clipon_radio
else
// Department headset
l_ear = headset
if(l_ear)
equip_item(H, l_ear, slot_l_ear, callback = radio_callback)
else if (wrist)
equip_item(H, wrist, slot_wrists, callback = radio_callback)
/obj/outfit/proc/turn_into_thinset(var/obj/item/radio/headset/wrist/radio)
if(istype(radio))
radio.icon_state = replacetext(radio.icon_state, "wrist", "thin")
radio.item_state = replacetext(radio.item_state, "wrist", "thin")
// Used to equip an item to the mob. Mainly to prevent copypasta for collect_not_del.
//override_collect temporarily allows equip_or_collect without enabling it for the job. Mostly used to prevent weirdness with hand equips when the player is missing one
/**
* Used to equip an item to the mob. Mainly to prevent copypasta for collect_not_del.
*
* *
*/
/obj/outfit/proc/equip_item(mob/living/carbon/human/H, path, slot, var/override_collect = FALSE, var/item_color, var/datum/callback/callback)
var/obj/item/I
if(isnum(path)) //Check if parameter is not numeric. Must be a path, list of paths or name of a gear datum
CRASH("Outfit [name] - Parameter path: [path] is numeric.")
if(islist(path)) //If its a list, select a random item
var/itempath = pick(path)
I = new itempath(H)
else if(GLOB.gear_datums[path]) //If its something else, we´ll check if its a gearpath and try to spawn it
var/datum/gear/G = GLOB.gear_datums[path]
I = G.spawn_random()
else
I = new path(H) //As fallback treat it as a path
if(I && callback)
callback.Invoke(I)
if(collect_not_del || override_collect)
H.equip_or_collect(I, slot)
else
H.equip_to_slot_or_del(I, slot)
/obj/outfit/proc/equip_uniform_accessory(mob/living/carbon/human/H)
if(!H)
return
if(islist(accessory))
accessory = pick(accessory)
var/obj/item/clothing/under/U = H.get_equipped_item(slot_w_uniform)
if(U)
var/obj/item/clothing/accessory/A = new accessory
U.attach_accessory(H, A)
if(!accessory_contents.len)
return
if(istype(A, /obj/item/clothing/accessory/storage))
var/obj/item/clothing/accessory/storage/S = A
for(var/v in accessory_contents)
var/number = accessory_contents[v]
for(var/i in 1 to number)
var/obj/item/I = new v
S.hold.insert_into_storage(I)
else if(istype(A, /obj/item/clothing/accessory/holster))
var/obj/item/clothing/accessory/holster/holster = A
var/w_type = accessory_contents[1]
var/obj/item/W = new w_type(H.loc)
if(W)
holster.holster(W, H)
/obj/outfit/proc/equip_suit_accessory(mob/living/carbon/human/H)
if(!H)
return
var/obj/item/clothing/suit/S = H.get_equipped_item(slot_wear_suit)
if(S)
var/obj/item/clothing/accessory/A = new suit_accessory
S.attach_accessory(H, A)
/**
* This proc handles actions done after the outfit was equipped,
* eg. toggling internals, personalizations or similar
*
* This process can and does sleep, and should never be waited upon, but only invoked asyncronously (`INVOKE_ASYNC`)
*/
/obj/outfit/proc/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
//to be overriden for changing items post equip (such as toggeling internals, ...)
/obj/outfit/proc/equip(mob/living/carbon/human/H, visualsOnly = FALSE)
SHOULD_NOT_SLEEP(TRUE)
//Start with uniform,suit,backpack for additional slots
if(back)
equip_item(H, back, slot_back)
if(uniform)
equip_item(H, uniform, slot_w_uniform)
if(accessory)
equip_uniform_accessory(H)
var/got_suit = FALSE
if(length(species_suit))
var/path = species_suit[H.species.name]
if(path)
got_suit = TRUE
equip_item(H, path, slot_wear_suit)
if(suit_accessory)
equip_suit_accessory(H)
if(suit && !got_suit)
equip_item(H, suit, slot_wear_suit)
if(suit_accessory)
equip_suit_accessory(H)
if(belt)
equip_item(H, belt, slot_belt)
var/got_gloves = FALSE
if(length(species_gloves))
var/path = species_gloves[H.species.name]
if(path)
got_gloves = TRUE
equip_item(H, path, slot_gloves)
if(gloves && !got_gloves)
equip_item(H, gloves, slot_gloves)
if(wrist)
equip_item(H, wrist, slot_wrists)
var/got_shoes = FALSE
if(pants)
equip_item(H, pants, slot_pants)
if(length(species_shoes))
var/path = species_shoes[H.species.name]
if(path)
got_shoes = TRUE
equip_item(H, path, slot_shoes)
if(shoes && !got_shoes)
equip_item(H, shoes, slot_shoes)
var/got_head = FALSE
if(length(species_head))
var/path = species_head[H.species.name]
if(path)
got_head = TRUE
equip_item(H, path, slot_head)
if(head && !got_head)
equip_item(H, head, slot_head)
if(mask)
equip_item(H, mask, slot_wear_mask)
if(l_ear)
equip_item(H, l_ear, slot_l_ear)
if(r_ear)
equip_item(H, r_ear, slot_r_ear)
if(glasses)
equip_item(H, glasses, slot_glasses)
if(suit_store)
equip_item(H, suit_store, slot_s_store)
//Hand equips. If person is missing an arm or hand it attempts to put it in the other hand.
//Override_collect should attempt to collect any items that can't be equipped regardless of collect_not_del settings for the outfit.
if(l_hand)
var/obj/item/organ/external/O
O = H.organs_by_name[BP_L_HAND]
if(!O || !O.is_usable())
equip_item(H, l_hand, slot_r_hand, override_collect = TRUE)
else
equip_item(H, l_hand, slot_l_hand, override_collect = TRUE)
if(r_hand)
var/obj/item/organ/external/O
O = H.organs_by_name[BP_R_HAND]
if(!O || !O.is_usable())
equip_item(H, r_hand, slot_l_hand, override_collect = TRUE)
else
equip_item(H, r_hand, slot_r_hand, override_collect = TRUE)
if(allow_pda_choice)
switch(H.pda_choice)
if (OUTFIT_NOTHING)
pda = null
if (OUTFIT_TABLET)
pda = tablet
if (OUTFIT_WRISTBOUND)
pda = wristbound
else
pda = tab_pda
if(pda && !visualsOnly)
var/obj/item/I = new pda(H)
switch(H.pda_choice)
if(OUTFIT_TAB_PDA)
I.desc_extended += "For its many years of service, this model has held a virtual monopoly for PDA models for NanoTrasen. The secret? A lapel pin affixed to the back."
if(OUTFIT_PDA_OLD)
I.icon = 'icons/obj/modular_computers/pda_old.dmi'
I.desc_extended += "Nicknamed affectionately as the 'Brick', PDA enthusiasts rejoice with the return of an old favorite, retrofitted to new modular computing standards."
if(OUTFIT_PDA_RUGGED)
I.icon = 'icons/obj/modular_computers/pda_rugged.dmi'
I.desc_extended += "EVA enthusiasts and owners of fat fingers just LOVE the huge tactile buttons provided by this model. Prone to butt-dialing, but don't let that hold you back."
if(OUTFIT_PDA_SLATE)
I.icon = 'icons/obj/modular_computers/pda_slate.dmi'
I.desc_extended += "A bet between an engineer and a disgruntled scientist, it turns out you CAN make a PDA out of an atmospherics scanner. Also, probably don't tell management, just enjoy."
if(OUTFIT_PDA_SMART)
I.icon = 'icons/obj/modular_computers/pda_smart.dmi'
I.desc_extended += "NanoTrasen originally designed this as a portable media player. Unfortunately, Royalty-free and corporate-approved ukulele isn't particularly popular."
I.update_icon()
if(!H.wrists && H.pda_choice == OUTFIT_WRISTBOUND)
H.equip_or_collect(I, slot_wrists)
else
H.equip_or_collect(I, slot_wear_id)
if(!visualsOnly) // Items in pockets or backpack don't show up on mob's icon.
if(l_pocket)
equip_item(H, l_pocket, slot_l_store)
if(r_pocket)
equip_item(H, r_pocket, slot_r_store)
if(H.back) // you would think, right
for(var/path in backpack_contents)
var/number = backpack_contents[path]
for(var/i in 1 to number)
H.equip_or_collect(new path(H), slot_in_backpack)
else
var/obj/item/storage/storage_item
if(!H.l_hand)
storage_item = new /obj/item/storage/bag/plasticbag(H)
H.equip_to_slot_or_del(storage_item, slot_l_hand)
if(!storage_item && !H.r_hand)
storage_item = new /obj/item/storage/bag/plasticbag(H)
H.equip_to_slot_or_del(storage_item, slot_r_hand)
if(storage_item)
for(var/path in backpack_contents)
var/number = backpack_contents[path]
for(var/i in 1 to number)
storage_item.handle_item_insertion(new path(H.loc), TRUE)
for(var/path in belt_contents)
var/number = belt_contents[path]
for(var/i in 1 to number)
H.equip_or_collect(new path(H), slot_in_belt)
if(id)
var/obj/item/modular_computer/personal_computer
if(istype(H.wear_id, /obj/item/modular_computer))
personal_computer = H.wear_id
else if(istype(H.wrists, /obj/item/modular_computer))
personal_computer = H.wrists
var/obj/item/ID = new id(H)
imprint_idcard(H, ID)
if(personal_computer?.card_slot)
addtimer(CALLBACK(src, PROC_REF(register_pda), personal_computer, ID), 2 SECOND)
else
H.equip_or_collect(ID, slot_wear_id)
INVOKE_ASYNC(src, PROC_REF(post_equip), H, visualsOnly)
if(!visualsOnly)
apply_fingerprints(H)
if(implants)
for(var/implant_type in implants)
var/obj/item/implant/I = new implant_type(H)
if(I.implanted(H))
I.forceMove(H)
I.imp_in = H
I.implanted = 1
var/obj/item/organ/external/affected = H.get_organ(BP_HEAD)
affected.implants += I
I.part = affected
if(spells)
for(var/spell in spells)
var/spell/new_spell = new spell
H.add_spell(new_spell)
if(spells[spell] > 1)
for(var/i = 1 to spells[spell])
new_spell.empower_spell()
H.update_body()
return 1
// this proc takes all the scattered voidsuit pieces and reassembles them into one piece
/obj/outfit/proc/organize_voidsuit(mob/living/carbon/human/H, var/add_magboots = TRUE)
var/obj/item/tank/T = H.s_store
H.unEquip(T, TRUE)
var/obj/item/clothing/suit/space/void/VS = H.wear_suit
H.unEquip(VS, TRUE)
var/obj/item/clothing/head/helmet/VH = H.head
H.unEquip(VH, TRUE, VS)
VS.helmet = VH
T.forceMove(VS)
VS.tank = T
if(add_magboots)
var/obj/item/clothing/shoes/magboots/M = new /obj/item/clothing/shoes/magboots(VH)
VS.boots = M
H.equip_to_slot_if_possible(VS, slot_wear_suit)
/obj/outfit/proc/apply_fingerprints(mob/living/carbon/human/H)
if(!istype(H))
return
if(H.back)
H.back.add_fingerprint(H, 1) //The 1 sets a flag to ignore gloves
for(var/obj/item/I in H.back.contents)
I.add_fingerprint(H, 1)
if(H.wear_id)
H.wear_id.add_fingerprint(H, 1)
if(H.w_uniform)
H.w_uniform.add_fingerprint(H, 1)
if(H.wear_suit)
H.wear_suit.add_fingerprint(H, 1)
if(H.wear_mask)
H.wear_mask.add_fingerprint(H, 1)
if(H.head)
H.head.add_fingerprint(H, 1)
if(H.shoes)
H.shoes.add_fingerprint(H, 1)
if(H.gloves)
H.gloves.add_fingerprint(H, 1)
if(H.wrists)
H.wrists.add_fingerprint(H, 1)
if(H.pants)
H.pants.add_fingerprint(H, 1)
if(H.l_ear)
H.l_ear.add_fingerprint(H, 1)
if(H.r_ear)
H.r_ear.add_fingerprint(H, 1)
if(H.glasses)
H.glasses.add_fingerprint(H, 1)
if(H.belt)
H.belt.add_fingerprint(H, 1)
for(var/obj/item/I in H.belt.contents)
I.add_fingerprint(H, 1)
if(H.s_store)
H.s_store.add_fingerprint(H, 1)
if(H.l_store)
H.l_store.add_fingerprint(H, 1)
if(H.r_store)
H.r_store.add_fingerprint(H, 1)
return 1
/obj/outfit/proc/imprint_idcard(mob/living/carbon/human/H, obj/item/card/id/C)
if(istype(C))
C.access = get_id_access(H)
C.rank = get_id_rank(H)
C.assignment = get_id_assignment(H)
addtimer(CALLBACK(H, TYPE_PROC_REF(/mob, set_id_info), C), 1 SECOND) // Delay a moment to allow an icon update to happen.
if(H.mind && H.mind.initial_account)
C.associated_account_number = H.mind.initial_account.account_number
/obj/outfit/proc/register_pda(obj/item/modular_computer/P, obj/item/card/id/I)
if(!P.card_slot)
return
P.card_slot.insert_id(I)
if(P.card_slot.stored_card && !P.hidden)
P.set_autorun("ntnrc_client")
P.enable_computer(null, TRUE) // passing null because we don't want the UI to open
P.minimize_program()
/obj/outfit/proc/get_id_access(mob/living/carbon/human/H)
return list()
/obj/outfit/proc/get_id_assignment(mob/living/carbon/human/H, var/ignore_suffix = FALSE)
. = GetAssignment(H)
if (. && . != "Unassigned" && H?.mind?.selected_faction && !ignore_suffix)
if (H.mind.selected_faction.title_suffix)
. += " ([H.mind.selected_faction.title_suffix])"
/obj/outfit/proc/get_id_rank(mob/living/carbon/human/H)
return GetAssignment(H)