Bug fixes, init signals for mobs, density updates. (#21108)

1. Look up now follows your mob (no more auto-removal when you move,
only when you go below a solid tile). Maybe add a hotkey?
2. Made the codebase work with the latest version of spacemandmm lint.
3. Advanced kits don't work on robotic limbs.
4. Density shifting should now always use set_density. Ported
TRAIT_UNDENSE from CM to better control the density of living mobs.
Fixes weird shifting behaviour with wall leaning -- what was happening
is that mob density was getting reset to TRUE because the mob wasn't
lying down.
5. TGUI say light prefs actually save.

---------

Co-authored-by: Matt Atlas <liermattia@gmail.com>
This commit is contained in:
Matt Atlas
2025-08-06 10:18:14 +00:00
committed by GitHub
co-authored by Matt Atlas
parent a045fc1709
commit d7706ece53
32 changed files with 154 additions and 43 deletions
@@ -71,6 +71,7 @@
"tgui_inputs",
"tgui_buttons_large",
"tgui_inputs_swapped",
"tgui_say_light_mode",
"ui_scale",
"ckey" = 1
)
@@ -90,6 +91,7 @@
"tgui_inputs" = pref.tgui_inputs,
"tgui_buttons_large" = pref.tgui_buttons_large,
"tgui_inputs_swapped" = pref.tgui_inputs_swapped,
"tgui_say_light_mode" = pref.tgui_say_light_mode,
"ui_scale" = pref.ui_scale
)
@@ -103,6 +105,7 @@
pref.tgui_inputs = sanitize_bool(pref.tgui_inputs, TRUE)
pref.tgui_buttons_large = sanitize_bool(pref.tgui_buttons_large, FALSE)
pref.tgui_inputs_swapped = sanitize_bool(pref.tgui_inputs_swapped, FALSE)
pref.tgui_say_light_mode = sanitize_bool(pref.tgui_say_light_mode, FALSE)
pref.ooccolor = sanitize_hexcolor(pref.ooccolor, initial(pref.ooccolor))
/datum/category_item/player_setup_item/player_global/ui/content(mob/user)
+1 -1
View File
@@ -28,5 +28,5 @@
/obj/item/storage/box/plasticbag/fill()
..()
for(var/i=0;i < storage_slots, i++)
for(var/i=0; i < storage_slots; i++)
new /obj/item/evidencebag/plasticbag(src)
+4 -4
View File
@@ -7,7 +7,7 @@
/obj/item/storage/box/swabs/fill()
..()
for(var/i=0;i<storage_slots,i++) // Fill 'er up.
for(var/i=0; i < storage_slots; i++) // Fill 'er up.
new /obj/item/forensics/swab(src)
/obj/item/storage/box/slides
@@ -17,7 +17,7 @@
/obj/item/storage/box/slides/fill()
..()
for(var/i=0;i<storage_slots,i++)
for(var/i=0; i < storage_slots; i++)
new /obj/item/forensics/slide(src)
/obj/item/storage/box/evidence
@@ -28,7 +28,7 @@
/obj/item/storage/box/evidence/fill()
..()
for(var/i=0;i<storage_slots,i++)
for(var/i=0; i < storage_slots; i++)
new /obj/item/evidencebag(src)
/obj/item/storage/box/fingerprints
@@ -40,5 +40,5 @@
/obj/item/storage/box/fingerprints/fill()
..()
for(var/i=0;i<storage_slots,i++)
for(var/i=0; i < storage_slots; i++)
new /obj/item/sample/print(src)
+1 -1
View File
@@ -18,7 +18,7 @@
log_and_message_admins("Blob spawned at \the [get_area(T)]", location = T)
Blob = new /obj/effect/blob/core(T)
for(var/i = 1; i < rand(3, 4), i++)
for(var/i = 1; i < rand(3, 4); i++)
Blob.process()
/datum/event/blob/tick()
@@ -23,7 +23,7 @@
t += "<hr>"
// Iterating by index simplifies editing/deletion in game,
// since the href_list["pos"] var is consistent
for(var/i = 1, i <= my_list.len; i++)
for(var/i = 1; i <= length(my_list); i++)
t += "#[i] | [display_data(my_list[i])] | "
t += "<a href='byond://?src=[REF(src)];edit=1;pos=[i]'>Edit</a> | "
t += "<a href='byond://?src=[REF(src)];remove=1;pos=[i]'>Remove</a><br>"
+1 -1
View File
@@ -9,7 +9,7 @@
icon = 'icons/mob/eye.dmi'
icon_state = "default-eye"
alpha = 127
density = 0
density = FALSE
var/sprint = 10
var/cooldown = 0
@@ -3,7 +3,7 @@
desc = "It's a g-g-g-g-ghooooost!" //jinkies!
icon = 'icons/mob/mob.dmi'
icon_state = "ghost"
density = 0
density = FALSE
canmove = 0
blinded = 0
anchored = 1 // don't get pushed around
@@ -8,7 +8,7 @@
invisibility = 101
density = 0
density = FALSE
stat = DEAD
canmove = 0
+4 -1
View File
@@ -140,7 +140,10 @@
if(src.ear_damage < 25)
src.ear_damage = FALSE
src.density = !src.lying
if(lying)
ADD_TRAIT(src, TRAIT_UNDENSE, TRAIT_SOURCE_LYING_DOWN)
else
REMOVE_TRAIT(src, TRAIT_UNDENSE, TRAIT_SOURCE_LYING_DOWN)
if(src.sdisabilities & BLIND)
src.blinded = TRUE
+8
View File
@@ -0,0 +1,8 @@
/// Called on [/mob/living/Initialize(mapload)], for the mob to register to relevant signals.
/mob/living/proc/register_init_signals()
RegisterSignals(src, list(SIGNAL_ADDTRAIT(TRAIT_UNDENSE), SIGNAL_REMOVETRAIT(TRAIT_UNDENSE)), PROC_REF(undense_changed))
/// Called when [TRAIT_UNDENSE] is gained or lost
/mob/living/proc/undense_changed(datum/source)
SIGNAL_HANDLER
update_density()
+1 -1
View File
@@ -182,7 +182,7 @@
set_sight_flags &= ~BLIND
set_sight(set_sight_flags)
set_see_invisible(initial(see_invisible))
set_see_invisible(see_invisible)
/mob/living/proc/update_dead_sight()
set_sight(sight|SEE_TURFS|SEE_MOBS|SEE_OBJS)
+20 -9
View File
@@ -33,7 +33,7 @@ default behaviour is:
*/
/mob/living/proc/can_move_mob(var/mob/living/swapped, swapping = 0, passive = 0)
if(!swapped)
return 1
return TRUE
if(!passive)
return swapped.can_move_mob(src, swapping, 1)
else
@@ -43,10 +43,10 @@ default behaviour is:
else
context_flags = swapped.mob_push_flags
if(!mob_bump_flag) //nothing defined, go wild
return 1
return TRUE
if(mob_bump_flag & context_flags)
return 1
return 0
return TRUE
return FALSE
/mob/living
var/tmp/last_push_notif
@@ -200,18 +200,20 @@ default behaviour is:
/mob/living/proc/can_swap_with(var/mob/living/tmob)
if(tmob.buckled_to || buckled_to)
return 0
return FALSE
//BubbleWrap: people in handcuffs are always switched around as if they were on 'help' intent to prevent a person being pulled from being seperated from their puller
if(!(tmob.mob_always_swap || (tmob.a_intent == I_HELP || tmob.restrained()) && (a_intent == I_HELP || src.restrained())))
return 0
return FALSE
if(!tmob.canmove || !canmove)
return 0
return FALSE
if(swap_density_check(src, tmob))
return 0
return FALSE
if(swap_density_check(tmob, src))
return 0
return FALSE
if(pulling?.density && tmob.pulling?.density) // if both are pulling, don't shuffle
return FALSE
@@ -922,6 +924,8 @@ default behaviour is:
COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
)
register_init_signals()
AddElement(/datum/element/connect_loc, loc_connections)
/mob/living/Destroy()
@@ -1078,3 +1082,10 @@ default behaviour is:
///Performs the aftereffects of blocking a projectile.
/mob/living/proc/block_projectile_effects()
return
/// Uses presence of [TRAIT_UNDENSE] to figure out what is the correct density state for the mob. Triggered by trait signal.
/mob/living/proc/update_density()
if(HAS_TRAIT(src, TRAIT_UNDENSE))
set_density(FALSE)
else
set_density(TRUE)
@@ -119,7 +119,7 @@
update_icon()
pass_flags = PASSTABLE | PASSMOB
layer = TURF_DETAIL_LAYER
density = FALSE
ADD_TRAIT(src, TRAIT_UNDENSE, TRAIT_SOURCE_WORM_BURROW)
visible_message(SPAN_DANGER("\The [src] burrows into the ground!"))
/mob/living/simple_animal/hostile/phoron_worm/proc/unburrow()
@@ -131,7 +131,7 @@
update_icon()
pass_flags = initial(pass_flags)
layer = initial(layer)
density = TRUE
REMOVE_TRAIT(src, TRAIT_UNDENSE, TRAIT_SOURCE_WORM_BURROW)
visible_message(SPAN_DANGER("\The [src] emerges from the ground!"))
if(mob_size > 15)
for(var/mob/living/M in orange(1,src))
@@ -772,7 +772,7 @@
/mob/living/simple_animal/death(gibbed, deathmessage = "dies!")
GLOB.move_manager.stop_looping(src)
movement_target = null
density = FALSE
ADD_TRAIT(src, TRAIT_UNDENSE, TRAIT_SOURCE_MOB_DEATH)
if (isopenturf(loc))
ADD_FALLING_ATOM(src)
. = ..(gibbed, deathmessage)
+2 -2
View File
@@ -912,12 +912,12 @@
canmove = !MOB_IS_INCAPACITATED(INCAPACITATION_KNOCKOUT) && !weakened
if(lying)
density = 0
ADD_TRAIT(src, TRAIT_UNDENSE, TRAIT_SOURCE_LYING_DOWN)
if(!lying_is_intentional)
if(l_hand) unEquip(l_hand)
if(r_hand) unEquip(r_hand)
else
density = initial(density)
REMOVE_TRAIT(src, TRAIT_UNDENSE, TRAIT_SOURCE_LYING_DOWN)
for(var/obj/item/grab/G in grabbed_by)
if(G.wielded)
+1 -1
View File
@@ -1,5 +1,5 @@
/mob
density = 1
density = TRUE
layer = MOB_LAYER
animate_movement = 2
movable_flags = MOVABLE_FLAG_PROXMOVE
@@ -150,7 +150,7 @@
return FALSE
set_current(C)
user.machine = ui_host()
user.set_machine(ui_host())
user.reset_view(current_camera)
check_eye(user)
+3 -3
View File
@@ -804,10 +804,10 @@
/atom/movable/z_observer/proc/follow()
/atom/movable/z_observer/z_up/follow()
forceMove(get_step(target_turf, UP))
forceMove(get_step(owner, UP))
if(isturf(src.loc))
var/turf/T = src.loc
if((T && TURF_IS_MIMICING(T)) && (get_turf(owner) == get_step(src, DOWN)))
if(T && TURF_IS_MIMICING(T))
return
owner.reset_view(null)
owner.z_eye = null
@@ -818,7 +818,7 @@
/// If we move down more than 1 step, don't move down again.
if((GET_Z(owner) - down_step.z) < 2)
forceMove(down_step)
if(owner.Adjacent(target_turf) && (owner.dir == get_dir(owner, target_turf)))
if(owner.Adjacent(target_turf) && (get_dir(owner, target_turf) & owner.dir))
return
owner.reset_view(null)
owner.z_eye = null
@@ -91,7 +91,7 @@ Targeted spells have two useful flags: INCLUDEUSER and SELECTABLE. These are exp
possible_targets += target
if(spell_flags & SELECTABLE)
for(var/i = 1; i <= max_targets, i++)
for(var/i = 1; i <= max_targets; i++)
if(!length(possible_targets))
to_chat(user, SPAN_WARNING("There are no targets in range!"))
break