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)