Merge pull request #15486 from SandPoot/kills-cache
Solves issues with overlays by updating the whole subsystem
This commit is contained in:
@@ -33,8 +33,6 @@ GLOBAL_LIST_INIT(bitflags, list(
|
||||
#define CONDUCT_1 (1<<5)
|
||||
///For machines and structures that should not break into parts, eg, holodeck stuff.
|
||||
#define NODECONSTRUCT_1 (1<<7)
|
||||
///Atom queued to SSoverlay.
|
||||
#define OVERLAY_QUEUED_1 (1<<8)
|
||||
///Item has priority to check when entering or leaving.
|
||||
#define ON_BORDER_1 (1<<9)
|
||||
///Whether or not this atom shows screentips when hovered over
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// A reasonable number of maximum overlays an object needs
|
||||
// If you think you need more, rethink it
|
||||
#define MAX_ATOM_OVERLAYS 100
|
||||
|
||||
/// Checks if an atom has reached the overlay limit, and make a loud error if it does.
|
||||
#define VALIDATE_OVERLAY_LIMIT(changed_on) \
|
||||
if(length(changed_on.overlays) >= MAX_ATOM_OVERLAYS) { \
|
||||
var/text_lays = overlays2text(changed_on.overlays); \
|
||||
stack_trace("Too many overlays on [changed_on.type] - [length(changed_on.overlays)], refusing to update and cutting.\
|
||||
\n What follows is a printout of all existing overlays at the time of the overflow \n[text_lays]"); \
|
||||
changed_on.overlays.Cut(); \
|
||||
changed_on.add_overlay(mutable_appearance('icons/testing/greyscale_error.dmi')); \
|
||||
} \
|
||||
|
||||
/// Performs any operations that ought to run after an appearance change
|
||||
/* #define POST_OVERLAY_CHANGE(changed_on) \
|
||||
if(alternate_appearances) { \
|
||||
for(var/I in changed_on.alternate_appearances){\
|
||||
var/datum/atom_hud/alternate_appearance/AA = changed_on.alternate_appearances[I];\
|
||||
if(AA.transfer_overlays){\
|
||||
AA.copy_overlays(changed_on, TRUE);\
|
||||
}\
|
||||
} \
|
||||
} */
|
||||
@@ -228,25 +228,6 @@
|
||||
#define SSMOBS_DT (SSmobs.wait/10)
|
||||
#define SSOBJ_DT (SSobj.wait/10)
|
||||
|
||||
//! ## Overlays subsystem
|
||||
|
||||
///Compile all the overlays for an atom from the cache lists
|
||||
// |= on overlays is not actually guaranteed to not add same appearances but we're optimistically using it anyway.
|
||||
#define COMPILE_OVERLAYS(A)\
|
||||
do {\
|
||||
var/list/ad = A.add_overlays;\
|
||||
var/list/rm = A.remove_overlays;\
|
||||
if(LAZYLEN(rm)){\
|
||||
A.overlays -= rm;\
|
||||
rm.Cut();\
|
||||
}\
|
||||
if(LAZYLEN(ad)){\
|
||||
A.overlays |= ad;\
|
||||
ad.Cut();\
|
||||
}\
|
||||
A.flags_1 &= ~OVERLAY_QUEUED_1;\
|
||||
} while(FALSE)
|
||||
|
||||
/**
|
||||
Create a new timer and add it to the queue.
|
||||
* Arguments:
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
var/icon/combined = new
|
||||
for(var/d in GLOB.cardinals)
|
||||
mannequin.setDir(d)
|
||||
COMPILE_OVERLAYS(mannequin)
|
||||
CHECK_TICK
|
||||
var/icon/capture = getFlatIcon(mannequin)
|
||||
CHECK_TICK
|
||||
|
||||
@@ -1065,7 +1065,6 @@ GLOBAL_LIST_EMPTY(friendly_animal_types)
|
||||
|
||||
|
||||
var/icon/out_icon = icon('icons/effects/effects.dmi', "nothing")
|
||||
COMPILE_OVERLAYS(body)
|
||||
for(var/D in showDirs)
|
||||
var/icon/partial = getFlatIcon(body, defdir = D, no_anim = no_anim)
|
||||
out_icon.Insert(partial,dir=D)
|
||||
|
||||
@@ -79,10 +79,6 @@
|
||||
#define CBT
|
||||
#endif
|
||||
|
||||
// A reasonable number of maximum overlays an object needs
|
||||
// If you think you need more, rethink it
|
||||
#define MAX_ATOM_OVERLAYS 100
|
||||
|
||||
#if !defined(CBT) && !defined(SPACEMAN_DMM)
|
||||
#warn Building with Dream Maker is no longer supported and will result in errors.
|
||||
#warn In order to build, run BUILD.bat in the root directory.
|
||||
|
||||
@@ -164,7 +164,6 @@ DEFINE_BITFIELD(flags_1, list(
|
||||
"NO_LAVA_GEN_1" = NO_LAVA_GEN_1,
|
||||
"NO_RUINS_1" = NO_RUINS_1,
|
||||
"NO_SCREENTIPS_1" = NO_SCREENTIPS_1,
|
||||
"OVERLAY_QUEUED_1" = OVERLAY_QUEUED_1,
|
||||
"ON_BORDER_1" = ON_BORDER_1,
|
||||
"PREVENT_CLICK_UNDER_1" = PREVENT_CLICK_UNDER_1,
|
||||
"PREVENT_CONTENTS_EXPLOSION_1" = PREVENT_CONTENTS_EXPLOSION_1,
|
||||
|
||||
@@ -1,187 +1,114 @@
|
||||
SUBSYSTEM_DEF(overlays)
|
||||
name = "Overlay"
|
||||
flags = SS_TICKER
|
||||
wait = 1
|
||||
priority = FIRE_PRIORITY_OVERLAYS
|
||||
init_order = INIT_ORDER_OVERLAY
|
||||
|
||||
var/list/queue
|
||||
flags = SS_NO_FIRE|SS_NO_INIT
|
||||
var/list/stats
|
||||
var/list/overlay_icon_state_caches
|
||||
var/list/overlay_icon_cache
|
||||
|
||||
/datum/controller/subsystem/overlays/PreInit()
|
||||
overlay_icon_state_caches = list()
|
||||
overlay_icon_cache = list()
|
||||
queue = list()
|
||||
stats = list()
|
||||
|
||||
/datum/controller/subsystem/overlays/Initialize()
|
||||
initialized = TRUE
|
||||
fire(mc_check = FALSE)
|
||||
return ..()
|
||||
|
||||
|
||||
/datum/controller/subsystem/overlays/stat_entry(msg)
|
||||
msg = "Ov:[length(queue)]"
|
||||
return ..()
|
||||
|
||||
|
||||
/datum/controller/subsystem/overlays/Shutdown()
|
||||
text2file(render_stats(stats), "[GLOB.log_directory]/overlay.log")
|
||||
|
||||
|
||||
/datum/controller/subsystem/overlays/Recover()
|
||||
overlay_icon_state_caches = SSoverlays.overlay_icon_state_caches
|
||||
overlay_icon_cache = SSoverlays.overlay_icon_cache
|
||||
queue = SSoverlays.queue
|
||||
stats = SSoverlays.stats
|
||||
|
||||
|
||||
/datum/controller/subsystem/overlays/fire(resumed = FALSE, mc_check = TRUE)
|
||||
var/list/queue = src.queue
|
||||
var/static/count = 0
|
||||
if (count)
|
||||
var/c = count
|
||||
count = 0 //so if we runtime on the Cut, we don't try again.
|
||||
queue.Cut(1,c+1)
|
||||
|
||||
for (var/thing in queue)
|
||||
count++
|
||||
if(thing)
|
||||
var/atom/A = thing
|
||||
if(A.overlays.len >= MAX_ATOM_OVERLAYS)
|
||||
//Break it real GOOD
|
||||
stack_trace("Too many overlays on [A.type] - [A.overlays.len], refusing to update and cutting")
|
||||
A.overlays.Cut()
|
||||
continue
|
||||
STAT_START_STOPWATCH
|
||||
COMPILE_OVERLAYS(A)
|
||||
UNSETEMPTY(A.add_overlays)
|
||||
UNSETEMPTY(A.remove_overlays)
|
||||
STAT_STOP_STOPWATCH
|
||||
STAT_LOG_ENTRY(stats, A.type)
|
||||
if(mc_check)
|
||||
if(MC_TICK_CHECK)
|
||||
break
|
||||
else
|
||||
CHECK_TICK
|
||||
|
||||
if (count)
|
||||
queue.Cut(1,count+1)
|
||||
count = 0
|
||||
/// Converts an overlay list into text for debug printing
|
||||
/// Of note: overlays aren't actually mutable appearances, they're just appearances
|
||||
/// Don't have access to that type tho, so this is the best you're gonna get
|
||||
/proc/overlays2text(list/overlays)
|
||||
var/list/unique_overlays = list()
|
||||
// As anything because we're basically doing type coerrsion, rather then actually filtering for mutable apperances
|
||||
for(var/mutable_appearance/overlay as anything in overlays)
|
||||
var/key = "[overlay.icon]-[overlay.icon_state]-[overlay.dir]"
|
||||
unique_overlays[key] += 1
|
||||
var/list/output_text = list()
|
||||
for(var/key in unique_overlays)
|
||||
output_text += "([key]) = [unique_overlays[key]]"
|
||||
return output_text.Join("\n")
|
||||
|
||||
/proc/iconstate2appearance(icon, iconstate)
|
||||
var/static/image/stringbro = new()
|
||||
var/list/icon_states_cache = SSoverlays.overlay_icon_state_caches
|
||||
var/list/cached_icon = icon_states_cache[icon]
|
||||
if (cached_icon)
|
||||
var/cached_appearance = cached_icon["[iconstate]"]
|
||||
if (cached_appearance)
|
||||
return cached_appearance
|
||||
stringbro.icon = icon
|
||||
stringbro.icon_state = iconstate
|
||||
if (!cached_icon) //not using the macro to save an associated lookup
|
||||
cached_icon = list()
|
||||
icon_states_cache[icon] = cached_icon
|
||||
var/cached_appearance = stringbro.appearance
|
||||
cached_icon["[iconstate]"] = cached_appearance
|
||||
return cached_appearance
|
||||
return stringbro.appearance
|
||||
|
||||
/proc/icon2appearance(icon)
|
||||
var/static/image/iconbro = new()
|
||||
var/list/icon_cache = SSoverlays.overlay_icon_cache
|
||||
. = icon_cache[icon]
|
||||
if (!.)
|
||||
iconbro.icon = icon
|
||||
. = iconbro.appearance
|
||||
icon_cache[icon] = .
|
||||
iconbro.icon = icon
|
||||
return iconbro.appearance
|
||||
|
||||
/atom/proc/build_appearance_list(old_overlays)
|
||||
var/static/image/appearance_bro = new()
|
||||
var/list/new_overlays = list()
|
||||
if (!islist(old_overlays))
|
||||
old_overlays = list(old_overlays)
|
||||
for (var/overlay in old_overlays)
|
||||
/atom/proc/build_appearance_list(list/build_overlays)
|
||||
if (!islist(build_overlays))
|
||||
build_overlays = list(build_overlays)
|
||||
for (var/overlay in build_overlays)
|
||||
if(!overlay)
|
||||
build_overlays -= overlay
|
||||
continue
|
||||
if (istext(overlay))
|
||||
new_overlays += iconstate2appearance(icon, overlay)
|
||||
#ifdef UNIT_TESTS
|
||||
// This is too expensive to run normally but running it during CI is a good test
|
||||
var/list/icon_states_available = icon_states(icon)
|
||||
if(!(overlay in icon_states_available))
|
||||
var/icon_file = "[icon]" || "Unknown Generated Icon"
|
||||
stack_trace("Invalid overlay: Icon object '[icon_file]' [REF(icon)] used in '[src]' [type] is missing icon state [overlay].")
|
||||
continue
|
||||
#endif
|
||||
var/index = build_overlays.Find(overlay)
|
||||
build_overlays[index] = iconstate2appearance(icon, overlay)
|
||||
else if(isicon(overlay))
|
||||
new_overlays += icon2appearance(overlay)
|
||||
else
|
||||
if(isloc(overlay))
|
||||
var/atom/A = overlay
|
||||
if (A.flags_1 & OVERLAY_QUEUED_1)
|
||||
COMPILE_OVERLAYS(A)
|
||||
appearance_bro.appearance = overlay //this works for images and atoms too!
|
||||
if(!ispath(overlay))
|
||||
var/image/I = overlay
|
||||
appearance_bro.dir = I.dir
|
||||
new_overlays += appearance_bro.appearance
|
||||
return new_overlays
|
||||
var/index = build_overlays.Find(overlay)
|
||||
build_overlays[index] = icon2appearance(overlay)
|
||||
return build_overlays
|
||||
|
||||
#define NOT_QUEUED_ALREADY (!(flags_1 & OVERLAY_QUEUED_1))
|
||||
#define QUEUE_FOR_COMPILE flags_1 |= OVERLAY_QUEUED_1; SSoverlays.queue += src;
|
||||
/atom/proc/cut_overlays()
|
||||
LAZYINITLIST(remove_overlays)
|
||||
remove_overlays = overlays.Copy()
|
||||
add_overlays = null
|
||||
STAT_START_STOPWATCH
|
||||
overlays = null
|
||||
// POST_OVERLAY_CHANGE(src)
|
||||
STAT_STOP_STOPWATCH
|
||||
STAT_LOG_ENTRY(SSoverlays.stats, type)
|
||||
|
||||
//If not already queued for work and there are overlays to remove
|
||||
if(NOT_QUEUED_ALREADY && remove_overlays.len)
|
||||
QUEUE_FOR_COMPILE
|
||||
|
||||
/atom/proc/cut_overlay(list/overlays)
|
||||
/atom/proc/cut_overlay(list/remove_overlays)
|
||||
if(!overlays)
|
||||
return
|
||||
overlays = build_appearance_list(overlays)
|
||||
LAZYINITLIST(add_overlays)
|
||||
LAZYINITLIST(remove_overlays)
|
||||
var/a_len = add_overlays.len
|
||||
var/r_len = remove_overlays.len
|
||||
remove_overlays += overlays
|
||||
add_overlays -= overlays
|
||||
STAT_START_STOPWATCH
|
||||
overlays -= build_appearance_list(remove_overlays)
|
||||
// POST_OVERLAY_CHANGE(src)
|
||||
STAT_STOP_STOPWATCH
|
||||
STAT_LOG_ENTRY(SSoverlays.stats, type)
|
||||
|
||||
var/fa_len = add_overlays.len
|
||||
var/fr_len = remove_overlays.len
|
||||
|
||||
//If not already queued and there is work to be done
|
||||
if(NOT_QUEUED_ALREADY && (fa_len != a_len || fr_len != r_len ))
|
||||
QUEUE_FOR_COMPILE
|
||||
UNSETEMPTY(add_overlays)
|
||||
|
||||
/atom/proc/add_overlay(list/overlays)
|
||||
/atom/proc/add_overlay(list/add_overlays)
|
||||
if(!overlays)
|
||||
return
|
||||
STAT_START_STOPWATCH
|
||||
overlays += build_appearance_list(add_overlays)
|
||||
VALIDATE_OVERLAY_LIMIT(src)
|
||||
// POST_OVERLAY_CHANGE(src)
|
||||
STAT_STOP_STOPWATCH
|
||||
STAT_LOG_ENTRY(SSoverlays.stats, type)
|
||||
|
||||
overlays = build_appearance_list(overlays)
|
||||
|
||||
LAZYINITLIST(add_overlays) //always initialized after this point
|
||||
var/a_len = add_overlays.len
|
||||
|
||||
add_overlays += overlays
|
||||
var/fa_len = add_overlays.len
|
||||
if(NOT_QUEUED_ALREADY && fa_len != a_len)
|
||||
QUEUE_FOR_COMPILE
|
||||
|
||||
/atom/proc/copy_overlays(atom/other, cut_old) //copys our_overlays from another atom
|
||||
/atom/proc/copy_overlays(atom/other, cut_old) //copys our_overlays from another atom
|
||||
if(!other)
|
||||
if(cut_old)
|
||||
cut_overlays()
|
||||
return
|
||||
|
||||
STAT_START_STOPWATCH
|
||||
var/list/cached_other = other.overlays.Copy()
|
||||
if(cached_other)
|
||||
if(cut_old || !LAZYLEN(overlays))
|
||||
remove_overlays = overlays
|
||||
add_overlays = cached_other
|
||||
if(NOT_QUEUED_ALREADY)
|
||||
QUEUE_FOR_COMPILE
|
||||
else if(cut_old)
|
||||
cut_overlays()
|
||||
|
||||
#undef NOT_QUEUED_ALREADY
|
||||
#undef QUEUE_FOR_COMPILE
|
||||
if(cut_old)
|
||||
if(cached_other)
|
||||
overlays = cached_other
|
||||
else
|
||||
overlays = null
|
||||
VALIDATE_OVERLAY_LIMIT(src)
|
||||
// POST_OVERLAY_CHANGE(src)
|
||||
STAT_STOP_STOPWATCH
|
||||
STAT_LOG_ENTRY(SSoverlays.stats, type)
|
||||
else if(cached_other)
|
||||
overlays += cached_other
|
||||
VALIDATE_OVERLAY_LIMIT(src)
|
||||
// POST_OVERLAY_CHANGE(src)
|
||||
STAT_STOP_STOPWATCH
|
||||
STAT_LOG_ENTRY(SSoverlays.stats, type)
|
||||
|
||||
//TODO: Better solution for these?
|
||||
/image/proc/add_overlay(x)
|
||||
@@ -207,3 +134,100 @@ SUBSYSTEM_DEF(overlays)
|
||||
overlays |= cached_other
|
||||
else if(cut_old)
|
||||
cut_overlays()
|
||||
|
||||
// Debug procs
|
||||
|
||||
/atom
|
||||
/// List of overlay "keys" (info about the appearance) -> mutable versions of static appearances
|
||||
/// Drawn from the overlays list
|
||||
var/list/realized_overlays
|
||||
/// List of underlay "keys" (info about the appearance) -> mutable versions of static appearances
|
||||
/// Drawn from the underlays list
|
||||
var/list/realized_underlays
|
||||
|
||||
/image
|
||||
/// List of overlay "keys" (info about the appearance) -> mutable versions of static appearances
|
||||
/// Drawn from the overlays list
|
||||
var/list/realized_overlays
|
||||
/// List of underlay "keys" (info about the appearance) -> mutable versions of static appearances
|
||||
/// Drawn from the underlays list
|
||||
var/list/realized_underlays
|
||||
|
||||
/// Takes the atoms's existing overlays and underlays, and makes them mutable so they can be properly vv'd in the realized_overlays/underlays list
|
||||
/atom/proc/realize_overlays()
|
||||
realized_overlays = realize_appearance_queue(overlays)
|
||||
realized_underlays = realize_appearance_queue(underlays)
|
||||
|
||||
/// Takes the image's existing overlays, and makes them mutable so they can be properly vv'd in the realized_overlays list
|
||||
/image/proc/realize_overlays()
|
||||
realized_overlays = realize_appearance_queue(overlays)
|
||||
realized_underlays = realize_appearance_queue(underlays)
|
||||
|
||||
/// Takes a list of appearnces, makes them mutable so they can be properly vv'd and inspected
|
||||
/proc/realize_appearance_queue(list/appearances)
|
||||
var/list/real_appearances = list()
|
||||
var/list/queue = appearances.Copy()
|
||||
var/queue_index = 0
|
||||
while(queue_index < length(queue))
|
||||
queue_index++
|
||||
// If it's not a command, we assert that it's an appearance
|
||||
var/mutable_appearance/appearance = queue[queue_index]
|
||||
if(!appearance) // Who fucking adds nulls to their sublists god you people are the worst
|
||||
continue
|
||||
|
||||
var/mutable_appearance/new_appearance = new /mutable_appearance()
|
||||
new_appearance.appearance = appearance
|
||||
var/key = "[appearance.icon]-[appearance.icon_state]-[appearance.plane]-[appearance.layer]-[appearance.dir]-[appearance.color]"
|
||||
var/tmp_key = key
|
||||
var/appearance_indx = 1
|
||||
while(real_appearances[tmp_key])
|
||||
tmp_key = "[key]-[appearance_indx]"
|
||||
appearance_indx++
|
||||
|
||||
real_appearances[tmp_key] = new_appearance
|
||||
var/add_index = queue_index
|
||||
// Now check its children
|
||||
for(var/mutable_appearance/child_appearance as anything in appearance.overlays)
|
||||
add_index++
|
||||
queue.Insert(add_index, child_appearance)
|
||||
for(var/mutable_appearance/child_appearance as anything in appearance.underlays)
|
||||
add_index++
|
||||
queue.Insert(add_index, child_appearance)
|
||||
return real_appearances
|
||||
|
||||
/// Takes two appearances as args, prints out, logs, and returns a text representation of their differences
|
||||
/// Including suboverlays
|
||||
/proc/diff_appearances(mutable_appearance/first, mutable_appearance/second, iter = 0)
|
||||
var/list/diffs = list()
|
||||
var/list/firstdeet = first.vars
|
||||
var/list/seconddeet = second.vars
|
||||
var/diff_found = FALSE
|
||||
for(var/name in first.vars)
|
||||
var/firstv = firstdeet[name]
|
||||
var/secondv = seconddeet[name]
|
||||
if(firstv ~= secondv)
|
||||
continue
|
||||
if((islist(firstv) || islist(secondv)) && length(firstv) == 0 && length(secondv) == 0)
|
||||
continue
|
||||
if(name == "vars") // Go away
|
||||
continue
|
||||
if(name == "_listen_lookup") // This is just gonna happen with marked datums, don't care
|
||||
continue
|
||||
if(name == "overlays")
|
||||
first.realize_overlays()
|
||||
second.realize_overlays()
|
||||
var/overlays_differ = FALSE
|
||||
for(var/i in 1 to length(first.realized_overlays))
|
||||
if(diff_appearances(first.realized_overlays[i], second.realized_overlays[i], iter + 1))
|
||||
overlays_differ = TRUE
|
||||
|
||||
if(!overlays_differ)
|
||||
continue
|
||||
|
||||
diff_found = TRUE
|
||||
diffs += "Diffs detected at [name]: First ([firstv]), Second ([secondv])"
|
||||
|
||||
var/text = "Depth of: [iter]\n\t[diffs.Join("\n\t")]"
|
||||
message_admins(text)
|
||||
log_world(text)
|
||||
return diff_found
|
||||
|
||||
@@ -317,7 +317,6 @@
|
||||
if(outfit_type)
|
||||
mannequin.equipOutfit(outfit_type,TRUE)
|
||||
mannequin.setDir(SOUTH)
|
||||
COMPILE_OVERLAYS(mannequin)
|
||||
. = image(mannequin)
|
||||
unset_busy_human_dummy("HOLODISK_PRESET")
|
||||
|
||||
|
||||
@@ -43,18 +43,23 @@
|
||||
/obj/machinery/computer/update_overlays()
|
||||
. = ..()
|
||||
SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays)
|
||||
if(stat & NOPOWER)
|
||||
. += "[icon_keyboard]_off"
|
||||
return
|
||||
. += icon_keyboard
|
||||
if(icon_keyboard)
|
||||
if(stat & NOPOWER)
|
||||
. += "[icon_keyboard]_off"
|
||||
else
|
||||
. += icon_keyboard
|
||||
|
||||
// This whole block lets screens ignore lighting and be visible even in the darkest room
|
||||
// We can't do this for many things that emit light unfortunately because it layers over things that would be on top of it
|
||||
var/overlay_state = icon_screen
|
||||
if(stat & BROKEN)
|
||||
overlay_state = "[icon_state]_broken"
|
||||
. += mutable_appearance(icon, overlay_state)
|
||||
. += emissive_appearance(icon, overlay_state)
|
||||
. += mutable_appearance(icon, "[icon_state]_broken")
|
||||
return // If we don't do this broken computers glow in the dark.
|
||||
|
||||
if(stat & NOPOWER) // Your screen can't be on if you've got no damn charge
|
||||
return
|
||||
|
||||
// This lets screens ignore lighting and be visible even in the darkest room
|
||||
if(icon_screen)
|
||||
. += mutable_appearance(icon, icon_screen)
|
||||
. += emissive_appearance(icon, icon_screen)
|
||||
|
||||
/obj/machinery/computer/power_change()
|
||||
..()
|
||||
|
||||
@@ -69,7 +69,7 @@ GLOBAL_LIST_INIT(arcade_prize_pool, list(
|
||||
name = "random arcade"
|
||||
desc = "random arcade machine"
|
||||
icon_state = "arcade"
|
||||
icon_keyboard = "no_keyboard"
|
||||
icon_keyboard = null
|
||||
icon_screen = "invaders"
|
||||
light_color = LIGHT_COLOR_GREEN
|
||||
var/list/prize_override
|
||||
|
||||
@@ -209,7 +209,7 @@
|
||||
name = "security camera monitor"
|
||||
desc = "An old TV hooked into the station's camera network."
|
||||
icon_state = "television"
|
||||
icon_keyboard = "no_keyboard"
|
||||
icon_keyboard = null
|
||||
icon_screen = "detective_tv"
|
||||
pass_flags = PASSTABLE
|
||||
|
||||
@@ -252,6 +252,7 @@
|
||||
desc = "Used for watching an empty arena."
|
||||
icon = 'icons/obj/stationobjs.dmi'
|
||||
icon_state = "telescreen"
|
||||
icon_keyboard = null
|
||||
layer = SIGN_LAYER
|
||||
network = list("thunder")
|
||||
density = FALSE
|
||||
|
||||
@@ -138,13 +138,13 @@
|
||||
name = "\improper DoorMex control console"
|
||||
icon_state = "oldcomp"
|
||||
icon_screen = "library"
|
||||
icon_keyboard = "no_keyboard"
|
||||
icon_keyboard = null
|
||||
|
||||
// /obj/machinery/computer/pod/old/mass_driver_controller
|
||||
// name = "\improper Mass Driver Controller"
|
||||
// icon = 'icons/obj/airlock_machines.dmi'
|
||||
// icon_state = "airlock_control_standby"
|
||||
// icon_keyboard = "no_keyboard"
|
||||
// icon_keyboard = null
|
||||
// density = FALSE
|
||||
|
||||
// /obj/machinery/computer/pod/old/mass_driver_controller/toxinsdriver
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
desc = "Gambling for the antisocial."
|
||||
icon = 'icons/obj/economy.dmi'
|
||||
icon_state = "slots1"
|
||||
icon_keyboard = null
|
||||
density = TRUE
|
||||
use_power = IDLE_POWER_USE
|
||||
idle_power_usage = 50
|
||||
|
||||
@@ -558,8 +558,9 @@ RLD
|
||||
. = ..()
|
||||
if(has_ammobar)
|
||||
var/ratio = CEILING((matter / max_matter) * ammo_sections, 1)
|
||||
cut_overlays() //To prevent infinite stacking of overlays
|
||||
add_overlay("[icon_state]_charge[ratio]")
|
||||
if(ratio)
|
||||
cut_overlays() //To prevent infinite stacking of overlays
|
||||
add_overlay("[icon_state]_charge[ratio]")
|
||||
|
||||
/obj/item/construction/rcd/Initialize(mapload)
|
||||
. = ..()
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
var/on = FALSE //if the paddles are equipped (1) or on the defib (0)
|
||||
var/safety = TRUE //if you can zap people with the defibs on harm mode
|
||||
var/powered = FALSE //if there's a cell in the defib with enough power for a revive, blocks paddles from reviving otherwise
|
||||
/// If the cell can be removed via screwdriver
|
||||
var/cell_removable = TRUE
|
||||
var/obj/item/shockpaddles/paddles
|
||||
var/obj/item/stock_parts/cell/cell
|
||||
var/combat = FALSE //if true, revive through hardsuits, allow for combat shocking, and tint paddles syndicate colors
|
||||
@@ -29,6 +31,16 @@
|
||||
var/timedeath = 10
|
||||
var/disarm_shock_time = 10
|
||||
var/always_emagged = FALSE
|
||||
/// The icon state for the paddle overlay, not applied if null
|
||||
var/paddle_state = "defibunit-paddles"
|
||||
/// The icon state for the powered on overlay, not applied if null
|
||||
var/powered_state = "defibunit-powered"
|
||||
/// The icon state for the charge bar overlay, not applied if null
|
||||
var/charge_state = "defibunit-charge"
|
||||
/// The icon state for the missing cell overlay, not applied if null
|
||||
var/nocell_state = "defibunit-nocell"
|
||||
/// The icon state for the emagged overlay, not applied if null
|
||||
var/emagged_state = "defibunit-emagged"
|
||||
|
||||
/obj/item/defibrillator/get_cell()
|
||||
return cell
|
||||
@@ -56,18 +68,19 @@
|
||||
|
||||
/obj/item/defibrillator/update_overlays()
|
||||
. = ..()
|
||||
if(!on)
|
||||
. += "[initial(icon_state)]-paddles"
|
||||
if(powered)
|
||||
. += "[initial(icon_state)]-powered"
|
||||
if(!QDELETED(cell))
|
||||
|
||||
if(!on && paddle_state)
|
||||
. += paddle_state
|
||||
if(powered && powered_state)
|
||||
. += powered_state
|
||||
if(!QDELETED(cell) && charge_state)
|
||||
var/ratio = cell.charge / cell.maxcharge
|
||||
ratio = CEILING(ratio*4, 1) * 25
|
||||
add_overlay("[initial(icon_state)]-charge[ratio]")
|
||||
if(!cell)
|
||||
. += "[initial(icon_state)]-nocell"
|
||||
if(!safety)
|
||||
. += "[initial(icon_state)]-emagged"
|
||||
. += "[charge_state][ratio]"
|
||||
if(!cell && nocell_state)
|
||||
. += "[nocell_state]"
|
||||
if(!safety && emagged_state)
|
||||
. += emagged_state
|
||||
|
||||
/obj/item/defibrillator/CheckParts(list/parts_list)
|
||||
..()
|
||||
@@ -103,6 +116,18 @@
|
||||
var/atom/movable/screen/inventory/hand/H = over_object
|
||||
M.putItemFromInventoryInHandIfPossible(src, H.held_index)
|
||||
|
||||
/obj/item/defibrillator/screwdriver_act(mob/living/user, obj/item/tool)
|
||||
if(!cell || !cell_removable)
|
||||
return FALSE
|
||||
|
||||
cell.update_appearance()
|
||||
cell.forceMove(get_turf(src))
|
||||
balloon_alert(user, "removed [cell]")
|
||||
cell = null
|
||||
tool.play_tool_sound(src, 50)
|
||||
update_power()
|
||||
return TRUE
|
||||
|
||||
/obj/item/defibrillator/attackby(obj/item/W, mob/user, params)
|
||||
if(W == paddles)
|
||||
toggle_paddles()
|
||||
@@ -120,13 +145,6 @@
|
||||
to_chat(user, "<span class='notice'>You install a cell in [src].</span>")
|
||||
update_power()
|
||||
|
||||
else if(W.tool_behaviour == TOOL_SCREWDRIVER)
|
||||
if(cell)
|
||||
cell.update_icon()
|
||||
cell.forceMove(get_turf(src))
|
||||
cell = null
|
||||
to_chat(user, "<span class='notice'>You remove the cell from [src].</span>")
|
||||
update_power()
|
||||
else
|
||||
return ..()
|
||||
|
||||
@@ -236,6 +254,11 @@
|
||||
item_state = "defibcompact"
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
slot_flags = ITEM_SLOT_BELT
|
||||
paddle_state = "defibcompact-paddles"
|
||||
powered_state = "defibcompact-powered"
|
||||
charge_state = "defibcompact-charge"
|
||||
nocell_state = "defibcompact-nocell"
|
||||
emagged_state = "defibcompact-emagged"
|
||||
|
||||
/obj/item/defibrillator/compact/item_action_slot_check(slot, mob/user, datum/action/A)
|
||||
if(slot == user.getBeltSlot())
|
||||
@@ -254,6 +277,13 @@
|
||||
always_emagged = TRUE
|
||||
disarm_shock_time = 0
|
||||
cell = /obj/item/stock_parts/cell/infinite
|
||||
paddles = /obj/item/shockpaddles/syndicate
|
||||
paddle_state = "defibcombat-paddles"
|
||||
powered_state = null
|
||||
emagged_state = null
|
||||
|
||||
/obj/item/defibrillator/compact/combat/loaded
|
||||
cell_removable = FALSE // Don't let people just have an infinite power cell
|
||||
|
||||
/obj/item/defibrillator/compact/combat/loaded/attackby(obj/item/W, mob/user, params)
|
||||
if(W == paddles)
|
||||
|
||||
@@ -639,6 +639,7 @@
|
||||
icon = 'icons/obj/cigarettes.dmi'
|
||||
icon_state = "matchbox"
|
||||
item_state = "zippo"
|
||||
illustration = null
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
slot_flags = ITEM_SLOT_BELT
|
||||
custom_price = PRICE_REALLY_CHEAP
|
||||
|
||||
@@ -76,7 +76,8 @@
|
||||
. += "[icon_door_override ? icon_door : icon_state]_open"
|
||||
return
|
||||
|
||||
. += "[icon_door || icon_state]_door"
|
||||
if(icon_door)
|
||||
. += "[icon_door || icon_state]_door"
|
||||
if(welded)
|
||||
. += icon_welded
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
name = "large cardboard box"
|
||||
desc = "Just a box..."
|
||||
icon_state = "cardboard"
|
||||
icon_door = null
|
||||
mob_storage_capacity = 1
|
||||
resistance_flags = FLAMMABLE
|
||||
max_integrity = 70
|
||||
|
||||
@@ -307,7 +307,6 @@ GLOBAL_VAR_INIT(say_disabled, FALSE)
|
||||
qdel(I)
|
||||
randomize_human(D)
|
||||
JB.equip(D, TRUE, FALSE)
|
||||
COMPILE_OVERLAYS(D)
|
||||
var/icon/I = icon(getFlatIcon(D), frame = 1)
|
||||
final.Insert(I, JB.title)
|
||||
qdel(D)
|
||||
|
||||
@@ -314,7 +314,6 @@
|
||||
|
||||
equipAntagOnDummy(mannequin, ert)
|
||||
|
||||
COMPILE_OVERLAYS(mannequin)
|
||||
CHECK_TICK
|
||||
var/icon/preview_icon = icon('icons/effects/effects.dmi', "nothing")
|
||||
preview_icon.Scale(48+32, 16+32)
|
||||
|
||||
@@ -391,7 +391,6 @@ GLOBAL_LIST_EMPTY(antagonists)
|
||||
/datum/antagonist/proc/render_preview_outfit(datum/outfit/outfit, mob/living/carbon/human/dummy)
|
||||
dummy = dummy || new /mob/living/carbon/human/dummy/consistent
|
||||
dummy.equipOutfit(outfit, visualsOnly = TRUE)
|
||||
COMPILE_OVERLAYS(dummy)
|
||||
var/icon = getFlatIcon(dummy)
|
||||
|
||||
// We don't want to qdel the dummy right away, since its items haven't initialized yet.
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
icon = 'icons/obj/abductor.dmi'
|
||||
icon_state = "camera"
|
||||
icon_keyboard = null
|
||||
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
|
||||
|
||||
/obj/machinery/computer/camera_advanced/abductor/CreateEye()
|
||||
|
||||
@@ -1342,7 +1342,6 @@ GLOBAL_LIST_INIT(hallucination_list, list(
|
||||
var/datum/preferences/prefs = C.client.prefs
|
||||
var/mob/living/carbon/human/dummy/M = generate_or_wait_for_human_dummy(DUMMY_HUMAN_SLOT_HALLUCINATION)
|
||||
prefs.copy_to(M)
|
||||
COMPILE_OVERLAYS(M)
|
||||
CHECK_TICK
|
||||
image = image(M,C)
|
||||
unset_busy_human_dummy(DUMMY_HUMAN_SLOT_HALLUCINATION)
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
name = "auxillary base management console"
|
||||
icon = 'icons/obj/terminals.dmi'
|
||||
icon_state = "dorm_available"
|
||||
icon_keyboard = null
|
||||
var/shuttleId = "colony_drop"
|
||||
desc = "Allows a deployable expedition base to be dropped from the station to a designated mining location. It can also \
|
||||
interface with the mining shuttle at the landing site if a mobile beacon is also deployed."
|
||||
|
||||
@@ -70,7 +70,6 @@
|
||||
|
||||
mannequin.regenerate_icons()
|
||||
|
||||
COMPILE_OVERLAYS(mannequin)
|
||||
parent.show_character_previews(new /mutable_appearance(mannequin))
|
||||
unset_busy_human_dummy(DUMMY_HUMAN_SLOT_PREFERENCES)
|
||||
|
||||
|
||||
@@ -20,7 +20,8 @@
|
||||
var/chargerate = 100 //how much power is given every tick in a recharger
|
||||
var/self_recharge = 0 //does it self recharge, over time, or not?
|
||||
var/ratingdesc = TRUE
|
||||
var/grown_battery = FALSE // If it's a grown that acts as a battery, add a wire overlay to it.
|
||||
///If it's a grown that acts as a battery, add a wire overlay to it.
|
||||
var/grown_battery = FALSE
|
||||
rad_flags = RAD_NO_CONTAMINATE // Prevent the same cheese as with the stock parts
|
||||
|
||||
/obj/item/stock_parts/cell/get_cell()
|
||||
@@ -61,7 +62,8 @@
|
||||
/obj/item/stock_parts/cell/update_overlays()
|
||||
. = ..()
|
||||
if(grown_battery)
|
||||
. += image('icons/obj/power.dmi',"grown_wires")
|
||||
. += image('icons/obj/power.dmi', "grown_wires")
|
||||
return
|
||||
if(charge < 0.01)
|
||||
return
|
||||
else if(charge/maxcharge >=0.995)
|
||||
|
||||
@@ -220,6 +220,7 @@
|
||||
can_suppress = FALSE
|
||||
burst_size = 3
|
||||
burst_shot_delay = 1
|
||||
automatic_burst_overlay = FALSE
|
||||
|
||||
// Bulldog shotgun //
|
||||
|
||||
|
||||
@@ -45,6 +45,8 @@
|
||||
desc = "A modified version of the basic phaser gun, this one fires less concentrated energy bolts designed for target practice."
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/disabler, /obj/item/ammo_casing/energy/laser/practice)
|
||||
icon_state = "decloner"
|
||||
//You have no icons for energy types, you're a decloner
|
||||
modifystate = FALSE
|
||||
|
||||
/obj/item/gun/energy/e_gun/hos
|
||||
name = "\improper X-01 MultiPhase Energy Gun"
|
||||
@@ -78,6 +80,7 @@
|
||||
lefthand_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/weapons/guns_righthand.dmi'
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/net, /obj/item/ammo_casing/energy/trap)
|
||||
modifystate = FALSE
|
||||
can_flashlight = 0
|
||||
ammo_x_offset = 1
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
density = TRUE
|
||||
icon = 'icons/obj/chemical.dmi'
|
||||
icon_state = "mixer0"
|
||||
icon_keyboard = null
|
||||
use_power = TRUE
|
||||
idle_power_usage = 20
|
||||
resistance_flags = ACID_PROOF
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
desc = "Controls the drop pod's launch system."
|
||||
icon = 'icons/obj/terminals.dmi'
|
||||
icon_state = "dorm_available"
|
||||
icon_keyboard = null
|
||||
light_color = LIGHT_COLOR_BLUE
|
||||
req_access = list(ACCESS_SYNDICATE)
|
||||
shuttleId = "steel_rain"
|
||||
|
||||
Reference in New Issue
Block a user