mirror of
https://github.com/yogstation13/Yogstation.git
synced 2025-02-26 09:04:50 +00:00
Speeds up the preference menu, implement object pooling (#17133)
* Speeds up the preference menu https://github.com/tgstation/tgstation/pull/63225 * Fix compile
This commit is contained in:
@@ -151,6 +151,8 @@
|
||||
#define ROBOTIC_LIMBS 26
|
||||
/// have no mouth to ingest/eat with
|
||||
#define NOMOUTH 27
|
||||
/// has a tail
|
||||
#define HAS_TAIL 28
|
||||
|
||||
//organ slots
|
||||
#define ORGAN_SLOT_BRAIN "brain"
|
||||
|
||||
@@ -269,3 +269,23 @@
|
||||
|
||||
/// The timer key used to know how long subsystem initialization takes
|
||||
#define SS_INIT_TIMER_KEY "ss_init"
|
||||
|
||||
|
||||
// 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
|
||||
|
||||
358
code/controllers/subsystem/wardrobe.dm
Normal file
358
code/controllers/subsystem/wardrobe.dm
Normal file
@@ -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)
|
||||
@@ -119,14 +119,33 @@
|
||||
/datum/component/storage/PreTransfer()
|
||||
update_actions()
|
||||
|
||||
/datum/component/storage/proc/set_holdable(can_hold_list, cant_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)
|
||||
|
||||
/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)
|
||||
can_hold = typecacheof(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)
|
||||
cant_hold = typecacheof(cant_hold_list)
|
||||
if (cant_hold_list != null)
|
||||
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()
|
||||
|
||||
@@ -75,6 +75,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
|
||||
|
||||
///ID of the slot containing a gas tank
|
||||
var/internals_slot = null
|
||||
|
||||
@@ -156,49 +159,49 @@
|
||||
|
||||
//Start with uniform,suit,backpack for additional slots
|
||||
if(uniform)
|
||||
H.equip_to_slot_or_del(new uniform(H),SLOT_W_UNIFORM, TRUE)
|
||||
H.equip_to_slot_or_del(SSwardrobe.provide_type(uniform, H), SLOT_W_UNIFORM, TRUE)
|
||||
if(suit)
|
||||
H.equip_to_slot_or_del(new suit(H),SLOT_WEAR_SUIT, TRUE)
|
||||
H.equip_to_slot_or_del(SSwardrobe.provide_type(suit, H), SLOT_WEAR_SUIT, TRUE)
|
||||
if(back)
|
||||
H.equip_to_slot_or_del(new back(H),SLOT_BACK, TRUE)
|
||||
H.equip_to_slot_or_del(SSwardrobe.provide_type(back, H), SLOT_BACK, TRUE)
|
||||
if(belt)
|
||||
H.equip_to_slot_or_del(new belt(H),SLOT_BELT, TRUE)
|
||||
H.equip_to_slot_or_del(SSwardrobe.provide_type(belt, H), SLOT_BELT, TRUE)
|
||||
if(gloves)
|
||||
H.equip_to_slot_or_del(new gloves(H),SLOT_GLOVES, TRUE)
|
||||
H.equip_to_slot_or_del(SSwardrobe.provide_type(gloves, H), SLOT_GLOVES, TRUE)
|
||||
if(shoes)
|
||||
H.equip_to_slot_or_del(new shoes(H),SLOT_SHOES, TRUE)
|
||||
H.equip_to_slot_or_del(SSwardrobe.provide_type(shoes, H), SLOT_SHOES, TRUE)
|
||||
if(head)
|
||||
H.equip_to_slot_or_del(new head(H),SLOT_HEAD, TRUE)
|
||||
H.equip_to_slot_or_del(SSwardrobe.provide_type(head, H), SLOT_HEAD, TRUE)
|
||||
if(mask)
|
||||
H.equip_to_slot_or_del(new mask(H),SLOT_WEAR_MASK, TRUE)
|
||||
H.equip_to_slot_or_del(SSwardrobe.provide_type(mask, H), SLOT_WEAR_MASK, TRUE)
|
||||
if(neck)
|
||||
H.equip_to_slot_or_del(new neck(H),SLOT_NECK, TRUE)
|
||||
H.equip_to_slot_or_del(SSwardrobe.provide_type(neck, H), SLOT_NECK, TRUE)
|
||||
if(ears)
|
||||
H.equip_to_slot_or_del(new ears(H),SLOT_EARS, TRUE)
|
||||
H.equip_to_slot_or_del(SSwardrobe.provide_type(ears, H), SLOT_EARS, TRUE)
|
||||
if(glasses)
|
||||
H.equip_to_slot_or_del(new glasses(H),SLOT_GLASSES, TRUE)
|
||||
H.equip_to_slot_or_del(SSwardrobe.provide_type(glasses, H), SLOT_GLASSES, TRUE)
|
||||
if(id)
|
||||
H.equip_to_slot_or_del(new id(H),SLOT_WEAR_ID, TRUE)
|
||||
H.equip_to_slot_or_del(SSwardrobe.provide_type(id, H), SLOT_WEAR_ID, TRUE)
|
||||
if(suit_store)
|
||||
H.equip_to_slot_or_del(new suit_store(H),SLOT_S_STORE, TRUE)
|
||||
H.equip_to_slot_or_del(SSwardrobe.provide_type(suit_store, H), SLOT_S_STORE, TRUE)
|
||||
|
||||
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),SLOT_L_STORE, TRUE)
|
||||
H.equip_to_slot_or_del(SSwardrobe.provide_type(l_pocket, H), SLOT_L_STORE, TRUE)
|
||||
if(r_pocket)
|
||||
H.equip_to_slot_or_del(new r_pocket(H),SLOT_R_STORE, TRUE)
|
||||
H.equip_to_slot_or_del(SSwardrobe.provide_type(r_pocket, H), SLOT_R_STORE, TRUE)
|
||||
|
||||
if(box)
|
||||
if(!backpack_contents)
|
||||
@@ -212,7 +215,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),SLOT_IN_BACKPACK, TRUE)
|
||||
H.equip_to_slot_or_del(SSwardrobe.provide_type(path, H),SLOT_IN_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
|
||||
@@ -227,7 +230,7 @@
|
||||
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)
|
||||
|
||||
H.update_body()
|
||||
@@ -288,6 +291,38 @@
|
||||
listclearnulls(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
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ GLOBAL_LIST_EMPTY(PDAs)
|
||||
|
||||
//Main variables
|
||||
var/owner = null // String name of owner
|
||||
var/default_cartridge = 0 // Access level defined by cartridge
|
||||
var/default_cartridge = 0 // Typepath of the default cartridge to use
|
||||
var/obj/item/cartridge/cartridge = null //current cartridge
|
||||
var/mode = 0 //Controls what menu the PDA will display. 0 is hub; the rest are either built in or based on cartridge.
|
||||
var/icon_alert = "pda-r" //Icon to be overlayed for message alerts. Taken from the pda icon file.
|
||||
@@ -89,7 +89,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/overlays_x_offset = 0 //x offset to use for certain overlays
|
||||
|
||||
var/underline_flag = TRUE //flag for underline
|
||||
@@ -121,13 +124,24 @@ 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)
|
||||
update_icon()
|
||||
|
||||
/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)
|
||||
@@ -151,6 +165,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
|
||||
|
||||
@@ -937,9 +959,8 @@ GLOBAL_LIST_EMPTY(PDAs)
|
||||
return
|
||||
|
||||
if(inserted_item)
|
||||
usr.put_in_hands(inserted_item)
|
||||
to_chat(usr, span_notice("You remove [inserted_item] from [src]."))
|
||||
inserted_item = null
|
||||
usr.put_in_hands(inserted_item) //Don't need to manage the pen ref, handled on Exited()
|
||||
update_icon()
|
||||
else
|
||||
to_chat(usr, span_warning("This PDA does not have a pen in it!"))
|
||||
@@ -1103,18 +1124,6 @@ GLOBAL_LIST_EMPTY(PDAs)
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
/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 ..()
|
||||
|
||||
//pAI verb and proc for sending PDA messages.
|
||||
/mob/living/silicon/proc/cmd_send_pdamesg(mob/user)
|
||||
var/list/plist = list()
|
||||
@@ -1240,6 +1249,23 @@ GLOBAL_LIST_EMPTY(PDAs)
|
||||
/obj/item/pda/proc/pda_no_detonate()
|
||||
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"
|
||||
desc = "A portable microcomputer by Thinktronic Systems, LTD. The surface is coated with polytetrafluoroethylene and banana drippings."
|
||||
ttone = "honk"
|
||||
@@ -81,7 +81,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
|
||||
icon_state = "pda-mime"
|
||||
silent = TRUE
|
||||
ttone = "silence"
|
||||
@@ -113,13 +113,13 @@
|
||||
/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
|
||||
icon_state = "pda-rd"
|
||||
|
||||
/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
|
||||
icon_state = "pda-captain"
|
||||
|
||||
/obj/item/pda/captain/Initialize()
|
||||
@@ -134,7 +134,7 @@
|
||||
/obj/item/pda/quartermaster
|
||||
name = "quartermaster PDA"
|
||||
default_cartridge = /obj/item/cartridge/quartermaster
|
||||
inserted_item = /obj/item/pen/fountain
|
||||
insert_type = /obj/item/pen/fountain
|
||||
icon_state = "pda-qm"
|
||||
|
||||
/obj/item/pda/shaftminer
|
||||
@@ -156,7 +156,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
|
||||
icon_state = "pda-lawyer"
|
||||
ttone = "objection"
|
||||
|
||||
@@ -175,7 +175,7 @@
|
||||
icon_state = "pda-library"
|
||||
icon_alert = "pda-r-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!
|
||||
@@ -198,7 +198,7 @@
|
||||
/obj/item/pda/bar
|
||||
name = "bartender PDA"
|
||||
icon_state = "pda-bartender"
|
||||
inserted_item = /obj/item/pen/fountain
|
||||
insert_type = /obj/item/pen/fountain
|
||||
|
||||
/obj/item/pda/atmos
|
||||
name = "atmospherics PDA"
|
||||
|
||||
@@ -49,12 +49,6 @@
|
||||
var/mob/living/simple_animal/bot/active_bot
|
||||
var/list/botlist = list()
|
||||
|
||||
/obj/item/cartridge/Initialize()
|
||||
. = ..()
|
||||
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"
|
||||
|
||||
@@ -363,9 +363,9 @@ GENE SCANNER
|
||||
mutant = TRUE
|
||||
else if (S.mutantlungs != initial(S.mutantlungs))
|
||||
mutant = TRUE
|
||||
else if (S.mutant_brain != initial(S.mutant_brain))
|
||||
else if (S.mutantbrain != initial(S.mutantbrain))
|
||||
mutant = TRUE
|
||||
else if (S.mutant_heart != initial(S.mutant_heart))
|
||||
else if (S.mutantheart != initial(S.mutantheart))
|
||||
mutant = TRUE
|
||||
else if (S.mutanteyes != initial(S.mutanteyes))
|
||||
mutant = TRUE
|
||||
|
||||
@@ -9,17 +9,27 @@
|
||||
name = "TRAC implant"
|
||||
desc = "A smaller tracking implant that supplies power for only a few minutes."
|
||||
var/lifespan = 5 MINUTES //how long does the implant last?
|
||||
///The id of the timer that's qdeleting us
|
||||
var/timerid
|
||||
allow_teleport = FALSE
|
||||
|
||||
/obj/item/implant/tracking/tra32/Initialize()
|
||||
/obj/item/implant/tracking/tra32/implant(mob/living/target, mob/user, silent, force)
|
||||
. = ..()
|
||||
QDEL_IN(src, lifespan)
|
||||
timerid = QDEL_IN(src, lifespan)
|
||||
|
||||
/obj/item/implant/tracking/tra32/removed(mob/living/source, silent, special)
|
||||
. = ..()
|
||||
deltimer(timerid)
|
||||
timerid = null
|
||||
|
||||
/obj/item/implant/tracking/tra32/Destroy()
|
||||
return ..()
|
||||
|
||||
/obj/item/implant/tracking/tra32/emp_act(severity) //Demands to call the severity and I do not care to oppose it
|
||||
. = ..()
|
||||
Destroy()
|
||||
|
||||
/obj/item/implant/tracking/New()
|
||||
/obj/item/implant/tracking/Initialize(mapload)
|
||||
..()
|
||||
GLOB.tracked_implants += src
|
||||
|
||||
|
||||
@@ -99,67 +99,131 @@
|
||||
icon_state = "utilitybelt_ce"
|
||||
item_state = "utility_ce"
|
||||
|
||||
/obj/item/storage/belt/utility/chief/full
|
||||
preload = TRUE
|
||||
|
||||
/obj/item/storage/belt/utility/chief/full/PopulateContents()
|
||||
new /obj/item/handdrill(src)
|
||||
new /obj/item/jawsoflife(src)
|
||||
new /obj/item/analyzer/ranged(src)
|
||||
new /obj/item/weldingtool/experimental(src)//This can be changed if this is too much
|
||||
new /obj/item/multitool/tricorder(src) //yogs: changes the multitool to the tricorder and removes the analyzer
|
||||
new /obj/item/stack/cable_coil(src,MAXCOIL,pick("red","yellow","orange"))
|
||||
new /obj/item/extinguisher/mini(src)
|
||||
new /obj/item/holosign_creator/multi/CE(src)
|
||||
SSwardrobe.provide_type(/obj/item/handdrill, src)
|
||||
SSwardrobe.provide_type(/obj/item/jawsoflife, src)
|
||||
SSwardrobe.provide_type(/obj/item/analyzer/ranged, src)
|
||||
SSwardrobe.provide_type(/obj/item/weldingtool/experimental, src) //This can be changed if this is too much //It's been 5 years
|
||||
SSwardrobe.provide_type(/obj/item/multitool/tricorder, src) //yogs: changes the multitool to the tricorder and removes the analyzer
|
||||
SSwardrobe.provide_type(/obj/item/stack/cable_coil, src)
|
||||
SSwardrobe.provide_type(/obj/item/extinguisher/mini, src)
|
||||
SSwardrobe.provide_type(/obj/item/holosign_creator/multi/CE, 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/handdrill
|
||||
to_preload += /obj/item/jawsoflife
|
||||
to_preload += /obj/item/analyzer/ranged
|
||||
to_preload += /obj/item/weldingtool/experimental
|
||||
to_preload += /obj/item/multitool/tricorder
|
||||
to_preload += /obj/item/stack/cable_coil
|
||||
to_preload += /obj/item/extinguisher/mini
|
||||
to_preload += /obj/item/holosign_creator/multi/CE
|
||||
return to_preload
|
||||
|
||||
/obj/item/storage/belt/utility/chief/admin/full
|
||||
preload = FALSE
|
||||
|
||||
/obj/item/storage/belt/utility/chief/admin/full/PopulateContents()
|
||||
new /obj/item/construction/rcd/combat/admin(src)
|
||||
new /obj/item/pipe_dispenser(src)
|
||||
new /obj/item/shuttle_creator/admin(src)
|
||||
new /obj/item/handdrill(src)
|
||||
new /obj/item/jawsoflife(src)
|
||||
new /obj/item/analyzer/ranged(src)
|
||||
new /obj/item/weldingtool/experimental(src)//This can be changed if this is too much
|
||||
new /obj/item/multitool/tricorder(src) //yogs: changes the multitool to the tricorder and removes the analyzer
|
||||
new /obj/item/storage/bag/construction/admin/full(src)
|
||||
new /obj/item/extinguisher/mini(src)
|
||||
new /obj/item/holosign_creator/multi/CE(src)
|
||||
SSwardrobe.provide_type(/obj/item/construction/rcd/combat/admin, src)
|
||||
SSwardrobe.provide_type(/obj/item/pipe_dispenser, src)
|
||||
SSwardrobe.provide_type(/obj/item/shuttle_creator/admin, src)
|
||||
SSwardrobe.provide_type(/obj/item/handdrill, src)
|
||||
SSwardrobe.provide_type(/obj/item/jawsoflife, src)
|
||||
SSwardrobe.provide_type(/obj/item/analyzer/ranged, src)
|
||||
SSwardrobe.provide_type(/obj/item/weldingtool/experimental, src) //This can be changed if this is too much
|
||||
SSwardrobe.provide_type(/obj/item/multitool/tricorder, src) //yogs: changes the multitool to the tricorder and removes the analyzer
|
||||
SSwardrobe.provide_type(/obj/item/storage/bag/construction/admin/full, src)
|
||||
SSwardrobe.provide_type(/obj/item/extinguisher/mini, src)
|
||||
SSwardrobe.provide_type(/obj/item/holosign_creator/multi/CE, src)
|
||||
|
||||
/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,MAXCOIL,pick("red","yellow","orange"))
|
||||
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/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,MAXCOIL,pick("red","yellow","orange"))
|
||||
new /obj/item/barrier_taperoll/engineering(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)
|
||||
SSwardrobe.provide_type(/obj/item/barrier_taperoll/engineering, 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
|
||||
to_preload += /obj/item/barrier_taperoll/engineering
|
||||
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)
|
||||
new /obj/item/barrier_taperoll/engineering()
|
||||
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)
|
||||
SSwardrobe.provide_type(/obj/item/barrier_taperoll/engineering, 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
|
||||
to_preload += /obj/item/barrier_taperoll/engineering
|
||||
return to_preload
|
||||
|
||||
/obj/item/storage/belt/utility/servant/PopulateContents()
|
||||
new /obj/item/screwdriver/brass(src)
|
||||
new /obj/item/wirecutters/brass(src)
|
||||
new /obj/item/wrench/brass(src)
|
||||
new /obj/item/crowbar/brass(src)
|
||||
new /obj/item/weldingtool/experimental/brass(src)
|
||||
new /obj/item/multitool(src)
|
||||
new /obj/item/stack/cable_coil(src, MAXCOIL, "yellow")
|
||||
SSwardrobe.provide_type(/obj/item/screwdriver/brass, src)
|
||||
SSwardrobe.provide_type(/obj/item/wirecutters/brass, src)
|
||||
SSwardrobe.provide_type(/obj/item/wrench/brass, src)
|
||||
SSwardrobe.provide_type(/obj/item/crowbar/brass, src)
|
||||
SSwardrobe.provide_type(/obj/item/weldingtool/experimental/brass, src)
|
||||
SSwardrobe.provide_type(/obj/item/multitool, src)
|
||||
SSwardrobe.provide_type(/obj/item/stack/cable_coil, src)
|
||||
|
||||
/obj/item/storage/belt/utility/servant/get_types_to_preload()
|
||||
var/list/to_preload = list() //Yes this is a pain. Yes this is the point
|
||||
to_preload += /obj/item/screwdriver/brass
|
||||
to_preload += /obj/item/wirecutters/brass
|
||||
to_preload += /obj/item/wrench/brass
|
||||
to_preload += /obj/item/crowbar/brass
|
||||
to_preload += /obj/item/weldingtool/experimental/brass
|
||||
to_preload += /obj/item/multitool
|
||||
to_preload += /obj/item/stack/cable_coil
|
||||
return to_preload
|
||||
|
||||
/obj/item/storage/belt/medical
|
||||
name = "medical belt"
|
||||
@@ -231,12 +295,12 @@
|
||||
item_state = "medical_cmo"
|
||||
|
||||
/obj/item/storage/belt/medical/chief/full/PopulateContents()
|
||||
new /obj/item/scalpel/advanced(src)
|
||||
new /obj/item/retractor/advanced(src)
|
||||
new /obj/item/cautery/advanced(src)
|
||||
new /obj/item/pinpointer/crew(src)
|
||||
new /obj/item/sensor_device(src)
|
||||
new /obj/item/healthanalyzer/advanced(src)
|
||||
SSwardrobe.provide_type(/obj/item/scalpel/advanced, src)
|
||||
SSwardrobe.provide_type(/obj/item/retractor/advanced, src)
|
||||
SSwardrobe.provide_type(/obj/item/cautery/advanced, src)
|
||||
SSwardrobe.provide_type(/obj/item/pinpointer/crew, src)
|
||||
SSwardrobe.provide_type(/obj/item/sensor_device, src)
|
||||
SSwardrobe.provide_type(/obj/item/healthanalyzer/advanced, src)
|
||||
|
||||
/obj/item/storage/belt/security
|
||||
name = "security belt"
|
||||
@@ -281,12 +345,12 @@
|
||||
))
|
||||
|
||||
/obj/item/storage/belt/security/full/PopulateContents()
|
||||
new /obj/item/reagent_containers/spray/pepper(src)
|
||||
new /obj/item/restraints/handcuffs(src)
|
||||
new /obj/item/grenade/flashbang(src)
|
||||
new /obj/item/assembly/flash/handheld(src)
|
||||
new /obj/item/melee/baton/loaded(src)
|
||||
new /obj/item/barrier_taperoll/police(src)
|
||||
SSwardrobe.provide_type(/obj/item/reagent_containers/spray/pepper, src)
|
||||
SSwardrobe.provide_type(/obj/item/restraints/handcuffs, src)
|
||||
SSwardrobe.provide_type(/obj/item/grenade/flashbang, src)
|
||||
SSwardrobe.provide_type(/obj/item/assembly/flash/handheld, src)
|
||||
SSwardrobe.provide_type(/obj/item/melee/baton/loaded, src)
|
||||
SSwardrobe.provide_type(/obj/item/barrier_taperoll/police, src)
|
||||
update_icon()
|
||||
|
||||
/obj/item/storage/belt/security/chief
|
||||
@@ -302,13 +366,13 @@
|
||||
STR.max_combined_w_class = 21
|
||||
|
||||
/obj/item/storage/belt/security/chief/full/PopulateContents()
|
||||
new /obj/item/reagent_containers/spray/pepper(src)
|
||||
new /obj/item/restraints/handcuffs(src)
|
||||
new /obj/item/grenade/flashbang(src)
|
||||
new /obj/item/assembly/flash/handheld(src)
|
||||
new /obj/item/melee/baton/loaded(src)
|
||||
new /obj/item/barrier_taperoll/police(src)
|
||||
new /obj/item/shield/riot/tele(src)
|
||||
SSwardrobe.provide_type(/obj/item/reagent_containers/spray/pepper, src)
|
||||
SSwardrobe.provide_type(/obj/item/restraints/handcuffs, src)
|
||||
SSwardrobe.provide_type(/obj/item/grenade/flashbang, src)
|
||||
SSwardrobe.provide_type(/obj/item/assembly/flash/handheld, src)
|
||||
SSwardrobe.provide_type(/obj/item/melee/baton/loaded, src)
|
||||
SSwardrobe.provide_type(/obj/item/barrier_taperoll/police, src)
|
||||
SSwardrobe.provide_type(/obj/item/shield/riot/tele, src)
|
||||
update_icon()
|
||||
|
||||
/obj/item/storage/belt/security/webbing
|
||||
|
||||
@@ -137,6 +137,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(/obj/item/clothing/mask/breath) in src
|
||||
var/obj/item/internals = locate(/obj/item/tank/internals/emergency_oxygen) in src
|
||||
new /obj/item/tank/internals/plasmaman/belt(src)
|
||||
qdel(mask) // Get rid of the items that shouldn't be
|
||||
qdel(internals)
|
||||
|
||||
/obj/item/storage/box/survival_mining/PopulateContents()
|
||||
new /obj/item/clothing/mask/gas/explorer(src)
|
||||
new /obj/item/tank/internals/emergency_oxygen(src)
|
||||
|
||||
@@ -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
|
||||
@@ -48,3 +52,9 @@
|
||||
/obj/item/storage/proc/emptyStorage()
|
||||
var/datum/component/storage/ST = GetComponent(/datum/component/storage)
|
||||
ST.do_quick_empty()
|
||||
|
||||
/// 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
|
||||
|
||||
@@ -26,8 +26,8 @@
|
||||
C.regenerate_limbs(1)
|
||||
if(!user.getorganslot(ORGAN_SLOT_BRAIN))
|
||||
var/obj/item/organ/brain/B
|
||||
if(C.has_dna() && C.dna.species.mutant_brain)
|
||||
B = new C.dna.species.mutant_brain()
|
||||
if(C.has_dna() && C.dna.species.mutantbrain)
|
||||
B = new C.dna.species.mutantbrain()
|
||||
else
|
||||
B = new()
|
||||
B.organ_flags &= ~ORGAN_VITAL
|
||||
|
||||
@@ -2301,6 +2301,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
character.dna.species.mutant_bodyparts |= "tail_polysmorph"
|
||||
|
||||
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()
|
||||
|
||||
@@ -561,7 +561,7 @@
|
||||
/obj/item/clothing/head/helmet/space/hardsuit/rd/proc/sense_explosion(datum/source, turf/epicenter, devastation_range, heavy_impact_range,
|
||||
light_impact_range, took, orig_dev_range, orig_heavy_range, orig_light_range)
|
||||
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
|
||||
|
||||
@@ -207,6 +207,8 @@
|
||||
box = /obj/item/storage/box/survival
|
||||
ipc_box = /obj/item/storage/box/ipc
|
||||
|
||||
preload = TRUE // These are used by the prefs ui, and also just kinda could use the extra help at roundstart
|
||||
|
||||
var/obj/item/id_type = /obj/item/card/id
|
||||
var/obj/item/modular_computer/pda_type = /obj/item/modular_computer/tablet/pda/preset/basic
|
||||
var/backpack = /obj/item/storage/backpack
|
||||
@@ -307,6 +309,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
|
||||
|
||||
@@ -69,6 +69,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)
|
||||
|
||||
@@ -80,3 +80,7 @@
|
||||
var/datum/martial_art/cqc/under_siege/justacook = new
|
||||
justacook.teach(H)
|
||||
|
||||
/datum/outfit/job/cook/get_types_to_preload()
|
||||
. = ..()
|
||||
. += /obj/item/clothing/suit/apron/chef
|
||||
. += /obj/item/clothing/head/soft/mime
|
||||
|
||||
@@ -60,3 +60,8 @@
|
||||
if(J.lawyers>1)
|
||||
uniform = /obj/item/clothing/under/lawyer/purpsuit
|
||||
suit = /obj/item/clothing/suit/toggle/lawyer/purple
|
||||
|
||||
/datum/outfit/job/lawyer/get_types_to_preload()
|
||||
. = ..()
|
||||
. += /obj/item/clothing/under/lawyer/purpsuit
|
||||
. += /obj/item/clothing/suit/toggle/lawyer/purple
|
||||
|
||||
@@ -61,3 +61,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
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
name = "regenerative core"
|
||||
desc = "All that remains of a hivelord. It can be used to heal quickly, but it will rapidly decay into uselessness. Radiation found in active space installments will slow its healing effects."
|
||||
icon_state = "roro core 2"
|
||||
visual = FALSE
|
||||
item_flags = NOBLUDGEON
|
||||
slot = "hivecore"
|
||||
force = 0
|
||||
|
||||
@@ -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
|
||||
var/list/alien_powers = list()
|
||||
|
||||
/obj/item/organ/alien/Initialize()
|
||||
|
||||
@@ -83,6 +83,9 @@
|
||||
var/list/all_scars
|
||||
var/visible_tumors = FALSE //if you are seem with some tumors, for examine
|
||||
|
||||
/// Only load in visual organs
|
||||
var/visual_only_organs = FALSE
|
||||
|
||||
COOLDOWN_DECLARE(bleeding_message_cd)
|
||||
|
||||
var/list/image/infra_images
|
||||
|
||||
@@ -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)
|
||||
@@ -14,6 +15,55 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy)
|
||||
/mob/living/carbon/human/dummy/Life()
|
||||
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)
|
||||
|
||||
//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/proc/wipe_state()
|
||||
delete_equipment()
|
||||
cut_overlays(TRUE)
|
||||
|
||||
@@ -148,30 +148,35 @@ GLOBAL_LIST_EMPTY(mentor_races)
|
||||
/// list of mobs that will ignore this species
|
||||
var/list/mob/living/ignored_by = list()
|
||||
//Breathing!
|
||||
///the mutant lungs, if different to the normal
|
||||
var/obj/item/organ/lungs/mutantlungs = null
|
||||
///what type of gas is breathed
|
||||
var/breathid = "o2"
|
||||
|
||||
///the brain, if any
|
||||
var/obj/item/organ/brain/mutant_brain = /obj/item/organ/brain
|
||||
///the heart
|
||||
var/obj/item/organ/heart/mutant_heart = /obj/item/organ/heart
|
||||
///the eyes
|
||||
var/obj/item/organ/eyes/mutanteyes = /obj/item/organ/eyes
|
||||
///the ears
|
||||
var/obj/item/organ/ears/mutantears = /obj/item/organ/ears
|
||||
///the special hands, if any, for example the zombie has those claw hands
|
||||
var/obj/item/mutanthands
|
||||
///the tongue, used for ssssssssssssss and other such speech impediments
|
||||
var/obj/item/organ/tongue/mutanttongue = /obj/item/organ/tongue
|
||||
///the tail, if any
|
||||
var/obj/item/organ/tail/mutanttail = null
|
||||
//Do NOT remove by setting to null. use OR make a RESPECTIVE TRAIT (removing stomach? add the NOSTOMACH trait to your species)
|
||||
//why does it work this way? because traits also disable the downsides of not having an organ, removing organs but not having the trait will make your species die
|
||||
|
||||
///Replaces default brain with a different organ
|
||||
var/obj/item/organ/brain/mutantbrain = /obj/item/organ/brain
|
||||
///Replaces default heart with a different organ
|
||||
var/obj/item/organ/heart/mutantheart = /obj/item/organ/heart
|
||||
///Replaces default lungs with a different organ
|
||||
var/obj/item/organ/lungs/mutantlungs = /obj/item/organ/lungs
|
||||
///Replaces default eyes with a different organ
|
||||
var/obj/item/organ/eyes/mutanteyes = /obj/item/organ/eyes
|
||||
///Replaces default ears with a different organ
|
||||
var/obj/item/organ/ears/mutantears = /obj/item/organ/ears
|
||||
///Replaces default tongue with a different organ
|
||||
var/obj/item/organ/tongue/mutanttongue = /obj/item/organ/tongue
|
||||
///Replaces default liver with a different organ
|
||||
var/obj/item/organ/liver/mutantliver = /obj/item/organ/liver
|
||||
///Replaces default stomach with a different organ
|
||||
var/obj/item/organ/stomach/mutantstomach = /obj/item/organ/stomach
|
||||
///Replaces default appendix with a different organ.
|
||||
var/obj/item/organ/appendix/mutantappendix = /obj/item/organ/appendix
|
||||
///Forces a species tail
|
||||
var/obj/item/organ/tail/mutanttail = /obj/item/organ/tail
|
||||
///Forces an item into this species' hands. Only an honorary mutantthing because this is not an organ and not loaded in the same way, you've been warned to do your research.
|
||||
var/obj/item/mutanthands
|
||||
|
||||
///The special liver, if any
|
||||
var/obj/item/organ/liver/mutantliver
|
||||
///the special stomach, if any
|
||||
var/obj/item/organ/stomach/mutantstomach
|
||||
var/override_float = FALSE
|
||||
|
||||
///Bitflag that controls what in game ways can select this species as a spawnable source. Think magic mirror and pride mirror, slime extract, ERT etc, see defines in __DEFINES/mobs.dm, defaults to NONE, so people actually have to think about it
|
||||
@@ -182,6 +187,9 @@ GLOBAL_LIST_EMPTY(mentor_races)
|
||||
|
||||
var/smells_like = "something alien"
|
||||
|
||||
//Should we preload this species's organs?
|
||||
var/preload = TRUE
|
||||
|
||||
///////////
|
||||
// PROCS //
|
||||
///////////
|
||||
@@ -257,135 +265,90 @@ GLOBAL_LIST_EMPTY(mentor_races)
|
||||
/datum/species/proc/has_toes()
|
||||
return FALSE
|
||||
|
||||
//Will regenerate missing organs
|
||||
/datum/species/proc/regenerate_organs(mob/living/carbon/C,datum/species/old_species,replace_current=TRUE)
|
||||
var/obj/item/organ/brain/brain = C.getorganslot(ORGAN_SLOT_BRAIN)
|
||||
var/obj/item/organ/heart/heart = C.getorganslot(ORGAN_SLOT_HEART)
|
||||
var/obj/item/organ/lungs/lungs = C.getorganslot(ORGAN_SLOT_LUNGS)
|
||||
var/obj/item/organ/appendix/appendix = C.getorganslot(ORGAN_SLOT_APPENDIX)
|
||||
var/obj/item/organ/eyes/eyes = C.getorganslot(ORGAN_SLOT_EYES)
|
||||
var/obj/item/organ/ears/ears = C.getorganslot(ORGAN_SLOT_EARS)
|
||||
var/obj/item/organ/tongue/tongue = C.getorganslot(ORGAN_SLOT_TONGUE)
|
||||
var/obj/item/organ/liver/liver = C.getorganslot(ORGAN_SLOT_LIVER)
|
||||
var/obj/item/organ/stomach/stomach = C.getorganslot(ORGAN_SLOT_STOMACH)
|
||||
var/obj/item/organ/tail/tail = C.getorganslot(ORGAN_SLOT_TAIL)
|
||||
/**
|
||||
* Corrects organs in a carbon, removing ones it doesn't need and adding ones it does.
|
||||
*
|
||||
* Takes all organ slots, removes organs a species should not have, adds organs a species should have.
|
||||
* can use replace_current to refresh all organs, creating an entirely new set.
|
||||
*
|
||||
* Arguments:
|
||||
* * C - carbon, the owner of the species datum AKA whoever we're regenerating organs in
|
||||
* * 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
|
||||
* * 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, 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, ORGAN_SLOT_TAIL = mutanttail)
|
||||
|
||||
var/should_have_brain = TRUE
|
||||
var/should_have_heart = !(NOBLOOD in species_traits)
|
||||
var/should_have_lungs = !(TRAIT_NOBREATH in inherent_traits)
|
||||
var/should_have_appendix = !(TRAIT_NOHUNGER in inherent_traits)
|
||||
var/should_have_eyes = TRUE
|
||||
var/should_have_ears = TRUE
|
||||
var/should_have_tongue = TRUE
|
||||
var/should_have_liver = !(NOLIVER in species_traits)
|
||||
var/should_have_stomach = !(NOSTOMACH in species_traits)
|
||||
var/should_have_tail = mutanttail
|
||||
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, ORGAN_SLOT_TAIL))
|
||||
|
||||
if(heart && (!should_have_heart || replace_current))
|
||||
heart.Remove(C,1)
|
||||
QDEL_NULL(heart)
|
||||
if(should_have_heart && !heart)
|
||||
heart = new mutant_heart()
|
||||
heart.Insert(C)
|
||||
var/obj/item/organ/oldorgan = C.getorganslot(slot) //used in removing
|
||||
var/obj/item/organ/neworgan = slot_mutantorgans[slot] //used in adding
|
||||
|
||||
if(lungs && (!should_have_lungs || replace_current))
|
||||
lungs.Remove(C,1)
|
||||
QDEL_NULL(lungs)
|
||||
if(should_have_lungs && !lungs)
|
||||
if(mutantlungs)
|
||||
lungs = new mutantlungs()
|
||||
else
|
||||
lungs = new()
|
||||
lungs.Insert(C)
|
||||
if(visual_only && !initial(neworgan.visual))
|
||||
continue
|
||||
|
||||
if(liver && (!should_have_liver || replace_current))
|
||||
liver.Remove(C,1)
|
||||
QDEL_NULL(liver)
|
||||
if(should_have_liver && !liver)
|
||||
if(mutantliver)
|
||||
liver = new mutantliver()
|
||||
else
|
||||
liver = new()
|
||||
liver.Insert(C)
|
||||
var/used_neworgan = FALSE
|
||||
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(stomach && (!should_have_stomach || replace_current))
|
||||
stomach.Remove(C,1)
|
||||
QDEL_NULL(stomach)
|
||||
if(should_have_stomach && !stomach)
|
||||
if(mutantstomach)
|
||||
stomach = new mutantstomach()
|
||||
else
|
||||
stomach = new()
|
||||
stomach.Insert(C)
|
||||
if(oldorgan && (!should_have || replace_current))
|
||||
if(slot == ORGAN_SLOT_BRAIN)
|
||||
var/obj/item/organ/brain/brain = oldorgan
|
||||
if(!brain.decoy_override)//"Just keep it if it's fake" - confucius, probably
|
||||
brain.Remove(C,TRUE, TRUE) //brain argument used so it doesn't cause any... sudden death.
|
||||
QDEL_NULL(brain)
|
||||
oldorgan = null //now deleted
|
||||
else
|
||||
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(appendix && (!should_have_appendix || replace_current))
|
||||
appendix.Remove(C,1)
|
||||
QDEL_NULL(appendix)
|
||||
if(should_have_appendix && !appendix)
|
||||
appendix = new()
|
||||
appendix.Insert(C)
|
||||
if(oldorgan)
|
||||
oldorgan.setOrganDamage(0)
|
||||
else if(should_have)
|
||||
if(slot == ORGAN_SLOT_TAIL)
|
||||
// Special snowflake code to handle tail appearances
|
||||
var/obj/item/organ/tail/new_tail = neworgan
|
||||
if(iscatperson(C))
|
||||
new_tail.tail_type = C.dna.features["tail_human"]
|
||||
if(ispolysmorph(C))
|
||||
new_tail.tail_type = C.dna.features["tail_polysmorph"]
|
||||
if(islizard(C))
|
||||
var/obj/item/organ/tail/lizard/new_lizard_tail = neworgan
|
||||
new_lizard_tail.tail_type = C.dna.features["tail_lizard"]
|
||||
new_lizard_tail.spines = C.dna.features["spines"]
|
||||
|
||||
if(tail && (!should_have_tail || replace_current))
|
||||
tail.Remove(C,1)
|
||||
QDEL_NULL(tail)
|
||||
if(should_have_tail && !tail)
|
||||
tail = new mutanttail()
|
||||
if(iscatperson(C))
|
||||
tail.tail_type = C.dna.features["tail_human"]
|
||||
if(ispolysmorph(C))
|
||||
tail.tail_type = C.dna.features["tail_polysmorph"]
|
||||
if(islizard(C))
|
||||
var/obj/item/organ/tail/lizard/T = tail
|
||||
T.tail_type = C.dna.features["tail_lizard"]
|
||||
T.spines = C.dna.features["spines"]
|
||||
tail.Insert(C)
|
||||
used_neworgan = TRUE
|
||||
neworgan.Insert(C, TRUE, FALSE)
|
||||
|
||||
if(C.get_bodypart(BODY_ZONE_HEAD))
|
||||
if(brain && (replace_current || !should_have_brain))
|
||||
if(!brain.decoy_override)//Just keep it if it's fake
|
||||
brain.Remove(C,TRUE,TRUE)
|
||||
QDEL_NULL(brain)
|
||||
if(should_have_brain && !brain)
|
||||
brain = new mutant_brain()
|
||||
brain.Insert(C, TRUE, TRUE)
|
||||
|
||||
if(eyes && (replace_current || !should_have_eyes))
|
||||
eyes.Remove(C,1)
|
||||
QDEL_NULL(eyes)
|
||||
if(should_have_eyes && !eyes)
|
||||
eyes = new mutanteyes
|
||||
eyes.Insert(C)
|
||||
|
||||
if(ears && (replace_current || !should_have_ears))
|
||||
ears.Remove(C,1)
|
||||
QDEL_NULL(ears)
|
||||
if(should_have_ears && !ears)
|
||||
ears = new mutantears
|
||||
ears.Insert(C)
|
||||
|
||||
if(tongue && (replace_current || !should_have_tongue))
|
||||
tongue.Remove(C,1)
|
||||
QDEL_NULL(tongue)
|
||||
if(should_have_tongue && !tongue)
|
||||
tongue = new mutanttongue
|
||||
tongue.Insert(C)
|
||||
if(!used_neworgan)
|
||||
qdel(neworgan)
|
||||
|
||||
if(old_species)
|
||||
for(var/mutantorgan in old_species.mutant_organs)
|
||||
var/obj/item/organ/I = C.getorgan(mutantorgan)
|
||||
if(I)
|
||||
I.Remove(C)
|
||||
QDEL_NULL(I)
|
||||
else
|
||||
for(var/mutantorgan in mutant_organs)
|
||||
// Snowflake check. If our species share this mutant organ, let's not remove it
|
||||
// just yet as we'll be properly replacing it later.
|
||||
if(mutantorgan in mutant_organs)
|
||||
continue
|
||||
var/obj/item/organ/I = C.getorgan(mutantorgan)
|
||||
if(I)
|
||||
I.Remove(C)
|
||||
QDEL_NULL(I)
|
||||
|
||||
for(var/path in mutant_organs)
|
||||
var/obj/item/organ/I = new path()
|
||||
I.Insert(C)
|
||||
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 = 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.
|
||||
//if(current_organ)
|
||||
// current_organ.before_organ_replacement(replacement)
|
||||
// organ.Insert will qdel any current organs in that slot, so we don't need to.
|
||||
replacement.Insert(C, TRUE, FALSE)
|
||||
|
||||
/datum/species/proc/on_species_gain(mob/living/carbon/C, datum/species/old_species, pref_load)
|
||||
// Drop the items the new species can't wear
|
||||
@@ -2157,7 +2120,8 @@ GLOBAL_LIST_EMPTY(mentor_races)
|
||||
/datum/species/proc/can_wag_tail(mob/living/carbon/human/H)
|
||||
if(H.IsParalyzed() || H.IsStun())
|
||||
return FALSE
|
||||
return (H.getorganslot(ORGAN_SLOT_TAIL))
|
||||
var/obj/item/organ/tail = H.getorganslot(ORGAN_SLOT_TAIL)
|
||||
return tail.get_availability(H.dna.species)
|
||||
|
||||
/datum/species/proc/is_wagging_tail(mob/living/carbon/human/H)
|
||||
return ("waggingtail_human" in mutant_bodyparts) || ("waggingtail_lizard" in mutant_bodyparts)
|
||||
@@ -2344,3 +2308,19 @@ GLOBAL_LIST_EMPTY(mentor_races)
|
||||
/datum/species/proc/force_drink_text(obj/O, mob/living/carbon/C, mob/user)
|
||||
. = TRUE
|
||||
C.visible_message(span_danger("[user] attempts to feed the contents of [O] to [C]."), span_userdanger("[user] attempts to feed the contents of [O] to [C]."))
|
||||
|
||||
/datum/species/proc/get_types_to_preload()
|
||||
var/list/to_store = list()
|
||||
to_store += mutant_organs
|
||||
//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
|
||||
to_store += mutanttail
|
||||
//We don't cache mutant hands because it's not constrained enough, too high a potential for failure
|
||||
return to_store
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
species_traits = list(NOTRANSSTING,NOEYESPRITES,NO_DNA_COPY,TRAIT_EASYDISMEMBER,ROBOTIC_LIMBS,NOZOMBIE,MUTCOLORS,NOHUSK,AGENDER,NOBLOOD)
|
||||
inherent_traits = list(TRAIT_RESISTCOLD,TRAIT_RADIMMUNE,TRAIT_COLDBLOODED,TRAIT_LIMBATTACHMENT,TRAIT_NOCRITDAMAGE,TRAIT_GENELESS,TRAIT_MEDICALIGNORE,TRAIT_NOCLONE,TRAIT_TOXIMMUNE,TRAIT_EASILY_WOUNDED,TRAIT_NODEFIB)
|
||||
inherent_biotypes = list(MOB_ROBOTIC, MOB_HUMANOID)
|
||||
mutant_brain = /obj/item/organ/brain/positron
|
||||
mutant_heart = /obj/item/organ/heart/cybernetic/ipc
|
||||
mutantbrain = /obj/item/organ/brain/positron
|
||||
mutantheart = /obj/item/organ/heart/cybernetic/ipc
|
||||
mutanteyes = /obj/item/organ/eyes/robotic
|
||||
mutanttongue = /obj/item/organ/tongue/robot
|
||||
mutantliver = /obj/item/organ/liver/cybernetic/upgraded/ipc
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
inherent_traits = list(TRAIT_NOHUNGER,TRAIT_NOBREATH)
|
||||
default_features = list("mcolor" = "FFF", "tail_human" = "None", "ears" = "None", "wings" = "None")
|
||||
use_skintones = TRUE
|
||||
mutant_brain = /obj/item/organ/brain/dullahan
|
||||
mutantbrain = /obj/item/organ/brain/dullahan
|
||||
mutanteyes = /obj/item/organ/eyes/dullahan
|
||||
mutanttongue = /obj/item/organ/tongue/dullahan
|
||||
mutantears = /obj/item/organ/ears/dullahan
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
attack_sound = 'sound/weapons/slash.ogg'
|
||||
miss_sound = 'sound/weapons/slashmiss.ogg'
|
||||
|
||||
species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS,HAS_FLESH,HAS_BONE,HAS_TAIL)
|
||||
mutant_bodyparts = list("ears", "tail_human")
|
||||
default_features = list("mcolor" = "FFF", "tail_human" = "Cat", "ears" = "Cat", "wings" = "None")
|
||||
rare_say_mod = list("meows"= 10)
|
||||
@@ -46,7 +47,7 @@
|
||||
var/obj/item/organ/tail/cat/tail = new
|
||||
tail.Insert(H, drop_if_replaced = FALSE)
|
||||
else
|
||||
mutanttail = null
|
||||
mutanttail = initial(old_species.mutanttail)
|
||||
|
||||
/datum/species/human/felinid/on_species_loss(mob/living/carbon/H, datum/species/new_species, pref_load)
|
||||
var/obj/item/organ/ears/cat/ears = H.getorgan(/obj/item/organ/ears/cat)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
id = "lizard"
|
||||
say_mod = "hisses"
|
||||
default_color = "00FF00"
|
||||
species_traits = list(MUTCOLORS,EYECOLOR,LIPS,HAS_FLESH,HAS_BONE)
|
||||
species_traits = list(MUTCOLORS,EYECOLOR,LIPS,HAS_FLESH,HAS_BONE,HAS_TAIL)
|
||||
inherent_biotypes = list(MOB_ORGANIC, MOB_HUMANOID, MOB_REPTILE)
|
||||
mutant_bodyparts = list("tail_lizard", "snout", "spines", "horns", "frills", "body_markings", "legs")
|
||||
mutanttongue = /obj/item/organ/tongue/lizard
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
smells_like = "dusty dryness"
|
||||
|
||||
/datum/species/moth/regenerate_organs(mob/living/carbon/C,datum/species/old_species,replace_current=TRUE)
|
||||
/datum/species/moth/regenerate_organs(mob/living/carbon/C, datum/species/old_species, replace_current = TRUE, visual_only = FALSE)
|
||||
. = ..()
|
||||
if(ishuman(C))
|
||||
var/mob/living/carbon/human/H = C
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
damage_overlay_type = "polysmorph"
|
||||
deathsound = 'sound/voice/hiss6.ogg'
|
||||
screamsound = 'sound/voice/hiss5.ogg'
|
||||
species_traits = list(NOEYESPRITES, FGENDER, MUTCOLORS, NOCOLORCHANGE, DIGITIGRADE, HAS_FLESH, HAS_BONE)
|
||||
species_traits = list(NOEYESPRITES, FGENDER, MUTCOLORS, NOCOLORCHANGE, DIGITIGRADE, HAS_FLESH, HAS_BONE, HAS_TAIL)
|
||||
inherent_traits = list(TRAIT_ACIDBLOOD, TRAIT_SKINNY)
|
||||
inherent_biotypes = list(MOB_ORGANIC, MOB_HUMANOID)
|
||||
mutanteyes = /obj/item/organ/eyes/polysmorph
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
inherent_traits = list(TRAIT_RESISTCOLD,TRAIT_NOBREATH,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_NOGUNS,TRAIT_RADIMMUNE,TRAIT_VIRUSIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NODISMEMBER,TRAIT_NOHUNGER)
|
||||
mutanteyes = /obj/item/organ/eyes/night_vision/nightmare
|
||||
mutant_organs = list(/obj/item/organ/heart/nightmare)
|
||||
mutant_brain = /obj/item/organ/brain/nightmare
|
||||
mutantbrain = /obj/item/organ/brain/nightmare
|
||||
|
||||
var/info_text = "You are a <span class='danger'>Nightmare</span>. The ability <span class='warning'>shadow walk</span> allows unlimited, unrestricted movement in the dark while activated. \
|
||||
Your <span class='warning'>light eater</span> will destroy any light producing objects you attack, as well as destroy any lights a living creature may be holding. You will automatically dodge gunfire and melee attacks when on a dark tile. If killed, you will eventually revive if left in darkness."
|
||||
@@ -93,6 +93,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"
|
||||
var/respawn_progress = 0
|
||||
var/obj/item/light_eater/blade
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_MAGIC | ERT_SPAWN
|
||||
exotic_bloodtype = "U"
|
||||
use_skintones = TRUE
|
||||
mutant_heart = /obj/item/organ/heart/vampire
|
||||
mutantheart = /obj/item/organ/heart/vampire
|
||||
mutanttongue = /obj/item/organ/tongue/vampire
|
||||
limbs_id = "human"
|
||||
skinned_type = /obj/item/stack/sheet/animalhide/human
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/obj/item/organ/appendix
|
||||
name = "appendix"
|
||||
icon_state = "appendix"
|
||||
visual = FALSE
|
||||
zone = BODY_ZONE_PRECISE_GROIN
|
||||
slot = ORGAN_SLOT_APPENDIX
|
||||
healing_factor = STANDARD_ORGAN_HEALING
|
||||
@@ -43,6 +44,9 @@
|
||||
S.reagents.add_reagent(/datum/reagent/toxin/bad_food, 5)
|
||||
return S
|
||||
|
||||
/obj/item/organ/appendix/get_availability(datum/species/species)
|
||||
return !(TRAIT_NOHUNGER in species.inherent_traits)
|
||||
|
||||
/obj/item/organ/appendix/cybernetic
|
||||
name = "cybernetic appendix"
|
||||
desc = "One of the most advanced cybernetic organs ever created."
|
||||
|
||||
@@ -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
|
||||
decay_factor = STANDARD_ORGAN_DECAY
|
||||
@@ -92,6 +93,7 @@
|
||||
name = "cat ears"
|
||||
icon = 'icons/obj/clothing/hats.dmi'
|
||||
icon_state = "kitty"
|
||||
visual = TRUE
|
||||
damage_multiplier = 2
|
||||
|
||||
/obj/item/organ/ears/cat/Insert(mob/living/carbon/human/H, special = 0, drop_if_replaced = TRUE)
|
||||
|
||||
@@ -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
|
||||
@@ -37,7 +38,6 @@
|
||||
old_eye_color = HMN.eye_color
|
||||
if(eye_color)
|
||||
HMN.eye_color = eye_color
|
||||
HMN.regenerate_icons()
|
||||
else
|
||||
eye_color = HMN.eye_color
|
||||
if(HAS_TRAIT(HMN, TRAIT_NIGHT_VISION) && !lighting_alpha)
|
||||
@@ -52,10 +52,15 @@
|
||||
if(ishuman(M) && eye_color)
|
||||
var/mob/living/carbon/human/HMN = M
|
||||
HMN.eye_color = old_eye_color
|
||||
HMN.regenerate_icons()
|
||||
HMN.update_body()
|
||||
M.update_tint()
|
||||
M.update_sight()
|
||||
|
||||
//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()
|
||||
..()
|
||||
var/mob/living/carbon/C = owner
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
name = "heart"
|
||||
desc = "I feel bad for the heartless bastard who lost this."
|
||||
icon_state = "heart"
|
||||
visual = FALSE
|
||||
zone = BODY_ZONE_CHEST
|
||||
slot = ORGAN_SLOT_HEART
|
||||
healing_factor = STANDARD_ORGAN_HEALING
|
||||
@@ -107,6 +108,9 @@
|
||||
owner.set_heartattack(TRUE)
|
||||
failed = TRUE
|
||||
|
||||
/obj/item/organ/heart/get_availability(datum/species/species)
|
||||
return !(NOBLOOD in species.species_traits)
|
||||
|
||||
/obj/item/organ/heart/cursed
|
||||
name = "cursed heart"
|
||||
desc = "A heart that, when inserted, will force you to pump it manually."
|
||||
|
||||
@@ -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
|
||||
@@ -65,6 +66,9 @@
|
||||
S.reagents.add_reagent(/datum/reagent/iron, 5)
|
||||
return S
|
||||
|
||||
/obj/item/organ/liver/get_availability(datum/species/species)
|
||||
return !(NOLIVER in species.species_traits)
|
||||
|
||||
/obj/item/organ/liver/fly
|
||||
name = "insectoid liver"
|
||||
icon_state = "liver-x" //xenomorph liver? It's just a black liver so it fits.
|
||||
|
||||
@@ -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
|
||||
@@ -481,6 +482,9 @@
|
||||
S.reagents.add_reagent(/datum/reagent/medicine/salbutamol, 5)
|
||||
return S
|
||||
|
||||
/obj/item/organ/lungs/get_availability(datum/species/species)
|
||||
return !(TRAIT_NOBREATH in species.inherent_traits)
|
||||
|
||||
/obj/item/organ/lungs/ipc
|
||||
name = "cooling radiator"
|
||||
desc = "A radiator in the shape of a lung used to exchange heat to cool down"
|
||||
|
||||
@@ -30,6 +30,9 @@
|
||||
var/high_threshold_cleared
|
||||
var/low_threshold_cleared
|
||||
|
||||
///Do we effect the appearance of our mob. Used to save time in preference code
|
||||
var/visual = TRUE
|
||||
|
||||
/obj/item/organ/proc/Insert(mob/living/carbon/M, special = 0, drop_if_replaced = TRUE,special_zone = null)
|
||||
if(!iscarbon(M) || owner == M)
|
||||
return
|
||||
@@ -175,6 +178,14 @@
|
||||
START_PROCESSING(SSobj, src)
|
||||
return ..()
|
||||
|
||||
///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()
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
if(owner)
|
||||
@@ -216,6 +227,20 @@
|
||||
else
|
||||
organ_flags &= ~ORGAN_FAILING
|
||||
|
||||
|
||||
/** get_availability
|
||||
* returns whether the species should innately have this organ.
|
||||
*
|
||||
* regenerate organs works with generic organs, so we need to get whether it can accept certain organs just by what this returns.
|
||||
* This is set to return true or false, depending on if a species has a trait that would nulify the purpose of the organ.
|
||||
* For example, lungs won't be given if you have NO_BREATH, stomachs check for NO_HUNGER, and livers check for NO_METABOLISM.
|
||||
* If you want a carbon to have a trait that normally blocks an organ but still want the organ. Attach the trait to the organ using the organ_traits var
|
||||
* Arguments:
|
||||
* owner_species - species, needed to return whether the species has an organ specific trait
|
||||
*/
|
||||
/obj/item/organ/proc/get_availability(datum/species/owner_species)
|
||||
return TRUE
|
||||
|
||||
//Looking for brains?
|
||||
//Try code/modules/mob/living/carbon/brain/brain_item.dm
|
||||
|
||||
|
||||
@@ -3,6 +3,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
|
||||
@@ -86,6 +87,9 @@
|
||||
SEND_SIGNAL(H, COMSIG_CLEAR_MOOD_EVENT, "disgust")
|
||||
..()
|
||||
|
||||
/obj/item/organ/stomach/get_availability(datum/species/species)
|
||||
return !(NOSTOMACH in species.species_traits)
|
||||
|
||||
/obj/item/organ/stomach/cybernetic
|
||||
name = "cybernetic stomach"
|
||||
desc = "A cybernetic metabolic furnace that can be connected to a digestive system in place of a stomach."
|
||||
|
||||
@@ -4,15 +4,19 @@
|
||||
name = "tail"
|
||||
desc = "A severed tail. What did you cut this off of?"
|
||||
icon_state = "severedtail"
|
||||
visual = TRUE
|
||||
zone = BODY_ZONE_PRECISE_GROIN
|
||||
slot = ORGAN_SLOT_TAIL
|
||||
var/tail_type = "None"
|
||||
|
||||
/obj/item/organ/tail/Remove(mob/living/carbon/human/H, special = 0)
|
||||
/obj/item/organ/tail/Remove(mob/living/carbon/human/H, special = 0)
|
||||
..()
|
||||
if(H && H.dna && H.dna.species)
|
||||
H.dna.species.stop_wagging_tail(H)
|
||||
|
||||
/obj/item/organ/tail/get_availability(datum/species/species)
|
||||
return (HAS_TAIL in species.species_traits)
|
||||
|
||||
/obj/item/organ/tail/cat
|
||||
name = "cat tail"
|
||||
desc = "A severed cat tail. Who's wagging now?"
|
||||
@@ -26,7 +30,7 @@
|
||||
H.dna.features["tail_human"] = tail_type
|
||||
H.update_body()
|
||||
|
||||
/obj/item/organ/tail/cat/Remove(mob/living/carbon/human/H, special = 0)
|
||||
/obj/item/organ/tail/cat/Remove(mob/living/carbon/human/H, special = 0)
|
||||
..()
|
||||
if(istype(H))
|
||||
H.dna.species.mutant_bodyparts -= "tail_human"
|
||||
|
||||
@@ -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 = list("licked", "slobbered", "slapped", "frenched", "tongued")
|
||||
|
||||
@@ -6,6 +6,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
|
||||
@@ -31,6 +32,7 @@
|
||||
zone = BODY_ZONE_HEAD
|
||||
slot = ORGAN_SLOT_ADAMANTINE_RESONATOR
|
||||
icon_state = "adamantine_resonator"
|
||||
visual = FALSE
|
||||
|
||||
/obj/item/organ/vocal_cords/adamantine
|
||||
name = "adamantine vocal cords"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -331,6 +331,7 @@
|
||||
#include "code\controllers\subsystem\traumas.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\processing\fastprocess.dm"
|
||||
#include "code\controllers\subsystem\processing\fields.dm"
|
||||
|
||||
Reference in New Issue
Block a user