diff --git a/_maps/layeniastation.json b/_maps/layeniastation.json
index 8b1ea69047..99a22bbf66 100644
--- a/_maps/layeniastation.json
+++ b/_maps/layeniastation.json
@@ -12,7 +12,7 @@
"traits": [
{
"Up": 1,
- "Baseturf": "/area/space",
+ "Baseturf": "/turf/space",
"Linkage": "Cross"
},
{
diff --git a/code/_onclick/other_mobs.dm b/code/_onclick/other_mobs.dm
index 35f691a99e..7bc994b9c5 100644
--- a/code/_onclick/other_mobs.dm
+++ b/code/_onclick/other_mobs.dm
@@ -35,7 +35,7 @@
SEND_SIGNAL(src, COMSIG_HUMAN_MELEE_UNARMED_ATTACK, A)
return . | A.attack_hand(src, intent, .)
-/atom/proc/attack_hand(mob/user, act_intent = user.a_intent, attackchain_flags)
+/atom/proc/attack_hand(mob/user, act_intent = user?.a_intent, attackchain_flags)
//SHOULD_NOT_SLEEP(TRUE)
if(!(interaction_flags_atom & INTERACT_ATOM_NO_FINGERPRINT_ATTACK_HAND))
add_fingerprint(user)
@@ -54,7 +54,7 @@
else if(attack_hand_is_action)
user.DelayNextAction()
-/atom/proc/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
+/atom/proc/on_attack_hand(mob/user, act_intent = user?.a_intent, unarmed_attack_flags)
//Return a non FALSE value to cancel whatever called this from propagating, if it respects it.
/atom/proc/_try_interact(mob/user)
diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm
index 9b88496e52..dd0a29bdb2 100644
--- a/code/controllers/subsystem/job.dm
+++ b/code/controllers/subsystem/job.dm
@@ -26,6 +26,19 @@ SUBSYSTEM_DEF(job)
set_overflow_role(CONFIG_GET(string/overflow_job))
return ..()
+/// Returns a list of jobs that we are allowed to fuck with during random events
+/datum/controller/subsystem/job/proc/get_valid_overflow_jobs()
+ var/static/list/overflow_jobs
+ if (!isnull(overflow_jobs))
+ return overflow_jobs
+
+ overflow_jobs = list()
+ for (var/datum/job/check_job in occupations) // TODO: Port joinable_occupations from upstream TG PR #60578.
+ if (!check_job.allow_bureaucratic_error)
+ continue
+ overflow_jobs += check_job
+ return overflow_jobs
+
/datum/controller/subsystem/job/proc/set_overflow_role(new_overflow_role)
var/datum/job/new_overflow = GetJob(new_overflow_role)
var/cap = CONFIG_GET(number/overflow_cap)
@@ -560,7 +573,8 @@ SUBSYSTEM_DEF(job)
var/jobstext = file2text("[global.config.directory]/jobs.txt")
for(var/datum/job/J in occupations)
var/regex/jobs = new("[J.title]=(-1|\\d+),(-1|\\d+)")
- jobs.Find(jobstext)
+ if(!jobs.Find(jobstext))
+ continue
J.total_positions = text2num(jobs.group[1])
J.spawn_positions = text2num(jobs.group[2])
diff --git a/code/datums/elements/mob_holder.dm b/code/datums/elements/mob_holder.dm
index 758708be3c..db7d91deed 100644
--- a/code/datums/elements/mob_holder.dm
+++ b/code/datums/elements/mob_holder.dm
@@ -182,9 +182,9 @@
return TRUE
/obj/item/clothing/head/mob_holder/relaymove(mob/living/user, direction)
- container_resist()
+ container_resist(user)
-/obj/item/clothing/head/mob_holder/container_resist()
+/obj/item/clothing/head/mob_holder/container_resist(mob/living/user)
if(isliving(loc))
var/mob/living/L = loc
L.visible_message("[held_mob] escapes from [L]!", "[held_mob] escapes your grip!")
diff --git a/code/datums/elements/photosynthesis.dm b/code/datums/elements/photosynthesis.dm
index a88a5d108c..1efe1d3597 100644
--- a/code/datums/elements/photosynthesis.dm
+++ b/code/datums/elements/photosynthesis.dm
@@ -49,8 +49,9 @@
return ..()
/datum/element/photosynthesis/process()
- for(var/A in attached_atoms)
- var/atom/movable/AM = A
+ for(var/atom/movable/AM as anything in attached_atoms)
+ if(isnull(AM))
+ continue
var/light_amount = 0 //how much light there is in the place, affects receiving nutrition and healing
if(isturf(AM.loc)) //else, there's considered to be no light
var/turf/T = AM.loc
diff --git a/code/datums/station_traits/negative_traits.dm b/code/datums/station_traits/negative_traits.dm
index 09be52d700..91043d3942 100644
--- a/code/datums/station_traits/negative_traits.dm
+++ b/code/datums/station_traits/negative_traits.dm
@@ -119,9 +119,9 @@
/datum/station_trait/overflow_job_bureaucracy/proc/set_overflow_job_override(datum/source)
SIGNAL_HANDLER
- var/datum/job/picked_job = pick(get_all_jobs())
- chosen_job_name = lowertext(picked_job.title) // like Chief Engineers vs like chief engineers
- SSjob.set_overflow_role(picked_job.type)
+ var/picked_job_title = SSjob.get_valid_overflow_jobs()
+ chosen_job_name = lowertext(picked_job_title) // like Chief Engineers vs like chief engineers
+ SSjob.set_overflow_role(SSjob.GetJobType(picked_job_title)) // TODO: port a blacklist for this from upstream TG PR
/datum/station_trait/slow_shuttle
name = "Slow Shuttle"
diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm
index 51bfd506e6..486b0d764b 100644
--- a/code/datums/status_effects/debuffs.dm
+++ b/code/datums/status_effects/debuffs.dm
@@ -1,5 +1,5 @@
/// The damage healed per tick while sleeping without any modifiers
-#define HEALING_SLEEP_DEFAULT 0.005
+#define HEALING_SLEEP_DEFAULT -0.005
//Largely negative status effects go here, even if they have small benificial effects
//STUN EFFECTS
diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm
index a6d5982a81..e88a0a1a03 100644
--- a/code/game/machinery/_machinery.dm
+++ b/code/game/machinery/_machinery.dm
@@ -556,44 +556,46 @@ Class Procs:
if((flags_1 & NODECONSTRUCT_1) && !W.works_from_distance)
return FALSE
var/shouldplaysound = 0
- if(component_parts)
- if(panel_open || W.works_from_distance)
- var/obj/item/circuitboard/machine/CB = locate(/obj/item/circuitboard/machine) in component_parts
- var/P
- if(W.works_from_distance)
- to_chat(user, display_parts(user))
- for(var/obj/item/A in component_parts)
- for(var/D in CB.req_components)
- if(ispath(A.type, D))
- P = D
- break
- for(var/obj/item/B in W.contents)
- if(istype(B, P) && istype(A, P))
- if(B.get_part_rating() > A.get_part_rating())
- if(istype(B,/obj/item/stack)) //conveniently this will mean A is also a stack and I will kill the first person to prove me wrong
- var/obj/item/stack/SA = A
- var/obj/item/stack/SB = B
- var/used_amt = SA.get_amount()
- if(!SB.use(used_amt))
- continue //if we don't have the exact amount to replace we don't
- var/obj/item/stack/SN = new SB.merge_type(null,used_amt)
- component_parts += SN
- else
- if(SEND_SIGNAL(W, COMSIG_TRY_STORAGE_TAKE, B, src))
- component_parts += B
- B.moveToNullspace()
- SEND_SIGNAL(W, COMSIG_TRY_STORAGE_INSERT, A, null, null, TRUE)
- component_parts -= A
- to_chat(user, "[capitalize(A.name)] replaced with [B.name].")
- shouldplaysound = 1 //Only play the sound when parts are actually replaced!
- break
- RefreshParts()
- else
- to_chat(user, display_parts(user))
- if(shouldplaysound)
- W.play_rped_sound()
- return TRUE
- return FALSE
+ if(!component_parts)
+ return FALSE
+ if(!panel_open && !W.works_from_distance)
+ to_chat(user, display_parts(user))
+ return FALSE
+ var/obj/item/circuitboard/machine/machine_board = locate(/obj/item/circuitboard/machine) in component_parts
+ if(!machine_board)
+ return FALSE
+ var/P
+ if(W.works_from_distance)
+ to_chat(user, display_parts(user))
+ for(var/obj/item/A in component_parts)
+ for(var/D in machine_board.req_components)
+ if(istype(A, D))
+ P = D
+ break
+ for(var/obj/item/B in W.contents)
+ if(istype(B, P) && istype(A, P))
+ if(B.get_part_rating() > A.get_part_rating())
+ if(istype(B,/obj/item/stack)) //conveniently this will mean A is also a stack and I will kill the first person to prove me wrong
+ var/obj/item/stack/SA = A
+ var/obj/item/stack/SB = B
+ var/used_amt = SA.get_amount()
+ if(!SB.use(used_amt))
+ continue //if we don't have the exact amount to replace we don't
+ var/obj/item/stack/SN = new SB.merge_type(null,used_amt)
+ component_parts += SN
+ else
+ if(SEND_SIGNAL(W, COMSIG_TRY_STORAGE_TAKE, B, src))
+ component_parts += B
+ B.moveToNullspace()
+ SEND_SIGNAL(W, COMSIG_TRY_STORAGE_INSERT, A, null, null, TRUE)
+ component_parts -= A
+ to_chat(user, "[capitalize(A.name)] replaced with [B.name].")
+ shouldplaysound = 1 //Only play the sound when parts are actually replaced!
+ break
+ RefreshParts()
+ if(shouldplaysound)
+ W.play_rped_sound()
+ return TRUE
/obj/machinery/proc/display_parts(mob/user)
. = list()
diff --git a/code/game/objects/items/credit_holochip.dm b/code/game/objects/items/credit_holochip.dm
index d5ccd4d960..e6b5a05bb3 100644
--- a/code/game/objects/items/credit_holochip.dm
+++ b/code/game/objects/items/credit_holochip.dm
@@ -84,13 +84,18 @@
var/obj/item/card/id/ID = I
if(!ID.registered_account)
to_chat(user, "[ID] doesn't have a linked account to deposit into!")
- return
- for(var/obj/item/holochip/money in src.loc.contents)
+ return STOP_ATTACK_PROC_CHAIN
+ var/atom/old_loc = loc // The following code can qdel src, which nulls its loc.
+ if(!old_loc)
+ ID.attackby(src, user)
+ return STOP_ATTACK_PROC_CHAIN
+ for(var/obj/item/holochip/money in old_loc.contents)
ID.attackby(money, user)
- for(var/obj/item/stack/spacecash/money in src.loc.contents)
+ for(var/obj/item/stack/spacecash/money in old_loc.contents)
ID.attackby(money, user)
- for(var/obj/item/coin/money in src.loc.contents)
+ for(var/obj/item/coin/money in old_loc.contents)
ID.attackby(money, user)
+ return STOP_ATTACK_PROC_CHAIN
/obj/item/holochip/AltClick(mob/user)
if(!istype(user) || !user.canUseTopic(src, BE_CLOSE, ismonkey(user)))
diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm
index 825becc776..428e9cf67b 100644
--- a/code/game/objects/items/devices/scanners.dm
+++ b/code/game/objects/items/devices/scanners.dm
@@ -861,8 +861,7 @@ GENETICS SCANNER
if (!isslime(M))
to_chat(user, "This device can only scan slimes!")
return
- var/mob/living/simple_animal/slime/T = M
- slime_scan(T, user)
+ slime_scan(M, user)
/proc/slime_scan(mob/living/simple_animal/slime/T, mob/living/user)
var/output = "Slime scan results:"
diff --git a/code/game/objects/items/devices/transfer_valve.dm b/code/game/objects/items/devices/transfer_valve.dm
index 34f7d10b02..9197752e71 100644
--- a/code/game/objects/items/devices/transfer_valve.dm
+++ b/code/game/objects/items/devices/transfer_valve.dm
@@ -110,7 +110,7 @@
if(.)
return
if(attached_device)
- attached_device.attack_hand()
+ attached_device.attack_hand(user)
//These keep attached devices synced up, for example a TTV with a mouse trap being found in a bag so it's triggered, or moving the TTV with an infrared beam sensor to update the beam's direction.
diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm
index fd1962a9f4..aceac5c799 100644
--- a/code/game/objects/items/robot/robot_upgrades.dm
+++ b/code/game/objects/items/robot/robot_upgrades.dm
@@ -218,21 +218,36 @@
name = "mining cyborg premium KA"
desc = "A premium kinetic accelerator replacement for the mining module's standard kinetic accelerator."
icon_state = "cyborg_upgrade3"
- require_module = 1
+ require_module = TRUE
module_type = list(/obj/item/robot_module/miner)
+ module_flags = BORG_MODULE_MINER // SANDSTORM EDIT
/obj/item/borg/upgrade/premiumka/action(mob/living/silicon/robot/R, user = usr)
. = ..()
if(.)
for(var/obj/item/gun/energy/kinetic_accelerator/cyborg/KA in R.module)
for(var/obj/item/borg/upgrade/modkit/M in KA.modkits)
- M.uninstall(src)
+ M.uninstall(KA)
R.module.remove_module(KA, TRUE)
var/obj/item/gun/energy/kinetic_accelerator/premiumka/cyborg/PKA = new /obj/item/gun/energy/kinetic_accelerator/premiumka/cyborg(R.module)
R.module.basic_modules += PKA
R.module.add_module(PKA, FALSE, TRUE)
+// SANDSTORM EDIT START
+/obj/item/borg/upgrade/premiumka/deactivate(mob/living/silicon/robot/R, user = usr)
+ . = ..()
+ if (.)
+ for(var/obj/item/gun/energy/kinetic_accelerator/premiumka/cyborg/PKA in R.module)
+ for(var/obj/item/borg/upgrade/modkit/M in PKA.modkits)
+ M.uninstall(PKA)
+ R.module.remove_module(PKA, TRUE)
+
+ var/obj/item/gun/energy/kinetic_accelerator/cyborg/KA = new (R.module)
+ R.module.basic_modules += KA
+ R.module.add_module(KA, FALSE, TRUE)
+// SANDSTORM EDIT END
+
/obj/item/borg/upgrade/tboh
name = "janitor cyborg trash bag of holding"
desc = "A trash bag of holding replacement for the janiborg's standard trash bag."
diff --git a/code/game/objects/items/shields.dm b/code/game/objects/items/shields.dm
index af9803f41f..d8669a4c4d 100644
--- a/code/game/objects/items/shields.dm
+++ b/code/game/objects/items/shields.dm
@@ -246,7 +246,7 @@
/obj/item/shield/riot/on_shield_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
var/final_damage = damage
- if(attack_type & ATTACK_TYPE_MELEE)
+ if(attack_type & (ATTACK_TYPE_MELEE | ATTACK_TYPE_THROWN))
if(istype(object, /obj)) //Assumption: non-object attackers are a meleeing mob. Therefore: Assuming physical attack in this case.
var/obj/hittingthing = object
if(hittingthing.damtype == BURN)
@@ -269,15 +269,15 @@
else if((shield_flags & SHIELD_KINETIC_STRONG))
final_damage *= 0.5
- if(attack_type & ATTACK_TYPE_PROJECTILE)
- var/obj/item/projectile/shootingthing = object
+ var/obj/item/projectile/shootingthing = object
+ if(attack_type & ATTACK_TYPE_PROJECTILE && istype(shootingthing))
if(is_energy_reflectable_projectile(shootingthing))
if((shield_flags & SHIELD_ENERGY_WEAK))
final_damage *= 2
else if((shield_flags & SHIELD_ENERGY_STRONG))
final_damage *= 0.5
- if(!is_energy_reflectable_projectile(object))
+ if(!is_energy_reflectable_projectile(shootingthing))
if((shield_flags & SHIELD_KINETIC_WEAK))
final_damage *= 2
else if((shield_flags & SHIELD_KINETIC_STRONG))
diff --git a/code/modules/arousal/organs/breasts.dm b/code/modules/arousal/organs/breasts.dm
index 83b1ddba67..f792d2e071 100644
--- a/code/modules/arousal/organs/breasts.dm
+++ b/code/modules/arousal/organs/breasts.dm
@@ -67,7 +67,7 @@
//this is far too lewd wah
/obj/item/organ/genital/breasts/modify_size(modifier, min = -INFINITY, max = INFINITY)
- var/new_value = clamp(size + modifier, max(min, min_size ? GLOB.breast_values[min_size] : -INFINITY), min(max_size ? GLOB.breast_values[max_size] : INFINITY, max))
+ var/new_value = clamp(size + modifier, max(min, min_size || -INFINITY), min(max_size || INFINITY, max))
if(new_value == size)
return
prev_size = size
@@ -76,24 +76,12 @@
..()
/obj/item/organ/genital/breasts/size_to_state()
- var/rounded = round(size)
- var/str_size
- switch(rounded)
- if(0) //flatchested
- str_size = "flat"
- if(1 to 8) //modest
- str_size = GLOB.breast_values[rounded]
- if(9 to 15) //massive
- str_size = GLOB.breast_values[rounded]
- if(16 to 17) //ridiculous
- str_size = GLOB.breast_values[rounded]
- if(18 to 24) //AWOOOOGAAAAAAA
- str_size = "massive"
- if(25 to 29) //AWOOOOOOGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- str_size = "giga"
- if(30 to INFINITY) //AWWWWWWWWWWWWWOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOGGGGGAAAAAAAAAAAAAAAAAAAAAA
- str_size = "impossible"
- return str_size
+ var/rounded = clamp(round(size), 0, INFINITY)
+ for(var/size_index in length(GLOB.breast_values) to 1 step -1) // This should go in the reverse of the defined order (i.e. greatest-to-least).
+ var/size_state = GLOB.breast_values[size_index]
+ if(GLOB.breast_values[size_state] <= rounded) // Return the greatest (last) size value that's less than or equal to our numerical size.
+ return size_state
+ return "flat" // Even flat was too large for you, I guess...? This should never happen.
/obj/item/organ/genital/breasts/update_size()//wah
var/rounded_size = round(size)
@@ -131,5 +119,16 @@
if(D.features["breasts_accessible"])
toggle_accessibility(TRUE)
+/obj/item/organ/genital/breasts/proc/get_lactation_amount_modifier()
+ switch(size)
+ if(-INFINITY to 3)
+ return 1
+ if(3 to 5)
+ return 2
+ if(5 to 8)
+ return 3
+ else
+ return clamp(size - 5, 0, INFINITY)
+
#undef BREASTS_ICON_MIN_SIZE
#undef BREASTS_ICON_MAX_SIZE
diff --git a/code/modules/assembly/bomb.dm b/code/modules/assembly/bomb.dm
index 8c025e2ab4..bc05d1095a 100644
--- a/code/modules/assembly/bomb.dm
+++ b/code/modules/assembly/bomb.dm
@@ -91,12 +91,12 @@
if(bombassembly)
bombassembly.on_found(finder)
-/obj/item/onetankbomb/on_attack_hand() //also for mousetraps
+/obj/item/onetankbomb/on_attack_hand(mob/user) //also for mousetraps
. = ..()
if(.)
return
if(bombassembly)
- bombassembly.attack_hand()
+ bombassembly.attack_hand(user)
/obj/item/onetankbomb/Move()
. = ..()
diff --git a/code/modules/food_and_drinks/kitchen_machinery/monkeyrecycler.dm b/code/modules/food_and_drinks/kitchen_machinery/monkeyrecycler.dm
index bdf0fee33a..1af7f08e41 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/monkeyrecycler.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/monkeyrecycler.dm
@@ -19,6 +19,7 @@ GLOBAL_LIST_EMPTY(monkey_recyclers)
. = ..()
if (mapload)
GLOB.monkey_recyclers += src
+ locate_camera_console()
/obj/machinery/monkey_recycler/Destroy()
GLOB.monkey_recyclers -= src
@@ -28,6 +29,15 @@ GLOBAL_LIST_EMPTY(monkey_recyclers)
connected.Cut()
return ..()
+/obj/machinery/monkey_recycler/proc/locate_camera_console()
+ if(length(connected))
+ return // we're already connected!
+ for(var/obj/machinery/computer/camera_advanced/xenobio/xeno_camera in GLOB.machines)
+ if(get_area(xeno_camera) == get_area(loc))
+ xeno_camera.connected_recycler = src
+ connected |= xeno_camera
+ break
+
/obj/machinery/monkey_recycler/RefreshParts() //Ranges from 0.2 to 0.8 per monkey recycled
cube_production = 0
for(var/obj/item/stock_parts/manipulator/B in component_parts)
@@ -75,6 +85,7 @@ GLOBAL_LIST_EMPTY(monkey_recyclers)
to_chat(user, "The monkey is attached to something.")
return
qdel(target)
+ target = null //we sleep in this proc, clear reference NOW
to_chat(user, "You stuff the monkey into the machine.")
playsound(src.loc, 'sound/machines/juicer.ogg', 50, 1)
var/offset = prob(50) ? -2 : 2
diff --git a/code/modules/integrated_electronics/subtypes/manipulation.dm b/code/modules/integrated_electronics/subtypes/manipulation.dm
index a6b8ed7f72..b81b146131 100644
--- a/code/modules/integrated_electronics/subtypes/manipulation.dm
+++ b/code/modules/integrated_electronics/subtypes/manipulation.dm
@@ -119,7 +119,7 @@
TR.harvest_dead()
return list()
else
- return TR.myseed.harvest_userless()
+ return TR.myseed?.harvest_userless()
/obj/item/integrated_circuit/manipulation/seed_extractor
name = "seed extractor module"
diff --git a/code/modules/mining/equipment/kinetic_crusher.dm b/code/modules/mining/equipment/kinetic_crusher.dm
index ff786cf830..0b24912e86 100644
--- a/code/modules/mining/equipment/kinetic_crusher.dm
+++ b/code/modules/mining/equipment/kinetic_crusher.dm
@@ -94,7 +94,7 @@
if(!QDELETED(target))
var/obj/item/crusher_trophy/T = t
T.on_melee_hit(target, user)
- if(!QDELETED(C) && !QDELETED(target))
+ if(C && !QDELING(C) && !QDELETED(target)) // C can be 0 here, and QDELETED will runtime if that's the case.
C.total_damage += target_health - target.health //we did some damage, but let's not assume how much we did
/obj/item/kinetic_crusher/afterattack(atom/target, mob/living/user, proximity_flag, clickparams)
@@ -132,18 +132,18 @@
var/obj/item/crusher_trophy/T = t
T.on_mark_detonation(target, user)
if(!QDELETED(L))
- if(!QDELETED(C))
+ if(C && !QDELING(C)) // C can be 0 here, and QDELETED will runtime if that's the case.
C.total_damage += target_health - L.health //we did some damage, but let's not assume how much we did
new /obj/effect/temp_visual/kinetic_blast(get_turf(L))
var/backstab_dir = get_dir(user, L)
var/def_check = L.getarmor(type = BOMB)
if((user.dir & backstab_dir) && (L.dir & backstab_dir))
- if(!QDELETED(C))
+ if(C && !QDELING(C)) // See above.
C.total_damage += detonation_damage + backstab_bonus //cheat a little and add the total before killing it, so certain mobs don't have much lower chances of giving an item
L.apply_damage(detonation_damage + backstab_bonus, BRUTE, blocked = def_check)
playsound(user, 'sound/weapons/kenetic_accel.ogg', 100, 1) //Seriously who spelled it wrong
else
- if(!QDELETED(C))
+ if(C && !QDELING(C)) // See above.
C.total_damage += detonation_damage
L.apply_damage(detonation_damage, BRUTE, blocked = def_check)
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 56ab63a237..5f4d40a1e9 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -103,7 +103,7 @@
. = ..()
var/hurt = TRUE
var/extra_speed = 0
- if(throwingdatum.thrower != src)
+ if(throwingdatum?.thrower != src)
extra_speed = min(max(0, throwingdatum.speed - initial(throw_speed)), 3)
if(GetComponent(/datum/component/tackler))
return
diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm
index 052fd0c87a..2f5a4e01e9 100644
--- a/code/modules/mob/living/carbon/human/emote.dm
+++ b/code/modules/mob/living/carbon/human/emote.dm
@@ -100,7 +100,7 @@
H.dna.species.stop_wagging_tail(H)
/datum/emote/living/carbon/human/wag/can_run_emote(mob/user, status_check = TRUE)
- if(!..())
+ if(!..() || !ishuman(user))
return FALSE
var/mob/living/carbon/human/H = user
return H.dna && H.dna.species && H.dna.species.can_wag_tail(user)
@@ -122,7 +122,7 @@
. = ..()
if(.)
var/mob/living/carbon/human/H = user
- if(findtext(select_message_type(user), "open"))
+ if(H.dna.species.mutant_bodyparts["wings"])
H.OpenWings()
else
H.CloseWings()
@@ -136,7 +136,7 @@
. = "closes " + message
/datum/emote/living/carbon/human/wing/can_run_emote(mob/user, status_check = TRUE)
- if(!..())
+ if(!..() || !ishuman(user))
return FALSE
var/mob/living/carbon/human/H = user
if(H.dna && H.dna.species && (H.dna.features["wings"] != "None"))
diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm
index c835b56b6b..3a28f65d4d 100644
--- a/code/modules/mob/living/carbon/life.dm
+++ b/code/modules/mob/living/carbon/life.dm
@@ -401,7 +401,7 @@
var/obj/item/organ/O = V
if(O)
O.on_life(seconds, times_fired)
- else
+ else if(!QDELETED(src))
if(reagents.has_reagent(/datum/reagent/toxin/formaldehyde, 1) || reagents.has_reagent(/datum/reagent/preservahyde, 1)) // No organ decay if the body contains formaldehyde. Or preservahyde.
return
for(var/V in internal_organs)
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index 8cdf1254db..5db4e123b9 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -109,7 +109,7 @@
/mob/living/hitby(atom/movable/AM, skipcatch, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum)
if(!isitem(AM))
// Filled with made up numbers for non-items.
- if(mob_run_block(AM, 30, "\the [AM.name]", ATTACK_TYPE_PROJECTILE, 0, throwingdatum.thrower, throwingdatum.thrower.zone_selected, list()))
+ if(mob_run_block(AM, 30, "\the [AM.name]", ATTACK_TYPE_THROWN, 0, throwingdatum?.thrower, throwingdatum?.thrower?.zone_selected, list()))
hitpush = FALSE
skipcatch = TRUE
blocked = TRUE
@@ -121,7 +121,7 @@
if(thrown_item.thrownby == WEAKREF(src)) //No throwing stuff at yourself to trigger hit reactions
return ..()
- if(mob_run_block(AM, thrown_item.throwforce, "\the [thrown_item.name]", ATTACK_TYPE_PROJECTILE, 0, throwingdatum.thrower, throwingdatum.thrower.zone_selected, list()))
+ if(mob_run_block(AM, thrown_item.throwforce, "\the [thrown_item.name]", ATTACK_TYPE_THROWN, 0, throwingdatum?.thrower, throwingdatum?.thrower?.zone_selected, list()))
hitpush = FALSE
skipcatch = TRUE
blocked = TRUE
diff --git a/code/modules/mob/living/simple_animal/slime/death.dm b/code/modules/mob/living/simple_animal/slime/death.dm
index 5cac0c630c..e23a0383c7 100644
--- a/code/modules/mob/living/simple_animal/slime/death.dm
+++ b/code/modules/mob/living/simple_animal/slime/death.dm
@@ -1,6 +1,10 @@
/mob/living/simple_animal/slime/death(gibbed)
if(stat == DEAD)
return
+
+ if(buckled)
+ Feedstop(silent = TRUE) //releases ourselves from the mob we fed on.
+
if(!gibbed)
if(is_adult)
var/mob/living/simple_animal/slime/M = new(loc, colour)
@@ -18,9 +22,6 @@
update_name()
return
- if(buckled)
- Feedstop(silent = TRUE) //releases ourselves from the mob we fed on.
-
stat = DEAD
cut_overlays()
diff --git a/code/modules/mob/living/simple_animal/slime/powers.dm b/code/modules/mob/living/simple_animal/slime/powers.dm
index 3624c00e6d..78f3d13436 100644
--- a/code/modules/mob/living/simple_animal/slime/powers.dm
+++ b/code/modules/mob/living/simple_animal/slime/powers.dm
@@ -170,6 +170,9 @@
to_chat(src, "I must be conscious to do this...")
return
+ if(istype(loc, /obj/machinery/computer/camera_advanced/xenobio))
+ return //no you cannot split while you're in the matrix (this prevents GC issues and slimes disappearing)
+
var/list/babies = list()
var/new_nutrition = round(nutrition * 0.9)
var/new_powerlevel = round(powerlevel / 4)
@@ -187,7 +190,7 @@
M.set_nutrition(new_nutrition) //Player slimes are more robust at spliting. Once an oversight of poor copypasta, now a feature!
M.powerlevel = new_powerlevel
if(i != 1)
- step_away(M,src)
+ step_away(M, get_turf(src))
M.Friends = Friends.Copy()
babies += M
M.mutation_chance = clamp(mutation_chance+(rand(5,-5)),0,100)
diff --git a/code/modules/mob/living/simple_animal/slime/slime.dm b/code/modules/mob/living/simple_animal/slime/slime.dm
index bc75a8dd13..54e5d86102 100644
--- a/code/modules/mob/living/simple_animal/slime/slime.dm
+++ b/code/modules/mob/living/simple_animal/slime/slime.dm
@@ -353,6 +353,7 @@
++Friends[user]
else
Friends[user] = 1
+ RegisterSignal(user, COMSIG_PARENT_QDELETING, PROC_REF(clear_friend))
to_chat(user, "You feed the slime the plasma. It chirps happily.")
var/obj/item/stack/sheet/mineral/plasma/S = W
S.use(1)
@@ -416,6 +417,10 @@
visible_message("The mutated core shudders, and collapses into a puddle, unable to maintain its form.")
qdel(src)
+/mob/living/simple_animal/slime/proc/clear_friend(mob/living/friend)
+ UnregisterSignal(friend, COMSIG_PARENT_QDELETING)
+ Friends -= friend
+
/mob/living/simple_animal/slime/proc/apply_water()
adjustBruteLoss(rand(15,20))
if(!client)
diff --git a/code/modules/ninja/suit/ninja_equipment_actions/energy_net_nets.dm b/code/modules/ninja/suit/ninja_equipment_actions/energy_net_nets.dm
index bc0828242e..22b07ac783 100644
--- a/code/modules/ninja/suit/ninja_equipment_actions/energy_net_nets.dm
+++ b/code/modules/ninja/suit/ninja_equipment_actions/energy_net_nets.dm
@@ -37,7 +37,7 @@
return ..()
/obj/structure/energy_net/attack_paw(mob/user)
- return attack_hand()
+ return attack_hand(user)
/obj/structure/energy_net/user_buckle_mob(mob/living/M, mob/user, check_loc = TRUE)
return//We only want our target to be buckled
diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm
index 448fdce496..20f66989ab 100644
--- a/code/modules/reagents/chemistry/reagents/other_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm
@@ -2568,6 +2568,8 @@
/obj/effect/decal/cleanable/semen/update_icon()
. = ..()
+ if(QDELETED(src) || !reagents)
+ return
add_atom_colour(mix_color_from_reagents(reagents.reagent_list), FIXED_COLOUR_PRIORITY)
/datum/reagent/consumable/semen/femcum
diff --git a/code/modules/recycling/disposal/holder.dm b/code/modules/recycling/disposal/holder.dm
index bce1b3d98d..4c6e79f6a2 100644
--- a/code/modules/recycling/disposal/holder.dm
+++ b/code/modules/recycling/disposal/holder.dm
@@ -22,7 +22,8 @@
// initialize a holder from the contents of a disposal unit
/obj/structure/disposalholder/proc/init(obj/machinery/disposal/D)
- gas = D.air_contents// transfer gas resv. into holder object
+ if(istype(D)) // This is sometimes called on non-machinery disposals system types.
+ gas = D.air_contents// transfer gas resv. into holder object
//Check for any living mobs trigger hasmob.
//hasmob effects whether the package goes to cargo or its tagged destination.
diff --git a/code/modules/ruins/objects_and_mobs/ash_walker_den.dm b/code/modules/ruins/objects_and_mobs/ash_walker_den.dm
index d074168b01..528c83aa2d 100644
--- a/code/modules/ruins/objects_and_mobs/ash_walker_den.dm
+++ b/code/modules/ruins/objects_and_mobs/ash_walker_den.dm
@@ -62,7 +62,7 @@
deadmind = H.get_ghost(FALSE, TRUE)
to_chat(deadmind, "Your body has been returned to the nest. You are being remade anew, and will awaken shortly. Your memories will remain intact in your new body, as your soul is being salvaged")
SEND_SOUND(deadmind, sound('sound/magic/enter_blood.ogg',volume=100))
- addtimer(CALLBACK(src, PROC_REF(remake_walker), H.mind, H.real_name), 20 SECONDS)
+ addtimer(CALLBACK(src, PROC_REF(remake_walker), H.mind, H.real_name, H.gender), 20 SECONDS) // SPLURT edit, adds H.gender as an argument.
new /obj/effect/gibspawner/generic(get_turf(H))
qdel(H)
return
diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm
index fc165c4fb0..17ab237b9f 100644
--- a/code/modules/surgery/bodyparts/_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/_bodyparts.dm
@@ -636,7 +636,6 @@
/obj/item/bodypart/proc/update_limb(dropping_limb, mob/living/carbon/source)
body_markings_list = list()
var/mob/living/carbon/C
- owner.create_weakref()
if(source)
C = source
if(!original_owner)
diff --git a/code/modules/vehicles/_vehicle.dm b/code/modules/vehicles/_vehicle.dm
index 4925cf40e7..c00357245a 100644
--- a/code/modules/vehicles/_vehicle.dm
+++ b/code/modules/vehicles/_vehicle.dm
@@ -20,18 +20,14 @@
var/emulate_door_bumps = TRUE //when bumping a door try to make occupants bump them to open them.
var/default_driver_move = TRUE //handle driver movement instead of letting something else do it like riding datums.
var/enclosed = FALSE // is the rider protected from bullets? assume no
- var/list/autogrant_actions_passenger //plain list of typepaths
- var/list/autogrant_actions_controller //assoc list "[bitflag]" = list(typepaths)
- var/list/mob/occupant_actions //assoc list mob = list(type = action datum assigned to mob)
+ var/list/autogrant_actions_passenger = list() //plain list of typepaths
+ var/list/autogrant_actions_controller = list() //assoc list "[bitflag]" = list(typepaths)
+ var/list/mob/occupant_actions = list() //assoc list mob = list(type = action datum assigned to mob)
var/obj/vehicle/trailer
var/mouse_pointer //do we have a special mouse
/obj/vehicle/Initialize(mapload)
. = ..()
- occupants = list()
- autogrant_actions_passenger = list()
- autogrant_actions_controller = list()
- occupant_actions = list()
generate_actions()
/obj/vehicle/examine(mob/user)
@@ -79,7 +75,7 @@
return is_occupant(M) && occupants[M] & VEHICLE_CONTROL_DRIVE
/obj/vehicle/proc/is_occupant(mob/M)
- return !isnull(occupants[M])
+ return !isnull(occupants?[M])
/obj/vehicle/proc/add_occupant(mob/M, control_flags)
if(!istype(M) || is_occupant(M))
diff --git a/html/changelogs/archive/2024-04.yml b/html/changelogs/archive/2024-04.yml
new file mode 100644
index 0000000000..82df78f725
--- /dev/null
+++ b/html/changelogs/archive/2024-04.yml
@@ -0,0 +1,6 @@
+2024-04-02:
+ ariaworld:
+ - bugfix: Sleep now heals instead of dealing damage.
+2024-04-06:
+ ariaworld:
+ - bugfix: Ported monkey and slime xenobio GC fixes from Paradise Station.
diff --git a/modular_sand/code/datums/interactions/interaction_datums/lewd/breasts.dm b/modular_sand/code/datums/interactions/interaction_datums/lewd/breasts.dm
index b3d662ac75..68b6c262f2 100644
--- a/modular_sand/code/datums/interactions/interaction_datums/lewd/breasts.dm
+++ b/modular_sand/code/datums/interactions/interaction_datums/lewd/breasts.dm
@@ -13,7 +13,6 @@
var/t_His = target.p_their()
var/obj/item/organ/genital/breasts/milkers = user.getorganslot(ORGAN_SLOT_BREASTS)
var/milktype = milkers?.fluid_id
- var/modifier
var/list/lines
if(!milkers || !milktype)
@@ -34,17 +33,7 @@
playlewdinteractionsound(get_turf(user), pick('modular_sand/sound/interactions/oral1.ogg',
'modular_sand/sound/interactions/oral2.ogg'), 70, 1, -1)
- switch(milkers.size)
- if("c", "d", "e")
- modifier = 2
- if("f", "g", "h")
- modifier = 3
- else
- if(milkers.size in GLOB.breast_values)
- modifier = clamp(GLOB.breast_values[milkers.size] - 5, 0, INFINITY)
- else
- modifier = 1
- target.reagents.add_reagent(milktype, rand(1,3 * modifier))
+ target.reagents.add_reagent(milktype, rand(1,3 * milkers.get_lactation_amount_modifier()))
/datum/interaction/lewd/titgrope
description = "Grope their breasts."
@@ -79,18 +68,7 @@
var/milktype = milkers?.fluid_id
if(milkers && milktype)
- var/modifier
- switch(milkers.size)
- if("c", "d", "e")
- modifier = 2
- if("f", "g", "h")
- modifier = 3
- else
- if(milkers.size in GLOB.breast_values) //SPLURT edit - global breast values
- modifier = clamp(GLOB.breast_values[milkers.size] - 5, 0, INFINITY)
- else
- modifier = 1
- liquid_container.reagents.add_reagent(milktype, rand(1,3 * modifier))
+ liquid_container.reagents.add_reagent(milktype, rand(1,3 * milkers.get_lactation_amount_modifier()))
user.visible_message(span_lewd("\The [user] milks [target]'s breasts into \the [liquid_container]."), ignored_mobs = user.get_unconsenting())
playlewdinteractionsound(get_turf(user), 'modular_sand/sound/interactions/squelch1.ogg', 50, 1, -1)
diff --git a/modular_sand/code/datums/interactions/interaction_datums/lewd/nipsuck.dm b/modular_sand/code/datums/interactions/interaction_datums/lewd/nipsuck.dm
index 8b65b38272..7078074466 100644
--- a/modular_sand/code/datums/interactions/interaction_datums/lewd/nipsuck.dm
+++ b/modular_sand/code/datums/interactions/interaction_datums/lewd/nipsuck.dm
@@ -7,135 +7,95 @@
interaction_sound = null
/datum/interaction/lewd/nipsuck/display_interaction(mob/living/carbon/human/user, mob/living/carbon/human/target)
- if((user.a_intent == INTENT_HELP) || (user.a_intent == INTENT_DISARM))
- user.visible_message(
- pick(span_lewd("\The [user] gently sucks on \the [target]'s [pick("nipple", "nipples")]."),
- span_lewd("\The [user] gently nibs \the [target]'s [pick("nipple", "nipples")]."),
- span_lewd("\The [user] licks \the [target]'s [pick("nipple", "nipples")].")))
- var/has_breasts = target.has_breasts()
- if(has_breasts == TRUE || has_breasts == HAS_EXPOSED_GENITAL)
- var/modifier = 1
- var/obj/item/organ/genital/breasts/B = target.getorganslot(ORGAN_SLOT_BREASTS)
- switch(B.size)
- if("c", "d", "e")
- modifier = 2
- if("f", "g", "h")
- modifier = 3
- else
- if(B.size in GLOB.breast_values)
- modifier = clamp(GLOB.breast_values[B.size] - 5, 0, INFINITY)
- else
- modifier = 1
- if(B.fluid_id)
- user.reagents.add_reagent(B.fluid_id, rand(1,2 * modifier) * user.get_fluid_mod(B)) //SPLURT edit
-
- if(user.a_intent == INTENT_HARM)
- user.visible_message(
- pick(span_lewd("\The [user] bites \the [target]'s [pick("nipple", "nipples")]."),
- span_lewd("\The [user] aggressively sucks \the [target]'s [pick("nipple", "nipples")].")))
- var/has_breasts = target.has_breasts()
- if(has_breasts == TRUE || has_breasts == HAS_EXPOSED_GENITAL)
- var/modifier = 1
- var/obj/item/organ/genital/breasts/B = target.getorganslot(ORGAN_SLOT_BREASTS)
- switch(B.size)
- if("c", "d", "e")
- modifier = 2
- if("f", "g", "h")
- modifier = 3
- else
- if(B.size in GLOB.breast_values)
- modifier = clamp(GLOB.breast_values[B.size] - 5, 0, INFINITY)
- else
- modifier = 1
- if(B.fluid_id)
- user.reagents.add_reagent(B.fluid_id, rand(1,3 * modifier)) //aggressive sucking leads to high rewards
-
- if(user.a_intent == INTENT_GRAB)
- user.visible_message(
- pick(span_lewd("\The [user] sucks \the [target]'s [pick("nipple", "nipples")] intently."),
- span_lewd("\The [user] feasts \the [target]'s [pick("nipple", "nipples")]."),
- span_lewd("\The [user] glomps \the [target]'s [pick("nipple", "nipples")].")))
- var/has_breasts = target.has_breasts()
- if(has_breasts == TRUE || has_breasts == HAS_EXPOSED_GENITAL)
- var/modifier = 1
- var/obj/item/organ/genital/breasts/B = target.getorganslot(ORGAN_SLOT_BREASTS)
- switch(B.size)
- if("c", "d", "e")
- modifier = 2
- if("f", "g", "h")
- modifier = 3
- else
- if(B.size in GLOB.breast_values)
- modifier = clamp(GLOB.breast_values[B.size] - 5, 0, INFINITY)
- else
- modifier = 1
- if(B.fluid_id)
- user.reagents.add_reagent(B.fluid_id, rand(1,3 * modifier)) //aggressive sucking leads to high rewards
+ var/user_message
+ var/amount_high = 2
+ switch(user.a_intent)
+ if(INTENT_HELP, INTENT_DISARM)
+ user_message = pick(span_lewd("\The [user] gently sucks on \the [target]'s [pick("nipple", "nipples")]."),
+ span_lewd("\The [user] gently nibs \the [target]'s [pick("nipple", "nipples")]."),
+ span_lewd("\The [user] licks \the [target]'s [pick("nipple", "nipples")]."))
+ if(INTENT_HARM)
+ amount_high = 3 // aggressive sucking has higher rewards
+ user_message = pick(span_lewd("\The [user] bites \the [target]'s [pick("nipple", "nipples")]."),
+ span_lewd("\The [user] aggressively sucks \the [target]'s [pick("nipple", "nipples")]."))
+ if(INTENT_GRAB)
+ amount_high = 3 // aggressive sucking has higher rewards
+ user_message = pick(span_lewd("\The [user] sucks \the [target]'s [pick("nipple", "nipples")] intently."),
+ span_lewd("\The [user] feasts \the [target]'s [pick("nipple", "nipples")]."),
+ span_lewd("\The [user] glomps \the [target]'s [pick("nipple", "nipples")]."))
+ user.visible_message(user_message)
+ var/has_breasts = target.has_breasts()
+ if(has_breasts == TRUE || has_breasts == HAS_EXPOSED_GENITAL)
+ var/obj/item/organ/genital/breasts/B = target.getorganslot(ORGAN_SLOT_BREASTS)
+ var/modifier = B?.get_lactation_amount_modifier() || 1
+ if(B.fluid_id)
+ user.reagents.add_reagent(B.fluid_id, rand(1,amount_high * modifier) * user.get_fluid_mod(B))
if(prob(5 + target.get_lust()))
- if(target.a_intent == INTENT_HELP)
- if(!target.has_breasts())
- user.visible_message(
- pick(span_lewd("\The [target] shivers in arousal."),
- span_lewd("\The [target] moans quietly."),
- span_lewd("\The [target] breathes out a soft moan."),
- span_lewd("\The [target] gasps."),
- span_lewd("\The [target] shudders softly."),
- span_lewd("\The [target] trembles as their chest gets molested.")))
- else
- user.visible_message(
- pick(span_lewd("\The [target] shivers in arousal."),
- span_lewd("\The [target] moans quietly."),
- span_lewd("\The [target] breathes out a soft moan."),
- span_lewd("\The [target] gasps."),
- span_lewd("\The [target] shudders softly."),
- span_lewd("\The [target] trembles as their breasts get molested."),
- span_lewd("\The [target] quivers in arousal as \the [user] delights themselves on their milk.")))
- if(target.get_lust() < 5)
- target.handle_post_sex(5, CUM_TARGET_MOUTH, user, ORGAN_SLOT_BREASTS) //SPLURT edit
- if(target.a_intent == INTENT_DISARM)
- if (target.restrained())
+ switch(target.a_intent)
+ if(INTENT_HELP)
if(!target.has_breasts())
user.visible_message(
- pick(span_lewd("\The [target] twists playfully against the restraints."),
- span_lewd("\The [target] squirms away from \the [user]'s mouth."),
- span_lewd("\The [target] slides back from \the [user]'s mouth."),
- span_lewd("\The [target] thrusts their bare chest forward into \the [user]'s mouth.")))
+ pick(span_lewd("\The [target] shivers in arousal."),
+ span_lewd("\The [target] moans quietly."),
+ span_lewd("\The [target] breathes out a soft moan."),
+ span_lewd("\The [target] gasps."),
+ span_lewd("\The [target] shudders softly."),
+ span_lewd("\The [target] trembles as their chest gets molested.")))
else
user.visible_message(
- pick(span_lewd("\The [target] twists playfully against the restraints."),
- span_lewd("\The [target] squirms away from \the [user]'s mouth."),
- span_lewd("\The [target] slides back from \the [user]'s mouth."),
- span_lewd("\The [target] thrust their bare breasts forward into \the [user]'s mouth.")))
- else
- if(!target.has_breasts())
- user.visible_message(
- pick(span_lewd("\The [target] playfully shoos away \the [user]'s head."),
- span_lewd("\The [target] squirms away from \the [user]'s mouth."),
- span_lewd("\The [target] holds \the [user]'s head against their chest."),
- span_lewd("\The [target] teasingly caresses \the [user]'s neck.")))
+ pick(span_lewd("\The [target] shivers in arousal."),
+ span_lewd("\The [target] moans quietly."),
+ span_lewd("\The [target] breathes out a soft moan."),
+ span_lewd("\The [target] gasps."),
+ span_lewd("\The [target] shudders softly."),
+ span_lewd("\The [target] trembles as their breasts get molested."),
+ span_lewd("\The [target] quivers in arousal as \the [user] delights themselves on their milk.")))
+ if(target.get_lust() < 5)
+ target.handle_post_sex(5, CUM_TARGET_MOUTH, user, ORGAN_SLOT_BREASTS) //SPLURT edit
+ if(INTENT_DISARM)
+ if (target.restrained())
+ if(!target.has_breasts())
+ user.visible_message(
+ pick(span_lewd("\The [target] twists playfully against the restraints."),
+ span_lewd("\The [target] squirms away from \the [user]'s mouth."),
+ span_lewd("\The [target] slides back from \the [user]'s mouth."),
+ span_lewd("\The [target] thrusts their bare chest forward into \the [user]'s mouth.")))
+ else
+ user.visible_message(
+ pick(span_lewd("\The [target] twists playfully against the restraints."),
+ span_lewd("\The [target] squirms away from \the [user]'s mouth."),
+ span_lewd("\The [target] slides back from \the [user]'s mouth."),
+ span_lewd("\The [target] thrust their bare breasts forward into \the [user]'s mouth.")))
else
- user.visible_message(
- pick(span_lewd("\The [target] playfully shoos away \the [user]'s head."),
- span_lewd("\The [target] squirms away from \the [user]'s mouth."),
- span_lewd("\The [target] holds \the [user]'s head against their breast."),
- span_lewd("\The [target] teasingly caresses \the [user]'s neck."),
- span_lewd("\The [target] rubs their breasts against \the [user]'s head.")))
- if(target.get_lust() < 10)
- target.handle_post_sex(NORMAL_LUST, CUM_TARGET_MOUTH, user, ORGAN_SLOT_BREASTS) //SPLURT edit
- if(target.a_intent == INTENT_GRAB)
- user.visible_message(
- pick(span_lewd("\The [target] grips \the [user]'s head tight."),
- span_lewd("\The [target] digs nails into \the [user]'s scalp."),
- span_lewd("\The [target] grabs and shoves \the [user]'s head away.")))
- if(target.a_intent == INTENT_HARM)
- user.adjustBruteLoss(1)
- user.visible_message(
- pick(span_lewd("\The [target] slaps \the [user] away."),
- span_lewd("\The [target] scratches [user]'s face."),
- span_lewd("\The [target] fiercely struggles against [user]."),
- span_lewd("\The [target] claws [user]'s face, drawing blood."),
- span_lewd("\The [target] elbows [user]'s mouth away.")))
+ if(!target.has_breasts())
+ user.visible_message(
+ pick(span_lewd("\The [target] playfully shoos away \the [user]'s head."),
+ span_lewd("\The [target] squirms away from \the [user]'s mouth."),
+ span_lewd("\The [target] holds \the [user]'s head against their chest."),
+ span_lewd("\The [target] teasingly caresses \the [user]'s neck.")))
+ else
+ user.visible_message(
+ pick(span_lewd("\The [target] playfully shoos away \the [user]'s head."),
+ span_lewd("\The [target] squirms away from \the [user]'s mouth."),
+ span_lewd("\The [target] holds \the [user]'s head against their breast."),
+ span_lewd("\The [target] teasingly caresses \the [user]'s neck."),
+ span_lewd("\The [target] rubs their breasts against \the [user]'s head.")))
+ if(target.get_lust() < 10)
+ target.handle_post_sex(NORMAL_LUST, CUM_TARGET_MOUTH, user, ORGAN_SLOT_BREASTS) //SPLURT edit
+ if(INTENT_GRAB)
+ user.visible_message(
+ pick(span_lewd("\The [target] grips \the [user]'s head tight."),
+ span_lewd("\The [target] digs nails into \the [user]'s scalp."),
+ span_lewd("\The [target] grabs and shoves \the [user]'s head away.")))
+ if(INTENT_HARM)
+ user.adjustBruteLoss(1)
+ user.visible_message(
+ pick(span_lewd("\The [target] slaps \the [user] away."),
+ span_lewd("\The [target] scratches [user]'s face."),
+ span_lewd("\The [target] fiercely struggles against [user]."),
+ span_lewd("\The [target] claws [user]'s face, drawing blood."),
+ span_lewd("\The [target] elbows [user]'s mouth away.")))
target.dir = get_dir(target, user)
user.dir = get_dir(user, target)
playlewdinteractionsound(get_turf(user), pick('modular_sand/sound/interactions/oral1.ogg',
diff --git a/modular_sand/code/datums/interactions/interaction_datums/lewd/self/breasts.dm b/modular_sand/code/datums/interactions/interaction_datums/lewd/self/breasts.dm
index 20927a02ae..7f5c37619c 100644
--- a/modular_sand/code/datums/interactions/interaction_datums/lewd/self/breasts.dm
+++ b/modular_sand/code/datums/interactions/interaction_datums/lewd/self/breasts.dm
@@ -61,18 +61,7 @@
var/milktype = milkers?.fluid_id
if(milkers && milktype)
- var/modifier
- switch(milkers.size)
- if(3 to 5)
- modifier = 2
- if(6 to 8)
- modifier = 3
- else
- if(milkers.size_to_state() in GLOB.breast_values)
- modifier = clamp(GLOB.breast_values[milkers.size_to_state()] - 5, 0, INFINITY)
- else
- modifier = 1
- liquid_container.reagents.add_reagent(milktype, rand(1,3 * modifier))
+ liquid_container.reagents.add_reagent(milktype, rand(1,3 * milkers.get_lactation_amount_modifier()))
user.visible_message(message = span_lewd("\The [user] [message]."), ignored_mobs = user.get_unconsenting())
playlewdinteractionsound(get_turf(user), 'modular_sand/sound/interactions/squelch1.ogg', 50, 1, -1)
@@ -92,7 +81,6 @@
var/u_His = user.p_their()
var/obj/item/organ/genital/breasts/milkers = user.getorganslot(ORGAN_SLOT_BREASTS)
var/milktype = milkers?.fluid_id
- var/modifier
var/list/lines
if(!milkers || !milktype)
@@ -113,14 +101,4 @@
playlewdinteractionsound(get_turf(user), pick('modular_sand/sound/interactions/oral1.ogg',
'modular_sand/sound/interactions/oral2.ogg'), 70, 1, -1)
- switch(milkers.size)
- if("c", "d", "e")
- modifier = 2
- if("f", "g", "h")
- modifier = 3
- else
- if(milkers.size in GLOB.breast_values)
- modifier = clamp(GLOB.breast_values[milkers.size] - 5, 0, INFINITY)
- else
- modifier = 1
- user.reagents.add_reagent(milktype, rand(1,3 * modifier) * user.get_fluid_mod(milkers)) //SPLURT edit
+ user.reagents.add_reagent(milktype, rand(1,3 * milkers.get_lactation_amount_modifier()) * user.get_fluid_mod(milkers)) //SPLURT edit
diff --git a/modular_sand/code/game/objects/effects/decals/cleanable/lewd_decals.dm b/modular_sand/code/game/objects/effects/decals/cleanable/lewd_decals.dm
index 9f71125dce..9c3ca35400 100644
--- a/modular_sand/code/game/objects/effects/decals/cleanable/lewd_decals.dm
+++ b/modular_sand/code/game/objects/effects/decals/cleanable/lewd_decals.dm
@@ -10,15 +10,17 @@
/obj/effect/decal/cleanable/semendrip/replace_decal(obj/effect/decal/cleanable/semendrip/C)
. = ..()
- reagents.trans_to(C, reagents.total_volume)
- transfer_blood_dna(C.blood_DNA)
+ if(!. || QDELETED(src))
+ return FALSE
var/obj/effect/decal/cleanable/semen/S = (locate(/obj/effect/decal/cleanable/semen) in C.loc)
- if(S)
- C.reagents.trans_to(S, C.reagents.total_volume)
- C.transfer_blood_dna(S.blood_DNA)
- C.update_icon()
- return
- if(C.reagents.total_volume >= 10)
+ if(S) // Merge ourselves into this puddle.
+ reagents.trans_to(S, reagents.total_volume)
+ S.transfer_blood_dna(blood_DNA)
+ update_icon()
+ return TRUE
+ reagents.trans_to(C, reagents.total_volume)
+ C.transfer_blood_dna(blood_DNA)
+ if(C.reagents.total_volume >= 10) // Turn the drip into a puddle.
S = new(C.loc)
C.reagents.trans_to(S, C.reagents.total_volume)
C.transfer_blood_dna(S.blood_DNA)
diff --git a/modular_sand/code/game/objects/items/robot/robot_upgrades.dm b/modular_sand/code/game/objects/items/robot/robot_upgrades.dm
index 1dcdf6deff..83e4e15ab1 100644
--- a/modular_sand/code/game/objects/items/robot/robot_upgrades.dm
+++ b/modular_sand/code/game/objects/items/robot/robot_upgrades.dm
@@ -101,38 +101,6 @@
R.module.basic_modules += PD
R.module.add_module(PD, FALSE, TRUE)
-/obj/item/borg/upgrade/premiumka
- name = "mining cyborg premium KA"
- desc = "A premium kinetic accelerator replacement for the mining module's standard kinetic accelerator."
- icon_state = "cyborg_upgrade3"
- require_module = TRUE
- module_type = list(/obj/item/robot_module/miner)
- module_flags = BORG_MODULE_MINER
-
-/obj/item/borg/upgrade/premiumka/action(mob/living/silicon/robot/R, user = usr)
- . = ..()
- if(.)
- for(var/obj/item/gun/energy/kinetic_accelerator/cyborg/KA in R.module)
- for(var/obj/item/borg/upgrade/modkit/M in KA.modkits)
- M.uninstall(src)
- R.module.remove_module(KA, TRUE)
-
- var/obj/item/gun/energy/kinetic_accelerator/premiumka/cyborg/PKA = new /obj/item/gun/energy/kinetic_accelerator/premiumka/cyborg(R.module)
- R.module.basic_modules += PKA
- R.module.add_module(PKA, FALSE, TRUE)
-
-/obj/item/borg/upgrade/premiumka/deactivate(mob/living/silicon/robot/R, user = usr)
- . = ..()
- if (.)
- for(var/obj/item/gun/energy/kinetic_accelerator/premiumka/cyborg/PKA in R.module)
- for(var/obj/item/borg/upgrade/modkit/M in PKA.modkits)
- M.uninstall(src)
- R.module.remove_module(PKA, TRUE)
-
- var/obj/item/gun/energy/kinetic_accelerator/cyborg/KA = new (R.module)
- R.module.basic_modules += KA
- R.module.add_module(KA, FALSE, TRUE)
-
/obj/item/borg/upgrade/expand/action(mob/living/silicon/robot/R, user = usr)
. = ..()
if(.)
diff --git a/modular_sand/code/modules/mining/equipment/kinetic_crusher.dm b/modular_sand/code/modules/mining/equipment/kinetic_crusher.dm
index 7d22f6e329..d0befc49dc 100644
--- a/modular_sand/code/modules/mining/equipment/kinetic_crusher.dm
+++ b/modular_sand/code/modules/mining/equipment/kinetic_crusher.dm
@@ -374,7 +374,7 @@
if(!QDELETED(target))
var/obj/item/crusher_trophy/T = t
T.on_melee_hit(target, user)
- if(!QDELETED(C) && !QDELETED(target))
+ if(C && !QDELING(C) && !QDELETED(target)) // C can be 0 here, and QDELETED will runtime if that's the case.
C.total_damage += target_health - target.health //we did some damage, but let's not assume how much we did
/obj/item/melee/zweihander/afterattack(atom/target, mob/living/user, proximity_flag, clickparams)
@@ -410,18 +410,18 @@
var/obj/item/crusher_trophy/T = t
T.on_mark_detonation(target, user)
if(!QDELETED(L))
- if(!QDELETED(C))
+ if(C && !QDELING(C)) // C can be 0 here, and QDELETED will runtime if that's the case.
C.total_damage += target_health - L.health //we did some damage, but let's not assume how much we did
new /obj/effect/temp_visual/kinetic_blast(get_turf(L))
var/backstab_dir = get_dir(user, L)
var/def_check = L.getarmor(type = "bomb")
if((user.dir & backstab_dir) && (L.dir & backstab_dir))
- if(!QDELETED(C))
+ if(C && !QDELING(C)) // See above.
C.total_damage += detonation_damage + backstab_bonus //cheat a little and add the total before killing it, so certain mobs don't have much lower chances of giving an item
L.apply_damage(detonation_damage + backstab_bonus, BRUTE, blocked = def_check)
playsound(user, 'sound/weapons/kenetic_accel.ogg', 100, 1) //Seriously who spelled it wrong
else
- if(!QDELETED(C))
+ if(C && !QDELING(C)) // See above.
C.total_damage += detonation_damage
L.apply_damage(detonation_damage, BRUTE, blocked = def_check)
diff --git a/modular_sand/code/modules/resize/resizing.dm b/modular_sand/code/modules/resize/resizing.dm
index 33ea66cfa7..1bf0c9f440 100644
--- a/modular_sand/code/modules/resize/resizing.dm
+++ b/modular_sand/code/modules/resize/resizing.dm
@@ -7,10 +7,8 @@
return FALSE
//Micro is on a table.
- var/turf/steppyspot = target.loc
- for(var/thing in steppyspot.contents)
- if(istype(thing, /obj/structure/table))
- return TRUE
+ if(locate(/obj/structure/table) in target.loc)
+ return TRUE
//Both small.
if(get_size(user) <= RESIZE_A_TINYMICRO && get_size(target) <= RESIZE_A_TINYMICRO)
@@ -48,10 +46,8 @@
return FALSE
//If on a table, don't
- var/turf/steppyspot = target.loc
- for(var/thing in steppyspot.contents)
- if(istype(thing, /obj/structure/table))
- return TRUE
+ if(locate(/obj/structure/table) in target.loc)
+ return TRUE
//Both small
if(get_size(user) <= RESIZE_A_TINYMICRO && get_size(target) <= RESIZE_A_TINYMICRO)
diff --git a/modular_splurt/code/_globalvars/lists/global_lewd.dm b/modular_splurt/code/_globalvars/lists/global_lewd.dm
index 7883a8ecb4..a2b49a5e21 100644
--- a/modular_splurt/code/_globalvars/lists/global_lewd.dm
+++ b/modular_splurt/code/_globalvars/lists/global_lewd.dm
@@ -16,4 +16,4 @@ GLOBAL_LIST_INIT(balls_nouns, list("balls", "nuts", "ballsack", "testicles", "sa
GLOBAL_LIST_INIT(butt_nouns, list("ass", "butt", "dumptruck", "tush", "badonk", "booty", "rump", "behind"))
-GLOBAL_LIST_INIT(breast_values, list("a" = 1, "b" = 2, "c" = 3, "d" = 4, "e" = 5, "f" = 6, "g" = 7, "h" = 8, "i" = 9, "j" = 10, "k" = 11, "l" = 12, "m" = 13, "n" = 14, "o" = 15, "huge" = 16, "massive" = 17, "giga" = 25, "impossible" = 30, "flat" = 0))
+GLOBAL_LIST_INIT(breast_values, list("flat" = 0, "a" = 1, "b" = 2, "c" = 3, "d" = 4, "e" = 5, "f" = 6, "g" = 7, "h" = 8, "i" = 9, "j" = 10, "k" = 11, "l" = 12, "m" = 13, "n" = 14, "o" = 15, "huge" = 16, "massive" = 17, "giga" = 25, "impossible" = 30))
diff --git a/modular_splurt/code/datums/genitals/genitals_interface.dm b/modular_splurt/code/datums/genitals/genitals_interface.dm
index 2c90652977..5505fa043e 100644
--- a/modular_splurt/code/datums/genitals/genitals_interface.dm
+++ b/modular_splurt/code/datums/genitals/genitals_interface.dm
@@ -1,5 +1,5 @@
/// Attempts to open the tgui menu
-/mob/living/verb/genital_menu()
+/mob/living/carbon/verb/genital_menu()
set name = "Genitals Menu"
set desc = "Manage your genital, or someone else's."
set category = "IC"
diff --git a/modular_splurt/code/datums/traits/trait_actions.dm b/modular_splurt/code/datums/traits/trait_actions.dm
index 251d300b22..7b86b1ca2f 100644
--- a/modular_splurt/code/datums/traits/trait_actions.dm
+++ b/modular_splurt/code/datums/traits/trait_actions.dm
@@ -1507,7 +1507,8 @@
var/mob/living/carbon/human/action_mob = owner
// Add outline effect
- action_mob.add_filter("rad_fiend_glow", 1, list("type" = "outline", "color" = glow_color+"30", "size" = glow_range))
+ if(glow_color && glow_range)
+ action_mob.add_filter("rad_fiend_glow", 1, list("type" = "outline", "color" = glow_color+"30", "size" = glow_range))
/datum/action/cosglow/update_glow/Remove()
. = ..()
@@ -1532,16 +1533,19 @@
glow_color = (input_color ? input_color : glow_color)
// Ask user for range input
- var/input_range = input(action_mob, "How much do you glow? Value may range between 1 to 2.", "Select Glow Range", glow_range) as num|null
+ var/input_range = input(action_mob, "How much do you glow? Value may range between 0 to 4. 0 disables glow.", "Select Glow Range", glow_range) as num|null
// Check if range input was given
- // Reset to stored color when not given input
- // Input is clamped in the 1-4 range
- glow_range = (input_range ? clamp(input_range, 0, 4) : glow_range) //More customisable, so you know when you're looking at someone with Radfiend (doom) or a normal player.
+ // Disable glow if input is 0.
+ // Reset to stored range when input is null.
+ // Input is clamped in the 0-4 range
+ glow_range = isnull(input_range) ? glow_range : clamp(input_range, 0, 4) //More customisable, so you know when you're looking at someone with Radfiend (doom) or a normal player.
// Update outline effect
- action_mob.remove_filter("rad_fiend_glow")
- action_mob.add_filter("rad_fiend_glow", 1, list("type" = "outline", "color" = glow_color+"30", "size" = glow_range))
+ if(glow_range && glow_color)
+ action_mob.add_filter("rad_fiend_glow", 1, list("type" = "outline", "color" = glow_color+"30", "size" = glow_range))
+ else
+ action_mob.remove_filter("rad_fiend_glow")
//
// Quirk: Rad Fiend
diff --git a/modular_splurt/code/game/machinery/research_table.dm b/modular_splurt/code/game/machinery/research_table.dm
index b98266d797..25c9087815 100644
--- a/modular_splurt/code/game/machinery/research_table.dm
+++ b/modular_splurt/code/game/machinery/research_table.dm
@@ -139,7 +139,7 @@
for(var/obj/item/organ/genital/genital in buckled_mob.internal_organs)
if(istype(genital, /obj/item/organ/genital/breasts))
var/obj/item/organ/genital/breasts/breasts = genital
- points_awarded += breasts.fluid_rate + GLOB.breast_values[breasts.size]
+ points_awarded += breasts.fluid_rate + breasts.size
continue
points_awarded += genital.fluid_rate + genital.size
points_awarded *= tier
diff --git a/modular_splurt/code/game/objects/items/implants/implant_hide_backpack.dm b/modular_splurt/code/game/objects/items/implants/implant_hide_backpack.dm
index ae8adae0a7..a9c02ea931 100644
--- a/modular_splurt/code/game/objects/items/implants/implant_hide_backpack.dm
+++ b/modular_splurt/code/game/objects/items/implants/implant_hide_backpack.dm
@@ -48,10 +48,9 @@
// Runs on losing the ability
/datum/action/item_action/hide_backpack/Remove(mob/user)
- . = ..()
-
- // Remove the trait
+ // Remove the trait (must be done before removal so that owner still exists)
adjust_trait(FALSE)
+ return ..()
// Function to update trait
/datum/action/item_action/hide_backpack/proc/adjust_trait(state)
diff --git a/modular_splurt/code/game/objects/items/lewd_items/leash.dm b/modular_splurt/code/game/objects/items/lewd_items/leash.dm
index bd661fd00c..9915a6a240 100644
--- a/modular_splurt/code/game/objects/items/lewd_items/leash.dm
+++ b/modular_splurt/code/game/objects/items/lewd_items/leash.dm
@@ -14,9 +14,9 @@ Icons, maybe?
/datum/status_effect/leash_dom
status_type = STATUS_EFFECT_UNIQUE
- alert_type = /obj/screen/alert/status_effect/leash_dom
+ alert_type = /atom/movable/screen/alert/status_effect/leash_dom
-/obj/screen/alert/status_effect/leash_dom
+/atom/movable/screen/alert/status_effect/leash_dom
name = "Leash Master"
desc = "You've got a leash, and a cute pet on the other end."
icon_state = "leash_master" //These call icons that don't exist, so no icon comes up. Which is good.
@@ -24,9 +24,9 @@ Icons, maybe?
/datum/status_effect/leash_freepet
status_type = STATUS_EFFECT_UNIQUE
- alert_type = /obj/screen/alert/status_effect/leash_freepet
+ alert_type = /atom/movable/screen/alert/status_effect/leash_freepet
-/obj/screen/alert/status_effect/leash_freepet
+/atom/movable/screen/alert/status_effect/leash_freepet
name = "Escaped Pet"
desc = "You're on a leash, but you've no master. If anyone grabs the leash they'll gain control!"
icon_state = "leash_freepet"
@@ -36,16 +36,15 @@ Icons, maybe?
id = "leashed"
status_type = STATUS_EFFECT_UNIQUE
var/mob/redirect_component
- alert_type = /obj/screen/alert/status_effect/leash_pet
+ alert_type = /atom/movable/screen/alert/status_effect/leash_pet
-/obj/screen/alert/status_effect/leash_pet
+/atom/movable/screen/alert/status_effect/leash_pet
name = "Leashed Pet"
desc = "You're on the hook now! Be good for your master."
icon_state = "leash_pet"
/datum/status_effect/leash_pet/on_apply()
- //redirect_component = WEAKREF(owner.AddComponent(/datum/component/redirect, list(COMSIG_LIVING_RESIST = CALLBACK(src, PROC_REF(owner_resist)))))
RegisterSignal(owner, COMSIG_LIVING_RESIST, PROC_REF(owner_resist))
redirect_component = owner
if(!owner.stat)
@@ -79,13 +78,36 @@ Icons, maybe?
force = 1
throwforce = 1
w_class = WEIGHT_CLASS_SMALL
- var/leash_used = 0 //A flag to see if the leash has been used yet, because for some reason picking up an unused leash is weird
- var/mob/living/leash_pet = "null" //Variable to store our pet later
- var/mob/living/leash_master = "null" //And our master too
+ var/mob/living/leash_pet = null //Variable to store our pet later
+ var/mob/living/leash_master = null //And our master too
var/mob/mobhook_leash_pet
var/mob/mobhook_leash_master //Needed to watch for these entities to move
var/mob/mobhook_leash_freepet
- var/leash_location[3] //Three digit list for us to store coordinates later
+
+/obj/item/leash/process(delta_time)
+ if(!leash_pet) //No pet, break loop
+ return PROCESS_KILL
+ if(!(leash_pet.get_item_by_slot(ITEM_SLOT_NECK))) //The pet has slipped their collar and is not the pet anymore.
+ leash_pet.visible_message(
+ span_warning("[leash_pet] has slipped out of their collar!"),
+ span_warning("You have slipped out of your collar!"),
+ target = leash_master,
+ target_message = span_warning("[leash_pet] has slipped out of their collar!")
+ )
+ leash_pet.remove_status_effect(/datum/status_effect/leash_pet)
+
+ if(!leash_pet.has_status_effect(/datum/status_effect/leash_pet)) //If there is no pet, there is no dom. Loop breaks.
+ //QDEL_NULL(mobhook_leash_master)
+ UnregisterSignal(mobhook_leash_master, COMSIG_MOVABLE_MOVED)
+ //QDEL_NULL(mobhook_leash_pet)
+ UnregisterSignal(mobhook_leash_pet, COMSIG_MOVABLE_MOVED)
+ //QDEL_NULL(mobhook_leash_freepet)
+ UnregisterSignal(mobhook_leash_freepet, COMSIG_MOVABLE_MOVED)
+ leash_pet.remove_status_effect(/datum/status_effect/leash_freepet)
+ leash_pet.remove_movespeed_modifier(/datum/movespeed_modifier/leash)
+ leash_master?.remove_status_effect(/datum/status_effect/leash_dom)
+ leash_pet = null
+ return PROCESS_KILL
//Called when someone is clicked with the leash
/obj/item/leash/attack(mob/living/carbon/C, mob/living/user, attackchain_flags, damage_multiplier) //C is the target, user is the one with the leash
@@ -103,15 +125,12 @@ Icons, maybe?
user.apply_status_effect(/datum/status_effect/leash_dom) //Is the leasher
leash_pet = C //Save pet reference for later
leash_master = user //Save dom reference for later
- //mobhook_leash_pet = leash_pet.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src, PROC_REF(on_pet_move))))
RegisterSignal(leash_pet, COMSIG_MOVABLE_MOVED, PROC_REF(on_pet_move))
mobhook_leash_pet = leash_pet
- //mobhook_leash_master = leash_master.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src, PROC_REF(on_master_move))))
RegisterSignal(leash_master, COMSIG_MOVABLE_MOVED, PROC_REF(on_master_move))
mobhook_leash_master = leash_master
- leash_used = 1
if(!leash_pet.has_status_effect(/datum/status_effect/leash_dom)) //Add slowdown if the pet didn't leash themselves
- leash_pet.add_movespeed_modifier(MOVESPEED_ID_LEASH)
+ leash_pet.add_movespeed_modifier(/datum/movespeed_modifier/leash)
for(var/mob/viewing in viewers(user, null))
if(viewing == leash_master)
to_chat(leash_master, span_warning("You have hooked a leash onto [leash_pet]!"))
@@ -120,116 +139,90 @@ Icons, maybe?
if(leash_pet.has_status_effect(/datum/status_effect/leash_dom)) //Pet leashed themself. They are not the dom
leash_pet.apply_status_effect(/datum/status_effect/leash_freepet)
leash_pet.remove_status_effect(/datum/status_effect/leash_dom)
- while(1) //While true loop. The mark of a genius coder. ##MAINLOOP START
- sleep(2) //Check every other tick
- if(leash_pet == "null") //No pet, break loop
- return
- if(!(leash_pet.get_item_by_slot(ITEM_SLOT_NECK))) //The pet has slipped their collar and is not the pet anymore.
- for(var/mob/viewing in viewers(user, null))
- viewing.show_message(span_notice("[leash_pet] has slipped out of their collar!!"), 1)
- to_chat(leash_pet, span_notice("You have slipped out of your collar!"))
- to_chat(loc, span_notice("[leash_pet] has slipped out of their collar!"))
- leash_pet.remove_status_effect(/datum/status_effect/leash_pet)
-
- if(!leash_pet.has_status_effect(/datum/status_effect/leash_pet)) //If there is no pet, there is no dom. Loop breaks.
- //QDEL_NULL(mobhook_leash_master)
- UnregisterSignal(mobhook_leash_master, COMSIG_MOVABLE_MOVED)
- //QDEL_NULL(mobhook_leash_pet)
- UnregisterSignal(mobhook_leash_pet, COMSIG_MOVABLE_MOVED)
- //QDEL_NULL(mobhook_leash_freepet)
- UnregisterSignal(mobhook_leash_freepet, COMSIG_MOVABLE_MOVED)
- if(leash_pet.has_status_effect(/datum/status_effect/leash_freepet))
- leash_pet.remove_status_effect(/datum/status_effect/leash_freepet)
- if(leash_pet.has_movespeed_modifier(MOVESPEED_ID_LEASH))
- leash_pet.remove_movespeed_modifier(MOVESPEED_ID_LEASH)
- if(!leash_master == "null")
- leash_master.remove_status_effect(/datum/status_effect/leash_dom)
- leash_used = 0 //reset the leash to neutral
- leash_pet = "null"
- return
-
+ START_PROCESSING(SSfastprocess, src) // The original while loop here ran every 2 deciseconds, and so does SSfastprocess.
else //No collar, no fun
- var/leash_message = pick("Your pet needs a collar")
+ var/leash_message = pick("[C] needs a collar before you can attach a leash to it.")
to_chat(user, span_notice("[leash_message]"))
//Called when the leash is used in hand
//Tugs the pet closer
/obj/item/leash/attack_self(mob/living/user)
- if(!leash_pet == "null") //No pet, no tug.
+ if(!leash_pet) //No pet, no tug.
return
//Yank the pet. Yank em in close.
apply_tug_mob_to_mob(leash_pet, leash_master, 1)
/obj/item/leash/proc/on_master_move()
+ SIGNAL_HANDLER
//Make sure the dom still has a pet
- if(leash_master == "null") //There must be a master
+ if(!leash_master) //There must be a master
return
- if(leash_pet == "null") //There must be a pet
+ if(!leash_pet) //There must be a pet
return
if(leash_pet == leash_master) //Pet is the master
return
if(!leash_pet.has_status_effect(/datum/status_effect/leash_pet))
- //QDEL_NULL(mobhook_leash_master) //Probably redundant, but it's nice to be safe
UnregisterSignal(mobhook_leash_master, COMSIG_MOVABLE_MOVED)
+ mobhook_leash_master = null
leash_master.remove_status_effect(/datum/status_effect/leash_dom)
return
+ addtimer(CALLBACK(src, PROC_REF(after_master_move)), 0.2 SECONDS)
+/obj/item/leash/proc/after_master_move()
//If the master moves, pull the pet in behind
- sleep(2) //A small sleep so the pet kind of bounces back after they make the step
- //Also, the sleep means that the distance check for master happens before the pet, to prevent both from proccing.
+ //Also, the timer means that the distance check for master happens before the pet, to prevent both from proccing.
- if(leash_master == "null") //Just to stop error messages
+ if(!leash_master) //Just to stop error messages
return
- if(leash_pet == "null")
+ if(!leash_pet)
return
apply_tug_mob_to_mob(leash_pet, leash_master, 2)
//Knock the pet over if they get further behind. Shouldn't happen too often.
sleep(3) //This way running normally won't just yank the pet to the ground.
- if(leash_master == "null") //Just to stop error messages. Break the loop early if something removed the master
+ if(!leash_master) //Just to stop error messages. Break the loop early if something removed the master
return
- if(leash_pet == "null")
+ if(!leash_pet)
return
- if(leash_pet.x > leash_master.x + 3 || leash_pet.x < leash_master.x - 3 || leash_pet.y > leash_master.y + 3 || leash_pet.y < leash_master.y - 3)
- //var/leash_knockdown_message = "[leash_pet] got pulled to the ground by their leash!"
- //to_chat(leash_master, span_notice("[leash_knockdown_message]"))
- //to_chat(leash_pet, span_notice("[leash_knockdown_message]"))
+ if(get_dist(leash_pet, leash_master) > 3)
+ leash_pet.visible_message(
+ span_warning("[leash_pet] is pulled to the ground by their leash!"),
+ span_warning("You are pulled to the ground by your leash!")
+ )
leash_pet.apply_effect(20, EFFECT_KNOCKDOWN, 0)
//This code is to check if the pet has gotten too far away, and then break the leash.
sleep(3) //Wait to snap the leash
- if(leash_master == "null") //Just to stop error messages
+ if(!leash_master) //Just to stop error messages
return
- if(leash_pet == "null")
+ if(!leash_pet)
return
- if(leash_pet.x > leash_master.x + 5 || leash_pet.x < leash_master.x - 5 || leash_pet.y > leash_master.y + 5 || leash_pet.y < leash_master.y - 5)
- var/leash_break_message = "The leash snapped free from [leash_pet]!"
- for(var/mob/viewing in viewers(leash_pet, null))
- if(viewing == leash_master)
- to_chat(leash_master, span_warning("The leash snapped free from your pet!"))
- if(viewing == leash_pet)
- to_chat(leash_pet, span_warning("Your leash has popped from your collar!"))
- else
- viewing.show_message(span_warning("[leash_break_message]"), 1)
+ if(get_dist(leash_pet, leash_master) > 5)
+ leash_pet.visible_message(
+ span_warning("The leash snaps free from [leash_pet]'s collar!"),
+ span_warning("Your leash pops from your collar!"),
+ target = leash_master,
+ target_message = span_warning("The leash snaps free from your pet's collar!")
+ )
leash_pet.apply_effect(20, EFFECT_KNOCKDOWN, 0)
leash_pet.adjustOxyLoss(5)
leash_pet.remove_status_effect(/datum/status_effect/leash_pet)
- leash_pet.remove_movespeed_modifier(MOVESPEED_ID_LEASH)
+ leash_pet.remove_movespeed_modifier(/datum/movespeed_modifier/leash)
leash_master.remove_status_effect(/datum/status_effect/leash_dom)
- //QDEL_NULL(mobhook_leash_master)
UnregisterSignal(mobhook_leash_master, COMSIG_MOVABLE_MOVED)
- //QDEL_NULL(mobhook_leash_pet)
UnregisterSignal(mobhook_leash_pet, COMSIG_MOVABLE_MOVED)
- leash_pet = "null"
- leash_master = "null"
- leash_used = 0
+ mobhook_leash_master = null
+ mobhook_leash_pet = null
+ leash_pet = null
+ leash_master = null
/obj/item/leash/proc/on_pet_move()
+ SIGNAL_HANDLER
//This should only work if there is a pet and a master.
//This is here pretty much just to stop the console from flooding with errors
- if(leash_master == "null")
+ if(!leash_master)
return
- if(leash_pet == "null")
+ if(!leash_pet)
return
//Make sure the pet is still a pet
if(!leash_pet.has_status_effect(/datum/status_effect/leash_pet))
@@ -240,84 +233,43 @@ Icons, maybe?
//The pet has escaped. There is no DOM. GO PET RUN.
if(leash_pet.has_status_effect(/datum/status_effect/leash_freepet))//If the pet is free, break
return
-
//If the pet gets too far away, they get tugged back
- sleep(3)//A small sleep so the pet kind of bounces back after they make the step
- if(leash_master == "null")
+ addtimer(CALLBACK(src, PROC_REF(after_pet_move)), 0.3 SECONDS) //A short timer so the pet kind of bounces back after they make the step
+
+/obj/item/leash/proc/after_pet_move()
+ if(!leash_master)
return
- if(leash_pet == "null")
+ if(!leash_pet)
return
- //West tug
- if(leash_pet.x > leash_master.x + 2)
- step(leash_pet, WEST, 1) //"1" is the speed of movement. We want the tug to be faster than their slow current walk speed.
- //East tug
- if(leash_pet.x < leash_master.x - 2)
- step(leash_pet, EAST, 1)
- //South tug
- if(leash_pet.y > leash_master.y + 2)
- step(leash_pet, SOUTH, 1)
- //North tug
- if(leash_pet.y < leash_master.y - 2)
- step(leash_pet, NORTH, 1)
+ for(var/i in 3 to get_dist(leash_pet, leash_master)) // Move the pet to a minimum of 2 tiles away from the master, so the pet trails behind them.
+ step_towards(leash_pet, leash_master)
/obj/item/leash/proc/on_freepet_move()
+ SIGNAL_HANDLER
//Pet is on the run. Let's drag the leash behind them.
- if(!leash_master == "null") //If there is a master, don't do this
+ if(leash_master) //If there is a master, don't do this
return
- if(leash_pet == "null") //If there is no pet, don't do this
+ if(!leash_pet) //If there is no pet, don't do this
return
- if(leash_pet.is_holding_item_of_type(/obj/item/leash)) //If the pet is holding the leash, don't do this
+ if(leash_pet.is_holding(src)) //If the pet is holding the leash, don't do this
return
- sleep(2)
- if(leash_pet == "null")
+ //If the pet gets too far away, we get tugged to them.
+ addtimer(CALLBACK(src, PROC_REF(after_freepet_move)), 0.2 SECONDS, TIMER_UNIQUE) //A short timer so the leash trails behind us.
+
+/obj/item/leash/proc/after_freepet_move()
+ if(!leash_pet)
return
- //Double move to catch the leash up to the pet
- if(src.x > leash_pet.x + 2)
- . = step(src, WEST, 1)
- if(src.x < leash_pet.x - 2)
- . = step(src, EAST, 1)
- if(src.y > leash_pet.y + 2)
- . = step(src, SOUTH, 1)
- if(src.y < leash_pet.y - 2)
- . = step(src, NORTH, 1)
- //Primary dragging code
- if(src.x > leash_pet.x + 1)
- . = step(src, WEST, 1) //"1" is the speed of movement. We want the tug to be faster than their slow current walk speed.
- if(src.y > leash_pet.y)//Check the other axis, and tug them into alignment so they are behind the pet
- . = step(src, SOUTH, 1)
- if(src.y < leash_pet.y)
- . = step(src, NORTH, 1)
- if(src.x < leash_pet.x - 1)
- . = step(src, EAST, 1)
- if(src.y > leash_pet.y)
- . = step(src, SOUTH, 1)
- if(src.y < leash_pet.y)
- . = step(src, NORTH, 1)
- if(src.y > leash_pet.y + 1)
- . = step(src, SOUTH, 1)
- if(src.x > leash_pet.x)
- . = step(src, WEST, 1)
- if(src.x < leash_pet.x)
- . = step(src, EAST, 1)
- if(src.y < leash_pet.y - 1)
- . = step(src, NORTH, 1)
- if(src.x > leash_pet.x)
- . = step(src, WEST, 1)
- if(src.x < leash_pet.x)
- . = step(src, EAST, 1)
+
+ for(var/i in 3 to get_dist(src, leash_pet)) // Move us to a minimum of 2 tiles away from the pet, so we trail behind them.
+ step_towards(src, leash_pet)
sleep(1)
//Just to prevent error messages
- if(leash_pet == "null")
+ if(!leash_pet)
return
- if(src.x > leash_pet.x + 5 || src.x < leash_pet.x - 5 || src.y > leash_pet.y + 5 || src.y < leash_pet.y - 5)
- var/leash_break_message = "The leash snapped free from [leash_pet]!"
- for(var/mob/viewing in viewers(leash_pet, null))
- if(viewing == leash_pet)
- to_chat(leash_pet, span_warning("Your leash has popped from your collar!"))
- else
- viewing.show_message(span_warning("[leash_break_message]"), 1)
+ if(get_dist(src, leash_pet) > 5)
+ leash_pet.visible_message(span_warning("\The [src] snaps free from \the [leash_pet]!"), span_warning("Your leash pops free from your collar!"))
leash_pet.apply_effect(20, EFFECT_KNOCKDOWN, 0)
leash_pet.adjustOxyLoss(5)
leash_pet.remove_status_effect(/datum/status_effect/leash_pet)
@@ -326,57 +278,62 @@ Icons, maybe?
UnregisterSignal(mobhook_leash_pet, COMSIG_MOVABLE_MOVED)
//QDEL_NULL(mobhook_leash_freepet)
UnregisterSignal(mobhook_leash_freepet, COMSIG_MOVABLE_MOVED)
- leash_pet = "null"
- leash_used = 0
+ leash_pet = null
//The proc below in question is the one causing all the errors apparently
/obj/item/leash/dropped(mob/user, silent)
//Drop the leash, and the leash effects stop
. = ..()
- if(leash_pet == "null") //There is no pet. Stop this silliness
+ if(!leash_pet) //There is no pet. Stop this silliness
return
- if(leash_master == "null")
+ if(!leash_master)
return
//Dropping procs any time the leash changes slots. So, we will wait a tick and see if the leash was actually dropped
addtimer(CALLBACK(src, PROC_REF(drop_effects), user, silent), 1)
/obj/item/leash/proc/drop_effects(mob/user, silent)
- if(leash_master.is_holding_item_of_type(/obj/item/leash) || istype(leash_master.get_item_by_slot(ITEM_SLOT_BELT), /obj/item/leash))
+ SIGNAL_HANDLER
+ if(leash_master.is_holding(src) || leash_master.get_item_by_slot(ITEM_SLOT_BELT) == src)
return //Dom still has the leash as it turns out. Cancel the proc.
- for(var/mob/viewing in viewers(leash_master, null))
- viewing.show_message(span_notice("[leash_master] has dropped the leash."), 1)
+ leash_master.visible_message(span_notice("\The [leash_master] drops \the [src]."), span_notice("You drop \the [src]."))
//DOM HAS DROPPED LEASH. PET IS FREE. SCP HAS BREACHED CONTAINMENT.
- leash_pet.remove_movespeed_modifier(MOVESPEED_ID_LEASH)
- //mobhook_leash_freepet = leash_pet.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src, PROC_REF(on_freepet_move))))
- RegisterSignal(leash_pet, COMSIG_MOVABLE_MOVED, PROC_REF(on_freepet_move))
+ leash_pet.remove_movespeed_modifier(/datum/movespeed_modifier/leash)
+ UnregisterSignal(leash_pet, COMSIG_MOVABLE_MOVED)
mobhook_leash_freepet = leash_pet
+ RegisterSignal(mobhook_leash_freepet, COMSIG_MOVABLE_MOVED, PROC_REF(on_freepet_move))
leash_master.remove_status_effect(/datum/status_effect/leash_dom) //No dom with no leash. We will get a new dom if the leash is picked back up.
- leash_master = "null"
+ leash_master = null
//QDEL_NULL(mobhook_leash_master)
UnregisterSignal(mobhook_leash_master, COMSIG_MOVABLE_MOVED)
/obj/item/leash/equipped(mob/user)
. = ..()
- if(leash_used == 0) //Don't apply statuses with a fresh leash. Keeps things clean on the backend.
+ if(!leash_pet) //Don't apply statuses with a petless leash.
return
addtimer(CALLBACK(src, PROC_REF(equip_effects), user), 2)
/obj/item/leash/proc/equip_effects(mob/user)
- if(leash_pet == "null")
+ if(!leash_pet)
+ return
+ if(leash_master == user)
+ return // Don't double-register.
+ if(leash_pet == user) //Pet picked up their own leash.
+ leash_master = null
return
leash_master = user
- if(leash_master.has_status_effect(/datum/status_effect/leash_freepet) || leash_master.has_status_effect(/datum/status_effect/leash_pet)) //Pet picked up their own leash.
- leash_master = "null"
- return
leash_master.apply_status_effect(/datum/status_effect/leash_dom)
- //mobhook_leash_master = leash_master.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src, PROC_REF(on_master_move))))
RegisterSignal(leash_master, COMSIG_MOVABLE_MOVED, PROC_REF(on_master_move))
mobhook_leash_master = leash_master
leash_pet.remove_status_effect(/datum/status_effect/leash_freepet)
//QDEL_NULL(mobhook_leash_freepet)
- UnregisterSignal(mobhook_leash_freepet, COMSIG_MOVABLE_MOVED)
- leash_pet.add_movespeed_modifier(MOVESPEED_ID_LEASH)
+ if(mobhook_leash_freepet)
+ UnregisterSignal(mobhook_leash_freepet, COMSIG_MOVABLE_MOVED)
+ leash_pet.add_movespeed_modifier(/datum/movespeed_modifier/leash)
+
+/datum/movespeed_modifier/leash
+ id = MOVESPEED_ID_LEASH
+ multiplicative_slowdown = 5
/*/datum/crafting_recipe/leash
name = "Leash"
diff --git a/modular_splurt/code/modules/antagonists/wendigo/mob/metabolization.dm b/modular_splurt/code/modules/antagonists/wendigo/mob/metabolization.dm
index 7293047456..36784c9652 100644
--- a/modular_splurt/code/modules/antagonists/wendigo/mob/metabolization.dm
+++ b/modular_splurt/code/modules/antagonists/wendigo/mob/metabolization.dm
@@ -12,13 +12,13 @@
/*
switch(nutrition)
if(NUTRITION_LEVEL_FULL to INFINITY)
- throw_alert("nutrition", /obj/screen/alert/fat)
+ throw_alert("nutrition", /atom/movable/screen/alert/fat)
if(NUTRITION_LEVEL_HUNGRY to NUTRITION_LEVEL_FULL)
clear_alert("nutrition")
if(NUTRITION_LEVEL_HUNGRY to NUTRITION_LEVEL_STARVING)
- throw_alert("nutrition", /obj/screen/alert/hungry)
+ throw_alert("nutrition", /atom/movable/screen/alert/hungry)
if(0 to NUTRITION_LEVEL_STARVING)
- throw_alert("nutrition", /obj/screen/alert/starving)
+ throw_alert("nutrition", /atom/movable/screen/alert/starving)
*/
/mob/living/carbon/wendigo/reagent_check(datum/reagent/R)
diff --git a/modular_splurt/code/modules/resize/resizing.dm b/modular_splurt/code/modules/resize/resizing.dm
index 9554d1db85..470987941f 100644
--- a/modular_splurt/code/modules/resize/resizing.dm
+++ b/modular_splurt/code/modules/resize/resizing.dm
@@ -1,7 +1,8 @@
/mob/living/handle_micro_bump_other(mob/living/target)
- // If the target is not in combat mode, if the target has the preference off, stop the interaction.
- if(target.a_intent != INTENT_HARM && SEND_SIGNAL(target, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE) && target.client.prefs.stomppref == FALSE)
+ // If the target is not in combat mode, if the target is a player mob with the preference off, stop the interaction.
+ var/datum/preferences/target_prefs = target.client?.prefs || GLOB.preferences_datums[target.ckey]
+ if(target.a_intent != INTENT_HARM && SEND_SIGNAL(target, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE) && (target.ckey && !target_prefs?.stomppref))
return FALSE
// Do the rest of the function normally.
diff --git a/tgui/packages/tgui/interfaces/PlayerPanel2.js b/tgui/packages/tgui/interfaces/PlayerPanel2.js
index 20fc9d34b4..b9c2eca41b 100644
--- a/tgui/packages/tgui/interfaces/PlayerPanel2.js
+++ b/tgui/packages/tgui/interfaces/PlayerPanel2.js
@@ -460,7 +460,7 @@ const GeneralActions = (props, context) => {
color="orange"
content="Smite"
confirmColor="average"
- disabled={!mob_type.includes("/mob/living")}
+ disabled={!mob_type.includes("/mob/living/carbon/human")}
onClick={() => act("smite")}
/>