From 80360fd8a97f52b73beebd730d39d84c8ad8df69 Mon Sep 17 00:00:00 2001 From: Markolie Date: Tue, 18 Aug 2015 20:22:38 +0200 Subject: [PATCH] Malf AI update --- code/__DEFINES/misc.dm | 5 + code/_globalvars/lists/objects.dm | 4 +- code/_onclick/ai.dm | 2 + .../gamemodes/malfunction/Malf_Modules.dm | 234 ++++++++++------ .../game/gamemodes/malfunction/malfunction.dm | 11 +- code/game/machinery/computer/ai_core.dm | 215 ++------------ code/game/machinery/computer/aifixer.dm | 84 +++--- code/game/mecha/equipment/tools/tools.dm | 14 +- code/game/mecha/mecha.dm | 264 ++++++++++-------- code/game/objects/items/devices/aicard.dm | 211 +++++++------- code/game/objects/items/weapons/RCD.dm | 8 +- code/modules/mob/living/silicon/ai/ai.dm | 30 +- code/modules/mob/living/silicon/ai/life.dm | 4 +- code/modules/mob/mob_movement.dm | 9 +- 14 files changed, 546 insertions(+), 549 deletions(-) diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index 4a5b588a629..4efd869751f 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -134,6 +134,11 @@ #define RESIZE_DEFAULT_SIZE 1 +//transfer_ai() defines. Main proc in ai_core.dm +#define AI_TRANS_TO_CARD 1 //Downloading AI to InteliCard. +#define AI_TRANS_FROM_CARD 2 //Uploading AI from InteliCard +#define AI_MECH_HACK 3 //Malfunctioning AI hijacking mecha + //singularity defines #define STAGE_ONE 1 #define STAGE_TWO 3 diff --git a/code/_globalvars/lists/objects.dm b/code/_globalvars/lists/objects.dm index 8c419cc35fb..690ac0e5c5b 100644 --- a/code/_globalvars/lists/objects.dm +++ b/code/_globalvars/lists/objects.dm @@ -17,6 +17,6 @@ var/global/list/table_recipes = list() //list of all table craft recipes var/global/list/all_areas = list() var/global/list/machines = list() -var/global/list/processing_power_items = list() - //items that ask to be called every cycle +var/global/list/processing_power_items = list() //items that ask to be called every cycle +var/global/list/rcd_list = list() //list of Rapid Construction Devices. diff --git a/code/_onclick/ai.dm b/code/_onclick/ai.dm index ca02a66a463..ed0e976c5da 100644 --- a/code/_onclick/ai.dm +++ b/code/_onclick/ai.dm @@ -43,6 +43,8 @@ return if(modifiers["middle"]) MiddleClickOn(A) + if(controlled_mech) //Are we piloting a mech? Placed here so the modifiers are not overridden. + controlled_mech.click_action(A, src) //Override AI normal click behavior. return if(modifiers["shift"]) ShiftClickOn(A) diff --git a/code/game/gamemodes/malfunction/Malf_Modules.dm b/code/game/gamemodes/malfunction/Malf_Modules.dm index 363053d4984..a464bfc29cd 100644 --- a/code/game/gamemodes/malfunction/Malf_Modules.dm +++ b/code/game/gamemodes/malfunction/Malf_Modules.dm @@ -34,7 +34,7 @@ rcd light flash thingy on matter drain /datum/AI_Module/large/fireproof_core - module_name = "Core upgrade" + module_name = "Core Upgrade" mod_pick_name = "coreup" description = "An upgrade to improve core resistance, making it immune to fire and heat. This effect is permanent." cost = 50 @@ -51,9 +51,9 @@ rcd light flash thingy on matter drain src << "Core fireproofed." /datum/AI_Module/large/upgrade_turrets - module_name = "AI Turret upgrade" + module_name = "AI Turret Upgrade" mod_pick_name = "turret" - description = "Improves the firing speed and health of all AI turrets. This effect is permanent." + description = "Improves the firing power and health of all AI turrets. This effect is permanent." cost = 50 one_time = 1 @@ -62,26 +62,25 @@ rcd light flash thingy on matter drain /mob/living/silicon/ai/proc/upgrade_turrets() set category = "Malfunction" set name = "Upgrade Turrets" + + if(stat) + return + src.verbs -= /mob/living/silicon/ai/proc/upgrade_turrets - for(var/obj/machinery/turret/turret in machines) - var/turf/T = get_turf(turret) - if(T.z in config.station_levels) - turret.health += 30 - turret.shot_delay = 20 for(var/obj/machinery/porta_turret/turret in machines) var/turf/T = get_turf(turret) if(T.z in config.station_levels) // Increase health by 37.5% of original max, decrease delays between shots to 66% turret.health += initial(turret.health) * 3 / 8 - turret.shot_delay = initial(turret.shot_delay) * 2 / 3 + turret.eprojectile = /obj/item/projectile/beam/heavylaser //Once you see it, you will know what it means to FEAR. + turret.eshot_sound = 'sound/weapons/lasercannonfire.ogg' src << "Turrets upgraded." /datum/AI_Module/large/lockdown module_name = "Hostile Station Lockdown" mod_pick_name = "lockdown" - description = "Overload the airlock, blast door and fire control networks, locking them down. Caution! This command also electrifies all airlocks. The networks will automatically reset after 90 seconds." - cost = 30 - one_time = 1 + description = "Overload the airlock, blast door and fire control networks, locking them down. Caution! This command also electrifies all airlocks. The networks will automatically reset after 120 seconds." + cost = 20 power_type = /mob/living/silicon/ai/proc/lockdown @@ -89,12 +88,11 @@ rcd light flash thingy on matter drain set category = "Malfunction" set name = "Initiate Hostile Lockdown" - if(src.stat == DEAD) - src << "You cannot begin a lockdown because you are dead!" + if(stat) return - + var/obj/machinery/door/airlock/AL - for(var/obj/machinery/door/airlock/D in airlocks) + for(var/obj/machinery/door/D in airlocks) if(!(D.z in config.contact_levels)) continue spawn() @@ -115,16 +113,16 @@ rcd light flash thingy on matter drain verbs -= /mob/living/silicon/ai/proc/lockdown minor_announcement.Announce("Hostile runtime detected in door controllers. Isolation lockdown protocols are now in effect. Please remain calm.","Network Alert") - src << "Lockdown initiated. Network reset in 90 seconds." - spawn(900) //90 Seconds. - disablelockdown() //Reset the lockdown after 90 seconds. + src << "Lockdown initiated. Network reset in two minutes." + spawn(1200) //120 Seconds. + disablelockdown() //Reset the lockdown after 120 seconds. /mob/living/silicon/ai/proc/disablelockdown() set category = "Malfunction" set name = "Disable Lockdown" var/obj/machinery/door/airlock/AL - for(var/obj/machinery/door/airlock/D in airlocks) + for(var/obj/machinery/door/D in airlocks) if(!(D.z in config.contact_levels)) continue spawn() @@ -141,28 +139,54 @@ rcd light flash thingy on matter drain minor_announcement.Announce("Automatic system reboot complete. Have a secure day.","Network Reset") /datum/AI_Module/large/disable_rcd - module_name = "RCD disable" + module_name = "RCD Disable" mod_pick_name = "rcd" - description = "Send a specialised pulse to break all RCD devices on the station." - cost = 50 + description = "Send a specialised pulse to break all hand-held and exosuit Rapid Cconstruction Devices on the station." + cost = 25 + one_time = 1 power_type = /mob/living/silicon/ai/proc/disable_rcd /mob/living/silicon/ai/proc/disable_rcd() set category = "Malfunction" set name = "Disable RCDs" - for(var/datum/AI_Module/large/disable_rcd/rcdmod in current_modules) - if(rcdmod.uses > 0) - rcdmod.uses -- - for(var/obj/item/weapon/rcd/rcd in world) - rcd.disabled = 1 - for(var/obj/item/mecha_parts/mecha_equipment/tool/rcd/rcd in world) - rcd.disabled = 1 - src << "Out of uses." + set desc = "Disable all RCD devices on the station, while sparing onboard cyborg RCDs." + + if(stat) + return + + for(var/obj/item/weapon/rcd/RCD in rcd_list) + if(!istype(/obj/item/weapon/rcd/borg, RCD)) //Ensures that cyborg RCDs are spared. + RCD.disabled = 1 + for(var/obj/item/mecha_parts/mecha_equipment/tool/rcd/MRCD in rcd_list) + MRCD.disabled = 1 + src << "RCD-disabling pulse emitted." + +/datum/AI_Module/large/mecha_domination + module_name = "Viral Mech Domination" + mod_pick_name = "mechjack" + description = "Hack into a mech's onboard computer, shunting all processes into it and ejecting any occupants. Once uploaded to the mech, it is impossible to leave.\ + Do not allow the mech to leave the station's vicinity or allow it to be destroyed." + cost = 30 + one_time = 1 + + power_type = /mob/living/silicon/ai/proc/mech_takeover + +/mob/living/silicon/ai/proc/mech_takeover() + set name = "Compile Mecha Virus" + set category = "Malfunction" + set desc = "Target a mech by clicking it. Click the appropriate command when ready." + + if(stat) + return + + can_dominate_mechs = 1 //Yep. This is all it does. Honk! + src << "Virus package compiled. Select a target mech at any time. You must remain on the station at all times. Loss of signal will result in total system lockout." + verbs -= /mob/living/silicon/ai/proc/mech_takeover + /datum/AI_Module/small/overload_machine - module_name = "Machine overload" + module_name = "Machine Overload" mod_pick_name = "overload" description = "Overloads an electrical machine, causing a small explosion. 2 uses." uses = 2 @@ -173,6 +197,10 @@ rcd light flash thingy on matter drain /mob/living/silicon/ai/proc/overload_machine(obj/machinery/M as obj in world) set name = "Overload Machine" set category = "Malfunction" + + if(stat) + return + if (istype(M, /obj/machinery)) if(istype(M,/obj/machinery/field_generator)) src << "This machine can not be overloaded due to a firewall." @@ -189,7 +217,7 @@ rcd light flash thingy on matter drain else src << "That's not a machine." /datum/AI_Module/small/override_machine - module_name = "Machine override" + module_name = "Machine Override" mod_pick_name = "override" description = "Overrides a machine's programming, causing it to rise up and attack everyone except other machines. 4 uses." uses = 4 @@ -201,6 +229,10 @@ rcd light flash thingy on matter drain /mob/living/silicon/ai/proc/override_machine(obj/machinery/M as obj in world) set name = "Override Machine" set category = "Malfunction" + + if(stat) + return + if (istype(M, /obj/machinery)) if(istype(M,/obj/machinery/field_generator)) src << "This machine can not be overloaded due to a firewall." @@ -234,8 +266,10 @@ rcd light flash thingy on matter drain /mob/living/silicon/ai/proc/place_transformer() set name = "Place Robotic Factory" set category = "Malfunction" + if(!canPlaceTransformer()) return + var/sure = alert(src, "Are you sure you want to place the machine here?", "Are you sure?", "Yes", "No") if(sure == "Yes") if(!canPlaceTransformer()) @@ -300,6 +334,10 @@ rcd light flash thingy on matter drain /mob/living/silicon/ai/proc/blackout() set category = "Malfunction" set name = "Blackout" + + if(stat) + return + for(var/datum/AI_Module/small/blackout/blackout in current_modules) if(blackout.uses > 0) blackout.uses -- @@ -310,70 +348,86 @@ rcd light flash thingy on matter drain src << "Overcurrent applied to the powernet." else src << "Out of uses." -/datum/AI_Module/small/reactivate_camera - module_name = "Reactivate camera" +/datum/AI_Module/small/reactivate_cameras + module_name = "Reactivate Camera Network" mod_pick_name = "recam" - description = "Reactivates a currently disabled camera. 5 uses." - uses = 5 - cost = 5 + description = "Runs a network-wide diagnostic on the camera network, resetting focus and re-routing power to failed cameras. Can be used to repair up to 30 cameras." + uses = 30 + cost = 10 + one_time = 1 - power_type = /client/proc/reactivate_camera + power_type = /mob/living/silicon/ai/proc/reactivate_cameras -/client/proc/reactivate_camera(obj/machinery/camera/C as obj in cameranet.cameras) - set name = "Reactivate Camera" +/mob/living/silicon/ai/proc/reactivate_cameras() + set name = "Reactivate Cameranet" set category = "Malfunction" - if (istype (C, /obj/machinery/camera)) - for(var/datum/AI_Module/small/reactivate_camera/camera in usr:current_modules) + + if(!CanUseTopic() || malf_cooldown) + return + var/fixedcams = 0 //Tells the AI how many cams it fixed. Stats are fun. + + for(var/datum/AI_Module/small/reactivate_cameras/camera in current_modules) + for(var/obj/machinery/camera/C in cameranet.cameras) + var/initial_range = initial(C.view_range) //To prevent calling the proc twice if(camera.uses > 0) if(!C.status) - C.deactivate(src) - camera.uses -- - src << "Camera reactivated." - else - src << "This camera is either active, or not repairable." - else src << "Out of uses." - else src << "That's not a camera." - -/datum/AI_Module/small/upgrade_camera - module_name = "Upgrade Camera" - mod_pick_name = "upgradecam" - description = "Upgrades a camera to have X-Ray vision, Motion and be EMP-Proof. 5 uses." - uses = 5 - cost = 5 - - power_type = /client/proc/upgrade_camera - -/client/proc/upgrade_camera(obj/machinery/camera/C as obj in cameranet.cameras) - set name = "Upgrade Camera" - set category = "Malfunction" - if(istype(C)) - var/datum/AI_Module/small/upgrade_camera/UC = locate(/datum/AI_Module/small/upgrade_camera) in usr:current_modules - if(UC) - if(UC.uses > 0) - if(C.assembly) - var/upgraded = 0 - - if(!C.isXRay()) - C.upgradeXRay() - upgraded = 1 - - if(!C.isEmpProof()) - C.upgradeEmpProof() - upgraded = 1 - - if(!C.isMotion()) - C.upgradeMotion() - upgraded = 1 - - if(upgraded) - UC.uses -- - C.visible_message("\icon[C] *beep*") - src << "Camera successully upgraded!" - else - src << "This camera is already upgraded!" + C.deactivate(src, 0) //Reactivates the camera based on status. Badly named proc. + fixedcams++ + camera.uses-- + if(C.view_range != initial_range) + C.view_range = initial_range //Fixes cameras with bad focus. + camera.uses-- + fixedcams++ + //If a camera is both deactivated and has bad focus, it will cost two uses to fully fix! else - src << "Out of uses." + src << "Out of uses." + verbs -= /mob/living/silicon/ai/proc/reactivate_cameras //It is useless now, clean it up. + break + src << "Diagnostic complete! Operations completed: [fixedcams]." + malf_cooldown = 1 + spawn(30) //Lag protection + malf_cooldown = 0 + +/datum/AI_Module/small/upgrade_cameras + module_name = "Upgrade Camera Network" + mod_pick_name = "upgradecam" + description = "Install broad-spectrum scanning and electrical redundancy firmware to the camera network, enabling EMP-Proofing and light-amplified X-ray vision." //I <3 pointless technobabble + //This used to have motion sensing as well, but testing quickly revealed that giving it to the whole cameranet is PURE HORROR. + one_time = 1 + cost = 35 //Decent price for omniscience! + + power_type = /mob/living/silicon/ai/proc/upgrade_cameras + +/mob/living/silicon/ai/proc/upgrade_cameras() + set name = "Upgrade Cameranet" + set category = "Malfunction" + + if(stat) + return + + var/upgradedcams = 0 + see_override = SEE_INVISIBLE_MINIMUM //Night-vision, without which X-ray would be very limited in power. + + for(var/obj/machinery/camera/C in cameranet.cameras) + if(C.assembly) + var/upgraded = 0 + + if(!C.isXRay()) + C.upgradeXRay() + //Update what it can see. + cameranet.updateVisibility(C, 0) + upgraded = 1 + + if(!C.isEmpProof()) + C.upgradeEmpProof() + upgraded = 1 + + if(upgraded) + upgradedcams++ + + src << "OTA firmware distribution complete! Cameras upgraded: [upgradedcams]. Light amplification system online." + verbs -= /mob/living/silicon/ai/proc/upgrade_cameras /datum/module_picker var/temp = null diff --git a/code/game/gamemodes/malfunction/malfunction.dm b/code/game/gamemodes/malfunction/malfunction.dm index fdca56bd5cc..e3fb3809c39 100644 --- a/code/game/gamemodes/malfunction/malfunction.dm +++ b/code/game/gamemodes/malfunction/malfunction.dm @@ -127,7 +127,7 @@ /datum/game_mode/malfunction/proc/capture_the_station() - world << "The AI has won!" + world << "The AI has accessed the station's core files!" world << "It has fully taken control of all of [station_name()]'s systems." to_nuke_or_not_to_nuke = 1 @@ -328,7 +328,8 @@ /datum/game_mode/proc/auto_declare_completion_malfunction() if( malf_ai.len || istype(ticker.mode,/datum/game_mode/malfunction) ) var/text = "The malfunctioning AI were:" - + var/module_text_temp = "
Purchased modules:
" //Added at the end + for(var/datum/mind/malf in malf_ai) text += "
[malf.key] was [malf.name] (" @@ -339,9 +340,13 @@ text += "operational" if(malf.current.real_name != malf.name) text += " as [malf.current.real_name]" + var/mob/living/silicon/ai/AI = malf.current + for(var/datum/AI_Module/mod in AI.current_modules) + module_text_temp += mod.module_name + "
" else text += "hardware destroyed" text += ")" - + text += module_text_temp + world << text return 1 \ No newline at end of file diff --git a/code/game/machinery/computer/ai_core.dm b/code/game/machinery/computer/ai_core.dm index 8aa64e18b6b..76fd8cf7ea5 100644 --- a/code/game/machinery/computer/ai_core.dm +++ b/code/game/machinery/computer/ai_core.dm @@ -233,201 +233,28 @@ This is a good place for AI-related object verbs so I'm sticking it here. If adding stuff to this, don't forget that an AI need to cancel_camera() whenever it physically moves to a different location. That prevents a few funky behaviors. */ -//What operation to perform based on target, what ineraction to perform based on object used, target itself, user. The object used is src and calls this proc. -/obj/item/proc/transfer_ai(var/choice as text, var/interaction as text, var/target, var/mob/U as mob) - if(!src:flush) - switch(choice) - if("AICORE")//AI mob. - var/mob/living/silicon/ai/T = target - switch(interaction) - if("AICARD") - var/obj/item/device/aicard/C = src - if(C.contents.len)//If there is an AI on card. - U << "\red Transfer failed: \black Existing AI found on this terminal. Remove existing AI to install a new one." - else - if (ticker.mode.name == "AI malfunction") - var/datum/game_mode/malfunction/malf = ticker.mode - for (var/datum/mind/malfai in malf.malf_ai) - if (T.mind == malfai) - U << "\red ERROR: \black Remote transfer interface disabled."//Do ho ho ho~ - return - new /obj/structure/AIcore/deactivated(T.loc)//Spawns a deactivated terminal at AI location. - T.aiRestorePowerRoutine = 0//So the AI initially has power. - T.control_disabled = 1//Can't control things remotely if you're stuck in a card! - T.loc = C//Throw AI into the card. - C.name = "inteliCard - [T.name]" - if (T.stat == 2) - C.icon_state = "aicard-404" - else - C.icon_state = "aicard-full" - T.cancel_camera() - T << "You have been downloaded to a mobile storage device. Remote device connection severed." - U << "\blue Transfer successful: \black [T.name] ([rand(1000,9999)].exe) removed from host terminal and stored within local memory." - /*if("NINJASUIT") - var/obj/item/clothing/suit/space/space_ninja/C = src - if(C.AI)//If there is an AI on card. - U << "\red Transfer failed: \black Existing AI found on this terminal. Remove existing AI to install a new one." - else - if (ticker.mode.name == "AI malfunction") - var/datum/game_mode/malfunction/malf = ticker.mode - for (var/datum/mind/malfai in malf.malf_ai) - if (T.mind == malfai) - U << "\red ERROR: \black Remote transfer interface disabled." - return - if(T.stat)//If the ai is dead/dying. - U << "\red ERROR: \black [T.name] data core is corrupted. Unable to install." - else - new /obj/structure/AIcore/deactivated(T.loc) - T.aiRestorePowerRoutine = 0 - T.control_disabled = 1 - T.loc = C - C.AI = T - T.cancel_camera() - T << "You have been downloaded to a mobile storage device. Remote device connection severed." - U << "\blue Transfer successful: \black [T.name] ([rand(1000,9999)].exe) removed from host terminal and stored within local memory."*/ +//The type of interaction, the player performing the operation, the AI itself, and the card object, if any. - if("INACTIVE")//Inactive AI object. - var/obj/structure/AIcore/deactivated/T = target - switch(interaction) - if("AICARD") - var/obj/item/device/aicard/C = src - var/mob/living/silicon/ai/A = locate() in C//I love locate(). Best proc ever. - if(A)//If AI exists on the card. Else nothing since both are empty. - A.control_disabled = 0 - A.loc = T.loc//To replace the terminal. - C.icon_state = "aicard" - C.name = "inteliCard" - C.overlays.Cut() - A.cancel_camera() - A << "You have been uploaded to a stationary terminal. Remote device connection restored." - U << "\blue Transfer successful: \black [A.name] ([rand(1000,9999)].exe) installed and executed succesfully. Local copy has been removed." - qdel(T) - /*if("NINJASUIT") - var/obj/item/clothing/suit/space/space_ninja/C = src - var/mob/living/silicon/ai/A = C.AI - if(A) - A.control_disabled = 0 - C.AI = null - A.loc = T.loc - A.cancel_camera() - A << "You have been uploaded to a stationary terminal. Remote device connection restored." - U << "\blue Transfer succesful: \black [A.name] ([rand(1000,9999)].exe) installed and executed succesfully. Local copy has been removed." - del(T)*/ - if("AIFIXER")//AI Fixer terminal. - var/obj/machinery/computer/aifixer/T = target - switch(interaction) - if("AICARD") - var/obj/item/device/aicard/C = src - if(!T.contents.len) - if (!C.contents.len) - U << "No AI to copy over!"//Well duh - else for(var/mob/living/silicon/ai/A in C) - C.icon_state = "aicard" - C.name = "inteliCard" - C.overlays.Cut() - A.loc = T - T.occupant = A - A.control_disabled = 1 - if (A.stat == 2) - T.overlays += image('icons/obj/computer.dmi', "ai-fixer-404") - else - T.overlays += image('icons/obj/computer.dmi', "ai-fixer-full") - T.overlays -= image('icons/obj/computer.dmi', "ai-fixer-empty") - A.cancel_camera() - A << "You have been uploaded to a stationary terminal. Sadly, there is no remote access from here." - U << "\blue Transfer successful: \black [A.name] ([rand(1000,9999)].exe) installed and executed succesfully. Local copy has been removed." - else - if(!C.contents.len && T.occupant && !T.active) - C.name = "inteliCard - [T.occupant.name]" - T.overlays += image('icons/obj/computer.dmi', "ai-fixer-empty") - if (T.occupant.stat == 2) - C.icon_state = "aicard-404" - T.overlays -= image('icons/obj/computer.dmi', "ai-fixer-404") - else - C.icon_state = "aicard-full" - T.overlays -= image('icons/obj/computer.dmi', "ai-fixer-full") - T.occupant << "You have been downloaded to a mobile storage device. Still no remote access." - U << "\blue Transfer succesful: \black [T.occupant.name] ([rand(1000,9999)].exe) removed from host terminal and stored within local memory." - T.occupant.loc = C - T.occupant.cancel_camera() - T.occupant = null - else if (C.contents.len) - U << "\red ERROR: \black Artificial intelligence detected on terminal." - else if (T.active) - U << "\red ERROR: \black Reconstruction in progress." - else if (!T.occupant) - U << "\red ERROR: \black Unable to locate artificial intelligence." - /*if("NINJASUIT") - var/obj/item/clothing/suit/space/space_ninja/C = src - if(!T.contents.len) - if (!C.AI) - U << "No AI to copy over!" - else - var/mob/living/silicon/ai/A = C.AI - A.loc = T - T.occupant = A - C.AI = null - A.control_disabled = 1 - T.overlays += image('icons/obj/computer.dmi', "ai-fixer-full") - T.overlays -= image('icons/obj/computer.dmi', "ai-fixer-empty") - A.cancel_camera() - A << "You have been uploaded to a stationary terminal. Sadly, there is no remote access from here." - U << "\blue Transfer successful: \black [A.name] ([rand(1000,9999)].exe) installed and executed succesfully. Local copy has been removed." - else - if(!C.AI && T.occupant && !T.active) - if (T.occupant.stat) - U << "\red ERROR: \black [T.occupant.name] data core is corrupted. Unable to install." - else - T.overlays += image('icons/obj/computer.dmi', "ai-fixer-empty") - T.overlays -= image('icons/obj/computer.dmi', "ai-fixer-full") - T.occupant << "You have been downloaded to a mobile storage device. Still no remote access." - U << "\blue Transfer successful: \black [T.occupant.name] ([rand(1000,9999)].exe) removed from host terminal and stored within local memory." - T.occupant.loc = C - T.occupant.cancel_camera() - T.occupant = null - else if (C.AI) - U << "\red ERROR: \black Artificial intelligence detected on terminal." - else if (T.active) - U << "\red ERROR: \black Reconstruction in progress." - else if (!T.occupant) - U << "\red ERROR: \black Unable to locate artificial intelligence."*/ - /*if("NINJASUIT")//Ninjasuit - var/obj/item/clothing/suit/space/space_ninja/T = target - switch(interaction) - if("AICARD") - var/obj/item/device/aicard/C = src - if(T.s_initialized&&U==T.affecting)//If the suit is initialized and the actor is the user. - var/mob/living/silicon/ai/A_T = locate() in C//Determine if there is an AI on target card. Saves time when checking later. - var/mob/living/silicon/ai/A = T.AI//Deterine if there is an AI in suit. +atom/proc/transfer_ai(var/interaction, var/mob/user, var/mob/living/silicon/ai/AI, var/obj/item/device/aicard/card) + if(istype(card)) + if(card.flush) + user << "ERROR: AI flush is in progress, cannot execute transfer protocol." + return 0 + return 1 - if(A)//If the host AI card is not empty. - if(A_T)//If there is an AI on the target card. - U << "\red ERROR: \black [A_T.name] already installed. Remove [A_T.name] to install a new one." - else - A.loc = C//Throw them into the target card. Since they are already on a card, transfer is easy. - C.name = "inteliCard - [A.name]" - C.icon_state = "aicard-full" - T.AI = null - A.cancel_camera() - A << "You have been uploaded to a mobile storage device." - U << "\blue SUCCESS: \black [A.name] ([rand(1000,9999)].exe) removed from host and stored within local memory." - else//If host AI is empty. - if(C.flush)//If the other card is flushing. - U << "\red ERROR: \black AI flush is in progress, cannot execute transfer protocol." - else - if(A_T&&!A_T.stat)//If there is an AI on the target card and it's not inactive. - A_T.loc = T//Throw them into suit. - C.icon_state = "aicard" - C.name = "inteliCard" - C.overlays.Cut() - T.AI = A_T - A_T.cancel_camera() - A_T << "You have been uploaded to a mobile storage device." - U << "\blue SUCCESS: \black [A_T.name] ([rand(1000,9999)].exe) removed from local memory and installed to host." - else if(A_T)//If the target AI is dead. Else just go to return since nothing would happen if both are empty. - U << "\red ERROR: \black [A_T.name] data core is corrupted. Unable to install."*/ - else - U << "\red ERROR: \black AI flush is in progress, cannot execute transfer protocol." - return \ No newline at end of file +/obj/structure/AIcore/deactivated/transfer_ai(var/interaction, var/mob/user, var/mob/living/silicon/ai/AI, var/obj/item/device/aicard/card) + if(!..()) + return + //Transferring a carded AI to a core. + if(interaction == AI_TRANS_FROM_CARD) + AI.control_disabled = 0 + AI.aiRadio.disabledAi = 0 + AI.loc = loc//To replace the terminal. + AI << "You have been uploaded to a stationary terminal. Remote device connection restored." + user << "Transfer successful: [AI.name] ([rand(1000,9999)].exe) installed and executed successfully. Local copy has been removed." + qdel(src) + else //If for some reason you use an empty card on an empty AI terminal. + user << "There is no AI loaded on this terminal!" + \ No newline at end of file diff --git a/code/game/machinery/computer/aifixer.dm b/code/game/machinery/computer/aifixer.dm index d291c65ac78..20d715a3a0e 100644 --- a/code/game/machinery/computer/aifixer.dm +++ b/code/game/machinery/computer/aifixer.dm @@ -10,28 +10,26 @@ light_color = LIGHT_COLOR_PURPLE -/obj/machinery/computer/aifixer/New() - src.overlays += image('icons/obj/computer.dmi', "ai-fixer-empty") - - /obj/machinery/computer/aifixer/attackby(I as obj, user as mob, params) - if(istype(I, /obj/item/device/aicard)) + if(occupant && istype(I, /obj/item/weapon/screwdriver)) if(stat & (NOPOWER|BROKEN)) - user << "This terminal isn't functioning right now, get it working!" - return - I:transfer_ai("AIFIXER","AICARD",src,user) - - ..() - return - + user << "The screws on [name]'s screen won't budge." + else + user << "The screws on [name]'s screen won't budge and it emits a warning beep." + return + else + ..() + /obj/machinery/computer/aifixer/attack_ai(var/mob/user as mob) - return attack_hand(user) + return interact(user) /obj/machinery/computer/aifixer/attack_hand(var/mob/user as mob) if(..()) return + + interact(user) - user.set_machine(src) +/obj/machinery/computer/aifixer/interact(mob/user) var/dat = "

AI System Integrity Restorer



" if (src.occupant) @@ -70,17 +68,11 @@ onclose(user, "computer") return -/obj/machinery/computer/aifixer/process() - if(..()) - src.updateDialog() - return - /obj/machinery/computer/aifixer/Topic(href, href_list) if(..()) return 1 if (href_list["fix"]) src.active = 1 - src.overlays += image('icons/obj/computer.dmi', "ai-fixer-on") while (src.occupant.health < 100) src.occupant.adjustOxyLoss(-1) src.occupant.adjustFireLoss(-1) @@ -92,32 +84,56 @@ src.occupant.lying = 0 dead_mob_list -= src.occupant living_mob_list += src.occupant - src.overlays -= image('icons/obj/computer.dmi', "ai-fixer-404") - src.overlays += image('icons/obj/computer.dmi', "ai-fixer-full") src.updateUsrDialog() sleep(10) src.active = 0 - src.overlays -= image('icons/obj/computer.dmi', "ai-fixer-on") - - src.add_fingerprint(usr) src.updateUsrDialog() + update_icon() return - /obj/machinery/computer/aifixer/update_icon() ..() - // Broken / Unpowered - if((stat & BROKEN) || (stat & NOPOWER)) - overlays.Cut() - - // Working / Powered + if(stat & (NOPOWER|BROKEN)) + return else + var/overlay_layer = LIGHTING_LAYER+0.2 // +0.1 from the default computer overlays + if(active) + overlays += image(icon,"ai-fixer-on",overlay_layer) if (occupant) switch (occupant.stat) if (0) - overlays += image('icons/obj/computer.dmi', "ai-fixer-full") + overlays += image(icon,"ai-fixer-full",overlay_layer) if (2) - overlays += image('icons/obj/computer.dmi', "ai-fixer-404") + overlays += image(icon,"ai-fixer-404",overlay_layer) else - overlays += image('icons/obj/computer.dmi', "ai-fixer-empty") + overlays += image(icon,"ai-fixer-empty",overlay_layer) + +/obj/machinery/computer/aifixer/transfer_ai(var/interaction, var/mob/user, var/mob/living/silicon/ai/AI, var/obj/item/device/aicard/card) + if(!..()) + return + //Downloading AI from card to terminal. + if(interaction == AI_TRANS_FROM_CARD) + if(stat & (NOPOWER|BROKEN)) + user << "[src] is offline and cannot take an AI at this time!" + return + AI.loc = src + occupant = AI + AI.control_disabled = 1 + AI.aiRadio.disabledAi = 1 + AI << "You have been uploaded to a stationary terminal. Sadly, there is no remote access from here." + user << "Transfer successful: [AI.name] ([rand(1000,9999)].exe) installed and executed successfully. Local copy has been removed." + update_icon() + + else //Uploading AI from terminal to card + if(occupant && !active) + occupant << "You have been downloaded to a mobile storage device. Still no remote access." + user << "Transfer successful: [occupant.name] ([rand(1000,9999)].exe) removed from host terminal and stored within local memory." + occupant.loc = card + occupant = null + update_icon() + else if (active) + user << "ERROR: Reconstruction in progress." + else if (!occupant) + user << "ERROR: Unable to locate artificial intelligence." + \ No newline at end of file diff --git a/code/game/mecha/equipment/tools/tools.dm b/code/game/mecha/equipment/tools/tools.dm index 90db7efaec9..7fa4e5668cb 100644 --- a/code/game/mecha/equipment/tools/tools.dm +++ b/code/game/mecha/equipment/tools/tools.dm @@ -284,12 +284,18 @@ var/mode = 0 //0 - deconstruct, 1 - wall or floor, 2 - airlock. var/disabled = 0 //malf var/canRwall = 0 + + New() + rcd_list += src + ..() + + Destroy() + rcd_list -= src + ..() action(atom/target) - if(istype(target,/area/shuttle)||istype(target, /turf/space/transit))//>implying these are ever made -Sieve - disabled = 1 - else - disabled = 0 + if(istype(target, /turf/space/transit))//>implying these are ever made -Sieve + return if(!istype(target, /turf) && !istype(target, /obj/machinery/door/airlock)) target = get_turf(target) if(!action_checks(target) || disabled || get_dist(chassis, target)>3) return diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index 27fe49e746d..b812cab95bd 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -96,11 +96,6 @@ mechas_list += src //global mech list return -/obj/mecha/Destroy() - src.go_out() - mechas_list -= src //global mech list - return ..() - //////////////////////// ////// Helpers ///////// //////////////////////// @@ -615,50 +610,65 @@ return /obj/mecha/proc/destroy() - spawn() - go_out() - var/turf/T = get_turf(src) - tag = "\ref[src]" //better safe then sorry - if(loc) - loc.Exited(src) - loc = null - if(T) - if(istype(src, /obj/mecha/working/ripley/)) - var/obj/mecha/working/ripley/R = src - if(R.cargo) - for(var/obj/O in R.cargo) //Dump contents of stored cargo - O.loc = T - R.cargo -= O - T.Entered(O) + go_out() + for(var/mob/M in src) //Let's just be ultra sure + if(isAI(M)) + M.gib() //AIs are loaded into the mech computer itself. When the mech dies, so does the AI. Forever. + else + M.Move(loc) + + if(prob(30)) + explosion(get_turf(loc), 0, 0, 1, 3) + + if(istype(src, /obj/mecha/working/ripley/)) + var/obj/mecha/working/ripley/R = src + if(R.cargo) + for(var/obj/O in R.cargo) //Dump contents of stored cargo + O.loc = loc + R.cargo -= O + loc.Entered(O) - if(prob(30)) - explosion(get_turf(loc), 0, 0, 1, 3) - spawn(0) - if(wreckage) - var/obj/effect/decal/mecha_wreckage/WR = new wreckage(T) - for(var/obj/item/mecha_parts/mecha_equipment/E in equipment) - if(E.salvageable && prob(30)) - WR.crowbar_salvage += E - E.forceMove(WR) - E.equip_ready = 1 - E.reliability = round(rand(E.reliability/3,E.reliability)) - else - E.forceMove(T) - E.destroy() - if(cell) - WR.crowbar_salvage += cell - cell.forceMove(WR) - cell.charge = rand(0, cell.charge) - if(internal_tank) - WR.crowbar_salvage += internal_tank - internal_tank.forceMove(WR) - else - for(var/obj/item/mecha_parts/mecha_equipment/E in equipment) - E.forceMove(T) - E.destroy() - spawn(0) - qdel(src) - return + if(wreckage) + var/obj/effect/decal/mecha_wreckage/WR = new wreckage(loc) + for(var/obj/item/mecha_parts/mecha_equipment/E in equipment) + if(E.salvageable && prob(30)) + WR.crowbar_salvage += E + E.forceMove(WR) + E.equip_ready = 1 + E.reliability = round(rand(E.reliability/3,E.reliability)) + else + E.forceMove(loc) + E.destroy() + if(cell) + WR.crowbar_salvage += cell + cell.forceMove(WR) + cell.charge = rand(0, cell.charge) + if(internal_tank) + WR.crowbar_salvage += internal_tank + internal_tank.forceMove(WR) + else + for(var/obj/item/mecha_parts/mecha_equipment/E in equipment) + E.forceMove(loc) + E.destroy() + if(cell) + qdel(cell) + if(internal_tank) + qdel(internal_tank) + equipment.Cut() + cell = null + internal_tank = null + + qdel(pr_int_temp_processor) + qdel(pr_give_air) + qdel(pr_internal_damage) + qdel(spark_system) + pr_int_temp_processor = null + pr_give_air = null + pr_internal_damage = null + spark_system = null + + mechas_list -= src //global mech list + return ..() /obj/mecha/ex_act(severity) src.log_message("Affected by explosion of severity: [severity].",1) @@ -916,16 +926,92 @@ return -/* +///////////////////////////////////// +//////////// AI piloting //////////// +///////////////////////////////////// + /obj/mecha/attack_ai(var/mob/living/silicon/ai/user as mob) - if(!istype(user, /mob/living/silicon/ai)) + if(!isAI(user)) return - var/output = {"Assume direct control over [src]? - Yes
- "} - user << browse(output, "window=mecha_attack_ai") - 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. + user << "Warning: Tracking Beacon detected. Enter at your own risk. Beacon Data:" + user << "[B.get_mecha_info()]" + //Nothing like a big, red link to make the player feel powerful! + user << "ASSUME DIRECT CONTROL?
" + +/obj/mecha/transfer_ai(var/interaction, mob/user, var/mob/living/silicon/ai/AI, var/obj/item/device/aicard/card) + if(!..()) + return + + //Transfer from core or card to mech. Proc is called by mech. + switch(interaction) + if(AI_TRANS_TO_CARD) //Upload AI from mech to AI card. + if(!maint_access) //Mech must be in maint mode to allow carding. + user << "[name] must have maintenance protocols active in order to allow a transfer." + return + AI = occupant + if(!AI || !isAI(occupant)) //Mech does not have an AI for a pilot + user << "No AI detected in the [name] onboard computer." + return + if (AI.mind.special_role == "malfunction") //Malf AIs cannot leave mechs. Except through death. + user << "ACCESS DENIED." + return + AI.aiRestorePowerRoutine = 0//So the AI initially has power. + AI.control_disabled = 1 + AI.aiRadio.disabledAi = 1 + AI.loc = card + occupant = null + AI.controlled_mech = null + AI.remote_control = null + icon_state = initial(icon_state)+"-open" + AI << "You have been downloaded to a mobile storage device. Wireless connection offline." + 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? + AI << "Pilot detected! Forced ejection initiated!" + 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. + AI = locate(/mob/living/silicon/ai) in card + if(!AI) + user << "There is no AI currently installed on this device." + return + else if(AI.stat || !AI.client) + user << "[AI.name] is currently unresponsive, and cannot be uploaded." + return + else if(occupant || dna) //Normal AIs cannot steal mechs! + user << "Access denied. [name] is [occupant ? "currently occupied" : "secured with a DNA lock"]." + return + AI.control_disabled = 0 + AI.aiRadio.disabledAi = 0 + user << "Transfer successful: [AI.name] ([rand(1000,9999)].exe) installed and executed successfully. Local copy has been removed." + 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) + AI.aiRestorePowerRoutine = 0 + AI.loc = src + occupant = AI + icon_state = initial(icon_state) + playsound(src, 'sound/machines/windowdoor.ogg', 50, 1) + if(!hasInternalDamage()) + occupant << sound('sound/mecha/nominal.ogg',volume=50) + AI.cancel_camera() + AI.controlled_mech = src + 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. + 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!" \ + : "You have been uploaded to a mech's onboard computer."]" + AI << "Use Middle-Mouse to activate mech functions and equipment. Click normally for AI interactions." ///////////////////////////////////// //////// Atmospheric stuff //////// @@ -1228,7 +1314,7 @@ return -/obj/mecha/proc/go_out() +/obj/mecha/proc/go_out(var/forced) if(!src.occupant) return var/atom/movable/mob_container if(ishuman(occupant)) @@ -1236,6 +1322,10 @@ else if(istype(occupant, /mob/living/carbon/brain)) var/mob/living/carbon/brain/brain = occupant 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. + 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 return if(mob_container.forceMove(src.loc))//ejecting mob container @@ -1677,13 +1767,13 @@ user << browse(null,"window=exosuit_add_access") return if(href_list["dna_lock"]) - if(usr != src.occupant) return - if(istype(occupant, /mob/living/carbon/brain)) - occupant_message("You are a brain. No.") + if(usr != src.occupant) return - if(src.occupant) - src.dna = src.occupant.dna.unique_enzymes - src.occupant_message("You feel a prick as the needle takes your DNA sample.") + if(src.occupant && !iscarbon(src.occupant)) + src.occupant << "You do not have any DNA!" + return + src.dna = src.occupant.dna.unique_enzymes + src.occupant_message("You feel a prick as the needle takes your DNA sample.") return if(href_list["reset_dna"]) if(usr != src.occupant) return @@ -1712,54 +1802,6 @@ return */ - - -/* - - if (href_list["ai_take_control"]) - var/mob/living/silicon/ai/AI = locate(href_list["ai_take_control"]) - var/duration = text2num(href_list["duration"]) - var/mob/living/silicon/ai/O = new /mob/living/silicon/ai(src) - var/cur_occupant = src.occupant - O.invisibility = 0 - O.canmove = 1 - O.name = AI.name - O.real_name = AI.real_name - O.anchored = 1 - O.aiRestorePowerRoutine = 0 - O.control_disabled = 1 // Can't control things remotely if you're stuck in a card! - O.laws = AI.laws - O.stat = AI.stat - O.oxyloss = AI.getOxyLoss() - O.fireloss = AI.getFireLoss() - O.bruteloss = AI.getBruteLoss() - O.toxloss = AI.toxloss - O.updatehealth() - src.occupant = O - if(AI.mind) - AI.mind.transfer_to(O) - AI.name = "Inactive AI" - AI.real_name = "Inactive AI" - AI.icon_state = "ai-empty" - spawn(duration) - AI.name = O.name - AI.real_name = O.real_name - if(O.mind) - O.mind.transfer_to(AI) - AI.control_disabled = 0 - AI.laws = O.laws - AI.oxyloss = O.getOxyLoss() - AI.fireloss = O.getFireLoss() - AI.bruteloss = O.getBruteLoss() - AI.toxloss = O.toxloss - AI.updatehealth() - del(O) - if (!AI.stat) - AI.icon_state = "ai" - else - AI.icon_state = "ai-crash" - src.occupant = cur_occupant -*/ return /////////////////////// diff --git a/code/game/objects/items/devices/aicard.dm b/code/game/objects/items/devices/aicard.dm index ff16cf9e43c..e2c0ddf1df0 100644 --- a/code/game/objects/items/devices/aicard.dm +++ b/code/game/objects/items/devices/aicard.dm @@ -5,129 +5,138 @@ item_state = "electronic" w_class = 2.0 slot_flags = SLOT_BELT + flags = NOBLUDGEON var/flush = null origin_tech = "programming=4;materials=4" - attack(mob/living/silicon/ai/M as mob, mob/user as mob) - if(!istype(M, /mob/living/silicon/ai))//If target is not an AI. - return ..() - - M.attack_log += text("\[[time_stamp()]\] Has been carded with [src.name] by [user.name] ([user.ckey])") - user.attack_log += text("\[[time_stamp()]\] Used the [src.name] to card [M.name] ([M.ckey])") - msg_admin_attack("[user.name] ([user.ckey])[isAntag(user) ? "(ANTAG)" : ""] used the [src.name] to card [M.name] ([M.ckey]) (JMP)") - - transfer_ai("AICORE", "AICARD", M, user) +/obj/item/device/aicard/afterattack(atom/target, mob/user, proximity) + ..() + if(!proximity || !target) return + var/mob/living/silicon/ai/AI = locate(/mob/living/silicon/ai) in src + if(AI) //AI is on the card, implies user wants to upload it. + target.transfer_ai(AI_TRANS_FROM_CARD, user, AI, src) + add_logs(AI,user, "carded", object="[name]") + else //No AI on the card, therefore the user wants to download one. + target.transfer_ai(AI_TRANS_TO_CARD, user, null, src) + update_state() //Whatever happened, update the card's state (icon, name) to match. - attack(mob/living/silicon/decoy/M as mob, mob/user as mob) - if (!istype (M, /mob/living/silicon/decoy)) - return ..() +/obj/item/device/aicard/proc/update_state() + var/mob/living/silicon/ai/AI = locate(/mob/living/silicon/ai) in src //AI is inside. + if(AI) + name = "intelliCard - [AI.name]" + if (AI.stat == DEAD) + icon_state = "aicard-404" else - M.death() - user << "ERROR ERROR ERROR" + icon_state = "aicard-full" + AI.cancel_camera() //AI are forced to move when transferred, so do this whenver one is downloaded. + else + icon_state = "aicard" + name = "intelliCard" + overlays.Cut() + +/obj/item/device/aicard/attack_self(mob/user) + if (!in_range(src, user)) + return + user.set_machine(src) + var/dat = "Intelicard
" + var/laws + for(var/mob/living/silicon/ai/A in src) + dat += "Stored AI: [A.name]
System integrity: [(A.health+100)/2]%
" - attack_self(mob/user) - if (!in_range(src, user)) - return - user.set_machine(src) - var/dat = "Intelicard
" - var/laws - for(var/mob/living/silicon/ai/A in src) - dat += "Stored AI: [A.name]
System integrity: [(A.health+100)/2]%
" + for (var/index = 1, index <= A.laws.ion.len, index++) + var/law = A.laws.ion[index] + if (length(law) > 0) + var/num = ionnum() + laws += "[num]. [law]" - for (var/index = 1, index <= A.laws.ion.len, index++) - var/law = A.laws.ion[index] - if (length(law) > 0) - var/num = ionnum() - laws += "[num]. [law]" + if (A.laws.zeroth) + laws += "0: [A.laws.zeroth]
" - if (A.laws.zeroth) - laws += "0: [A.laws.zeroth]
" + var/number = 1 + for (var/index = 1, index <= A.laws.inherent.len, index++) + var/law = A.laws.inherent[index] + if (length(law) > 0) + laws += "[number]: [law]
" + number++ - var/number = 1 - for (var/index = 1, index <= A.laws.inherent.len, index++) - var/law = A.laws.inherent[index] - if (length(law) > 0) - laws += "[number]: [law]
" - number++ + for (var/index = 1, index <= A.laws.supplied.len, index++) + var/law = A.laws.supplied[index] + if (length(law) > 0) + laws += "[number]: [law]
" + number++ - for (var/index = 1, index <= A.laws.supplied.len, index++) - var/law = A.laws.supplied[index] - if (length(law) > 0) - laws += "[number]: [law]
" - number++ + dat += "Laws:
[laws]
" - dat += "Laws:
[laws]
" - - if (A.stat == 2) - dat += "AI nonfunctional" + if (A.stat == 2) + dat += "AI nonfunctional" + else + if (!src.flush) + dat += {"Wipe AI"} else - if (!src.flush) - dat += {"Wipe AI"} - else - dat += "Wipe in progress" - dat += "
" - dat += {"[A.control_disabled ? "Enable" : "Disable"] Wireless Activity"} - dat += "
" - dat += "Subspace Transceiver is: [A.aiRadio.disabledAi ? "Disabled" : "Enabled"]" - dat += "
" - dat += {"[A.aiRadio.disabledAi ? "Enable" : "Disable"] Subspace Transceiver"} - dat += "
" - dat += {" Close"} - user << browse(dat, "window=aicard") - onclose(user, "aicard") + dat += "Wipe in progress" + dat += "
" + dat += {"[A.control_disabled ? "Enable" : "Disable"] Wireless Activity"} + dat += "
" + dat += "Subspace Transceiver is: [A.aiRadio.disabledAi ? "Disabled" : "Enabled"]" + dat += "
" + dat += {"[A.aiRadio.disabledAi ? "Enable" : "Disable"] Subspace Transceiver"} + dat += "
" + dat += {" Close"} + user << browse(dat, "window=aicard") + onclose(user, "aicard") + return + +/obj/item/device/aicard/Topic(href, href_list) + var/mob/U = usr + if (!in_range(src, U)||U.machine!=src)//If they are not in range of 1 or less or their machine is not the card (ie, clicked on something else). + U << browse(null, "window=aicard") + U.unset_machine() return - Topic(href, href_list) - var/mob/U = usr - if (!in_range(src, U)||U.machine!=src)//If they are not in range of 1 or less or their machine is not the card (ie, clicked on something else). + add_fingerprint(U) + U.set_machine(src) + + switch(href_list["choice"])//Now we switch based on choice. + if ("Close") U << browse(null, "window=aicard") U.unset_machine() return - add_fingerprint(U) - U.set_machine(src) + if ("Wipe") + var/confirm = alert("Are you sure you want to wipe this card's memory? This cannot be undone once started.", "Confirm Wipe", "Yes", "No") + if(confirm == "Yes") + if(isnull(src)||!in_range(src, U)||U.machine!=src) + U << browse(null, "window=aicard") + U.unset_machine() + return + else + flush = 1 + for(var/mob/living/silicon/ai/A in src) + A.suiciding = 1 + A << "Your core files are being wiped!" + while (A.stat != 2) + A.adjustOxyLoss(2) + A.updatehealth() + sleep(10) + flush = 0 - switch(href_list["choice"])//Now we switch based on choice. - if ("Close") - U << browse(null, "window=aicard") - U.unset_machine() - return + if ("Radio") + for(var/mob/living/silicon/ai/A in src) + A.aiRadio.disabledAi = !A.aiRadio.disabledAi + A << "Your Subspace Transceiver has been: [A.aiRadio.disabledAi ? "disabled" : "enabled"]" + U << "You [A.aiRadio.disabledAi ? "Disable" : "Enable"] the AI's Subspace Transceiver" - if ("Wipe") - var/confirm = alert("Are you sure you want to wipe this card's memory? This cannot be undone once started.", "Confirm Wipe", "Yes", "No") - if(confirm == "Yes") - if(isnull(src)||!in_range(src, U)||U.machine!=src) - U << browse(null, "window=aicard") - U.unset_machine() - return - else - flush = 1 - for(var/mob/living/silicon/ai/A in src) - A.suiciding = 1 - A << "Your core files are being wiped!" - while (A.stat != 2) - A.adjustOxyLoss(2) - A.updatehealth() - sleep(10) - flush = 0 - - if ("Radio") - for(var/mob/living/silicon/ai/A in src) - A.aiRadio.disabledAi = !A.aiRadio.disabledAi - A << "Your Subspace Transceiver has been: [A.aiRadio.disabledAi ? "disabled" : "enabled"]" - U << "You [A.aiRadio.disabledAi ? "Disable" : "Enable"] the AI's Subspace Transceiver" - - if ("Wireless") - for(var/mob/living/silicon/ai/A in src) - A.control_disabled = !A.control_disabled - A << "The intelicard's wireless port has been [A.control_disabled ? "disabled" : "enabled"]!" - if (A.control_disabled) - overlays -= image('icons/obj/aicards.dmi', "aicard-on") - else - overlays += image('icons/obj/aicards.dmi', "aicard-on") - attack_self(U) + if ("Wireless") + for(var/mob/living/silicon/ai/A in src) + A.control_disabled = !A.control_disabled + A << "The intelicard's wireless port has been [A.control_disabled ? "disabled" : "enabled"]!" + if (A.control_disabled) + overlays -= image('icons/obj/aicards.dmi', "aicard-on") + else + overlays += image('icons/obj/aicards.dmi', "aicard-on") + attack_self(U) /obj/item/device/aicard/ex_act(severity) switch(severity) diff --git a/code/game/objects/items/weapons/RCD.dm b/code/game/objects/items/weapons/RCD.dm index 24788c42a0e..3330a3831cf 100644 --- a/code/game/objects/items/weapons/RCD.dm +++ b/code/game/objects/items/weapons/RCD.dm @@ -34,8 +34,14 @@ RCD src.spark_system = new /datum/effect/effect/system/spark_spread spark_system.set_up(5, 0, src) spark_system.attach(src) + rcd_list += src return - + + Destroy() + qdel(spark_system) + spark_system = null + rcd_list -= src + return ..() attackby(obj/item/weapon/W, mob/user, params) ..() diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 8fe44113b27..54cfb80de05 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -55,6 +55,7 @@ var/list/ai_verbs_default = list( var/ioncheck[1] var/lawchannel = "Common" // Default channel on which to state laws var/icon/holo_icon//Default is assigned when AI is created. + var/obj/mecha/controlled_mech //For controlled_mech a mech, to determine whether to relaymove or use the AI eye. var/obj/item/device/pda/ai/aiPDA = null var/obj/item/device/multitool/aiMulti = null var/custom_sprite = 0 //For our custom sprites @@ -66,9 +67,10 @@ var/list/ai_verbs_default = list( var/processing_time = 100 var/list/datum/AI_Module/current_modules = list() var/fire_res_on_core = 0 - + var/can_dominate_mechs = 0 var/control_disabled = 0 // Set to 1 to stop AI from interacting via Click() -- TLE var/malfhacking = 0 // More or less a copy of the above var, so that malf AIs can hack and still get new cyborgs -- NeoFite + var/malf_cooldown = 0 //Cooldown var for malf modules var/obj/machinery/power/apc/malfhack = null var/explosive = 0 //does the AI explode when it dies? @@ -533,6 +535,14 @@ var/list/ai_verbs_default = list( botcall() return + if (href_list["ai_take_control"]) //Mech domination + var/obj/mecha/M = locate(href_list["ai_take_control"]) + if(controlled_mech) + src << "You are already loaded into an onboard computer!" + return + if(M) + M.transfer_ai(AI_MECH_HACK,src, usr) //Called om the mech itself. + else if (href_list["faketrack"]) var/mob/target = locate(href_list["track"]) in mob_list var/mob/living/silicon/ai/A = locate(href_list["track2"]) in mob_list @@ -980,6 +990,24 @@ var/list/ai_verbs_default = list( /mob/living/silicon/ai/proc/is_in_chassis() return istype(loc, /turf) + +/mob/living/silicon/ai/transfer_ai(var/interaction, var/mob/user, var/mob/living/silicon/ai/AI, var/obj/item/device/aicard/card) + if(!..()) + return + if(interaction == AI_TRANS_TO_CARD)//The only possible interaction. Upload AI mob to a card. + if(!mind) + user << "No intelligence patterns detected." //No more magical carding of empty cores, AI RETURN TO BODY!!!11 + return + if (mind.special_role == "malfunction") //AI MALF!! + user << "ERROR: Remote transfer interface disabled."//Do ho ho ho~ + return + new /obj/structure/AIcore/deactivated(loc)//Spawns a deactivated terminal at AI location. + aiRestorePowerRoutine = 0//So the AI initially has power. + control_disabled = 1//Can't control things remotely if you're stuck in a card! + aiRadio.disabledAi = 1 //No talking on the built-in radio for you either! + loc = card//Throw AI into the card. + src << "You have been downloaded to a mobile storage device. Remote device connection severed." + user << "Transfer successful: [name] ([rand(1000,9999)].exe) removed from host terminal and stored within local memory." #undef AI_CHECK_WIRELESS #undef AI_CHECK_RADIO diff --git a/code/modules/mob/living/silicon/ai/life.dm b/code/modules/mob/living/silicon/ai/life.dm index 5be1da223a9..b8bcfa4ead3 100644 --- a/code/modules/mob/living/silicon/ai/life.dm +++ b/code/modules/mob/living/silicon/ai/life.dm @@ -44,7 +44,7 @@ loc = T.loc if (istype(loc, /area)) //stage = 4 - if (!loc.power_equip && !istype(src.loc,/obj/item)) + if (!loc.power_equip && !is_type_in_list(src.loc,list(/obj/item, /obj/mecha))) //stage = 5 blind = 1 @@ -97,7 +97,7 @@ src.see_in_dark = 0 src.see_invisible = SEE_INVISIBLE_LIVING - if (((!loc.power_equip) || istype(T, /turf/space)) && !istype(src.loc,/obj/item)) + if (((!loc.power_equip) || istype(T, /turf/space)) && !is_type_in_list(src.loc,list(/obj/item, /obj/mecha))) if (src:aiRestorePowerRoutine==0) src:aiRestorePowerRoutine = 1 diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index df5727e4315..1201c95b74c 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -191,8 +191,6 @@ if(world.time < move_delay) return - if(isAI(mob)) return AIMove(n,direct,mob) - if(!isliving(mob)) return mob.Move(n,direct) if(moving) return 0 @@ -211,10 +209,6 @@ var/mob/spirit/currentSpirit = mob return currentSpirit.Spirit_Move(direct) - // handle possible AI movement - if(isAI(mob)) - return AIMove(n,direct,mob) - if(mob.notransform) return//This is sota the goto stop mobs from moving var if(isliving(mob)) @@ -235,6 +229,9 @@ if(mob.remote_control) //we're controlling something, our movement is relayed to it return mob.remote_control.relaymove(mob, direct) + if(isAI(mob)) + return AIMove(n,direct,mob) + if(!mob.canmove) return