Files
Stephen 036a29b765 all mbc code from jan 1st to 2/16/2020
includes a lot of small things + fixes.
big ones are Simple Lights, New Grab stuff, Disorient Eye/Ear protection armor values, access reprogrammer and deconstruction device

also added flabo to the list of contributors, we missed her on the first go!

check changelog for full list, i merged that too
2020-02-17 01:36:01 -05:00

1138 lines
40 KiB
Plaintext

// living
/mob/living
var/spell_soulguard = 0
// this is a read only variable. do not set it directly.
// use set_burning or update_burning instead.
// the value means how on fire is the mob, from 0 to 100
var/datum/hud/vision/vision
//AI Vars
var/ai_busy = 0
var/ai_laststep = 0
var/ai_state = 0
var/ai_threatened = 0
var/ai_movedelay = 6
var/ai_lastaction = 0
var/ai_actiondelay = 10
var/ai_pounced = 0
var/ai_attacked = 0
var/ai_frustration = 0
var/ai_throw = 0
var/ai_attackadmins = 1
var/ai_attacknpc = 1
var/ai_suicidal = 0 //Will it attack itself?
var/ai_active = 0
var/blood_id = null
var/mob/living/ai_target = null
var/list/mob/living/ai_target_old = list()
var/is_npc = 0
var/move_laying = null
var/list/mob/dead/target_observer/observers = list()
var/static/image/speech_bubble = image('icons/mob/mob.dmi', "speech")
var/static/image/sleep_bubble = image('icons/mob/mob.dmi', "sleep")
var/image/static_image = null
var/static_type_override = null
var/icon/default_static_icon = null // set to an icon and that's what the static will generate from
var/in_point_mode = 0
var/butt_op_stage = 0.0 // sigh
var/dna_to_absorb = 10
var/canspeak = 1
var/datum/organHolder/organHolder = 0 //Not all living mobs will use organholder. Instantiate on New() if you want one.
var/list/stomach_process = list() //digesting foods
var/list/skin_process = list() //digesting patches
var/sound_burp = 'sound/voice/burp.ogg'
var/sound_scream = 'sound/voice/screams/robot_scream.ogg' // for silicon mobs
//var/sound_scream = 'sound/voice/screams/male_scream.ogg'
//var/sound_femalescream = 'sound/voice/screams/female_scream.ogg'
var/sound_flip1 = 'sound/machines/whistlealert.ogg' // for silicon mobs
var/sound_flip2 = 'sound/machines/whistlebeep.ogg' // for silicon mobs
var/sound_fart = 'sound/voice/farts/poo2.ogg'
var/sound_snap = 'sound/impact_sounds/Generic_Snap_1.ogg'
var/sound_fingersnap = 'sound/effects/fingersnap.ogg'
var/sound_gasp = 'sound/voice/gasps/gasp.ogg'
var/voice_type = 0
var/last_voice_sound = 0
var/speechbubble_enabled = 1
var/speechpopupstyle = null
var/caneat = 1
var/candrink = 1
//#ifdef MAP_OVERRIDE_DESTINY
var/hibernating = 0 // if they're stored in the cryotron, Life() gets skipped
//#endif
var/throws_can_hit_me = 1
/mob/living/New()
vision = new()
src.attach_hud(vision)
..()
SPAWN_DBG(0)
src.get_static_image()
sleep_bubble.appearance_flags = RESET_TRANSFORM
/mob/living/flash(duration)
vision.flash(duration)
/mob/living/disposing()
ai_target = null
ai_target_old.len = 0
move_laying = null
for (var/atom in stomach_process)
var/atom/A = atom
qdel(A)
for (var/atom in skin_process)
var/atom/A = atom
qdel(A)
stomach_process = null
skin_process = null
for(var/mob/dead/target_observer/TO in observers)
observers -= TO
TO.ghostize()
for(var/mob/dead/aieye/E in src.contents)
E.cancel_camera()
observers.len = 0
if (src.static_image)
mob_static_icons.Remove(src.static_image)
src.static_image = null
..()
/mob/living/death(gibbed)
if (src.key) statlog_death(src,gibbed)
if (src.client && (ticker.round_elapsed_ticks >= 12000))
var/num_players = 0
for(var/mob/players in mobs)
if (players.client && !isdead(players) && !isVRghost(players) && !isghostcritter(players) && !inafterlife(players)) num_players++
LAGCHECK(LAG_HIGH)
if (num_players <= 5 && master_mode != "battle_royale")
if (!emergency_shuttle.online && current_state != GAME_STATE_FINISHED && ticker.mode.crew_shortage_enabled)
emergency_shuttle.incall()
boutput(world, "<span style=\"color:blue\"><B>Alert: The emergency shuttle has been called.</B></span>")
boutput(world, "<span style=\"color:blue\">- - - <b>Reason:</b> Crew shortages and fatalities.</span>")
boutput(world, "<span style=\"color:blue\"><B>It will arrive in [round(emergency_shuttle.timeleft()/60)] minutes.</B></span>")
if (deathConfettiActive || (src.mind && src.mind.assigned_role == "Clown")) //Active if XMAS or manually toggled. Or if theyre a clown. Clowns always have death confetti.
src.deathConfetti()
var/youdied = "You have died!"
if (prob(1))
if (gibbed)
youdied = pick("Cleanup on aisle 5", "What a mess...", "Nice and creamy", "Salsa con [src.real_name]", "Someone get the janitor!", "Gib and let gib", "Rest In Pieces")
else
youdied = pick("Congratulations on your recent death!", "Welp, so much for that.", "You are dead. Not big surprise.", "You are no longer alive.", "haha you died loser.", "R.I.P. [src.real_name]", "well, shit.", "Better luck next time.", "MISSING: Life, 100 credit reward")
boutput(src, {"
<div style="border: 3px solid red; padding: 3px;">
<div style="background: black; padding: 0.1em; color: #f33; text-align: center; font-size: 150%; font-weight: bold;">
[youdied]
</div>
<div style=" text-align: center;">
[!gibbed ? {"
<a href="byond://winset?command=Ghost" style="display: inline-block; font-size: 130%; font-weight: bold;">Become a Ghost</a>
<br><em style="color: #666; font-size: 75%;">You can also use the "<a href="byond://winset?command=Ghost" style="font-family: 'Consolas', monospace;">Ghost</a>" command to observe.</em><br><br>
"} : ""]
<strong>You may be revived if someone clones you.</strong>
<br>Otherwise, you'll have to wait for the next round.
<br>
<br>There's still plenty to do, even while dead!
<br><strong><a href='byond://winset?command=Afterlife-Bar'>Visit the Afterlife Bar</a> &bull; <a href='byond://winset?command=Enter-VR'>Enter Virtual Reality</a>
<br><a href='byond://winset?command=Enter-Ghostdrone-Queue'>Become a Ghost Drone</a> &bull; <a href='byond://winset?command=Respawn-as-Animal'>Become a Critter</a></strong>
</div>
</div>
"})
return ..(gibbed)
/mob/living/Login()
..()
if(!isdead(src))
respawn_controller.unsubscribeRespawnee(src.ckey)
/mob/living/Life(datum/controller/process/mobs/parent)
//#ifdef MAP_OVERRIDE_DESTINY
if (hibernating)
if (istype(src.loc, /obj/cryotron))
if (!stat)
setunconscious(src)
return 1
else
hibernating = 0
//#endif
if (..(parent))
return 1
return
/mob/living/update_camera()
for (var/mob/dead/target_observer/observer in observers)
if (observer.client)
src.apply_camera(observer.client)
..()
/mob/living/attach_hud(datum/hud/hud)
for (var/mob/dead/target_observer/observer in observers)
observer.attach_hud(hud)
return ..()
/mob/living/detach_hud(datum/hud/hud)
if (observers.len) //Wire note: Attempted fix for BUG: Bad ref (f:410976) in IncRefCount(DM living.dm:132)
for (var/mob/dead/target_observer/observer in observers)
observer.detach_hud(hud)
return ..()
/mob/living/projCanHit(datum/projectile/P)
if (!src.lying || (src:lying && prob(P.hit_ground_chance))) return 1
return 0
/mob/living/proc/hand_attack(atom/target, params, location, control, origParams)
target.attack_hand(src, params, location, control, origParams)
/mob/living/proc/hand_range_attack(atom/target, params, location, control, origParams)
.= 0
var/datum/limb/L = src.equipped_limb()
if (L)
.= L.attack_range(target,src,params)
if (.)
src.lastattacked = src
/mob/living/proc/weapon_attack(atom/target, obj/item/W, reach, params)
var/usingInner = 0
if (W.useInnerItem && W.contents.len > 0)
var/obj/item/held = W.holding
if (!held)
held = pick(W.contents)
if (held && !istype(held, /obj/ability_button))
W = held
usingInner = 1
if (reach)
target.attackby(W, src, params)
if (W && (equipped() == W || usingInner))
var/pixelable = isturf(target)
if (!pixelable)
if (istype(target, /atom/movable) && isturf(target:loc))
pixelable = 1
if (pixelable)
if (!W.pixelaction(target, params, src, reach))
if (W)
W.afterattack(target, src, reach, params)
else if (!pixelable && W)
W.afterattack(target, src, reach, params)
/mob/living/onMouseDrag(src_object,over_object,src_location,over_location,src_control,over_control,params)
if (!src.stat && !src.restrained() && !src.getStatusDuration("weakened") && !src.getStatusDuration("paralysis") && !src.getStatusDuration("stunned"))
var/obj/item/W = src.equipped()
if (W) //nah dude, don't typecheck. just assume that mobs can only hold items, this proc gets called a fuckload
W.onMouseDrag(src_object,over_object,src_location,over_location,src_control,over_control,params)
return
/* nothing currently uses needOnMouseMove, so im commenting this out.
/mob/living/onMouseMove(object,location,control,params)
var/obj/item/W = src.equipped()
if(W.needOnMouseMove)
if (!src.stat && !src.restrained() && !src.getStatusDuration("weakened") && !src.getStatusDuration("paralysis") && !src.getStatusDuration("stunned"))
if (W && istype(W))
W.onMouseMove(object,location,control,params)
return
*/
/mob/living/onMouseDown(object,location,control,params)
if (!src.stat && !src.restrained() && !src.getStatusDuration("weakened") && !src.getStatusDuration("paralysis") && !src.getStatusDuration("stunned"))
var/obj/item/W = src.equipped()
if (W && istype(W))
W.onMouseDown(object,location,control,params)
return
/mob/living/onMouseUp(object,location,control,params)
if (!src.stat && !src.restrained() && !src.getStatusDuration("weakened") && !src.getStatusDuration("paralysis") && !src.getStatusDuration("stunned"))
var/obj/item/W = src.equipped()
if (W && istype(W))
W.onMouseUp(object,location,control,params)
return
/mob/living/hotkey(name)
switch (name)
if ("togglepoint")
src.toggle_point_mode()
if ("say_radio")
src.say_radio()
else
return ..()
//gross are we tg or something with all of these /s
// i'd like to hear your suggestion for better searching for procs!!! - cirr
/mob/living/Click(location,control,params)
if(istype(usr, /mob/dead/observer) && usr.client && !usr.client.keys_modifier && !usr:in_point_mode)
var/mob/dead/observer/O = usr
O.insert_observer(src)
else return ..()
/mob/living/click(atom/target, params, location, control)
. = ..()
if (. == 100)
return 100
if (params["middle"])
src.swap_hand()
return
if (location != "map")
//#ifdef MAP_OVERRIDE_DESTINY
if (src.hibernating && istype(src.loc, /obj/cryotron))
var/obj/cryotron/cryo = src.loc
if (cryo.exit_prompt(src))
return
//#endif
if (src.client && src.client.check_key(KEY_EXAMINE))
target.examine() // in theory, usr should be us, this is shit though
return
if (src.in_point_mode || (src.client && src.client.check_key(KEY_POINT)))
src.point(target)
if (src.in_point_mode)
src.toggle_point_mode()
return
if (src.restrained())
if (src.handcuffed)
boutput(src, "<span style=\"color:red\">You are handcuffed! Use Resist to attempt removal.</span>")
return
if (!src.stat && !src.getStatusDuration("weakened") && !src.getStatusDuration("paralysis") && !src.getStatusDuration("stunned"))
if (target != src && ishuman(src))
var/mob/living/carbon/human/S = src
if (S.sims)
var/mult = S.sims.getMoodActionMultiplier()
if (mult < 0.5)
if (prob((0.5 - mult) * 200))
boutput(src, pick("<span style=\"color:red\">You're not in the mood to attack that.</span>", "<span style=\"color:red\">You don't feel like doing that.</span>"))
return
var/obj/item/equipped = src.equipped()
var/use_delay = !(target in src.contents) && !istype(target,/obj/screen) && (!disable_next_click || ismob(target) || (target && target.flags & USEDELAY) || (equipped && equipped.flags & USEDELAY))
var/grace_penalty = 0
if ((target == equipped || use_delay) && world.time < src.next_click) // if we ignore next_click on attack_self we get... instachoking, so let's not do that
var/time_left = src.next_click - world.time
// since we're essentially encouraging players to click as soon as they possibly can, and how clicking strongly depends on lag, having a strong cutoff feels like bullshit
// the grace window gives people a small amount of leeway without increasing the overall click rate by much
if (time_left > CLICK_GRACE_WINDOW || (equipped && (equipped.flags & EXTRADELAY))) // also let's not enable this for guns.
return time_left
else
grace_penalty = time_left
if (target == equipped)
equipped.attack_self(src, params, location, control)
else if (params["ctrl"])
var/atom/movable/movable = target
if (istype(movable))
movable.pull()
if (mob_flags & AT_GUNPOINT)
for(var/obj/item/grab/gunpoint/G in grabbed_by)
G.shoot()
.= 0
return
else
var/reach = can_reach(src, target)
if (reach || (equipped && equipped.special) || (equipped && (equipped.flags & EXTRADELAY))) //Fuck you, magic number prickjerk //MBC : added bit to get weapon_attack->pixelaction to work for itemspecial
if (use_delay)
src.next_click = world.time + (equipped ? equipped.click_delay : src.click_delay)
if (src.invisibility > 0 && get_dist(src, target) > 0) // dont want to check for a cloaker every click if we're not invisible
for (var/obj/item/cloaking_device/I in src)
if (I.active)
I.deactivate(src)
src.visible_message("<span style=\"color:blue\"><b>[src]'s cloak is disrupted!</b></span>")
if (equipped)
weapon_attack(target, equipped, reach, params)
else
hand_attack(target, params, location, control)
//If lastattacked was set, this must be a combat action!! Use combat click delay || the other condition is whether a special attack was just triggered.
if ((lastattacked != null && (src.lastattacked == target || src.lastattacked == equipped || src.lastattacked == src) && use_delay) || (equipped && equipped.special && equipped.special.last_use >= world.time - src.click_delay))
src.next_click = world.time + (equipped ? max(equipped.click_delay,src.combat_click_delay) : src.combat_click_delay)
src.lastattacked = null
else if (!equipped)
// oh god
// okay, this is only ACTUALLY used for power gloves, I'm disabling the rest for now because heavy hasvar usage is causing lag
/*if(hasvar(src, "back") && src:back) src:back:equipment_click(target, src)
if(hasvar(src, "belt") && src:belt) src:belt:equipment_click(target, src)
if(src.ears) src.ears.equipment_click(target, src)
if(hasvar(src, "glasses") && src:glasses) src:glasses:equipment_click(target, src)*/
if(hasvar(src, "gloves") && src:gloves) src:gloves:equipment_click(target, src)
/*if(hasvar(src, "head") && src:head) src:head:equipment_click(target, src)
if(src.l_hand) src.l_hand.equipment_click(target, src)
if(hasvar(src, "l_store") && src:l_store) src:l_store:equipment_click(target, src)
if(src.r_hand) src.r_hand.equipment_click(target, src)
if(hasvar(src, "r_store") && src:r_store) src:r_store:equipment_click(target, src)
if(hasvar(src, "shoes") && src:shoes) src:shoes:equipment_click(target, src)
if(hasvar(src, "wear_id") && src:wear_id) src:wear_id:equipment_click(target, src)
if(src.wear_mask) src.wear_mask.equipment_click(target, src)
if(hasvar(src, "wear_suit") && src:wear_suit) src:wear_suit:equipment_click(target, src)
if(hasvar(src, "w_uniform") && src:w_uniform) src:w_uniform:equipment_click(target, src)*/
hand_range_attack(target, params, location, control)
if (lastattacked != null && (src.lastattacked == target || src.lastattacked == equipped || src.lastattacked == src) && use_delay)
src.next_click = world.time + src.combat_click_delay
src.lastattacked = null
//Don't think I need the above, this should work here.
if (istype(src.loc, /obj/machinery/vehicle))
var/obj/machinery/vehicle/ship = src.loc
if (ship.sensors)
if (ship.sensors.active)
var/obj/machinery/vehicle/target_pod = target
if (src.loc != target_pod && istype(target_pod))
ship.sensors.end_tracking()
ship.sensors.quick_obtain_target(target_pod)
else
if (istype(target, /obj/machinery/vehicle))
boutput(src, "<span style=\"color:red\">Sensors are inactive, unable to target craft!</span>")
if (src.next_click >= world.time) // since some of these attack functions go wild with modifying next_click, we implement the clicking grace window with a penalty instead of changing how next_click is set
src.next_click += grace_penalty
/mob/living/update_cursor()
..()
if (src.client)
if (src.in_point_mode || src.client.check_key(KEY_POINT))
src.set_cursor('icons/cursors/point.dmi')
return
if (src.client.check_key(KEY_EXAMINE))
src.set_cursor('icons/cursors/examine.dmi')
return
if (src.client.check_key(KEY_PULL))
src.set_cursor('icons/cursors/pull.dmi')
return
//src.set_cursor(null)
/mob/living/key_down(key)
if (key == "alt" || key == "ctrl" || key == "shift")
update_cursor()
/mob/living/key_up(key)
if (key == "alt" || key == "ctrl" || key == "shift")
update_cursor()
/mob/living/proc/toggle_point_mode(var/force_off = 0)
if (force_off)
src.in_point_mode = 0
src.update_cursor()
return
src.in_point_mode = !(src.in_point_mode)
src.update_cursor()
/mob/living/verb/point(var/atom/target as mob|obj|turf in oview())
set name = "Point"
if (!isturf(src.loc) || usr.stat || usr.restrained())
return
if (isghostcritter(src))
return
if (src.reagents && src.reagents.has_reagent("capulettium_plus"))
src.show_text("You are completely paralysed and can't point!", "red")
return
if (istype(target, /obj/decal/point))
return
var/obj/item/gun/G = src.equipped()
if(!istype(G) || !ismob(target))
src.visible_message("<span style='color:#605b59'><b>[src]</b> points to [target].</span>")
else
src.visible_message("<span style='font-weight:bold;color:#f00;font-size:120%;'>[src] points \the [G] at [target]!</span>")
var/obj/decal/point/P = new(get_turf(target))
P.pixel_x = target.pixel_x
P.pixel_y = target.pixel_y
P.color = src.bioHolder.mobAppearance.customization_first_color
src = null // required to make sure its deleted
SPAWN_DBG (20)
P.invisibility = 101
qdel(P)
/mob/living/proc/set_burning(var/new_value)
setStatus("burning", new_value*10)
return
/mob/living/proc/update_burning(var/change)
changeStatus("burning", change*10)
return
/mob/living/proc/update_burning_icon(var/force_remove = 0)
return
/mob/living/proc/get_equipped_ore_scoop()
return null
/mob/living/proc/talk_into_equipment(var/mode, var/messages, var/param, var/lang_id)
switch (mode)
if ("headset")
if (src.ears)
src.ears.talk_into(src, messages, param, src.real_name, lang_id)
if ("secure headset")
if (src.ears)
src.ears.talk_into(src, messages, param, src.real_name, lang_id)
if ("right hand")
if (src.r_hand)
src.r_hand.talk_into(src, messages, param, src.real_name, lang_id)
else
src.emote("handpuppet")
if ("left hand")
if (src.l_hand)
src.l_hand.talk_into(src, messages, param, src.real_name, lang_id)
else
src.emote("handpuppet")
/mob/living/say(var/message)
message = strip_html(trim(copytext(sanitize_noencode(message), 1, MAX_MESSAGE_LEN)))
if (!message)
return
if (reverse_mode) message = reverse_text(message)
logTheThing("diary", src, null, ": [message]", "say")
#ifdef DATALOGGER
// Jewel's attempted fix for: null.ScanText()
if (game_stats)
game_stats.ScanText(message)
#endif
if (src.client && src.client.ismuted())
boutput(src, "You are currently muted and may not speak.")
return
if(!src.canspeak)
boutput(src, "<span style=\"color:red\">You can not speak!</span>")
return
if(src.reagents && src.reagents.has_reagent("capulettium_plus"))
boutput(src, "<span style=\"color:red\">You are completely paralysed and can't speak!</span>")
return
if (isdead(src))
if (dd_hasprefix(message, "*")) // no dead emote spam
return
return src.say_dead(message)
if(src.z == 2 && istype(get_area(src),/area/afterlife)) //check zlevel before doing istype
if (dd_hasprefix(message, ":d"))
message = trim(copytext(message, 3, MAX_MESSAGE_LEN))
return src.say_dead(message)
// wtf?
if (src.stat)
return
// emotes
if (dd_hasprefix(message, "*") && !src.stat)
return src.emote(copytext(message, 2),1)
// Mute disability
if (src.bioHolder && src.bioHolder.HasEffect("mute"))
boutput(src, "<span style=\"color:red\">You seem to be unable to speak.</span>")
return
if (src.wear_mask && src.wear_mask.is_muzzle)
boutput(src, "<span style=\"color:red\">Your muzzle prevents you from speaking.</span>")
return
if (ishuman(src))
var/mob/living/carbon/human/H = src
// If theres no oxygen
if (H.oxyloss > 10 || H.losebreath >= 4) // Perfluorodecalin cap - normal life() depletion - buffer.
H.whisper(message)
return
//Pod coloseum is broken - disable this unnecessary istype
/*
if (istype(loc, /obj/machinery/colosseum_putt))
var/obj/machinery/colosseum_putt/C = loc
logTheThing("say", src, null, "<i>broadcasted between Colosseum Putts:</i> \"[message]\"")
C.broadcast(message)
return
*/
var/italics = 0
var/forced_language = null
var/message_range = null
var/message_mode = null
var/secure_headset_mode = null
var/skip_open_mics_in_range = 0 // For any radios or intercoms that happen to be in range.
if (prob(50) && src.get_brain_damage() >= 60)
if (ishuman(src))
message_mode = "headset"
// Special message handling
else if (copytext(message, 1, 2) == ";")
message_mode = "headset"
message = copytext(message, 2)
else if ((length(message) >= 2) && (copytext(message,1,2) == ":"))
switch (lowertext( copytext(message,2,4) ))
if ("rh")
message_mode = "right hand"
message = copytext(message, 4)
if ("lh")
message_mode = "left hand"
message = copytext(message, 4)
/*else if (copytext(message, 1, 3) == ":w")
message_mode = "whisper"
message = copytext(message, 3)*/
if ("in")
message_mode = "intercom"
message = copytext(message, 4)
else
// AI radios. See further down in this proc (Convair880).
if (isAI(src))
switch (lowertext(copytext(message, 2, 3))) // One vs. two letter prefix.
if ("1")
message_mode = "internal 1"
message = copytext(message, 3)
if ("2")
message_mode = "internal 2"
message = copytext(message, 3)
if ("3")
message_mode = "monitor"
var/end = 3
if (!lowertext(copytext(message,3,4) == " "))
end = 4
secure_headset_mode = lowertext(copytext(message,3,end)) //why did i do this to the players
message = copytext(message, end)
else
message = copytext(message, 3)
else
if (ishuman(src) || iscritter(src) || isrobot(src)) // this is shit
message_mode = "secure headset"
secure_headset_mode = lowertext(copytext(message,2,3))
message = copytext(message, 3)
forced_language = get_special_language(secure_headset_mode)
message = trim(message)
if (!message)
return
if (src.voice_type && world.time > last_voice_sound + 8)
var/VT = voice_type
var/ending = copytext(message, length(message))
switch (message_mode)
if ("headset", "secure headset", "right hand", "left hand", "intercom")
VT = "radio"
ending = 0
if (ending == "?")
playsound(src, sounds_speak["[VT]?"], 55, 0.01, 8, src.get_age_pitch_for_talk(), ignore_flag = SOUND_SPEECH)
speech_bubble.icon_state = "?"
else if (ending == "!")
playsound(src, sounds_speak["[VT]!"], 55, 0.01, 8, src.get_age_pitch_for_talk(), ignore_flag = SOUND_SPEECH)
speech_bubble.icon_state = "!"
else
playsound(src, sounds_speak["[VT]"], 55, 0.01, 8, src.get_age_pitch_for_talk(), ignore_flag = SOUND_SPEECH)
speech_bubble.icon_state = "speech"
last_voice_sound = world.time
else
speech_bubble.icon_state = "speech"
if (text2num(message)) //mbc : check mob.dmi for the icons
var/n = round(text2num(message),1)
if ((n >= 0 && n <= 20) || n == 420)
speech_bubble.icon_state = "[n]"
if (src.stuttering)
message = stutter(message)
if (src.get_brain_damage() >= 60)
message = replacetext(message, "is ", "am ")
message = replacetext(message, "are ", "am ")
message = replacetext(message, "i ", "me ")
message = replacetext(message, "have ", "am ")
message = replacetext(message, "youre ", "your ")
message = replacetext(message, "you're ", "your ")
message = replacetext(message, "attack ", "kill ")
message = replacetext(message, "hurt", " kill")
message = replacetext(message, "acquire ", "get ")
message = replacetext(message, "attempt ", "try ")
message = replacetext(message, "attention ", "help ")
message = replacetext(message, "attempt ", "try ")
message = replacetext(message, "grief", "grife")
message = replacetext(message, "her ", "she ")
message = replacetext(message, "him ", "he ")
message = replacetext(message, "heal", "fix")
message = replacetext(message, "repair ", "fix")
message = replacetext(message, "heal ", "fix")
message = replacetext(message, "space", "spess")
message = replacetext(message, "clown", "honky man")
message = replacetext(message, "cluwne", "bad honky man")
message = replacetext(message, "traitor", "bad guy")
message = replacetext(message, "spy", "bad guy")
message = replacetext(message, "operative", "bad guy")
message = replacetext(message, "nukie", "bad guy")
message = replacetext(message, "vampire", "bad guy")
message = replacetext(message, "wrestler", "bad guy")
message = replacetext(message, "alien", "allen")
message = replacetext(message, "changeling", "alien")
message = replacetext(message, "pain", "hurt")
message = replacetext(message, "damage", "hurt")
message = replacetext(message, "they", "them")
if (prob(20))
if(prob(25))
message = uppertext(message)
message = "[message][stutter(pick("!", "!!", "!!!"))]"
if(!src.stuttering && prob(8))
message = stutter(message)
UpdateOverlays(speech_bubble, "speech_bubble")
SPAWN_DBG(15)
UpdateOverlays(null, "speech_bubble")
//Blobchat handling
if (src.mob_flags & SPEECH_BLOB)
message = html_encode(src.say_quote(message))
var/rendered = "<span class='game blobsay'>"
rendered += "<span class='prefix'>BLOB:</span> "
rendered += "<span class='name text-normal' data-ctx='\ref[src.mind]'>[src.get_heard_name()]</span> "
rendered += "<span class='message'>[message]</span>"
rendered += "</span>"
for (var/mob/M in mobs)
if (istype(M, /mob/new_player))
continue
if (M.client && (isblob(M) || (M.client.holder && M.client.deadchat && !M.client.player_mode)))
var/thisR = rendered
if ((M.mob_flags & MOB_HEARS_ALL || M.client.holder) && src.mind)
thisR = "<span class='adminHearing' data-ctx='[M.client.chatOutput.ctxFlag]'>[rendered]</span>"
M.show_message(thisR, 2)
return
//Overmind handling - Cirr
// this code duplication is pretty bad. todo: fix the code duplication, hopefully
if (src.mob_flags & SPEECH_INTRUDER)
var/scrambledMessage = src.say_quote(stutter(say_superdrunk(message)))
message = src.say_quote(message)
var/rendered = "<span class='game astralsay'>"
rendered += "<span class='prefix'>ASTRAL:</span> "
rendered += "<span class='name text-normal' data-ctx='\ref[src.mind]'>[src.get_heard_name()]</span> "
rendered += "<span class='message'>[message]</span>"
rendered += "</span>"
for (var/mob/M in mobs)
if (istype(M, /mob/new_player))
continue
if (M.client)
if((istype(M, /mob/living/intangible/intruder) || M.client.holder))
var/thisR = rendered
if (M.client.holder && src.mind)
thisR = "<span class='adminHearing' data-ctx='[M.client.chatOutput.ctxFlag]'>[rendered]</span>"
boutput(M, thisR)
else
if(prob(30))
boutput(M, "<em>A strange, unnatural voice enters your mind... [scrambledMessage]</em>")
else if(prob(20))
var/description = "[pick("chimes", "croaks", "shines", "whispers", "sings", "echoes")] "
description += "[pick("strangely", "hollowly", "incomprehensibly", "menacingly", "peacefully", "softly")]"
boutput(M, "<em>A strange, unnatural voice [description].</em>")
return
var/list/messages = process_language(message, forced_language)
var/lang_id = get_language_id(forced_language)
// Do they have a phone?
var/obj/item/equipped_talk_thing = src.equipped()
if(equipped_talk_thing && equipped_talk_thing.flags & TALK_INTO_HAND && !message_mode)
equipped_talk_thing.talk_into(src, messages, secure_headset_mode, src.real_name, lang_id)
switch (message_mode)
if ("headset", "secure headset", "right hand", "left hand")
talk_into_equipment(message_mode, messages, secure_headset_mode, lang_id)
message_range = 1
italics = 1
//Might put this back if people are used to the old system.
/*if ("whisper")
message_range = 1
italics = 1*/
// Added shortcuts for the AI mainframe radios. All the relevant vars are already defined here, and
// I didn't want to have to reinvent the wheel in silicon.dm (Convair880).
if ("internal 1", "internal 2", "monitor")
var/mob/living/silicon/ai/A
var/obj/item/device/radio/R1
var/obj/item/device/radio/R2
var/obj/item/device/radio/R3
if (isAI(src))
A = src
else if (issilicon(src))
var/mob/living/silicon/S = src
if (S.dependent && S.mainframe && isAI(S.mainframe)) // AI-controlled robot.
A = S.mainframe
if (A && isAI(A))
if (A.radio1 && istype(A.radio1, /obj/item/device/radio/))
R1 = A.radio1
if (A.radio2 && istype(A.radio2, /obj/item/device/radio/))
R2 = A.radio2
if (A.radio3 && istype(A.radio3, /obj/item/device/radio/))
R3 = A.radio3
switch (message_mode)
if ("internal 1")
if (R1 && !(A.stat || A.getStatusDuration("stunned")|| A.getStatusDuration("weakened"))) // Mainframe may be stunned when the shell isn't.
R1.talk_into(src, messages, null, A.name, lang_id)
italics = 1
skip_open_mics_in_range = 1 // First AI intercom broadcasts everything by default.
//DEBUG_MESSAGE("AI radio #1 triggered. Message: [message]")
else
src.show_text("Mainframe radio inoperable or unavailable.", "red")
if ("internal 2")
if (R2 && !(A.stat || A.getStatusDuration("stunned") || A.getStatusDuration("weakened")))
R2.talk_into(src, messages, null, A.name, lang_id)
italics = 1
skip_open_mics_in_range = 1
//DEBUG_MESSAGE("AI radio #2 triggered. Message: [message]")
else
src.show_text("Mainframe radio inoperable or unavailable.", "red")
if ("monitor")
if (R3 && !(A.stat || A.getStatusDuration("stunned") || A.getStatusDuration("weakened")))
R3.talk_into(src, messages, secure_headset_mode, A.name, lang_id)
italics = 1
skip_open_mics_in_range = 1
//DEBUG_MESSAGE("AI radio #3 triggered. Message: [message]")
else
src.show_text("Mainframe radio inoperable or unavailable.", "red")
if ("intercom")
for (var/obj/item/device/radio/intercom/I in view(1, null))
I.talk_into(src, messages, null, src.real_name, lang_id)
for (var/obj/colosseum_radio/M in hearers(1, src))
M.hear_talk(src, messages, real_name, lang_id)
message_range = 1
italics = 1
var/heardname = src.real_name
if (!skip_open_mics_in_range)
src.send_hear_talks(message_range, messages, heardname, lang_id)
var/list/listening = list()
var/list/olocs = list()
var/thickness = 0
if (isturf(loc))
listening = all_hearers(message_range, src)
else
olocs = obj_loc_chain(src)
if(olocs.len > 0) // fix runtime list index out of bounds when loc is null (IT CAN HAPPEN, APPARENTLY)
for (var/atom/movable/AM in olocs)
thickness += AM.soundproofing
listening = all_hearers(message_range, olocs[olocs.len])
listening -= src
listening += src
var/list/heard_a = list() // understood us
var/list/heard_b = list() // didn't understand us
for (var/mob/M in listening)
if (M.say_understands(src, forced_language))
heard_a += M
else
heard_b += M
var/list/processed = list()
var/rendered = null
if (length(heard_a))
processed = saylist(messages[1], heard_a, olocs, thickness, italics, processed)
if (length(heard_b))
processed = saylist(messages[2], heard_b, olocs, thickness, italics, processed, 1)
message = src.say_quote(messages[1])
if (italics)
message = "<i>[message]</i>"
if (!message_range && speechpopups)
new /obj/maptext_junk/speech(src, msg = messages[1], style = src.speechpopupstyle)
rendered = "<span class='game say'>[src.get_heard_name()] <span class='message'>[message]</span></span>"
if(src.mob_flags & SPEECH_REVERSE)
rendered = "<span style='-ms-transform: rotate(180deg)'>[rendered]</span>"
var/viewrange = 0
for (var/client/C)
var/mob/M = C.mob
if (!M || M.z == 2 && istype(M, /mob/new_player))
continue
//Hello welcome to the world's most awful if
if (( \
M.mob_flags & MOB_HEARS_ALL || \
(iswraith(M) && !M.density) || \
(isintangible(M) && M in hearers(src)) || \
( \
(!isturf(src.loc) && src.loc == M.loc) && \
!(M in heard_a) && \
!istype(M, /mob/dead/target_observer) && \
M != src \
) \
))
var/thisR = rendered
if (src.mind && M.client.chatOutput && (M.mob_flags & MOB_HEARS_ALL || M.client.holder))
thisR = "<span class='adminHearing' data-ctx='[M.client.chatOutput.ctxFlag]'>[rendered]</span>"
if (isobserver(M)) //if a ghooooost (dead) (and online)
viewrange = (((istext(C.view) ? WIDE_TILE_WIDTH : SQUARE_TILE_WIDTH) - 1) / 2)
if (M.client.local_deadchat || iswraith(M)) //only listening locally (or a wraith)? w/e man dont bold dat
//if (M in range(M.client.view, src))
if (get_dist(M,src) <= viewrange)
M.show_message(thisR, 2)
else
//if (M in range(M.client.view, src)) //you're not just listening locally and the message is nearby? sweet! bold that sucka brosef
if (get_dist(M,src) <= viewrange) //you're not just listening locally and the message is nearby? sweet! bold that sucka brosef
M.show_message("<span class='bold'>[thisR]</span>", 2) //awwwww yeeeeeah lookat dat bold
else
M.show_message(thisR, 2)
else
M.show_message(thisR, 2)
// helper proooocs
/mob/proc/send_hear_talks(var/message_range, var/messages, var/heardname, var/lang_id) //helper to send hear_talk to all mob, obj, and turf
for (var/thing in all_view(message_range, src))
var/atom/A = thing
A.hear_talk(src,messages,heardname,lang_id)
/mob/proc/get_heard_name()
return "<span class='name' data-ctx='\ref[src.mind]'>[src.name]</span>"
/mob/proc/move_callback_trigger(var/obj/move_laying, var/turf/NewLoc, var/oldloc, direct)
if (move_laying)
if((direct & (NORTH|SOUTH)) && (direct & (EAST|WEST)))//MBC : work around the diagonal bug that we don't understand : if((direct & (NORTH|SOUTH)) && (direct & (EAST|WEST)))
for (var/d in cardinal)
if (direct & d)
var/nloc = get_step(oldloc, d)
move_laying.move_callback(src, oldloc, nloc)
oldloc = nloc
else
move_laying.move_callback(src, oldloc, NewLoc)
/mob/living/Move(var/turf/NewLoc, direct)
var/oldloc = loc
. = ..()
if (isturf(oldloc) && isturf(loc) && move_laying)
var/list/equippedlist = src.equipped_list()
if (equippedlist && equippedlist.len)
var/move_callback_happened = 0
for (var/I in equippedlist)
if (I == move_laying)
move_callback_trigger(move_laying, NewLoc, oldloc, direct)
move_callback_happened = 1
else if (islist(move_laying))
for (var/M in move_laying)
if (I == M)
move_callback_trigger(M, NewLoc, oldloc, direct)
move_callback_happened = 1
if (!move_callback_happened)
move_laying = null
else
move_laying = null
/mob/living/change_misstep_chance(var/amount)
if (..())
return
src.misstep_chance = max(0,min(misstep_chance + amount,100))
return
/mob/living/proc/get_static_image()
if (src.disposed)
return
if (!islist(default_mob_static_icons))
return
if (src.static_image)
mob_static_icons.Remove(src.static_image)
var/checkpath = src.static_type_override ? src.static_type_override : src.type
if (ishuman(src))
var/mob/living/carbon/human/H = src
if (istype(H.mutantrace) && H.mutantrace.icon_override_static)
checkpath = H.mutantrace.type
if (ispath(checkpath))
var/generate_static = 1
if (default_mob_static_icons.Find(checkpath))
if (istype(default_mob_static_icons[checkpath], /image))
src.static_image = image(default_mob_static_icons[checkpath])
DEBUG_MESSAGE(bicon(src.static_image) + "<br>\ref[src.static_image]")
DEBUG_MESSAGE(bicon(default_mob_static_icons[checkpath]) + "<br>\ref[default_mob_static_icons[checkpath]]")
src.static_image.override = 1
src.static_image.loc = src
src.static_image.plane = PLANE_LIGHTING
mob_static_icons.Add(src.static_image)
generate_static = 0
if (generate_static)
if (ispath(checkpath, /datum/mutantrace) && ishuman(src))
var/mob/living/carbon/human/H = src
if (H.mutantrace)
src.static_image = getTexturedImage(icon(H.mutantrace.icon, H.mutantrace.icon_state), "static", ICON_OVERLAY)
else
src.static_image = getTexturedImage(src.default_static_icon ? src.default_static_icon : icon(src.icon, src.icon_state), "static", ICON_OVERLAY)
if (src.static_image)
default_mob_static_icons[checkpath] = image(src.static_image)
DEBUG_MESSAGE(bicon(src.static_image) + "<br>\ref[src.static_image]")
DEBUG_MESSAGE(bicon(default_mob_static_icons[checkpath]) + "<br>\ref[default_mob_static_icons[checkpath]]")
src.static_image.override = 1
src.static_image.loc = src
src.static_image.plane = PLANE_LIGHTING
mob_static_icons.Add(src.static_image)
return src.static_image
/proc/check_static_defaults()
if (!islist(default_mob_static_icons))
DEBUG_MESSAGE("default_mob_static_icons is not a list")
return
for (var/Type in default_mob_static_icons)
var/image/i = default_mob_static_icons[Type]
if (i)
DEBUG_MESSAGE(bicon(i) + "\ref[i][Type]")
var/global/icon/human_static_base_idiocy_bullshit_crap = icon('icons/mob/human.dmi', "body_m")
/mob/living/verb/give_item()
set name = "Give Item"
set src in view(1)
set category = "Local"
SPAWN_DBG (7) //secret spawn delay, so you can't spam this during combat for a free "stun"
if (usr && isliving(usr) && !issilicon(usr) && get_dist(src,usr) <= 1)
var/mob/living/L = usr
L.give_to(src)
/mob/living/proc/give_to(var/mob/living/M)
if (!M) return
#ifdef TWITCH_BOT_ALLOWED
if (IS_TWITCH_CONTROLLED(M))
return
#endif
var/message = null
var/obj/item/thing = src.equipped()
if (!thing)
if (src.l_hand)
thing = src.l_hand
else if (src.r_hand)
thing = src.r_hand
if (thing)
if (alert(M, "[src] offers [his_or_her(src)] [thing] to you. Do you accept it?", "Choice", "Yes", "No") == "Yes")
if (!thing || !M || !(get_dist(src, M) <= 1) || thing.loc != src || src.restrained())
return
src.u_equip(thing)
if (src.bioHolder && src.bioHolder.HasEffect("clumsy") && prob(50))
message = "<B>[src]</B> tries to hand [thing] to [M], but [src] drops it!"
thing.set_loc(src.loc)
else if (M.bioHolder && M.bioHolder.HasEffect("clumsy") && prob(50))
message = "<B>[src]</B> tries to hand [thing] to [M], but [M] drops it!"
thing.set_loc(M.loc)
else if (M.put_in_hand(thing))
message = "<B>[src]</B> hands [thing] to [M]."
else
src.put_in_hand_or_drop(thing)
message = "<B>[src]</B> tries to hand [thing] to [M], but [M]'s hands are full!"
else
message = "<B>[src]</B> tries to hand [thing] to [M], but [M] declines."
src.visible_message("<span style=\"color:#888888\">[message]</span>")
/mob/living/proc/pull_speed_modifier(var/atom/move_target = 0)
var/mod = 1
if (src.pulling && istype(src.pulling, /atom/movable) && !(src.is_hulk() || (src.bioHolder && src.bioHolder.HasEffect("strong"))))
var/atom/movable/M = src.pulling
// hi grayshift sorry grayshift
if (get_dist(src,M) > 0 && get_dist(move_target,M) > 0) //i think this is mbc dist stuff for if we're actually stepping away and pulling the thing or not?
if(pull_slowing)
mod *= max(M.p_class, 1)
else
if(istype(M,/obj/machinery/nuclearbomb)) //can't speed off super fast with the nuke, it's heavy
mod *= max(M.p_class, 1)
// else, ignore p_class*/
else if (ishuman(src))
if(ishuman(M))
// if they're not on help intent and also not standing, THEN we might deign to use the p_class
var/mob/living/carbon/human/H = M
if(istype(H) && H.intent != INTENT_HELP && H.lying)
mod *= max(H.p_class, 1)
else if(istype(M, /obj/storage))
// if the storage object contains mobs, use its p_class (updated within storage to reflect containing mobs or not)
var/contains_unwilling_mobs = 0
var/obj/storage/S = M
for(var/mob/B in M.contents)
if(B.intent != INTENT_HELP && B.lying)
contains_unwilling_mobs = 1
break
if(contains_unwilling_mobs)
mod *= max(S.p_class, 1)
return mod