Merge branch 'master' into 91-files-changed-fml

This commit is contained in:
Detective-Google
2020-05-02 17:16:18 -05:00
committed by GitHub
80 changed files with 1987 additions and 1900 deletions

View File

@@ -40,6 +40,7 @@
#define CALTROP_BYPASS_SHOES 1
#define CALTROP_IGNORE_WALKERS 2
// Spellcasting
#define SPELL_SKIP_ALL_REQS (1<<0)
#define SPELL_SKIP_CENTCOM (1<<1)
#define SPELL_SKIP_STAT (1<<2)
@@ -53,3 +54,24 @@
#define SPELL_CULT_ARMOR (1<<10)
#define SPELL_WIZARD_GARB (SPELL_WIZARD_HAT|SPELL_WIZARD_ROBE)
#define SPELL_CULT_GARB (SPELL_CULT_HELMET|SPELL_CULT_ARMOR)
//// Identification ////
// /datum/component/identification/identification_flags
/// Delete on successful broad identification (so the main way we "uncover" how an object works, since this won't be on it to obfuscate it)
#define ID_COMPONENT_DEL_ON_IDENTIFY (1<<0)
/// We've already been successfully deepscanned by a deconstructive analyzer
#define ID_COMPONENT_DECONSTRUCTOR_DEEPSCANNED (1<<1)
// /datum/component/identification/identification_effect_flags
/// Block user from getting actions if they don't know how to use this. Triggered on equip.
#define ID_COMPONENT_EFFECT_NO_ACTIONS (1<<0)
// /datum/component/identification/identification_method_flags
/// Can be identified in a deconstructive analyzer
#define ID_COMPONENT_IDENTIFY_WITH_DECONSTRUCTOR (1<<0)
// Return values for /datum/component/deitnfication/check_knowledge()
/// Has no knowledge, default
#define ID_COMPONENT_KNOWLEDGE_NONE 0
/// Has full knowledge
#define ID_COMPONENT_KNOWLEDGE_FULL 1

View File

@@ -30,7 +30,7 @@
#define COMSIG_PARENT_ATTACKBY "atom_attackby" //from base of atom/attackby(): (/obj/item, /mob/living, params)
#define COMPONENT_NO_AFTERATTACK 1 //Return this in response if you don't want afterattack to be called
#define COMSIG_ATOM_HULK_ATTACK "hulk_attack" //from base of atom/attack_hulk(): (/mob/living/carbon/human)
#define COMSIG_PARENT_EXAMINE "atom_examine" //from base of atom/examine(): (/mob)
#define COMSIG_PARENT_EXAMINE "atom_examine" //from base of atom/examine(): (/mob, list/examine_return_text)
#define COMSIG_ATOM_GET_EXAMINE_NAME "atom_examine_name" //from base of atom/get_examine_name(): (/mob, list/overrides)
//Positions for overrides list
#define EXAMINE_POSITION_ARTICLE 1
@@ -242,6 +242,8 @@
#define COMSIG_ITEM_AFTERATTACK "item_afterattack" //from base of obj/item/afterattack(): (atom/target, mob/user, params)
#define COMSIG_ITEM_ALT_AFTERATTACK "item_alt_afterattack" //from base of obj/item/altafterattack(): (atom/target, mob/user, proximity, params)
#define COMSIG_ITEM_EQUIPPED "item_equip" //from base of obj/item/equipped(): (/mob/equipper, slot)
// Do not grant actions on equip.
#define COMPONENT_NO_GRANT_ACTIONS 1
#define COMSIG_ITEM_DROPPED "item_drop" //from base of obj/item/dropped(): (mob/user)
// relocated, tell inventory procs if those called this that the item isn't available anymore.
#define COMPONENT_DROPPED_RELOCATION 1
@@ -254,6 +256,9 @@
// THE FOLLOWING TWO BLOCKS SHOULD RETURN BLOCK FLAGS AS DEFINED IN __DEFINES/combat.dm!
#define COMSIG_ITEM_CHECK_BLOCK "check_block" //from base of obj/item/check_block(): (mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
#define COMSIG_ITEM_RUN_BLOCK "run_block" //from base of obj/item/run_block(): (mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
#define COMSIG_ITEM_DECONSTRUCTOR_DEEPSCAN "deconstructor_deepscan" //Called by deconstructive analyzers deepscanning an item: (obj/machinery/rnd/destructive_analyzer/analyzer_machine, mob/user, list/information_list)
// Uncovered information
#define COMPONENT_DEEPSCAN_UNCOVERED_INFORMATION 1
// /obj/item/clothing signals
#define COMSIG_SHOES_STEP_ACTION "shoes_step_action" //from base of obj/item/clothing/shoes/proc/step_action(): ()

View File

@@ -8,6 +8,10 @@
#define PLANE_SPACE_PARALLAX -90
#define PLANE_SPACE_PARALLAX_RENDER_TARGET "PLANE_SPACE_PARALLAX"
#define OPENSPACE_LAYER 17 //Openspace layer over all
#define OPENSPACE_PLANE -4 //Openspace plane below all turfs
#define OPENSPACE_BACKDROP_PLANE -3 //Black square just over openspace plane to guaranteed cover all in openspace turf
#define FLOOR_PLANE -2
#define FLOOR_PLANE_RENDER_TARGET "FLOOR_PLANE"
#define GAME_PLANE -1
@@ -105,10 +109,6 @@
#define ABOVE_LIGHTING_LAYER 16
#define ABOVE_LIGHTING_RENDER_TARGET "ABOVE_LIGHTING_PLANE"
#define FLOOR_OPENSPACE_PLANE 17
#define OPENSPACE_LAYER 17
#define OPENSPACE_RENDER_TARGET "OPENSPACE_PLANE"
#define BYOND_LIGHTING_PLANE 18
#define BYOND_LIGHTING_LAYER 18
#define BYOND_LIGHTING_RENDER_TARGET "BYOND_LIGHTING_PLANE"

View File

@@ -22,14 +22,14 @@
//used in design to specify which machine can build it
#define IMPRINTER (1<<0) //For circuits. Uses glass/chemicals.
#define PROTOLATHE (1<<1) //New stuff. Uses glass/metal/chemicals
#define AUTOLATHE (1<<2) //Uses glass/metal only.
#define CRAFTLATHE (1<<3) //Uses fuck if I know. For use eventually.
#define MECHFAB (1<<4) //Remember, objects utilising this flag should have construction_time and construction_cost vars.
#define BIOGENERATOR (1<<5) //Uses biomass
#define LIMBGROWER (1<<6) //Uses synthetic flesh
#define SMELTER (1<<7) //uses various minerals
#define AUTOYLATHE (1<<8) // CITADEL ADD
#define PROTOLATHE (1<<1) //New stuff. Uses materials/chemicals
#define AUTOLATHE (1<<2) //Uses materials only.
#define TOYLATHE (1<<3) //Glass/metal/plastic. Meant for toys.
#define NO_PUBLIC_LATHE (1<<4) //prevents the design from being auto-unlocked by public auto(y)lathes.
#define MECHFAB (1<<5) //Remember, objects utilising this flag should have construction_time and construction_cost vars.
#define BIOGENERATOR (1<<6) //Uses biomass
#define LIMBGROWER (1<<7) //Uses synthetic flesh
#define SMELTER (1<<8) //uses various minerals
#define NANITE_COMPILER (1<<9) //Prints nanite disks
#define AUTOBOTTLER (1<<10) //Uses booze, for printing
//Note: More then one of these can be added to a design but imprinter and lathe designs are incompatable.

View File

@@ -515,3 +515,17 @@ GLOBAL_LIST_INIT(pda_reskins, list(PDA_SKIN_CLASSIC = 'icons/obj/pda.dmi', PDA_S
#define NIGHTSHIFT_AREA_NONE 4 //default/highest.
#define UNTIL(X) while(!(X)) stoplag()
//Scavenging element defines for special loot "events".
#define SCAVENGING_FOUND_NOTHING "found_nothing"
#define SCAVENGING_SPAWN_MOUSE "spawn_mouse"
#define SCAVENGING_SPAWN_MICE "spawn_mice"
#define SCAVENGING_SPAWN_TOM "spawn_tom_the_mouse"
//Scavenging element defines for ckey/mind restrictions.
#define NO_LOOT_RESTRICTION 0
#define LOOT_RESTRICTION_MIND 1
#define LOOT_RESTRICTION_CKEY 2
#define LOOT_RESTRICTION_MIND_PILE 3 //limited to the current pile.
#define LOOT_RESTRICTION_CKEY_PILE 4 //Idem

View File

@@ -60,7 +60,8 @@
#define DESIGN_ID_IGNORE "IGNORE_THIS_DESIGN"
#define RESEARCH_MATERIAL_RECLAMATION_ID "__materials"
#define RESEARCH_MATERIAL_RECLAMATION_ID "__materials"
#define RESEARCH_DEEP_SCAN_ID "__deepscan"
//When adding new types, update the list below!
#define TECHWEB_POINT_TYPE_GENERIC "General Research"

View File

@@ -317,7 +317,7 @@ GLOBAL_LIST_EMPTY(species_list)
else
return "unknown"
/proc/do_mob(mob/user , mob/target, time = 30, uninterruptible = 0, progress = 1, datum/callback/extra_checks = null, ignorehelditem = 0)
/proc/do_mob(mob/user , mob/target, time = 30, uninterruptible = 0, progress = 1, datum/callback/extra_checks = null, ignorehelditem = FALSE, resume_time = 0 SECONDS)
if(!user || !target)
return 0
var/user_loc = user.loc
@@ -336,10 +336,10 @@ GLOBAL_LIST_EMPTY(species_list)
var/endtime = world.time+time
var/starttime = world.time
. = 1
while (world.time < endtime)
while (world.time + resume_time < endtime)
stoplag(1)
if (progress)
progbar.update(world.time - starttime)
progbar.update(world.time - starttime + resume_time)
if(QDELETED(user) || QDELETED(target))
. = 0
break
@@ -371,7 +371,7 @@ GLOBAL_LIST_EMPTY(species_list)
checked_health["health"] = health
return ..()
/proc/do_after(mob/user, var/delay, needhand = 1, atom/target = null, progress = 1, datum/callback/extra_checks = null, required_mobility_flags = (MOBILITY_USE|MOBILITY_MOVE))
/proc/do_after(mob/user, var/delay, needhand = 1, atom/target = null, progress = 1, datum/callback/extra_checks = null, required_mobility_flags = (MOBILITY_USE|MOBILITY_MOVE), resume_time = 0 SECONDS)
if(!user)
return 0
var/atom/Tloc = null
@@ -400,10 +400,10 @@ GLOBAL_LIST_EMPTY(species_list)
var/starttime = world.time
. = 1
var/mob/living/L = isliving(user) && user //evals to last thing eval'd
while (world.time < endtime)
while (world.time + resume_time < endtime)
stoplag(1)
if (progress)
progbar.update(world.time - starttime)
progbar.update(world.time - starttime + resume_time)
if(drifting && !user.inertia_dir)
drifting = 0

View File

@@ -563,7 +563,7 @@
if(objective.completable)
var/completion = objective.check_completion()
if(completion >= 1)
objective_parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</span>"
objective_parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</B></span>"
else if(completion <= 0)
objective_parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
else

View File

@@ -265,5 +265,15 @@ GLOBAL_LIST_INIT(bitfields, list(
"STORAGE_LIMIT_MAX_W_CLASS" = STORAGE_LIMIT_MAX_W_CLASS,
"STORAGE_LIMIT_COMBINED_W_CLASS" = STORAGE_LIMIT_COMBINED_W_CLASS,
"STORAGE_LIMIT_VOLUME" = STORAGE_LIMIT_VOLUME
),
"vis_flags" = list(
"VIS_INHERIT_ICON" = VIS_INHERIT_ICON,
"VIS_INHERIT_ICON_STATE" = VIS_INHERIT_ICON_STATE,
"VIS_INHERIT_DIR" = VIS_INHERIT_DIR,
"VIS_INHERIT_LAYER" = VIS_INHERIT_LAYER,
"VIS_INHERIT_PLANE" = VIS_INHERIT_PLANE,
"VIS_INHERIT_ID" = VIS_INHERIT_ID,
"VIS_UNDERLAY" = VIS_UNDERLAY,
"VIS_HIDE" = VIS_HIDE
)
))

View File

@@ -43,15 +43,26 @@ GLOBAL_LIST_EMPTY(mob_config_movespeed_type_lookup)
GLOBAL_LIST_EMPTY(latejoiners) //CIT CHANGE - All latejoining people, for traitor-target purposes.
/proc/update_config_movespeed_type_lookup(update_mobs = TRUE)
var/list/mob_types = list()
// NOTE: This is entirely based on the fact that byond typesof/subtypesof gets longer/deeper paths before shallower ones.
// If that ever breaks this entire proc breaks.
var/list/mob_types = typesof(/mob)
var/list/entry_value = CONFIG_GET(keyed_list/multiplicative_movespeed)
var/list/configured_types = list()
for(var/path in entry_value)
var/value = entry_value[path]
if(!value)
if(isnull(value))
continue
// associative list sets for elements that already exist preserve order
mob_types[path] = value
// now go back up through it to set everything, making absolute sure that base paths are overridden by child paths all the way down the path tree.
for(var/i in length(mob_types) to 1 step -1)
var/path = mob_types[i]
if(isnull(mob_types[path]))
continue
// we're going from bottom to top so it should be safe to do this without further checks..
for(var/subpath in typesof(path))
mob_types[subpath] = value
GLOB.mob_config_movespeed_type_lookup = mob_types
configured_types[subpath] = mob_types[path]
GLOB.mob_config_movespeed_type_lookup = configured_types
if(update_mobs)
update_mob_config_movespeeds()

View File

@@ -19,7 +19,7 @@
///Things rendered on "openspace"; holes in multi-z
/obj/screen/plane_master/openspace
name = "open space plane master"
plane = FLOOR_OPENSPACE_PLANE
plane = OPENSPACE_BACKDROP_PLANE
appearance_flags = PLANE_MASTER
blend_mode = BLEND_MULTIPLY
alpha = 255
@@ -106,8 +106,8 @@
*
* You really shouldn't be directly using this, use atom helpers instead
*/
/obj/screen/plane_master/emissive_unblockable
name = "emissive mob plane master"
/obj/screen/plane_master/emissive_blocker
name = "emissive blocker plane master"
plane = EMISSIVE_BLOCKER_PLANE
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
render_target = EMISSIVE_BLOCKER_RENDER_TARGET

View File

@@ -44,6 +44,13 @@ SUBSYSTEM_DEF(mapping)
var/stat_map_name = "Loading..."
/// Lookup list for random generated IDs.
var/list/random_generated_ids_by_original = list()
/// next id for separating obfuscated ids.
var/obfuscation_next_id = 1
/// "secret" key
var/obfuscation_secret
//dlete dis once #39770 is resolved
/datum/controller/subsystem/mapping/proc/HACK_LoadMapConfig()
if(!config)
@@ -54,6 +61,10 @@ SUBSYSTEM_DEF(mapping)
#endif
stat_map_name = config.map_name
/datum/controller/subsystem/mapping/PreInit()
if(!obfuscation_secret)
obfuscation_secret = md5(GUID()) //HAH! Guess this!
/datum/controller/subsystem/mapping/Initialize(timeofday)
HACK_LoadMapConfig()
if(initialized)
@@ -590,3 +601,15 @@ GLOBAL_LIST_EMPTY(the_station_areas)
LM.load()
if(GLOB.stationroom_landmarks.len)
seedStation() //I'm sure we can trust everyone not to insert a 1x1 rooms which loads a landmark which loads a landmark which loads a la...
/**
* Generates an obfuscated but constant id for an original id for cases where you don't want players codediving for an id.
* WARNING: MAKE SURE PLAYERS ARE NOT ABLE TO ACCESS THIS. To save performance, it's just secret + an incrementing number. Very guessable if you know what the secret is.
*/
/datum/controller/subsystem/mapping/proc/get_obfuscated_id(original, id_type = "GENERAL")
if(!original)
return //no.
var/key = "[original]%[id_type]"
if(random_generated_ids_by_original[key])
return random_generated_ids_by_original[key]
. = random_generated_ids_by_original[key] = "[obfuscation_secret]%[obfuscation_next_id++]"

View File

@@ -1,95 +0,0 @@
/datum/component/archaeology
dupe_mode = COMPONENT_DUPE_UNIQUE
var/list/archdrops = list(/obj/item/bikehorn = list(ARCH_PROB = 100, ARCH_MAXDROP = 1)) // honk~
var/prob2drop
var/dug
var/datum/callback/callback
/datum/component/archaeology/Initialize(list/_archdrops = list(), datum/callback/_callback)
archdrops = _archdrops
for(var/i in archdrops)
if(isnull(archdrops[i][ARCH_MAXDROP]))
archdrops[i][ARCH_MAXDROP] = 1
stack_trace("ARCHAEOLOGY WARNING: [parent] contained a null max_drop value in [i].")
if(isnull(archdrops[i][ARCH_PROB]))
archdrops[i][ARCH_PROB] = 100
stack_trace("ARCHAEOLOGY WARNING: [parent] contained a null probability value in [i].")
callback = _callback
RegisterSignal(parent, COMSIG_PARENT_ATTACKBY,.proc/Dig)
RegisterSignal(parent, COMSIG_ATOM_EX_ACT, .proc/BombDig)
RegisterSignal(parent, COMSIG_ATOM_SING_PULL, .proc/SingDig)
/datum/component/archaeology/InheritComponent(datum/component/archaeology/A, i_am_original)
var/list/other_archdrops = A.archdrops
var/list/_archdrops = archdrops
for(var/I in other_archdrops)
_archdrops[I] += other_archdrops[I]
/datum/component/archaeology/proc/Dig(datum/source, obj/item/I, mob/living/user)
if(dug)
to_chat(user, "<span class='notice'>Looks like someone has dug here already.</span>")
return
if(!isturf(user.loc))
return
if(I.tool_behaviour == TOOL_SHOVEL || I.tool_behaviour == TOOL_MINING)
to_chat(user, "<span class='notice'>You start digging...</span>")
if(I.use_tool(parent, user, 40, volume=50))
to_chat(user, "<span class='notice'>You dig a hole.</span>")
gets_dug()
dug = TRUE
SSblackbox.record_feedback("tally", "pick_used_mining", 1, I.type)
return COMPONENT_NO_AFTERATTACK
/datum/component/archaeology/proc/gets_dug()
if(dug)
return
else
var/turf/open/OT = get_turf(parent)
for(var/thing in archdrops)
var/maxtodrop = archdrops[thing][ARCH_MAXDROP]
for(var/i in 1 to maxtodrop)
if(prob(archdrops[thing][ARCH_PROB])) // can't win them all!
new thing(OT)
if(isopenturf(OT))
if(OT.postdig_icon_change)
if(istype(OT, /turf/open/floor/plating/asteroid/) && !OT.postdig_icon)
var/turf/open/floor/plating/asteroid/AOT = parent
AOT.icon_plating = "[AOT.environment_type]_dug"
AOT.icon_state = "[AOT.environment_type]_dug"
else
if(isplatingturf(OT))
var/turf/open/floor/plating/POT = parent
POT.icon_plating = "[POT.postdig_icon]"
POT.icon_state = "[OT.postdig_icon]"
if(OT.slowdown) //Things like snow slow you down until you dig them.
OT.slowdown = 0
dug = TRUE
if(callback)
callback.Invoke()
/datum/component/archaeology/proc/SingDig(datum/source, S, current_size)
switch(current_size)
if(STAGE_THREE)
if(prob(30))
gets_dug()
if(STAGE_FOUR)
if(prob(50))
gets_dug()
else
if(current_size >= STAGE_FIVE && prob(70))
gets_dug()
/datum/component/archaeology/proc/BombDig(datum/source, severity, target)
switch(severity)
if(3)
return
if(2)
if(prob(20))
gets_dug()
if(1)
gets_dug()

View File

@@ -260,6 +260,15 @@
subcategory = CAT_TOOL
category = CAT_MISC
/datum/crafting_recipe/electrochromatic_kit
name = "Electrochromatic Kit"
result = /obj/item/electronics/electrochromatic_kit
reqs = list(/obj/item/stack/sheet/metal = 1,
/obj/item/stack/cable_coil = 1)
time = 5
subcategory = CAT_TOOL
category = CAT_MISC
////////////
//Vehicles//
////////////

View File

@@ -0,0 +1,88 @@
/**
* Identification components
*/
/datum/component/identification
/// General flags for how we should work.
var/identification_flags = NONE
/// General flags for what we should do.
var/identification_effect_flags = NONE
/// General flags for how we can be identified.
var/identification_method_flags = NONE
/// If this is set, show this on examine to the examiner if they know how to use it.
var/additional_examine_text = "<span class='notice'>You seem to know more about this item than others..</span>"
/// Added to deconstructive analyzer say on success if set
var/deconstructor_reveal_text = "item operation instructions"
/datum/component/identification/Initialize(id_flags, id_effect_flags, id_method_flags)
if(!isobj(parent))
return COMPONENT_INCOMPATIBLE
. = ..()
if(. & COMPONENT_INCOMPATIBLE)
return
identification_flags = id_flags
identification_effect_flags = id_effect_flags
identification_method_flags = id_method_flags
/datum/component/identification/RegisterWithParent()
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, .proc/on_examine)
if(identification_effect_flags & ID_COMPONENT_EFFECT_NO_ACTIONS)
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, .proc/on_equip)
if(identification_method_flags & ID_COMPONENT_IDENTIFY_WITH_DECONSTRUCTOR)
RegisterSignal(parent, COMSIG_ITEM_DECONSTRUCTOR_DEEPSCAN, .proc/on_deconstructor_deepscan)
/datum/component/identification/UnregisterFromParent()
var/list/unregister = list(COMSIG_PARENT_EXAMINE)
if(identification_effect_flags & ID_COMPONENT_EFFECT_NO_ACTIONS)
unregister += COMSIG_ITEM_EQUIPPED
if(identification_method_flags & ID_COMPONENT_IDENTIFY_WITH_DECONSTRUCTOR)
unregister += COMSIG_ITEM_DECONSTRUCTOR_DEEPSCAN
UnregisterSignal(parent, unregister)
/datum/component/identification/proc/on_examine(datum/source, mob/user, list/returnlist)
if(check_knowledge(user) != ID_COMPONENT_KNOWLEDGE_FULL)
return
if(!additional_examine_text)
return
returnlist += additional_examine_text
/datum/component/identification/vv_edit_var(var_name, var_value)
// since i care SOOO much about memory optimization, we only register signals we need to
// so when someone vv's us, we should probably make sure we have the ones we need to with an update.
if((var_value == NAMEOF(src, identification_flags)) || (var_value == NAMEOF(src, identification_effect_flags)) || (var_value == NAMEOF(src, identification_method_flags)))
UnregisterFromParent()
. = ..()
if((var_value == NAMEOF(src, identification_flags)) || (var_value == NAMEOF(src, identification_effect_flags)) || (var_value == NAMEOF(src, identification_method_flags)))
RegisterWithParent()
/datum/component/identification/proc/on_equip(datum/source, mob/user)
if(check_knowledge(user) == ID_COMPONENT_KNOWLEDGE_FULL)
return
if(identification_method_flags & ID_COMPONENT_EFFECT_NO_ACTIONS)
return COMPONENT_NO_GRANT_ACTIONS
/datum/component/identification/proc/check_knowledge(mob/user)
return ID_COMPONENT_KNOWLEDGE_NONE
/datum/component/identification/proc/on_identify(mob/user)
if(identification_flags & ID_COMPONENT_DEL_ON_IDENTIFY)
qdel(src)
/datum/component/identification/proc/on_deconstructor_deepscan(datum/source, obj/machinery/rnd/destructive_analyzer/analyzer, mob/user, list/information = list())
if((identification_method_flags & ID_COMPONENT_IDENTIFY_WITH_DECONSTRUCTOR) && !(identification_flags & ID_COMPONENT_DECONSTRUCTOR_DEEPSCANNED))
identification_flags |= ID_COMPONENT_DECONSTRUCTOR_DEEPSCANNED
on_identify(user)
if(deconstructor_reveal_text)
information += deconstructor_reveal_text
return COMPONENT_DEEPSCAN_UNCOVERED_INFORMATION
/**
* Identification component subtype - Syndicate
*
* Checks if the user is a traitor.
*/
/datum/component/identification/syndicate
/datum/component/identification/syndicate/check_knowledge(mob/user)
. = ..()
if(user?.mind?.has_antag_datum(/datum/antagonist/traitor))
. = max(., ID_COMPONENT_KNOWLEDGE_FULL)

View File

@@ -0,0 +1,215 @@
/*
* Scavenging element. Its scope shouldn't elude your imagination.
* Basically loot piles that can be searched through for some items.
* In my opinion, these are more engaging than normal maintenance loot spawners.
* The loot doesn't have to be strictly made of items and objects, you could also use it to invoke some "events"
* such as mice, rats, an halloween spook, persistent relics, traps, etcetera, go wild.
*/
/datum/element/scavenging
element_flags = ELEMENT_BESPOKE|ELEMENT_DETACH
id_arg_index = 3
var/list/loot_left_per_atom = list() //loot left per attached atom.
var/list/loot_table //pickweight list of available loot.
var/list/unique_loot //limited loot, once the associated value reaches zero, its key is removed from loot_table
var/scavenge_time = 12 SECONDS //how much time it takes
var/can_use_hands = TRUE //bare handed scavenge time multiplier. If set to zero, only tools are usable.
var/list/tool_types //which tool types the player can use instead of scavenging by hand, associated value is their speed.
var/del_atom_on_depletion = FALSE //Will the atom be deleted when there is no loot left?
var/list/search_texts = list("starts searching through", "start searching through", "You hear rummaging...")
var/loot_restriction = NO_LOOT_RESTRICTION
var/maximum_loot_per_player = 1 //only relevant if there is a restriction.
var/list/scavenger_restriction_list //used for restrictions.
var/mean_loot_weight = 0
var/list/progress_per_atom = list() //seconds of ditched progress per atom, used to resume the work instead of starting over.
var/static/list/players_busy_scavenging = list() //players already busy scavenging.
/datum/element/scavenging/Attach(atom/target, amount = 5, list/loot, list/unique, time = 12 SECONDS, hands = TRUE, list/tools, list/texts, \
del_deplete = FALSE, restriction = NO_LOOT_RESTRICTION, max_per_player = 1)
. = ..()
if(. == ELEMENT_INCOMPATIBLE || !length(loot) || !amount || !istype(target) || isarea(target))
return ELEMENT_INCOMPATIBLE
loot_left_per_atom[target] = amount
if(!loot_table)
loot_table = loot
for(var/A in loot_table) //tally the list weights
mean_loot_weight += loot_table[A]
mean_loot_weight /= length(loot_table)
if(!unique_loot)
unique_loot = unique || list()
scavenge_time = time
can_use_hands = hands
tool_types = tools
if(texts)
search_texts = texts
del_atom_on_depletion = del_deplete
loot_restriction = restriction
maximum_loot_per_player = max_per_player
if(can_use_hands)
RegisterSignal(target, list(COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_ATTACK_PAW), .proc/scavenge_barehanded)
if(tool_types)
RegisterSignal(target, COMSIG_PARENT_ATTACKBY, .proc/scavenge_tool)
RegisterSignal(target, COMSIG_PARENT_EXAMINE, .proc/on_examine)
/datum/element/scavenging/Detach(atom/target)
. = ..()
loot_left_per_atom -= target
progress_per_atom -= target
if(maximum_loot_per_player == LOOT_RESTRICTION_MIND_PILE || maximum_loot_per_player == LOOT_RESTRICTION_CKEY_PILE)
maximum_loot_per_player -= target
UnregisterSignal(target, list(COMSIG_ATOM_ATTACK_HAND, COMSIG_PARENT_ATTACKBY, COMSIG_PARENT_EXAMINE))
/datum/element/scavenging/proc/on_examine(atom/source, mob/user, list/examine_list)
var/methods = tool_types.Copy()
if(can_use_hands)
methods += list("bare handed")
if(!length(methods))
return
var/text = english_list(methods, "", " or ")
examine_list += "<span class='notice'>Looks like [source.p_they()] can be scavenged [length(tool_types) ? "with" : ""][length(methods == 1) ? "" : "either "][length(tool_types) ? "a " : ""][text]</span>"
/datum/element/scavenging/proc/scavenge_barehanded(atom/source, mob/user)
scavenge(source, user, 1)
return COMPONENT_NO_ATTACK_HAND
/datum/element/scavenging/proc/scavenge_tool(atom/source, obj/item/I, mob/user, params)
if(user.a_intent == INTENT_HARM || !I.tool_behaviour) //Robust trash disposal techniques!
return
var/speed_multi = tool_types[I.tool_behaviour]
if(!speed_multi)
return
scavenge(source, user, speed_multi)
return COMPONENT_NO_AFTERATTACK
/// This proc has to be asynced (cough cough, do_after) in order to return the comsig values in time to stop the attack chain.
/datum/element/scavenging/proc/scavenge(atom/source, mob/user, speed_multi = 1)
set waitfor = FALSE
if(players_busy_scavenging[user])
return
players_busy_scavenging[user] = TRUE
var/progress_done = progress_per_atom[source]
var/len_messages = length(search_texts)
var/msg_first_person
if(len_messages >= 2)
msg_first_person = "<span class='notice'>You [progress_done ? ", resume a ditched task and " : ""][search_texts[2]] [source].</span>"
var/msg_blind
if(len_messages >= 3)
msg_blind = "<span class='italic'>[search_texts[3]]</span>"
user.visible_message("<span class='notice'>[user] [search_texts[1]] [source].</span>", msg_first_person, msg_blind)
if(do_after(user, scavenge_time * speed_multi, TRUE, source, TRUE, CALLBACK(src, .proc/set_progress, source, world.time), resume_time = progress_done * speed_multi))
spawn_loot(source, user)
players_busy_scavenging -= user
/datum/element/scavenging/proc/set_progress(atom/source, start_time)
progress_per_atom[source] = world.time - start_time
return TRUE
/datum/element/scavenging/proc/spawn_loot(atom/source, mob/user)
progress_per_atom -= source
var/loot = pickweight(loot_table)
var/special = TRUE
var/free = FALSE
if(!loot_left_per_atom[source])
to_chat(user, "<span class='warning'>Looks likes there is nothing worth of interest left in [source], what a shame...</span>")
return
var/num_times = 0
switch(loot_restriction)
if(LOOT_RESTRICTION_MIND)
num_times = LAZYACCESS(scavenger_restriction_list, user.mind)
if(LOOT_RESTRICTION_CKEY)
num_times = LAZYACCESS(scavenger_restriction_list, user.ckey)
if(LOOT_RESTRICTION_MIND_PILE)
var/list/L = LAZYACCESS(scavenger_restriction_list, source)
if(L)
num_times = LAZYACCESS(L, user.mind)
if(LOOT_RESTRICTION_CKEY_PILE)
var/list/L = LAZYACCESS(scavenger_restriction_list, source)
if(L)
num_times = LAZYACCESS(L, user.ckey)
if(num_times >= maximum_loot_per_player)
to_chat(user, "<span class='warning'>You can't find anything else vaguely useful in [source]. Another set of eyes might, however.</span>")
return
switch(loot) // TODO: datumize these out.
if(SCAVENGING_FOUND_NOTHING)
to_chat(user, "<span class='notice'>You found nothing, better luck next time.</span>")
free = TRUE //doesn't consume the loot pile.
if(SCAVENGING_SPAWN_MOUSE)
var/nasty_rodent = pick("mouse", "rodent", "squeaky critter", "stupid pest", "annoying cable chewer", "nasty, ugly, evil, disease-ridden rodent")
to_chat(user, "<span class='notice'>You found something in [source]... no wait, that's just another [nasty_rodent].</span>")
new /mob/living/simple_animal/mouse(source.loc)
if(SCAVENGING_SPAWN_MICE)
user.visible_message("<span class='notice'>A small gang of mice emerges from [source].</span>", \
"<span class='notice'>You found something in [source]... no wait, that's just another- <b>no wait, that's a lot of damn mice.</b></span>")
for(var/i in 1 to rand(4, 6))
new /mob/living/simple_animal/mouse(source.loc)
if(SCAVENGING_SPAWN_TOM)
if(GLOB.tom_existed) //There can only be one.
to_chat(user, "<span class='notice'>You found nothing, better luck next time.</span>")
free = TRUE
else
to_chat(user, "<span class='notice'>You found something in [source]... no wait, that's Tom, the mouse! What is he doing here?</span>")
new /mob/living/simple_animal/mouse/brown/Tom(source.loc)
else
special = FALSE
if(!special) //generic loot. Nothing too strange like more loot spawners anyway.
var/atom/A = new loot(source.loc)
if(isitem(A) && !user.get_active_held_item())
user.put_in_hands(A)
var/rarity_append = "."
switch(loot_table[loot]/mean_loot_weight*100)
if(0 to 1)
rarity_append = "! <b>AMAZING!</b>"
if(1 to 2)
rarity_append = "! Woah!"
if(2 to 5)
rarity_append = ". Rare!"
if(5 to 10)
rarity_append = ". Great."
if(10 to 25)
rarity_append = ". Nice."
if(20 to 50)
rarity_append = ". Not bad."
to_chat(user, "You found something in [source]... it's \a [A][rarity_append]")
if(unique_loot[loot])
var/loot_left = --unique_loot[loot]
if(!loot_left)
loot_table -= loot
unique_loot -= loot
mean_loot_weight = 0
for(var/A in loot_table) //re-tally the list weights
mean_loot_weight += loot_table[A]
mean_loot_weight /= length(loot_table)
if(free)
return
--loot_left_per_atom[source]
if(del_atom_on_depletion && !loot_left_per_atom[source])
source.visible_message("<span class='warning'>[source] has been looted clean.</span>")
qdel(source)
return
if(!loot_restriction)
return
LAZYINITLIST(scavenger_restriction_list)
switch(loot_restriction)
if(LOOT_RESTRICTION_MIND)
scavenger_restriction_list[user.mind]++
if(LOOT_RESTRICTION_CKEY)
scavenger_restriction_list[user.ckey]++
if(LOOT_RESTRICTION_MIND_PILE)
LAZYINITLIST(scavenger_restriction_list[source])
var/list/L = scavenger_restriction_list[source]
L[user.mind]++
if(LOOT_RESTRICTION_CKEY_PILE)
LAZYINITLIST(scavenger_restriction_list[source])
var/list/L = scavenger_restriction_list[source]
L[user.ckey]++

View File

@@ -1,48 +0,0 @@
/datum/wires/autoylathe
holder_type = /obj/machinery/autoylathe
proper_name = "Autoylathe"
/datum/wires/autoylathe/New(atom/holder)
wires = list(
WIRE_HACK, WIRE_DISABLE,
WIRE_SHOCK, WIRE_ZAP
)
add_duds(6)
..()
/datum/wires/autoylathe/interactable(mob/user)
var/obj/machinery/autoylathe/A = holder
if(A.panel_open)
return TRUE
/datum/wires/autoylathe/get_status()
var/obj/machinery/autoylathe/A = holder
var/list/status = list()
status += "The red light is [A.disabled ? "on" : "off"]."
status += "The blue light is [A.hacked ? "on" : "off"]."
return status
/datum/wires/autoylathe/on_pulse(wire)
var/obj/machinery/autoylathe/A = holder
switch(wire)
if(WIRE_HACK)
A.adjust_hacked(!A.hacked)
addtimer(CALLBACK(A, /obj/machinery/autoylathe.proc/reset, wire), 60)
if(WIRE_SHOCK)
A.shocked = !A.shocked
addtimer(CALLBACK(A, /obj/machinery/autoylathe.proc/reset, wire), 60)
if(WIRE_DISABLE)
A.disabled = !A.disabled
addtimer(CALLBACK(A, /obj/machinery/autoylathe.proc/reset, wire), 60)
/datum/wires/autoylathe/on_cut(wire, mend)
var/obj/machinery/autoylathe/A = holder
switch(wire)
if(WIRE_HACK)
A.adjust_hacked(!mend)
if(WIRE_HACK)
A.shocked = !mend
if(WIRE_DISABLE)
A.disabled = !mend
if(WIRE_ZAP)
A.shock(usr, 50)

View File

@@ -68,7 +68,7 @@
if(vs.plane == EMISSIVE_BLOCKER_PLANE)
SSvis_overlays.remove_vis_overlay(src, list(vs))
break
SSvis_overlays.add_vis_overlay(src, icon, icon_state, EMISSIVE_BLOCKER_LAYER, EMISSIVE_BLOCKER_PLANE)
SSvis_overlays.add_vis_overlay(src, icon, icon_state, EMISSIVE_BLOCKER_LAYER, EMISSIVE_BLOCKER_PLANE, dir)
/atom/movable/proc/can_zFall(turf/source, levels = 1, turf/target, direction)
if(!direction)

View File

@@ -839,3 +839,11 @@ GLOBAL_VAR_INIT(dynamic_forced_storyteller, null)
return RULE_OF_THREE(40, 20, x) + 50
if (20 to INFINITY)
return rand(90, 100)
/datum/game_mode/dynamic/ghost_info()
. = list()
. += "Current threat: [threat]"
. += "Target threat: [threat_level]"
. += "Storyteller: <b>[storyteller.name]</b><br/>"
. += "Parameters: centre = [GLOB.dynamic_curve_centre] ; width = [GLOB.dynamic_curve_width].<br/>"
. += "<i>On average, <b>[peaceful_percentage]</b>% of the rounds are more peaceful.</i><br/>"

View File

@@ -604,3 +604,7 @@
/// Mode specific admin panel.
/datum/game_mode/proc/admin_panel()
return
/// Mode specific info for ghost game_info
/datum/game_mode/proc/ghost_info()
return

View File

@@ -28,11 +28,11 @@
var/prod_coeff = 1
var/datum/design/being_built
var/datum/techweb/stored_research
var/list/datum/design/matching_designs
var/selected_category
var/screen = 1
var/datum/techweb/stored_research = /datum/techweb/specialized/autounlocking/autolathe
var/list/categories = list(
"Tools",
"Electronics",
@@ -45,9 +45,7 @@
"Dinnerware",
"Imported"
)
/obj/machinery/autolathe/Initialize()
var/static/list/allowed_types = list(
var/list/allowed_materials = list(
/datum/material/iron,
/datum/material/glass,
/datum/material/gold,
@@ -63,10 +61,12 @@
/datum/material/adamantine,
/datum/material/mythril
)
AddComponent(/datum/component/material_container, allowed_types, _show_on_examine=TRUE, _after_insert=CALLBACK(src, .proc/AfterMaterialInsert))
/obj/machinery/autolathe/Initialize()
AddComponent(/datum/component/material_container, allowed_materials, _show_on_examine=TRUE, _after_insert=CALLBACK(src, .proc/AfterMaterialInsert))
. = ..()
wires = new /datum/wires/autolathe(src)
stored_research = new /datum/techweb/specialized/autounlocking/autolathe
stored_research = new stored_research
matching_designs = list()
/obj/machinery/autolathe/Destroy()
@@ -91,7 +91,7 @@
if(AUTOLATHE_SEARCH_MENU)
dat = search_win(user)
var/datum/browser/popup = new(user, "autolathe", name, 400, 500)
var/datum/browser/popup = new(user, name, name, 400, 500)
popup.set_content(dat)
popup.open()
@@ -439,3 +439,32 @@
desc = "An autolathe reprogrammed with security protocols to prevent hacking."
hackable = FALSE
circuit = /obj/item/circuitboard/machine/autolathe/secure
stored_research = /datum/techweb/specialized/autounlocking/autolathe/public
/obj/machinery/autolathe/toy
name = "autoylathe"
desc = "It produces toys using plastic, metal and glass."
circuit = /obj/item/circuitboard/machine/autolathe/toy
stored_research = /datum/techweb/specialized/autounlocking/autolathe/toy
categories = list(
"Toys",
"Figurines",
"Pistols",
"Rifles",
"Heavy",
"Melee",
"Armor",
"Adult",
"Misc",
"Imported"
)
allowed_materials = list(
/datum/material/iron,
/datum/material/glass,
/datum/material/plastic
)
/obj/machinery/autolathe/toy/hacked/Initialize()
. = ..()
adjust_hacked(TRUE)

View File

@@ -16,6 +16,9 @@
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
/obj/machinery/button/Initialize(mapload, ndir = 0, built = 0)
if(istext(id) && mapload)
if(copytext(id, 1, 2) == "!")
id = SSmapping.get_obfuscated_id(id)
. = ..()
if(built)
setDir(ndir)
@@ -260,6 +263,11 @@
req_access = list()
id = 1
/obj/machinery/button/electrochromatic
name = "window dim control"
desc = "Controls linked electrochromatic windows"
device_type = /obj/item/assembly/control/electrochromatic
/obj/item/wallframe/button
name = "button frame"
desc = "Used for building buttons."

View File

@@ -1,415 +0,0 @@
#define AUTOYLATHE_MAIN_MENU 1
#define AUTOYLATHE_CATEGORY_MENU 2
#define AUTOYLATHE_SEARCH_MENU 3
/obj/machinery/autoylathe
name = "autoylathe"
desc = "It produces toys using plastic, metal and glass."
icon_state = "autolathe"
density = TRUE
use_power = IDLE_POWER_USE
idle_power_usage = 10
active_power_usage = 100
circuit = /obj/item/circuitboard/machine/autoylathe
layer = BELOW_OBJ_LAYER
var/operating = FALSE
var/list/L = list()
var/list/LL = list()
var/hacked = FALSE
var/disabled = 0
var/shocked = FALSE
var/hack_wire
var/disable_wire
var/shock_wire
var/busy = FALSE
var/prod_coeff = 1
var/datum/design/being_built
var/datum/techweb/stored_research
var/list/datum/design/matching_designs
var/selected_category
var/screen = 1
var/list/categories = list(
"Toys",
"Figurines",
"Pistols",
"Rifles",
"Heavy",
"Melee",
"Armor",
"Adult",
"Misc",
"Imported"
)
/obj/machinery/autoylathe/Initialize()
var/static/list/allowed_materials = list(
/datum/material/iron,
/datum/material/glass,
/datum/material/plastic
)
AddComponent(/datum/component/material_container, allowed_materials, 0, TRUE, null, null, CALLBACK(src, .proc/AfterMaterialInsert))
. = ..()
wires = new /datum/wires/autoylathe(src)
stored_research = new /datum/techweb/specialized/autounlocking/autoylathe
matching_designs = list()
/obj/machinery/autoylathe/Destroy()
QDEL_NULL(wires)
return ..()
/obj/machinery/autoylathe/ui_interact(mob/user)
. = ..()
if(!is_operational())
return
if(shocked && !(stat & NOPOWER))
shock(user,50)
var/dat
switch(screen)
if(AUTOYLATHE_MAIN_MENU)
dat = main_win(user)
if(AUTOYLATHE_CATEGORY_MENU)
dat = category_win(user,selected_category)
if(AUTOYLATHE_SEARCH_MENU)
dat = search_win(user)
var/datum/browser/popup = new(user, "Autoylathe", name, 400, 500)
popup.set_content(dat)
popup.open()
/obj/machinery/autoylathe/on_deconstruction()
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
materials.retrieve_all()
/obj/machinery/autoylathe/attackby(obj/item/O, mob/user, params)
if (busy)
to_chat(user, "<span class=\"alert\">The autoylathe is busy. Please wait for completion of previous operation.</span>")
return TRUE
if(default_deconstruction_screwdriver(user, "autolathe_t", "autolathe", O))
updateUsrDialog()
return TRUE
if(default_deconstruction_crowbar(O))
return TRUE
if(panel_open && is_wire_tool(O))
wires.interact(user)
return TRUE
if(user.a_intent == INTENT_HARM) //so we can hit the machine
return ..()
if(stat)
return TRUE
if(istype(O, /obj/item/disk/design_disk))
user.visible_message("[user] begins to load \the [O] in \the [src]...",
"You begin to load a design from \the [O]...",
"You hear the chatter of a floppy drive.")
busy = TRUE
var/obj/item/disk/design_disk/D = O
if(do_after(user, 14.4, target = src))
for(var/B in D.blueprints)
if(B)
stored_research.add_design(B)
busy = FALSE
return TRUE
return ..()
/obj/machinery/autoylathe/proc/AfterMaterialInsert(obj/item/item_inserted, id_inserted, amount_inserted)
if(item_inserted.custom_materials?.len && item_inserted.custom_materials[SSmaterials.GetMaterialRef(/datum/material/glass)])
flick("autolathe_r",src)//plays glass insertion animation by default otherwise
else
flick("autolathe_o",src)//plays metal insertion animation
use_power(min(1000, amount_inserted / 100))
updateUsrDialog()
/obj/machinery/autoylathe/Topic(href, href_list)
if(..())
return
if (!busy)
if(href_list["menu"])
screen = text2num(href_list["menu"])
updateUsrDialog()
if(href_list["category"])
selected_category = href_list["category"]
updateUsrDialog()
if(href_list["make"])
/////////////////
//href protection
being_built = stored_research.isDesignResearchedID(href_list["make"])
if(!being_built)
return
var/multiplier = text2num(href_list["multiplier"])
var/is_stack = ispath(being_built.build_path, /obj/item/stack)
multiplier = clamp(multiplier,1,50)
/////////////////
var/coeff = (is_stack ? 1 : prod_coeff) //stacks are unaffected by production coefficient
var/total_amount = 0
for(var/MAT in being_built.materials)
total_amount += being_built.materials[MAT]
var/power = max(2000, (total_amount)*multiplier/5) //Change this to use all materials
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
var/list/materials_used = list()
var/list/custom_materials = list() //These will apply their material effect, This should usually only be one.
for(var/MAT in being_built.materials)
var/datum/material/used_material = MAT
var/amount_needed = being_built.materials[MAT] * coeff * multiplier
if(istext(used_material)) //This means its a category
var/list/list_to_show = list()
for(var/i in SSmaterials.materials_by_category[used_material])
if(materials.materials[i] > 0)
list_to_show += i
used_material = input("Choose [used_material]", "Custom Material") as null|anything in list_to_show
if(!used_material)
return //Didn't pick any material, so you can't build shit either.
custom_materials[used_material] += amount_needed
materials_used[used_material] = amount_needed
if(materials.has_materials(materials_used))
busy = TRUE
use_power(power)
icon_state = "autolathe_n"
var/time = is_stack ? 32 : 32*coeff*multiplier
addtimer(CALLBACK(src, .proc/make_item, power, materials_used, custom_materials, multiplier, coeff, is_stack), time)
else
to_chat(usr, "<span class=\"alert\">Not enough materials for this operation.</span>")
if(href_list["search"])
matching_designs.Cut()
for(var/v in stored_research.researched_designs)
var/datum/design/D = SSresearch.techweb_design_by_id(v)
if(findtext(D.name,href_list["to_search"]))
matching_designs.Add(D)
updateUsrDialog()
else
to_chat(usr, "<span class=\"alert\">The autoylathe is busy. Please wait for completion of previous operation.</span>")
updateUsrDialog()
return
/obj/machinery/autoylathe/proc/make_item(power, list/materials_used, list/picked_materials, multiplier, coeff, is_stack)
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
var/atom/A = drop_location()
use_power(power)
materials.use_materials(materials_used)
if(is_stack)
var/obj/item/stack/N = new being_built.build_path(A, multiplier)
N.update_icon()
N.autoylathe_crafted(src)
else
for(var/i=1, i<=multiplier, i++)
var/obj/item/new_item = new being_built.build_path(A)
new_item.autoylathe_crafted(src)
if(length(picked_materials))
new_item.set_custom_materials(picked_materials, 1 / multiplier) //Ensure we get the non multiplied amount
icon_state = "autolathe"
busy = FALSE
updateDialog()
/obj/machinery/autoylathe/RefreshParts()
var/T = 0
for(var/obj/item/stock_parts/matter_bin/MB in component_parts)
T += MB.rating*75000
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
materials.max_amount = T
T=1.2
for(var/obj/item/stock_parts/manipulator/M in component_parts)
T -= M.rating*0.2
prod_coeff = clamp(T,1,0) // Coeff going 1 -> 0,8 -> 0,6 -> 0,4
/obj/machinery/autoylathe/proc/main_win(mob/user)
var/dat = "<div class='statusDisplay'><h3>Autoylathe Menu:</h3><br>"
dat += materials_printout()
dat += "<form name='search' action='?src=[REF(src)]'>\
<input type='hidden' name='src' value='[REF(src)]'>\
<input type='hidden' name='search' value='to_search'>\
<input type='hidden' name='menu' value='[AUTOYLATHE_SEARCH_MENU]'>\
<input type='text' name='to_search'>\
<input type='submit' value='Search'>\
</form><hr>"
var/line_length = 1
dat += "<table style='width:100%' align='center'><tr>"
for(var/C in categories)
if(line_length > 2)
dat += "</tr><tr>"
line_length = 1
dat += "<td><A href='?src=[REF(src)];category=[C];menu=[AUTOYLATHE_CATEGORY_MENU]'>[C]</A></td>"
line_length++
dat += "</tr></table></div>"
return dat
/obj/machinery/autoylathe/proc/category_win(mob/user,selected_category)
var/dat = "<A href='?src=[REF(src)];menu=[AUTOYLATHE_MAIN_MENU]'>Return to main menu</A>"
dat += "<div class='statusDisplay'><h3>Browsing [selected_category]:</h3><br>"
dat += materials_printout()
for(var/v in stored_research.researched_designs)
var/datum/design/D = SSresearch.techweb_design_by_id(v)
if(!(selected_category in D.category))
continue
if(disabled || !can_build(D))
dat += "<span class='linkOff'>[D.name]</span>"
else
dat += "<a href='?src=[REF(src)];make=[D.id];multiplier=1'>[D.name]</a>"
if(ispath(D.build_path, /obj/item/stack))
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
var/max_multiplier
for(var/datum/material/mat in D.materials)
max_multiplier = min(D.maxstack, round(materials.get_material_amount(mat)/D.materials[mat]))
if (max_multiplier>10 && !disabled)
dat += " <a href='?src=[REF(src)];make=[D.id];multiplier=10'>x10</a>"
if (max_multiplier>25 && !disabled)
dat += " <a href='?src=[REF(src)];make=[D.id];multiplier=25'>x25</a>"
if(max_multiplier > 0 && !disabled)
dat += " <a href='?src=[REF(src)];make=[D.id];multiplier=[max_multiplier]'>x[max_multiplier]</a>"
else
if(!disabled && can_build(D, 5))
dat += " <a href='?src=[REF(src)];make=[D.id];multiplier=5'>x5</a>"
if(!disabled && can_build(D, 10))
dat += " <a href='?src=[REF(src)];make=[D.id];multiplier=10'>x10</a>"
dat += "[get_design_cost(D)]<br>"
dat += "</div>"
return dat
/obj/machinery/autoylathe/proc/search_win(mob/user)
var/dat = "<A href='?src=[REF(src)];menu=[AUTOYLATHE_MAIN_MENU]'>Return to main menu</A>"
dat += "<div class='statusDisplay'><h3>Search results:</h3><br>"
dat += materials_printout()
for(var/v in matching_designs)
var/datum/design/D = v
if(disabled || !can_build(D))
dat += "<span class='linkOff'>[D.name]</span>"
else
dat += "<a href='?src=[REF(src)];make=[D.id];multiplier=1'>[D.name]</a>"
if(ispath(D.build_path, /obj/item/stack))
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
var/max_multiplier
for(var/datum/material/mat in D.materials)
max_multiplier = min(D.maxstack, round(materials.get_material_amount(mat)/D.materials[mat]))
if (max_multiplier>10 && !disabled)
dat += " <a href='?src=[REF(src)];make=[D.id];multiplier=10'>x10</a>"
if (max_multiplier>25 && !disabled)
dat += " <a href='?src=[REF(src)];make=[D.id];multiplier=25'>x25</a>"
if(max_multiplier > 0 && !disabled)
dat += " <a href='?src=[REF(src)];make=[D.id];multiplier=[max_multiplier]'>x[max_multiplier]</a>"
dat += "[get_design_cost(D)]<br>"
dat += "</div>"
return dat
/obj/machinery/autoylathe/proc/materials_printout()
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
var/dat = "<b>Total amount:</b> [materials.total_amount] / [materials.max_amount] cm<sup>3</sup><br>"
for(var/mat_id in materials.materials)
var/datum/material/M = mat_id
var/mineral_amount = materials.materials[mat_id]
if(mineral_amount > 0)
dat += "<b>[M.name] amount:</b> [mineral_amount] cm<sup>3</sup><br>"
return dat
/obj/machinery/autoylathe/proc/can_build(datum/design/D, amount = 1)
if(D.make_reagents.len)
return FALSE
var/coeff = (ispath(D.build_path, /obj/item/stack) ? 1 : prod_coeff)
var/list/required_materials = list()
for(var/i in D.materials)
required_materials[i] = D.materials[i] * coeff * amount
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
return materials.has_materials(required_materials)
/obj/machinery/autoylathe/proc/get_design_cost(datum/design/D)
var/coeff = (ispath(D.build_path, /obj/item/stack) ? 1 : prod_coeff)
var/dat
for(var/i in D.materials)
if(istext(i)) //Category handling
dat += "[D.materials[i] * coeff] [i]"
else
var/datum/material/M = i
dat += "[D.materials[i] * coeff] [M.name] "
return dat
/obj/machinery/autoylathe/proc/reset(wire)
switch(wire)
if(WIRE_HACK)
if(!wires.is_cut(wire))
adjust_hacked(FALSE)
if(WIRE_SHOCK)
if(!wires.is_cut(wire))
shocked = FALSE
if(WIRE_DISABLE)
if(!wires.is_cut(wire))
disabled = FALSE
/obj/machinery/autoylathe/proc/shock(mob/user, prb)
if(stat & (BROKEN|NOPOWER)) // unpowered, no shock
return FALSE
if(!prob(prb))
return FALSE
var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
s.set_up(5, 1, src)
s.start()
if (electrocute_mob(user, get_area(src), src, 0.7, TRUE))
return TRUE
else
return FALSE
/obj/machinery/autoylathe/proc/adjust_hacked(state)
hacked = state
for(var/id in SSresearch.techweb_designs)
var/datum/design/D = SSresearch.techweb_designs[id]
if((D.build_type & AUTOYLATHE) && ("hacked" in D.category))
if(hacked)
stored_research.add_design(D)
else
stored_research.remove_design(D)
/obj/machinery/autoylathe/hacked/Initialize()
. = ..()
adjust_hacked(TRUE)
//Called when the object is constructed by an autoylathe
//Has a reference to the autoylathe so you can do !!FUN!! things with hacked lathes
/obj/item/proc/autoylathe_crafted(obj/machinery/autoylathe/A)
return

View File

@@ -7,7 +7,7 @@
move_resist = INFINITY
obj_flags = 0
vis_flags = NONE
vis_flags = VIS_INHERIT_PLANE
/obj/effect/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir)
return

View File

@@ -49,5 +49,6 @@
/obj/effect/overlay/vis
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
anchored = TRUE
vis_flags = NONE
var/unused = 0 //When detected to be unused it gets set to world.time, after a while it gets removed
var/cache_expiration = 2 MINUTES // overlays which go unused for 2 minutes get cleaned up

View File

@@ -434,11 +434,12 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
// for items that can be placed in multiple slots
// note this isn't called during the initial dressing of a player
/obj/item/proc/equipped(mob/user, slot)
SEND_SIGNAL(src, COMSIG_ITEM_EQUIPPED, user, slot)
for(var/X in actions)
var/datum/action/A = X
if(item_action_slot_check(slot, user, A)) //some items only give their actions buttons when in a specific slot.
A.Grant(user)
. = SEND_SIGNAL(src, COMSIG_ITEM_EQUIPPED, user, slot)
if(!(. & COMPONENT_NO_GRANT_ACTIONS))
for(var/X in actions)
var/datum/action/A = X
if(item_action_slot_check(slot, user, A)) //some items only give their actions buttons when in a specific slot.
A.Grant(user)
item_flags |= IN_INVENTORY
user.update_equipment_speed_mods()

View File

@@ -258,6 +258,8 @@ update_label("John Doe", "Clowny")
chameleon_action.chameleon_type = /obj/item/card/id
chameleon_action.chameleon_name = "ID Card"
chameleon_action.initialize_disguises()
if(!anyone)
AddComponent(/datum/component/identification/syndicate, ID_COMPONENT_DEL_ON_IDENTIFY, ID_COMPONENT_EFFECT_NO_ACTIONS, NONE) //no deconstructive analyzer usage.
/obj/item/card/id/syndicate/afterattack(obj/item/O, mob/user, proximity)
if(!proximity)

View File

@@ -1054,9 +1054,9 @@
build_path = /obj/machinery/vending/kink
req_components = list(/obj/item/vending_refill/kink = 1)
/obj/item/circuitboard/machine/autoylathe
/obj/item/circuitboard/machine/autolathe/toy
name = "Autoylathe (Machine Board)"
build_path = /obj/machinery/autoylathe
build_path = /obj/machinery/autolathe/toy
req_components = list(
/obj/item/stock_parts/matter_bin = 3,
/obj/item/stock_parts/manipulator = 1,

View File

@@ -726,13 +726,12 @@
to_chat(usr, "<span class='warning'>A color that dark on an object like this? Surely not...</span>")
return FALSE
target.add_atom_colour(paint_color, WASHABLE_COLOUR_PRIORITY)
if(istype(target, /obj/structure/window))
if(color_hex2num(paint_color) < 255)
target.set_opacity(255)
else
target.set_opacity(initial(target.opacity))
var/obj/structure/window/W = target
W.spraycan_paint(paint_color)
else
target.add_atom_colour(paint_color, WASHABLE_COLOUR_PRIORITY)
. = use_charges(user, 2)
var/fraction = min(1, . / reagents.maximum_volume)

View File

@@ -0,0 +1,14 @@
/obj/item/electronics/electrochromatic_kit
name = "electrochromatic kit"
desc = "A kit for upgrading a window into an electrochromatic one."
/// Electrochromatic ID
var/id
/obj/item/electronics/electrochromatic_kit/attack_self(mob/user)
. = ..()
if(.)
return
var/new_id = input(user, "Set this kit's electrochromatic ID", "Set ID", id) as text|null
if(isnull(new_id))
return
id = new_id

View File

@@ -294,18 +294,18 @@
name = "chameleon kit"
/obj/item/storage/box/syndie_kit/chameleon/PopulateContents()
new /obj/item/clothing/under/chameleon(src)
new /obj/item/clothing/suit/chameleon(src)
new /obj/item/clothing/gloves/chameleon(src)
new /obj/item/clothing/shoes/chameleon(src)
new /obj/item/clothing/glasses/chameleon(src)
new /obj/item/clothing/head/chameleon(src)
new /obj/item/clothing/mask/chameleon(src)
new /obj/item/storage/backpack/chameleon(src)
new /obj/item/radio/headset/chameleon(src)
new /obj/item/stamp/chameleon(src)
new /obj/item/pda/chameleon(src)
new /obj/item/clothing/neck/cloak/chameleon(src)
new /obj/item/clothing/under/chameleon/syndicate(src)
new /obj/item/clothing/suit/chameleon/syndicate(src)
new /obj/item/clothing/gloves/chameleon/insulated/syndicate(src)
new /obj/item/clothing/shoes/chameleon/syndicate(src)
new /obj/item/clothing/glasses/chameleon/syndicate(src)
new /obj/item/clothing/head/chameleon/syndicate(src)
new /obj/item/clothing/mask/chameleon/syndicate(src)
new /obj/item/storage/backpack/chameleon/syndicate(src)
new /obj/item/radio/headset/chameleon/syndicate(src)
new /obj/item/stamp/chameleon/syndicate(src)
new /obj/item/pda/chameleon/syndicate(src)
new /obj/item/clothing/neck/cloak/chameleon/syndicate(src)
//5*(2*4) = 5*8 = 45, 45 damage if you hit one person with all 5 stars.
//Not counting the damage it will do while embedded (2*4 = 8, at 15% chance)
@@ -510,4 +510,4 @@
new /obj/item/clothing/under/chameleon(src)
new /obj/item/storage/fancy/cigarettes/cigpack_syndicate(src)
new /obj/item/lighter(src)

View File

@@ -3,6 +3,7 @@
var/crit_fail = FALSE
animate_movement = 2
speech_span = SPAN_ROBOT
vis_flags = VIS_INHERIT_PLANE //when this be added to vis_contents of something it inherit something.plane, important for visualisation of obj in openspace.
var/obj_flags = CAN_BE_HIT
var/set_obj_flags // ONLY FOR MAPPING: Sets flags from a string list, handled in Initialize. Usage: set_obj_flags = "EMAGGED;!CAN_BE_HIT" to set EMAGGED and clear CAN_BE_HIT.

View File

@@ -0,0 +1,66 @@
/*
* Loot piles structures, somewhat inspired from Polaris 13 ones but without the one search per pile ckey/mind restriction
* because the actual code is located its own element and has enough variables already. the piles themselves merely cosmetical.
*/
/obj/structure/loot_pile
name = "pile of junk"
desc = "Lots of junk lying around. They say one man's trash is another man's treasure."
icon = 'icons/obj/loot_piles.dmi'
icon_state = "randompile"
density = FALSE
anchored = TRUE
var/loot_amount = 5
var/delete_on_depletion = FALSE
var/can_use_hands = TRUE
var/scavenge_time = 12 SECONDS
var/allowed_tools = list(TOOL_SHOVEL = 0.6) //list of tool_behaviours with associated speed multipliers (lower is better)
var/icon_states_to_use = list("junk_pile1", "junk_pile2", "junk_pile3", "junk_pile4", "junk_pile5")
var/list/loot
/*
* Associated values in this list are not weights but numbers of times the kery can be rolled
* before being removed from ALL piles with same kind. This is why I wanted 'scavenging' to be an element and not a component.
*/
var/list/unique_loot
/*
* used for restrictions such as "one per mind", "one per ckey". Depending on the setting, these can be either limited to
* the current pile or shared throughout all atoms attached to this element.
*/
var/loot_restriction = NO_LOOT_RESTRICTION
var/maximum_loot_per_player = 1
/obj/structure/loot_pile/Initialize()
. = ..()
icon_state = pick(icon_states_to_use)
/obj/structure/loot_pile/ComponentInitialize()
. = ..()
if(loot)
AddElement(/datum/element/scavenging, loot_amount, loot, unique_loot, scavenge_time, can_use_hands, allowed_tools, null, delete_on_depletion, loot_restriction, maximum_loot_per_player)
//uses the maintenance_loot global list, mostly boring stuff and mices.
/obj/structure/loot_pile/maint
name = "trash pile"
desc = "A heap of garbage, but maybe there's something interesting inside?"
density = TRUE
layer = TABLE_LAYER
climbable = TRUE
pass_flags = LETPASSTHROW
loot = list(
SCAVENGING_FOUND_NOTHING = 50,
SCAVENGING_SPAWN_MOUSE = 10,
SCAVENGING_SPAWN_MICE = 5,
SCAVENGING_SPAWN_TOM = 1,
/obj/item/clothing/gloves/color/yellow = 0.5)
unique_loot = list(/obj/item/clothing/gloves/color/yellow = 5, SCAVENGING_SPAWN_TOM = 1)
/obj/structure/loot_pile/maint/ComponentInitialize()
var/static/safe_maint_items
if(!safe_maint_items)
safe_maint_items = list()
for(var/A in GLOB.maintenance_loot)
if(ispath(A, /obj/item))
safe_maint_items[A] = GLOB.maintenance_loot[A]
loot += safe_maint_items
return ..()

View File

@@ -1,3 +1,18 @@
#define NOT_ELECTROCHROMATIC 0
#define ELECTROCHROMATIC_OFF 1
#define ELECTROCHROMATIC_DIMMED 2
GLOBAL_LIST_EMPTY(electrochromatic_window_lookup)
/proc/do_electrochromatic_toggle(new_status, id)
var/list/windows = GLOB.electrochromatic_window_lookup["[id]"]
if(!windows)
return
var/obj/structure/window/W //define outside for performance because obviously this matters.
for(var/i in windows)
W = i
new_status? W.electrochromatic_dim() : W.electrochromatic_off()
/obj/structure/window
name = "window"
desc = "A window."
@@ -28,8 +43,15 @@
rad_insulation = RAD_VERY_LIGHT_INSULATION
rad_flags = RAD_PROTECT_CONTENTS
/// Electrochromatic status
var/electrochromatic_status = NOT_ELECTROCHROMATIC
/// Electrochromatic ID. Set the first character to ! to replace with a SSmapping generated pseudorandom obfuscated ID for mapping purposes.
var/electrochromatic_id
/obj/structure/window/examine(mob/user)
. = ..()
if(electrochromatic_status != NOT_ELECTROCHROMATIC)
. += "<span class='notice'>The window has electrochromatic circuitry on it.</span>"
if(reinf)
if(anchored && state == WINDOW_SCREWED_TO_FRAME)
. += "<span class='notice'>The window is <b>screwed</b> to the frame.</span>"
@@ -52,6 +74,10 @@
if(reinf && anchored)
state = WINDOW_SCREWED_TO_FRAME
if(mapload && electrochromatic_id)
if(copytext(electrochromatic_id, 1, 2) == "!")
electrochromatic_id = SSmapping.get_obfuscated_id(electrochromatic_id)
ini_dir = dir
air_update_turf(1)
@@ -62,6 +88,12 @@
real_explosion_block = explosion_block
explosion_block = EXPLOSION_BLOCK_PROC
if(electrochromatic_status != NOT_ELECTROCHROMATIC)
var/old = electrochromatic_status
make_electrochromatic()
if(old == ELECTROCHROMATIC_DIMMED)
electrochromatic_dim()
/obj/structure/window/ComponentInitialize()
. = ..()
AddComponent(/datum/component/simple_rotation,ROTATION_ALTCLICK | ROTATION_CLOCKWISE | ROTATION_COUNTERCLOCKWISE | ROTATION_VERBS ,null,CALLBACK(src, .proc/can_be_rotated),CALLBACK(src,.proc/after_rotation))
@@ -177,6 +209,24 @@
to_chat(user, "<span class='warning'>[src] is already in good condition!</span>")
return
if(istype(I, /obj/item/electronics/electrochromatic_kit) && user.a_intent == INTENT_HELP)
var/obj/item/electronics/electrochromatic_kit/K = I
if(electrochromatic_status != NOT_ELECTROCHROMATIC)
to_chat(user, "<span class='warning'>[src] is already electrochromatic!</span>")
return
if(anchored)
to_chat(user, "<span class='warning'>[src] must not be attached to the floor!</span>")
return
if(!K.id)
to_chat(user, "<span class='warning'>[K] has no ID set!</span>")
return
if(!user.temporarilyRemoveItemFromInventory(K))
to_chat(user, "<span class='warning'>[K] is stuck to your hand!</span>")
return
user.visible_message("<span class='notice'>[user] upgrades [src] with [I].</span>", "<span class='notice'>You upgrade [src] with [I].</span>")
make_electrochromatic(K.id)
qdel(K)
if(!(flags_1&NODECONSTRUCT_1))
if(istype(I, /obj/item/screwdriver))
I.play_tool_sound(src, 75)
@@ -224,6 +274,91 @@
air_update_turf(TRUE)
update_nearby_icons()
/obj/structure/window/proc/spraycan_paint(paint_color)
if(color_hex2num(paint_color) < 255)
set_opacity(255)
else
set_opacity(initial(opacity))
add_atom_colour(paint_color, WASHABLE_COLOUR_PRIORITY)
/obj/structure/window/proc/electrochromatic_dim()
if(electrochromatic_status == ELECTROCHROMATIC_DIMMED)
return
electrochromatic_status = ELECTROCHROMATIC_DIMMED
animate(src, color = "#222222", time = 2)
set_opacity(TRUE)
/obj/structure/window/proc/electrochromatic_off()
if(electrochromatic_status == ELECTROCHROMATIC_OFF)
return
electrochromatic_status = ELECTROCHROMATIC_OFF
var/current = color
update_atom_colour()
var/newcolor = color
color = current
animate(src, color = newcolor, time = 2)
/obj/structure/window/proc/remove_electrochromatic()
electrochromatic_off()
electrochromatic_status = NOT_ELECTROCHROMATIC
if(!electrochromatic_id)
return
var/list/L = GLOB.electrochromatic_window_lookup["[electrochromatic_id]"]
if(L)
L -= src
electrochromatic_id = null
/obj/structure/window/vv_edit_var(var_name, var_value)
var/check_status
if(var_name == NAMEOF(src, electrochromatic_id))
if(electrochromatic_id && GLOB.electrochromatic_window_lookup["[electrochromatic_id]"])
GLOB.electrochromatic_window_lookup[electrochromatic_id] -= src
if(var_name == NAMEOF(src, electrochromatic_status))
check_status = TRUE
. = ..() //do this first incase it runtimes.
if(var_name == NAMEOF(src, electrochromatic_id))
if((electrochromatic_status != NOT_ELECTROCHROMATIC) && electrochromatic_id)
LAZYINITLIST(GLOB.electrochromatic_window_lookup[electrochromatic_id])
GLOB.electrochromatic_window_lookup[electrochromatic_id] += src
if(check_status)
if(electrochromatic_status == NOT_ELECTROCHROMATIC)
remove_electrochromatic()
return
else if(electrochromatic_status == ELECTROCHROMATIC_OFF)
if(!electrochromatic_id)
return
else
make_electrochromatic()
electrochromatic_off()
return
else if(electrochromatic_status == ELECTROCHROMATIC_DIMMED)
if(!electrochromatic_id)
return
else
make_electrochromatic()
electrochromatic_dim()
return
else
remove_electrochromatic()
/obj/structure/window/proc/make_electrochromatic(new_id = electrochromatic_id)
remove_electrochromatic()
if(!new_id)
CRASH("Attempted to make electrochromatic with null ID.")
electrochromatic_id = new_id
electrochromatic_status = ELECTROCHROMATIC_OFF
LAZYINITLIST(GLOB.electrochromatic_window_lookup["[electrochromatic_id]"])
GLOB.electrochromatic_window_lookup[electrochromatic_id] |= src
/obj/structure/window/update_atom_colour()
if((electrochromatic_status != ELECTROCHROMATIC_OFF) && (electrochromatic_status != ELECTROCHROMATIC_DIMMED))
return FALSE
. = ..()
if(color && (color_hex2num(color) < 255))
set_opacity(255)
else
set_opacity(FALSE)
/obj/structure/window/proc/check_state(checked_state)
if(state == checked_state)
return TRUE
@@ -263,7 +398,6 @@
if(BURN)
playsound(src, 'sound/items/Welder.ogg', 100, 1)
/obj/structure/window/deconstruct(disassembled = TRUE)
if(QDELETED(src))
return
@@ -272,6 +406,9 @@
if(!(flags_1 & NODECONSTRUCT_1))
for(var/obj/item/shard/debris in spawnDebris(drop_location()))
transfer_fingerprints_to(debris) // transfer fingerprints to shards only
if(electrochromatic_status != NOT_ELECTROCHROMATIC) //eh fine keep your kit.
new /obj/item/electronics/electrochromatic_kit(drop_location())
// Intentionally not setting the ID so you can't decon one to know all of the IDs.
qdel(src)
update_nearby_icons()
@@ -315,9 +452,9 @@
density = FALSE
air_update_turf(1)
update_nearby_icons()
remove_electrochromatic()
return ..()
/obj/structure/window/Move()
var/turf/T = loc
. = ..()
@@ -731,7 +868,6 @@
set_opacity(TRUE)
queue_smooth(src)
/obj/structure/window/paperframe/attackby(obj/item/W, mob/user)
if(W.get_temperature())
fire_act(W.get_temperature())
@@ -749,3 +885,7 @@
return
..()
update_icon()
#undef NOT_ELECTROCHROMATIC
#undef ELECTROCHROMATIC_OFF
#undef ELECTROCHROMATIC_DIMMED

View File

@@ -1,7 +1,20 @@
GLOBAL_DATUM_INIT(openspace_backdrop_one_for_all, /atom/movable/openspace_backdrop, new)
/atom/movable/openspace_backdrop
name = "openspace_backdrop"
anchored = TRUE
icon = 'icons/turf/floors.dmi'
icon_state = "grey"
plane = OPENSPACE_BACKDROP_PLANE
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
layer = SPLASHSCREEN_LAYER
/turf/open/openspace
name = "open space"
desc = "Watch your step!"
icon_state = "grey"
icon_state = "transparent"
baseturfs = /turf/open/openspace
CanAtmosPassVertical = ATMOS_PASS_YES
//mouse_opacity = MOUSE_OPACITY_TRANSPARENT
@@ -14,8 +27,9 @@
/turf/open/openspace/Initialize() // handle plane and layer here so that they don't cover other obs/turfs in Dream Maker
. = ..()
plane = FLOOR_OPENSPACE_PLANE
plane = OPENSPACE_PLANE
layer = OPENSPACE_LAYER
vis_contents += GLOB.openspace_backdrop_one_for_all //Special grey square for projecting backdrop darkness filter on it.
return INITIALIZE_HINT_LATELOAD
/turf/open/openspace/LateInitialize()

View File

@@ -1,6 +1,8 @@
/turf
icon = 'icons/turf/floors.dmi'
level = 1
vis_flags = VIS_INHERIT_PLANE|VIS_INHERIT_ID //when this be added to vis_contents of something it inherit something.plane and be associatet with something on clicking,
//important for visualisation of turf in openspace and interraction with openspace that show you turf.
var/intact = 1

View File

@@ -81,7 +81,7 @@ GLOBAL_PROTECT(admin_verbs_admin)
)
GLOBAL_LIST_INIT(admin_verbs_ban, list(/client/proc/unban_panel, /client/proc/DB_ban_panel, /client/proc/stickybanpanel))
GLOBAL_PROTECT(admin_verbs_ban)
GLOBAL_LIST_INIT(admin_verbs_sounds, list(/client/proc/play_local_sound, /client/proc/play_sound, /client/proc/set_round_end_sound))
GLOBAL_LIST_INIT(admin_verbs_sounds, list(/client/proc/play_local_sound, /client/proc/play_sound, /client/proc/manual_play_web_sound, /client/proc/set_round_end_sound))
GLOBAL_PROTECT(admin_verbs_sounds)
GLOBAL_LIST_INIT(admin_verbs_fun, list(
/client/proc/cmd_admin_dress,

View File

@@ -138,6 +138,49 @@
SSblackbox.record_feedback("tally", "admin_verb", 1, "Play Internet Sound")
/client/proc/manual_play_web_sound()
set category = "Fun"
set name = "Manual Play Internet Sound"
if(!check_rights(R_SOUNDS))
return
var/web_sound_input = input("Enter content stream URL (fetch this from local youtube-dl!)", "Play Internet Sound via direct URL") as text|null
if(istext(web_sound_input))
if(!length(web_sound_input))
log_admin("[key_name(src)] stopped web sound")
message_admins("[key_name(src)] stopped web sound")
var/mob/M
for(var/i in GLOB.player_list)
M = i
M?.client?.chatOutput?.stopMusic()
return
else
if(web_sound_input && !findtext(web_sound_input, GLOB.is_http_protocol))
to_chat(src, "<span class='boldwarning'>BLOCKED: Content URL not using http(s) protocol</span>")
return
var/freq = input(usr, "What frequency would you like the sound to play at?",, 1) as null|num
if(isnull(freq))
return
if(!freq)
freq = 1
SSblackbox.record_feedback("nested tally", "played_url", 1, list("[ckey]", "[web_sound_input]"))
var/logstr = "[key_name(src)] played web sound at freq [freq]: [web_sound_input]"
log_admin(logstr)
message_admins(logstr)
var/mob/M
var/client/C
var/datum/chatOutput/O
for(var/i in GLOB.player_list)
M = i
C = M.client
if(!(C?.prefs?.toggles & SOUND_MIDI))
continue
O = C.chatOutput
if(!O || O.broken || !O.loaded)
continue
O.sendMusic(web_sound_input, freq)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Manual Play Internet Sound")
/client/proc/set_round_end_sound(S as sound)
set category = "Fun"
set name = "Set Round End Sound"

View File

@@ -42,7 +42,7 @@
if(objective.completable)
var/completion = objective.check_completion()
if(completion >= 1)
report += "<B>Objective #[objective_count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</span>"
report += "<B>Objective #[objective_count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</B></span>"
else if(completion <= 0)
report += "<B>Objective #[objective_count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
win = FALSE

View File

@@ -14,10 +14,10 @@
set waitfor = FALSE // Don't make on_gain() wait for this function to finish. This lets this code run on the side.
var/notice_healing
while(owner && !AmFinalDeath()) // owner.has_antag_datum(ANTAG_DATUM_BLOODSUCKER) == src
if(owner.current.stat == CONSCIOUS && !poweron_feed && !HAS_TRAIT(owner.current, TRAIT_DEATHCOMA)) // Deduct Blood
if(owner.current.stat == CONSCIOUS && !poweron_feed && !HAS_TRAIT(owner.current, TRAIT_FAKEDEATH)) // Deduct Blood
AddBloodVolume(passive_blood_drain) // -.1 currently
if(HandleHealing(1)) // Heal
if(notice_healing == FALSE && owner.current.blood_volume > 0)
if(!notice_healing && owner.current.blood_volume > 0)
to_chat(owner, "<span class='notice'>The power of your blood begins knitting your wounds...</span>")
notice_healing = TRUE
else if(notice_healing == TRUE)
@@ -25,7 +25,7 @@
HandleStarving() // Death
HandleDeath() // Standard Update
update_hud()// Daytime Sleep in Coffin
if(SSticker.mode.is_daylight() && !HAS_TRAIT_FROM(owner.current, TRAIT_DEATHCOMA, "bloodsucker"))
if(SSticker.mode.is_daylight() && !HAS_TRAIT_FROM(owner.current, TRAIT_FAKEDEATH, "bloodsucker"))
if(istype(owner.current.loc, /obj/structure/closet/crate/coffin))
Torpor_Begin()
// Wait before next pass
@@ -83,7 +83,7 @@
// NOTE: Mult of 0 is just a TEST to see if we are injured and need to go into Torpor!
//It is called from your coffin on close (by you only)
var/actual_regen = regen_rate + additional_regen
if(poweron_masquerade == TRUE || owner.current.AmStaked())
if(poweron_masquerade|| owner.current.AmStaked())
return FALSE
if(owner.current.reagents.has_reagent(/datum/reagent/consumable/garlic))
return FALSE
@@ -101,8 +101,8 @@
var/mob/living/carbon/C = owner.current
var/costMult = 1 // Coffin makes it cheaper
var/fireheal = 0 // BURN: Heal in Coffin while Fakedeath, or when damage above maxhealth (you can never fully heal fire)
var/amInCoffinWhileTorpor = istype(C.loc, /obj/structure/closet/crate/coffin) && (mult == 0 || HAS_TRAIT(C, TRAIT_FAKEDEATH)) // Check for mult 0 OR death coma. (mult 0 means we're testing from coffin)
if(amInCoffinWhileTorpor)
// Check for mult 0 OR death coma. (mult 0 means we're testing from coffin)
if(istype(C.loc, /obj/structure/closet/crate/coffin) && (mult == 0 || HAS_TRAIT(C, TRAIT_FAKEDEATH)))
mult *= 4 // Increase multiplier if we're sleeping in a coffin.
fireheal = min(C.getFireLoss(), regen_rate) // NOTE: Burn damage ONLY heals in torpor.
C.ExtinguishMob()
@@ -112,6 +112,9 @@
CheckVampOrgans() // Heart, Eyes
if(check_limbs(costMult))
return TRUE
else if(owner.current.stat >= UNCONSCIOUS) //Faster regeneration and slight burn healing while unconcious
mult *= 2
fireheal = min(C.getFireLoss(), regen_rate * 0.2)
// BRUTE: Always Heal
var/bruteheal = min(C.getBruteLoss(), actual_regen)
@@ -120,8 +123,6 @@
if(bruteheal + fireheal + toxinheal > 0) // Just a check? Don't heal/spend, and return.
if(mult == 0)
return TRUE
if(owner.current.stat >= UNCONSCIOUS) //Faster regeneration while unconcious, so you dont have to wait all day
mult *= 2
// We have damage. Let's heal (one time)
C.adjustBruteLoss(-bruteheal * mult, forced = TRUE)// Heal BRUTE / BURN in random portions throughout the body.
C.adjustFireLoss(-fireheal * mult, forced = TRUE)
@@ -146,12 +147,6 @@
to_chat(C, "<span class='notice'>Your flesh knits as it regrows your [L]!</span>")
playsound(C, 'sound/magic/demon_consume.ogg', 50, TRUE)
return TRUE
/*for(var/obj/item/bodypart/BP in C.bodyparts)
if(!istype(BP) && !BP.status == 2)
return FALSE
to_chat(C, "<span class='notice'>Your body expels the [BP]!</span>")
BP.drop_limb()
return TRUE */
/datum/antagonist/bloodsucker/proc/CureDisabilities()
var/mob/living/carbon/C = owner.current
@@ -176,7 +171,7 @@
// EMPTY: Frenzy!
// BLOOD_VOLUME_GOOD: [336] Pale (handled in bloodsucker_integration.dm
// BLOOD_VOLUME_BAD: [224] Jitter
if(owner.current.blood_volume < BLOOD_VOLUME_BAD && !prob(0.5))
if(owner.current.blood_volume < BLOOD_VOLUME_BAD && !prob(0.5 && HAS_TRAIT(owner, TRAIT_FAKEDEATH)) && !poweron_masquerade)
owner.current.Jitter(3)
// BLOOD_VOLUME_SURVIVE: [122] Blur Vision
if(owner.current.blood_volume < BLOOD_VOLUME_BAD / 2)
@@ -230,16 +225,16 @@
Torpor_Begin()
to_chat(owner, "<span class='danger'>Your immortal body will not yet relinquish your soul to the abyss. You enter Torpor.</span>")
sleep(30) //To avoid spam
if(poweron_masquerade == TRUE)
if(poweron_masquerade)
to_chat(owner, "<span class='warning'>Your wounds will not heal until you disable the <span class='boldnotice'>Masquerade</span> power.</span>")
// End Torpor:
else // No damage, OR toxin healed AND brute healed and NOT in coffin (since you cannot heal burn)
if(total_damage <= 0 || total_toxloss <= 0 && total_brute <= 0 && !istype(owner.current.loc, /obj/structure/closet/crate/coffin))
// Not Daytime, Not in Torpor
if(!SSticker.mode.is_daylight() && HAS_TRAIT_FROM(owner.current, TRAIT_FAKEDEATH, "bloodsucker"))
// Not Daytime, Not in Torpor, enough health to not die the moment you end torpor
if(!SSticker.mode.is_daylight() && HAS_TRAIT_FROM(owner.current, TRAIT_FAKEDEATH, "bloodsucker") && total_damage < owner.current.getMaxHealth())
Torpor_End()
// Fake Unconscious
if(poweron_masquerade == TRUE && total_damage >= owner.current.getMaxHealth() - HEALTH_THRESHOLD_FULLCRIT)
if(poweron_masquerade && total_damage >= owner.current.getMaxHealth() - HEALTH_THRESHOLD_FULLCRIT)
owner.current.Unconscious(20, 1)
/datum/antagonist/bloodsucker/proc/Torpor_Begin(amInCoffin = FALSE)
@@ -249,6 +244,7 @@
ADD_TRAIT(owner.current, TRAIT_NODEATH, "bloodsucker") // Without this, you'll just keep dying while you recover.
ADD_TRAIT(owner.current, TRAIT_RESISTHIGHPRESSURE, "bloodsucker") // So you can heal in space. Otherwise you just...heal forever.
ADD_TRAIT(owner.current, TRAIT_RESISTLOWPRESSURE, "bloodsucker")
owner.current.Jitter(0)
// Visuals
owner.current.update_sight()
owner.current.reload_fullscreen()
@@ -256,6 +252,9 @@
for(var/datum/action/bloodsucker/power in powers)
if(power.active && !power.can_use_in_torpor)
power.DeactivatePower()
if(owner.current.suiciding)
owner.current.suiciding = FALSE //Youll die but not for long.
to_chat(owner.current, "<span class='warning'>Your body keeps you going, even as you try to end yourself.</span>")
/datum/antagonist/bloodsucker/proc/Torpor_End()
owner.current.stat = SOFT_CRIT

View File

@@ -45,15 +45,15 @@
// (FINAL LIL WARNING)
while(time_til_cycle > 5)
sleep(10)
if (cancel_me)
if(cancel_me)
return
//sleep(TIME_BLOODSUCKER_DAY_FINAL_WARN - 50)
warn_daylight(3,"<span class = 'userdanger'>Seek cover, for Sol rises!</span>")
// Part 3: Night Ending
while (time_til_cycle > 0)
while(time_til_cycle > 0)
sleep(10)
if (cancel_me)
if(cancel_me)
return
//sleep(50)
warn_daylight(4,"<span class = 'userdanger'>Solar flares bombard the station with deadly UV light!</span><br><span class = ''>Stay in cover for the next [TIME_BLOODSUCKER_DAY / 60] minutes or risk Final Death!</span>",\
@@ -69,11 +69,11 @@
while(time_til_cycle > 0)
punish_vamps()
sleep(TIME_BLOODSUCKER_BURN_INTERVAL)
if (cancel_me)
if(cancel_me)
return
//daylight_time -= TIME_BLOODSUCKER_BURN_INTERVAL
// Issue Level Up!
if(!issued_XP && time_til_cycle <= 15)
if(!issued_XP && time_til_cycle <= 5)
issued_XP = TRUE
vamps_rank_up()

View File

@@ -227,7 +227,7 @@
// Traits
for(var/T in defaultTraits)
REMOVE_TRAIT(owner.current, T, BLOODSUCKER_TRAIT)
if(had_toxlover == TRUE)
if(had_toxlover)
ADD_TRAIT(owner.current, TRAIT_TOXINLOVER, SPECIES_TRAIT)
// Traits: Species

View File

@@ -18,14 +18,11 @@
. = ..()
if(!.)
return
// must have nobody around to see the cloak
var/watchers = viewers(9,get_turf(owner))
for(var/mob/living/M in watchers)
for(var/mob/living/M in viewers(9, owner))
if(M != owner)
to_chat(owner, "<span class='warning'>You may only vanish into the shadows unseen.</span>")
return FALSE
return TRUE
/datum/action/bloodsucker/cloak/ActivatePower()

View File

@@ -52,6 +52,7 @@
REMOVE_TRAIT(user, TRAIT_NOHARDCRIT, "bloodsucker")
REMOVE_TRAIT(user, TRAIT_NOSOFTCRIT, "bloodsucker")
REMOVE_TRAIT(user, TRAIT_VIRUSIMMUNE, "bloodsucker")
REMOVE_TRAIT(user, TRAIT_NOBREATH, "bloodsucker")
var/obj/item/organ/heart/vampheart/H = user.getorganslot(ORGAN_SLOT_HEART)
var/obj/item/organ/eyes/vassal/bloodsucker/E = user.getorganslot(ORGAN_SLOT_EYES)
E.flash_protect = 0
@@ -93,6 +94,7 @@
ADD_TRAIT(user, TRAIT_NOHARDCRIT, "bloodsucker")
ADD_TRAIT(user, TRAIT_NOSOFTCRIT, "bloodsucker")
ADD_TRAIT(user, TRAIT_VIRUSIMMUNE, "bloodsucker")
ADD_TRAIT(user, TRAIT_NOBREATH, "bloodsucker")
// HEART
var/obj/item/organ/heart/H = user.getorganslot(ORGAN_SLOT_HEART)

View File

@@ -28,7 +28,7 @@
/datum/action/bloodsucker/targeted/trespass/CheckValidTarget(atom/A)
// Can't target my tile
if (A == get_turf(owner) || get_turf(A) == get_turf(owner))
if(A == get_turf(owner) || get_turf(A) == get_turf(owner))
return FALSE
return TRUE // All we care about is destination. Anything you click is fine.
@@ -43,13 +43,13 @@
// Are either tiles WALLS?
var/turf/from_turf = get_turf(owner)
var/this_dir // = get_dir(from_turf, target_turf)
for (var/i=1 to 2)
for(var/i=1 to 2)
// Keep Prev Direction if we've reached final turf
if (from_turf != final_turf)
if(from_turf != final_turf)
this_dir = get_dir(from_turf, final_turf) // Recalculate dir so we don't overshoot on a diagonal.
from_turf = get_step(from_turf, this_dir)
// ERROR! Wall!
if (iswallturf(from_turf))
if(iswallturf(from_turf))
if (display_error)
var/wallwarning = (i == 1) ? "in the way" : "at your destination"
to_chat(owner, "<span class='warning'>There is a solid wall [wallwarning].</span>")
@@ -84,7 +84,7 @@
user.next_move = world.time + mist_delay
user.Stun(mist_delay, ignore_canstun = TRUE)
user.notransform = TRUE
user.density = 0
user.density = FALSE
var/invis_was = user.invisibility
user.invisibility = INVISIBILITY_MAXIMUM
@@ -94,7 +94,7 @@
sleep(mist_delay / 2)
// Move & Freeze
if (isturf(target_turf))
if(isturf(target_turf))
do_teleport(owner, target_turf, no_effects=TRUE, channel = TELEPORT_CHANNEL_QUANTUM) // in teleport.dm?
user.next_move = world.time + mist_delay / 2
user.Stun(mist_delay / 2, ignore_canstun = TRUE)

View File

@@ -111,7 +111,7 @@
if(objective.completable)
var/completion = objective.check_completion()
if(completion >= 1)
parts += "<B>Objective #[objective_count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</span>"
parts += "<B>Objective #[objective_count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</B></span>"
else if(completion <= 0)
parts += "<B>Objective #[objective_count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
win = FALSE

View File

@@ -563,7 +563,7 @@
if(objective.completable)
var/completion = objective.check_completion()
if(completion >= 1)
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</span>"
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</B></span>"
else if(completion <= 0)
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
changelingwin = FALSE

View File

@@ -441,7 +441,7 @@
if(objective.completable)
var/completion = objective.check_completion()
if(completion >= 1)
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</span>"
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</B></span>"
else if(completion <= 0)
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
else

View File

@@ -55,7 +55,7 @@
if(objective.completable)
var/completion = objective.check_completion()
if(completion >= 1)
result += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</span>"
result += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</B></span>"
else if(completion <= 0)
result += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
win = FALSE

View File

@@ -239,7 +239,7 @@
if(objective.completable)
var/completion = objective.check_completion()
if(completion >= 1)
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</span>"
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</B></span>"
else if(completion <= 0)
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
traitorwin = FALSE

View File

@@ -269,7 +269,7 @@
if(objective.completable)
var/completion = objective.check_completion()
if(completion >= 1)
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</span>"
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</B></span>"
else if(completion <= 0)
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
wizardwin = FALSE

View File

@@ -3,14 +3,35 @@
desc = "A small electronic device able to control a blast door remotely."
icon_state = "control"
attachable = TRUE
var/id = null
var/can_change_id = 0
/// Our ID. Make the first character ! if you want to obfuscate it as a mapper via randomization.
var/id
/// Can the ID be changed if used in hand?
var/can_change_id = FALSE
/// Show ID?
var/show_id = TRUE
var/cooldown = FALSE //Door cooldowns
/obj/item/assembly/control/Initialize(mapload)
if(mapload && id)
if(copytext(id, 1, 2) == "!")
id = SSmapping.get_obfuscated_id(id)
return ..()
/obj/item/assembly/control/examine(mob/user)
. = ..()
if(id)
if(id && show_id)
. += "<span class='notice'>Its channel ID is '[id]'.</span>"
if(can_change_id)
. += "<span class='notice'>Use in hand to change ID.</span>"
/obj/item/assembly/control/attack_self(mob/living/user)
. = ..()
if(!can_change_id)
return
var/new_id
new_id = input(user, "Set ID", "Set ID", show_id? id : null) as text|null
if(!isnull(new_id)) //0/"" is considered !, so check null instead of just !.
id = new_id
/obj/item/assembly/control/activate()
cooldown = TRUE
@@ -22,7 +43,6 @@
INVOKE_ASYNC(M, openclose ? /obj/machinery/door/poddoor.proc/open : /obj/machinery/door/poddoor.proc/close)
addtimer(VARSET_CALLBACK(src, cooldown, FALSE), 10)
/obj/item/assembly/control/airlock
name = "airlock controller"
desc = "A small electronic device able to control an airlock remotely."
@@ -123,7 +143,6 @@
addtimer(VARSET_CALLBACK(src, cooldown, FALSE), 50)
/obj/item/assembly/control/crematorium
name = "crematorium controller"
desc = "An evil-looking remote controller for a crematorium."
@@ -135,3 +154,14 @@
C.cremate(usr)
addtimer(VARSET_CALLBACK(src, cooldown, FALSE), 50)
/obj/item/assembly/control/electrochromatic
name = "electrochromatic window controller"
desc = "Toggles linked electrochromatic windows."
can_change_id = TRUE
/// Stores our status to prevent windows from desyncing.
var/on = FALSE
/obj/item/assembly/control/electrochromatic/activate()
on = !on
do_electrochromatic_toggle(on, id)

View File

@@ -192,6 +192,7 @@ GLOBAL_LIST_INIT(nonreactive_gases, typecacheof(list(/datum/gas/oxygen, /datum/g
anchored = TRUE // should only appear in vis_contents, but to be safe
layer = FLY_LAYER
appearance_flags = TILE_BOUND
vis_flags = NONE
/obj/effect/overlay/gas/New(state, alph)
. = ..()

View File

@@ -225,6 +225,7 @@
if(!istype(user) || !user.canUseTopic(src, BE_CLOSE))
return
target_temperature = min_temperature
to_chat(user,"<span class='notice'>You minimize the temperature on the [src].</span>")
investigate_log("was set to [target_temperature] K by [key_name(usr)]", INVESTIGATE_ATMOS)
message_admins("[src.name] was minimized by [ADMIN_LOOKUPFLW(usr)] at [ADMIN_COORDJMP(T)], [A]")
return TRUE
@@ -257,6 +258,7 @@
if(!istype(user) || !user.canUseTopic(src, BE_CLOSE))
return
target_temperature = max_temperature
to_chat(user,"<span class='notice'>You maximize the temperature on the [src].</span>")
investigate_log("was set to [target_temperature] K by [key_name(usr)]", INVESTIGATE_ATMOS)
message_admins("[src.name] was maximized by [ADMIN_LOOKUPFLW(usr)] at [ADMIN_COORDJMP(T)], [A]")
return TRUE

View File

@@ -262,7 +262,15 @@
return
random_look(owner)
/obj/item/clothing/under/chameleon
// Forgive me for my sins...
#define CHAMELEON_CLOTHING_DEFINE(path) \
##path/syndicate/Initialize(mapload){\
. = ..();\
AddComponent(/datum/component/identification/syndicate, ID_COMPONENT_DEL_ON_IDENTIFY, ID_COMPONENT_EFFECT_NO_ACTIONS, ID_COMPONENT_IDENTIFY_WITH_DECONSTRUCTOR);\
}\
##path
CHAMELEON_CLOTHING_DEFINE(/obj/item/clothing/under/chameleon)
//starts off as black
name = "black jumpsuit"
icon_state = "black"
@@ -300,7 +308,7 @@
. = ..()
chameleon_action.emp_randomise(INFINITY)
/obj/item/clothing/suit/chameleon
CHAMELEON_CLOTHING_DEFINE(/obj/item/clothing/suit/chameleon)
name = "armor"
desc = "A slim armored vest that protects against most types of damage."
icon_state = "armor"
@@ -329,7 +337,7 @@
. = ..()
chameleon_action.emp_randomise(INFINITY)
/obj/item/clothing/glasses/chameleon
CHAMELEON_CLOTHING_DEFINE(/obj/item/clothing/glasses/chameleon)
name = "Optical Meson Scanner"
desc = "Used by engineering and mining staff to see basic structural and terrain layouts through walls, regardless of lighting condition."
icon_state = "meson"
@@ -357,7 +365,7 @@
. = ..()
chameleon_action.emp_randomise(INFINITY)
/obj/item/clothing/gloves/chameleon
CHAMELEON_CLOTHING_DEFINE(/obj/item/clothing/gloves/chameleon)
desc = "These gloves will protect the wearer from electric shock."
name = "insulated gloves"
icon_state = "yellow"
@@ -368,6 +376,9 @@
var/datum/action/item_action/chameleon/change/chameleon_action
CHAMELEON_CLOTHING_DEFINE(/obj/item/clothing/gloves/chameleon/insulated)
siemens_coefficient = 0
/obj/item/clothing/gloves/chameleon/Initialize()
. = ..()
chameleon_action = new(src)
@@ -386,7 +397,7 @@
. = ..()
chameleon_action.emp_randomise(INFINITY)
/obj/item/clothing/head/chameleon
CHAMELEON_CLOTHING_DEFINE(/obj/item/clothing/head/chameleon)
name = "grey cap"
desc = "It's a baseball hat in a tasteful grey colour."
icon_state = "greysoft"
@@ -429,7 +440,7 @@
var/datum/action/item_action/chameleon/drone/randomise/randomise_action = new(src)
randomise_action.UpdateButtonIcon()
/obj/item/clothing/mask/chameleon
CHAMELEON_CLOTHING_DEFINE(/obj/item/clothing/mask/chameleon)
name = "gas mask"
desc = "A face-covering mask that can be connected to an air supply. While good for concealing your identity, it isn't good for blocking gas flow." //More accurate
icon_state = "gas_alt"
@@ -486,7 +497,7 @@
/obj/item/clothing/mask/chameleon/drone/attack_self(mob/user)
to_chat(user, "<span class='notice'>[src] does not have a voice changer.</span>")
/obj/item/clothing/shoes/chameleon
CHAMELEON_CLOTHING_DEFINE(/obj/item/clothing/shoes/chameleon)
name = "black shoes"
icon_state = "black"
desc = "A pair of black shoes."
@@ -511,7 +522,7 @@
return
chameleon_action.emp_randomise()
/obj/item/clothing/shoes/chameleon/noslip
CHAMELEON_CLOTHING_DEFINE(/obj/item/clothing/shoes/chameleon/noslip)
name = "black shoes"
icon_state = "black"
desc = "A pair of black shoes."
@@ -521,7 +532,7 @@
. = ..()
chameleon_action.emp_randomise(INFINITY)
/obj/item/storage/backpack/chameleon
CHAMELEON_CLOTHING_DEFINE(/obj/item/storage/backpack/chameleon)
name = "backpack"
var/datum/action/item_action/chameleon/change/chameleon_action
@@ -542,7 +553,7 @@
. = ..()
chameleon_action.emp_randomise(INFINITY)
/obj/item/storage/belt/chameleon
CHAMELEON_CLOTHING_DEFINE(/obj/item/storage/belt/chameleon)
name = "toolbelt"
desc = "Holds tools."
var/datum/action/item_action/chameleon/change/chameleon_action
@@ -570,7 +581,7 @@
. = ..()
chameleon_action.emp_randomise(INFINITY)
/obj/item/radio/headset/chameleon
CHAMELEON_CLOTHING_DEFINE(/obj/item/radio/headset/chameleon)
name = "radio headset"
var/datum/action/item_action/chameleon/change/chameleon_action
@@ -591,7 +602,7 @@
. = ..()
chameleon_action.emp_randomise(INFINITY)
/obj/item/pda/chameleon
CHAMELEON_CLOTHING_DEFINE(/obj/item/pda/chameleon)
name = "PDA"
var/datum/action/item_action/chameleon/change/pda/chameleon_action
@@ -613,7 +624,7 @@
. = ..()
chameleon_action.emp_randomise(INFINITY)
/obj/item/stamp/chameleon
CHAMELEON_CLOTHING_DEFINE(/obj/item/stamp/chameleon)
var/datum/action/item_action/chameleon/change/chameleon_action
/obj/item/stamp/chameleon/Initialize()
@@ -627,7 +638,7 @@
. = ..()
chameleon_action.emp_randomise(INFINITY)
/obj/item/clothing/neck/cloak/chameleon
CHAMELEON_CLOTHING_DEFINE(/obj/item/clothing/neck/cloak/chameleon)
name = "black tie"
desc = "A neosilk clip-on tie."
icon = 'icons/obj/clothing/neck.dmi'

View File

@@ -23,6 +23,9 @@
/datum/round_event/wormholes/start()
for(var/turf/open/floor/T in world)
if(is_station_level(T.z))
var/area/A = get_area(T)
if(A.outdoors)
continue
pick_turfs += T
for(var/i = 1, i <= number_of_wormholes, i++)

View File

@@ -937,3 +937,20 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
spawners_menu = new(src)
spawners_menu.ui_interact(src)
/mob/dead/observer/verb/game_info()
set name = "Game info"
set desc = "Shows various info relating to the game mode, antagonists etc."
set category = "Ghost"
if(!started_as_observer && can_reenter_corpse)
to_chat(src, "You cannot see this info unless you are an observer or you've chosen Do Not Resuscitate!")
return
var/list/stuff = list("[SSticker.mode.name]")
stuff += "Antagonists:\n"
for(var/datum/antagonist/A in GLOB.antagonists)
if(A.owner)
stuff += "[A.owner] the [A.name]"
var/ghost_info = SSticker.mode.ghost_info()
if(ghost_info)
stuff += ghost_info
to_chat(src,stuff.Join("\n"))

View File

@@ -101,7 +101,7 @@ GLOBAL_LIST_INIT(dwarf_last, world.file2list("strings/names/dwarf_last.txt")) //
/obj/item/organ/dwarfgland/on_life() //Primary loop to hook into to start delayed loops for other loops..
. = ..()
if(!owner || owner.stat == DEAD)
if(owner && owner.stat != DEAD)
dwarf_cycle_ticker()
//Handles the delayed tick cycle by just adding on increments per each on_life() tick

View File

@@ -264,6 +264,9 @@
if((last_newpatient_speak + 300) < world.time) //Don't spam these messages!
var/list/messagevoice = list("Hey, [H.name]! Hold on, I'm coming." = 'sound/voice/medbot/coming.ogg',"Wait [H.name]! I want to help!" = 'sound/voice/medbot/help.ogg',"[H.name], you appear to be injured!" = 'sound/voice/medbot/injured.ogg')
var/message = pick(messagevoice)
if(prob(1) && ISINRANGE_EX(H.getFireLoss(), 0, 20))
message = "Notices your minor burns*OwO what's this?"
messagevoice[message] = 'sound/voice/medbot/owo.ogg'
speak(message)
playsound(loc, messagevoice[message], 50, 0)
last_newpatient_speak = world.time

View File

@@ -97,6 +97,8 @@
body_color = "brown"
icon_state = "mouse_brown"
GLOBAL_VAR(tom_existed)
//TOM IS ALIVE! SQUEEEEEEEE~K :)
/mob/living/simple_animal/mouse/brown/Tom
name = "Tom"
@@ -106,6 +108,10 @@
response_harm = "splats"
gold_core_spawnable = NO_SPAWN
/mob/living/simple_animal/mouse/brown/Tom/Initialize()
. = ..()
GLOB.tom_existed = TRUE
/obj/item/reagent_containers/food/snacks/deadmouse
name = "dead mouse"
desc = "It looks like somebody dropped the bass on it. A lizard's favorite meal."

View File

@@ -10,6 +10,8 @@
throwforce = 10
blocks_emissive = EMISSIVE_BLOCK_GENERIC
vis_flags = VIS_INHERIT_PLANE //when this be added to vis_contents of something it inherit something.plane, important for visualisation of mob in openspace.
var/lighting_alpha = LIGHTING_PLANE_ALPHA_VISIBLE
var/datum/mind/mind
var/list/datum/action/actions = list()

View File

@@ -4,8 +4,8 @@
fire_sound = 'sound/weapons/ionrifle.ogg'
/obj/item/ammo_casing/energy/ion/hos
projectile_type = /obj/item/projectile/ion/weak
e_cost = 300
projectile_type = /obj/item/projectile/ion
e_cost = 200
/obj/item/ammo_casing/energy/declone
projectile_type = /obj/item/projectile/energy/declone

View File

@@ -74,3 +74,11 @@
materials = list(/datum/material/glass = 20)
build_path = /obj/item/stock_parts/cell/emergency_light
category = list("initial", "Electronics")
/datum/design/electrochromatic_control
name = "Electrochromatic Control Circuit"
id = "electrochromatic_control_circuit"
build_type = AUTOLATHE
materials = list(/datum/material/iron = 100, /datum/material/glass = 100)
build_path = /obj/item/assembly/control/electrochromatic
category = list("initial", "Electronics")

View File

@@ -1,5 +1,5 @@
/datum/design/autoylathe
build_type = AUTOYLATHE
build_type = TOYLATHE
/datum/design/autoylathe/mech
category = list("initial", "Figurines")
@@ -558,7 +558,7 @@
/datum/design/foam_x9
name = "Foam Force X9 Rifle"
id = "foam_x9"
build_type = AUTOYLATHE
build_type = TOYLATHE
materials = list(/datum/material/plastic = 4000, /datum/material/iron = 500)
build_path = /obj/item/gun/ballistic/automatic/x9/toy
category = list("initial", "Rifles")
@@ -566,7 +566,7 @@
/datum/design/foam_dart
name = "Box of Foam Darts"
id = "foam_dart"
build_type = AUTOYLATHE
build_type = TOYLATHE
materials = list(/datum/material/plastic = 500, /datum/material/iron = 100)
build_path = /obj/item/ammo_box/foambox
category = list("initial", "Misc")
@@ -574,7 +574,7 @@
/datum/design/foam_magpistol
name = "Foam Force Magpistol"
id = "magfoam_launcher"
build_type = AUTOYLATHE
build_type = TOYLATHE
materials = list(/datum/material/plastic = 2000, /datum/material/iron = 250)
build_path = /obj/item/gun/ballistic/shotgun/toy/mag
category = list("initial", "Pistols")
@@ -582,7 +582,7 @@
/datum/design/foam_magrifle
name = "Foam Force MagRifle"
id = "foam_magrifle"
build_type = AUTOYLATHE
build_type = TOYLATHE
materials = list(/datum/material/plastic = 4000, /datum/material/iron = 500)
build_path = /obj/item/gun/ballistic/automatic/toy/magrifle
category = list("initial", "Rifles")
@@ -590,7 +590,7 @@
/datum/design/foam_hyperburst
name = "MagTag Hyper Rifle"
id = "foam_hyperburst"
build_type = AUTOYLATHE
build_type = TOYLATHE
materials = list(/datum/material/plastic = 4000, /datum/material/iron = 2000, /datum/material/glass = 1000)
build_path = /obj/item/gun/energy/laser/practice/hyperburst
category = list("initial", "Rifles")
@@ -598,7 +598,7 @@
/datum/design/foam_sp
name = "Foam Force Stealth Pistol"
id = "foam_sp"
build_type = AUTOYLATHE
build_type = TOYLATHE
materials = list(/datum/material/plastic = 2000, /datum/material/iron = 1000)
build_path = /obj/item/gun/ballistic/automatic/toy/pistol/stealth
category = list("initial", "Pistols")
@@ -606,7 +606,7 @@
/datum/design/toyray
name = "RayTag Gun"
id = "toyray"
build_type = AUTOYLATHE
build_type = TOYLATHE
materials = list(/datum/material/plastic = 2000, /datum/material/iron = 1000, /datum/material/glass = 1000)
build_path = /obj/item/gun/energy/laser/practice/raygun
category = list("initial", "Pistols")
@@ -614,7 +614,7 @@
/datum/design/am4c
name = "Foam Force AM4-C Rifle"
id = "foam_am4c"
build_type = AUTOYLATHE
build_type = TOYLATHE
materials = list(/datum/material/plastic = 4000, /datum/material/iron = 500)
build_path = /obj/item/gun/ballistic/automatic/AM4C
category = list("initial", "Rifles")
@@ -622,7 +622,7 @@
/datum/design/foam_f3
name = "Replica F3 Justicar"
id = "foam_f3"
build_type = AUTOYLATHE
build_type = TOYLATHE
materials = list(/datum/material/plastic = 2000, /datum/material/iron = 250)
build_path = /obj/item/toy/gun/justicar
category = list("initial", "Pistols")
@@ -630,7 +630,7 @@
/datum/design/toy_blaster
name = "pump-action plastic blaster"
id = "toy_blaster"
build_type = AUTOYLATHE
build_type = TOYLATHE
materials = list(/datum/material/plastic = 2000, /datum/material/iron = 750, /datum/material/glass = 1000)
build_path = /obj/item/gun/energy/pumpaction/toy
category = list("initial", "Rifles")
@@ -638,7 +638,7 @@
/datum/design/capammo
name = "Box of Caps"
id = "capammo"
build_type = AUTOYLATHE
build_type = TOYLATHE
materials = list(/datum/material/iron = 10, /datum/material/glass = 10)
build_path = /obj/item/toy/ammo/gun
category = list("initial", "Misc")
@@ -646,7 +646,7 @@
/datum/design/foam_smg
name = "Foam Force SMG"
id = "foam_smg"
build_type = AUTOYLATHE
build_type = TOYLATHE
materials = list(/datum/material/plastic = 2000, /datum/material/iron = 250)
build_path = /obj/item/gun/ballistic/automatic/toy/unrestricted
category = list("initial", "Pistols")
@@ -654,7 +654,7 @@
/datum/design/foam_pistol
name = "Foam Force Pistol"
id = "foam_pistol"
build_type = AUTOYLATHE
build_type = TOYLATHE
materials = list(/datum/material/plastic = 2000, /datum/material/iron = 250)
build_path = /obj/item/gun/ballistic/automatic/toy/pistol/unrestricted
category = list("initial", "Pistols")
@@ -662,7 +662,7 @@
/datum/design/foam_shotgun
name = "Foam Force Shotgun"
id = "foam_shotgun"
build_type = AUTOYLATHE
build_type = TOYLATHE
materials = list(/datum/material/plastic = 4000, /datum/material/iron = 500)
build_path = /obj/item/gun/ballistic/shotgun/toy/unrestricted
category = list("initial", "Rifles")
@@ -670,7 +670,7 @@
/datum/design/foam_dartred
name = "Box of Lastag Red Foam Darts"
id = "redfoam_dart"
build_type = AUTOYLATHE
build_type = TOYLATHE
materials = list(/datum/material/plastic = 500, /datum/material/iron = 100)
build_path = /obj/item/ammo_box/foambox/tag/red
category = list("initial", "Misc")
@@ -678,7 +678,7 @@
/datum/design/foam_dartblue
name = "Box of Lastag Blue Foam Darts"
id = "bluefoam_dart"
build_type = AUTOYLATHE
build_type = TOYLATHE
materials = list(/datum/material/plastic = 500, /datum/material/iron = 100)
build_path = /obj/item/ammo_box/foambox/tag/blue
category = list("initial", "Misc")
@@ -686,7 +686,7 @@
/datum/design/foam_bow
name = "Foam Force Crossbow"
id = "foam_bow"
build_type = AUTOYLATHE
build_type = TOYLATHE
materials = list(/datum/material/plastic = 2000, /datum/material/iron = 250)
build_path = /obj/item/gun/ballistic/shotgun/toy/crossbow
category = list("initial", "Pistols")
@@ -694,7 +694,7 @@
/datum/design/foam_c20
name = "Donksoft C20R"
id = "foam_c20"
build_type = AUTOYLATHE
build_type = TOYLATHE
materials = list(/datum/material/plastic = 4000, /datum/material/iron = 500)
build_path = /obj/item/gun/ballistic/automatic/c20r/toy/unrestricted
category = list("hacked", "Rifles")
@@ -702,7 +702,7 @@
/datum/design/foam_l6
name = "Donksoft LMG"
id = "foam_LMG"
build_type = AUTOYLATHE
build_type = TOYLATHE
materials = list(/datum/material/plastic = 4000, /datum/material/iron = 500)
build_path = /obj/item/gun/ballistic/automatic/l6_saw/toy/unrestricted
category = list("hacked", "Rifles")

View File

@@ -111,6 +111,6 @@
name = "Machine Design (Autoylathe)"
desc = "The circuit board for an autoylathe."
id = "autoylathe"
build_path = /obj/item/circuitboard/machine/autoylathe
build_path = /obj/item/circuitboard/machine/autolathe/toy
departmental_flags = DEPARTMENTAL_FLAG_ALL
category = list("Misc. Machinery")

View File

@@ -101,7 +101,7 @@ Note: Must be placed within 3 tiles of the R&D Console
if(!istype(loaded_item) || !istype(linked_console))
return FALSE
if (id && id != RESEARCH_MATERIAL_RECLAMATION_ID)
if (id && id != RESEARCH_MATERIAL_RECLAMATION_ID && id != RESEARCH_DEEP_SCAN_ID)
var/datum/techweb_node/TN = SSresearch.techweb_node_by_id(id)
if(!istype(TN))
return FALSE
@@ -125,7 +125,7 @@ Note: Must be placed within 3 tiles of the R&D Console
if(destroy_item(loaded_item))
linked_console.stored_research.boost_with_path(SSresearch.techweb_node_by_id(TN.id), dpath)
else
else if(id == RESEARCH_MATERIAL_RECLAMATION_ID)
var/list/point_value = techweb_item_point_check(loaded_item)
if(linked_console.stored_research.deconstructed_items[loaded_item.type])
point_value = list()
@@ -143,6 +143,15 @@ Note: Must be placed within 3 tiles of the R&D Console
if(destroy_item(loaded_item))
linked_console.stored_research.add_point_list(point_value)
linked_console.stored_research.deconstructed_items[loaded_type] = point_value
else if(id == RESEARCH_DEEP_SCAN_ID)
var/list/return_list = list()
. = SEND_SIGNAL(loaded_item, COMSIG_ITEM_DECONSTRUCTOR_DEEPSCAN, src, user, return_list)
flick("d_analyzer_process", src)
if(. & COMPONENT_DEEPSCAN_UNCOVERED_INFORMATION)
say("New information uncovered from item deep scan[length(return_list)? ": [english_list(return_list)]" : ""].")
else
say("Item deep scan uncovered no new information.")
return TRUE
/obj/machinery/rnd/destructive_analyzer/proc/unload_item()

View File

@@ -160,7 +160,7 @@
if(!host_mob.client) //less brainpower
points *= 0.25
SSresearch.science_tech.add_point_list(list(TECHWEB_POINT_TYPE_GENERIC = points))
/datum/nanite_program/researchplus
name = "Neural Network"
desc = "The nanites link the host's brains together forming a neural research network, that becomes more efficient with the amount of total hosts."
@@ -184,7 +184,7 @@
SSnanites.neural_network_count--
else
SSnanites.neural_network_count -= 0.25
/datum/nanite_program/researchplus/active_effect()
if(!iscarbon(host_mob))
return
@@ -234,7 +234,7 @@
var/spread_cooldown = 0
/datum/nanite_program/spreading/active_effect()
if(spread_cooldown < world.time)
if(world.time < spread_cooldown)
return
spread_cooldown = world.time + 50
var/list/mob/living/target_hosts = list()

View File

@@ -47,6 +47,9 @@ Nothing else in the console has ID requirements.
var/research_control = TRUE
/// Long action cooldown to prevent spam
var/last_long_action = 0
/obj/machinery/computer/rdconsole/production
circuit = /obj/item/circuitboard/computer/rdconsole/production
research_control = FALSE
@@ -583,10 +586,8 @@ Nothing else in the console has ID requirements.
l += "<table><tr><td>[icon2html(linked_destroy.loaded_item, usr)]</td><td><b>[linked_destroy.loaded_item.name]</b> <A href='?src=[REF(src)];eject_item=1'>Eject</A></td></tr></table>[RDSCREEN_NOBREAK]"
l += "Select a node to boost by deconstructing this item. This item can boost:"
var/anything = FALSE
var/list/boostable_nodes = techweb_item_boost_check(linked_destroy.loaded_item)
for(var/id in boostable_nodes)
anything = TRUE
var/list/worth = boostable_nodes[id]
var/datum/techweb_node/N = SSresearch.techweb_node_by_id(id)
@@ -620,7 +621,6 @@ Nothing else in the console has ID requirements.
// point deconstruction and material reclamation use the same ID to prevent accidentally missing the points
var/list/point_values = techweb_item_point_check(linked_destroy.loaded_item)
if(point_values)
anything = TRUE
l += "<div class='statusDisplay'>[RDSCREEN_NOBREAK]"
if (stored_research.deconstructed_items[linked_destroy.loaded_item.type])
l += "<span class='linkOff'>Point Deconstruction</span>"
@@ -636,10 +636,8 @@ Nothing else in the console has ID requirements.
for (var/M in materials)
l += "* [CallMaterialName(M)] x [materials[M]]"
l += "</div>[RDSCREEN_NOBREAK]"
anything = TRUE
if (!anything)
l += "Nothing!"
l += "<div class='statusDisplay'><A href='?src=[REF(src)];deconstruct=[RESEARCH_DEEP_SCAN_ID]'>Nondestructive Deep Scan</A></div>"
l += "</div>"
return l
@@ -926,6 +924,9 @@ Nothing else in the console has ID requirements.
screen = RDSCREEN_MENU
say("Ejecting Technology Disk")
if(ls["deconstruct"])
if((last_long_action + 1 SECONDS) > world.time)
return
last_long_action = world.time
if(QDELETED(linked_destroy))
say("No Destructive Analyzer Linked!")
return
@@ -1037,7 +1038,7 @@ Nothing else in the console has ID requirements.
autolathe_friendly = FALSE
D.category -= "Imported"
if(D.build_type & (AUTOLATHE|PROTOLATHE|CRAFTLATHE)) // Specifically excludes circuit imprinter and mechfab
if(D.build_type & (AUTOLATHE|PROTOLATHE|TOYLATHE)) // Specifically excludes circuit imprinter and mechfab
D.build_type = autolathe_friendly ? (D.build_type | AUTOLATHE) : D.build_type
D.category |= "Imported"
d_disk.blueprints[slot] = D

View File

@@ -344,6 +344,7 @@
/datum/techweb/specialized/autounlocking
var/design_autounlock_buildtypes = NONE
var/design_autounlock_skip_types = NONE
var/design_autounlock_categories = list("initial") //if a design has a buildtype that matches the abovea and either has a category in this or this is null, unlock it.
var/node_autounlock_ids = list() //autounlock nodes of this type.
@@ -356,7 +357,7 @@
research_node_id(id, TRUE, FALSE)
for(var/id in SSresearch.techweb_designs)
var/datum/design/D = SSresearch.techweb_design_by_id(id)
if(D.build_type & design_autounlock_buildtypes)
if(D.build_type & (design_autounlock_buildtypes & allowed_buildtypes) && !(D.build_type & design_autounlock_skip_types))
for(var/i in D.category)
if(i in design_autounlock_categories)
add_design_by_id(D.id)
@@ -364,7 +365,16 @@
/datum/techweb/specialized/autounlocking/autolathe
design_autounlock_buildtypes = AUTOLATHE
allowed_buildtypes = AUTOLATHE
allowed_buildtypes = AUTOLATHE|TOYLATHE
/datum/techweb/specialized/autounlocking/autolathe/public
design_autounlock_skip_types = NO_PUBLIC_LATHE
/datum/techweb/specialized/autounlocking/autolathe/toy
design_autounlock_buildtypes = TOYLATHE
/datum/techweb/specialized/autounlocking/autolathe/toy/public
design_autounlock_skip_types = NO_PUBLIC_LATHE
/datum/techweb/specialized/autounlocking/limbgrower
design_autounlock_buildtypes = LIMBGROWER
@@ -381,10 +391,6 @@
/datum/techweb/specialized/autounlocking/exofab
allowed_buildtypes = MECHFAB
/datum/techweb/specialized/autounlocking/autoylathe
design_autounlock_buildtypes = AUTOYLATHE
allowed_buildtypes = AUTOYLATHE
/datum/techweb/specialized/autounlocking/autobottler
design_autounlock_buildtypes = AUTOBOTTLER
allowed_buildtypes = AUTOBOTTLER

View File

@@ -13,7 +13,7 @@
/obj/item/organ/appendix/on_life()
. = ..()
if(.)
if(. || !owner)
return
owner.adjustToxLoss(4, TRUE, TRUE) //forced to ensure people don't use it to gain tox as slime person

View File

@@ -26,7 +26,7 @@
/obj/item/organ/liver/on_life()
. = ..()
if(!.)//can't process reagents with a failing liver
if(!. || !owner)//can't process reagents with a failing liver
return
if(filterToxins && !HAS_TRAIT(owner, TRAIT_TOXINLOVER))

View File

@@ -108,7 +108,7 @@
var/datum/gas_mixture/enviro = T.return_air()
local_temp = enviro.temperature
else if(istype(loc, /mob/) && !owner)
else if(!owner && ismob(loc))
var/mob/M = loc
if(is_type_in_typecache(M.loc, GLOB.freezing_objects))
if(!(organ_flags & ORGAN_FROZEN))
@@ -137,9 +137,7 @@
/obj/item/organ/proc/on_life() //repair organ damage if the organ is not failing or synthetic
if(organ_flags & ORGAN_FAILING || !owner)
return FALSE
if(is_cold())
return FALSE
if(damage)
if(!is_cold() && damage)
///Damage decrements by a percent of its maxhealth
var/healing_amount = -(maxHealth * healing_factor)
///Damage decrements again by a percent of its maxhealth, up to a total of 4 extra times depending on the owner's satiety

View File

@@ -579,39 +579,36 @@ GLOBAL_LIST_EMPTY(vending_products)
if(!R || !istype(R) || !R.product_path)
vend_ready = TRUE
return
if(R.amount <= 0)
to_chat(usr, "<span class='warning'>Sold out.</span>")
vend_ready = TRUE
return
if(R in hidden_records)
if(!extended_inventory)
vend_ready = TRUE
return
else if(R in coin_records)
if(!(coin))
to_chat(usr, "<span class='warning'>You need to a coin to get this item!</span>")
if(!coin)
to_chat(usr, "<span class='warning'>You need to insert a coin to get this item!</span>")
vend_ready = TRUE
return
if(coin && coin.string_attached)
if(!prob(50))
to_chat(usr, "<span class='warning'>You weren't able to pull [coin] out fast enough, the machine ate it, string and all!</span>")
QDEL_NULL(coin)
return
if(!usr.CanReach(src))
to_chat(usr, "<span class='notice'>You successfully pull [coin] out of [src] to the floor.</span>")
coin = null
if(!usr.put_in_hands(coin))
if(prob(50))
if(usr.put_in_hands(coin))
to_chat(usr, "<span class='notice'>You successfully pull [coin] out before [src] could swallow it.</span>")
coin = null
else
to_chat(usr, "<span class='warning'>You couldn't pull [coin] out because your hands are full!</span>")
QDEL_NULL(coin)
to_chat(usr, "<span class='notice'>You successfully pull [coin] out before [src] could swallow it.</span>")
coin = null
QDEL_NULL(coin)
else
to_chat(usr, "<span class='warning'>You weren't able to pull [coin] out fast enough, the machine ate it, string and all!</span>")
QDEL_NULL(coin)
else
QDEL_NULL(coin)
else if(!(R in product_records))
vend_ready = TRUE
message_admins("Vending machine exploit attempted by [ADMIN_LOOKUPFLW(usr)]!")
return
if(R.amount <= 0)
to_chat(usr, "<span class='warning'>Sold out.</span>")
vend_ready = TRUE
return
else
R.amount--
if(((last_reply + 200) <= world.time) && vend_reply)
speak(vend_reply)
last_reply = world.time
@@ -623,8 +620,7 @@ GLOBAL_LIST_EMPTY(vending_products)
to_chat(usr, "<span class='notice'>You take [R.name] out of the slot.</span>")
else
to_chat(usr, "<span class='warning'>[capitalize(R.name)] falls onto the floor!</span>")
R.amount--
SSblackbox.record_feedback("nested tally", "vending_machine_usage", 1, list("[type]", "[R.product_path]"))
vend_ready = TRUE
return