mirror of
https://github.com/KabKebab/GS13.git
synced 2026-08-27 07:01:29 +01:00
Uploading all files.
This commit is contained in:
@@ -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
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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()
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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]")
|
||||
|
||||
@@ -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
|
||||
@@ -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()
|
||||
@@ -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
|
||||
@@ -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()
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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].")
|
||||
@@ -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)
|
||||
@@ -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
|
||||
..()
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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."
|
||||
@@ -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
|
||||
@@ -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
Reference in New Issue
Block a user