Merge branch 'master' of https://github.com/Citadel-Station-13/Citadel-Station-13 into Ghommie-cit723
This commit is contained in:
+19
-4
@@ -1,13 +1,28 @@
|
||||
#define TICK_LIMIT_RUNNING 80
|
||||
/// Percentage of tick to leave for master controller to run
|
||||
#define MAPTICK_MC_MIN_RESERVE 70
|
||||
/// internal_tick_usage is updated every tick by extools
|
||||
#define MAPTICK_LAST_INTERNAL_TICK_USAGE ((GLOB.internal_tick_usage / world.tick_lag) * 100)
|
||||
/// Tick limit while running normally
|
||||
#define TICK_BYOND_RESERVE 2
|
||||
#define TICK_LIMIT_RUNNING (max(100 - TICK_BYOND_RESERVE - MAPTICK_LAST_INTERNAL_TICK_USAGE, MAPTICK_MC_MIN_RESERVE))
|
||||
/// Tick limit used to resume things in stoplag
|
||||
#define TICK_LIMIT_TO_RUN 70
|
||||
/// Tick limit for MC while running
|
||||
#define TICK_LIMIT_MC 70
|
||||
#define TICK_LIMIT_MC_INIT_DEFAULT 98
|
||||
/// Tick limit while initializing
|
||||
#define TICK_LIMIT_MC_INIT_DEFAULT (100 - TICK_BYOND_RESERVE)
|
||||
|
||||
#define TICK_USAGE world.tick_usage //for general usage
|
||||
#define TICK_USAGE_REAL world.tick_usage //to be used where the result isn't checked
|
||||
/// for general usage of tick_usage
|
||||
#define TICK_USAGE world.tick_usage
|
||||
/// to be used where the result isn't checked
|
||||
#define TICK_USAGE_REAL world.tick_usage
|
||||
|
||||
/// Returns true if tick_usage is above the limit
|
||||
#define TICK_CHECK ( TICK_USAGE > Master.current_ticklimit )
|
||||
/// runs stoplag if tick_usage is above the limit
|
||||
#define CHECK_TICK ( TICK_CHECK ? stoplag() : 0 )
|
||||
|
||||
/// Returns true if tick usage is above 95, for high priority usage
|
||||
#define TICK_CHECK_HIGH_PRIORITY ( TICK_USAGE > 95 )
|
||||
/// runs stoplag if tick_usage is above 95, for high priority usage
|
||||
#define CHECK_TICK_HIGH_PRIORITY ( TICK_CHECK_HIGH_PRIORITY? stoplag() : 0 )
|
||||
|
||||
@@ -89,6 +89,8 @@
|
||||
#define MASSIVE_OBJ_LAYER 11
|
||||
#define POINT_LAYER 12
|
||||
|
||||
#define CHAT_LAYER 12.1
|
||||
|
||||
#define EMISSIVE_BLOCKER_PLANE 12
|
||||
#define EMISSIVE_BLOCKER_LAYER 12
|
||||
#define EMISSIVE_BLOCKER_RENDER_TARGET "*EMISSIVE_BLOCKER_PLANE"
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
/// Flag for atoms, this flag ensures it isn't re-colored by materials. Useful for snowflake icons such as default toolboxes.
|
||||
#define MATERIAL_COLOR (1<<0)
|
||||
#define MATERIAL_ADD_PREFIX (1<<1)
|
||||
#define MATERIAL_NO_EFFECTS (1<<2)
|
||||
#define MATERIAL_EFFECTS (1<<2)
|
||||
#define MATERIAL_AFFECT_STATISTICS (1<<3)
|
||||
@@ -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
|
||||
|
||||
@@ -52,3 +52,4 @@
|
||||
#define ORGAN_EXTERNAL (1<<3) //Was this organ implanted/inserted/etc, if true will not be removed during species change.
|
||||
#define ORGAN_VITAL (1<<4) //Currently only the brain
|
||||
#define ORGAN_NO_SPOIL (1<<5) //Do not spoil under any circumstances
|
||||
#define ORGAN_NO_DISMEMBERMENT (1<<6) //Immune to disembowelment.
|
||||
|
||||
@@ -132,8 +132,15 @@
|
||||
|
||||
#define RUNLEVELS_DEFAULT (RUNLEVEL_SETUP | RUNLEVEL_GAME | RUNLEVEL_POSTGAME)
|
||||
|
||||
|
||||
|
||||
// SSair run section
|
||||
#define SSAIR_PIPENETS 1
|
||||
#define SSAIR_ATMOSMACHINERY 2
|
||||
#define SSAIR_REACTQUEUE 3
|
||||
#define SSAIR_EXCITEDGROUPS 4
|
||||
#define SSAIR_HIGHPRESSURE 5
|
||||
#define SSAIR_HOTSPOTS 6
|
||||
#define SSAIR_SUPERCONDUCTIVITY 7
|
||||
#define SSAIR_REBUILD_PIPENETS 8
|
||||
|
||||
#define COMPILE_OVERLAYS(A)\
|
||||
if (TRUE) {\
|
||||
|
||||
@@ -88,6 +88,7 @@
|
||||
init_subtypes(/datum/crafting_recipe, GLOB.crafting_recipes)
|
||||
|
||||
INVOKE_ASYNC(GLOBAL_PROC, /proc/init_ref_coin_values) //so the current procedure doesn't sleep because of UNTIL()
|
||||
INVOKE_ASYNC(GLOBAL_PROC, /proc/setupGenetics)
|
||||
|
||||
//creates every subtype of prototype (excluding prototype) and adds it to list L.
|
||||
//if no list/L is provided, one is created.
|
||||
@@ -113,3 +114,25 @@
|
||||
UNTIL(C.flags_1 & INITIALIZED_1) //we want to make sure the value is calculated and not null.
|
||||
GLOB.coin_values[path] = C.value
|
||||
qdel(C)
|
||||
|
||||
/proc/setupGenetics()
|
||||
var/list/mutations = subtypesof(/datum/mutation/human)
|
||||
shuffle_inplace(mutations)
|
||||
for(var/A in subtypesof(/datum/generecipe))
|
||||
var/datum/generecipe/GR = A
|
||||
GLOB.mutation_recipes[initial(GR.required)] = initial(GR.result)
|
||||
for(var/i in 1 to LAZYLEN(mutations))
|
||||
var/path = mutations[i] //byond gets pissy when we do it in one line
|
||||
var/datum/mutation/human/B = new path ()
|
||||
B.alias = "Mutation #[i]"
|
||||
GLOB.all_mutations[B.type] = B
|
||||
GLOB.full_sequences[B.type] = generate_gene_sequence(B.blocks)
|
||||
if(B.locked)
|
||||
continue
|
||||
if(B.quality == POSITIVE)
|
||||
GLOB.good_mutations |= B
|
||||
else if(B.quality == NEGATIVE)
|
||||
GLOB.bad_mutations |= B
|
||||
else if(B.quality == MINOR_NEGATIVE)
|
||||
GLOB.not_good_mutations |= B
|
||||
CHECK_TICK
|
||||
@@ -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
|
||||
|
||||
@@ -33,3 +33,5 @@ GLOBAL_VAR(bible_icon_state)
|
||||
GLOBAL_VAR(bible_item_state)
|
||||
GLOBAL_VAR(holy_weapon_type)
|
||||
GLOBAL_VAR(holy_armor_type)
|
||||
|
||||
GLOBAL_VAR_INIT(internal_tick_usage, 0.2 * world.tick_lag)
|
||||
|
||||
+24
-11
@@ -36,6 +36,9 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
|
||||
var/sleep_delta = 1
|
||||
|
||||
///Only run ticker subsystems for the next n ticks.
|
||||
var/skip_ticks = 0
|
||||
|
||||
var/make_runtime = 0
|
||||
|
||||
var/initializations_finished_with_no_players_logged_in //I wonder what this could be?
|
||||
@@ -335,7 +338,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
new/datum/controller/failsafe() // (re)Start the failsafe.
|
||||
|
||||
//now do the actual stuff
|
||||
if (!queue_head || !(iteration % 3))
|
||||
if (!skip_ticks)
|
||||
var/checking_runlevel = current_runlevel
|
||||
if(cached_runlevel != checking_runlevel)
|
||||
//resechedule subsystems
|
||||
@@ -381,6 +384,8 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
|
||||
iteration++
|
||||
last_run = world.time
|
||||
if (skip_ticks)
|
||||
skip_ticks--
|
||||
src.sleep_delta = MC_AVERAGE_FAST(src.sleep_delta, sleep_delta)
|
||||
current_ticklimit = TICK_LIMIT_RUNNING
|
||||
if (processing * sleep_delta <= world.tick_lag)
|
||||
@@ -444,10 +449,12 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
while (queue_node)
|
||||
if (ran && TICK_USAGE > TICK_LIMIT_RUNNING)
|
||||
break
|
||||
|
||||
queue_node_flags = queue_node.flags
|
||||
queue_node_priority = queue_node.queued_priority
|
||||
|
||||
if (!(queue_node_flags & SS_TICKER) && skip_ticks)
|
||||
queue_node = queue_node.queue_next
|
||||
continue
|
||||
//super special case, subsystems where we can't make them pause mid way through
|
||||
//if we can't run them this tick (without going over a tick)
|
||||
//we bump up their priority and attempt to run them next tick
|
||||
@@ -455,14 +462,15 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
// in those cases, so we just let them run)
|
||||
if (queue_node_flags & SS_NO_TICK_CHECK)
|
||||
if (queue_node.tick_usage > TICK_LIMIT_RUNNING - TICK_USAGE && ran_non_ticker)
|
||||
queue_node.queued_priority += queue_priority_count * 0.1
|
||||
queue_priority_count -= queue_node_priority
|
||||
queue_priority_count += queue_node.queued_priority
|
||||
current_tick_budget -= queue_node_priority
|
||||
queue_node = queue_node.queue_next
|
||||
if (!(queue_node_flags & SS_BACKGROUND))
|
||||
queue_node.queued_priority += queue_priority_count * 0.1
|
||||
queue_priority_count -= queue_node_priority
|
||||
queue_priority_count += queue_node.queued_priority
|
||||
current_tick_budget -= queue_node_priority
|
||||
queue_node = queue_node.queue_next
|
||||
continue
|
||||
|
||||
if ((queue_node_flags & SS_BACKGROUND) && !bg_calc)
|
||||
if (!bg_calc && (queue_node_flags & SS_BACKGROUND))
|
||||
current_tick_budget = queue_priority_count_bg
|
||||
bg_calc = TRUE
|
||||
|
||||
@@ -515,7 +523,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
queue_node.paused_ticks = 0
|
||||
queue_node.paused_tick_usage = 0
|
||||
|
||||
if (queue_node_flags & SS_BACKGROUND) //update our running total
|
||||
if (bg_calc) //update our running total
|
||||
queue_priority_count_bg -= queue_node_priority
|
||||
else
|
||||
queue_priority_count -= queue_node_priority
|
||||
@@ -583,14 +591,19 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
log_world("MC: SoftReset: Finished.")
|
||||
. = 1
|
||||
|
||||
/// Warns us that the end of tick byond map_update will be laggier then normal, so that we can just skip running subsystems this tick.
|
||||
/datum/controller/master/proc/laggy_byond_map_update_incoming()
|
||||
if (!skip_ticks)
|
||||
skip_ticks = 1
|
||||
|
||||
|
||||
/datum/controller/master/stat_entry()
|
||||
if(!statclick)
|
||||
statclick = new/obj/effect/statclick/debug(null, "Initializing...", src)
|
||||
|
||||
stat("Byond:", "(FPS:[world.fps]) (TickCount:[world.time/world.tick_lag]) (TickDrift:[round(Master.tickdrift,1)]([round((Master.tickdrift/(world.time/world.tick_lag))*100,0.1)]%))")
|
||||
stat("Master Controller:", statclick.update("(TickRate:[Master.processing]) (Iteration:[Master.iteration])"))
|
||||
stat("Byond:", "(FPS:[world.fps]) (TickCount:[world.time/world.tick_lag]) (TickDrift:[round(Master.tickdrift,1)]([round((Master.tickdrift/(world.time/world.tick_lag))*100,0.1)]%)) (Internal Tick Usage: [round(MAPTICK_LAST_INTERNAL_TICK_USAGE,0.1)]%)")
|
||||
stat("Master Controller:", statclick.update("(TickRate:[Master.processing]) (Iteration:[Master.iteration]) (TickLimit: [round(Master.current_ticklimit, 0.1)])"))
|
||||
|
||||
|
||||
/datum/controller/master/StartLoadingMap()
|
||||
//disallow more than one map to load at once, multithreading it will just cause race conditions
|
||||
|
||||
@@ -1,11 +1,3 @@
|
||||
#define SSAIR_PIPENETS 1
|
||||
#define SSAIR_ATMOSMACHINERY 2
|
||||
#define SSAIR_REACTQUEUE 3
|
||||
#define SSAIR_EXCITEDGROUPS 4
|
||||
#define SSAIR_HIGHPRESSURE 5
|
||||
#define SSAIR_HOTSPOTS 6
|
||||
#define SSAIR_SUPERCONDUCTIVITY 7
|
||||
|
||||
SUBSYSTEM_DEF(air)
|
||||
name = "Atmospherics"
|
||||
init_order = INIT_ORDER_AIR
|
||||
@@ -20,6 +12,7 @@ SUBSYSTEM_DEF(air)
|
||||
var/cost_hotspots = 0
|
||||
var/cost_superconductivity = 0
|
||||
var/cost_pipenets = 0
|
||||
var/cost_rebuilds = 0
|
||||
var/cost_atmos_machinery = 0
|
||||
|
||||
var/list/excited_groups = list()
|
||||
@@ -27,6 +20,7 @@ SUBSYSTEM_DEF(air)
|
||||
var/list/turf_react_queue = list()
|
||||
var/list/hotspots = list()
|
||||
var/list/networks = list()
|
||||
var/list/pipenets_needing_rebuilt = list()
|
||||
var/list/obj/machinery/atmos_machinery = list()
|
||||
var/list/pipe_init_dirs_cache = list()
|
||||
|
||||
@@ -39,7 +33,7 @@ SUBSYSTEM_DEF(air)
|
||||
|
||||
|
||||
var/list/currentrun = list()
|
||||
var/currentpart = SSAIR_PIPENETS
|
||||
var/currentpart = SSAIR_REBUILD_PIPENETS
|
||||
|
||||
var/map_loading = TRUE
|
||||
var/list/queued_for_activation
|
||||
@@ -52,6 +46,7 @@ SUBSYSTEM_DEF(air)
|
||||
msg += "HS:[round(cost_hotspots,1)]|"
|
||||
msg += "SC:[round(cost_superconductivity,1)]|"
|
||||
msg += "PN:[round(cost_pipenets,1)]|"
|
||||
msg += "RB:[round(cost_rebuilds,1)]|"
|
||||
msg += "AM:[round(cost_atmos_machinery,1)]"
|
||||
msg += "} "
|
||||
msg += "AT:[active_turfs.len]|"
|
||||
@@ -77,6 +72,18 @@ SUBSYSTEM_DEF(air)
|
||||
/datum/controller/subsystem/air/fire(resumed = 0)
|
||||
var/timer = TICK_USAGE_REAL
|
||||
|
||||
if(currentpart == SSAIR_REBUILD_PIPENETS)
|
||||
var/list/pipenet_rebuilds = pipenets_needing_rebuilt
|
||||
for(var/thing in pipenet_rebuilds)
|
||||
var/obj/machinery/atmospherics/AT = thing
|
||||
AT.build_network()
|
||||
cost_rebuilds = MC_AVERAGE(cost_rebuilds, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
|
||||
pipenets_needing_rebuilt.Cut()
|
||||
if(state != SS_RUNNING)
|
||||
return
|
||||
resumed = FALSE
|
||||
currentpart = SSAIR_PIPENETS
|
||||
|
||||
if(currentpart == SSAIR_PIPENETS || !resumed)
|
||||
process_pipenets(resumed)
|
||||
cost_pipenets = MC_AVERAGE(cost_pipenets, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
|
||||
@@ -137,9 +144,7 @@ SUBSYSTEM_DEF(air)
|
||||
if(state != SS_RUNNING)
|
||||
return
|
||||
resumed = 0
|
||||
currentpart = SSAIR_PIPENETS
|
||||
|
||||
|
||||
currentpart = SSAIR_REBUILD_PIPENETS
|
||||
|
||||
/datum/controller/subsystem/air/proc/process_pipenets(resumed = 0)
|
||||
if (!resumed)
|
||||
@@ -156,6 +161,9 @@ SUBSYSTEM_DEF(air)
|
||||
if(MC_TICK_CHECK)
|
||||
return
|
||||
|
||||
/datum/controller/subsystem/air/proc/add_to_rebuild_queue(atmos_machine)
|
||||
if(istype(atmos_machine, /obj/machinery/atmospherics))
|
||||
pipenets_needing_rebuilt += atmos_machine
|
||||
|
||||
/datum/controller/subsystem/air/proc/process_atmos_machinery(resumed = 0)
|
||||
var/seconds = wait * 0.1
|
||||
|
||||
@@ -16,7 +16,6 @@ SUBSYSTEM_DEF(atoms)
|
||||
|
||||
/datum/controller/subsystem/atoms/Initialize(timeofday)
|
||||
GLOB.fire_overlay.appearance_flags = RESET_COLOR
|
||||
setupGenetics() //to set the mutations' sequence.
|
||||
initialized = INITIALIZATION_INNEW_MAPLOAD
|
||||
InitializeAtoms()
|
||||
return ..()
|
||||
@@ -107,28 +106,6 @@ SUBSYSTEM_DEF(atoms)
|
||||
old_initialized = SSatoms.old_initialized
|
||||
BadInitializeCalls = SSatoms.BadInitializeCalls
|
||||
|
||||
/datum/controller/subsystem/atoms/proc/setupGenetics()
|
||||
var/list/mutations = subtypesof(/datum/mutation/human)
|
||||
shuffle_inplace(mutations)
|
||||
for(var/A in subtypesof(/datum/generecipe))
|
||||
var/datum/generecipe/GR = A
|
||||
GLOB.mutation_recipes[initial(GR.required)] = initial(GR.result)
|
||||
for(var/i in 1 to LAZYLEN(mutations))
|
||||
var/path = mutations[i] //byond gets pissy when we do it in one line
|
||||
var/datum/mutation/human/B = new path ()
|
||||
B.alias = "Mutation #[i]"
|
||||
GLOB.all_mutations[B.type] = B
|
||||
GLOB.full_sequences[B.type] = generate_gene_sequence(B.blocks)
|
||||
if(B.locked)
|
||||
continue
|
||||
if(B.quality == POSITIVE)
|
||||
GLOB.good_mutations |= B
|
||||
else if(B.quality == NEGATIVE)
|
||||
GLOB.bad_mutations |= B
|
||||
else if(B.quality == MINOR_NEGATIVE)
|
||||
GLOB.not_good_mutations |= B
|
||||
CHECK_TICK
|
||||
|
||||
/datum/controller/subsystem/atoms/proc/InitLog()
|
||||
. = ""
|
||||
for(var/path in BadInitializeCalls)
|
||||
|
||||
@@ -42,6 +42,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)
|
||||
@@ -52,6 +59,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)
|
||||
@@ -568,3 +579,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++]"
|
||||
|
||||
@@ -150,6 +150,8 @@
|
||||
friend_talk(message)
|
||||
|
||||
/mob/camera/imaginary_friend/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode, atom/movable/source)
|
||||
if (client?.prefs.chat_on_map && (client.prefs.see_chat_non_mob || ismob(speaker)))
|
||||
create_chat_message(speaker, message_language, raw_message, spans, message_mode)
|
||||
to_chat(src, compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode, FALSE, source))
|
||||
|
||||
/mob/camera/imaginary_friend/proc/friend_talk(message)
|
||||
|
||||
@@ -0,0 +1,236 @@
|
||||
#define CHAT_MESSAGE_SPAWN_TIME 0.2 SECONDS
|
||||
#define CHAT_MESSAGE_LIFESPAN 5 SECONDS
|
||||
#define CHAT_MESSAGE_EOL_FADE 0.7 SECONDS
|
||||
#define CHAT_MESSAGE_EXP_DECAY 0.7 // Messages decay at pow(factor, idx in stack)
|
||||
#define CHAT_MESSAGE_HEIGHT_DECAY 0.9 // Increase message decay based on the height of the message
|
||||
#define CHAT_MESSAGE_APPROX_LHEIGHT 11 // Approximate height in pixels of an 'average' line, used for height decay
|
||||
#define CHAT_MESSAGE_WIDTH 96 // pixels
|
||||
#define CHAT_MESSAGE_MAX_LENGTH 110 // characters
|
||||
#define WXH_TO_HEIGHT(x) text2num(copytext((x), findtextEx((x), "x") + 1)) // thanks lummox
|
||||
|
||||
/**
|
||||
* # Chat Message Overlay
|
||||
*
|
||||
* Datum for generating a message overlay on the map
|
||||
*/
|
||||
/datum/chatmessage
|
||||
/// The visual element of the chat messsage
|
||||
var/image/message
|
||||
/// The location in which the message is appearing
|
||||
var/atom/message_loc
|
||||
/// The client who heard this message
|
||||
var/client/owned_by
|
||||
/// Contains the scheduled destruction time
|
||||
var/scheduled_destruction
|
||||
/// Contains the approximate amount of lines for height decay
|
||||
var/approx_lines
|
||||
|
||||
/**
|
||||
* Constructs a chat message overlay
|
||||
*
|
||||
* Arguments:
|
||||
* * text - The text content of the overlay
|
||||
* * target - The target atom to display the overlay at
|
||||
* * owner - The mob that owns this overlay, only this mob will be able to view it
|
||||
* * extra_classes - Extra classes to apply to the span that holds the text
|
||||
* * lifespan - The lifespan of the message in deciseconds
|
||||
*/
|
||||
/datum/chatmessage/New(text, atom/target, mob/owner, list/extra_classes = null, lifespan = CHAT_MESSAGE_LIFESPAN)
|
||||
. = ..()
|
||||
if (!istype(target))
|
||||
CRASH("Invalid target given for chatmessage")
|
||||
if(QDELETED(owner) || !istype(owner) || !owner.client)
|
||||
stack_trace("/datum/chatmessage created with [isnull(owner) ? "null" : "invalid"] mob owner")
|
||||
qdel(src)
|
||||
return
|
||||
INVOKE_ASYNC(src, .proc/generate_image, text, target, owner, extra_classes, lifespan)
|
||||
|
||||
/datum/chatmessage/Destroy()
|
||||
if (owned_by)
|
||||
if (owned_by.seen_messages)
|
||||
LAZYREMOVEASSOC(owned_by.seen_messages, message_loc, src)
|
||||
owned_by.images.Remove(message)
|
||||
owned_by = null
|
||||
message_loc = null
|
||||
message = null
|
||||
return ..()
|
||||
|
||||
/**
|
||||
* Generates a chat message image representation
|
||||
*
|
||||
* Arguments:
|
||||
* * text - The text content of the overlay
|
||||
* * target - The target atom to display the overlay at
|
||||
* * owner - The mob that owns this overlay, only this mob will be able to view it
|
||||
* * extra_classes - Extra classes to apply to the span that holds the text
|
||||
* * lifespan - The lifespan of the message in deciseconds
|
||||
*/
|
||||
/datum/chatmessage/proc/generate_image(text, atom/target, mob/owner, list/extra_classes, lifespan)
|
||||
// Register client who owns this message
|
||||
owned_by = owner.client
|
||||
RegisterSignal(owned_by, COMSIG_PARENT_QDELETING, .proc/qdel, src)
|
||||
|
||||
// Clip message
|
||||
var/maxlen = owned_by.prefs.max_chat_length
|
||||
if (length_char(text) > maxlen)
|
||||
text = copytext_char(text, 1, maxlen + 1) + "..." // BYOND index moment
|
||||
|
||||
// Calculate target color if not already present
|
||||
if (!target.chat_color || target.chat_color_name != target.name)
|
||||
target.chat_color = colorize_string(target.name)
|
||||
target.chat_color_darkened = colorize_string(target.name, 0.85, 0.85)
|
||||
target.chat_color_name = target.name
|
||||
|
||||
// Get rid of any URL schemes that might cause BYOND to automatically wrap something in an anchor tag
|
||||
var/static/regex/url_scheme = new(@"[A-Za-z][A-Za-z0-9+-\.]*:\/\/", "g")
|
||||
text = replacetext(text, url_scheme, "")
|
||||
|
||||
// Reject whitespace
|
||||
var/static/regex/whitespace = new(@"^\s*$")
|
||||
if (whitespace.Find(text))
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
// Non mobs speakers can be small
|
||||
if (!ismob(target))
|
||||
extra_classes |= "small"
|
||||
|
||||
// Append radio icon if from a virtual speaker
|
||||
if (extra_classes.Find("virtual-speaker"))
|
||||
var/image/r_icon = image('icons/UI_Icons/chat/chat_icons.dmi', icon_state = "radio")
|
||||
text = "\icon[r_icon] " + text
|
||||
|
||||
// We dim italicized text to make it more distinguishable from regular text
|
||||
var/tgt_color = extra_classes.Find("italics") ? target.chat_color_darkened : target.chat_color
|
||||
|
||||
// Approximate text height
|
||||
// Note we have to replace HTML encoded metacharacters otherwise MeasureText will return a zero height
|
||||
// BYOND Bug #2563917
|
||||
// Construct text
|
||||
var/static/regex/html_metachars = new(@"&[A-Za-z]{1,7};", "g")
|
||||
var/complete_text = "<span class='center maptext [extra_classes != null ? extra_classes.Join(" ") : ""]' style='color: [tgt_color]'>[text]</span>"
|
||||
var/mheight = WXH_TO_HEIGHT(owned_by.MeasureText(replacetext(complete_text, html_metachars, "m"), null, CHAT_MESSAGE_WIDTH))
|
||||
approx_lines = max(1, mheight / CHAT_MESSAGE_APPROX_LHEIGHT)
|
||||
|
||||
// Translate any existing messages upwards, apply exponential decay factors to timers
|
||||
message_loc = target
|
||||
if (owned_by.seen_messages)
|
||||
var/idx = 1
|
||||
var/combined_height = approx_lines
|
||||
for(var/msg in owned_by.seen_messages[message_loc])
|
||||
var/datum/chatmessage/m = msg
|
||||
animate(m.message, pixel_y = m.message.pixel_y + mheight, time = CHAT_MESSAGE_SPAWN_TIME)
|
||||
combined_height += m.approx_lines
|
||||
var/sched_remaining = m.scheduled_destruction - world.time
|
||||
if (sched_remaining > CHAT_MESSAGE_SPAWN_TIME)
|
||||
var/remaining_time = (sched_remaining) * (CHAT_MESSAGE_EXP_DECAY ** idx++) * (CHAT_MESSAGE_HEIGHT_DECAY ** combined_height)
|
||||
m.scheduled_destruction = world.time + remaining_time
|
||||
addtimer(CALLBACK(m, .proc/end_of_life), remaining_time, TIMER_UNIQUE|TIMER_OVERRIDE)
|
||||
|
||||
// Build message image
|
||||
message = image(loc = message_loc, layer = CHAT_LAYER)
|
||||
message.plane = GAME_PLANE
|
||||
message.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA | KEEP_APART
|
||||
message.alpha = 0
|
||||
message.pixel_y = owner.bound_height * 0.95
|
||||
message.maptext_width = CHAT_MESSAGE_WIDTH
|
||||
message.maptext_height = mheight
|
||||
message.maptext_x = (CHAT_MESSAGE_WIDTH - owner.bound_width) * -0.5
|
||||
message.maptext = complete_text
|
||||
|
||||
// View the message
|
||||
LAZYADDASSOC(owned_by.seen_messages, message_loc, src)
|
||||
owned_by.images |= message
|
||||
animate(message, alpha = 255, time = CHAT_MESSAGE_SPAWN_TIME)
|
||||
|
||||
// Prepare for destruction
|
||||
scheduled_destruction = world.time + (lifespan - CHAT_MESSAGE_EOL_FADE)
|
||||
addtimer(CALLBACK(src, .proc/end_of_life), lifespan - CHAT_MESSAGE_EOL_FADE, TIMER_UNIQUE|TIMER_OVERRIDE)
|
||||
|
||||
/**
|
||||
* Applies final animations to overlay CHAT_MESSAGE_EOL_FADE deciseconds prior to message deletion
|
||||
*/
|
||||
/datum/chatmessage/proc/end_of_life(fadetime = CHAT_MESSAGE_EOL_FADE)
|
||||
animate(message, alpha = 0, time = fadetime, flags = ANIMATION_PARALLEL)
|
||||
QDEL_IN(src, fadetime)
|
||||
|
||||
/**
|
||||
* Creates a message overlay at a defined location for a given speaker
|
||||
*
|
||||
* Arguments:
|
||||
* * speaker - The atom who is saying this message
|
||||
* * message_language - The language that the message is said in
|
||||
* * raw_message - The text content of the message
|
||||
* * spans - Additional classes to be added to the message
|
||||
* * message_mode - Bitflags relating to the mode of the message
|
||||
*/
|
||||
/mob/proc/create_chat_message(atom/movable/speaker, datum/language/message_language, raw_message, list/spans, message_mode)
|
||||
// Ensure the list we are using, if present, is a copy so we don't modify the list provided to us
|
||||
spans = spans?.Copy()
|
||||
|
||||
// Check for virtual speakers (aka hearing a message through a radio)
|
||||
var/atom/movable/originalSpeaker = speaker
|
||||
if (istype(speaker, /atom/movable/virtualspeaker))
|
||||
var/atom/movable/virtualspeaker/v = speaker
|
||||
speaker = v.source
|
||||
spans |= "virtual-speaker"
|
||||
|
||||
// Ignore virtual speaker (most often radio messages) from ourself
|
||||
if (originalSpeaker != src && speaker == src)
|
||||
return
|
||||
|
||||
// Display visual above source
|
||||
new /datum/chatmessage(lang_treat(speaker, message_language, raw_message, spans, null, TRUE), speaker, src, spans)
|
||||
|
||||
|
||||
// Tweak these defines to change the available color ranges
|
||||
#define CM_COLOR_SAT_MIN 0.6
|
||||
#define CM_COLOR_SAT_MAX 0.7
|
||||
#define CM_COLOR_LUM_MIN 0.65
|
||||
#define CM_COLOR_LUM_MAX 0.75
|
||||
|
||||
/**
|
||||
* Gets a color for a name, will return the same color for a given string consistently within a round.atom
|
||||
*
|
||||
* Note that this proc aims to produce pastel-ish colors using the HSL colorspace. These seem to be favorable for displaying on the map.
|
||||
*
|
||||
* Arguments:
|
||||
* * name - The name to generate a color for
|
||||
* * sat_shift - A value between 0 and 1 that will be multiplied against the saturation
|
||||
* * lum_shift - A value between 0 and 1 that will be multiplied against the luminescence
|
||||
*/
|
||||
/datum/chatmessage/proc/colorize_string(name, sat_shift = 1, lum_shift = 1)
|
||||
// seed to help randomness
|
||||
var/static/rseed = rand(1,26)
|
||||
|
||||
// get hsl using the selected 6 characters of the md5 hash
|
||||
var/hash = copytext(md5(name + GLOB.round_id), rseed, rseed + 6)
|
||||
var/h = hex2num(copytext(hash, 1, 3)) * (360 / 255)
|
||||
var/s = (hex2num(copytext(hash, 3, 5)) >> 2) * ((CM_COLOR_SAT_MAX - CM_COLOR_SAT_MIN) / 63) + CM_COLOR_SAT_MIN
|
||||
var/l = (hex2num(copytext(hash, 5, 7)) >> 2) * ((CM_COLOR_LUM_MAX - CM_COLOR_LUM_MIN) / 63) + CM_COLOR_LUM_MIN
|
||||
|
||||
// adjust for shifts
|
||||
s *= clamp(sat_shift, 0, 1)
|
||||
l *= clamp(lum_shift, 0, 1)
|
||||
|
||||
// convert to rgb
|
||||
var/h_int = round(h/60) // mapping each section of H to 60 degree sections
|
||||
var/c = (1 - abs(2 * l - 1)) * s
|
||||
var/x = c * (1 - abs((h / 60) % 2 - 1))
|
||||
var/m = l - c * 0.5
|
||||
x = (x + m) * 255
|
||||
c = (c + m) * 255
|
||||
m *= 255
|
||||
switch(h_int)
|
||||
if(0)
|
||||
return "#[num2hex(c, 2)][num2hex(x, 2)][num2hex(m, 2)]"
|
||||
if(1)
|
||||
return "#[num2hex(x, 2)][num2hex(c, 2)][num2hex(m, 2)]"
|
||||
if(2)
|
||||
return "#[num2hex(m, 2)][num2hex(c, 2)][num2hex(x, 2)]"
|
||||
if(3)
|
||||
return "#[num2hex(m, 2)][num2hex(x, 2)][num2hex(c, 2)]"
|
||||
if(4)
|
||||
return "#[num2hex(x, 2)][num2hex(m, 2)][num2hex(c, 2)]"
|
||||
if(5)
|
||||
return "#[num2hex(c, 2)][num2hex(m, 2)][num2hex(x, 2)]"
|
||||
@@ -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()
|
||||
@@ -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//
|
||||
////////////
|
||||
|
||||
@@ -39,4 +39,5 @@
|
||||
/obj/effect/abstract/mirage_holder
|
||||
name = "Mirage holder"
|
||||
anchored = TRUE
|
||||
plane = PLANE_SPACE
|
||||
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
|
||||
@@ -164,7 +164,7 @@
|
||||
release()
|
||||
|
||||
/obj/item/clothing/head/mob_holder/mob_can_equip(mob/living/M, mob/living/equipper, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE)
|
||||
if(!ishuman(M)) //monkeys holding monkeys holding monkeys...
|
||||
if(M == held_mob || !ishuman(M)) //monkeys holding monkeys holding monkeys...
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
|
||||
@@ -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]++
|
||||
@@ -41,9 +41,7 @@
|
||||
/datum/skill_holder/proc/get_skill_affinity(skill)
|
||||
if(!ispath(skill))
|
||||
CRASH("Invalid get_skill_affinity call. Use typepaths.") //until a time when we somehow need text ids for dynamic skills, I'm enforcing this.
|
||||
if(!skills)
|
||||
return 1
|
||||
var/affinity = skill_affinities[skill]
|
||||
var/affinity = LAZYACCESS(skill_affinities, skill)
|
||||
if(isnull(affinity))
|
||||
return 1
|
||||
return affinity
|
||||
|
||||
+8
-1
@@ -50,6 +50,13 @@
|
||||
var/list/blood_DNA
|
||||
var/list/suit_fibers
|
||||
|
||||
/// Last name used to calculate a color for the chatmessage overlays
|
||||
var/chat_color_name
|
||||
/// Last color calculated for the the chatmessage overlays
|
||||
var/chat_color
|
||||
/// A luminescence-shifted value of the last color calculated for chatmessage overlays
|
||||
var/chat_color_darkened
|
||||
|
||||
/atom/New(loc, ...)
|
||||
//atom creation method that preloads variables at creation
|
||||
if(GLOB.use_preloader && (src.type == GLOB._preloader.target_path))//in case the instanciated atom is creating other atoms in New()
|
||||
@@ -965,7 +972,7 @@ Proc for attack log creation, because really why not
|
||||
for(var/x in materials)
|
||||
var/datum/material/custom_material = SSmaterials.GetMaterialRef(x)
|
||||
|
||||
if(!(material_flags & MATERIAL_NO_EFFECTS))
|
||||
if(material_flags & MATERIAL_EFFECTS)
|
||||
custom_material.on_applied(src, materials[custom_material] * multiplier * material_modifier, material_flags)
|
||||
custom_materials[custom_material] += materials[x] * multiplier
|
||||
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -53,6 +53,11 @@
|
||||
using_power = FALSE
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/recharger/Exited(atom/movable/M, atom/newloc)
|
||||
. = ..()
|
||||
if(charging == M)
|
||||
setCharging()
|
||||
|
||||
/obj/machinery/recharger/attackby(obj/item/G, mob/user, params)
|
||||
if(istype(G, /obj/item/wrench))
|
||||
if(charging)
|
||||
@@ -111,13 +116,11 @@
|
||||
charging.update_icon()
|
||||
charging.forceMove(drop_location())
|
||||
user.put_in_hands(charging)
|
||||
setCharging(null)
|
||||
|
||||
/obj/machinery/recharger/attack_tk(mob/user)
|
||||
if(charging)
|
||||
charging.update_icon()
|
||||
charging.forceMove(drop_location())
|
||||
setCharging(null)
|
||||
|
||||
/obj/machinery/recharger/process()
|
||||
if(stat & (NOPOWER|BROKEN) || !anchored)
|
||||
|
||||
@@ -530,7 +530,6 @@
|
||||
equip_cooldown = 0
|
||||
var/obj/item/gun/medbeam/mech/medigun
|
||||
custom_materials = list(/datum/material/iron = 15000, /datum/material/glass = 8000, /datum/material/plasma = 3000, /datum/material/gold = 8000, /datum/material/diamond = 2000)
|
||||
material_flags = MATERIAL_NO_EFFECTS
|
||||
|
||||
/obj/item/mecha_parts/mecha_equipment/medical/mechmedbeam/Initialize()
|
||||
. = ..()
|
||||
|
||||
@@ -188,7 +188,6 @@
|
||||
|
||||
var/location = get_step(src,(dir))
|
||||
var/obj/item/I = new D.build_path(location)
|
||||
I.material_flags |= MATERIAL_NO_EFFECTS //Find a better way to do this.
|
||||
I.set_custom_materials(res_coef)
|
||||
say("\The [I] is complete.")
|
||||
being_built = null
|
||||
|
||||
@@ -914,11 +914,3 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
|
||||
. = ..()
|
||||
if(var_name == NAMEOF(src, slowdown))
|
||||
set_slowdown(var_value) //don't care if it's a duplicate edit as slowdown'll be set, do it anyways to force normal behavior.
|
||||
|
||||
//Called when the object is constructed by an autolathe
|
||||
//Has a reference to the autolathe so you can do !!FUN!! things with hacked lathes
|
||||
/obj/item/proc/autolathe_crafted(obj/machinery/autolathe/A)
|
||||
return
|
||||
|
||||
/obj/item/proc/rnd_crafted(obj/machinery/rnd/production/P)
|
||||
return
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -68,10 +68,9 @@
|
||||
update_icon()
|
||||
|
||||
/obj/item/multitool/update_icon_state()
|
||||
icon_state = initial(icon_state)
|
||||
if(selected_io)
|
||||
icon_state = "multitool_red"
|
||||
else
|
||||
icon_state = "multitool"
|
||||
icon_state += "_red"
|
||||
|
||||
/obj/item/multitool/proc/wire(var/datum/integrated_io/io, mob/user)
|
||||
if(!io.holder.assembly)
|
||||
|
||||
@@ -648,7 +648,7 @@
|
||||
item_state = "mace_greyscale"
|
||||
lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
|
||||
material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS //Material type changes the prefix as well as the color.
|
||||
material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS | MATERIAL_EFFECTS //Material type changes the prefix as well as the color.
|
||||
custom_materials = list(/datum/material/iron = 12000) //Defaults to an Iron Mace.
|
||||
slot_flags = ITEM_SLOT_BELT
|
||||
force = 14
|
||||
|
||||
@@ -112,7 +112,7 @@
|
||||
. = ..()
|
||||
if(!proximity)
|
||||
return
|
||||
if(!isturf(target) || !isobj(target))
|
||||
if(!isturf(target) && !isobj(target))
|
||||
return
|
||||
if(target.color != initial(target.color))
|
||||
target.remove_atom_colour(WASHABLE_COLOUR_PRIORITY)
|
||||
|
||||
@@ -107,6 +107,8 @@
|
||||
return TRUE
|
||||
|
||||
/obj/item/shield/proc/user_shieldbash(mob/living/user, atom/target, harmful)
|
||||
if(!(user.combat_flags & COMBAT_FLAG_COMBAT_ACTIVE)) //Combat mode has to be enabled for shield bashing
|
||||
return FALSE
|
||||
if(!(shield_flags & SHIELD_CAN_BASH))
|
||||
to_chat(user, "<span class='warning'>[src] can't be used to shield bash!</span>")
|
||||
return FALSE
|
||||
|
||||
@@ -109,7 +109,6 @@ GLOBAL_LIST_INIT(pglass_recipes, list ( \
|
||||
merge_type = /obj/item/stack/sheet/plasmaglass
|
||||
grind_results = list(/datum/reagent/silicon = 20, /datum/reagent/toxin/plasma = 10)
|
||||
tableVariant = /obj/structure/table/plasmaglass
|
||||
material_flags = MATERIAL_NO_EFFECTS
|
||||
shard_type = /obj/item/shard/plasma
|
||||
|
||||
/obj/item/stack/sheet/plasmaglass/fifty
|
||||
@@ -209,7 +208,6 @@ GLOBAL_LIST_INIT(prglass_recipes, list ( \
|
||||
custom_materials = list(/datum/material/plasma=MINERAL_MATERIAL_AMOUNT * 0.5, /datum/material/glass=MINERAL_MATERIAL_AMOUNT, /datum/material/iron=MINERAL_MATERIAL_AMOUNT * 0.5,)
|
||||
armor = list("melee" = 20, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 100)
|
||||
resistance_flags = ACID_PROOF
|
||||
material_flags = MATERIAL_NO_EFFECTS
|
||||
merge_type = /obj/item/stack/sheet/plasmarglass
|
||||
grind_results = list(/datum/reagent/silicon = 20, /datum/reagent/toxin/plasma = 10, /datum/reagent/iron = 10)
|
||||
point_value = 23
|
||||
@@ -259,7 +257,6 @@ GLOBAL_LIST_INIT(plastitaniumglass_recipes, list(
|
||||
item_state = "sheet-plastitaniumglass"
|
||||
custom_materials = list(/datum/material/titanium=MINERAL_MATERIAL_AMOUNT * 0.5, /datum/material/plasma=MINERAL_MATERIAL_AMOUNT * 0.5, /datum/material/glass=MINERAL_MATERIAL_AMOUNT)
|
||||
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 100)
|
||||
material_flags = MATERIAL_NO_EFFECTS
|
||||
resistance_flags = ACID_PROOF
|
||||
merge_type = /obj/item/stack/sheet/plastitaniumglass
|
||||
shard_type = /obj/item/shard
|
||||
|
||||
@@ -324,7 +324,6 @@ GLOBAL_LIST_INIT(titanium_recipes, list ( \
|
||||
custom_materials = list(/datum/material/titanium=MINERAL_MATERIAL_AMOUNT, /datum/material/plasma=MINERAL_MATERIAL_AMOUNT)
|
||||
point_value = 45
|
||||
merge_type = /obj/item/stack/sheet/mineral/plastitanium
|
||||
material_flags = MATERIAL_NO_EFFECTS
|
||||
|
||||
/obj/item/stack/sheet/mineral/plastitanium/fifty
|
||||
amount = 50
|
||||
|
||||
@@ -215,7 +215,6 @@ GLOBAL_LIST_INIT(plasteel_recipes, list ( \
|
||||
grind_results = list(/datum/reagent/iron = 20, /datum/reagent/toxin/plasma = 20)
|
||||
point_value = 23
|
||||
tableVariant = /obj/structure/table/reinforced
|
||||
material_flags = MATERIAL_NO_EFFECTS
|
||||
|
||||
/obj/item/stack/sheet/plasteel/get_main_recipes()
|
||||
. = ..()
|
||||
|
||||
@@ -78,4 +78,3 @@
|
||||
turf_type = /turf/open/floor/mineral/plastitanium
|
||||
mineralType = "plastitanium"
|
||||
custom_materials = list(/datum/material/titanium=250, /datum/material/plasma=250)
|
||||
material_flags = MATERIAL_NO_EFFECTS
|
||||
@@ -315,3 +315,11 @@
|
||||
current_skin = choice
|
||||
icon_state = unique_reskin[choice]
|
||||
to_chat(M, "[src] is now skinned as '[choice]'.")
|
||||
|
||||
//Called when the object is constructed by an autolathe
|
||||
//Has a reference to the autolathe so you can do !!FUN!! things with hacked lathes
|
||||
/obj/proc/autolathe_crafted(obj/machinery/autolathe/A)
|
||||
return
|
||||
|
||||
/obj/proc/rnd_crafted(obj/machinery/rnd/production/P)
|
||||
return
|
||||
|
||||
@@ -153,7 +153,7 @@
|
||||
///Material chair
|
||||
/obj/structure/chair/greyscale
|
||||
icon_state = "chair_greyscale"
|
||||
material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
|
||||
material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS | MATERIAL_EFFECTS
|
||||
item_chair = /obj/item/chair/greyscale
|
||||
buildstacktype = null //Custom mats handle this
|
||||
|
||||
@@ -382,7 +382,7 @@
|
||||
/obj/item/chair/greyscale
|
||||
icon_state = "chair_greyscale_toppled"
|
||||
item_state = "chair_greyscale"
|
||||
material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
|
||||
material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS | MATERIAL_EFFECTS
|
||||
origin_type = /obj/structure/chair/greyscale
|
||||
|
||||
/obj/item/chair/stool
|
||||
|
||||
@@ -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 ..()
|
||||
@@ -211,7 +211,7 @@
|
||||
/obj/structure/table/greyscale
|
||||
icon = 'icons/obj/smooth_structures/table_greyscale.dmi'
|
||||
icon_state = "table"
|
||||
material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
|
||||
material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS | MATERIAL_EFFECTS
|
||||
buildstack = null //No buildstack, so generate from mat datums
|
||||
|
||||
/*
|
||||
|
||||
@@ -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
|
||||
|
||||
+10
-5
@@ -96,21 +96,26 @@ GLOBAL_LIST_INIT(freqtospan, list(
|
||||
return "[say_mod(input, message_mode)][spanned ? ", \"[spanned]\"" : ""]"
|
||||
// Citadel edit [spanned ? ", \"[spanned]\"" : ""]"
|
||||
|
||||
/atom/movable/proc/lang_treat(atom/movable/speaker, datum/language/language, raw_message, list/spans, message_mode)
|
||||
/// Quirky citadel proc for our custom sayverbs to strip the verb out. Snowflakey as hell, say rewrite 3.0 when?
|
||||
/atom/movable/proc/quoteless_say_quote(input, list/spans = list(speech_span), message_mode)
|
||||
var/pos = findtext(input, "*")
|
||||
return pos? copytext(input, pos + 1) : input
|
||||
|
||||
/atom/movable/proc/lang_treat(atom/movable/speaker, datum/language/language, raw_message, list/spans, message_mode, no_quote = FALSE)
|
||||
if(has_language(language))
|
||||
var/atom/movable/AM = speaker.GetSource()
|
||||
if(AM) //Basically means "if the speaker is virtual"
|
||||
return AM.say_quote(raw_message, spans, message_mode)
|
||||
return no_quote ? AM.quoteless_say_quote(raw_message, spans, message_mode) : AM.say_quote(raw_message, spans, message_mode)
|
||||
else
|
||||
return speaker.say_quote(raw_message, spans, message_mode)
|
||||
return no_quote ? speaker.quoteless_say_quote(raw_message, spans, message_mode) : speaker.say_quote(raw_message, spans, message_mode)
|
||||
else if(language)
|
||||
var/atom/movable/AM = speaker.GetSource()
|
||||
var/datum/language/D = GLOB.language_datum_instances[language]
|
||||
raw_message = D.scramble(raw_message)
|
||||
if(AM)
|
||||
return AM.say_quote(raw_message, spans, message_mode)
|
||||
return no_quote ? AM.quoteless_say_quote(raw_message, spans, message_mode) : AM.say_quote(raw_message, spans, message_mode)
|
||||
else
|
||||
return speaker.say_quote(raw_message, spans, message_mode)
|
||||
return no_quote ? speaker.quoteless_say_quote(raw_message, spans, message_mode) : speaker.say_quote(raw_message, spans, message_mode)
|
||||
else
|
||||
return "makes a strange sound."
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ GLOBAL_LIST(topic_status_cache)
|
||||
//This happens after the Master subsystem new(s) (it's a global datum)
|
||||
//So subsystems globals exist, but are not initialised
|
||||
/world/New()
|
||||
if(fexists("byond-extools.dll"))
|
||||
call("byond-extools.dll", "maptick_initialize")()
|
||||
enable_debugger()
|
||||
|
||||
world.Profile(PROFILE_START)
|
||||
|
||||
@@ -128,6 +128,9 @@ the new instance inside the host to be updated to the template's stats.
|
||||
link = FOLLOW_LINK(src, to_follow)
|
||||
else
|
||||
link = ""
|
||||
// Create map text prior to modifying message for goonchat
|
||||
if (client?.prefs.chat_on_map && (client.prefs.see_chat_non_mob || ismob(speaker)))
|
||||
create_chat_message(speaker, message_language, raw_message, spans, message_mode)
|
||||
// Recompose the message, because it's scrambled by default
|
||||
message = compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode, FALSE, source)
|
||||
to_chat(src, "[link] [message]")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
return
|
||||
|
||||
|
||||
/turf/open/hotspot_expose(exposed_temperature, exposed_volume, soh)
|
||||
/turf/open/hotspot_expose(exposed_temperature, exposed_volume, soh = FALSE, holo = FALSE)
|
||||
var/datum/gas_mixture/air_contents = return_air()
|
||||
if(!air_contents)
|
||||
return 0
|
||||
@@ -35,7 +35,7 @@
|
||||
if(oxy < 0.5)
|
||||
return 0
|
||||
|
||||
active_hotspot = new /obj/effect/hotspot(src)
|
||||
active_hotspot = new /obj/effect/hotspot(src, holo)
|
||||
active_hotspot.temperature = exposed_temperature*50
|
||||
active_hotspot.volume = exposed_volume*25
|
||||
|
||||
@@ -67,8 +67,10 @@
|
||||
var/bypassing = FALSE
|
||||
var/visual_update_tick = 0
|
||||
|
||||
/obj/effect/hotspot/Initialize()
|
||||
/obj/effect/hotspot/Initialize(mapload, holo = FALSE)
|
||||
. = ..()
|
||||
if(holo)
|
||||
flags_1 |= HOLOGRAM_1
|
||||
SSair.hotspots += src
|
||||
perform_exposure()
|
||||
setDir(pick(GLOB.cardinals))
|
||||
@@ -192,7 +194,8 @@
|
||||
|
||||
if(bypassing)
|
||||
icon_state = "3"
|
||||
location.burn_tile()
|
||||
if(!(flags_1 & HOLOGRAM_1))
|
||||
location.burn_tile()
|
||||
|
||||
//Possible spread due to radiated heat
|
||||
if(location.air.temperature > FIRE_MINIMUM_TEMPERATURE_TO_SPREAD)
|
||||
@@ -200,7 +203,7 @@
|
||||
for(var/t in location.atmos_adjacent_turfs)
|
||||
var/turf/open/T = t
|
||||
if(!T.active_hotspot)
|
||||
T.hotspot_expose(radiated_temperature, CELL_VOLUME/4)
|
||||
T.hotspot_expose(radiated_temperature, CELL_VOLUME/4, flags_1 & HOLOGRAM_1)
|
||||
|
||||
else
|
||||
if(volume > CELL_VOLUME*0.4)
|
||||
@@ -224,7 +227,8 @@
|
||||
var/turf/open/T = loc
|
||||
if(istype(T) && T.active_hotspot == src)
|
||||
T.active_hotspot = null
|
||||
DestroyTurf()
|
||||
if(!(flags_1 & HOLOGRAM_1))
|
||||
DestroyTurf()
|
||||
return ..()
|
||||
|
||||
/obj/effect/hotspot/proc/DestroyTurf()
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
nullifyNode(i)
|
||||
|
||||
SSair.atmos_machinery -= src
|
||||
SSair.pipenets_needing_rebuilt -= src
|
||||
|
||||
dropContents()
|
||||
if(pipe_vision_img)
|
||||
|
||||
@@ -116,7 +116,7 @@
|
||||
if(node2)
|
||||
node2.atmosinit()
|
||||
node2.addMember(src)
|
||||
build_network()
|
||||
SSair.add_to_rebuild_queue(src)
|
||||
|
||||
return TRUE
|
||||
|
||||
|
||||
@@ -144,7 +144,7 @@
|
||||
var/datum/pipeline/parent = parents[i]
|
||||
if(!parent)
|
||||
stack_trace("Component is missing a pipenet! Rebuilding...")
|
||||
build_network()
|
||||
SSair.add_to_rebuild_queue(src)
|
||||
parent.update = 1
|
||||
|
||||
/obj/machinery/atmospherics/components/returnPipenets()
|
||||
|
||||
@@ -447,6 +447,6 @@
|
||||
if(node)
|
||||
node.atmosinit()
|
||||
node.addMember(src)
|
||||
build_network()
|
||||
SSair.add_to_rebuild_queue(src)
|
||||
|
||||
#undef CRYOMOBS
|
||||
|
||||
@@ -116,7 +116,7 @@
|
||||
if(node)
|
||||
node.atmosinit()
|
||||
node.addMember(src)
|
||||
build_network()
|
||||
SSair.add_to_rebuild_queue(src)
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/thermomachine/ui_status(mob/user)
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
nodes = list()
|
||||
for(var/obj/machinery/atmospherics/A in needs_nullifying)
|
||||
A.disconnect(src)
|
||||
A.build_network()
|
||||
SSair.add_to_rebuild_queue(A)
|
||||
|
||||
/obj/machinery/atmospherics/pipe/layer_manifold/proc/get_all_connected_nodes()
|
||||
return front_nodes + back_nodes + nodes
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
var/obj/machinery/atmospherics/oldN = nodes[i]
|
||||
..()
|
||||
if(oldN)
|
||||
oldN.build_network()
|
||||
SSair.add_to_rebuild_queue(oldN)
|
||||
|
||||
/obj/machinery/atmospherics/pipe/destroy_network()
|
||||
QDEL_NULL(parent)
|
||||
|
||||
@@ -159,7 +159,6 @@
|
||||
/obj/machinery/portable_atmospherics/canister/proto
|
||||
name = "prototype canister"
|
||||
|
||||
|
||||
/obj/machinery/portable_atmospherics/canister/proto/default
|
||||
name = "prototype canister"
|
||||
desc = "The best way to fix an atmospheric emergency... or the best way to introduce one."
|
||||
@@ -172,7 +171,6 @@
|
||||
can_min_release_pressure = (ONE_ATMOSPHERE / 30)
|
||||
prototype = TRUE
|
||||
|
||||
|
||||
/obj/machinery/portable_atmospherics/canister/proto/default/oxygen
|
||||
name = "prototype canister"
|
||||
desc = "A prototype canister for a prototype bike, what could go wrong?"
|
||||
@@ -181,8 +179,6 @@
|
||||
filled = 1
|
||||
release_pressure = ONE_ATMOSPHERE*2
|
||||
|
||||
|
||||
|
||||
/obj/machinery/portable_atmospherics/canister/New(loc, datum/gas_mixture/existing_mixture)
|
||||
..()
|
||||
if(existing_mixture)
|
||||
@@ -192,7 +188,7 @@
|
||||
pump = new(src, FALSE)
|
||||
pump.on = TRUE
|
||||
pump.stat = 0
|
||||
pump.build_network()
|
||||
SSair.add_to_rebuild_queue(pump)
|
||||
|
||||
update_icon()
|
||||
|
||||
@@ -208,6 +204,7 @@
|
||||
air_contents.gases[gas_type] = (maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature)
|
||||
if(starter_temp)
|
||||
air_contents.temperature = starter_temp
|
||||
|
||||
/obj/machinery/portable_atmospherics/canister/air/create_gas()
|
||||
air_contents.gases[/datum/gas/oxygen] = (O2STANDARD * maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature)
|
||||
air_contents.gases[/datum/gas/nitrogen] = (N2STANDARD * maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature)
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
pump = new(src, FALSE)
|
||||
pump.on = TRUE
|
||||
pump.stat = 0
|
||||
pump.build_network()
|
||||
SSair.add_to_rebuild_queue(pump)
|
||||
|
||||
/obj/machinery/portable_atmospherics/pump/Destroy()
|
||||
var/turf/T = get_turf(src)
|
||||
|
||||
@@ -66,12 +66,9 @@
|
||||
P.info += "Item: [pack.name]<br/>"
|
||||
P.info += "Contents: <br/>"
|
||||
P.info += "<ul>"
|
||||
for(var/atom/movable/AM in C.contents - P)
|
||||
if((P.errors & MANIFEST_ERROR_CONTENTS))
|
||||
if(prob(50))
|
||||
P.info += "<li>[AM.name]</li>"
|
||||
else
|
||||
continue
|
||||
for(var/atom/movable/AM in C.contents - P - C.lockerelectronics)
|
||||
if((P.errors & MANIFEST_ERROR_CONTENTS) && prob(50))
|
||||
continue
|
||||
P.info += "<li>[AM.name]</li>"
|
||||
P.info += "</ul>"
|
||||
P.info += "<h4>Stamp below to confirm receipt of goods:</h4>"
|
||||
|
||||
@@ -84,3 +84,6 @@
|
||||
var/next_keysend_reset = 0
|
||||
var/next_keysend_trip_reset = 0
|
||||
var/keysend_tripped = FALSE
|
||||
|
||||
/// Messages currently seen by this client
|
||||
var/list/seen_messages
|
||||
|
||||
@@ -48,6 +48,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
var/UI_style = null
|
||||
var/buttons_locked = FALSE
|
||||
var/hotkeys = FALSE
|
||||
var/chat_on_map = TRUE
|
||||
var/max_chat_length = CHAT_MESSAGE_MAX_LENGTH
|
||||
var/see_chat_non_mob = TRUE
|
||||
var/tgui_fancy = TRUE
|
||||
var/tgui_lock = TRUE
|
||||
var/windowflashing = TRUE
|
||||
@@ -824,6 +827,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
dat += "<b>UI Style:</b> <a href='?_src_=prefs;task=input;preference=ui'>[UI_style]</a><br>"
|
||||
dat += "<b>tgui Monitors:</b> <a href='?_src_=prefs;preference=tgui_lock'>[(tgui_lock) ? "Primary" : "All"]</a><br>"
|
||||
dat += "<b>tgui Style:</b> <a href='?_src_=prefs;preference=tgui_fancy'>[(tgui_fancy) ? "Fancy" : "No Frills"]</a><br>"
|
||||
dat += "<b>Show Runechat Chat Bubbles:</b> <a href='?_src_=prefs;preference=chat_on_map'>[chat_on_map ? "Enabled" : "Disabled"]</a><br>"
|
||||
dat += "<b>Runechat message char limit:</b> <a href='?_src_=prefs;preference=max_chat_length;task=input'>[max_chat_length]</a><br>"
|
||||
dat += "<b>See Runechat for non-mobs:</b> <a href='?_src_=prefs;preference=see_chat_non_mob'>[see_chat_non_mob ? "Enabled" : "Disabled"]</a><br>"
|
||||
dat += "<br>"
|
||||
dat += "<b>Action Buttons:</b> <a href='?_src_=prefs;preference=action_buttons'>[(buttons_locked) ? "Locked In Place" : "Unlocked"]</a><br>"
|
||||
dat += "<b>Keybindings:</b> <a href='?_src_=prefs;preference=hotkeys'>[(hotkeys) ? "Hotkeys" : "Default"]</a><br>"
|
||||
@@ -2134,6 +2140,10 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
var/pickedPDASkin = input(user, "Choose your PDA reskin.", "Character Preference", pda_skin) as null|anything in GLOB.pda_reskins
|
||||
if(pickedPDASkin)
|
||||
pda_skin = pickedPDASkin
|
||||
if ("max_chat_length")
|
||||
var/desiredlength = input(user, "Choose the max character length of shown Runechat messages. Valid range is 1 to [CHAT_MESSAGE_MAX_LENGTH] (default: [initial(max_chat_length)]))", "Character Preference", max_chat_length) as null|num
|
||||
if (!isnull(desiredlength))
|
||||
max_chat_length = clamp(desiredlength, 1, CHAT_MESSAGE_MAX_LENGTH)
|
||||
|
||||
if("hud_toggle_color")
|
||||
var/new_toggle_color = input(user, "Choose your HUD toggle flash color:", "Game Preference",hud_toggle_color) as color|null
|
||||
@@ -2236,6 +2246,10 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
winset(user, null, "input.focus=true input.background-color=[COLOR_INPUT_ENABLED] mainwindow.macro=default")
|
||||
else
|
||||
winset(user, null, "input.focus=true input.background-color=[COLOR_INPUT_ENABLED] mainwindow.macro=old_default")
|
||||
if("chat_on_map")
|
||||
chat_on_map = !chat_on_map
|
||||
if("see_chat_non_mob")
|
||||
see_chat_non_mob = !see_chat_non_mob
|
||||
if("action_buttons")
|
||||
buttons_locked = !buttons_locked
|
||||
if("tgui_fancy")
|
||||
|
||||
@@ -162,7 +162,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
if(malformed_hockeys[hockey])
|
||||
features["cock_shape"] = malformed_hockeys[hockey]
|
||||
features["cock_taur"] = TRUE
|
||||
|
||||
|
||||
if(current_version < 29)
|
||||
var/digestable
|
||||
var/devourable
|
||||
@@ -211,6 +211,9 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
S["lastchangelog"] >> lastchangelog
|
||||
S["UI_style"] >> UI_style
|
||||
S["hotkeys"] >> hotkeys
|
||||
S["chat_on_map"] >> chat_on_map
|
||||
S["max_chat_length"] >> max_chat_length
|
||||
S["see_chat_non_mob"] >> see_chat_non_mob
|
||||
S["tgui_fancy"] >> tgui_fancy
|
||||
S["tgui_lock"] >> tgui_lock
|
||||
S["buttons_locked"] >> buttons_locked
|
||||
@@ -266,6 +269,9 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
lastchangelog = sanitize_text(lastchangelog, initial(lastchangelog))
|
||||
UI_style = sanitize_inlist(UI_style, GLOB.available_ui_styles, GLOB.available_ui_styles[1])
|
||||
hotkeys = sanitize_integer(hotkeys, 0, 1, initial(hotkeys))
|
||||
chat_on_map = sanitize_integer(chat_on_map, 0, 1, initial(chat_on_map))
|
||||
max_chat_length = sanitize_integer(max_chat_length, 1, CHAT_MESSAGE_MAX_LENGTH, initial(max_chat_length))
|
||||
see_chat_non_mob = sanitize_integer(see_chat_non_mob, 0, 1, initial(see_chat_non_mob))
|
||||
tgui_fancy = sanitize_integer(tgui_fancy, 0, 1, initial(tgui_fancy))
|
||||
tgui_lock = sanitize_integer(tgui_lock, 0, 1, initial(tgui_lock))
|
||||
buttons_locked = sanitize_integer(buttons_locked, 0, 1, initial(buttons_locked))
|
||||
@@ -319,6 +325,9 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
WRITE_FILE(S["lastchangelog"], lastchangelog)
|
||||
WRITE_FILE(S["UI_style"], UI_style)
|
||||
WRITE_FILE(S["hotkeys"], hotkeys)
|
||||
WRITE_FILE(S["chat_on_map"], chat_on_map)
|
||||
WRITE_FILE(S["max_chat_length"], max_chat_length)
|
||||
WRITE_FILE(S["see_chat_non_mob"], see_chat_non_mob)
|
||||
WRITE_FILE(S["tgui_fancy"], tgui_fancy)
|
||||
WRITE_FILE(S["tgui_lock"], tgui_lock)
|
||||
WRITE_FILE(S["buttons_locked"], buttons_locked)
|
||||
|
||||
@@ -243,7 +243,7 @@
|
||||
icon_state = "knight_greyscale"
|
||||
item_state = "knight_greyscale"
|
||||
armor = list("melee" = 35, "bullet" = 10, "laser" = 10, "energy" = 10, "bomb" = 10, "bio" = 10, "rad" = 10, "fire" = 40, "acid" = 40)
|
||||
material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS //Can change color and add prefix
|
||||
material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS | MATERIAL_EFFECTS //Can change color and add prefix
|
||||
|
||||
/obj/item/clothing/head/helmet/skull
|
||||
name = "skull helmet"
|
||||
|
||||
@@ -285,7 +285,7 @@
|
||||
icon_state = "knight_greyscale"
|
||||
item_state = "knight_greyscale"
|
||||
armor = list("melee" = 35, "bullet" = 10, "laser" = 10, "energy" = 10, "bomb" = 10, "bio" = 10, "rad" = 10, "fire" = 40, "acid" = 40)
|
||||
material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS //Can change color and add prefix
|
||||
material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS | MATERIAL_EFFECTS //Can change color and add prefix
|
||||
|
||||
/obj/item/clothing/suit/armor/vest/durathread
|
||||
name = "makeshift vest"
|
||||
|
||||
@@ -101,7 +101,7 @@
|
||||
if((flags_inv & HIDEACCESSORY) || (A.flags_inv & HIDEACCESSORY))
|
||||
return TRUE
|
||||
|
||||
accessory_overlay = mutable_appearance('icons/mob/clothing/accessories.dmi', "attached_accessory.icon_state")
|
||||
accessory_overlay = mutable_appearance('icons/mob/clothing/accessories.dmi', attached_accessory.icon_state)
|
||||
accessory_overlay.alpha = attached_accessory.alpha
|
||||
accessory_overlay.color = attached_accessory.color
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
role_name = "random animal"
|
||||
var/animals = 1
|
||||
var/one = "one"
|
||||
/// Blacklisted mob_biotypes - Hey can we like, not have player controlled megafauna?
|
||||
var/blacklisted_biotypes = MOB_EPIC
|
||||
fakeable = TRUE
|
||||
|
||||
/datum/round_event/ghost_role/sentience/announce(fake)
|
||||
@@ -33,6 +35,8 @@
|
||||
var/turf/T = get_turf(L)
|
||||
if(!T || !is_station_level(T.z))
|
||||
continue
|
||||
if(L.mob_biotypes & blacklisted_biotypes) //hey can you don't
|
||||
continue
|
||||
if(!(L in GLOB.player_list) && !L.mind)
|
||||
potential += L
|
||||
|
||||
|
||||
@@ -701,6 +701,9 @@ GLOBAL_LIST_INIT(hallucination_list, list(
|
||||
target.client.images |= speech_overlay
|
||||
sleep(30)
|
||||
target.client.images.Remove(speech_overlay)
|
||||
var/spans = list(person.speech_span)
|
||||
if (target.client?.prefs.chat_on_map)
|
||||
target.create_chat_message(person, understood_language, chosen, spans, 0)
|
||||
else // Radio talk
|
||||
var/chosen = specific_message
|
||||
if(!chosen)
|
||||
|
||||
@@ -260,6 +260,7 @@ All foods are distributed among various categories. Use common sense.
|
||||
slice.desc = "[desc]"
|
||||
if(foodtype != initial(foodtype))
|
||||
slice.foodtype = foodtype //if something happens that overrode our food type, make sure the slice carries that over
|
||||
slice.adjust_food_quality(food_quality)
|
||||
|
||||
/obj/item/reagent_containers/food/snacks/proc/generate_trash(atom/location)
|
||||
if(trash)
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
slice.name = "raw [subjectname] cutlet"
|
||||
else if(subjectjob)
|
||||
slice.name = "raw [subjectjob] cutlet"
|
||||
slice.adjust_food_quality(food_quality)
|
||||
|
||||
/obj/item/reagent_containers/food/snacks/meat/slab/human/initialize_cooked_food(obj/item/reagent_containers/food/snacks/meat/S, cooking_efficiency)
|
||||
..()
|
||||
|
||||
@@ -184,9 +184,9 @@
|
||||
|
||||
for (var/i=1 to meat_produced)
|
||||
var/obj/item/reagent_containers/food/snacks/meat/slab/newmeat = new typeofmeat
|
||||
newmeat.adjust_food_quality(meat_quality)
|
||||
newmeat.name = "[sourcename] [newmeat.name]"
|
||||
if(istype(newmeat))
|
||||
newmeat.adjust_food_quality(meat_quality)
|
||||
newmeat.subjectname = sourcename
|
||||
newmeat.reagents.add_reagent (/datum/reagent/consumable/nutriment, sourcenutriment / meat_produced) // Thehehe. Fat guys go first
|
||||
if(sourcejob)
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
s.set_up(3, 1, T)
|
||||
s.start()
|
||||
T.temperature = 5000
|
||||
T.hotspot_expose(50000,50000,1)
|
||||
T.hotspot_expose(50000, 50000, TRUE, TRUE)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
department_flag = ENGSEC
|
||||
faction = "Station"
|
||||
total_positions = 3
|
||||
spawn_positions = 2
|
||||
spawn_positions = 3
|
||||
supervisors = "the chief engineer"
|
||||
selection_color = "#ff9b3d"
|
||||
exp_requirements = 60
|
||||
|
||||
@@ -75,31 +75,31 @@
|
||||
apply_healing_core(target, user)
|
||||
|
||||
/obj/item/organ/regenerative_core/proc/apply_healing_core(atom/target, mob/user)
|
||||
if(ishuman(target))
|
||||
var/mob/living/carbon/human/H = target
|
||||
if(inert)
|
||||
to_chat(user, "<span class='notice'>[src] has decayed and can no longer be used to heal.</span>")
|
||||
return
|
||||
else
|
||||
if(H.stat == DEAD)
|
||||
to_chat(user, "<span class='notice'>[src] are useless on the dead.</span>")
|
||||
return
|
||||
if(H != user)
|
||||
H.visible_message("[user] forces [H] to apply [src]... [H.p_they()] quickly regenerate all injuries!")
|
||||
SSblackbox.record_feedback("nested tally", "hivelord_core", 1, list("[type]", "used", "other"))
|
||||
else
|
||||
to_chat(user, "<span class='notice'>You start to smear [src] on yourself. It feels and smells disgusting, but you feel amazingly refreshed in mere moments.</span>")
|
||||
SSblackbox.record_feedback("nested tally", "hivelord_core", 1, list("[type]", "used", "self"))
|
||||
if(AmBloodsucker(H))
|
||||
H.revive(full_heal = FALSE)
|
||||
else
|
||||
H.revive(full_heal = TRUE)
|
||||
qdel(src)
|
||||
user.log_message("[user] used [src] to heal [H]! Wake the fuck up, Samurai!", LOG_ATTACK, color="green") //Logging for 'old' style legion core use, when clicking on a sprite of yourself or another.
|
||||
if(!user || !ishuman(target))
|
||||
return
|
||||
var/mob/living/carbon/human/H = target
|
||||
if(inert)
|
||||
to_chat(user, "<span class='notice'>[src] has decayed and can no longer be used to heal.</span>")
|
||||
return
|
||||
if(H.stat == DEAD)
|
||||
to_chat(user, "<span class='notice'>[src] are useless on the dead.</span>")
|
||||
return
|
||||
if(H != user)
|
||||
H.visible_message("[user] forces [H] to apply [src]... [H.p_they()] quickly regenerate all injuries!")
|
||||
SSblackbox.record_feedback("nested tally", "hivelord_core", 1, list("[type]", "used", "other"))
|
||||
else
|
||||
to_chat(user, "<span class='notice'>You start to smear [src] on yourself. It feels and smells disgusting, but you feel amazingly refreshed in mere moments.</span>")
|
||||
SSblackbox.record_feedback("nested tally", "hivelord_core", 1, list("[type]", "used", "self"))
|
||||
if(AmBloodsucker(H))
|
||||
H.revive(full_heal = FALSE)
|
||||
else
|
||||
H.revive(full_heal = TRUE)
|
||||
qdel(src)
|
||||
user.log_message("[user] used [src] to heal [H == user ? "[H.p_them()]self" : H]! Wake the fuck up, Samurai!", LOG_ATTACK, color="green") //Logging for 'old' style legion core use, when clicking on a sprite of yourself or another.
|
||||
|
||||
/obj/item/organ/regenerative_core/attack_self(mob/user) //Knouli's first hack! Allows for the use of the core in hand rather than needing to click on the target, yourself, to selfheal. Its a rip of the proc just above - but skips on distance check and only uses 'user' rather than 'target'
|
||||
. = ..()
|
||||
apply_healing_core(user)
|
||||
apply_healing_core(user, user)
|
||||
|
||||
|
||||
/obj/item/organ/regenerative_core/Insert(mob/living/carbon/M, special = 0, drop_if_replaced = TRUE)
|
||||
|
||||
@@ -70,7 +70,6 @@
|
||||
singular_name = "uranium ore chunk"
|
||||
points = 30
|
||||
custom_materials = list(/datum/material/uranium=MINERAL_MATERIAL_AMOUNT)
|
||||
material_flags = MATERIAL_NO_EFFECTS
|
||||
refined_type = /obj/item/stack/sheet/mineral/uranium
|
||||
|
||||
/obj/item/stack/ore/iron
|
||||
@@ -319,7 +318,7 @@ GLOBAL_LIST_INIT(sand_recipes, list(\
|
||||
throwforce = 2
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
custom_materials = list(/datum/material/iron = 400)
|
||||
material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
|
||||
material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS | MATERIAL_EFFECTS
|
||||
var/string_attached
|
||||
var/list/sideslist = list("heads","tails")
|
||||
var/cooldown = 0
|
||||
|
||||
@@ -55,7 +55,6 @@
|
||||
var/gender = NEUTER //Determines if the accessory will be skipped or included in random hair generations
|
||||
var/gender_specific //Something that can be worn by either gender, but looks different on each
|
||||
var/color_src = MUTCOLORS //Currently only used by mutantparts so don't worry about hair and stuff. This is the source that this accessory will get its color from. Default is MUTCOLOR, but can also be HAIR, FACEHAIR, EYECOLOR and 0 if none.
|
||||
var/hasinner //Decides if this sprite has an "inner" part, such as the fleshy parts on ears.
|
||||
var/locked = FALSE //Is this part locked from roundstart selection? Used for parts that apply effects
|
||||
var/dimension_x = 32
|
||||
var/dimension_y = 32
|
||||
|
||||
@@ -29,9 +29,10 @@
|
||||
/datum/sprite_accessory/ears/human/bigwolfinner
|
||||
name = "Big Wolf (ALT)"
|
||||
icon_state = "bigwolfinner"
|
||||
hasinner = 1
|
||||
icon = 'modular_citadel/icons/mob/mam_ears.dmi'
|
||||
color_src = MATRIXED
|
||||
extra = TRUE
|
||||
extra_color_src = NONE
|
||||
|
||||
/datum/sprite_accessory/ears/human/bigwolfdark
|
||||
name = "Dark Big Wolf"
|
||||
@@ -42,15 +43,17 @@
|
||||
/datum/sprite_accessory/ears/human/bigwolfinnerdark
|
||||
name = "Dark Big Wolf (ALT)"
|
||||
icon_state = "bigwolfinnerdark"
|
||||
hasinner = 1
|
||||
icon = 'modular_citadel/icons/mob/mam_ears.dmi'
|
||||
color_src = MATRIXED
|
||||
extra = TRUE
|
||||
extra_color_src = NONE
|
||||
|
||||
/datum/sprite_accessory/ears/cat
|
||||
name = "Cat"
|
||||
icon_state = "cat"
|
||||
hasinner = 1
|
||||
color_src = HAIR
|
||||
extra = TRUE
|
||||
extra_color_src = NONE
|
||||
|
||||
/datum/sprite_accessory/ears/human/cow
|
||||
name = "Cow"
|
||||
@@ -196,7 +199,8 @@
|
||||
/datum/sprite_accessory/mam_ears/bigwolfinner
|
||||
name = "Big Wolf (ALT)"
|
||||
icon_state = "bigwolfinner"
|
||||
hasinner = 1
|
||||
extra = TRUE
|
||||
extra_color_src = NONE
|
||||
|
||||
/datum/sprite_accessory/mam_ears/bigwolfdark
|
||||
name = "Dark Big Wolf"
|
||||
@@ -205,12 +209,13 @@
|
||||
/datum/sprite_accessory/mam_ears/bigwolfinnerdark
|
||||
name = "Dark Big Wolf (ALT)"
|
||||
icon_state = "bigwolfinnerdark"
|
||||
hasinner = 1
|
||||
extra = TRUE
|
||||
extra_color_src = NONE
|
||||
|
||||
/datum/sprite_accessory/mam_ears/cat
|
||||
name = "Cat"
|
||||
icon_state = "cat"
|
||||
hasinner = 1
|
||||
icon = 'icons/mob/mutant_bodyparts.dmi'
|
||||
color_src = HAIR
|
||||
|
||||
/datum/sprite_accessory/mam_ears/catbig
|
||||
|
||||
@@ -33,6 +33,9 @@
|
||||
else
|
||||
to_follow = V.source
|
||||
var/link = FOLLOW_LINK(src, to_follow)
|
||||
// Create map text prior to modifying message for goonchat
|
||||
if (client?.prefs.chat_on_map && (client.prefs.see_chat_non_mob || ismob(speaker)))
|
||||
create_chat_message(speaker, message_language, raw_message, spans, message_mode)
|
||||
// Recompose the message, because it's scrambled by default
|
||||
message = compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode, FALSE, source)
|
||||
to_chat(src, "[link] [message]")
|
||||
|
||||
@@ -540,10 +540,16 @@
|
||||
return 1
|
||||
|
||||
/mob/living/carbon/proc/spew_organ(power = 5, amt = 1)
|
||||
var/list/spillable_organs = list()
|
||||
for(var/A in internal_organs)
|
||||
var/obj/item/organ/O = A
|
||||
if(!(O.organ_flags & ORGAN_NO_DISMEMBERMENT))
|
||||
spillable_organs += O
|
||||
for(var/i in 1 to amt)
|
||||
if(!internal_organs.len)
|
||||
if(!spillable_organs.len)
|
||||
break //Guess we're out of organs!
|
||||
var/obj/item/organ/guts = pick(internal_organs)
|
||||
var/obj/item/organ/guts = pick(spillable_organs)
|
||||
spillable_organs -= guts
|
||||
var/turf/T = get_turf(src)
|
||||
guts.Remove()
|
||||
guts.forceMove(T)
|
||||
@@ -551,6 +557,7 @@
|
||||
guts.throw_at(throw_target, power, 4, src)
|
||||
|
||||
|
||||
|
||||
/mob/living/carbon/fully_replace_character_name(oldname,newname)
|
||||
..()
|
||||
if(dna)
|
||||
|
||||
@@ -41,18 +41,14 @@
|
||||
if(no_brain && istype(O, /obj/item/organ/brain))
|
||||
qdel(O) //so the brain isn't transfered to the head when the head drops.
|
||||
continue
|
||||
var/org_zone = check_zone(O.zone) //both groin and chest organs.
|
||||
if(org_zone == BODY_ZONE_CHEST)
|
||||
if(!(O.organ_flags & ORGAN_NO_DISMEMBERMENT) && check_zone(O.zone) == BODY_ZONE_CHEST)
|
||||
O.Remove()
|
||||
O.forceMove(Tsec)
|
||||
O.throw_at(get_edge_target_turf(src,pick(GLOB.alldirs)),rand(1,3),5)
|
||||
else
|
||||
for(var/X in internal_organs)
|
||||
var/obj/item/organ/I = X
|
||||
if(no_brain && istype(I, /obj/item/organ/brain))
|
||||
qdel(I)
|
||||
continue
|
||||
if(no_organs && !istype(I, /obj/item/organ/brain))
|
||||
if(I.organ_flags & ORGAN_NO_DISMEMBERMENT || (no_brain && istype(I, /obj/item/organ/brain)) || (no_organs && !istype(I, /obj/item/organ/brain)))
|
||||
qdel(I)
|
||||
continue
|
||||
I.Remove()
|
||||
|
||||
@@ -909,22 +909,6 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
|
||||
standing += accessory_overlay
|
||||
|
||||
if(S.hasinner)
|
||||
var/mutable_appearance/inner_accessory_overlay = mutable_appearance(S.icon, layer = -layer)
|
||||
if(S.gender_specific)
|
||||
inner_accessory_overlay.icon_state = "[g]_[bodypart]inner_[S.icon_state]_[layertext]"
|
||||
else
|
||||
inner_accessory_overlay.icon_state = "m_[bodypart]inner_[S.icon_state]_[layertext]"
|
||||
|
||||
if(S.center)
|
||||
inner_accessory_overlay = center_image(inner_accessory_overlay, S.dimension_x, S.dimension_y)
|
||||
|
||||
if(OFFSET_MUTPARTS in H.dna.species.offset_features)
|
||||
inner_accessory_overlay.pixel_x += H.dna.species.offset_features[OFFSET_MUTPARTS][1]
|
||||
inner_accessory_overlay.pixel_y += H.dna.species.offset_features[OFFSET_MUTPARTS][2]
|
||||
|
||||
standing += inner_accessory_overlay
|
||||
|
||||
if(S.extra) //apply the extra overlay, if there is one
|
||||
var/mutable_appearance/extra_accessory_overlay = mutable_appearance(S.icon, layer = -layer)
|
||||
if(S.gender_specific)
|
||||
|
||||
@@ -111,7 +111,7 @@
|
||||
if(pickupTarget)
|
||||
if(restrained() || blacklistItems[pickupTarget] || HAS_TRAIT(pickupTarget, TRAIT_NODROP))
|
||||
pickupTarget = null
|
||||
else
|
||||
else if(!isobj(loc) || istype(loc, /obj/item/clothing/head/mob_holder))
|
||||
pickupTimer++
|
||||
if(pickupTimer >= 4)
|
||||
blacklistItems[pickupTarget] ++
|
||||
@@ -126,10 +126,8 @@
|
||||
pickupTarget = null
|
||||
pickupTimer = 0
|
||||
else if(ismob(pickupTarget.loc)) // in someones hand
|
||||
if(istype(pickupTarget, /obj/item/clothing/head/mob_holder/))
|
||||
var/obj/item/clothing/head/mob_holder/h = pickupTarget
|
||||
if(h && h.held_mob==src)
|
||||
return//dont let them pickpocket themselves
|
||||
if(istype(pickupTarget, /obj/item/clothing/head/mob_holder))
|
||||
return//dont let them pickpocket themselves or hold other monkys.
|
||||
var/mob/M = pickupTarget.loc
|
||||
if(!pickpocketing)
|
||||
pickpocketing = TRUE
|
||||
|
||||
@@ -237,6 +237,10 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
|
||||
deaf_message = "<span class='notice'>You can't hear yourself!</span>"
|
||||
deaf_type = 2 // Since you should be able to hear yourself without looking
|
||||
|
||||
// Create map text prior to modifying message for goonchat
|
||||
if (client?.prefs.chat_on_map && stat != UNCONSCIOUS && (client.prefs.see_chat_non_mob || ismob(speaker)) && can_hear())
|
||||
create_chat_message(speaker, message_language, raw_message, spans, message_mode)
|
||||
|
||||
// Recompose message for AI hrefs, language incomprehension.
|
||||
message = compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode, FALSE, source)
|
||||
|
||||
@@ -289,7 +293,7 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
|
||||
//speech bubble
|
||||
var/list/speech_bubble_recipients = list()
|
||||
for(var/mob/M in listening)
|
||||
if(M.client)
|
||||
if(M.client && !M.client.prefs.chat_on_map)
|
||||
speech_bubble_recipients.Add(M.client)
|
||||
var/image/I = image('icons/mob/talk.dmi', src, "[bubble_type][say_test(message)]", FLY_LAYER)
|
||||
I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA
|
||||
|
||||
@@ -822,21 +822,21 @@
|
||||
return get_dist(src, A) <= max(viewscale[1]*0.5,viewscale[2]*0.5)
|
||||
|
||||
/mob/living/silicon/ai/proc/relay_speech(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode)
|
||||
raw_message = lang_treat(speaker, message_language, raw_message, spans, message_mode)
|
||||
var/treated_message = lang_treat(speaker, message_language, raw_message, spans, message_mode)
|
||||
var/start = "Relayed Speech: "
|
||||
var/namepart = "[speaker.GetVoice()][speaker.get_alt_name()]"
|
||||
var/hrefpart = "<a href='?src=[REF(src)];track=[html_encode(namepart)]'>"
|
||||
var/jobpart
|
||||
var/jobpart = "Unknown"
|
||||
|
||||
if (iscarbon(speaker))
|
||||
var/mob/living/carbon/S = speaker
|
||||
if(S.job)
|
||||
jobpart = "[S.job]"
|
||||
else
|
||||
jobpart = "Unknown"
|
||||
|
||||
var/rendered = "<i><span class='game say'>[start]<span class='name'>[hrefpart][namepart] ([jobpart])</a> </span><span class='message'>[raw_message]</span></span></i>"
|
||||
var/rendered = "<i><span class='game say'>[start]<span class='name'>[hrefpart][namepart] ([jobpart])</a> </span><span class='message'>[treated_message]</span></span></i>"
|
||||
|
||||
if (client?.prefs.chat_on_map && (client.prefs.see_chat_non_mob || ismob(speaker)))
|
||||
create_chat_message(speaker, message_language, raw_message, spans, message_mode)
|
||||
show_message(rendered, MSG_AUDIBLE)
|
||||
|
||||
/mob/living/silicon/ai/fully_replace_character_name(oldname,newname)
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -28,11 +28,19 @@
|
||||
var/bitcoinproduction_drain = 0.15
|
||||
var/bitcoinmining = FALSE
|
||||
rad_insulation = RAD_EXTREME_INSULATION
|
||||
var/obj/item/radio/Radio
|
||||
|
||||
/obj/machinery/power/rad_collector/anchored
|
||||
anchored = TRUE
|
||||
|
||||
/obj/machinery/power/rad_collector/Initialize()
|
||||
. = ..()
|
||||
Radio = new /obj/item/radio(src)
|
||||
Radio.listening = 0
|
||||
Radio.set_frequency(FREQ_ENGINEERING)
|
||||
|
||||
/obj/machinery/power/rad_collector/Destroy()
|
||||
QDEL_NULL(Radio)
|
||||
return ..()
|
||||
|
||||
/obj/machinery/power/rad_collector/process()
|
||||
@@ -42,6 +50,7 @@
|
||||
if(!loaded_tank.air_contents.gases[/datum/gas/plasma])
|
||||
investigate_log("<font color='red'>out of fuel</font>.", INVESTIGATE_SINGULO)
|
||||
playsound(src, 'sound/machines/ding.ogg', 50, 1)
|
||||
Radio.talk_into(src, "Insufficient plasma in [get_area(src)] [src], ejecting \the [loaded_tank].", FREQ_ENGINEERING)
|
||||
eject()
|
||||
else
|
||||
var/gasdrained = min(powerproduction_drain*drainratio,loaded_tank.air_contents.gases[/datum/gas/plasma])
|
||||
@@ -55,6 +64,7 @@
|
||||
else if(is_station_level(z) && SSresearch.science_tech)
|
||||
if(!loaded_tank.air_contents.gases[/datum/gas/tritium] || !loaded_tank.air_contents.gases[/datum/gas/oxygen])
|
||||
playsound(src, 'sound/machines/ding.ogg', 50, 1)
|
||||
Radio.talk_into(src, "Insufficient oxygen and tritium in [get_area(src)] [src] to produce research points, ejecting \the [loaded_tank].", FREQ_ENGINEERING)
|
||||
eject()
|
||||
else
|
||||
var/gasdrained = bitcoinproduction_drain*drainratio
|
||||
|
||||
@@ -245,7 +245,6 @@
|
||||
icon_state = "beakerbluespace"
|
||||
custom_materials = list(/datum/material/glass = 5000, /datum/material/plasma = 3000, /datum/material/diamond = 1000, /datum/material/bluespace = 1000)
|
||||
volume = 300
|
||||
material_flags = MATERIAL_NO_EFFECTS
|
||||
amount_per_transfer_from_this = 10
|
||||
possible_transfer_amounts = list(5,10,15,20,25,30,50,100,300)
|
||||
container_HP = 5
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -95,11 +95,9 @@
|
||||
message_admins("[ADMIN_LOOKUPFLW(user)] has built [amount] of [path] at a [src]([type]).")
|
||||
for(var/i in 1 to amount)
|
||||
var/obj/O = new path(get_turf(src))
|
||||
if(efficient_with(O.type) && isitem(O))
|
||||
var/obj/item/I = O
|
||||
I.material_flags |= MATERIAL_NO_EFFECTS //Find a better way to do this.
|
||||
I.set_custom_materials(matlist.Copy())
|
||||
I.rnd_crafted(src)
|
||||
if(efficient_with(O.type))
|
||||
O.set_custom_materials(matlist.Copy())
|
||||
O.rnd_crafted(src)
|
||||
SSblackbox.record_feedback("nested tally", "item_printed", amount, list("[type]", "[path]"))
|
||||
investigate_log("[key_name(user)] built [amount] of [path] at [src]([type]).", INVESTIGATE_RESEARCH)
|
||||
|
||||
|
||||
@@ -249,7 +249,7 @@ All ShuttleMove procs go here
|
||||
A.atmosinit()
|
||||
if(A.returnPipenet())
|
||||
A.addMember(src)
|
||||
build_network()
|
||||
SSair.add_to_rebuild_queue(src)
|
||||
else
|
||||
// atmosinit() calls update_icon(), so we don't need to call it
|
||||
update_icon()
|
||||
|
||||
@@ -60,8 +60,7 @@
|
||||
playsound(get_turf(C), 'sound/misc/splort.ogg', 80, 1)
|
||||
for(var/X in C.internal_organs)
|
||||
var/obj/item/organ/O = X
|
||||
var/org_zone = check_zone(O.zone)
|
||||
if(org_zone != BODY_ZONE_CHEST)
|
||||
if(O.organ_flags & ORGAN_NO_DISMEMBERMENT || check_zone(O.zone) != BODY_ZONE_CHEST)
|
||||
continue
|
||||
O.Remove()
|
||||
O.forceMove(T)
|
||||
@@ -295,6 +294,7 @@
|
||||
O.Insert(C)
|
||||
|
||||
update_bodypart_damage_state()
|
||||
update_disabled()
|
||||
|
||||
C.updatehealth()
|
||||
C.update_body()
|
||||
@@ -302,7 +302,6 @@
|
||||
C.update_damage_overlays()
|
||||
C.update_mobility()
|
||||
|
||||
|
||||
/obj/item/bodypart/head/attach_limb(mob/living/carbon/C, special)
|
||||
//Transfer some head appearance vars over
|
||||
if(brain)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
name = "arm-mounted implant"
|
||||
desc = "You shouldn't see this! Adminhelp and report this as an issue on github!"
|
||||
zone = BODY_ZONE_R_ARM
|
||||
organ_flags = ORGAN_SYNTHETIC
|
||||
icon_state = "implant-toolkit"
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
actions_types = list(/datum/action/item_action/organ_action/toggle)
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
var/zone = BODY_ZONE_CHEST
|
||||
var/slot
|
||||
// DO NOT add slots with matching names to different zones - it will break internal_organs_slot list!
|
||||
var/organ_flags = 0
|
||||
var/organ_flags = NONE
|
||||
var/maxHealth = STANDARD_ORGAN_THRESHOLD
|
||||
var/damage = 0 //total damage this organ has sustained
|
||||
///Healing factor and decay factor function on % of maxhealth, and do not work by applying a static number per tick
|
||||
|
||||
@@ -89,6 +89,7 @@
|
||||
desc = "A reverse engineered clockwork slab. Is this really a good idea?."
|
||||
item = /obj/item/clockwork/slab/traitor
|
||||
cost = 20
|
||||
player_minimum = 20
|
||||
refundable = TRUE
|
||||
restricted_roles = list("Chaplain")
|
||||
|
||||
@@ -97,6 +98,7 @@
|
||||
desc = "A replica of a Nar'sian tome. This is probably a bad idea.."
|
||||
item = /obj/item/tome/traitor
|
||||
cost = 20
|
||||
player_minimum = 20
|
||||
refundable = TRUE
|
||||
restricted_roles = list("Chaplain")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user