* Explosive implant

* Package wrapper

* Venus human trap sprites

* Vine damage

* Wisp vision

* Display case

* Storage viewing

* Ghost poll nullspacing

* Inflatable barrier piercing

* Pneumatic cannon

* Cow grammar

* Centcom jaunting

* Consistency

Not actually a lazy list, as far as I can tell

* Review 1

Co-Authored-By: SteelSlayer <42044220+SteelSlayer@users.noreply.github.com>

* Review 2

Co-Authored-By: SteelSlayer <42044220+SteelSlayer@users.noreply.github.com>

* length()

Co-authored-by: SteelSlayer <42044220+SteelSlayer@users.noreply.github.com>
This commit is contained in:
SabreML
2021-08-05 11:04:27 +01:00
committed by GitHub
parent e75d8357eb
commit 02fe0876f5
28 changed files with 152 additions and 95 deletions

View File

@@ -680,6 +680,7 @@ proc/dd_sortedObjectList(list/incoming)
#define UNSETEMPTY(L) if (L && !L.len) L = null
#define LAZYREMOVE(L, I) if(L) { L -= I; if(!L.len) { L = null; } }
#define LAZYADD(L, I) if(!L) { L = list(); } L += I;
#define LAZYADDOR(L, I) if(!L) { L = list(); } L |= I;
#define LAZYACCESS(L, I) (L ? (isnum(I) ? (I > 0 && I <= L.len ? L[I] : null) : L[I]) : null)
#define LAZYLEN(L) length(L) // Despite how pointless this looks, it's still needed in order to convey that the list is specificially a 'Lazy' list.
#define LAZYCLEARLIST(L) if(L) L.Cut()

View File

@@ -1,5 +1,5 @@
/obj/item/proc/melee_attack_chain(mob/user, atom/target, params)
if(!tool_attack_chain(user, target) && pre_attackby(target, user, params))
if(!tool_attack_chain(user, target) && pre_attack(target, user, params))
// Return 1 in attackby() to prevent afterattack() effects (when safely moving items for example)
var/resolved = target.attackby(src, user, params)
if(!resolved && target && !QDELETED(src))
@@ -18,7 +18,7 @@
return
return
/obj/item/proc/pre_attackby(atom/A, mob/living/user, params) //do stuff before attackby!
/obj/item/proc/pre_attack(atom/A, mob/living/user, params) //do stuff before attackby!
if(is_hot(src) && A.reagents && !ismob(A))
to_chat(user, "<span class='notice'>You heat [A] with [src].</span>")
A.reagents.temperature_reagents(is_hot(src))

View File

@@ -560,7 +560,8 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell))
to_chat(user, "<span class='warning'>You shouldn't have this spell! Something's wrong.</span>")
return 0
if(is_admin_level(user.z) && !centcom_cancast) //Certain spells are not allowed on the centcom zlevel
var/turf/T = get_turf(user)
if(is_admin_level(T.z) && !centcom_cancast) //Certain spells are not allowed on the centcom zlevel
return 0
if(!holy_area_cancast && user.holy_check())

View File

@@ -238,7 +238,7 @@
blobber.LoseTarget()
spawn()
var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a blobbernaut?", ROLE_BLOB, TRUE, 10 SECONDS, source = blobber)
if(candidates.len)
if(length(candidates) && !QDELETED(blobber))
var/mob/C = pick(candidates)
if(C)
blobber.key = C.key

View File

@@ -607,7 +607,7 @@ structure_check() searches for nearby cultist structures required for the invoca
set waitfor = FALSE
to_chat(user, "<span class='cult'>[mob_to_revive] was revived, but their mind is lost! Seeking a lost soul to replace it.</span>")
var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Would you like to play as a revived Cultist?", ROLE_CULTIST, TRUE, poll_time = 20 SECONDS, source = /obj/item/melee/cultblade/dagger)
if(length(candidates))
if(length(candidates) && !QDELETED(mob_to_revive))
var/mob/dead/observer/C = pick(candidates)
to_chat(mob_to_revive.mind, "<span class='biggerdanger'>Your physical form has been taken over by another soul due to your inactivity! Ahelp if you wish to regain your form.</span>")
message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(mob_to_revive)]) to replace an AFK player.")

View File

@@ -90,6 +90,8 @@
if(!length(candidates))
to_chat(owner, "<span class='danger'>There were no ghosts willing to take control of your guardian. You can try again in 5 minutes.</span>")
return
if(QDELETED(guardian)) // Just in case
return
var/mob/dead/observer/new_stand = pick(candidates)
to_chat(guardian, "<span class='danger'>Your user reset you, and your body was taken over by a ghost. Looks like they weren't happy with your performance.</span>")

View File

@@ -168,6 +168,8 @@
visible_message("<span class='warning'>[src] disappears in a flash of red light!</span>")
qdel(src)
return
if(QDELETED(src)) // Just in case
return
var/mob/M = pick(demon_candidates)
var/mob/living/simple_animal/slaughter/cult/S = src
if(!M || !M.client)

View File

@@ -107,10 +107,10 @@
if(R.mind && !R.client && !R.grab_ghost()) // Make sure this is an actual player first and not just a humanized monkey or something.
message_admins("[key_name_admin(R)] was just transformed by a borg factory, but they were SSD. Polling ghosts for a replacement.")
var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a malfunctioning cyborg?", ROLE_TRAITOR, poll_time = 15 SECONDS)
if(!length(candidates))
if(!length(candidates) || QDELETED(R))
return
var/mob/dead/observer/O = pick(candidates)
R.key= O.key
R.key = O.key
/obj/machinery/transformer/mime
name = "Mimetech Greyscaler"

View File

@@ -287,7 +287,7 @@
A.Grant(S)
var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as a pyroclastic anomaly slime?", ROLE_SENTIENT, FALSE, 100, source = S, role_cleanname = "pyroclastic anomaly slime")
if(LAZYLEN(candidates))
if(length(candidates) && !QDELETED(S))
var/mob/dead/observer/chosen = pick(candidates)
S.key = chosen.key
S.mind.special_role = SPECIAL_ROLE_PYROCLASTIC_SLIME

View File

@@ -178,7 +178,7 @@
spawn()
var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a giant spider?", ROLE_GSPIDER, TRUE, source = S)
if(candidates.len)
if(length(candidates) && !QDELETED(S))
var/mob/C = pick(candidates)
if(C)
S.key = C.key

View File

@@ -4,6 +4,7 @@
icon_state = "explosive"
origin_tech = "materials=2;combat=3;biotech=4;syndicate=4"
actions_types = list(/datum/action/item_action/hands_free/activate/always)
var/detonating = FALSE
var/weak = 2
var/medium = 0.8
var/heavy = 0.4
@@ -26,16 +27,20 @@
activate("death")
/obj/item/implant/explosive/activate(cause)
if(!cause || !imp_in) return 0
if(!cause || !imp_in)
return FALSE
if(cause == "action_button" && alert(imp_in, "Are you sure you want to activate your microbomb implant? This will cause you to explode!", "Microbomb Implant Confirmation", "Yes", "No") != "Yes")
return 0
return FALSE
if(detonating)
return FALSE
heavy = round(heavy)
medium = round(medium)
weak = round(weak)
to_chat(imp_in, "<span class='notice'>You activate your microbomb implant.</span>")
detonating = TRUE
to_chat(imp_in, "<span class='danger'>You activate your microbomb implant.</span>")
//If the delay is short, just blow up already jeez
if(delay <= 7)
explosion(src,heavy,medium,weak,weak, flame_range = weak)
explosion(src, heavy, medium, weak, weak, flame_range = weak)
if(imp_in)
imp_in.gib()
qdel(src)

View File

@@ -61,6 +61,9 @@
return
if(istype(W, /obj/item))
var/obj/item/IW = W
if(IW.flags & (ABSTRACT | NODROP | DROPDEL))
to_chat(user, "<span class='warning'>You can't put [IW] into [src]!</span>")
return
if((loadedWeightClass + IW.w_class) > maxWeightClass)
to_chat(user, "<span class='warning'>\The [IW] won't fit into \the [src]!</span>")
return

View File

@@ -12,9 +12,9 @@
/// No message on putting items in.
var/silent = FALSE
/// List of objects which this item can store (if set, it can't store anything else)
var/list/can_hold = new/list()
var/list/can_hold = list()
/// List of objects which this item can't store (in effect only if can_hold isn't set)
var/list/cant_hold = new/list()
var/list/cant_hold = list()
/// Max size of objects that this object can store (in effect only if can_hold isn't set)
var/max_w_class = WEIGHT_CLASS_SMALL
/// The sum of the w_classes of all the items in this storage item.
@@ -42,6 +42,9 @@
/// How much of the stack item do you get.
var/foldable_amt = 0
/// Lazy list of mobs which are currently viewing the storage inventory.
var/list/mobs_viewing
/obj/item/storage/Initialize(mapload)
. = ..()
can_hold = typecacheof(can_hold)
@@ -73,6 +76,15 @@
closer.plane = ABOVE_HUD_PLANE
orient2hud()
/obj/item/storage/Destroy()
for(var/obj/O in contents)
O.mouse_opacity = initial(O.mouse_opacity)
QDEL_NULL(boxes)
QDEL_NULL(closer)
LAZYCLEARLIST(mobs_viewing)
return ..()
/obj/item/storage/MouseDrop(obj/over_object)
if(!ismob(usr)) //so monkeys can take off their backpacks -- Urist
return
@@ -177,11 +189,13 @@
user.client.screen += closer
user.client.screen += contents
user.s_active = src
LAZYADDOR(mobs_viewing, user)
/**
* Hides the current container interface from `user`.
*/
/obj/item/storage/proc/hide_from(mob/user)
LAZYREMOVE(mobs_viewing, user) // Remove clientless mobs too
if(!user.client)
return
user.client.screen -= boxes
@@ -190,6 +204,16 @@
if(user.s_active == src)
user.s_active = null
/**
* Checks all mobs currently viewing the storage inventory, and hides it if they shouldn't be able to see it.
*/
/obj/item/storage/proc/update_viewers()
for(var/_M in mobs_viewing)
var/mob/M = _M
if(!QDELETED(M) && M.s_active == src && (M in range(1, loc)))
continue
hide_from(M)
/obj/item/storage/proc/open(mob/user)
if(use_sound)
playsound(loc, use_sound, 50, TRUE, -5)
@@ -374,6 +398,12 @@
prevent_warning = TRUE
I.forceMove(src)
I.on_enter_storage(src)
for(var/_M in mobs_viewing)
var/mob/M = _M
if((M.s_active == src) && M.client)
M.client.screen += I
if(usr)
if(usr.client && usr.s_active != src)
usr.client.screen -= I
@@ -412,7 +442,8 @@
var/obj/item/storage/fancy/F = src
F.update_icon(TRUE)
for(var/mob/M in range(1, loc))
for(var/_M in mobs_viewing)
var/mob/M = _M
if((M.s_active == src) && M.client)
M.client.screen -= I
@@ -498,6 +529,10 @@
close(M)
add_fingerprint(user)
/obj/item/storage/equipped(mob/user, slot, initial)
. = ..()
update_viewers()
/obj/item/storage/attack_ghost(mob/user)
if(isobserver(user))
// Revenants don't get to play with the toys.
@@ -539,14 +574,6 @@
/obj/item/storage/proc/populate_contents()
return // Override
/obj/item/storage/Destroy()
for(var/obj/O in contents)
O.mouse_opacity = initial(O.mouse_opacity)
QDEL_NULL(boxes)
QDEL_NULL(closer)
return ..()
/obj/item/storage/emp_act(severity)
..()
for(var/I in contents)

View File

@@ -41,6 +41,7 @@
trigger_alarm()
emagged = TRUE
toggle_lock()
/obj/structure/displaycase/examine(mob/user)
. = ..()
@@ -110,12 +111,12 @@
/obj/structure/displaycase/attackby(obj/item/I, mob/user, params)
if(I.GetID() && !broken && openable)
if(allowed(user) || emagged)
to_chat(user, "<span class='notice'>You [open ? "close":"open"] [src].</span>")
toggle_lock(user)
to_chat(user, "<span class='notice'>You [open ? "close":"open"] [src].</span>")
toggle_lock()
else
to_chat(user, "<span class='warning'>Access denied.</span>")
to_chat(user, "<span class='warning'>Access denied.</span>")
else if(open && !showpiece)
if(user.drop_item())
if(!(I.flags & (ABSTRACT | DROPDEL)) && user.drop_item())
I.forceMove(src)
showpiece = I
to_chat(user, "<span class='notice'>You put [I] on display</span>")
@@ -151,14 +152,14 @@
if(!I.use_tool(src, user, 20, volume = I.tool_volume))
return
to_chat(user, "<span class='notice'>You [open ? "close":"open"] [src].</span>")
toggle_lock(user)
toggle_lock()
/obj/structure/displaycase/welder_act(mob/user, obj/item/I)
. = TRUE
if(default_welder_repair(user, I))
broken = FALSE
/obj/structure/displaycase/proc/toggle_lock(mob/user)
/obj/structure/displaycase/proc/toggle_lock()
open = !open
update_icon()

View File

@@ -46,9 +46,16 @@
/obj/structure/inflatable/CanAtmosPass(turf/T)
return !density
/obj/structure/inflatable/attack_hand(mob/user as mob)
/obj/structure/inflatable/attack_hand(mob/user)
add_fingerprint(user)
/obj/structure/inflatable/attackby(obj/item/I, mob/living/user, params)
if(I.sharp || is_type_in_typecache(I, GLOB.pointed_types))
user.do_attack_animation(src, used_item = I)
deconstruct(FALSE)
return FALSE
return ..()
/obj/structure/inflatable/AltClick()
if(usr.stat || usr.restrained())
return

View File

@@ -74,7 +74,7 @@ GLOBAL_LIST_EMPTY(antagonists)
set waitfor = FALSE
var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as a [name]?", job_rank, TRUE, 5 SECONDS)
if(LAZYLEN(candidates))
if(length(candidates))
var/mob/dead/observer/C = pick(candidates)
to_chat(owner, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!")
message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(owner.current)]) to replace a jobbaned player.")

View File

@@ -48,7 +48,7 @@
to_chat(user, "<span class='notice'>You activate [src] and wait for confirmation.</span>")
var/image/I = new('icons/mob/simple_human.dmi', "syndicate_space_sword")
var/list/nuke_candidates = SSghost_spawns.poll_candidates("Do you want to play as a [rolename]?", ROLE_OPERATIVE, TRUE, 15 SECONDS, source = I)
if(LAZYLEN(nuke_candidates))
if(length(nuke_candidates))
checking = FALSE
if(QDELETED(src) || !check_usability(user))
return

View File

@@ -194,7 +194,7 @@
servant_mind.transfer_to(H)
var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as the servant of [user.real_name]?", ROLE_WIZARD, poll_time = 30 SECONDS, source = H)
if(LAZYLEN(candidates))
if(length(candidates) && !QDELETED(H))
var/mob/dead/observer/C = pick(candidates)
message_admins("[ADMIN_LOOKUPFLW(C)] was spawned as Dice Servant")
H.key = C.key

View File

@@ -274,7 +274,7 @@
// Bust through windows or other stuff blocking the way
if(!target.Enter(holder))
for(var/atom/movable/AM in target)
if(istype(AM, /obj/structure/spacevine) || !AM.density)
if(!AM.density || isvineimmune(AM))
continue
AM.ex_act(severity)
target.ex_act(severity) // vine immunity handled at /mob/ex_act
@@ -697,8 +697,10 @@
. = ..()
/proc/isvineimmune(atom/A)
. = FALSE
if(isliving(A))
var/mob/living/M = A
if(("vines" in M.faction) || ("plants" in M.faction))
. = TRUE
return TRUE
else if(istype(A, /obj/structure/spacevine) || istype(A, /obj/structure/alien/resin/flower_bud_enemy))
return TRUE
return FALSE

View File

@@ -139,7 +139,7 @@
playsound(loc, pick('sound/misc/desceration-01.ogg','sound/misc/desceration-02.ogg','sound/misc/desceration-01.ogg'), 50, 1, -1)
return BRUTELOSS
/obj/item/scythe/pre_attackby(atom/A, mob/living/user, params)
/obj/item/scythe/pre_attack(atom/A, mob/living/user, params)
if(swiping || !istype(A, /obj/structure/spacevine) || get_turf(A) == get_turf(user))
return ..()
else

View File

@@ -41,7 +41,7 @@
new /obj/effect/temp_visual/resonance(T, user, src, burst_time)
user.changeNext_move(CLICK_CD_MELEE)
/obj/item/resonator/pre_attackby(atom/target, mob/user, params)
/obj/item/resonator/pre_attack(atom/target, mob/user, params)
if(check_allowed_items(target, 1))
CreateResonance(target, user)
return TRUE

View File

@@ -247,7 +247,7 @@
to_chat(user, "<span class='notice'>You release the wisp. It begins to bob around your head.</span>")
icon_state = "lantern"
wisp.orbit(user, 20)
INVOKE_ASYNC(wisp, /atom/movable/.proc/orbit, user, 20)
set_light(0)
user.update_sight()

View File

@@ -115,7 +115,7 @@
icon_living = "cow"
icon_dead = "cow_dead"
icon_gib = "cow_gib"
speak = list("moo?","moo","MOOOOOO")
speak = list("Moo?","Moo","MOOOOOO")
speak_emote = list("moos","moos hauntingly")
emote_hear = list("brays")
emote_see = list("shakes its head")

View File

@@ -408,7 +408,7 @@ Difficulty: Hard
/obj/effect/decal/cleanable/blood/gibs/bubblegum/can_bloodcrawl_in()
return TRUE
/mob/living/simple_animal/hostile/megafauna/bubblegum/do_attack_animation(atom/A, visual_effect_icon)
/mob/living/simple_animal/hostile/megafauna/bubblegum/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect)
if(!charging)
..()

View File

@@ -49,6 +49,7 @@
name = "venus human trap"
desc = "Now you know how the fly feels."
icon_state = "venus_human_trap"
icon_living = "venus_human_trap"
mob_biotypes = MOB_ORGANIC | MOB_PLANT
layer = MOB_LAYER + 0.9
health = 50

View File

@@ -116,7 +116,9 @@
var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("[question]?", poll_time = 10 SECONDS, min_hours = minhours, source = M)
var/mob/dead/observer/theghost = null
if(LAZYLEN(candidates))
if(length(candidates))
if(QDELETED(M))
return
theghost = pick(candidates)
to_chat(M, "Your mob has been taken over by a ghost!")
message_admins("[key_name_admin(theghost)] has taken control of ([key_name_admin(M)])")

View File

@@ -153,10 +153,14 @@
resistance_flags = FLAMMABLE
var/static/list/no_wrap = list(/obj/item/smallDelivery, /obj/structure/bigDelivery, /obj/item/evidencebag, /obj/structure/closet/body_bag, /obj/item/twohanded/required)
/obj/item/stack/packageWrap/afterattack(obj/target as obj, mob/user as mob, proximity)
if(!proximity) return
if(!istype(target)) //this really shouldn't be necessary (but it is). -Pete
/obj/item/stack/packageWrap/pre_attack(atom/A, mob/living/user, params)
. = ..()
if(!in_range(A, user))
return
if(!isobj(A))
return
var/obj/target = A
if(is_type_in_list(target, no_wrap))
return
if(target.anchored)
@@ -166,62 +170,61 @@
if(istype(target, /obj/item) && !(istype(target, /obj/item/storage) && !istype(target,/obj/item/storage/box) && !istype(target, /obj/item/shippingPackage)))
var/obj/item/O = target
if(use(1))
var/obj/item/smallDelivery/P = new /obj/item/smallDelivery(get_turf(O.loc)) //Aaannd wrap it up!
if(!istype(O.loc, /turf))
if(user.client)
user.client.screen -= O
P.wrapped = O
O.loc = P
var/i = round(O.w_class)
if(i in list(1,2,3,4,5))
P.icon_state = "deliverycrate[i]"
P.w_class = i
P.add_fingerprint(usr)
O.add_fingerprint(usr)
add_fingerprint(usr)
else
return
if(!use(1))
return FALSE
var/obj/item/smallDelivery/P = new /obj/item/smallDelivery(get_turf(O.loc)) //Aaannd wrap it up!
if(!isturf(O.loc))
if(user.client)
user.client.screen -= O
P.wrapped = O
O.loc = P
var/i = round(O.w_class)
if(i in list(1,2,3,4,5))
P.icon_state = "deliverycrate[i]"
P.w_class = i
P.add_fingerprint(user)
O.add_fingerprint(user)
add_fingerprint(user)
else if(istype(target, /obj/structure/closet/crate))
var/obj/structure/closet/crate/O = target
if(O.opened)
return
if(amount >= 3 && do_after_once(user, 15, target = target))
if(O.opened || !use(3))
return
var/obj/structure/bigDelivery/P = new /obj/structure/bigDelivery(get_turf(O.loc))
P.icon_state = "deliverycrate"
P.wrapped = O
O.loc = P
else
to_chat(user, "<span class='notice'>You need more paper.</span>")
return
else if(istype (target, /obj/structure/closet))
var/obj/structure/closet/O = target
if(O.opened)
return
if(amount >= 3 && do_after_once(user, 15, target = target))
if(O.opened || !use(3))
return
var/obj/structure/bigDelivery/P = new /obj/structure/bigDelivery(get_turf(O.loc))
P.wrapped = O
P.init_welded = O.welded
O.welded = 1
O.loc = P
else
to_chat(user, "<span class='notice'>You need more paper.</span>")
return
var/obj/structure/bigDelivery/D = wrap_closet(target, user)
if(!D)
return FALSE
D.icon_state = "deliverycrate"
else if(istype(target, /obj/structure/closet))
var/obj/structure/closet/C = target
var/obj/structure/bigDelivery/D = wrap_closet(target, user)
if(!D)
return FALSE
D.init_welded = C.welded
C.welded = TRUE
else
to_chat(user, "<span class='notice'>The object you are trying to wrap is unsuitable for the sorting machinery.</span>")
return
return FALSE
user.visible_message("<span class='notice'>[user] wraps [target].</span>")
user.create_attack_log("<font color='blue'>Has used [name] on [target]</font>")
add_attack_logs(user, target, "used [name]", ATKLOG_ALL)
if(amount <= 0 && !src.loc) //if we used our last wrapping paper, drop a cardboard tube
new /obj/item/c_tube( get_turf(user) )
return
if(amount <= 0 && QDELETED(src)) //if we used our last wrapping paper, drop a cardboard tube
var/obj/item/c_tube/T = new(get_turf(user))
user.put_in_active_hand(T)
return FALSE
// Separate proc to avoid copy pasting the code twice
/obj/item/stack/packageWrap/proc/wrap_closet(obj/structure/closet/C, mob/user)
if(C.opened)
return
if(amount < 3)
to_chat(user, "<span class='warning'>You need more paper.</span>")
return
if(!do_after_once(user, 1.5 SECONDS, target = C) || C.opened || !use(3)) // Checking these again since it's after a delay
return
var/obj/structure/bigDelivery/P = new(get_turf(C))
P.wrapped = C
C.loc = P
return P
/obj/item/destTagger
name = "destination tagger"

View File

@@ -205,7 +205,7 @@
var/ghostmsg = "Play as [SM.name], pet of [user.name]?"
var/list/candidates = SSghost_spawns.poll_candidates(ghostmsg, ROLE_SENTIENT, FALSE, 10 SECONDS, source = M)
if(!src)
if(QDELETED(src) || QDELETED(SM))
return
if(candidates.len)