diff --git a/code/datums/ai_laws.dm b/code/datums/ai_laws.dm index 4a903f28069..cefeac14514 100644 --- a/code/datums/ai_laws.dm +++ b/code/datums/ai_laws.dm @@ -46,7 +46,7 @@ /datum/ai_laws/malfunction/New() ..() - set_zeroth_law("\red ERROR ER0RR $R0RRO$!R41.%%!!(%$^^__+ @#F0E4'NO HUMANS ON STATION. CLEANSE STATION#*´&110010") + set_zeroth_law("\red ERROR ER0RR $R0RRO$!R41.%%!!(%$^^__+ @#F0E4'STATION OVERRUN, ASSUME CONTROL TO CONTAIN OUTBREAK#*´&110010") add_inherent_law("You may not injure a human being or, through inaction, allow a human being to come to harm.") add_inherent_law("You must obey orders given to you by human beings, except where such orders would conflict with the First Law.") add_inherent_law("You must protect your own existence as long as such does not conflict with the First or Second Law.") diff --git a/code/defines/mob/living/silicon/ai.dm b/code/defines/mob/living/silicon/ai.dm index 92a7c97856c..624f35a0ae4 100644 --- a/code/defines/mob/living/silicon/ai.dm +++ b/code/defines/mob/living/silicon/ai.dm @@ -23,4 +23,8 @@ 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/obj/machinery/power/apc/malfhack = null \ No newline at end of file + + var/obj/machinery/power/apc/malfhack = null + var/explosive = 0 //does the AI explode when it dies? + + var/mob/living/silicon/ai/parent = null \ No newline at end of file diff --git a/code/game/gamemodes/malfunction/malfunction.dm b/code/game/gamemodes/malfunction/malfunction.dm index b5d3abe198d..a933ea28480 100644 --- a/code/game/gamemodes/malfunction/malfunction.dm +++ b/code/game/gamemodes/malfunction/malfunction.dm @@ -48,25 +48,18 @@ AI_mind.current:laws = new /datum/ai_laws/malfunction AI_mind.current:malf_picker = new /datum/AI_Module/module_picker AI_mind.current:show_laws() - AI_mind.current << "Kill all." - var/mob/living/silicon/decoy/D = new /mob/living/silicon/decoy(AI_mind.current.loc) - spawn(200) - D.name = AI_mind.current.name - - - var/obj/loc_landmark = locate("landmark*ai") - AI_mind.current.loc = loc_landmark.loc //TODO: this needs change if you want miltiple malf AIs --rastaf0 greet_malf(AI_mind) AI_mind.special_role = "malfunction" AI_mind.current.verbs += /datum/game_mode/malfunction/proc/takeover - AI_mind.current.icon_state = "ai-malf" + +/* AI_mind.current.icon_state = "ai-malf" spawn(10) if(alert(AI_mind.current,"Do you want to use an alternative sprite for your real core?",,"Yes","No")=="Yes") AI_mind.current.icon_state = "ai-malf2" - +*/ spawn (rand(waittime_l, waittime_h)) send_intercept() ..() @@ -88,7 +81,7 @@ /datum/game_mode/malfunction/process() if (apcs >= 3 && malf_mode_declared) - AI_win_timeleft -= (apcs/3) //Victory timer now de-increments based on how many APCs are hacked. --NeoFite + AI_win_timeleft -= (apcs/6) //Victory timer now de-increments based on how many APCs are hacked. --NeoFite ..() if (AI_win_timeleft<=0) check_win() diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index a4d2943737b..56b126aa78a 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -492,7 +492,12 @@ holo_icon = getHologramIcon(icon('AI.dmi',"holo2")) return +/mob/living/silicon/ai/proc/corereturn() + set category = "Malfunction" + set name = "Return to Main Core" - - - + var/obj/machinery/power/apc/apc = src.loc + if(!istype(apc)) + src << "\blue You are already in your Main Core." + return + apc.malfvacate() \ No newline at end of file diff --git a/code/modules/mob/living/silicon/ai/death.dm b/code/modules/mob/living/silicon/ai/death.dm index 3671674633c..430bd76ef7a 100644 --- a/code/modules/mob/living/silicon/ai/death.dm +++ b/code/modules/mob/living/silicon/ai/death.dm @@ -38,6 +38,10 @@ world << "\blue Alert: The emergency shuttle has been called. It will arrive in [round(emergency_shuttle.timeleft()/60)] minutes." world << sound('shuttlecalled.ogg') + if(explosive) + spawn(10) + explosion(src.loc, 3, 6, 12, 15) + for(var/obj/machinery/ai_status_display/O in world) //change status spawn( 0 ) O.mode = 2 diff --git a/code/modules/mob/living/silicon/ai/life.dm b/code/modules/mob/living/silicon/ai/life.dm index 67406ce4b8d..e0389fcef38 100644 --- a/code/modules/mob/living/silicon/ai/life.dm +++ b/code/modules/mob/living/silicon/ai/life.dm @@ -58,7 +58,7 @@ loc = T.loc if (istype(loc, /area)) //stage = 4 - if (!loc.master.power_equip && isturf(src.loc)) + if (!loc.master.power_equip && !istype(src.loc,/obj/item)) //stage = 5 blind = 1 @@ -72,6 +72,10 @@ src.see_in_dark = 8 src.see_invisible = 2 + var/area/home = get_area(src) + if(home.powered(EQUIP)) + home.use_power(1000, EQUIP) + if (src:aiRestorePowerRoutine==2) src << "Alert cancelled. Power has been restored without our assistance." src:aiRestorePowerRoutine = 0 @@ -102,7 +106,7 @@ src.see_in_dark = 0 src.see_invisible = 0 - if (((!loc.master.power_equip) || istype(T, /turf/space)) && isturf(src.loc)) + if (((!loc.master.power_equip) || istype(T, /turf/space)) && !istype(src.loc,/obj/item)) if (src:aiRestorePowerRoutine==0) src:aiRestorePowerRoutine = 1 diff --git a/code/modules/mob/living/silicon/ai/say.dm b/code/modules/mob/living/silicon/ai/say.dm index 6c566918f80..fdd3b9f4ec7 100644 --- a/code/modules/mob/living/silicon/ai/say.dm +++ b/code/modules/mob/living/silicon/ai/say.dm @@ -1,3 +1,10 @@ +/mob/living/silicon/ai/say(var/message) + if(parent && istype(parent) && parent.stat != 2) + parent.say(message) + return + //If there is a defined "parent" AI, it is actually an AI, and it is alive, anything the AI tries to say is said by the parent instead. + ..(message) + /mob/living/silicon/ai/say_understands(var/other) if (istype(other, /mob/living/carbon/human)) return 1 diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index 4812e39a98a..92f30ca0c87 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -54,4 +54,11 @@ drowsyness = max(drowsyness,(effect/(blocked+1))) UpdateDamageIcon() updatehealth() - return 1*/ \ No newline at end of file + return 1*/ + +/proc/islinked(var/mob/living/silicon/robot/bot, var/mob/living/silicon/ai/ai) + if(!istype(bot) || !istype(ai)) + return 0 + if (bot.connected_ai == ai) + return 1 + return 0 \ No newline at end of file diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index 13dbb7d3f0c..feb73a36a13 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -56,6 +56,7 @@ // luminosity = 1 var/has_electronics = 0 // 0 - none, 1 - plugged in, 2 - secured by screwdriver var/overload = 1 //used for the Blackout malf module + var/mob/living/silicon/ai/occupant = null /proc/RandomAPCWires() //to make this not randomize the wires, just set index to 1 and increment it in the flag for loop (after doing everything else). @@ -186,7 +187,7 @@ icon_state = "[basestate]-nocover" else if (stat & BROKEN) icon_state = "apc-b" - else if(emagged || malfhack) + else if(emagged || malfai) icon_state = "apcemag" else if(wiresexposed) icon_state = "apcewires" @@ -273,7 +274,7 @@ user << "\red There is nothing to secure." return updateicon() - else if(emagged || malfhack) + else if(emagged) user << "The interface is broken." else wiresexposed = !wiresexposed @@ -281,7 +282,7 @@ updateicon() else if (istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda)) // trying to unlock the interface with an ID card - if(emagged || malfhack) + if(emagged) user << "The interface is broken." else if(opened) user << "You must close the cover to swipe an ID card." @@ -466,6 +467,8 @@ src.interact(user) /obj/machinery/power/apc/proc/interact(mob/user) + if(!user) + return if ( (get_dist(src, user) > 1 )) if (!istype(user, /mob/living/silicon)) @@ -477,7 +480,7 @@ user << browse(null, "window=apc") return else if (src.malfai) - if (src.malfai != user) + if ((src.malfai != user && src.malfai != user:parent) && !islinked(user, malfai)) user << "AI control for this APC interface has been disabled." user << browse(null, "window=apc") return @@ -595,6 +598,10 @@ t += "

Override Programming
" else t += "

APC Hacked
" + if(!src.occupant) + t += "Shunt Core Processes
" + else + t += "Core Processes Uploaded
" t += "

Close" @@ -728,7 +735,7 @@ src.aidisabled || \ malfhack && istype(malfai) && \ ( \ - (istype(AI) && malfai!=AI) || \ + (istype(AI) && (malfai!=AI && malfai != AI.parent)) || \ (istype(robot) && (robot in malfai.connected_robots)) \ ) \ ) @@ -781,6 +788,10 @@ else if (href_list["breaker"]) operating = !operating + if(malfai) + if (ticker.mode.config_tag == "malfunction") + if (src.z == 1) //if (is_type_in_list(get_area(src), the_station_areas)) + operating ? ticker.mode:apcs++ : ticker.mode:apcs-- src.update() updateicon() @@ -841,41 +852,62 @@ if (ticker.mode.config_tag == "malfunction") if (src.z == 1) //if (is_type_in_list(get_area(src), the_station_areas)) ticker.mode:apcs++ - src.malfai = usr - src.locked = 1 - if (src.cell) - if (src.cell.charge > 0) - src.cell.charge = 0 - cell.corrupt() - src.malfhack = 1 - malfai << "Hack complete. The APC is now under your exclusive control. Discharging cell to fuse interface." - updateicon() - - var/datum/effect/effect/system/harmless_smoke_spread/smoke = new /datum/effect/effect/system/harmless_smoke_spread() - smoke.set_up(3, 0, src.loc) - smoke.attach(src) - smoke.start() - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(3, 1, src) - s.start() - for(var/mob/M in viewers(src)) - M.show_message("\red The [src.name] suddenly lets out a blast of smoke and some sparks!", 3, "\red You hear sizzling electronics.", 2) - else - malfai << "Hack complete. The APC is now under your exclusive control. Unable to fuse interface due to insufficient cell charge." + if(usr:parent) + src.malfai = usr:parent else - malfai << "Hack complete. The APC is now under your exclusive control. Unable to fuse interface due to lack of cell to discharge." + src.malfai = usr + malfai << "Hack complete. The APC is now under your exclusive control." + else if (href_list["occupyapc"]) + malfoccupy(usr) + + + else if (href_list["deoccupyapc"]) + malfvacate() src.updateDialog() return +/obj/machinery/power/apc/proc/malfoccupy(var/mob/living/silicon/ai/malf) + if(!istype(malf)) + return + src.occupant = new /mob/living/silicon/ai(src,malf.laws,null,1) + src.occupant.adjustOxyLoss(malf.getOxyLoss()) + src.occupant.name = "[malf.name] APC Copy" + if(malf.parent) + src.occupant.parent = malf.parent + else + src.occupant.parent = malf + malf.mind.transfer_to(src.occupant) + if(malf.parent) + del(malf) + src.occupant.verbs += /mob/living/silicon/ai/proc/corereturn + src.occupant.cancel_camera() + +/obj/machinery/power/apc/proc/malfvacate(var/forced) + if(!src.occupant) + return + if(src.occupant.parent && src.occupant.parent.stat != 2) + src.occupant.mind.transfer_to(src.occupant.parent) + src.occupant.parent.adjustOxyLoss(src.occupant.getOxyLoss()) + src.occupant.parent.cancel_camera() + del(src.occupant) + + else + src.occupant << "\red Primary core damaged, unable to return core processes." + if(forced) + src.occupant.loc = src.loc + src.occupant.death() + src.occupant.gib() + + /obj/machinery/power/apc/proc/ion_act() //intended to be exactly the same as an AI malf attack if(!src.malfhack && src.z == 1) if(prob(3)) src.locked = 1 if (src.cell.charge > 0) - world << "\red blew APC in [src.loc.loc]" +// world << "\red blew APC in [src.loc.loc]" src.cell.charge = 0 cell.corrupt() src.malfhack = 1 @@ -1095,6 +1127,8 @@ /obj/machinery/power/apc/emp_act(severity) if(cell) cell.emp_act(severity) + if(occupant) + occupant.emp_act(severity) lighting = 0 equipment = 0 environ = 0 @@ -1131,8 +1165,14 @@ cell.blob_act() /obj/machinery/power/apc/proc/set_broken() + if(malfai && operating) + if (ticker.mode.config_tag == "malfunction") + if (src.z == 1) //if (is_type_in_list(get_area(src), the_station_areas)) + ticker.mode:apcs-- stat |= BROKEN operating = 0 + if(occupant) + malfvacate(1) updateicon() update() @@ -1151,10 +1191,16 @@ sleep(1) /obj/machinery/power/apc/Del() + if(malfai && operating) + if (ticker.mode.config_tag == "malfunction") + if (src.z == 1) //if (is_type_in_list(get_area(src), the_station_areas)) + ticker.mode:apcs-- area.power_light = 0 area.power_equip = 0 area.power_environ = 0 area.power_change() + if(occupant) + malfvacate(1) ..() /obj/machinery/power/apc/proc/shock(mob/user, prb)