Revert "PLASE"

This reverts commit 8af225e6e3.
This commit is contained in:
Fermi
2019-11-24 02:44:00 +00:00
parent 8af225e6e3
commit 00601a0c87
2145 changed files with 1387321 additions and 5257 deletions
+313
View File
@@ -0,0 +1,313 @@
/datum/component
var/dupe_mode = COMPONENT_DUPE_HIGHLANDER
var/dupe_type
var/datum/parent
//only set to true if you are able to properly transfer this component
//At a minimum RegisterWithParent and UnregisterFromParent should be used
//Make sure you also implement PostTransfer for any post transfer handling
var/can_transfer = FALSE
/datum/component/New(datum/P, ...)
parent = P
var/list/arguments = args.Copy(2)
if(Initialize(arglist(arguments)) == COMPONENT_INCOMPATIBLE)
qdel(src, TRUE, TRUE)
CRASH("Incompatible [type] assigned to a [P.type]! args: [json_encode(arguments)]")
_JoinParent(P)
/datum/component/proc/_JoinParent()
var/datum/P = parent
//lazy init the parent's dc list
var/list/dc = P.datum_components
if(!dc)
P.datum_components = dc = list()
//set up the typecache
var/our_type = type
for(var/I in _GetInverseTypeList(our_type))
var/test = dc[I]
if(test) //already another component of this type here
var/list/components_of_type
if(!length(test))
components_of_type = list(test)
dc[I] = components_of_type
else
components_of_type = test
if(I == our_type) //exact match, take priority
var/inserted = FALSE
for(var/J in 1 to components_of_type.len)
var/datum/component/C = components_of_type[J]
if(C.type != our_type) //but not over other exact matches
components_of_type.Insert(J, I)
inserted = TRUE
break
if(!inserted)
components_of_type += src
else //indirect match, back of the line with ya
components_of_type += src
else //only component of this type, no list
dc[I] = src
RegisterWithParent()
// If you want/expect to be moving the component around between parents, use this to register on the parent for signals
/datum/component/proc/RegisterWithParent()
return
/datum/component/proc/Initialize(...)
return
/datum/component/Destroy(force=FALSE, silent=FALSE)
if(!force && parent)
_RemoveFromParent()
if(!silent)
SEND_SIGNAL(parent, COMSIG_COMPONENT_REMOVING, src)
parent = null
return ..()
/datum/component/proc/_RemoveFromParent()
var/datum/P = parent
var/list/dc = P.datum_components
for(var/I in _GetInverseTypeList())
var/list/components_of_type = dc[I]
if(length(components_of_type)) //
var/list/subtracted = components_of_type - src
if(subtracted.len == 1) //only 1 guy left
dc[I] = subtracted[1] //make him special
else
dc[I] = subtracted
else //just us
dc -= I
if(!dc.len)
P.datum_components = null
UnregisterFromParent()
/datum/component/proc/UnregisterFromParent()
return
/datum/proc/RegisterSignal(datum/target, sig_type_or_types, proctype, override = FALSE)
if(QDELETED(src) || QDELETED(target))
return
var/list/procs = signal_procs
if(!procs)
signal_procs = procs = list()
if(!procs[target])
procs[target] = list()
var/list/lookup = target.comp_lookup
if(!lookup)
target.comp_lookup = lookup = list()
var/list/sig_types = islist(sig_type_or_types) ? sig_type_or_types : list(sig_type_or_types)
for(var/sig_type in sig_types)
if(!override && procs[target][sig_type])
stack_trace("[sig_type] overridden. Use override = TRUE to suppress this warning")
procs[target][sig_type] = proctype
if(!lookup[sig_type]) // Nothing has registered here yet
lookup[sig_type] = src
else if(lookup[sig_type] == src) // We already registered here
continue
else if(!length(lookup[sig_type])) // One other thing registered here
lookup[sig_type] = list(lookup[sig_type]=TRUE)
lookup[sig_type][src] = TRUE
else // Many other things have registered here
lookup[sig_type][src] = TRUE
signal_enabled = TRUE
/datum/proc/UnregisterSignal(datum/target, sig_type_or_types)
var/list/lookup = target.comp_lookup
if(!signal_procs || !signal_procs[target] || !lookup)
return
if(!islist(sig_type_or_types))
sig_type_or_types = list(sig_type_or_types)
for(var/sig in sig_type_or_types)
switch(length(lookup[sig]))
if(2)
lookup[sig] = (lookup[sig]-src)[1]
if(1)
stack_trace("[target] ([target.type]) somehow has single length list inside comp_lookup")
if(src in lookup[sig])
lookup -= sig
if(!length(lookup))
target.comp_lookup = null
break
if(0)
lookup -= sig
if(!length(lookup))
target.comp_lookup = null
break
else
lookup[sig] -= src
signal_procs[target] -= sig_type_or_types
if(!signal_procs[target].len)
signal_procs -= target
/datum/component/proc/InheritComponent(datum/component/C, i_am_original)
return
/datum/component/proc/PreTransfer()
return
/datum/component/proc/PostTransfer()
return COMPONENT_INCOMPATIBLE //Do not support transfer by default as you must properly support it
/datum/component/proc/_GetInverseTypeList(our_type = type)
//we can do this one simple trick
var/current_type = parent_type
. = list(our_type, current_type)
//and since most components are root level + 1, this won't even have to run
while (current_type != /datum/component)
current_type = type2parent(current_type)
. += current_type
/datum/proc/_SendSignal(sigtype, list/arguments)
var/target = comp_lookup[sigtype]
if(!length(target))
var/datum/C = target
if(!C.signal_enabled)
return NONE
var/proctype = C.signal_procs[src][sigtype]
return NONE | CallAsync(C, proctype, arguments)
. = NONE
for(var/I in target)
var/datum/C = I
if(!C.signal_enabled)
continue
var/proctype = C.signal_procs[src][sigtype]
. |= CallAsync(C, proctype, arguments)
// The type arg is casted so initial works, you shouldn't be passing a real instance into this
/datum/proc/GetComponent(datum/component/c_type)
if(initial(c_type.dupe_mode) == COMPONENT_DUPE_ALLOWED)
stack_trace("GetComponent was called to get a component of which multiple copies could be on an object. This can easily break and should be changed. Type: \[[c_type]\]")
var/list/dc = datum_components
if(!dc)
return null
. = dc[c_type]
if(length(.))
return .[1]
/datum/proc/GetExactComponent(c_type)
var/list/dc = datum_components
if(!dc)
return null
var/datum/component/C = dc[c_type]
if(C)
if(length(C))
C = C[1]
if(C.type == c_type)
return C
return null
/datum/proc/GetComponents(c_type)
var/list/dc = datum_components
if(!dc)
return null
. = dc[c_type]
if(!length(.))
return list(.)
/datum/proc/AddComponent(new_type, ...)
var/datum/component/nt = new_type
var/dm = initial(nt.dupe_mode)
var/dt = initial(nt.dupe_type)
var/datum/component/old_comp
var/datum/component/new_comp
if(ispath(nt))
if(nt == /datum/component)
CRASH("[nt] attempted instantiation!")
else
new_comp = nt
nt = new_comp.type
args[1] = src
if(dm != COMPONENT_DUPE_ALLOWED)
if(!dt)
old_comp = GetExactComponent(nt)
else
old_comp = GetComponent(dt)
if(old_comp)
switch(dm)
if(COMPONENT_DUPE_UNIQUE)
if(!new_comp)
new_comp = new nt(arglist(args))
if(!QDELETED(new_comp))
old_comp.InheritComponent(new_comp, TRUE)
QDEL_NULL(new_comp)
if(COMPONENT_DUPE_HIGHLANDER)
if(!new_comp)
new_comp = new nt(arglist(args))
if(!QDELETED(new_comp))
new_comp.InheritComponent(old_comp, FALSE)
QDEL_NULL(old_comp)
if(COMPONENT_DUPE_UNIQUE_PASSARGS)
if(!new_comp)
var/list/arguments = args.Copy(2)
old_comp.InheritComponent(null, TRUE, arguments)
else
old_comp.InheritComponent(new_comp, TRUE)
else if(!new_comp)
new_comp = new nt(arglist(args)) // There's a valid dupe mode but there's no old component, act like normal
else if(!new_comp)
new_comp = new nt(arglist(args)) // Dupes are allowed, act like normal
if(!old_comp && !QDELETED(new_comp)) // Nothing related to duplicate components happened and the new component is healthy
SEND_SIGNAL(src, COMSIG_COMPONENT_ADDED, new_comp)
return new_comp
return old_comp
/datum/proc/LoadComponent(component_type, ...)
. = GetComponent(component_type)
if(!.)
return AddComponent(arglist(args))
/datum/component/proc/RemoveComponent()
if(!parent)
return
var/datum/old_parent = parent
PreTransfer()
_RemoveFromParent()
parent = null
SEND_SIGNAL(old_parent, COMSIG_COMPONENT_REMOVING, src)
/datum/proc/TakeComponent(datum/component/target)
if(!target || target.parent == src)
return
if(target.parent)
target.RemoveComponent()
target.parent = src
var/result = target.PostTransfer()
switch(result)
if(COMPONENT_INCOMPATIBLE)
var/c_type = target.type
qdel(target)
CRASH("Incompatible [c_type] transfer attempt to a [type]!")
if(target == AddComponent(target))
target._JoinParent()
/datum/proc/TransferComponents(datum/target)
var/list/dc = datum_components
if(!dc)
return
var/comps = dc[/datum/component]
if(islist(comps))
for(var/datum/component/I in comps)
if(I.can_transfer)
target.TakeComponent(I)
else
var/datum/component/C = comps
if(C.can_transfer)
target.TakeComponent(comps)
/datum/component/ui_host()
return parent
+48
View File
@@ -0,0 +1,48 @@
/datum/component/anti_magic
var/magic = FALSE
var/holy = FALSE
var/psychic = FALSE
var/allowed_slots = ~ITEM_SLOT_BACKPACK
var/charges = INFINITY
var/blocks_self = TRUE
var/datum/callback/reaction
var/datum/callback/expire
/datum/component/anti_magic/Initialize(_magic = FALSE, _holy = FALSE, _psychic = FALSE, _allowed_slots, _charges, _blocks_self = TRUE, datum/callback/_reaction, datum/callback/_expire)
if(isitem(parent))
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, .proc/on_equip)
RegisterSignal(parent, COMSIG_ITEM_DROPPED, .proc/on_drop)
else if(ismob(parent))
RegisterSignal(parent, COMSIG_MOB_RECEIVE_MAGIC, .proc/protect)
else
return COMPONENT_INCOMPATIBLE
magic = _magic
holy = _holy
psychic = _psychic
if(_allowed_slots)
allowed_slots = _allowed_slots
if(!isnull(_charges))
charges = _charges
blocks_self = _blocks_self
reaction = _reaction
expire = _expire
/datum/component/anti_magic/proc/on_equip(datum/source, mob/equipper, slot)
if(!CHECK_BITFIELD(allowed_slots, slotdefine2slotbit(slot))) //Check that the slot is valid for antimagic
UnregisterSignal(equipper, COMSIG_MOB_RECEIVE_MAGIC)
return
RegisterSignal(equipper, COMSIG_MOB_RECEIVE_MAGIC, .proc/protect, TRUE)
/datum/component/anti_magic/proc/on_drop(datum/source, mob/user)
UnregisterSignal(user, COMSIG_MOB_RECEIVE_MAGIC)
/datum/component/anti_magic/proc/protect(datum/source, mob/user, _magic, _holy, _psychic, chargecost = 1, self, list/protection_sources)
if(((_magic && magic) || (_holy && holy) || (_psychic && psychic)) && (!self || blocks_self))
protection_sources += parent
reaction?.Invoke(user, chargecost)
charges -= chargecost
if(charges <= 0)
expire?.Invoke(user)
qdel(src)
return COMPONENT_BLOCK_MAGIC
+62
View File
@@ -0,0 +1,62 @@
/datum/component/caltrop
var/min_damage
var/max_damage
var/probability
var/flags
var/cooldown = 0
/datum/component/caltrop/Initialize(_min_damage = 0, _max_damage = 0, _probability = 100, _flags = NONE)
min_damage = _min_damage
max_damage = max(_min_damage, _max_damage)
probability = _probability
flags = _flags
RegisterSignal(parent, list(COMSIG_MOVABLE_CROSSED), .proc/Crossed)
/datum/component/caltrop/proc/Crossed(datum/source, atom/movable/AM)
var/atom/A = parent
if(!A.has_gravity())
return
if(!prob(probability))
return
if(ishuman(AM))
var/mob/living/carbon/human/H = AM
if(HAS_TRAIT(H, TRAIT_PIERCEIMMUNE))
return
if((flags & CALTROP_IGNORE_WALKERS) && H.m_intent == MOVE_INTENT_WALK)
return
var/picked_def_zone = pick(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
var/obj/item/bodypart/O = H.get_bodypart(picked_def_zone)
if(!istype(O))
return
if(O.status == BODYPART_ROBOTIC)
return
var/feetCover = (H.wear_suit && (H.wear_suit.body_parts_covered & FEET)) || (H.w_uniform && (H.w_uniform.body_parts_covered & FEET) || (H.shoes && (H.shoes.body_parts_covered & FEET)))
if(!(flags & CALTROP_BYPASS_SHOES) && feetCover)
return
if((H.movement_type & FLYING) || H.buckled)
return
var/damage = rand(min_damage, max_damage)
if(HAS_TRAIT(H, TRAIT_LIGHT_STEP))
damage *= 0.75
H.apply_damage(damage, BRUTE, picked_def_zone)
if(cooldown < world.time - 10) //cooldown to avoid message spam.
if(!H.incapacitated(ignore_restraints = TRUE))
H.visible_message("<span class='danger'>[H] steps on [A].</span>", \
"<span class='userdanger'>You step on [A]!</span>")
else
H.visible_message("<span class='danger'>[H] slides on [A]!</span>", \
"<span class='userdanger'>You slide on [A]!</span>")
cooldown = world.time
H.Knockdown(60)
+143
View File
@@ -0,0 +1,143 @@
// Used by /turf/open/chasm and subtypes to implement the "dropping" mechanic
/datum/component/chasm
var/turf/target_turf
var/fall_message = "GAH! Ah... where are you?"
var/oblivion_message = "You stumble and stare into the abyss before you. It stares back, and you fall into the enveloping dark."
var/static/list/falling_atoms = list() // Atoms currently falling into chasms
var/static/list/forbidden_types = typecacheof(list(
/obj/singularity,
/obj/docking_port,
/obj/structure/lattice,
/obj/structure/stone_tile,
/obj/item/projectile,
/obj/effect/projectile,
/obj/effect/portal,
/obj/effect/abstract,
/obj/effect/hotspot,
/obj/effect/landmark,
/obj/effect/temp_visual,
/obj/effect/light_emitter/tendril,
/obj/effect/collapse,
/obj/effect/particle_effect/ion_trails,
/obj/effect/dummy/phased_mob
))
/datum/component/chasm/Initialize(turf/target)
RegisterSignal(parent, list(COMSIG_MOVABLE_CROSSED, COMSIG_ATOM_ENTERED), .proc/Entered)
target_turf = target
START_PROCESSING(SSobj, src) // process on create, in case stuff is still there
/datum/component/chasm/proc/Entered(datum/source, atom/movable/AM)
START_PROCESSING(SSobj, src)
drop_stuff(AM)
/datum/component/chasm/process()
if (!drop_stuff())
STOP_PROCESSING(SSobj, src)
/datum/component/chasm/proc/is_safe()
//if anything matching this typecache is found in the chasm, we don't drop things
var/static/list/chasm_safeties_typecache = typecacheof(list(/obj/structure/lattice/catwalk, /obj/structure/stone_tile))
var/atom/parent = src.parent
var/list/found_safeties = typecache_filter_list(parent.contents, chasm_safeties_typecache)
for(var/obj/structure/stone_tile/S in found_safeties)
if(S.fallen)
LAZYREMOVE(found_safeties, S)
return LAZYLEN(found_safeties)
/datum/component/chasm/proc/drop_stuff(AM)
. = 0
if (is_safe())
return FALSE
var/atom/parent = src.parent
var/to_check = AM ? list(AM) : parent.contents
for (var/thing in to_check)
if (droppable(thing))
. = 1
INVOKE_ASYNC(src, .proc/drop, thing)
/datum/component/chasm/proc/droppable(atom/movable/AM)
// avoid an infinite loop, but allow falling a large distance
if(falling_atoms[AM] && falling_atoms[AM] > 30)
return FALSE
if(!isliving(AM) && !isobj(AM))
return FALSE
if(is_type_in_typecache(AM, forbidden_types) || AM.throwing || (AM.movement_type & FLOATING))
return FALSE
//Flies right over the chasm
if(ismob(AM))
var/mob/M = AM
if(M.buckled) //middle statement to prevent infinite loops just in case!
var/mob/buckled_to = M.buckled
if((!ismob(M.buckled) || (buckled_to.buckled != M)) && !droppable(M.buckled))
return FALSE
if(M.is_flying())
return FALSE
if(ishuman(AM))
var/mob/living/carbon/human/H = AM
if(istype(H.belt, /obj/item/wormhole_jaunter))
var/obj/item/wormhole_jaunter/J = H.belt
//To freak out any bystanders
H.visible_message("<span class='boldwarning'>[H] falls into [parent]!</span>")
J.chasm_react(H)
return FALSE
return TRUE
/datum/component/chasm/proc/drop(atom/movable/AM)
//Make sure the item is still there after our sleep
if(!AM || QDELETED(AM))
return
falling_atoms[AM] = (falling_atoms[AM] || 0) + 1
var/turf/T = target_turf
if(T)
// send to the turf below
AM.visible_message("<span class='boldwarning'>[AM] falls into [parent]!</span>", "<span class='userdanger'>[fall_message]</span>")
T.visible_message("<span class='boldwarning'>[AM] falls from above!</span>")
AM.forceMove(T)
if(isliving(AM))
var/mob/living/L = AM
L.Knockdown(100)
L.adjustBruteLoss(30)
falling_atoms -= AM
else
// send to oblivion
AM.visible_message("<span class='boldwarning'>[AM] falls into [parent]!</span>", "<span class='userdanger'>[oblivion_message]</span>")
if (isliving(AM))
var/mob/living/L = AM
L.notransform = TRUE
L.Stun(200)
L.resting = TRUE
var/oldtransform = AM.transform
var/oldcolor = AM.color
var/oldalpha = AM.alpha
animate(AM, transform = matrix() - matrix(), alpha = 0, color = rgb(0, 0, 0), time = 10)
for(var/i in 1 to 5)
//Make sure the item is still there after our sleep
if(!AM || QDELETED(AM))
return
AM.pixel_y--
sleep(2)
//Make sure the item is still there after our sleep
if(!AM || QDELETED(AM))
return
if(iscyborg(AM))
var/mob/living/silicon/robot/S = AM
qdel(S.mmi)
falling_atoms -= AM
qdel(AM)
if(AM && !QDELETED(AM)) //It's indestructible
var/atom/parent = src.parent
parent.visible_message("<span class='boldwarning'>[parent] spits out [AM]!</span>")
AM.alpha = oldalpha
AM.color = oldcolor
AM.transform = oldtransform
AM.throw_at(get_edge_target_turf(parent,pick(GLOB.alldirs)),rand(1, 10),rand(1, 10))
+46
View File
@@ -0,0 +1,46 @@
/datum/component/cleaning
dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
/datum/component/cleaning/Initialize()
if(!ismovableatom(parent))
return COMPONENT_INCOMPATIBLE
RegisterSignal(parent, list(COMSIG_MOVABLE_MOVED), .proc/Clean)
/datum/component/cleaning/proc/Clean()
var/atom/movable/AM = parent
var/turf/T = AM.loc
SEND_SIGNAL(T, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
for(var/A in T)
if(is_cleanable(A))
qdel(A)
else if(isitem(A))
var/obj/item/cleaned_item = A
SEND_SIGNAL(cleaned_item, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
cleaned_item.clean_blood()
if(ismob(cleaned_item.loc))
var/mob/M = cleaned_item.loc
M.regenerate_icons()
else if(ishuman(A))
var/mob/living/carbon/human/cleaned_human = A
if(cleaned_human.lying)
if(cleaned_human.head)
SEND_SIGNAL(cleaned_human.head, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
cleaned_human.head.clean_blood()
cleaned_human.update_inv_head()
if(cleaned_human.wear_suit)
SEND_SIGNAL(cleaned_human.wear_suit, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
cleaned_human.wear_suit.clean_blood()
cleaned_human.update_inv_wear_suit()
else if(cleaned_human.w_uniform)
SEND_SIGNAL(cleaned_human.w_uniform, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
cleaned_human.w_uniform.clean_blood()
cleaned_human.update_inv_w_uniform()
if(cleaned_human.shoes)
SEND_SIGNAL(cleaned_human.shoes, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
cleaned_human.shoes.clean_blood()
cleaned_human.update_inv_shoes()
SEND_SIGNAL(cleaned_human, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_WEAK)
cleaned_human.clean_blood()
cleaned_human.wash_cream()
cleaned_human.regenerate_icons()
to_chat(cleaned_human, "<span class='danger'>[src] cleans your face!</span>")
+75
View File
@@ -0,0 +1,75 @@
/datum/component/decal
dupe_mode = COMPONENT_DUPE_ALLOWED
can_transfer = TRUE
var/cleanable
var/description
var/mutable_appearance/pic
var/first_dir // This only stores the dir arg from init
/datum/component/decal/Initialize(_icon, _icon_state, _dir, _cleanable=CLEAN_GOD, _color, _layer=TURF_LAYER, _description, _alpha=255)
if(!isatom(parent) || !generate_appearance(_icon, _icon_state, _dir, _layer, _color, _alpha))
return COMPONENT_INCOMPATIBLE
first_dir = _dir
description = _description
cleanable = _cleanable
apply()
/datum/component/decal/RegisterWithParent()
if(first_dir)
RegisterSignal(parent, COMSIG_ATOM_DIR_CHANGE, .proc/rotate_react)
if(cleanable)
RegisterSignal(parent, COMSIG_COMPONENT_CLEAN_ACT, .proc/clean_react)
if(description)
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, .proc/examine)
/datum/component/decal/UnregisterFromParent()
UnregisterSignal(parent, list(COMSIG_ATOM_DIR_CHANGE, COMSIG_COMPONENT_CLEAN_ACT, COMSIG_PARENT_EXAMINE))
/datum/component/decal/Destroy()
remove()
return ..()
/datum/component/decal/PreTransfer()
remove()
/datum/component/decal/PostTransfer()
remove()
apply()
/datum/component/decal/proc/generate_appearance(_icon, _icon_state, _dir, _layer, _color, _alpha)
if(!_icon || !_icon_state)
return FALSE
// It has to be made from an image or dir breaks because of a byond bug
var/temp_image = image(_icon, null, _icon_state, _layer, _dir)
pic = new(temp_image)
pic.color = _color
pic.alpha = _alpha
return TRUE
/datum/component/decal/proc/apply(atom/thing)
var/atom/master = thing || parent
master.add_overlay(pic, TRUE)
if(isitem(master))
addtimer(CALLBACK(master, /obj/item/.proc/update_slot_icon), 0, TIMER_UNIQUE)
/datum/component/decal/proc/remove(atom/thing)
var/atom/master = thing || parent
master.cut_overlay(pic, TRUE)
if(isitem(master))
addtimer(CALLBACK(master, /obj/item/.proc/update_slot_icon), 0, TIMER_UNIQUE)
/datum/component/decal/proc/rotate_react(datum/source, old_dir, new_dir)
if(old_dir == new_dir)
return
remove()
pic.dir = turn(pic.dir, dir2angle(old_dir) - dir2angle(new_dir))
apply()
/datum/component/decal/proc/clean_react(datum/source, strength)
if(strength >= cleanable)
qdel(src)
/datum/component/decal/proc/examine(datum/source, mob/user)
to_chat(user, description)
+13
View File
@@ -0,0 +1,13 @@
/datum/component/decal/blood
dupe_mode = COMPONENT_DUPE_UNIQUE
/datum/component/decal/blood/Initialize(_icon, _icon_state, _dir, _cleanable=CLEAN_STRENGTH_BLOOD, _color, _layer=ABOVE_OBJ_LAYER)
if(!isitem(parent))
return COMPONENT_INCOMPATIBLE
. = ..()
RegisterSignal(parent, COMSIG_ATOM_GET_EXAMINE_NAME, .proc/get_examine_name)
/datum/component/decal/blood/proc/get_examine_name(datum/source, mob/user, list/override)
var/atom/A = parent
return COMPONENT_EXNAME_CHANGED
+11
View File
@@ -0,0 +1,11 @@
/datum/component/wearertargeting/earprotection
signals = list(COMSIG_CARBON_SOUNDBANG)
mobtype = /mob/living/carbon
proctype = .proc/reducebang
/datum/component/wearertargeting/earprotection/Initialize(_valid_slots)
. = ..()
valid_slots = _valid_slots
/datum/component/wearertargeting/earprotection/proc/reducebang(datum/source, list/reflist)
reflist[1]--
+108
View File
@@ -0,0 +1,108 @@
/datum/component/footstep
var/steps = 0
var/volume
var/e_range
/datum/component/footstep/Initialize(volume_ = 0.5, e_range_ = -1)
if(!isliving(parent))
return COMPONENT_INCOMPATIBLE
volume = volume_
e_range = e_range_
RegisterSignal(parent, list(COMSIG_MOVABLE_MOVED), .proc/play_footstep)
/datum/component/footstep/proc/play_footstep()
var/turf/open/T = get_turf(parent)
if(!istype(T))
return
var/mob/living/LM = parent
var/v = volume
var/e = e_range
if(!T.footstep || LM.buckled || LM.lying || !LM.canmove || LM.resting || LM.buckled || LM.throwing || LM.movement_type & (VENTCRAWLING | FLYING))
if (LM.lying && !LM.buckled && !(!T.footstep || LM.movement_type & (VENTCRAWLING | FLYING))) //play crawling sound if we're lying
playsound(T, 'sound/effects/footstep/crawl1.ogg', 15 * v)
return
if(HAS_TRAIT(LM, TRAIT_SILENT_STEP))
return
if(iscarbon(LM))
var/mob/living/carbon/C = LM
if(!C.get_bodypart(BODY_ZONE_L_LEG) && !C.get_bodypart(BODY_ZONE_R_LEG))
return
if(ishuman(C) && C.m_intent == MOVE_INTENT_WALK)
v /= 2
e -= 5
steps++
if(steps >= 3)
steps = 0
else
return
if(prob(80) && !LM.has_gravity(T)) // don't need to step as often when you hop around
return
//begin playsound shenanigans//
//for barefooted non-clawed mobs like monkeys
if(isbarefoot(LM))
playsound(T, pick(GLOB.barefootstep[T.barefootstep][1]),
GLOB.barefootstep[T.barefootstep][2] * v,
TRUE,
GLOB.barefootstep[T.barefootstep][3] + e)
return
//for xenomorphs, dogs, and other clawed mobs
if(isclawfoot(LM))
if(isalienadult(LM)) //xenos are stealthy and get quieter footsteps
v /= 3
e -= 5
playsound(T, pick(GLOB.clawfootstep[T.clawfootstep][1]),
GLOB.clawfootstep[T.clawfootstep][2] * v,
TRUE,
GLOB.clawfootstep[T.clawfootstep][3] + e)
return
//for megafauna and other large and imtimidating mobs such as the bloodminer
if(isheavyfoot(LM))
playsound(T, pick(GLOB.heavyfootstep[T.heavyfootstep][1]),
GLOB.heavyfootstep[T.heavyfootstep][2] * v,
TRUE,
GLOB.heavyfootstep[T.heavyfootstep][3] + e)
return
//for slimes
if(isslime(LM))
playsound(T, 'sound/effects/footstep/slime1.ogg', 15 * v)
return
//for (simple) humanoid mobs (clowns, russians, pirates, etc.)
if(isshoefoot(LM))
if(!ishuman(LM))
playsound(T, pick(GLOB.footstep[T.footstep][1]),
GLOB.footstep[T.footstep][2] * v,
TRUE,
GLOB.footstep[T.footstep][3] + e)
return
if(ishuman(LM)) //for proper humans, they're special
var/mob/living/carbon/human/H = LM
var/feetCover = (H.wear_suit && (H.wear_suit.body_parts_covered & FEET)) || (H.w_uniform && (H.w_uniform.body_parts_covered & FEET) || (H.shoes && (H.shoes.body_parts_covered & FEET)))
if (H.dna.features["taur"] == "Naga" || H.dna.features["taur"] == "Tentacle") //are we a naga or tentacle taur creature
playsound(T, 'sound/effects/footstep/crawl1.ogg', 15 * v)
return
if(feetCover) //are we wearing shoes
playsound(T, pick(GLOB.footstep[T.footstep][1]),
GLOB.footstep[T.footstep][2] * v,
TRUE,
GLOB.footstep[T.footstep][3] + e)
if(!feetCover) //are we NOT wearing shoes
playsound(T, pick(GLOB.barefootstep[T.barefootstep][1]),
GLOB.barefootstep[T.barefootstep][2] * v,
TRUE,
GLOB.barefootstep[T.barefootstep][3] + e)
+88
View File
@@ -0,0 +1,88 @@
/datum/component/infective
dupe_mode = COMPONENT_DUPE_ALLOWED
var/list/datum/disease/diseases //make sure these are the static, non-processing versions!
var/expire_time
var/min_clean_strength = CLEAN_WEAK
/datum/component/infective/Initialize(list/datum/disease/_diseases, expire_in)
if(islist(_diseases))
diseases = _diseases
else
diseases = list(_diseases)
if(expire_in)
expire_time = world.time + expire_in
QDEL_IN(src, expire_in)
if(!ismovableatom(parent))
return COMPONENT_INCOMPATIBLE
RegisterSignal(parent, COMSIG_COMPONENT_CLEAN_ACT, .proc/clean)
RegisterSignal(parent, COMSIG_MOVABLE_BUCKLE, .proc/try_infect_buckle)
RegisterSignal(parent, COMSIG_MOVABLE_BUMP, .proc/try_infect_collide)
RegisterSignal(parent, COMSIG_MOVABLE_CROSSED, .proc/try_infect_crossed)
RegisterSignal(parent, COMSIG_MOVABLE_IMPACT_ZONE, .proc/try_infect_impact_zone)
if(isitem(parent))
RegisterSignal(parent, COMSIG_ITEM_ATTACK_ZONE, .proc/try_infect_attack_zone)
RegisterSignal(parent, COMSIG_ITEM_ATTACK, .proc/try_infect_attack)
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, .proc/try_infect_equipped)
if(istype(parent, /obj/item/reagent_containers/food/snacks))
RegisterSignal(parent, COMSIG_FOOD_EATEN, .proc/try_infect_eat)
else if(istype(parent, /obj/effect/decal/cleanable/blood/gibs))
RegisterSignal(parent, COMSIG_GIBS_STREAK, .proc/try_infect_streak)
/datum/component/infective/proc/try_infect_eat(datum/source, mob/living/eater, mob/living/feeder)
for(var/V in diseases)
eater.ForceContractDisease(V)
try_infect(feeder, BODY_ZONE_L_ARM)
/datum/component/infective/proc/clean(datum/source, clean_strength)
if(clean_strength >= min_clean_strength)
qdel(src)
/datum/component/infective/proc/try_infect_buckle(datum/source, mob/M, force)
if(isliving(M))
try_infect(M)
/datum/component/infective/proc/try_infect_collide(datum/source, atom/A)
var/atom/movable/P = parent
if(P.throwing)
//this will be handled by try_infect_impact_zone()
return
if(isliving(A))
try_infect(A)
/datum/component/infective/proc/try_infect_impact_zone(datum/source, mob/living/target, hit_zone)
try_infect(target, hit_zone)
/datum/component/infective/proc/try_infect_attack_zone(datum/source, mob/living/carbon/target, mob/living/user, hit_zone)
try_infect(user, BODY_ZONE_L_ARM)
try_infect(target, hit_zone)
/datum/component/infective/proc/try_infect_attack(datum/source, mob/living/target, mob/living/user)
if(!iscarbon(target)) //this case will be handled by try_infect_attack_zone
try_infect(target)
try_infect(user, BODY_ZONE_L_ARM)
/datum/component/infective/proc/try_infect_equipped(datum/source, mob/living/L, slot)
var/old_permeability
if(isitem(parent))
//if you are putting an infective item on, it obviously will not protect you, so set its permeability high enough that it will never block ContactContractDisease()
var/obj/item/I = parent
old_permeability = I.permeability_coefficient
I.permeability_coefficient = 1.01
try_infect(L, slot2body_zone(slot))
if(isitem(parent))
var/obj/item/I = parent
I.permeability_coefficient = old_permeability
/datum/component/infective/proc/try_infect_crossed(datum/source, atom/movable/M)
if(isliving(M))
try_infect(M, BODY_ZONE_PRECISE_L_FOOT)
/datum/component/infective/proc/try_infect_streak(datum/source, list/directions, list/output_diseases)
output_diseases |= diseases
/datum/component/infective/proc/try_infect(mob/living/L, target_zone)
for(var/V in diseases)
L.ContactContractDisease(V, target_zone)
+74
View File
@@ -0,0 +1,74 @@
/datum/component/jousting
var/current_direction = NONE
var/max_tile_charge = 5
var/min_tile_charge = 2 //tiles before this code gets into effect.
var/current_tile_charge = 0
var/movement_reset_tolerance = 2 //deciseconds
var/unmounted_damage_boost_per_tile = 0
var/unmounted_knockdown_chance_per_tile = 0
var/unmounted_knockdown_time = 0
var/mounted_damage_boost_per_tile = 2
var/mounted_knockdown_chance_per_tile = 20
var/mounted_knockdown_time = 20
var/requires_mob_riding = TRUE //whether this only works if the attacker is riding a mob, rather than anything they can buckle to.
var/requires_mount = TRUE //kinda defeats the point of jousting if you're not mounted but whatever.
var/mob/current_holder
var/current_timerid
/datum/component/jousting/Initialize()
if(!isitem(parent))
return COMPONENT_INCOMPATIBLE
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, .proc/on_equip)
RegisterSignal(parent, COMSIG_ITEM_DROPPED, .proc/on_drop)
RegisterSignal(parent, COMSIG_ITEM_ATTACK, .proc/on_attack)
/datum/component/jousting/proc/on_equip(datum/source, mob/user, slot)
RegisterSignal(user, COMSIG_MOVABLE_MOVED, .proc/mob_move, TRUE)
current_holder = user
/datum/component/jousting/proc/on_drop(datum/source, mob/user)
UnregisterSignal(user, COMSIG_MOVABLE_MOVED)
current_holder = null
current_direction = NONE
current_tile_charge = 0
/datum/component/jousting/proc/on_attack(datum/source, mob/living/target, mob/user)
if(user != current_holder)
return
var/current = current_tile_charge
var/obj/item/I = parent
var/target_buckled = target.buckled ? TRUE : FALSE //we don't need the reference of what they're buckled to, just whether they are.
if((requires_mount && ((requires_mob_riding && !ismob(user.buckled)) || (!user.buckled))) || !current_direction || (current_tile_charge < min_tile_charge))
return
var/turf/target_turf = get_step(user, current_direction)
if(target in range(1, target_turf))
var/knockdown_chance = (target_buckled? mounted_knockdown_chance_per_tile : unmounted_knockdown_chance_per_tile) * current
var/knockdown_time = (target_buckled? mounted_knockdown_time : unmounted_knockdown_time)
var/damage = (target_buckled? mounted_damage_boost_per_tile : unmounted_damage_boost_per_tile) * current
var/sharp = I.get_sharpness()
var/msg
if(damage)
msg += "[user] [sharp? "impales" : "slams into"] [target] [sharp? "on" : "with"] their [parent]"
target.apply_damage(damage, BRUTE, user.zone_selected, 0)
if(prob(knockdown_chance))
msg += " and knocks [target] [target_buckled? "off of [target.buckled]" : "down"]"
if(target_buckled)
target.buckled.unbuckle_mob(target)
target.Knockdown(knockdown_time)
if(length(msg))
user.visible_message("<span class='danger'>[msg]!</span>")
/datum/component/jousting/proc/mob_move(datum/source, newloc, dir)
if(!current_holder || (requires_mount && ((requires_mob_riding && !ismob(current_holder.buckled)) || (!current_holder.buckled))))
return
if(dir != current_direction)
current_tile_charge = 0
current_direction = dir
if(current_tile_charge < max_tile_charge)
current_tile_charge++
if(current_timerid)
deltimer(current_timerid)
current_timerid = addtimer(CALLBACK(src, .proc/reset_charge), movement_reset_tolerance, TIMER_STOPPABLE)
/datum/component/jousting/proc/reset_charge()
current_tile_charge = 0
+239
View File
@@ -0,0 +1,239 @@
#define LOCKON_AIMING_MAX_CURSOR_RADIUS 7
#define LOCKON_IGNORE_RESULT "ignore_my_result"
#define LOCKON_RANGING_BREAK_CHECK if(current_ranging_id != this_id){return LOCKON_IGNORE_RESULT}
/datum/component/lockon_aiming
dupe_mode = COMPONENT_DUPE_ALLOWED
var/lock_icon = 'icons/mob/cameramob.dmi'
var/lock_icon_state = "marker"
var/mutable_appearance/lock_appearance
var/list/image/lock_images
var/list/target_typecache
var/list/immune_weakrefs //list(weakref = TRUE)
var/mob_stat_check = TRUE //if a potential target is a mob make sure it's conscious!
var/lock_amount = 1
var/lock_cursor_range = 5
var/list/locked_weakrefs
var/update_disabled = FALSE
var/current_ranging_id = 0
var/list/last_location
var/datum/callback/on_lock
var/datum/callback/can_target_callback
/datum/component/lockon_aiming/Initialize(range, list/typecache, amount, list/immune, datum/callback/when_locked, icon, icon_state, datum/callback/target_callback)
if(!ismob(parent))
return COMPONENT_INCOMPATIBLE
if(target_callback)
can_target_callback = target_callback
else
can_target_callback = CALLBACK(src, .proc/can_target)
if(range)
lock_cursor_range = range
if(typecache)
target_typecache = typecache
if(amount)
lock_amount = amount
immune_weakrefs = list(WEAKREF(parent) = TRUE) //Manually take this out if you want..
if(immune)
for(var/i in immune)
if(isweakref(i))
immune_weakrefs[i] = TRUE
else if(isatom(i))
immune_weakrefs[WEAKREF(i)] = TRUE
if(when_locked)
on_lock = when_locked
if(icon)
lock_icon = icon
if(icon_state)
lock_icon_state = icon_state
generate_lock_visuals()
var/mob/M = parent
LAZYOR(M.mousemove_intercept_objects, src)
START_PROCESSING(SSfastprocess, src)
/datum/component/lockon_aiming/Destroy()
var/mob/M = parent
clear_visuals()
LAZYREMOVE(M.mousemove_intercept_objects, src)
STOP_PROCESSING(SSfastprocess, src)
return ..()
/datum/component/lockon_aiming/proc/show_visuals()
LAZYINITLIST(lock_images)
var/mob/M = parent
if(!M.client)
return
for(var/i in locked_weakrefs)
var/datum/weakref/R = i
var/atom/A = R.resolve()
if(!A)
continue //It'll be cleared by processing.
var/image/I = new
I.appearance = lock_appearance
I.loc = A
M.client.images |= I
lock_images |= I
/datum/component/lockon_aiming/proc/clear_visuals()
var/mob/M = parent
if(!M.client)
return
if(!lock_images)
return
for(var/i in lock_images)
M.client.images -= i
qdel(i)
lock_images.Cut()
/datum/component/lockon_aiming/proc/refresh_visuals()
clear_visuals()
show_visuals()
/datum/component/lockon_aiming/proc/generate_lock_visuals()
lock_appearance = mutable_appearance(icon = lock_icon, icon_state = lock_icon_state, layer = FLOAT_LAYER)
/datum/component/lockon_aiming/proc/unlock_all(refresh_vis = TRUE)
LAZYCLEARLIST(locked_weakrefs)
if(refresh_vis)
refresh_visuals()
/datum/component/lockon_aiming/proc/unlock(atom/A, refresh_vis = TRUE)
if(!A.weak_reference)
return
LAZYREMOVE(locked_weakrefs, A.weak_reference)
if(refresh_vis)
refresh_visuals()
/datum/component/lockon_aiming/proc/lock(atom/A, refresh_vis = TRUE)
LAZYOR(locked_weakrefs, WEAKREF(A))
if(refresh_vis)
refresh_visuals()
/datum/component/lockon_aiming/proc/add_immune_atom(atom/A)
var/datum/weakref/R = WEAKREF(A)
if(immune_weakrefs && (immune_weakrefs[R]))
return
LAZYSET(immune_weakrefs, R, TRUE)
/datum/component/lockon_aiming/proc/remove_immune_atom(atom/A)
if(!A.weak_reference || !immune_weakrefs) //if A doesn't have a weakref how did it get on the immunity list?
return
LAZYREMOVE(immune_weakrefs, A.weak_reference)
/datum/component/lockon_aiming/onMouseMove(object,location,control,params)
var/mob/M = parent
if(!istype(M) || !M.client)
return
var/datum/position/P = mouse_absolute_datum_map_position_from_client(M.client)
if(!P)
return
var/turf/T = P.return_turf()
LAZYINITLIST(last_location)
if(length(last_location) == 3 && last_location[1] == T.x && last_location[2] == T.y && last_location[3] == T.z)
return //Same turf, don't bother.
if(last_location)
last_location.Cut()
else
last_location = list()
last_location.len = 3
last_location[1] = T.x
last_location[2] = T.y
last_location[3] = T.z
autolock()
/datum/component/lockon_aiming/process()
if(update_disabled)
return
if(!last_location)
return
var/changed = FALSE
for(var/i in locked_weakrefs)
var/datum/weakref/R = i
if(istype(R))
var/atom/thing = R.resolve()
if(!istype(thing) || (get_dist(thing, locate(last_location[1], last_location[2], last_location[3])) > lock_cursor_range))
unlock(R)
changed = TRUE
else
unlock(R)
changed = TRUE
if(changed)
autolock()
/datum/component/lockon_aiming/proc/autolock()
var/mob/M = parent
if(!M.client)
return FALSE
var/datum/position/current = mouse_absolute_datum_map_position_from_client(M.client)
var/turf/target = current.return_turf()
var/list/atom/targets = get_nearest(target, target_typecache, lock_amount, lock_cursor_range)
if(targets == LOCKON_IGNORE_RESULT)
return
unlock_all(FALSE)
for(var/i in targets)
if(immune_weakrefs[WEAKREF(i)])
continue
lock(i, FALSE)
refresh_visuals()
on_lock.Invoke(locked_weakrefs)
/datum/component/lockon_aiming/proc/can_target(atom/A)
var/mob/M = A
return is_type_in_typecache(A, target_typecache) && !(ismob(A) && mob_stat_check && M.stat != CONSCIOUS) && !immune_weakrefs[WEAKREF(A)]
/datum/component/lockon_aiming/proc/get_nearest(turf/T, list/typecache, amount, range)
current_ranging_id++
var/this_id = current_ranging_id
var/list/L = list()
var/turf/center = get_turf(T)
if(amount < 1 || range < 0 || !istype(center) || !islist(typecache))
return
if(range == 0)
return typecache_filter_list(T.contents + T, typecache)
var/x = 0
var/y = 0
var/cd = 0
while(cd <= range)
x = center.x - cd + 1
y = center.y + cd
LOCKON_RANGING_BREAK_CHECK
for(x in x to center.x + cd)
T = locate(x, y, center.z)
if(T)
L |= special_list_filter(T.contents, can_target_callback)
if(L.len >= amount)
L.Cut(amount+1)
return L
LOCKON_RANGING_BREAK_CHECK
y = center.y + cd - 1
x = center.x + cd
for(y in center.y - cd to y)
T = locate(x, y, center.z)
if(T)
L |= special_list_filter(T.contents, can_target_callback)
if(L.len >= amount)
L.Cut(amount+1)
return L
LOCKON_RANGING_BREAK_CHECK
y = center.y - cd
x = center.x + cd - 1
for(x in center.x - cd to x)
T = locate(x, y, center.z)
if(T)
L |= special_list_filter(T.contents, can_target_callback)
if(L.len >= amount)
L.Cut(amount+1)
return L
LOCKON_RANGING_BREAK_CHECK
y = center.y - cd + 1
x = center.x - cd
for(y in y to center.y + cd)
T = locate(x, y, center.z)
if(T)
L |= special_list_filter(T.contents, can_target_callback)
if(L.len >= amount)
L.Cut(amount+1)
return L
LOCKON_RANGING_BREAK_CHECK
cd++
CHECK_TICK
+42
View File
@@ -0,0 +1,42 @@
/datum/component/mirage_border
can_transfer = TRUE
var/obj/effect/abstract/mirage_holder/holder
/datum/component/mirage_border/Initialize(turf/target, direction, range=world.view)
if(!isturf(parent))
return COMPONENT_INCOMPATIBLE
if(!target || !direction)
. = COMPONENT_INCOMPATIBLE
CRASH("[type] improperly instanced with the following args: target=\[[target]\], direction=\[[direction]\], range=\[[range]\]")
holder = new(parent)
var/x = target.x
var/y = target.y
var/z = target.z
var/turf/southwest = locate(CLAMP(x - (direction & WEST ? range : 0), 1, world.maxx), CLAMP(y - (direction & SOUTH ? range : 0), 1, world.maxy), CLAMP(z, 1, world.maxz))
var/turf/northeast = locate(CLAMP(x + (direction & EAST ? range : 0), 1, world.maxx), CLAMP(y + (direction & NORTH ? range : 0), 1, world.maxy), CLAMP(z, 1, world.maxz))
//holder.vis_contents += block(southwest, northeast) // This doesnt work because of beta bug memes
for(var/i in block(southwest, northeast))
holder.vis_contents += i
if(direction & SOUTH)
holder.pixel_y -= world.icon_size * range
if(direction & WEST)
holder.pixel_x -= world.icon_size * range
/datum/component/mirage_border/Destroy()
QDEL_NULL(holder)
return ..()
/datum/component/mirage_border/PreTransfer()
holder.moveToNullspace()
/datum/component/mirage_border/PostTransfer()
if(!isturf(parent))
return COMPONENT_INCOMPATIBLE
holder.forceMove(parent)
/obj/effect/abstract/mirage_holder
name = "Mirage holder"
anchored = TRUE
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+283
View File
@@ -0,0 +1,283 @@
#define MINOR_INSANITY_PEN 5
#define MAJOR_INSANITY_PEN 10
/datum/component/mood
var/mood //Real happiness
var/sanity = 100 //Current sanity
var/shown_mood //Shown happiness, this is what others can see when they try to examine you, prevents antag checking by noticing traitors are always very happy.
var/mood_level = 5 //To track what stage of moodies they're on
var/sanity_level = 5 //To track what stage of sanity they're on
var/mood_modifier = 1 //Modifier to allow certain mobs to be less affected by moodlets
var/list/datum/mood_event/mood_events = list()
var/insanity_effect = 0 //is the owner being punished for low mood? If so, how much?
var/obj/screen/mood/screen_obj
/datum/component/mood/Initialize()
if(!isliving(parent))
return COMPONENT_INCOMPATIBLE
START_PROCESSING(SSmood, src)
RegisterSignal(parent, COMSIG_ADD_MOOD_EVENT, .proc/add_event)
RegisterSignal(parent, COMSIG_CLEAR_MOOD_EVENT, .proc/clear_event)
RegisterSignal(parent, COMSIG_MODIFY_SANITY, .proc/modify_sanity)
RegisterSignal(parent, COMSIG_MOB_HUD_CREATED, .proc/modify_hud)
var/mob/living/owner = parent
if(owner.hud_used)
modify_hud()
var/datum/hud/hud = owner.hud_used
hud.show_hud(hud.hud_version)
/datum/component/mood/Destroy()
STOP_PROCESSING(SSmood, src)
unmodify_hud()
return ..()
/datum/component/mood/proc/print_mood(mob/user)
var/msg = "<span class='info'>*---------*\n<EM>Your current mood</EM>\n"
msg += "<span class='notice'>My mental status: </span>" //Long term
switch(sanity)
if(SANITY_GREAT to INFINITY)
msg += "<span class='nicegreen'>My mind feels like a temple!<span>\n"
if(SANITY_NEUTRAL to SANITY_GREAT)
msg += "<span class='nicegreen'>I have been feeling great lately!<span>\n"
if(SANITY_DISTURBED to SANITY_NEUTRAL)
msg += "<span class='nicegreen'>I have felt quite decent lately.<span>\n"
if(SANITY_UNSTABLE to SANITY_DISTURBED)
msg += "<span class='warning'>I'm feeling a little bit unhinged...</span>\n"
if(SANITY_CRAZY to SANITY_UNSTABLE)
msg += "<span class='boldwarning'>I'm freaking out!!</span>\n"
if(SANITY_INSANE to SANITY_CRAZY)
msg += "<span class='boldwarning'>AHAHAHAHAHAHAHAHAHAH!!</span>\n"
msg += "<span class='notice'>My current mood: </span>" //Short term
switch(mood_level)
if(1)
msg += "<span class='boldwarning'>I wish I was dead!<span>\n"
if(2)
msg += "<span class='boldwarning'>I feel terrible...<span>\n"
if(3)
msg += "<span class='boldwarning'>I feel very upset.<span>\n"
if(4)
msg += "<span class='boldwarning'>I'm a bit sad.<span>\n"
if(5)
msg += "<span class='nicegreen'>I'm alright.<span>\n"
if(6)
msg += "<span class='nicegreen'>I feel pretty okay.<span>\n"
if(7)
msg += "<span class='nicegreen'>I feel pretty good.<span>\n"
if(8)
msg += "<span class='nicegreen'>I feel amazing!<span>\n"
if(9)
msg += "<span class='nicegreen'>I love life!<span>\n"
msg += "<span class='notice'>Moodlets:\n</span>"//All moodlets
if(mood_events.len)
for(var/i in mood_events)
var/datum/mood_event/event = mood_events[i]
msg += event.description
else
msg += "<span class='nicegreen'>I don't have much of a reaction to anything right now.<span>\n"
to_chat(user || parent, msg)
/datum/component/mood/proc/update_mood() //Called whenever a mood event is added or removed
mood = 0
shown_mood = 0
for(var/i in mood_events)
var/datum/mood_event/event = mood_events[i]
mood += event.mood_change
if(!event.hidden)
shown_mood += event.mood_change
mood *= mood_modifier
shown_mood *= mood_modifier
switch(mood)
if(-INFINITY to MOOD_LEVEL_SAD4)
mood_level = 1
if(MOOD_LEVEL_SAD4 to MOOD_LEVEL_SAD3)
mood_level = 2
if(MOOD_LEVEL_SAD3 to MOOD_LEVEL_SAD2)
mood_level = 3
if(MOOD_LEVEL_SAD2 to MOOD_LEVEL_SAD1)
mood_level = 4
if(MOOD_LEVEL_SAD1 to MOOD_LEVEL_HAPPY1)
mood_level = 5
if(MOOD_LEVEL_HAPPY1 to MOOD_LEVEL_HAPPY2)
mood_level = 6
if(MOOD_LEVEL_HAPPY2 to MOOD_LEVEL_HAPPY3)
mood_level = 7
if(MOOD_LEVEL_HAPPY3 to MOOD_LEVEL_HAPPY4)
mood_level = 8
if(MOOD_LEVEL_HAPPY4 to INFINITY)
mood_level = 9
update_mood_icon()
/datum/component/mood/proc/update_mood_icon()
var/mob/living/owner = parent
if(owner.client && owner.hud_used)
if(sanity < 25)
screen_obj.icon_state = "mood_insane"
else if (owner.has_status_effect(/datum/status_effect/chem/enthrall))//Fermichem enthral chem, maybe change?
screen_obj.icon_state = "mood_entrance"
else
screen_obj.icon_state = "mood[mood_level]"
/datum/component/mood/process() //Called on SSmood process
if(QDELETED(parent)) // workaround to an obnoxious sneaky periodical runtime.
qdel(src)
return
var/mob/living/owner = parent
switch(mood_level)
if(1)
setSanity(sanity-0.2)
if(2)
setSanity(sanity-0.125, minimum=SANITY_CRAZY)
if(3)
setSanity(sanity-0.075, minimum=SANITY_UNSTABLE)
if(4)
setSanity(sanity-0.025, minimum=SANITY_DISTURBED)
if(5)
setSanity(sanity+0.1)
if(6)
setSanity(sanity+0.15)
if(7)
setSanity(sanity+0.20)
if(8)
setSanity(sanity+0.25, maximum=SANITY_GREAT)
if(9)
setSanity(sanity+0.4, maximum=SANITY_GREAT)
if(HAS_TRAIT(owner, TRAIT_DEPRESSION))
if(prob(0.05))
add_event(null, "depression", /datum/mood_event/depression)
clear_event(null, "jolly")
if(HAS_TRAIT(owner, TRAIT_JOLLY))
if(prob(0.05))
add_event(null, "jolly", /datum/mood_event/jolly)
clear_event(null, "depression")
HandleNutrition(owner)
/datum/component/mood/proc/setSanity(amount, minimum=SANITY_INSANE, maximum=SANITY_NEUTRAL)//I'm sure bunging this in here will have no negative repercussions.
var/mob/living/master = parent
if(amount == sanity)
return
// If we're out of the acceptable minimum-maximum range move back towards it in steps of 0.5
// If the new amount would move towards the acceptable range faster then use it instead
if(sanity < minimum && amount < sanity + 0.5)
amount = sanity + 0.5
else if(sanity > maximum && amount > sanity - 0.5)
amount = sanity - 0.5
// Disturbed stops you from getting any more sane
if(HAS_TRAIT(master, TRAIT_UNSTABLE))
sanity = min(amount,sanity)
else
sanity = amount
switch(sanity)
if(-INFINITY to SANITY_CRAZY)
setInsanityEffect(MAJOR_INSANITY_PEN)
master.add_movespeed_modifier(MOVESPEED_ID_SANITY, TRUE, 100, override=TRUE, multiplicative_slowdown=1.5) //Did we change something ? movetypes is runtiming, movetypes=(~FLYING))
sanity_level = 6
if(SANITY_CRAZY to SANITY_UNSTABLE)
setInsanityEffect(MINOR_INSANITY_PEN)
master.add_movespeed_modifier(MOVESPEED_ID_SANITY, TRUE, 100, override=TRUE, multiplicative_slowdown=1)//, movetypes=(~FLYING))
sanity_level = 5
if(SANITY_UNSTABLE to SANITY_DISTURBED)
setInsanityEffect(0)
master.add_movespeed_modifier(MOVESPEED_ID_SANITY, TRUE, 100, override=TRUE, multiplicative_slowdown=0.5)//, movetypes=(~FLYING))
sanity_level = 4
if(SANITY_DISTURBED to SANITY_NEUTRAL)
setInsanityEffect(0)
master.remove_movespeed_modifier(MOVESPEED_ID_SANITY, TRUE)
sanity_level = 3
if(SANITY_NEUTRAL+1 to SANITY_GREAT+1) //shitty hack but +1 to prevent it from responding to super small differences
setInsanityEffect(0)
master.remove_movespeed_modifier(MOVESPEED_ID_SANITY, TRUE)
sanity_level = 2
if(SANITY_GREAT+1 to INFINITY)
setInsanityEffect(0)
master.remove_movespeed_modifier(MOVESPEED_ID_SANITY, TRUE)
sanity_level = 1
//update_mood_icon()
/datum/component/mood/proc/setInsanityEffect(newval)//More code so that the previous proc works
if(newval == insanity_effect)
return
//var/mob/living/master = parent
//master.crit_threshold = (master.crit_threshold - insanity_effect) + newval
insanity_effect = newval
/datum/component/mood/proc/modify_sanity(datum/source, amount, minimum = -INFINITY, maximum = INFINITY)
setSanity(sanity + amount, minimum, maximum)
/datum/component/mood/proc/add_event(datum/source, category, type, param) //Category will override any events in the same category, should be unique unless the event is based on the same thing like hunger.
var/datum/mood_event/the_event
if(mood_events[category])
the_event = mood_events[category]
if(the_event.type != type)
clear_event(null, category)
else
if(the_event.timeout)
addtimer(CALLBACK(src, .proc/clear_event, null, category), the_event.timeout, TIMER_UNIQUE|TIMER_OVERRIDE)
return 0 //Don't have to update the event.
the_event = new type(src, param)//This causes a runtime for some reason, was this me? No - there's an event floating around missing a definition.
mood_events[category] = the_event
update_mood()
if(the_event.timeout)
addtimer(CALLBACK(src, .proc/clear_event, null, category), the_event.timeout, TIMER_UNIQUE|TIMER_OVERRIDE)
/datum/component/mood/proc/clear_event(datum/source, category)
var/datum/mood_event/event = mood_events[category]
if(!event)
return 0
mood_events -= category
qdel(event)
update_mood()
/datum/component/mood/proc/modify_hud(datum/source)
var/mob/living/owner = parent
var/datum/hud/hud = owner.hud_used
screen_obj = new
hud.infodisplay += screen_obj
RegisterSignal(hud, COMSIG_PARENT_QDELETED, .proc/unmodify_hud)
RegisterSignal(screen_obj, COMSIG_CLICK, .proc/hud_click)
/datum/component/mood/proc/unmodify_hud(datum/source)
if(!screen_obj || !parent)
return
var/mob/living/owner = parent
var/datum/hud/hud = owner.hud_used
if(hud && hud.infodisplay)
hud.infodisplay -= screen_obj
QDEL_NULL(screen_obj)
/datum/component/mood/proc/hud_click(datum/source, location, control, params, mob/user)
print_mood(user)
/datum/component/mood/proc/HandleNutrition(mob/living/L)
switch(L.nutrition)
if(NUTRITION_LEVEL_FULL to INFINITY)
add_event(null, "nutrition", /datum/mood_event/fat)
if(NUTRITION_LEVEL_WELL_FED to NUTRITION_LEVEL_FULL)
add_event(null, "nutrition", /datum/mood_event/wellfed)
if( NUTRITION_LEVEL_FED to NUTRITION_LEVEL_WELL_FED)
add_event(null, "nutrition", /datum/mood_event/fed)
if(NUTRITION_LEVEL_HUNGRY to NUTRITION_LEVEL_FED)
clear_event(null, "nutrition")
if(NUTRITION_LEVEL_STARVING to NUTRITION_LEVEL_HUNGRY)
add_event(null, "nutrition", /datum/mood_event/hungry)
if(0 to NUTRITION_LEVEL_STARVING)
add_event(null, "nutrition", /datum/mood_event/starving)
#undef MINOR_INSANITY_PEN
#undef MAJOR_INSANITY_PEN
+151
View File
@@ -0,0 +1,151 @@
/datum/component/orbiter
can_transfer = TRUE
dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
var/list/orbiters
//radius: range to orbit at, radius of the circle formed by orbiting (in pixels)
//clockwise: whether you orbit clockwise or anti clockwise
//rotation_speed: how fast to rotate (how many ds should it take for a rotation to complete)
//rotation_segments: the resolution of the orbit circle, less = a more block circle, this can be used to produce hexagons (6 segments) triangles (3 segments), and so on, 36 is the best default.
//pre_rotation: Chooses to rotate src 90 degress towards the orbit dir (clockwise/anticlockwise), useful for things to go "head first" like ghosts
/datum/component/orbiter/Initialize(atom/movable/orbiter, radius, clockwise, rotation_speed, rotation_segments, pre_rotation)
if(!istype(orbiter) || !isatom(parent) || isarea(parent))
return COMPONENT_INCOMPATIBLE
orbiters = list()
var/atom/master = parent
master.orbiters = src
begin_orbit(orbiter, radius, clockwise, rotation_speed, rotation_segments, pre_rotation)
/datum/component/orbiter/RegisterWithParent()
var/atom/target = parent
while(ismovableatom(target))
RegisterSignal(target, COMSIG_MOVABLE_MOVED, .proc/move_react)
target = target.loc
/datum/component/orbiter/UnregisterFromParent()
var/atom/target = parent
while(ismovableatom(target))
UnregisterSignal(target, COMSIG_MOVABLE_MOVED)
target = target.loc
/datum/component/orbiter/Destroy()
var/atom/master = parent
master.orbiters = null
for(var/i in orbiters)
end_orbit(i)
orbiters = null
return ..()
/datum/component/orbiter/InheritComponent(datum/component/orbiter/newcomp, original, list/arguments)
if(arguments)
begin_orbit(arglist(arguments))
return
// The following only happens on component transfers
orbiters += newcomp.orbiters
/datum/component/orbiter/PostTransfer()
if(!isatom(parent) || isarea(parent) || !get_turf(parent))
return COMPONENT_INCOMPATIBLE
move_react()
/datum/component/orbiter/proc/begin_orbit(atom/movable/orbiter, radius, clockwise, rotation_speed, rotation_segments, pre_rotation)
if(orbiter.orbiting)
if(orbiter.orbiting == src)
orbiter.orbiting.end_orbit(orbiter, TRUE)
else
orbiter.orbiting.end_orbit(orbiter)
orbiters[orbiter] = TRUE
orbiter.orbiting = src
RegisterSignal(orbiter, COMSIG_MOVABLE_MOVED, .proc/orbiter_move_react)
var/matrix/initial_transform = matrix(orbiter.transform)
// Head first!
if(pre_rotation)
var/matrix/M = matrix(orbiter.transform)
var/pre_rot = 90
if(!clockwise)
pre_rot = -90
M.Turn(pre_rot)
orbiter.transform = M
var/matrix/shift = matrix(orbiter.transform)
shift.Translate(0, radius)
orbiter.transform = shift
orbiter.SpinAnimation(rotation_speed, -1, clockwise, rotation_segments, parallel = FALSE)
//we stack the orbits up client side, so we can assign this back to normal server side without it breaking the orbit
orbiter.transform = initial_transform
orbiter.forceMove(get_turf(parent))
to_chat(orbiter, "<span class='notice'>Now orbiting [parent].</span>")
/datum/component/orbiter/proc/end_orbit(atom/movable/orbiter, refreshing=FALSE)
if(!orbiters[orbiter])
return
UnregisterSignal(orbiter, COMSIG_MOVABLE_MOVED)
orbiter.SpinAnimation(0, 0)
orbiters -= orbiter
orbiter.stop_orbit(src)
orbiter.orbiting = null
if(!refreshing && !length(orbiters) && !QDELING(src))
qdel(src)
// This proc can receive signals by either the thing being directly orbited or anything holding it
/datum/component/orbiter/proc/move_react(atom/orbited, atom/oldloc, direction)
set waitfor = FALSE // Transfer calls this directly and it doesnt care if the ghosts arent done moving
var/atom/movable/master = parent
if(master.loc == oldloc)
return
var/turf/newturf = get_turf(master)
if(!newturf)
qdel(src)
// Handling the signals of stuff holding us (or not anymore)
// These are prety rarely activated, how often are you following something in a bag?
if(oldloc && !isturf(oldloc)) // We used to be registered to it, probably
var/atom/target = oldloc
while(ismovableatom(target))
UnregisterSignal(target, COMSIG_MOVABLE_MOVED)
target = target.loc
if(orbited?.loc && orbited.loc != newturf) // We want to know when anything holding us moves too
var/atom/target = orbited.loc
while(ismovableatom(target))
RegisterSignal(target, COMSIG_MOVABLE_MOVED, .proc/move_react, TRUE)
target = target.loc
var/atom/curloc = master.loc
for(var/i in orbiters)
var/atom/movable/thing = i
if(QDELETED(thing) || thing.loc == newturf)
continue
thing.forceMove(newturf)
if(CHECK_TICK && master.loc != curloc)
// We moved again during the checktick, cancel current operation
break
/datum/component/orbiter/proc/orbiter_move_react(atom/movable/orbiter, atom/oldloc, direction)
if(orbiter.loc == get_turf(parent))
return
end_orbit(orbiter)
/////////////////////
/atom/movable/proc/orbit(atom/A, radius = 10, clockwise = FALSE, rotation_speed = 20, rotation_segments = 36, pre_rotation = TRUE)
if(!istype(A) || !get_turf(A) || A == src)
return
return A.AddComponent(/datum/component/orbiter, src, radius, clockwise, rotation_speed, rotation_segments, pre_rotation)
/atom/movable/proc/stop_orbit(datum/component/orbiter/orbits)
return // We're just a simple hook
/atom/proc/transfer_observers_to(atom/target)
if(!orbiters || !istype(target) || !get_turf(target) || target == src)
return
target.TakeComponent(orbiters)
+164
View File
@@ -0,0 +1,164 @@
#define ROTATION_ALTCLICK (1<<0)
#define ROTATION_WRENCH (1<<1)
#define ROTATION_VERBS (1<<2)
#define ROTATION_COUNTERCLOCKWISE (1<<3)
#define ROTATION_CLOCKWISE (1<<4)
#define ROTATION_FLIP (1<<5)
/datum/component/simple_rotation
var/datum/callback/can_user_rotate //Checks if user can rotate
var/datum/callback/can_be_rotated //Check if object can be rotated at all
var/datum/callback/after_rotation //Additional stuff to do after rotation
var/rotation_flags = NONE
var/default_rotation_direction = ROTATION_CLOCKWISE
/datum/component/simple_rotation/Initialize(rotation_flags = NONE ,can_user_rotate,can_be_rotated,after_rotation)
if(!ismovableatom(parent))
return COMPONENT_INCOMPATIBLE
//throw if no rotation direction is specificed ?
src.rotation_flags = rotation_flags
if(can_user_rotate)
src.can_user_rotate = can_user_rotate
else
src.can_user_rotate = CALLBACK(src,.proc/default_can_user_rotate)
if(can_be_rotated)
src.can_be_rotated = can_be_rotated
else
src.can_be_rotated = CALLBACK(src,.proc/default_can_be_rotated)
if(after_rotation)
src.after_rotation = after_rotation
else
src.after_rotation = CALLBACK(src,.proc/default_after_rotation)
//Try Clockwise,counter,flip in order
if(src.rotation_flags & ROTATION_FLIP)
default_rotation_direction = ROTATION_FLIP
if(src.rotation_flags & ROTATION_COUNTERCLOCKWISE)
default_rotation_direction = ROTATION_COUNTERCLOCKWISE
if(src.rotation_flags & ROTATION_CLOCKWISE)
default_rotation_direction = ROTATION_CLOCKWISE
/datum/component/simple_rotation/proc/add_signals()
if(rotation_flags & ROTATION_ALTCLICK)
RegisterSignal(parent, COMSIG_CLICK_ALT, .proc/HandRot)
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, .proc/ExamineMessage)
if(rotation_flags & ROTATION_WRENCH)
RegisterSignal(parent, COMSIG_PARENT_ATTACKBY, .proc/WrenchRot)
/datum/component/simple_rotation/proc/add_verbs()
if(rotation_flags & ROTATION_VERBS)
var/atom/movable/AM = parent
if(rotation_flags & ROTATION_FLIP)
AM.verbs += /atom/movable/proc/simple_rotate_flip
if(rotation_flags & ROTATION_CLOCKWISE)
AM.verbs += /atom/movable/proc/simple_rotate_clockwise
if(rotation_flags & ROTATION_COUNTERCLOCKWISE)
AM.verbs += /atom/movable/proc/simple_rotate_counterclockwise
/datum/component/simple_rotation/proc/remove_verbs()
if(parent)
var/atom/movable/AM = parent
AM.verbs -= /atom/movable/proc/simple_rotate_flip
AM.verbs -= /atom/movable/proc/simple_rotate_clockwise
AM.verbs -= /atom/movable/proc/simple_rotate_counterclockwise
/datum/component/simple_rotation/proc/remove_signals()
UnregisterSignal(parent, list(COMSIG_CLICK_ALT, COMSIG_PARENT_EXAMINE, COMSIG_PARENT_ATTACKBY))
/datum/component/simple_rotation/RegisterWithParent()
add_verbs()
add_signals()
. = ..()
/datum/component/simple_rotation/PostTransfer()
//Because of the callbacks which we don't track cleanly we can't transfer this
//item cleanly, better to let the new of the new item create a new rotation datum
//instead (there's no real state worth transferring)
return COMPONENT_NOTRANSFER
/datum/component/simple_rotation/UnregisterFromParent()
remove_verbs()
remove_signals()
. = ..()
/datum/component/simple_rotation/Destroy()
QDEL_NULL(can_user_rotate)
QDEL_NULL(can_be_rotated)
QDEL_NULL(after_rotation)
//Signals + verbs removed via UnRegister
. = ..()
/datum/component/simple_rotation/RemoveComponent()
remove_verbs()
. = ..()
/datum/component/simple_rotation/proc/ExamineMessage(datum/source, mob/user)
if(rotation_flags & ROTATION_ALTCLICK)
to_chat(user, "<span class='notice'>Alt-click to rotate it clockwise.</span>")
/datum/component/simple_rotation/proc/HandRot(datum/source, mob/user, rotation = default_rotation_direction)
if(!can_be_rotated.Invoke(user, rotation) || !can_user_rotate.Invoke(user, rotation))
return
BaseRot(user, rotation)
/datum/component/simple_rotation/proc/WrenchRot(datum/source, obj/item/I, mob/living/user)
if(!can_be_rotated.Invoke(user,default_rotation_direction) || !can_user_rotate.Invoke(user,default_rotation_direction))
return
if(istype(I,/obj/item/wrench))
BaseRot(user,default_rotation_direction)
return COMPONENT_NO_AFTERATTACK
/datum/component/simple_rotation/proc/BaseRot(mob/user,rotation_type)
var/atom/movable/AM = parent
var/rot_degree
switch(rotation_type)
if(ROTATION_CLOCKWISE)
rot_degree = -90
if(ROTATION_COUNTERCLOCKWISE)
rot_degree = 90
if(ROTATION_FLIP)
rot_degree = 180
AM.setDir(turn(AM.dir,rot_degree))
after_rotation.Invoke(user,rotation_type)
/datum/component/simple_rotation/proc/default_can_user_rotate(mob/living/user, rotation_type)
if(!istype(user) || !user.canUseTopic(parent, BE_CLOSE, NO_DEXTERY))
return FALSE
return TRUE
/datum/component/simple_rotation/proc/default_can_be_rotated(mob/user, rotation_type)
var/atom/movable/AM = parent
return !AM.anchored
/datum/component/simple_rotation/proc/default_after_rotation(mob/user, rotation_type)
to_chat(user,"<span class='notice'>You [rotation_type == ROTATION_FLIP ? "flip" : "rotate"] [parent].</span>")
/atom/movable/proc/simple_rotate_clockwise()
set name = "Rotate Clockwise"
set category = "Object"
set src in oview(1)
var/datum/component/simple_rotation/rotcomp = GetComponent(/datum/component/simple_rotation)
if(rotcomp)
rotcomp.HandRot(null,usr,ROTATION_CLOCKWISE)
/atom/movable/proc/simple_rotate_counterclockwise()
set name = "Rotate Counter-Clockwise"
set category = "Object"
set src in oview(1)
var/datum/component/simple_rotation/rotcomp = GetComponent(/datum/component/simple_rotation)
if(rotcomp)
rotcomp.HandRot(null,usr,ROTATION_COUNTERCLOCKWISE)
/atom/movable/proc/simple_rotate_flip()
set name = "Flip"
set category = "Object"
set src in oview(1)
var/datum/component/simple_rotation/rotcomp = GetComponent(/datum/component/simple_rotation)
if(rotcomp)
rotcomp.HandRot(null,usr,ROTATION_FLIP)
@@ -0,0 +1,89 @@
/datum/component/storage/concrete/pockets
max_items = 2
max_w_class = WEIGHT_CLASS_SMALL
max_combined_w_class = 50
rustle_sound = FALSE
/datum/component/storage/concrete/pockets/handle_item_insertion(obj/item/I, prevent_warning, mob/user)
. = ..()
if(. && silent && !prevent_warning)
if(quickdraw)
to_chat(user, "<span class='notice'>You discreetly slip [I] into [parent]. Alt-click [parent] to remove it.</span>")
else
to_chat(user, "<span class='notice'>You discreetly slip [I] into [parent].</span>")
/datum/component/storage/concrete/pockets
max_w_class = WEIGHT_CLASS_NORMAL
/datum/component/storage/concrete/pockets/small
max_items = 1
attack_hand_interact = FALSE
/datum/component/storage/concrete/pockets/small/collar
max_items = 1
/datum/component/storage/concrete/pockets/small/collar/Initialize()
. = ..()
can_hold = typecacheof(list(
/obj/item/reagent_containers/food/snacks/cookie,
/obj/item/reagent_containers/food/snacks/sugarcookie))
/datum/component/storage/concrete/pockets/small/collar/locked/Initialize()
. = ..()
can_hold = typecacheof(list(
/obj/item/reagent_containers/food/snacks/cookie,
/obj/item/reagent_containers/food/snacks/sugarcookie,
/obj/item/key/collar))
/datum/component/storage/concrete/pockets/tiny
max_items = 1
max_w_class = WEIGHT_CLASS_TINY
attack_hand_interact = FALSE
/datum/component/storage/concrete/pockets/small/detective
attack_hand_interact = TRUE // so the detectives would discover pockets in their hats
/datum/component/storage/concrete/pockets/shoes
attack_hand_interact = FALSE
quickdraw = TRUE
silent = TRUE
/datum/component/storage/concrete/pockets/shoes/Initialize()
. = ..()
cant_hold = typecacheof(list(/obj/item/screwdriver/power))
can_hold = typecacheof(list(
/obj/item/kitchen/knife, /obj/item/switchblade, /obj/item/pen, /obj/item/melee/cultblade/dagger,
/obj/item/scalpel, /obj/item/reagent_containers/syringe, /obj/item/dnainjector,
/obj/item/reagent_containers/hypospray/medipen, /obj/item/reagent_containers/dropper,
/obj/item/implanter, /obj/item/screwdriver, /obj/item/weldingtool/mini,
/obj/item/firing_pin, /obj/item/gun/ballistic/automatic/pistol
))
/datum/component/storage/concrete/pockets/shoes/clown/Initialize()
. = ..()
cant_hold = typecacheof(list(/obj/item/screwdriver/power))
can_hold = typecacheof(list(
/obj/item/kitchen/knife, /obj/item/switchblade, /obj/item/pen, /obj/item/melee/cultblade/dagger,
/obj/item/scalpel, /obj/item/reagent_containers/syringe, /obj/item/dnainjector,
/obj/item/reagent_containers/hypospray/medipen, /obj/item/reagent_containers/dropper,
/obj/item/implanter, /obj/item/screwdriver, /obj/item/weldingtool/mini,
/obj/item/firing_pin, /obj/item/bikehorn, /obj/item/gun/ballistic/automatic/pistol))
/datum/component/storage/concrete/pockets/pocketprotector
max_items = 3
max_w_class = WEIGHT_CLASS_TINY
var/atom/original_parent
/datum/component/storage/concrete/pockets/pocketprotector/Initialize()
original_parent = parent
. = ..()
can_hold = typecacheof(list( //Same items as a PDA
/obj/item/pen,
/obj/item/toy/crayon,
/obj/item/lipstick,
/obj/item/flashlight/pen,
/obj/item/clothing/mask/cigarette))
/datum/component/storage/concrete/pockets/pocketprotector/real_location()
// if the component is reparented to a jumpsuit, the items still go in the protector
return original_parent
@@ -0,0 +1,5 @@
/datum/component/storage/concrete/secret_satchel/can_be_inserted(obj/item/I, stop_messages = FALSE, mob/M)
if(SSpersistence.spawned_objects[I])
to_chat(M, "<span class='warning'>[I] is unstable after its journey through space and time, it wouldn't survive another trip.</span>")
return FALSE
return ..()
@@ -309,7 +309,6 @@
else
var/datum/numbered_display/ND = .[I.type]
ND.number++
. = sortTim(., /proc/cmp_numbered_displays_name_asc, associative = TRUE)
//This proc determines the size of the inventory to be displayed. Please touch it only if you know what you're doing.
/datum/component/storage/proc/orient2hud(mob/user, maxcolumns)
+44
View File
@@ -0,0 +1,44 @@
/datum/component/swarming
var/offset_x = 0
var/offset_y = 0
var/is_swarming = FALSE
var/list/swarm_members = list()
/datum/component/swarming/Initialize(max_x = 24, max_y = 24)
offset_x = rand(-max_x, max_x)
offset_y = rand(-max_y, max_y)
RegisterSignal(parent, COMSIG_MOVABLE_CROSSED, .proc/join_swarm)
RegisterSignal(parent, COMSIG_MOVABLE_UNCROSSED, .proc/leave_swarm)
/datum/component/swarming/proc/join_swarm(datum/source, atom/movable/AM)
var/datum/component/swarming/other_swarm = AM.GetComponent(/datum/component/swarming)
if(!other_swarm)
return
swarm()
swarm_members |= other_swarm
other_swarm.swarm()
other_swarm.swarm_members |= src
/datum/component/swarming/proc/leave_swarm(datum/source, atom/movable/AM)
var/datum/component/swarming/other_swarm = AM.GetComponent(/datum/component/swarming)
if(!other_swarm || !(other_swarm in swarm_members))
return
swarm_members -= other_swarm
if(!swarm_members.len)
unswarm()
other_swarm.swarm_members -= src
if(!other_swarm.swarm_members.len)
other_swarm.unswarm()
/datum/component/swarming/proc/swarm()
var/atom/movable/owner = parent
if(!is_swarming)
is_swarming = TRUE
animate(owner, pixel_x = owner.pixel_x + offset_x, pixel_y = owner.pixel_y + offset_y, time = 2)
/datum/component/swarming/proc/unswarm()
var/atom/movable/owner = parent
if(is_swarming)
animate(owner, pixel_x = owner.pixel_x - offset_x, pixel_y = owner.pixel_y - offset_y, time = 2)
is_swarming = FALSE
+81
View File
@@ -0,0 +1,81 @@
/datum/component/thermite
dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
var/amount
var/overlay
var/static/list/blacklist = typecacheof(list(
/turf/open/lava,
/turf/open/space,
/turf/open/water,
/turf/open/chasm)
)
var/static/list/immunelist = typecacheof(list(
/turf/closed/wall/mineral/diamond,
/turf/closed/indestructible,
/turf/open/indestructible)
)
var/static/list/resistlist = typecacheof(
/turf/closed/wall/r_wall
)
/datum/component/thermite/Initialize(_amount)
if(!istype(parent, /turf) || blacklist[parent.type])
return COMPONENT_INCOMPATIBLE
if(immunelist[parent.type])
_amount*=0 //Yeah the overlay can still go on it and be cleaned but you arent burning down a diamond wall
if(resistlist[parent.type])
_amount*=0.25
amount = _amount*10
var/turf/master = parent
overlay = mutable_appearance('icons/effects/effects.dmi', "thermite")
master.add_overlay(overlay)
RegisterSignal(parent, COMSIG_COMPONENT_CLEAN_ACT, .proc/clean_react)
RegisterSignal(parent, COMSIG_PARENT_ATTACKBY, .proc/attackby_react)
RegisterSignal(parent, COMSIG_ATOM_FIRE_ACT, .proc/flame_react)
/datum/component/thermite/Destroy()
var/turf/master = parent
master.cut_overlay(overlay)
return ..()
/datum/component/thermite/InheritComponent(datum/component/thermite/newC, i_am_original, list/arguments)
if(!i_am_original)
return
if(newC)
amount += newC.amount
else
amount += arguments[1]
/datum/component/thermite/proc/thermite_melt(mob/user)
var/turf/master = parent
master.cut_overlay(overlay)
var/obj/effect/overlay/thermite/fakefire = new(master)
playsound(master, 'sound/items/welder.ogg', 100, 1)
if(amount >= 50)
var/burning_time = max(100, 100-amount)
master = master.Melt()
master.burn_tile()
if(user)
master.add_hiddenprint(user)
QDEL_IN(fakefire, burning_time)
else
QDEL_IN(fakefire, 50)
/datum/component/thermite/proc/clean_react(datum/source, strength)
//Thermite is just some loose powder, you could probably clean it with your hands. << todo?
qdel(src)
/datum/component/thermite/proc/flame_react(datum/source, exposed_temperature, exposed_volume)
if(exposed_temperature > 1922) // This is roughly the real life requirement to ignite thermite
thermite_melt()
/datum/component/thermite/proc/attackby_react(datum/source, obj/item/thing, mob/user, params)
if(thing.get_temperature())
thermite_melt(user)
+128
View File
@@ -0,0 +1,128 @@
/datum/component/virtual_reality
can_transfer = TRUE
var/datum/mind/mastermind // where is my mind t. pixies
var/datum/mind/current_mind
var/obj/machinery/vr_sleeper/vr_sleeper
var/datum/action/quit_vr/quit_action
var/you_die_in_the_game_you_die_for_real = FALSE
var/datum/component/virtual_reality/inception //The component works on a very fragile link betwixt mind, ckey and death.
/datum/component/virtual_reality/Initialize(mob/M, obj/machinery/vr_sleeper/gaming_pod, yolo = FALSE, new_char = TRUE)
if(!ismob(parent) || !istype(M))
return COMPONENT_INCOMPATIBLE
var/mob/vr_M = parent
mastermind = M.mind
RegisterSignal(M, list(COMSIG_MOB_DEATH, COMSIG_PARENT_QDELETED), .proc/game_over)
RegisterSignal(M, COMSIG_MOB_KEY_CHANGE, .proc/switch_player)
RegisterSignal(mastermind, COMSIG_MIND_TRANSFER, .proc/switch_player)
you_die_in_the_game_you_die_for_real = yolo
quit_action = new()
if(gaming_pod)
vr_sleeper = gaming_pod
RegisterSignal(vr_sleeper, COMSIG_ATOM_EMAG_ACT, .proc/you_only_live_once)
RegisterSignal(vr_sleeper, COMSIG_MACHINE_EJECT_OCCUPANT, .proc/revert_to_reality)
vr_M.ckey = M.ckey
var/datum/component/virtual_reality/clusterfk = M.GetComponent(/datum/component/virtual_reality)
if(clusterfk && !clusterfk.inception)
clusterfk.inception = src
SStgui.close_user_uis(M, src)
/datum/component/virtual_reality/RegisterWithParent()
var/mob/M = parent
current_mind = M.mind
quit_action.Grant(M)
RegisterSignal(quit_action, COMSIG_ACTION_TRIGGER, .proc/revert_to_reality)
RegisterSignal(M, list(COMSIG_MOB_DEATH, COMSIG_PARENT_QDELETED), .proc/game_over)
RegisterSignal(M, COMSIG_MOB_GHOSTIZE, .proc/be_a_quitter)
RegisterSignal(M, COMSIG_MOB_KEY_CHANGE, .proc/pass_me_the_remote)
RegisterSignal(current_mind, COMSIG_MIND_TRANSFER, .proc/pass_me_the_remote)
mastermind.current.audiovisual_redirect = M
if(vr_sleeper)
vr_sleeper.vr_mob = M
/datum/component/virtual_reality/UnregisterFromParent()
quit_action.Remove(parent)
UnregisterSignal(parent, list(COMSIG_MOB_DEATH, COMSIG_PARENT_QDELETED, COMSIG_MOB_KEY_CHANGE, COMSIG_MOB_GHOSTIZE))
UnregisterSignal(current_mind, COMSIG_MIND_TRANSFER)
UnregisterSignal(quit_action, COMSIG_ACTION_TRIGGER)
current_mind = null
mastermind.current.audiovisual_redirect = null
/datum/component/virtual_reality/proc/switch_player(datum/source, mob/new_mob, mob/old_mob)
if(vr_sleeper || !new_mob.mind)
// Machineries currently don't deal up with the occupant being polymorphed et similar... Or did something fuck up?
revert_to_reality()
return
old_mob.audiovisual_redirect = null
new_mob.audiovisual_redirect = parent
/datum/component/virtual_reality/proc/action_trigger(datum/signal_source, datum/action/source)
if(source != quit_action)
return COMPONENT_ACTION_BLOCK_TRIGGER
revert_to_reality(signal_source)
/datum/component/virtual_reality/proc/you_only_live_once()
if(you_die_in_the_game_you_die_for_real || vr_sleeper?.only_current_user_can_interact)
return FALSE
you_die_in_the_game_you_die_for_real = TRUE
return TRUE
/datum/component/virtual_reality/proc/pass_me_the_remote(datum/source, mob/new_mob)
if(new_mob == mastermind.current)
revert_to_reality(source)
return TRUE
new_mob.TakeComponent(src)
return TRUE
/datum/component/virtual_reality/PostTransfer()
if(!ismob(parent))
return COMPONENT_INCOMPATIBLE
/datum/component/virtual_reality/proc/revert_to_reality(datum/source)
quit_it()
/datum/component/virtual_reality/proc/game_over(datum/source)
quit_it(TRUE, TRUE)
/datum/component/virtual_reality/proc/be_a_quitter(datum/source, can_reenter_corpse)
quit_it()
return COMPONENT_BLOCK_GHOSTING
/datum/component/virtual_reality/proc/virtual_reality_in_a_virtual_reality(mob/player, killme = FALSE, datum/component/virtual_reality/yo_dawg)
var/mob/M = parent
quit_it(FALSE, killme, player, yo_dawg)
yo_dawg.inception = null
if(killme)
M.death(FALSE)
/datum/component/virtual_reality/proc/quit_it(deathcheck = FALSE, cleanup = FALSE, mob/override)
var/mob/M = parent
var/mob/dreamer = override ? override : mastermind.current
if(!mastermind)
to_chat(M, "<span class='warning'>You feel a dreadful sensation, something terrible happened. You try to wake up, but you find yourself unable to...</span>")
else
var/key_transfer = FALSE
if(inception?.parent)
inception.virtual_reality_in_a_virtual_reality(dreamer, cleanup, src)
else
key_transfer = TRUE
if(key_transfer)
M.transfer_ckey(dreamer, FALSE)
dreamer.stop_sound_channel(CHANNEL_HEARTBEAT)
dreamer.audiovisual_redirect = null
if(deathcheck && you_die_in_the_game_you_die_for_real)
to_chat(mastermind, "<span class='warning'>You feel everything fading away...</span>")
dreamer.death(FALSE)
if(cleanup)
var/obj/effect/vr_clean_master/cleanbot = locate() in get_area(M)
if(cleanbot)
LAZYADD(cleanbot.corpse_party, M)
if(vr_sleeper)
vr_sleeper.vr_mob = null
vr_sleeper = null
qdel(src)
/datum/component/virtual_reality/Destroy()
var/datum/action/quit_vr/delet_me = quit_action
. = ..()
qdel(delet_me)
+15
View File
@@ -0,0 +1,15 @@
/datum/component/waddling
dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
/datum/component/waddling/Initialize()
if(!isliving(parent))
return COMPONENT_INCOMPATIBLE
RegisterSignal(parent, list(COMSIG_MOVABLE_MOVED), .proc/Waddle)
/datum/component/waddling/proc/Waddle()
var/mob/living/L = parent
if(L.incapacitated() || L.lying)
return
animate(L, pixel_z = 4, time = 0)
animate(pixel_z = 0, transform = turn(matrix(), pick(-12, 0, 12)), time=2)
animate(pixel_z = 0, transform = matrix(), time = 0)
+22
View File
@@ -0,0 +1,22 @@
// A dummy parent type used for easily making components that target an item's wearer rather than the item itself.
/datum/component/wearertargeting
var/list/valid_slots = list()
var/list/signals = list()
var/proctype = .proc/pass
var/mobtype = /mob/living
/datum/component/wearertargeting/Initialize()
if(!isitem(parent))
return COMPONENT_INCOMPATIBLE
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, .proc/on_equip)
RegisterSignal(parent, COMSIG_ITEM_DROPPED, .proc/on_drop)
/datum/component/wearertargeting/proc/on_equip(datum/source, mob/equipper, slot)
if((slot in valid_slots) && istype(equipper, mobtype))
RegisterSignal(equipper, signals, proctype, TRUE)
else
UnregisterSignal(equipper, signals)
/datum/component/wearertargeting/proc/on_drop(datum/source, mob/user)
UnregisterSignal(user, signals)
+209
View File
@@ -0,0 +1,209 @@
/datum/component/wet_floor
dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
can_transfer = TRUE
var/highest_strength = TURF_DRY
var/lube_flags = NONE //why do we have this?
var/list/time_left_list //In deciseconds.
var/static/mutable_appearance/permafrost_overlay = mutable_appearance('icons/effects/water.dmi', "ice_floor")
var/static/mutable_appearance/ice_overlay = mutable_appearance('icons/turf/overlays.dmi', "snowfloor")
var/static/mutable_appearance/water_overlay = mutable_appearance('icons/effects/water.dmi', "wet_floor_static")
var/static/mutable_appearance/generic_turf_overlay = mutable_appearance('icons/effects/water.dmi', "wet_static")
var/current_overlay
var/permanent = FALSE
var/last_process = 0
/datum/component/wet_floor/InheritComponent(datum/newcomp, orig, argslist)
if(!newcomp) //We are getting passed the arguments of a would-be new component, but not a new component
add_wet(arglist(argslist))
else //We are being passed in a full blown component
var/datum/component/wet_floor/WF = newcomp //Lets make an assumption
if(WF.gc()) //See if it's even valid, still. Also does LAZYLEN and stuff for us.
CRASH("Wet floor component tried to inherit another, but the other was able to garbage collect while being inherited! What a waste of time!")
return
for(var/i in WF.time_left_list)
add_wet(text2num(i), WF.time_left_list[i])
/datum/component/wet_floor/Initialize(strength, duration_minimum, duration_add, duration_maximum, _permanent = FALSE)
if(!isopenturf(parent))
return COMPONENT_INCOMPATIBLE
add_wet(strength, duration_minimum, duration_add, duration_maximum)
permanent = _permanent
if(!permanent)
START_PROCESSING(SSwet_floors, src)
addtimer(CALLBACK(src, .proc/gc, TRUE), 1) //GC after initialization.
last_process = world.time
/datum/component/wet_floor/RegisterWithParent()
RegisterSignal(parent, COMSIG_TURF_IS_WET, .proc/is_wet)
RegisterSignal(parent, COMSIG_TURF_MAKE_DRY, .proc/dry)
/datum/component/wet_floor/UnregisterFromParent()
UnregisterSignal(parent, list(COMSIG_TURF_IS_WET, COMSIG_TURF_MAKE_DRY))
/datum/component/wet_floor/Destroy()
STOP_PROCESSING(SSwet_floors, src)
var/turf/T = parent
qdel(T.GetComponent(/datum/component/slippery))
if(istype(T)) //If this is false there is so many things wrong with it.
T.cut_overlay(current_overlay)
else
stack_trace("Warning: Wet floor component wasn't on a turf when being destroyed! This is really bad!")
return ..()
/datum/component/wet_floor/proc/update_overlay()
var/intended
if(!istype(parent, /turf/open/floor))
intended = generic_turf_overlay
else
switch(highest_strength)
if(TURF_WET_PERMAFROST)
intended = permafrost_overlay
if(TURF_WET_ICE)
intended = ice_overlay
else
intended = water_overlay
if(current_overlay != intended)
var/turf/T = parent
T.cut_overlay(current_overlay)
T.add_overlay(intended)
current_overlay = intended
/datum/component/wet_floor/proc/AfterSlip(mob/living/L)
if(highest_strength == TURF_WET_LUBE)
L.confused = max(L.confused, 8)
/datum/component/wet_floor/proc/update_flags()
var/intensity
lube_flags = NONE
switch(highest_strength)
if(TURF_WET_WATER)
intensity = 60
lube_flags = NO_SLIP_WHEN_WALKING
if(TURF_WET_LUBE)
intensity = 80
lube_flags = SLIDE | GALOSHES_DONT_HELP
if(TURF_WET_ICE)
intensity = 120
lube_flags = SLIDE | GALOSHES_DONT_HELP
if(TURF_WET_PERMAFROST)
intensity = 120
lube_flags = SLIDE_ICE | GALOSHES_DONT_HELP
if(TURF_WET_SUPERLUBE)
intensity = 120
lube_flags = SLIDE | GALOSHES_DONT_HELP | SLIP_WHEN_CRAWLING
else
qdel(parent.GetComponent(/datum/component/slippery))
return
var/datum/component/slippery/S = parent.LoadComponent(/datum/component/slippery, NONE, CALLBACK(src, .proc/AfterSlip))
S.intensity = intensity
S.lube_flags = lube_flags
/datum/component/wet_floor/proc/dry(datum/source, strength = TURF_WET_WATER, immediate = FALSE, duration_decrease = INFINITY)
for(var/i in time_left_list)
if(text2num(i) <= strength)
time_left_list[i] = max(0, time_left_list[i] - duration_decrease)
if(immediate)
check()
/datum/component/wet_floor/proc/max_time_left()
. = 0
for(var/i in time_left_list)
. = max(., time_left_list[i])
/datum/component/wet_floor/process()
var/turf/open/T = parent
var/diff = world.time - last_process
var/decrease = 0
var/t = T.GetTemperature()
switch(t)
if(-INFINITY to T0C)
add_wet(TURF_WET_ICE, max_time_left()) //Water freezes into ice!
if(T0C to T0C + 100)
decrease = ((T.air.temperature - T0C) / SSwet_floors.temperature_coeff) * (diff / SSwet_floors.time_ratio)
if(T0C + 100 to INFINITY)
decrease = INFINITY
decrease = max(0, decrease)
if((is_wet() & TURF_WET_ICE) && t > T0C) //Ice melts into water!
for(var/obj/O in T.contents)
if(O.obj_flags & FROZEN)
O.make_unfrozen()
add_wet(TURF_WET_WATER, max_time_left())
dry(null, TURF_WET_ICE)
dry(null, ALL, FALSE, decrease)
check()
last_process = world.time
/datum/component/wet_floor/proc/update_strength()
highest_strength = 0 //Not bitflag.
for(var/i in time_left_list)
highest_strength = max(highest_strength, text2num(i))
/datum/component/wet_floor/proc/is_wet()
. = 0
for(var/i in time_left_list)
. |= text2num(i)
/datum/component/wet_floor/PreTransfer()
var/turf/O = parent
O.cut_overlay(current_overlay)
//That turf is no longer slippery, we're out of here
//Slippery components don't transfer due to callbacks
qdel(O.GetComponent(/datum/component/slippery))
/datum/component/wet_floor/PostTransfer()
if(!isopenturf(parent))
return COMPONENT_INCOMPATIBLE
var/turf/T = parent
T.add_overlay(current_overlay)
//Make sure to add/update any slippery component on the new turf (update_flags calls LoadComponent)
update_flags()
//NB it's possible we get deleted after this, due to inherit
/datum/component/wet_floor/proc/add_wet(type, duration_minimum = 0, duration_add = 0, duration_maximum = MAXIMUM_WET_TIME, _permanent = FALSE)
var/static/list/allowed_types = list(TURF_WET_WATER, TURF_WET_LUBE, TURF_WET_ICE, TURF_WET_PERMAFROST, TURF_WET_SUPERLUBE)
if(duration_minimum <= 0 || !type)
return FALSE
if(type in allowed_types)
return _do_add_wet(type, duration_minimum, duration_add, duration_maximum)
else
. = NONE
for(var/i in allowed_types)
if(!(type & i))
continue
. |= _do_add_wet(i, duration_minimum, duration_add, duration_maximum)
if(_permanent)
permanent = TRUE
STOP_PROCESSING(SSwet_floors, src)
/datum/component/wet_floor/proc/_do_add_wet(type, duration_minimum, duration_add, duration_maximum)
var/time = 0
if(LAZYACCESS(time_left_list, "[type]"))
time = CLAMP(LAZYACCESS(time_left_list, "[type]") + duration_add, duration_minimum, duration_maximum)
else
time = min(duration_minimum, duration_maximum)
LAZYSET(time_left_list, "[type]", time)
check(TRUE)
return TRUE
/datum/component/wet_floor/proc/gc(on_init = FALSE)
if(!LAZYLEN(time_left_list))
if(on_init)
var/turf/T = parent
stack_trace("Warning: Wet floor component gc'd right after initialization! What a waste of time and CPU! Type = [T? T.type : "ERROR - NO PARENT"], Location = [istype(T)? AREACOORD(T) : "ERROR - INVALID PARENT"].")
qdel(src)
return TRUE
return FALSE
/datum/component/wet_floor/proc/check(force_update = FALSE)
var/changed = FALSE
for(var/i in time_left_list)
if(time_left_list[i] <= 0)
time_left_list -= i
changed = TRUE
if(changed || force_update)
update_strength()
update_overlay()
update_flags()
gc()