diff --git a/code/_onclick/ai.dm b/code/_onclick/ai.dm
index e05cbeb9ba2..61b67770096 100644
--- a/code/_onclick/ai.dm
+++ b/code/_onclick/ai.dm
@@ -99,6 +99,14 @@
/mob/living/silicon/ai/MiddleClickOn(var/atom/A)
A.AIMiddleClick(src)
+/*
+ Sticking a minor pAI override into this because I can.
+*/
+/mob/living/silicon/pai/MiddleClickOn(var/atom/A)
+ if(src.loc == src.card)
+ return A.AIMiddleClick(src)
+ return ..()
+
/*
The following criminally helpful code is just the previous code cleaned up;
I have no idea why it was in atoms.dm instead of respective files.
@@ -152,10 +160,18 @@
/obj/machinery/turretid/AIAltClick() //toggles lethal on turrets
Topic(src, list("src"= "\ref[src]", "command"="lethal", "value"="[!lethal]"), 1) // 1 meaning no window (consistency!)
-/atom/proc/AIMiddleClick()
- return
+/atom/proc/AIMiddleClick(var/mob/living/silicon/user)
+ var/obj/item/weapon/rig/rig = user.get_rig()
+ if(rig && rig.wearer && rig.ai_can_move_suit(user, check_user_module = 1))
+ if(rig.wearer.HardsuitClickOn(src, alert_ai = 1))
+ return 1
+ return 0
/obj/machinery/door/airlock/AIMiddleClick() // Toggles door bolt lights.
+
+ if(..())
+ return
+
if(!src.lights)
Topic(src, list("src"= "\ref[src]", "command"="lights", "activate" = "1"), 1) // 1 meaning no window (consistency!)
else
diff --git a/code/_onclick/rig.dm b/code/_onclick/rig.dm
index ad9346ec15f..d19101f69b5 100644
--- a/code/_onclick/rig.dm
+++ b/code/_onclick/rig.dm
@@ -47,13 +47,13 @@
return
..()
-/mob/living/carbon/human/proc/HardsuitClickOn(atom/A)
+/mob/living/carbon/human/proc/HardsuitClickOn(var/atom/A, var/alert_ai = 0)
if(back)
var/obj/item/weapon/rig/rig = back
if(istype(rig) && rig.selected_module)
if(world.time <= next_move) return 1
next_move = world.time + 8
- rig.selected_module.engage(A)
+ rig.selected_module.engage(A, alert_ai)
return 1
return 0
diff --git a/code/game/objects/items/devices/aicard.dm b/code/game/objects/items/devices/aicard.dm
index 6b239c6b072..3e8c9c69c6e 100644
--- a/code/game/objects/items/devices/aicard.dm
+++ b/code/game/objects/items/devices/aicard.dm
@@ -120,10 +120,13 @@
if(user.client)
user << "Transfer successful: [ai.name] ([rand(1000,9999)].exe) removed from host terminal and stored within local memory."
+ ai.canmove = 1
update_icon()
return 1
/obj/item/device/aicard/proc/clear()
+ if(carded_ai && istype(carded_ai.loc, /turf))
+ carded_ai.canmove = 0
name = initial(name)
carded_ai = null
update_icon()
@@ -140,10 +143,9 @@
carded_ai.show_message(rendered, type)
..()
-/*
/obj/item/device/aicard/relaymove(var/mob/user, var/direction)
- if(src.loc && istype(src.loc.loc, /obj/item/rig_module))
- var/obj/item/rig_module/module = src.loc.loc
- if(!module.holder || !direction)
- return
- module.holder.forced_move(direction)*/
+ if(user.stat || user.stunned)
+ return
+ var/obj/item/weapon/rig/rig = src.get_rig()
+ if(istype(rig))
+ rig.forced_move(direction, user)
diff --git a/code/game/objects/items/devices/paicard.dm b/code/game/objects/items/devices/paicard.dm
index f7fa46678b0..685f2a90431 100644
--- a/code/game/objects/items/devices/paicard.dm
+++ b/code/game/objects/items/devices/paicard.dm
@@ -10,12 +10,12 @@
var/looking_for_personality = 0
var/mob/living/silicon/pai/pai
-/*/obj/item/device/paicard/relaymove(var/mob/user, var/direction)
- if(src.loc && istype(src.loc.loc, /obj/item/rig_module))
- var/obj/item/rig_module/module = src.loc.loc
- if(!module.holder || !direction)
- return
- module.holder.forced_move(direction)*/
+/obj/item/device/paicard/relaymove(var/mob/user, var/direction)
+ if(user.stat || user.stunned)
+ return
+ var/obj/item/weapon/rig/rig = src.get_rig()
+ if(istype(rig))
+ rig.forced_move(direction, user)
/obj/item/device/paicard/New()
..()
diff --git a/code/modules/clothing/spacesuits/rig/modules/ninja.dm b/code/modules/clothing/spacesuits/rig/modules/ninja.dm
index 6b1362a7666..0dfa1bd93e6 100644
--- a/code/modules/clothing/spacesuits/rig/modules/ninja.dm
+++ b/code/modules/clothing/spacesuits/rig/modules/ninja.dm
@@ -95,14 +95,14 @@
playsound(T, "sparks", 50, 1)
anim(T,M,'icons/mob/mob.dmi',,"phaseout",,M.dir)
-/obj/item/rig_module/teleporter/engage(atom/target)
+/obj/item/rig_module/teleporter/engage(var/atom/target, var/notify_ai)
if(!..()) return 0
var/mob/living/carbon/human/H = holder.wearer
if(!istype(H.loc, /turf))
- H << "You cannot teleport out of your current location."
+ usr << "You cannot teleport out of your current location."
return 0
var/turf/T
@@ -112,15 +112,15 @@
T = get_teleport_loc(get_turf(H), H, rand(5, 9))
if(!T || T.density)
- H << "You cannot teleport into solid walls."
+ usr << "You cannot teleport into solid walls."
return 0
if(T.z in config.admin_levels)
- H << "You cannot use your teleporter on this Z-level."
+ usr << "You cannot use your teleporter on this Z-level."
return 0
if(T.contains_dense_objects())
- H << "You cannot teleport to a location with solid objects."
+ usr << "You cannot teleport to a location with solid objects."
phase_out(H,get_turf(H))
H.loc = T
diff --git a/code/modules/clothing/spacesuits/rig/rig.dm b/code/modules/clothing/spacesuits/rig/rig.dm
index 60197daef79..e949cee66dc 100644
--- a/code/modules/clothing/spacesuits/rig/rig.dm
+++ b/code/modules/clothing/spacesuits/rig/rig.dm
@@ -27,6 +27,8 @@
var/interface_path = "hardsuit.tmpl"
var/ai_interface_path = "hardsuit.tmpl"
var/interface_title = "Hardsuit Controller"
+ var/wearer_move_delay //Used for AI moving.
+ var/ai_controlled_move_delay = 10
// Keeps track of what this rig should spawn with.
var/suit_type = "hardsuit"
@@ -326,6 +328,8 @@
else
if(offline)
offline = 0
+ if(istype(wearer) && !wearer.wearing_rig)
+ wearer.wearing_rig = src
chest.slowdown = initial(slowdown)
if(offline)
@@ -544,11 +548,13 @@
/obj/item/weapon/rig/proc/notify_ai(var/message)
if(!message || !installed_modules || !installed_modules.len)
- return
+ return 0
+ . = 0
for(var/obj/item/rig_module/module in installed_modules)
for(var/mob/living/silicon/ai/ai in module.contents)
if(ai && ai.client && !ai.stat)
ai << "[message]"
+ . = 1
/obj/item/weapon/rig/equipped(mob/living/carbon/human/M)
..()
@@ -565,6 +571,7 @@
if(istype(M) && M.back == src)
M.visible_message("[M] struggles into \the [src].", "You struggle into \the [src].")
wearer = M
+ wearer.wearing_rig = src
update_icon()
/obj/item/weapon/rig/proc/toggle_piece(var/piece, var/mob/living/carbon/human/H, var/deploy_mode)
@@ -676,6 +683,7 @@
..()
for(var/piece in list("helmet","gauntlets","chest","boots"))
toggle_piece(piece, user, ONLY_RETRACT)
+ wearer.wearing_rig = null
wearer = null
//Todo
@@ -757,14 +765,133 @@
return 1
return 0
-/*/obj/item/weapon/rig/proc/forced_move(dir)
- if(locked_down)
+/obj/item/weapon/rig/proc/ai_can_move_suit(var/mob/user, var/check_user_module = 0, var/check_for_ai = 0)
+
+ if(check_for_ai)
+ if(!(locate(/obj/item/rig_module/ai_container) in contents))
+ return 0
+ var/found_ai
+ for(var/obj/item/rig_module/ai_container/module in contents)
+ if(module.damage >= 2)
+ continue
+ if(module.integrated_ai && module.integrated_ai.client && !module.integrated_ai.stat)
+ found_ai = 1
+ break
+ if(!found_ai)
+ return 0
+
+ if(check_user_module)
+ world << "Checking user module"
+ if(!user)
+ world << "No user"
+ return 0
+ if(!user.loc || !user.loc.loc)
+ world << "No user loc"
+ return 0
+ var/obj/item/rig_module/ai_container/module = user.loc.loc
+ if(!istype(module) || module.damage >= 2)
+ user << "Your host module is unable to interface with the suit."
+ return 0
+
+ if(offline || !cell || !cell.charge || locked_down)
+ if(user) user << "Your host rig is unpowered and unresponsive."
return 0
- if(!control_overridden)
- return
if(!wearer || wearer.back != src)
+ if(user) user << "Your host rig is not being worn."
return 0
- wearer.Move(null,dir)*/
+ if(!wearer.stat && !control_overridden && !ai_override_enabled)
+ if(user) user << "You are locked out of the suit servo controller."
+ return 0
+ return 1
+
+/obj/item/weapon/rig/proc/force_rest(var/mob/user)
+ if(!ai_can_move_suit(user, check_user_module = 1))
+ return
+ wearer.lay_down()
+ user << "\The [wearer] is now [wearer.resting ? "resting" : "getting up"]."
+
+/obj/item/weapon/rig/proc/forced_move(var/direction, var/mob/user)
+
+ // Why is all this shit in client/Move()? Who knows?
+ if(world.time < wearer_move_delay)
+ return
+
+ if(!wearer.loc || !ai_can_move_suit(user, check_user_module = 1))
+ return
+
+ //This is sota the goto stop mobs from moving var
+ if(wearer.transforming || !wearer.canmove)
+ return
+
+ if(locate(/obj/effect/stop/, wearer.loc))
+ for(var/obj/effect/stop/S in wearer.loc)
+ if(S.victim == wearer)
+ return
+
+ if(!wearer.lastarea)
+ wearer.lastarea = get_area(wearer.loc)
+
+ if((istype(wearer.loc, /turf/space)) || (wearer.lastarea.has_gravity == 0))
+ if(!wearer.Process_Spacemove(0))
+ return 0
+
+ if(malfunctioning)
+ direction = pick(cardinal)
+
+ // Inside an object, tell it we moved.
+ if(isobj(wearer.loc) || ismob(wearer.loc))
+ var/atom/O = wearer.loc
+ return O.relaymove(wearer, direction)
+
+ if(isturf(wearer.loc))
+ if(wearer.restrained())//Why being pulled while cuffed prevents you from moving
+ for(var/mob/M in range(wearer, 1))
+ if(M.pulling == wearer)
+ if(!M.restrained() && M.stat == 0 && M.canmove && wearer.Adjacent(M))
+ user << "Your host is restrained! They can't move!"
+ return 0
+ else
+ M.stop_pulling()
+
+ if(wearer.pinned.len)
+ src << "Your host is pinned to a wall by [wearer.pinned[1]]!"
+ return 0
+
+ // AIs are a bit slower than regular and ignore move intent.
+ wearer.last_move_intent = world.time + ai_controlled_move_delay
+ wearer_move_delay = world.time + ai_controlled_move_delay
+
+ var/tickcomp = 0
+ if(config.Tickcomp)
+ tickcomp = ((1/(world.tick_lag))*1.3) - 1.3
+ wearer_move_delay += tickcomp
+
+ if(istype(wearer.buckled, /obj/vehicle))
+ //manually set move_delay for vehicles so we don't inherit any mob movement penalties
+ //specific vehicle move delays are set in code\modules\vehicles\vehicle.dm
+ wearer_move_delay = world.time + tickcomp
+ return wearer.buckled.relaymove(wearer, direction)
+
+ if(istype(wearer.machine, /obj/machinery))
+ if(wearer.machine.relaymove(wearer, direction))
+ return
+
+ if(wearer.pulledby || wearer.buckled) // Wheelchair driving!
+ if(istype(wearer.loc, /turf/space))
+ return // No wheelchair driving in space
+ if(istype(wearer.pulledby, /obj/structure/bed/chair/wheelchair))
+ return wearer.pulledby.relaymove(wearer, direction)
+ else if(istype(wearer.buckled, /obj/structure/bed/chair/wheelchair))
+ if(ishuman(wearer.buckled))
+ var/obj/item/organ/external/l_hand = wearer.get_organ("l_hand")
+ var/obj/item/organ/external/r_hand = wearer.get_organ("r_hand")
+ if((!l_hand || (l_hand.status & ORGAN_DESTROYED)) && (!r_hand || (r_hand.status & ORGAN_DESTROYED)))
+ return // No hands to drive your chair? Tough luck!
+ wearer_move_delay += 2
+ return wearer.buckled.relaymove(wearer,direction)
+
+ cell.use(200) //Arbitrary, TODO
+ wearer.Move(get_step(get_turf(wearer),direction),direction)
/atom/proc/get_rig()
if(loc)
diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm
index 1eb0b53f04f..ff7255523d3 100644
--- a/code/modules/mob/living/carbon/human/death.dm
+++ b/code/modules/mob/living/carbon/human/death.dm
@@ -61,11 +61,13 @@
if(!gibbed && species.death_sound)
playsound(loc, species.death_sound, 80, 1, 1)
-
if(ticker && ticker.mode)
sql_report_death(src)
ticker.mode.check_win()
+ if(wearing_rig)
+ wearing_rig.notify_ai("Warning: user death event. Mobility control passed to integrated intelligence system.")
+
return ..(gibbed,species.death_message)
/mob/living/carbon/human/proc/ChangeToHusk()
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index dc34cfec3ca..e9daa58d5f6 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -7,6 +7,7 @@
var/list/hud_list[10]
var/embedded_flag //To check if we've need to roll for damage on movement while an item is imbedded in us.
+ var/obj/item/weapon/rig/wearing_rig // This is very not good, but it's much much better than calling get_rig() every update_canmove() call.
/mob/living/carbon/human/New(var/new_loc, var/new_species = null)
@@ -1373,3 +1374,7 @@
if(istype(shoes, /obj/item/clothing/shoes/magboots) && (shoes.flags & NOSLIP)) //magboots + dense_object = no floating
return 1
return 0
+
+/mob/living/carbon/human/can_stand_overridden()
+ return wearing_rig && wearing_rig.ai_can_move_suit(check_for_ai = 1)
+
diff --git a/code/modules/mob/living/carbon/human/human_damage.dm b/code/modules/mob/living/carbon/human/human_damage.dm
index 8f825046097..07f9aab3ec4 100644
--- a/code/modules/mob/living/carbon/human/human_damage.dm
+++ b/code/modules/mob/living/carbon/human/human_damage.dm
@@ -145,6 +145,9 @@
/mob/living/carbon/human/Paralyse(amount)
if(HULK in mutations) return
+ // Notify our AI if they can now control the suit.
+ if(wearing_rig && !stat && paralysis < amount) //We are passing out right this second.
+ wearing_rig.notify_ai("Warning: user consciousness failure. Mobility control passed to integrated intelligence system.")
..()
/mob/living/carbon/human/getCloneLoss()
diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm
index f4b28d2b6b0..17d125d488a 100644
--- a/code/modules/mob/living/carbon/human/human_movement.dm
+++ b/code/modules/mob/living/carbon/human/human_movement.dm
@@ -1,5 +1,9 @@
/mob/living/carbon/human/movement_delay()
+ // Used by the AI suit control proc.
+ if(stat)
+ return 20
+
var/tally = 0
if(species.slowdown)
diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm
index 282831641c2..a55f94e01ae 100644
--- a/code/modules/mob/living/carbon/human/life.dm
+++ b/code/modules/mob/living/carbon/human/life.dm
@@ -52,6 +52,10 @@
// update the current life tick, can be used to e.g. only do something every 4 ticks
life_tick++
+ // This is not an ideal place for this but it will do for now.
+ if(wearing_rig && wearing_rig.offline)
+ wearing_rig = null
+
in_stasis = istype(loc, /obj/structure/closet/body_bag/cryobag) && loc:opened == 0
if(in_stasis) loc:used++
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 632bd4dc737..4212ad0e3a3 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -657,7 +657,7 @@ default behaviour is:
set category = "IC"
resting = !resting
- src << "\blue You are now [resting ? "resting" : "getting up"]"
+ src << "You are now [resting ? "resting" : "getting up"]."
/mob/living/proc/handle_ventcrawl(var/obj/machinery/atmospherics/unary/vent_pump/vent_found = null, var/ignore_items = 0) // -- TLE -- Merged by Carn
if(stat)
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index 3004b9dd9e4..f6be9a8a873 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -684,5 +684,15 @@ var/list/ai_verbs_default = list(
icon_state = selected_sprite.alive_icon
set_light(1, 1, selected_sprite.alive_light)
+// Pass lying down or getting up to our pet human, if we're in a rig.
+/mob/living/silicon/ai/lay_down()
+ set name = "Rest"
+ set category = "IC"
+
+ resting = 0
+ var/obj/item/weapon/rig/rig = src.get_rig()
+ if(istype(rig))
+ rig.force_rest(src)
+
#undef AI_CHECK_WIRELESS
#undef AI_CHECK_RADIO
diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm
index ad06c58d6ea..d8d474e438d 100644
--- a/code/modules/mob/living/silicon/pai/pai.dm
+++ b/code/modules/mob/living/silicon/pai/pai.dm
@@ -73,8 +73,6 @@
var/current_pda_messaging = null
/mob/living/silicon/pai/New(var/obj/item/device/paicard)
-
- canmove = 0
src.loc = paicard
card = paicard
sradio = new(src)
@@ -277,8 +275,6 @@
var/obj/item/device/pda/holder = card.loc
holder.pai = null
- canmove = 1
-
src.client.perspective = EYE_PERSPECTIVE
src.client.eye = src
src.forceMove(get_turf(card))
@@ -340,12 +336,16 @@
set name = "Rest"
set category = "IC"
+ // Pass lying down or getting up to our pet human, if we're in a rig.
if(istype(src.loc,/obj/item/device/paicard))
resting = 0
+ var/obj/item/weapon/rig/rig = src.get_rig()
+ if(istype(rig))
+ rig.force_rest(src)
else
resting = !resting
icon_state = resting ? "[chassis]_rest" : "[chassis]"
- src << "\blue You are now [resting ? "resting" : "getting up"]"
+ src << "You are now [resting ? "resting" : "getting up"]"
canmove = !resting
@@ -394,7 +394,8 @@
card.loc = get_turf(card)
src.forceMove(card)
card.forceMove(card.loc)
- canmove = 0
+ canmove = 1
+ resting = 0
icon_state = "[chassis]"
/mob/living/silicon/pai/start_pulling(var/atom/movable/AM)
@@ -430,4 +431,4 @@
get_scooped(H)
return
else
- return ..()
+ return ..()
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 9f24c4e1a17..4c9013d9dcb 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -689,40 +689,52 @@
if(transforming) return 0
return 1
+// Not sure what to call this. Used to check if humans are wearing an AI-controlled exosuit and hence don't need to fall over yet.
+/mob/proc/can_stand_overridden()
+ return 0
+
+/mob/proc/cannot_stand()
+ return stat || weakened || paralysis || resting || sleeping || (status_flags & FAKEDEATH)
+
//Updates canmove, lying and icons. Could perhaps do with a rename but I can't think of anything to describe it.
/mob/proc/update_canmove()
- if(istype(buckled, /obj/vehicle))
- var/obj/vehicle/V = buckled
- if(stat || weakened || paralysis || resting || sleeping || (status_flags & FAKEDEATH))
- lying = 1
- canmove = 0
- pixel_y = V.mob_offset_y - 5
- else
- if(buckled.buckle_lying != -1) lying = buckled.buckle_lying
- canmove = 1
- pixel_y = V.mob_offset_y
- else if(buckled)
- anchored = 1
- canmove = 0
- if(istype(buckled))
- if(buckled.buckle_lying != -1)
- lying = buckled.buckle_lying
- if(buckled.buckle_movable)
- anchored = 0
- canmove = 1
- else if( stat || weakened || paralysis || resting || sleeping || (status_flags & FAKEDEATH))
- lying = 1
- canmove = 0
- else if(stunned)
- canmove = 0
- else if(captured)
- anchored = 1
- canmove = 0
- lying = 0
- else
+ if(!resting && cannot_stand() && can_stand_overridden())
lying = 0
canmove = 1
+ else
+ if(istype(buckled, /obj/vehicle))
+ var/obj/vehicle/V = buckled
+ if(cannot_stand())
+ lying = 1
+ canmove = 0
+ pixel_y = V.mob_offset_y - 5
+ else
+ if(buckled.buckle_lying != -1) lying = buckled.buckle_lying
+ canmove = 1
+ pixel_y = V.mob_offset_y
+ else if(buckled)
+ anchored = 1
+ canmove = 0
+ if(istype(buckled))
+ if(buckled.buckle_lying != -1)
+ lying = buckled.buckle_lying
+ if(buckled.buckle_movable)
+ anchored = 0
+ canmove = 1
+
+ else if(cannot_stand())
+ lying = 1
+ canmove = 0
+ else if(stunned)
+ canmove = 0
+ else if(captured)
+ anchored = 1
+ canmove = 0
+ lying = 0
+ else
+ lying = 0
+ canmove = 1
if(lying)
density = 0
diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm
index bb333b82c54..7e47244acc5 100644
--- a/code/modules/mob/mob_movement.dm
+++ b/code/modules/mob/mob_movement.dm
@@ -222,7 +222,6 @@
if(Process_Grab()) return
-
if(!mob.canmove)
return
@@ -235,7 +234,6 @@
if((istype(mob.loc, /turf/space)) || (mob.lastarea.has_gravity == 0))
if(!mob.Process_Spacemove(0)) return 0
-
if(isobj(mob.loc) || ismob(mob.loc))//Inside an object, tell it we moved
var/atom/O = mob.loc
return O.relaymove(mob, direct)