From 3c82deb0a6e36458dd604a95ccaee27c755331a7 Mon Sep 17 00:00:00 2001 From: HIDgamer Date: Tue, 3 Sep 2024 07:05:44 +0300 Subject: [PATCH 01/14] CPR Buddy 9000 --- code/game/machinery/bots/cprbot.dm | 196 ++++++++++++++++++ .../vending/vendor_types/crew/synthetic.dm | 1 + code/game/objects/items/cprbot.dm | 112 ++++++++++ colonialmarines.dme | 2 + icons/obj/structures/machinery/aibots.dmi | Bin 11156 -> 18615 bytes 5 files changed, 311 insertions(+) create mode 100644 code/game/machinery/bots/cprbot.dm create mode 100644 code/game/objects/items/cprbot.dm diff --git a/code/game/machinery/bots/cprbot.dm b/code/game/machinery/bots/cprbot.dm new file mode 100644 index 000000000000..44c2f85976a1 --- /dev/null +++ b/code/game/machinery/bots/cprbot.dm @@ -0,0 +1,196 @@ +/obj/structure/machinery/bot/cprbot + name = "CPRbot" + desc = "Designed for urgent medical intervention, this CPRbot offers high-tech support in a compact form." + icon = 'icons/obj/structures/machinery/aibots.dmi' + icon_state = "cprbot0" + density = FALSE + anchored = FALSE + health = 100 + maxhealth = 100 + req_access = list(ACCESS_MARINE_MEDBAY) + var/const/search_radius = 10 + var/const/cprbot_proximity_check_radius = 2 // Radius to check for nearby CPRbots + var/processing = TRUE + var/mob/living/carbon/human/target = null + var/cpr_cooldown = 0 + var/path = list() // Path for movement logic + var/currently_healing = 0 + var/iff_signal = FACTION_MARINE // IFF signal to check for valid targets + var/list/medical_facts = list( + "Did you know? The human heart beats over 100,000 times a day.", + "Fun fact: Blood makes up about 7% of your body's weight.", + "Medical trivia: Your brain uses 20% of the oxygen you breathe.", + "Laughter really can increase your pain tolerance.", + "Did you know? The human skeleton is made up of 206 bones.", + "Fun fact: The average adult human body contains about 5 liters of blood.", + "Medical trivia: The human body has around 37.2 trillion cells.", + "The skin is the largest organ in the human body.", + "Did you know? The liver can regenerate itself if a portion is removed.", + "Fun fact: Your sense of smell is closely linked to your memory." + "The only muscle that never tires is that heart." + "Did you know? not breathing can lead to a premature cessation of life!" + ) + var/list/idle_messages = list( + "Stay still, I'm assessing the situation.", + "Just a routine check-up, don't worry.", + "Scanning the area for any casualties.", + "I’m ready to save lives, one compression at a time.", + "I hope everyone is feeling alright today!" + "It's not magic it's CPR Buddy 9000!" + "I should have been a plastic surgeon." + "What kind of medbay is this, everyone’s dropping like flies" + "Each breath a day keeps me at bay!" + "I sense a disturbance in my circuit board. as of a million people stopped breathing and were suddenly silent." + ) + var/motivational_message = "Live! Live! Don't die on me now!" + var/list/has_said_to_patient = list() // Track which patients have been warned + var/last_message_time = 0 // Tracks the last time a message was spoken + +/obj/structure/machinery/bot/cprbot/New() + ..() + spawn(5) // Wait for 5 seconds after spawning before starting initialization + src.initialize_cprbot() + +/obj/structure/machinery/bot/cprbot/proc/initialize_cprbot() + while (processing && health > 0) + src.find_and_move_to_patient() + if (target && world.time >= cpr_cooldown) + src.perform_cpr(target) + src.random_message() // Check if it's time to send a random message + sleep(2) // Slower processing loop, moves once every 2 seconds + +/obj/structure/machinery/bot/cprbot/proc/random_message() + if (world.time >= last_message_time + 600) // At least 1 minute (600 deciseconds) cooldown + if (currently_healing) + src.speak(motivational_message) + else + if (prob(50)) + src.speak(pick(medical_facts)) + else + src.speak(pick(idle_messages)) + last_message_time = world.time // Update the last message time + +/obj/structure/machinery/bot/cprbot/proc/speak(message) + if (!processing || !message) + return + visible_message("[src] beeps, \"[message]\"") + +/obj/structure/machinery/bot/cprbot/proc/find_and_move_to_patient() + var/list/humans = list() + for (var/mob/living/carbon/human/H in range(search_radius)) + if (H.stat == DEAD && H.check_tod() && H.is_revivable() && H.get_target_lock(iff_signal) && !src.has_nearby_cprbot(H)) + humans += H + + if (humans.len > 0) + target = src.get_nearest(humans) + if (target && !has_said_to_patient.Find(target)) + visible_message("[target] is injured! I'm coming!") + has_said_to_patient += target + src.move_to_target(target) + else + target = null + +/obj/structure/machinery/bot/cprbot/proc/has_nearby_cprbot(mob/living/carbon/human/H) + // Check if there are any other CPRbots within a two-tile radius of the target + for (var/obj/structure/machinery/bot/cprbot/nearby_cprbot in range(H, cprbot_proximity_check_radius)) + if (nearby_cprbot != src) // Ignore self + return TRUE + return FALSE + +/obj/structure/machinery/bot/cprbot/proc/get_nearest(list/humans) + var/mob/living/carbon/human/nearest = null + var/distance = search_radius + 1 + + for (var/mob/living/carbon/human/H in humans) + var/d = get_dist(src, H) + if (d < distance) + nearest = H + distance = d + + return nearest + +/obj/structure/machinery/bot/cprbot/proc/move_to_target(mob/living/carbon/human/H) + if (H) + var/pathfinding_result = AStar(src.loc, get_turf(H), /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 30) + if (length(pathfinding_result) == 0) + // No reachable path to the target, so stop looking for this patient + target = null + return + path = pathfinding_result + + // Begin moving towards the target if a path exists + if (get_dist(src, H) > 1) + if (length(path) > 0) + step_to(src, path[1]) + path -= path[1] + spawn(10) // Moves every 10 ticks (approximately 1 second) + if (length(path)) + step_to(src, path[1]) + path -= path[1] + else + currently_healing = 1 + else + // No valid target, stop looking + target = null + + +/obj/structure/machinery/bot/cprbot/proc/perform_cpr(mob/living/carbon/human/H) + if (!H || H.stat != DEAD || !H.is_revivable() || !ishuman_strict(H)) + target = null + icon_state = "cprbot0" + currently_healing = 0 + return + + if (get_dist(src, H) > 1) + src.move_to_target(H) + return + + icon_state = "cprbot_active" + H.revive_grace_period += 4 SECONDS + cpr_cooldown = world.time + 7 SECONDS + H.visible_message(SPAN_NOTICE("[src] automatically performs CPR on [H].")) + H.visible_message(SPAN_DANGER("Currently performing CPR on [H] do not intervene!")) + currently_healing = 1 + + spawn(0.5) + while (H.is_revivable() && H.stat == DEAD && get_dist(src, H) <= 1) + sleep(0.5) + icon_state = "cprbot0" + currently_healing = 0 + +/obj/structure/machinery/bot/cprbot/proc/self_destruct(mob/living/carbon/human/user = null) + var/obj/item/cprbot_item/I = new /obj/item/cprbot_item(src.loc) + + if (user) + if (!user.put_in_active_hand(I)) + if (!user.put_in_inactive_hand(I)) + I.forceMove(src.loc) + else + I.forceMove(src.loc) + + qdel(src) + +/obj/structure/machinery/bot/cprbot/attack_hand(mob/user as mob) + if (..()) + return TRUE + + SEND_SIGNAL(user, COMSIG_LIVING_ATTACKHAND_HUMAN, src) + + if (user != src) + visible_message(SPAN_DANGER("[user] begins to undeploy [src]!")) + src.self_destruct(user) + return TRUE + +/obj/structure/machinery/bot/cprbot/explode() + src.on = 0 + src.visible_message(SPAN_DANGER("[src] blows apart!"), null, null, 1) + var/turf/Tsec = get_turf(src) + + new /obj/item/cprbot_broken(Tsec) + + var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread + s.set_up(3, 1, src) + s.start() + qdel(src) + return + diff --git a/code/game/machinery/vending/vendor_types/crew/synthetic.dm b/code/game/machinery/vending/vendor_types/crew/synthetic.dm index 240155176efa..841a146898ba 100644 --- a/code/game/machinery/vending/vendor_types/crew/synthetic.dm +++ b/code/game/machinery/vending/vendor_types/crew/synthetic.dm @@ -411,6 +411,7 @@ GLOBAL_LIST_INIT(cm_vending_synth_tools, list( list("Maintenance Jack", 15, /obj/item/maintenance_jack, null, VENDOR_ITEM_REGULAR), list("Portable Dialysis Machine", 15, /obj/item/tool/portadialysis, null, VENDOR_ITEM_REGULAR), list("Telescopic Baton", 15, /obj/item/weapon/telebaton, null, VENDOR_ITEM_REGULAR), + list("CPR Buddy 9k", 15, /obj/item/cprbot_item, null, VENDOR_ITEM_REGULAR), )) //------------EXPERIMENTAL TOOL KITS--------------- diff --git a/code/game/objects/items/cprbot.dm b/code/game/objects/items/cprbot.dm new file mode 100644 index 000000000000..32c8c2345cf4 --- /dev/null +++ b/code/game/objects/items/cprbot.dm @@ -0,0 +1,112 @@ +/obj/item/cprbot_item + name = "CPRbot" + desc = "A compact CPRbot 9000 asemply" + icon = 'icons/obj/structures/machinery/aibots.dmi' + icon_state = "cprbot" + w_class = SIZE_MEDIUM + var/deployment_path = /obj/structure/machinery/bot/cprbot + +/obj/item/cprbot_item/proc/deploy_cprbot(mob/user, atom/location) + if(!user || !location) + return + // Attempt to delete the item first + qdel(src) + world << "Deleting item [src]. Deploying CPRbot at [location]." + + // Proceed with the CPRbot deployment + var/obj/structure/machinery/bot/cprbot/R = new deployment_path(location) + if(R) + R.add_fingerprint(user) + world << "CPRbot deployed successfully at [location]." + else + world << "Failed to deploy CPRbot at [location]." + +/obj/item/cprbot_item/attack_self(mob/user) + if (..()) + return TRUE + if(user) + deploy_cprbot(user, user.loc) + +/obj/item/cprbot_item/afterattack(atom/target, mob/user, proximity) + if(proximity && isturf(target)) + var/turf/T = target + if(!T.density) + deploy_cprbot(user, T) + +/obj/item/cprbot_broken + name = "CPRbot" + desc = "A compact CPRbot 9000 asemply it appears to be in bad shape" + icon = 'icons/obj/structures/machinery/aibots.dmi' + icon_state = "cprbot_broken" + w_class = SIZE_MEDIUM + +/obj/item/cprbot_broken/attackby(obj/item/W, mob/living/user) + if(iswelder(W)) + if(!HAS_TRAIT(W, TRAIT_TOOL_BLOWTORCH)) + to_chat(user, SPAN_WARNING("You need a stronger blowtorch!")) + return + + var/obj/item/tool/weldingtool/WT = W + if(!WT.isOn()) + to_chat(user, SPAN_WARNING("\The [WT] needs to be on!")) + return + + if(!WT.remove_fuel(5, user)) // Ensure the welder has enough fuel to operate + to_chat(user, SPAN_NOTICE("You need more welding fuel to complete this task.")) + return + + playsound(src, 'sound/items/Welder.ogg', 25, 1) + + if(!do_after(user, 10 * user.get_skill_duration_multiplier(SKILL_CONSTRUCTION), INTERRUPT_ALL | BEHAVIOR_IMMOBILE, BUSY_ICON_BUILD)) + return + + var/obj/item/cprbot_item/I = new /obj/item/cprbot_item(src.loc) + + if(user) + if(!user.put_in_active_hand(I)) + if(!user.put_in_inactive_hand(I)) + I.forceMove(src.loc) + else + I.forceMove(src.loc) + +/obj/item/cprbot_broken/attackby(obj/item/W, mob/living/user) + if(iswelder(W)) + if(!HAS_TRAIT(W, TRAIT_TOOL_BLOWTORCH)) + to_chat(user, SPAN_WARNING("You need a stronger blowtorch!")) + return + + var/obj/item/tool/weldingtool/WT = W + if(!WT.isOn()) + to_chat(user, SPAN_WARNING("\The [WT] needs to be on!")) + return + + if(!WT.remove_fuel(5, user)) // Ensure enough fuel is available + to_chat(user, SPAN_NOTICE("You need more welding fuel to complete this task.")) + return + + playsound(src, 'sound/items/Welder.ogg', 25, 1) + + if(!do_after(user, 10 * user.get_skill_duration_multiplier(SKILL_CONSTRUCTION), INTERRUPT_ALL | BEHAVIOR_IMMOBILE, BUSY_ICON_BUILD)) + return + + // Create the repaired item + var/obj/item/cprbot_item/I = new /obj/item/cprbot_item(src.loc) + + // Check if the broken item is in the user's hand + var/hand_was_active = user.get_active_hand() == src + var/hand_was_inactive = user.get_inactive_hand() == src + + // Remove the broken item + qdel(src) + + // Attempt to place the new item into the user's hands + if (hand_was_active) + if (!user.put_in_active_hand(I)) + I.forceMove(user.loc) // Place it at user's location if hands are full + else if (hand_was_inactive) + if (!user.put_in_inactive_hand(I)) + I.forceMove(user.loc) // Place it at user's location if hands are full + else + I.forceMove(user.loc) // Place at the original location if not in hand + + diff --git a/colonialmarines.dme b/colonialmarines.dme index 48996a93ec30..f4a7f188cd9a 100644 --- a/colonialmarines.dme +++ b/colonialmarines.dme @@ -904,6 +904,7 @@ #include "code\game\machinery\atmoalter\scrubber.dm" #include "code\game\machinery\bots\bots.dm" #include "code\game\machinery\bots\cleanbot.dm" +#include "code\game\machinery\bots\cprbot.dm" #include "code\game\machinery\bots\floorbot.dm" #include "code\game\machinery\bots\medbot.dm" #include "code\game\machinery\bots\mulebot.dm" @@ -1088,6 +1089,7 @@ #include "code\game\objects\items\contraband.dm" #include "code\game\objects\items\cosmetics.dm" #include "code\game\objects\items\cpr_dummy.dm" +#include "code\game\objects\items\cprbot.dm" #include "code\game\objects\items\disks.dm" #include "code\game\objects\items\fulton.dm" #include "code\game\objects\items\gift_wrappaper.dm" diff --git a/icons/obj/structures/machinery/aibots.dmi b/icons/obj/structures/machinery/aibots.dmi index 18cf52cb43be8c819c28be6e280af091e4192bb3..1fecfe73b336e0f4425da2f9e3460cb7c8844ba8 100644 GIT binary patch literal 18615 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 literal 11156 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 Qv@M{oZFs#z%l5DT0|%)Fc>n+a From 5cfd524420984654b4496c8312a0efae4e3ca113 Mon Sep 17 00:00:00 2001 From: HIDgamer Date: Tue, 3 Sep 2024 08:05:28 +0300 Subject: [PATCH 02/14] Idle message and medical facts list fix. --- code/game/machinery/bots/cprbot.dm | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/code/game/machinery/bots/cprbot.dm b/code/game/machinery/bots/cprbot.dm index 44c2f85976a1..cf6a1ded2e2b 100644 --- a/code/game/machinery/bots/cprbot.dm +++ b/code/game/machinery/bots/cprbot.dm @@ -26,21 +26,21 @@ "Medical trivia: The human body has around 37.2 trillion cells.", "The skin is the largest organ in the human body.", "Did you know? The liver can regenerate itself if a portion is removed.", - "Fun fact: Your sense of smell is closely linked to your memory." - "The only muscle that never tires is that heart." - "Did you know? not breathing can lead to a premature cessation of life!" + "Fun fact: Your sense of smell is closely linked to your memory.", + "The only muscle that never tires is that heart.", + "Did you know? not breathing can lead to a premature cessation of life!", ) var/list/idle_messages = list( "Stay still, I'm assessing the situation.", "Just a routine check-up, don't worry.", "Scanning the area for any casualties.", "I’m ready to save lives, one compression at a time.", - "I hope everyone is feeling alright today!" - "It's not magic it's CPR Buddy 9000!" - "I should have been a plastic surgeon." - "What kind of medbay is this, everyone’s dropping like flies" - "Each breath a day keeps me at bay!" - "I sense a disturbance in my circuit board. as of a million people stopped breathing and were suddenly silent." + "I hope everyone is feeling alright today!", + "It's not magic it's CPR Buddy 9000!", + "I should have been a plastic surgeon.", + "What kind of medbay is this, everyone’s dropping like flies", + "Each breath a day keeps me at bay!", + "I sense a disturbance in my circuit board. as of a million people stopped breathing and were suddenly silent.", ) var/motivational_message = "Live! Live! Don't die on me now!" var/list/has_said_to_patient = list() // Track which patients have been warned From d135e66de15022b13cda860025a477f4b5c8de56 Mon Sep 17 00:00:00 2001 From: HIDgamer Date: Tue, 3 Sep 2024 16:27:19 +0300 Subject: [PATCH 03/14] Garbage Collection --- code/game/machinery/bots/cprbot.dm | 26 ++++++++++++++++---------- code/game/objects/items/cprbot.dm | 2 -- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/code/game/machinery/bots/cprbot.dm b/code/game/machinery/bots/cprbot.dm index cf6a1ded2e2b..ca7735071cd5 100644 --- a/code/game/machinery/bots/cprbot.dm +++ b/code/game/machinery/bots/cprbot.dm @@ -27,8 +27,8 @@ "The skin is the largest organ in the human body.", "Did you know? The liver can regenerate itself if a portion is removed.", "Fun fact: Your sense of smell is closely linked to your memory.", - "The only muscle that never tires is that heart.", - "Did you know? not breathing can lead to a premature cessation of life!", + "The only muscle that never tires is that heart.", + "Did you know? Not breathing can lead to a premature cessation of life!" ) var/list/idle_messages = list( "Stay still, I'm assessing the situation.", @@ -36,11 +36,11 @@ "Scanning the area for any casualties.", "I’m ready to save lives, one compression at a time.", "I hope everyone is feeling alright today!", - "It's not magic it's CPR Buddy 9000!", - "I should have been a plastic surgeon.", - "What kind of medbay is this, everyone’s dropping like flies", - "Each breath a day keeps me at bay!", - "I sense a disturbance in my circuit board. as of a million people stopped breathing and were suddenly silent.", + "It's not magic, it's CPR Buddy 9000!", + "I should have been a plastic surgeon.", + "What kind of medbay is this? Everyone’s dropping like flies.", + "Each breath a day keeps me at bay!", + "I sense a disturbance in my circuit board, as if a million people stopped breathing and were suddenly silent." ) var/motivational_message = "Live! Live! Don't die on me now!" var/list/has_said_to_patient = list() // Track which patients have been warned @@ -59,6 +59,12 @@ src.random_message() // Check if it's time to send a random message sleep(2) // Slower processing loop, moves once every 2 seconds +/obj/structure/machinery/bot/cprbot/Destroy() + processing = FALSE + target = null + path = null + return ..() + /obj/structure/machinery/bot/cprbot/proc/random_message() if (world.time >= last_message_time + 600) // At least 1 minute (600 deciseconds) cooldown if (currently_healing) @@ -130,10 +136,8 @@ else currently_healing = 1 else - // No valid target, stop looking target = null - /obj/structure/machinery/bot/cprbot/proc/perform_cpr(mob/living/carbon/human/H) if (!H || H.stat != DEAD || !H.is_revivable() || !ishuman_strict(H)) target = null @@ -168,6 +172,7 @@ else I.forceMove(src.loc) + src.Destroy() qdel(src) /obj/structure/machinery/bot/cprbot/attack_hand(mob/user as mob) @@ -191,6 +196,7 @@ var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread s.set_up(3, 1, src) s.start() + + src.Destroy() qdel(src) return - diff --git a/code/game/objects/items/cprbot.dm b/code/game/objects/items/cprbot.dm index 32c8c2345cf4..07e1d99c14ff 100644 --- a/code/game/objects/items/cprbot.dm +++ b/code/game/objects/items/cprbot.dm @@ -108,5 +108,3 @@ I.forceMove(user.loc) // Place it at user's location if hands are full else I.forceMove(user.loc) // Place at the original location if not in hand - - From d3f44047328b11375272045004fb68ed2bf88173 Mon Sep 17 00:00:00 2001 From: HIDgamer Date: Tue, 3 Sep 2024 17:26:56 +0300 Subject: [PATCH 04/14] fixed indents --- code/game/machinery/bots/cprbot.dm | 332 ++++++++++++++--------------- code/game/objects/items/cprbot.dm | 182 ++++++++-------- 2 files changed, 257 insertions(+), 257 deletions(-) diff --git a/code/game/machinery/bots/cprbot.dm b/code/game/machinery/bots/cprbot.dm index ca7735071cd5..1c2e8061db7c 100644 --- a/code/game/machinery/bots/cprbot.dm +++ b/code/game/machinery/bots/cprbot.dm @@ -1,202 +1,202 @@ /obj/structure/machinery/bot/cprbot - name = "CPRbot" - desc = "Designed for urgent medical intervention, this CPRbot offers high-tech support in a compact form." - icon = 'icons/obj/structures/machinery/aibots.dmi' - icon_state = "cprbot0" - density = FALSE - anchored = FALSE - health = 100 - maxhealth = 100 - req_access = list(ACCESS_MARINE_MEDBAY) - var/const/search_radius = 10 - var/const/cprbot_proximity_check_radius = 2 // Radius to check for nearby CPRbots - var/processing = TRUE - var/mob/living/carbon/human/target = null - var/cpr_cooldown = 0 - var/path = list() // Path for movement logic - var/currently_healing = 0 - var/iff_signal = FACTION_MARINE // IFF signal to check for valid targets - var/list/medical_facts = list( - "Did you know? The human heart beats over 100,000 times a day.", - "Fun fact: Blood makes up about 7% of your body's weight.", - "Medical trivia: Your brain uses 20% of the oxygen you breathe.", - "Laughter really can increase your pain tolerance.", - "Did you know? The human skeleton is made up of 206 bones.", - "Fun fact: The average adult human body contains about 5 liters of blood.", - "Medical trivia: The human body has around 37.2 trillion cells.", - "The skin is the largest organ in the human body.", - "Did you know? The liver can regenerate itself if a portion is removed.", - "Fun fact: Your sense of smell is closely linked to your memory.", - "The only muscle that never tires is that heart.", - "Did you know? Not breathing can lead to a premature cessation of life!" - ) - var/list/idle_messages = list( - "Stay still, I'm assessing the situation.", - "Just a routine check-up, don't worry.", - "Scanning the area for any casualties.", - "I’m ready to save lives, one compression at a time.", - "I hope everyone is feeling alright today!", - "It's not magic, it's CPR Buddy 9000!", - "I should have been a plastic surgeon.", - "What kind of medbay is this? Everyone’s dropping like flies.", - "Each breath a day keeps me at bay!", - "I sense a disturbance in my circuit board, as if a million people stopped breathing and were suddenly silent." - ) - var/motivational_message = "Live! Live! Don't die on me now!" - var/list/has_said_to_patient = list() // Track which patients have been warned - var/last_message_time = 0 // Tracks the last time a message was spoken + name = "CPRbot" + desc = "Designed for urgent medical intervention, this CPRbot offers high-tech support in a compact form." + icon = 'icons/obj/structures/machinery/aibots.dmi' + icon_state = "cprbot0" + density = FALSE + anchored = FALSE + health = 100 + maxhealth = 100 + req_access = list(ACCESS_MARINE_MEDBAY) + var/const/search_radius = 10 + var/const/cprbot_proximity_check_radius = 2 // Radius to check for nearby CPRbots + var/processing = TRUE + var/mob/living/carbon/human/target = null + var/cpr_cooldown = 0 + var/path = list() // Path for movement logic + var/currently_healing = 0 + var/iff_signal = FACTION_MARINE // IFF signal to check for valid targets + var/list/medical_facts = list( + "Did you know? The human heart beats over 100,000 times a day.", + "Fun fact: Blood makes up about 7% of your body's weight.", + "Medical trivia: Your brain uses 20% of the oxygen you breathe.", + "Laughter really can increase your pain tolerance.", + "Did you know? The human skeleton is made up of 206 bones.", + "Fun fact: The average adult human body contains about 5 liters of blood.", + "Medical trivia: The human body has around 37.2 trillion cells.", + "The skin is the largest organ in the human body.", + "Did you know? The liver can regenerate itself if a portion is removed.", + "Fun fact: Your sense of smell is closely linked to your memory.", + "The only muscle that never tires is that heart.", + "Did you know? Not breathing can lead to a premature cessation of life!" + ) + var/list/idle_messages = list( + "Stay still, I'm assessing the situation.", + "Just a routine check-up, don't worry.", + "Scanning the area for any casualties.", + "I’m ready to save lives, one compression at a time.", + "I hope everyone is feeling alright today!", + "It's not magic, it's CPR Buddy 9000!", + "I should have been a plastic surgeon.", + "What kind of medbay is this? Everyone’s dropping like flies.", + "Each breath a day keeps me at bay!", + "I sense a disturbance in my circuit board, as if a million people stopped breathing and were suddenly silent." + ) + var/motivational_message = "Live! Live! Don't die on me now!" + var/list/has_said_to_patient = list() // Track which patients have been warned + var/last_message_time = 0 // Tracks the last time a message was spoken /obj/structure/machinery/bot/cprbot/New() - ..() - spawn(5) // Wait for 5 seconds after spawning before starting initialization - src.initialize_cprbot() + ..() + spawn(5) // Wait for 5 seconds after spawning before starting initialization + src.initialize_cprbot() /obj/structure/machinery/bot/cprbot/proc/initialize_cprbot() - while (processing && health > 0) - src.find_and_move_to_patient() - if (target && world.time >= cpr_cooldown) - src.perform_cpr(target) - src.random_message() // Check if it's time to send a random message - sleep(2) // Slower processing loop, moves once every 2 seconds + while (processing && health > 0) + src.find_and_move_to_patient() + if (target && world.time >= cpr_cooldown) + src.perform_cpr(target) + src.random_message() // Check if it's time to send a random message + sleep(2) // Slower processing loop, moves once every 2 seconds /obj/structure/machinery/bot/cprbot/Destroy() - processing = FALSE - target = null - path = null - return ..() + processing = FALSE + target = null + path = null + return ..() /obj/structure/machinery/bot/cprbot/proc/random_message() - if (world.time >= last_message_time + 600) // At least 1 minute (600 deciseconds) cooldown - if (currently_healing) - src.speak(motivational_message) - else - if (prob(50)) - src.speak(pick(medical_facts)) - else - src.speak(pick(idle_messages)) - last_message_time = world.time // Update the last message time + if (world.time >= last_message_time + 600) // At least 1 minute (600 deciseconds) cooldown + if (currently_healing) + src.speak(motivational_message) + else + if (prob(50)) + src.speak(pick(medical_facts)) + else + src.speak(pick(idle_messages)) + last_message_time = world.time // Update the last message time /obj/structure/machinery/bot/cprbot/proc/speak(message) - if (!processing || !message) - return - visible_message("[src] beeps, \"[message]\"") + if (!processing || !message) + return + visible_message("[src] beeps, \"[message]\"") /obj/structure/machinery/bot/cprbot/proc/find_and_move_to_patient() - var/list/humans = list() - for (var/mob/living/carbon/human/H in range(search_radius)) - if (H.stat == DEAD && H.check_tod() && H.is_revivable() && H.get_target_lock(iff_signal) && !src.has_nearby_cprbot(H)) - humans += H - - if (humans.len > 0) - target = src.get_nearest(humans) - if (target && !has_said_to_patient.Find(target)) - visible_message("[target] is injured! I'm coming!") - has_said_to_patient += target - src.move_to_target(target) - else - target = null + var/list/humans = list() + for (var/mob/living/carbon/human/H in range(search_radius)) + if (H.stat == DEAD && H.check_tod() && H.is_revivable() && H.get_target_lock(iff_signal) && !src.has_nearby_cprbot(H)) + humans += H + + if (humans.len > 0) + target = src.get_nearest(humans) + if (target && !has_said_to_patient.Find(target)) + visible_message("[target] is injured! I'm coming!") + has_said_to_patient += target + src.move_to_target(target) + else + target = null /obj/structure/machinery/bot/cprbot/proc/has_nearby_cprbot(mob/living/carbon/human/H) - // Check if there are any other CPRbots within a two-tile radius of the target - for (var/obj/structure/machinery/bot/cprbot/nearby_cprbot in range(H, cprbot_proximity_check_radius)) - if (nearby_cprbot != src) // Ignore self - return TRUE - return FALSE + // Check if there are any other CPRbots within a two-tile radius of the target + for (var/obj/structure/machinery/bot/cprbot/nearby_cprbot in range(H, cprbot_proximity_check_radius)) + if (nearby_cprbot != src) // Ignore self + return TRUE + return FALSE /obj/structure/machinery/bot/cprbot/proc/get_nearest(list/humans) - var/mob/living/carbon/human/nearest = null - var/distance = search_radius + 1 + var/mob/living/carbon/human/nearest = null + var/distance = search_radius + 1 - for (var/mob/living/carbon/human/H in humans) - var/d = get_dist(src, H) - if (d < distance) - nearest = H - distance = d + for (var/mob/living/carbon/human/H in humans) + var/d = get_dist(src, H) + if (d < distance) + nearest = H + distance = d - return nearest + return nearest /obj/structure/machinery/bot/cprbot/proc/move_to_target(mob/living/carbon/human/H) - if (H) - var/pathfinding_result = AStar(src.loc, get_turf(H), /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 30) - if (length(pathfinding_result) == 0) - // No reachable path to the target, so stop looking for this patient - target = null - return - path = pathfinding_result - - // Begin moving towards the target if a path exists - if (get_dist(src, H) > 1) - if (length(path) > 0) - step_to(src, path[1]) - path -= path[1] - spawn(10) // Moves every 10 ticks (approximately 1 second) - if (length(path)) - step_to(src, path[1]) - path -= path[1] - else - currently_healing = 1 - else - target = null + if (H) + var/pathfinding_result = AStar(src.loc, get_turf(H), /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 30) + if (length(pathfinding_result) == 0) + // No reachable path to the target, so stop looking for this patient + target = null + return + path = pathfinding_result + + // Begin moving towards the target if a path exists + if (get_dist(src, H) > 1) + if (length(path) > 0) + step_to(src, path[1]) + path -= path[1] + spawn(10) // Moves every 10 ticks (approximately 1 second) + if (length(path)) + step_to(src, path[1]) + path -= path[1] + else + currently_healing = 1 + else + target = null /obj/structure/machinery/bot/cprbot/proc/perform_cpr(mob/living/carbon/human/H) - if (!H || H.stat != DEAD || !H.is_revivable() || !ishuman_strict(H)) - target = null - icon_state = "cprbot0" - currently_healing = 0 - return - - if (get_dist(src, H) > 1) - src.move_to_target(H) - return - - icon_state = "cprbot_active" - H.revive_grace_period += 4 SECONDS - cpr_cooldown = world.time + 7 SECONDS - H.visible_message(SPAN_NOTICE("[src] automatically performs CPR on [H].")) - H.visible_message(SPAN_DANGER("Currently performing CPR on [H] do not intervene!")) - currently_healing = 1 - - spawn(0.5) - while (H.is_revivable() && H.stat == DEAD && get_dist(src, H) <= 1) - sleep(0.5) - icon_state = "cprbot0" - currently_healing = 0 + if (!H || H.stat != DEAD || !H.is_revivable() || !ishuman_strict(H)) + target = null + icon_state = "cprbot0" + currently_healing = 0 + return + + if (get_dist(src, H) > 1) + src.move_to_target(H) + return + + icon_state = "cprbot_active" + H.revive_grace_period += 4 SECONDS + cpr_cooldown = world.time + 7 SECONDS + H.visible_message(SPAN_NOTICE("[src] automatically performs CPR on [H].")) + H.visible_message(SPAN_DANGER("Currently performing CPR on [H] do not intervene!")) + currently_healing = 1 + + spawn(0.5) + while (H.is_revivable() && H.stat == DEAD && get_dist(src, H) <= 1) + sleep(0.5) + icon_state = "cprbot0" + currently_healing = 0 /obj/structure/machinery/bot/cprbot/proc/self_destruct(mob/living/carbon/human/user = null) - var/obj/item/cprbot_item/I = new /obj/item/cprbot_item(src.loc) + var/obj/item/cprbot_item/I = new /obj/item/cprbot_item(src.loc) - if (user) - if (!user.put_in_active_hand(I)) - if (!user.put_in_inactive_hand(I)) - I.forceMove(src.loc) - else - I.forceMove(src.loc) + if (user) + if (!user.put_in_active_hand(I)) + if (!user.put_in_inactive_hand(I)) + I.forceMove(src.loc) + else + I.forceMove(src.loc) - src.Destroy() - qdel(src) + src.Destroy() + qdel(src) /obj/structure/machinery/bot/cprbot/attack_hand(mob/user as mob) - if (..()) - return TRUE + if (..()) + return TRUE - SEND_SIGNAL(user, COMSIG_LIVING_ATTACKHAND_HUMAN, src) + SEND_SIGNAL(user, COMSIG_LIVING_ATTACKHAND_HUMAN, src) - if (user != src) - visible_message(SPAN_DANGER("[user] begins to undeploy [src]!")) - src.self_destruct(user) - return TRUE + if (user != src) + visible_message(SPAN_DANGER("[user] begins to undeploy [src]!")) + src.self_destruct(user) + return TRUE /obj/structure/machinery/bot/cprbot/explode() - src.on = 0 - src.visible_message(SPAN_DANGER("[src] blows apart!"), null, null, 1) - var/turf/Tsec = get_turf(src) + src.on = 0 + src.visible_message(SPAN_DANGER("[src] blows apart!"), null, null, 1) + var/turf/Tsec = get_turf(src) - new /obj/item/cprbot_broken(Tsec) + new /obj/item/cprbot_broken(Tsec) - var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread - s.set_up(3, 1, src) - s.start() + var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread + s.set_up(3, 1, src) + s.start() - src.Destroy() - qdel(src) - return + src.Destroy() + qdel(src) + return diff --git a/code/game/objects/items/cprbot.dm b/code/game/objects/items/cprbot.dm index 07e1d99c14ff..c2e03053c426 100644 --- a/code/game/objects/items/cprbot.dm +++ b/code/game/objects/items/cprbot.dm @@ -1,110 +1,110 @@ /obj/item/cprbot_item - name = "CPRbot" - desc = "A compact CPRbot 9000 asemply" - icon = 'icons/obj/structures/machinery/aibots.dmi' - icon_state = "cprbot" - w_class = SIZE_MEDIUM - var/deployment_path = /obj/structure/machinery/bot/cprbot + name = "CPRbot" + desc = "A compact CPRbot 9000 asemply" + icon = 'icons/obj/structures/machinery/aibots.dmi' + icon_state = "cprbot" + w_class = SIZE_MEDIUM + var/deployment_path = /obj/structure/machinery/bot/cprbot /obj/item/cprbot_item/proc/deploy_cprbot(mob/user, atom/location) - if(!user || !location) - return - // Attempt to delete the item first - qdel(src) - world << "Deleting item [src]. Deploying CPRbot at [location]." - - // Proceed with the CPRbot deployment - var/obj/structure/machinery/bot/cprbot/R = new deployment_path(location) - if(R) - R.add_fingerprint(user) - world << "CPRbot deployed successfully at [location]." - else - world << "Failed to deploy CPRbot at [location]." + if(!user || !location) + return + // Attempt to delete the item first + qdel(src) + world << "Deleting item [src]. Deploying CPRbot at [location]." + + // Proceed with the CPRbot deployment + var/obj/structure/machinery/bot/cprbot/R = new deployment_path(location) + if(R) + R.add_fingerprint(user) + world << "CPRbot deployed successfully at [location]." + else + world << "Failed to deploy CPRbot at [location]." /obj/item/cprbot_item/attack_self(mob/user) - if (..()) - return TRUE - if(user) - deploy_cprbot(user, user.loc) + if (..()) + return TRUE + if(user) + deploy_cprbot(user, user.loc) /obj/item/cprbot_item/afterattack(atom/target, mob/user, proximity) - if(proximity && isturf(target)) - var/turf/T = target - if(!T.density) - deploy_cprbot(user, T) + if(proximity && isturf(target)) + var/turf/T = target + if(!T.density) + deploy_cprbot(user, T) /obj/item/cprbot_broken - name = "CPRbot" - desc = "A compact CPRbot 9000 asemply it appears to be in bad shape" - icon = 'icons/obj/structures/machinery/aibots.dmi' - icon_state = "cprbot_broken" - w_class = SIZE_MEDIUM + name = "CPRbot" + desc = "A compact CPRbot 9000 asemply it appears to be in bad shape" + icon = 'icons/obj/structures/machinery/aibots.dmi' + icon_state = "cprbot_broken" + w_class = SIZE_MEDIUM /obj/item/cprbot_broken/attackby(obj/item/W, mob/living/user) - if(iswelder(W)) - if(!HAS_TRAIT(W, TRAIT_TOOL_BLOWTORCH)) - to_chat(user, SPAN_WARNING("You need a stronger blowtorch!")) - return + if(iswelder(W)) + if(!HAS_TRAIT(W, TRAIT_TOOL_BLOWTORCH)) + to_chat(user, SPAN_WARNING("You need a stronger blowtorch!")) + return - var/obj/item/tool/weldingtool/WT = W - if(!WT.isOn()) - to_chat(user, SPAN_WARNING("\The [WT] needs to be on!")) - return + var/obj/item/tool/weldingtool/WT = W + if(!WT.isOn()) + to_chat(user, SPAN_WARNING("\The [WT] needs to be on!")) + return - if(!WT.remove_fuel(5, user)) // Ensure the welder has enough fuel to operate - to_chat(user, SPAN_NOTICE("You need more welding fuel to complete this task.")) - return + if(!WT.remove_fuel(5, user)) // Ensure the welder has enough fuel to operate + to_chat(user, SPAN_NOTICE("You need more welding fuel to complete this task.")) + return - playsound(src, 'sound/items/Welder.ogg', 25, 1) + playsound(src, 'sound/items/Welder.ogg', 25, 1) - if(!do_after(user, 10 * user.get_skill_duration_multiplier(SKILL_CONSTRUCTION), INTERRUPT_ALL | BEHAVIOR_IMMOBILE, BUSY_ICON_BUILD)) - return + if(!do_after(user, 10 * user.get_skill_duration_multiplier(SKILL_CONSTRUCTION), INTERRUPT_ALL | BEHAVIOR_IMMOBILE, BUSY_ICON_BUILD)) + return - var/obj/item/cprbot_item/I = new /obj/item/cprbot_item(src.loc) + var/obj/item/cprbot_item/I = new /obj/item/cprbot_item(src.loc) - if(user) - if(!user.put_in_active_hand(I)) - if(!user.put_in_inactive_hand(I)) - I.forceMove(src.loc) - else - I.forceMove(src.loc) + if(user) + if(!user.put_in_active_hand(I)) + if(!user.put_in_inactive_hand(I)) + I.forceMove(src.loc) + else + I.forceMove(src.loc) /obj/item/cprbot_broken/attackby(obj/item/W, mob/living/user) - if(iswelder(W)) - if(!HAS_TRAIT(W, TRAIT_TOOL_BLOWTORCH)) - to_chat(user, SPAN_WARNING("You need a stronger blowtorch!")) - return - - var/obj/item/tool/weldingtool/WT = W - if(!WT.isOn()) - to_chat(user, SPAN_WARNING("\The [WT] needs to be on!")) - return - - if(!WT.remove_fuel(5, user)) // Ensure enough fuel is available - to_chat(user, SPAN_NOTICE("You need more welding fuel to complete this task.")) - return - - playsound(src, 'sound/items/Welder.ogg', 25, 1) - - if(!do_after(user, 10 * user.get_skill_duration_multiplier(SKILL_CONSTRUCTION), INTERRUPT_ALL | BEHAVIOR_IMMOBILE, BUSY_ICON_BUILD)) - return - - // Create the repaired item - var/obj/item/cprbot_item/I = new /obj/item/cprbot_item(src.loc) - - // Check if the broken item is in the user's hand - var/hand_was_active = user.get_active_hand() == src - var/hand_was_inactive = user.get_inactive_hand() == src - - // Remove the broken item - qdel(src) - - // Attempt to place the new item into the user's hands - if (hand_was_active) - if (!user.put_in_active_hand(I)) - I.forceMove(user.loc) // Place it at user's location if hands are full - else if (hand_was_inactive) - if (!user.put_in_inactive_hand(I)) - I.forceMove(user.loc) // Place it at user's location if hands are full - else - I.forceMove(user.loc) // Place at the original location if not in hand + if(iswelder(W)) + if(!HAS_TRAIT(W, TRAIT_TOOL_BLOWTORCH)) + to_chat(user, SPAN_WARNING("You need a stronger blowtorch!")) + return + + var/obj/item/tool/weldingtool/WT = W + if(!WT.isOn()) + to_chat(user, SPAN_WARNING("\The [WT] needs to be on!")) + return + + if(!WT.remove_fuel(5, user)) // Ensure enough fuel is available + to_chat(user, SPAN_NOTICE("You need more welding fuel to complete this task.")) + return + + playsound(src, 'sound/items/Welder.ogg', 25, 1) + + if(!do_after(user, 10 * user.get_skill_duration_multiplier(SKILL_CONSTRUCTION), INTERRUPT_ALL | BEHAVIOR_IMMOBILE, BUSY_ICON_BUILD)) + return + + // Create the repaired item + var/obj/item/cprbot_item/I = new /obj/item/cprbot_item(src.loc) + + // Check if the broken item is in the user's hand + var/hand_was_active = user.get_active_hand() == src + var/hand_was_inactive = user.get_inactive_hand() == src + + // Remove the broken item + qdel(src) + + // Attempt to place the new item into the user's hands + if (hand_was_active) + if (!user.put_in_active_hand(I)) + I.forceMove(user.loc) // Place it at user's location if hands are full + else if (hand_was_inactive) + if (!user.put_in_inactive_hand(I)) + I.forceMove(user.loc) // Place it at user's location if hands are full + else + I.forceMove(user.loc) // Place at the original location if not in hand From 682a229badbd1eaa15cb8c9c177ba0df5b453de7 Mon Sep 17 00:00:00 2001 From: HIDgamer Date: Wed, 4 Sep 2024 03:22:58 +0300 Subject: [PATCH 05/14] Code update --- code/game/machinery/bots/cprbot.dm | 108 ++++++++++++++++------------- 1 file changed, 61 insertions(+), 47 deletions(-) diff --git a/code/game/machinery/bots/cprbot.dm b/code/game/machinery/bots/cprbot.dm index 1c2e8061db7c..f478ba83cbb1 100644 --- a/code/game/machinery/bots/cprbot.dm +++ b/code/game/machinery/bots/cprbot.dm @@ -1,5 +1,5 @@ /obj/structure/machinery/bot/cprbot - name = "CPRbot" + name = "\improper CPRbot" desc = "Designed for urgent medical intervention, this CPRbot offers high-tech support in a compact form." icon = 'icons/obj/structures/machinery/aibots.dmi' icon_state = "cprbot0" @@ -8,15 +8,28 @@ health = 100 maxhealth = 100 req_access = list(ACCESS_MARINE_MEDBAY) - var/const/search_radius = 10 - var/const/cprbot_proximity_check_radius = 2 // Radius to check for nearby CPRbots + + /// Radius to search for patients + var/const/search_radius = 7 + /// Radius to check for nearby CPRbots + var/const/cprbot_proximity_check_radius = 2 + /// Indicates whether the bot is processing var/processing = TRUE + /// Current target for CPR var/mob/living/carbon/human/target = null + /// Time when the bot can perform CPR again var/cpr_cooldown = 0 - var/path = list() // Path for movement logic - var/currently_healing = 0 - var/iff_signal = FACTION_MARINE // IFF signal to check for valid targets - var/list/medical_facts = list( + /// Path for movement logic + var/path = list() + /// Indicates whether the bot is currently healing + var/currently_healing = FALSE + /// IFF signal to check for valid targets + var/iff_signal = FACTION_MARINE + /// Cooldown for the random idle messages and medical facts + var/cooldown_time = 60 SECONDS + COOLDOWN_DECLARE(message_cooldown) + + var/static/list/medical_facts = list( "Did you know? The human heart beats over 100,000 times a day.", "Fun fact: Blood makes up about 7% of your body's weight.", "Medical trivia: Your brain uses 20% of the oxygen you breathe.", @@ -30,7 +43,8 @@ "The only muscle that never tires is that heart.", "Did you know? Not breathing can lead to a premature cessation of life!" ) - var/list/idle_messages = list( + + var/static/list/idle_messages = list( "Stay still, I'm assessing the situation.", "Just a routine check-up, don't worry.", "Scanning the area for any casualties.", @@ -42,13 +56,16 @@ "Each breath a day keeps me at bay!", "I sense a disturbance in my circuit board, as if a million people stopped breathing and were suddenly silent." ) + + /// Message to display when performing CPR var/motivational_message = "Live! Live! Don't die on me now!" - var/list/has_said_to_patient = list() // Track which patients have been warned - var/last_message_time = 0 // Tracks the last time a message was spoken + /// List of patients who have been warned + var/list/has_said_to_patient = list() + /// Tracks the last time a message was spoken + var/last_message_time = 0 /obj/structure/machinery/bot/cprbot/New() ..() - spawn(5) // Wait for 5 seconds after spawning before starting initialization src.initialize_cprbot() /obj/structure/machinery/bot/cprbot/proc/initialize_cprbot() @@ -57,24 +74,28 @@ if (target && world.time >= cpr_cooldown) src.perform_cpr(target) src.random_message() // Check if it's time to send a random message - sleep(2) // Slower processing loop, moves once every 2 seconds + sleep(0.5 SECONDS) // Slower processing loop, moves once every 2 seconds /obj/structure/machinery/bot/cprbot/Destroy() - processing = FALSE target = null path = null return ..() /obj/structure/machinery/bot/cprbot/proc/random_message() - if (world.time >= last_message_time + 600) // At least 1 minute (600 deciseconds) cooldown - if (currently_healing) - src.speak(motivational_message) + if (!COOLDOWN_FINISHED(src, message_cooldown)) + return // Exit if the cooldown period has not elapsed yet + + // Send a message based on the current state + if (currently_healing) + speak(motivational_message) + else + if (prob(50)) + speak(pick(medical_facts)) else - if (prob(50)) - src.speak(pick(medical_facts)) - else - src.speak(pick(idle_messages)) - last_message_time = world.time // Update the last message time + speak(pick(idle_messages)) + + // Start the cooldown timer for the next message + COOLDOWN_START(src, message_cooldown, cooldown_time) /obj/structure/machinery/bot/cprbot/proc/speak(message) if (!processing || !message) @@ -83,11 +104,11 @@ /obj/structure/machinery/bot/cprbot/proc/find_and_move_to_patient() var/list/humans = list() - for (var/mob/living/carbon/human/H in range(search_radius)) - if (H.stat == DEAD && H.check_tod() && H.is_revivable() && H.get_target_lock(iff_signal) && !src.has_nearby_cprbot(H)) - humans += H + for (var/mob/living/carbon/human/patient in range(search_radius)) + if (patient.stat == DEAD && patient.check_tod() && patient.is_revivable() && patient.get_target_lock(iff_signal) && !src.has_nearby_cprbot(patient)) + humans += patient - if (humans.len > 0) + if (length(humans) > 0) target = src.get_nearest(humans) if (target && !has_said_to_patient.Find(target)) visible_message("[target] is injured! I'm coming!") @@ -107,11 +128,11 @@ var/mob/living/carbon/human/nearest = null var/distance = search_radius + 1 - for (var/mob/living/carbon/human/H in humans) - var/d = get_dist(src, H) - if (d < distance) - nearest = H - distance = d + for (var/mob/living/carbon/human/patient in humans) + var/length = get_dist(src, patient) + if (length < distance) + nearest = patient + distance = length return nearest @@ -129,15 +150,16 @@ if (length(path) > 0) step_to(src, path[1]) path -= path[1] - spawn(10) // Moves every 10 ticks (approximately 1 second) - if (length(path)) - step_to(src, path[1]) - path -= path[1] + sleep(0.5 SECONDS) + if (length(path)) + step_to(src, path[1]) + path -= path[1] else - currently_healing = 1 + currently_healing = TRUE else target = null + /obj/structure/machinery/bot/cprbot/proc/perform_cpr(mob/living/carbon/human/H) if (!H || H.stat != DEAD || !H.is_revivable() || !ishuman_strict(H)) target = null @@ -154,13 +176,7 @@ cpr_cooldown = world.time + 7 SECONDS H.visible_message(SPAN_NOTICE("[src] automatically performs CPR on [H].")) H.visible_message(SPAN_DANGER("Currently performing CPR on [H] do not intervene!")) - currently_healing = 1 - - spawn(0.5) - while (H.is_revivable() && H.stat == DEAD && get_dist(src, H) <= 1) - sleep(0.5) - icon_state = "cprbot0" - currently_healing = 0 + currently_healing = TRUE /obj/structure/machinery/bot/cprbot/proc/self_destruct(mob/living/carbon/human/user = null) var/obj/item/cprbot_item/I = new /obj/item/cprbot_item(src.loc) @@ -172,7 +188,6 @@ else I.forceMove(src.loc) - src.Destroy() qdel(src) /obj/structure/machinery/bot/cprbot/attack_hand(mob/user as mob) @@ -193,10 +208,9 @@ new /obj/item/cprbot_broken(Tsec) - var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread - s.set_up(3, 1, src) - s.start() + var/datum/effect_system/spark_spread/spark = new /datum/effect_system/spark_spread + spark.set_up(3, 1, src) + spark.start() - src.Destroy() qdel(src) return From ea309482827f9eb74acc2512221c28c1451e449f Mon Sep 17 00:00:00 2001 From: HIDgamer Date: Sun, 8 Sep 2024 11:33:21 +0300 Subject: [PATCH 06/14] Major code refinement and updates. --- code/game/machinery/bots/cprbot.dm | 285 ++++++++++++++++++++--------- code/game/objects/items/cprbot.dm | 5 +- 2 files changed, 198 insertions(+), 92 deletions(-) diff --git a/code/game/machinery/bots/cprbot.dm b/code/game/machinery/bots/cprbot.dm index f478ba83cbb1..36be4bef20d5 100644 --- a/code/game/machinery/bots/cprbot.dm +++ b/code/game/machinery/bots/cprbot.dm @@ -1,3 +1,9 @@ +#define STATE_CPRBOT_IDLE "idle" +#define STATE_CPRBOT_MOVING "moving" +#define STATE_CPRBOT_CPR "cpr" +#define STATE_CPRBOT_FOLLOWING_OWNER "following_owner" + + /obj/structure/machinery/bot/cprbot name = "\improper CPRbot" desc = "Designed for urgent medical intervention, this CPRbot offers high-tech support in a compact form." @@ -10,25 +16,25 @@ req_access = list(ACCESS_MARINE_MEDBAY) /// Radius to search for patients - var/const/search_radius = 7 + var/static/search_radius = 7 /// Radius to check for nearby CPRbots - var/const/cprbot_proximity_check_radius = 2 - /// Indicates whether the bot is processing - var/processing = TRUE - /// Current target for CPR - var/mob/living/carbon/human/target = null - /// Time when the bot can perform CPR again - var/cpr_cooldown = 0 - /// Path for movement logic - var/path = list() + var/static/cprbot_proximity_check_radius = 2 + /// Current target for CPR (using weak reference) + var/datum/weakref/human /// Indicates whether the bot is currently healing var/currently_healing = FALSE + var/cpr_ready = TRUE /// IFF signal to check for valid targets var/iff_signal = FACTION_MARINE /// Cooldown for the random idle messages and medical facts var/cooldown_time = 60 SECONDS + var/movement_delay = 4 + var/owner + var/fast_processing = FALSE COOLDOWN_DECLARE(message_cooldown) + var/state = STATE_CPRBOT_IDLE + var/static/list/medical_facts = list( "Did you know? The human heart beats over 100,000 times a day.", "Fun fact: Blood makes up about 7% of your body's weight.", @@ -64,21 +70,43 @@ /// Tracks the last time a message was spoken var/last_message_time = 0 -/obj/structure/machinery/bot/cprbot/New() - ..() - src.initialize_cprbot() +/obj/structure/machinery/bot/cprbot/Initialize(mapload, ...) + . = ..() + start_processing() -/obj/structure/machinery/bot/cprbot/proc/initialize_cprbot() - while (processing && health > 0) - src.find_and_move_to_patient() - if (target && world.time >= cpr_cooldown) - src.perform_cpr(target) - src.random_message() // Check if it's time to send a random message - sleep(0.5 SECONDS) // Slower processing loop, moves once every 2 seconds +/obj/structure/machinery/bot/cprbot/process() + if (health > 0) + think() + // this makes it halt for whole minute, find different way to do cooldown + //random_message() // Check if it's time to send a random message + else + stop_processing() + +/obj/structure/machinery/bot/cprbot/proc/think() + switch (state) + if (STATE_CPRBOT_IDLE) + find_and_move_to_patient() + if (STATE_CPRBOT_MOVING) + move_to_target() + if (STATE_CPRBOT_CPR) + try_perform_cpr() + if (STATE_CPRBOT_FOLLOWING_OWNER) + find_and_move_to_patient() + + +/obj/structure/machinery/bot/cprbot/start_processing() + START_PROCESSING(SSobj, src) + +/obj/structure/machinery/bot/cprbot/stop_processing() + if (fast_processing) + STOP_PROCESSING(SSfastobj, src) + else + STOP_PROCESSING(SSobj, src) /obj/structure/machinery/bot/cprbot/Destroy() - target = null - path = null + human = null + + stop_processing() return ..() /obj/structure/machinery/bot/cprbot/proc/random_message() @@ -98,86 +126,144 @@ COOLDOWN_START(src, message_cooldown, cooldown_time) /obj/structure/machinery/bot/cprbot/proc/speak(message) - if (!processing || !message) + if (!message) return visible_message("[src] beeps, \"[message]\"") +/obj/structure/machinery/bot/cprbot/proc/go_idle() + human = null + state = STATE_CPRBOT_IDLE + cpr_ready = TRUE + currently_healing = FALSE + walk_to(src, 0) // make sure we stop walking + update_icon() + +/obj/structure/machinery/bot/cprbot/proc/valid_cpr_target(mob/living/carbon/human/patient) + return patient.stat == DEAD && patient.check_tod() && patient.is_revivable() && patient.get_target_lock(iff_signal) + /obj/structure/machinery/bot/cprbot/proc/find_and_move_to_patient() - var/list/humans = list() - for (var/mob/living/carbon/human/patient in range(search_radius)) - if (patient.stat == DEAD && patient.check_tod() && patient.is_revivable() && patient.get_target_lock(iff_signal) && !src.has_nearby_cprbot(patient)) - humans += patient - - if (length(humans) > 0) - target = src.get_nearest(humans) - if (target && !has_said_to_patient.Find(target)) - visible_message("[target] is injured! I'm coming!") - has_said_to_patient += target - src.move_to_target(target) + var/list/potential_patients = list() + has_said_to_patient = list() + for (var/mob/living/carbon/human/patient in view(search_radius, src)) + if (valid_cpr_target(patient)) + potential_patients += patient + + for (var/obj/structure/machinery/bot/cprbot/another_cpr_bot in view(search_radius, src)) + if (another_cpr_bot == src) + continue + + var/mob/living/carbon/human/another_bot_patient + + // sanity checks + if (another_cpr_bot.human != null) + another_bot_patient = another_cpr_bot.human.resolve() + + // sanity checks + if (another_bot_patient == null) + continue + + // Another CPR bot is targetting this patient, skip + if (another_bot_patient in potential_patients) + potential_patients.Remove(another_bot_patient) + + if (potential_patients.len) + var/mob/living/carbon/human/patient = potential_patients[1] + human = WEAKREF(patient) + if (patient && !(patient in has_said_to_patient)) + visible_message("[patient] is injured! I'm coming!") + has_said_to_patient += patient + + move_to_target() else - target = null - -/obj/structure/machinery/bot/cprbot/proc/has_nearby_cprbot(mob/living/carbon/human/H) - // Check if there are any other CPRbots within a two-tile radius of the target - for (var/obj/structure/machinery/bot/cprbot/nearby_cprbot in range(H, cprbot_proximity_check_radius)) - if (nearby_cprbot != src) // Ignore self - return TRUE - return FALSE - -/obj/structure/machinery/bot/cprbot/proc/get_nearest(list/humans) - var/mob/living/carbon/human/nearest = null - var/distance = search_radius + 1 - - for (var/mob/living/carbon/human/patient in humans) - var/length = get_dist(src, patient) - if (length < distance) - nearest = patient - distance = length - - return nearest - -/obj/structure/machinery/bot/cprbot/proc/move_to_target(mob/living/carbon/human/H) - if (H) - var/pathfinding_result = AStar(src.loc, get_turf(H), /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 30) - if (length(pathfinding_result) == 0) - // No reachable path to the target, so stop looking for this patient - target = null - return - path = pathfinding_result - - // Begin moving towards the target if a path exists - if (get_dist(src, H) > 1) - if (length(path) > 0) - step_to(src, path[1]) - path -= path[1] - sleep(0.5 SECONDS) - if (length(path)) - step_to(src, path[1]) - path -= path[1] + // If no patients are found, check if owner is nearby and follow them if idle + if (state == STATE_CPRBOT_IDLE && (owner && (owner in view(search_radius, src)))) + state = STATE_CPRBOT_FOLLOWING_OWNER + walk_to(src, owner, 0, movement_delay) + else if (state == STATE_CPRBOT_FOLLOWING_OWNER) + // Continue following the owner if no patient is in sight + walk_to(src, owner, 0, movement_delay) else - currently_healing = TRUE - else - target = null + go_idle() +/obj/structure/machinery/bot/cprbot/proc/can_still_see_patient() + if (human == null) + return FALSE -/obj/structure/machinery/bot/cprbot/proc/perform_cpr(mob/living/carbon/human/H) - if (!H || H.stat != DEAD || !H.is_revivable() || !ishuman_strict(H)) - target = null - icon_state = "cprbot0" - currently_healing = 0 + var/mob/living/carbon/human/patient = human.resolve() + if (patient == null) + return FALSE + + return patient in view(search_radius, src) + +/obj/structure/machinery/bot/cprbot/proc/patient_in_range() + if (human == null) + return FALSE + + var/mob/living/carbon/human/patient = human.resolve() + if (patient == null) + return FALSE + + return get_dist(src, patient) == 0 + +/obj/structure/machinery/bot/cprbot/proc/move_to_target() + var/mob/living/carbon/human/patient = human.resolve() + // If we cannot see them anymore then stop moving + if (!can_still_see_patient()) + go_idle() return - if (get_dist(src, H) > 1) - src.move_to_target(H) + // It might not exist anymore or something + if (patient == null) + go_idle() return - icon_state = "cprbot_active" - H.revive_grace_period += 4 SECONDS - cpr_cooldown = world.time + 7 SECONDS - H.visible_message(SPAN_NOTICE("[src] automatically performs CPR on [H].")) - H.visible_message(SPAN_DANGER("Currently performing CPR on [H] do not intervene!")) + if (!patient_in_range()) + // We are already moving + if (state == STATE_CPRBOT_MOVING) + return + + state = STATE_CPRBOT_MOVING + walk_to(src, patient, 0, movement_delay) + else + walk_to(src, 0) // make sure we stop walking + state = STATE_CPRBOT_CPR + switch_to_faster_processing() + try_perform_cpr() + +/obj/structure/machinery/bot/cprbot/proc/perform_cpr(mob/living/carbon/human/target) + if (!cpr_ready) + return + + currently_healing = TRUE + + update_icon() + + target.revive_grace_period += 4 SECONDS + target.visible_message(SPAN_NOTICE("[src] automatically performs CPR on [target].")) + target.visible_message(SPAN_DANGER("Currently performing CPR on [target] do not intervene!")) currently_healing = TRUE + cpr_ready = FALSE + addtimer(VARSET_CALLBACK(src, cpr_ready, TRUE), 7 SECONDS) + +/obj/structure/machinery/bot/cprbot/proc/try_perform_cpr() + currently_healing = TRUE + // Resolve the weak reference to check if the target still exists + var/mob/living/carbon/human/target = human.resolve() + + if (!patient_in_range()) + go_idle() + switch_to_slower_processing() + return + + // Check if the target is valid and still needs CPR + if (!target || target.stat != DEAD || !target.is_revivable() || !ishuman_strict(target)) + go_idle() + switch_to_slower_processing() + return + + perform_cpr(target) + /obj/structure/machinery/bot/cprbot/proc/self_destruct(mob/living/carbon/human/user = null) var/obj/item/cprbot_item/I = new /obj/item/cprbot_item(src.loc) @@ -194,6 +280,10 @@ if (..()) return TRUE + if (!issynth(user)) + visible_message(SPAN_DANGER("[user] tries to undeploy [src]!")) + return FALSE + SEND_SIGNAL(user, COMSIG_LIVING_ATTACKHAND_HUMAN, src) if (user != src) @@ -201,6 +291,15 @@ src.self_destruct(user) return TRUE +/obj/structure/machinery/bot/cprbot/update_icon() + . = ..() + + switch(state) + if (STATE_CPRBOT_IDLE) + icon_state = "cprbot0" + if (STATE_CPRBOT_CPR) + icon_state = "cprbot_active" + /obj/structure/machinery/bot/cprbot/explode() src.on = 0 src.visible_message(SPAN_DANGER("[src] blows apart!"), null, null, 1) @@ -214,3 +313,13 @@ qdel(src) return + +/obj/structure/machinery/bot/cprbot/proc/switch_to_faster_processing() + STOP_PROCESSING(SSobj, src) + START_PROCESSING(SSfastobj, src) + fast_processing = TRUE + +/obj/structure/machinery/bot/cprbot/proc/switch_to_slower_processing() + STOP_PROCESSING(SSfastobj, src) + START_PROCESSING(SSobj, src) + fast_processing = FALSE diff --git a/code/game/objects/items/cprbot.dm b/code/game/objects/items/cprbot.dm index c2e03053c426..e9280f5d0514 100644 --- a/code/game/objects/items/cprbot.dm +++ b/code/game/objects/items/cprbot.dm @@ -11,15 +11,12 @@ return // Attempt to delete the item first qdel(src) - world << "Deleting item [src]. Deploying CPRbot at [location]." // Proceed with the CPRbot deployment var/obj/structure/machinery/bot/cprbot/R = new deployment_path(location) if(R) R.add_fingerprint(user) - world << "CPRbot deployed successfully at [location]." - else - world << "Failed to deploy CPRbot at [location]." + R.owner = user /obj/item/cprbot_item/attack_self(mob/user) if (..()) From 642561c2485c51781b70ccdf67ee2f0e13ecb27e Mon Sep 17 00:00:00 2001 From: HIDgamer Date: Mon, 9 Sep 2024 08:37:27 +0300 Subject: [PATCH 07/14] Updates and Bug fixes --- code/game/machinery/bots/cprbot.dm | 11 +++++------ code/game/objects/items/cprbot.dm | 28 +++++++++++++++++----------- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/code/game/machinery/bots/cprbot.dm b/code/game/machinery/bots/cprbot.dm index 36be4bef20d5..b38038881c17 100644 --- a/code/game/machinery/bots/cprbot.dm +++ b/code/game/machinery/bots/cprbot.dm @@ -60,7 +60,8 @@ "I should have been a plastic surgeon.", "What kind of medbay is this? Everyone’s dropping like flies.", "Each breath a day keeps me at bay!", - "I sense a disturbance in my circuit board, as if a million people stopped breathing and were suddenly silent." + "I sense a disturbance in my circuit board, as if a million people stopped breathing and were suddenly silent.", + "It's a Dyer situation it is!" ) /// Message to display when performing CPR @@ -77,8 +78,6 @@ /obj/structure/machinery/bot/cprbot/process() if (health > 0) think() - // this makes it halt for whole minute, find different way to do cooldown - //random_message() // Check if it's time to send a random message else stop_processing() @@ -257,7 +256,7 @@ return // Check if the target is valid and still needs CPR - if (!target || target.stat != DEAD || !target.is_revivable() || !ishuman_strict(target)) + if ((target.stat != DEAD) || (target.stat == DEAD && !target.check_tod())) go_idle() switch_to_slower_processing() return @@ -280,8 +279,8 @@ if (..()) return TRUE - if (!issynth(user)) - visible_message(SPAN_DANGER("[user] tries to undeploy [src]!")) + if(!skillcheck(user, SKILL_MEDICAL, SKILL_MEDICAL_MEDIC)) + visible_message(SPAN_DANGER("[user] fails to undeploy [src] ")) return FALSE SEND_SIGNAL(user, COMSIG_LIVING_ATTACKHAND_HUMAN, src) diff --git a/code/game/objects/items/cprbot.dm b/code/game/objects/items/cprbot.dm index e9280f5d0514..403e0d40cb8f 100644 --- a/code/game/objects/items/cprbot.dm +++ b/code/game/objects/items/cprbot.dm @@ -6,23 +6,29 @@ w_class = SIZE_MEDIUM var/deployment_path = /obj/structure/machinery/bot/cprbot +/obj/item/cprbot_item/attack_self(mob/user as mob) + if (..()) + return TRUE + + if(user) + deploy_cprbot(user, user.loc) + /obj/item/cprbot_item/proc/deploy_cprbot(mob/user, atom/location) if(!user || !location) return - // Attempt to delete the item first + + if (istype(user)) + if(!skillcheck(user, SKILL_MEDICAL, SKILL_MEDICAL_MEDIC)) + to_chat(user, SPAN_WARNING("You don't seem to know how to use [src]...")) + return + qdel(src) // Proceed with the CPRbot deployment - var/obj/structure/machinery/bot/cprbot/R = new deployment_path(location) - if(R) - R.add_fingerprint(user) - R.owner = user - -/obj/item/cprbot_item/attack_self(mob/user) - if (..()) - return TRUE - if(user) - deploy_cprbot(user, user.loc) + var/obj/structure/machinery/bot/cprbot/entity = new deployment_path(location) + if(entity) + entity.add_fingerprint(user) + entity.owner = user /obj/item/cprbot_item/afterattack(atom/target, mob/user, proximity) if(proximity && isturf(target)) From ded37607388a583404d8940ce2b5468f1d67e648 Mon Sep 17 00:00:00 2001 From: HIDgamer Date: Mon, 9 Sep 2024 11:32:56 +0300 Subject: [PATCH 08/14] CPRbot sound update. --- code/game/machinery/bots/cprbot.dm | 6 +++++- sound/CPRbot/CPR.ogg | Bin 0 -> 11075 bytes sound/CPRbot/CPRbot_beep.ogg | Bin 0 -> 9483 bytes sound/CPRbot/CPRbot_poweron.ogg | Bin 0 -> 13479 bytes 4 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 sound/CPRbot/CPR.ogg create mode 100644 sound/CPRbot/CPRbot_beep.ogg create mode 100644 sound/CPRbot/CPRbot_poweron.ogg diff --git a/code/game/machinery/bots/cprbot.dm b/code/game/machinery/bots/cprbot.dm index b38038881c17..6bdbfc39eb74 100644 --- a/code/game/machinery/bots/cprbot.dm +++ b/code/game/machinery/bots/cprbot.dm @@ -74,10 +74,13 @@ /obj/structure/machinery/bot/cprbot/Initialize(mapload, ...) . = ..() start_processing() + playsound(loc, 'sound/CPRbot/CPRbot_poweron.ogg', 25, 1) /obj/structure/machinery/bot/cprbot/process() if (health > 0) think() + + random_message() else stop_processing() @@ -128,6 +131,7 @@ if (!message) return visible_message("[src] beeps, \"[message]\"") + playsound(loc, 'sound/CPRbot/CPRbot_beep.ogg', 25, 1) /obj/structure/machinery/bot/cprbot/proc/go_idle() human = null @@ -241,7 +245,7 @@ target.visible_message(SPAN_NOTICE("[src] automatically performs CPR on [target].")) target.visible_message(SPAN_DANGER("Currently performing CPR on [target] do not intervene!")) currently_healing = TRUE - + playsound(loc, 'sound/CPRbot/CPR.ogg', 25, 1) cpr_ready = FALSE addtimer(VARSET_CALLBACK(src, cpr_ready, TRUE), 7 SECONDS) diff --git a/sound/CPRbot/CPR.ogg b/sound/CPRbot/CPR.ogg new file mode 100644 index 0000000000000000000000000000000000000000..87431270242f2d6810cfd5df03dc2fe6650bcf94 GIT binary patch literal 11075 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 USdI33o_6yQmjv0P{iS^W3-}QBh5!Hn literal 0 HcmV?d00001 diff --git a/sound/CPRbot/CPRbot_beep.ogg b/sound/CPRbot/CPRbot_beep.ogg new file mode 100644 index 0000000000000000000000000000000000000000..718128602ed6f378585c653964b0e8c37da9bb6f GIT binary patch literal 9483 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 Qbs>%;JI{;>-7&)cA3!sq3IG5A literal 0 HcmV?d00001 diff --git a/sound/CPRbot/CPRbot_poweron.ogg b/sound/CPRbot/CPRbot_poweron.ogg new file mode 100644 index 0000000000000000000000000000000000000000..f6d6f9117777d5f2e8e8f67c07c97792d7a7d72c GIT binary patch literal 13479 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 NkyauU{3RqS`(M!*XXF3? literal 0 HcmV?d00001 From 9d7f9b588033c07a0b5f48a9f241283e52479cc8 Mon Sep 17 00:00:00 2001 From: HIDgamer Date: Tue, 10 Sep 2024 11:27:45 +0300 Subject: [PATCH 09/14] CPR warning --- code/game/machinery/bots/cprbot.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/machinery/bots/cprbot.dm b/code/game/machinery/bots/cprbot.dm index 6bdbfc39eb74..c3b8a0c51106 100644 --- a/code/game/machinery/bots/cprbot.dm +++ b/code/game/machinery/bots/cprbot.dm @@ -243,7 +243,7 @@ target.revive_grace_period += 4 SECONDS target.visible_message(SPAN_NOTICE("[src] automatically performs CPR on [target].")) - target.visible_message(SPAN_DANGER("Currently performing CPR on [target] do not intervene!")) + target.balloon_alert_to_viewers("Performing CPR on [target], do not intervene!") currently_healing = TRUE playsound(loc, 'sound/CPRbot/CPR.ogg', 25, 1) cpr_ready = FALSE From 4aac00446b185c073d247fc469686222cfc09518 Mon Sep 17 00:00:00 2001 From: HIDgamer Date: Thu, 12 Sep 2024 14:19:25 +0300 Subject: [PATCH 10/14] Minor tweaks --- code/game/machinery/bots/cprbot.dm | 8 ++++++-- code/game/objects/items/cprbot.dm | 12 ++++++------ icons/obj/structures/machinery/aibots.dmi | Bin 18615 -> 18650 bytes sound/CPRbot/CPRbot_poweroff.ogg | Bin 0 -> 17019 bytes 4 files changed, 12 insertions(+), 8 deletions(-) create mode 100644 sound/CPRbot/CPRbot_poweroff.ogg diff --git a/code/game/machinery/bots/cprbot.dm b/code/game/machinery/bots/cprbot.dm index c3b8a0c51106..cd0b92c24596 100644 --- a/code/game/machinery/bots/cprbot.dm +++ b/code/game/machinery/bots/cprbot.dm @@ -243,7 +243,7 @@ target.revive_grace_period += 4 SECONDS target.visible_message(SPAN_NOTICE("[src] automatically performs CPR on [target].")) - target.balloon_alert_to_viewers("Performing CPR on [target], do not intervene!") + target.balloon_alert_to_viewers("Performing CPR, stay clear!") currently_healing = TRUE playsound(loc, 'sound/CPRbot/CPR.ogg', 25, 1) cpr_ready = FALSE @@ -270,6 +270,8 @@ /obj/structure/machinery/bot/cprbot/proc/self_destruct(mob/living/carbon/human/user = null) var/obj/item/cprbot_item/I = new /obj/item/cprbot_item(src.loc) + playsound(loc, 'sound/CPRbot/CPRbot_poweroff.ogg', 25, 1) + if (user) if (!user.put_in_active_hand(I)) if (!user.put_in_inactive_hand(I)) @@ -291,7 +293,7 @@ if (user != src) visible_message(SPAN_DANGER("[user] begins to undeploy [src]!")) - src.self_destruct(user) + self_destruct(user) return TRUE /obj/structure/machinery/bot/cprbot/update_icon() @@ -308,6 +310,8 @@ src.visible_message(SPAN_DANGER("[src] blows apart!"), null, null, 1) var/turf/Tsec = get_turf(src) + playsound(loc, 'sound/CPRbot/CPRbot_poweroff.ogg', 25, 1) + new /obj/item/cprbot_broken(Tsec) var/datum/effect_system/spark_spread/spark = new /datum/effect_system/spark_spread diff --git a/code/game/objects/items/cprbot.dm b/code/game/objects/items/cprbot.dm index 403e0d40cb8f..0e4f4b61c470 100644 --- a/code/game/objects/items/cprbot.dm +++ b/code/game/objects/items/cprbot.dm @@ -93,7 +93,7 @@ return // Create the repaired item - var/obj/item/cprbot_item/I = new /obj/item/cprbot_item(src.loc) + var/obj/item/cprbot_item/Item = new /obj/item/cprbot_item(src.loc) // Check if the broken item is in the user's hand var/hand_was_active = user.get_active_hand() == src @@ -104,10 +104,10 @@ // Attempt to place the new item into the user's hands if (hand_was_active) - if (!user.put_in_active_hand(I)) - I.forceMove(user.loc) // Place it at user's location if hands are full + if (!user.put_in_active_hand(Item)) + Item.forceMove(user.loc) // Place it at user's location if hands are full else if (hand_was_inactive) - if (!user.put_in_inactive_hand(I)) - I.forceMove(user.loc) // Place it at user's location if hands are full + if (!user.put_in_inactive_hand(Item)) + Item.forceMove(user.loc) // Place it at user's location if hands are full else - I.forceMove(user.loc) // Place at the original location if not in hand + Item.forceMove(user.loc) // Place at the original location if not in hand diff --git a/icons/obj/structures/machinery/aibots.dmi b/icons/obj/structures/machinery/aibots.dmi index 1fecfe73b336e0f4425da2f9e3460cb7c8844ba8..6f5785c4d7b4929d6afcef295cb1da4405a58682 100644 GIT binary patch literal 18650 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 FeT^Q! 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 literal 18615 zcmbWfcU)6Xzb(8&?_Fs^KtxdKT|t6?f*{QTC@5Gc(p8Y2L{LysiqZrT5D-+Ff)ptU zf*@T44ZTQ{UPDRS@5JA8-sjx+p7-8+?jQMNXP4PCv-hm^UF$oON7j}m+#F&Y5Cn0b zHZ?vAK?p4PIJ2>UJs%^BkAojIA?NM=ja}|szkSos|E8}G1O>e;O6rb{P~mItUKM(J zrANL!tvV9e(5`7G#)9UwdTxI=H7CDF<}p%S%D;5%$)|nC2!oyG z$G@3-ujJibJv$WoR-(2qa)s^NixG*Qh?$QtOHTChwYp0$vshxS0zydSwJP5ja)#~^ z>l_)I(m!6uo^u>(7d-4adG)Bp`BO((Hw=EpzkjyIbaLaEr|S7?>1vXl^FA_D?~h(GPF^Dx+MZ$!=e zdQs=tu%_658W+S_(h`VF*9;lLhb7hRf zNH@6i2cp&TFX^+AB?L}9Zi6!dN|(aFN@nSCjRYkz;z2zJmS{#(ziio{aX%n(?)BA_-#qH-IR*^_??gyC?ZykMOWDctK%L8kRR22)|}B z_XO*$?uPLVx)~5CM(p*lm&@55b93_-n;3AHKNHBlPdv~dj5+@tb!B;R>rj}!QwN&f zqPQS#gTs6f6YIUCBX(+^8YJh#>r@-NYiPE_I8gVQDzy8zZcJb888r1I=}CNhFwK@5 zy3H56#fFV`@>rlZ=Ep*#;VnnnQ2$}bz=-hao5xFro8&1u6yJoFwadLk4C4fA@afZ! zvb5Cu1phWD0He}z$CAbhsk?P8uA)~_P}OHbkP+qF!a~3XYvrfGMRi-a$5Q!3wS^1n zU>W>5l4reTj`sx=!I{g&(8J@C^9(DB9wk?p35RgvD)WjQy zmE~0{KmSK2$RA@ZWwtzMP;^gAnU)g9&%s>7|FU=`N+-TRcH_pTdBe)z;wS9tSoq!< z#0V?zrA-!94zLnvoW-~`SI645w0K5Ji3}#9S1xV>HF~KLy_;IW={vC#S2^k$P3ltb zqxaUfNuyoe7{?Adea3Us_I^M~^YzqAPH{g62528OS}HrO^O>Kl99Zz0yOYEg$~nGkghDK)(vecy7<$T(CIwF)Hs5XRQKXWwxbT$vs9E2i zX}*Z3##IgZUrV%PG`^t9XRksMEA#tt0n~RU(Aj%mB$EBBU7C^zm=FeLO-*@LzvX=2 zZ!6kbTE-5-og#B_d9({ZHAS=%pJS}}i${kZBS$t4c1;&g{4V)i$I(dRx=a!H(3O^` zj)YWM;q&XfOTi)=v)zRAEbIC8sAc7d(+$D~p?v85Qfy)t`*+^IqoOeaDg$GZ6vX&^ zUD2Q0RyGEFT8&Ntp?jd6=Xgmf<%eS8n!rNG*csfVj}nO$JQpusba!_*AifxSy~Vv0 zywes^RTSyec@phOYP`Z2?|KhInM7WkD=|SpvEjj&@y%V-P42{;a2EX=58pO)o8pp5 zyyAx?5Pqw(#V1liDW}JO9KZRQdCVDBUeRTSswk29x;xRWCQzgQ#zOe#gBPhAGo9Bj zxy7y(nxH**8`1oQ<>iUol6oQ#-#+MIZRw)`39XA)D%vv{-0GiRSSVe~TZ`h}Pk*~h zT9FzXzlM-6u^p>|Oji}8;~?5j3yOs)vx62Fm>PaqHFUt~lzA~3FSOZ5(W zgbL438LWdZNtcsHub~WwSd{vR?tfx&fm;O6^ZQ}E3=Xyb2q!nPj`>NtuYJSQ&5dG| zFML7xic21j=ghGN6J#wpQ|jT4O&yEhf(6FyN1^iY&1h?3vEOyj7x7Wz>=JJN%lk8C z`V*GqTn||A{)H^L2!5s$_Cg`@dMEhqpUh@5;A6UKBah&t?=*v$PVnu0AOQV$4kZ>y zvRC`hon?WZsP=nl0r0OG;YshzL;*r*nM7RYDk>_f@}Cn3(?%q-p6)lE5c4T^^2<_6fS>GB+iY9Yt=|FKr9%1C1(U2Kn~T<$-8)0*I0FVwo4FnM$}xl z-q%x94Lce+_7#ewhLZ1AZvZ2xJ?r7>9v=G55~zAcH(cyX6|B4fmaxD)&P$7s!XIQS z&CfegKHyw7)Bve~a_`o_LN=a9m>@qLY~J#FL?r%0GnFm3Sefw19d9kns_@B&cXg%MN)fB7EH&M%P7dj)@z8%L zBMxfTThkH;3=!}&aw>o1$N%_VFdTkP3botX-CYt^0)_zsD}?EY zs&qOX@>#IVt&RzjS5j(zcj3LEQq7-f;qlsom&>+w zW5qi;GcM~3%0Jxh3k|!LutEUzV+xxOT@~NLIO?v(Et6MgyVLy0s2c?|q+<3j92Kc{ z!%9UI*Z=4ZTubo7O`R?$ZEQKzQKM--Ebm~K7Zd1K!4CK+S?S6O?M@^0naSzZjvCOy zw_iE_RxsL-g9fbzS#U@3NIagO_c(8B%K)vfulue2K4~#@e!m=coTb2X-+}I7+01 zb4u50Jw4tjZ8Rl=t*zCw7-E0G%@#iTK6~L(?qzLz+U%bMR+Bir&9%al)XTIN;nd5- zKa&?@(tYY`8c_{B%-{ht+;8K0Z_dpYVd{~XpOLh}+QRWECTD_nTG%9M;oxhOfN%Zo zn|%}%Ot}*lOw!P&f75nnRejGH>N9-5-81=q)K{8}zrN0o2ZYCvmXl&OaDi0wjwY@& zEA@UJ*HD!}uX-@)UaYEg>W?31?=JC{Ae=kC+KkftHWvt1uXeW=2+GCY%z_&hq_YA$ z@3tM@Wd@qVl1@}=+gK5%JVG9tAV({A~(HySiGj>6Ewq_qRHhU2TN83$e+kG`LiujJgROLA*B-FG|y zF*yi>ZfNap;WgH0t_xbbHZd*BIn|%N{k12RVnjXd{LPxG+{Hl01Xa(iJEm5FQJ0IW ztC^{+o4i3QB^YjaT9tj5U?8k~Wgp*5gtPVFF};u>eJ(Qx*6y))IkbF7UZ?`MR-gjv zfq=Faj~+06TIbd+jetm?evKb%CT0RHuiD?$uC0X2=iy86gTdITxy)V01J7`qBPD0n z%q10xPPDE={ux7$r&nHnxo1_}ydG%T3Xf7|X4e3sVDODsRtY*2NbH5u(B-f&(RT>>H-vFp8m8*2@~|T`ClpbDFL%uYRj- zh@K~H`~i<^vzu~axIHXzU_}PANs~Zc9@vVF>zXXRC&yQP*Kj=?(bbqOWbNl0^^-xr(2iH3>EN#gkg?z=AX zFWS;f<&|SSPR89?silTIS22MEX1mF5{IJ#yAZF^MjlQEcSamQoJ=&+m;}cHpqk`oM z8&}9`@EiLm%9I@D8c9agbH;Jv_jb{rfa^#{O8uTP*RDN=RL(0(O1C0*hzBQPA!vLw zbj1Zn+kLt|h6~<4vN&9QPUV{D`twZ6qv^OPcVDYGqSlu2_QFZSgAh(5aiDEE&Z@S;misVA}%#q@xKZ^qz%D>MHq1^P^-n-StF)M>q@Lu_aRNV&9?cD*Yfq^!Qjhn8 z>mVsbMQ(Q_C1Ql<#A=*pvxvQec-WKd&RHV|2l3PvMi2+tJn?;aailx_df>k};H}eS+!Y$NG3sj2g(O$p$Hg_i_o$x>NIwo!4zxga#Q3J$ ztHy8bet;(D3MM=(t}G_DLf0lB^4Gja1gK6 zN7hR6>J=5OmRJ0KZiV{#X5K^LrYB>TB4Ty*NDF@T-QhnI+q=bj!a6^i4)-IKS(oc| zl`!uJPL@!E`k1x)1U+o>smd1T3BSt(t^RB+k=gsZSsyr91i17FIFLTyd#i9cZtr^f zBnHzbuk~dw>0q@m3s0gG&i{|~x9P1L!rcpdN}gZfzgg{0 zZ418=a=h%#K_&RvmRy8)anaYRhDd2|@cvEk>M-Y03=ZF4_q#z-xB6g%>P$+(<;(&z z3o+iS?vw>%Y3{rAqMDk>$ynsry)vh0$x@*DA}C?hF&64^&tD3)l=XIrQQnykDvTPo zaO|R2VTcs+E99darsIQIz3&!uVb2z?_?sBaNegZ%wsozf>xW-Z2@?L!CYE>4l27L8 zRXI1DSd9|@3`C6N(Z`$qa+WXQgM~WBM;_6!DsbgW{I3g*^E7v9Ts3XN@K0-qC^p3j zEefq#MPOG#Hvg1%WUISr=;o}A={iHU$K!c{P!HQ4ioGd+2~&_W()sBV)=ac~C)(}; z;xSXM)(5jwZ~&_Wgk9h{w(8O4eR@GS67{Dv>8_c%`M@VsEejUo-1&+q0A*EGiM+Qh z;`k)Cw~oT_l8_rsc}eLxfv%@H)8yQL1$gf>*?Vaqc#3WLWa#S9rF$7Bu}yUrirUAU z7VN}5PtSI*u}Df18czqPd)8W}ONU{+7`)ek1fF#Zw;!&*fvB8C!1xg|?4 zsa0KBouN31(i0~0g3`Q8)W7`v3GD+k)0y5<-sO1xavWub96-)~+>L@A(Va3P`Oi?v znK@FZF`CtdYuwk?m{7o9h&zP%pM#+H+HV)*4(=pm`oe<7@A%F!{6S@*TszS`b8}s# z?V8$^^l^e%p%P0baJAQ6Fy-n_>l;g$2-7{kk1_WhDGTiHj*~pf0nvvNv^po3*-SNZ ze!EP#Js3#|16uT%L?`cko6*PlNkM2Pg`d1mS zevlrV{2ALl zjUxO1$0Ogh&#>=*l)Z-hMlYqX@XTg)9XClsX2X_ZXz5(tPZ9Y0c?j~*rxe+3FZ|;t8>rm+J-^R(z zQaKh)fh|@tITps=jfWDu4)q98?#T;4D?C7pe{=vn#)Q}^*LbKU4k%Fs0?uov&vv)< zB?oB;+lX+iS4dH{4I+#I_OzxJ9jD1YNxRn)p;oPT^w$1#{lJ9PaphMJ=h%!t1t#LU z3QSmQQ654=C@+yHy9-wrmq++H^EY+(@vvdXJj-@RUbFT1ureXEApK_N?OosqYy?)d zFlKhMW{0!(&D*^;p>BKalXXgM&4}MY*O4*Z%{E>OCXK6jt9KgYB$v2od+K>iJ&|yo z;z@kp^WE?8_@^uCtL_Q0$NJ3$EUe*s2RW=fkLd4io&)A~FgXFerQMJ58rIqD+O+G)zRL1rek}`x0V8sm z_)-1HN6jTSKurpl+jkal(}Bp}+9l-5w=S%G?Kn?rwEf5Z$8aC1$eq)BHdk8)VO~n^ z;lVc?ty__SEz|j=jUu=uf9EMi)`ioxEYI9EFvuy5XkLQANZB6bI&R5&axx_dJ@dMN zieNX`wf31KcBF~@-1BeRR0Ra<^F!7s3WBr z>aY0Bv28`f{rUk<$>A>2{7ex+6r*$5FCS@ZynF`xxa&NySSf1WP#2z@#1}Z3T>RXZ|p*x zU4ldkN+^=`0j;obpZ&)j7s%j>k{|J>M9M&@)bKAE!8VN5J=6{Y-$x4Rz#JFICe#YII$oeLt=9i4Z{>s9OSb`&a# zh;af+{)dQe+`fjoqqGC7+xvL$N*)zhdiU|j&cs+6b#;Ylfc0a*nrGf`BEDBYMd@pq zruU*^NlN7S{Gu1wh#M-&qXP4J2xgz?ZcX0CtZ&R=Zc-K|;*4!=52BaFqOS(JMqe(T zpA4y87=7|CZb3#VosE{d((~+@LBTlra-1aoh5Gs!`Gu0UIunWi*pB#8obf9E3Ga;h zp%j*e(c8pEr}gylZfRT+3Dci3uzo1nuiaSpoD~y}`(gO0eOfz*cLZoL6`y2G82;?= z`HiCM*hhEkY473J7YZx09_$~?Ks`hK+9J%4?Yf1NGbvMqMx^2eB;oq(M`C}q*Kue( zu%s{v9tE4|?Y2ut4d4~hwhYUHJ*DnVmvPBkbp&)o+t#@;KVTh~B!1+^Y}c<1o83l9 zM3bJUZAb0BBMpHx%5Iw22ph&{8UOVM8JGaVD+6~5H2PdO(>^tcQO>pEHo6D@-g>CS zh4Q`!PpK>)<&-inok)ICY^U$#k?17p>tWn`B3uxHOe<^rS>n;2R-0nq?P!bT*Q$F> zEVGIg*V$jiqgb|S1vZ6ur%Z3SxMXK%BO*~dQuBr^CBjQ7C5aMW8cU2zl*geD%wxu@ zE0m3-yS3X}e}+&&$AN50BPLD)uQ~D%fTu&<4R+yhdq*fuFrbFqox>4z4~$3lG|xLdddq3Ik)0 zQ+wd4$_kWdGfoJbRB-u5lF}EkwUa6`6n5gB??Q>}Up^mL-?8JwXUErv<;})@5uLUw zaC&Ff*CogHB}gEgLb$1uUqWmEaWi`oJ$WdQ0A5u@{)PS7+ubL`!O7Q6pgFfipLj8v8BD!Vi+AG1oMFc@l!j2vXA_;* zz9d0~7cuGr@fSuwoLdYLEA~;JX8unsGb$qv9-!$;OA<^Bm6a9uo$j~Yg zb&E9B^hklz;8gm9&AW-`TMf+vtyy`IX6;i(nP_uPMx7V^mZ^vC zjJrRz2aCu8@DyA#k&KYao|zyD8rgOjmsT8HI6@9h@d*60B=kjZ8>Wf zc-x92ZO>!OP3jOMt&Ny=Oc3%5 zAqo@@+uz%C2yM&MM(WDoMh8OLRSGKqV=jQ?Lu=b2`=ba1g$^}xxO#La0SaPqhIEE$ z7&mCHa?7{`5kB2*c_djEyqfca8cvfuoGYY_QA9A|Kd#_^<_57~$@+r~_%V^rmM$ip z^tcSfvIrAY)d*=jFEKH#u3+dk4QW?1B|Z?c?ZUP@DRJ-af`FB44J4ITB;o_9z_edq zm?b!xjUsf;UyRsY!ofApO_evmeg56Pjg~#K9X$8|)*Mvc)#m5bg{fG>Bw3N1d2sP_ zClu}t6J8wx{Liryo@px5vubN)P`mK=m=o@ea>N=J{;WZJTibbRQMzB|9AbsU_{$Ri zubv|vb)U;-0zkI7YVZ&oeUL=G#Q2$?{af1-C?Wr~Q|}?ELyT`-UjK8fHVzAUR?=vhp$;LAhYGTYzs(ba~ON|eal?2A0Y8&mcODgOh@nUOsD zdl*v1`8wCl^D`oDf*@;r`zgf&4dAox;W5N254M*2?^yBJQZ9X{a)lnYE|*u1I;Zv{ znpYj&Gr zCU}j)1o>9Bkc98|r*-%$(34P>+xkw9C3J}~YZURY5(RJ>75KEVRJW{A$&PrdZrSAc z2N3^-H+}SYunkQu!JVwTUaLD@S5w}O_>Y#}UI&AMsi}sVITtKU*wX$220k_!DhPyr zFR@}lbY!CUReiB*@xCc^>TgAOCC5ZRrFWS)vRItz-z8)QGW;H)wRi7xPA^mYqNHA& z-CAmV^*;R#9!11g;sA6lI#x(Rek)I$CO{~2GTCpMpaB>?=kAo0vxp4khaT*B-IO9$SBjY(ZV~cR#ws*KIjDWUvRnGrCeW#H-#^jJ zP$s=UX1M{zSQ^yYbQPy$9>t$`8{}q9O&~yuy+G76i1cBDKTV!#aL7CtBpU-K&UaZ36RmUV$+e9ms$7*;tt0 z^!|dL%31-5>@9+Dr_CkN@isb`Rofus2O+Oh(3wQ%lBi9GlOY(+RC-hF1m^r7@*g-$ z4Q|Y#res#%7JoB%e0%a)2a=$1+!4|^46SVvME%TKBz-rtF^D;G%!3g$H!rS0ino(T~@utH+JewY5- zM;mQo;a7pOPn~+>8cx<-_=Cg-k=eh@>k-=u3Yw)>3@nV}-eg85OSau!-)ZH+-4$I{ zuNz;YBaN&^2E#MUdj%OB?AtG{tZso^bgsZH6kilTHB(Ek1Yv1_ZfE)bTd8_}M%}Tl zNs?z_$j%|U@!o__4<$RFJVAneC zhy&y~n>u{r=+10}69S@H*T0H^$;q~b7c2HF{XOlW$Tzm3Q|rVjpns zWvNBEE$?H2p63gRlN1E%sGE1b1Aw$C^;tT+wfMyCd!9K;c zNeHTX9OL()nt3H9r9D&h=?_#z7L@TL!>{C$V42g#A*oN}ufwbqP3M@I4eFP=>Btd1 zSh0TmliK{r{e79*dP7dF{Sgcu>03f9yrPdkFf;d)#~eDov+Rs1E1P&*1UP~q!r9SC zFnQ9TpGA@tI)44I`FTLwvWDahNvs|VZIc)$>vdVvk9MA64Piu~b3vJVF9 zOMj7uDpm}ak2fNqZi`p@Ga1sfm)>Gs4q6@3`ig=-?fH{fzZV=3D04c;V2_@GhF&J| z-+gh5+aikjFhH5VC{fEQl_z6D)Alr9+ezdJU}--3n=W(fXF{M%sLZ$N`sMa zM@yF4Hvaif(Xlss-G%Eu#fWM$&it!6jCeeI&RUA%1z=27x!?^T{F<`3^yB>>o1fyd zpH7fXcrlGL;e03gN7Q{8pfH;|Y*$Kz8Ree*TC{oxA`?KIf_%O~U_7HoSO22Fc;%2{ zm7~%@3B>zHO|E>g>B`Bc$%l?n#^bgoPw(4Xr%x(KW9- zyH&B!{tO;f1`e#w z=3}3RSAk#8?(MW!M95wuZ-`-QDRR8Frz*2f=Nl9zV+D$+=Mdlp-K{mL9=($6j`+Sv zXT!f}r=6_tJGaH#{CCeeev+m&w?a{v?mJOl0Bn4xR+~1=a|Yo<)^fc6np1jQF!g^k zoAkd*^BJ;B@!YM?Xd$*#1(AQMd_42W-s#Kd)lnpIb>S6MCdH&%2@lP2a*}z@XSL2!Uql(vwm@$-Iy@Xv?}>{U{7`A?bU8(90Nqj4m&OkZz~qr zmw7R3!%y37$4^1kFDaq(JZ&ZH8@p%u`~=Hf7cG+3karlqNhC(n+HJE?SYHJ(D9Ar- zf8y1V!3zrgbpSFC9)I1XfcrSsxoN(9o()>t`QXJ|e?LGDdaFXaZU))%K+i%aA|4=C zx?kuzw#gG>ZfQ0lj+18^KYx1PU#9-bhDu3tX}6eTd<07QU@(aaXvV#n zMHur#eCE>Sq(&yFk=orA#B%xbjq%}XeEz-DD|H{eg3U;gBaN7uFPj6~2&irE5}u~$ zW7N*Khzi?eiW<`tfl6TmMW2N+Lb*P4o>dAeM&SKWPvJgs*v5$|d5Tk5p{e?gMb*+W zrfFf1`C7==v%`0{QNM?)4=Om8@_`$kT9!X z>*Zq?9PjUz6sZiO-sV0$Jv0*NjP&q)XS4O2YtxzfJ(6fN5TkAlcqE*4tJURu4ID{T z6c)3ozJBCzpx4Rv2d9jv9=T@FLl!X@efAb5x{lM-qp4ozPO0~#($uK>J~Gdu(R6H) zeCREf@1spF%3IG_ox$(^if~~TOA*Bqn4IK!?X$c<7q0;g33+&m1#CyZzTINmkyZv} zd=5ZBV8fuh(`2c;syaU#?LtNlRvp1hJOa1yU&2VzoGZMyS0eFA(Dy^(-q8r1V6zXB zpC6*_Zt*K=RXUHB=X0&~?%2sCHDkUpsel#|CFXFQZG`^e6O!ys*VS{0a!Ep}MH%=Z zB%P1OX{tH5;MAUiqg$fjdiu`~ZYyY`GF%$QYUBiXAf%N?zXnQv(azTJzEnAOW8lD( zKOnz0VVVc=ag0vJRicF6YSDn(M{zOV17+pqolIuz;l|6JOfRgSBo@!UWkfYLz90qqd!g|AR02-%5|0Zq#t?Al%5sGd^ z;?5Rbl(#BJ$sx6fXnpL8d43x)+tF(_hWnK zjLpqs-XQ+?ADbqgBz|rDGE@>Zw6xbzN%)S=KEO>TcM0F70nY1`-x417sl2*vGG056wK0MWl%hYreDL7GnQodk=rT~>N#!NR($EHGuQ|vX!_hg zgfLhiQ*+K5gI#i#X`bG8Ijsx4wI80~mPcxG-nL><agFb)kVTkB4f_tYuzZD6E4H z=lx!!yup_4}i&F_n0&)#cmYqPeo0bgdIn?Rk6FX)WY+}pKD z`Lr0zvn;wXI9rdr;$7UteD~T)u@Vpv6qZ zm813KS+mMfX5{piNmUIfbvlKlTt$5?fjPTMxI=NfabtXOpv-1BZ;R~{+-!dMDO{S5 zUEkQSv9Xbc6_*?v7*+0c{NZ8bgAN})Os_4dSkbf@8%DnNin6gyBJS39!|37QikmF1 zyOBdqhKk<`E(dXaLYK>vOYY1bDXTK2b!F)0ynWjmPqUM}Y)a13fZE&Jxp^U1Qsj{G z;2NlM`F#<1Z5fkonzx2r&zEB0$-e$gskI)hF_g^Wc;WQr6N~IJzlzdnlb%j4A0bV7Va_MA{NDJJdX)jJwPVex($}! zTm(60)ws+cf-;C7GIRrDtKr|5$2ikzt*f5cpx1#1zJYA7wc?C>utP8KIMqmu5<|@I3^TgRY*y`}1 zYo{s#HE1_hU=XX&+C+T#3U|*9)e3$GTH~Os@fBK&75YKG)ys-0ChUBs2n`h32jA*L z%`nC5%Cif0U&0>JI|JY;N8YidM zmh}>nA5(unk|YSdLcjmz6tPVMQ_jq&z%z|!@J&$?)d?(S|Nqh^o$zM6G$#NJ2lzg5 zh9`35R4By_5EjtLEeE(hij*`B9Xr?nDv|muEQnO3j{vqaVi5j0+@=N|f(iA^cz~zf zhvCpDnU#9hDJGSTJPZS*5IlkVfQbi6sMcY^+~ydZEOYMYbH4bAwv~gulDQv8Eh< z6{MBm-}mc$dr8Y)A^Zi$T+$0sI=};7O`YK#rCs1*ExxVuMd^B?)8KBTQ~ES3_N1@g z1Mt~HCuhBRWNmoaHppm=FLh&XYtEpmk1eX5+9l3oI@z87O{F%bvH#}M|HRpNOZPW$ zb?doRbnsDOlyfILR^SNmC21`~ywC<(cMtd^#W*OxHiJMM*k7?!13`4s^S01zyYlj$ zWFa!76iuf;z^Wfs@cIxQoZ?@nZPz(=JDWdAw}Q@g<&N$eeI=&=%Y(C2gT7eCV)q5HT8b2Xj#X13i=|5!(Zh|K-b`R>uj%Q6 zbCpUGwXRaT>ORXiQ=N*aNz1^&g{QQ;kv!^KC?JcLe3MaAmksDW{HfwS0&?qq#q1}$ zdYCh}>6fMiG&KOGu#X_1v0bH`T@io@fOukoc%m6!%m1EVzD(EWhGnck(7f8(Xz-~V zim#E1pel^q1LD~6Sg2(nOveKaawXx%4}sN-1xyN`YVP;Wp)}m)VpB<(6bj{^&ff6B zlu^L4jQg&!8-W)1&;Qb578W#zPikrmz5`F900SCb z8k_UK2Gmd*rT2}+bL#7YIkL}HQJt$Aw2yflpk{xi;XZg(5b#^!EtzBf5u@_dN-mK< z+n9lZ8YeRkEDr8|bsVf#FwX=r5T|ASxhtr~Ul+^ZOG7``X|w*vdVc(P-Abp`V3_}> zFmKF9+J~JaqSYyke8(UhL=LV5>w>_dk1t;gXMhw=Nr|RV!p>ysONfe!1}%LeFyS*F ztsn(cAAmo#h-U}DHI>f+*3P*7k1WG(~ZXzg{=Fgpvd?!*f@uv#!ZvLz8PF zT4dE;Wn$(>s+yYGl#J>lYj*p?R;#2-WT$FaenGrdnHe4@<}6`8QSD-!ifC)36GA6JU^9$z)mTQVi~SX=V)Yy8OT0cPpm{4dsMk-)Y`pWoaV3f8kmwPV zorBYnHmV#*c9UGm#6n!}+>=3N2~sZ&iOeS9hKMwEv8T~-Icux8AcM*U^)o{-aWlG> zMao4QQtgy{7Ebk}%jjt>S-ari>O(l@Yf(-E=u56EAw|eAFed~4TBE;0joT(oln&C6 z!{~%A1LI4m1jG}O!*>-97zPVubD0&uMRqK* zt-oL}|I64t?3K%ZV&tDHPOB9^DX!}?qx$1>nSiRFc&)fo`rtmeZTndfJPD-VD1uJP z|DsVI_ej8?QAd-8Y>x$Ro_M|EkimOr;~c!PeGYCR&{XB1_bU+Om(d-q9Ih|vu^*8| ztJHe?9rovzI(3_MItzYd@olJ96e!rLou60I=bV6W{iWd0@Kd>NW+I=KPqW&h2~Qrv zql76O&}#HLQB_##3Y@0td1eI*SV>i?badfY@ip+`q_0oOww4rDzCg1}QeUy5=P{^0 zxOEIDYX~2ri|tzlqVPO5tZVq_A`q!pX@GF;fq58uw^{vNChnuxQAS86u*uq61=xs< zBehg|n>~D7L_w=HQ37gbSPkCm?G4(^i&_DC5fJLtoo>feeX>4eFz{NUANN%ev(@%- z`g4j*KW66-I~UGV1#*V(T^jy{E6GNGQj=F5>>RS9T2V6}Q|bQx`G>%?1cLMp7DHF7 zKq@CNmh-QIN%ZGgDd9PC16!`IG>N>_2p|y+{giN%nI@`5ogXck^J&5Ghdp#!oHopnra9-TL3p540qxUODt)s+DM#CtEJK7p z6YQ>yEF5_YD-OcIh|nBiE(aQyE;{i^JU|{r1oyt;uyqk>*CBO^xo3&8fCQ+ zTFa4UL_kh``yvPnCx2D(e{|qQ`l430W|w%QNGKx7=?;U)zp1Bkn0g&pmb|EY@{g-_ z-5SpfAUals_wH2#%atL%lQzpt(`t;TxDn&s6=u23yjM^!DA|71jvaIUEC(D9NmrJa zi~QPl1q6fg#mE=HCGi~8lHC|*rIeUM;`^XPiEGU~&gD)XwP4-R)a>ka(&ilEsr@=8 zWkM*336=3%dCj9Ks&(mOcBK<(EOsUK|4lJ z2UiU{Dwgi$zj~s>q|uAydB2Ux6}8VLK5szAo<}i2J8`HoimEL1K5O>m%Nvek^Y=Y) z?kvt##OGg;kKL)Kt|n)0@2Fo7|5y4%k_{~J{y$&-__cP$SCA>e?}?b^<3N74*zf%R z^GeCYDgk^~<%WOgxW8J;J)qr0A00u|R#)==4xSIMrN?3|_3qPoc3;2UPjqip;_r@A zD}o@FO#1%Eb6pdxMTOrbtP zr3Pj<^!+1d@PuAwTqcFXDpN;-j|pJC_{@19T^j)vUsMS@6i1U|t9x`5F~!r?02ENE z>i`GX>?u2wM)~0>`Hx{47n4424z$udX8+uyg&1d`p-SwIKI_T^>Wi9EWPpLj{Nzo* z35#!a<#96;Fg*`6-pl(8)0w9EhJXPMB^N~U9c(IsjXrEF>D^t)N!n!6*?(9=4!Sw( zRONgXPrftLbrn(pzEHL<(iiuUs|{ZX^qOmw!<-$9zB*OFHXUMG2?b}d?{qBEcQpeqC17V z>NykBcAQYJ+ZZb(?@-Q2uB_s=Fl?YT0`0qVIT8VpdHa4>(89T`A|AzKgGoss4^?~R zgAkNS(+-7Y&OT37W{6Xir=umkyeEqC-^JVwb%Rfwov;6 z9?fZeZ*NNSdw%xjh zqU+5}Z$M4JsY@on)HY4kj5zLDRwD|AKQqm7|6xko-IV3! zqdnH$xm3&F(+}=U*CWHW{Xpvxq1))!4nrHG@WVcnI}E9sZiqKlnXx94!Ax*nz5vJ? zrjptX_fa<0Yd5-dPbDVAU_i=XLH7qHJ+I`$MKnr>?fw@+Ki5qv7?fugq^6d5O%7W@ z5kuia%VK9H@ouZ;yX@z!~KK#XfAkRqk_n`2KBF!Z9|m3@$e0D)7d@ ziBF?C$zVAVTH}TkG#-;?1aep3#IzZrwU#3Q<=k*n@1;vL_DMCPGL>@y>A zMl00O4B9zCg^}#wz0JTvY@fac{t`fFq=*YlZmxLXCn zvxdH}+QfmGvx9}8Yv%?=PZ^p(zYe}(0(nz4fiem`QZaD6C-s6LcNS^(_lN!FUTn2fl6?j)FfVf=-{bG%hxDj`)87xyh0) diff --git a/sound/CPRbot/CPRbot_poweroff.ogg b/sound/CPRbot/CPRbot_poweroff.ogg new file mode 100644 index 0000000000000000000000000000000000000000..9bb77dce2eadee0065b7210c842333b4b083b42b GIT binary patch literal 17019 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 X-Z=lEgehexNDpUwy0|#b!T)~&A7L#W literal 0 HcmV?d00001 From 0827daef9cdb59aa02f794fb88a011719d8482cf Mon Sep 17 00:00:00 2001 From: HIDgamer Date: Sat, 21 Sep 2024 14:41:40 +0300 Subject: [PATCH 11/14] minor tweaks --- code/game/machinery/bots/cprbot.dm | 13 +++--- code/game/objects/items/cprbot.dm | 75 +++++++++++++++--------------- 2 files changed, 43 insertions(+), 45 deletions(-) diff --git a/code/game/machinery/bots/cprbot.dm b/code/game/machinery/bots/cprbot.dm index cd0b92c24596..5631c96357c5 100644 --- a/code/game/machinery/bots/cprbot.dm +++ b/code/game/machinery/bots/cprbot.dm @@ -95,7 +95,6 @@ if (STATE_CPRBOT_FOLLOWING_OWNER) find_and_move_to_patient() - /obj/structure/machinery/bot/cprbot/start_processing() START_PROCESSING(SSobj, src) @@ -268,16 +267,16 @@ perform_cpr(target) /obj/structure/machinery/bot/cprbot/proc/self_destruct(mob/living/carbon/human/user = null) - var/obj/item/cprbot_item/I = new /obj/item/cprbot_item(src.loc) + var/obj/item/cprbot_item/cprbot_item = new /obj/item/cprbot_item(src.loc) playsound(loc, 'sound/CPRbot/CPRbot_poweroff.ogg', 25, 1) if (user) - if (!user.put_in_active_hand(I)) - if (!user.put_in_inactive_hand(I)) - I.forceMove(src.loc) + if (!user.put_in_active_hand(cprbot_item)) + if (!user.put_in_inactive_hand(cprbot_item)) + cprbot_item.forceMove(src.loc) else - I.forceMove(src.loc) + cprbot_item.forceMove(src.loc) qdel(src) @@ -306,7 +305,7 @@ icon_state = "cprbot_active" /obj/structure/machinery/bot/cprbot/explode() - src.on = 0 + src.on = FALSE src.visible_message(SPAN_DANGER("[src] blows apart!"), null, null, 1) var/turf/Tsec = get_turf(src) diff --git a/code/game/objects/items/cprbot.dm b/code/game/objects/items/cprbot.dm index 0e4f4b61c470..b44f6982d4fe 100644 --- a/code/game/objects/items/cprbot.dm +++ b/code/game/objects/items/cprbot.dm @@ -1,6 +1,6 @@ /obj/item/cprbot_item name = "CPRbot" - desc = "A compact CPRbot 9000 asemply" + desc = "A compact CPRbot 9000 assembly" icon = 'icons/obj/structures/machinery/aibots.dmi' icon_state = "cprbot" w_class = SIZE_MEDIUM @@ -17,44 +17,43 @@ if(!user || !location) return - if (istype(user)) - if(!skillcheck(user, SKILL_MEDICAL, SKILL_MEDICAL_MEDIC)) - to_chat(user, SPAN_WARNING("You don't seem to know how to use [src]...")) - return + if (istype(user) && !skillcheck(user, SKILL_MEDICAL, SKILL_MEDICAL_MEDIC)) + to_chat(user, SPAN_WARNING("You don't seem to know how to use [src]...")) + return qdel(src) // Proceed with the CPRbot deployment - var/obj/structure/machinery/bot/cprbot/entity = new deployment_path(location) - if(entity) - entity.add_fingerprint(user) - entity.owner = user + var/obj/structure/machinery/bot/cprbot/cprbot_entity = new deployment_path(location) + if(cprbot_entity) + cprbot_entity.add_fingerprint(user) + cprbot_entity.owner = user /obj/item/cprbot_item/afterattack(atom/target, mob/user, proximity) if(proximity && isturf(target)) - var/turf/T = target - if(!T.density) - deploy_cprbot(user, T) + var/turf/target_turf = target + if(!target_turf.density) + deploy_cprbot(user, target_turf) /obj/item/cprbot_broken name = "CPRbot" - desc = "A compact CPRbot 9000 asemply it appears to be in bad shape" + desc = "A compact CPRbot 9000 assembly, it appears to be in bad shape" icon = 'icons/obj/structures/machinery/aibots.dmi' icon_state = "cprbot_broken" w_class = SIZE_MEDIUM -/obj/item/cprbot_broken/attackby(obj/item/W, mob/living/user) - if(iswelder(W)) - if(!HAS_TRAIT(W, TRAIT_TOOL_BLOWTORCH)) +/obj/item/cprbot_broken/attackby(obj/item/attacked_by, mob/living/user) + if(iswelder(attacked_by)) + if(!HAS_TRAIT(attacked_by, TRAIT_TOOL_BLOWTORCH)) to_chat(user, SPAN_WARNING("You need a stronger blowtorch!")) return - var/obj/item/tool/weldingtool/WT = W - if(!WT.isOn()) - to_chat(user, SPAN_WARNING("\The [WT] needs to be on!")) + var/obj/item/tool/weldingtool/welder_tool = attacked_by + if(!welder_tool.isOn()) + to_chat(user, SPAN_WARNING("The [welder_tool] needs to be on!")) return - if(!WT.remove_fuel(5, user)) // Ensure the welder has enough fuel to operate + if(!welder_tool.remove_fuel(5, user)) // Ensure the welder has enough fuel to operate to_chat(user, SPAN_NOTICE("You need more welding fuel to complete this task.")) return @@ -63,27 +62,27 @@ if(!do_after(user, 10 * user.get_skill_duration_multiplier(SKILL_CONSTRUCTION), INTERRUPT_ALL | BEHAVIOR_IMMOBILE, BUSY_ICON_BUILD)) return - var/obj/item/cprbot_item/I = new /obj/item/cprbot_item(src.loc) + var/obj/item/cprbot_item/new_cprbot_item = new /obj/item/cprbot_item(src.loc) if(user) - if(!user.put_in_active_hand(I)) - if(!user.put_in_inactive_hand(I)) - I.forceMove(src.loc) + if(!user.put_in_active_hand(new_cprbot_item)) + if(!user.put_in_inactive_hand(new_cprbot_item)) + new_cprbot_item.forceMove(src.loc) else - I.forceMove(src.loc) + new_cprbot_item.forceMove(src.loc) -/obj/item/cprbot_broken/attackby(obj/item/W, mob/living/user) - if(iswelder(W)) - if(!HAS_TRAIT(W, TRAIT_TOOL_BLOWTORCH)) +/obj/item/cprbot_broken/attackby(obj/item/attacked_by, mob/living/user) + if(iswelder(attacked_by)) + if(!HAS_TRAIT(attacked_by, TRAIT_TOOL_BLOWTORCH)) to_chat(user, SPAN_WARNING("You need a stronger blowtorch!")) return - var/obj/item/tool/weldingtool/WT = W - if(!WT.isOn()) - to_chat(user, SPAN_WARNING("\The [WT] needs to be on!")) + var/obj/item/tool/weldingtool/welder_tool = attacked_by + if(!welder_tool.isOn()) + to_chat(user, SPAN_WARNING("The [welder_tool] needs to be on!")) return - if(!WT.remove_fuel(5, user)) // Ensure enough fuel is available + if(!welder_tool.remove_fuel(5, user)) // Ensure enough fuel is available to_chat(user, SPAN_NOTICE("You need more welding fuel to complete this task.")) return @@ -93,7 +92,7 @@ return // Create the repaired item - var/obj/item/cprbot_item/Item = new /obj/item/cprbot_item(src.loc) + var/obj/item/cprbot_item/repaired_cprbot_item = new /obj/item/cprbot_item(src.loc) // Check if the broken item is in the user's hand var/hand_was_active = user.get_active_hand() == src @@ -104,10 +103,10 @@ // Attempt to place the new item into the user's hands if (hand_was_active) - if (!user.put_in_active_hand(Item)) - Item.forceMove(user.loc) // Place it at user's location if hands are full + if (!user.put_in_active_hand(repaired_cprbot_item)) + repaired_cprbot_item.forceMove(user.loc) // Place it at user's location if hands are full else if (hand_was_inactive) - if (!user.put_in_inactive_hand(Item)) - Item.forceMove(user.loc) // Place it at user's location if hands are full + if (!user.put_in_inactive_hand(repaired_cprbot_item)) + repaired_cprbot_item.forceMove(user.loc) // Place it at user's location if hands are full else - Item.forceMove(user.loc) // Place at the original location if not in hand + repaired_cprbot_item.forceMove(user.loc) // Place at the original location if not in hand From 7244c1eb7e595b8af39326cba059d3b44a58bb31 Mon Sep 17 00:00:00 2001 From: HIDgamer Date: Sun, 22 Sep 2024 02:23:29 +0300 Subject: [PATCH 12/14] AI pathfinding update --- code/game/machinery/bots/cprbot.dm | 206 +++++++++++++++++++++++++---- 1 file changed, 179 insertions(+), 27 deletions(-) diff --git a/code/game/machinery/bots/cprbot.dm b/code/game/machinery/bots/cprbot.dm index 5631c96357c5..b54e1246d95e 100644 --- a/code/game/machinery/bots/cprbot.dm +++ b/code/game/machinery/bots/cprbot.dm @@ -21,6 +21,7 @@ var/static/cprbot_proximity_check_radius = 2 /// Current target for CPR (using weak reference) var/datum/weakref/human + var/list/botcard_access = list(ACCESS_MARINE_MEDBAY) /// Indicates whether the bot is currently healing var/currently_healing = FALSE var/cpr_ready = TRUE @@ -35,6 +36,12 @@ var/state = STATE_CPRBOT_IDLE + var/turf/last_location // used for pathfinding + + var/list/path = list() + + var/move_to_delay = 4 + var/static/list/medical_facts = list( "Did you know? The human heart beats over 100,000 times a day.", "Fun fact: Blood makes up about 7% of your body's weight.", @@ -60,8 +67,7 @@ "I should have been a plastic surgeon.", "What kind of medbay is this? Everyone’s dropping like flies.", "Each breath a day keeps me at bay!", - "I sense a disturbance in my circuit board, as if a million people stopped breathing and were suddenly silent.", - "It's a Dyer situation it is!" + "I sense a disturbance in my circuit board, as if a million people stopped breathing and were suddenly silent." ) /// Message to display when performing CPR @@ -75,6 +81,12 @@ . = ..() start_processing() playsound(loc, 'sound/CPRbot/CPRbot_poweron.ogg', 25, 1) + src.botcard = new /obj/item/card/id(src) + if(!LAZYLEN(src.botcard_access)) + var/datum/job/J = GLOB.RoleAuthority ? GLOB.RoleAuthority.roles_by_path[/datum/job/civilian/doctor] : new /datum/job/civilian/doctor + botcard.access = J.get_access() + else + src.botcard.access = src.botcard_access /obj/structure/machinery/bot/cprbot/process() if (health > 0) @@ -93,7 +105,46 @@ if (STATE_CPRBOT_CPR) try_perform_cpr() if (STATE_CPRBOT_FOLLOWING_OWNER) - find_and_move_to_patient() + follow_owner() + +/obj/structure/machinery/bot/cprbot/proc/follow_owner() + if (!owner || owner == null) + go_idle() + return + + if (!(owner in view(search_radius, src))) + go_idle() + return + + if (length(src.path)) + var/turf/next = path[1] + if(loc == next) + path -= next + if (length(src.path)) + walk_to(src, path[1], 0, move_to_delay) + else + walk_to(src, 0) + state = STATE_CPRBOT_CPR + switch_to_faster_processing() + try_perform_cpr() + return + walk_to(src, next, 0, move_to_delay) + return + + if (!patient_in_range(owner)) + if (last_location == loc) + walk_to(src, 0) + call_astar_pathfinding(owner) // Use the owner as the target for pathfinding + return + + if (state == STATE_CPRBOT_MOVING) + last_location = loc + return + + state = STATE_CPRBOT_MOVING + walk_to(src, owner, 0, movement_delay) + else + go_idle() /obj/structure/machinery/bot/cprbot/start_processing() START_PROCESSING(SSobj, src) @@ -106,6 +157,8 @@ /obj/structure/machinery/bot/cprbot/Destroy() human = null + path = null + botcard_access = null stop_processing() return ..() @@ -156,15 +209,12 @@ var/mob/living/carbon/human/another_bot_patient - // sanity checks if (another_cpr_bot.human != null) another_bot_patient = another_cpr_bot.human.resolve() - // sanity checks if (another_bot_patient == null) continue - // Another CPR bot is targetting this patient, skip if (another_bot_patient in potential_patients) potential_patients.Remove(another_bot_patient) @@ -177,61 +227,146 @@ move_to_target() else - // If no patients are found, check if owner is nearby and follow them if idle if (state == STATE_CPRBOT_IDLE && (owner && (owner in view(search_radius, src)))) state = STATE_CPRBOT_FOLLOWING_OWNER - walk_to(src, owner, 0, movement_delay) + call_astar_pathfinding(owner) // Use the owner as the target for pathfinding else if (state == STATE_CPRBOT_FOLLOWING_OWNER) - // Continue following the owner if no patient is in sight - walk_to(src, owner, 0, movement_delay) + call_astar_pathfinding(owner) // Continue following the owner if no patient is in sight else go_idle() +/obj/structure/machinery/bot/cprbot/proc/call_astar_pathfinding(mob/living/carbon/human/target = null) + if (target == null) + return FALSE + + var/turf/target_turf = get_turf(target) + if (!target_turf) + return FALSE + + // Calculate the path using your A* function + var/list/path = AStar(src.loc, target_turf, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 30, id=botcard) + + if (!path || !length(path)) + return FALSE + + // Delete the straight line sections since walk_to moves better without that. + var/list/path_new = list() + var/turf/last = path[path.len] + path_new.Add(path[1]) + + // Iterate over the path, trimming unnecessary straight-line turfs + for (var/i = 2; i <= path.len; i++) + var/turf/current_turf = path[i] + var/turf/next_turf = (i + 1 <= path.len) ? path[i + 1] : null + + if (next_turf && (next_turf.x == current_turf.x || next_turf.y == current_turf.y)) + path_new.Add(current_turf) + + // Scan ahead to find the end of this straight line section + for (var/j = i + 1; j <= path.len; j++) + var/turf/next_next_turf = (j + 1 <= path.len) ? path[j + 1] : null + var/turf/previous_turf = path[j - 1] + + // If this is a corner or end of straight line, add it to path_new + if (next_next_turf && (next_next_turf.x != previous_turf.x || next_next_turf.y != previous_turf.y)) + path_new.Add(path[j]) + i = j // Move i to the end of this segment + break + else if (j == path.len) // End of path + path = path_new.Copy() + path.Add(last) + src.path = path + return TRUE + else + path_new.Add(current_turf) + + path = path_new.Copy() + path.Add(last) + + // Set the final path + if (length(path) > 0) + src.path = path + return TRUE + else + return FALSE + /obj/structure/machinery/bot/cprbot/proc/can_still_see_patient() - if (human == null) - return FALSE + if (human == null) + return FALSE - var/mob/living/carbon/human/patient = human.resolve() - if (patient == null) - return FALSE + var/mob/living/carbon/human/patient = human.resolve() + if (patient == null) + return FALSE - return patient in view(search_radius, src) + return patient in view(search_radius, src) -/obj/structure/machinery/bot/cprbot/proc/patient_in_range() +/obj/structure/machinery/bot/cprbot/proc/patient_in_range(mob/living/carbon/human/patient = null) if (human == null) return FALSE - var/mob/living/carbon/human/patient = human.resolve() + if (patient == null) + patient = human.resolve() + if (patient == null) return FALSE return get_dist(src, patient) == 0 /obj/structure/machinery/bot/cprbot/proc/move_to_target() - var/mob/living/carbon/human/patient = human.resolve() - // If we cannot see them anymore then stop moving + if (!human) + go_idle() + return + + var/mob/living/carbon/human/target = human.resolve() + if (!target) + go_idle() + return + if (!can_still_see_patient()) go_idle() return - // It might not exist anymore or something - if (patient == null) + if (target == null || is_no_longer_valid(target)) go_idle() return + if (length(src.path)) + var/turf/next = path[1] + if (loc == next) + path -= next + if (length(src.path)) + walk_to(src, path[1], 0, move_to_delay) + else + walk_to(src, 0) + state = STATE_CPRBOT_CPR + switch_to_faster_processing() + try_perform_cpr() + return + + walk_to(src, next, 0, move_to_delay) + return + if (!patient_in_range()) - // We are already moving + if (last_location == loc) + walk_to(src, 0) + call_astar_pathfinding(target) // Use the current target for pathfinding + return + if (state == STATE_CPRBOT_MOVING) + last_location = loc return state = STATE_CPRBOT_MOVING - walk_to(src, patient, 0, movement_delay) + walk_to(src, target, 0, movement_delay) else - walk_to(src, 0) // make sure we stop walking + walk_to(src, 0) state = STATE_CPRBOT_CPR switch_to_faster_processing() try_perform_cpr() +/obj/structure/machinery/bot/cprbot/proc/is_no_longer_valid(mob/living/carbon/human/target) + return (target.stat != DEAD) || (target.stat == DEAD && !target.check_tod()) + /obj/structure/machinery/bot/cprbot/proc/perform_cpr(mob/living/carbon/human/target) if (!cpr_ready) return @@ -250,7 +385,10 @@ /obj/structure/machinery/bot/cprbot/proc/try_perform_cpr() currently_healing = TRUE - // Resolve the weak reference to check if the target still exists + if (!human) + go_idle() + return + var/mob/living/carbon/human/target = human.resolve() if (!patient_in_range()) @@ -259,7 +397,7 @@ return // Check if the target is valid and still needs CPR - if ((target.stat != DEAD) || (target.stat == DEAD && !target.check_tod())) + if (is_no_longer_valid(target)) go_idle() switch_to_slower_processing() return @@ -329,3 +467,17 @@ STOP_PROCESSING(SSfastobj, src) START_PROCESSING(SSobj, src) fast_processing = FALSE + +/obj/structure/machinery/bot/cprbot/Collide(atom/A) //Leave no door unopened! + if ((istype(A, /obj/structure/machinery/door)) && (!isnull(src.botcard))) + var/obj/structure/machinery/door/D = A + if (!istype(D, /obj/structure/machinery/door/firedoor) && D.check_access(src.botcard) && !istype(D,/obj/structure/machinery/door/poddoor)) + D.open() + else if ((istype(A, /mob/living/)) && (!src.anchored)) + src.forceMove(A.loc) + return + +#undef STATE_CPRBOT_IDLE +#undef STATE_CPRBOT_MOVING +#undef STATE_CPRBOT_CPR +#undef STATE_CPRBOT_FOLLOWING_OWNER From 65903b955ad7d777f981c517782734142c47cfbd Mon Sep 17 00:00:00 2001 From: HIDgamer Date: Sun, 22 Sep 2024 02:28:28 +0300 Subject: [PATCH 13/14] fixing taps --- code/game/machinery/bots/cprbot.dm | 118 ++++++++++++++--------------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/code/game/machinery/bots/cprbot.dm b/code/game/machinery/bots/cprbot.dm index b54e1246d95e..854b83617344 100644 --- a/code/game/machinery/bots/cprbot.dm +++ b/code/game/machinery/bots/cprbot.dm @@ -236,69 +236,69 @@ go_idle() /obj/structure/machinery/bot/cprbot/proc/call_astar_pathfinding(mob/living/carbon/human/target = null) - if (target == null) - return FALSE - - var/turf/target_turf = get_turf(target) - if (!target_turf) - return FALSE - - // Calculate the path using your A* function - var/list/path = AStar(src.loc, target_turf, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 30, id=botcard) - - if (!path || !length(path)) - return FALSE - - // Delete the straight line sections since walk_to moves better without that. - var/list/path_new = list() - var/turf/last = path[path.len] - path_new.Add(path[1]) - - // Iterate over the path, trimming unnecessary straight-line turfs - for (var/i = 2; i <= path.len; i++) - var/turf/current_turf = path[i] - var/turf/next_turf = (i + 1 <= path.len) ? path[i + 1] : null - - if (next_turf && (next_turf.x == current_turf.x || next_turf.y == current_turf.y)) - path_new.Add(current_turf) - - // Scan ahead to find the end of this straight line section - for (var/j = i + 1; j <= path.len; j++) - var/turf/next_next_turf = (j + 1 <= path.len) ? path[j + 1] : null - var/turf/previous_turf = path[j - 1] - - // If this is a corner or end of straight line, add it to path_new - if (next_next_turf && (next_next_turf.x != previous_turf.x || next_next_turf.y != previous_turf.y)) - path_new.Add(path[j]) - i = j // Move i to the end of this segment - break - else if (j == path.len) // End of path - path = path_new.Copy() - path.Add(last) - src.path = path - return TRUE - else - path_new.Add(current_turf) - - path = path_new.Copy() - path.Add(last) - - // Set the final path - if (length(path) > 0) - src.path = path - return TRUE - else - return FALSE + if (target == null) + return FALSE + + var/turf/target_turf = get_turf(target) + if (!target_turf) + return FALSE + + // Calculate the path using your A* function + var/list/path = AStar(src.loc, target_turf, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 30, id=botcard) + + if (!path || !length(path)) + return FALSE + + // Delete the straight line sections since walk_to moves better without that. + var/list/path_new = list() + var/turf/last = path[path.len] + path_new.Add(path[1]) + + // Iterate over the path, trimming unnecessary straight-line turfs + for (var/i = 2; i <= path.len; i++) + var/turf/current_turf = path[i] + var/turf/next_turf = (i + 1 <= path.len) ? path[i + 1] : null + + if (next_turf && (next_turf.x == current_turf.x || next_turf.y == current_turf.y)) + path_new.Add(current_turf) + + // Scan ahead to find the end of this straight line section + for (var/j = i + 1; j <= path.len; j++) + var/turf/next_next_turf = (j + 1 <= path.len) ? path[j + 1] : null + var/turf/previous_turf = path[j - 1] + + // If this is a corner or end of straight line, add it to path_new + if (next_next_turf && (next_next_turf.x != previous_turf.x || next_next_turf.y != previous_turf.y)) + path_new.Add(path[j]) + i = j // Move i to the end of this segment + break + else if (j == path.len) // End of path + path = path_new.Copy() + path.Add(last) + src.path = path + return TRUE + else + path_new.Add(current_turf) + + path = path_new.Copy() + path.Add(last) + + // Set the final path + if (length(path) > 0) + src.path = path + return TRUE + else + return FALSE /obj/structure/machinery/bot/cprbot/proc/can_still_see_patient() - if (human == null) - return FALSE + if (human == null) + return FALSE - var/mob/living/carbon/human/patient = human.resolve() - if (patient == null) - return FALSE + var/mob/living/carbon/human/patient = human.resolve() + if (patient == null) + return FALSE - return patient in view(search_radius, src) + return patient in view(search_radius, src) /obj/structure/machinery/bot/cprbot/proc/patient_in_range(mob/living/carbon/human/patient = null) if (human == null) From 5a71c0a65242439ae9b995ea8d6311ba12266296 Mon Sep 17 00:00:00 2001 From: HIDgamer Date: Thu, 26 Sep 2024 12:55:21 +0300 Subject: [PATCH 14/14] Pathfinding tweaks --- code/game/machinery/bots/cprbot.dm | 176 +++++++++++++++++------------ 1 file changed, 101 insertions(+), 75 deletions(-) diff --git a/code/game/machinery/bots/cprbot.dm b/code/game/machinery/bots/cprbot.dm index 854b83617344..faecf15a5d93 100644 --- a/code/game/machinery/bots/cprbot.dm +++ b/code/game/machinery/bots/cprbot.dm @@ -1,3 +1,9 @@ +/obj/item/cprbot_item + name = "Placeholder" + +/obj/item/cprbot_broken + name = "Placeholder" + #define STATE_CPRBOT_IDLE "idle" #define STATE_CPRBOT_MOVING "moving" #define STATE_CPRBOT_CPR "cpr" @@ -112,35 +118,27 @@ go_idle() return + // Check if the owner is in range of the bot's view if (!(owner in view(search_radius, src))) go_idle() return + // If the bot has a path, proceed with walking along it if (length(src.path)) var/turf/next = path[1] - if(loc == next) - path -= next - if (length(src.path)) - walk_to(src, path[1], 0, move_to_delay) - else - walk_to(src, 0) - state = STATE_CPRBOT_CPR - switch_to_faster_processing() - try_perform_cpr() - return + if (loc == next) + path -= next // Remove the turf from the path once reached + walk_to(src, next, 0, move_to_delay) return + // If the owner is not in range, move towards them directly if (!patient_in_range(owner)) - if (last_location == loc) - walk_to(src, 0) - call_astar_pathfinding(owner) // Use the owner as the target for pathfinding - return - + // If the bot is already moving, just return if (state == STATE_CPRBOT_MOVING) - last_location = loc return + // Set the state to moving and walk directly to the owner's location state = STATE_CPRBOT_MOVING walk_to(src, owner, 0, movement_delay) else @@ -209,12 +207,15 @@ var/mob/living/carbon/human/another_bot_patient + // sanity checks if (another_cpr_bot.human != null) another_bot_patient = another_cpr_bot.human.resolve() + // sanity checks if (another_bot_patient == null) continue + // Another CPR bot is targetting this patient, skip if (another_bot_patient in potential_patients) potential_patients.Remove(another_bot_patient) @@ -227,68 +228,94 @@ move_to_target() else + // If no patients are found, check if owner is nearby and follow them if idle if (state == STATE_CPRBOT_IDLE && (owner && (owner in view(search_radius, src)))) state = STATE_CPRBOT_FOLLOWING_OWNER - call_astar_pathfinding(owner) // Use the owner as the target for pathfinding + walk_to(src, owner, 0, movement_delay) else if (state == STATE_CPRBOT_FOLLOWING_OWNER) - call_astar_pathfinding(owner) // Continue following the owner if no patient is in sight + // Continue following the owner if no patient is in sight + walk_to(src, owner, 0, movement_delay) else go_idle() -/obj/structure/machinery/bot/cprbot/proc/call_astar_pathfinding(mob/living/carbon/human/target = null) - if (target == null) +/obj/structure/machinery/bot/cprbot/proc/call_astar_pathfinding() + if (human == null) return FALSE - var/turf/target_turf = get_turf(target) - if (!target_turf) + var/mob/living/carbon/human/patient = human.resolve() + if (patient == null) return FALSE + var/turf/target_turf = get_turf(patient) + + if (!target_turf) + return + // Calculate the path using your A* function - var/list/path = AStar(src.loc, target_turf, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 30, id=botcard) + path = AStar(src.loc, target_turf, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 30, id=botcard) - if (!path || !length(path)) - return FALSE + if (!length(path)) + return // Delete the straight line sections since walk_to moves better without that. var/list/path_new = list() var/turf/last = path[path.len] - path_new.Add(path[1]) - // Iterate over the path, trimming unnecessary straight-line turfs - for (var/i = 2; i <= path.len; i++) - var/turf/current_turf = path[i] - var/turf/next_turf = (i + 1 <= path.len) ? path[i + 1] : null - - if (next_turf && (next_turf.x == current_turf.x || next_turf.y == current_turf.y)) - path_new.Add(current_turf) - - // Scan ahead to find the end of this straight line section - for (var/j = i + 1; j <= path.len; j++) - var/turf/next_next_turf = (j + 1 <= path.len) ? path[j + 1] : null - var/turf/previous_turf = path[j - 1] - - // If this is a corner or end of straight line, add it to path_new - if (next_next_turf && (next_next_turf.x != previous_turf.x || next_next_turf.y != previous_turf.y)) - path_new.Add(path[j]) - i = j // Move i to the end of this segment - break - else if (j == path.len) // End of path - path = path_new.Copy() - path.Add(last) - src.path = path - return TRUE - else - path_new.Add(current_turf) + if (!istype(last, /turf)) // Ensure last is a turf before accessing it + return + + path_new.Add(path[1]) + for (var/i = 2; i < path.len; i++) { + if (istype(path[i], /turf) && istype(path[i + 1], /turf)) { + var/turf/current_turf = path[i] + var/turf/next_turf = path[i + 1] + + if ((next_turf.x == current_turf.x) || (next_turf.y == current_turf.y)) { // We have a straight line, scan for more to cut down + path_new.Add(current_turf) + for (var/j = i + 1; j < path.len; j++) { + if (istype(path[j + 1], /turf)) { + var/turf/next_next_turf = path[j + 1] + var/turf/prev_turf = path[j - 1] + + // This is a corner and the endpoint of our line + if ((next_next_turf.x != prev_turf.x) && (next_next_turf.y != prev_turf.y)) { + path_new.Add(path[j]) + i = j + 1 + break + } + } + + if (j == path.len - 1) { + path = list() + path = path_new.Copy() + path.Add(last) + return + } + } + } else { + path_new.Add(current_turf) + } + } + } + + path = list() path = path_new.Copy() path.Add(last) - // Set the final path - if (length(path) > 0) - src.path = path - return TRUE - else - return FALSE + if (!path || length(path) == 0) { + src.human = null + src.currently_healing = FALSE + return + } + + // Set the path + src.path = path + + // Start moving + state = STATE_CPRBOT_MOVING + move_to_target() + return /obj/structure/machinery/bot/cprbot/proc/can_still_see_patient() if (human == null) @@ -313,27 +340,26 @@ return get_dist(src, patient) == 0 /obj/structure/machinery/bot/cprbot/proc/move_to_target() - if (!human) - go_idle() - return - - var/mob/living/carbon/human/target = human.resolve() - if (!target) + var/mob/living/carbon/human/patient = human ? human.resolve() : null + // If we cannot see them anymore then stop moving + if (!can_still_see_patient()) go_idle() return - if (!can_still_see_patient()) + // It might not exist anymore or something + if (patient == null) go_idle() return - if (target == null || is_no_longer_valid(target)) + if (is_no_longer_valid(patient)) go_idle() return if (length(src.path)) var/turf/next = path[1] - if (loc == next) + if(loc == next) path -= next + if (length(src.path)) walk_to(src, path[1], 0, move_to_delay) else @@ -349,17 +375,18 @@ if (!patient_in_range()) if (last_location == loc) walk_to(src, 0) - call_astar_pathfinding(target) // Use the current target for pathfinding + call_astar_pathfinding() return + // We are already moving if (state == STATE_CPRBOT_MOVING) last_location = loc return state = STATE_CPRBOT_MOVING - walk_to(src, target, 0, movement_delay) + walk_to(src, patient, 0, movement_delay) else - walk_to(src, 0) + walk_to(src, 0) // make sure we stop walking state = STATE_CPRBOT_CPR switch_to_faster_processing() try_perform_cpr() @@ -385,10 +412,7 @@ /obj/structure/machinery/bot/cprbot/proc/try_perform_cpr() currently_healing = TRUE - if (!human) - go_idle() - return - + // Resolve the weak reference to check if the target still exists var/mob/living/carbon/human/target = human.resolve() if (!patient_in_range()) @@ -405,7 +429,7 @@ perform_cpr(target) /obj/structure/machinery/bot/cprbot/proc/self_destruct(mob/living/carbon/human/user = null) - var/obj/item/cprbot_item/cprbot_item = new /obj/item/cprbot_item(src.loc) + var/obj/item/cprbot_item = new /obj/item/cprbot_item(src.loc) playsound(loc, 'sound/CPRbot/CPRbot_poweroff.ogg', 25, 1) @@ -468,13 +492,15 @@ START_PROCESSING(SSobj, src) fast_processing = FALSE -/obj/structure/machinery/bot/cprbot/Collide(atom/A) //Leave no door unopened! +/obj/structure/machinery/bot/medbot/Collide(atom/A) //Leave no door unopened! if ((istype(A, /obj/structure/machinery/door)) && (!isnull(src.botcard))) var/obj/structure/machinery/door/D = A if (!istype(D, /obj/structure/machinery/door/firedoor) && D.check_access(src.botcard) && !istype(D,/obj/structure/machinery/door/poddoor)) D.open() + src.frustration = 0 else if ((istype(A, /mob/living/)) && (!src.anchored)) src.forceMove(A.loc) + src.frustration = 0 return #undef STATE_CPRBOT_IDLE