mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-14 11:42:27 +00:00
## About The Pull Request AI now gains its malf points over time, based on the number of APCs hacked. I also added a simple way to lock malf modules from being bought if you haven't hacked enough APCs, to avoid cheese with delta or robot factory. ## Why It's Good For The Game APCs hacked are a constant risk of being found, and instantly targeted, yet they give one time bonus, that is fairly small. This incentivises not using antag powers, and using natural AI tools, which blurs the line for the crew between malf and subverted AIs, and makes fighting it easier and more boring. With constant gain from the APCs the risk of being found is rewarded, and malf that hacks a big amount of APCs and avoids being found can use its powers fairly liberally, at a cost of having to manage the detection chance.
145 lines
6.2 KiB
Plaintext
145 lines
6.2 KiB
Plaintext
/obj/machinery/power/apc/proc/get_malf_status(mob/living/silicon/ai/malf)
|
|
if(!istype(malf) || !malf.malf_picker)
|
|
return APC_AI_NO_MALF
|
|
if(malfai != malf)
|
|
return APC_AI_NO_HACK
|
|
if(occupier == malf)
|
|
return APC_AI_HACK_SHUNT_HERE
|
|
if(istype(malf.loc, /obj/machinery/power/apc))
|
|
return APC_AI_HACK_SHUNT_ANOTHER
|
|
return APC_AI_HACK_NO_SHUNT
|
|
|
|
/obj/machinery/power/apc/proc/malfhack(mob/living/silicon/ai/malf)
|
|
if(!istype(malf))
|
|
return
|
|
if(get_malf_status(malf) != APC_AI_NO_HACK)
|
|
return
|
|
if(malf.malfhacking)
|
|
to_chat(malf, span_warning("You are already hacking an APC!"))
|
|
return
|
|
to_chat(malf, span_notice("Beginning override of APC systems. This takes some time, and you cannot perform other actions during the process."))
|
|
malf.malfhack = src
|
|
malf.malfhacking = addtimer(CALLBACK(malf, TYPE_PROC_REF(/mob/living/silicon/ai/, malfhacked), src), 30 SECONDS + 10*malf.hacked_apcs.len SECONDS, TIMER_STOPPABLE)
|
|
|
|
var/atom/movable/screen/alert/hackingapc/hacking_apc
|
|
hacking_apc = malf.throw_alert(ALERT_HACKING_APC, /atom/movable/screen/alert/hackingapc)
|
|
hacking_apc.target = src
|
|
|
|
/obj/machinery/power/apc/proc/malfoccupy(mob/living/silicon/ai/malf)
|
|
if(!istype(malf))
|
|
return
|
|
if(istype(malf.loc, /obj/machinery/power/apc)) // Already in an APC
|
|
to_chat(malf, span_warning("You must evacuate your current APC first!"))
|
|
return
|
|
if(!malf.can_shunt)
|
|
to_chat(malf, span_warning("You cannot shunt!"))
|
|
return
|
|
if(!is_station_level(z))
|
|
return
|
|
INVOKE_ASYNC(src, PROC_REF(malfshunt), malf)
|
|
|
|
/obj/machinery/power/apc/proc/malfshunt(mob/living/silicon/ai/malf)
|
|
var/confirm = tgui_alert(malf, "Are you sure that you want to shunt? This will take you out of your core!", "Shunt to [name]?", list("Yes", "No"))
|
|
if(confirm != "Yes")
|
|
return
|
|
malf.ShutOffDoomsdayDevice()
|
|
occupier = malf
|
|
if (isturf(malf.loc)) // create a deactivated AI core if the AI isn't coming from an emergency mech shunt
|
|
malf.linked_core = new /obj/structure/ai_core/deactivated(malf.loc)
|
|
malf.linked_core.remote_ai = malf // note that we do not set the deactivated core's core_mmi.brainmob
|
|
malf.forceMove(src) // move INTO the APC, not to its tile
|
|
if(!findtext(occupier.name, "APC Copy"))
|
|
occupier.name = "[malf.name] APC Copy"
|
|
malf.shunted = TRUE
|
|
occupier.eyeobj.name = "[occupier.name] (AI Eye)"
|
|
occupier.eyeobj.forceMove(src.loc)
|
|
for(var/obj/item/pinpointer/nuke/disk_pinpointers in GLOB.pinpointer_list)
|
|
disk_pinpointers.switch_mode_to(TRACK_MALF_AI) //Pinpointer will track the shunted AI
|
|
var/datum/action/innate/core_return/return_action = new
|
|
return_action.Grant(occupier)
|
|
SEND_SIGNAL(src, COMSIG_SILICON_AI_OCCUPY_APC, occupier)
|
|
SEND_SIGNAL(occupier, COMSIG_SILICON_AI_OCCUPY_APC, occupier)
|
|
occupier.cancel_camera()
|
|
|
|
/obj/machinery/power/apc/proc/malfvacate(forced)
|
|
if(!occupier)
|
|
return
|
|
SEND_SIGNAL(occupier, COMSIG_SILICON_AI_VACATE_APC, occupier)
|
|
SEND_SIGNAL(src, COMSIG_SILICON_AI_VACATE_APC, occupier)
|
|
if(forced)
|
|
occupier.forceMove(drop_location())
|
|
INVOKE_ASYNC(occupier, TYPE_PROC_REF(/mob/living, death))
|
|
occupier.gib(DROP_ALL_REMAINS)
|
|
occupier = null
|
|
return
|
|
if(occupier.linked_core)
|
|
occupier.shunted = FALSE
|
|
occupier.forceMove(occupier.linked_core.loc)
|
|
qdel(occupier.linked_core)
|
|
occupier.cancel_camera()
|
|
occupier = null
|
|
else
|
|
stack_trace("An AI: [occupier] has vacated an APC with no linked core and without being gibbed.")
|
|
|
|
if(!occupier.nuking) //Pinpointers go back to tracking the nuke disk, as long as the AI (somehow) isn't mid-nuking.
|
|
for(var/obj/item/pinpointer/nuke/disk_pinpointers in GLOB.pinpointer_list)
|
|
disk_pinpointers.switch_mode_to(TRACK_NUKE_DISK)
|
|
disk_pinpointers.alert = FALSE
|
|
|
|
/obj/machinery/power/apc/transfer_ai(interaction, mob/user, mob/living/silicon/ai/AI, obj/item/aicard/card)
|
|
. = ..()
|
|
if(!.)
|
|
return
|
|
if(card.AI)
|
|
to_chat(user, span_warning("[card] is already occupied!"))
|
|
return FALSE
|
|
if(!occupier)
|
|
to_chat(user, span_warning("There's nothing in [src] to transfer!"))
|
|
return FALSE
|
|
if(!occupier.mind || !occupier.client)
|
|
to_chat(user, span_warning("[occupier] is either inactive or destroyed!"))
|
|
return FALSE
|
|
if(occupier.linked_core) //if they have an active linked_core, they can't be transferred from an APC
|
|
to_chat(user, span_warning("[occupier] is refusing all attempts at transfer!") )
|
|
return FALSE
|
|
if(transfer_in_progress)
|
|
to_chat(user, span_warning("There's already a transfer in progress!"))
|
|
return FALSE
|
|
if(interaction != AI_TRANS_TO_CARD || occupier.stat)
|
|
return FALSE
|
|
var/turf/user_turf = get_turf(user)
|
|
if(!user_turf)
|
|
return FALSE
|
|
transfer_in_progress = TRUE
|
|
user.visible_message(span_notice("[user] slots [card] into [src]..."), span_notice("Transfer process initiated. Sending request for AI approval..."))
|
|
playsound(src, 'sound/machines/click.ogg', 50, TRUE)
|
|
SEND_SOUND(occupier, sound('sound/announcer/notice/notice2.ogg')) //To alert the AI that someone's trying to card them if they're tabbed out
|
|
if(tgui_alert(occupier, "[user] is attempting to transfer you to \a [card.name]. Do you consent to this?", "APC Transfer", list("Yes - Transfer Me", "No - Keep Me Here")) == "No - Keep Me Here")
|
|
to_chat(user, span_danger("AI denied transfer request. Process terminated."))
|
|
playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 50, TRUE)
|
|
transfer_in_progress = FALSE
|
|
return FALSE
|
|
if(user.loc != user_turf)
|
|
to_chat(user, span_danger("Location changed. Process terminated."))
|
|
to_chat(occupier, span_warning("[user] moved away! Transfer canceled."))
|
|
transfer_in_progress = FALSE
|
|
return FALSE
|
|
to_chat(user, span_notice("AI accepted request. Transferring stored intelligence to [card]..."))
|
|
to_chat(occupier, span_notice("Transfer starting. You will be moved to [card] shortly."))
|
|
if(!do_after(user, 5 SECONDS, target = src))
|
|
to_chat(occupier, span_warning("[user] was interrupted! Transfer canceled."))
|
|
transfer_in_progress = FALSE
|
|
return FALSE
|
|
if(!occupier || !card)
|
|
transfer_in_progress = FALSE
|
|
return FALSE
|
|
user.visible_message(span_notice("[user] transfers [occupier] to [card]!"), span_notice("Transfer complete! [occupier] is now stored in [card]."))
|
|
to_chat(occupier, span_notice("Transfer complete! You've been stored in [user]'s [card.name]."))
|
|
occupier.forceMove(card)
|
|
card.AI = occupier
|
|
occupier.shunted = FALSE
|
|
occupier.cancel_camera()
|
|
occupier = null
|
|
transfer_in_progress = FALSE
|
|
return TRUE
|