Merge remote-tracking branch 'upstream/master' into borger-tweaks

This commit is contained in:
SteelSlayer
2020-08-18 11:09:48 -05:00
371 changed files with 4484 additions and 8606 deletions
@@ -53,7 +53,6 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
if(ismob(body))
T = get_turf(body) //Where is the body located?
attack_log_old = body.attack_log_old //preserve our attack logs by copying them to our ghost
logs = body.logs.Copy()
var/mutable_appearance/MA = copy_appearance(body)
if(body.mind && body.mind.name)
+4 -4
View File
@@ -47,7 +47,7 @@
. = trim(. + trim(msg))
. += "\""
/mob/proc/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency)
/mob/proc/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency, use_voice = TRUE)
if(!client)
return 0
@@ -74,7 +74,7 @@
return 0
var/speaker_name = speaker.name
if(ishuman(speaker))
if(use_voice && ishuman(speaker))
var/mob/living/carbon/human/H = speaker
speaker_name = H.GetVoice()
@@ -99,9 +99,9 @@
if(speaker == src)
to_chat(src, "<span class='warning'>You cannot hear yourself speak!</span>")
else
to_chat(src, "<span class='name'>[speaker_name]</span>[speaker.GetAltName()] talks but you cannot hear [speaker.p_them()].")
to_chat(src, "<span class='name'>[speaker.name]</span> talks but you cannot hear [speaker.p_them()].")
else
to_chat(src, "<span class='game say'><span class='name'>[speaker_name]</span>[speaker.GetAltName()] [track][message]</span>")
to_chat(src, "<span class='game say'><span class='name'>[speaker_name]</span>[use_voice ? speaker.GetAltName() : ""] [track][message]</span>")
if(speech_sound && (get_dist(speaker, src) <= world.view && src.z == speaker.z))
var/turf/source = speaker? get_turf(speaker) : get_turf(src)
playsound_local(source, speech_sound, sound_vol, 1, sound_frequency)
@@ -81,7 +81,7 @@
/mob/living/carbon/alien/larva/show_inv(mob/user as mob)
return
/mob/living/carbon/alien/larva/start_pulling(atom/movable/AM, state, force = move_force, supress_message = FALSE)
/mob/living/carbon/alien/larva/start_pulling(atom/movable/AM, state, force = pull_force, show_message = FALSE)
return FALSE
/* Commented out because it's duplicated in life.dm
@@ -70,7 +70,7 @@
return
polling = 1
spawn()
var/list/candidates = pollCandidates("Do you want to play as an alien?", ROLE_ALIEN, 0)
var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as an alien?", ROLE_ALIEN, FALSE, source = /mob/living/carbon/alien/larva)
var/mob/C = null
// To stop clientless larva, we will check that our host has a client
@@ -226,13 +226,6 @@
brainmob.emp_damage += rand(0,10)
..()
/obj/item/mmi/relaymove(var/mob/user, var/direction)
if(user.stat || user.stunned)
return
var/obj/item/rig/rig = src.get_rig()
if(rig)
rig.forced_move(direction, user)
/obj/item/mmi/Destroy()
if(isrobot(loc))
var/mob/living/silicon/robot/borg = loc
@@ -109,9 +109,6 @@
// log_world("k")
sql_report_death(src)
if(wearing_rig)
wearing_rig.notify_ai("<span class='danger'>Warning: user death event. Mobility control passed to integrated intelligence system.</span>")
/mob/living/carbon/human/update_revive()
. = ..()
if(. && healthdoll)
@@ -308,14 +308,14 @@
m_type = 1
if("bow", "bows")
if(!buckled)
if(!restrained())
var/M = handle_emote_param(param)
message = "<B>[src]</B> bows[M ? " to [M]" : ""]."
m_type = 1
if("salute", "salutes")
if(!buckled)
if(!restrained())
var/M = handle_emote_param(param)
message = "<B>[src]</B> salutes[M ? " to [M]" : ""]."
@@ -5,7 +5,6 @@
icon = 'icons/mob/human.dmi'
icon_state = "body_m_s"
deathgasp_on_death = TRUE
var/obj/item/rig/wearing_rig // This is very not good, but it's much much better than calling get_rig() every update_canmove() call.
/mob/living/carbon/human/New(loc)
icon = null // This is now handled by overlays -- we just keep an icon for the sake of the map editor.
@@ -182,13 +181,6 @@
stat("Tank Pressure", internal.air_contents.return_pressure())
stat("Distribution Pressure", internal.distribute_pressure)
if(istype(back, /obj/item/rig))
var/obj/item/rig/suit = back
var/cell_status = "ERROR"
if(suit.cell)
cell_status = "[suit.cell.charge]/[suit.cell.maxcharge]"
stat(null, "Suit charge: [cell_status]")
// I REALLY need to split up status panel things into datums
var/mob/living/simple_animal/borer/B = has_brain_worms()
if(B && B.controlling)
@@ -963,16 +955,6 @@
var/obj/item/clothing/mask/MT = src.wear_mask
tinted += MT.tint
//god help me
if(istype(back, /obj/item/rig))
var/obj/item/rig/O = back
if(O.helmet && O.helmet == head && (O.helmet.body_parts_covered & HEAD))
if((O.offline && O.offline_vision_restriction == 1) || (!O.offline && O.vision_restriction == 1))
tinted = 2
if((O.offline && O.offline_vision_restriction == 2) || (!O.offline && O.vision_restriction == 2))
tinted = 3
//im so sorry
return tinted
@@ -129,13 +129,6 @@
O.heal_damage(0, -amount, internal = 0, robo_repair = O.is_robotic(), updating_health = updating_health)
return STATUS_UPDATE_HEALTH
/mob/living/carbon/human/Paralyse(amount)
// Notify our AI if they can now control the suit.
if(wearing_rig && !stat && paralysis < amount) //We are passing out right this second.
wearing_rig.notify_ai("<span class='danger'>Warning: user consciousness failure. Mobility control passed to integrated intelligence system.</span>")
return ..()
/mob/living/carbon/human/adjustCloneLoss(amount)
if(dna.species && amount > 0)
amount = amount * dna.species.clone_mod
@@ -342,7 +335,5 @@ This function restores all organs.
..(damage, damagetype, def_zone, blocked)
return 1
//Handle BRUTE and BURN damage
handle_suit_punctures(damagetype, damage)
//Handle species apply_damage procs
return dna.species.apply_damage(damage, damagetype, def_zone, blocked, src, sharp, used_weapon)
@@ -531,17 +531,6 @@ emp_act
w_uniform.add_mob_blood(source)
update_inv_w_uniform()
/mob/living/carbon/human/proc/handle_suit_punctures(var/damtype, var/damage)
if(!wear_suit) return
if(!istype(wear_suit,/obj/item/clothing/suit/space)) return
if(damtype != BURN && damtype != BRUTE) return
var/obj/item/clothing/suit/space/SS = wear_suit
var/penetrated_dam = max(0,(damage - max(0,(SS.breach_threshold - SS.damage))))
if(penetrated_dam) SS.create_breaches(damtype, penetrated_dam)
/mob/living/carbon/human/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE)
if(user.a_intent == INTENT_HARM)
if(HAS_TRAIT(user, TRAIT_PACIFISM))
@@ -16,12 +16,6 @@
else if(istype(wear_suit, /obj/item/clothing/suit/space/hardsuit))
var/obj/item/clothing/suit/space/hardsuit/C = wear_suit
thrust = C.jetpack
else if(istype(back,/obj/item/rig))
var/obj/item/rig/rig = back
for(var/obj/item/rig_module/maneuvering_jets/module in rig.installed_modules)
thrust = module.jets
break
if(thrust)
if((movement_dir || thrust.stabilizers) && thrust.allow_thrust(0.01, src))
return 1
@@ -302,14 +302,6 @@
if(!(head && head.flags & AIRTIGHT)) //if NOT (head AND head.flags CONTAIN AIRTIGHT)
null_internals = 1 //not wearing a mask or suitable helmet
if(istype(back, /obj/item/rig)) //wearing a rigsuit
var/obj/item/rig/rig = back //needs to be typecasted because this doesn't use get_rig() for some reason
if(rig.offline && (rig.air_supply && internal == rig.air_supply)) //if rig IS offline AND (rig HAS air_supply AND internal IS air_supply)
null_internals = 1 //offline suits do not breath
else if(rig.air_supply && internal == rig.air_supply) //if rig HAS air_supply AND internal IS rig air_supply
skip_contents_check = 1 //skip contents.Find() check, the oxygen is valid even being outside of the mob
if(!contents.Find(internal) && (!skip_contents_check)) //if internal NOT IN contents AND skip_contents_check IS false
null_internals = 1 //not a rigsuit and your oxygen is gone
@@ -62,11 +62,6 @@
return ..()
/mob/living/carbon/human/proc/HasVoiceChanger()
if(istype(back, /obj/item/rig))
var/obj/item/rig/rig = back
if(rig.speech && rig.speech.voice_holder && rig.speech.voice_holder.active && rig.speech.voice_holder.voice)
return rig.speech.voice_holder.voice
for(var/obj/item/gear in list(wear_mask, wear_suit, head))
if(!gear)
continue
@@ -519,9 +519,6 @@
/datum/species/proc/spec_attack_hand(mob/living/carbon/human/M, mob/living/carbon/human/H, datum/martial_art/attacker_style = M.martial_art) //Handles any species-specific attackhand events.
if(!istype(M))
return
if(H.frozen)
to_chat(M, "<span class='warning'>Do not touch Admin-Frozen people.</span>")
return
if(istype(M))
var/obj/item/organ/external/temp = M.bodyparts_by_name["r_hand"]
@@ -817,20 +814,6 @@ It'll return null if the organ doesn't correspond, so include null checks when u
if(!isnull(hat.lighting_alpha))
H.lighting_alpha = min(hat.lighting_alpha, H.lighting_alpha)
if(istype(H.back, /obj/item/rig)) ///aghhh so snowflakey
var/obj/item/rig/rig = H.back
if(rig.visor)
if(!rig.helmet || (H.head && rig.helmet == H.head))
if(rig.visor && rig.visor.vision && rig.visor.active && rig.visor.vision.glasses)
var/obj/item/clothing/glasses/G = rig.visor.vision.glasses
if(istype(G))
H.sight |= G.vision_flags
H.see_in_dark = max(G.see_in_dark, H.see_in_dark)
H.see_invisible = min(G.invis_view, H.see_invisible)
if(!isnull(G.lighting_alpha))
H.lighting_alpha = min(G.lighting_alpha, H.lighting_alpha)
if(H.vision_type)
H.sight |= H.vision_type.sight_flags
H.see_in_dark = max(H.see_in_dark, H.vision_type.see_in_dark)
@@ -273,10 +273,10 @@ GLOBAL_LIST_EMPTY(damage_icon_parts)
overlays_standing[UNDERWEAR_LAYER] = mutable_appearance(underwear_standing, layer = -UNDERWEAR_LAYER)
apply_overlay(UNDERWEAR_LAYER)
if(lip_style && (LIPS in dna.species.species_traits))
var/icon/lips = icon("icon" = 'icons/mob/human_face.dmi', "icon_state" = "lips_[lip_style]_s")
lips.Blend(lip_color, ICON_ADD)
standing += mutable_appearance(lips, layer = -BODY_LAYER)
if(lip_style && (LIPS in dna.species.species_traits))
var/mutable_appearance/lips = mutable_appearance('icons/mob/human_face.dmi', "lips_[lip_style]_s")
lips.color = lip_color
standing += lips
overlays_standing[BODY_LAYER] = standing
apply_overlay(BODY_LAYER)
@@ -964,10 +964,6 @@ GLOBAL_LIST_EMPTY(damage_icon_parts)
var/mutable_appearance/standing
if(back.icon_override)
standing = mutable_appearance(back.icon_override, "[back.icon_state]", layer = -BACK_LAYER)
else if(istype(back, /obj/item/rig))
//If this is a rig and a mob_icon is set, it will take species into account in the rig update_icon() proc.
var/obj/item/rig/rig = back
standing = rig.mob_icon
else if(back.sprite_sheets && back.sprite_sheets[dna.species.name])
standing = mutable_appearance(back.sprite_sheets[dna.species.name], "[back.icon_state]", layer = -BACK_LAYER)
else
+40 -25
View File
@@ -3,8 +3,29 @@
var/datum/atom_hud/data/human/medical/advanced/medhud = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED]
medhud.add_to_hud(src)
faction += "\ref[src]"
determine_move_and_pull_forces()
GLOB.mob_living_list += src
// Used to determine the forces dependend on the mob size
// Will only change the force if the force was not set in the mob type itself
/mob/living/proc/determine_move_and_pull_forces()
var/value
switch(mob_size)
if(MOB_SIZE_TINY)
value = MOVE_FORCE_EXTREMELY_WEAK
if(MOB_SIZE_SMALL)
value = MOVE_FORCE_WEAK
if(MOB_SIZE_HUMAN)
value = MOVE_FORCE_NORMAL
if(MOB_SIZE_LARGE)
value = MOVE_FORCE_NORMAL // For now
if(!move_force)
move_force = value
if(!pull_force)
pull_force = value
if(!move_resist)
move_resist = value
/mob/living/prepare_huds()
..()
prepare_data_huds()
@@ -59,8 +80,9 @@
//Even if we don't push/swap places, we "touched" them, so spread fire
spreadFire(M)
if(now_pushing)
return 1
// No pushing if we're already pushing past something, or if the mob we're pushing into is anchored.
if(now_pushing || M.anchored)
return TRUE
//Should stop you pushing a restrained person out of the way
if(isliving(M))
@@ -68,7 +90,7 @@
if(L.pulledby && L.pulledby != src && L.restrained())
if(!(world.time % 5))
to_chat(src, "<span class='warning'>[L] is restrained, you cannot push past.</span>")
return 1
return TRUE
if(L.pulling)
if(ismob(L.pulling))
@@ -76,28 +98,28 @@
if(P.restrained())
if(!(world.time % 5))
to_chat(src, "<span class='warning'>[L] is restrained, you cannot push past.</span>")
return 1
return TRUE
if(moving_diagonally) //no mob swap during diagonal moves.
return 1
return TRUE
if(a_intent == INTENT_HELP) // Help intent doesn't mob swap a mob pulling a structure
if(isstructure(M.pulling) || isstructure(pulling))
return 1
return TRUE
if(!M.buckled && !M.has_buckled_mobs())
var/mob_swap
//the puller can always swap with it's victim if on grab intent
if(M.pulledby == src && a_intent == INTENT_GRAB)
mob_swap = 1
mob_swap = TRUE
//restrained people act if they were on 'help' intent to prevent a person being pulled from being seperated from their puller
else if((M.restrained() || M.a_intent == INTENT_HELP) && (restrained() || a_intent == INTENT_HELP))
mob_swap = 1
mob_swap = TRUE
if(mob_swap)
//switch our position with M
if(loc && !loc.Adjacent(M.loc))
return 1
now_pushing = 1
return TRUE
now_pushing = TRUE
var/oldloc = loc
var/oldMloc = M.loc
@@ -114,18 +136,18 @@
if(!M_passmob)
M.pass_flags &= ~PASSMOB
now_pushing = 0
return 1
now_pushing = FALSE
return TRUE
// okay, so we didn't switch. but should we push?
// not if he's not CANPUSH of course
if(!(M.status_flags & CANPUSH))
return 1
return TRUE
//anti-riot equipment is also anti-push
if(M.r_hand && (prob(M.r_hand.block_chance * 2)) && !istype(M.r_hand, /obj/item/clothing))
return 1
return TRUE
if(M.l_hand && (prob(M.l_hand.block_chance * 2)) && !istype(M.l_hand, /obj/item/clothing))
return 1
return TRUE
//Called when we bump into an obj
/mob/living/proc/ObjBump(obj/O)
@@ -175,13 +197,6 @@
AM.setDir(current_dir)
now_pushing = FALSE
/mob/living/Stat()
. = ..()
if(. && get_rig_stats)
var/obj/item/rig/rig = get_rig()
if(rig)
SetupStat(rig)
/mob/living/proc/can_track(mob/living/user)
//basic fast checks go first. When overriding this proc, I recommend calling ..() at the end.
var/turf/T = get_turf(src)
@@ -209,7 +224,7 @@
set category = "Object"
if(istype(AM) && Adjacent(AM))
start_pulling(AM)
start_pulling(AM, show_message = TRUE)
else
stop_pulling()
@@ -927,10 +942,10 @@
return 0
return 1
/mob/living/start_pulling(atom/movable/AM, state, force = pull_force, supress_message = FALSE)
/mob/living/start_pulling(atom/movable/AM, state, force = pull_force, show_message = FALSE)
if(!AM || !src)
return FALSE
if(!(AM.can_be_pulled(src, state, force)))
if(!(AM.can_be_pulled(src, state, force, show_message)))
return FALSE
if(incapacitated())
return
+2 -2
View File
@@ -113,7 +113,7 @@
var/armor = run_armor_check(zone, "melee", "Your armor has protected your [parse_zone(zone)].", "Your armor has softened hit to your [parse_zone(zone)].", I.armour_penetration)
apply_damage(I.throwforce, dtype, zone, armor, is_sharp(I), I)
if(I.thrownby)
add_attack_logs(I.thrownby, src, "Hit with thrown [I]")
add_attack_logs(I.thrownby, src, "Hit with thrown [I]", !I.throwforce ? ATKLOG_ALMOSTALL : null) // Only message if the person gets damages
else
return 1
else
@@ -219,7 +219,7 @@
fire_stacks += L.fire_stacks
IgniteMob()
/mob/living/can_be_pulled(user, grab_state, force)
/mob/living/can_be_pulled(user, grab_state, force, show_message = FALSE)
return ..() && !(buckled && buckled.buckle_prevents_pull)
/mob/living/water_act(volume, temperature, source, method = REAGENT_TOUCH)
@@ -2,6 +2,11 @@
see_invisible = SEE_INVISIBLE_LIVING
pressure_resistance = 10
// Will be determined based on mob size if left null. Done in living/proc/determine_move_and_pull_forces()
move_resist = null
move_force = null
pull_force = null
//Health and life related vars
var/maxHealth = 100 //Maximum health that should be possible.
var/health = 100 //A mob's health
+2 -2
View File
@@ -451,14 +451,14 @@ proc/get_radio_key_from_channel(var/channel)
var/speech_bubble_test = say_test(message)
for(var/mob/M in listening)
M.hear_say(message_pieces, verb, italics, src)
M.hear_say(message_pieces, verb, italics, src, use_voice = FALSE)
if(M.client)
speech_bubble_recipients.Add(M.client)
if(eavesdropping.len)
stars_all(message_pieces) //hopefully passing the message twice through stars() won't hurt... I guess if you already don't understand the language, when they speak it too quietly to hear normally you would be able to catch even less.
for(var/mob/M in eavesdropping)
M.hear_say(message_pieces, verb, italics, src)
M.hear_say(message_pieces, verb, italics, src, use_voice = FALSE)
if(M.client)
speech_bubble_recipients.Add(M.client)
+11 -27
View File
@@ -63,6 +63,7 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
//MALFUNCTION
var/datum/module_picker/malf_picker
var/datum/action/innate/ai/choose_modules/modules_action
var/list/datum/AI_Module/current_modules = list()
var/can_dominate_mechs = 0
var/shunted = 0 //1 if the AI is currently shunted. Used to differentiate between shunted and ghosted/braindead
@@ -851,13 +852,6 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
to_chat(src, "<span class='notice'>Switched to [network] camera network.</span>")
//End of code by Mord_Sith
/mob/living/silicon/ai/proc/choose_modules()
set category = "Malfunction"
set name = "Choose Module"
malf_picker.use(src)
/mob/living/silicon/ai/proc/ai_statuschange()
set category = "AI Commands"
set name = "AI Status"
@@ -1028,15 +1022,6 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
return
/mob/living/silicon/ai/proc/corereturn()
set category = "Malfunction"
set name = "Return to Main Core"
var/obj/machinery/power/apc/apc = loc
if(!istype(apc))
to_chat(src, "<span class='notice'>You are already in your Main Core.</span>")
return
apc.malfvacate()
//Toggles the luminosity and applies it by re-entereing the camera.
/mob/living/silicon/ai/proc/toggle_camera_light()
@@ -1181,16 +1166,6 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
/mob/living/silicon/ai/can_buckle()
return FALSE
// Pass lying down or getting up to our pet human, if we're in a rig.
/mob/living/silicon/ai/lay_down()
set name = "Rest"
set category = "IC"
resting = 0
var/obj/item/rig/rig = get_rig()
if(rig)
rig.force_rest(src)
/mob/living/silicon/ai/switch_to_camera(obj/machinery/camera/C)
if(!C.can_use() || !is_in_chassis())
return FALSE
@@ -1244,8 +1219,17 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
to_chat(src, "In the top right corner of the screen you will find the Malfunctions tab, where you can purchase various abilities, from upgraded surveillance to station ending doomsday devices.")
to_chat(src, "You are also capable of hacking APCs, which grants you more points to spend on your Malfunction powers. The drawback is that a hacked APC will give you away if spotted by the crew. Hacking an APC takes 60 seconds.")
view_core() //A BYOND bug requires you to be viewing your core before your verbs update
verbs += /mob/living/silicon/ai/proc/choose_modules
malf_picker = new /datum/module_picker
modules_action = new(malf_picker)
modules_action.Grant(src)
///Removes all malfunction-related /datum/action's from the target AI.
/mob/living/silicon/ai/proc/remove_malf_abilities()
QDEL_NULL(modules_action)
for(var/datum/AI_Module/AM in current_modules)
for(var/datum/action/A in actions)
if(istype(A, initial(AM.power_type)))
qdel(A)
/mob/living/silicon/ai/proc/open_nearest_door(mob/living/target)
if(!istype(target))
@@ -142,7 +142,7 @@
acceleration = !acceleration
to_chat(usr, "Camera acceleration has been toggled [acceleration ? "on" : "off"].")
/mob/camera/aiEye/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency)
/mob/camera/aiEye/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency, use_voice = TRUE)
if(relay_speech)
if(istype(ai))
ai.relay_speech(speaker, message_pieces, verb)
+1 -4
View File
@@ -435,9 +435,6 @@
// Pass lying down or getting up to our pet human, if we're in a rig.
if(stat == CONSCIOUS && istype(loc,/obj/item/paicard))
resting = 0
var/obj/item/rig/rig = get_rig()
if(istype(rig))
rig.force_rest(src)
else
resting = !resting
to_chat(src, "<span class='notice'>You are now [resting ? "resting" : "getting up"]</span>")
@@ -520,7 +517,7 @@
/mob/living/silicon/pai/Bumped()
return
/mob/living/silicon/pai/start_pulling(atom/movable/AM, state, force = move_force, supress_message = FALSE)
/mob/living/silicon/pai/start_pulling(atom/movable/AM, state, force = pull_force, show_message = FALSE)
return FALSE
/mob/living/silicon/pai/update_canmove(delay_action_updates = 0)
@@ -19,6 +19,7 @@
ventcrawler = 2
magpulse = 1
mob_size = MOB_SIZE_SMALL
pull_force = MOVE_FORCE_VERY_WEAK // Can only drag small items
modules_break = FALSE
@@ -35,6 +36,14 @@
var/reboot_cooldown = 60 // one minute
var/last_reboot
var/emagged_time
var/list/pullable_drone_items = list(
/obj/item/pipe,
/obj/structure/disposalconstruct,
/obj/item/stack/cable_coil,
/obj/item/stack/rods,
/obj/item/stack/sheet,
/obj/item/stack/tile
)
holder_type = /obj/item/holder/drone
// var/sprite[0]
@@ -69,6 +78,10 @@
verbs -= /mob/living/silicon/robot/verb/Namepick
module = new /obj/item/robot_module/drone(src)
//Allows Drones to hear the Engineering channel.
module.channels = list("Engineering" = 1)
radio.recalculateChannels()
//Grab stacks.
stack_metal = locate(/obj/item/stack/sheet/metal/cyborg) in src.module
stack_wood = locate(/obj/item/stack/sheet/wood) in src.module
@@ -154,15 +167,17 @@
return
else
user.visible_message("<span class='warning'>\the [user] swipes [user.p_their()] ID card through [src], attempting to shut it down.</span>", "<span class='warning'>You swipe your ID card through \the [src], attempting to shut it down.</span>")
var/confirm = alert("Using your ID on a Maintenance Drone will shut it down, are you sure you want to do this?", "Disable Drone", "Yes", "No")
if(confirm == ("Yes") && (user in range(3, src)))
user.visible_message("<span class='warning'>\the [user] swipes [user.p_their()] ID card through [src], attempting to shut it down.</span>", "<span class='warning'>You swipe your ID card through \the [src], attempting to shut it down.</span>")
if(emagged)
return
if(emagged)
return
if(allowed(W))
shut_down()
else
to_chat(user, "<span class='warning'>Access denied.</span>")
if(allowed(W))
shut_down()
else
to_chat(user, "<span class='warning'>Access denied.</span>")
return
@@ -321,20 +336,22 @@
/mob/living/silicon/robot/drone/Bumped(atom/movable/AM)
return
/mob/living/silicon/robot/drone/start_pulling(var/atom/movable/AM)
/mob/living/silicon/robot/drone/start_pulling(atom/movable/AM, state, force = pull_force, show_message = FALSE)
if(is_type_in_list(AM, pullable_drone_items))
..(AM, force = INFINITY) // Drone power! Makes them able to drag pipes and such
if(istype(AM,/obj/item/pipe) || istype(AM,/obj/structure/disposalconstruct))
..()
else if(istype(AM,/obj/item))
var/obj/item/O = AM
if(O.w_class > WEIGHT_CLASS_SMALL)
to_chat(src, "<span class='warning'>You are too small to pull that.</span>")
if(show_message)
to_chat(src, "<span class='warning'>You are too small to pull that.</span>")
return
else
..()
else
to_chat(src, "<span class='warning'>You are too small to pull that.</span>")
return
if(show_message)
to_chat(src, "<span class='warning'>You are too small to pull that.</span>")
/mob/living/silicon/robot/drone/add_robot_verbs()
src.verbs |= silicon_subsystems
@@ -354,3 +371,20 @@
/mob/living/simple_animal/drone/flash_eyes(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0)
if(affect_silicon)
return ..()
/mob/living/silicon/robot/drone/decompile_act(obj/item/matter_decompiler/C, mob/user)
if(!client && istype(user, /mob/living/silicon/robot/drone))
to_chat(user, "<span class='warning'>You begin decompiling the other drone.</span>")
if(!do_after(user, 5 SECONDS, target = loc))
to_chat(user, "<span class='warning'>You need to remain still while decompiling such a large object.</span>")
return
if(QDELETED(src) || QDELETED(user))
return ..()
to_chat(user, "<span class='warning'>You carefully and thoroughly decompile your downed fellow, storing as much of its resources as you can within yourself.</span>")
new/obj/effect/decal/cleanable/blood/oil(get_turf(src))
C.stored_comms["metal"] += 15
C.stored_comms["glass"] += 15
C.stored_comms["wood"] += 5
qdel(src)
return TRUE
return ..()
@@ -8,7 +8,6 @@
//Has a list of items that it can hold.
var/list/can_hold = list(
/obj/item/stock_parts/cell,
/obj/item/firealarm_electronics,
/obj/item/airalarm_electronics,
/obj/item/airlock_electronics,
@@ -24,6 +23,8 @@
/obj/item/mounted/frame/firealarm,
/obj/item/mounted/frame/newscaster_frame,
/obj/item/mounted/frame/intercom,
/obj/item/mounted/frame/extinguisher,
/obj/item/mounted/frame/light_switch,
/obj/item/rack_parts,
/obj/item/camera_assembly,
/obj/item/tank,
@@ -151,15 +152,13 @@
var/list/stored_comms = list(
"metal" = 0,
"glass" = 0,
"wood" = 0,
"plastic" = 0
"wood" = 0
)
/obj/item/matter_decompiler/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob)
return
/obj/item/matter_decompiler/afterattack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, proximity, params)
/obj/item/matter_decompiler/afterattack(atom/target, mob/living/user, proximity, params)
if(!proximity) return //Not adjacent.
//We only want to deal with using this on turfs. Specific items aren't important.
@@ -168,101 +167,11 @@
return
//Used to give the right message.
var/grabbed_something = 0
var/grabbed_something = FALSE
for(var/mob/M in T)
if(istype(M,/mob/living/simple_animal/lizard) || istype(M,/mob/living/simple_animal/mouse))
src.loc.visible_message("<span class='notice'>[src.loc] sucks [M] into its decompiler. There's a horrible crunching noise.</span>","<span class='warning'>It's a bit of a struggle, but you manage to suck [M] into your decompiler. It makes a series of visceral crunching noises.</span>")
new/obj/effect/decal/cleanable/blood/splatter(get_turf(src))
qdel(M)
stored_comms["wood"]++
stored_comms["wood"]++
stored_comms["plastic"]++
stored_comms["plastic"]++
return
else if(istype(M,/mob/living/silicon/robot/drone) && !M.client)
var/mob/living/silicon/robot/drone/D = src.loc
if(!istype(D))
return
to_chat(D, "<span class='warning'>You begin decompiling the other drone.</span>")
if(!do_after(D, 50, target = target))
to_chat(D, "<span class='warning'>You need to remain still while decompiling such a large object.</span>")
return
if(!M || !D) return
to_chat(D, "<span class='warning'>You carefully and thoroughly decompile your downed fellow, storing as much of its resources as you can within yourself.</span>")
qdel(M)
new/obj/effect/decal/cleanable/blood/oil(get_turf(src))
stored_comms["metal"] += 15
stored_comms["glass"] += 15
stored_comms["wood"] += 5
stored_comms["plastic"] += 5
return
else
continue
for(var/obj/W in T)
//Different classes of items give different commodities.
if(istype(W,/obj/item/cigbutt))
stored_comms["plastic"]++
else if(istype(W,/obj/structure/spider/spiderling))
stored_comms["wood"]++
stored_comms["wood"]++
stored_comms["plastic"]++
stored_comms["plastic"]++
else if(istype(W,/obj/item/light))
var/obj/item/light/L = W
if(L.status >= 2) //In before someone changes the inexplicably local defines. ~ Z
stored_comms["metal"]++
stored_comms["glass"]++
else
continue
else if(istype(W,/obj/effect/decal/remains/robot))
stored_comms["metal"]++
stored_comms["metal"]++
stored_comms["plastic"]++
stored_comms["plastic"]++
stored_comms["glass"]++
else if(istype(W,/obj/item/trash))
stored_comms["metal"]++
stored_comms["plastic"]++
stored_comms["plastic"]++
else if(istype(W,/obj/effect/decal/cleanable/blood/gibs/robot))
stored_comms["metal"]++
stored_comms["metal"]++
stored_comms["glass"]++
stored_comms["glass"]++
else if(istype(W,/obj/item/ammo_casing))
stored_comms["metal"]++
else if(istype(W,/obj/item/shard))
stored_comms["glass"]++
stored_comms["glass"]++
stored_comms["glass"]++
else if(istype(W,/obj/item/reagent_containers/food/snacks/grown))
stored_comms["wood"]++
stored_comms["wood"]++
stored_comms["wood"]++
stored_comms["wood"]++
else if(istype(W,/obj/item/broken_bottle))
stored_comms["glass"]++
stored_comms["glass"]++
stored_comms["glass"]++
else if(istype(W,/obj/item/light/tube) || istype(W,/obj/item/light/bulb))
stored_comms["glass"]++
else
continue
qdel(W)
grabbed_something = 1
for(var/atom/movable/A in T)
if(A.decompile_act(src, user)) // Each decompileable mob or obj needs to have this defined
grabbed_something = TRUE
if(grabbed_something)
to_chat(user, "<span class='notice'>You deploy your decompiler and clear out the contents of \the [T].</span>")
@@ -353,11 +262,5 @@
stack_wood = new /obj/item/stack/sheet/wood(src.module)
stack_wood.amount = 1
stack = stack_wood
if("plastic")
if(!stack_plastic)
stack_plastic = new /obj/item/stack/sheet/plastic(src.module)
stack_plastic.amount = 1
stack = stack_plastic
stack.amount++
decompiler.stored_comms[type]--
@@ -1439,7 +1439,9 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
default_cell_type = /obj/item/stock_parts/cell/bluespace
/mob/living/silicon/robot/destroyer/init(alien = FALSE, connect_to_AI = TRUE, mob/living/silicon/ai/ai_to_sync_to = null)
..()
aiCamera = new/obj/item/camera/siliconcam/robot_camera(src)
additional_law_channels["Binary"] = ":b "
laws = new /datum/ai_laws/deathsquad
module = new /obj/item/robot_module/destroyer(src)
module.add_languages(src)
module.add_subsystems_and_actions(src)
@@ -1448,6 +1450,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
qdel(radio)
radio = new /obj/item/radio/borg/ert/specops(src)
radio.recalculateChannels()
playsound(loc, 'sound/mecha/nominalsyndi.ogg', 75, 0)
/mob/living/silicon/robot/destroyer/borg_icons()
if(base_icon == "")
@@ -756,7 +756,7 @@ Pass a positive integer as an argument to override a bot's default speed.
// send a radio signal with multiple data key/values
/mob/living/simple_animal/bot/proc/post_signal_multiple(var/freq, var/list/keyval)
if(z != 1) //Bot control will only work on station.
if(!is_station_level(z)) //Bot control will only work on station.
return
var/datum/radio_frequency/frequency = SSradio.return_frequency(freq)
if(!frequency)
@@ -474,8 +474,7 @@
return
build_step++
to_chat(user, "<span class='notice'>You complete the Securitron! Beep boop.</span>")
var/mob/living/simple_animal/bot/secbot/S = new /mob/living/simple_animal/bot/secbot
S.forceMove(get_turf(src))
var/mob/living/simple_animal/bot/secbot/S = new /mob/living/simple_animal/bot/secbot(get_turf(src))
S.name = created_name
S.robot_arm = robot_arm
qdel(I)
@@ -512,7 +511,7 @@
//General Griefsky
else if((istype(I, /obj/item/wrench)) && (build_step == 3))
var/obj/item/griefsky_assembly/A = new /obj/item/griefsky_assembly
var/obj/item/griefsky_assembly/A = new /obj/item/griefsky_assembly(get_turf(src))
user.put_in_hands(A)
to_chat(user, "<span class='notice'>You adjust the arm slots for extra weapons!.</span>")
user.unEquip(src, 1)
@@ -540,8 +539,7 @@
if(!user.unEquip(I))
return
to_chat(user, "<span class='notice'>You complete General Griefsky!.</span>")
var/mob/living/simple_animal/bot/secbot/griefsky/S = new /mob/living/simple_animal/bot/secbot/griefsky
S.forceMove(get_turf(src))
new /mob/living/simple_animal/bot/secbot/griefsky(get_turf(src))
qdel(I)
qdel(src)
@@ -556,8 +554,7 @@
if(!user.unEquip(I))
return
to_chat(user, "<span class='notice'>You complete Genewul Giftskee!.</span>")
var/mob/living/simple_animal/bot/secbot/griefsky/toy/S = new /mob/living/simple_animal/bot/secbot/griefsky/toy
S.forceMove(get_turf(src))
new /mob/living/simple_animal/bot/secbot/griefsky/toy(get_turf(src))
qdel(I)
qdel(src)
@@ -123,6 +123,7 @@
for(var/mob/living/simple_animal/mouse/M in view(1, src))
if(!M.stat && Adjacent(M))
custom_emote(1, "splats \the [M]!")
M.death()
M.splat()
movement_target = null
stop_automated_movement = 0
@@ -126,6 +126,7 @@
gold_core_spawnable = FRIENDLY_SPAWN
blood_volume = BLOOD_VOLUME_NORMAL
var/obj/item/udder/udder = null
gender = FEMALE
/mob/living/simple_animal/cow/Initialize()
udder = new()
@@ -23,3 +23,14 @@
butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 1)
can_collar = 1
gold_core_spawnable = FRIENDLY_SPAWN
/mob/living/simple_animal/lizard/decompile_act(obj/item/matter_decompiler/C, mob/user)
if(!istype(user, /mob/living/silicon/robot/drone))
user.visible_message("<span class='notice'>[user] sucks [src] into its decompiler. There's a horrible crunching noise.</span>", \
"<span class='warning'>It's a bit of a struggle, but you manage to suck [src] into your decompiler. It makes a series of visceral crunching noises.</span>")
new/obj/effect/decal/cleanable/blood/splatter(get_turf(src))
C.stored_comms["wood"] += 2
C.stored_comms["glass"] += 2
qdel(src)
return TRUE
return ..()
@@ -80,22 +80,16 @@
icon_resting = "mouse_[mouse_color]_sleep"
desc = "It's a small [mouse_color] rodent, often seen hiding in maintenance areas and making a nuisance of itself."
/mob/living/simple_animal/mouse/proc/splat()
src.health = 0
src.stat = DEAD
src.icon_dead = "mouse_[mouse_color]_splat"
src.icon_state = "mouse_[mouse_color]_splat"
layer = MOB_LAYER
if(client)
client.time_died_as_mouse = world.time
/mob/living/simple_animal/mouse/attack_hand(mob/living/carbon/human/M as mob)
if(M.a_intent == INTENT_HELP)
get_scooped(M)
..()
/mob/living/simple_animal/mouse/start_pulling(var/atom/movable/AM)//Prevents mouse from pulling things
to_chat(src, "<span class='warning'>You are too small to pull anything.</span>")
/mob/living/simple_animal/mouse/start_pulling(atom/movable/AM, state, force = pull_force, show_message = FALSE)//Prevents mouse from pulling things
if(istype(AM, /obj/item/reagent_containers/food/snacks/cheesewedge))
return ..() // Get dem
if(show_message)
to_chat(src, "<span class='warning'>You are too small to pull anything except cheese.</span>")
return
/mob/living/simple_animal/mouse/Crossed(AM as mob|obj, oldloc)
@@ -110,6 +104,10 @@
desc = "It's toast."
death()
/mob/living/simple_animal/mouse/proc/splat()
icon_dead = "mouse_[mouse_color]_splat"
icon_state = "mouse_[mouse_color]_splat"
/mob/living/simple_animal/mouse/death(gibbed)
// Only execute the below if we successfully died
playsound(src, squeak_sound, 40, 1)
@@ -234,3 +232,14 @@
gold_core_spawnable = NO_SPAWN
can_collar = 0
butcher_results = list(/obj/item/stack/sheet/metal = 1)
/mob/living/simple_animal/mouse/decompile_act(obj/item/matter_decompiler/C, mob/user)
if(!(istype(user, /mob/living/silicon/robot/drone)))
user.visible_message("<span class='notice'>[user] sucks [src] into its decompiler. There's a horrible crunching noise.</span>", \
"<span class='warning'>It's a bit of a struggle, but you manage to suck [src] into your decompiler. It makes a series of visceral crunching noises.</span>")
new/obj/effect/decal/cleanable/blood/splatter(get_turf(src))
C.stored_comms["wood"] += 2
C.stored_comms["glass"] += 2
qdel(src)
return TRUE
return ..()
@@ -95,6 +95,10 @@
for(var/mob/A in searched_for)
. += A
// All bee sprites are made up of overlays. They do not have any special sprite overlays for items placed on them, such as collars, so this proc is unneeded.
/mob/living/simple_animal/hostile/poison/bees/regenerate_icons()
return
/mob/living/simple_animal/hostile/poison/bees/proc/generate_bee_visuals()
overlays.Cut()
@@ -51,6 +51,10 @@
if(!(eggtype in eggtypes))
to_chat(src, "<span class='danger'>Unrecognized egg type.</span>")
return 0
if(fed < feedings_to_lay)
// We have to check this again after the popup, to account for people spam-clicking the button, then doing all the popups at once.
to_chat(src, "<span class='warning'>You must wrap more humanoid prey before you can do this!</span>")
return
visible_message("<span class='notice'>[src] lays a cluster of eggs.</span>")
if(eggtype == TS_DESC_RED)
DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/red, 1)
@@ -15,7 +15,7 @@
ai_target_method = TS_DAMAGE_SIMPLE
icon_state = "terror_princess1"
icon_living = "terror_princess1"
icon_dead = "terror_princess_dead1"
icon_dead = "terror_princess1_dead"
maxHealth = 150
health = 150
regen_points_per_hp = 1 // always regens very fast
@@ -51,7 +51,7 @@
if(fed == 0)
icon_state = "terror_princess1"
icon_living = "terror_princess1"
icon_dead = "terror_princess_dead1"
icon_dead = "terror_princess1_dead"
desc = "An enormous spider. It looks strangely cute and fluffy, with soft pink fur covering most of its body."
else if(fed == 1)
icon_state = "terror_princess2"
@@ -287,6 +287,10 @@
if(eggtype == null || numlings == null)
to_chat(src, "<span class='danger'>Cancelled.</span>")
return
if(canlay < numlings)
// We have to check this again after the popups, to account for people spam-clicking the button, then doing all the popups at once.
to_chat(src, "<span class='warning'>Too soon to do this again!</span>")
return
canlay -= numlings
eggslaid += numlings
if(eggtype == TS_DESC_RED)
@@ -710,7 +710,7 @@
ears.talk_into(src, message_pieces, message_mode, verb)
used_radios += ears
/mob/living/simple_animal/parrot/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency)
/mob/living/simple_animal/parrot/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency, use_voice = TRUE)
if(speaker != src && prob(50))
parrot_hear(html_decode(multilingual_to_message(message_pieces)))
..()
@@ -32,8 +32,9 @@
animate_ghostly_presence(src, -1, 20, 1) // Restart the floating animation after the attack animation, as it will be cancelled.
/mob/living/simple_animal/possessed_object/start_pulling(var/atom/movable/AM) // Silly motherfuckers think they can pull things.
to_chat(src, "<span class='warning'>You are unable to pull [AM]!</span>")
/mob/living/simple_animal/possessed_object/start_pulling(atom/movable/AM, state, force = pull_force, show_message = FALSE) // Silly motherfuckers think they can pull things.
if(show_message)
to_chat(src, "<span class='warning'>You are unable to pull [AM]!</span>")
/mob/living/simple_animal/possessed_object/ghost() // Ghosting will return the object to normal, and will not disqualify the ghoster from various mid-round antag positions.
@@ -615,3 +615,8 @@
if(pcollar && collar_type)
add_overlay("[collar_type]collar")
add_overlay("[collar_type]tag")
/mob/living/simple_animal/Login()
..()
walk(src, 0) // if mob is moving under ai control, then stop AI movement
@@ -9,7 +9,7 @@
return verb
/mob/living/simple_animal/slime/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency)
/mob/living/simple_animal/slime/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency, use_voice = TRUE)
if(speaker != src && !stat)
if(speaker in Friends)
speech_buffer = list()
@@ -265,7 +265,7 @@
/mob/living/simple_animal/slime/unEquip(obj/item/I, force)
return
/mob/living/simple_animal/slime/start_pulling(atom/movable/AM, state, force = move_force, supress_message = FALSE)
/mob/living/simple_animal/slime/start_pulling(atom/movable/AM, state, force = pull_force, show_message = FALSE)
return
/mob/living/simple_animal/slime/attack_ui(slot)
+1
View File
@@ -28,6 +28,7 @@
/mob/Login()
GLOB.player_list |= src
last_known_ckey = ckey
update_Login_details()
world.update_status()
+9 -4
View File
@@ -19,7 +19,6 @@
for(var/datum/alternate_appearance/AA in viewing_alternate_appearances)
AA.viewers -= src
viewing_alternate_appearances = null
logs.Cut()
LAssailant = null
return ..()
@@ -880,6 +879,9 @@ GLOBAL_LIST_INIT(slot_equipment_priority, list( \
return
if(!Adjacent(usr))
return
if(IsFrozen(src) && !is_admin(usr))
to_chat(usr, "<span class='boldannounce'>Interacting with admin-frozen players is not permitted.</span>")
return
if(isLivingSSD(src) && M.client && M.client.send_ssd_warning(src))
return
show_inv(usr)
@@ -1235,10 +1237,13 @@ GLOBAL_LIST_INIT(slot_equipment_priority, list( \
create_log_in_list(debug_log, text, collapse, world.timeofday)
/mob/proc/create_log(log_type, what, target = null, turf/where = get_turf(src))
LAZYINITLIST(logs[log_type])
var/list/log_list = logs[log_type]
if(!ckey)
return
var/real_ckey = ckey
if(ckey[1] == "@") // Admin aghosting will do this
real_ckey = copytext(ckey, 2)
var/datum/log_record/record = new(log_type, src, what, target, where, world.time)
log_list.Add(record)
GLOB.logging.add_log(real_ckey, record)
/proc/create_log_in_list(list/target, text, collapse = TRUE, last_log)//forgive me code gods for this shitcode proc
//this proc enables lovely stuff like an attack log that looks like this: "[18:20:29-18:20:45]21x John Smith attacked Andrew Jackson with a crowbar."
+1 -1
View File
@@ -36,7 +36,7 @@
var/list/attack_log_old = list( )
var/list/debug_log = null
var/list/logs = list() // Logs for each log type defined in __DEFINES/logs.dm
var/last_known_ckey = null // Used in logging
var/last_log = 0
var/obj/machinery/machine = null
+1 -1
View File
@@ -113,7 +113,7 @@
var/question = "Do you want to play as [M.real_name ? M.real_name : M.name][M.job ? " ([M.job])" : ""]"
if(alert("Do you want to show the antag status?","Show antag status","Yes","No") == "Yes")
question += ", [M.mind?.special_role ? M.mind?.special_role : "No special role"]"
var/list/mob/dead/observer/candidates = pollCandidates("[question]?", poll_time = 100, min_hours = minhours)
var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("[question]?", poll_time = 10 SECONDS, min_hours = minhours, source = M)
var/mob/dead/observer/theghost = null
if(LAZYLEN(candidates))
+1 -1
View File
@@ -90,7 +90,7 @@
if(adminonly)
question = "(<font color='#997700'>Admin only poll</font>) " + question
var output = "<!DOCTYPE html><html><body>"
var/output = "<!DOCTYPE html><html><body>"
if(polltype == POLLTYPE_MULTI || polltype == POLLTYPE_OPTION)
select_query = GLOB.dbcon.NewQuery("SELECT text, percentagecalc, (SELECT COUNT(optionid) FROM [format_table_name("poll_vote")] WHERE optionid = poll_option.id GROUP BY optionid) AS votecount FROM [format_table_name("poll_option")] WHERE pollid = [pollid]")
select_query.Execute()
+1
View File
@@ -55,6 +55,7 @@
return
say_dead_direct("[pick("complains", "moans", "whines", "laments", "blubbers", "salts")], <span class='message'>\"[message]\"</span>", src)
create_log(DEADCHAT_LOG, message)
/mob/proc/say_understands(var/mob/other, var/datum/language/speaking = null)
if(stat == DEAD)
+2 -5
View File
@@ -92,14 +92,11 @@
O.job = "Cyborg"
if(O.mind && O.mind.assigned_role == "Cyborg")
if(O.mind.role_alt_title == "Android")
if(O.mind.role_alt_title == "Robot")
O.mmi = new /obj/item/mmi/robotic_brain(O)
else if(O.mind.role_alt_title == "Robot")
O.mmi = null //Robots do not have removable brains.
else
O.mmi = new /obj/item/mmi(O)
if(O.mmi) O.mmi.transfer_identity(src) //Does not transfer key/client.
O.mmi.transfer_identity(src) //Does not transfer key/client.
O.update_pipe_vision()