diff --git a/code/__DEFINES/hud.dm b/code/__DEFINES/hud.dm index 6fe2d302e7b..6771d8062fc 100644 --- a/code/__DEFINES/hud.dm +++ b/code/__DEFINES/hud.dm @@ -25,6 +25,7 @@ #define PLANT_TOXIN_HUD "20"// Toxin level #define PLANT_PEST_HUD "21"// Pest level #define PLANT_WEED_HUD "22"// Weed level +#define DIAG_TRACK_HUD "23"// Mech tracking beacon //data HUD (medhud, sechud) defines //Don't forget to update human/New() if you change these! diff --git a/code/game/data_huds.dm b/code/game/data_huds.dm index 172aaa821f2..59557caea6a 100644 --- a/code/game/data_huds.dm +++ b/code/game/data_huds.dm @@ -44,7 +44,7 @@ hud_icons = list(ID_HUD, IMPTRACK_HUD, IMPLOYAL_HUD, IMPCHEM_HUD, WANTED_HUD) /datum/atom_hud/data/diagnostic - hud_icons = list (DIAG_HUD, DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_MECH_HUD, DIAG_BOT_HUD) + hud_icons = list (DIAG_HUD, DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_MECH_HUD, DIAG_BOT_HUD, DIAG_TRACK_HUD) /datum/atom_hud/data/hydroponic hud_icons = list (PLANT_NUTRIENT_HUD, PLANT_WATER_HUD, PLANT_STATUS_HUD, PLANT_HEALTH_HUD, PLANT_TOXIN_HUD, PLANT_PEST_HUD, PLANT_WEED_HUD) @@ -287,6 +287,17 @@ if(internal_damage) holder.icon_state = "hudwarn" +/obj/mecha/proc/diag_hud_set_mechtracking() //Shows tracking beacons on the mech + var/image/holder = hud_list[DIAG_TRACK_HUD] + var/new_icon_state //This var exists so that the holder's icon state is set only once in the event of multiple mech beacons. + for(var/obj/item/mecha_parts/mecha_tracking/T in trackers) + if(T.ai_beacon) //Beacon with AI uplink + new_icon_state = "hudtrackingai" + break //Immediately terminate upon finding an AI beacon to ensure it is always shown over the normal one, as mechs can have several trackers. + else + new_icon_state = "hudtracking" + holder.icon_state = new_icon_state + /*~~~~~~~~~ Bots! ~~~~~~~~~~*/ diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index 6e155779aa5..4ec4746a198 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -50,6 +50,7 @@ var/obj/machinery/atmospherics/unary/portables_connector/connected_port = null var/obj/item/device/radio/radio = null + var/list/trackers = list() var/max_temperature = 25000 var/internal_damage_threshold = 50 //health percentage below which internal damage is possible @@ -74,7 +75,7 @@ var/melee_cooldown = 10 var/melee_can_hit = 1 - hud_possible = list (DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_MECH_HUD) + hud_possible = list (DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_MECH_HUD, DIAG_TRACK_HUD) /obj/mecha/New() ..() @@ -102,6 +103,7 @@ diag_hud_set_mechhealth() diag_hud_set_mechcell() diag_hud_set_mechstat() + diag_hud_set_mechtracking() //////////////////////// ////// Helpers ///////// @@ -165,7 +167,7 @@ /obj/mecha/proc/drop_item()//Derpfix, but may be useful in future for engineering exosuits. return -/obj/mecha/hear_talk(mob/M as mob, text) +/obj/mecha/hear_talk(mob/M, text) if(M == occupant && radio.broadcasting) radio.talk_into(M, text) @@ -465,7 +467,7 @@ qdel(src) return -/obj/mecha/attack_hand(mob/living/user as mob) +/obj/mecha/attack_hand(mob/living/user) user.changeNext_move(CLICK_CD_MELEE) user.do_attack_animation(src) log_message("Attack by hand/paw. Attacker - [user].",1) @@ -481,7 +483,7 @@ return -/obj/mecha/attack_alien(mob/living/user as mob) +/obj/mecha/attack_alien(mob/living/user) log_message("Attack by alien. Attacker - [user].",1) user.changeNext_move(CLICK_CD_MELEE) user.do_attack_animation(src) @@ -500,7 +502,7 @@ return -/obj/mecha/attack_animal(mob/living/simple_animal/user as mob) +/obj/mecha/attack_animal(mob/living/simple_animal/user) log_message("Attack by simple animal. Attacker - [user].",1) user.changeNext_move(CLICK_CD_MELEE) user.do_attack_animation(src) @@ -513,7 +515,7 @@ take_damage(damage) check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) visible_message("[user] [user.attacktext] [src]!") - user.create_attack_log("attacked [src.name]") + user.create_attack_log("attacked [name]") else log_append_to_last("Armor saved.") playsound(loc, 'sound/weapons/slash.ogg', 50, 1, -1) @@ -798,7 +800,9 @@ to_chat(user, "\the [W] is stuck to your hand, you cannot put it in \the [src]") return W.forceMove(src) + trackers += W user.visible_message("[user] attaches [W] to [src].", "You attach [W] to [src].") + diag_hud_set_mechtracking() return else if(istype(W, /obj/item/weapon/paintkit)) @@ -866,20 +870,35 @@ //////////// AI piloting //////////// ///////////////////////////////////// -/obj/mecha/attack_ai(var/mob/living/silicon/ai/user as mob) +/obj/mecha/attack_ai(mob/living/silicon/ai/user) if(!isAI(user)) return //Allows the Malf to scan a mech's status and loadout, helping it to decide if it is a worthy chariot. if(user.can_dominate_mechs) examine(user) //Get diagnostic information! - var/obj/item/mecha_parts/mecha_tracking/B = locate(/obj/item/mecha_parts/mecha_tracking) in src - if(B) //Beacons give the AI more detailed mech information. + for(var/obj/item/mecha_parts/mecha_tracking/B in trackers) to_chat(user, "Warning: Tracking Beacon detected. Enter at your own risk. Beacon Data:") - to_chat(user, "[B.get_mecha_info()]") + to_chat(user, "[B.get_mecha_info_text()]") + break //Nothing like a big, red link to make the player feel powerful! to_chat(user, "ASSUME DIRECT CONTROL?
") + else + examine(user) + if(occupant) + user << "This exosuit has a pilot and cannot be controlled." + return + var/can_control_mech = FALSE + for(var/obj/item/mecha_parts/mecha_tracking/ai_control/A in trackers) + can_control_mech = TRUE + to_chat(user, "[bicon(src)] Status of [name]:\n\ + [A.get_mecha_info_text()]") + break + if(!can_control_mech) + to_chat(user, "You cannot control exosuits without AI control beacons installed.") + return + to_chat(user, "Take control of exosuit?
") -/obj/mecha/transfer_ai(var/interaction, mob/user, var/mob/living/silicon/ai/AI, var/obj/item/device/aicard/card) +/obj/mecha/transfer_ai(interaction, mob/user, mob/living/silicon/ai/AI, obj/item/device/aicard/card) if(!..()) return @@ -907,12 +926,13 @@ to_chat(AI, "You have been downloaded to a mobile storage device. Wireless connection offline.") to_chat(user, "Transfer successful: [AI.name] ([rand(1000,9999)].exe) removed from [name] and stored within local memory.") - if(AI_MECH_HACK) //Called by Malf AI mob on the mech. - new /obj/structure/AIcore/deactivated(AI.loc) - if(occupant) //Oh, I am sorry, were you using that? - to_chat(AI, "Pilot detected! Forced ejection initiated!") - to_chat(occupant, "You have been forcibly ejected!") - go_out(1) //IT IS MINE, NOW. SUCK IT, RD! + if(AI_MECH_HACK) //Called by AIs on the mech + AI.linked_core = new /obj/structure/AIcore/deactivated(AI.loc) + if(AI.can_dominate_mechs) + if(occupant) //Oh, I am sorry, were you using that? + to_chat(AI, "Pilot detected! Forced ejection initiated!") + to_chat(occupant, "You have been forcibly ejected!") + go_out(1) //IT IS MINE, NOW. SUCK IT, RD! ai_enter_mech(AI, interaction) if(AI_TRANS_FROM_CARD) //Using an AI card to upload to a mech. @@ -932,7 +952,7 @@ ai_enter_mech(AI, interaction) //Hack and From Card interactions share some code, so leave that here for both to use. -/obj/mecha/proc/ai_enter_mech(var/mob/living/silicon/ai/AI, var/interaction) +/obj/mecha/proc/ai_enter_mech(mob/living/silicon/ai/AI, interaction) AI.aiRestorePowerRoutine = 0 AI.loc = src occupant = AI @@ -945,7 +965,7 @@ AI.remote_control = src AI.canmove = 1 //Much easier than adding AI checks! Be sure to set this back to 0 if you decide to allow an AI to leave a mech somehow. AI.can_shunt = 0 //ONE AI ENTERS. NO AI LEAVES. - to_chat(AI, "[interaction == AI_MECH_HACK ? "Takeover of [name] complete! You are now permanently loaded onto the onboard computer. Do not attempt to leave the station sector!" \ + to_chat(AI, "[AI.can_dominate_mechs ? "Takeover of [name] complete! You are now permanently loaded onto the onboard computer. Do not attempt to leave the station sector!" \ : "You have been uploaded to a mech's onboard computer."]") to_chat(AI, "Use Middle-Mouse to activate mech functions and equipment. Click normally for AI interactions.") @@ -1172,7 +1192,7 @@ to_chat(user, "You stop inserting the MMI.") return 0 -/obj/mecha/proc/mmi_moved_inside(var/obj/item/device/mmi/mmi_as_oc as obj,mob/user as mob) +/obj/mecha/proc/mmi_moved_inside(obj/item/device/mmi/mmi_as_oc,mob/user) if(mmi_as_oc && user in range(1)) if(!mmi_as_oc.brainmob || !mmi_as_oc.brainmob.client) to_chat(user, "Consciousness matrix not detected.") @@ -1239,7 +1259,7 @@ if(occupant && occupant == M) // The occupant exited the mech without calling go_out() go_out(1, newloc) -/obj/mecha/proc/go_out(var/forced, var/atom/newloc = loc) +/obj/mecha/proc/go_out(forced, atom/newloc = loc) if(!occupant) return var/atom/movable/mob_container @@ -1248,15 +1268,29 @@ if(ishuman(occupant)) mob_container = occupant //RemoveActions(occupant, human_occupant=1) - else if(istype(occupant, /mob/living/carbon/brain)) + else if(isbrain(occupant)) var/mob/living/carbon/brain/brain = occupant //RemoveActions(brain) mob_container = brain.container - else if(isAI(occupant) && forced) //This should only happen if there are multiple AIs in a round, and at least one is Malf. - //RemoveActions(occupant) - occupant.gib() //If one Malf decides to steal a mech from another AI (even other Malfs!), they are destroyed, as they have nowhere to go when replaced. - occupant = null - return + else if(isAI(occupant)) + var/mob/living/silicon/ai/AI = occupant + if(forced)//This should only happen if there are multiple AIs in a round, and at least one is Malf. + //RemoveActions(occupant) + occupant.gib() //If one Malf decides to steal a mech from another AI (even other Malfs!), they are destroyed, as they have nowhere to go when replaced. + occupant = null + return + else + if(!AI.linked_core || qdeleted(AI.linked_core)) + to_chat(AI, "Inactive core destroyed. Unable to return.") + AI.linked_core = null + return + to_chat(AI, "Returning to core...") + AI.controlled_mech = null + AI.remote_control = null + //RemoveActions(occupant, 1) + mob_container = AI + newloc = get_turf(AI.linked_core) + qdel(AI.linked_core) else return var/mob/living/L = occupant @@ -1439,6 +1473,7 @@ diag_hud_set_mechhealth() diag_hud_set_mechcell() diag_hud_set_mechstat() + diag_hud_set_mechtracking() /obj/mecha/speech_bubble(var/bubble_state = "",var/bubble_loc = src, var/list/bubble_recipients = list()) diff --git a/code/game/mecha/mecha_control_console.dm b/code/game/mecha/mecha_control_console.dm index 11c940f7971..2bb73e2c636 100644 --- a/code/game/mecha/mecha_control_console.dm +++ b/code/game/mecha/mecha_control_console.dm @@ -10,10 +10,10 @@ var/screen = 0 var/stored_data -/obj/machinery/computer/mecha/attack_ai(var/mob/user as mob) +/obj/machinery/computer/mecha/attack_ai(mob/user) return attack_hand(user) -/obj/machinery/computer/mecha/attack_hand(var/mob/user as mob) +/obj/machinery/computer/mecha/attack_hand(mob/user) ui_interact(user) /obj/machinery/computer/mecha/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) @@ -73,11 +73,12 @@ icon_state = "motion2" w_class = 2 origin_tech = "programming=2;magnets=2" + var/ai_beacon = FALSE //If this beacon allows for AI control. Exists to avoid using istype() on checking. /obj/item/mecha_parts/mecha_tracking/proc/get_mecha_info() if(!in_mecha()) - return 0 - var/obj/mecha/M = src.loc + return FALSE + var/obj/mecha/M = loc var/list/answer[0] answer["reference"] = "\ref[src]" answer["name"] = sanitize(replacetext(M.name,"\"","'")) // Double apostrophes break JSON @@ -101,6 +102,25 @@ return answer +/obj/item/mecha_parts/mecha_tracking/proc/get_mecha_info_text() + if(!in_mecha()) + return FALSE + var/obj/mecha/M = loc + var/cell_charge = M.get_charge() + var/area/A = get_area(M) + var/answer = {"Name: [M.name] + Integrity: [M.health/initial(M.health)*100]% + Cell charge: [isnull(cell_charge)?"Not found":"[M.cell.percent()]%"] + Airtank: [M.return_pressure()]kPa + Pilot: [M.occupant||"None"] + Location: [sanitize(A.name)||"Unknown"] + Active equipment: [M.selected||"None"]
"} + if(istype(M, /obj/mecha/working/ripley)) + var/obj/mecha/working/ripley/RM = M + answer += "Used cargo space: [RM.cargo.len/RM.cargo_capacity*100]%
" + + return answer + /obj/item/mecha_parts/mecha_tracking/emp_act() qdel(src) return @@ -110,9 +130,9 @@ return /obj/item/mecha_parts/mecha_tracking/proc/in_mecha() - if(istype(src.loc, /obj/mecha)) - return src.loc - return 0 + if(istype(loc, /obj/mecha)) + return loc + return FALSE /obj/item/mecha_parts/mecha_tracking/proc/shock() var/obj/mecha/M = in_mecha() @@ -121,19 +141,26 @@ qdel(src) /obj/item/mecha_parts/mecha_tracking/proc/get_mecha_log() - if(!src.in_mecha()) + if(!in_mecha()) return 0 - var/obj/mecha/M = src.loc + var/obj/mecha/M = loc return M.get_log_html() +/obj/item/mecha_parts/mecha_tracking/ai_control + name = "exosuit AI control beacon" + desc = "A device used to transmit exosuit data. Also allows active AI units to take control of said exosuit." + origin_tech = "programming=3;magnets=2;engineering=2" + ai_beacon = TRUE + /obj/item/weapon/storage/box/mechabeacons name = "Exosuit Tracking Beacons" - New() - ..() - new /obj/item/mecha_parts/mecha_tracking(src) - new /obj/item/mecha_parts/mecha_tracking(src) - new /obj/item/mecha_parts/mecha_tracking(src) - new /obj/item/mecha_parts/mecha_tracking(src) - new /obj/item/mecha_parts/mecha_tracking(src) - new /obj/item/mecha_parts/mecha_tracking(src) - new /obj/item/mecha_parts/mecha_tracking(src) + +/obj/item/weapon/storage/box/mechabeacons/New() + ..() + new /obj/item/mecha_parts/mecha_tracking(src) + new /obj/item/mecha_parts/mecha_tracking(src) + new /obj/item/mecha_parts/mecha_tracking(src) + new /obj/item/mecha_parts/mecha_tracking(src) + new /obj/item/mecha_parts/mecha_tracking(src) + new /obj/item/mecha_parts/mecha_tracking(src) + new /obj/item/mecha_parts/mecha_tracking(src) diff --git a/code/game/mecha/medical/medical.dm b/code/game/mecha/medical/medical.dm index ce28687a9e6..8aee3ef1f4c 100644 --- a/code/game/mecha/medical/medical.dm +++ b/code/game/mecha/medical/medical.dm @@ -4,4 +4,4 @@ /obj/mecha/medical/New() ..() - new /obj/item/mecha_parts/mecha_tracking(src) + trackers += new /obj/item/mecha_parts/mecha_tracking(src) diff --git a/code/game/mecha/working/ripley.dm b/code/game/mecha/working/ripley.dm index f26e34834fb..bb5e0237c9b 100644 --- a/code/game/mecha/working/ripley.dm +++ b/code/game/mecha/working/ripley.dm @@ -110,10 +110,10 @@ //Add ore box to cargo cargo.Add(new /obj/structure/ore_box(src)) - //Attach hydrolic clamp + //Attach hydraulic clamp var/obj/item/mecha_parts/mecha_equipment/hydraulic_clamp/HC = new /obj/item/mecha_parts/mecha_equipment/hydraulic_clamp HC.attach(src) - for(var/obj/item/mecha_parts/mecha_tracking/B in contents)//Deletes the beacon so it can't be found easily + for(var/obj/item/mecha_parts/mecha_tracking/B in trackers)//Deletes the beacon so it can't be found easily qdel(B) var/obj/item/mecha_parts/mecha_equipment/mining_scanner/scanner = new /obj/item/mecha_parts/mecha_equipment/mining_scanner diff --git a/code/game/mecha/working/working.dm b/code/game/mecha/working/working.dm index ccbfe2ef21b..e4c001a508a 100644 --- a/code/game/mecha/working/working.dm +++ b/code/game/mecha/working/working.dm @@ -3,4 +3,4 @@ /obj/mecha/working/New() ..() - new /obj/item/mecha_parts/mecha_tracking(src) + trackers += new /obj/item/mecha_parts/mecha_tracking(src) diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 6d3527d3f09..44bb237848a 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -99,6 +99,8 @@ var/list/ai_verbs_default = list( var/obj/machinery/camera/portable/builtInCamera + var/obj/structure/AIcore/deactivated/linked_core //For exosuit control + var/arrivalmsg = "$name, $rank, has arrived on the station." /mob/living/silicon/ai/proc/add_ai_verbs() @@ -535,7 +537,16 @@ var/list/ai_verbs_default = list( if(href_list["ai_take_control"]) //Mech domination var/obj/mecha/M = locate(href_list["ai_take_control"]) if(controlled_mech) - to_chat(src, "You are already loaded into an onboard computer!") + to_chat(src, "You are already loaded into an onboard computer!") + return + if(!cameranet.checkCameraVis(M)) + to_chat(src, "Exosuit is no longer near active cameras.") + return + if(lacks_power()) + to_chat(src, "You're depowered!") + return + if(!isturf(loc)) + to_chat(src, "You aren't in your core!") return if(M) M.transfer_ai(AI_MECH_HACK,src, usr) //Called om the mech itself. diff --git a/code/modules/research/designs/mechfabricator_designs.dm b/code/modules/research/designs/mechfabricator_designs.dm index 702afa68f2f..5ea86d2c7a3 100644 --- a/code/modules/research/designs/mechfabricator_designs.dm +++ b/code/modules/research/designs/mechfabricator_designs.dm @@ -1078,6 +1078,16 @@ construction_time = 50 category = list("Misc") +/datum/design/mecha_tracking_ai_control + name = "AI Control Beacon" + id = "mecha_tracking_ai_control" + build_type = MECHFAB + build_path = /obj/item/mecha_parts/mecha_tracking/ai_control + materials = list(MAT_METAL = 1000, MAT_GLASS = 500, MAT_SILVER = 200) + req_tech = list("programming" = 3, "magnets" = 2, "engineering" = 2) + construction_time = 50 + category = list("Misc") + /datum/design/ipc_head name = "IPC Head" id = "ipc_head" diff --git a/icons/mob/hud.dmi b/icons/mob/hud.dmi index efff252421b..1de6fc4ac1d 100644 Binary files a/icons/mob/hud.dmi and b/icons/mob/hud.dmi differ