diff --git a/code/__DEFINES/ai/ai_blackboard.dm b/code/__DEFINES/ai/ai_blackboard.dm
index cbef7194bc9..3e9a65027af 100644
--- a/code/__DEFINES/ai/ai_blackboard.dm
+++ b/code/__DEFINES/ai/ai_blackboard.dm
@@ -176,8 +176,6 @@
#define BB_MOD_TARGET "BB_mod_target"
///The module the AI was created from
#define BB_MOD_MODULE "BB_mod_module"
-///Range for a MOD AI controller.
-#define MOD_AI_RANGE 200
///Only target mobs with these traits
#define BB_TARGET_ONLY_WITH_TRAITS "BB_target_only_with_traits"
diff --git a/code/__DEFINES/dcs/signals/signals_object.dm b/code/__DEFINES/dcs/signals/signals_object.dm
index b66d92b8df7..487872df053 100644
--- a/code/__DEFINES/dcs/signals/signals_object.dm
+++ b/code/__DEFINES/dcs/signals/signals_object.dm
@@ -226,6 +226,8 @@
#define COMSIG_MULTITOOL_REMOVE_BUFFER "multitool_remove_buffer"
///from [/obj/effect/mine/proc/triggermine]:
#define COMSIG_MINE_TRIGGERED "minegoboom"
+///from [/obj/structure/closet/supplypod/proc/handleReturnAfterDeparting]:
+#define COMSIG_SUPPLYPOD_RETURNING "supplypodgohome"
///from [/obj/structure/closet/supplypod/proc/preOpen]:
#define COMSIG_SUPPLYPOD_LANDED "supplypodgoboom"
diff --git a/code/datums/ai/movement/ai_movement_jps.dm b/code/datums/ai/movement/ai_movement_jps.dm
index 3781dd7f5dc..1153833a31e 100644
--- a/code/datums/ai/movement/ai_movement_jps.dm
+++ b/code/datums/ai/movement/ai_movement_jps.dm
@@ -53,6 +53,3 @@
/datum/ai_movement/jps/bot/travel_to_beacon
maximum_length = AI_BOT_PATH_LENGTH
max_pathing_attempts = 10
-
-/datum/ai_movement/jps/modsuit
- maximum_length = MOD_AI_RANGE
diff --git a/code/datums/ai/objects/mod.dm b/code/datums/ai/objects/mod.dm
deleted file mode 100644
index 46b41adb1c8..00000000000
--- a/code/datums/ai/objects/mod.dm
+++ /dev/null
@@ -1,48 +0,0 @@
-/// An AI controller for the MODsuit pathfinder module. It's activated by module and attaches itself to the user.
-/datum/ai_controller/mod
- blackboard = list(
- BB_MOD_TARGET,
- BB_MOD_MODULE,
- )
- can_idle = FALSE
- max_target_distance = MOD_AI_RANGE //a little spicy but its one specific item that summons it, and it doesn't run otherwise
- ai_movement = /datum/ai_movement/jps/modsuit
- ///ID card generated from the suit's required access. Used for pathing.
- var/obj/item/card/id/advanced/id_card
-
-/datum/ai_controller/mod/TryPossessPawn(atom/new_pawn)
- if(!istype(new_pawn, /obj/item/mod/control))
- return AI_CONTROLLER_INCOMPATIBLE
- var/obj/item/mod/control/mod = new_pawn
- id_card = new /obj/item/card/id/advanced/simple_bot()
- if(length(mod.req_access))
- id_card.set_access(mod.req_access)
- return ..() //Run parent at end
-
-/datum/ai_controller/mod/UnpossessPawn(destroy)
- QDEL_NULL(id_card)
- return ..() //Run parent at end
-
-/datum/ai_controller/mod/SelectBehaviors(seconds_per_tick)
- current_behaviors = list()
- if(blackboard[BB_MOD_TARGET] && blackboard[BB_MOD_MODULE])
- queue_behavior(/datum/ai_behavior/mod_attach)
-
-/datum/ai_controller/mod/get_access()
- return id_card.GetAccess()
-
-/datum/ai_behavior/mod_attach
- behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT|AI_BEHAVIOR_MOVE_AND_PERFORM
-
-/datum/ai_behavior/mod_attach/perform(seconds_per_tick, datum/ai_controller/controller)
- if(!controller.pawn.Adjacent(controller.blackboard[BB_MOD_TARGET]))
- return AI_BEHAVIOR_DELAY
- var/obj/item/mod/module/pathfinder/module = controller.blackboard[BB_MOD_MODULE]
- module.attach(controller.blackboard[BB_MOD_TARGET])
- return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
-
-/datum/ai_behavior/mod_attach/finish_action(datum/ai_controller/controller, succeeded)
- . = ..()
- controller.clear_blackboard_key(BB_MOD_TARGET)
- var/obj/item/mod/module/pathfinder/module = controller.blackboard[BB_MOD_MODULE]
- module.end_recall(succeeded)
diff --git a/code/game/machinery/botlaunchpad.dm b/code/game/machinery/botlaunchpad.dm
index e044bb1e296..9cd9c647acc 100644
--- a/code/game/machinery/botlaunchpad.dm
+++ b/code/game/machinery/botlaunchpad.dm
@@ -48,7 +48,7 @@
launched_bot = WEAKREF(possible_bot)
podspawn(list(
"target" = get_turf(src),
- "path" = /obj/structure/closet/supplypod/botpod,
+ "path" = /obj/structure/closet/supplypod/transport/botpod,
"style" = /datum/pod_style/seethrough,
"reverse_dropoff_coords" = list(reverse_turf.x, reverse_turf.y, reverse_turf.z)
))
@@ -66,15 +66,6 @@
var/mob/living/simple_animal/bot/simple_bot = our_bot
simple_bot.call_bot(src, get_turf(src))
-/obj/structure/closet/supplypod/botpod
- style = /datum/pod_style/seethrough
- explosionSize = list(0,0,0,0)
- reversing = TRUE
+/obj/structure/closet/supplypod/transport/botpod
reverse_option_list = list("Mobs"=TRUE,"Objects"=FALSE,"Anchored"=FALSE,"Underfloor"=FALSE,"Wallmounted"=FALSE,"Floors"=FALSE,"Walls"=FALSE,"Mecha"=FALSE)
- delays = list(POD_TRANSIT = 0, POD_FALLING = 0, POD_OPENING = 0, POD_LEAVING = 0)
- reverse_delays = list(POD_TRANSIT = 15, POD_FALLING = 10, POD_OPENING = 0, POD_LEAVING = 0)
- custom_rev_delay = TRUE
- effectQuiet = TRUE
leavingSound = 'sound/vehicles/rocketlaunch.ogg'
- close_sound = null
- pod_flags = FIRST_SOUNDS
diff --git a/code/modules/cargo/supplypod.dm b/code/modules/cargo/supplypod.dm
index 2488942a9f2..73875c011f7 100644
--- a/code/modules/cargo/supplypod.dm
+++ b/code/modules/cargo/supplypod.dm
@@ -71,6 +71,19 @@
bluespace = TRUE
explosionSize = list(0,0,0,0)
+/// Quick setup for if you want a pod that transports a specific object somewhere and makes it look like it is flying away
+/obj/structure/closet/supplypod/transport
+ style = /datum/pod_style/seethrough
+ specialised = TRUE
+ explosionSize = list(0,0,0,0)
+ reversing = TRUE
+ delays = list(POD_TRANSIT = 0, POD_FALLING = 0, POD_OPENING = 0, POD_LEAVING = 0)
+ reverse_delays = list(POD_TRANSIT = 15, POD_FALLING = 10, POD_OPENING = 0, POD_LEAVING = 0)
+ custom_rev_delay = TRUE
+ effectQuiet = TRUE
+ close_sound = null
+ pod_flags = FIRST_SOUNDS
+
/obj/structure/closet/supplypod/podspawn/deathmatch
desc = "A blood-red styled drop pod."
specialised = TRUE
@@ -300,6 +313,7 @@
if (custom_rev_delay)
delays = reverse_delays
backToNonReverseIcon()
+ SEND_SIGNAL(src, COMSIG_SUPPLYPOD_RETURNING)
var/turf/return_turf = locate(reverse_dropoff_coords[1], reverse_dropoff_coords[2], reverse_dropoff_coords[3])
new /obj/effect/pod_landingzone(return_turf, src)
@@ -308,9 +322,9 @@
var/list/boom = explosionSize
resistance_flags = initial(resistance_flags)
set_density(TRUE) //Density is originally false so the pod doesn't block anything while it's still falling through the air
- AddComponent(/datum/component/pellet_cloud, projectile_type=shrapnel_type, magnitude=shrapnel_magnitude)
if(effectShrapnel)
- SEND_SIGNAL(src, COMSIG_SUPPLYPOD_LANDED)
+ AddComponent(/datum/component/pellet_cloud, projectile_type=shrapnel_type, magnitude=shrapnel_magnitude)
+ SEND_SIGNAL(src, COMSIG_SUPPLYPOD_LANDED)
for (var/mob/living/target_living in turf_underneath)
if (iscarbon(target_living)) //If effectLimb is true (which means we pop limbs off when we hit people):
if (effectLimb)
@@ -364,6 +378,7 @@
moveToNullspace()
addtimer(CALLBACK(src, PROC_REF(open_pod), benis), delays[POD_OPENING]) //After the opening delay passes, we use the open proc from this supplyprod while referencing the contents of the "holder", in this case the gondolapod mob
else if (ispath(style, /datum/pod_style/seethrough))
+ transform = matrix()
open_pod(src)
else
addtimer(CALLBACK(src, PROC_REF(open_pod), src), delays[POD_OPENING]) //After the opening delay passes, we use the open proc from this supplypod, while referencing this supplypod's contents
@@ -415,7 +430,8 @@
if (!holder)
return
take_contents(holder)
- playsound(holder, close_sound, soundVolume*0.75, TRUE, -3)
+ if (close_sound)
+ playsound(holder, close_sound, soundVolume*0.75, TRUE, -3)
holder.setClosed()
addtimer(CALLBACK(src, PROC_REF(preReturn), holder), delays[POD_LEAVING] * 0.2) //Start to leave a bit after closing for cinematic effect
@@ -658,7 +674,11 @@
stack_trace("Pod landingzone effect created with no pod")
return INITIALIZE_HINT_QDEL
transform = matrix() * 1.5
- animate(src, transform = matrix()*0.01, time = pod.delays[POD_TRANSIT]+pod.delays[POD_FALLING])
+ var/arrival_time = pod.delays[POD_TRANSIT] + pod.delays[POD_FALLING]
+ if (arrival_time > 0)
+ animate(src, transform = matrix()*0.01, time = arrival_time)
+ else
+ alpha = 0
/obj/effect/pod_landingzone //This is the object that forceMoves the supplypod to its location
name = "Landing Zone Indicator"
@@ -685,7 +705,11 @@
if (!pod.effectStealth)
helper = new (drop_location(), pod)
alpha = 255
- animate(src, transform = matrix().Turn(90), time = pod.delays[POD_TRANSIT]+pod.delays[POD_FALLING])
+ var/arrival_time = pod.delays[POD_TRANSIT] + pod.delays[POD_FALLING]
+ if (arrival_time > 0)
+ animate(src, transform = matrix().Turn(90), time = arrival_time)
+ else
+ alpha = 0
if (single_order)
if (istype(single_order, /datum/supply_order))
var/datum/supply_order/SO = single_order
@@ -701,7 +725,7 @@
if(pod.effectStun) //If effectStun is true, stun any mobs caught on this pod_landingzone until the pod gets a chance to hit them
for (var/mob/living/target_living in get_turf(src))
target_living.Stun(pod.delays[POD_TRANSIT]+10, ignore_canstun = TRUE)//you ain't goin nowhere, kid.
- if (pod.delays[POD_TRANSIT] + pod.delays[POD_FALLING] < pod.fallingSoundLength)
+ if (arrival_time < pod.fallingSoundLength)
pod.fallingSoundLength = 3 //The default falling sound is a little long, so if the landing time is shorter than the default falling sound, use a special, shorter default falling sound
pod.fallingSound = 'sound/items/weapons/mortar_whistle.ogg'
var/soundStartTime = pod.delays[POD_TRANSIT] - pod.fallingSoundLength + pod.delays[POD_FALLING]
diff --git a/code/modules/mod/modules/module_pathfinder.dm b/code/modules/mod/modules/module_pathfinder.dm
index 4b948d6ed07..e9497e48e82 100644
--- a/code/modules/mod/modules/module_pathfinder.dm
+++ b/code/modules/mod/modules/module_pathfinder.dm
@@ -1,3 +1,5 @@
+#define PATHFINDER_PRE_ANIMATE_TIME (2 SECONDS)
+
///Pathfinder - Can fly the suit from a long distance to an implant installed in someone.
/obj/item/mod/module/pathfinder
name = "MOD pathfinder module"
@@ -24,6 +26,8 @@
var/image/jet_icon
/// Allow suit activation - Lets this module be recalled from the MOD.
var/allow_suit_activation = FALSE // I'm not here to argue about balance
+ /// Are we currently travelling?
+ var/in_transit = FALSE
/obj/item/mod/module/pathfinder/Initialize(mapload)
@@ -88,7 +92,7 @@
return
balloon_alert(activator, "implanted")
if(!(activator == mod.wearer)) // someone else implanted you
- balloon_alert(mod.wearer, "pathfinder MOD tracker implanted!")
+ balloon_alert(mod.wearer, "tracker implanted!")
playsound(src, 'sound/effects/spray.ogg', 30, TRUE, -6)
/obj/item/mod/module/pathfinder/proc/attach(mob/living/user)
@@ -101,64 +105,81 @@
return
mod.quick_deploy(user)
human_user.update_action_buttons(TRUE)
- balloon_alert(human_user, "[mod] attached")
playsound(mod, 'sound/machines/ping.ogg', 50, TRUE)
drain_power(use_energy_cost)
/obj/item/mod/module/pathfinder/proc/recall(mob/recaller)
if(!implant)
- balloon_alert(recaller, "no target implant!")
+ balloon_alert(recaller, "no implant!")
return FALSE
if(recaller != implant.imp_in && !allow_suit_activation) // No pAI recalling
- balloon_alert(recaller, "sector safety regulations prevent MOD-side recalling!")
+ balloon_alert(recaller, "invalid user!")
return FALSE
if(mod.open)
balloon_alert(recaller, "cover open!")
return FALSE
- if(mod.ai_controller)
- balloon_alert(recaller, "already moving!")
+ if(in_transit)
+ balloon_alert(recaller, "suit in transit!")
return FALSE
if(ismob(get_atom_on_turf(mod)))
- balloon_alert(recaller, "already on someone!")
+ balloon_alert(recaller, "already worn!")
return FALSE
- if(mod.z != implant.imp_in.z || get_dist(implant.imp_in, mod) > MOD_AI_RANGE)
- balloon_alert(recaller, "too far!")
- return FALSE
- var/datum/ai_controller/mod_ai = new /datum/ai_controller/mod(mod)
- mod.ai_controller = mod_ai
- mod_ai.set_movement_target(type, implant.imp_in)
- mod_ai.set_blackboard_key(BB_MOD_TARGET, implant.imp_in)
- mod_ai.set_blackboard_key(BB_MOD_MODULE, src)
- mod.interaction_flags_item &= ~INTERACT_ITEM_ATTACK_HAND_PICKUP
- mod.AddElement(/datum/element/movetype_handler)
- ADD_TRAIT(mod, TRAIT_MOVE_FLYING, MOD_TRAIT)
- animate(mod, 0.2 SECONDS, pixel_x = base_pixel_y, pixel_y = base_pixel_y)
- mod.add_overlay(jet_icon)
- RegisterSignal(mod, COMSIG_MOVABLE_MOVED, PROC_REF(on_move))
+
+ in_transit = TRUE
+ animate(mod, 0.5 SECONDS, pixel_x = base_pixel_y, pixel_y = base_pixel_y)
+ mod.Shake(pixelshiftx = 1, pixelshifty = 1, duration = PATHFINDER_PRE_ANIMATE_TIME)
+ addtimer(CALLBACK(src, PROC_REF(do_recall), recaller), PATHFINDER_PRE_ANIMATE_TIME, TIMER_DELETE_ME)
+
balloon_alert(recaller, "suit recalled")
if(!(recaller == mod.wearer))
balloon_alert(mod.wearer, "suit recalled")
return TRUE
-/obj/item/mod/module/pathfinder/proc/on_move(atom/movable/source, atom/old_loc, dir, forced)
- SIGNAL_HANDLER
-
- var/matrix/mod_matrix = matrix()
- mod_matrix.Turn(get_angle(source, implant.imp_in))
- source.transform = mod_matrix
-
-/obj/item/mod/module/pathfinder/proc/end_recall(successful = TRUE)
- if(!mod)
+/// Pod-transport the suit to its owner
+/obj/item/mod/module/pathfinder/proc/do_recall(mob/recaller)
+ var/container = get_atom_on_turf(mod)
+ if(ismob(container))
+ balloon_alert(recaller, "launch interrupted!")
return
- QDEL_NULL(mod.ai_controller)
- mod.interaction_flags_item |= INTERACT_ITEM_ATTACK_HAND_PICKUP
- REMOVE_TRAIT(mod, TRAIT_MOVE_FLYING, MOD_TRAIT)
- mod.RemoveElement(/datum/element/movetype_handler)
+
+ if(iscloset(container))
+ var/obj/structure/closet/closet = container
+ if (!closet.opened)
+ if (!closet.open())
+ playsound(closet, 'sound/effects/bang.ogg', vol = 50, vary = TRUE)
+ closet.bust_open()
+
+
+ mod.add_overlay(jet_icon)
+ playsound(mod, 'sound/vehicles/rocketlaunch.ogg', vol = 80, vary = FALSE)
+ var/turf/land_target = get_turf(implant.imp_in)
+ var/obj/structure/closet/supplypod/pod = podspawn(list(
+ "target" = get_turf(mod),
+ "path" = /obj/structure/closet/supplypod/transport/module_pathfinder,
+ "reverse_dropoff_coords" = list(land_target.x, land_target.y, land_target.z),
+ ))
+
+ pod.insert(mod, pod)
+ RegisterSignal(pod, COMSIG_SUPPLYPOD_RETURNING, PROC_REF(pod_takeoff))
+
+ if (istype(container, /obj/machinery/suit_storage_unit))
+ var/obj/machinery/suit_storage_unit/storage = container
+ storage.locked = FALSE
+ storage.open_machine()
+
+/// Track when pod has taken off so we don't falsely report the initial landing
+/obj/item/mod/module/pathfinder/proc/pod_takeoff(datum/pod)
+ SIGNAL_HANDLER
+ RegisterSignal(pod, COMSIG_SUPPLYPOD_LANDED, PROC_REF(pod_landed))
+
+/// When the pod landed, we can recall again
+/obj/item/mod/module/pathfinder/proc/pod_landed()
+ SIGNAL_HANDLER
+ in_transit = FALSE
mod.cut_overlay(jet_icon)
- mod.transform = matrix()
- UnregisterSignal(mod, COMSIG_MOVABLE_MOVED)
- if(!successful)
- balloon_alert(implant.imp_in, "suit lost connection!")
+ playsound(mod, 'sound/items/handling/toolbox/toolbox_drop.ogg', vol = 80, vary = FALSE)
+ if (implant?.imp_in?.Adjacent(src))
+ INVOKE_ASYNC(src, PROC_REF(attach), implant.imp_in)
// ###########
// THE INPLANT
@@ -173,8 +194,6 @@
/// The pathfinder module we are linked to.
var/obj/item/mod/module/pathfinder/module
-
-
/obj/item/implant/mod/Initialize(mapload)
. = ..()
if(!istype(loc, /obj/item/mod/module/pathfinder))
@@ -182,8 +201,6 @@
module = loc
/obj/item/implant/mod/Destroy()
- if(module?.mod?.ai_controller)
- module.end_recall(successful = FALSE)
module = null
return ..()
@@ -192,7 +209,6 @@
Name: Nakamura Engineering Pathfinder Implant
\
Implant Details: Allows for the recall of a Modular Outerwear Device by the implant owner at any time.
"
-
/datum/action/item_action/mod_recall
name = "Recall MOD"
desc = "Recall a MODsuit anyplace, anytime."
@@ -205,7 +221,7 @@
COOLDOWN_DECLARE(recall_cooldown)
/datum/action/item_action/mod_recall/New(Target)
- ..()
+ . = ..()
if(!istype(Target, /obj/item/implant/mod))
qdel(src)
return
@@ -215,5 +231,14 @@
if(!COOLDOWN_FINISHED(src, recall_cooldown))
implant.balloon_alert(owner, "on cooldown!")
return
- if(implant.module.recall(owner)) // change this
- COOLDOWN_START(src, recall_cooldown, 15 SECONDS)
+ if(implant.module.recall(owner))
+ implant.balloon_alert(owner, "suit incoming...")
+ COOLDOWN_START(src, recall_cooldown, 5 SECONDS)
+
+/// Special pod subtype we use just to make insertion check easy
+/obj/structure/closet/supplypod/transport/module_pathfinder
+
+/obj/structure/closet/supplypod/transport/module_pathfinder/insertion_allowed(atom/to_insert)
+ return istype(to_insert, /obj/item/mod/control)
+
+#undef PATHFINDER_PRE_ANIMATE_TIME
diff --git a/tgstation.dme b/tgstation.dme
index 3d73e7cf7f4..acbea823808 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -1041,7 +1041,6 @@
#include "code\datums\ai\movement\ai_movement_complete_stop.dm"
#include "code\datums\ai\movement\ai_movement_dumb.dm"
#include "code\datums\ai\movement\ai_movement_jps.dm"
-#include "code\datums\ai\objects\mod.dm"
#include "code\datums\ai\objects\vending_machines\vending_machine_behaviors.dm"
#include "code\datums\ai\objects\vending_machines\vending_machine_controller.dm"
#include "code\datums\ai\robot_customer\robot_customer_behaviors.dm"