mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-24 05:30:05 +01:00
[MIRROR] Speeds up the preference menu, significantly. Adds object pooling, other stuff too [MDB IGNORE] (#9962)
* Speeds up the preference menu, significantly. Adds object pooling, other stuff too * First fixes * Feex * Quick fix for the unit test to shut up * Fixing the runtime with randomly-colored jumpsuits * Fixes that one hard del Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com> Co-authored-by: GoldenAlpharex <58045821+GoldenAlpharex@users.noreply.github.com> Co-authored-by: GoldenAlpharex <jerego1234@hotmail.com>
This commit is contained in:
co-authored by
LemonInTheDark
GoldenAlpharex
GoldenAlpharex
parent
f7a4750b59
commit
b7164873d4
@@ -260,7 +260,7 @@
|
||||
#define SSAIR_SUPERCONDUCTIVITY 7
|
||||
#define SSAIR_PROCESS_ATOMS 8
|
||||
|
||||
//Pipeline rebuild helper defines, these suck but it'll do for now
|
||||
//Pipeline rebuild helper defines, these suck but it'll do for now //Fools you actually merged it
|
||||
#define SSAIR_REBUILD_PIPELINE 1
|
||||
#define SSAIR_REBUILD_QUEUE 2
|
||||
|
||||
@@ -269,6 +269,25 @@
|
||||
#define SSEXPLOSIONS_TURFS 2
|
||||
#define SSEXPLOSIONS_THROWS 3
|
||||
|
||||
// Wardrobe subsystem tasks
|
||||
#define SSWARDROBE_STOCK 1
|
||||
#define SSWARDROBE_INSPECT 2
|
||||
|
||||
//Wardrobe cache metadata indexes
|
||||
#define WARDROBE_CACHE_COUNT 1
|
||||
#define WARDROBE_CACHE_LAST_INSPECT 2
|
||||
#define WARDROBE_CACHE_CALL_INSERT 3
|
||||
#define WARDROBE_CACHE_CALL_REMOVAL 4
|
||||
|
||||
//Wardrobe preloaded stock indexes
|
||||
#define WARDROBE_STOCK_CONTENTS 1
|
||||
#define WARDROBE_STOCK_CALL_INSERT 2
|
||||
#define WARDROBE_STOCK_CALL_REMOVAL 3
|
||||
|
||||
//Wardrobe callback master list indexes
|
||||
#define WARDROBE_CALLBACK_INSERT 1
|
||||
#define WARDROBE_CALLBACK_REMOVE 2
|
||||
|
||||
// Subsystem delta times or tickrates, in seconds. I.e, how many seconds in between each process() call for objects being processed by that subsystem.
|
||||
// Only use these defines if you want to access some other objects processing delta_time, otherwise use the delta_time that is sent as a parameter to process()
|
||||
#define SSFLUIDS_DT (SSfluids.wait/10)
|
||||
|
||||
@@ -0,0 +1,358 @@
|
||||
/// This subsystem strives to make loading large amounts of select objects as smooth at execution as possible
|
||||
/// It preloads a set of types to store, and caches them until requested
|
||||
/// Doesn't catch everything mind, this is intentional. There's many types that expect to either
|
||||
/// A: Not sit in a list for 2 hours, or B: have extra context passed into them, or for their parent to be their location
|
||||
/// You should absolutely not spam this system, it will break things in new and wonderful ways
|
||||
/// S close enough for government work though.
|
||||
/// Fuck you goonstation
|
||||
SUBSYSTEM_DEF(wardrobe)
|
||||
name = "Wardrobe"
|
||||
wait = 10 // This is more like a queue then anything else
|
||||
flags = SS_BACKGROUND
|
||||
runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT // We're going to fill up our cache while players sit in the lobby
|
||||
/// How much to cache outfit items
|
||||
/// Multiplier, 2 would mean cache enough items to stock 1 of each preloaded order twice, etc
|
||||
var/cache_intensity = 2
|
||||
/// How many more then the template of a type are we allowed to have before we delete applicants?
|
||||
var/overflow_lienency = 2
|
||||
/// List of type -> list(insertion callback, removal callback) callbacks for insertion/removal to use.
|
||||
/// Set in setup_callbacks, used in canonization.
|
||||
var/list/initial_callbacks = list()
|
||||
/// Canonical list of types required to fill all preloaded stocks once.
|
||||
/// Type -> list(count, last inspection timestamp, call on insert, call on removal)
|
||||
var/list/canon_minimum = list()
|
||||
/// List of types to load. Type -> count //(I'd do a list of lists but this needs to be refillable)
|
||||
var/list/order_list = list()
|
||||
/// List of lists. Contains our preloaded atoms. Type -> list(last inspect time, list(instances))
|
||||
var/list/preloaded_stock = list()
|
||||
/// The last time we inspected our stock
|
||||
var/last_inspect_time = 0
|
||||
/// How often to inspect our stock, in deciseconds
|
||||
var/inspect_delay = 30 SECONDS
|
||||
/// What we're currently doing
|
||||
var/current_task = SSWARDROBE_STOCK
|
||||
/// How many times we've had to generate a stock item on request
|
||||
var/stock_miss = 0
|
||||
/// How many times we've successfully returned a cached item
|
||||
var/stock_hit = 0
|
||||
/// How many items would we make just by loading the master list once?
|
||||
var/one_go_master = 0
|
||||
|
||||
/datum/controller/subsystem/wardrobe/Initialize(start_timeofday)
|
||||
. = ..()
|
||||
setup_callbacks()
|
||||
load_outfits()
|
||||
load_species()
|
||||
load_pda_nicknacks()
|
||||
load_storage_contents()
|
||||
hard_refresh_queue()
|
||||
stock_hit = 0
|
||||
stock_miss = 0
|
||||
|
||||
/// Resets the load queue to the master template, accounting for the existing stock
|
||||
/datum/controller/subsystem/wardrobe/proc/hard_refresh_queue()
|
||||
for(var/datum/type_to_queue as anything in canon_minimum)
|
||||
var/list/master_info = canon_minimum[type_to_queue]
|
||||
var/amount_to_load = master_info[WARDROBE_CACHE_COUNT] * cache_intensity
|
||||
|
||||
var/list/stock_info = preloaded_stock[type_to_queue]
|
||||
if(stock_info) // If we already have stuff, reduce the amount we load
|
||||
amount_to_load -= length(stock_info[WARDROBE_STOCK_CONTENTS])
|
||||
set_queue_item(type_to_queue, amount_to_load)
|
||||
|
||||
/datum/controller/subsystem/wardrobe/stat_entry(msg)
|
||||
var/total_provided = max(stock_hit + stock_miss, 1)
|
||||
var/current_max_store = (one_go_master * cache_intensity) + (overflow_lienency * length(canon_minimum))
|
||||
msg += " P:[length(canon_minimum)] Q:[length(order_list)] S:[length(preloaded_stock)] I:[cache_intensity] O:[overflow_lienency]"
|
||||
msg += " H:[stock_hit] M:[stock_miss] T:[total_provided] H/T:[PERCENT(stock_hit / total_provided)]% M/T:[PERCENT(stock_miss / total_provided)]%"
|
||||
msg += " MAX:[current_max_store]"
|
||||
msg += " ID:[inspect_delay] NI:[last_inspect_time + inspect_delay]"
|
||||
return ..()
|
||||
|
||||
/datum/controller/subsystem/wardrobe/fire(resumed=FALSE)
|
||||
if(current_task != SSWARDROBE_INSPECT && world.time - last_inspect_time >= inspect_delay)
|
||||
current_task = SSWARDROBE_INSPECT
|
||||
|
||||
switch(current_task)
|
||||
if(SSWARDROBE_STOCK)
|
||||
stock_wardrobe()
|
||||
if(SSWARDROBE_INSPECT)
|
||||
run_inspection()
|
||||
if(state != SS_RUNNING)
|
||||
return
|
||||
current_task = SSWARDROBE_STOCK
|
||||
last_inspect_time = world.time
|
||||
|
||||
/// Turns the order list into actual loaded items, this is where most work is done
|
||||
/datum/controller/subsystem/wardrobe/proc/stock_wardrobe()
|
||||
for(var/atom/movable/type_to_stock as anything in order_list)
|
||||
var/amount_to_stock = order_list[type_to_stock]
|
||||
for(var/i in 1 to amount_to_stock)
|
||||
if(MC_TICK_CHECK)
|
||||
order_list[type_to_stock] = (amount_to_stock - (i - 1)) // Account for types we've already created
|
||||
return
|
||||
var/atom/movable/new_member = new type_to_stock()
|
||||
stash_object(new_member)
|
||||
|
||||
order_list -= type_to_stock
|
||||
if(MC_TICK_CHECK)
|
||||
return
|
||||
|
||||
/// Once every medium while, go through the current stock and make sure we don't have too much of one thing
|
||||
/// Or that we're not too low on some other stock
|
||||
/// This exists as a failsafe, so the wardrobe doesn't just end up generating too many items or accidentially running out somehow
|
||||
/datum/controller/subsystem/wardrobe/proc/run_inspection()
|
||||
for(var/datum/loaded_type as anything in canon_minimum)
|
||||
var/list/master_info = canon_minimum[loaded_type]
|
||||
var/last_looked_at = master_info[WARDROBE_CACHE_LAST_INSPECT]
|
||||
if(last_looked_at == last_inspect_time)
|
||||
continue
|
||||
|
||||
var/list/stock_info = preloaded_stock[loaded_type]
|
||||
var/amount_held = 0
|
||||
if(stock_info)
|
||||
var/list/held_objects = stock_info[WARDROBE_STOCK_CONTENTS]
|
||||
amount_held = length(held_objects)
|
||||
|
||||
var/target_stock = master_info[WARDROBE_CACHE_COUNT] * cache_intensity
|
||||
var/target_delta = amount_held - target_stock
|
||||
// If we've got too much
|
||||
if(target_delta > overflow_lienency)
|
||||
unload_stock(loaded_type, target_delta - overflow_lienency)
|
||||
if(state != SS_RUNNING)
|
||||
return
|
||||
|
||||
// If we have more then we target, just don't you feel me?
|
||||
target_delta = min(target_delta, 0) //I only want negative numbers to matter here
|
||||
|
||||
// If we don't have enough, queue enough to make up the remainder
|
||||
// If we have too much in the queue, cull to 0. We do this so time isn't wasted creating and destroying entries
|
||||
set_queue_item(loaded_type, abs(target_delta))
|
||||
|
||||
master_info[WARDROBE_CACHE_LAST_INSPECT] = last_inspect_time
|
||||
|
||||
if(MC_TICK_CHECK)
|
||||
return
|
||||
|
||||
/// Takes a path to get the callback owner for
|
||||
/// Returns the deepest path in our callback store that matches the input
|
||||
/// The hope is this will prevent dumb conflicts, since the furthest down is always going to be the most relevant
|
||||
/datum/controller/subsystem/wardrobe/proc/get_callback_type(datum/to_check)
|
||||
var/longest_path
|
||||
var/longest_path_length = 0
|
||||
for(var/datum/path as anything in initial_callbacks)
|
||||
if(ispath(to_check, path))
|
||||
var/stringpath = "[path]"
|
||||
var/pathlength = length(splittext(stringpath, "/")) // We get the "depth" of the path
|
||||
if(pathlength < longest_path_length)
|
||||
continue
|
||||
longest_path = path
|
||||
longest_path_length = pathlength
|
||||
return longest_path
|
||||
|
||||
/**
|
||||
* Canonizes the type, which means it's now managed by the subsystem, and will be created deleted and passed out to comsumers
|
||||
*
|
||||
* Arguments:
|
||||
* * type to stock - What type exactly do you want us to remember?
|
||||
*
|
||||
*/
|
||||
/datum/controller/subsystem/wardrobe/proc/canonize_type(type_to_stock)
|
||||
if(!type_to_stock)
|
||||
return
|
||||
if(!ispath(type_to_stock))
|
||||
stack_trace("Non path [type_to_stock] attempted to canonize itself. Something's fucky")
|
||||
var/list/master_info = canon_minimum[type_to_stock]
|
||||
if(!master_info)
|
||||
master_info = new /list(WARDROBE_CACHE_CALL_REMOVAL)
|
||||
master_info[WARDROBE_CACHE_COUNT] = 0
|
||||
//Decide on the appropriate callbacks to use
|
||||
var/callback_type = get_callback_type(type_to_stock)
|
||||
var/list/callback_info = initial_callbacks[callback_type]
|
||||
if(callback_info)
|
||||
master_info[WARDROBE_CACHE_CALL_INSERT] = callback_info[WARDROBE_CALLBACK_INSERT]
|
||||
master_info[WARDROBE_CACHE_CALL_REMOVAL] = callback_info[WARDROBE_CALLBACK_REMOVE]
|
||||
canon_minimum[type_to_stock] = master_info
|
||||
master_info[WARDROBE_CACHE_COUNT] += 1
|
||||
one_go_master++
|
||||
|
||||
/datum/controller/subsystem/wardrobe/proc/add_queue_item(queued_type, amount)
|
||||
var/amount_held = order_list[queued_type] || 0
|
||||
set_queue_item(queued_type, amount_held + amount)
|
||||
|
||||
/datum/controller/subsystem/wardrobe/proc/remove_queue_item(queued_type, amount)
|
||||
var/amount_held = order_list[queued_type]
|
||||
if(!amount_held)
|
||||
return
|
||||
set_queue_item(queued_type, amount_held - amount)
|
||||
|
||||
/datum/controller/subsystem/wardrobe/proc/set_queue_item(queued_type, amount)
|
||||
var/list/master_info = canon_minimum[queued_type]
|
||||
if(!master_info)
|
||||
stack_trace("We just tried to queue a type \[[queued_type]\] that's not stored in the master canon")
|
||||
return
|
||||
|
||||
var/target_amount = master_info[WARDROBE_CACHE_COUNT] * cache_intensity
|
||||
var/list/stock_info = preloaded_stock[queued_type]
|
||||
if(stock_info)
|
||||
target_amount -= length(stock_info[WARDROBE_STOCK_CONTENTS])
|
||||
|
||||
amount = min(amount, target_amount) // If we're trying to set more then we need, don't!
|
||||
|
||||
if(amount <= 0) // If we already have all we need, end it
|
||||
order_list -= queued_type
|
||||
return
|
||||
|
||||
order_list[queued_type] = amount
|
||||
|
||||
/// Take an existing object, and insert it into our storage
|
||||
/// If we can't or won't take it, it's deleted. You do not own this object after passing it in
|
||||
/datum/controller/subsystem/wardrobe/proc/stash_object(atom/movable/object)
|
||||
var/object_type = object.type
|
||||
var/list/master_info = canon_minimum[object_type]
|
||||
// I will not permit objects you didn't reserve ahead of time
|
||||
if(!master_info)
|
||||
qdel(object)
|
||||
return
|
||||
|
||||
var/stock_target = master_info[WARDROBE_CACHE_COUNT] * cache_intensity
|
||||
var/amount_held = 0
|
||||
var/list/stock_info = preloaded_stock[object_type]
|
||||
if(stock_info)
|
||||
amount_held = length(stock_info[WARDROBE_STOCK_CONTENTS])
|
||||
|
||||
// Doublely so for things we already have too much of
|
||||
if(amount_held - stock_target >= overflow_lienency)
|
||||
qdel(object)
|
||||
return
|
||||
// Fuck off
|
||||
if(QDELETED(object))
|
||||
stack_trace("We tried to stash a qdeleted object, what did you do")
|
||||
return
|
||||
|
||||
if(!stock_info)
|
||||
stock_info = new /list(WARDROBE_STOCK_CALL_REMOVAL)
|
||||
stock_info[WARDROBE_STOCK_CONTENTS] = list()
|
||||
stock_info[WARDROBE_STOCK_CALL_INSERT] = master_info[WARDROBE_CACHE_CALL_INSERT]
|
||||
stock_info[WARDROBE_STOCK_CALL_REMOVAL] = master_info[WARDROBE_CACHE_CALL_REMOVAL]
|
||||
preloaded_stock[object_type] = stock_info
|
||||
|
||||
var/datum/callback/do_on_insert = stock_info[WARDROBE_STOCK_CALL_INSERT]
|
||||
if(do_on_insert)
|
||||
do_on_insert.object = object
|
||||
do_on_insert.Invoke()
|
||||
do_on_insert.object = null
|
||||
|
||||
object.moveToNullspace()
|
||||
stock_info[WARDROBE_STOCK_CONTENTS] += object
|
||||
|
||||
/datum/controller/subsystem/wardrobe/proc/provide_type(datum/requested_type, atom/movable/location)
|
||||
var/atom/movable/requested_object
|
||||
var/list/stock_info = preloaded_stock[requested_type]
|
||||
if(!stock_info)
|
||||
stock_miss++
|
||||
requested_object = new requested_type(location)
|
||||
return requested_object
|
||||
|
||||
var/list/contents = stock_info[WARDROBE_STOCK_CONTENTS]
|
||||
var/contents_length = length(contents)
|
||||
requested_object = contents[contents_length]
|
||||
contents.len--
|
||||
|
||||
if(QDELETED(requested_object))
|
||||
stack_trace("We somehow ended up with a qdeleted or null object in SSwardrobe's stock. Something's weird, likely to do with reinsertion. Typepath of [requested_type]")
|
||||
stock_miss++
|
||||
requested_object = new requested_type(location)
|
||||
return requested_object
|
||||
|
||||
if(location)
|
||||
requested_object.forceMove(location)
|
||||
|
||||
var/datum/callback/do_on_removal = stock_info[WARDROBE_STOCK_CALL_REMOVAL]
|
||||
if(do_on_removal)
|
||||
do_on_removal.object = requested_object
|
||||
do_on_removal.Invoke()
|
||||
do_on_removal.object = null
|
||||
|
||||
stock_hit++
|
||||
add_queue_item(requested_type, 1) // Requeue the item, under the assumption we'll never see it again
|
||||
if(!(contents_length - 1))
|
||||
preloaded_stock -= requested_type
|
||||
|
||||
return requested_object
|
||||
|
||||
/// Unloads an amount of some type we have in stock
|
||||
/// Private function, for internal use only
|
||||
/datum/controller/subsystem/wardrobe/proc/unload_stock(datum/unload_type, amount, force = FALSE)
|
||||
var/list/stock_info = preloaded_stock[unload_type]
|
||||
if(!stock_info)
|
||||
return
|
||||
|
||||
var/list/unload_from = stock_info[WARDROBE_STOCK_CONTENTS]
|
||||
for(var/i in 1 to min(amount, length(unload_from)))
|
||||
var/datum/nuke = unload_from[unload_from.len]
|
||||
unload_from.len--
|
||||
qdel(nuke)
|
||||
if(!force && MC_TICK_CHECK && length(unload_from))
|
||||
return
|
||||
|
||||
if(!length(stock_info[WARDROBE_STOCK_CONTENTS]))
|
||||
preloaded_stock -= unload_type
|
||||
|
||||
/// Sets up insertion and removal callbacks by typepath
|
||||
/// We will always use the deepest path. So /obj/item/blade/knife superceeds the entries of /obj/item and /obj/item/blade
|
||||
/// Mind this
|
||||
/datum/controller/subsystem/wardrobe/proc/setup_callbacks()
|
||||
var/list/play_with = new /list(WARDROBE_CALLBACK_REMOVE) // Turns out there's a global list of pdas. Let's work around that yeah?
|
||||
play_with[WARDROBE_CALLBACK_INSERT] = CALLBACK(null, /obj/item/pda/proc/display_pda)
|
||||
play_with[WARDROBE_CALLBACK_REMOVE] = CALLBACK(null, /obj/item/pda/proc/cloak_pda)
|
||||
initial_callbacks[/obj/item/pda] = play_with
|
||||
|
||||
play_with = new /list(WARDROBE_CALLBACK_REMOVE) // Don't want organs rotting on the job
|
||||
play_with[WARDROBE_CALLBACK_INSERT] = CALLBACK(null, /obj/item/organ/proc/enter_wardrobe)
|
||||
play_with[WARDROBE_CALLBACK_REMOVE] = CALLBACK(null, /obj/item/organ/proc/exit_wardrobe)
|
||||
initial_callbacks[/obj/item/organ] = play_with
|
||||
|
||||
play_with = new /list(WARDROBE_CALLBACK_REMOVE)
|
||||
play_with[WARDROBE_CALLBACK_REMOVE] = CALLBACK(null, /obj/item/storage/box/survival/proc/wardrobe_removal)
|
||||
initial_callbacks[/obj/item/storage/box/survival] = play_with
|
||||
|
||||
/datum/controller/subsystem/wardrobe/proc/load_outfits()
|
||||
for(var/datum/outfit/to_stock as anything in subtypesof(/datum/outfit))
|
||||
if(!initial(to_stock.preload)) // Clearly not interested
|
||||
continue
|
||||
var/datum/outfit/hang_up = new to_stock()
|
||||
for(var/datum/outfit_item as anything in hang_up.get_types_to_preload())
|
||||
canonize_type(outfit_item)
|
||||
CHECK_TICK
|
||||
|
||||
/datum/controller/subsystem/wardrobe/proc/load_species()
|
||||
for(var/datum/species/to_record as anything in subtypesof(/datum/species))
|
||||
if(!initial(to_record.preload))
|
||||
continue
|
||||
var/datum/species/fossil_record = new to_record()
|
||||
for(var/obj/item/species_request as anything in fossil_record.get_types_to_preload())
|
||||
for(var/i in 1 to 5) // Store 5 of each species, since that seems on par with 1 of each outfit
|
||||
canonize_type(species_request)
|
||||
CHECK_TICK
|
||||
|
||||
/datum/controller/subsystem/wardrobe/proc/load_pda_nicknacks()
|
||||
for(var/obj/item/pda/pager as anything in typesof(/obj/item/pda))
|
||||
var/obj/item/pda/flip_phone = new pager()
|
||||
for(var/datum/outfit_item_type as anything in flip_phone.get_types_to_preload())
|
||||
canonize_type(outfit_item_type)
|
||||
qdel(flip_phone)
|
||||
CHECK_TICK
|
||||
|
||||
/datum/controller/subsystem/wardrobe/proc/load_storage_contents()
|
||||
for(var/obj/item/storage/crate as anything in subtypesof(/obj/item/storage))
|
||||
if(!initial(crate.preload))
|
||||
continue
|
||||
var/obj/item/pda/another_crate = new crate()
|
||||
//Unlike other uses, I really don't want people being lazy with this one.
|
||||
var/list/somehow_more_boxes = another_crate.get_types_to_preload()
|
||||
if(!length(somehow_more_boxes))
|
||||
stack_trace("You appear to have set preload to true on [crate] without defining get_types_to_preload. Please be more strict about your scope, this stuff is spooky")
|
||||
for(var/datum/a_really_small_box as anything in somehow_more_boxes)
|
||||
canonize_type(a_really_small_box)
|
||||
qdel(another_crate)
|
||||
@@ -115,14 +115,33 @@
|
||||
/datum/component/storage/PreTransfer()
|
||||
update_actions()
|
||||
|
||||
/datum/component/storage/proc/set_holdable(can_hold_list, cant_hold_list)
|
||||
can_hold_description = generate_hold_desc(can_hold_list)
|
||||
/// Almost 100% of the time the lists passed into set_holdable are reused for each instance of the component
|
||||
/// Just fucking cache it 4head
|
||||
/// Yes I could generalize this, but I don't want anyone else using it. in fact, DO NOT COPY THIS
|
||||
/// If you find yourself needing this pattern, you're likely better off using static typecaches
|
||||
/// I'm not because I do not trust implementers of the storage component to use them, BUT
|
||||
/// IF I FIND YOU USING THIS PATTERN IN YOUR CODE I WILL BREAK YOU ACROSS MY KNEES
|
||||
/// ~Lemon
|
||||
GLOBAL_LIST_EMPTY(cached_storage_typecaches)
|
||||
|
||||
if (can_hold_list != null)
|
||||
can_hold = string_list(typecacheof(can_hold_list))
|
||||
/datum/component/storage/proc/set_holdable(list/can_hold_list, list/cant_hold_list)
|
||||
if(!islist(can_hold_list))
|
||||
can_hold_list = list(can_hold_list)
|
||||
if(!islist(cant_hold_list))
|
||||
cant_hold_list = list(cant_hold_list)
|
||||
|
||||
can_hold_description = generate_hold_desc(can_hold_list)
|
||||
if (can_hold_list)
|
||||
var/unique_key = can_hold_list.Join("-")
|
||||
if(!GLOB.cached_storage_typecaches[unique_key])
|
||||
GLOB.cached_storage_typecaches[unique_key] = typecacheof(can_hold_list)
|
||||
can_hold = GLOB.cached_storage_typecaches[unique_key]
|
||||
|
||||
if (cant_hold_list != null)
|
||||
cant_hold = string_list(typecacheof(cant_hold_list))
|
||||
var/unique_key = cant_hold_list.Join("-")
|
||||
if(!GLOB.cached_storage_typecaches[unique_key])
|
||||
GLOB.cached_storage_typecaches[unique_key] = typecacheof(cant_hold_list)
|
||||
cant_hold = GLOB.cached_storage_typecaches[unique_key]
|
||||
|
||||
/datum/component/storage/proc/generate_hold_desc(can_hold_list)
|
||||
var/list/desc = list()
|
||||
|
||||
+60
-21
@@ -117,6 +117,9 @@
|
||||
/// Should the toggle helmet proc be called on the helmet during equip
|
||||
var/toggle_helmet = TRUE
|
||||
|
||||
///Should we preload some of this job's items?
|
||||
var/preload = FALSE
|
||||
|
||||
/// Any undershirt. While on humans it is a string, here we use paths to stay consistent with the rest of the equips.
|
||||
var/datum/sprite_accessory/undershirt = null
|
||||
|
||||
@@ -163,35 +166,35 @@
|
||||
|
||||
//Start with uniform,suit,backpack for additional slots
|
||||
if(uniform)
|
||||
H.equip_to_slot_or_del(new uniform(H),ITEM_SLOT_ICLOTHING, TRUE)
|
||||
H.equip_to_slot_or_del(SSwardrobe.provide_type(uniform, H), ITEM_SLOT_ICLOTHING, TRUE)
|
||||
if(suit)
|
||||
H.equip_to_slot_or_del(new suit(H),ITEM_SLOT_OCLOTHING, TRUE)
|
||||
H.equip_to_slot_or_del(SSwardrobe.provide_type(suit, H), ITEM_SLOT_OCLOTHING, TRUE)
|
||||
if(back)
|
||||
H.equip_to_slot_or_del(new back(H),ITEM_SLOT_BACK, TRUE)
|
||||
H.equip_to_slot_or_del(SSwardrobe.provide_type(back, H), ITEM_SLOT_BACK, TRUE)
|
||||
if(belt)
|
||||
H.equip_to_slot_or_del(new belt(H),ITEM_SLOT_BELT, TRUE)
|
||||
H.equip_to_slot_or_del(SSwardrobe.provide_type(belt, H), ITEM_SLOT_BELT, TRUE)
|
||||
if(gloves)
|
||||
H.equip_to_slot_or_del(new gloves(H),ITEM_SLOT_GLOVES, TRUE)
|
||||
H.equip_to_slot_or_del(SSwardrobe.provide_type(gloves, H), ITEM_SLOT_GLOVES, TRUE)
|
||||
if(shoes)
|
||||
H.equip_to_slot_or_del(new shoes(H),ITEM_SLOT_FEET, TRUE)
|
||||
H.equip_to_slot_or_del(SSwardrobe.provide_type(shoes, H), ITEM_SLOT_FEET, TRUE)
|
||||
if(head)
|
||||
H.equip_to_slot_or_del(new head(H),ITEM_SLOT_HEAD, TRUE)
|
||||
H.equip_to_slot_or_del(SSwardrobe.provide_type(head, H), ITEM_SLOT_HEAD, TRUE)
|
||||
if(mask)
|
||||
H.equip_to_slot_or_del(new mask(H),ITEM_SLOT_MASK, TRUE)
|
||||
H.equip_to_slot_or_del(SSwardrobe.provide_type(mask, H), ITEM_SLOT_MASK, TRUE)
|
||||
if(neck)
|
||||
H.equip_to_slot_or_del(new neck(H),ITEM_SLOT_NECK, TRUE)
|
||||
H.equip_to_slot_or_del(SSwardrobe.provide_type(neck, H), ITEM_SLOT_NECK, TRUE)
|
||||
if(ears)
|
||||
H.equip_to_slot_or_del(new ears(H),ITEM_SLOT_EARS, TRUE)
|
||||
H.equip_to_slot_or_del(SSwardrobe.provide_type(ears, H), ITEM_SLOT_EARS, TRUE)
|
||||
if(glasses)
|
||||
H.equip_to_slot_or_del(new glasses(H),ITEM_SLOT_EYES, TRUE)
|
||||
H.equip_to_slot_or_del(SSwardrobe.provide_type(glasses, H), ITEM_SLOT_EYES, TRUE)
|
||||
if(id)
|
||||
H.equip_to_slot_or_del(new id(H),ITEM_SLOT_ID, TRUE)
|
||||
H.equip_to_slot_or_del(SSwardrobe.provide_type(id, H), ITEM_SLOT_ID, TRUE) //We don't provide ids (Fix this?)
|
||||
if(!visualsOnly && id_trim && H.wear_id)
|
||||
var/obj/item/card/id/id_card = H.wear_id
|
||||
if(istype(id_card) && !SSid_access.apply_trim_to_card(id_card, id_trim))
|
||||
WARNING("Unable to apply trim [id_trim] to [id_card] in outfit [name].")
|
||||
if(suit_store)
|
||||
H.equip_to_slot_or_del(new suit_store(H),ITEM_SLOT_SUITSTORE, TRUE)
|
||||
H.equip_to_slot_or_del(SSwardrobe.provide_type(suit_store, H), ITEM_SLOT_SUITSTORE, TRUE)
|
||||
|
||||
if(undershirt)
|
||||
H.undershirt = initial(undershirt.name)
|
||||
@@ -199,20 +202,20 @@
|
||||
if(accessory)
|
||||
var/obj/item/clothing/under/U = H.w_uniform
|
||||
if(U)
|
||||
U.attach_accessory(new accessory(H))
|
||||
U.attach_accessory(SSwardrobe.provide_type(accessory, H))
|
||||
else
|
||||
WARNING("Unable to equip accessory [accessory] in outfit [name]. No uniform present!")
|
||||
|
||||
if(l_hand)
|
||||
H.put_in_l_hand(new l_hand(H))
|
||||
H.put_in_l_hand(SSwardrobe.provide_type(l_hand, H))
|
||||
if(r_hand)
|
||||
H.put_in_r_hand(new r_hand(H))
|
||||
H.put_in_r_hand(SSwardrobe.provide_type(r_hand, H))
|
||||
|
||||
if(!visualsOnly) // Items in pockets or backpack don't show up on mob's icon.
|
||||
if(l_pocket)
|
||||
H.equip_to_slot_or_del(new l_pocket(H),ITEM_SLOT_LPOCKET, TRUE)
|
||||
H.equip_to_slot_or_del(SSwardrobe.provide_type(l_pocket, H), ITEM_SLOT_LPOCKET, TRUE)
|
||||
if(r_pocket)
|
||||
H.equip_to_slot_or_del(new r_pocket(H),ITEM_SLOT_RPOCKET, TRUE)
|
||||
H.equip_to_slot_or_del(SSwardrobe.provide_type(r_pocket, H), ITEM_SLOT_RPOCKET, TRUE)
|
||||
|
||||
if(box)
|
||||
if(!backpack_contents)
|
||||
@@ -226,7 +229,7 @@
|
||||
if(!isnum(number))//Default to 1
|
||||
number = 1
|
||||
for(var/i in 1 to number)
|
||||
H.equip_to_slot_or_del(new path(H),ITEM_SLOT_BACKPACK, TRUE)
|
||||
H.equip_to_slot_or_del(SSwardrobe.provide_type(path, H), ITEM_SLOT_BACKPACK, TRUE)
|
||||
|
||||
if(!H.head && toggle_helmet && istype(H.wear_suit, /obj/item/clothing/suit/space/hardsuit))
|
||||
var/obj/item/clothing/suit/space/hardsuit/HS = H.wear_suit
|
||||
@@ -241,13 +244,13 @@
|
||||
H.update_action_buttons_icon()
|
||||
if(implants)
|
||||
for(var/implant_type in implants)
|
||||
var/obj/item/implant/I = new implant_type(H)
|
||||
var/obj/item/implant/I = SSwardrobe.provide_type(implant_type, H)
|
||||
I.implant(H, null, TRUE)
|
||||
|
||||
// Insert the skillchips associated with this outfit into the target.
|
||||
if(skillchips)
|
||||
for(var/skillchip_path in skillchips)
|
||||
var/obj/item/skillchip/skillchip_instance = new skillchip_path()
|
||||
var/obj/item/skillchip/skillchip_instance = SSwardrobe.provide_type(skillchip_path)
|
||||
var/implant_msg = H.implant_skillchip(skillchip_instance)
|
||||
if(implant_msg)
|
||||
stack_trace("Failed to implant [H] with [skillchip_instance], on job [src]. Failure message: [implant_msg]")
|
||||
@@ -352,6 +355,42 @@
|
||||
list_clear_nulls(types)
|
||||
return types
|
||||
|
||||
/// Return a list of types to pregenerate for later equipping
|
||||
/// This should not be things that do unique stuff in Initialize() based off their location, since we'll be storing them for a while
|
||||
/datum/outfit/proc/get_types_to_preload()
|
||||
var/list/preload = list()
|
||||
preload += id
|
||||
preload += uniform
|
||||
preload += suit
|
||||
preload += suit_store
|
||||
preload += back
|
||||
//Load in backpack gear and shit
|
||||
for(var/datum/type_to_load in backpack_contents)
|
||||
for(var/i in 1 to backpack_contents[type_to_load])
|
||||
preload += type_to_load
|
||||
preload += belt
|
||||
preload += ears
|
||||
preload += glasses
|
||||
preload += gloves
|
||||
preload += head
|
||||
preload += mask
|
||||
preload += neck
|
||||
preload += shoes
|
||||
preload += l_pocket
|
||||
preload += r_pocket
|
||||
preload += l_hand
|
||||
preload += r_hand
|
||||
preload += accessory
|
||||
preload += box
|
||||
for(var/implant_type in implants)
|
||||
preload += implant_type
|
||||
for(var/skillpath in skillchips)
|
||||
preload += skillpath
|
||||
|
||||
preload -= typesof(/obj/item/clothing/under/color/random) // SKYRAT EDIT - Don't preload random jumpsuit spawners that delete themselves
|
||||
|
||||
return preload
|
||||
|
||||
/// Return a json list of this outfit
|
||||
/datum/outfit/proc/get_json_data()
|
||||
. = list()
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
name = "body egg"
|
||||
desc = "All slimy and yuck."
|
||||
icon_state = "innards"
|
||||
visual = TRUE
|
||||
zone = BODY_ZONE_CHEST
|
||||
slot = ORGAN_SLOT_PARASITE_EGG
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ GLOBAL_LIST_EMPTY(PDAs)
|
||||
|
||||
/// String name of owner
|
||||
var/owner = null
|
||||
/// Access level defined by cartridge
|
||||
/// Typepath of the default cartridge to use
|
||||
var/default_cartridge = 0
|
||||
/// Current cartridge
|
||||
var/obj/item/cartridge/cartridge = null
|
||||
@@ -97,8 +97,10 @@ GLOBAL_LIST_EMPTY(PDAs)
|
||||
var/datum/picture/picture //Scanned photo
|
||||
|
||||
var/list/contained_item = list(/obj/item/pen, /obj/item/toy/crayon, /obj/item/lipstick, /obj/item/flashlight/pen, /obj/item/clothing/mask/cigarette)
|
||||
var/obj/item/inserted_item //Used for pen, crayon, and lipstick insertion or removal. Same as above.
|
||||
|
||||
//This is the typepath to load "into" the pda
|
||||
var/obj/item/insert_type = /obj/item/pen
|
||||
//This is the currently inserted item
|
||||
var/obj/item/inserted_item
|
||||
var/underline_flag = TRUE //flag for underline
|
||||
|
||||
/obj/item/pda/suicide_act(mob/living/carbon/user)
|
||||
@@ -128,15 +130,26 @@ GLOBAL_LIST_EMPTY(PDAs)
|
||||
|
||||
GLOB.PDAs += src
|
||||
if(default_cartridge)
|
||||
cartridge = new default_cartridge(src)
|
||||
if(inserted_item)
|
||||
inserted_item = new inserted_item(src)
|
||||
else
|
||||
inserted_item = new /obj/item/pen(src)
|
||||
cartridge = SSwardrobe.provide_type(default_cartridge, src)
|
||||
cartridge.host_pda = src
|
||||
if(insert_type)
|
||||
inserted_item = SSwardrobe.provide_type(insert_type, src)
|
||||
RegisterSignal(src, COMSIG_LIGHT_EATER_ACT, .proc/on_light_eater)
|
||||
|
||||
update_appearance()
|
||||
|
||||
/obj/item/pda/Destroy()
|
||||
GLOB.PDAs -= src
|
||||
if(istype(id))
|
||||
QDEL_NULL(id)
|
||||
if(istype(cartridge))
|
||||
QDEL_NULL(cartridge)
|
||||
if(istype(pai))
|
||||
QDEL_NULL(pai)
|
||||
if(istype(inserted_item))
|
||||
QDEL_NULL(inserted_item)
|
||||
return ..()
|
||||
|
||||
/obj/item/pda/equipped(mob/user, slot)
|
||||
. = ..()
|
||||
if(!equipped)
|
||||
@@ -163,6 +176,14 @@ GLOBAL_LIST_EMPTY(PDAs)
|
||||
font_mode = FONT_MONO
|
||||
equipped = TRUE
|
||||
|
||||
/obj/item/pda/Exited(atom/movable/gone, direction)
|
||||
. = ..()
|
||||
if(gone == cartridge)
|
||||
cartridge.host_pda = null
|
||||
cartridge = null
|
||||
if(gone == inserted_item)
|
||||
inserted_item = null
|
||||
|
||||
/obj/item/pda/proc/update_label()
|
||||
name = "PDA-[owner] ([ownjob])" //Name generalisation
|
||||
|
||||
@@ -976,9 +997,8 @@ GLOBAL_LIST_EMPTY(PDAs)
|
||||
return
|
||||
|
||||
if(inserted_item)
|
||||
user.put_in_hands(inserted_item)
|
||||
to_chat(user, span_notice("You remove [inserted_item] from [src]."))
|
||||
inserted_item = null
|
||||
user.put_in_hands(inserted_item) //Don't need to manage the pen ref, handled on Exited()
|
||||
update_appearance()
|
||||
playsound(src, 'sound/machines/pda_button2.ogg', 50, TRUE)
|
||||
else
|
||||
@@ -988,11 +1008,9 @@ GLOBAL_LIST_EMPTY(PDAs)
|
||||
if(issilicon(user) || !user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK)) //TK disabled to stop cartridge teleporting into hand
|
||||
return
|
||||
if (!isnull(cartridge))
|
||||
user.put_in_hands(cartridge)
|
||||
to_chat(user, span_notice("You eject [cartridge] from [src]."))
|
||||
user.put_in_hands(cartridge) //We don't manage reference clearing here, dealt with in Exited()
|
||||
scanmode = PDA_SCANNER_NONE
|
||||
cartridge.host_pda = null
|
||||
cartridge = null
|
||||
updateSelfDialog()
|
||||
update_appearance()
|
||||
|
||||
@@ -1191,18 +1209,6 @@ GLOBAL_LIST_EMPTY(PDAs)
|
||||
explosion(src, devastation_range = -1, heavy_impact_range = -1, light_impact_range = 2, flash_range = 3)
|
||||
qdel(src)
|
||||
|
||||
/obj/item/pda/Destroy()
|
||||
GLOB.PDAs -= src
|
||||
if(istype(id))
|
||||
QDEL_NULL(id)
|
||||
if(istype(cartridge))
|
||||
QDEL_NULL(cartridge)
|
||||
if(istype(pai))
|
||||
QDEL_NULL(pai)
|
||||
if(istype(inserted_item))
|
||||
QDEL_NULL(inserted_item)
|
||||
return ..()
|
||||
|
||||
//AI verb and proc for sending PDA messages.
|
||||
|
||||
/obj/item/pda/ai/verb/cmd_toggle_pda_receiver()
|
||||
@@ -1306,6 +1312,23 @@ GLOBAL_LIST_EMPTY(PDAs)
|
||||
SIGNAL_HANDLER
|
||||
return COMPONENT_PDA_NO_DETONATE
|
||||
|
||||
/// Return a list of types you want to pregenerate and use later
|
||||
/// Do not pass in things that care about their init location, or expect extra input
|
||||
/// Also as a curtiousy to me, don't pass in any bombs
|
||||
/obj/item/pda/proc/get_types_to_preload()
|
||||
var/list/preload = list()
|
||||
preload += default_cartridge
|
||||
preload += insert_type
|
||||
return preload
|
||||
|
||||
/// Callbacks for preloading pdas
|
||||
/obj/item/pda/proc/display_pda()
|
||||
GLOB.PDAs += src
|
||||
|
||||
/// See above, we don't want jerry from accounting to try and message nullspace his new bike
|
||||
/obj/item/pda/proc/cloak_pda()
|
||||
GLOB.PDAs -= src
|
||||
|
||||
#undef PDA_SCANNER_NONE
|
||||
#undef PDA_SCANNER_MEDICAL
|
||||
#undef PDA_SCANNER_FORENSICS
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
/obj/item/pda/clown
|
||||
name = "clown PDA"
|
||||
default_cartridge = /obj/item/cartridge/virus/clown
|
||||
inserted_item = /obj/item/toy/crayon/rainbow
|
||||
insert_type = /obj/item/toy/crayon/rainbow
|
||||
icon_state = "pda-clown"
|
||||
greyscale_config = null
|
||||
greyscale_config = null
|
||||
@@ -28,7 +28,7 @@
|
||||
/obj/item/pda/mime
|
||||
name = "mime PDA"
|
||||
default_cartridge = /obj/item/cartridge/virus/mime
|
||||
inserted_item = /obj/item/toy/crayon/mime
|
||||
insert_type = /obj/item/toy/crayon/mime
|
||||
greyscale_config = /datum/greyscale_config/pda/mime
|
||||
greyscale_colors = "#e2e2e2#cc4242"
|
||||
desc = "A portable microcomputer by Thinktronic Systems, LTD. The hardware has been modified for compliance with the vows of silence."
|
||||
@@ -147,14 +147,14 @@
|
||||
/obj/item/pda/heads/rd
|
||||
name = "research director PDA"
|
||||
default_cartridge = /obj/item/cartridge/rd
|
||||
inserted_item = /obj/item/pen/fountain
|
||||
insert_type = /obj/item/pen/fountain
|
||||
greyscale_config = /datum/greyscale_config/pda/stripe_thick/head
|
||||
greyscale_colors = "#e2e2e2#000099#9F5CA5"
|
||||
|
||||
/obj/item/pda/captain
|
||||
name = "captain PDA"
|
||||
default_cartridge = /obj/item/cartridge/captain
|
||||
inserted_item = /obj/item/pen/fountain/captain
|
||||
insert_type = /obj/item/pen/fountain/captain
|
||||
greyscale_config = /datum/greyscale_config/pda/captain
|
||||
greyscale_colors = "#2C7CB2#FF0000#FFFFFF#F5D67B"
|
||||
|
||||
@@ -170,7 +170,7 @@
|
||||
/obj/item/pda/quartermaster
|
||||
name = "quartermaster PDA"
|
||||
default_cartridge = /obj/item/cartridge/quartermaster
|
||||
inserted_item = /obj/item/pen/survival
|
||||
insert_type = /obj/item/pen/survival
|
||||
greyscale_config = /datum/greyscale_config/pda/stripe_thick
|
||||
greyscale_colors = "#D6B328#6506ca#927444"
|
||||
|
||||
@@ -195,7 +195,7 @@
|
||||
/obj/item/pda/lawyer
|
||||
name = "lawyer PDA"
|
||||
default_cartridge = /obj/item/cartridge/lawyer
|
||||
inserted_item = /obj/item/pen/fountain
|
||||
insert_type = /obj/item/pen/fountain
|
||||
greyscale_colors = "#5B74A5#f7e062"
|
||||
ttone = "objection"
|
||||
|
||||
@@ -219,7 +219,7 @@
|
||||
icon_pai = "pai_overlay_library"
|
||||
icon_inactive_pai = "pai_off_overlay_library"
|
||||
default_cartridge = /obj/item/cartridge/curator
|
||||
inserted_item = /obj/item/pen/fountain
|
||||
insert_type = /obj/item/pen/fountain
|
||||
desc = "A portable microcomputer by Thinktronic Systems, LTD. This model is a WGW-11 series e-reader."
|
||||
note = "Congratulations, your station has chosen the Thinktronic 5290 WGW-11 Series E-reader and Personal Data Assistant!"
|
||||
silent = TRUE //Quiet in the library!
|
||||
@@ -239,7 +239,7 @@
|
||||
/obj/item/pda/bar
|
||||
name = "bartender PDA"
|
||||
greyscale_colors = "#333333#c7c7c7"
|
||||
inserted_item = /obj/item/pen/fountain
|
||||
insert_type = /obj/item/pen/fountain
|
||||
|
||||
/obj/item/pda/atmos
|
||||
name = "atmospherics PDA"
|
||||
|
||||
@@ -34,12 +34,6 @@
|
||||
var/mob/living/simple_animal/bot/active_bot
|
||||
var/list/botlist = list()
|
||||
|
||||
/obj/item/cartridge/Initialize(mapload)
|
||||
. = ..()
|
||||
var/obj/item/pda/pda = loc
|
||||
if(istype(pda))
|
||||
host_pda = pda
|
||||
|
||||
/obj/item/cartridge/engineering
|
||||
name = "\improper Power-ON cartridge"
|
||||
icon_state = "cart-e"
|
||||
@@ -137,8 +131,6 @@
|
||||
. = ..()
|
||||
radio = new(src)
|
||||
|
||||
|
||||
|
||||
/obj/item/cartridge/quartermaster
|
||||
name = "space parts & space vendors cartridge"
|
||||
desc = "Perfect for the Quartermaster on the go!"
|
||||
|
||||
@@ -6,21 +6,25 @@
|
||||
var/lifespan_postmortem = 6000
|
||||
///will people implanted with this act as teleporter beacons?
|
||||
var/allow_teleport = TRUE
|
||||
///The id of the timer that's qdeleting us
|
||||
var/timerid
|
||||
|
||||
/obj/item/implant/tracking/c38
|
||||
name = "TRAC implant"
|
||||
desc = "A smaller tracking implant that supplies power for only a few minutes."
|
||||
var/lifespan = 3000 //how many deciseconds does the implant last?
|
||||
///The id of the timer that's qdeleting us
|
||||
var/timerid
|
||||
allow_teleport = FALSE
|
||||
|
||||
/obj/item/implant/tracking/c38/Initialize(mapload)
|
||||
/obj/item/implant/tracking/c38/implant(mob/living/target, mob/user, silent, force)
|
||||
. = ..()
|
||||
timerid = QDEL_IN(src, lifespan)
|
||||
|
||||
/obj/item/implant/tracking/c38/Destroy()
|
||||
/obj/item/implant/tracking/c38/removed(mob/living/source, silent, special)
|
||||
. = ..()
|
||||
deltimer(timerid)
|
||||
timerid = null
|
||||
|
||||
/obj/item/implant/tracking/c38/Destroy()
|
||||
return ..()
|
||||
|
||||
/obj/item/implant/tracking/Initialize(mapload)
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
/obj/item/implant/uplink/Initialize(mapload, owner, uplink_flag)
|
||||
. = ..()
|
||||
if(!uplink_flag)
|
||||
uplink_flag = src.uplink_flag
|
||||
var/datum/component/uplink/new_uplink = AddComponent(/datum/component/uplink, _owner = owner, _lockable = TRUE, _enabled = FALSE, uplink_flag = uplink_flag, starting_tc = starting_tc)
|
||||
new_uplink.unlock_text = "Your Syndicate Uplink has been cunningly implanted in you, for a small TC fee. Simply trigger the uplink to access it."
|
||||
RegisterSignal(src, COMSIG_COMPONENT_REMOVING, .proc/_component_removal)
|
||||
|
||||
@@ -81,24 +81,52 @@
|
||||
inhand_icon_state = "utility_ce"
|
||||
worn_icon_state = "utility_ce"
|
||||
|
||||
/obj/item/storage/belt/utility/chief/full
|
||||
preload = TRUE
|
||||
|
||||
/obj/item/storage/belt/utility/chief/full/PopulateContents()
|
||||
new /obj/item/screwdriver/power(src)
|
||||
new /obj/item/crowbar/power(src)
|
||||
new /obj/item/weldingtool/electric(src) // SKYRAT EDIT - original: new /obj/item/weldingtool/experimental(src)
|
||||
new /obj/item/multitool(src)
|
||||
new /obj/item/stack/cable_coil(src)
|
||||
new /obj/item/extinguisher/mini(src)
|
||||
new /obj/item/analyzer/ranged(src) //SKYRAT EDIT - original: new /obj/item/analyzer(src)
|
||||
SSwardrobe.provide_type(/obj/item/screwdriver/power, src)
|
||||
SSwardrobe.provide_type(/obj/item/crowbar/power, src)
|
||||
SSwardrobe.provide_type(/obj/item/weldingtool/electric, src)//This can be changed if this is too much //It's been 5 years // SKYRAT EDIT - ORIGINAL: SSwardrobe.provide_type(/obj/item/weldingtool/experimental, src)
|
||||
SSwardrobe.provide_type(/obj/item/multitool, src)
|
||||
SSwardrobe.provide_type(/obj/item/stack/cable_coil, src)
|
||||
SSwardrobe.provide_type(/obj/item/extinguisher/mini, src)
|
||||
SSwardrobe.provide_type(/obj/item/analyzer/ranged, src) //SKYRAT EDIT - ORIGINAL: SSwardrobe.provide_type(/obj/item/analyzer, src)
|
||||
//much roomier now that we've managed to remove two tools
|
||||
|
||||
/obj/item/storage/belt/utility/chief/full/get_types_to_preload()
|
||||
var/list/to_preload = list() //Yes this is a pain. Yes this is the point
|
||||
to_preload += /obj/item/screwdriver/power
|
||||
to_preload += /obj/item/crowbar/power
|
||||
to_preload += /obj/item/weldingtool/electric // SKYRAT EDIT - Electric welder
|
||||
to_preload += /obj/item/multitool
|
||||
to_preload += /obj/item/stack/cable_coil
|
||||
to_preload += /obj/item/extinguisher/mini
|
||||
to_preload += /obj/item/analyzer/ranged // SKYRAT EDIT - Ranged Analyzer
|
||||
return to_preload
|
||||
|
||||
/obj/item/storage/belt/utility/full/PopulateContents()
|
||||
new /obj/item/screwdriver(src)
|
||||
new /obj/item/wrench(src)
|
||||
new /obj/item/weldingtool(src)
|
||||
new /obj/item/crowbar(src)
|
||||
new /obj/item/wirecutters(src)
|
||||
new /obj/item/multitool(src)
|
||||
new /obj/item/stack/cable_coil(src)
|
||||
SSwardrobe.provide_type(/obj/item/screwdriver, src)
|
||||
SSwardrobe.provide_type(/obj/item/wrench, src)
|
||||
SSwardrobe.provide_type(/obj/item/weldingtool, src)
|
||||
SSwardrobe.provide_type(/obj/item/crowbar, src)
|
||||
SSwardrobe.provide_type(/obj/item/wirecutters, src)
|
||||
SSwardrobe.provide_type(/obj/item/multitool, src)
|
||||
SSwardrobe.provide_type(/obj/item/stack/cable_coil, src)
|
||||
|
||||
/obj/item/storage/belt/utility/full/get_types_to_preload()
|
||||
var/list/to_preload = list() //Yes this is a pain. Yes this is the point
|
||||
to_preload += /obj/item/screwdriver
|
||||
to_preload += /obj/item/wrench
|
||||
to_preload += /obj/item/weldingtool
|
||||
to_preload += /obj/item/crowbar
|
||||
to_preload += /obj/item/wirecutters
|
||||
to_preload += /obj/item/multitool
|
||||
to_preload += /obj/item/stack/cable_coil
|
||||
return to_preload
|
||||
|
||||
/obj/item/storage/belt/utility/full/powertools
|
||||
preload = FALSE
|
||||
|
||||
/obj/item/storage/belt/utility/full/powertools/PopulateContents()
|
||||
new /obj/item/screwdriver/power(src)
|
||||
@@ -119,22 +147,47 @@
|
||||
new /obj/item/stack/cable_coil(src)
|
||||
|
||||
/obj/item/storage/belt/utility/full/engi/PopulateContents()
|
||||
new /obj/item/screwdriver(src)
|
||||
new /obj/item/wrench(src)
|
||||
new /obj/item/weldingtool/largetank(src)
|
||||
new /obj/item/crowbar(src)
|
||||
new /obj/item/wirecutters(src)
|
||||
new /obj/item/multitool(src)
|
||||
new /obj/item/stack/cable_coil(src)
|
||||
SSwardrobe.provide_type(/obj/item/screwdriver, src)
|
||||
SSwardrobe.provide_type(/obj/item/wrench, src)
|
||||
SSwardrobe.provide_type(/obj/item/weldingtool/largetank, src)
|
||||
SSwardrobe.provide_type(/obj/item/crowbar, src)
|
||||
SSwardrobe.provide_type(/obj/item/wirecutters, src)
|
||||
SSwardrobe.provide_type(/obj/item/multitool, src)
|
||||
SSwardrobe.provide_type(/obj/item/stack/cable_coil, src)
|
||||
|
||||
/obj/item/storage/belt/utility/full/engi/get_types_to_preload()
|
||||
var/list/to_preload = list() //Yes this is a pain. Yes this is the point
|
||||
to_preload += /obj/item/screwdriver
|
||||
to_preload += /obj/item/wrench
|
||||
to_preload += /obj/item/weldingtool/largetank
|
||||
to_preload += /obj/item/crowbar
|
||||
to_preload += /obj/item/wirecutters
|
||||
to_preload += /obj/item/multitool
|
||||
to_preload += /obj/item/stack/cable_coil
|
||||
return to_preload
|
||||
|
||||
/obj/item/storage/belt/utility/atmostech/PopulateContents()
|
||||
new /obj/item/screwdriver(src)
|
||||
new /obj/item/wrench(src)
|
||||
new /obj/item/weldingtool(src)
|
||||
new /obj/item/crowbar(src)
|
||||
new /obj/item/wirecutters(src)
|
||||
new /obj/item/t_scanner(src)
|
||||
new /obj/item/extinguisher/mini(src)
|
||||
SSwardrobe.provide_type(/obj/item/screwdriver, src)
|
||||
SSwardrobe.provide_type(/obj/item/wrench, src)
|
||||
SSwardrobe.provide_type(/obj/item/weldingtool, src)
|
||||
SSwardrobe.provide_type(/obj/item/crowbar, src)
|
||||
SSwardrobe.provide_type(/obj/item/wirecutters, src)
|
||||
SSwardrobe.provide_type(/obj/item/t_scanner, src)
|
||||
SSwardrobe.provide_type(/obj/item/extinguisher/mini, src)
|
||||
|
||||
/obj/item/storage/belt/utility/atmostech/get_types_to_preload()
|
||||
var/list/to_preload = list() //Yes this is a pain. Yes this is the point
|
||||
to_preload += /obj/item/screwdriver
|
||||
to_preload += /obj/item/wrench
|
||||
to_preload += /obj/item/weldingtool
|
||||
to_preload += /obj/item/crowbar
|
||||
to_preload += /obj/item/wirecutters
|
||||
to_preload += /obj/item/t_scanner
|
||||
to_preload += /obj/item/extinguisher/mini
|
||||
return to_preload
|
||||
|
||||
/obj/item/storage/belt/utility/syndicate
|
||||
preload = FALSE
|
||||
|
||||
/obj/item/storage/belt/utility/syndicate/PopulateContents()
|
||||
new /obj/item/screwdriver/nuke(src)
|
||||
@@ -215,16 +268,30 @@
|
||||
/obj/item/stack/sticky_tape //surgical tape
|
||||
))
|
||||
|
||||
/obj/item/storage/belt/medical/paramedic
|
||||
preload = TRUE
|
||||
|
||||
/obj/item/storage/belt/medical/paramedic/PopulateContents()
|
||||
new /obj/item/sensor_device(src)
|
||||
new /obj/item/pinpointer/crew/prox(src)
|
||||
new /obj/item/stack/medical/gauze/twelve(src)
|
||||
new /obj/item/reagent_containers/syringe(src)
|
||||
new /obj/item/stack/medical/bone_gel(src)
|
||||
new /obj/item/stack/sticky_tape/surgical(src)
|
||||
new /obj/item/reagent_containers/glass/bottle/formaldehyde(src)
|
||||
SSwardrobe.provide_type(/obj/item/sensor_device, src)
|
||||
SSwardrobe.provide_type(/obj/item/pinpointer/crew/prox, src)
|
||||
SSwardrobe.provide_type(/obj/item/stack/medical/gauze/twelve, src)
|
||||
SSwardrobe.provide_type(/obj/item/reagent_containers/syringe, src)
|
||||
SSwardrobe.provide_type(/obj/item/stack/medical/bone_gel, src)
|
||||
SSwardrobe.provide_type(/obj/item/stack/sticky_tape/surgical, src)
|
||||
SSwardrobe.provide_type(/obj/item/reagent_containers/glass/bottle/formaldehyde, src)
|
||||
update_appearance()
|
||||
|
||||
/obj/item/storage/belt/medical/paramedic/get_types_to_preload()
|
||||
var/list/to_preload = list() //Yes this is a pain. Yes this is the point
|
||||
to_preload += /obj/item/sensor_device
|
||||
to_preload += /obj/item/pinpointer/crew/prox
|
||||
to_preload += /obj/item/stack/medical/gauze/twelve
|
||||
to_preload += /obj/item/reagent_containers/syringe
|
||||
to_preload += /obj/item/stack/medical/bone_gel
|
||||
to_preload += /obj/item/stack/sticky_tape/surgical
|
||||
to_preload += /obj/item/reagent_containers/glass/bottle/formaldehyde
|
||||
return to_preload
|
||||
|
||||
/obj/item/storage/belt/security
|
||||
name = "security belt"
|
||||
desc = "Can hold security gear like handcuffs and flashes."
|
||||
|
||||
@@ -141,6 +141,15 @@
|
||||
..() // we want the survival stuff too.
|
||||
new /obj/item/radio/off(src)
|
||||
|
||||
/obj/item/storage/box/survival/proc/wardrobe_removal()
|
||||
if(!isplasmaman(loc)) //We need to specially fill the box with plasmaman gear, since it's intended for one
|
||||
return
|
||||
var/obj/item/mask = locate(mask_type) in src
|
||||
var/obj/item/internals = locate(internal_type) in src
|
||||
new /obj/item/tank/internals/plasmaman/belt(src)
|
||||
qdel(mask) // Get rid of the items that shouldn't be
|
||||
qdel(internals)
|
||||
|
||||
// Mining survival box
|
||||
/obj/item/storage/box/survival/mining
|
||||
mask_type = /obj/item/clothing/mask/gas/explorer
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
var/rummage_if_nodrop = TRUE
|
||||
var/component_type = /datum/component/storage/concrete
|
||||
/// Should we preload the contents of this type?
|
||||
/// BE CAREFUL, THERE'S SOME REALLY NASTY SHIT IN THIS TYPEPATH
|
||||
/// SANTA IS EVIL
|
||||
var/preload = FALSE
|
||||
|
||||
/obj/item/storage/get_dumping_location(obj/item/storage/source,mob/user)
|
||||
return src
|
||||
@@ -54,3 +58,9 @@
|
||||
continue
|
||||
important_thing.forceMove(drop_location())
|
||||
return ..()
|
||||
|
||||
/// Returns a list of object types to be preloaded by our code
|
||||
/// I'll say it again, be very careful with this. We only need it for a few things
|
||||
/// Don't do anything stupid, please
|
||||
/obj/item/storage/proc/get_types_to_preload()
|
||||
return
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
desc = "An alien organ that twists and writhes when exposed to light."
|
||||
icon = 'icons/obj/surgery.dmi'
|
||||
icon_state = "demon_heart-on"
|
||||
visual = TRUE
|
||||
color = "#1C1C1C"
|
||||
decay_factor = 0
|
||||
/// How many life ticks in the dark the owner has been dead for. Used for nightmare respawns.
|
||||
|
||||
@@ -561,6 +561,7 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/character_preview_view)
|
||||
character.dna.real_name = character.real_name
|
||||
|
||||
if(icon_updates)
|
||||
character.icon_render_key = null //turns out if you don't set this to null update_body_parts does nothing, since it assumes the operation was cached
|
||||
character.update_body()
|
||||
character.update_hair()
|
||||
character.update_body_parts()
|
||||
|
||||
@@ -575,7 +575,7 @@
|
||||
SIGNAL_HANDLER
|
||||
|
||||
var/turf/T = get_turf(src)
|
||||
if(T.z != epicenter.z)
|
||||
if(T?.z != epicenter.z)
|
||||
return
|
||||
if(get_dist(epicenter, T) > explosion_detection_dist)
|
||||
return
|
||||
|
||||
@@ -258,6 +258,8 @@
|
||||
shoes = /obj/item/clothing/shoes/sneakers/black
|
||||
box = /obj/item/storage/box/survival
|
||||
|
||||
preload = TRUE // These are used by the prefs ui, and also just kinda could use the extra help at roundstart
|
||||
|
||||
var/backpack = /obj/item/storage/backpack
|
||||
var/satchel = /obj/item/storage/backpack/satchel
|
||||
var/duffelbag = /obj/item/storage/backpack/duffelbag
|
||||
@@ -333,6 +335,16 @@
|
||||
types += duffelbag
|
||||
return types
|
||||
|
||||
/datum/outfit/job/get_types_to_preload()
|
||||
var/list/preload = ..()
|
||||
preload += backpack
|
||||
preload += satchel
|
||||
preload += duffelbag
|
||||
preload += /obj/item/storage/backpack/satchel/leather
|
||||
var/skirtpath = "[uniform]/skirt"
|
||||
preload += text2path(skirtpath)
|
||||
return preload
|
||||
|
||||
/// An overridable getter for more dynamic goodies.
|
||||
/datum/job/proc/get_mail_goodies(mob/recipient)
|
||||
return mail_goodies
|
||||
|
||||
@@ -58,6 +58,8 @@ Assistant
|
||||
|
||||
var/index = (jumpsuit_number % GLOB.colored_assistant.jumpsuits.len) + 1
|
||||
|
||||
//We don't cache these, because they can delete on init
|
||||
//Too fragile, better to just eat the cost
|
||||
if (target.jumpsuit_style == PREF_SUIT)
|
||||
uniform = GLOB.colored_assistant.jumpsuits[index]
|
||||
else
|
||||
|
||||
@@ -75,6 +75,11 @@
|
||||
if(HAS_TRAIT(SSstation, STATION_TRAIT_BANANIUM_SHIPMENTS))
|
||||
backpack_contents[/obj/item/stack/sheet/mineral/bananium/five] = 1
|
||||
|
||||
/datum/outfit/job/clown/get_types_to_preload()
|
||||
. = ..()
|
||||
if(HAS_TRAIT(SSstation, STATION_TRAIT_BANANIUM_SHIPMENTS))
|
||||
. += /obj/item/stack/sheet/mineral/bananium/five
|
||||
|
||||
/datum/outfit/job/clown/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
|
||||
..()
|
||||
if(visualsOnly)
|
||||
|
||||
@@ -110,3 +110,8 @@
|
||||
head = /obj/item/clothing/head/soft/mime
|
||||
if(!visualsOnly)
|
||||
J.cooks++
|
||||
|
||||
/datum/outfit/job/cook/get_types_to_preload()
|
||||
. = ..()
|
||||
. += /obj/item/clothing/suit/apron/chef
|
||||
. += /obj/item/clothing/head/soft/mime
|
||||
|
||||
@@ -48,3 +48,9 @@
|
||||
if(GARBAGEDAY in SSevents.holidays)
|
||||
backpack_contents += /obj/item/gun/ballistic/revolver
|
||||
r_pocket = /obj/item/ammo_box/a357
|
||||
|
||||
/datum/outfit/job/janitor/get_types_to_preload()
|
||||
. = ..()
|
||||
if(GARBAGEDAY in SSevents.holidays)
|
||||
. += /obj/item/gun/ballistic/revolver
|
||||
. += /obj/item/ammo_box/a357
|
||||
|
||||
@@ -56,3 +56,8 @@
|
||||
else
|
||||
use_purple_suit = TRUE
|
||||
..()
|
||||
|
||||
/datum/outfit/job/lawyer/get_types_to_preload()
|
||||
. = ..()
|
||||
. += /obj/item/clothing/under/rank/civilian/lawyer/purpsuit
|
||||
. += /obj/item/clothing/suit/toggle/lawyer/purple
|
||||
|
||||
@@ -56,3 +56,7 @@
|
||||
..()
|
||||
if(prob(0.4))
|
||||
neck = /obj/item/clothing/neck/tie/horrible
|
||||
|
||||
/datum/outfit/job/scientist/get_types_to_preload()
|
||||
. = ..()
|
||||
. += /obj/item/clothing/neck/tie/horrible
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
name = "regenerative core"
|
||||
desc = "All that remains of a hivelord. It can be used to help keep your body going, but it will rapidly decay into uselessness."
|
||||
icon_state = "roro core 2"
|
||||
visual = FALSE
|
||||
item_flags = NOBLUDGEON
|
||||
slot = ORGAN_SLOT_REGENERATIVE_CORE
|
||||
organ_flags = NONE
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
name = "brain"
|
||||
desc = "A piece of juicy meat found in a person's head."
|
||||
icon_state = "brain"
|
||||
visual = TRUE
|
||||
throw_speed = 3
|
||||
throw_range = 5
|
||||
layer = ABOVE_MOB_LAYER
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/obj/item/organ/alien
|
||||
icon_state = "xgibmid2"
|
||||
visual = FALSE
|
||||
food_reagents = list(/datum/reagent/consumable/nutriment = 5, /datum/reagent/toxin/acid = 10)
|
||||
var/list/alien_powers = list()
|
||||
|
||||
|
||||
@@ -106,6 +106,9 @@
|
||||
/// Can other carbons be shoved into this one to make it fall?
|
||||
var/can_be_shoved_into = FALSE
|
||||
|
||||
/// Only load in visual organs
|
||||
var/visual_only_organs = FALSE
|
||||
|
||||
COOLDOWN_DECLARE(bleeding_message_cd)
|
||||
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
real_name = "Test Dummy"
|
||||
status_flags = GODMODE|CANPUSH
|
||||
mouse_drag_pointer = MOUSE_INACTIVE_POINTER
|
||||
visual_only_organs = TRUE
|
||||
var/in_use = FALSE
|
||||
|
||||
INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy)
|
||||
@@ -17,6 +18,60 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy)
|
||||
/mob/living/carbon/human/dummy/attach_rot(mapload)
|
||||
return
|
||||
|
||||
/mob/living/carbon/human/dummy/set_species(datum/species/mrace, icon_update = TRUE, pref_load = FALSE)
|
||||
harvest_organs()
|
||||
return ..()
|
||||
|
||||
///Let's extract our dummies organs and limbs for storage, to reduce the cache missed that spamming a dummy cause
|
||||
/mob/living/carbon/human/dummy/proc/harvest_organs()
|
||||
for(var/slot in list(ORGAN_SLOT_BRAIN, ORGAN_SLOT_HEART, ORGAN_SLOT_LUNGS, ORGAN_SLOT_APPENDIX, \
|
||||
ORGAN_SLOT_EYES, ORGAN_SLOT_EARS, ORGAN_SLOT_TONGUE, ORGAN_SLOT_LIVER, ORGAN_SLOT_STOMACH))
|
||||
var/obj/item/organ/current_organ = getorganslot(slot) //Time to cache it lads
|
||||
if(current_organ)
|
||||
current_organ.Remove(src, special = TRUE) //Please don't somehow kill our dummy
|
||||
SSwardrobe.stash_object(current_organ)
|
||||
|
||||
var/datum/species/current_species = dna.species
|
||||
for(var/organ_path in current_species.mutant_organs)
|
||||
var/obj/item/organ/current_organ = getorgan(organ_path)
|
||||
if(current_organ)
|
||||
current_organ.Remove(src, special = TRUE) //Please don't somehow kill our dummy
|
||||
SSwardrobe.stash_object(current_organ)
|
||||
|
||||
for(var/obj/item/organ/external/organ in internal_organs)
|
||||
if(organ.type in current_species.external_organs)
|
||||
organ.Remove(src)
|
||||
SSwardrobe.stash_object(organ)
|
||||
|
||||
//Instead of just deleting our equipment, we save what we can and reinsert it into SSwardrobe's store
|
||||
//Hopefully this makes preference reloading not the worst thing ever
|
||||
/mob/living/carbon/human/dummy/delete_equipment()
|
||||
var/list/items_to_check = get_all_slots() + held_items
|
||||
var/list/to_nuke = list() //List of items queued for deletion, can't qdel them before iterating their contents in case they hold something
|
||||
///Travel to the bottom of the contents chain, expanding it out
|
||||
for(var/i = 1; i <= length(items_to_check); i++) //Needs to be a c style loop since it can expand
|
||||
var/obj/item/checking = items_to_check[i]
|
||||
if(!checking) //Nulls in the list, depressing
|
||||
continue
|
||||
if(!isitem(checking)) //What the fuck are you on
|
||||
to_nuke += checking
|
||||
continue
|
||||
|
||||
var/list/contents = checking.contents
|
||||
if(length(contents))
|
||||
items_to_check |= contents //Please don't make an infinite loop somehow thx
|
||||
to_nuke += checking //Goodbye
|
||||
continue
|
||||
|
||||
//I'm making the bet that if you're empty of other items you're not going to OOM if reapplied. I assume you're here because I was wrong
|
||||
if(ismob(checking.loc))
|
||||
var/mob/checkings_owner = checking.loc
|
||||
checkings_owner.temporarilyRemoveItemFromInventory(checking, TRUE) //Clear out of there yeah?
|
||||
SSwardrobe.stash_object(checking)
|
||||
|
||||
for(var/obj/item/delete as anything in to_nuke)
|
||||
qdel(delete)
|
||||
|
||||
/mob/living/carbon/human/dummy/has_equipped(obj/item/item, slot, initial = FALSE)
|
||||
return item.visual_equipped(src, slot, initial)
|
||||
|
||||
|
||||
@@ -212,6 +212,9 @@ GLOBAL_LIST_EMPTY(features_by_species)
|
||||
///List of visual overlays created by handle_body()
|
||||
var/list/body_vis_overlays = list()
|
||||
|
||||
//Should we preload this species's organs?
|
||||
var/preload = TRUE
|
||||
|
||||
///////////
|
||||
// PROCS //
|
||||
///////////
|
||||
@@ -311,8 +314,9 @@ GLOBAL_LIST_EMPTY(features_by_species)
|
||||
* * old_species - datum, used when regenerate organs is called in a switching species to remove old mutant organs.
|
||||
* * replace_current - boolean, forces all old organs to get deleted whether or not they pass the species' ability to keep that organ
|
||||
* * excluded_zones - list, add zone defines to block organs inside of the zones from getting handled. see headless mutation for an example
|
||||
* * visual_only - boolean, only load organs that change how the species looks. Do not use for normal gameplay stuff
|
||||
*/
|
||||
/datum/species/proc/regenerate_organs(mob/living/carbon/C,datum/species/old_species,replace_current=TRUE,list/excluded_zones)
|
||||
/datum/species/proc/regenerate_organs(mob/living/carbon/C, datum/species/old_species, replace_current = TRUE, list/excluded_zones, visual_only = FALSE)
|
||||
//what should be put in if there is no mutantorgan (brains handled separately)
|
||||
var/list/slot_mutantorgans = list(ORGAN_SLOT_BRAIN = mutantbrain, ORGAN_SLOT_HEART = mutantheart, ORGAN_SLOT_LUNGS = mutantlungs, ORGAN_SLOT_APPENDIX = mutantappendix, \
|
||||
ORGAN_SLOT_EYES = mutanteyes, ORGAN_SLOT_EARS = mutantears, ORGAN_SLOT_TONGUE = mutanttongue, ORGAN_SLOT_LIVER = mutantliver, ORGAN_SLOT_STOMACH = mutantstomach)
|
||||
@@ -322,8 +326,12 @@ GLOBAL_LIST_EMPTY(features_by_species)
|
||||
|
||||
var/obj/item/organ/oldorgan = C.getorganslot(slot) //used in removing
|
||||
var/obj/item/organ/neworgan = slot_mutantorgans[slot] //used in adding
|
||||
|
||||
if(visual_only && !initial(neworgan.visual))
|
||||
continue
|
||||
|
||||
var/used_neworgan = FALSE
|
||||
neworgan = new neworgan()
|
||||
neworgan = SSwardrobe.provide_type(neworgan)
|
||||
var/should_have = neworgan.get_availability(src) //organ proc that points back to a species trait (so if the species is supposed to have this organ)
|
||||
|
||||
if(oldorgan && (!should_have || replace_current) && !(oldorgan.zone in excluded_zones) && !(oldorgan.organ_flags & ORGAN_UNREMOVABLE))
|
||||
@@ -339,7 +347,6 @@ GLOBAL_LIST_EMPTY(features_by_species)
|
||||
oldorgan.Remove(C,TRUE)
|
||||
QDEL_NULL(oldorgan) //we cannot just tab this out because we need to skip the deleting if it is a decoy brain.
|
||||
|
||||
|
||||
if(oldorgan)
|
||||
oldorgan.setOrganDamage(0)
|
||||
else if(should_have && !(initial(neworgan.zone) in excluded_zones))
|
||||
@@ -363,7 +370,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
|
||||
for(var/organ_path in mutant_organs)
|
||||
var/obj/item/organ/current_organ = C.getorgan(organ_path)
|
||||
if(!current_organ || replace_current)
|
||||
var/obj/item/organ/replacement = new organ_path()
|
||||
var/obj/item/organ/replacement = SSwardrobe.provide_type(organ_path)
|
||||
// If there's an existing mutant organ, we're technically replacing it.
|
||||
// Let's abuse the snowflake proc that skillchips added. Basically retains
|
||||
// feature parity with every other organ too.
|
||||
@@ -406,7 +413,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
|
||||
|
||||
C.mob_biotypes = inherent_biotypes
|
||||
|
||||
regenerate_organs(C,old_species)
|
||||
regenerate_organs(C, old_species, visual_only = C.visual_only_organs)
|
||||
|
||||
if(exotic_bloodtype && C.dna.blood_type != exotic_bloodtype)
|
||||
C.dna.blood_type = exotic_bloodtype
|
||||
@@ -426,6 +433,16 @@ GLOBAL_LIST_EMPTY(features_by_species)
|
||||
else //Entries in the list should only ever be items or null, so if it's not an item, we can assume it's an empty hand
|
||||
INVOKE_ASYNC(C, /mob/proc/put_in_hands, new mutanthands)
|
||||
|
||||
if(ishuman(C))
|
||||
var/mob/living/carbon/human/human = C
|
||||
for(var/obj/item/organ/external/organ_path as anything in external_organs)
|
||||
//Load a persons preferences from DNA
|
||||
var/feature_key_name = human.dna.features[initial(organ_path.feature_key)]
|
||||
|
||||
var/obj/item/organ/external/new_organ = SSwardrobe.provide_type(organ_path)
|
||||
new_organ.set_sprite(feature_key_name)
|
||||
new_organ.Insert(human)
|
||||
|
||||
for(var/X in inherent_traits)
|
||||
ADD_TRAIT(C, X, SPECIES_TRAIT)
|
||||
|
||||
@@ -2249,3 +2266,21 @@ GLOBAL_LIST_EMPTY(features_by_species)
|
||||
/// Returns the species's scream sound.
|
||||
/datum/species/proc/get_scream_sound(mob/living/carbon/human/human)
|
||||
return
|
||||
|
||||
/datum/species/proc/get_types_to_preload()
|
||||
var/list/to_store = list()
|
||||
to_store += mutant_organs
|
||||
for(var/obj/item/organ/external/horny as anything in external_organs)
|
||||
to_store += horny //Haha get it?
|
||||
|
||||
//Don't preload brains, cause reuse becomes a horrible headache
|
||||
to_store += mutantheart
|
||||
to_store += mutantlungs
|
||||
to_store += mutanteyes
|
||||
to_store += mutantears
|
||||
to_store += mutanttongue
|
||||
to_store += mutantliver
|
||||
to_store += mutantstomach
|
||||
to_store += mutantappendix
|
||||
//We don't cache mutant hands because it's not constrained enough, too high a potential for failure
|
||||
return to_store
|
||||
|
||||
@@ -99,6 +99,7 @@
|
||||
//useless organs we throw in just to fuck with surgeons a bit more
|
||||
/obj/item/organ/fly
|
||||
desc = "You have no idea what the hell this is, or how it manages to keep something alive in any capacity."
|
||||
visual = FALSE
|
||||
|
||||
/obj/item/organ/fly/Initialize(mapload)
|
||||
. = ..()
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
payday_modifier = 0.75
|
||||
family_heirlooms = list(/obj/item/flashlight/lantern/heirloom_moth)
|
||||
|
||||
/datum/species/moth/regenerate_organs(mob/living/carbon/C,datum/species/old_species,replace_current=TRUE,list/excluded_zones)
|
||||
/datum/species/moth/regenerate_organs(mob/living/carbon/C, datum/species/old_species, replace_current= TRUE, list/excluded_zones, visual_only)
|
||||
. = ..()
|
||||
if(ishuman(C))
|
||||
var/mob/living/carbon/human/H = C
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
name = "appendix"
|
||||
icon_state = "appendix"
|
||||
base_icon_state = "appendix"
|
||||
visual = FALSE
|
||||
zone = BODY_ZONE_PRECISE_GROIN
|
||||
slot = ORGAN_SLOT_APPENDIX
|
||||
food_reagents = list(/datum/reagent/consumable/nutriment = 5, /datum/reagent/toxin/bad_food = 5)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
/obj/item/organ/cyberimp
|
||||
name = "cybernetic implant"
|
||||
desc = "A state-of-the-art implant that improves a baseline's functionality."
|
||||
visual = FALSE
|
||||
status = ORGAN_ROBOTIC
|
||||
organ_flags = ORGAN_SYNTHETIC
|
||||
var/implant_color = "#FFFFFF"
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
desc = "There are three parts to the ear. Inner, middle and outer. Only one of these parts should be normally visible."
|
||||
zone = BODY_ZONE_HEAD
|
||||
slot = ORGAN_SLOT_EARS
|
||||
visual = FALSE
|
||||
gender = PLURAL
|
||||
|
||||
healing_factor = STANDARD_ORGAN_HEALING
|
||||
@@ -63,6 +64,7 @@
|
||||
name = "cat ears"
|
||||
icon = 'icons/obj/clothing/hats.dmi'
|
||||
icon_state = "kitty"
|
||||
visual = TRUE
|
||||
damage_multiplier = 2
|
||||
|
||||
//SKYRAT EDIT REMOVAL BEGIN - CUSTOMIZATION
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
///Unremovable is until the features are completely finished
|
||||
organ_flags = ORGAN_UNREMOVABLE | ORGAN_EDIBLE
|
||||
visual = TRUE
|
||||
|
||||
///Sometimes we need multiple layers, for like the back, middle and front of the person
|
||||
var/layers
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
name = BODY_ZONE_PRECISE_EYES
|
||||
icon_state = "eyeballs"
|
||||
desc = "I see you!"
|
||||
visual = TRUE
|
||||
zone = BODY_ZONE_PRECISE_EYES
|
||||
slot = ORGAN_SLOT_EYES
|
||||
gender = PLURAL
|
||||
@@ -44,7 +45,6 @@
|
||||
old_eye_color = human_owner.eye_color
|
||||
if(eye_color)
|
||||
human_owner.eye_color = eye_color
|
||||
human_owner.regenerate_icons()
|
||||
else
|
||||
eye_color = human_owner.eye_color
|
||||
if(HAS_TRAIT(human_owner, TRAIT_NIGHT_VISION) && !lighting_alpha)
|
||||
@@ -62,7 +62,6 @@
|
||||
is_emissive = TRUE
|
||||
if(eye_color)
|
||||
affected_human.eye_color = eye_color
|
||||
affected_human.regenerate_icons()
|
||||
else
|
||||
eye_color = affected_human.eye_color
|
||||
if(HAS_TRAIT(affected_human, TRAIT_NIGHT_VISION) && !lighting_alpha)
|
||||
@@ -79,7 +78,7 @@
|
||||
if(ishuman(eye_owner) && eye_color)
|
||||
var/mob/living/carbon/human/human_owner = eye_owner
|
||||
human_owner.eye_color = old_eye_color
|
||||
human_owner.regenerate_icons()
|
||||
human_owner.update_body()
|
||||
eye_owner.cure_blind(EYE_DAMAGE)
|
||||
eye_owner.cure_nearsighted(EYE_DAMAGE)
|
||||
eye_owner.set_blindness(0)
|
||||
@@ -88,6 +87,10 @@
|
||||
eye_owner.update_sight()
|
||||
is_emissive = FALSE
|
||||
|
||||
//Gotta reset the eye color, because that persists
|
||||
/obj/item/organ/eyes/enter_wardrobe()
|
||||
. = ..()
|
||||
eye_color = initial(eye_color)
|
||||
|
||||
/obj/item/organ/eyes/on_life(delta_time, times_fired)
|
||||
. = ..()
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
desc = "I feel bad for the heartless bastard who lost this."
|
||||
icon_state = "heart-on"
|
||||
base_icon_state = "heart"
|
||||
visual = FALSE
|
||||
zone = BODY_ZONE_CHEST
|
||||
slot = ORGAN_SLOT_HEART
|
||||
|
||||
@@ -256,6 +257,7 @@
|
||||
/obj/item/organ/heart/ethereal
|
||||
name = "crystal core"
|
||||
icon_state = "ethereal_heart" //Welp. At least it's more unique in functionaliy.
|
||||
visual = TRUE //This is used by the ethereal species for color
|
||||
desc = "A crystal-like organ that functions similarly to a heart for Ethereals. It can revive its owner."
|
||||
|
||||
///Cooldown for the next time we can crystalize
|
||||
@@ -273,7 +275,6 @@
|
||||
. = ..()
|
||||
add_atom_colour(ethereal_color, FIXED_COLOUR_PRIORITY)
|
||||
|
||||
|
||||
/obj/item/organ/heart/ethereal/Insert(mob/living/carbon/owner, special = 0)
|
||||
. = ..()
|
||||
RegisterSignal(owner, COMSIG_MOB_STATCHANGE, .proc/on_stat_change)
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
/obj/item/organ/liver
|
||||
name = "liver"
|
||||
icon_state = "liver"
|
||||
visual = FALSE
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
zone = BODY_ZONE_CHEST
|
||||
slot = ORGAN_SLOT_LIVER
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
var/operated = FALSE //whether we can still have our damages fixed through surgery
|
||||
name = "lungs"
|
||||
icon_state = "lungs"
|
||||
visual = FALSE
|
||||
zone = BODY_ZONE_CHEST
|
||||
slot = ORGAN_SLOT_LUNGS
|
||||
gender = PLURAL
|
||||
|
||||
@@ -33,6 +33,8 @@
|
||||
var/reagent_vol = 10
|
||||
|
||||
var/failure_time = 0
|
||||
///Do we effect the appearance of our mob. Used to save time in preference code
|
||||
var/visual = TRUE
|
||||
|
||||
// Players can look at prefs before atoms SS init, and without this
|
||||
// they would not be able to see external organs, such as moth wings.
|
||||
@@ -42,6 +44,7 @@ INITIALIZE_IMMEDIATE(/obj/item/organ)
|
||||
|
||||
/obj/item/organ/Initialize(mapload)
|
||||
. = ..()
|
||||
START_PROCESSING(SSobj, src)
|
||||
if(organ_flags & ORGAN_EDIBLE)
|
||||
AddComponent(/datum/component/edible,\
|
||||
initial_reagents = food_reagents,\
|
||||
@@ -153,10 +156,14 @@ INITIALIZE_IMMEDIATE(/obj/item/organ)
|
||||
if(damage > high_threshold)
|
||||
. += span_warning("[src] is starting to look discolored.")
|
||||
|
||||
/obj/item/organ/Initialize(mapload)
|
||||
. = ..()
|
||||
///Used as callbacks by object pooling
|
||||
/obj/item/organ/proc/exit_wardrobe()
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
//See above
|
||||
/obj/item/organ/proc/enter_wardrobe()
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
|
||||
/obj/item/organ/Destroy()
|
||||
if(owner)
|
||||
// The special flag is important, because otherwise mobs can die
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
/obj/item/organ/stomach
|
||||
name = "stomach"
|
||||
icon_state = "stomach"
|
||||
visual = FALSE
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
zone = BODY_ZONE_CHEST
|
||||
slot = ORGAN_SLOT_STOMACH
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
/obj/item/organ/tail
|
||||
name = "tail"
|
||||
desc = "A severed tail. What did you cut this off of?"
|
||||
visual = TRUE
|
||||
icon_state = "severedtail"
|
||||
zone = BODY_ZONE_PRECISE_GROIN
|
||||
slot = ORGAN_SLOT_TAIL
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
name = "tongue"
|
||||
desc = "A fleshy muscle mostly used for lying."
|
||||
icon_state = "tonguenormal"
|
||||
visual = FALSE
|
||||
zone = BODY_ZONE_PRECISE_MOUTH
|
||||
slot = ORGAN_SLOT_TONGUE
|
||||
attack_verb_continuous = list("licks", "slobbers", "slaps", "frenches", "tongues")
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/obj/item/organ/vocal_cords //organs that are activated through speech with the :x/MODE_KEY_VOCALCORDS channel
|
||||
name = "vocal cords"
|
||||
icon_state = "appendix"
|
||||
visual = FALSE
|
||||
zone = BODY_ZONE_PRECISE_MOUTH
|
||||
slot = ORGAN_SLOT_VOICE
|
||||
gender = PLURAL
|
||||
@@ -18,6 +19,7 @@
|
||||
owner.say(message, spans = spans, sanitize = FALSE)
|
||||
|
||||
/obj/item/organ/adamantine_resonator
|
||||
visual = FALSE
|
||||
name = "adamantine resonator"
|
||||
desc = "Fragments of adamantine exist in all golems, stemming from their origins as purely magical constructs. These are used to \"hear\" messages from their leaders."
|
||||
zone = BODY_ZONE_HEAD
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
#include "crayons.dm"
|
||||
#include "create_and_destroy.dm"
|
||||
#include "designs.dm"
|
||||
#include "dummy_spawn.dm"
|
||||
#include "dynamic_ruleset_sanity.dm"
|
||||
#include "egg_glands.dm"
|
||||
#include "emoting.dm"
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
///This set of tests is focused on ensuring the stability of preference dummies
|
||||
///And by extension the hacks built to make them fast
|
||||
///Organ consistency, object pooling via the wardrobe ss, etc
|
||||
|
||||
//Test spawning one of every species
|
||||
/datum/unit_test/dummy_spawn_species
|
||||
|
||||
/datum/unit_test/dummy_spawn_species/Run()
|
||||
var/mob/living/carbon/human/dummy/lad = allocate(/mob/living/carbon/human/dummy)
|
||||
for(var/datum/species/testing_testing as anything in subtypesof(/datum/species))
|
||||
lad.set_species(testing_testing, icon_update = FALSE, pref_load = TRUE) //I wonder if I should somehow hook into the species pref here
|
||||
|
||||
///Equips and devests our dummy of one of every job outfit
|
||||
/datum/unit_test/dummy_spawn_outfit
|
||||
|
||||
/datum/unit_test/dummy_spawn_outfit/Run()
|
||||
var/mob/living/carbon/human/dummy/lad = allocate(/mob/living/carbon/human/dummy)
|
||||
for(var/datum/job/one_two_three as anything in subtypesof(/datum/job))
|
||||
var/datum/job/can_you_hear_this = SSjob.GetJobType(one_two_three)
|
||||
if(!can_you_hear_this)
|
||||
log_world("Job type [one_two_three] could not be retrieved from SSjob")
|
||||
continue
|
||||
lad.job = can_you_hear_this
|
||||
lad.dress_up_as_job(can_you_hear_this, TRUE)
|
||||
lad.wipe_state() //Nuke it all
|
||||
@@ -3,6 +3,7 @@
|
||||
desc = "An implant that can be placed in a user's head to control circuits using their brain."
|
||||
icon = 'icons/obj/wiremod.dmi'
|
||||
icon_state = "bci"
|
||||
visual = FALSE
|
||||
zone = BODY_ZONE_HEAD
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
zone = BODY_ZONE_HEAD
|
||||
slot = ORGAN_SLOT_ZOMBIE
|
||||
icon_state = "blacktumor"
|
||||
visual = FALSE
|
||||
var/causes_damage = TRUE
|
||||
var/datum/species/old_species = /datum/species/human
|
||||
var/living_transformation_time = 30
|
||||
|
||||
@@ -182,7 +182,7 @@ GLOBAL_LIST_EMPTY(total_uf_len_by_block)
|
||||
holder.transform = holder.transform.Translate(0, translate)
|
||||
current_body_size = features["body_size"]
|
||||
|
||||
/mob/living/carbon/set_species(datum/species/mrace, icon_update = TRUE, var/list/override_features, var/list/override_mutantparts, var/list/override_markings, retain_features = FALSE, retain_mutantparts = FALSE)
|
||||
/mob/living/carbon/set_species(datum/species/mrace, icon_update = TRUE, pref_load = FALSE, var/list/override_features, var/list/override_mutantparts, var/list/override_markings, retain_features = FALSE, retain_mutantparts = FALSE)
|
||||
if(QDELETED(src))
|
||||
CRASH("You're trying to change your species post deletion, this is a recipe for madness")
|
||||
if(mrace && has_dna())
|
||||
@@ -194,7 +194,7 @@ GLOBAL_LIST_EMPTY(total_uf_len_by_block)
|
||||
else
|
||||
return
|
||||
deathsound = new_race.deathsound
|
||||
dna.species.on_species_loss(src, new_race)
|
||||
dna.species.on_species_loss(src, new_race, pref_load)
|
||||
var/datum/species/old_species = dna.species
|
||||
dna.species = new_race
|
||||
|
||||
@@ -221,7 +221,7 @@ GLOBAL_LIST_EMPTY(total_uf_len_by_block)
|
||||
|
||||
dna.update_body_size()
|
||||
|
||||
dna.species.on_species_gain(src, old_species)
|
||||
dna.species.on_species_gain(src, old_species, pref_load)
|
||||
|
||||
|
||||
if(ishuman(src))
|
||||
|
||||
+3
@@ -62,6 +62,9 @@
|
||||
randname = "[randname]-[rand(100, 999)]"
|
||||
return randname
|
||||
|
||||
/datum/species/robotic/get_types_to_preload()
|
||||
return ..() - typesof(/obj/item/organ/cyberimp/arm/power_cord) // Don't cache things that lead to hard deletions.
|
||||
|
||||
/datum/species/robotic/ipc
|
||||
name = "I.P.C."
|
||||
id = SPECIES_IPC
|
||||
|
||||
@@ -511,6 +511,7 @@
|
||||
#include "code\controllers\subsystem\title.dm"
|
||||
#include "code\controllers\subsystem\vis_overlays.dm"
|
||||
#include "code\controllers\subsystem\vote.dm"
|
||||
#include "code\controllers\subsystem\wardrobe.dm"
|
||||
#include "code\controllers\subsystem\weather.dm"
|
||||
#include "code\controllers\subsystem\wiremod_composite.dm"
|
||||
#include "code\controllers\subsystem\processing\acid.dm"
|
||||
|
||||
Reference in New Issue
Block a user