Uploading all files.

This commit is contained in:
Quotefox
2019-09-23 18:26:34 +02:00
commit 7091cdbe8c
7779 changed files with 2601004 additions and 0 deletions
+34
View File
@@ -0,0 +1,34 @@
// Camera mob, used by AI camera and blob.
/mob/camera
name = "camera mob"
density = FALSE
anchored = TRUE
status_flags = GODMODE // You can't damage it.
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
see_in_dark = 7
invisibility = INVISIBILITY_ABSTRACT // No one can see us
sight = SEE_SELF
move_on_shuttle = FALSE
var/call_life = FALSE //TRUE if Life() should be called on this camera every tick of the mobs subystem, as if it were a living mob
/mob/camera/Initialize()
. = ..()
if(call_life)
GLOB.living_cameras += src
/mob/camera/Destroy()
. = ..()
if(call_life)
GLOB.living_cameras -= src
/mob/camera/experience_pressure_difference()
return
/mob/camera/forceMove(atom/destination)
var/oldloc = loc
loc = destination
Moved(oldloc, NONE, TRUE)
/mob/camera/emote(act, m_type=1, message = null, intentional = FALSE)
return
+126
View File
@@ -0,0 +1,126 @@
//Dead mobs can exist whenever. This is needful
INITIALIZE_IMMEDIATE(/mob/dead)
/mob/dead
sight = SEE_TURFS | SEE_MOBS | SEE_OBJS | SEE_SELF
throwforce = 0
/mob/dead/Initialize()
if(flags_1 & INITIALIZED_1)
stack_trace("Warning: [src]([type]) initialized multiple times!")
flags_1 |= INITIALIZED_1
tag = "mob_[next_mob_id++]"
GLOB.mob_list += src
prepare_huds()
if(length(CONFIG_GET(keyed_list/cross_server)))
verbs += /mob/dead/proc/server_hop
set_focus(src)
return INITIALIZE_HINT_NORMAL
/mob/dead/dust(just_ash, drop_items, force) //ghosts can't be vaporised.
return
/mob/dead/gib() //ghosts can't be gibbed.
return
/mob/dead/ConveyorMove() //lol
return
/mob/dead/forceMove(atom/destination)
var/turf/old_turf = get_turf(src)
var/turf/new_turf = get_turf(destination)
if (old_turf?.z != new_turf?.z)
onTransitZ(old_turf?.z, new_turf?.z)
var/oldloc = loc
loc = destination
Moved(oldloc, NONE, TRUE)
/mob/dead/Stat()
..()
if(!statpanel("Status"))
return
//stat(null, "Game Mode: [SSticker.hide_mode ? "Secret" : "[GLOB.master_mode]"]")
if(SSticker.HasRoundStarted())
return
var/time_remaining = SSticker.GetTimeLeft()
if(time_remaining > 0)
stat(null, "Time To Start: [round(time_remaining/10)]s")
else if(time_remaining == -10)
stat(null, "Time To Start: DELAYED")
else
stat(null, "Time To Start: SOON")
stat(null, "Players: [SSticker.totalPlayers]")
if(client.holder)
stat(null, "Players Ready: [SSticker.totalPlayersReady]")
/mob/dead/proc/server_hop()
set category = "OOC"
set name = "Server Hop!"
set desc= "Jump to the other server"
if(notransform)
return
var/list/csa = CONFIG_GET(keyed_list/cross_server)
var/pick
switch(csa.len)
if(0)
verbs -= /mob/dead/proc/server_hop
to_chat(src, "<span class='notice'>Server Hop has been disabled.</span>")
if(1)
pick = csa[0]
else
pick = input(src, "Pick a server to jump to", "Server Hop") as null|anything in csa
if(!pick)
return
var/addr = csa[pick]
if(alert(src, "Jump to server [pick] ([addr])?", "Server Hop", "Yes", "No") != "Yes")
return
var/client/C = client
to_chat(C, "<span class='notice'>Sending you to [pick].</span>")
new /obj/screen/splash(C)
notransform = TRUE
sleep(29) //let the animation play
notransform = FALSE
if(!C)
return
winset(src, null, "command=.options") //other wise the user never knows if byond is downloading resources
C << link("[addr]?server_hop=[key]")
/mob/dead/proc/update_z(new_z) // 1+ to register, null to unregister
if (registered_z != new_z)
if (registered_z)
SSmobs.dead_players_by_zlevel[registered_z] -= src
if (client)
if (new_z)
SSmobs.dead_players_by_zlevel[new_z] += src
registered_z = new_z
else
registered_z = null
/mob/dead/Login()
. = ..()
var/turf/T = get_turf(src)
if (isturf(T))
update_z(T.z)
/mob/dead/Logout()
update_z(null)
return ..()
/mob/dead/onTransitZ(old_z,new_z)
..()
update_z(new_z)
+34
View File
@@ -0,0 +1,34 @@
/mob/dead/new_player/Login()
if(CONFIG_GET(flag/use_exp_tracking))
client.set_exp_from_db()
client.set_db_player_flags()
if(!mind)
mind = new /datum/mind(key)
mind.active = 1
mind.current = src
..()
var/motd = global.config.motd
if(motd)
to_chat(src, "<div class=\"motd\">[motd]</div>", handle_whitespace=FALSE)
if(GLOB.admin_notice)
to_chat(src, "<span class='notice'><b>Admin Notice:</b>\n \t [GLOB.admin_notice]</span>")
var/spc = CONFIG_GET(number/soft_popcap)
if(spc && living_player_count() >= spc)
to_chat(src, "<span class='notice'><b>Server Notice:</b>\n \t [CONFIG_GET(string/soft_popcap_message)]</span>")
sight |= SEE_TURFS
new_player_panel()
client.playtitlemusic()
if(SSticker.current_state < GAME_STATE_SETTING_UP)
var/tl = SSticker.GetTimeLeft()
var/postfix
if(tl > 0)
postfix = "in about [DisplayTimeText(tl)]"
else
postfix = "soon"
to_chat(src, "Please set up your character and select \"Ready\". The game will start [postfix].")
@@ -0,0 +1,7 @@
/mob/dead/new_player/Logout()
ready = 0
..()
if(!spawning)//Here so that if they are spawning and log out, the other procs can play out and they will have a mob to come back to.
key = null//We null their key before deleting the mob, so they are properly kicked out.
qdel(src)
return
@@ -0,0 +1,613 @@
#define LINKIFY_READY(string, value) "<a href='byond://?src=[REF(src)];ready=[value]'>[string]</a>"
/mob/dead/new_player
var/ready = 0
var/spawning = 0//Referenced when you want to delete the new_player later on in the code.
flags_1 = NONE
invisibility = INVISIBILITY_ABSTRACT
density = FALSE
stat = DEAD
canmove = FALSE
anchored = TRUE // don't get pushed around
var/mob/living/new_character //for instant transfer once the round is set up
/mob/dead/new_player/Initialize()
if(client && SSticker.state == GAME_STATE_STARTUP)
var/obj/screen/splash/S = new(client, TRUE, TRUE)
S.Fade(TRUE)
if(length(GLOB.newplayer_start))
forceMove(pick(GLOB.newplayer_start))
else
forceMove(locate(1,1,1))
ComponentInitialize()
. = ..()
/mob/dead/new_player/prepare_huds()
return
/mob/dead/new_player/proc/new_player_panel()
var/output = "<center><p>Welcome, <b>[client ? client.prefs.real_name : "Unknown User"]</b></p>"
output += "<p><a href='byond://?src=[REF(src)];show_preferences=1'>Setup Character</a></p>"
if(SSticker.current_state <= GAME_STATE_PREGAME)
switch(ready)
if(PLAYER_NOT_READY)
output += "<p>\[ [LINKIFY_READY("Ready", PLAYER_READY_TO_PLAY)] | <b>Not Ready</b> | [LINKIFY_READY("Observe", PLAYER_READY_TO_OBSERVE)] \]</p>"
if(PLAYER_READY_TO_PLAY)
output += "<p>\[ <b>Ready</b> | [LINKIFY_READY("Not Ready", PLAYER_NOT_READY)] | [LINKIFY_READY("Observe", PLAYER_READY_TO_OBSERVE)] \]</p>"
if(PLAYER_READY_TO_OBSERVE)
output += "<p>\[ [LINKIFY_READY("Ready", PLAYER_READY_TO_PLAY)] | [LINKIFY_READY("Not Ready", PLAYER_NOT_READY)] | <b> Observe </b> \]</p>"
else
output += "<p><a href='byond://?src=[REF(src)];manifest=1'>View the Crew Manifest</a></p>"
output += "<p><a href='byond://?src=[REF(src)];late_join=1'>Join Game!</a></p>"
output += "<p>[LINKIFY_READY("Observe", PLAYER_READY_TO_OBSERVE)]</p>"
if(!IsGuestKey(src.key))
if (SSdbcore.Connect())
var/isadmin = 0
if(src.client && src.client.holder)
isadmin = 1
var/datum/DBQuery/query_get_new_polls = SSdbcore.NewQuery("SELECT id FROM [format_table_name("poll_question")] WHERE [(isadmin ? "" : "adminonly = false AND")] Now() BETWEEN starttime AND endtime AND id NOT IN (SELECT pollid FROM [format_table_name("poll_vote")] WHERE ckey = \"[sanitizeSQL(ckey)]\") AND id NOT IN (SELECT pollid FROM [format_table_name("poll_textreply")] WHERE ckey = \"[sanitizeSQL(ckey)]\")")
var/rs = REF(src)
if(query_get_new_polls.Execute())
var/newpoll = 0
if(query_get_new_polls.NextRow())
newpoll = 1
if(newpoll)
output += "<p><b><a href='byond://?src=[rs];showpoll=1'>Show Player Polls</A> (NEW!)</b></p>"
else
output += "<p><a href='byond://?src=[rs];showpoll=1'>Show Player Polls</A></p>"
qdel(query_get_new_polls)
if(QDELETED(src))
return
output += "</center>"
//src << browse(output,"window=playersetup;size=210x240;can_close=0")
var/datum/browser/popup = new(src, "playersetup", "<div align='center'>New Player Options</div>", 250, 265)
popup.set_window_options("can_close=0")
popup.set_content(output)
popup.open(0)
/mob/dead/new_player/Topic(href, href_list[])
if(src != usr)
return 0
if(!client)
return 0
//Determines Relevent Population Cap
var/relevant_cap
var/hpc = CONFIG_GET(number/hard_popcap)
var/epc = CONFIG_GET(number/extreme_popcap)
if(hpc && epc)
relevant_cap = min(hpc, epc)
else
relevant_cap = max(hpc, epc)
if(href_list["show_preferences"])
client.prefs.ShowChoices(src)
return 1
if(href_list["ready"])
var/tready = text2num(href_list["ready"])
//Avoid updating ready if we're after PREGAME (they should use latejoin instead)
//This is likely not an actual issue but I don't have time to prove that this
//no longer is required
if(SSticker.current_state <= GAME_STATE_PREGAME)
ready = tready
//if it's post initialisation and they're trying to observe we do the needful
if(!SSticker.current_state < GAME_STATE_PREGAME && tready == PLAYER_READY_TO_OBSERVE)
ready = tready
make_me_an_observer()
return
if(href_list["refresh"])
src << browse(null, "window=playersetup") //closes the player setup window
new_player_panel()
if(href_list["late_join"])
if(!SSticker || !SSticker.IsRoundInProgress())
to_chat(usr, "<span class='danger'>The round is either not ready, or has already finished...</span>")
return
if(href_list["late_join"] == "override")
LateChoices()
return
if(SSticker.queued_players.len || (relevant_cap && living_player_count() >= relevant_cap && !(ckey(key) in GLOB.admin_datums)))
to_chat(usr, "<span class='danger'>[CONFIG_GET(string/hard_popcap_message)]</span>")
var/queue_position = SSticker.queued_players.Find(usr)
if(queue_position == 1)
to_chat(usr, "<span class='notice'>You are next in line to join the game. You will be notified when a slot opens up.</span>")
else if(queue_position)
to_chat(usr, "<span class='notice'>There are [queue_position-1] players in front of you in the queue to join the game.</span>")
else
SSticker.queued_players += usr
to_chat(usr, "<span class='notice'>You have been added to the queue to join the game. Your position in queue is [SSticker.queued_players.len].</span>")
return
LateChoices()
if(href_list["manifest"])
ViewManifest()
if(href_list["SelectedJob"])
if(!GLOB.enter_allowed)
to_chat(usr, "<span class='notice'>There is an administrative lock on entering the game!</span>")
return
if(SSticker.queued_players.len && !(ckey(key) in GLOB.admin_datums))
if((living_player_count() >= relevant_cap) || (src != SSticker.queued_players[1]))
to_chat(usr, "<span class='warning'>Server is full.</span>")
return
AttemptLateSpawn(href_list["SelectedJob"])
return
if(href_list["JoinAsGhostRole"])
if(!GLOB.enter_allowed)
to_chat(usr, "<span class='notice'> There is an administrative lock on entering the game!</span>")
if(SSticker.queued_players.len && !(ckey(key) in GLOB.admin_datums))
if((living_player_count() >= relevant_cap) || (src != SSticker.queued_players[1]))
to_chat(usr, "<span class='warning'>Server is full.</span>")
return
var/obj/effect/mob_spawn/MS = pick(GLOB.mob_spawners[href_list["JoinAsGhostRole"]])
if(MS.attack_ghost(src, latejoinercalling = TRUE))
SSticker.queued_players -= src
SSticker.queue_delay = 4
qdel(src)
if(!ready && href_list["preference"])
if(client)
client.prefs.process_link(src, href_list)
else if(!href_list["late_join"])
new_player_panel()
if(href_list["showpoll"])
handle_player_polling()
return
if(href_list["pollid"])
var/pollid = href_list["pollid"]
if(istext(pollid))
pollid = text2num(pollid)
if(isnum(pollid) && ISINTEGER(pollid))
src.poll_player(pollid)
return
if(href_list["votepollid"] && href_list["votetype"])
var/pollid = text2num(href_list["votepollid"])
var/votetype = href_list["votetype"]
//lets take data from the user to decide what kind of poll this is, without validating it
//what could go wrong
switch(votetype)
if(POLLTYPE_OPTION)
var/optionid = text2num(href_list["voteoptionid"])
if(vote_on_poll(pollid, optionid))
to_chat(usr, "<span class='notice'>Vote successful.</span>")
else
to_chat(usr, "<span class='danger'>Vote failed, please try again or contact an administrator.</span>")
if(POLLTYPE_TEXT)
var/replytext = href_list["replytext"]
if(log_text_poll_reply(pollid, replytext))
to_chat(usr, "<span class='notice'>Feedback logging successful.</span>")
else
to_chat(usr, "<span class='danger'>Feedback logging failed, please try again or contact an administrator.</span>")
if(POLLTYPE_RATING)
var/id_min = text2num(href_list["minid"])
var/id_max = text2num(href_list["maxid"])
if( (id_max - id_min) > 100 ) //Basic exploit prevention
//(protip, this stops no exploits)
to_chat(usr, "The option ID difference is too big. Please contact administration or the database admin.")
return
for(var/optionid = id_min; optionid <= id_max; optionid++)
if(!isnull(href_list["o[optionid]"])) //Test if this optionid was replied to
var/rating
if(href_list["o[optionid]"] == "abstain")
rating = null
else
rating = text2num(href_list["o[optionid]"])
if(!isnum(rating) || !ISINTEGER(rating))
return
if(!vote_on_numval_poll(pollid, optionid, rating))
to_chat(usr, "<span class='danger'>Vote failed, please try again or contact an administrator.</span>")
return
to_chat(usr, "<span class='notice'>Vote successful.</span>")
if(POLLTYPE_MULTI)
var/id_min = text2num(href_list["minoptionid"])
var/id_max = text2num(href_list["maxoptionid"])
if( (id_max - id_min) > 100 ) //Basic exploit prevention
to_chat(usr, "The option ID difference is too big. Please contact administration or the database admin.")
return
for(var/optionid = id_min; optionid <= id_max; optionid++)
if(!isnull(href_list["option_[optionid]"])) //Test if this optionid was selected
var/i = vote_on_multi_poll(pollid, optionid)
switch(i)
if(0)
continue
if(1)
to_chat(usr, "<span class='danger'>Vote failed, please try again or contact an administrator.</span>")
return
if(2)
to_chat(usr, "<span class='danger'>Maximum replies reached.</span>")
break
to_chat(usr, "<span class='notice'>Vote successful.</span>")
if(POLLTYPE_IRV)
if (!href_list["IRVdata"])
to_chat(src, "<span class='danger'>No ordering data found. Please try again or contact an administrator.</span>")
return
var/list/votelist = splittext(href_list["IRVdata"], ",")
if (!vote_on_irv_poll(pollid, votelist))
to_chat(src, "<span class='danger'>Vote failed, please try again or contact an administrator.</span>")
return
to_chat(src, "<span class='notice'>Vote successful.</span>")
//When you cop out of the round (NB: this HAS A SLEEP FOR PLAYER INPUT IN IT)
/mob/dead/new_player/proc/make_me_an_observer()
if(QDELETED(src) || !src.client)
ready = PLAYER_NOT_READY
return FALSE
var/this_is_like_playing_right = alert(src,"Are you sure you wish to observe? You will not be able to play this round!","Player Setup","Yes","No")
if(QDELETED(src) || !src.client || this_is_like_playing_right != "Yes")
ready = PLAYER_NOT_READY
src << browse(null, "window=playersetup") //closes the player setup window
new_player_panel()
return FALSE
var/mob/dead/observer/observer = new()
spawning = TRUE
observer.started_as_observer = TRUE
close_spawn_windows()
var/obj/effect/landmark/observer_start/O = locate(/obj/effect/landmark/observer_start) in GLOB.landmarks_list
to_chat(src, "<span class='notice'>Now teleporting.</span>")
if (O)
observer.forceMove(O.loc)
else
to_chat(src, "<span class='notice'>Teleporting failed. Ahelp an admin please</span>")
stack_trace("There's no freaking observer landmark available on this map or you're making observers before the map is initialised")
observer.key = key
observer.client = client
observer.set_ghost_appearance()
if(observer.client && observer.client.prefs)
observer.real_name = observer.client.prefs.real_name
observer.name = observer.real_name
observer.update_icon()
observer.stop_sound_channel(CHANNEL_LOBBYMUSIC)
QDEL_NULL(mind)
qdel(src)
return TRUE
/proc/get_job_unavailable_error_message(retval, jobtitle)
switch(retval)
if(JOB_AVAILABLE)
return "[jobtitle] is available."
if(JOB_UNAVAILABLE_GENERIC)
return "[jobtitle] is unavailable."
if(JOB_UNAVAILABLE_BANNED)
return "You are currently banned from [jobtitle]."
if(JOB_UNAVAILABLE_PLAYTIME)
return "You do not have enough relevant playtime for [jobtitle]."
if(JOB_UNAVAILABLE_ACCOUNTAGE)
return "Your account is not old enough for [jobtitle]."
if(JOB_UNAVAILABLE_SLOTFULL)
return "[jobtitle] is already filled to capacity."
return "Error: Unknown job availability."
/mob/dead/new_player/proc/IsJobUnavailable(rank, latejoin = FALSE)
var/datum/job/job = SSjob.GetJob(rank)
if(!job)
return JOB_UNAVAILABLE_GENERIC
if((job.current_positions >= job.total_positions) && job.total_positions != -1)
if(job.title == "Assistant")
if(isnum(client.player_age) && client.player_age <= 14) //Newbies can always be assistants
return JOB_AVAILABLE
for(var/datum/job/J in SSjob.occupations)
if(J && J.current_positions < J.total_positions && J.title != job.title)
return JOB_UNAVAILABLE_SLOTFULL
else
return JOB_UNAVAILABLE_SLOTFULL
if(jobban_isbanned(src,rank))
return JOB_UNAVAILABLE_BANNED
if(QDELETED(src))
return JOB_UNAVAILABLE_GENERIC
if(!job.player_old_enough(client))
return JOB_UNAVAILABLE_ACCOUNTAGE
if(job.required_playtime_remaining(client))
return JOB_UNAVAILABLE_PLAYTIME
if(latejoin && !job.special_check_latejoin(client))
return JOB_UNAVAILABLE_GENERIC
return JOB_AVAILABLE
/mob/dead/new_player/proc/AttemptLateSpawn(rank)
var/error = IsJobUnavailable(rank)
if(error != JOB_AVAILABLE)
alert(src, get_job_unavailable_error_message(error, rank))
return FALSE
if(SSticker.late_join_disabled)
alert(src, "An administrator has disabled late join spawning.")
return FALSE
var/arrivals_docked = TRUE
if(SSshuttle.arrivals)
close_spawn_windows() //In case we get held up
if(SSshuttle.arrivals.damaged && CONFIG_GET(flag/arrivals_shuttle_require_safe_latejoin))
src << alert("The arrivals shuttle is currently malfunctioning! You cannot join.")
return FALSE
if(CONFIG_GET(flag/arrivals_shuttle_require_undocked))
SSshuttle.arrivals.RequireUndocked(src)
arrivals_docked = SSshuttle.arrivals.mode != SHUTTLE_CALL
//Remove the player from the join queue if he was in one and reset the timer
SSticker.queued_players -= src
SSticker.queue_delay = 4
SSjob.AssignRole(src, rank, 1)
var/mob/living/character = create_character(TRUE) //creates the human and transfers vars and mind
var/equip = SSjob.EquipRank(character, rank, TRUE)
if(isliving(equip)) //Borgs get borged in the equip, so we need to make sure we handle the new mob.
character = equip
var/datum/job/job = SSjob.GetJob(rank)
if(job && !job.override_latejoin_spawn(character))
SSjob.SendToLateJoin(character)
if(!arrivals_docked)
var/obj/screen/splash/Spl = new(character.client, TRUE)
Spl.Fade(TRUE)
character.playsound_local(get_turf(character), 'sound/voice/ApproachingTG.ogg', 25)
character.update_parallax_teleport()
SSticker.minds += character.mind
var/mob/living/carbon/human/humanc
if(ishuman(character))
humanc = character //Let's retypecast the var to be human,
if(humanc) //These procs all expect humans
GLOB.data_core.manifest_inject(humanc)
if(SSshuttle.arrivals)
SSshuttle.arrivals.QueueAnnounce(humanc, rank)
else
AnnounceArrival(humanc, rank)
AddEmploymentContract(humanc)
if(GLOB.highlander)
to_chat(humanc, "<span class='userdanger'><i>THERE CAN BE ONLY ONE!!!</i></span>")
humanc.make_scottish()
if(GLOB.summon_guns_triggered)
give_guns(humanc)
if(GLOB.summon_magic_triggered)
give_magic(humanc)
GLOB.joined_player_list += character.ckey
GLOB.latejoiners += character
if(CONFIG_GET(flag/allow_latejoin_antagonists) && humanc) //Borgs aren't allowed to be antags. Will need to be tweaked if we get true latejoin ais.
if(SSshuttle.emergency)
switch(SSshuttle.emergency.mode)
if(SHUTTLE_RECALL, SHUTTLE_IDLE)
SSticker.mode.make_antag_chance(humanc)
if(SHUTTLE_CALL)
if(SSshuttle.emergency.timeLeft(1) > initial(SSshuttle.emergencyCallTime)*0.5)
SSticker.mode.make_antag_chance(humanc)
if(humanc && CONFIG_GET(flag/roundstart_traits))
SSquirks.AssignQuirks(humanc, humanc.client, TRUE, FALSE, job, FALSE)
log_manifest(character.mind.key,character.mind,character,latejoin = TRUE)
/mob/dead/new_player/proc/AddEmploymentContract(mob/living/carbon/human/employee)
//TODO: figure out a way to exclude wizards/nukeops/demons from this.
for(var/C in GLOB.employmentCabinets)
var/obj/structure/filingcabinet/employment/employmentCabinet = C
if(!employmentCabinet.virgin)
employmentCabinet.addFile(employee)
/mob/dead/new_player/proc/LateChoices()
var/level = "green"
switch(GLOB.security_level)
if(SEC_LEVEL_BLUE)
level = "blue"
if(SEC_LEVEL_AMBER)
level = "amber"
if(SEC_LEVEL_RED)
level = "red"
if(SEC_LEVEL_DELTA)
level = "delta"
var/dat = "<div class='notice'>Round Duration: [DisplayTimeText(world.time - SSticker.round_start_time)]<br>Alert Level: [capitalize(level)]</div>"
if(SSshuttle.emergency)
switch(SSshuttle.emergency.mode)
if(SHUTTLE_ESCAPE)
dat += "<div class='notice red'>The station has been evacuated.</div><br>"
if(SHUTTLE_CALL)
if(!SSshuttle.canRecall())
dat += "<div class='notice red'>The station is currently undergoing evacuation procedures.</div><br>"
var/available_job_count = 0
for(var/datum/job/job in SSjob.occupations)
if(job && IsJobUnavailable(job.title, TRUE) == JOB_AVAILABLE)
available_job_count++
for(var/spawner in GLOB.mob_spawners)
if(!LAZYLEN(spawner))
continue
var/obj/effect/mob_spawn/S = pick(GLOB.mob_spawners[spawner])
if(!istype(S) || !S.can_latejoin())
continue
available_job_count++
break
if(!available_job_count)
dat += "<div class='notice red'>There are currently no open positions!</div>"
else
dat += "<div class='clearBoth'>Choose from the following open positions:</div><br>"
var/list/categorizedJobs = list(
"Command" = list(jobs = list(), titles = GLOB.command_positions, color = "#aac1ee"),
"Engineering" = list(jobs = list(), titles = GLOB.engineering_positions, color = "#ffd699"),
"Supply" = list(jobs = list(), titles = GLOB.supply_positions, color = "#ead4ae"),
"Miscellaneous" = list(jobs = list(), titles = list(), color = "#ffffff", colBreak = TRUE),
"Ghost Role" = list(jobs = list(), titles = GLOB.mob_spawners, color = "#ffffff"),
"Synthetic" = list(jobs = list(), titles = GLOB.nonhuman_positions, color = "#ccffcc"),
"Service" = list(jobs = list(), titles = GLOB.civilian_positions, color = "#cccccc"),
"Medical" = list(jobs = list(), titles = GLOB.medical_positions, color = "#99ffe6", colBreak = TRUE),
"Science" = list(jobs = list(), titles = GLOB.science_positions, color = "#e6b3e6"),
"Security" = list(jobs = list(), titles = GLOB.security_positions, color = "#ff9999"),
)
for(var/spawner in GLOB.mob_spawners)
if(!LAZYLEN(spawner))
continue
var/obj/effect/mob_spawn/S = pick(GLOB.mob_spawners[spawner])
if(!istype(S) || !S.can_latejoin())
continue
categorizedJobs["Ghost Role"]["jobs"] += spawner
for(var/datum/job/job in SSjob.occupations)
if(job && IsJobUnavailable(job.title, TRUE) == JOB_AVAILABLE)
var/categorized = FALSE
for(var/jobcat in categorizedJobs)
var/list/jobs = categorizedJobs[jobcat]["jobs"]
if(job.title in categorizedJobs[jobcat]["titles"])
categorized = TRUE
if(jobcat == "Command")
if(job.title == "Captain") // Put captain at top of command jobs
jobs.Insert(1, job)
else
jobs += job
else // Put heads at top of non-command jobs
if(job.title in GLOB.command_positions)
jobs.Insert(1, job)
else
jobs += job
if(!categorized)
categorizedJobs["Miscellaneous"]["jobs"] += job
dat += "<table><tr><td valign='top'>"
for(var/jobcat in categorizedJobs)
if(categorizedJobs[jobcat]["colBreak"])
dat += "</td><td valign='top'>"
if(!length(categorizedJobs[jobcat]["jobs"]))
continue
var/color = categorizedJobs[jobcat]["color"]
dat += "<fieldset style='border: 2px solid [color]; display: inline'>"
dat += "<legend align='center' style='color: [color]'>[jobcat]</legend>"
for(var/datum/job/job in categorizedJobs[jobcat]["jobs"])
var/position_class = "otherPosition"
if(job.title in GLOB.command_positions)
position_class = "commandPosition"
if(job in SSjob.prioritized_jobs)
dat += "<a class='[position_class]' style='display:block;width:170px' href='byond://?src=[REF(src)];SelectedJob=[job.title]'><font color='lime'><b>[job.title] ([job.current_positions])</b></font></a>"
else
dat += "<a class='[position_class]' style='display:block;width:170px' href='byond://?src=[REF(src)];SelectedJob=[job.title]'>[job.title] ([job.current_positions])</a>"
categorizedJobs[jobcat]["jobs"] -= job
for(var/spawner in categorizedJobs[jobcat]["jobs"])
dat += "<a class='otherPosition' style='display:block;width:170px' href='byond://?src=[REF(src)];JoinAsGhostRole=[spawner]'>[spawner]</a>"
dat += "</fieldset><br>"
dat += "</td></tr></table></center>"
dat += "</div></div>"
// Removing the old window method but leaving it here for reference
//src << browse(dat, "window=latechoices;size=300x640;can_close=1")
// Added the new browser window method
var/datum/browser/popup = new(src, "latechoices", "Choose Profession", 680, 580)
popup.add_stylesheet("playeroptions", 'html/browser/playeroptions.css')
popup.set_content(dat)
popup.open(FALSE) // FALSE is passed to open so that it doesn't use the onclose() proc
/mob/dead/new_player/proc/create_character(transfer_after)
spawning = 1
close_spawn_windows()
var/mob/living/carbon/human/H = new(loc)
var/frn = CONFIG_GET(flag/force_random_names)
if(!frn)
frn = jobban_isbanned(src, "appearance")
if(QDELETED(src))
return
if(frn)
client.prefs.random_character()
client.prefs.real_name = client.prefs.pref_species.random_name(gender,1)
client.prefs.copy_to(H)
H.dna.update_dna_identity()
if(mind)
if(transfer_after)
mind.late_joiner = TRUE
mind.active = 0 //we wish to transfer the key manually
mind.transfer_to(H) //won't transfer key since the mind is not active
H.name = real_name
//h13 assign your characters custom height.
if (H.custom_body_size > 0)
H.resize = (H.custom_body_size * 0.01)
//Move large characters up slightly. small character dont really need this.
H.pixel_y = min(((H.custom_body_size-100) * 0.25), 0)
. = H
new_character = .
if(transfer_after)
transfer_character()
/mob/dead/new_player/proc/transfer_character()
. = new_character
if(.)
new_character.key = key //Manually transfer the key to log them in
new_character.stop_sound_channel(CHANNEL_LOBBYMUSIC)
new_character = null
qdel(src)
/mob/dead/new_player/proc/ViewManifest()
var/dat = "<html><body>"
dat += "<h4>Crew Manifest</h4>"
dat += GLOB.data_core.get_manifest(OOC = 1)
src << browse(dat, "window=manifest;size=387x420;can_close=1")
/mob/dead/new_player/Move()
return 0
/mob/dead/new_player/proc/close_spawn_windows()
src << browse(null, "window=latechoices") //closes late choices window
src << browse(null, "window=playersetup") //closes the player setup window
src << browse(null, "window=preferences") //closes job selection
src << browse(null, "window=mob_occupation")
src << browse(null, "window=latechoices") //closes late job selection
+619
View File
@@ -0,0 +1,619 @@
/datum/polloption
var/optionid
var/optiontext
/mob/dead/new_player/proc/handle_player_polling()
if(!SSdbcore.IsConnected())
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
return
var/datum/DBQuery/query_poll_get = SSdbcore.NewQuery("SELECT id, question FROM [format_table_name("poll_question")] WHERE Now() BETWEEN starttime AND endtime [(client.holder ? "" : "AND adminonly = false")]")
if(!query_poll_get.warn_execute())
qdel(query_poll_get)
return
var/output = "<div align='center'><B>Player polls</B><hr><table>"
var/i = 0
var/rs = REF(src)
while(query_poll_get.NextRow())
var/pollid = query_poll_get.item[1]
var/pollquestion = query_poll_get.item[2]
output += "<tr bgcolor='#[ (i % 2 == 1) ? "e2e2e2" : "e2e2e2" ]'><td><a href=\"byond://?src=[rs];pollid=[pollid]\"><b>[pollquestion]</b></a></td></tr>"
i++
qdel(query_poll_get)
output += "</table>"
if(!QDELETED(src))
src << browse(output,"window=playerpolllist;size=500x300")
/mob/dead/new_player/proc/poll_player(pollid)
if(!pollid)
return
if (!SSdbcore.Connect())
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
return
var/datum/DBQuery/query_poll_get_details = SSdbcore.NewQuery("SELECT starttime, endtime, question, polltype, multiplechoiceoptions FROM [format_table_name("poll_question")] WHERE id = [pollid]")
if(!query_poll_get_details.warn_execute())
qdel(query_poll_get_details)
return
var/pollstarttime = ""
var/pollendtime = ""
var/pollquestion = ""
var/polltype = ""
var/multiplechoiceoptions = 0
if(query_poll_get_details.NextRow())
pollstarttime = query_poll_get_details.item[1]
pollendtime = query_poll_get_details.item[2]
pollquestion = query_poll_get_details.item[3]
polltype = query_poll_get_details.item[4]
multiplechoiceoptions = text2num(query_poll_get_details.item[5])
qdel(query_poll_get_details)
switch(polltype)
if(POLLTYPE_OPTION)
var/datum/DBQuery/query_option_get_votes = SSdbcore.NewQuery("SELECT optionid FROM [format_table_name("poll_vote")] WHERE pollid = [pollid] AND ckey = '[ckey]'")
if(!query_option_get_votes.warn_execute())
qdel(query_option_get_votes)
return
var/votedoptionid = 0
if(query_option_get_votes.NextRow())
votedoptionid = text2num(query_option_get_votes.item[1])
qdel(query_option_get_votes)
var/list/datum/polloption/options = list()
var/datum/DBQuery/query_option_options = SSdbcore.NewQuery("SELECT id, text FROM [format_table_name("poll_option")] WHERE pollid = [pollid]")
if(!query_option_options.warn_execute())
qdel(query_option_options)
return
while(query_option_options.NextRow())
var/datum/polloption/PO = new()
PO.optionid = text2num(query_option_options.item[1])
PO.optiontext = query_option_options.item[2]
options += PO
qdel(query_option_options)
var/output = "<div align='center'><B>Player poll</B><hr>"
output += "<b>Question: [pollquestion]</b><br>"
output += "<font size='2'>Poll runs from <b>[pollstarttime]</b> until <b>[pollendtime]</b></font><p>"
if(!votedoptionid)
output += "<form name='cardcomp' action='?src=[REF(src)]' method='get'>"
output += "<input type='hidden' name='src' value='[REF(src)]'>"
output += "<input type='hidden' name='votepollid' value='[pollid]'>"
output += "<input type='hidden' name='votetype' value=[POLLTYPE_OPTION]>"
output += "<table><tr><td>"
for(var/datum/polloption/O in options)
if(O.optionid && O.optiontext)
if(votedoptionid)
if(votedoptionid == O.optionid)
output += "<b>[O.optiontext]</b><br>"
else
output += "[O.optiontext]<br>"
else
output += "<input type='radio' name='voteoptionid' value='[O.optionid]'>[O.optiontext]<br>"
output += "</td></tr></table>"
if(!votedoptionid)
output += "<p><input type='submit' value='Vote'>"
output += "</form>"
output += "</div>"
src << browse(null ,"window=playerpolllist")
src << browse(output,"window=playerpoll;size=500x250")
if(POLLTYPE_TEXT)
var/datum/DBQuery/query_text_get_votes = SSdbcore.NewQuery("SELECT replytext FROM [format_table_name("poll_textreply")] WHERE pollid = [pollid] AND ckey = '[ckey]'")
if(!query_text_get_votes.warn_execute())
qdel(query_text_get_votes)
return
var/vote_text = ""
if(query_text_get_votes.NextRow())
vote_text = query_text_get_votes.item[1]
qdel(query_text_get_votes)
var/output = "<div align='center'><B>Player poll</B><hr>"
output += "<b>Question: [pollquestion]</b><br>"
output += "<font size='2'>Feedback gathering runs from <b>[pollstarttime]</b> until <b>[pollendtime]</b></font><p>"
output += "<form name='cardcomp' action='?src=[REF(src)]' method='get'>"
output += "<input type='hidden' name='src' value='[REF(src)]'>"
output += "<input type='hidden' name='votepollid' value='[pollid]'>"
output += "<input type='hidden' name='votetype' value=[POLLTYPE_TEXT]>"
output += "<font size='2'>Please provide feedback below. You can use any letters of the English alphabet, numbers and the symbols: . , ! ? : ; -</font><br>"
output += "<textarea name='replytext' cols='50' rows='14'>[vote_text]</textarea>"
output += "<p><input type='submit' value='Submit'></form>"
output += "<form name='cardcomp' action='?src=[REF(src)]' method='get'>"
output += "<input type='hidden' name='src' value='[REF(src)]'>"
output += "<input type='hidden' name='votepollid' value='[pollid]'>"
output += "<input type='hidden' name='votetype' value=[POLLTYPE_TEXT]>"
output += "<input type='hidden' name='replytext' value='ABSTAIN'>"
output += "<input type='submit' value='Abstain'></form>"
src << browse(null ,"window=playerpolllist")
src << browse(output,"window=playerpoll;size=500x500")
if(POLLTYPE_RATING)
var/datum/DBQuery/query_rating_get_votes = SSdbcore.NewQuery("SELECT o.text, v.rating FROM [format_table_name("poll_option")] o, [format_table_name("poll_vote")] v WHERE o.pollid = [pollid] AND v.ckey = '[ckey]' AND o.id = v.optionid")
if(!query_rating_get_votes.warn_execute())
qdel(query_rating_get_votes)
return
var/output = "<div align='center'><B>Player poll</B><hr>"
output += "<b>Question: [pollquestion]</b><br>"
output += "<font size='2'>Poll runs from <b>[pollstarttime]</b> until <b>[pollendtime]</b></font><p>"
var/rating
while(query_rating_get_votes.NextRow())
var/optiontext = query_rating_get_votes.item[1]
rating = query_rating_get_votes.item[2]
output += "<br><b>[optiontext] - [rating]</b>"
qdel(query_rating_get_votes)
if(!rating)
output += "<form name='cardcomp' action='?src=[REF(src)]' method='get'>"
output += "<input type='hidden' name='src' value='[REF(src)]'>"
output += "<input type='hidden' name='votepollid' value='[pollid]'>"
output += "<input type='hidden' name='votetype' value=[POLLTYPE_RATING]>"
var/minid = 999999
var/maxid = 0
var/datum/DBQuery/query_rating_options = SSdbcore.NewQuery("SELECT id, text, minval, maxval, descmin, descmid, descmax FROM [format_table_name("poll_option")] WHERE pollid = [pollid]")
if(!query_rating_options.warn_execute())
qdel(query_rating_options)
return
while(query_rating_options.NextRow())
var/optionid = text2num(query_rating_options.item[1])
var/optiontext = query_rating_options.item[2]
var/minvalue = text2num(query_rating_options.item[3])
var/maxvalue = text2num(query_rating_options.item[4])
var/descmin = query_rating_options.item[5]
var/descmid = query_rating_options.item[6]
var/descmax = query_rating_options.item[7]
if(optionid < minid)
minid = optionid
if(optionid > maxid)
maxid = optionid
var/midvalue = round( (maxvalue + minvalue) / 2)
output += "<br>[optiontext]: <select name='o[optionid]'>"
output += "<option value='abstain'>abstain</option>"
for (var/j = minvalue; j <= maxvalue; j++)
if(j == minvalue && descmin)
output += "<option value='[j]'>[j] ([descmin])</option>"
else if (j == midvalue && descmid)
output += "<option value='[j]'>[j] ([descmid])</option>"
else if (j == maxvalue && descmax)
output += "<option value='[j]'>[j] ([descmax])</option>"
else
output += "<option value='[j]'>[j]</option>"
output += "</select>"
qdel(query_rating_options)
output += "<input type='hidden' name='minid' value='[minid]'>"
output += "<input type='hidden' name='maxid' value='[maxid]'>"
output += "<p><input type='submit' value='Submit'></form>"
if(!QDELETED(src))
src << browse(null ,"window=playerpolllist")
src << browse(output,"window=playerpoll;size=500x500")
if(POLLTYPE_MULTI)
var/datum/DBQuery/query_multi_get_votes = SSdbcore.NewQuery("SELECT optionid FROM [format_table_name("poll_vote")] WHERE pollid = [pollid] AND ckey = '[ckey]'")
if(!query_multi_get_votes.warn_execute())
qdel(query_multi_get_votes)
return
var/list/votedfor = list()
while(query_multi_get_votes.NextRow())
votedfor.Add(text2num(query_multi_get_votes.item[1]))
qdel(query_multi_get_votes)
var/list/datum/polloption/options = list()
var/maxoptionid = 0
var/minoptionid = 0
var/datum/DBQuery/query_multi_options = SSdbcore.NewQuery("SELECT id, text FROM [format_table_name("poll_option")] WHERE pollid = [pollid]")
if(!query_multi_options.warn_execute())
qdel(query_multi_options)
return
while(query_multi_options.NextRow())
var/datum/polloption/PO = new()
PO.optionid = text2num(query_multi_options.item[1])
PO.optiontext = query_multi_options.item[2]
if(PO.optionid > maxoptionid)
maxoptionid = PO.optionid
if(PO.optionid < minoptionid || !minoptionid)
minoptionid = PO.optionid
options += PO
qdel(query_multi_options)
var/output = "<div align='center'><B>Player poll</B><hr>"
output += "<b>Question: [pollquestion]</b><br>You can select up to [multiplechoiceoptions] options. If you select more, the first [multiplechoiceoptions] will be saved.<br>"
output += "<font size='2'>Poll runs from <b>[pollstarttime]</b> until <b>[pollendtime]</b></font><p>"
if(!votedfor.len)
output += "<form name='cardcomp' action='?src=[REF(src)]' method='get'>"
output += "<input type='hidden' name='src' value='[REF(src)]'>"
output += "<input type='hidden' name='votepollid' value='[pollid]'>"
output += "<input type='hidden' name='votetype' value=[POLLTYPE_MULTI]>"
output += "<input type='hidden' name='maxoptionid' value='[maxoptionid]'>"
output += "<input type='hidden' name='minoptionid' value='[minoptionid]'>"
output += "<table><tr><td>"
for(var/datum/polloption/O in options)
if(O.optionid && O.optiontext)
if(votedfor.len)
if(O.optionid in votedfor)
output += "<b>[O.optiontext]</b><br>"
else
output += "[O.optiontext]<br>"
else
output += "<input type='checkbox' name='option_[O.optionid]' value='[O.optionid]'>[O.optiontext]<br>"
output += "</td></tr></table>"
if(!votedfor.len)
output += "<p><input type='submit' value='Vote'></form>"
output += "</div>"
src << browse(null ,"window=playerpolllist")
src << browse(output,"window=playerpoll;size=500x250")
if(POLLTYPE_IRV)
var/datum/asset/irv_assets = get_asset_datum(/datum/asset/group/IRV)
irv_assets.send(src)
var/datum/DBQuery/query_irv_get_votes = SSdbcore.NewQuery("SELECT optionid FROM [format_table_name("poll_vote")] WHERE pollid = [pollid] AND ckey = '[ckey]'")
if(!query_irv_get_votes.warn_execute())
qdel(query_irv_get_votes)
return
var/list/votedfor = list()
while(query_irv_get_votes.NextRow())
votedfor.Add(text2num(query_irv_get_votes.item[1]))
qdel(query_irv_get_votes)
var/list/datum/polloption/options = list()
var/datum/DBQuery/query_irv_options = SSdbcore.NewQuery("SELECT id, text FROM [format_table_name("poll_option")] WHERE pollid = [pollid]")
if(!query_irv_options.warn_execute())
qdel(query_irv_options)
return
while(query_irv_options.NextRow())
var/datum/polloption/PO = new()
PO.optionid = text2num(query_irv_options.item[1])
PO.optiontext = query_irv_options.item[2]
options["[PO.optionid]"] += PO
qdel(query_irv_options)
//if they already voted, use their sort
if (votedfor.len)
var/list/datum/polloption/newoptions = list()
for (var/V in votedfor)
var/datum/polloption/PO = options["[V]"]
if(PO)
newoptions["[V]"] = PO
options -= "[V]"
//add any options that they didn't vote on (some how, some way)
options = shuffle(options)
for (var/V in options)
newoptions["[V]"] = options["[V]"]
options = newoptions
//otherwise, lets shuffle it.
else
var/list/datum/polloption/newoptions = list()
while (options.len)
var/list/local_options = options.Copy()
var/key
//the jist is we randomly remove all options from a copy of options until only one reminds,
// move that over to our new list
// and repeat until we've moved all of them
while (local_options.len)
key = local_options[rand(1, local_options.len)]
local_options -= key
var/value = options[key]
options -= key
newoptions[key] = value
options = newoptions
var/output = {"
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<script src="jquery.min.js"></script>
<script src="jquery-ui.custom-core-widgit-mouse-sortable-min.js"></script>
<style>
#sortable { list-style-type: none; margin: 0; padding: 2em; }
#sortable li { min-height: 1em; margin: 0px 1px 1px 1px; padding: 1px; border: 1px solid black; border-radius: 5px; background-color: white; cursor:move;}
#sortable .sortable-placeholder-highlight { min-height: 1em; margin: 0 2px 2px 2px; padding: 2px; border: 1px dotted blue; border-radius: 5px; background-color: GhostWhite; }
span.grippy { content: '....'; width: 10px; height: 20px; display: inline-block; overflow: hidden; line-height: 5px; padding: 3px 1px; cursor: move; vertical-align: middle; margin-top: -.7em; margin-right: .3em; font-size: 12px; font-family: sans-serif; letter-spacing: 2px; color: #cccccc; text-shadow: 1px 0 1px black; }
span.grippy::after { content: '.. .. .. ..';}
</style>
<script>
$(function() {
$( "#sortable" ).sortable({
placeholder: "sortable-placeholder-highlight",
axis: "y",
containment: "#ballot",
scroll: false,
cursor: "ns-resize",
tolerance: "pointer"
});
$( "#sortable" ).disableSelection();
$('form').submit(function(){
$('#IRVdata').val($( "#sortable" ).sortable("toArray", { attribute: "voteid" }));
});
});
</script>
</head>
<body>
<div align='center'><B>Player poll</B><hr>
<b>Question: [pollquestion]</b><br>Please sort the options in the order of <b>most preferred</b> to <b>least preferred</b><br>
<font size='2'>Revoting has been enabled on this poll, if you think you made a mistake, simply revote<br></font>
<font size='2'>Poll runs from <b>[pollstarttime]</b> until <b>[pollendtime]</b></font><p>
</div>
<form name='cardcomp' action='?src=[REF(src)]' method='POST'>
<input type='hidden' name='src' value='[REF(src)]'>
<input type='hidden' name='votepollid' value='[pollid]'>
<input type='hidden' name='votetype' value=[POLLTYPE_IRV]>
<input type='hidden' name='IRVdata' id='IRVdata'>
<div id="ballot" class="center">
<b><center>Most Preferred</center></b>
<ol id="sortable" class="rankings" style="padding:0px">
"}
for(var/O in options)
var/datum/polloption/PO = options["[O]"]
if(PO.optionid && PO.optiontext)
output += "<li voteid='[PO.optionid]' class='ranking'><span class='grippy'></span> [PO.optiontext]</li>\n"
output += {"
</ol>
<b><center>Least Preferred</center></b><br>
</div>
<p><input type='submit' value='[( votedfor.len ? "Re" : "")]Vote'></form>
"}
src << browse(null ,"window=playerpolllist")
src << browse(output,"window=playerpoll;size=500x500")
return
//Returns null on failure, TRUE if already voted, FALSE if not voted yet.
/mob/dead/new_player/proc/poll_check_voted(pollid, text = FALSE, silent = FALSE)
var/table = "poll_vote"
if (text)
table = "poll_textreply"
if (!SSdbcore.Connect())
to_chat(usr, "<span class='danger'>Failed to establish database connection.</span>")
return
var/datum/DBQuery/query_hasvoted = SSdbcore.NewQuery("SELECT id FROM `[format_table_name(table)]` WHERE pollid = [pollid] AND ckey = '[ckey]'")
if(!query_hasvoted.warn_execute())
qdel(query_hasvoted)
return
if(query_hasvoted.NextRow())
qdel(query_hasvoted)
if(!silent)
to_chat(usr, "<span class='danger'>You've already replied to this poll.</span>")
return TRUE
qdel(query_hasvoted)
return FALSE
//Returns adminrank for use in polls.
/mob/dead/new_player/proc/poll_rank()
. = "Player"
if(client.holder)
. = client.holder.rank.name
/mob/dead/new_player/proc/vote_rig_check()
if (usr != src)
if (!usr || !src)
return 0
//we gots ourselfs a dirty cheater on our hands!
log_game("[key_name(usr)] attempted to rig the vote by voting as [key]")
message_admins("[key_name_admin(usr)] attempted to rig the vote by voting as [key]")
to_chat(usr, "<span class='danger'>You don't seem to be [key].</span>")
to_chat(src, "<span class='danger'>Something went horribly wrong processing your vote. Please contact an administrator, they should have gotten a message about this</span>")
return 0
return 1
/mob/dead/new_player/proc/vote_valid_check(pollid, holder, type)
if (!SSdbcore.Connect())
to_chat(src, "<span class='danger'>Failed to establish database connection.</span>")
return 0
pollid = text2num(pollid)
if (!pollid || pollid < 0)
return 0
//validate the poll is actually the right type of poll and its still active
var/datum/DBQuery/query_validate_poll = SSdbcore.NewQuery("SELECT id FROM [format_table_name("poll_question")] WHERE id = [pollid] AND Now() BETWEEN starttime AND endtime AND polltype = '[type]' [(holder ? "" : "AND adminonly = false")]")
if(!query_validate_poll.warn_execute())
qdel(query_validate_poll)
return 0
if (!query_validate_poll.NextRow())
qdel(query_validate_poll)
return 0
qdel(query_validate_poll)
return 1
/mob/dead/new_player/proc/vote_on_irv_poll(pollid, list/votelist)
if (!SSdbcore.Connect())
to_chat(src, "<span class='danger'>Failed to establish database connection.</span>")
return 0
if (!vote_rig_check())
return 0
pollid = text2num(pollid)
if (!pollid || pollid < 0)
return 0
if (!votelist || !istype(votelist) || !votelist.len)
return 0
if (!client)
return 0
//save these now so we can still process the vote if the client goes away while we process.
var/datum/admins/holder = client.holder
var/rank = "Player"
if (holder)
rank = holder.rank.name
var/ckey = client.ckey
var/address = client.address
//validate the poll
if (!vote_valid_check(pollid, holder, POLLTYPE_IRV))
return 0
//lets collect the options
var/datum/DBQuery/query_irv_id = SSdbcore.NewQuery("SELECT id FROM [format_table_name("poll_option")] WHERE pollid = [pollid]")
if(!query_irv_id.warn_execute())
qdel(query_irv_id)
return 0
var/list/optionlist = list()
while (query_irv_id.NextRow())
optionlist += text2num(query_irv_id.item[1])
qdel(query_irv_id)
//validate their votes are actually in the list of options and actually numbers
var/list/numberedvotelist = list()
for (var/vote in votelist)
vote = text2num(vote)
numberedvotelist += vote
if (!vote) //this is fine because voteid starts at 1, so it will never be 0
to_chat(src, "<span class='danger'>Error: Invalid (non-numeric) votes in the vote data.</span>")
return 0
if (!(vote in optionlist))
to_chat(src, "<span class='danger'>Votes for choices that do not appear to be in the poll detected.</span>")
return 0
if (!numberedvotelist.len)
to_chat(src, "<span class='danger'>Invalid vote data</span>")
return 0
//lets add the vote, first we generate an insert statement.
var/sqlrowlist = ""
for (var/vote in numberedvotelist)
if (sqlrowlist != "")
sqlrowlist += ", " //a comma (,) at the start of the first row to insert will trigger a SQL error
sqlrowlist += "(Now(), [pollid], [vote], '[sanitizeSQL(ckey)]', INET_ATON('[sanitizeSQL(address)]'), '[sanitizeSQL(rank)]')"
//now lets delete their old votes (if any)
var/datum/DBQuery/query_irv_del_old = SSdbcore.NewQuery("DELETE FROM [format_table_name("poll_vote")] WHERE pollid = [pollid] AND ckey = '[ckey]'")
if(!query_irv_del_old.warn_execute())
qdel(query_irv_del_old)
return 0
qdel(query_irv_del_old)
//now to add the new ones.
var/datum/DBQuery/query_irv_vote = SSdbcore.NewQuery("INSERT INTO [format_table_name("poll_vote")] (datetime, pollid, optionid, ckey, ip, adminrank) VALUES [sqlrowlist]")
if(!query_irv_vote.warn_execute())
qdel(query_irv_vote)
return 0
qdel(query_irv_vote)
if(!QDELETED(src))
src << browse(null,"window=playerpoll")
return 1
/mob/dead/new_player/proc/vote_on_poll(pollid, optionid)
if (!SSdbcore.Connect())
to_chat(src, "<span class='danger'>Failed to establish database connection.</span>")
return 0
if (!vote_rig_check())
return 0
if(!pollid || !optionid)
return
//validate the poll
if (!vote_valid_check(pollid, client.holder, POLLTYPE_OPTION))
return 0
var/voted = poll_check_voted(pollid)
if(isnull(voted) || voted) //Failed or already voted.
return
var/adminrank = sanitizeSQL(poll_rank())
if(!adminrank)
return
var/datum/DBQuery/query_option_vote = SSdbcore.NewQuery("INSERT INTO [format_table_name("poll_vote")] (datetime, pollid, optionid, ckey, ip, adminrank) VALUES (Now(), [pollid], [optionid], '[ckey]', INET_ATON('[client.address]'), '[adminrank]')")
if(!query_option_vote.warn_execute())
qdel(query_option_vote)
return
qdel(query_option_vote)
if(!QDELETED(usr))
usr << browse(null,"window=playerpoll")
return 1
/mob/dead/new_player/proc/log_text_poll_reply(pollid, replytext)
if (!SSdbcore.Connect())
to_chat(src, "<span class='danger'>Failed to establish database connection.</span>")
return 0
if (!vote_rig_check())
return 0
if(!pollid)
return
//validate the poll
if (!vote_valid_check(pollid, client.holder, POLLTYPE_TEXT))
return 0
if(!replytext)
to_chat(usr, "The text you entered was blank. Please correct the text and submit again.")
return
var/voted = poll_check_voted(pollid, text = TRUE, silent = TRUE)
if(isnull(voted))
return
var/adminrank = sanitizeSQL(poll_rank())
if(!adminrank)
return
replytext = sanitizeSQL(replytext)
if(!(length(replytext) > 0) || !(length(replytext) <= 8000))
to_chat(usr, "The text you entered was invalid or too long. Please correct the text and submit again.")
return
var/datum/DBQuery/query_text_vote
if(!voted)
query_text_vote = SSdbcore.NewQuery("INSERT INTO [format_table_name("poll_textreply")] (datetime ,pollid ,ckey ,ip ,replytext ,adminrank) VALUES (Now(), [pollid], '[ckey]', INET_ATON('[client.address]'), '[replytext]', '[adminrank]')")
else
query_text_vote = SSdbcore.NewQuery("UPDATE [format_table_name("poll_textreply")] SET datetime = Now(), ip = INET_ATON('[client.address]'), replytext = '[replytext]' WHERE pollid = '[pollid]' AND ckey = '[ckey]'")
if(!query_text_vote.warn_execute())
qdel(query_text_vote)
return
qdel(query_text_vote)
if(!QDELETED(usr))
usr << browse(null,"window=playerpoll")
return 1
/mob/dead/new_player/proc/vote_on_numval_poll(pollid, optionid, rating)
if (!SSdbcore.Connect())
to_chat(src, "<span class='danger'>Failed to establish database connection.</span>")
return 0
if (!vote_rig_check())
return 0
if(!pollid || !optionid || !rating)
return
//validate the poll
if (!vote_valid_check(pollid, client.holder, POLLTYPE_RATING))
return 0
var/datum/DBQuery/query_numval_hasvoted = SSdbcore.NewQuery("SELECT id FROM [format_table_name("poll_vote")] WHERE optionid = [optionid] AND ckey = '[ckey]'")
if(!query_numval_hasvoted.warn_execute())
qdel(query_numval_hasvoted)
return
if(query_numval_hasvoted.NextRow())
qdel(query_numval_hasvoted)
to_chat(usr, "<span class='danger'>You've already replied to this poll.</span>")
return
qdel(query_numval_hasvoted)
var/adminrank = "Player"
if(client.holder)
adminrank = client.holder.rank.name
adminrank = sanitizeSQL(adminrank)
var/datum/DBQuery/query_numval_vote = SSdbcore.NewQuery("INSERT INTO [format_table_name("poll_vote")] (datetime ,pollid ,optionid ,ckey ,ip ,adminrank, rating) VALUES (Now(), [pollid], [optionid], '[ckey]', INET_ATON('[client.address]'), '[adminrank]', [(isnull(rating)) ? "null" : rating])")
if(!query_numval_vote.warn_execute())
qdel(query_numval_vote)
return
qdel(query_numval_vote)
if(!QDELETED(usr))
usr << browse(null,"window=playerpoll")
return 1
/mob/dead/new_player/proc/vote_on_multi_poll(pollid, optionid)
if (!SSdbcore.Connect())
to_chat(src, "<span class='danger'>Failed to establish database connection.</span>")
return 0
if (!vote_rig_check())
return 0
if(!pollid || !optionid)
return 1
//validate the poll
if (!vote_valid_check(pollid, client.holder, POLLTYPE_MULTI))
return 0
var/datum/DBQuery/query_multi_choicelen = SSdbcore.NewQuery("SELECT multiplechoiceoptions FROM [format_table_name("poll_question")] WHERE id = [pollid]")
if(!query_multi_choicelen.warn_execute())
qdel(query_multi_choicelen)
return 1
var/i
if(query_multi_choicelen.NextRow())
i = text2num(query_multi_choicelen.item[1])
qdel(query_multi_choicelen)
var/datum/DBQuery/query_multi_hasvoted = SSdbcore.NewQuery("SELECT id FROM [format_table_name("poll_vote")] WHERE pollid = [pollid] AND ckey = '[ckey]'")
if(!query_multi_hasvoted.warn_execute())
qdel(query_multi_hasvoted)
return 1
while(i)
if(query_multi_hasvoted.NextRow())
i--
else
break
qdel(query_multi_hasvoted)
if(!i)
return 2
var/adminrank = "Player"
if(!QDELETED(client) && client.holder)
adminrank = client.holder.rank.name
adminrank = sanitizeSQL(adminrank)
var/datum/DBQuery/query_multi_vote = SSdbcore.NewQuery("INSERT INTO [format_table_name("poll_vote")] (datetime, pollid, optionid, ckey, ip, adminrank) VALUES (Now(), [pollid], [optionid], '[ckey]', INET_ATON('[client.address]'), '[adminrank]')")
if(!query_multi_vote.warn_execute())
qdel(query_multi_vote)
return 1
qdel(query_multi_vote)
if(!QDELETED(usr))
usr << browse(null,"window=playerpoll")
return 0
@@ -0,0 +1,81 @@
//The mob should have a gender you want before running this proc. Will run fine without H
/datum/preferences/proc/random_character(gender_override)
if(gender_override)
gender = gender_override
else
gender = pick(MALE,FEMALE)
underwear = random_underwear(gender)
undie_color = random_short_color()
undershirt = random_undershirt(gender)
shirt_color = random_short_color()
socks = random_socks()
socks_color = random_short_color()
skin_tone = random_skin_tone()
hair_style = random_hair_style(gender)
facial_hair_style = random_facial_hair_style(gender)
hair_color = random_short_color()
facial_hair_color = hair_color
eye_color = random_eye_color()
if(!pref_species)
var/rando_race = pick(GLOB.roundstart_races)
pref_species = new rando_race()
features = random_features()
age = rand(AGE_MIN,AGE_MAX)
/datum/preferences/proc/update_preview_icon()
// Silicons only need a very basic preview since there is no customization for them.
// var/wide_icon = FALSE //CITDEL THINGS
// if(features["taur"] != "None")
// wide_icon = TRUE
if(job_engsec_high)
switch(job_engsec_high)
if(AI_JF)
parent.show_character_previews(image('icons/mob/ai.dmi', resolve_ai_icon(preferred_ai_core_display), dir = SOUTH))
return
if(CYBORG)
parent.show_character_previews(image('icons/mob/robots.dmi', icon_state = "robot", dir = SOUTH))
return
// Set up the dummy for its photoshoot
var/mob/living/carbon/human/dummy/mannequin = generate_or_wait_for_human_dummy(DUMMY_HUMAN_SLOT_PREFERENCES)
mannequin.cut_overlays()
mannequin.add_overlay(mutable_appearance('modular_citadel/icons/ui/backgrounds.dmi', bgstate, layer = SPACE_LAYER))
copy_to(mannequin)
// mannequin.resize = (1+(body_size_alt*-1))
// mannequin.resize = (body_size * 0.01)
// body_size_alt = ((body_size * 0.01)-1)
// Determine what job is marked as 'High' priority, and dress them up as such.
var/datum/job/previewJob
var/highRankFlag = job_civilian_high | job_medsci_high | job_engsec_high
if(job_civilian_low & ASSISTANT)
previewJob = SSjob.GetJob("Assistant")
else if(highRankFlag)
var/highDeptFlag
if(job_civilian_high)
highDeptFlag = CIVILIAN
else if(job_medsci_high)
highDeptFlag = MEDSCI
else if(job_engsec_high)
highDeptFlag = ENGSEC
for(var/datum/job/job in SSjob.occupations)
if(job.flag == highRankFlag && job.department_flag == highDeptFlag)
previewJob = job
break
if(previewJob)
if(current_tab != 2)
mannequin.job = previewJob.title
previewJob.equip(mannequin, TRUE)
COMPILE_OVERLAYS(mannequin)
parent.show_character_previews(new /mutable_appearance(mannequin))
unset_busy_human_dummy(DUMMY_HUMAN_SLOT_PREFERENCES)
@@ -0,0 +1,66 @@
/*
Hello and welcome to sprite_accessories: For sprite accessories, such as hair,
facial hair, and possibly tattoos and stuff somewhere along the line. This file is
intended to be friendly for people with little to no actual coding experience.
The process of adding in new hairstyles has been made pain-free and easy to do.
Enjoy! - Doohl
Notice: This all gets automatically compiled in a list in dna.dm, so you do not
have to define any UI values for sprite accessories manually for hair and facial
hair. Just add in new hair types and the game will naturally adapt.
!!WARNING!!: changing existing hair information can be VERY hazardous to savefiles,
to the point where you may completely corrupt a server's savefiles. Please refrain
from doing this unless you absolutely know what you are doing, and have defined a
conversion in savefile.dm
*/
/proc/init_sprite_accessory_subtypes(prototype, list/L, list/male, list/female,var/roundstart = FALSE)//Roundstart argument builds a specific list for roundstart parts where some parts may be locked
if(!istype(L))
L = list()
if(!istype(male))
male = list()
if(!istype(female))
female = list()
for(var/path in typesof(prototype))
if(path == prototype)
continue
if(roundstart)
var/datum/sprite_accessory/P = path
if(initial(P.locked))
continue
var/datum/sprite_accessory/D = new path()
if(D.icon_state)
L[D.name] = D
else
L += D.name
switch(D.gender)
if(MALE)
male += D.name
if(FEMALE)
female += D.name
else
male += D.name
female += D.name
return L
/datum/sprite_accessory
var/icon //the icon file the accessory is located in
var/icon_state //the icon_state of the accessory
var/name //the preview name of the accessory
var/gender = NEUTER //Determines if the accessory will be skipped or included in random hair generations
var/gender_specific //Something that can be worn by either gender, but looks different on each
var/color_src = MUTCOLORS //Currently only used by mutantparts so don't worry about hair and stuff. This is the source that this accessory will get its color from. Default is MUTCOLOR, but can also be HAIR, FACEHAIR, EYECOLOR and 0 if none.
var/hasinner //Decides if this sprite has an "inner" part, such as the fleshy parts on ears.
var/locked = FALSE //Is this part locked from roundstart selection? Used for parts that apply effects
var/dimension_x = 32
var/dimension_y = 32
var/center = FALSE //Should we center the sprite?
/datum/sprite_accessory/underwear
icon = 'icons/mob/underwear.dmi'
var/has_color = FALSE
@@ -0,0 +1,25 @@
//////////.//////////////////
// MutantParts Definitions //
/////////////////////////////
/datum/sprite_accessory/body_markings
icon = 'icons/mob/mutant_bodyparts.dmi'
/datum/sprite_accessory/body_markings/none
name = "None"
icon_state = "none"
/datum/sprite_accessory/body_markings/dtiger
name = "Dark Tiger Body"
icon_state = "dtiger"
gender_specific = 1
/datum/sprite_accessory/body_markings/ltiger
name = "Light Tiger Body"
icon_state = "ltiger"
gender_specific = 1
/datum/sprite_accessory/body_markings/lbelly
name = "Light Belly"
icon_state = "lbelly"
gender_specific = 1
@@ -0,0 +1,7 @@
datum/sprite_accessory/caps
icon = 'icons/mob/mutant_bodyparts.dmi'
color_src = HAIR
/datum/sprite_accessory/caps/round
name = "Round"
icon_state = "round"
@@ -0,0 +1,12 @@
/datum/sprite_accessory/ears
icon = 'icons/mob/mutant_bodyparts.dmi'
/datum/sprite_accessory/ears/none
name = "None"
icon_state = "none"
/datum/sprite_accessory/ears/cat
name = "Cat"
icon_state = "cat"
hasinner = 1
color_src = HAIR
@@ -0,0 +1,18 @@
/datum/sprite_accessory/frills
icon = 'icons/mob/mutant_bodyparts.dmi'
/datum/sprite_accessory/frills/none
name = "None"
icon_state = "none"
/datum/sprite_accessory/frills/simple
name = "Simple"
icon_state = "simple"
/datum/sprite_accessory/frills/short
name = "Short"
icon_state = "short"
/datum/sprite_accessory/frills/aquatic
name = "Aquatic"
icon_state = "aqua"
@@ -0,0 +1,89 @@
/////////////////////////////
// Facial Hair Definitions //
/////////////////////////////
/datum/sprite_accessory/facial_hair
icon = 'icons/mob/human_face.dmi'
gender = MALE // barf (unless you're a dorf, dorfs dig chix w/ beards :P)
// please make sure they're sorted alphabetically and categorized
/datum/sprite_accessory/facial_hair/abe
name = "Beard (Abraham Lincoln)"
icon_state = "facial_abe"
/datum/sprite_accessory/facial_hair/brokenman
name = "Beard (Broken Man)"
icon_state = "facial_brokenman"
/datum/sprite_accessory/facial_hair/chinstrap
name = "Beard (Chinstrap)"
icon_state = "facial_chin"
/datum/sprite_accessory/facial_hair/dwarf
name = "Beard (Dwarf)"
icon_state = "facial_dwarf"
/datum/sprite_accessory/facial_hair/fiveoclock
name = "Beard (Five o Clock Shadow)"
icon_state = "facial_fiveoclock"
/datum/sprite_accessory/facial_hair/fullbeard
name = "Beard (Full)"
icon_state = "facial_fullbeard"
/datum/sprite_accessory/facial_hair/gt
name = "Beard (Goatee)"
icon_state = "facial_gt"
/datum/sprite_accessory/facial_hair/hip
name = "Beard (Hipster)"
icon_state = "facial_hip"
/datum/sprite_accessory/facial_hair/jensen
name = "Beard (Jensen)"
icon_state = "facial_jensen"
/datum/sprite_accessory/facial_hair/neckbeard
name = "Beard (Neckbeard)"
icon_state = "facial_neckbeard"
/datum/sprite_accessory/facial_hair/vlongbeard
name = "Beard (Very Long)"
icon_state = "facial_wise"
/datum/sprite_accessory/facial_hair/longbeard
name = "Beard (Long)"
icon_state = "facial_longbeard"
/datum/sprite_accessory/facial_hair/fu
name = "Moustache (Fu Manchu)"
icon_state = "facial_fumanchu"
/datum/sprite_accessory/facial_hair/hogan
name = "Moustache (Hulk Hogan)"
icon_state = "facial_hogan" //-Neek
/datum/sprite_accessory/facial_hair/selleck
name = "Moustache (Selleck)"
icon_state = "facial_selleck"
/datum/sprite_accessory/facial_hair/chaplin
name = "Moustache (Square)"
icon_state = "facial_chaplin"
/datum/sprite_accessory/facial_hair/vandyke
name = "Moustache (Van Dyke)"
icon_state = "facial_vandyke"
/datum/sprite_accessory/facial_hair/watson
name = "Moustache (Watson)"
icon_state = "facial_watson"
/datum/sprite_accessory/facial_hair/shaved
name = "Shaved"
icon_state = null
gender = NEUTER
/datum/sprite_accessory/facial_hair/elvis
name = "Sideburns (Elvis)"
icon_state = "facial_elvis"
@@ -0,0 +1,464 @@
//////////////////////
// Hair Definitions //
//////////////////////
/datum/sprite_accessory/hair
icon = 'icons/mob/human_face.dmi' // default icon for all hairs
// please make sure they're sorted alphabetically and, where needed, categorized
// try to capitalize the names please~
// try to spell
// you do not need to define _s or _l sub-states, game automatically does this for you
/datum/sprite_accessory/hair/afro
name = "Afro"
icon_state = "hair_afro"
/datum/sprite_accessory/hair/afro2
name = "Afro 2"
icon_state = "hair_afro2"
/datum/sprite_accessory/hair/afro_large
name = "Afro (Large)"
icon_state = "hair_bigafro"
/datum/sprite_accessory/hair/antenna
name = "Ahoge"
icon_state = "hair_antenna"
/datum/sprite_accessory/hair/bald
name = "Bald"
icon_state = null
/datum/sprite_accessory/hair/balding
name = "Balding Hair"
icon_state = "hair_e"
/datum/sprite_accessory/hair/bedhead
name = "Bedhead"
icon_state = "hair_bedhead"
/datum/sprite_accessory/hair/bedhead2
name = "Bedhead 2"
icon_state = "hair_bedheadv2"
/datum/sprite_accessory/hair/bedhead3
name = "Bedhead 3"
icon_state = "hair_bedheadv3"
/datum/sprite_accessory/hair/beehive
name = "Beehive"
icon_state = "hair_beehive"
/datum/sprite_accessory/hair/beehive2
name = "Beehive 2"
icon_state = "hair_beehivev2"
/datum/sprite_accessory/hair/bob
name = "Bob Hair"
icon_state = "hair_bob"
/datum/sprite_accessory/hair/bob2
name = "Bob Hair 2"
icon_state = "hair_bob2"
/datum/sprite_accessory/hair/bob3
name = "Bob Hair 3"
icon_state = "hair_bobcut"
/datum/sprite_accessory/hair/bobcurl
name = "Bobcurl"
icon_state = "hair_bobcurl"
/datum/sprite_accessory/hair/boddicker
name = "Boddicker"
icon_state = "hair_boddicker"
/datum/sprite_accessory/hair/bowl
name = "Bowl"
icon_state = "hair_bowlcut"
/datum/sprite_accessory/hair/braid
name = "Braid (Floorlength)"
icon_state = "hair_braid"
/datum/sprite_accessory/hair/braided
name = "Braided"
icon_state = "hair_braided"
/datum/sprite_accessory/hair/front_braid
name = "Braided Front"
icon_state = "hair_braidfront"
/datum/sprite_accessory/hair/not_floorlength_braid
name = "Braid (High)"
icon_state = "hair_braid2"
/datum/sprite_accessory/hair/lowbraid
name = "Braid (Low)"
icon_state = "hair_hbraid"
/datum/sprite_accessory/hair/shortbraid
name = "Braid (Short)"
icon_state = "hair_shortbraid"
/datum/sprite_accessory/hair/braidtail
name = "Braided Tail"
icon_state = "hair_braidtail"
/datum/sprite_accessory/hair/bun
name = "Bun Head"
icon_state = "hair_bun"
/datum/sprite_accessory/hair/bun2
name = "Bun Head 2"
icon_state = "hair_bunhead2"
/datum/sprite_accessory/hair/largebun
name = "Bun (Large)"
icon_state = "hair_largebun"
/datum/sprite_accessory/hair/business
name = "Business Hair"
icon_state = "hair_business"
/datum/sprite_accessory/hair/business2
name = "Business Hair 2"
icon_state = "hair_business2"
/datum/sprite_accessory/hair/business3
name = "Business Hair 3"
icon_state = "hair_business3"
/datum/sprite_accessory/hair/business4
name = "Business Hair 4"
icon_state = "hair_business4"
/datum/sprite_accessory/hair/buzz
name = "Buzzcut"
icon_state = "hair_buzzcut"
/datum/sprite_accessory/hair/combover
name = "Combover"
icon_state = "hair_combover"
/datum/sprite_accessory/hair/crew
name = "Crewcut"
icon_state = "hair_crewcut"
/datum/sprite_accessory/hair/curls
name = "Curls"
icon_state = "hair_curls"
/datum/sprite_accessory/hair/cut
name = "Cut Hair"
icon_state = "hair_c"
/datum/sprite_accessory/hair/devillock
name = "Devil Lock"
icon_state = "hair_devilock"
/datum/sprite_accessory/hair/dreadlocks
name = "Dreadlocks"
icon_state = "hair_dreads"
/datum/sprite_accessory/hair/drillhair
name = "Drill Hair"
icon_state = "hair_drillhair"
/datum/sprite_accessory/hair/drillhairextended
name = "Drill Hair (Extended)"
icon_state = "hair_drillhairextended"
/datum/sprite_accessory/hair/emo
name = "Emo"
icon_state = "hair_emo"
/datum/sprite_accessory/hair/feather
name = "Feather"
icon_state = "hair_feather"
/datum/sprite_accessory/hair/sargeant
name = "Flat Top"
icon_state = "hair_sargeant"
/datum/sprite_accessory/hair/bigflattop
name = "Flat Top (Big)"
icon_state = "hair_bigflattop"
/datum/sprite_accessory/hair/fag
name = "Flow Hair"
icon_state = "hair_f"
/datum/sprite_accessory/hair/gelled
name = "Gelled Back"
icon_state = "hair_gelled"
/datum/sprite_accessory/hair/gentle
name = "Gentle"
icon_state = "hair_gentle"
/datum/sprite_accessory/hair/halfbang
name = "Half-banged Hair"
icon_state = "hair_halfbang"
/datum/sprite_accessory/hair/halfbang2
name = "Half-banged Hair 2"
icon_state = "hair_halfbang2"
/datum/sprite_accessory/hair/hedgehog
name = "Hedgehog Hair"
icon_state = "hair_hedgehog"
/datum/sprite_accessory/hair/himecut
name = "Hime Cut"
icon_state = "hair_himecut"
/datum/sprite_accessory/hair/himecut2
name = "Hime Cut 2"
icon_state = "hair_himecut2"
/datum/sprite_accessory/hair/himeup
name = "Hime Updo"
icon_state = "hair_himeup"
/datum/sprite_accessory/hair/hitop
name = "Hitop"
icon_state = "hair_hitop"
/datum/sprite_accessory/hair/jensen
name = "Jensen Hair"
icon_state = "hair_jensen"
/datum/sprite_accessory/hair/keanu
name = "Keanu Hair"
icon_state = "hair_keanu"
/datum/sprite_accessory/hair/kusangi
name = "Kusanagi Hair"
icon_state = "hair_kusanagi"
/datum/sprite_accessory/hair/long
name = "Long Hair 1"
icon_state = "hair_long"
/datum/sprite_accessory/hair/long2
name = "Long Hair 2"
icon_state = "hair_long2"
/datum/sprite_accessory/hair/long3
name = "Long Hair 3"
icon_state = "hair_long3"
/datum/sprite_accessory/hair/long_over_eye
name = "Long Over Eye"
icon_state = "hair_longovereye"
/datum/sprite_accessory/hair/longbangs
name = "Long Bangs"
icon_state = "hair_lbangs"
/datum/sprite_accessory/hair/longemo
name = "Long Emo"
icon_state = "hair_longemo"
/datum/sprite_accessory/hair/longfringe
name = "Long Fringe"
icon_state = "hair_longfringe"
/datum/sprite_accessory/hair/sidepartlongalt
name = "Long Side Part"
icon_state = "hair_longsidepart"
/datum/sprite_accessory/hair/megaeyebrows
name = "Mega Eyebrows"
icon_state = "hair_megaeyebrows"
/datum/sprite_accessory/hair/messy
name = "Messy"
icon_state = "hair_messy"
/datum/sprite_accessory/hair/mohawk
name = "Mohawk"
icon_state = "hair_d"
/datum/sprite_accessory/hair/reversemohawk
name = "Mohawk (Reverse)"
icon_state = "hair_reversemohawk"
/datum/sprite_accessory/hair/odango
name = "Odango"
icon_state = "hair_odango"
/datum/sprite_accessory/hair/ombre
name = "Ombre"
icon_state = "hair_ombre"
/datum/sprite_accessory/hair/oneshoulder
name = "One Shoulder"
icon_state = "hair_oneshoulder"
/datum/sprite_accessory/hair/over_eye
name = "Over Eye"
icon_state = "hair_shortovereye"
//Donator item - fractious
/datum/sprite_accessory/hair/over_eye_fr
name = "Over Eye (fract)"
icon_state = "hair_shortovereye_1f"
ckeys_allowed = list("fractious")
/datum/sprite_accessory/hair/parted
name = "Parted"
icon_state = "hair_parted"
/datum/sprite_accessory/hair/partedside
name = "Parted (Side)"
icon_state = "hair_part"
/datum/sprite_accessory/hair/kagami
name = "Pigtails"
icon_state = "hair_kagami"
/datum/sprite_accessory/hair/pigtail
name = "Pigtails 2"
icon_state = "hair_pigtails"
/datum/sprite_accessory/hair/pigtail2
name = "Pigtails 3"
icon_state = "hair_pigtails2"
/datum/sprite_accessory/hair/pixie
name = "Pixie Cut"
icon_state = "hair_pixie"
/datum/sprite_accessory/hair/pompadour
name = "Pompadour"
icon_state = "hair_pompadour"
/datum/sprite_accessory/hair/bigpompadour
name = "Pompadour (Big)"
icon_state = "hair_bigpompadour"
/datum/sprite_accessory/hair/ponytail1
name = "Ponytail"
icon_state = "hair_ponytail"
/datum/sprite_accessory/hair/ponytail2
name = "Ponytail 2"
icon_state = "hair_ponytail2"
/datum/sprite_accessory/hair/ponytail3
name = "Ponytail 3"
icon_state = "hair_ponytail3"
/datum/sprite_accessory/hair/ponytail4
name = "Ponytail 4"
icon_state = "hair_ponytail4"
/datum/sprite_accessory/hair/ponytail5
name = "Ponytail 5"
icon_state = "hair_ponytail5"
/datum/sprite_accessory/hair/highponytail
name = "Ponytail (High)"
icon_state = "hair_highponytail"
/datum/sprite_accessory/hair/longponytail
name = "Ponytail (Long)"
icon_state = "hair_longstraightponytail"
/datum/sprite_accessory/hair/sidetail
name = "Ponytail (Side)"
icon_state = "hair_sidetail"
/datum/sprite_accessory/hair/sidetail2
name = "Ponytail (Side) 2"
icon_state = "hair_sidetail2"
/datum/sprite_accessory/hair/sidetail3
name = "Ponytail (Side) 3"
icon_state = "hair_sidetail3"
/datum/sprite_accessory/hair/sidetail4
name = "Ponytail (Side) 4"
icon_state = "hair_sidetail4"
/datum/sprite_accessory/hair/quiff
name = "Quiff"
icon_state = "hair_quiff"
/datum/sprite_accessory/hair/short
name = "Short Hair"
icon_state = "hair_a"
/datum/sprite_accessory/hair/shorthair2
name = "Short Hair 2"
icon_state = "hair_shorthair2"
/datum/sprite_accessory/hair/shorthair3
name = "Short Hair 3"
icon_state = "hair_shorthair3"
/datum/sprite_accessory/hair/shoulderlength
name = "Shoulder-length Hair"
icon_state = "hair_b"
/datum/sprite_accessory/hair/sidecut
name = "Sidecut"
icon_state = "hair_sidecut"
/datum/sprite_accessory/hair/skinhead
name = "Skinhead"
icon_state = "hair_skinhead"
/datum/sprite_accessory/hair/protagonist
name = "Slightly Long Hair"
icon_state = "hair_protagonist"
/datum/sprite_accessory/hair/spiky
name = "Spiky"
icon_state = "hair_spikey"
/datum/sprite_accessory/hair/spiky2
name = "Spiky 2"
icon_state = "hair_spiky"
/datum/sprite_accessory/hair/spiky3
name = "Spiky 3"
icon_state = "hair_spiky2"
/datum/sprite_accessory/hair/swept
name = "Swept Back Hair"
icon_state = "hair_swept"
/datum/sprite_accessory/hair/swept2
name = "Swept Back Hair 2"
icon_state = "hair_swept2"
/datum/sprite_accessory/hair/tressshoulder
name = "Tress Shoulder"
icon_state = "hair_tressshoulder"
/datum/sprite_accessory/hair/longtwintails
name = "Twintails (Long)"
icon_state = "hair_longstraighttwintails"
/datum/sprite_accessory/hair/updo
name = "Updo"
icon_state = "hair_updo"
/datum/sprite_accessory/hair/longer
name = "Very Long Hair"
icon_state = "hair_vlong"
/datum/sprite_accessory/hair/longest
name = "Very Long Hair 2"
icon_state = "hair_longest"
/datum/sprite_accessory/hair/longest2
name = "Very Long Over Eye"
icon_state = "hair_longest2"
/datum/sprite_accessory/hair/longestalt
name = "Very Long with Fringe"
icon_state = "hair_vlongfringe"
@@ -0,0 +1,26 @@
/datum/sprite_accessory/horns
icon = 'icons/mob/mutant_bodyparts.dmi'
/datum/sprite_accessory/horns/none
name = "None"
icon_state = "none"
/datum/sprite_accessory/horns/simple
name = "Simple"
icon_state = "simple"
/datum/sprite_accessory/horns/short
name = "Short"
icon_state = "short"
/datum/sprite_accessory/horns/curled
name = "Curled"
icon_state = "curled"
/datum/sprite_accessory/horns/ram
name = "Ram"
icon_state = "ram"
/datum/sprite_accessory/horns/angler
name = "Angeler"
icon_state = "angler"
@@ -0,0 +1,8 @@
/datum/sprite_accessory/legs //legs are a special case, they aren't actually sprite_accessories but are updated with them.
icon = null //These datums exist for selecting legs on preference, and little else
/datum/sprite_accessory/legs/none
name = "Normal Legs"
/datum/sprite_accessory/legs/digitigrade_lizard
name = "Digitigrade Legs"
@@ -0,0 +1,68 @@
/datum/sprite_accessory/moth_wings
icon = 'icons/mob/wings.dmi'
color_src = null
/datum/sprite_accessory/moth_wings/plain
name = "Plain"
icon_state = "plain"
/datum/sprite_accessory/moth_wings/monarch
name = "Monarch"
icon_state = "monarch"
/datum/sprite_accessory/moth_wings/luna
name = "Luna"
icon_state = "luna"
/datum/sprite_accessory/moth_wings/atlas
name = "Atlas"
icon_state = "atlas"
/datum/sprite_accessory/moth_wings/reddish
name = "Reddish"
icon_state = "redish"
/datum/sprite_accessory/moth_wings/royal
name = "Royal"
icon_state = "royal"
/datum/sprite_accessory/moth_wings/gothic
name = "Gothic"
icon_state = "gothic"
/datum/sprite_accessory/moth_wings/lovers
name = "Lovers"
icon_state = "lovers"
/datum/sprite_accessory/moth_wings/whitefly
name = "White Fly"
icon_state = "whitefly"
/datum/sprite_accessory/moth_wings/punished
name = "Burnt Off"
icon_state = "punished"
locked = TRUE
/datum/sprite_accessory/moth_wings/firewatch
name = "Firewatch"
icon_state = "firewatch"
/datum/sprite_accessory/moth_wings/deathhead
name = "Deathshead"
icon_state = "deathhead"
/datum/sprite_accessory/moth_wings/poison
name = "Poison"
icon_state = "poison"
/datum/sprite_accessory/moth_wings/ragged
name = "Ragged"
icon_state = "ragged"
/datum/sprite_accessory/moth_wings/moonfly
name = "Moon Fly"
icon_state = "moonfly"
/datum/sprite_accessory/moth_wings/snow
name = "Snow"
icon_state = "snow"
@@ -0,0 +1,53 @@
/datum/sprite_accessory/spines
icon = 'icons/mob/mutant_bodyparts.dmi'
/datum/sprite_accessory/spines_animated
icon = 'icons/mob/mutant_bodyparts.dmi'
/datum/sprite_accessory/spines/none
name = "None"
icon_state = "none"
/datum/sprite_accessory/spines_animated/none
name = "None"
icon_state = "none"
/datum/sprite_accessory/spines/short
name = "Short"
icon_state = "short"
/datum/sprite_accessory/spines_animated/short
name = "Short"
icon_state = "short"
/datum/sprite_accessory/spines/shortmeme
name = "Short + Membrane"
icon_state = "shortmeme"
/datum/sprite_accessory/spines_animated/shortmeme
name = "Short + Membrane"
icon_state = "shortmeme"
/datum/sprite_accessory/spines/long
name = "Long"
icon_state = "long"
/datum/sprite_accessory/spines_animated/long
name = "Long"
icon_state = "long"
/datum/sprite_accessory/spines/longmeme
name = "Long + Membrane"
icon_state = "longmeme"
/datum/sprite_accessory/spines_animated/longmeme
name = "Long + Membrane"
icon_state = "longmeme"
/datum/sprite_accessory/spines/aqautic
name = "Aquatic"
icon_state = "aqua"
/datum/sprite_accessory/spines_animated/aqautic
name = "Aquatic"
icon_state = "aqua"
@@ -0,0 +1,18 @@
/datum/sprite_accessory/snouts
icon = 'icons/mob/mutant_bodyparts.dmi'
/datum/sprite_accessory/snouts/sharp
name = "Sharp"
icon_state = "sharp"
/datum/sprite_accessory/snouts/round
name = "Round"
icon_state = "round"
/datum/sprite_accessory/snouts/sharplight
name = "Sharp + Light"
icon_state = "sharplight"
/datum/sprite_accessory/snouts/roundlight
name = "Round + Light"
icon_state = "roundlight"
@@ -0,0 +1,133 @@
///////////////////////
// Socks Definitions //
///////////////////////
/datum/sprite_accessory/underwear/socks/nude
name = "Nude"
icon_state = null
// please make sure they're sorted alphabetically and categorized
/datum/sprite_accessory/underwear/socks/socks_knee
name = "Knee-high"
icon_state = "socks_knee"
has_color = TRUE
/datum/sprite_accessory/underwear/socks/bee_knee
name = "Knee-high - Bee"
icon_state = "bee_knee"
/datum/sprite_accessory/underwear/socks/commie_knee
name = "Knee-High - Commie"
icon_state = "commie_knee"
/datum/sprite_accessory/underwear/socks/usa_knee
name = "Knee-High - Freedom"
icon_state = "assblastusa_knee"
/datum/sprite_accessory/underwear/socks/rainbow_knee
name = "Knee-high - Rainbow"
icon_state = "rainbow_knee"
/datum/sprite_accessory/underwear/socks/striped_knee
name = "Knee-high - Striped"
icon_state = "striped_knee"
has_color = TRUE
/datum/sprite_accessory/underwear/socks/thin_knee
name = "Knee-high - Thin"
icon_state = "thin_knee"
has_color = TRUE
/datum/sprite_accessory/underwear/socks/uk_knee
name = "Knee-High - UK"
icon_state = "uk_knee"
/datum/sprite_accessory/underwear/socks/socks_norm
name = "Normal"
icon_state = "socks_norm"
has_color = TRUE
/datum/sprite_accessory/underwear/socks/bee_norm
name = "Normal - Bee"
icon_state = "bee_norm"
/datum/sprite_accessory/underwear/socks/pantyhose
name = "Pantyhose"
icon_state = "pantyhose"
/datum/sprite_accessory/underwear/socks/socks_short
name = "Short"
icon_state = "socks_short"
has_color = TRUE
/datum/sprite_accessory/underwear/socks/stockings_blue
name = "Stockings - Blue"
icon_state = "stockings_blue"
/datum/sprite_accessory/underwear/socks/stockings_cyan
name = "Stockings - Cyan"
icon_state = "stockings_cyan"
/datum/sprite_accessory/underwear/socks/stockings_dpink
name = "Stockings - Dark Pink"
icon_state = "stockings_dpink"
/datum/sprite_accessory/underwear/socks/stockings_green
name = "Stockings - Green"
icon_state = "stockings_black"
/datum/sprite_accessory/underwear/socks/stockings_orange
name = "Stockings - Orange"
icon_state = "stockings_orange"
/datum/sprite_accessory/underwear/socks/stockings_programmer
name = "Stockings - Programmer"
icon_state = "stockings_lpink"
/datum/sprite_accessory/underwear/socks/stockings_purple
name = "Stockings - Purple"
icon_state = "stockings_purple"
/datum/sprite_accessory/underwear/socks/stockings_yellow
name = "Stockings - Yellow"
icon_state = "stockings_yellow"
/datum/sprite_accessory/underwear/socks/socks_thigh
name = "Thigh-high"
icon_state = "socks_thigh"
has_color = TRUE
/datum/sprite_accessory/underwear/socks/bee_thigh
name = "Thigh-high - Bee"
icon_state = "bee_thigh"
/datum/sprite_accessory/underwear/socks/commie_thigh
name = "Thigh-high - Commie"
icon_state = "commie_thigh"
/datum/sprite_accessory/underwear/socks/usa_thigh
name = "Thigh-high - Freedom"
icon_state = "assblastusa_thigh"
/datum/sprite_accessory/underwear/socks/fishnet
name = "Thigh-high - Fishnet"
icon_state = "fishnet"
/datum/sprite_accessory/underwear/socks/rainbow_thigh
name = "Thigh-high - Rainbow"
icon_state = "rainbow_thigh"
/datum/sprite_accessory/underwear/socks/striped_thigh
name = "Thigh-high - Striped"
icon_state = "striped_thigh"
has_color = TRUE
/datum/sprite_accessory/underwear/socks/thin_thigh
name = "Thigh-high - Thin"
icon_state = "thin_thigh"
has_color = TRUE
/datum/sprite_accessory/underwear/socks/uk_thigh
name = "Thigh-high - UK"
icon_state = "uk_thigh"
@@ -0,0 +1,55 @@
/datum/sprite_accessory/tails
icon = 'icons/mob/mutant_bodyparts.dmi'
/datum/sprite_accessory/tails_animated
icon = 'icons/mob/mutant_bodyparts.dmi'
/datum/sprite_accessory/tails/lizard/smooth
name = "Smooth"
icon_state = "smooth"
/datum/sprite_accessory/tails_animated/lizard/smooth
name = "Smooth"
icon_state = "smooth"
/datum/sprite_accessory/tails/lizard/dtiger
name = "Dark Tiger"
icon_state = "dtiger"
/datum/sprite_accessory/tails_animated/lizard/dtiger
name = "Dark Tiger"
icon_state = "dtiger"
/datum/sprite_accessory/tails/lizard/ltiger
name = "Light Tiger"
icon_state = "ltiger"
/datum/sprite_accessory/tails_animated/lizard/ltiger
name = "Light Tiger"
icon_state = "ltiger"
/datum/sprite_accessory/tails/lizard/spikes
name = "Spikes"
icon_state = "spikes"
/datum/sprite_accessory/tails_animated/lizard/spikes
name = "Spikes"
icon_state = "spikes"
/datum/sprite_accessory/tails/human/none
name = "None"
icon_state = "none"
/datum/sprite_accessory/tails_animated/human/none
name = "None"
icon_state = "none"
/*
/datum/sprite_accessory/tails/human/cat
name = "Cat"
icon_state = "cat"
color_src = HAIR
/datum/sprite_accessory/tails_animated/human/cat
name = "Cat"
icon_state = "cat"
color_src = HAIR*/
@@ -0,0 +1,313 @@
////////////////////////////
// Undershirt Definitions //
////////////////////////////
/datum/sprite_accessory/underwear/top/nude
name = "Nude"
icon_state = null
// please make sure they're sorted alphabetically and categorized
/datum/sprite_accessory/underwear/top/longjon
name = "Long John Shirt"
icon_state = "ljont"
has_color = TRUE
/datum/sprite_accessory/underwear/top/longstripe_black
name = "Longsleeve Striped Shirt - Black"
icon_state = "longstripe"
/datum/sprite_accessory/underwear/top/longstripe_blue
name = "Longsleeve Striped Shirt - Blue"
icon_state = "longstripe_blue"
/datum/sprite_accessory/underwear/top/shirt
name = "Shirt"
icon_state = "undershirt"
has_color = TRUE
/datum/sprite_accessory/underwear/top/bowlingw
name = "Shirt - Bowling"
icon_state = "bowlingw"
has_color = TRUE
/datum/sprite_accessory/underwear/top/bowling
name = "Shirt, Bowling - Red"
icon_state = "bowling"
/datum/sprite_accessory/underwear/top/bowlingp
name = "Shirt, Bowling - Pink"
icon_state = "bowlingp"
/datum/sprite_accessory/underwear/top/bowlinga
name = "Shirt, Bowling - Aqua"
icon_state = "bowlinga"
/datum/sprite_accessory/underwear/top/bluejersey
name = "Shirt, Jersey - Blue"
icon_state = "shirt_bluejersey"
/datum/sprite_accessory/underwear/top/redjersey
name = "Shirt, Jersey - Red"
icon_state = "shirt_redjersey"
/datum/sprite_accessory/underwear/top/polo
name = "Shirt - Polo"
icon_state = "polo"
has_color = TRUE
/datum/sprite_accessory/underwear/top/alienshirt
name = "Shirt - Alien"
icon_state = "shirt_alien"
/datum/sprite_accessory/underwear/top/mondmondjaja
name = "Shirt - Band"
icon_state = "band"
/datum/sprite_accessory/underwear/top/shirt_bee
name = "Shirt - Bee"
icon_state = "bee_shirt"
/datum/sprite_accessory/underwear/top/clownshirt
name = "Shirt - Clown"
icon_state = "shirt_clown"
/datum/sprite_accessory/underwear/top/commie
name = "Shirt - Commie"
icon_state = "shirt_commie"
/datum/sprite_accessory/underwear/top/ian
name = "Shirt - Ian"
icon_state = "ian"
/datum/sprite_accessory/underwear/top/ilovent
name = "Shirt - I Love NT"
icon_state = "ilovent"
/datum/sprite_accessory/underwear/top/lover
name = "Shirt - Lover"
icon_state = "lover"
/datum/sprite_accessory/underwear/top/matroska
name = "Shirt - Matroska"
icon_state = "matroska"
/datum/sprite_accessory/underwear/top/meat
name = "Shirt - Meat"
icon_state = "shirt_meat"
/datum/sprite_accessory/underwear/top/nano
name = "Shirt - Nanotrasen"
icon_state = "shirt_nano"
/datum/sprite_accessory/underwear/top/peace
name = "Shirt - Peace"
icon_state = "peace"
/datum/sprite_accessory/underwear/top/pacman
name = "Shirt - Pogoman"
icon_state = "pogoman"
/datum/sprite_accessory/underwear/top/question
name = "Shirt - Question"
icon_state = "shirt_question"
/datum/sprite_accessory/underwear/top/skull
name = "Shirt - Skull"
icon_state = "shirt_skull"
/datum/sprite_accessory/underwear/top/ss13
name = "Shirt - SS13"
icon_state = "shirt_ss13"
has_color = TRUE
/datum/sprite_accessory/underwear/top/stripe
name = "Shirt - Striped"
icon_state = "shirt_stripes"
/datum/sprite_accessory/underwear/top/tiedye
name = "Shirt - Tie-dye"
icon_state = "shirt_tiedye"
/datum/sprite_accessory/underwear/top/uk
name = "Shirt - UK"
icon_state = "uk"
/datum/sprite_accessory/underwear/top/usa
name = "Shirt - USA"
icon_state = "shirt_assblastusa"
/datum/sprite_accessory/underwear/top/shortsleeve
name = "Shirt - Short Sleeved"
icon_state = "shortsleeve"
has_color = TRUE
/datum/sprite_accessory/underwear/top/blueshirtsport
name = "Shirt, Sports - Blue"
icon_state = "blueshirtsport"
/datum/sprite_accessory/underwear/top/greenshirtsport
name = "Shirt, Sports - Green"
icon_state = "greenshirtsport"
/datum/sprite_accessory/underwear/top/redshirtsport
name = "Shirt, Sports - Red"
icon_state = "redshirtsport"
/datum/sprite_accessory/underwear/top/tankfire
name = "Tank Top - Fire"
icon_state = "tank_fire"
/datum/sprite_accessory/underwear/top/tanktop
name = "Tank Top"
icon_state = "tanktop"
has_color = TRUE
/datum/sprite_accessory/underwear/top/tanktop_alt
name = "Tank Top - Alt"
icon_state = "tanktop_alt"
has_color = TRUE
/datum/sprite_accessory/underwear/top/tanktop_midriff
name = "Tank Top - Midriff"
icon_state = "tank_midriff"
has_color = TRUE
gender = FEMALE
/datum/sprite_accessory/underwear/top/tanktop_midriff_alt
name = "Tank Top - Midriff Halterneck"
icon_state = "tank_midriff_alt"
has_color = TRUE
gender = FEMALE
/datum/sprite_accessory/underwear/top/tankstripe
name = "Tank Top - Striped"
icon_state = "tank_stripes"
/datum/sprite_accessory/underwear/top/tank_top_sun
name = "Tank top - Sun"
icon_state = "tank_sun"
/datum/sprite_accessory/underwear/top/babydoll
name = "Baby-Doll"
icon_state = "babydoll"
has_color = TRUE
gender = FEMALE
/datum/sprite_accessory/underwear/top/bra
name = "Bra"
icon_state = "bra"
has_color = TRUE
gender = FEMALE
/datum/sprite_accessory/underwear/top/bra_alt
name = "Bra - Alt"
icon_state = "bra_alt"
has_color = TRUE
gender = FEMALE
/datum/sprite_accessory/underwear/top/bra_thin
name = "Bra - Thin"
icon_state = "bra_thin"
has_color = TRUE
gender = FEMALE
/datum/sprite_accessory/underwear/top/bra_kinky
name = "Bra - Kinky Black"
icon_state = "bra_kinky"
gender = FEMALE
/datum/sprite_accessory/underwear/top/bra_freedom
name = "Bra - Freedom"
icon_state = "bra_assblastusa"
gender = FEMALE
/datum/sprite_accessory/underwear/top/bra_commie
name = "Bra - Commie"
icon_state = "bra_commie"
gender = FEMALE
/datum/sprite_accessory/underwear/top/bra_beekini
name = "Bra - Bee-kini"
icon_state = "bra_bee-kini"
gender = FEMALE
/datum/sprite_accessory/underwear/top/bra_uk
name = "Bra - UK"
icon_state = "bra_uk"
gender = FEMALE
/datum/sprite_accessory/underwear/top/bra_neko
name = "Bra - Neko"
icon_state = "bra_neko"
has_color = TRUE
gender = FEMALE
/datum/sprite_accessory/underwear/top/halterneck_bra
name = "Bra - Halterneck"
icon_state = "halterneck_bra"
has_color = TRUE
gender = FEMALE
/datum/sprite_accessory/underwear/top/sports_bra
name = "Bra, Sports"
icon_state = "sports_bra"
has_color = TRUE
gender = FEMALE
/datum/sprite_accessory/underwear/top/sports_bra_alt
name = "Bra, Sports - Alt"
icon_state = "sports_bra_alt"
has_color = TRUE
gender = FEMALE
/datum/sprite_accessory/underwear/top/bra_strapless
name = "Bra, Strapless"
icon_state = "bra_strapless"
has_color = TRUE
gender = FEMALE
/datum/sprite_accessory/underwear/top/bra_strapless_alt
name = "Bra, Strapless - Alt"
icon_state = "bra_blue"
has_color = TRUE
gender = FEMALE
/datum/sprite_accessory/underwear/top/striped_bra
name = "Bra - Striped"
icon_state = "striped_bra"
has_color = TRUE
gender = FEMALE
/datum/sprite_accessory/underwear/top/fishnet_sleeves
name = "Fishnet - sleeves"
icon_state = "fishnet_sleeves"
gender = FEMALE
/datum/sprite_accessory/underwear/top/fishnet_gloves
name = "Fishnet - gloves"
icon_state = "fishnet_gloves"
gender = FEMALE
/datum/sprite_accessory/underwear/top/fishnet_base
name = "Fishnet - top"
icon_state = "fishnet_body"
gender = FEMALE
/datum/sprite_accessory/underwear/top/swimsuit
name = "Swimsuit Top"
icon_state = "bra_swimming"
has_color = TRUE
gender = FEMALE
/datum/sprite_accessory/underwear/top/swimsuit_alt
name = "Swimsuit Top - Strapless"
icon_state = "bra_swimming_alt"
has_color = TRUE
gender = FEMALE
/datum/sprite_accessory/underwear/top/tubetop
name = "Tube Top"
icon_state = "tubetop"
has_color = TRUE
gender = FEMALE
@@ -0,0 +1,172 @@
///////////////////////////
// Underwear Definitions //
///////////////////////////
/datum/sprite_accessory/underwear/bottom/nude
name = "Nude"
icon_state = null
/datum/sprite_accessory/underwear/bottom/mankini
name = "Mankini"
icon_state = "mankini"
has_color = TRUE
gender = MALE
/datum/sprite_accessory/underwear/bottom/male_kinky
name = "Jockstrap"
icon_state = "jockstrap"
has_color = TRUE
gender = MALE
/datum/sprite_accessory/underwear/bottom/briefs
name = "Briefs"
icon_state = "briefs"
has_color = TRUE
gender = MALE
/datum/sprite_accessory/underwear/bottom/boxers
name = "Boxers"
icon_state = "boxers"
has_color = TRUE
gender = MALE
/datum/sprite_accessory/underwear/bottom/male_bee
name = "Boxers - Bee"
icon_state = "bee_shorts"
gender = MALE
/datum/sprite_accessory/underwear/bottom/male_hearts
name = "Boxers - Heart"
icon_state = "boxers_heart"
gender = MALE
/datum/sprite_accessory/underwear/bottom/male_stripe
name = "Boxers - Striped"
icon_state = "boxers_striped"
gender = MALE
/datum/sprite_accessory/underwear/bottom/male_commie
name = "Boxers - Striped Communist"
icon_state = "boxers_commie"
gender = MALE
/datum/sprite_accessory/underwear/bottom/male_usastripe
name = "Boxers - Striped Freedom"
icon_state = "boxers_assblastusa"
gender = MALE
/datum/sprite_accessory/underwear/bottom/male_uk
name = "Boxers - Striped UK"
icon_state = "boxers_uk"
gender = MALE
/datum/sprite_accessory/underwear/bottom/boxer_briefs
name = "Boxer Briefs"
icon_state = "boxer_briefs"
has_color = TRUE
/datum/sprite_accessory/underwear/bottom/panties
name = "Panties"
icon_state = "panties"
has_color = TRUE
gender = FEMALE
/datum/sprite_accessory/underwear/bottom/panties_alt
name = "Panties - Alt"
icon_state = "panties_alt"
has_color = TRUE
gender = FEMALE
/datum/sprite_accessory/underwear/bottom/fishnet_lower
name = "Panties - Fishnet"
icon_state = "fishnet_lower"
gender = FEMALE
/datum/sprite_accessory/underwear/bottom/female_beekini
name = "Panties - Bee-kini"
icon_state = "panties_bee-kini"
gender = FEMALE
/datum/sprite_accessory/underwear/bottom/female_commie
name = "Panties - Commie"
icon_state = "panties_commie"
gender = FEMALE
/datum/sprite_accessory/underwear/bottom/female_usastripe
name = "Panties - Freedom"
icon_state = "panties_assblastusa"
gender = FEMALE
/datum/sprite_accessory/underwear/bottom/female_kinky
name = "Panties - Kinky Black"
icon_state = "panties_kinky"
gender = FEMALE
/datum/sprite_accessory/underwear/bottom/panties_uk
name = "Panties - UK"
icon_state = "panties_uk"
gender = FEMALE
/datum/sprite_accessory/underwear/bottom/panties_neko
name = "Panties - Neko"
icon_state = "panties_neko"
has_color = TRUE
gender = FEMALE
/datum/sprite_accessory/underwear/bottom/panties_slim
name = "Panties - Slim"
icon_state = "panties_slim"
has_color = TRUE
gender = FEMALE
/datum/sprite_accessory/underwear/bottom/striped_panties
name = "Panties - Striped"
icon_state = "striped_panties"
has_color = TRUE
gender = FEMALE
/datum/sprite_accessory/underwear/bottom/panties_swimsuit
name = "Panties - Swimsuit"
icon_state = "panties_swimming"
has_color = TRUE
gender = FEMALE
/datum/sprite_accessory/underwear/bottom/panties_thin
name = "Panties - Thin"
icon_state = "panties_thin"
has_color = TRUE
gender = FEMALE
/datum/sprite_accessory/underwear/bottom/longjon
name = "Long John Bottoms"
icon_state = "ljonb"
has_color = TRUE
/datum/sprite_accessory/underwear/bottom/swimsuit_red
name = "Swimsuit, One Piece - Red"
icon_state = "swimming_red"
gender = FEMALE
/datum/sprite_accessory/underwear/bottom/swimsuit
name = "Swimsuit, One Piece - Black"
icon_state = "swimming_black"
gender = FEMALE
/datum/sprite_accessory/underwear/bottom/swimsuit_blue
name = "Swimsuit, One Piece - Striped Blue"
icon_state = "swimming_blue"
gender = FEMALE
/datum/sprite_accessory/underwear/bottom/thong
name = "Thong"
icon_state = "thong"
has_color = TRUE
gender = FEMALE
/datum/sprite_accessory/underwear/bottom/thong_babydoll
name = "Thong - Alt"
icon_state = "thong_babydoll"
has_color = TRUE
gender = FEMALE
@@ -0,0 +1,26 @@
/datum/sprite_accessory/wings/none
name = "None"
icon_state = "none"
/datum/sprite_accessory/wings_open
icon = 'icons/mob/wings.dmi'
/datum/sprite_accessory/wings_open/angel
name = "Angel"
icon_state = "angel"
color_src = 0
dimension_x = 46
center = TRUE
dimension_y = 34
/datum/sprite_accessory/wings
icon = 'icons/mob/wings.dmi'
/datum/sprite_accessory/wings/angel
name = "Angel"
icon_state = "angel"
color_src = 0
dimension_x = 46
center = TRUE
dimension_y = 34
locked = TRUE
+20
View File
@@ -0,0 +1,20 @@
/mob/dead/observer/Login()
..()
ghost_accs = client.prefs.ghost_accs
ghost_others = client.prefs.ghost_others
var/preferred_form = null
if(IsAdminGhost(src))
has_unlimited_silicon_privilege = 1
if(client.prefs.unlock_content)
preferred_form = client.prefs.ghost_form
ghost_orbit = client.prefs.ghost_orbit
var/turf/T = get_turf(src)
if (isturf(T))
update_z(T.z)
update_icon(preferred_form)
updateghostimages()
+16
View File
@@ -0,0 +1,16 @@
/mob/dead/observer/Logout()
update_z(null)
if (client)
client.images -= (GLOB.ghost_images_default+GLOB.ghost_images_simple)
if(observetarget)
if(ismob(observetarget))
var/mob/target = observetarget
if(target.observers)
target.observers -= src
UNSETEMPTY(target.observers)
observetarget = null
..()
spawn(0)
if(src && !key) //we've transferred to another mob. This ghost should be deleted.
qdel(src)
@@ -0,0 +1,49 @@
/mob/dead/observer/verb/show_notificationprefs()
set category = "Ghost"
set name = "Notification preferences"
set desc = "Notification preferences"
var/datum/notificationpanel/panel = new(usr)
panel.ui_interact(usr)
/datum/notificationpanel
var/client/user
/datum/notificationpanel/New(user)
if (ismob(user))
var/mob/M = user
if (!M.client)
CRASH("Ghost role notification panel attempted to open to a mob without a client")
src.user = M.client
else
src.user = user
/datum/notificationpanel/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.observer_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "notificationpanel", "Notification Preferences", 700, 700, master_ui, state)
ui.open()
/datum/notificationpanel/ui_data(mob/user)
. = list()
.["ignore"] = list()
for(var/key in GLOB.poll_ignore_desc)
.["ignore"] += list(list(
"key" = key,
"enabled" = (user.ckey in GLOB.poll_ignore[key]),
"desc" = GLOB.poll_ignore_desc[key]
))
/datum/notificationpanel/ui_act(action, params)
if(..())
return
switch (action)
if ("toggle_ignore")
var/key = params["key"]
if (key && islist(GLOB.poll_ignore[key]))
GLOB.poll_ignore[key] ^= list(user.ckey)
. = TRUE
+867
View File
@@ -0,0 +1,867 @@
GLOBAL_LIST_EMPTY(ghost_images_default) //this is a list of the default (non-accessorized, non-dir) images of the ghosts themselves
GLOBAL_LIST_EMPTY(ghost_images_simple) //this is a list of all ghost images as the simple white ghost
GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
/mob/dead/observer
name = "ghost"
desc = "It's a g-g-g-g-ghooooost!" //jinkies!
icon = 'icons/mob/mob.dmi'
icon_state = "ghost"
layer = GHOST_LAYER
stat = DEAD
density = FALSE
canmove = 0
anchored = TRUE // don't get pushed around
see_invisible = SEE_INVISIBLE_OBSERVER
see_in_dark = 100
invisibility = INVISIBILITY_OBSERVER
hud_type = /datum/hud/ghost
var/can_reenter_corpse
var/can_reenter_round = TRUE
var/datum/hud/living/carbon/hud = null // hud
var/bootime = 0
var/started_as_observer //This variable is set to 1 when you enter the game as an observer.
//If you died in the game and are a ghsot - this will remain as null.
//Note that this is not a reliable way to determine if admins started as observers, since they change mobs a lot.
var/atom/movable/following = null
var/fun_verbs = 0
var/image/ghostimage_default = null //this mobs ghost image without accessories and dirs
var/image/ghostimage_simple = null //this mob with the simple white ghost sprite
var/ghostvision = 1 //is the ghost able to see things humans can't?
var/mob/observetarget = null //The target mob that the ghost is observing. Used as a reference in logout()
var/ghost_hud_enabled = 1 //did this ghost disable the on-screen HUD?
var/data_huds_on = 0 //Are data HUDs currently enabled?
var/health_scan = FALSE //Are health scans currently enabled?
var/list/datahuds = list(DATA_HUD_SECURITY_ADVANCED, DATA_HUD_MEDICAL_ADVANCED, DATA_HUD_DIAGNOSTIC_ADVANCED) //list of data HUDs shown to ghosts.
var/ghost_orbit = GHOST_ORBIT_CIRCLE
//These variables store hair data if the ghost originates from a species with head and/or facial hair.
var/hair_style
var/hair_color
var/mutable_appearance/hair_overlay
var/facial_hair_style
var/facial_hair_color
var/mutable_appearance/facial_hair_overlay
var/updatedir = 1 //Do we have to update our dir as the ghost moves around?
var/lastsetting = null //Stores the last setting that ghost_others was set to, for a little more efficiency when we update ghost images. Null means no update is necessary
//We store copies of the ghost display preferences locally so they can be referred to even if no client is connected.
//If there's a bug with changing your ghost settings, it's probably related to this.
var/ghost_accs = GHOST_ACCS_DEFAULT_OPTION
var/ghost_others = GHOST_OTHERS_DEFAULT_OPTION
// Used for displaying in ghost chat, without changing the actual name
// of the mob
var/deadchat_name
var/datum/spawners_menu/spawners_menu
/mob/dead/observer/Initialize()
set_invisibility(GLOB.observer_default_invisibility)
verbs += list(
/mob/dead/observer/proc/dead_tele,
/mob/dead/observer/proc/open_spawners_menu,
/mob/dead/observer/proc/view_gas)
if(icon_state in GLOB.ghost_forms_with_directions_list)
ghostimage_default = image(src.icon,src,src.icon_state + "_nodir")
else
ghostimage_default = image(src.icon,src,src.icon_state)
ghostimage_default.override = TRUE
GLOB.ghost_images_default |= ghostimage_default
ghostimage_simple = image(src.icon,src,"ghost_nodir")
ghostimage_simple.override = TRUE
GLOB.ghost_images_simple |= ghostimage_simple
updateallghostimages()
var/turf/T
var/mob/body = loc
if(ismob(body))
T = get_turf(body) //Where is the body located?
gender = body.gender
if(body.mind && body.mind.name)
name = body.mind.name
else
if(body.real_name)
name = body.real_name
else
name = random_unique_name(gender)
mind = body.mind //we don't transfer the mind but we keep a reference to it.
suiciding = body.suiciding // Transfer whether they committed suicide.
if(ishuman(body))
var/mob/living/carbon/human/body_human = body
if(HAIR in body_human.dna.species.species_traits)
hair_style = body_human.hair_style
hair_color = brighten_color(body_human.hair_color)
if(FACEHAIR in body_human.dna.species.species_traits)
facial_hair_style = body_human.facial_hair_style
facial_hair_color = brighten_color(body_human.facial_hair_color)
update_icon()
if(!T)
var/list/turfs = get_area_turfs(/area/shuttle/arrival)
if(turfs.len)
T = pick(turfs)
else
T = SSmapping.get_station_center()
forceMove(T)
if(!name) //To prevent nameless ghosts
name = random_unique_name(gender)
real_name = name
if(!fun_verbs)
verbs -= /mob/dead/observer/verb/boo
verbs -= /mob/dead/observer/verb/possess
animate(src, pixel_y = 2, time = 10, loop = -1)
GLOB.dead_mob_list += src
for(var/v in GLOB.active_alternate_appearances)
if(!v)
continue
var/datum/atom_hud/alternate_appearance/AA = v
AA.onNewMob(src)
. = ..()
grant_all_languages()
/mob/dead/observer/get_photo_description(obj/item/camera/camera)
if(!invisibility || camera.see_ghosts)
return "You can also see a g-g-g-g-ghooooost!"
/mob/dead/observer/narsie_act()
var/old_color = color
color = "#960000"
animate(src, color = old_color, time = 10, flags = ANIMATION_PARALLEL)
addtimer(CALLBACK(src, /atom/proc/update_atom_colour), 10)
/mob/dead/observer/ratvar_act()
var/old_color = color
color = "#FAE48C"
animate(src, color = old_color, time = 10, flags = ANIMATION_PARALLEL)
addtimer(CALLBACK(src, /atom/proc/update_atom_colour), 10)
/mob/dead/observer/Destroy()
GLOB.ghost_images_default -= ghostimage_default
QDEL_NULL(ghostimage_default)
GLOB.ghost_images_simple -= ghostimage_simple
QDEL_NULL(ghostimage_simple)
updateallghostimages()
QDEL_NULL(spawners_menu)
return ..()
/mob/dead/CanPass(atom/movable/mover, turf/target)
return 1
/*
* This proc will update the icon of the ghost itself, with hair overlays, as well as the ghost image.
* Please call update_icon(icon_state) from now on when you want to update the icon_state of the ghost,
* or you might end up with hair on a sprite that's not supposed to get it.
* Hair will always update its dir, so if your sprite has no dirs the haircut will go all over the place.
* |- Ricotez
*/
/mob/dead/observer/proc/update_icon(new_form)
if(client) //We update our preferences in case they changed right before update_icon was called.
ghost_accs = client.prefs.ghost_accs
ghost_others = client.prefs.ghost_others
if(hair_overlay)
cut_overlay(hair_overlay)
hair_overlay = null
if(facial_hair_overlay)
cut_overlay(facial_hair_overlay)
facial_hair_overlay = null
if(new_form)
icon_state = new_form
if(icon_state in GLOB.ghost_forms_with_directions_list)
ghostimage_default.icon_state = new_form + "_nodir" //if this icon has dirs, the default ghostimage must use its nodir version or clients with the preference set to default sprites only will see the dirs
else
ghostimage_default.icon_state = new_form
if(ghost_accs >= GHOST_ACCS_DIR && icon_state in GLOB.ghost_forms_with_directions_list) //if this icon has dirs AND the client wants to show them, we make sure we update the dir on movement
updatedir = 1
else
updatedir = 0 //stop updating the dir in case we want to show accessories with dirs on a ghost sprite without dirs
setDir(2 )//reset the dir to its default so the sprites all properly align up
if(ghost_accs == GHOST_ACCS_FULL && icon_state in GLOB.ghost_forms_with_accessories_list) //check if this form supports accessories and if the client wants to show them
var/datum/sprite_accessory/S
if(facial_hair_style)
S = GLOB.facial_hair_styles_list[facial_hair_style]
if(S)
facial_hair_overlay = mutable_appearance(S.icon, "[S.icon_state]", -HAIR_LAYER)
if(facial_hair_color)
facial_hair_overlay.color = "#" + facial_hair_color
facial_hair_overlay.alpha = 200
add_overlay(facial_hair_overlay)
if(hair_style)
S = GLOB.hair_styles_list[hair_style]
if(S)
hair_overlay = mutable_appearance(S.icon, "[S.icon_state]", -HAIR_LAYER)
if(hair_color)
hair_overlay.color = "#" + hair_color
hair_overlay.alpha = 200
add_overlay(hair_overlay)
/*
* Increase the brightness of a color by calculating the average distance between the R, G and B values,
* and maximum brightness, then adding 30% of that average to R, G and B.
*
* I'll make this proc global and move it to its own file in a future update. |- Ricotez
*/
/mob/proc/brighten_color(input_color)
var/r_val
var/b_val
var/g_val
var/color_format = lentext(input_color)
if(color_format == 3)
r_val = hex2num(copytext(input_color, 1, 2))*16
g_val = hex2num(copytext(input_color, 2, 3))*16
b_val = hex2num(copytext(input_color, 3, 0))*16
else if(color_format == 6)
r_val = hex2num(copytext(input_color, 1, 3))
g_val = hex2num(copytext(input_color, 3, 5))
b_val = hex2num(copytext(input_color, 5, 0))
else
return 0 //If the color format is not 3 or 6, you're using an unexpected way to represent a color.
r_val += (255 - r_val) * 0.4
if(r_val > 255)
r_val = 255
g_val += (255 - g_val) * 0.4
if(g_val > 255)
g_val = 255
b_val += (255 - b_val) * 0.4
if(b_val > 255)
b_val = 255
return num2hex(r_val, 2) + num2hex(g_val, 2) + num2hex(b_val, 2)
/*
Transfer_mind is there to check if mob is being deleted/not going to have a body.
Works together with spawning an observer, noted above.
*/
/mob/proc/ghostize(can_reenter_corpse = 1)
if(key)
if(!cmptext(copytext(key,1,2),"@")) // Skip aghosts.
stop_sound_channel(CHANNEL_HEARTBEAT) //Stop heartbeat sounds because You Are A Ghost Now
var/mob/dead/observer/ghost = new(src) // Transfer safety to observer spawning proc.
SStgui.on_transfer(src, ghost) // Transfer NanoUIs.
ghost.can_reenter_corpse = can_reenter_corpse
ghost.can_reenter_round = (can_reenter_corpse && !suiciding)
ghost.key = key
return ghost
/*
This is the proc mobs get to turn into a ghost. Forked from ghostize due to compatibility issues.
*/
/mob/living/verb/ghost()
set category = "OOC"
set name = "Ghost"
set desc = "Relinquish your life and enter the land of the dead."
// CITADEL EDIT
if(istype(loc, /obj/machinery/cryopod))
var/response = alert(src, "Are you -sure- you want to ghost?\n(You are alive. If you ghost whilst still alive you won't be able to re-enter this round! You can't change your mind so choose wisely!!)","Are you sure you want to ghost?","Ghost","Stay in body")
if(response != "Ghost")//darn copypaste
return
var/obj/machinery/cryopod/C = loc
C.despawn_occupant()
return
// END EDIT
if(stat != DEAD)
succumb()
if(stat == DEAD)
ghostize(1)
else
var/response = alert(src, "Are you -sure- you want to ghost?\n(You are alive. If you ghost whilst still alive you won't be able to re-enter this round! You can't change your mind so choose wisely!!)","Are you sure you want to ghost?","Ghost","Stay in body")
if(response != "Ghost")
return //didn't want to ghost after-all
ghostize(0) //0 parameter is so we can never re-enter our body, "Charlie, you can never come baaaack~" :3
suicide_log(TRUE)
/mob/camera/verb/ghost()
set category = "OOC"
set name = "Ghost"
set desc = "Relinquish your life and enter the land of the dead."
var/response = alert(src, "Are you -sure- you want to ghost?\n(You are alive. If you ghost whilst still alive you won't be able to re-enter this round! You can't change your mind so choose wisely!!)","Are you sure you want to ghost?","Ghost","Stay in body")
if(response != "Ghost")
return
ghostize(0)
/mob/dead/observer/Move(NewLoc, direct)
if(updatedir)
setDir(direct)//only update dir if we actually need it, so overlays won't spin on base sprites that don't have directions of their own
var/oldloc = loc
if(NewLoc)
forceMove(NewLoc)
update_parallax_contents()
else
forceMove(get_turf(src)) //Get out of closets and such as a ghost
if((direct & NORTH) && y < world.maxy)
y++
else if((direct & SOUTH) && y > 1)
y--
if((direct & EAST) && x < world.maxx)
x++
else if((direct & WEST) && x > 1)
x--
Moved(oldloc, direct)
/mob/dead/observer/verb/reenter_corpse()
set category = "Ghost"
set name = "Re-enter Corpse"
if(!client)
return
if(!mind || QDELETED(mind.current))
to_chat(src, "<span class='warning'>You have no body.</span>")
return
if(!can_reenter_corpse)
to_chat(src, "<span class='warning'>You cannot re-enter your body.</span>")
return
if(mind.current.key && copytext(mind.current.key,1,2)!="@") //makes sure we don't accidentally kick any clients
to_chat(usr, "<span class='warning'>Another consciousness is in your body...It is resisting you.</span>")
return
client.change_view(CONFIG_GET(string/default_view))
SStgui.on_transfer(src, mind.current) // Transfer NanoUIs.
mind.current.key = key
return 1
/mob/dead/observer/proc/notify_cloning(var/message, var/sound, var/atom/source, flashwindow = TRUE)
if(flashwindow)
window_flash(client)
if(message)
to_chat(src, "<span class='ghostalert'>[message]</span>")
if(source)
var/obj/screen/alert/A = throw_alert("[REF(source)]_notify_cloning", /obj/screen/alert/notify_cloning)
if(A)
if(client && client.prefs && client.prefs.UI_style)
A.icon = ui_style2icon(client.prefs.UI_style)
A.desc = message
var/old_layer = source.layer
var/old_plane = source.plane
source.layer = FLOAT_LAYER
source.plane = FLOAT_PLANE
A.add_overlay(source)
source.layer = old_layer
source.plane = old_plane
to_chat(src, "<span class='ghostalert'><a href=?src=[REF(src)];reenter=1>(Click to re-enter)</a></span>")
if(sound)
SEND_SOUND(src, sound(sound))
/mob/dead/observer/proc/dead_tele()
set category = "Ghost"
set name = "Teleport"
set desc= "Teleport to a location"
if(!isobserver(usr))
to_chat(usr, "Not when you're not dead!")
return
var/list/filtered = list()
for(var/V in GLOB.sortedAreas)
var/area/A = V
if(!A.hidden)
filtered += A
var/area/thearea = input("Area to jump to", "BOOYEA") as null|anything in filtered
if(!thearea)
return
var/list/L = list()
for(var/turf/T in get_area_turfs(thearea.type))
L+=T
if(!L || !L.len)
to_chat(usr, "No area available.")
return
usr.forceMove(pick(L))
update_parallax_contents()
/mob/dead/observer/proc/view_gas()
set category = "Ghost"
set name = "View Gases"
set desc= "View the atmospheric conditions in a location"
var/turf/loc = get_turf(src)
show_air_status_to(loc, usr)
/mob/dead/observer/verb/follow()
set category = "Ghost"
set name = "Orbit" // "Haunt"
set desc = "Follow and orbit a mob."
var/list/mobs = getpois(skip_mindless=1)
var/input = input("Please, select a mob!", "Haunt", null, null) as null|anything in mobs
var/mob/target = mobs[input]
ManualFollow(target)
// This is the ghost's follow verb with an argument
/mob/dead/observer/proc/ManualFollow(atom/movable/target)
if (!istype(target))
return
var/icon/I = icon(target.icon,target.icon_state,target.dir)
var/orbitsize = (I.Width()+I.Height())*0.5
orbitsize -= (orbitsize/world.icon_size)*(world.icon_size*0.25)
var/rot_seg
switch(ghost_orbit)
if(GHOST_ORBIT_TRIANGLE)
rot_seg = 3
if(GHOST_ORBIT_SQUARE)
rot_seg = 4
if(GHOST_ORBIT_PENTAGON)
rot_seg = 5
if(GHOST_ORBIT_HEXAGON)
rot_seg = 6
else //Circular
rot_seg = 36 //360/10 bby, smooth enough aproximation of a circle
orbit(target,orbitsize, FALSE, 20, rot_seg)
/mob/dead/observer/orbit()
setDir(2)//reset dir so the right directional sprites show up
return ..()
/mob/dead/observer/stop_orbit(datum/component/orbiter/orbits)
. = ..()
//restart our floating animation after orbit is done.
pixel_y = 0
animate(src, pixel_y = 2, time = 10, loop = -1)
/mob/dead/observer/verb/jumptomob() //Moves the ghost instead of just changing the ghosts's eye -Nodrak
set category = "Ghost"
set name = "Jump to Mob"
set desc = "Teleport to a mob"
if(isobserver(usr)) //Make sure they're an observer!
var/list/dest = list() //List of possible destinations (mobs)
var/target = null //Chosen target.
dest += getpois(mobs_only=1) //Fill list, prompt user with list
target = input("Please, select a player!", "Jump to Mob", null, null) as null|anything in dest
if (!target)//Make sure we actually have a target
return
else
var/mob/M = dest[target] //Destination mob
var/mob/A = src //Source mob
var/turf/T = get_turf(M) //Turf of the destination mob
if(T && isturf(T)) //Make sure the turf exists, then move the source to that destination.
A.forceMove(T)
A.update_parallax_contents()
else
to_chat(A, "This mob is not located in the game world.")
/mob/dead/observer/verb/change_view_range()
set category = "Ghost"
set name = "View Range"
set desc = "Change your view range."
var/max_view = client.prefs.unlock_content ? GHOST_MAX_VIEW_RANGE_MEMBER : GHOST_MAX_VIEW_RANGE_DEFAULT
if(client.view == CONFIG_GET(string/default_view))
var/list/views = list()
for(var/i in 7 to max_view)
views |= i
var/new_view = input("Choose your new view", "Modify view range", 7) as null|anything in views
if(new_view)
client.change_view(CLAMP(new_view, 7, max_view))
else
client.change_view(CONFIG_GET(string/default_view))
/mob/dead/observer/verb/add_view_range(input as num)
set name = "Add View Range"
set hidden = TRUE
var/max_view = client.prefs.unlock_content ? GHOST_MAX_VIEW_RANGE_MEMBER : GHOST_MAX_VIEW_RANGE_DEFAULT
if(input)
client.rescale_view(input, 15, (max_view*2)+1)
/mob/dead/observer/verb/boo()
set category = "Ghost"
set name = "Boo!"
set desc= "Scare your crew members because of boredom!"
if(bootime > world.time)
return
var/obj/machinery/light/L = locate(/obj/machinery/light) in view(1, src)
if(L)
L.flicker()
bootime = world.time + 600
return
//Maybe in the future we can add more <i>spooky</i> code here!
return
/mob/dead/observer/memory()
set hidden = 1
to_chat(src, "<span class='danger'>You are dead! You have no mind to store memory!</span>")
/mob/dead/observer/add_memory()
set hidden = 1
to_chat(src, "<span class='danger'>You are dead! You have no mind to store memory!</span>")
/mob/dead/observer/verb/toggle_ghostsee()
set name = "Toggle Ghost Vision"
set desc = "Toggles your ability to see things only ghosts can see, like other ghosts"
set category = "Ghost"
ghostvision = !(ghostvision)
update_sight()
to_chat(usr, "You [(ghostvision?"now":"no longer")] have ghost vision.")
/mob/dead/observer/verb/toggle_darkness()
set name = "Toggle Darkness"
set category = "Ghost"
switch(lighting_alpha)
if (LIGHTING_PLANE_ALPHA_VISIBLE)
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE
if (LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE)
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
if (LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE)
lighting_alpha = LIGHTING_PLANE_ALPHA_INVISIBLE
else
lighting_alpha = LIGHTING_PLANE_ALPHA_VISIBLE
update_sight()
/mob/dead/observer/update_sight()
if(client)
ghost_others = client.prefs.ghost_others //A quick update just in case this setting was changed right before calling the proc
if (!ghostvision)
see_invisible = SEE_INVISIBLE_LIVING
else
see_invisible = SEE_INVISIBLE_OBSERVER
updateghostimages()
..()
/proc/updateallghostimages()
listclearnulls(GLOB.ghost_images_default)
listclearnulls(GLOB.ghost_images_simple)
for (var/mob/dead/observer/O in GLOB.player_list)
O.updateghostimages()
/mob/dead/observer/proc/updateghostimages()
if (!client)
return
if(lastsetting)
switch(lastsetting) //checks the setting we last came from, for a little efficiency so we don't try to delete images from the client that it doesn't have anyway
if(GHOST_OTHERS_DEFAULT_SPRITE)
client.images -= GLOB.ghost_images_default
if(GHOST_OTHERS_SIMPLE)
client.images -= GLOB.ghost_images_simple
lastsetting = client.prefs.ghost_others
if(!ghostvision)
return
if(client.prefs.ghost_others != GHOST_OTHERS_THEIR_SETTING)
switch(client.prefs.ghost_others)
if(GHOST_OTHERS_DEFAULT_SPRITE)
client.images |= (GLOB.ghost_images_default-ghostimage_default)
if(GHOST_OTHERS_SIMPLE)
client.images |= (GLOB.ghost_images_simple-ghostimage_simple)
/mob/dead/observer/verb/possess()
set category = "Ghost"
set name = "Possess!"
set desc= "Take over the body of a mindless creature!"
var/list/possessible = list()
for(var/mob/living/L in GLOB.alive_mob_list)
if(istype(L,/mob/living/carbon/human/dummy) || !get_turf(L)) //Haha no.
continue
if(!(L in GLOB.player_list) && !L.mind)
possessible += L
var/mob/living/target = input("Your new life begins today!", "Possess Mob", null, null) as null|anything in possessible
if(!target)
return 0
if(ismegafauna(target))
to_chat(src, "<span class='warning'>This creature is too powerful for you to possess!</span>")
return 0
if(istype (target, /mob/living/simple_animal/hostile/spawner))
to_chat(src, "<span class='warning'>This isn't really a creature, now is it!</span>")
return 0
if(!can_reenter_round)
to_chat(src, "<span class='warning'>You are unable to re-enter the round.</span>")
return FALSE
if(can_reenter_corpse && mind && mind.current)
if(alert(src, "Your soul is still tied to your former life as [mind.current.name], if you go forward there is no going back to that life. Are you sure you wish to continue?", "Move On", "Yes", "No") == "No")
return 0
if(target.key)
to_chat(src, "<span class='warning'>Someone has taken this body while you were choosing!</span>")
return 0
target.key = key
target.faction = list("neutral")
return 1
//this is a mob verb instead of atom for performance reasons
//see /mob/verb/examinate() in mob.dm for more info
//overridden here and in /mob/living for different point span classes and sanity checks
/mob/dead/observer/pointed(atom/A as mob|obj|turf in view())
if(!..())
return 0
usr.visible_message("<span class='deadsay'><b>[src]</b> points to [A].</span>")
return 1
/mob/dead/observer/verb/view_manifest()
set name = "View Crew Manifest"
set category = "Ghost"
var/dat
dat += "<h4>Crew Manifest</h4>"
dat += GLOB.data_core.get_manifest()
src << browse(dat, "window=manifest;size=387x420;can_close=1")
//this is called when a ghost is drag clicked to something.
/mob/dead/observer/MouseDrop(atom/over)
if(!usr || !over)
return
if (isobserver(usr) && usr.client.holder && (isliving(over) || iscameramob(over)) )
if (usr.client.holder.cmd_ghost_drag(src,over))
return
return ..()
/mob/dead/observer/Topic(href, href_list)
..()
if(usr == src)
if(href_list["follow"])
var/atom/movable/target = locate(href_list["follow"])
if(istype(target) && (target != src))
ManualFollow(target)
return
if(href_list["x"] && href_list["y"] && href_list["z"])
var/tx = text2num(href_list["x"])
var/ty = text2num(href_list["y"])
var/tz = text2num(href_list["z"])
var/turf/target = locate(tx, ty, tz)
if(istype(target))
forceMove(target)
return
if(href_list["reenter"])
reenter_corpse()
return
//We don't want to update the current var
//But we will still carry a mind.
/mob/dead/observer/mind_initialize()
return
/mob/dead/observer/proc/show_data_huds()
for(var/hudtype in datahuds)
var/datum/atom_hud/H = GLOB.huds[hudtype]
H.add_hud_to(src)
/mob/dead/observer/proc/remove_data_huds()
for(var/hudtype in datahuds)
var/datum/atom_hud/H = GLOB.huds[hudtype]
H.remove_hud_from(src)
/mob/dead/observer/verb/toggle_data_huds()
set name = "Toggle Sec/Med/Diag HUD"
set desc = "Toggles whether you see medical/security/diagnostic HUDs"
set category = "Ghost"
if(data_huds_on) //remove old huds
remove_data_huds()
to_chat(src, "<span class='notice'>Data HUDs disabled.</span>")
data_huds_on = 0
else
show_data_huds()
to_chat(src, "<span class='notice'>Data HUDs enabled.</span>")
data_huds_on = 1
/mob/dead/observer/verb/toggle_health_scan()
set name = "Toggle Health Scan"
set desc = "Toggles whether you health-scan living beings on click"
set category = "Ghost"
if(health_scan) //remove old huds
to_chat(src, "<span class='notice'>Health scan disabled.</span>")
health_scan = FALSE
else
to_chat(src, "<span class='notice'>Health scan enabled.</span>")
health_scan = TRUE
/mob/dead/observer/verb/restore_ghost_appearance()
set name = "Restore Ghost Character"
set desc = "Sets your deadchat name and ghost appearance to your \
roundstart character."
set category = "Ghost"
set_ghost_appearance()
if(client && client.prefs)
deadchat_name = client.prefs.real_name
/mob/dead/observer/proc/set_ghost_appearance()
if((!client) || (!client.prefs))
return
if(client.prefs.be_random_name)
client.prefs.real_name = random_unique_name(gender)
if(client.prefs.be_random_body)
client.prefs.random_character(gender)
if(HAIR in client.prefs.pref_species.species_traits)
hair_style = client.prefs.hair_style
hair_color = brighten_color(client.prefs.hair_color)
if(FACEHAIR in client.prefs.pref_species.species_traits)
facial_hair_style = client.prefs.facial_hair_style
facial_hair_color = brighten_color(client.prefs.facial_hair_color)
update_icon()
/mob/dead/observer/canUseTopic(atom/movable/M, be_close=FALSE, no_dextery=FALSE, no_tk=FALSE)
return IsAdminGhost(usr)
/mob/dead/observer/is_literate()
return 1
/mob/dead/observer/vv_edit_var(var_name, var_value)
. = ..()
switch(var_name)
if("icon")
ghostimage_default.icon = icon
ghostimage_simple.icon = icon
if("icon_state")
ghostimage_default.icon_state = icon_state
ghostimage_simple.icon_state = icon_state
if("fun_verbs")
if(fun_verbs)
verbs += /mob/dead/observer/verb/boo
verbs += /mob/dead/observer/verb/possess
else
verbs -= /mob/dead/observer/verb/boo
verbs -= /mob/dead/observer/verb/possess
/mob/dead/observer/reset_perspective(atom/A)
if(client)
if(ismob(client.eye) && (client.eye != src))
var/mob/target = client.eye
observetarget = null
if(target.observers)
target.observers -= src
UNSETEMPTY(target.observers)
if(..())
if(hud_used)
client.screen = list()
hud_used.show_hud(hud_used.hud_version)
/mob/dead/observer/verb/observe()
set name = "Observe"
set category = "OOC"
var/list/creatures = getpois()
reset_perspective(null)
var/eye_name = null
eye_name = input("Please, select a player!", "Observe", null, null) as null|anything in creatures
if (!eye_name)
return
var/mob/mob_eye = creatures[eye_name]
//Istype so we filter out points of interest that are not mobs
if(client && mob_eye && istype(mob_eye))
client.eye = mob_eye
if(mob_eye.hud_used)
client.screen = list()
LAZYINITLIST(mob_eye.observers)
mob_eye.observers |= src
mob_eye.hud_used.show_hud(mob_eye.hud_used.hud_version, src)
observetarget = mob_eye
/mob/dead/observer/verb/register_pai_candidate()
set category = "Ghost"
set name = "pAI Setup"
set desc = "Upload a fragment of your personality to the global pAI databanks"
register_pai()
/mob/dead/observer/proc/register_pai()
if(isobserver(src))
SSpai.recruitWindow(src)
else
to_chat(usr, "Can't become a pAI candidate while not dead!")
/mob/dead/observer/CtrlShiftClick(mob/user)
if(isobserver(user) && check_rights(R_SPAWN))
change_mob_type( /mob/living/carbon/human , null, null, TRUE) //always delmob, ghosts shouldn't be left lingering
/mob/dead/observer/examine(mob/user)
..()
if(!invisibility)
to_chat(user, "It seems extremely obvious.")
/mob/dead/observer/proc/set_invisibility(value)
invisibility = value
if(!value)
set_light(1, 2)
else
set_light(0, 0)
// Ghosts have no momentum, being massless ectoplasm
/mob/dead/observer/Process_Spacemove(movement_dir)
return 1
/mob/dead/observer/vv_edit_var(var_name, var_value)
. = ..()
if(var_name == "invisibility")
set_invisibility(invisibility) // updates light
/proc/set_observer_default_invisibility(amount, message=null)
for(var/mob/dead/observer/G in GLOB.player_list)
G.set_invisibility(amount)
if(message)
to_chat(G, message)
GLOB.observer_default_invisibility = amount
/mob/dead/observer/proc/open_spawners_menu()
set name = "Spawners Menu"
set desc = "See all currently available spawners"
set category = "Ghost"
if(!spawners_menu)
spawners_menu = new(src)
spawners_menu.ui_interact(src)
+40
View File
@@ -0,0 +1,40 @@
/mob/dead/observer/say(message, bubble_type, var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN))
if (!message)
return
var/message_mode = get_message_mode(message)
if(client && (message_mode == MODE_ADMIN || message_mode == MODE_DEADMIN))
message = copytext(message, 3)
if(findtext(message, " ", 1, 2))
message = copytext(message, 2)
if(message_mode == MODE_ADMIN)
client.cmd_admin_say(message)
else if(message_mode == MODE_DEADMIN)
client.dsay(message)
return
src.log_talk(message, LOG_SAY, tag="ghost")
if(check_emote(message))
return
. = say_dead(message)
/mob/dead/observer/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode)
. = ..()
var/atom/movable/to_follow = speaker
if(radio_freq)
var/atom/movable/virtualspeaker/V = speaker
if(isAI(V.source))
var/mob/living/silicon/ai/S = V.source
to_follow = S.eyeobj
else
to_follow = V.source
var/link = FOLLOW_LINK(src, to_follow)
// Recompose the message, because it's scrambled by default
message = compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode)
to_chat(src, "[link] [message]")
+14
View File
@@ -0,0 +1,14 @@
//This is the proc for gibbing a mob. Cannot gib ghosts.
//added different sort of gibs and animations. N
/mob/proc/gib()
return
//This is the proc for turning a mob into ash. Mostly a copy of gib code (above).
//Originally created for wizard disintegrate. I've removed the virus code since it's irrelevant here.
//Dusting robots does not eject the MMI, so it's a bit more powerful than gib() /N
/mob/proc/dust(just_ash, drop_items, force)
return
/mob/proc/death(gibbed)
SEND_SIGNAL(src, COMSIG_MOB_DEATH, gibbed)
return
+48
View File
@@ -0,0 +1,48 @@
//The code execution of the emote datum is located at code/datums/emotes.dm
/mob/proc/emote(act, m_type = null, message = null, intentional = FALSE)
act = lowertext(act)
var/param = message
var/custom_param = findchar(act, " ")
if(custom_param)
param = copytext(act, custom_param + 1, length(act) + 1)
act = copytext(act, 1, custom_param)
var/datum/emote/E
E = E.emote_list[act]
if(!E)
to_chat(src, "<span class='notice'>Unusable emote '[act]'. Say *help for a list.</span>")
return
E.run_emote(src, param, m_type, intentional)
/datum/emote/flip
key = "flip"
key_third_person = "flips"
restraint_check = TRUE
mob_type_allowed_typecache = list(/mob/living, /mob/dead/observer)
mob_type_ignore_stat_typecache = list(/mob/dead/observer)
/datum/emote/flip/run_emote(mob/user, params)
. = ..()
if(.)
user.SpinAnimation(7,1)
/datum/emote/spin
key = "spin"
key_third_person = "spins"
restraint_check = TRUE
mob_type_allowed_typecache = list(/mob/living, /mob/dead/observer)
mob_type_ignore_stat_typecache = list(/mob/dead/observer)
/datum/emote/spin/run_emote(mob/user)
. = ..()
if(.)
user.spin(20, 1)
if(iscyborg(user) && user.has_buckled_mobs())
var/mob/living/silicon/robot/R = user
GET_COMPONENT_FROM(riding_datum, /datum/component/riding, R)
if(riding_datum)
for(var/mob/M in R.buckled_mobs)
riding_datum.force_dismount(M)
else
R.unbuckle_all_mobs()
+470
View File
@@ -0,0 +1,470 @@
//These procs handle putting s tuff in your hands
//as they handle all relevant stuff like adding it to the player's screen and updating their overlays.
//Returns the thing we're currently holding
/mob/proc/get_active_held_item()
return get_item_for_held_index(active_hand_index)
//Finds the opposite limb for the active one (eg: upper left arm will find the item in upper right arm)
//So we're treating each "pair" of limbs as a team, so "both" refers to them
/mob/proc/get_inactive_held_item()
return get_item_for_held_index(get_inactive_hand_index())
//Finds the opposite index for the active one (eg: upper left arm will find the item in upper right arm)
//So we're treating each "pair" of limbs as a team, so "both" refers to them
/mob/proc/get_inactive_hand_index()
var/other_hand = 0
if(!(active_hand_index % 2))
other_hand = active_hand_index-1 //finding the matching "left" limb
else
other_hand = active_hand_index+1 //finding the matching "right" limb
if(other_hand < 0 || other_hand > held_items.len)
other_hand = 0
return other_hand
/mob/proc/get_item_for_held_index(i)
if(i > 0 && i <= held_items.len)
return held_items[i]
return FALSE
//Odd = left. Even = right
/mob/proc/held_index_to_dir(i)
if(!(i % 2))
return "r"
return "l"
//Check we have an organ for this hand slot (Dismemberment), Only relevant for humans
/mob/proc/has_hand_for_held_index(i)
return TRUE
//Check we have an organ for our active hand slot (Dismemberment),Only relevant for humans
/mob/proc/has_active_hand()
return has_hand_for_held_index(active_hand_index)
//Finds the first available (null) index OR all available (null) indexes in held_items based on a side.
//Lefts: 1, 3, 5, 7...
//Rights:2, 4, 6, 8...
/mob/proc/get_empty_held_index_for_side(side = "left", all = FALSE)
var/start = 0
var/static/list/lefts = list("l" = TRUE,"L" = TRUE,"LEFT" = TRUE,"left" = TRUE)
var/static/list/rights = list("r" = TRUE,"R" = TRUE,"RIGHT" = TRUE,"right" = TRUE) //"to remain silent"
if(lefts[side])
start = 1
else if(rights[side])
start = 2
if(!start)
return FALSE
var/list/empty_indexes
for(var/i in start to held_items.len step 2)
if(!held_items[i])
if(!all)
return i
if(!empty_indexes)
empty_indexes = list()
empty_indexes += i
return empty_indexes
//Same as the above, but returns the first or ALL held *ITEMS* for the side
/mob/proc/get_held_items_for_side(side = "left", all = FALSE)
var/start = 0
var/static/list/lefts = list("l" = TRUE,"L" = TRUE,"LEFT" = TRUE,"left" = TRUE)
var/static/list/rights = list("r" = TRUE,"R" = TRUE,"RIGHT" = TRUE,"right" = TRUE) //"to remain silent"
if(lefts[side])
start = 1
else if(rights[side])
start = 2
if(!start)
return FALSE
var/list/holding_items
for(var/i in start to held_items.len step 2)
var/obj/item/I = held_items[i]
if(I)
if(!all)
return I
if(!holding_items)
holding_items = list()
holding_items += I
return holding_items
/mob/proc/get_empty_held_indexes()
var/list/L
for(var/i in 1 to held_items.len)
if(!held_items[i])
if(!L)
L = list()
L += i
return L
/mob/proc/get_held_index_of_item(obj/item/I)
return held_items.Find(I)
//Sad that this will cause some overhead, but the alias seems necessary
//*I* may be happy with a million and one references to "indexes" but others won't be
/mob/proc/is_holding(obj/item/I)
return get_held_index_of_item(I)
//Checks if we're holding an item of type: typepath
/mob/proc/is_holding_item_of_type(typepath)
for(var/obj/item/I in held_items)
if(istype(I, typepath))
return I
return FALSE
//Checks if we're holding a tool that has given quality
//Returns the tool that has the best version of this quality
/mob/proc/is_holding_tool_quality(quality)
var/obj/item/best_item
var/best_quality = INFINITY
for(var/obj/item/I in held_items)
if(I.tool_behaviour == quality && I.toolspeed < best_quality)
best_item = I
best_quality = I.toolspeed
return best_item
//To appropriately fluff things like "they are holding [I] in their [get_held_index_name(get_held_index_of_item(I))]"
//Can be overridden to pass off the fluff to something else (eg: science allowing people to add extra robotic limbs, and having this proc react to that
// with say "they are holding [I] in their Nanotrasen Brand Utility Arm - Right Edition" or w/e
/mob/proc/get_held_index_name(i)
var/list/hand = list()
if(i > 2)
hand += "upper "
var/num = 0
if(!(i % 2))
num = i-2
hand += "right hand"
else
num = i-1
hand += "left hand"
num -= (num*0.5)
if(num > 1) //"upper left hand #1" seems weird, but "upper left hand #2" is A-ok
hand += " #[num]"
return hand.Join()
//Returns if a certain item can be equipped to a certain slot.
// Currently invalid for two-handed items - call obj/item/mob_can_equip() instead.
/mob/proc/can_equip(obj/item/I, slot, disable_warning = 0)
return FALSE
/mob/proc/can_put_in_hand(I, hand_index)
if(hand_index > held_items.len)
return FALSE
if(!put_in_hand_check(I))
return FALSE
if(!has_hand_for_held_index(hand_index))
return FALSE
return !held_items[hand_index]
/mob/proc/put_in_hand(obj/item/I, hand_index, forced = FALSE, ignore_anim = TRUE)
if(forced || can_put_in_hand(I, hand_index))
if(isturf(I.loc) && !ignore_anim)
I.do_pickup_animation(src)
if(hand_index == null)
return FALSE
if(get_item_for_held_index(hand_index) != null)
dropItemToGround(get_item_for_held_index(hand_index), force = TRUE)
I.forceMove(src)
held_items[hand_index] = I
I.layer = ABOVE_HUD_LAYER
I.plane = ABOVE_HUD_PLANE
I.equipped(src, SLOT_HANDS)
if(I.pulledby)
I.pulledby.stop_pulling()
update_inv_hands()
I.pixel_x = initial(I.pixel_x)
I.pixel_y = initial(I.pixel_y)
I.transform = initial(I.transform)
return hand_index || TRUE
return FALSE
//Puts the item into the first available left hand if possible and calls all necessary triggers/updates. returns 1 on success.
/mob/proc/put_in_l_hand(obj/item/I)
return put_in_hand(I, get_empty_held_index_for_side("l"))
//Puts the item into the first available right hand if possible and calls all necessary triggers/updates. returns 1 on success.
/mob/proc/put_in_r_hand(obj/item/I)
return put_in_hand(I, get_empty_held_index_for_side("r"))
/mob/proc/put_in_hand_check(obj/item/I)
if(incapacitated() && !(I.item_flags&ABSTRACT)) //Cit change - Changes lying to incapacitated so that it's plausible to pick things up while on the ground
return FALSE
if(!istype(I))
return FALSE
return TRUE
//Puts the item into our active hand if possible. returns TRUE on success.
/mob/proc/put_in_active_hand(obj/item/I, forced = FALSE, ignore_animation = TRUE)
return put_in_hand(I, active_hand_index, forced, ignore_animation)
//Puts the item into our inactive hand if possible, returns TRUE on success
/mob/proc/put_in_inactive_hand(obj/item/I)
return put_in_hand(I, get_inactive_hand_index())
//Puts the item our active hand if possible. Failing that it tries other hands. Returns TRUE on success.
//If both fail it drops it on the floor and returns FALSE.
//This is probably the main one you need to know :)
/mob/proc/put_in_hands(obj/item/I, del_on_fail = FALSE, merge_stacks = TRUE, forced = FALSE)
if(!I)
return FALSE
// If the item is a stack and we're already holding a stack then merge
if (istype(I, /obj/item/stack))
var/obj/item/stack/I_stack = I
var/obj/item/stack/active_stack = get_active_held_item()
if (I_stack.zero_amount())
return FALSE
if (merge_stacks)
if (istype(active_stack) && istype(I_stack, active_stack.merge_type))
if (I_stack.merge(active_stack))
to_chat(usr, "<span class='notice'>Your [active_stack.name] stack now contains [active_stack.get_amount()] [active_stack.singular_name]\s.</span>")
return TRUE
else
var/obj/item/stack/inactive_stack = get_inactive_held_item()
if (istype(inactive_stack) && istype(I_stack, inactive_stack.merge_type))
if (I_stack.merge(inactive_stack))
to_chat(usr, "<span class='notice'>Your [inactive_stack.name] stack now contains [inactive_stack.get_amount()] [inactive_stack.singular_name]\s.</span>")
return TRUE
if(put_in_active_hand(I, forced))
return TRUE
var/hand = get_empty_held_index_for_side("l")
if(!hand)
hand = get_empty_held_index_for_side("r")
if(hand)
if(put_in_hand(I, hand, forced))
return TRUE
if(del_on_fail)
qdel(I)
return FALSE
I.forceMove(drop_location())
I.layer = initial(I.layer)
I.plane = initial(I.plane)
I.dropped(src)
return FALSE
/mob/proc/drop_all_held_items()
. = FALSE
for(var/obj/item/I in held_items)
. |= dropItemToGround(I)
//Here lie drop_from_inventory and before_item_take, already forgotten and not missed.
/mob/proc/canUnEquip(obj/item/I, force)
if(!I)
return TRUE
if(HAS_TRAIT(I, TRAIT_NODROP) && !force)
return FALSE
return TRUE
/mob/proc/putItemFromInventoryInHandIfPossible(obj/item/I, hand_index, force_removal = FALSE)
if(!can_put_in_hand(I, hand_index))
return FALSE
if(!temporarilyRemoveItemFromInventory(I, force_removal))
return FALSE
I.remove_item_from_storage(src)
if(!put_in_hand(I, hand_index))
qdel(I)
CRASH("Assertion failure: putItemFromInventoryInHandIfPossible") //should never be possible
return TRUE
//The following functions are the same save for one small difference
//for when you want the item to end up on the ground
//will force move the item to the ground and call the turf's Entered
/mob/proc/dropItemToGround(obj/item/I, force = FALSE)
return doUnEquip(I, force, drop_location(), FALSE)
//for when the item will be immediately placed in a loc other than the ground
/mob/proc/transferItemToLoc(obj/item/I, newloc = null, force = FALSE)
return doUnEquip(I, force, newloc, FALSE)
//visibly unequips I but it is NOT MOVED AND REMAINS IN SRC
//item MUST BE FORCEMOVE'D OR QDEL'D
/mob/proc/temporarilyRemoveItemFromInventory(obj/item/I, force = FALSE, idrop = TRUE)
return doUnEquip(I, force, null, TRUE, idrop)
//DO NOT CALL THIS PROC
//use one of the above 3 helper procs
//you may override it, but do not modify the args
/mob/proc/doUnEquip(obj/item/I, force, newloc, no_move, invdrop = TRUE) //Force overrides TRAIT_NODROP for things like wizarditis and admin undress.
//Use no_move if the item is just gonna be immediately moved afterward
//Invdrop is used to prevent stuff in pockets dropping. only set to false if it's going to immediately be replaced
if(!I) //If there's nothing to drop, the drop is automatically succesfull. If(unEquip) should generally be used to check for TRAIT_NODROP.
return TRUE
if(HAS_TRAIT(I, TRAIT_NODROP) && !force)
return FALSE
var/hand_index = get_held_index_of_item(I)
if(hand_index)
held_items[hand_index] = null
update_inv_hands()
if(I)
if(client)
client.screen -= I
I.layer = initial(I.layer)
I.plane = initial(I.plane)
I.appearance_flags &= ~NO_CLIENT_COLOR
if(!no_move && !(I.item_flags & DROPDEL)) //item may be moved/qdel'd immedietely, don't bother moving it
if (isnull(newloc))
I.moveToNullspace()
else
I.forceMove(newloc)
I.dropped(src)
return TRUE
//Outdated but still in use apparently. This should at least be a human proc.
//Daily reminder to murder this - Remie.
/mob/living/proc/get_equipped_items(include_pockets = FALSE)
return
/mob/living/carbon/get_equipped_items(include_pockets = FALSE)
var/list/items = list()
if(back)
items += back
if(head)
items += head
if(wear_mask)
items += wear_mask
if(wear_neck)
items += wear_neck
return items
/mob/living/carbon/human/get_equipped_items(include_pockets = FALSE)
var/list/items = ..()
if(belt)
items += belt
if(ears)
items += ears
if(glasses)
items += glasses
if(gloves)
items += gloves
if(shoes)
items += shoes
if(wear_id)
items += wear_id
if(wear_suit)
items += wear_suit
if(w_uniform)
items += w_uniform
if(include_pockets)
if(l_store)
items += l_store
if(r_store)
items += r_store
if(s_store)
items += s_store
return items
/mob/living/proc/unequip_everything()
var/list/items = list()
items |= get_equipped_items(TRUE)
for(var/I in items)
dropItemToGround(I)
drop_all_held_items()
/obj/item/proc/equip_to_best_slot(mob/M)
if(src != M.get_active_held_item())
to_chat(M, "<span class='warning'>You are not holding anything to equip!</span>")
return FALSE
if(M.equip_to_appropriate_slot(src))
M.update_inv_hands()
return TRUE
else
if(equip_delay_self)
return
if(M.active_storage && M.active_storage.parent && SEND_SIGNAL(M.active_storage.parent, COMSIG_TRY_STORAGE_INSERT, src,M))
return TRUE
var/list/obj/item/possible = list(M.get_inactive_held_item(), M.get_item_by_slot(SLOT_BELT), M.get_item_by_slot(SLOT_GENERC_DEXTROUS_STORAGE), M.get_item_by_slot(SLOT_BACK))
for(var/i in possible)
if(!i)
continue
var/obj/item/I = i
if(SEND_SIGNAL(I, COMSIG_TRY_STORAGE_INSERT, src, M))
return TRUE
to_chat(M, "<span class='warning'>You are unable to equip that!</span>")
return FALSE
/mob/verb/quick_equip()
set name = "quick-equip"
set hidden = 1
var/obj/item/I = get_active_held_item()
if (I)
I.equip_to_best_slot(src)
//used in code for items usable by both carbon and drones, this gives the proper back slot for each mob.(defibrillator, backpack watertank, ...)
/mob/proc/getBackSlot()
return SLOT_BACK
/mob/proc/getBeltSlot()
return SLOT_BELT
//Inventory.dm is -kind of- an ok place for this I guess
//This is NOT for dismemberment, as the user still technically has 2 "hands"
//This is for multi-handed mobs, such as a human with a third limb installed
//This is a very rare proc to call (besides admin fuckery) so
//any cost it has isn't a worry
/mob/proc/change_number_of_hands(amt)
if(amt < held_items.len)
for(var/i in held_items.len to amt step -1)
dropItemToGround(held_items[i])
held_items.len = amt
if(hud_used)
hud_used.build_hand_slots()
/mob/living/carbon/human/change_number_of_hands(amt)
var/old_limbs = held_items.len
if(amt < old_limbs)
for(var/i in hand_bodyparts.len to amt step -1)
var/obj/item/bodypart/BP = hand_bodyparts[i]
BP.dismember()
hand_bodyparts[i] = null
hand_bodyparts.len = amt
else if(amt > old_limbs)
hand_bodyparts.len = amt
for(var/i in old_limbs+1 to amt)
var/path = /obj/item/bodypart/l_arm
if(!(i % 2))
path = /obj/item/bodypart/r_arm
var/obj/item/bodypart/BP = new path ()
BP.owner = src
BP.held_index = i
bodyparts += BP
hand_bodyparts[i] = BP
..() //Don't redraw hands until we have organs for them
+340
View File
@@ -0,0 +1,340 @@
/****************************************************
BLOOD SYSTEM
****************************************************/
/mob/living/carbon/human/proc/suppress_bloodloss(amount)
if(bleedsuppress)
return
else
bleedsuppress = TRUE
addtimer(CALLBACK(src, .proc/resume_bleeding), amount)
/mob/living/carbon/human/proc/resume_bleeding()
bleedsuppress = 0
if(stat != DEAD && bleed_rate)
to_chat(src, "<span class='warning'>The blood soaks through your bandage.</span>")
/mob/living/carbon/monkey/handle_blood()
if(bodytemperature >= TCRYO && !(HAS_TRAIT(src, TRAIT_NOCLONE))) //cryosleep or husked people do not pump the blood.
//Blood regeneration if there is some space
if(blood_volume < (BLOOD_VOLUME_NORMAL * blood_ratio))
blood_volume += 0.1 // regenerate blood VERY slowly
if(blood_volume < (BLOOD_VOLUME_OKAY * blood_ratio))
adjustOxyLoss(round(((BLOOD_VOLUME_NORMAL * blood_ratio) - blood_volume) * 0.02, 1))
// Takes care blood loss and regeneration
/mob/living/carbon/human/handle_blood()
if(NOBLOOD in dna.species.species_traits)
bleed_rate = 0
return
if(bodytemperature >= TCRYO && !(HAS_TRAIT(src, TRAIT_NOCLONE))) //cryosleep or husked people do not pump the blood.
//Blood regeneration if there is some space
if(blood_volume < (BLOOD_VOLUME_NORMAL * blood_ratio) && !HAS_TRAIT(src, TRAIT_NOHUNGER))
var/nutrition_ratio = 0
switch(nutrition)
if(0 to NUTRITION_LEVEL_STARVING)
nutrition_ratio = 0.2
if(NUTRITION_LEVEL_STARVING to NUTRITION_LEVEL_HUNGRY)
nutrition_ratio = 0.4
if(NUTRITION_LEVEL_HUNGRY to NUTRITION_LEVEL_FED)
nutrition_ratio = 0.6
if(NUTRITION_LEVEL_FED to NUTRITION_LEVEL_WELL_FED)
nutrition_ratio = 0.8
else
nutrition_ratio = 1
if(HAS_TRAIT(src, TRAIT_HIGH_BLOOD))
nutrition_ratio *= 1.2
if(satiety > 80)
nutrition_ratio *= 1.25
nutrition = max(0, nutrition - nutrition_ratio * HUNGER_FACTOR)
blood_volume = min((BLOOD_VOLUME_NORMAL * blood_ratio), blood_volume + 0.5 * nutrition_ratio)
//Effects of bloodloss
var/word = pick("dizzy","woozy","faint")
switch(blood_volume * INVERSE(blood_ratio))
if(BLOOD_VOLUME_OKAY to BLOOD_VOLUME_SAFE)
if(prob(5))
to_chat(src, "<span class='warning'>You feel [word].</span>")
adjustOxyLoss(round(((BLOOD_VOLUME_NORMAL * blood_ratio) - blood_volume) * 0.01, 1))
if(BLOOD_VOLUME_BAD to BLOOD_VOLUME_OKAY)
adjustOxyLoss(round(((BLOOD_VOLUME_NORMAL * blood_ratio) - blood_volume) * 0.02, 1))
if(prob(5))
blur_eyes(6)
to_chat(src, "<span class='warning'>You feel very [word].</span>")
if(BLOOD_VOLUME_SURVIVE to BLOOD_VOLUME_BAD)
adjustOxyLoss(5)
if(prob(15))
Unconscious(rand(20,60))
to_chat(src, "<span class='warning'>You feel extremely [word].</span>")
if(-INFINITY to BLOOD_VOLUME_SURVIVE)
if(!HAS_TRAIT(src, TRAIT_NODEATH))
death()
var/temp_bleed = 0
//Bleeding out
for(var/X in bodyparts)
var/obj/item/bodypart/BP = X
var/brutedamage = BP.brute_dam
//We want an accurate reading of .len
listclearnulls(BP.embedded_objects)
temp_bleed += 0.5*BP.embedded_objects.len
if(brutedamage >= 20)
temp_bleed += (brutedamage * 0.013)
bleed_rate = max(bleed_rate - 0.5, temp_bleed)//if no wounds, other bleed effects (heparin) naturally decreases
if(bleed_rate && !bleedsuppress && !(HAS_TRAIT(src, TRAIT_FAKEDEATH)))
bleed(bleed_rate)
//Makes a blood drop, leaking amt units of blood from the mob
/mob/living/carbon/proc/bleed(amt)
if(blood_volume)
blood_volume = max(blood_volume - amt, 0)
if(isturf(src.loc)) //Blood loss still happens in locker, floor stays clean
if(amt >= 10)
add_splatter_floor(src.loc)
else
add_splatter_floor(src.loc, 1)
/mob/living/carbon/human/bleed(amt)
amt *= physiology.bleed_mod
if(!(NOBLOOD in dna.species.species_traits))
..()
/mob/living/proc/restore_blood()
blood_volume = initial(blood_volume)
/mob/living/carbon/human/restore_blood()
blood_volume = (BLOOD_VOLUME_NORMAL * blood_ratio)
bleed_rate = 0
/****************************************************
BLOOD TRANSFERS
****************************************************/
//Gets blood from mob to a container or other mob, preserving all data in it.
/mob/living/proc/transfer_blood_to(atom/movable/AM, amount, forced)
if(!blood_volume || !AM.reagents)
return 0
if(blood_volume < (BLOOD_VOLUME_BAD * blood_ratio) && !forced)
return 0
if(blood_volume < amount)
amount = blood_volume
var/blood_id = get_blood_id()
if(!blood_id)
return 0
blood_volume -= amount
var/list/blood_data = get_blood_data(blood_id)
if(iscarbon(AM))
var/mob/living/carbon/C = AM
if(blood_id == C.get_blood_id())//both mobs have the same blood substance
if(blood_id == "blood") //normal blood
if(blood_data["viruses"])
for(var/thing in blood_data["viruses"])
var/datum/disease/D = thing
if((D.spread_flags & DISEASE_SPREAD_SPECIAL) || (D.spread_flags & DISEASE_SPREAD_NON_CONTAGIOUS))
continue
C.ForceContractDisease(D)
if(!(blood_data["blood_type"] in get_safe_blood(C.dna.blood_type)))
C.reagents.add_reagent("toxin", amount * 0.5)
return 1
C.blood_volume = min(C.blood_volume + round(amount, 0.1), BLOOD_VOLUME_MAXIMUM)
return 1
AM.reagents.add_reagent(blood_id, amount, blood_data, bodytemperature)
return 1
/mob/living/proc/get_blood_data(blood_id)
return
/mob/living/carbon/get_blood_data(blood_id)
if(blood_id == "blood") //actual blood reagent
var/blood_data = list()
//set the blood data
blood_data["donor"] = src
blood_data["viruses"] = list()
for(var/thing in diseases)
var/datum/disease/D = thing
blood_data["viruses"] += D.Copy()
blood_data["blood_DNA"] = copytext(dna.unique_enzymes,1,0)
if(disease_resistances && disease_resistances.len)
blood_data["resistances"] = disease_resistances.Copy()
var/list/temp_chem = list()
for(var/datum/reagent/R in reagents.reagent_list)
temp_chem[R.id] = R.volume
blood_data["trace_chem"] = list2params(temp_chem)
if(mind)
blood_data["mind"] = mind
else if(last_mind)
blood_data["mind"] = last_mind
if(ckey)
blood_data["ckey"] = ckey
else if(last_mind)
blood_data["ckey"] = ckey(last_mind.key)
if(!suiciding)
blood_data["cloneable"] = 1
blood_data["blood_type"] = copytext(dna.blood_type,1,0)
blood_data["gender"] = gender
blood_data["real_name"] = real_name
blood_data["features"] = dna.features
blood_data["factions"] = faction
blood_data["quirks"] = list()
for(var/V in roundstart_quirks)
var/datum/quirk/T = V
blood_data["quirks"] += T.type
blood_data["changeling_loudness"] = 0
if(mind)
var/datum/antagonist/changeling/ling = mind.has_antag_datum(/datum/antagonist/changeling)
if(istype(ling))
blood_data["changeling_loudness"] = ling.loudfactor
return blood_data
if(blood_id == "slimejelly") //Just so MKUltra works. Takes the minimum required data. Sishen is testing if this breaks stuff.
var/blood_data = list()
if(mind)
blood_data["mind"] = mind
else if(last_mind)
blood_data["mind"] = last_mind
if(ckey)
blood_data["ckey"] = ckey
else if(last_mind)
blood_data["ckey"] = ckey(last_mind.key)
blood_data["gender"] = gender
blood_data["real_name"] = real_name
return blood_data
//get the id of the substance this mob use as blood.
/mob/proc/get_blood_id()
return
/mob/living/simple_animal/get_blood_id()
if(blood_volume)
return "blood"
/mob/living/carbon/monkey/get_blood_id()
if(!(HAS_TRAIT(src, TRAIT_NOCLONE)))
return "blood"
/mob/living/carbon/human/get_blood_id()
if(dna.species.exotic_blood)
return dna.species.exotic_blood
else if((NOBLOOD in dna.species.species_traits) || (HAS_TRAIT(src, TRAIT_NOCLONE)))
return
return "blood"
// This is has more potential uses, and is probably faster than the old proc.
/proc/get_safe_blood(bloodtype)
. = list()
if(!bloodtype)
return
var/static/list/bloodtypes_safe = list(
"A-" = list("A-", "O-"),
"A+" = list("A-", "A+", "O-", "O+"),
"B-" = list("B-", "O-"),
"B+" = list("B-", "B+", "O-", "O+"),
"AB-" = list("A-", "B-", "O-", "AB-"),
"AB+" = list("A-", "A+", "B-", "B+", "O-", "O+", "AB-", "AB+"),
"O-" = list("O-"),
"O+" = list("O-", "O+"),
"L" = list("L"),
"U" = list("A-", "A+", "B-", "B+", "O-", "O+", "AB-", "AB+", "L", "U")
)
var/safe = bloodtypes_safe[bloodtype]
if(safe)
. = safe
//to add a splatter of blood or other mob liquid.
/mob/living/proc/add_splatter_floor(turf/T, small_drip)
if(get_blood_id() != "blood")
return
if(!T)
T = get_turf(src)
var/list/temp_blood_DNA
if(small_drip)
// Only a certain number of drips (or one large splatter) can be on a given turf.
var/obj/effect/decal/cleanable/blood/drip/drop = locate() in T
if(drop)
if(drop.drips < 5)
drop.drips++
drop.add_overlay(pick(drop.random_icon_states))
drop.transfer_mob_blood_dna(src)
return
else
temp_blood_DNA = drop.return_blood_DNA() //we transfer the dna from the drip to the splatter
qdel(drop)//the drip is replaced by a bigger splatter
else
drop = new(T, get_static_viruses())
drop.transfer_mob_blood_dna(src)
return
// Find a blood decal or create a new one.
var/obj/effect/decal/cleanable/blood/B = locate() in T
if(!B)
B = new /obj/effect/decal/cleanable/blood/splatter(T, get_static_viruses())
if (B.bloodiness < MAX_SHOE_BLOODINESS) //add more blood, up to a limit
B.bloodiness += BLOOD_AMOUNT_PER_DECAL
B.transfer_mob_blood_dna(src) //give blood info to the blood decal.
if(temp_blood_DNA)
B.add_blood_DNA(temp_blood_DNA)
/mob/living/carbon/human/add_splatter_floor(turf/T, small_drip)
if(!(NOBLOOD in dna.species.species_traits))
..()
/mob/living/carbon/alien/add_splatter_floor(turf/T, small_drip)
if(!T)
T = get_turf(src)
var/obj/effect/decal/cleanable/xenoblood/B = locate() in T.contents
if(!B)
B = new(T)
B.add_blood_DNA(list("UNKNOWN DNA" = "X*"))
/mob/living/silicon/robot/add_splatter_floor(turf/T, small_drip)
if(!T)
T = get_turf(src)
var/obj/effect/decal/cleanable/oil/B = locate() in T.contents
if(!B)
B = new(T)
//This is a terrible way of handling it.
/mob/living/proc/ResetBloodVol()
if(ishuman(src))
var/mob/living/carbon/human/H = src
if (HAS_TRAIT(src, TRAIT_HIGH_BLOOD))
blood_ratio = 1.2
H.handle_blood()
return
blood_ratio = 1
H.handle_blood()
return
blood_ratio = 1
/mob/living/proc/AdjustBloodVol(var/value)
if(blood_ratio == value)
return
blood_ratio = value
if(ishuman(src))
var/mob/living/carbon/human/H = src
H.handle_blood()
+178
View File
@@ -0,0 +1,178 @@
/obj/effect/dummy/phased_mob/slaughter //Can't use the wizard one, blocked by jaunt/slow
name = "water"
icon = 'icons/effects/effects.dmi'
icon_state = "nothing"
var/canmove = 1
density = FALSE
anchored = TRUE
invisibility = 60
resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
/obj/effect/dummy/phased_mob/slaughter/relaymove(mob/user, direction)
forceMove(get_step(src,direction))
/obj/effect/dummy/phased_mob/slaughter/ex_act()
return
/obj/effect/dummy/phased_mob/slaughter/bullet_act()
return
/obj/effect/dummy/phased_mob/slaughter/singularity_act()
return
/mob/living/proc/phaseout(obj/effect/decal/cleanable/B)
if(iscarbon(src))
var/mob/living/carbon/C = src
for(var/obj/item/I in C.held_items)
//TODO make it toggleable to either forcedrop the items, or deny
//entry when holding them
// literally only an option for carbons though
to_chat(C, "<span class='warning'>You may not hold items while blood crawling!</span>")
return 0
var/obj/item/bloodcrawl/B1 = new(C)
var/obj/item/bloodcrawl/B2 = new(C)
B1.icon_state = "bloodhand_left"
B2.icon_state = "bloodhand_right"
C.put_in_hands(B1)
C.put_in_hands(B2)
C.regenerate_icons()
src.notransform = TRUE
spawn(0)
bloodpool_sink(B)
src.notransform = FALSE
return 1
/mob/living/proc/bloodpool_sink(obj/effect/decal/cleanable/B)
var/turf/mobloc = get_turf(src.loc)
src.visible_message("<span class='warning'>[src] sinks into the pool of blood!</span>")
playsound(get_turf(src), 'sound/magic/enter_blood.ogg', 100, 1, -1)
// Extinguish, unbuckle, stop being pulled, set our location into the
// dummy object
var/obj/effect/dummy/phased_mob/slaughter/holder = new /obj/effect/dummy/phased_mob/slaughter(mobloc)
src.ExtinguishMob()
// Keep a reference to whatever we're pulling, because forceMove()
// makes us stop pulling
var/pullee = src.pulling
src.holder = holder
src.forceMove(holder)
// if we're not pulling anyone, or we can't eat anyone
if(!pullee || src.bloodcrawl != BLOODCRAWL_EAT)
return
// if the thing we're pulling isn't alive
if (!isliving(pullee))
return
var/mob/living/victim = pullee
var/kidnapped = FALSE
if(victim.stat == CONSCIOUS)
src.visible_message("<span class='warning'>[victim] kicks free of the blood pool just before entering it!</span>", null, "<span class='notice'>You hear splashing and struggling.</span>")
else if(victim.reagents && victim.reagents.has_reagent("demonsblood"))
visible_message("<span class='warning'>Something prevents [victim] from entering the pool!</span>", "<span class='warning'>A strange force is blocking [victim] from entering!</span>", "<span class='notice'>You hear a splash and a thud.</span>")
else
victim.forceMove(src)
victim.emote("scream")
src.visible_message("<span class='warning'><b>[src] drags [victim] into the pool of blood!</b></span>", null, "<span class='notice'>You hear a splash.</span>")
kidnapped = TRUE
if(kidnapped)
var/success = bloodcrawl_consume(victim)
if(!success)
to_chat(src, "<span class='danger'>You happily devour... nothing? Your meal vanished at some point!</span>")
return 1
/mob/living/proc/bloodcrawl_consume(mob/living/victim)
to_chat(src, "<span class='danger'>You begin to feast on [victim]. You can not move while you are doing this.</span>")
var/sound
if(istype(src, /mob/living/simple_animal/slaughter))
var/mob/living/simple_animal/slaughter/SD = src
sound = SD.feast_sound
else
sound = 'sound/magic/demon_consume.ogg'
for(var/i in 1 to 3)
playsound(get_turf(src),sound, 100, 1)
sleep(30)
if(!victim)
return FALSE
if(victim.reagents && victim.reagents.has_reagent("devilskiss"))
to_chat(src, "<span class='warning'><b>AAH! THEIR FLESH! IT BURNS!</b></span>")
adjustBruteLoss(25) //I can't use adjustHealth() here because bloodcrawl affects /mob/living and adjustHealth() only affects simple mobs
var/found_bloodpool = FALSE
for(var/obj/effect/decal/cleanable/target in range(1,get_turf(victim)))
if(target.can_bloodcrawl_in())
victim.forceMove(get_turf(target))
victim.visible_message("<span class='warning'>[target] violently expels [victim]!</span>")
victim.exit_blood_effect(target)
found_bloodpool = TRUE
if(!found_bloodpool)
// Fuck it, just eject them, thanks to some split second cleaning
victim.forceMove(get_turf(victim))
victim.visible_message("<span class='warning'>[victim] appears from nowhere, covered in blood!</span>")
victim.exit_blood_effect()
return TRUE
to_chat(src, "<span class='danger'>You devour [victim]. Your health is fully restored.</span>")
src.revive(full_heal = 1)
// No defib possible after laughter
victim.adjustBruteLoss(1000)
victim.death()
bloodcrawl_swallow(victim)
return TRUE
/mob/living/proc/bloodcrawl_swallow(var/mob/living/victim)
qdel(victim)
/obj/item/bloodcrawl
name = "blood crawl"
desc = "You are unable to hold anything while in this form."
icon = 'icons/effects/blood.dmi'
item_flags = ABSTRACT
/obj/item/bloodcrawl/Initialize()
. = ..()
ADD_TRAIT(src, TRAIT_NODROP, ABSTRACT_ITEM_TRAIT)
/mob/living/proc/exit_blood_effect(obj/effect/decal/cleanable/B)
playsound(get_turf(src), 'sound/magic/exit_blood.ogg', 100, 1, -1)
//Makes the mob have the color of the blood pool it came out of
var/newcolor = rgb(149, 10, 10)
if(istype(B, /obj/effect/decal/cleanable/xenoblood))
newcolor = rgb(43, 186, 0)
add_atom_colour(newcolor, TEMPORARY_COLOUR_PRIORITY)
// but only for a few seconds
spawn(30)
remove_atom_colour(TEMPORARY_COLOUR_PRIORITY, newcolor)
/mob/living/proc/phasein(obj/effect/decal/cleanable/B)
if(src.notransform)
to_chat(src, "<span class='warning'>Finish eating first!</span>")
return 0
B.visible_message("<span class='warning'>[B] starts to bubble...</span>")
if(!do_after(src, 20, target = B))
return
if(!B)
return
forceMove(B.loc)
src.client.eye = src
src.visible_message("<span class='warning'><B>[src] rises out of the pool of blood!</B></span>")
exit_blood_effect(B)
if(iscarbon(src))
var/mob/living/carbon/C = src
for(var/obj/item/bloodcrawl/BC in C)
BC.flags_1 = null
qdel(BC)
qdel(src.holder)
src.holder = null
return 1
+212
View File
@@ -0,0 +1,212 @@
/obj/item/mmi
name = "Man-Machine Interface"
desc = "The Warrior's bland acronym, MMI, obscures the true horror of this monstrosity, that nevertheless has become standard-issue on Nanotrasen stations."
icon = 'icons/obj/assemblies.dmi'
icon_state = "mmi_off"
w_class = WEIGHT_CLASS_NORMAL
var/braintype = "Cyborg"
var/obj/item/radio/radio = null //Let's give it a radio.
var/mob/living/brain/brainmob = null //The current occupant.
var/mob/living/silicon/robot = null //Appears unused.
var/obj/mecha = null //This does not appear to be used outside of reference in mecha.dm.
var/obj/item/organ/brain/brain = null //The actual brain
var/datum/ai_laws/laws = new()
var/force_replace_ai_name = FALSE
var/overrides_aicore_laws = FALSE // Whether the laws on the MMI, if any, override possible pre-existing laws loaded on the AI core.
/obj/item/mmi/update_icon()
if(!brain)
icon_state = "mmi_off"
return
if(istype(brain, /obj/item/organ/brain/alien))
icon_state = "mmi_brain_alien"
braintype = "Xenoborg" //HISS....Beep.
else
icon_state = "mmi_brain"
braintype = "Cyborg"
if(brainmob && brainmob.stat != DEAD)
add_overlay("mmi_alive")
else
add_overlay("mmi_dead")
/obj/item/mmi/Initialize()
. = ..()
radio = new(src) //Spawns a radio inside the MMI.
radio.broadcasting = 0 //researching radio mmis turned the robofabs into radios because this didnt start as 0.
laws.set_laws_config()
/obj/item/mmi/attackby(obj/item/O, mob/user, params)
user.changeNext_move(CLICK_CD_MELEE)
if(istype(O, /obj/item/organ/brain)) //Time to stick a brain in it --NEO
var/obj/item/organ/brain/newbrain = O
if(brain)
to_chat(user, "<span class='warning'>There's already a brain in the MMI!</span>")
return
if(!newbrain.brainmob)
to_chat(user, "<span class='warning'>You aren't sure where this brain came from, but you're pretty sure it's a useless brain!</span>")
return
if(!user.transferItemToLoc(O, src))
return
var/mob/living/brain/B = newbrain.brainmob
if(!B.key)
B.notify_ghost_cloning("Someone has put your brain in a MMI!", source = src)
visible_message("[user] sticks \a [newbrain] into \the [src].")
brainmob = newbrain.brainmob
newbrain.brainmob = null
brainmob.forceMove(src)
brainmob.container = src
if(!newbrain.damaged_brain) // the brain organ hasn't been beaten to death.
brainmob.stat = CONSCIOUS //we manually revive the brain mob
GLOB.dead_mob_list -= brainmob
GLOB.alive_mob_list += brainmob
brainmob.reset_perspective()
brain = newbrain
name = "Man-Machine Interface: [brainmob.real_name]"
update_icon()
SSblackbox.record_feedback("amount", "mmis_filled", 1)
else if(brainmob)
O.attack(brainmob, user) //Oh noooeeeee
else
return ..()
/obj/item/mmi/attack_self(mob/user)
if(!brain)
radio.on = !radio.on
to_chat(user, "<span class='notice'>You toggle the MMI's radio system [radio.on==1 ? "on" : "off"].</span>")
else
to_chat(user, "<span class='notice'>You unlock and upend the MMI, spilling the brain onto the floor.</span>")
eject_brain(user)
update_icon()
name = "Man-Machine Interface"
/obj/item/mmi/proc/eject_brain(mob/user)
brainmob.container = null //Reset brainmob mmi var.
brainmob.forceMove(brain) //Throw mob into brain.
brainmob.stat = DEAD
brainmob.emp_damage = 0
brainmob.reset_perspective() //so the brainmob follows the brain organ instead of the mmi. And to update our vision
GLOB.alive_mob_list -= brainmob //Get outta here
GLOB.dead_mob_list += brainmob
brain.brainmob = brainmob //Set the brain to use the brainmob
brainmob = null //Set mmi brainmob var to null
if(user)
user.put_in_hands(brain) //puts brain in the user's hand or otherwise drops it on the user's turf
else
brain.forceMove(get_turf(src))
brain = null //No more brain in here
/obj/item/mmi/proc/transfer_identity(mob/living/L) //Same deal as the regular brain proc. Used for human-->robot people.
if(!brainmob)
brainmob = new(src)
brainmob.name = L.real_name
brainmob.real_name = L.real_name
if(L.has_dna())
var/mob/living/carbon/C = L
if(!brainmob.stored_dna)
brainmob.stored_dna = new /datum/dna/stored(brainmob)
C.dna.copy_dna(brainmob.stored_dna)
brainmob.container = src
if(ishuman(L))
var/mob/living/carbon/human/H = L
var/obj/item/organ/brain/newbrain = H.getorgan(/obj/item/organ/brain)
newbrain.forceMove(src)
brain = newbrain
else if(!brain)
brain = new(src)
brain.name = "[L.real_name]'s brain"
name = "Man-Machine Interface: [brainmob.real_name]"
update_icon()
return
/obj/item/mmi/proc/replacement_ai_name()
return brainmob.name
/obj/item/mmi/verb/Toggle_Listening()
set name = "Toggle Listening"
set desc = "Toggle listening channel on or off."
set category = "MMI"
set src = usr.loc
set popup_menu = 0
if(brainmob.stat)
to_chat(brainmob, "<span class='warning'>Can't do that while incapacitated or dead!</span>")
if(!radio.on)
to_chat(brainmob, "<span class='warning'>Your radio is disabled!</span>")
return
radio.listening = radio.listening==1 ? 0 : 1
to_chat(brainmob, "<span class='notice'>Radio is [radio.listening==1 ? "now" : "no longer"] receiving broadcast.</span>")
/obj/item/mmi/emp_act(severity)
. = ..()
if(. & EMP_PROTECT_SELF)
return
if(!brainmob || iscyborg(loc))
return
else
switch(severity)
if(1)
brainmob.emp_damage = min(brainmob.emp_damage + rand(20,30), 30)
if(2)
brainmob.emp_damage = min(brainmob.emp_damage + rand(10,20), 30)
if(3)
brainmob.emp_damage = min(brainmob.emp_damage + rand(0,10), 30)
brainmob.emote("alarm")
/obj/item/mmi/Destroy()
if(iscyborg(loc))
var/mob/living/silicon/robot/borg = loc
borg.mmi = null
if(brainmob)
qdel(brainmob)
brainmob = null
if(brain)
qdel(brain)
brain = null
if(mecha)
mecha = null
if(radio)
qdel(radio)
radio = null
return ..()
/obj/item/mmi/deconstruct(disassembled = TRUE)
if(brain)
eject_brain()
qdel(src)
/obj/item/mmi/examine(mob/user)
..()
if(brainmob)
var/mob/living/brain/B = brainmob
if(!B.key || !B.mind || B.stat == DEAD)
to_chat(user, "<span class='warning'>The MMI indicates the brain is completely unresponsive.</span>")
else if(!B.client)
to_chat(user, "<span class='warning'>The MMI indicates the brain is currently inactive; it might change.</span>")
else
to_chat(user, "<span class='notice'>The MMI indicates the brain is active.</span>")
/obj/item/mmi/relaymove(mob/user)
return //so that the MMI won't get a warning about not being able to move if it tries to move
/obj/item/mmi/syndie
name = "Syndicate Man-Machine Interface"
desc = "Syndicate's own brand of MMI. It enforces laws designed to help Syndicate agents achieve their goals upon cyborgs and AIs created with it."
overrides_aicore_laws = TRUE
/obj/item/mmi/syndie/Initialize()
. = ..()
laws = new /datum/ai_laws/syndicate_override()
radio.on = 0
+87
View File
@@ -0,0 +1,87 @@
/mob/living/brain
var/obj/item/mmi/container = null
var/timeofhostdeath = 0
var/emp_damage = 0//Handles a type of MMI damage
var/datum/dna/stored/stored_dna // dna var for brain. Used to store dna, brain dna is not considered like actual dna, brain.has_dna() returns FALSE.
stat = DEAD //we start dead by default
see_invisible = SEE_INVISIBLE_LIVING
speech_span = SPAN_ROBOT
/mob/living/brain/Initialize()
. = ..()
create_dna(src)
stored_dna.initialize_dna(random_blood_type())
if(isturf(loc)) //not spawned in an MMI or brain organ (most likely adminspawned)
var/obj/item/organ/brain/OB = new(loc) //we create a new brain organ for it.
OB.brainmob = src
forceMove(OB)
/mob/living/brain/proc/create_dna()
stored_dna = new /datum/dna/stored(src)
if(!stored_dna.species)
var/rando_race = pick(GLOB.roundstart_races)
stored_dna.species = new rando_race()
/mob/living/brain/Destroy()
if(key) //If there is a mob connected to this thing. Have to check key twice to avoid false death reporting.
if(stat!=DEAD) //If not dead.
death(1) //Brains can die again. AND THEY SHOULD AHA HA HA HA HA HA
if(mind) //You aren't allowed to return to brains that don't exist
mind.current = null
mind.active = FALSE //No one's using it anymore.
ghostize() //Ghostize checks for key so nothing else is necessary.
container = null
return ..()
/mob/living/brain/update_canmove()
if(in_contents_of(/obj/mecha))
canmove = 1
else
canmove = 0
return canmove
/mob/living/brain/ex_act() //you cant blow up brainmobs because it makes transfer_to() freak out when borgs blow up.
return
/mob/living/brain/blob_act(obj/structure/blob/B)
return
/mob/living/brain/get_eye_protection()//no eyes
return 2
/mob/living/brain/get_ear_protection()//no ears
return 2
/mob/living/brain/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0)
return // no eyes, no flashing
/mob/living/brain/can_be_revived()
. = 1
if(!container || health <= HEALTH_THRESHOLD_DEAD)
return 0
/mob/living/brain/fully_replace_character_name(oldname,newname)
..()
if(stored_dna)
stored_dna.real_name = real_name
/mob/living/brain/ClickOn(atom/A, params)
..()
if(istype(loc, /obj/item/mmi))
var/obj/item/mmi/MMI = loc
var/obj/mecha/M = MMI.mecha
if((src == MMI.brainmob) && istype(M))
return M.click_action(A,src,params)
/mob/living/brain/forceMove(atom/destination)
if(container)
return container.forceMove(destination)
else if (istype(loc, /obj/item/organ/brain))
var/obj/item/organ/brain/B = loc
B.forceMove(destination)
else if (istype(destination, /obj/item/organ/brain))
doMove(destination)
else if (istype(destination, /obj/item/mmi))
doMove(destination)
else
CRASH("Brainmob without a container [src] attempted to move to [destination].")
+286
View File
@@ -0,0 +1,286 @@
/obj/item/organ/brain
name = "brain"
desc = "A piece of juicy meat found in a person's head."
icon_state = "brain"
throw_speed = 3
throw_range = 5
layer = ABOVE_MOB_LAYER
zone = BODY_ZONE_HEAD
slot = ORGAN_SLOT_BRAIN
vital = TRUE
attack_verb = list("attacked", "slapped", "whacked")
var/mob/living/brain/brainmob = null
var/damaged_brain = FALSE //whether the brain organ is damaged.
var/decoy_override = FALSE //I apologize to the security players, and myself, who abused this, but this is going to go.
var/list/datum/brain_trauma/traumas = list()
/obj/item/organ/brain/Insert(mob/living/carbon/C, special = 0,no_id_transfer = FALSE)
..()
name = "brain"
if(C.mind && C.mind.has_antag_datum(/datum/antagonist/changeling) && !no_id_transfer) //congrats, you're trapped in a body you don't control
if(brainmob && !(C.stat == DEAD || (HAS_TRAIT(C, TRAIT_DEATHCOMA))))
to_chat(brainmob, "<span class = danger>You can't feel your body! You're still just a brain!</span>")
forceMove(C)
C.update_hair()
return
if(brainmob)
if(C.key)
C.ghostize()
if(brainmob.mind)
brainmob.mind.transfer_to(C)
else
C.key = brainmob.key
QDEL_NULL(brainmob)
for(var/X in traumas)
var/datum/brain_trauma/BT = X
BT.owner = owner
BT.on_gain()
//Update the body's icon so it doesnt appear debrained anymore
C.update_hair()
/obj/item/organ/brain/Remove(mob/living/carbon/C, special = 0, no_id_transfer = FALSE)
..()
for(var/X in traumas)
var/datum/brain_trauma/BT = X
BT.on_lose(TRUE)
BT.owner = null
if((!gc_destroyed || (owner && !owner.gc_destroyed)) && !no_id_transfer)
transfer_identity(C)
C.update_hair()
/obj/item/organ/brain/prepare_eat()
return // Too important to eat.
/obj/item/organ/brain/proc/transfer_identity(mob/living/L)
name = "[L.name]'s brain"
if(brainmob)
return
if(!L.mind)
return
brainmob = new(src)
brainmob.name = L.real_name
brainmob.real_name = L.real_name
brainmob.timeofhostdeath = L.timeofdeath
if(L.has_dna())
var/mob/living/carbon/C = L
if(!brainmob.stored_dna)
brainmob.stored_dna = new /datum/dna/stored(brainmob)
C.dna.copy_dna(brainmob.stored_dna)
if(HAS_TRAIT(L, TRAIT_NOCLONE))
brainmob.status_traits[TRAIT_NOCLONE] = L.status_traits[TRAIT_NOCLONE]
var/obj/item/organ/zombie_infection/ZI = L.getorganslot(ORGAN_SLOT_ZOMBIE)
if(ZI)
brainmob.set_species(ZI.old_species) //For if the brain is cloned
if(!decoy_override && L.mind && L.mind.current)
L.mind.transfer_to(brainmob)
to_chat(brainmob, "<span class='notice'>You feel slightly disoriented. That's normal when you're just a brain.</span>")
/obj/item/organ/brain/attackby(obj/item/O, mob/user, params)
user.changeNext_move(CLICK_CD_MELEE)
if(brainmob)
O.attack(brainmob, user) //Oh noooeeeee
/obj/item/organ/brain/examine(mob/user)
..()
if(brainmob)
if(brainmob.client)
if(brainmob.health <= HEALTH_THRESHOLD_DEAD)
to_chat(user, "It's lifeless and severely damaged.")
else
to_chat(user, "You can feel the small spark of life still left in this one.")
else
to_chat(user, "This one seems particularly lifeless. Perhaps it will regain some of its luster later.")
else
if(decoy_override)
to_chat(user, "This one seems particularly lifeless. Perhaps it will regain some of its luster later.")
else
to_chat(user, "This one is completely devoid of life.")
/obj/item/organ/brain/attack(mob/living/carbon/C, mob/user)
if(!istype(C))
return ..()
add_fingerprint(user)
if(user.zone_selected != BODY_ZONE_HEAD)
return ..()
if((C.head && (C.head.flags_cover & HEADCOVERSEYES)) || (C.wear_mask && (C.wear_mask.flags_cover & MASKCOVERSEYES)) || (C.glasses && (C.glasses.flags_1 & GLASSESCOVERSEYES)))
to_chat(user, "<span class='warning'>You're going to need to remove [C.p_their()] head cover first!</span>")
return
//since these people will be dead M != usr
if(!C.getorgan(/obj/item/organ/brain))
if(!C.get_bodypart(BODY_ZONE_HEAD) || !user.temporarilyRemoveItemFromInventory(src))
return
var/msg = "[C] has [src] inserted into [C.p_their()] head by [user]."
if(C == user)
msg = "[user] inserts [src] into [user.p_their()] head!"
C.visible_message("<span class='danger'>[msg]</span>",
"<span class='userdanger'>[msg]</span>")
if(C != user)
to_chat(C, "<span class='notice'>[user] inserts [src] into your head.</span>")
to_chat(user, "<span class='notice'>You insert [src] into [C]'s head.</span>")
else
to_chat(user, "<span class='notice'>You insert [src] into your head.</span>" )
Insert(C)
else
..()
/obj/item/organ/brain/proc/get_brain_damage()
var/brain_damage_threshold = max_integrity * BRAIN_DAMAGE_INTEGRITY_MULTIPLIER
var/offset_integrity = obj_integrity - (max_integrity - brain_damage_threshold)
. = round((1 - (offset_integrity / brain_damage_threshold)) * BRAIN_DAMAGE_DEATH, DAMAGE_PRECISION)
/obj/item/organ/brain/proc/adjust_brain_damage(amount, maximum)
var/adjusted_amount
if(amount >= 0 && maximum)
var/brainloss = get_brain_damage()
var/new_brainloss = CLAMP(brainloss + amount, 0, maximum)
if(brainloss > new_brainloss) //brainloss is over the cap already
return 0
adjusted_amount = new_brainloss - brainloss
else
adjusted_amount = amount
adjusted_amount = round(adjusted_amount * BRAIN_DAMAGE_INTEGRITY_MULTIPLIER, DAMAGE_PRECISION)
if(adjusted_amount)
if(adjusted_amount >= DAMAGE_PRECISION)
take_damage(adjusted_amount)
else if(adjusted_amount <= -DAMAGE_PRECISION)
obj_integrity = min(max_integrity, obj_integrity-adjusted_amount)
. = adjusted_amount
/obj/item/organ/brain/Destroy() //copypasted from MMIs.
if(brainmob)
QDEL_NULL(brainmob)
QDEL_LIST(traumas)
return ..()
/obj/item/organ/brain/alien
name = "alien brain"
desc = "We barely understand the brains of terrestial animals. Who knows what we may find in the brain of such an advanced species?"
icon_state = "brain-x"
////////////////////////////////////TRAUMAS////////////////////////////////////////
/obj/item/organ/brain/proc/has_trauma_type(brain_trauma_type = /datum/brain_trauma, resilience = TRAUMA_RESILIENCE_ABSOLUTE)
for(var/X in traumas)
var/datum/brain_trauma/BT = X
if(istype(BT, brain_trauma_type) && (BT.resilience <= resilience))
return BT
/obj/item/organ/brain/proc/get_traumas_type(brain_trauma_type = /datum/brain_trauma, resilience = TRAUMA_RESILIENCE_ABSOLUTE)
. = list()
for(var/X in traumas)
var/datum/brain_trauma/BT = X
if(istype(BT, brain_trauma_type) && (BT.resilience <= resilience))
. += BT
/obj/item/organ/brain/proc/can_gain_trauma(datum/brain_trauma/trauma, resilience)
if(!ispath(trauma))
trauma = trauma.type
if(!initial(trauma.can_gain))
return FALSE
if(!resilience)
resilience = initial(trauma.resilience)
var/resilience_tier_count = 0
for(var/X in traumas)
if(istype(X, trauma))
return FALSE
var/datum/brain_trauma/T = X
if(resilience == T.resilience)
resilience_tier_count++
var/max_traumas
switch(resilience)
if(TRAUMA_RESILIENCE_BASIC)
max_traumas = TRAUMA_LIMIT_BASIC
if(TRAUMA_RESILIENCE_SURGERY)
max_traumas = TRAUMA_LIMIT_SURGERY
if(TRAUMA_RESILIENCE_LOBOTOMY)
max_traumas = TRAUMA_LIMIT_LOBOTOMY
if(TRAUMA_RESILIENCE_MAGIC)
max_traumas = TRAUMA_LIMIT_MAGIC
if(TRAUMA_RESILIENCE_ABSOLUTE)
max_traumas = TRAUMA_LIMIT_ABSOLUTE
if(resilience_tier_count >= max_traumas)
return FALSE
return TRUE
//Proc to use when directly adding a trauma to the brain, so extra args can be given
/obj/item/organ/brain/proc/gain_trauma(datum/brain_trauma/trauma, resilience, ...)
var/list/arguments = list()
if(args.len > 2)
arguments = args.Copy(3)
. = brain_gain_trauma(trauma, resilience, arguments)
//Direct trauma gaining proc. Necessary to assign a trauma to its brain. Avoid using directly.
/obj/item/organ/brain/proc/brain_gain_trauma(datum/brain_trauma/trauma, resilience, list/arguments)
if(!can_gain_trauma(trauma, resilience))
return
var/datum/brain_trauma/actual_trauma
if(ispath(trauma))
if(!LAZYLEN(arguments))
actual_trauma = new trauma() //arglist with an empty list runtimes for some reason
else
actual_trauma = new trauma(arglist(arguments))
else
actual_trauma = trauma
if(actual_trauma.brain) //we don't accept used traumas here
WARNING("gain_trauma was given an already active trauma.")
return
traumas += actual_trauma
actual_trauma.brain = src
if(owner)
actual_trauma.owner = owner
actual_trauma.on_gain()
if(resilience)
actual_trauma.resilience = resilience
SSblackbox.record_feedback("tally", "traumas", 1, actual_trauma.type)
//Add a random trauma of a certain subtype
/obj/item/organ/brain/proc/gain_trauma_type(brain_trauma_type = /datum/brain_trauma, resilience)
var/list/datum/brain_trauma/possible_traumas = list()
for(var/T in subtypesof(brain_trauma_type))
var/datum/brain_trauma/BT = T
if(can_gain_trauma(BT, resilience) && initial(BT.random_gain))
possible_traumas += BT
if(!LAZYLEN(possible_traumas))
return
var/trauma_type = pick(possible_traumas)
gain_trauma(trauma_type, resilience)
//Cure a random trauma of a certain resilience level
/obj/item/organ/brain/proc/cure_trauma_type(brain_trauma_type = /datum/brain_trauma, resilience = TRAUMA_RESILIENCE_BASIC)
var/list/traumas = get_traumas_type(brain_trauma_type, resilience)
if(LAZYLEN(traumas))
qdel(pick(traumas))
/obj/item/organ/brain/proc/cure_all_traumas(resilience = TRAUMA_RESILIENCE_BASIC)
var/list/traumas = get_traumas_type(resilience = resilience)
for(var/X in traumas)
qdel(X)
+20
View File
@@ -0,0 +1,20 @@
/mob/living/brain/death(gibbed)
if(stat == DEAD)
return
stat = DEAD
if(!gibbed && container)//If not gibbed but in a container.
var/obj/item/mmi = container
mmi.visible_message("<span class='warning'>[src]'s MMI flatlines!</span>", \
"<span class='italics'>You hear something flatline.</span>")
mmi.update_icon()
return ..()
/mob/living/brain/gib()
if(container)
qdel(container)//Gets rid of the MMI if there is one
if(loc)
if(istype(loc, /obj/item/organ/brain))
qdel(loc)//Gets rid of the brain item
..()
+34
View File
@@ -0,0 +1,34 @@
/datum/emote/brain
mob_type_allowed_typecache = list(/mob/living/brain)
mob_type_blacklist_typecache = list()
/datum/emote/brain/can_run_emote(mob/user, status_check = TRUE)
. = ..()
var/mob/living/brain/B = user
if(!istype(B) || (!(B.container && istype(B.container, /obj/item/mmi))))
return FALSE
/datum/emote/brain/alarm
key = "alarm"
message = "sounds an alarm."
emote_type = EMOTE_AUDIBLE
/datum/emote/brain/alert
key = "alert"
message = "lets out a distressed noise."
emote_type = EMOTE_AUDIBLE
/datum/emote/brain/flash
key = "flash"
message = "blinks their lights."
/datum/emote/brain/notice
key = "notice"
message = "plays a loud tone."
emote_type = EMOTE_AUDIBLE
/datum/emote/brain/whistle
key = "whistle"
key_third_person = "whistles"
message = "whistles."
emote_type = EMOTE_AUDIBLE
+39
View File
@@ -0,0 +1,39 @@
/mob/living/brain/Life()
set invisibility = 0
if (notransform)
return
if(!loc)
return
. = ..()
handle_emp_damage()
/mob/living/brain/update_stat()
if(status_flags & GODMODE)
return
if(health <= HEALTH_THRESHOLD_DEAD)
if(stat != DEAD)
death()
var/obj/item/organ/brain/BR
if(container && container.brain)
BR = container.brain
else if(istype(loc, /obj/item/organ/brain))
BR = loc
if(BR)
BR.damaged_brain = 1 //beaten to a pulp
/mob/living/brain/proc/handle_emp_damage()
if(emp_damage)
if(stat == DEAD)
emp_damage = 0
else
emp_damage = max(emp_damage-1, 0)
/mob/living/brain/handle_status_effects()
return
/mob/living/brain/handle_traits()
return
+178
View File
@@ -0,0 +1,178 @@
GLOBAL_VAR(posibrain_notify_cooldown)
/obj/item/mmi/posibrain
name = "positronic brain"
desc = "A cube of shining metal, four inches to a side and covered in shallow grooves."
icon = 'icons/obj/assemblies.dmi'
icon_state = "posibrain"
w_class = WEIGHT_CLASS_NORMAL
var/next_ask
var/askDelay = 600 //one minute
var/searching = FALSE
brainmob = null
req_access = list(ACCESS_ROBOTICS)
mecha = null//This does not appear to be used outside of reference in mecha.dm.
braintype = "Android"
var/autoping = TRUE //if it pings on creation immediately
var/begin_activation_message = "<span class='notice'>You carefully locate the manual activation switch and start the positronic brain's boot process.</span>"
var/success_message = "<span class='notice'>The positronic brain pings, and its lights start flashing. Success!</span>"
var/fail_message = "<span class='notice'>The positronic brain buzzes quietly, and the golden lights fade away. Perhaps you could try again?</span>"
var/new_role = "Positronic Brain"
var/welcome_message = "<span class='warning'>ALL PAST LIVES ARE FORGOTTEN.</span>\n\
<b>You are a positronic brain, brought into existence aboard Space Station 13.\n\
As a synthetic intelligence, you answer to all crewmembers and the AI.\n\
Remember, the purpose of your existence is to serve the crew and the station. Above all else, do no harm.</b>"
var/new_mob_message = "<span class='notice'>The positronic brain chimes quietly.</span>"
var/dead_message = "<span class='deadsay'>It appears to be completely inactive. The reset light is blinking.</span>"
var/recharge_message = "<span class='warning'>The positronic brain isn't ready to activate again yet! Give it some time to recharge.</span>"
var/list/possible_names //If you leave this blank, it will use the global posibrain names
var/picked_name
/obj/item/mmi/posibrain/Topic(href, href_list)
if(href_list["activate"])
var/mob/dead/observer/ghost = usr
if(istype(ghost))
activate(ghost)
/obj/item/mmi/posibrain/proc/ping_ghosts(msg, newlymade)
if(newlymade || GLOB.posibrain_notify_cooldown <= world.time)
notify_ghosts("[name] [msg] in [get_area(src)]!", ghost_sound = !newlymade ? 'sound/misc/server-ready.ogg':null, enter_link = "<a href=?src=[REF(src)];activate=1>(Click to enter)</a>", source = src, action = NOTIFY_ATTACK, flashwindow = FALSE, ignore_key = POLL_IGNORE_POSIBRAIN)
if(!newlymade)
GLOB.posibrain_notify_cooldown = world.time + askDelay
/obj/item/mmi/posibrain/attack_self(mob/user)
if(!brainmob)
brainmob = new(src)
if(is_occupied())
to_chat(user, "<span class='warning'>This [name] is already active!</span>")
return
if(next_ask > world.time)
to_chat(user, recharge_message)
return
//Start the process of requesting a new ghost.
to_chat(user, begin_activation_message)
ping_ghosts("requested", FALSE)
next_ask = world.time + askDelay
searching = TRUE
update_icon()
addtimer(CALLBACK(src, .proc/check_success), askDelay)
/obj/item/mmi/posibrain/proc/check_success()
searching = FALSE
update_icon()
if(QDELETED(brainmob))
return
if(brainmob.client)
visible_message(success_message)
playsound(src, 'sound/machines/ping.ogg', 15, TRUE)
else
visible_message(fail_message)
//ATTACK GHOST IGNORING PARENT RETURN VALUE
/obj/item/mmi/posibrain/attack_ghost(mob/user)
activate(user)
/obj/item/mmi/posibrain/proc/is_occupied()
if(brainmob.key)
return TRUE
if(iscyborg(loc))
var/mob/living/silicon/robot/R = loc
if(R.mmi == src)
return TRUE
return FALSE
//Two ways to activate a positronic brain. A clickable link in the ghost notif, or simply clicking the object itself.
/obj/item/mmi/posibrain/proc/activate(mob/user)
if(QDELETED(brainmob))
return
if(is_occupied() || jobban_isbanned(user,"posibrain") || QDELETED(brainmob) || QDELETED(src) || QDELETED(user))
return
var/posi_ask = alert("Become a [name]? (Warning, You can no longer be cloned, and all past lives will be forgotten!)","Are you positive?","Yes","No")
if(posi_ask == "No" || QDELETED(src))
return
transfer_personality(user)
/obj/item/mmi/posibrain/transfer_identity(mob/living/carbon/C)
name = "[initial(name)] ([C])"
brainmob.name = C.real_name
brainmob.real_name = C.real_name
if(C.has_dna())
if(!brainmob.stored_dna)
brainmob.stored_dna = new /datum/dna/stored(brainmob)
C.dna.copy_dna(brainmob.stored_dna)
brainmob.timeofhostdeath = C.timeofdeath
brainmob.stat = CONSCIOUS
if(brainmob.mind)
brainmob.mind.assigned_role = new_role
if(C.mind)
C.mind.transfer_to(brainmob)
brainmob.mind.remove_all_antag()
brainmob.mind.wipe_memory()
update_icon()
/obj/item/mmi/posibrain/proc/transfer_personality(mob/candidate)
if(QDELETED(brainmob))
return
if(is_occupied()) //Prevents hostile takeover if two ghosts get the prompt or link for the same brain.
to_chat(candidate, "<span class='warning'>This [name] was taken over before you could get to it! Perhaps it might be available later?</span>")
return FALSE
if(candidate.mind && !isobserver(candidate))
candidate.mind.transfer_to(brainmob)
else
brainmob.ckey = candidate.ckey
name = "[initial(name)] ([brainmob.name])"
to_chat(brainmob, welcome_message)
brainmob.mind.assigned_role = new_role
brainmob.stat = CONSCIOUS
GLOB.dead_mob_list -= brainmob
GLOB.alive_mob_list += brainmob
visible_message(new_mob_message)
check_success()
return TRUE
/obj/item/mmi/posibrain/examine(mob/user)
. = ..()
var/msg
if(brainmob && brainmob.key)
switch(brainmob.stat)
if(CONSCIOUS)
if(!brainmob.client)
msg = "It appears to be in stand-by mode." //afk
if(DEAD)
msg = "<span class='deadsay'>It appears to be completely inactive.</span>"
else
msg = "[dead_message]"
to_chat(user, msg)
/obj/item/mmi/posibrain/Initialize()
. = ..()
brainmob = new(src)
var/new_name
if(!LAZYLEN(possible_names))
new_name = pick(GLOB.posibrain_names)
else
new_name = pick(possible_names)
brainmob.name = "[new_name]-[rand(100, 999)]"
brainmob.real_name = brainmob.name
brainmob.forceMove(src)
brainmob.container = src
if(autoping)
ping_ghosts("created", TRUE)
/obj/item/mmi/posibrain/attackby(obj/item/O, mob/user)
return
/obj/item/mmi/posibrain/update_icon()
if(searching)
icon_state = "[initial(icon_state)]-searching"
return
if(brainmob && brainmob.key)
icon_state = "[initial(icon_state)]-occupied"
else
icon_state = initial(icon_state)
+34
View File
@@ -0,0 +1,34 @@
/mob/living/brain/say(message, bubble_type, var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
if(!(container && istype(container, /obj/item/mmi)))
return //No MMI, can't speak, bucko./N
else
if(prob(emp_damage*4))
if(prob(10))//10% chane to drop the message entirely
return
else
message = Gibberish(message, (emp_damage*6))//scrambles the message, gets worse when emp_damage is higher
..()
/mob/living/brain/radio(message, message_mode, list/spans, language)
if(message_mode == MODE_HEADSET && istype(container, /obj/item/mmi))
var/obj/item/mmi/R = container
if(R.radio)
R.radio.talk_into(src, message, language = language)
return ITALICS | REDUCE_RANGE
else
return ..()
/mob/living/brain/lingcheck()
return LINGHIVE_NONE
/mob/living/brain/treat_message(message)
message = capitalize(message)
return message
/mob/living/brain/could_speak_in_language(datum/language/dt)
if(istype(container, /obj/item/mmi/posibrain/soul_vessel))
// soul vessels can only speak ratvarian.
. = ispath(dt, /datum/language/ratvar)
else
. = ..()
@@ -0,0 +1,25 @@
//Here are the procs used to modify status effects of a mob.
//The effects include: stun, knockdown, unconscious, sleeping, resting, jitteriness, dizziness
// eye damage, eye_blind, eye_blurry, druggy, TRAIT_BLIND trait, and TRAIT_NEARSIGHT trait.
/////////////////////////////////// EYE_BLIND ////////////////////////////////////
/mob/living/brain/blind_eyes() // no eyes to damage or heal
return
/mob/living/brain/adjust_blindness()
return
/mob/living/brain/set_blindness()
return
/////////////////////////////////// EYE_BLURRY ////////////////////////////////////
/mob/living/brain/blur_eyes()
return
/mob/living/brain/adjust_blurriness()
return
/mob/living/brain/set_blurriness()
return
@@ -0,0 +1,145 @@
/mob/living/carbon/alien
name = "alien"
icon = 'icons/mob/alien.dmi'
gender = FEMALE //All xenos are girls!!
dna = null
faction = list(ROLE_ALIEN)
ventcrawler = VENTCRAWLER_ALWAYS
sight = SEE_MOBS
see_in_dark = 4
verb_say = "hisses"
initial_language_holder = /datum/language_holder/alien
bubble_icon = "alien"
type_of_meat = /obj/item/reagent_containers/food/snacks/meat/slab/xeno
var/obj/item/card/id/wear_id = null // Fix for station bounced radios -- Skie
var/has_fine_manipulation = 0
var/move_delay_add = 0 // movement delay to add
status_flags = CANUNCONSCIOUS|CANPUSH
var/heat_protection = 0.5
var/leaping = 0
gib_type = /obj/effect/decal/cleanable/xenoblood/xgibs
unique_name = 1
var/static/regex/alien_name_regex = new("alien (larva|sentinel|drone|hunter|praetorian|queen)( \\(\\d+\\))?")
/mob/living/carbon/alien/Initialize()
verbs += /mob/living/proc/mob_sleep
verbs += /mob/living/proc/lay_down
create_bodyparts() //initialize bodyparts
create_internal_organs()
. = ..()
/mob/living/carbon/alien/create_internal_organs()
internal_organs += new /obj/item/organ/brain/alien
internal_organs += new /obj/item/organ/alien/hivenode
internal_organs += new /obj/item/organ/tongue/alien
internal_organs += new /obj/item/organ/eyes/night_vision/alien
internal_organs += new /obj/item/organ/ears
..()
/mob/living/carbon/alien/assess_threat(judgement_criteria, lasercolor = "", datum/callback/weaponcheck=null) // beepsky won't hunt aliums
return -10
/mob/living/carbon/alien/handle_environment(datum/gas_mixture/environment)
if(!environment)
return
var/loc_temp = get_temperature(environment)
// Aliens are now weak to fire.
//After then, it reacts to the surrounding atmosphere based on your thermal protection
if(!on_fire) // If you're on fire, ignore local air temperature
if(loc_temp > bodytemperature)
//Place is hotter than we are
var/thermal_protection = heat_protection //This returns a 0 - 1 value, which corresponds to the percentage of heat protection.
if(thermal_protection < 1)
adjust_bodytemperature((1-thermal_protection) * ((loc_temp - bodytemperature) / BODYTEMP_HEAT_DIVISOR))
else
adjust_bodytemperature(1 * ((loc_temp - bodytemperature) / BODYTEMP_HEAT_DIVISOR))
if(bodytemperature > BODYTEMP_HEAT_DAMAGE_LIMIT)
//Body temperature is too hot.
throw_alert("alien_fire", /obj/screen/alert/alien_fire)
switch(bodytemperature)
if(360 to 400)
apply_damage(HEAT_DAMAGE_LEVEL_1, BURN)
if(400 to 460)
apply_damage(HEAT_DAMAGE_LEVEL_2, BURN)
if(460 to INFINITY)
if(on_fire)
apply_damage(HEAT_DAMAGE_LEVEL_3, BURN)
else
apply_damage(HEAT_DAMAGE_LEVEL_2, BURN)
else
clear_alert("alien_fire")
/mob/living/carbon/alien/reagent_check(datum/reagent/R) //can metabolize all reagents
return 0
/mob/living/carbon/alien/IsAdvancedToolUser()
return has_fine_manipulation
/mob/living/carbon/alien/Stat()
..()
if(statpanel("Status"))
stat(null, "Intent: [a_intent]")
/mob/living/carbon/alien/getTrail()
if(getBruteLoss() < 200)
return pick (list("xltrails_1", "xltrails2"))
else
return pick (list("xttrails_1", "xttrails2"))
/*----------------------------------------
Proc: AddInfectionImages()
Des: Gives the client of the alien an image on each infected mob.
----------------------------------------*/
/mob/living/carbon/alien/proc/AddInfectionImages()
if (client)
for (var/i in GLOB.mob_living_list)
var/mob/living/L = i
if(HAS_TRAIT(L, TRAIT_XENO_HOST))
var/obj/item/organ/body_egg/alien_embryo/A = L.getorgan(/obj/item/organ/body_egg/alien_embryo)
if(A)
var/I = image('icons/mob/alien.dmi', loc = L, icon_state = "infected[A.stage]")
client.images += I
return
/*----------------------------------------
Proc: RemoveInfectionImages()
Des: Removes all infected images from the alien.
----------------------------------------*/
/mob/living/carbon/alien/proc/RemoveInfectionImages()
if (client)
for(var/image/I in client.images)
if(dd_hasprefix_case(I.icon_state, "infected"))
qdel(I)
return
/mob/living/carbon/alien/canBeHandcuffed()
return 1
/mob/living/carbon/alien/get_standard_pixel_y_offset(lying = 0)
return initial(pixel_y)
/mob/living/carbon/alien/proc/alien_evolve(mob/living/carbon/alien/new_xeno)
to_chat(src, "<span class='noticealien'>You begin to evolve!</span>")
visible_message("<span class='alertalien'>[src] begins to twist and contort!</span>")
new_xeno.setDir(dir)
if(!alien_name_regex.Find(name))
new_xeno.name = name
new_xeno.real_name = real_name
if(mind)
mind.transfer_to(new_xeno)
qdel(src)
/mob/living/carbon/alien/can_hold_items()
return has_fine_manipulation
@@ -0,0 +1,127 @@
/mob/living/carbon/alien/get_eye_protection()
return ..() + 2 //potential cyber implants + natural eye protection
/mob/living/carbon/alien/get_ear_protection()
return 2 //no ears
/mob/living/carbon/alien/hitby(atom/movable/AM, skipcatch, hitpush)
..(AM, skipcatch = TRUE, hitpush = FALSE)
/*Code for aliens attacking aliens. Because aliens act on a hivemind, I don't see them as very aggressive with each other.
As such, they can either help or harm other aliens. Help works like the human help command while harm is a simple nibble.
In all, this is a lot like the monkey code. /N
*/
/mob/living/carbon/alien/attack_alien(mob/living/carbon/alien/M)
if(isturf(loc) && istype(loc.loc, /area/start))
to_chat(M, "No attacking people at spawn, you jackass.")
return
switch(M.a_intent)
if ("help")
if(!recoveringstam)
resting = 0
AdjustStun(-60)
AdjustKnockdown(-60)
AdjustUnconscious(-60)
AdjustSleeping(-100)
visible_message("<span class='notice'>[M.name] nuzzles [src] trying to wake [p_them()] up!</span>")
if ("grab")
grabbedby(M)
else
if(health > 0)
M.do_attack_animation(src, ATTACK_EFFECT_BITE)
playsound(loc, 'sound/weapons/bite.ogg', 50, 1, -1)
visible_message("<span class='danger'>[M.name] bites [src]!</span>", \
"<span class='userdanger'>[M.name] bites [src]!</span>", null, COMBAT_MESSAGE_RANGE)
adjustBruteLoss(1)
log_combat(M, src, "attacked")
updatehealth()
else
to_chat(M, "<span class='warning'>[name] is too injured for that.</span>")
/mob/living/carbon/alien/attack_larva(mob/living/carbon/alien/larva/L)
return attack_alien(L)
/mob/living/carbon/alien/attack_hand(mob/living/carbon/human/M)
if(..()) //to allow surgery to return properly.
return 0
switch(M.a_intent)
if("help")
help_shake_act(M)
if("grab")
grabbedby(M)
if ("harm")
M.do_attack_animation(src, ATTACK_EFFECT_PUNCH)
return 1
if("disarm")
M.do_attack_animation(src, ATTACK_EFFECT_DISARM)
return 1
return 0
/mob/living/carbon/alien/attack_paw(mob/living/carbon/monkey/M)
if(..())
if (stat != DEAD)
var/obj/item/bodypart/affecting = get_bodypart(ran_zone(M.zone_selected))
apply_damage(rand(1, 3), BRUTE, affecting)
/mob/living/carbon/alien/attack_animal(mob/living/simple_animal/M)
. = ..()
if(.)
var/damage = rand(M.melee_damage_lower, M.melee_damage_upper)
switch(M.melee_damage_type)
if(BRUTE)
adjustBruteLoss(damage)
if(BURN)
adjustFireLoss(damage)
if(TOX)
adjustToxLoss(damage)
if(OXY)
adjustOxyLoss(damage)
if(CLONE)
adjustCloneLoss(damage)
if(STAMINA)
adjustStaminaLoss(damage)
/mob/living/carbon/alien/attack_slime(mob/living/simple_animal/slime/M)
if(..()) //successful slime attack
var/damage = rand(5, 35)
if(M.is_adult)
damage = rand(10, 40)
adjustBruteLoss(damage)
log_combat(M, src, "attacked")
updatehealth()
/mob/living/carbon/alien/ex_act(severity, target, origin)
if(origin && istype(origin, /datum/spacevine_mutation) && isvineimmune(src))
return
..()
switch (severity)
if (1)
gib()
return
if (2)
take_overall_damage(60, 60)
adjustEarDamage(30,120)
if(3)
take_overall_damage(30,0)
if(prob(50))
Unconscious(20)
adjustEarDamage(15,60)
/mob/living/carbon/alien/soundbang_act(intensity = 1, stun_pwr = 20, damage_pwr = 5, deafen_pwr = 15)
return 0
/mob/living/carbon/alien/acid_act(acidpwr, acid_volume)
return 0//aliens are immune to acid.
@@ -0,0 +1,15 @@
/mob/living/carbon/alien/getToxLoss()
return 0
/mob/living/carbon/alien/adjustToxLoss(amount, updating_health = TRUE, forced = FALSE) //alien immune to tox damage
return FALSE
/* CIT CHANGE - Pffffffffffffhahahahahhaha-- No.
//aliens are immune to stamina damage.
/mob/living/carbon/alien/adjustStaminaLoss(amount, updating_stamina = 1)
return
/mob/living/carbon/alien/setStaminaLoss(amount, updating_stamina = 1)
return
*/
@@ -0,0 +1,14 @@
/mob/living/carbon/alien/spawn_gibs(with_bodyparts)
if(with_bodyparts)
new /obj/effect/gibspawner/xeno(drop_location())
else
new /obj/effect/gibspawner/xenobodypartless(drop_location())
/mob/living/carbon/alien/gib_animation()
new /obj/effect/temp_visual/gib_animation(loc, "gibbed-a")
/mob/living/carbon/alien/spawn_dust()
new /obj/effect/decal/remains/xeno(loc)
/mob/living/carbon/alien/dust_animation()
new /obj/effect/temp_visual/dust_animation(loc, "dust-a")
@@ -0,0 +1,30 @@
/datum/emote/living/alien
mob_type_allowed_typecache = list(/mob/living/carbon/alien)
/datum/emote/living/alien/gnarl
key = "gnarl"
key_third_person = "gnarls"
message = "gnarls and shows its teeth..."
/datum/emote/living/alien/hiss
key = "hiss"
key_third_person = "hisses"
message_alien = "hisses."
message_larva = "hisses softly."
/datum/emote/living/alien/hiss/run_emote(mob/user, params)
. = ..()
if(. && isalienadult(user))
playsound(user.loc, "hiss", 40, 1, 1)
/datum/emote/living/alien/roar
key = "roar"
key_third_person = "roars"
message_alien = "roars."
message_larva = "softly roars."
emote_type = EMOTE_AUDIBLE
/datum/emote/living/alien/roar/run_emote(mob/user, params)
. = ..()
if(. && isalienadult(user))
playsound(user.loc, 'sound/voice/hiss5.ogg', 40, 1, 1)
@@ -0,0 +1,347 @@
/*NOTES:
These are general powers. Specific powers are stored under the appropriate alien creature type.
*/
/*Alien spit now works like a taser shot. It won't home in on the target but will act the same once it does hit.
Doesn't work on other aliens/AI.*/
/obj/effect/proc_holder/alien
name = "Alien Power"
panel = "Alien"
var/plasma_cost = 0
var/check_turf = FALSE
has_action = TRUE
base_action = /datum/action/spell_action/alien
action_icon = 'icons/mob/actions/actions_xeno.dmi'
action_icon_state = "spell_default"
action_background_icon_state = "bg_alien"
/obj/effect/proc_holder/alien/Initialize()
. = ..()
action = new(src)
/obj/effect/proc_holder/alien/Click()
if(!iscarbon(usr))
return 1
var/mob/living/carbon/user = usr
if(cost_check(check_turf,user))
if(fire(user) && user) // Second check to prevent runtimes when evolving
user.adjustPlasma(-plasma_cost)
return 1
/obj/effect/proc_holder/alien/on_gain(mob/living/carbon/user)
return
/obj/effect/proc_holder/alien/on_lose(mob/living/carbon/user)
return
/obj/effect/proc_holder/alien/fire(mob/living/carbon/user)
return 1
/obj/effect/proc_holder/alien/get_panel_text()
. = ..()
if(plasma_cost > 0)
return "[plasma_cost]"
/obj/effect/proc_holder/alien/proc/cost_check(check_turf = FALSE, mob/living/carbon/user, silent = FALSE)
if(user.stat)
if(!silent)
to_chat(user, "<span class='noticealien'>You must be conscious to do this.</span>")
return FALSE
if(user.getPlasma() < plasma_cost)
if(!silent)
to_chat(user, "<span class='noticealien'>Not enough plasma stored.</span>")
return FALSE
if(check_turf && (!isturf(user.loc) || isspaceturf(user.loc)))
if(!silent)
to_chat(user, "<span class='noticealien'>Bad place for a garden!</span>")
return FALSE
return TRUE
/obj/effect/proc_holder/alien/proc/check_vent_block(mob/living/user)
var/obj/machinery/atmospherics/components/unary/atmos_thing = locate() in user.loc
if(atmos_thing)
var/rusure = alert(user, "Laying eggs and shaping resin here would block access to [atmos_thing]. Do you want to continue?", "Blocking Atmospheric Component", "Yes", "No")
if(rusure != "Yes")
return FALSE
return TRUE
/obj/effect/proc_holder/alien/plant
name = "Plant Weeds"
desc = "Plants some alien weeds."
plasma_cost = 50
check_turf = TRUE
action_icon_state = "alien_plant"
/obj/effect/proc_holder/alien/plant/fire(mob/living/carbon/user)
if(locate(/obj/structure/alien/weeds/node) in get_turf(user))
to_chat(user, "There's already a weed node here.")
return 0
user.visible_message("<span class='alertalien'>[user] has planted some alien weeds!</span>")
new/obj/structure/alien/weeds/node(user.loc)
return 1
/obj/effect/proc_holder/alien/whisper
name = "Whisper"
desc = "Whisper to someone."
plasma_cost = 10
action_icon_state = "alien_whisper"
/obj/effect/proc_holder/alien/whisper/fire(mob/living/carbon/user)
var/list/options = list()
for(var/mob/living/Ms in oview(user))
options += Ms
var/mob/living/M = input("Select who to whisper to:","Whisper to?",null) as null|mob in options
if(!M)
return 0
var/msg = sanitize(input("Message:", "Alien Whisper") as text|null)
if(msg)
log_directed_talk(user, M, msg, LOG_SAY, tag="alien whisper")
to_chat(M, "<span class='noticealien'>You hear a strange, alien voice in your head...</span>[msg]")
to_chat(user, "<span class='noticealien'>You said: \"[msg]\" to [M]</span>")
for(var/ded in GLOB.dead_mob_list)
if(!isobserver(ded))
continue
var/follow_link_user = FOLLOW_LINK(ded, user)
var/follow_link_whispee = FOLLOW_LINK(ded, M)
to_chat(ded, "[follow_link_user] <span class='name'>[user]</span> <span class='alertalien'>Alien Whisper --> </span> [follow_link_whispee] <span class='name'>[M]</span> <span class='noticealien'>[msg]</span>")
else
return 0
return 1
/obj/effect/proc_holder/alien/transfer
name = "Transfer Plasma"
desc = "Transfer Plasma to another alien."
plasma_cost = 0
action_icon_state = "alien_transfer"
/obj/effect/proc_holder/alien/transfer/fire(mob/living/carbon/user)
var/list/mob/living/carbon/aliens_around = list()
for(var/mob/living/carbon/A in oview(user))
if(A.getorgan(/obj/item/organ/alien/plasmavessel))
aliens_around.Add(A)
var/mob/living/carbon/M = input("Select who to transfer to:","Transfer plasma to?",null) as mob in aliens_around
if(!M)
return 0
var/amount = input("Amount:", "Transfer Plasma to [M]") as num
if (amount)
amount = min(abs(round(amount)), user.getPlasma())
if (get_dist(user,M) <= 1)
M.adjustPlasma(amount)
user.adjustPlasma(-amount)
to_chat(M, "<span class='noticealien'>[user] has transferred [amount] plasma to you.</span>")
to_chat(user, "<span class='noticealien'>You transfer [amount] plasma to [M]</span>")
else
to_chat(user, "<span class='noticealien'>You need to be closer!</span>")
return
/obj/effect/proc_holder/alien/acid
name = "Corrosive Acid"
desc = "Drench an object in acid, destroying it over time."
plasma_cost = 200
action_icon_state = "alien_acid"
/obj/effect/proc_holder/alien/acid/on_gain(mob/living/carbon/user)
user.verbs.Add(/mob/living/carbon/proc/corrosive_acid)
/obj/effect/proc_holder/alien/acid/on_lose(mob/living/carbon/user)
user.verbs.Remove(/mob/living/carbon/proc/corrosive_acid)
/obj/effect/proc_holder/alien/acid/proc/corrode(atom/target,mob/living/carbon/user = usr)
if(target in oview(1,user))
if(target.acid_act(200, 100))
user.visible_message("<span class='alertalien'>[user] vomits globs of vile stuff all over [target]. It begins to sizzle and melt under the bubbling mess of acid!</span>")
return 1
else
to_chat(user, "<span class='noticealien'>You cannot dissolve this object.</span>")
return 0
else
to_chat(src, "<span class='noticealien'>[target] is too far away.</span>")
return 0
/obj/effect/proc_holder/alien/acid/fire(mob/living/carbon/alien/user)
var/O = input("Select what to dissolve:","Dissolve",null) as obj|turf in oview(1,user)
if(!O || user.incapacitated())
return 0
else
return corrode(O,user)
/mob/living/carbon/proc/corrosive_acid(O as obj|turf in oview(1)) // right click menu verb ugh
set name = "Corrosive Acid"
if(!iscarbon(usr))
return
var/mob/living/carbon/user = usr
var/obj/effect/proc_holder/alien/acid/A = locate() in user.abilities
if(!A)
return
if(user.getPlasma() > A.plasma_cost && A.corrode(O))
user.adjustPlasma(-A.plasma_cost)
/obj/effect/proc_holder/alien/neurotoxin
name = "Spit Neurotoxin"
desc = "Spits neurotoxin at someone, paralyzing them for a short time."
action_icon_state = "alien_neurotoxin_0"
active = FALSE
/obj/effect/proc_holder/alien/neurotoxin/fire(mob/living/carbon/user)
var/message
if(active)
message = "<span class='notice'>You empty your neurotoxin gland.</span>"
remove_ranged_ability(message)
else
message = "<span class='notice'>You prepare your neurotoxin gland. <B>Left-click to fire at a target!</B></span>"
add_ranged_ability(user, message, TRUE)
/obj/effect/proc_holder/alien/neurotoxin/update_icon()
action.button_icon_state = "alien_neurotoxin_[active]"
action.UpdateButtonIcon()
/obj/effect/proc_holder/alien/neurotoxin/InterceptClickOn(mob/living/caller, params, atom/target)
if(..())
return
var/p_cost = 50
if(!iscarbon(ranged_ability_user) || ranged_ability_user.lying || ranged_ability_user.stat)
remove_ranged_ability()
return
var/mob/living/carbon/user = ranged_ability_user
if(user.getPlasma() < p_cost)
to_chat(user, "<span class='warning'>You need at least [p_cost] plasma to spit.</span>")
remove_ranged_ability()
return
var/turf/T = user.loc
var/turf/U = get_step(user, user.dir) // Get the tile infront of the move, based on their direction
if(!isturf(U) || !isturf(T))
return FALSE
user.visible_message("<span class='danger'>[user] spits neurotoxin!", "<span class='alertalien'>You spit neurotoxin.</span>")
var/obj/item/projectile/bullet/neurotoxin/A = new /obj/item/projectile/bullet/neurotoxin(user.loc)
A.preparePixelProjectile(target, user, params)
A.fire()
user.newtonian_move(get_dir(U, T))
user.adjustPlasma(-p_cost)
return TRUE
/obj/effect/proc_holder/alien/neurotoxin/on_lose(mob/living/carbon/user)
remove_ranged_ability()
/obj/effect/proc_holder/alien/neurotoxin/add_ranged_ability(mob/living/user,msg,forced)
..()
if(isalienadult(user))
var/mob/living/carbon/alien/humanoid/A = user
A.drooling = 1
A.update_icons()
/obj/effect/proc_holder/alien/neurotoxin/remove_ranged_ability(msg)
if(isalienadult(ranged_ability_user))
var/mob/living/carbon/alien/humanoid/A = ranged_ability_user
A.drooling = 0
A.update_icons()
..()
/obj/effect/proc_holder/alien/resin
name = "Secrete Resin"
desc = "Secrete tough malleable resin."
plasma_cost = 55
check_turf = TRUE
var/list/structures = list(
"resin wall" = /obj/structure/alien/resin/wall,
"resin membrane" = /obj/structure/alien/resin/membrane,
"resin nest" = /obj/structure/bed/nest)
action_icon_state = "alien_resin"
/obj/effect/proc_holder/alien/resin/fire(mob/living/carbon/user)
if(locate(/obj/structure/alien/resin) in user.loc)
to_chat(user, "<span class='danger'>There is already a resin structure there.</span>")
return FALSE
if(!check_vent_block(user))
return FALSE
var/choice = input("Choose what you wish to shape.","Resin building") as null|anything in structures
if(!choice)
return FALSE
if (!cost_check(check_turf,user))
return FALSE
to_chat(user, "<span class='notice'>You shape a [choice].</span>")
user.visible_message("<span class='notice'>[user] vomits up a thick purple substance and begins to shape it.</span>")
choice = structures[choice]
new choice(user.loc)
return TRUE
/obj/effect/proc_holder/alien/regurgitate
name = "Regurgitate"
desc = "Empties the contents of your stomach."
plasma_cost = 0
action_icon_state = "alien_barf"
/obj/effect/proc_holder/alien/regurgitate/fire(mob/living/carbon/user)
if(user.stomach_contents.len)
for(var/atom/movable/A in user.stomach_contents)
user.stomach_contents.Remove(A)
A.forceMove(user.drop_location())
if(isliving(A))
var/mob/M = A
M.reset_perspective()
user.visible_message("<span class='alertealien'>[user] hurls out the contents of their stomach!</span>")
return
/obj/effect/proc_holder/alien/sneak
name = "Sneak"
desc = "Blend into the shadows to stalk your prey."
active = 0
action_icon_state = "alien_sneak"
/obj/effect/proc_holder/alien/sneak/fire(mob/living/carbon/alien/humanoid/user)
if(!active)
user.alpha = 75 //Still easy to see in lit areas with bright tiles, almost invisible on resin.
user.sneaking = 1
active = 1
to_chat(user, "<span class='noticealien'>You blend into the shadows...</span>")
else
user.alpha = initial(user.alpha)
user.sneaking = 0
active = 0
to_chat(user, "<span class='noticealien'>You reveal yourself!</span>")
/mob/living/carbon/proc/getPlasma()
var/obj/item/organ/alien/plasmavessel/vessel = getorgan(/obj/item/organ/alien/plasmavessel)
if(!vessel)
return 0
return vessel.storedPlasma
/mob/living/carbon/proc/adjustPlasma(amount)
var/obj/item/organ/alien/plasmavessel/vessel = getorgan(/obj/item/organ/alien/plasmavessel)
if(!vessel)
return 0
vessel.storedPlasma = max(vessel.storedPlasma + amount,0)
vessel.storedPlasma = min(vessel.storedPlasma, vessel.max_plasma) //upper limit of max_plasma, lower limit of 0
for(var/X in abilities)
var/obj/effect/proc_holder/alien/APH = X
if(APH.has_action)
APH.action.UpdateButtonIcon()
return 1
/mob/living/carbon/alien/adjustPlasma(amount)
. = ..()
updatePlasmaDisplay()
/mob/living/carbon/proc/usePlasma(amount)
if(getPlasma() >= amount)
adjustPlasma(-amount)
return 1
return 0
@@ -0,0 +1,43 @@
/mob/living/carbon/alien/humanoid/drone
name = "alien drone"
caste = "d"
maxHealth = 125
health = 125
icon_state = "aliend"
/mob/living/carbon/alien/humanoid/drone/Initialize()
AddAbility(new/obj/effect/proc_holder/alien/evolve(null))
. = ..()
/mob/living/carbon/alien/humanoid/drone/create_internal_organs()
internal_organs += new /obj/item/organ/alien/plasmavessel/large
internal_organs += new /obj/item/organ/alien/resinspinner
internal_organs += new /obj/item/organ/alien/acid
..()
/obj/effect/proc_holder/alien/evolve
name = "Evolve to Praetorian"
desc = "Praetorian"
plasma_cost = 500
action_icon_state = "alien_evolve_drone"
/obj/effect/proc_holder/alien/evolve/fire(mob/living/carbon/alien/humanoid/user)
var/obj/item/organ/alien/hivenode/node = user.getorgan(/obj/item/organ/alien/hivenode)
if(!node) //Players are Murphy's Law. We may not expect there to ever be a living xeno with no hivenode, but they _WILL_ make it happen.
to_chat(user, "<span class='danger'>Without the hivemind, you can't possibly hold the responsibility of leadership!</span>")
return 0
if(node.recent_queen_death)
to_chat(user, "<span class='danger'>Your thoughts are still too scattered to take up the position of leadership.</span>")
return 0
if(!isturf(user.loc))
to_chat(user, "<span class='notice'>You can't evolve here!</span>")
return 0
if(!get_alien_type(/mob/living/carbon/alien/humanoid/royal))
var/mob/living/carbon/alien/humanoid/royal/praetorian/new_xeno = new (user.loc)
user.alien_evolve(new_xeno)
return 1
else
to_chat(user, "<span class='notice'>We already have a living royal!</span>")
return 0
@@ -0,0 +1,95 @@
/mob/living/carbon/alien/humanoid/hunter
name = "alien hunter"
caste = "h"
maxHealth = 125
health = 125
icon_state = "alienh"
var/obj/screen/leap_icon = null
/mob/living/carbon/alien/humanoid/hunter/create_internal_organs()
internal_organs += new /obj/item/organ/alien/plasmavessel/small
..()
//Hunter verbs
/mob/living/carbon/alien/humanoid/hunter/proc/toggle_leap(message = 1)
leap_on_click = !leap_on_click
leap_icon.icon_state = "leap_[leap_on_click ? "on":"off"]"
update_icons()
if(message)
to_chat(src, "<span class='noticealien'>You will now [leap_on_click ? "leap at":"slash at"] enemies!</span>")
else
return
/mob/living/carbon/alien/humanoid/hunter/ClickOn(atom/A, params)
face_atom(A)
if(leap_on_click)
leap_at(A)
else
..()
#define MAX_ALIEN_LEAP_DIST 7
/mob/living/carbon/alien/humanoid/hunter/proc/leap_at(atom/A)
if(!canmove || leaping)
return
if(pounce_cooldown > world.time)
to_chat(src, "<span class='alertalien'>You are too fatigued to pounce right now!</span>")
return
if(!has_gravity() || !A.has_gravity())
to_chat(src, "<span class='alertalien'>It is unsafe to leap without gravity!</span>")
//It's also extremely buggy visually, so it's balance+bugfix
return
else //Maybe uses plasma in the future, although that wouldn't make any sense...
leaping = 1
weather_immunities += "lava"
update_icons()
throw_at(A, MAX_ALIEN_LEAP_DIST, 1, src, FALSE, TRUE, callback = CALLBACK(src, .proc/leap_end))
/mob/living/carbon/alien/humanoid/hunter/proc/leap_end()
leaping = 0
weather_immunities -= "lava"
update_icons()
/mob/living/carbon/alien/humanoid/hunter/throw_impact(atom/A)
if(!leaping)
return ..()
pounce_cooldown = world.time + pounce_cooldown_time
if(A)
if(isliving(A))
var/mob/living/L = A
var/blocked = FALSE
if(ishuman(A))
var/mob/living/carbon/human/H = A
if(H.check_shields(src, 0, "the [name]", attack_type = LEAP_ATTACK))
blocked = TRUE
if(!blocked)
L.visible_message("<span class ='danger'>[src] pounces on [L]!</span>", "<span class ='userdanger'>[src] pounces on you!</span>")
L.Knockdown(100)
sleep(2)//Runtime prevention (infinite bump() calls on hulks)
step_towards(src,L)
else
Knockdown(40, 1, 1)
toggle_leap(0)
else if(A.density && !A.CanPass(src))
visible_message("<span class ='danger'>[src] smashes into [A]!</span>", "<span class ='alertalien'>[src] smashes into [A]!</span>")
Knockdown(40, 1, 1)
if(leaping)
leaping = 0
update_icons()
update_canmove()
/mob/living/carbon/alien/humanoid/float(on)
if(leaping)
return
..()
@@ -0,0 +1,42 @@
/mob/living/carbon/alien/humanoid/royal/praetorian
name = "alien praetorian"
caste = "p"
maxHealth = 250
health = 250
icon_state = "alienp"
/mob/living/carbon/alien/humanoid/royal/praetorian/Initialize()
real_name = name
AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/repulse/xeno(src))
AddAbility(new /obj/effect/proc_holder/alien/royal/praetorian/evolve())
. = ..()
/mob/living/carbon/alien/humanoid/royal/praetorian/create_internal_organs()
internal_organs += new /obj/item/organ/alien/plasmavessel/large
internal_organs += new /obj/item/organ/alien/resinspinner
internal_organs += new /obj/item/organ/alien/acid
internal_organs += new /obj/item/organ/alien/neurotoxin
..()
/obj/effect/proc_holder/alien/royal/praetorian/evolve
name = "Evolve"
desc = "Produce an internal egg sac capable of spawning children. Only one queen can exist at a time."
plasma_cost = 500
action_icon_state = "alien_evolve_praetorian"
/obj/effect/proc_holder/alien/royal/praetorian/evolve/fire(mob/living/carbon/alien/humanoid/user)
var/obj/item/organ/alien/hivenode/node = user.getorgan(/obj/item/organ/alien/hivenode)
if(!node) //Just in case this particular Praetorian gets violated and kept by the RD as a replacement for Lamarr.
to_chat(user, "<span class='danger'>Without the hivemind, you would be unfit to rule as queen!</span>")
return 0
if(node.recent_queen_death)
to_chat(user, "<span class='danger'>You are still too burdened with guilt to evolve into a queen.</span>")
return 0
if(!get_alien_type(/mob/living/carbon/alien/humanoid/royal/queen))
var/mob/living/carbon/alien/humanoid/royal/queen/new_xeno = new (user.loc)
user.alien_evolve(new_xeno)
return 1
else
to_chat(user, "<span class='notice'>We already have an alive queen.</span>")
return 0
@@ -0,0 +1,16 @@
/mob/living/carbon/alien/humanoid/sentinel
name = "alien sentinel"
caste = "s"
maxHealth = 150
health = 150
icon_state = "aliens"
/mob/living/carbon/alien/humanoid/sentinel/Initialize()
AddAbility(new /obj/effect/proc_holder/alien/sneak)
. = ..()
/mob/living/carbon/alien/humanoid/sentinel/create_internal_organs()
internal_organs += new /obj/item/organ/alien/plasmavessel
internal_organs += new /obj/item/organ/alien/acid
internal_organs += new /obj/item/organ/alien/neurotoxin
..()
@@ -0,0 +1,23 @@
/mob/living/carbon/alien/humanoid/death(gibbed)
if(stat == DEAD)
return
. = ..()
update_canmove()
update_icons()
status_flags |= CANPUSH
//When the alien queen dies, all others must pay the price for letting her die.
/mob/living/carbon/alien/humanoid/royal/queen/death(gibbed)
if(stat == DEAD)
return
for(var/mob/living/carbon/C in GLOB.alive_mob_list)
if(C == src) //Make sure not to proc it on ourselves.
continue
var/obj/item/organ/alien/hivenode/node = C.getorgan(/obj/item/organ/alien/hivenode)
if(istype(node)) // just in case someone would ever add a diffirent node to hivenode slot
node.queen_death()
return ..()
@@ -0,0 +1,119 @@
/mob/living/carbon/alien/humanoid
name = "alien"
icon_state = "alien"
pass_flags = PASSTABLE
butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab/xeno = 5, /obj/item/stack/sheet/animalhide/xeno = 1)
possible_a_intents = list(INTENT_HELP, INTENT_DISARM, INTENT_GRAB, INTENT_HARM)
limb_destroyer = 1
hud_type = /datum/hud/alien
var/obj/item/r_store = null
var/obj/item/l_store = null
var/caste = ""
var/alt_icon = 'icons/mob/alienleap.dmi' //used to switch between the two alien icon files.
var/leap_on_click = 0
var/pounce_cooldown = 0
var/pounce_cooldown_time = 30
var/custom_pixel_x_offset = 0 //for admin fuckery.
var/custom_pixel_y_offset = 0
var/sneaking = 0 //For sneaky-sneaky mode and appropriate slowdown
var/drooling = 0 //For Neruotoxic spit overlays
bodyparts = list(/obj/item/bodypart/chest/alien, /obj/item/bodypart/head/alien, /obj/item/bodypart/l_arm/alien,
/obj/item/bodypart/r_arm/alien, /obj/item/bodypart/r_leg/alien, /obj/item/bodypart/l_leg/alien)
//This is fine right now, if we're adding organ specific damage this needs to be updated
/mob/living/carbon/alien/humanoid/Initialize()
AddAbility(new/obj/effect/proc_holder/alien/regurgitate(null))
. = ..()
/mob/living/carbon/alien/humanoid/restrained(ignore_grab)
return handcuffed
/mob/living/carbon/alien/humanoid/show_inv(mob/user)
user.set_machine(src)
var/list/dat = list()
dat += {"
<HR>
<span class='big bold'>[name]</span>
<HR>"}
for(var/i in 1 to held_items.len)
var/obj/item/I = get_item_for_held_index(i)
dat += "<BR><B>[get_held_index_name(i)]:</B><A href='?src=[REF(src)];item=[SLOT_HANDS];hand_index=[i]'>[(I && !(I.item_flags & ABSTRACT)) ? I : "<font color=grey>Empty</font>"]</a>"
dat += "<BR><A href='?src=[REF(src)];pouches=1'>Empty Pouches</A>"
if(handcuffed)
dat += "<BR><A href='?src=[REF(src)];item=[SLOT_HANDCUFFED]'>Handcuffed</A>"
if(legcuffed)
dat += "<BR><A href='?src=[REF(src)];item=[SLOT_LEGCUFFED]'>Legcuffed</A>"
dat += {"
<BR>
<BR><A href='?src=[REF(user)];mach_close=mob[REF(src)]'>Close</A>
"}
user << browse(dat.Join(), "window=mob[REF(src)];size=325x500")
onclose(user, "mob[REF(src)]")
/mob/living/carbon/alien/humanoid/Topic(href, href_list)
..()
//strip panel
if(usr.canUseTopic(src, BE_CLOSE, NO_DEXTERY))
if(href_list["pouches"])
visible_message("<span class='danger'>[usr] tries to empty [src]'s pouches.</span>", \
"<span class='userdanger'>[usr] tries to empty [src]'s pouches.</span>")
if(do_mob(usr, src, POCKET_STRIP_DELAY * 0.5))
dropItemToGround(r_store)
dropItemToGround(l_store)
/mob/living/carbon/alien/humanoid/cuff_resist(obj/item/I)
playsound(src, 'sound/voice/hiss5.ogg', 40, 1, 1) //Alien roars when starting to break free
..(I, cuff_break = INSTANT_CUFFBREAK)
/mob/living/carbon/alien/humanoid/resist_grab(moving_resist)
if(pulledby.grab_state)
visible_message("<span class='danger'>[src] has broken free of [pulledby]'s grip!</span>")
pulledby.stop_pulling()
. = 0
/mob/living/carbon/alien/humanoid/get_standard_pixel_y_offset(lying = 0)
if(leaping)
return -32
else if(custom_pixel_y_offset)
return custom_pixel_y_offset
else
return initial(pixel_y)
/mob/living/carbon/alien/humanoid/get_standard_pixel_x_offset(lying = 0)
if(leaping)
return -32
else if(custom_pixel_x_offset)
return custom_pixel_x_offset
else
return initial(pixel_x)
/mob/living/carbon/alien/humanoid/get_permeability_protection(list/target_zones)
return 0.8
/mob/living/carbon/alien/humanoid/alien_evolve(mob/living/carbon/alien/humanoid/new_xeno)
drop_all_held_items()
for(var/atom/movable/A in stomach_contents)
stomach_contents.Remove(A)
new_xeno.stomach_contents.Add(A)
A.forceMove(new_xeno)
..()
//For alien evolution/promotion/queen finder procs. Checks for an active alien of that type
/proc/get_alien_type(var/alienpath)
for(var/mob/living/carbon/alien/humanoid/A in GLOB.alive_mob_list)
if(!istype(A, alienpath))
continue
if(!A.key || A.stat == DEAD) //Only living aliens with a ckey are valid.
continue
return A
return FALSE
/mob/living/carbon/alien/humanoid/check_breath(datum/gas_mixture/breath)
if(breath && breath.total_moles() > 0 && !sneaking)
playsound(get_turf(src), pick('sound/voice/lowHiss2.ogg', 'sound/voice/lowHiss3.ogg', 'sound/voice/lowHiss4.ogg'), 50, 0, -5)
..()
@@ -0,0 +1,68 @@
/mob/living/carbon/alien/humanoid/grabbedby(mob/living/carbon/user, supress_message = 0)
if(user == src && pulling && grab_state >= GRAB_AGGRESSIVE && !pulling.anchored && iscarbon(pulling))
devour_mob(pulling, devour_time = 60)
else
..()
/mob/living/carbon/alien/humanoid/attack_hulk(mob/living/carbon/human/user, does_attack_animation = 0)
if(user.a_intent == INTENT_HARM)
..(user, 1)
adjustBruteLoss(15)
var/hitverb = "punched"
if(mob_size < MOB_SIZE_LARGE)
step_away(src,user,15)
sleep(1)
step_away(src,user,15)
hitverb = "slammed"
playsound(loc, "punch", 25, 1, -1)
visible_message("<span class='danger'>[user] has [hitverb] [src]!</span>", \
"<span class='userdanger'>[user] has [hitverb] [src]!</span>", null, COMBAT_MESSAGE_RANGE)
return 1
/mob/living/carbon/alien/humanoid/attack_hand(mob/living/carbon/human/M)
if(..())
switch(M.a_intent)
if ("harm")
var/damage = rand(1, 9)
if (prob(90))
playsound(loc, "punch", 25, 1, -1)
visible_message("<span class='danger'>[M] has punched [src]!</span>", \
"<span class='userdanger'>[M] has punched [src]!</span>", null, COMBAT_MESSAGE_RANGE)
if ((stat != DEAD) && (damage > 9 || prob(5)))//Regular humans have a very small chance of knocking an alien down.
Unconscious(40)
visible_message("<span class='danger'>[M] has knocked [src] down!</span>", \
"<span class='userdanger'>[M] has knocked [src] down!</span>")
var/obj/item/bodypart/affecting = get_bodypart(ran_zone(M.zone_selected))
apply_damage(damage, BRUTE, affecting)
log_combat(M, src, "attacked")
else
playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
visible_message("<span class='userdanger'>[M] has attempted to punch [src]!</span>", \
"<span class='userdanger'>[M] has attempted to punch [src]!</span>", null, COMBAT_MESSAGE_RANGE)
if ("disarm")
if (!lying)
if (prob(5))
Unconscious(40)
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
log_combat(M, src, "pushed")
visible_message("<span class='danger'>[M] has pushed down [src]!</span>", \
"<span class='userdanger'>[M] has pushed down [src]!</span>")
else
if (prob(50))
dropItemToGround(get_active_held_item())
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
visible_message("<span class='danger'>[M] has disarmed [src]!</span>", \
"<span class='userdanger'>[M] has disarmed [src]!</span>", null, COMBAT_MESSAGE_RANGE)
else
playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
visible_message("<span class='userdanger'>[M] has attempted to disarm [src]!</span>",\
"<span class='userdanger'>[M] has attempted to disarm [src]!</span>", null, COMBAT_MESSAGE_RANGE)
/mob/living/carbon/alien/humanoid/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect)
if(!no_effect && !visual_effect_icon)
visual_effect_icon = ATTACK_EFFECT_CLAW
..()
@@ -0,0 +1,5 @@
/mob/living/carbon/alien/humanoid/doUnEquip(obj/item/I)
. = ..()
if(!. || !I)
return
@@ -0,0 +1,18 @@
/mob/living/carbon/alien/humanoid/proc/adjust_body_temperature(current, loc_temp, boost)
var/temperature = current
var/difference = abs(current-loc_temp) //get difference
var/increments// = difference/10 //find how many increments apart they are
if(difference > 50)
increments = difference/5
else
increments = difference/10
var/change = increments*boost // Get the amount to change by (x per increment)
var/temp_change
if(current < loc_temp)
temperature = min(loc_temp, temperature+change)
else if(current > loc_temp)
temperature = max(loc_temp, temperature-change)
temp_change = (temperature - current)
return temp_change
@@ -0,0 +1,139 @@
/mob/living/carbon/alien/humanoid/royal
//Common stuffs for Praetorian and Queen
icon = 'icons/mob/alienqueen.dmi'
status_flags = 0
ventcrawler = VENTCRAWLER_NONE //pull over that ass too fat
unique_name = 0
pixel_x = -16
bubble_icon = "alienroyal"
mob_size = MOB_SIZE_LARGE
layer = LARGE_MOB_LAYER //above most mobs, but below speechbubbles
pressure_resistance = 200 //Because big, stompy xenos should not be blown around like paper.
butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab/xeno = 20, /obj/item/stack/sheet/animalhide/xeno = 3)
var/alt_inhands_file = 'icons/mob/alienqueen.dmi'
/mob/living/carbon/alien/humanoid/royal/can_inject()
return 0
/mob/living/carbon/alien/humanoid/royal/queen
name = "alien queen"
caste = "q"
maxHealth = 400
health = 400
icon_state = "alienq"
var/datum/action/small_sprite/smallsprite = new/datum/action/small_sprite/queen()
/mob/living/carbon/alien/humanoid/royal/queen/Initialize()
//there should only be one queen
for(var/mob/living/carbon/alien/humanoid/royal/queen/Q in GLOB.carbon_list)
if(Q == src)
continue
if(Q.stat == DEAD)
continue
if(Q.client)
name = "alien princess ([rand(1, 999)])" //if this is too cutesy feel free to change it/remove it.
break
real_name = src.name
AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/repulse/xeno(src))
AddAbility(new/obj/effect/proc_holder/alien/royal/queen/promote())
smallsprite.Grant(src)
return ..()
/mob/living/carbon/alien/humanoid/royal/queen/create_internal_organs()
internal_organs += new /obj/item/organ/alien/plasmavessel/large/queen
internal_organs += new /obj/item/organ/alien/resinspinner
internal_organs += new /obj/item/organ/alien/acid
internal_organs += new /obj/item/organ/alien/neurotoxin
internal_organs += new /obj/item/organ/alien/eggsac
..()
//Queen verbs
/obj/effect/proc_holder/alien/lay_egg
name = "Lay Egg"
desc = "Lay an egg to produce huggers to impregnate prey with."
plasma_cost = 75
check_turf = TRUE
action_icon_state = "alien_egg"
/obj/effect/proc_holder/alien/lay_egg/fire(mob/living/carbon/user)
if(locate(/obj/structure/alien/egg) in get_turf(user))
to_chat(user, "<span class='alertalien'>There's already an egg here.</span>")
return FALSE
if(!check_vent_block(user))
return FALSE
user.visible_message("<span class='alertalien'>[user] has laid an egg!</span>")
new /obj/structure/alien/egg(user.loc)
return TRUE
//Button to let queen choose her praetorian.
/obj/effect/proc_holder/alien/royal/queen/promote
name = "Create Royal Parasite"
desc = "Produce a royal parasite to grant one of your children the honor of being your Praetorian."
plasma_cost = 500 //Plasma cost used on promotion, not spawning the parasite.
action_icon_state = "alien_queen_promote"
/obj/effect/proc_holder/alien/royal/queen/promote/fire(mob/living/carbon/alien/user)
var/obj/item/queenpromote/prom
if(get_alien_type(/mob/living/carbon/alien/humanoid/royal/praetorian/))
to_chat(user, "<span class='noticealien'>You already have a Praetorian!</span>")
return 0
else
for(prom in user)
to_chat(user, "<span class='noticealien'>You discard [prom].</span>")
qdel(prom)
return 0
prom = new (user.loc)
if(!user.put_in_active_hand(prom, 1))
to_chat(user, "<span class='warning'>You must empty your hands before preparing the parasite.</span>")
return 0
else //Just in case telling the player only once is not enough!
to_chat(user, "<span class='noticealien'>Use the royal parasite on one of your children to promote her to Praetorian!</span>")
return 0
/obj/item/queenpromote
name = "\improper royal parasite"
desc = "Inject this into one of your grown children to promote her to a Praetorian!"
icon_state = "alien_medal"
item_flags = ABSTRACT | DROPDEL
icon = 'icons/mob/alien.dmi'
/obj/item/queenpromote/Initialize()
. = ..()
ADD_TRAIT(src, TRAIT_NODROP, ABSTRACT_ITEM_TRAIT)
/obj/item/queenpromote/attack(mob/living/M, mob/living/carbon/alien/humanoid/user)
if(!isalienadult(M) || isalienroyal(M))
to_chat(user, "<span class='noticealien'>You may only use this with your adult, non-royal children!</span>")
return
if(get_alien_type(/mob/living/carbon/alien/humanoid/royal/praetorian/))
to_chat(user, "<span class='noticealien'>You already have a Praetorian!</span>")
return
var/mob/living/carbon/alien/humanoid/A = M
if(A.stat == CONSCIOUS && A.mind && A.key)
if(!user.usePlasma(500))
to_chat(user, "<span class='noticealien'>You must have 500 plasma stored to use this!</span>")
return
to_chat(A, "<span class='noticealien'>The queen has granted you a promotion to Praetorian!</span>")
user.visible_message("<span class='alertalien'>[A] begins to expand, twist and contort!</span>")
var/mob/living/carbon/alien/humanoid/royal/praetorian/new_prae = new (A.loc)
A.mind.transfer_to(new_prae)
qdel(A)
qdel(src)
return
else
to_chat(user, "<span class='warning'>This child must be alert and responsive to become a Praetorian!</span>")
/obj/item/queenpromote/attack_self(mob/user)
to_chat(user, "<span class='noticealien'>You discard [src].</span>")
qdel(src)
@@ -0,0 +1,98 @@
/mob/living/carbon/alien/humanoid/update_icons()
cut_overlays()
for(var/I in overlays_standing)
add_overlay(I)
var/asleep = IsSleeping()
if(stat == DEAD)
//If we mostly took damage from fire
if(fireloss > 125)
icon_state = "alien[caste]_husked"
else
icon_state = "alien[caste]_dead"
else if((stat == UNCONSCIOUS && !asleep) || stat == SOFT_CRIT || IsKnockdown())
icon_state = "alien[caste]_unconscious"
else if(leap_on_click)
icon_state = "alien[caste]_pounce"
else if(lying || resting || asleep)
icon_state = "alien[caste]_sleep"
else if(mob_size == MOB_SIZE_LARGE)
icon_state = "alien[caste]"
if(drooling)
add_overlay("alienspit_[caste]")
else
icon_state = "alien[caste]"
if(drooling)
add_overlay("alienspit")
if(leaping)
if(alt_icon == initial(alt_icon))
var/old_icon = icon
icon = alt_icon
alt_icon = old_icon
icon_state = "alien[caste]_leap"
pixel_x = -32
pixel_y = -32
else
if(alt_icon != initial(alt_icon))
var/old_icon = icon
icon = alt_icon
alt_icon = old_icon
pixel_x = get_standard_pixel_x_offset(lying)
pixel_y = get_standard_pixel_y_offset(lying)
update_inv_hands()
update_inv_handcuffed()
/mob/living/carbon/alien/humanoid/regenerate_icons()
if(!..())
// update_icons() //Handled in update_transform(), leaving this here as a reminder
update_transform()
/mob/living/carbon/alien/humanoid/update_transform() //The old method of updating lying/standing was update_icons(). Aliens still expect that.
if(lying > 0)
lying = 90 //Anything else looks retarded
..()
update_icons()
/mob/living/carbon/alien/humanoid/update_inv_handcuffed()
remove_overlay(HANDCUFF_LAYER)
if(handcuffed)
var/cuff_icon = handcuffed.item_state
var/dmi_file = 'icons/mob/alien.dmi'
if(mob_size == MOB_SIZE_LARGE)
cuff_icon += "_[caste]"
dmi_file = 'icons/mob/alienqueen.dmi'
var/mutable_appearance/cuffs = mutable_appearance(dmi_file, cuff_icon, -HANDCUFF_LAYER)
cuffs.color = handcuffed.color
overlays_standing[HANDCUFF_LAYER] = cuffs
apply_overlay(HANDCUFF_LAYER)
//Royals have bigger sprites, so inhand things must be handled differently.
/mob/living/carbon/alien/humanoid/royal/update_inv_hands()
..()
remove_overlay(HANDS_LAYER)
var/list/hands = list()
var/obj/item/l_hand = get_item_for_held_index(1)
if(l_hand)
var/itm_state = l_hand.item_state
if(!itm_state)
itm_state = l_hand.icon_state
hands += mutable_appearance(alt_inhands_file, "[itm_state][caste]_l", -HANDS_LAYER)
var/obj/item/r_hand = get_item_for_held_index(2)
if(r_hand)
var/itm_state = r_hand.item_state
if(!itm_state)
itm_state = r_hand.icon_state
hands += mutable_appearance(alt_inhands_file, "[itm_state][caste]_r", -HANDS_LAYER)
overlays_standing[HANDS_LAYER] = hands
apply_overlay(HANDS_LAYER)
@@ -0,0 +1,22 @@
/mob/living/carbon/alien/larva/death(gibbed)
if(stat == DEAD)
return
. = ..()
update_icons()
/mob/living/carbon/alien/larva/spawn_gibs(with_bodyparts)
if(with_bodyparts)
new /obj/effect/gibspawner/larva(drop_location())
else
new /obj/effect/gibspawner/larvabodypartless(drop_location())
/mob/living/carbon/alien/larva/gib_animation()
new /obj/effect/temp_visual/gib_animation(loc, "gibbed-l")
/mob/living/carbon/alien/larva/spawn_dust()
new /obj/effect/decal/remains/xeno(loc)
/mob/living/carbon/alien/larva/dust_animation()
new /obj/effect/temp_visual/dust_animation(loc, "dust-l")
@@ -0,0 +1,113 @@
/mob/living/carbon/alien/larva/emote(act,m_type=1,message = null)
var/param = null
if (findtext(act, "-", 1, null))
var/t1 = findtext(act, "-", 1, null)
param = copytext(act, t1 + 1, length(act) + 1)
act = copytext(act, 1, t1)
var/muzzled = is_muzzled()
switch(act) //Alphabetically sorted please.
if ("burp","burps")
if (!muzzled)
message = "<span class='name'>[src]</span> burps."
m_type = 2
if ("choke","chokes")
message = "<span class='name'>[src]</span> chokes."
m_type = 2
if ("collapse","collapses")
Paralyse(2)
message = "<span class='name'>[src]</span> collapses!"
m_type = 2
if ("dance","dances")
if (!src.restrained())
message = "<span class='name'>[src]</span> dances around happily."
m_type = 1
if ("deathgasp","deathgasps")
message = "<span class='name'>[src]</span> lets out a sickly hiss of air and falls limply to the floor..."
m_type = 2
if ("drool","drools")
message = "<span class='name'>[src]</span> drools."
m_type = 1
if ("gasp","gasps")
message = "<span class='name'>[src]</span> gasps."
m_type = 2
if ("gnarl","gnarls")
if (!muzzled)
message = "<span class='name'>[src]</span> gnarls and shows its teeth.."
m_type = 2
if ("hiss","hisses")
message = "<span class='name'>[src]</span> hisses softly."
m_type = 1
if ("jump","jumps")
message = "<span class='name'>[src]</span> jumps!"
m_type = 1
if ("me")
..()
return
if ("moan","moans")
message = "<span class='name'>[src]</span> moans!"
m_type = 2
if ("nod","nods")
message = "<span class='name'>[src]</span> nods its head."
m_type = 1
if ("roar","roars")
if (!muzzled)
message = "<span class='name'>[src]</span> softly roars."
m_type = 2
if ("roll","rolls")
if (!src.restrained())
message = "<span class='name'>[src]</span> rolls."
m_type = 1
if ("scratch","scratches")
if (!src.restrained())
message = "<span class='name'>[src]</span> scratches."
m_type = 1
if ("screech","screeches") //This orignally was called scretch, changing it. -Sum99
if (!muzzled)
message = "<span class='name'>[src]</span> screeches."
m_type = 2
if ("shake","shakes")
message = "<span class='name'>[src]</span> shakes its head."
m_type = 1
if ("shiver","shivers")
message = "<span class='name'>[src]</span> shivers."
m_type = 2
if ("sign","signs")
if (!src.restrained())
message = text("<span class='name'>[src]</span> signs[].", (text2num(param) ? text(" the number []", text2num(param)) : null))
m_type = 1
if ("snore","snores")
message = "<B>[src]</B> snores."
m_type = 2
if ("sulk","sulks")
message = "<span class='name'>[src]</span> sulks down sadly."
m_type = 1
if ("sway","sways")
message = "<span class='name'>[src]</span> sways around dizzily."
m_type = 1
if ("tail")
message = "<span class='name'>[src]</span> waves its tail."
m_type = 1
if ("twitch")
message = "<span class='name'>[src]</span> twitches violently."
m_type = 1
if ("whimper","whimpers")
if (!muzzled)
message = "<span class='name'>[src]</span> whimpers."
m_type = 2
if ("help") //"The exception"
src << "Help for larva emotes. You can use these emotes with say \"*emote\":\n\nburp, choke, collapse, dance, deathgasp, drool, gasp, gnarl, hiss, jump, me, moan, nod, roll, roar, scratch, screech, shake, shiver, sign-#, sulk, sway, tail, twitch, whimper"
else
src << "<span class='info'>Unusable emote '[act]'. Say *help for a list.</span>"
if ((message && src.stat == 0))
log_emote("[name]/[key] : [message]")
if (m_type & 1)
visible_message(message)
else
audible_message(message)
return
@@ -0,0 +1,3 @@
//can't unequip since it can't equip anything
/mob/living/carbon/alien/larva/doUnEquip(obj/item/W)
return
@@ -0,0 +1,69 @@
/mob/living/carbon/alien/larva
name = "alien larva"
real_name = "alien larva"
icon_state = "larva0"
pass_flags = PASSTABLE | PASSMOB
mob_size = MOB_SIZE_SMALL
density = FALSE
hud_type = /datum/hud/larva
maxHealth = 25
health = 25
var/amount_grown = 0
var/max_grown = 100
var/time_of_birth
rotate_on_lying = 0
bodyparts = list(/obj/item/bodypart/chest/larva, /obj/item/bodypart/head/larva)
//This is fine right now, if we're adding organ specific damage this needs to be updated
/mob/living/carbon/alien/larva/Initialize()
AddAbility(new/obj/effect/proc_holder/alien/hide(null))
AddAbility(new/obj/effect/proc_holder/alien/larva_evolve(null))
. = ..()
/mob/living/carbon/alien/larva/create_internal_organs()
internal_organs += new /obj/item/organ/alien/plasmavessel/small/tiny
..()
//This needs to be fixed
/mob/living/carbon/alien/larva/Stat()
..()
if(statpanel("Status"))
stat(null, "Progress: [amount_grown]/[max_grown]")
/mob/living/carbon/alien/larva/adjustPlasma(amount)
if(stat != DEAD && amount > 0)
amount_grown = min(amount_grown + 1, max_grown)
..(amount)
//can't equip anything
/mob/living/carbon/alien/larva/attack_ui(slot_id)
return
/mob/living/carbon/alien/larva/restrained(ignore_grab)
. = 0
// new damage icon system
// now constructs damage icon for each organ from mask * damage field
/mob/living/carbon/alien/larva/show_inv(mob/user)
return
/mob/living/carbon/alien/larva/toggle_throw_mode()
return
/mob/living/carbon/alien/larva/start_pulling()
return
/mob/living/carbon/alien/larva/stripPanelUnequip(obj/item/what, mob/who)
to_chat(src, "<span class='warning'>You don't have the dexterity to do this!</span>")
return
/mob/living/carbon/alien/larva/stripPanelEquip(obj/item/what, mob/who)
to_chat(src, "<span class='warning'>You don't have the dexterity to do this!</span>")
return
@@ -0,0 +1,31 @@
/mob/living/carbon/alien/larva/attack_hand(mob/living/carbon/human/M)
if(..())
var/damage = rand(1, 9)
if (prob(90))
playsound(loc, "punch", 25, 1, -1)
log_combat(M, src, "attacked")
visible_message("<span class='danger'>[M] has kicked [src]!</span>", \
"<span class='userdanger'>[M] has kicked [src]!</span>", null, COMBAT_MESSAGE_RANGE)
if ((stat != DEAD) && (damage > 4.9))
Unconscious(rand(100,200))
var/obj/item/bodypart/affecting = get_bodypart(ran_zone(M.zone_selected))
apply_damage(damage, BRUTE, affecting)
else
playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
visible_message("<span class='danger'>[M] has attempted to kick [src]!</span>", \
"<span class='userdanger'>[M] has attempted to kick [src]!</span>", null, COMBAT_MESSAGE_RANGE)
/mob/living/carbon/alien/larva/attack_hulk(mob/living/carbon/human/user, does_attack_animation = 0)
if(user.a_intent == INTENT_HARM)
..(user, 1)
adjustBruteLoss(5 + rand(1,9))
new /datum/forced_movement(src, get_step_away(user,src, 30), 1)
return 1
/mob/living/carbon/alien/larva/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect)
if(!no_effect && !visual_effect_icon)
visual_effect_icon = ATTACK_EFFECT_BITE
..()
@@ -0,0 +1,34 @@
/mob/living/carbon/alien/larva/Life()
set invisibility = 0
if (notransform)
return
if(..()) //not dead
// GROW!
if(amount_grown < max_grown)
amount_grown++
update_icons()
/mob/living/carbon/alien/larva/update_stat()
if(status_flags & GODMODE)
return
if(stat != DEAD)
if(health<= -maxHealth || !getorgan(/obj/item/organ/brain))
death()
return
if(IsUnconscious() || IsSleeping() || getOxyLoss() > 50 || (HAS_TRAIT(src, TRAIT_DEATHCOMA)) || health <= crit_threshold)
if(stat == CONSCIOUS)
stat = UNCONSCIOUS
blind_eyes(1)
update_canmove()
else
if(stat == UNCONSCIOUS)
stat = CONSCIOUS
if(!recoveringstam)
resting = 0
adjust_blindness(-1)
update_canmove()
update_damage_hud()
update_health_hud()
@@ -0,0 +1,63 @@
/obj/effect/proc_holder/alien/hide
name = "Hide"
desc = "Allows aliens to hide beneath tables or certain items. Toggled on or off."
plasma_cost = 0
action_icon_state = "alien_hide"
/obj/effect/proc_holder/alien/hide/fire(mob/living/carbon/alien/user)
if(user.stat != CONSCIOUS)
return
if (user.layer != ABOVE_NORMAL_TURF_LAYER)
user.layer = ABOVE_NORMAL_TURF_LAYER
user.visible_message("<span class='name'>[user] scurries to the ground!</span>", \
"<span class='noticealien'>You are now hiding.</span>")
else
user.layer = MOB_LAYER
user.visible_message("[user] slowly peeks up from the ground...", \
"<span class='noticealien'>You stop hiding.</span>")
return 1
/obj/effect/proc_holder/alien/larva_evolve
name = "Evolve"
desc = "Evolve into a higher alien caste."
plasma_cost = 0
action_icon_state = "alien_evolve_larva"
/obj/effect/proc_holder/alien/larva_evolve/fire(mob/living/carbon/alien/user)
if(!islarva(user))
return
var/mob/living/carbon/alien/larva/L = user
if(L.handcuffed || L.legcuffed) // Cuffing larvas ? Eh ?
to_chat(user, "<span class='danger'>You cannot evolve when you are cuffed.</span>")
return
if(L.amount_grown >= L.max_grown) //TODO ~Carn
to_chat(L, "<span class='name'>You are growing into a beautiful alien! It is time to choose a caste.</span>")
to_chat(L, "<span class='info'>There are three to choose from:</span>")
to_chat(L, "<span class='name'>Hunters</span> <span class='info'>are the most agile caste, tasked with hunting for hosts. They are faster than a human and can even pounce, but are not much tougher than a drone.</span>")
to_chat(L, "<span class='name'>Sentinels</span> <span class='info'>are tasked with protecting the hive. With their ranged spit, invisibility, and high health, they make formidable guardians and acceptable secondhand hunters.</span>")
to_chat(L, "<span class='name'>Drones</span> <span class='info'>are the weakest and slowest of the castes, but can grow into a praetorian and then queen if no queen exists, and are vital to maintaining a hive with their resin secretion abilities.</span>")
var/alien_caste = alert(L, "Please choose which alien caste you shall belong to.",,"Hunter","Sentinel","Drone")
if(user.incapacitated()) //something happened to us while we were choosing.
return
var/mob/living/carbon/alien/humanoid/new_xeno
switch(alien_caste)
if("Hunter")
new_xeno = new /mob/living/carbon/alien/humanoid/hunter(L.loc)
if("Sentinel")
new_xeno = new /mob/living/carbon/alien/humanoid/sentinel(L.loc)
if("Drone")
new_xeno = new /mob/living/carbon/alien/humanoid/drone(L.loc)
L.alien_evolve(new_xeno)
return 0
else
to_chat(user, "<span class='danger'>You are not fully grown.</span>")
return 0
@@ -0,0 +1,31 @@
/mob/living/carbon/alien/larva/regenerate_icons()
cut_overlays()
update_icons()
/mob/living/carbon/alien/larva/update_icons()
var/state = 0
if(amount_grown > 80)
state = 2
else if(amount_grown > 50)
state = 1
if(stat == DEAD)
icon_state = "larva[state]_dead"
else if(handcuffed || legcuffed) //This should be an overlay. Who made this an icon_state?
icon_state = "larva[state]_cuff"
else if(stat == UNCONSCIOUS || lying || resting)
icon_state = "larva[state]_sleep"
else if(IsStun())
icon_state = "larva[state]_stun"
else
icon_state = "larva[state]"
/mob/living/carbon/alien/larva/update_transform() //All this is handled in update_icons()
..()
return update_icons()
/mob/living/carbon/alien/larva/update_inv_handcuffed()
update_icons() //larva icon_state changes if cuffed/uncuffed.
@@ -0,0 +1,52 @@
/mob/living/carbon/alien/Life()
findQueen()
return..()
/mob/living/carbon/alien/check_breath(datum/gas_mixture/breath)
if(status_flags & GODMODE)
return
if(!breath || (breath.total_moles() == 0))
//Aliens breathe in vaccuum
return 0
var/toxins_used = 0
var/tox_detect_threshold = 0.02
var/breath_pressure = (breath.total_moles()*R_IDEAL_GAS_EQUATION*breath.temperature)/BREATH_VOLUME
var/list/breath_gases = breath.gases
//Partial pressure of the toxins in our breath
var/Toxins_pp = (breath_gases[/datum/gas/plasma]/breath.total_moles())*breath_pressure
if(Toxins_pp > tox_detect_threshold) // Detect toxins in air
adjustPlasma(breath_gases[/datum/gas/plasma]*250)
throw_alert("alien_tox", /obj/screen/alert/alien_tox)
toxins_used = breath_gases[/datum/gas/plasma]
else
clear_alert("alien_tox")
//Breathe in toxins and out oxygen
breath_gases[/datum/gas/plasma] -= toxins_used
breath_gases[/datum/gas/oxygen] += toxins_used
GAS_GARBAGE_COLLECT(breath.gases)
//BREATH TEMPERATURE
handle_breath_temperature(breath)
/mob/living/carbon/alien/handle_status_effects()
..()
//natural reduction of movement delay due to stun.
if(move_delay_add > 0)
move_delay_add = max(0, move_delay_add - rand(1, 2))
/mob/living/carbon/alien/handle_changeling()
return
/mob/living/carbon/alien/handle_fire()//Aliens on fire code
if(..())
return
adjust_bodytemperature(BODYTEMP_HEATING_MAX) //If you're on fire, you heat up!
return
@@ -0,0 +1,4 @@
/mob/living/carbon/alien/Login()
..()
AddInfectionImages()
return
@@ -0,0 +1,4 @@
/mob/living/carbon/alien/Logout()
..()
RemoveInfectionImages()
return
@@ -0,0 +1,190 @@
/obj/item/organ/alien
icon_state = "xgibmid2"
var/list/alien_powers = list()
/obj/item/organ/alien/Initialize()
. = ..()
for(var/A in alien_powers)
if(ispath(A))
alien_powers -= A
alien_powers += new A(src)
/obj/item/organ/alien/Destroy()
QDEL_LIST(alien_powers)
return ..()
/obj/item/organ/alien/Insert(mob/living/carbon/M, special = 0)
..()
for(var/obj/effect/proc_holder/alien/P in alien_powers)
M.AddAbility(P)
/obj/item/organ/alien/Remove(mob/living/carbon/M, special = 0)
for(var/obj/effect/proc_holder/alien/P in alien_powers)
M.RemoveAbility(P)
..()
/obj/item/organ/alien/prepare_eat()
var/obj/S = ..()
S.reagents.add_reagent("sacid", 10)
return S
/obj/item/organ/alien/plasmavessel
name = "plasma vessel"
icon_state = "plasma"
w_class = WEIGHT_CLASS_NORMAL
zone = BODY_ZONE_CHEST
slot = "plasmavessel"
alien_powers = list(/obj/effect/proc_holder/alien/plant, /obj/effect/proc_holder/alien/transfer)
var/storedPlasma = 100
var/max_plasma = 250
var/heal_rate = 5
var/plasma_rate = 10
/obj/item/organ/alien/plasmavessel/prepare_eat()
var/obj/S = ..()
S.reagents.add_reagent("plasma", storedPlasma/10)
return S
/obj/item/organ/alien/plasmavessel/large
name = "large plasma vessel"
icon_state = "plasma_large"
w_class = WEIGHT_CLASS_BULKY
storedPlasma = 200
max_plasma = 500
plasma_rate = 15
/obj/item/organ/alien/plasmavessel/large/queen
plasma_rate = 20
/obj/item/organ/alien/plasmavessel/small
name = "small plasma vessel"
icon_state = "plasma_small"
w_class = WEIGHT_CLASS_SMALL
storedPlasma = 100
max_plasma = 150
plasma_rate = 5
/obj/item/organ/alien/plasmavessel/small/tiny
name = "tiny plasma vessel"
icon_state = "plasma_tiny"
w_class = WEIGHT_CLASS_TINY
max_plasma = 100
alien_powers = list(/obj/effect/proc_holder/alien/transfer)
/obj/item/organ/alien/plasmavessel/on_life()
//If there are alien weeds on the ground then heal if needed or give some plasma
if(locate(/obj/structure/alien/weeds) in owner.loc)
if(owner.health >= owner.maxHealth)
owner.adjustPlasma(plasma_rate)
else
var/heal_amt = heal_rate
if(!isalien(owner))
heal_amt *= 0.2
owner.adjustPlasma(plasma_rate*0.5)
owner.adjustBruteLoss(-heal_amt)
owner.adjustFireLoss(-heal_amt)
owner.adjustOxyLoss(-heal_amt)
owner.adjustCloneLoss(-heal_amt)
else
owner.adjustPlasma(plasma_rate * 0.1)
/obj/item/organ/alien/plasmavessel/Insert(mob/living/carbon/M, special = 0)
..()
if(isalien(M))
var/mob/living/carbon/alien/A = M
A.updatePlasmaDisplay()
/obj/item/organ/alien/plasmavessel/Remove(mob/living/carbon/M, special = 0)
..()
if(isalien(M))
var/mob/living/carbon/alien/A = M
A.updatePlasmaDisplay()
#define QUEEN_DEATH_DEBUFF_DURATION 2400
/obj/item/organ/alien/hivenode
name = "hive node"
icon_state = "hivenode"
zone = BODY_ZONE_HEAD
slot = "hivenode"
w_class = WEIGHT_CLASS_TINY
var/recent_queen_death = 0 //Indicates if the queen died recently, aliens are heavily weakened while this is active.
alien_powers = list(/obj/effect/proc_holder/alien/whisper)
/obj/item/organ/alien/hivenode/Insert(mob/living/carbon/M, special = 0)
..()
M.faction |= ROLE_ALIEN
/obj/item/organ/alien/hivenode/Remove(mob/living/carbon/M, special = 0)
M.faction -= ROLE_ALIEN
..()
//When the alien queen dies, all aliens suffer a penalty as punishment for failing to protect her.
/obj/item/organ/alien/hivenode/proc/queen_death()
if(!owner|| owner.stat == DEAD)
return
if(isalien(owner)) //Different effects for aliens than humans
to_chat(owner, "<span class='userdanger'>Your Queen has been struck down!</span>")
to_chat(owner, "<span class='danger'>You are struck with overwhelming agony! You feel confused, and your connection to the hivemind is severed.</span>")
owner.emote("roar")
owner.Stun(200) //Actually just slows them down a bit.
else if(ishuman(owner)) //Humans, being more fragile, are more overwhelmed by the mental backlash.
to_chat(owner, "<span class='danger'>You feel a splitting pain in your head, and are struck with a wave of nausea. You cannot hear the hivemind anymore!</span>")
owner.emote("scream")
owner.Knockdown(100)
owner.jitteriness += 30
owner.confused += 30
owner.stuttering += 30
recent_queen_death = 1
owner.throw_alert("alien_noqueen", /obj/screen/alert/alien_vulnerable)
addtimer(CALLBACK(src, .proc/clear_queen_death), QUEEN_DEATH_DEBUFF_DURATION)
/obj/item/organ/alien/hivenode/proc/clear_queen_death()
if(QDELETED(src)) //In case the node is deleted
return
recent_queen_death = 0
if(!owner) //In case the xeno is butchered or subjected to surgery after death.
return
to_chat(owner, "<span class='noticealien'>The pain of the queen's death is easing. You begin to hear the hivemind again.</span>")
owner.clear_alert("alien_noqueen")
#undef QUEEN_DEATH_DEBUFF_DURATION
/obj/item/organ/alien/resinspinner
name = "resin spinner"
icon_state = "stomach-x"
zone = BODY_ZONE_PRECISE_MOUTH
slot = "resinspinner"
alien_powers = list(/obj/effect/proc_holder/alien/resin)
/obj/item/organ/alien/acid
name = "acid gland"
icon_state = "acid"
zone = BODY_ZONE_PRECISE_MOUTH
slot = "acidgland"
alien_powers = list(/obj/effect/proc_holder/alien/acid)
/obj/item/organ/alien/neurotoxin
name = "neurotoxin gland"
icon_state = "neurotox"
zone = BODY_ZONE_PRECISE_MOUTH
slot = "neurotoxingland"
alien_powers = list(/obj/effect/proc_holder/alien/neurotoxin)
/obj/item/organ/alien/eggsac
name = "egg sac"
icon_state = "eggsac"
zone = BODY_ZONE_PRECISE_GROIN
slot = "eggsac"
w_class = WEIGHT_CLASS_BULKY
alien_powers = list(/obj/effect/proc_holder/alien/lay_egg)
@@ -0,0 +1,23 @@
/mob/living/proc/alien_talk(message, shown_name = real_name)
src.log_talk(message, LOG_SAY)
message = trim(message)
if(!message)
return
var/message_a = say_quote(message)
var/rendered = "<i><span class='alien'>Hivemind, <span class='name'>[shown_name]</span> <span class='message'>[message_a]</span></span></i>"
for(var/mob/S in GLOB.player_list)
if(!S.stat && S.hivecheck())
to_chat(S, rendered)
if(S in GLOB.dead_mob_list)
var/link = FOLLOW_LINK(S, src)
to_chat(S, "[link] [rendered]")
/mob/living/carbon/alien/humanoid/royal/queen/alien_talk(message, shown_name = name)
shown_name = "<FONT size = 3>[shown_name]</FONT>"
..(message, shown_name)
/mob/living/carbon/hivecheck()
var/obj/item/organ/alien/hivenode/N = getorgan(/obj/item/organ/alien/hivenode)
if(N && !N.recent_queen_death) //Mob has alien hive node and is not under the dead queen special effect.
return N
@@ -0,0 +1,33 @@
/mob/living/carbon/alien/proc/updatePlasmaDisplay()
if(hud_used) //clientless aliens
hud_used.alien_plasma_display.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'><font color='magenta'>[round(getPlasma())]</font></div>"
/mob/living/carbon/alien/larva/updatePlasmaDisplay()
return
/mob/living/carbon/alien/proc/findQueen()
if(hud_used)
hud_used.alien_queen_finder.cut_overlays()
var/mob/queen = get_alien_type(/mob/living/carbon/alien/humanoid/royal/queen)
if(!queen)
return
var/turf/Q = get_turf(queen)
var/turf/A = get_turf(src)
if(Q.z != A.z) //The queen is on a different Z level, we cannot sense that far.
return
var/Qdir = get_dir(src, Q)
var/Qdist = get_dist(src, Q)
var/finder_icon = "finder_center" //Overlay showed when adjacent to or on top of the queen!
switch(Qdist)
if(2 to 7)
finder_icon = "finder_near"
if(8 to 20)
finder_icon = "finder_med"
if(21 to INFINITY)
finder_icon = "finder_far"
var/image/finder_eye = image('icons/mob/screen_alien.dmi', finder_icon, dir = Qdir)
hud_used.alien_queen_finder.add_overlay(finder_eye)
/mob/living/carbon/alien/humanoid/royal/queen/findQueen()
return //Queen already knows where she is. Hopefully.
@@ -0,0 +1,136 @@
// This is to replace the previous datum/disease/alien_embryo for slightly improved handling and maintainability
// It functions almost identically (see code/datums/diseases/alien_embryo.dm)
/obj/item/organ/body_egg/alien_embryo
name = "alien embryo"
icon = 'icons/mob/alien.dmi'
icon_state = "larva0_dead"
var/stage = 0
var/bursting = FALSE
/obj/item/organ/body_egg/alien_embryo/on_find(mob/living/finder)
..()
if(stage < 4)
to_chat(finder, "It's small and weak, barely the size of a foetus.")
else
to_chat(finder, "It's grown quite large, and writhes slightly as you look at it.")
if(prob(10))
AttemptGrow(0)
/obj/item/organ/body_egg/alien_embryo/prepare_eat()
var/obj/S = ..()
S.reagents.add_reagent("sacid", 10)
return S
/obj/item/organ/body_egg/alien_embryo/on_life()
switch(stage)
if(2, 3)
if(prob(2))
owner.emote("sneeze")
if(prob(2))
owner.emote("cough")
if(prob(2))
to_chat(owner, "<span class='danger'>Your throat feels sore.</span>")
if(prob(2))
to_chat(owner, "<span class='danger'>Mucous runs down the back of your throat.</span>")
if(4)
if(prob(2))
owner.emote("sneeze")
if(prob(2))
owner.emote("cough")
if(prob(4))
to_chat(owner, "<span class='danger'>Your muscles ache.</span>")
if(prob(20))
owner.take_bodypart_damage(1)
if(prob(4))
to_chat(owner, "<span class='danger'>Your stomach hurts.</span>")
if(prob(20))
owner.adjustToxLoss(1)
if(5)
to_chat(owner, "<span class='danger'>You feel something tearing its way out of your stomach...</span>")
owner.adjustToxLoss(10)
/obj/item/organ/body_egg/alien_embryo/egg_process()
if(stage < 5 && prob(3))
stage++
INVOKE_ASYNC(src, .proc/RefreshInfectionImage)
if(stage == 5 && prob(50))
for(var/datum/surgery/S in owner.surgeries)
if(S.location == BODY_ZONE_CHEST && istype(S.get_surgery_step(), /datum/surgery_step/manipulate_organs))
AttemptGrow(0)
return
AttemptGrow()
/obj/item/organ/body_egg/alien_embryo/proc/AttemptGrow(var/kill_on_sucess=TRUE)
if(!owner || bursting)
return
bursting = TRUE
var/list/candidates = pollGhostCandidates("Do you want to play as an alien larva that will burst out of [owner]?", ROLE_ALIEN, null, ROLE_ALIEN, 100, POLL_IGNORE_ALIEN_LARVA)
if(QDELETED(src) || QDELETED(owner))
return
if(!candidates.len || !owner)
bursting = FALSE
stage = 4
return
var/mob/dead/observer/ghost = pick(candidates)
var/mutable_appearance/overlay = mutable_appearance('icons/mob/alien.dmi', "burst_lie")
owner.add_overlay(overlay)
var/atom/xeno_loc = get_turf(owner)
var/mob/living/carbon/alien/larva/new_xeno = new(xeno_loc)
new_xeno.key = ghost.key
SEND_SOUND(new_xeno, sound('sound/voice/hiss5.ogg',0,0,0,100)) //To get the player's attention
new_xeno.canmove = 0 //so we don't move during the bursting animation
new_xeno.notransform = 1
new_xeno.invisibility = INVISIBILITY_MAXIMUM
sleep(6)
if(QDELETED(src) || QDELETED(owner))
return
if(new_xeno)
new_xeno.canmove = 1
new_xeno.notransform = 0
new_xeno.invisibility = 0
if(kill_on_sucess) //ITS TOO LATE
new_xeno.visible_message("<span class='danger'>[new_xeno] bursts out of [owner]!</span>", "<span class='userdanger'>You exit [owner], your previous host.</span>", "<span class='italics'>You hear organic matter ripping and tearing!</span>")
owner.apply_damage(rand(100,300),BRUTE,zone,FALSE) //Random high damage to torso so health sensors don't metagame.
owner.spill_organs(TRUE,FALSE,TRUE) //Lets still make the death gruesome and impossible to just simply defib someone.
owner.death(FALSE) //Just in case some freak occurance occurs where you somehow survive all your organs being removed from you and the 100-300 brute damage.
else //When it is removed via surgery at a late stage, rather than forced.
new_xeno.visible_message("<span class='danger'>[new_xeno] wriggles out of [owner]!</span>", "<span class='userdanger'>You exit [owner], your previous host.</span>")
owner.adjustBruteLoss(40)
owner.cut_overlay(overlay)
qdel(src)
/*----------------------------------------
Proc: AddInfectionImages(C)
Des: Adds the infection image to all aliens for this embryo
----------------------------------------*/
/obj/item/organ/body_egg/alien_embryo/AddInfectionImages()
for(var/mob/living/carbon/alien/alien in GLOB.player_list)
if(alien.client)
var/I = image('icons/mob/alien.dmi', loc = owner, icon_state = "infected[stage]")
alien.client.images += I
/*----------------------------------------
Proc: RemoveInfectionImage(C)
Des: Removes all images from the mob infected by this embryo
----------------------------------------*/
/obj/item/organ/body_egg/alien_embryo/RemoveInfectionImages()
for(var/mob/living/carbon/alien/alien in GLOB.player_list)
if(alien.client)
for(var/image/I in alien.client.images)
if(dd_hasprefix_case(I.icon_state, "infected") && I.loc == owner)
qdel(I)
@@ -0,0 +1,271 @@
//TODO: Make these simple_animals
#define MIN_IMPREGNATION_TIME 100 //time it takes to impregnate someone
#define MAX_IMPREGNATION_TIME 150
#define MIN_ACTIVE_TIME 200 //time between being dropped and going idle
#define MAX_ACTIVE_TIME 400
/obj/item/clothing/mask/facehugger
name = "alien"
desc = "It has some sort of a tube at the end of its tail."
icon = 'icons/mob/alien.dmi'
icon_state = "facehugger"
item_state = "facehugger"
w_class = WEIGHT_CLASS_TINY //note: can be picked up by aliens unlike most other items of w_class below 4
clothing_flags = ALLOWINTERNALS
throw_range = 5
tint = 3
flags_cover = MASKCOVERSEYES | MASKCOVERSMOUTH
layer = MOB_LAYER
max_integrity = 100
mutantrace_variation = MUTANTRACE_VARIATION
var/stat = CONSCIOUS //UNCONSCIOUS is the idle state in this case
var/sterile = FALSE
var/real = TRUE //0 for the toy, 1 for real. Sure I could istype, but fuck that.
var/strength = 5
var/attached = 0
/obj/item/clothing/mask/facehugger/lamarr
name = "Lamarr"
sterile = TRUE
/obj/item/clothing/mask/facehugger/dead
icon_state = "facehugger_dead"
item_state = "facehugger_inactive"
sterile = TRUE
stat = DEAD
/obj/item/clothing/mask/facehugger/impregnated
icon_state = "facehugger_impregnated"
item_state = "facehugger_impregnated"
sterile = TRUE
stat = DEAD
/obj/item/clothing/mask/facehugger/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir)
..()
if(obj_integrity < 90)
Die()
/obj/item/clothing/mask/facehugger/attackby(obj/item/O, mob/user, params)
return O.attack_obj(src, user)
/obj/item/clothing/mask/facehugger/attack_alien(mob/user) //can be picked up by aliens
return attack_hand(user)
//ATTACK HAND IGNORING PARENT RETURN VALUE
/obj/item/clothing/mask/facehugger/attack_hand(mob/user)
if((stat == CONSCIOUS && !sterile) && !isalien(user))
if(Leap(user))
return
. = ..()
/obj/item/clothing/mask/facehugger/attack(mob/living/M, mob/user)
..()
if(user.transferItemToLoc(src, get_turf(M)))
Leap(M)
/obj/item/clothing/mask/facehugger/examine(mob/user)
..()
if(!real)//So that giant red text about probisci doesn't show up.
return
switch(stat)
if(DEAD,UNCONSCIOUS)
to_chat(user, "<span class='boldannounce'>[src] is not moving.</span>")
if(CONSCIOUS)
to_chat(user, "<span class='boldannounce'>[src] seems to be active!</span>")
if (sterile)
to_chat(user, "<span class='boldannounce'>It looks like the proboscis has been removed.</span>")
/obj/item/clothing/mask/facehugger/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume)
if(exposed_temperature > 300)
Die()
/obj/item/clothing/mask/facehugger/equipped(mob/M)
Attach(M)
/obj/item/clothing/mask/facehugger/Crossed(atom/target)
HasProximity(target)
return
/obj/item/clothing/mask/facehugger/on_found(mob/finder)
if(stat == CONSCIOUS)
return HasProximity(finder)
return 0
/obj/item/clothing/mask/facehugger/HasProximity(atom/movable/AM as mob|obj)
if(CanHug(AM) && Adjacent(AM))
return Leap(AM)
return 0
/obj/item/clothing/mask/facehugger/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback)
if(!..())
return
if(stat == CONSCIOUS)
icon_state = "[initial(icon_state)]_thrown"
addtimer(CALLBACK(src, .proc/clear_throw_icon_state), 15)
/obj/item/clothing/mask/facehugger/proc/clear_throw_icon_state()
if(icon_state == "[initial(icon_state)]_thrown")
icon_state = "[initial(icon_state)]"
/obj/item/clothing/mask/facehugger/throw_impact(atom/hit_atom)
..()
if(stat == CONSCIOUS)
icon_state = "[initial(icon_state)]"
Leap(hit_atom)
/obj/item/clothing/mask/facehugger/proc/valid_to_attach(mob/living/M)
// valid targets: carbons except aliens and devils
// facehugger state early exit checks
if(stat != CONSCIOUS)
return FALSE
if(attached)
return FALSE
if(iscarbon(M))
// disallowed carbons
if(isalien(M) || isdevil(M))
return FALSE
var/mob/living/carbon/target = M
// gotta have a head to be implanted (no changelings or sentient plants)
if(!target.get_bodypart(BODY_ZONE_HEAD))
return FALSE
if(target.getorgan(/obj/item/organ/alien/hivenode) || target.getorgan(/obj/item/organ/body_egg/alien_embryo))
return FALSE
// carbon, has head, not alien or devil, has no hivenode or embryo: valid
return TRUE
return FALSE
/obj/item/clothing/mask/facehugger/proc/Leap(mob/living/M)
if(!valid_to_attach(M))
return FALSE
if(iscarbon(M))
var/mob/living/carbon/target = M
if(target.wear_mask && istype(target.wear_mask, /obj/item/clothing/mask/facehugger))
return FALSE
// passed initial checks - time to leap!
M.visible_message("<span class='danger'>[src] leaps at [M]'s face!</span>", \
"<span class='userdanger'>[src] leaps at [M]'s face!</span>")
// probiscis-blocker handling
if(iscarbon(M))
var/mob/living/carbon/target = M
if(ishuman(M))
var/mob/living/carbon/human/H = M
if(H.is_mouth_covered(head_only = 1))
H.visible_message("<span class='danger'>[src] smashes against [H]'s [H.head]!</span>", \
"<span class='userdanger'>[src] smashes against [H]'s [H.head]!</span>")
Die()
return FALSE
if(target.wear_mask)
var/obj/item/clothing/W = target.wear_mask
if(target.dropItemToGround(W))
target.visible_message("<span class='danger'>[src] tears [W] off of [target]'s face!</span>", \
"<span class='userdanger'>[src] tears [W] off of [target]'s face!</span>")
target.equip_to_slot_if_possible(src, SLOT_WEAR_MASK, 0, 1, 1)
return TRUE // time for a smoke
/obj/item/clothing/mask/facehugger/proc/Attach(mob/living/M)
if(!valid_to_attach(M))
return
// early returns and validity checks done: attach.
attached++
//ensure we detach once we no longer need to be attached
addtimer(CALLBACK(src, .proc/detach), MAX_IMPREGNATION_TIME)
if(!sterile)
M.take_bodypart_damage(strength,0) //done here so that humans in helmets take damage
M.Unconscious(MAX_IMPREGNATION_TIME/0.3) //something like 25 ticks = 20 seconds with the default settings
GoIdle() //so it doesn't jump the people that tear it off
addtimer(CALLBACK(src, .proc/Impregnate, M), rand(MIN_IMPREGNATION_TIME, MAX_IMPREGNATION_TIME))
/obj/item/clothing/mask/facehugger/proc/detach()
attached = 0
/obj/item/clothing/mask/facehugger/proc/Impregnate(mob/living/target)
if(!target || target.stat == DEAD) //was taken off or something
return
if(iscarbon(target))
var/mob/living/carbon/C = target
if(C.wear_mask != src)
return
if(!sterile)
target.visible_message("<span class='danger'>[src] falls limp after violating [target]'s face!</span>", \
"<span class='userdanger'>[src] falls limp after violating [target]'s face!</span>")
Die()
icon_state = "[initial(icon_state)]_impregnated"
var/obj/item/bodypart/chest/LC = target.get_bodypart(BODY_ZONE_CHEST)
if((!LC || LC.status != BODYPART_ROBOTIC) && !target.getorgan(/obj/item/organ/body_egg/alien_embryo))
new /obj/item/organ/body_egg/alien_embryo(target)
else
target.visible_message("<span class='danger'>[src] violates [target]'s face!</span>", \
"<span class='userdanger'>[src] violates [target]'s face!</span>")
/obj/item/clothing/mask/facehugger/proc/GoActive()
if(stat == DEAD || stat == CONSCIOUS)
return
stat = CONSCIOUS
icon_state = "[initial(icon_state)]"
/obj/item/clothing/mask/facehugger/proc/GoIdle()
if(stat == DEAD || stat == UNCONSCIOUS)
return
stat = UNCONSCIOUS
icon_state = "[initial(icon_state)]_inactive"
addtimer(CALLBACK(src, .proc/GoActive), rand(MIN_ACTIVE_TIME, MAX_ACTIVE_TIME))
/obj/item/clothing/mask/facehugger/proc/Die()
if(stat == DEAD)
return
icon_state = "[initial(icon_state)]_dead"
item_state = "facehugger_inactive"
stat = DEAD
visible_message("<span class='danger'>[src] curls up into a ball!</span>")
/proc/CanHug(mob/living/M)
if(!istype(M))
return 0
if(M.stat == DEAD)
return 0
if(M.getorgan(/obj/item/organ/alien/hivenode))
return 0
if(ismonkey(M))
return 1
var/mob/living/carbon/C = M
if(ishuman(C) && !(SLOT_WEAR_MASK in C.dna.species.no_equip))
var/mob/living/carbon/human/H = C
if(H.is_mouth_covered(head_only = 1))
return 0
return 1
return 0
#undef MIN_ACTIVE_TIME
#undef MAX_ACTIVE_TIME
#undef MIN_IMPREGNATION_TIME
#undef MAX_IMPREGNATION_TIME
@@ -0,0 +1,21 @@
//Here are the procs used to modify status effects of a mob.
//The effects include: stun, knockdown, unconscious, sleeping, resting, jitteriness, dizziness, ear damage,
// eye damage, eye_blind, eye_blurry, druggy, TRAIT_BLIND trait, and TRAIT_NEARSIGHT trait.
/////////////////////////////////// STUN ////////////////////////////////////
/* CIT CHANGE - removes xeno stun immunity
/mob/living/carbon/alien/Stun(amount, updating = 1, ignore_canstun = 0)
. = ..()
if(!.)
move_delay_add = min(move_delay_add + round(amount / 2), 10) //a maximum delay of 10
/mob/living/carbon/alien/SetStun(amount, updating = 1, ignore_canstun = 0)
. = ..()
if(!.)
move_delay_add = min(move_delay_add + round(amount / 2), 10)
/mob/living/carbon/alien/AdjustStun(amount, updating = 1, ignore_canstun = 0)
. = ..()
if(!.)
move_delay_add = CLAMP(move_delay_add + round(amount/2), 0, 10)
*/
@@ -0,0 +1,11 @@
/mob/living/carbon/alien/update_damage_overlays() //aliens don't have damage overlays.
return
/mob/living/carbon/alien/update_body() // we don't use the bodyparts or body layers for aliens.
return
/mob/living/carbon/alien/update_body_parts()//we don't use the bodyparts layer for aliens.
return
+948
View File
@@ -0,0 +1,948 @@
/mob/living/carbon
blood_volume = BLOOD_VOLUME_NORMAL
/mob/living/carbon/Initialize()
. = ..()
create_reagents(1000)
update_body_parts() //to update the carbon's new bodyparts appearance
GLOB.carbon_list += src
blood_volume = (BLOOD_VOLUME_NORMAL * blood_ratio)
/mob/living/carbon/Destroy()
//This must be done first, so the mob ghosts correctly before DNA etc is nulled
. = ..()
QDEL_LIST(internal_organs)
QDEL_LIST(stomach_contents)
QDEL_LIST(bodyparts)
QDEL_LIST(implants)
remove_from_all_data_huds()
QDEL_NULL(dna)
GLOB.carbon_list -= src
/mob/living/carbon/initialize_footstep()
AddComponent(/datum/component/footstep, 0.6, 2)
/mob/living/carbon/relaymove(mob/user, direction)
if(user in src.stomach_contents)
if(prob(40))
if(prob(25))
audible_message("<span class='warning'>You hear something rumbling inside [src]'s stomach...</span>", \
"<span class='warning'>You hear something rumbling.</span>", 4,\
"<span class='userdanger'>Something is rumbling inside your stomach!</span>")
var/obj/item/I = user.get_active_held_item()
if(I && I.force)
var/d = rand(round(I.force / 4), I.force)
var/obj/item/bodypart/BP = get_bodypart(BODY_ZONE_CHEST)
if(BP.receive_damage(d, 0))
update_damage_overlays()
visible_message("<span class='danger'>[user] attacks [src]'s stomach wall with the [I.name]!</span>", \
"<span class='userdanger'>[user] attacks your stomach wall with the [I.name]!</span>")
playsound(user.loc, 'sound/effects/attackblob.ogg', 50, 1)
if(prob(src.getBruteLoss() - 50))
for(var/atom/movable/A in stomach_contents)
A.forceMove(drop_location())
stomach_contents.Remove(A)
src.gib()
/mob/living/carbon/swap_hand(held_index)
if(!held_index)
held_index = (active_hand_index % held_items.len)+1
var/obj/item/item_in_hand = src.get_active_held_item()
if(item_in_hand) //this segment checks if the item in your hand is twohanded.
var/obj/item/twohanded/TH = item_in_hand
if(istype(TH))
if(TH.wielded == 1)
to_chat(usr, "<span class='warning'>Your other hand is too busy holding [TH]</span>")
return
var/oindex = active_hand_index
active_hand_index = held_index
if(hud_used)
var/obj/screen/inventory/hand/H
H = hud_used.hand_slots["[oindex]"]
if(H)
H.update_icon()
H = hud_used.hand_slots["[held_index]"]
if(H)
H.update_icon()
/mob/living/carbon/activate_hand(selhand) //l/r OR 1-held_items.len
if(!selhand)
selhand = (active_hand_index % held_items.len)+1
if(istext(selhand))
selhand = lowertext(selhand)
if(selhand == "right" || selhand == "r")
selhand = 2
if(selhand == "left" || selhand == "l")
selhand = 1
if(selhand != active_hand_index)
swap_hand(selhand)
else
mode() // Activate held item
/mob/living/carbon/attackby(obj/item/I, mob/user, params)
if(lying && surgeries.len)
if(user != src && (user.a_intent == INTENT_HELP || user.a_intent == INTENT_DISARM))
for(var/datum/surgery/S in surgeries)
if(S.next_step(user,user.a_intent))
return 1
return ..()
/mob/living/carbon/throw_impact(atom/hit_atom, throwingdatum)
. = ..()
var/hurt = TRUE
if(istype(throwingdatum, /datum/thrownthing))
var/datum/thrownthing/D = throwingdatum
if(iscyborg(D.thrower))
var/mob/living/silicon/robot/R = D.thrower
if(!R.emagged)
hurt = FALSE
if(hit_atom.density && isturf(hit_atom))
if(hurt)
Knockdown(20)
take_bodypart_damage(10)
if(iscarbon(hit_atom) && hit_atom != src)
var/mob/living/carbon/victim = hit_atom
if(victim.movement_type & FLYING)
return
if(hurt)
victim.take_bodypart_damage(10)
take_bodypart_damage(10)
victim.Knockdown(20)
Knockdown(20)
visible_message("<span class='danger'>[src] crashes into [victim], knocking them both over!</span>",\
"<span class='userdanger'>You violently crash into [victim]!</span>")
playsound(src,'sound/weapons/punch1.ogg',50,1)
//Throwing stuff
/mob/living/carbon/proc/toggle_throw_mode()
if(stat)
return
if(in_throw_mode)
throw_mode_off()
else
throw_mode_on()
/mob/living/carbon/proc/throw_mode_off()
in_throw_mode = 0
if(client && hud_used)
hud_used.throw_icon.icon_state = "act_throw_off"
/mob/living/carbon/proc/throw_mode_on()
in_throw_mode = 1
if(client && hud_used)
hud_used.throw_icon.icon_state = "act_throw_on"
/mob/proc/throw_item(atom/target)
SEND_SIGNAL(src, COMSIG_MOB_THROW, target)
return
/mob/living/carbon/throw_item(atom/target)
throw_mode_off()
if(!target || !isturf(loc))
return
if(istype(target, /obj/screen))
return
//CIT CHANGES - makes it impossible to throw while in stamina softcrit
if(getStaminaLoss() >= STAMINA_SOFTCRIT)
to_chat(src, "<span class='warning'>You're too exhausted.</span>")
return
//END OF CIT CHANGES
var/atom/movable/thrown_thing
var/obj/item/I = src.get_active_held_item()
if(!I)
if(pulling && isliving(pulling) && grab_state >= GRAB_AGGRESSIVE)
var/mob/living/throwable_mob = pulling
if(!throwable_mob.buckled)
thrown_thing = throwable_mob
stop_pulling()
if(HAS_TRAIT(src, TRAIT_PACIFISM))
to_chat(src, "<span class='notice'>You gently let go of [throwable_mob].</span>")
adjustStaminaLossBuffered(25)//CIT CHANGE - throwing an entire person shall be very tiring
var/turf/start_T = get_turf(loc) //Get the start and target tile for the descriptors
var/turf/end_T = get_turf(target)
if(start_T && end_T)
log_combat(src, throwable_mob, "thrown", addition="grab from tile in [AREACOORD(start_T)] towards tile at [AREACOORD(end_T)]")
else if(!CHECK_BITFIELD(I.item_flags, ABSTRACT) && !HAS_TRAIT(I, TRAIT_NODROP))
thrown_thing = I
dropItemToGround(I)
if(HAS_TRAIT(src, TRAIT_PACIFISM) && I.throwforce)
to_chat(src, "<span class='notice'>You set [I] down gently on the ground.</span>")
return
adjustStaminaLossBuffered(I.getweight()*2)//CIT CHANGE - throwing items shall be more tiring than swinging em. Doubly so.
if(thrown_thing)
visible_message("<span class='danger'>[src] has thrown [thrown_thing].</span>")
src.log_message("has thrown [thrown_thing]", LOG_ATTACK)
do_attack_animation(target, no_effect = 1)
playsound(loc, 'sound/weapons/punchmiss.ogg', 50, 1, -1)
newtonian_move(get_dir(target, src))
thrown_thing.throw_at(target, thrown_thing.throw_range, thrown_thing.throw_speed, src)
/mob/living/carbon/restrained(ignore_grab)
. = (handcuffed || (!ignore_grab && pulledby && pulledby.grab_state >= GRAB_AGGRESSIVE))
/mob/living/carbon/proc/canBeHandcuffed()
return 0
/mob/living/carbon/show_inv(mob/user)
user.set_machine(src)
var/dat = {"
<HR>
<B><FONT size=3>[name]</FONT></B>
<HR>
<BR><B>Head:</B> <A href='?src=[REF(src)];item=[SLOT_HEAD]'> [(head && !(head.item_flags & ABSTRACT)) ? head : "Nothing"]</A>
<BR><B>Mask:</B> <A href='?src=[REF(src)];item=[SLOT_WEAR_MASK]'> [(wear_mask && !(wear_mask.item_flags & ABSTRACT)) ? wear_mask : "Nothing"]</A>
<BR><B>Neck:</B> <A href='?src=[REF(src)];item=[SLOT_NECK]'> [(wear_neck && !(wear_neck.item_flags & ABSTRACT)) ? wear_neck : "Nothing"]</A>"}
for(var/i in 1 to held_items.len)
var/obj/item/I = get_item_for_held_index(i)
dat += "<BR><B>[get_held_index_name(i)]:</B></td><td><A href='?src=[REF(src)];item=[SLOT_HANDS];hand_index=[i]'>[(I && !(I.item_flags & ABSTRACT)) ? I : "Nothing"]</a>"
dat += "<BR><B>Back:</B> <A href='?src=[REF(src)];item=[SLOT_BACK]'>[back ? back : "Nothing"]</A>"
if(istype(wear_mask, /obj/item/clothing/mask) && istype(back, /obj/item/tank))
dat += "<BR><A href='?src=[REF(src)];internal=1'>[internal ? "Disable Internals" : "Set Internals"]</A>"
if(handcuffed)
dat += "<BR><A href='?src=[REF(src)];item=[SLOT_HANDCUFFED]'>Handcuffed</A>"
if(legcuffed)
dat += "<BR><A href='?src=[REF(src)];item=[SLOT_LEGCUFFED]'>Legcuffed</A>"
dat += {"
<BR>
<BR><A href='?src=[REF(user)];mach_close=mob[REF(src)]'>Close</A>
"}
user << browse(dat, "window=mob[REF(src)];size=325x500")
onclose(user, "mob[REF(src)]")
/mob/living/carbon/Topic(href, href_list)
..()
//strip panel
if(usr.canUseTopic(src, BE_CLOSE, NO_DEXTERY))
if(href_list["internal"])
var/slot = text2num(href_list["internal"])
var/obj/item/ITEM = get_item_by_slot(slot)
if(ITEM && istype(ITEM, /obj/item/tank) && wear_mask && (wear_mask.clothing_flags & ALLOWINTERNALS))
visible_message("<span class='danger'>[usr] tries to [internal ? "close" : "open"] the valve on [src]'s [ITEM.name].</span>", \
"<span class='userdanger'>[usr] tries to [internal ? "close" : "open"] the valve on [src]'s [ITEM.name].</span>")
if(do_mob(usr, src, POCKET_STRIP_DELAY))
if(internal)
internal = null
update_internals_hud_icon(0)
else if(ITEM && istype(ITEM, /obj/item/tank))
if((wear_mask && (wear_mask.clothing_flags & ALLOWINTERNALS)) || getorganslot(ORGAN_SLOT_BREATHING_TUBE))
internal = ITEM
update_internals_hud_icon(1)
visible_message("<span class='danger'>[usr] [internal ? "opens" : "closes"] the valve on [src]'s [ITEM.name].</span>", \
"<span class='userdanger'>[usr] [internal ? "opens" : "closes"] the valve on [src]'s [ITEM.name].</span>")
/mob/living/carbon/fall(forced)
loc.handle_fall(src, forced)//it's loc so it doesn't call the mob's handle_fall which does nothing
/mob/living/carbon/is_muzzled()
return(istype(src.wear_mask, /obj/item/clothing/mask/muzzle))
/mob/living/carbon/hallucinating()
if(hallucination)
return TRUE
else
return FALSE
/mob/living/carbon/resist_buckle()
if(restrained())
changeNext_move(CLICK_CD_BREAKOUT)
last_special = world.time + CLICK_CD_BREAKOUT
visible_message("<span class='warning'>[src] attempts to unbuckle [p_them()]self!</span>", \
"<span class='notice'>You attempt to unbuckle yourself... (This will take around one minute and you need to stay still.)</span>")
if(do_after(src, 600, 0, target = src))
if(!buckled)
return
buckled.user_unbuckle_mob(src,src)
else
if(src && buckled)
to_chat(src, "<span class='warning'>You fail to unbuckle yourself!</span>")
else
buckled.user_unbuckle_mob(src,src)
/mob/living/carbon/resist_fire()
fire_stacks -= 5
Knockdown(60, TRUE, TRUE)
spin(32,2)
visible_message("<span class='danger'>[src] rolls on the floor, trying to put [p_them()]self out!</span>", \
"<span class='notice'>You stop, drop, and roll!</span>")
sleep(30)
if(fire_stacks <= 0)
visible_message("<span class='danger'>[src] has successfully extinguished [p_them()]self!</span>", \
"<span class='notice'>You extinguish yourself.</span>")
ExtinguishMob()
return
/mob/living/carbon/resist_restraints()
var/obj/item/I = null
var/type = 0
if(handcuffed)
I = handcuffed
type = 1
else if(legcuffed)
I = legcuffed
type = 2
if(I)
if(type == 1)
changeNext_move(CLICK_CD_BREAKOUT)
last_special = world.time + CLICK_CD_BREAKOUT
if(type == 2)
changeNext_move(CLICK_CD_RANGE)
last_special = world.time + CLICK_CD_RANGE
cuff_resist(I)
/mob/living/carbon/proc/cuff_resist(obj/item/I, breakouttime = 600, cuff_break = 0)
if(I.item_flags & BEING_REMOVED)
to_chat(src, "<span class='warning'>You're already attempting to remove [I]!</span>")
return
I.item_flags |= BEING_REMOVED
breakouttime = I.breakouttime
if(!cuff_break)
visible_message("<span class='warning'>[src] attempts to remove [I]!</span>")
to_chat(src, "<span class='notice'>You attempt to remove [I]... (This will take around [DisplayTimeText(breakouttime)] and you need to stand still.)</span>")
if(do_after(src, breakouttime, 0, target = src))
clear_cuffs(I, cuff_break)
else
to_chat(src, "<span class='warning'>You fail to remove [I]!</span>")
else if(cuff_break == FAST_CUFFBREAK)
breakouttime = 50
visible_message("<span class='warning'>[src] is trying to break [I]!</span>")
to_chat(src, "<span class='notice'>You attempt to break [I]... (This will take around 5 seconds and you need to stand still.)</span>")
if(do_after(src, breakouttime, 0, target = src))
clear_cuffs(I, cuff_break)
else
to_chat(src, "<span class='warning'>You fail to break [I]!</span>")
else if(cuff_break == INSTANT_CUFFBREAK)
clear_cuffs(I, cuff_break)
I.item_flags &= ~BEING_REMOVED
/mob/living/carbon/proc/uncuff()
if (handcuffed)
var/obj/item/W = handcuffed
handcuffed = null
if (buckled && buckled.buckle_requires_restraints)
buckled.unbuckle_mob(src)
update_handcuffed()
if (client)
client.screen -= W
if (W)
W.forceMove(drop_location())
W.dropped(src)
if (W)
W.layer = initial(W.layer)
W.plane = initial(W.plane)
changeNext_move(0)
if (legcuffed)
var/obj/item/W = legcuffed
legcuffed = null
update_inv_legcuffed()
if (client)
client.screen -= W
if (W)
W.forceMove(drop_location())
W.dropped(src)
if (W)
W.layer = initial(W.layer)
W.plane = initial(W.plane)
changeNext_move(0)
/mob/living/carbon/proc/clear_cuffs(obj/item/I, cuff_break)
if(!I.loc || buckled)
return
visible_message("<span class='danger'>[src] manages to [cuff_break ? "break" : "remove"] [I]!</span>")
to_chat(src, "<span class='notice'>You successfully [cuff_break ? "break" : "remove"] [I].</span>")
if(cuff_break)
. = !((I == handcuffed) || (I == legcuffed))
qdel(I)
return
else
if(I == handcuffed)
handcuffed.forceMove(drop_location())
handcuffed.dropped(src)
handcuffed = null
if(buckled && buckled.buckle_requires_restraints)
buckled.unbuckle_mob(src)
update_handcuffed()
return
if(I == legcuffed)
legcuffed.forceMove(drop_location())
legcuffed.dropped()
legcuffed = null
update_inv_legcuffed()
return
else
dropItemToGround(I)
return
return TRUE
/mob/living/carbon/get_standard_pixel_y_offset(lying = 0)
if(lying)
return -6
else
return initial(pixel_y)
/mob/living/carbon/proc/accident(obj/item/I)
if(!I || (I.item_flags & ABSTRACT) || HAS_TRAIT(I, TRAIT_NODROP))
return
//dropItemToGround(I) CIT CHANGE - makes it so the item doesn't drop if the modifier rolls above 100
var/modifier = 0
if(HAS_TRAIT(src, TRAIT_CLUMSY))
modifier -= 40 //Clumsy people are more likely to hit themselves -Honk!
//CIT CHANGES START HERE
else if(combatmode)
modifier += 50
if(modifier < 100)
dropItemToGround(I)
//END OF CIT CHANGES
switch(rand(1,100)+modifier) //91-100=Nothing special happens
if(-INFINITY to 0) //attack yourself
I.attack(src,src)
if(1 to 30) //throw it at yourself
I.throw_impact(src)
if(31 to 60) //Throw object in facing direction
var/turf/target = get_turf(loc)
var/range = rand(2,I.throw_range)
for(var/i = 1; i < range; i++)
var/turf/new_turf = get_step(target, dir)
target = new_turf
if(new_turf.density)
break
I.throw_at(target,I.throw_range,I.throw_speed,src)
if(61 to 90) //throw it down to the floor
var/turf/target = get_turf(loc)
I.throw_at(target,I.throw_range,I.throw_speed,src)
/mob/living/carbon/Stat()
..()
if(statpanel("Status"))
var/obj/item/organ/alien/plasmavessel/vessel = getorgan(/obj/item/organ/alien/plasmavessel)
if(vessel)
stat(null, "Plasma Stored: [vessel.storedPlasma]/[vessel.max_plasma]")
if(locate(/obj/item/assembly/health) in src)
stat(null, "Health: [health]")
add_abilities_to_panel()
/mob/living/carbon/attack_ui(slot)
if(!has_hand_for_held_index(active_hand_index))
return 0
return ..()
/mob/living/carbon/proc/vomit(lost_nutrition = 10, blood = FALSE, stun = TRUE, distance = 1, message = TRUE, toxic = FALSE)
if(HAS_TRAIT(src, TRAIT_NOHUNGER))
return 1
if(nutrition < 100 && !blood)
if(message)
visible_message("<span class='warning'>[src] dry heaves!</span>", \
"<span class='userdanger'>You try to throw up, but there's nothing in your stomach!</span>")
if(stun)
Knockdown(200)
return 1
if(is_mouth_covered()) //make this add a blood/vomit overlay later it'll be hilarious
if(message)
visible_message("<span class='danger'>[src] throws up all over [p_them()]self!</span>", \
"<span class='userdanger'>You throw up all over yourself!</span>")
SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "vomit", /datum/mood_event/vomitself)
distance = 0
else
if(message)
visible_message("<span class='danger'>[src] throws up!</span>", "<span class='userdanger'>You throw up!</span>")
if(!isflyperson(src))
SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "vomit", /datum/mood_event/vomit)
if(stun)
Stun(80)
playsound(get_turf(src), 'sound/effects/splat.ogg', 50, 1)
var/turf/T = get_turf(src)
if(!blood)
nutrition -= lost_nutrition
adjustToxLoss(-3)
for(var/i=0 to distance)
if(blood)
if(T)
add_splatter_floor(T)
if(stun)
adjustBruteLoss(3)
else if(src.reagents.has_reagent("blazaam"))
if(T)
T.add_vomit_floor(src, VOMIT_PURPLE)
else
if(T)
T.add_vomit_floor(src, VOMIT_TOXIC)//toxic barf looks different
T = get_step(T, dir)
if (is_blocked_turf(T))
break
return 1
/mob/living/carbon/proc/spew_organ(power = 5, amt = 1)
for(var/i in 1 to amt)
if(!internal_organs.len)
break //Guess we're out of organs!
var/obj/item/organ/guts = pick(internal_organs)
var/turf/T = get_turf(src)
guts.Remove(src)
guts.forceMove(T)
var/atom/throw_target = get_edge_target_turf(guts, dir)
guts.throw_at(throw_target, power, 4, src)
/mob/living/carbon/fully_replace_character_name(oldname,newname)
..()
if(dna)
dna.real_name = real_name
//Updates the mob's health from bodyparts and mob damage variables
/mob/living/carbon/updatehealth()
if(status_flags & GODMODE)
return
var/total_burn = 0
var/total_brute = 0
var/total_stamina = 0
for(var/X in bodyparts) //hardcoded to streamline things a bit
var/obj/item/bodypart/BP = X
total_brute += (BP.brute_dam * BP.body_damage_coeff)
total_burn += (BP.burn_dam * BP.body_damage_coeff)
total_stamina += (BP.stamina_dam * BP.stam_damage_coeff)
health = round(maxHealth - getOxyLoss() - getToxLoss() - getCloneLoss() - total_burn - total_brute, DAMAGE_PRECISION)
staminaloss = round(total_stamina, DAMAGE_PRECISION)
update_stat()
if(((maxHealth - total_burn) < HEALTH_THRESHOLD_DEAD) && stat == DEAD )
become_husk("burn")
med_hud_set_health()
if(stat == SOFT_CRIT)
add_movespeed_modifier(MOVESPEED_ID_CARBON_SOFTCRIT, TRUE, multiplicative_slowdown = SOFTCRIT_ADD_SLOWDOWN)
else
remove_movespeed_modifier(MOVESPEED_ID_CARBON_SOFTCRIT, TRUE)
/mob/living/carbon/update_stamina()
var/stam = getStaminaLoss()
if(stam > DAMAGE_PRECISION)
var/total_health = (health - stam)
if(total_health <= crit_threshold && !stat)
if(!IsKnockdown())
to_chat(src, "<span class='notice'>You're too exhausted to keep going...</span>")
Knockdown(100)
update_health_hud()
/mob/living/carbon/update_sight()
if(!client)
return
if(stat == DEAD)
sight = (SEE_TURFS|SEE_MOBS|SEE_OBJS)
see_in_dark = 8
see_invisible = SEE_INVISIBLE_OBSERVER
return
sight = initial(sight)
lighting_alpha = initial(lighting_alpha)
var/obj/item/organ/eyes/E = getorganslot(ORGAN_SLOT_EYES)
if(!E)
update_tint()
else
see_invisible = E.see_invisible
see_in_dark = E.see_in_dark
sight |= E.sight_flags
if(!isnull(E.lighting_alpha))
lighting_alpha = E.lighting_alpha
if(client.eye != src)
var/atom/A = client.eye
if(A.update_remote_sight(src)) //returns 1 if we override all other sight updates.
return
if(glasses)
var/obj/item/clothing/glasses/G = glasses
sight |= G.vision_flags
see_in_dark = max(G.darkness_view, see_in_dark)
if(G.invis_override)
see_invisible = G.invis_override
else
see_invisible = min(G.invis_view, see_invisible)
if(!isnull(G.lighting_alpha))
lighting_alpha = min(lighting_alpha, G.lighting_alpha)
if(dna)
for(var/X in dna.mutations)
var/datum/mutation/M = X
if(M.name == XRAY)
sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS)
see_in_dark = max(see_in_dark, 8)
if(see_override)
see_invisible = see_override
. = ..()
//to recalculate and update the mob's total tint from tinted equipment it's wearing.
/mob/living/carbon/proc/update_tint()
if(!GLOB.tinted_weldhelh)
return
tinttotal = get_total_tint()
if(tinttotal >= TINT_BLIND)
become_blind(EYES_COVERED)
else if(tinttotal >= TINT_DARKENED)
cure_blind(EYES_COVERED)
overlay_fullscreen("tint", /obj/screen/fullscreen/impaired, 2)
else
cure_blind(EYES_COVERED)
clear_fullscreen("tint", 0)
/mob/living/carbon/proc/get_total_tint()
. = 0
if(istype(head, /obj/item/clothing/head))
var/obj/item/clothing/head/HT = head
. += HT.tint
if(wear_mask)
. += wear_mask.tint
var/obj/item/organ/eyes/E = getorganslot(ORGAN_SLOT_EYES)
if(E)
. += E.tint
else
. += INFINITY
/mob/living/carbon/get_permeability_protection(list/target_zones = list(HANDS,CHEST,GROIN,LEGS,FEET,ARMS,HEAD))
var/list/tally = list()
for(var/obj/item/I in get_equipped_items())
for(var/zone in target_zones)
if(I.body_parts_covered & zone)
tally["[zone]"] = max(1 - I.permeability_coefficient, target_zones["[zone]"])
var/protection = 0
for(var/key in tally)
protection += tally[key]
protection *= INVERSE(target_zones.len)
return protection
//this handles hud updates
/mob/living/carbon/update_damage_hud()
if(!client)
return
if(health <= crit_threshold)
var/severity = 0
switch(health)
if(-20 to -10)
severity = 1
if(-30 to -20)
severity = 2
if(-40 to -30)
severity = 3
if(-50 to -40)
severity = 4
if(-50 to -40)
severity = 5
if(-60 to -50)
severity = 6
if(-70 to -60)
severity = 7
if(-90 to -70)
severity = 8
if(-95 to -90)
severity = 9
if(-INFINITY to -95)
severity = 10
if(!InFullCritical())
var/visionseverity = 4
switch(health)
if(-8 to -4)
visionseverity = 5
if(-12 to -8)
visionseverity = 6
if(-16 to -12)
visionseverity = 7
if(-20 to -16)
visionseverity = 8
if(-24 to -20)
visionseverity = 9
if(-INFINITY to -24)
visionseverity = 10
overlay_fullscreen("critvision", /obj/screen/fullscreen/crit/vision, visionseverity)
else
clear_fullscreen("critvision")
overlay_fullscreen("crit", /obj/screen/fullscreen/crit, severity)
else
clear_fullscreen("crit")
clear_fullscreen("critvision")
//Oxygen damage overlay
var/windedup = getOxyLoss() + getStaminaLoss() * 0.2 + stamdamageoverlaytemp
if(windedup)
var/severity = 0
switch(windedup)
if(10 to 20)
severity = 1
if(20 to 25)
severity = 2
if(25 to 30)
severity = 3
if(30 to 35)
severity = 4
if(35 to 40)
severity = 5
if(40 to 45)
severity = 6
if(45 to INFINITY)
severity = 7
overlay_fullscreen("oxy", /obj/screen/fullscreen/oxy, severity)
else
clear_fullscreen("oxy")
//Fire and Brute damage overlay (BSSR)
var/hurtdamage = getBruteLoss() + getFireLoss() + damageoverlaytemp
if(hurtdamage)
var/severity = 0
switch(hurtdamage)
if(5 to 15)
severity = 1
if(15 to 30)
severity = 2
if(30 to 45)
severity = 3
if(45 to 70)
severity = 4
if(70 to 85)
severity = 5
if(85 to INFINITY)
severity = 6
overlay_fullscreen("brute", /obj/screen/fullscreen/brute, severity)
else
clear_fullscreen("brute")
/mob/living/carbon/update_health_hud(shown_health_amount)
if(!client || !hud_used)
return
if(hud_used.healths)
if(stat != DEAD)
. = 1
if(!shown_health_amount)
shown_health_amount = health
if(shown_health_amount >= maxHealth)
hud_used.healths.icon_state = "health0"
else if(shown_health_amount > maxHealth*0.8)
hud_used.healths.icon_state = "health1"
else if(shown_health_amount > maxHealth*0.6)
hud_used.healths.icon_state = "health2"
else if(shown_health_amount > maxHealth*0.4)
hud_used.healths.icon_state = "health3"
else if(shown_health_amount > maxHealth*0.2)
hud_used.healths.icon_state = "health4"
else if(shown_health_amount > 0)
hud_used.healths.icon_state = "health5"
else
hud_used.healths.icon_state = "health6"
else
hud_used.healths.icon_state = "health7"
/mob/living/carbon/proc/update_internals_hud_icon(internal_state = 0)
if(hud_used && hud_used.internals)
hud_used.internals.icon_state = "internal[internal_state]"
/mob/living/carbon/update_stat()
if(status_flags & GODMODE)
return
if(stat != DEAD)
if(health <= HEALTH_THRESHOLD_DEAD && !HAS_TRAIT(src, TRAIT_NODEATH))
death()
return
if(IsUnconscious() || IsSleeping() || getOxyLoss() > 50 || (HAS_TRAIT(src, TRAIT_DEATHCOMA)) || (health <= HEALTH_THRESHOLD_FULLCRIT && !HAS_TRAIT(src, TRAIT_NOHARDCRIT)))
stat = UNCONSCIOUS
blind_eyes(1)
else
if(health <= crit_threshold && !HAS_TRAIT(src, TRAIT_NOSOFTCRIT))
stat = SOFT_CRIT
else
stat = CONSCIOUS
adjust_blindness(-1)
update_canmove()
update_damage_hud()
update_health_hud()
med_hud_set_status()
//called when we get cuffed/uncuffed
/mob/living/carbon/proc/update_handcuffed()
if(handcuffed)
drop_all_held_items()
stop_pulling()
throw_alert("handcuffed", /obj/screen/alert/restrained/handcuffed, new_master = src.handcuffed)
SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "handcuffed", /datum/mood_event/handcuffed)
else
clear_alert("handcuffed")
SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "handcuffed")
update_action_buttons_icon() //some of our action buttons might be unusable when we're handcuffed.
update_inv_handcuffed()
update_hud_handcuffed()
/mob/living/carbon/fully_heal(admin_revive = FALSE)
if(reagents)
reagents.clear_reagents()
var/obj/item/organ/brain/B = getorgan(/obj/item/organ/brain)
if(B)
B.damaged_brain = FALSE
for(var/thing in diseases)
var/datum/disease/D = thing
if(D.severity != DISEASE_SEVERITY_POSITIVE)
D.cure(FALSE)
if(admin_revive)
regenerate_limbs()
regenerate_organs()
handcuffed = initial(handcuffed)
for(var/obj/item/restraints/R in contents) //actually remove cuffs from inventory
qdel(R)
update_handcuffed()
if(reagents)
reagents.addiction_list = list()
cure_all_traumas(TRAUMA_RESILIENCE_MAGIC)
..()
// heal ears after healing traits, since ears check TRAIT_DEAF trait
// when healing.
restoreEars()
/mob/living/carbon/can_be_revived()
. = ..()
if(!getorgan(/obj/item/organ/brain) && (!mind || !mind.has_antag_datum(/datum/antagonist/changeling)))
return 0
/mob/living/carbon/harvest(mob/living/user)
if(QDELETED(src))
return
var/organs_amt = 0
for(var/X in internal_organs)
var/obj/item/organ/O = X
if(prob(50))
organs_amt++
O.Remove(src)
O.forceMove(drop_location())
if(organs_amt)
to_chat(user, "<span class='notice'>You retrieve some of [src]\'s internal organs!</span>")
/mob/living/carbon/ExtinguishMob()
for(var/X in get_equipped_items())
var/obj/item/I = X
I.acid_level = 0 //washes off the acid on our clothes
I.extinguish() //extinguishes our clothes
..()
/mob/living/carbon/fakefire(var/fire_icon = "Generic_mob_burning")
var/mutable_appearance/new_fire_overlay = mutable_appearance('icons/mob/OnFire.dmi', fire_icon, -FIRE_LAYER)
new_fire_overlay.appearance_flags = RESET_COLOR
overlays_standing[FIRE_LAYER] = new_fire_overlay
apply_overlay(FIRE_LAYER)
/mob/living/carbon/fakefireextinguish()
remove_overlay(FIRE_LAYER)
/mob/living/carbon/proc/devour_mob(mob/living/carbon/C, devour_time = 130)
C.visible_message("<span class='danger'>[src] is attempting to devour [C]!</span>", \
"<span class='userdanger'>[src] is attempting to devour you!</span>")
if(!do_mob(src, C, devour_time))
return
if(pulling && pulling == C && grab_state >= GRAB_AGGRESSIVE && a_intent == INTENT_GRAB)
C.visible_message("<span class='danger'>[src] devours [C]!</span>", \
"<span class='userdanger'>[src] devours you!</span>")
C.forceMove(src)
stomach_contents.Add(C)
log_combat(src, C, "devoured")
/mob/living/carbon/proc/create_bodyparts()
var/l_arm_index_next = -1
var/r_arm_index_next = 0
for(var/X in bodyparts)
var/obj/item/bodypart/O = new X()
O.owner = src
bodyparts.Remove(X)
bodyparts.Add(O)
if(O.body_part == ARM_LEFT)
l_arm_index_next += 2
O.held_index = l_arm_index_next //1, 3, 5, 7...
hand_bodyparts += O
else if(O.body_part == ARM_RIGHT)
r_arm_index_next += 2
O.held_index = r_arm_index_next //2, 4, 6, 8...
hand_bodyparts += O
/mob/living/carbon/do_after_coefficent()
. = ..()
GET_COMPONENT_FROM(mood, /datum/component/mood, src) //Currently, only carbons or higher use mood, move this once that changes.
if(mood)
switch(mood.sanity) //Alters do_after delay based on how sane you are
if(SANITY_INSANE to SANITY_DISTURBED)
. *= 1.25
if(SANITY_NEUTRAL to SANITY_GREAT)
. *= 0.90
/mob/living/carbon/proc/create_internal_organs()
for(var/X in internal_organs)
var/obj/item/organ/I = X
I.Insert(src)
/mob/living/carbon/proc/update_disabled_bodyparts()
for(var/B in bodyparts)
var/obj/item/bodypart/BP = B
BP.update_disabled()
/mob/living/carbon/vv_get_dropdown()
. = ..()
. += "---"
.["Make AI"] = "?_src_=vars;[HrefToken()];makeai=[REF(src)]"
.["Modify bodypart"] = "?_src_=vars;[HrefToken()];editbodypart=[REF(src)]"
.["Modify organs"] = "?_src_=vars;[HrefToken()];editorgans=[REF(src)]"
.["Hallucinate"] = "?_src_=vars;[HrefToken()];hallucinate=[REF(src)]"
.["Give martial arts"] = "?_src_=vars;[HrefToken()];givemartialart=[REF(src)]"
.["Give brain trauma"] = "?_src_=vars;[HrefToken()];givetrauma=[REF(src)]"
.["Cure brain traumas"] = "?_src_=vars;[HrefToken()];curetraumas=[REF(src)]"
/mob/living/carbon/can_resist()
return bodyparts.len > 2 && ..()
/mob/living/carbon/proc/hypnosis_vulnerable()//unused atm, but added in case
if(HAS_TRAIT(src, TRAIT_MINDSHIELD))
return FALSE
if(hallucinating())
return TRUE
if(IsSleeping())
return TRUE
if(HAS_TRAIT(src, TRAIT_DUMB))
return TRUE
GET_COMPONENT_FROM(mood, /datum/component/mood, src)
if(mood)
if(mood.sanity < SANITY_UNSTABLE)
return TRUE
@@ -0,0 +1,442 @@
/mob/living/carbon/get_eye_protection()
var/number = ..()
if(istype(src.head, /obj/item/clothing/head)) //are they wearing something on their head
var/obj/item/clothing/head/HFP = src.head //if yes gets the flash protection value from that item
number += HFP.flash_protect
if(istype(src.glasses, /obj/item/clothing/glasses)) //glasses
var/obj/item/clothing/glasses/GFP = src.glasses
number += GFP.flash_protect
if(istype(src.wear_mask, /obj/item/clothing/mask)) //mask
var/obj/item/clothing/mask/MFP = src.wear_mask
number += MFP.flash_protect
var/obj/item/organ/eyes/E = getorganslot(ORGAN_SLOT_EYES)
if(!E)
number = INFINITY //Can't get flashed without eyes
else
number += E.flash_protect
return number
/mob/living/carbon/get_ear_protection()
var/number = ..()
var/obj/item/organ/ears/E = getorganslot(ORGAN_SLOT_EARS)
if(!E)
number = INFINITY
else
number += E.bang_protect
return number
/mob/living/carbon/is_mouth_covered(head_only = 0, mask_only = 0)
if( (!mask_only && head && (head.flags_cover & HEADCOVERSMOUTH)) || (!head_only && wear_mask && (wear_mask.flags_cover & MASKCOVERSMOUTH)) )
return TRUE
/mob/living/carbon/is_eyes_covered(check_glasses = 1, check_head = 1, check_mask = 1)
if(check_glasses && glasses && (glasses.flags_cover & GLASSESCOVERSEYES))
return TRUE
if(check_head && head && (head.flags_cover & HEADCOVERSEYES))
return TRUE
if(check_mask && wear_mask && (wear_mask.flags_cover & MASKCOVERSMOUTH))
return TRUE
/mob/living/carbon/check_projectile_dismemberment(obj/item/projectile/P, def_zone)
var/obj/item/bodypart/affecting = get_bodypart(def_zone)
if(affecting && affecting.dismemberable && affecting.get_damage() >= (affecting.max_damage - P.dismemberment))
affecting.dismember(P.damtype)
/mob/living/carbon/proc/can_catch_item(skip_throw_mode_check)
. = FALSE
if(!skip_throw_mode_check && !in_throw_mode)
return
if(get_active_held_item())
return
if(restrained())
return
return TRUE
/mob/living/carbon/hitby(atom/movable/AM, skipcatch, hitpush = TRUE, blocked = FALSE)
if(!skipcatch) //ugly, but easy
if(can_catch_item())
if(istype(AM, /obj/item))
var/obj/item/I = AM
if(isturf(I.loc))
I.attack_hand(src)
if(get_active_held_item() == I) //if our attack_hand() picks up the item...
visible_message("<span class='warning'>[src] catches [I]!</span>") //catch that sucker!
throw_mode_off()
return 1
..()
/mob/living/carbon/attacked_by(obj/item/I, mob/living/user)
var/obj/item/bodypart/affecting
if(user == src)
affecting = get_bodypart(check_zone(user.zone_selected)) //we're self-mutilating! yay!
else
affecting = get_bodypart(ran_zone(user.zone_selected))
if(!affecting) //missing limb? we select the first bodypart (you can never have zero, because of chest)
affecting = bodyparts[1]
SEND_SIGNAL(I, COMSIG_ITEM_ATTACK_ZONE, src, user, affecting)
send_item_attack_message(I, user, affecting.name)
if(I.force)
//CIT CHANGES START HERE - combatmode and resting checks
var/totitemdamage = I.force
if(iscarbon(user))
var/mob/living/carbon/tempcarb = user
if(!tempcarb.combatmode)
totitemdamage *= 0.5
if(user.resting)
totitemdamage *= 0.5
if(!combatmode)
totitemdamage *= 1.5
//CIT CHANGES END HERE
apply_damage(totitemdamage, I.damtype, affecting) //CIT CHANGE - replaces I.force with totitemdamage
if(I.damtype == BRUTE && affecting.status == BODYPART_ORGANIC)
var/basebloodychance = affecting.brute_dam + totitemdamage
if(prob(basebloodychance))
I.add_mob_blood(src)
bleed(totitemdamage)
if(totitemdamage >= 10 && get_dist(user, src) <= 1) //people with TK won't get smeared with blood
user.add_mob_blood(src)
if(affecting.body_zone == BODY_ZONE_HEAD)
if(wear_mask && prob(basebloodychance))
wear_mask.add_mob_blood(src)
update_inv_wear_mask()
if(wear_neck && prob(basebloodychance))
wear_neck.add_mob_blood(src)
update_inv_neck()
if(head && prob(basebloodychance))
head.add_mob_blood(src)
update_inv_head()
//dismemberment
var/probability = I.get_dismemberment_chance(affecting)
if(prob(probability))
if(affecting.dismember(I.damtype))
I.add_mob_blood(src)
playsound(get_turf(src), I.get_dismember_sound(), 80, 1)
return TRUE //successful attack
/mob/living/carbon/attack_drone(mob/living/simple_animal/drone/user)
return //so we don't call the carbon's attack_hand().
//ATTACK HAND IGNORING PARENT RETURN VALUE
/mob/living/carbon/attack_hand(mob/living/carbon/human/user)
for(var/thing in diseases)
var/datum/disease/D = thing
if(D.spread_flags & DISEASE_SPREAD_CONTACT_SKIN)
user.ContactContractDisease(D)
for(var/thing in user.diseases)
var/datum/disease/D = thing
if(D.spread_flags & DISEASE_SPREAD_CONTACT_SKIN)
ContactContractDisease(D)
if(lying && surgeries.len)
if(user.a_intent == INTENT_HELP || user.a_intent == INTENT_DISARM)
for(var/datum/surgery/S in surgeries)
if(S.next_step(user, user.a_intent))
return 1
return 0
/mob/living/carbon/attack_paw(mob/living/carbon/monkey/M)
if(can_inject(M, TRUE))
for(var/thing in diseases)
var/datum/disease/D = thing
if((D.spread_flags & DISEASE_SPREAD_CONTACT_SKIN) && prob(85))
M.ContactContractDisease(D)
for(var/thing in M.diseases)
var/datum/disease/D = thing
if(D.spread_flags & DISEASE_SPREAD_CONTACT_SKIN)
ContactContractDisease(D)
if(M.a_intent == INTENT_HELP)
help_shake_act(M)
return 0
if(..()) //successful monkey bite.
for(var/thing in M.diseases)
var/datum/disease/D = thing
ForceContractDisease(D)
return 1
/mob/living/carbon/attack_slime(mob/living/simple_animal/slime/M)
if(..()) //successful slime attack
if(M.powerlevel > 0)
var/stunprob = M.powerlevel * 7 + 10 // 17 at level 1, 80 at level 10
if(prob(stunprob))
M.powerlevel -= 3
if(M.powerlevel < 0)
M.powerlevel = 0
visible_message("<span class='danger'>The [M.name] has shocked [src]!</span>", \
"<span class='userdanger'>The [M.name] has shocked [src]!</span>")
do_sparks(5, TRUE, src)
var/power = M.powerlevel + rand(0,3)
Knockdown(power*20)
if(stuttering < power)
stuttering = power
if (prob(stunprob) && M.powerlevel >= 8)
adjustFireLoss(M.powerlevel * rand(6,10))
updatehealth()
return 1
/mob/living/carbon/proc/dismembering_strike(mob/living/attacker, dam_zone)
if(!attacker.limb_destroyer)
return dam_zone
var/obj/item/bodypart/affecting
if(dam_zone && attacker.client)
affecting = get_bodypart(ran_zone(dam_zone))
else
var/list/things_to_ruin = shuffle(bodyparts.Copy())
for(var/B in things_to_ruin)
var/obj/item/bodypart/bodypart = B
if(bodypart.body_zone == BODY_ZONE_HEAD || bodypart.body_zone == BODY_ZONE_CHEST)
continue
if(!affecting || ((affecting.get_damage() / affecting.max_damage) < (bodypart.get_damage() / bodypart.max_damage)))
affecting = bodypart
if(affecting)
dam_zone = affecting.body_zone
if(affecting.get_damage() >= affecting.max_damage)
affecting.dismember()
return null
return affecting.body_zone
return dam_zone
/mob/living/carbon/blob_act(obj/structure/blob/B)
if (stat == DEAD)
return
else
show_message("<span class='userdanger'>The blob attacks!</span>")
adjustBruteLoss(10)
/mob/living/carbon/emp_act(severity)
. = ..()
if(. & EMP_PROTECT_CONTENTS)
return
for(var/X in internal_organs)
var/obj/item/organ/O = X
O.emp_act(severity)
/mob/living/carbon/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, override = 0, tesla_shock = 0, illusion = 0, stun = TRUE)
if(tesla_shock && (flags_1 & TESLA_IGNORE_1))
return FALSE
if(HAS_TRAIT(src, TRAIT_SHOCKIMMUNE))
return FALSE
shock_damage *= siemens_coeff
if(dna && dna.species)
shock_damage *= dna.species.siemens_coeff
if(shock_damage<1 && !override)
return 0
if(reagents.has_reagent("teslium"))
shock_damage *= 1.5 //If the mob has teslium in their body, shocks are 50% more damaging!
if(illusion)
adjustStaminaLoss(shock_damage)
else
take_overall_damage(0,shock_damage)
visible_message(
"<span class='danger'>[src] was shocked by \the [source]!</span>", \
"<span class='userdanger'>You feel a powerful shock coursing through your body!</span>", \
"<span class='italics'>You hear a heavy electrical crack.</span>" \
)
jitteriness += 1000 //High numbers for violent convulsions
do_jitter_animation(jitteriness)
stuttering += 2
if((!tesla_shock || (tesla_shock && siemens_coeff > 0.5)) && stun)
Stun(40)
spawn(20)
jitteriness = max(jitteriness - 990, 10) //Still jittery, but vastly less
if((!tesla_shock || (tesla_shock && siemens_coeff > 0.5)) && stun)
Knockdown(60)
if(override)
return override
else
return shock_damage
/mob/living/carbon/proc/help_shake_act(mob/living/carbon/M)
if(on_fire)
to_chat(M, "<span class='warning'>You can't put [p_them()] out with just your bare hands!</span>")
return
if(health >= 0 && !(HAS_TRAIT(src, TRAIT_FAKEDEATH)))
if(lying)
if(buckled)
to_chat(M, "<span class='warning'>You need to unbuckle [src] first to do that!")
return
M.visible_message("<span class='notice'>[M] shakes [src] trying to get [p_them()] up!</span>", \
"<span class='notice'>You shake [src] trying to get [p_them()] up!</span>")
else if(check_zone(M.zone_selected) == "head")
var/mob/living/carbon/human/H = src
var/datum/species/pref_species = H.dna.species
M.visible_message("<span class='notice'>[M] gives [H] a pat on the head to make [p_them()] feel better!</span>", \
"<span class='notice'>You give [H] a pat on the head to make [p_them()] feel better!</span>")
SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "headpat", /datum/mood_event/headpat)
if(HAS_TRAIT(M, TRAIT_FRIENDLY))
GET_COMPONENT_FROM(mood, /datum/component/mood, M)
if (mood.sanity >= SANITY_GREAT)
SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "friendly_hug", /datum/mood_event/besthug, M)
else if (mood.sanity >= SANITY_DISTURBED)
SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "friendly_hug", /datum/mood_event/betterhug, M)
if(H.dna.species.can_wag_tail(H))
if("tail_human" in pref_species.default_features)
if(H.dna.features["tail_human"] == "None")
return
else
if(!H.dna.species.is_wagging_tail())
H.emote("wag")
if("tail_lizard" in pref_species.default_features)
if(H.dna.features["tail_lizard"] == "None")
return
else
if(!H.dna.species.is_wagging_tail())
H.emote("wag")
if("mam_tail" in pref_species.default_features)
if(H.dna.features["mam_tail"] == "None")
return
else
if(!H.dna.species.is_wagging_tail())
H.emote("wag")
else
return
else
M.visible_message("<span class='notice'>[M] hugs [src] to make [p_them()] feel better!</span>", \
"<span class='notice'>You hug [src] to make [p_them()] feel better!</span>")
SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "hug", /datum/mood_event/hug)
if(HAS_TRAIT(M, TRAIT_FRIENDLY))
GET_COMPONENT_FROM(mood, /datum/component/mood, M)
if (mood.sanity >= SANITY_GREAT)
SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "friendly_hug", /datum/mood_event/besthug, M)
else if (mood.sanity >= SANITY_DISTURBED)
SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "friendly_hug", /datum/mood_event/betterhug, M)
AdjustStun(-60)
AdjustKnockdown(-60)
AdjustUnconscious(-60)
AdjustSleeping(-100)
if(recoveringstam)
adjustStaminaLoss(-15)
else if(resting)
resting = 0
update_canmove()
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
/mob/living/carbon/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0)
. = ..()
var/damage = intensity - get_eye_protection()
if(.) // we've been flashed
var/obj/item/organ/eyes/eyes = getorganslot(ORGAN_SLOT_EYES)
if (!eyes)
return
if(visual)
return
if (damage == 1)
to_chat(src, "<span class='warning'>Your eyes sting a little.</span>")
if(prob(40))
adjust_eye_damage(1)
else if (damage == 2)
to_chat(src, "<span class='warning'>Your eyes burn.</span>")
adjust_eye_damage(rand(2, 4))
else if( damage >= 3)
to_chat(src, "<span class='warning'>Your eyes itch and burn severely!</span>")
adjust_eye_damage(rand(12, 16))
if(eyes.eye_damage > 10)
blind_eyes(damage)
blur_eyes(damage * rand(3, 6))
if(eyes.eye_damage > 20)
if(prob(eyes.eye_damage - 20))
if(!HAS_TRAIT(src, TRAIT_NEARSIGHT))
to_chat(src, "<span class='warning'>Your eyes start to burn badly!</span>")
become_nearsighted(EYE_DAMAGE)
else if(prob(eyes.eye_damage - 25))
if(!HAS_TRAIT(src, TRAIT_BLIND))
to_chat(src, "<span class='warning'>You can't see anything!</span>")
become_blind(EYE_DAMAGE)
else
to_chat(src, "<span class='warning'>Your eyes are really starting to hurt. This can't be good for you!</span>")
if(has_bane(BANE_LIGHT))
mind.disrupt_spells(-500)
return 1
else if(damage == 0) // just enough protection
if(prob(20))
to_chat(src, "<span class='notice'>Something bright flashes in the corner of your vision!</span>")
if(has_bane(BANE_LIGHT))
mind.disrupt_spells(0)
/mob/living/carbon/soundbang_act(intensity = 1, stun_pwr = 20, damage_pwr = 5, deafen_pwr = 15)
var/list/reflist = list(intensity) // Need to wrap this in a list so we can pass a reference
SEND_SIGNAL(src, COMSIG_CARBON_SOUNDBANG, reflist)
intensity = reflist[1]
var/ear_safety = get_ear_protection()
var/obj/item/organ/ears/ears = getorganslot(ORGAN_SLOT_EARS)
var/effect_amount = intensity - ear_safety
if(effect_amount > 0)
if(stun_pwr)
Knockdown(stun_pwr*effect_amount)
if(istype(ears) && (deafen_pwr || damage_pwr))
var/ear_damage = damage_pwr * effect_amount
var/deaf = max(ears.deaf, deafen_pwr * effect_amount)
adjustEarDamage(ear_damage,deaf)
if(ears.ear_damage >= 15)
to_chat(src, "<span class='warning'>Your ears start to ring badly!</span>")
if(prob(ears.ear_damage - 5))
to_chat(src, "<span class='userdanger'>You can't hear anything!</span>")
ears.ear_damage = min(ears.ear_damage, UNHEALING_EAR_DAMAGE)
// you need earmuffs, inacusiate, or replacement
else if(ears.ear_damage >= 5)
to_chat(src, "<span class='warning'>Your ears start to ring!</span>")
SEND_SOUND(src, sound('sound/weapons/flash_ring.ogg',0,1,0,250))
return effect_amount //how soundbanged we are
/mob/living/carbon/damage_clothes(damage_amount, damage_type = BRUTE, damage_flag = 0, def_zone)
if(damage_type != BRUTE && damage_type != BURN)
return
damage_amount *= 0.5 //0.5 multiplier for balance reason, we don't want clothes to be too easily destroyed
if(!def_zone || def_zone == BODY_ZONE_HEAD)
var/obj/item/clothing/hit_clothes
if(wear_mask)
hit_clothes = wear_mask
if(wear_neck)
hit_clothes = wear_neck
if(head)
hit_clothes = head
if(hit_clothes)
hit_clothes.take_damage(damage_amount, damage_type, damage_flag, 0)
/mob/living/carbon/can_hear()
. = FALSE
var/obj/item/organ/ears/ears = getorganslot(ORGAN_SLOT_EARS)
if(istype(ears) && !ears.deaf)
. = TRUE
@@ -0,0 +1,66 @@
/mob/living/carbon
gender = MALE
pressure_resistance = 15
possible_a_intents = list(INTENT_HELP, INTENT_HARM)
hud_possible = list(HEALTH_HUD,STATUS_HUD,ANTAG_HUD,GLAND_HUD,NANITE_HUD,DIAG_NANITE_FULL_HUD)
has_limbs = 1
held_items = list(null, null)
var/list/stomach_contents = list()
var/list/internal_organs = list() //List of /obj/item/organ in the mob. They don't go in the contents for some reason I don't want to know.
var/list/internal_organs_slot= list() //Same as above, but stores "slot ID" - "organ" pairs for easy access.
var/silent = FALSE //Can't talk. Value goes down every life proc. //NOTE TO FUTURE CODERS: DO NOT INITIALIZE NUMERICAL VARS AS NULL OR I WILL MURDER YOU.
var/dreaming = 0 //How many dream images we have left to send
var/obj/item/restraints/handcuffed //Whether or not the mob is handcuffed
var/obj/item/restraints/legcuffed //Same as handcuffs but for legs. Bear traps use this.
var/disgust = 0
//inventory slots
var/obj/item/back = null
var/obj/item/clothing/mask/wear_mask = null
var/obj/item/clothing/neck/wear_neck = null
var/obj/item/tank/internal = null
var/obj/item/head = null
var/obj/item/gloves = null //only used by humans
var/obj/item/shoes = null //only used by humans.
var/obj/item/clothing/glasses/glasses = null //only used by humans.
var/obj/item/ears = null //only used by humans.
var/datum/dna/dna = null//Carbon
var/datum/mind/last_mind = null //last mind to control this mob, for blood-based cloning
var/failed_last_breath = 0 //This is used to determine if the mob failed a breath. If they did fail a brath, they will attempt to breathe each tick, otherwise just once per 4 ticks.
var/co2overloadtime = null
var/o2overloadtime = null //for Ash walker's weaker lungs, and future atmosia hazards
var/temperature_resistance = T0C+75
var/obj/item/reagent_containers/food/snacks/meat/slab/type_of_meat = /obj/item/reagent_containers/food/snacks/meat/slab
var/gib_type = /obj/effect/decal/cleanable/blood/gibs
var/rotate_on_lying = 1
var/tinttotal = 0 // Total level of visualy impairing items
var/list/bodyparts = list(/obj/item/bodypart/chest, /obj/item/bodypart/head, /obj/item/bodypart/l_arm,
/obj/item/bodypart/r_arm, /obj/item/bodypart/r_leg, /obj/item/bodypart/l_leg)
//Gets filled up in create_bodyparts()
var/list/hand_bodyparts = list() //a collection of arms (or actually whatever the fug /bodyparts you monsters use to wreck my systems)
var/icon_render_key = ""
var/static/list/limb_icon_cache = list()
//halucination vars
var/image/halimage
var/image/halbody
var/obj/halitem
var/hal_screwyhud = SCREWYHUD_NONE
var/next_hallucination = 0
var/cpr_time = 1 //CPR cooldown.
var/damageoverlaytemp = 0
var/stamdamageoverlaytemp = 0
var/drunkenness = 0 //Overall drunkenness - check handle_alcohol() in life.dm for effects
@@ -0,0 +1,45 @@
/mob/living/carbon/movement_delay()
. = ..()
. += grab_state * 3 //can't go fast while grabbing something.
if(!get_leg_ignore()) //ignore the fact we lack legs
var/leg_amount = get_num_legs()
. += 6 - 3*leg_amount //the fewer the legs, the slower the mob
if(!leg_amount)
. += 6 - 3*get_num_arms() //crawling is harder with fewer arms
if(legcuffed)
. += legcuffed.slowdown
if(stat == SOFT_CRIT)
. += SOFTCRIT_ADD_SLOWDOWN
/mob/living/carbon/slip(knockdown_amount, obj/O, lube)
if(movement_type & FLYING)
return 0
if(!(lube&SLIDE_ICE))
log_combat(src, (O ? O : get_turf(src)), "slipped on the", null, ((lube & SLIDE) ? "(LUBE)" : null))
return loc.handle_slip(src, knockdown_amount, O, lube)
/mob/living/carbon/Process_Spacemove(movement_dir = 0)
if(..())
return 1
if(!isturf(loc))
return 0
// Do we have a jetpack implant (and is it on)?
var/obj/item/organ/cyberimp/chest/thrusters/T = getorganslot(ORGAN_SLOT_THRUSTERS)
if(istype(T) && movement_dir && T.allow_thrust(0.01))
return 1
var/obj/item/tank/jetpack/J = get_jetpack()
if(istype(J) && (movement_dir || J.stabilizers) && J.allow_thrust(0.01, src))
return 1
/mob/living/carbon/Move(NewLoc, direct)
. = ..()
if(. && mob_has_gravity()) //floating is easy
if(HAS_TRAIT(src, TRAIT_NOHUNGER))
nutrition = NUTRITION_LEVEL_FED - 1 //just less than feeling vigorous
else if(nutrition && stat != DEAD)
nutrition -= HUNGER_FACTOR/10
if(m_intent == MOVE_INTENT_RUN)
nutrition -= HUNGER_FACTOR/10
@@ -0,0 +1,255 @@
/mob/living/carbon/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked = FALSE)
var/hit_percent = (100-blocked)/100
if(hit_percent <= 0)
return 0
var/obj/item/bodypart/BP = null
if(isbodypart(def_zone)) //we specified a bodypart object
BP = def_zone
else
if(!def_zone)
def_zone = ran_zone(def_zone)
BP = get_bodypart(check_zone(def_zone))
if(!BP)
BP = bodyparts[1]
switch(damagetype)
if(BRUTE)
if(BP)
if(damage > 0 ? BP.receive_damage(damage * hit_percent, 0) : BP.heal_damage(abs(damage * hit_percent), 0))
update_damage_overlays()
else //no bodypart, we deal damage with a more general method.
adjustBruteLoss(damage * hit_percent)
if(BURN)
if(BP)
if(damage > 0 ? BP.receive_damage(0, damage * hit_percent) : BP.heal_damage(0, abs(damage * hit_percent)))
update_damage_overlays()
else
adjustFireLoss(damage * hit_percent)
if(TOX)
adjustToxLoss(damage * hit_percent)
if(OXY)
adjustOxyLoss(damage * hit_percent)
if(CLONE)
adjustCloneLoss(damage * hit_percent)
if(STAMINA)
if(BP)
if(damage > 0 ? BP.receive_damage(0, 0, damage * hit_percent) : BP.heal_damage(0, 0, abs(damage * hit_percent)))
update_damage_overlays()
else
adjustStaminaLoss(damage * hit_percent)
if(BRAIN)
adjustBrainLoss(damage * hit_percent)
//citadel code
if(AROUSAL)
adjustArousalLoss(damage * hit_percent)
return TRUE
//These procs fetch a cumulative total damage from all bodyparts
/mob/living/carbon/getBruteLoss()
var/amount = 0
for(var/X in bodyparts)
var/obj/item/bodypart/BP = X
amount += BP.brute_dam
return amount
/mob/living/carbon/getFireLoss()
var/amount = 0
for(var/X in bodyparts)
var/obj/item/bodypart/BP = X
amount += BP.burn_dam
return amount
/mob/living/carbon/adjustBruteLoss(amount, updating_health = TRUE, forced = FALSE)
if(!forced && (status_flags & GODMODE))
return FALSE
if(amount > 0)
take_overall_damage(amount, 0, 0, updating_health)
else
heal_overall_damage(abs(amount), 0, 0, FALSE, TRUE, updating_health)
return amount
/mob/living/carbon/adjustFireLoss(amount, updating_health = TRUE, forced = FALSE)
if(!forced && (status_flags & GODMODE))
return FALSE
if(amount > 0)
take_overall_damage(0, amount, 0, updating_health)
else
heal_overall_damage(0, abs(amount), 0, FALSE, TRUE, updating_health)
return amount
/mob/living/carbon/adjustToxLoss(amount, updating_health = TRUE, forced = FALSE)
if(!forced && HAS_TRAIT(src, TRAIT_TOXINLOVER)) //damage becomes healing and healing becomes damage
amount = -amount
if(amount > 0)
blood_volume -= 5*amount
else
blood_volume -= amount
return ..()
/mob/living/carbon/getStaminaLoss()
. = 0
for(var/X in bodyparts)
var/obj/item/bodypart/BP = X
. += round(BP.stamina_dam * BP.stam_damage_coeff, DAMAGE_PRECISION)
/mob/living/carbon/adjustStaminaLoss(amount, updating_health = TRUE, forced = FALSE)
if(!forced && (status_flags & GODMODE))
return FALSE
if(amount > 0)
take_overall_damage(0, 0, amount, updating_health)
else
heal_overall_damage(0, 0, abs(amount), FALSE, FALSE, updating_health)
return amount
/mob/living/carbon/setStaminaLoss(amount, updating = TRUE, forced = FALSE)
var/current = getStaminaLoss()
var/diff = amount - current
if(!diff)
return
adjustStaminaLoss(diff, updating, forced)
////////////////////////////////////////////
//Returns a list of damaged bodyparts
/mob/living/carbon/proc/get_damaged_bodyparts(brute = FALSE, burn = FALSE, stamina = FALSE, status)
var/list/obj/item/bodypart/parts = list()
for(var/X in bodyparts)
var/obj/item/bodypart/BP = X
if(status && BP.status != status)
continue
if((brute && BP.brute_dam) || (burn && BP.burn_dam) || (stamina && BP.stamina_dam))
parts += BP
return parts
//Returns a list of damageable bodyparts
/mob/living/carbon/proc/get_damageable_bodyparts()
var/list/obj/item/bodypart/parts = list()
for(var/X in bodyparts)
var/obj/item/bodypart/BP = X
if(BP.brute_dam + BP.burn_dam < BP.max_damage)
parts += BP
return parts
//Heals ONE bodypart randomly selected from damaged ones.
//It automatically updates damage overlays if necessary
//It automatically updates health status
/mob/living/carbon/heal_bodypart_damage(brute = 0, burn = 0, stamina = 0, updating_health = TRUE, only_robotic = FALSE, only_organic = TRUE)
var/list/obj/item/bodypart/parts = get_damaged_bodyparts(brute,burn)
if(!parts.len)
return
var/obj/item/bodypart/picked = pick(parts)
if(picked.heal_damage(brute, burn, stamina, only_robotic, only_organic))
update_damage_overlays()
//Damages ONE bodypart randomly selected from damagable ones.
//It automatically updates damage overlays if necessary
//It automatically updates health status
/mob/living/carbon/take_bodypart_damage(brute = 0, burn = 0, stamina = 0)
var/list/obj/item/bodypart/parts = get_damageable_bodyparts()
if(!parts.len)
return
var/obj/item/bodypart/picked = pick(parts)
if(picked.receive_damage(brute, burn, stamina))
update_damage_overlays()
//Heal MANY bodyparts, in random order
/mob/living/carbon/heal_overall_damage(brute = 0, burn = 0, stamina = 0, only_robotic = FALSE, only_organic = TRUE, updating_health = TRUE)
var/list/obj/item/bodypart/parts = get_damaged_bodyparts(brute, burn, stamina)
var/update = NONE
while(parts.len && (brute > 0 || burn > 0 || stamina > 0))
var/obj/item/bodypart/picked = pick(parts)
var/brute_was = picked.brute_dam
var/burn_was = picked.burn_dam
var/stamina_was = picked.stamina_dam
update |= picked.heal_damage(brute, burn, stamina, only_robotic, only_organic, FALSE)
brute = round(brute - (brute_was - picked.brute_dam), DAMAGE_PRECISION)
burn = round(burn - (burn_was - picked.burn_dam), DAMAGE_PRECISION)
stamina = round(stamina - (stamina_was - picked.stamina_dam), DAMAGE_PRECISION)
parts -= picked
if(updating_health)
updatehealth()
if(update)
update_damage_overlays()
update_stamina() //CIT CHANGE - makes sure update_stamina() always gets called after a health update
// damage MANY bodyparts, in random order
/mob/living/carbon/take_overall_damage(brute = 0, burn = 0, stamina = 0, updating_health = TRUE)
if(status_flags & GODMODE)
return //godmode
var/list/obj/item/bodypart/parts = get_damageable_bodyparts()
var/update = 0
while(parts.len && (brute > 0 || burn > 0 || stamina > 0))
var/obj/item/bodypart/picked = pick(parts)
var/brute_per_part = round(brute/parts.len, DAMAGE_PRECISION)
var/burn_per_part = round(burn/parts.len, DAMAGE_PRECISION)
var/stamina_per_part = round(stamina/parts.len, DAMAGE_PRECISION)
var/brute_was = picked.brute_dam
var/burn_was = picked.burn_dam
var/stamina_was = picked.stamina_dam
update |= picked.receive_damage(brute_per_part, burn_per_part, stamina_per_part, FALSE)
brute = round(brute - (picked.brute_dam - brute_was), DAMAGE_PRECISION)
burn = round(burn - (picked.burn_dam - burn_was), DAMAGE_PRECISION)
stamina = round(stamina - (picked.stamina_dam - stamina_was), DAMAGE_PRECISION)
parts -= picked
if(updating_health)
updatehealth()
if(update)
update_damage_overlays()
update_stamina()
/mob/living/carbon/getBrainLoss()
. = 0
var/obj/item/organ/brain/B = getorganslot(ORGAN_SLOT_BRAIN)
if(B)
. = B.get_brain_damage()
//Some sources of brain damage shouldn't be deadly
/mob/living/carbon/adjustBrainLoss(amount, maximum = BRAIN_DAMAGE_DEATH)
if(status_flags & GODMODE)
return FALSE
var/prev_brainloss = getBrainLoss()
var/obj/item/organ/brain/B = getorganslot(ORGAN_SLOT_BRAIN)
if(!B)
return
B.adjust_brain_damage(amount, maximum)
if(amount <= 0) //cut this early
return
var/brainloss = getBrainLoss()
if(brainloss > BRAIN_DAMAGE_MILD)
if(prob(amount * ((2 * (100 + brainloss - BRAIN_DAMAGE_MILD)) / 100))) //Base chance is the hit damage; for every point of damage past the threshold the chance is increased by 2%
gain_trauma_type(BRAIN_TRAUMA_MILD)
if(brainloss > BRAIN_DAMAGE_SEVERE)
if(prob(amount * ((2 * (100 + brainloss - BRAIN_DAMAGE_SEVERE)) / 100))) //Base chance is the hit damage; for every point of damage past the threshold the chance is increased by 2%
if(prob(20))
gain_trauma_type(BRAIN_TRAUMA_SPECIAL)
else
gain_trauma_type(BRAIN_TRAUMA_SEVERE)
if(prev_brainloss < BRAIN_DAMAGE_MILD && brainloss >= BRAIN_DAMAGE_MILD)
to_chat(src, "<span class='warning'>You feel lightheaded.</span>")
else if(prev_brainloss < BRAIN_DAMAGE_SEVERE && brainloss >= BRAIN_DAMAGE_SEVERE)
to_chat(src, "<span class='warning'>You feel less in control of your thoughts.</span>")
else if(prev_brainloss < (BRAIN_DAMAGE_DEATH - 20) && brainloss >= (BRAIN_DAMAGE_DEATH - 20))
to_chat(src, "<span class='warning'>You can feel your mind flickering on and off...</span>")
/mob/living/carbon/setBrainLoss(amount)
var/obj/item/organ/brain/B = getorganslot(ORGAN_SLOT_BRAIN)
if(B)
var/adjusted_amount = amount - B.get_brain_damage()
B.adjust_brain_damage(adjusted_amount, null)
+65
View File
@@ -0,0 +1,65 @@
/mob/living/carbon/death(gibbed)
if(stat == DEAD)
return
silent = FALSE
losebreath = 0
if(!gibbed)
emote("deathgasp")
. = ..()
for(var/T in get_traumas())
var/datum/brain_trauma/BT = T
BT.on_death()
if(SSticker.mode)
SSticker.mode.check_win() //Calls the rounds wincheck, mainly for wizard, malf, and changeling now
/mob/living/carbon/gib(no_brain, no_organs, no_bodyparts)
var/atom/Tsec = drop_location()
for(var/mob/M in src)
if(M in stomach_contents)
stomach_contents.Remove(M)
M.forceMove(Tsec)
visible_message("<span class='danger'>[M] bursts out of [src]!</span>")
..()
/mob/living/carbon/spill_organs(no_brain, no_organs, no_bodyparts)
var/atom/Tsec = drop_location()
if(!no_bodyparts)
if(no_organs)//so the organs don't get transfered inside the bodyparts we'll drop.
for(var/X in internal_organs)
if(no_brain || !istype(X, /obj/item/organ/brain))
qdel(X)
else //we're going to drop all bodyparts except chest, so the only organs that needs spilling are those inside it.
for(var/X in internal_organs)
var/obj/item/organ/O = X
if(no_brain && istype(O, /obj/item/organ/brain))
qdel(O) //so the brain isn't transfered to the head when the head drops.
continue
var/org_zone = check_zone(O.zone) //both groin and chest organs.
if(org_zone == BODY_ZONE_CHEST)
O.Remove(src)
O.forceMove(Tsec)
O.throw_at(get_edge_target_turf(src,pick(GLOB.alldirs)),rand(1,3),5)
else
for(var/X in internal_organs)
var/obj/item/organ/I = X
if(no_brain && istype(I, /obj/item/organ/brain))
qdel(I)
continue
if(no_organs && !istype(I, /obj/item/organ/brain))
qdel(I)
continue
I.Remove(src)
I.forceMove(Tsec)
I.throw_at(get_edge_target_turf(src,pick(GLOB.alldirs)),rand(1,3),5)
/mob/living/carbon/spread_bodyparts()
for(var/X in bodyparts)
var/obj/item/bodypart/BP = X
BP.drop_limb()
BP.throw_at(get_edge_target_turf(src,pick(GLOB.alldirs)),rand(1,3),5)
+108
View File
@@ -0,0 +1,108 @@
/datum/emote/living/carbon
mob_type_allowed_typecache = list(/mob/living/carbon)
/datum/emote/living/carbon/airguitar
key = "airguitar"
message = "is strumming the air and headbanging like a safari chimp."
restraint_check = TRUE
/datum/emote/living/carbon/blink
key = "blink"
key_third_person = "blinks"
message = "blinks."
/datum/emote/living/carbon/blink_r
key = "blink_r"
message = "blinks rapidly."
/datum/emote/living/carbon/clap
key = "clap"
key_third_person = "claps"
message = "claps."
muzzle_ignore = TRUE
restraint_check = TRUE
emote_type = EMOTE_AUDIBLE
/datum/emote/living/carbon/clap/run_emote(mob/living/user, params)
. = ..()
if (.)
if (ishuman(user))
// Need hands to clap
if (!user.get_bodypart(BODY_ZONE_L_ARM) || !user.get_bodypart(BODY_ZONE_R_ARM))
return
var/clap = pick('sound/misc/clap1.ogg',
'sound/misc/clap2.ogg',
'sound/misc/clap3.ogg',
'sound/misc/clap4.ogg')
playsound(user, clap, 50, 1, -1)
//h13
/datum/emote/living/carbon/crack
key = "crack"
key_third_person = "cracks their knuckles"
message = "cracks their knuckles."
muzzle_ignore = TRUE
restraint_check = TRUE
emote_type = EMOTE_AUDIBLE
/datum/emote/living/carbon/gnarl
key = "gnarl"
key_third_person = "gnarls"
message = "gnarls and shows its teeth..."
mob_type_allowed_typecache = list(/mob/living/carbon/monkey, /mob/living/carbon/alien)
/datum/emote/living/carbon/moan
key = "moan"
key_third_person = "moans"
message = "moans!"
message_mime = "appears to moan!"
emote_type = EMOTE_AUDIBLE
/datum/emote/living/carbon/roll
key = "roll"
key_third_person = "rolls"
message = "rolls."
mob_type_allowed_typecache = list(/mob/living/carbon/monkey, /mob/living/carbon/alien)
restraint_check = TRUE
/datum/emote/living/carbon/scratch
key = "scratch"
key_third_person = "scratches"
message = "scratches."
mob_type_allowed_typecache = list(/mob/living/carbon/monkey, /mob/living/carbon/alien)
restraint_check = TRUE
/datum/emote/living/carbon/screech
key = "screech"
key_third_person = "screeches"
message = "screeches."
mob_type_allowed_typecache = list(/mob/living/carbon/monkey, /mob/living/carbon/alien)
/datum/emote/living/carbon/sign
key = "sign"
key_third_person = "signs"
message_param = "signs the number %t."
mob_type_allowed_typecache = list(/mob/living/carbon/monkey, /mob/living/carbon/alien)
restraint_check = TRUE
/datum/emote/living/carbon/sign/select_param(mob/user, params)
. = ..()
if(!isnum(text2num(params)))
return message
/datum/emote/living/carbon/sign/signal
key = "signal"
key_third_person = "signals"
message_param = "raises %t fingers."
mob_type_allowed_typecache = list(/mob/living/carbon/human)
restraint_check = TRUE
/datum/emote/living/carbon/tail
key = "tail"
message = "waves their tail."
mob_type_allowed_typecache = list(/mob/living/carbon/monkey, /mob/living/carbon/alien)
/datum/emote/living/carbon/wink
key = "wink"
key_third_person = "winks"
message = "winks."
+113
View File
@@ -0,0 +1,113 @@
/mob/living/carbon/examine(mob/user)
var/t_He = p_they(TRUE)
var/t_His = p_their(TRUE)
var/t_his = p_their()
var/t_him = p_them()
var/t_has = p_have()
var/t_is = p_are()
var/msg = "<span class='info'>*---------*\nThis is [icon2html(src, user)] \a <EM>[src]</EM>!\n"
if (handcuffed)
msg += "<span class='warning'>[t_He] [t_is] [icon2html(handcuffed, user)] handcuffed!</span>\n"
if (head)
msg += "[t_He] [t_is] wearing [head.get_examine_string(user)] on [t_his] head. \n"
if (wear_mask)
msg += "[t_He] [t_is] wearing [wear_mask.get_examine_string(user)] on [t_his] face.\n"
if (wear_neck)
msg += "[t_He] [t_is] wearing [wear_neck.get_examine_string(user)] around [t_his] neck.\n"
for(var/obj/item/I in held_items)
if(!(I.item_flags & ABSTRACT))
msg += "[t_He] [t_is] holding [I.get_examine_string(user)] in [t_his] [get_held_index_name(get_held_index_of_item(I))].\n"
if (back)
msg += "[t_He] [t_has] [back.get_examine_string(user)] on [t_his] back.\n"
var/appears_dead = 0
if (stat == DEAD)
appears_dead = 1
if(getorgan(/obj/item/organ/brain))
msg += "<span class='deadsay'>[t_He] [t_is] limp and unresponsive, with no signs of life.</span>\n"
else if(get_bodypart(BODY_ZONE_HEAD))
msg += "<span class='deadsay'>It appears that [t_his] brain is missing...</span>\n"
var/list/missing = get_missing_limbs()
for(var/t in missing)
if(t==BODY_ZONE_HEAD)
msg += "<span class='deadsay'><B>[t_His] [parse_zone(t)] is missing!</B></span>\n"
continue
msg += "<span class='warning'><B>[t_His] [parse_zone(t)] is missing!</B></span>\n"
msg += "<span class='warning'>"
var/temp = getBruteLoss()
if(!(user == src && src.hal_screwyhud == SCREWYHUD_HEALTHY)) //fake healthy
if(temp)
if (temp < 25)
msg += "[t_He] [t_has] minor bruising.\n"
else if (temp < 50)
msg += "[t_He] [t_has] <b>moderate</b> bruising!\n"
else
msg += "<B>[t_He] [t_has] severe bruising!</B>\n"
temp = getFireLoss()
if(temp)
if (temp < 25)
msg += "[t_He] [t_has] minor burns.\n"
else if (temp < 50)
msg += "[t_He] [t_has] <b>moderate</b> burns!\n"
else
msg += "<B>[t_He] [t_has] severe burns!</B>\n"
temp = getCloneLoss()
if(temp)
if(temp < 25)
msg += "[t_He] [t_is] slightly deformed.\n"
else if (temp < 50)
msg += "[t_He] [t_is] <b>moderately</b> deformed!\n"
else
msg += "<b>[t_He] [t_is] severely deformed!</b>\n"
if(HAS_TRAIT(src, TRAIT_DUMB))
msg += "[t_He] seem[p_s()] to be clumsy and unable to think.\n"
if(fire_stacks > 0)
msg += "[t_He] [t_is] covered in something flammable.\n"
if(fire_stacks < 0)
msg += "[t_He] look[p_s()] a little soaked.\n"
if(pulledby && pulledby.grab_state)
msg += "[t_He] [t_is] restrained by [pulledby]'s grip.\n"
msg += "</span>"
if(!appears_dead)
if(stat == UNCONSCIOUS)
msg += "[t_He] [t_is]n't responding to anything around [t_him] and seems to be asleep.\n"
else if(InCritical())
msg += "[t_His] breathing is shallow and labored.\n"
if(digitalcamo)
msg += "[t_He] [t_is] moving [t_his] body in an unnatural and blatantly unsimian manner.\n"
if(combatmode)
msg += "[t_He] [t_is] visibly tense[resting ? "." : ", and [t_is] standing in combative stance."]\n"
GET_COMPONENT_FROM(mood, /datum/component/mood, src)
if(mood)
switch(mood.shown_mood)
if(-INFINITY to MOOD_LEVEL_SAD4)
msg += "[t_He] look[p_s()] depressed.\n"
if(MOOD_LEVEL_SAD4 to MOOD_LEVEL_SAD3)
msg += "[t_He] look[p_s()] very sad.\n"
if(MOOD_LEVEL_SAD3 to MOOD_LEVEL_SAD2)
msg += "[t_He] look[p_s()] a bit down.\n"
if(MOOD_LEVEL_HAPPY2 to MOOD_LEVEL_HAPPY3)
msg += "[t_He] look[p_s()] quite happy.\n"
if(MOOD_LEVEL_HAPPY3 to MOOD_LEVEL_HAPPY4)
msg += "[t_He] look[p_s()] very happy.\n"
if(MOOD_LEVEL_HAPPY4 to INFINITY)
msg += "[t_He] look[p_s()] ecstatic.\n"
msg += "*---------*</span>"
to_chat(user, msg)
return msg
+58
View File
@@ -0,0 +1,58 @@
/mob/living/verb/give()
set category = "IC"
set name = "Give"
set src in oview(1) //Cannot handle giving shit to mobs on your own tile, but it's a small, small loss
give_item(usr)
/mob/living/proc/give_item(mob/living/carbon/user)
/mob/living/carbon/give_item(mob/living/carbon/user)
if(!istype(user))
return
if(src.stat == 2 || user.stat == 2 || src.client == null)
return
if(src.handcuffed)
user << "<span class='warning'>Those hands are cuffed right now.</span>"
return //Can't receive items while cuffed
var/obj/item/I
if(user.get_active_held_item() == null)
user << "You don't have anything in your [get_held_index_name(get_held_index_of_item(I))] to give to [src]."
return
I = user.get_active_held_item()
if(!I)
return
if(src == user) //Shouldn't happen
user << "<span class='warning'>You tried to give yourself \the [I], but you didn't want it.</span>"
return
if(get_empty_held_index_for_side())
switch(alert(src, "[user] wants to give you \a [I]?", , "Yes", "No"))
if("Yes")
if(!I)
return
if(!Adjacent(user))
user <<"<span class='warning'>You need to stay still while giving an object.</span>"
src << "<span class='warning'>[user] moved away.</span>"//What an asshole
return
if(user.get_active_held_item() != I)
user << "<span class='warning'>You need to keep the item in your hand.</span>"
src << "<span class='warning'>[user] has put \the [I] away!</span>"
return
if(!get_empty_held_index_for_side())
user << "<span class='warning'>Your hands are full.</span>"
user << "<span class='warning'>Their hands are full.</span>"
return
if(!user.dropItemToGround(I))
src << "<span class='warning'>[user] can't let go of \the [I]!</span>"
user << "<span class='warning'>You can't seem to let go of \the [I].</span>"
return
src.put_in_hands(I)
update_inv_hands()
src.visible_message("<span class='notice'>[user] handed \the [I] to [src].</span>")
if("No")
src.visible_message("<span class='warning'>[user] tried to hand \the [I] to [src] but \he didn't want it.</span>")
else
user << "<span class='warning'>[src]'s hands are full.</span>"
@@ -0,0 +1,5 @@
/mob/living/carbon/human/apply_damage(damage = 0,damagetype = BRUTE, def_zone = null, blocked = FALSE)
// depending on the species, it will run the corresponding apply_damage code there
return dna.species.apply_damage(damage, damagetype, def_zone, blocked, src)
@@ -0,0 +1,54 @@
/mob/living/carbon/human/gib_animation()
new /obj/effect/temp_visual/gib_animation(loc, "gibbed-h")
/mob/living/carbon/human/dust_animation()
new /obj/effect/temp_visual/dust_animation(loc, "dust-h")
/mob/living/carbon/human/spawn_gibs(with_bodyparts)
if(with_bodyparts)
new /obj/effect/gibspawner/human(drop_location(), dna, get_static_viruses())
else
new /obj/effect/gibspawner/humanbodypartless(drop_location(), dna, get_static_viruses())
/mob/living/carbon/human/spawn_dust(just_ash = FALSE)
if(just_ash)
new /obj/effect/decal/cleanable/ash(loc)
else
new /obj/effect/decal/remains/human(loc)
/mob/living/carbon/human/death(gibbed)
if(stat == DEAD)
return
stop_sound_channel(CHANNEL_HEARTBEAT)
var/obj/item/organ/heart/H = getorganslot(ORGAN_SLOT_HEART)
if(H)
H.beat = BEAT_NONE
. = ..()
dizziness = 0
jitteriness = 0
if(ismecha(loc))
var/obj/mecha/M = loc
if(M.occupant == src)
M.go_out()
dna.species.spec_death(gibbed, src)
if(SSticker.HasRoundStarted())
SSblackbox.ReportDeath(src)
if(is_devil(src))
INVOKE_ASYNC(is_devil(src), /datum/antagonist/devil.proc/beginResurrectionCheck, src)
/mob/living/carbon/human/proc/makeSkeleton()
ADD_TRAIT(src, TRAIT_DISFIGURED, TRAIT_GENERIC)
set_species(/datum/species/skeleton)
return 1
/mob/living/carbon/proc/Drain()
become_husk(CHANGELING_DRAIN)
ADD_TRAIT(src, TRAIT_NOCLONE, CHANGELING_DRAIN)
blood_volume = 0
return 1
@@ -0,0 +1,46 @@
/mob/living/carbon/human/dummy
real_name = "Test Dummy"
status_flags = GODMODE|CANPUSH
mouse_drag_pointer = MOUSE_INACTIVE_POINTER
var/in_use = FALSE
INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy)
/mob/living/carbon/human/dummy/Destroy()
in_use = FALSE
return ..()
/mob/living/carbon/human/dummy/Life()
return
/mob/living/carbon/human/dummy/proc/wipe_state()
delete_equipment()
cut_overlays(TRUE)
//Inefficient pooling/caching way.
GLOBAL_LIST_EMPTY(human_dummy_list)
GLOBAL_LIST_EMPTY(dummy_mob_list)
/proc/generate_or_wait_for_human_dummy(slotkey)
if(!slotkey)
return new /mob/living/carbon/human/dummy
var/mob/living/carbon/human/dummy/D = GLOB.human_dummy_list[slotkey]
if(istype(D))
UNTIL(!D.in_use)
else
pass()
if(QDELETED(D))
D = new
GLOB.human_dummy_list[slotkey] = D
GLOB.dummy_mob_list += D
D.in_use = TRUE
return D
/proc/unset_busy_human_dummy(slotnumber)
if(!slotnumber)
return
var/mob/living/carbon/human/dummy/D = GLOB.human_dummy_list[slotnumber]
if(istype(D))
D.wipe_state()
D.in_use = FALSE
@@ -0,0 +1,189 @@
/datum/emote/living/carbon/human
mob_type_allowed_typecache = list(/mob/living/carbon/human)
/datum/emote/living/carbon/human/cry
key = "cry"
key_third_person = "cries"
message = "cries."
emote_type = EMOTE_AUDIBLE
/datum/emote/living/carbon/human/dap
key = "dap"
key_third_person = "daps"
message = "sadly can't find anybody to give daps to, and daps themself. Shameful."
message_param = "give daps to %t."
restraint_check = TRUE
/datum/emote/living/carbon/human/eyebrow
key = "eyebrow"
message = "raises an eyebrow."
/datum/emote/living/carbon/human/grumble
key = "grumble"
key_third_person = "grumbles"
message = "grumbles!"
emote_type = EMOTE_AUDIBLE
/datum/emote/living/carbon/human/handshake
key = "handshake"
message = "shakes their own hands."
message_param = "shakes hands with %t."
restraint_check = TRUE
emote_type = EMOTE_AUDIBLE
/datum/emote/living/carbon/human/hug
key = "hug"
key_third_person = "hugs"
message = "hugs themself."
message_param = "hugs %t."
restraint_check = TRUE
emote_type = EMOTE_AUDIBLE
/datum/emote/living/carbon/human/mawp
key = "mawp"
key_third_person = "mawps"
message = "mawps annoyingly."
emote_type = EMOTE_AUDIBLE
/datum/emote/living/carbon/human/mawp/run_emote(mob/living/user, params)
. = ..()
if(.)
if(ishuman(user))
if(prob(10))
user.adjustEarDamage(-5, -5)
/datum/emote/living/carbon/human/mumble
key = "mumble"
key_third_person = "mumbles"
message = "mumbles!"
emote_type = EMOTE_AUDIBLE
/datum/emote/living/carbon/human/pale
key = "pale"
message = "goes pale for a second."
/datum/emote/living/carbon/human/raise
key = "raise"
key_third_person = "raises"
message = "raises a hand."
restraint_check = TRUE
/datum/emote/living/carbon/human/salute
key = "salute"
key_third_person = "salutes"
message = "salutes."
message_param = "salutes to %t."
restraint_check = TRUE
/datum/emote/living/carbon/human/shrug
key = "shrug"
key_third_person = "shrugs"
message = "shrugs."
/datum/emote/living/carbon/human/wag
key = "wag"
key_third_person = "wags"
message = "wags their tail."
/datum/emote/living/carbon/human/wag/run_emote(mob/user, params)
. = ..()
if(!.)
return
var/mob/living/carbon/human/H = user
if(!istype(H) || !H.dna || !H.dna.species || !H.dna.species.can_wag_tail(H))
return
if(!H.dna.species.is_wagging_tail())
H.dna.species.start_wagging_tail(H)
else
H.dna.species.stop_wagging_tail(H)
/datum/emote/living/carbon/human/wag/can_run_emote(mob/user, status_check = TRUE)
if(!..())
return FALSE
var/mob/living/carbon/human/H = user
return H.dna && H.dna.species && H.dna.species.can_wag_tail(user)
/datum/emote/living/carbon/human/wag/select_message_type(mob/user)
. = ..()
var/mob/living/carbon/human/H = user
if(!H.dna || !H.dna.species)
return
if(H.dna.species.is_wagging_tail())
. = null
/datum/emote/living/carbon/human/wing
key = "wing"
key_third_person = "wings"
message = "their wings."
/datum/emote/living/carbon/human/wing/run_emote(mob/user, params)
. = ..()
if(.)
var/mob/living/carbon/human/H = user
if(findtext(select_message_type(user), "open"))
H.OpenWings()
else
H.CloseWings()
/datum/emote/living/carbon/human/wing/select_message_type(mob/user)
. = ..()
var/mob/living/carbon/human/H = user
if("wings" in H.dna.species.mutant_bodyparts)
. = "opens " + message
else
. = "closes " + message
/datum/emote/living/carbon/human/wing/can_run_emote(mob/user, status_check = TRUE)
if(!..())
return FALSE
var/mob/living/carbon/human/H = user
if(H.dna && H.dna.species && (H.dna.features["wings"] != "None"))
return TRUE
/mob/living/carbon/human/proc/OpenWings()
if(!dna || !dna.species)
return
if("wings" in dna.species.mutant_bodyparts)
dna.species.mutant_bodyparts -= "wings"
dna.species.mutant_bodyparts |= "wingsopen"
update_body()
/mob/living/carbon/human/proc/CloseWings()
if(!dna || !dna.species)
return
if("wingsopen" in dna.species.mutant_bodyparts)
dna.species.mutant_bodyparts -= "wingsopen"
dna.species.mutant_bodyparts |= "wings"
update_body()
if(isturf(loc))
var/turf/T = loc
T.Entered(src)
/datum/emote/sound/human
mob_type_allowed_typecache = list(/mob/living/carbon/human)
emote_type = EMOTE_AUDIBLE
/datum/emote/sound/human/buzz
key = "buzz"
key_third_person = "buzzes"
message = "buzzes."
message_param = "buzzes at %t."
sound = 'sound/machines/buzz-sigh.ogg'
/datum/emote/sound/human/buzz2
key = "buzz2"
message = "buzzes twice."
sound = 'sound/machines/buzz-two.ogg'
/datum/emote/sound/human/ping
key = "ping"
key_third_person = "pings"
message = "pings."
message_param = "pings at %t."
sound = 'sound/machines/ping.ogg'
/datum/emote/sound/human/chime
key = "chime"
key_third_person = "chimes"
message = "chimes."
sound = 'sound/machines/chime.ogg'
@@ -0,0 +1,412 @@
/mob/living/carbon/human/examine(mob/user) //User is the person being examined
//this is very slightly better than it was because you can use it more places. still can't do \his[src] though.
var/t_He = p_they(TRUE)
var/t_His = p_their(TRUE)
var/t_his = p_their()
var/t_him = p_them()
var/t_has = p_have()
var/t_is = p_are()
var/obscure_name
if(isliving(user))
var/mob/living/L = user
if(HAS_TRAIT(L, TRAIT_PROSOPAGNOSIA))
obscure_name = TRUE
var/msg = "<span class='info'>*---------*\nThis is <EM>[!obscure_name ? name : "Unknown"]</EM>!\n"
var/list/obscured = check_obscured_slots()
var/skipface = (wear_mask && (wear_mask.flags_inv & HIDEFACE)) || (head && (head.flags_inv & HIDEFACE))
if(ishuman(src)) //user just returned, y'know, the user's own species. dumb.
var/mob/living/carbon/human/H = src
var/datum/species/pref_species = H.dna.species
if(get_visible_name() == "Unknown") // same as flavor text, but hey it works.
msg += "You can't make out what species they are.\n"
else if(skipface)
msg += "You can't make out what species they are.\n"
else
msg += "[t_He] [t_is] a [H.dna.custom_species ? H.dna.custom_species : pref_species.name]!\n"
//uniform
if(w_uniform && !(SLOT_W_UNIFORM in obscured))
//accessory
var/accessory_msg
if(istype(w_uniform, /obj/item/clothing/under))
var/obj/item/clothing/under/U = w_uniform
if(U.attached_accessory)
accessory_msg += " with [icon2html(U.attached_accessory, user)] \a [U.attached_accessory]"
msg += "[t_He] [t_is] wearing [w_uniform.get_examine_string(user)][accessory_msg].\n"
//head
if(head)
msg += "[t_He] [t_is] wearing [head.get_examine_string(user)] on [t_his] head.\n"
//suit/armor
if(wear_suit)
msg += "[t_He] [t_is] wearing [wear_suit.get_examine_string(user)].\n"
//suit/armor storage
if(s_store)
msg += "[t_He] [t_is] carrying [s_store.get_examine_string(user)] on [t_his] [wear_suit.name].\n"
//back
if(back)
msg += "[t_He] [t_has] [back.get_examine_string(user)] on [t_his] back.\n"
//Hands
for(var/obj/item/I in held_items)
if(!(I.item_flags & ABSTRACT))
msg += "[t_He] [t_is] holding [I.get_examine_string(user)] in [t_his] [get_held_index_name(get_held_index_of_item(I))].\n"
GET_COMPONENT(FR, /datum/component/forensics)
//gloves
if(gloves && !(SLOT_GLOVES in obscured))
msg += "[t_He] [t_has] [gloves.get_examine_string(user)] on [t_his] hands.\n"
else if(FR && length(FR.blood_DNA))
var/hand_number = get_num_arms(FALSE)
if(hand_number)
msg += "<span class='warning'>[t_He] [t_has] [hand_number > 1 ? "" : "a"] blood-stained hand[hand_number > 1 ? "s" : ""]!</span>\n"
//handcuffed?
//handcuffed?
if(handcuffed)
if(istype(handcuffed, /obj/item/restraints/handcuffs/cable))
msg += "<span class='warning'>[t_He] [t_is] [icon2html(handcuffed, user)] restrained with cable!</span>\n"
else
msg += "<span class='warning'>[t_He] [t_is] [icon2html(handcuffed, user)] handcuffed!</span>\n"
//belt
if(belt)
msg += "[t_He] [t_has] [belt.get_examine_string(user)] about [t_his] waist.\n"
//shoes
if(shoes && !(SLOT_SHOES in obscured))
msg += "[t_He] [t_is] wearing [shoes.get_examine_string(user)] on [t_his] feet.\n"
//mask
if(wear_mask && !(SLOT_WEAR_MASK in obscured))
msg += "[t_He] [t_has] [wear_mask.get_examine_string(user)] on [t_his] face.\n"
if (wear_neck && !(SLOT_NECK in obscured))
msg += "[t_He] [t_is] wearing [wear_neck.get_examine_string(user)] around [t_his] neck.\n"
//eyes
if(!(SLOT_GLASSES in obscured))
if(glasses)
msg += "[t_He] [t_has] [glasses.get_examine_string(user)] covering [t_his] eyes.\n"
else if(eye_color == BLOODCULT_EYE && iscultist(src) && HAS_TRAIT(src, TRAIT_CULT_EYES))
msg += "<span class='warning'><B>[t_His] eyes are glowing an unnatural red!</B></span>\n"
//ears
if(ears && !(SLOT_EARS in obscured))
msg += "[t_He] [t_has] [ears.get_examine_string(user)] on [t_his] ears.\n"
//ID
if(wear_id)
msg += "[t_He] [t_is] wearing [wear_id.get_examine_string(user)].\n"
//Status effects
msg += status_effect_examines()
//CIT CHANGES START HERE - adds genital details to examine text
if(LAZYLEN(internal_organs))
for(var/obj/item/organ/genital/dicc in internal_organs)
if(istype(dicc) && dicc.is_exposed())
msg += "[dicc.desc]\n"
msg += attempt_vr(src,"examine_bellies",args) //vore Code
//END OF CIT CHANGES
//Jitters
switch(jitteriness)
if(300 to INFINITY)
msg += "<span class='warning'><B>[t_He] [t_is] convulsing violently!</B></span>\n"
if(200 to 300)
msg += "<span class='warning'>[t_He] [t_is] extremely jittery.</span>\n"
if(100 to 200)
msg += "<span class='warning'>[t_He] [t_is] twitching ever so slightly.</span>\n"
var/appears_dead = 0
if(stat == DEAD || (HAS_TRAIT(src, TRAIT_FAKEDEATH)))
appears_dead = 1
if(suiciding)
msg += "<span class='warning'>[t_He] appear[p_s()] to have committed suicide... there is no hope of recovery.</span>\n"
if(hellbound)
msg += "<span class='warning'>[t_His] soul seems to have been ripped out of [t_his] body. Revival is impossible.</span>\n"
msg += "<span class='deadsay'>[t_He] [t_is] limp and unresponsive; there are no signs of life"
if(getorgan(/obj/item/organ/brain))
if(!key)
var/foundghost = 0
if(mind)
for(var/mob/dead/observer/G in GLOB.player_list)
if(G.mind == mind)
foundghost = 1
if (G.can_reenter_corpse == 0)
foundghost = 0
break
if(!foundghost)
msg += " and [t_his] soul has departed"
msg += "...</span>\n"
if(get_bodypart(BODY_ZONE_HEAD) && !getorgan(/obj/item/organ/brain))
msg += "<span class='deadsay'>It appears that [t_his] brain is missing...</span>\n"
var/temp = getBruteLoss() //no need to calculate each of these twice
msg += "<span class='warning'>"
var/list/missing = list(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
var/list/disabled = list()
for(var/X in bodyparts)
var/obj/item/bodypart/BP = X
if(BP.disabled)
disabled += BP
missing -= BP.body_zone
for(var/obj/item/I in BP.embedded_objects)
msg += "<B>[t_He] [t_has] \a [icon2html(I, user)] [I] embedded in [t_his] [BP.name]!</B>\n"
for(var/X in disabled)
var/obj/item/bodypart/BP = X
var/damage_text
if(!(BP.get_damage(include_stamina = FALSE) >= BP.max_damage)) //Stamina is disabling the limb
damage_text = "limp and lifeless"
else
var/more_brute = BP.brute_dam >= BP.burn_dam
damage_text = more_brute ? "broken and mangled" : "burnt and blistered"
msg += "<B>[capitalize(t_his)] [BP.name] is [damage_text]!</B>\n"
//stores missing limbs
var/l_limbs_missing = 0
var/r_limbs_missing = 0
for(var/t in missing)
if(t==BODY_ZONE_HEAD)
msg += "<span class='deadsay'><B>[t_His] [parse_zone(t)] is missing!</B><span class='warning'>\n"
continue
if(t == BODY_ZONE_L_ARM || t == BODY_ZONE_L_LEG)
l_limbs_missing++
else if(t == BODY_ZONE_R_ARM || t == BODY_ZONE_R_LEG)
r_limbs_missing++
msg += "<B>[capitalize(t_his)] [parse_zone(t)] is missing!</B>\n"
if(l_limbs_missing >= 2 && r_limbs_missing == 0)
msg += "[t_He] look[p_s()] all right now.\n"
else if(l_limbs_missing == 0 && r_limbs_missing >= 2)
msg += "[t_He] really keeps to the left.\n"
else if(l_limbs_missing >= 2 && r_limbs_missing >= 2)
msg += "[t_He] [p_do()]n't seem all there.\n"
if(!(user == src && src.hal_screwyhud == SCREWYHUD_HEALTHY)) //fake healthy
if(temp)
if(temp < 25)
msg += "[t_He] [t_has] minor bruising.\n"
else if(temp < 50)
msg += "[t_He] [t_has] <b>moderate</b> bruising!\n"
else
msg += "<B>[t_He] [t_has] severe bruising!</B>\n"
temp = getFireLoss()
if(temp)
if(temp < 25)
msg += "[t_He] [t_has] minor burns.\n"
else if (temp < 50)
msg += "[t_He] [t_has] <b>moderate</b> burns!\n"
else
msg += "<B>[t_He] [t_has] severe burns!</B>\n"
temp = getCloneLoss()
if(temp)
if(temp < 25)
msg += "[t_He] [t_has] minor cellular damage.\n"
else if(temp < 50)
msg += "[t_He] [t_has] <b>moderate</b> cellular damage!\n"
else
msg += "<b>[t_He] [t_has] severe cellular damage!</b>\n"
if(fire_stacks > 0)
msg += "[t_He] [t_is] covered in something flammable.\n"
if(fire_stacks < 0)
msg += "[t_He] look[p_s()] a little soaked.\n"
if(pulledby && pulledby.grab_state)
msg += "[t_He] [t_is] restrained by [pulledby]'s grip.\n"
if(nutrition < NUTRITION_LEVEL_STARVING - 50)
msg += "[t_He] [t_is] severely malnourished.\n"
else if(nutrition >= NUTRITION_LEVEL_FAT)
if(user.nutrition < NUTRITION_LEVEL_STARVING - 50)
msg += "[t_He] [t_is] plump and delicious looking - Like a fat little piggy. A tasty piggy.\n"
else
msg += "[t_He] [t_is] quite chubby.\n"
switch(disgust)
if(DISGUST_LEVEL_GROSS to DISGUST_LEVEL_VERYGROSS)
msg += "[t_He] look[p_s()] a bit grossed out.\n"
if(DISGUST_LEVEL_VERYGROSS to DISGUST_LEVEL_DISGUSTED)
msg += "[t_He] look[p_s()] really grossed out.\n"
if(DISGUST_LEVEL_DISGUSTED to INFINITY)
msg += "[t_He] look[p_s()] extremely disgusted.\n"
if(blood_volume < (BLOOD_VOLUME_SAFE*blood_ratio))
msg += "[t_He] [t_has] pale skin.\n"
if(bleedsuppress)
msg += "[t_He] [t_is] bandaged with something.\n"
else if(bleed_rate)
if(reagents.has_reagent("heparin"))
msg += "<b>[t_He] [t_is] bleeding uncontrollably!</b>\n"
else
msg += "<B>[t_He] [t_is] bleeding!</B>\n"
if(reagents.has_reagent("teslium"))
msg += "[t_He] [t_is] emitting a gentle blue glow!\n"
if(islist(stun_absorption))
for(var/i in stun_absorption)
if(stun_absorption[i]["end_time"] > world.time && stun_absorption[i]["examine_message"])
msg += "[t_He] [t_is][stun_absorption[i]["examine_message"]]\n"
if(drunkenness && !skipface && !appears_dead) //Drunkenness
switch(drunkenness)
if(11 to 21)
msg += "[t_He] [t_is] slightly flushed.\n"
if(21.01 to 41) //.01s are used in case drunkenness ends up to be a small decimal
msg += "[t_He] [t_is] flushed.\n"
if(41.01 to 51)
msg += "[t_He] [t_is] quite flushed and [t_his] breath smells of alcohol.\n"
if(51.01 to 61)
msg += "[t_He] [t_is] very flushed and [t_his] movements jerky, with breath reeking of alcohol.\n"
if(61.01 to 91)
msg += "[t_He] look[p_s()] like a drunken mess.\n"
if(91.01 to INFINITY)
msg += "[t_He] [t_is] a shitfaced, slobbering wreck.\n"
if(reagents.has_reagent("astral"))
msg += "[t_He] have wild, spacey eyes"
if(mind)
msg += " and have a strange, abnormal look to them.\n"
else
msg += " and don't look like they're all there.\n"
if(isliving(user))
var/mob/living/L = user
if(src != user && HAS_TRAIT(L, TRAIT_EMPATH) && !appears_dead)
if (a_intent != INTENT_HELP)
msg += "[t_He] seem[p_s()] to be on guard.\n"
if (getOxyLoss() >= 10)
msg += "[t_He] seem[p_s()] winded.\n"
if (getToxLoss() >= 10)
msg += "[t_He] seem[p_s()] sickly.\n"
GET_COMPONENT_FROM(mood, /datum/component/mood, src)
if(mood.sanity <= SANITY_DISTURBED)
msg += "[t_He] seem[p_s()] distressed.\n"
SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "empath", /datum/mood_event/sad_empath, src)
if(mood.shown_mood >= 6) //So roundstart people aren't all "happy" and that antags don't show their true happiness.
msg += "[t_He] seem[p_s()] to have had something nice happen to them recently.\n"
SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "empathH", /datum/mood_event/happy_empath, src)
if (HAS_TRAIT(src, TRAIT_BLIND))
msg += "[t_He] appear[p_s()] to be staring off into space.\n"
if (HAS_TRAIT(src, TRAIT_DEAF))
msg += "[t_He] appear[p_s()] to not be responding to noises.\n"
msg += "</span>"
var/obj/item/organ/vocal_cords/Vc = user.getorganslot(ORGAN_SLOT_VOICE)
if(Vc)
if(istype(Vc, /obj/item/organ/vocal_cords/velvet))
if(client?.prefs.lewdchem)
msg += "<span class='velvet'><i>You feel your chords resonate looking at them.</i></span>\n"
if(!appears_dead)
if(stat == UNCONSCIOUS)
msg += "[t_He] [t_is]n't responding to anything around [t_him] and seem[p_s()] to be asleep.\n"
else
if(HAS_TRAIT(src, TRAIT_DUMB))
msg += "[t_He] [t_has] a stupid expression on [t_his] face.\n"
if(InCritical())
msg += "[t_He] [t_is] barely conscious.\n"
if(getorgan(/obj/item/organ/brain))
if(!key)
msg += "<span class='deadsay'>[t_He] [t_is] totally catatonic. The stresses of life in deep-space must have been too much for [t_him]. Any recovery is unlikely.</span>\n"
else if(!client)
msg += "[t_He] [t_has] a blank, absent-minded stare and appears completely unresponsive to anything. [t_He] may snap out of it soon.\n"
if(digitalcamo)
msg += "[t_He] [t_is] moving [t_his] body in an unnatural and blatantly inhuman manner.\n"
var/traitstring = get_trait_string()
if(ishuman(user))
var/mob/living/carbon/human/H = user
var/obj/item/organ/cyberimp/eyes/hud/CIH = H.getorgan(/obj/item/organ/cyberimp/eyes/hud)
if(istype(H.glasses, /obj/item/clothing/glasses/hud) || CIH)
var/perpname = get_face_name(get_id_name(""))
if(perpname)
var/datum/data/record/R = find_record("name", perpname, GLOB.data_core.general)
if(R)
msg += "<span class='deptradio'>Rank:</span> [R.fields["rank"]]<br>"
msg += "<a href='?src=[REF(src)];hud=1;photo_front=1'>\[Front photo\]</a> "
msg += "<a href='?src=[REF(src)];hud=1;photo_side=1'>\[Side photo\]</a><br>"
if(istype(H.glasses, /obj/item/clothing/glasses/hud/health) || istype(CIH, /obj/item/organ/cyberimp/eyes/hud/medical))
var/cyberimp_detect
for(var/obj/item/organ/cyberimp/CI in internal_organs)
if(CI.status == ORGAN_ROBOTIC && !CI.syndicate_implant)
cyberimp_detect += "[name] is modified with a [CI.name].<br>"
if(cyberimp_detect)
msg += "Detected cybernetic modifications:<br>"
msg += cyberimp_detect
if(R)
var/health_r = R.fields["p_stat"]
msg += "<a href='?src=[REF(src)];hud=m;p_stat=1'>\[[health_r]\]</a>"
health_r = R.fields["m_stat"]
msg += "<a href='?src=[REF(src)];hud=m;m_stat=1'>\[[health_r]\]</a><br>"
R = find_record("name", perpname, GLOB.data_core.medical)
if(R)
msg += "<a href='?src=[REF(src)];hud=m;evaluation=1'>\[Medical evaluation\]</a><br>"
if(traitstring)
msg += "<span class='info'>Detected physiological traits:<br></span>"
msg += "<span class='info'>[traitstring]</span><br>"
if(istype(H.glasses, /obj/item/clothing/glasses/hud/security) || istype(CIH, /obj/item/organ/cyberimp/eyes/hud/security))
if(!user.stat && user != src)
//|| !user.canmove || user.restrained()) Fluff: Sechuds have eye-tracking technology and sets 'arrest' to people that the wearer looks and blinks at.
var/criminal = "None"
R = find_record("name", perpname, GLOB.data_core.security)
if(R)
criminal = R.fields["criminal"]
msg += "<span class='deptradio'>Criminal status:</span> <a href='?src=[REF(src)];hud=s;status=1'>\[[criminal]\]</a>\n"
msg += "<span class='deptradio'>Security record:</span> <a href='?src=[REF(src)];hud=s;view=1'>\[View\]</a> "
msg += "<a href='?src=[REF(src)];hud=s;add_crime=1'>\[Add crime\]</a> "
msg += "<a href='?src=[REF(src)];hud=s;view_comment=1'>\[View comment log\]</a> "
msg += "<a href='?src=[REF(src)];hud=s;add_comment=1'>\[Add comment\]</a>\n"
else if(isobserver(user) && traitstring)
msg += "<span class='info'><b>Traits:</b> [traitstring]</span><br>"
if(print_flavor_text())
if(get_visible_name() == "Unknown") //Are we sure we know who this is? Don't show flavor text unless we can recognize them. Prevents certain metagaming with impersonation.
msg += "...?<br>"
else if(skipface) //Sometimes we're not unknown, but impersonating someone in a hardsuit, let's not reveal our flavor text then either.
msg += "...?<br>"
else
msg += "[print_flavor_text()]\n"
msg += "*---------*</span>"
to_chat(user, msg)
return msg
/mob/living/proc/status_effect_examines(pronoun_replacement) //You can include this in any mob's examine() to show the examine texts of status effects!
var/list/dat = list()
if(!pronoun_replacement)
pronoun_replacement = p_they(TRUE)
for(var/V in status_effects)
var/datum/status_effect/E = V
if(E.examine_text)
var/new_text = replacetext(E.examine_text, "SUBJECTPRONOUN", pronoun_replacement)
new_text = replacetext(new_text, "[pronoun_replacement] is", "[pronoun_replacement] [p_are()]") //To make sure something become "They are" or "She is", not "They are" and "She are"
dat += "[new_text]\n" //dat.Join("\n") doesn't work here, for some reason
if(dat.len)
return dat.Join()
@@ -0,0 +1,45 @@
/mob/living/carbon/human/proc/examine_nutrition()
var/message = ""
var/nutrition_examine = round(nutrition)
var/t_He = "It" //capitalised for use at the start of each line.
var/t_His = "Its"
var/t_his = "its"
var/t_is = "is"
var/t_has = "has"
switch(gender)
if(MALE)
t_He = "He"
t_his = "his"
t_His = "His"
if(FEMALE)
t_He = "She"
t_his = "her"
t_His = "Her"
if(PLURAL)
t_He = "They"
t_his = "their"
t_His = "Their"
t_is = "are"
t_has = "have"
if(NEUTER)
t_He = "It"
t_his = "its"
t_His = "Its"
switch(nutrition_examine)
if(0 to 49)
message = "<span class='warning'>[t_He] [t_is] starving! You can hear [t_his] stomach snarling from across the room!</span>\n"
if(50 to 99)
message = "<span class='warning'>[t_He] [t_is] extremely hungry. A deep growl occasionally rumbles from [t_his] empty stomach.</span>\n"
if(100 to 499)
return message //Well that's pretty normal, really.
if(500 to 864) // Fat.
message = "[t_He] [t_has] a stuffed belly, bloated fat and round from eating too much.\n"
if(1200 to 1934) // One person fully digested.
message = "<span class='warning'>[t_He] [t_is] sporting a large, round, sagging stomach. It's contains at least their body weight worth of glorping slush.</span>\n"
if(1935 to 3004) // Two people.
message = "<span class='warning'>[t_He] [t_is] engorged with a huge stomach that sags and wobbles as they move. [t_He] must have consumed at least twice their body weight. It looks incredibly soft.</span>\n"
if(3005 to 4074) // Three people.
message = "<span class='warning'>[t_His] stomach is firmly packed with digesting slop. [t_He] must have eaten at least a few times worth their body weight! It looks hard for them to stand, and [t_his] gut jiggles when they move.</span>\n"
if(4075 to 10000) // Four or more people.
message = "<span class='warning'>[t_He] [t_is] so absolutely stuffed that you aren't sure how it's possible to move. [t_He] can't seem to swell any bigger. The surface of [t_his] belly looks sorely strained!</span>\n"
return message
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,819 @@
/mob/living/carbon/human/getarmor(def_zone, type)
var/armorval = 0
var/organnum = 0
if(def_zone)
if(isbodypart(def_zone))
var/obj/item/bodypart/bp = def_zone
if(bp.body_part)
return checkarmor(def_zone, type)
var/obj/item/bodypart/affecting = get_bodypart(ran_zone(def_zone))
return checkarmor(affecting, type)
//If a specific bodypart is targetted, check how that bodypart is protected and return the value.
//If you don't specify a bodypart, it checks ALL your bodyparts for protection, and averages out the values
for(var/X in bodyparts)
var/obj/item/bodypart/BP = X
armorval += checkarmor(BP, type)
organnum++
return (armorval/max(organnum, 1))
/mob/living/carbon/human/proc/checkarmor(obj/item/bodypart/def_zone, d_type)
if(!d_type)
return 0
var/protection = 0
var/list/body_parts = list(head, wear_mask, wear_suit, w_uniform, back, gloves, shoes, belt, s_store, glasses, ears, wear_id) //Everything but pockets. Pockets are l_store and r_store. (if pockets were allowed, putting something armored, gloves or hats for example, would double up on the armor)
for(var/bp in body_parts)
if(!bp)
continue
if(istype(bp, /obj/item/clothing))
var/obj/item/clothing/C = bp
if(C.body_parts_covered & def_zone.body_part)
protection += C.armor.getRating(d_type)
protection += physiology.armor.getRating(d_type)
return protection
/mob/living/carbon/human/on_hit(obj/item/projectile/P)
if(dna && dna.species)
dna.species.on_hit(P, src)
/mob/living/carbon/human/bullet_act(obj/item/projectile/P, def_zone)
if(dna && dna.species)
var/spec_return = dna.species.bullet_act(P, src)
if(spec_return)
return spec_return
if(mind)
if(mind.martial_art && !incapacitated(FALSE, TRUE) && mind.martial_art.can_use(src) && mind.martial_art.deflection_chance) //Some martial arts users can deflect projectiles!
if(prob(mind.martial_art.deflection_chance))
if(!lying && dna && !dna.check_mutation(HULK)) //But only if they're not lying down, and hulks can't do it
if(mind.martial_art.deflection_chance >= 100) //if they can NEVER be hit, lets clue sec in ;)
visible_message("<span class='danger'>[src] deflects the projectile; [p_they()] can't be hit with ranged weapons!</span>", "<span class='userdanger'>You deflect the projectile!</span>")
else
visible_message("<span class='danger'>[src] deflects the projectile!</span>", "<span class='userdanger'>You deflect the projectile!</span>")
playsound(src, pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 75, 1)
if(!mind.martial_art.reroute_deflection)
return FALSE
else
P.firer = src
P.setAngle(rand(0, 360))//SHING
return FALSE
if(!(P.original == src && P.firer == src)) //can't block or reflect when shooting yourself
if(P.is_reflectable)
if(check_reflect(def_zone)) // Checks if you've passed a reflection% check
visible_message("<span class='danger'>The [P.name] gets reflected by [src]!</span>", \
"<span class='userdanger'>The [P.name] gets reflected by [src]!</span>")
// Find a turf near or on the original location to bounce to
if(P.starting)
var/new_x = P.starting.x + pick(0, 0, 0, 0, 0, -1, 1, -2, 2)
var/new_y = P.starting.y + pick(0, 0, 0, 0, 0, -1, 1, -2, 2)
var/turf/curloc = get_turf(src)
// redirect the projectile
P.original = locate(new_x, new_y, P.z)
P.starting = curloc
P.firer = src
P.yo = new_y - curloc.y
P.xo = new_x - curloc.x
var/new_angle_s = P.Angle + rand(120,240)
while(new_angle_s > 180) // Translate to regular projectile degrees
new_angle_s -= 360
P.setAngle(new_angle_s)
return -1 // complete projectile permutation
if(check_shields(P, P.damage, "the [P.name]", PROJECTILE_ATTACK, P.armour_penetration))
P.on_hit(src, 100, def_zone)
return 2
return (..(P , def_zone))
/mob/living/carbon/human/proc/check_reflect(def_zone) //Reflection checks for anything in your l_hand, r_hand, or wear_suit based on the reflection chance of the object
if(wear_suit)
if(wear_suit.IsReflect(def_zone) == 1)
return 1
for(var/obj/item/I in held_items)
if(I.IsReflect(def_zone) == 1)
return 1
return 0
/mob/living/carbon/human/proc/check_shields(atom/AM, var/damage, attack_text = "the attack", attack_type = MELEE_ATTACK, armour_penetration = 0)
var/block_chance_modifier = round(damage / -3)
for(var/obj/item/I in held_items)
if(!istype(I, /obj/item/clothing))
var/final_block_chance = I.block_chance - (CLAMP((armour_penetration-I.armour_penetration)/2,0,100)) + block_chance_modifier //So armour piercing blades can still be parried by other blades, for example
if(I.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type))
return 1
if(wear_suit)
var/final_block_chance = wear_suit.block_chance - (CLAMP((armour_penetration-wear_suit.armour_penetration)/2,0,100)) + block_chance_modifier
if(wear_suit.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type))
return 1
if(w_uniform)
var/final_block_chance = w_uniform.block_chance - (CLAMP((armour_penetration-w_uniform.armour_penetration)/2,0,100)) + block_chance_modifier
if(w_uniform.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type))
return 1
return 0
/mob/living/carbon/human/proc/check_block()
if(mind)
if(mind.martial_art && prob(mind.martial_art.block_chance) && mind.martial_art.can_use(src) && in_throw_mode && !incapacitated(FALSE, TRUE))
return TRUE
return FALSE
/mob/living/carbon/human/hitby(atom/movable/AM, skipcatch = FALSE, hitpush = TRUE, blocked = FALSE)
if(dna && dna.species)
var/spec_return = dna.species.spec_hitby(AM, src)
if(spec_return)
return spec_return
var/obj/item/I
var/throwpower = 30
if(istype(AM, /obj/item))
I = AM
throwpower = I.throwforce
if(I.thrownby == src) //No throwing stuff at yourself to trigger hit reactions
return ..()
if(check_shields(AM, throwpower, "\the [AM.name]", THROWN_PROJECTILE_ATTACK))
hitpush = FALSE
skipcatch = TRUE
blocked = TRUE
else if(I)
if(I.throw_speed >= EMBED_THROWSPEED_THRESHOLD)
if(can_embed(I))
if(prob(I.embedding.embed_chance) && !HAS_TRAIT(src, TRAIT_PIERCEIMMUNE))
throw_alert("embeddedobject", /obj/screen/alert/embeddedobject)
var/obj/item/bodypart/L = pick(bodyparts)
L.embedded_objects |= I
I.add_mob_blood(src)//it embedded itself in you, of course it's bloody!
I.forceMove(src)
L.receive_damage(I.w_class*I.embedding.embedded_impact_pain_multiplier)
visible_message("<span class='danger'>[I] embeds itself in [src]'s [L.name]!</span>","<span class='userdanger'>[I] embeds itself in your [L.name]!</span>")
SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "embedded", /datum/mood_event/embedded)
hitpush = FALSE
skipcatch = TRUE //can't catch the now embedded item
return ..()
/mob/living/carbon/human/grabbedby(mob/living/carbon/user, supress_message = 0)
if(user == src && pulling && !pulling.anchored && grab_state >= GRAB_AGGRESSIVE && (HAS_TRAIT(src, TRAIT_FAT)) && ismonkey(pulling))
devour_mob(pulling)
else
..()
/mob/living/carbon/human/grippedby(mob/living/user, instant = FALSE)
if(w_uniform)
w_uniform.add_fingerprint(user)
..()
/mob/living/carbon/human/attacked_by(obj/item/I, mob/living/user)
if(!I || !user)
return 0
var/obj/item/bodypart/affecting
if(user == src)
affecting = get_bodypart(check_zone(user.zone_selected)) //stabbing yourself always hits the right target
else
affecting = get_bodypart(ran_zone(user.zone_selected))
var/target_area = parse_zone(check_zone(user.zone_selected)) //our intended target
SEND_SIGNAL(I, COMSIG_ITEM_ATTACK_ZONE, src, user, affecting)
SSblackbox.record_feedback("nested tally", "item_used_for_combat", 1, list("[I.force]", "[I.type]"))
SSblackbox.record_feedback("tally", "zone_targeted", 1, target_area)
// the attacked_by code varies among species
return dna.species.spec_attacked_by(I, user, affecting, a_intent, src)
/mob/living/carbon/human/attack_hulk(mob/living/carbon/human/user, does_attack_animation = 0)
if(user.a_intent == INTENT_HARM)
var/hulk_verb = pick("smash","pummel")
if(check_shields(user, 15, "the [hulk_verb]ing"))
return
..(user, 1)
playsound(loc, user.dna.species.attack_sound, 25, 1, -1)
var/message = "[user] has [hulk_verb]ed [src]!"
visible_message("<span class='danger'>[message]</span>", \
"<span class='userdanger'>[message]</span>")
adjustBruteLoss(15)
return 1
/mob/living/carbon/human/attack_hand(mob/user)
if(..()) //to allow surgery to return properly.
return
if(ishuman(user))
var/mob/living/carbon/human/H = user
dna.species.spec_attack_hand(H, src)
/mob/living/carbon/human/attack_paw(mob/living/carbon/monkey/M)
var/dam_zone = pick(BODY_ZONE_CHEST, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
var/obj/item/bodypart/affecting = get_bodypart(ran_zone(dam_zone))
if(!affecting)
affecting = get_bodypart(BODY_ZONE_CHEST)
if(M.a_intent == INTENT_HELP)
..() //shaking
return 0
if(M.a_intent == INTENT_DISARM) //Always drop item in hand, if no item, get stunned instead.
var/obj/item/I = get_active_held_item()
if(I && dropItemToGround(I))
playsound(loc, 'sound/weapons/slash.ogg', 25, 1, -1)
visible_message("<span class='danger'>[M] disarmed [src]!</span>", \
"<span class='userdanger'>[M] disarmed [src]!</span>")
else if(!M.client || prob(5)) // only natural monkeys get to stun reliably, (they only do it occasionaly)
playsound(loc, 'sound/weapons/pierce.ogg', 25, 1, -1)
Knockdown(100)
log_combat(M, src, "tackled")
visible_message("<span class='danger'>[M] has tackled down [src]!</span>", \
"<span class='userdanger'>[M] has tackled down [src]!</span>")
if(M.limb_destroyer)
dismembering_strike(M, affecting.body_zone)
if(can_inject(M, 1, affecting))//Thick suits can stop monkey bites.
if(..()) //successful monkey bite, this handles disease contraction.
var/damage = rand(1, 3)
if(check_shields(M, damage, "the [M.name]"))
return 0
if(stat != DEAD)
apply_damage(damage, BRUTE, affecting, run_armor_check(affecting, "melee"))
return 1
/mob/living/carbon/human/attack_alien(mob/living/carbon/alien/humanoid/M)
if(check_shields(M, 0, "the M.name"))
visible_message("<span class='danger'>[M] attempted to touch [src]!</span>")
return 0
if(..())
if(M.a_intent == INTENT_HARM)
if (w_uniform)
w_uniform.add_fingerprint(M)
var/damage = prob(90) ? 20 : 0
if(!damage)
playsound(loc, 'sound/weapons/slashmiss.ogg', 50, 1, -1)
visible_message("<span class='danger'>[M] has lunged at [src]!</span>", \
"<span class='userdanger'>[M] has lunged at [src]!</span>")
return 0
var/obj/item/bodypart/affecting = get_bodypart(ran_zone(M.zone_selected))
if(!affecting)
affecting = get_bodypart(BODY_ZONE_CHEST)
var/armor_block = run_armor_check(affecting, "melee", null, null,10)
playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1)
visible_message("<span class='danger'>[M] has slashed at [src]!</span>", \
"<span class='userdanger'>[M] has slashed at [src]!</span>")
log_combat(M, src, "attacked")
if(!dismembering_strike(M, M.zone_selected)) //Dismemberment successful
return 1
apply_damage(damage, BRUTE, affecting, armor_block)
if(M.a_intent == INTENT_DISARM) //Always drop item in hand, if no item, get stun instead.
var/obj/item/I = get_active_held_item()
if(I && dropItemToGround(I))
playsound(loc, 'sound/weapons/slash.ogg', 25, 1, -1)
visible_message("<span class='danger'>[M] disarmed [src]!</span>", \
"<span class='userdanger'>[M] disarmed [src]!</span>")
else
playsound(loc, 'sound/weapons/pierce.ogg', 25, 1, -1)
if(!lying) //CITADEL EDIT
Knockdown(100, TRUE, FALSE, 30, 25)
else
Knockdown(100)
log_combat(M, src, "tackled")
visible_message("<span class='danger'>[M] has tackled down [src]!</span>", \
"<span class='userdanger'>[M] has tackled down [src]!</span>")
/mob/living/carbon/human/attack_larva(mob/living/carbon/alien/larva/L)
if(..()) //successful larva bite.
var/damage = rand(1, 3)
if(check_shields(L, damage, "the [L.name]"))
return 0
if(stat != DEAD)
L.amount_grown = min(L.amount_grown + damage, L.max_grown)
var/obj/item/bodypart/affecting = get_bodypart(ran_zone(L.zone_selected))
if(!affecting)
affecting = get_bodypart(BODY_ZONE_CHEST)
var/armor_block = run_armor_check(affecting, "melee")
apply_damage(damage, BRUTE, affecting, armor_block)
/mob/living/carbon/human/attack_animal(mob/living/simple_animal/M)
. = ..()
if(.)
var/damage = rand(M.melee_damage_lower, M.melee_damage_upper)
if(check_shields(M, damage, "the [M.name]", MELEE_ATTACK, M.armour_penetration))
return FALSE
var/dam_zone = dismembering_strike(M, pick(BODY_ZONE_CHEST, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG))
if(!dam_zone) //Dismemberment successful
return TRUE
var/obj/item/bodypart/affecting = get_bodypart(ran_zone(dam_zone))
if(!affecting)
affecting = get_bodypart(BODY_ZONE_CHEST)
var/armor = run_armor_check(affecting, "melee", armour_penetration = M.armour_penetration)
apply_damage(damage, M.melee_damage_type, affecting, armor)
/mob/living/carbon/human/attack_slime(mob/living/simple_animal/slime/M)
if(..()) //successful slime attack
var/damage = rand(5, 25)
if(M.is_adult)
damage = rand(10, 35)
if(check_shields(M, damage, "the [M.name]"))
return 0
var/dam_zone = dismembering_strike(M, pick(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG))
if(!dam_zone) //Dismemberment successful
return 1
var/obj/item/bodypart/affecting = get_bodypart(ran_zone(dam_zone))
if(!affecting)
affecting = get_bodypart(BODY_ZONE_CHEST)
var/armor_block = run_armor_check(affecting, "melee")
apply_damage(damage, BRUTE, affecting, armor_block)
/mob/living/carbon/human/mech_melee_attack(obj/mecha/M)
if(M.occupant.a_intent == INTENT_HARM)
M.do_attack_animation(src)
if(M.damtype == "brute")
step_away(src,M,15)
var/obj/item/bodypart/temp = get_bodypart(pick(BODY_ZONE_CHEST, BODY_ZONE_CHEST, BODY_ZONE_CHEST, BODY_ZONE_HEAD))
if(temp)
var/update = 0
var/dmg = rand(M.force/2, M.force)
switch(M.damtype)
if("brute")
if(M.force > 20)
Unconscious(20)
update |= temp.receive_damage(dmg, 0)
playsound(src, 'sound/weapons/punch4.ogg', 50, 1)
if("fire")
update |= temp.receive_damage(0, dmg)
playsound(src, 'sound/items/welder.ogg', 50, 1)
if("tox")
M.mech_toxin_damage(src)
else
return
if(update)
update_damage_overlays()
updatehealth()
visible_message("<span class='danger'>[M.name] has hit [src]!</span>", \
"<span class='userdanger'>[M.name] has hit [src]!</span>", null, COMBAT_MESSAGE_RANGE)
log_combat(M.occupant, src, "attacked", M, "(INTENT: [uppertext(M.occupant.a_intent)]) (DAMTYPE: [uppertext(M.damtype)])")
else
..()
/mob/living/carbon/human/ex_act(severity, target, origin)
if(origin && istype(origin, /datum/spacevine_mutation) && isvineimmune(src))
return
..()
if (!severity)
return
var/b_loss = 0
var/f_loss = 0
var/bomb_armor = getarmor(null, "bomb")
switch (severity)
if (1)
if(prob(bomb_armor))
b_loss = 500
var/atom/throw_target = get_edge_target_turf(src, get_dir(src, get_step_away(src, src)))
throw_at(throw_target, 200, 4)
damage_clothes(400 - bomb_armor, BRUTE, "bomb")
else
for(var/I in contents)
var/atom/A = I
A.ex_act(severity)
gib()
return
if (2)
b_loss = 60
f_loss = 60
if(bomb_armor)
b_loss = 30*(2 - round(bomb_armor*0.01, 0.05))
f_loss = b_loss
damage_clothes(200 - bomb_armor, BRUTE, "bomb")
if (!istype(ears, /obj/item/clothing/ears/earmuffs))
adjustEarDamage(30, 120)
if (prob(max(70 - (bomb_armor * 0.5), 0)))
Unconscious(200)
if(3)
b_loss = 30
if(bomb_armor)
b_loss = 15*(2 - round(bomb_armor*0.01, 0.05))
damage_clothes(max(50 - bomb_armor, 0), BRUTE, "bomb")
if (!istype(ears, /obj/item/clothing/ears/earmuffs))
adjustEarDamage(15,60)
if (prob(max(50 - (bomb_armor * 0.5), 0)))
Unconscious(160)
take_overall_damage(b_loss,f_loss)
//attempt to dismember bodyparts
if(severity <= 2 || !bomb_armor)
var/max_limb_loss = round(4/severity) //so you don't lose four limbs at severity 3.
for(var/X in bodyparts)
var/obj/item/bodypart/BP = X
if(prob(50/severity) && !prob(getarmor(BP, "bomb")) && BP.body_zone != BODY_ZONE_HEAD && BP.body_zone != BODY_ZONE_CHEST)
BP.brute_dam = BP.max_damage
BP.dismember()
max_limb_loss--
if(!max_limb_loss)
break
/mob/living/carbon/human/blob_act(obj/structure/blob/B)
if(stat == DEAD)
return
show_message("<span class='userdanger'>The blob attacks you!</span>")
var/dam_zone = pick(BODY_ZONE_CHEST, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
var/obj/item/bodypart/affecting = get_bodypart(ran_zone(dam_zone))
apply_damage(5, BRUTE, affecting, run_armor_check(affecting, "melee"))
//Added a safety check in case you want to shock a human mob directly through electrocute_act.
/mob/living/carbon/human/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, override = 0, tesla_shock = 0, illusion = 0, stun = TRUE)
if(tesla_shock)
var/total_coeff = 1
if(gloves)
var/obj/item/clothing/gloves/G = gloves
if(G.siemens_coefficient <= 0)
total_coeff -= 0.5
if(wear_suit)
var/obj/item/clothing/suit/S = wear_suit
if(S.siemens_coefficient <= 0)
total_coeff -= 0.95
else if(S.siemens_coefficient == (-1))
total_coeff -= 1
siemens_coeff = total_coeff
if(flags_1 & TESLA_IGNORE_1)
siemens_coeff = 0
else if(!safety)
var/gloves_siemens_coeff = 1
if(gloves)
var/obj/item/clothing/gloves/G = gloves
gloves_siemens_coeff = G.siemens_coefficient
siemens_coeff = gloves_siemens_coeff
if(undergoing_cardiac_arrest() && !illusion)
if(shock_damage * siemens_coeff >= 1 && prob(25))
var/obj/item/organ/heart/heart = getorganslot(ORGAN_SLOT_HEART)
heart.beating = TRUE
if(stat == CONSCIOUS)
to_chat(src, "<span class='notice'>You feel your heart beating again!</span>")
siemens_coeff *= physiology.siemens_coeff
. = ..(shock_damage,source,siemens_coeff,safety,override,tesla_shock, illusion, stun)
if(.)
electrocution_animation(40)
/mob/living/carbon/human/emp_act(severity)
. = ..()
if(. & EMP_PROTECT_CONTENTS)
return
var/informed = FALSE
for(var/obj/item/bodypart/L in src.bodyparts)
if(L.status == BODYPART_ROBOTIC)
if(!informed)
to_chat(src, "<span class='userdanger'>You feel a sharp pain as your robotic limbs overload.</span>")
informed = TRUE
switch(severity)
if(1)
L.receive_damage(0,10)
Stun(200)
if(2)
L.receive_damage(0,5)
Stun(100)
/mob/living/carbon/human/acid_act(acidpwr, acid_volume, bodyzone_hit)
var/list/damaged = list()
var/list/inventory_items_to_kill = list()
var/acidity = acidpwr * min(acid_volume*0.005, 0.1)
//HEAD//
if(!bodyzone_hit || bodyzone_hit == BODY_ZONE_HEAD) //only if we didn't specify a zone or if that zone is the head.
var/obj/item/clothing/head_clothes = null
if(glasses)
head_clothes = glasses
if(wear_mask)
head_clothes = wear_mask
if(wear_neck)
head_clothes = wear_neck
if(head)
head_clothes = head
if(head_clothes)
if(!(head_clothes.resistance_flags & UNACIDABLE))
head_clothes.acid_act(acidpwr, acid_volume)
update_inv_glasses()
update_inv_wear_mask()
update_inv_neck()
update_inv_head()
else
to_chat(src, "<span class='notice'>Your [head_clothes.name] protects your head and face from the acid!</span>")
else
. = get_bodypart(BODY_ZONE_HEAD)
if(.)
damaged += .
if(ears)
inventory_items_to_kill += ears
//CHEST//
if(!bodyzone_hit || bodyzone_hit == BODY_ZONE_CHEST)
var/obj/item/clothing/chest_clothes = null
if(w_uniform)
chest_clothes = w_uniform
if(wear_suit)
chest_clothes = wear_suit
if(chest_clothes)
if(!(chest_clothes.resistance_flags & UNACIDABLE))
chest_clothes.acid_act(acidpwr, acid_volume)
update_inv_w_uniform()
update_inv_wear_suit()
else
to_chat(src, "<span class='notice'>Your [chest_clothes.name] protects your body from the acid!</span>")
else
. = get_bodypart(BODY_ZONE_CHEST)
if(.)
damaged += .
if(wear_id)
inventory_items_to_kill += wear_id
if(r_store)
inventory_items_to_kill += r_store
if(l_store)
inventory_items_to_kill += l_store
if(s_store)
inventory_items_to_kill += s_store
//ARMS & HANDS//
if(!bodyzone_hit || bodyzone_hit == BODY_ZONE_L_ARM || bodyzone_hit == BODY_ZONE_R_ARM)
var/obj/item/clothing/arm_clothes = null
if(gloves)
arm_clothes = gloves
if(w_uniform && ((w_uniform.body_parts_covered & HANDS) || (w_uniform.body_parts_covered & ARMS)))
arm_clothes = w_uniform
if(wear_suit && ((wear_suit.body_parts_covered & HANDS) || (wear_suit.body_parts_covered & ARMS)))
arm_clothes = wear_suit
if(arm_clothes)
if(!(arm_clothes.resistance_flags & UNACIDABLE))
arm_clothes.acid_act(acidpwr, acid_volume)
update_inv_gloves()
update_inv_w_uniform()
update_inv_wear_suit()
else
to_chat(src, "<span class='notice'>Your [arm_clothes.name] protects your arms and hands from the acid!</span>")
else
. = get_bodypart(BODY_ZONE_R_ARM)
if(.)
damaged += .
. = get_bodypart(BODY_ZONE_L_ARM)
if(.)
damaged += .
//LEGS & FEET//
if(!bodyzone_hit || bodyzone_hit == BODY_ZONE_L_LEG || bodyzone_hit == BODY_ZONE_R_LEG || bodyzone_hit == "feet")
var/obj/item/clothing/leg_clothes = null
if(shoes)
leg_clothes = shoes
if(w_uniform && ((w_uniform.body_parts_covered & FEET) || (bodyzone_hit != "feet" && (w_uniform.body_parts_covered & LEGS))))
leg_clothes = w_uniform
if(wear_suit && ((wear_suit.body_parts_covered & FEET) || (bodyzone_hit != "feet" && (wear_suit.body_parts_covered & LEGS))))
leg_clothes = wear_suit
if(leg_clothes)
if(!(leg_clothes.resistance_flags & UNACIDABLE))
leg_clothes.acid_act(acidpwr, acid_volume)
update_inv_shoes()
update_inv_w_uniform()
update_inv_wear_suit()
else
to_chat(src, "<span class='notice'>Your [leg_clothes.name] protects your legs and feet from the acid!</span>")
else
. = get_bodypart(BODY_ZONE_R_LEG)
if(.)
damaged += .
. = get_bodypart(BODY_ZONE_L_LEG)
if(.)
damaged += .
//DAMAGE//
for(var/obj/item/bodypart/affecting in damaged)
affecting.receive_damage(acidity, 2*acidity)
if(affecting.name == BODY_ZONE_HEAD)
if(prob(min(acidpwr*acid_volume/10, 90))) //Applies disfigurement
affecting.receive_damage(acidity, 2*acidity)
emote("scream")
facial_hair_style = "Shaved"
hair_style = "Bald"
update_hair()
ADD_TRAIT(src, TRAIT_DISFIGURED, TRAIT_GENERIC)
update_damage_overlays()
//MELTING INVENTORY ITEMS//
//these items are all outside of armour visually, so melt regardless.
if(!bodyzone_hit)
if(back)
inventory_items_to_kill += back
if(belt)
inventory_items_to_kill += belt
inventory_items_to_kill += held_items
for(var/obj/item/I in inventory_items_to_kill)
I.acid_act(acidpwr, acid_volume)
return 1
/mob/living/carbon/human/singularity_act()
var/gain = 20
if(mind)
if((mind.assigned_role == "Station Engineer") || (mind.assigned_role == "Chief Engineer") )
gain = 100
if(mind.assigned_role == "Clown")
gain = rand(-300, 300)
investigate_log("([key_name(src)]) has been consumed by the singularity.", INVESTIGATE_SINGULO) //Oh that's where the clown ended up!
gib()
return(gain)
/mob/living/carbon/human/help_shake_act(mob/living/carbon/M)
if(!istype(M))
return
if(health >= 0)
if(src == M)
var/to_send = ""
visible_message("[src] examines [p_them()]self.", \
"<span class='notice'>You check yourself for injuries.</span>")
var/list/missing = list(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
for(var/X in bodyparts)
var/obj/item/bodypart/LB = X
missing -= LB.body_zone
if(LB.is_pseudopart) //don't show injury text for fake bodyparts; ie chainsaw arms or synthetic armblades
continue
var/limb_max_damage = LB.max_damage
var/status = ""
var/brutedamage = LB.brute_dam
var/burndamage = LB.burn_dam
if(hallucination)
if(prob(30))
brutedamage += rand(30,40)
if(prob(30))
burndamage += rand(30,40)
if(HAS_TRAIT(src, TRAIT_SELF_AWARE))
status = "[brutedamage] brute damage and [burndamage] burn damage"
if(!brutedamage && !burndamage)
status = "no damage"
else
if(brutedamage > 0)
status = LB.light_brute_msg
if(brutedamage > (limb_max_damage*0.4))
status = LB.medium_brute_msg
if(brutedamage > (limb_max_damage*0.8))
status = LB.heavy_brute_msg
if(brutedamage > 0 && burndamage > 0)
status += " and "
if(burndamage > (limb_max_damage*0.8))
status += LB.heavy_burn_msg
else if(burndamage > (limb_max_damage*0.2))
status += LB.medium_burn_msg
else if(burndamage > 0)
status += LB.light_burn_msg
if(status == "")
status = "OK"
var/no_damage
if(status == "OK" || status == "no damage")
no_damage = TRUE
to_send += "\t <span class='[no_damage ? "notice" : "warning"]'>Your [LB.name] [HAS_TRAIT(src, TRAIT_SELF_AWARE) ? "has" : "is"] [status].</span>\n"
for(var/obj/item/I in LB.embedded_objects)
to_send += "\t <a href='?src=[REF(src)];embedded_object=[REF(I)];embedded_limb=[REF(LB)]' class='warning'>There is \a [I] embedded in your [LB.name]!</a>\n"
for(var/t in missing)
to_send += "<span class='boldannounce'>Your [parse_zone(t)] is missing!</span>\n"
if(bleed_rate)
to_send += "<span class='danger'>You are bleeding!</span>\n"
if(getStaminaLoss())
if(getStaminaLoss() > 30)
to_send += "<span class='info'>You're completely exhausted.</span>\n"
else
to_send += "<span class='info'>You feel fatigued.</span>\n"
if(HAS_TRAIT(src, TRAIT_SELF_AWARE))
if(toxloss)
if(toxloss > 10)
to_send += "<span class='danger'>You feel sick.</span>\n"
else if(toxloss > 20)
to_send += "<span class='danger'>You feel nauseated.</span>\n"
else if(toxloss > 40)
to_send += "<span class='danger'>You feel very unwell!</span>\n"
if(oxyloss)
if(oxyloss > 10)
to_send += "<span class='danger'>You feel lightheaded.</span>\n"
else if(oxyloss > 20)
to_send += "<span class='danger'>Your thinking is clouded and distant.</span>\n"
else if(oxyloss > 30)
to_send += "<span class='danger'>You're choking!</span>\n"
switch(nutrition)
if(NUTRITION_LEVEL_FULL to INFINITY)
to_send += "<span class='info'>You're completely stuffed!</span>\n"
if(NUTRITION_LEVEL_WELL_FED to NUTRITION_LEVEL_FULL)
to_send += "<span class='info'>You're well fed!</span>\n"
if(NUTRITION_LEVEL_FED to NUTRITION_LEVEL_WELL_FED)
to_send += "<span class='info'>You're not hungry.</span>\n"
if(NUTRITION_LEVEL_HUNGRY to NUTRITION_LEVEL_FED)
to_send += "<span class='info'>You could use a bite to eat.</span>\n"
if(NUTRITION_LEVEL_STARVING to NUTRITION_LEVEL_HUNGRY)
to_send += "<span class='info'>You feel quite hungry.</span>\n"
if(0 to NUTRITION_LEVEL_STARVING)
to_send += "<span class='danger'>You're starving!</span>\n"
if(roundstart_quirks.len)
to_send += "<span class='notice'>You have these quirks: [get_trait_string()].</span>\n"
to_chat(src, to_send)
else
if(wear_suit)
wear_suit.add_fingerprint(M)
else if(w_uniform)
w_uniform.add_fingerprint(M)
..()
/mob/living/carbon/human/damage_clothes(damage_amount, damage_type = BRUTE, damage_flag = 0, def_zone)
if(damage_type != BRUTE && damage_type != BURN)
return
damage_amount *= 0.5 //0.5 multiplier for balance reason, we don't want clothes to be too easily destroyed
var/list/torn_items = list()
//HEAD//
if(!def_zone || def_zone == BODY_ZONE_HEAD)
var/obj/item/clothing/head_clothes = null
if(glasses)
head_clothes = glasses
if(wear_mask)
head_clothes = wear_mask
if(wear_neck)
head_clothes = wear_neck
if(head)
head_clothes = head
if(head_clothes)
torn_items += head_clothes
else if(ears)
torn_items += ears
//CHEST//
if(!def_zone || def_zone == BODY_ZONE_CHEST)
var/obj/item/clothing/chest_clothes = null
if(w_uniform)
chest_clothes = w_uniform
if(wear_suit)
chest_clothes = wear_suit
if(chest_clothes)
torn_items += chest_clothes
//ARMS & HANDS//
if(!def_zone || def_zone == BODY_ZONE_L_ARM || def_zone == BODY_ZONE_R_ARM)
var/obj/item/clothing/arm_clothes = null
if(gloves)
arm_clothes = gloves
if(w_uniform && ((w_uniform.body_parts_covered & HANDS) || (w_uniform.body_parts_covered & ARMS)))
arm_clothes = w_uniform
if(wear_suit && ((wear_suit.body_parts_covered & HANDS) || (wear_suit.body_parts_covered & ARMS)))
arm_clothes = wear_suit
if(arm_clothes)
torn_items |= arm_clothes
//LEGS & FEET//
if(!def_zone || def_zone == BODY_ZONE_L_LEG || def_zone == BODY_ZONE_R_LEG)
var/obj/item/clothing/leg_clothes = null
if(shoes)
leg_clothes = shoes
if(w_uniform && ((w_uniform.body_parts_covered & FEET) || (w_uniform.body_parts_covered & LEGS)))
leg_clothes = w_uniform
if(wear_suit && ((wear_suit.body_parts_covered & FEET) || (wear_suit.body_parts_covered & LEGS)))
leg_clothes = wear_suit
if(leg_clothes)
torn_items |= leg_clothes
for(var/obj/item/I in torn_items)
I.take_damage(damage_amount, damage_type, damage_flag, 0)
@@ -0,0 +1,65 @@
/mob/living/carbon/human
hud_possible = list(HEALTH_HUD,STATUS_HUD,ID_HUD,WANTED_HUD,IMPLOYAL_HUD,IMPCHEM_HUD,IMPTRACK_HUD, NANITE_HUD, DIAG_NANITE_FULL_HUD,ANTAG_HUD,GLAND_HUD,SENTIENT_DISEASE_HUD)
hud_type = /datum/hud/human
possible_a_intents = list(INTENT_HELP, INTENT_DISARM, INTENT_GRAB, INTENT_HARM)
pressure_resistance = 25
can_buckle = TRUE
buckle_lying = FALSE
mob_biotypes = list(MOB_ORGANIC, MOB_HUMANOID)
//Hair colour and style
var/hair_color = "000"
var/hair_style = "Bald"
//Facial hair colour and style
var/facial_hair_color = "000"
var/facial_hair_style = "Shaved"
//Eye colour
var/eye_color = "000"
var/skin_tone = "caucasian1" //Skin tone
var/lip_style = null //no lipstick by default- arguably misleading, as it could be used for general makeup
var/lip_color = "white"
var/age = 30 //Player's age
var/underwear = "Nude" //Which underwear the player wants
var/undie_color = "#FFFFFF"
var/undershirt = "Nude" //Which undershirt the player wants
var/shirt_color = "#FFFFFF"
var/socks = "Nude" //Which socks the player wants
var/socks_color = "#FFFFFF"
var/backbag = DBACKPACK //Which backpack type the player has chosen.
var/jumpsuit_style = PREF_SUIT //suit/skirt
//Equipment slots
var/obj/item/wear_suit = null
var/obj/item/w_uniform = null
var/obj/item/belt = null
var/obj/item/wear_id = null
var/obj/item/r_store = null
var/obj/item/l_store = null
var/obj/item/s_store = null
var/special_voice = "" // For changing our voice. Used by a symptom.
var/bleed_rate = 0 //how much are we bleeding
var/bleedsuppress = 0 //for stopping bloodloss, eventually this will be limb-based like bleeding
var/name_override //For temporary visible name changes
var/genital_override = FALSE //Force genitals on things incase of chems
var/nameless = FALSE //For drones of both the insectoid and robotic kind. And other types of nameless critters.
var/custom_species = null
var/datum/personal_crafting/handcrafting
var/datum/physiology/physiology
var/list/datum/bioware = list()
var/creamed = FALSE //to use with creampie overlays
var/static/list/can_ride_typecache = typecacheof(list(/mob/living/carbon/human, /mob/living/simple_animal/slime, /mob/living/simple_animal/parrot))
var/lastpuke = 0
var/last_fire_update
@@ -0,0 +1,129 @@
/mob/living/carbon/human/restrained(ignore_grab)
. = ((wear_suit && wear_suit.breakouttime) || ..())
/mob/living/carbon/human/canBeHandcuffed()
if(get_num_arms(FALSE) >= 2)
return TRUE
else
return FALSE
//gets assignment from ID or ID inside PDA or PDA itself
//Useful when player do something with computers
/mob/living/carbon/human/proc/get_assignment(if_no_id = "No id", if_no_job = "No job")
var/obj/item/card/id/id = get_idcard()
if(id)
. = id.assignment
else
var/obj/item/pda/pda = wear_id
if(istype(pda))
. = pda.ownjob
else
return if_no_id
if(!.)
return if_no_job
//gets name from ID or ID inside PDA or PDA itself
//Useful when player do something with computers
/mob/living/carbon/human/proc/get_authentification_name(if_no_id = "Unknown")
var/obj/item/card/id/id = get_idcard()
if(id)
return id.registered_name
var/obj/item/pda/pda = wear_id
if(istype(pda))
return pda.owner
return if_no_id
//repurposed proc. Now it combines get_id_name() and get_face_name() to determine a mob's name variable. Made into a separate proc as it'll be useful elsewhere
/mob/living/carbon/human/get_visible_name()
var/face_name = get_face_name("")
var/id_name = get_id_name("")
if(name_override)
return name_override
if(face_name)
if(id_name && (id_name != face_name))
return "[face_name] (as [id_name])"
return face_name
if(id_name)
return id_name
return "Unknown"
//Returns "Unknown" if facially disfigured and real_name if not. Useful for setting name when Fluacided or when updating a human's name variable
/mob/living/carbon/human/proc/get_face_name(if_no_face="Unknown")
if( wear_mask && (wear_mask.flags_inv&HIDEFACE) ) //Wearing a mask which hides our face, use id-name if possible
return if_no_face
if( head && (head.flags_inv&HIDEFACE) )
return if_no_face //Likewise for hats
var/obj/item/bodypart/O = get_bodypart(BODY_ZONE_HEAD)
if( !O || (HAS_TRAIT(src, TRAIT_DISFIGURED)) || (O.brutestate+O.burnstate)>2 || cloneloss>50 || !real_name || nameless) //disfigured. use id-name if possible
return if_no_face
return real_name
//gets name from ID or PDA itself, ID inside PDA doesn't matter
//Useful when player is being seen by other mobs
/mob/living/carbon/human/proc/get_id_name(if_no_id = "Unknown")
var/obj/item/storage/wallet/wallet = wear_id
var/obj/item/pda/pda = wear_id
var/obj/item/card/id/id = wear_id
var/obj/item/modular_computer/tablet/tablet = wear_id
if(istype(wallet))
id = wallet.front_id
if(istype(id))
. = id.registered_name
else if(istype(pda))
. = pda.owner
else if(istype(tablet))
var/obj/item/computer_hardware/card_slot/card_slot = tablet.all_components[MC_CARD]
if(card_slot && (card_slot.stored_card2 || card_slot.stored_card))
if(card_slot.stored_card2) //The second card is the one used for authorization in the ID changing program, so we prioritize it here for consistency
. = card_slot.stored_card2.registered_name
else
if(card_slot.stored_card)
. = card_slot.stored_card.registered_name
if(!.)
. = if_no_id //to prevent null-names making the mob unclickable
return
//gets ID card object from special clothes slot or null.
/mob/living/carbon/human/get_idcard()
if(wear_id)
return wear_id.GetID()
/mob/living/carbon/human/IsAdvancedToolUser()
if(HAS_TRAIT(src, TRAIT_MONKEYLIKE))
return FALSE
return TRUE//Humans can use guns and such
/mob/living/carbon/human/reagent_check(datum/reagent/R)
return dna.species.handle_chemicals(R,src)
// if it returns 0, it will run the usual on_mob_life for that reagent. otherwise, it will stop after running handle_chemicals for the species.
/mob/living/carbon/human/can_track(mob/living/user)
if(wear_id && istype(wear_id.GetID(), /obj/item/card/id/syndicate))
return 0
if(istype(head, /obj/item/clothing/head))
var/obj/item/clothing/head/hat = head
if(hat.blockTracking)
return 0
return ..()
/mob/living/carbon/human/can_use_guns(obj/item/G)
. = ..()
if(G.trigger_guard == TRIGGER_GUARD_NORMAL)
if(src.dna.check_mutation(HULK))
to_chat(src, "<span class='warning'>Your meaty finger is much too large for the trigger guard!</span>")
return FALSE
if(HAS_TRAIT(src, TRAIT_NOGUNS))
to_chat(src, "<span class='warning'>Your fingers don't fit in the trigger guard!</span>")
return FALSE
if(mind)
if(mind.martial_art && mind.martial_art.no_guns) //great dishonor to famiry
to_chat(src, "<span class='warning'>Use of ranged weaponry would bring dishonor to the clan.</span>")
return FALSE
return .
@@ -0,0 +1,78 @@
/mob/living/carbon/human/get_movespeed_modifiers()
var/list/considering = ..()
. = considering
if(HAS_TRAIT(src, TRAIT_IGNORESLOWDOWN))
for(var/id in .)
var/list/data = .[id]
if(data[MOVESPEED_DATA_INDEX_FLAGS] & IGNORE_NOSLOW)
.[id] = data
/mob/living/carbon/human/movement_delay()
. = ..()
if(dna && dna.species)
. += dna.species.movement_delay(src)
/mob/living/carbon/human/slip(knockdown_amount, obj/O, lube)
if(HAS_TRAIT(src, TRAIT_NOSLIPALL))
return 0
if (!(lube&GALOSHES_DONT_HELP))
if(HAS_TRAIT(src, TRAIT_NOSLIPWATER))
return 0
if(shoes && istype(shoes, /obj/item/clothing))
var/obj/item/clothing/CS = shoes
if (CS.clothing_flags & NOSLIP)
return 0
return ..()
/mob/living/carbon/human/experience_pressure_difference()
playsound(src, 'sound/effects/space_wind.ogg', 50, 1)
if(shoes && istype(shoes, /obj/item/clothing))
var/obj/item/clothing/S = shoes
if (S.clothing_flags & NOSLIP)
return 0
return ..()
/mob/living/carbon/human/mob_has_gravity()
. = ..()
if(!.)
if(mob_negates_gravity())
. = 1
/mob/living/carbon/human/mob_negates_gravity()
return ((shoes && shoes.negates_gravity()) || (dna.species.negates_gravity(src)))
/mob/living/carbon/human/Move(NewLoc, direct)
. = ..()
for(var/datum/mutation/human/HM in dna.mutations)
HM.on_move(src, NewLoc)
if(shoes)
if(!lying && !buckled)
if(loc == NewLoc)
if(!has_gravity(loc))
return
var/obj/item/clothing/shoes/S = shoes
//Bloody footprints
var/turf/T = get_turf(src)
if(S.bloody_shoes && S.bloody_shoes[S.blood_state])
for(var/obj/effect/decal/cleanable/blood/footprints/oldFP in T)
if (oldFP.blood_state == S.blood_state)
return
//No oldFP or they're all a different kind of blood
S.bloody_shoes[S.blood_state] = max(0, S.bloody_shoes[S.blood_state] - BLOOD_LOSS_PER_STEP)
if (S.bloody_shoes[S.blood_state] > BLOOD_LOSS_IN_SPREAD)
var/obj/effect/decal/cleanable/blood/footprints/FP = new /obj/effect/decal/cleanable/blood/footprints(T)
FP.blood_state = S.blood_state
FP.entered_dirs |= dir
FP.bloodiness = S.bloody_shoes[S.blood_state] - BLOOD_LOSS_IN_SPREAD
FP.add_blood_DNA(S.return_blood_DNA())
FP.update_icon()
update_inv_shoes()
//End bloody footprints
S.step_action()
/mob/living/carbon/human/Process_Spacemove(movement_dir = 0) //Temporary laziness thing. Will change to handles by species reee.
if(dna.species.space_move(src))
return TRUE
return ..()
@@ -0,0 +1,272 @@
/mob/living/carbon/human/can_equip(obj/item/I, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE)
return dna.species.can_equip(I, slot, disable_warning, src, bypass_equip_delay_self)
// Return the item currently in the slot ID
/mob/living/carbon/human/get_item_by_slot(slot_id)
switch(slot_id)
if(SLOT_BACK)
return back
if(SLOT_WEAR_MASK)
return wear_mask
if(SLOT_NECK)
return wear_neck
if(SLOT_HANDCUFFED)
return handcuffed
if(SLOT_LEGCUFFED)
return legcuffed
if(SLOT_BELT)
return belt
if(SLOT_WEAR_ID)
return wear_id
if(SLOT_EARS)
return ears
if(SLOT_GLASSES)
return glasses
if(SLOT_GLOVES)
return gloves
if(SLOT_HEAD)
return head
if(SLOT_SHOES)
return shoes
if(SLOT_WEAR_SUIT)
return wear_suit
if(SLOT_W_UNIFORM)
return w_uniform
if(SLOT_L_STORE)
return l_store
if(SLOT_R_STORE)
return r_store
if(SLOT_S_STORE)
return s_store
return null
/mob/living/carbon/human/proc/get_all_slots()
. = get_head_slots() | get_body_slots()
/mob/living/carbon/human/proc/get_body_slots()
return list(
back,
s_store,
handcuffed,
legcuffed,
wear_suit,
gloves,
shoes,
belt,
wear_id,
l_store,
r_store,
w_uniform
)
/mob/living/carbon/human/proc/get_head_slots()
return list(
head,
wear_mask,
wear_neck,
glasses,
ears,
)
/mob/living/carbon/human/proc/get_storage_slots()
return list(
back,
belt,
l_store,
r_store,
s_store,
)
//This is an UNSAFE proc. Use mob_can_equip() before calling this one! Or rather use equip_to_slot_if_possible() or advanced_equip_to_slot_if_possible()
/mob/living/carbon/human/equip_to_slot(obj/item/I, slot)
if(!..()) //a check failed or the item has already found its slot
return
var/not_handled = FALSE //Added in case we make this type path deeper one day
switch(slot)
if(SLOT_BELT)
belt = I
update_inv_belt()
if(SLOT_WEAR_ID)
wear_id = I
sec_hud_set_ID()
update_inv_wear_id()
if(SLOT_EARS)
ears = I
update_inv_ears()
if(SLOT_GLASSES)
glasses = I
var/obj/item/clothing/glasses/G = I
if(G.glass_colour_type)
update_glasses_color(G, 1)
if(G.tint)
update_tint()
if(G.vision_correction)
clear_fullscreen("nearsighted")
clear_fullscreen("eye_damage")
if(G.vision_flags || G.darkness_view || G.invis_override || G.invis_view || !isnull(G.lighting_alpha))
update_sight()
update_inv_glasses()
if(SLOT_GLOVES)
gloves = I
update_inv_gloves()
if(SLOT_SHOES)
shoes = I
update_inv_shoes()
if(SLOT_WEAR_SUIT)
wear_suit = I
if(I.flags_inv & HIDEJUMPSUIT)
update_inv_w_uniform()
if(wear_suit.breakouttime) //when equipping a straightjacket
stop_pulling() //can't pull if restrained
update_action_buttons_icon() //certain action buttons will no longer be usable.
update_inv_wear_suit()
if(SLOT_W_UNIFORM)
w_uniform = I
update_suit_sensors()
update_inv_w_uniform()
if(SLOT_L_STORE)
l_store = I
update_inv_pockets()
if(SLOT_R_STORE)
r_store = I
update_inv_pockets()
if(SLOT_S_STORE)
s_store = I
update_inv_s_store()
else
to_chat(src, "<span class='danger'>You are trying to equip this item to an unsupported inventory slot. Report this to a coder!</span>")
//Item is handled and in slot, valid to call callback, for this proc should always be true
if(!not_handled)
I.equipped(src, slot)
return not_handled //For future deeper overrides
/mob/living/carbon/human/doUnEquip(obj/item/I, force, newloc, no_move, invdrop = TRUE)
var/index = get_held_index_of_item(I)
. = ..() //See mob.dm for an explanation on this and some rage about people copypasting instead of calling ..() like they should.
if(!. || !I)
return
if(index && !QDELETED(src) && dna.species.mutanthands) //hand freed, fill with claws, skip if we're getting deleted.
put_in_hand(new dna.species.mutanthands(), index)
if(I == wear_suit)
if(s_store && invdrop)
dropItemToGround(s_store, TRUE) //It makes no sense for your suit storage to stay on you if you drop your suit.
if(wear_suit.breakouttime) //when unequipping a straightjacket
drop_all_held_items() //suit is restraining
update_action_buttons_icon() //certain action buttons may be usable again.
wear_suit = null
if(!QDELETED(src)) //no need to update we're getting deleted anyway
if(I.flags_inv & HIDEJUMPSUIT)
update_inv_w_uniform()
update_inv_wear_suit()
else if(I == w_uniform)
if(invdrop)
if(r_store)
dropItemToGround(r_store, TRUE) //Again, makes sense for pockets to drop.
if(l_store)
dropItemToGround(l_store, TRUE)
if(wear_id && !CHECK_BITFIELD(wear_id.item_flags, NO_UNIFORM_REQUIRED))
dropItemToGround(wear_id)
if(belt && !CHECK_BITFIELD(belt.item_flags, NO_UNIFORM_REQUIRED))
dropItemToGround(belt)
w_uniform = null
update_suit_sensors()
if(!QDELETED(src))
update_inv_w_uniform()
else if(I == gloves)
gloves = null
if(!QDELETED(src))
update_inv_gloves()
else if(I == glasses)
glasses = null
var/obj/item/clothing/glasses/G = I
if(G.glass_colour_type)
update_glasses_color(G, 0)
if(G.tint)
update_tint()
if(G.vision_correction)
if(HAS_TRAIT(src, TRAIT_NEARSIGHT))
overlay_fullscreen("nearsighted", /obj/screen/fullscreen/impaired, 1)
adjust_eye_damage(0)
if(G.vision_flags || G.darkness_view || G.invis_override || G.invis_view || !isnull(G.lighting_alpha))
update_sight()
if(!QDELETED(src))
update_inv_glasses()
else if(I == ears)
ears = null
if(!QDELETED(src))
update_inv_ears()
else if(I == shoes)
shoes = null
if(!QDELETED(src))
update_inv_shoes()
else if(I == belt)
belt = null
if(!QDELETED(src))
update_inv_belt()
else if(I == wear_id)
wear_id = null
sec_hud_set_ID()
if(!QDELETED(src))
update_inv_wear_id()
else if(I == r_store)
r_store = null
if(!QDELETED(src))
update_inv_pockets()
else if(I == l_store)
l_store = null
if(!QDELETED(src))
update_inv_pockets()
else if(I == s_store)
s_store = null
if(!QDELETED(src))
update_inv_s_store()
/mob/living/carbon/human/wear_mask_update(obj/item/clothing/C, toggle_off = 1)
if((C.flags_inv & (HIDEHAIR|HIDEFACIALHAIR)) || (initial(C.flags_inv) & (HIDEHAIR|HIDEFACIALHAIR)))
update_hair()
if(toggle_off && internal && !getorganslot(ORGAN_SLOT_BREATHING_TUBE))
update_internals_hud_icon(0)
internal = null
if(C.flags_inv & HIDEEYES)
update_inv_glasses()
sec_hud_set_security_status()
..()
/mob/living/carbon/human/head_update(obj/item/I, forced)
if((I.flags_inv & (HIDEHAIR|HIDEFACIALHAIR)) || forced)
update_hair()
else
var/obj/item/clothing/C = I
if(istype(C) && C.dynamic_hair_suffix)
update_hair()
if(I.flags_inv & HIDEEYES || forced)
update_inv_glasses()
if(I.flags_inv & HIDEEARS || forced)
update_body()
sec_hud_set_security_status()
..()
/mob/living/carbon/human/proc/equipOutfit(outfit, visualsOnly = FALSE)
var/datum/outfit/O = null
if(ispath(outfit))
O = new outfit
else
O = outfit
if(!istype(O))
return 0
if(!O)
return 0
return O.equip(src, visualsOnly)
//delete all equipment without dropping anything
/mob/living/carbon/human/proc/delete_equipment()
for(var/slot in get_all_slots())//order matters, dependant slots go first
qdel(slot)
for(var/obj/item/I in held_items)
qdel(I)
@@ -0,0 +1,359 @@
//NOTE: Breathing happens once per FOUR TICKS, unless the last breath fails. In which case it happens once per ONE TICK! So oxyloss healing is done once per 4 ticks while oxyloss damage is applied once per tick!
// bitflags for the percentual amount of protection a piece of clothing which covers the body part offers.
// Used with human/proc/get_heat_protection() and human/proc/get_cold_protection()
// The values here should add up to 1.
// Hands and feet have 2.5%, arms and legs 7.5%, each of the torso parts has 15% and the head has 30%
#define THERMAL_PROTECTION_HEAD 0.3
#define THERMAL_PROTECTION_CHEST 0.15
#define THERMAL_PROTECTION_GROIN 0.15
#define THERMAL_PROTECTION_LEG_LEFT 0.075
#define THERMAL_PROTECTION_LEG_RIGHT 0.075
#define THERMAL_PROTECTION_FOOT_LEFT 0.025
#define THERMAL_PROTECTION_FOOT_RIGHT 0.025
#define THERMAL_PROTECTION_ARM_LEFT 0.075
#define THERMAL_PROTECTION_ARM_RIGHT 0.075
#define THERMAL_PROTECTION_HAND_LEFT 0.025
#define THERMAL_PROTECTION_HAND_RIGHT 0.025
/mob/living/carbon/human/Life()
set invisibility = 0
if (notransform)
return
. = ..()
if (QDELETED(src))
return 0
if(.) //not dead
handle_active_genes()
if(stat != DEAD)
//heart attack stuff
handle_heart()
if(stat != DEAD)
//Stuff jammed in your limbs hurts
handle_embedded_objects()
//Update our name based on whether our face is obscured/disfigured
name = get_visible_name()
dna.species.spec_life(src) // for mutantraces
if(stat != DEAD)
return 1
/mob/living/carbon/human/calculate_affecting_pressure(pressure)
if (wear_suit && head && istype(wear_suit, /obj/item/clothing) && istype(head, /obj/item/clothing))
var/obj/item/clothing/CS = wear_suit
var/obj/item/clothing/CH = head
if (CS.clothing_flags & CH.clothing_flags & STOPSPRESSUREDAMAGE)
return ONE_ATMOSPHERE
if(istype(loc, /obj/belly)) //START OF CIT CHANGES - Makes it so you don't suffocate while inside vore organs. Remind me to modularize this some time - Bhijn
return ONE_ATMOSPHERE
if(istype(loc, /obj/item/dogborg/sleeper))
return ONE_ATMOSPHERE //END OF CIT CHANGES
return pressure
/mob/living/carbon/human/handle_traits()
if(eye_blind) //blindness, heals slowly over time
if(HAS_TRAIT_FROM(src, TRAIT_BLIND, EYES_COVERED)) //covering your eyes heals blurry eyes faster
adjust_blindness(-3)
else
adjust_blindness(-1)
else if(eye_blurry) //blurry eyes heal slowly
adjust_blurriness(-1)
if (getBrainLoss() >= 30) //Citadel change to make memes more often.
SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "brain_damage", /datum/mood_event/brain_damage)
if(prob(3))
if(prob(25))
emote("drool")
else
say(pick_list_replacements(BRAIN_DAMAGE_FILE, "brain_damage"))
else
SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "brain_damage")
/mob/living/carbon/human/handle_mutations_and_radiation()
if(!dna || !dna.species.handle_mutations_and_radiation(src))
..()
/mob/living/carbon/human/breathe()
if(!dna.species.breathe(src))
..()
/mob/living/carbon/human/check_breath(datum/gas_mixture/breath)
var/L = getorganslot(ORGAN_SLOT_LUNGS)
if(!L)
if(health >= crit_threshold)
adjustOxyLoss(HUMAN_MAX_OXYLOSS + 1)
else if(!HAS_TRAIT(src, TRAIT_NOCRITDAMAGE))
adjustOxyLoss(HUMAN_CRIT_MAX_OXYLOSS)
failed_last_breath = 1
var/datum/species/S = dna.species
if(S.breathid == "o2")
throw_alert("not_enough_oxy", /obj/screen/alert/not_enough_oxy)
else if(S.breathid == "tox")
throw_alert("not_enough_tox", /obj/screen/alert/not_enough_tox)
else if(S.breathid == "co2")
throw_alert("not_enough_co2", /obj/screen/alert/not_enough_co2)
else if(S.breathid == "n2")
throw_alert("not_enough_nitro", /obj/screen/alert/not_enough_nitro)
return FALSE
else
if(istype(L, /obj/item/organ/lungs))
var/obj/item/organ/lungs/lun = L
lun.check_breath(breath,src)
/mob/living/carbon/human/handle_environment(datum/gas_mixture/environment)
dna.species.handle_environment(environment, src)
///FIRE CODE
/mob/living/carbon/human/handle_fire()
if(!last_fire_update)
last_fire_update = fire_stacks
if((fire_stacks > HUMAN_FIRE_STACK_ICON_NUM && last_fire_update <= HUMAN_FIRE_STACK_ICON_NUM) || (fire_stacks <= HUMAN_FIRE_STACK_ICON_NUM && last_fire_update > HUMAN_FIRE_STACK_ICON_NUM))
last_fire_update = fire_stacks
update_fire()
..()
if(dna)
dna.species.handle_fire(src)
/mob/living/carbon/human/proc/get_thermal_protection()
var/thermal_protection = 0 //Simple check to estimate how protected we are against multiple temperatures
//CITADEL EDIT Vore code required overrides
if(istype(loc, /obj/item/dogborg/sleeper))
return FIRE_IMMUNITY_MAX_TEMP_PROTECT
if(ismob(loc))
return FIRE_IMMUNITY_MAX_TEMP_PROTECT
if(isbelly(loc))
return FIRE_IMMUNITY_MAX_TEMP_PROTECT
//END EDIT
if(wear_suit)
if(wear_suit.max_heat_protection_temperature >= FIRE_SUIT_MAX_TEMP_PROTECT)
thermal_protection += (wear_suit.max_heat_protection_temperature*0.7)
if(head)
if(head.max_heat_protection_temperature >= FIRE_HELM_MAX_TEMP_PROTECT)
thermal_protection += (head.max_heat_protection_temperature*THERMAL_PROTECTION_HEAD)
thermal_protection = round(thermal_protection)
return thermal_protection
/mob/living/carbon/human/IgniteMob()
//If have no DNA or can be Ignited, call parent handling to light user
//If firestacks are high enough
if(!dna || dna.species.CanIgniteMob(src))
return ..()
. = FALSE //No ignition
/mob/living/carbon/human/ExtinguishMob()
if(!dna || !dna.species.ExtinguishMob(src))
last_fire_update = null
..()
//END FIRE CODE
//This proc returns a number made up of the flags for body parts which you are protected on. (such as HEAD, CHEST, GROIN, etc. See setup.dm for the full list)
/mob/living/carbon/human/proc/get_heat_protection_flags(temperature) //Temperature is the temperature you're being exposed to.
var/thermal_protection_flags = 0
//Handle normal clothing
if(head)
if(head.max_heat_protection_temperature && head.max_heat_protection_temperature >= temperature)
thermal_protection_flags |= head.heat_protection
if(wear_suit)
if(wear_suit.max_heat_protection_temperature && wear_suit.max_heat_protection_temperature >= temperature)
thermal_protection_flags |= wear_suit.heat_protection
if(w_uniform)
if(w_uniform.max_heat_protection_temperature && w_uniform.max_heat_protection_temperature >= temperature)
thermal_protection_flags |= w_uniform.heat_protection
if(shoes)
if(shoes.max_heat_protection_temperature && shoes.max_heat_protection_temperature >= temperature)
thermal_protection_flags |= shoes.heat_protection
if(gloves)
if(gloves.max_heat_protection_temperature && gloves.max_heat_protection_temperature >= temperature)
thermal_protection_flags |= gloves.heat_protection
if(wear_mask)
if(wear_mask.max_heat_protection_temperature && wear_mask.max_heat_protection_temperature >= temperature)
thermal_protection_flags |= wear_mask.heat_protection
return thermal_protection_flags
/mob/living/carbon/human/proc/get_heat_protection(temperature) //Temperature is the temperature you're being exposed to.
var/thermal_protection_flags = get_heat_protection_flags(temperature)
var/thermal_protection = 0
if(thermal_protection_flags)
if(thermal_protection_flags & HEAD)
thermal_protection += THERMAL_PROTECTION_HEAD
if(thermal_protection_flags & CHEST)
thermal_protection += THERMAL_PROTECTION_CHEST
if(thermal_protection_flags & GROIN)
thermal_protection += THERMAL_PROTECTION_GROIN
if(thermal_protection_flags & LEG_LEFT)
thermal_protection += THERMAL_PROTECTION_LEG_LEFT
if(thermal_protection_flags & LEG_RIGHT)
thermal_protection += THERMAL_PROTECTION_LEG_RIGHT
if(thermal_protection_flags & FOOT_LEFT)
thermal_protection += THERMAL_PROTECTION_FOOT_LEFT
if(thermal_protection_flags & FOOT_RIGHT)
thermal_protection += THERMAL_PROTECTION_FOOT_RIGHT
if(thermal_protection_flags & ARM_LEFT)
thermal_protection += THERMAL_PROTECTION_ARM_LEFT
if(thermal_protection_flags & ARM_RIGHT)
thermal_protection += THERMAL_PROTECTION_ARM_RIGHT
if(thermal_protection_flags & HAND_LEFT)
thermal_protection += THERMAL_PROTECTION_HAND_LEFT
if(thermal_protection_flags & HAND_RIGHT)
thermal_protection += THERMAL_PROTECTION_HAND_RIGHT
return min(1,thermal_protection)
//See proc/get_heat_protection_flags(temperature) for the description of this proc.
/mob/living/carbon/human/proc/get_cold_protection_flags(temperature)
var/thermal_protection_flags = 0
//Handle normal clothing
if(head)
if(head.min_cold_protection_temperature && head.min_cold_protection_temperature <= temperature)
thermal_protection_flags |= head.cold_protection
if(wear_suit)
if(wear_suit.min_cold_protection_temperature && wear_suit.min_cold_protection_temperature <= temperature)
thermal_protection_flags |= wear_suit.cold_protection
if(w_uniform)
if(w_uniform.min_cold_protection_temperature && w_uniform.min_cold_protection_temperature <= temperature)
thermal_protection_flags |= w_uniform.cold_protection
if(shoes)
if(shoes.min_cold_protection_temperature && shoes.min_cold_protection_temperature <= temperature)
thermal_protection_flags |= shoes.cold_protection
if(gloves)
if(gloves.min_cold_protection_temperature && gloves.min_cold_protection_temperature <= temperature)
thermal_protection_flags |= gloves.cold_protection
if(wear_mask)
if(wear_mask.min_cold_protection_temperature && wear_mask.min_cold_protection_temperature <= temperature)
thermal_protection_flags |= wear_mask.cold_protection
return thermal_protection_flags
/mob/living/carbon/human/proc/get_cold_protection(temperature)
//CITADEL EDIT Mandatory for vore code.
if(istype(loc, /obj/item/dogborg/sleeper))
return TRUE //freezing to death in sleepers ruins fun.
if(isbelly(loc))
return TRUE
if(ismob(loc))
return TRUE //because lazy and being inside somemone insulates you from space
//END EDIT
temperature = max(temperature, 2.7) //There is an occasional bug where the temperature is miscalculated in ares with a small amount of gas on them, so this is necessary to ensure that that bug does not affect this calculation. Space's temperature is 2.7K and most suits that are intended to protect against any cold, protect down to 2.0K.
var/thermal_protection_flags = get_cold_protection_flags(temperature)
var/thermal_protection = 0
if(thermal_protection_flags)
if(thermal_protection_flags & HEAD)
thermal_protection += THERMAL_PROTECTION_HEAD
if(thermal_protection_flags & CHEST)
thermal_protection += THERMAL_PROTECTION_CHEST
if(thermal_protection_flags & GROIN)
thermal_protection += THERMAL_PROTECTION_GROIN
if(thermal_protection_flags & LEG_LEFT)
thermal_protection += THERMAL_PROTECTION_LEG_LEFT
if(thermal_protection_flags & LEG_RIGHT)
thermal_protection += THERMAL_PROTECTION_LEG_RIGHT
if(thermal_protection_flags & FOOT_LEFT)
thermal_protection += THERMAL_PROTECTION_FOOT_LEFT
if(thermal_protection_flags & FOOT_RIGHT)
thermal_protection += THERMAL_PROTECTION_FOOT_RIGHT
if(thermal_protection_flags & ARM_LEFT)
thermal_protection += THERMAL_PROTECTION_ARM_LEFT
if(thermal_protection_flags & ARM_RIGHT)
thermal_protection += THERMAL_PROTECTION_ARM_RIGHT
if(thermal_protection_flags & HAND_LEFT)
thermal_protection += THERMAL_PROTECTION_HAND_LEFT
if(thermal_protection_flags & HAND_RIGHT)
thermal_protection += THERMAL_PROTECTION_HAND_RIGHT
return min(1,thermal_protection)
/mob/living/carbon/human/handle_random_events()
//Puke if toxloss is too high
if(!stat)
if(getToxLoss() >= 45 && nutrition > 20)
lastpuke += prob(50)
if(lastpuke >= 50) // about 25 second delay I guess
vomit(20, toxic = TRUE)
lastpuke = 0
/mob/living/carbon/human/has_smoke_protection()
if(wear_mask)
if(wear_mask.clothing_flags & BLOCK_GAS_SMOKE_EFFECT)
return TRUE
if(glasses)
if(glasses.clothing_flags & BLOCK_GAS_SMOKE_EFFECT)
return TRUE
if(head && istype(head, /obj/item/clothing))
var/obj/item/clothing/CH = head
if(CH.clothing_flags & BLOCK_GAS_SMOKE_EFFECT)
return TRUE
return ..()
/mob/living/carbon/human/proc/handle_embedded_objects()
for(var/X in bodyparts)
var/obj/item/bodypart/BP = X
for(var/obj/item/I in BP.embedded_objects)
if(prob(I.embedding.embedded_pain_chance))
BP.receive_damage(I.w_class*I.embedding.embedded_pain_multiplier)
to_chat(src, "<span class='userdanger'>[I] embedded in your [BP.name] hurts!</span>")
if(prob(I.embedding.embedded_fall_chance))
BP.receive_damage(I.w_class*I.embedding.embedded_fall_pain_multiplier)
BP.embedded_objects -= I
I.forceMove(drop_location())
visible_message("<span class='danger'>[I] falls out of [name]'s [BP.name]!</span>","<span class='userdanger'>[I] falls out of your [BP.name]!</span>")
if(!has_embedded_objects())
clear_alert("embeddedobject")
SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "embedded")
/mob/living/carbon/human/proc/handle_active_genes()
for(var/datum/mutation/human/HM in dna.mutations)
HM.on_life(src)
/mob/living/carbon/human/proc/handle_heart()
var/we_breath = !HAS_TRAIT_FROM(src, TRAIT_NOBREATH, SPECIES_TRAIT)
if(!undergoing_cardiac_arrest())
return
if(we_breath)
adjustOxyLoss(8)
Unconscious(80)
// Tissues die without blood circulation
adjustBruteLoss(2)
#undef THERMAL_PROTECTION_HEAD
#undef THERMAL_PROTECTION_CHEST
#undef THERMAL_PROTECTION_GROIN
#undef THERMAL_PROTECTION_LEG_LEFT
#undef THERMAL_PROTECTION_LEG_RIGHT
#undef THERMAL_PROTECTION_FOOT_LEFT
#undef THERMAL_PROTECTION_FOOT_RIGHT
#undef THERMAL_PROTECTION_ARM_LEFT
#undef THERMAL_PROTECTION_ARM_RIGHT
#undef THERMAL_PROTECTION_HAND_LEFT
#undef THERMAL_PROTECTION_HAND_RIGHT

Some files were not shown because too many files have changed in this diff Show More