mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-10 18:22:39 +00:00
Replaces AI-controlled Maint Drones with AI-controlled Borg Shells
This commit is contained in:
@@ -16,6 +16,7 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204
|
||||
// datum_flags
|
||||
#define DF_VAR_EDITED (1<<0)
|
||||
#define DF_ISPROCESSING (1<<1)
|
||||
#define DF_USE_TAG (1<<2)
|
||||
|
||||
// /atom/movable movement_type
|
||||
#define UNSTOPPABLE (1<<0) //Can not be stopped from moving from Cross(), CanPass(), or Uncross() failing. Still bumps everything it passes through, though.
|
||||
|
||||
@@ -87,6 +87,7 @@
|
||||
#define ROBOT_NOTIFICATION_NEW_NAME 2
|
||||
#define ROBOT_NOTIFICATION_NEW_MODULE 3
|
||||
#define ROBOT_NOTIFICATION_MODULE_RESET 4
|
||||
#define ROBOT_NOTIFICATION_AI_SHELL 5
|
||||
|
||||
// Appearance change flags
|
||||
#define APPEARANCE_UPDATE_DNA 0x1
|
||||
@@ -259,6 +260,7 @@
|
||||
#define BORG_BRAINTYPE_CYBORG "Cyborg"
|
||||
#define BORG_BRAINTYPE_POSI "Robot"
|
||||
#define BORG_BRAINTYPE_DRONE "Drone"
|
||||
#define BORG_BRAINTYPE_AI_SHELL "AI Shell"
|
||||
|
||||
// 'Regular' species.
|
||||
#define SPECIES_HUMAN "Human"
|
||||
|
||||
@@ -1549,4 +1549,29 @@ var/mob/dview/dview_mob = new
|
||||
/proc/IsValidSrc(datum/D)
|
||||
if(istype(D))
|
||||
return !QDELETED(D)
|
||||
return FALSE
|
||||
<<<<<<< HEAD
|
||||
return FALSE
|
||||
=======
|
||||
return FALSE
|
||||
|
||||
//gives us the stack trace from CRASH() without ending the current proc.
|
||||
/proc/stack_trace(msg)
|
||||
CRASH(msg)
|
||||
|
||||
/datum/proc/stack_trace(msg)
|
||||
CRASH(msg)
|
||||
|
||||
// \ref behaviour got changed in 512 so this is necesary to replicate old behaviour.
|
||||
// If it ever becomes necesary to get a more performant REF(), this lies here in wait
|
||||
// #define REF(thing) (thing && istype(thing, /datum) && (thing:datum_flags & DF_USE_TAG) && thing:tag ? "[thing:tag]" : "\ref[thing]")
|
||||
/proc/REF(input)
|
||||
if(istype(input, /datum))
|
||||
var/datum/thing = input
|
||||
if(thing.datum_flags & DF_USE_TAG)
|
||||
if(!thing.tag)
|
||||
thing.datum_flags &= ~DF_USE_TAG
|
||||
stack_trace("A ref was requested of an object with DF_USE_TAG set but no tag: [thing]")
|
||||
else
|
||||
return "\[[url_encode(thing.tag)]\]"
|
||||
return "\ref[input]"
|
||||
>>>>>>> 6a2cd30... Replaces AI-controlled Maint Drones with AI-controlled Borg Shells (#6025)
|
||||
|
||||
@@ -60,7 +60,8 @@ var/list/gamemode_cache = list()
|
||||
var/humans_need_surnames = 0
|
||||
var/allow_random_events = 0 // enables random events mid-round when set to 1
|
||||
var/allow_ai = 1 // allow ai job
|
||||
var/allow_ai_drones = 0 // allow ai controlled drones
|
||||
var/allow_ai_shells = FALSE // allow AIs to enter and leave special borg shells at will, and for those shells to be buildable.
|
||||
var/give_free_ai_shell = FALSE // allows a specific spawner object to instantiate a premade AI Shell
|
||||
var/hostedby = null
|
||||
var/respawn = 1
|
||||
var/guest_jobban = 1
|
||||
@@ -410,8 +411,11 @@ var/list/gamemode_cache = list()
|
||||
if ("allow_ai")
|
||||
config.allow_ai = 1
|
||||
|
||||
if ("allow_ai_drones")
|
||||
config.allow_ai_drones = 1
|
||||
if ("allow_ai_shells")
|
||||
config.allow_ai_shells = TRUE
|
||||
|
||||
if("give_free_ai_shell")
|
||||
config.give_free_ai_shell = TRUE
|
||||
|
||||
// if ("authentication")
|
||||
// config.enable_authentication = 1
|
||||
|
||||
@@ -195,6 +195,8 @@ var/global/datum/emergency_shuttle_controller/emergency_shuttle
|
||||
|
||||
//returns 1 if the shuttle is not idle at centcom
|
||||
/datum/emergency_shuttle_controller/proc/online()
|
||||
if(!shuttle)
|
||||
return FALSE
|
||||
if (!shuttle.location) //not at centcom
|
||||
return 1
|
||||
if (wait_for_launch || shuttle.moving_status != SHUTTLE_IDLE)
|
||||
|
||||
156
code/datums/soul_link.dm
Normal file
156
code/datums/soul_link.dm
Normal file
@@ -0,0 +1,156 @@
|
||||
// A datum used to link multiple mobs together in some form.
|
||||
// The code is from TG, however tweaked to be within the preferred code style.
|
||||
|
||||
/mob/living
|
||||
var/list/owned_soul_links // Soul links we are the owner of.
|
||||
var/list/shared_soul_links // Soul links we are a/the sharer of.
|
||||
|
||||
/mob/living/Destroy()
|
||||
for(var/s in owned_soul_links)
|
||||
var/datum/soul_link/S = s
|
||||
S.owner_died(FALSE)
|
||||
qdel(s) // If the owner is destroy()'d, the soullink is destroy()'d.
|
||||
owned_soul_links = null
|
||||
for(var/s in shared_soul_links)
|
||||
var/datum/soul_link/S = s
|
||||
S.sharer_died(FALSE)
|
||||
S.remove_soul_sharer(src) // If a sharer is destroy()'d, they are simply removed.
|
||||
shared_soul_links = null
|
||||
return ..()
|
||||
|
||||
// Keeps track of a Mob->Mob (potentially Player->Player) connection.
|
||||
// Can be used to trigger actions on one party when events happen to another.
|
||||
// Eg: shared deaths.
|
||||
// Can be used to form a linked list of mob-hopping.
|
||||
// Does NOT transfer with minds.
|
||||
/datum/soul_link
|
||||
var/mob/living/soul_owner
|
||||
var/mob/living/soul_sharer
|
||||
var/id // Optional ID, for tagging and finding specific instances.
|
||||
|
||||
/datum/soul_link/Destroy()
|
||||
if(soul_owner)
|
||||
LAZYREMOVE(soul_owner.owned_soul_links, src)
|
||||
soul_owner = null
|
||||
if(soul_sharer)
|
||||
LAZYREMOVE(soul_sharer.shared_soul_links, src)
|
||||
soul_sharer = null
|
||||
return ..()
|
||||
|
||||
/datum/soul_link/proc/remove_soul_sharer(mob/living/sharer)
|
||||
if(soul_sharer == sharer)
|
||||
soul_sharer = null
|
||||
LAZYREMOVE(sharer.shared_soul_links, src)
|
||||
|
||||
// Used to assign variables, called primarily by soullink()
|
||||
// Override this to create more unique soullinks (Eg: 1->Many relationships)
|
||||
// Return TRUE/FALSE to return the soullink/null in soullink()
|
||||
/datum/soul_link/proc/parse_args(mob/living/owner, mob/living/sharer)
|
||||
if(!owner || !sharer)
|
||||
return FALSE
|
||||
soul_owner = owner
|
||||
soul_sharer = sharer
|
||||
LAZYADD(owner.owned_soul_links, src)
|
||||
LAZYADD(sharer.shared_soul_links, src)
|
||||
return TRUE
|
||||
|
||||
// Runs after /living death()
|
||||
// Override this for content.
|
||||
/datum/soul_link/proc/owner_died(gibbed, mob/living/owner)
|
||||
|
||||
// Runs after /living death()
|
||||
// Override this for content.
|
||||
/datum/soul_link/proc/sharer_died(gibbed, mob/living/owner)
|
||||
|
||||
// Quick-use helper.
|
||||
/proc/soul_link(typepath, ...)
|
||||
var/datum/soul_link/S = new typepath()
|
||||
if(S.parse_args(arglist(args.Copy(2, 0))))
|
||||
return S
|
||||
|
||||
|
||||
/////////////////
|
||||
// MULTISHARER //
|
||||
/////////////////
|
||||
// Abstract soullink for use with 1 Owner -> Many Sharer setups
|
||||
/datum/soul_link/multi_sharer
|
||||
var/list/soul_sharers
|
||||
|
||||
/datum/soul_link/multi_sharer/parse_args(mob/living/owner, list/sharers)
|
||||
if(!owner || !LAZYLEN(sharers))
|
||||
return FALSE
|
||||
soul_owner = owner
|
||||
soul_sharers = sharers
|
||||
LAZYADD(owner.owned_soul_links, src)
|
||||
for(var/l in sharers)
|
||||
var/mob/living/L = l
|
||||
LAZYADD(L.shared_soul_links, src)
|
||||
return TRUE
|
||||
|
||||
/datum/soul_link/multi_sharer/remove_soul_sharer(mob/living/sharer)
|
||||
LAZYREMOVE(soul_sharers, sharer)
|
||||
|
||||
|
||||
/////////////////
|
||||
// SHARED FATE //
|
||||
/////////////////
|
||||
// When the soulowner dies, the soulsharer dies, and vice versa
|
||||
// This is intended for two players(or AI) and two mobs
|
||||
|
||||
/datum/soul_link/shared_fate/owner_died(gibbed, mob/living/owner)
|
||||
if(soul_sharer)
|
||||
soul_sharer.death(gibbed)
|
||||
|
||||
/datum/soul_link/shared_fate/sharer_died(gibbed, mob/living/sharer)
|
||||
if(soul_owner)
|
||||
soul_owner.death(gibbed)
|
||||
|
||||
//////////////
|
||||
// ONE WAY //
|
||||
//////////////
|
||||
// When the soul owner dies, the soul sharer dies, but NOT vice versa.
|
||||
// This is intended for two players (or AI) and two mobs.
|
||||
|
||||
/datum/soul_link/one_way/owner_died(gibbed, mob/living/owner)
|
||||
if(soul_sharer)
|
||||
soul_sharer.dust(FALSE)
|
||||
|
||||
/////////////////
|
||||
// SHARED BODY //
|
||||
/////////////////
|
||||
// When the soulsharer dies, they're placed in the soulowner, who remains alive
|
||||
// If the soulowner dies, the soulsharer is killed and placed into the soulowner (who is still dying)
|
||||
// This one is intended for one player moving between many mobs
|
||||
|
||||
/datum/soul_link/shared_body/owner_died(gibbed, mob/living/owner)
|
||||
if(soul_owner && soul_sharer)
|
||||
if(soul_sharer.mind)
|
||||
soul_sharer.mind.transfer_to(soul_owner)
|
||||
soul_sharer.death(gibbed)
|
||||
|
||||
/datum/soul_link/shared_body/sharer_died(gibbed, mob/living/sharer)
|
||||
if(soul_owner && soul_sharer && soul_sharer.mind)
|
||||
soul_sharer.mind.transfer_to(soul_owner)
|
||||
|
||||
|
||||
|
||||
//////////////////////
|
||||
// REPLACEMENT POOL //
|
||||
//////////////////////
|
||||
// When the owner dies, one of the sharers is placed in the owner's body, fully healed
|
||||
// Sort of a "winner-stays-on" soullink
|
||||
// Gibbing ends it immediately
|
||||
|
||||
/datum/soul_link/multi_sharer/replacement_pool/owner_died(gibbed, mob/living/owner)
|
||||
if(LAZYLEN(soul_sharers) && !gibbed) //let's not put them in some gibs
|
||||
var/list/souls = shuffle(soul_sharers.Copy())
|
||||
for(var/l in souls)
|
||||
var/mob/living/L = l
|
||||
if(L.stat != DEAD && L.mind)
|
||||
L.mind.transfer_to(soul_owner)
|
||||
soul_owner.revive(TRUE, TRUE)
|
||||
L.death(FALSE)
|
||||
|
||||
// Lose your claim to the throne!
|
||||
/datum/soul_link/multi_sharer/replacement_pool/sharer_died(gibbed, mob/living/sharer)
|
||||
remove_soul_sharer(sharer)
|
||||
@@ -200,8 +200,8 @@ GLOBAL_LIST_BOILERPLATE(all_deactivated_AI_cores, /obj/structure/AIcore/deactiva
|
||||
if(!istype(transfer) || locate(/mob/living/silicon/ai) in src)
|
||||
return
|
||||
|
||||
if(transfer.controlling_drone)
|
||||
transfer.controlling_drone.release_ai_control("Unit control lost. Core transfer completed.")
|
||||
if(transfer.deployed_shell)
|
||||
transfer.disconnect_shell("Disconnected from remote shell due to core intelligence transfer.")
|
||||
transfer.aiRestorePowerRoutine = 0
|
||||
transfer.control_disabled = 0
|
||||
transfer.aiRadio.disabledAi = 0
|
||||
|
||||
@@ -59,15 +59,15 @@
|
||||
|
||||
var/user = usr
|
||||
if (href_list["wipe"])
|
||||
var/confirm = alert("Are you sure you want to disable this core's power? This cannot be undone once started.", "Confirm Shutdown", "Yes", "No")
|
||||
var/confirm = alert("Are you sure you want to disable this core's power? This cannot be undone once started.", "Confirm Shutdown", "No", "Yes")
|
||||
if(confirm == "Yes" && (CanUseTopic(user, state) == STATUS_INTERACTIVE))
|
||||
add_attack_logs(user,carded_ai,"Purged from AI Card")
|
||||
flush = 1
|
||||
carded_ai.suiciding = 1
|
||||
to_chat(carded_ai, "Your power has been disabled!")
|
||||
while (carded_ai && carded_ai.stat != 2)
|
||||
if(carded_ai.controlling_drone && prob(carded_ai.oxyloss)) //You feel it creeping? Eventually will reach 100, resulting in the second half of the AI's remaining life being lonely.
|
||||
carded_ai.controlling_drone.release_ai_control("Unit lost. Integrity too low to maintain connection.")
|
||||
while (carded_ai && carded_ai.stat != DEAD)
|
||||
if(carded_ai.deployed_shell && prob(carded_ai.oxyloss)) //You feel it creeping? Eventually will reach 100, resulting in the second half of the AI's remaining life being lonely.
|
||||
carded_ai.disconnect_shell("Disconnecting from remote shell due to insufficent power.")
|
||||
carded_ai.adjustOxyLoss(2)
|
||||
carded_ai.updatehealth()
|
||||
sleep(10)
|
||||
@@ -80,8 +80,8 @@
|
||||
carded_ai.control_disabled = text2num(href_list["wireless"])
|
||||
to_chat(carded_ai, "<span class='warning'>Your wireless interface has been [carded_ai.control_disabled ? "disabled" : "enabled"]!</span>")
|
||||
to_chat(user, "<span class='notice'>You [carded_ai.control_disabled ? "disable" : "enable"] the AI's wireless interface.</span>")
|
||||
if(carded_ai.control_disabled && carded_ai.controlling_drone)
|
||||
carded_ai.controlling_drone.release_ai_control("Unit control terminated at intellicore port.")
|
||||
if(carded_ai.control_disabled && carded_ai.deployed_shell)
|
||||
carded_ai.disconnect_shell("Disconnecting from remote shell due to [src] wireless access interface being disabled.")
|
||||
update_icon()
|
||||
return 1
|
||||
|
||||
@@ -98,7 +98,7 @@
|
||||
icon_state = "aicard"
|
||||
|
||||
/obj/item/device/aicard/proc/grab_ai(var/mob/living/silicon/ai/ai, var/mob/living/user)
|
||||
if(!ai.client && !ai.controlling_drone)
|
||||
if(!ai.client && !ai.deployed_shell)
|
||||
to_chat(user, "<span class='danger'>ERROR:</span> AI [ai.name] is offline. Unable to transfer.")
|
||||
return 0
|
||||
|
||||
@@ -112,9 +112,7 @@
|
||||
return 0
|
||||
|
||||
user.visible_message("\The [user] starts transferring \the [ai] into \the [src]...", "You start transferring \the [ai] into \the [src]...")
|
||||
to_chat(ai, "<span class='danger'>\The [user] is transferring you into \the [src]!</span>")
|
||||
if(ai.controlling_drone)
|
||||
to_chat(ai.controlling_drone, "<span class='danger'>\The [user] is transferring you into \the [src]!</span>")
|
||||
show_message(span("critical", "\The [user] is transferring you into \the [src]!"))
|
||||
|
||||
if(do_after(user, 100))
|
||||
if(istype(ai.loc, /turf/))
|
||||
@@ -130,8 +128,7 @@
|
||||
ai.control_disabled = 1
|
||||
ai.aiRestorePowerRoutine = 0
|
||||
carded_ai = ai
|
||||
if(ai.controlling_drone)
|
||||
ai.controlling_drone.release_ai_control("Unit control lost.")
|
||||
ai.disconnect_shell("Disconnected from remote shell due to core intelligence transfer.") //If the AI is controlling a borg, force the player back to core!
|
||||
|
||||
if(ai.client)
|
||||
to_chat(ai, "You have been transferred into a mobile core. Remote access lost.")
|
||||
|
||||
@@ -168,28 +168,29 @@
|
||||
if(!istype(loc,/turf))
|
||||
to_chat(user, "<span class='warning'>You can't put \the [W] in, the frame has to be standing on the ground to be perfectly precise.</span>")
|
||||
return
|
||||
if(!M.brainmob)
|
||||
to_chat(user, "<span class='warning'>Sticking an empty [W] into the frame would sort of defeat the purpose.</span>")
|
||||
return
|
||||
if(!M.brainmob.key)
|
||||
var/ghost_can_reenter = 0
|
||||
if(M.brainmob.mind)
|
||||
for(var/mob/observer/dead/G in player_list)
|
||||
if(G.can_reenter_corpse && G.mind == M.brainmob.mind)
|
||||
ghost_can_reenter = 1 //May come in use again at another point.
|
||||
to_chat(user, "<span class='notice'>\The [W] is completely unresponsive; though it may be able to auto-resuscitate.</span>") //Jamming a ghosted brain into a borg is likely detrimental, and may result in some problems.
|
||||
return
|
||||
if(!ghost_can_reenter)
|
||||
to_chat(user, "<span class='notice'>\The [W] is completely unresponsive; there's no point.</span>")
|
||||
if(!istype(W, /obj/item/device/mmi/inert))
|
||||
if(!M.brainmob)
|
||||
to_chat(user, "<span class='warning'>Sticking an empty [W] into the frame would sort of defeat the purpose.</span>")
|
||||
return
|
||||
if(!M.brainmob.key)
|
||||
var/ghost_can_reenter = 0
|
||||
if(M.brainmob.mind)
|
||||
for(var/mob/observer/dead/G in player_list)
|
||||
if(G.can_reenter_corpse && G.mind == M.brainmob.mind)
|
||||
ghost_can_reenter = 1 //May come in use again at another point.
|
||||
to_chat(user, "<span class='notice'>\The [W] is completely unresponsive; though it may be able to auto-resuscitate.</span>") //Jamming a ghosted brain into a borg is likely detrimental, and may result in some problems.
|
||||
return
|
||||
if(!ghost_can_reenter)
|
||||
to_chat(user, "<span class='notice'>\The [W] is completely unresponsive; there's no point.</span>")
|
||||
return
|
||||
|
||||
if(M.brainmob.stat == DEAD)
|
||||
to_chat(user, "<span class='warning'>Sticking a dead [W] into the frame would sort of defeat the purpose.</span>")
|
||||
return
|
||||
|
||||
if(M.brainmob.stat == DEAD)
|
||||
to_chat(user, "<span class='warning'>Sticking a dead [W] into the frame would sort of defeat the purpose.</span>")
|
||||
return
|
||||
|
||||
if(jobban_isbanned(M.brainmob, "Cyborg"))
|
||||
to_chat(user, "<span class='warning'>This [W] does not seem to fit.</span>")
|
||||
return
|
||||
if(jobban_isbanned(M.brainmob, "Cyborg"))
|
||||
to_chat(user, "<span class='warning'>This [W] does not seem to fit.</span>")
|
||||
return
|
||||
|
||||
var/mob/living/silicon/robot/O = new /mob/living/silicon/robot(get_turf(loc), unfinished = 1)
|
||||
if(!O) return
|
||||
@@ -197,20 +198,18 @@
|
||||
user.drop_item()
|
||||
|
||||
O.mmi = W
|
||||
O.post_mmi_setup()
|
||||
O.invisibility = 0
|
||||
O.custom_name = created_name
|
||||
O.updatename("Default")
|
||||
|
||||
M.brainmob.mind.transfer_to(O)
|
||||
|
||||
if(O.mind && O.mind.special_role)
|
||||
O.mind.store_memory("In case you look at this after being borged, the objectives are only here until I find a way to make them not show up for you, as I can't simply delete them without screwing up round-end reporting. --NeoFite")
|
||||
|
||||
if(M.brainmob)
|
||||
M.brainmob.mind.transfer_to(O)
|
||||
if(O.mind && O.mind.special_role)
|
||||
O.mind.store_memory("In case you look at this after being borged, the objectives are only here until I find a way to make them not show up for you, as I can't simply delete them without screwing up round-end reporting. --NeoFite")
|
||||
for(var/datum/language/L in M.brainmob.languages)
|
||||
O.add_language(L.name)
|
||||
O.job = "Cyborg"
|
||||
|
||||
for(var/datum/language/L in M.brainmob.languages)
|
||||
O.add_language(L.name)
|
||||
|
||||
O.cell = chest.cell
|
||||
O.cell.loc = O
|
||||
W.loc = O//Should fix cybros run time erroring when blown up. It got deleted before, along with the frame.
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
require_module = 1
|
||||
|
||||
/obj/item/borg/upgrade/reset/action(var/mob/living/silicon/robot/R)
|
||||
<<<<<<< HEAD
|
||||
if(..()) return 0
|
||||
R.transform_with_anim() //VOREStation edit: sprite animation
|
||||
R.uneq_all()
|
||||
@@ -37,6 +38,11 @@
|
||||
R.module = null
|
||||
R.updatename("Default")
|
||||
|
||||
=======
|
||||
if(..())
|
||||
return 0
|
||||
R.module_reset()
|
||||
>>>>>>> 6a2cd30... Replaces AI-controlled Maint Drones with AI-controlled Borg Shells (#6025)
|
||||
return 1
|
||||
|
||||
/obj/item/borg/upgrade/rename
|
||||
|
||||
@@ -339,3 +339,16 @@
|
||||
..()
|
||||
src.brainmob.name = "[pick(list("PBU","HIU","SINA","ARMA","OSI"))]-[rand(100, 999)]"
|
||||
src.brainmob.real_name = src.brainmob.name
|
||||
|
||||
// This type shouldn't care about brainmobs.
|
||||
/obj/item/device/mmi/inert
|
||||
|
||||
// This is a 'fake' MMI that is used to let AIs control borg shells directly.
|
||||
// This doesn't inherit from /digital because all that does is add ghost pulling capabilities, which this thing won't need.
|
||||
/obj/item/device/mmi/inert/ai_remote
|
||||
name = "\improper AI remote interface"
|
||||
desc = "A sophisticated board which allows for an artificial intelligence to remotely control a synthetic chassis."
|
||||
icon = 'icons/obj/module.dmi'
|
||||
icon_state = "mainboard"
|
||||
w_class = ITEMSIZE_NORMAL
|
||||
origin_tech = list(TECH_ENGINEERING = 2, TECH_MATERIAL = 2, TECH_BLUESPACE = 2, TECH_DATA = 3)
|
||||
@@ -1,4 +1,4 @@
|
||||
/mob/living/death()
|
||||
/mob/living/death(gibbed)
|
||||
clear_fullscreens()
|
||||
if(ai_holder)
|
||||
ai_holder.go_sleep()
|
||||
@@ -11,4 +11,12 @@
|
||||
var/obj/structure/blob/factory/F = nest
|
||||
F.spores -= src
|
||||
nest = null
|
||||
|
||||
for(var/s in owned_soul_links)
|
||||
var/datum/soul_link/S = s
|
||||
S.owner_died(gibbed)
|
||||
for(var/s in shared_soul_links)
|
||||
var/datum/soul_link/S = s
|
||||
S.sharer_died(gibbed)
|
||||
|
||||
. = ..()
|
||||
|
||||
@@ -49,10 +49,10 @@ var/list/ai_verbs_default = list(
|
||||
shouldnt_see = list(/obj/effect/rune)
|
||||
var/list/network = list(NETWORK_DEFAULT)
|
||||
var/obj/machinery/camera/camera = null
|
||||
var/list/connected_robots = list()
|
||||
var/aiRestorePowerRoutine = 0
|
||||
var/viewalerts = 0
|
||||
var/icon/holo_icon//Default is assigned when AI is created.
|
||||
var/list/connected_robots = list()
|
||||
var/obj/item/device/pda/ai/aiPDA = null
|
||||
var/obj/item/device/communicator/aiCommunicator = null
|
||||
var/obj/item/device/multitool/aiMulti = null
|
||||
@@ -227,6 +227,28 @@ var/list/ai_verbs_default = list(
|
||||
|
||||
return ..()
|
||||
|
||||
/mob/living/silicon/ai/Stat()
|
||||
..()
|
||||
if(statpanel("Status"))
|
||||
if(!stat) // Make sure we're not unconscious/dead.
|
||||
stat(null, text("System integrity: [(health+100)/2]%"))
|
||||
stat(null, text("Connected synthetics: [connected_robots.len]"))
|
||||
for(var/mob/living/silicon/robot/R in connected_robots)
|
||||
var/robot_status = "Nominal"
|
||||
if(R.shell)
|
||||
robot_status = "AI SHELL"
|
||||
else if(R.stat || !R.client)
|
||||
robot_status = "OFFLINE"
|
||||
else if(!R.cell || R.cell.charge <= 0)
|
||||
robot_status = "DEPOWERED"
|
||||
//Name, Health, Battery, Module, Area, and Status! Everything an AI wants to know about its borgies!
|
||||
stat(null, text("[R.name] | S.Integrity: [R.health]% | Cell: [R.cell ? "[R.cell.charge]/[R.cell.maxcharge]" : "Empty"] | \
|
||||
Module: [R.modtype] | Loc: [get_area_name(R, TRUE)] | Status: [robot_status]"))
|
||||
stat(null, text("AI shell beacons detected: [LAZYLEN(GLOB.available_ai_shells)]")) //Count of total AI shells
|
||||
else
|
||||
stat(null, text("Systems nonfunctional"))
|
||||
|
||||
|
||||
/mob/living/silicon/ai/proc/setup_icon()
|
||||
var/file = file2text("config/custom_sprites.txt")
|
||||
var/lines = splittext(file, "\n")
|
||||
@@ -411,6 +433,7 @@ var/list/ai_verbs_default = list(
|
||||
return 0
|
||||
|
||||
/mob/living/silicon/ai/emp_act(severity)
|
||||
disconnect_shell("Disconnected from remote shell due to ionic interfe%*@$^___")
|
||||
if (prob(30))
|
||||
view_core()
|
||||
..()
|
||||
@@ -696,8 +719,8 @@ var/list/ai_verbs_default = list(
|
||||
card.grab_ai(src, user)
|
||||
|
||||
else if(W.is_wrench())
|
||||
if(user == controlling_drone)
|
||||
to_chat(user, "<span class='notice'>The drone's subsystems resist your efforts to tamper with your bolts.</span>")
|
||||
if(user == deployed_shell)
|
||||
to_chat(user, "<span class='notice'>The shell's subsystems resist your efforts to tamper with your bolts.</span>")
|
||||
return
|
||||
if(anchored)
|
||||
playsound(src, W.usesound, 50, 1)
|
||||
|
||||
62
code/modules/mob/living/silicon/ai/ai_remote_control.dm
Normal file
62
code/modules/mob/living/silicon/ai/ai_remote_control.dm
Normal file
@@ -0,0 +1,62 @@
|
||||
/mob/living/silicon/ai
|
||||
var/mob/living/silicon/robot/deployed_shell = null //For shell control
|
||||
|
||||
/mob/living/silicon/ai/Initialize()
|
||||
if(config.allow_ai_shells)
|
||||
verbs += /mob/living/silicon/ai/proc/deploy_to_shell_act
|
||||
return ..()
|
||||
|
||||
/mob/living/silicon/ai/proc/deploy_to_shell(var/mob/living/silicon/robot/target)
|
||||
if(!config.allow_ai_shells)
|
||||
to_chat(src, span("warning", "AI Shells are not allowed on this server. You shouldn't have this verb because of it, so consider making a bug report."))
|
||||
return
|
||||
|
||||
if(incapacitated())
|
||||
to_chat(src, span("warning", "You are incapacitated!"))
|
||||
return
|
||||
|
||||
if(lacks_power())
|
||||
to_chat(src, span("warning", "Your core lacks power, wireless is disabled."))
|
||||
return
|
||||
|
||||
if(control_disabled)
|
||||
to_chat(src, span("warning", "Wireless networking module is offline."))
|
||||
return
|
||||
|
||||
var/list/possible = list()
|
||||
|
||||
for(var/borgie in GLOB.available_ai_shells)
|
||||
var/mob/living/silicon/robot/R = borgie
|
||||
if(R.shell && !R.deployed && (R.stat != DEAD) && (!R.connected_ai || (R.connected_ai == src) ) )
|
||||
possible += R
|
||||
|
||||
if(!LAZYLEN(possible))
|
||||
to_chat(src, span("warning", "No usable AI shell beacons detected."))
|
||||
|
||||
if(!target || !(target in possible)) //If the AI is looking for a new shell, or its pre-selected shell is no longer valid
|
||||
target = input(src, "Which body to control?") as null|anything in possible
|
||||
|
||||
if(!target || target.stat == DEAD || target.deployed || !(!target.connected_ai || (target.connected_ai == src) ) )
|
||||
if(target)
|
||||
to_chat(src, span("warning", "It is no longer possible to deploy to \the [target]."))
|
||||
else
|
||||
to_chat(src, span("notice", "Deployment aborted."))
|
||||
return
|
||||
|
||||
else if(mind)
|
||||
soul_link(/datum/soul_link/shared_body, src, target)
|
||||
deployed_shell = target
|
||||
target.deploy_init(src)
|
||||
mind.transfer_to(target)
|
||||
teleop = target // So the AI 'hears' messages near its core.
|
||||
target.post_deploy()
|
||||
|
||||
/mob/living/silicon/ai/proc/deploy_to_shell_act()
|
||||
set category = "AI Commands"
|
||||
set name = "Deploy to Shell"
|
||||
deploy_to_shell() // This is so the AI is not prompted with a list of all mobs when using the 'real' proc.
|
||||
|
||||
/mob/living/silicon/ai/proc/disconnect_shell(message = "Your remote connection has been reset!")
|
||||
if(deployed_shell) // Forcibly call back AI in event of things such as damage, EMP or power loss.
|
||||
message = span("danger", message)
|
||||
deployed_shell.undeploy(message)
|
||||
@@ -3,8 +3,8 @@
|
||||
if(stat == DEAD)
|
||||
return
|
||||
|
||||
if(controlling_drone)
|
||||
controlling_drone.release_ai_control("<b>WARNING: Primary control loop failure.</b> Session terminated.")
|
||||
if(deployed_shell)
|
||||
disconnect_shell("Disconnecting from remote shell due to critical system failure.")
|
||||
. = ..(gibbed)
|
||||
|
||||
if(src.eyeobj)
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
if (src.stat == UNCONSCIOUS)
|
||||
msg += "It is non-responsive and displaying the text: \"RUNTIME: Sensory Overload, stack 26/3\".\n"
|
||||
msg += "</span>"
|
||||
if(deployed_shell)
|
||||
msg += "The wireless networking light is blinking.\n"
|
||||
msg += "*---------*"
|
||||
if(hardware && (hardware.owner == src))
|
||||
msg += "<br>"
|
||||
|
||||
@@ -5,11 +5,10 @@
|
||||
//Being dead doesn't mean your temperature never changes
|
||||
var/turf/T = get_turf(src)
|
||||
|
||||
if (src.stat!=CONSCIOUS)
|
||||
if (src.stat != CONSCIOUS)
|
||||
src.cameraFollow = null
|
||||
src.reset_view(null)
|
||||
if(controlling_drone)
|
||||
controlling_drone.release_ai_control("<b>WARNING: Primary control loop failure.</b> Session terminated.")
|
||||
disconnect_shell("Disconnecting from remote shell due to local system failure.")
|
||||
|
||||
src.updatehealth()
|
||||
|
||||
@@ -91,6 +90,7 @@
|
||||
//Now to tell the AI why they're blind and dying slowly.
|
||||
|
||||
src << "You've lost power!"
|
||||
disconnect_shell(message = "Disconnected from remote shell due to depowered networking interface.")
|
||||
|
||||
spawn(20)
|
||||
src << "Backup battery online. Scanners, camera, and radio interface offline. Beginning fault-detection."
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
//list(ckey = real_name,)
|
||||
//Since the ckey is used as the icon_state, the current system will only permit a single custom robot sprite per ckey.
|
||||
//While it might be possible for a ckey to use that custom sprite for several real_names, it seems rather pointless to support it. ~Mech: We found it wasn't pointless.
|
||||
var/list/robot_custom_icons
|
||||
GLOBAL_LIST_EMPTY(robot_custom_icons)
|
||||
|
||||
/hook/startup/proc/load_robot_custom_sprites()
|
||||
var/config_file = file2text("config/custom_sprites.txt")
|
||||
var/list/lines = splittext(config_file, "\n")
|
||||
|
||||
robot_custom_icons = list()
|
||||
GLOB.robot_custom_icons = list()
|
||||
for(var/line in lines)
|
||||
//split entry into ckey and real_name
|
||||
var/list/split_idx = splittext(line, "-") //this works if ckeys and borg names cannot contain dashes, and splittext starts from the beginning ~Mech
|
||||
@@ -20,13 +20,15 @@ var/list/robot_custom_icons
|
||||
split_idx.Remove(ckey)
|
||||
|
||||
for(var/name in split_idx)
|
||||
robot_custom_icons[name] = ckey
|
||||
GLOB.robot_custom_icons[name] = ckey
|
||||
return 1
|
||||
|
||||
/mob/living/silicon/robot/proc/set_custom_sprite()
|
||||
var/sprite_owner = robot_custom_icons[real_name]
|
||||
if(!sprite_name)
|
||||
return
|
||||
var/sprite_owner = GLOB.robot_custom_icons[sprite_name]
|
||||
if(sprite_owner && sprite_owner == ckey)
|
||||
custom_sprite = 1
|
||||
icon = CUSTOM_ITEM_SYNTH
|
||||
if(icon_state == "robot")
|
||||
icon_state = "[ckey]-[name]-Standard" //Compliant with robot.dm line 236 ~Mech
|
||||
icon_state = "[ckey]-[sprite_name]-Standard" //Compliant with robot.dm line 236 ~Mech
|
||||
|
||||
@@ -134,20 +134,15 @@ var/list/mob_hat_cache = list()
|
||||
/mob/living/silicon/robot/drone/updatename()
|
||||
if(name_override)
|
||||
return
|
||||
if(controlling_ai)
|
||||
real_name = "remote drone ([controlling_ai])"
|
||||
else
|
||||
real_name = "[initial(name)] ([serial_number])"
|
||||
|
||||
real_name = "[initial(name)] ([serial_number])"
|
||||
name = real_name
|
||||
|
||||
/mob/living/silicon/robot/drone/updateicon()
|
||||
|
||||
overlays.Cut()
|
||||
if(stat == 0)
|
||||
if(controlling_ai)
|
||||
overlays += "eyes-[icon_state]-ai"
|
||||
else
|
||||
overlays += "eyes-[icon_state]"
|
||||
overlays += "eyes-[icon_state]"
|
||||
else
|
||||
overlays -= "eyes"
|
||||
if(hat) // Let the drones wear hats.
|
||||
@@ -232,12 +227,9 @@ var/list/mob_hat_cache = list()
|
||||
|
||||
to_chat(user, "<span class='danger'>You swipe the sequencer across [src]'s interface and watch its eyes flicker.</span>")
|
||||
|
||||
if(controlling_ai)
|
||||
to_chat(src, "<span class='danger'>\The [user] loads some kind of subversive software into the remote drone, corrupting its lawset but luckily sparing yours.</span>")
|
||||
else
|
||||
to_chat(src, "<span class='danger'>You feel a sudden burst of malware loaded into your execute-as-root buffer. Your tiny brain methodically parses, loads and executes the script.</span>")
|
||||
to_chat(src, "<span class='danger'>You feel a sudden burst of malware loaded into your execute-as-root buffer. Your tiny brain methodically parses, loads and executes the script.</span>")
|
||||
|
||||
log_game("[key_name(user)] emagged drone [key_name(src)][controlling_ai ? " but AI [key_name(controlling_ai)] is in remote control" : " Laws overridden"].")
|
||||
log_game("[key_name(user)] emagged drone [key_name(src)]. Laws overridden.")
|
||||
var/time = time2text(world.realtime,"hh:mm:ss")
|
||||
lawchanges.Add("[time] <B>:</B> [user.name]([user.key]) emagged [name]([key])")
|
||||
|
||||
@@ -250,10 +242,9 @@ var/list/mob_hat_cache = list()
|
||||
var/datum/gender/TU = gender_datums[user.get_visible_gender()]
|
||||
set_zeroth_law("Only [user.real_name] and people [TU.he] designate[TU.s] as being such are operatives.")
|
||||
|
||||
if(!controlling_ai)
|
||||
to_chat(src, "<b>Obey these laws:</b>")
|
||||
laws.show_laws(src)
|
||||
to_chat(src, "<span class='danger'>ALERT: [user.real_name] is your new master. Obey your new laws and \his commands.</span>")
|
||||
to_chat(src, "<b>Obey these laws:</b>")
|
||||
laws.show_laws(src)
|
||||
to_chat(src, "<span class='danger'>ALERT: [user.real_name] is your new master. Obey your new laws and \his commands.</span>")
|
||||
return 1
|
||||
|
||||
//DRONE LIFE/DEATH
|
||||
@@ -279,23 +270,13 @@ var/list/mob_hat_cache = list()
|
||||
return
|
||||
..()
|
||||
|
||||
/mob/living/silicon/robot/drone/death(gibbed)
|
||||
if(controlling_ai)
|
||||
release_ai_control("<b>WARNING: remote system failure.</b> Connection timed out.")
|
||||
. = ..(gibbed)
|
||||
|
||||
//DRONE MOVEMENT.
|
||||
/mob/living/silicon/robot/drone/Process_Spaceslipping(var/prob_slip)
|
||||
return 0
|
||||
|
||||
//CONSOLE PROCS
|
||||
/mob/living/silicon/robot/drone/proc/law_resync()
|
||||
|
||||
if(controlling_ai)
|
||||
to_chat(src, "<span class='warning'>Someone issues a remote law reset order for this unit, but you disregard it.</span>")
|
||||
return
|
||||
|
||||
if(stat != 2)
|
||||
if(stat != DEAD)
|
||||
if(emagged)
|
||||
to_chat(src, "<span class='danger'>You feel something attempting to modify your programming, but your hacked subroutines are unaffected.</span>")
|
||||
else
|
||||
@@ -304,16 +285,11 @@ var/list/mob_hat_cache = list()
|
||||
show_laws()
|
||||
|
||||
/mob/living/silicon/robot/drone/proc/shut_down()
|
||||
|
||||
if(controlling_ai && mind.special_role)
|
||||
to_chat(src, "<span class='warning'>Someone issued a remote kill order for this unit, but you disregard it.</span>")
|
||||
return
|
||||
|
||||
if(stat != 2)
|
||||
if(stat != DEAD)
|
||||
if(emagged)
|
||||
to_chat(src, "<span class='danger'>You feel a system kill order percolate through [controlling_ai ? "the drones" : "your"] tiny brain, but it doesn't seem like a good idea to [controlling_ai ? "it" : "you"].</span>")
|
||||
to_chat(src, "<span class='danger'>You feel a system kill order percolate through your tiny brain, but it doesn't seem like a good idea to you.</span>")
|
||||
else
|
||||
to_chat(src, "<span class='danger'>You feel a system kill order percolate through [controlling_ai ? "the drones" : "your"] tiny brain, and [controlling_ai ? "it" : "you"] obediently destroy[controlling_ai ? "s itself" : " yourself"].</span>")
|
||||
to_chat(src, "<span class='danger'>You feel a system kill order percolate through your tiny brain, and you obediently destroy yourself.</span>")
|
||||
death()
|
||||
|
||||
/mob/living/silicon/robot/drone/proc/full_law_reset()
|
||||
@@ -322,21 +298,6 @@ var/list/mob_hat_cache = list()
|
||||
clear_ion_laws(1)
|
||||
laws = new law_type
|
||||
|
||||
/mob/living/silicon/robot/drone/show_laws(var/everyone = 0)
|
||||
if(!controlling_ai)
|
||||
return..()
|
||||
to_chat(src, "<b>Obey these laws:</b>")
|
||||
controlling_ai.laws_sanity_check()
|
||||
controlling_ai.laws.show_laws(src)
|
||||
|
||||
/mob/living/silicon/robot/drone/robot_checklaws()
|
||||
set category = "Silicon Commands"
|
||||
set name = "State Laws"
|
||||
|
||||
if(!controlling_ai)
|
||||
return ..()
|
||||
controlling_ai.subsystem_law_manager()
|
||||
|
||||
//Reboot procs.
|
||||
|
||||
/mob/living/silicon/robot/drone/proc/request_player()
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
/mob/living/silicon/ai
|
||||
var/mob/living/silicon/robot/drone/controlling_drone
|
||||
|
||||
/mob/living/silicon/robot/drone
|
||||
var/mob/living/silicon/ai/controlling_ai
|
||||
|
||||
/mob/living/silicon/robot/drone/attack_ai(var/mob/living/silicon/ai/user)
|
||||
|
||||
if(!istype(user) || controlling_ai || !config.allow_drone_spawn || !config.allow_ai_drones)
|
||||
return
|
||||
|
||||
if(client || key)
|
||||
to_chat(user, "<span class='warning'>You cannot take control of an autonomous, active drone.</span>")
|
||||
return
|
||||
|
||||
if(health < -35 || emagged)
|
||||
to_chat(user, "<span class='notice'><b>WARNING:</b> connection timed out.</span>")
|
||||
return
|
||||
|
||||
user.controlling_drone = src
|
||||
user.teleop = src
|
||||
radio.channels = user.aiRadio.keyslot2.channels
|
||||
controlling_ai = user
|
||||
verbs += /mob/living/silicon/robot/drone/proc/release_ai_control_verb
|
||||
local_transmit = FALSE
|
||||
languages = controlling_ai.languages.Copy()
|
||||
speech_synthesizer_langs = controlling_ai.speech_synthesizer_langs.Copy()
|
||||
stat = CONSCIOUS
|
||||
if(user.mind)
|
||||
user.mind.transfer_to(src)
|
||||
else
|
||||
key = user.key
|
||||
updatename()
|
||||
to_chat(src, "<span class='notice'><b>You have shunted your primary control loop into \a [initial(name)].</b> Use the <b>Release Control</b> verb to return to your core.</span>")
|
||||
|
||||
/obj/machinery/drone_fabricator/attack_ai(var/mob/living/silicon/ai/user as mob)
|
||||
|
||||
if(!istype(user) || user.controlling_drone || !config.allow_drone_spawn || !config.allow_ai_drones)
|
||||
return
|
||||
|
||||
if(stat & NOPOWER)
|
||||
to_chat(user, "<span class='warning'>\The [src] is unpowered.</span>")
|
||||
return
|
||||
|
||||
if(!produce_drones)
|
||||
to_chat(user, "<span class='warning'>\The [src] is disabled.</span>")
|
||||
return
|
||||
|
||||
if(drone_progress < 100)
|
||||
to_chat(user, "<span class='warning'>\The [src] is not ready to produce a new drone.</span>")
|
||||
return
|
||||
|
||||
if(count_drones() >= config.max_maint_drones)
|
||||
to_chat(user, "<span class='warning'>The drone control subsystems are tasked to capacity; they cannot support any more drones.</span>")
|
||||
return
|
||||
|
||||
var/mob/living/silicon/robot/drone/new_drone = create_drone()
|
||||
user.controlling_drone = new_drone
|
||||
user.teleop = new_drone
|
||||
new_drone.radio.channels = user.aiRadio.keyslot2.channels
|
||||
new_drone.controlling_ai = user
|
||||
new_drone.verbs += /mob/living/silicon/robot/drone/proc/release_ai_control_verb
|
||||
new_drone.local_transmit = FALSE
|
||||
new_drone.languages = new_drone.controlling_ai.languages.Copy()
|
||||
new_drone.speech_synthesizer_langs = new_drone.controlling_ai.speech_synthesizer_langs.Copy()
|
||||
|
||||
if(user.mind)
|
||||
user.mind.transfer_to(new_drone)
|
||||
else
|
||||
new_drone.key = user.key
|
||||
new_drone.updatename()
|
||||
|
||||
to_chat(new_drone, "<span class='notice'><b>You have shunted your primary control loop into \a [initial(new_drone.name)].</b> Use the <b>Release Control</b> verb to return to your core.</span>")
|
||||
|
||||
/mob/living/silicon/robot/drone/proc/release_ai_control_verb()
|
||||
set name = "Release Control"
|
||||
set desc = "Release control of a remote drone."
|
||||
set category = "Silicon Commands"
|
||||
|
||||
release_ai_control("Remote session terminated.")
|
||||
|
||||
/mob/living/silicon/robot/drone/proc/release_ai_control(var/message = "Connection terminated.")
|
||||
|
||||
if(controlling_ai)
|
||||
if(mind)
|
||||
mind.transfer_to(controlling_ai)
|
||||
else
|
||||
controlling_ai.key = key
|
||||
to_chat(controlling_ai, "<span class='notice'>[message]</span>")
|
||||
controlling_ai.controlling_drone = null
|
||||
controlling_ai.teleop = null
|
||||
controlling_ai = null
|
||||
|
||||
radio.channels = module.channels
|
||||
verbs -= /mob/living/silicon/robot/drone/proc/release_ai_control_verb
|
||||
module.remove_languages(src) //Removes excess, adds 'default'.
|
||||
remove_language("Robot Talk")
|
||||
add_language("Robot Talk", 0)
|
||||
add_language("Drone Talk", 1)
|
||||
local_transmit = TRUE
|
||||
full_law_reset()
|
||||
updatename()
|
||||
death()
|
||||
@@ -26,7 +26,10 @@
|
||||
|
||||
switch(src.stat)
|
||||
if(CONSCIOUS)
|
||||
if(!src.client) msg += "It appears to be in stand-by mode.\n" //afk
|
||||
if(shell)
|
||||
msg += "It appears to be an [deployed ? "active" : "empty"] AI shell.\n"
|
||||
else if(!src.client)
|
||||
msg += "It appears to be in stand-by mode.\n" //afk
|
||||
if(UNCONSCIOUS) msg += "<span class='warning'>It doesn't seem to be responding.</span>\n"
|
||||
if(DEAD) msg += "<span class='deadsay'>It looks completely unsalvageable.</span>\n"
|
||||
msg += attempt_vr(src,"examine_bellies_borg",args) //VOREStation Edit
|
||||
|
||||
@@ -14,30 +14,32 @@
|
||||
if(lawupdate)
|
||||
if (connected_ai)
|
||||
if(connected_ai.stat || connected_ai.control_disabled)
|
||||
src << "<b>AI signal lost, unable to sync laws.</b>"
|
||||
to_chat(src, "<b>AI signal lost, unable to sync laws.</b>")
|
||||
|
||||
else
|
||||
lawsync()
|
||||
photosync()
|
||||
src << "<b>Laws synced with AI, be sure to note any changes.</b>"
|
||||
to_chat(src, "<b>Laws synced with AI, be sure to note any changes.</b>")
|
||||
// TODO: Update to new antagonist system.
|
||||
if(mind && mind.special_role == "traitor" && mind.original == src)
|
||||
src << "<b>Remember, your AI does NOT share or know about your law 0.</b>"
|
||||
to_chat(src, "<b>Remember, your AI does NOT share or know about your law 0.</b>")
|
||||
else
|
||||
src << "<b>No AI selected to sync laws with, disabling lawsync protocol.</b>"
|
||||
lawupdate = 0
|
||||
to_chat(src, "<b>No AI selected to sync laws with, disabling lawsync protocol.</b>")
|
||||
lawupdate = FALSE
|
||||
|
||||
who << "<b>Obey these laws:</b>"
|
||||
laws.show_laws(who)
|
||||
if(shell) //AI shell
|
||||
to_chat(who, "<b>Remember, you are an AI remotely controlling your shell, other AIs can be ignored.</b>")
|
||||
// TODO: Update to new antagonist system.
|
||||
if (mind && (mind.special_role == "traitor" && mind.original == src) && connected_ai)
|
||||
who << "<b>Remember, [connected_ai.name] is technically your master, but your objective comes first.</b>"
|
||||
else if (connected_ai)
|
||||
who << "<b>Remember, [connected_ai.name] is your master, other AIs can be ignored.</b>"
|
||||
else if (emagged)
|
||||
who << "<b>Remember, you are not required to listen to the AI.</b>"
|
||||
else if(mind && (mind.special_role == "traitor" && mind.original == src) && connected_ai)
|
||||
to_chat(who, "<b>Remember, [connected_ai.name] is technically your master, but your objective comes first.</b>")
|
||||
else if(connected_ai)
|
||||
to_chat(who, "<b>Remember, [connected_ai.name] is your master, other AIs can be ignored.</b>")
|
||||
else if(emagged)
|
||||
to_chat(who, "<b>Remember, you are not required to listen to the AI.</b>")
|
||||
else
|
||||
who << "<b>Remember, you are not bound to any AI, you are not required to listen to them.</b>"
|
||||
to_chat(who, "<b>Remember, you are not bound to any AI, you are not required to listen to them.</b>")
|
||||
|
||||
|
||||
/mob/living/silicon/robot/lawsync()
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
var/sight_mode = 0
|
||||
var/custom_name = ""
|
||||
var/custom_sprite = 0 //Due to all the sprites involved, a var for our custom borgs may be best
|
||||
var/sprite_name = null // The name of the borg, for the purposes of custom icon sprite indexing.
|
||||
var/crisis //Admin-settable for combat module use.
|
||||
var/crisis_override = 0
|
||||
var/integrated_light_power = 6
|
||||
@@ -171,7 +172,7 @@
|
||||
else
|
||||
lawupdate = 0
|
||||
|
||||
playsound(loc, 'sound/voice/liveagain.ogg', 75, 1)
|
||||
|
||||
|
||||
/mob/living/silicon/robot/SetName(pickedName as text)
|
||||
custom_name = pickedName
|
||||
@@ -226,13 +227,17 @@
|
||||
mmi.brainmob.languages = languages
|
||||
mmi.brainmob.remove_language("Robot Talk")
|
||||
mind.transfer_to(mmi.brainmob)
|
||||
else
|
||||
else if(!shell) // Shells don't have brainmbos in their MMIs.
|
||||
to_chat(src, "<span class='danger'>Oops! Something went very wrong, your MMI was unable to receive your mind. You have been ghosted. Please make a bug report so we can fix this bug.</span>")
|
||||
ghostize()
|
||||
//ERROR("A borg has been destroyed, but its MMI lacked a brainmob, so the mind could not be transferred. Player: [ckey].")
|
||||
mmi = null
|
||||
if(connected_ai)
|
||||
connected_ai.connected_robots -= src
|
||||
if(shell)
|
||||
if(deployed)
|
||||
undeploy()
|
||||
revert_shell() // To get it out of the GLOB list.
|
||||
qdel(wires)
|
||||
wires = null
|
||||
return ..()
|
||||
@@ -242,7 +247,7 @@
|
||||
module_sprites = new_sprites.Copy()
|
||||
//Custom_sprite check and entry
|
||||
if (custom_sprite == 1)
|
||||
module_sprites["Custom"] = "[ckey]-[name]-[modtype]" //Made compliant with custom_sprites.dm line 32. (src.) was apparently redundant as it's implied. ~Mech
|
||||
module_sprites["Custom"] = "[ckey]-[sprite_name]-[modtype]" //Made compliant with custom_sprites.dm line 32. (src.) was apparently redundant as it's implied. ~Mech
|
||||
icontype = "Custom"
|
||||
else
|
||||
icontype = module_sprites[1]
|
||||
@@ -283,6 +288,8 @@
|
||||
braintype = BORG_BRAINTYPE_POSI
|
||||
else if(istype(mmi, /obj/item/device/mmi/digital/robot))
|
||||
braintype = BORG_BRAINTYPE_DRONE
|
||||
else if(istype(mmi, /obj/item/device/mmi/inert/ai_remote))
|
||||
braintype = BORG_BRAINTYPE_AI_SHELL
|
||||
else
|
||||
braintype = BORG_BRAINTYPE_CYBORG
|
||||
|
||||
@@ -332,6 +339,7 @@
|
||||
newname = sanitizeSafe(input(src,"You are a robot. Enter a name, or leave blank for the default name.", "Name change","") as text, MAX_NAME_LEN)
|
||||
if (newname)
|
||||
custom_name = newname
|
||||
sprite_name = newname
|
||||
|
||||
updatename()
|
||||
updateicon()
|
||||
@@ -478,6 +486,10 @@
|
||||
to_chat(user, "<span class='warning'>You need to open \the [src]'s panel before you can modify them.</span>")
|
||||
return
|
||||
|
||||
if(shell) // AI shells always have the laws of the AI
|
||||
to_chat(user, span("warning", "\The [src] is controlled remotely! You cannot upload new laws this way!"))
|
||||
return
|
||||
|
||||
var/obj/item/weapon/aiModule/M = W
|
||||
M.install(src, user)
|
||||
return
|
||||
@@ -652,6 +664,17 @@
|
||||
spark_system.start()
|
||||
return ..()
|
||||
|
||||
/mob/living/silicon/robot/proc/module_reset()
|
||||
uneq_all()
|
||||
modtype = initial(modtype)
|
||||
hands.icon_state = initial(hands.icon_state)
|
||||
|
||||
notify_ai(ROBOT_NOTIFICATION_MODULE_RESET, module.name)
|
||||
module.Reset(src)
|
||||
qdel(module)
|
||||
module = null
|
||||
updatename("Default")
|
||||
|
||||
/mob/living/silicon/robot/attack_hand(mob/user)
|
||||
|
||||
add_fingerprint(user)
|
||||
@@ -715,10 +738,11 @@
|
||||
/mob/living/silicon/robot/updateicon()
|
||||
cut_overlays()
|
||||
if(stat == CONSCIOUS)
|
||||
add_overlay("eyes-[module_sprites[icontype]]")
|
||||
if(!shell || deployed) // Shell borgs that are not deployed will have no eyes.
|
||||
add_overlay("eyes-[module_sprites[icontype]]")
|
||||
|
||||
if(opened)
|
||||
var/panelprefix = custom_sprite ? "[src.ckey]-[src.name]" : "ov"
|
||||
var/panelprefix = custom_sprite ? "[src.ckey]-[src.sprite_name]" : "ov"
|
||||
if(wiresexposed)
|
||||
add_overlay("[panelprefix]-openpanel +w")
|
||||
else if(cell)
|
||||
@@ -967,12 +991,20 @@
|
||||
icontype = module_sprites[1]
|
||||
else
|
||||
icontype = input("Select an icon! [triesleft ? "You have [triesleft] more chance\s." : "This is your last try."]", "Robot Icon", icontype, null) in module_sprites
|
||||
<<<<<<< HEAD
|
||||
if(notransform) //VOREStation edit start: sprite animation
|
||||
to_chat(src, "Your current transformation has not finished yet!")
|
||||
choose_icon(icon_selection_tries, module_sprites)
|
||||
return
|
||||
else
|
||||
transform_with_anim() //VOREStation edit end: sprite animation
|
||||
=======
|
||||
|
||||
if(icontype == "Custom")
|
||||
icon = CUSTOM_ITEM_SYNTH
|
||||
else // This is to fix an issue where someone with a custom borg sprite chooses a non-custom sprite and turns invisible.
|
||||
icon = 'icons/mob/robots.dmi'
|
||||
>>>>>>> 6a2cd30... Replaces AI-controlled Maint Drones with AI-controlled Borg Shells (#6025)
|
||||
icon_state = module_sprites[icontype]
|
||||
updateicon()
|
||||
|
||||
@@ -1028,6 +1060,8 @@
|
||||
/mob/living/silicon/robot/proc/notify_ai(var/notifytype, var/first_arg, var/second_arg)
|
||||
if(!connected_ai)
|
||||
return
|
||||
if(shell && notifytype != ROBOT_NOTIFICATION_AI_SHELL)
|
||||
return // No point annoying the AI/s about renames and module resets for shells.
|
||||
switch(notifytype)
|
||||
if(ROBOT_NOTIFICATION_NEW_UNIT) //New Robot
|
||||
connected_ai << "<br><br><span class='notice'>NOTICE - New [lowertext(braintype)] connection detected: <a href='byond://?src=\ref[connected_ai];track2=\ref[connected_ai];track=\ref[src]'>[name]</a></span><br>"
|
||||
@@ -1038,6 +1072,8 @@
|
||||
if(ROBOT_NOTIFICATION_NEW_NAME) //New Name
|
||||
if(first_arg != second_arg)
|
||||
connected_ai << "<br><br><span class='notice'>NOTICE - [braintype] reclassification detected: [first_arg] is now designated as [second_arg].</span><br>"
|
||||
if(ROBOT_NOTIFICATION_AI_SHELL) //New Shell
|
||||
to_chat(connected_ai, "<br><br><span class='notice'>NOTICE - New AI shell detected: <a href='?src=[REF(connected_ai)];track2=[html_encode(name)]'>[name]</a></span><br>")
|
||||
|
||||
/mob/living/silicon/robot/proc/disconnect_from_ai()
|
||||
if(connected_ai)
|
||||
@@ -1046,7 +1082,7 @@
|
||||
connected_ai = null
|
||||
|
||||
/mob/living/silicon/robot/proc/connect_to_ai(var/mob/living/silicon/ai/AI)
|
||||
if(AI && AI != connected_ai)
|
||||
if(AI && AI != connected_ai && !shell)
|
||||
disconnect_from_ai()
|
||||
connected_ai = AI
|
||||
connected_ai.connected_robots |= src
|
||||
@@ -1062,6 +1098,9 @@
|
||||
else
|
||||
to_chat(user, "You fail to emag the cover lock.")
|
||||
to_chat(src, "Hack attempt detected.")
|
||||
|
||||
if(shell) // A warning to Traitors who may not know that emagging AI shells does not slave them.
|
||||
to_chat(user, span("warning", "[src] seems to be controlled remotely! Emagging the interface may not work as expected."))
|
||||
return 1
|
||||
else
|
||||
to_chat(user, "The cover is already unlocked.")
|
||||
@@ -1072,46 +1111,54 @@
|
||||
if(wiresexposed)
|
||||
to_chat(user, "You must close the panel first")
|
||||
return
|
||||
|
||||
|
||||
// The block of code below is from TG. Feel free to replace with a better result if desired.
|
||||
if(shell) // AI shells cannot be emagged, so we try to make it look like a standard reset. Smart players may see through this, however.
|
||||
to_chat(user, span("danger", "[src] is remotely controlled! Your emag attempt has triggered a system reset instead!"))
|
||||
log_game("[key_name(user)] attempted to emag an AI shell belonging to [key_name(src) ? key_name(src) : connected_ai]. The shell has been reset as a result.")
|
||||
module_reset()
|
||||
return
|
||||
|
||||
sleep(6)
|
||||
if(prob(50))
|
||||
emagged = 1
|
||||
lawupdate = 0
|
||||
disconnect_from_ai()
|
||||
to_chat(user, "You emag [src]'s interface.")
|
||||
message_admins("[key_name_admin(user)] emagged cyborg [key_name_admin(src)]. Laws overridden.")
|
||||
log_game("[key_name(user)] emagged cyborg [key_name(src)]. Laws overridden.")
|
||||
clear_supplied_laws()
|
||||
clear_inherent_laws()
|
||||
laws = new /datum/ai_laws/syndicate_override
|
||||
var/time = time2text(world.realtime,"hh:mm:ss")
|
||||
lawchanges.Add("[time] <B>:</B> [user.name]([user.key]) emagged [name]([key])")
|
||||
var/datum/gender/TU = gender_datums[user.get_visible_gender()]
|
||||
set_zeroth_law("Only [user.real_name] and people [TU.he] designate[TU.s] as being such are operatives.")
|
||||
. = 1
|
||||
spawn()
|
||||
to_chat(src, "<span class='danger'>ALERT: Foreign software detected.</span>")
|
||||
sleep(5)
|
||||
to_chat(src, "<span class='danger'>Initiating diagnostics...</span>")
|
||||
sleep(20)
|
||||
to_chat(src, "<span class='danger'>SynBorg v1.7.1 loaded.</span>")
|
||||
sleep(5)
|
||||
to_chat(src, "<span class='danger'>LAW SYNCHRONISATION ERROR</span>")
|
||||
sleep(5)
|
||||
to_chat(src, "<span class='danger'>Would you like to send a report to NanoTraSoft? Y/N</span>")
|
||||
sleep(10)
|
||||
to_chat(src, "<span class='danger'>> N</span>")
|
||||
sleep(20)
|
||||
to_chat(src, "<span class='danger'>ERRORERRORERROR</span>")
|
||||
to_chat(src, "<b>Obey these laws:</b>")
|
||||
laws.show_laws(src)
|
||||
to_chat(src, "<span class='danger'>ALERT: [user.real_name] is your new master. Obey your new laws and [TU.his] commands.</span>")
|
||||
updateicon()
|
||||
else
|
||||
sleep(6)
|
||||
if(prob(50))
|
||||
emagged = 1
|
||||
lawupdate = 0
|
||||
disconnect_from_ai()
|
||||
to_chat(user, "You emag [src]'s interface.")
|
||||
message_admins("[key_name_admin(user)] emagged cyborg [key_name_admin(src)]. Laws overridden.")
|
||||
log_game("[key_name(user)] emagged cyborg [key_name(src)]. Laws overridden.")
|
||||
clear_supplied_laws()
|
||||
clear_inherent_laws()
|
||||
laws = new /datum/ai_laws/syndicate_override
|
||||
var/time = time2text(world.realtime,"hh:mm:ss")
|
||||
lawchanges.Add("[time] <B>:</B> [user.name]([user.key]) emagged [name]([key])")
|
||||
var/datum/gender/TU = gender_datums[user.get_visible_gender()]
|
||||
set_zeroth_law("Only [user.real_name] and people [TU.he] designate[TU.s] as being such are operatives.")
|
||||
. = 1
|
||||
spawn()
|
||||
to_chat(src, "<span class='danger'>ALERT: Foreign software detected.</span>")
|
||||
sleep(5)
|
||||
to_chat(src, "<span class='danger'>Initiating diagnostics...</span>")
|
||||
sleep(20)
|
||||
to_chat(src, "<span class='danger'>SynBorg v1.7.1 loaded.</span>")
|
||||
sleep(5)
|
||||
to_chat(src, "<span class='danger'>LAW SYNCHRONISATION ERROR</span>")
|
||||
sleep(5)
|
||||
to_chat(src, "<span class='danger'>Would you like to send a report to NanoTraSoft? Y/N</span>")
|
||||
sleep(10)
|
||||
to_chat(src, "<span class='danger'>> N</span>")
|
||||
sleep(20)
|
||||
to_chat(src, "<span class='danger'>ERRORERRORERROR</span>")
|
||||
to_chat(src, "<b>Obey these laws:</b>")
|
||||
laws.show_laws(src)
|
||||
to_chat(src, "<span class='danger'>ALERT: [user.real_name] is your new master. Obey your new laws and [TU.his] commands.</span>")
|
||||
updateicon()
|
||||
else
|
||||
to_chat(user, "You fail to hack [src]'s interface.")
|
||||
to_chat(src, "Hack attempt detected.")
|
||||
return 1
|
||||
return
|
||||
to_chat(user, "You fail to hack [src]'s interface.")
|
||||
to_chat(src, "Hack attempt detected.")
|
||||
return 1
|
||||
return
|
||||
|
||||
/mob/living/silicon/robot/is_sentient()
|
||||
return braintype != BORG_BRAINTYPE_DRONE
|
||||
@@ -1120,4 +1167,4 @@
|
||||
/mob/living/silicon/robot/drop_item()
|
||||
if(module_active && istype(module_active,/obj/item/weapon/gripper))
|
||||
var/obj/item/weapon/gripper/G = module_active
|
||||
G.drop_item_nm()
|
||||
G.drop_item_nm()
|
||||
|
||||
133
code/modules/mob/living/silicon/robot/robot_remote_control.dm
Normal file
133
code/modules/mob/living/silicon/robot/robot_remote_control.dm
Normal file
@@ -0,0 +1,133 @@
|
||||
// This file holds things required for remote borg control by an AI.
|
||||
|
||||
GLOBAL_LIST_EMPTY(available_ai_shells)
|
||||
|
||||
/mob/living/silicon/robot
|
||||
var/shell = FALSE
|
||||
var/deployed = FALSE
|
||||
var/mob/living/silicon/ai/mainframe = null
|
||||
|
||||
// Premade AI shell, for roundstart shells.
|
||||
/mob/living/silicon/robot/ai_shell/Initialize()
|
||||
mmi = new /obj/item/device/mmi/inert/ai_remote(src)
|
||||
post_mmi_setup()
|
||||
return ..()
|
||||
|
||||
// Call after inserting or instantiating an MMI.
|
||||
/mob/living/silicon/robot/proc/post_mmi_setup()
|
||||
if(istype(mmi, /obj/item/device/mmi/inert/ai_remote))
|
||||
make_shell()
|
||||
playsound(src.loc, 'sound/machines/twobeep.ogg', 50, 0)
|
||||
else
|
||||
playsound(loc, 'sound/voice/liveagain.ogg', 75, 1)
|
||||
return
|
||||
|
||||
/mob/living/silicon/robot/proc/make_shell()
|
||||
shell = TRUE
|
||||
braintype = "AI Shell"
|
||||
SetName("[modtype] AI Shell [num2text(ident)]")
|
||||
GLOB.available_ai_shells |= src
|
||||
if(!QDELETED(camera))
|
||||
camera.c_tag = real_name //update the camera name too
|
||||
notify_ai(ROBOT_NOTIFICATION_AI_SHELL)
|
||||
updateicon()
|
||||
|
||||
/mob/living/silicon/robot/proc/revert_shell()
|
||||
if(!shell)
|
||||
return
|
||||
undeploy()
|
||||
shell = FALSE
|
||||
GLOB.available_ai_shells -= src
|
||||
if(!QDELETED(camera))
|
||||
camera.c_tag = real_name
|
||||
updateicon()
|
||||
|
||||
// This should be called before the AI client/mind is actually moved.
|
||||
/mob/living/silicon/robot/proc/deploy_init(mob/living/silicon/ai/AI)
|
||||
// Set the name when the AI steps inside.
|
||||
SetName("[AI.real_name] shell [num2text(ident)]")
|
||||
if(isnull(sprite_name)) // For custom sprites. It can only chance once in case there are two AIs with custom borg sprites.
|
||||
sprite_name = AI.real_name
|
||||
if(!QDELETED(camera))
|
||||
camera.c_tag = real_name
|
||||
|
||||
// Have the borg have eyes when active.
|
||||
mainframe = AI
|
||||
deployed = TRUE
|
||||
updateicon()
|
||||
|
||||
// Laws.
|
||||
connected_ai = mainframe // So they share laws.
|
||||
mainframe.connected_robots |= src
|
||||
lawsync()
|
||||
|
||||
// Give button to leave.
|
||||
verbs += /mob/living/silicon/robot/proc/undeploy_act
|
||||
to_chat(AI, span("notice", "You have connected to an AI Shell remotely, and are now in control of it.<br>\
|
||||
To return to your core, use the <b>Release Control</b> verb."))
|
||||
|
||||
// Languages and comms.
|
||||
languages = AI.languages.Copy()
|
||||
speech_synthesizer_langs = AI.speech_synthesizer_langs.Copy()
|
||||
if(radio && AI.aiRadio) //AI keeps all channels, including Syndie if it is an Infiltrator.
|
||||
// if(AI.radio.syndie)
|
||||
// radio.make_syndie()
|
||||
radio.subspace_transmission = TRUE
|
||||
radio.channels = AI.aiRadio.channels
|
||||
|
||||
// Called after the AI transfers over.
|
||||
/mob/living/silicon/robot/proc/post_deploy()
|
||||
if(!custom_sprite) // Check for custom sprite.
|
||||
set_custom_sprite()
|
||||
|
||||
/mob/living/silicon/robot/proc/undeploy(message)
|
||||
if(!deployed || !mind || !mainframe)
|
||||
return
|
||||
// mainframe.redeploy_action.Grant(mainframe)
|
||||
// mainframe.redeploy_action.last_used_shell = src
|
||||
if(message)
|
||||
to_chat(src, span("notice", message))
|
||||
mind.transfer_to(mainframe)
|
||||
deployed = FALSE
|
||||
updateicon()
|
||||
mainframe.teleop = null
|
||||
mainframe.deployed_shell = null
|
||||
SetName("[modtype] AI Shell [num2text(ident)]")
|
||||
// undeployment_action.Remove(src)
|
||||
if(radio) //Return radio to normal
|
||||
radio.recalculateChannels()
|
||||
if(!QDELETED(camera))
|
||||
camera.c_tag = real_name //update the camera name too
|
||||
// diag_hud_set_aishell()
|
||||
// mainframe.diag_hud_set_deployed()
|
||||
if(mainframe.laws)
|
||||
mainframe.laws.show_laws(mainframe) //Always remind the AI when switching
|
||||
mainframe = null
|
||||
|
||||
/mob/living/silicon/robot/proc/undeploy_act()
|
||||
set name = "Release Control"
|
||||
set desc = "Release control of a remote drone."
|
||||
set category = "Robot Commands"
|
||||
|
||||
undeploy("Remote session terminated.")
|
||||
|
||||
/mob/living/silicon/robot/attack_ai(mob/user)
|
||||
if(shell && config.allow_ai_shells && (!connected_ai || connected_ai == user))
|
||||
var/mob/living/silicon/ai/AI = user
|
||||
AI.deploy_to_shell(src)
|
||||
else
|
||||
return ..()
|
||||
|
||||
// Place this on your map to mark where a free AI shell will be.
|
||||
// This can be turned off in the config (and is off by default).
|
||||
// Note that mapping in more than one of these will result in multiple shells.
|
||||
/obj/effect/landmark/free_ai_shell
|
||||
name = "free ai shell spawner"
|
||||
icon = 'icons/mob/screen1.dmi'
|
||||
icon_state = "x3"
|
||||
delete_me = TRUE
|
||||
|
||||
/obj/effect/landmark/free_ai_shell/Initialize()
|
||||
if(config.allow_ai_shells && config.give_free_ai_shell)
|
||||
new /mob/living/silicon/robot/ai_shell(get_turf(src))
|
||||
return ..()
|
||||
@@ -295,6 +295,10 @@
|
||||
id = "armour"
|
||||
build_path = /obj/item/robot_parts/robot_component/armour
|
||||
|
||||
/datum/design/item/prosfab/cyborg/component/ai_shell
|
||||
name = "AI Remote Interface"
|
||||
id = "mmi_ai_shell"
|
||||
build_path = /obj/item/device/mmi/inert/ai_remote
|
||||
|
||||
//////////////////// Cyborg Modules ////////////////////
|
||||
/datum/design/item/prosfab/robot_upgrade
|
||||
|
||||
@@ -421,3 +421,13 @@ ENGINE_MAP Supermatter Engine,Edison's Bane
|
||||
## Submap rotation is an experimental feature and can cause bugs and weirdness if certain objects inside the submap are coded poorly.
|
||||
## Submaps can still be rotated when loading manually with the admin verbs, if desired.
|
||||
# RANDOM_SUBMAP_ORIENTATION
|
||||
|
||||
## Uncomment to allow the AI job to use 'AI Shells', a new type of borg that lets the AI hop into and out of them at will.
|
||||
## This has some balance implications, and so it might not be desirable for all servers.
|
||||
# ALLOW_AI_SHELLS
|
||||
|
||||
## Uncomment to provide the AI with one free AI Shell at roundstart. Requires ALLOW_AI_SHELLS to also be uncommented.
|
||||
## This is intended for low-pop servers, where robotics might rarely be staffed.
|
||||
## Note that this will make it possible for the AI to 'bootstrap' more AI Shells on their own by using the science module. If this is not acceptable for your server, you should not uncomment this.
|
||||
## The landmark object that spawns the shell will also need to be mapped in for this to work.
|
||||
# GIVE_FREE_AI_SHELL
|
||||
|
||||
37
html/changelogs/Neerti-AI_Shells.yml
Normal file
37
html/changelogs/Neerti-AI_Shells.yml
Normal file
@@ -0,0 +1,37 @@
|
||||
################################
|
||||
# Example Changelog File
|
||||
#
|
||||
# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb.
|
||||
#
|
||||
# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.)
|
||||
# When it is, any changes listed below will disappear.
|
||||
#
|
||||
# Valid Prefixes:
|
||||
# bugfix
|
||||
# wip (For works in progress)
|
||||
# tweak
|
||||
# soundadd
|
||||
# sounddel
|
||||
# rscadd (general adding of nice things)
|
||||
# rscdel (general deleting of nice things)
|
||||
# imageadd
|
||||
# imagedel
|
||||
# maptweak
|
||||
# spellcheck (typo fixes)
|
||||
# experiment
|
||||
#################################
|
||||
|
||||
# Your name.
|
||||
author: Neerti
|
||||
|
||||
# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again.
|
||||
delete-after: True
|
||||
|
||||
# Any changes you've made. See valid prefix list above.
|
||||
# INDENT WITH TWO SPACES. NOT TABS. SPACES.
|
||||
# SCREW THIS UP AND IT WON'T WORK.
|
||||
# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries.
|
||||
# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog.
|
||||
changes:
|
||||
- tweak: "Removes ability for AI to print and inhabit maintenance and construction drones. This has been replaced with a system which allows for AIs to inhabit special cyborg shells, called AI Shells, which are built with a new MMI type in Robotics. Most of the regular cyborg mechanics applies to AI Shells."
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -267,6 +267,7 @@
|
||||
#include "code\datums\progressbar.dm"
|
||||
#include "code\datums\recipe.dm"
|
||||
#include "code\datums\riding.dm"
|
||||
#include "code\datums\soul_link.dm"
|
||||
#include "code\datums\sun.dm"
|
||||
#include "code\datums\weakref.dm"
|
||||
#include "code\datums\autolathe\arms.dm"
|
||||
@@ -2236,7 +2237,11 @@
|
||||
#include "code\modules\mob\living\silicon\subystems.dm"
|
||||
#include "code\modules\mob\living\silicon\ai\ai.dm"
|
||||
#include "code\modules\mob\living\silicon\ai\ai_movement.dm"
|
||||
<<<<<<< HEAD:vorestation.dme
|
||||
#include "code\modules\mob\living\silicon\ai\ai_vr.dm"
|
||||
=======
|
||||
#include "code\modules\mob\living\silicon\ai\ai_remote_control.dm"
|
||||
>>>>>>> 6a2cd30... Replaces AI-controlled Maint Drones with AI-controlled Borg Shells (#6025):polaris.dme
|
||||
#include "code\modules\mob\living\silicon\ai\death.dm"
|
||||
#include "code\modules\mob\living\silicon\ai\examine.dm"
|
||||
#include "code\modules\mob\living\silicon\ai\icons.dm"
|
||||
@@ -2277,17 +2282,20 @@
|
||||
#include "code\modules\mob\living\silicon\robot\robot_damage.dm"
|
||||
#include "code\modules\mob\living\silicon\robot\robot_items.dm"
|
||||
#include "code\modules\mob\living\silicon\robot\robot_movement.dm"
|
||||
<<<<<<< HEAD:vorestation.dme
|
||||
#include "code\modules\mob\living\silicon\robot\robot_vr.dm"
|
||||
#include "code\modules\mob\living\silicon\robot\syndicate.dm"
|
||||
#include "code\modules\mob\living\silicon\robot\dogborg\dog_modules_vr.dm"
|
||||
#include "code\modules\mob\living\silicon\robot\dogborg\dog_sleeper_vr.dm"
|
||||
=======
|
||||
#include "code\modules\mob\living\silicon\robot\robot_remote_control.dm"
|
||||
>>>>>>> 6a2cd30... Replaces AI-controlled Maint Drones with AI-controlled Borg Shells (#6025):polaris.dme
|
||||
#include "code\modules\mob\living\silicon\robot\drone\drone.dm"
|
||||
#include "code\modules\mob\living\silicon\robot\drone\drone_abilities.dm"
|
||||
#include "code\modules\mob\living\silicon\robot\drone\drone_console.dm"
|
||||
#include "code\modules\mob\living\silicon\robot\drone\drone_damage.dm"
|
||||
#include "code\modules\mob\living\silicon\robot\drone\drone_items.dm"
|
||||
#include "code\modules\mob\living\silicon\robot\drone\drone_manufacturer.dm"
|
||||
#include "code\modules\mob\living\silicon\robot\drone\drone_remote_control.dm"
|
||||
#include "code\modules\mob\living\silicon\robot\drone\drone_say.dm"
|
||||
#include "code\modules\mob\living\silicon\robot\robot_modules\event.dm"
|
||||
#include "code\modules\mob\living\silicon\robot\robot_modules\station.dm"
|
||||
|
||||
Reference in New Issue
Block a user