Merge remote-tracking branch 'upstream/master' into ambitioncheck
This commit is contained in:
@@ -224,6 +224,7 @@
|
||||
else
|
||||
to_chat(owner, "<span class='userdanger'>You feel your heart lurching in your chest...</span>")
|
||||
owner.adjustOxyLoss(8)
|
||||
else
|
||||
|
||||
/datum/brain_trauma/severe/discoordination
|
||||
name = "Discoordination"
|
||||
|
||||
+62
-35
@@ -4,7 +4,7 @@
|
||||
var/window_id // window_id is used as the window name for browse and onclose
|
||||
var/width = 0
|
||||
var/height = 0
|
||||
var/atom/ref = null
|
||||
var/datum/weakref/ref = null
|
||||
var/window_options = "can_close=1;can_minimize=1;can_maximize=0;can_resize=1;titlebar=1;" // window option is set using window_id
|
||||
var/stylesheets[0]
|
||||
var/scripts[0]
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
|
||||
/datum/browser/New(nuser, nwindow_id, ntitle = 0, nwidth = 0, nheight = 0, atom/nref = null)
|
||||
|
||||
user = nuser
|
||||
RegisterSignal(user, COMSIG_PARENT_QDELETING, .proc/user_deleted)
|
||||
window_id = nwindow_id
|
||||
if (ntitle)
|
||||
title = format_text(ntitle)
|
||||
@@ -26,7 +26,11 @@
|
||||
if (nheight)
|
||||
height = nheight
|
||||
if (nref)
|
||||
ref = nref
|
||||
ref = WEAKREF(nref)
|
||||
|
||||
/datum/browser/proc/user_deleted(datum/source)
|
||||
SIGNAL_HANDLER
|
||||
user = null
|
||||
|
||||
/datum/browser/proc/add_head_content(nhead_content)
|
||||
head_content = nhead_content
|
||||
@@ -35,7 +39,7 @@
|
||||
window_options = nwindow_options
|
||||
|
||||
/datum/browser/proc/add_stylesheet(name, file)
|
||||
if(istype(name, /datum/asset/spritesheet))
|
||||
if (istype(name, /datum/asset/spritesheet))
|
||||
var/datum/asset/spritesheet/sheet = name
|
||||
stylesheets["spritesheet_[sheet.name].css"] = "data/spritesheets/[sheet.name]"
|
||||
else
|
||||
@@ -94,27 +98,32 @@
|
||||
"}
|
||||
|
||||
/datum/browser/proc/open(use_onclose = TRUE)
|
||||
if(isnull(window_id)) //null check because this can potentially nuke goonchat
|
||||
if(isnull(window_id)) //null check because this can potentially nuke goonchat
|
||||
WARNING("Browser [title] tried to open with a null ID")
|
||||
to_chat(user, "<span class='userdanger'>The [title] browser you tried to open failed a sanity check! Please report this on github!</span>")
|
||||
to_chat(user, span_userdanger("The [title] browser you tried to open failed a sanity check! Please report this on github!"))
|
||||
return
|
||||
var/window_size = ""
|
||||
if(width && height)
|
||||
if (width && height)
|
||||
window_size = "size=[width]x[height];"
|
||||
common_asset.send(user)
|
||||
if(stylesheets.len)
|
||||
if (stylesheets.len)
|
||||
SSassets.transport.send_assets(user, stylesheets)
|
||||
if(scripts.len)
|
||||
if (scripts.len)
|
||||
SSassets.transport.send_assets(user, scripts)
|
||||
user << browse(get_content(), "window=[window_id];[window_size][window_options]")
|
||||
if(use_onclose)
|
||||
if (use_onclose)
|
||||
setup_onclose()
|
||||
|
||||
/datum/browser/proc/setup_onclose()
|
||||
set waitfor = 0 //winexists sleeps, so we don't need to.
|
||||
for (var/i in 1 to 10)
|
||||
if (user && winexists(user, window_id))
|
||||
onclose(user, window_id, ref)
|
||||
if (user?.client && winexists(user, window_id))
|
||||
var/atom/send_ref
|
||||
if(ref)
|
||||
send_ref = ref.resolve()
|
||||
if(!send_ref)
|
||||
ref = null
|
||||
onclose(user, window_id, send_ref)
|
||||
break
|
||||
|
||||
/datum/browser/proc/close()
|
||||
@@ -153,11 +162,35 @@
|
||||
opentime = 0
|
||||
close()
|
||||
|
||||
//designed as a drop in replacement for alert(); functions the same. (outside of needing User specified)
|
||||
/proc/tgalert(mob/User, Message, Title, Button1="Ok", Button2, Button3, StealFocus = 1, Timeout = 6000)
|
||||
/**
|
||||
* **DEPRECATED: USE tgui_alert(...) INSTEAD**
|
||||
*
|
||||
* Designed as a drop in replacement for alert(); functions the same. (outside of needing User specified)
|
||||
* Arguments:
|
||||
* * User - The user to show the alert to.
|
||||
* * Message - The textual body of the alert.
|
||||
* * Title - The title of the alert's window.
|
||||
* * Button1 - The first button option.
|
||||
* * Button2 - The second button option.
|
||||
* * Button3 - The third button option.
|
||||
* * StealFocus - Boolean operator controlling if the alert will steal the user's window focus.
|
||||
* * Timeout - The timeout of the window, after which no responses will be valid.
|
||||
*/
|
||||
/proc/tgalert(mob/User, Message, Title, Button1="Ok", Button2, Button3, StealFocus = TRUE, Timeout = 6000)
|
||||
if (!User)
|
||||
User = usr
|
||||
switch(askuser(User, Message, Title, Button1, Button2, Button3, StealFocus, Timeout))
|
||||
if (!istype(User))
|
||||
if (istype(User, /client))
|
||||
var/client/client = User
|
||||
User = client.mob
|
||||
else
|
||||
return
|
||||
|
||||
// Get user's response using a modal
|
||||
var/datum/browser/modal/alert/A = new(User, Message, Title, Button1, Button2, Button3, StealFocus, Timeout)
|
||||
A.open()
|
||||
A.wait()
|
||||
switch(A.selectedbutton)
|
||||
if (1)
|
||||
return Button1
|
||||
if (2)
|
||||
@@ -197,8 +230,8 @@
|
||||
.=..()
|
||||
opentime = 0
|
||||
|
||||
/datum/browser/modal/open(use_onclose = 1)
|
||||
set waitfor = 0
|
||||
/datum/browser/modal/open(use_onclose)
|
||||
set waitfor = FALSE
|
||||
opentime = world.time
|
||||
|
||||
if (stealfocus)
|
||||
@@ -277,7 +310,7 @@
|
||||
opentime = 0
|
||||
close()
|
||||
|
||||
/proc/presentpicker(var/mob/User,Message, Title, Button1="Ok", Button2, Button3, StealFocus = 1,Timeout = 6000,list/values, inputtype = "checkbox", width, height, slidecolor)
|
||||
/proc/presentpicker(mob/User,Message, Title, Button1="Ok", Button2, Button3, StealFocus = 1,Timeout = 6000,list/values, inputtype = "checkbox", width, height, slidecolor)
|
||||
if (!istype(User))
|
||||
if (istype(User, /client/))
|
||||
var/client/C = User
|
||||
@@ -290,7 +323,7 @@
|
||||
if (A.selectedbutton)
|
||||
return list("button" = A.selectedbutton, "values" = A.valueslist)
|
||||
|
||||
/proc/input_bitfield(var/mob/User, title, bitfield, current_value, nwidth = 350, nheight = 350, nslidecolor, allowed_edit_list = null)
|
||||
/proc/input_bitfield(mob/User, title, bitfield, current_value, nwidth = 350, nheight = 350, nslidecolor, allowed_edit_list = null)
|
||||
if (!User || !(bitfield in GLOB.bitfields))
|
||||
return
|
||||
var/list/pickerlist = list()
|
||||
@@ -401,7 +434,7 @@
|
||||
opentime = 0
|
||||
close()
|
||||
|
||||
/proc/presentpreflikepicker(var/mob/User,Message, Title, Button1="Ok", Button2, Button3, StealFocus = 1,Timeout = 6000,list/settings, width, height, slidecolor)
|
||||
/proc/presentpreflikepicker(mob/User,Message, Title, Button1="Ok", Button2, Button3, StealFocus = 1,Timeout = 6000,list/settings, width, height, slidecolor)
|
||||
if (!istype(User))
|
||||
if (istype(User, /client/))
|
||||
var/client/C = User
|
||||
@@ -414,12 +447,6 @@
|
||||
if (A.selectedbutton)
|
||||
return list("button" = A.selectedbutton, "settings" = A.settings)
|
||||
|
||||
// This will allow you to show an icon in the browse window
|
||||
// This is added to mob so that it can be used without a reference to the browser object
|
||||
// There is probably a better place for this...
|
||||
/mob/proc/browse_rsc_icon(icon, icon_state, dir = -1)
|
||||
|
||||
|
||||
// Registers the on-close verb for a browse window (client/verb/.windowclose)
|
||||
// this will be called when the close-button of a window is pressed.
|
||||
//
|
||||
@@ -427,8 +454,8 @@
|
||||
// e.g. canisters, timers, etc.
|
||||
//
|
||||
// windowid should be the specified window name
|
||||
// e.g. code is : user << browse(text, "window=fred")
|
||||
// then use : onclose(user, "fred")
|
||||
// e.g. code is : user << browse(text, "window=fred")
|
||||
// then use : onclose(user, "fred")
|
||||
//
|
||||
// Optionally, specify the "ref" parameter as the controlled atom (usually src)
|
||||
// to pass a "close=1" parameter to the atom's Topic() proc for special handling.
|
||||
@@ -451,18 +478,18 @@
|
||||
// otherwise, just reset the client mob's machine var.
|
||||
//
|
||||
/client/verb/windowclose(atomref as text)
|
||||
set hidden = TRUE // hide this verb from the user's panel
|
||||
set name = ".windowclose" // no autocomplete on cmd line
|
||||
set hidden = TRUE // hide this verb from the user's panel
|
||||
set name = ".windowclose" // no autocomplete on cmd line
|
||||
|
||||
if(atomref!="null") // if passed a real atomref
|
||||
var/hsrc = locate(atomref) // find the reffed atom
|
||||
if(atomref!="null") // if passed a real atomref
|
||||
var/hsrc = locate(atomref) // find the reffed atom
|
||||
var/href = "close=1"
|
||||
if(hsrc)
|
||||
usr = src.mob
|
||||
src.Topic(href, params2list(href), hsrc) // this will direct to the atom's
|
||||
return // Topic() proc via client.Topic()
|
||||
src.Topic(href, params2list(href), hsrc) // this will direct to the atom's
|
||||
return // Topic() proc via client.Topic()
|
||||
|
||||
// no atomref specified (or not found)
|
||||
// so just reset the user mob's machine var
|
||||
if(src && src.mob)
|
||||
if(src?.mob)
|
||||
src.mob.unset_machine()
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/datum/changelog
|
||||
var/static/list/changelog_items = list()
|
||||
|
||||
/datum/changelog/ui_state()
|
||||
return GLOB.always_state
|
||||
|
||||
/datum/changelog/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if (!ui)
|
||||
ui = new(user, src, "Changelog")
|
||||
ui.open()
|
||||
|
||||
/datum/changelog/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
if(action == "get_month")
|
||||
var/datum/asset/changelog_item/changelog_item = changelog_items[params["date"]]
|
||||
if (!changelog_item)
|
||||
changelog_item = new /datum/asset/changelog_item(params["date"])
|
||||
changelog_items[params["date"]] = changelog_item
|
||||
return ui.send_asset(changelog_item)
|
||||
|
||||
/datum/changelog/ui_static_data()
|
||||
var/list/data = list( "dates" = list() )
|
||||
var/regex/ymlRegex = regex(@"\.yml", "g")
|
||||
|
||||
for(var/archive_file in flist("[global.config.directory]/../html/changelogs/archive/"))
|
||||
var/archive_date = ymlRegex.Replace(archive_file, "")
|
||||
data["dates"] = list(archive_date) + data["dates"]
|
||||
|
||||
return data
|
||||
@@ -18,8 +18,6 @@
|
||||
#define CHAT_LAYER_Z_STEP 0.0001
|
||||
/// The number of z-layer 'slices' usable by the chat message layering
|
||||
#define CHAT_LAYER_MAX_Z (CHAT_LAYER_MAX - CHAT_LAYER) / CHAT_LAYER_Z_STEP
|
||||
/// Macro from Lummox used to get height from a MeasureText proc
|
||||
#define WXH_TO_HEIGHT(x) text2num(copytext(x, findtextEx(x, "x") + 1))
|
||||
|
||||
/**
|
||||
* # Chat Message Overlay
|
||||
@@ -125,7 +123,7 @@
|
||||
|
||||
// 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")
|
||||
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
|
||||
@@ -165,7 +163,7 @@
|
||||
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
|
||||
message.maptext = MAPTEXT(complete_text)
|
||||
|
||||
// View the message
|
||||
LAZYADDASSOC(owned_by.seen_messages, message_loc, src)
|
||||
|
||||
@@ -101,7 +101,7 @@
|
||||
if(!C)
|
||||
return
|
||||
watching += C
|
||||
M.overlay_fullscreen("cinematic",/atom/movable/screen/fullscreen/cinematic_backdrop)
|
||||
M.overlay_fullscreen("cinematic",/atom/movable/screen/fullscreen/tiled/cinematic_backdrop)
|
||||
C.screen += screen
|
||||
|
||||
//Sound helper
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
///Allows you to set a theme for a set of areas without tying them to looping sounds explicitly
|
||||
/datum/component/area_sound_manager
|
||||
///area -> looping sound type
|
||||
var/list/area_to_looping_type = list()
|
||||
///Current sound loop
|
||||
var/datum/looping_sound/our_loop
|
||||
///A list of "acceptable" z levels to be on. If you leave this, we're gonna delete ourselves
|
||||
var/list/accepted_zs
|
||||
///The timer id of our current start delay, if it exists
|
||||
var/timerid
|
||||
|
||||
/datum/component/area_sound_manager/Initialize(area_loop_pairs, change_on, remove_on, acceptable_zs)
|
||||
if(!ismovable(parent))
|
||||
return
|
||||
area_to_looping_type = area_loop_pairs
|
||||
accepted_zs = acceptable_zs
|
||||
change_the_track()
|
||||
|
||||
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, .proc/react_to_move)
|
||||
RegisterSignal(parent, COMSIG_MOVABLE_Z_CHANGED, .proc/react_to_z_move)
|
||||
RegisterSignal(parent, change_on, .proc/handle_change)
|
||||
RegisterSignal(parent, remove_on, .proc/handle_removal)
|
||||
|
||||
/datum/component/area_sound_manager/Destroy(force, silent)
|
||||
QDEL_NULL(our_loop)
|
||||
. = ..()
|
||||
|
||||
/datum/component/area_sound_manager/proc/react_to_move(datum/source, atom/oldloc, dir, forced)
|
||||
SIGNAL_HANDLER
|
||||
var/list/loop_lookup = area_to_looping_type
|
||||
if(loop_lookup[get_area(oldloc)] == loop_lookup[get_area(parent)])
|
||||
return
|
||||
change_the_track(TRUE)
|
||||
|
||||
/datum/component/area_sound_manager/proc/react_to_z_move(datum/source, old_z, new_z)
|
||||
SIGNAL_HANDLER
|
||||
if(!length(accepted_zs) || (new_z in accepted_zs))
|
||||
return
|
||||
qdel(src)
|
||||
|
||||
/datum/component/area_sound_manager/proc/handle_removal(datum/source)
|
||||
SIGNAL_HANDLER
|
||||
qdel(src)
|
||||
|
||||
/datum/component/area_sound_manager/proc/handle_change(datum/source)
|
||||
SIGNAL_HANDLER
|
||||
change_the_track()
|
||||
|
||||
/datum/component/area_sound_manager/proc/change_the_track(skip_start = FALSE)
|
||||
var/time_remaining = 0
|
||||
|
||||
if(our_loop)
|
||||
var/our_id = our_loop.timerid || timerid
|
||||
if(our_id)
|
||||
time_remaining = timeleft(our_id, SSsound_loops) || 0
|
||||
//Time left will sometimes return negative values, just ignore them and start a new sound loop now
|
||||
time_remaining = max(time_remaining, 0)
|
||||
QDEL_NULL(our_loop)
|
||||
|
||||
var/area/our_area = get_area(parent)
|
||||
var/new_loop_type = area_to_looping_type[our_area]
|
||||
if(!new_loop_type)
|
||||
return
|
||||
|
||||
our_loop = new new_loop_type(parent, FALSE, TRUE, skip_start)
|
||||
|
||||
//If we're still playing, wait a bit before changing the sound so we don't double up
|
||||
if(time_remaining)
|
||||
timerid = addtimer(CALLBACK(src, .proc/start_looping_sound), time_remaining, TIMER_UNIQUE | TIMER_CLIENT_TIME | TIMER_STOPPABLE | TIMER_NO_HASH_WAIT | TIMER_DELETE_ME, SSsound_loops)
|
||||
return
|
||||
timerid = null
|
||||
our_loop.start()
|
||||
|
||||
/datum/component/area_sound_manager/proc/start_looping_sound()
|
||||
timerid = null
|
||||
if(our_loop)
|
||||
our_loop.start()
|
||||
@@ -66,7 +66,7 @@
|
||||
/datum/component/storage/concrete/_insert_physical_item(obj/item/I, override = FALSE)
|
||||
. = TRUE
|
||||
var/atom/real_location = real_location()
|
||||
if(I.loc != real_location && real_location)
|
||||
if(real_location && I.loc != real_location)
|
||||
I.forceMove(real_location)
|
||||
refresh_mob_views()
|
||||
|
||||
@@ -95,16 +95,19 @@
|
||||
return FALSE
|
||||
|
||||
/datum/component/storage/concrete/proc/on_contents_del(datum/source, atom/A)
|
||||
SIGNAL_HANDLER
|
||||
var/atom/real_location = parent
|
||||
if(A in real_location)
|
||||
usr = null
|
||||
remove_from_storage(A, null)
|
||||
|
||||
/datum/component/storage/concrete/proc/on_deconstruct(datum/source, disassembled)
|
||||
SIGNAL_HANDLER
|
||||
if(drop_all_on_deconstruct)
|
||||
do_quick_empty()
|
||||
|
||||
/datum/component/storage/concrete/proc/on_break(datum/source, damage_flag)
|
||||
SIGNAL_HANDLER
|
||||
if(drop_all_on_break)
|
||||
do_quick_empty()
|
||||
if(unlock_on_break)
|
||||
@@ -131,14 +134,16 @@
|
||||
var/list/seeing_mobs = can_see_contents()
|
||||
for(var/mob/M in seeing_mobs)
|
||||
M.client.screen -= AM
|
||||
if(ismob(parent.loc) && isitem(AM))
|
||||
var/obj/item/I = AM
|
||||
var/mob/M = parent.loc
|
||||
I.dropped(M)
|
||||
I.item_flags &= ~IN_STORAGE
|
||||
I.remove_outline()
|
||||
if(isitem(AM))
|
||||
var/obj/item/removed_item = AM
|
||||
removed_item.item_flags &= ~IN_STORAGE
|
||||
if(ismob(parent.loc))
|
||||
var/mob/carrying_mob = parent.loc
|
||||
removed_item.dropped(carrying_mob, TRUE)
|
||||
if(new_location)
|
||||
AM.forceMove(new_location) // exited comsig will handle removal reset.
|
||||
//Reset the items values
|
||||
_removal_reset(AM)
|
||||
AM.forceMove(new_location)
|
||||
//We don't want to call this if the item is being destroyed
|
||||
AM.on_exit_storage(src)
|
||||
else
|
||||
@@ -147,7 +152,7 @@
|
||||
refresh_mob_views()
|
||||
if(isobj(parent))
|
||||
var/obj/O = parent
|
||||
O.update_icon()
|
||||
O.update_appearance()
|
||||
return TRUE
|
||||
|
||||
/datum/component/storage/concrete/proc/slave_can_insert_object(datum/component/storage/slave, obj/item/I, stop_messages = FALSE, mob/M)
|
||||
@@ -158,7 +163,7 @@
|
||||
if(. && !prevent_warning)
|
||||
slave.mob_item_insertion_feedback(usr, M, I)
|
||||
|
||||
/datum/component/storage/concrete/handle_item_insertion(obj/item/I, prevent_warning = FALSE, mob/M, datum/component/storage/remote) //Remote is null or the slave datum
|
||||
/datum/component/storage/concrete/handle_item_insertion(obj/item/I, prevent_warning = FALSE, mob/M, datum/component/storage/remote) //Remote is null or the slave datum
|
||||
var/datum/component/storage/concrete/master = master()
|
||||
var/atom/parent = src.parent
|
||||
var/moved = FALSE
|
||||
@@ -168,7 +173,7 @@
|
||||
if(!M.temporarilyRemoveItemFromInventory(I))
|
||||
return FALSE
|
||||
else
|
||||
moved = TRUE //At this point if the proc fails we need to manually move the object back to the turf/mob/whatever.
|
||||
moved = TRUE //At this point if the proc fails we need to manually move the object back to the turf/mob/whatever.
|
||||
if(I.pulledby)
|
||||
I.pulledby.stop_pulling()
|
||||
if(silent)
|
||||
@@ -203,7 +208,7 @@
|
||||
/datum/component/storage/concrete/update_icon()
|
||||
if(isobj(parent))
|
||||
var/obj/O = parent
|
||||
O.update_icon()
|
||||
O.update_appearance()
|
||||
for(var/i in slaves)
|
||||
var/datum/component/storage/slave = i
|
||||
slave.update_icon()
|
||||
|
||||
@@ -418,38 +418,38 @@
|
||||
return TRUE
|
||||
|
||||
/datum/component/storage/proc/mousedrop_onto(datum/source, atom/over_object, mob/M)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
set waitfor = FALSE
|
||||
. = COMPONENT_NO_MOUSEDROP
|
||||
if(!ismob(M))
|
||||
return
|
||||
if(!over_object)
|
||||
return
|
||||
if(ismecha(M.loc)) // stops inventory actions in a mech
|
||||
return
|
||||
if(M.incapacitated() || !M.canUseStorage())
|
||||
return
|
||||
var/atom/A = parent
|
||||
if(ismob(M)) //all the check for item manipulation are in other places, you can safely open any storages as anything and its not buggy, i checked
|
||||
A.add_fingerprint(M)
|
||||
if(istype(A, /obj/item))
|
||||
var/obj/item/I = A
|
||||
I.remove_outline() //Removes the outline when we drag
|
||||
if(!over_object)
|
||||
return FALSE
|
||||
if(ismecha(M.loc)) // stops inventory actions in a mech
|
||||
return FALSE
|
||||
// this must come before the screen objects only block, dunno why it wasn't before
|
||||
if(over_object == M)
|
||||
user_show_to_mob(M, trigger_on_found = TRUE)
|
||||
return
|
||||
if(isrevenant(M))
|
||||
RevenantThrow(over_object, M, source)
|
||||
return
|
||||
if(!M.incapacitated())
|
||||
if(!istype(over_object, /atom/movable/screen))
|
||||
dump_content_at(over_object, M)
|
||||
return
|
||||
if(A.loc != M)
|
||||
return
|
||||
playsound(A, "rustle", 50, 1, -5)
|
||||
A.do_jiggle()
|
||||
if(istype(over_object, /atom/movable/screen/inventory/hand))
|
||||
var/atom/movable/screen/inventory/hand/H = over_object
|
||||
M.putItemFromInventoryInHandIfPossible(A, H.held_index)
|
||||
return
|
||||
A.add_fingerprint(M)
|
||||
A.add_fingerprint(M)
|
||||
// this must come before the screen objects only block, dunno why it wasn't before
|
||||
if(over_object == M)
|
||||
user_show_to_mob(M, trigger_on_found = TRUE)
|
||||
if(isrevenant(M))
|
||||
INVOKE_ASYNC(GLOBAL_PROC, .proc/RevenantThrow, over_object, M, source)
|
||||
return
|
||||
if(!istype(over_object, /atom/movable/screen))
|
||||
INVOKE_ASYNC(src, .proc/dump_content_at, over_object, M)
|
||||
return
|
||||
if(A.loc != M)
|
||||
return
|
||||
playsound(A, "rustle", 50, TRUE, -5)
|
||||
A.do_jiggle()
|
||||
if(istype(over_object, /atom/movable/screen/inventory/hand))
|
||||
var/atom/movable/screen/inventory/hand/H = over_object
|
||||
M.putItemFromInventoryInHandIfPossible(A, H.held_index)
|
||||
return
|
||||
A.add_fingerprint(M)
|
||||
|
||||
/datum/component/storage/proc/user_show_to_mob(mob/M, force = FALSE, trigger_on_found = FALSE)
|
||||
var/atom/A = parent
|
||||
|
||||
@@ -370,7 +370,7 @@
|
||||
user.emote("scream")
|
||||
user.gain_trauma(/datum/brain_trauma/severe/paralysis/spinesnapped) // oopsie indeed!
|
||||
shake_camera(user, 7, 7)
|
||||
user.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/flash)
|
||||
user.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/tiled/flash)
|
||||
user.clear_fullscreen("flash", 4.5)
|
||||
|
||||
if(94 to 98)
|
||||
@@ -381,7 +381,7 @@
|
||||
user.gain_trauma_type(BRAIN_TRAUMA_MILD)
|
||||
user.playsound_local(get_turf(user), 'sound/weapons/flashbang.ogg', 100, TRUE, 8, 0.9)
|
||||
shake_camera(user, 6, 6)
|
||||
user.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/flash)
|
||||
user.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/tiled/flash)
|
||||
user.clear_fullscreen("flash", 3.5)
|
||||
|
||||
if(84 to 93)
|
||||
@@ -394,7 +394,7 @@
|
||||
user.playsound_local(get_turf(user), 'sound/weapons/flashbang.ogg', 100, TRUE, 8, 0.9)
|
||||
user.DefaultCombatKnockdown(40)
|
||||
shake_camera(user, 5, 5)
|
||||
user.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/flash)
|
||||
user.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/tiled/flash)
|
||||
user.clear_fullscreen("flash", 2.5)
|
||||
|
||||
if(64 to 83)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
GLOBAL_LIST_EMPTY(uplinks)
|
||||
#define PEN_ROTATIONS 2
|
||||
|
||||
/**
|
||||
* Uplinks
|
||||
@@ -17,7 +18,7 @@ GLOBAL_LIST_EMPTY(uplinks)
|
||||
var/telecrystals
|
||||
var/selected_cat
|
||||
var/owner = null
|
||||
var/datum/game_mode/gamemode
|
||||
var/uplink_flag
|
||||
var/datum/uplink_purchase_log/purchase_log
|
||||
var/list/uplink_items
|
||||
var/hidden_crystals = 0
|
||||
@@ -26,11 +27,11 @@ GLOBAL_LIST_EMPTY(uplinks)
|
||||
var/failsafe_code
|
||||
var/compact_mode = FALSE
|
||||
var/debug = FALSE
|
||||
var/saved_player_population = 0
|
||||
var/list/filters = list()
|
||||
///Instructions on how to access the uplink based on location
|
||||
var/unlock_text
|
||||
var/list/previous_attempts
|
||||
|
||||
|
||||
/datum/component/uplink/Initialize(_owner, _lockable = TRUE, _enabled = FALSE, datum/game_mode/_gamemode, starting_tc = 20, datum/traitor_class/traitor_class)
|
||||
/datum/component/uplink/Initialize(_owner, _lockable = TRUE, _enabled = FALSE, uplink_flag = UPLINK_TRAITORS, starting_tc = TELECRYSTALS_DEFAULT)
|
||||
if(!isitem(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
@@ -44,16 +45,13 @@ GLOBAL_LIST_EMPTY(uplinks)
|
||||
RegisterSignal(parent, COMSIG_IMPLANT_EXISTING_UPLINK, .proc/new_implant)
|
||||
else if(istype(parent, /obj/item/pda))
|
||||
RegisterSignal(parent, COMSIG_PDA_CHANGE_RINGTONE, .proc/new_ringtone)
|
||||
// RegisterSignal(parent, COMSIG_PDA_CHECK_DETONATE, .proc/check_detonate)
|
||||
else if(istype(parent, /obj/item/radio))
|
||||
RegisterSignal(parent, COMSIG_RADIO_NEW_FREQUENCY, .proc/new_frequency)
|
||||
else if(istype(parent, /obj/item/pen))
|
||||
RegisterSignal(parent, COMSIG_PEN_ROTATED, .proc/pen_rotation)
|
||||
|
||||
GLOB.uplinks += src
|
||||
if(istype(traitor_class))
|
||||
filters = traitor_class.uplink_filters
|
||||
starting_tc = traitor_class.TC
|
||||
uplink_items = get_uplink_items(gamemode, TRUE, allow_restricted, filters)
|
||||
GLOB.uplinks |= src
|
||||
|
||||
if(_owner)
|
||||
owner = _owner
|
||||
@@ -64,44 +62,58 @@ GLOBAL_LIST_EMPTY(uplinks)
|
||||
purchase_log = new(owner, src)
|
||||
lockable = _lockable
|
||||
active = _enabled
|
||||
gamemode = _gamemode
|
||||
src.uplink_flag = uplink_flag
|
||||
update_items()
|
||||
telecrystals = starting_tc
|
||||
if(!lockable)
|
||||
active = TRUE
|
||||
locked = FALSE
|
||||
saved_player_population = GLOB.joined_player_list.len
|
||||
|
||||
previous_attempts = list()
|
||||
|
||||
/datum/component/uplink/InheritComponent(datum/component/uplink/U)
|
||||
lockable |= U.lockable
|
||||
active |= U.active
|
||||
if(!gamemode)
|
||||
gamemode = U.gamemode
|
||||
uplink_flag |= U.uplink_flag
|
||||
telecrystals += U.telecrystals
|
||||
if(purchase_log && U.purchase_log)
|
||||
purchase_log.MergeWithAndDel(U.purchase_log)
|
||||
|
||||
/datum/component/uplink/Destroy()
|
||||
GLOB.uplinks -= src
|
||||
gamemode = null
|
||||
purchase_log = null
|
||||
return ..()
|
||||
|
||||
/datum/component/uplink/proc/update_items()
|
||||
var/updated_items
|
||||
updated_items = get_uplink_items(uplink_flag, TRUE, allow_restricted)
|
||||
update_sales(updated_items)
|
||||
uplink_items = updated_items
|
||||
|
||||
/datum/component/uplink/proc/update_sales(updated_items)
|
||||
var/discount_categories = list("Discounted Gear", "Discounted Team Gear", "Limited Stock Team Gear")
|
||||
if (uplink_items == null)
|
||||
return
|
||||
for (var/category in discount_categories) // Makes sure discounted items aren't renewed or replaced
|
||||
if (uplink_items[category] != null && updated_items[category] != null)
|
||||
updated_items[category] = uplink_items[category]
|
||||
|
||||
/datum/component/uplink/proc/LoadTC(mob/user, obj/item/stack/telecrystal/TC, silent = FALSE)
|
||||
if(!silent)
|
||||
to_chat(user, "<span class='notice'>You slot [TC] into [parent] and charge its internal uplink.</span>")
|
||||
to_chat(user, span_notice("You slot [TC] into [parent] and charge its internal uplink."))
|
||||
var/amt = TC.amount
|
||||
telecrystals += amt
|
||||
TC.use(amt)
|
||||
|
||||
/datum/component/uplink/proc/set_gamemode(_gamemode)
|
||||
gamemode = _gamemode
|
||||
uplink_items = get_uplink_items(gamemode, TRUE, allow_restricted)
|
||||
// log_uplink("[key_name(user)] loaded [amt] telecrystals into [parent]'s uplink")
|
||||
|
||||
/datum/component/uplink/proc/OnAttackBy(datum/source, obj/item/I, mob/user)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(!active)
|
||||
return //no hitting everyone/everything just to try to slot tcs in!
|
||||
return //no hitting everyone/everything just to try to slot tcs in!
|
||||
if(istype(I, /obj/item/stack/telecrystal))
|
||||
LoadTC(user, I)
|
||||
// CIT SPECIFIC: STEALING from unlocked uplink.
|
||||
if(active)
|
||||
if(I.GetComponent(/datum/component/uplink))
|
||||
var/datum/component/uplink/hidden_uplink = I.GetComponent(/datum/component/uplink)
|
||||
@@ -118,31 +130,26 @@ GLOBAL_LIST_EMPTY(uplinks)
|
||||
var/cost = UI.refund_amount || UI.cost
|
||||
if(I.type == path && UI.refundable && I.check_uplink_validity())
|
||||
telecrystals += cost
|
||||
purchase_log.total_spent -= cost
|
||||
to_chat(user, "<span class='notice'>[I] refunded.</span>")
|
||||
// log_uplink("[key_name(user)] refunded [UI] for [cost] telecrystals using [parent]'s uplink")
|
||||
if(purchase_log)
|
||||
purchase_log.total_spent -= cost
|
||||
to_chat(user, span_notice("[I] refunded."))
|
||||
qdel(I)
|
||||
return
|
||||
|
||||
/datum/component/uplink/proc/interact(datum/source, mob/user)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(locked)
|
||||
return
|
||||
active = TRUE
|
||||
update_items()
|
||||
if(user)
|
||||
//update the saved population
|
||||
var/previous_player_population = saved_player_population
|
||||
saved_player_population = GLOB.joined_player_list.len
|
||||
//if population has changed, update uplink items
|
||||
if(saved_player_population != previous_player_population)
|
||||
//make sure discounts are not rerolled
|
||||
var/old_discounts = uplink_items["Discounted Gear"]
|
||||
uplink_items = get_uplink_items(gamemode, FALSE, allow_restricted, filters)
|
||||
if(old_discounts)
|
||||
uplink_items["Discounted Gear"] = old_discounts
|
||||
ui_interact(user)
|
||||
|
||||
INVOKE_ASYNC(src, .proc/ui_interact, user)
|
||||
// an unlocked uplink blocks also opening the PDA or headset menu
|
||||
return COMPONENT_NO_INTERACT
|
||||
|
||||
|
||||
/datum/component/uplink/ui_state(mob/user)
|
||||
if(istype(parent, /obj/item/implant/uplink))
|
||||
return GLOB.not_incapacitated_state
|
||||
@@ -178,15 +185,10 @@ GLOBAL_LIST_EMPTY(uplinks)
|
||||
var/datum/uplink_item/I = uplink_items[category][item]
|
||||
if(I.limited_stock == 0)
|
||||
continue
|
||||
if(I.restricted_roles.len)
|
||||
var/is_inaccessible = TRUE
|
||||
for(var/R in I.restricted_roles)
|
||||
if(R == user.mind.assigned_role || debug)
|
||||
is_inaccessible = FALSE
|
||||
if(is_inaccessible)
|
||||
if(length(I.restricted_roles))
|
||||
if(!debug && !(user.mind.assigned_role in I.restricted_roles))
|
||||
continue
|
||||
/*
|
||||
if(I.restricted_species) //catpeople specfic gloves.
|
||||
if(I.restricted_species)
|
||||
if(ishuman(user))
|
||||
var/is_inaccessible = TRUE
|
||||
var/mob/living/carbon/human/H = user
|
||||
@@ -196,7 +198,6 @@ GLOBAL_LIST_EMPTY(uplinks)
|
||||
break
|
||||
if(is_inaccessible)
|
||||
continue
|
||||
*/
|
||||
cat["items"] += list(list(
|
||||
"name" = I.name,
|
||||
"cost" = I.cost,
|
||||
@@ -255,25 +256,41 @@ GLOBAL_LIST_EMPTY(uplinks)
|
||||
// Implant signal responses
|
||||
|
||||
/datum/component/uplink/proc/implant_activation()
|
||||
SIGNAL_HANDLER
|
||||
|
||||
var/obj/item/implant/implant = parent
|
||||
locked = FALSE
|
||||
interact(null, implant.imp_in)
|
||||
|
||||
/datum/component/uplink/proc/implanting(datum/source, list/arguments)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
var/mob/user = arguments[2]
|
||||
owner = "[user.key]"
|
||||
owner = user?.key
|
||||
if(owner && !purchase_log)
|
||||
LAZYINITLIST(GLOB.uplink_purchase_logs_by_key)
|
||||
if(GLOB.uplink_purchase_logs_by_key[owner])
|
||||
purchase_log = GLOB.uplink_purchase_logs_by_key[owner]
|
||||
else
|
||||
purchase_log = new(owner, src)
|
||||
|
||||
/datum/component/uplink/proc/old_implant(datum/source, list/arguments, obj/item/implant/new_implant)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
// It kinda has to be weird like this until implants are components
|
||||
return SEND_SIGNAL(new_implant, COMSIG_IMPLANT_EXISTING_UPLINK, src)
|
||||
|
||||
/datum/component/uplink/proc/new_implant(datum/source, datum/component/uplink/uplink)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
uplink.telecrystals += telecrystals
|
||||
return COMPONENT_DELETE_NEW_IMPLANT
|
||||
|
||||
// PDA signal responses
|
||||
|
||||
/datum/component/uplink/proc/new_ringtone(datum/source, mob/living/user, new_ring_text)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
var/obj/item/pda/master = parent
|
||||
if(trim(lowertext(new_ring_text)) != trim(lowertext(unlock_code)))
|
||||
if(trim(lowertext(new_ring_text)) == trim(lowertext(failsafe_code)))
|
||||
@@ -282,14 +299,21 @@ GLOBAL_LIST_EMPTY(uplinks)
|
||||
return
|
||||
locked = FALSE
|
||||
interact(null, user)
|
||||
to_chat(user, "<span class='hear'>The PDA softly beeps.</span>")
|
||||
to_chat(user, span_hear("The PDA softly beeps."))
|
||||
user << browse(null, "window=pda")
|
||||
master.mode = 0
|
||||
return COMPONENT_STOP_RINGTONE_CHANGE
|
||||
|
||||
/datum/component/uplink/proc/check_detonate()
|
||||
SIGNAL_HANDLER
|
||||
|
||||
// return COMPONENT_PDA_NO_DETONATE
|
||||
|
||||
// Radio signal responses
|
||||
|
||||
/datum/component/uplink/proc/new_frequency(datum/source, list/arguments)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
var/obj/item/radio/master = parent
|
||||
var/frequency = arguments[1]
|
||||
if(frequency != unlock_code)
|
||||
@@ -303,15 +327,22 @@ GLOBAL_LIST_EMPTY(uplinks)
|
||||
// Pen signal responses
|
||||
|
||||
/datum/component/uplink/proc/pen_rotation(datum/source, degrees, mob/living/carbon/user)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
var/obj/item/pen/master = parent
|
||||
if(degrees != unlock_code)
|
||||
if(degrees == failsafe_code) //Getting failsafes on pens is risky business
|
||||
failsafe()
|
||||
return
|
||||
locked = FALSE
|
||||
master.degrees = 0
|
||||
interact(null, user)
|
||||
to_chat(user, "<span class='warning'>Your pen makes a clicking noise, before quickly rotating back to 0 degrees!</span>")
|
||||
previous_attempts += degrees
|
||||
if(length(previous_attempts) > PEN_ROTATIONS)
|
||||
popleft(previous_attempts)
|
||||
|
||||
if(compare_list(previous_attempts, unlock_code))
|
||||
locked = FALSE
|
||||
previous_attempts.Cut()
|
||||
master.degrees = 0
|
||||
interact(null, user)
|
||||
to_chat(user, span_warning("Your pen makes a clicking noise, before quickly rotating back to 0 degrees!"))
|
||||
|
||||
else if(compare_list(previous_attempts, failsafe_code))
|
||||
failsafe(user)
|
||||
|
||||
/datum/component/uplink/proc/setup_unlock_code()
|
||||
unlock_code = generate_code()
|
||||
@@ -321,15 +352,18 @@ GLOBAL_LIST_EMPTY(uplinks)
|
||||
else if(istype(parent,/obj/item/radio))
|
||||
unlock_note = "<B>Radio Frequency:</B> [format_frequency(unlock_code)] ([P.name])."
|
||||
else if(istype(parent,/obj/item/pen))
|
||||
unlock_note = "<B>Uplink Degrees:</B> [unlock_code] ([P.name])."
|
||||
unlock_note = "<B>Uplink Degrees:</B> [english_list(unlock_code)] ([P.name])."
|
||||
|
||||
/datum/component/uplink/proc/generate_code()
|
||||
if(istype(parent,/obj/item/pda))
|
||||
return "[rand(100,999)] [pick(GLOB.phonetic_alphabet)]"
|
||||
else if(istype(parent,/obj/item/radio))
|
||||
return sanitize_frequency(rand(MIN_FREQ, MAX_FREQ))
|
||||
return return_unused_frequency()
|
||||
else if(istype(parent,/obj/item/pen))
|
||||
return rand(1, 360)
|
||||
var/list/L = list()
|
||||
for(var/i in 1 to PEN_ROTATIONS)
|
||||
L += rand(1, 360)
|
||||
return L
|
||||
|
||||
/datum/component/uplink/proc/failsafe(mob/living/carbon/user)
|
||||
if(!parent)
|
||||
@@ -339,5 +373,5 @@ GLOBAL_LIST_EMPTY(uplinks)
|
||||
return
|
||||
message_admins("[ADMIN_LOOKUPFLW(user)] has triggered an uplink failsafe explosion at [AREACOORD(T)] The owner of the uplink was [ADMIN_LOOKUPFLW(owner)].")
|
||||
log_game("[key_name(user)] triggered an uplink failsafe explosion. The owner of the uplink was [key_name(owner)].")
|
||||
explosion(T,1,2,3)
|
||||
explosion(parent, devastation_range = 1, heavy_impact_range = 2, light_impact_range = 3)
|
||||
qdel(parent) //Alternatively could brick the uplink.
|
||||
|
||||
@@ -106,7 +106,7 @@
|
||||
active_timers = null
|
||||
for(var/thing in timers)
|
||||
var/datum/timedevent/timer = thing
|
||||
if (timer.spent)
|
||||
if (timer.spent && !(timer.flags & TIMER_DELETE_ME))
|
||||
continue
|
||||
qdel(timer)
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
return
|
||||
|
||||
switch(stage)
|
||||
if(2 || 3) //Pretend to be a cold and give time to spread.
|
||||
if(2, 3) //Pretend to be a cold and give time to spread.
|
||||
if(prob(8))
|
||||
affected_mob.emote("sneeze")
|
||||
if(prob(8))
|
||||
|
||||
@@ -168,7 +168,7 @@
|
||||
L.visible_message("<span class='warning'>[held_mob] escapes from [L]!</span>", "<span class='warning'>[held_mob] escapes your grip!</span>")
|
||||
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)
|
||||
/obj/item/clothing/head/mob_holder/mob_can_equip(M, equipper, slot, disable_warning, bypass_equip_delay_self)
|
||||
if(M == held_mob || !ishuman(M)) //monkeys holding monkeys holding monkeys...
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
@@ -0,0 +1,517 @@
|
||||
/// An element for atoms that, when dragged and dropped onto a mob, opens a strip panel.
|
||||
/datum/element/strippable
|
||||
element_flags = ELEMENT_BESPOKE | ELEMENT_DETACH
|
||||
id_arg_index = 2
|
||||
|
||||
/// An assoc list of keys to /datum/strippable_item
|
||||
var/list/items
|
||||
|
||||
/// A proc path that returns TRUE/FALSE if we should show the strip panel for this entity.
|
||||
/// If it does not exist, the strip menu will always show.
|
||||
/// Will be called with (mob/user).
|
||||
var/should_strip_proc_path
|
||||
|
||||
/// An existing strip menus
|
||||
var/list/strip_menus
|
||||
|
||||
/datum/element/strippable/Attach(datum/target, list/items, should_strip_proc_path)
|
||||
. = ..()
|
||||
if (!isatom(target))
|
||||
return ELEMENT_INCOMPATIBLE
|
||||
|
||||
RegisterSignal(target, COMSIG_MOUSEDROP_ONTO, .proc/mouse_drop_onto)
|
||||
|
||||
src.items = items
|
||||
src.should_strip_proc_path = should_strip_proc_path
|
||||
|
||||
/datum/element/strippable/Detach(datum/source)
|
||||
. = ..()
|
||||
|
||||
UnregisterSignal(source, COMSIG_MOUSEDROP_ONTO)
|
||||
|
||||
if (!isnull(strip_menus))
|
||||
qdel(strip_menus[source])
|
||||
strip_menus -= source
|
||||
|
||||
/datum/element/strippable/proc/mouse_drop_onto(datum/source, atom/over, mob/user)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if (user == source)
|
||||
return
|
||||
|
||||
if (over != user)
|
||||
return
|
||||
|
||||
// Cyborgs buckle people by dragging them onto them, unless in combat mode.
|
||||
if (iscyborg(user))
|
||||
var/mob/living/silicon/robot/cyborg_user = user
|
||||
if (cyborg_user.a_intent == INTENT_HARM)
|
||||
return
|
||||
|
||||
if (!isnull(should_strip_proc_path) && !call(source, should_strip_proc_path)(user))
|
||||
return
|
||||
|
||||
var/datum/strip_menu/strip_menu
|
||||
|
||||
if (isnull(strip_menu))
|
||||
strip_menu = new(source, src)
|
||||
LAZYSET(strip_menus, source, strip_menu)
|
||||
|
||||
INVOKE_ASYNC(strip_menu, /datum/.proc/ui_interact, user)
|
||||
|
||||
/// A representation of an item that can be stripped down
|
||||
/datum/strippable_item
|
||||
/// The STRIPPABLE_ITEM_* key
|
||||
var/key
|
||||
|
||||
/// Should we warn about dangerous clothing?
|
||||
var/warn_dangerous_clothing = TRUE
|
||||
|
||||
/// Gets the item from the given source.
|
||||
/datum/strippable_item/proc/get_item(atom/source)
|
||||
|
||||
/// Tries to equip the item onto the given source.
|
||||
/// Returns TRUE/FALSE depending on if it is allowed.
|
||||
/// This should be used for checking if an item CAN be equipped.
|
||||
/// It should not perform the equipping itself.
|
||||
/datum/strippable_item/proc/try_equip(atom/source, obj/item/equipping, mob/user)
|
||||
if (HAS_TRAIT(equipping, TRAIT_NODROP))
|
||||
to_chat(user, span_warning("You can't put [equipping] on [source], it's stuck to your hand!"))
|
||||
return FALSE
|
||||
|
||||
return TRUE
|
||||
|
||||
/// Start the equipping process. This is the proc you should yield in.
|
||||
/// Returns TRUE/FALSE depending on if it is allowed.
|
||||
/datum/strippable_item/proc/start_equip(atom/source, obj/item/equipping, mob/user)
|
||||
if (warn_dangerous_clothing && isclothing(source))
|
||||
source.visible_message(
|
||||
span_notice("[user] tries to put [equipping] on [source]."),
|
||||
span_notice("[user] tries to put [equipping] on you."),
|
||||
ignored_mobs = user,
|
||||
)
|
||||
|
||||
if(ishuman(source))
|
||||
var/mob/living/carbon/human/victim_human = source
|
||||
if(victim_human.key && !victim_human.client) // AKA braindead
|
||||
if(victim_human.stat <= SOFT_CRIT && LAZYLEN(victim_human.afk_thefts) <= AFK_THEFT_MAX_MESSAGES)
|
||||
var/list/new_entry = list(list(user.name, "tried equipping you with [equipping]", world.time))
|
||||
LAZYADD(victim_human.afk_thefts, new_entry)
|
||||
|
||||
to_chat(user, span_notice("You try to put [equipping] on [source]..."))
|
||||
|
||||
var/log = "[key_name(source)] is having [equipping] put on them by [key_name(user)]"
|
||||
user.log_message(log, LOG_ATTACK, color="red")
|
||||
source.log_message(log, LOG_VICTIM, color="red", log_globally=FALSE)
|
||||
|
||||
return TRUE
|
||||
|
||||
/// The proc that places the item on the source. This should not yield.
|
||||
/datum/strippable_item/proc/finish_equip(atom/source, obj/item/equipping, mob/user)
|
||||
SHOULD_NOT_SLEEP(TRUE)
|
||||
return TRUE
|
||||
|
||||
/// Tries to unequip the item from the given source.
|
||||
/// Returns TRUE/FALSE depending on if it is allowed.
|
||||
/// This should be used for checking if it CAN be unequipped.
|
||||
/// It should not perform the unequipping itself.
|
||||
/datum/strippable_item/proc/try_unequip(atom/source, mob/user)
|
||||
SHOULD_NOT_SLEEP(TRUE)
|
||||
|
||||
var/obj/item/item = get_item(source)
|
||||
if (isnull(item))
|
||||
return FALSE
|
||||
|
||||
if (ismob(source))
|
||||
var/mob/mob_source = source
|
||||
if (!item.canStrip(user, mob_source))
|
||||
return FALSE
|
||||
|
||||
return TRUE
|
||||
|
||||
/// Start the unequipping process. This is the proc you should yield in.
|
||||
/// Returns TRUE/FALSE depending on if it is allowed.
|
||||
/datum/strippable_item/proc/start_unequip(atom/source, mob/user)
|
||||
var/obj/item/item = get_item(source)
|
||||
if (isnull(item))
|
||||
return FALSE
|
||||
|
||||
source.visible_message(
|
||||
span_warning("[user] tries to remove [source]'s [item.name]."),
|
||||
span_userdanger("[user] tries to remove your [item.name]."),
|
||||
ignored_mobs = user,
|
||||
)
|
||||
|
||||
to_chat(user, span_danger("You try to remove [source]'s [item]..."))
|
||||
user.log_message("[key_name(source)] is being stripped of [item] by [key_name(user)]", LOG_ATTACK, color="red")
|
||||
source.log_message("[key_name(source)] is being stripped of [item] by [key_name(user)]", LOG_VICTIM, color="red", log_globally=FALSE)
|
||||
item.add_fingerprint(source)
|
||||
|
||||
if(ishuman(source))
|
||||
var/mob/living/carbon/human/victim_human = source
|
||||
if(victim_human.key && !victim_human.client) // AKA braindead
|
||||
if(victim_human.stat <= SOFT_CRIT && LAZYLEN(victim_human.afk_thefts) <= AFK_THEFT_MAX_MESSAGES)
|
||||
var/list/new_entry = list(list(user.name, "tried unequipping your [item.name]", world.time))
|
||||
LAZYADD(victim_human.afk_thefts, new_entry)
|
||||
|
||||
return TRUE
|
||||
|
||||
/// The proc that unequips the item from the source. This should not yield.
|
||||
/datum/strippable_item/proc/finish_unequip(atom/source, mob/user)
|
||||
|
||||
/// Returns a STRIPPABLE_OBSCURING_* define to report on whether or not this is obscured.
|
||||
/datum/strippable_item/proc/get_obscuring(atom/source)
|
||||
SHOULD_NOT_SLEEP(TRUE)
|
||||
return STRIPPABLE_OBSCURING_NONE
|
||||
|
||||
/// Returns the ID of this item's strippable action.
|
||||
/// Return `null` if there is no alternate action.
|
||||
/// Any return value of this must be in StripMenu.
|
||||
/datum/strippable_item/proc/get_alternate_action(atom/source, mob/user)
|
||||
if(get_obscuring(source) == STRIPPABLE_OBSCURING_COMPLETELY)
|
||||
return FALSE
|
||||
return null
|
||||
|
||||
/// Performs an alternative action on this strippable_item.
|
||||
/// `has_alternate_action` needs to be TRUE.
|
||||
/datum/strippable_item/proc/alternate_action(atom/source, mob/user)
|
||||
if(get_obscuring(source) == STRIPPABLE_OBSCURING_COMPLETELY)
|
||||
return null
|
||||
return TRUE
|
||||
|
||||
/// Returns whether or not this item should show.
|
||||
/datum/strippable_item/proc/should_show(atom/source, mob/user)
|
||||
return TRUE
|
||||
|
||||
/// A preset for equipping items onto mob slots
|
||||
/datum/strippable_item/mob_item_slot
|
||||
/// The ITEM_SLOT_* to equip to.
|
||||
var/item_slot
|
||||
|
||||
/datum/strippable_item/mob_item_slot/get_item(atom/source)
|
||||
if (!ismob(source))
|
||||
return null
|
||||
|
||||
var/mob/mob_source = source
|
||||
return mob_source.get_item_by_slot(item_slot)
|
||||
|
||||
/datum/strippable_item/mob_item_slot/try_equip(atom/source, obj/item/equipping, mob/user)
|
||||
. = ..()
|
||||
if (!.)
|
||||
return
|
||||
|
||||
if (!ismob(source))
|
||||
return FALSE
|
||||
|
||||
if (!equipping.mob_can_equip(
|
||||
source,
|
||||
user,
|
||||
item_slot,
|
||||
disable_warning = TRUE,
|
||||
bypass_equip_delay_self = TRUE,
|
||||
))
|
||||
to_chat(user, span_warning("\The [equipping] doesn't fit in that place!"))
|
||||
return FALSE
|
||||
|
||||
return TRUE
|
||||
|
||||
/datum/strippable_item/mob_item_slot/start_equip(atom/source, obj/item/equipping, mob/user)
|
||||
. = ..()
|
||||
if (!.)
|
||||
return
|
||||
|
||||
if (!ismob(source))
|
||||
return FALSE
|
||||
|
||||
if (!do_mob(user, source, get_equip_delay(equipping)))
|
||||
return FALSE
|
||||
|
||||
if(get_obscuring(source) == STRIPPABLE_OBSCURING_COMPLETELY)
|
||||
return FALSE
|
||||
|
||||
if (!equipping.mob_can_equip(
|
||||
source,
|
||||
user,
|
||||
item_slot,
|
||||
disable_warning = TRUE,
|
||||
bypass_equip_delay_self = TRUE,
|
||||
))
|
||||
return FALSE
|
||||
|
||||
if (!user.temporarilyRemoveItemFromInventory(equipping))
|
||||
return FALSE
|
||||
|
||||
return TRUE
|
||||
|
||||
/datum/strippable_item/mob_item_slot/finish_equip(atom/source, obj/item/equipping, mob/user)
|
||||
if(!..())
|
||||
return FALSE
|
||||
if (!ismob(source))
|
||||
return FALSE
|
||||
|
||||
var/mob/mob_source = source
|
||||
mob_source.equip_to_slot(equipping, item_slot)
|
||||
|
||||
/datum/strippable_item/mob_item_slot/get_obscuring(atom/source)
|
||||
if (iscarbon(source))
|
||||
var/mob/living/carbon/carbon_source = source
|
||||
return (item_slot in carbon_source.check_obscured_slots()) \
|
||||
? STRIPPABLE_OBSCURING_COMPLETELY \
|
||||
: STRIPPABLE_OBSCURING_NONE
|
||||
|
||||
return FALSE
|
||||
|
||||
/datum/strippable_item/mob_item_slot/start_unequip(atom/source, mob/user)
|
||||
. = ..()
|
||||
if (!.)
|
||||
return
|
||||
|
||||
return start_unequip_mob(get_item(source), source, user)
|
||||
|
||||
/datum/strippable_item/mob_item_slot/finish_unequip(atom/source, mob/user)
|
||||
var/obj/item/item = get_item(source)
|
||||
if (isnull(item))
|
||||
return FALSE
|
||||
|
||||
if (!ismob(source))
|
||||
return FALSE
|
||||
|
||||
return finish_unequip_mob(item, source, user)
|
||||
|
||||
/// Returns the delay of equipping this item to a mob
|
||||
/datum/strippable_item/mob_item_slot/proc/get_equip_delay(obj/item/equipping)
|
||||
return equipping.equip_delay_other
|
||||
|
||||
/// A utility function for `/datum/strippable_item`s to start unequipping an item from a mob.
|
||||
/proc/start_unequip_mob(obj/item/item, mob/source, mob/user, strip_delay)
|
||||
if (!do_mob(user, source, strip_delay || item.strip_delay, ignorehelditem = TRUE))
|
||||
return FALSE
|
||||
|
||||
return TRUE
|
||||
|
||||
/// A utility function for `/datum/strippable_item`s to finish unequipping an item from a mob.
|
||||
/proc/finish_unequip_mob(obj/item/item, mob/source, mob/user)
|
||||
if (!item.doStrip(user, source))
|
||||
return FALSE
|
||||
|
||||
user.log_message("[key_name(source)] has been stripped of [item] by [key_name(user)]", LOG_ATTACK, color="red")
|
||||
source.log_message("[key_name(source)] has been stripped of [item] by [key_name(user)]", LOG_VICTIM, color="red", log_globally=FALSE)
|
||||
|
||||
// Updates speed in case stripped speed affecting item
|
||||
source.update_equipment_speed_mods()
|
||||
|
||||
/// A representation of the stripping UI
|
||||
/datum/strip_menu
|
||||
/// The owner who has the element /datum/element/strippable
|
||||
var/atom/movable/owner
|
||||
|
||||
/// The strippable element itself
|
||||
var/datum/element/strippable/strippable
|
||||
|
||||
/// A lazy list of user mobs to a list of strip menu keys that they're interacting with
|
||||
var/list/interactions
|
||||
|
||||
/datum/strip_menu/New(atom/movable/owner, datum/element/strippable/strippable)
|
||||
. = ..()
|
||||
src.owner = owner
|
||||
src.strippable = strippable
|
||||
|
||||
/datum/strip_menu/Destroy()
|
||||
owner = null
|
||||
strippable = null
|
||||
|
||||
return ..()
|
||||
|
||||
/datum/strip_menu/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if (!ui)
|
||||
ui = new(user, src, "StripMenu")
|
||||
ui.open()
|
||||
|
||||
/datum/strip_menu/ui_assets(mob/user)
|
||||
return list(
|
||||
get_asset_datum(/datum/asset/simple/inventory),
|
||||
)
|
||||
|
||||
/datum/strip_menu/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
|
||||
var/list/items = list()
|
||||
|
||||
for (var/strippable_key in strippable.items)
|
||||
var/datum/strippable_item/item_data = strippable.items[strippable_key]
|
||||
|
||||
if (!item_data.should_show(owner, user))
|
||||
continue
|
||||
|
||||
var/list/result
|
||||
|
||||
if(strippable_key in LAZYACCESS(interactions, user))
|
||||
LAZYSET(result, "interacting", TRUE)
|
||||
|
||||
var/obscuring = item_data.get_obscuring(owner)
|
||||
if (obscuring != STRIPPABLE_OBSCURING_NONE)
|
||||
LAZYSET(result, "obscured", obscuring)
|
||||
items[strippable_key] = result
|
||||
continue
|
||||
|
||||
var/obj/item/item = item_data.get_item(owner)
|
||||
if (isnull(item))
|
||||
items[strippable_key] = result
|
||||
continue
|
||||
|
||||
LAZYINITLIST(result)
|
||||
|
||||
result["icon"] = icon2base64(icon(item.icon, item.icon_state, SOUTH, 1))
|
||||
result["name"] = item.name
|
||||
result["alternate"] = item_data.get_alternate_action(owner, user)
|
||||
|
||||
items[strippable_key] = result
|
||||
|
||||
data["items"] = items
|
||||
|
||||
// While most `\the`s are implicit, this one is not.
|
||||
// In this case, `\The` would otherwise be used.
|
||||
// This doesn't match with what it's used for, which is to say "Stripping the alien drone",
|
||||
// as opposed to "Stripping The alien drone".
|
||||
// Human names will still show without "the", as they are proper nouns.
|
||||
data["name"] = "\the [owner]"
|
||||
|
||||
/// Customize the strip menu
|
||||
data["long_strip_menu"] = user.client.prefs.long_strip_menu
|
||||
|
||||
return data
|
||||
|
||||
/datum/strip_menu/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
||||
. = ..()
|
||||
if (.)
|
||||
return
|
||||
|
||||
. = TRUE
|
||||
|
||||
var/mob/user = usr
|
||||
|
||||
switch (action)
|
||||
if ("use")
|
||||
var/key = params["key"]
|
||||
var/datum/strippable_item/strippable_item = strippable.items[key]
|
||||
|
||||
if (isnull(strippable_item))
|
||||
return
|
||||
|
||||
if (!strippable_item.should_show(owner, user))
|
||||
return
|
||||
|
||||
if (strippable_item.get_obscuring(owner) == STRIPPABLE_OBSCURING_COMPLETELY)
|
||||
return
|
||||
|
||||
var/item = strippable_item.get_item(owner)
|
||||
if (isnull(item))
|
||||
var/obj/item/held_item = user.get_active_held_item()
|
||||
if (isnull(held_item))
|
||||
return
|
||||
|
||||
if (strippable_item.try_equip(owner, held_item, user))
|
||||
LAZYORASSOCLIST(interactions, user, key)
|
||||
|
||||
// Yielding call
|
||||
var/should_finish = strippable_item.start_equip(owner, held_item, user)
|
||||
|
||||
LAZYREMOVEASSOC(interactions, user, key)
|
||||
|
||||
if (!should_finish)
|
||||
return
|
||||
|
||||
if (QDELETED(src) || QDELETED(owner))
|
||||
return
|
||||
|
||||
// They equipped an item in the meantime
|
||||
if (!isnull(strippable_item.get_item(owner)))
|
||||
return
|
||||
|
||||
if (!user.Adjacent(owner))
|
||||
return
|
||||
|
||||
strippable_item.finish_equip(owner, held_item, user)
|
||||
else if (strippable_item.try_unequip(owner, user))
|
||||
LAZYORASSOCLIST(interactions, user, key)
|
||||
|
||||
var/should_unequip = strippable_item.start_unequip(owner, user)
|
||||
|
||||
LAZYREMOVEASSOC(interactions, user, key)
|
||||
|
||||
// Yielding call
|
||||
if (!should_unequip)
|
||||
return
|
||||
|
||||
if (QDELETED(src) || QDELETED(owner))
|
||||
return
|
||||
|
||||
// They changed the item in the meantime
|
||||
if (strippable_item.get_item(owner) != item)
|
||||
return
|
||||
|
||||
if (!user.Adjacent(owner))
|
||||
return
|
||||
|
||||
strippable_item.finish_unequip(owner, user)
|
||||
if ("alt")
|
||||
var/key = params["key"]
|
||||
var/datum/strippable_item/strippable_item = strippable.items[key]
|
||||
|
||||
if (isnull(strippable_item))
|
||||
return
|
||||
|
||||
if (!strippable_item.should_show(owner, user))
|
||||
return
|
||||
|
||||
if (strippable_item.get_obscuring(owner) == STRIPPABLE_OBSCURING_COMPLETELY)
|
||||
return
|
||||
|
||||
var/item = strippable_item.get_item(owner)
|
||||
if (isnull(item))
|
||||
return
|
||||
|
||||
if (isnull(strippable_item.get_alternate_action(owner, user)))
|
||||
return
|
||||
|
||||
LAZYORASSOCLIST(interactions, user, key)
|
||||
|
||||
// Potentially yielding
|
||||
strippable_item.alternate_action(owner, user)
|
||||
|
||||
LAZYREMOVEASSOC(interactions, user, key)
|
||||
|
||||
/datum/strip_menu/ui_host(mob/user)
|
||||
return owner
|
||||
|
||||
/datum/strip_menu/ui_state(mob/user)
|
||||
return GLOB.always_state
|
||||
|
||||
/datum/strip_menu/ui_status(mob/user, datum/ui_state/state)
|
||||
. = ..()
|
||||
|
||||
if(isliving(user))
|
||||
var/mob/living/living_user = user
|
||||
|
||||
if (
|
||||
living_user.stat == CONSCIOUS \
|
||||
&& living_user.Adjacent(owner)
|
||||
)
|
||||
return UI_INTERACTIVE
|
||||
if(IsAdminGhost(user))
|
||||
return UI_INTERACTIVE
|
||||
if(user.Adjacent(owner))
|
||||
return UI_UPDATE
|
||||
else
|
||||
return UI_DISABLED
|
||||
|
||||
/// Creates an assoc list of keys to /datum/strippable_item
|
||||
/proc/create_strippable_list(types)
|
||||
var/list/strippable_items = list()
|
||||
|
||||
for (var/strippable_type in types)
|
||||
var/datum/strippable_item/strippable_item = new strippable_type
|
||||
strippable_items[strippable_item.key] = strippable_item
|
||||
|
||||
return strippable_items
|
||||
@@ -0,0 +1,44 @@
|
||||
///This element just handles creating and destroying an area sound manager that's hooked into weather stuff
|
||||
/datum/element/weather_listener
|
||||
element_flags = ELEMENT_BESPOKE
|
||||
id_arg_index = 2
|
||||
var/weather_type
|
||||
//What events to change the track on
|
||||
var/list/sound_change_signals
|
||||
//The weather type we're working with
|
||||
var/weather_trait
|
||||
//The playlist of sounds to draw from. Pass by ref
|
||||
var/list/playlist
|
||||
|
||||
|
||||
/datum/element/weather_listener/Attach(datum/target, w_type, trait, weather_playlist)
|
||||
. = ..()
|
||||
if(!weather_type)
|
||||
weather_type = w_type
|
||||
sound_change_signals = list(
|
||||
COMSIG_WEATHER_TELEGRAPH(weather_type),
|
||||
COMSIG_WEATHER_START(weather_type),
|
||||
COMSIG_WEATHER_WINDDOWN(weather_type),
|
||||
COMSIG_WEATHER_END(weather_type)
|
||||
)
|
||||
weather_trait = trait
|
||||
playlist = weather_playlist
|
||||
|
||||
RegisterSignal(target, COMSIG_MOVABLE_Z_CHANGED, .proc/handle_z_level_change, override = TRUE)
|
||||
RegisterSignal(target, COMSIG_MOB_CLIENT_LOGOUT, .proc/handle_logout, override = TRUE)
|
||||
|
||||
/datum/element/weather_listener/Detach(datum/source)
|
||||
. = ..()
|
||||
UnregisterSignal(source, COMSIG_MOVABLE_Z_CHANGED, COMSIG_MOB_CLIENT_LOGOUT)
|
||||
|
||||
/datum/element/weather_listener/proc/handle_z_level_change(datum/source, old_z, new_z)
|
||||
SIGNAL_HANDLER
|
||||
var/list/fitting_z_levels = SSmapping.levels_by_trait(weather_trait)
|
||||
if(!(new_z in fitting_z_levels))
|
||||
return
|
||||
var/datum/component/our_comp = source.AddComponent(/datum/component/area_sound_manager, playlist, list(), COMSIG_MOB_CLIENT_LOGOUT, fitting_z_levels)
|
||||
our_comp.RegisterSignal(SSdcs, sound_change_signals, /datum/component/area_sound_manager/proc/handle_change)
|
||||
|
||||
/datum/element/weather_listener/proc/handle_logout(datum/source, client/this_is_a_null_ref)
|
||||
SIGNAL_HANDLER
|
||||
source.RemoveElement(/datum/element/weather_listener, weather_type, weather_trait, playlist)
|
||||
@@ -1,132 +0,0 @@
|
||||
/* HUD DATUMS */
|
||||
|
||||
GLOBAL_LIST_EMPTY(all_huds)
|
||||
|
||||
//GLOBAL HUD LIST
|
||||
GLOBAL_LIST_INIT(huds, list(
|
||||
DATA_HUD_SECURITY_BASIC = new/datum/atom_hud/data/human/security/basic(),
|
||||
DATA_HUD_SECURITY_ADVANCED = new/datum/atom_hud/data/human/security/advanced(),
|
||||
DATA_HUD_MEDICAL_BASIC = new/datum/atom_hud/data/human/medical/basic(),
|
||||
DATA_HUD_MEDICAL_ADVANCED = new/datum/atom_hud/data/human/medical/advanced(),
|
||||
DATA_HUD_DIAGNOSTIC_BASIC = new/datum/atom_hud/data/diagnostic/basic(),
|
||||
DATA_HUD_DIAGNOSTIC_ADVANCED = new/datum/atom_hud/data/diagnostic/advanced(),
|
||||
DATA_HUD_ABDUCTOR = new/datum/atom_hud/abductor(),
|
||||
DATA_HUD_SENTIENT_DISEASE = new/datum/atom_hud/sentient_disease(),
|
||||
DATA_HUD_AI_DETECT = new/datum/atom_hud/ai_detector(),
|
||||
ANTAG_HUD_CULT = new/datum/atom_hud/antag(),
|
||||
ANTAG_HUD_REV = new/datum/atom_hud/antag(),
|
||||
ANTAG_HUD_OPS = new/datum/atom_hud/antag(),
|
||||
ANTAG_HUD_WIZ = new/datum/atom_hud/antag(),
|
||||
ANTAG_HUD_SHADOW = new/datum/atom_hud/antag(),
|
||||
ANTAG_HUD_TRAITOR = new/datum/atom_hud/antag/hidden(),
|
||||
ANTAG_HUD_NINJA = new/datum/atom_hud/antag/hidden(),
|
||||
ANTAG_HUD_CHANGELING = new/datum/atom_hud/antag/hidden(),
|
||||
ANTAG_HUD_ABDUCTOR = new/datum/atom_hud/antag/hidden(),
|
||||
ANTAG_HUD_DEVIL = new/datum/atom_hud/antag(),
|
||||
ANTAG_HUD_SINTOUCHED = new/datum/atom_hud/antag/hidden(),
|
||||
ANTAG_HUD_SOULLESS = new/datum/atom_hud/antag/hidden(),
|
||||
ANTAG_HUD_CLOCKWORK = new/datum/atom_hud/antag(),
|
||||
ANTAG_HUD_BROTHER = new/datum/atom_hud/antag/hidden(),
|
||||
ANTAG_HUD_BLOODSUCKER = new/datum/atom_hud/antag/bloodsucker(),
|
||||
ANTAG_HUD_FUGITIVE = new/datum/atom_hud/antag(),
|
||||
ANTAG_HUD_HERETIC = new/datum/atom_hud/antag/hidden()
|
||||
))
|
||||
|
||||
/datum/atom_hud
|
||||
var/list/atom/hudatoms = list() //list of all atoms which display this hud
|
||||
var/list/hudusers = list() //list with all mobs who can see the hud
|
||||
var/list/hud_icons = list() //these will be the indexes for the atom's hud_list
|
||||
|
||||
var/list/next_time_allowed = list() //mobs associated with the next time this hud can be added to them
|
||||
var/list/queued_to_see = list() //mobs that have triggered the cooldown and are queued to see the hud, but do not yet
|
||||
|
||||
/datum/atom_hud/New()
|
||||
GLOB.all_huds += src
|
||||
|
||||
/datum/atom_hud/Destroy()
|
||||
for(var/v in hudusers)
|
||||
remove_hud_from(v)
|
||||
for(var/v in hudatoms)
|
||||
remove_from_hud(v)
|
||||
GLOB.all_huds -= src
|
||||
return ..()
|
||||
|
||||
/datum/atom_hud/proc/remove_hud_from(mob/M)
|
||||
if(!M || !hudusers[M])
|
||||
return
|
||||
if (!--hudusers[M])
|
||||
hudusers -= M
|
||||
if(queued_to_see[M])
|
||||
queued_to_see -= M
|
||||
else
|
||||
for(var/atom/A in hudatoms)
|
||||
remove_from_single_hud(M, A)
|
||||
|
||||
/datum/atom_hud/proc/remove_from_hud(atom/A)
|
||||
if(!A)
|
||||
return FALSE
|
||||
for(var/mob/M in hudusers)
|
||||
remove_from_single_hud(M, A)
|
||||
hudatoms -= A
|
||||
return TRUE
|
||||
|
||||
/datum/atom_hud/proc/remove_from_single_hud(mob/M, atom/A) //unsafe, no sanity apart from client
|
||||
if(!M || !M.client || !A)
|
||||
return
|
||||
for(var/i in hud_icons)
|
||||
M.client.images -= A.hud_list[i]
|
||||
|
||||
/datum/atom_hud/proc/add_hud_to(mob/M)
|
||||
if(!M)
|
||||
return
|
||||
if(!hudusers[M])
|
||||
hudusers[M] = 1
|
||||
if(next_time_allowed[M] > world.time)
|
||||
if(!queued_to_see[M])
|
||||
addtimer(CALLBACK(src, .proc/show_hud_images_after_cooldown, M), next_time_allowed[M] - world.time)
|
||||
queued_to_see[M] = TRUE
|
||||
else
|
||||
next_time_allowed[M] = world.time + ADD_HUD_TO_COOLDOWN
|
||||
for(var/atom/A in hudatoms)
|
||||
add_to_single_hud(M, A)
|
||||
else
|
||||
hudusers[M]++
|
||||
|
||||
/datum/atom_hud/proc/show_hud_images_after_cooldown(M)
|
||||
if(queued_to_see[M])
|
||||
queued_to_see -= M
|
||||
next_time_allowed[M] = world.time + ADD_HUD_TO_COOLDOWN
|
||||
for(var/atom/A in hudatoms)
|
||||
add_to_single_hud(M, A)
|
||||
|
||||
/datum/atom_hud/proc/add_to_hud(atom/A)
|
||||
if(!A)
|
||||
return FALSE
|
||||
hudatoms |= A
|
||||
for(var/mob/M in hudusers)
|
||||
if(!queued_to_see[M])
|
||||
add_to_single_hud(M, A)
|
||||
return TRUE
|
||||
|
||||
/datum/atom_hud/proc/add_to_single_hud(mob/M, atom/A) //unsafe, no sanity apart from client
|
||||
if(!M || !M.client || !A)
|
||||
return
|
||||
for(var/i in hud_icons)
|
||||
if(A.hud_list[i])
|
||||
M.client.images |= A.hud_list[i]
|
||||
|
||||
//MOB PROCS
|
||||
/mob/proc/reload_huds()
|
||||
for(var/datum/atom_hud/hud in GLOB.all_huds)
|
||||
if(hud && hud.hudusers[src])
|
||||
for(var/atom/A in hud.hudatoms)
|
||||
hud.add_to_single_hud(src, A)
|
||||
|
||||
/mob/dead/new_player/reload_huds()
|
||||
return
|
||||
|
||||
/mob/proc/add_click_catcher()
|
||||
client.screen += client.void
|
||||
|
||||
/mob/dead/new_player/add_click_catcher()
|
||||
return
|
||||
@@ -1,21 +1,21 @@
|
||||
/*
|
||||
output_atoms (list of atoms) The destination(s) for the sounds
|
||||
output_atoms (list of atoms) The destination(s) for the sounds
|
||||
|
||||
mid_sounds (list or soundfile) Since this can be either a list or a single soundfile you can have random sounds. May contain further lists but must contain a soundfile at the end.
|
||||
mid_length (num) The length to wait between playing mid_sounds
|
||||
mid_sounds (list or soundfile) Since this can be either a list or a single soundfile you can have random sounds. May contain further lists but must contain a soundfile at the end.
|
||||
mid_length (num) The length to wait between playing mid_sounds
|
||||
|
||||
start_sound (soundfile) Played before starting the mid_sounds loop
|
||||
start_length (num) How long to wait before starting the main loop after playing start_sound
|
||||
start_sound (soundfile) Played before starting the mid_sounds loop
|
||||
start_length (num) How long to wait before starting the main loop after playing start_sound
|
||||
|
||||
end_sound (soundfile) The sound played after the main loop has concluded
|
||||
end_sound (soundfile) The sound played after the main loop has concluded
|
||||
|
||||
chance (num) Chance per loop to play a mid_sound
|
||||
volume (num) Sound output volume
|
||||
max_loops (num) The max amount of loops to run for.
|
||||
direct (bool) If true plays directly to provided atoms instead of from them
|
||||
chance (num) Chance per loop to play a mid_sound
|
||||
volume (num) Sound output volume
|
||||
max_loops (num) The max amount of loops to run for.
|
||||
direct (bool) If true plays directly to provided atoms instead of from them
|
||||
*/
|
||||
/datum/looping_sound
|
||||
var/list/atom/output_atoms
|
||||
var/atom/parent
|
||||
var/mid_sounds
|
||||
var/mid_length
|
||||
///Override for volume of start sound
|
||||
@@ -34,38 +34,46 @@
|
||||
var/falloff_exponent
|
||||
var/timerid
|
||||
var/falloff_distance
|
||||
var/skip_starting_sounds = FALSE
|
||||
var/loop_started = FALSE
|
||||
|
||||
/datum/looping_sound/New(list/_output_atoms=list(), start_immediately=FALSE, _direct=FALSE)
|
||||
/datum/looping_sound/New(_parent, start_immediately=FALSE, _direct=FALSE, _skip_starting_sounds = FALSE)
|
||||
if(!mid_sounds)
|
||||
WARNING("A looping sound datum was created without sounds to play.")
|
||||
return
|
||||
|
||||
output_atoms = _output_atoms
|
||||
set_parent(_parent)
|
||||
direct = _direct
|
||||
skip_starting_sounds = _skip_starting_sounds
|
||||
|
||||
if(start_immediately)
|
||||
start()
|
||||
|
||||
/datum/looping_sound/Destroy()
|
||||
stop()
|
||||
output_atoms = null
|
||||
stop(TRUE)
|
||||
return ..()
|
||||
|
||||
/datum/looping_sound/proc/start(atom/add_thing)
|
||||
if(add_thing)
|
||||
output_atoms |= add_thing
|
||||
/datum/looping_sound/proc/start(on_behalf_of)
|
||||
if(on_behalf_of)
|
||||
set_parent(on_behalf_of)
|
||||
if(timerid)
|
||||
return
|
||||
on_start()
|
||||
|
||||
/datum/looping_sound/proc/stop(atom/remove_thing)
|
||||
if(remove_thing)
|
||||
output_atoms -= remove_thing
|
||||
/datum/looping_sound/proc/stop(null_parent)
|
||||
if(null_parent)
|
||||
set_parent(null)
|
||||
if(!timerid)
|
||||
return
|
||||
on_stop()
|
||||
deltimer(timerid)
|
||||
deltimer(timerid, SSsound_loops)
|
||||
timerid = null
|
||||
loop_started = FALSE
|
||||
|
||||
/datum/looping_sound/proc/start_sound_loop()
|
||||
loop_started = TRUE
|
||||
sound_loop()
|
||||
timerid = addtimer(CALLBACK(src, .proc/sound_loop, world.time), mid_length, TIMER_CLIENT_TIME | TIMER_STOPPABLE | TIMER_LOOP | TIMER_DELETE_ME, SSsound_loops)
|
||||
|
||||
/datum/looping_sound/proc/sound_loop(starttime)
|
||||
if(max_loops && world.time >= starttime + mid_length * max_loops)
|
||||
@@ -73,21 +81,15 @@
|
||||
return
|
||||
if(!chance || prob(chance))
|
||||
play(get_sound(starttime))
|
||||
if(!timerid)
|
||||
timerid = addtimer(CALLBACK(src, .proc/sound_loop, world.time), mid_length, TIMER_CLIENT_TIME | TIMER_STOPPABLE | TIMER_LOOP)
|
||||
|
||||
/datum/looping_sound/proc/play(soundfile, volume_override)
|
||||
var/list/atoms_cache = output_atoms
|
||||
var/sound/S = sound(soundfile)
|
||||
if(direct)
|
||||
S.channel = SSsounds.random_available_channel()
|
||||
S.volume = volume_override || volume //Use volume as fallback if theres no override
|
||||
for(var/i in 1 to atoms_cache.len)
|
||||
var/atom/thing = atoms_cache[i]
|
||||
if(direct)
|
||||
SEND_SOUND(thing, S)
|
||||
else
|
||||
playsound(thing, S, volume, vary, extra_range, falloff_exponent = falloff_exponent, falloff_distance = falloff_distance)
|
||||
SEND_SOUND(parent, S)
|
||||
else
|
||||
playsound(parent, S, volume, vary, extra_range, falloff_exponent = falloff_exponent, falloff_distance = falloff_distance)
|
||||
|
||||
/datum/looping_sound/proc/get_sound(starttime, _mid_sounds)
|
||||
. = _mid_sounds || mid_sounds
|
||||
@@ -96,11 +98,22 @@
|
||||
|
||||
/datum/looping_sound/proc/on_start()
|
||||
var/start_wait = 0
|
||||
if(start_sound)
|
||||
if(start_sound && !skip_starting_sounds)
|
||||
play(start_sound, start_volume)
|
||||
start_wait = start_length
|
||||
addtimer(CALLBACK(src, .proc/sound_loop), start_wait, TIMER_CLIENT_TIME)
|
||||
timerid = addtimer(CALLBACK(src, .proc/start_sound_loop), start_wait, TIMER_CLIENT_TIME | TIMER_DELETE_ME | TIMER_STOPPABLE, SSsound_loops)
|
||||
|
||||
/datum/looping_sound/proc/on_stop()
|
||||
if(end_sound)
|
||||
if(end_sound && loop_started)
|
||||
play(end_sound, end_volume)
|
||||
|
||||
/datum/looping_sound/proc/set_parent(new_parent)
|
||||
if(parent)
|
||||
UnregisterSignal(parent, COMSIG_PARENT_QDELETING)
|
||||
parent = new_parent
|
||||
if(parent)
|
||||
RegisterSignal(parent, COMSIG_PARENT_QDELETING, .proc/handle_parent_del)
|
||||
|
||||
/datum/looping_sound/proc/handle_parent_del(datum/source)
|
||||
SIGNAL_HANDLER
|
||||
set_parent(null)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#define RAD_GEIGER_LOW 100 // Geiger counter sound thresholds
|
||||
#define RAD_GEIGER_LOW 100 // Geiger counter sound thresholds
|
||||
#define RAD_GEIGER_MEDIUM 500
|
||||
#define RAD_GEIGER_HIGH 1000
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/random = 40, /obj/structure/spawner/lavaland = 2, \
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelord/legion/random = 30, /obj/structure/spawner/lavaland/legion = 3, \
|
||||
SPAWN_MEGAFAUNA = 4, /mob/living/simple_animal/hostile/asteroid/goldgrub = 10)
|
||||
flora_spawn_list = list(/obj/structure/flora/ash/leaf_shroom = 2 , /obj/structure/flora/ash/cap_shroom = 2 , /obj/structure/flora/ash/stem_shroom = 2 , /obj/structure/flora/ash/cacti = 1, /obj/structure/flora/ash/tall_shroom = 2)
|
||||
flora_spawn_list = list(/obj/structure/flora/ash/leaf_shroom = 2 , /obj/structure/flora/ash/cap_shroom = 2 , /obj/structure/flora/ash/stem_shroom = 2 , /obj/structure/flora/ash/cacti = 1, /obj/structure/flora/ash/tall_shroom = 2, /obj/structure/flora/ashtree = 1)
|
||||
feature_spawn_list = list(/obj/structure/geyser/random = 1)
|
||||
|
||||
initial_closed_chance = 45
|
||||
|
||||
@@ -120,7 +120,7 @@ Simple datum which is instanced once per type and is used for every object of sa
|
||||
return
|
||||
I.hitsound = item_sound_override
|
||||
I.usesound = item_sound_override
|
||||
I.throwhitsound = item_sound_override
|
||||
I.mob_throw_hit_sound = item_sound_override
|
||||
// I.mob_throw_hit_sound = item_sound_override
|
||||
// I.equip_sound = item_sound_override
|
||||
// I.pickup_sound = item_sound_override
|
||||
|
||||
+40
-37
@@ -229,7 +229,6 @@
|
||||
/datum/mind/proc/has_antag_datum(datum_type, check_subtypes = TRUE)
|
||||
if(!datum_type)
|
||||
return
|
||||
. = FALSE
|
||||
for(var/a in antag_datums)
|
||||
var/datum/antagonist/A = a
|
||||
if(check_subtypes && istype(A, datum_type))
|
||||
@@ -296,11 +295,17 @@
|
||||
remove_rev()
|
||||
SSticker.mode.update_cult_icons_removed(src)
|
||||
|
||||
/datum/mind/proc/equip_traitor(datum/traitor_class/traitor_class, silent = FALSE, datum/antagonist/uplink_owner)
|
||||
/**
|
||||
* ## give_uplink
|
||||
*
|
||||
* A mind proc for giving anyone an uplink.
|
||||
* arguments:
|
||||
* * silent: if this should send a message to the mind getting the uplink. traitors do not use this silence, but the silence var on their antag datum.
|
||||
* * antag_datum: the antag datum of the uplink owner, for storing it in antag memory. optional!
|
||||
*/
|
||||
/datum/mind/proc/equip_traitor(silent = FALSE, datum/antagonist/antag_datum)
|
||||
if(!current)
|
||||
return
|
||||
if(!traitor_class)
|
||||
traitor_class = GLOB.traitor_classes[TRAITOR_HUMAN]
|
||||
var/mob/living/carbon/human/traitor_mob = current
|
||||
if (!istype(traitor_mob))
|
||||
return
|
||||
@@ -314,16 +319,9 @@
|
||||
P = locate() in PDA
|
||||
if (!P) // If we couldn't find a pen in the PDA, or we didn't even have a PDA, do it the old way
|
||||
P = locate() in all_contents
|
||||
if(!P) // I do not have a pen.
|
||||
var/obj/item/pen/inowhaveapen
|
||||
if(istype(traitor_mob.back,/obj/item/storage)) //ok buddy you better have a backpack!
|
||||
inowhaveapen = new /obj/item/pen(traitor_mob.back)
|
||||
else
|
||||
inowhaveapen = new /obj/item/pen(traitor_mob.loc)
|
||||
traitor_mob.put_in_hands(inowhaveapen) // I hope you don't have arms and your traitor pen gets stolen for all this trouble you've caused.
|
||||
P = inowhaveapen
|
||||
|
||||
var/obj/item/uplink_loc
|
||||
var/implant = FALSE
|
||||
|
||||
if(traitor_mob.client && traitor_mob.client.prefs)
|
||||
switch(traitor_mob.client.prefs.uplink_spawn_loc)
|
||||
@@ -341,33 +339,38 @@
|
||||
uplink_loc = P
|
||||
if(UPLINK_PEN)
|
||||
uplink_loc = P
|
||||
if(!uplink_loc)
|
||||
uplink_loc = PDA
|
||||
if(!uplink_loc)
|
||||
uplink_loc = R
|
||||
if(UPLINK_IMPLANT)
|
||||
implant = TRUE
|
||||
|
||||
if (!uplink_loc)
|
||||
if(!silent)
|
||||
to_chat(traitor_mob, "Unfortunately, [traitor_class.employer] wasn't able to get you an Uplink.")
|
||||
. = 0
|
||||
else
|
||||
. = uplink_loc
|
||||
var/datum/component/uplink/U = uplink_loc.AddComponent(/datum/component/uplink, traitor_mob.key,traitor_class)
|
||||
if(!U)
|
||||
CRASH("Uplink creation failed.")
|
||||
U.setup_unlock_code()
|
||||
if(!silent)
|
||||
if(uplink_loc == R)
|
||||
to_chat(traitor_mob, "[traitor_class.employer] has cunningly disguised a Syndicate Uplink as your [R.name]. Simply dial the frequency [format_frequency(U.unlock_code)] to unlock its hidden features.")
|
||||
else if(uplink_loc == PDA)
|
||||
to_chat(traitor_mob, "[traitor_class.employer] has cunningly disguised a Syndicate Uplink as your [PDA.name]. Simply enter the code \"[U.unlock_code]\" into the ringtone select to unlock its hidden features.")
|
||||
else if(uplink_loc == P)
|
||||
to_chat(traitor_mob, "[traitor_class.employer] has cunningly disguised a Syndicate Uplink as your [P.name]. Simply twist the top of the pen [U.unlock_code] from its starting position to unlock its hidden features.")
|
||||
if(!uplink_loc) // We've looked everywhere, let's just implant you
|
||||
implant = TRUE
|
||||
|
||||
if(uplink_owner)
|
||||
uplink_owner.antag_memory += U.unlock_note + "<br>"
|
||||
else
|
||||
traitor_mob.mind.store_memory(U.unlock_note)
|
||||
if(implant)
|
||||
var/obj/item/implant/uplink/starting/new_implant = new(traitor_mob)
|
||||
new_implant.implant(traitor_mob, null, silent = TRUE)
|
||||
if(!silent)
|
||||
to_chat(traitor_mob, span_boldnotice("Your Syndicate Uplink has been cunningly implanted in you, for a small TC fee. Simply trigger the uplink to access it."))
|
||||
return new_implant
|
||||
|
||||
. = uplink_loc
|
||||
var/unlock_text
|
||||
var/datum/component/uplink/new_uplink = uplink_loc.AddComponent(/datum/component/uplink, traitor_mob.key)
|
||||
if(!new_uplink)
|
||||
CRASH("Uplink creation failed.")
|
||||
new_uplink.setup_unlock_code()
|
||||
if(uplink_loc == R)
|
||||
unlock_text = "Your Uplink is cunningly disguised as your [R.name]. Simply dial the frequency [format_frequency(new_uplink.unlock_code)] to unlock its hidden features."
|
||||
else if(uplink_loc == PDA)
|
||||
unlock_text = "Your Uplink is cunningly disguised as your [PDA.name]. Simply enter the code \"[new_uplink.unlock_code]\" into the ringtone select to unlock its hidden features."
|
||||
else if(uplink_loc == P)
|
||||
unlock_text = "Your Uplink is cunningly disguised as your [P.name]. Simply twist the top of the pen [english_list(new_uplink.unlock_code)] from its starting position to unlock its hidden features."
|
||||
new_uplink.unlock_text = unlock_text
|
||||
if(!silent)
|
||||
to_chat(traitor_mob, span_boldnotice(unlock_text))
|
||||
if(!antag_datum)
|
||||
traitor_mob.mind.store_memory(new_uplink.unlock_note)
|
||||
return
|
||||
antag_datum.antag_memory += new_uplink.unlock_note + "<br>"
|
||||
|
||||
//Link a new mobs mind to the creator of said mob. They will join any team they are currently on, and will only switch teams when their creator does.
|
||||
|
||||
|
||||
@@ -294,3 +294,39 @@
|
||||
description = "<span class='boldwarning'>I hate when my shoes come untied!</span>\n"
|
||||
mood_change = -3
|
||||
timeout = 1 MINUTES
|
||||
|
||||
/datum/mood_event/high_five_alone
|
||||
description = "<span class='boldwarning'>I tried getting a high-five with no one around, how embarassing!</span>\n"
|
||||
mood_change = -2
|
||||
timeout = 1 MINUTES
|
||||
|
||||
/datum/mood_event/high_five_full_hand
|
||||
description = "<span class='boldwarning'>Oh God, I don't even know how to high-five correctly...</span>\n"
|
||||
mood_change = -1
|
||||
timeout = 45 SECONDS
|
||||
|
||||
/datum/mood_event/left_hanging
|
||||
description = "<span class='boldwarning'>But everyone loves high fives! Maybe people just... hate me?</span>\n"
|
||||
mood_change = -2
|
||||
timeout = 1.5 MINUTES
|
||||
|
||||
/datum/mood_event/too_slow
|
||||
description = "<span class='boldwarning'>NO! HOW COULD I BE.... TOO SLOW???</span>\n"
|
||||
mood_change = -2 // multiplied by how many people saw it happen, up to 8, so potentially massive. the ULTIMATE prank carries a lot of weight
|
||||
timeout = 2 MINUTES
|
||||
|
||||
/datum/mood_event/too_slow/add_effects(param)
|
||||
var/people_laughing_at_you = 1 // start with 1 in case they're on the same tile or something
|
||||
for(var/mob/living/carbon/iter_carbon in oview(owner, 7))
|
||||
if(iter_carbon.stat == CONSCIOUS)
|
||||
people_laughing_at_you++
|
||||
if(people_laughing_at_you > 7)
|
||||
break
|
||||
|
||||
mood_change *= people_laughing_at_you
|
||||
return ..()
|
||||
|
||||
/datum/mood_event/sacrifice_bad
|
||||
description = "<span class='warning'>Those darn savages!</span>\n"
|
||||
mood_change = -5
|
||||
timeout = 2 MINUTES
|
||||
|
||||
@@ -205,3 +205,23 @@
|
||||
/datum/mood_event/cleared_stomach
|
||||
description = "<span class='nicegreen'>Feels nice to get that out of the way!</span>\n"
|
||||
mood_change = 3
|
||||
|
||||
/datum/mood_event/high_five
|
||||
description = "<span class='nicegreen'>I love getting high fives!</span>\n"
|
||||
mood_change = 2
|
||||
timeout = 45 SECONDS
|
||||
|
||||
/datum/mood_event/high_ten
|
||||
description = "<span class='nicegreen'>AMAZING! A HIGH-TEN!</span>\n"
|
||||
mood_change = 3
|
||||
timeout = 45 SECONDS
|
||||
|
||||
/datum/mood_event/down_low
|
||||
description = "<span class='nicegreen'>HA! What a rube, they never stood a chance...</span>\n"
|
||||
mood_change = 4
|
||||
timeout = 1.5 MINUTES
|
||||
|
||||
/datum/mood_event/sacrifice_good
|
||||
description = "<span class='nicegreen'>The gods are pleased with this offering!</span>\n"
|
||||
mood_change = 5
|
||||
timeout = 3 MINUTES
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
return
|
||||
ADD_TRAIT(owner, TRAIT_RESISTCOLD, "cold_resistance")
|
||||
ADD_TRAIT(owner, TRAIT_RESISTLOWPRESSURE, "cold_resistance")
|
||||
ADD_TRAIT(owner, TRAIT_LOWPRESSURECOOLING, "cold_resistance")
|
||||
owner.add_filter("space_glow", 2, list("type" = "outline", "color" = "#ffe46bd8", "size" = 1))
|
||||
addtimer(CALLBACK(src, .proc/glow_loop, owner), rand(1,19))
|
||||
|
||||
@@ -27,5 +28,6 @@
|
||||
return
|
||||
REMOVE_TRAIT(owner, TRAIT_RESISTCOLD, "cold_resistance")
|
||||
REMOVE_TRAIT(owner, TRAIT_RESISTLOWPRESSURE, "cold_resistance")
|
||||
REMOVE_TRAIT(owner, TRAIT_LOWPRESSURECOOLING, "cold_resistance")
|
||||
owner.remove_filter("space_glow")
|
||||
|
||||
|
||||
@@ -771,7 +771,7 @@
|
||||
/datum/status_effect/necropolis_curse/proc/apply_curse(set_curse)
|
||||
curse_flags |= set_curse
|
||||
if(curse_flags & CURSE_BLINDING)
|
||||
owner.overlay_fullscreen("curse", /atom/movable/screen/fullscreen/curse, 1)
|
||||
owner.overlay_fullscreen("curse", /atom/movable/screen/fullscreen/scaled/curse, 1)
|
||||
|
||||
/datum/status_effect/necropolis_curse/proc/remove_curse(remove_curse)
|
||||
if(remove_curse & CURSE_BLINDING)
|
||||
@@ -929,11 +929,13 @@
|
||||
|
||||
/atom/movable/screen/alert/status_effect/strandling/Click(location, control, params)
|
||||
. = ..()
|
||||
to_chat(mob_viewer, "<span class='notice'>You attempt to remove the durathread strand from around your neck.</span>")
|
||||
if(do_after(mob_viewer, 35, null, mob_viewer))
|
||||
if(isliving(mob_viewer))
|
||||
var/mob/living/L = mob_viewer
|
||||
to_chat(mob_viewer, "<span class='notice'>You successfully remove the durathread strand.</span>")
|
||||
if(usr != owner)
|
||||
return
|
||||
to_chat(owner, "<span class='notice'>You attempt to remove the durathread strand from around your neck.</span>")
|
||||
if(do_after(owner, 35, null, owner))
|
||||
if(isliving(owner))
|
||||
var/mob/living/L = owner
|
||||
to_chat(owner, "<span class='notice'>You successfully remove the durathread strand.</span>")
|
||||
L.remove_status_effect(STATUS_EFFECT_CHOKINGSTRAND)
|
||||
|
||||
|
||||
|
||||
@@ -83,3 +83,89 @@
|
||||
/datum/status_effect/throat_soothed/on_remove()
|
||||
REMOVE_TRAIT(owner, TRAIT_SOOTHED_THROAT, "[STATUS_EFFECT_TRAIT]_[id]")
|
||||
return ..()
|
||||
|
||||
// this status effect is used to negotiate the high-fiving capabilities of all concerned parties
|
||||
/datum/status_effect/offering
|
||||
id = "offering"
|
||||
duration = -1
|
||||
tick_interval = -1
|
||||
status_type = STATUS_EFFECT_UNIQUE
|
||||
alert_type = null
|
||||
/// The people who were offered this item at the start
|
||||
var/list/possible_takers
|
||||
/// The actual item being offered
|
||||
var/obj/item/offered_item
|
||||
/// The type of alert given to people when offered, in case you need to override some behavior (like for high-fives)
|
||||
var/give_alert_type = /atom/movable/screen/alert/give
|
||||
|
||||
/datum/status_effect/offering/on_creation(mob/living/new_owner, obj/item/offer, give_alert_override)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
|
||||
offered_item = offer
|
||||
if(give_alert_override)
|
||||
give_alert_type = give_alert_override
|
||||
|
||||
for(var/mob/living/carbon/possible_taker in orange(1, owner))
|
||||
if(!owner.CanReach(possible_taker) || IS_DEAD_OR_INCAP(possible_taker) || !possible_taker.can_hold_items())
|
||||
continue
|
||||
register_candidate(possible_taker)
|
||||
|
||||
if(!possible_takers) // no one around
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
RegisterSignal(owner, COMSIG_MOVABLE_MOVED, .proc/check_owner_in_range)
|
||||
RegisterSignal(offered_item, list(COMSIG_PARENT_QDELETING, COMSIG_ITEM_DROPPED), .proc/dropped_item)
|
||||
//RegisterSignal(owner, COMSIG_PARENT_EXAMINE_MORE, .proc/check_fake_out)
|
||||
|
||||
/datum/status_effect/offering/Destroy()
|
||||
for(var/i in possible_takers)
|
||||
var/mob/living/carbon/removed_taker = i
|
||||
remove_candidate(removed_taker)
|
||||
LAZYCLEARLIST(possible_takers)
|
||||
return ..()
|
||||
|
||||
/// Hook up the specified carbon mob to be offered the item in question, give them the alert and signals and all
|
||||
/datum/status_effect/offering/proc/register_candidate(mob/living/carbon/possible_candidate)
|
||||
var/atom/movable/screen/alert/give/G = possible_candidate.throw_alert("[owner]", give_alert_type)
|
||||
if(!G)
|
||||
return
|
||||
LAZYADD(possible_takers, possible_candidate)
|
||||
RegisterSignal(possible_candidate, COMSIG_MOVABLE_MOVED, .proc/check_taker_in_range)
|
||||
G.setup(possible_candidate, owner, offered_item)
|
||||
|
||||
/// Remove the alert and signals for the specified carbon mob. Automatically removes the status effect when we lost the last taker
|
||||
/datum/status_effect/offering/proc/remove_candidate(mob/living/carbon/removed_candidate)
|
||||
removed_candidate.clear_alert("[owner]")
|
||||
LAZYREMOVE(possible_takers, removed_candidate)
|
||||
UnregisterSignal(removed_candidate, COMSIG_MOVABLE_MOVED)
|
||||
if(!possible_takers && !QDELING(src))
|
||||
qdel(src)
|
||||
|
||||
/// One of our possible takers moved, see if they left us hanging
|
||||
/datum/status_effect/offering/proc/check_taker_in_range(mob/living/carbon/taker)
|
||||
SIGNAL_HANDLER
|
||||
if(owner.CanReach(taker) && !IS_DEAD_OR_INCAP(taker))
|
||||
return
|
||||
|
||||
remove_candidate(taker)
|
||||
|
||||
/// The offerer moved, see if anyone is out of range now
|
||||
/datum/status_effect/offering/proc/check_owner_in_range(mob/living/carbon/source)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
for(var/i in possible_takers)
|
||||
var/mob/living/carbon/checking_taker = i
|
||||
if(!istype(checking_taker) || !owner.CanReach(checking_taker) || IS_DEAD_OR_INCAP(checking_taker))
|
||||
remove_candidate(checking_taker)
|
||||
|
||||
/// We lost the item, give it up
|
||||
/datum/status_effect/offering/proc/dropped_item(obj/item/source)
|
||||
SIGNAL_HANDLER
|
||||
qdel(src)
|
||||
|
||||
/datum/status_effect/offering/secret_handshake
|
||||
id = "secret_handshake"
|
||||
give_alert_type = /atom/movable/screen/alert/give/secret_handshake
|
||||
|
||||
@@ -25,18 +25,19 @@
|
||||
/datum/status_effect/proc/on_creation(mob/living/new_owner, ...)
|
||||
if(new_owner)
|
||||
owner = new_owner
|
||||
if(owner)
|
||||
LAZYADD(owner.status_effects, src)
|
||||
if(!owner || !on_apply())
|
||||
if(QDELETED(owner) || !on_apply())
|
||||
qdel(src)
|
||||
return
|
||||
if(owner)
|
||||
LAZYADD(owner.status_effects, src)
|
||||
if(duration != -1)
|
||||
duration = world.time + duration
|
||||
next_tick = world.time + tick_interval
|
||||
if(alert_type)
|
||||
var/atom/movable/screen/alert/status_effect/A = owner.throw_alert(id, alert_type)
|
||||
A.attached_effect = src //so the alert can reference us, if it needs to
|
||||
linked_alert = A //so we can reference the alert, if we need to
|
||||
if(istype(A))
|
||||
A?.attached_effect = src //so the alert can reference us, if it needs to
|
||||
linked_alert = A //so we can reference the alert, if we need to
|
||||
START_PROCESSING(SSstatus_effects, src)
|
||||
return TRUE
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//A reference to this list is passed into area sound managers, and it's modified in a manner that preserves that reference in ash_storm.dm
|
||||
GLOBAL_LIST_EMPTY(ash_storm_sounds)
|
||||
//Ash storms happen frequently on lavaland. They heavily obscure vision, and cause high fire damage to anyone caught outside.
|
||||
/datum/weather/ash_storm
|
||||
name = "ash storm"
|
||||
@@ -25,55 +27,41 @@
|
||||
probability = 90
|
||||
|
||||
barometer_predictable = TRUE
|
||||
|
||||
var/datum/looping_sound/active_outside_ashstorm/sound_ao = new(list(), FALSE, TRUE)
|
||||
var/datum/looping_sound/active_inside_ashstorm/sound_ai = new(list(), FALSE, TRUE)
|
||||
var/datum/looping_sound/weak_outside_ashstorm/sound_wo = new(list(), FALSE, TRUE)
|
||||
var/datum/looping_sound/weak_inside_ashstorm/sound_wi = new(list(), FALSE, TRUE)
|
||||
var/list/weak_sounds = list()
|
||||
var/list/strong_sounds = list()
|
||||
|
||||
/datum/weather/ash_storm/telegraph()
|
||||
. = ..()
|
||||
var/list/inside_areas = list()
|
||||
var/list/outside_areas = list()
|
||||
var/list/eligible_areas = list()
|
||||
for (var/z in impacted_z_levels)
|
||||
eligible_areas += SSmapping.areas_in_z["[z]"]
|
||||
for(var/i in 1 to eligible_areas.len)
|
||||
var/area/place = eligible_areas[i]
|
||||
if(place.outdoors)
|
||||
outside_areas += place
|
||||
weak_sounds[place] = /datum/looping_sound/weak_outside_ashstorm
|
||||
strong_sounds[place] = /datum/looping_sound/active_outside_ashstorm
|
||||
else
|
||||
inside_areas += place
|
||||
weak_sounds[place] = /datum/looping_sound/weak_inside_ashstorm
|
||||
strong_sounds[place] = /datum/looping_sound/active_inside_ashstorm
|
||||
CHECK_TICK
|
||||
|
||||
sound_ao.output_atoms = outside_areas
|
||||
sound_ai.output_atoms = inside_areas
|
||||
sound_wo.output_atoms = outside_areas
|
||||
sound_wi.output_atoms = inside_areas
|
||||
|
||||
sound_wo.start()
|
||||
sound_wi.start()
|
||||
//We modify this list instead of setting it to weak/stron sounds in order to preserve things that hold a reference to it
|
||||
//It's essentially a playlist for a bunch of components that chose what sound to loop based on the area a player is in
|
||||
GLOB.ash_storm_sounds += weak_sounds
|
||||
return ..()
|
||||
|
||||
/datum/weather/ash_storm/start()
|
||||
. = ..()
|
||||
sound_wo.stop()
|
||||
sound_wi.stop()
|
||||
|
||||
sound_ao.start()
|
||||
sound_ai.start()
|
||||
GLOB.ash_storm_sounds -= weak_sounds
|
||||
GLOB.ash_storm_sounds += strong_sounds
|
||||
return ..()
|
||||
|
||||
/datum/weather/ash_storm/wind_down()
|
||||
. = ..()
|
||||
sound_ao.stop()
|
||||
sound_ai.stop()
|
||||
|
||||
sound_wo.start()
|
||||
sound_wi.start()
|
||||
GLOB.ash_storm_sounds -= strong_sounds
|
||||
GLOB.ash_storm_sounds += weak_sounds
|
||||
return ..()
|
||||
|
||||
/datum/weather/ash_storm/end()
|
||||
. = ..()
|
||||
sound_wo.stop()
|
||||
sound_wi.stop()
|
||||
GLOB.ash_storm_sounds -= weak_sounds
|
||||
return ..()
|
||||
|
||||
/datum/weather/ash_storm/proc/is_ash_immune(atom/L)
|
||||
while (L && !isturf(L))
|
||||
@@ -88,6 +76,11 @@
|
||||
var/mob/living/the_mob = L
|
||||
if("ash" in the_mob.weather_immunities)
|
||||
return TRUE
|
||||
// if(istype(L, /obj/structure/closet))
|
||||
// var/obj/structure/closet/the_locker = L
|
||||
// if(the_locker.weather_protection)
|
||||
// if("ash" in the_locker.weather_protection)
|
||||
// return TRUE
|
||||
L = L.loc //Check parent items immunities (recurses up to the turf)
|
||||
return FALSE //RIP you
|
||||
|
||||
|
||||
@@ -119,12 +119,20 @@
|
||||
victim.bleed(blood_bled, TRUE)
|
||||
if(14 to 19)
|
||||
victim.visible_message("<span class='smalldanger'>[victim] spits out a string of blood from the blow to [victim.p_their()] chest!</span>", "<span class='danger'>You spit out a string of blood from the blow to your chest!</span>", vision_distance=COMBAT_MESSAGE_RANGE)
|
||||
new /obj/effect/temp_visual/dir_setting/bloodsplatter(victim.loc, victim.dir)
|
||||
if(ishuman(victim))
|
||||
var/mob/living/carbon/human/H = victim
|
||||
new /obj/effect/temp_visual/dir_setting/bloodsplatter(victim.loc, victim.dir, H.dna.species.exotic_blood_color)
|
||||
else
|
||||
new /obj/effect/temp_visual/dir_setting/bloodsplatter(victim.loc, victim.dir)
|
||||
victim.bleed(blood_bled)
|
||||
if(20 to INFINITY)
|
||||
victim.visible_message("<span class='danger'>[victim] chokes up a spray of blood from the blow to [victim.p_their()] chest!</span>", "<span class='danger'><b>You choke up on a spray of blood from the blow to your chest!</b></span>", vision_distance=COMBAT_MESSAGE_RANGE)
|
||||
victim.bleed(blood_bled)
|
||||
new /obj/effect/temp_visual/dir_setting/bloodsplatter(victim.loc, victim.dir)
|
||||
if(ishuman(victim))
|
||||
var/mob/living/carbon/human/H = victim
|
||||
new /obj/effect/temp_visual/dir_setting/bloodsplatter(victim.loc, victim.dir, H.dna.species.exotic_blood_color)
|
||||
else
|
||||
new /obj/effect/temp_visual/dir_setting/bloodsplatter(victim.loc, victim.dir)
|
||||
victim.add_splatter_floor(get_step(victim.loc, victim.dir))
|
||||
|
||||
|
||||
|
||||
@@ -39,12 +39,20 @@
|
||||
victim.bleed(blood_bled, TRUE)
|
||||
if(14 to 19)
|
||||
victim.visible_message("<span class='smalldanger'>A small stream of blood spurts from the hole in [victim]'s [limb.name]!</span>", "<span class='danger'>You spit out a string of blood from the blow to your [limb.name]!</span>", vision_distance=COMBAT_MESSAGE_RANGE)
|
||||
new /obj/effect/temp_visual/dir_setting/bloodsplatter(victim.loc, victim.dir)
|
||||
if(ishuman(victim))
|
||||
var/mob/living/carbon/human/H = victim
|
||||
new /obj/effect/temp_visual/dir_setting/bloodsplatter(victim.loc, victim.dir, H.dna.species.exotic_blood_color)
|
||||
else
|
||||
new /obj/effect/temp_visual/dir_setting/bloodsplatter(victim.loc, victim.dir)
|
||||
victim.bleed(blood_bled)
|
||||
if(20 to INFINITY)
|
||||
victim.visible_message("<span class='danger'>A spray of blood streams from the gash in [victim]'s [limb.name]!</span>", "<span class='danger'><b>You choke up on a spray of blood from the blow to your [limb.name]!</b></span>", vision_distance=COMBAT_MESSAGE_RANGE)
|
||||
victim.bleed(blood_bled)
|
||||
new /obj/effect/temp_visual/dir_setting/bloodsplatter(victim.loc, victim.dir)
|
||||
if(ishuman(victim))
|
||||
var/mob/living/carbon/human/H = victim
|
||||
new /obj/effect/temp_visual/dir_setting/bloodsplatter(victim.loc, victim.dir, H.dna.species.exotic_blood_color)
|
||||
else
|
||||
new /obj/effect/temp_visual/dir_setting/bloodsplatter(victim.loc, victim.dir)
|
||||
victim.add_splatter_floor(get_step(victim.loc, victim.dir))
|
||||
|
||||
/datum/wound/pierce/handle_process()
|
||||
|
||||
Reference in New Issue
Block a user