Files
Paradise/code/modules/mob/mob.dm
Mike Long 29c9aca1a8 Devil game mode
Fixes more compile errors.  Down to 65 now.

updates << into to_chat

Down to 60 errors, also starts to port the codex gigas and law 666 for cyborg devils.

Fixes more compile errors.  Down to 41 now.

Replaces timers with spawns, and <<s with to_chats
40 compile errors.

Down to 34 compile errors.

whoops, actually down to 34 now.

Down to 25 compile errors.

Down to 15 compile errors, I'llprobably need some help at this point.

Woo!  Down to 7 compile errors.

Ported over devil hud.  Number of errors up to 19.

WOO!  It compiles.  It's completely untested, but it compiles.

Adds devils to traitor panel

Implements iron, silver and salt banes.

implements flashing lights bane

Selling your soul prevents cloning, and some other methods of revival.

Implements harvest bane

Merged and sorted icons/obj/bureaucracy.dmi

Adds toy codex gigas

Fixes compile errors, adds codex gigas sprite.

Lots of bug fixes.  Contracts work, devil revival is more consistant, etc

Adds missing icons for flaming contracts, summon pitchfork, summon wealth, employment cabinet, and sintouch.

Converts DEEP LORE explanations from hell to inferno incorporated.

Banishes the compile errors.

Devils come from hell again.

replaces offer drink obligation with a much more lore appropriate devil's fiddle reference
Also fixes contract bashing brain damage chance.

Undoes some changes I accidentally did to example config files.

Fixes up a few remaining bugs.

Puts in the codex gigas and employment contract cabinets.
 -- Lemon - I kinda skipped this one, I'll patch it back in later because
 map conflicts are suffering incarnate

Solves the devil law problem in a REALLY hacky snowflake way.

Fixes a few methods in which a hellbound can be revived.

Devils respawn with a limited number of appropriate items, instead of COMPLETELY naked upon corpse destruction.  Also adds lines to example config.

Updates devil laws to be less hacky.

Objective to sintouch x mortals now greentexts correctly.

Contracts no longer cause brain damage.  I didn't realize it was lethal on this codebase.  Oops.

Splits dust(visual_only) into dust() and dust_animation() procs

Fixes some defines.
Adds undef statements to improve compile times.

Fixes race changes from demonic form changes.

Fixes small runtime error. (Which somehow didn't break anything?)

Implements lots of small changes/corrections suggested by CrazyLemon64

I still need to test these changes, along with other potential issues he brought up.

Fixes harvest bane and power contracts.  Also adds a few </span> tag enders.

Corrects some edge cases with revival contracts.

Fixes compile error.

Reverts unneccecary change to item/weapon/reagent_containers

Cleans up the code for readability.

Prevents cloning of hellbound individuals.

Latejoins now properly have employment contracts added to employment cabinets (provided they still exist)

Infernal contracts are no longer rendered unreadable by fire and alcohol.

All fireproof paper remains readable after being fireballed, not just infernal contracts.  (Though infernal contracts are the only fireproof paper atm)

Fixes an edge case problem with cloning.

Adds is_revivable proc to mind.

Removes snowflake code involving preventing soulseller resurrection.

Indulges in the sin of sloth, and copies tg's lazy list macros

Proc calls that transform the user no longer go to a null target

Fixes devil UI, human regression will keep appearance, and adds danceoff

Devil's base forms no longer suffocate inside the devil

Fixes runtimes, gets stuff working

The arch devil can now blast down walls with their pitchfork

EXTERMINATE ALL SPIRITS

Activates devil clause in voice of god

Fawks Mcclood

Feature P A R I T Y

Fixes devil bugs from testing

- Does a death refactor to make sure that diabolical resurrection works

- Walls no longer leave girders when blasted by the devil

- Getting a new body gives you a rudimentary amount of equipment to work
with to get out of maintenance or whereever

Does all the icons in a single commit on their own because icon

conflicts suck

Starting point of extra devil rebalance/fixes

Ports devil friends

Also oops tramples all over Fethas' corpse PR that's still up I need
to take care of that one

Styling fixes
2019-01-13 12:00:49 +01:00

1340 lines
37 KiB
Plaintext

/mob/Destroy()//This makes sure that mobs with clients/keys are not just deleted from the game.
GLOB.mob_list -= src
GLOB.dead_mob_list -= src
GLOB.living_mob_list -= src
QDEL_NULL(hud_used)
if(mind && mind.current == src)
spellremove(src)
mobspellremove(src)
QDEL_LIST(viruses)
ghostize()
for(var/mob/dead/observer/M in following_mobs)
M.following = null
following_mobs = null
QDEL_LIST_ASSOC_VAL(tkgrabbed_objects)
for(var/I in tkgrabbed_objects)
qdel(tkgrabbed_objects[I])
tkgrabbed_objects = null
if(buckled)
buckled.unbuckle_mob()
if(viewing_alternate_appearances)
for(var/datum/alternate_appearance/AA in viewing_alternate_appearances)
AA.viewers -= src
viewing_alternate_appearances = null
..()
return QDEL_HINT_HARDDEL
/mob/Initialize()
GLOB.mob_list += src
if(stat == DEAD)
GLOB.dead_mob_list += src
else
GLOB.living_mob_list += src
prepare_huds()
..()
/atom/proc/prepare_huds()
hud_list = list()
for(var/hud in hud_possible)
var/hint = hud_possible[hud]
switch(hint)
if(HUD_LIST_LIST)
hud_list[hud] = list()
else
var/image/I = image('icons/mob/hud.dmi', src, "")
hud_list[hud] = I
/mob/proc/generate_name()
return name
/mob/proc/GetAltName()
return ""
/mob/proc/Cell()
set category = "Admin"
set hidden = 1
if(!loc) return 0
var/datum/gas_mixture/environment = loc.return_air()
var/t = "<span class='notice'>Coordinates: [x],[y] \n</span>"
t+= "<span class='warning'>Temperature: [environment.temperature] \n</span>"
t+= "<span class='notice'>Nitrogen: [environment.nitrogen] \n</span>"
t+= "<span class='notice'>Oxygen: [environment.oxygen] \n</span>"
t+= "<span class='notice'>Plasma : [environment.toxins] \n</span>"
t+= "<span class='notice'>Carbon Dioxide: [environment.carbon_dioxide] \n</span>"
for(var/datum/gas/trace_gas in environment.trace_gases)
to_chat(usr, "<span class='notice'>[trace_gas.type]: [trace_gas.moles] \n</span>")
usr.show_message(t, 1)
/mob/proc/show_message(msg, type, alt, alt_type)//Message, type of message (1 or 2), alternative message, alt message type (1 or 2)
if(!client) return
if(type)
if(type & 1 && !has_vision(information_only=TRUE))//Vision related
if(!( alt ))
return
else
msg = alt
type = alt_type
if(type & 2 && !can_hear())//Hearing related
if(!( alt ))
return
else
msg = alt
type = alt_type
if(type & 1 && !has_vision(information_only=TRUE))
return
// Added voice muffling for Issue 41.
if(stat == UNCONSCIOUS || (sleeping > 0 && stat != DEAD))
to_chat(src, "<I>... You can almost hear someone talking ...</I>")
else
to_chat(src, msg)
return
// Show a message to all mobs in sight of this one
// This would be for visible actions by the src mob
// message is the message output to anyone who can see e.g. "[src] does something!"
// self_message (optional) is what the src mob sees e.g. "You do something!"
// blind_message (optional) is what blind people will hear e.g. "You hear something!"
/mob/visible_message(var/message, var/self_message, var/blind_message)
for(var/mob/M in get_mobs_in_view(7, src))
if(M.see_invisible < invisibility)
continue //can't view the invisible
var/msg = message
if(self_message && M == src)
msg = self_message
M.show_message(msg, 1, blind_message, 2)
// Show a message to all mobs in sight of this atom
// Use for objects performing visible actions
// message is output to anyone who can see, e.g. "The [src] does something!"
// blind_message (optional) is what blind people will hear e.g. "You hear something!"
/atom/proc/visible_message(var/message, var/blind_message)
for(var/mob/M in get_mobs_in_view(7, src))
if(!M.client)
continue
M.show_message(message, 1, blind_message, 2)
// Show a message to all mobs in earshot of this one
// This would be for audible actions by the src mob
// message is the message output to anyone who can hear.
// self_message (optional) is what the src mob hears.
// deaf_message (optional) is what deaf people will see.
// hearing_distance (optional) is the range, how many tiles away the message can be heard.
/mob/audible_message(var/message, var/deaf_message, var/hearing_distance, var/self_message)
var/range = 7
if(hearing_distance)
range = hearing_distance
var/msg = message
for(var/mob/M in get_mobs_in_view(range, src))
if(self_message && M == src)
msg = self_message
M.show_message(msg, 2, deaf_message, 1)
// based on say code
var/omsg = replacetext(message, "<B>[src]</B> ", "")
var/list/listening_obj = new
for(var/atom/movable/A in view(range, src))
if(istype(A, /mob))
var/mob/M = A
for(var/obj/O in M.contents)
listening_obj |= O
else if(istype(A, /obj))
var/obj/O = A
listening_obj |= O
for(var/obj/O in listening_obj)
O.hear_message(src, omsg)
// Show a message to all mobs in earshot of this atom
// Use for objects performing audible actions
// message is the message output to anyone who can hear.
// deaf_message (optional) is what deaf people will see.
// hearing_distance (optional) is the range, how many tiles away the message can be heard.
/atom/proc/audible_message(var/message, var/deaf_message, var/hearing_distance)
var/range = 7
if(hearing_distance)
range = hearing_distance
for(var/mob/M in get_mobs_in_view(range, src))
M.show_message( message, 2, deaf_message, 1)
/mob/proc/findname(msg)
for(var/mob/M in GLOB.mob_list)
if(M.real_name == text("[]", msg))
return M
return 0
/mob/proc/movement_delay()
return 0
/mob/proc/Life(seconds, times_fired)
if(forced_look)
if(!isnum(forced_look))
var/atom/A = locateUID(forced_look)
if(istype(A))
var/view = client ? client.view : world.view
if(get_dist(src, A) > view || !(src in viewers(view, A)))
forced_look = null
to_chat(src, "<span class='notice'>Your direction target has left your view, you are no longer facing anything.</span>")
return
setDir()
// handle_typing_indicator()
return
//This proc is called whenever someone clicks an inventory ui slot.
/mob/proc/attack_ui(slot)
var/obj/item/W = get_active_hand()
if(istype(W))
if(istype(W, /obj/item/clothing))
var/obj/item/clothing/C = W
if(C.hardsuit_restrict_helmet)
to_chat(src, "<span class='warning'>You must fasten the helmet to a hardsuit first. (Target the head and use on a hardsuit)</span>")// Stop eva helms equipping.
else
equip_to_slot_if_possible(C, slot)
else
equip_to_slot_if_possible(W, slot)
else if(!restrained())
W = get_item_by_slot(slot)
if(W)
W.attack_hand(src)
if(ishuman(src) && W == src:head)
src:update_hair()
src:update_fhair()
/mob/proc/put_in_any_hand_if_possible(obj/item/W as obj, del_on_fail = 0, disable_warning = 1, redraw_mob = 1)
if(equip_to_slot_if_possible(W, slot_l_hand, del_on_fail, disable_warning, redraw_mob))
return 1
else if(equip_to_slot_if_possible(W, slot_r_hand, del_on_fail, disable_warning, redraw_mob))
return 1
return 0
//This is a SAFE proc. Use this instead of equip_to_slot()!
//set del_on_fail to have it delete W if it fails to equip
//set disable_warning to disable the 'you are unable to equip that' warning.
//unset redraw_mob to prevent the mob from being redrawn at the end.
/mob/proc/equip_to_slot_if_possible(obj/item/W as obj, slot, del_on_fail = 0, disable_warning = 0, redraw_mob = 1)
if(!istype(W)) return 0
if(!W.mob_can_equip(src, slot, disable_warning))
if(del_on_fail)
qdel(W)
else
if(!disable_warning)
to_chat(src, "<span class='warning'>You are unable to equip that.</span>")//Only print if del_on_fail is false
return 0
equip_to_slot(W, slot, redraw_mob) //This proc should not ever fail.
return 1
//This is an UNSAFE proc. It merely handles the actual job of equipping. All the checks on whether you can or can't eqip need to be done before! Use mob_can_equip() for that task.
//In most cases you will want to use equip_to_slot_if_possible()
/mob/proc/equip_to_slot(obj/item/W as obj, slot)
return
//This is just a commonly used configuration for the equip_to_slot_if_possible() proc, used to equip people when the rounds tarts and when events happen and such.
/mob/proc/equip_to_slot_or_del(obj/item/W as obj, slot)
return equip_to_slot_if_possible(W, slot, 1, 1, 0)
// Convinience proc. Collects crap that fails to equip either onto the mob's back, or drops it.
// Used in job equipping so shit doesn't pile up at the start loc.
/mob/living/carbon/human/proc/equip_or_collect(var/obj/item/W, var/slot)
if(W.mob_can_equip(src, slot, 1))
//Mob can equip. Equip it.
equip_to_slot_or_del(W, slot)
else
//Mob can't equip it. Put it their backpack or toss it on the floor
if(istype(back, /obj/item/storage))
var/obj/item/storage/S = back
//Now, B represents a container we can insert W into.
S.handle_item_insertion(W,1)
return S
var/turf/T = get_turf(src)
if(istype(T))
W.forceMove(T)
return T
//The list of slots by priority. equip_to_appropriate_slot() uses this list. Doesn't matter if a mob type doesn't have a slot.
var/list/slot_equipment_priority = list( \
slot_back,\
slot_wear_pda,\
slot_wear_id,\
slot_w_uniform,\
slot_wear_suit,\
slot_wear_mask,\
slot_head,\
slot_shoes,\
slot_gloves,\
slot_l_ear,\
slot_r_ear,\
slot_glasses,\
slot_belt,\
slot_s_store,\
slot_tie,\
slot_l_store,\
slot_r_store\
)
//puts the item "W" into an appropriate slot in a human's inventory
//returns 0 if it cannot, 1 if successful
/mob/proc/equip_to_appropriate_slot(obj/item/W)
if(!istype(W)) return 0
for(var/slot in slot_equipment_priority)
if(istype(W,/obj/item/storage/) && slot == slot_head) // Storage items should be put on the belt before the head
continue
if(equip_to_slot_if_possible(W, slot, 0, 1, 1)) //del_on_fail = 0; disable_warning = 0; redraw_mob = 1
return 1
return 0
/mob/proc/check_for_open_slot(obj/item/W)
if(!istype(W)) return 0
var/openslot = 0
for(var/slot in slot_equipment_priority)
if(W.mob_check_equip(src, slot, 1) == 1)
openslot = 1
break
return openslot
/obj/item/proc/mob_check_equip(M as mob, slot, disable_warning = 0)
if(!M) return 0
if(!slot) return 0
if(ishuman(M))
//START HUMAN
var/mob/living/carbon/human/H = M
switch(slot)
if(slot_l_hand)
if(H.l_hand)
return 0
return 1
if(slot_r_hand)
if(H.r_hand)
return 0
return 1
if(slot_wear_mask)
if( !(slot_flags & SLOT_MASK) )
return 0
if(H.wear_mask)
return 0
return 1
if(slot_back)
if( !(slot_flags & SLOT_BACK) )
return 0
if(H.back)
if(!(H.back.flags & NODROP))
return 2
else
return 0
return 1
if(slot_wear_suit)
if( !(slot_flags & SLOT_OCLOTHING) )
return 0
if(H.wear_suit)
if(!(H.wear_suit.flags & NODROP))
return 2
else
return 0
return 1
if(slot_gloves)
if( !(slot_flags & SLOT_GLOVES) )
return 0
if(H.gloves)
if(!(H.gloves.flags & NODROP))
return 2
else
return 0
return 1
if(slot_shoes)
if( !(slot_flags & SLOT_FEET) )
return 0
if(H.shoes)
if(!(H.shoes.flags & NODROP))
return 2
else
return 0
return 1
if(slot_belt)
if(!H.w_uniform)
if(!disable_warning)
to_chat(H, "<span class='warning'>You need a jumpsuit before you can attach this [name].</span>")
return 0
if( !(slot_flags & SLOT_BELT) )
return 0
if(H.belt)
if(!(H.belt.flags & NODROP))
return 2
else
return 0
return 1
if(slot_glasses)
if( !(slot_flags & SLOT_EYES) )
return 0
if(H.glasses)
if(!(H.glasses.flags & NODROP))
return 2
else
return 0
return 1
if(slot_head)
if( !(slot_flags & SLOT_HEAD) )
return 0
if(H.head)
if(!(H.head.flags & NODROP))
return 2
else
return 0
return 1
if(slot_l_ear)
if( !(slot_flags & slot_l_ear) )
return 0
if(H.l_ear)
if(!(H.l_ear.flags & NODROP))
return 2
else
return 0
return 1
if(slot_r_ear)
if( !(slot_flags & slot_r_ear) )
return 0
if(H.r_ear)
if(!(H.r_ear.flags & NODROP))
return 2
else
return 0
return 1
if(slot_w_uniform)
if( !(slot_flags & SLOT_ICLOTHING) )
return 0
if((FAT in H.mutations) && !(flags_size & ONESIZEFITSALL))
return 0
if(H.w_uniform)
if(!(H.w_uniform.flags & NODROP))
return 2
else
return 0
return 1
if(slot_wear_id)
if(!H.w_uniform)
if(!disable_warning)
to_chat(H, "<span class='warning'>You need a jumpsuit before you can attach this [name].</span>")
return 0
if( !(slot_flags & SLOT_ID) )
return 0
if(H.wear_id)
if(!(H.wear_id.flags & NODROP))
return 2
else
return 0
return 1
if(slot_l_store)
if(H.l_store)
return 0
if(!H.w_uniform)
if(!disable_warning)
to_chat(H, "<span class='warning'>You need a jumpsuit before you can attach this [name].</span>")
return 0
if(slot_flags & SLOT_DENYPOCKET)
return
if( w_class <= WEIGHT_CLASS_SMALL || (slot_flags & SLOT_POCKET) )
return 1
if(slot_r_store)
if(H.r_store)
return 0
if(!H.w_uniform)
if(!disable_warning)
to_chat(H, "<span class='warning'>You need a jumpsuit before you can attach this [name].</span>")
return 0
if(slot_flags & SLOT_DENYPOCKET)
return 0
if( w_class <= WEIGHT_CLASS_SMALL || (slot_flags & SLOT_POCKET) )
return 1
return 0
if(slot_s_store)
if(!H.wear_suit)
if(!disable_warning)
to_chat(H, "<span class='warning'>You need a suit before you can attach this [name].</span>")
return 0
if(!H.wear_suit.allowed)
if(!disable_warning)
to_chat(usr, "You somehow have a suit with no defined allowed items for suit storage, stop that.")
return 0
if(src.w_class > WEIGHT_CLASS_BULKY)
if(!disable_warning)
to_chat(usr, "The [name] is too big to attach.")
return 0
if( istype(src, /obj/item/pda) || istype(src, /obj/item/pen) || is_type_in_list(src, H.wear_suit.allowed) )
if(H.s_store)
if(!(H.s_store.flags & NODROP))
return 2
else
return 0
else
return 1
return 0
if(slot_handcuffed)
if(H.handcuffed)
return 0
if(!istype(src, /obj/item/restraints/handcuffs))
return 0
return 1
if(slot_legcuffed)
if(H.legcuffed)
return 0
if(!istype(src, /obj/item/restraints/legcuffs))
return 0
return 1
if(slot_in_backpack)
if(H.back && istype(H.back, /obj/item/storage/backpack))
var/obj/item/storage/backpack/B = H.back
if(B.contents.len < B.storage_slots && w_class <= B.max_w_class)
return 1
return 0
return 0 //Unsupported slot
//END HUMAN
// If you're looking for `reset_perspective`, that's a synonym for this proc.
/mob/proc/reset_perspective(atom/A)
if(client)
if(istype(A, /atom/movable))
client.perspective = EYE_PERSPECTIVE
client.eye = A
else
if(isturf(loc))
client.eye = client.mob
client.perspective = MOB_PERSPECTIVE
else
client.perspective = EYE_PERSPECTIVE
client.eye = loc
return 1
/mob/living/reset_perspective(atom/A)
. = ..()
if(.)
// Above check means the mob has a client
update_sight()
if(client.eye != src)
var/atom/AT = client.eye
AT.get_remote_view_fullscreens(src)
else
clear_fullscreen("remote_view", 0)
update_pipe_vision()
/mob/dead/reset_perspective(atom/A)
if(client)
if(ismob(client.eye) && (client.eye != src))
// Note to self: Use `client.eye` for ghost following in place
// of periodic ghost updates
var/mob/target = client.eye
target.following_mobs -= src
. = ..()
if(.)
// Allows sharing HUDs with ghosts
if(hud_used)
client.screen = list()
hud_used.show_hud(hud_used.hud_version)
/mob/setDir(new_dir)
if(forced_look)
if(isnum(forced_look))
dir = forced_look
else
var/atom/A = locateUID(forced_look)
if(istype(A))
dir = get_cardinal_dir(src, A)
return
. = ..()
/mob/proc/show_inv(mob/user)
user.set_machine(src)
var/dat = {"<table>
<tr><td><B>Left Hand:</B></td><td><A href='?src=[UID()];item=[slot_l_hand]'>[(l_hand && !(l_hand.flags&ABSTRACT)) ? l_hand : "<font color=grey>Empty</font>"]</A></td></tr>
<tr><td><B>Right Hand:</B></td><td><A href='?src=[UID()];item=[slot_r_hand]'>[(r_hand && !(r_hand.flags&ABSTRACT)) ? r_hand : "<font color=grey>Empty</font>"]</A></td></tr>
<tr><td>&nbsp;</td></tr>"}
dat += {"</table>
<A href='?src=[user.UID()];mach_close=mob\ref[src]'>Close</A>
"}
var/datum/browser/popup = new(user, "mob\ref[src]", "[src]", 440, 250)
popup.set_content(dat)
popup.open()
//mob verbs are faster than object verbs. See http://www.byond.com/forum/?post=1326139&page=2#comment8198716 for why this isn't atom/verb/examine()
/mob/verb/examinate(atom/A as mob|obj|turf in view())
set name = "Examine"
set category = "IC"
if(!has_vision(information_only = TRUE) && !isobserver(src))
to_chat(src, "<span class='notice'>Something is there but you can't see it.</span>")
return 1
face_atom(A)
A.examine(src)
//same as above
//note: ghosts can point, this is intended
//visible_message will handle invisibility properly
//overriden here and in /mob/dead/observer for different point span classes and sanity checks
/mob/verb/pointed(atom/A as mob|obj|turf in view())
set name = "Point To"
set category = "Object"
if(next_move >= world.time)
return
if(!src || !isturf(src.loc))
return 0
if(istype(A, /obj/effect/temp_visual/point))
return 0
var/tile = get_turf(A)
if(!tile)
return 0
changeNext_move(CLICK_CD_POINT)
var/obj/P = new /obj/effect/temp_visual/point(tile)
P.invisibility = invisibility
return 1
/mob/proc/ret_grab(obj/effect/list_container/mobl/L as obj, flag)
if((!( istype(l_hand, /obj/item/grab) ) && !( istype(r_hand, /obj/item/grab) )))
if(!( L ))
return null
else
return L.container
else
if(!( L ))
L = new /obj/effect/list_container/mobl( null )
L.container += src
L.master = src
if(istype(l_hand, /obj/item/grab))
var/obj/item/grab/G = l_hand
if(!( L.container.Find(G.affecting) ))
L.container += G.affecting
if(G.affecting)
G.affecting.ret_grab(L, 1)
if(istype(r_hand, /obj/item/grab))
var/obj/item/grab/G = r_hand
if(!( L.container.Find(G.affecting) ))
L.container += G.affecting
if(G.affecting)
G.affecting.ret_grab(L, 1)
if(!( flag ))
if(L.master == src)
var/list/temp = list( )
temp += L.container
//L = null
qdel(L)
return temp
else
return L.container
return
/mob/verb/mode()
set name = "Activate Held Object"
set category = null
set src = usr
if(istype(loc,/obj/mecha)) return
if(hand)
var/obj/item/W = l_hand
if(W)
W.attack_self(src)
update_inv_l_hand()
else
var/obj/item/W = r_hand
if(W)
W.attack_self(src)
update_inv_r_hand()
return
/*
/mob/verb/dump_source()
var/master = "<PRE>"
for(var/t in typesof(/area))
master += text("[]\n", t)
//Foreach goto(26)
src << browse(master)
return
*/
/mob/verb/memory()
set name = "Notes"
set category = "IC"
if(mind)
mind.show_memory(src)
else
to_chat(src, "The game appears to have misplaced your mind datum, so we can't show you your notes.")
/mob/verb/add_memory(msg as message)
set name = "Add Note"
set category = "IC"
msg = copytext(msg, 1, MAX_MESSAGE_LEN)
msg = sanitize_simple(html_encode(msg), list("\n" = "<BR>"))
if(mind)
mind.store_memory(msg)
else
to_chat(src, "The game appears to have misplaced your mind datum, so we can't show you your notes.")
/mob/proc/store_memory(msg as message, popup, sane = 1)
msg = copytext(msg, 1, MAX_MESSAGE_LEN)
if(sane)
msg = sanitize(msg)
if(length(memory) == 0)
memory += msg
else
memory += "<BR>[msg]"
if(popup)
memory()
/mob/proc/update_flavor_text()
set src in usr
if(usr != src)
to_chat(usr, "No.")
var/msg = input(usr,"Set the flavor text in your 'examine' verb. The flavor text should be a physical descriptor of your character at a glance.","Flavor Text",html_decode(flavor_text)) as message|null
if(msg != null)
msg = copytext(msg, 1, MAX_MESSAGE_LEN)
msg = html_encode(msg)
flavor_text = msg
/mob/proc/print_flavor_text(var/shrink = 1)
if(flavor_text && flavor_text != "")
var/msg = replacetext(flavor_text, "\n", " ")
if(lentext(msg) <= 40 || !shrink)
return "<span class='notice'>[html_encode(msg)]</span>" //Repeat after me, "I will not give players access to decoded HTML."
else
return "<span class='notice'>[copytext_preserve_html(msg, 1, 37)]... <a href='byond://?src=[UID()];flavor_more=1'>More...</a></span>"
/mob/proc/is_dead()
return stat == DEAD
/mob
var/newPlayerType = /mob/new_player
/mob/verb/abandon_mob()
set name = "Respawn"
set category = "OOC"
if(!abandon_allowed)
to_chat(usr, "<span class='warning'>Respawning is disabled.</span>")
return
if(stat != DEAD || !ticker)
to_chat(usr, "<span class='boldnotice'>You must be dead to use this!</span>")
return
log_game("[key_name(usr)] has respawned.")
to_chat(usr, "<span class='boldnotice'>Make sure to play a different character, and please roleplay correctly!</span>")
if(!client)
log_game("[key_name(usr)] respawn failed due to disconnect.")
return
client.screen.Cut()
client.screen += client.void
if(!client)
log_game("[key_name(usr)] respawn failed due to disconnect.")
return
var/mob/new_player/M = new /mob/new_player()
if(!client)
log_game("[key_name(usr)] respawn failed due to disconnect.")
qdel(M)
return
M.key = key
return
/mob/verb/observe()
set name = "Observe"
set category = "OOC"
var/is_admin = 0
if(client.holder && (client.holder.rights & R_ADMIN))
is_admin = 1
else if(stat != DEAD || istype(src, /mob/new_player))
to_chat(usr, "<span class='notice'>You must be observing to use this!</span>")
return
if(is_admin && stat == DEAD)
is_admin = 0
var/list/names = list()
var/list/namecounts = list()
var/list/creatures = list()
for(var/obj/O in world) //EWWWWWWWWWWWWWWWWWWWWWWWW ~needs to be optimised
if(!O.loc)
continue
if(istype(O, /obj/item/disk/nuclear))
var/name = "Nuclear Disk"
if(names.Find(name))
namecounts[name]++
name = "[name] ([namecounts[name]])"
else
names.Add(name)
namecounts[name] = 1
creatures[name] = O
if(istype(O, /obj/singularity))
var/name = "Singularity"
if(names.Find(name))
namecounts[name]++
name = "[name] ([namecounts[name]])"
else
names.Add(name)
namecounts[name] = 1
creatures[name] = O
for(var/mob/M in sortAtom(GLOB.mob_list))
var/name = M.name
if(names.Find(name))
namecounts[name]++
name = "[name] ([namecounts[name]])"
else
names.Add(name)
namecounts[name] = 1
creatures[name] = M
client.perspective = EYE_PERSPECTIVE
var/eye_name = null
var/ok = "[is_admin ? "Admin Observe" : "Observe"]"
eye_name = input("Please, select a player!", ok, null, null) as null|anything in creatures
if(!eye_name)
return
var/mob/mob_eye = creatures[eye_name]
if(client && mob_eye)
client.eye = mob_eye
if(is_admin)
client.adminobs = 1
if(mob_eye == client.mob || client.eye == client.mob)
client.adminobs = 0
/mob/verb/cancel_camera()
set name = "Cancel Camera View"
set category = "OOC"
reset_perspective(null)
unset_machine()
if(istype(src, /mob/living))
if(src:cameraFollow)
src:cameraFollow = null
/mob/Topic(href, href_list)
if(href_list["mach_close"])
var/t1 = text("window=[href_list["mach_close"]]")
unset_machine()
src << browse(null, t1)
if(href_list["refresh"])
if(machine && in_range(src, usr))
show_inv(machine)
if(!usr.incapacitated() && in_range(src, usr))
if(href_list["item"])
var/slot = text2num(href_list["item"])
var/obj/item/what = get_item_by_slot(slot)
if(what)
usr.stripPanelUnequip(what,src,slot)
else
usr.stripPanelEquip(what,src,slot)
if(usr.machine == src)
if(Adjacent(usr))
show_inv(usr)
else
usr << browse(null,"window=mob\ref[src]")
if(href_list["flavor_more"])
usr << browse(text("<HTML><HEAD><TITLE>[]</TITLE></HEAD><BODY><TT>[]</TT></BODY></HTML>", name, replacetext(flavor_text, "\n", "<BR>")), text("window=[];size=500x200", name))
onclose(usr, "[name]")
if(href_list["flavor_change"])
update_flavor_text()
return
// The src mob is trying to strip an item from someone
// Defined in living.dm
/mob/proc/stripPanelUnequip(obj/item/what, mob/who)
return
// The src mob is trying to place an item on someone
// Defined in living.dm
/mob/proc/stripPanelEquip(obj/item/what, mob/who)
return
/mob/MouseDrop(mob/M as mob)
..()
if(M != usr) return
if(isliving(M)) // Ewww
var/mob/living/L = M
if(L.mob_size <= MOB_SIZE_SMALL)
return // Stops pAI drones and small mobs (borers, parrots, crabs) from stripping people. --DZD
if(!M.can_strip) return
if(usr == src) return
if(!Adjacent(usr)) return
show_inv(usr)
/mob/proc/can_use_hands()
return
/mob/proc/is_mechanical()
return mind && (mind.assigned_role == "Cyborg" || mind.assigned_role == "AI")
/mob/proc/is_ready()
return client && !!mind
/mob/proc/is_in_brig()
if(!loc || !loc.loc)
return 0
// They should be in a cell or the Brig portion of the shuttle.
var/area/A = loc.loc
if(!istype(A, /area/security/prison) && !istype(A, /area/prison))
if(!istype(A, /area/shuttle/escape) || loc.name != "Brig floor")
return 0
// If they still have their ID they're not brigged.
for(var/obj/item/card/id/card in src)
return 0
for(var/obj/item/pda/P in src)
if(P.id)
return 0
return 1
/mob/proc/get_gender()
return gender
/mob/proc/is_muzzled()
return 0
/mob/Stat()
..()
show_stat_turf_contents()
statpanel("Status") // We only want alt-clicked turfs to come before Status
if(mind && mind.changeling)
add_stings_to_statpanel(mind.changeling.purchasedpowers)
if(mob_spell_list && mob_spell_list.len)
for(var/obj/effect/proc_holder/spell/S in mob_spell_list)
add_spell_to_statpanel(S)
if(mind && istype(src, /mob/living) && mind.spell_list && mind.spell_list.len)
for(var/obj/effect/proc_holder/spell/S in mind.spell_list)
add_spell_to_statpanel(S)
if(is_admin(src))
if(statpanel("DI")) //not looking at that panel
stat("Loc", "([x], [y], [z]) [loc]")
stat("CPU", "[world.cpu]")
stat("Instances", "[world.contents.len]")
if(processScheduler)
processScheduler.statProcesses()
if(statpanel("MC")) //looking at that panel
var/turf/T = get_turf(client.eye)
stat("Location:", COORD(T))
stat("CPU:", "[world.cpu]")
stat("Instances:", "[num2text(world.contents.len, 10)]")
GLOB.stat_entry()
stat(null)
if(Master)
Master.stat_entry()
else
stat("Master Controller:", "ERROR")
if(Failsafe)
Failsafe.stat_entry()
else
stat("Failsafe Controller:", "ERROR")
if(Master)
stat(null)
for(var/datum/controller/subsystem/SS in Master.subsystems)
SS.stat_entry()
statpanel("Status") // Switch to the Status panel again, for the sake of the lazy Stat procs
// this function displays the station time in the status panel
/mob/proc/show_stat_station_time()
stat(null, "Round Time: [worldtime2text()]")
stat(null, "Station Time: [station_time_timestamp()]")
// this function displays the shuttles ETA in the status panel if the shuttle has been called
/mob/proc/show_stat_emergency_shuttle_eta()
var/ETA = SSshuttle.emergency.getModeStr()
if(ETA)
stat(null, "[ETA] [SSshuttle.emergency.getTimerStr()]")
/mob/proc/show_stat_turf_contents()
if(listed_turf && client)
if(!TurfAdjacent(listed_turf))
listed_turf = null
else
statpanel(listed_turf.name, null, listed_turf)
var/list/statpanel_things = list()
for(var/foo in listed_turf)
var/atom/A = foo
if(A.invisibility > see_invisible)
continue
if(is_type_in_list(A, shouldnt_see))
continue
statpanel_things += A
statpanel(listed_turf.name, null, statpanel_things)
/mob/proc/add_stings_to_statpanel(var/list/stings)
for(var/obj/effect/proc_holder/changeling/S in stings)
if(S.chemical_cost >=0 && S.can_be_used_by(src))
statpanel("[S.panel]",((S.chemical_cost > 0) ? "[S.chemical_cost]" : ""),S)
/mob/proc/add_spell_to_statpanel(var/obj/effect/proc_holder/spell/S)
switch(S.charge_type)
if("recharge")
statpanel(S.panel,"[S.charge_counter/10.0]/[S.charge_max/10]",S)
if("charges")
statpanel(S.panel,"[S.charge_counter]/[S.charge_max]",S)
if("holdervar")
statpanel(S.panel,"[S.holder_var_type] [S.holder_var_amount]",S)
// facing verbs
/mob/proc/canface()
if(!canmove) return 0
if(client.moving) return 0
if(world.time < client.move_delay) return 0
if(stat==2) return 0
if(anchored) return 0
if(notransform) return 0
if(restrained()) return 0
return 1
/mob/proc/fall(var/forced)
drop_l_hand()
drop_r_hand()
/mob/proc/facedir(ndir)
if(!canface())
return 0
setDir(ndir)
client.move_delay += movement_delay()
return 1
/mob/verb/eastface()
set hidden = 1
return facedir(EAST)
/mob/verb/westface()
set hidden = 1
return facedir(WEST)
/mob/verb/northface()
set hidden = 1
return facedir(NORTH)
/mob/verb/southface()
set hidden = 1
return facedir(SOUTH)
/mob/proc/IsAdvancedToolUser()//This might need a rename but it should replace the can this mob use things check
return 0
/mob/proc/swap_hand()
return
/mob/proc/activate_hand(selhand)
return
/mob/dead/observer/verb/respawn()
set name = "Respawn as NPC"
set category = "Ghost"
if(jobban_isbanned(usr, ROLE_SENTIENT))
to_chat(usr, "<span class='warning'>You are banned from playing as sentient animals.</span>")
return
if(!ticker || ticker.current_state < 3)
to_chat(src, "<span class='warning'>You can't respawn as an NPC before the game starts!</span>")
return
if((usr in GLOB.respawnable_list) && (stat==2 || istype(usr,/mob/dead/observer)))
var/list/creatures = list("Mouse")
for(var/mob/living/L in GLOB.living_mob_list)
if(safe_respawn(L.type) && L.stat!=2)
if(!L.key)
creatures += L
var/picked = input("Please select an NPC to respawn as", "Respawn as NPC") as null|anything in creatures
switch(picked)
if("Mouse")
GLOB.respawnable_list -= usr
become_mouse()
spawn(5)
GLOB.respawnable_list += usr
else
var/mob/living/NPC = picked
if(istype(NPC) && !NPC.key)
GLOB.respawnable_list -= usr
NPC.key = key
spawn(5)
GLOB.respawnable_list += usr
else
to_chat(usr, "You are not dead or you have given up your right to be respawned!")
return
/mob/proc/become_mouse()
var/timedifference = world.time - client.time_died_as_mouse
if(client.time_died_as_mouse && timedifference <= mouse_respawn_time * 600)
var/timedifference_text
timedifference_text = time2text(mouse_respawn_time * 600 - timedifference,"mm:ss")
to_chat(src, "<span class='warning'>You may only spawn again as a mouse more than [mouse_respawn_time] minutes after your death. You have [timedifference_text] left.</span>")
return
//find a viable mouse candidate
var/mob/living/simple_animal/mouse/host
var/obj/machinery/atmospherics/unary/vent_pump/vent_found
var/list/found_vents = list()
for(var/obj/machinery/atmospherics/unary/vent_pump/v in world)
if(!v.welded && v.z == src.z)
found_vents.Add(v)
if(found_vents.len)
vent_found = pick(found_vents)
host = new /mob/living/simple_animal/mouse(vent_found.loc)
else
to_chat(src, "<span class='warning'>Unable to find any unwelded vents to spawn mice at.</span>")
if(host)
host.ckey = src.ckey
to_chat(host, "<span class='info'>You are now a mouse. Try to avoid interaction with players, and do not give hints away that you are more than a simple rodent.</span>")
/mob/proc/assess_threat() //For sec bot threat assessment
return 5
/mob/proc/get_ghost(even_if_they_cant_reenter = 0)
if(mind)
return mind.get_ghost(even_if_they_cant_reenter)
/mob/proc/grab_ghost(force)
if(mind)
return mind.grab_ghost(force = force)
/mob/proc/notify_ghost_cloning(message = "Someone is trying to revive you. Re-enter your corpse if you want to be revived!", sound = 'sound/effects/genetics.ogg', atom/source = null, flashwindow = TRUE)
var/mob/dead/observer/ghost = get_ghost()
if(ghost)
if(flashwindow)
window_flash(ghost.client)
ghost.notify_cloning(message, sound, source)
return ghost
/mob/proc/fakevomit(green = 0, no_text = 0) //for aesthetic vomits that need to be instant and do not stun. -Fox
if(stat==DEAD)
return
var/turf/location = loc
if(istype(location, /turf/simulated))
if(green)
if(!no_text)
visible_message("<span class='warning'>[src] vomits up some green goo!</span>","<span class='warning'>You vomit up some green goo!</span>")
new /obj/effect/decal/cleanable/vomit/green(location)
else
if(!no_text)
visible_message("<span class='warning'>[src] pukes all over [p_them()]self!</span>","<span class='warning'>You puke all over yourself!</span>")
location.add_vomit_floor(src, 1)
playsound(location, 'sound/effects/splat.ogg', 50, 1)
/mob/proc/AddSpell(obj/effect/proc_holder/spell/S)
mob_spell_list += S
S.action.Grant(src)
/mob/proc/RemoveSpell(obj/effect/proc_holder/spell/spell) //To remove a specific spell from a mind
if(!spell)
return
for(var/obj/effect/proc_holder/spell/S in mob_spell_list)
if(istype(S, spell))
qdel(S)
mob_spell_list -= S
//override to avoid rotating pixel_xy on mobs
/mob/shuttleRotate(rotation)
dir = angle2dir(rotation+dir2angle(dir))
/mob/proc/handle_ventcrawl()
return // Only living mobs can ventcrawl
//You can buckle on mobs if you're next to them since most are dense
/mob/buckle_mob(mob/living/M, force = 0)
if(M.buckled)
return 0
var/turf/T = get_turf(src)
if(M.loc != T)
var/old_density = density
density = 0
var/can_step = step_towards(M, T)
density = old_density
if(!can_step)
return 0
return ..()
//Default buckling shift visual for mobs
/mob/post_buckle_mob(mob/living/M)
if(M == buckled_mob) //post buckling
M.pixel_y = initial(M.pixel_y) + 9
if(M.layer < layer)
M.layer = layer + 0.1
else //post unbuckling
M.layer = initial(M.layer)
M.pixel_y = initial(M.pixel_y)
/mob/proc/can_unbuckle(mob/user)
return 1
//Can the mob see reagents inside of containers?
/mob/proc/can_see_reagents()
return 0
//Can this mob leave its location without breaking things terrifically?
/mob/proc/can_safely_leave_loc()
return 1 // Yes, you can
/mob/proc/IsVocal()
return 1
/mob/proc/get_access()
return list() //must return list or IGNORE_ACCESS
/mob/proc/faction_check(mob/target)
for(var/F in faction)
if(F in target.faction)
return 1
return 0
/mob/proc/create_attack_log(text, collapse = TRUE)
LAZYINITLIST(attack_log)
create_log_in_list(attack_log, text, collapse, last_log)
last_log = world.timeofday
/mob/proc/create_debug_log(text, collapse = TRUE)
LAZYINITLIST(debug_log)
create_log_in_list(debug_log, text, collapse, world.timeofday)
/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."
//That makes the logs easier to read, but because all of this is stored in strings, weird things have to be used to get it all out.
var/new_log = "\[[time_stamp()]] [text]"
if(target.len)//if there are other logs already present
var/previous_log = target[target.len]//get the latest log
var/last_log_is_range = (copytext(previous_log, 10, 11) == "-") //whether the last log is a time range or not. The "-" will be an indicator that it is.
var/x_sign_position = findtext(previous_log, "x")
if(world.timeofday - last_log > 100)//if more than 10 seconds from last log
collapse = 0//don't collapse anyway
//the following checks if the last log has the same contents as the new one
if(last_log_is_range)
if(!(copytext(previous_log, x_sign_position + 13) == text))//the 13 is there because of span classes; you won't see those normally in-game
collapse = 0
else
if(!(copytext(previous_log, 12) == text))
collapse = 0
if(collapse == 1)
var/rep = 0
var/old_timestamp = copytext(previous_log, 2, 10)//copy the first time value. This one doesn't move when it's a timespan, so no biggie
//An attack log entry can either be a time range with multiple occurences of an action or a single one, with just one time stamp
if(last_log_is_range)
rep = text2num(copytext(previous_log, 44, x_sign_position))//get whatever number is right before the 'x'
new_log = "\[[old_timestamp]-[time_stamp()]]<font color='purple'><b>[rep?rep+1:2]x</b></font> [text]"
target -= target[target.len]//remove the last log
target += new_log
/mob/vv_get_dropdown()
. = ..()
.["Show player panel"] = "?_src_=vars;mob_player_panel=[UID()]"
.["Give Spell"] = "?_src_=vars;give_spell=[UID()]"
.["Give Disease"] = "?_src_=vars;give_disease=[UID()]"
.["Toggle Godmode"] = "?_src_=vars;godmode=[UID()]"
.["Toggle Build Mode"] = "?_src_=vars;build_mode=[UID()]"
.["Make 2spooky"] = "?_src_=vars;make_skeleton=[UID()]"
.["Assume Direct Control"] = "?_src_=vars;direct_control=[UID()]"
.["Offer Control to Ghosts"] = "?_src_=vars;offer_control=[UID()]"
.["Drop Everything"] = "?_src_=vars;drop_everything=[UID()]"
.["Regenerate Icons"] = "?_src_=vars;regenerateicons=[UID()]"
.["Add Language"] = "?_src_=vars;addlanguage=[UID()]"
.["Remove Language"] = "?_src_=vars;remlanguage=[UID()]"
.["Add Organ"] = "?_src_=vars;addorgan=[UID()]"
.["Remove Organ"] = "?_src_=vars;remorgan=[UID()]"
.["Fix NanoUI"] = "?_src_=vars;fix_nano=[UID()]"
.["Add Verb"] = "?_src_=vars;addverb=[UID()]"
.["Remove Verb"] = "?_src_=vars;remverb=[UID()]"
.["Gib"] = "?_src_=vars;gib=[UID()]"
/mob/proc/spin(spintime, speed)
set waitfor = 0
var/D = dir
while(spintime >= speed)
sleep(speed)
switch(D)
if(NORTH)
D = EAST
if(SOUTH)
D = WEST
if(EAST)
D = SOUTH
if(WEST)
D = NORTH
setDir(D)
spintime -= speed
/mob/proc/is_literate()
return FALSE