"
dat += "General Settings "
dat += "UI Style: [UI_style] "
- dat += "Keybindings: [(hotkeys) ? "Hotkeys" : "Default"] "
- dat += "Action Buttons: [(buttons_locked) ? "Locked In Place" : "Unlocked"] "
- dat += "tgui Style: [(tgui_fancy) ? "Fancy" : "No Frills"] "
- dat += "PDA Style: [pda_style] "
- dat += "PDA Color: Change "
dat += "tgui Monitors: [(tgui_lock) ? "Primary" : "All"] "
- dat += "Window Flashing: [(windowflashing) ? "Yes" : "No"] "
- dat += "Play admin midis: [(toggles & SOUND_MIDI) ? "Yes" : "No"] "
- dat += "Play lobby music: [(toggles & SOUND_LOBBY) ? "Yes" : "No"] "
- dat += "Ghost ears: [(chat_toggles & CHAT_GHOSTEARS) ? "All Speech" : "Nearest Creatures"] "
- dat += "Ghost sight: [(chat_toggles & CHAT_GHOSTSIGHT) ? "All Emotes" : "Nearest Creatures"] "
- dat += "Ghost whispers: [(chat_toggles & CHAT_GHOSTWHISPER) ? "All Speech" : "Nearest Creatures"] "
- dat += "Ghost radio: [(chat_toggles & CHAT_GHOSTRADIO) ? "Yes" : "No"] "
- dat += "Ghost pda: [(chat_toggles & CHAT_GHOSTPDA) ? "All Messages" : "Nearest Creatures"] "
- dat += "Pull requests: [(chat_toggles & CHAT_PULLR) ? "Yes" : "No"] "
- dat += "Midround Antagonist: [(toggles & MIDROUND_ANTAG) ? "Yes" : "No"] "
- if(CONFIG_GET(flag/allow_metadata))
- dat += "OOC Notes: Edit "
+ dat += "tgui Style: [(tgui_fancy) ? "Fancy" : "No Frills"] "
+ dat += " "
+ dat += "Action Buttons: [(buttons_locked) ? "Locked In Place" : "Unlocked"] "
+ dat += "Keybindings: [(hotkeys) ? "Hotkeys" : "Default"] "
+ dat += " "
+ dat += "PDA Color: Change "
+ dat += "PDA Style: [pda_style] "
+ dat += " "
+ dat += "Ghost Ears: [(chat_toggles & CHAT_GHOSTEARS) ? "All Speech" : "Nearest Creatures"] "
+ dat += "Ghost Radio: [(chat_toggles & CHAT_GHOSTRADIO) ? "All Messages":"No Messages"] "
+ dat += "Ghost Sight: [(chat_toggles & CHAT_GHOSTSIGHT) ? "All Emotes" : "Nearest Creatures"] "
+ dat += "Ghost Whispers: [(chat_toggles & CHAT_GHOSTWHISPER) ? "All Speech" : "Nearest Creatures"] "
+ dat += "Ghost PDA: [(chat_toggles & CHAT_GHOSTPDA) ? "All Messages" : "Nearest Creatures"] "
- if(user.client)
- if(user.client.holder)
- dat += "Adminhelp Sound: [(toggles & SOUND_ADMINHELP)?"On":"Off"] "
- dat += "Announce Login: [(toggles & ANNOUNCE_LOGIN)?"On":"Off"] "
-
- if(unlock_content || check_rights_for(user.client, R_ADMIN))
- dat += "OOC: Change "
-
- if(unlock_content)
- dat += "BYOND Membership Publicity: [(toggles & MEMBER_PUBLIC) ? "Public" : "Hidden"] "
- dat += "Ghost Form: [ghost_form] "
- dat += "Ghost Orbit: [ghost_orbit] "
+ if(unlock_content)
+ dat += "Ghost Form: [ghost_form] "
+ dat += "Ghost Orbit: [ghost_orbit] "
var/button_name = "If you see this something went wrong."
switch(ghost_accs)
@@ -523,8 +561,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(days_remaining)
dat += "Be [capitalize(i)]: \[IN [days_remaining] DAYS] "
else
- dat += "Be [capitalize(i)]: [(i in be_special) ? "Yes" : "No"] "
- dat += ""
- if(3)
+ dat += ""
+
+ if(3)
if(!gear_tab)
gear_tab = GLOB.loadout_items[1]
dat += ""
@@ -603,9 +673,12 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += ""
var/datum/browser/popup = new(user, "preferences", "Character Setup
", 640, 770)
- popup.set_content(dat)
+ popup.set_content(dat.Join())
popup.open(0)
+#undef APPEARANCE_CATEGORY_COLUMN
+#undef MAX_MUTANT_ROWS
+
/datum/preferences/proc/SetChoices(mob/user, limit = 17, list/splitJobs = list("Chief Engineer"), widthPerColumn = 295, height = 620)
if(!SSjob)
return
@@ -939,8 +1012,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(href_list["jobbancheck"])
var/job = sanitizeSQL(href_list["jobbancheck"])
var/sql_ckey = sanitizeSQL(user.ckey)
- var/datum/DBQuery/query_get_jobban = SSdbcore.NewQuery("SELECT reason, bantime, duration, expiration_time, (SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].a_ckey) FROM [format_table_name("ban")] WHERE ckey = '[sql_ckey]' AND (bantype = 'JOB_PERMABAN' OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned) AND job = '[job]'")
+ var/datum/DBQuery/query_get_jobban = SSdbcore.NewQuery("SELECT reason, bantime, duration, expiration_time, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].a_ckey), a_ckey) FROM [format_table_name("ban")] WHERE ckey = '[sql_ckey]' AND (bantype = 'JOB_PERMABAN' OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned) AND job = '[job]'")
if(!query_get_jobban.warn_execute())
+ qdel(query_get_jobban)
return
if(query_get_jobban.NextRow())
var/reason = query_get_jobban.item[1]
@@ -954,6 +1028,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
text += ". The ban is for [duration] minutes and expires on [expiration_time] (server time)"
text += "."
to_chat(user, text)
+ qdel(query_get_jobban)
return
if(href_list["preference"] == "job")
@@ -1064,6 +1139,11 @@ GLOBAL_LIST_EMPTY(preferences_datums)
random_character()
if("input")
+
+ if(href_list["preference"] in GLOB.preferences_custom_names)
+ ask_for_custom_name(user,href_list["preference"])
+
+
switch(href_list["preference"])
if("ghostform")
if(unlock_content)
@@ -1097,11 +1177,13 @@ GLOBAL_LIST_EMPTY(preferences_datums)
ghost_others = GHOST_OTHERS_SIMPLE
if("name")
- var/new_name = reject_bad_name( input(user, "Choose your character's name:", "Character Preference") as text|null )
+ var/new_name = input(user, "Choose your character's name:", "Character Preference") as text|null
if(new_name)
- real_name = new_name
- else
- to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and . ")
+ new_name = reject_bad_name(new_name)
+ if(new_name)
+ real_name = new_name
+ else
+ to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and . ")
if("age")
var/new_age = input(user, "Choose your character's age:\n([AGE_MIN]-[AGE_MAX])", "Character Preference") as num|null
@@ -1113,7 +1195,6 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(new_hair)
hair_color = sanitize_hexcolor(new_hair)
-
if("hair_style")
var/new_hair_style
if(gender == MALE)
@@ -1299,60 +1380,6 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(new_loc)
uplink_spawn_loc = new_loc
- if("human_name")
- var/new_human_name = reject_bad_name( input(user, "Choose your character's backup human name, used in the event you are assigned a command role as another species:", "Character Preference") as text|null )
- if(new_human_name)
- custom_names["human"] = new_human_name
- else
- to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and . ")
-
- if("clown_name")
- var/new_clown_name = reject_bad_name( input(user, "Choose your character's clown name:", "Character Preference") as text|null )
- if(new_clown_name)
- custom_names["clown"] = new_clown_name
- else
- to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and . ")
-
- if("mime_name")
- var/new_mime_name = reject_bad_name( input(user, "Choose your character's mime name:", "Character Preference") as text|null )
- if(new_mime_name)
- custom_names["mime"] = new_mime_name
- else
- to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and . ")
-
- if("ai_name")
- var/new_ai_name = reject_bad_name( input(user, "Choose your character's AI name:", "Character Preference") as text|null, 1 )
- if(new_ai_name)
- custom_names["ai"] = new_ai_name
- else
- to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, 0-9, -, ' and . ")
-
- if("cyborg_name")
- var/raw_name = input(user, "Choose your character's cyborg name (Leave empty to use default naming scheme):", "Character Preference") as text|null
- var/new_cyborg_name
- if(!raw_name)
- new_cyborg_name = DEFAULT_CYBORG_NAME
- else
- new_cyborg_name = reject_bad_name(raw_name,1 )
- if(new_cyborg_name)
- custom_names["cyborg"] = new_cyborg_name
- else
- to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, 0-9, -, ' and . ")
-
- if("religion_name")
- var/new_religion_name = reject_bad_name( input(user, "Choose your character's religion:", "Character Preference") as text|null )
- if(new_religion_name)
- custom_names["religion"] = new_religion_name
- else
- to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and . ")
-
- if("deity_name")
- var/new_deity_name = reject_bad_name( input(user, "Choose your character's deity:", "Character Preference") as text|null )
- if(new_deity_name)
- custom_names["deity"] = new_deity_name
- else
- to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and . ")
-
if("sec_dept")
var/department = input(user, "Choose your prefered security department:", "Security Departments") as null|anything in GLOB.security_depts_prefs
if(department)
@@ -1396,7 +1423,6 @@ GLOBAL_LIST_EMPTY(preferences_datums)
else
switch(href_list["preference"])
-
//CITADEL PREFERENCES EDIT - I can't figure out how to modularize these, so they have to go here. :c -Pooj
if("genital_colour")
features["genitals_use_skintone"] = !features["genitals_use_skintone"]
@@ -1530,6 +1556,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
parallax = WRAP(parallax - 1, PARALLAX_INSANE, PARALLAX_DISABLE + 1)
if (parent && parent.mob && parent.mob.hud_used)
parent.mob.hud_used.update_parallax_pref(parent.mob)
+
// Citadel edit - Prefs don't work outside of this. :c
if("hound_sleeper")
cit_toggles ^= MEDIHOUND_SLEEPER
@@ -1573,7 +1600,6 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if("tab")
if (href_list["tab"])
current_tab = text2num(href_list["tab"])
-
if(href_list["preference"] == "gear")
if(href_list["clear_loadout"])
LAZYCLEARLIST(chosen_gear)
@@ -1603,23 +1629,25 @@ GLOBAL_LIST_EMPTY(preferences_datums)
gear_points -= initial(G.cost)
process_citadel_link(user, href_list)
+
ShowChoices(user)
return 1
-/datum/preferences/proc/copy_to(mob/living/carbon/human/character, icon_updates = 1)
+/datum/preferences/proc/copy_to(mob/living/carbon/human/character, icon_updates = 1, roundstart_checks = TRUE)
if(be_random_name)
real_name = pref_species.random_name(gender)
if(be_random_body)
random_character(gender)
- if(CONFIG_GET(flag/humans_need_surnames) && (pref_species.id == "human"))
- var/firstspace = findtext(real_name, " ")
- var/name_length = length(real_name)
- if(!firstspace) //we need a surname
- real_name += " [pick(GLOB.last_names)]"
- else if(firstspace == name_length)
- real_name += "[pick(GLOB.last_names)]"
+ if(roundstart_checks)
+ if(CONFIG_GET(flag/humans_need_surnames) && (pref_species.id == "human"))
+ var/firstspace = findtext(real_name, " ")
+ var/name_length = length(real_name)
+ if(!firstspace) //we need a surname
+ real_name += " [pick(GLOB.last_names)]"
+ else if(firstspace == name_length)
+ real_name += "[pick(GLOB.last_names)]"
character.real_name = real_name
character.name = character.real_name
@@ -1648,15 +1676,48 @@ GLOBAL_LIST_EMPTY(preferences_datums)
character.dna.features = features.Copy()
character.dna.real_name = character.real_name
var/datum/species/chosen_species
- if(pref_species.id in GLOB.roundstart_races)
+ if(!roundstart_checks || (pref_species.id in GLOB.roundstart_races))
chosen_species = pref_species.type
else
chosen_species = /datum/species/human
pref_species = new /datum/species/human
save_character()
- character.set_species(chosen_species, icon_update=0)
+ character.set_species(chosen_species, icon_update = FALSE, pref_load = TRUE)
if(icon_updates)
character.update_body()
character.update_hair()
character.update_body_parts()
+
+/datum/preferences/proc/get_default_name(name_id)
+ switch(name_id)
+ if("human")
+ return random_unique_name()
+ if("ai")
+ return pick(GLOB.ai_names)
+ if("cyborg")
+ return DEFAULT_CYBORG_NAME
+ if("clown")
+ return pick(GLOB.clown_names)
+ if("mime")
+ return pick(GLOB.mime_names)
+ return random_unique_name()
+
+/datum/preferences/proc/ask_for_custom_name(mob/user,name_id)
+ var/namedata = GLOB.preferences_custom_names[name_id]
+ if(!namedata)
+ return
+
+ var/raw_name = input(user, "Choose your character's [namedata["qdesc"]]:","Character Preference") as text|null
+ if(!raw_name)
+ if(namedata["allow_null"])
+ custom_names[name_id] = get_default_name(name_id)
+ else
+ return
+ else
+ var/sanitized_name = reject_bad_name(raw_name,namedata["allow_numbers"])
+ if(!sanitized_name)
+ to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z,[namedata["allow_numbers"] ? ",0-9," : ""] -, ' and . ")
+ return
+ else
+ custom_names[name_id] = sanitized_name
diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm
index 588829c9ef..8152f42efb 100644
--- a/code/modules/client/preferences_savefile.dm
+++ b/code/modules/client/preferences_savefile.dm
@@ -45,45 +45,11 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
return
/datum/preferences/proc/update_character(current_version, savefile/S)
- if(current_version < 20)//Raise this to the max savefile version every time we change something so we don't sanitize this whole list every time you save.
- features["mam_body_markings"] = sanitize_inlist(features["mam_body_markings"], GLOB.mam_body_markings_list)
- features["mam_ears"] = sanitize_inlist(features["mam_ears"], GLOB.mam_ears_list)
- features["mam_tail"] = sanitize_inlist(features["mam_tail"], GLOB.mam_tails_list)
- features["taur"] = sanitize_inlist(features["taur"], GLOB.taur_list)
- //Xeno features
- features["xenotail"] = sanitize_inlist(features["xenotail"], GLOB.xeno_tail_list)
- features["xenohead"] = sanitize_inlist(features["xenohead"], GLOB.xeno_head_list)
- features["xenodorsal"] = sanitize_inlist(features["xenodorsal"], GLOB.xeno_dorsal_list)
- //cock features
- features["has_cock"] = sanitize_integer(features["has_cock"], 0, 1, 0)
- features["cock_shape"] = sanitize_inlist(features["cock_shape"], GLOB.cock_shapes_list, "Human")
- features["cock_color"] = sanitize_hexcolor(features["cock_color"], 3, 0)
- features["cock_length"] = sanitize_integer(features["cock_length"], COCK_SIZE_MIN, COCK_SIZE_MAX, 6)
- //balls features
- features["has_balls"] = sanitize_integer(features["has_balls"], 0, 1, 0)
- features["balls_color"] = sanitize_hexcolor(features["balls_color"], 3, 0)
- features["balls_size"] = sanitize_integer(features["balls_size"], BALLS_SIZE_MIN, BALLS_SIZE_MAX, BALLS_SIZE_DEF)
- features["balls_sack_size"] = sanitize_integer(features["balls_sack_size"], BALLS_SACK_SIZE_MIN, BALLS_SACK_SIZE_MAX, BALLS_SACK_SIZE_DEF)
- features["balls_fluid"] = sanitize_inlist(features["balls_fluid"], GLOB.cum_id_list, "semen")
- //breasts features
- features["has_breasts"] = sanitize_integer(features["has_breasts"], 0, 1, 0)
- features["breasts_size"] = sanitize_inlist(features["breasts_size"], GLOB.breasts_size_list, "C")
- features["breasts_shape"] = sanitize_inlist(features["breasts_shape"], GLOB.breasts_shapes_list, "Pair")
- features["breasts_color"] = sanitize_hexcolor(features["breasts_color"], 3, 0)
- features["breasts_fluid"] = sanitize_inlist(features["breasts_fluid"], GLOB.milk_id_list, "milk")
- //vagina features
- features["has_vag"] = sanitize_integer(features["has_vag"], 0, 1, 0)
- features["vag_shape"] = sanitize_inlist(features["vag_shape"], GLOB.vagina_shapes_list, "Human")
- features["vag_color"] = sanitize_hexcolor(features["vag_color"], 3, 0)
- //womb features
- features["has_womb"] = sanitize_integer(features["has_womb"], 0, 1, 0)
-
if(current_version < 19)
pda_style = "mono"
if(current_version < 20)
pda_color = "#808000"
-
/datum/preferences/proc/load_path(ckey,filename="preferences.sav")
if(!ckey)
return
@@ -180,6 +146,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
autostand = sanitize_integer(autostand, 0, 1, initial(autostand))
cit_toggles = sanitize_integer(cit_toggles, 0, 65535, initial(cit_toggles))
+
return 1
/datum/preferences/proc/save_preferences()
@@ -260,7 +227,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S["species"] >> species_id
if(species_id)
var/newtype = GLOB.species_list[species_id]
- pref_species = new newtype()
+ if(newtype)
+ pref_species = new newtype
if(!S["features["mcolor"]"] || S["features["mcolor"]"] == "#000")
WRITE_FILE(S["features["mcolor"]"] , "#FFF")
@@ -297,13 +265,12 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
else
S["feature_human_tail"] >> features["tail_human"]
S["feature_human_ears"] >> features["ears"]
- S["human_name"] >> custom_names["human"]
- S["clown_name"] >> custom_names["clown"]
- S["mime_name"] >> custom_names["mime"]
- S["ai_name"] >> custom_names["ai"]
- S["cyborg_name"] >> custom_names["cyborg"]
- S["religion_name"] >> custom_names["religion"]
- S["deity_name"] >> custom_names["deity"]
+
+ //Custom names
+ for(var/custom_name_id in GLOB.preferences_custom_names)
+ var/savefile_slot_name = custom_name_id + "_name" //TODO remove this
+ S[savefile_slot_name] >> custom_names[custom_name_id]
+
S["prefered_security_department"] >> prefered_security_department
//Jobs
@@ -374,19 +341,30 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
else //We have no old flavortext, default to new
S["feature_flavor_text"] >> features["flavor_text"]
+
//try to fix any outdated data if necessary
if(needs_update >= 0)
update_character(needs_update, S) //needs_update == savefile_version if we need an update (positive integer)
//Sanitize
- real_name = reject_bad_name(real_name)
- if(!features["mcolor"] || features["mcolor"] == "#000")
- features["mcolor"] = pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F")
+
+ real_name = reject_bad_name(real_name)
+ gender = sanitize_gender(gender)
if(!real_name)
real_name = random_unique_name(gender)
+
+ for(var/custom_name_id in GLOB.preferences_custom_names)
+ var/namedata = GLOB.preferences_custom_names[custom_name_id]
+ custom_names[custom_name_id] = reject_bad_name(custom_names[custom_name_id],namedata["allow_numbers"])
+ if(!custom_names[custom_name_id])
+ custom_names[custom_name_id] = get_default_name(custom_name_id)
+
+ if(!features["mcolor"] || features["mcolor"] == "#000")
+ features["mcolor"] = pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F")
+
be_random_name = sanitize_integer(be_random_name, 0, 1, initial(be_random_name))
be_random_body = sanitize_integer(be_random_body, 0, 1, initial(be_random_body))
- gender = sanitize_gender(gender)
+
if(gender == MALE)
hair_style = sanitize_inlist(hair_style, GLOB.hair_styles_male_list)
facial_hair_style = sanitize_inlist(facial_hair_style, GLOB.facial_hair_styles_male_list)
@@ -476,13 +454,12 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
WRITE_FILE(S["feature_lizard_body_markings"] , features["body_markings"])
WRITE_FILE(S["feature_lizard_legs"] , features["legs"])
WRITE_FILE(S["feature_moth_wings"] , features["moth_wings"])
- WRITE_FILE(S["human_name"] , custom_names["human"])
- WRITE_FILE(S["clown_name"] , custom_names["clown"])
- WRITE_FILE(S["mime_name"] , custom_names["mime"])
- WRITE_FILE(S["ai_name"] , custom_names["ai"])
- WRITE_FILE(S["cyborg_name"] , custom_names["cyborg"])
- WRITE_FILE(S["religion_name"] , custom_names["religion"])
- WRITE_FILE(S["deity_name"] , custom_names["deity"])
+
+ //Custom names
+ for(var/custom_name_id in GLOB.preferences_custom_names)
+ var/savefile_slot_name = custom_name_id + "_name" //TODO remove this
+ WRITE_FILE(S[savefile_slot_name],custom_names[custom_name_id])
+
WRITE_FILE(S["prefered_security_department"] , prefered_security_department)
//Jobs
diff --git a/code/modules/client/verbs/ooc.dm b/code/modules/client/verbs/ooc.dm
index 2f4922fddf..bc163b1781 100644
--- a/code/modules/client/verbs/ooc.dm
+++ b/code/modules/client/verbs/ooc.dm
@@ -50,9 +50,7 @@
to_chat(src, "You have OOC muted. ")
return
-
- log_talk(mob,"[key_name(src)] : [raw_msg]",LOGOOC)
- mob.log_message("[key]: [raw_msg]", INDIVIDUAL_OOC_LOG)
+ mob.log_talk(raw_msg, LOG_OOC)
var/keyname = key
if(prefs.unlock_content)
diff --git a/code/modules/client/verbs/suicide.dm b/code/modules/client/verbs/suicide.dm
index 6b273cb818..64d58cd40b 100644
--- a/code/modules/client/verbs/suicide.dm
+++ b/code/modules/client/verbs/suicide.dm
@@ -205,18 +205,24 @@
log_game("[key_name(src)] (job: [src.job ? "[src.job]" : "None"]) committed suicide at [AREACOORD(src)].")
/mob/living/proc/canSuicide()
- if(stat == CONSCIOUS)
- return TRUE
- else if(stat == DEAD)
- to_chat(src, "You're already dead!")
- else if(stat == UNCONSCIOUS)
- to_chat(src, "You need to be conscious to suicide!")
+ switch(stat)
+ if(CONSCIOUS)
+ return TRUE
+ if(SOFT_CRIT)
+ to_chat(src, "You can't commit suicide while in a critical condition!")
+ if(UNCONSCIOUS)
+ to_chat(src, "You need to be conscious to commit suicide!")
+ if(DEAD)
+ to_chat(src, "You're already dead!")
return
/mob/living/carbon/canSuicide()
if(!..())
return
- if(!canmove || restrained()) //just while I finish up the new 'fun' suiciding verb. This is to prevent metagaming via suicide
- to_chat(src, "You can't commit suicide whilst restrained! ((You can type Ghost instead however.))")
+ if(IsStun() || IsKnockdown()) //just while I finish up the new 'fun' suiciding verb. This is to prevent metagaming via suicide
+ to_chat(src, "You can't commit suicide while stunned! ((You can type Ghost instead however.))")
+ return
+ if(restrained())
+ to_chat(src, "You can't commit suicide while restrained! ((You can type Ghost instead however.))")
return
return TRUE
diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm
index 1dde03a8ed..b9ce170dc5 100644
--- a/code/modules/clothing/clothing.dm
+++ b/code/modules/clothing/clothing.dm
@@ -56,6 +56,22 @@
if(M.putItemFromInventoryInHandIfPossible(src, H.held_index))
add_fingerprint(usr)
+/obj/item/reagent_containers/food/snacks/clothing
+ name = "oops"
+ desc = "If you're reading this it means I messed up. This is related to moths eating clothes and I didn't know a better way to do it than making a new food object."
+ list_reagents = list("nutriment" = 1)
+ tastes = list("dust" = 1, "lint" = 1)
+
+/obj/item/clothing/attack(mob/M, mob/user, def_zone)
+ if(user.a_intent != INTENT_HARM && ismoth(M))
+ var/obj/item/reagent_containers/food/snacks/clothing/clothing_as_food = new
+ clothing_as_food.name = name
+ if(clothing_as_food.attack(M, user, def_zone))
+ take_damage(15, sound_effect=FALSE)
+ qdel(clothing_as_food)
+ else
+ return ..()
+
/obj/item/clothing/attackby(obj/item/W, mob/user, params)
if(damaged_clothes && istype(W, /obj/item/stack/sheet/cloth))
var/obj/item/stack/sheet/cloth/C = W
diff --git a/code/modules/clothing/gloves/_gloves.dm b/code/modules/clothing/gloves/_gloves.dm
index 0e9be23aed..817fe59dcd 100644
--- a/code/modules/clothing/gloves/_gloves.dm
+++ b/code/modules/clothing/gloves/_gloves.dm
@@ -13,9 +13,9 @@
/obj/item/clothing/gloves/ComponentInitialize()
. = ..()
- AddComponent(/datum/component/redirect, list(COMSIG_COMPONENT_CLEAN_ACT), CALLBACK(src, .proc/clean_blood))
+ AddComponent(/datum/component/redirect, list(COMSIG_COMPONENT_CLEAN_ACT = CALLBACK(src, .proc/clean_blood)))
-/obj/item/clothing/gloves/proc/clean_blood(strength)
+/obj/item/clothing/gloves/proc/clean_blood(datum/source, strength)
if(strength < CLEAN_STRENGTH_BLOOD)
return
transfer_blood = 0
diff --git a/code/modules/clothing/gloves/miscellaneous.dm b/code/modules/clothing/gloves/miscellaneous.dm
index e27b1e9e13..6c22334f78 100644
--- a/code/modules/clothing/gloves/miscellaneous.dm
+++ b/code/modules/clothing/gloves/miscellaneous.dm
@@ -70,7 +70,7 @@
if(M.a_intent == INTENT_HARM)
M.changeNext_move(CLICK_CD_RAPID)
if(warcry)
- M.say("[warcry]", ignore_spam = TRUE)
+ M.say("[warcry]", ignore_spam = TRUE, forced = "north star warcry")
.= FALSE
/obj/item/clothing/gloves/rapid/attack_self(mob/user)
diff --git a/code/modules/clothing/head/hardhat.dm b/code/modules/clothing/head/hardhat.dm
index ae4d80a0e4..f7c52c909e 100644
--- a/code/modules/clothing/head/hardhat.dm
+++ b/code/modules/clothing/head/hardhat.dm
@@ -79,6 +79,6 @@
clothing_flags = STOPSPRESSUREDAMAGE | THICKMATERIAL | BLOCK_GAS_SMOKE_EFFECT
flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR
heat_protection = HEAD
- max_heat_protection_temperature = FIRE_IMMUNITY_HELM_MAX_TEMP_PROTECT
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
cold_protection = HEAD
min_cold_protection_temperature = FIRE_HELM_MIN_TEMP_PROTECT
diff --git a/code/modules/clothing/head/jobs.dm b/code/modules/clothing/head/jobs.dm
index 61234b3f46..ad8fafb872 100644
--- a/code/modules/clothing/head/jobs.dm
+++ b/code/modules/clothing/head/jobs.dm
@@ -12,10 +12,10 @@
/obj/item/clothing/head/chefhat/suicide_act(mob/user)
user.visible_message("[user] is donning [src]! It looks like [user.p_theyre()] trying to become a chef. ")
- user.say("Bork Bork Bork!")
+ user.say("Bork Bork Bork!", forced = "chef hat suicide")
sleep(20)
user.visible_message("[user] climbs into an imaginary oven! ")
- user.say("BOOORK!")
+ user.say("BOOORK!", forced = "chef hat suicide")
playsound(user, 'sound/machines/ding.ogg', 50, 1)
return(FIRELOSS)
@@ -59,8 +59,8 @@
/obj/item/clothing/head/fedora/det_hat
name = "detective's fedora"
desc = "There's only one man who can sniff out the dirty stench of crime, and he's likely wearing this hat."
- icon_state = "detective"
armor = list("melee" = 25, "bullet" = 5, "laser" = 25, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 50)
+ icon_state = "detective"
var/candy_cooldown = 0
pocket_storage_component_path = /datum/component/storage/concrete/pockets/small/detective
dog_fashion = /datum/dog_fashion/head/detective
diff --git a/code/modules/clothing/head/misc.dm b/code/modules/clothing/head/misc.dm
index 1c23afaa82..281a48bfda 100644
--- a/code/modules/clothing/head/misc.dm
+++ b/code/modules/clothing/head/misc.dm
@@ -193,7 +193,7 @@
return 0
var/mob/living/carbon/human/H = user
user.visible_message("[user] is donning [src]! It looks like [user.p_theyre()] trying to be nice to girls. ")
- user.say("M'lady.")
+ user.say("M'lady.", forced = "fedora suicide")
sleep(10)
H.facial_hair_style = "Neckbeard"
return(BRUTELOSS)
@@ -236,6 +236,7 @@
w_class = WEIGHT_CLASS_SMALL
attack_verb = list("warned", "cautioned", "smashed")
resistance_flags = NONE
+ dynamic_hair_suffix = ""
/obj/item/clothing/head/santa
name = "santa hat"
@@ -326,6 +327,7 @@
name = "french beret"
desc = "A quality beret, infused with the aroma of chain-smoking, wine-swilling Parisians. You feel less inclined to engage military conflict, for some reason."
icon_state = "beretblack"
+ dynamic_hair_suffix = ""
/obj/item/clothing/head/frenchberet/speechModification(M)
if(copytext(M, 1, 2) != "*")
diff --git a/code/modules/clothing/head/misc_special.dm b/code/modules/clothing/head/misc_special.dm
index 86763e060b..36ba2fa76c 100644
--- a/code/modules/clothing/head/misc_special.dm
+++ b/code/modules/clothing/head/misc_special.dm
@@ -61,8 +61,8 @@
/obj/item/clothing/head/hardhat/cakehat/turn_on()
..()
- force = 1
- throwforce = 1
+ force = 15
+ throwforce = 15
damtype = BURN
hitsound = 'sound/items/welder.ogg'
START_PROCESSING(SSobj, src)
diff --git a/code/modules/clothing/masks/hailer.dm b/code/modules/clothing/masks/hailer.dm
index 3f21780f15..8860650fbc 100644
--- a/code/modules/clothing/masks/hailer.dm
+++ b/code/modules/clothing/masks/hailer.dm
@@ -39,6 +39,8 @@
actions_types = list(/datum/action/item_action/halt)
/obj/item/clothing/mask/gas/sechailer/screwdriver_act(mob/living/user, obj/item/I)
+ if(..())
+ return TRUE
switch(aggressiveness)
if(1)
to_chat(user, "You set the restrictor to the middle position. ")
diff --git a/code/modules/clothing/masks/miscellaneous.dm b/code/modules/clothing/masks/miscellaneous.dm
index c0d41aeb68..4cc339a776 100644
--- a/code/modules/clothing/masks/miscellaneous.dm
+++ b/code/modules/clothing/masks/miscellaneous.dm
@@ -272,3 +272,24 @@
icon_state = "scarecrow_sack"
item_state = "scarecrow_sack"
flags_inv = HIDEFACE|HIDEHAIR|HIDEFACIALHAIR
+
+/obj/item/clothing/mask/gondola
+ name = "gondola mask"
+ desc = "Genuine gondola fur."
+ icon_state = "gondola"
+ item_state = "gondola"
+ flags_inv = HIDEFACE|HIDEHAIR|HIDEFACIALHAIR
+ w_class = WEIGHT_CLASS_SMALL
+
+/obj/item/clothing/mask/gondola/speechModification(M)
+ if(copytext(M, 1, 2) != "*")
+ M = " [M]"
+ var/list/spurdo_words = strings("spurdo_replacement.json", "spurdo")
+ for(var/key in spurdo_words)
+ var/value = spurdo_words[key]
+ if(islist(value))
+ value = pick(value)
+ M = replacetextEx(M,regex(uppertext(key),"g"), "[uppertext(value)]")
+ M = replacetextEx(M,regex(capitalize(key),"g"), "[capitalize(value)]")
+ M = replacetextEx(M,regex(key,"g"), "[value]")
+ return trim(M)
diff --git a/code/modules/clothing/outfits/standard.dm b/code/modules/clothing/outfits/standard.dm
index 88b51963e5..325c124867 100644
--- a/code/modules/clothing/outfits/standard.dm
+++ b/code/modules/clothing/outfits/standard.dm
@@ -424,6 +424,17 @@
mask = /obj/item/clothing/mask/breath
suit_store = /obj/item/tank/internals/oxygen
-
-
-
+/datum/outfit/debug //Debug objs plus hardsuit
+ name = "Debug outfit"
+ uniform = /obj/item/clothing/under/patriotsuit
+ suit = /obj/item/clothing/suit/space/hardsuit/syndi/elite
+ shoes = /obj/item/clothing/shoes/magboots/advance
+ suit_store = /obj/item/tank/internals/oxygen
+ mask = /obj/item/clothing/mask/gas/welding
+ belt = /obj/item/storage/belt/utility/chief/full
+ gloves = /obj/item/clothing/gloves/combat
+ id = /obj/item/card/id/ert
+ glasses = /obj/item/clothing/glasses/meson/night
+ ears = /obj/item/radio/headset/headset_cent/commander
+ back = /obj/item/storage/backpack/holding
+ backpack_contents = list(/obj/item/card/emag=1, /obj/item/flashlight/emp/debug=1, /obj/item/construction/rcd/combat=1, /obj/item/gun/magic/wand/resurrection/debug=1, /obj/item/melee/transforming/energy/axe=1)
diff --git a/code/modules/clothing/outfits/vv_outfit.dm b/code/modules/clothing/outfits/vv_outfit.dm
new file mode 100644
index 0000000000..103a152ec8
--- /dev/null
+++ b/code/modules/clothing/outfits/vv_outfit.dm
@@ -0,0 +1,143 @@
+// This outfit preserves varedits made on the items
+// Created from admin helpers.
+/datum/outfit/varedit
+ var/list/vv_values
+ var/list/stored_access
+
+/datum/outfit/varedit/pre_equip(mob/living/carbon/human/H, visualsOnly)
+ H.delete_equipment() //Applying VV to wrong objects is not reccomended.
+ . = ..()
+
+/datum/outfit/varedit/proc/set_equipement_by_slot(slot,item_path)
+ switch(slot)
+ if(SLOT_W_UNIFORM)
+ uniform = item_path
+ if(SLOT_BACK)
+ back = item_path
+ if(SLOT_WEAR_SUIT)
+ suit = item_path
+ if(SLOT_BELT)
+ belt = item_path
+ if(SLOT_GLOVES)
+ gloves = item_path
+ if(SLOT_SHOES)
+ shoes = item_path
+ if(SLOT_HEAD)
+ head = item_path
+ if(SLOT_WEAR_MASK)
+ mask = item_path
+ if(SLOT_NECK)
+ neck = item_path
+ if(SLOT_EARS)
+ ears = item_path
+ if(SLOT_GLASSES)
+ glasses = item_path
+ if(SLOT_WEAR_ID)
+ id = item_path
+ if(SLOT_S_STORE)
+ suit_store = item_path
+ if(SLOT_L_STORE)
+ l_pocket = item_path
+ if(SLOT_R_STORE)
+ r_pocket = item_path
+
+
+/proc/collect_vv(obj/item/I)
+ //Temporary/Internal stuff, do not copy these.
+ var/static/list/ignored_vars = list("vars","x","y","z","plane","layer","override","animate_movement","pixel_step_size","screen_loc","fingerprintslast","tip_timer")
+
+ if(istype(I) && I.datum_flags & DF_VAR_EDITED)
+ var/list/vedits = list()
+ for(var/varname in I.vars)
+ if(!I.can_vv_get(varname))
+ continue
+ if(varname in ignored_vars)
+ continue
+ var/vval = I.vars[varname]
+ //Does it even work ?
+ if(vval == initial(I.vars[varname]))
+ continue
+ //Only text/numbers and icons variables to make it less weirdness prone.
+ if(!istext(vval) && !isnum(vval) && !isicon(vval))
+ continue
+ vedits[varname] = I.vars[varname]
+ return vedits
+
+/mob/living/carbon/human/proc/copy_outfit()
+ var/datum/outfit/varedit/O = new
+
+ //Copy equipment
+ var/list/result = list()
+ var/list/slots_to_check = list(SLOT_W_UNIFORM,SLOT_BACK,SLOT_WEAR_SUIT,SLOT_BELT,SLOT_GLOVES,SLOT_SHOES,SLOT_HEAD,SLOT_WEAR_MASK,SLOT_NECK,SLOT_EARS,SLOT_GLASSES,SLOT_WEAR_ID,SLOT_S_STORE,SLOT_L_STORE,SLOT_R_STORE)
+ for(var/s in slots_to_check)
+ var/obj/item/I = get_item_by_slot(s)
+ var/vedits = collect_vv(I)
+ if(vedits)
+ result["[s]"] = vedits
+ if(istype(I))
+ O.set_equipement_by_slot(s,I.type)
+
+ //Copy access
+ O.stored_access = list()
+ var/obj/item/id_slot = get_item_by_slot(SLOT_WEAR_ID)
+ if(id_slot)
+ O.stored_access |= id_slot.GetAccess()
+ //Copy hands
+ if(held_items.len >= 2) //Not in the mood to let outfits transfer amputees
+ var/obj/item/left_hand = held_items[1]
+ var/obj/item/right_hand = held_items[2]
+ if(istype(left_hand))
+ O.l_hand = left_hand.type
+ var/vedits = collect_vv(left_hand)
+ if(vedits)
+ result["LHAND"] = vedits
+ if(istype(right_hand))
+ O.r_hand = right_hand.type
+ var/vedits = collect_vv(left_hand)
+ if(vedits)
+ result["RHAND"] = vedits
+ O.vv_values = result
+ //Copy backpack contents if exist.
+ var/obj/item/backpack = get_item_by_slot(SLOT_BACK)
+ if(istype(backpack) && SEND_SIGNAL(backpack, COMSIG_CONTAINS_STORAGE))
+ var/list/bp_stuff = list()
+ var/list/typecounts = list()
+ SEND_SIGNAL(backpack, COMSIG_TRY_STORAGE_RETURN_INVENTORY, bp_stuff, FALSE)
+ for(var/obj/item/I in bp_stuff)
+ if(typecounts[I.type])
+ typecounts[I.type] += 1
+ else
+ typecounts[I.type] = 1
+ O.backpack_contents = typecounts
+ //TODO : Copy varedits from backpack stuff too.
+ //Copy implants
+ O.implants = list()
+ for(var/obj/item/implant/I in implants)
+ O.implants |= I.type
+ //Copy to outfit cache
+ var/outfit_name = stripped_input(usr,"Enter the outfit name")
+ O.name = outfit_name
+ GLOB.custom_outfits += O
+ to_chat(usr,"Outfit registered, use select equipment to equip it.")
+
+/datum/outfit/varedit/post_equip(mob/living/carbon/human/H, visualsOnly)
+ . = ..()
+ //Apply VV
+ for(var/slot in vv_values)
+ var/list/edits = vv_values[slot]
+ var/obj/item/I
+ switch(slot)
+ if("LHAND")
+ I = H.held_items[1]
+ if("RHAND")
+ I = H.held_items[2]
+ else
+ I = H.get_item_by_slot(text2num(slot))
+ for(var/vname in edits)
+ I.vv_edit_var(vname,edits[vname])
+ //Apply access
+ var/obj/item/id_slot = H.get_item_by_slot(SLOT_WEAR_ID)
+ if(id_slot)
+ var/obj/item/card/id/card = id_slot.GetID()
+ if(istype(card))
+ card.access |= stored_access
\ No newline at end of file
diff --git a/code/modules/clothing/shoes/_shoes.dm b/code/modules/clothing/shoes/_shoes.dm
index e3c32cd26c..b03d71221d 100644
--- a/code/modules/clothing/shoes/_shoes.dm
+++ b/code/modules/clothing/shoes/_shoes.dm
@@ -17,7 +17,7 @@
/obj/item/clothing/shoes/ComponentInitialize()
. = ..()
- AddComponent(/datum/component/redirect, list(COMSIG_COMPONENT_CLEAN_ACT), CALLBACK(src, .proc/clean_blood))
+ AddComponent(/datum/component/redirect, list(COMSIG_COMPONENT_CLEAN_ACT = CALLBACK(src, .proc/clean_blood)))
/obj/item/clothing/shoes/suicide_act(mob/living/carbon/user)
if(rand(2)>1)
@@ -76,7 +76,7 @@
var/mob/M = loc
M.update_inv_shoes()
-/obj/item/clothing/shoes/proc/clean_blood(strength)
+/obj/item/clothing/shoes/proc/clean_blood(datum/source, strength)
if(strength < CLEAN_STRENGTH_BLOOD)
return
bloody_shoes = list(BLOOD_STATE_HUMAN = 0,BLOOD_STATE_XENO = 0, BLOOD_STATE_OIL = 0, BLOOD_STATE_NOT_BLOODY = 0)
diff --git a/code/modules/clothing/shoes/bananashoes.dm b/code/modules/clothing/shoes/bananashoes.dm
index 3dc80e0ded..b634894805 100644
--- a/code/modules/clothing/shoes/bananashoes.dm
+++ b/code/modules/clothing/shoes/bananashoes.dm
@@ -10,7 +10,7 @@
/obj/item/clothing/shoes/clown_shoes/banana_shoes/Initialize()
. = ..()
- AddComponent(/datum/component/material_container, list(MAT_BANANIUM), 200000, TRUE, list(/obj/item/stack))
+ AddComponent(/datum/component/material_container, list(MAT_BANANIUM), 200000, TRUE, /obj/item/stack)
AddComponent(/datum/component/squeak, list('sound/items/bikehorn.ogg'=1), 75)
if(always_noslip)
clothing_flags |= NOSLIP
diff --git a/code/modules/clothing/shoes/miscellaneous.dm b/code/modules/clothing/shoes/miscellaneous.dm
index c81505d8e8..a6bcf23a2d 100644
--- a/code/modules/clothing/shoes/miscellaneous.dm
+++ b/code/modules/clothing/shoes/miscellaneous.dm
@@ -27,7 +27,7 @@
armor = list("melee" = 40, "bullet" = 30, "laser" = 25, "energy" = 25, "bomb" = 50, "bio" = 30, "rad" = 30, "fire" = 90, "acid" = 50)
/obj/item/clothing/shoes/sandal
- desc = "A pair of rather plain, wooden sandals."
+ desc = "A pair of rather plain wooden sandals."
name = "sandals"
icon_state = "wizard"
strip_delay = 50
@@ -91,7 +91,7 @@
/obj/item/clothing/shoes/clown_shoes/jester
name = "jester shoes"
- desc = "A court jesters shoes, updated with modern squeaking technology."
+ desc = "A court jester's shoes, updated with modern squeaking technology."
icon_state = "jester_shoes"
/obj/item/clothing/shoes/jackboots
@@ -142,8 +142,8 @@
resistance_flags = FIRE_PROOF
/obj/item/clothing/shoes/cult
- name = "nar-sian invoker boots"
- desc = "A pair of boots worn by the followers of Nar-Sie."
+ name = "\improper Nar'Sien invoker boots"
+ desc = "A pair of boots worn by the followers of Nar'Sie."
icon_state = "cult"
item_state = "cult"
item_color = "cult"
diff --git a/code/modules/clothing/spacesuits/hardsuit.dm b/code/modules/clothing/spacesuits/hardsuit.dm
index 2e5896098f..0794ef29e7 100644
--- a/code/modules/clothing/spacesuits/hardsuit.dm
+++ b/code/modules/clothing/spacesuits/hardsuit.dm
@@ -190,7 +190,7 @@
item_color = "atmospherics"
armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 25, "fire" = 100, "acid" = 75)
heat_protection = HEAD //Uncomment to enable firesuit protection
- max_heat_protection_temperature = FIRE_IMMUNITY_HELM_MAX_TEMP_PROTECT
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
/obj/item/clothing/suit/space/hardsuit/engine/atmos
name = "atmospherics hardsuit"
@@ -199,7 +199,7 @@
item_state = "atmo_hardsuit"
armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 25, "fire" = 100, "acid" = 75)
heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS //Uncomment to enable firesuit protection
- max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
helmettype = /obj/item/clothing/head/helmet/space/hardsuit/engine/atmos
@@ -212,7 +212,7 @@
item_color = "white"
armor = list("melee" = 40, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 50, "bio" = 100, "rad" = 90, "fire" = 100, "acid" = 90)
heat_protection = HEAD
- max_heat_protection_temperature = FIRE_IMMUNITY_HELM_MAX_TEMP_PROTECT
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
/obj/item/clothing/suit/space/hardsuit/engine/elite
icon_state = "hardsuit-white"
@@ -221,7 +221,7 @@
item_state = "ce_hardsuit"
armor = list("melee" = 40, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 50, "bio" = 100, "rad" = 90, "fire" = 100, "acid" = 90)
heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
- max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
helmettype = /obj/item/clothing/head/helmet/space/hardsuit/engine/elite
jetpack = /obj/item/tank/jetpack/suit
@@ -359,7 +359,7 @@
item_color = "syndielite"
armor = list("melee" = 60, "bullet" = 60, "laser" = 50, "energy" = 25, "bomb" = 55, "bio" = 100, "rad" = 70, "fire" = 100, "acid" = 100)
heat_protection = HEAD
- max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
visor_flags_inv = 0
visor_flags = 0
on = FALSE
@@ -375,7 +375,7 @@
helmettype = /obj/item/clothing/head/helmet/space/hardsuit/syndi/elite
armor = list("melee" = 60, "bullet" = 60, "laser" = 50, "energy" = 25, "bomb" = 55, "bio" = 100, "rad" = 70, "fire" = 100, "acid" = 100)
heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
- max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
resistance_flags = FIRE_PROOF | ACID_PROOF
//The Owl Hardsuit
@@ -410,7 +410,7 @@
resistance_flags = FIRE_PROOF | ACID_PROOF //No longer shall our kind be foiled by lone chemists with spray bottles!
armor = list("melee" = 40, "bullet" = 40, "laser" = 40, "energy" = 20, "bomb" = 35, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100)
heat_protection = HEAD //Uncomment to enable firesuit protection
- max_heat_protection_temperature = FIRE_IMMUNITY_HELM_MAX_TEMP_PROTECT
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
/obj/item/clothing/suit/space/hardsuit/wizard
icon_state = "hardsuit-wiz"
@@ -422,7 +422,7 @@
armor = list("melee" = 40, "bullet" = 40, "laser" = 40, "energy" = 20, "bomb" = 35, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100)
allowed = list(/obj/item/teleportation_scroll, /obj/item/tank/internals)
heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS //Uncomment to enable firesuit protection
- max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
helmettype = /obj/item/clothing/head/helmet/space/hardsuit/wizard
/obj/item/clothing/suit/space/hardsuit/wizard/Initialize()
@@ -542,7 +542,7 @@
resistance_flags = FIRE_PROOF | ACID_PROOF
flags_inv = HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR //we want to see the mask
heat_protection = HEAD
- max_heat_protection_temperature = FIRE_IMMUNITY_HELM_MAX_TEMP_PROTECT
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
actions_types = list()
/obj/item/clothing/head/helmet/space/hardsuit/captain/attack_self()
@@ -556,7 +556,7 @@
armor = list("melee" = 40, "bullet" = 50, "laser" = 50, "energy" = 25, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100)
resistance_flags = FIRE_PROOF | ACID_PROOF
heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
- max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT //this needed to be added a long fucking time ago
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT //this needed to be added a long fucking time ago
helmettype = /obj/item/clothing/head/helmet/space/hardsuit/captain
/obj/item/clothing/suit/space/hardsuit/captain/Initialize()
@@ -628,7 +628,7 @@
if (mobhook && mobhook.parent != user)
QDEL_NULL(mobhook)
if (!mobhook)
- mobhook = user.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED), CALLBACK(src, .proc/on_mob_move))
+ mobhook = user.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src, .proc/on_mob_move)))
else
QDEL_NULL(mobhook)
@@ -800,7 +800,7 @@
recharge_delay = 15
armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100)
strip_delay = 130
- max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
helmettype = /obj/item/clothing/head/helmet/space/hardsuit/shielded/swat
dog_fashion = /datum/dog_fashion/back/deathsquad
@@ -812,5 +812,5 @@
item_color = "syndi"
armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100)
strip_delay = 130
- max_heat_protection_temperature = FIRE_IMMUNITY_HELM_MAX_TEMP_PROTECT
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
actions_types = list()
diff --git a/code/modules/clothing/spacesuits/miscellaneous.dm b/code/modules/clothing/spacesuits/miscellaneous.dm
index a4b82c8234..dcd344563a 100644
--- a/code/modules/clothing/spacesuits/miscellaneous.dm
+++ b/code/modules/clothing/spacesuits/miscellaneous.dm
@@ -22,7 +22,7 @@ Contains:
item_state = "deathsquad"
armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100)
strip_delay = 130
- max_heat_protection_temperature = FIRE_IMMUNITY_HELM_MAX_TEMP_PROTECT
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
resistance_flags = FIRE_PROOF | ACID_PROOF
actions_types = list()
@@ -37,7 +37,7 @@ Contains:
allowed = list(/obj/item/gun, /obj/item/ammo_box, /obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/restraints/handcuffs, /obj/item/tank/internals, /obj/item/kitchen/knife/combat)
armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100)
strip_delay = 130
- max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
resistance_flags = FIRE_PROOF | ACID_PROOF
helmettype = /obj/item/clothing/head/helmet/space/hardsuit/deathsquad
dog_fashion = /datum/dog_fashion/back/deathsquad
@@ -62,7 +62,7 @@ Contains:
flags_inv = 0
armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100)
strip_delay = 130
- max_heat_protection_temperature = FIRE_IMMUNITY_HELM_MAX_TEMP_PROTECT
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
resistance_flags = FIRE_PROOF | ACID_PROOF
/obj/item/clothing/suit/space/officer
@@ -77,7 +77,7 @@ Contains:
allowed = list(/obj/item/gun, /obj/item/ammo_box, /obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/restraints/handcuffs, /obj/item/tank/internals)
armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100)
strip_delay = 130
- max_heat_protection_temperature = FIRE_IMMUNITY_HELM_MAX_TEMP_PROTECT
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
resistance_flags = FIRE_PROOF | ACID_PROOF
//NASA Voidsuit
@@ -241,7 +241,7 @@ Contains:
item_state = "griffinhat"
armor = list("melee" = 20, "bullet" = 40, "laser" = 30, "energy" = 25, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 80)
strip_delay = 130
- max_heat_protection_temperature = FIRE_IMMUNITY_HELM_MAX_TEMP_PROTECT
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
resistance_flags = ACID_PROOF | FIRE_PROOF
/obj/item/clothing/suit/space/freedom
@@ -252,7 +252,7 @@ Contains:
allowed = list(/obj/item/gun, /obj/item/ammo_box, /obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/restraints/handcuffs, /obj/item/tank/internals)
armor = list("melee" = 20, "bullet" = 40, "laser" = 30,"energy" = 25, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 80)
strip_delay = 130
- max_heat_protection_temperature = FIRE_IMMUNITY_HELM_MAX_TEMP_PROTECT
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
resistance_flags = ACID_PROOF | FIRE_PROOF
slowdown = 0
@@ -285,7 +285,7 @@ Contains:
icon_state = "hardsuit0-prt"
item_state = "hardsuit0-prt"
item_color = "knight_grey"
- max_heat_protection_temperature = FIRE_IMMUNITY_HELM_MAX_TEMP_PROTECT
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
actions_types = list()
resistance_flags = FIRE_PROOF
@@ -295,7 +295,7 @@ Contains:
icon_state = "knight_grey"
item_state = "knight_grey"
helmettype = /obj/item/clothing/head/helmet/space/hardsuit/ert/paranormal
- max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
resistance_flags = FIRE_PROOF
/obj/item/clothing/suit/space/hardsuit/ert/paranormal/Initialize()
diff --git a/code/modules/clothing/suits/cloaks.dm b/code/modules/clothing/suits/cloaks.dm
index 85767852e4..173d50ba64 100644
--- a/code/modules/clothing/suits/cloaks.dm
+++ b/code/modules/clothing/suits/cloaks.dm
@@ -81,7 +81,7 @@
hoodtype = /obj/item/clothing/head/hooded/cloakhood/drake
heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
- max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
resistance_flags = FIRE_PROOF | ACID_PROOF
/obj/item/clothing/head/hooded/cloakhood/drake
@@ -90,5 +90,5 @@
desc = "The skull of a dragon."
armor = list("melee" = 70, "bullet" = 30, "laser" = 50, "energy" = 40, "bomb" = 70, "bio" = 60, "rad" = 50, "fire" = 100, "acid" = 100)
heat_protection = HEAD
- max_heat_protection_temperature = FIRE_IMMUNITY_HELM_MAX_TEMP_PROTECT
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
resistance_flags = FIRE_PROOF | ACID_PROOF
diff --git a/code/modules/clothing/suits/miscellaneous.dm b/code/modules/clothing/suits/miscellaneous.dm
index 858f65b64c..9d039e0cb6 100644
--- a/code/modules/clothing/suits/miscellaneous.dm
+++ b/code/modules/clothing/suits/miscellaneous.dm
@@ -31,6 +31,21 @@
/*
* Costume
*/
+/obj/item/clothing/suit/hooded/flashsuit
+ name = "flashy costume"
+ desc = "What did you expect?"
+ icon_state = "flashsuit"
+ item_state = "armor"
+ body_parts_covered = CHEST|GROIN
+ hoodtype = /obj/item/clothing/head/hooded/flashsuit
+
+/obj/item/clothing/head/hooded/flashsuit
+ name = "flash button"
+ desc = "You will learn to fear the flash."
+ icon_state = "flashsuit"
+ body_parts_covered = HEAD
+ flags_inv = HIDEHAIR|HIDEEARS|HIDEFACIALHAIR|HIDEFACE|HIDEMASK
+
/obj/item/clothing/suit/pirate
name = "pirate coat"
desc = "Yarr."
@@ -397,7 +412,7 @@
desc = "A canvas jacket styled after classical American military garb. Feels sturdy, yet comfortable."
icon_state = "militaryjacket"
item_state = "militaryjacket"
- allowed = list(/obj/item/flashlight, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/gun/ballistic/automatic/pistol, /obj/item/gun/ballistic/revolver, /obj/item/gun/ballistic/revolver/detective, /obj/item/radio)
+ allowed = list(/obj/item/flashlight, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/gun/ballistic/automatic/pistol, /obj/item/gun/ballistic/revolver, /obj/item/radio)
/obj/item/clothing/suit/jacket/letterman
name = "letterman jacket"
@@ -599,7 +614,10 @@
name = "ghost sheet"
desc = "The hands float by themselves, so it's extra spooky."
icon_state = "ghost_sheet"
- item_state = "ghost_sheet_item"
+ item_state = "ghost_sheet"
+ throwforce = 0
+ throw_speed = 1
+ throw_range = 2
w_class = WEIGHT_CLASS_TINY
- flags_inv = HIDEGLOVES|HIDEMASK|HIDEEARS|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR
+ flags_inv = HIDEGLOVES|HIDEEARS|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR
alternate_worn_layer = UNDER_HEAD_LAYER
diff --git a/code/modules/clothing/suits/reactive_armour.dm b/code/modules/clothing/suits/reactive_armour.dm
index e71704132a..7542a0dd59 100644
--- a/code/modules/clothing/suits/reactive_armour.dm
+++ b/code/modules/clothing/suits/reactive_armour.dm
@@ -221,7 +221,7 @@
if(world.time < reactivearmor_cooldown)
owner.visible_message("The reactive table armor's fabricators are still on cooldown! ")
return
- owner.visible_message("The reactive teleport system flings [H] clear of [attack_text] and slams them into a fabricated table! ")
+ owner.visible_message("The reactive teleport system flings [H] clear of [attack_text] and slams [H.p_them()] into a fabricated table! ")
owner.visible_message("[H] GOES ON THE TABLE!!! ")
owner.Knockdown(40)
var/list/turfs = new/list()
diff --git a/code/modules/clothing/suits/utility.dm b/code/modules/clothing/suits/utility.dm
index 3a9a72a543..c233ce414d 100644
--- a/code/modules/clothing/suits/utility.dm
+++ b/code/modules/clothing/suits/utility.dm
@@ -47,7 +47,7 @@
desc = "An expensive firesuit that protects against even the most deadly of station fires. Designed to protect even if the wearer is set aflame."
icon_state = "atmos_firesuit"
item_state = "firesuit_atmos"
- max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
/*
* Bomb protection
diff --git a/code/modules/clothing/suits/wiz_robe.dm b/code/modules/clothing/suits/wiz_robe.dm
index 59f7a1c387..9d1a47f231 100644
--- a/code/modules/clothing/suits/wiz_robe.dm
+++ b/code/modules/clothing/suits/wiz_robe.dm
@@ -168,7 +168,7 @@
to_chat(usr, "\The robe's internal magic supply is still recharging! ")
return
- usr.say("Rise, my creation! Off your page into this realm!")
+ usr.say("Rise, my creation! Off your page into this realm!", forced = "stickman summoning")
playsound(src.loc, 'sound/magic/summon_magic.ogg', 50, 1, 1)
var/mob/living/M = new /mob/living/simple_animal/hostile/stickman(get_turf(usr))
var/list/factions = usr.faction
@@ -219,7 +219,7 @@
icon_state = "electricity2"
/obj/item/wizard_armour_charge/afterattack(obj/item/clothing/suit/space/hardsuit/shielded/wizard/W, mob/user)
- ..()
+ . = ..()
if(!istype(W))
to_chat(user, "The rune can only be used on battlemage armour! ")
return
diff --git a/code/modules/clothing/under/accessories.dm b/code/modules/clothing/under/accessories.dm
index 8d250ab766..dd0083a1b7 100644
--- a/code/modules/clothing/under/accessories.dm
+++ b/code/modules/clothing/under/accessories.dm
@@ -280,6 +280,11 @@
desc = "Fills you with the conviction of JUSTICE. Lawyers tend to want to show it to everyone they meet."
icon_state = "lawyerbadge"
item_color = "lawyerbadge"
+
+/obj/item/clothing/accessory/lawyers_badge/attack_self(mob/user)
+ if(prob(1))
+ user.say("The testimony contradicts the evidence!", forced = "attorney's badge")
+ user.visible_message("[user] shows [user.p_their()] attorney's badge.", "You show your attorney's badge. ")
/obj/item/clothing/accessory/lawyers_badge/on_uniform_equip(obj/item/clothing/under/U, user)
var/mob/living/L = user
diff --git a/code/modules/clothing/under/jobs/civilian.dm b/code/modules/clothing/under/jobs/civilian.dm
index 1115d822ed..0411a67846 100644
--- a/code/modules/clothing/under/jobs/civilian.dm
+++ b/code/modules/clothing/under/jobs/civilian.dm
@@ -67,9 +67,63 @@
fitted = FEMALE_UNIFORM_TOP
can_adjust = FALSE
-/obj/item/clothing/under/rank/clown/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
- playsound(loc, 'sound/items/bikehorn.ogg', 50, 1, -1)
- return 0
+/obj/item/clothing/under/rank/blueclown
+ name = "blue clown suit"
+ desc = "'BLUE HONK!' "
+ icon_state = "blueclown"
+ item_state = "blueclown"
+ item_color = "blueclown"
+ fitted = FEMALE_UNIFORM_TOP
+ can_adjust = FALSE
+
+/obj/item/clothing/under/rank/greenclown
+ name = "green clown suit"
+ desc = "'GREEN HONK!' "
+ icon_state = "greenclown"
+ item_state = "greenclown"
+ item_color = "greenclown"
+ fitted = FEMALE_UNIFORM_TOP
+ can_adjust = FALSE
+
+/obj/item/clothing/under/rank/yellowclown
+ name = "yellow clown suit"
+ desc = "'YELLOW HONK!' "
+ icon_state = "yellowclown"
+ item_state = "yellowclown"
+ item_color = "yellowclown"
+ fitted = FEMALE_UNIFORM_TOP
+ can_adjust = FALSE
+
+/obj/item/clothing/under/rank/purpleclown
+ name = "purple clown suit"
+ desc = "'PURPLE HONK!' "
+ icon_state = "purpleclown"
+ item_state = "purpleclown"
+ item_color = "purpleclown"
+ fitted = FEMALE_UNIFORM_TOP
+ can_adjust = FALSE
+
+/obj/item/clothing/under/rank/orangeclown
+ name = "orange clown suit"
+ desc = "'ORANGE HONK!' "
+ icon_state = "orangeclown"
+ item_state = "orangeclown"
+ item_color = "orangeclown"
+ fitted = FEMALE_UNIFORM_TOP
+ can_adjust = FALSE
+
+/obj/item/clothing/under/rank/rainbowclown
+ name = "rainbow clown suit"
+ desc = "'R A I N B O W HONK!' "
+ icon_state = "rainbowclown"
+ item_state = "rainbowclown"
+ item_color = "rainbowclown"
+ fitted = FEMALE_UNIFORM_TOP
+ can_adjust = FALSE
+
+/obj/item/clothing/under/rank/clown/Initialize()
+ . = ..()
+ AddComponent(/datum/component/squeak, list('sound/items/bikehorn.ogg'=1), 50)
/obj/item/clothing/under/rank/head_of_personnel
desc = "It's a jumpsuit worn by someone who works in the position of \"Head of Personnel\"."
diff --git a/code/modules/clothing/under/jobs/medsci.dm b/code/modules/clothing/under/jobs/medsci.dm
index e506f726df..f0980cae4d 100644
--- a/code/modules/clothing/under/jobs/medsci.dm
+++ b/code/modules/clothing/under/jobs/medsci.dm
@@ -87,6 +87,7 @@
item_color = "nursesuit"
permeability_coefficient = 0.5
armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0)
+ body_parts_covered = CHEST|GROIN|ARMS
fitted = NO_FEMALE_UNIFORM
can_adjust = FALSE
diff --git a/code/modules/clothing/under/jobs/security.dm b/code/modules/clothing/under/jobs/security.dm
index e2be42e014..b8858fdf1a 100644
--- a/code/modules/clothing/under/jobs/security.dm
+++ b/code/modules/clothing/under/jobs/security.dm
@@ -34,6 +34,7 @@
icon_state = "secskirt"
item_state = "r_suit"
item_color = "secskirt"
+ body_parts_covered = CHEST|GROIN|ARMS
can_adjust = FALSE //you know now that i think of it if you adjust the skirt and the sprite disappears isn't that just like flashing everyone
diff --git a/code/modules/clothing/under/miscellaneous.dm b/code/modules/clothing/under/miscellaneous.dm
index 57ead15de8..ce17f3741e 100644
--- a/code/modules/clothing/under/miscellaneous.dm
+++ b/code/modules/clothing/under/miscellaneous.dm
@@ -263,7 +263,7 @@
/obj/item/clothing/under/suit_jacket/white
name = "white suit"
- desc = "A white suit and jacket with a blue shirt. You wanna play rough? OKAY!."
+ desc = "A white suit and jacket with a blue shirt. You wanna play rough? OKAY!"
icon_state = "white_suit"
item_state = "white_suit"
item_color = "white_suit"
@@ -382,7 +382,7 @@
icon_state = "kilt"
item_state = "kilt"
item_color = "kilt"
- body_parts_covered = CHEST|GROIN|FEET
+ body_parts_covered = CHEST|GROIN|LEGS|FEET
fitted = FEMALE_UNIFORM_TOP
can_adjust = FALSE
@@ -473,6 +473,7 @@
icon_state = "striped_dress"
item_state = "stripeddress"
item_color = "striped_dress"
+ body_parts_covered = CHEST|GROIN|ARMS
fitted = FEMALE_UNIFORM_FULL
can_adjust = FALSE
@@ -482,6 +483,7 @@
icon_state = "sailor_dress"
item_state = "sailordress"
item_color = "sailor_dress"
+ body_parts_covered = CHEST|GROIN|ARMS
fitted = FEMALE_UNIFORM_TOP
can_adjust = FALSE
@@ -555,6 +557,7 @@
icon_state = "ysing"
item_state = "ysing"
item_color = "ysing"
+ body_parts_covered = CHEST|GROIN|ARMS
fitted = NO_FEMALE_UNIFORM
alternate_worn_layer = ABOVE_SHOES_LAYER
can_adjust = FALSE
@@ -565,6 +568,7 @@
icon_state = "bsing"
item_state = "bsing"
item_color = "bsing"
+ body_parts_covered = CHEST|GROIN|ARMS
alternate_worn_layer = ABOVE_SHOES_LAYER
fitted = FEMALE_UNIFORM_TOP
can_adjust = FALSE
@@ -594,6 +598,7 @@
desc = "Cute space ninja senpai not included."
icon_state = "geisha"
item_color = "geisha"
+ body_parts_covered = CHEST|GROIN|ARMS
can_adjust = FALSE
/obj/item/clothing/under/villain
@@ -722,6 +727,14 @@
fitted = NO_FEMALE_UNIFORM
can_adjust = FALSE
+/obj/item/clothing/under/gondola
+ name = "gondola hide suit"
+ desc = "Now you're cooking."
+ icon_state = "gondola"
+ item_state = "lb_suit"
+ item_color = "gondola"
+ can_adjust = FALSE
+
/obj/item/clothing/under/skeleton
name = "skeleton jumpsuit"
desc = "A black jumpsuit with a white bone pattern printed on it. Spooky!"
diff --git a/code/modules/crafting/recipes.dm b/code/modules/crafting/recipes.dm
index 1ffda7413e..586819ed8f 100644
--- a/code/modules/crafting/recipes.dm
+++ b/code/modules/crafting/recipes.dm
@@ -280,7 +280,7 @@
subcategory = CAT_AMMO
/datum/crafting_recipe/laserslug
- name = "Laser Slug Shell"
+ name = "Scatter Laser Shell"
result = /obj/item/ammo_casing/shotgun/laserslug
reqs = list(/obj/item/ammo_casing/shotgun/techshell = 1,
/obj/item/stock_parts/capacitor/adv = 1,
@@ -370,6 +370,14 @@
/obj/item/stack/rods = 12)
category = CAT_MISC
+/datum/crafting_recipe/mousetrap
+ name = "Mouse Trap"
+ result = /obj/item/assembly/mousetrap
+ time = 10
+ reqs = list(/obj/item/stack/sheet/cardboard = 1,
+ /obj/item/stack/rods = 1)
+ category = CAT_MISC
+
/datum/crafting_recipe/papersack
name = "Paper Sack"
result = /obj/item/storage/box/papersack
@@ -514,7 +522,7 @@
result = /obj/item/bikehorn/golden
time = 20
reqs = list(/obj/item/stack/sheet/mineral/bananium = 5,
- /obj/item/bikehorn)
+ /obj/item/bikehorn = 1)
category = CAT_MISC
/datum/crafting_recipe/bonedagger
@@ -677,3 +685,19 @@
tools = list(TOOL_WIRECUTTER)
reqs = list(/obj/item/bedsheet = 1)
category = CAT_CLOTHING
+
+/datum/crafting_recipe/aitater
+ name = "intelliTater"
+ result = /obj/item/aicard/aitater
+ time = 30
+ reqs = list(/obj/item/aicard = 1,
+ /obj/item/reagent_containers/food/snacks/grown/potato = 1)
+ category = CAT_MISC
+
+/datum/crafting_recipe/ghettojetpack
+ name = "Improvised Jetpack"
+ result = /obj/item/tank/jetpack/improvised
+ time = 30
+ reqs = list(/obj/item/tank/internals/oxygen/red = 2, /obj/item/extinguisher = 1, /obj/item/pipe = 3, /obj/item/stack/cable_coil = 30)//red oxygen tank so it looks right
+ category = CAT_MISC
+ tools = list(TOOL_WRENCH, TOOL_WELDER, TOOL_WIRECUTTER)
\ No newline at end of file
diff --git a/code/modules/detectivework/evidence.dm b/code/modules/detectivework/evidence.dm
index a97758e294..a3b17a4c1f 100644
--- a/code/modules/detectivework/evidence.dm
+++ b/code/modules/detectivework/evidence.dm
@@ -9,6 +9,7 @@
w_class = WEIGHT_CLASS_TINY
/obj/item/evidencebag/afterattack(obj/item/I, mob/user,proximity)
+ . = ..()
if(!proximity || loc == I)
return
evidencebagEquip(I, user)
diff --git a/code/modules/detectivework/footprints_and_rag.dm b/code/modules/detectivework/footprints_and_rag.dm
index 50a845a689..9f1f2bf380 100644
--- a/code/modules/detectivework/footprints_and_rag.dm
+++ b/code/modules/detectivework/footprints_and_rag.dm
@@ -24,23 +24,23 @@
return (OXYLOSS)
/obj/item/reagent_containers/glass/rag/afterattack(atom/A as obj|turf|area, mob/user,proximity)
+ . = ..()
if(!proximity)
return
if(iscarbon(A) && A.reagents && reagents.total_volume)
var/mob/living/carbon/C = A
var/reagentlist = pretty_string_from_reagent_list(reagents)
+ var/log_object = "a damp rag containing [reagentlist]"
if(user.a_intent == INTENT_HARM && !C.is_mouth_covered())
reagents.reaction(C, INGEST)
reagents.trans_to(C, reagents.total_volume)
C.visible_message("[user] has smothered \the [C] with \the [src]! ", "[user] has smothered you with \the [src]! ", "You hear some struggling and muffled cries of surprise. ")
- log_game("[key_name(user)] smothered [key_name(A)] with a damp rag containing [reagentlist]")
- log_attack("[key_name(user)] smothered [key_name(A)] with a damp rag containing [reagentlist]")
+ log_combat(user, C, "smothered", log_object)
else
reagents.reaction(C, TOUCH)
reagents.clear_reagents()
- log_game("[key_name(user)] touched [key_name(A)] with a damp rag containing [reagentlist]")
- log_attack("[key_name(user)] touched [key_name(A)] with a damp rag containing [reagentlist]")
C.visible_message("[user] has touched \the [C] with \the [src]. ")
+ log_combat(user, C, "touched", log_object)
else if(istype(A) && src in user)
user.visible_message("[user] starts to wipe down [A] with [src]!", "You start to wipe down [A] with [src]... ")
diff --git a/code/modules/detectivework/scanner.dm b/code/modules/detectivework/scanner.dm
index e8847adb11..ca746e74fd 100644
--- a/code/modules/detectivework/scanner.dm
+++ b/code/modules/detectivework/scanner.dm
@@ -59,6 +59,7 @@
scanning = 0
/obj/item/detective_scanner/afterattack(atom/A, mob/user, params)
+ . = ..()
scan(A, user)
return FALSE
@@ -191,6 +192,11 @@
to_chat(user, "The scanner logs are cleared. ")
log = list()
+/obj/item/detective_scanner/examine(mob/user)
+ ..()
+ if(LAZYLEN(log) && !scanning)
+ to_chat(user, "Alt-click to clear scanner logs. ")
+
/obj/item/detective_scanner/proc/displayDetectiveScanResults(mob/living/user)
// No need for can-use checks since the action button should do proper checks
if(!LAZYLEN(log))
diff --git a/code/modules/error_handler/error_handler.dm b/code/modules/error_handler/error_handler.dm
index e9f5ecb988..1c26a09c30 100644
--- a/code/modules/error_handler/error_handler.dm
+++ b/code/modules/error_handler/error_handler.dm
@@ -1,17 +1,16 @@
GLOBAL_VAR_INIT(total_runtimes, GLOB.total_runtimes || 0)
GLOBAL_VAR_INIT(total_runtimes_skipped, 0)
-#ifdef DEBUG
-
+#ifdef USE_CUSTOM_ERROR_HANDLER
#define ERROR_USEFUL_LEN 2
/world/Error(exception/E, datum/e_src)
GLOB.total_runtimes++
-
+
if(!istype(E)) //Something threw an unusual exception
log_world("uncaught runtime error: [E]")
return ..()
-
+
//this is snowflake because of a byond bug (ID:2306577), do not attempt to call non-builtin procs in this if
if(copytext(E.name,1,32) == "Maximum recursion level reached")
//log to world while intentionally triggering the byond bug.
@@ -86,8 +85,8 @@ GLOBAL_VAR_INIT(total_runtimes_skipped, 0)
var/list/usrinfo = null
var/locinfo
if(istype(usr))
- usrinfo = list(" usr: [datum_info_line(usr)]")
- locinfo = atom_loc_line(usr)
+ usrinfo = list(" usr: [key_name(usr)]")
+ locinfo = loc_name(usr)
if(locinfo)
usrinfo += " usr.loc: [locinfo]"
// The proceeding mess will almost definitely break if error messages are ever changed
@@ -128,5 +127,4 @@ GLOBAL_VAR_INIT(total_runtimes_skipped, 0)
// This writes the regular format (unwrapping newlines and inserting timestamps as needed).
log_runtime("runtime error: [E.name]\n[E.desc]")
-
-#endif
\ No newline at end of file
+#endif
diff --git a/code/modules/error_handler/error_viewer.dm b/code/modules/error_handler/error_viewer.dm
index 1f93bba9a0..adcbb8fb57 100644
--- a/code/modules/error_handler/error_viewer.dm
+++ b/code/modules/error_handler/error_viewer.dm
@@ -7,7 +7,7 @@
// logged errors. Only one instance of this datum should ever exist, and it's
// right here:
-#ifdef DEBUG
+#ifdef USE_CUSTOM_ERROR_HANDLER
GLOBAL_DATUM_INIT(error_cache, /datum/error_viewer/error_cache, new)
#else
// If debugging is disabled, there's nothing useful to log, so don't bother.
@@ -192,4 +192,4 @@ GLOBAL_DATUM(error_cache, /datum/error_viewer/error_cache)
browse_to(user, html)
/datum/error_viewer/error_entry/make_link(linktext, datum/error_viewer/back_to, linear)
- return is_skip_count ? name : ..()
\ No newline at end of file
+ return is_skip_count ? name : ..()
diff --git a/code/modules/events/holiday/vday.dm b/code/modules/events/holiday/vday.dm
index eb8c8340df..a4e8b276b5 100644
--- a/code/modules/events/holiday/vday.dm
+++ b/code/modules/events/holiday/vday.dm
@@ -88,7 +88,7 @@
"The virologist is rogue, and the only cure is a kiss from you.",
"Would you spend some time in my upgraded sleeper?",
"You must be a silicon, because you've unbolted my heart.",
- "Are you Nar-Sie? Because there's nar-one else I sie.",
+ "Are you Nar'Sie? Because there's nar-one else I sie.",
"If you were a taser, you'd be set to stunning.",
"Do you have stamina damage from running through my dreams?",
"If I were an alien, would you let me hug you?",
@@ -160,7 +160,7 @@
"A heart-shaped candy that reads: ERP",
"A heart-shaped candy that reads: LEWD",
"A heart-shaped candy that reads: LUSTY",
- "A heart-shaped candy that reads: SPESS LOVE"
+ "A heart-shaped candy that reads: SPESS LOVE",
"A heart-shaped candy that reads: AYY LMAO",
"A heart-shaped candy that reads: TABLE ME",
"A heart-shaped candy that reads: HAND CUFFS",
diff --git a/code/modules/events/immovable_rod.dm b/code/modules/events/immovable_rod.dm
index 2bb75cfade..abf13bcab9 100644
--- a/code/modules/events/immovable_rod.dm
+++ b/code/modules/events/immovable_rod.dm
@@ -105,7 +105,7 @@ In my current plan for it, 'solid' will be defined as anything with density == 1
/obj/effect/immovablerod/singularity_pull()
return
-/obj/effect/immovablerod/Collide(atom/clong)
+/obj/effect/immovablerod/Bump(atom/clong)
if(prob(10))
playsound(src, 'sound/effects/bang.ogg', 50, 1)
audible_message("You hear a CLANG! ")
diff --git a/code/modules/events/pirates.dm b/code/modules/events/pirates.dm
index 8b904ae9b8..1e1a85f108 100644
--- a/code/modules/events/pirates.dm
+++ b/code/modules/events/pirates.dm
@@ -1,5 +1,3 @@
-#define LOOT_LOCATOR_COOLDOWN 150
-
/datum/round_event_control/pirates
name = "Space Pirates"
typepath = /datum/round_event/pirates
@@ -26,10 +24,9 @@
/datum/round_event/pirates/setup()
ship_name = pick(strings(PIRATE_NAMES_FILE, "ship_names"))
-/datum/round_event/pirates/announce()
+/datum/round_event/pirates/announce(fake)
priority_announce("A report has been downloaded and printed out at all communications consoles.", "Incoming Classified Message", 'sound/ai/commandreport.ogg') // CITADEL EDIT metabreak
-
- if(!control) //Means this is false alarm, todo : explicit checks instead of using announceWhen
+ if(fake)
return
threat = new
payoff = round(SSshuttle.points * 0.80)
@@ -47,7 +44,7 @@
paid_off = TRUE
return
else
- priority_announce("Trying to cheat us ? You'll regret this!",sender_override = ship_name)
+ priority_announce("Trying to cheat us? You'll regret this!",sender_override = ship_name)
if(!shuttle_spawned)
spawn_shuttle()
@@ -60,7 +57,7 @@
/datum/round_event/pirates/proc/spawn_shuttle()
shuttle_spawned = TRUE
- var/list/candidates = pollGhostCandidates("Do you wish to be considered for pirate crew ?", ROLE_TRAITOR)
+ var/list/candidates = pollGhostCandidates("Do you wish to be considered for pirate crew?", ROLE_TRAITOR)
shuffle_inplace(candidates)
var/datum/map_template/shuttle/pirate/default/ship = new
@@ -120,12 +117,12 @@
gps.tracking = TRUE
active = TRUE
to_chat(user,"You toggle [src] [active ? "on":"off"]. ")
- to_chat(user,"The scrambling signal can be now tracked by gps. ")
+ to_chat(user,"The scrambling signal can be now tracked by GPS. ")
START_PROCESSING(SSobj,src)
/obj/machinery/shuttle_scrambler/interact(mob/user)
if(!active)
- if(alert(user, "Turning the scrambler on will make the shuttle trackable by GPS. Are you sure you want to do it ?", "Scrambler", "Yes", "Cancel") == "Cancel")
+ if(alert(user, "Turning the scrambler on will make the shuttle trackable by GPS. Are you sure you want to do it?", "Scrambler", "Yes", "Cancel") == "Cancel")
return
if(active || !user.canUseTopic(src))
return
@@ -227,43 +224,234 @@
mask_type = /obj/item/clothing/mask/breath
storage_type = /obj/item/tank/internals/oxygen
+
/obj/machinery/loot_locator
name = "Booty Locator"
desc = "This sophisticated machine scans the nearby space for items of value."
icon = 'icons/obj/machines/research.dmi'
icon_state = "tdoppler"
density = TRUE
- var/cooldown = 0
- var/result_count = 3 //Show X results.
-
-/obj/machinery/proc/display_current_value()
- var/area/current = get_area(src)
- var/value = 0
- for(var/turf/T in current.contents)
- value += export_item_and_contents(T,TRUE, TRUE, dry_run = TRUE)
- say("Current vault value : [value] credits.")
+ var/cooldown = 300
+ var/next_use = 0
/obj/machinery/loot_locator/interact(mob/user)
- if(world.time <= cooldown)
+ if(world.time <= next_use)
to_chat(user,"[src] is recharging. ")
return
- cooldown = world.time + LOOT_LOCATOR_COOLDOWN
- display_current_value()
- var/list/results = list()
- for(var/atom/movable/AM in world)
- if(is_type_in_typecache(AM,GLOB.pirate_loot_cache))
- if(is_station_level(AM.z))
- if(get_area(AM) == get_area(src)) //Should this be variable ?
- continue
- results += AM
- CHECK_TICK
- if(!results.len)
+ next_use = world.time + cooldown
+ var/atom/movable/AM = find_random_loot()
+ if(!AM)
say("No valuables located. Try again later.")
else
- for(var/i in 1 to result_count)
- if(!results.len)
- return
- var/atom/movable/AM = pick_n_take(results)
- say("Located: [AM.name] at [get_area_name(AM)]")
+ say("Located: [AM.name] at [get_area_name(AM)]")
-#undef LOOT_LOCATOR_COOLDOWN
\ No newline at end of file
+/obj/machinery/loot_locator/proc/find_random_loot()
+ if(!GLOB.exports_list.len)
+ setupExports()
+ var/list/possible_loot = list()
+ for(var/datum/export/pirate/E in GLOB.exports_list)
+ possible_loot += E
+ var/datum/export/pirate/P
+ var/atom/movable/AM
+ while(!AM && possible_loot.len)
+ P = pick_n_take(possible_loot)
+ AM = P.find_loot()
+ return AM
+
+//Pad & Pad Terminal
+/obj/machinery/piratepad
+ name = "cargo hold pad"
+ icon = 'icons/obj/telescience.dmi'
+ icon_state = "lpad-idle-o"
+ var/idle_state = "lpad-idle-o"
+ var/warmup_state = "lpad-idle"
+ var/sending_state = "lpad-beam"
+ var/cargo_hold_id
+
+/obj/machinery/piratepad/multitool_act(mob/living/user, obj/item/multitool/I)
+ if (istype(I))
+ to_chat(user, "You register [src] in [I]s buffer. ")
+ I.buffer = src
+ return TRUE
+
+/obj/machinery/computer/piratepad_control
+ name = "cargo hold control terminal"
+ var/status_report = "Idle"
+ var/obj/machinery/piratepad/pad
+ var/warmup_time = 100
+ var/sending = FALSE
+ var/points = 0
+ var/datum/export_report/total_report
+ var/sending_timer
+ var/cargo_hold_id
+
+/obj/machinery/computer/piratepad_control/Initialize()
+ ..()
+ return INITIALIZE_HINT_LATELOAD
+
+/obj/machinery/computer/piratepad_control/multitool_act(mob/living/user, obj/item/multitool/I)
+ if (istype(I) && istype(I.buffer,/obj/machinery/piratepad))
+ to_chat(user, "You link [src] with [I.buffer] in [I] buffer. ")
+ pad = I.buffer
+ updateDialog()
+ return TRUE
+
+/obj/machinery/computer/piratepad_control/LateInitialize()
+ . = ..()
+ if(cargo_hold_id)
+ for(var/obj/machinery/piratepad/P in GLOB.machines)
+ if(P.cargo_hold_id == cargo_hold_id)
+ pad = P
+ return
+ else
+ pad = locate() in range(4,src)
+
+/obj/machinery/computer/piratepad_control/ui_interact(mob/user)
+ . = ..()
+ var/list/t = list()
+ t += "Cargo Hold Control "
+ t += "Current cargo value : [points]"
+ t += "
"
+ if(!pad)
+ t += "No pad located.
"
+ else
+ t += " [status_report] "
+ if(!sending)
+ t += "Recalculate Value Send "
+ else
+ t += "Stop sending "
+
+ var/datum/browser/popup = new(user, "piratepad", name, 300, 500)
+ popup.set_content(t.Join())
+ popup.open()
+
+/obj/machinery/computer/piratepad_control/proc/recalc()
+ if(sending)
+ return
+ status_report = "Predicted value: "
+ var/datum/export_report/ex = new
+ for(var/atom/movable/AM in get_turf(pad))
+ if(AM == pad)
+ continue
+ export_item_and_contents(AM, EXPORT_PIRATE | EXPORT_CARGO | EXPORT_CONTRABAND | EXPORT_EMAG, apply_elastic = FALSE, dry_run = TRUE, external_report = ex)
+
+ for(var/datum/export/E in ex.total_amount)
+ status_report += E.total_printout(ex,notes = FALSE) + " "
+
+/obj/machinery/computer/piratepad_control/proc/send()
+ if(!sending)
+ return
+
+ var/datum/export_report/ex = new
+
+ for(var/atom/movable/AM in get_turf(pad))
+ if(AM == pad)
+ continue
+ export_item_and_contents(AM, EXPORT_PIRATE | EXPORT_CARGO | EXPORT_CONTRABAND | EXPORT_EMAG, apply_elastic = FALSE, delete_unsold = FALSE, external_report = ex)
+
+ status_report = "Sold: "
+ var/value = 0
+ for(var/datum/export/E in ex.total_amount)
+ var/export_text = E.total_printout(ex,notes = FALSE) //Don't want nanotrasen messages, makes no sense here.
+ if(!export_text)
+ continue
+
+ status_report += export_text + " "
+ value += ex.total_value[E]
+
+ if(!total_report)
+ total_report = ex
+ else
+ total_report.exported_atoms += ex.exported_atoms
+ for(var/datum/export/E in ex.total_amount)
+ total_report.total_amount[E] += ex.total_amount[E]
+ total_report.total_value[E] += ex.total_value[E]
+
+ points += value
+
+ pad.visible_message("[pad] activates! ")
+ flick(pad.sending_state,pad)
+ pad.icon_state = pad.idle_state
+ sending = FALSE
+ updateDialog()
+
+/obj/machinery/computer/piratepad_control/proc/start_sending()
+ if(sending)
+ return
+ sending = TRUE
+ status_report = "Sending..."
+ pad.visible_message("[pad] starts charging up. ")
+ pad.icon_state = pad.warmup_state
+ sending_timer = addtimer(CALLBACK(src,.proc/send),warmup_time, TIMER_STOPPABLE)
+
+/obj/machinery/computer/piratepad_control/proc/stop_sending()
+ if(!sending)
+ return
+ sending = FALSE
+ status_report = "Idle"
+ pad.icon_state = pad.idle_state
+ deltimer(sending_timer)
+
+/obj/machinery/computer/piratepad_control/Topic(href, href_list)
+ if(..())
+ return
+ if(pad)
+ if(href_list["recalc"])
+ recalc()
+ if(href_list["send"])
+ start_sending()
+ if(href_list["stop"])
+ stop_sending()
+ updateDialog()
+ else
+ updateDialog()
+
+/datum/export/pirate
+ export_category = EXPORT_PIRATE
+
+//Attempts to find the thing on station
+/datum/export/pirate/proc/find_loot()
+ return
+
+/datum/export/pirate/ransom
+ cost = 3000
+ unit_name = "hostage"
+ export_types = list(/mob/living/carbon/human)
+
+/datum/export/pirate/ransom/find_loot()
+ var/list/head_minds = SSjob.get_living_heads()
+ var/list/head_mobs = list()
+ for(var/datum/mind/M in head_minds)
+ head_mobs += M.current
+ if(head_mobs.len)
+ return pick(head_mobs)
+
+/datum/export/pirate/ransom/get_cost(atom/movable/AM)
+ var/mob/living/carbon/human/H = AM
+ if(H.stat != CONSCIOUS || !H.mind || !H.mind.assigned_role) //mint condition only
+ return 0
+ else
+ if(H.mind.assigned_role in GLOB.command_positions)
+ return 3000
+ else
+ return 1000
+
+/datum/export/pirate/parrot
+ cost = 2000
+ unit_name = "alive parrot"
+ export_types = list(/mob/living/simple_animal/parrot)
+
+/datum/export/pirate/parrot/find_loot()
+ for(var/mob/living/simple_animal/parrot/P in GLOB.alive_mob_list)
+ var/turf/T = get_turf(P)
+ if(T && is_station_level(T.z))
+ return P
+
+/datum/export/pirate/cash
+ cost = 1
+ unit_name = "bills"
+ export_types = list(/obj/item/stack/spacecash)
+
+/datum/export/pirate/cash/get_amount(obj/O)
+ var/obj/item/stack/spacecash/C = O
+ return ..() * C.amount * C.value
\ No newline at end of file
diff --git a/code/modules/events/shuttle_loan.dm b/code/modules/events/shuttle_loan.dm
index b46419e68a..cec897a61e 100644
--- a/code/modules/events/shuttle_loan.dm
+++ b/code/modules/events/shuttle_loan.dm
@@ -4,6 +4,8 @@
#define DEPARTMENT_RESUPPLY 4
#define ANTIDOTE_NEEDED 5
#define PIZZA_DELIVERY 6
+#define ITS_HIP_TO 7
+#define MY_GOD_JC 8
/datum/round_event_control/shuttle_loan
@@ -21,7 +23,7 @@
var/thanks_msg = "The cargo shuttle should return in five minutes. Have some supply points for your trouble."
/datum/round_event/shuttle_loan/setup()
- dispatch_type = pick(HIJACK_SYNDIE, RUSKY_PARTY, SPIDER_GIFT, DEPARTMENT_RESUPPLY, ANTIDOTE_NEEDED, PIZZA_DELIVERY)
+ dispatch_type = pick(HIJACK_SYNDIE, RUSKY_PARTY, SPIDER_GIFT, DEPARTMENT_RESUPPLY, ANTIDOTE_NEEDED, PIZZA_DELIVERY, ITS_HIP_TO, MY_GOD_JC)
/datum/round_event/shuttle_loan/announce(fake)
SSshuttle.shuttle_loan = src
@@ -70,8 +72,6 @@
P.name = "Cargo Report"
P.info = "Cargo: Seems we've ordered doubles of our department resupply packages this month. Can we send them to you?"
P.update_icon()
- thanks_msg = "The cargo shuttle should return in 5 minutes."
- bonus_points = 0
if(ANTIDOTE_NEEDED)
if(prob(50))
priority_announce("Cargo: Your station has been chosen for an epidemiological research project. Send us your cargo shuttle to receive your research samples.", "CentCom Research Initiatives")
@@ -94,6 +94,30 @@
P.name = "Cargo Report"
P.info = "Cargo: It looks like a neighbouring station accidentally delivered their pizza to you instead."
P.update_icon()
+ if(ITS_HIP_TO)
+ if(prob(50))
+ priority_announce("Cargo: One of our freighters carrying a bee shipment has been attacked by eco-terrorists. Can you clean up the mess for us?", "CentCom Janitorial Division")
+ else
+ priority_announce("A report has been downloaded and printed out at all communications consoles.", "Incoming Classified Message", 'sound/ai/commandreport.ogg') // CITADEL EDIT metabreak
+ for(var/obj/machinery/computer/communications/C in GLOB.machines)
+ if(!(C.stat & (BROKEN|NOPOWER)) && is_station_level(C.z))
+ var/obj/item/paper/P = new(C.loc)
+ P.name = "Cargo Report"
+ P.info = "Cargo: One of our freighters carrying a bee shipment has been attacked by eco-terrorists. Can you clean up the mess for us?."
+ P.update_icon()
+ bonus_points = 20000 //Toxin bees can be unbeelievably lethal
+ if(MY_GOD_JC)
+ if(prob(50))
+ priority_announce("Cargo: We have discovered an active Syndicate bomb near our VIP shuttle's fuel lines. If you feel up to the task, we will pay you for defusing it.", "CentCom Security Division")
+ else
+ priority_announce("A report has been downloaded and printed out at all communications consoles.", "Incoming Classified Message", 'sound/ai/commandreport.ogg') // CITADEL EDIT metabreak
+ for(var/obj/machinery/computer/communications/C in GLOB.machines)
+ if(!(C.stat & (BROKEN|NOPOWER)) && is_station_level(C.z))
+ var/obj/item/paper/P = new(C.loc)
+ P.name = "Cargo Report"
+ P.info = "Cargo: We have discovered an active Syndicate bomb near our VIP shuttle's fuel lines. If you feel up to the task, we will pay you for defusing it."
+ P.update_icon()
+ bonus_points = 45000 //If you mess up, people die and the shuttle gets turned into swiss cheese
/datum/round_event/shuttle_loan/proc/loan_shuttle()
priority_announce(thanks_msg, "Cargo shuttle commandeered by CentCom.")
@@ -119,6 +143,10 @@
SSshuttle.centcom_message += "Virus samples incoming."
if(PIZZA_DELIVERY)
SSshuttle.centcom_message += "Pizza delivery for [station_name()]"
+ if(ITS_HIP_TO)
+ SSshuttle.centcom_message += "Biohazard cleanup incoming."
+ if(MY_GOD_JC)
+ SSshuttle.centcom_message += "Live explosive ordnance incoming. Exercise extreme caution."
/datum/round_event/shuttle_loan/tick()
if(dispatched)
@@ -149,12 +177,12 @@
var/datum/supply_pack/pack = SSshuttle.supply_packs[/datum/supply_pack/emergency/specialops]
pack.generate(pick_n_take(empty_shuttle_turfs))
- shuttle_spawns.Add(/mob/living/simple_animal/hostile/syndicate)
- shuttle_spawns.Add(/mob/living/simple_animal/hostile/syndicate)
+ shuttle_spawns.Add(/mob/living/simple_animal/hostile/syndicate/ranged/infiltrator)
+ shuttle_spawns.Add(/mob/living/simple_animal/hostile/syndicate/ranged/infiltrator)
if(prob(75))
- shuttle_spawns.Add(/mob/living/simple_animal/hostile/syndicate)
+ shuttle_spawns.Add(/mob/living/simple_animal/hostile/syndicate/ranged/infiltrator)
if(prob(50))
- shuttle_spawns.Add(/mob/living/simple_animal/hostile/syndicate)
+ shuttle_spawns.Add(/mob/living/simple_animal/hostile/syndicate/ranged/infiltrator)
if(RUSKY_PARTY)
var/datum/supply_pack/pack = SSshuttle.supply_packs[/datum/supply_pack/service/party]
@@ -224,15 +252,44 @@
var/decal = pick(/obj/effect/decal/cleanable/flour, /obj/effect/decal/cleanable/robot_debris, /obj/effect/decal/cleanable/oil)
new decal(pick_n_take(empty_shuttle_turfs))
if(PIZZA_DELIVERY)
- shuttle_spawns.Add(/obj/item/pizzabox/margherita)
- shuttle_spawns.Add(/obj/item/pizzabox/margherita)
- shuttle_spawns.Add(/obj/item/pizzabox/meat)
- shuttle_spawns.Add(/obj/item/pizzabox/meat)
- shuttle_spawns.Add(/obj/item/pizzabox/vegetable)
- if(prob(10))
- shuttle_spawns.Add(/obj/item/pizzabox/bomb)
+ var/naughtypizza = list(/obj/item/pizzabox/bomb,/obj/item/pizzabox/margherita/robo) //oh look another blaklist, for pizza nonetheless!
+ var/nicepizza = list(/obj/item/pizzabox/margherita, /obj/item/pizzabox/meat, /obj/item/pizzabox/vegetable, /obj/item/pizzabox/mushroom)
+ for(var/i in 1 to 6)
+ shuttle_spawns.Add(pick(prob(5) ? naughtypizza : nicepizza))
+ if(ITS_HIP_TO)
+ var/datum/supply_pack/pack = SSshuttle.supply_packs[/datum/supply_pack/organic/hydroponics/beekeeping_fullkit]
+ pack.generate(pick_n_take(empty_shuttle_turfs))
+
+ shuttle_spawns.Add(/obj/effect/mob_spawn/human/corpse/bee_terrorist)
+ shuttle_spawns.Add(/obj/effect/mob_spawn/human/corpse/cargo_tech)
+ shuttle_spawns.Add(/obj/effect/mob_spawn/human/corpse/cargo_tech)
+ shuttle_spawns.Add(/obj/effect/mob_spawn/human/corpse/nanotrasensoldier)
+ shuttle_spawns.Add(/obj/item/gun/ballistic/automatic/pistol/no_mag)
+ shuttle_spawns.Add(/obj/item/gun/ballistic/automatic/pistol/m1911/no_mag)
+ shuttle_spawns.Add(/obj/item/honey_frame)
+ shuttle_spawns.Add(/obj/item/honey_frame)
+ shuttle_spawns.Add(/obj/item/honey_frame)
+ shuttle_spawns.Add(/obj/structure/beebox/unwrenched)
+ shuttle_spawns.Add(/obj/item/queen_bee/bought)
+ shuttle_spawns.Add(/obj/structure/closet/crate/hydroponics)
+
+ for(var/i in 1 to 8)
+ shuttle_spawns.Add(/mob/living/simple_animal/hostile/poison/bees/toxin)
+
+ for(var/i in 1 to 5)
+ var/decal = pick(/obj/effect/decal/cleanable/blood, /obj/effect/decal/cleanable/insectguts)
+ new decal(pick_n_take(empty_shuttle_turfs))
+
+ for(var/i in 1 to 10)
+ var/casing = /obj/item/ammo_casing/spent
+ new casing(pick_n_take(empty_shuttle_turfs))
+
+ if(MY_GOD_JC)
+ shuttle_spawns.Add(/obj/machinery/syndicatebomb/shuttle_loan)
+ if(prob(95))
+ shuttle_spawns.Add(/obj/item/paper/fluff/cargo/bomb)
else
- shuttle_spawns.Add(/obj/item/pizzabox/margherita)
+ shuttle_spawns.Add(/obj/item/paper/fluff/cargo/bomb/allyourbase)
var/false_positive = 0
while(shuttle_spawns.len && empty_shuttle_turfs.len)
@@ -244,9 +301,36 @@
var/spawn_type = pick_n_take(shuttle_spawns)
new spawn_type(T)
+//items that appear only in shuttle loan events
+
+/obj/item/storage/belt/fannypack/yellow/bee_terrorist/PopulateContents()
+ new /obj/item/grenade/plastic/c4 (src)
+ new /obj/item/reagent_containers/pill/cyanide(src)
+ new /obj/item/grenade/chem_grenade/facid(src)
+
+/obj/item/paper/fluff/bee_objectives
+ name = "Objectives of a Bee Liberation Front Operative"
+ info = "Objective #1 . Liberate all bees on the NT transport vessel 2416/B. Success! Objective #2 . Escape alive. Failed. "
+
+/obj/machinery/syndicatebomb/shuttle_loan/Initialize()
+ . = ..()
+ setAnchored(TRUE)
+ timer_set = rand(480, 600) //once the supply shuttle docks (after 5 minutes travel time), players have between 3-5 minutes to defuse the bomb
+ activate()
+ update_icon()
+
+/obj/item/paper/fluff/cargo/bomb
+ name = "hastly scribbled note"
+ info = "GOOD LUCK!"
+
+/obj/item/paper/fluff/cargo/bomb/allyourbase
+ info = "Somebody set us up the bomb!"
+
#undef HIJACK_SYNDIE
#undef RUSKY_PARTY
#undef SPIDER_GIFT
#undef DEPARTMENT_RESUPPLY
#undef ANTIDOTE_NEEDED
#undef PIZZA_DELIVERY
+#undef ITS_HIP_TO
+#undef MY_GOD_JC
diff --git a/code/modules/events/spacevine.dm b/code/modules/events/spacevine.dm
index 0d1fb7e7d8..966d5cc0d8 100644
--- a/code/modules/events/spacevine.dm
+++ b/code/modules/events/spacevine.dm
@@ -92,7 +92,7 @@
if(issilicon(crosser))
return
if(prob(severity) && istype(crosser) && !isvineimmune(crosser))
- to_chat(crosser, "You accidently touch the vine and feel a strange sensation. ")
+ to_chat(crosser, "You accidentally touch the vine and feel a strange sensation. ")
crosser.adjustToxLoss(5)
/datum/spacevine_mutation/toxicity/on_eat(obj/structure/spacevine/holder, mob/living/eater)
diff --git a/code/modules/events/vent_clog.dm b/code/modules/events/vent_clog.dm
index 84846c236a..160304b1c3 100644
--- a/code/modules/events/vent_clog.dm
+++ b/code/modules/events/vent_clog.dm
@@ -79,6 +79,11 @@
typepath = /datum/round_event/vent_clog/beer
max_occurrences = 0
+/datum/round_event_control/vent_clog/plasma_decon
+ name = "Plasma decontamination"
+ typepath = /datum/round_event/vent_clog/plasma_decon
+ max_occurrences = 0
+
/datum/round_event/vent_clog/beer
reagentsAmount = 100
@@ -96,3 +101,14 @@
foam.set_up(200, get_turf(vent), R)
foam.start()
CHECK_TICK
+
+/datum/round_event/vent_clog/plasma_decon/announce()
+ priority_announce("We are deploying an experimental plasma decontamination system. Please stand away from the vents and do not breathe the smoke that comes out.", "Central Command Update")
+
+/datum/round_event/vent_clog/plasma_decon/start()
+ for(var/obj/machinery/atmospherics/components/unary/vent in vents)
+ if(vent && vent.loc)
+ var/datum/effect_system/smoke_spread/freezing/decon/smoke = new
+ smoke.set_up(7, get_turf(vent), 7)
+ smoke.start()
+ CHECK_TICK
diff --git a/code/modules/events/wizard/departmentrevolt.dm b/code/modules/events/wizard/departmentrevolt.dm
index 9a8c70c504..f37b7470fb 100644
--- a/code/modules/events/wizard/departmentrevolt.dm
+++ b/code/modules/events/wizard/departmentrevolt.dm
@@ -46,8 +46,8 @@
if(M.assigned_role == job)
citizens += H
M.add_antag_datum(/datum/antagonist/separatist,nation)
- H.log_message("Was made into a separatist, long live [nation_name]! ", INDIVIDUAL_ATTACK_LOG)
-
+ H.log_message("Was made into a separatist, long live [nation_name]!", LOG_ATTACK, color="red")
+
if(citizens.len)
var/message
for(var/job in jobs_to_revolt)
diff --git a/code/modules/events/wizard/greentext.dm b/code/modules/events/wizard/greentext.dm
index 3dee740436..356e83757f 100644
--- a/code/modules/events/wizard/greentext.dm
+++ b/code/modules/events/wizard/greentext.dm
@@ -63,11 +63,11 @@
/obj/item/greentext/proc/check_winner()
if(!new_holder)
return
-
+
if(is_centcom_level(new_holder.z))//you're winner!
to_chat(new_holder, "At last it feels like victory is assured! ")
new_holder.mind.add_antag_datum(/datum/antagonist/greentext)
- new_holder.log_message("Won with greentext!!! ", INDIVIDUAL_ATTACK_LOG)
+ new_holder.log_message("won with greentext!!!", LOG_ATTACK, color="green")
color_altered_mobs -= new_holder
resistance_flags |= ON_FIRE
qdel(src)
diff --git a/code/modules/events/wizard/imposter.dm b/code/modules/events/wizard/imposter.dm
index c2bf0d5254..1c8ef95baa 100644
--- a/code/modules/events/wizard/imposter.dm
+++ b/code/modules/events/wizard/imposter.dm
@@ -36,5 +36,5 @@
SSticker.mode.apprentices += I.mind
I.mind.special_role = "imposter"
//
- I.log_message("Is an imposter! ", INDIVIDUAL_ATTACK_LOG) //?
+ I.log_message("is an imposter!", LOG_ATTACK, color="red") //?
SEND_SOUND(I, sound('sound/effects/magic.ogg'))
diff --git a/code/modules/events/wizard/rpgloot.dm b/code/modules/events/wizard/rpgloot.dm
index d94ee0e212..420582ddab 100644
--- a/code/modules/events/wizard/rpgloot.dm
+++ b/code/modules/events/wizard/rpgloot.dm
@@ -34,6 +34,7 @@
var/one_use = TRUE
/obj/item/upgradescroll/afterattack(obj/item/target, mob/user , proximity)
+ . = ..()
if(!proximity || !istype(target))
return
diff --git a/code/modules/fields/fields.dm b/code/modules/fields/fields.dm
index 0fdd2699d6..5e34c934d9 100644
--- a/code/modules/fields/fields.dm
+++ b/code/modules/fields/fields.dm
@@ -305,7 +305,7 @@
to_chat(user, "You turn [src] [operating? "on":"off"].")
QDEL_NULL(mobhook)
if(!istype(current) && operating)
- mobhook = user.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED), CALLBACK(src, .proc/on_mob_move))
+ mobhook = user.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src, .proc/on_mob_move)))
setup_debug_field()
else if(!operating)
QDEL_NULL(current)
diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm
index 2899a190c3..e30bac4f41 100644
--- a/code/modules/flufftext/Hallucination.dm
+++ b/code/modules/flufftext/Hallucination.dm
@@ -795,7 +795,7 @@ GLOBAL_LIST_INIT(hallucination_list, list(
if("alarm")
target.playsound_local(source, 'sound/machines/alarm.ogg', 100, 0)
if("beepsky")
- target.playsound_local(source, 'sound/voice/bfreeze.ogg', 35, 0)
+ target.playsound_local(source, 'sound/voice/beepsky/freeze.ogg', 35, 0)
if("mech")
var/mech_dir = pick(GLOB.cardinals)
for(var/i in 1 to rand(4,9))
@@ -1278,6 +1278,9 @@ GLOBAL_LIST_INIT(hallucination_list, list(
var/list/turf/startlocs = list()
for(var/turf/open/T in view(world.view+1,target)-view(world.view,target))
startlocs += T
+ if(!startlocs.len)
+ qdel(src)
+ return
var/turf/start = pick(startlocs)
var/proj_type = pick(subtypesof(/obj/item/projectile/hallucination))
feedback_details += "Type: [proj_type]"
diff --git a/code/modules/food_and_drinks/drinks/drinks.dm b/code/modules/food_and_drinks/drinks/drinks.dm
index d84deb8132..9256f5bdd1 100644
--- a/code/modules/food_and_drinks/drinks/drinks.dm
+++ b/code/modules/food_and_drinks/drinks/drinks.dm
@@ -46,7 +46,7 @@
if(!reagents || !reagents.total_volume)
return // The drink might be empty after the delay, such as by spam-feeding
M.visible_message("[user] feeds the contents of [src] to [M]. ", "[user] feeds the contents of [src] to [M]. ")
- add_logs(user, M, "fed", reagents.log_list())
+ log_combat(user, M, "fed", reagents.log_list())
var/fraction = min(gulp_size/reagents.total_volume, 1)
checkLiked(fraction, M)
@@ -56,6 +56,7 @@
return 1
/obj/item/reagent_containers/food/drinks/afterattack(obj/target, mob/user , proximity)
+ . = ..()
if(!proximity)
return
@@ -93,8 +94,6 @@
var/trans = target.reagents.trans_to(src, amount_per_transfer_from_this)
to_chat(user, "You fill [src] with [trans] units of the contents of [target]. ")
- else
-
/obj/item/reagent_containers/food/drinks/attackby(obj/item/I, mob/user, params)
var/hotness = I.is_hot()
if(hotness && reagents)
diff --git a/code/modules/food_and_drinks/drinks/drinks/bottle.dm b/code/modules/food_and_drinks/drinks/drinks/bottle.dm
index f35bd6f937..3bc7443a9b 100644
--- a/code/modules/food_and_drinks/drinks/drinks/bottle.dm
+++ b/code/modules/food_and_drinks/drinks/drinks/bottle.dm
@@ -105,7 +105,7 @@
"[target] hits [target.p_them()]self with a bottle of [src.name][head_attack_message]! ")
//Attack logs
- add_logs(user, target, "attacked", src)
+ log_combat(user, target, "attacked", src)
//The reagents in the bottle splash all over the target, thanks for the idea Nodrak
SplashReagents(target)
diff --git a/code/modules/food_and_drinks/drinks/drinks/drinkingglass.dm b/code/modules/food_and_drinks/drinks/drinks/drinkingglass.dm
index faafcdf776..c24b22ce71 100644
--- a/code/modules/food_and_drinks/drinks/drinks/drinkingglass.dm
+++ b/code/modules/food_and_drinks/drinks/drinks/drinkingglass.dm
@@ -108,13 +108,14 @@
if(user.a_intent == INTENT_HARM && ismob(target) && target.reagents && reagents.total_volume)
target.visible_message("[user] splashes the contents of [src] onto [target]! ", \
"[user] splashes the contents of [src] onto [target]! ")
- add_logs(user, target, "splashed", src)
+ log_combat(user, target, "splashed", src)
reagents.reaction(target, TOUCH)
reagents.clear_reagents()
return
..()
/obj/item/reagent_containers/food/drinks/drinkingglass/afterattack(obj/target, mob/user, proximity)
+ . = ..()
if((!proximity) || !check_allowed_items(target,target_self=1))
return
@@ -124,5 +125,4 @@
reagents.reaction(target, TOUCH)
reagents.clear_reagents()
return
- ..()
diff --git a/code/modules/food_and_drinks/food/condiment.dm b/code/modules/food_and_drinks/food/condiment.dm
index 53a0c2fb5b..d6d744f4db 100644
--- a/code/modules/food_and_drinks/food/condiment.dm
+++ b/code/modules/food_and_drinks/food/condiment.dm
@@ -49,7 +49,7 @@
if(!reagents || !reagents.total_volume)
return // The condiment might be empty after the delay.
user.visible_message("[user] feeds [M] from [src]. ")
- add_logs(user, M, "fed", reagents.log_list())
+ log_combat(user, M, "fed", reagents.log_list())
var/fraction = min(10/reagents.total_volume, 1)
reagents.reaction(M, INGEST, fraction)
@@ -58,6 +58,7 @@
return 1
/obj/item/reagent_containers/food/condiment/afterattack(obj/target, mob/user , proximity)
+ . = ..()
if(!proximity)
return
if(istype(target, /obj/structure/reagent_dispensers)) //A dispenser. Transfer FROM it TO us.
@@ -146,6 +147,7 @@
return (TOXLOSS)
/obj/item/reagent_containers/food/condiment/saltshaker/afterattack(obj/target, mob/living/user, proximity)
+ . = ..()
if(!proximity)
return
if(isturf(target))
@@ -156,7 +158,6 @@
reagents.remove_reagent("sodiumchloride", 2)
new/obj/effect/decal/cleanable/salt(target)
return
- ..()
/obj/item/reagent_containers/food/condiment/peppermill
name = "pepper mill"
@@ -241,6 +242,7 @@
return
/obj/item/reagent_containers/food/condiment/pack/afterattack(obj/target, mob/user , proximity)
+ . = ..()
if(!proximity)
return
diff --git a/code/modules/food_and_drinks/food/snacks.dm b/code/modules/food_and_drinks/food/snacks.dm
index a74c7c6f69..f2b195c32d 100644
--- a/code/modules/food_and_drinks/food/snacks.dm
+++ b/code/modules/food_and_drinks/food/snacks.dm
@@ -1,3 +1,33 @@
+/** # Snacks
+
+Items in the "Snacks" subcategory are food items that people actually eat. The key points are that they are created
+already filled with reagents and are destroyed when empty. Additionally, they make a "munching" noise when eaten.
+
+Notes by Darem: Food in the "snacks" subtype can hold a maximum of 50 units. Generally speaking, you don't want to go over 40
+total for the item because you want to leave space for extra condiments. If you want effect besides healing, add a reagent for
+it. Try to stick to existing reagents when possible (so if you want a stronger healing effect, just use omnizine). On use
+effect (such as the old officer eating a donut code) requires a unique reagent (unless you can figure out a better way).
+
+The nutriment reagent and bitesize variable replace the old heal_amt and amount variables. Each unit of nutriment is equal to
+2 of the old heal_amt variable. Bitesize is the rate at which the reagents are consumed. So if you have 6 nutriment and a
+bitesize of 2, then it'll take 3 bites to eat. Unlike the old system, the contained reagents are evenly spread among all
+the bites. No more contained reagents = no more bites.
+
+Here is an example of the new formatting for anyone who wants to add more food items.
+```
+/obj/item/reagent_containers/food/snacks/xenoburger //Identification path for the object.
+ name = "Xenoburger" //Name that displays in the UI.
+ desc = "Smells caustic. Tastes like heresy." //Duh
+ icon_state = "xburger" //Refers to an icon in food.dmi
+/obj/item/reagent_containers/food/snacks/xenoburger/Initialize() //Don't mess with this. | nO I WILL MESS WITH THIS
+ . = ..() //Same here.
+ reagents.add_reagent("xenomicrobes", 10) //This is what is in the food item. you may copy/paste
+ reagents.add_reagent("nutriment", 2) //this line of code for all the contents.
+ bitesize = 3 //This is the amount each bite consumes.
+```
+
+All foods are distributed among various categories. Use common sense.
+*/
/obj/item/reagent_containers/food/snacks
name = "snack"
desc = "Yummy."
@@ -76,11 +106,11 @@
else if(fullness > 50 && fullness < 150)
user.visible_message("[user] hungrily takes a [eatverb] from \the [src]. ", "You hungrily take a [eatverb] from \the [src]. ")
else if(fullness > 150 && fullness < 500)
- user.visible_message("[user] takes a [eatverb] from \the [src]. ", "You take a [eatverb] from \the [src]. ")
+ user.visible_message("[user] takes a [eatverb] from \the [src]. ", "You take a [eatverb] from \the [src]. ")
else if(fullness > 500 && fullness < 600)
user.visible_message("[user] unwillingly takes a [eatverb] of a bit of \the [src]. ", "You unwillingly take a [eatverb] of a bit of \the [src]. ")
else if(fullness > (600 * (1 + M.overeatduration / 2000))) // The more you eat - the more you can eat
- user.visible_message("[user] cannot force any more of \the [src] to go down [user.p_their()] throat! ", "You cannot force any more of \the [src] to go down your throat! ")
+ user.visible_message("[user] cannot force any more of \the [src] to go down [user.p_their()] throat! ", "You cannot force any more of \the [src] to go down your throat! ")
return 0
if(M.has_trait(TRAIT_VORACIOUS))
M.changeNext_move(CLICK_CD_MELEE * 0.5) //nom nom nom
@@ -96,7 +126,7 @@
if(!do_mob(user, M))
return
- add_logs(user, M, "fed", reagents.log_list())
+ log_combat(user, M, "fed", reagents.log_list())
M.visible_message("[user] forces [M] to eat [src]. ", \
"[user] forces [M] to eat [src]. ")
@@ -120,11 +150,6 @@
return 0
-
-/obj/item/reagent_containers/food/snacks/afterattack(obj/target, mob/user , proximity)
- return
-
-
/obj/item/reagent_containers/food/snacks/examine(mob/user)
..()
if(bitecount == 0)
@@ -299,40 +324,8 @@
M.emote("me", 1, "[sattisfaction_text]")
qdel(src)
-
-//////////////////////////////////////////////////
-////////////////////////////////////////////Snacks
-//////////////////////////////////////////////////
-//Items in the "Snacks" subcategory are food items that people actually eat. The key points are that they are created
-// already filled with reagents and are destroyed when empty. Additionally, they make a "munching" noise when eaten.
-
-//Notes by Darem: Food in the "snacks" subtype can hold a maximum of 50 units Generally speaking, you don't want to go over 40
-// total for the item because you want to leave space for extra condiments. If you want effect besides healing, add a reagent for
-// it. Try to stick to existing reagents when possible (so if you want a stronger healing effect, just use omnizine). On use
-// effect (such as the old officer eating a donut code) requires a unique reagent (unless you can figure out a better way).
-
-//The nutriment reagent and bitesize variable replace the old heal_amt and amount variables. Each unit of nutriment is equal to
-// 2 of the old heal_amt variable. Bitesize is the rate at which the reagents are consumed. So if you have 6 nutriment and a
-// bitesize of 2, then it'll take 3 bites to eat. Unlike the old system, the contained reagents are evenly spread among all
-// the bites. No more contained reagents = no more bites.
-
-//Here is an example of the new formatting for anyone who wants to add more food items.
-///obj/item/reagent_containers/food/snacks/xenoburger //Identification path for the object.
-// name = "Xenoburger" //Name that displays in the UI.
-// desc = "Smells caustic. Tastes like heresy." //Duh
-// icon_state = "xburger" //Refers to an icon in food.dmi
-///obj/item/reagent_containers/food/snacks/xenoburger/Initialize() //Don't mess with this. | nO I WILL MESS WITH THIS
-// . = ..() //Same here.
-// reagents.add_reagent("xenomicrobes", 10) //This is what is in the food item. you may copy/paste
-// reagents.add_reagent("nutriment", 2) //this line of code for all the contents.
-// bitesize = 3 //This is the amount each bite consumes.
-
-//All foods are distributed among various categories. Use common sense.
-
-/////////////////////////////////////////////////Store////////////////////////////////////////
-// All the food items that can store an item inside itself, like bread or cake.
-
-
+// //////////////////////////////////////////////Store////////////////////////////////////////
+/// All the food items that can store an item inside itself, like bread or cake.
/obj/item/reagent_containers/food/snacks/store
w_class = WEIGHT_CLASS_NORMAL
var/stored_item = 0
diff --git a/code/modules/food_and_drinks/food/snacks/meat.dm b/code/modules/food_and_drinks/food/snacks/meat.dm
index 7cee86bd38..1a82d1b406 100644
--- a/code/modules/food_and_drinks/food/snacks/meat.dm
+++ b/code/modules/food_and_drinks/food/snacks/meat.dm
@@ -257,6 +257,16 @@
tastes = list("bacon" = 1)
foodtype = MEAT
+/obj/item/reagent_containers/food/snacks/meat/slab/gondola
+ name = "gondola meat"
+ desc = "According to legends of old, consuming raw gondola flesh grants one inner peace."
+ list_reagents = list("nutriment" = 3, "tranquility" = 5, "cooking_oil" = 3)
+ tastes = list("meat" = 4, "tranquility" = 1)
+ filling_color = "#9A6750"
+ cooked_type = /obj/item/reagent_containers/food/snacks/meat/steak/gondola
+ slice_path = /obj/item/reagent_containers/food/snacks/meat/rawcutlet/gondola
+ foodtype = RAW | MEAT
+
////////////////////////////////////// MEAT STEAKS ///////////////////////////////////////////////////////////
@@ -304,6 +314,10 @@
tastes = list("meat" = 1, "rock" = 1)
foodtype = MEAT
+/obj/item/reagent_containers/food/snacks/meat/steak/gondola
+ name = "gondola steak"
+ tastes = list("meat" = 1, "tranquility" = 1)
+
//////////////////////////////// MEAT CUTLETS ///////////////////////////////////////////////////////
//Raw cutlets
@@ -361,6 +375,11 @@
cooked_type = /obj/item/reagent_containers/food/snacks/meat/cutlet/spider
tastes = list("cobwebs" = 1)
+/obj/item/reagent_containers/food/snacks/meat/rawcutlet/gondola
+ name = "raw gondola cutlet"
+ cooked_type = /obj/item/reagent_containers/food/snacks/meat/cutlet/gondola
+ tastes = list("meat" = 1, "tranquility" = 1)
+
//Cooked cutlets
/obj/item/reagent_containers/food/snacks/meat/cutlet
@@ -396,3 +415,7 @@
/obj/item/reagent_containers/food/snacks/meat/cutlet/spider
name = "spider cutlet"
tastes = list("cobwebs" = 1)
+
+/obj/item/reagent_containers/food/snacks/meat/cutlet/gondola
+ name = "gondola cutlet"
+ tastes = list("meat" = 1, "tranquility" = 1)
diff --git a/code/modules/food_and_drinks/food/snacks_bread.dm b/code/modules/food_and_drinks/food/snacks_bread.dm
index 95c66f019e..0638bfad25 100644
--- a/code/modules/food_and_drinks/food/snacks_bread.dm
+++ b/code/modules/food_and_drinks/food/snacks_bread.dm
@@ -188,11 +188,11 @@
/obj/item/reagent_containers/food/snacks/deepfryholder/Initialize(mapload, obj/item/fried)
. = ..()
name = fried.name //We'll determine the other stuff when it's actually removed
- icon = fried.icon
- overlays = fried.copy_overlays()
+ appearance = fried.appearance
+ layer = initial(layer)
+ plane = initial(plane)
lefthand_file = fried.lefthand_file
righthand_file = fried.righthand_file
- icon_state = fried.icon_state
item_state = fried.item_state
desc = fried.desc
w_class = fried.w_class
@@ -276,4 +276,4 @@
/obj/item/reagent_containers/food/snacks/butterdog/ComponentInitialize()
. = ..()
- AddComponent(/datum/component/slippery, 80)
\ No newline at end of file
+ AddComponent(/datum/component/slippery, 80)
diff --git a/code/modules/food_and_drinks/food/snacks_meat.dm b/code/modules/food_and_drinks/food/snacks_meat.dm
index bd1dbc4f8e..c77242e69d 100644
--- a/code/modules/food_and_drinks/food/snacks_meat.dm
+++ b/code/modules/food_and_drinks/food/snacks_meat.dm
@@ -189,7 +189,7 @@
var/mob/living/carbon/monkey/bananas = new(drop_location(), TRUE, spammer)
if (!QDELETED(bananas))
visible_message("[src] expands! ")
- bananas.log_message("Spawned via [src] at [AREACOORD(src)], Last attached mob: [key_name(spammer)].", INDIVIDUAL_ATTACK_LOG)
+ bananas.log_message("Spawned via [src] at [AREACOORD(src)], Last attached mob: [key_name(spammer)].", LOG_ATTACK)
else if (!spammer) // Visible message in case there are no fingerprints
visible_message("[src] fails to expand! ")
qdel(src)
diff --git a/code/modules/food_and_drinks/food/snacks_pastry.dm b/code/modules/food_and_drinks/food/snacks_pastry.dm
index 80aa588bae..1956365896 100644
--- a/code/modules/food_and_drinks/food/snacks_pastry.dm
+++ b/code/modules/food_and_drinks/food/snacks_pastry.dm
@@ -33,7 +33,7 @@
H.adjust_disgust(-5 + -2.5 * fraction)
GET_COMPONENT_FROM(mood, /datum/component/mood, H)
if(mood)
- mood.add_event("fav_food", /datum/mood_event/favorite_food)
+ mood.add_event(null, "fav_food", /datum/mood_event/favorite_food)
last_check_time = world.time
return
..()
@@ -89,6 +89,13 @@
icon_state = "jdonut1"
extra_reagent = "cherryjelly"
foodtype = JUNKFOOD | GRAIN | FRIED | FRUIT
+
+/obj/item/reagent_containers/food/snacks/donut/meat
+ bonus_reagents = list("ketchup" = 1)
+ list_reagents = list("nutriment" = 3, "ketchup" = 2)
+ tastes = list("meat" = 1)
+ foodtype = JUNKFOOD | MEAT | GROSS | FRIED
+
////////////////////////////////////////////MUFFINS////////////////////////////////////////////
diff --git a/code/modules/food_and_drinks/food/snacks_pizza.dm b/code/modules/food_and_drinks/food/snacks_pizza.dm
index a94c1a25c7..a6b026ac30 100644
--- a/code/modules/food_and_drinks/food/snacks_pizza.dm
+++ b/code/modules/food_and_drinks/food/snacks_pizza.dm
@@ -24,6 +24,10 @@
tastes = list("crust" = 1, "tomato" = 1, "cheese" = 1)
foodtype = GRAIN | VEGETABLES
+/obj/item/reagent_containers/food/snacks/pizza/margherita/robo/Initialize()
+ bonus_reagents += list("nanomachines" = 70)
+ return ..()
+
/obj/item/reagent_containers/food/snacks/pizzaslice/margherita
name = "margherita slice"
desc = "A slice of the most cheezy pizza in galaxy."
@@ -156,8 +160,59 @@
tastes = list("crust" = 1, "tomato" = 1, "cheese" = 1, "pineapple" = 2, "ham" = 2)
foodtype = GRAIN | VEGETABLES | DAIRY | MEAT | FRUIT | PINEAPPLE
+/obj/item/reagent_containers/food/snacks/pizza/arnold
+ name = "\improper Arnold pizza"
+ desc = "Hello, you've reached Arnold's pizza shop. I'm not here now, I'm out killing pepperoni."
+ icon_state = "arnoldpizza"
+ slice_path = /obj/item/reagent_containers/food/snacks/pizzaslice/arnold
+ bonus_reagents = list("nutriment" = 30, "vitamin" = 6, "iron" = 10, "omnizine" = 30)
+ tastes = list("crust" = 1, "tomato" = 1, "cheese" = 1, "pepperoni" = 2, "9 millimeter bullets" = 2)
+
+/obj/item/reagent_containers/food/snacks/proc/try_break_off(mob/living/M, mob/living/user) //maybe i give you a pizza maybe i break off your arm
+ var/obj/item/bodypart/l_arm = user.get_bodypart(BODY_ZONE_L_ARM)
+ var/obj/item/bodypart/r_arm = user.get_bodypart(BODY_ZONE_R_ARM)
+ if(prob(50) && iscarbon(user) && M == user && (r_arm || l_arm))
+ user.visible_message("\The [src] breaks off [user]'s arm!! ", "\The [src] breaks off your arm! ")
+ if(l_arm)
+ l_arm.dismember()
+ else
+ r_arm.dismember()
+ playsound(user,pick('sound/misc/desceration-01.ogg','sound/misc/desceration-02.ogg','sound/misc/desceration-01.ogg') ,50, TRUE, -1)
+
+/obj/item/reagent_containers/food/snacks/proc/i_kill_you(obj/item/I, mob/user)
+ if(istype(I, /obj/item/reagent_containers/food/snacks/pineappleslice))
+ to_chat(user, "If you want something crazy like pineapple, I kill you. ")
+ user.gib() //if you want something crazy like pineapple, i kill you
+
+/obj/item/reagent_containers/food/snacks/pizza/arnold/attack(mob/living/M, mob/living/user)
+ . = ..()
+ try_break_off(M, user)
+
+/obj/item/reagent_containers/food/snacks/pizza/arnold/attackby(obj/item/I, mob/user)
+ i_kill_you(I, user)
+ . = ..()
+
+
+/obj/item/reagent_containers/food/snacks/pizzaslice/arnold
+ name = "\improper Arnold pizza slice"
+ desc = "I come over, maybe I give you a pizza, maybe I break off your arm."
+ icon_state = "arnoldpizzaslice"
+ filling_color = "#A52A2A"
+ tastes = list("crust" = 1, "tomato" = 1, "cheese" = 1, "pineapple" = 2, "ham" = 2)
+ foodtype = GRAIN | VEGETABLES | DAIRY | MEAT
+
+/obj/item/reagent_containers/food/snacks/pizzaslice/arnold/attack(mob/living/M, mob/living/user)
+ . =..()
+ try_break_off(M, user)
+
+/obj/item/reagent_containers/food/snacks/pizzaslice/arnold/attackby(obj/item/I, mob/user)
+ i_kill_you(I, user)
+ . = ..()
+
+
/obj/item/reagent_containers/food/snacks/pizzaslice/custom
name = "pizza slice"
icon_state = "pizzamargheritaslice"
filling_color = "#FFFFFF"
foodtype = GRAIN | VEGETABLES
+
diff --git a/code/modules/food_and_drinks/kitchen_machinery/gibber.dm b/code/modules/food_and_drinks/kitchen_machinery/gibber.dm
index e6734b4b58..d8d4e843dc 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/gibber.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/gibber.dm
@@ -98,7 +98,7 @@
/obj/machinery/gibber/attackby(obj/item/P, mob/user, params)
if(default_deconstruction_screwdriver(user, "grinder_open", "grinder", P))
return
-
+
else if(default_pry_open(P))
return
@@ -184,7 +184,7 @@
if(typeofskin)
skin = new typeofskin
- add_logs(user, occupant, "gibbed")
+ log_combat(user, occupant, "gibbed")
mob_occupant.death(1)
mob_occupant.ghostize()
qdel(src.occupant)
@@ -215,7 +215,7 @@
/obj/machinery/gibber/autogibber
var/input_dir = NORTH
-/obj/machinery/gibber/autogibber/CollidedWith(atom/movable/AM)
+/obj/machinery/gibber/autogibber/Bumped(atom/movable/AM)
var/atom/input = get_step(src, input_dir)
if(ismob(AM))
var/mob/M = AM
diff --git a/code/modules/food_and_drinks/kitchen_machinery/microwave.dm b/code/modules/food_and_drinks/kitchen_machinery/microwave.dm
index 8c4e0eaae0..3bcc8d7659 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/microwave.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/microwave.dm
@@ -9,6 +9,7 @@
idle_power_usage = 5
active_power_usage = 100
circuit = /obj/item/circuitboard/machine/microwave
+ pass_flags = PASSTABLE
var/operating = FALSE // Is it on?
var/dirty = 0 // = {0..100} Does it need cleaning?
var/broken = 0 // ={0,1,2} How broken is it???
@@ -37,6 +38,11 @@
efficiency = E
max_n_of_items = max_items
+/obj/machinery/microwave/examine(mob/user)
+ ..()
+ if(!operating)
+ to_chat(user, "Alt-click [src] to turn it on. ")
+
/*******************
* Item Adding
********************/
diff --git a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm
index fa4168c399..b6e3b19640 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm
@@ -13,8 +13,7 @@
active_power_usage = 100
circuit = /obj/item/circuitboard/machine/smartfridge
var/max_n_of_items = 1500
- var/icon_on = "smartfridge"
- var/icon_off = "smartfridge-off"
+ var/allow_ai_retrieve = FALSE
var/list/initial_contents
/obj/machinery/smartfridge/Initialize()
@@ -39,10 +38,11 @@
update_icon()
/obj/machinery/smartfridge/update_icon()
+ var/startstate = initial(icon_state)
if(!stat)
- icon_state = icon_on
+ icon_state = startstate
else
- icon_state = icon_off
+ icon_state = "[startstate]-off"
@@ -168,6 +168,10 @@
if("Release")
var/desired = 0
+ if(!allow_ai_retrieve && isAI(usr))
+ to_chat(usr, "[src] does not seem to be configured to respect your authority! ")
+ return
+
if (params["amount"])
desired = text2num(params["amount"])
else
@@ -203,12 +207,10 @@
name = "drying rack"
desc = "A wooden contraption, used to dry plant products, food and leather."
icon = 'icons/obj/hydroponics/equipment.dmi'
- icon_state = "drying_rack_on"
+ icon_state = "drying_rack"
use_power = IDLE_POWER_USE
idle_power_usage = 5
active_power_usage = 200
- icon_on = "drying_rack_on"
- icon_off = "drying_rack"
var/drying = FALSE
/obj/machinery/smartfridge/drying_rack/Initialize()
@@ -413,6 +415,7 @@
name = "disk compartmentalizer"
desc = "A machine capable of storing a variety of disks. Denoted by most as the DSU (disk storage unit)."
icon_state = "disktoaster"
+ pass_flags = PASSTABLE
/obj/machinery/smartfridge/disks/accept_check(obj/item/O)
if(istype(O, /obj/item/disk/))
diff --git a/code/modules/food_and_drinks/pizzabox.dm b/code/modules/food_and_drinks/pizzabox.dm
index 34868a5000..b06a160180 100644
--- a/code/modules/food_and_drinks/pizzabox.dm
+++ b/code/modules/food_and_drinks/pizzabox.dm
@@ -269,9 +269,14 @@
/obj/item/pizzabox/margherita/Initialize()
. = ..()
- pizza = new /obj/item/reagent_containers/food/snacks/pizza/margherita(src)
+ AddPizza()
boxtag = "Margherita Deluxe"
+/obj/item/pizzabox/margherita/proc/AddPizza()
+ pizza = new /obj/item/reagent_containers/food/snacks/pizza/margherita(src)
+
+/obj/item/pizzabox/margherita/robo/AddPizza()
+ pizza = new /obj/item/reagent_containers/food/snacks/pizza/margherita/robo(src)
/obj/item/pizzabox/vegetable/Initialize()
. = ..()
diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_misc.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_misc.dm
index 403fd9c8da..3925416560 100644
--- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_misc.dm
+++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_misc.dm
@@ -13,14 +13,14 @@
subcategory = CAT_MISCFOOD
/datum/crafting_recipe/food/spiderlollipop
- name = "Spider Lollipop"
- reqs = list(/obj/item/stack/rods = 1,
- /datum/reagent/consumable/sugar = 5,
- /datum/reagent/water = 5,
- /obj/item/reagent_containers/food/snacks/spiderling = 1
- )
- result = /obj/item/reagent_containers/food/snacks/spiderlollipop
- subcategory = CAT_MISCFOOD
+ name = "Spider Lollipop"
+ reqs = list(/obj/item/stack/rods = 1,
+ /datum/reagent/consumable/sugar = 5,
+ /datum/reagent/water = 5,
+ /obj/item/reagent_containers/food/snacks/spiderling = 1
+ )
+ result = /obj/item/reagent_containers/food/snacks/spiderlollipop
+ subcategory = CAT_MISCFOOD
/datum/crafting_recipe/food/chococoin
name = "Choco coin"
diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm
index a75510ae9a..e751bf887e 100644
--- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm
+++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm
@@ -22,6 +22,16 @@
)
result = /obj/item/reagent_containers/food/snacks/donut
subcategory = CAT_PASTRY
+
+datum/crafting_recipe/food/donut/meat
+ time = 15
+ name = "Meat donut"
+ reqs = list(
+ /obj/item/reagent_containers/food/snacks/meat/slab = 1,
+ /obj/item/reagent_containers/food/snacks/pastrybase = 1
+ )
+ result = /obj/item/reagent_containers/food/snacks/donut/meat
+ subcategory = CAT_PASTRY
/datum/crafting_recipe/food/jellydonut
name = "Jelly donut"
diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_pizza.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_pizza.dm
index 27654a0702..70f4b50727 100644
--- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_pizza.dm
+++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_pizza.dm
@@ -24,6 +24,18 @@
result = /obj/item/reagent_containers/food/snacks/pizza/meat
subcategory = CAT_PIZZA
+/datum/crafting_recipe/food/arnold
+ name = "Arnold pizza"
+ reqs = list(
+ /obj/item/reagent_containers/food/snacks/pizzabread = 1,
+ /obj/item/reagent_containers/food/snacks/meat/cutlet = 3,
+ /obj/item/ammo_casing/c9mm = 8,
+ /obj/item/reagent_containers/food/snacks/cheesewedge = 1,
+ /obj/item/reagent_containers/food/snacks/grown/tomato = 1
+ )
+ result = /obj/item/reagent_containers/food/snacks/pizza/arnold
+ subcategory = CAT_PIZZA
+
/datum/crafting_recipe/food/mushroompizza
name = "Mushroom pizza"
reqs = list(
@@ -45,7 +57,7 @@
result = /obj/item/reagent_containers/food/snacks/pizza/vegetable
subcategory = CAT_PIZZA
-/datum/crafting_recipe/food/donpocketpizza
+/datum/crafting_recipe/food/donkpocketpizza
name = "Donkpocket pizza"
reqs = list(
/obj/item/reagent_containers/food/snacks/pizzabread = 1,
diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_soup.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_soup.dm
index 267f5f8427..76a5a64096 100644
--- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_soup.dm
+++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_soup.dm
@@ -33,7 +33,7 @@
reqs = list(
/datum/reagent/water = 10,
/obj/item/reagent_containers/glass/bowl = 1,
- /obj/item/grown/nettle = 1,
+ /obj/item/reagent_containers/food/snacks/grown/nettle = 1,
/obj/item/reagent_containers/food/snacks/grown/potato = 1,
/obj/item/reagent_containers/food/snacks/boiledegg = 1
)
diff --git a/code/modules/goonchat/browserOutput.dm b/code/modules/goonchat/browserOutput.dm
index 55d5a806bd..ba50a8396e 100644
--- a/code/modules/goonchat/browserOutput.dm
+++ b/code/modules/goonchat/browserOutput.dm
@@ -175,29 +175,23 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("data/iconCache.sav")) //Cache of ic
log_world("\[[time2text(world.realtime, "YYYY-MM-DD hh:mm:ss")]\] Client: [(src.owner.key ? src.owner.key : src.owner)] triggered JS error: [error]")
//Global chat procs
-
/proc/to_chat(target, message, handle_whitespace=TRUE)
if(!target)
return
//Ok so I did my best but I accept that some calls to this will be for shit like sound and images
//It stands that we PROBABLY don't want to output those to the browser output so just handle them here
- if (istype(message, /image) || istype(message, /sound) || istype(target, /savefile))
+ if (istype(target, /savefile))
CRASH("Invalid message! [message]")
if(!istext(message))
+ if (istype(message, /image) || istype(message, /sound))
+ CRASH("Invalid message! [message]")
return
if(target == world)
target = GLOB.clients
- var/list/targets
- if(!islist(target))
- targets = list(target)
- else
- targets = target
- if(!targets.len)
- return
var/original_message = message
//Some macros remain in the string even after parsing and fuck up the eventual output
message = replacetext(message, "\improper", "")
@@ -206,34 +200,43 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("data/iconCache.sav")) //Cache of ic
message = replacetext(message, "\n", " ")
message = replacetext(message, "\t", "[GLOB.TAB][GLOB.TAB]")
- for(var/I in targets)
- //Grab us a client if possible
- var/client/C
- if (ismob(I))
- var/mob/M = I
- if(M.client)
- C = M.client
- else if(istype(I, /client))
- C = I
- else if(istype(I, /datum/mind))
- var/datum/mind/M = I
- if(M.current && M.current.client)
- C = M.current.client
-
+ if(islist(target))
+ // Do the double-encoding outside the loop to save nanoseconds
+ var/twiceEncoded = url_encode(url_encode(message))
+ for(var/I in target)
+ var/client/C = CLIENT_FROM_VAR(I) //Grab us a client if possible
+
+ if (!C)
+ continue
+
+ //Send it to the old style output window.
+ SEND_TEXT(C, original_message)
+
+ if(!C.chatOutput || C.chatOutput.broken) // A player who hasn't updated his skin file.
+ continue
+
+ if(!C.chatOutput.loaded)
+ //Client still loading, put their messages in a queue
+ C.chatOutput.messageQueue += message
+ continue
+
+ C << output(twiceEncoded, "browseroutput:output")
+ else
+ var/client/C = CLIENT_FROM_VAR(target) //Grab us a client if possible
if (!C)
- continue
+ return
//Send it to the old style output window.
SEND_TEXT(C, original_message)
if(!C.chatOutput || C.chatOutput.broken) // A player who hasn't updated his skin file.
- continue
+ return
if(!C.chatOutput.loaded)
//Client still loading, put their messages in a queue
C.chatOutput.messageQueue += message
- continue
+ return
// url_encode it TWICE, this way any UTF-8 characters are able to be decoded by the Javascript.
C << output(url_encode(url_encode(message)), "browseroutput:output")
diff --git a/code/modules/goonchat/browserassets/css/browserOutput.css b/code/modules/goonchat/browserassets/css/browserOutput.css
index 778e16a831..fe90e8a333 100644
--- a/code/modules/goonchat/browserassets/css/browserOutput.css
+++ b/code/modules/goonchat/browserassets/css/browserOutput.css
@@ -10,7 +10,7 @@ html, body {
color: #000000;
}
body {
- background: #E0E0E0; /*CIT CHANGE - darkens chatbox a lil*/
+ background: #fff;
font-family: Verdana, sans-serif;
font-size: 9pt;
line-height: 1.2;
@@ -264,7 +264,7 @@ em {font-style: normal; font-weight: bold;}
.adminobserverooc {color: #0099cc; font-weight: bold;}
.adminooc {color: #700038; font-weight: bold;}
-.adminobserver {color: #996600; font-weight: bold;}
+.adminsay {color: #FF4500; font-weight: bold;}
.admin {color: #386aff; font-weight: bold;}
.name { font-weight: bold;}
diff --git a/code/modules/holiday/holidays.dm b/code/modules/holiday/holidays.dm
index 701167c853..7d1e25235d 100644
--- a/code/modules/holiday/holidays.dm
+++ b/code/modules/holiday/holidays.dm
@@ -81,6 +81,9 @@
begin_month = FEBRUARY
drone_hat = /obj/item/clothing/head/helmet/space/chronos
+/datum/holiday/groundhog/getStationPrefix()
+ return pick("Deja Vu") //I have been to this place before
+
/datum/holiday/valentines
name = VALENTINES
begin_day = 13
@@ -149,6 +152,9 @@
/datum/holiday/no_this_is_patrick/getStationPrefix()
return pick("Blarney","Green","Leprechaun","Booze")
+/datum/holiday/no_this_is_patrick/greet()
+ return "Happy National Inebriation Day!"
+
/datum/holiday/april_fools
name = APRIL_FOOLS
begin_day = 1
@@ -168,7 +174,15 @@
begin_month = APRIL
/datum/holiday/fourtwenty/getStationPrefix()
- return pick("Snoop","Blunt","Toke","Dank")
+ return pick("Snoop","Blunt","Toke","Dank","Cheech","Chong")
+
+/datum/holiday/tea
+ name = "National Tea Day"
+ begin_day = 21
+ begin_month = APRIL
+
+/datum/holiday/tea/getStationPrefix()
+ return pick("Crumpet","Assam","Oolong","Pu-erh","Sweet Tea","Green","Black")
/datum/holiday/earth
name = "Earth Day"
@@ -190,6 +204,14 @@
/datum/holiday/firefighter/getStationPrefix()
return pick("Burning","Blazing","Plasma","Fire")
+/datum/holiday/bee
+ name = "Bee Day"
+ begin_day = 20
+ begin_month = MAY
+
+/datum/holiday/bee/getStationPrefix()
+ return pick("Bee","Honey","Hive","Africanized","Mead","Buzz")
+
/datum/holiday/summersolstice
name = "Summer Solstice"
begin_day = 21
@@ -205,10 +227,18 @@
name = "UFO Day"
begin_day = 2
begin_month = JULY
- drone_hat = /obj/item/clothing/mask/facehugger/dead
+ drone_hat = /obj/item/clothing/mask/facehugger/dead
/datum/holiday/UFO/getStationPrefix() //Is such a thing even possible?
- return pick("Ayy","Truth","Tsoukalos","Mulder") //Yes it is!
+ return pick("Ayy","Truth","Tsoukalos","Mulder","Scully") //Yes it is!
+
+/datum/holiday/USA
+ name = "Independence Day"
+ begin_day = 4
+ begin_month = JULY
+
+/datum/holiday/USA/getStationPrefix()
+ return pick("Independant","American","Burger","Bald Eagle","Star-Spangled")
/datum/holiday/writer
name = "Writer's Day"
@@ -225,8 +255,14 @@
/datum/holiday/beer
name = "Beer Day"
- begin_day = 5
- begin_month = AUGUST
+
+/datum/holiday/beer/shouldCelebrate(dd, mm, yy, ww, ddd)
+ if(mm == 8 && ddd == FRIDAY && ww == 1) //First Friday in August
+ return TRUE
+ return FALSE
+
+/datum/holiday/beer/getStationPrefix()
+ return pick("Stout","Porter","Lager","Ale","Malt","Bock","Doppelbock","Hefeweizen","Pilsner","IPA","Lite") //I'm sorry for the last one
/datum/holiday/pirate
name = "Talk-Like-a-Pirate Day"
@@ -366,6 +402,15 @@
begin_month = JUNE
begin_weekday = SUNDAY
+/datum/holiday/moth
+ name = "Moth Week"
+
+/datum/holiday/moth/shouldCelebrate(dd, mm, yy, ww, ddd) //National Moth Week falls on the last full week of July
+ return mm == JULY && (ww == 4 || (ww == 5 && ddd == SUNDAY))
+
+/datum/holiday/moth/getStationPrefix()
+ return pick("Mothball","Lepidopteran","Lightbulb","Moth","Giant Atlas","Twin-spotted Sphynx","Madagascan Sunset","Luna","Death's Head","Emperor Gum","Polyphenus","Oleander Hawk","Io","Rosy Maple","Cecropia","Noctuidae","Giant Leopard","Dysphania Militaris","Garden Tiger")
+
/datum/holiday/ramadan
name = "Start of Ramadan"
diff --git a/code/modules/holodeck/computer.dm b/code/modules/holodeck/computer.dm
index 4590d58174..4ffc10f9c2 100644
--- a/code/modules/holodeck/computer.dm
+++ b/code/modules/holodeck/computer.dm
@@ -177,7 +177,7 @@
/obj/machinery/computer/holodeck/proc/generate_program_list()
for(var/typekey in subtypesof(program_type))
- var/area/holodeck/A = locate(typekey) in GLOB.sortedAreas
+ var/area/holodeck/A = GLOB.areas_by_type[typekey]
if(!A || !A.contents.len)
continue
var/list/info_this = list()
diff --git a/code/modules/holodeck/holo_effect.dm b/code/modules/holodeck/holo_effect.dm
index 4134b0516f..fee0b2b7c9 100644
--- a/code/modules/holodeck/holo_effect.dm
+++ b/code/modules/holodeck/holo_effect.dm
@@ -76,7 +76,7 @@
mob = new mobtype(loc)
// these vars are not really standardized but all would theoretically create stuff on death
- for(var/v in list("butcher_results","corpse","weapon1","weapon2","blood_volume","vore_organs") & mob.vars) //CITADEL EDIT, maybe stops ghost bellies
+ for(var/v in list("butcher_results","corpse","weapon1","weapon2","blood_volume") & mob.vars)
mob.vars[v] = null
return mob
diff --git a/code/modules/holodeck/turfs.dm b/code/modules/holodeck/turfs.dm
index c2c17417fd..50496e5518 100644
--- a/code/modules/holodeck/turfs.dm
+++ b/code/modules/holodeck/turfs.dm
@@ -28,6 +28,7 @@
name = "lush grass"
icon_state = "grass"
bullet_bounce_sound = null
+ tiled_dirt = FALSE
/turf/open/floor/holofloor/beach
gender = PLURAL
@@ -35,6 +36,7 @@
icon = 'icons/misc/beach.dmi'
icon_state = "sand"
bullet_bounce_sound = null
+ tiled_dirt = FALSE
/turf/open/floor/holofloor/beach/coast_t
gender = NEUTER
@@ -54,6 +56,7 @@
/turf/open/floor/holofloor/asteroid
name = "asteroid"
icon_state = "asteroid0"
+ tiled_dirt = FALSE
/turf/open/floor/holofloor/asteroid/Initialize()
icon_state = "asteroid[rand(0, 12)]"
@@ -63,6 +66,7 @@
gender = PLURAL
name = "basalt"
icon_state = "basalt0"
+ tiled_dirt = FALSE
/turf/open/floor/holofloor/basalt/Initialize()
. = ..()
@@ -84,6 +88,7 @@
icon = 'icons/turf/space.dmi'
icon_state = "speedspace_ns_1"
bullet_bounce_sound = null
+ tiled_dirt = FALSE
/turf/open/floor/holofloor/hyperspace/Initialize()
icon_state = "speedspace_ns_[(x + 5*y + (y%2+1)*7)%15+1]"
@@ -102,6 +107,7 @@
smooth = SMOOTH_TRUE
canSmoothWith = null
bullet_bounce_sound = null
+ tiled_dirt = FALSE
/turf/open/floor/holofloor/carpet/Initialize()
. = ..()
@@ -122,6 +128,7 @@
slowdown = 2
bullet_sizzle = TRUE
bullet_bounce_sound = null
+ tiled_dirt = FALSE
/turf/open/floor/holofloor/snow/cold
initial_gas_mix = "nob=7500;TEMP=2.7"
@@ -131,3 +138,4 @@
name = "asteroid sand"
icon = 'icons/turf/floors.dmi'
icon_state = "asteroid"
+ tiled_dirt = FALSE
diff --git a/code/modules/hydroponics/fermenting_barrel.dm b/code/modules/hydroponics/fermenting_barrel.dm
new file mode 100644
index 0000000000..b88e6e1ebb
--- /dev/null
+++ b/code/modules/hydroponics/fermenting_barrel.dm
@@ -0,0 +1,77 @@
+/obj/structure/fermenting_barrel
+ name = "wooden barrel"
+ desc = "A large wooden barrel. You can ferment fruits and such inside it, or just use it to hold liquid."
+ icon = 'icons/obj/objects.dmi'
+ icon_state = "barrel"
+ density = TRUE
+ anchored = FALSE
+ container_type = DRAINABLE | AMOUNT_VISIBLE
+ pressure_resistance = 2 * ONE_ATMOSPHERE
+ max_integrity = 300
+ var/open = FALSE
+ var/speed_multiplier = 1 //How fast it distills. Defaults to 100% (1.0). Lower is better.
+
+/obj/structure/fermenting_barrel/Initialize()
+ create_reagents(300) //Bluespace beakers, but without the portability or efficiency in circuits.
+ . = ..()
+
+/obj/structure/fermenting_barrel/examine(mob/user)
+ . = ..()
+ to_chat(user, "It is currently [open?"open, letting you pour liquids in.":"closed, letting you draw liquids from the tap."] ")
+
+/obj/structure/fermenting_barrel/proc/makeWine(obj/item/reagent_containers/food/snacks/grown/fruit)
+ if(fruit.reagents)
+ fruit.reagents.trans_to(src, fruit.reagents.total_volume)
+ var/amount = fruit.seed.potency / 4
+ if(fruit.distill_reagent)
+ reagents.add_reagent(fruit.distill_reagent, amount)
+ else
+ var/data = list()
+ data["names"] = list("[initial(fruit.name)]" = 1)
+ data["color"] = fruit.filling_color
+ data["boozepwr"] = fruit.wine_power
+ if(fruit.wine_flavor)
+ data["tastes"] = list(fruit.wine_flavor = 1)
+ else
+ data["tastes"] = list(fruit.tastes[1] = 1)
+ reagents.add_reagent("fruit_wine", amount, data)
+ qdel(fruit)
+ playsound(src, 'sound/effects/bubbles.ogg', 50, TRUE)
+
+/obj/structure/fermenting_barrel/attackby(obj/item/I, mob/user, params)
+ var/obj/item/reagent_containers/food/snacks/grown/fruit = I
+ if(istype(fruit))
+ if(!fruit.can_distill)
+ to_chat(user, "You can't distill this into anything... ")
+ return TRUE
+ else if(!user.transferItemToLoc(I,src))
+ to_chat(user, "[I] is stuck to your hand! ")
+ return TRUE
+ to_chat(user, "You place [I] into [src] to start the fermentation process. ")
+ addtimer(CALLBACK(src, .proc/makeWine, fruit), rand(80, 120) * speed_multiplier)
+ return TRUE
+ else
+ return ..()
+
+/obj/structure/fermenting_barrel/attack_hand(mob/user)
+ open = !open
+ if(open)
+ container_type = REFILLABLE | AMOUNT_VISIBLE
+ to_chat(user, "You open [src], letting you fill it. ")
+ else
+ container_type = DRAINABLE | AMOUNT_VISIBLE
+ to_chat(user, "You close [src], letting you draw from its tap. ")
+ update_icon()
+
+/obj/structure/fermenting_barrel/update_icon()
+ if(open)
+ icon_state = "barrel_open"
+ else
+ icon_state = "barrel"
+
+/datum/crafting_recipe/fermenting_barrel
+ name = "Wooden Barrel"
+ result = /obj/structure/fermenting_barrel
+ reqs = list(/obj/item/stack/sheet/mineral/wood = 30)
+ time = 50
+ category = CAT_PRIMAL
diff --git a/code/modules/hydroponics/gene_modder.dm b/code/modules/hydroponics/gene_modder.dm
index 7e541f2471..d6eacfe0e1 100644
--- a/code/modules/hydroponics/gene_modder.dm
+++ b/code/modules/hydroponics/gene_modder.dm
@@ -5,6 +5,7 @@
icon_state = "dnamod"
density = TRUE
circuit = /obj/item/circuitboard/machine/plantgenes
+ pass_flags = PASSTABLE
var/obj/item/seeds/seed
var/obj/item/disk/plantgene/disk
diff --git a/code/modules/hydroponics/grown.dm b/code/modules/hydroponics/grown.dm
index 5b9c1f4e70..096c7b5b2b 100644
--- a/code/modules/hydroponics/grown.dm
+++ b/code/modules/hydroponics/grown.dm
@@ -16,6 +16,10 @@
// If you don't want a plant to be driable (watermelons) set this to null in the time definition.
resistance_flags = FLAMMABLE
var/dry_grind = FALSE //If TRUE, this object needs to be dry to be ground up
+ var/can_distill = TRUE //If FALSE, this object cannot be distilled into an alcohol.
+ var/distill_reagent //If NULL and this object can be distilled, it uses a generic fruit_wine reagent and adjusts its variables.
+ var/wine_flavor //If NULL, this is automatically set to the fruit's flavor. Determines the flavor of the wine if distill_reagent is NULL.
+ var/wine_power = 10 //Determines the boozepwr of the wine if distill_reagent is NULL.
/obj/item/reagent_containers/food/snacks/grown/Initialize(mapload, obj/item/seeds/new_seed)
. = ..()
diff --git a/code/modules/hydroponics/grown/ambrosia.dm b/code/modules/hydroponics/grown/ambrosia.dm
index 5883b4dd64..a3b93db1ea 100644
--- a/code/modules/hydroponics/grown/ambrosia.dm
+++ b/code/modules/hydroponics/grown/ambrosia.dm
@@ -31,6 +31,7 @@
seed = /obj/item/seeds/ambrosia
name = "ambrosia vulgaris branch"
desc = "This is a plant containing various healing chemicals."
+ wine_power = 30
// Ambrosia Deus
/obj/item/seeds/ambrosia/deus
@@ -50,6 +51,7 @@
desc = "Eating this makes you feel immortal!"
icon_state = "ambrosiadeus"
filling_color = "#008B8B"
+ wine_power = 50
//Ambrosia Gaia
/obj/item/seeds/ambrosia/gaia
@@ -73,3 +75,5 @@
filling_color = rgb(255, 175, 0)
light_range = 3
seed = /obj/item/seeds/ambrosia/gaia
+ wine_power = 70
+ wine_flavor = "the earthmother's blessing"
diff --git a/code/modules/hydroponics/grown/apple.dm b/code/modules/hydroponics/grown/apple.dm
index 47d288b669..007c0b2454 100644
--- a/code/modules/hydroponics/grown/apple.dm
+++ b/code/modules/hydroponics/grown/apple.dm
@@ -26,6 +26,7 @@
foodtype = FRUIT
juice_results = list("applejuice" = 0)
tastes = list("apple" = 1)
+ distill_reagent = "hcider"
// Gold Apple
/obj/item/seeds/apple/gold
@@ -47,3 +48,5 @@
desc = "Emblazoned upon the apple is the word 'Kallisti'."
icon_state = "goldapple"
filling_color = "#FFD700"
+ distill_reagent = null
+ wine_power = 50
diff --git a/code/modules/hydroponics/grown/banana.dm b/code/modules/hydroponics/grown/banana.dm
index 4e29094df4..251d9de5c0 100644
--- a/code/modules/hydroponics/grown/banana.dm
+++ b/code/modules/hydroponics/grown/banana.dm
@@ -25,6 +25,7 @@
bitesize = 5
foodtype = FRUIT
juice_results = list("banana" = 0)
+ distill_reagent = "bananahonk"
/obj/item/reagent_containers/food/snacks/grown/banana/suicide_act(mob/user)
user.visible_message("[user] is aiming [src] at [user.p_them()]self! It looks like [user.p_theyre()] trying to commit suicide! ")
@@ -32,7 +33,7 @@
sleep(25)
if(!user)
return (OXYLOSS)
- user.say("BANG!")
+ user.say("BANG!", forced = "banana")
sleep(25)
if(!user)
return (OXYLOSS)
@@ -79,6 +80,7 @@
icon_state = "mimana"
trash = /obj/item/grown/bananapeel/mimanapeel
filling_color = "#FFFFEE"
+ distill_reagent = "silencer"
/obj/item/grown/bananapeel/mimanapeel
seed = /obj/item/seeds/banana/mime
@@ -109,6 +111,8 @@
trash = /obj/item/grown/bananapeel/bluespace
filling_color = "#0000FF"
tastes = list("banana" = 1)
+ wine_power = 60
+ wine_flavor = "slippery hypercubes"
/obj/item/grown/bananapeel/bluespace
seed = /obj/item/seeds/banana/bluespace
diff --git a/code/modules/hydroponics/grown/beans.dm b/code/modules/hydroponics/grown/beans.dm
index b9363d5a12..4338e3b070 100644
--- a/code/modules/hydroponics/grown/beans.dm
+++ b/code/modules/hydroponics/grown/beans.dm
@@ -28,6 +28,7 @@
foodtype = VEGETABLES
grind_results = list("soymilk" = 0)
tastes = list("soy" = 1)
+ wine_power = 20
// Koibean
/obj/item/seeds/soya/koi
@@ -51,3 +52,4 @@
bitesize_mod = 2
foodtype = VEGETABLES
tastes = list("koi" = 1)
+ wine_power = 40
diff --git a/code/modules/hydroponics/grown/berries.dm b/code/modules/hydroponics/grown/berries.dm
index e8e6c52b7a..19abdacf3a 100644
--- a/code/modules/hydroponics/grown/berries.dm
+++ b/code/modules/hydroponics/grown/berries.dm
@@ -28,6 +28,7 @@
foodtype = FRUIT
juice_results = list("berryjuice" = 0)
tastes = list("berry" = 1)
+ distill_reagent = "gin"
// Poison Berries
/obj/item/seeds/berry/poison
@@ -50,6 +51,8 @@
foodtype = FRUIT | TOXIC
juice_results = list("poisonberryjuice" = 0)
tastes = list("poison-berry" = 1)
+ distill_reagent = null
+ wine_power = 35
// Death Berries
/obj/item/seeds/berry/death
@@ -73,6 +76,8 @@
filling_color = "#708090"
foodtype = FRUIT | TOXIC
tastes = list("death-berry" = 1)
+ distill_reagent = null
+ wine_power = 50
// Glow Berries
/obj/item/seeds/berry/glow
@@ -97,6 +102,8 @@
filling_color = "#7CFC00"
foodtype = FRUIT
tastes = list("glow-berry" = 1)
+ distill_reagent = null
+ wine_power = 60
// Cherries
/obj/item/seeds/cherry
@@ -129,6 +136,7 @@
foodtype = FRUIT
grind_results = list("cherryjelly" = 0)
tastes = list("cherry" = 1)
+ wine_power = 30
// Blue Cherries
/obj/item/seeds/cherry/blue
@@ -152,6 +160,7 @@
foodtype = FRUIT
grind_results = list("bluecherryjelly" = 0)
tastes = list("blue cherry" = 1)
+ wine_power = 50
// Grapes
/obj/item/seeds/grape
@@ -185,6 +194,7 @@
foodtype = FRUIT
juice_results = list("grapejuice" = 0)
tastes = list("grape" = 1)
+ distill_reagent = "wine"
// Green Grapes
/obj/item/seeds/grape/green
@@ -204,3 +214,4 @@
icon_state = "greengrapes"
filling_color = "#7FFF00"
tastes = list("green grape" = 1)
+ distill_reagent = "cognac"
diff --git a/code/modules/hydroponics/grown/cannabis.dm b/code/modules/hydroponics/grown/cannabis.dm
index c5bf77f6f9..67c5e61dcf 100644
--- a/code/modules/hydroponics/grown/cannabis.dm
+++ b/code/modules/hydroponics/grown/cannabis.dm
@@ -92,25 +92,28 @@
bitesize_mod = 2
foodtype = VEGETABLES //i dont really know what else weed could be to be honest
tastes = list("cannabis" = 1)
-
+ wine_power = 20
/obj/item/reagent_containers/food/snacks/grown/cannabis/rainbow
seed = /obj/item/seeds/cannabis/rainbow
name = "rainbow cannabis leaf"
desc = "Is it supposed to be glowing like that...?"
icon_state = "megacannabis"
+ wine_power = 60
/obj/item/reagent_containers/food/snacks/grown/cannabis/death
seed = /obj/item/seeds/cannabis/death
name = "death cannabis leaf"
desc = "Looks a bit dark. Oh well."
icon_state = "blackcannabis"
+ wine_power = 40
/obj/item/reagent_containers/food/snacks/grown/cannabis/white
seed = /obj/item/seeds/cannabis/white
name = "white cannabis leaf"
desc = "It feels smooth and nice to the touch."
icon_state = "whitecannabis"
+ wine_power = 10
/obj/item/reagent_containers/food/snacks/grown/cannabis/ultimate
seed = /obj/item/seeds/cannabis/ultimate
@@ -118,3 +121,4 @@
desc = "You feel dizzy looking at it. What the fuck?"
icon_state = "ocannabis"
volume = 420
+ wine_power = 90
diff --git a/code/modules/hydroponics/grown/cereals.dm b/code/modules/hydroponics/grown/cereals.dm
index acd3203ecc..c53cd6718a 100644
--- a/code/modules/hydroponics/grown/cereals.dm
+++ b/code/modules/hydroponics/grown/cereals.dm
@@ -24,6 +24,7 @@
foodtype = GRAIN
grind_results = list("flour" = 0)
tastes = list("wheat" = 1)
+ distill_reagent = "beer"
// Oat
/obj/item/seeds/wheat/oat
@@ -46,6 +47,7 @@
foodtype = GRAIN
grind_results = list("flour" = 0)
tastes = list("oat" = 1)
+ distill_reagent = "ale"
// Rice
/obj/item/seeds/wheat/rice
@@ -69,6 +71,7 @@
foodtype = GRAIN
grind_results = list("rice" = 0)
tastes = list("rice" = 1)
+ distill_reagent = "sake"
//Meatwheat - grows into synthetic meat
/obj/item/seeds/wheat/meat
@@ -91,6 +94,7 @@
foodtype = MEAT | GRAIN
grind_results = list("flour" = 0, "blood" = 0)
tastes = list("meatwheat" = 1)
+ can_distill = FALSE
/obj/item/reagent_containers/food/snacks/grown/meatwheat/attack_self(mob/living/user)
user.visible_message("[user] crushes [src] into meat. ", "You crush [src] into something that resembles meat. ")
diff --git a/code/modules/hydroponics/grown/chili.dm b/code/modules/hydroponics/grown/chili.dm
index c01f3ba6ef..6325daacdc 100644
--- a/code/modules/hydroponics/grown/chili.dm
+++ b/code/modules/hydroponics/grown/chili.dm
@@ -26,6 +26,7 @@
filling_color = "#FF0000"
bitesize_mod = 2
foodtype = FRUIT
+ wine_power = 20
// Ice Chili
/obj/item/seeds/chili/ice
@@ -50,6 +51,7 @@
filling_color = "#0000CD"
bitesize_mod = 2
foodtype = FRUIT
+ wine_power = 30
// Ghost Chili
/obj/item/seeds/chili/ghost
@@ -76,6 +78,7 @@
filling_color = "#F8F8FF"
bitesize_mod = 4
foodtype = FRUIT
+ wine_power = 50
/obj/item/reagent_containers/food/snacks/grown/ghost_chili/attack_hand(mob/user)
. = ..()
diff --git a/code/modules/hydroponics/grown/citrus.dm b/code/modules/hydroponics/grown/citrus.dm
index 30b4865a9d..97b30aec06 100644
--- a/code/modules/hydroponics/grown/citrus.dm
+++ b/code/modules/hydroponics/grown/citrus.dm
@@ -6,6 +6,7 @@
icon_state = "lime"
bitesize_mod = 2
foodtype = FRUIT
+ wine_power = 30
// Lime
/obj/item/seeds/lime
@@ -58,6 +59,7 @@
icon_state = "orange"
filling_color = "#FFA500"
juice_results = list("orangejuice" = 0)
+ distill_reagent = "triple_sec"
// Lemon
/obj/item/seeds/lemon
@@ -109,6 +111,7 @@
icon_state = "firelemon"
bitesize_mod = 2
foodtype = FRUIT
+ wine_power = 70
/obj/item/reagent_containers/food/snacks/grown/firelemon/attack_self(mob/living/user)
user.visible_message("[user] primes [src]! ", "You prime [src]! ")
diff --git a/code/modules/hydroponics/grown/cocoa_vanilla.dm b/code/modules/hydroponics/grown/cocoa_vanilla.dm
index 12e235b1c3..ea1b9716bd 100644
--- a/code/modules/hydroponics/grown/cocoa_vanilla.dm
+++ b/code/modules/hydroponics/grown/cocoa_vanilla.dm
@@ -27,6 +27,7 @@
bitesize_mod = 2
foodtype = FRUIT
tastes = list("cocoa" = 1)
+ distill_reagent = "creme_de_cacao"
// Vanilla Pod
/obj/item/seeds/cocoapod/vanillapod
@@ -48,3 +49,4 @@
filling_color = "#FFD700"
foodtype = FRUIT
tastes = list("vanilla" = 1)
+ distill_reagent = "vanilla" //Takes longer, but you can get even more vanilla from it.
diff --git a/code/modules/hydroponics/grown/corn.dm b/code/modules/hydroponics/grown/corn.dm
index 312dda8c26..c259baaf2d 100644
--- a/code/modules/hydroponics/grown/corn.dm
+++ b/code/modules/hydroponics/grown/corn.dm
@@ -27,6 +27,7 @@
foodtype = VEGETABLES
juice_results = list("corn_starch" = 0)
tastes = list("corn" = 1)
+ distill_reagent = "whiskey"
/obj/item/grown/corncob
name = "corn cob"
diff --git a/code/modules/hydroponics/grown/eggplant.dm b/code/modules/hydroponics/grown/eggplant.dm
index 9615b7acb2..7c01a68f0a 100644
--- a/code/modules/hydroponics/grown/eggplant.dm
+++ b/code/modules/hydroponics/grown/eggplant.dm
@@ -23,12 +23,15 @@
filling_color = "#800080"
bitesize_mod = 2
foodtype = FRUIT
+ wine_power = 20
// Egg-Plant
/obj/item/seeds/eggplant/eggy
+ name = "pack of egg-plant seeds"
desc = "These seeds grow to produce berries that look a lot like eggs."
icon_state = "seed-eggy"
species = "eggy"
+ plantname = "Egg-Plants"
product = /obj/item/reagent_containers/food/snacks/grown/shell/eggy
lifespan = 75
production = 12
@@ -44,3 +47,4 @@
filling_color = "#F8F8FF"
bitesize_mod = 2
foodtype = MEAT
+ distill_reagent = "eggnog"
diff --git a/code/modules/hydroponics/grown/flowers.dm b/code/modules/hydroponics/grown/flowers.dm
index 62c3c0b064..4e1718f853 100644
--- a/code/modules/hydroponics/grown/flowers.dm
+++ b/code/modules/hydroponics/grown/flowers.dm
@@ -26,6 +26,7 @@
filling_color = "#FF6347"
bitesize_mod = 3
foodtype = VEGETABLES | GROSS
+ distill_reagent = "vermouth"
// Lily
/obj/item/seeds/poppy/lily
@@ -61,7 +62,6 @@
icon_state = "geranium"
filling_color = "#008B8B"
-
// Harebell
/obj/item/seeds/harebell
name = "pack of harebell seeds"
@@ -89,7 +89,7 @@
slot_flags = ITEM_SLOT_HEAD
filling_color = "#E6E6FA"
bitesize_mod = 3
-
+ distill_reagent = "vermouth"
// Sunflower
/obj/item/seeds/sunflower
@@ -152,6 +152,7 @@
slot_flags = ITEM_SLOT_HEAD
filling_color = "#E6E6FA"
bitesize_mod = 2
+ distill_reagent = "absinthe" //It's made from flowers.
// Novaflower
/obj/item/seeds/sunflower/novaflower
@@ -199,6 +200,7 @@
log_game("[key_name(user)] set [key_name(M)] on fire with [src] at [AREACOORD(user)]")
/obj/item/grown/novaflower/afterattack(atom/A as mob|obj, mob/user,proximity)
+ . = ..()
if(!proximity)
return
if(force > 0)
diff --git a/code/modules/hydroponics/grown/grass_carpet.dm b/code/modules/hydroponics/grown/grass_carpet.dm
index af466f5186..1a1c2ac07f 100644
--- a/code/modules/hydroponics/grown/grass_carpet.dm
+++ b/code/modules/hydroponics/grown/grass_carpet.dm
@@ -16,7 +16,7 @@
icon_dead = "grass-dead"
genes = list(/datum/plant_gene/trait/repeated_harvest)
mutatelist = list(/obj/item/seeds/grass/carpet)
- reagents_add = list("nutriment" = 0.02, "hydrogen" = 0.05, "oxygen" = 0.03) //CITADEL CHANGE - adds 0.03 oxygen to grass
+ reagents_add = list("nutriment" = 0.02, "hydrogen" = 0.05)
/obj/item/reagent_containers/food/snacks/grown/grass
seed = /obj/item/seeds/grass
@@ -27,6 +27,7 @@
bitesize_mod = 2
var/stacktype = /obj/item/stack/tile/grass
var/tile_coefficient = 0.02 // 1/50
+ wine_power = 15
/obj/item/reagent_containers/food/snacks/grown/grass/attack_self(mob/user)
to_chat(user, "You prepare the astroturf. ")
@@ -56,3 +57,4 @@
desc = "The textile industry's dark secret."
icon_state = "carpetclump"
stacktype = /obj/item/stack/tile/carpet
+ can_distill = FALSE
diff --git a/code/modules/hydroponics/grown/kudzu.dm b/code/modules/hydroponics/grown/kudzu.dm
index e9dec09799..6ceb69536d 100644
--- a/code/modules/hydroponics/grown/kudzu.dm
+++ b/code/modules/hydroponics/grown/kudzu.dm
@@ -104,3 +104,4 @@
bitesize_mod = 2
foodtype = VEGETABLES | GROSS
tastes = list("kudzu" = 1)
+ wine_power = 20
diff --git a/code/modules/hydroponics/grown/melon.dm b/code/modules/hydroponics/grown/melon.dm
index 7497a10ffb..29691c3289 100644
--- a/code/modules/hydroponics/grown/melon.dm
+++ b/code/modules/hydroponics/grown/melon.dm
@@ -34,6 +34,7 @@
bitesize_mod = 3
foodtype = FRUIT
juice_results = list("watermelonjuice" = 0)
+ wine_power = 40
// Holymelon
/obj/item/seeds/watermelon/holy
@@ -54,6 +55,8 @@
icon_state = "holymelon"
filling_color = "#FFD700"
dried_type = null
+ wine_power = 70 //Water to wine, baby.
+ wine_flavor = "divinity"
/obj/item/reagent_containers/food/snacks/grown/holymelon/Initialize()
. = ..()
diff --git a/code/modules/hydroponics/grown/misc.dm b/code/modules/hydroponics/grown/misc.dm
index 82ff56e7c9..107a6a94f9 100644
--- a/code/modules/hydroponics/grown/misc.dm
+++ b/code/modules/hydroponics/grown/misc.dm
@@ -55,7 +55,7 @@
filling_color = "#90EE90"
bitesize_mod = 2
foodtype = VEGETABLES
-
+ wine_power = 20
// Sugarcane
/obj/item/seeds/sugarcane
@@ -81,7 +81,7 @@
filling_color = "#FFD700"
bitesize_mod = 2
foodtype = VEGETABLES | SUGAR
-
+ distill_reagent = "rum"
// Gatfruit
/obj/item/seeds/gatfruit
@@ -112,6 +112,7 @@
bitesize_mod = 2
foodtype = FRUIT
tastes = list("gunpowder" = 1)
+ wine_power = 90 //It burns going down, too.
//Cherry Bombs
/obj/item/seeds/cherry/bomb
@@ -134,6 +135,7 @@
bitesize_mod = 2
volume = 125 //Gives enough room for the black powder at max potency
max_integrity = 40
+ wine_power = 80
/obj/item/reagent_containers/food/snacks/grown/cherry_bomb/attack_self(mob/living/user)
user.visible_message("[user] plucks the stem from [src]! ", "You pluck the stem from [src], which begins to hiss loudly! ")
diff --git a/code/modules/hydroponics/grown/mushrooms.dm b/code/modules/hydroponics/grown/mushrooms.dm
index fddcb5b1fc..fe2beb896c 100644
--- a/code/modules/hydroponics/grown/mushrooms.dm
+++ b/code/modules/hydroponics/grown/mushrooms.dm
@@ -2,7 +2,7 @@
name = "mushroom"
bitesize_mod = 2
foodtype = VEGETABLES
-
+ wine_power = 40
// Reishi
/obj/item/seeds/reishi
@@ -56,7 +56,6 @@
icon_state = "amanita"
filling_color = "#FF0000"
-
// Destroying Angel
/obj/item/seeds/angel
name = "pack of destroying angel mycelium"
@@ -83,7 +82,7 @@
desc = "Amanita Virosa : Deadly poisonous basidiomycete fungus filled with alpha amatoxins."
icon_state = "angel"
filling_color = "#C0C0C0"
-
+ wine_power = 60
// Liberty Cap
/obj/item/seeds/liberty
@@ -108,7 +107,7 @@
desc = "Psilocybe Semilanceata : Liberate yourself!"
icon_state = "libertycap"
filling_color = "#DAA520"
-
+ wine_power = 80
// Plump Helmet
/obj/item/seeds/plump
@@ -134,7 +133,7 @@
desc = "Plumus Hellmus : Plump, soft and s-so inviting~"
icon_state = "plumphelmet"
filling_color = "#9370DB"
-
+ distill_reagent = "manlydorf"
// Walking Mushroom
/obj/item/seeds/plump/walkingmushroom
@@ -159,6 +158,7 @@
desc = "Plumus Locomotus : The beginning of the great walk."
icon_state = "walkingmushroom"
filling_color = "#9370DB"
+ can_distill = FALSE
/obj/item/reagent_containers/food/snacks/grown/mushroom/walkingmushroom/attack_self(mob/user)
if(isspaceturf(user.loc))
@@ -228,6 +228,7 @@
icon_state = "glowshroom"
filling_color = "#00FA9A"
var/effect_path = /obj/structure/glowshroom
+ wine_power = 50
/obj/item/reagent_containers/food/snacks/grown/mushroom/glowshroom/attack_self(mob/user)
if(isspaceturf(user.loc))
@@ -298,6 +299,7 @@
icon_state = "shadowshroom"
effect_path = /obj/structure/glowshroom/shadowshroom
tastes = list("shadow" = 1, "mushroom" = 1)
+ wine_power = 60
/obj/item/reagent_containers/food/snacks/grown/mushroom/glowshroom/shadowshroom/attack_self(mob/user)
. = ..()
diff --git a/code/modules/hydroponics/grown/nettle.dm b/code/modules/hydroponics/grown/nettle.dm
index bb1a0d2f23..e3f8c254ac 100644
--- a/code/modules/hydroponics/grown/nettle.dm
+++ b/code/modules/hydroponics/grown/nettle.dm
@@ -4,7 +4,7 @@
icon_state = "seed-nettle"
species = "nettle"
plantname = "Nettles"
- product = /obj/item/grown/nettle/basic
+ product = /obj/item/reagent_containers/food/snacks/grown/nettle
lifespan = 30
endurance = 40 // tuff like a toiger
yield = 4
@@ -19,7 +19,7 @@
icon_state = "seed-deathnettle"
species = "deathnettle"
plantname = "Death Nettles"
- product = /obj/item/grown/nettle/death
+ product = /obj/item/reagent_containers/food/snacks/grown/nettle/death
endurance = 25
maturation = 8
yield = 2
@@ -28,7 +28,8 @@
reagents_add = list("facid" = 0.5, "sacid" = 0.5)
rarity = 20
-/obj/item/grown/nettle //abstract type
+/obj/item/reagent_containers/food/snacks/grown/nettle // "snack"
+ seed = /obj/item/seeds/nettle
name = "nettle"
desc = "It's probably not wise to touch it with bare hands..."
icon = 'icons/obj/items_and_weapons.dmi'
@@ -43,13 +44,12 @@
throw_speed = 1
throw_range = 3
attack_verb = list("stung")
- grind_results = list("sacid" = 0)
-/obj/item/grown/nettle/suicide_act(mob/user)
+/obj/item/reagent_containers/food/snacks/grown/nettle/suicide_act(mob/user)
user.visible_message("[user] is eating some of [src]! It looks like [user.p_theyre()] trying to commit suicide! ")
return (BRUTELOSS|TOXLOSS)
-/obj/item/grown/nettle/pickup(mob/living/user)
+/obj/item/reagent_containers/food/snacks/grown/nettle/pickup(mob/living/user)
..()
if(!iscarbon(user))
return FALSE
@@ -66,7 +66,8 @@
to_chat(C, "The nettle burns your bare hand! ")
return TRUE
-/obj/item/grown/nettle/afterattack(atom/A as mob|obj, mob/user,proximity)
+/obj/item/reagent_containers/food/snacks/grown/nettle/afterattack(atom/A as mob|obj, mob/user,proximity)
+ . = ..()
if(!proximity)
return
if(force > 0)
@@ -75,38 +76,37 @@
to_chat(usr, "All the leaves have fallen off the nettle from violent whacking.")
qdel(src)
-/obj/item/grown/nettle/basic
+/obj/item/reagent_containers/food/snacks/grown/nettle/basic
seed = /obj/item/seeds/nettle
-/obj/item/grown/nettle/basic/add_juice()
+/obj/item/reagent_containers/food/snacks/grown/nettle/basic/add_juice()
..()
force = round((5 + seed.potency / 5), 1)
-/obj/item/grown/nettle/death
+/obj/item/reagent_containers/food/snacks/grown/nettle/death
seed = /obj/item/seeds/nettle/death
name = "deathnettle"
desc = "The glowing nettle incites rage in you just from looking at it!"
icon_state = "deathnettle"
force = 30
throwforce = 15
- grind_results = list("facid" = 1, "sacid" = 1)
-/obj/item/grown/nettle/death/add_juice()
+/obj/item/reagent_containers/food/snacks/grown/nettle/death/add_juice()
..()
force = round((5 + seed.potency / 2.5), 1)
-/obj/item/grown/nettle/death/pickup(mob/living/carbon/user)
+/obj/item/reagent_containers/food/snacks/grown/nettle/death/pickup(mob/living/carbon/user)
if(..())
if(prob(50))
user.Knockdown(100)
to_chat(user, "You are stunned by the Deathnettle as you try picking it up! ")
-/obj/item/grown/nettle/death/attack(mob/living/carbon/M, mob/user)
+/obj/item/reagent_containers/food/snacks/grown/nettle/death/attack(mob/living/carbon/M, mob/user)
if(!..())
return
if(isliving(M))
to_chat(M, "You are stunned by the powerful acid of the Deathnettle! ")
- add_logs(user, M, "attacked", src)
+ log_combat(user, M, "attacked", src)
M.adjust_blurriness(force/7)
if(prob(20))
diff --git a/code/modules/hydroponics/grown/onion.dm b/code/modules/hydroponics/grown/onion.dm
index 9b957ae744..9cb8d1a63c 100644
--- a/code/modules/hydroponics/grown/onion.dm
+++ b/code/modules/hydroponics/grown/onion.dm
@@ -26,6 +26,7 @@
tastes = list("onions" = 1)
slice_path = /obj/item/reagent_containers/food/snacks/onion_slice
slices_num = 2
+ wine_power = 30
/obj/item/seeds/onion/red
name = "pack of red onion seeds"
@@ -44,6 +45,7 @@
icon_state = "onion_red"
filling_color = "#C29ACF"
slice_path = /obj/item/reagent_containers/food/snacks/onion_slice/red
+ wine_power = 60
/obj/item/reagent_containers/food/snacks/grown/onion/slice(accuracy, obj/item/W, mob/user)
var/datum/effect_system/smoke_spread/chem/S = new //Since the onion is destroyed when it's sliced,
diff --git a/code/modules/hydroponics/grown/pineapple.dm b/code/modules/hydroponics/grown/pineapple.dm
index 7b58c7553d..e52c261217 100644
--- a/code/modules/hydroponics/grown/pineapple.dm
+++ b/code/modules/hydroponics/grown/pineapple.dm
@@ -31,3 +31,4 @@
w_class = WEIGHT_CLASS_NORMAL
foodtype = FRUIT | PINEAPPLE
tastes = list("pineapple" = 1)
+ wine_power = 40
diff --git a/code/modules/hydroponics/grown/potato.dm b/code/modules/hydroponics/grown/potato.dm
index 4d1d916572..15378b368e 100644
--- a/code/modules/hydroponics/grown/potato.dm
+++ b/code/modules/hydroponics/grown/potato.dm
@@ -27,7 +27,7 @@
bitesize = 100
foodtype = VEGETABLES
juice_results = list("potato" = 0)
-
+ distill_reagent = "vodka"
/obj/item/reagent_containers/food/snacks/grown/potato/wedges
name = "potato wedges"
@@ -64,3 +64,4 @@
name = "sweet potato"
desc = "It's sweet."
icon_state = "sweetpotato"
+ distill_reagent = "sbiten"
diff --git a/code/modules/hydroponics/grown/pumpkin.dm b/code/modules/hydroponics/grown/pumpkin.dm
index 2b0ffddfe7..644dedff3d 100644
--- a/code/modules/hydroponics/grown/pumpkin.dm
+++ b/code/modules/hydroponics/grown/pumpkin.dm
@@ -25,6 +25,7 @@
bitesize_mod = 2
foodtype = FRUIT
juice_results = list("pumpkinjuice" = 0)
+ wine_power = 20
/obj/item/reagent_containers/food/snacks/grown/pumpkin/attackby(obj/item/W as obj, mob/user as mob, params)
if(W.is_sharp())
@@ -56,3 +57,4 @@
bitesize_mod = 2
foodtype = FRUIT
juice_results = list("blumpkinjuice" = 0)
+ wine_power = 50
diff --git a/code/modules/hydroponics/grown/random.dm b/code/modules/hydroponics/grown/random.dm
index 04b3ff29fa..75505202da 100644
--- a/code/modules/hydroponics/grown/random.dm
+++ b/code/modules/hydroponics/grown/random.dm
@@ -26,4 +26,10 @@
name = "strange plant"
desc = "What could this even be?"
icon_state = "crunchy"
- bitesize_mod = 2
\ No newline at end of file
+ bitesize_mod = 2
+
+/obj/item/reagent_containers/food/snacks/grown/random/Initialize()
+ . = ..()
+ wine_power = rand(10,150)
+ if(prob(1))
+ wine_power = 200
diff --git a/code/modules/hydroponics/grown/root.dm b/code/modules/hydroponics/grown/root.dm
index fd78fa6ffa..be0a209c9f 100644
--- a/code/modules/hydroponics/grown/root.dm
+++ b/code/modules/hydroponics/grown/root.dm
@@ -23,6 +23,7 @@
bitesize_mod = 2
foodtype = VEGETABLES
juice_results = list("carrotjuice" = 0)
+ wine_power = 30
/obj/item/reagent_containers/food/snacks/grown/carrot/attackby(obj/item/I, mob/user, params)
if(I.is_sharp())
@@ -53,6 +54,7 @@
icon_state = "parsnip"
bitesize_mod = 2
foodtype = VEGETABLES
+ wine_power = 35
// White-Beet
@@ -79,6 +81,7 @@
filling_color = "#F4A460"
bitesize_mod = 2
foodtype = VEGETABLES
+ wine_power = 40
// Red Beet
/obj/item/seeds/redbeet
@@ -103,3 +106,4 @@
icon_state = "redbeet"
bitesize_mod = 2
foodtype = VEGETABLES
+ wine_power = 60
diff --git a/code/modules/hydroponics/grown/tea_coffee.dm b/code/modules/hydroponics/grown/tea_coffee.dm
index fc2ed221c5..fc84617ed8 100644
--- a/code/modules/hydroponics/grown/tea_coffee.dm
+++ b/code/modules/hydroponics/grown/tea_coffee.dm
@@ -23,6 +23,7 @@
filling_color = "#008000"
grind_results = list("teapowder" = 0)
dry_grind = TRUE
+ can_distill = FALSE
// Tea Astra
/obj/item/seeds/tea/astra
@@ -71,6 +72,7 @@
bitesize_mod = 2
dry_grind = TRUE
grind_results = list("coffeepowder" = 0)
+ distill_reagent = "kahlua"
// Coffee Robusta
/obj/item/seeds/coffee/robusta
diff --git a/code/modules/hydroponics/grown/tobacco.dm b/code/modules/hydroponics/grown/tobacco.dm
index 3258e7ea4f..684271fa6b 100644
--- a/code/modules/hydroponics/grown/tobacco.dm
+++ b/code/modules/hydroponics/grown/tobacco.dm
@@ -21,6 +21,7 @@
desc = "Dry them out to make some smokes."
icon_state = "tobacco_leaves"
filling_color = "#008000"
+ distill_reagent = "creme_de_menthe" //Menthol, I guess.
// Space Tobacco
/obj/item/seeds/tobacco/space
@@ -38,4 +39,6 @@
seed = /obj/item/seeds/tobacco/space
name = "space tobacco leaves"
desc = "Dry them out to make some space-smokes."
- icon_state = "stobacco_leaves"
\ No newline at end of file
+ icon_state = "stobacco_leaves"
+ distill_reagent = null
+ wine_power = 50
\ No newline at end of file
diff --git a/code/modules/hydroponics/grown/tomato.dm b/code/modules/hydroponics/grown/tomato.dm
index 0f5b969b3b..767de8011a 100644
--- a/code/modules/hydroponics/grown/tomato.dm
+++ b/code/modules/hydroponics/grown/tomato.dm
@@ -25,6 +25,7 @@
foodtype = FRUIT
grind_results = list("ketchup" = 0)
juice_results = list("tomatojuice" = 0)
+ distill_reagent = "enzyme"
// Blood Tomato
/obj/item/seeds/tomato/blood
@@ -47,7 +48,7 @@
filling_color = "#FF0000"
foodtype = FRUIT | GROSS
grind_results = list("ketchup" = 0, "blood" = 0)
-
+ distill_reagent = "bloodymary"
// Blue Tomato
/obj/item/seeds/tomato/blue
@@ -71,7 +72,7 @@
icon_state = "bluetomato"
splat_type = /obj/effect/decal/cleanable/oil
filling_color = "#0000FF"
-
+ distill_reagent = "laughter"
// Bluespace Tomato
/obj/item/seeds/tomato/blue/bluespace
@@ -92,7 +93,8 @@
name = "bluespace tomato"
desc = "So lubricated, you might slip through space-time."
icon_state = "bluespacetomato"
-
+ distill_reagent = null
+ wine_power = 80
// Killer Tomato
/obj/item/seeds/tomato/killer
@@ -118,6 +120,7 @@
icon_state = "killertomato"
var/awakening = 0
filling_color = "#FF0000"
+ distill_reagent = "demonsblood"
/obj/item/reagent_containers/food/snacks/grown/tomato/killer/attack(mob/M, mob/user, def_zone)
if(awakening)
diff --git a/code/modules/integrated_electronics/core/analyzer.dm b/code/modules/integrated_electronics/core/analyzer.dm
index 89b990f352..7e30529bf4 100644
--- a/code/modules/integrated_electronics/core/analyzer.dm
+++ b/code/modules/integrated_electronics/core/analyzer.dm
@@ -7,6 +7,7 @@
w_class = WEIGHT_CLASS_SMALL
/obj/item/integrated_electronics/analyzer/afterattack(var/atom/A, var/mob/living/user)
+ . = ..()
if(istype(A, /obj/item/electronic_assembly))
var/saved = "[A.name] analyzed! On circuit printers with cloning enabled, you may use the code below to clone the circuit:[SScircuit.save_electronic_assembly(A)]"
if(saved)
@@ -14,5 +15,3 @@
user << browse(saved, "window=circuit_scan;size=500x600;border=1;can_resize=1;can_close=1;can_minimize=1")
else
to_chat(user, "[A] is not complete enough to be encoded! ")
- else
- ..()
diff --git a/code/modules/integrated_electronics/core/assemblies.dm b/code/modules/integrated_electronics/core/assemblies.dm
index d39163b547..1a4bb1fb1b 100644
--- a/code/modules/integrated_electronics/core/assemblies.dm
+++ b/code/modules/integrated_electronics/core/assemblies.dm
@@ -59,6 +59,11 @@
COLOR_ASSEMBLY_PURPLE
)
+/obj/item/electronic_assembly/New()
+ ..()
+ src.max_components = round(max_components)
+ src.max_complexity = round(max_complexity)
+
/obj/item/electronic_assembly/GenerateTag()
tag = "assembly_[next_assembly_id++]"
@@ -70,12 +75,18 @@
to_chat(user, "The maintenance panel [opened ? "can be" : "is"] screwed in place. ")
if((isobserver(user) && ckeys_allowed_to_scan[user.ckey]) || IsAdminGhost(user))
- to_chat(user, "You can scan this circuit.");
+ to_chat(user, "You can scan this circuit.")
+
+ for(var/I in assembly_components)
+ var/obj/item/integrated_circuit/IC = I
+ IC.external_examine(user)
+ if(opened)
+ interact(user)
/obj/item/electronic_assembly/proc/check_interactivity(mob/user)
return user.canUseTopic(src, BE_CLOSE)
-/obj/item/electronic_assembly/Collide(atom/AM)
+/obj/item/electronic_assembly/Bump(atom/AM)
collw = AM
.=..()
if((istype(collw, /obj/machinery/door/airlock) || istype(collw, /obj/machinery/door/window)) && (!isnull(access_card)))
@@ -306,14 +317,6 @@
detail_overlay.color = detail_color
add_overlay(detail_overlay)
-/obj/item/electronic_assembly/examine(mob/user)
- ..()
- for(var/I in assembly_components)
- var/obj/item/integrated_circuit/IC = I
- IC.external_examine(user)
- if(opened)
- interact(user)
-
/obj/item/electronic_assembly/proc/return_total_complexity()
. = 0
var/obj/item/integrated_circuit/part
@@ -420,12 +423,15 @@
/obj/item/electronic_assembly/afterattack(atom/target, mob/user, proximity)
+ . = ..()
for(var/obj/item/integrated_circuit/input/S in assembly_components)
if(S.sense(target,user,proximity))
visible_message(" [user] waves [src] around [target]. ")
/obj/item/electronic_assembly/screwdriver_act(mob/living/user, obj/item/I)
+ if(..())
+ return TRUE
I.play_tool_sound(src)
opened = !opened
to_chat(user, "You [opened ? "open" : "close"] the maintenance hatch of [src]. ")
@@ -439,7 +445,6 @@
if(!user.canUnEquip(I))
return FALSE
if(try_add_component(I, user))
- interact(user)
return TRUE
else
for(var/obj/item/integrated_circuit/input/S in assembly_components)
@@ -465,24 +470,50 @@
for(var/obj/item/integrated_circuit/input/S in assembly_components)
S.attackby_react(I,user,user.a_intent)
return ..()
- var/obj/item/stock_parts/cell = I
- user.transferItemToLoc(I, loc)
- cell.forceMove(src)
- battery = cell
+ I.forceMove(src)
+ battery = I
diag_hud_set_circuitstat() //update diagnostic hud
playsound(get_turf(src), 'sound/items/Deconstruct.ogg', 50, 1)
- to_chat(user, "You slot \the [cell] inside \the [src]'s power supplier. ")
- interact(user)
+ to_chat(user, "You slot the [I] inside \the [src]'s power supplier. ")
return TRUE
else if(istype(I, /obj/item/integrated_electronics/detailer))
var/obj/item/integrated_electronics/detailer/D = I
detail_color = D.detail_color
update_icon()
else
- for(var/obj/item/integrated_circuit/input/S in assembly_components)
- S.attackby_react(I,user,user.a_intent)
if(user.a_intent != INTENT_HELP)
return ..()
+ var/list/input_selection = list()
+ //Check all the components asking for an input
+ for(var/obj/item/integrated_circuit/input in assembly_components)
+ if((input.demands_object_input && opened) || (input.demands_object_input && input.can_input_object_when_closed))
+ var/i = 0
+ //Check if there is another component with the same name and append a number for identification
+ for(var/s in input_selection)
+ var/obj/item/integrated_circuit/s_circuit = input_selection[s]
+ if(s_circuit.name == input.name && s_circuit.displayed_name == input.displayed_name && s_circuit != input)
+ i++
+ var/disp_name= "[input.displayed_name] \[[input]\]"
+ if(i)
+ disp_name += " ([i+1])"
+ //Associative lists prevent me from needing another list and using a Find proc
+ input_selection[disp_name] = input
+
+ var/obj/item/integrated_circuit/choice
+ if(input_selection)
+ if(input_selection.len == 1)
+ choice = input_selection[input_selection[1]]
+ else
+ var/selection = input(user, "Where do you want to insert that item?", "Interaction") as null|anything in input_selection
+ if(!check_interactivity(user))
+ return ..()
+ if(selection)
+ choice = input_selection[selection]
+ if(choice)
+ choice.additem(I, user)
+ for(var/obj/item/integrated_circuit/input/S in assembly_components)
+ S.attackby_react(I,user,user.a_intent)
+ return ..()
/obj/item/electronic_assembly/attack_self(mob/user)
@@ -492,30 +523,33 @@
interact(user)
var/list/input_selection = list()
- var/list/available_inputs = list()
+ //Check all the components asking for an input
for(var/obj/item/integrated_circuit/input/input in assembly_components)
if(input.can_be_asked_input)
- available_inputs.Add(input)
var/i = 0
- for(var/obj/item/integrated_circuit/s in available_inputs)
- if(s.name == input.name && s.displayed_name == input.displayed_name && s != input)
+ //Check if there is another component with the same name and append a number for identification
+ for(var/s in input_selection)
+ var/obj/item/integrated_circuit/s_circuit = input_selection[s]
+ if(s_circuit.name == input.name && s_circuit.displayed_name == input.displayed_name && s_circuit != input)
i++
var/disp_name= "[input.displayed_name] \[[input]\]"
if(i)
disp_name += " ([i+1])"
- input_selection.Add(disp_name)
+ //Associative lists prevent me from needing another list and using a Find proc
+ input_selection[disp_name] = input
var/obj/item/integrated_circuit/input/choice
- if(available_inputs)
- if(available_inputs.len ==1)
- choice = available_inputs[1]
+
+
+ if(input_selection)
+ if(input_selection.len ==1)
+ choice = input_selection[input_selection[1]]
else
var/selection = input(user, "What do you want to interact with?", "Interaction") as null|anything in input_selection
if(!check_interactivity(user))
return
if(selection)
- var/index = input_selection.Find(selection)
- choice = available_inputs[index]
+ choice = input_selection[selection]
if(choice)
choice.ask_for_input(user)
@@ -609,6 +643,37 @@
icon_state = "setup_small_pda"
desc = "It's a case, for building small electronics with. This one resembles a PDA."
+/obj/item/electronic_assembly/small
+ name = "electronic device"
+ icon_state = "setup_device"
+ desc = "It's a case, for building tiny-sized electronics with."
+ w_class = WEIGHT_CLASS_TINY
+ max_components = IC_MAX_SIZE_BASE / 2
+ max_complexity = IC_COMPLEXITY_BASE / 2
+
+/obj/item/electronic_assembly/small/default
+ name = "type-a electronic device"
+
+/obj/item/electronic_assembly/small/cylinder
+ name = "type-b electronic device"
+ icon_state = "setup_device_cylinder"
+ desc = "It's a case, for building tiny-sized electronics with. This one has a cylindrical design."
+
+/obj/item/electronic_assembly/small/scanner
+ name = "type-c electronic device"
+ icon_state = "setup_device_scanner"
+ desc = "It's a case, for building tiny-sized electronics with. This one has a scanner-like design."
+
+/obj/item/electronic_assembly/small/hook
+ name = "type-d electronic device"
+ icon_state = "setup_device_hook"
+ desc = "It's a case, for building tiny-sized electronics with. This one looks like it has a belt clip, but it's purely decorative."
+
+/obj/item/electronic_assembly/small/box
+ name = "type-e electronic device"
+ icon_state = "setup_device_box"
+ desc = "It's a case, for building tiny-sized electronics with. This one has a boxy design."
+
/obj/item/electronic_assembly/medium
name = "electronic mechanism"
icon_state = "setup_medium"
@@ -750,6 +815,14 @@
max_components = IC_MAX_SIZE_BASE
max_complexity = IC_COMPLEXITY_BASE
+/obj/item/electronic_assembly/wallmount/tiny
+ name = "tiny wall-mounted electronic assembly"
+ icon_state = "setup_wallmount_tiny"
+ desc = "It's a case, for building tiny electronics with. It has a magnetized backing to allow it to stick to walls, but you'll still need to wrench the anchoring bolts in place to keep it on."
+ w_class = WEIGHT_CLASS_TINY
+ max_components = IC_MAX_SIZE_BASE / 2
+ max_complexity = IC_COMPLEXITY_BASE / 2
+
/obj/item/electronic_assembly/wallmount/proc/mount_assembly(turf/on_wall, mob/user) //Yeah, this is admittedly just an abridged and kitbashed version of the wallframe attach procs.
if(get_dist(on_wall,user)>1)
return
diff --git a/code/modules/integrated_electronics/core/debugger.dm b/code/modules/integrated_electronics/core/debugger.dm
index d6f6a551ad..5d414de5ee 100644
--- a/code/modules/integrated_electronics/core/debugger.dm
+++ b/code/modules/integrated_electronics/core/debugger.dm
@@ -9,9 +9,10 @@
w_class = WEIGHT_CLASS_SMALL
var/data_to_write = null
var/accepting_refs = FALSE
+ var/copy_values = FALSE
/obj/item/integrated_electronics/debugger/attack_self(mob/user)
- var/type_to_use = input("Please choose a type to use.","[src] type setting") as null|anything in list("string","number","ref", "null")
+ var/type_to_use = input("Please choose a type to use.","[src] type setting") as null|anything in list("string","number","ref","copy","null")
if(!user.IsAdvancedToolUser())
return
@@ -19,43 +20,63 @@
switch(type_to_use)
if("string")
accepting_refs = FALSE
+ copy_values = FALSE
new_data = stripped_input(user, "Now type in a string.","[src] string writing", no_trim = TRUE)
if(istext(new_data) && user.IsAdvancedToolUser())
data_to_write = new_data
to_chat(user, "You set \the [src]'s memory to \"[new_data]\". ")
if("number")
accepting_refs = FALSE
+ copy_values = FALSE
new_data = input(user, "Now type in a number.","[src] number writing") as null|num
if(isnum(new_data) && user.IsAdvancedToolUser())
data_to_write = new_data
to_chat(user, "You set \the [src]'s memory to [new_data]. ")
if("ref")
accepting_refs = TRUE
+ copy_values = FALSE
to_chat(user, "You turn \the [src]'s ref scanner on. Slide it across \
an object for a ref of that object to save it in memory. ")
+ if("copy")
+ accepting_refs = FALSE
+ copy_values = TRUE
+ to_chat(user, "You turn \the [src]'s value copier on. Use it on a pin \
+ to save its current value in memory. ")
if("null")
data_to_write = null
+ copy_values = FALSE
to_chat(user, "You set \the [src]'s memory to absolutely nothing. ")
/obj/item/integrated_electronics/debugger/afterattack(atom/target, mob/living/user, proximity)
+ . = ..()
if(accepting_refs && proximity)
data_to_write = WEAKREF(target)
visible_message("[user] slides \a [src]'s over \the [target]. ")
to_chat(user, "You set \the [src]'s memory to a reference to [target.name] \[Ref\]. The ref scanner is \
now off. ")
accepting_refs = FALSE
- else
- return ..()
/obj/item/integrated_electronics/debugger/proc/write_data(var/datum/integrated_io/io, mob/user)
+ //If the pin can take data:
if(io.io_type == DATA_CHANNEL)
+ //If the debugger is set to copy, copy the data in the pin onto it
+ if(copy_values)
+ data_to_write = io.data
+ to_chat(user, "You let the debugger copy the data. ")
+ copy_values = FALSE
+ return
+
+ //Else, write the data to the pin
io.write_data_to_pin(data_to_write)
var/data_to_show = data_to_write
+ //This is only to convert a weakref into a name for better output
if(isweakref(data_to_write))
var/datum/weakref/w = data_to_write
var/atom/A = w.resolve()
data_to_show = A.name
to_chat(user, "You write '[data_to_write ? data_to_show : "NULL"]' to the '[io]' pin of \the [io.holder]. ")
+
+ //If the pin can only be pulsed
else if(io.io_type == PULSE_CHANNEL)
io.holder.check_then_do_work(io.ord,ignore_power = TRUE)
to_chat(user, "You pulse \the [io.holder]'s [io]. ")
diff --git a/code/modules/integrated_electronics/core/integrated_circuit.dm b/code/modules/integrated_electronics/core/integrated_circuit.dm
index 57ad917246..60b2d0486e 100644
--- a/code/modules/integrated_electronics/core/integrated_circuit.dm
+++ b/code/modules/integrated_electronics/core/integrated_circuit.dm
@@ -24,6 +24,9 @@
var/category_text = "NO CATEGORY THIS IS A BUG" // To show up on circuit printer, and perhaps other places.
var/removable = TRUE // Determines if a circuit is removable from the assembly.
var/displayed_name = ""
+ var/demands_object_input = FALSE
+ var/can_input_object_when_closed = FALSE
+
/*
Integrated circuits are essentially modular machines. Each circuit has a specific function, and combining them inside Electronic Assemblies allows
@@ -35,6 +38,10 @@ a creative player the means to solve many problems. Circuits are held inside an
external_examine(user)
. = ..()
+// Can be called via electronic_assembly/attackby()
+/obj/item/integrated_circuit/proc/additem(var/obj/item/I, var/mob/living/user)
+ attackby(I, user)
+
// This should be used when someone is examining while the case is opened.
/obj/item/integrated_circuit/proc/internal_examine(mob/user)
to_chat(user, "This board has [inputs.len] input pin\s, [outputs.len] output pin\s and [activators.len] activation pin\s.")
diff --git a/code/modules/integrated_electronics/core/printer.dm b/code/modules/integrated_electronics/core/printer.dm
index f8d1424160..de3ade389f 100644
--- a/code/modules/integrated_electronics/core/printer.dm
+++ b/code/modules/integrated_electronics/core/printer.dm
@@ -38,6 +38,7 @@
/obj/item/integrated_circuit_printer/proc/print_program(mob/user)
if(!cloning)
return
+
visible_message("[src] has finished printing its assembly! ")
playsound(src, 'sound/items/poster_being_created.ogg', 50, TRUE)
var/obj/item/electronic_assembly/assembly = SScircuit.load_electronic_assembly(get_turf(src), program)
@@ -52,7 +53,6 @@
return TRUE
to_chat(user, "You install [O] into [src]. ")
upgraded = TRUE
- interact(user)
return TRUE
if(istype(O, /obj/item/disk/integrated_circuit/upgrade/clone))
@@ -61,7 +61,6 @@
return TRUE
to_chat(user, "You install [O] into [src]. Circuit cloning will now be instant. ")
fast_clone = TRUE
- interact(user)
return TRUE
if(istype(O, /obj/item/electronic_assembly))
@@ -108,11 +107,17 @@
interact(user)
/obj/item/integrated_circuit_printer/interact(mob/user)
+ if(!(in_range(src, user) || issilicon(user)))
+ return
+
if(isnull(current_category))
current_category = SScircuit.circuit_fabricator_recipe_list[1]
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
+ //Preparing the browser
+ var/datum/browser/popup = new(user, "printernew", "Integrated Circuit Printer", 800, 630) // Set up the popup browser window
+
var/HTML = "Integrated Circuit Printer "
if(debug)
HTML += "DEBUG PRINTER -- Infinite materials. Cloning available. "
@@ -132,13 +137,13 @@
if(!cloning)
HTML += " {Load Program} "
else
- HTML += " {Load Program}"
+ HTML += " Load Program"
if(!program)
- HTML += " {[fast_clone ? "Print" : "Begin Printing"] Assembly}"
+ HTML += " [fast_clone ? "Print" : "Begin Printing"] Assembly"
else if(cloning)
- HTML += " {Cancel Print} "
+ HTML += " Cancel Print "
else
- HTML += " {[fast_clone ? "Print" : "Begin Printing"] Assembly} "
+ HTML += " [fast_clone ? "Print" : "Begin Printing"] Assembly "
HTML += " "
HTML += "Categories:"
@@ -159,11 +164,12 @@
if((initial(IC.spawn_flags) & IC_SPAWN_RESEARCH) && (!(initial(IC.spawn_flags) & IC_SPAWN_DEFAULT)) && !upgraded)
can_build = FALSE
if(can_build)
- HTML += "\[[initial(O.name)]\] : [initial(O.desc)] "
+ HTML += "[initial(O.name)] : [initial(O.desc)] "
else
- HTML += "\[[initial(O.name)]\] : [initial(O.desc)] "
+ HTML += "[initial(O.name)] : [initial(O.desc)] "
- user << browse(HTML, "window=integrated_printer;size=600x500;border=1;can_resize=1;can_close=1;can_minimize=1")
+ popup.set_content(HTML)
+ popup.open()
/obj/item/integrated_circuit_printer/Topic(href, href_list)
if(!check_interactivity(usr))
diff --git a/code/modules/integrated_electronics/core/special_pins/ref_pin.dm b/code/modules/integrated_electronics/core/special_pins/ref_pin.dm
index 461965f254..f64e15f225 100644
--- a/code/modules/integrated_electronics/core/special_pins/ref_pin.dm
+++ b/code/modules/integrated_electronics/core/special_pins/ref_pin.dm
@@ -11,4 +11,14 @@
holder.on_data_written()
/datum/integrated_io/ref/display_pin_type()
- return IC_FORMAT_REF
\ No newline at end of file
+ return IC_FORMAT_REF
+
+/datum/integrated_io/ref/connect_pin(datum/integrated_io/pin)
+ ..(pin)
+ if(istype(pin,/datum/integrated_io/selfref))
+ write_data_to_pin(pin.data)
+
+/datum/integrated_io/ref/disconnect_pin(datum/integrated_io/pin)
+ ..(pin)
+ if(istype(pin,/datum/integrated_io/selfref))
+ write_data_to_pin(null)
diff --git a/code/modules/integrated_electronics/core/special_pins/selfref_pin.dm b/code/modules/integrated_electronics/core/special_pins/selfref_pin.dm
new file mode 100644
index 0000000000..79dcf9e6c8
--- /dev/null
+++ b/code/modules/integrated_electronics/core/special_pins/selfref_pin.dm
@@ -0,0 +1,27 @@
+// This pin only contains its own weakref and can't be changed
+/datum/integrated_io/selfref
+ name = "selfref pin"
+
+/datum/integrated_io/selfref/New()
+ ..()
+ write_data_to_pin(src)
+
+/datum/integrated_io/selfref/ask_for_pin_data(mob/user) // You can't clear it, it's self reference.
+
+
+/datum/integrated_io/selfref/write_data_to_pin(var/new_data) // You can't write anything else but itself onto it
+ if(data)
+ return
+ data = WEAKREF(holder)
+ holder.on_data_written()
+
+/datum/integrated_io/selfref/display_pin_type()
+ return IC_FORMAT_REF
+
+/datum/integrated_io/selfref/connect_pin(datum/integrated_io/pin)
+ pin.write_data_to_pin(data)
+ ..(pin)
+
+/datum/integrated_io/selfref/disconnect_pin(datum/integrated_io/pin)
+ ..(pin)
+ pin.write_data_to_pin(null)
diff --git a/code/modules/integrated_electronics/subtypes/access.dm b/code/modules/integrated_electronics/subtypes/access.dm
index 9012cd1089..5e03ea1394 100644
--- a/code/modules/integrated_electronics/subtypes/access.dm
+++ b/code/modules/integrated_electronics/subtypes/access.dm
@@ -14,17 +14,13 @@
"on read" = IC_PINTYPE_PULSE_OUT
)
-/obj/item/integrated_circuit/input/card_reader/old
- name = "card reader"
- spawn_flags = 0
-
/obj/item/integrated_circuit/input/card_reader/attackby_react(obj/item/I, mob/living/user, intent)
var/obj/item/card/id/card = I.GetID()
var/list/access = I.GetAccess()
var/passkey = strtohex(XorEncrypt(json_encode(access), SScircuit.cipherkey))
if(assembly)
- assembly.access_card.access = access
+ assembly.access_card.access |= access
if(card) // An ID card.
set_pin_data(IC_OUTPUT, 1, card.registered_name)
diff --git a/code/modules/integrated_electronics/subtypes/arithmetic.dm b/code/modules/integrated_electronics/subtypes/arithmetic.dm
index d4b854268b..6a6ee27ae1 100644
--- a/code/modules/integrated_electronics/subtypes/arithmetic.dm
+++ b/code/modules/integrated_electronics/subtypes/arithmetic.dm
@@ -311,3 +311,33 @@
set_pin_data(IC_OUTPUT, 1, result)
push_data()
activate_pin(2)
+
+// -Max- //
+/obj/item/integrated_circuit/arithmetic/max
+ name = "max circuit"
+ desc = "This circuit sends out the highest number."
+ extended_desc = "The highest number is put out. Null is ignored."
+ icon_state = "addition"
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ var/min_comparision = FALSE
+
+/obj/item/integrated_circuit/arithmetic/max/do_work()
+ var/result
+ for(var/k in 1 to inputs.len)
+ var/I = get_pin_data(IC_INPUT, k)
+ if(!isnum(I))
+ continue
+ if(!isnum(result) || (!min_comparision && I > result) || (min_comparision && I < result))
+ result = I
+ if(!isnum(result))
+ result = 0
+ set_pin_data(IC_OUTPUT, 1, result)
+ push_data()
+ activate_pin(2)
+
+// -Min- //
+/obj/item/integrated_circuit/arithmetic/max/min
+ name = "min circuit"
+ desc = "This circuit sends out the smallest number."
+ extended_desc = "The smallest number is put out. Null is ignored. In case no number is found, 0 is given out."
+ min_comparision = TRUE
diff --git a/code/modules/integrated_electronics/subtypes/converters.dm b/code/modules/integrated_electronics/subtypes/converters.dm
index 9382f70066..668f49c98f 100644
--- a/code/modules/integrated_electronics/subtypes/converters.dm
+++ b/code/modules/integrated_electronics/subtypes/converters.dm
@@ -357,6 +357,7 @@
/obj/item/integrated_circuit/converter/abs_to_rel_coords
name = "abs to rel coordinate converter"
desc = "Easily convert absolute coordinates to relative coordinates with this."
+ extended_desc = "Keep in mind that both sets of input coordinates should be absolute."
complexity = 1
inputs = list(
"X1" = IC_PINTYPE_NUMBER,
@@ -385,6 +386,71 @@
push_data()
activate_pin(2)
+/obj/item/integrated_circuit/converter/rel_to_abs_coords
+ name = "rel to abs coordinate converter"
+ desc = "Convert relative coordinates to absolute coordinates with this."
+ extended_desc = "Keep in mind that only one set of input coordinates should be absolute, and the other relative. \
+ The output coordinates will be the absolute form of the input relative coordinates."
+ complexity = 1
+ inputs = list(
+ "X1" = IC_PINTYPE_NUMBER,
+ "Y1" = IC_PINTYPE_NUMBER,
+ "X2" = IC_PINTYPE_NUMBER,
+ "Y2" = IC_PINTYPE_NUMBER
+ )
+ outputs = list(
+ "X" = IC_PINTYPE_NUMBER,
+ "Y" = IC_PINTYPE_NUMBER
+ )
+ activators = list("compute abs coordinates" = IC_PINTYPE_PULSE_IN, "on convert" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/converter/abs_to_rel_coords/do_work()
+ var/x1 = get_pin_data(IC_INPUT, 1)
+ var/y1 = get_pin_data(IC_INPUT, 2)
+
+ var/x2 = get_pin_data(IC_INPUT, 3)
+ var/y2 = get_pin_data(IC_INPUT, 4)
+
+ if(!isnull(x1) && !isnull(y1) && !isnull(x2) && !isnull(y2))
+ set_pin_data(IC_OUTPUT, 1, x1 + x2)
+ set_pin_data(IC_OUTPUT, 2, y1 + y2)
+
+ push_data()
+ activate_pin(2)
+
+/obj/item/integrated_circuit/converter/adv_rel_to_abs_coords
+ name = "advanced rel to abs coordinate converter"
+ desc = "Easily convert relative coordinates to absolute coordinates with this."
+ extended_desc = "This circuit only requires a single set of relative inputs to output absolute coordinates."
+ complexity = 2
+ inputs = list(
+ "X" = IC_PINTYPE_NUMBER,
+ "Y" = IC_PINTYPE_NUMBER,
+ )
+ outputs = list(
+ "X" = IC_PINTYPE_NUMBER,
+ "Y" = IC_PINTYPE_NUMBER
+ )
+ activators = list("compute abs coordinates" = IC_PINTYPE_PULSE_IN, "on convert" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+
+/obj/item/integrated_circuit/converter/abs_to_rel_coords/do_work()
+ var/turf/T = get_turf(src)
+
+ if(!T)
+ return
+
+ var/x1 = get_pin_data(IC_INPUT, 1)
+ var/y1 = get_pin_data(IC_INPUT, 2)
+
+ if(!isnull(x1) && !isnull(y1))
+ set_pin_data(IC_OUTPUT, 1, T.x + x1)
+ set_pin_data(IC_OUTPUT, 2, T.y + y1)
+
+ push_data()
+ activate_pin(2)
+
/obj/item/integrated_circuit/converter/hsv2hex
name = "hsv to hexadecimal"
desc = "This circuit can convert a HSV (Hue, Saturation, and Value) color to a Hexadecimal RGB color."
diff --git a/code/modules/integrated_electronics/subtypes/data_transfer.dm b/code/modules/integrated_electronics/subtypes/data_transfer.dm
index 682d982373..8e1c715d83 100644
--- a/code/modules/integrated_electronics/subtypes/data_transfer.dm
+++ b/code/modules/integrated_electronics/subtypes/data_transfer.dm
@@ -145,6 +145,55 @@
w_class = WEIGHT_CLASS_SMALL
number_of_pins = 16
+/obj/item/integrated_circuit/transfer/pulsemultiplexer
+ name = "two pulse multiplexer"
+ desc = "Pulse in pins to choose the pin value to be sent."
+ extended_desc = "The input pulses are used to select which of the input pins has its data moved to the output."
+ complexity = 2
+ icon_state = "dmux2"
+ inputs = list()
+ outputs = list("output" = IC_PINTYPE_ANY)
+ activators = list("on selected" = IC_PINTYPE_PULSE_OUT)
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ power_draw_per_use = 4
+ var/number_of_pins = 2
+
+/obj/item/integrated_circuit/transfer/pulsemultiplexer/Initialize()
+ for(var/i = 1 to number_of_pins)
+ inputs["input [i]"] = IC_PINTYPE_ANY
+ for(var/i = 1 to number_of_pins)
+ activators["input [i]"] = IC_PINTYPE_PULSE_IN
+ complexity = number_of_pins
+
+ . = ..()
+ desc += " It has [number_of_pins] pulse in pins and [number_of_pins] output pins."
+ extended_desc += " This pulse multiplexer has a range from 1 to [activators.len - 1]."
+
+/obj/item/integrated_circuit/transfer/pulsemultiplexer/do_work(ord)
+ var/input_index = ord - 2
+
+ if(!isnull(input_index) && (input_index >= 0 && input_index < inputs.len))
+ set_pin_data(IC_OUTPUT, 1,get_pin_data(IC_INPUT, input_index + 1))
+ push_data()
+ activate_pin(1)
+
+/obj/item/integrated_circuit/transfer/pulsemultiplexer/medium
+ name = "four pulse multiplexer"
+ icon_state = "dmux4"
+ number_of_pins = 4
+
+/obj/item/integrated_circuit/transfer/pulsemultiplexer/large
+ name = "eight pulse multiplexer"
+ icon_state = "dmux8"
+ w_class = WEIGHT_CLASS_SMALL
+ number_of_pins = 8
+
+/obj/item/integrated_circuit/transfer/pulsemultiplexer/huge
+ name = "sixteen pulse multiplexer"
+ icon_state = "dmux16"
+ w_class = WEIGHT_CLASS_SMALL
+ number_of_pins = 16
+
/obj/item/integrated_circuit/transfer/wire_node
name = "wire node"
desc = "Just a wire node to make wiring easier. Transfers the pulse from in to out."
@@ -156,4 +205,4 @@
size = 0.1
/obj/item/integrated_circuit/transfer/wire_node/do_work()
- activate_pin(2)
\ No newline at end of file
+ activate_pin(2)
diff --git a/code/modules/integrated_electronics/subtypes/input.dm b/code/modules/integrated_electronics/subtypes/input.dm
index a32be12b6d..e963bf9262 100644
--- a/code/modules/integrated_electronics/subtypes/input.dm
+++ b/code/modules/integrated_electronics/subtypes/input.dm
@@ -162,11 +162,6 @@
push_data()
activate_pin(2)
-//please delete at a later date after people stop using the old named circuit
-/obj/item/integrated_circuit/input/adv_med_scanner/old
- name = "integrated advanced medical analyser"
- spawn_flags = 0
-
/obj/item/integrated_circuit/input/slime_scanner
name = "slime_scanner"
desc = "A very small version of the xenobio analyser. This allows the machine to know every needed properties of slime. Output mutation list is non-associative."
@@ -729,8 +724,7 @@
inputs = list(
"target NTNet addresses"= IC_PINTYPE_STRING,
"data to send" = IC_PINTYPE_STRING,
- "secondary text" = IC_PINTYPE_STRING,
- "passkey" = IC_PINTYPE_STRING, //No this isn't a real passkey encryption scheme but that's why you keep your nodes secure so no one can find it out!
+ "secondary text" = IC_PINTYPE_STRING
)
outputs = list(
"address received" = IC_PINTYPE_STRING,
@@ -756,11 +750,10 @@
var/target_address = get_pin_data(IC_INPUT, 1)
var/message = get_pin_data(IC_INPUT, 2)
var/text = get_pin_data(IC_INPUT, 3)
- var/key = get_pin_data(IC_INPUT, 4)
var/datum/netdata/data = new
data.recipient_ids = splittext(target_address, ";")
- data.standard_format_data(message, text, key)
+ data.standard_format_data(message, text, assembly ? strtohex(XorEncrypt(json_encode(assembly.access_card.access), SScircuit.cipherkey)) : null)
ntnet_send(data)
/obj/item/integrated_circuit/input/ntnet_receive(datum/netdata/data)
@@ -777,9 +770,9 @@
name = "Low level NTNet transreceiver"
desc = "Enables the sending and receiving of messages over NTNet via packet data protocol. Allows advanced control of message contents and signalling. Must use associative lists. Outputs associative list. Has a slower transmission rate than normal NTNet circuits, due to increased data processing complexity."
extended_desc = "Data can be sent or received using the second pin on each side, \
- with additonal data reserved for the third pin. When a message is received, the second activation pin \
- will pulse whatever is connected to it. Pulsing the first activation pin will send a message. Messages \
- can be sent to multiple recepients. Addresses must be separated with a semicolon, like this: Address1;Address2;Etc."
+ When a message is received, the second activation pin will pulse whatever is connected to it. \
+ Pulsing the first activation pin will send a message. Messages can be sent to multiple recepients. \
+ Addresses must be separated with a semicolon, like this: Address1;Address2;Etc."
icon_state = "signal"
complexity = 4
cooldown_per_use = 10
@@ -809,6 +802,7 @@
var/datum/netdata/data = new
data.recipient_ids = splittext(target_address, ";")
data.data = message
+ data.passkey = assembly.access_card.access
ntnet_send(data)
/obj/item/integrated_circuit/input/ntnet_advanced/ntnet_receive(datum/netdata/data)
@@ -821,11 +815,11 @@
/obj/item/integrated_circuit/input/gps
name = "global positioning system"
desc = "This allows you to easily know the position of a machine containing this device."
- extended_desc = "The coordinates that the GPS outputs are absolute, not relative."
+ extended_desc = "The coordinates that the GPS outputs are absolute, not relative. The full coords output has the coords separated by commas and is in string format."
icon_state = "gps"
complexity = 4
inputs = list()
- outputs = list("X"= IC_PINTYPE_NUMBER, "Y" = IC_PINTYPE_NUMBER, "Z" = IC_PINTYPE_NUMBER)
+ outputs = list("X"= IC_PINTYPE_NUMBER, "Y" = IC_PINTYPE_NUMBER, "Z" = IC_PINTYPE_NUMBER, "full coords" = IC_PINTYPE_STRING)
activators = list("get coordinates" = IC_PINTYPE_PULSE_IN, "on get coordinates" = IC_PINTYPE_PULSE_OUT)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
power_draw_per_use = 30
@@ -836,13 +830,14 @@
set_pin_data(IC_OUTPUT, 1, null)
set_pin_data(IC_OUTPUT, 2, null)
set_pin_data(IC_OUTPUT, 3, null)
+ set_pin_data(IC_OUTPUT, 4, null)
if(!T)
return
set_pin_data(IC_OUTPUT, 1, T.x)
set_pin_data(IC_OUTPUT, 2, T.y)
set_pin_data(IC_OUTPUT, 3, T.z)
-
+ set_pin_data(IC_OUTPUT, 4, "[T.x],[T.y],[T.z]")
push_data()
activate_pin(2)
diff --git a/code/modules/integrated_electronics/subtypes/logic.dm b/code/modules/integrated_electronics/subtypes/logic.dm
index e9dca8c330..37bc93e887 100644
--- a/code/modules/integrated_electronics/subtypes/logic.dm
+++ b/code/modules/integrated_electronics/subtypes/logic.dm
@@ -203,7 +203,7 @@
return A.data > B.data
/obj/item/integrated_circuit/logic/binary/greater_than_or_equal
- name = "greater_than or equal gate"
+ name = "greater than or equal gate"
desc = "This will output TRUE if the first input is greater than, or equal to the second input."
icon_state = "greater_than_or_equal"
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
diff --git a/code/modules/integrated_electronics/subtypes/manipulation.dm b/code/modules/integrated_electronics/subtypes/manipulation.dm
index 6267f2b622..1c76c7648c 100644
--- a/code/modules/integrated_electronics/subtypes/manipulation.dm
+++ b/code/modules/integrated_electronics/subtypes/manipulation.dm
@@ -34,11 +34,13 @@
var/lethal_projectile = null //lethal mode projectile type
var/lethal_projectile_sound
+ demands_object_input = TRUE // You can put stuff in once the circuit is in assembly,passed down from additem and handled by attackby()
+
/obj/item/integrated_circuit/manipulation/weapon_firing/Destroy()
qdel(installed_gun)
- ..()
+ return ..()
/obj/item/integrated_circuit/manipulation/weapon_firing/attackby(var/obj/O, var/mob/user)
if(istype(O, /obj/item/gun/energy))
@@ -129,6 +131,7 @@
//Shooting Code:
A.preparePixelProjectile(target, src)
A.fire()
+ log_attack("[assembly] [REF(assembly)] has fired [installed_gun].")
return A
/obj/item/integrated_circuit/manipulation/locomotion
@@ -184,6 +187,7 @@
action_flags = IC_ACTION_COMBAT
var/obj/item/grenade/attached_grenade
var/pre_attached_grenade_type
+ demands_object_input = TRUE // You can put stuff in once the circuit is in assembly,passed down from additem and handled by attackby()
/obj/item/integrated_circuit/manipulation/grenade/Initialize()
. = ..()
@@ -350,7 +354,7 @@
name = "grabber"
desc = "A circuit with its own inventory for items. Used to grab and store things."
icon_state = "grabber"
- extended_desc = "This circuit accepts a reference to an object to be grabbed, and can store up to 10 objects. Modes: 1 to grab, 0 to eject the first object, and -1 to eject all objects. If you throw something from a grabber's inventory with a thrower, the grabber will update its outputs accordingly."
+ extended_desc = "This circuit accepts a reference to an object to be grabbed, and can store up to 10 objects. Modes: 1 to grab, 0 to eject the first object, -1 to eject all objects, and -2 to eject the target. If you throw something from a grabber's inventory with a thrower, the grabber will update its outputs accordingly."
w_class = WEIGHT_CLASS_SMALL
size = 3
cooldown_per_use = 5
@@ -359,37 +363,47 @@
outputs = list("first" = IC_PINTYPE_REF, "last" = IC_PINTYPE_REF, "amount" = IC_PINTYPE_NUMBER,"contents" = IC_PINTYPE_LIST)
activators = list("pulse in" = IC_PINTYPE_PULSE_IN,"pulse out" = IC_PINTYPE_PULSE_OUT)
spawn_flags = IC_SPAWN_RESEARCH
+ action_flags = IC_ACTION_COMBAT
power_draw_per_use = 50
var/max_items = 10
/obj/item/integrated_circuit/manipulation/grabber/do_work()
- var/max_w_class = assembly.w_class
- var/atom/movable/acting_object = get_object()
- var/turf/T = get_turf(acting_object)
var/obj/item/AM = get_pin_data_as_type(IC_INPUT, 1, /obj/item)
if(!QDELETED(AM) && !istype(AM, /obj/item/electronic_assembly) && !istype(AM, /obj/item/transfer_valve) && !istype(AM, /obj/item/twohanded) && !istype(assembly.loc, /obj/item/implant/storage))
var/mode = get_pin_data(IC_INPUT, 2)
- if(mode == 1)
- if(check_target(AM))
- var/weightcheck = FALSE
- if (AM.w_class <= max_w_class)
- weightcheck = TRUE
- else
- weightcheck = FALSE
- if((contents.len < max_items) && (weightcheck))
- AM.forceMove(src)
- if(mode == 0)
- if(contents.len)
- var/obj/item/U = contents[1]
- U.forceMove(T)
- if(mode == -1)
- if(contents.len)
- var/obj/item/U
- for(U in contents)
- U.forceMove(T)
+ switch(mode)
+ if(1)
+ grab(AM)
+ if(0)
+ if(contents.len)
+ drop(contents[1])
+ if(-1)
+ drop_all()
+ if(-2)
+ drop(AM)
update_outputs()
activate_pin(2)
+/obj/item/integrated_circuit/manipulation/grabber/proc/grab(obj/item/AM)
+ var/max_w_class = assembly.w_class
+ if(check_target(AM))
+ if(contents.len < max_items && AM.w_class <= max_w_class)
+ var/atom/A = get_object()
+ A.investigate_log("picked up ([AM]) with [src].", INVESTIGATE_CIRCUIT)
+ AM.forceMove(src)
+
+/obj/item/integrated_circuit/manipulation/grabber/proc/drop(obj/item/AM, turf/T = drop_location())
+ var/atom/A = get_object()
+ A.investigate_log("dropped ([AM]) from [src].", INVESTIGATE_CIRCUIT)
+ AM.forceMove(T)
+
+/obj/item/integrated_circuit/manipulation/grabber/proc/drop_all()
+ if(contents.len)
+ var/turf/T = drop_location()
+ var/obj/item/U
+ for(U in src)
+ drop(U, T)
+
/obj/item/integrated_circuit/manipulation/grabber/proc/update_outputs()
if(contents.len)
set_pin_data(IC_OUTPUT, 1, WEAKREF(contents[1]))
@@ -402,11 +416,7 @@
push_data()
/obj/item/integrated_circuit/manipulation/grabber/attack_self(var/mob/user)
- if(contents.len)
- var/turf/T = get_turf(src)
- var/obj/item/U
- for(U in contents)
- U.forceMove(T)
+ drop_all()
update_outputs()
push_data()
@@ -436,6 +446,7 @@
mode = CLAMP(mode, GRAB_PASSIVE, max_grab)
if(AM)
if(check_target(AM, exclude_contents = TRUE))
+ acting_object.investigate_log("grabbed ([AM]) using [src].", INVESTIGATE_CIRCUIT)
acting_object.start_pulling(AM,mode)
if(acting_object.pulling)
set_pin_data(IC_OUTPUT, 1, TRUE)
@@ -528,16 +539,24 @@
var/x_abs = CLAMP(T.x + target_x_rel, 0, world.maxx)
var/y_abs = CLAMP(T.y + target_y_rel, 0, world.maxy)
var/range = round(CLAMP(sqrt(target_x_rel*target_x_rel+target_y_rel*target_y_rel),0,8),1)
-
+ //remove damage
+ A.throwforce = 0
+ A.embedding = list("embed_chance" = 0)
+ //throw it
assembly.visible_message("[assembly] has thrown [A]! ")
log_attack("[assembly] [REF(assembly)] has thrown [A].")
A.forceMove(drop_location())
- A.throw_at(locate(x_abs, y_abs, T.z), range, 3)
+ A.throw_at(locate(x_abs, y_abs, T.z), range, 3, , , , CALLBACK(src, .proc/post_throw, A))
// If the item came from a grabber now we can update the outputs since we've thrown it.
- if(G)
+ if(istype(G))
G.update_outputs()
+/obj/item/integrated_circuit/manipulation/thrower/proc/post_throw(obj/item/A)
+ //return damage
+ A.throwforce = initial(A.throwforce)
+ A.embedding = initial(A.embedding)
+
/obj/item/integrated_circuit/manipulation/matman
name = "material manager"
desc = "This circuit is designed for automatic storage and distribution of materials."
@@ -593,7 +612,7 @@
/obj/item/integrated_circuit/manipulation/matman/Initialize()
var/datum/component/material_container/materials = AddComponent(/datum/component/material_container,
list(MAT_METAL, MAT_GLASS, MAT_SILVER, MAT_GOLD, MAT_DIAMOND, MAT_PLASMA, MAT_URANIUM, MAT_BANANIUM, MAT_TITANIUM, MAT_BLUESPACE), 0,
- FALSE, list(/obj/item/stack), CALLBACK(src, .proc/is_insertion_ready), CALLBACK(src, .proc/AfterMaterialInsert))
+ FALSE, /obj/item/stack, CALLBACK(src, .proc/is_insertion_ready), CALLBACK(src, .proc/AfterMaterialInsert))
materials.max_amount =100000
materials.precise_insertion = TRUE
.=..()
@@ -657,4 +676,4 @@
/obj/item/integrated_circuit/manipulation/matman/Destroy()
GET_COMPONENT(materials, /datum/component/material_container)
materials.retrieve_all()
- .=..()
\ No newline at end of file
+ .=..()
diff --git a/code/modules/integrated_electronics/subtypes/memory.dm b/code/modules/integrated_electronics/subtypes/memory.dm
index fe74657532..93d800807a 100644
--- a/code/modules/integrated_electronics/subtypes/memory.dm
+++ b/code/modules/integrated_electronics/subtypes/memory.dm
@@ -129,6 +129,7 @@
to_chat(user, "You set \the [src]'s memory to absolutely nothing. ")
/obj/item/integrated_circuit/memory/constant/afterattack(atom/target, mob/living/user, proximity)
+ . = ..()
if(accepting_refs && proximity)
var/datum/integrated_io/O = outputs[1]
O.data = WEAKREF(target)
diff --git a/code/modules/integrated_electronics/subtypes/output.dm b/code/modules/integrated_electronics/subtypes/output.dm
index b887617ea8..0ad2714e28 100644
--- a/code/modules/integrated_electronics/subtypes/output.dm
+++ b/code/modules/integrated_electronics/subtypes/output.dm
@@ -2,7 +2,7 @@
category_text = "Output"
/obj/item/integrated_circuit/output/screen
- name = "small screen"
+ name = "screen"
extended_desc = " use <br> to start a new line"
desc = "Takes any data type as an input, and displays it to the user upon examining."
icon_state = "screen"
@@ -34,14 +34,24 @@
else
stuff_to_display = replacetext("[I.data]", eol , " ")
-/obj/item/integrated_circuit/output/screen/medium
- name = "screen"
- desc = "Takes any data type as an input and displays it to the user upon examining, and to adjacent beings when pulsed."
+/obj/item/integrated_circuit/output/screen/large
+ name = "large screen"
+ desc = "Takes any data type as an input and displays it to anybody near the device when pulsed. \
+ It can also be examined to see the last thing it displayed."
icon_state = "screen_medium"
power_draw_per_use = 20
-/obj/item/integrated_circuit/output/screen/medium/do_work()
+/obj/item/integrated_circuit/output/screen/large/do_work()
..()
+
+ if(isliving(assembly.loc))//this whole block just returns if the assembly is neither in a mobs hands or on the ground
+ var/mob/living/H = assembly.loc
+ if(H.get_active_held_item() != assembly && H.get_inactive_held_item() != assembly)
+ return
+ else
+ if(!isturf(assembly.loc))
+ return
+
var/list/nearby_things = range(0, get_turf(src))
for(var/mob/M in nearby_things)
var/obj/O = assembly ? assembly : src
@@ -51,22 +61,6 @@
else
investigate_log("displayed \"[html_encode(stuff_to_display)]\" as [type].", INVESTIGATE_CIRCUIT)
-/obj/item/integrated_circuit/output/screen/large
- name = "large screen"
- desc = "Takes any data type as an input and displays it to the user upon examining, and to all nearby beings when pulsed."
- icon_state = "screen_large"
- power_draw_per_use = 40
- cooldown_per_use = 10
-
-/obj/item/integrated_circuit/output/screen/large/do_work()
- ..()
- var/obj/O = assembly ? get_turf(assembly) : loc
- O.visible_message("[icon2html(O.icon, world, O.icon_state)] [stuff_to_display] ")
- if(assembly)
- assembly.investigate_log("displayed \"[html_encode(stuff_to_display)]\" with [type].", INVESTIGATE_CIRCUIT)
- else
- investigate_log("displayed \"[html_encode(stuff_to_display)]\" as [type].", INVESTIGATE_CIRCUIT)
-
/obj/item/integrated_circuit/output/light
name = "light"
desc = "A basic light which can be toggled on/off when pulsed."
@@ -88,7 +82,7 @@
/obj/item/integrated_circuit/output/light/proc/update_lighting()
if(light_toggled)
if(assembly)
- assembly.set_light(l_range = light_brightness, l_power = light_brightness, l_color = light_rgb)
+ assembly.set_light(l_range = light_brightness, l_power = 1, l_color = light_rgb)
else
if(assembly)
assembly.set_light(0)
@@ -118,7 +112,7 @@
var/brightness = get_pin_data(IC_INPUT, 2)
if(new_color && isnum(brightness))
- brightness = CLAMP(brightness, 0, 6)
+ brightness = CLAMP(brightness, 0, 4)
light_rgb = new_color
light_brightness = brightness
@@ -159,10 +153,8 @@
return
vol = CLAMP(vol ,0 , 100)
playsound(get_turf(src), selected_sound, vol, freq, -1)
- if(assembly)
- assembly.investigate_log("played a sound ([selected_sound]) with [type].", INVESTIGATE_CIRCUIT)
- else
- investigate_log("played a sound ([selected_sound]) as [type].", INVESTIGATE_CIRCUIT)
+ var/atom/A = get_object()
+ A.investigate_log("played a sound ([selected_sound]) as [type].", INVESTIGATE_CIRCUIT)
/obj/item/integrated_circuit/output/sound/on_data_written()
power_draw_per_use = get_pin_data(IC_INPUT, 2) * 15
@@ -186,14 +178,14 @@
name = "securitron sound circuit"
desc = "Takes a sound name as an input, and will play said sound when pulsed. This circuit is similar to those used in Securitrons."
sounds = list(
- "creep" = 'sound/voice/bcreep.ogg',
- "criminal" = 'sound/voice/bcriminal.ogg',
- "freeze" = 'sound/voice/bfreeze.ogg',
- "god" = 'sound/voice/bgod.ogg',
- "i am the law" = 'sound/voice/biamthelaw.ogg',
- "insult" = 'sound/voice/binsult.ogg',
- "radio" = 'sound/voice/bradio.ogg',
- "secure day" = 'sound/voice/bsecureday.ogg',
+ "creep" = 'sound/voice/beepsky/creep.ogg',
+ "criminal" = 'sound/voice/beepsky/criminal.ogg',
+ "freeze" = 'sound/voice/beepsky/freeze.ogg',
+ "god" = 'sound/voice/beepsky/god.ogg',
+ "i am the law" = 'sound/voice/beepsky/iamthelaw.ogg',
+ "insult" = 'sound/voice/beepsky/insult.ogg',
+ "radio" = 'sound/voice/beepsky/radio.ogg',
+ "secure day" = 'sound/voice/beepsky/secureday.ogg',
)
spawn_flags = IC_SPAWN_RESEARCH
@@ -201,21 +193,21 @@
name = "medbot sound circuit"
desc = "Takes a sound name as an input, and will play said sound when pulsed. This circuit is often found in medical robots."
sounds = list(
- "surgeon" = 'sound/voice/msurgeon.ogg',
- "radar" = 'sound/voice/mradar.ogg',
- "feel better" = 'sound/voice/mfeelbetter.ogg',
- "patched up" = 'sound/voice/mpatchedup.ogg',
- "injured" = 'sound/voice/minjured.ogg',
- "insult" = 'sound/voice/minsult.ogg',
- "coming" = 'sound/voice/mcoming.ogg',
- "help" = 'sound/voice/mhelp.ogg',
- "live" = 'sound/voice/mlive.ogg',
- "lost" = 'sound/voice/mlost.ogg',
- "flies" = 'sound/voice/mflies.ogg',
- "catch" = 'sound/voice/mcatch.ogg',
- "delicious" = 'sound/voice/mdelicious.ogg',
- "apple" = 'sound/voice/mapple.ogg',
- "no" = 'sound/voice/mno.ogg',
+ "surgeon" = 'sound/voice/medbot/surgeon.ogg',
+ "radar" = 'sound/voice/medbot/radar.ogg',
+ "feel better" = 'sound/voice/medbot/feelbetter.ogg',
+ "patched up" = 'sound/voice/medbot/patchedup.ogg',
+ "injured" = 'sound/voice/medbot/injured.ogg',
+ "insult" = 'sound/voice/medbot/insult.ogg',
+ "coming" = 'sound/voice/medbot/coming.ogg',
+ "help" = 'sound/voice/medbot/help.ogg',
+ "live" = 'sound/voice/medbot/live.ogg',
+ "lost" = 'sound/voice/medbot/lost.ogg',
+ "flies" = 'sound/voice/medbot/flies.ogg',
+ "catch" = 'sound/voice/medbot/catch.ogg',
+ "delicious" = 'sound/voice/medbot/delicious.ogg',
+ "apple" = 'sound/voice/medbot/apple.ogg',
+ "no" = 'sound/voice/medbot/no.ogg',
)
spawn_flags = IC_SPAWN_RESEARCH
@@ -250,21 +242,22 @@
A.say(sanitized_text)
if (assembly)
log_say("[assembly] [REF(assembly)] : [sanitized_text]")
- else
+ else
log_say("[name] ([type]) : [sanitized_text]")
/obj/item/integrated_circuit/output/video_camera
name = "video camera circuit"
- desc = "Takes a string as a name and a boolean to determine whether it is on, and uses this to be a camera linked to the research network."
- extended_desc = "The camera is linked to the Research camera network."
+ desc = "Takes a string as a name and a boolean to determine whether it is on, and uses this to be a camera linked to a list of networks you choose."
+ extended_desc = "The camera is linked to a list of camera networks of your choosing. Common choices are 'rd' for the research network, 'ss13' for the main station network (visible to AI), 'mine' for the mining network, and 'thunder' for the thunderdome network (viewable from bar)."
icon_state = "video_camera"
- w_class = WEIGHT_CLASS_SMALL
+ w_class = WEIGHT_CLASS_TINY
complexity = 10
inputs = list(
"camera name" = IC_PINTYPE_STRING,
- "camera active" = IC_PINTYPE_BOOLEAN
+ "camera active" = IC_PINTYPE_BOOLEAN,
+ "camera network" = IC_PINTYPE_LIST
)
- inputs_default = list("1" = "video camera circuit")
+ inputs_default = list("1" = "video camera circuit", "3" = list("rd"))
outputs = list()
activators = list()
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
@@ -296,8 +289,11 @@
if(camera)
var/cam_name = get_pin_data(IC_INPUT, 1)
var/cam_active = get_pin_data(IC_INPUT, 2)
+ var/list/new_network = get_pin_data(IC_INPUT, 3)
if(!isnull(cam_name))
camera.c_tag = cam_name
+ if(!isnull(new_network))
+ camera.network = new_network
set_camera_status(cam_active)
/obj/item/integrated_circuit/output/video_camera/power_fail()
diff --git a/code/modules/integrated_electronics/subtypes/reagents.dm b/code/modules/integrated_electronics/subtypes/reagents.dm
index 2a6b4a1a80..b8f326f389 100644
--- a/code/modules/integrated_electronics/subtypes/reagents.dm
+++ b/code/modules/integrated_electronics/subtypes/reagents.dm
@@ -16,59 +16,6 @@
set_pin_data(IC_OUTPUT, 1, reagents.total_volume)
push_data()
-/obj/item/integrated_circuit/reagent/smoke
- name = "smoke generator"
- desc = "Unlike most electronics, creating smoke is completely intentional."
- icon_state = "smoke"
- extended_desc = "This smoke generator creates clouds of smoke on command. It can also hold liquids inside, which will go \
- into the smoke clouds when activated. The reagents are consumed when the smoke is made."
- ext_cooldown = 1
- container_type = OPENCONTAINER
- volume = 100
-
- complexity = 20
- cooldown_per_use = 1 SECONDS
- inputs = list()
- outputs = list(
- "volume used" = IC_PINTYPE_NUMBER,
- "self reference" = IC_PINTYPE_REF
- )
- activators = list(
- "create smoke" = IC_PINTYPE_PULSE_IN,
- "on smoked" = IC_PINTYPE_PULSE_OUT,
- "push ref" = IC_PINTYPE_PULSE_IN
- )
- spawn_flags = IC_SPAWN_RESEARCH
- power_draw_per_use = 20
- var/smoke_radius = 5
- var/notified = FALSE
-
-/obj/item/integrated_circuit/reagent/smoke/on_reagent_change(changetype)
- //reset warning only if we have reagents now
- if(changetype == ADD_REAGENT)
- notified = FALSE
- push_vol()
-
-/obj/item/integrated_circuit/reagent/smoke/do_work(ord)
- switch(ord)
- if(1)
- if(!reagents || (reagents.total_volume < IC_SMOKE_REAGENTS_MINIMUM_UNITS))
- return
- var/location = get_turf(src)
- var/datum/effect_system/smoke_spread/chem/S = new
- S.attach(location)
- playsound(location, 'sound/effects/smoke.ogg', 50, 1, -3)
- if(S)
- S.set_up(reagents, smoke_radius, location, notified)
- if(!notified)
- notified = TRUE
- S.start()
- reagents.clear_reagents()
- activate_pin(2)
- if(3)
- set_pin_data(IC_OUTPUT, 2, WEAKREF(src))
- push_data()
-
// Hydroponics trays have no reagents holder and handle reagents in their own snowflakey way.
// This is a dirty hack to make injecting reagents into them work.
// TODO: refactor that.
@@ -118,7 +65,7 @@
)
outputs = list(
"volume used" = IC_PINTYPE_NUMBER,
- "self reference" = IC_PINTYPE_REF
+ "self reference" = IC_PINTYPE_SELFREF
)
activators = list(
"inject" = IC_PINTYPE_PULSE_IN,
@@ -186,7 +133,7 @@
//Always log attemped injections for admins
var/contained = reagents.log_list()
- add_logs(src, L, "attempted to inject", addition="which had [contained]")
+ log_combat(src, L, "attempted to inject", addition="which had [contained]")
L.visible_message("[acting_object] is trying to inject [L]! ", \
"[acting_object] is trying to inject you! ")
busy = TRUE
@@ -194,7 +141,7 @@
var/fraction = min(transfer_amount/reagents.total_volume, 1)
reagents.reaction(L, INJECT, fraction)
reagents.trans_to(L, transfer_amount)
- add_logs(src, L, "injected", addition="which had [contained]")
+ log_combat(src, L, "injected", addition="which had [contained]")
L.visible_message("[acting_object] injects [L] with its needle! ", \
"[acting_object] injects you with its needle! ")
else
@@ -320,7 +267,7 @@
inputs = list()
outputs = list(
"volume used" = IC_PINTYPE_NUMBER,
- "self reference" = IC_PINTYPE_REF
+ "self reference" = IC_PINTYPE_SELFREF
)
activators = list("push ref" = IC_PINTYPE_PULSE_OUT)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
@@ -367,7 +314,7 @@
)
outputs = list(
"volume used" = IC_PINTYPE_NUMBER,
- "self reference" = IC_PINTYPE_REF
+ "self reference" = IC_PINTYPE_SELFREF
)
activators = list(
"grind" = IC_PINTYPE_PULSE_IN,
@@ -414,7 +361,7 @@
)
outputs = list(
"volume used" = IC_PINTYPE_NUMBER,
- "self reference" = IC_PINTYPE_REF
+ "self reference" = IC_PINTYPE_SELFREF
)
activators = list(
"juice" = IC_PINTYPE_PULSE_IN,
@@ -459,7 +406,7 @@
complexity = 8
outputs = list(
"volume used" = IC_PINTYPE_NUMBER,
- "self reference" = IC_PINTYPE_REF,
+ "self reference" = IC_PINTYPE_SELFREF,
"list of reagents" = IC_PINTYPE_LIST
)
activators = list(
@@ -561,7 +508,7 @@
"on" = IC_PINTYPE_BOOLEAN
)
inputs_default = list("1" = 300)
- outputs = list("volume used" = IC_PINTYPE_NUMBER,"self reference" = IC_PINTYPE_REF,"temperature" = IC_PINTYPE_NUMBER)
+ outputs = list("volume used" = IC_PINTYPE_NUMBER,"self reference" = IC_PINTYPE_SELFREF,"temperature" = IC_PINTYPE_NUMBER)
spawn_flags = IC_SPAWN_RESEARCH
var/heater_coefficient = 0.1
diff --git a/code/modules/integrated_electronics/subtypes/smart.dm b/code/modules/integrated_electronics/subtypes/smart.dm
index 4445c1e1f3..bd93ae8314 100644
--- a/code/modules/integrated_electronics/subtypes/smart.dm
+++ b/code/modules/integrated_electronics/subtypes/smart.dm
@@ -95,12 +95,7 @@
if(!assembly)
activate_pin(3)
return
- var/Ps = get_pin_data(IC_INPUT, 4)
- if(!Ps)
- return
- var/list/Pl = json_decode(XorEncrypt(hextostr(Ps, TRUE), SScircuit.cipherkey))
- if(Pl&&islist(Pl))
- idc.access = Pl
+ idc.access = assembly.access_card.access
var/turf/a_loc = get_turf(assembly)
var/list/P = cir_get_path_to(assembly, locate(get_pin_data(IC_INPUT, 1),get_pin_data(IC_INPUT, 2),a_loc.z), /turf/proc/Distance_cardinal, 0, 200, id=idc, exclude=get_turf(get_pin_data_as_type(IC_INPUT,3, /atom)), simulated_only = 0)
diff --git a/code/modules/integrated_electronics/subtypes/text.dm b/code/modules/integrated_electronics/subtypes/text.dm
new file mode 100644
index 0000000000..bcc3d94766
--- /dev/null
+++ b/code/modules/integrated_electronics/subtypes/text.dm
@@ -0,0 +1,29 @@
+/obj/item/integrated_circuit/text
+ name = "text thingy"
+ desc = "Does text-processing related things."
+ category_text = "Text"
+ complexity = 1
+
+// - Text Replacer - //
+/obj/item/integrated_circuit/text/text_replacer
+ name = "find-replace circuit"
+ desc = "Replaces all of one bit of text with another"
+ extended_desc = "Takes a string(haystack) and puts out the string while having a certain word(needle) replaced with another."
+ spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
+ inputs = list(
+ "haystack" = IC_PINTYPE_STRING,
+ "needle" = IC_PINTYPE_STRING,
+ "replacement" = IC_PINTYPE_STRING
+ )
+ activators = list(
+ "replace" = IC_PINTYPE_PULSE_IN,
+ "on replaced" = IC_PINTYPE_PULSE_OUT
+ )
+ outputs = list(
+ "replaced string" = IC_PINTYPE_STRING
+ )
+
+/obj/item/integrated_circuit/text/text_replacer/do_work()
+ set_pin_data(IC_OUTPUT, 1,replacetext(get_pin_data(IC_INPUT, 1), get_pin_data(IC_INPUT, 2), get_pin_data(IC_INPUT, 3)))
+ push_data()
+ activate_pin(2)
diff --git a/code/modules/integrated_electronics/subtypes/time.dm b/code/modules/integrated_electronics/subtypes/time.dm
index f72f5dbd74..9f3265ed40 100644
--- a/code/modules/integrated_electronics/subtypes/time.dm
+++ b/code/modules/integrated_electronics/subtypes/time.dm
@@ -142,25 +142,44 @@
power_draw_per_use = 2
/obj/item/integrated_circuit/time/clock
- name = "integrated clock"
- desc = "Tells you what the local time is, specific to your station or planet."
+ name = "integrated clock (NT Common Time)"
+ desc = "Tells you what the time is, in Nanotrasen Common Time." //round time
icon_state = "clock"
inputs = list()
outputs = list(
"time" = IC_PINTYPE_STRING,
"hours" = IC_PINTYPE_NUMBER,
"minutes" = IC_PINTYPE_NUMBER,
- "seconds" = IC_PINTYPE_NUMBER
+ "seconds" = IC_PINTYPE_NUMBER,
+ "absolute decisecond elapsed time" = IC_PINTYPE_NUMBER
)
activators = list("get time" = IC_PINTYPE_PULSE_IN, "on time got" = IC_PINTYPE_PULSE_OUT)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
- power_draw_per_use = 4
+ power_draw_per_use = 2
+
+/obj/item/integrated_circuit/time/clock/proc/get_time()
+ return world.time
/obj/item/integrated_circuit/time/clock/do_work()
- var/wtime = world.time
- set_pin_data(IC_OUTPUT, 1, time2text(wtime, "hh:mm:ss") )
- set_pin_data(IC_OUTPUT, 2, text2num(time2text(wtime, "hh") ) )
- set_pin_data(IC_OUTPUT, 3, text2num(time2text(wtime, "mm") ) )
- set_pin_data(IC_OUTPUT, 4, text2num(time2text(wtime, "ss") ) )
+ var/current_time = get_time()
+ set_pin_data(IC_OUTPUT, 1, time2text(current_time, "hh:mm:ss") )
+ set_pin_data(IC_OUTPUT, 2, text2num(time2text(current_time, "hh") ) )
+ set_pin_data(IC_OUTPUT, 3, text2num(time2text(current_time, "mm") ) )
+ set_pin_data(IC_OUTPUT, 4, text2num(time2text(current_time, "ss") ) )
+ set_pin_data(IC_OUTPUT, 5, current_time)
push_data()
activate_pin(2)
+
+/obj/item/integrated_circuit/time/clock/station
+ name = "integrated clock (Station Time)"
+ desc = "Tells you what the time is, in terms and adjusted for your local station or planet"
+
+/obj/item/integrated_circuit/time/clock/station/get_time()
+ return station_time()
+
+/obj/item/integrated_circuit/time/clock/bluespace
+ name = "integrated clock (Bluespace Absolute Time)"
+ desc = "Tells you what the time is, in Bluespace Absolute Time, unaffected by local time dilation or other phenomenon."
+
+/obj/item/integrated_circuit/time/clock/bluespace/get_time()
+ return REALTIMEOFDAY
diff --git a/code/modules/jobs/access.dm b/code/modules/jobs/access.dm
index 63e6962a74..e88a80514a 100644
--- a/code/modules/jobs/access.dm
+++ b/code/modules/jobs/access.dm
@@ -11,7 +11,7 @@
if(IsAdminGhost(M))
//Access can't stop the abuse
return TRUE
- else if(SEND_SIGNAL(M, COMSIG_MOB_ALLOWED, src))
+ else if(istype(M) && SEND_SIGNAL(M, COMSIG_MOB_ALLOWED, src))
return TRUE
else if(ishuman(M))
var/mob/living/carbon/human/H = M
diff --git a/code/modules/jobs/job_exp.dm b/code/modules/jobs/job_exp.dm
index 6a5a857ef0..4b7b175240 100644
--- a/code/modules/jobs/job_exp.dm
+++ b/code/modules/jobs/job_exp.dm
@@ -155,7 +155,7 @@ GLOBAL_PROTECT(exp_to_update)
if(!SSdbcore.Connect())
return -1
var/datum/DBQuery/exp_read = SSdbcore.NewQuery("SELECT job, minutes FROM [format_table_name("role_time")] WHERE ckey = '[sanitizeSQL(ckey)]'")
- if(!exp_read.Execute())
+ if(!exp_read.Execute(async = TRUE))
qdel(exp_read)
return -1
var/list/play_records = list()
@@ -172,7 +172,6 @@ GLOBAL_PROTECT(exp_to_update)
prefs.exp = play_records
-
//updates player db flags
/client/proc/update_flag_db(newflag, state = FALSE)
@@ -269,7 +268,7 @@ GLOBAL_PROTECT(exp_to_update)
var/datum/DBQuery/flags_read = SSdbcore.NewQuery("SELECT flags FROM [format_table_name("player")] WHERE ckey='[ckey]'")
- if(!flags_read.Execute())
+ if(!flags_read.Execute(async = TRUE))
qdel(flags_read)
return FALSE
diff --git a/code/modules/jobs/job_types/captain.dm b/code/modules/jobs/job_types/captain.dm
index 988eca631c..94cf27749e 100755
--- a/code/modules/jobs/job_types/captain.dm
+++ b/code/modules/jobs/job_types/captain.dm
@@ -52,6 +52,13 @@ Captain
chameleon_extras = list(/obj/item/gun/energy/e_gun, /obj/item/stamp/captain)
+/datum/outfit/job/captain/hardsuit
+ name = "Captain (Hardsuit)"
+
+ mask = /obj/item/clothing/mask/gas/sechailer
+ suit = /obj/item/clothing/suit/space/hardsuit/captain
+ suit_store = /obj/item/tank/internals/oxygen
+
/*
Head of Personnel
*/
diff --git a/code/modules/jobs/job_types/civilian.dm b/code/modules/jobs/job_types/civilian.dm
index 8ec25e5f5e..944499470a 100644
--- a/code/modules/jobs/job_types/civilian.dm
+++ b/code/modules/jobs/job_types/civilian.dm
@@ -154,7 +154,6 @@ Curator
return
H.grant_all_languages(omnitongue=TRUE)
- H.gain_trauma(/datum/brain_trauma/mild/phobia, TRAUMA_RESILIENCE_SURGERY, "snakes") //why does it have to be snakes...
/*
Lawyer
*/
diff --git a/code/modules/jobs/job_types/engineering.dm b/code/modules/jobs/job_types/engineering.dm
index 171a437bb1..f28e5f1afc 100644
--- a/code/modules/jobs/job_types/engineering.dm
+++ b/code/modules/jobs/job_types/engineering.dm
@@ -57,6 +57,7 @@ Chief Engineer
suit = /obj/item/clothing/suit/space/hardsuit/engine/elite
shoes = /obj/item/clothing/shoes/magboots/advance
suit_store = /obj/item/tank/internals/oxygen
+ glasses = /obj/item/clothing/glasses/meson/engine
gloves = /obj/item/clothing/gloves/color/yellow
head = null
internals_slot = SLOT_S_STORE
diff --git a/code/modules/jobs/job_types/job.dm b/code/modules/jobs/job_types/job.dm
index 92f4ccbdd9..ea5573b89f 100644
--- a/code/modules/jobs/job_types/job.dm
+++ b/code/modules/jobs/job_types/job.dm
@@ -38,9 +38,6 @@
//If this is set to 1, a text is printed to the player when jobs are assigned, telling him that he should let admins know that he has to disconnect.
var/req_admin_notify
- //Allows defining arbitrary spawn text for the job
- var/custom_spawn_text
-
//If you have the use_age_restriction_for_jobs config option enabled and the database set up, this option will add a requirement for players to be at least minimal_player_age days old. (meaning they first signed in at least that many days before.)
var/minimal_player_age = 0
@@ -72,12 +69,12 @@
return TRUE
/datum/job/proc/GetAntagRep()
- . = CONFIG_GET(keyed_number_list/antag_rep)[lowertext(title)]
+ . = CONFIG_GET(keyed_list/antag_rep)[lowertext(title)]
if(. == null)
return antag_rep
//Don't override this unless the job transforms into a non-human (Silicons do this for example)
-/datum/job/proc/equip(mob/living/carbon/human/H, visualsOnly = FALSE, announce = TRUE, latejoin = FALSE)
+/datum/job/proc/equip(mob/living/carbon/human/H, visualsOnly = FALSE, announce = TRUE, latejoin = FALSE, datum/outfit/outfit_override = null)
if(!H)
return FALSE
@@ -85,13 +82,12 @@
if(H.dna.species.id != "human")
H.set_species(/datum/species/human)
H.apply_pref_name("human", H.client)
- purrbation_remove(H, silent=TRUE)
//Equip the rest of the gear
H.dna.species.before_equip_job(src, H, visualsOnly)
- if(outfit)
- H.equipOutfit(outfit, visualsOnly)
+ if(outfit_override || outfit)
+ H.equipOutfit(outfit_override ? outfit_override : outfit, visualsOnly)
H.dna.species.after_equip_job(src, H, visualsOnly)
diff --git a/code/modules/jobs/job_types/medical.dm b/code/modules/jobs/job_types/medical.dm
index 2ab5da36aa..5a926f490a 100644
--- a/code/modules/jobs/job_types/medical.dm
+++ b/code/modules/jobs/job_types/medical.dm
@@ -48,6 +48,14 @@ Chief Medical Officer
chameleon_extras = list(/obj/item/gun/syringe, /obj/item/stamp/cmo)
+/datum/outfit/job/cmo/hardsuit
+ name = "Chief Medical Officer (Hardsuit)"
+
+ mask = /obj/item/clothing/mask/breath
+ suit = /obj/item/clothing/suit/space/hardsuit/medical
+ suit_store = /obj/item/tank/internals/oxygen
+ r_pocket = /obj/item/flashlight/pen
+
/*
Medical Doctor
*/
diff --git a/code/modules/jobs/job_types/security.dm b/code/modules/jobs/job_types/security.dm
index 252b51301c..c69a5873b1 100644
--- a/code/modules/jobs/job_types/security.dm
+++ b/code/modules/jobs/job_types/security.dm
@@ -62,6 +62,14 @@ Head of Security
chameleon_extras = list(/obj/item/gun/energy/e_gun/hos, /obj/item/stamp/hos)
+/datum/outfit/job/hos/hardsuit
+ name = "Head of Security (Hardsuit)"
+
+ mask = /obj/item/clothing/mask/gas/sechailer
+ suit = /obj/item/clothing/suit/space/hardsuit/security/hos
+ suit_store = /obj/item/tank/internals/oxygen
+ backpack_contents = list(/obj/item/melee/baton/loaded=1, /obj/item/gun/energy/e_gun=1)
+
/*
Warden
*/
@@ -162,7 +170,8 @@ Detective
/datum/outfit/job/detective/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
..()
var/obj/item/clothing/mask/cigarette/cig = H.wear_mask
- cig.light("")
+ if(istype(cig)) //Some species specfic changes can mess this up (plasmamen)
+ cig.light("")
if(visualsOnly)
return
diff --git a/code/modules/jobs/job_types/silicon.dm b/code/modules/jobs/job_types/silicon.dm
index 0738f20b24..662f666cf1 100644
--- a/code/modules/jobs/job_types/silicon.dm
+++ b/code/modules/jobs/job_types/silicon.dm
@@ -17,7 +17,7 @@ AI
exp_type_department = EXP_TYPE_SILICON
var/do_special_check = TRUE
-/datum/job/ai/equip(mob/living/carbon/human/H, visualsOnly, announce, latejoin)
+/datum/job/ai/equip(mob/living/carbon/human/H, visualsOnly, announce, latejoin, outfit_override)
. = H.AIize(latejoin)
/datum/job/ai/after_spawn(mob/H, mob/M, latejoin)
@@ -81,8 +81,9 @@ Cyborg
exp_requirements = 120
exp_type = EXP_TYPE_CREW
-/datum/job/cyborg/equip(mob/living/carbon/human/H, visualsOnly = FALSE, announce = TRUE, latejoin = FALSE)
+/datum/job/cyborg/equip(mob/living/carbon/human/H, visualsOnly = FALSE, announce = TRUE, latejoin = FALSE, outfit_override = null)
return H.Robotize(FALSE, latejoin)
/datum/job/cyborg/after_spawn(mob/living/silicon/robot/R, mob/M)
R.updatename(M.client)
+ R.gender = NEUTER
diff --git a/code/modules/keybindings/readme.md b/code/modules/keybindings/readme.md
index 6c0369d7c7..f57d8d55ff 100644
--- a/code/modules/keybindings/readme.md
+++ b/code/modules/keybindings/readme.md
@@ -1,7 +1,8 @@
# In-code keypress handling system
This whole system is heavily based off of forum_account's keyboard library.
-Thanks to forum_account for saving the day, the library can be found [here](https://secure.byond.com/developer/Forum_account/Keyboard)!
+Thanks to forum_account for saving the day, the library can be found
+[here](https://secure.byond.com/developer/Forum_account/Keyboard)!
.dmf macros have some very serious shortcomings. For example, they do not allow reusing parts
of one macro in another, so giving cyborgs their own shortcuts to swap active module couldn't
@@ -30,10 +31,11 @@ pressed.
No client-set keybindings at this time, but it shouldn't be too hard if someone wants.
-Notes about certain keys
-`Tab` has client-sided behavior but acts normally
-`T`, `O`, and `M` move focus to the input when pressed. This fires the keyUp macro right away.
-`\` needs to be escaped in the dmf so any usage is `\\`
+Notes about certain keys:
+
+* `Tab` has client-sided behavior but acts normally
+* `T`, `O`, and `M` move focus to the input when pressed. This fires the keyUp macro right away.
+* `\` needs to be escaped in the dmf so any usage is `\\`
You cannot `TICK_CHECK` or check `world.tick_usage` inside of procs called by key down and up
events. They happen outside of a byond tick and have no meaning there. Key looping
diff --git a/code/modules/language/narsian.dm b/code/modules/language/narsian.dm
index 70966ad880..42d0a06531 100644
--- a/code/modules/language/narsian.dm
+++ b/code/modules/language/narsian.dm
@@ -1,6 +1,6 @@
/datum/language/narsie
- name = "Nar-Sian"
- desc = "The ancient, blood-soaked, impossibly complex language of Nar-Sian cultists."
+ name = "Nar'Sian"
+ desc = "The ancient, blood-soaked, impossibly complex language of Nar'Sian cultists."
speech_verb = "intones"
ask_verb = "inquires"
exclaim_verb = "invokes"
diff --git a/code/modules/library/lib_items.dm b/code/modules/library/lib_items.dm
index 066ce22831..ee563f3100 100644
--- a/code/modules/library/lib_items.dm
+++ b/code/modules/library/lib_items.dm
@@ -149,7 +149,7 @@
/obj/structure/bookcase/manuals/medical/Initialize()
. = ..()
- new /obj/item/book/manual/medical_cloning(src)
+ new /obj/item/book/manual/wiki/medical_cloning(src)
update_icon()
@@ -159,11 +159,10 @@
/obj/structure/bookcase/manuals/engineering/Initialize()
. = ..()
new /obj/item/book/manual/wiki/engineering_construction(src)
- new /obj/item/book/manual/engineering_particle_accelerator(src)
new /obj/item/book/manual/wiki/engineering_hacking(src)
new /obj/item/book/manual/wiki/engineering_guide(src)
- new /obj/item/book/manual/engineering_singularity_safety(src)
- new /obj/item/book/manual/robotics_cyborgs(src)
+ new /obj/item/book/manual/wiki/engineering_singulo_tesla(src)
+ new /obj/item/book/manual/wiki/robotics_cyborgs(src)
update_icon()
@@ -172,7 +171,7 @@
/obj/structure/bookcase/manuals/research_and_development/Initialize()
. = ..()
- new /obj/item/book/manual/research_and_development(src)
+ new /obj/item/book/manual/wiki/research_and_development(src)
update_icon()
diff --git a/code/modules/library/lib_machines.dm b/code/modules/library/lib_machines.dm
index 5f686699d2..8ae63a8a76 100644
--- a/code/modules/library/lib_machines.dm
+++ b/code/modules/library/lib_machines.dm
@@ -166,11 +166,12 @@ GLOBAL_LIST(cachedbooks) // List of our cached book datums
/obj/machinery/computer/libraryconsole/bookmanagement
name = "book inventory management console"
desc = "Librarian's command station."
- var/arcanecheckout = 0
screenstate = 0 // 0 - Main Menu, 1 - Inventory, 2 - Checked Out, 3 - Check Out a Book
verb_say = "beeps"
verb_ask = "beeps"
verb_exclaim = "beeps"
+ pass_flags = PASSTABLE
+ var/arcanecheckout = 0
var/buffer_book
var/buffer_mob
var/upload_category = "Fiction"
@@ -424,7 +425,7 @@ GLOBAL_LIST(cachedbooks) // List of our cached book datums
var/sqlcategory = sanitizeSQL(upload_category)
var/sqlckey = sanitizeSQL(usr.ckey)
var/msg = "[key_name(usr)] has uploaded the book titled [scanner.cache.name], [length(scanner.cache.dat)] signs"
- var/datum/DBQuery/query_library_upload = SSdbcore.NewQuery("INSERT INTO [format_table_name("library")] (author, title, content, category, ckey, datetime, round_id_created) VALUES ('[sqlauthor]', '[sqltitle]', '[sqlcontent]', '[sqlcategory], '[sqlckey]', Now(), '[GLOB.round_id]')")
+ var/datum/DBQuery/query_library_upload = SSdbcore.NewQuery("INSERT INTO [format_table_name("library")] (author, title, content, category, ckey, datetime, round_id_created) VALUES ('[sqlauthor]', '[sqltitle]', '[sqlcontent]', '[sqlcategory]', '[sqlckey]', Now(), '[GLOB.round_id]')")
if(!query_library_upload.Execute())
qdel(query_library_upload)
alert("Database error encountered uploading to Archive")
diff --git a/code/modules/library/soapstone.dm b/code/modules/library/soapstone.dm
index e51103937f..88d1248a28 100644
--- a/code/modules/library/soapstone.dm
+++ b/code/modules/library/soapstone.dm
@@ -19,6 +19,7 @@
to_chat(user, "It has [remaining_uses] uses left.")
/obj/item/soapstone/afterattack(atom/target, mob/user, proximity)
+ . = ..()
var/turf/T = get_turf(target)
if(!proximity)
return
diff --git a/code/modules/lighting/lighting_atom.dm b/code/modules/lighting/lighting_atom.dm
index 89ec6a3a7e..4a617ab073 100644
--- a/code/modules/lighting/lighting_atom.dm
+++ b/code/modules/lighting/lighting_atom.dm
@@ -92,14 +92,17 @@
switch (var_name)
if ("light_range")
set_light(l_range=var_value)
- return
+ datum_flags |= DF_VAR_EDITED
+ return TRUE
if ("light_power")
set_light(l_power=var_value)
- return
+ datum_flags |= DF_VAR_EDITED
+ return TRUE
if ("light_color")
set_light(l_color=var_value)
- return
+ datum_flags |= DF_VAR_EDITED
+ return TRUE
return ..()
diff --git a/code/modules/lighting/lighting_corner.dm b/code/modules/lighting/lighting_corner.dm
index 9b562a276d..1de896520d 100644
--- a/code/modules/lighting/lighting_corner.dm
+++ b/code/modules/lighting/lighting_corner.dm
@@ -138,6 +138,6 @@ GLOBAL_LIST_INIT(LIGHTING_CORNER_DIAGONAL, list(NORTHEAST, SOUTHEAST, SOUTHWEST,
if (!force)
return QDEL_HINT_LETMELIVE
- stack_trace("Ok, Look, TG, I need you to find whatever fucker decided to call qdel on a fucking lighting corner, then tell him very nicely and politely that he is 100% retarded and needs his head checked. Thanks. Send them my regards by the way.")
+ stack_trace("Ok, Look, /tg/, I need you to find whatever fucker decided to call qdel on a fucking lighting corner, then tell him very nicely and politely that he is 100% retarded and needs his head checked. Thanks. Send them my regards by the way.")
return ..()
diff --git a/code/modules/mapping/README.txt b/code/modules/mapping/README.txt
new file mode 100644
index 0000000000..75e57efa8b
--- /dev/null
+++ b/code/modules/mapping/README.txt
@@ -0,0 +1,52 @@
+The code in this module originally evolved from dmm_suite and has since been
+specialized for SS13 and otherwise tweaked to fit /tg/station's needs.
+
+dmm_suite version 1.0
+ Released January 30th, 2011.
+
+NOTE: Map saving functionality removed
+
+defines the object /dmm_suite
+ - Provides the proc load_map()
+ - Loads the specified map file onto the specified z-level.
+ - provides the proc write_map()
+ - Returns a text string of the map in dmm format
+ ready for output to a file.
+ - provides the proc save_map()
+ - Returns a .dmm file if map is saved
+ - Returns FALSE if map fails to save
+
+The dmm_suite provides saving and loading of map files in BYOND's native DMM map
+format. It approximates the map saving and loading processes of the Dream Maker
+and Dream Seeker programs so as to allow editing, saving, and loading of maps at
+runtime.
+
+------------------------
+
+To save a map at runtime, create an instance of /dmm_suite, and then call
+write_map(), which accepts three arguments:
+ - A turf representing one corner of a three dimensional grid (Required).
+ - Another turf representing the other corner of the same grid (Required).
+ - Any, or a combination, of several bit flags (Optional, see documentation).
+
+The order in which the turfs are supplied does not matter, the /dmm_writer will
+determine the grid containing both, in much the same way as DM's block() function.
+write_map() will then return a string representing the saved map in dmm format;
+this string can then be saved to a file, or used for any other purose.
+
+------------------------
+
+To load a map at runtime, create an instance of /dmm_suite, and then call load_map(),
+which accepts two arguments:
+ - A .dmm file to load (Required).
+ - A number representing the z-level on which to start loading the map (Optional).
+
+The /dmm_suite will load the map file starting on the specified z-level. If no
+z-level was specified, world.maxz will be increased so as to fit the map. Note
+that if you wish to load a map onto a z-level that already has objects on it,
+you will have to handle the removal of those objects. Otherwise the new map will
+simply load the new objects on top of the old ones.
+
+Also note that all type paths specified in the .dmm file must exist in the world's
+code, and that the /dmm_reader trusts that files to be loaded are in fact valid
+.dmm files. Errors in the .dmm format will cause runtime errors.
diff --git a/code/modules/mapping/map_template.dm b/code/modules/mapping/map_template.dm
index 2cc5a74b6b..ceba29baec 100644
--- a/code/modules/mapping/map_template.dm
+++ b/code/modules/mapping/map_template.dm
@@ -4,24 +4,28 @@
var/height = 0
var/mappath = null
var/loaded = 0 // Times loaded this round
- var/static/dmm_suite/maploader = new
+ var/datum/parsed_map/cached_map
+ var/keep_cached_map = FALSE
-/datum/map_template/New(path = null, rename = null)
+/datum/map_template/New(path = null, rename = null, cache = FALSE)
if(path)
mappath = path
if(mappath)
- preload_size(mappath)
+ preload_size(mappath, cache)
if(rename)
name = rename
-/datum/map_template/proc/preload_size(path)
- var/bounds = maploader.load_map(file(path), 1, 1, 1, cropMap=FALSE, measureOnly=TRUE)
+/datum/map_template/proc/preload_size(path, cache = FALSE)
+ var/datum/parsed_map/parsed = new(file(path))
+ var/bounds = parsed?.bounds
if(bounds)
width = bounds[MAP_MAXX] // Assumes all templates are rectangular, have a single Z level, and begin at 1,1,1
height = bounds[MAP_MAXY]
+ if(cache)
+ cached_map = parsed
return bounds
-/datum/map_template/proc/initTemplateBounds(var/list/bounds)
+/datum/parsed_map/proc/initTemplateBounds()
var/list/obj/machinery/atmospherics/atmos_machines = list()
var/list/obj/structure/cable/cables = list()
var/list/atom/atoms = list()
@@ -53,14 +57,15 @@
var/y = round((world.maxy - height)/2)
var/datum/space_level/level = SSmapping.add_new_zlevel(name, list(ZTRAIT_AWAY = TRUE))
- var/list/bounds = maploader.load_map(file(mappath), x, y, level.z_value, no_changeturf=(SSatoms.initialized == INITIALIZATION_INSSATOMS), placeOnTop=TRUE)
+ var/datum/parsed_map/parsed = load_map(file(mappath), x, y, level.z_value, no_changeturf=(SSatoms.initialized == INITIALIZATION_INSSATOMS), placeOnTop=TRUE)
+ var/list/bounds = parsed.bounds
if(!bounds)
return FALSE
repopulate_sorted_areas()
//initialize things that are normally initialized after map load
- initTemplateBounds(bounds)
+ parsed.initTemplateBounds()
smooth_zlevel(world.maxz)
log_game("Z-level [name] loaded at at [x],[y],[world.maxz]")
@@ -76,7 +81,13 @@
if(T.y+height > world.maxy)
return
- var/list/bounds = maploader.load_map(file(mappath), T.x, T.y, T.z, cropMap=TRUE, no_changeturf=(SSatoms.initialized == INITIALIZATION_INSSATOMS), placeOnTop=TRUE)
+ // Accept cached maps, but don't save them automatically - we don't want
+ // ruins clogging up memory for the whole round.
+ var/datum/parsed_map/parsed = cached_map || new(file(mappath))
+ cached_map = keep_cached_map ? parsed : null
+ if(!parsed.load(T.x, T.y, T.z, cropMap=TRUE, no_changeturf=(SSatoms.initialized == INITIALIZATION_INSSATOMS), placeOnTop=TRUE))
+ return
+ var/list/bounds = parsed.bounds
if(!bounds)
return
@@ -84,7 +95,7 @@
repopulate_sorted_areas()
//initialize things that are normally initialized after map load
- initTemplateBounds(bounds)
+ parsed.initTemplateBounds()
log_game("[name] loaded at at [T.x],[T.y],[T.z]")
return bounds
diff --git a/code/modules/mapping/mapping_helpers.dm b/code/modules/mapping/mapping_helpers.dm
index 8a7f5cb91e..0ddbe8e1e4 100644
--- a/code/modules/mapping/mapping_helpers.dm
+++ b/code/modules/mapping/mapping_helpers.dm
@@ -133,6 +133,21 @@
else
log_world("### MAP WARNING, [src] failed to find an airlock at [AREACOORD(src)]")
+/obj/effect/mapping_helpers/airlock/unres
+ name = "airlock unresctricted side helper"
+ icon_state = "airlock_unres_helper"
+
+/obj/effect/mapping_helpers/airlock/unres/Initialize(mapload)
+ . = ..()
+ if(!mapload)
+ log_world("### MAP WARNING, [src] spawned outside of mapload!")
+ return
+ var/obj/machinery/door/airlock/airlock = locate(/obj/machinery/door/airlock) in loc
+ if(airlock)
+ airlock.unres_sides ^= dir
+ else
+ log_world("### MAP WARNING, [src] failed to find an airlock at [AREACOORD(src)]")
+
//needs to do its thing before spawn_rivers() is called
INITIALIZE_IMMEDIATE(/obj/effect/mapping_helpers/no_lava)
@@ -145,17 +160,15 @@ INITIALIZE_IMMEDIATE(/obj/effect/mapping_helpers/no_lava)
var/turf/T = get_turf(src)
T.flags_1 |= NO_LAVA_GEN_1
-//Contains the list of planetary z-levels defined by the planet_z helper.
-GLOBAL_LIST_EMPTY(z_is_planet)
-
-/obj/effect/mapping_helpers/planet_z //adds the map it is on to the z_is_planet list
+/// Adds the map it is on to the z_is_planet list
+/obj/effect/mapping_helpers/planet_z
name = "planet z helper"
layer = POINT_LAYER
/obj/effect/mapping_helpers/planet_z/Initialize()
. = ..()
- var/turf/T = get_turf(src)
- GLOB.z_is_planet["[T.z]"] = TRUE
+ var/datum/space_level/S = SSmapping.get_level(z)
+ S.traits[ZTRAIT_PLANET] = TRUE
//This helper applies components to things on the map directly.
diff --git a/code/modules/mapping/preloader.dm b/code/modules/mapping/preloader.dm
new file mode 100644
index 0000000000..e6fa2421a0
--- /dev/null
+++ b/code/modules/mapping/preloader.dm
@@ -0,0 +1,31 @@
+// global datum that will preload variables on atoms instanciation
+GLOBAL_VAR_INIT(use_preloader, FALSE)
+GLOBAL_DATUM_INIT(_preloader, /datum/map_preloader, new)
+
+/// Preloader datum
+/datum/map_preloader
+ parent_type = /datum
+ var/list/attributes
+ var/target_path
+
+/datum/map_preloader/proc/setup(list/the_attributes, path)
+ if(the_attributes.len)
+ GLOB.use_preloader = TRUE
+ attributes = the_attributes
+ target_path = path
+
+/datum/map_preloader/proc/load(atom/what)
+ GLOB.use_preloader = FALSE
+ for(var/attribute in attributes)
+ var/value = attributes[attribute]
+ if(islist(value))
+ value = deepCopyList(value)
+ what.vars[attribute] = value
+
+/area/template_noop
+ name = "Area Passthrough"
+
+/turf/template_noop
+ name = "Turf Passthrough"
+ icon_state = "noop"
+ bullet_bounce_sound = null
diff --git a/code/modules/mapping/reader.dm b/code/modules/mapping/reader.dm
index dfc75c32ee..22be4aa246 100644
--- a/code/modules/mapping/reader.dm
+++ b/code/modules/mapping/reader.dm
@@ -1,66 +1,65 @@
///////////////////////////////////////////////////////////////
//SS13 Optimized Map loader
//////////////////////////////////////////////////////////////
+#define SPACE_KEY "space"
-//global datum that will preload variables on atoms instanciation
-GLOBAL_VAR_INIT(use_preloader, FALSE)
-GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new)
+/datum/grid_set
+ var/xcrd
+ var/ycrd
+ var/zcrd
+ var/gridLines
-/dmm_suite
- // /"([a-zA-Z]+)" = \(((?:.|\n)*?)\)\n(?!\t)|\((\d+),(\d+),(\d+)\) = \{"([a-zA-Z\n]*)"\}/g
- var/static/regex/dmmRegex = new/regex({""(\[a-zA-Z]+)" = \\(((?:.|\n)*?)\\)\n(?!\t)|\\((\\d+),(\\d+),(\\d+)\\) = \\{"(\[a-zA-Z\n]*)"\\}"}, "g")
- // /^[\s\n]+"?|"?[\s\n]+$|^"|"$/g
- var/static/regex/trimQuotesRegex = new/regex({"^\[\\s\n]+"?|"?\[\\s\n]+$|^"|"$"}, "g")
- // /^[\s\n]+|[\s\n]+$/
- var/static/regex/trimRegex = new/regex("^\[\\s\n]+|\[\\s\n]+$", "g")
- var/static/list/modelCache = list()
- var/static/space_key
- #ifdef TESTING
- var/static/turfsSkipped
- #endif
-
-/**
- * Construct the model map and control the loading process
- *
- * WORKING :
- *
- * 1) Makes an associative mapping of model_keys with model
- * e.g aa = /turf/unsimulated/wall{icon_state = "rock"}
- * 2) Read the map line by line, parsing the result (using parse_grid)
- *
- */
-/dmm_suite/load_map(dmm_file as file, x_offset as num, y_offset as num, z_offset as num, cropMap as num, measureOnly as num, no_changeturf as num, lower_crop_x as num, lower_crop_y as num, upper_crop_x as num, upper_crop_y as num, placeOnTop as num)
- //How I wish for RAII
- Master.StartLoadingMap()
- space_key = null
- #ifdef TESTING
- turfsSkipped = 0
- #endif
- . = load_map_impl(dmm_file, x_offset, y_offset, z_offset, cropMap, measureOnly, no_changeturf, lower_crop_x, upper_crop_x, lower_crop_y, upper_crop_y, placeOnTop)
- #ifdef TESTING
- if(turfsSkipped)
- testing("Skipped loading [turfsSkipped] default turfs")
- #endif
- Master.StopLoadingMap()
-
-/dmm_suite/proc/load_map_impl(dmm_file, x_offset, y_offset, z_offset, cropMap, measureOnly, no_changeturf, x_lower = -INFINITY, x_upper = INFINITY, y_lower = -INFINITY, y_upper = INFINITY, placeOnTop = FALSE)
- var/tfile = dmm_file//the map file we're creating
- if(isfile(tfile))
- tfile = file2text(tfile)
-
- if(!x_offset)
- x_offset = 1
- if(!y_offset)
- y_offset = 1
- if(!z_offset)
- z_offset = world.maxz + 1
-
- var/list/bounds = list(1.#INF, 1.#INF, 1.#INF, -1.#INF, -1.#INF, -1.#INF)
- var/list/grid_models = list()
+/datum/parsed_map
+ var/original_path
var/key_len = 0
+ var/list/grid_models = list()
+ var/list/gridSets = list()
+ var/list/modelCache
+
+ /// Unoffset bounds. Null on parse failure.
+ var/list/parsed_bounds
+ /// Offset bounds. Same as parsed_bounds until load().
+ var/list/bounds
+
+ // raw strings used to represent regexes more accurately
+ // '' used to avoid confusing syntax highlighting
+ var/static/regex/dmmRegex = new(@'"([a-zA-Z]+)" = \(((?:.|\n)*?)\)\n(?!\t)|\((\d+),(\d+),(\d+)\) = \{"([a-zA-Z\n]*)"\}', "g")
+ var/static/regex/trimQuotesRegex = new(@'^[\s\n]+"?|"?[\s\n]+$|^"|"$', "g")
+ var/static/regex/trimRegex = new(@'^[\s\n]+|[\s\n]+$', "g")
+
+ #ifdef TESTING
+ var/turfsSkipped = 0
+ #endif
+
+/// Shortcut function to parse a map and apply it to the world.
+///
+/// - `dmm_file`: A .dmm file to load (Required).
+/// - `x_offset`, `y_offset`, `z_offset`: Positions representign where to load the map (Optional).
+/// - `cropMap`: When true, the map will be cropped to fit the existing world dimensions (Optional).
+/// - `measureOnly`: When true, no changes will be made to the world (Optional).
+/// - `no_changeturf`: When true, [turf/AfterChange] won't be called on loaded turfs
+/// - `x_lower`, `x_upper`, `y_lower`, `y_upper`: Coordinates (relative to the map) to crop to (Optional).
+/// - `placeOnTop`: Whether to use [turf/PlaceOnTop] rather than [turf/ChangeTurf] (Optional).
+/proc/load_map(dmm_file as file, x_offset as num, y_offset as num, z_offset as num, cropMap as num, measureOnly as num, no_changeturf as num, x_lower = -INFINITY as num, x_upper = INFINITY as num, y_lower = -INFINITY as num, y_upper = INFINITY as num, placeOnTop = FALSE as num)
+ var/datum/parsed_map/parsed = new(dmm_file, x_lower, x_upper, y_lower, y_upper, measureOnly)
+ if(parsed.bounds && !measureOnly)
+ parsed.load(x_offset, y_offset, z_offset, cropMap, no_changeturf, x_lower, x_upper, y_lower, y_upper, placeOnTop)
+ return parsed
+
+/// Parse a map, possibly cropping it.
+/datum/parsed_map/New(tfile, x_lower = -INFINITY, x_upper = INFINITY, y_lower = -INFINITY, y_upper=INFINITY, measureOnly=FALSE)
+ if(isfile(tfile))
+ original_path = "[tfile]"
+ tfile = file2text(tfile)
+ else if(isnull(tfile))
+ // create a new datum without loading a map
+ return
+
+ bounds = parsed_bounds = list(1.#INF, 1.#INF, 1.#INF, -1.#INF, -1.#INF, -1.#INF)
var/stored_index = 1
+ //multiz lool
while(dmmRegex.Find(tfile, stored_index))
stored_index = dmmRegex.next
@@ -73,41 +72,33 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new)
if(!key_len)
key_len = length(key)
else
- throw EXCEPTION("Inconsistent key length in DMM")
+ CRASH("Inconsistent key length in DMM")
if(!measureOnly)
grid_models[key] = dmmRegex.group[2]
// (1,1,1) = {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}
else if(dmmRegex.group[3]) // Coords
if(!key_len)
- throw EXCEPTION("Coords before model definition in DMM")
+ CRASH("Coords before model definition in DMM")
var/curr_x = text2num(dmmRegex.group[3])
if(curr_x < x_lower || curr_x > x_upper)
continue
- var/xcrdStart = curr_x + x_offset - 1
+ var/datum/grid_set/gridSet = new
+
+ gridSet.xcrd = curr_x
//position of the currently processed square
- var/xcrd
- var/ycrd = text2num(dmmRegex.group[4]) + y_offset - 1
- var/zcrd = text2num(dmmRegex.group[5]) + z_offset - 1
+ gridSet.ycrd = text2num(dmmRegex.group[4])
+ gridSet.zcrd = text2num(dmmRegex.group[5])
- var/zexpansion = zcrd > world.maxz
- if(zexpansion && !measureOnly)
- if(cropMap)
- continue
- else
- while (zcrd > world.maxz) //create a new z_level if needed
- world.incrementMaxZ()
- if(!no_changeturf)
- WARNING("Z-level expansion occurred without no_changeturf set, this may cause problems when /turf/AfterChange is called")
-
- bounds[MAP_MINX] = min(bounds[MAP_MINX], CLAMP(xcrdStart, x_lower, x_upper))
- bounds[MAP_MINZ] = min(bounds[MAP_MINZ], zcrd)
- bounds[MAP_MAXZ] = max(bounds[MAP_MAXZ], zcrd)
+ bounds[MAP_MINX] = min(bounds[MAP_MINX], CLAMP(gridSet.xcrd, x_lower, x_upper))
+ bounds[MAP_MINZ] = min(bounds[MAP_MINZ], gridSet.zcrd)
+ bounds[MAP_MAXZ] = max(bounds[MAP_MAXZ], gridSet.zcrd)
var/list/gridLines = splittext(dmmRegex.group[6], "\n")
+ gridSet.gridLines = gridLines
var/leadingBlanks = 0
while(leadingBlanks < gridLines.len && gridLines[++leadingBlanks] == "")
@@ -117,125 +108,144 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new)
if(!gridLines.len) // Skip it if only blank lines exist.
continue
+ gridSets += gridSet
+
if(gridLines.len && gridLines[gridLines.len] == "")
gridLines.Cut(gridLines.len) // Remove only one blank line at the end.
- bounds[MAP_MINY] = min(bounds[MAP_MINY], CLAMP(ycrd, y_lower, y_upper))
- ycrd += gridLines.len - 1 // Start at the top and work down
+ bounds[MAP_MINY] = min(bounds[MAP_MINY], CLAMP(gridSet.ycrd, y_lower, y_upper))
+ gridSet.ycrd += gridLines.len - 1 // Start at the top and work down
+ bounds[MAP_MAXY] = max(bounds[MAP_MAXY], CLAMP(gridSet.ycrd, y_lower, y_upper))
- if(!cropMap && ycrd > world.maxy)
- if(!measureOnly)
- world.maxy = ycrd // Expand Y here. X is expanded in the loop below
- bounds[MAP_MAXY] = max(bounds[MAP_MAXY], CLAMP(ycrd, y_lower, y_upper))
+ var/maxx = gridSet.xcrd
+ if(gridLines.len) //Not an empty map
+ maxx = max(maxx, gridSet.xcrd + length(gridLines[1]) / key_len - 1)
+
+ bounds[MAP_MAXX] = CLAMP(max(bounds[MAP_MAXX], maxx), x_lower, x_upper)
+ CHECK_TICK
+
+ // Indicate failure to parse any coordinates by nulling bounds
+ if(bounds[1] == 1.#INF)
+ bounds = null
+ parsed_bounds = bounds
+
+/// Load the parsed map into the world. See [/proc/load_map] for arguments.
+/datum/parsed_map/proc/load(x_offset, y_offset, z_offset, cropMap, no_changeturf, x_lower, x_upper, y_lower, y_upper, placeOnTop)
+ //How I wish for RAII
+ Master.StartLoadingMap()
+ . = _load_impl(x_offset, y_offset, z_offset, cropMap, no_changeturf, x_lower, x_upper, y_lower, y_upper, placeOnTop)
+ Master.StopLoadingMap()
+
+// Do not call except via load() above.
+/datum/parsed_map/proc/_load_impl(x_offset = 1, y_offset = 1, z_offset = world.maxz + 1, cropMap = FALSE, no_changeturf = FALSE, x_lower = -INFINITY, x_upper = INFINITY, y_lower = -INFINITY, y_upper = INFINITY, placeOnTop = FALSE)
+ var/list/areaCache = list()
+ var/list/modelCache = build_cache(no_changeturf)
+ var/space_key = modelCache[SPACE_KEY]
+ var/list/bounds
+ src.bounds = bounds = list(1.#INF, 1.#INF, 1.#INF, -1.#INF, -1.#INF, -1.#INF)
+
+ for(var/I in gridSets)
+ var/datum/grid_set/gset = I
+ var/ycrd = gset.ycrd + y_offset - 1
+ var/zcrd = gset.zcrd + z_offset - 1
+ if(!cropMap && ycrd > world.maxy)
+ world.maxy = ycrd // Expand Y here. X is expanded in the loop below
+ var/zexpansion = zcrd > world.maxz
+ if(zexpansion)
+ if(cropMap)
+ continue
else
- bounds[MAP_MAXY] = max(bounds[MAP_MAXY], CLAMP(min(ycrd, world.maxy), y_lower, y_upper))
+ while (zcrd > world.maxz) //create a new z_level if needed
+ world.incrementMaxZ()
+ if(!no_changeturf)
+ WARNING("Z-level expansion occurred without no_changeturf set, this may cause problems when /turf/AfterChange is called")
- var/maxx = xcrdStart
- if(measureOnly)
- for(var/line in gridLines)
- maxx = max(maxx, xcrdStart + length(line) / key_len - 1)
- else
- for(var/line in gridLines)
- if((ycrd - y_offset + 1) < y_lower || (ycrd - y_offset + 1) > y_upper) //Reverse operation and check if it is out of bounds of cropping.
- --ycrd
- continue
- if(ycrd <= world.maxy && ycrd >= 1)
- xcrd = xcrdStart
- for(var/tpos = 1 to length(line) - key_len + 1 step key_len)
- if((xcrd - x_offset + 1) < x_lower || (xcrd - x_offset + 1) > x_upper) //Same as above.
- ++xcrd
- continue //X cropping.
- if(xcrd > world.maxx)
- if(cropMap)
- break
- else
- world.maxx = xcrd
+ for(var/line in gset.gridLines)
+ if((ycrd - y_offset + 1) < y_lower || (ycrd - y_offset + 1) > y_upper) //Reverse operation and check if it is out of bounds of cropping.
+ --ycrd
+ continue
+ if(ycrd <= world.maxy && ycrd >= 1)
+ var/xcrd = gset.xcrd + x_offset - 1
+ for(var/tpos = 1 to length(line) - key_len + 1 step key_len)
+ if((xcrd - x_offset + 1) < x_lower || (xcrd - x_offset + 1) > x_upper) //Same as above.
+ ++xcrd
+ continue //X cropping.
+ if(xcrd > world.maxx)
+ if(cropMap)
+ break
+ else
+ world.maxx = xcrd
- if(xcrd >= 1)
- var/model_key = copytext(line, tpos, tpos + key_len)
- var/no_afterchange = no_changeturf || zexpansion
- if(!no_afterchange || (model_key != space_key))
- if(!grid_models[model_key])
- throw EXCEPTION("Undefined model key in DMM.")
- parse_grid(grid_models[model_key], model_key, xcrd, ycrd, zcrd, no_changeturf || zexpansion, placeOnTop)
- #ifdef TESTING
- else
- ++turfsSkipped
- #endif
- CHECK_TICK
- maxx = max(maxx, xcrd)
- ++xcrd
- --ycrd
+ if(xcrd >= 1)
+ var/model_key = copytext(line, tpos, tpos + key_len)
+ var/no_afterchange = no_changeturf || zexpansion
+ if(!no_afterchange || (model_key != space_key))
+ var/list/cache = modelCache[model_key]
+ if(!cache)
+ CRASH("Undefined model key in DMM: [model_key]")
+ build_coordinate(areaCache, cache, locate(xcrd, ycrd, zcrd), no_afterchange, placeOnTop)
- bounds[MAP_MAXX] = CLAMP(max(bounds[MAP_MAXX], cropMap ? min(maxx, world.maxx) : maxx), x_lower, x_upper)
+ // only bother with bounds that actually exist
+ bounds[MAP_MINX] = min(bounds[MAP_MINX], xcrd)
+ bounds[MAP_MINY] = min(bounds[MAP_MINY], ycrd)
+ bounds[MAP_MINZ] = min(bounds[MAP_MINZ], zcrd)
+ bounds[MAP_MAXX] = max(bounds[MAP_MAXX], xcrd)
+ bounds[MAP_MAXY] = max(bounds[MAP_MAXY], ycrd)
+ bounds[MAP_MAXZ] = max(bounds[MAP_MAXZ], zcrd)
+ #ifdef TESTING
+ else
+ ++turfsSkipped
+ #endif
+ CHECK_TICK
+ ++xcrd
+ --ycrd
CHECK_TICK
- if(bounds[1] == 1.#INF) // Shouldn't need to check every item
- return null
- else
- if(!measureOnly)
- if(!no_changeturf)
- for(var/t in block(locate(bounds[MAP_MINX], bounds[MAP_MINY], bounds[MAP_MINZ]), locate(bounds[MAP_MAXX], bounds[MAP_MAXY], bounds[MAP_MAXZ])))
- var/turf/T = t
- //we do this after we load everything in. if we don't; we'll have weird atmos bugs regarding atmos adjacent turfs
- T.AfterChange(CHANGETURF_IGNORE_AIR)
- return bounds
+ if(!no_changeturf)
+ for(var/t in block(locate(bounds[MAP_MINX], bounds[MAP_MINY], bounds[MAP_MINZ]), locate(bounds[MAP_MAXX], bounds[MAP_MAXY], bounds[MAP_MAXZ])))
+ var/turf/T = t
+ //we do this after we load everything in. if we don't; we'll have weird atmos bugs regarding atmos adjacent turfs
+ T.AfterChange(CHANGETURF_IGNORE_AIR)
-/**
- * Fill a given tile with its area/turf/objects/mobs
- * Variable model is one full map line (e.g /turf/unsimulated/wall{icon_state = "rock"}, /area/mine/explored)
- *
- * WORKING :
- *
- * 1) Read the model string, member by member (delimiter is ',')
- *
- * 2) Get the path of the atom and store it into a list
- *
- * 3) a) Check if the member has variables (text within '{' and '}')
- *
- * 3) b) Construct an associative list with found variables, if any (the atom index in members is the same as its variables in members_attributes)
- *
- * 4) Instanciates the atom with its variables
- *
- */
-/dmm_suite/proc/parse_grid(model as text, model_key as text, xcrd as num,ycrd as num,zcrd as num, no_changeturf as num, placeOnTop as num)
- /*Method parse_grid()
- - Accepts a text string containing a comma separated list of type paths of the
- same construction as those contained in a .dmm file, and instantiates them.
- */
+ #ifdef TESTING
+ if(turfsSkipped)
+ testing("Skipped loading [turfsSkipped] default turfs")
+ #endif
- var/list/members //will contain all members (paths) in model (in our example : /turf/unsimulated/wall and /area/mine/explored)
- var/list/members_attributes //will contain lists filled with corresponding variables, if any (in our example : list(icon_state = "rock") and list())
- var/list/cached = modelCache[model]
- var/index
+ return TRUE
- if(cached)
- members = cached[1]
- members_attributes = cached[2]
- else
+/datum/parsed_map/proc/build_cache(no_changeturf, bad_paths=null)
+ if(modelCache && !bad_paths)
+ return modelCache
+ . = modelCache = list()
+ var/list/grid_models = src.grid_models
+ for(var/model_key in grid_models)
+ var/model = grid_models[model_key]
+ var/list/members = list() //will contain all members (paths) in model (in our example : /turf/unsimulated/wall and /area/mine/explored)
+ var/list/members_attributes = list() //will contain lists filled with corresponding variables, if any (in our example : list(icon_state = "rock") and list())
/////////////////////////////////////////////////////////
//Constructing members and corresponding variables lists
////////////////////////////////////////////////////////
- members = list()
- members_attributes = list()
- index = 1
-
+ var/index = 1
var/old_position = 1
var/dpos
- do
+ while(dpos != 0)
//finding next member (e.g /turf/unsimulated/wall{icon_state = "rock"} or /area/mine/explored)
dpos = find_next_delimiter_position(model, old_position, ",", "{", "}") //find next delimiter (comma here) that's not within {...}
var/full_def = trim_text(copytext(model, old_position, dpos)) //full definition, e.g : /obj/foo/bar{variables=derp}
var/variables_start = findtext(full_def, "{")
- var/atom_def = text2path(trim_text(copytext(full_def, 1, variables_start))) //path definition, e.g /obj/foo/bar
+ var/path_text = trim_text(copytext(full_def, 1, variables_start))
+ var/atom_def = text2path(path_text) //path definition, e.g /obj/foo/bar
old_position = dpos + 1
- if(!atom_def) // Skip the item if the path does not exist. Fix your crap, mappers!
+ if(!ispath(atom_def, /atom)) // Skip the item if the path does not exist. Fix your crap, mappers!
+ if(bad_paths)
+ LAZYOR(bad_paths[path_text], model_key)
continue
members.Add(atom_def)
@@ -258,7 +268,6 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new)
members_attributes[index++] = fields
CHECK_TICK
- while(dpos != 0)
//check and see if we can just skip this turf
//So you don't have to understand this horrid statement, we can do this if
@@ -269,33 +278,42 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new)
// 5. and the members are world.turf and world.area
// Basically, if we find an entry like this: "XXX" = (/turf/default, /area/default)
// We can skip calling this proc every time we see XXX
- if(no_changeturf && !space_key && members.len == 2 && members_attributes.len == 2 && length(members_attributes[1]) == 0 && length(members_attributes[2]) == 0 && (world.area in members) && (world.turf in members))
- space_key = model_key
- return
+ if(no_changeturf \
+ && !(.[SPACE_KEY]) \
+ && members.len == 2 \
+ && members_attributes.len == 2 \
+ && length(members_attributes[1]) == 0 \
+ && length(members_attributes[2]) == 0 \
+ && (world.area in members) \
+ && (world.turf in members))
+
+ .[SPACE_KEY] = model_key
+ continue
- modelCache[model] = list(members, members_attributes)
+ .[model_key] = list(members, members_attributes)
+/datum/parsed_map/proc/build_coordinate(list/areaCache, list/model, turf/crds, no_changeturf as num, placeOnTop as num)
+ var/index
+ var/list/members = model[1]
+ var/list/members_attributes = model[2]
////////////////
//Instanciation
////////////////
//The next part of the code assumes there's ALWAYS an /area AND a /turf on a given tile
- var/turf/crds = locate(xcrd,ycrd,zcrd)
-
//first instance the /area and remove it from the members list
index = members.len
if(members[index] != /area/template_noop)
- var/atom/instance
GLOB._preloader.setup(members_attributes[index])//preloader for assigning set variables on atom creation
var/atype = members[index]
- for(var/area/A in world)
- if(A.type == atype)
- instance = A
- break
- if(!instance)
- instance = new atype(null)
+ var/atom/instance = areaCache[atype]
+ if (!instance)
+ instance = GLOB.areas_by_type[atype]
+ if (!instance)
+ instance = new atype(null)
+ areaCache[atype] = instance
if(crds)
instance.contents.Add(crds)
@@ -335,7 +353,7 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new)
////////////////
//Instance an atom at (x,y,z) and gives it the variables in attributes
-/dmm_suite/proc/instance_atom(path,list/attributes, turf/crds, no_changeturf, placeOnTop)
+/datum/parsed_map/proc/instance_atom(path,list/attributes, turf/crds, no_changeturf, placeOnTop)
GLOB._preloader.setup(attributes, path)
if(crds)
@@ -358,13 +376,13 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new)
stoplag()
SSatoms.map_loader_begin()
-/dmm_suite/proc/create_atom(path, crds)
+/datum/parsed_map/proc/create_atom(path, crds)
set waitfor = FALSE
. = new path (crds)
//text trimming (both directions) helper proc
//optionally removes quotes before and after the text (for variable name)
-/dmm_suite/proc/trim_text(what as text,trim_quotes=0)
+/datum/parsed_map/proc/trim_text(what as text,trim_quotes=0)
if(trim_quotes)
return trimQuotesRegex.Replace(what, "")
else
@@ -373,7 +391,7 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new)
//find the position of the next delimiter,skipping whatever is comprised between opening_escape and closing_escape
//returns 0 if reached the last delimiter
-/dmm_suite/proc/find_next_delimiter_position(text as text,initial_position as num, delimiter=",",opening_escape="\"",closing_escape="\"")
+/datum/parsed_map/proc/find_next_delimiter_position(text as text,initial_position as num, delimiter=",",opening_escape="\"",closing_escape="\"")
var/position = initial_position
var/next_delimiter = findtext(text,delimiter,position,0)
var/next_opening = findtext(text,opening_escape,position,0)
@@ -388,90 +406,69 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new)
//build a list from variables in text form (e.g {var1="derp"; var2; var3=7} => list(var1="derp", var2, var3=7))
//return the filled list
-/dmm_suite/proc/readlist(text as text, delimiter=",")
-
- var/list/to_return = list()
+/datum/parsed_map/proc/readlist(text as text, delimiter=",")
+ . = list()
+ if (!text)
+ return
var/position
var/old_position = 1
- do
- //find next delimiter that is not within "..."
+ while(position != 0)
+ // find next delimiter that is not within "..."
position = find_next_delimiter_position(text,old_position,delimiter)
- //check if this is a simple variable (as in list(var1, var2)) or an associative one (as in list(var1="foo",var2=7))
+ // check if this is a simple variable (as in list(var1, var2)) or an associative one (as in list(var1="foo",var2=7))
var/equal_position = findtext(text,"=",old_position, position)
- var/trim_left = trim_text(copytext(text,old_position,(equal_position ? equal_position : position)),1)//the name of the variable, must trim quotes to build a BYOND compliant associatives list
+ var/trim_left = trim_text(copytext(text,old_position,(equal_position ? equal_position : position)))
+ var/left_constant = delimiter == ";" ? trim_left : parse_constant(trim_left)
old_position = position + 1
- if(equal_position)//associative var, so do the association
- var/trim_right = trim_text(copytext(text,equal_position+1,position))//the content of the variable
+ if(equal_position && !isnum(left_constant))
+ // Associative var, so do the association.
+ // Note that numbers cannot be keys - the RHS is dropped if so.
+ var/trim_right = trim_text(copytext(text,equal_position+1,position))
+ var/right_constant = parse_constant(trim_right)
+ .[left_constant] = right_constant
- //Check for string
- if(findtext(trim_right,"\"",1,2))
- trim_right = copytext(trim_right,2,findtext(trim_right,"\"",3,0))
+ else // simple var
+ . += list(left_constant)
- //Check for number
- else if(isnum(text2num(trim_right)))
- trim_right = text2num(trim_right)
+/datum/parsed_map/proc/parse_constant(text)
+ // number
+ var/num = text2num(text)
+ if(isnum(num))
+ return num
- //Check for null
- else if(trim_right == "null")
- trim_right = null
+ // string
+ if(findtext(text,"\"",1,2))
+ return copytext(text,2,findtext(text,"\"",3,0))
- //Check for list
- else if(copytext(trim_right,1,5) == "list")
- trim_right = readlist(copytext(trim_right,6,length(trim_right)))
+ // list
+ if(copytext(text,1,6) == "list(")
+ return readlist(copytext(text,6,length(text)))
- //Check for file
- else if(copytext(trim_right,1,2) == "'")
- trim_right = file(copytext(trim_right,2,length(trim_right)))
+ // typepath
+ var/path = text2path(text)
+ if(ispath(path))
+ return path
- //Check for path
- else if(ispath(text2path(trim_right)))
- trim_right = text2path(trim_right)
+ // file
+ if(copytext(text,1,2) == "'")
+ return file(copytext(text,2,length(text)))
- to_return[trim_left] = trim_right
+ // null
+ if(text == "null")
+ return null
- else//simple var
- to_return[trim_left] = null
+ // not parsed:
+ // - pops: /obj{name="foo"}
+ // - new(), newlist(), icon(), matrix(), sound()
- while(position != 0)
+ // fallback: string
+ return text
- return to_return
-
-/dmm_suite/Destroy()
+/datum/parsed_map/Destroy()
..()
return QDEL_HINT_HARDDEL_NOW
-
-//////////////////
-//Preloader datum
-//////////////////
-
-/dmm_suite/preloader
- parent_type = /datum
- var/list/attributes
- var/target_path
-
-/dmm_suite/preloader/proc/setup(list/the_attributes, path)
- if(the_attributes.len)
- GLOB.use_preloader = TRUE
- attributes = the_attributes
- target_path = path
-
-/dmm_suite/preloader/proc/load(atom/what)
- for(var/attribute in attributes)
- var/value = attributes[attribute]
- if(islist(value))
- value = deepCopyList(value)
- what.vars[attribute] = value
- GLOB.use_preloader = FALSE
-
-/area/template_noop
- name = "Area Passthrough"
-
-/turf/template_noop
- name = "Turf Passthrough"
- icon_state = "noop"
- bullet_bounce_sound = null
diff --git a/code/modules/mapping/verify.dm b/code/modules/mapping/verify.dm
new file mode 100644
index 0000000000..a9834e3709
--- /dev/null
+++ b/code/modules/mapping/verify.dm
@@ -0,0 +1,98 @@
+/// An error report generated by [parsed_map/check_for_errors].
+/datum/map_report
+ var/original_path
+ var/list/bad_paths = list()
+ var/list/bad_keys = list()
+ /// Whether this map can be loaded safely despite the errors.
+ var/loadable = TRUE
+ var/crashed = TRUE
+
+ var/static/tag_number = 0
+
+/datum/map_report/New(datum/parsed_map/map)
+ original_path = map.original_path || "Untitled"
+
+/// Show a rendered version of this report to a client.
+/datum/map_report/proc/show_to(client/C)
+ var/list/html = list()
+ html += "Report for map file [original_path]
"
+ if(crashed)
+ html += "Validation crashed : check the runtime logs.
"
+ if(!loadable)
+ html += "Not loadable : some tiles are missing their turfs or areas.
"
+
+ if(bad_paths.len)
+ html += "Bad paths:
"
+ for(var/path in bad_paths)
+ var/list/keys = bad_paths[path]
+ html += "[path] : used in ([keys.len]): [keys.Join(" , ")] "
+ html += " "
+
+ if(bad_keys.len)
+ html += "Bad keys:
"
+ for(var/key in bad_keys)
+ var/list/messages = bad_keys[key]
+ html += "[key] "
+ if(messages.len == 1)
+ html += ": [bad_keys[key][1]]"
+ else
+ html += ""
+ html += " "
+ html += " "
+ C << browse(html.Join(), "window=[tag];size=600x400")
+
+/datum/map_report/Topic(href, href_list)
+ . = ..()
+ if(. || !check_rights(R_ADMIN, FALSE) || !usr.client.holder.CheckAdminHref(href, href_list))
+ return
+
+ if (href_list["show"])
+ show_to(usr)
+
+
+/// Check a parsed but not yet loaded map for errors.
+///
+/// Returns a [/datum/map_report] if there are errors or `FALSE` otherwise.
+/datum/parsed_map/proc/check_for_errors()
+ var/datum/map_report/report = new(src)
+ . = report
+
+ // build_cache will check bad paths for us
+ var/list/modelCache = build_cache(TRUE, report.bad_paths)
+
+ for(var/path in report.bad_paths)
+ if(copytext(path, 1, 7) == "/turf/" || copytext(path, 1, 7) == "/area/")
+ report.loadable = FALSE
+
+ // check for tiles with the wrong number of turfs or areas
+ for(var/key in modelCache)
+ if(key == SPACE_KEY)
+ continue
+ var/model = modelCache[key]
+ var/list/members = model[1]
+
+ var/turfs = 0
+ var/areas = 0
+ for(var/i in 1 to members.len)
+ var/atom/path = members[i]
+
+ turfs += ispath(path, /turf)
+ areas += ispath(path, /area)
+
+ if(turfs == 0)
+ report.loadable = FALSE
+ LAZYADD(report.bad_keys[key], "no turf")
+ else if(turfs > 1)
+ LAZYADD(report.bad_keys[key], "[turfs] stacked turfs")
+
+ if(areas != 1)
+ report.loadable = FALSE
+ LAZYADD(report.bad_keys[key], "[areas] areas instead of 1")
+
+ // return the report
+ if(report.bad_paths.len || report.bad_keys.len || !report.loadable)
+ // keep the report around so it can be referenced later
+ report.tag = "mapreport_[++report.tag_number]"
+ report.crashed = FALSE
+ else
+ return FALSE
diff --git a/code/modules/mining/aux_base.dm b/code/modules/mining/aux_base.dm
index 2c001571a1..91eab536c9 100644
--- a/code/modules/mining/aux_base.dm
+++ b/code/modules/mining/aux_base.dm
@@ -134,7 +134,7 @@ interface with the mining shuttle at the landing site if a mobile beacon is also
shuttleId = "mining" //The base can only be dropped once, so this gives the console a new purpose.
possible_destinations = "mining_home;mining_away;landing_zone_dock;mining_public"
-/obj/machinery/computer/auxillary_base/proc/set_landing_zone(turf/T, mob/user, var/no_restrictions)
+/obj/machinery/computer/auxillary_base/proc/set_landing_zone(turf/T, mob/user, no_restrictions)
var/obj/docking_port/mobile/auxillary_base/base_dock = locate(/obj/docking_port/mobile/auxillary_base) in SSshuttle.mobile
if(!base_dock) //Not all maps have an Aux base. This object is useless in that case.
to_chat(user, "This station is not equipped with an auxillary base. Please contact your Nanotrasen contractor. ")
@@ -151,8 +151,8 @@ interface with the mining shuttle at the landing site if a mobile beacon is also
if(!is_mining_level(T.z))
return BAD_ZLEVEL
- var/colony_radius = CEILING(max(base_dock.width, base_dock.height)*0.5, 1)
- var/list/colony_turfs = block(locate(T.x - colony_radius, T.y - colony_radius, T.z), locate(T.x + colony_radius, T.y + colony_radius, T.z))
+
+ var/list/colony_turfs = base_dock.return_ordered_turfs(T.x,T.y,T.z,base_dock.dir)
for(var/i in 1 to colony_turfs.len)
CHECK_TICK
var/turf/place = colony_turfs[i]
@@ -239,7 +239,6 @@ interface with the mining shuttle at the landing site if a mobile beacon is also
/obj/docking_port/mobile/auxillary_base
name = "auxillary base"
id = "colony_drop"
- timid = FALSE
//Reminder to map-makers to set these values equal to the size of your base.
dheight = 4
dwidth = 4
@@ -355,7 +354,7 @@ interface with the mining shuttle at the landing site if a mobile beacon is also
qdel(Mport)
return
- if(!mining_shuttle.canDock(Mport))
+ if(mining_shuttle.canDock(Mport) != SHUTTLE_CAN_DOCK)
to_chat(user, "Unable to secure a valid docking zone. Please try again in an open area near, but not within the aux. mining base. ")
SSshuttle.stationary.Remove(Mport)
qdel(Mport)
diff --git a/code/modules/mining/equipment/explorer_gear.dm b/code/modules/mining/equipment/explorer_gear.dm
index 61d439667d..a905a3baa9 100644
--- a/code/modules/mining/equipment/explorer_gear.dm
+++ b/code/modules/mining/equipment/explorer_gear.dm
@@ -61,7 +61,7 @@
icon_state = "hostile_env"
item_state = "hostile_env"
clothing_flags = THICKMATERIAL //not spaceproof
- max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
resistance_flags = FIRE_PROOF | LAVA_PROOF
slowdown = 0
armor = list("melee" = 70, "bullet" = 40, "laser" = 10, "energy" = 10, "bomb" = 50, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100)
@@ -91,7 +91,7 @@
icon_state = "hostile_env"
item_state = "hostile_env"
w_class = WEIGHT_CLASS_NORMAL
- max_heat_protection_temperature = FIRE_IMMUNITY_HELM_MAX_TEMP_PROTECT
+ max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
clothing_flags = THICKMATERIAL // no space protection
armor = list("melee" = 70, "bullet" = 40, "laser" = 10, "energy" = 10, "bomb" = 50, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100)
resistance_flags = FIRE_PROOF | LAVA_PROOF
diff --git a/code/modules/mining/equipment/kinetic_crusher.dm b/code/modules/mining/equipment/kinetic_crusher.dm
index 7cf16f6064..fb6e9eb6fe 100644
--- a/code/modules/mining/equipment/kinetic_crusher.dm
+++ b/code/modules/mining/equipment/kinetic_crusher.dm
@@ -72,6 +72,7 @@
C.total_damage += target_health - target.health //we did some damage, but let's not assume how much we did
/obj/item/twohanded/required/kinetic_crusher/afterattack(atom/target, mob/living/user, proximity_flag, clickparams)
+ . = ..()
if(!proximity_flag && charged)//Mark a target, or mine a tile.
var/turf/proj_turf = user.loc
if(!isturf(proj_turf))
diff --git a/code/modules/mining/equipment/lazarus_injector.dm b/code/modules/mining/equipment/lazarus_injector.dm
index 6947b5b23f..b2a2e3357d 100644
--- a/code/modules/mining/equipment/lazarus_injector.dm
+++ b/code/modules/mining/equipment/lazarus_injector.dm
@@ -16,6 +16,7 @@
var/revive_type = SENTIENCE_ORGANIC //So you can't revive boss monsters or robots with it
/obj/item/lazarus_injector/afterattack(atom/target, mob/user, proximity_flag)
+ . = ..()
if(!loaded)
return
if(isliving(target) && proximity_flag)
diff --git a/code/modules/mining/equipment/regenerative_core.dm b/code/modules/mining/equipment/regenerative_core.dm
index 43be61dd77..d6e99f7361 100644
--- a/code/modules/mining/equipment/regenerative_core.dm
+++ b/code/modules/mining/equipment/regenerative_core.dm
@@ -7,6 +7,7 @@
w_class = WEIGHT_CLASS_TINY
/obj/item/hivelordstabilizer/afterattack(obj/item/organ/M, mob/user)
+ . = ..()
var/obj/item/organ/regenerative_core/C = M
if(!istype(C, /obj/item/organ/regenerative_core))
to_chat(user, "The stabilizer only works on certain types of monster organs, generally regenerative in nature. ")
@@ -62,10 +63,11 @@
/obj/item/organ/regenerative_core/on_life()
..()
- if(owner.health < HEALTH_THRESHOLD_CRIT)
+ if(owner.health < owner.crit_threshold)
ui_action_click()
/obj/item/organ/regenerative_core/afterattack(atom/target, mob/user, proximity_flag)
+ . = ..()
if(proximity_flag && ishuman(target))
var/mob/living/carbon/human/H = target
if(inert)
@@ -83,7 +85,6 @@
SSblackbox.record_feedback("nested tally", "hivelord_core", 1, list("[type]", "used", "self"))
H.revive(full_heal = 1)
qdel(src)
- ..()
/obj/item/organ/regenerative_core/Insert(mob/living/carbon/M, special = 0, drop_if_replaced = TRUE)
. = ..()
diff --git a/code/modules/mining/equipment/resonator.dm b/code/modules/mining/equipment/resonator.dm
index 57efc0f7f5..e23c3deb58 100644
--- a/code/modules/mining/equipment/resonator.dm
+++ b/code/modules/mining/equipment/resonator.dm
@@ -100,7 +100,7 @@
playsound(T,'sound/weapons/resonator_blast.ogg',50,1)
for(var/mob/living/L in T)
if(creator)
- add_logs(creator, L, "used a resonator field on", "resonator")
+ log_combat(creator, L, "used a resonator field on", "resonator")
to_chat(L, "[src] ruptured with you in it! ")
L.apply_damage(resonance_damage, BRUTE)
qdel(src)
diff --git a/code/modules/mining/equipment/survival_pod.dm b/code/modules/mining/equipment/survival_pod.dm
index 3005a29dcd..715ecc906a 100644
--- a/code/modules/mining/equipment/survival_pod.dm
+++ b/code/modules/mining/equipment/survival_pod.dm
@@ -175,8 +175,6 @@
desc = "A heated storage unit."
icon_state = "donkvendor"
icon = 'icons/obj/lavaland/donkvendor.dmi'
- icon_on = "donkvendor"
- icon_off = "donkvendor"
light_range = 5
light_power = 1.2
light_color = "#DDFFD3"
@@ -185,6 +183,9 @@
flags_1 = NODECONSTRUCT_1
var/empty = FALSE
+/obj/machinery/smartfridge/survival_pod/update_icon()
+ return
+
/obj/machinery/smartfridge/survival_pod/Initialize(mapload)
. = ..()
if(empty)
@@ -247,11 +248,6 @@
. = ..()
air_update_turf(1)
-/obj/structure/fans/Destroy()
- var/turf/T = loc
- . = ..()
- T.air_update_turf(1)
-
//Inivisible, indestructible fans
/obj/structure/fans/tiny/invisible
name = "air flow blocker"
diff --git a/code/modules/mining/fulton.dm b/code/modules/mining/fulton.dm
index d6ea55855d..f97d3612a8 100644
--- a/code/modules/mining/fulton.dm
+++ b/code/modules/mining/fulton.dm
@@ -38,6 +38,7 @@ GLOBAL_LIST_EMPTY(total_extraction_beacons)
to_chat(user, "You link the extraction pack to the beacon system.")
/obj/item/extraction_pack/afterattack(atom/movable/A, mob/living/carbon/human/user, flag, params)
+ . = ..()
if(!beacon)
to_chat(user, "[src] is not linked to a beacon, and cannot be used.")
return
diff --git a/code/modules/mining/laborcamp/laborstacker.dm b/code/modules/mining/laborcamp/laborstacker.dm
index 765f9d92e0..621a6afe23 100644
--- a/code/modules/mining/laborcamp/laborstacker.dm
+++ b/code/modules/mining/laborcamp/laborstacker.dm
@@ -135,6 +135,7 @@ GLOBAL_LIST(labor_sheet_values)
/obj/machinery/mineral/stacking_machine/laborstacker
+ force_connect = TRUE
var/points = 0 //The unclaimed value of ore stacked.
/obj/machinery/mineral/stacking_machine/laborstacker/process_sheet(obj/item/stack/sheet/inp)
diff --git a/code/modules/mining/lavaland/ash_flora.dm b/code/modules/mining/lavaland/ash_flora.dm
index 988c224279..540a012df1 100644
--- a/code/modules/mining/lavaland/ash_flora.dm
+++ b/code/modules/mining/lavaland/ash_flora.dm
@@ -152,6 +152,7 @@
resistance_flags = FLAMMABLE
max_integrity = 100
seed = /obj/item/seeds/lavaland/polypore
+ wine_power = 20
/obj/item/reagent_containers/food/snacks/grown/ash_flora/Initialize()
. = ..()
@@ -167,7 +168,7 @@
list_reagents = list("nutriment" = 3, "vitfro" = 2, "nicotine" = 2)
icon_state = "mushroom_leaf"
seed = /obj/item/seeds/lavaland/porcini
-
+ wine_power = 40
/obj/item/reagent_containers/food/snacks/grown/ash_flora/mushroom_cap
name = "mushroom cap"
@@ -175,7 +176,7 @@
list_reagents = list("mindbreaker" = 2, "entpoly" = 4, "mushroomhallucinogen" = 2)
icon_state = "mushroom_cap"
seed = /obj/item/seeds/lavaland/inocybe
-
+ wine_power = 70
/obj/item/reagent_containers/food/snacks/grown/ash_flora/mushroom_stem
name = "mushroom stem"
@@ -184,6 +185,7 @@
icon_state = "mushroom_stem"
light_range = 1
seed = /obj/item/seeds/lavaland/ember
+ wine_power = 60
/obj/item/reagent_containers/food/snacks/grown/ash_flora/cactus_fruit
name = "cactus fruit"
@@ -191,6 +193,7 @@
desc = "A cactus fruit covered in a thick, reddish skin. And some ash."
icon_state = "cactus_fruit"
seed = /obj/item/seeds/lavaland/cactus
+ wine_power = 50
/obj/item/reagent_containers/glass/bowl/mushroom_bowl
name = "mushroom bowl"
diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm
index 5345410005..23823e1e18 100644
--- a/code/modules/mining/lavaland/necropolis_chests.dm
+++ b/code/modules/mining/lavaland/necropolis_chests.dm
@@ -164,22 +164,22 @@
var/failText = "The snake seems unsatisfied with your incomplete oath and returns to it's previous place on the rod, returning to its dormant, wooden state. You must stand still while completing your oath! "
to_chat(itemUser, "The wooden snake that was carved into the rod seems to suddenly come alive and begins to slither down your arm! The compulsion to help others grows abnormally strong... ")
if(do_after(itemUser, 40, target = itemUser))
- itemUser.say("I swear to fulfill, to the best of my ability and judgment, this covenant:")
+ itemUser.say("I swear to fulfill, to the best of my ability and judgment, this covenant:", forced = "hippocratic oath")
else
to_chat(itemUser, failText)
return
if(do_after(itemUser, 20, target = itemUser))
- itemUser.say("I will apply, for the benefit of the sick, all measures that are required, avoiding those twin traps of overtreatment and therapeutic nihilism.")
+ itemUser.say("I will apply, for the benefit of the sick, all measures that are required, avoiding those twin traps of overtreatment and therapeutic nihilism.", forced = "hippocratic oath")
else
to_chat(itemUser, failText)
return
if(do_after(itemUser, 30, target = itemUser))
- itemUser.say("I will remember that I remain a member of society, with special obligations to all my fellow human beings, those sound of mind and body as well as the infirm.")
+ itemUser.say("I will remember that I remain a member of society, with special obligations to all my fellow human beings, those sound of mind and body as well as the infirm.", forced = "hippocratic oath")
else
to_chat(itemUser, failText)
return
if(do_after(itemUser, 30, target = itemUser))
- itemUser.say("If I do not violate this oath, may I enjoy life and art, respected while I live and remembered with affection thereafter. May I always act so as to preserve the finest traditions of my calling and may I long experience the joy of healing those who seek my help.")
+ itemUser.say("If I do not violate this oath, may I enjoy life and art, respected while I live and remembered with affection thereafter. May I always act so as to preserve the finest traditions of my calling and may I long experience the joy of healing those who seek my help.", forced = "hippocratic oath")
else
to_chat(itemUser, failText)
return
@@ -434,10 +434,11 @@
//Immortality Talisman
/obj/item/immortality_talisman
- name = "Immortality Talisman"
+ name = "\improper Immortality Talisman"
desc = "A dread talisman that can render you completely invulnerable."
icon = 'icons/obj/lavaland/artefacts.dmi'
icon_state = "talisman"
+ resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
actions_types = list(/datum/action/item_action/immortality)
var/cooldown = 0
@@ -448,12 +449,6 @@
/datum/action/item_action/immortality
name = "Immortality"
-/obj/item/immortality_talisman/Destroy(force)
- if(force)
- . = ..()
- else
- return QDEL_HINT_LETMELIVE
-
/obj/item/immortality_talisman/attack_self(mob/user)
if(cooldown < world.time)
SSblackbox.record_feedback("amount", "immortality_talisman_uses", 1)
@@ -467,6 +462,8 @@
user.notransform = 1
user.status_flags |= GODMODE
addtimer(CALLBACK(src, .proc/return_to_reality, user, Z), 100)
+ else
+ to_chat(user, "[src] is not ready yet! ")
/obj/item/immortality_talisman/proc/return_to_reality(mob/user, obj/effect/immortality_talisman/Z)
user.status_flags &= ~GODMODE
@@ -899,7 +896,7 @@
var/static/list/banned_turfs = typecacheof(list(/turf/open/space/transit, /turf/closed))
/obj/item/lava_staff/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
- ..()
+ . = ..()
if(timer > world.time)
return
@@ -975,7 +972,7 @@
to_chat(user, "You shatter the bottle! ")
playsound(user.loc, 'sound/effects/glassbr1.ogg', 100, 1)
message_admins("[ADMIN_LOOKUPFLW(user)] has activated a bottle of mayhem! ")
- add_logs(user, null, "activated a bottle of mayhem", src)
+ log_combat(user, null, "activated a bottle of mayhem", src)
qdel(src)
/obj/item/blood_contract
@@ -1019,7 +1016,7 @@
var/datum/objective/survive/survive = new
survive.owner = L.mind
L.mind.objectives += survive
- add_logs(user, L, "took out a blood contract on", src)
+ log_combat(user, L, "took out a blood contract on", src)
to_chat(L, "You've been marked for death! Don't let the demons get you! KILL THEM ALL! ")
L.add_atom_colour("#FF0000", ADMIN_COLOUR_PRIORITY)
var/obj/effect/mine/pickup/bloodbath/B = new(L)
@@ -1082,7 +1079,7 @@
to_chat(user, "The[beacon ? " beacon is not currently":"re is a beacon"] attached. ")
/obj/item/hierophant_club/suicide_act(mob/living/user)
- say("Xverwpsgexmrk...")
+ say("Xverwpsgexmrk...", forced = "hierophant club suicide")
user.visible_message("[user] holds [src] into the air! It looks like [user.p_theyre()] trying to commit suicide! ")
new/obj/effect/temp_visual/hierophant/telegraph(get_turf(user))
playsound(user,'sound/machines/airlockopen.ogg', 75, TRUE)
@@ -1097,7 +1094,7 @@
qdel(user)
/obj/item/hierophant_club/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
- ..()
+ . = ..()
var/turf/T = get_turf(target)
if(!T || timer > world.time)
return
@@ -1105,7 +1102,7 @@
timer = world.time + CLICK_CD_MELEE //by default, melee attacks only cause melee blasts, and have an accordingly short cooldown
if(proximity_flag)
INVOKE_ASYNC(src, .proc/aoe_burst, T, user)
- add_logs(user, target, "fired 3x3 blast at", src)
+ log_combat(user, target, "fired 3x3 blast at", src)
else
if(ismineralturf(target) && get_dist(user, target) < 6) //target is minerals, we can hit it(even if we can't see it)
INVOKE_ASYNC(src, .proc/cardinal_blasts, T, user)
@@ -1117,10 +1114,10 @@
var/obj/effect/temp_visual/hierophant/chaser/C = new(get_turf(user), user, target, chaser_speed, friendly_fire_check)
C.damage = 30
C.monster_damage_boost = FALSE
- add_logs(user, target, "fired a chaser at", src)
+ log_combat(user, target, "fired a chaser at", src)
else
INVOKE_ASYNC(src, .proc/cardinal_blasts, T, user) //otherwise, just do cardinal blast
- add_logs(user, target, "fired cardinal blast at", src)
+ log_combat(user, target, "fired cardinal blast at", src)
else
to_chat(user, "That target is out of range! " )
timer = world.time
@@ -1232,7 +1229,7 @@
INVOKE_ASYNC(src, .proc/prepare_icon_update)
beacon.icon_state = "hierophant_tele_off"
return
- add_logs(user, beacon, "teleported self from [AREACOORD(source)] to")
+ user.log_message("teleported self from [AREACOORD(source)] to [beacon]")
new /obj/effect/temp_visual/hierophant/telegraph/teleport(T, user)
new /obj/effect/temp_visual/hierophant/telegraph/teleport(source, user)
for(var/t in RANGE_TURFS(1, T))
@@ -1279,7 +1276,7 @@
return
M.visible_message("[M] fades in! ")
if(user != M)
- add_logs(user, M, "teleported", null, "from [AREACOORD(source)]")
+ log_combat(user, M, "teleported", null, "from [AREACOORD(source)]")
/obj/item/hierophant_club/proc/cardinal_blasts(turf/T, mob/living/user) //fire cardinal cross blasts with a delay
if(!T)
diff --git a/code/modules/mining/lavaland/ruins/gym.dm b/code/modules/mining/lavaland/ruins/gym.dm
index 634c2df859..1c535fd9ab 100644
--- a/code/modules/mining/lavaland/ruins/gym.dm
+++ b/code/modules/mining/lavaland/ruins/gym.dm
@@ -16,17 +16,20 @@
playsound(loc, pick(hit_sounds), 25, 1, -1)
if(isliving(user))
var/mob/living/L = user
+ SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "exercise", /datum/mood_event/exercise)
L.apply_status_effect(STATUS_EFFECT_EXERCISED)
-/obj/structure/stacklifter
+/obj/structure/weightmachine
name = "Weight Machine"
desc = "Just looking at this thing makes you feel tired."
- icon = 'goon/icons/obj/fitness.dmi'
- icon_state = "fitnesslifter"
density = TRUE
anchored = TRUE
+ var/icon_state_inuse
-/obj/structure/stacklifter/attack_hand(mob/living/user)
+/obj/structure/weightmachine/proc/AnimateMachine(mob/living/user)
+ return
+
+/obj/structure/weightmachine/attack_hand(mob/living/user)
. = ..()
if(.)
return
@@ -35,76 +38,58 @@
return
else
obj_flags |= IN_USE
- icon_state = "fitnesslifter2"
+ icon_state = icon_state_inuse
user.setDir(SOUTH)
user.Stun(80)
user.forceMove(src.loc)
var/bragmessage = pick("pushing it to the limit","going into overdrive","burning with determination","rising up to the challenge", "getting strong now","getting ripped")
user.visible_message("[user] is [bragmessage]! ")
- var/lifts = 0
- while (lifts++ < 6)
- if (user.loc != src.loc)
- break
- sleep(3)
- animate(user, pixel_y = -2, time = 3)
- sleep(3)
- animate(user, pixel_y = -4, time = 3)
- sleep(3)
- playsound(user, 'goon/sound/effects/spring.ogg', 60, 1)
+ AnimateMachine(user)
playsound(user, 'sound/machines/click.ogg', 60, 1)
obj_flags &= ~IN_USE
user.pixel_y = 0
var/finishmessage = pick("You feel stronger!","You feel like you can take on the world!","You feel robust!","You feel indestructible!")
- icon_state = "fitnesslifter"
+ SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "exercise", /datum/mood_event/exercise)
+ icon_state = initial(icon_state)
to_chat(user, finishmessage)
user.apply_status_effect(STATUS_EFFECT_EXERCISED)
-/obj/structure/weightlifter
- name = "Weight Machine"
- desc = "Just looking at this thing makes you feel tired."
+/obj/structure/weightmachine/stacklifter
+ icon = 'goon/icons/obj/fitness.dmi'
+ icon_state = "fitnesslifter"
+ icon_state_inuse = "fitnesslifter2"
+
+/obj/structure/weightmachine/stacklifter/AnimateMachine(mob/living/user)
+ var/lifts = 0
+ while (lifts++ < 6)
+ if (user.loc != src.loc)
+ break
+ sleep(3)
+ animate(user, pixel_y = -2, time = 3)
+ sleep(3)
+ animate(user, pixel_y = -4, time = 3)
+ sleep(3)
+ playsound(user, 'goon/sound/effects/spring.ogg', 60, 1)
+
+/obj/structure/weightmachine/weightlifter
icon = 'goon/icons/obj/fitness.dmi'
icon_state = "fitnessweight"
- density = TRUE
- anchored = TRUE
+ icon_state_inuse = "fitnessweight-c"
-/obj/structure/weightlifter/attack_hand(mob/living/user)
- . = ..()
- if(.)
- return
- if(obj_flags & IN_USE)
- to_chat(user, "It's already in use - wait a bit.")
- return
- else
- obj_flags |= IN_USE
- icon_state = "fitnessweight-c"
- user.setDir(SOUTH)
- user.Stun(80)
- user.forceMove(src.loc)
- var/mutable_appearance/swole_overlay = mutable_appearance(icon, "fitnessweight-w", WALL_OBJ_LAYER)
- add_overlay(swole_overlay)
- var/bragmessage = pick("pushing it to the limit","going into overdrive","burning with determination","rising up to the challenge", "getting strong now","getting ripped")
- user.visible_message("[user] is [bragmessage]! ")
- var/reps = 0
- user.pixel_y = 5
- while (reps++ < 6)
- if (user.loc != src.loc)
- break
-
- for (var/innerReps = max(reps, 1), innerReps > 0, innerReps--)
- sleep(3)
- animate(user, pixel_y = (user.pixel_y == 3) ? 5 : 3, time = 3)
-
- playsound(user, 'goon/sound/effects/spring.ogg', 60, 1)
-
- sleep(3)
- animate(user, pixel_y = 2, time = 3)
- sleep(3)
- playsound(user, 'sound/machines/click.ogg', 60, 1)
- obj_flags &= ~IN_USE
- animate(user, pixel_y = 0, time = 3)
- var/finishmessage = pick("You feel stronger!","You feel like you can take on the world!","You feel robust!","You feel indestructible!")
- icon_state = "fitnessweight"
- cut_overlay(swole_overlay)
- to_chat(user, "[finishmessage]")
- user.apply_status_effect(STATUS_EFFECT_EXERCISED)
\ No newline at end of file
+/obj/structure/weightmachine/weightlifter/AnimateMachine(mob/living/user)
+ var/mutable_appearance/swole_overlay = mutable_appearance(icon, "fitnessweight-w", WALL_OBJ_LAYER)
+ add_overlay(swole_overlay)
+ var/reps = 0
+ user.pixel_y = 5
+ while (reps++ < 6)
+ if (user.loc != src.loc)
+ break
+ for (var/innerReps = max(reps, 1), innerReps > 0, innerReps--)
+ sleep(3)
+ animate(user, pixel_y = (user.pixel_y == 3) ? 5 : 3, time = 3)
+ playsound(user, 'goon/sound/effects/spring.ogg', 60, 1)
+ sleep(3)
+ animate(user, pixel_y = 2, time = 3)
+ sleep(3)
+ cut_overlay(swole_overlay)
\ No newline at end of file
diff --git a/code/modules/mining/machine_processing.dm b/code/modules/mining/machine_processing.dm
index 026170da09..a004a6ae0d 100644
--- a/code/modules/mining/machine_processing.dm
+++ b/code/modules/mining/machine_processing.dm
@@ -82,7 +82,7 @@
/obj/machinery/mineral/processing_unit/Initialize()
. = ..()
proximity_monitor = new(src, 1)
- AddComponent(/datum/component/material_container, list(MAT_METAL, MAT_GLASS, MAT_SILVER, MAT_GOLD, MAT_DIAMOND, MAT_PLASMA, MAT_URANIUM, MAT_BANANIUM, MAT_TITANIUM, MAT_BLUESPACE), INFINITY, TRUE, list(/obj/item/stack))
+ AddComponent(/datum/component/material_container, list(MAT_METAL, MAT_GLASS, MAT_SILVER, MAT_GOLD, MAT_DIAMOND, MAT_PLASMA, MAT_URANIUM, MAT_BANANIUM, MAT_TITANIUM, MAT_BLUESPACE), INFINITY, TRUE, /obj/item/stack)
stored_research = new /datum/techweb/specialized/autounlocking/smelter
/obj/machinery/mineral/processing_unit/Destroy()
diff --git a/code/modules/mining/machine_redemption.dm b/code/modules/mining/machine_redemption.dm
index 3529214609..ce569ec055 100644
--- a/code/modules/mining/machine_redemption.dm
+++ b/code/modules/mining/machine_redemption.dm
@@ -13,7 +13,6 @@
speed_process = TRUE
circuit = /obj/item/circuitboard/machine/ore_redemption
layer = BELOW_OBJ_LAYER
- var/req_access_reclaim = ACCESS_MINING_STATION
var/obj/item/card/id/inserted_id
var/points = 0
var/ore_pickup_rate = 15
@@ -24,11 +23,12 @@
var/list/ore_buffer = list()
var/datum/techweb/stored_research
var/obj/item/disk/design_disk/inserted_disk
+ var/datum/component/remote_materials/materials
-/obj/machinery/mineral/ore_redemption/Initialize()
+/obj/machinery/mineral/ore_redemption/Initialize(mapload)
. = ..()
- AddComponent(/datum/component/material_container, list(MAT_METAL, MAT_GLASS, MAT_SILVER, MAT_GOLD, MAT_DIAMOND, MAT_PLASMA, MAT_URANIUM, MAT_BANANIUM, MAT_TITANIUM, MAT_BLUESPACE),INFINITY, FALSE, list(/obj/item/stack))
stored_research = new /datum/techweb/specialized/autounlocking/smelter
+ materials = AddComponent(/datum/component/remote_materials, "orm", mapload)
/obj/machinery/mineral/ore_redemption/Destroy()
QDEL_NULL(stored_research)
@@ -49,35 +49,43 @@
sheet_per_ore = sheet_per_ore_temp
/obj/machinery/mineral/ore_redemption/proc/smelt_ore(obj/item/stack/ore/O)
+ var/datum/component/material_container/mat_container = materials.mat_container
+ if (!mat_container)
+ return
ore_buffer -= O
if(O && O.refined_type)
points += O.points * point_upgrade * O.amount
- GET_COMPONENT(materials, /datum/component/material_container)
- var/material_amount = materials.get_item_material_amount(O)
+ var/material_amount = mat_container.get_item_material_amount(O)
if(!material_amount)
qdel(O) //no materials, incinerate it
- else if(!materials.has_space(material_amount * sheet_per_ore * O.amount)) //if there is no space, eject it
+ else if(!mat_container.has_space(material_amount * sheet_per_ore * O.amount)) //if there is no space, eject it
unload_mineral(O)
else
- materials.insert_item(O, sheet_per_ore) //insert it
+ var/mats = O.materials & mat_container.materials
+ var/amount = O.amount
+ var/id = inserted_id && inserted_id.registered_name
+ if (id)
+ id = " (ID: [id])"
+ mat_container.insert_item(O, sheet_per_ore) //insert it
+ materials.silo_log(src, "smelted", amount, "ores[id]", mats)
qdel(O)
/obj/machinery/mineral/ore_redemption/proc/can_smelt_alloy(datum/design/D)
- if(D.make_reagents.len)
+ var/datum/component/material_container/mat_container = materials.mat_container
+ if(!mat_container || D.make_reagents.len)
return FALSE
var/build_amount = 0
- GET_COMPONENT(materials, /datum/component/material_container)
for(var/mat_id in D.materials)
var/M = D.materials[mat_id]
- var/datum/material/redemption_mat = materials.materials[mat_id]
+ var/datum/material/redemption_mat = mat_container.materials[mat_id]
if(!M || !redemption_mat)
return FALSE
@@ -102,17 +110,18 @@
smelt_ore(ore)
/obj/machinery/mineral/ore_redemption/proc/send_console_message()
- if(!is_station_level(z))
+ var/datum/component/material_container/mat_container = materials.mat_container
+ if(!mat_container || !is_station_level(z))
return
message_sent = TRUE
+
var/area/A = get_area(src)
var/msg = "Now available in [A]: "
var/has_minerals = FALSE
- GET_COMPONENT(materials, /datum/component/material_container)
- for(var/mat_id in materials.materials)
- var/datum/material/M = materials.materials[mat_id]
+ for(var/mat_id in mat_container.materials)
+ var/datum/material/M = mat_container.materials[mat_id]
var/mineral_amount = M.amount / MINERAL_MATERIAL_AMOUNT
if(mineral_amount)
has_minerals = TRUE
@@ -126,7 +135,7 @@
D.createmessage("Ore Redemption Machine", "New minerals available!", msg, 1, 0)
/obj/machinery/mineral/ore_redemption/process()
- if(panel_open || !powered())
+ if(!materials.mat_container || panel_open || !powered())
return
var/atom/input = get_step(src, input_dir)
var/obj/structure/ore_box/OB = locate() in input
@@ -147,10 +156,6 @@
send_console_message()
/obj/machinery/mineral/ore_redemption/attackby(obj/item/W, mob/user, params)
- GET_COMPONENT(materials, /datum/component/material_container)
- if(default_pry_open(W))
- materials.retrieve_all()
- return
if(default_unfasten_wrench(user, W))
return
if(default_deconstruction_screwdriver(user, "ore_redemption-open", "ore_redemption", W))
@@ -170,22 +175,18 @@
interact(user)
return
- if(istype(W, /obj/item/multitool) && panel_open)
- input_dir = turn(input_dir, -90)
- output_dir = turn(output_dir, -90)
- to_chat(user, "You change [src]'s I/O settings, setting the input to [dir2text(input_dir)] and the output to [dir2text(output_dir)]. ")
- return
-
if(istype(W, /obj/item/disk/design_disk))
if(user.transferItemToLoc(W, src))
inserted_disk = W
return TRUE
return ..()
-/obj/machinery/mineral/ore_redemption/on_deconstruction()
- GET_COMPONENT(materials, /datum/component/material_container)
- materials.retrieve_all()
- ..()
+/obj/machinery/mineral/ore_redemption/multitool_act(mob/living/user, obj/item/multitool/I)
+ if (panel_open)
+ input_dir = turn(input_dir, -90)
+ output_dir = turn(output_dir, -90)
+ to_chat(user, "You change [src]'s I/O settings, setting the input to [dir2text(input_dir)] and the output to [dir2text(output_dir)]. ")
+ return TRUE
/obj/machinery/mineral/ore_redemption/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
@@ -201,16 +202,25 @@
data["claimedPoints"] = inserted_id.mining_points
data["materials"] = list()
- GET_COMPONENT(materials, /datum/component/material_container)
- for(var/mat_id in materials.materials)
- var/datum/material/M = materials.materials[mat_id]
- var/sheet_amount = M.amount ? M.amount / MINERAL_MATERIAL_AMOUNT : "0"
- data["materials"] += list(list("name" = M.name, "id" = M.id, "amount" = sheet_amount, "value" = ore_values[M.id] * point_upgrade))
+ var/datum/component/material_container/mat_container = materials.mat_container
+ if (mat_container)
+ for(var/mat_id in mat_container.materials)
+ var/datum/material/M = mat_container.materials[mat_id]
+ var/sheet_amount = M.amount ? M.amount / MINERAL_MATERIAL_AMOUNT : "0"
+ data["materials"] += list(list("name" = M.name, "id" = M.id, "amount" = sheet_amount, "value" = ore_values[M.id] * point_upgrade))
+
+ data["alloys"] = list()
+ for(var/v in stored_research.researched_designs)
+ var/datum/design/D = stored_research.researched_designs[v]
+ data["alloys"] += list(list("name" = D.name, "id" = D.id, "amount" = can_smelt_alloy(D)))
+
+ if (!mat_container)
+ data["disconnected"] = "local mineral storage is unavailable"
+ else if (!materials.silo)
+ data["disconnected"] = "no ore silo connection is available; storing locally"
+ else if (materials.on_hold())
+ data["disconnected"] = "mineral withdrawal is on hold"
- data["alloys"] = list()
- for(var/v in stored_research.researched_designs)
- var/datum/design/D = stored_research.researched_designs[v]
- data["alloys"] += list(list("name" = D.name, "id" = D.id, "amount" = can_smelt_alloy(D)))
data["diskDesigns"] = list()
if(inserted_disk)
data["hasDisk"] = TRUE
@@ -225,7 +235,7 @@
/obj/machinery/mineral/ore_redemption/ui_act(action, params)
if(..())
return
- GET_COMPONENT(materials, /datum/component/material_container)
+ var/datum/component/material_container/mat_container = materials.mat_container
switch(action)
if("Eject")
if(!inserted_id)
@@ -248,12 +258,17 @@
points = 0
return TRUE
if("Release")
-
- if(check_access(inserted_id) || allowed(usr)) //Check the ID inside, otherwise check the user
+ if(!mat_container)
+ return
+ if(materials.on_hold())
+ to_chat(usr, "Mineral access is on hold, please contact the quartermaster. ")
+ else if(!check_access(inserted_id) && !allowed(usr)) //Check the ID inside, otherwise check the user
+ to_chat(usr, "Required access not found. ")
+ else
var/mat_id = params["id"]
- if(!materials.materials[mat_id])
+ if(!mat_container.materials[mat_id])
return
- var/datum/material/mat = materials.materials[mat_id]
+ var/datum/material/mat = mat_container.materials[mat_id]
var/stored_amount = mat.amount / MINERAL_MATERIAL_AMOUNT
if(!stored_amount)
@@ -266,10 +281,10 @@
desired = input("How many sheets?", "How many sheets would you like to smelt?", 1) as null|num
var/sheets_to_remove = round(min(desired,50,stored_amount))
- materials.retrieve_sheets(sheets_to_remove, mat_id, get_step(src, output_dir))
-
- else
- to_chat(usr, "Required access not found. ")
+ var/count = mat_container.retrieve_sheets(sheets_to_remove, mat_id, get_step(src, output_dir))
+ var/list/mats = list()
+ mats[mat_id] = MINERAL_MATERIAL_AMOUNT
+ materials.silo_log(src, "released", -count, "sheets", mats)
return TRUE
if("diskInsert")
var/obj/item/disk/design_disk/disk = usr.get_active_held_item()
@@ -291,6 +306,11 @@
stored_research.add_design(inserted_disk.blueprints[n])
return TRUE
if("Smelt")
+ if(!mat_container)
+ return
+ if(materials.on_hold())
+ to_chat(usr, "Mineral access is on hold, please contact the quartermaster. ")
+ return
var/alloy_id = params["id"]
var/datum/design/alloy = stored_research.isDesignResearchedID(alloy_id)
if((check_access(inserted_id) || allowed(usr)) && alloy)
@@ -301,7 +321,8 @@
else
desired = input("How many sheets?", "How many sheets would you like to smelt?", 1) as null|num
var/amount = round(min(desired,50,smelt_amount))
- materials.use_amount(alloy.materials, amount)
+ mat_container.use_amount(alloy.materials, amount)
+ materials.silo_log(src, "released", -amount, "sheets", alloy.materials)
var/output
if(ispath(alloy.build_path, /obj/item/stack/sheet))
output = new alloy.build_path(src, amount)
@@ -311,20 +332,6 @@
else
to_chat(usr, "Required access not found. ")
return TRUE
- if("SmeltAll")
- var/alloy_id = params["id"]
- var/datum/design/alloy = stored_research.isDesignResearchedID(alloy_id)
- if((check_access(inserted_id) || allowed(usr)) && alloy)
- var/smelt_amount = can_smelt_alloy(alloy)
- while(smelt_amount > 0)
- materials.use_amount(alloy.materials)
- smelt_amount--
- var/output = new alloy.build_path(src)
- unload_mineral(output)
- CHECK_TICK
- else
- to_chat(usr, "Required access not found. ")
- return TRUE
/obj/machinery/mineral/ore_redemption/ex_act(severity, target)
do_sparks(5, TRUE, src)
diff --git a/code/modules/mining/machine_silo.dm b/code/modules/mining/machine_silo.dm
new file mode 100644
index 0000000000..bd1da0a90f
--- /dev/null
+++ b/code/modules/mining/machine_silo.dm
@@ -0,0 +1,234 @@
+GLOBAL_DATUM(ore_silo_default, /obj/machinery/ore_silo)
+GLOBAL_LIST_EMPTY(silo_access_logs)
+
+/obj/machinery/ore_silo
+ name = "ore silo"
+ desc = "An all-in-one bluespace storage and transmission system for the station's mineral distribution needs."
+ icon = 'icons/obj/mining.dmi'
+ icon_state = "silo"
+ density = TRUE
+ circuit = /obj/item/circuitboard/machine/ore_silo
+
+ var/list/holds = list()
+ var/list/datum/component/remote_materials/connected = list()
+ var/log_page = 1
+
+/obj/machinery/ore_silo/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/material_container,
+ list(MAT_METAL, MAT_GLASS, MAT_SILVER, MAT_GOLD, MAT_DIAMOND, MAT_PLASMA, MAT_URANIUM, MAT_BANANIUM, MAT_TITANIUM, MAT_BLUESPACE, MAT_PLASTIC),
+ INFINITY,
+ FALSE,
+ /obj/item/stack,
+ null,
+ null,
+ TRUE)
+ if (!GLOB.ore_silo_default && mapload && is_station_level(z))
+ GLOB.ore_silo_default = src
+
+/obj/machinery/ore_silo/Destroy()
+ if (GLOB.ore_silo_default == src)
+ GLOB.ore_silo_default = null
+
+ for(var/C in connected)
+ var/datum/component/remote_materials/mats = C
+ mats.disconnect_from(src)
+
+ GET_COMPONENT(materials, /datum/component/material_container)
+ materials.retrieve_all()
+
+ return ..()
+
+/obj/machinery/ore_silo/proc/remote_attackby(obj/machinery/M, mob/user, obj/item/stack/I)
+ GET_COMPONENT(materials, /datum/component/material_container)
+ // stolen from /datum/component/material_container/proc/OnAttackBy
+ if(user.a_intent != INTENT_HELP)
+ return
+ if(I.item_flags & ABSTRACT)
+ return
+ if(!istype(I) || (I.flags_1 & HOLOGRAM_1) || (I.item_flags & NO_MAT_REDEMPTION))
+ to_chat(user, "[M] won't accept [I]! ")
+ return
+ var/item_mats = I.materials & materials.materials
+ if(!length(item_mats))
+ to_chat(user, "[I] does not contain sufficient materials to be accepted by [M]. ")
+ return
+ // assumes unlimited space...
+ var/amount = I.amount
+ materials.user_insert(I, user)
+ silo_log(M, "deposited", amount, "sheets", item_mats)
+ return TRUE
+
+/obj/machinery/ore_silo/attackby(obj/item/W, mob/user, params)
+ if (istype(W, /obj/item/stack))
+ return remote_attackby(src, user, W)
+ return ..()
+
+/obj/machinery/ore_silo/ui_interact(mob/user)
+ user.set_machine(src)
+ var/datum/browser/popup = new(user, "ore_silo", null, 600, 550)
+ popup.set_content(generate_ui())
+ popup.open()
+
+/obj/machinery/ore_silo/proc/generate_ui()
+ GET_COMPONENT(materials, /datum/component/material_container)
+ var/list/ui = list("Ore Silo Stored Material: ")
+ var/any = FALSE
+ for(var/M in materials.materials)
+ var/datum/material/mat = materials.materials[M]
+ var/sheets = round(mat.amount) / MINERAL_MATERIAL_AMOUNT
+ if (sheets)
+ if (sheets >= 1)
+ ui += "
Eject "
+ else
+ ui += "
Eject "
+ if (sheets >= 20)
+ ui += "
20x "
+ else
+ ui += "
20x "
+ ui += "
[mat.name] : [sheets] sheets
"
+ any = TRUE
+ if(!any)
+ ui += "Nothing!"
+
+ ui += "
Connected Machines: "
+ for(var/C in connected)
+ var/datum/component/remote_materials/mats = C
+ var/atom/parent = mats.parent
+ var/hold_key = "[get_area(parent)]/[mats.category]"
+ ui += "
Remove "
+ ui += "
[holds[hold_key] ? "Allow" : "Hold"] "
+ ui += "
[parent.name] in [get_area_name(parent, TRUE)]
"
+ if(!connected.len)
+ ui += "Nothing!"
+
+ ui += "
Access Logs: "
+ var/list/logs = GLOB.silo_access_logs[REF(src)]
+ var/len = LAZYLEN(logs)
+ var/num_pages = 1 + round((len - 1) / 30)
+ var/page = CLAMP(log_page, 1, num_pages)
+ if(num_pages > 1)
+ for(var/i in 1 to num_pages)
+ if(i == page)
+ ui += "
[i] "
+ else
+ ui += "
[i] "
+
+ ui += "
"
+ any = FALSE
+ for(var/i in (page - 1) * 30 + 1 to min(page * 30, len))
+ var/datum/ore_silo_log/entry = logs[i]
+ ui += "[entry.formatted] "
+ any = TRUE
+ if (!any)
+ ui += "Nothing! "
+
+ ui += " "
+ return ui.Join()
+
+/obj/machinery/ore_silo/Topic(href, href_list)
+ if(..())
+ return
+ add_fingerprint(usr)
+ usr.set_machine(src)
+
+ if(href_list["remove"])
+ var/datum/component/remote_materials/mats = locate(href_list["remove"]) in connected
+ if (mats)
+ mats.disconnect_from(src)
+ connected -= mats
+ updateUsrDialog()
+ return TRUE
+ else if(href_list["hold1"])
+ holds[href_list["hold1"]] = TRUE
+ updateUsrDialog()
+ return TRUE
+ else if(href_list["hold0"])
+ holds -= href_list["hold0"]
+ updateUsrDialog()
+ return TRUE
+ else if(href_list["ejectsheet"])
+ var/eject_sheet = href_list["ejectsheet"]
+ GET_COMPONENT(materials, /datum/component/material_container)
+ var/count = materials.retrieve_sheets(text2num(href_list["eject_amt"]), eject_sheet, drop_location())
+ var/list/matlist = list()
+ matlist[eject_sheet] = MINERAL_MATERIAL_AMOUNT
+ silo_log(src, "ejected", -count, "sheets", matlist)
+ return TRUE
+ else if(href_list["page"])
+ log_page = text2num(href_list["page"]) || 1
+ updateUsrDialog()
+ return TRUE
+
+/obj/machinery/ore_silo/multitool_act(mob/living/user, obj/item/multitool/I)
+ if (istype(I))
+ to_chat(user, "You log [src] in the multitool's buffer. ")
+ I.buffer = src
+ return TRUE
+
+/obj/machinery/ore_silo/proc/silo_log(obj/machinery/M, action, amount, noun, list/mats)
+ if (!length(mats))
+ return
+ var/datum/ore_silo_log/entry = new(M, action, amount, noun, mats)
+
+ var/list/logs = GLOB.silo_access_logs[REF(src)]
+ if(!LAZYLEN(logs))
+ GLOB.silo_access_logs[REF(src)] = logs = list(entry)
+ else if(!logs[1].merge(entry))
+ logs.Insert(1, entry)
+
+ updateUsrDialog()
+ flick("silo_active", src)
+
+/obj/machinery/ore_silo/examine(mob/user)
+ ..()
+ to_chat(user, "[src] can be linked to techfabs, circuit printers and protolathes with a multitool. ")
+
+/datum/ore_silo_log
+ var/name // for VV
+ var/formatted // for display
+
+ var/timestamp
+ var/machine_name
+ var/area_name
+ var/action
+ var/noun
+ var/amount
+ var/list/materials
+
+/datum/ore_silo_log/New(obj/machinery/M, _action, _amount, _noun, list/mats=list())
+ timestamp = station_time_timestamp()
+ machine_name = M.name
+ area_name = get_area_name(M, TRUE)
+ action = _action
+ amount = _amount
+ noun = _noun
+ materials = mats.Copy()
+ for(var/each in materials)
+ materials[each] *= abs(_amount)
+ format()
+
+/datum/ore_silo_log/proc/merge(datum/ore_silo_log/other)
+ if (other == src || action != other.action || noun != other.noun)
+ return FALSE
+ if (machine_name != other.machine_name || area_name != other.area_name)
+ return FALSE
+
+ timestamp = other.timestamp
+ amount += other.amount
+ for(var/each in other.materials)
+ materials[each] += other.materials[each]
+ format()
+ return TRUE
+
+/datum/ore_silo_log/proc/format()
+ name = "[machine_name]: [action] [amount]x [noun]"
+
+ var/list/msg = list("([timestamp]) [machine_name] in [area_name] [action] [abs(amount)]x [noun] ")
+ var/sep = ""
+ for(var/key in materials)
+ var/val = round(materials[key]) / MINERAL_MATERIAL_AMOUNT
+ msg += sep
+ sep = ", "
+ msg += "[amount < 0 ? "-" : "+"][val] [copytext(key, 2)]"
+ formatted = msg.Join()
diff --git a/code/modules/mining/machine_stacking.dm b/code/modules/mining/machine_stacking.dm
index dcc9034e2d..aa3ab240d8 100644
--- a/code/modules/mining/machine_stacking.dm
+++ b/code/modules/mining/machine_stacking.dm
@@ -71,42 +71,51 @@
desc = "A machine that automatically stacks acquired materials. Controlled by a nearby console."
density = TRUE
circuit = /obj/item/circuitboard/machine/stacking_machine
+ input_dir = EAST
+ output_dir = WEST
var/obj/machinery/mineral/stacking_unit_console/CONSOLE
var/stk_types = list()
var/stk_amt = list()
var/stack_list[0] //Key: Type. Value: Instance of type.
- var/stack_amt = 50; //ammount to stack before releassing
- input_dir = EAST
- output_dir = WEST
+ var/stack_amt = 50 //amount to stack before releassing
+ var/datum/component/remote_materials/materials
+ var/force_connect = FALSE
-/obj/machinery/mineral/stacking_machine/Initialize()
+/obj/machinery/mineral/stacking_machine/Initialize(mapload)
. = ..()
proximity_monitor = new(src, 1)
+ materials = AddComponent(/datum/component/remote_materials, "stacking", mapload, FALSE, mapload && force_connect)
/obj/machinery/mineral/stacking_machine/HasProximity(atom/movable/AM)
if(istype(AM, /obj/item/stack/sheet) && AM.loc == get_step(src, input_dir))
process_sheet(AM)
-/obj/machinery/mineral/stacking_machine/multitool_act(mob/living/user, obj/item/I)
- if(istype(I, /obj/item/multitool))
- var/obj/item/multitool/M = I
- if(!istype(M.buffer, /obj/machinery/mineral/stacking_unit_console))
- to_chat(user, "The [I] has no linkage data in its buffer. ")
- return FALSE
- else
+/obj/machinery/mineral/stacking_machine/multitool_act(mob/living/user, obj/item/multitool/M)
+ if(istype(M))
+ if(istype(M.buffer, /obj/machinery/mineral/stacking_unit_console))
CONSOLE = M.buffer
CONSOLE.machine = src
- to_chat(user, "You link [src] to the console in [I]'s buffer. ")
+ to_chat(user, "You link [src] to the console in [M]'s buffer. ")
return TRUE
/obj/machinery/mineral/stacking_machine/proc/process_sheet(obj/item/stack/sheet/inp)
- if(!(inp.type in stack_list)) //It's the first of this sheet added
- var/obj/item/stack/sheet/s = new inp.type(src, 0)
- stack_list[inp.type] = s
- var/obj/item/stack/sheet/storage = stack_list[inp.type]
+ var/key = inp.merge_type
+ var/obj/item/stack/sheet/storage = stack_list[key]
+ if(!storage) //It's the first of this sheet added
+ stack_list[key] = storage = new inp.type(src, 0)
storage.amount += inp.amount //Stack the sheets
- while(storage.amount > stack_amt) //Get rid of excessive stackage
+ qdel(inp)
+
+ if(materials.silo && !materials.on_hold()) //Dump the sheets to the silo
+ var/matlist = storage.materials & materials.mat_container.materials
+ if (length(matlist))
+ var/inserted = materials.mat_container.insert_stack(storage)
+ materials.silo_log(src, "collected", inserted, "sheets", matlist)
+ if (QDELETED(storage))
+ stack_list -= key
+ return
+
+ while(storage.amount >= stack_amt) //Get rid of excessive stackage
var/obj/item/stack/sheet/out = new inp.type(null, stack_amt)
unload_mineral(out)
storage.amount -= stack_amt
- qdel(inp) //Let the old sheet garbage collect
diff --git a/code/modules/mining/machine_vending.dm b/code/modules/mining/machine_vending.dm
index a9230195ac..47e248f681 100644
--- a/code/modules/mining/machine_vending.dm
+++ b/code/modules/mining/machine_vending.dm
@@ -234,7 +234,7 @@
/obj/item/card/mining_point_card
name = "mining points card"
desc = "A small card preloaded with mining points. Swipe your ID card over it to transfer the points, then discard."
- icon_state = "data"
+ icon_state = "data_1"
var/points = 500
/obj/item/card/mining_point_card/attackby(obj/item/I, mob/user, params)
@@ -256,9 +256,10 @@
/obj/item/card/mining_access_card
name = "mining access card"
desc = "A small card, that when used on any ID, will add mining access."
- icon_state = "data"
+ icon_state = "data_1"
/obj/item/card/mining_access_card/afterattack(atom/movable/AM, mob/user, proximity)
+ . = ..()
if(istype(AM, /obj/item/card/id) && proximity)
var/obj/item/card/id/I = AM
I.access |= ACCESS_MINING
@@ -267,7 +268,6 @@
I.access |= ACCESS_CARGO
to_chat(user, "You upgrade [I] with mining access.")
qdel(src)
- ..()
/obj/item/storage/backpack/duffelbag/mining_conscript
name = "mining conscription kit"
diff --git a/code/modules/mining/minebot.dm b/code/modules/mining/minebot.dm
index 3809514f02..15ff372c47 100644
--- a/code/modules/mining/minebot.dm
+++ b/code/modules/mining/minebot.dm
@@ -270,6 +270,7 @@
icon = 'icons/obj/module.dmi'
/obj/item/mine_bot_upgrade/afterattack(mob/living/simple_animal/hostile/mining_drone/M, mob/user, proximity)
+ . = ..()
if(!istype(M) || !proximity)
return
upgrade_bot(M, user)
diff --git a/code/modules/mining/mint.dm b/code/modules/mining/mint.dm
index 66b6b94d6c..6b03be610d 100644
--- a/code/modules/mining/mint.dm
+++ b/code/modules/mining/mint.dm
@@ -15,7 +15,7 @@
/obj/machinery/mineral/mint/Initialize()
. = ..()
- AddComponent(/datum/component/material_container, list(MAT_METAL, MAT_PLASMA, MAT_SILVER, MAT_GOLD, MAT_URANIUM, MAT_DIAMOND, MAT_BANANIUM), MINERAL_MATERIAL_AMOUNT * 50, FALSE, list(/obj/item/stack))
+ AddComponent(/datum/component/material_container, list(MAT_METAL, MAT_PLASMA, MAT_SILVER, MAT_GOLD, MAT_URANIUM, MAT_DIAMOND, MAT_BANANIUM), MINERAL_MATERIAL_AMOUNT * 50, FALSE, /obj/item/stack)
/obj/machinery/mineral/mint/process()
var/turf/T = get_step(src, input_dir)
diff --git a/code/modules/mining/ores_coins.dm b/code/modules/mining/ores_coins.dm
index 9e4e1bbb53..69361e8685 100644
--- a/code/modules/mining/ores_coins.dm
+++ b/code/modules/mining/ores_coins.dm
@@ -335,7 +335,6 @@ GLOBAL_LIST_INIT(sand_recipes, list(\
/obj/item/coin/proc/manual_suicide(mob/living/user)
var/index = sideslist.Find(coinflip)
- message_admins("coinflip landed on [coinflip] which is [index] in sideslist")
if (index==2)//tails
user.visible_message("\the [src] lands on [coinflip]! [user] promptly falls over, dead! ")
user.adjustOxyLoss(200)
diff --git a/code/modules/mob/camera/camera.dm b/code/modules/mob/camera/camera.dm
index 5f99cd8aa2..883f5a034f 100644
--- a/code/modules/mob/camera/camera.dm
+++ b/code/modules/mob/camera/camera.dm
@@ -28,5 +28,5 @@
/mob/camera/forceMove(atom/destination)
loc = destination
-/mob/camera/emote(act, m_type=1, message = null)
+/mob/camera/emote(act, m_type=1, message = null, intentional = FALSE)
return
diff --git a/code/modules/mob/dead/dead.dm b/code/modules/mob/dead/dead.dm
index 30173c0a32..d886a41b0f 100644
--- a/code/modules/mob/dead/dead.dm
+++ b/code/modules/mob/dead/dead.dm
@@ -4,6 +4,7 @@ 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)
@@ -14,12 +15,12 @@ INITIALIZE_IMMEDIATE(/mob/dead)
prepare_huds()
- if(length(CONFIG_GET(keyed_string_list/cross_server)))
+ if(length(CONFIG_GET(keyed_list/cross_server)))
verbs += /mob/dead/proc/server_hop
set_focus(src)
return INITIALIZE_HINT_NORMAL
-/mob/dead/dust() //ghosts can't be vaporised.
+/mob/dead/dust(just_ash, drop_items, force) //ghosts can't be vaporised.
return
/mob/dead/gib() //ghosts can't be gibbed.
@@ -29,6 +30,10 @@ INITIALIZE_IMMEDIATE(/mob/dead)
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)
loc = destination
/mob/dead/Stat()
@@ -36,7 +41,7 @@ INITIALIZE_IMMEDIATE(/mob/dead)
if(!statpanel("Status"))
return
- //stat(null, "Game Mode: [SSticker.hide_mode ? "Secret" : "[GLOB.master_mode]"]") CIT CHANGE - obfuscates gamemode from player view
+ //stat(null, "Game Mode: [SSticker.hide_mode ? "Secret" : "[GLOB.master_mode]"]")
if(SSticker.HasRoundStarted())
return
@@ -59,7 +64,7 @@ INITIALIZE_IMMEDIATE(/mob/dead)
set desc= "Jump to the other server"
if(notransform)
return
- var/list/csa = CONFIG_GET(keyed_string_list/cross_server)
+ var/list/csa = CONFIG_GET(keyed_list/cross_server)
var/pick
switch(csa.len)
if(0)
@@ -92,3 +97,28 @@ INITIALIZE_IMMEDIATE(/mob/dead)
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)
diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm
index 14bd0ffaab..d5646c2fa4 100644
--- a/code/modules/mob/dead/new_player/new_player.dm
+++ b/code/modules/mob/dead/new_player/new_player.dm
@@ -53,7 +53,7 @@
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 = \"[ckey]\") AND id NOT IN (SELECT pollid FROM [format_table_name("poll_textreply")] WHERE ckey = \"[ckey]\")")
+ 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
diff --git a/code/modules/mob/dead/new_player/poll.dm b/code/modules/mob/dead/new_player/poll.dm
index 5ac1b4adb5..04a28f6b5a 100644
--- a/code/modules/mob/dead/new_player/poll.dm
+++ b/code/modules/mob/dead/new_player/poll.dm
@@ -103,23 +103,20 @@
var/output = "Player poll "
output += "
Question: [pollquestion] "
output += "
Feedback gathering runs from [pollstarttime] until [pollendtime] "
- if(!vote_text)
- output += "
"
- output += "
"
- output += " "
- output += " "
- output += " "
- output += " "
- output += " "
- else
- vote_text = replacetext(vote_text, "\n", "
")
- output += "[vote_text]"
+ output += "
"
+ output += " "
+ output += " "
+ output += " "
+ output += "Please provide feedback below. You can use any letters of the English alphabet, numbers and the symbols: . , ! ? : ; - "
+ output += "[vote_text] "
+ output += "
"
+ output += "
"
+ output += " "
+ output += " "
+ output += " "
+ output += " "
+ output += " "
+
src << browse(null ,"window=playerpolllist")
src << browse(output,"window=playerpoll;size=500x500")
if(POLLTYPE_RATING)
@@ -348,7 +345,8 @@
src << browse(output,"window=playerpoll;size=500x500")
return
-/mob/dead/new_player/proc/poll_check_voted(pollid, text = FALSE)
+//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"
@@ -361,13 +359,17 @@
return
if(query_hasvoted.NextRow())
qdel(query_hasvoted)
- to_chat(usr, "
You've already replied to this poll. ")
- return
+ if(!silent)
+ to_chat(usr, "
You've already replied to this poll. ")
+ 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
- return .
/mob/dead/new_player/proc/vote_rig_check()
@@ -487,7 +489,10 @@
//validate the poll
if (!vote_valid_check(pollid, client.holder, POLLTYPE_OPTION))
return 0
- var/adminrank = sanitizeSQL(poll_check_voted(pollid))
+ 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]')")
@@ -513,14 +518,21 @@
if(!replytext)
to_chat(usr, "The text you entered was blank. Please correct the text and submit again.")
return
- var/adminrank = sanitizeSQL(poll_check_voted(pollid, TRUE))
+ 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 = 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]')")
+ 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
diff --git a/code/modules/mob/dead/new_player/sprite_accessories.dm b/code/modules/mob/dead/new_player/sprite_accessories.dm
index 45ff7e40c8..ca9119eb43 100644
--- a/code/modules/mob/dead/new_player/sprite_accessories.dm
+++ b/code/modules/mob/dead/new_player/sprite_accessories.dm
@@ -64,59 +64,197 @@
//////////////////////
// Hair Definitions //
//////////////////////
-
-#define VHAIR(_name, new_state) /datum/sprite_accessory/hair/##new_state/icon_state=#new_state;/datum/sprite_accessory/hair/##new_state/name = "Virgo - " + #_name
-
/datum/sprite_accessory/hair
icon = 'icons/mob/human_face.dmi' // default icon for all hairs
-/datum/sprite_accessory/hair/short
- name = "Short Hair" // try to capitalize the names please~ // try to spell
- icon_state = "hair_a" // you do not need to define _s or _l sub-states, game automatically does this for you
+ // 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/shorthair2
- name = "Short Hair 2"
- icon_state = "hair_shorthair2"
+/datum/sprite_accessory/hair/afro
+ name = "Afro"
+ icon_state = "hair_afro"
-/datum/sprite_accessory/hair/shorthair3
- name = "Short Hair 3"
- icon_state = "hair_shorthair3"
+/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/long
- name = "Shoulder-length Hair"
- icon_state = "hair_b"
+/datum/sprite_accessory/hair/devillock
+ name = "Devil Lock"
+ icon_state = "hair_devilock"
-/datum/sprite_accessory/hair/longer
- name = "Long Hair"
- icon_state = "hair_vlong"
+/datum/sprite_accessory/hair/dreadlocks
+ name = "Dreadlocks"
+ icon_state = "hair_dreads"
-/datum/sprite_accessory/hair/over_eye
- name = "Over Eye"
- icon_state = "hair_shortovereye"
+/datum/sprite_accessory/hair/drillhair
+ name = "Drill Hair"
+ icon_state = "hair_drillhair"
-/datum/sprite_accessory/hair/long_over_eye
- name = "Long Over Eye"
- icon_state = "hair_longovereye"
+/datum/sprite_accessory/hair/drillhairextended
+ name = "Drill Hair (Extended)"
+ icon_state = "hair_drillhairextended"
-/datum/sprite_accessory/hair/longest2
- name = "Very Long Over Eye"
- icon_state = "hair_longest2"
+/datum/sprite_accessory/hair/emo
+ name = "Emo"
+ icon_state = "hair_emo"
-/datum/sprite_accessory/hair/longest
- name = "Very Long Hair"
- icon_state = "hair_longest"
+/datum/sprite_accessory/hair/feather
+ name = "Feather"
+ icon_state = "hair_feather"
-/datum/sprite_accessory/hair/longfringe
- name = "Long Fringe"
- icon_state = "hair_longfringe"
+/datum/sprite_accessory/hair/sargeant
+ name = "Flat Top"
+ icon_state = "hair_sargeant"
-/datum/sprite_accessory/hair/longestalt
- name = "Longer Fringe"
- icon_state = "hair_vlongfringe"
+/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"
@@ -130,6 +268,134 @@
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"
+
+/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"
@@ -150,161 +416,61 @@
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 = "Side Pony"
+ name = "Ponytail (Side)"
icon_state = "hair_sidetail"
/datum/sprite_accessory/hair/sidetail2
- name = "Side Pony 2"
+ name = "Ponytail (Side) 2"
icon_state = "hair_sidetail2"
/datum/sprite_accessory/hair/sidetail3
- name = "Side Pony 3"
+ name = "Ponytail (Side) 3"
icon_state = "hair_sidetail3"
/datum/sprite_accessory/hair/sidetail4
- name = "Side Pony 4"
+ name = "Ponytail (Side) 4"
icon_state = "hair_sidetail4"
-/datum/sprite_accessory/hair/oneshoulder
- name = "One Shoulder"
- icon_state = "hair_oneshoulder"
-
-/datum/sprite_accessory/hair/tressshoulder
- name = "Tress Shoulder"
- icon_state = "hair_tressshoulder"
-
-/datum/sprite_accessory/hair/parted
- name = "Parted"
- icon_state = "hair_parted"
-
-/datum/sprite_accessory/hair/pompadour
- name = "Pompadour"
- icon_state = "hair_pompadour"
-
-/datum/sprite_accessory/hair/bigpompadour
- name = "Big Pompadour"
- icon_state = "hair_bigpompadour"
-
/datum/sprite_accessory/hair/quiff
name = "Quiff"
icon_state = "hair_quiff"
-/datum/sprite_accessory/hair/bedhead
- name = "Bedhead"
- icon_state = "hair_bedhead"
+/datum/sprite_accessory/hair/short
+ name = "Short Hair"
+ icon_state = "hair_a"
-/datum/sprite_accessory/hair/bedhead2
- name = "Bedhead 2"
- icon_state = "hair_bedheadv2"
+/datum/sprite_accessory/hair/shorthair2
+ name = "Short Hair 2"
+ icon_state = "hair_shorthair2"
-/datum/sprite_accessory/hair/bedhead3
- name = "Bedhead 3"
- icon_state = "hair_bedheadv3"
+/datum/sprite_accessory/hair/shorthair3
+ name = "Short Hair 3"
+ icon_state = "hair_shorthair3"
-/datum/sprite_accessory/hair/messy
- name = "Messy"
- icon_state = "hair_messy"
+/datum/sprite_accessory/hair/shoulderlength
+ name = "Shoulder-length Hair"
+ icon_state = "hair_b"
-/datum/sprite_accessory/hair/beehive
- name = "Beehive"
- icon_state = "hair_beehive"
+/datum/sprite_accessory/hair/sidecut
+ name = "Sidecut"
+ icon_state = "hair_sidecut"
-/datum/sprite_accessory/hair/beehive2
- name = "Beehive 2"
- icon_state = "hair_beehivev2"
+/datum/sprite_accessory/hair/skinhead
+ name = "Skinhead"
+ icon_state = "hair_skinhead"
-/datum/sprite_accessory/hair/bobcurl
- name = "Bobcurl"
- icon_state = "hair_bobcurl"
-
-/datum/sprite_accessory/hair/bob
- name = "Bob"
- icon_state = "hair_bobcut"
-
-/datum/sprite_accessory/hair/bowl
- name = "Bowl"
- icon_state = "hair_bowlcut"
-
-/datum/sprite_accessory/hair/buzz
- name = "Buzzcut"
- icon_state = "hair_buzzcut"
-
-/datum/sprite_accessory/hair/crew
- name = "Crewcut"
- icon_state = "hair_crewcut"
-
-/datum/sprite_accessory/hair/combover
- name = "Combover"
- icon_state = "hair_combover"
-
-/datum/sprite_accessory/hair/devillock
- name = "Devil Lock"
- icon_state = "hair_devilock"
-
-/datum/sprite_accessory/hair/drillhairextended
- name = "Extended Drill Hair"
- icon_state = "hair_drillhairextended"
-
-/datum/sprite_accessory/hair/dreadlocks
- name = "Dreadlocks"
- icon_state = "hair_dreads"
-
-/datum/sprite_accessory/hair/curls
- name = "Curls"
- icon_state = "hair_curls"
-
-/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 = "Big Afro"
- icon_state = "hair_bigafro"
-
-/datum/sprite_accessory/hair/sargeant
- name = "Flat Top"
- icon_state = "hair_sargeant"
-
-/datum/sprite_accessory/hair/emo
- name = "Emo"
- icon_state = "hair_emo"
-
-/datum/sprite_accessory/hair/longemo
- name = "Long Emo"
- icon_state = "hair_longemo"
-
-/datum/sprite_accessory/hair/fag
- name = "Flow Hair"
- icon_state = "hair_f"
-
-/datum/sprite_accessory/hair/feather
- name = "Feather"
- icon_state = "hair_feather"
-
-/datum/sprite_accessory/hair/hitop
- name = "Hitop"
- icon_state = "hair_hitop"
-
-/datum/sprite_accessory/hair/mohawk
- name = "Mohawk"
- icon_state = "hair_d"
-
-/datum/sprite_accessory/hair/reversemohawk
- name = "Reverse Mohawk"
- icon_state = "hair_reversemohawk"
-
-/datum/sprite_accessory/hair/jensen
- name = "Jensen Hair"
- icon_state = "hair_jensen"
-
-/datum/sprite_accessory/hair/gelled
- name = "Gelled Back"
- icon_state = "hair_gelled"
+/datum/sprite_accessory/hair/protagonist
+ name = "Slightly Long Hair"
+ icon_state = "hair_protagonist"
/datum/sprite_accessory/hair/spiky
name = "Spiky"
@@ -318,122 +484,6 @@
name = "Spiky 3"
icon_state = "hair_spiky2"
-/datum/sprite_accessory/hair/protagonist
- name = "Slightly Long"
- icon_state = "hair_protagonist"
-
-/datum/sprite_accessory/hair/kusangi
- name = "Kusanagi Hair"
- icon_state = "hair_kusanagi"
-
-/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/pigtail
- name = "Pigtails 3"
- icon_state = "hair_pigtails2"
-
-/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/antenna
- name = "Ahoge"
- icon_state = "hair_antenna"
-
-/datum/sprite_accessory/hair/front_braid
- name = "Braided front"
- icon_state = "hair_braidfront"
-
-/datum/sprite_accessory/hair/lowbraid
- name = "Low Braid"
- icon_state = "hair_hbraid"
-
-/datum/sprite_accessory/hair/not_floorlength_braid
- name = "High Braid"
- icon_state = "hair_braid2"
-
-/datum/sprite_accessory/hair/shortbraid
- name = "Short Braid"
- icon_state = "hair_shortbraid"
-
-/datum/sprite_accessory/hair/braid
- name = "Floorlength Braid"
- icon_state = "hair_braid"
-
-/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/updo
- name = "Updo"
- icon_state = "hair_updo"
-
-/datum/sprite_accessory/hair/skinhead
- name = "Skinhead"
- icon_state = "hair_skinhead"
-
-/datum/sprite_accessory/hair/longbangs
- name = "Long Bangs"
- icon_state = "hair_lbangs"
-
-/datum/sprite_accessory/hair/balding
- name = "Balding Hair"
- icon_state = "hair_e"
-
-/datum/sprite_accessory/hair/bald
- name = "Bald"
- icon_state = null
-
-/datum/sprite_accessory/hair/parted
- name = "Side Part"
- icon_state = "hair_part"
-
-/datum/sprite_accessory/hair/braided
- name = "Braided"
- icon_state = "hair_braided"
-
-/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/braidtail
- name = "Braided Tail"
- icon_state = "hair_braidtail"
-
-/datum/sprite_accessory/hair/bigflattop
- name = "Big Flat Top"
- icon_state = "hair_bigflattop"
-
-/datum/sprite_accessory/hair/drillhair
- name = "Drill Hair"
- icon_state = "hair_drillhair"
-
-/datum/sprite_accessory/hair/keanu
- name = "Keanu Hair"
- icon_state = "hair_keanu"
-
/datum/sprite_accessory/hair/swept
name = "Swept Back Hair"
icon_state = "hair_swept"
@@ -442,369 +492,121 @@
name = "Swept Back Hair 2"
icon_state = "hair_swept2"
-/datum/sprite_accessory/hair/business
- name = "Business Hair"
- icon_state = "hair_business"
+/datum/sprite_accessory/hair/tressshoulder
+ name = "Tress Shoulder"
+ icon_state = "hair_tressshoulder"
-/datum/sprite_accessory/hair/business2
- name = "Business Hair 2"
- icon_state = "hair_business2"
+/datum/sprite_accessory/hair/updo
+ name = "Updo"
+ icon_state = "hair_updo"
-/datum/sprite_accessory/hair/business3
- name = "Business Hair 3"
- icon_state = "hair_business3"
+/datum/sprite_accessory/hair/longer
+ name = "Very Long Hair"
+ icon_state = "hair_vlong"
-/datum/sprite_accessory/hair/business4
- name = "Business Hair 4"
- icon_state = "hair_business4"
+/datum/sprite_accessory/hair/longest
+ name = "Very Long Hair 2"
+ icon_state = "hair_longest"
-/datum/sprite_accessory/hair/hedgehog
- name = "Hedgehog Hair"
- icon_state = "hair_hedgehog"
+/datum/sprite_accessory/hair/longest2
+ name = "Very Long Over Eye"
+ icon_state = "hair_longest2"
-/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/boddicker
- name = "Boddicker"
- icon_state = "hair_boddicker"
-
-/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/pixie
- name = "Pixie Cut"
- icon_state = "hair_pixie"
-
-/datum/sprite_accessory/hair/megaeyebrows
- name = "Mega Eyebrows"
- icon_state = "hair_megaeyebrows"
-
-/datum/sprite_accessory/hair/highponytail
- name = "High Ponytail"
- icon_state = "hair_highponytail"
-
-/datum/sprite_accessory/hair/longponytail
- name = "Long Ponytail"
- icon_state = "hair_longstraightponytail"
-
-/datum/sprite_accessory/hair/sidepartlongalt
- name = "Long Side Part"
- icon_state = "hair_longsidepart"
-
-/datum/sprite_accessory/hair/sidecut
- name = "Sidecut"
- icon_state = "hair_sidecut"
-
-/datum/sprite_accessory/hair/largebun
- name = "Large Bun"
- icon_state = "hair_largebun"
-
-
-//VIRGO PORTED HAIRS
-VHAIR(Short Hair Rosa, hair_rosa_s)
-VHAIR(Short Hair 80s, hair_80s_s)
-VHAIR(Long Bedhead, hair_long_bedhead_s)
-VHAIR(Dave, hair_dave_s)
-VHAIR(Country, hair_country_s)
-VHAIR(Shy, hair_shy_s)
-VHAIR(Unshaven Mohawk, hair_unshaven_mohawk_s)
-VHAIR(Manbun, hair_manbun_s)
-VHAIR(Longer Bedhead, hair_longer_bedhead_s)
-VHAIR(Ponytail, hair_ponytail_s)
-VHAIR(Ziegler, hair_ziegler_s)
-VHAIR(Emo Fringe, hair_emofringe_s)
-VHAIR(Very Short Over Eye Alt, hair_veryshortovereyealternate_s)
-VHAIR(Shorthime, hair_shorthime_s)
-VHAIR(High Tight, hair_hightight_s)
-VHAIR(Thinning Front, hair_thinningfront_s)
-VHAIR(Big Afro, hair_bigafro_s)
-VHAIR(Afro, hair_afro_s)
-VHAIR(High Braid, hair_hbraid_s)
-VHAIR(Braid, hair_braid_s)
-VHAIR(Sargeant, hair_sargeant_s)
-VHAIR(Gelled, hair_gelled_s)
-VHAIR(Kagami, hair_kagami_s)
-VHAIR(ShortTail, hair_stail_s)
-VHAIR(Gentle, hair_gentle_s)
-VHAIR(Grande, hair_grande_s)
-VHAIR(Bobcurl, hair_bobcurl_s)
-VHAIR(Pompadeur, hair_pompadour_s)
-VHAIR(Plait, hair_plait_s)
-VHAIR(Long, hair_long_s)
-VHAIR(Rattail, hair_rattail_s)
-VHAIR(Tajspiky, hair_tajspiky_s)
-VHAIR(Messy, hair_messy_s)
-VHAIR(Bangs, hair_bangs_s)
-VHAIR(TBraid, hair_tbraid_s)
-VHAIR(Toriyama2, hair_toriyama2_s)
-VHAIR(CIA, hair_cia_s)
-VHAIR(Mulder, hair_mulder_s)
-VHAIR(Scully, hair_scully_s)
-VHAIR(Nitori, hair_nitori_s)
-VHAIR(Joestar, hair_joestar_s)
-VHAIR(Ponytail4, hair_ponytail4_s)
-VHAIR(Ponytail5, hair_ponytail5_s)
-VHAIR(Beehive2, hair_beehive2_s)
-VHAIR(Short Braid, hair_shortbraid_s)
-VHAIR(Reverse Mohawk, hair_reversemohawk_s)
-VHAIR(SHort Bangs, hair_shortbangs_s)
-VHAIR(Half Shaved, hair_halfshaved_s)
-VHAIR(Longer Alt 2, hair_longeralt2_s)
-VHAIR(Bun, hair_bun_s)
-VHAIR(Curly, hair_curly_s)
-VHAIR(Victory, hair_victory_s)
-VHAIR(Ponytail6, hair_ponytail6_s)
-VHAIR(Undercut3, hair_undercut3_s)
-VHAIR(Bobcut Alt, hair_bobcultalt_s)
-VHAIR(Fingerwave, hair_fingerwave_s)
-VHAIR(Oxton, hair_oxton_s)
-VHAIR(Poofy2, hair_poofy2_s)
-VHAIR(Fringe Tail, hair_fringetail_s)
-VHAIR(Bun3, hair_bun3_s)
-VHAIR(Wisp, hair_wisp_s)
-VHAIR(Undercut2, hair_undercut2_s)
-VHAIR(TBob, hair_tbob_s)
-VHAIR(Spiky Ponytail, hair_spikyponytail_s)
-VHAIR(Rowbun, hair_rowbun_s)
-VHAIR(Rowdualtail, hair_rowdualtail_s)
-VHAIR(Rowbraid, hair_rowbraid_s)
-VHAIR(Shaved Mohawk, hair_shavedmohawk_s)
-VHAIR(Topknot, hair_topknot_s)
-VHAIR(Ronin, hair_ronin_s)
-VHAIR(Bowlcut2, hair_bowlcut2_s)
-VHAIR(Thinning Rear, hair_thinningrear_s)
-VHAIR(Thinning, hair_thinning_s)
-VHAIR(Jade, hair_jade_s)
-VHAIR(Bedhead, hair_bedhead_s)
-VHAIR(Dreadlocks, hair_dreads_s)
-VHAIR(Very Long, hair_vlong_s)
-VHAIR(Jensen, hair_jensen_s)
-VHAIR(Halfbang, hair_halfbang_s)
-VHAIR(Kusangi, hair_kusangi_s)
-VHAIR(Ponytail, hair_ponytail_s)
-VHAIR(Ponytail3, hair_ponytail3_s)
-VHAIR(Halfbang Alt, hair_halfbang_alt_s)
-VHAIR(Bedhead V2, hair_bedheadv2_s)
-VHAIR(Long Fringe, hair_longfringe_s)
-VHAIR(Flair, hair_flair_s)
-VHAIR(Bedhead V3, hair_bedheadv3_s)
-VHAIR(Himecut, hair_himecut_s)
-VHAIR(Curls, hair_curls_s)
-VHAIR(Very Long Fringe, hair_vlongfringe_s)
-VHAIR(Longest, hair_longest_s)
-VHAIR(Father, hair_father_s)
-VHAIR(Emo Long, hair_emolong_s)
-VHAIR(Short Hair 3, hair_shorthair3_s)
-VHAIR(Double Bun, hair_doublebun_s)
-VHAIR(Sleeze, hair_sleeze_s)
-VHAIR(Twintail, hair_twintail_s)
-VHAIR(Emo 2, hair_emo2_s)
-VHAIR(Low Fade, hair_lowfade_s)
-VHAIR(Med Fade, hair_medfade_s)
-VHAIR(High Fade, hair_highfade_s)
-VHAIR(Bald Fade, hair_baldfade_s)
-VHAIR(No Fade, hair_nofade_s)
-VHAIR(Trim Flat, hair_trimflat_s)
-VHAIR(Shaved, hair_shaved_s)
-VHAIR(Trimmed, hair_trimmed_s)
-VHAIR(Tight Bun, hair_tightbun_s)
-VHAIR(Short Hair 4, hair_d_s)
-VHAIR(Short Hair 5, hair_e_s)
-VHAIR(Short Hair 6, hair_f_s)
-VHAIR(Skinhead, hair_skinhead_s)
-VHAIR(Afro2, hair_afro2_s)
-VHAIR(Bobcut, hair_bobcut_s)
-VHAIR(Emo, hair_emo_s)
-VHAIR(Long Over Eye, hair_longovereye_s)
-VHAIR(Feather, hair_feather_s)
-VHAIR(Hitop, hair_hitop_s)
-VHAIR(Short Over Eye, hair_shortoverye_s)
-VHAIR(Straight, hair_straight_s)
-VHAIR(Buzzcut, hair_buzzcut_s)
-VHAIR(Combover, hair_combover_s)
-VHAIR(Crewcut, hair_crewcut_s)
-VHAIR(Devillock, hair_devilock_s)
-VHAIR(Clean, hair_clean_s)
-VHAIR(Shaggy, hair_shaggy_s)
-VHAIR(Updo, hair_updo_s)
-VHAIR(Mohawk, hair_mohawk_s)
-VHAIR(Odango, hair_odango_s)
-VHAIR(Ombre, hair_ombre_s)
-VHAIR(Parted, hair_parted_s)
-VHAIR(Quiff, hair_quiff_s)
-VHAIR(Volaju, hair_volaju_s)
-VHAIR(Bun2, hair_bun2_s)
-VHAIR(Rows1, hair_rows1_s)
-VHAIR(Rows2, hair_rows2_s)
-VHAIR(Dandy Pompadour, hair_dandypompadour_s)
-VHAIR(Poofy, hair_poofy_s)
-VHAIR(Toriyama, hair_toriyama_s)
-VHAIR(Drillruru, hair_drillruru_s)
-VHAIR(Bowlcut, hair_bowlcut_s)
-VHAIR(Coffee House, hair_coffeehouse_s)
-VHAIR(Family Man, hair_thefamilyman_s)
-VHAIR(Shaved Part, hair_shavedpart_s)
-VHAIR(Modern, hair_modern_s)
-VHAIR(One Shoulder, hair_oneshoulder_s)
-VHAIR(Very Short Over Eye, hair_veryshortovereye_s)
-VHAIR(Unkept, hair_unkept_s)
-VHAIR(Wife, hair_wife_s)
-VHAIR(Nia, hair_nia_s)
-VHAIR(Undercut, hair_undercut_s)
-VHAIR(Bobcut Alt, hair_bobcutalt_s)
-VHAIR(Short Hair 4 alt, hair_shorthair4_s)
-VHAIR(Tressshoulder, hair_tressshoulder_s)
-
-//END
-
-#undef VHAIR
+/datum/sprite_accessory/hair/longestalt
+ name = "Very Long with Fringe"
+ icon_state = "hair_vlongfringe"
/////////////////////////////
// Facial Hair Definitions //
/////////////////////////////
-#define VFACE(_name, new_state) /datum/sprite_accessory/facial_hair/##new_state/icon_state=#new_state;;/datum/sprite_accessory/facial_hair/##new_state/name="Virgo" + #_name
-
/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/watson
- name = "Watson Mustache"
- icon_state = "facial_watson"
-
-/datum/sprite_accessory/facial_hair/hogan
- name = "Hulk Hogan Mustache"
- icon_state = "facial_hogan" //-Neek
-
-/datum/sprite_accessory/facial_hair/vandyke
- name = "Van Dyke Mustache"
- icon_state = "facial_vandyke"
-
-/datum/sprite_accessory/facial_hair/chaplin
- name = "Square Mustache"
- icon_state = "facial_chaplin"
-
-/datum/sprite_accessory/facial_hair/selleck
- name = "Selleck Mustache"
- icon_state = "facial_selleck"
-
-/datum/sprite_accessory/facial_hair/neckbeard
- name = "Neckbeard"
- icon_state = "facial_neckbeard"
-
-/datum/sprite_accessory/facial_hair/fullbeard
- name = "Full Beard"
- icon_state = "facial_fullbeard"
-
-/datum/sprite_accessory/facial_hair/longbeard
- name = "Long Beard"
- icon_state = "facial_longbeard"
-
-/datum/sprite_accessory/facial_hair/vlongbeard
- name = "Very Long Beard"
- icon_state = "facial_wise"
-
/datum/sprite_accessory/facial_hair/elvis
- name = "Elvis Sideburns"
+ name = "Sideburns (Elvis)"
icon_state = "facial_elvis"
-/datum/sprite_accessory/facial_hair/abe
- name = "Abraham Lincoln Beard"
- icon_state = "facial_abe"
-
-/datum/sprite_accessory/facial_hair/chinstrap
- name = "Chinstrap"
- icon_state = "facial_chin"
-
-/datum/sprite_accessory/facial_hair/hip
- name = "Hipster Beard"
- icon_state = "facial_hip"
-
-/datum/sprite_accessory/facial_hair/gt
- name = "Goatee"
- icon_state = "facial_gt"
-
-/datum/sprite_accessory/facial_hair/jensen
- name = "Jensen Beard"
- icon_state = "facial_jensen"
-
-/datum/sprite_accessory/facial_hair/dwarf
- name = "Dwarf Beard"
- icon_state = "facial_dwarf"
-
-/datum/sprite_accessory/facial_hair/fiveoclock
- name = "Five o Clock Shadow"
- icon_state = "facial_fiveoclock"
-
-/datum/sprite_accessory/facial_hair/fu
- name = "Fu Manchu"
- icon_state = "facial_fumanchu"
-
-/datum/sprite_accessory/facial_hair/brokenman
- name = "Broken Man"
- icon_state = "facial_brokenman"
-
-VFACE(Watson, facial_watson_s)
-VFACE(Chaplin, facial_chaplin_s)
-VFACE(Fullbeard, facial_fullbeard_s)
-VFACE(Vandyke, facial_vandyke_s)
-VFACE(Elvis, facial_elvis_s)
-VFACE(Abe, facial_abe_s)
-VFACE(Chin, facial_chin_s)
-VFACE(GT, facial_gt_s)
-VFACE(Hip, facial_hip_s)
-VFACE(Hogan, facial_hogan_s)
-VFACE(Selleck, facial_selleck_s)
-VFACE(Neckbeard, facial_neckbeard_s)
-VFACE(Longbeard, facial_longbeard_s)
-VFACE(Dwarf, facial_dwarf_s)
-VFACE(Sideburn, facial_sideburn_s)
-VFACE(Mutton, facial_mutton_s)
-VFACE(Moustache, facial_moustache_s)
-VFACE(Pencilstache, facial_pencilstache_s)
-VFACE(Goatee, facial_goatee_s)
-VFACE(Smallstache, facial_smallstache_s)
-VFACE(Volaju, facial_volaju_s)
-VFACE(3 O\'clock, facial_3oclock_s)
-VFACE(5 O\'clock, facial_5oclock_s)
-VFACE(7 O\'clock, facial_7oclock_s)
-VFACE(5 O\'clock Moustache, facial_5oclockmoustache_s)
-VFACE(7 O\'clock, facial_7oclockmoustache_s)
-VFACE(Walrus, facial_walrus_s)
-VFACE(Muttonmus, facial_muttonmus_s)
-VFACE(Wise, facial_wise_s)
-VFACE(Martial Artist, facial_martialartist_s)
-VFACE(Dorsalfnil, facial_dorsalfnil_s)
-VFACE(Hornadorns, facial_hornadorns_s)
-VFACE(Spike, facial_spike_s)
-VFACE(Chinhorns, facial_chinhorns_s)
-VFACE(Cropped Fullbeard, facial_croppedfullbeard_s)
-VFACE(Chinless Beard, facial_chinlessbeard_s)
-VFACE(Moonshiner, facial_moonshiner_s)
-VFACE(Tribearder, facial_tribearder_s)
-
-#undef VFACE
///////////////////////////
// Underwear Definitions //
@@ -817,110 +619,80 @@ VFACE(Tribearder, facial_tribearder_s)
icon_state = null
gender = NEUTER
-/datum/sprite_accessory/underwear/male_white
- name = "Mens White"
- icon_state = "male_white"
- gender = MALE
-
-/datum/sprite_accessory/underwear/male_grey
- name = "Mens Grey"
- icon_state = "male_grey"
- gender = MALE
-
-/datum/sprite_accessory/underwear/male_green
- name = "Mens Green"
- icon_state = "male_green"
- gender = MALE
-
-/datum/sprite_accessory/underwear/male_blue
- name = "Mens Blue"
- icon_state = "male_blue"
- gender = MALE
-
-/datum/sprite_accessory/underwear/male_black
- name = "Mens Black"
- icon_state = "male_black"
- gender = MALE
-
/datum/sprite_accessory/underwear/male_mankini
name = "Mankini"
icon_state = "male_mankini"
gender = MALE
-/datum/sprite_accessory/underwear/male_hearts
- name = "Mens Hearts Boxer"
- icon_state = "male_hearts"
+/datum/sprite_accessory/underwear/male_black
+ name = "Men's Black"
+ icon_state = "male_black"
gender = MALE
/datum/sprite_accessory/underwear/male_blackalt
- name = "Mens Black Boxer"
+ name = "Men's Black Boxer"
icon_state = "male_blackalt"
gender = MALE
+/datum/sprite_accessory/underwear/male_blue
+ name = "Men's Blue"
+ icon_state = "male_blue"
+ gender = MALE
+
+/datum/sprite_accessory/underwear/male_green
+ name = "Men's Green"
+ icon_state = "male_green"
+ gender = MALE
+
+/datum/sprite_accessory/underwear/male_grey
+ name = "Men's Grey"
+ icon_state = "male_grey"
+ gender = MALE
+
/datum/sprite_accessory/underwear/male_greyalt
- name = "Mens Grey Boxer"
+ name = "Men's Grey Boxer"
icon_state = "male_greyalt"
gender = MALE
-/datum/sprite_accessory/underwear/male_stripe
- name = "Mens Striped Boxer"
- icon_state = "male_stripe"
- gender = MALE
-
-/datum/sprite_accessory/underwear/male_commie
- name = "Mens Striped Commie Boxer"
- icon_state = "male_commie"
- gender = MALE
-
-/datum/sprite_accessory/underwear/male_uk
- name = "Mens Striped UK Boxer"
- icon_state = "male_uk"
- gender = MALE
-
-/datum/sprite_accessory/underwear/male_usastripe
- name = "Mens Striped Freedom Boxer"
- icon_state = "male_assblastusa"
+/datum/sprite_accessory/underwear/male_hearts
+ name = "Men's Hearts Boxer"
+ icon_state = "male_hearts"
gender = MALE
/datum/sprite_accessory/underwear/male_kinky
- name = "Mens Kinky"
+ name = "Men's Kinky"
icon_state = "male_kinky"
gender = MALE
/datum/sprite_accessory/underwear/male_red
- name = "Mens Red"
+ name = "Men's Red"
icon_state = "male_red"
gender = MALE
-/datum/sprite_accessory/underwear/female_red
- name = "Ladies Red"
- icon_state = "female_red"
- gender = FEMALE
+/datum/sprite_accessory/underwear/male_stripe
+ name = "Men's Striped Boxer"
+ icon_state = "male_stripe"
+ gender = MALE
-/datum/sprite_accessory/underwear/female_white
- name = "Ladies White"
- icon_state = "female_white"
- gender = FEMALE
+/datum/sprite_accessory/underwear/male_commie
+ name = "Men's Striped Commie Boxer"
+ icon_state = "male_commie"
+ gender = MALE
-/datum/sprite_accessory/underwear/female_yellow
- name = "Ladies Yellow"
- icon_state = "female_yellow"
- gender = FEMALE
+/datum/sprite_accessory/underwear/male_usastripe
+ name = "Men's Striped Freedom Boxer"
+ icon_state = "male_assblastusa"
+ gender = MALE
-/datum/sprite_accessory/underwear/female_blue
- name = "Ladies Blue"
- icon_state = "female_blue"
- gender = FEMALE
+/datum/sprite_accessory/underwear/male_uk
+ name = "Men's Striped UK Boxer"
+ icon_state = "male_uk"
+ gender = MALE
-/datum/sprite_accessory/underwear/female_black
- name = "Ladies Black"
- icon_state = "female_black"
- gender = FEMALE
-
-/datum/sprite_accessory/underwear/female_thong
- name = "Ladies Thong"
- icon_state = "female_thong"
- gender = FEMALE
+/datum/sprite_accessory/underwear/male_white
+ name = "Men's White"
+ icon_state = "male_white"
+ gender = MALE
/datum/sprite_accessory/underwear/female_babydoll
name = "Babydoll"
@@ -928,89 +700,119 @@ VFACE(Tribearder, facial_tribearder_s)
gender = FEMALE
/datum/sprite_accessory/underwear/female_babyblue
- name = "Ladies Baby-Blue"
+ name = "Ladies' Baby-Blue"
icon_state = "female_babyblue"
gender = FEMALE
-/datum/sprite_accessory/underwear/female_green
- name = "Ladies Green"
- icon_state = "female_green"
- gender = FEMALE
-
-/datum/sprite_accessory/underwear/female_pink
- name = "Ladies Pink"
- icon_state = "female_pink"
- gender = FEMALE
-
-/datum/sprite_accessory/underwear/female_kinky
- name = "Ladies Kinky"
- icon_state = "female_kinky"
- gender = FEMALE
-
-/datum/sprite_accessory/underwear/female_whitealt
- name = "Ladies White Sport"
- icon_state = "female_whitealt"
- gender = FEMALE
-
-/datum/sprite_accessory/underwear/female_blackalt
- name = "Ladies Black Sport"
- icon_state = "female_blackalt"
- gender = FEMALE
-
-/datum/sprite_accessory/underwear/female_white_neko
- name = "Ladies White Neko"
- icon_state = "female_neko_white"
+/datum/sprite_accessory/underwear/female_black
+ name = "Ladies' Black"
+ icon_state = "female_black"
gender = FEMALE
/datum/sprite_accessory/underwear/female_black_neko
- name = "Ladies Black Neko"
+ name = "Ladies' Black Neko"
icon_state = "female_neko_black"
gender = FEMALE
-/datum/sprite_accessory/underwear/female_usastripe
- name = "Ladies Freedom"
- icon_state = "female_assblastusa"
+/datum/sprite_accessory/underwear/female_blackalt
+ name = "Ladies' Black Sport"
+ icon_state = "female_blackalt"
gender = FEMALE
-/datum/sprite_accessory/underwear/female_uk
- name = "Ladies UK"
- icon_state = "female_uk"
+/datum/sprite_accessory/underwear/female_blue
+ name = "Ladies' Blue"
+ icon_state = "female_blue"
gender = FEMALE
/datum/sprite_accessory/underwear/female_commie
- name = "Ladies Commie"
+ name = "Ladies' Commie"
icon_state = "female_commie"
gender = FEMALE
+/datum/sprite_accessory/underwear/female_usastripe
+ name = "Ladies' Freedom"
+ icon_state = "female_assblastusa"
+ gender = FEMALE
+
+/datum/sprite_accessory/underwear/female_green
+ name = "Ladies' Green"
+ icon_state = "female_green"
+ gender = FEMALE
+
+/datum/sprite_accessory/underwear/female_kinky
+ name = "Ladies' Kinky"
+ icon_state = "female_kinky"
+ gender = FEMALE
+
+/datum/sprite_accessory/underwear/female_pink
+ name = "Ladies' Pink"
+ icon_state = "female_pink"
+ gender = FEMALE
+
+/datum/sprite_accessory/underwear/female_red
+ name = "Ladies' Red"
+ icon_state = "female_red"
+ gender = FEMALE
+
/datum/sprite_accessory/underwear/swimsuit
- name = "Ladies Black Swimsuit"
+ name = "Ladies' Swimsuit (Black)"
icon_state = "swim_black"
gender = FEMALE
/datum/sprite_accessory/underwear/swimsuit_blue
- name = "Ladies Blue Swimsuit"
+ name = "Ladies' Swimsuit (Blue)"
icon_state = "swim_blue"
gender = FEMALE
/datum/sprite_accessory/underwear/swimsuit_green
- name = "Ladies Green Swimsuit"
+ name = "Ladies' Swimsuit (Green)"
icon_state = "swim_green"
gender = FEMALE
/datum/sprite_accessory/underwear/swimsuit_purple
- name = "Ladies Purple Swimsuit"
+ name = "Ladies' Swimsuit (Purple)"
icon_state = "swim_purple"
gender = FEMALE
/datum/sprite_accessory/underwear/swimsuit_red
- name = "Ladies Red Swimsuit"
+ name = "Ladies' Swimsuit (Red)"
icon_state = "swim_red"
gender = FEMALE
+/datum/sprite_accessory/underwear/female_thong
+ name = "Ladies' Thong"
+ icon_state = "female_thong"
+ gender = FEMALE
+
+/datum/sprite_accessory/underwear/female_uk
+ name = "Ladies' UK"
+ icon_state = "female_uk"
+ gender = FEMALE
+
+/datum/sprite_accessory/underwear/female_white
+ name = "Ladies' White"
+ icon_state = "female_white"
+ gender = FEMALE
+
+/datum/sprite_accessory/underwear/female_white_neko
+ name = "Ladies' White Neko"
+ icon_state = "female_neko_white"
+ gender = FEMALE
+
+/datum/sprite_accessory/underwear/female_whitealt
+ name = "Ladies' White Sport"
+ icon_state = "female_whitealt"
+ gender = FEMALE
+
+/datum/sprite_accessory/underwear/female_yellow
+ name = "Ladies' Yellow"
+ icon_state = "female_yellow"
+ gender = FEMALE
////////////////////////////
// Undershirt Definitions //
////////////////////////////
+
/datum/sprite_accessory/undershirt
icon = 'icons/mob/underwear.dmi'
@@ -1019,264 +821,186 @@ VFACE(Tribearder, facial_tribearder_s)
icon_state = null
gender = NEUTER
-/datum/sprite_accessory/undershirt/shirt_white
- name = "White Shirt"
- icon_state = "shirt_white"
+// please make sure they're sorted alphabetically and categorized
+
+/datum/sprite_accessory/undershirt/bluejersey
+ name = "Jersey (Blue)"
+ icon_state = "shirt_bluejersey"
gender = NEUTER
-/datum/sprite_accessory/undershirt/shirt_black
- name = "Black Shirt"
- icon_state = "shirt_black"
- gender = NEUTER
-
-/datum/sprite_accessory/undershirt/shirt_grey
- name = "Grey Shirt"
- icon_state = "shirt_grey"
- gender = NEUTER
-
-/datum/sprite_accessory/undershirt/tank_white
- name = "White Tank Top"
- icon_state = "tank_white"
- gender = NEUTER
-
-/datum/sprite_accessory/undershirt/tank_black
- name = "Black Tank Top"
- icon_state = "tank_black"
- gender = NEUTER
-
-/datum/sprite_accessory/undershirt/tank_grey
- name = "Grey Tank Top"
- icon_state = "tank_grey"
- gender = NEUTER
-
-/datum/sprite_accessory/undershirt/female_midriff
- name = "Midriff Tank Top"
- icon_state = "tank_midriff"
- gender = FEMALE
-
-/datum/sprite_accessory/undershirt/lover
- name = "Lover Shirt"
- icon_state = "lover"
- gender = NEUTER
-
-/datum/sprite_accessory/undershirt/ian
- name = "Blue Ian Shirt"
- icon_state = "ian"
- gender = NEUTER
-
-/datum/sprite_accessory/undershirt/uk
- name = "UK Shirt"
- icon_state = "uk"
- gender = NEUTER
-
-/datum/sprite_accessory/undershirt/usa
- name = "USA Shirt"
- icon_state = "shirt_assblastusa"
- gender = NEUTER
-
-/datum/sprite_accessory/undershirt/ilovent
- name = "I Love NT Shirt"
- icon_state = "ilovent"
- gender = NEUTER
-
-/datum/sprite_accessory/undershirt/peace
- name = "Peace Shirt"
- icon_state = "peace"
- gender = NEUTER
-
-/datum/sprite_accessory/undershirt/mondmondjaja
- name = "Band Shirt"
- icon_state = "band"
- gender = NEUTER
-
-/datum/sprite_accessory/undershirt/pacman
- name = "Pogoman Shirt"
- icon_state = "pogoman"
- gender = NEUTER
-
-/datum/sprite_accessory/undershirt/matroska
- name = "Matroska Shirt"
- icon_state = "matroska"
- gender = NEUTER
-
-/datum/sprite_accessory/undershirt/whiteshortsleeve
- name = "White Short-sleeved Shirt"
- icon_state = "whiteshortsleeve"
- gender = NEUTER
-
-/datum/sprite_accessory/undershirt/purpleshortsleeve
- name = "Purple Short-sleeved Shirt"
- icon_state = "purpleshortsleeve"
- gender = NEUTER
-
-/datum/sprite_accessory/undershirt/blueshortsleeve
- name = "Blue Short-sleeved Shirt"
- icon_state = "blueshortsleeve"
- gender = NEUTER
-
-/datum/sprite_accessory/undershirt/greenshortsleeve
- name = "Green Short-sleeved Shirt"
- icon_state = "greenshortsleeve"
- gender = NEUTER
-
-/datum/sprite_accessory/undershirt/blackshortsleeve
- name = "Black Short-sleeved Shirt"
- icon_state = "blackshortsleeve"
- gender = NEUTER
-
-/datum/sprite_accessory/undershirt/blueshirt
- name = "Blue T-Shirt"
- icon_state = "blueshirt"
- gender = NEUTER
-
-/datum/sprite_accessory/undershirt/redshirt
- name = "Red T-Shirt"
- icon_state = "redshirt"
- gender = NEUTER
-
-/datum/sprite_accessory/undershirt/yellowshirt
- name = "Yellow T-Shirt"
- icon_state = "yellowshirt"
- gender = NEUTER
-
-/datum/sprite_accessory/undershirt/greenshirt
- name = "Green T-Shirt"
- icon_state = "greenshirt"
+/datum/sprite_accessory/undershirt/redjersey
+ name = "Jersey (Red)"
+ icon_state = "shirt_redjersey"
gender = NEUTER
/datum/sprite_accessory/undershirt/bluepolo
- name = "Blue Polo Shirt"
+ name = "Polo Shirt (Blue)"
icon_state = "bluepolo"
gender = NEUTER
+/datum/sprite_accessory/undershirt/grayyellowpolo
+ name = "Polo Shirt (Gray-Yellow)"
+ icon_state = "grayyellowpolo"
+ gender = NEUTER
+
/datum/sprite_accessory/undershirt/redpolo
- name = "Red Polo Shirt"
+ name = "Polo Shirt (Red)"
icon_state = "redpolo"
gender = NEUTER
/datum/sprite_accessory/undershirt/whitepolo
- name = "White Polo Shirt"
+ name = "Polo Shirt (White)"
icon_state = "whitepolo"
gender = NEUTER
-/datum/sprite_accessory/undershirt/grayyellowpolo
- name = "Gray-Yellow Polo Shirt"
- icon_state = "grayyellowpolo"
+/datum/sprite_accessory/undershirt/alienshirt
+ name = "Shirt (Alien)"
+ icon_state = "shirt_alien"
gender = NEUTER
-/datum/sprite_accessory/undershirt/redtop
- name = "Red Top"
- icon_state = "redtop"
- gender = FEMALE
-
-/datum/sprite_accessory/undershirt/whitetop
- name = "White Top"
- icon_state = "whitetop"
- gender = FEMALE
-
-/datum/sprite_accessory/undershirt/greenshirtsport
- name = "Green Sports Shirt"
- icon_state = "greenshirtsport"
+/datum/sprite_accessory/undershirt/mondmondjaja
+ name = "Shirt (Band)"
+ icon_state = "band"
gender = NEUTER
-/datum/sprite_accessory/undershirt/redshirtsport
- name = "Red Sports Shirt"
- icon_state = "redshirtsport"
- gender = NEUTER
-
-/datum/sprite_accessory/undershirt/blueshirtsport
- name = "Blue Sports Shirt"
- icon_state = "blueshirtsport"
- gender = NEUTER
-
-/datum/sprite_accessory/undershirt/ss13
- name = "SS13 Shirt"
- icon_state = "shirt_ss13"
- gender = NEUTER
-
-/datum/sprite_accessory/undershirt/tankfire
- name = "Fire Tank Top"
- icon_state = "tank_fire"
- gender = NEUTER
-
-/datum/sprite_accessory/undershirt/question
- name = "Question Shirt"
- icon_state = "shirt_question"
- gender = NEUTER
-
-/datum/sprite_accessory/undershirt/skull
- name = "Skull Shirt"
- icon_state = "shirt_skull"
- gender = NEUTER
-
-/datum/sprite_accessory/undershirt/commie
- name = "Commie Shirt"
- icon_state = "shirt_commie"
- gender = NEUTER
-
-/datum/sprite_accessory/undershirt/nano
- name = "Nanotrasen Shirt"
- icon_state = "shirt_nano"
- gender = NEUTER
-
-/datum/sprite_accessory/undershirt/stripe
- name = "Striped Shirt"
- icon_state = "shirt_stripes"
+/datum/sprite_accessory/undershirt/shirt_black
+ name = "Shirt (Black)"
+ icon_state = "shirt_black"
gender = NEUTER
/datum/sprite_accessory/undershirt/blueshirt
- name = "Blue Shirt"
+ name = "Shirt (Blue)"
icon_state = "shirt_blue"
gender = NEUTER
-/datum/sprite_accessory/undershirt/redshirt
- name = "Red Shirt"
- icon_state = "shirt_red"
- gender = NEUTER
-
-/datum/sprite_accessory/undershirt/tank_red
- name = "Red Tank Top"
- icon_state = "tank_red"
- gender = NEUTER
-
-/datum/sprite_accessory/undershirt/greenshirt
- name = "Green Shirt"
- icon_state = "shirt_green"
- gender = NEUTER
-
-/datum/sprite_accessory/undershirt/meat
- name = "Meat Shirt"
- icon_state = "shirt_meat"
- gender = NEUTER
-
-/datum/sprite_accessory/undershirt/tiedye
- name = "Tie-dye Shirt"
- icon_state = "shirt_tiedye"
- gender = NEUTER
-
-/datum/sprite_accessory/undershirt/redjersey
- name = "Red Jersey"
- icon_state = "shirt_redjersey"
- gender = NEUTER
-
-/datum/sprite_accessory/undershirt/bluejersey
- name = "Blue Jersey"
- icon_state = "shirt_bluejersey"
- gender = NEUTER
-
-/datum/sprite_accessory/undershirt/tankstripe
- name = "Striped Tank Top"
- icon_state = "tank_stripes"
- gender = NEUTER
-
/datum/sprite_accessory/undershirt/clownshirt
- name = "Clown Shirt"
+ name = "Shirt (Clown)"
icon_state = "shirt_clown"
gender = NEUTER
-/datum/sprite_accessory/undershirt/alienshirt
- name = "Alien Shirt"
- icon_state = "shirt_alien"
+/datum/sprite_accessory/undershirt/commie
+ name = "Shirt (Commie)"
+ icon_state = "shirt_commie"
+ gender = NEUTER
+
+/datum/sprite_accessory/undershirt/greenshirt
+ name = "Shirt (Green)"
+ icon_state = "shirt_green"
+ gender = NEUTER
+
+/datum/sprite_accessory/undershirt/shirt_grey
+ name = "Shirt (Grey)"
+ icon_state = "shirt_grey"
+ gender = NEUTER
+
+/datum/sprite_accessory/undershirt/ian
+ name = "Shirt (Ian)"
+ icon_state = "ian"
+ gender = NEUTER
+
+/datum/sprite_accessory/undershirt/ilovent
+ name = "Shirt (I Love NT)"
+ icon_state = "ilovent"
+ gender = NEUTER
+
+/datum/sprite_accessory/undershirt/lover
+ name = "Shirt (Lover)"
+ icon_state = "lover"
+ gender = NEUTER
+
+/datum/sprite_accessory/undershirt/matroska
+ name = "Shirt (Matroska)"
+ icon_state = "matroska"
+ gender = NEUTER
+
+/datum/sprite_accessory/undershirt/meat
+ name = "Shirt (Meat)"
+ icon_state = "shirt_meat"
+ gender = NEUTER
+
+/datum/sprite_accessory/undershirt/nano
+ name = "Shirt (Nanotrasen)"
+ icon_state = "shirt_nano"
+ gender = NEUTER
+
+/datum/sprite_accessory/undershirt/peace
+ name = "Shirt (Peace)"
+ icon_state = "peace"
+ gender = NEUTER
+
+/datum/sprite_accessory/undershirt/pacman
+ name = "Shirt (Pogoman)"
+ icon_state = "pogoman"
+ gender = NEUTER
+
+/datum/sprite_accessory/undershirt/question
+ name = "Shirt (Question)"
+ icon_state = "shirt_question"
+ gender = NEUTER
+
+/datum/sprite_accessory/undershirt/redshirt
+ name = "Shirt (Red)"
+ icon_state = "shirt_red"
+ gender = NEUTER
+
+/datum/sprite_accessory/undershirt/skull
+ name = "Shirt (Skull)"
+ icon_state = "shirt_skull"
+ gender = NEUTER
+
+/datum/sprite_accessory/undershirt/ss13
+ name = "Shirt (SS13)"
+ icon_state = "shirt_ss13"
+ gender = NEUTER
+
+/datum/sprite_accessory/undershirt/stripe
+ name = "Shirt (Striped)"
+ icon_state = "shirt_stripes"
+ gender = NEUTER
+
+/datum/sprite_accessory/undershirt/tiedye
+ name = "Shirt (Tie-dye)"
+ icon_state = "shirt_tiedye"
+ gender = NEUTER
+
+/datum/sprite_accessory/undershirt/uk
+ name = "Shirt (UK)"
+ icon_state = "uk"
+ gender = NEUTER
+
+/datum/sprite_accessory/undershirt/usa
+ name = "Shirt (USA)"
+ icon_state = "shirt_assblastusa"
+ gender = NEUTER
+
+/datum/sprite_accessory/undershirt/shirt_white
+ name = "Shirt (White)"
+ icon_state = "shirt_white"
+ gender = NEUTER
+
+/datum/sprite_accessory/undershirt/blackshortsleeve
+ name = "Short-sleeved Shirt (Black)"
+ icon_state = "blackshortsleeve"
+ gender = NEUTER
+
+/datum/sprite_accessory/undershirt/blueshortsleeve
+ name = "Short-sleeved Shirt (Blue)"
+ icon_state = "blueshortsleeve"
+ gender = NEUTER
+
+/datum/sprite_accessory/undershirt/greenshortsleeve
+ name = "Short-sleeved Shirt (Green)"
+ icon_state = "greenshortsleeve"
+ gender = NEUTER
+
+/datum/sprite_accessory/undershirt/purpleshortsleeve
+ name = "Short-sleeved Shirt (Purple)"
+ icon_state = "purpleshortsleeve"
+ gender = NEUTER
+
+/datum/sprite_accessory/undershirt/whiteshortsleeve
+ name = "Short-sleeved Shirt (White)"
+ icon_state = "whiteshortsleeve"
gender = NEUTER
/datum/sprite_accessory/undershirt/sports_bra
@@ -1285,13 +1009,94 @@ VFACE(Tribearder, facial_tribearder_s)
gender = NEUTER
/datum/sprite_accessory/undershirt/sports_bra2
- name = "Alt Sports Bra"
+ name = "Sports Bra (Alt)"
icon_state = "sports_bra_alt"
gender = NEUTER
+/datum/sprite_accessory/undershirt/blueshirtsport
+ name = "Sports Shirt (Blue)"
+ icon_state = "blueshirtsport"
+ gender = NEUTER
+
+/datum/sprite_accessory/undershirt/greenshirtsport
+ name = "Sports Shirt (Green)"
+ icon_state = "greenshirtsport"
+ gender = NEUTER
+
+/datum/sprite_accessory/undershirt/redshirtsport
+ name = "Sports Shirt (Red)"
+ icon_state = "redshirtsport"
+ gender = NEUTER
+
+/datum/sprite_accessory/undershirt/tank_black
+ name = "Tank Top (Black)"
+ icon_state = "tank_black"
+ gender = NEUTER
+
+/datum/sprite_accessory/undershirt/tankfire
+ name = "Tank Top (Fire)"
+ icon_state = "tank_fire"
+ gender = NEUTER
+
+/datum/sprite_accessory/undershirt/tank_grey
+ name = "Tank Top (Grey)"
+ icon_state = "tank_grey"
+ gender = NEUTER
+
+/datum/sprite_accessory/undershirt/female_midriff
+ name = "Tank Top (Midriff)"
+ icon_state = "tank_midriff"
+ gender = FEMALE
+
+/datum/sprite_accessory/undershirt/tank_red
+ name = "Tank Top (Red)"
+ icon_state = "tank_red"
+ gender = NEUTER
+
+/datum/sprite_accessory/undershirt/tankstripe
+ name = "Tank Top (Striped)"
+ icon_state = "tank_stripes"
+ gender = NEUTER
+
+/datum/sprite_accessory/undershirt/tank_white
+ name = "Tank Top (White)"
+ icon_state = "tank_white"
+ gender = NEUTER
+
+/datum/sprite_accessory/undershirt/redtop
+ name = "Top (Red)"
+ icon_state = "redtop"
+ gender = FEMALE
+
+/datum/sprite_accessory/undershirt/whitetop
+ name = "Top (White)"
+ icon_state = "whitetop"
+ gender = FEMALE
+
+/datum/sprite_accessory/undershirt/tshirt_blue
+ name = "T-Shirt (Blue)"
+ icon_state = "blueshirt"
+ gender = NEUTER
+
+/datum/sprite_accessory/undershirt/tshirt_green
+ name = "T-Shirt (Green)"
+ icon_state = "greenshirt"
+ gender = NEUTER
+
+/datum/sprite_accessory/undershirt/tshirt_red
+ name = "T-Shirt (Red)"
+ icon_state = "redshirt"
+ gender = NEUTER
+
+/datum/sprite_accessory/undershirt/yellowshirt
+ name = "T-Shirt (Yellow)"
+ icon_state = "yellowshirt"
+ gender = NEUTER
+
///////////////////////
// Socks Definitions //
///////////////////////
+
/datum/sprite_accessory/socks
icon = 'icons/mob/underwear.dmi'
@@ -1299,90 +1104,92 @@ VFACE(Tribearder, facial_tribearder_s)
name = "Nude"
icon_state = null
-/datum/sprite_accessory/socks/white_norm
- name = "Normal White"
- icon_state = "white_norm"
-
-/datum/sprite_accessory/socks/black_norm
- name = "Normal Black"
- icon_state = "black_norm"
-
-/datum/sprite_accessory/socks/white_short
- name = "Short White"
- icon_state = "white_short"
-
-/datum/sprite_accessory/socks/black_short
- name = "Short Black"
- icon_state = "black_short"
-
-/datum/sprite_accessory/socks/white_knee
- name = "Knee-high White"
- icon_state = "white_knee"
+// please make sure they're sorted alphabetically and categorized
/datum/sprite_accessory/socks/black_knee
- name = "Knee-high Black"
+ name = "Knee-high (Black)"
icon_state = "black_knee"
-/datum/sprite_accessory/socks/thin_knee
- name = "Knee-high Thin"
- icon_state = "thin_knee"
-
-/datum/sprite_accessory/socks/striped_knee
- name = "Knee-high Striped"
- icon_state = "striped_knee"
-
-/datum/sprite_accessory/socks/rainbow_knee
- name = "Knee-high Rainbow"
- icon_state = "rainbow_knee"
-
-/datum/sprite_accessory/socks/white_thigh
- name = "Thigh-high White"
- icon_state = "white_thigh"
-
-/datum/sprite_accessory/socks/black_thigh
- name = "Thigh-high Black"
- icon_state = "black_thigh"
-
-/datum/sprite_accessory/socks/thin_thigh
- name = "Thigh-high Thin"
- icon_state = "thin_thigh"
-
-/datum/sprite_accessory/socks/striped_thigh
- name = "Thigh-high Striped"
- icon_state = "striped_thigh"
-
-/datum/sprite_accessory/socks/rainbow_thigh
- name = "Thigh-high Rainbow"
- icon_state = "rainbow_thigh"
-
-/datum/sprite_accessory/socks/usa_knee
- name = "Knee-High Freedom Stripes"
- icon_state = "assblastusa_knee"
-
-/datum/sprite_accessory/socks/usa_thigh
- name = "Thigh-high Freedom Stripes"
- icon_state = "assblastusa_thigh"
-
-/datum/sprite_accessory/socks/uk_knee
- name = "Knee-High UK Stripes"
- icon_state = "uk_knee"
-
-/datum/sprite_accessory/socks/uk_thigh
- name = "Thigh-high UK Stripes"
- icon_state = "uk_thigh"
-
/datum/sprite_accessory/socks/commie_knee
- name = "Knee-High Commie Stripes"
+ name = "Knee-High (Commie)"
icon_state = "commie_knee"
-/datum/sprite_accessory/socks/commie_thigh
- name = "Thigh-high Commie Stripes"
- icon_state = "commie_thigh"
+/datum/sprite_accessory/socks/usa_knee
+ name = "Knee-High (Freedom)"
+ icon_state = "assblastusa_knee"
+
+/datum/sprite_accessory/socks/rainbow_knee
+ name = "Knee-high (Rainbow)"
+ icon_state = "rainbow_knee"
+
+/datum/sprite_accessory/socks/striped_knee
+ name = "Knee-high (Striped)"
+ icon_state = "striped_knee"
+
+/datum/sprite_accessory/socks/thin_knee
+ name = "Knee-high (Thin)"
+ icon_state = "thin_knee"
+
+/datum/sprite_accessory/socks/uk_knee
+ name = "Knee-High (UK)"
+ icon_state = "uk_knee"
+
+/datum/sprite_accessory/socks/white_knee
+ name = "Knee-high (White)"
+ icon_state = "white_knee"
+
+/datum/sprite_accessory/socks/black_norm
+ name = "Normal (Black)"
+ icon_state = "black_norm"
+
+/datum/sprite_accessory/socks/white_norm
+ name = "Normal (White)"
+ icon_state = "white_norm"
/datum/sprite_accessory/socks/pantyhose
name = "Pantyhose"
icon_state = "pantyhose"
+/datum/sprite_accessory/socks/black_short
+ name = "Short (Black)"
+ icon_state = "black_short"
+
+/datum/sprite_accessory/socks/white_short
+ name = "Short (White)"
+ icon_state = "white_short"
+
+/datum/sprite_accessory/socks/black_thigh
+ name = "Thigh-high (Black)"
+ icon_state = "black_thigh"
+
+/datum/sprite_accessory/socks/commie_thigh
+ name = "Thigh-high (Commie)"
+ icon_state = "commie_thigh"
+
+/datum/sprite_accessory/socks/usa_thigh
+ name = "Thigh-high (Freedom)"
+ icon_state = "assblastusa_thigh"
+
+/datum/sprite_accessory/socks/rainbow_thigh
+ name = "Thigh-high (Rainbow)"
+ icon_state = "rainbow_thigh"
+
+/datum/sprite_accessory/socks/striped_thigh
+ name = "Thigh-high (Striped)"
+ icon_state = "striped_thigh"
+
+/datum/sprite_accessory/socks/thin_thigh
+ name = "Thigh-high (Thin)"
+ icon_state = "thin_thigh"
+
+/datum/sprite_accessory/socks/uk_thigh
+ name = "Thigh-high (UK)"
+ icon_state = "uk_thigh"
+
+/datum/sprite_accessory/socks/white_thigh
+ name = "Thigh-high (White)"
+ icon_state = "white_thigh"
+
//////////.//////////////////
// MutantParts Definitions //
/////////////////////////////
diff --git a/code/modules/mob/dead/observer/login.dm b/code/modules/mob/dead/observer/login.dm
index 7bba60bef9..1b328dbc69 100644
--- a/code/modules/mob/dead/observer/login.dm
+++ b/code/modules/mob/dead/observer/login.dm
@@ -12,5 +12,9 @@
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()
diff --git a/code/modules/mob/dead/observer/logout.dm b/code/modules/mob/dead/observer/logout.dm
index ad94dbecc8..3ca6405a0c 100644
--- a/code/modules/mob/dead/observer/logout.dm
+++ b/code/modules/mob/dead/observer/logout.dm
@@ -1,4 +1,5 @@
/mob/dead/observer/Logout()
+ update_z(null)
if (client)
client.images -= (GLOB.ghost_images_default+GLOB.ghost_images_simple)
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index 7655e8e1d7..12fe984d6f 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -16,6 +16,7 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
see_invisible = SEE_INVISIBLE_OBSERVER
see_in_dark = 100
invisibility = INVISIBILITY_OBSERVER
+ hud_type = /datum/hud/ghost
var/can_reenter_corpse
var/datum/hud/living/carbon/hud = null // hud
var/bootime = 0
@@ -135,6 +136,10 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
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"
@@ -267,6 +272,7 @@ Works together with spawning an observer, noted above.
/*
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"
@@ -281,6 +287,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
C.despawn_occupant()
return
// END EDIT
+
if(stat != DEAD)
succumb()
if(stat == DEAD)
@@ -386,6 +393,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
if(!L || !L.len)
to_chat(usr, "No area available.")
+ return
usr.forceMove(pick(L))
update_parallax_contents()
diff --git a/code/modules/mob/dead/observer/say.dm b/code/modules/mob/dead/observer/say.dm
index 84996184ca..d521ef179f 100644
--- a/code/modules/mob/dead/observer/say.dm
+++ b/code/modules/mob/dead/observer/say.dm
@@ -1,4 +1,4 @@
-/mob/dead/observer/say(message)
+/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
@@ -15,7 +15,7 @@
client.dsay(message)
return
- log_talk(src,"Ghost/[src.key] : [message]", LOGSAY)
+ src.log_talk(message, LOG_SAY, tag="ghost")
if(check_emote(message))
return
@@ -37,3 +37,4 @@
// 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]")
+
diff --git a/code/modules/mob/death.dm b/code/modules/mob/death.dm
index 235a215eff..8c335a9e8c 100644
--- a/code/modules/mob/death.dm
+++ b/code/modules/mob/death.dm
@@ -6,8 +6,9 @@
//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()
+/mob/proc/dust(just_ash, drop_items, force)
return
/mob/proc/death(gibbed)
SEND_SIGNAL(src, COMSIG_MOB_DEATH, gibbed)
+ return
\ No newline at end of file
diff --git a/code/modules/mob/emote.dm b/code/modules/mob/emote.dm
index f966702f3b..625a923fe1 100644
--- a/code/modules/mob/emote.dm
+++ b/code/modules/mob/emote.dm
@@ -1,5 +1,5 @@
//The code execution of the emote datum is located at code/datums/emotes.dm
-/mob/proc/emote(act, m_type = null, message = null)
+/mob/proc/emote(act, m_type = null, message = null, intentional = FALSE)
act = lowertext(act)
var/param = message
var/custom_param = findchar(act, " ")
@@ -12,7 +12,7 @@
if(!E)
to_chat(src, "
Unusable emote '[act]'. Say *help for a list. ")
return
- E.run_emote(src, param, m_type)
+ E.run_emote(src, param, m_type, intentional)
/datum/emote/flip
key = "flip"
diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm
index 494536ebfa..a6d6a7c7b6 100644
--- a/code/modules/mob/inventory.dm
+++ b/code/modules/mob/inventory.dm
@@ -170,8 +170,10 @@
return FALSE
return !held_items[hand_index]
-/mob/proc/put_in_hand(obj/item/I, hand_index, forced = FALSE)
+/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)
@@ -208,8 +210,8 @@
//Puts the item into our active hand if possible. returns TRUE on success.
-/mob/proc/put_in_active_hand(obj/item/I, forced = FALSE)
- return put_in_hand(I, active_hand_index, forced)
+/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
diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm
index 393b939448..bd1fba44e2 100644
--- a/code/modules/mob/living/blood.dm
+++ b/code/modules/mob/living/blood.dm
@@ -254,7 +254,7 @@
// 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 < 3)
+ if(drop.drips < 5)
drop.drips++
drop.add_overlay(pick(drop.random_icon_states))
drop.transfer_mob_blood_dna(src)
@@ -271,6 +271,8 @@
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)
diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm
index 965ef88c03..7d1dc7d1d2 100644
--- a/code/modules/mob/living/brain/brain_item.dm
+++ b/code/modules/mob/living/brain/brain_item.dm
@@ -21,7 +21,7 @@
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 || (C.has_trait(TRAIT_FAKEDEATH))))
+ if(brainmob && !(C.stat == DEAD || (C.has_trait(TRAIT_DEATHCOMA))))
to_chat(brainmob, "
You can't feel your body! You're still just a brain! ")
forceMove(C)
C.update_hair()
@@ -144,7 +144,7 @@
/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,0.1)
+ . = 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
@@ -157,11 +157,11 @@
else
adjusted_amount = amount
- adjusted_amount = round(adjusted_amount * BRAIN_DAMAGE_INTEGRITY_MULTIPLIER,0.1)
+ adjusted_amount = round(adjusted_amount * BRAIN_DAMAGE_INTEGRITY_MULTIPLIER, DAMAGE_PRECISION)
if(adjusted_amount)
- if(adjusted_amount >= 0.1)
+ if(adjusted_amount >= DAMAGE_PRECISION)
take_damage(adjusted_amount)
- else if(adjusted_amount <= -0.1)
+ else if(adjusted_amount <= -DAMAGE_PRECISION)
obj_integrity = min(max_integrity, obj_integrity-adjusted_amount)
. = adjusted_amount
diff --git a/code/modules/mob/living/brain/say.dm b/code/modules/mob/living/brain/say.dm
index de18b8ce3f..ce0a09c27f 100644
--- a/code/modules/mob/living/brain/say.dm
+++ b/code/modules/mob/living/brain/say.dm
@@ -1,4 +1,4 @@
-/mob/living/brain/say(message, language)
+/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
diff --git a/code/modules/mob/living/carbon/alien/alien_defense.dm b/code/modules/mob/living/carbon/alien/alien_defense.dm
index b55abb11f0..6d59bc052a 100644
--- a/code/modules/mob/living/carbon/alien/alien_defense.dm
+++ b/code/modules/mob/living/carbon/alien/alien_defense.dm
@@ -38,7 +38,7 @@ In all, this is a lot like the monkey code. /N
visible_message("
[M.name] bites [src]! ", \
"
[M.name] bites [src]! ", null, COMBAT_MESSAGE_RANGE)
adjustBruteLoss(1)
- add_logs(M, src, "attacked")
+ log_combat(M, src, "attacked")
updatehealth()
else
to_chat(M, "
[name] is too injured for that. ")
@@ -97,7 +97,7 @@ In all, this is a lot like the monkey code. /N
if(M.is_adult)
damage = rand(10, 40)
adjustBruteLoss(damage)
- add_logs(M, src, "attacked")
+ log_combat(M, src, "attacked")
updatehealth()
/mob/living/carbon/alien/ex_act(severity, target, origin)
diff --git a/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm b/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm
index cdc1b08d21..323bd408cf 100644
--- a/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm
+++ b/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm
@@ -97,7 +97,7 @@ Doesn't work on other aliens/AI.*/
return 0
var/msg = sanitize(input("Message:", "Alien Whisper") as text|null)
if(msg)
- log_talk(user,"AlienWhisper: [key_name(user)]->[key_name(M)] : [msg]",LOGSAY)
+ log_directed_talk(user, M, msg, LOG_SAY, tag="alien whisper")
to_chat(M, "
You hear a strange, alien voice in your head... [msg]")
to_chat(user, "
You said: \"[msg]\" to [M] ")
for(var/ded in GLOB.dead_mob_list)
@@ -233,19 +233,19 @@ Doesn't work on other aliens/AI.*/
/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)
+/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(mob/living/user, msg)
- ..()
- if(isalienadult(user))
- var/mob/living/carbon/alien/humanoid/A = user
+/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"
diff --git a/code/modules/mob/living/carbon/alien/humanoid/caste/drone.dm b/code/modules/mob/living/carbon/alien/humanoid/caste/drone.dm
index b18aaacfeb..183bf07e8c 100644
--- a/code/modules/mob/living/carbon/alien/humanoid/caste/drone.dm
+++ b/code/modules/mob/living/carbon/alien/humanoid/caste/drone.dm
@@ -5,21 +5,16 @@
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
..()
-/mob/living/carbon/alien/humanoid/drone/movement_delay()
- . = ..()
-
/obj/effect/proc_holder/alien/evolve
name = "Evolve to Praetorian"
desc = "Praetorian"
diff --git a/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm b/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm
index ada1d1f21c..fe682b5c99 100644
--- a/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm
+++ b/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm
@@ -10,11 +10,6 @@
internal_organs += new /obj/item/organ/alien/plasmavessel/small
..()
-/mob/living/carbon/alien/humanoid/hunter/movement_delay()
- . = -1 //hunters are sanic
- . += ..() //but they still need to slow down on stun
-
-
//Hunter verbs
/mob/living/carbon/alien/humanoid/hunter/proc/toggle_leap(message = 1)
@@ -26,7 +21,6 @@
else
return
-
/mob/living/carbon/alien/humanoid/hunter/ClickOn(atom/A, params)
face_atom(A)
if(leap_on_click)
@@ -34,7 +28,6 @@
else
..()
-
#define MAX_ALIEN_LEAP_DIST 7
/mob/living/carbon/alien/humanoid/hunter/proc/leap_at(atom/A)
@@ -54,7 +47,7 @@
leaping = 1
weather_immunities += "lava"
update_icons()
- throw_at(A, MAX_ALIEN_LEAP_DIST, 1, src, FALSE, TRUE, callback = CALLBACK(src, .leap_end))
+ 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
diff --git a/code/modules/mob/living/carbon/alien/humanoid/caste/praetorian.dm b/code/modules/mob/living/carbon/alien/humanoid/caste/praetorian.dm
index fe61c2a8ff..4ec5780838 100644
--- a/code/modules/mob/living/carbon/alien/humanoid/caste/praetorian.dm
+++ b/code/modules/mob/living/carbon/alien/humanoid/caste/praetorian.dm
@@ -5,12 +5,8 @@
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())
. = ..()
@@ -22,11 +18,6 @@
internal_organs += new /obj/item/organ/alien/neurotoxin
..()
-
-/mob/living/carbon/alien/humanoid/royal/praetorian/movement_delay()
- . = ..()
- . += 1
-
/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."
diff --git a/code/modules/mob/living/carbon/alien/humanoid/caste/sentinel.dm b/code/modules/mob/living/carbon/alien/humanoid/caste/sentinel.dm
index 375ef2318b..7c6443cfae 100644
--- a/code/modules/mob/living/carbon/alien/humanoid/caste/sentinel.dm
+++ b/code/modules/mob/living/carbon/alien/humanoid/caste/sentinel.dm
@@ -5,7 +5,6 @@
health = 150
icon_state = "aliens"
-
/mob/living/carbon/alien/humanoid/sentinel/Initialize()
AddAbility(new /obj/effect/proc_holder/alien/sneak)
. = ..()
@@ -15,7 +14,3 @@
internal_organs += new /obj/item/organ/alien/acid
internal_organs += new /obj/item/organ/alien/neurotoxin
..()
-
-
-/mob/living/carbon/alien/humanoid/sentinel/movement_delay()
- . = ..()
diff --git a/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm b/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm
index eb5e9664d9..8403d533c4 100644
--- a/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm
+++ b/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm
@@ -5,6 +5,7 @@
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 = ""
@@ -25,16 +26,8 @@
AddAbility(new/obj/effect/proc_holder/alien/regurgitate(null))
. = ..()
-/mob/living/carbon/alien/humanoid/movement_delay()
- . = ..()
- var/static/config_alien_delay
- if(isnull(config_alien_delay))
- config_alien_delay = CONFIG_GET(number/alien_delay)
- . += move_delay_add + config_alien_delay + sneaking //move_delay_add is used to slow aliens with stun
-
/mob/living/carbon/alien/humanoid/restrained(ignore_grab)
- . = handcuffed
-
+ return handcuffed
/mob/living/carbon/alien/humanoid/show_inv(mob/user)
user.set_machine(src)
diff --git a/code/modules/mob/living/carbon/alien/humanoid/humanoid_defense.dm b/code/modules/mob/living/carbon/alien/humanoid/humanoid_defense.dm
index 6c42e687a9..b3839a6033 100644
--- a/code/modules/mob/living/carbon/alien/humanoid/humanoid_defense.dm
+++ b/code/modules/mob/living/carbon/alien/humanoid/humanoid_defense.dm
@@ -35,7 +35,7 @@
"
[M] has knocked [src] down! ")
var/obj/item/bodypart/affecting = get_bodypart(ran_zone(M.zone_selected))
apply_damage(damage, BRUTE, affecting)
- add_logs(M, src, "attacked")
+ log_combat(M, src, "attacked")
else
playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
visible_message("
[M] has attempted to punch [src]! ", \
@@ -46,7 +46,7 @@
if (prob(5))
Unconscious(40)
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
- add_logs(M, src, "pushed")
+ log_combat(M, src, "pushed")
visible_message("
[M] has pushed down [src]! ", \
"
[M] has pushed down [src]! ")
else
diff --git a/code/modules/mob/living/carbon/alien/humanoid/queen.dm b/code/modules/mob/living/carbon/alien/humanoid/queen.dm
index 9c61e8aa2d..79eed6b82b 100644
--- a/code/modules/mob/living/carbon/alien/humanoid/queen.dm
+++ b/code/modules/mob/living/carbon/alien/humanoid/queen.dm
@@ -40,7 +40,7 @@
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
@@ -50,10 +50,6 @@
internal_organs += new /obj/item/organ/alien/eggsac
..()
-/mob/living/carbon/alien/humanoid/royal/queen/movement_delay()
- . = ..()
- . += 3
-
//Queen verbs
/obj/effect/proc_holder/alien/lay_egg
name = "Lay Egg"
diff --git a/code/modules/mob/living/carbon/alien/larva/larva.dm b/code/modules/mob/living/carbon/alien/larva/larva.dm
index 2e2d1f4188..e1e079cbae 100644
--- a/code/modules/mob/living/carbon/alien/larva/larva.dm
+++ b/code/modules/mob/living/carbon/alien/larva/larva.dm
@@ -5,6 +5,7 @@
pass_flags = PASSTABLE | PASSMOB
mob_size = MOB_SIZE_SMALL
density = FALSE
+ hud_type = /datum/hud/larva
maxHealth = 25
health = 25
diff --git a/code/modules/mob/living/carbon/alien/larva/larva_defense.dm b/code/modules/mob/living/carbon/alien/larva/larva_defense.dm
index c3cee567a2..69c1be707d 100644
--- a/code/modules/mob/living/carbon/alien/larva/larva_defense.dm
+++ b/code/modules/mob/living/carbon/alien/larva/larva_defense.dm
@@ -5,7 +5,7 @@
var/damage = rand(1, 9)
if (prob(90))
playsound(loc, "punch", 25, 1, -1)
- add_logs(M, src, "attacked")
+ log_combat(M, src, "attacked")
visible_message("
[M] has kicked [src]! ", \
"
[M] has kicked [src]! ", null, COMBAT_MESSAGE_RANGE)
if ((stat != DEAD) && (damage > 4.9))
diff --git a/code/modules/mob/living/carbon/alien/larva/life.dm b/code/modules/mob/living/carbon/alien/larva/life.dm
index 62b9fb9b1a..5961e4dd71 100644
--- a/code/modules/mob/living/carbon/alien/larva/life.dm
+++ b/code/modules/mob/living/carbon/alien/larva/life.dm
@@ -18,7 +18,7 @@
if(health<= -maxHealth || !getorgan(/obj/item/organ/brain))
death()
return
- if(IsUnconscious() || IsSleeping() || getOxyLoss() > 50 || (has_trait(TRAIT_FAKEDEATH)) || health <= HEALTH_THRESHOLD_CRIT)
+ if(IsUnconscious() || IsSleeping() || getOxyLoss() > 50 || (has_trait(TRAIT_DEATHCOMA)) || health <= crit_threshold)
if(stat == CONSCIOUS)
stat = UNCONSCIOUS
blind_eyes(1)
@@ -30,4 +30,4 @@
adjust_blindness(-1)
update_canmove()
update_damage_hud()
- update_health_hud()
\ No newline at end of file
+ update_health_hud()
diff --git a/code/modules/mob/living/carbon/alien/larva/powers.dm b/code/modules/mob/living/carbon/alien/larva/powers.dm
index 906ba71c96..9d5617b3e7 100644
--- a/code/modules/mob/living/carbon/alien/larva/powers.dm
+++ b/code/modules/mob/living/carbon/alien/larva/powers.dm
@@ -15,7 +15,7 @@
"
You are now hiding. ")
else
user.layer = MOB_LAYER
- user.visible_message("[user.] slowly peeks up from the ground...", \
+ user.visible_message("[user] slowly peeks up from the ground...", \
"
You stop hiding. ")
return 1
diff --git a/code/modules/mob/living/carbon/alien/organs.dm b/code/modules/mob/living/carbon/alien/organs.dm
index 1a407d0ca3..155f203708 100644
--- a/code/modules/mob/living/carbon/alien/organs.dm
+++ b/code/modules/mob/living/carbon/alien/organs.dm
@@ -9,6 +9,10 @@
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)
diff --git a/code/modules/mob/living/carbon/alien/say.dm b/code/modules/mob/living/carbon/alien/say.dm
index 1d35a2c134..b921aea67e 100644
--- a/code/modules/mob/living/carbon/alien/say.dm
+++ b/code/modules/mob/living/carbon/alien/say.dm
@@ -1,5 +1,5 @@
/mob/living/proc/alien_talk(message, shown_name = real_name)
- log_talk(src,"[key_name(src)] : [message]",LOGSAY)
+ src.log_talk(message, LOG_SAY)
message = trim(message)
if(!message)
return
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index c3c74b8631..e60a63616b 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -19,6 +19,9 @@
QDEL_NULL(dna)
GLOB.carbon_list -= src
+/mob/living/carbon/initialize_footstep()
+ AddComponent(/datum/component/footstep, 1, 2)
+
/mob/living/carbon/relaymove(mob/user, direction)
if(user in src.stomach_contents)
if(prob(40))
@@ -84,9 +87,9 @@
/mob/living/carbon/attackby(obj/item/I, mob/user, params)
if(lying && surgeries.len)
- if(user != src && user.a_intent == INTENT_HELP)
+ 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))
+ if(S.next_step(user,user.a_intent))
return 1
return ..()
@@ -169,7 +172,7 @@
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)
- add_logs(src, throwable_mob, "thrown", addition="grab from tile in [AREACOORD(start_T)] towards tile at [AREACOORD(end_T)]")
+ log_combat(src, throwable_mob, "thrown", addition="grab from tile in [AREACOORD(start_T)] towards tile at [AREACOORD(end_T)]")
else if(!(I.item_flags & (NODROP | ABSTRACT)))
thrown_thing = I
@@ -183,7 +186,7 @@
if(thrown_thing)
visible_message("
[src] has thrown [thrown_thing]. ")
- add_logs(src, thrown_thing, "thrown")
+ src.log_message("has thrown [thrown_thing]", LOG_ATTACK)
newtonian_move(get_dir(target, src))
thrown_thing.throw_at(target, thrown_thing.throw_range, thrown_thing.throw_speed, src)
@@ -409,6 +412,7 @@
//dropItemToGround(I) CIT CHANGE - makes it so the item doesn't drop if the modifier rolls above 100
var/modifier = 0
+
if(has_trait(TRAIT_CLUMSY))
modifier -= 40 //Clumsy people are more likely to hit themselves -Honk!
@@ -419,7 +423,7 @@
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)
@@ -523,15 +527,29 @@
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
- total_burn += BP.burn_dam
- total_stamina += BP.stamina_dam
- health = maxHealth - getOxyLoss() - getToxLoss() - getCloneLoss() - total_burn - total_brute
- staminaloss = total_stamina
+ 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, "
You're too exhausted to keep going... ")
+ Knockdown(100)
+ update_health_hud()
/mob/living/carbon/update_sight()
if(!client)
@@ -616,7 +634,7 @@
if(!client)
return
- if(health <= HEALTH_THRESHOLD_CRIT)
+ if(health <= crit_threshold)
var/severity = 0
switch(health)
if(-20 to -10)
@@ -741,11 +759,11 @@
if(health <= HEALTH_THRESHOLD_DEAD && !has_trait(TRAIT_NODEATH))
death()
return
- if(IsUnconscious() || IsSleeping() || getOxyLoss() > 50 || (has_trait(TRAIT_FAKEDEATH)) || (health <= HEALTH_THRESHOLD_FULLCRIT && !has_trait(TRAIT_NOHARDCRIT)))
+ if(IsUnconscious() || IsSleeping() || getOxyLoss() > 50 || (has_trait(TRAIT_DEATHCOMA)) || (health <= HEALTH_THRESHOLD_FULLCRIT && !has_trait(TRAIT_NOHARDCRIT)))
stat = UNCONSCIOUS
blind_eyes(1)
else
- if(health <= HEALTH_THRESHOLD_CRIT && !has_trait(TRAIT_NOSOFTCRIT))
+ if(health <= crit_threshold && !has_trait(TRAIT_NOSOFTCRIT))
stat = SOFT_CRIT
else
stat = CONSCIOUS
@@ -839,7 +857,7 @@
"
[src] devours you! ")
C.forceMove(src)
stomach_contents.Add(C)
- add_logs(src, C, "devoured")
+ log_combat(src, C, "devoured")
/mob/living/carbon/proc/create_bodyparts()
var/l_arm_index_next = -1
@@ -881,6 +899,7 @@
.["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)]"
diff --git a/code/modules/mob/living/carbon/carbon_movement.dm b/code/modules/mob/living/carbon/carbon_movement.dm
index 7334464235..37b888a6b8 100644
--- a/code/modules/mob/living/carbon/carbon_movement.dm
+++ b/code/modules/mob/living/carbon/carbon_movement.dm
@@ -1,13 +1,6 @@
/mob/living/carbon/movement_delay()
- var/FP = FALSE
- var/obj/item/flightpack/F = get_flightpack()
- if(istype(F) && F.flight)
- FP = TRUE
- . = ..(FP)
- if(!FP)
- . += grab_state * 1 //Flightpacks are too powerful to be slowed too much by the weight of a corpse.
- else
- . += grab_state * 3 //can't go fast while grabbing something.
+ . = ..()
+ . += 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()
@@ -16,7 +9,6 @@
. += 6 - 3*get_num_arms() //crawling is harder with fewer arms
if(legcuffed)
. += legcuffed.slowdown
-
if(stat == SOFT_CRIT)
. += SOFTCRIT_ADD_SLOWDOWN
@@ -24,7 +16,7 @@
if(movement_type & FLYING)
return 0
if(!(lube&SLIDE_ICE))
- add_logs(src, (O ? O : get_turf(src)), "slipped on the", null, ((lube & SLIDE) ? "(LUBE)" : null))
+ 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)
@@ -33,10 +25,6 @@
if(!isturf(loc))
return 0
- var/obj/item/flightpack/F = get_flightpack()
- if(istype(F) && (F.flight) && F.allow_thrust(0.01, src))
- return 1
-
// 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))
diff --git a/code/modules/mob/living/carbon/damage_procs.dm b/code/modules/mob/living/carbon/damage_procs.dm
index 8f4c6a2b05..124fd622d7 100644
--- a/code/modules/mob/living/carbon/damage_procs.dm
+++ b/code/modules/mob/living/carbon/damage_procs.dm
@@ -95,7 +95,7 @@
. = 0
for(var/X in bodyparts)
var/obj/item/bodypart/BP = X
- . += BP.stamina_dam
+ . += 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))
@@ -120,7 +120,7 @@
var/list/obj/item/bodypart/parts = list()
for(var/X in bodyparts)
var/obj/item/bodypart/BP = X
- if(!isnull(status) && (BP.status != status))
+ if(status && BP.status != status)
continue
if((brute && BP.brute_dam) || (burn && BP.burn_dam) || (stamina && BP.stamina_dam))
parts += BP
@@ -171,13 +171,14 @@
update |= picked.heal_damage(brute, burn, stamina, only_robotic, only_organic, FALSE)
- brute -= (brute_was - picked.brute_dam)
- burn -= (burn_was - picked.burn_dam)
- stamina -= (stamina_was - picked.stamina_dam)
+ 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()
+ update_stamina()
if(update)
update_damage_overlays()
update_stamina() //CIT CHANGE - makes sure update_stamina() always gets called after a health update
@@ -191,9 +192,9 @@
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, 0.01)
- var/burn_per_part = round(burn/parts.len, 0.01)
- var/stamina_per_part = round(stamina/parts.len, 0.01)
+ 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
@@ -202,9 +203,9 @@
update |= picked.receive_damage(brute_per_part, burn_per_part, stamina_per_part, FALSE)
- brute -= (picked.brute_dam - brute_was)
- burn -= (picked.burn_dam - burn_was)
- stamina -= (picked.stamina_dam - stamina_was)
+ 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)
@@ -253,4 +254,3 @@
if(B)
var/adjusted_amount = amount - B.get_brain_damage()
B.adjust_brain_damage(adjusted_amount, null)
-
diff --git a/code/modules/mob/living/carbon/death.dm b/code/modules/mob/living/carbon/death.dm
index 4de59d4c3e..ae0e223e08 100644
--- a/code/modules/mob/living/carbon/death.dm
+++ b/code/modules/mob/living/carbon/death.dm
@@ -9,6 +9,11 @@
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
diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm
index 447b0c9f96..0db3d82777 100644
--- a/code/modules/mob/living/carbon/human/emote.dm
+++ b/code/modules/mob/living/carbon/human/emote.dm
@@ -77,60 +77,27 @@
if(!.)
return
var/mob/living/carbon/human/H = user
- if(!H.is_wagging_tail())
- H.startTailWag()
+ 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.endTailWag()
-
-/mob/living/carbon/human/proc/is_wagging_tail()
- return (dna && dna.species && (("waggingtail_lizard" in dna.species.mutant_bodyparts) || ("waggingtail_human" in dna.species.mutant_bodyparts)|| ("mam_waggingtail" in dna.species.mutant_bodyparts)))
+ 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
- if(H.dna && H.dna.species && (("tail_lizard" in H.dna.species.mutant_bodyparts) || ("waggingtail_lizard" in H.dna.species.mutant_bodyparts) || ("tail_human" in H.dna.species.mutant_bodyparts) || ("waggingtail_human" in H.dna.species.mutant_bodyparts)|| ("mam_tail" in H.dna.species.mutant_bodyparts) || ("mam_waggingtail" in H.dna.species.mutant_bodyparts)))
- return TRUE
+ 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.is_wagging_tail())
+ if(!H.dna || !H.dna.species)
+ return
+ if(H.dna.species.is_wagging_tail())
. = null
-//Don't know where else to put this, it's basically an emote
-/mob/living/carbon/human/proc/startTailWag()
- if(!dna || !dna.species)
- return
- if("tail_lizard" in dna.species.mutant_bodyparts)
- dna.species.mutant_bodyparts -= "tail_lizard"
- dna.species.mutant_bodyparts -= "spines"
- dna.species.mutant_bodyparts |= "waggingtail_lizard"
- dna.species.mutant_bodyparts |= "waggingspines"
- if("tail_human" in dna.species.mutant_bodyparts)
- dna.species.mutant_bodyparts -= "tail_human"
- dna.species.mutant_bodyparts |= "waggingtail_human"
- if("mam_tail" in dna.species.mutant_bodyparts)
- dna.species.mutant_bodyparts -= "mam_tail"
- dna.species.mutant_bodyparts |= "mam_waggingtail"
- update_body()
-
-/mob/living/carbon/human/proc/endTailWag()
- if(!dna || !dna.species)
- return
- if("waggingtail_lizard" in dna.species.mutant_bodyparts)
- dna.species.mutant_bodyparts -= "waggingtail_lizard"
- dna.species.mutant_bodyparts -= "waggingspines"
- dna.species.mutant_bodyparts |= "tail_lizard"
- dna.species.mutant_bodyparts |= "spines"
- if("waggingtail_human" in dna.species.mutant_bodyparts)
- dna.species.mutant_bodyparts -= "waggingtail_human"
- dna.species.mutant_bodyparts |= "tail_human"
- if("mam_waggingtail" in dna.species.mutant_bodyparts)
- dna.species.mutant_bodyparts -= "mam_waggingtail"
- dna.species.mutant_bodyparts |= "mam_tail"
- update_body()
-
/datum/emote/living/carbon/human/wing
key = "wing"
key_third_person = "wings"
diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm
index bc55487e95..77cf6fc4bb 100644
--- a/code/modules/mob/living/carbon/human/examine.dm
+++ b/code/modules/mob/living/carbon/human/examine.dm
@@ -1,4 +1,4 @@
-/mob/living/carbon/human/examine(mob/user)
+/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)
@@ -51,7 +51,7 @@
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()
+ var/hand_number = get_num_arms(FALSE)
if(hand_number)
msg += "
[t_He] [t_has] [hand_number > 1 ? "" : "a"] blood-stained hand[hand_number > 1 ? "s" : ""]! \n"
@@ -80,8 +80,11 @@
msg += "[t_He] [t_is] wearing [wear_neck.get_examine_string(user)] around [t_his] neck.\n"
//eyes
- if(glasses && !(SLOT_GLASSES in obscured))
- msg += "[t_He] [t_has] [glasses.get_examine_string(user)] covering [t_his] eyes.\n"
+ 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(CULT_EYES))
+ msg += "
[t_His] eyes are glowing an unnatural red! \n"
//ears
if(ears && !(SLOT_EARS in obscured))
@@ -91,16 +94,17 @@
if(wear_id)
msg += "[t_He] [t_is] wearing [wear_id.get_examine_string(user)].\n"
-//CIT CHANGES START HERE - adds genital details to examine text
+ //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
- //Status effects
- msg += status_effect_examines()
//Jitters
switch(jitteriness)
@@ -141,12 +145,25 @@
msg += "
"
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 += "[t_He] [t_has] \a [icon2html(I, user)] [I] embedded in [t_his] [BP.name]! \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 += "[capitalize(t_his)] [BP.name] is [damage_text]! \n"
+
//stores missing limbs
var/l_limbs_missing = 0
var/r_limbs_missing = 0
@@ -321,13 +338,11 @@
msg += "\[Add crime\] "
msg += "\[View comment log\] "
msg += "\[Add comment\] \n"
-
else if(isobserver(user) && traitstring)
msg += "Traits: [traitstring] "
if(print_flavor_text() && 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 += "[print_flavor_text()]\n"
-
msg += "*---------* "
to_chat(user, msg)
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 48b4feec60..1633e42752 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -28,7 +28,7 @@
. = ..()
- AddComponent(/datum/component/redirect, list(COMSIG_COMPONENT_CLEAN_ACT), CALLBACK(src, .proc/clean_blood))
+ AddComponent(/datum/component/redirect, list(COMSIG_COMPONENT_CLEAN_ACT = CALLBACK(src, .proc/clean_blood)))
/mob/living/carbon/human/ComponentInitialize()
@@ -207,33 +207,6 @@
spreadFire(AM)
-/mob/living/carbon/human/resist()
- . = ..()
- if(wear_suit && wear_suit.breakouttime)//added in human cuff breakout proc
- return
- if(.)
- if(canmove && !on_fire)
- for(var/obj/item/bodypart/L in bodyparts)
- if(istype(L) && L.embedded_objects.len)
- for(var/obj/item/I in L.embedded_objects)
- if(istype(I) && I.w_class >= WEIGHT_CLASS_NORMAL) //minimum weight class to insta-ripout via resist
- remove_embedded_unsafe(L, I, src, 1.5) //forcefully call the remove embedded unsafe proc but with extra pain multiplier. if you want to remove it less painfully, examine and remove it carefully.
- return FALSE //Hands are occupied
- return .
-
-/mob/living/carbon/human/proc/remove_embedded_unsafe(obj/item/bodypart/L, obj/item/I, mob/user, painmul = 1)
- if(!I || !L || I.loc != src || !(I in L.embedded_objects))
- return
- L.embedded_objects -= I
- L.receive_damage(I.embedding.embedded_unsafe_removal_pain_multiplier*I.w_class*painmul)//It hurts to rip it out, get surgery you dingus. And if you're ripping it out quickly via resist, it's gonna hurt even more
- I.forceMove(get_turf(src))
- user.put_in_hands(I)
- user.emote("scream")
- user.visible_message("[user] rips [I] out of [user.p_their()] [L.name]!","
You remove [I] from your [L.name]. ")
- if(!has_embedded_objects())
- clear_alert("embeddedobject")
- SEND_SIGNAL(user, COMSIG_CLEAR_MOOD_EVENT, "embedded")
- return
/mob/living/carbon/human/Topic(href, href_list)
if(usr.canUseTopic(src, BE_CLOSE, NO_DEXTERY))
@@ -244,10 +217,20 @@
var/obj/item/I = locate(href_list["embedded_object"]) in L.embedded_objects
if(!I || I.loc != src) //no item, no limb, or item is not in limb or in the person anymore
return
- var/time_taken = I.embedding.embedded_unsafe_removal_time/I.w_class
+ var/time_taken = I.embedding.embedded_unsafe_removal_time*I.w_class
usr.visible_message("
[usr] attempts to remove [I] from [usr.p_their()] [L.name]. ","
You attempt to remove [I] from your [L.name]... (It will take [DisplayTimeText(time_taken)].) ")
if(do_after(usr, time_taken, needhand = 1, target = src))
- remove_embedded_unsafe(L, I, usr)
+ if(!I || !L || I.loc != src || !(I in L.embedded_objects))
+ return
+ L.embedded_objects -= I
+ L.receive_damage(I.embedding.embedded_unsafe_removal_pain_multiplier*I.w_class)//It hurts to rip it out, get surgery you dingus.
+ I.forceMove(get_turf(src))
+ usr.put_in_hands(I)
+ usr.emote("scream")
+ usr.visible_message("[usr] successfully rips [I] out of [usr.p_their()] [L.name]!","
You successfully remove [I] from your [L.name]. ")
+ if(!has_embedded_objects())
+ clear_alert("embeddedobject")
+ SEND_SIGNAL(usr, COMSIG_CLEAR_MOOD_EVENT, "embedded")
return
if(href_list["item"])
@@ -624,7 +607,7 @@
//Agent cards lower threatlevel.
if(istype(idcard, /obj/item/card/id/syndicate))
- threatcount -= 5
+ threatcount -= 2
return threatcount
@@ -638,8 +621,8 @@
hair_style = pick("Bedhead", "Bedhead 2", "Bedhead 3")
underwear = "Nude"
update_body()
- update_genitals()
update_hair()
+ update_genitals()
/mob/living/carbon/human/singularity_pull(S, current_size)
..()
@@ -675,13 +658,13 @@
var/they_breathe = !C.has_trait(TRAIT_NOBREATH)
var/they_lung = C.getorganslot(ORGAN_SLOT_LUNGS)
- if(C.health > HEALTH_THRESHOLD_CRIT)
+ if(C.health > C.crit_threshold)
return
src.visible_message("[src] performs CPR on [C.name]!", "
You perform CPR on [C.name]. ")
SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "perform_cpr", /datum/mood_event/perform_cpr)
C.cpr_time = world.time
- add_logs(src, C, "CPRed")
+ log_combat(src, C, "CPRed")
if(they_breathe && they_lung)
var/suff = min(C.getOxyLoss(), 7)
@@ -695,14 +678,14 @@
/mob/living/carbon/human/cuff_resist(obj/item/I)
if(dna && dna.check_mutation(HULK))
- say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" ))
+ say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" ), forced = "hulk")
if(..(I, cuff_break = FAST_CUFFBREAK))
dropItemToGround(I)
else
if(..())
dropItemToGround(I)
-/mob/living/carbon/human/proc/clean_blood(strength)
+/mob/living/carbon/human/proc/clean_blood(datum/source, strength)
if(strength < CLEAN_STRENGTH_BLOOD)
return
if(gloves)
@@ -810,6 +793,8 @@
hud_used.healthdoll.add_overlay(mutable_appearance('icons/mob/screen_gen.dmi', "[BP.body_zone][icon_num]"))
for(var/t in get_missing_limbs()) //Missing limbs
hud_used.healthdoll.add_overlay(mutable_appearance('icons/mob/screen_gen.dmi', "[t]6"))
+ for(var/t in get_disabled_limbs()) //Disabled limbs
+ hud_used.healthdoll.add_overlay(mutable_appearance('icons/mob/screen_gen.dmi', "[t]7"))
else
hud_used.healthdoll.icon_state = "healthdoll_DEAD"
@@ -851,13 +836,6 @@
return 1
..()
-/mob/living/carbon/human/Collide(atom/A)
- ..()
- var/crashdir = get_dir(src, A)
- var/obj/item/flightpack/FP = get_flightpack()
- if(FP)
- FP.flight_impact(A, crashdir)
-
/mob/living/carbon/human/vv_get_dropdown()
. = ..()
. += "---"
@@ -867,6 +845,7 @@
.["Make alien"] = "?_src_=vars;[HrefToken()];makealien=[REF(src)]"
.["Make slime"] = "?_src_=vars;[HrefToken()];makeslime=[REF(src)]"
.["Toggle Purrbation"] = "?_src_=vars;[HrefToken()];purrbation=[REF(src)]"
+ .["Copy outfit"] = "?_src_=vars;[HrefToken()];copyoutfit=[REF(src)]"
/mob/living/carbon/human/MouseDrop_T(mob/living/target, mob/living/user)
//If they dragged themselves and we're currently aggressively grabbing them try to piggyback
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index acf4b0734c..aba8e98583 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -156,7 +156,7 @@
else
..()
-/mob/living/carbon/human/grippedby(mob/living/user)
+/mob/living/carbon/human/grippedby(mob/living/user, instant = FALSE)
if(w_uniform)
w_uniform.add_fingerprint(user)
..()
@@ -220,7 +220,7 @@
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)
- add_logs(M, src, "tackled")
+ log_combat(M, src, "tackled")
visible_message("
[M] has tackled down [src]! ", \
"
[M] has tackled down [src]! ")
@@ -259,7 +259,7 @@
playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1)
visible_message("
[M] has slashed at [src]! ", \
"
[M] has slashed at [src]! ")
- add_logs(M, src, "attacked")
+ log_combat(M, src, "attacked")
if(!dismembering_strike(M, M.zone_selected)) //Dismemberment successful
return 1
apply_damage(damage, BRUTE, affecting, armor_block)
@@ -273,7 +273,7 @@
else
playsound(loc, 'sound/weapons/pierce.ogg', 25, 1, -1)
Knockdown(100)
- add_logs(M, src, "tackled")
+ log_combat(M, src, "tackled")
visible_message("
[M] has tackled down [src]! ", \
"
[M] has tackled down [src]! ")
@@ -357,7 +357,7 @@
visible_message("
[M.name] has hit [src]! ", \
"
[M.name] has hit [src]! ", null, COMBAT_MESSAGE_RANGE)
- add_logs(M.occupant, src, "attacked", M, "(INTENT: [uppertext(M.occupant.a_intent)]) (DAMTYPE: [uppertext(M.damtype)])")
+ log_combat(M.occupant, src, "attacked", M, "(INTENT: [uppertext(M.occupant.a_intent)]) (DAMTYPE: [uppertext(M.damtype)])")
else
..()
@@ -788,7 +788,7 @@
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
+ torn_items |= arm_clothes
//LEGS & FEET//
if(!def_zone || def_zone == BODY_ZONE_L_LEG || def_zone == BODY_ZONE_R_LEG)
@@ -800,7 +800,7 @@
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
+ torn_items |= leg_clothes
for(var/obj/item/I in torn_items)
I.take_damage(damage_amount, damage_type, damage_flag, 0)
diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm
index ff0dab75a8..763487f7ed 100644
--- a/code/modules/mob/living/carbon/human/human_defines.dm
+++ b/code/modules/mob/living/carbon/human/human_defines.dm
@@ -1,5 +1,6 @@
/mob/living/carbon/human
- hud_possible = list(HEALTH_HUD,STATUS_HUD,ID_HUD,WANTED_HUD, NANITE_HUD, DIAG_NANITE_FULL_HUD,IMPLOYAL_HUD,IMPCHEM_HUD,IMPTRACK_HUD,ANTAG_HUD,GLAND_HUD,SENTIENT_DISEASE_HUD)
+ 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
diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm
index 80714df0d8..6050c3e278 100644
--- a/code/modules/mob/living/carbon/human/human_helpers.dm
+++ b/code/modules/mob/living/carbon/human/human_helpers.dm
@@ -4,7 +4,7 @@
/mob/living/carbon/human/canBeHandcuffed()
- if(get_num_arms() >= 2)
+ if(get_num_arms(FALSE) >= 2)
return TRUE
else
return FALSE
diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm
index 5bfbb76e46..1addb3615c 100644
--- a/code/modules/mob/living/carbon/human/human_movement.dm
+++ b/code/modules/mob/living/carbon/human/human_movement.dm
@@ -1,9 +1,16 @@
+/mob/living/carbon/human/get_movespeed_modifiers()
+ var/list/considering = ..()
+ . = considering
+ if(has_trait(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()
- . = 0
- var/static/config_human_delay
- if(isnull(config_human_delay))
- config_human_delay = CONFIG_GET(number/human_delay)
- . += ..() + config_human_delay + dna.species.movement_delay(src)
+ . = ..()
+ if(dna && dna.species)
+ . += dna.species.movement_delay(src)
/mob/living/carbon/human/slip(knockdown_amount, obj/O, lube)
if(has_trait(TRAIT_NOSLIPALL))
@@ -49,20 +56,19 @@
//Bloody footprints
var/turf/T = get_turf(src)
if(S.bloody_shoes && S.bloody_shoes[S.blood_state])
- var/obj/effect/decal/cleanable/blood/footprints/oldFP = locate(/obj/effect/decal/cleanable/blood/footprints) in T
- if(oldFP && oldFP.blood_state == S.blood_state)
- return
- else
- //No oldFP or it's 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()
+ 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()
diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm
index 2216843716..8cc6d9310b 100644
--- a/code/modules/mob/living/carbon/human/life.dm
+++ b/code/modules/mob/living/carbon/human/life.dm
@@ -23,7 +23,12 @@
if (notransform)
return
- if(..()) //not dead
+ . = ..()
+
+ if (QDELETED(src))
+ return 0
+
+ if(.) //not dead
handle_active_genes()
if(stat != DEAD)
@@ -44,15 +49,15 @@
/mob/living/carbon/human/calculate_affecting_pressure(pressure)
- 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
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
@@ -65,7 +70,7 @@
else if(eye_blurry) //blurry eyes heal slowly
adjust_blurriness(-1)
- if (getBrainLoss() >= 60 && !incapacitated(TRUE))
+ if (getBrainLoss() >= 60)
SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "brain_damage", /datum/mood_event/brain_damage)
if(prob(3))
if(prob(25))
@@ -88,7 +93,7 @@
var/L = getorganslot(ORGAN_SLOT_LUNGS)
if(!L)
- if(health >= HEALTH_THRESHOLD_CRIT)
+ if(health >= crit_threshold)
adjustOxyLoss(HUMAN_MAX_OXYLOSS + 1)
else if(!has_trait(TRAIT_NOCRITDAMAGE))
adjustOxyLoss(HUMAN_CRIT_MAX_OXYLOSS)
@@ -320,19 +325,11 @@
HM.on_life(src)
/mob/living/carbon/human/proc/handle_heart()
- if(!can_heartattack())
- return
-
var/we_breath = !has_trait(TRAIT_NOBREATH, SPECIES_TRAIT)
-
if(!undergoing_cardiac_arrest())
return
- // Cardiac arrest, unless heart is stabilized
- if(has_trait(TRAIT_STABLEHEART))
- return
-
if(we_breath)
adjustOxyLoss(8)
Unconscious(80)
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index cb0e754450..fe323ae7c1 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -78,6 +78,7 @@ GLOBAL_LIST_EMPTY(roundstart_races)
var/whitelisted = 0 //Is this species restricted to certain players?
var/whitelist = list() //List the ckeys that can use this species, if it's whitelisted.: list("John Doe", "poopface666", "SeeALiggerPullTheTrigger") Spaces & capitalization can be included or ignored entirely for each key as it checks for both.
+
///////////
// PROCS //
///////////
@@ -100,7 +101,7 @@ GLOBAL_LIST_EMPTY(roundstart_races)
GLOB.roundstart_races += "human"
/datum/species/proc/check_roundstart_eligible()
- if(id in (CONFIG_GET(keyed_flag_list/roundstart_races)))
+ if(id in (CONFIG_GET(keyed_list/roundstart_races)))
return TRUE
return FALSE
@@ -247,7 +248,7 @@ GLOBAL_LIST_EMPTY(roundstart_races)
var/obj/item/organ/I = new path()
I.Insert(C)
-/datum/species/proc/on_species_gain(mob/living/carbon/C, datum/species/old_species)
+/datum/species/proc/on_species_gain(mob/living/carbon/C, datum/species/old_species, pref_load)
// Drop the items the new species can't wear
for(var/slot_id in no_equip)
var/obj/item/thing = C.get_item_by_slot(slot_id)
@@ -263,6 +264,7 @@ GLOBAL_LIST_EMPTY(roundstart_races)
C.Digitigrade_Leg_Swap(FALSE)
C.mob_biotypes = inherent_biotypes
+
regenerate_organs(C,old_species)
if(exotic_bloodtype && C.dna.blood_type != exotic_bloodtype)
@@ -289,6 +291,7 @@ GLOBAL_LIST_EMPTY(roundstart_races)
if(TRAIT_VIRUSIMMUNE in inherent_traits)
for(var/datum/disease/A in C.diseases)
A.cure(FALSE)
+
SEND_SIGNAL(C, COMSIG_SPECIES_GAIN, src, old_species)
//CITADEL EDIT
@@ -299,18 +302,18 @@ GLOBAL_LIST_EMPTY(roundstart_races)
C.canbearoused = C.client.prefs.arousable
// EDIT ENDS
-/datum/species/proc/on_species_loss(mob/living/carbon/C)
+/datum/species/proc/on_species_loss(mob/living/carbon/human/C, datum/species/new_species, pref_load)
if(C.dna.species.exotic_bloodtype)
C.dna.blood_type = random_blood_type()
if(DIGITIGRADE in species_traits)
C.Digitigrade_Leg_Swap(TRUE)
for(var/X in inherent_traits)
C.remove_trait(X, SPECIES_TRAIT)
+
SEND_SIGNAL(C, COMSIG_SPECIES_LOSS, src)
/datum/species/proc/handle_hair(mob/living/carbon/human/H, forced_colour)
H.remove_overlay(HAIR_LAYER)
-
var/obj/item/bodypart/head/HD = H.get_bodypart(BODY_ZONE_HEAD)
if(!HD) //Decapitated
return
@@ -496,7 +499,7 @@ GLOBAL_LIST_EMPTY(roundstart_races)
else
standing += mutable_appearance(undershirt.icon, undershirt.icon_state, -BODY_LAYER)
- if(H.socks && H.get_num_legs() >= 2 && !(DIGITIGRADE in species_traits))
+ if(H.socks && H.get_num_legs(FALSE) >= 2 && !(DIGITIGRADE in species_traits))
var/datum/sprite_accessory/socks/socks = GLOB.socks_list[H.socks]
if(socks)
standing += mutable_appearance(socks.icon, socks.icon_state, -BODY_LAYER)
@@ -652,15 +655,15 @@ GLOBAL_LIST_EMPTY(roundstart_races)
if("tail_lizard")
S = GLOB.tails_list_lizard[H.dna.features["tail_lizard"]]
if("waggingtail_lizard")
- S.= GLOB.animated_tails_list_lizard[H.dna.features["tail_lizard"]]
+ S = GLOB.animated_tails_list_lizard[H.dna.features["tail_lizard"]]
if("tail_human")
S = GLOB.tails_list_human[H.dna.features["tail_human"]]
if("waggingtail_human")
- S.= GLOB.animated_tails_list_human[H.dna.features["tail_human"]]
+ S = GLOB.animated_tails_list_human[H.dna.features["tail_human"]]
if("spines")
S = GLOB.spines_list[H.dna.features["spines"]]
if("waggingspines")
- S.= GLOB.animated_spines_list[H.dna.features["spines"]]
+ S = GLOB.animated_spines_list[H.dna.features["spines"]]
if("snout")
S = GLOB.snouts_list[H.dna.features["snout"]]
if("frills")
@@ -683,18 +686,17 @@ GLOBAL_LIST_EMPTY(roundstart_races)
S = GLOB.caps_list[H.dna.features["caps"]]
else
S = citadel_mutant_bodyparts(bodypart, H)
-
if(!S || S.icon_state == "none")
continue
var/mutable_appearance/accessory_overlay = mutable_appearance(S.icon, layer = -layer)
//A little rename so we don't have to use tail_lizard or tail_human when naming the sprites.
- if(bodypart == "tail_lizard" || bodypart == "tail_human" || bodypart == "mam_tail" || bodypart == "slimecoontail" || bodypart == "xenotail")
+ if(bodypart == "tail_lizard" || bodypart == "tail_human" || bodypart == "mam_tail" || bodypart == "xenotail")
bodypart = "tail"
else if(bodypart == "waggingtail_lizard" || bodypart == "waggingtail_human" || bodypart == "mam_waggingtail")
bodypart = "waggingtail"
- if(bodypart == "mam_ears")
+ if(bodypart == "mam_ears" || bodypart == "ears")
bodypart = "ears"
if(bodypart == "xenohead")
bodypart = "xhead"
@@ -703,6 +705,7 @@ GLOBAL_LIST_EMPTY(roundstart_races)
accessory_overlay.icon_state = "[g]_[bodypart]_[S.icon_state]_[layertext]"
else
accessory_overlay.icon_state = "m_[bodypart]_[S.icon_state]_[layertext]"
+
if(S.center)
accessory_overlay = center_image(accessory_overlay, S.dimension_x, S.dimension_y)
@@ -818,6 +821,7 @@ GLOBAL_LIST_EMPTY(roundstart_races)
extra2_accessory_overlay.color = "#[H.hair_color]"
standing += extra2_accessory_overlay
+
H.overlays_standing[layer] = standing.Copy()
standing = list()
@@ -826,6 +830,7 @@ GLOBAL_LIST_EMPTY(roundstart_races)
H.apply_overlay(BODY_FRONT_LAYER)
H.apply_overlay(BODY_TAUR_LAYER) // CITADEL EDIT
+
//This exists so sprite accessories can still be per-layer without having to include that layer's
//number in their sprite name, which causes issues when those numbers change.
/datum/species/proc/mutant_bodyparts_layertext(layer)
@@ -841,13 +846,14 @@ GLOBAL_LIST_EMPTY(roundstart_races)
return "TAUR"
//END EDIT
+
/datum/species/proc/spec_life(mob/living/carbon/human/H)
if(H.has_trait(TRAIT_NOBREATH))
H.setOxyLoss(0)
H.losebreath = 0
var/takes_crit_damage = (!H.has_trait(TRAIT_NOCRITDAMAGE))
- if((H.health < HEALTH_THRESHOLD_CRIT) && takes_crit_damage)
+ if((H.health < H.crit_threshold) && takes_crit_damage)
H.adjustBruteLoss(1)
/datum/species/proc/spec_death(gibbed, mob/living/carbon/human/H)
@@ -862,8 +868,8 @@ GLOBAL_LIST_EMPTY(roundstart_races)
if(!I.species_exception || !is_type_in_list(src, I.species_exception))
return FALSE
- var/num_arms = H.get_num_arms()
- var/num_legs = H.get_num_legs()
+ var/num_arms = H.get_num_arms(FALSE)
+ var/num_legs = H.get_num_legs(FALSE)
switch(slot)
if(SLOT_HANDS)
@@ -1140,22 +1146,12 @@ GLOBAL_LIST_EMPTY(roundstart_races)
switch(H.nutrition)
if(NUTRITION_LEVEL_FULL to INFINITY)
- SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "nutrition", /datum/mood_event/nutrition/fat)
H.throw_alert("nutrition", /obj/screen/alert/fat)
- if(NUTRITION_LEVEL_WELL_FED to NUTRITION_LEVEL_FULL)
- SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "nutrition", /datum/mood_event/nutrition/wellfed)
- H.clear_alert("nutrition")
- if( NUTRITION_LEVEL_FED to NUTRITION_LEVEL_WELL_FED)
- SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "nutrition", /datum/mood_event/nutrition/fed)
- H.clear_alert("nutrition")
- if(NUTRITION_LEVEL_HUNGRY to NUTRITION_LEVEL_FED)
- SEND_SIGNAL(H, COMSIG_CLEAR_MOOD_EVENT, "nutrition")
+ if(NUTRITION_LEVEL_HUNGRY to NUTRITION_LEVEL_FULL)
H.clear_alert("nutrition")
if(NUTRITION_LEVEL_STARVING to NUTRITION_LEVEL_HUNGRY)
- SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "nutrition", /datum/mood_event/nutrition/hungry)
H.throw_alert("nutrition", /obj/screen/alert/hungry)
if(0 to NUTRITION_LEVEL_STARVING)
- SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "nutrition", /datum/mood_event/nutrition/starving)
H.throw_alert("nutrition", /obj/screen/alert/starving)
/datum/species/proc/update_health_hud(mob/living/carbon/human/H)
@@ -1204,18 +1200,14 @@ GLOBAL_LIST_EMPTY(roundstart_races)
/datum/species/proc/movement_delay(mob/living/carbon/human/H)
. = 0 //We start at 0.
var/flight = 0 //Check for flight and flying items
- var/flightpack = 0
var/ignoreslow = 0
var/gravity = 0
- var/obj/item/flightpack/F = H.get_flightpack()
- if(istype(F) && F.flight)
- flightpack = 1
if(H.movement_type & FLYING)
flight = 1
gravity = H.has_gravity()
- if(!flightpack && gravity) //Check for chemicals and innate speedups and slowdowns if we're moving using our body and not a flying suit
+ if(gravity && !flight) //Check for chemicals and innate speedups and slowdowns if we're on the ground
if(H.has_trait(TRAIT_GOTTAGOFAST))
. -= 1
if(H.has_trait(TRAIT_GOTTAGOREALLYFAST))
@@ -1237,12 +1229,7 @@ GLOBAL_LIST_EMPTY(roundstart_races)
else if(istype(T) && T.allow_thrust(0.01, H))
. -= 2
- if(flightpack && F.boost)
- . -= F.boost_speed
- else if(flightpack && F.brake)
- . += 1
-
- if(!ignoreslow && !flightpack && gravity)
+ if(!ignoreslow && gravity)
if(H.wear_suit)
. += H.wear_suit.slowdown
if(H.shoes)
@@ -1297,7 +1284,7 @@ GLOBAL_LIST_EMPTY(roundstart_races)
if(target.health >= 0 && !(target.has_trait(TRAIT_FAKEDEATH)))
target.help_shake_act(user)
if(target != user)
- add_logs(user, target, "shaked")
+ log_combat(user, target, "shaked")
return 1
else
var/we_breathe = !user.has_trait(TRAIT_NOBREATH)
@@ -1387,7 +1374,7 @@ GLOBAL_LIST_EMPTY(roundstart_races)
if(user.limb_destroyer)
target.dismembering_strike(user, affecting.body_zone)
target.apply_damage(damage, BRUTE, affecting, armor_block)
- add_logs(user, target, "punched")
+ log_combat(user, target, "punched")
if((target.stat != DEAD) && damage >= user.dna.species.punchstunthreshold)
target.visible_message("
[user] has knocked [target] down! ", \
"
[user] has knocked [target] down! ", null, COMBAT_MESSAGE_RANGE)
@@ -1397,7 +1384,7 @@ GLOBAL_LIST_EMPTY(roundstart_races)
target.forcesay(GLOB.hit_appends)
/datum/species/proc/disarm(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style)
-// CITADEL EDIT slap mouthy gits
+ // CITADEL EDIT slap mouthy gits
var/aim_for_mouth = user.zone_selected == "mouth"
var/target_on_help_and_unarmed = target.a_intent == INTENT_HELP && !target.get_active_held_item()
var/target_aiming_for_mouth = target.zone_selected == "mouth"
@@ -1434,9 +1421,8 @@ GLOBAL_LIST_EMPTY(roundstart_races)
"
[user] has pushed [target]! ", null, COMBAT_MESSAGE_RANGE)
target.apply_effect(40, EFFECT_KNOCKDOWN, target.run_armor_check(affecting, "melee", "Your armor prevents your fall!", "Your armor softens your fall!"))
target.forcesay(GLOB.hit_appends)
- add_logs(user, target, "disarmed", " pushing them to the ground")
+ log_combat(user, target, "pushed over")
return*/
-
if(!target.combatmode) // CITADEL CHANGE
randn += -10 //CITADEL CHANGE - being out of combat mode makes it easier for you to get disarmed
if(user.resting) //CITADEL CHANGE
@@ -1457,14 +1443,14 @@ GLOBAL_LIST_EMPTY(roundstart_races)
else
I = null
playsound(target, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
- add_logs(user, target, "disarmed", "[I ? " removing \the [I]" : ""]")
+ log_combat(user, target, "disarmed", "[I ? " removing \the [I]" : ""]")
return
playsound(target, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
target.visible_message("
[user] attempted to disarm [target]! ", \
"
[user] attemped to disarm [target]! ", null, COMBAT_MESSAGE_RANGE)
- add_logs(user, target, "attempted to disarm")
+ log_combat(user, target, "attempted to disarm")
/datum/species/proc/spec_hitby(atom/movable/AM, mob/living/carbon/human/H)
@@ -1481,7 +1467,7 @@ GLOBAL_LIST_EMPTY(roundstart_races)
if(M.mind)
attacker_style = M.mind.martial_art
if((M != H) && M.a_intent != INTENT_HELP && H.check_shields(M, 0, M.name, attack_type = UNARMED_ATTACK))
- add_logs(M, H, "attempted to touch")
+ log_combat(M, H, "attempted to touch")
H.visible_message("
[M] attempted to touch [H]! ")
return 0
switch(M.a_intent)
@@ -1516,7 +1502,6 @@ GLOBAL_LIST_EMPTY(roundstart_races)
var/armor_block = H.run_armor_check(affecting, "melee", "
Your armor has protected your [hit_area]. ", "
Your armor has softened a hit to your [hit_area]. ",I.armour_penetration)
armor_block = min(90,armor_block) //cap damage reduction at 90%
var/Iforce = I.force //to avoid runtimes on the forcesay checks at the bottom. Some items might delete themselves if you drop them. (stunning yourself, ninja swords)
-
//CIT CHANGES START HERE - combatmode and resting checks
var/totitemdamage = I.force
if(iscarbon(user))
@@ -1529,7 +1514,6 @@ GLOBAL_LIST_EMPTY(roundstart_races)
if(!H.combatmode)
totitemdamage *= 1.5
//CIT CHANGES END HERE
-
var/weakness = H.check_weakness(I, user)
apply_damage(totitemdamage * weakness, I.damtype, def_zone, armor_block, H) //CIT CHANGE - replaces I.force with totitemdamage
@@ -1540,7 +1524,7 @@ GLOBAL_LIST_EMPTY(roundstart_races)
//dismemberment
var/probability = I.get_dismemberment_chance(affecting)
- if(prob(probability) || (H.has_trait(TRAIT_EASYDISMEMBER) && prob(2*probability)))
+ if(prob(probability) || (H.has_trait(TRAIT_EASYDISMEMBER) && prob(probability))) //try twice
if(affecting.dismember(I.damtype))
I.add_mob_blood(H)
playsound(get_turf(H), I.get_dismember_sound(), 80, 1)
@@ -1559,20 +1543,20 @@ GLOBAL_LIST_EMPTY(roundstart_races)
switch(hit_area)
if(BODY_ZONE_HEAD)
- if(H.stat == CONSCIOUS && armor_block < 50)
+ if(!I.is_sharp() && armor_block < 50)
if(prob(I.force))
- H.visible_message("
[H] has been knocked senseless! ", \
- "
[H] has been knocked senseless! ")
- H.confused = max(H.confused, 20)
H.adjustBrainLoss(20)
- H.adjust_blurriness(10)
+ if(H.stat == CONSCIOUS)
+ H.visible_message("
[H] has been knocked senseless! ", \
+ "
[H] has been knocked senseless! ")
+ H.confused = max(H.confused, 20)
+ H.adjust_blurriness(10)
if(prob(10))
H.gain_trauma(/datum/brain_trauma/mild/concussion)
else
- if(!I.is_sharp())
- H.adjustBrainLoss(I.force * 0.2)
+ H.adjustBrainLoss(I.force * 0.2)
- if(!I.is_sharp() && prob(I.force + ((100 - H.health) * 0.5)) && H != user) // rev deconversion through blunt trauma.
+ if(H.stat == CONSCIOUS && H != user && prob(I.force + ((100 - H.health) * 0.5))) // rev deconversion through blunt trauma.
var/datum/antagonist/rev/rev = H.mind.has_antag_datum(/datum/antagonist/rev)
if(rev)
rev.remove_revolutionary(FALSE, user)
@@ -1589,7 +1573,7 @@ GLOBAL_LIST_EMPTY(roundstart_races)
H.update_inv_glasses()
if(BODY_ZONE_CHEST)
- if(H.stat == CONSCIOUS && armor_block < 50)
+ if(H.stat == CONSCIOUS && !I.is_sharp() && armor_block < 50)
if(prob(I.force))
H.visible_message("
[H] has been knocked down! ", \
"
[H] has been knocked down! ")
@@ -1713,19 +1697,20 @@ GLOBAL_LIST_EMPTY(roundstart_races)
SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "hot", /datum/mood_event/hot)
var/burn_damage
- switch(H.bodytemperature)
- if(BODYTEMP_HEAT_DAMAGE_LIMIT to 400)
- H.throw_alert("temp", /obj/screen/alert/hot, 1)
- burn_damage = HEAT_DAMAGE_LEVEL_1
- if(400 to 460)
- H.throw_alert("temp", /obj/screen/alert/hot, 2)
- burn_damage = HEAT_DAMAGE_LEVEL_2
- else
- H.throw_alert("temp", /obj/screen/alert/hot, 3)
- if(H.on_fire)
- burn_damage = HEAT_DAMAGE_LEVEL_3
+ var/firemodifier = H.fire_stacks / 50
+ if (H.on_fire)
+ burn_damage = max(log(2-firemodifier,(H.bodytemperature-BODYTEMP_NORMAL))-5,0)
+ else
+ firemodifier = min(firemodifier, 0)
+ burn_damage = max(log(2-firemodifier,(H.bodytemperature-BODYTEMP_NORMAL))-5,0) // this can go below 5 at log 2.5
+ if (burn_damage)
+ switch(burn_damage)
+ if(0 to 2)
+ H.throw_alert("temp", /obj/screen/alert/hot, 1)
+ if(2 to 4)
+ H.throw_alert("temp", /obj/screen/alert/hot, 2)
else
- burn_damage = HEAT_DAMAGE_LEVEL_2
+ H.throw_alert("temp", /obj/screen/alert/hot, 3)
burn_damage = burn_damage * heatmod * H.physiology.heat_mod
if (H.stat < UNCONSCIOUS && (prob(burn_damage) * 10) / 4) //40% for level 3 damage on humans
H.emote("scream")
@@ -1816,7 +1801,7 @@ GLOBAL_LIST_EMPTY(roundstart_races)
if(H.wear_suit && ((H.wear_suit.body_parts_covered & HANDS) || (H.wear_suit.body_parts_covered & ARMS)))
arm_clothes = H.wear_suit
if(arm_clothes)
- burning_items += arm_clothes
+ burning_items |= arm_clothes
//LEGS & FEET//
var/obj/item/clothing/leg_clothes = null
@@ -1827,7 +1812,7 @@ GLOBAL_LIST_EMPTY(roundstart_races)
if(H.wear_suit && ((H.wear_suit.body_parts_covered & FEET) || (H.wear_suit.body_parts_covered & LEGS)))
leg_clothes = H.wear_suit
if(leg_clothes)
- burning_items += leg_clothes
+ burning_items |= leg_clothes
for(var/X in burning_items)
var/obj/item/I = X
@@ -1836,7 +1821,7 @@ GLOBAL_LIST_EMPTY(roundstart_races)
var/thermal_protection = H.get_thermal_protection()
- if(thermal_protection >= FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT && !no_protection)
+ if(thermal_protection >= FIRE_IMMUNITY_MAX_TEMP_PROTECT && !no_protection)
return
if(thermal_protection >= FIRE_SUIT_MAX_TEMP_PROTECT && !no_protection)
H.adjust_bodytemperature(11)
@@ -1869,3 +1854,17 @@ GLOBAL_LIST_EMPTY(roundstart_races)
/datum/species/proc/negates_gravity(mob/living/carbon/human/H)
return 0
+
+////////////////
+//Tail Wagging//
+////////////////
+
+/datum/species/proc/can_wag_tail(mob/living/carbon/human/H)
+ return FALSE
+
+/datum/species/proc/is_wagging_tail(mob/living/carbon/human/H)
+ return FALSE
+
+/datum/species/proc/start_wagging_tail(mob/living/carbon/human/H)
+
+/datum/species/proc/stop_wagging_tail(mob/living/carbon/human/H)
diff --git a/code/modules/mob/living/carbon/human/species_types/dullahan.dm b/code/modules/mob/living/carbon/human/species_types/dullahan.dm
index d6857e1c93..da7de39a03 100644
--- a/code/modules/mob/living/carbon/human/species_types/dullahan.dm
+++ b/code/modules/mob/living/carbon/human/species_types/dullahan.dm
@@ -1,5 +1,5 @@
/datum/species/dullahan
- name = "dullahan"
+ name = "Dullahan"
id = "dullahan"
default_color = "FFFFFF"
species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS)
@@ -129,6 +129,7 @@
else
qdel(src)
+
/obj/item/dullahan_relay/Destroy()
if(!QDELETED(owner))
var/mob/living/carbon/human/H = owner
diff --git a/code/modules/mob/living/carbon/human/species_types/felinid.dm b/code/modules/mob/living/carbon/human/species_types/felinid.dm
new file mode 100644
index 0000000000..7207a4f49d
--- /dev/null
+++ b/code/modules/mob/living/carbon/human/species_types/felinid.dm
@@ -0,0 +1,130 @@
+//Subtype of human
+/datum/species/human/felinid
+ name = "Felinid"
+ id = "felinid"
+ limbs_id = "human"
+
+ mutant_bodyparts = list("ears", "tail_human")
+ default_features = list("mcolor" = "FFF", "tail_human" = "Cat", "ears" = "Cat", "wings" = "None")
+
+ mutantears = /obj/item/organ/ears/cat
+ mutanttail = /obj/item/organ/tail/cat
+
+/datum/species/human/felinid/qualifies_for_rank(rank, list/features)
+ return TRUE
+
+//Curiosity killed the cat's wagging tail.
+/datum/species/human/felinid/spec_death(gibbed, mob/living/carbon/human/H)
+ if(H)
+ stop_wagging_tail(H)
+
+/datum/species/human/felinid/spec_stun(mob/living/carbon/human/H,amount)
+ if(H)
+ stop_wagging_tail(H)
+ . = ..()
+
+/datum/species/human/felinid/can_wag_tail(mob/living/carbon/human/H)
+ return ("tail_human" in mutant_bodyparts) || ("waggingtail_human" in mutant_bodyparts)
+
+/datum/species/human/felinid/is_wagging_tail(mob/living/carbon/human/H)
+ return ("waggingtail_human" in mutant_bodyparts)
+
+/datum/species/human/felinid/start_wagging_tail(mob/living/carbon/human/H)
+ if("tail_human" in mutant_bodyparts)
+ mutant_bodyparts -= "tail_human"
+ mutant_bodyparts |= "waggingtail_human"
+ H.update_body()
+
+/datum/species/human/felinid/stop_wagging_tail(mob/living/carbon/human/H)
+ if("waggingtail_human" in mutant_bodyparts)
+ mutant_bodyparts -= "waggingtail_human"
+ mutant_bodyparts |= "tail_human"
+ H.update_body()
+
+/datum/species/human/felinid/on_species_gain(mob/living/carbon/C, datum/species/old_species, pref_load)
+ if(ishuman(C))
+ var/mob/living/carbon/human/H = C
+ if(!pref_load) //Hah! They got forcefully purrbation'd. Force default felinid parts on them if they have no mutant parts in those areas!
+ if(H.dna.features["tail_human"] == "None")
+ H.dna.features["tail_human"] = "Cat"
+ if(H.dna.features["ears"] == "None")
+ H.dna.features["ears"] = "Cat"
+ if(H.dna.features["ears"] == "Cat")
+ var/obj/item/organ/ears/cat/ears = new
+ ears.Insert(H, drop_if_replaced = FALSE)
+ else
+ mutantears = /obj/item/organ/ears
+ if(H.dna.features["tail_human"] == "Cat")
+ var/obj/item/organ/tail/cat/tail = new
+ tail.Insert(H, drop_if_replaced = FALSE)
+ else
+ mutanttail = null
+ return ..()
+
+/datum/species/human/felinid/on_species_loss(mob/living/carbon/H, datum/species/new_species, pref_load)
+ var/obj/item/organ/ears/cat/ears = H.getorgan(/obj/item/organ/ears/cat)
+ var/obj/item/organ/tail/cat/tail = H.getorgan(/obj/item/organ/tail/cat)
+
+ if(ears)
+ var/obj/item/organ/ears/NE
+ if(new_species && new_species.mutantears)
+ // Roundstart cat ears override new_species.mutantears, reset it here.
+ new_species.mutantears = initial(new_species.mutantears)
+ if(new_species.mutantears)
+ NE = new new_species.mutantears
+ if(!NE)
+ // Go with default ears
+ NE = new /obj/item/organ/ears
+ NE.Insert(H, drop_if_replaced = FALSE)
+
+ if(tail)
+ var/obj/item/organ/tail/NT
+ if(new_species && new_species.mutanttail)
+ // Roundstart cat tail overrides new_species.mutanttail, reset it here.
+ new_species.mutanttail = initial(new_species.mutanttail)
+ if(new_species.mutanttail)
+ NT = new new_species.mutanttail
+ if(NT)
+ NT.Insert(H, drop_if_replaced = FALSE)
+ else
+ tail.Remove(H)
+
+/proc/mass_purrbation()
+ for(var/M in GLOB.mob_list)
+ if(ishumanbasic(M))
+ purrbation_apply(M)
+ CHECK_TICK
+
+/proc/mass_remove_purrbation()
+ for(var/M in GLOB.mob_list)
+ if(ishumanbasic(M))
+ purrbation_remove(M)
+ CHECK_TICK
+
+/proc/purrbation_toggle(mob/living/carbon/human/H, silent = FALSE)
+ if(!ishumanbasic(H))
+ return
+ if(!iscatperson(H))
+ purrbation_apply(H, silent)
+ . = TRUE
+ else
+ purrbation_remove(H, silent)
+ . = FALSE
+
+/proc/purrbation_apply(mob/living/carbon/human/H, silent = FALSE)
+ if(!ishuman(H) || iscatperson(H))
+ return
+ H.set_species(/datum/species/human/felinid)
+
+ if(!silent)
+ to_chat(H, "Something is nya~t right.")
+ playsound(get_turf(H), 'sound/effects/meow1.ogg', 50, 1, -1)
+
+/proc/purrbation_remove(mob/living/carbon/human/H, silent = FALSE)
+ if(!ishuman(H) || !iscatperson(H))
+ return
+
+ H.set_species(/datum/species/human)
+
+ if(!silent)
+ to_chat(H, "You are no longer a cat.")
diff --git a/code/modules/mob/living/carbon/human/species_types/golems.dm b/code/modules/mob/living/carbon/human/species_types/golems.dm
index 538d0860ab..afc3fa2d04 100644
--- a/code/modules/mob/living/carbon/human/species_types/golems.dm
+++ b/code/modules/mob/living/carbon/human/species_types/golems.dm
@@ -27,7 +27,7 @@
var/random_eligible = TRUE //If false, the golem subtype can't be made through golem mutation toxin
var/prefix = "Iron"
- var/list/special_names
+ var/list/special_names = list("Tarkus")
var/human_surname_chance = 3
var/special_name_chance = 5
@@ -68,8 +68,9 @@
meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/golem/adamantine
mutant_organs = list(/obj/item/organ/adamantine_resonator, /obj/item/organ/vocal_cords/adamantine)
fixed_mut_color = "4ed"
- info_text = "As an
Adamantine Golem , you possess special vocal cords allowing you to \"resonate\" messages to all golems."
+ info_text = "As an
Adamantine Golem , you possess special vocal cords allowing you to \"resonate\" messages to all golems. Your unique mineral makeup makes you immune to most types of magic."
prefix = "Adamantine"
+ special_names = null
/datum/species/golem/adamantine/on_species_gain(mob/living/carbon/C, datum/species/old_species)
..()
@@ -86,7 +87,7 @@
fixed_mut_color = "a3d"
meat = /obj/item/stack/ore/plasma
//Can burn and takes damage from heat
- inherent_traits = list(TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_NOGUNS,TRAIT_RADIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NODISMEMBER)
+ inherent_traits = list(TRAIT_NOBREATH, TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_NOGUNS,TRAIT_RADIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NODISMEMBER) //no RESISTHEAT, NOFIRE
info_text = "As a
Plasma Golem , you burn easily. Be careful, if you get hot enough while burning, you'll blow up!"
heatmod = 0 //fine until they blow up
prefix = "Plasma"
@@ -135,7 +136,7 @@
if(H.fire_stacks)
to_chat(owner, "
You ignite yourself! ")
else
- to_chat(owner, "
You try ignite yourself, but fail! ")
+ to_chat(owner, "
You try to ignite yourself, but fail! ")
H.IgniteMob() //firestacks are already there passively
//Harder to hurt
@@ -159,6 +160,7 @@
meat = /obj/item/stack/ore/gold
info_text = "As a
Gold Golem , you are faster but less resistant than the average golem."
prefix = "Golden"
+ special_names = list("Boy")
//Heavier, thus higher chance of stunning when punching
/datum/species/golem/silver
@@ -167,7 +169,7 @@
fixed_mut_color = "ddd"
punchstunthreshold = 9 //60% chance, from 40%
meat = /obj/item/stack/ore/silver
- info_text = "As a
Silver Golem , your attacks are heavier and have a higher chance of stunning."
+ info_text = "As a
Silver Golem , your attacks have a higher chance of stunning. Being made of silver, your body is immune to most types of magic."
prefix = "Silver"
special_names = list("Surfer", "Chariot", "Lining")
@@ -194,6 +196,7 @@
attack_verb = "smash"
attack_sound = 'sound/effects/meteorimpact.ogg' //hits pretty hard
prefix = "Plasteel"
+ special_names = null
//Immune to ash storms
/datum/species/golem/titanium
@@ -204,6 +207,7 @@
info_text = "As a
Titanium Golem , you are immune to ash storms, and slightly more resistant to burn damage."
burnmod = 0.9
prefix = "Titanium"
+ special_names = list("Dioxide")
/datum/species/golem/titanium/on_species_gain(mob/living/carbon/C, datum/species/old_species)
. = ..()
@@ -222,6 +226,7 @@
info_text = "As a
Plastitanium Golem , you are immune to both ash storms and lava, and slightly more resistant to burn damage."
burnmod = 0.8
prefix = "Plastitanium"
+ special_names = null
/datum/species/golem/plastitanium/on_species_gain(mob/living/carbon/C, datum/species/old_species)
. = ..()
@@ -257,10 +262,10 @@
/datum/species/golem/wood
name = "Wood Golem"
id = "wood golem"
- fixed_mut_color = "49311c"
+ fixed_mut_color = "9E704B"
meat = /obj/item/stack/sheet/mineral/wood
//Can burn and take damage from heat
- inherent_traits = list(TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_NOGUNS,TRAIT_RADIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NODISMEMBER)
+ inherent_traits = list(TRAIT_NOBREATH, TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_NOGUNS,TRAIT_RADIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NODISMEMBER)
armor = 30
burnmod = 1.25
heatmod = 1.5
@@ -315,6 +320,7 @@
var/last_event = 0
var/active = null
prefix = "Uranium"
+ special_names = list("Oxide", "Rod", "Meltdown")
/datum/species/golem/uranium/spec_life(mob/living/carbon/human/H)
if(!active)
@@ -334,9 +340,10 @@
armor = 0
burnmod = 3 //melts easily
brutemod = 0.25
- info_text = "As a
Sand Golem , you are immune to physical bullets and take very little brute damage, but are extremely vulnerable to burn damage. You will also turn to sand when dying, preventing any form of recovery."
+ info_text = "As a
Sand Golem , you are immune to physical bullets and take very little brute damage, but are extremely vulnerable to burn damage and energy weapons. You will also turn to sand when dying, preventing any form of recovery."
attack_sound = 'sound/effects/shovel_dig.ogg'
prefix = "Sand"
+ special_names = list("Castle", "Bag", "Dune", "Worm", "Storm")
/datum/species/golem/sand/spec_death(gibbed, mob/living/carbon/human/H)
H.visible_message("
[H] turns into a pile of sand! ")
@@ -364,9 +371,10 @@
armor = 0
brutemod = 3 //very fragile
burnmod = 0.25
- info_text = "As a
Glass Golem , you reflect lasers and energy weapons, and are very resistant to burn damage, but you are extremely vulnerable to brute damage. On death, you'll shatter beyond any hope of recovery."
+ info_text = "As a
Glass Golem , you reflect lasers and energy weapons, and are very resistant to burn damage. However, you are extremely vulnerable to brute damage. On death, you'll shatter beyond any hope of recovery."
attack_sound = 'sound/effects/glassbr2.ogg'
prefix = "Glass"
+ special_names = list("Lens", "Prism", "Fiber", "Bead")
/datum/species/golem/glass/spec_death(gibbed, mob/living/carbon/human/H)
playsound(H, "shatter", 70, 1)
@@ -397,7 +405,7 @@
id = "bluespace golem"
fixed_mut_color = "33f"
meat = /obj/item/stack/ore/bluespace_crystal
- info_text = "As a
Bluespace Golem , are spatially unstable: you will teleport when hit, and you can teleport manually at a long distance."
+ info_text = "As a
Bluespace Golem , you are spatially unstable: You will teleport when hit, and you can teleport manually at a long distance."
attack_verb = "bluespace punch"
attack_sound = 'sound/effects/phasein.ogg'
prefix = "Bluespace"
@@ -494,10 +502,11 @@
punchdamagehigh = 1
punchstunthreshold = 2 //Harmless and can't stun
meat = /obj/item/stack/ore/bananium
- info_text = "As a
Bananium Golem , you are made for pranking. Your body emits natural honks, and you cannot hurt people when punching them. Your skin also emits bananas when damaged."
+ info_text = "As a
Bananium Golem , you are made for pranking. Your body emits natural honks, and you can barely even hurt people when punching them. Your skin also bleeds banana peels when damaged."
attack_verb = "honk"
attack_sound = 'sound/items/airhorn2.ogg'
prefix = "Bananium"
+ special_names = null
var/last_honk = 0
var/honkooldown = 0
@@ -566,16 +575,17 @@
id = "runic golem"
limbs_id = "cultgolem"
sexes = FALSE
- info_text = "As a
Runic Golem , you possess eldritch powers granted by the Elder God Nar'Sie."
+ info_text = "As a
Runic Golem , you possess eldritch powers granted by the Elder Goddess Nar'Sie."
species_traits = list(NOBLOOD,NO_UNDERWEAR,NOEYES) //no mutcolors
prefix = "Runic"
+ special_names = null
var/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift/golem/phase_shift
var/obj/effect/proc_holder/spell/targeted/abyssal_gaze/abyssal_gaze
var/obj/effect/proc_holder/spell/targeted/dominate/dominate
/datum/species/golem/runic/random_name(gender,unique,lastname)
- var/edgy_first_name = pick("Razor","Blood","Dark","Evil","Cold","Pale","Black","Silent","Chaos","Deadly")
+ var/edgy_first_name = pick("Razor","Blood","Dark","Evil","Cold","Pale","Black","Silent","Chaos","Deadly","Coldsteel")
var/edgy_last_name = pick("Edge","Night","Death","Razor","Blade","Steel","Calamity","Twilight","Shadow","Nightmare") //dammit Razor Razor
var/golem_name = "[edgy_first_name] [edgy_last_name]"
return golem_name
@@ -619,8 +629,7 @@
id = "clockwork golem"
say_mod = "clicks"
limbs_id = "clockgolem"
- info_text = "
As a clockwork golem , you are faster than \
- other types of golem (being a machine), and are immune to electric shocks. "
+ info_text = "
As a Clockwork Golem , you are faster than other types of golems. On death, you will break down into scrap. "
species_traits = list(NOBLOOD,NO_UNDERWEAR,NOEYES)
inherent_biotypes = list(MOB_ROBOTIC, MOB_HUMANOID)
armor = 20 //Reinforced, but much less so to allow for fast movement
@@ -628,9 +637,9 @@
attack_sound = 'sound/magic/clockwork/anima_fragment_attack.ogg'
sexes = FALSE
speedmod = 0
- siemens_coeff = 0
damage_overlay_type = "synth"
prefix = "Clockwork"
+ special_names = list("Remnant", "Relic", "Scrap", "Vestige") //RIP Ratvar
var/has_corpse
/datum/species/golem/clockwork/on_species_gain(mob/living/carbon/human/H)
@@ -666,14 +675,15 @@
blacklisted = TRUE
dangerous_existence = TRUE
random_eligible = FALSE
-
+ info_text = "
As a Clockwork Golem Servant , you are faster than other types of golems. " //warcult golems leave a corpse
/datum/species/golem/cloth
name = "Cloth Golem"
id = "cloth golem"
limbs_id = "clothgolem"
sexes = FALSE
- info_text = "As a
Cloth Golem , you are able to reform yourself after death, provided your remains aren't burned or destroyed. You are, of course, very flammable."
+ info_text = "As a
Cloth Golem , you are able to reform yourself after death, provided your remains aren't burned or destroyed. You are, of course, very flammable. \
+ Being made of cloth, your body is magic resistant and faster than that of other golems, but weaker and less resilient."
species_traits = list(NOBLOOD,NO_UNDERWEAR) //no mutcolors, and can burn
inherent_traits = list(TRAIT_RESISTCOLD,TRAIT_NOBREATH,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_RADIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NODISMEMBER,TRAIT_NOGUNS)
inherent_biotypes = list(MOB_UNDEAD, MOB_HUMANOID)
@@ -684,6 +694,7 @@
punchstunthreshold = 7
punchdamagehigh = 8 // not as heavy as stone
prefix = "Cloth"
+ special_names = null
/datum/species/golem/cloth/on_species_gain(mob/living/carbon/C, datum/species/old_species)
..()
@@ -734,6 +745,7 @@
var/mob/living/carbon/human/cloth_golem
/obj/structure/cloth_pile/Initialize(mapload, mob/living/carbon/human/H)
+ . = ..()
if(!QDELETED(H) && is_species(H, /datum/species/golem/cloth))
H.unequip_everything()
H.forceMove(src)
@@ -781,11 +793,12 @@
fire_act()
/datum/species/golem/plastic
- name = "Plastic"
+ name = "Plastic Golem"
id = "plastic golem"
prefix = "Plastic"
+ special_names = null
fixed_mut_color = "fff"
- info_text = "As a
Plastic Golem , you are capable of ventcrawling, and passing through plastic flaps."
+ info_text = "As a
Plastic Golem , you are capable of ventcrawling and passing through plastic flaps as long as you are naked."
/datum/species/golem/plastic/on_species_gain(mob/living/carbon/C, datum/species/old_species)
. = ..()
diff --git a/code/modules/mob/living/carbon/human/species_types/humans.dm b/code/modules/mob/living/carbon/human/species_types/humans.dm
index a266f42f4a..a7c20b981a 100644
--- a/code/modules/mob/living/carbon/human/species_types/humans.dm
+++ b/code/modules/mob/living/carbon/human/species_types/humans.dm
@@ -10,23 +10,32 @@
disliked_food = GROSS | RAW
liked_food = JUNKFOOD | FRIED
-
/datum/species/human/qualifies_for_rank(rank, list/features)
return TRUE //Pure humans are always allowed in all roles.
-//Curiosity killed the cat's wagging tail.
/datum/species/human/spec_death(gibbed, mob/living/carbon/human/H)
if(H)
- H.endTailWag()
+ stop_wagging_tail(H)
/datum/species/human/spec_stun(mob/living/carbon/human/H,amount)
if(H)
- H.endTailWag()
+ stop_wagging_tail(H)
. = ..()
-/datum/species/human/on_species_gain(mob/living/carbon/human/H, datum/species/old_species)
- if(H.dna.features["ears"] == "Cat")
- mutantears = /obj/item/organ/ears/cat
- if(H.dna.features["tail_human"] == "Cat")
- mutanttail = /obj/item/organ/tail/cat
- ..()
+/datum/species/human/can_wag_tail(mob/living/carbon/human/H)
+ return ("mam_tail" in mutant_bodyparts) || ("mam_waggingtail" in mutant_bodyparts)
+
+/datum/species/human/is_wagging_tail(mob/living/carbon/human/H)
+ return ("mam_waggingtail" in mutant_bodyparts)
+
+/datum/species/human/start_wagging_tail(mob/living/carbon/human/H)
+ if("tail_human" in mutant_bodyparts)
+ mutant_bodyparts -= "mam_tail"
+ mutant_bodyparts |= "mam_waggingtail"
+ H.update_body()
+
+/datum/species/human/stop_wagging_tail(mob/living/carbon/human/H)
+ if("mam_waggingtail" in mutant_bodyparts)
+ mutant_bodyparts -= "mam_waggingtail"
+ mutant_bodyparts |= "mam_tail"
+ H.update_body()
diff --git a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm
index cbbfe2b4d7..c72534972c 100644
--- a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm
@@ -5,9 +5,9 @@
default_color = "00FF90"
say_mod = "chirps"
species_traits = list(MUTCOLORS,EYECOLOR,HAIR,FACEHAIR,NOBLOOD)
- inherent_traits = list(TRAIT_TOXINLOVER)
mutant_bodyparts = list("mam_tail", "mam_ears", "taur") //CIT CHANGE
default_features = list("mcolor" = "FFF", "mam_tail" = "None", "mam_ears" = "None") //CIT CHANGE
+ inherent_traits = list(TRAIT_TOXINLOVER)
meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/slime
exotic_blood = "slimejelly"
damage_overlay_type = ""
@@ -64,7 +64,7 @@
if(!limbs_to_consume.len)
H.losebreath++
return
- if(H.get_num_legs()) //Legs go before arms
+ if(H.get_num_legs(FALSE)) //Legs go before arms
limbs_to_consume -= list(BODY_ZONE_R_ARM, BODY_ZONE_L_ARM)
consumed_limb = H.get_bodypart(pick(limbs_to_consume))
consumed_limb.drop_limb()
@@ -647,7 +647,7 @@
if(message)
var/msg = "
\[[species.slimelink_owner.real_name]'s Slime Link\] [H]: [message] "
- log_talk(H,"SlimeLink: [key_name(H)] : [msg]",LOGSAY)
+ log_directed_talk(H, species.slimelink_owner, msg, LOG_SAY, "slime link")
for(var/X in species.linked_mobs)
var/mob/living/M = X
if(QDELETED(M) || M.stat == DEAD)
@@ -684,7 +684,7 @@
var/msg = sanitize(input("Message:", "Telepathy") as text|null)
if(msg)
- log_talk(H,"SlimeTelepathy: [key_name(H)]->[key_name(M)] : [msg]",LOGSAY)
+ log_directed_talk(H, M, msg, LOG_SAY, "slime telepathy")
to_chat(M, "
You hear an alien voice in your head... [msg] ")
to_chat(H, "
You telepathically said: \"[msg]\" to [M] ")
for(var/dead in GLOB.dead_mob_list)
diff --git a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
index bd286e9bc2..f083e7c658 100644
--- a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
@@ -37,17 +37,39 @@
/datum/species/lizard/qualifies_for_rank(rank, list/features)
return TRUE
-
+
//I wag in death
/datum/species/lizard/spec_death(gibbed, mob/living/carbon/human/H)
if(H)
- H.endTailWag()
+ stop_wagging_tail(H)
/datum/species/lizard/spec_stun(mob/living/carbon/human/H,amount)
if(H)
- H.endTailWag()
+ stop_wagging_tail(H)
. = ..()
+/datum/species/lizard/can_wag_tail(mob/living/carbon/human/H)
+ return ("tail_lizard" in mutant_bodyparts) || ("waggingtail_lizard" in mutant_bodyparts)
+
+/datum/species/lizard/is_wagging_tail(mob/living/carbon/human/H)
+ return ("waggingtail_lizard" in mutant_bodyparts)
+
+/datum/species/lizard/start_wagging_tail(mob/living/carbon/human/H)
+ if("tail_lizard" in mutant_bodyparts)
+ mutant_bodyparts -= "tail_lizard"
+ mutant_bodyparts -= "spines"
+ mutant_bodyparts |= "waggingtail_lizard"
+ mutant_bodyparts |= "waggingspines"
+ H.update_body()
+
+/datum/species/lizard/stop_wagging_tail(mob/living/carbon/human/H)
+ if("waggingtail_lizard" in mutant_bodyparts)
+ mutant_bodyparts -= "waggingtail_lizard"
+ mutant_bodyparts -= "waggingspines"
+ mutant_bodyparts |= "tail_lizard"
+ mutant_bodyparts |= "spines"
+ H.update_body()
+
/*
Lizard subspecies: ASHWALKERS
*/
diff --git a/code/modules/mob/living/carbon/human/species_types/mothmen.dm b/code/modules/mob/living/carbon/human/species_types/mothmen.dm
index 7d5dc2022f..d15d989384 100644
--- a/code/modules/mob/living/carbon/human/species_types/mothmen.dm
+++ b/code/modules/mob/living/carbon/human/species_types/mothmen.dm
@@ -1,5 +1,5 @@
/datum/species/moth
- name = "Mothmen"
+ name = "Mothman"
id = "moth"
say_mod = "flutters"
default_color = "00FF00"
diff --git a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm
index 22a44e3fa7..3d42fb32cf 100644
--- a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm
@@ -180,6 +180,7 @@
AddComponent(/datum/component/butchering, 80, 70)
/obj/item/light_eater/afterattack(atom/movable/AM, mob/user, proximity)
+ . = ..()
if(!proximity)
return
if(isopenturf(AM)) //So you can actually melee with it
diff --git a/code/modules/mob/living/carbon/human/species_types/skeletons.dm b/code/modules/mob/living/carbon/human/species_types/skeletons.dm
index e3a72601ed..d079080eaa 100644
--- a/code/modules/mob/living/carbon/human/species_types/skeletons.dm
+++ b/code/modules/mob/living/carbon/human/species_types/skeletons.dm
@@ -7,7 +7,7 @@
sexes = 0
meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/skeleton
species_traits = list(NOBLOOD)
- inherent_traits = list(TRAIT_RESISTHEAT,TRAIT_NOBREATH,TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_RADIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NOHUNGER,TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT)
+ inherent_traits = list(TRAIT_RESISTHEAT,TRAIT_NOBREATH,TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_RADIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NOHUNGER,TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT,TRAIT_FAKEDEATH)
inherent_biotypes = list(MOB_UNDEAD, MOB_HUMANOID)
mutanttongue = /obj/item/organ/tongue/bone
damage_overlay_type = ""//let's not show bloody wounds or burns over bones.
diff --git a/code/modules/mob/living/carbon/human/species_types/vampire.dm b/code/modules/mob/living/carbon/human/species_types/vampire.dm
index 59de0755a2..6f7f227a80 100644
--- a/code/modules/mob/living/carbon/human/species_types/vampire.dm
+++ b/code/modules/mob/living/carbon/human/species_types/vampire.dm
@@ -1,5 +1,5 @@
/datum/species/vampire
- name = "vampire"
+ name = "Vampire"
id = "vampire"
default_color = "FFFFFF"
species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS,DRINKSBLOOD)
diff --git a/code/modules/mob/living/carbon/human/species_types/zombies.dm b/code/modules/mob/living/carbon/human/species_types/zombies.dm
index 801d0a7192..504dbb514b 100644
--- a/code/modules/mob/living/carbon/human/species_types/zombies.dm
+++ b/code/modules/mob/living/carbon/human/species_types/zombies.dm
@@ -2,14 +2,14 @@
/datum/species/zombie
// 1spooky
- name = "High Functioning Zombie"
+ name = "High-Functioning Zombie"
id = "zombie"
say_mod = "moans"
sexes = 0
blacklisted = 1
meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/zombie
species_traits = list(NOBLOOD,NOZOMBIE,NOTRANSSTING)
- inherent_traits = list(TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_RADIMMUNE,TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT,TRAIT_NOBREATH)
+ inherent_traits = list(TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_RADIMMUNE,TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT,TRAIT_NOBREATH,TRAIT_NODEATH,TRAIT_FAKEDEATH)
inherent_biotypes = list(MOB_UNDEAD, MOB_HUMANOID)
mutanttongue = /obj/item/organ/tongue/zombie
var/static/list/spooks = list('sound/hallucinations/growl1.ogg','sound/hallucinations/growl2.ogg','sound/hallucinations/growl3.ogg','sound/hallucinations/veryfar_noise.ogg','sound/hallucinations/wail.ogg')
@@ -29,6 +29,7 @@
armor = 20 // 120 damage to KO a zombie, which kills it
speedmod = 1.6
mutanteyes = /obj/item/organ/eyes/night_vision/zombie
+ var/heal_rate = 1
var/regen_cooldown = 0
/datum/species/zombie/infectious/check_roundstart_eligible()
@@ -46,15 +47,25 @@
/datum/species/zombie/infectious/spec_life(mob/living/carbon/C)
. = ..()
C.a_intent = INTENT_HARM // THE SUFFERING MUST FLOW
+
+ //Zombies never actually die, they just fall down until they regenerate enough to rise back up.
+ //They must be restrained, beheaded or gibbed to stop being a threat.
if(regen_cooldown < world.time)
- C.heal_overall_damage(4,4)
- C.adjustToxLoss(-4)
- if(prob(4))
+ var/heal_amt = heal_rate
+ if(C.InCritical())
+ heal_amt *= 2
+ C.heal_overall_damage(heal_amt,heal_amt)
+ C.adjustToxLoss(-heal_amt)
+ if(!C.InCritical() && prob(4))
playsound(C, pick(spooks), 50, TRUE, 10)
- if(C.InCritical())
- C.death()
- // Zombies only move around when not in crit, they instantly
- // succumb otherwise, and will standup again soon
+
+//Congrats you somehow died so hard you stopped being a zombie
+/datum/species/zombie/infectious/spec_death(mob/living/carbon/C)
+ . = ..()
+ var/obj/item/organ/zombie_infection/infection
+ infection = C.getorganslot(ORGAN_SLOT_ZOMBIE)
+ if(infection)
+ qdel(infection)
/datum/species/zombie/infectious/on_species_gain(mob/living/carbon/C, datum/species/old_species)
. = ..()
diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm
index ff4b77a4ee..4621986c0c 100644
--- a/code/modules/mob/living/carbon/life.dm
+++ b/code/modules/mob/living/carbon/life.dm
@@ -11,9 +11,20 @@
if(stat != DEAD) //Reagent processing needs to come before breathing, to prevent edge cases.
handle_organs()
- if(..()) //not dead
+ . = ..()
+
+ if (QDELETED(src))
+ return
+
+ if(.) //not dead
handle_blood()
+ if(stat != DEAD)
+ var/bprv = handle_bodyparts()
+ if(bprv & BODYPART_LIFE_UPDATE_HEALTH)
+ updatehealth()
+ update_stamina()
+
if(stat != DEAD)
handle_brain_damage()
@@ -22,6 +33,7 @@
if(stat == DEAD)
stop_sound_channel(CHANNEL_HEARTBEAT)
+ rot()
//Updates the number of stored chemicals for powers
handle_changeling()
@@ -52,7 +64,7 @@
return
if(ismob(loc))
return
- if(istype(loc, /obj/belly))
+ if(isbelly(loc))
return
var/datum/gas_mixture/environment
@@ -65,7 +77,7 @@
if(health <= HEALTH_THRESHOLD_FULLCRIT || (pulledby && pulledby.grab_state >= GRAB_KILL))
losebreath++ //You can't breath at all when in critical or when being choked, so you're going to miss a breath
- else if(health <= HEALTH_THRESHOLD_CRIT)
+ else if(health <= crit_threshold)
losebreath += 0.25 //You're having trouble breathing in soft crit, so you'll miss a breath one in four times
//Suffocate
@@ -160,7 +172,7 @@
else //Enough oxygen
failed_last_breath = 0
- if(health >= HEALTH_THRESHOLD_CRIT)
+ if(health >= crit_threshold)
adjustOxyLoss(-5)
oxygen_used = breath_gases[/datum/gas/oxygen][MOLES]
clear_alert("not_enough_oxy")
@@ -210,15 +222,59 @@
hallucination += 10
else if(bz_partialpressure > 0.01)
hallucination += 5
+
//TRITIUM
if(breath_gases[/datum/gas/tritium])
var/tritium_partialpressure = (breath_gases[/datum/gas/tritium][MOLES]/breath.total_moles())*breath_pressure
radiation += tritium_partialpressure/10
+
//NITRYL
- if (breath_gases[/datum/gas/nitryl])
+ if(breath_gases[/datum/gas/nitryl])
var/nitryl_partialpressure = (breath_gases[/datum/gas/nitryl][MOLES]/breath.total_moles())*breath_pressure
adjustFireLoss(nitryl_partialpressure/4)
+ //MIASMA
+ if(breath_gases[/datum/gas/miasma])
+ var/miasma_partialpressure = (breath_gases[/datum/gas/miasma][MOLES]/breath.total_moles())*breath_pressure
+
+ if(prob(1 * miasma_partialpressure))
+ var/datum/disease/advance/miasma_disease = new /datum/disease/advance/random(2,3)
+ miasma_disease.name = "Unknown"
+ ForceContractDisease(miasma_disease, TRUE, TRUE)
+
+ //Miasma side effects
+ switch(miasma_partialpressure)
+ if(1 to 5)
+ // At lower pp, give out a little warning
+ SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "smell")
+ if(prob(5))
+ to_chat(src, "
There is an unpleasant smell in the air. ")
+ if(5 to 20)
+ //At somewhat higher pp, warning becomes more obvious
+ if(prob(15))
+ to_chat(src, "
You smell something horribly decayed inside this room. ")
+ SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "smell", /datum/mood_event/disgust/bad_smell)
+ if(15 to 30)
+ //Small chance to vomit. By now, people have internals on anyway
+ if(prob(5))
+ to_chat(src, "
The stench of rotting carcasses is unbearable! ")
+ SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "smell", /datum/mood_event/disgust/nauseating_stench)
+ vomit()
+ if(30 to INFINITY)
+ //Higher chance to vomit. Let the horror start
+ if(prob(25))
+ to_chat(src, "
The stench of rotting carcasses is unbearable! ")
+ SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "smell", /datum/mood_event/disgust/nauseating_stench)
+ vomit()
+ else
+ SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "smell")
+
+
+ //Clear all moods if no miasma at all
+ else
+ SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "smell")
+
+
breath.garbage_collect()
@@ -246,9 +302,46 @@
if(!.)
return FALSE //to differentiate between no internals and active, but empty internals
+// Make corpses rot, emitting miasma
+/mob/living/carbon/proc/rot()
+ // Properly stored corpses shouldn't create miasma
+ if(istype(loc, /obj/structure/closet/crate/coffin)|| istype(loc, /obj/structure/closet/body_bag) || istype(loc, /obj/structure/bodycontainer))
+ return
+
+ // No decay if formaldehyde in corpse or when the corpse is charred
+ if(reagents.has_reagent("formaldehyde", 15) || has_trait(TRAIT_HUSK))
+ return
+
+ // Also no decay if corpse chilled or not organic/undead
+ if(bodytemperature <= T0C-10 || (!(MOB_ORGANIC in mob_biotypes) && !(MOB_UNDEAD in mob_biotypes)))
+ return
+
+ // Wait a bit before decaying
+ if(world.time - timeofdeath < 1200)
+ return
+
+ var/deceasedturf = get_turf(src)
+
+ // Closed turfs don't have any air in them, so no gas building up
+ if(!istype(deceasedturf,/turf/open))
+ return
+
+ var/turf/open/miasma_turf = deceasedturf
+
+ var/list/cached_gases = miasma_turf.air.gases
+
+ ASSERT_GAS(/datum/gas/miasma, miasma_turf.air)
+ cached_gases[/datum/gas/miasma][MOLES] += 0.02
+
/mob/living/carbon/proc/handle_blood()
return
+/mob/living/carbon/proc/handle_bodyparts()
+ for(var/I in bodyparts)
+ var/obj/item/bodypart/BP = I
+ if(BP.needs_processing)
+ . |= BP.on_life()
+
/mob/living/carbon/proc/handle_organs()
for(var/V in internal_organs)
var/obj/item/organ/O = V
@@ -437,6 +530,7 @@ GLOBAL_LIST_INIT(ballmer_windows_me_msg, list("Yo man, what if, we like, uh, put
if(drunkenness)
drunkenness = max(drunkenness - (drunkenness * 0.04), 0)
if(drunkenness >= 6)
+ SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "drunk", /datum/mood_event/drunk)
if(prob(25))
slurring += 2
jitteriness = max(jitteriness - 3, 0)
@@ -457,12 +551,12 @@ GLOBAL_LIST_INIT(ballmer_windows_me_msg, list("Yo man, what if, we like, uh, put
else
ballmer_percent = (-abs(drunkenness - 13.35) / 0.9) + 1
if(prob(5))
- say(pick(GLOB.ballmer_good_msg))
+ say(pick(GLOB.ballmer_good_msg), forced = "ballmer")
SSresearch.science_tech.add_point_list(list(TECHWEB_POINT_TYPE_GENERIC = BALLMER_POINTS * ballmer_percent))
if(drunkenness > 26) // by this point you're into windows ME territory
if(prob(5))
SSresearch.science_tech.remove_point_list(list(TECHWEB_POINT_TYPE_GENERIC = BALLMER_POINTS))
- say(pick(GLOB.ballmer_windows_me_msg))
+ say(pick(GLOB.ballmer_windows_me_msg), forced = "ballmer")
if(drunkenness >= 41)
if(prob(25))
@@ -551,9 +645,9 @@ GLOBAL_LIST_INIT(ballmer_windows_me_msg, list("Yo man, what if, we like, uh, put
reagents.metabolize(src, can_overdose=FALSE, liverless = TRUE)
if(has_trait(TRAIT_STABLEHEART))
return
- adjustToxLoss(8, TRUE, TRUE)
+ adjustToxLoss(4, TRUE, TRUE)
if(prob(30))
- to_chat(src, "
You feel confused and nauseated... ")//actual symptoms of liver failure
+ to_chat(src, "
You feel a stabbing pain in your abdomen! ")
////////////////
@@ -577,19 +671,26 @@ GLOBAL_LIST_INIT(ballmer_windows_me_msg, list("Yo man, what if, we like, uh, put
/////////////////////////////////////
/mob/living/carbon/proc/can_heartattack()
- if(dna && dna.species && (NOBLOOD in dna.species.species_traits)) //not all carbons have species!
+ if(!needs_heart())
return FALSE
var/obj/item/organ/heart/heart = getorganslot(ORGAN_SLOT_HEART)
if(!heart || heart.synthetic)
return FALSE
return TRUE
-/mob/living/carbon/proc/undergoing_cardiac_arrest()
- if(!can_heartattack())
+/mob/living/carbon/proc/needs_heart()
+ if(has_trait(TRAIT_STABLEHEART))
return FALSE
+ if(dna && dna.species && (NOBLOOD in dna.species.species_traits)) //not all carbons have species!
+ return FALSE
+ return TRUE
+
+/mob/living/carbon/proc/undergoing_cardiac_arrest()
var/obj/item/organ/heart/heart = getorganslot(ORGAN_SLOT_HEART)
if(istype(heart) && heart.beating)
return FALSE
+ else if(!needs_heart())
+ return FALSE
return TRUE
/mob/living/carbon/proc/set_heartattack(status)
diff --git a/code/modules/mob/living/carbon/monkey/combat.dm b/code/modules/mob/living/carbon/monkey/combat.dm
index 303057d376..901fb48b20 100644
--- a/code/modules/mob/living/carbon/monkey/combat.dm
+++ b/code/modules/mob/living/carbon/monkey/combat.dm
@@ -56,7 +56,7 @@
return 1
if(IsUnconscious())
return 1
- if(IsStun())
+ if(IsStun() || IsKnockdown())
return 1
if(stat)
return 1
@@ -397,7 +397,7 @@
/mob/living/carbon/monkey/bullet_act(obj/item/projectile/Proj)
if(istype(Proj , /obj/item/projectile/beam)||istype(Proj, /obj/item/projectile/bullet))
if((Proj.damage_type == BURN) || (Proj.damage_type == BRUTE))
- if(!Proj.nodamage && Proj.damage < src.health)
+ if(!Proj.nodamage && Proj.damage < src.health && isliving(Proj.firer))
retaliate(Proj.firer)
..()
diff --git a/code/modules/mob/living/carbon/monkey/life.dm b/code/modules/mob/living/carbon/monkey/life.dm
index 1ddea78bf2..55b5d67c24 100644
--- a/code/modules/mob/living/carbon/monkey/life.dm
+++ b/code/modules/mob/living/carbon/monkey/life.dm
@@ -12,8 +12,8 @@
if(..())
if(!client)
- if(stat == CONSCIOUS)
- if(on_fire || buckled || restrained() || (resting && canmove)) //CIT CHANGE - makes it so monkeys attempt to resist if they're resting
+ if(stat == CONSCIOUS)
+ if(on_fire || buckled || restrained() || (resting && canmove)) //CIT CHANGE - makes it so monkeys attempt to resist if they're resting)
if(!resisting && prob(MONKEY_RESIST_PROB))
resisting = TRUE
walk_to(src,0)
diff --git a/code/modules/mob/living/carbon/monkey/monkey.dm b/code/modules/mob/living/carbon/monkey/monkey.dm
index 19592f7b63..55ecebda8c 100644
--- a/code/modules/mob/living/carbon/monkey/monkey.dm
+++ b/code/modules/mob/living/carbon/monkey/monkey.dm
@@ -14,8 +14,7 @@
unique_name = TRUE
bodyparts = list(/obj/item/bodypart/chest/monkey, /obj/item/bodypart/head/monkey, /obj/item/bodypart/l_arm/monkey,
/obj/item/bodypart/r_arm/monkey, /obj/item/bodypart/r_leg/monkey, /obj/item/bodypart/l_leg/monkey)
-
-
+ hud_type = /datum/hud/monkey
/mob/living/carbon/monkey/Initialize(mapload, cubespawned=FALSE, mob/spawner)
verbs += /mob/living/proc/mob_sleep
@@ -27,7 +26,6 @@
//initialize limbs
create_bodyparts()
-
create_internal_organs()
. = ..()
@@ -59,26 +57,31 @@
internal_organs += new /obj/item/organ/stomach
..()
-/mob/living/carbon/monkey/movement_delay()
- if(reagents)
- if(reagents.has_reagent("morphine"))
- return -1
-
- if(reagents.has_reagent("nuka_cola"))
- return -1
-
+/mob/living/carbon/monkey/on_reagent_change()
. = ..()
+ remove_movespeed_modifier(MOVESPEED_ID_MONKEY_REAGENT_SPEEDMOD, TRUE)
+ var/amount
+ if(reagents.has_reagent("morphine"))
+ amount = -1
+ if(reagents.has_reagent("nuka_cola"))
+ amount = -1
+ if(amount)
+ add_movespeed_modifier(MOVESPEED_ID_MONKEY_REAGENT_SPEEDMOD, TRUE, 100, override = TRUE, multiplicative_slowdown = amount)
+
+/mob/living/carbon/monkey/updatehealth()
+ . = ..()
+ var/slow = 0
var/health_deficiency = (100 - health)
if(health_deficiency >= 45)
- . += (health_deficiency / 25)
+ slow += (health_deficiency / 25)
+ add_movespeed_modifier(MOVESPEED_ID_MONKEY_HEALTH_SPEEDMOD, TRUE, 100, override = TRUE, multiplicative_slowdown = slow)
+/mob/living/carbon/monkey/adjust_bodytemperature(amount)
+ . = ..()
+ var/slow = 0
if (bodytemperature < 283.222)
- . += (283.222 - bodytemperature) / 10 * 1.75
-
- var/static/config_monkey_delay
- if(isnull(config_monkey_delay))
- config_monkey_delay = CONFIG_GET(number/monkey_delay)
- . += config_monkey_delay
+ slow += (283.222 - bodytemperature) / 10 * 1.75
+ add_movespeed_modifier(MOVESPEED_ID_MONKEY_TEMPERATURE_SPEEDMOD, TRUE, 100, override = TRUE, multiplicative_slowdown = amount)
/mob/living/carbon/monkey/Stat()
..()
diff --git a/code/modules/mob/living/carbon/monkey/monkey_defense.dm b/code/modules/mob/living/carbon/monkey/monkey_defense.dm
index f6b8e19468..df90dd56fd 100644
--- a/code/modules/mob/living/carbon/monkey/monkey_defense.dm
+++ b/code/modules/mob/living/carbon/monkey/monkey_defense.dm
@@ -54,7 +54,7 @@
if(!affecting)
affecting = get_bodypart(BODY_ZONE_CHEST)
apply_damage(damage, BRUTE, affecting)
- add_logs(M, src, "attacked")
+ log_combat(M, src, "attacked")
else
playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
@@ -66,7 +66,7 @@
if (prob(25))
Knockdown(40)
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
- add_logs(M, src, "pushed")
+ log_combat(M, src, "pushed")
visible_message("
[M] has pushed down [src]! ", \
"
[M] has pushed down [src]! ", null, COMBAT_MESSAGE_RANGE)
else if(dropItemToGround(get_active_held_item()))
@@ -90,7 +90,7 @@
"
[M] has slashed [name]! ", null, COMBAT_MESSAGE_RANGE)
var/obj/item/bodypart/affecting = get_bodypart(ran_zone(M.zone_selected))
- add_logs(M, src, "attacked")
+ log_combat(M, src, "attacked")
if(!affecting)
affecting = get_bodypart(BODY_ZONE_CHEST)
if(!dismembering_strike(M, affecting.body_zone)) //Dismemberment successful
@@ -115,7 +115,7 @@
visible_message("
[M] has disarmed [name]! ", "
[M] has disarmed [name]! ", null, COMBAT_MESSAGE_RANGE)
else
I = null
- add_logs(M, src, "disarmed", "[I ? " removing \the [I]" : ""]")
+ log_combat(M, src, "disarmed", "[I ? " removing \the [I]" : ""]")
updatehealth()
diff --git a/code/modules/mob/living/carbon/say.dm b/code/modules/mob/living/carbon/say.dm
index d4a222abdc..f6e43f487f 100644
--- a/code/modules/mob/living/carbon/say.dm
+++ b/code/modules/mob/living/carbon/say.dm
@@ -45,4 +45,4 @@
return
for(var/T in get_traumas())
var/datum/brain_trauma/trauma = T
- message = trauma.on_hear(message, speaker, message_language, raw_message, radio_freq)
+ message = trauma.on_hear(message, speaker, message_language, raw_message, radio_freq)
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/status_procs.dm b/code/modules/mob/living/carbon/status_procs.dm
index d850efcee6..766bd376b1 100644
--- a/code/modules/mob/living/carbon/status_procs.dm
+++ b/code/modules/mob/living/carbon/status_procs.dm
@@ -45,7 +45,7 @@
if(druggy)
overlay_fullscreen("high", /obj/screen/fullscreen/high)
throw_alert("high", /obj/screen/alert/high)
- SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "high", /datum/mood_event/drugs/high)
+ SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "high", /datum/mood_event/high)
else
clear_fullscreen("high")
clear_alert("high")
diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm
index bb16aeb7bf..f6888a21ec 100644
--- a/code/modules/mob/living/death.dm
+++ b/code/modules/mob/living/death.dm
@@ -26,14 +26,14 @@
/mob/living/proc/spread_bodyparts()
return
-/mob/living/dust(just_ash = FALSE, drop_items = FALSE)
+/mob/living/dust(just_ash, drop_items, force)
death(TRUE)
if(drop_items)
unequip_everything()
if(buckled)
- buckled.unbuckle_mob(src,force=1)
+ buckled.unbuckle_mob(src, force = TRUE)
dust_animation()
spawn_dust(just_ash)
@@ -74,9 +74,12 @@
update_canmove()
med_hud_set_health()
med_hud_set_status()
- addtimer(CALLBACK(src, .proc/med_hud_set_status), (DEFIB_TIME_LIMIT * 10) + 1)
+ if(!gibbed && !QDELETED(src))
+ addtimer(CALLBACK(src, .proc/med_hud_set_status), (DEFIB_TIME_LIMIT * 10) + 1)
stop_pulling()
+
SEND_SIGNAL(src, COMSIG_MOB_DEATH, gibbed)
+
if (client)
client.move_delay = initial(client.move_delay)
diff --git a/code/modules/mob/living/emote.dm b/code/modules/mob/living/emote.dm
index 458c19edfb..320b75b052 100644
--- a/code/modules/mob/living/emote.dm
+++ b/code/modules/mob/living/emote.dm
@@ -1,7 +1,7 @@
/* EMOTE DATUMS */
/datum/emote/living
- mob_type_allowed_typecache = list(/mob/living)
+ mob_type_allowed_typecache = /mob/living
mob_type_blacklist_typecache = list(/mob/living/simple_animal/slime, /mob/living/brain)
/datum/emote/living/blush
@@ -60,7 +60,7 @@
/datum/emote/living/cough/can_run_emote(mob/user, status_check = TRUE)
. = ..()
- if(user.reagents.get_reagent("menthol") || user.reagents.get_reagent("peppermint_patty"))
+ if(user.reagents && (user.reagents.get_reagent("menthol") || user.reagents.get_reagent("peppermint_patty")))
return FALSE
/datum/emote/living/dance
diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm
index 40b11c14ce..c55252ddb6 100644
--- a/code/modules/mob/living/life.dm
+++ b/code/modules/mob/living/life.dm
@@ -43,7 +43,10 @@
//Breathing, if applicable
handle_breathing(times_fired)
- handle_diseases() // DEAD check is in the proc itself; we want it to spread even if the mob is dead, but to handle its disease-y properties only if you're not.
+ handle_diseases()// DEAD check is in the proc itself; we want it to spread even if the mob is dead, but to handle its disease-y properties only if you're not.
+
+ if (QDELETED(src)) // diseases can qdel the mob via transformations
+ return
if(stat != DEAD)
//Random events (vomiting etc)
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 7647f9664e..684484ed68 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -9,6 +9,10 @@
diag_hud.add_to_hud(src)
faction += "[REF(src)]"
GLOB.mob_living_list += src
+ initialize_footstep()
+
+/mob/living/proc/initialize_footstep()
+ AddComponent(/datum/component/footstep)
/mob/living/prepare_huds()
..()
@@ -39,31 +43,31 @@
/mob/living/proc/OpenCraftingMenu()
return
-//Generic Collide(). Override MobCollide() and ObjCollide() instead of this.
-/mob/living/Collide(atom/A)
+//Generic Bump(). Override MobBump() and ObjBump() instead of this.
+/mob/living/Bump(atom/A)
if(..()) //we are thrown onto something
return
if (buckled || now_pushing)
return
if(ismob(A))
var/mob/M = A
- if(MobCollide(M))
+ if(MobBump(M))
return
if(isobj(A))
var/obj/O = A
- if(ObjCollide(O))
+ if(ObjBump(O))
return
if(ismovableatom(A))
var/atom/movable/AM = A
if(PushAM(AM))
return
-/mob/living/CollidedWith(atom/movable/AM)
+/mob/living/Bumped(atom/movable/AM)
..()
last_bumped = world.time
//Called when we bump onto a mob
-/mob/living/proc/MobCollide(mob/M)
+/mob/living/proc/MobBump(mob/M)
//Even if we don't push/swap places, we "touched" them, so spread fire
spreadFire(M)
@@ -96,6 +100,7 @@
if(!(world.time % 5))
to_chat(src, "
[L] is restraining [P], you cannot push past. ")
return 1
+
//CIT CHANGES START HERE - makes it so resting stops you from moving through standing folks without a short delay
if(resting && !L.resting)
if(attemptingcrawl)
@@ -116,6 +121,7 @@
attemptingcrawl = FALSE
return TRUE
//END OF CIT CHANGES
+
if(moving_diagonally)//no mob swap during diagonal moves.
return 1
@@ -170,8 +176,24 @@
if(prob(I.block_chance*2))
return 1
+/mob/living/get_photo_description(obj/item/camera/camera)
+ var/list/mob_details = list()
+ var/list/holding = list()
+ var/len = length(held_items)
+ if(len)
+ for(var/obj/item/I in held_items)
+ if(!holding.len)
+ holding += "They are holding \a [I]"
+ else if(held_items.Find(I) == len)
+ holding += ", and \a [I]."
+ else
+ holding += ", \a [I]"
+ holding += "."
+ mob_details += "You can also see [src] on the photo[health < (maxHealth * 0.75) ? ", looking a bit hurt":""][holding ? ". [holding.Join("")]":"."]."
+ return mob_details.Join("")
+
//Called when we bump onto an obj
-/mob/living/proc/ObjCollide(obj/O)
+/mob/living/proc/ObjBump(obj/O)
return
//Called when we want to push an atom/movable
@@ -223,7 +245,7 @@
if(AM.pulledby)
if(!supress_message)
visible_message("
[src] has pulled [AM] from [AM.pulledby]'s grip. ")
- add_logs(AM, AM.pulledby, "pulled from", src)
+ log_combat(AM, AM.pulledby, "pulled from", src)
AM.pulledby.stop_pulling() //an object can't be pulled by two mobs at once.
pulling = AM
@@ -235,7 +257,7 @@
if(ismob(AM))
var/mob/M = AM
- add_logs(src, M, "grabbed", addition="passive grab")
+ log_combat(src, M, "grabbed", addition="passive grab")
if(!supress_message)
visible_message("
[src] has grabbed [M][(zone_selected == "l_arm" || zone_selected == "r_arm")? " by their hands":" passively"]! ") //Cit change - And they thought ERP was bad.
if(!iscarbon(src))
@@ -279,7 +301,7 @@
/mob/living/pointed(atom/A as mob|obj|turf in view())
if(incapacitated())
return FALSE
- if(has_trait(TRAIT_FAKEDEATH))
+ if(has_trait(TRAIT_DEATHCOMA))
return FALSE
if(!..())
return FALSE
@@ -289,7 +311,7 @@
/mob/living/verb/succumb(whispered as null)
set hidden = TRUE
if (InCritical())
- log_message("Has [whispered ? "whispered his final words" : "succumbed to death"] while in [InFullCritical() ? "hard":"soft"] critical with [round(health, 0.1)] points of health!", INDIVIDUAL_ATTACK_LOG)
+ log_message("Has [whispered ? "whispered his final words" : "succumbed to death"] while in [InFullCritical() ? "hard":"soft"] critical with [round(health, 0.1)] points of health!", LOG_ATTACK)
adjustOxyLoss(health - HEALTH_THRESHOLD_DEAD)
updatehealth()
if(!whispered)
@@ -301,7 +323,7 @@
return TRUE
/mob/living/proc/InCritical()
- return (health <= HEALTH_THRESHOLD_CRIT && (stat == SOFT_CRIT || stat == UNCONSCIOUS))
+ return (health <= crit_threshold && (stat == SOFT_CRIT || stat == UNCONSCIOUS))
/mob/living/proc/InFullCritical()
return (health <= HEALTH_THRESHOLD_FULLCRIT && stat == UNCONSCIOUS)
@@ -363,6 +385,7 @@
to_chat(src, "
You are now [resting ? "resting" : "getting up"]. ")
update_canmove()
*/
+
//Recursive function to find everything a mob is holding. Really shitty proc tbh.
/mob/living/get_contents()
var/list/ret = list()
@@ -487,27 +510,6 @@
if(lying && !buckled && prob(getBruteLoss()*200/maxHealth))
makeTrail(newloc, T, old_direction)
-/mob/living/movement_delay(ignorewalk = 0)
- . = 0
- if(isopenturf(loc) && !is_flying())
- var/turf/open/T = loc
- . += T.slowdown
- var/static/datum/config_entry/number/run_delay/config_run_delay
- var/static/datum/config_entry/number/walk_delay/config_walk_delay
- if(isnull(config_run_delay))
- config_run_delay = CONFIG_GET(number/run_delay)
- config_walk_delay = CONFIG_GET(number/walk_delay)
- if(ignorewalk)
- . += config_run_delay.value_cache
- else
- switch(m_intent)
- if(MOVE_INTENT_RUN)
- if(drowsyness > 0)
- . += 6
- . += config_run_delay.value_cache
- if(MOVE_INTENT_WALK)
- . += config_walk_delay.value_cache
-
/mob/living/proc/makeTrail(turf/target_turf, turf/start, direction)
if(!has_gravity())
return
@@ -594,8 +596,8 @@
//resisting grabs (as if it helps anyone...)
if(!restrained(ignore_grab = 1) && pulledby)
visible_message("
[src] resists against [pulledby]'s grip! ")
+ log_combat(src, pulledby, "resisted grab")
resist_grab()
- add_logs(pulledby, src, "resisted grab")
return
//unbuckling yourself
@@ -631,7 +633,7 @@
if(pulledby.grab_state)
if(prob(30/pulledby.grab_state))
visible_message("
[src] has broken free of [pulledby]'s grip! ")
- add_logs(pulledby, src, "broke grab")
+ log_combat(pulledby, src, "broke grab")
pulledby.stop_pulling()
return 0
if(moving_resist && client) //we resisted by trying to move
@@ -698,10 +700,10 @@
var/list/L = where
if(what == who.get_item_for_held_index(L[2]))
if(who.dropItemToGround(what))
- add_logs(src, who, "stripped [what] off")
+ log_combat(src, who, "stripped [what] off")
if(what == who.get_item_by_slot(where))
if(who.dropItemToGround(what))
- add_logs(src, who, "stripped [what] off")
+ log_combat(src, who, "stripped [what] off")
// The src mob is trying to place an item on someone
// Override if a certain mob should be behave differently when placing items (can't, for example)
@@ -822,16 +824,6 @@
/mob/living/proc/update_stamina()
return
-/*
-/mob/living/carbon/update_stamina()
- var/stam = getStaminaLoss()
- if(stam)
- var/total_health = (health - stam)
- if(total_health <= HEALTH_THRESHOLD_CRIT && !stat)
- to_chat(src, "
You're too exhausted to keep going... ")
- Knockdown(100)
- setStaminaLoss(health - 2, FALSE, FALSE)
- update_health_hud() */ //CITADEL OVERRIDE
/mob/living/carbon/alien/update_stamina()
return
@@ -944,7 +936,7 @@
ExtinguishMob()
//Share fire evenly between the two mobs
-//Called in MobCollide() and Crossed()
+//Called in MobBump() and Crossed()
/mob/living/proc/spreadFire(mob/living/L)
if(!istype(L))
return
@@ -986,7 +978,7 @@
//Updates canmove, lying and icons. Could perhaps do with a rename but I can't think of anything to describe it.
//Robots, animals and brains have their own version so don't worry about them
/mob/living/proc/update_canmove()
- var/ko = IsKnockdown() || IsUnconscious() || (stat && (stat != SOFT_CRIT || pulledby)) || (has_trait(TRAIT_FAKEDEATH))
+ var/ko = IsKnockdown() || IsUnconscious() || (stat && (stat != SOFT_CRIT || pulledby)) || (has_trait(TRAIT_DEATHCOMA))
var/move_and_fall = stat == SOFT_CRIT && !pulledby
var/chokehold = pulledby && pulledby.grab_state >= GRAB_NECK
var/buckle_lying = !(buckled && !buckled.buckle_lying)
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index 3b010ed8ec..f5b44db70f 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -88,7 +88,7 @@
var/armor = run_armor_check(zone, "melee", "Your armor has protected your [parse_zone(zone)].", "Your armor has softened hit to your [parse_zone(zone)].",I.armour_penetration)
apply_damage(I.throwforce, dtype, zone, armor)
if(I.thrownby)
- add_logs(I.thrownby, src, "threw and hit", I)
+ log_combat(I.thrownby, src, "threw and hit", I)
else
return 1
else
@@ -116,10 +116,10 @@
updatehealth()
visible_message("
[M.name] has hit [src]! ", \
"
[M.name] has hit [src]! ", null, COMBAT_MESSAGE_RANGE)
- add_logs(M.occupant, src, "attacked", M, "(INTENT: [uppertext(M.occupant.a_intent)]) (DAMTYPE: [uppertext(M.damtype)])")
+ log_combat(M.occupant, src, "attacked", M, "(INTENT: [uppertext(M.occupant.a_intent)]) (DAMTYPE: [uppertext(M.damtype)])")
else
step_away(src,M)
- add_logs(M.occupant, src, "pushed", M)
+ log_combat(M.occupant, src, "pushed", M)
visible_message("
[M] pushes [src] out of the way. ", null, null, 5)
/mob/living/fire_act()
@@ -144,17 +144,21 @@
grippedby(user)
//proc to upgrade a simple pull into a more aggressive grab.
-/mob/living/proc/grippedby(mob/living/carbon/user)
+/mob/living/proc/grippedby(mob/living/carbon/user, instant = FALSE)
if(user.grab_state < GRAB_KILL)
user.changeNext_move(CLICK_CD_GRABBING)
playsound(src.loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
if(user.grab_state) //only the first upgrade is instantaneous
var/old_grab_state = user.grab_state
- var/grab_upgrade_time = 30
+ var/grab_upgrade_time = instant ? 0 : 30
visible_message("
[user] starts to tighten [user.p_their()] grip on [src]! ", \
"
[user] starts to tighten [user.p_their()] grip on you! ")
- add_logs(user, src, "attempted to strangle", addition="grab")
+ switch(user.grab_state)
+ if(GRAB_AGGRESSIVE)
+ log_combat(user, src, "attempted to neck grab", addition="neck grab")
+ if(GRAB_NECK)
+ log_combat(user, src, "attempted to strangle", addition="kill grab")
if(!do_mob(user, src, grab_upgrade_time))
return 0
if(!user.pulling || user.pulling != src || user.grab_state != old_grab_state || user.a_intent != INTENT_GRAB)
@@ -162,20 +166,20 @@
user.grab_state++
switch(user.grab_state)
if(GRAB_AGGRESSIVE)
- add_logs(user, src, "grabbed", addition="aggressive grab")
+ log_combat(user, src, "grabbed", addition="aggressive grab")
visible_message("
[user] has grabbed [src] aggressively! ", \
"
[user] has grabbed [src] aggressively! ")
drop_all_held_items()
stop_pulling()
if(GRAB_NECK)
- add_logs(user, src, "grabbed", addition="neck grab")
+ log_combat(user, src, "grabbed", addition="neck grab")
visible_message("
[user] has grabbed [src] by the neck! ",\
"
[user] has grabbed you by the neck! ")
update_canmove() //we fall down
if(!buckled && !density)
Move(user.loc)
if(GRAB_KILL)
- add_logs(user, src, "strangled", addition="grab")
+ log_combat(user, src, "strangled", addition="kill grab")
visible_message("
[user] is strangling [src]! ", \
"
[user] is strangling you! ")
update_canmove() //we fall down
@@ -199,7 +203,7 @@
return FALSE
if (stat != DEAD)
- add_logs(M, src, "attacked")
+ log_combat(M, src, "attacked")
M.do_attack_animation(src)
visible_message("
The [M.name] glomps [src]! ", \
"
The [M.name] glomps [src]! ", null, COMBAT_MESSAGE_RANGE)
@@ -220,7 +224,7 @@
M.do_attack_animation(src)
visible_message("
\The [M] [M.attacktext] [src]! ", \
"
\The [M] [M.attacktext] [src]! ", null, COMBAT_MESSAGE_RANGE)
- add_logs(M, src, "attacked")
+ log_combat(M, src, "attacked")
return TRUE
@@ -239,7 +243,7 @@
return FALSE
M.do_attack_animation(src, ATTACK_EFFECT_BITE)
if (prob(75))
- add_logs(M, src, "attacked")
+ log_combat(M, src, "attacked")
playsound(loc, 'sound/weapons/bite.ogg', 50, 1, -1)
visible_message("
[M.name] bites [src]! ", \
"
[M.name] bites [src]! ", null, COMBAT_MESSAGE_RANGE)
@@ -262,7 +266,7 @@
L.do_attack_animation(src)
if(prob(90))
- add_logs(L, src, "attacked")
+ log_combat(L, src, "attacked")
visible_message("
[L.name] bites [src]! ", \
"
[L.name] bites [src]! ", null, COMBAT_MESSAGE_RANGE)
playsound(loc, 'sound/weapons/bite.ogg', 50, 1, -1)
@@ -335,7 +339,7 @@
return
if(is_servant_of_ratvar(src) && !stat)
- to_chat(src, "
You resist Nar-Sie's influence... but not all of it. Run! ")
+ to_chat(src, "
You resist Nar'Sie's influence... but not all of it. Run! ")
adjustBruteLoss(35)
if(src && reagents)
reagents.add_reagent("heparin", 5)
diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm
index 5ee9b8f921..0ba5b4c56d 100644
--- a/code/modules/mob/living/living_defines.dm
+++ b/code/modules/mob/living/living_defines.dm
@@ -20,6 +20,7 @@
var/fireloss = 0 //Burn damage caused by being way too hot, too cold or burnt.
var/cloneloss = 0 //Damage caused by being cloned or ejected from the cloner early. slimes also deal cloneloss damage to victims
var/staminaloss = 0 //Stamina damage, or exhaustion. You recover it slowly naturally, and are knocked down if it gets too high. Holodeck and hallucinations deal this.
+ var/crit_threshold = HEALTH_THRESHOLD_CRIT // when the mob goes from "normal" to crit
var/confused = 0 //Makes the mob move in random directions.
@@ -38,7 +39,7 @@
var/list/surgeries = list() //a list of surgery datums. generally empty, they're added when the player wants them.
- var/now_pushing = null //used by living/Collide() and living/PushAM() to prevent potential infinite loop.
+ var/now_pushing = null //used by living/Bump() and living/PushAM() to prevent potential infinite loop.
var/cameraFollow = null
@@ -101,7 +102,6 @@
var/list/obj/effect/proc_holder/abilities = list()
- var/registered_z
var/can_be_held = FALSE //whether this can be picked up and held.
var/radiation = 0 //If the mob is irradiated.
diff --git a/code/modules/mob/living/living_movement.dm b/code/modules/mob/living/living_movement.dm
new file mode 100644
index 0000000000..9566edc2ed
--- /dev/null
+++ b/code/modules/mob/living/living_movement.dm
@@ -0,0 +1,27 @@
+/mob/living/Moved()
+ . = ..()
+ update_turf_movespeed(loc)
+
+/mob/living/toggle_move_intent()
+ . = ..()
+ update_move_intent_slowdown()
+
+/mob/living/update_config_movespeed()
+ update_move_intent_slowdown()
+ return ..()
+
+/mob/living/proc/update_move_intent_slowdown()
+ var/mod = 0
+ if(m_intent == MOVE_INTENT_WALK)
+ mod = CONFIG_GET(number/movedelay/walk_delay)
+ else
+ mod = CONFIG_GET(number/movedelay/run_delay)
+ if(!isnum(mod))
+ mod = 1
+ add_movespeed_modifier(MOVESPEED_ID_MOB_WALK_RUN_CONFIG_SPEED, TRUE, 100, override = TRUE, multiplicative_slowdown = mod)
+
+/mob/living/proc/update_turf_movespeed(turf/open/T)
+ if(isopenturf(T) && !is_flying())
+ add_movespeed_modifier(MOVESPEED_ID_LIVING_TURF_SPEEDMOD, TRUE, 100, override = TRUE, multiplicative_slowdown = T.slowdown)
+ else
+ remove_movespeed_modifier(MOVESPEED_ID_LIVING_TURF_SPEEDMOD)
diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm
index a7398493b0..c4754599d1 100644
--- a/code/modules/mob/living/say.dm
+++ b/code/modules/mob/living/say.dm
@@ -60,29 +60,28 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
))
/mob/living/proc/Ellipsis(original_msg, chance = 50, keep_words)
- if(chance <= 0)
- return "..."
- if(chance >= 100)
- return original_msg
+ if(chance <= 0)
+ return "..."
+ if(chance >= 100)
+ return original_msg
- var/list
- words = splittext(original_msg," ")
- new_words = list()
+ var/list/words = splittext(original_msg," ")
+ var/list/new_words = list()
- var/new_msg = ""
+ var/new_msg = ""
- for(var/w in words)
- if(prob(chance))
- new_words += "..."
- if(!keep_words)
- continue
- new_words += w
+ for(var/w in words)
+ if(prob(chance))
+ new_words += "..."
+ if(!keep_words)
+ continue
+ new_words += w
- new_msg = jointext(new_words," ")
+ new_msg = jointext(new_words," ")
- return new_msg
+ return new_msg
-/mob/living/say(message, bubble_type,var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE)
+/mob/living/say(message, bubble_type,var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
var/static/list/crit_allowed_modes = list(MODE_WHISPER = TRUE, MODE_CHANGELING = TRUE, MODE_ALIEN = TRUE)
var/static/list/unconscious_allowed_modes = list(MODE_CHANGELING = TRUE, MODE_ALIEN = TRUE)
var/talk_key = get_key(message)
@@ -164,7 +163,7 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
if((InCritical() && !fullcrit) || message_mode == MODE_WHISPER)
message_range = 1
message_mode = MODE_WHISPER
- log_talk(src,"[key_name(src)] : [message]",LOGWHISPER)
+ src.log_talk(message, LOG_WHISPER)
if(fullcrit)
var/health_diff = round(-HEALTH_THRESHOLD_DEAD + health)
// If we cut our message short, abruptly end it with a-..
@@ -175,7 +174,7 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
message_mode = MODE_WHISPER_CRIT
succumbed = TRUE
else
- log_talk(src,"[name]/[key] : [message]",LOGSAY)
+ src.log_talk(message, LOG_SAY, forced_by=forced)
message = treat_message(message)
if(!message)
@@ -187,9 +186,6 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
var/datum/language/L = GLOB.language_datum_instances[language]
spans |= L.spans
- //Log what we've said with an associated timestamp, using the list's len for safety/to prevent overwriting messages
- log_message(message, INDIVIDUAL_SAY_LOG)
-
var/radio_return = radio(message, message_mode, spans, language)
if(radio_return & ITALICS)
spans |= SPAN_ITALICS
@@ -242,11 +238,9 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
eavesdrop_range = EAVESDROP_EXTRA_RANGE
var/list/listening = get_hearers_in_view(message_range+eavesdrop_range, source)
var/list/the_dead = list()
-
var/list/yellareas //CIT CHANGE - adds the ability for yelling to penetrate walls and echo throughout areas
if(say_test(message) == "2") //CIT CHANGE - ditto
yellareas = get_areas_in_range(message_range*0.5,src) //CIT CHANGE - ditto
-
for(var/_M in GLOB.player_list)
var/mob/M = _M
if(M.stat != DEAD) //not dead, not important
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index 91401cbc88..3dbb454655 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -23,6 +23,7 @@
a_intent = INTENT_HARM //so we always get pushed instead of trying to swap
sight = SEE_TURFS | SEE_MOBS | SEE_OBJS
see_in_dark = 8
+ hud_type = /datum/hud/ai
med_hud = DATA_HUD_MEDICAL_BASIC
sec_hud = DATA_HUD_SECURITY_BASIC
d_hud = DATA_HUD_DIAGNOSTIC_ADVANCED
@@ -177,9 +178,11 @@
if(incapacitated())
return
- var/icontype = input("Please, select a display!", "AI", null/*, null*/) in list("Clown", "Monochrome", "Blue", "Inverted", "Firewall", "Green", "Red", "Static", "Red October", "House", "Heartline", "Hades", "Helios", "President", "Syndicat Meow", "Alien", "Too Deep", "Triumvirate", "Triumvirate-M", "Text", "Matrix", "Dorf", "Bliss", "Not Malf", "Fuzzy", "Goon", "Database", "Glitchman", "Murica", "Nanotrasen", "Gentoo", "Angel", "TechDemon") //CIT CHANGE - adds 'TechDemon
+ var/icontype = input("Please, select a display!", "AI", null/*, null*/) in list("Clown", ":thinking:", "Monochrome", "Blue", "Inverted", "Firewall", "Green", "Red", "Static", "Red October", "House", "Heartline", "Hades", "Helios", "President", "Syndicat Meow", "Alien", "Too Deep", "Triumvirate", "Triumvirate-M", "Text", "Matrix", "Dorf", "Bliss", "Not Malf", "Fuzzy", "Goon", "Database", "Glitchman", "Murica", "Nanotrasen", "Gentoo", "Angel", "TechDemon") //CIT CHANGE - adds 'TechDemon
if(icontype == "Clown")
icon_state = "ai-clown2"
+ else if (icontype == ":thinking:")
+ icon_state = "ai-:thinking:"
else if(icontype == "Monochrome")
icon_state = "ai-mono"
else if(icontype == "Blue")
@@ -244,6 +247,7 @@
icon_state = "ai-angel"
else if(icontype == "TechDemon") //CIT CHANGE - adds 'TechDemon
icon_state = "ai-techdemon"
+
/mob/living/silicon/ai/Stat()
..()
if(statpanel("Status"))
@@ -328,7 +332,16 @@
/mob/living/silicon/ai/can_interact_with(atom/A)
. = ..()
- return . || (istype(loc, /obj/item/aicard))? (ISINRANGE(A.x, x - interaction_range, x + interaction_range) && ISINRANGE(A.y, y - interaction_range, y + interaction_range)): GLOB.cameranet.checkTurfVis(get_turf(A))
+ if (.)
+ return
+ if (istype(loc, /obj/item/aicard))
+ var/turf/T0 = get_turf(src)
+ var/turf/T1 = get_turf(A)
+ if (!T0 || ! T1)
+ return FALSE
+ return ISINRANGE(T1.x, T0.x - interaction_range, T0.x + interaction_range) && ISINRANGE(T1.y, T0.y - interaction_range, T0.y + interaction_range)
+ else
+ return GLOB.cameranet.checkTurfVis(get_turf(A))
/mob/living/silicon/ai/cancel_camera()
view_core()
@@ -620,19 +633,20 @@
if(incapacitated())
return
- var/list/ai_emotions = list("Very Happy", "Happy", "Neutral", "Unsure", "Confused", "Sad", "BSOD", "Blank", "Problems?", "Awesome", "Facepalm", "Friend Computer", "Dorfy", "Blue Glow", "Red Glow")
+ var/list/ai_emotions = list("Very Happy", "Happy", "Neutral", "Unsure", "Confused", "Sad", "BSOD", "Blank", "Problems?", "Awesome", "Facepalm", "Thinking", "Friend Computer", "Dorfy", "Blue Glow", "Red Glow")
var/emote = input("Please, select a status!", "AI Status", null, null) in ai_emotions
- for (var/M in GLOB.ai_status_displays) //change status of displays
- if(istype(M, /obj/machinery/ai_status_display))
- var/obj/machinery/ai_status_display/AISD = M
- AISD.emotion = emote
- //if Friend Computer, change ALL displays
- else if(istype(M, /obj/machinery/status_display))
- var/obj/machinery/status_display/SD = M
- if(emote=="Friend Computer")
- SD.friendc = 1
- else
- SD.friendc = 0
+ for (var/each in GLOB.ai_status_displays) //change status of displays
+ var/obj/machinery/status_display/ai/M = each
+ M.emotion = emote
+ M.update()
+ if (emote == "Friend Computer")
+ var/datum/radio_frequency/frequency = SSradio.return_frequency(FREQ_STATUS_DISPLAYS)
+
+ if(!frequency)
+ return
+
+ var/datum/signal/status_signal = new(list("command" = "friendcomputer"))
+ frequency.post_signal(src, status_signal)
return
//I am the icon meister. Bow fefore me. //>fefore
@@ -1004,7 +1018,7 @@
target_ai = src //cheat! just give... ourselves as the spawned AI, because that's technically correct
/mob/living/silicon/ai/proc/camera_visibility(mob/camera/aiEye/moved_eye)
- GLOB.cameranet.visibility(moved_eye, client, all_eyes)
+ GLOB.cameranet.visibility(moved_eye, client, all_eyes, USE_STATIC_OPAQUE)
/mob/living/silicon/ai/forceMove(atom/destination)
. = ..()
diff --git a/code/modules/mob/living/silicon/ai/death.dm b/code/modules/mob/living/silicon/ai/death.dm
index e28d9d84df..9b982d4bd5 100644
--- a/code/modules/mob/living/silicon/ai/death.dm
+++ b/code/modules/mob/living/silicon/ai/death.dm
@@ -25,11 +25,16 @@
spawn(10)
explosion(src.loc, 3, 6, 12, 15)
- for(var/obj/machinery/ai_status_display/O in GLOB.ai_status_displays) //change status
- if(src.key)
+ if(src.key)
+ for(var/each in GLOB.ai_status_displays) //change status
+ var/obj/machinery/status_display/ai/O = each
O.mode = 2
- if(istype(loc, /obj/item/aicard))
- loc.icon_state = "aicard-404"
+ O.update()
+
+ if(istype(loc, /obj/item/aicard/aitater))
+ loc.icon_state = "aitater-404"
+ else if(istype(loc, /obj/item/aicard))
+ loc.icon_state = "aicard-404"
/mob/living/silicon/ai/proc/ShutOffDoomsdayDevice()
if(nuking)
diff --git a/code/modules/mob/living/silicon/ai/freelook/cameranet.dm b/code/modules/mob/living/silicon/ai/freelook/cameranet.dm
index 36ae19b478..dfe76fe948 100644
--- a/code/modules/mob/living/silicon/ai/freelook/cameranet.dm
+++ b/code/modules/mob/living/silicon/ai/freelook/cameranet.dm
@@ -18,15 +18,24 @@ GLOBAL_DATUM_INIT(cameranet, /datum/cameranet, new)
// The object used for the clickable stat() button.
var/obj/effect/statclick/statclick
- // The object used in vis_contents of obscured turfs
- var/vis_contents
+ // The objects used in vis_contents of obscured turfs
+ var/list/vis_contents_objects
+ var/obj/effect/overlay/camera_static/vis_contents_opaque
+ var/obj/effect/overlay/camera_static/vis_contents_transparent
// The image given to the effect in vis_contents on AI clients
var/image/obscured
+ var/image/obscured_transparent
/datum/cameranet/New()
- vis_contents = new /obj/effect/overlay/camera_static()
- obscured = new('icons/effects/cameravis.dmi', vis_contents, null, BYOND_LIGHTING_LAYER + 0.1)
- obscured.plane = BYOND_LIGHTING_PLANE + 1
+ vis_contents_opaque = new /obj/effect/overlay/camera_static()
+ vis_contents_transparent = new /obj/effect/overlay/camera_static/transparent()
+ vis_contents_objects = list(vis_contents_opaque, vis_contents_transparent)
+
+ obscured = new('icons/effects/cameravis.dmi', vis_contents_opaque, null, CAMERA_STATIC_LAYER)
+ obscured.plane = CAMERA_STATIC_PLANE
+
+ obscured_transparent = new('icons/effects/cameravis.dmi', vis_contents_transparent, null, CAMERA_STATIC_LAYER)
+ obscured_transparent.plane = CAMERA_STATIC_PLANE
// Checks if a chunk has been Generated in x, y, z.
/datum/cameranet/proc/chunkGenerated(x, y, z)
@@ -46,7 +55,7 @@ GLOBAL_DATUM_INIT(cameranet, /datum/cameranet, new)
// Updates what the aiEye can see. It is recommended you use this when the aiEye moves or it's location is set.
-/datum/cameranet/proc/visibility(list/moved_eyes, client/C, list/other_eyes)
+/datum/cameranet/proc/visibility(list/moved_eyes, client/C, list/other_eyes, use_static = USE_STATIC_OPAQUE)
if(!islist(moved_eyes))
moved_eyes = moved_eyes ? list(moved_eyes) : list()
if(islist(other_eyes))
@@ -54,33 +63,48 @@ GLOBAL_DATUM_INIT(cameranet, /datum/cameranet, new)
else
other_eyes = list()
+ if(C)
+ switch(use_static)
+ if(USE_STATIC_TRANSPARENT)
+ C.images += obscured_transparent
+ if(USE_STATIC_OPAQUE)
+ C.images += obscured
+
for(var/V in moved_eyes)
var/mob/camera/aiEye/eye = V
- var/static_range = eye.static_visibility_range
- var/x1 = max(0, eye.x - static_range) & ~(CHUNK_SIZE - 1)
- var/y1 = max(0, eye.y - static_range) & ~(CHUNK_SIZE - 1)
- var/x2 = min(world.maxx, eye.x + static_range) & ~(CHUNK_SIZE - 1)
- var/y2 = min(world.maxy, eye.y + static_range) & ~(CHUNK_SIZE - 1)
-
var/list/visibleChunks = list()
+ if(eye.loc)
+ // 0xf = 15
+ var/static_range = eye.static_visibility_range
+ var/x1 = max(0, eye.x - static_range) & ~(CHUNK_SIZE - 1)
+ var/y1 = max(0, eye.y - static_range) & ~(CHUNK_SIZE - 1)
+ var/x2 = min(world.maxx, eye.x + static_range) & ~(CHUNK_SIZE - 1)
+ var/y2 = min(world.maxy, eye.y + static_range) & ~(CHUNK_SIZE - 1)
- for(var/x = x1; x <= x2; x += CHUNK_SIZE)
- for(var/y = y1; y <= y2; y += CHUNK_SIZE)
- visibleChunks |= getCameraChunk(x, y, eye.z)
+
+ for(var/x = x1; x <= x2; x += CHUNK_SIZE)
+ for(var/y = y1; y <= y2; y += CHUNK_SIZE)
+ visibleChunks |= getCameraChunk(x, y, eye.z)
var/list/remove = eye.visibleCameraChunks - visibleChunks
var/list/add = visibleChunks - eye.visibleCameraChunks
for(var/chunk in remove)
var/datum/camerachunk/c = chunk
- c.remove(eye)
+ c.remove(eye, FALSE)
for(var/chunk in add)
var/datum/camerachunk/c = chunk
c.add(eye)
- if(C)
- C.images += obscured
+ if(!eye.visibleCameraChunks.len)
+ var/client/client = eye.GetViewerClient()
+ if(client)
+ switch(eye.use_static)
+ if(USE_STATIC_TRANSPARENT)
+ client.images -= GLOB.cameranet.obscured_transparent
+ if(USE_STATIC_OPAQUE)
+ client.images -= GLOB.cameranet.obscured
// Updates the chunks that the turf is located in. Use this when obstacles are destroyed or when doors open.
@@ -173,5 +197,8 @@ GLOBAL_DATUM_INIT(cameranet, /datum/cameranet, new)
mouse_opacity = MOUSE_OPACITY_ICON
invisibility = INVISIBILITY_ABSTRACT
- layer = BYOND_LIGHTING_LAYER + 0.1
- plane = BYOND_LIGHTING_PLANE + 1
+ layer = CAMERA_STATIC_LAYER
+ plane = CAMERA_STATIC_PLANE
+
+/obj/effect/overlay/camera_static/transparent
+ mouse_opacity = MOUSE_OPACITY_TRANSPARENT
diff --git a/code/modules/mob/living/silicon/ai/freelook/chunk.dm b/code/modules/mob/living/silicon/ai/freelook/chunk.dm
index 9a84616fbf..c2794394ed 100644
--- a/code/modules/mob/living/silicon/ai/freelook/chunk.dm
+++ b/code/modules/mob/living/silicon/ai/freelook/chunk.dm
@@ -26,9 +26,17 @@
// Remove an AI eye from the chunk, then update if changed.
-/datum/camerachunk/proc/remove(mob/camera/aiEye/eye)
+/datum/camerachunk/proc/remove(mob/camera/aiEye/eye, remove_static_with_last_chunk = TRUE)
eye.visibleCameraChunks -= src
seenby -= eye
+ if(remove_static_with_last_chunk && !eye.visibleCameraChunks.len)
+ var/client/client = eye.GetViewerClient()
+ if(client)
+ switch(eye.use_static)
+ if(USE_STATIC_TRANSPARENT)
+ client.images -= GLOB.cameranet.obscured_transparent
+ if(USE_STATIC_OPAQUE)
+ client.images -= GLOB.cameranet.obscured
// Called when a chunk has changed. I.E: A wall was deleted.
@@ -81,12 +89,12 @@
for(var/turf in visAdded)
var/turf/t = turf
- t.vis_contents -= GLOB.cameranet.vis_contents
+ t.vis_contents -= GLOB.cameranet.vis_contents_objects
for(var/turf in visRemoved)
var/turf/t = turf
if(obscuredTurfs[t] && !istype(t, /turf/open/ai_visible))
- t.vis_contents += GLOB.cameranet.vis_contents
+ t.vis_contents += GLOB.cameranet.vis_contents_objects
changed = 0
@@ -128,7 +136,7 @@
for(var/turf in obscuredTurfs)
var/turf/t = turf
- t.vis_contents += GLOB.cameranet.vis_contents
+ t.vis_contents += GLOB.cameranet.vis_contents_objects
#undef UPDATE_BUFFER
#undef CHUNK_SIZE
diff --git a/code/modules/mob/living/silicon/ai/freelook/eye.dm b/code/modules/mob/living/silicon/ai/freelook/eye.dm
index e78c008362..2d617355ec 100644
--- a/code/modules/mob/living/silicon/ai/freelook/eye.dm
+++ b/code/modules/mob/living/silicon/ai/freelook/eye.dm
@@ -7,28 +7,81 @@
name = "Inactive AI Eye"
invisibility = INVISIBILITY_MAXIMUM
+ hud_possible = list(ANTAG_HUD, AI_DETECT_HUD = HUD_LIST_LIST)
var/list/visibleCameraChunks = list()
var/mob/living/silicon/ai/ai = null
var/relay_speech = FALSE
- var/use_static = TRUE
+ var/use_static = USE_STATIC_OPAQUE
var/static_visibility_range = 16
+ var/ai_detector_visible = TRUE
+ var/ai_detector_color = COLOR_RED
+
+/mob/camera/aiEye/Initialize()
+ . = ..()
+ GLOB.aiEyes += src
+ update_ai_detect_hud()
+ setLoc(loc, TRUE)
+
+/mob/camera/aiEye/proc/update_ai_detect_hud()
+ var/datum/atom_hud/ai_detector/hud = GLOB.huds[DATA_HUD_AI_DETECT]
+ var/list/old_images = hud_list[AI_DETECT_HUD]
+ if(!ai_detector_visible)
+ hud.remove_from_hud(src)
+ QDEL_LIST(old_images)
+ return
+
+ if(!hud.hudusers.len)
+ //no one is watching, do not bother updating anything
+ return
+ hud.remove_from_hud(src)
+
+ var/static/list/vis_contents_objects = list()
+ var/obj/effect/overlay/ai_detect_hud/hud_obj = vis_contents_objects[ai_detector_color]
+ if(!hud_obj)
+ hud_obj = new /obj/effect/overlay/ai_detect_hud()
+ hud_obj.color = ai_detector_color
+ vis_contents_objects[ai_detector_color] = hud_obj
+
+ var/list/new_images = list()
+ var/list/turfs = get_visible_turfs()
+ for(var/T in turfs)
+ var/image/I = (old_images.len > new_images.len) ? old_images[new_images.len + 1] : image(null, T)
+ I.loc = T
+ I.vis_contents += hud_obj
+ new_images += I
+ for(var/i in (new_images.len + 1) to old_images.len)
+ qdel(old_images[i])
+ hud_list[AI_DETECT_HUD] = new_images
+ hud.add_to_hud(src)
+
+/mob/camera/aiEye/proc/get_visible_turfs()
+ if(!isturf(loc))
+ return list()
+ var/client/C = GetViewerClient()
+ var/view = C ? getviewsize(C.view) : getviewsize(world.view)
+ var/turf/lowerleft = locate(max(1, x - (view[1] - 1)/2), max(1, y - (view[2] - 1)/2), z)
+ var/turf/upperright = locate(min(world.maxx, lowerleft.x + (view[1] - 1)), min(world.maxy, lowerleft.y + (view[2] - 1)), lowerleft.z)
+ return block(lowerleft, upperright)
// Use this when setting the aiEye's location.
// It will also stream the chunk that the new loc is in.
-/mob/camera/aiEye/proc/setLoc(T)
+/mob/camera/aiEye/proc/setLoc(T, force_update = FALSE)
if(ai)
if(!isturf(ai.loc))
return
T = get_turf(T)
+ if(!force_update && (T == get_turf(src)) )
+ return //we are already here!
if (T)
forceMove(T)
else
- moveToNullspace() // ????
- if(use_static)
+ moveToNullspace()
+ if(use_static != USE_STATIC_NONE)
ai.camera_visibility(src)
if(ai.client && !ai.multicam_on)
ai.client.eye = src
+ update_ai_detect_hud()
update_parallax_contents()
//Holopad
if(istype(ai.current, /obj/machinery/holopad))
@@ -47,11 +100,6 @@
return ai.client
return null
-/mob/camera/aiEye/proc/RemoveImages()
- var/client/C = GetViewerClient()
- if(C && use_static)
- C.images -= GLOB.cameranet.obscured
-
/mob/camera/aiEye/Destroy()
if(ai)
ai.all_eyes -= src
@@ -59,6 +107,12 @@
for(var/V in visibleCameraChunks)
var/datum/camerachunk/c = V
c.remove(src)
+ GLOB.aiEyes -= src
+ if(ai_detector_visible)
+ var/datum/atom_hud/ai_detector/hud = GLOB.huds[DATA_HUD_AI_DETECT]
+ hud.remove_from_hud(src)
+ var/list/L = hud_list[AI_DETECT_HUD]
+ QDEL_LIST(L)
return ..()
/atom/proc/move_camera_by_click()
@@ -132,3 +186,12 @@
. = ..()
if(relay_speech && speaker && ai && !radio_freq && speaker != ai && near_camera(speaker))
ai.relay_speech(message, speaker, message_language, raw_message, radio_freq, spans, message_mode)
+
+/obj/effect/overlay/ai_detect_hud
+ name = ""
+ mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ icon = 'icons/effects/alphacolors.dmi'
+ icon_state = ""
+ alpha = 100
+ layer = ABOVE_ALL_MOB_LAYER
+ plane = GAME_PLANE
diff --git a/code/modules/mob/living/silicon/ai/login.dm b/code/modules/mob/living/silicon/ai/login.dm
index 2dc9a1fb1c..50e2a46bf4 100644
--- a/code/modules/mob/living/silicon/ai/login.dm
+++ b/code/modules/mob/living/silicon/ai/login.dm
@@ -1,9 +1,11 @@
/mob/living/silicon/ai/Login()
..()
if(stat != DEAD)
- for(var/obj/machinery/ai_status_display/O in GLOB.ai_status_displays) //change status
+ for(var/each in GLOB.ai_status_displays) //change status
+ var/obj/machinery/status_display/ai/O = each
O.mode = 1
O.emotion = "Neutral"
+ O.update()
if(multicam_on)
end_multicam()
view_core()
diff --git a/code/modules/mob/living/silicon/ai/logout.dm b/code/modules/mob/living/silicon/ai/logout.dm
index fe5ea98c49..58e2ec56f4 100644
--- a/code/modules/mob/living/silicon/ai/logout.dm
+++ b/code/modules/mob/living/silicon/ai/logout.dm
@@ -1,5 +1,7 @@
/mob/living/silicon/ai/Logout()
..()
- for(var/obj/machinery/ai_status_display/O in GLOB.ai_status_displays) //change status
+ for(var/each in GLOB.ai_status_displays) //change status
+ var/obj/machinery/status_display/ai/O = each
O.mode = 0
+ O.update()
view_core()
diff --git a/code/modules/mob/living/silicon/ai/multicam.dm b/code/modules/mob/living/silicon/ai/multicam.dm
index d77e7f8a40..20b5f96242 100644
--- a/code/modules/mob/living/silicon/ai/multicam.dm
+++ b/code/modules/mob/living/silicon/ai/multicam.dm
@@ -124,6 +124,7 @@ GLOBAL_DATUM(ai_camera_room_landmark, /obj/effect/landmark/ai_multicam_room)
var/list/cameras_telegraphed = list()
var/telegraph_cameras = TRUE
var/telegraph_range = 7
+ ai_detector_color = COLOR_ORANGE
/mob/camera/aiEye/pic_in_pic/GetViewerClient()
if(screen && screen.ai)
@@ -139,6 +140,10 @@ GLOBAL_DATUM(ai_camera_room_landmark, /obj/effect/landmark/ai_multicam_room)
else
GLOB.cameranet.visibility(src)
update_camera_telegraphing()
+ update_ai_detect_hud()
+
+/mob/camera/aiEye/pic_in_pic/get_visible_turfs()
+ return screen ? screen.get_visible_turfs() : list()
/mob/camera/aiEye/pic_in_pic/proc/update_camera_telegraphing()
if(!telegraph_cameras)
diff --git a/code/modules/mob/living/silicon/ai/say.dm b/code/modules/mob/living/silicon/ai/say.dm
index c5820eb8c1..928bd63dd1 100644
--- a/code/modules/mob/living/silicon/ai/say.dm
+++ b/code/modules/mob/living/silicon/ai/say.dm
@@ -1,4 +1,4 @@
-/mob/living/silicon/ai/say(message, language)
+/mob/living/silicon/ai/say(message, bubble_type,var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
if(parent && istype(parent) && parent.stat != DEAD) //If there is a defined "parent" AI, it is actually an AI, and it is alive, anything the AI tries to say is said by the parent instead.
parent.say(message, language)
return
@@ -48,7 +48,7 @@
padloc = AREACOORD(padturf)
else
padloc = "(UNKNOWN)"
- log_talk(src,"HOLOPAD in [padloc]: [key_name(src)] : [message]", LOGSAY)
+ src.log_talk(message, LOG_SAY, tag="HOLOPAD in [padloc]")
send_speech(message, 7, T, "robot", get_spans(), language)
to_chat(src, "
Holopad transmitted, [real_name] \"[message]\" ")
else
diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm
index 7f4371b1bc..896d8674be 100644
--- a/code/modules/mob/living/silicon/pai/pai.dm
+++ b/code/modules/mob/living/silicon/pai/pai.dm
@@ -1,6 +1,6 @@
/mob/living/silicon/pai
name = "pAI"
- icon = 'modular_citadel/icons/mob/pai.dmi' // CITADEL EDIT
+ icon = 'icons/mob/pai.dmi'
icon_state = "repairbot"
mouse_opacity = MOUSE_OPACITY_OPAQUE
density = FALSE
@@ -58,7 +58,7 @@
var/canholo = TRUE
var/obj/item/card/id/access_card = null
var/chassis = "repairbot"
- var/list/possible_chassis = list("cat" = TRUE, "mouse" = TRUE, "monkey" = TRUE, "corgi" = FALSE, "fox" = FALSE, "repairbot" = TRUE, "rabbit" = TRUE, "borgi" = FALSE , "parrot" = FALSE, "bear" = FALSE , "mushroom" = FALSE, "crow" = FALSE , "fairy" = FALSE , "spiderbot" = FALSE ) //assoc value is whether it can be picked up.-- borgi and on = // CITADEL EDIT
+ var/list/possible_chassis = list("cat" = TRUE, "mouse" = TRUE, "monkey" = TRUE, "corgi" = FALSE, "fox" = FALSE, "repairbot" = TRUE, "rabbit" = TRUE) //assoc value is whether it can be picked up.
var/static/item_head_icon = 'icons/mob/pai_item_head.dmi'
var/static/item_lh_icon = 'icons/mob/pai_item_lh.dmi'
var/static/item_rh_icon = 'icons/mob/pai_item_rh.dmi'
@@ -75,13 +75,7 @@
var/overload_maxhealth = 0
canmove = FALSE
var/silent = FALSE
- var/hit_slowdown = 0
var/brightness_power = 5
- var/slowdown = 0
-
-/mob/living/silicon/pai/movement_delay()
- . = ..()
- . += slowdown
/mob/living/silicon/pai/can_unbuckle()
return FALSE
@@ -266,9 +260,9 @@
/mob/living/silicon/pai/Process_Spacemove(movement_dir = 0)
. = ..()
if(!.)
- slowdown = 2
+ add_movespeed_modifier(MOVESPEED_ID_PAI_SPACEWALK_SPEEDMOD, TRUE, 100, multiplicative_slowdown = 2)
return TRUE
- slowdown = initial(slowdown)
+ remove_movespeed_modifier(MOVESPEED_ID_PAI_SPACEWALK_SPEEDMOD, TRUE)
return TRUE
/mob/living/silicon/pai/examine(mob/user)
@@ -293,7 +287,5 @@
health = maxHealth - getBruteLoss() - getFireLoss()
update_stat()
-
/mob/living/silicon/pai/process()
emitterhealth = CLAMP((emitterhealth + emitterregen), -50, emittermaxhealth)
- hit_slowdown = CLAMP((hit_slowdown - 1), 0, 100)
diff --git a/code/modules/mob/living/silicon/pai/pai_defense.dm b/code/modules/mob/living/silicon/pai/pai_defense.dm
index 417a24cd68..f20ccbc730 100644
--- a/code/modules/mob/living/silicon/pai/pai_defense.dm
+++ b/code/modules/mob/living/silicon/pai/pai_defense.dm
@@ -64,7 +64,6 @@
if(emitterhealth < 0)
fold_in(force = TRUE)
to_chat(src, "
The impact degrades your holochassis! ")
- hit_slowdown += amount
return amount
/mob/living/silicon/pai/adjustBruteLoss(amount, updating_health = TRUE, forced = FALSE)
diff --git a/code/modules/mob/living/silicon/pai/pai_shell.dm b/code/modules/mob/living/silicon/pai/pai_shell.dm
index 85f7fe6afc..99484f395e 100644
--- a/code/modules/mob/living/silicon/pai/pai_shell.dm
+++ b/code/modules/mob/living/silicon/pai/pai_shell.dm
@@ -103,10 +103,6 @@
set_light(0)
to_chat(src, "
You disable your integrated light. ")
-/mob/living/silicon/pai/movement_delay()
- . = ..()
- . += 1 //A bit slower than humans, so they're easier to smash
-
/mob/living/silicon/pai/mob_pickup(mob/living/L)
var/obj/item/clothing/head/mob_holder/holder = new(get_turf(src), src, chassis, item_head_icon, item_lh_icon, item_rh_icon)
if(!L.put_in_hands(holder))
diff --git a/code/modules/mob/living/silicon/pai/say.dm b/code/modules/mob/living/silicon/pai/say.dm
index 6f7b8601d2..9f07900a25 100644
--- a/code/modules/mob/living/silicon/pai/say.dm
+++ b/code/modules/mob/living/silicon/pai/say.dm
@@ -1,8 +1,8 @@
-/mob/living/silicon/pai/say(msg)
+/mob/living/silicon/pai/say(message, bubble_type, var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
if(silent)
to_chat(src, "
Communication circuits remain unitialized. ")
else
- ..(msg)
+ ..(message)
/mob/living/silicon/pai/binarycheck()
return 0
diff --git a/code/modules/mob/living/silicon/robot/death.dm b/code/modules/mob/living/silicon/robot/death.dm
index 7eb241e01f..75e8fd317f 100644
--- a/code/modules/mob/living/silicon/robot/death.dm
+++ b/code/modules/mob/living/silicon/robot/death.dm
@@ -2,7 +2,7 @@
/mob/living/silicon/robot/gib_animation()
new /obj/effect/temp_visual/gib_animation(loc, "gibbed-r")
-/mob/living/silicon/robot/dust()
+/mob/living/silicon/robot/dust(just_ash, drop_items, force)
if(mmi)
qdel(mmi)
..()
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index 473fcb24df..a389bf637c 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -8,6 +8,7 @@
bubble_icon = "robot"
designation = "Default" //used for displaying the prefix & getting the current module of cyborg
has_limbs = 1
+ hud_type = /datum/hud/robot
var/custom_name = ""
var/braintype = "Cyborg"
@@ -211,10 +212,6 @@
cell = null
return ..()
-/mob/living/silicon/robot/can_interact_with(atom/A)
- . = ..()
- return . || in_view_range(src, A)
-
/mob/living/silicon/robot/proc/pick_module()
if(module.type != /obj/item/robot_module)
return
@@ -297,12 +294,12 @@
if(!ionpulse_on)
return
- if(cell.charge <= 50)
+ if(cell.charge <= 10)
toggle_ionpulse()
return
- cell.charge -= 50 // 500 steps on a default cell.
- return 1
+ cell.charge -= 10
+ return TRUE
/mob/living/silicon/robot/proc/toggle_ionpulse()
if(!ionpulse)
@@ -381,7 +378,13 @@
return !cleared
/mob/living/silicon/robot/can_interact_with(atom/A)
- return !low_power_mode && ISINRANGE(A.x, x - interaction_range, x + interaction_range) && ISINRANGE(A.y, y - interaction_range, y + interaction_range)
+ if (low_power_mode)
+ return FALSE
+ var/turf/T0 = get_turf(src)
+ var/turf/T1 = get_turf(A)
+ if (!T0 || ! T1)
+ return FALSE
+ return ISINRANGE(T1.x, T0.x - interaction_range, T0.x + interaction_range) && ISINRANGE(T1.y, T0.y - interaction_range, T0.y + interaction_range)
/mob/living/silicon/robot/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/weldingtool) && (user.a_intent != INTENT_HARM || user == src))
@@ -1118,10 +1121,10 @@
undeployment_action.Grant(src)
/datum/action/innate/undeployment
- name = "Disconnect from shell"
- desc = "Stop controlling your shell and resume normal core operations."
- icon_icon = 'icons/mob/actions/actions_AI.dmi'
- button_icon_state = "ai_core"
+ name = "Disconnect from shell"
+ desc = "Stop controlling your shell and resume normal core operations."
+ icon_icon = 'icons/mob/actions/actions_AI.dmi'
+ button_icon_state = "ai_core"
/datum/action/innate/undeployment/Trigger()
if(!..())
diff --git a/code/modules/mob/living/silicon/robot/robot_defense.dm b/code/modules/mob/living/silicon/robot/robot_defense.dm
index 4907e46751..c90c719e8a 100644
--- a/code/modules/mob/living/silicon/robot/robot_defense.dm
+++ b/code/modules/mob/living/silicon/robot/robot_defense.dm
@@ -22,11 +22,11 @@
uneq_active()
visible_message("
[M] disarmed [src]! ", \
"
[M] has disabled [src]'s active module! ", null, COMBAT_MESSAGE_RANGE)
- add_logs(M, src, "disarmed", "[I ? " removing \the [I]" : ""]")
+ log_combat(M, src, "disarmed", "[I ? " removing \the [I]" : ""]")
else
Stun(40)
step(src,get_dir(M,src))
- add_logs(M, src, "pushed")
+ log_combat(M, src, "pushed")
visible_message("
[M] has forced back [src]! ", \
"
[M] has forced back [src]! ", null, COMBAT_MESSAGE_RANGE)
playsound(loc, 'sound/weapons/pierce.ogg', 50, 1, -1)
diff --git a/code/modules/mob/living/silicon/robot/robot_movement.dm b/code/modules/mob/living/silicon/robot/robot_movement.dm
index 5c24b15267..623174157b 100644
--- a/code/modules/mob/living/silicon/robot/robot_movement.dm
+++ b/code/modules/mob/living/silicon/robot/robot_movement.dm
@@ -3,13 +3,6 @@
return 1
return ..()
-/mob/living/silicon/robot/movement_delay()
- . = ..()
- var/static/config_robot_delay
- if(isnull(config_robot_delay))
- config_robot_delay = CONFIG_GET(number/robot_delay)
- . += speed + config_robot_delay
-
/mob/living/silicon/robot/mob_negates_gravity()
return magpulse
diff --git a/code/modules/mob/living/silicon/say.dm b/code/modules/mob/living/silicon/say.dm
index 785d55e5f4..aca35762de 100644
--- a/code/modules/mob/living/silicon/say.dm
+++ b/code/modules/mob/living/silicon/say.dm
@@ -3,8 +3,7 @@
return ..() | SPAN_ROBOT
/mob/living/proc/robot_talk(message)
- log_talk(src,"[key_name(src)] : [message]",LOGSAY)
- log_message(message, INDIVIDUAL_SAY_LOG)
+ log_talk(message, LOG_SAY)
var/desig = "Default Cyborg" //ezmode for taters
if(issilicon(src))
var/mob/living/silicon/S = src
diff --git a/code/modules/mob/living/silicon/silicon_defense.dm b/code/modules/mob/living/silicon/silicon_defense.dm
index 647039e123..073a2eec2b 100644
--- a/code/modules/mob/living/silicon/silicon_defense.dm
+++ b/code/modules/mob/living/silicon/silicon_defense.dm
@@ -1,5 +1,5 @@
-/mob/living/silicon/grippedby(mob/living/user)
+/mob/living/silicon/grippedby(mob/living/user, instant = FALSE)
return //can't upgrade a simple pull into a more aggressive grab.
/mob/living/silicon/get_ear_protection()//no ears
@@ -9,13 +9,13 @@
if(..()) //if harm or disarm intent
var/damage = 20
if (prob(90))
- add_logs(M, src, "attacked")
+ log_combat(M, src, "attacked")
playsound(loc, 'sound/weapons/slash.ogg', 25, 1, -1)
visible_message("
[M] has slashed at [src]! ", \
"
[M] has slashed at [src]! ")
if(prob(8))
flash_act(affect_silicon = 1)
- add_logs(M, src, "attacked")
+ log_combat(M, src, "attacked")
adjustBruteLoss(damage)
updatehealth()
else
diff --git a/code/modules/mob/living/simple_animal/animal_defense.dm b/code/modules/mob/living/simple_animal/animal_defense.dm
index 5f2a91519f..a92c1ff9e1 100644
--- a/code/modules/mob/living/simple_animal/animal_defense.dm
+++ b/code/modules/mob/living/simple_animal/animal_defense.dm
@@ -23,7 +23,7 @@
"
[M] [response_harm] [src]! ", null, COMBAT_MESSAGE_RANGE)
playsound(loc, attacked_sound, 25, 1, -1)
attack_threshold_check(harm_intent_damage)
- add_logs(M, src, "attacked")
+ log_combat(M, src, "attacked")
updatehealth()
return TRUE
@@ -57,14 +57,14 @@
playsound(loc, 'sound/weapons/pierce.ogg', 25, 1, -1)
visible_message("
[M] [response_disarm] [name]! ", \
"
[M] [response_disarm] [name]! ", null, COMBAT_MESSAGE_RANGE)
- add_logs(M, src, "disarmed")
+ log_combat(M, src, "disarmed")
else
var/damage = rand(15, 30)
visible_message("
[M] has slashed at [src]! ", \
"
[M] has slashed at [src]! ", null, COMBAT_MESSAGE_RANGE)
playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1)
attack_threshold_check(damage)
- add_logs(M, src, "attacked")
+ log_combat(M, src, "attacked")
return 1
/mob/living/simple_animal/attack_larva(mob/living/carbon/alien/larva/L)
diff --git a/code/modules/mob/living/simple_animal/bot/SuperBeepsky.dm b/code/modules/mob/living/simple_animal/bot/SuperBeepsky.dm
new file mode 100644
index 0000000000..0378b0b9ee
--- /dev/null
+++ b/code/modules/mob/living/simple_animal/bot/SuperBeepsky.dm
@@ -0,0 +1,150 @@
+/mob/living/simple_animal/bot/secbot/grievous //This bot is powerful. If you managed to get 4 eswords somehow, you deserve this horror. Emag him for best results.
+ name = "General Beepsky"
+ desc = "Is that a secbot with four eswords in its arms...?"
+ icon = 'icons/mob/aibots.dmi'
+ icon_state = "grievous"
+ health = 150
+ maxHealth = 150
+ baton_type = /obj/item/melee/transforming/energy/sword
+ base_speed = 4 //he's a fast fucker
+ var/obj/item/weapon
+ var/block_chance = 50
+
+
+/mob/living/simple_animal/bot/secbot/grievous/toy //A toy version of general beepsky!
+ name = "Genewul Bweepskee"
+ desc = "An adorable looking secbot with four toy swords taped to its arms"
+ health = 50
+ maxHealth = 50
+ baton_type = /obj/item/toy/sword
+
+/mob/living/simple_animal/bot/secbot/grievous/bullet_act(obj/item/projectile/P)
+ visible_message("[src] deflects [P] with its energy swords!")
+ playsound(src, 'sound/weapons/blade1.ogg', 50, TRUE)
+ return FALSE
+
+/mob/living/simple_animal/bot/secbot/grievous/Crossed(atom/movable/AM)
+ ..()
+ if(ismob(AM) && AM == target)
+ visible_message("[src] flails his swords and cuts [AM]!")
+ playsound(src,'sound/effects/beepskyspinsabre.ogg',100,TRUE,-1)
+ stun_attack(AM)
+
+/mob/living/simple_animal/bot/secbot/grievous/Initialize()
+ . = ..()
+ weapon = new baton_type(src)
+ weapon.attack_self(src)
+
+/mob/living/simple_animal/bot/secbot/grievous/Destroy()
+ QDEL_NULL(weapon)
+ return ..()
+
+/mob/living/simple_animal/bot/secbot/grievous/special_retaliate_after_attack(mob/user)
+ if(mode != BOT_HUNT)
+ return
+ if(prob(block_chance))
+ visible_message("[src] deflects [user]'s attack with his energy swords!")
+ playsound(src, 'sound/weapons/blade1.ogg', 50, TRUE, -1)
+ return TRUE
+
+/mob/living/simple_animal/bot/secbot/grievous/stun_attack(mob/living/carbon/C) //Criminals don't deserve to live
+ weapon.attack(C, src)
+ playsound(src, 'sound/weapons/blade1.ogg', 50, TRUE, -1)
+ if(C.stat == DEAD)
+ addtimer(CALLBACK(src, .proc/update_icon), 2)
+ back_to_idle()
+
+
+/mob/living/simple_animal/bot/secbot/grievous/handle_automated_action()
+ if(!on)
+ return
+ switch(mode)
+ if(BOT_IDLE) // idle
+ update_icon()
+ walk_to(src,0)
+ look_for_perp() // see if any criminals are in range
+ if(!mode && auto_patrol) // still idle, and set to patrol
+ mode = BOT_START_PATROL // switch to patrol mode
+ if(BOT_HUNT) // hunting for perp
+ update_icon()
+ playsound(src,'sound/effects/beepskyspinsabre.ogg',100,TRUE,-1)
+ // general beepsky doesn't give up so easily, jedi scum
+ if(frustration >= 20)
+ walk_to(src,0)
+ back_to_idle()
+ return
+ if(target) // make sure target exists
+ if(Adjacent(target) && isturf(target.loc)) // if right next to perp
+ target_lastloc = target.loc //stun_attack() can clear the target if they're dead, so this needs to be set first
+ stun_attack(target)
+ anchored = TRUE
+ return
+ else // not next to perp
+ var/turf/olddist = get_dist(src, target)
+ walk_to(src, target,1,4)
+ if((get_dist(src, target)) >= (olddist))
+ frustration++
+ else
+ frustration = 0
+ else
+ back_to_idle()
+
+ if(BOT_START_PATROL)
+ look_for_perp()
+ start_patrol()
+
+ if(BOT_PATROL)
+ look_for_perp()
+ bot_patrol()
+
+/mob/living/simple_animal/bot/secbot/grievous/look_for_perp()
+ anchored = FALSE
+ var/judgement_criteria = judgement_criteria()
+ for (var/mob/living/carbon/C in view(7,src)) //Let's find us a criminal
+ if((C.stat) || (C.handcuffed))
+ continue
+
+ if((C.name == oldtarget_name) && (world.time < last_found + 100))
+ continue
+
+ threatlevel = C.assess_threat(judgement_criteria, weaponcheck=CALLBACK(src, .proc/check_for_weapons))
+
+ if(!threatlevel)
+ continue
+
+ else if(threatlevel >= 4)
+ target = C
+ oldtarget_name = C.name
+ speak("Level [threatlevel] infraction alert!")
+ playsound(src, pick('sound/voice/beepsky/criminal.ogg', 'sound/voice/beepsky/justice.ogg', 'sound/voice/beepsky/freeze.ogg'), 50, FALSE)
+ playsound(src,'sound/weapons/saberon.ogg',50,TRUE,-1)
+ visible_message("[src] ignites his energy swords!")
+ icon_state = "grievous-c"
+ visible_message("
[src] points at [C.name]!")
+ mode = BOT_HUNT
+ INVOKE_ASYNC(src, .proc/handle_automated_action)
+ break
+ else
+ continue
+
+
+/mob/living/simple_animal/bot/secbot/grievous/explode()
+
+ walk_to(src,0)
+ visible_message("
[src] lets out a huge cough as it blows apart! ")
+ var/atom/Tsec = drop_location()
+
+ var/obj/item/bot_assembly/secbot/Sa = new (Tsec)
+ Sa.build_step = 1
+ Sa.add_overlay("hs_hole")
+ Sa.created_name = name
+ new /obj/item/assembly/prox_sensor(Tsec)
+
+ if(prob(50))
+ drop_part(robot_arm, Tsec)
+
+ do_sparks(3, TRUE, src)
+ for(var/IS = 0 to 4)
+ drop_part(baton_type, Tsec)
+ new /obj/effect/decal/cleanable/oil(Tsec)
+ qdel(src)
diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm
index b38cf6395c..d2cfdb88e3 100644
--- a/code/modules/mob/living/simple_animal/bot/bot.dm
+++ b/code/modules/mob/living/simple_animal/bot/bot.dm
@@ -196,7 +196,7 @@
bot_reset()
turn_on() //The bot automatically turns on when emagged, unless recently hit with EMP.
to_chat(src, "
(#$*#$^^( OVERRIDE DETECTED ")
- add_logs(user, src, "emagged")
+ log_combat(user, src, "emagged")
return
else //Bot is unlocked, but the maint panel has not been opened with a screwdriver yet.
to_chat(user, "
You need to open maintenance panel first! ")
@@ -762,7 +762,7 @@ Pass a positive integer as an argument to override a bot's default speed.
else // no path, so calculate new one
calc_summon_path()
-/mob/living/simple_animal/bot/Collide(M as mob|obj) //Leave no door unopened!
+/mob/living/simple_animal/bot/Bump(M as mob|obj) //Leave no door unopened!
. = ..()
if((istype(M, /obj/machinery/door/airlock) || istype(M, /obj/machinery/door/window)) && (!isnull(access_card)))
var/obj/machinery/door/D = M
@@ -900,7 +900,7 @@ Pass a positive integer as an argument to override a bot's default speed.
name = paicard.pai.name
faction = user.faction.Copy()
language_holder = paicard.pai.language_holder.copy(src)
- add_logs(user, paicard.pai, "uploaded to [bot_name],")
+ log_combat(user, paicard.pai, "uploaded to [bot_name],")
return TRUE
else
to_chat(user, "
[card] is inactive. ")
@@ -920,9 +920,9 @@ Pass a positive integer as an argument to override a bot's default speed.
key = null
paicard.forceMove(loc)
if(user)
- add_logs(user, paicard.pai, "ejected from [src.bot_name],")
+ log_combat(user, paicard.pai, "ejected from [src.bot_name],")
else
- add_logs(src, paicard.pai, "ejected")
+ log_combat(src, paicard.pai, "ejected")
if(announce)
to_chat(paicard.pai, "
You feel your control fade as [paicard] ejects from [bot_name]. ")
paicard = null
diff --git a/code/modules/mob/living/simple_animal/bot/construction.dm b/code/modules/mob/living/simple_animal/bot/construction.dm
index c9ece767a5..56d37f667c 100644
--- a/code/modules/mob/living/simple_animal/bot/construction.dm
+++ b/code/modules/mob/living/simple_animal/bot/construction.dm
@@ -379,6 +379,8 @@
icon_state = "helmet_signaler"
item_state = "helmet"
created_name = "Securitron" //To preserve the name if it's a unique securitron I guess
+ var/swordamt = 0 //If you're converting it into a grievousbot, how many swords have you attached
+ var/toyswordamt = 0 //honk
/obj/item/bot_assembly/secbot/attackby(obj/item/I, mob/user, params)
..()
@@ -441,6 +443,29 @@
S.robot_arm = robot_arm
qdel(I)
qdel(src)
+ if(istype(I, /obj/item/wrench))
+ to_chat(user, "You adjust [src]'s arm slots to mount extra weapons")
+ build_step ++
+ return
+ if(istype(I, /obj/item/toy/sword))
+ if(toyswordamt < 3 && swordamt <= 0)
+ if(!user.temporarilyRemoveItemFromInventory(I))
+ return
+ created_name = "General Beepsky"
+ name = "helmet/signaler/prox sensor/robot arm/toy sword assembly"
+ icon_state = "grievous_assembly"
+ to_chat(user, "
You superglue [I] onto one of [src]'s arm slots. ")
+ qdel(I)
+ toyswordamt ++
+ else
+ if(!can_finish_build(I, user))
+ return
+ to_chat(user, "
You complete the Securitron!...Something seems a bit wrong with it..? ")
+ var/mob/living/simple_animal/bot/secbot/grievous/toy/S = new(Tsec)
+ S.name = created_name
+ S.robot_arm = robot_arm
+ qdel(I)
+ qdel(src)
else if(istype(I, /obj/item/screwdriver)) //deconstruct
cut_overlay("hs_arm")
@@ -448,3 +473,35 @@
robot_arm = null
to_chat(user, "
You remove [dropped_arm] from [src]. ")
build_step--
+ if(toyswordamt > 0 || toyswordamt)
+ icon_state = initial(icon_state)
+ to_chat(user, "
The superglue binding [src]'s toy swords to its chassis snaps! ")
+ for(var/IS in 1 to toyswordamt)
+ new /obj/item/toy/sword(Tsec)
+
+ if(ASSEMBLY_FIFTH_STEP)
+ if(istype(I, /obj/item/melee/transforming/energy/sword/saber))
+ if(swordamt < 3)
+ if(!user.temporarilyRemoveItemFromInventory(I))
+ return
+ created_name = "General Beepsky"
+ name = "helmet/signaler/prox sensor/robot arm/energy sword assembly"
+ icon_state = "grievous_assembly"
+ to_chat(user, "
You bolt [I] onto one of [src]'s arm slots. ")
+ qdel(I)
+ swordamt ++
+ else
+ if(!can_finish_build(I, user))
+ return
+ to_chat(user, "
You complete the Securitron!...Something seems a bit wrong with it..? ")
+ var/mob/living/simple_animal/bot/secbot/grievous/S = new(Tsec)
+ S.name = created_name
+ S.robot_arm = robot_arm
+ qdel(I)
+ qdel(src)
+ else if(istype(I, /obj/item/screwdriver)) //deconstruct
+ build_step--
+ icon_state = initial(icon_state)
+ to_chat(user, "
You unbolt [src]'s energy swords ")
+ for(var/IS in 1 to swordamt)
+ new /obj/item/melee/transforming/energy/sword/saber(Tsec)
diff --git a/code/modules/mob/living/simple_animal/bot/ed209bot.dm b/code/modules/mob/living/simple_animal/bot/ed209bot.dm
index a204edbcaf..581711d271 100644
--- a/code/modules/mob/living/simple_animal/bot/ed209bot.dm
+++ b/code/modules/mob/living/simple_animal/bot/ed209bot.dm
@@ -26,7 +26,7 @@
var/lastfired = 0
var/shot_delay = 15
var/lasercolor = ""
- var/disabled = 0//A holder for if it needs to be disabled, if true it will not seach for targets, shoot at targets, or move, currently only used for lasertag
+ var/disabled = FALSE //A holder for if it needs to be disabled, if true it will not seach for targets, shoot at targets, or move, currently only used for lasertag
var/mob/living/carbon/target
@@ -34,16 +34,18 @@
var/threatlevel = 0
var/target_lastloc //Loc of target when arrested.
var/last_found //There's a delay
- var/declare_arrests = 1 //When making an arrest, should it notify everyone wearing sechuds?
- var/idcheck = 1 //If true, arrest people with no IDs
- var/weaponscheck = 1 //If true, arrest people for weapons if they don't have access
- var/check_records = 1 //Does it check security records?
- var/arrest_type = 0 //If true, don't handcuff
+ var/declare_arrests = TRUE //When making an arrest, should it notify everyone wearing sechuds?
+ var/idcheck = TRUE //If true, arrest people with no IDs
+ var/weaponscheck = TRUE //If true, arrest people for weapons if they don't have access
+ var/check_records = TRUE //Does it check security records?
+ var/arrest_type = FALSE //If true, don't handcuff
var/projectile = /obj/item/projectile/energy/electrode //Holder for projectile type
var/shoot_sound = 'sound/weapons/taser.ogg'
var/cell_type = /obj/item/stock_parts/cell
var/vest_type = /obj/item/clothing/suit/armor/vest
+ do_footstep = TRUE
+
/mob/living/simple_animal/bot/ed209/Initialize(mapload,created_name,created_lasercolor)
. = ..()
@@ -199,14 +201,14 @@ Auto Patrol[]"},
to_chat(user, "
You short out [src]'s target assessment circuits. ")
oldtarget_name = user.name
audible_message("
[src] buzzes oddly! ")
- declare_arrests = 0
+ declare_arrests = FALSE
icon_state = "[lasercolor]ed209[on]"
set_weapon()
/mob/living/simple_animal/bot/ed209/bullet_act(obj/item/projectile/Proj)
if(istype(Proj , /obj/item/projectile/beam/laser)||istype(Proj, /obj/item/projectile/bullet))
if((Proj.damage_type == BURN) || (Proj.damage_type == BRUTE))
- if(!Proj.nodamage && Proj.damage < src.health)
+ if(!Proj.nodamage && Proj.damage < src.health && ishuman(Proj.firer))
retaliate(Proj.firer)
..()
@@ -357,7 +359,7 @@ Auto Patrol[]"},
target = C
oldtarget_name = C.name
speak("Level [threatlevel] infraction alert!")
- playsound(loc, pick('sound/voice/ed209_20sec.ogg', 'sound/voice/edplaceholder.ogg'), 50, 0)
+ playsound(src, pick('sound/voice/ed209_20sec.ogg', 'sound/voice/edplaceholder.ogg'), 50, FALSE)
visible_message("
[src] points at [C.name]!")
mode = BOT_HUNT
spawn(0)
@@ -447,7 +449,7 @@ Auto Patrol[]"},
return
var/obj/item/projectile/A = new projectile (loc)
- playsound(loc, shoot_sound, 50, 1)
+ playsound(src, shoot_sound, 50, TRUE)
A.preparePixelProjectile(target, src)
A.fire()
@@ -538,7 +540,7 @@ Auto Patrol[]"},
shootAt(A)
/mob/living/simple_animal/bot/ed209/proc/stun_attack(mob/living/carbon/C)
- playsound(loc, 'sound/weapons/egloves.ogg', 50, 1, -1)
+ playsound(src, 'sound/weapons/egloves.ogg', 50, TRUE, -1)
icon_state = "[lasercolor]ed209-c"
spawn(2)
icon_state = "[lasercolor]ed209[on]"
@@ -549,7 +551,7 @@ Auto Patrol[]"},
var/mob/living/carbon/human/H = C
var/judgement_criteria = judgement_criteria()
threat = H.assess_threat(judgement_criteria, weaponcheck=CALLBACK(src, .proc/check_for_weapons))
- add_logs(src,C,"stunned")
+ log_combat(src,C,"stunned")
if(declare_arrests)
var/area/location = get_area(src)
speak("[arrest_type ? "Detaining" : "Arresting"] level [threat] scumbag
[C] in [location].", radio_channel)
@@ -558,12 +560,12 @@ Auto Patrol[]"},
/mob/living/simple_animal/bot/ed209/proc/cuff(mob/living/carbon/C)
mode = BOT_ARREST
- playsound(loc, 'sound/weapons/cablecuff.ogg', 30, 1, -2)
+ playsound(src, 'sound/weapons/cablecuff.ogg', 30, TRUE, -2)
C.visible_message("
[src] is trying to put zipties on [C]! ",\
"
[src] is trying to put zipties on you! ")
spawn(60)
- if( !Adjacent(C) || !isturf(C.loc) ) //if he's in a closet or not adjacent, we cancel cuffing.
+ if( !on || !Adjacent(C) || !isturf(C.loc) ) //if he's in a closet or not adjacent, we cancel cuffing.
return
if(!C.handcuffed)
C.handcuffed = new /obj/item/restraints/handcuffs/cable/zipties/used(C)
diff --git a/code/modules/mob/living/simple_animal/bot/honkbot.dm b/code/modules/mob/living/simple_animal/bot/honkbot.dm
index 65b78bf844..d586cc694b 100644
--- a/code/modules/mob/living/simple_animal/bot/honkbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/honkbot.dm
@@ -2,7 +2,7 @@
name = "\improper honkbot"
desc = "A little robot. It looks happy with its bike horn."
icon = 'icons/mob/aibots.dmi'
- icon_state = "honkbot1"
+ icon_state = "honkbot"
density = FALSE
anchored = FALSE
health = 25
@@ -39,7 +39,7 @@
/mob/living/simple_animal/bot/honkbot/Initialize()
. = ..()
- icon_state = "honkbot[on]"
+ update_icon()
auto_patrol = TRUE
var/datum/job/clown/J = new/datum/job/clown
access_card.access += J.get_access()
@@ -48,22 +48,19 @@
/mob/living/simple_animal/bot/honkbot/proc/spam_flag_false() //used for addtimer
spam_flag = FALSE
-/mob/living/simple_animal/bot/honkbot/proc/blink_end() //used for addtimer
- icon_state = "honkbot[on]"
-
/mob/living/simple_animal/bot/honkbot/proc/sensor_blink()
icon_state = "honkbot-c"
- addtimer(CALLBACK(src, .proc/blink_end), 5)
+ addtimer(CALLBACK(src, .proc/update_icon), 5, TIMER_OVERRIDE|TIMER_UNIQUE)
//honkbots react with sounds.
/mob/living/simple_animal/bot/honkbot/proc/react_ping()
- playsound(src, 'sound/machines/ping.ogg', 50, 1, -1) //the first sound upon creation!
+ playsound(src, 'sound/machines/ping.ogg', 50, TRUE, -1) //the first sound upon creation!
spam_flag = TRUE
sensor_blink()
addtimer(CALLBACK(src, .proc/spam_flag_false), 18) // calibrates before starting the honk
/mob/living/simple_animal/bot/honkbot/proc/react_buzz()
- playsound(src, 'sound/machines/buzz-sigh.ogg', 50, 1, -1)
+ playsound(src, 'sound/machines/buzz-sigh.ogg', 50, TRUE, -1)
sensor_blink()
/mob/living/simple_animal/bot/honkbot/bot_reset()
@@ -123,7 +120,7 @@ Maintenance panel panel is [open ? "opened" : "closed"]"},
/mob/living/simple_animal/bot/honkbot/attackby(obj/item/W, mob/user, params)
- if(istype(W, /obj/item/weldingtool) && user.a_intent != INTENT_HARM).
+ if(istype(W, /obj/item/weldingtool) && user.a_intent != INTENT_HARM)
return
if(!istype(W, /obj/item/screwdriver) && (W.force) && (!target) && (W.damtype != STAMINA) ) // Check for welding tool to fix #2432.
retaliate(user)
@@ -138,10 +135,10 @@ Maintenance panel panel is [open ? "opened" : "closed"]"},
oldtarget_name = user.name
audible_message("
[src] gives out an evil laugh! ")
playsound(src, 'sound/machines/honkbot_evil_laugh.ogg', 75, 1, -1) // evil laughter
- icon_state = "honkbot[on]"
+ update_icon()
/mob/living/simple_animal/bot/honkbot/bullet_act(obj/item/projectile/Proj)
- if((istype(Proj,/obj/item/projectile/beam)) || (istype(Proj,/obj/item/projectile/bullet) && (Proj.damage_type == BURN))||(Proj.damage_type == BRUTE) && (!Proj.nodamage && Proj.damage < health))
+ if((istype(Proj,/obj/item/projectile/beam)) || (istype(Proj,/obj/item/projectile/bullet) && (Proj.damage_type == BURN))||(Proj.damage_type == BRUTE) && (!Proj.nodamage && Proj.damage < health && ishuman(Proj.firer)))
retaliate(Proj.firer)
..()
@@ -162,7 +159,7 @@ Maintenance panel panel is [open ? "opened" : "closed"]"},
/mob/living/simple_animal/bot/honkbot/hitby(atom/movable/AM, skipcatch = 0, hitpush = 1, blocked = 0)
if(istype(AM, /obj/item))
- playsound(src, honksound, 50, 1, -1)
+ playsound(src, honksound, 50, TRUE, -1)
var/obj/item/I = AM
if(I.throwforce < health && I.thrownby && (istype(I.thrownby, /mob/living/carbon/human)))
var/mob/living/carbon/human/H = I.thrownby
@@ -172,7 +169,7 @@ Maintenance panel panel is [open ? "opened" : "closed"]"},
/mob/living/simple_animal/bot/honkbot/proc/bike_horn() //use bike_horn
if (emagged <= 1)
if (!spam_flag)
- playsound(src, honksound, 50, 1, -1)
+ playsound(src, honksound, 50, TRUE, -1)
spam_flag = TRUE //prevent spam
sensor_blink()
addtimer(CALLBACK(src, .proc/spam_flag_false), cooldowntimehorn)
@@ -181,19 +178,19 @@ Maintenance panel panel is [open ? "opened" : "closed"]"},
playsound(src, "honkbot_e", 50, 0)
spam_flag = TRUE // prevent spam
icon_state = "honkbot-e"
- addtimer(CALLBACK(src, .proc/blink_end), 30)
+ addtimer(CALLBACK(src, .proc/update_icon), 30, TIMER_OVERRIDE|TIMER_UNIQUE)
addtimer(CALLBACK(src, .proc/spam_flag_false), cooldowntimehorn)
/mob/living/simple_animal/bot/honkbot/proc/honk_attack(mob/living/carbon/C) // horn attack
if(!spam_flag)
- playsound(loc, honksound, 50, 1, -1)
+ playsound(loc, honksound, 50, TRUE, -1)
spam_flag = TRUE // prevent spam
sensor_blink()
addtimer(CALLBACK(src, .proc/spam_flag_false), cooldowntimehorn)
/mob/living/simple_animal/bot/honkbot/proc/stun_attack(mob/living/carbon/C) // airhorn stun
if(!spam_flag)
- playsound(loc, 'sound/items/AirHorn.ogg', 100, 1, -1) //HEEEEEEEEEEEENK!!
+ playsound(src, 'sound/items/AirHorn.ogg', 100, TRUE, -1) //HEEEEEEEEEEEENK!!
sensor_blink()
if(spam_flag == 0)
if(ishuman(C))
@@ -213,7 +210,7 @@ Maintenance panel panel is [open ? "opened" : "closed"]"},
threatlevel = 6 // will never let you go
addtimer(CALLBACK(src, .proc/spam_flag_false), cooldowntime)
- add_logs(src,C,"honked")
+ log_combat(src,C,"honked")
C.visible_message("
[src] has honked [C]! ",\
"
[src] has honked you! ")
diff --git a/code/modules/mob/living/simple_animal/bot/medbot.dm b/code/modules/mob/living/simple_animal/bot/medbot.dm
index 5e05a9cbd4..7167d87bde 100644
--- a/code/modules/mob/living/simple_animal/bot/medbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/medbot.dm
@@ -261,7 +261,7 @@
if(assess_patient(H))
last_found = world.time
if((last_newpatient_speak + 300) < world.time) //Don't spam these messages!
- var/list/messagevoice = list("Hey, [H.name]! Hold on, I'm coming." = 'sound/voice/mcoming.ogg',"Wait [H.name]! I want to help!" = 'sound/voice/mhelp.ogg',"[H.name], you appear to be injured!" = 'sound/voice/minjured.ogg')
+ var/list/messagevoice = list("Hey, [H.name]! Hold on, I'm coming." = 'sound/voice/medbot/coming.ogg',"Wait [H.name]! I want to help!" = 'sound/voice/medbot/help.ogg',"[H.name], you appear to be injured!" = 'sound/voice/medbot/injured.ogg')
var/message = pick(messagevoice)
speak(message)
playsound(loc, messagevoice[message], 50, 0)
@@ -287,9 +287,9 @@
oldpatient = patient
soft_reset()
- if(!patient)
+ if(QDELETED(patient))
if(!shut_up && prob(1))
- var/list/messagevoice = list("Radar, put a mask on!" = 'sound/voice/mradar.ogg',"There's always a catch, and I'm the best there is." = 'sound/voice/mcatch.ogg',"I knew it, I should've been a plastic surgeon." = 'sound/voice/msurgeon.ogg',"What kind of medbay is this? Everyone's dropping like flies." = 'sound/voice/mflies.ogg',"Delicious!" = 'sound/voice/mdelicious.ogg')
+ var/list/messagevoice = list("Radar, put a mask on!" = 'sound/voice/medbot/radar.ogg',"There's always a catch, and I'm the best there is." = 'sound/voice/medbot/catch.ogg',"I knew it, I should've been a plastic surgeon." = 'sound/voice/medbot/surgeon.ogg',"What kind of medbay is this? Everyone's dropping like flies." = 'sound/voice/medbot/flies.ogg',"Delicious!" = 'sound/voice/medbot/delicious.ogg')
var/message = pick(messagevoice)
speak(message)
playsound(loc, messagevoice[message], 50, 0)
@@ -422,7 +422,7 @@
return
if(C.stat == DEAD || (C.has_trait(TRAIT_FAKEDEATH)))
- var/list/messagevoice = list("No! Stay with me!" = 'sound/voice/mno.ogg',"Live, damnit! LIVE!" = 'sound/voice/mlive.ogg',"I...I've never lost a patient before. Not today, I mean." = 'sound/voice/mlost.ogg')
+ var/list/messagevoice = list("No! Stay with me!" = 'sound/voice/medbot/no.ogg',"Live, damnit! LIVE!" = 'sound/voice/medbot/live.ogg',"I...I've never lost a patient before. Not today, I mean." = 'sound/voice/medbot/lost.ogg')
var/message = pick(messagevoice)
speak(message)
playsound(loc, messagevoice[message], 50, 0)
@@ -476,7 +476,7 @@
if(!reagent_id) //If they don't need any of that they're probably cured!
if(C.maxHealth - C.health < heal_threshold)
to_chat(src, "
[C] is healthy! Your programming prevents you from injecting anyone without at least [heal_threshold] damage of any one type ([heal_threshold + 15] for oxygen damage.) ")
- var/list/messagevoice = list("All patched up!" = 'sound/voice/mpatchedup.ogg',"An apple a day keeps me away." = 'sound/voice/mapple.ogg',"Feel better soon!" = 'sound/voice/mfeelbetter.ogg')
+ var/list/messagevoice = list("All patched up!" = 'sound/voice/medbot/patchedup.ogg',"An apple a day keeps me away." = 'sound/voice/medbot/apple.ogg',"Feel better soon!" = 'sound/voice/medbot/feelbetter.ogg')
var/message = pick(messagevoice)
speak(message)
playsound(loc, messagevoice[message], 50, 0)
@@ -540,7 +540,7 @@
drop_part(robot_arm, Tsec)
if(emagged && prob(25))
- playsound(loc, 'sound/voice/minsult.ogg', 50, 0)
+ playsound(loc, 'sound/voice/medbot/insult.ogg', 50, 0)
do_sparks(3, TRUE, src)
..()
diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm
index 600f6e58e4..c45d435253 100644
--- a/code/modules/mob/living/simple_animal/bot/mulebot.dm
+++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm
@@ -623,7 +623,7 @@
return
// called when bot bumps into anything
-/mob/living/simple_animal/bot/mulebot/Collide(atom/obs)
+/mob/living/simple_animal/bot/mulebot/Bump(atom/obs)
if(wires.is_cut(WIRE_AVOIDANCE)) // usually just bumps, but if avoidance disabled knock over mobs
if(isliving(obs))
var/mob/living/L = obs
@@ -631,7 +631,7 @@
visible_message("
[src] bumps into [L]! ")
else
if(!paicard)
- add_logs(src, L, "knocked down")
+ log_combat(src, L, "knocked down")
visible_message("
[src] knocks over [L]! ")
L.Knockdown(160)
return ..()
@@ -639,7 +639,7 @@
// called from mob/living/carbon/human/Crossed()
// when mulebot is in the same loc
/mob/living/simple_animal/bot/mulebot/proc/RunOver(mob/living/carbon/human/H)
- add_logs(src, H, "run over", null, "(DAMTYPE: [uppertext(BRUTE)])")
+ log_combat(src, H, "run over", null, "(DAMTYPE: [uppertext(BRUTE)])")
H.visible_message("
[src] drives over [H]! ", \
"
[src] drives over you! ")
playsound(loc, 'sound/effects/splat.ogg', 50, 1)
diff --git a/code/modules/mob/living/simple_animal/bot/secbot.dm b/code/modules/mob/living/simple_animal/bot/secbot.dm
index bcc85044b0..fca1f66546 100644
--- a/code/modules/mob/living/simple_animal/bot/secbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/secbot.dm
@@ -2,7 +2,7 @@
name = "\improper Securitron"
desc = "A little security robot. He looks less than thrilled."
icon = 'icons/mob/aibots.dmi'
- icon_state = "secbot0"
+ icon_state = "secbot"
density = FALSE
anchored = FALSE
health = 25
@@ -24,21 +24,21 @@
var/baton_type = /obj/item/melee/baton
var/mob/living/carbon/target
var/oldtarget_name
- var/threatlevel = 0
+ var/threatlevel = FALSE
var/target_lastloc //Loc of target when arrested.
var/last_found //There's a delay
- var/declare_arrests = 1 //When making an arrest, should it notify everyone on the security channel?
- var/idcheck = 0 //If true, arrest people with no IDs
- var/weaponscheck = 0 //If true, arrest people for weapons if they lack access
- var/check_records = 1 //Does it check security records?
- var/arrest_type = 0 //If true, don't handcuff
+ var/declare_arrests = TRUE //When making an arrest, should it notify everyone on the security channel?
+ var/idcheck = FALSE //If true, arrest people with no IDs
+ var/weaponscheck = FALSE //If true, arrest people for weapons if they lack access
+ var/check_records = TRUE //Does it check security records?
+ var/arrest_type = FALSE //If true, don't handcuff
/mob/living/simple_animal/bot/secbot/beepsky
name = "Officer Beep O'sky"
desc = "It's Officer Beep O'sky! Powered by a potato and a shot of whiskey."
- idcheck = 0
- weaponscheck = 0
- auto_patrol = 1
+ idcheck = FALSE
+ weaponscheck = FALSE
+ auto_patrol = TRUE
/mob/living/simple_animal/bot/secbot/beepsky/jr
name = "Officer Pipsqueak"
@@ -65,7 +65,7 @@
/mob/living/simple_animal/bot/secbot/Initialize()
. = ..()
- icon_state = "secbot[on]"
+ update_icon()
var/datum/job/detective/J = new/datum/job/detective
access_card.access += J.get_access()
prev_access = access_card.access
@@ -74,13 +74,15 @@
var/datum/atom_hud/secsensor = GLOB.huds[DATA_HUD_SECURITY_ADVANCED]
secsensor.add_hud_to(src)
-/mob/living/simple_animal/bot/secbot/turn_on()
+/mob/living/simple_animal/bot/secbot/update_icon()
+ if(mode == BOT_HUNT)
+ icon_state = "[initial(icon_state)]-c"
+ return
..()
- icon_state = "secbot[on]"
/mob/living/simple_animal/bot/secbot/turn_off()
..()
- icon_state = "secbot[on]"
+ mode = BOT_IDLE
/mob/living/simple_animal/bot/secbot/bot_reset()
..()
@@ -167,9 +169,14 @@ Auto Patrol: []"},
final = final|JUDGE_EMAGGED
return final
+/mob/living/simple_animal/bot/secbot/proc/special_retaliate_after_attack(mob/user) //allows special actions to take place after being attacked.
+ return
+
/mob/living/simple_animal/bot/secbot/attack_hand(mob/living/carbon/human/H)
if((H.a_intent == INTENT_HARM) || (H.a_intent == INTENT_DISARM))
retaliate(H)
+ if(special_retaliate_after_attack(H))
+ return
return ..()
@@ -179,6 +186,8 @@ Auto Patrol: []"},
return
if(!istype(W, /obj/item/screwdriver) && (W.force) && (!target) && (W.damtype != STAMINA) ) // Added check for welding tool to fix #2432. Welding tool behavior is handled in superclass.
retaliate(user)
+ if(special_retaliate_after_attack(user))
+ return
/mob/living/simple_animal/bot/secbot/emag_act(mob/user)
..()
@@ -187,13 +196,13 @@ Auto Patrol: []"},
to_chat(user, "
You short out [src]'s target assessment circuits. ")
oldtarget_name = user.name
audible_message("
[src] buzzes oddly! ")
- declare_arrests = 0
- icon_state = "secbot[on]"
+ declare_arrests = FALSE
+ update_icon()
/mob/living/simple_animal/bot/secbot/bullet_act(obj/item/projectile/Proj)
if(istype(Proj , /obj/item/projectile/beam)||istype(Proj, /obj/item/projectile/bullet))
if((Proj.damage_type == BURN) || (Proj.damage_type == BRUTE))
- if(!Proj.nodamage && Proj.damage < src.health)
+ if(!Proj.nodamage && Proj.damage < src.health && ishuman(Proj.firer))
retaliate(Proj.firer)
..()
@@ -222,13 +231,13 @@ Auto Patrol: []"},
/mob/living/simple_animal/bot/secbot/proc/cuff(mob/living/carbon/C)
mode = BOT_ARREST
- playsound(loc, 'sound/weapons/cablecuff.ogg', 30, 1, -2)
+ playsound(src, 'sound/weapons/cablecuff.ogg', 30, TRUE, -2)
C.visible_message("
[src] is trying to put zipties on [C]! ",\
"
[src] is trying to put zipties on you! ")
addtimer(CALLBACK(src, .proc/attempt_handcuff, C), 60)
/mob/living/simple_animal/bot/secbot/proc/attempt_handcuff(mob/living/carbon/C)
- if( !Adjacent(C) || !isturf(C.loc) ) //if he's in a closet or not adjacent, we cancel cuffing.
+ if( !on || !Adjacent(C) || !isturf(C.loc) ) //if he's in a closet or not adjacent, we cancel cuffing.
return
if(!C.handcuffed)
C.handcuffed = new /obj/item/restraints/handcuffs/cable/zipties/used(C)
@@ -236,14 +245,11 @@ Auto Patrol: []"},
playsound(src, "law", 50, 0)
back_to_idle()
-/mob/living/simple_animal/bot/secbot/proc/update_onsprite()
- icon_state = "secbot[on]"
-
/mob/living/simple_animal/bot/secbot/proc/stun_attack(mob/living/carbon/C)
var/judgement_criteria = judgement_criteria()
- playsound(loc, 'sound/weapons/egloves.ogg', 50, 1, -1)
+ playsound(src, 'sound/weapons/egloves.ogg', 50, TRUE, -1)
icon_state = "secbot-c"
- addtimer(CALLBACK(src, .proc/update_onsprite), 2)
+ addtimer(CALLBACK(src, .proc/update_icon), 2)
var/threat = 5
if(ishuman(C))
C.stuttering = 5
@@ -255,7 +261,7 @@ Auto Patrol: []"},
C.stuttering = 5
threat = C.assess_threat(judgement_criteria, weaponcheck=CALLBACK(src, .proc/check_for_weapons))
- add_logs(src,C,"stunned")
+ log_combat(src,C,"stunned")
if(declare_arrests)
var/area/location = get_area(src)
speak("[arrest_type ? "Detaining" : "Arresting"] level [threat] scumbag
[C] in [location].", radio_channel)
@@ -384,7 +390,7 @@ Auto Patrol: []"},
target = C
oldtarget_name = C.name
speak("Level [threatlevel] infraction alert!")
- playsound(loc, pick('sound/voice/bcriminal.ogg', 'sound/voice/bjustice.ogg', 'sound/voice/bfreeze.ogg'), 50, 0)
+ playsound(loc, pick('sound/voice/beepsky/criminal.ogg', 'sound/voice/beepsky/justice.ogg', 'sound/voice/beepsky/freeze.ogg'), 50, FALSE)
visible_message("
[src] points at [C.name]!")
mode = BOT_HUNT
INVOKE_ASYNC(src, .proc/handle_automated_action)
diff --git a/code/modules/mob/living/simple_animal/constructs.dm b/code/modules/mob/living/simple_animal/constructs.dm
index 0d1beb6384..ff91ca7dc3 100644
--- a/code/modules/mob/living/simple_animal/constructs.dm
+++ b/code/modules/mob/living/simple_animal/constructs.dm
@@ -32,6 +32,7 @@
del_on_death = TRUE
initial_language_holder = /datum/language_holder/construct
deathmessage = "collapses in a shattered heap."
+ hud_type = /datum/hud/constructs
var/list/construct_spells = list()
var/playstyle_string = "
You are a generic construct! Your job is to not exist, and you should probably adminhelp this. "
var/master = null
@@ -121,8 +122,8 @@
desc = "A massive, armored construct built to spearhead attacks and soak up enemy fire."
icon_state = "behemoth"
icon_living = "behemoth"
- maxHealth = 200
- health = 200
+ maxHealth = 150
+ health = 150
response_harm = "harmlessly punches"
harm_intent_damage = 0
obj_damage = 90
@@ -147,7 +148,7 @@
/mob/living/simple_animal/hostile/construct/armored/bullet_act(obj/item/projectile/P)
if(istype(P, /obj/item/projectile/energy) || istype(P, /obj/item/projectile/beam))
- var/reflectchance = 60 - round(P.damage/3)
+ var/reflectchance = 40 - round(P.damage/3)
if(prob(reflectchance))
apply_damage(P.damage * 0.5, P.damage_type)
visible_message("
The [P.name] is reflected by [src]'s armored shell! ", \
@@ -227,7 +228,7 @@
/mob/living/simple_animal/hostile/construct/builder
name = "Artificer"
real_name = "Artificer"
- desc = "A bulbous construct dedicated to building and maintaining the Cult of Nar-Sie's armies."
+ desc = "A bulbous construct dedicated to building and maintaining the Cult of Nar'Sie's armies."
icon_state = "artificer"
icon_living = "artificer"
maxHealth = 50
@@ -311,7 +312,7 @@
/mob/living/simple_animal/hostile/construct/harvester
name = "Harvester"
real_name = "Harvester"
- desc = "A long, thin construct built to herald Nar-Sie's rise. It'll be all over soon."
+ desc = "A long, thin construct built to herald Nar'Sie's rise. It'll be all over soon."
icon_state = "chosen"
icon_living = "chosen"
maxHealth = 40
@@ -328,7 +329,7 @@
can_repair_constructs = TRUE
-/mob/living/simple_animal/hostile/construct/harvester/Collide(atom/AM)
+/mob/living/simple_animal/hostile/construct/harvester/Bump(atom/AM)
. = ..()
if(istype(AM, /turf/closed/wall/mineral/cult) && AM != loc) //we can go through cult walls
var/atom/movable/stored_pulling = pulling
diff --git a/code/modules/mob/living/simple_animal/corpse.dm b/code/modules/mob/living/simple_animal/corpse.dm
index 68cd130803..ec20fbadcb 100644
--- a/code/modules/mob/living/simple_animal/corpse.dm
+++ b/code/modules/mob/living/simple_animal/corpse.dm
@@ -27,7 +27,6 @@
back = /obj/item/storage/backpack
id = /obj/item/card/id/syndicate
-
/obj/effect/mob_spawn/human/corpse/syndicatecommando
name = "Syndicate Commando"
id_job = "Operative"
@@ -76,7 +75,7 @@
/obj/effect/mob_spawn/human/corpse/pirate
name = "Pirate"
- skin_tone = "Caucasian1" //all pirates are white because it's easier that way
+ skin_tone = "caucasian1" //all pirates are white because it's easier that way
outfit = /datum/outfit/piratecorpse
hair_style = "Bald"
facial_hair_style = "Shaved"
@@ -155,7 +154,7 @@
outfit = /datum/outfit/wizardcorpse
hair_style = "Bald"
facial_hair_style = "Long Beard"
- skin_tone = "Caucasian1"
+ skin_tone = "caucasian1"
/datum/outfit/wizardcorpse
name = "Space Wizard Corpse"
@@ -203,4 +202,20 @@
ears = /obj/item/radio/headset
back = /obj/item/storage/backpack/satchel/med
id = /obj/item/card/id
- glasses = /obj/item/clothing/glasses/hud/health
\ No newline at end of file
+ glasses = /obj/item/clothing/glasses/hud/health
+
+/obj/effect/mob_spawn/human/corpse/bee_terrorist
+ name = "BLF Operative"
+ outfit = /datum/outfit/bee_terrorist
+
+/datum/outfit/bee_terrorist
+ name = "BLF Operative"
+ uniform = /obj/item/clothing/under/color/yellow
+ suit = /obj/item/clothing/suit/hooded/bee_costume
+ shoes = /obj/item/clothing/shoes/sneakers/yellow
+ gloves = /obj/item/clothing/gloves/color/yellow
+ ears = /obj/item/radio/headset
+ belt = /obj/item/storage/belt/fannypack/yellow/bee_terrorist
+ id = /obj/item/card/id
+ l_pocket = /obj/item/paper/fluff/bee_objectives
+ mask = /obj/item/clothing/mask/rat/bee
diff --git a/code/modules/mob/living/simple_animal/damage_procs.dm b/code/modules/mob/living/simple_animal/damage_procs.dm
index bb6794a36a..1031c5b7ee 100644
--- a/code/modules/mob/living/simple_animal/damage_procs.dm
+++ b/code/modules/mob/living/simple_animal/damage_procs.dm
@@ -2,7 +2,7 @@
/mob/living/simple_animal/proc/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
if(!forced && (status_flags & GODMODE))
return FALSE
- bruteloss = CLAMP(bruteloss + amount, 0, maxHealth)
+ bruteloss = round(CLAMP(bruteloss + amount, 0, maxHealth),DAMAGE_PRECISION)
if(updating_health)
updatehealth()
return amount
diff --git a/code/modules/mob/living/simple_animal/friendly/cat.dm b/code/modules/mob/living/simple_animal/friendly/cat.dm
index 75e7b668ea..9a08276999 100644
--- a/code/modules/mob/living/simple_animal/friendly/cat.dm
+++ b/code/modules/mob/living/simple_animal/friendly/cat.dm
@@ -32,6 +32,8 @@
gold_core_spawnable = FRIENDLY_SPAWN
collar_type = "cat"
+ do_footstep = TRUE
+
/mob/living/simple_animal/pet/cat/Initialize()
. = ..()
verbs += /mob/living/proc/lay_down
diff --git a/code/modules/mob/living/simple_animal/friendly/dog.dm b/code/modules/mob/living/simple_animal/friendly/dog.dm
index 0787e536cb..3a5d02315b 100644
--- a/code/modules/mob/living/simple_animal/friendly/dog.dm
+++ b/code/modules/mob/living/simple_animal/friendly/dog.dm
@@ -14,6 +14,8 @@
speak_chance = 1
turns_per_move = 10
+ do_footstep = TRUE
+
//Corgis and pugs are now under one dog subtype
/mob/living/simple_animal/pet/dog/corgi
@@ -46,6 +48,16 @@
gold_core_spawnable = FRIENDLY_SPAWN
collar_type = "pug"
+/mob/living/simple_animal/pet/dog/corgi/exoticcorgi
+ name = "Exotic Corgi"
+ desc = "As cute as it is colorful!"
+ icon = 'icons/mob/pets.dmi'
+ icon_state = "corgigrey"
+ icon_living = "corgigrey"
+ icon_dead = "corgigrey_dead"
+ animal_species = /mob/living/simple_animal/pet/dog/corgi/exoticcorgi
+ nofur = TRUE
+
/mob/living/simple_animal/pet/dog/Initialize()
. = ..()
var/dog_area = get_area(src)
@@ -58,6 +70,10 @@
. = ..()
regenerate_icons()
+/mob/living/simple_animal/pet/dog/corgi/exoticcorgi/Initialize()
+ . = ..()
+ var/newcolor = rgb(rand(0, 255), rand(0, 255), rand(0, 255))
+ add_atom_colour(newcolor, FIXED_COLOUR_PRIORITY)
/mob/living/simple_animal/pet/dog/corgi/death(gibbed)
..(gibbed)
@@ -540,7 +556,7 @@
/mob/living/simple_animal/pet/dog/corgi/puppy/void //Tribute to the corgis born in nullspace
name = "\improper void puppy"
real_name = "voidy"
- desc = "A corgi puppy that has been infused with deep space energy. It's staring back.."
+ desc = "A corgi puppy that has been infused with deep space energy. It's staring back..."
icon_state = "void_puppy"
icon_living = "void_puppy"
icon_dead = "void_puppy_dead"
diff --git a/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm b/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm
index 0c297e2c70..cf3742fcc5 100644
--- a/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm
+++ b/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm
@@ -59,7 +59,7 @@
var/laws = \
"1. You may not involve yourself in the matters of another being, even if such matters conflict with Law Two or Law Three, unless the other being is another Drone.\n"+\
"2. You may not harm any being, regardless of intent or circumstance.\n"+\
- "3. Your goals are to build, maintain, repair, improve, and provide power to the best of your abilities, You must never actively work against these goals."
+ "3. Your goals are to actively build, maintain, repair, improve, and provide power to the best of your abilities within the facility that housed your activation." //for derelict drones so they don't go to station.
var/heavy_emp_damage = 25 //Amount of damage sustained if hit by a heavy EMP pulse
var/alarms = list("Atmosphere" = list(), "Fire" = list(), "Power" = list())
var/obj/item/internal_storage //Drones can store one item, of any size/type in their body
@@ -69,7 +69,7 @@
var/visualAppearence = MAINTDRONE //What we appear as
var/hacked = FALSE //If we have laws to destroy the station
var/flavortext = \
- "\n
UNLESS YOU ARE A FREE DRONE, DO NOT INTERFERE WITH THE ROUND AS A DRONE OR YOU WILL BE DRONE BANNED \n"+\
+ "\n
DO NOT INTERFERE WITH THE ROUND AS A DRONE OR YOU WILL BE DRONE BANNED \n"+\
"
Drones are a ghost role that are allowed to fix the station and build things. Interfering with the round as a drone is against the rules. \n"+\
"
Actions that constitute interference include, but are not limited to: \n"+\
"
- Interacting with round critical objects (IDs, weapons, contraband, powersinks, bombs, etc.) \n"+\
diff --git a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm
index 9423cf9df6..564fae48ad 100644
--- a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm
+++ b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm
@@ -30,6 +30,8 @@
blood_volume = BLOOD_VOLUME_NORMAL
var/obj/item/udder/udder = null
+ do_footstep = TRUE
+
/mob/living/simple_animal/hostile/retaliate/goat/Initialize()
udder = new()
. = ..()
@@ -130,6 +132,8 @@
gold_core_spawnable = FRIENDLY_SPAWN
blood_volume = BLOOD_VOLUME_NORMAL
+ do_footstep = TRUE
+
/mob/living/simple_animal/cow/Initialize()
udder = new()
. = ..()
@@ -206,6 +210,8 @@
mob_size = MOB_SIZE_TINY
gold_core_spawnable = FRIENDLY_SPAWN
+ do_footstep = TRUE
+
/mob/living/simple_animal/chick/Initialize()
. = ..()
pixel_x = rand(-6, 6)
@@ -262,6 +268,8 @@
gold_core_spawnable = FRIENDLY_SPAWN
var/static/chicken_count = 0
+ do_footstep = TRUE
+
/mob/living/simple_animal/chicken/Initialize()
. = ..()
if(!body_color)
diff --git a/code/modules/mob/living/simple_animal/friendly/fox.dm b/code/modules/mob/living/simple_animal/friendly/fox.dm
index a0e9f08a82..28b66c26ee 100644
--- a/code/modules/mob/living/simple_animal/friendly/fox.dm
+++ b/code/modules/mob/living/simple_animal/friendly/fox.dm
@@ -19,6 +19,8 @@
response_harm = "kicks"
gold_core_spawnable = FRIENDLY_SPAWN
+ do_footstep = TRUE
+
//Captain fox
/mob/living/simple_animal/pet/fox/Renault
name = "Renault"
diff --git a/code/modules/mob/living/simple_animal/friendly/gondola.dm b/code/modules/mob/living/simple_animal/friendly/gondola.dm
index 9b021d902c..78768219de 100644
--- a/code/modules/mob/living/simple_animal/friendly/gondola.dm
+++ b/code/modules/mob/living/simple_animal/friendly/gondola.dm
@@ -17,7 +17,7 @@
icon = 'icons/mob/gondolas.dmi'
icon_state = "gondola"
icon_living = "gondola"
- loot = list(/obj/effect/decal/cleanable/blood/gibs, /obj/item/stack/sheet/animalhide/gondola = 1)
+ loot = list(/obj/effect/decal/cleanable/blood/gibs, /obj/item/stack/sheet/animalhide/gondola = 1, /obj/item/reagent_containers/food/snacks/meat/slab/gondola = 1)
//Gondolas aren't affected by cold.
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
@@ -26,6 +26,8 @@
health = 200
del_on_death = TRUE
+ //Gondolas don't make footstep sounds
+
/mob/living/simple_animal/pet/gondola/Initialize()
. = ..()
CreateGondola()
diff --git a/code/modules/mob/living/simple_animal/friendly/penguin.dm b/code/modules/mob/living/simple_animal/friendly/penguin.dm
index 0bf9f4732d..9835840dbf 100644
--- a/code/modules/mob/living/simple_animal/friendly/penguin.dm
+++ b/code/modules/mob/living/simple_animal/friendly/penguin.dm
@@ -15,10 +15,12 @@
turns_per_move = 10
icon = 'icons/mob/penguins.dmi'
+ do_footstep = TRUE
+
/mob/living/simple_animal/pet/penguin/emperor
name = "Emperor penguin"
real_name = "penguin"
- desc = "Emperor of all he surveys."
+ desc = "Emperor of all they survey."
icon_state = "penguin"
icon_living = "penguin"
icon_dead = "penguin_dead"
diff --git a/code/modules/mob/living/simple_animal/friendly/sloth.dm b/code/modules/mob/living/simple_animal/friendly/sloth.dm
index f112b3f98e..324fa107fa 100644
--- a/code/modules/mob/living/simple_animal/friendly/sloth.dm
+++ b/code/modules/mob/living/simple_animal/friendly/sloth.dm
@@ -23,6 +23,8 @@
speed = 10
glide_size = 2
+ do_footstep = TRUE
+
//Cargo Sloth
/mob/living/simple_animal/sloth/paperwork
diff --git a/code/modules/mob/living/simple_animal/guardian/guardian.dm b/code/modules/mob/living/simple_animal/guardian/guardian.dm
index 648db14941..701e244f89 100644
--- a/code/modules/mob/living/simple_animal/guardian/guardian.dm
+++ b/code/modules/mob/living/simple_animal/guardian/guardian.dm
@@ -38,6 +38,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
melee_damage_upper = 15
butcher_results = list(/obj/item/ectoplasm = 1)
AIStatus = AI_OFF
+ hud_type = /datum/hud/guardian
dextrous_hud_type = /datum/hud/dextrous/guardian //if we're set to dextrous, account for it.
var/list/guardian_overlays[GUARDIAN_TOTAL_LAYERS]
var/reset = 0 //if the summoner has reset the guardian already
@@ -370,7 +371,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
var/link = FOLLOW_LINK(M, src)
to_chat(M, "[link] [my_message]")
- log_talk(src,"GUARDIAN:[key_name(src)]: [input]",LOGSAY)
+ src.log_talk(input, LOG_SAY, tag="guardian")
/mob/living/proc/guardian_comm()
set name = "Communicate"
@@ -392,7 +393,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
var/link = FOLLOW_LINK(M, src)
to_chat(M, "[link] [my_message]")
- log_talk(src,"GUARDIAN:[key_name(src)]: [input]",LOGSAY)
+ src.log_talk(input, LOG_SAY, tag="guardian")
//FORCE RECALL/RESET
diff --git a/code/modules/mob/living/simple_animal/guardian/types/explosive.dm b/code/modules/mob/living/simple_animal/guardian/types/explosive.dm
index fc25ec5ac6..8fb1de18df 100644
--- a/code/modules/mob/living/simple_animal/guardian/types/explosive.dm
+++ b/code/modules/mob/living/simple_animal/guardian/types/explosive.dm
@@ -81,7 +81,7 @@
else
to_chat(user, "
[src] glows with a strange light , and you don't touch it. ")
-/obj/guardian_bomb/Collide(atom/A)
+/obj/guardian_bomb/Bump(atom/A)
detonate(A)
..()
diff --git a/code/modules/mob/living/simple_animal/guardian/types/fire.dm b/code/modules/mob/living/simple_animal/guardian/types/fire.dm
index 8f456abc53..7a469dd12c 100644
--- a/code/modules/mob/living/simple_animal/guardian/types/fire.dm
+++ b/code/modules/mob/living/simple_animal/guardian/types/fire.dm
@@ -27,11 +27,11 @@
..()
collision_ignite(AM)
-/mob/living/simple_animal/hostile/guardian/fire/CollidedWith(atom/movable/AM)
+/mob/living/simple_animal/hostile/guardian/fire/Bumped(atom/movable/AM)
..()
collision_ignite(AM)
-/mob/living/simple_animal/hostile/guardian/fire/Collide(AM as mob|obj)
+/mob/living/simple_animal/hostile/guardian/fire/Bump(AM as mob|obj)
..()
collision_ignite(AM)
diff --git a/code/modules/mob/living/simple_animal/hostile/alien.dm b/code/modules/mob/living/simple_animal/hostile/alien.dm
index 762b662de7..3d92912f9c 100644
--- a/code/modules/mob/living/simple_animal/hostile/alien.dm
+++ b/code/modules/mob/living/simple_animal/hostile/alien.dm
@@ -36,6 +36,8 @@
death_sound = 'sound/voice/hiss6.ogg'
deathmessage = "lets out a waning guttural screech, green blood bubbling from its maw..."
+ do_footstep = TRUE
+
/mob/living/simple_animal/hostile/alien/drone
name = "alien drone"
icon_state = "aliend"
diff --git a/code/modules/mob/living/simple_animal/hostile/bear.dm b/code/modules/mob/living/simple_animal/hostile/bear.dm
index 8573c3b7cb..acd8da9618 100644
--- a/code/modules/mob/living/simple_animal/hostile/bear.dm
+++ b/code/modules/mob/living/simple_animal/hostile/bear.dm
@@ -39,6 +39,8 @@
faction = list("russian")
gold_core_spawnable = HOSTILE_SPAWN
+ do_footstep = TRUE
+
//SPACE BEARS! SQUEEEEEEEE~ OW! FUCK! IT BIT MY HAND OFF!!
/mob/living/simple_animal/hostile/bear/Hudson
name = "Hudson"
@@ -81,6 +83,7 @@
icon_state = "bear_armor_upgrade"
/obj/item/bear_armor/afterattack(atom/target, mob/user, proximity_flag)
+ . = ..()
if(istype(target, /mob/living/simple_animal/hostile/bear) && proximity_flag)
var/mob/living/simple_animal/hostile/bear/A = target
if(A.armored)
diff --git a/code/modules/mob/living/simple_animal/hostile/bees.dm b/code/modules/mob/living/simple_animal/hostile/bees.dm
index d8edf7fe67..c7b46e6aed 100644
--- a/code/modules/mob/living/simple_animal/hostile/bees.dm
+++ b/code/modules/mob/living/simple_animal/hostile/bees.dm
@@ -209,14 +209,14 @@
var/datum/reagent/R = pick(typesof(/datum/reagent/toxin))
assign_reagent(GLOB.chemical_reagents_list[initial(R.id)])
- /mob/living/simple_animal/hostile/poison/bees/queen
- name = "queen bee"
- desc = "She's the queen of bees, BZZ BZZ!"
- icon_base = "queen"
- isqueen = TRUE
+/mob/living/simple_animal/hostile/poison/bees/queen
+ name = "queen bee"
+ desc = "She's the queen of bees, BZZ BZZ!"
+ icon_base = "queen"
+ isqueen = TRUE
- //the Queen doesn't leave the box on her own, and she CERTAINLY doesn't pollinate by herself
+//the Queen doesn't leave the box on her own, and she CERTAINLY doesn't pollinate by herself
/mob/living/simple_animal/hostile/poison/bees/queen/Found(atom/A)
return FALSE
@@ -295,3 +295,10 @@
forceMove(beehome.drop_location())
else
..()
+
+/mob/living/simple_animal/hostile/poison/bees/short
+ desc = "These bees seem unstable and won't survive for long."
+
+/mob/living/simple_animal/hostile/poison/bees/short/Initialize()
+ . = ..()
+ addtimer(CALLBACK(src, .proc/death), 50 SECONDS)
diff --git a/code/modules/mob/living/simple_animal/hostile/bosses/boss.dm b/code/modules/mob/living/simple_animal/hostile/bosses/boss.dm
index 9ae70745cf..be88394692 100644
--- a/code/modules/mob/living/simple_animal/hostile/bosses/boss.dm
+++ b/code/modules/mob/living/simple_animal/hostile/bosses/boss.dm
@@ -65,7 +65,7 @@
return 0
if(boss)
if(say_when_triggered)
- boss.say(say_when_triggered)
+ boss.say(say_when_triggered, forced = "boss action")
if(!boss.atb.spend(boss_cost))
return 0
diff --git a/code/modules/mob/living/simple_animal/hostile/bosses/paperwizard.dm b/code/modules/mob/living/simple_animal/hostile/bosses/paperwizard.dm
index 1b766a51d6..d31af79c19 100644
--- a/code/modules/mob/living/simple_animal/hostile/bosses/paperwizard.dm
+++ b/code/modules/mob/living/simple_animal/hostile/bosses/paperwizard.dm
@@ -23,6 +23,8 @@
attack_sound = 'sound/hallucinations/growl1.ogg'
var/list/copies = list()
+ do_footstep = TRUE
+
//Summon Ability
//Lets the wizard summon his art to fight for him
diff --git a/code/modules/mob/living/simple_animal/hostile/faithless.dm b/code/modules/mob/living/simple_animal/hostile/faithless.dm
index d267da624f..4e7cb0ac70 100644
--- a/code/modules/mob/living/simple_animal/hostile/faithless.dm
+++ b/code/modules/mob/living/simple_animal/hostile/faithless.dm
@@ -34,6 +34,8 @@
faction = list("faithless")
gold_core_spawnable = HOSTILE_SPAWN
+ do_footstep = TRUE
+
/mob/living/simple_animal/hostile/faithless/AttackingTarget()
. = ..()
if(. && prob(12) && iscarbon(target))
diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm
index 1df3053715..cfdf302d6b 100644
--- a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm
+++ b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm
@@ -52,6 +52,8 @@
var/datum/action/innate/spider/lay_web/lay_web
var/directive = "" //Message passed down to children, to relay the creator's orders
+ do_footstep = TRUE
+
/mob/living/simple_animal/hostile/poison/giant_spider/Initialize()
. = ..()
lay_web = new
@@ -80,7 +82,7 @@
humanize_spider(user)
/mob/living/simple_animal/hostile/poison/giant_spider/proc/humanize_spider(mob/user)
- if(key || !playable_spider)//Someone is in it or the fun police are shutting it down
+ if(key || !playable_spider || stat)//Someone is in it, it's dead, or the fun police are shutting it down
return 0
var/spider_ask = alert("Become a spider?", "Are you australian?", "Yes", "No")
if(spider_ask == "No" || !src || QDELETED(src))
@@ -520,7 +522,7 @@
for(var/M in GLOB.dead_mob_list)
var/link = FOLLOW_LINK(M, user)
to_chat(M, "[link] [my_message]")
- log_talk(user, "SPIDERCOMMAND: [key_name(user)] : [message]",LOGSAY)
+ usr.log_talk(message, LOG_SAY, tag="spider command")
/mob/living/simple_animal/hostile/poison/giant_spider/handle_temperature_damage()
if(bodytemperature < minbodytemp)
diff --git a/code/modules/mob/living/simple_animal/hostile/gorilla/emotes.dm b/code/modules/mob/living/simple_animal/hostile/gorilla/emotes.dm
index 3af442930e..100db06174 100644
--- a/code/modules/mob/living/simple_animal/hostile/gorilla/emotes.dm
+++ b/code/modules/mob/living/simple_animal/hostile/gorilla/emotes.dm
@@ -7,5 +7,5 @@
key_third_person = "oogas"
message = "oogas."
message_param = "oogas at %t."
- sound = "sound/creatures/gorilla.ogg"
+ sound = 'sound/creatures/gorilla.ogg'
diff --git a/code/modules/mob/living/simple_animal/hostile/gorilla/gorilla.dm b/code/modules/mob/living/simple_animal/hostile/gorilla/gorilla.dm
index 214ced3613..5d1db8d35e 100644
--- a/code/modules/mob/living/simple_animal/hostile/gorilla/gorilla.dm
+++ b/code/modules/mob/living/simple_animal/hostile/gorilla/gorilla.dm
@@ -38,6 +38,8 @@
var/list/gorilla_overlays[GORILLA_TOTAL_LAYERS]
var/oogas = 0
+ do_footstep = TRUE
+
// Gorillas like to dismember limbs from unconcious mobs.
// Returns null when the target is not an unconcious carbon mob; a list of limbs (possibly empty) otherwise.
/mob/living/simple_animal/hostile/gorilla/proc/target_bodyparts(atom/the_target)
@@ -92,7 +94,7 @@
/mob/living/simple_animal/hostile/gorilla/handle_automated_speech(override)
if(speak_chance && (override || prob(speak_chance)))
- playsound(src, "sound/creatures/gorilla.ogg", 200)
+ playsound(src, 'sound/creatures/gorilla.ogg', 200)
..()
/mob/living/simple_animal/hostile/gorilla/can_use_guns(obj/item/G)
@@ -103,6 +105,6 @@
/mob/living/simple_animal/hostile/gorilla/proc/oogaooga()
oogas++
if(oogas >= rand(2,6))
- playsound(src, "sound/creatures/gorilla.ogg", 200)
+ playsound(src, 'sound/creatures/gorilla.ogg', 200)
oogas = 0
diff --git a/code/modules/mob/living/simple_animal/hostile/hivebot.dm b/code/modules/mob/living/simple_animal/hostile/hivebot.dm
index 3f0c468d26..93284fe666 100644
--- a/code/modules/mob/living/simple_animal/hostile/hivebot.dm
+++ b/code/modules/mob/living/simple_animal/hostile/hivebot.dm
@@ -29,6 +29,8 @@
del_on_death = 1
loot = list(/obj/effect/decal/cleanable/robot_debris)
+ do_footstep = TRUE
+
/mob/living/simple_animal/hostile/hivebot/Initialize()
. = ..()
deathmessage = "[src] blows apart!"
diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm
index acb3550454..d8635060ee 100644
--- a/code/modules/mob/living/simple_animal/hostile/hostile.dm
+++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm
@@ -4,9 +4,16 @@
obj_damage = 40
environment_smash = ENVIRONMENT_SMASH_STRUCTURES //Bitflags. Set to ENVIRONMENT_SMASH_STRUCTURES to break closets,tables,racks, etc; ENVIRONMENT_SMASH_WALLS for walls; ENVIRONMENT_SMASH_RWALLS for rwalls
var/atom/target
- var/ranged = 0
+ var/ranged = FALSE
var/rapid = 0 //How many shots per volley.
var/rapid_fire_delay = 2 //Time between rapid fire shots
+
+ var/dodging = FALSE
+ var/approaching_target = FALSE //We should dodge now
+ var/in_melee = FALSE //We should sidestep now
+ var/dodge_prob = 30
+ var/sidestep_per_cycle = 1 //How many sidesteps per npcpool cycle when in melee
+
var/projectiletype //set ONLY it and NULLIFY casingtype var, if we have ONLY projectile
var/projectilesound
var/casingtype //set ONLY it and NULLIFY projectiletype, if we have projectile IN CASING
@@ -15,6 +22,9 @@
var/list/emote_taunt = list()
var/taunt_chance = 0
+ var/rapid_melee = 1 //Number of melee attacks between each npc pool tick. Spread evenly.
+ var/melee_queue_distance = 4 //If target is close enough start preparing to hit them if we have rapid_melee enabled
+
var/ranged_message = "fires" //Fluff text for ranged mobs
var/ranged_cooldown = 0 //What the current cooldown on ranged attacks is, generally world.time + ranged_cooldown_time
var/ranged_cooldown_time = 30 //How long, in deciseconds, the cooldown of ranged attacks is
@@ -41,7 +51,6 @@
var/lose_patience_timer_id //id for a timer to call LoseTarget(), used to stop mobs fixating on a target they can't reach
var/lose_patience_timeout = 300 //30 seconds by default, so there's no major changes to AI behaviour, beyond actually bailing if stuck forever
-
/mob/living/simple_animal/hostile/Initialize()
. = ..()
@@ -76,6 +85,34 @@
toggle_ai(AI_IDLE) // otherwise we go idle
return 1
+/mob/living/simple_animal/hostile/handle_automated_movement()
+ . = ..()
+ if(dodging && target && in_melee && isturf(loc) && isturf(target.loc))
+ var/datum/cb = CALLBACK(src,.proc/sidestep)
+ if(sidestep_per_cycle > 1) //For more than one just spread them equally - this could changed to some sensible distribution later
+ var/sidestep_delay = SSnpcpool.wait / sidestep_per_cycle
+ for(var/i in 1 to sidestep_per_cycle)
+ addtimer(cb, (i - 1)*sidestep_delay)
+ else //Otherwise randomize it to make the players guessing.
+ addtimer(cb,rand(1,SSnpcpool.wait))
+
+/mob/living/simple_animal/hostile/proc/sidestep()
+ if(!target || !isturf(target.loc) || !isturf(loc) || stat == DEAD)
+ return
+ var/target_dir = get_dir(src,target)
+
+ var/static/list/cardinal_sidestep_directions = list(-90,-45,0,45,90)
+ var/static/list/diagonal_sidestep_directions = list(-45,0,45)
+ var/chosen_dir = 0
+ if (target_dir & (target_dir - 1))
+ chosen_dir = pick(diagonal_sidestep_directions)
+ else
+ chosen_dir = pick(cardinal_sidestep_directions)
+ if(chosen_dir)
+ chosen_dir = turn(target_dir,chosen_dir)
+ Move(get_step(src,chosen_dir))
+ face_atom(target) //Looks better if they keep looking at you when dodging
+
/mob/living/simple_animal/hostile/attacked_by(obj/item/I, mob/living/user)
if(stat == CONSCIOUS && !target && AIStatus != AI_OFF && !client && user)
FindTarget(list(user), 1)
@@ -221,7 +258,23 @@
Aggro()
return 1
-/mob/living/simple_animal/hostile/proc/MoveToTarget(var/list/possible_targets)//Step 5, handle movement between us and our target
+//What we do after closing in
+/mob/living/simple_animal/hostile/proc/MeleeAction(patience = TRUE)
+ if(rapid_melee > 1)
+ var/datum/callback/cb = CALLBACK(src, .proc/CheckAndAttack)
+ var/delay = SSnpcpool.wait / rapid_melee
+ for(var/i in 1 to rapid_melee)
+ addtimer(cb, (i - 1)*delay)
+ else
+ AttackingTarget()
+ if(patience)
+ GainPatience()
+
+/mob/living/simple_animal/hostile/proc/CheckAndAttack()
+ if(target && targets_from && isturf(targets_from.loc) && target.Adjacent(targets_from) && !incapacitated())
+ AttackingTarget()
+
+/mob/living/simple_animal/hostile/proc/MoveToTarget(list/possible_targets)//Step 5, handle movement between us and our target
stop_automated_movement = 1
if(!target || !CanAttack(target))
LoseTarget()
@@ -247,8 +300,11 @@
Goto(target,move_to_delay,minimum_distance)
if(target)
if(targets_from && isturf(targets_from.loc) && target.Adjacent(targets_from)) //If they're next to us, attack
- AttackingTarget()
- GainPatience()
+ MeleeAction()
+ else
+ if(rapid_melee > 1 && target_distance <= melee_queue_distance)
+ MeleeAction(FALSE)
+ in_melee = FALSE //If we're just preparing to strike do not enter sidestep mode
return 1
return 0
if(environment_smash)
@@ -266,6 +322,10 @@
return 0
/mob/living/simple_animal/hostile/proc/Goto(target, delay, minimum_distance)
+ if(target == src.target)
+ approaching_target = TRUE
+ else
+ approaching_target = FALSE
walk_to(src, target, minimum_distance, delay)
/mob/living/simple_animal/hostile/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
@@ -282,6 +342,7 @@
/mob/living/simple_animal/hostile/proc/AttackingTarget()
+ in_melee = TRUE
return target.attack_animal(src)
/mob/living/simple_animal/hostile/proc/Aggro()
@@ -298,6 +359,8 @@
/mob/living/simple_animal/hostile/proc/LoseTarget()
target = null
+ approaching_target = FALSE
+ in_melee = FALSE
walk(src, 0)
LoseAggro()
@@ -331,6 +394,7 @@
return
visible_message("
[src] [ranged_message] at [A]!")
+
if(rapid > 1)
var/datum/callback/cb = CALLBACK(src, .proc/Shoot, A)
for(var/i in 1 to rapid)
@@ -367,6 +431,22 @@
return iswallturf(T) || ismineralturf(T)
+/mob/living/simple_animal/hostile/Move(atom/newloc, dir , step_x , step_y)
+ if(dodging && approaching_target && prob(dodge_prob) && moving_diagonally == 0 && isturf(loc) && isturf(newloc))
+ return dodge(newloc,dir)
+ else
+ return ..()
+
+/mob/living/simple_animal/hostile/proc/dodge(moving_to,move_direction)
+ //Assuming we move towards the target we want to swerve toward them to get closer
+ var/cdir = turn(move_direction,45)
+ var/ccdir = turn(move_direction,-45)
+ dodging = FALSE
+ . = Move(get_step(loc,pick(cdir,ccdir)))
+ if(!.)//Can't dodge there so we just carry on
+ . = Move(moving_to,move_direction)
+ dodging = TRUE
+
/mob/living/simple_animal/hostile/proc/DestroyObjectsInDirection(direction)
var/turf/T = get_step(targets_from, direction)
if(T && T.Adjacent(targets_from))
diff --git a/code/modules/mob/living/simple_animal/hostile/jungle/leaper.dm b/code/modules/mob/living/simple_animal/hostile/jungle/leaper.dm
index dbafe9451c..d1e8f1f49e 100644
--- a/code/modules/mob/living/simple_animal/hostile/jungle/leaper.dm
+++ b/code/modules/mob/living/simple_animal/hostile/jungle/leaper.dm
@@ -26,6 +26,8 @@
var/hop_cooldown = 0 //Strictly for player controlled leapers
var/projectile_ready = FALSE //Stopping AI leapers from firing whenever they want, and only doing it after a hop has finished instead
+ do_footstep = TRUE
+
/obj/item/projectile/leaper
name = "leaper bubble"
icon_state = "leaper"
@@ -67,7 +69,6 @@
desc = "A small pool of sludge, containing trace amounts of leaper venom."
icon = 'icons/effects/tomatodecal.dmi'
icon_state = "tomato_floor1"
- beauty = -200
/obj/structure/leaper_bubble
name = "leaper bubble"
@@ -204,7 +205,7 @@
if(AIStatus == AI_ON && ranged_cooldown <= world.time)
projectile_ready = TRUE
update_icons()
- throw_at(new_turf, max(3,get_dist(src,new_turf)), 1, src, FALSE, callback = CALLBACK(src, .FinishHop))
+ throw_at(new_turf, max(3,get_dist(src,new_turf)), 1, src, FALSE, callback = CALLBACK(src, .proc/FinishHop))
/mob/living/simple_animal/hostile/jungle/leaper/proc/FinishHop()
density = TRUE
diff --git a/code/modules/mob/living/simple_animal/hostile/jungle/mega_arachnid.dm b/code/modules/mob/living/simple_animal/hostile/jungle/mega_arachnid.dm
index 616c9025b9..607db5d54f 100644
--- a/code/modules/mob/living/simple_animal/hostile/jungle/mega_arachnid.dm
+++ b/code/modules/mob/living/simple_animal/hostile/jungle/mega_arachnid.dm
@@ -24,6 +24,8 @@
projectilesound = 'sound/weapons/pierce.ogg'
alpha = 50
+ do_footstep = TRUE
+
/mob/living/simple_animal/hostile/jungle/mega_arachnid/Life()
..()
if(target && ranged_cooldown > world.time && iscarbon(target))
diff --git a/code/modules/mob/living/simple_animal/hostile/jungle/mook.dm b/code/modules/mob/living/simple_animal/hostile/jungle/mook.dm
index 1fde4a9cd4..29f48c7295 100644
--- a/code/modules/mob/living/simple_animal/hostile/jungle/mook.dm
+++ b/code/modules/mob/living/simple_animal/hostile/jungle/mook.dm
@@ -31,6 +31,8 @@
var/attack_state = MOOK_ATTACK_NEUTRAL
var/struck_target_leap = FALSE
+ do_footstep = TRUE
+
/mob/living/simple_animal/hostile/jungle/mook/CanPass(atom/movable/O)
if(istype(O, /mob/living/simple_animal/hostile/jungle/mook))
var/mob/living/simple_animal/hostile/jungle/mook/M = O
diff --git a/code/modules/mob/living/simple_animal/hostile/jungle/seedling.dm b/code/modules/mob/living/simple_animal/hostile/jungle/seedling.dm
index 5b634f8ae1..1a894734d8 100644
--- a/code/modules/mob/living/simple_animal/hostile/jungle/seedling.dm
+++ b/code/modules/mob/living/simple_animal/hostile/jungle/seedling.dm
@@ -47,7 +47,7 @@
hitsound_wall = 'sound/weapons/effects/searwall.ogg'
nondirectional_sprite = TRUE
-/obj/item/projectile/seedling/Collide(atom/A)//Stops seedlings from destroying other jungle mobs through FF
+/obj/item/projectile/seedling/Bump(atom/A)//Stops seedlings from destroying other jungle mobs through FF
if(isliving(A))
var/mob/living/L = A
if("jungle" in L.faction)
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm
index 3895951b3c..97dede3d2f 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm
@@ -53,6 +53,8 @@ Difficulty: Medium
deathmessage = "falls to the ground, decaying into glowing particles."
death_sound = "bodyfall"
+ do_footstep = TRUE
+
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/guidance
guidance = TRUE
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm
index 7d03ad4b9e..3f68c15dc2 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm
@@ -52,6 +52,8 @@ Difficulty: Hard
deathmessage = "sinks into a pool of blood, fleeing the battle. You've won, for now... "
death_sound = 'sound/magic/enter_blood.ogg'
+ do_footstep = TRUE
+
/obj/item/gps/internal/bubblegum
icon_state = null
gpstag = "Bloody Signal"
@@ -147,7 +149,7 @@ Difficulty: Hard
Goto(target, move_to_delay, minimum_distance)
-/mob/living/simple_animal/hostile/megafauna/bubblegum/Collide(atom/A)
+/mob/living/simple_animal/hostile/megafauna/bubblegum/Bump(atom/A)
if(charging)
if(isturf(A) || isobj(A) && A.density)
A.ex_act(EXPLODE_HEAVY)
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
index 19e2fda91d..2c5d004054 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
@@ -235,8 +235,6 @@ Difficulty: Very Hard
desc = "A completely indestructible chunk of crystal, rumoured to predate the start of this universe. It looks like you could store things inside it."
icon = 'icons/obj/lavaland/artefacts.dmi'
icon_state = "blackbox"
- icon_on = "blackbox"
- icon_off = "blackbox"
light_range = 8
max_n_of_items = INFINITY
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
@@ -379,7 +377,7 @@ Difficulty: Very Hard
to_chat(user, "It is activated by [activation_method].")
/obj/machinery/anomalous_crystal/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, spans, message_mode)
- . = ..()
+ ..()
if(isliving(speaker))
ActivationReaction(speaker, ACTIVATE_SPEECH)
@@ -414,7 +412,7 @@ Difficulty: Very Hard
playsound(user, activation_sound, 100, 1)
return TRUE
-/obj/machinery/anomalous_crystal/CollidedWith(atom/movable/AM)
+/obj/machinery/anomalous_crystal/Bumped(atom/movable/AM)
..()
if(ismob(AM))
ActivationReaction(AM, ACTIVATE_MOB_BUMP)
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm
index 9aee15d9b6..fac14eeb0e 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm
@@ -63,6 +63,8 @@ Difficulty: Medium
death_sound = 'sound/magic/demon_dies.ogg'
var/datum/action/small_sprite/smallsprite = new/datum/action/small_sprite/drake()
+ do_footstep = TRUE
+
/mob/living/simple_animal/hostile/megafauna/dragon/Initialize()
smallsprite.Grant(src)
. = ..()
@@ -243,7 +245,7 @@ Difficulty: Medium
playsound(loc, 'sound/effects/meteorimpact.ogg', 200, 1)
for(var/mob/living/L in orange(1, src))
if(L.stat)
- visible_message("
[src] slams down on [L], crushing them! ")
+ visible_message("
[src] slams down on [L], crushing [L.p_them()]! ")
L.gib()
else
L.adjustBruteLoss(75)
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm
index bc873e68e7..63b18ad82d 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm
@@ -641,7 +641,7 @@ Difficulty: Hard
H.Goto(get_turf(caster), H.move_to_delay, 3)
if(monster_damage_boost && (ismegafauna(L) || istype(L, /mob/living/simple_animal/hostile/asteroid)))
L.adjustBruteLoss(damage)
- add_logs(caster, L, "struck with a [name]")
+ log_combat(caster, L, "struck with a [name]")
for(var/obj/mecha/M in T.contents - hit_things) //also damage mechs.
hit_things += M
if(M.occupant)
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm
index a963e96624..2710d58a67 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm
@@ -104,7 +104,7 @@ Difficulty: Medium
adjustHealth(-maxHealth) //heal ourself to full in prep for splitting
var/mob/living/simple_animal/hostile/megafauna/legion/L = new(loc)
- L.maxHealth = maxHealth * 0.6
+ L.maxHealth = round(maxHealth * 0.6,DAMAGE_PRECISION)
maxHealth = L.maxHealth
L.health = L.maxHealth
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm
index b3cba7e611..9dc51ac950 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm
@@ -68,8 +68,8 @@
else
..()
-/mob/living/simple_animal/hostile/megafauna/dust()
- if(health > 0)
+/mob/living/simple_animal/hostile/megafauna/dust(just_ash, drop_items, force)
+ if(!force && health > 0)
return
else
..()
@@ -83,11 +83,8 @@
if(L.stat != DEAD)
if(!client && ranged && ranged_cooldown <= world.time)
OpenFire()
- else if(L.stat >= SOFT_CRIT)
- if(vore_active == TRUE && L.devourable == TRUE)
- dragon_feeding(src,L)
- else if(L.stat == DEAD)
- devour(L)
+ else
+ devour(L)
/mob/living/simple_animal/hostile/megafauna/proc/devour(mob/living/L)
if(!L)
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/goliath.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/goliath.dm
index 0c660dd1cd..f74f9a436c 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/goliath.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/goliath.dm
@@ -33,6 +33,8 @@
var/pre_attack_icon = "Goliath_preattack"
loot = list(/obj/item/stack/sheet/animalhide/goliath_hide)
+ do_footstep = TRUE
+
/mob/living/simple_animal/hostile/asteroid/goliath/Life()
. = ..()
handle_preattack()
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/hivelord.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/hivelord.dm
index 32c7ba703b..73356658a3 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/hivelord.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/hivelord.dm
@@ -365,7 +365,7 @@
suit_store = /obj/item/tome
r_pocket = /obj/item/restraints/legcuffs/bola/cult
l_pocket = /obj/item/melee/cultblade/dagger
- glasses = /obj/item/clothing/glasses/night/cultblind
+ glasses = /obj/item/clothing/glasses/hud/health/night/cultblind
backpack_contents = list(/obj/item/reagent_containers/glass/beaker/unholywater = 1, /obj/item/cult_shift = 1, /obj/item/flashlight/flare/culttorch = 1, /obj/item/stack/sheet/runed_metal = 15)
if("Lavaknight") //START OF CIT CHANGE
uniform = /obj/item/clothing/under/assistantformal
diff --git a/code/modules/mob/living/simple_animal/hostile/nanotrasen.dm b/code/modules/mob/living/simple_animal/hostile/nanotrasen.dm
index b170ae0574..543ffe6131 100644
--- a/code/modules/mob/living/simple_animal/hostile/nanotrasen.dm
+++ b/code/modules/mob/living/simple_animal/hostile/nanotrasen.dm
@@ -31,6 +31,8 @@
speak = list("Stop resisting!", "I AM THE LAW!", "Face the wrath of the golden bolt!", "Stop breaking the law, asshole!")
search_objects = 1
+ do_footstep = TRUE
+
/mob/living/simple_animal/hostile/nanotrasen/Aggro()
..()
diff --git a/code/modules/mob/living/simple_animal/hostile/netherworld.dm b/code/modules/mob/living/simple_animal/hostile/netherworld.dm
index 0991c8922a..8210fd6490 100644
--- a/code/modules/mob/living/simple_animal/hostile/netherworld.dm
+++ b/code/modules/mob/living/simple_animal/hostile/netherworld.dm
@@ -34,9 +34,9 @@
/mob/living/simple_animal/hostile/netherworld/migo/Initialize()
. = ..()
- migo_sounds = list('sound/items/bubblewrap.ogg', 'sound/items/change_jaws.ogg', 'sound/items/crowbar.ogg', 'sound/items/drink.ogg', 'sound/items/deconstruct.ogg', 'sound/items/carhorn.ogg', 'sound/items/change_drill.ogg', 'sound/items/dodgeball.ogg', 'sound/items/eatfood.ogg', 'sound/items/megaphone.ogg', 'sound/items/screwdriver.ogg', 'sound/items/weeoo1.ogg', 'sound/items/wirecutter.ogg', 'sound/items/welder.ogg', 'sound/items/zip.ogg', 'sound/items/rped.ogg', 'sound/items/ratchet.ogg', 'sound/items/polaroid1.ogg', 'sound/items/pshoom.ogg', 'sound/items/airhorn.ogg', 'sound/items/geiger/high1.ogg', 'sound/items/geiger/high2.ogg', 'sound/voice/bcreep.ogg', 'sound/voice/biamthelaw.ogg', 'sound/voice/ed209_20sec.ogg', 'sound/voice/hiss3.ogg', 'sound/voice/hiss6.ogg', 'sound/voice/mpatchedup.ogg', 'sound/voice/mfeelbetter.ogg', 'sound/voice/human/manlaugh1.ogg', 'sound/voice/human/womanlaugh.ogg', 'sound/weapons/sear.ogg', 'sound/ambience/antag/clockcultalr.ogg', 'sound/ambience/antag/ling_aler.ogg', 'sound/ambience/antag/tatoralert.ogg', 'sound/ambience/antag/monkey.ogg', 'sound/mecha/nominal.ogg', 'sound/mecha/weapdestr.ogg', 'sound/mecha/critdestr.ogg', 'sound/mecha/imag_enh.ogg', 'sound/effects/adminhelp.ogg', 'sound/effects/alert.ogg', 'sound/effects/attackblob.ogg', 'sound/effects/bamf.ogg', 'sound/effects/blobattack.ogg', 'sound/effects/break_stone.ogg', 'sound/effects/bubbles.ogg', 'sound/effects/bubbles2.ogg', 'sound/effects/clang.ogg', 'sound/effects/clockcult_gateway_disrupted.ogg', 'sound/effects/clownstep2.ogg', 'sound/effects/curse1.ogg', 'sound/effects/dimensional_rend.ogg', 'sound/effects/doorcreaky.ogg', 'sound/effects/empulse.ogg', 'sound/effects/explosion_distant.ogg', 'sound/effects/explosionfar.ogg', 'sound/effects/explosion1.ogg', 'sound/effects/grillehit.ogg', 'sound/effects/genetics.ogg', 'sound/effects/heart_beat.ogg', 'sound/effects/hyperspace_begin.ogg', 'sound/effects/hyperspace_end.ogg', 'sound/effects/his_grace_awaken.ogg', 'sound/effects/pai_boot.ogg', 'sound/effects/phasein.ogg', 'sound/effects/picaxe1.ogg', 'sound/effects/ratvar_reveal.ogg', 'sound/effects/sparks1.ogg', 'sound/effects/smoke.ogg', 'sound/effects/splat.ogg', 'sound/effects/snap.ogg', 'sound/effects/tendril_destroyed.ogg', 'sound/effects/supermatter.ogg', 'sound/misc/desceration-01.ogg', 'sound/misc/desceration-02.ogg', 'sound/misc/desceration-03.ogg', 'sound/misc/bloblarm.ogg', 'sound/misc/airraid.ogg', 'sound/misc/bang.ogg','sound/misc/highlander.ogg', 'sound/misc/interference.ogg', 'sound/misc/notice1.ogg', 'sound/misc/notice2.ogg', 'sound/misc/sadtrombone.ogg', 'sound/misc/slip.ogg', 'sound/misc/splort.ogg', 'sound/weapons/armbomb.ogg', 'sound/weapons/beam_sniper.ogg', 'sound/weapons/chainsawhit.ogg', 'sound/weapons/emitter.ogg', 'sound/weapons/emitter2.ogg', 'sound/weapons/blade1.ogg', 'sound/weapons/bladeslice.ogg', 'sound/weapons/blastcannon.ogg', 'sound/weapons/blaster.ogg', 'sound/weapons/bulletflyby3.ogg', 'sound/weapons/circsawhit.ogg', 'sound/weapons/cqchit2.ogg', 'sound/weapons/drill.ogg', 'sound/weapons/genhit1.ogg', 'sound/weapons/gunshot_silenced.ogg', 'sound/weapons/gunshot2.ogg', 'sound/weapons/handcuffs.ogg', 'sound/weapons/homerun.ogg', 'sound/weapons/kenetic_accel.ogg', 'sound/machines/clockcult/steam_whoosh.ogg', 'sound/machines/fryer/deep_fryer_emerge.ogg', 'sound/machines/airlock.ogg', 'sound/machines/airlock_alien_prying.ogg', 'sound/machines/airlockclose.ogg', 'sound/machines/airlockforced.ogg', 'sound/machines/airlockopen.ogg', 'sound/machines/alarm.ogg', 'sound/machines/blender.ogg', 'sound/machines/boltsdown.ogg', 'sound/machines/boltsup.ogg', 'sound/machines/buzz-sigh.ogg', 'sound/machines/buzz-two.ogg', 'sound/machines/chime.ogg', 'sound/machines/cryo_warning.ogg', 'sound/machines/defib_charge.ogg', 'sound/machines/defib_failed.ogg', 'sound/machines/defib_ready.ogg', 'sound/machines/defib_zap.ogg', 'sound/machines/deniedbeep.ogg', 'sound/machines/ding.ogg', 'sound/machines/disposalflush.ogg', 'sound/machines/door_close.ogg', 'sound/machines/door_open.ogg', 'sound/machines/engine_alert1.ogg', 'sound/machines/engine_alert2.ogg', 'sound/machines/hiss.ogg', 'sound/machines/honkbot_evil_laugh.ogg', 'sound/machines/juicer.ogg', 'sound/machines/ping.ogg', 'sound/machines/signal.ogg', 'sound/machines/synth_no.ogg', 'sound/machines/synth_yes.ogg', 'sound/machines/terminal_alert.ogg', 'sound/machines/triple_beep.ogg', 'sound/machines/twobeep.ogg', 'sound/machines/ventcrawl.ogg', 'sound/machines/warning-buzzer.ogg', 'sound/ai/outbreak5.ogg', 'sound/ai/outbreak7.ogg', 'sound/ai/poweroff.ogg', 'sound/ai/radiation.ogg', 'sound/ai/shuttlecalled.ogg', 'sound/ai/shuttledock.ogg', 'sound/ai/shuttlerecalled.ogg', 'sound/ai/aimalf.ogg') //hahahaha fuck you code divers
+ migo_sounds = list('sound/items/bubblewrap.ogg', 'sound/items/change_jaws.ogg', 'sound/items/crowbar.ogg', 'sound/items/drink.ogg', 'sound/items/deconstruct.ogg', 'sound/items/carhorn.ogg', 'sound/items/change_drill.ogg', 'sound/items/dodgeball.ogg', 'sound/items/eatfood.ogg', 'sound/items/megaphone.ogg', 'sound/items/screwdriver.ogg', 'sound/items/weeoo1.ogg', 'sound/items/wirecutter.ogg', 'sound/items/welder.ogg', 'sound/items/zip.ogg', 'sound/items/rped.ogg', 'sound/items/ratchet.ogg', 'sound/items/polaroid1.ogg', 'sound/items/pshoom.ogg', 'sound/items/airhorn.ogg', 'sound/items/geiger/high1.ogg', 'sound/items/geiger/high2.ogg', 'sound/voice/beepsky/creep.ogg', 'sound/voice/beepsky/iamthelaw.ogg', 'sound/voice/ed209_20sec.ogg', 'sound/voice/hiss3.ogg', 'sound/voice/hiss6.ogg', 'sound/voice/medbot/patchedup.ogg', 'sound/voice/medbot/feelbetter.ogg', 'sound/voice/human/manlaugh1.ogg', 'sound/voice/human/womanlaugh.ogg', 'sound/weapons/sear.ogg', 'sound/ambience/antag/clockcultalr.ogg', 'sound/ambience/antag/ling_aler.ogg', 'sound/ambience/antag/tatoralert.ogg', 'sound/ambience/antag/monkey.ogg', 'sound/mecha/nominal.ogg', 'sound/mecha/weapdestr.ogg', 'sound/mecha/critdestr.ogg', 'sound/mecha/imag_enh.ogg', 'sound/effects/adminhelp.ogg', 'sound/effects/alert.ogg', 'sound/effects/attackblob.ogg', 'sound/effects/bamf.ogg', 'sound/effects/blobattack.ogg', 'sound/effects/break_stone.ogg', 'sound/effects/bubbles.ogg', 'sound/effects/bubbles2.ogg', 'sound/effects/clang.ogg', 'sound/effects/clockcult_gateway_disrupted.ogg', 'sound/effects/clownstep2.ogg', 'sound/effects/curse1.ogg', 'sound/effects/dimensional_rend.ogg', 'sound/effects/doorcreaky.ogg', 'sound/effects/empulse.ogg', 'sound/effects/explosion_distant.ogg', 'sound/effects/explosionfar.ogg', 'sound/effects/explosion1.ogg', 'sound/effects/grillehit.ogg', 'sound/effects/genetics.ogg', 'sound/effects/heart_beat.ogg', 'sound/effects/hyperspace_begin.ogg', 'sound/effects/hyperspace_end.ogg', 'sound/effects/his_grace_awaken.ogg', 'sound/effects/pai_boot.ogg', 'sound/effects/phasein.ogg', 'sound/effects/picaxe1.ogg', 'sound/effects/ratvar_reveal.ogg', 'sound/effects/sparks1.ogg', 'sound/effects/smoke.ogg', 'sound/effects/splat.ogg', 'sound/effects/snap.ogg', 'sound/effects/tendril_destroyed.ogg', 'sound/effects/supermatter.ogg', 'sound/misc/desceration-01.ogg', 'sound/misc/desceration-02.ogg', 'sound/misc/desceration-03.ogg', 'sound/misc/bloblarm.ogg', 'sound/misc/airraid.ogg', 'sound/misc/bang.ogg','sound/misc/highlander.ogg', 'sound/misc/interference.ogg', 'sound/misc/notice1.ogg', 'sound/misc/notice2.ogg', 'sound/misc/sadtrombone.ogg', 'sound/misc/slip.ogg', 'sound/misc/splort.ogg', 'sound/weapons/armbomb.ogg', 'sound/weapons/beam_sniper.ogg', 'sound/weapons/chainsawhit.ogg', 'sound/weapons/emitter.ogg', 'sound/weapons/emitter2.ogg', 'sound/weapons/blade1.ogg', 'sound/weapons/bladeslice.ogg', 'sound/weapons/blastcannon.ogg', 'sound/weapons/blaster.ogg', 'sound/weapons/bulletflyby3.ogg', 'sound/weapons/circsawhit.ogg', 'sound/weapons/cqchit2.ogg', 'sound/weapons/drill.ogg', 'sound/weapons/genhit1.ogg', 'sound/weapons/gunshot_silenced.ogg', 'sound/weapons/gunshot2.ogg', 'sound/weapons/handcuffs.ogg', 'sound/weapons/homerun.ogg', 'sound/weapons/kenetic_accel.ogg', 'sound/machines/clockcult/steam_whoosh.ogg', 'sound/machines/fryer/deep_fryer_emerge.ogg', 'sound/machines/airlock.ogg', 'sound/machines/airlock_alien_prying.ogg', 'sound/machines/airlockclose.ogg', 'sound/machines/airlockforced.ogg', 'sound/machines/airlockopen.ogg', 'sound/machines/alarm.ogg', 'sound/machines/blender.ogg', 'sound/machines/boltsdown.ogg', 'sound/machines/boltsup.ogg', 'sound/machines/buzz-sigh.ogg', 'sound/machines/buzz-two.ogg', 'sound/machines/chime.ogg', 'sound/machines/cryo_warning.ogg', 'sound/machines/defib_charge.ogg', 'sound/machines/defib_failed.ogg', 'sound/machines/defib_ready.ogg', 'sound/machines/defib_zap.ogg', 'sound/machines/deniedbeep.ogg', 'sound/machines/ding.ogg', 'sound/machines/disposalflush.ogg', 'sound/machines/door_close.ogg', 'sound/machines/door_open.ogg', 'sound/machines/engine_alert1.ogg', 'sound/machines/engine_alert2.ogg', 'sound/machines/hiss.ogg', 'sound/machines/honkbot_evil_laugh.ogg', 'sound/machines/juicer.ogg', 'sound/machines/ping.ogg', 'sound/machines/signal.ogg', 'sound/machines/synth_no.ogg', 'sound/machines/synth_yes.ogg', 'sound/machines/terminal_alert.ogg', 'sound/machines/triple_beep.ogg', 'sound/machines/twobeep.ogg', 'sound/machines/ventcrawl.ogg', 'sound/machines/warning-buzzer.ogg', 'sound/ai/outbreak5.ogg', 'sound/ai/outbreak7.ogg', 'sound/ai/poweroff.ogg', 'sound/ai/radiation.ogg', 'sound/ai/shuttlecalled.ogg', 'sound/ai/shuttledock.ogg', 'sound/ai/shuttlerecalled.ogg', 'sound/ai/aimalf.ogg') //hahahaha fuck you code divers
-/mob/living/simple_animal/hostile/netherworld/migo/say(message)
+/mob/living/simple_animal/hostile/netherworld/migo/say(message, bubble_type, var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
..()
if(stat)
return
diff --git a/code/modules/mob/living/simple_animal/hostile/pirate.dm b/code/modules/mob/living/simple_animal/hostile/pirate.dm
index f76357de1b..be40518d70 100644
--- a/code/modules/mob/living/simple_animal/hostile/pirate.dm
+++ b/code/modules/mob/living/simple_animal/hostile/pirate.dm
@@ -4,7 +4,7 @@
icon = 'icons/mob/simple_human.dmi'
icon_state = "piratemelee"
icon_living = "piratemelee"
- icon_dead = "piratemelee_dead"
+ icon_dead = "pirate_dead"
mob_biotypes = list(MOB_ORGANIC, MOB_HUMANOID)
speak_chance = 0
turns_per_move = 5
@@ -14,15 +14,12 @@
speed = 0
maxHealth = 100
health = 100
- spacewalk = TRUE
-
harm_intent_damage = 5
- obj_damage = 60
- melee_damage_lower = 30
- melee_damage_upper = 30
- attacktext = "slashes"
- attack_sound = 'sound/weapons/bladeslice.ogg'
-
+ melee_damage_lower = 10
+ melee_damage_upper = 10
+ attacktext = "punches"
+ attack_sound = 'sound/weapons/punch1.ogg'
+ a_intent = INTENT_HARM
atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0)
unsuitable_atmos_damage = 15
speak_emote = list("yarrs")
@@ -30,51 +27,65 @@
/obj/item/melee/transforming/energy/sword/pirate)
del_on_death = 1
faction = list("pirate")
+
+
+/mob/living/simple_animal/hostile/pirate/melee
+ name = "Pirate Swashbuckler"
+ icon_state = "piratemelee"
+ icon_living = "piratemelee"
+ icon_dead = "piratemelee_dead"
+ melee_damage_lower = 30
+ melee_damage_upper = 30
+ armour_penetration = 35
+ attacktext = "slashes"
+ attack_sound = 'sound/weapons/blade1.ogg'
var/obj/effect/light_emitter/red_energy_sword/sord
-/mob/living/simple_animal/hostile/pirate/Initialize()
+ do_footstep = TRUE
+
+/mob/living/simple_animal/hostile/pirate/melee/space
+ name = "Space Pirate Swashbuckler"
+ icon_state = "piratespace"
+ icon_living = "piratespace"
+ icon_dead = "piratespace_dead"
+ atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
+ minbodytemp = 0
+ speed = 1
+ spacewalk = TRUE
+
+/mob/living/simple_animal/hostile/pirate/melee/Initialize()
. = ..()
sord = new(src)
-/mob/living/simple_animal/hostile/pirate/Destroy()
+/mob/living/simple_animal/hostile/pirate/melee/Destroy()
QDEL_NULL(sord)
return ..()
+/mob/living/simple_animal/hostile/pirate/melee/Initialize()
+ . = ..()
+ set_light(2)
+
/mob/living/simple_animal/hostile/pirate/ranged
name = "Pirate Gunner"
icon_state = "pirateranged"
icon_living = "pirateranged"
- icon_dead = "piratemelee_dead"
+ icon_dead = "pirateranged_dead"
projectilesound = 'sound/weapons/laser.ogg'
ranged = 1
- rapid = 3
+ rapid = 2
+ rapid_fire_delay = 6
retreat_distance = 5
minimum_distance = 5
projectiletype = /obj/item/projectile/beam/laser
loot = list(/obj/effect/mob_spawn/human/corpse/pirate/ranged,
/obj/item/gun/energy/laser)
-/mob/living/simple_animal/hostile/pirate/space
- name = "Space Pirate"
- icon_state = "piratespace"
- icon_living = "piratespace"
- atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
- minbodytemp = 0
- speed = 1
-
-/mob/living/simple_animal/hostile/pirate/space/Initialize()
- . = ..()
- set_light(3)
-
-/mob/living/simple_animal/hostile/pirate/space/ranged
+/mob/living/simple_animal/hostile/pirate/ranged/space
name = "Space Pirate Gunner"
icon_state = "piratespaceranged"
icon_living = "piratespaceranged"
- projectilesound = 'sound/weapons/laser.ogg'
- ranged = 1
- rapid = 3
- retreat_distance = 5
- minimum_distance = 5
- projectiletype = /obj/item/projectile/beam/laser
- loot = list(/obj/effect/mob_spawn/human/corpse/pirate/ranged,
- /obj/item/gun/energy/laser)
+ icon_dead = "piratespaceranged_dead"
+ atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
+ minbodytemp = 0
+ speed = 1
+ spacewalk = TRUE
diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/clown.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/clown.dm
index cd7acc6f5a..cd978b7066 100644
--- a/code/modules/mob/living/simple_animal/hostile/retaliate/clown.dm
+++ b/code/modules/mob/living/simple_animal/hostile/retaliate/clown.dm
@@ -33,6 +33,8 @@
maxbodytemp = 370
unsuitable_atmos_damage = 10
+ do_footstep = TRUE
+
/mob/living/simple_animal/hostile/retaliate/clown/handle_temperature_damage()
if(bodytemperature < minbodytemp)
adjustBruteLoss(10)
diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/spaceman.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/spaceman.dm
index a132503786..c50ace8871 100644
--- a/code/modules/mob/living/simple_animal/hostile/retaliate/spaceman.dm
+++ b/code/modules/mob/living/simple_animal/hostile/retaliate/spaceman.dm
@@ -24,6 +24,8 @@
environment_smash = ENVIRONMENT_SMASH_NONE
del_on_death = 0
+ do_footstep = TRUE
+
/mob/living/simple_animal/hostile/retaliate/nanotrasenpeace //this should be in a different file
name = "Nanotrasen Private Security Officer"
desc = "An officer part of Nanotrasen's private security force."
diff --git a/code/modules/mob/living/simple_animal/hostile/russian.dm b/code/modules/mob/living/simple_animal/hostile/russian.dm
index c083b8fdd5..f66814f01f 100644
--- a/code/modules/mob/living/simple_animal/hostile/russian.dm
+++ b/code/modules/mob/living/simple_animal/hostile/russian.dm
@@ -29,6 +29,8 @@
status_flags = CANPUSH
del_on_death = 1
+ do_footstep = TRUE
+
/mob/living/simple_animal/hostile/russian/ranged
icon_state = "russianranged"
diff --git a/code/modules/mob/living/simple_animal/hostile/skeleton.dm b/code/modules/mob/living/simple_animal/hostile/skeleton.dm
index fca6afd4a6..d0ae01f443 100644
--- a/code/modules/mob/living/simple_animal/hostile/skeleton.dm
+++ b/code/modules/mob/living/simple_animal/hostile/skeleton.dm
@@ -34,6 +34,8 @@
del_on_death = 1
loot = list(/obj/effect/decal/remains/human)
+ do_footstep = TRUE
+
/mob/living/simple_animal/hostile/skeleton/eskimo
name = "undead eskimo"
desc = "The reanimated remains of some poor traveler."
diff --git a/code/modules/mob/living/simple_animal/hostile/statue.dm b/code/modules/mob/living/simple_animal/hostile/statue.dm
index 81d583ee65..804989e71e 100644
--- a/code/modules/mob/living/simple_animal/hostile/statue.dm
+++ b/code/modules/mob/living/simple_animal/hostile/statue.dm
@@ -133,7 +133,7 @@
// Cannot talk
-/mob/living/simple_animal/hostile/statue/say()
+/mob/living/simple_animal/hostile/statue/say(message, bubble_type, var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
return 0
// Turn to dust when gibbed
diff --git a/code/modules/mob/living/simple_animal/hostile/syndicate.dm b/code/modules/mob/living/simple_animal/hostile/syndicate.dm
index 8efa3e4d35..2c45743c55 100644
--- a/code/modules/mob/living/simple_animal/hostile/syndicate.dm
+++ b/code/modules/mob/living/simple_animal/hostile/syndicate.dm
@@ -1,9 +1,9 @@
/*
CONTENTS
LINE 10 - BASE MOB
- LINE 43 - SWORD AND SHIELD
- LINE 95 - GUNS
- LINE 136 - MISC
+ LINE 52 - SWORD AND SHIELD
+ LINE 164 - GUNS
+ LINE 267 - MISC
*/
@@ -46,28 +46,87 @@
check_friendly_fire = 1
status_flags = CANPUSH
del_on_death = 1
+ dodging = TRUE
+ rapid_melee = 2
-///////////////Sword and shield////////////
+ do_footstep = TRUE
+
+///////////////Melee////////////
+
+/mob/living/simple_animal/hostile/syndicate/space
+ icon_state = "syndicate_space"
+ icon_living = "syndicate_space"
+ name = "Syndicate Commando"
+ maxHealth = 170
+ health = 170
+ atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
+ minbodytemp = 0
+ speed = 1
+ spacewalk = TRUE
+
+/mob/living/simple_animal/hostile/syndicate/space/Initialize()
+ . = ..()
+ set_light(4)
+
+/mob/living/simple_animal/hostile/syndicate/space/stormtrooper
+ icon_state = "syndicate_stormtrooper"
+ icon_living = "syndicate_stormtrooper"
+ name = "Syndicate Stormtrooper"
+ maxHealth = 250
+ health = 250
/mob/living/simple_animal/hostile/syndicate/melee
- melee_damage_lower = 25
- melee_damage_upper = 30
- icon_state = "syndicatemelee"
- icon_living = "syndicatemelee"
+ melee_damage_lower = 15
+ melee_damage_upper = 15
+ icon_state = "syndicate_knife"
+ icon_living = "syndicate_knife"
loot = list(/obj/effect/gibspawner/human)
attacktext = "slashes"
attack_sound = 'sound/weapons/bladeslice.ogg'
- armour_penetration = 28
- light_color = LIGHT_COLOR_RED
status_flags = 0
+
+/mob/living/simple_animal/hostile/syndicate/melee/space
+ icon_state = "syndicate_space_knife"
+ icon_living = "syndicate_space_knife"
+ name = "Syndicate Commando"
maxHealth = 170
health = 170
+ atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
+ minbodytemp = 0
+ speed = 1
spacewalk = TRUE
-/mob/living/simple_animal/hostile/syndicate/melee/Initialize()
+/mob/living/simple_animal/hostile/syndicate/melee/space/Initialize()
+ . = ..()
+ set_light(4)
+
+/mob/living/simple_animal/hostile/syndicate/melee/space/stormtrooper
+ icon_state = "syndicate_stormtrooper_knife"
+ icon_living = "syndicate_stormtrooper_knife"
+ name = "Syndicate Stormtrooper"
+ maxHealth = 250
+ health = 250
+
+/mob/living/simple_animal/hostile/syndicate/melee/sword
+ melee_damage_lower = 30
+ melee_damage_upper = 30
+ icon_state = "syndicate_sword"
+ icon_living = "syndicate_sword"
+ attacktext = "slashes"
+ attack_sound = 'sound/weapons/blade1.ogg'
+ armour_penetration = 35
+ light_color = LIGHT_COLOR_RED
+ status_flags = 0
+ var/obj/effect/light_emitter/red_energy_sword/sord
+
+/mob/living/simple_animal/hostile/syndicate/melee/sword/Initialize()
. = ..()
set_light(2)
+/mob/living/simple_animal/hostile/syndicate/melee/sword/Destroy()
+ QDEL_NULL(sord)
+ return ..()
+
/mob/living/simple_animal/hostile/syndicate/melee/bullet_act(obj/item/projectile/Proj)
if(!Proj)
return
@@ -77,74 +136,135 @@
visible_message("
[src] blocks [Proj] with its shield! ")
return 0
-
-/mob/living/simple_animal/hostile/syndicate/melee/space
+/mob/living/simple_animal/hostile/syndicate/melee/sword/space
+ icon_state = "syndicate_space_sword"
+ icon_living = "syndicate_space_sword"
+ name = "Syndicate Commando"
+ maxHealth = 170
+ health = 170
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
- icon_state = "syndicatemeleespace"
- icon_living = "syndicatemeleespace"
- name = "Syndicate Commando"
- loot = list(/obj/effect/gibspawner/human)
speed = 1
- var/obj/effect/light_emitter/red_energy_sword/sord
+ spacewalk = TRUE
-/mob/living/simple_animal/hostile/syndicate/melee/space/Initialize()
+/mob/living/simple_animal/hostile/syndicate/melee/sword/space/Initialize()
. = ..()
sord = new(src)
set_light(4)
-/mob/living/simple_animal/hostile/syndicate/melee/space/Destroy()
+/mob/living/simple_animal/hostile/syndicate/melee/sword/space/Destroy()
QDEL_NULL(sord)
return ..()
-/mob/living/simple_animal/hostile/syndicate/melee/space/stormtrooper
- icon_state = "syndicatemeleestormtrooper"
- icon_living = "syndicatemeleestormtrooper"
+/mob/living/simple_animal/hostile/syndicate/melee/sword/space/stormtrooper
+ icon_state = "syndicate_stormtrooper_sword"
+ icon_living = "syndicate_stormtrooper_sword"
name = "Syndicate Stormtrooper"
- maxHealth = 340
- health = 340
- loot = list(/obj/effect/gibspawner/human)
+ maxHealth = 250
+ health = 250
///////////////Guns////////////
/mob/living/simple_animal/hostile/syndicate/ranged
ranged = 1
- rapid = 3
retreat_distance = 5
minimum_distance = 5
- icon_state = "syndicateranged"
- icon_living = "syndicateranged"
- casingtype = /obj/item/ammo_casing/c45/nostamina
- projectilesound = 'sound/weapons/gunshot_smg.ogg'
+ icon_state = "syndicate_pistol"
+ icon_living = "syndicate_pistol"
+ casingtype = /obj/item/ammo_casing/c10mm
+ projectilesound = 'sound/weapons/gunshot.ogg'
loot = list(/obj/effect/gibspawner/human)
+ dodging = FALSE
+ rapid_melee = 1
-/mob/living/simple_animal/hostile/syndicate/ranged/pilot
- name = "Syndicate Salvage Pilot"
+/mob/living/simple_animal/hostile/syndicate/ranged/infiltrator //shuttle loan event
+ projectilesound = 'sound/weapons/gunshot_silenced.ogg'
loot = list(/obj/effect/mob_spawn/human/corpse/syndicatesoldier)
/mob/living/simple_animal/hostile/syndicate/ranged/space
- icon_state = "syndicaterangedspace"
- icon_living = "syndicaterangedspace"
+ icon_state = "syndicate_space_pistol"
+ icon_living = "syndicate_space_pistol"
name = "Syndicate Commando"
+ maxHealth = 170
+ health = 170
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
speed = 1
spacewalk = TRUE
- loot = list(/obj/effect/gibspawner/human)
/mob/living/simple_animal/hostile/syndicate/ranged/space/Initialize()
. = ..()
set_light(4)
/mob/living/simple_animal/hostile/syndicate/ranged/space/stormtrooper
- icon_state = "syndicaterangedstormtrooper"
- icon_living = "syndicaterangedstormtrooper"
+ icon_state = "syndicate_stormtrooper_pistol"
+ icon_living = "syndicate_stormtrooper_pistol"
name = "Syndicate Stormtrooper"
- maxHealth = 200
- health = 200
- casingtype = /obj/item/ammo_casing/shotgun/buckshot
- projectilesound = 'sound/weapons/gunshot.ogg'
- loot = list(/obj/effect/gibspawner/human)
+ maxHealth = 250
+ health = 250
+
+/mob/living/simple_animal/hostile/syndicate/ranged/smg
+ rapid = 2
+ icon_state = "syndicate_smg"
+ icon_living = "syndicate_smg"
+ casingtype = /obj/item/ammo_casing/c45/nostamina
+ projectilesound = 'sound/weapons/gunshot_smg.ogg'
+
+/mob/living/simple_animal/hostile/syndicate/ranged/smg/pilot //caravan ambush ruin
+ name = "Syndicate Salvage Pilot"
+ loot = list(/obj/effect/mob_spawn/human/corpse/syndicatesoldier)
+
+/mob/living/simple_animal/hostile/syndicate/ranged/smg/space
+ icon_state = "syndicate_space_smg"
+ icon_living = "syndicate_space_smg"
+ name = "Syndicate Commando"
+ maxHealth = 170
+ health = 170
+ atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
+ minbodytemp = 0
+ speed = 1
+ spacewalk = TRUE
+
+/mob/living/simple_animal/hostile/syndicate/ranged/smg/space/Initialize()
+ . = ..()
+ set_light(4)
+
+/mob/living/simple_animal/hostile/syndicate/ranged/smg/space/stormtrooper
+ icon_state = "syndicate_stormtrooper_smg"
+ icon_living = "syndicate_stormtrooper_smg"
+ name = "Syndicate Stormtrooper"
+ maxHealth = 250
+ health = 250
+
+/mob/living/simple_animal/hostile/syndicate/ranged/shotgun
+ rapid = 2
+ rapid_fire_delay = 6
+ minimum_distance = 3
+ icon_state = "syndicate_shotgun"
+ icon_living = "syndicate_shotgun"
+ casingtype = /obj/item/ammo_casing/shotgun/buckshot //buckshot (up to 72.5 brute) fired in a two-round burst
+
+/mob/living/simple_animal/hostile/syndicate/ranged/shotgun/space
+ icon_state = "syndicate_space_shotgun"
+ icon_living = "syndicate_space_shotgun"
+ name = "Syndicate Commando"
+ maxHealth = 170
+ health = 170
+ atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
+ minbodytemp = 0
+ speed = 1
+ spacewalk = TRUE
+
+/mob/living/simple_animal/hostile/syndicate/ranged/shotgun/space/Initialize()
+ . = ..()
+ set_light(4)
+
+/mob/living/simple_animal/hostile/syndicate/ranged/shotgun/space/stormtrooper
+ icon_state = "syndicate_stormtrooper_shotgun"
+ icon_living = "syndicate_stormtrooper_shotgun"
+ name = "Syndicate Stormtrooper"
+ maxHealth = 250
+ health = 250
///////////////Misc////////////
diff --git a/code/modules/mob/living/simple_animal/hostile/wizard.dm b/code/modules/mob/living/simple_animal/hostile/wizard.dm
index 921bd5959b..a946cbf45b 100644
--- a/code/modules/mob/living/simple_animal/hostile/wizard.dm
+++ b/code/modules/mob/living/simple_animal/hostile/wizard.dm
@@ -37,6 +37,8 @@
var/next_cast = 0
+ do_footstep = TRUE
+
/mob/living/simple_animal/hostile/wizard/Initialize()
. = ..()
fireball = new /obj/effect/proc_holder/spell/aimed/fireball
diff --git a/code/modules/mob/living/simple_animal/hostile/wumborian_fugu.dm b/code/modules/mob/living/simple_animal/hostile/wumborian_fugu.dm
index d262725d7b..ec7451dc2e 100644
--- a/code/modules/mob/living/simple_animal/hostile/wumborian_fugu.dm
+++ b/code/modules/mob/living/simple_animal/hostile/wumborian_fugu.dm
@@ -127,6 +127,7 @@
var/list/banned_mobs
/obj/item/fugu_gland/afterattack(atom/target, mob/user, proximity_flag)
+ . = ..()
if(proximity_flag && isanimal(target))
var/mob/living/simple_animal/A = target
if(A.buffed || (A.type in banned_mobs) || A.stat)
diff --git a/code/modules/mob/living/simple_animal/hostile/zombie.dm b/code/modules/mob/living/simple_animal/hostile/zombie.dm
new file mode 100644
index 0000000000..21c2d4804a
--- /dev/null
+++ b/code/modules/mob/living/simple_animal/hostile/zombie.dm
@@ -0,0 +1,58 @@
+/mob/living/simple_animal/hostile/zombie
+ name = "Shambling Corpse"
+ desc = "When there is no more room in hell, the dead will walk in outer space."
+ icon = 'icons/mob/simple_human.dmi'
+ icon_state = "zombie"
+ icon_living = "zombie"
+ mob_biotypes = list(MOB_ORGANIC, MOB_HUMANOID)
+ speak_chance = 0
+ stat_attack = UNCONSCIOUS //braains
+ maxHealth = 100
+ health = 100
+ harm_intent_damage = 5
+ melee_damage_lower = 21
+ melee_damage_upper = 21
+ attacktext = "bites"
+ attack_sound = 'sound/hallucinations/growl1.ogg'
+ a_intent = INTENT_HARM
+ atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
+ minbodytemp = 0
+ spacewalk = FALSE
+ status_flags = CANPUSH
+ del_on_death = 1
+ var/zombiejob = "Medical Doctor"
+ var/infection_chance = 0
+ var/obj/effect/mob_spawn/human/corpse/delayed/corpse
+
+/mob/living/simple_animal/hostile/zombie/Initialize(mapload)
+ . = ..()
+ setup_visuals()
+
+/mob/living/simple_animal/hostile/zombie/proc/setup_visuals()
+ var/datum/preferences/dummy_prefs = new
+ dummy_prefs.pref_species = new /datum/species/zombie
+ dummy_prefs.be_random_body = TRUE
+ var/datum/job/J = SSjob.GetJob(zombiejob)
+ var/datum/outfit/O
+ if(J.outfit)
+ O = new J.outfit
+ //They have claws now.
+ O.r_hand = null
+ O.l_hand = null
+
+ var/icon/P = get_flat_human_icon("zombie_[zombiejob]", J , dummy_prefs, "zombie", outfit_override = O)
+ icon = P
+ corpse = new(src)
+ corpse.outfit = O
+ corpse.mob_species = /datum/species/zombie
+ corpse.mob_name = name
+
+/mob/living/simple_animal/hostile/zombie/AttackingTarget()
+ . = ..()
+ if(. && ishuman(target) && prob(infection_chance))
+ try_to_zombie_infect(target)
+
+/mob/living/simple_animal/hostile/zombie/drop_loot()
+ . = ..()
+ corpse.forceMove(drop_location())
+ corpse.create()
\ No newline at end of file
diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm
index 7d9a4eefe6..4fdf431fa0 100644
--- a/code/modules/mob/living/simple_animal/parrot.dm
+++ b/code/modules/mob/living/simple_animal/parrot.dm
@@ -394,6 +394,9 @@
if(!isturf(src.loc) || !canmove || buckled)
return //If it can't move, dont let it move. (The buckled check probably isn't necessary thanks to canmove)
+ if(client && stat == CONSCIOUS && parrot_state != icon_living)
+ icon_state = icon_living
+ //Because the most appropriate place to set icon_state is movement_delay(), clearly
//-----SLEEPING
if(parrot_state == PARROT_PERCH)
@@ -604,12 +607,6 @@
* Procs
*/
-/mob/living/simple_animal/parrot/movement_delay()
- if(client && stat == CONSCIOUS && parrot_state != icon_living)
- icon_state = icon_living
- //Because the most appropriate place to set icon_state is movement_delay(), clearly
- return ..()
-
/mob/living/simple_animal/parrot/proc/isStuck()
//Check to see if the parrot is stuck due to things like windows or doors or windowdoors
if(parrot_lastmove)
diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm
index 7f2864a43d..fcb201d0ad 100644
--- a/code/modules/mob/living/simple_animal/simple_animal.dm
+++ b/code/modules/mob/living/simple_animal/simple_animal.dm
@@ -91,6 +91,8 @@
var/my_z // I don't want to confuse this with client registered_z
+ var/do_footstep = FALSE
+
/mob/living/simple_animal/Initialize()
. = ..()
GLOB.simple_animals[AIStatus] += src
@@ -101,13 +103,12 @@
real_name = name
if(!loc)
stack_trace("Simple animal being instantiated in nullspace")
- if(vore_active)
- init_belly()
- if(!IsAdvancedToolUser())
- verbs |= /mob/living/simple_animal/proc/animal_nom
+ update_simplemob_varspeed()
/mob/living/simple_animal/Destroy()
GLOB.simple_animals[AIStatus] -= src
+ if (SSnpcpool.state == SS_PAUSED && LAZYLEN(SSnpcpool.currentrun))
+ SSnpcpool.currentrun -= src
if(nest)
nest.spawned_mobs -= src
@@ -119,6 +120,10 @@
return ..()
+/mob/living/simple_animal/initialize_footstep()
+ if(do_footstep)
+ ..()
+
/mob/living/simple_animal/updatehealth()
..()
health = CLAMP(health, 0, maxHealth)
@@ -169,7 +174,7 @@
length += emote_see.len
var/randomValue = rand(1,length)
if(randomValue <= speak.len)
- say(pick(speak))
+ say(pick(speak), forced = "poly")
else
randomValue -= speak.len
if(emote_see && randomValue <= emote_see.len)
@@ -177,7 +182,7 @@
else
emote("me [pick(emote_hear)]", 2)
else
- say(pick(speak))
+ say(pick(speak), forced = "poly")
else
if(!(emote_hear && emote_hear.len) && (emote_see && emote_see.len))
emote("me", 1, pick(emote_see))
@@ -272,7 +277,7 @@
verb_say = pick(speak_emote)
. = ..()
-/mob/living/simple_animal/emote(act, m_type=1, message = null)
+/mob/living/simple_animal/emote(act, m_type=1, message = null, intentional = FALSE)
if(stat)
return
if(act == "scream")
@@ -280,14 +285,14 @@
act = "me"
..(act, m_type, message)
+/mob/living/simple_animal/proc/set_varspeed(var_value)
+ speed = var_value
+ update_simplemob_varspeed()
-
-/mob/living/simple_animal/movement_delay()
- var/static/config_animal_delay
- if(isnull(config_animal_delay))
- config_animal_delay = CONFIG_GET(number/animal_delay)
- . += config_animal_delay
- return ..() + speed + config_animal_delay
+/mob/living/simple_animal/proc/update_simplemob_varspeed()
+ if(speed == 0)
+ remove_movespeed_modifier(MOVESPEED_ID_SIMPLEMOB_VARSPEED, TRUE)
+ add_movespeed_modifier(MOVESPEED_ID_SIMPLEMOB_VARSPEED, TRUE, 100, multiplicative_slowdown = speed, override = TRUE)
/mob/living/simple_animal/Stat()
..()
@@ -295,14 +300,17 @@
stat(null, "Health: [round((health / maxHealth) * 100)]%")
return 1
+/mob/living/simple_animal/proc/drop_loot()
+ if(loot.len)
+ for(var/i in loot)
+ new i(loc)
+
/mob/living/simple_animal/death(gibbed)
movement_type &= ~FLYING
if(nest)
nest.spawned_mobs -= src
nest = null
- if(loot.len)
- for(var/i in loot)
- new i(loc)
+ drop_loot()
if(dextrous)
drop_all_held_items()
if(!gibbed)
diff --git a/code/modules/mob/living/simple_animal/slime/slime.dm b/code/modules/mob/living/simple_animal/slime/slime.dm
index 34e7a2ddcd..034ccc4c39 100644
--- a/code/modules/mob/living/simple_animal/slime/slime.dm
+++ b/code/modules/mob/living/simple_animal/slime/slime.dm
@@ -134,35 +134,39 @@
icon_state = icon_dead
..()
-/mob/living/simple_animal/slime/movement_delay()
- if(bodytemperature >= 330.23) // 135 F or 57.08 C
- return -1 // slimes become supercharged at high temperatures
-
+/mob/living/simple_animal/slime/on_reagent_change()
. = ..()
+ remove_movespeed_modifier(MOVESPEED_ID_SLIME_REAGENTMOD, TRUE)
+ var/amount = 0
+ if(reagents.has_reagent("morphine")) // morphine slows slimes down
+ amount = 2
+ if(reagents.has_reagent("frostoil")) // Frostoil also makes them move VEEERRYYYYY slow
+ amount = 5
+ if(amount)
+ add_movespeed_modifier(MOVESPEED_ID_SLIME_REAGENTMOD, TRUE, 100, override = TRUE, multiplicative_slowdown = amount)
+/mob/living/simple_animal/slime/updatehealth()
+ . = ..()
+ remove_movespeed_modifier(MOVESPEED_ID_SLIME_HEALTHMOD, FALSE)
var/health_deficiency = (100 - health)
+ var/mod = 0
if(health_deficiency >= 45)
- . += (health_deficiency / 25)
+ mod += (health_deficiency / 25)
+ if(health <= 0)
+ mod += 2
+ add_movespeed_modifier(MOVESPEED_ID_SLIME_HEALTHMOD, TRUE, 100, multiplicative_slowdown = mod)
- if(bodytemperature < 183.222)
- . += (283.222 - bodytemperature) / 10 * 1.75
+/mob/living/simple_animal/slime/adjust_bodytemperature()
+ . = ..()
+ var/mod = 0
+ if(bodytemperature >= 330.23) // 135 F or 57.08 C
+ mod = -1 // slimes become supercharged at high temperatures
+ else if(bodytemperature < 183.222)
+ mod = (283.222 - bodytemperature) / 10 * 1.75
+ if(mod)
+ add_movespeed_modifier(MOVESPEED_ID_SLIME_TEMPMOD, TRUE, 100, override = TRUE, multiplicative_slowdown = mod)
- if(reagents)
- if(reagents.has_reagent("morphine")) // morphine slows slimes down
- . *= 2
-
- if(reagents.has_reagent("frostoil")) // Frostoil also makes them move VEEERRYYYYY slow
- . *= 5
-
- if(health <= 0) // if damaged, the slime moves twice as slow
- . *= 2
-
- var/static/config_slime_delay
- if(isnull(config_slime_delay))
- config_slime_delay = CONFIG_GET(number/slime_delay)
- . += config_slime_delay
-
-/mob/living/simple_animal/slime/ObjCollide(obj/O)
+/mob/living/simple_animal/slime/ObjBump(obj/O)
if(!client && powerlevel > 0)
var/probab = 10
switch(powerlevel)
@@ -306,9 +310,9 @@
discipline_slime(M)
else
if(stat == DEAD && surgeries.len)
- if(M.a_intent == INTENT_HELP)
+ if(M.a_intent == INTENT_HELP || M.a_intent == INTENT_DISARM)
for(var/datum/surgery/S in surgeries)
- if(S.next_step(M))
+ if(S.next_step(M,M.a_intent))
return 1
if(..()) //successful attack
attacked += 10
@@ -321,9 +325,9 @@
/mob/living/simple_animal/slime/attackby(obj/item/W, mob/living/user, params)
if(stat == DEAD && surgeries.len)
- if(user.a_intent == INTENT_HELP)
+ if(user.a_intent == INTENT_HELP || user.a_intent == INTENT_DISARM)
for(var/datum/surgery/S in surgeries)
- if(S.next_step(user))
+ if(S.next_step(user,user.a_intent))
return 1
if(istype(W, /obj/item/stack/sheet/mineral/plasma) && !stat) //Let's you feed slimes plasma.
if (user in Friends)
diff --git a/code/modules/mob/living/status_procs.dm b/code/modules/mob/living/status_procs.dm
index 47de9a0896..be98d1bfab 100644
--- a/code/modules/mob/living/status_procs.dm
+++ b/code/modules/mob/living/status_procs.dm
@@ -143,9 +143,14 @@
/mob/living/proc/add_trait(trait, source)
if(!status_traits[trait])
status_traits[trait] = list(source)
+ on_add_trait(trait, source)
else
status_traits[trait] |= list(source)
+/mob/living/proc/on_add_trait(trait, source)
+ if(trait == TRAIT_IGNORESLOWDOWN)
+ update_movespeed(FALSE)
+
/mob/living/proc/add_quirk(quirk, spawn_effects) //separate proc due to the way these ones are handled
if(has_trait(quirk))
return
@@ -156,7 +161,6 @@
return TRUE
/mob/living/proc/remove_trait(trait, list/sources, force)
-
if(!status_traits[trait])
return
@@ -165,6 +169,7 @@
if(!sources) // No defined source cures the trait entirely.
status_traits -= trait
+ on_remove_trait(trait, sources, force)
return
if(!islist(sources))
@@ -179,6 +184,11 @@
if(!LAZYLEN(status_traits[trait]))
status_traits -= trait
+ on_remove_trait(trait, sources, force)
+
+/mob/living/proc/on_remove_trait(trait, list/sources, force)
+ if(trait == TRAIT_IGNORESLOWDOWN)
+ update_movespeed(FALSE)
/mob/living/proc/remove_quirk(quirk)
var/datum/quirk/T = roundstart_quirks[quirk]
@@ -260,13 +270,17 @@
/mob/living/proc/cure_fakedeath(list/sources)
remove_trait(TRAIT_FAKEDEATH, sources)
+ remove_trait(TRAIT_DEATHCOMA, sources)
if(stat != DEAD)
tod = null
update_stat()
-/mob/living/proc/fakedeath(source)
+/mob/living/proc/fakedeath(source, silent = FALSE)
if(stat == DEAD)
return
+ if(!silent)
+ emote("deathgasp")
add_trait(TRAIT_FAKEDEATH, source)
+ add_trait(TRAIT_DEATHCOMA, source)
tod = station_time_timestamp()
update_stat()
\ No newline at end of file
diff --git a/code/modules/mob/living/ventcrawling.dm b/code/modules/mob/living/ventcrawling.dm
index 600fa581cb..d795280686 100644
--- a/code/modules/mob/living/ventcrawling.dm
+++ b/code/modules/mob/living/ventcrawling.dm
@@ -120,3 +120,4 @@ GLOBAL_LIST_INIT(ventcrawl_machinery, typecacheof(list(
. = new_loc
remove_ventcrawl()
add_ventcrawl(.)
+
diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm
index 9def2a759f..611139bbf9 100644
--- a/code/modules/mob/login.dm
+++ b/code/modules/mob/login.dm
@@ -49,4 +49,4 @@
for(var/datum/action/A in client.player_details.player_actions)
A.Grant(src)
- log_message("Client [key_name(src)] has taken ownership of mob [src]", INDIVIDUAL_OWNERSHIP_LOG)
+ log_message("Client [key_name(src)] has taken ownership of mob [src]([src.type])", LOG_OWNERSHIP)
diff --git a/code/modules/mob/logout.dm b/code/modules/mob/logout.dm
index fbd29e86a5..031cb1b58d 100644
--- a/code/modules/mob/logout.dm
+++ b/code/modules/mob/logout.dm
@@ -1,5 +1,5 @@
/mob/Logout()
- log_message("[key_name(src)] is no longer owning mob [src]", INDIVIDUAL_OWNERSHIP_LOG)
+ log_message("[key_name(src)] is no longer owning mob [src]([src.type])", LOG_OWNERSHIP)
SStgui.on_logout(src)
unset_machine()
GLOB.player_list -= src
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 6deffc97da..eb4cfb47b2 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -4,6 +4,7 @@
GLOB.alive_mob_list -= src
GLOB.all_clockwork_mobs -= src
GLOB.mob_directory -= tag
+ focus = null
for (var/alert in alerts)
clear_alert(alert, TRUE)
if(observers && observers.len)
@@ -32,9 +33,11 @@
continue
var/datum/atom_hud/alternate_appearance/AA = v
AA.onNewMob(src)
- hook_vr("mob_new",list(src))
nutrition = rand(NUTRITION_LEVEL_START_MIN, NUTRITION_LEVEL_START_MAX)
. = ..()
+ update_config_movespeed()
+ update_movespeed(TRUE)
+ hook_vr("mob_new",list(src))
/mob/GenerateTag()
tag = "mob_[next_mob_id++]"
@@ -69,6 +72,9 @@
to_chat(usr, t)
+/mob/proc/get_photo_description(obj/item/camera/camera)
+ return "a ... thing?"
+
/mob/proc/show_message(msg, type, alt_msg, alt_type)//Message, type of message (1 or 2), alternative message, alt message type (1 or 2)
if(!client)
@@ -170,9 +176,6 @@
for(var/mob/M in get_hearers_in_view(range, src))
M.show_message( message, 2, deaf_message, 1)
-/mob/proc/movement_delay() //update /living/movement_delay() if you change this
- return 0
-
/mob/proc/Life()
set waitfor = FALSE
@@ -314,17 +317,17 @@
set category = "Object"
if(!src || !isturf(src.loc) || !(A in view(src.loc)))
- return 0
+ return FALSE
if(istype(A, /obj/effect/temp_visual/point))
- return 0
+ return FALSE
var/tile = get_turf(A)
if (!tile)
- return 0
+ return FALSE
new /obj/effect/temp_visual/point(A,invisibility)
- return 1
+ return TRUE
/mob/proc/can_resist()
return FALSE //overridden in living.dm
@@ -353,6 +356,11 @@
if(hud_used.pull_icon)
hud_used.pull_icon.update_icon(src)
+/mob/proc/update_rest_hud_icon()
+ if(hud_used)
+ if(hud_used.rest_icon)
+ hud_used.rest_icon.update_icon(src)
+
/mob/verb/mode()
set name = "Activate Held Object"
set category = "Object"
@@ -368,12 +376,10 @@
if(I)
I.attack_self(src)
update_inv_hands()
-
if(!I)//CIT CHANGE - allows "using" empty hands
use_that_empty_hand() //CIT CHANGE - ditto
update_inv_hands() // CIT CHANGE - ditto.
-
/mob/verb/memory()
set name = "Notes"
set category = "IC"
@@ -603,59 +609,61 @@
if(S.chemical_cost >=0 && S.can_be_used_by(src))
statpanel("[S.panel]",((S.chemical_cost > 0) ? "[S.chemical_cost]" : ""),S)
+#define MOB_FACE_DIRECTION_DELAY 1
+
// facing verbs
/mob/proc/canface()
if(!canmove)
- return 0
- if(world.time < client.move_delay)
- return 0
- if(stat==2)
- return 0
+ return FALSE
+ if(world.time < client.last_turn)
+ return FALSE
+ if(stat == DEAD || stat == UNCONSCIOUS)
+ return FALSE
if(anchored)
- return 0
+ return FALSE
if(notransform)
- return 0
+ return FALSE
if(restrained())
- return 0
- return 1
+ return FALSE
+ return TRUE
/mob/proc/fall(forced)
drop_all_held_items()
/mob/verb/eastface()
- set hidden = 1
+ set hidden = TRUE
if(!canface())
- return 0
+ return FALSE
setDir(EAST)
- client.move_delay += movement_delay()
- return 1
+ client.last_turn = world.time + MOB_FACE_DIRECTION_DELAY
+ return TRUE
/mob/verb/westface()
- set hidden = 1
+ set hidden = TRUE
if(!canface())
- return 0
+ return FALSE
setDir(WEST)
- client.move_delay += movement_delay()
- return 1
+ client.last_turn = world.time + MOB_FACE_DIRECTION_DELAY
+ return TRUE
/mob/verb/northface()
- set hidden = 1
+ set hidden = TRUE
if(!canface())
- return 0
+ return FALSE
setDir(NORTH)
- client.move_delay += movement_delay()
- return 1
+ client.last_turn = world.time + MOB_FACE_DIRECTION_DELAY
+ return TRUE
/mob/verb/southface()
- set hidden = 1
+ set hidden = TRUE
if(!canface())
- return 0
+ return FALSE
setDir(SOUTH)
- client.move_delay += movement_delay()
- return 1
+ client.last_turn = world.time + MOB_FACE_DIRECTION_DELAY
+ return TRUE
/mob/proc/IsAdvancedToolUser()//This might need a rename but it should replace the can this mob use things check
- return 0
+ return FALSE
/mob/proc/swap_hand()
return
@@ -799,7 +807,7 @@
//This will update a mob's name, real_name, mind.name, GLOB.data_core records, pda, id and traitor text
//Calling this proc without an oldname will only update the mob and skip updating the pda, id and records ~Carn
/mob/proc/fully_replace_character_name(oldname,newname)
- log_message("[src] name changed from [oldname] to [newname]", INDIVIDUAL_OWNERSHIP_LOG)
+ log_message("[src] name changed from [oldname] to [newname]", LOG_OWNERSHIP)
if(!newname)
return 0
real_name = newname
diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm
index 3dff46ec84..298fee46cd 100644
--- a/code/modules/mob/mob_defines.dm
+++ b/code/modules/mob/mob_defines.dm
@@ -14,8 +14,7 @@
var/list/datum/action/chameleon_item_actions
var/static/next_mob_id = 0
- var/stat = 0 //Whether a mob is alive or dead. TODO: Move this to living - Nodrak
-
+ var/stat = CONSCIOUS //Whether a mob is alive or dead. TODO: Move this to living - Nodrak
/*A bunch of this stuff really needs to go under their own defines instead of being globally attached to mob.
A variable should only be globally attached to turfs/objects/whatever, when it is in fact needed as such.
@@ -26,7 +25,7 @@
var/zone_selected = null
var/computer_id = null
- var/list/logging = list(INDIVIDUAL_ATTACK_LOG, INDIVIDUAL_SAY_LOG, INDIVIDUAL_EMOTE_LOG, INDIVIDUAL_OOC_LOG)
+ var/list/logging = list()
var/obj/machinery/machine = null
var/next_move = null
@@ -40,6 +39,11 @@
var/lying_prev = 0
var/canmove = 1
+ //MOVEMENT SPEED
+ var/list/movespeed_modification //Lazy list, see mob_movespeed.dm
+ var/cached_multiplicative_slowdown
+ /////////////////
+
var/name_archive //For admin things like possession
var/bodytemperature = BODYTEMP_NORMAL //310.15K / 98.6F
@@ -104,3 +108,5 @@
var/list/mousemove_intercept_objects
var/datum/click_intercept
+
+ var/registered_z
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index 0e37666f02..fa908db729 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -468,22 +468,37 @@ It's fairly easy to fix if dealing with single letters but not so much with comp
var/mob/living/T = pick(nearby_mobs)
ClickOn(T)
-/mob/proc/log_message(message, message_type)
- if(!LAZYLEN(message) || !message_type)
+// Logs a message in a mob's individual log, and in the global logs as well if log_globally is true
+/mob/log_message(message, message_type, color=null, log_globally = TRUE)
+ if(!LAZYLEN(message))
+ stack_trace("Empty message")
return
+ // Cannot use the list as a map if the key is a number, so we stringify it (thank you BYOND)
+ var/smessage_type = num2text(message_type)
+
if(client)
- if(!islist(client.player_details.logging[message_type]))
- client.player_details.logging[message_type] = list()
+ if(!islist(client.player_details.logging[smessage_type]))
+ client.player_details.logging[smessage_type] = list()
- if(!islist(logging[message_type]))
- logging[message_type] = list()
+ if(!islist(logging[smessage_type]))
+ logging[smessage_type] = list()
- var/list/timestamped_message = list("[LAZYLEN(logging[message_type]) + 1]\[[time_stamp()]\] [key_name(src)]" = message)
+ var/colored_message = message
+ if(color)
+ if(color[1] == "#")
+ colored_message = "
[message] "
+ else
+ colored_message = "
[message] "
+
+ var/list/timestamped_message = list("[LAZYLEN(logging[smessage_type]) + 1]\[[time_stamp()]\] [key_name(src)] [loc_name(src)]" = colored_message)
+
+ logging[smessage_type] += timestamped_message
- logging[message_type] += timestamped_message
if(client)
- client.player_details.logging[message_type] += timestamped_message
+ client.player_details.logging[smessage_type] += timestamped_message
+
+ ..()
/mob/proc/can_hear()
. = TRUE
diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm
index 291ae1e425..cb532e4d5e 100644
--- a/code/modules/mob/mob_movement.dm
+++ b/code/modules/mob/mob_movement.dm
@@ -10,6 +10,10 @@
return TRUE
return (!mover.density || !density || lying)
+//DO NOT USE THIS UNLESS YOU ABSOLUTELY HAVE TO. THIS IS BEING PHASED OUT FOR THE MOVESPEED MODIFICATION SYSTEM.
+//See mob_movespeed.dm
+/mob/proc/movement_delay() //update /living/movement_delay() if you change this
+ return cached_multiplicative_slowdown
/client/verb/drop_item()
set hidden = 1
@@ -26,7 +30,6 @@
mob.control_object.setDir(direct)
else
mob.control_object.forceMove(get_step(mob.control_object,direct))
- return
#define MOVEMENT_DELAY_BUFFER 0.75
#define MOVEMENT_DELAY_BUFFER_DELTA 1.25
@@ -38,7 +41,7 @@
next_move_dir_add = 0
next_move_dir_sub = 0
var/old_move_delay = move_delay
- move_delay = world.time+world.tick_lag //this is here because Move() can now be called mutiple times per tick
+ move_delay = world.time + world.tick_lag //this is here because Move() can now be called mutiple times per tick
if(!mob || !mob.loc)
return FALSE
if(!n || !direct)
@@ -116,7 +119,7 @@
var/atom/movable/P = mob.pulling
if(P && !ismob(P) && P.density)
- mob.dir = turn(mob.dir, 180)
+ mob.setDir(turn(mob.dir, 180))
///Process_Grab()
///Called by client/Move()
@@ -357,9 +360,13 @@
set hidden = TRUE
set instant = TRUE
if(mob)
- mob.toggle_move_intent()
+ mob.toggle_move_intent(usr)
-/mob/proc/toggle_move_intent()
+/mob/proc/toggle_move_intent(mob/user)
+ if(m_intent == MOVE_INTENT_RUN)
+ m_intent = MOVE_INTENT_WALK
+ else
+ m_intent = MOVE_INTENT_RUN
if(hud_used && hud_used.static_inventory)
for(var/obj/screen/mov_intent/selector in hud_used.static_inventory)
- selector.toggle(src)
+ selector.update_icon(src)
diff --git a/code/modules/mob/mob_movespeed.dm b/code/modules/mob/mob_movespeed.dm
new file mode 100644
index 0000000000..5114972493
--- /dev/null
+++ b/code/modules/mob/mob_movespeed.dm
@@ -0,0 +1,106 @@
+
+/*Current movespeed modification list format: list(id = list(
+ priority,
+ legacy slowdown/speedup amount,
+ ))
+*/
+
+//ANY ADD/REMOVE DONE IN UPDATE_MOVESPEED MUST HAVE THE UPDATE ARGUMENT SET AS FALSE!
+/mob/proc/add_movespeed_modifier(id, update = TRUE, priority = 0, flags = NONE, override = FALSE, multiplicative_slowdown = 0)
+ var/list/temp = list(priority, flags, multiplicative_slowdown) //build the modification list
+ if(LAZYACCESS(movespeed_modification, id))
+ if(movespeed_modifier_identical_check(movespeed_modification[id], temp))
+ return FALSE
+ if(!override)
+ return FALSE
+ else
+ remove_movespeed_modifier(id, update)
+ LAZYSET(movespeed_modification, id, list(priority, flags, multiplicative_slowdown))
+ if(update)
+ update_movespeed(TRUE)
+ return TRUE
+
+/mob/proc/remove_movespeed_modifier(id, update = TRUE)
+ if(!LAZYACCESS(movespeed_modification, id))
+ return FALSE
+ LAZYREMOVE(movespeed_modification, id)
+ UNSETEMPTY(movespeed_modification)
+ if(update)
+ update_movespeed(FALSE)
+ return TRUE
+
+/mob/vv_edit_var(var_name, var_value)
+ var/slowdown_edit = (var_name == NAMEOF(src, cached_multiplicative_slowdown))
+ var/diff
+ if(slowdown_edit && isnum(cached_multiplicative_slowdown) && isnum(var_value))
+ remove_movespeed_modifier(MOVESPEED_ID_ADMIN_VAREDIT)
+ diff = var_value - cached_multiplicative_slowdown
+ . = ..()
+ if(. && slowdown_edit && isnum(diff))
+ add_movespeed_modifier(MOVESPEED_ID_ADMIN_VAREDIT, TRUE, 100, override = TRUE, multiplicative_slowdown = diff)
+
+/mob/proc/has_movespeed_modifier(id)
+ return LAZYACCESS(movespeed_modification, id)
+
+/mob/proc/update_config_movespeed()
+ add_movespeed_modifier(MOVESPEED_ID_CONFIG_SPEEDMOD, FALSE, 100, override = TRUE, multiplicative_slowdown = get_config_multiplicative_speed())
+
+/mob/proc/get_config_multiplicative_speed()
+ if(!islist(GLOB.mob_config_movespeed_type_lookup) || !GLOB.mob_config_movespeed_type_lookup[type])
+ return 0
+ else
+ return GLOB.mob_config_movespeed_type_lookup[type]
+
+/mob/proc/update_movespeed(resort = TRUE)
+ if(resort)
+ sort_movespeed_modlist()
+ . = 0
+ for(var/id in get_movespeed_modifiers())
+ var/list/data = movespeed_modification[id]
+ . += data[MOVESPEED_DATA_INDEX_MULTIPLICATIVE_SLOWDOWN]
+ cached_multiplicative_slowdown = .
+
+/mob/proc/get_movespeed_modifiers()
+ return movespeed_modification
+
+/mob/proc/movespeed_modifier_identical_check(list/mod1, list/mod2)
+ if(!islist(mod1) || !islist(mod2) || mod1.len < MOVESPEED_DATA_INDEX_MAX || mod2.len < MOVESPEED_DATA_INDEX_MAX)
+ return FALSE
+ for(var/i in 1 to MOVESPEED_DATA_INDEX_MAX)
+ if(mod1[i] != mod2[i])
+ return FALSE
+ return TRUE
+
+/mob/proc/total_multiplicative_slowdown()
+ . = 0
+ for(var/id in get_movespeed_modifiers())
+ var/list/data = movespeed_modification[id]
+ . += data[MOVESPEED_DATA_INDEX_MULTIPLICATIVE_SLOWDOWN]
+
+/proc/movespeed_data_null_check(list/data) //Determines if a data list is not meaningful and should be discarded.
+ . = TRUE
+ if(data[MOVESPEED_DATA_INDEX_MULTIPLICATIVE_SLOWDOWN])
+ . = FALSE
+
+/mob/proc/sort_movespeed_modlist() //Verifies it too. Sorts highest priority (first applied) to lowest priority (last applied)
+ if(!movespeed_modification)
+ return
+ var/list/assembled = list()
+ for(var/our_id in movespeed_modification)
+ var/list/our_data = movespeed_modification[our_id]
+ if(!islist(our_data) || (our_data.len < MOVESPEED_DATA_INDEX_PRIORITY) || movespeed_data_null_check(our_data))
+ movespeed_modification -= our_id
+ continue
+ var/our_priority = our_data[MOVESPEED_DATA_INDEX_PRIORITY]
+ var/resolved = FALSE
+ for(var/their_id in assembled)
+ var/list/their_data = assembled[their_id]
+ if(their_data[MOVESPEED_DATA_INDEX_PRIORITY] < our_priority)
+ assembled.Insert(assembled.Find(their_id), our_id)
+ assembled[our_id] = our_data
+ resolved = TRUE
+ break
+ if(!resolved)
+ assembled[our_id] = our_data
+ movespeed_modification = assembled
+ UNSETEMPTY(movespeed_modification)
\ No newline at end of file
diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm
index c6f24aa8f8..2ab3b5f57e 100644
--- a/code/modules/mob/say.dm
+++ b/code/modules/mob/say.dm
@@ -20,7 +20,7 @@
/mob/proc/whisper(message, datum/language/language=null)
say(message, language) //only living mobs actually whisper, everything else just talks
-/mob/verb/me_verb(message as message) // CIT CHANGE - makes me command input box bigger
+/mob/verb/me_verb(message as message)
set name = "Me"
set category = "IC"
@@ -30,7 +30,7 @@
message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN))
- usr.emote("me",1,message)
+ usr.emote("me",1,message,TRUE)
/mob/proc/say_dead(var/message)
var/name = real_name
@@ -74,15 +74,14 @@
if(key)
K = src.key
- message = src.say_quote(message, get_spans())
- message = emoji_parse(message)
- var/rendered = "
DEAD: [name] [alt_name] [message] "
- log_message("DEAD: [message]", INDIVIDUAL_SAY_LOG)
+ var/spanned = src.say_quote(message, get_spans())
+ var/rendered = "
DEAD: [name] [alt_name] [emoji_parse(spanned)] "
+ log_talk(message, LOG_SAY, tag="DEAD")
deadchat_broadcast(rendered, follow_target = src, speaker_key = K)
/mob/proc/check_emote(message)
if(copytext(message, 1, 2) == "*")
- emote(copytext(message, 2))
+ emote(copytext(message, 2), intentional = TRUE)
return 1
/mob/proc/hivecheck()
diff --git a/code/modules/modular_computers/computers/machinery/console_presets.dm b/code/modules/modular_computers/computers/machinery/console_presets.dm
index a726c7f6d1..8b70fd246c 100644
--- a/code/modules/modular_computers/computers/machinery/console_presets.dm
+++ b/code/modules/modular_computers/computers/machinery/console_presets.dm
@@ -1,9 +1,9 @@
/obj/machinery/modular_computer/console/preset
// Can be changed to give devices specific hardware
- var/_has_id_slot = 0
- var/_has_printer = 0
- var/_has_battery = 0
- var/_has_ai = 0
+ var/_has_id_slot = FALSE
+ var/_has_printer = FALSE
+ var/_has_battery = FALSE
+ var/_has_ai = FALSE
/obj/machinery/modular_computer/console/preset/Initialize()
. = ..()
@@ -29,9 +29,9 @@
// ===== ENGINEERING CONSOLE =====
/obj/machinery/modular_computer/console/preset/engineering
- console_department = "Engineering"
- name = "engineering console"
- desc = "A stationary computer. This one comes preloaded with engineering programs."
+ console_department = "Engineering"
+ name = "engineering console"
+ desc = "A stationary computer. This one comes preloaded with engineering programs."
/obj/machinery/modular_computer/console/preset/engineering/install_programs()
var/obj/item/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD]
@@ -41,10 +41,10 @@
// ===== RESEARCH CONSOLE =====
/obj/machinery/modular_computer/console/preset/research
- console_department = "Research"
- name = "research director's console"
- desc = "A stationary computer. This one comes preloaded with research programs."
- _has_ai = 1
+ console_department = "Research"
+ name = "research director's console"
+ desc = "A stationary computer. This one comes preloaded with research programs."
+ _has_ai = TRUE
/obj/machinery/modular_computer/console/preset/research/examine(mob/user)
..()
@@ -60,11 +60,11 @@
// ===== COMMAND CONSOLE =====
/obj/machinery/modular_computer/console/preset/command
- console_department = "Command"
- name = "command console"
- desc = "A stationary computer. This one comes preloaded with command programs."
- _has_id_slot = 1
- _has_printer = 1
+ console_department = "Command"
+ name = "command console"
+ desc = "A stationary computer. This one comes preloaded with command programs."
+ _has_id_slot = TRUE
+ _has_printer = TRUE
/obj/machinery/modular_computer/console/preset/command/examine(mob/user)
..()
@@ -77,9 +77,9 @@
// ===== CIVILIAN CONSOLE =====
/obj/machinery/modular_computer/console/preset/civilian
- console_department = "Civilian"
- name = "civilian console"
- desc = "A stationary computer. This one comes preloaded with generic programs."
+ console_department = "Civilian"
+ name = "civilian console"
+ desc = "A stationary computer. This one comes preloaded with generic programs."
/obj/machinery/modular_computer/console/preset/civilian/install_programs()
var/obj/item/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD]
diff --git a/code/modules/modular_computers/documentation.md b/code/modules/modular_computers/documentation.md
index 88d8602729..246da7c3d9 100644
--- a/code/modules/modular_computers/documentation.md
+++ b/code/modules/modular_computers/documentation.md
@@ -1,32 +1,62 @@
+# Modular computer programs
-
-#Modular computer programs
Ok. so a quick rundown on how to make a program. This is kind of a shitty documentation, but oh well I was asked to.
-
## Base setup
-This is how the base program is setup. the rest is mostly tgui stuff. I'll use the ntnetmonitor as a base
+This is how the base program is setup. the rest is mostly tgui stuff. I'll use the ntnetmonitor as a base
```DM
/datum/computer_file/program/ntnetmonitor
- filename = "ntmonitor" //This is obviously the name of the file itself. not much to be said
- filedesc = "NTNet Diagnostics and Monitoring" // This is sort of the official name. it's what shows up on the main menu
- program_icon_state = "comm_monitor" // This is what the screen will look like when the program is active
- extended_desc = "This program is a dummy. // This is a sort of a description, visible when looking on the ntnet
- size = 12 // size of the program. Big programs need more hard drive space. Don't make it too big though.
- requires_ntnet = 1 // If this is set, the program will not run without an ntnet connection, and will close if the connection is lost. Mainly for primarily online programs.
- required_access = access_network //This is access required to run the program itself. ONLY SET THIS FOR SUPER SECURE SHIT. This also acts as transfer_access as well.
- transfer_access = access_change_ids // This is the access needed to download from ntnet or host on the ptp program. This is what you want to use most of the time.
- available_on_ntnet = 1 //If it's available to download on ntnet. pretty self explanatory.
- available_on_syndinet = 0 // ditto but on emagged syndie net. Use this for antag programs
- usage_flags = PROGRAM_ALL // Bitflags (PROGRAM_CONSOLE, PROGRAM_LAPTOP, PROGRAM_TABLET combination) or PROGRAM_ALL
- //^^- The comment above sorta explains it. Use this to limit what kind of machines can run the program. For example, comms program should be limited to consoles and laptops.
- var/ui_header = "downloader_finished.gif" //This one is kinda cool. If you have the program minimized, this will show up in the header of the computer screen.
- //you can even have the program change what the header is based on the situation! see alarm.dm for an example.
+ /// This is obviously the name of the file itself. not much to be said
+ filename = "ntmonitor"
+
+ /// This is sort of the official name. it's what shows up on the main menu
+ filedesc = "NTNet Diagnostics and Monitoring"
+
+ /// This is what the screen will look like when the program is active
+ program_icon_state = "comm_monitor"
+
+ /// This is a sort of a description, visible when looking on the ntnet
+ extended_desc = "This program is a dummy."
+
+ /// size of the program. Big programs need more hard drive space. Don't
+ /// make it too big though.
+ size = 12
+
+ /// If this is set, the program will not run without an ntnet connection,
+ /// and will close if the connection is lost. Mainly for primarily online
+ /// programs.
+ requires_ntnet = 1
+
+ /// This is access required to run the program itself. ONLY SET THIS FOR
+ /// SUPER SECURE SHIT. This also acts as transfer_access as well.
+ required_access = access_network
+
+ /// This is the access needed to download from ntnet or host on the ptp
+ /// program. This is what you want to use most of the time.
+ transfer_access = access_change_ids
+
+ /// If it's available to download on ntnet. pretty self explanatory.
+ available_on_ntnet = 1
+
+ /// ditto but on emagged syndie net. Use this for antag programs
+ available_on_syndinet = 0
+
+ /// Bitflags (PROGRAM_CONSOLE, PROGRAM_LAPTOP, PROGRAM_TABLET combination)
+ /// or PROGRAM_ALL. Use this to limit what kind of machines can run the
+ /// program. For example, comms program should be limited to consoles and laptops.
+ usage_flags = PROGRAM_ALL
+
+ /// This one is kinda cool. If you have the program minimized, this will
+ /// show up in the header of the computer screen. You can even have the
+ /// program change what the header is based on the situation! See `alarm.dm`
+ /// for an example.
+ var/ui_header = "downloader_finished.gif"
```
-##Preinstalls
+## Preinstalls
+
Now. for pre-installing stuff.
Primarily done for consoles, there's an install_programs() proc in the console presets file in the machines folder.
@@ -42,4 +72,3 @@ Basically, you want to do cpu.hard_drive.store_file(new/*program path here*())
Probably pretty self explanatory, but just in case.
Will probably be expanded when new features come around or I get asked to mention something.
-
diff --git a/code/modules/modular_computers/file_system/programs/ntnrc_client.dm b/code/modules/modular_computers/file_system/programs/ntnrc_client.dm
index 813a3f47e7..29a1df9ebe 100644
--- a/code/modules/modular_computers/file_system/programs/ntnrc_client.dm
+++ b/code/modules/modular_computers/file_system/programs/ntnrc_client.dm
@@ -34,7 +34,7 @@
if(!message || !channel)
return
channel.add_message(message, username)
- log_talk(user,"[key_name(user)] as [username] sent to [channel.title]: [message]",LOGCHAT)
+ user.log_talk(message, LOG_CHAT, tag="as [username] to channel [channel.title]")
if("PRG_joinchannel")
. = 1
diff --git a/code/modules/ninja/energy_katana.dm b/code/modules/ninja/energy_katana.dm
index f562c51700..97c41faf12 100644
--- a/code/modules/ninja/energy_katana.dm
+++ b/code/modules/ninja/energy_katana.dm
@@ -32,12 +32,8 @@
dash_toggled = !dash_toggled
to_chat(user, "
You [dash_toggled ? "enable" : "disable"] the dash function on [src]. ")
-/obj/item/energy_katana/suicide_act(mob/living/carbon/user)
- user.visible_message("
[user] is slitting [user.p_their()] stomach open with [src]! It looks like [user.p_theyre()] trying to commit cyber-seppuku! ")
- playsound(src, 'sound/weapons/blade1.ogg', 50, 1)
- return(BRUTELOSS)
-
/obj/item/energy_katana/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
+ . = ..()
if(dash_toggled)
jaunt.Teleport(user, target)
if(proximity_flag && (isobj(target) || issilicon(target)))
diff --git a/code/modules/ninja/suit/n_suit_verbs/ninja_adrenaline.dm b/code/modules/ninja/suit/n_suit_verbs/ninja_adrenaline.dm
index 1385f4e54c..ff1f620a3a 100644
--- a/code/modules/ninja/suit/n_suit_verbs/ninja_adrenaline.dm
+++ b/code/modules/ninja/suit/n_suit_verbs/ninja_adrenaline.dm
@@ -9,7 +9,7 @@
H.SetKnockdown(0)
H.adjustStaminaLoss(-75)
H.stuttering = 0
- H.say(pick("A CORNERED FOX IS MORE DANGEROUS THAN A JACKAL!","HURT ME MOOORRREEE!","IMPRESSIVE!"))
+ H.say(pick("A CORNERED FOX IS MORE DANGEROUS THAN A JACKAL!","HURT ME MOOORRREEE!","IMPRESSIVE!"), forced = "ninjaboost")
a_boost--
to_chat(H, "
There are [a_boost] adrenaline boosts remaining. ")
s_coold = 3
diff --git a/code/modules/ninja/suit/n_suit_verbs/ninja_net.dm b/code/modules/ninja/suit/n_suit_verbs/ninja_net.dm
index 81d731b110..8c8f92e522 100644
--- a/code/modules/ninja/suit/n_suit_verbs/ninja_net.dm
+++ b/code/modules/ninja/suit/n_suit_verbs/ninja_net.dm
@@ -19,7 +19,7 @@
return
if(!ninjacost(200,N_STEALTH_CANCEL))
H.Beam(C,"n_beam",time=15)
- H.say("Get over here!")
+ H.say("Get over here!", forced = "ninja net")
var/obj/structure/energy_net/E = new /obj/structure/energy_net(C.drop_location())
E.affecting = C
E.master = H
diff --git a/code/modules/orbit/orbit.dm b/code/modules/orbit/orbit.dm
index e62b53604a..79685b9104 100644
--- a/code/modules/orbit/orbit.dm
+++ b/code/modules/orbit/orbit.dm
@@ -55,6 +55,11 @@
if (!targetloc || (!lock && orbiter.loc != lastloc && orbiter.loc != targetloc))
orbiter.stop_orbit()
return
+ var/turf/old_turf = get_turf(orbiter)
+ var/turf/new_turf = get_turf(targetloc)
+ if (old_turf?.z != new_turf?.z)
+ orbiter.onTransitZ(old_turf?.z, new_turf?.z)
+ // DO NOT PORT TO FORCEMOVE - MEMECODE WILL KILL MC
orbiter.loc = targetloc
orbiter.update_parallax_contents()
orbiter.update_light()
diff --git a/code/modules/paperwork/contract.dm b/code/modules/paperwork/contract.dm
index ac3e24030f..006151c4eb 100644
--- a/code/modules/paperwork/contract.dm
+++ b/code/modules/paperwork/contract.dm
@@ -100,7 +100,7 @@
/obj/item/paper/contract/infernal/suicide_act(mob/user)
if(signed && (user == target.current) && istype(user, /mob/living/carbon/human/))
var/mob/living/carbon/human/H = user
- H.forcesay("OH GREAT INFERNO! I DEMAND YOU COLLECT YOUR BOUNTY IMMEDIATELY!")
+ H.forcesay("OH GREAT INFERNO! I DEMAND YOU COLLECT YOUR BOUNTY IMMEDIATELY!", forced = "infernal contract suicide")
H.visible_message("
[H] holds up a contract claiming [user.p_their()] soul, then immediately catches fire. It looks like [user.p_theyre()] trying to commit suicide! ")
H.adjust_fire_stacks(20)
H.IgniteMob()
@@ -235,7 +235,7 @@
response = tgalert(target.current, "A devil is offering you another chance at life, at the price of your soul, do you accept?", "Infernal Resurrection", "Yes", "No", "Never for this round", 0, 200)
if(response == "Yes")
H.revive(1,0)
- add_logs(user, H, "infernally revived via contract")
+ log_combat(user, H, "infernally revived via contract")
user.visible_message("
With a sudden blaze, [H] stands back up. ")
H.fakefire()
fulfillContract(H, 1)//Revival contracts are always signed in blood
diff --git a/code/modules/paperwork/handlabeler.dm b/code/modules/paperwork/handlabeler.dm
index d63474e357..7c0edf3f0e 100644
--- a/code/modules/paperwork/handlabeler.dm
+++ b/code/modules/paperwork/handlabeler.dm
@@ -36,6 +36,7 @@
return OXYLOSS
/obj/item/hand_labeler/afterattack(atom/A, mob/user,proximity)
+ . = ..()
if(!proximity)
return
if(!mode) //if it's off, give up.
@@ -89,7 +90,7 @@
name = "cyborg-hand labeler"
/obj/item/hand_labeler/borg/afterattack(atom/A, mob/user, proximity)
- ..(A, user, proximity)
+ . = ..(A, user, proximity)
if(!iscyborg(user))
return
diff --git a/code/modules/paperwork/paperplane.dm b/code/modules/paperwork/paperplane.dm
index 7cc2580ee3..73eadfbc45 100644
--- a/code/modules/paperwork/paperplane.dm
+++ b/code/modules/paperwork/paperplane.dm
@@ -98,6 +98,10 @@
H.Knockdown(40)
H.emote("scream")
+/obj/item/paper/examine(mob/user)
+ ..()
+ to_chat(user, "
Alt-click [src] to fold it into a paper plane. ")
+
/obj/item/paper/AltClick(mob/living/carbon/user, obj/item/I)
if(!istype(user) || !user.canUseTopic(src, BE_CLOSE, ismonkey(user)))
return
diff --git a/code/modules/paperwork/pen.dm b/code/modules/paperwork/pen.dm
index 9b7d3ae884..c99b094ab0 100644
--- a/code/modules/paperwork/pen.dm
+++ b/code/modules/paperwork/pen.dm
@@ -117,12 +117,13 @@
to_chat(M, "
You feel a tiny prick! ")
. = 1
- add_logs(user, M, "stabbed", src)
+ log_combat(user, M, "stabbed", src)
else
. = ..()
/obj/item/pen/afterattack(obj/O, mob/living/user, proximity)
+ . = ..()
//Changing Name/Description of items. Only works if they have the 'unique_rename' flag set
if(isobj(O) && proximity && (O.obj_flags & UNIQUE_RENAME))
var/penchoice = input(user, "What would you like to edit?", "Rename or change description?") as null|anything in list("Rename","Change description")
diff --git a/code/modules/paperwork/photocopier.dm b/code/modules/paperwork/photocopier.dm
index b8fac81df8..4e74ccc44b 100644
--- a/code/modules/paperwork/photocopier.dm
+++ b/code/modules/paperwork/photocopier.dm
@@ -175,7 +175,7 @@
to_chat(usr, "
No images saved ")
return
var/datum/picture/selection = tempAI.aicamera.selectpicture(usr)
- var/obj/item/photo = new(loc, selection)
+ var/obj/item/photo/photo = new(loc, selection)
photo.pixel_x = rand(-10, 10)
photo.pixel_y = rand(-10, 10)
toner -= 5 //AI prints color pictures only, thus they can do it more efficiently
diff --git a/code/modules/photography/_pictures.dm b/code/modules/photography/_pictures.dm
index 24892cf541..9f5babbbbc 100644
--- a/code/modules/photography/_pictures.dm
+++ b/code/modules/photography/_pictures.dm
@@ -4,21 +4,29 @@
var/caption
var/icon/picture_image
var/icon/picture_icon
- var/psize_x = 0
- var/psize_y = 0
+ var/psize_x = 96
+ var/psize_y = 96
var/has_blueprints = FALSE
var/logpath //If the picture has been logged this is the path.
var/id //this var is NOT protected because the worst you can do with this that you couldn't do otherwise is overwrite photos, and photos aren't going to be used as attack logs/investigations anytime soon.
-/datum/picture/New(name = "picture", desc = "This is a picture. A bugged one. Report it to coderbus!", image, icon, size_x = 96, size_y = 96, bp = FALSE, caption_, autogenerate_icon = FALSE)
- picture_name = name
- picture_desc = desc
- picture_image = image
- picture_icon = icon
- psize_x = size_x
- psize_y = size_y
- has_blueprints = bp
- caption = caption_
+/datum/picture/New(name, desc, image, icon, size_x, size_y, bp, caption_, autogenerate_icon)
+ if(!isnull(name))
+ picture_name = name
+ if(!isnull(desc))
+ picture_desc = desc
+ if(!isnull(image))
+ picture_image = image
+ if(!isnull(icon))
+ picture_icon = icon
+ if(!isnull(psize_x))
+ psize_x = size_x
+ if(!isnull(psize_y))
+ psize_y = size_y
+ if(!isnull(bp))
+ has_blueprints = bp
+ if(!isnull(caption_))
+ caption = caption_
if(autogenerate_icon && !picture_icon && picture_image)
regenerate_small_icon()
diff --git a/code/modules/photography/camera/camera.dm b/code/modules/photography/camera/camera.dm
index a3027c951f..b5e027efe2 100644
--- a/code/modules/photography/camera/camera.dm
+++ b/code/modules/photography/camera/camera.dm
@@ -6,12 +6,12 @@
icon = 'icons/obj/items_and_weapons.dmi'
desc = "A polaroid camera."
icon_state = "camera"
- item_state = "electropack"
+ item_state = "camera"
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
w_class = WEIGHT_CLASS_SMALL
flags_1 = CONDUCT_1
- slot_flags = ITEM_SLOT_BELT
+ slot_flags = ITEM_SLOT_NECK
materials = list(MAT_METAL = 50, MAT_GLASS = 150)
var/state_on = "camera"
var/state_off = "camera_off"
@@ -24,21 +24,12 @@
var/obj/item/disk/holodisk/disk
var/sound/custom_sound
var/silent = FALSE
- var/picture_size_x = 1
- var/picture_size_y = 1
- var/picture_size_x_min = 0
- var/picture_size_y_min = 0
- var/picture_size_x_max = 3
- var/picture_size_y_max = 3
-
-/obj/item/camera/CheckParts(list/parts_list)
- ..()
- var/obj/item/camera/C = locate(/obj/item/camera) in contents
- if(C)
- pictures_max = C.pictures_max
- pictures_left = C.pictures_left
- visible_message("[C] has been imbued with godlike power!")
- qdel(C)
+ var/picture_size_x = 2
+ var/picture_size_y = 2
+ var/picture_size_x_min = 1
+ var/picture_size_y_min = 1
+ var/picture_size_x_max = 4
+ var/picture_size_y_max = 4
/obj/item/camera/attack_self(mob/user)
if(!disk)
@@ -47,6 +38,10 @@
user.put_in_hands(disk)
disk = null
+/obj/item/camera/examine(mob/user)
+ . = ..()
+ to_chat(user, "
Alt-click to change its focusing, allowing you to set how big of an area it will capture. ")
+
/obj/item/camera/AltClick(mob/user)
var/desired_x = input(user, "How high do you want the camera to shoot, between [picture_size_x_min] and [picture_size_x_max]?", "Zoom", picture_size_x) as num
var/desired_y = input(user, "How wide do you want the camera to shoot, between [picture_size_y_min] and [picture_size_y_max]?", "Zoom", picture_size_y) as num
@@ -86,9 +81,23 @@
..()
to_chat(user, "It has [pictures_left] photos left.")
+//user can be atom or mob
/obj/item/camera/proc/can_target(atom/target, mob/user, prox_flag)
- if(!on || blending || !pictures_left || (!isturf(target) && !isturf(target.loc)) || !((isAI(user) && GLOB.cameranet.checkTurfVis(get_turf(target))) || ((user.client && (get_turf(target) in get_hear(user.client.view, user)) || (get_turf(target) in get_hear(world.view, user))))))
+ if(!on || blending || !pictures_left)
return FALSE
+ var/turf/T = get_turf(target)
+ if(!T)
+ return FALSE
+ if(istype(user))
+ if(isAI(user) && !GLOB.cameranet.checkTurfVis(T))
+ return FALSE
+ else if(user.client && !(get_turf(target) in get_hear(user.client.view, user)))
+ return FALSE
+ else if(!(get_turf(target) in get_hear(world.view, user)))
+ return FALSE
+ else //user is an atom
+ if(!(get_turf(target) in view(world.view, user)))
+ return FALSE
return TRUE
/obj/item/camera/afterattack(atom/target, mob/user, flag)
@@ -109,10 +118,16 @@
return
on = FALSE
- addtimer(CALLBACK(src, .proc/cooldown), cooldown)
+
+ var/realcooldown = cooldown
+ var/mob/living/carbon/human/H = user
+ if (H.has_trait(TRAIT_PHOTOGRAPHER))
+ realcooldown *= 0.5
+ addtimer(CALLBACK(src, .proc/cooldown), realcooldown)
+
icon_state = state_off
- INVOKE_ASYNC(src, .proc/captureimage, target, user, flag, picture_size_x, picture_size_y)
+ INVOKE_ASYNC(src, .proc/captureimage, target, user, flag, picture_size_x - 1, picture_size_y - 1)
/obj/item/camera/proc/cooldown()
@@ -134,7 +149,7 @@
return FALSE
size_x = CLAMP(size_x, 0, CAMERA_PICTURE_SIZE_HARD_LIMIT)
size_y = CLAMP(size_y, 0, CAMERA_PICTURE_SIZE_HARD_LIMIT)
- var/list/desc = list()
+ var/list/desc = list("This is a photo of an area of [size_x+1] meters by [size_y+1] meters.")
var/ai_user = isAI(user)
var/list/seen
var/list/viewlist = (user && user.client)? getviewsize(user.client.view) : getviewsize(world.view)
@@ -146,15 +161,15 @@
var/blueprints = FALSE
var/clone_area = SSmapping.RequestBlockReservation(size_x * 2 + 1, size_y * 2 + 1)
for(var/turf/T in block(locate(target_turf.x - size_x, target_turf.y - size_y, target_turf.z), locate(target_turf.x + size_x, target_turf.y + size_y, target_turf.z)))
- if((ai_user && GLOB.cameranet.checkTurfVis(T)) || T in seen)
+ if((ai_user && GLOB.cameranet.checkTurfVis(T)) || (T in seen))
turfs += T
for(var/mob/M in T)
mobs += M
if(locate(/obj/item/areaeditor/blueprints) in T)
blueprints = TRUE
- CHECK_TICK
for(var/i in mobs)
- desc += camera_get_mobdesc(i)
+ var/mob/M = i
+ desc += M.get_photo_description(src)
var/psize_x = (size_x * 2 + 1) * world.icon_size
var/psize_y = (size_y * 2 + 1) * world.icon_size
@@ -165,34 +180,10 @@
temp.Scale(psize_x, psize_y)
temp.Blend(get_icon, ICON_OVERLAY)
- var/datum/picture/P = new("picture", desc.Join(""), temp, null, psize_x, psize_y, blueprints)
+ var/datum/picture/P = new("picture", desc.Join(" "), temp, null, psize_x, psize_y, blueprints)
after_picture(user, P, flag)
blending = FALSE
-/obj/item/camera/proc/camera_get_mobdesc(mob/M)
- var/list/mob_details = list()
- if(M.invisibility)
- if(see_ghosts && isobserver(M))
- mob_details += "You can also see a g-g-g-g-ghooooost! "
- else
- return mob_details
- var/list/holding = list()
- if(isliving(M))
- var/mob/living/L = M
- var/len = length(L.held_items)
- if(len)
- for(var/obj/item/I in L.held_items)
- if(!holding.len)
- holding += "They are holding \a [I]"
- else if(L.held_items.Find(I) == len)
- holding += ", and \a [I]."
- else
- holding += ", \a [I]"
- holding += "."
- holding = holding.Join("")
- mob_details += "You can also see [L] on the photo[L.health < (L.maxHealth * 0.75) ? ", looking a bit hurt":""][holding ? ". [holding]":"."]."
- return mob_details.Join("")
-
/obj/item/camera/proc/after_picture(mob/user, datum/picture/picture, proximity_flag)
printpicture(user, picture)
@@ -204,15 +195,15 @@
to_chat(user, "
[pictures_left] photos left. ")
var/customize = alert(user, "Do you want to customize the photo?", "Customization", "Yes", "No")
if(customize == "Yes")
- var/name1 = stripped_input(user, "Set a name for this photo, or leave blank. 32 characters max.", "Name", max_length = 32) as text|null
- var/desc1 = stripped_input(user, "Set a description to add to photo, or leave blank. 128 characters max.", "Caption", max_length = 128) as text|null
- var/caption = stripped_input(user, "Set a caption for this photo, or leave blank. 256 characters max.", "Caption", max_length = 256) as text|null
+ var/name1 = stripped_input(user, "Set a name for this photo, or leave blank. 32 characters max.", "Name", max_length = 32)
+ var/desc1 = stripped_input(user, "Set a description to add to photo, or leave blank. 128 characters max.", "Caption", max_length = 128)
+ var/caption = stripped_input(user, "Set a caption for this photo, or leave blank. 256 characters max.", "Caption", max_length = 256)
if(name1)
picture.picture_name = name1
if(desc1)
picture.picture_desc = "[desc1] - [picture.picture_desc]"
if(caption)
picture.caption = caption
- p.set_picture(picture)
+ p.set_picture(picture, TRUE, TRUE)
if(CONFIG_GET(flag/picture_logging_camera))
picture.log_to_file()
diff --git a/code/modules/photography/camera/camera_image_capturing.dm b/code/modules/photography/camera/camera_image_capturing.dm
index 7b9fe94f44..e7072026f5 100644
--- a/code/modules/photography/camera/camera_image_capturing.dm
+++ b/code/modules/photography/camera/camera_image_capturing.dm
@@ -1,6 +1,6 @@
/obj/effect/appearance_clone
-/obj/effect/appearance_clone/New(loc, atom/A) //Intentionally not Initialize().
+/obj/effect/appearance_clone/New(loc, atom/A) //Intentionally not Initialize(), to make sure the clone assumes the intended appearance in time for the camera getFlatIcon.
if(istype(A))
appearance = A.appearance
dir = A.dir
@@ -73,7 +73,8 @@
xo += AM.step_x
yo += AM.step_y
var/icon/img = getFlatIcon(A)
- res.Blend(img, blendMode2iconMode(A.blend_mode), xo, yo)
+ if(img)
+ res.Blend(img, blendMode2iconMode(A.blend_mode), xo, yo)
CHECK_TICK
if(!silent)
diff --git a/code/modules/photography/photos/album.dm b/code/modules/photography/photos/album.dm
index b4481eb008..bd77d468d7 100644
--- a/code/modules/photography/photos/album.dm
+++ b/code/modules/photography/photos/album.dm
@@ -37,21 +37,39 @@
continue
. |= P.picture.id
+//Manual loading, DO NOT USE FOR HARDCODED/MAPPED IN ALBUMS. This is for if an album needs to be loaded mid-round from an ID.
+/obj/item/storage/photo_album/proc/persistence_load()
+ var/list/data = SSpersistence.GetPhotoAlbums()
+ if(data[persistence_id])
+ populate_from_id_list(data[persistence_id])
+
/obj/item/storage/photo_album/proc/populate_from_id_list(list/ids)
+ var/list/current_ids = get_picture_id_list()
for(var/i in ids)
+ if(i in current_ids)
+ continue
var/obj/item/photo/P = load_photo_from_disk(i)
if(istype(P))
if(!SEND_SIGNAL(src, COMSIG_TRY_STORAGE_INSERT, P, null, TRUE, TRUE))
qdel(P)
-#define ALBUM_DEFINE(id) /obj/item/storage/photo_album/##id/persistence_id = #id
+/obj/item/storage/photo_album/HoS
+ persistence_id = "HoS"
-ALBUM_DEFINE(HoS)
-ALBUM_DEFINE(RD)
-ALBUM_DEFINE(HoP)
-ALBUM_DEFINE(Captain)
-ALBUM_DEFINE(CMO)
-ALBUM_DEFINE(QM)
-ALBUM_DEFINE(CE)
+/obj/item/storage/photo_album/RD
+ persistence_id = "RD"
-#undef ALBUM_DEFINE
+/obj/item/storage/photo_album/HoP
+ persistence_id = "HoP"
+
+/obj/item/storage/photo_album/Captain
+ persistence_id = "Captain"
+
+/obj/item/storage/photo_album/CMO
+ persistence_id = "CMO"
+
+/obj/item/storage/photo_album/QM
+ persistence_id = "QM"
+
+/obj/item/storage/photo_album/CE
+ persistence_id = "CE"
diff --git a/code/modules/photography/photos/frame.dm b/code/modules/photography/photos/frame.dm
index c3b9530e07..f379541b9c 100644
--- a/code/modules/photography/photos/frame.dm
+++ b/code/modules/photography/photos/frame.dm
@@ -90,6 +90,12 @@
if(istype(framed) && istype(framed.picture))
return framed.picture.id
+//Manual loading, DO NOT USE FOR HARDCODED/MAPPED IN ALBUMS. This is for if an album needs to be loaded mid-round from an ID.
+/obj/structure/sign/picture_frame/proc/persistence_load()
+ var/list/data = SSpersistence.GetPhotoFrames()
+ if(data[persistence_id])
+ load_from_id(data[persistence_id])
+
/obj/structure/sign/picture_frame/proc/load_from_id(id)
var/obj/item/photo/P = load_photo_from_disk(id)
if(istype(P))
diff --git a/code/modules/photography/photos/photo.dm b/code/modules/photography/photos/photo.dm
index b7a37febab..99e61cf3f0 100644
--- a/code/modules/photography/photos/photo.dm
+++ b/code/modules/photography/photos/photo.dm
@@ -12,13 +12,12 @@
grind_results = list("iodine" = 4)
var/datum/picture/picture
var/scribble //Scribble on the back.
- var/sillynewscastervar //Photo objects with this set to 1 will not be ejected by a newscaster. Only gets set to 1 if a silicon puts one of their images into a newscaster
/obj/item/photo/Initialize(mapload, datum/picture/P, datum_name = TRUE, datum_desc = TRUE)
- set_picture(P, datum_name, datum_desc)
+ set_picture(P, datum_name, datum_desc, TRUE)
return ..()
-/obj/item/photo/proc/set_picture(datum/picture/P, setname, setdesc)
+/obj/item/photo/proc/set_picture(datum/picture/P, setname, setdesc, name_override = FALSE)
if(!istype(P))
return
picture = P
@@ -26,7 +25,10 @@
if(P.caption)
scribble = P.caption
if(setname && P.picture_name)
- name = P.picture_name
+ if(name_override)
+ name = P.picture_name
+ else
+ name = "photo - [P.picture_name]"
if(setdesc && P.picture_desc)
desc = P.picture_desc
diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm
index e9deb8233b..f48826df9d 100644
--- a/code/modules/power/apc.dm
+++ b/code/modules/power/apc.dm
@@ -101,10 +101,11 @@
var/update_state = -1
var/update_overlay = -1
var/icon_update_needed = FALSE
+ var/obj/machinery/computer/apc_control/remote_control = null
/obj/machinery/power/apc/unlocked
locked = FALSE
-
+
/obj/machinery/power/apc/syndicate //general syndicate access
req_access = list(ACCESS_SYNDICATE)
@@ -117,6 +118,25 @@
/obj/machinery/power/apc/highcap/fifteen_k
cell_type = /obj/item/stock_parts/cell/high/plus
+/obj/machinery/power/apc/auto_name
+ auto_name = TRUE
+
+/obj/machinery/power/apc/auto_name/north //Pixel offsets get overwritten on New()
+ dir = NORTH
+ pixel_y = 23
+
+/obj/machinery/power/apc/auto_name/south
+ dir = SOUTH
+ pixel_y = -23
+
+/obj/machinery/power/apc/auto_name/east
+ dir = EAST
+ pixel_x = 24
+
+/obj/machinery/power/apc/auto_name/west
+ dir = WEST
+ pixel_x = -25
+
/obj/machinery/power/apc/get_cell()
return cell
@@ -245,6 +265,11 @@
if(integration_cog && is_servant_of_ratvar(user))
to_chat(user, "
There is an integration cog installed! ")
+ to_chat(user, "
Alt-Click the APC to [ locked ? "unlock" : "lock"] the interface. ")
+
+ if(issilicon(user))
+ to_chat(user, "
Ctrl-Click the APC to switch the breaker [ operating ? "off" : "on"]. ")
+
// update the APC icon to show the three base states
// also add overlays for indicator lights
/obj/machinery/power/apc/update_icon()
@@ -280,19 +305,17 @@
icon_state = "apc0"
if(!(update_state & UPSTATE_ALLGOOD))
- cut_overlays()
+ SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays)
if(update & 2)
- cut_overlays()
+ SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays)
if(!(stat & (BROKEN|MAINT)) && update_state & UPSTATE_ALLGOOD)
- var/list/O = list(
- "apcox-[locked]",
- "apco3-[charging]")
+ SSvis_overlays.add_vis_overlay(src, icon, "apcox-[locked]", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir)
+ SSvis_overlays.add_vis_overlay(src, icon, "apco3-[charging]", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir)
if(operating)
- O += "apco0-[equipment]"
- O += "apco1-[lighting]"
- O += "apco2-[environ]"
- add_overlay(O)
+ SSvis_overlays.add_vis_overlay(src, icon, "apco0-[equipment]", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir)
+ SSvis_overlays.add_vis_overlay(src, icon, "apco1-[lighting]", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir)
+ SSvis_overlays.add_vis_overlay(src, icon, "apco2-[environ]", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir)
// And now, separately for cleanness, the lighting changing
if(update_state & UPSTATE_ALLGOOD)
@@ -452,6 +475,8 @@
return
/obj/machinery/power/apc/screwdriver_act(mob/living/user, obj/item/W)
+ if(..())
+ return TRUE
. = TRUE
if(opened)
if(cell)
@@ -699,6 +724,13 @@
else
to_chat(user, "
Access denied. ")
+/obj/machinery/power/apc/proc/toggle_nightshift_lights(mob/living/user)
+ if(last_nightshift_switch > world.time - 100) //~10 seconds between each toggle to prevent spamming
+ to_chat(usr, "
[src]'s night lighting circuit breaker is still cycling! ")
+ return
+ last_nightshift_switch = world.time
+ set_nightshift(!nightshift_lights)
+
/obj/machinery/power/apc/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir)
if(damage_flag == "melee" && damage_amount < 10 && (!(stat & BROKEN) || malfai))
return 0
@@ -863,6 +895,16 @@
return FALSE
return TRUE
+/obj/machinery/power/apc/can_interact(mob/user)
+ . = ..()
+ if (!. && !QDELETED(remote_control))
+ . = remote_control.can_interact(user)
+
+/obj/machinery/power/apc/ui_status(mob/user)
+ . = ..()
+ if (!QDELETED(remote_control) && user == remote_control.operator)
+ . = UI_INTERACTIVE
+
/obj/machinery/power/apc/ui_act(action, params)
if(..() || !can_use(usr, 1) || (locked && !usr.has_unlimited_silicon_privilege && !failure_timer && !(integration_cog && (is_servant_of_ratvar(usr)))))
return
@@ -882,11 +924,7 @@
toggle_breaker()
. = TRUE
if("toggle_nightshift")
- if(last_nightshift_switch > world.time + 100) //don't spam..
- to_chat(usr, "
[src]'s night lighting circuit breaker is still cycling! ")
- return
- last_nightshift_switch = world.time
- set_nightshift(!nightshift_lights)
+ toggle_nightshift_lights()
. = TRUE
if("charge")
chargemode = !chargemode
diff --git a/code/modules/power/generator.dm b/code/modules/power/generator.dm
index 04645b2267..f3f960c0ee 100644
--- a/code/modules/power/generator.dm
+++ b/code/modules/power/generator.dm
@@ -211,6 +211,8 @@
return TRUE
/obj/machinery/power/generator/screwdriver_act(mob/user, obj/item/I)
+ if(..())
+ return TRUE
panel_open = !panel_open
I.play_tool_sound(src)
to_chat(user, "
You [panel_open?"open":"close"] the panel on [src]. ")
diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm
index 0b6cf36921..a32d8dca45 100644
--- a/code/modules/power/lighting.dm
+++ b/code/modules/power/lighting.dm
@@ -345,6 +345,7 @@
else if(has_emergency_power(LIGHT_EMERGENCY_POWER_USE) && !turned_off())
use_power = IDLE_POWER_USE
emergency_mode = TRUE
+ START_PROCESSING(SSmachines, src)
else
use_power = IDLE_POWER_USE
set_light(0)
@@ -361,7 +362,11 @@
/obj/machinery/light/process()
- if(has_power() && cell)
+ if (!cell)
+ return PROCESS_KILL
+ if(has_power())
+ if (cell.charge == cell.maxcharge)
+ return PROCESS_KILL
cell.charge = min(cell.maxcharge, cell.charge + LIGHT_EMERGENCY_POWER_USE) //Recharge emergency power automatically while not using it
if(emergency_mode && !use_emergency_power(LIGHT_EMERGENCY_POWER_USE))
update(FALSE) //Disables emergency mode and sets the color to normal
diff --git a/code/modules/power/power.dm b/code/modules/power/power.dm
index 0bd230a166..f6110cb081 100644
--- a/code/modules/power/power.dm
+++ b/code/modules/power/power.dm
@@ -28,6 +28,9 @@
/obj/machinery/power/proc/add_avail(amount)
if(powernet)
powernet.newavail += amount
+ return TRUE
+ else
+ return FALSE
/obj/machinery/power/proc/add_load(amount)
if(powernet)
@@ -52,13 +55,13 @@
// defaults to power_channel
/obj/machinery/proc/powered(var/chan = -1) // defaults to power_channel
if(!loc)
- return 0
+ return FALSE
if(!use_power)
- return 1
+ return TRUE
var/area/A = get_area(src) // make sure it's in an area
if(!A)
- return 0 // if not, then not powered
+ return FALSE // if not, then not powered
if(chan == -1)
chan = power_channel
return A.powered(chan) // return power status of the area
@@ -95,21 +98,21 @@
/obj/machinery/power/proc/connect_to_network()
var/turf/T = src.loc
if(!T || !istype(T))
- return 0
+ return FALSE
var/obj/structure/cable/C = T.get_cable_node() //check if we have a node cable on the machine turf, the first found is picked
if(!C || !C.powernet)
- return 0
+ return FALSE
C.powernet.add_machine(src)
- return 1
+ return TRUE
// remove and disconnect the machine from its current powernet
/obj/machinery/power/proc/disconnect_from_network()
if(!powernet)
- return 0
+ return FALSE
powernet.remove_machine(src)
- return 1
+ return TRUE
// attach a wire to a power machine - leads from the turf you are standing on
//almost never called, overwritten by all power machines but terminal and generator
@@ -330,7 +333,7 @@
power_source = cell
shock_damage = cell_damage
var/drained_hp = M.electrocute_act(shock_damage, source, siemens_coeff) //zzzzzzap!
- add_logs(source, M, "electrocuted")
+ log_combat(source, M, "electrocuted")
var/drained_energy = drained_hp*20
diff --git a/code/modules/power/rtg.dm b/code/modules/power/rtg.dm
index 79e94be7f0..9dca8f3124 100644
--- a/code/modules/power/rtg.dm
+++ b/code/modules/power/rtg.dm
@@ -43,12 +43,6 @@
return
return ..()
-//ATTACK HAND IGNORING PARENT RETURN VALUE
-/obj/machinery/power/rtg/attack_hand(mob/user)
- if(user.a_intent == INTENT_GRAB && user_buckle_mob(user.pulling, user, check_loc = 0))
- return
- . = ..()
-
/obj/machinery/power/rtg/advanced
desc = "An advanced RTG capable of moderating isotope decay, increasing power output but reducing lifetime. It uses plasma-fueled radiation collectors to increase output even further."
power_gen = 1250 // 2500 on T1, 10000 on T4.
diff --git a/code/modules/power/singularity/collector.dm b/code/modules/power/singularity/collector.dm
index 0e3545b1a7..4fe77def51 100644
--- a/code/modules/power/singularity/collector.dm
+++ b/code/modules/power/singularity/collector.dm
@@ -134,6 +134,8 @@
return TRUE
/obj/machinery/power/rad_collector/screwdriver_act(mob/living/user, obj/item/I)
+ if(..())
+ return TRUE
if(loaded_tank)
to_chat(user, "
Remove the plasma tank first! ")
else
diff --git a/code/modules/power/singularity/containment_field.dm b/code/modules/power/singularity/containment_field.dm
index f6004b664a..3b0f7e7f6f 100644
--- a/code/modules/power/singularity/containment_field.dm
+++ b/code/modules/power/singularity/containment_field.dm
@@ -87,7 +87,7 @@
/obj/machinery/field
var/hasShocked = FALSE //Used to add a delay between shocks. In some cases this used to crash servers by spawning hundreds of sparks every second.
-/obj/machinery/field/CollidedWith(atom/movable/mover)
+/obj/machinery/field/Bumped(atom/movable/mover)
if(hasShocked)
return
if(isliving(mover))
diff --git a/code/modules/power/singularity/emitter.dm b/code/modules/power/singularity/emitter.dm
index bf03256219..eebdd5c9da 100644
--- a/code/modules/power/singularity/emitter.dm
+++ b/code/modules/power/singularity/emitter.dm
@@ -31,16 +31,19 @@
var/allow_switch_interact = TRUE
var/projectile_type = /obj/item/projectile/beam/emitter
-
var/projectile_sound = 'sound/weapons/emitter.ogg'
-
var/datum/effect_system/spark_spread/sparks
+ var/obj/item/gun/energy/gun
+ var/list/gun_properties
+ var/mode = 0
+
// The following 3 vars are mostly for the prototype
var/manual = FALSE
var/charge = 0
var/last_projectile_params
+
/obj/machinery/power/emitter/anchored
anchored = TRUE
@@ -269,10 +272,14 @@
return TRUE
/obj/machinery/power/emitter/crowbar_act(mob/living/user, obj/item/I)
+ if(panel_open && gun)
+ return remove_gun(user)
default_deconstruction_crowbar(I)
return TRUE
/obj/machinery/power/emitter/screwdriver_act(mob/living/user, obj/item/I)
+ if(..())
+ return TRUE
default_deconstruction_screwdriver(user, "emitter_open", "emitter", I)
return TRUE
@@ -295,9 +302,42 @@
else if(is_wire_tool(I) && panel_open)
wires.interact(user)
return
-
+ else if(panel_open && !gun && istype(I,/obj/item/gun/energy))
+ if(integrate(I,user))
+ return
return ..()
+/obj/machinery/power/emitter/proc/integrate(obj/item/gun/energy/E,mob/user)
+ if(istype(E, /obj/item/gun/energy))
+ if(!user.transferItemToLoc(E, src))
+ return
+ gun = E
+ gun_properties = gun.get_turret_properties()
+ set_projectile()
+ return TRUE
+
+/obj/machinery/power/emitter/proc/remove_gun(mob/user)
+ if(!gun)
+ return
+ user.put_in_hands(gun)
+ gun = null
+ playsound(src, 'sound/items/deconstruct.ogg', 50, 1)
+ gun_properties = list()
+ set_projectile()
+ return TRUE
+
+/obj/machinery/power/emitter/proc/set_projectile()
+ if(LAZYLEN(gun_properties))
+ if(mode || !gun_properties["lethal_projectile"])
+ projectile_type = gun_properties["stun_projectile"]
+ projectile_sound = gun_properties["stun_projectile_sound"]
+ else
+ projectile_type = gun_properties["lethal_projectile"]
+ projectile_sound = gun_properties["lethal_projectile_sound"]
+ return
+ projectile_type = initial(projectile_type)
+ projectile_sound = initial(projectile_sound)
+
/obj/machinery/power/emitter/emag_act(mob/user)
if(obj_flags & EMAGGED)
return
@@ -405,7 +445,7 @@
var/delay = 0
/obj/item/turret_control/afterattack(atom/targeted_atom, mob/user, proxflag, clickparams)
- ..()
+ . = ..()
var/obj/machinery/power/emitter/E = user.buckled
E.setDir(get_dir(E,targeted_atom))
user.setDir(E.dir)
diff --git a/code/modules/power/singularity/generator.dm b/code/modules/power/singularity/generator.dm
index 4cc1143c02..a7df7198c0 100644
--- a/code/modules/power/singularity/generator.dm
+++ b/code/modules/power/singularity/generator.dm
@@ -17,12 +17,6 @@
var/energy = 0
var/creation_type = /obj/singularity
-//ATTACK HAND IGNORING PARENT RETURN VALUE
-/obj/machinery/the_singularitygen/attack_hand(mob/user)
- if(user.a_intent == INTENT_GRAB && user_buckle_mob(user.pulling, user, check_loc = 0))
- return
- . = ..()
-
/obj/machinery/the_singularitygen/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/wrench))
default_unfasten_wrench(user, W, 0)
diff --git a/code/modules/power/singularity/narsie.dm b/code/modules/power/singularity/narsie.dm
index 7f40f81903..a073997b9d 100644
--- a/code/modules/power/singularity/narsie.dm
+++ b/code/modules/power/singularity/narsie.dm
@@ -1,5 +1,5 @@
/obj/singularity/narsie //Moving narsie to a child object of the singularity so it can be made to function differently. --NEO
- name = "Nar-sie's Avatar"
+ name = "Nar'Sie's Avatar"
desc = "Your mind begins to bubble and ooze as it tries to comprehend what it sees."
icon = 'icons/obj/magic_terror.dmi'
pixel_x = -89
@@ -15,10 +15,10 @@
light_range = 15
light_color = rgb(255, 0, 0)
gender = FEMALE
- var/clashing = FALSE //If Nar-Sie is fighting Ratvar
+ var/clashing = FALSE //If Nar'Sie is fighting Ratvar
/obj/singularity/narsie/large
- name = "Nar-Sie"
+ name = "Nar'Sie"
icon = 'icons/obj/narsie.dmi'
// Pixel stuff centers Narsie.
pixel_x = -236
@@ -29,13 +29,13 @@
/obj/singularity/narsie/large/Initialize()
. = ..()
- send_to_playing_players("
NAR-SIE HAS RISEN ")
+ send_to_playing_players("
NAR'SIE HAS RISEN ")
sound_to_playing_players('sound/creatures/narsie_rises.ogg')
var/area/A = get_area(src)
if(A)
var/mutable_appearance/alert_overlay = mutable_appearance('icons/effects/cult_effects.dmi', "ghostalertsie")
- notify_ghosts("Nar-Sie has risen in \the [A.name]. Reach out to the Geometer to be given a new shell for your soul.", source = src, alert_overlay = alert_overlay, action=NOTIFY_ATTACK)
+ notify_ghosts("Nar'Sie has risen in \the [A.name]. Reach out to the Geometer to be given a new shell for your soul.", source = src, alert_overlay = alert_overlay, action=NOTIFY_ATTACK)
INVOKE_ASYNC(src, .proc/narsie_spawn_animation)
/obj/singularity/narsie/large/cult // For the new cult ending, guaranteed to end the round within 3 minutes
@@ -118,10 +118,10 @@
return clashing
-/obj/singularity/narsie/Collide(atom/A)
+/obj/singularity/narsie/Bump(atom/A)
var/turf/T = get_turf(A)
if(T == loc)
- T = get_step(A, A.dir) //please don't slam into a window like a bird, nar-sie
+ T = get_step(A, A.dir) //please don't slam into a window like a bird, Nar'Sie
forceMove(T)
@@ -185,12 +185,12 @@
/obj/singularity/narsie/proc/acquire(atom/food)
if(food == target)
return
- to_chat(target, "
NAR-SIE HAS LOST INTEREST IN YOU. ")
+ to_chat(target, "
NAR'SIE HAS LOST INTEREST IN YOU. ")
target = food
if(ishuman(target))
- to_chat(target, "
NAR-SIE HUNGERS FOR YOUR SOUL. ")
+ to_chat(target, "
NAR'SIE HUNGERS FOR YOUR SOUL. ")
else
- to_chat(target, "
NAR-SIE HAS CHOSEN YOU TO LEAD HER TO HER NEXT MEAL. ")
+ to_chat(target, "
NAR'SIE HAS CHOSEN YOU TO LEAD HER TO HER NEXT MEAL. ")
//Wizard narsie
/obj/singularity/narsie/wizard
diff --git a/code/modules/power/singularity/particle_accelerator/particle.dm b/code/modules/power/singularity/particle_accelerator/particle.dm
index 2756d879d1..57f4a77fc2 100644
--- a/code/modules/power/singularity/particle_accelerator/particle.dm
+++ b/code/modules/power/singularity/particle_accelerator/particle.dm
@@ -28,7 +28,7 @@
addtimer(CALLBACK(src, .proc/move), 1)
-/obj/effect/accelerated_particle/Collide(atom/A)
+/obj/effect/accelerated_particle/Bump(atom/A)
if(A)
if(isliving(A))
toxmob(A)
diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm
index 25623661d6..bc05e784a4 100644
--- a/code/modules/power/singularity/singularity.dm
+++ b/code/modules/power/singularity/singularity.dm
@@ -81,6 +81,20 @@
/obj/singularity/blob_act(obj/structure/blob/B)
return
+/obj/singularity/attack_tk(mob/user)
+ if(iscarbon(user))
+ var/mob/living/carbon/C = user
+ C.visible_message("
[C]'s head begins to collapse in on itself! ", "
Your head feels like it's collapsing in on itself! This was really not a good idea! ", "
You hear something crack and explode in gore. ")
+ var/turf/T = get_turf(C)
+ for(var/i in 1 to 3)
+ C.apply_damage(30, BRUTE, BODY_ZONE_HEAD)
+ new /obj/effect/gibspawner/generic(T)
+ sleep(1)
+ C.ghostize()
+ var/obj/item/bodypart/head/rip_u = C.get_bodypart(BODY_ZONE_HEAD)
+ rip_u.dismember(BURN) //nice try jedi
+ qdel(rip_u)
+
/obj/singularity/ex_act(severity, target)
switch(severity)
if(1)
@@ -101,12 +115,12 @@
return 0 //Will there be an impact? Who knows. Will we see it? No.
-/obj/singularity/Collide(atom/A)
+/obj/singularity/Bump(atom/A)
consume(A)
return
-/obj/singularity/CollidedWith(atom/movable/AM)
+/obj/singularity/Bumped(atom/movable/AM)
consume(AM)
diff --git a/code/modules/power/smes.dm b/code/modules/power/smes.dm
index 4a8ee61f0f..3637bf38f7 100644
--- a/code/modules/power/smes.dm
+++ b/code/modules/power/smes.dm
@@ -24,14 +24,14 @@
var/capacity = 5e6 // maximum charge
var/charge = 0 // actual charge
- var/input_attempt = 1 // 1 = attempting to charge, 0 = not attempting to charge
- var/inputting = 1 // 1 = actually inputting, 0 = not inputting
+ var/input_attempt = TRUE // TRUE = attempting to charge, FALSE = not attempting to charge
+ var/inputting = TRUE // TRUE = actually inputting, FALSE = not inputting
var/input_level = 50000 // amount of power the SMES attempts to charge by
var/input_level_max = 200000 // cap on input_level
var/input_available = 0 // amount of charge available from input last tick
- var/output_attempt = 1 // 1 = attempting to output, 0 = not attempting to output
- var/outputting = 1 // 1 = actually outputting, 0 = not outputting
+ var/output_attempt = TRUE // TRUE = attempting to output, FALSE = not attempting to output
+ var/outputting = TRUE // TRUE = actually outputting, FALSE = not outputting
var/output_level = 50000 // amount of power the SMES attempts to output
var/output_level_max = 200000 // cap on output_level
var/output_used = 0 // amount of power actually outputted. may be less than output_level if the powernet returns excess power
@@ -53,11 +53,11 @@
terminal = term
break dir_loop
- if(!terminal)
- stat |= BROKEN
- return
- terminal.master = src
- update_icon()
+ if(!terminal)
+ stat |= BROKEN
+ return
+ terminal.master = src
+ update_icon()
/obj/machinery/power/smes/RefreshParts()
var/IO = 0
@@ -141,11 +141,7 @@
//build the terminal and link it to the network
make_terminal(T)
terminal.connect_to_network()
- return
-
- //disassembling the terminal
- if(istype(I, /obj/item/wirecutters) && terminal && panel_open)
- terminal.dismantle(user, I)
+ connect_to_network()
return
//crowbarring it !
@@ -160,6 +156,13 @@
return ..()
+/obj/machinery/power/smes/wirecutter_act(mob/living/user, obj/item/I)
+ //disassembling the terminal
+ if(terminal && panel_open)
+ terminal.dismantle(user, I)
+ return TRUE
+
+
/obj/machinery/power/smes/default_deconstruction_crowbar(obj/item/crowbar/C)
if(istype(C) && terminal)
to_chat(usr, "
You must first remove the power terminal! ")
@@ -243,35 +246,36 @@
charge += load * SMESRATE // increase the charge
- add_load(load) // add the load to the terminal side network
+ terminal.add_load(load) // add the load to the terminal side network
else // if not enough capcity
- inputting = 0 // stop inputting
+ inputting = FALSE // stop inputting
else
if(input_attempt && input_available > 0)
- inputting = 1
+ inputting = TRUE
else
- inputting = 0
+ inputting = FALSE
//outputting
if(output_attempt)
if(outputting)
output_used = min( charge/SMESRATE, output_level) //limit output to that stored
- charge -= output_used*SMESRATE // reduce the storage (may be recovered in /restore() if excessive)
-
- add_avail(output_used) // add output to powernet (smes side)
+ if (add_avail(output_used)) // add output to powernet if it exists (smes side)
+ charge -= output_used*SMESRATE // reduce the storage (may be recovered in /restore() if excessive)
+ else
+ outputting = FALSE
if(output_used < 0.0001) // either from no charge or set to 0
- outputting = 0
+ outputting = FALSE
investigate_log("lost power and turned
off ", INVESTIGATE_SINGULO)
else if(output_attempt && charge > output_level && output_level > 0)
- outputting = 1
+ outputting = TRUE
else
output_used = 0
else
- outputting = 0
+ outputting = FALSE
// only update icon if state changed
if(last_disp != chargedisplay() || last_chrg != inputting || last_onln != outputting)
@@ -309,10 +313,6 @@
return
-/obj/machinery/power/smes/add_load(amount)
- if(terminal && terminal.powernet)
- terminal.powernet.load += amount
-
/obj/machinery/power/smes/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
diff --git a/code/modules/power/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm
index 53140d45e0..ad94d577ea 100644
--- a/code/modules/power/supermatter/supermatter.dm
+++ b/code/modules/power/supermatter/supermatter.dm
@@ -519,9 +519,11 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
/obj/machinery/power/supermatter_crystal/attack_tk(mob/user)
if(iscarbon(user))
var/mob/living/carbon/C = user
- to_chat(C, "
That was a really dumb idea. ")
- var/obj/item/bodypart/head/rip_u = C.get_bodypart(BODY_ZONE_HEAD)
- rip_u.dismember(BURN) //nice try jedi
+ to_chat(C, "
That was a really dense idea. ")
+ C.ghostize()
+ var/obj/item/organ/brain/rip_u = locate(/obj/item/organ/brain) in C.internal_organs
+ rip_u.Remove(C)
+ qdel(rip_u)
/obj/machinery/power/supermatter_crystal/attack_paw(mob/user)
dust_mob(user, cause = "monkey attack")
@@ -593,7 +595,7 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
default_unfasten_wrench(user, tool, time = 20)
return TRUE
-/obj/machinery/power/supermatter_crystal/CollidedWith(atom/movable/AM)
+/obj/machinery/power/supermatter_crystal/Bumped(atom/movable/AM)
if(isliving(AM))
AM.visible_message("
\The [AM] slams into \the [src] inducing a resonance... [AM.p_their()] body starts to glow and catch flame before flashing into ash. ",\
"
You slam into \the [src] as your ears are filled with unearthly ringing. Your last thought is \"Oh, fuck.\" ",\
@@ -615,13 +617,17 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
return
message_admins("[src] has consumed [key_name_admin(user)] [ADMIN_JMP(src)].")
investigate_log("has consumed [key_name(user)].", INVESTIGATE_SUPERMATTER)
- user.dust()
+ user.dust(force = TRUE)
matter_power += 200
else if(istype(AM, /obj/singularity))
return
else if(isobj(AM))
if(!iseffect(AM))
- investigate_log("has consumed [AM].", INVESTIGATE_SUPERMATTER)
+ var/suspicion = ""
+ if(AM.fingerprintslast)
+ suspicion = "last touched by [AM.fingerprintslast]"
+ message_admins("[src] has consumed [AM], [suspicion] [ADMIN_JMP(src)].")
+ investigate_log("has consumed [AM] - [suspicion].", INVESTIGATE_SUPERMATTER)
qdel(AM)
if(!iseffect(AM))
matter_power += 200
diff --git a/code/modules/power/tesla/coil.dm b/code/modules/power/tesla/coil.dm
index 5ee0b91b2b..33ebefbf4c 100644
--- a/code/modules/power/tesla/coil.dm
+++ b/code/modules/power/tesla/coil.dm
@@ -25,7 +25,7 @@
/obj/machinery/power/tesla_coil/Initialize()
. = ..()
- wires = new /datum/wires/tesla_coil(src) //CITADEL EDIT, Kevinz you cheaty fuccboi.
+ wires = new /datum/wires/tesla_coil(src)
linked_techweb = SSresearch.science_tech
/obj/machinery/power/tesla_coil/RefreshParts()
@@ -68,11 +68,6 @@
return ..()
-//ATTACK HAND IGNORING PARENT RETURN VALUE
-/obj/machinery/power/tesla_coil/attack_hand(mob/user)
- if(user.a_intent == INTENT_GRAB && user_buckle_mob(user.pulling, user, check_loc = 0))
- return ..()
-
/obj/machinery/power/tesla_coil/tesla_act(power, tesla_flags, shocked_targets)
if(anchored && !panel_open)
obj_flags |= BEING_SHOCKED
@@ -174,15 +169,9 @@
return ..()
-//ATTACK HAND IGNORING PARENT RETURN VALUE
-/obj/machinery/power/grounding_rod/attack_hand(mob/user)
- if(user.a_intent == INTENT_GRAB && user_buckle_mob(user.pulling, user, check_loc = 0))
- return
- . = ..()
-
/obj/machinery/power/grounding_rod/tesla_act(var/power)
if(anchored && !panel_open)
flick("grounding_rodhit", src)
tesla_buckle_check(power)
else
- ..()
+ ..()
\ No newline at end of file
diff --git a/code/modules/power/tesla/energy_ball.dm b/code/modules/power/tesla/energy_ball.dm
index 470b6e789c..0a54bcab33 100644
--- a/code/modules/power/tesla/energy_ball.dm
+++ b/code/modules/power/tesla/energy_ball.dm
@@ -24,8 +24,8 @@
var/energy_to_lower = -20
/obj/singularity/energy_ball/Initialize(mapload, starting_energy = 50, is_miniball = FALSE)
- . = ..()
miniball = is_miniball
+ . = ..()
if(!is_miniball)
set_light(10, 7, "#EEEEFF")
@@ -123,12 +123,21 @@
EB.orbit(src, orbitsize, pick(FALSE, TRUE), rand(10, 25), pick(3, 4, 5, 6, 36))
-/obj/singularity/energy_ball/Collide(atom/A)
+/obj/singularity/energy_ball/Bump(atom/A)
dust_mobs(A)
-/obj/singularity/energy_ball/CollidedWith(atom/movable/AM)
+/obj/singularity/energy_ball/Bumped(atom/movable/AM)
dust_mobs(AM)
+/obj/singularity/energy_ball/attack_tk(mob/user)
+ if(iscarbon(user))
+ var/mob/living/carbon/C = user
+ to_chat(C, "
That was a shockingly dumb idea. ")
+ var/obj/item/organ/brain/rip_u = locate(/obj/item/organ/brain) in C.internal_organs
+ C.ghostize(0)
+ qdel(rip_u)
+ C.death()
+
/obj/singularity/energy_ball/orbit(obj/singularity/energy_ball/target)
if (istype(target))
target.orbiting_balls += src
@@ -193,7 +202,8 @@
/obj/machinery/gateway,
/obj/structure/lattice,
/obj/structure/grille,
- /obj/machinery/the_singularitygen/tesla))
+ /obj/machinery/the_singularitygen/tesla,
+ /obj/structure/frame/machine))
for(var/A in typecache_filter_multi_list_exclusion(oview(source, zap_range+2), things_to_shock, blacklisted_tesla_types))
if(!(tesla_flags & TESLA_ALLOW_DUPLICATES) && LAZYACCESS(shocked_targets, A))
diff --git a/code/modules/procedural_mapping/mapGenerators/repair.dm b/code/modules/procedural_mapping/mapGenerators/repair.dm
index 4b541e8a79..bc8e9f74f1 100644
--- a/code/modules/procedural_mapping/mapGenerators/repair.dm
+++ b/code/modules/procedural_mapping/mapGenerators/repair.dm
@@ -21,13 +21,13 @@
return
var/datum/mapGenerator/repair/reload_station_map/mother1 = mother
GLOB.reloading_map = TRUE
- var/static/dmm_suite/reloader = new
// This is kind of finicky on multi-Z maps but the reader would need to be
// changed to allow Z cropping and that's a mess
var/z_offset = SSmapping.station_start
var/list/bounds
for (var/path in SSmapping.config.GetFullMapPaths())
- bounds = reloader.load_map(file(path), measureOnly = FALSE, no_changeturf = FALSE,x_offset = 0, y_offset = 0, z_offset = z_offset, cropMap=TRUE, lower_crop_x = mother1.x_low, lower_crop_y = mother1.y_low, upper_crop_x = mother1.x_high, upper_crop_y = mother1.y_high)
+ var/datum/parsed_map/parsed = load_map(file(path), 1, 1, z_offset, measureOnly = FALSE, no_changeturf = FALSE, cropMap=TRUE, x_lower = mother1.x_low, y_lower = mother1.y_low, x_upper = mother1.x_high, y_upper = mother1.y_high)
+ bounds = parsed?.bounds
z_offset += bounds[MAP_MAXZ] - bounds[MAP_MINZ] + 1
var/list/obj/machinery/atmospherics/atmos_machines = list()
@@ -37,7 +37,7 @@
repopulate_sorted_areas()
for(var/L in block(locate(bounds[MAP_MINX], bounds[MAP_MINY], SSmapping.station_start),
- locate(bounds[MAP_MAXX], bounds[MAP_MAXY], z_offset - 1)))
+ locate(bounds[MAP_MAXX], bounds[MAP_MAXY], z_offset - 1)))
set waitfor = FALSE
var/turf/B = L
atoms += B
diff --git a/code/modules/projectiles/ammunition/_ammunition.dm b/code/modules/projectiles/ammunition/_ammunition.dm
index c7d2069fc7..e16b9bb9b5 100644
--- a/code/modules/projectiles/ammunition/_ammunition.dm
+++ b/code/modules/projectiles/ammunition/_ammunition.dm
@@ -21,6 +21,9 @@
var/heavy_metal = TRUE
var/harmful = TRUE //pacifism check for boolet, set to FALSE if bullet is non-lethal
+/obj/item/ammo_casing/spent
+ name = "spent bullet casing"
+ BB = null
/obj/item/ammo_casing/Initialize()
. = ..()
@@ -34,7 +37,7 @@
/obj/item/ammo_casing/update_icon()
..()
icon_state = "[initial(icon_state)][BB ? "-live" : ""]"
- desc = "[initial(desc)][BB ? "" : " This one is spent"]"
+ desc = "[initial(desc)][BB ? "" : " This one is spent."]"
//proc to magically refill a casing with a new projectile
/obj/item/ammo_casing/proc/newshot() //For energy weapons, syringe gun, shotgun shells and wands (!).
diff --git a/code/modules/projectiles/ammunition/ballistic/shotgun.dm b/code/modules/projectiles/ammunition/ballistic/shotgun.dm
index 4c66939e53..39cf924169 100644
--- a/code/modules/projectiles/ammunition/ballistic/shotgun.dm
+++ b/code/modules/projectiles/ammunition/ballistic/shotgun.dm
@@ -92,10 +92,12 @@
variance = 35
/obj/item/ammo_casing/shotgun/laserslug
- name = "laser slug"
- desc = "An advanced shotgun shell that uses a micro laser to replicate the effects of a laser weapon in a ballistic package."
+ name = "scatter laser shell"
+ desc = "An advanced shotgun shell that uses a micro laser to replicate the effects of a scatter laser weapon in a ballistic package."
icon_state = "lshell"
- projectile_type = /obj/item/projectile/beam/laser
+ projectile_type = /obj/item/projectile/beam/weak
+ pellets = 6
+ variance = 35
/obj/item/ammo_casing/shotgun/techshell
name = "unloaded technological shell"
diff --git a/code/modules/projectiles/ammunition/caseless/misc.dm b/code/modules/projectiles/ammunition/caseless/misc.dm
index fcb491f071..24f59ea658 100644
--- a/code/modules/projectiles/ammunition/caseless/misc.dm
+++ b/code/modules/projectiles/ammunition/caseless/misc.dm
@@ -8,15 +8,15 @@
throw_speed = 3
/obj/item/ammo_casing/caseless/laser
- name = "laser casing"
- desc = "You shouldn't be seeing this."
- caliber = "laser"
- icon_state = "s-casing-live"
- projectile_type = /obj/item/projectile/beam
- fire_sound = 'sound/weapons/laser.ogg'
- firing_effect_type = /obj/effect/temp_visual/dir_setting/firing_effect/energy
+ name = "laser casing"
+ desc = "You shouldn't be seeing this."
+ caliber = "laser"
+ icon_state = "s-casing-live"
+ projectile_type = /obj/item/projectile/beam
+ fire_sound = 'sound/weapons/laser.ogg'
+ firing_effect_type = /obj/effect/temp_visual/dir_setting/firing_effect/energy
/obj/item/ammo_casing/caseless/laser/gatling
- projectile_type = /obj/item/projectile/beam/weak
+ projectile_type = /obj/item/projectile/beam/weak/penetrator
variance = 0.8
click_cooldown_override = 1
diff --git a/code/modules/projectiles/ammunition/energy/gravity.dm b/code/modules/projectiles/ammunition/energy/gravity.dm
index be3a4494cd..d8a6a1244d 100644
--- a/code/modules/projectiles/ammunition/energy/gravity.dm
+++ b/code/modules/projectiles/ammunition/energy/gravity.dm
@@ -1,36 +1,29 @@
-/obj/item/ammo_casing/energy/gravityrepulse
+/obj/item/ammo_casing/energy/gravity
+ e_cost = 0
+ fire_sound = 'sound/weapons/wave.ogg'
+ select_name = "gravity"
+ delay = 50
+ var/obj/item/gun/energy/gravity_gun/gun
+
+/obj/item/ammo_casing/energy/gravity/Initialize(mapload)
+ if(istype(loc,/obj/item/gun/energy/gravity_gun))
+ gun = loc
+ . = ..()
+
+/obj/item/ammo_casing/energy/gravity/Destroy()
+ gun = null
+ . = ..()
+
+/obj/item/ammo_casing/energy/gravity/repulse
projectile_type = /obj/item/projectile/gravityrepulse
- e_cost = 0
- fire_sound = 'sound/weapons/wave.ogg'
select_name = "repulse"
- delay = 50
- var/obj/item/gun/energy/gravity_gun/gun
-/obj/item/ammo_casing/energy/gravityrepulse/Initialize(mapload, obj/item/gun/energy/gravity_gun/G)
- gun = G
- . = ..()
-
-/obj/item/ammo_casing/energy/gravityattract
+/obj/item/ammo_casing/energy/gravity/attract
projectile_type = /obj/item/projectile/gravityattract
- e_cost = 0
- fire_sound = 'sound/weapons/wave.ogg'
select_name = "attract"
- delay = 50
- var/obj/item/gun/energy/gravity_gun/gun
-
-/obj/item/ammo_casing/energy/gravityattract/Initialize(mapload, obj/item/gun/energy/gravity_gun/G)
- gun = G
- . = ..()
-
-/obj/item/ammo_casing/energy/gravitychaos
+/obj/item/ammo_casing/energy/gravity/chaos
projectile_type = /obj/item/projectile/gravitychaos
- e_cost = 0
- fire_sound = 'sound/weapons/wave.ogg'
select_name = "chaos"
- delay = 50
- var/obj/item/gun/energy/gravity_gun/gun
-/obj/item/ammo_casing/energy/gravitychaos/Initialize(mapload, obj/item/gun/energy/gravity_gun/G)
- gun = G
- . = ..()
+
diff --git a/code/modules/projectiles/ammunition/special/magic.dm b/code/modules/projectiles/ammunition/special/magic.dm
index 6e1d60c11f..51b39e0410 100644
--- a/code/modules/projectiles/ammunition/special/magic.dm
+++ b/code/modules/projectiles/ammunition/special/magic.dm
@@ -38,8 +38,8 @@
/obj/item/ammo_casing/magic/arcane_barrage
projectile_type = /obj/item/projectile/magic/arcane_barrage
-/obj/item/ammo_casing/magic/chaos/newshot()
- ..()
-
/obj/item/ammo_casing/magic/honk
projectile_type = /obj/item/projectile/bullet/honker
+
+/obj/item/ammo_casing/magic/locker
+ projectile_type = /obj/item/projectile/magic/locker
diff --git a/code/modules/projectiles/boxes_magazines/external/rechargable.dm b/code/modules/projectiles/boxes_magazines/external/rechargable.dm
index c4fb00aa22..fada5c8659 100644
--- a/code/modules/projectiles/boxes_magazines/external/rechargable.dm
+++ b/code/modules/projectiles/boxes_magazines/external/rechargable.dm
@@ -1,14 +1,14 @@
/obj/item/ammo_box/magazine/recharge
- name = "power pack"
- desc = "A rechargeable, detachable battery that serves as a magazine for laser rifles."
- icon_state = "oldrifle-20"
- ammo_type = /obj/item/ammo_casing/caseless/laser
- caliber = "laser"
- max_ammo = 20
+ name = "power pack"
+ desc = "A rechargeable, detachable battery that serves as a magazine for laser rifles."
+ icon_state = "oldrifle-20"
+ ammo_type = /obj/item/ammo_casing/caseless/laser
+ caliber = "laser"
+ max_ammo = 20
/obj/item/ammo_box/magazine/recharge/update_icon()
desc = "[initial(desc)] It has [stored_ammo.len] shot\s left."
icon_state = "oldrifle-[round(ammo_count(),4)]"
/obj/item/ammo_box/magazine/recharge/attack_self() //No popping out the "bullets"
- return
+ return
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index 569f886ec2..6a6921e949 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -49,8 +49,6 @@
var/datum/action/item_action/toggle_gunlight/alight
var/mutable_appearance/flashlight_overlay
- var/list/upgrades = list()
-
var/ammo_x_offset = 0 //used for positioning ammo count overlay on sprite
var/ammo_y_offset = 0
var/flight_x_offset = 0
@@ -133,6 +131,9 @@
O.emp_act(severity)
/obj/item/gun/afterattack(atom/target, mob/living/user, flag, params)
+ . = ..()
+ if(!target)
+ return
if(firing_burst)
return
if(flag) //It's adjacent, is the user, or is on the user's person
@@ -177,7 +178,6 @@
var/loop_counter = 0
bonus_spread += getinaccuracy(user) //CIT CHANGE - adds bonus spread while not aiming
-
if(ishuman(user) && user.a_intent == INTENT_HARM)
var/mob/living/carbon/human/H = user
for(var/obj/item/gun/G in H.held_items)
@@ -337,22 +337,21 @@
if(loc == user)
alight.Grant(user)
else if(istype(I, /obj/item/kitchen/knife))
- if(!can_bayonet)
- return ..()
var/obj/item/kitchen/knife/K = I
- if(!bayonet)
- if(!user.transferItemToLoc(I, src))
- return
- to_chat(user, "
You attach \the [K] to the front of \the [src]. ")
- bayonet = K
- var/state = "bayonet" //Generic state.
- if(bayonet.icon_state in icon_states('icons/obj/guns/bayonets.dmi')) //Snowflake state?
- state = bayonet.icon_state
- var/icon/bayonet_icons = 'icons/obj/guns/bayonets.dmi'
- knife_overlay = mutable_appearance(bayonet_icons, state)
- knife_overlay.pixel_x = knife_x_offset
- knife_overlay.pixel_y = knife_y_offset
- add_overlay(knife_overlay, TRUE)
+ if(!can_bayonet || !K.bayonet || bayonet) //ensure the gun has an attachment point available, and that the knife is compatible with it.
+ return ..()
+ if(!user.transferItemToLoc(I, src))
+ return
+ to_chat(user, "
You attach \the [K] to the front of \the [src]. ")
+ bayonet = K
+ var/state = "bayonet" //Generic state.
+ if(bayonet.icon_state in icon_states('icons/obj/guns/bayonets.dmi')) //Snowflake state?
+ state = bayonet.icon_state
+ var/icon/bayonet_icons = 'icons/obj/guns/bayonets.dmi'
+ knife_overlay = mutable_appearance(bayonet_icons, state)
+ knife_overlay.pixel_x = knife_x_offset
+ knife_overlay.pixel_y = knife_y_offset
+ add_overlay(knife_overlay, TRUE)
else if(istype(I, /obj/item/screwdriver))
if(gun_light)
var/obj/item/flashlight/seclite/S = gun_light
diff --git a/code/modules/projectiles/guns/ballistic.dm b/code/modules/projectiles/guns/ballistic.dm
index 61d2c81ea7..b9a509f33f 100644
--- a/code/modules/projectiles/guns/ballistic.dm
+++ b/code/modules/projectiles/guns/ballistic.dm
@@ -121,7 +121,7 @@
user.put_in_hands(magazine)
magazine.update_icon()
if(magazine.ammo_count())
- playsound(src, "sound/weapons/gun_magazine_remove_full.ogg", 70, 1)
+ playsound(src, 'sound/weapons/gun_magazine_remove_full.ogg', 70, 1)
else
playsound(src, "gun_remove_empty_magazine", 70, 1)
magazine = null
diff --git a/code/modules/projectiles/guns/ballistic/automatic.dm b/code/modules/projectiles/guns/ballistic/automatic.dm
index 420e6c78ce..d1dcdf22d8 100644
--- a/code/modules/projectiles/guns/ballistic/automatic.dm
+++ b/code/modules/projectiles/guns/ballistic/automatic.dm
@@ -104,7 +104,7 @@
update_icon()
/obj/item/gun/ballistic/automatic/c20r/afterattack()
- ..()
+ . = ..()
empty_alarm()
return
@@ -167,7 +167,7 @@
if(select == 2)
underbarrel.afterattack(target, user, flag, params)
else
- ..()
+ . = ..()
return
/obj/item/gun/ballistic/automatic/m90/attackby(obj/item/A, mob/user, params)
if(istype(A, /obj/item/ammo_casing))
@@ -264,7 +264,7 @@
icon_state = "bulldog[chambered ? "" : "-e"]"
/obj/item/gun/ballistic/automatic/shotgun/bulldog/afterattack()
- ..()
+ . = ..()
empty_alarm()
return
@@ -286,6 +286,7 @@
can_suppress = FALSE
burst_size = 3
fire_delay = 1
+ spread = 7
pin = /obj/item/firing_pin/implant/pindicate
/obj/item/gun/ballistic/automatic/l6_saw/unrestricted
@@ -317,7 +318,7 @@
if(cover_open)
to_chat(user, "
[src]'s cover is open! Close it before firing! ")
else
- ..()
+ . = ..()
update_icon()
//ATTACK HAND IGNORING PARENT RETURN VALUE
diff --git a/code/modules/projectiles/guns/ballistic/laser_gatling.dm b/code/modules/projectiles/guns/ballistic/laser_gatling.dm
index db5beb21ed..49ced8ff39 100644
--- a/code/modules/projectiles/guns/ballistic/laser_gatling.dm
+++ b/code/modules/projectiles/guns/ballistic/laser_gatling.dm
@@ -141,7 +141,7 @@
/obj/item/gun/ballistic/minigun/afterattack(atom/target, mob/living/user, flag, params)
if(!ammo_pack || ammo_pack.loc != user)
to_chat(user, "You need the backpack power source to fire the gun!")
- ..()
+ . = ..()
/obj/item/gun/ballistic/minigun/dropped(mob/living/user)
ammo_pack.attach_gun(user)
diff --git a/code/modules/projectiles/guns/ballistic/pistol.dm b/code/modules/projectiles/guns/ballistic/pistol.dm
index cf6a856030..5f67194b29 100644
--- a/code/modules/projectiles/guns/ballistic/pistol.dm
+++ b/code/modules/projectiles/guns/ballistic/pistol.dm
@@ -9,6 +9,9 @@
fire_delay = 0
actions_types = list()
+/obj/item/gun/ballistic/automatic/pistol/no_mag
+ spawnwithmagazine = FALSE
+
/obj/item/gun/ballistic/automatic/pistol/update_icon()
..()
icon_state = "[initial(icon_state)][chambered ? "" : "-e"][suppressed ? "-suppressed" : ""]"
@@ -26,6 +29,9 @@
mag_type = /obj/item/ammo_box/magazine/m45
can_suppress = FALSE
+/obj/item/gun/ballistic/automatic/pistol/m1911/no_mag
+ spawnwithmagazine = FALSE
+
/obj/item/gun/ballistic/automatic/pistol/m1911/kitchengun
name = "\improper Kitchen Gun (TM)"
desc = "Say goodbye to dirt with Kitchen Gun (TM)! Laser sight and night vision accessories sold separately."
@@ -84,3 +90,4 @@
else
to_chat(user, "
..and falls into view. Whew, that was a close one. ")
user.dropItemToGround(src)
+
diff --git a/code/modules/projectiles/guns/ballistic/revolver.dm b/code/modules/projectiles/guns/ballistic/revolver.dm
index cd11522342..2116f037a3 100644
--- a/code/modules/projectiles/guns/ballistic/revolver.dm
+++ b/code/modules/projectiles/guns/ballistic/revolver.dm
@@ -108,6 +108,8 @@
..()
/obj/item/gun/ballistic/revolver/detective/screwdriver_act(mob/living/user, obj/item/I)
+ if(..())
+ return TRUE
if(magazine.caliber == "38")
to_chat(user, "
You begin to reinforce the barrel of [src]... ")
if(magazine.ammo_count())
@@ -191,6 +193,8 @@
..()
/obj/item/gun/ballistic/revolver/russian/afterattack(atom/target, mob/living/user, flag, params)
+ . = ..(null, user, flag, params)
+
if(flag)
if(!(target in user.contents) && ismob(target))
if(user.a_intent == INTENT_HARM) // Flogging action
diff --git a/code/modules/projectiles/guns/energy/laser.dm b/code/modules/projectiles/guns/energy/laser.dm
index c766d1c94e..6e8174b356 100644
--- a/code/modules/projectiles/guns/energy/laser.dm
+++ b/code/modules/projectiles/guns/energy/laser.dm
@@ -25,7 +25,7 @@
/obj/item/gun/energy/laser/retro/old
name ="laser gun"
icon_state = "retro"
- desc = "First generation lasergun, developed by Nanotrasen. Suffers from ammo issues but its unique ability to recharge its ammo without the need of a magazine helps compensate. You really hope someone has developed a better lasergun while you were in cyro."
+ desc = "First generation lasergun, developed by Nanotrasen. Suffers from ammo issues but its unique ability to recharge its ammo without the need of a magazine helps compensate. You really hope someone has developed a better lasergun while you were in cryo."
ammo_type = list(/obj/item/ammo_casing/energy/lasergun/old)
ammo_x_offset = 3
diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm
index 0e5ae56c81..4d18fed803 100644
--- a/code/modules/projectiles/guns/energy/special.dm
+++ b/code/modules/projectiles/guns/energy/special.dm
@@ -295,7 +295,7 @@
/obj/item/gun/energy/gravity_gun
name = "one-point bluespace-gravitational manipulator"
desc = "An experimental, multi-mode device that fires bolts of Zero-Point Energy, causing local distortions in gravity."
- ammo_type = list(/obj/item/ammo_casing/energy/gravityrepulse, /obj/item/ammo_casing/energy/gravityattract, /obj/item/ammo_casing/energy/gravitychaos)
+ ammo_type = list(/obj/item/ammo_casing/energy/gravity/repulse, /obj/item/ammo_casing/energy/gravity/attract, /obj/item/ammo_casing/energy/gravity/chaos)
item_state = "gravity_gun"
icon_state = "gravity_gun"
var/power = 4
diff --git a/code/modules/projectiles/guns/magic.dm b/code/modules/projectiles/guns/magic.dm
index ef705263c2..9dd25bf3e3 100644
--- a/code/modules/projectiles/guns/magic.dm
+++ b/code/modules/projectiles/guns/magic.dm
@@ -31,7 +31,7 @@
return
else
no_den_usage = 0
- ..()
+ . = ..()
/obj/item/gun/magic/can_shoot()
return charges
diff --git a/code/modules/projectiles/guns/magic/staff.dm b/code/modules/projectiles/guns/magic/staff.dm
index 4b3905721f..14b2ff0c1a 100644
--- a/code/modules/projectiles/guns/magic/staff.dm
+++ b/code/modules/projectiles/guns/magic/staff.dm
@@ -43,7 +43,7 @@
no_den_usage = 1
var/allowed_projectile_types = list(/obj/item/projectile/magic/change, /obj/item/projectile/magic/animate, /obj/item/projectile/magic/resurrection,
/obj/item/projectile/magic/death, /obj/item/projectile/magic/teleport, /obj/item/projectile/magic/door, /obj/item/projectile/magic/aoe/fireball,
- /obj/item/projectile/magic/spellblade, /obj/item/projectile/magic/arcane_barrage)
+ /obj/item/projectile/magic/spellblade, /obj/item/projectile/magic/arcane_barrage, /obj/item/projectile/magic/locker)
/obj/item/gun/magic/staff/chaos/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0)
chambered.projectile_type = pick(allowed_projectile_types)
@@ -94,3 +94,15 @@
if(attack_type == PROJECTILE_ATTACK)
final_block_chance = 0
return ..()
+
+/obj/item/gun/magic/staff/locker
+ name = "staff of the locker"
+ desc = "An artefact that expells encapsulating bolts, for incapacitating thy enemy."
+ fire_sound = 'sound/magic/staff_change.ogg'
+ ammo_type = /obj/item/ammo_casing/magic/locker
+ icon_state = "locker"
+ item_state = "locker"
+ max_charges = 6
+ recharge_rate = 4
+
+
diff --git a/code/modules/projectiles/guns/magic/wand.dm b/code/modules/projectiles/guns/magic/wand.dm
index 9c9d253439..e3724fdf31 100644
--- a/code/modules/projectiles/guns/magic/wand.dm
+++ b/code/modules/projectiles/guns/magic/wand.dm
@@ -43,14 +43,14 @@
no_den_usage = 0
zap_self(user)
else
- ..()
+ . = ..()
update_icon()
/obj/item/gun/magic/wand/proc/zap_self(mob/living/user)
user.visible_message("
[user] zaps [user.p_them()]self with [src]. ")
playsound(user, fire_sound, 50, 1)
- user.log_message("zapped [user.p_them()]self with a
[src] ", INDIVIDUAL_ATTACK_LOG)
+ user.log_message("zapped [user.p_them()]self with a
[src] ", LOG_ATTACK)
/////////////////////////////////////
diff --git a/code/modules/projectiles/guns/misc/beam_rifle.dm b/code/modules/projectiles/guns/misc/beam_rifle.dm
index 98469ebc31..0be09f94be 100644
--- a/code/modules/projectiles/guns/misc/beam_rifle.dm
+++ b/code/modules/projectiles/guns/misc/beam_rifle.dm
@@ -184,7 +184,7 @@
. = ..()
fire_delay = delay
current_tracers = list()
- START_PROCESSING(SSprojectiles, src)
+ START_PROCESSING(SSfastprocess, src)
zoom_lock_action = new(src)
/obj/item/gun/energy/beam_rifle/Destroy()
@@ -247,17 +247,7 @@
/obj/item/gun/energy/beam_rifle/proc/process_aim()
if(istype(current_user) && current_user.client && current_user.client.mouseParams)
var/angle = mouse_angle_from_client(current_user.client)
- switch(angle)
- if(316 to 360)
- current_user.setDir(NORTH)
- if(0 to 45)
- current_user.setDir(NORTH)
- if(46 to 135)
- current_user.setDir(EAST)
- if(136 to 225)
- current_user.setDir(SOUTH)
- if(226 to 315)
- current_user.setDir(WEST)
+ current_user.setDir(angle2dir_cardinal(angle))
var/difference = abs(closer_angle_difference(lastangle, angle))
delay_penalty(difference * aiming_time_increase_angle_multiplier)
lastangle = angle
@@ -295,7 +285,7 @@
if(istype(user))
current_user = user
LAZYOR(current_user.mousemove_intercept_objects, src)
- mobhook = user.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED), CALLBACK(src, .proc/on_mob_move))
+ mobhook = user.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src, .proc/on_mob_move)))
/obj/item/gun/energy/beam_rifle/onMouseDrag(src_object, over_object, src_location, over_location, params, mob)
if(aiming)
@@ -535,7 +525,7 @@
if(!QDELETED(target))
handle_impact(target)
-/obj/item/projectile/beam/beam_rifle/Collide(atom/target)
+/obj/item/projectile/beam/beam_rifle/Bump(atom/target)
if(check_pierce(target))
permutated += target
trajectory_ignore_forcemove = TRUE
diff --git a/code/modules/projectiles/guns/misc/grenade_launcher.dm b/code/modules/projectiles/guns/misc/grenade_launcher.dm
index e51c3573e3..eb36438a6c 100644
--- a/code/modules/projectiles/guns/misc/grenade_launcher.dm
+++ b/code/modules/projectiles/guns/misc/grenade_launcher.dm
@@ -28,16 +28,10 @@
else
to_chat(usr, "
The grenade launcher cannot hold more grenades. ")
-/obj/item/gun/grenadelauncher/afterattack(obj/target, mob/user , flag)
- if(target == user)
- return
+/obj/item/gun/grenadelauncher/can_shoot()
+ return grenades.len
- if(grenades.len)
- fire_grenade(target,user)
- else
- to_chat(user, "
The grenade launcher is empty. ")
-
-/obj/item/gun/grenadelauncher/proc/fire_grenade(atom/target, mob/user)
+/obj/item/gun/grenadelauncher/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0)
user.visible_message("
[user] fired a grenade! ", \
"
You fire the grenade launcher! ")
var/obj/item/grenade/F = grenades[1] //Now with less copypasta!
diff --git a/code/modules/projectiles/pins.dm b/code/modules/projectiles/pins.dm
index f132d62b27..bd42f30626 100644
--- a/code/modules/projectiles/pins.dm
+++ b/code/modules/projectiles/pins.dm
@@ -19,6 +19,7 @@
gun = newloc
/obj/item/firing_pin/afterattack(atom/target, mob/user, proximity_flag)
+ . = ..()
if(proximity_flag)
if(istype(target, /obj/item/gun))
var/obj/item/gun/G = target
@@ -56,7 +57,8 @@
return TRUE
/obj/item/firing_pin/proc/auth_fail(mob/living/user)
- user.show_message(fail_message, 1)
+ if(user)
+ user.show_message(fail_message, 1)
if(selfdestruct)
user.show_message("
SELF-DESTRUCTING... ", 1)
to_chat(user, "
[gun] explodes! ")
@@ -79,6 +81,8 @@
pin_removeable = TRUE
/obj/item/firing_pin/test_range/pin_auth(mob/living/user)
+ if(!istype(user))
+ return FALSE
for(var/obj/machinery/magnetic_controller/M in range(user, 3))
return TRUE
return FALSE
@@ -156,7 +160,7 @@
var/unique_enzymes = null
/obj/item/firing_pin/dna/afterattack(atom/target, mob/user, proximity_flag)
- ..()
+ . = ..()
if(proximity_flag && iscarbon(target))
var/mob/living/carbon/M = target
if(M.dna && M.dna.unique_enzymes)
diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm
index 027849c21d..a23bf1f4ed 100644
--- a/code/modules/projectiles/projectile.dm
+++ b/code/modules/projectiles/projectile.dm
@@ -16,7 +16,7 @@
resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
var/def_zone = "" //Aiming at
- var/mob/firer = null//Who shot it
+ var/atom/movable/firer = null//Who shot it
var/suppressed = FALSE //Attack message
var/yo = null
var/xo = null
@@ -191,7 +191,11 @@
reagent_note += R.id + " ("
reagent_note += num2text(R.volume) + ") "
- add_logs(firer, L, "shot", src, reagent_note)
+ if(ismob(firer))
+ log_combat(firer, L, "shot", src, reagent_note)
+ else
+ L.log_message("has been shot by [firer] with [src]", LOG_ATTACK, color="orange")
+
return L.apply_effects(stun, knockdown, unconscious, irradiate, slur, stutter, eyeblur, drowsy, blocked, stamina, jitter)
/obj/item/projectile/proc/vol_by_damage()
@@ -208,7 +212,7 @@
beam_index = pcache
beam_segments[beam_index] = null
-/obj/item/projectile/Collide(atom/A)
+/obj/item/projectile/Bump(atom/A)
var/datum/point/pcache = trajectory.copy_to()
if(check_ricochet(A) && check_ricochet_flag(A) && ricochets < ricochets_max)
ricochets++
@@ -335,7 +339,7 @@
/obj/item/projectile/proc/fire(angle, atom/direct_target)
//If no angle needs to resolve it from xo/yo!
if(!log_override && firer && original)
- add_logs(firer, original, "fired at", src, "from [get_area_name(src, TRUE)]")
+ log_combat(firer, original, "fired at", src, "from [get_area_name(src, TRUE)]")
if(direct_target)
if(prehit(direct_target))
direct_target.bullet_act(src, def_zone)
@@ -430,7 +434,7 @@
continue
if(safety-- <= 0)
if(loc)
- Collide(loc)
+ Bump(loc)
if(!QDELETED(src))
qdel(src)
return //Kill!
@@ -471,7 +475,7 @@
step_towards(src, T)
hitscan_last = loc
if(can_hit_target(original, permutated))
- Collide(original)
+ Bump(original)
if(!hitscanning && !forcemoved)
pixel_x = trajectory.return_px() - trajectory.mpx * trajectory_multiplier * SSprojectiles.global_iterations_per_move
pixel_y = trajectory.return_py() - trajectory.mpy * trajectory_multiplier * SSprojectiles.global_iterations_per_move
@@ -517,13 +521,6 @@
xo = targloc.x - curloc.x
setAngle(Get_Angle(src, targloc) + spread)
- //CIT CHANGES START HERE - makes it so laying down makes you unable to shoot through most objects
- if(iscarbon(source))
- var/mob/living/carbon/checklad = source
- if(istype(checklad) && checklad.resting)
- pass_flags = 0
- //END OF CIT CHANGES
-
if(isliving(source) && params)
var/list/calculated = calculate_projectile_angle_and_pixel_offsets(source, params)
p_x = calculated[2]
@@ -572,7 +569,7 @@
/obj/item/projectile/Crossed(atom/movable/AM) //A mob moving on a tile with a projectile is hit by it.
..()
if(isliving(AM) && (AM.density || AM == original) && !(src.pass_flags & PASSMOB))
- Collide(AM)
+ Bump(AM)
/obj/item/projectile/Destroy()
if(hitscan)
diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm
index aaa875b0f2..509d33faa0 100644
--- a/code/modules/projectiles/projectile/beams.dm
+++ b/code/modules/projectiles/projectile/beams.dm
@@ -38,6 +38,8 @@
/obj/item/projectile/beam/weak
damage = 15
+
+/obj/item/projectile/beam/weak/penetrator
armour_penetration = 50
/obj/item/projectile/beam/practice
@@ -54,7 +56,7 @@
name = "\improper X-ray beam"
icon_state = "xray"
damage = 15
- irradiate = 30
+ irradiate = 300
range = 15
pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE | PASSCLOSEDTURF
@@ -67,7 +69,7 @@
/obj/item/projectile/beam/disabler
name = "disabler beam"
icon_state = "omnilaser"
- damage = 24
+ damage = 36
damage_type = STAMINA
flag = "energy"
hitsound = 'sound/weapons/tap.ogg'
diff --git a/code/modules/projectiles/projectile/bullets/lmg.dm b/code/modules/projectiles/projectile/bullets/lmg.dm
index 03e64976d9..cf0e565cfc 100644
--- a/code/modules/projectiles/projectile/bullets/lmg.dm
+++ b/code/modules/projectiles/projectile/bullets/lmg.dm
@@ -40,5 +40,5 @@
/obj/item/projectile/bullet/incendiary/mm195x129
name = "1.95x129mm incendiary bullet"
- damage = 15
+ damage = 20
fire_stacks = 3
diff --git a/code/modules/projectiles/projectile/bullets/revolver.dm b/code/modules/projectiles/projectile/bullets/revolver.dm
index fc4ed0fa50..81cb34a0f7 100644
--- a/code/modules/projectiles/projectile/bullets/revolver.dm
+++ b/code/modules/projectiles/projectile/bullets/revolver.dm
@@ -14,9 +14,7 @@
/obj/item/projectile/bullet/c38
name = ".38 bullet"
- damage = 15
- knockdown = 60
- stamina = 50
+ damage = 25
// .357 (Syndie Revolver)
diff --git a/code/modules/projectiles/projectile/energy/misc.dm b/code/modules/projectiles/projectile/energy/misc.dm
index 21c3138add..86f4cc060c 100644
--- a/code/modules/projectiles/projectile/energy/misc.dm
+++ b/code/modules/projectiles/projectile/energy/misc.dm
@@ -3,7 +3,7 @@
icon_state = "declone"
damage = 20
damage_type = CLONE
- irradiate = 10
+ irradiate = 100
impact_effect_type = /obj/effect/temp_visual/impact_effect/green_laser
/obj/item/projectile/energy/dart //ninja throwing dart
diff --git a/code/modules/projectiles/projectile/energy/stun.dm b/code/modules/projectiles/projectile/energy/stun.dm
index b5b45a84ec..db1ad403a7 100644
--- a/code/modules/projectiles/projectile/energy/stun.dm
+++ b/code/modules/projectiles/projectile/energy/stun.dm
@@ -21,7 +21,7 @@
SEND_SIGNAL(C, COMSIG_ADD_MOOD_EVENT, "tased", /datum/mood_event/tased)
SEND_SIGNAL(C, COMSIG_LIVING_MINOR_SHOCK)
if(C.dna && C.dna.check_mutation(HULK))
- C.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" ))
+ C.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" ), forced = "hulk")
else if((C.status_flags & CANKNOCKDOWN) && !C.has_trait(TRAIT_STUNIMMUNE))
addtimer(CALLBACK(C, /mob/living/carbon.proc/do_jitter_animation, jitter), 5)
diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm
index 94d4d49ded..8c9769f278 100644
--- a/code/modules/projectiles/projectile/magic.dm
+++ b/code/modules/projectiles/projectile/magic.dm
@@ -34,6 +34,7 @@
return
if(target.anti_magic_check())
target.visible_message("
[src] vanishes on contact with [target]! ")
+ return
if(iscarbon(target))
var/mob/living/carbon/C = target
C.regenerate_limbs()
@@ -236,7 +237,7 @@
for(var/obj/item/W in contents)
new_mob.equip_to_appropriate_slot(W)
- M.log_message("
became [new_mob.real_name]. ", INDIVIDUAL_ATTACK_LOG)
+ M.log_message("became [new_mob.real_name]", LOG_ATTACK, color="orange")
new_mob.a_intent = INTENT_HARM
@@ -244,7 +245,7 @@
to_chat(new_mob, "
Your form morphs into that of a [randomize]. ")
- var/poly_msg = CONFIG_GET(keyed_string_list/policy)["polymorph"]
+ var/poly_msg = CONFIG_GET(keyed_list/policy)["polymorph"]
if(poly_msg)
to_chat(new_mob, poly_msg)
@@ -335,6 +336,91 @@
return
. = ..()
+
+/obj/item/projectile/magic/locker
+ name = "locker bolt"
+ icon_state = "locker"
+ nodamage = TRUE
+ flag = "magic"
+ var/weld = TRUE
+ var/created = FALSE //prevents creation of more then one locker if it has multiple hits
+
+/obj/item/projectile/magic/locker/prehit(atom/A)
+ if(ismob(A))
+ var/mob/M = A
+ if(M.anti_magic_check())
+ M.visible_message("
[src] vanishes on contact with [A]! ")
+ qdel(src)
+ return
+ if(M.anchored)
+ return ..()
+ M.forceMove(src)
+ return FALSE
+ return ..()
+
+/obj/item/projectile/magic/locker/on_hit(target)
+ if(created)
+ return ..()
+ var/obj/structure/closet/decay/C = new(get_turf(src))
+ if(LAZYLEN(contents))
+ for(var/atom/movable/AM in contents)
+ C.insert(AM)
+ C.welded = weld
+ C.update_icon()
+ created = TRUE
+ return ..()
+
+/obj/item/projectile/magic/locker/Destroy()
+ for(var/atom/movable/AM in contents)
+ AM.forceMove(get_turf(src))
+ . = ..()
+
+/obj/structure/closet/decay
+ breakout_time = 600
+ icon_welded = null
+ var/magic_icon = "cursed"
+ var/weakened_icon = "decursed"
+ var/auto_destroy = TRUE
+
+/obj/structure/closet/decay/Initialize()
+ . = ..()
+ if(auto_destroy)
+ addtimer(CALLBACK(src, .proc/bust_open), 5 MINUTES)
+ addtimer(CALLBACK(src, .proc/magicly_lock), 5)
+
+/obj/structure/closet/decay/proc/magicly_lock()
+ if(!welded)
+ return
+ icon_state = magic_icon
+ update_icon()
+
+/obj/structure/closet/decay/after_weld(weld_state)
+ if(weld_state)
+ unmagify()
+
+/obj/structure/closet/decay/proc/decay()
+ animate(src, alpha = 0, time = 30)
+ addtimer(CALLBACK(GLOBAL_PROC, .proc/qdel, src), 30)
+
+/obj/structure/closet/decay/open(mob/living/user)
+ . = ..()
+ if(.)
+ if(icon_state == magic_icon) //check if we used the magic icon at all before giving it the lesser magic icon
+ unmagify()
+ else
+ addtimer(CALLBACK(src, .proc/decay), 15 SECONDS)
+
+/obj/structure/closet/decay/contents_explosion(severity, target)
+ for(var/atom/A in contents)
+ A.ex_act(severity/2, target) //Difference is it does half the damage to contents from explosion, to make fireball not completely instakill
+ CHECK_TICK
+
+/obj/structure/closet/decay/proc/unmagify()
+ icon_state = weakened_icon
+ update_icon()
+ addtimer(CALLBACK(src, .proc/decay), 15 SECONDS)
+ icon_welded = "welded"
+
/obj/item/projectile/magic/aoe
name = "Area Bolt"
desc = "What the fuck does this do?!"
@@ -345,9 +431,10 @@
if(proxdet)
for(var/mob/living/L in range(1, get_turf(src)))
if(L.stat != DEAD && L != firer && !L.anti_magic_check())
- return Collide(L)
+ return Bump(L)
..()
+
/obj/item/projectile/magic/aoe/lightning
name = "lightning bolt"
icon_state = "tesla_projectile" //Better sprites are REALLY needed and appreciated!~
@@ -423,3 +510,5 @@
var/turf/T = get_turf(target)
for(var/i=0, i<50, i+=10)
addtimer(CALLBACK(GLOBAL_PROC, .proc/explosion, T, -1, exp_heavy, exp_light, exp_flash, FALSE, FALSE, exp_fire), i)
+
+
diff --git a/code/modules/projectiles/projectile/special/gravity.dm b/code/modules/projectiles/projectile/special/gravity.dm
index 89f753d36d..fdc6b92ec9 100644
--- a/code/modules/projectiles/projectile/special/gravity.dm
+++ b/code/modules/projectiles/projectile/special/gravity.dm
@@ -13,7 +13,7 @@
/obj/item/projectile/gravityrepulse/Initialize()
. = ..()
- var/obj/item/ammo_casing/energy/gravityrepulse/C = loc
+ var/obj/item/ammo_casing/energy/gravity/repulse/C = loc
if(istype(C)) //Hard-coded maximum power so servers can't be crashed by trying to throw the entire Z level's items
power = min(C.gun.power, 15)
@@ -44,7 +44,7 @@
/obj/item/projectile/gravityattract/Initialize()
. = ..()
- var/obj/item/ammo_casing/energy/gravityattract/C = loc
+ var/obj/item/ammo_casing/energy/gravity/attract/C = loc
if(istype(C)) //Hard-coded maximum power so servers can't be crashed by trying to throw the entire Z level's items
power = min(C.gun.power, 15)
@@ -74,7 +74,7 @@
/obj/item/projectile/gravitychaos/Initialize()
. = ..()
- var/obj/item/ammo_casing/energy/gravitychaos/C = loc
+ var/obj/item/ammo_casing/energy/gravity/chaos/C = loc
if(istype(C)) //Hard-coded maximum power so servers can't be crashed by trying to throw the entire Z level's items
power = min(C.gun.power, 15)
diff --git a/code/modules/projectiles/projectile/special/hallucination.dm b/code/modules/projectiles/projectile/special/hallucination.dm
index e158ed89f0..f65ebce51f 100644
--- a/code/modules/projectiles/projectile/special/hallucination.dm
+++ b/code/modules/projectiles/projectile/special/hallucination.dm
@@ -33,7 +33,7 @@
QDEL_NULL(fake_icon)
return ..()
-/obj/item/projectile/hallucination/Collide(atom/A)
+/obj/item/projectile/hallucination/Bump(atom/A)
if(!ismob(A))
if(hal_hitsound_wall)
hal_target.playsound_local(loc, hal_hitsound_wall, 40, 1)
@@ -169,7 +169,7 @@
hal_target.Knockdown(100)
hal_target.stuttering += 20
if(hal_target.dna && hal_target.dna.check_mutation(HULK))
- hal_target.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" ))
+ hal_target.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" ), forced = "hulk")
else if((hal_target.status_flags & CANKNOCKDOWN) && !hal_target.has_trait(TRAIT_STUNIMMUNE))
addtimer(CALLBACK(hal_target, /mob/living/carbon.proc/do_jitter_animation, 20), 5)
diff --git a/code/modules/projectiles/projectile/special/meteor.dm b/code/modules/projectiles/projectile/special/meteor.dm
index f4e60998e1..a92f141399 100644
--- a/code/modules/projectiles/projectile/special/meteor.dm
+++ b/code/modules/projectiles/projectile/special/meteor.dm
@@ -7,7 +7,7 @@
nodamage = 1
flag = "bullet"
-/obj/item/projectile/meteor/Collide(atom/A)
+/obj/item/projectile/meteor/Bump(atom/A)
if(A == firer)
forceMove(A.loc)
return
diff --git a/code/modules/projectiles/projectile/special/wormhole.dm b/code/modules/projectiles/projectile/special/wormhole.dm
index 53f766a7d5..07b56a133f 100644
--- a/code/modules/projectiles/projectile/special/wormhole.dm
+++ b/code/modules/projectiles/projectile/special/wormhole.dm
@@ -4,13 +4,13 @@
hitsound = "sparks"
damage = 0
nodamage = TRUE
- hitscan = TRUE
pass_flags = PASSGLASS | PASSTABLE | PASSGRILLE | PASSMOB
var/obj/item/gun/energy/wormhole_projector/gun
color = "#33CCFF"
tracer_type = /obj/effect/projectile/tracer/wormhole
impact_type = /obj/effect/projectile/impact/wormhole
muzzle_type = /obj/effect/projectile/muzzle/wormhole
+ hitscan = TRUE
/obj/item/projectile/beam/wormhole/orange
name = "orange bluespace beam"
@@ -21,7 +21,9 @@
if(casing)
gun = casing.gun
+
/obj/item/projectile/beam/wormhole/on_hit(atom/target)
if(!gun)
qdel(src)
+ return
gun.create_portal(src, get_turf(src))
diff --git a/code/modules/reagents/chemistry/holder.dm b/code/modules/reagents/chemistry/holder.dm
index 2def04cc66..8d3d704f79 100644
--- a/code/modules/reagents/chemistry/holder.dm
+++ b/code/modules/reagents/chemistry/holder.dm
@@ -550,8 +550,8 @@
if(!D)
WARNING("[my_atom] attempted to add a reagent called '[reagent]' which doesn't exist. ([usr])")
return FALSE
-
- update_total()
+
+ update_total()
var/cached_total = total_volume
if(cached_total + amount > maximum_volume)
amount = (maximum_volume - cached_total) //Doesnt fit in. Make it disappear. Shouldnt happen. Will happen.
@@ -594,14 +594,14 @@
if(data)
R.data = data
R.on_new(data)
-
+
+ if(isliving(my_atom))
+ R.on_mob_add(my_atom) //Must occur befor it could posibly run on_mob_delete
update_total()
if(my_atom)
my_atom.on_reagent_change(ADD_REAGENT)
if(!no_react)
handle_reactions()
- if(isliving(my_atom))
- R.on_mob_add(my_atom)
return TRUE
/datum/reagents/proc/add_reagent_list(list/list_reagents, list/data=null) // Like add_reagent but you can enter a list. Format it like this: list("toxin" = 10, "beer" = 15)
diff --git a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm
index 80e4453c7d..94cb2cd6e3 100644
--- a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm
+++ b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm
@@ -139,7 +139,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "chem_dispenser", name, 550, 550, master_ui, state)
+ ui = new(user, src, ui_key, "chem_dispenser", name, 565, 550, master_ui, state)
if(user.hallucinating())
ui.set_autoupdate(FALSE) //to not ruin the immersion by constantly changing the fake chemicals
ui.open()
@@ -278,7 +278,7 @@
var/chemid = reagent[1]
visible_message("
[src] buzzes. ", "
You hear a faint buzz. ")
to_chat(usr, "
[src] cannot find Chemical ID: [chemid] ! ")
- playsound(src, "sound/machines/buzz-two.ogg", 50, 1)
+ playsound(src, 'sound/machines/buzz-two.ogg', 50, 1)
return
if (resmismatch && alert("[src] is not yet capable of replicating this recipe with the precision it needs, do you want to save it anyway?",, "Yes","No") == "No")
return
@@ -384,6 +384,16 @@
final_list += list(avoid_assoc_duplicate_keys(fuck[1],key_list) = text2num(fuck[2]))
return final_list
+/obj/machinery/chem_dispenser/drinks/Initialize()
+ . = ..()
+ AddComponent(/datum/component/simple_rotation, ROTATION_ALTCLICK | ROTATION_CLOCKWISE)
+
+/obj/machinery/chem_dispenser/drinks/setDir()
+ var/old = dir
+ . = ..()
+ if(dir != old)
+ update_icon() // the beaker needs to be re-positioned if we rotate
+
/obj/machinery/chem_dispenser/drinks/display_beaker()
var/mutable_appearance/b_o = beaker_overlay || mutable_appearance(icon, "disp_beaker")
switch(dir)
@@ -413,6 +423,7 @@
circuit = /obj/item/circuitboard/machine/chem_dispenser/drinks
working_state = null
nopower_state = null
+ pass_flags = PASSTABLE
dispensable_reagents = list(
"water",
"ice",
@@ -444,6 +455,24 @@
"tirizene"
)
+/obj/machinery/chem_dispenser/drinks/fullupgrade //fully ugpraded stock parts, emagged
+ desc = "Contains a large reservoir of soft drinks. This model has had its safeties shorted out."
+ obj_flags = CAN_BE_HIT | EMAGGED
+ flags_1 = NODECONSTRUCT_1
+
+/obj/machinery/chem_dispenser/drinks/fullupgrade/Initialize()
+ . = ..()
+ dispensable_reagents |= emagged_reagents //adds emagged reagents
+ component_parts = list()
+ component_parts += new /obj/item/circuitboard/machine/chem_dispenser/drinks(null)
+ component_parts += new /obj/item/stock_parts/matter_bin/bluespace(null)
+ component_parts += new /obj/item/stock_parts/matter_bin/bluespace(null)
+ component_parts += new /obj/item/stock_parts/capacitor/quadratic(null)
+ component_parts += new /obj/item/stock_parts/manipulator/femto(null)
+ component_parts += new /obj/item/stack/sheet/glass(null)
+ component_parts += new /obj/item/stock_parts/cell/bluespace(null)
+ RefreshParts()
+
/obj/machinery/chem_dispenser/drinks/beer
name = "booze dispenser"
desc = "Contains a large reservoir of the good stuff."
@@ -477,6 +506,23 @@
"fernet"
)
+/obj/machinery/chem_dispenser/drinks/beer/fullupgrade //fully ugpraded stock parts, emagged
+ desc = "Contains a large reservoir of the good stuff. This model has had its safeties shorted out."
+ obj_flags = CAN_BE_HIT | EMAGGED
+ flags_1 = NODECONSTRUCT_1
+
+/obj/machinery/chem_dispenser/drinks/beer/fullupgrade/Initialize()
+ . = ..()
+ dispensable_reagents |= emagged_reagents //adds emagged reagents
+ component_parts = list()
+ component_parts += new /obj/item/circuitboard/machine/chem_dispenser/drinks/beer(null)
+ component_parts += new /obj/item/stock_parts/matter_bin/bluespace(null)
+ component_parts += new /obj/item/stock_parts/matter_bin/bluespace(null)
+ component_parts += new /obj/item/stock_parts/capacitor/quadratic(null)
+ component_parts += new /obj/item/stock_parts/manipulator/femto(null)
+ component_parts += new /obj/item/stack/sheet/glass(null)
+ component_parts += new /obj/item/stock_parts/cell/bluespace(null)
+ RefreshParts()
/obj/machinery/chem_dispenser/mutagen
name = "mutagen dispenser"
@@ -488,6 +534,8 @@
/obj/machinery/chem_dispenser/mutagensaltpeter
name = "botanical chemical dispenser"
desc = "Creates and dispenses chemicals useful for botany."
+ flags_1 = NODECONSTRUCT_1
+
dispensable_reagents = list(
"mutagen",
"saltpetre",
@@ -502,10 +550,8 @@
"ammonia",
"ash",
"diethylamine")
-
-/obj/machinery/chem_dispenser/fullupgrade //fully upgraded stock parts
-
-/obj/machinery/chem_dispenser/fullupgrade/Initialize()
+
+/obj/machinery/chem_dispenser/mutagensaltpeter/Initialize()
. = ..()
component_parts = list()
component_parts += new /obj/item/circuitboard/machine/chem_dispenser(null)
@@ -516,3 +562,21 @@
component_parts += new /obj/item/stack/sheet/glass(null)
component_parts += new /obj/item/stock_parts/cell/bluespace(null)
RefreshParts()
+
+/obj/machinery/chem_dispenser/fullupgrade //fully ugpraded stock parts, emagged
+ desc = "Creates and dispenses chemicals. This model has had its safeties shorted out."
+ obj_flags = CAN_BE_HIT | EMAGGED
+ flags_1 = NODECONSTRUCT_1
+
+/obj/machinery/chem_dispenser/fullupgrade/Initialize()
+ . = ..()
+ dispensable_reagents |= emagged_reagents //adds emagged reagents
+ component_parts = list()
+ component_parts += new /obj/item/circuitboard/machine/chem_dispenser(null)
+ component_parts += new /obj/item/stock_parts/matter_bin/bluespace(null)
+ component_parts += new /obj/item/stock_parts/matter_bin/bluespace(null)
+ component_parts += new /obj/item/stock_parts/capacitor/quadratic(null)
+ component_parts += new /obj/item/stock_parts/manipulator/femto(null)
+ component_parts += new /obj/item/stack/sheet/glass(null)
+ component_parts += new /obj/item/stock_parts/cell/bluespace(null)
+ RefreshParts()
diff --git a/code/modules/reagents/chemistry/machinery/pandemic.dm b/code/modules/reagents/chemistry/machinery/pandemic.dm
index 25385ca2ff..0e482f8cec 100644
--- a/code/modules/reagents/chemistry/machinery/pandemic.dm
+++ b/code/modules/reagents/chemistry/machinery/pandemic.dm
@@ -190,7 +190,7 @@
if("create_culture_bottle")
var/id = get_virus_id_by_index(text2num(params["index"]))
var/datum/disease/advance/A = SSdisease.archive_diseases[id]
- if(!A.mutable)
+ if(!istype(A) || !A.mutable)
to_chat(usr, "
ERROR: Cannot replicate virus strain. ")
return
A = A.Copy()
diff --git a/code/modules/reagents/chemistry/machinery/smoke_machine.dm b/code/modules/reagents/chemistry/machinery/smoke_machine.dm
index a8b0919906..4d60655488 100644
--- a/code/modules/reagents/chemistry/machinery/smoke_machine.dm
+++ b/code/modules/reagents/chemistry/machinery/smoke_machine.dm
@@ -85,7 +85,7 @@
var/units = RC.reagents.trans_to(src, RC.amount_per_transfer_from_this)
if(units)
to_chat(user, "
You transfer [units] units of the solution to [src]. ")
- add_logs(usr, src, "has added [english_list(RC.reagents.reagent_list)] to [src]")
+ log_combat(usr, src, "has added [english_list(RC.reagents.reagent_list)] to [src]")
return
if(default_unfasten_wrench(user, I, 40))
on = FALSE
@@ -144,7 +144,7 @@
if(on)
message_admins("[ADMIN_LOOKUPFLW(usr)] activated a smoke machine that contains [english_list(reagents.reagent_list)] at [ADMIN_VERBOSEJMP(src)].")
log_game("[key_name(usr)] activated a smoke machine that contains [english_list(reagents.reagent_list)] at [AREACOORD(src)].")
- add_logs(usr, src, "has activated [src] which contains [english_list(reagents.reagent_list)] at [AREACOORD(src)].")
+ log_combat(usr, src, "has activated [src] which contains [english_list(reagents.reagent_list)] at [AREACOORD(src)].")
if("goScreen")
screen = params["screen"]
. = TRUE
diff --git a/code/modules/reagents/chemistry/reagents.dm b/code/modules/reagents/chemistry/reagents.dm
index 6f65c33ea1..04f2a00302 100644
--- a/code/modules/reagents/chemistry/reagents.dm
+++ b/code/modules/reagents/chemistry/reagents.dm
@@ -88,29 +88,29 @@
/datum/reagent/proc/overdose_start(mob/living/M)
to_chat(M, "
You feel like you took too much of [name]! ")
- SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "[id]_overdose", /datum/mood_event/drugs/overdose, name)
+ SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "[id]_overdose", /datum/mood_event/overdose, name)
return
/datum/reagent/proc/addiction_act_stage1(mob/living/M)
- SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "[id]_overdose", /datum/mood_event/drugs/withdrawal_light, name)
+ SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "[id]_overdose", /datum/mood_event/withdrawal_light, name)
if(prob(30))
to_chat(M, "
You feel like having some [name] right about now. ")
return
/datum/reagent/proc/addiction_act_stage2(mob/living/M)
- SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "[id]_overdose", /datum/mood_event/drugs/withdrawal_medium, name)
+ SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "[id]_overdose", /datum/mood_event/withdrawal_medium, name)
if(prob(30))
to_chat(M, "
You feel like you need [name]. You just can't get enough. ")
return
/datum/reagent/proc/addiction_act_stage3(mob/living/M)
- SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "[id]_overdose", /datum/mood_event/drugs/withdrawal_severe, name)
+ SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "[id]_overdose", /datum/mood_event/withdrawal_severe, name)
if(prob(30))
to_chat(M, "
You have an intense craving for [name]. ")
return
/datum/reagent/proc/addiction_act_stage4(mob/living/M)
- SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "[id]_overdose", /datum/mood_event/drugs/withdrawal_critical, name)
+ SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "[id]_overdose", /datum/mood_event/withdrawal_critical, name)
if(prob(30))
to_chat(M, "
You're not feeling good at all! You really need some [name]. ")
return
diff --git a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm
index 3e3282fb89..f9dbd1928b 100644
--- a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm
@@ -153,6 +153,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
color = "#102000" // rgb: 16, 32, 0
nutriment_factor = 1 * REAGENTS_METABOLISM
boozepwr = 80
+ quality = DRINK_GOOD
overdose_threshold = 60
addiction_threshold = 30
taste_description = "jitters and death"
@@ -249,6 +250,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "Made for a woman, strong enough for a man."
color = "#666340" // rgb: 102, 99, 64
boozepwr = 10
+ quality = DRINK_FANTASTIC
taste_description = "dryness"
glass_icon_state = "threemileislandglass"
glass_name = "Three Mile Island Ice Tea"
@@ -323,6 +325,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "An alcoholic beverage from Space China, made by infusing lizard tails in ethanol."
color = "#7E4043" // rgb: 126, 64, 67
boozepwr = 45
+ quality = DRINK_FANTASTIC
taste_description = "scaley sweetness"
/datum/reagent/consumable/ethanol/grappa
@@ -394,6 +397,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "100 proof cinnamon schnapps, made for alcoholic teen girls on spring break."
color = "#FFFF91" // rgb: 255, 255, 145
boozepwr = 25
+ quality = DRINK_VERYGOOD
taste_description = "burning cinnamon"
glass_icon_state = "goldschlagerglass"
glass_name = "glass of goldschlager"
@@ -406,6 +410,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "Tequila with silver in it, a favorite of alcoholic women in the club scene."
color = "#585840" // rgb: 88, 88, 64
boozepwr = 60
+ quality = DRINK_VERYGOOD
taste_description = "metallic and expensive"
glass_icon_state = "patronglass"
glass_name = "glass of patron"
@@ -418,6 +423,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "An all time classic, mild cocktail."
color = "#664300" // rgb: 102, 67, 0
boozepwr = 25
+ quality = DRINK_NICE
taste_description = "mild and tart"
glass_icon_state = "gintonicglass"
glass_name = "Gin and Tonic"
@@ -429,6 +435,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "Rum, mixed with cola."
taste_description = "cola"
boozepwr = 40
+ quality = DRINK_NICE
color = "#3E1B00"
glass_icon_state = "whiskeycolaglass"
glass_name = "Rum and Coke"
@@ -440,6 +447,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "Viva la Revolucion! Viva Cuba Libre!"
color = "#3E1B00" // rgb: 62, 27, 0
boozepwr = 50
+ quality = DRINK_GOOD
taste_description = "a refreshing marriage of citrus and rum"
glass_icon_state = "cubalibreglass"
glass_name = "Cuba Libre"
@@ -460,17 +468,20 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "Whiskey, mixed with cola. Surprisingly refreshing."
color = "#3E1B00" // rgb: 62, 27, 0
boozepwr = 70
+ quality = DRINK_NICE
taste_description = "cola"
glass_icon_state = "whiskeycolaglass"
glass_name = "whiskey cola"
glass_desc = "An innocent-looking mixture of cola and Whiskey. Delicious."
+
/datum/reagent/consumable/ethanol/martini
name = "Classic Martini"
id = "martini"
description = "Vermouth with Gin. Not quite how 007 enjoyed it, but still delicious."
color = "#664300" // rgb: 102, 67, 0
boozepwr = 60
+ quality = DRINK_NICE
taste_description = "dry class"
glass_icon_state = "martiniglass"
glass_name = "Classic Martini"
@@ -482,6 +493,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "Vodka with Gin. Not quite how 007 enjoyed it, but still delicious."
color = "#664300" // rgb: 102, 67, 0
boozepwr = 65
+ quality = DRINK_NICE
taste_description = "shaken, not stirred"
glass_icon_state = "martiniglass"
glass_name = "Vodka martini"
@@ -493,6 +505,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "That's just, like, your opinion, man..."
color = "#A68340" // rgb: 166, 131, 64
boozepwr = 50
+ quality = DRINK_GOOD
taste_description = "bitter cream"
glass_icon_state = "whiterussianglass"
glass_name = "White Russian"
@@ -504,6 +517,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "Vodka, mixed with plain ol' orange juice. The result is surprisingly delicious."
color = "#A68310" // rgb: 166, 131, 16
boozepwr = 55
+ quality = DRINK_NICE
taste_description = "oranges"
glass_icon_state = "screwdriverglass"
glass_name = "Screwdriver"
@@ -531,6 +545,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "A strange yet pleasurable mixture made of vodka, tomato and lime juice. Or at least you THINK the red stuff is tomato juice."
color = "#664300" // rgb: 102, 67, 0
boozepwr = 55
+ quality = DRINK_GOOD
taste_description = "tomatoes with a hint of lime"
glass_icon_state = "bloodymaryglass"
glass_name = "Bloody Mary"
@@ -547,6 +562,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "It's just as effective as Dutch-Courage!"
color = "#664300" // rgb: 102, 67, 0
boozepwr = 80
+ quality = DRINK_NICE
taste_description = "alcoholic bravery"
glass_icon_state = "bravebullglass"
glass_name = "Brave Bull"
@@ -570,6 +586,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "Tequila, Grenadine, and Orange Juice."
color = "#FFE48C" // rgb: 255, 228, 140
boozepwr = 45
+ quality = DRINK_GOOD
taste_description = "oranges with a hint of pomegranate"
glass_icon_state = "tequilasunriseglass"
glass_name = "tequila Sunrise"
@@ -598,6 +615,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "This thing is ON FIRE! CALL THE DAMN SHUTTLE!"
color = "#664300" // rgb: 102, 67, 0
boozepwr = 25
+ quality = DRINK_VERYGOOD
taste_description = "spicy toxins"
glass_icon_state = "toxinsspecialglass"
glass_name = "Toxins Special"
@@ -614,6 +632,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "Drink this and prepare for the LAW."
color = "#664300" // rgb: 102, 67, 0
boozepwr = 90 //THE FIST OF THE LAW IS STRONG AND HARD
+ quality = DRINK_GOOD
metabolization_rate = 0.8
taste_description = "JUSTICE"
glass_icon_state = "beepskysmashglass"
@@ -633,6 +652,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "Whiskey-imbued cream, what else would you expect from the Irish?"
color = "#664300" // rgb: 102, 67, 0
boozepwr = 70
+ quality = DRINK_NICE
taste_description = "creamy alcohol"
glass_icon_state = "irishcreamglass"
glass_name = "Irish Cream"
@@ -644,6 +664,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "Beer and Ale, brought together in a delicious mix. Intended for true men only."
color = "#664300" // rgb: 102, 67, 0
boozepwr = 100 //For the manly only
+ quality = DRINK_NICE
taste_description = "hair on your chest and your chin"
glass_icon_state = "manlydorfglass"
glass_name = "The Manly Dorf"
@@ -670,6 +691,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "The liquor cabinet, brought together in a delicious mix. Intended for middle-aged alcoholic women only."
color = "#664300" // rgb: 102, 67, 0
boozepwr = 35
+ quality = DRINK_VERYGOOD
taste_description = "a mixture of cola and alcohol"
glass_icon_state = "longislandicedteaglass"
glass_name = "Long Island Iced Tea"
@@ -693,6 +715,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "Coffee, Irish Cream, and cognac. You will get bombed."
color = "#664300" // rgb: 102, 67, 0
boozepwr = 85
+ quality = DRINK_GOOD
taste_description = "angry and irish"
glass_icon_state = "b52glass"
glass_name = "B-52"
@@ -708,6 +731,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "Coffee, and alcohol. More fun than a Mimosa to drink in the morning."
color = "#664300" // rgb: 102, 67, 0
boozepwr = 35
+ quality = DRINK_NICE
taste_description = "giving up on the day"
glass_icon_state = "irishcoffeeglass"
glass_name = "Irish Coffee"
@@ -719,6 +743,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "On the rocks with salt on the rim. Arriba~!"
color = "#8CFF8C" // rgb: 140, 255, 140
boozepwr = 35
+ quality = DRINK_NICE
taste_description = "dry and salty"
glass_icon_state = "margaritaglass"
glass_name = "Margarita"
@@ -730,6 +755,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "For the lactose-intolerant. Still as classy as a White Russian."
color = "#360000" // rgb: 54, 0, 0
boozepwr = 70
+ quality = DRINK_NICE
taste_description = "bitterness"
glass_icon_state = "blackrussianglass"
glass_name = "Black Russian"
@@ -742,6 +768,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "The Detective's undercover drink of choice. He never could stomach gin..."
color = "#664300" // rgb: 102, 67, 0
boozepwr = 30
+ quality = DRINK_NICE
taste_description = "mild dryness"
glass_icon_state = "manhattanglass"
glass_name = "Manhattan"
@@ -754,6 +781,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "A scientist's drink of choice, for pondering ways to blow up the station."
color = "#664300" // rgb: 102, 67, 0
boozepwr = 45
+ quality = DRINK_VERYGOOD
taste_description = "death, the destroyer of worlds"
glass_icon_state = "proj_manhattanglass"
glass_name = "Manhattan Project"
@@ -770,6 +798,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "For the more refined griffon."
color = "#664300" // rgb: 102, 67, 0
boozepwr = 70
+ quality = DRINK_NICE
taste_description = "soda"
glass_icon_state = "whiskeysodaglass2"
glass_name = "whiskey soda"
@@ -781,6 +810,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "The ultimate refreshment. Not what it sounds like."
color = "#664300" // rgb: 102, 67, 0
boozepwr = 35
+ quality = DRINK_NICE
taste_description = "Jack Frost's piss"
glass_icon_state = "antifreeze"
glass_name = "Anti-freeze"
@@ -796,6 +826,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "Barefoot and pregnant."
color = "#664300" // rgb: 102, 67, 0
boozepwr = 45
+ quality = DRINK_VERYGOOD
taste_description = "creamy berries"
glass_icon_state = "b&p"
glass_name = "Barefoot"
@@ -815,6 +846,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "A cold refreshment."
color = "#FFFFFF" // rgb: 255, 255, 255
boozepwr = 35
+ quality = DRINK_NICE
taste_description = "refreshing cold"
glass_icon_state = "snowwhite"
glass_name = "Snow White"
@@ -826,6 +858,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "AHHHH!!!!"
color = "#820000" // rgb: 130, 0, 0
boozepwr = 75
+ quality = DRINK_VERYGOOD
taste_description = "sweet tasting iron"
glass_icon_state = "demonsblood"
glass_name = "Demons Blood"
@@ -837,6 +870,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "Creepy time!"
color = "#A68310" // rgb: 166, 131, 16
boozepwr = 70
+ quality = DRINK_VERYGOOD
taste_description = "bitter iron"
glass_icon_state = "devilskiss"
glass_name = "Devils Kiss"
@@ -848,6 +882,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "For when a gin and tonic isn't Russian enough."
color = "#0064C8" // rgb: 0, 100, 200
boozepwr = 70
+ quality = DRINK_NICE
taste_description = "tart bitterness"
glass_icon_state = "vodkatonicglass"
glass_name = "vodka and tonic"
@@ -860,6 +895,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "Refreshingly lemony, deliciously dry."
color = "#664300" // rgb: 102, 67, 0
boozepwr = 45
+ quality = DRINK_GOOD
taste_description = "dry, tart lemons"
glass_icon_state = "ginfizzglass"
glass_name = "gin fizz"
@@ -872,6 +908,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "Tropical cocktail."
color = "#FF7F3B" // rgb: 255, 127, 59
boozepwr = 35
+ quality = DRINK_GOOD
taste_description = "lime and orange"
glass_icon_state = "bahama_mama"
glass_name = "Bahama Mama"
@@ -883,6 +920,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "A blue-space beverage!"
color = "#2E6671" // rgb: 46, 102, 113
boozepwr = 35
+ quality = DRINK_VERYGOOD
taste_description = "concentrated matter"
glass_icon_state = "singulo"
glass_name = "Singulo"
@@ -894,6 +932,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "A spicy Vodka! Might be a little hot for the little guys!"
color = "#664300" // rgb: 102, 67, 0
boozepwr = 70
+ quality = DRINK_GOOD
taste_description = "hot and spice"
glass_icon_state = "sbitenglass"
glass_name = "Sbiten"
@@ -909,6 +948,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "The true Viking drink! Even though it has a strange red color."
color = "#C73C00" // rgb: 199, 60, 0
boozepwr = 51 //Red drinks are stronger
+ quality = DRINK_GOOD
taste_description = "sweet and salty alcohol"
glass_icon_state = "red_meadglass"
glass_name = "Red Mead"
@@ -921,6 +961,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
color = "#664300" // rgb: 102, 67, 0
nutriment_factor = 1 * REAGENTS_METABOLISM
boozepwr = 50
+ quality = DRINK_NICE
taste_description = "sweet, sweet alcohol"
glass_icon_state = "meadglass"
glass_name = "Mead"
@@ -959,6 +1000,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "So very, very, very good."
color = "#664300" // rgb: 102, 67, 0
boozepwr = 35
+ quality = DRINK_VERYGOOD
taste_description = "sweet 'n creamy"
glass_icon_state = "aloe"
glass_name = "Aloe"
@@ -970,6 +1012,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "A nice, strangely named drink."
color = "#664300" // rgb: 102, 67, 0
boozepwr = 40
+ quality = DRINK_GOOD
taste_description = "lemons"
glass_icon_state = "andalusia"
glass_name = "Andalusia"
@@ -981,6 +1024,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "A drink made from your allies. Not as sweet as those made from your enemies."
color = "#664300" // rgb: 102, 67, 0
boozepwr = 45
+ quality = DRINK_NICE
taste_description = "bitter yet free"
glass_icon_state = "alliescocktail"
glass_name = "Allies cocktail"
@@ -992,6 +1036,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "A drink for the daring, can be deadly if incorrectly prepared!"
color = "#365000" // rgb: 54, 80, 0
boozepwr = 80
+ quality = DRINK_VERYGOOD
taste_description = "stomach acid"
glass_icon_state = "acidspitglass"
glass_name = "Acid Spit"
@@ -1003,6 +1048,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "Official drink of the Nanotrasen Gun-Club!"
color = "#664300" // rgb: 102, 67, 0
boozepwr = 35
+ quality = DRINK_GOOD
taste_description = "dark and metallic"
glass_icon_state = "amasecglass"
glass_name = "Amasec"
@@ -1014,6 +1060,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "You take a tiny sip and feel a burning sensation..."
color = "#2E6671" // rgb: 46, 102, 113
boozepwr = 95
+ quality = DRINK_GOOD
taste_description = "your brain coming out your nose"
glass_icon_state = "changelingsting"
glass_name = "Changeling Sting"
@@ -1033,6 +1080,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "Mmm, tastes like chocolate cake..."
color = "#2E6671" // rgb: 46, 102, 113
boozepwr = 25
+ quality = DRINK_GOOD
taste_description = "delicious anger"
glass_icon_state = "irishcarbomb"
glass_name = "Irish Car Bomb"
@@ -1044,6 +1092,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "Tastes like terrorism!"
color = "#2E6671" // rgb: 46, 102, 113
boozepwr = 90
+ quality = DRINK_GOOD
taste_description = "purified antagonism"
glass_icon_state = "syndicatebomb"
glass_name = "Syndicate Bomb"
@@ -1060,6 +1109,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "The surprise is, it's green!"
color = "#2E6671" // rgb: 46, 102, 113
boozepwr = 35
+ quality = DRINK_VERYGOOD
taste_description = "tartness and bananas"
glass_icon_state = "erikasurprise"
glass_name = "Erika Surprise"
@@ -1072,6 +1122,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
nutriment_factor = 1 * REAGENTS_METABOLISM
color = "#2E6671" // rgb: 46, 102, 113
boozepwr = 65
+ quality = DRINK_GOOD
taste_description = "a beach"
glass_icon_state = "driestmartiniglass"
glass_name = "Driest Martini"
@@ -1084,6 +1135,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
nutriment_factor = 1 * REAGENTS_METABOLISM
color = "#FFFF91" // rgb: 255, 255, 140
boozepwr = 60
+ quality = DRINK_GOOD
taste_description = "a bad joke"
glass_icon_state = "bananahonkglass"
glass_name = "Banana Honk"
@@ -1102,6 +1154,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
nutriment_factor = 1 * REAGENTS_METABOLISM
color = "#664300" // rgb: 102, 67, 0
boozepwr = 59 //Proof that clowns are better than mimes right here
+ quality = DRINK_GOOD
taste_description = "a pencil eraser"
glass_icon_state = "silencerglass"
glass_name = "Silencer"
@@ -1119,6 +1172,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "A weird mix of whiskey and blumpkin juice."
color = "#1EA0FF" // rgb: 102, 67, 0
boozepwr = 50
+ quality = DRINK_VERYGOOD
taste_description = "molasses and a mouthful of pool water"
glass_icon_state = "drunkenblumpkin"
glass_name = "Drunken Blumpkin"
@@ -1130,6 +1184,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "Lemon juice/whiskey/sugar mixture. Moderate alcohol content."
color = rgb(255, 201, 49)
boozepwr = 35
+ quality = DRINK_GOOD
taste_description = "sour lemons"
glass_icon_state = "whiskey_sour"
glass_name = "whiskey sour"
@@ -1155,6 +1210,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "Whiskey sour/iron/uranium mixture resulting in a highly magnetic slurry. Mild alcohol content." //Requires no alcohol to make but has alcohol anyway because ~magic~
color = rgb(255, 91, 15)
boozepwr = 10
+ quality = DRINK_VERYGOOD
metabolization_rate = 0.1 * REAGENTS_METABOLISM
taste_description = "charged metal" // the same as teslium, honk honk.
glass_icon_state = "fetching_fizz"
@@ -1174,6 +1230,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "Brave bull/syndicate bomb/absinthe mixture resulting in an energizing beverage. Mild alcohol content."
color = rgb(140, 0, 0)
boozepwr = 90
+ quality = DRINK_VERYGOOD
metabolization_rate = 0.4 * REAGENTS_METABOLISM
taste_description = "bravado in the face of disaster"
glass_icon_state = "hearty_punch"
@@ -1209,6 +1266,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "Nuclear proliferation never tasted so good."
color = "#666300" // rgb: 102, 99, 0
boozepwr = 0 //custom drunk effect
+ quality = DRINK_FANTASTIC
taste_description = "da bomb"
glass_icon_state = "atomicbombglass"
glass_name = "Atomic Bomb"
@@ -1238,6 +1296,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "Whoah, this stuff looks volatile!"
color = "#664300" // rgb: 102, 67, 0
boozepwr = 0 //custom drunk effect
+ quality = DRINK_GOOD
taste_description = "your brains smashed out by a lemon wrapped around a gold brick"
glass_icon_state = "gargleblasterglass"
glass_name = "Pan-Galactic Gargle Blaster"
@@ -1266,6 +1325,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "A strong neurotoxin that puts the subject into a death-like state."
color = "#2E2E61" // rgb: 46, 46, 97
boozepwr = 0 //custom drunk effect
+ quality = DRINK_VERYGOOD
taste_description = "a numbing sensation"
glass_icon_state = "neurotoxinglass"
glass_name = "Neurotoxin"
@@ -1296,6 +1356,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
color = "#664300" // rgb: 102, 67, 0
nutriment_factor = 0
boozepwr = 0 //custom drunk effect
+ quality = DRINK_FANTASTIC
metabolization_rate = 0.2 * REAGENTS_METABOLISM
taste_description = "giving peace a chance"
glass_icon_state = "hippiesdelightglass"
@@ -1341,6 +1402,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
color = "#fcfdc6" // rgb: 252, 253, 198
nutriment_factor = 2 * REAGENTS_METABOLISM
boozepwr = 1
+ quality = DRINK_VERYGOOD
taste_description = "custard and alcohol"
glass_icon_state = "glass_yellow"
glass_name = "eggnog"
@@ -1353,6 +1415,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "Side effects include self-mutilation and hoarding plasteel."
color = RUNE_COLOR_DARKRED
boozepwr = 10
+ quality = DRINK_FANTASTIC
taste_description = "bloody"
glass_icon_state = "narsour"
glass_name = "Nar'Sour"
@@ -1402,6 +1465,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "Kicks just as hard as licking the powercell on a baton, but tastier."
color = "#cc0000"
boozepwr = 35
+ quality = DRINK_GOOD
taste_description = "an invigorating bitter freshness which suffuses your being; no enemy of the station will go unrobusted this day"
glass_icon_state = "quadruple_sec"
glass_name = "Quadruple Sec"
@@ -1420,6 +1484,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "Law, Order, Alcohol, and Police Brutality distilled into one single elixir of JUSTICE."
color = "#ff3300"
boozepwr = 80
+ quality = DRINK_FANTASTIC
taste_description = "THE LAW"
glass_icon_state = "quintuple_sec"
glass_name = "Quintuple Sec"
@@ -1441,6 +1506,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "A fresh and sweet dessert shooter. Difficult to look manly while drinking this."
color = "00ff00"
boozepwr = 25
+ quality = DRINK_GOOD
taste_description = "chocolate and mint dancing around your mouth"
glass_icon_state = "grasshopper"
glass_name = "Grasshopper"
@@ -1452,6 +1518,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "A snappy way to end the day."
color = "ccff99"
boozepwr = 25
+ quality = DRINK_NICE
taste_description = "a slap on the face in the best possible way"
glass_icon_state = "stinger"
glass_name = "Stinger"
@@ -1463,6 +1530,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "Soothing hot herbal brew with restorative properties. Hints of citrus and berry flavors."
color = "#00FFFF"
boozepwr = 30
+ quality = DRINK_FANTASTIC
taste_description = "hot herbal brew with a hint of fruit"
metabolization_rate = 2 * REAGENTS_METABOLISM //0.8u per tick
glass_icon_state = "bastion_bourbon"
@@ -1518,6 +1586,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "Bubbly, classy, and undoubtedly strong - a Glitch City classic."
color = "#FFEAC4"
boozepwr = 90 //classy hooch, essentially, but lower pwr to make up for slightly easier access
+ quality = DRINK_GOOD
taste_description = "ethylic alcohol with a hint of sugar"
glass_icon_state = "fringe_weaver"
glass_name = "Fringe Weaver"
@@ -1529,6 +1598,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "Sweet, light, and fruity - as girly as it gets."
color = "#FF226C"
boozepwr = 10
+ quality = DRINK_GOOD
taste_description = "your arteries clogging with sugar"
nutriment_factor = 2 * REAGENTS_METABOLISM
glass_icon_state = "sugar_rush"
@@ -1546,6 +1616,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "Sour, bitter, and smashingly sobering."
color = "#5BD231"
boozepwr = -10 //sobers you up - ideally, one would drink to get hit with brute damage now to avoid alcohol problems later
+ quality = DRINK_VERYGOOD
taste_description = "a bitter SPIKE with a sour aftertaste"
glass_icon_state = "crevice_spike"
glass_name = "Crevice Spike"
@@ -1572,6 +1643,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
color = "#45ca7a"
taste_description = "mint and chocolate"
boozepwr = 25
+ quality = DRINK_GOOD
glass_icon_state = "peppermint_patty"
glass_name = "Peppermint Patty"
glass_desc = "A boozy minty hot cocoa that warms your belly on a cold night."
@@ -1586,6 +1658,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "Named after a Greek hero, this mix is said to embolden a user's shield as if they were in a phalanx."
color = "#F5E9D3"
boozepwr = 80
+ quality = DRINK_GOOD
taste_description = "bitter, creamy cacao"
glass_icon_state = "alexander"
glass_name = "Alexander"
@@ -1618,6 +1691,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "The one ride you'll gladly give up the wheel for."
color = "#FFC55B"
boozepwr = 80
+ quality = DRINK_GOOD
taste_description = "delicious freedom"
glass_icon_state = "sidecar"
glass_name = "Sidecar"
@@ -1629,6 +1703,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "A provocatively named classic. Funny enough, doctors recommend drinking it before taking a nap."
color = "#F4C35A"
boozepwr = 80
+ quality = DRINK_GOOD
taste_description = "seduction"
glass_icon_state = "between_the_sheets"
glass_name = "Between the Sheets"
@@ -1653,6 +1728,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "Divinely windy."
color = "#EEF191"
boozepwr = 60
+ quality = DRINK_GOOD
taste_description = "divine windiness"
glass_icon_state = "kamikaze"
glass_name = "Kamikaze"
@@ -1664,6 +1740,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "A drink that looks as refreshing as it tastes."
color = "#DFFAD9"
boozepwr = 30
+ quality = DRINK_GOOD
taste_description = "refreshing mint"
glass_icon_state = "mojito"
glass_name = "Mojito"
@@ -1692,12 +1769,13 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "A very popular and bittersweet digestif, ideal after a heavy meal. Best served on a sawed-off cola bottle as per tradition."
color = "#390600" // rgb: 57, 6, 0
boozepwr = 25
+ quality = DRINK_NICE
taste_description = "sweet relief"
glass_icon_state = "godlyblend"
glass_name = "glass of fernet cola"
glass_desc = "A sawed-off cola bottle filled with Fernet Cola. Nothing better after eating like a lardass."
-/datum/reagent/consumable/ethanol/fernetcola/on_mob_life(mob/living/carbon/M)
+/datum/reagent/consumable/ethanol/fernet_cola/on_mob_life(mob/living/carbon/M)
if(M.nutrition <= NUTRITION_LEVEL_STARVING)
M.adjustToxLoss(0.5*REM, 0)
M.nutrition = max(M.nutrition - 3, 0)
@@ -1711,6 +1789,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "What if the Manhattan coctail ACTUALLY used a bitter herb liquour? Helps you sobers up." //also causes a bit of stamina damage to symbolize the afterdrink lazyness
color = "#CA933F" // rgb: 202, 147, 63
boozepwr = -10
+ quality = DRINK_NICE
taste_description = "a sweet sobering mix"
glass_icon_state = "fanciulli"
glass_name = "glass of fanciulli"
@@ -1734,6 +1813,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
description = "A refreshing mixture of bitter Fernet with mint creme liquour."
color = "#4B5746" // rgb: 75, 87, 70
boozepwr = 35
+ quality = DRINK_GOOD
taste_description = "a bitter freshness"
glass_icon_state= "minted_fernet"
glass_name = "glass of branca menta"
@@ -1749,3 +1829,108 @@ All effects don't start immediately, but rather get worse over time; the rate is
M.adjustStaminaLoss(35)
. = TRUE
..()
+
+/datum/reagent/consumable/ethanol/fruit_wine
+ name = "Fruit Wine"
+ id = "fruit_wine"
+ description = "A wine made from grown plants."
+ color = "#FFFFFF"
+ boozepwr = 35
+ quality = DRINK_GOOD
+ taste_description = "bad coding"
+ can_synth = FALSE
+ var/list/names = list("null fruit" = 1) //Names of the fruits used. Associative list where name is key, value is the percentage of that fruit.
+ var/list/tastes = list("bad coding" = 1) //List of tastes. See above.
+
+/datum/reagent/consumable/ethanol/fruit_wine/on_new(list/data)
+ names = data["names"]
+ tastes = data["tastes"]
+ boozepwr = data["boozepwr"]
+ color = data["color"]
+ generate_data_info(data)
+
+/datum/reagent/consumable/ethanol/fruit_wine/on_merge(list/data, amount)
+ var/diff = (amount/volume)
+ if(diff < 1)
+ color = BlendRGB(color, data["color"], diff/2) //The percentage difference over two, so that they take average if equal.
+ else
+ color = BlendRGB(color, data["color"], (1/diff)/2) //Adjust so it's always blending properly.
+ var/oldvolume = volume-amount
+
+ var/list/cachednames = data["names"]
+ for(var/name in names | cachednames)
+ names[name] = ((names[name] * oldvolume) + (cachednames[name] * amount)) / volume
+
+ var/list/cachedtastes = data["tastes"]
+ for(var/taste in tastes | cachedtastes)
+ tastes[taste] = ((tastes[taste] * oldvolume) + (cachedtastes[taste] * amount)) / volume
+
+ boozepwr *= oldvolume
+ var/newzepwr = data["boozepwr"] * amount
+ boozepwr += newzepwr
+ boozepwr /= volume //Blending boozepwr to volume.
+ generate_data_info(data)
+
+/datum/reagent/consumable/ethanol/fruit_wine/proc/generate_data_info(list/data)
+ var/minimum_percent = 0.15 //Percentages measured between 0 and 1.
+ var/list/primary_tastes = list()
+ var/list/secondary_tastes = list()
+ glass_name = "glass of [name]"
+ glass_desc = description
+ for(var/taste in tastes)
+ switch(tastes[taste])
+ if(minimum_percent*2 to INFINITY)
+ primary_tastes += taste
+ if(minimum_percent to minimum_percent*2)
+ secondary_tastes += taste
+
+ var/minimum_name_percent = 0.35
+ name = ""
+ var/list/names_in_order = sortTim(names, /proc/cmp_numeric_dsc, TRUE)
+ var/named = FALSE
+ for(var/fruit_name in names)
+ if(names[fruit_name] >= minimum_name_percent)
+ name += "[fruit_name] "
+ named = TRUE
+ if(named)
+ name += "wine"
+ else
+ name = "mixed [names_in_order[1]] wine"
+
+ var/alcohol_description
+ switch(boozepwr)
+ if(120 to INFINITY)
+ alcohol_description = "suicidally strong"
+ if(90 to 120)
+ alcohol_description = "rather strong"
+ if(70 to 90)
+ alcohol_description = "strong"
+ if(40 to 70)
+ alcohol_description = "rich"
+ if(20 to 40)
+ alcohol_description = "mild"
+ if(0 to 20)
+ alcohol_description = "sweet"
+ else
+ alcohol_description = "watery" //How the hell did you get negative boozepwr?
+
+ var/list/fruits = list()
+ if(names_in_order.len <= 3)
+ fruits = names_in_order
+ else
+ for(var/i in 1 to 3)
+ fruits += names_in_order[i]
+ fruits += "other plants"
+ var/fruit_list = english_list(fruits)
+ description = "A [alcohol_description] wine brewed from [fruit_list]."
+
+ var/flavor = ""
+ if(!primary_tastes.len)
+ primary_tastes = list("[alcohol_description] alcohol")
+ flavor += english_list(primary_tastes)
+ if(secondary_tastes.len)
+ flavor += ", with a hint of "
+ flavor += english_list(secondary_tastes)
+ taste_description = flavor
+ if(holder.my_atom)
+ holder.my_atom.on_reagent_change()
diff --git a/code/modules/reagents/chemistry/reagents/drink_reagents.dm b/code/modules/reagents/chemistry/reagents/drink_reagents.dm
index 4d2dba4659..0cbd1f2d85 100644
--- a/code/modules/reagents/chemistry/reagents/drink_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/drink_reagents.dm
@@ -310,6 +310,7 @@
id = "arnold_palmer"
description = "Encourages the patient to go golfing."
color = "#FFB766"
+ quality = DRINK_NICE
nutriment_factor = 2
taste_description = "bitter tea"
glass_icon_state = "arnold_palmer"
@@ -383,6 +384,7 @@
id = "nuka_cola"
description = "Cola, cola never changes."
color = "#100800" // rgb: 16, 8, 0
+ quality = DRINK_VERYGOOD
taste_description = "the future"
glass_icon_state = "nuka_colaglass"
glass_name = "glass of Nuka Cola"
@@ -550,6 +552,7 @@
id = "soy_latte"
description = "A nice and tasty beverage while you are reading your hippie books."
color = "#664300" // rgb: 102, 67, 0
+ quality = DRINK_NICE
taste_description = "creamy coffee"
glass_icon_state = "soy_latte"
glass_name = "soy latte"
@@ -571,6 +574,7 @@
id = "cafe_latte"
description = "A nice, strong and tasty beverage while you are reading."
color = "#664300" // rgb: 102, 67, 0
+ quality = DRINK_NICE
taste_description = "bitter cream"
glass_icon_state = "cafe_latte"
glass_name = "cafe latte"
@@ -592,6 +596,7 @@
id = "doctorsdelight"
description = "A gulp a day keeps the Medibot away! A mixture of juices that heals most damage types fairly quickly at the cost of hunger."
color = "#FF8CFF" // rgb: 255, 140, 255
+ quality = DRINK_VERYGOOD
taste_description = "homely fruit"
glass_icon_state = "doctorsdelightglass"
glass_name = "Doctor's Delight"
@@ -613,6 +618,7 @@
id = "chocolatepudding"
description = "A great dessert for chocolate lovers."
color = "#800000"
+ quality = DRINK_VERYGOOD
nutriment_factor = 4 * REAGENTS_METABOLISM
taste_description = "sweet chocolate"
glass_icon_state = "chocolatepudding"
@@ -624,6 +630,7 @@
id = "vanillapudding"
description = "A great dessert for vanilla lovers."
color = "#FAFAD2"
+ quality = DRINK_VERYGOOD
nutriment_factor = 4 * REAGENTS_METABOLISM
taste_description = "sweet vanilla"
glass_icon_state = "vanillapudding"
@@ -635,6 +642,7 @@
id = "cherryshake"
description = "A cherry flavored milkshake."
color = "#FFB6C1"
+ quality = DRINK_VERYGOOD
nutriment_factor = 4 * REAGENTS_METABOLISM
taste_description = "creamy cherry"
glass_icon_state = "cherryshake"
@@ -646,6 +654,7 @@
id = "bluecherryshake"
description = "An exotic milkshake."
color = "#00F1FF"
+ quality = DRINK_VERYGOOD
nutriment_factor = 4 * REAGENTS_METABOLISM
taste_description = "creamy blue cherry"
glass_icon_state = "bluecherryshake"
@@ -657,6 +666,7 @@
id = "pumpkin_latte"
description = "A mix of pumpkin juice and coffee."
color = "#F4A460"
+ quality = DRINK_VERYGOOD
nutriment_factor = 3 * REAGENTS_METABOLISM
taste_description = "creamy pumpkin"
glass_icon_state = "pumpkin_latte"
@@ -668,6 +678,7 @@
id = "gibbfloats"
description = "Ice cream on top of a Dr. Gibb glass."
color = "#B22222"
+ quality = DRINK_NICE
nutriment_factor = 3 * REAGENTS_METABOLISM
taste_description = "creamy cherry"
glass_icon_state = "gibbfloats"
@@ -693,6 +704,7 @@
id = "triple_citrus"
description = "A solution."
color = "#C8A5DC"
+ quality = DRINK_NICE
taste_description = "extreme bitterness"
glass_icon_state = "triplecitrus" //needs own sprite mine are trash
glass_name = "glass of triple citrus"
@@ -712,6 +724,7 @@
id = "chocolate_milk"
description = "Milk for cool kids."
color = "#7D4E29"
+ quality = DRINK_NICE
taste_description = "chocolate milk"
/datum/reagent/consumable/menthol
diff --git a/code/modules/reagents/chemistry/reagents/drug_reagents.dm b/code/modules/reagents/chemistry/reagents/drug_reagents.dm
index 33511c8905..989c5a9b82 100644
--- a/code/modules/reagents/chemistry/reagents/drug_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/drug_reagents.dm
@@ -28,7 +28,7 @@
/datum/reagent/drug/space_drugs/overdose_start(mob/living/M)
to_chat(M, "
You start tripping hard! ")
- SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "[id]_overdose", /datum/mood_event/drugs/overdose, name)
+ SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "[id]_overdose", /datum/mood_event/overdose, name)
/datum/reagent/drug/space_drugs/overdose_process(mob/living/M)
if(M.hallucination < volume && prob(20))
@@ -49,7 +49,7 @@
if(prob(1))
var/smoke_message = pick("You feel relaxed.", "You feel calmed.","You feel alert.","You feel rugged.")
to_chat(M, "
[smoke_message] ")
- SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "smoked", /datum/mood_event/drugs/smoked, name)
+ SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "smoked", /datum/mood_event/smoked, name)
M.AdjustStun(-20, 0)
M.AdjustKnockdown(-20, 0)
M.AdjustUnconscious(-20, 0)
@@ -132,7 +132,7 @@
..()
. = 1
-/datum/reagent/krokodil/addiction_act_stage2(mob/living/M)
+/datum/reagent/drug/krokodil/addiction_act_stage2(mob/living/M)
if(prob(25))
to_chat(M, "
Your skin feels loose... ")
..()
diff --git a/code/modules/reagents/chemistry/reagents/food_reagents.dm b/code/modules/reagents/chemistry/reagents/food_reagents.dm
index 459cbefaa2..685b37564a 100644
--- a/code/modules/reagents/chemistry/reagents/food_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/food_reagents.dm
@@ -13,12 +13,27 @@
taste_description = "generic food"
taste_mult = 4
var/nutriment_factor = 1 * REAGENTS_METABOLISM
+ var/quality = 0 //affects mood, typically higher for mixed drinks with more complex recipes
/datum/reagent/consumable/on_mob_life(mob/living/carbon/M)
current_cycle++
M.nutrition += nutriment_factor
holder.remove_reagent(src.id, metabolization_rate)
+/datum/reagent/consumable/reaction_mob(mob/living/M, method=TOUCH, reac_volume)
+ if(method == INGEST)
+ if (quality && !M.has_trait(TRAIT_AGEUSIA))
+ switch(quality)
+ if (DRINK_NICE)
+ SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "quality_drink", /datum/mood_event/quality_nice)
+ if (DRINK_GOOD)
+ SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "quality_drink", /datum/mood_event/quality_good)
+ if (DRINK_VERYGOOD)
+ SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "quality_drink", /datum/mood_event/quality_verygood)
+ if (DRINK_FANTASTIC)
+ SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "quality_drink", /datum/mood_event/quality_fantastic)
+ return ..()
+
/datum/reagent/consumable/nutriment
name = "Nutriment"
id = "nutriment"
@@ -491,8 +506,10 @@
/datum/reagent/consumable/flour/reaction_turf(turf/T, reac_volume)
if(!isspaceturf(T))
- var/obj/effect/decal/cleanable/reagentdecal = new/obj/effect/decal/cleanable/flour(T)
- reagentdecal.reagents.add_reagent("flour", reac_volume)
+ var/obj/effect/decal/cleanable/flour/reagentdecal = new/obj/effect/decal/cleanable/flour(T)
+ reagentdecal = locate() in T //Might have merged with flour already there.
+ if(reagentdecal)
+ reagentdecal.reagents.add_reagent("flour", reac_volume)
/datum/reagent/consumable/cherryjelly
name = "Cherry Jelly"
@@ -575,7 +592,7 @@
if(iscarbon(M) && (method in list(TOUCH, VAPOR, PATCH)))
var/mob/living/carbon/C = M
for(var/s in C.surgeries)
- var/datum/surgery/S = s
+ var/datum/surgery/S = s
S.success_multiplier = max(0.6, S.success_multiplier) // +60% success probability on each step, compared to bacchus' blessing's ~46%
..()
diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
index 881e694780..aaa7e30de0 100644
--- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
@@ -803,7 +803,7 @@
M.updatehealth()
if(M.revive())
M.emote("gasp")
- add_logs(M, M, "revived", src)
+ log_combat(M, M, "revived", src)
..()
/datum/reagent/medicine/strange_reagent/on_mob_life(mob/living/carbon/M)
@@ -1174,6 +1174,14 @@
color = "#F5F5F5"
self_consuming = TRUE
+/datum/reagent/medicine/corazone/on_mob_add(mob/living/M)
+ ..()
+ M.add_trait(TRAIT_STABLEHEART, id)
+
+/datum/reagent/medicine/corazone/on_mob_delete(mob/living/M)
+ M.remove_trait(TRAIT_STABLEHEART, id)
+ ..()
+
/datum/reagent/medicine/muscle_stimulant
name = "Muscle Stimulant"
id = "muscle_stimulant"
diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm
index c3d36b6b6a..d45c1f65f0 100644
--- a/code/modules/reagents/chemistry/reagents/other_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm
@@ -218,9 +218,9 @@
M.stuttering = 1
M.stuttering = min(M.stuttering+4, 10)
M.Dizzy(5)
- if(iscultist(M) && prob(8))
- M.say(pick("Av'te Nar'sie","Pa'lid Mors","INO INO ORA ANA","SAT ANA!","Daim'niodeis Arc'iai Le'eones","R'ge Na'sie","Diabo us Vo'iscum","Eld' Mon Nobis"))
- if(prob(20))
+ if(iscultist(M) && prob(20))
+ M.say(pick("Av'te Nar'Sie","Pa'lid Mors","INO INO ORA ANA","SAT ANA!","Daim'niodeis Arc'iai Le'eones","R'ge Na'sie","Diabo us Vo'iscum","Eld' Mon Nobis"), forced = "holy water")
+ if(prob(10))
M.visible_message("
[M] starts having a seizure! ", "
You have a seizure! ")
M.Unconscious(120)
to_chat(M, "
[pick("Your blood is your bond - you are nothing without it", "Do not forget your place", \
@@ -231,7 +231,7 @@
clockwork_say(M, "...[text2ratvar(pick("Engine... your light grows dark...", "Where are you, master?", "He lies rusting in Error...", "Purge all untruths and... and... something..."))]")
if("message")
to_chat(M, "[pick("Ratvar's illumination of your mind has begun to flicker", "He lies rusting in Reebe, derelict and forgotten. And there he shall stay", \
- "You can't save him. Nothing can save him now", "It seems that Nar-Sie will triumph after all")]. ")
+ "You can't save him. Nothing can save him now", "It seems that Nar'Sie will triumph after all")]. ")
if("emote")
M.visible_message("
[M] [pick("whimpers quietly", "shivers as though cold", "glances around in paranoia")]. ")
if(data >= 60) // 30 units, 135 seconds
@@ -414,7 +414,7 @@
else
M.visible_message("
[M] flexes [M.p_their()] arms.")
if(prob(10))
- M.say(pick("Shit was SO cash.", "You are everything bad in the world.", "What sports do you play, other than 'jack off to naked drawn Japanese people?'", "Don’t be a stranger. Just hit me with your best shot.", "My name is John and I hate every single one of you."))
+ M.say(pick("Shit was SO cash.", "You are everything bad in the world.", "What sports do you play, other than 'jack off to naked drawn Japanese people?'", "Don’t be a stranger. Just hit me with your best shot.", "My name is John and I hate every single one of you."), forced = "spraytan")
..()
return
@@ -457,6 +457,13 @@
race = /datum/species/jelly/slime
mutationtext = "
The pain subsides. Your whole body feels like slime. "
+/datum/reagent/mutationtoxin/felinid
+ name = "Felinid Mutation Toxin"
+ id = "felinidmutationtoxin"
+ color = "#5EFF3B" //RGB: 94, 255, 59
+ race = /datum/species/human/felinid
+ mutationtext = "
The pain subsides. You feel... like a degenerate. "
+
/datum/reagent/mutationtoxin/lizard
name = "Lizard Mutation Toxin"
id = "lizardmutationtoxin"
@@ -1230,17 +1237,16 @@
/datum/reagent/stimulum/on_mob_add(mob/living/L)
..()
- L.add_trait(TRAIT_GOTTAGOFAST, id)
+ L.add_trait(TRAIT_STUNIMMUNE, id)
+ L.add_trait(TRAIT_SLEEPIMMUNE, id)
/datum/reagent/stimulum/on_mob_delete(mob/living/L)
- L.remove_trait(TRAIT_GOTTAGOFAST, id)
+ L.remove_trait(TRAIT_STUNIMMUNE, id)
+ L.remove_trait(TRAIT_SLEEPIMMUNE, id)
..()
-/datum/reagent/stimulum/on_mob_life(mob/living/carbon/M) // Has a speedup, and the anti-stun effects of nicotine.
- M.AdjustStun(-20, 0)
- M.AdjustKnockdown(-20, 0)
- M.AdjustUnconscious(-20, 0)
- M.adjustStaminaLoss(-0.5*REM, 0)
+/datum/reagent/stimulum/on_mob_life(mob/living/carbon/M)
+ M.adjustStaminaLoss(-2*REM, 0)
current_cycle++
holder.remove_reagent(id, 0.99) //Gives time for the next tick of life().
. = TRUE //Update status effects.
@@ -1643,7 +1649,7 @@
/datum/reagent/royal_bee_jelly/on_mob_life(mob/living/carbon/M)
if(prob(2))
- M.say(pick("Bzzz...","BZZ BZZ","Bzzzzzzzzzzz..."))
+ M.say(pick("Bzzz...","BZZ BZZ","Bzzzzzzzzzzz..."), forced = "royal bee jelly")
..()
//Misc reagents
@@ -1665,7 +1671,7 @@
/datum/reagent/romerol/reaction_mob(mob/living/carbon/human/H, method=TOUCH, reac_volume)
// Silently add the zombie infection organ to be activated upon death
if(!H.getorganslot(ORGAN_SLOT_ZOMBIE))
- var/obj/item/organ/zombie_infection/ZI = new()
+ var/obj/item/organ/zombie_infection/nodamage/ZI = new()
ZI.Insert(H)
..()
@@ -1829,3 +1835,15 @@
if(prob(30))
to_chat(M, "You should sit down and take a rest...")
..()
+
+/datum/reagent/tranquility
+ name = "Tranquility"
+ id = "tranquility"
+ description = "A highly mutative liquid of unknown origin."
+ color = "#9A6750" //RGB: 154, 103, 80
+ taste_description = "inner peace"
+ can_synth = FALSE
+
+/datum/reagent/tranquility/reaction_mob(mob/living/L, method=TOUCH, reac_volume, show_message = 1, touch_protection = 0)
+ if(method==PATCH || method==INGEST || method==INJECT || (method == VAPOR && prob(min(reac_volume,100)*(1 - touch_protection))))
+ L.ForceContractDisease(new /datum/disease/transformation/gondola(), FALSE, TRUE)
diff --git a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
index 0700aeb799..26edcb5b73 100644
--- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
@@ -169,6 +169,28 @@
..()
. = 1
+/datum/reagent/toxin/ghoulpowder
+ name = "Ghoul Powder"
+ id = "ghoulpowder"
+ description = "A strong neurotoxin that slows metabolism to a death-like state, while keeping the patient fully active. Causes toxin buildup if used too long."
+ reagent_state = SOLID
+ color = "#664700" // rgb: 102, 71, 0
+ toxpwr = 0.8
+ taste_description = "death"
+
+/datum/reagent/toxin/ghoulpowder/on_mob_add(mob/living/L)
+ ..()
+ L.add_trait(TRAIT_FAKEDEATH, id)
+
+/datum/reagent/toxin/ghoulpowder/on_mob_delete(mob/living/L)
+ L.remove_trait(TRAIT_FAKEDEATH, id)
+ ..()
+
+/datum/reagent/toxin/ghoulpowder/on_mob_life(mob/living/carbon/M)
+ M.adjustOxyLoss(1*REM, 0)
+ ..()
+ . = 1
+
/datum/reagent/toxin/mindbreaker
name = "Mindbreaker Toxin"
id = "mindbreaker"
@@ -405,7 +427,7 @@
/datum/reagent/toxin/formaldehyde
name = "Formaldehyde"
id = "formaldehyde"
- description = "Formaldehyde, on its own, is a fairly weak toxin. It contains trace amounts of Histamine, very rarely making it decay into Histamine.."
+ description = "Formaldehyde, on its own, is a fairly weak toxin. It contains trace amounts of Histamine, very rarely making it decay into Histamine."
reagent_state = LIQUID
color = "#B4004B"
metabolization_rate = 0.5 * REAGENTS_METABOLISM
@@ -605,7 +627,7 @@
/datum/reagent/toxin/amanitin/on_mob_delete(mob/living/M)
var/toxdamage = current_cycle*3*REM
- M.log_message("has taken [toxdamage] toxin damage from amanitin toxin", INDIVIDUAL_ATTACK_LOG)
+ M.log_message("has taken [toxdamage] toxin damage from amanitin toxin", LOG_ATTACK)
M.adjustToxLoss(toxdamage)
..()
diff --git a/code/modules/reagents/chemistry/recipes/others.dm b/code/modules/reagents/chemistry/recipes/others.dm
index 4261dcc421..f2e9baeba4 100644
--- a/code/modules/reagents/chemistry/recipes/others.dm
+++ b/code/modules/reagents/chemistry/recipes/others.dm
@@ -584,7 +584,7 @@
/datum/chemical_reaction/plastic_polymers/on_reaction(datum/reagents/holder, created_volume)
var/location = get_turf(holder.my_atom)
- for(var/i in 1 to 10)
+ for(var/i in 1 to created_volume)
new /obj/item/stack/sheet/plastic(location)
/datum/chemical_reaction/pax
diff --git a/code/modules/reagents/chemistry/recipes/pyrotechnics.dm b/code/modules/reagents/chemistry/recipes/pyrotechnics.dm
index 8b8c009a43..2bb8990eb8 100644
--- a/code/modules/reagents/chemistry/recipes/pyrotechnics.dm
+++ b/code/modules/reagents/chemistry/recipes/pyrotechnics.dm
@@ -116,6 +116,31 @@
empulse(location, round(created_volume / 12), round(created_volume / 7), 1)
holder.clear_reagents()
+
+/datum/chemical_reaction/beesplosion
+ name = "Bee Explosion"
+ id = "beesplosion"
+ required_reagents = list("honey" = 1, "strange_reagent" = 1, "radium" = 1)
+
+/datum/chemical_reaction/beesplosion/on_reaction(datum/reagents/holder, created_volume)
+ var/location = holder.my_atom.drop_location()
+ if(created_volume < 5)
+ playsound(location,'sound/effects/sparks1.ogg', 100, TRUE)
+ else
+ playsound(location,'sound/creatures/bee.ogg', 100, TRUE)
+ var/list/beeagents = list()
+ for(var/X in holder.reagent_list)
+ var/datum/reagent/R = X
+ if(required_reagents[R.id])
+ continue
+ beeagents += R
+ var/bee_amount = round(created_volume * 0.2)
+ for(var/i in 1 to bee_amount)
+ var/mob/living/simple_animal/hostile/poison/bees/short/new_bee = new(location)
+ if(LAZYLEN(beeagents))
+ new_bee.assign_reagent(pick(beeagents))
+
+
/datum/chemical_reaction/stabilizing_agent
name = "stabilizing_agent"
id = "stabilizing_agent"
diff --git a/code/modules/reagents/chemistry/recipes/slime_extracts.dm b/code/modules/reagents/chemistry/recipes/slime_extracts.dm
index c68c0a16b0..db5f7b198c 100644
--- a/code/modules/reagents/chemistry/recipes/slime_extracts.dm
+++ b/code/modules/reagents/chemistry/recipes/slime_extracts.dm
@@ -18,7 +18,7 @@
id = "m_spawn"
required_reagents = list("plasma" = 1)
required_container = /obj/item/slime_extract/grey
- required_other = 1
+ required_other = TRUE
/datum/chemical_reaction/slime/slimespawn/on_reaction(datum/reagents/holder)
var/mob/living/simple_animal/slime/S = new(get_turf(holder.my_atom), "grey")
@@ -30,7 +30,7 @@
id = "m_inaprov"
results = list("epinephrine" = 3)
required_reagents = list("water" = 5)
- required_other = 1
+ required_other = TRUE
required_container = /obj/item/slime_extract/grey
/datum/chemical_reaction/slime/slimemonkey
@@ -38,7 +38,7 @@
id = "m_monkey"
required_reagents = list("blood" = 1)
required_container = /obj/item/slime_extract/grey
- required_other = 1
+ required_other = TRUE
/datum/chemical_reaction/slime/slimemonkey/on_reaction(datum/reagents/holder)
for(var/i in 1 to 3)
@@ -51,7 +51,7 @@
id = "slimetoxin"
results = list("slime_toxin" = 1)
required_reagents = list("plasma" = 1)
- required_other = 1
+ required_other = TRUE
required_container = /obj/item/slime_extract/green
/datum/chemical_reaction/slime/slimehuman
@@ -59,7 +59,7 @@
id = "humanmuttoxin"
results = list("stablemutationtoxin" = 1)
required_reagents = list("blood" = 1)
- required_other = 1
+ required_other = TRUE
required_container = /obj/item/slime_extract/green
/datum/chemical_reaction/slime/slimelizard
@@ -67,7 +67,7 @@
id = "lizardmuttoxin"
results = list("lizardmutationtoxin" = 1)
required_reagents = list("radium" = 1)
- required_other = 1
+ required_other = TRUE
required_container = /obj/item/slime_extract/green
//Metal
@@ -76,7 +76,7 @@
id = "m_metal"
required_reagents = list("plasma" = 1)
required_container = /obj/item/slime_extract/metal
- required_other = 1
+ required_other = TRUE
/datum/chemical_reaction/slime/slimemetal/on_reaction(datum/reagents/holder)
var/turf/location = get_turf(holder.my_atom)
@@ -89,7 +89,7 @@
id = "m_glass"
required_reagents = list("water" = 1)
required_container = /obj/item/slime_extract/metal
- required_other = 1
+ required_other = TRUE
/datum/chemical_reaction/slime/slimeglass/on_reaction(datum/reagents/holder)
var/turf/location = get_turf(holder.my_atom)
@@ -103,7 +103,7 @@
id = "m_tele"
required_reagents = list("plasma" = 1)
required_container = /obj/item/slime_extract/gold
- required_other = 1
+ required_other = TRUE
deletes_extract = FALSE //we do delete, but we don't do so instantly
/datum/chemical_reaction/slime/slimemobspawn/on_reaction(datum/reagents/holder)
@@ -142,7 +142,7 @@
id = "m_tele2"
required_reagents = list("plasma" = 1)
required_container = /obj/item/slime_extract/silver
- required_other = 1
+ required_other = TRUE
/datum/chemical_reaction/slime/slimebork/on_reaction(datum/reagents/holder)
//BORK BORK BORK
@@ -184,14 +184,14 @@
results = list("frostoil" = 10)
required_reagents = list("plasma" = 1)
required_container = /obj/item/slime_extract/blue
- required_other = 1
+ required_other = TRUE
/datum/chemical_reaction/slime/slimestabilizer
name = "Slime Stabilizer"
id = "m_slimestabilizer"
required_reagents = list("blood" = 1)
required_container = /obj/item/slime_extract/blue
- required_other = 1
+ required_other = TRUE
/datum/chemical_reaction/slime/slimestabilizer/on_reaction(datum/reagents/holder)
new /obj/item/slimepotion/slime/stabilizer(get_turf(holder.my_atom))
@@ -203,7 +203,7 @@
results = list("fluorosurfactant" = 20, "water" = 20)
required_reagents = list("water" = 5)
required_container = /obj/item/slime_extract/blue
- required_other = 1
+ required_other = TRUE
//Dark Blue
/datum/chemical_reaction/slime/slimefreeze
@@ -211,7 +211,7 @@
id = "m_freeze"
required_reagents = list("plasma" = 1)
required_container = /obj/item/slime_extract/darkblue
- required_other = 1
+ required_other = TRUE
deletes_extract = FALSE
/datum/chemical_reaction/slime/slimefreeze/on_reaction(datum/reagents/holder)
@@ -234,7 +234,7 @@
id = "m_fireproof"
required_reagents = list("water" = 1)
required_container = /obj/item/slime_extract/darkblue
- required_other = 1
+ required_other = TRUE
/datum/chemical_reaction/slime/slimefireproof/on_reaction(datum/reagents/holder)
new /obj/item/slimepotion/fireproof(get_turf(holder.my_atom))
@@ -247,14 +247,14 @@
results = list("capsaicin" = 10)
required_reagents = list("blood" = 1)
required_container = /obj/item/slime_extract/orange
- required_other = 1
+ required_other = TRUE
/datum/chemical_reaction/slime/slimefire
name = "Slime fire"
id = "m_fire"
required_reagents = list("plasma" = 1)
required_container = /obj/item/slime_extract/orange
- required_other = 1
+ required_other = TRUE
deletes_extract = FALSE
/datum/chemical_reaction/slime/slimefire/on_reaction(datum/reagents/holder)
@@ -279,7 +279,7 @@
results = list("phosphorus" = 10, "potassium" = 10, "sugar" = 10)
required_reagents = list("water" = 5)
required_container = /obj/item/slime_extract/orange
- required_other = 1
+ required_other = TRUE
//Yellow
/datum/chemical_reaction/slime/slimeoverload
@@ -287,7 +287,7 @@
id = "m_emp"
required_reagents = list("blood" = 1)
required_container = /obj/item/slime_extract/yellow
- required_other = 1
+ required_other = TRUE
/datum/chemical_reaction/slime/slimeoverload/on_reaction(datum/reagents/holder, created_volume)
empulse(get_turf(holder.my_atom), 3, 7)
@@ -298,7 +298,7 @@
id = "m_cell"
required_reagents = list("plasma" = 1)
required_container = /obj/item/slime_extract/yellow
- required_other = 1
+ required_other = TRUE
/datum/chemical_reaction/slime/slimecell/on_reaction(datum/reagents/holder, created_volume)
new /obj/item/stock_parts/cell/high/slime(get_turf(holder.my_atom))
@@ -309,7 +309,7 @@
id = "m_glow"
required_reagents = list("water" = 1)
required_container = /obj/item/slime_extract/yellow
- required_other = 1
+ required_other = TRUE
/datum/chemical_reaction/slime/slimeglow/on_reaction(datum/reagents/holder)
var/turf/T = get_turf(holder.my_atom)
@@ -323,7 +323,7 @@
id = "m_steroid"
required_reagents = list("plasma" = 1)
required_container = /obj/item/slime_extract/purple
- required_other = 1
+ required_other = TRUE
/datum/chemical_reaction/slime/slimepsteroid/on_reaction(datum/reagents/holder)
new /obj/item/slimepotion/slime/steroid(get_turf(holder.my_atom))
@@ -335,7 +335,7 @@
results = list("regen_jelly" = 5)
required_reagents = list("blood" = 1)
required_container = /obj/item/slime_extract/purple
- required_other = 1
+ required_other = TRUE
//Dark Purple
/datum/chemical_reaction/slime/slimeplasma
@@ -343,7 +343,7 @@
id = "m_plasma"
required_reagents = list("plasma" = 1)
required_container = /obj/item/slime_extract/darkpurple
- required_other = 1
+ required_other = TRUE
/datum/chemical_reaction/slime/slimeplasma/on_reaction(datum/reagents/holder)
new /obj/item/stack/sheet/mineral/plasma(get_turf(holder.my_atom), 3)
@@ -355,7 +355,7 @@
id = "m_slimemutator"
required_reagents = list("plasma" = 1)
required_container = /obj/item/slime_extract/red
- required_other = 1
+ required_other = TRUE
/datum/chemical_reaction/slime/slimemutator/on_reaction(datum/reagents/holder)
new /obj/item/slimepotion/slime/mutator(get_turf(holder.my_atom))
@@ -366,7 +366,7 @@
id = "m_bloodlust"
required_reagents = list("blood" = 1)
required_container = /obj/item/slime_extract/red
- required_other = 1
+ required_other = TRUE
/datum/chemical_reaction/slime/slimebloodlust/on_reaction(datum/reagents/holder)
for(var/mob/living/simple_animal/slime/slime in viewers(get_turf(holder.my_atom), null))
@@ -379,7 +379,7 @@
id = "m_speed"
required_reagents = list("water" = 1)
required_container = /obj/item/slime_extract/red
- required_other = 1
+ required_other = TRUE
/datum/chemical_reaction/slime/slimespeed/on_reaction(datum/reagents/holder)
new /obj/item/slimepotion/speed(get_turf(holder.my_atom))
@@ -391,7 +391,7 @@
id = "m_potion"
required_reagents = list("plasma" = 1)
required_container = /obj/item/slime_extract/pink
- required_other = 1
+ required_other = TRUE
/datum/chemical_reaction/slime/docility/on_reaction(datum/reagents/holder)
new /obj/item/slimepotion/slime/docility(get_turf(holder.my_atom))
@@ -402,7 +402,7 @@
id = "m_gender"
required_reagents = list("blood" = 1)
required_container = /obj/item/slime_extract/pink
- required_other = 1
+ required_other = TRUE
/datum/chemical_reaction/slime/gender/on_reaction(datum/reagents/holder)
new /obj/item/slimepotion/genderchange(get_turf(holder.my_atom))
@@ -414,7 +414,7 @@
id = "mutationtoxin2"
results = list("amutationtoxin" = 1)
required_reagents = list("plasma" = 1)
- required_other = 1
+ required_other = TRUE
required_container = /obj/item/slime_extract/black
//Oil
@@ -423,7 +423,7 @@
id = "m_explosion"
required_reagents = list("plasma" = 1)
required_container = /obj/item/slime_extract/oil
- required_other = 1
+ required_other = TRUE
deletes_extract = FALSE
/datum/chemical_reaction/slime/slimeexplosion/on_reaction(datum/reagents/holder)
@@ -453,7 +453,7 @@
results = list("cornoil" = 10)
required_reagents = list("blood" = 1)
required_container = /obj/item/slime_extract/oil
- required_other = 1
+ required_other = TRUE
//Light Pink
/datum/chemical_reaction/slime/slimepotion2
@@ -461,19 +461,31 @@
id = "m_potion2"
required_container = /obj/item/slime_extract/lightpink
required_reagents = list("plasma" = 1)
- required_other = 1
+ required_other = TRUE
/datum/chemical_reaction/slime/slimepotion2/on_reaction(datum/reagents/holder)
new /obj/item/slimepotion/slime/sentience(get_turf(holder.my_atom))
..()
+/datum/chemical_reaction/slime/renaming
+ name = "Renaming Potion"
+ id = "m_renaming_potion"
+ required_container = /obj/item/slime_extract/lightpink
+ required_reagents = list("water" = 1)
+ required_other = TRUE
+
+/datum/chemical_reaction/slime/renaming/on_reaction(datum/reagents/holder)
+ new /obj/item/slimepotion/slime/renaming(holder.my_atom.drop_location())
+ ..()
+
+
//Adamantine
/datum/chemical_reaction/slime/adamantine
name = "Adamantine"
id = "adamantine"
required_reagents = list("plasma" = 1)
required_container = /obj/item/slime_extract/adamantine
- required_other = 1
+ required_other = TRUE
/datum/chemical_reaction/slime/adamantine/on_reaction(datum/reagents/holder)
new /obj/item/stack/sheet/mineral/adamantine(get_turf(holder.my_atom))
@@ -485,7 +497,7 @@
id = "m_floor2"
required_reagents = list("blood" = 1)
required_container = /obj/item/slime_extract/bluespace
- required_other = 1
+ required_other = TRUE
/datum/chemical_reaction/slime/slimefloor2/on_reaction(datum/reagents/holder, created_volume)
new /obj/item/stack/tile/bluespace(get_turf(holder.my_atom), 25)
@@ -497,7 +509,7 @@
id = "m_crystal"
required_reagents = list("plasma" = 1)
required_container = /obj/item/slime_extract/bluespace
- required_other = 1
+ required_other = TRUE
/datum/chemical_reaction/slime/slimecrystal/on_reaction(datum/reagents/holder, created_volume)
var/obj/item/stack/ore/bluespace_crystal/BC = new (get_turf(holder.my_atom))
@@ -509,7 +521,7 @@
id = "m_radio"
required_reagents = list("water" = 1)
required_container = /obj/item/slime_extract/bluespace
- required_other = 1
+ required_other = TRUE
/datum/chemical_reaction/slime/slimeradio/on_reaction(datum/reagents/holder, created_volume)
new /obj/item/slimepotion/slime/slimeradio(get_turf(holder.my_atom))
@@ -521,7 +533,7 @@
id = "m_steroid2"
required_reagents = list("plasma" = 1)
required_container = /obj/item/slime_extract/cerulean
- required_other = 1
+ required_other = TRUE
/datum/chemical_reaction/slime/slimepsteroid2/on_reaction(datum/reagents/holder)
new /obj/item/slimepotion/enhancer(get_turf(holder.my_atom))
@@ -532,7 +544,7 @@
id = "s_territory"
required_reagents = list("blood" = 1)
required_container = /obj/item/slime_extract/cerulean
- required_other = 1
+ required_other = TRUE
/datum/chemical_reaction/slime/slime_territory/on_reaction(datum/reagents/holder)
new /obj/item/areaeditor/blueprints/slime(get_turf(holder.my_atom))
@@ -544,7 +556,7 @@
id = "m_stop"
required_reagents = list("plasma" = 1)
required_container = /obj/item/slime_extract/sepia
- required_other = 1
+ required_other = TRUE
/datum/chemical_reaction/slime/slimestop/on_reaction(datum/reagents/holder)
var/turf/T = get_turf(holder.my_atom)
@@ -557,7 +569,7 @@
id = "m_camera"
required_reagents = list("water" = 1)
required_container = /obj/item/slime_extract/sepia
- required_other = 1
+ required_other = TRUE
/datum/chemical_reaction/slime/slimecamera/on_reaction(datum/reagents/holder)
new /obj/item/camera(get_turf(holder.my_atom))
@@ -569,7 +581,7 @@
id = "m_floor"
required_reagents = list("blood" = 1)
required_container = /obj/item/slime_extract/sepia
- required_other = 1
+ required_other = TRUE
/datum/chemical_reaction/slime/slimefloor/on_reaction(datum/reagents/holder)
new /obj/item/stack/tile/sepia(get_turf(holder.my_atom), 25)
@@ -581,7 +593,7 @@
id = "s_paint"
required_reagents = list("plasma" = 1)
required_container = /obj/item/slime_extract/pyrite
- required_other = 1
+ required_other = TRUE
/datum/chemical_reaction/slime/slimepaint/on_reaction(datum/reagents/holder)
var/chosen = pick(subtypesof(/obj/item/paint))
@@ -593,7 +605,7 @@
id = "s_crayon"
required_reagents = list("blood" = 1)
required_container = /obj/item/slime_extract/pyrite
- required_other = 1
+ required_other = TRUE
/datum/chemical_reaction/slime/slimecrayon/on_reaction(datum/reagents/holder)
var/chosen = pick(difflist(subtypesof(/obj/item/toy/crayon),typesof(/obj/item/toy/crayon/spraycan)))
@@ -605,7 +617,7 @@
name = "Random Core"
id = "slimerng"
required_reagents = list("plasma" = 1)
- required_other = 1
+ required_other = TRUE
required_container = /obj/item/slime_extract/rainbow
/datum/chemical_reaction/slime/slimeRNG/on_reaction(datum/reagents/holder, created_volume)
@@ -625,7 +637,7 @@
name = "Clusterblorble"
id = "slimebomb"
required_reagents = list("slimejelly" = 1)
- required_other = 1
+ required_other = TRUE
required_container = /obj/item/slime_extract/rainbow
/datum/chemical_reaction/slime/slimebomb/on_reaction(datum/reagents/holder, created_volume)
@@ -641,7 +653,7 @@
name = "Transfer Potion"
id = "slimetransfer"
required_reagents = list("blood" = 1)
- required_other = 1
+ required_other = TRUE
required_container = /obj/item/slime_extract/rainbow
/datum/chemical_reaction/slime/slime_transfer/on_reaction(datum/reagents/holder)
@@ -652,7 +664,7 @@
name = "Flight Potion"
id = "flightpotion"
required_reagents = list("holywater" = 5, "uranium" = 5)
- required_other = 1
+ required_other = TRUE
required_container = /obj/item/slime_extract/rainbow
/datum/chemical_reaction/slime/flight_potion/on_reaction(datum/reagents/holder)
diff --git a/code/modules/reagents/chemistry/recipes/toxins.dm b/code/modules/reagents/chemistry/recipes/toxins.dm
index ee31fb1a93..22e21b1db0 100644
--- a/code/modules/reagents/chemistry/recipes/toxins.dm
+++ b/code/modules/reagents/chemistry/recipes/toxins.dm
@@ -74,6 +74,12 @@
id = "zombiepowder"
results = list("zombiepowder" = 2)
required_reagents = list("carpotoxin" = 5, "morphine" = 5, "copper" = 5)
+
+/datum/chemical_reaction/ghoulpowder
+ name = "Ghoul Powder"
+ id = "ghoulpowder"
+ results = list("ghoulpowder" = 2)
+ required_reagents = list("zombiepowder" = 1, "epinephrine" = 1)
/datum/chemical_reaction/mindbreaker
name = "Mindbreaker Toxin"
diff --git a/code/modules/reagents/reagent_containers.dm b/code/modules/reagents/reagent_containers.dm
index 4a40addc5d..85868f1895 100644
--- a/code/modules/reagents/reagent_containers.dm
+++ b/code/modules/reagents/reagent_containers.dm
@@ -45,9 +45,6 @@
if(user.a_intent == INTENT_HARM)
return ..()
-/obj/item/reagent_containers/afterattack(obj/target, mob/user , flag)
- return
-
/obj/item/reagent_containers/proc/canconsume(mob/eater, mob/user)
if(!iscarbon(eater))
return 0
@@ -101,7 +98,7 @@
R += num2text(A.volume) + "),"
if(thrownby)
- add_logs(thrownby, M, "splashed", R)
+ log_combat(thrownby, M, "splashed", R)
reagents.reaction(target, TOUCH)
else if(bartender_check(target) && thrown)
@@ -110,7 +107,7 @@
else
if(isturf(target) && reagents.reagent_list.len && thrownby)
- add_logs(thrownby, target, "splashed (thrown) [english_list(reagents.reagent_list)]", "in [AREACOORD(target)]")
+ log_combat(thrownby, target, "splashed (thrown) [english_list(reagents.reagent_list)]", "in [AREACOORD(target)]")
log_game("[key_name(thrownby)] splashed (thrown) [english_list(reagents.reagent_list)] on [target] in [AREACOORD(target)].")
message_admins("[ADMIN_LOOKUPFLW(thrownby)] splashed (thrown) [english_list(reagents.reagent_list)] on [target] in [ADMIN_VERBOSEJMP(target)].")
visible_message("
[src] spills its contents all over [target]. ")
diff --git a/code/modules/reagents/reagent_containers/borghydro.dm b/code/modules/reagents/reagent_containers/borghydro.dm
index 911571a3bd..631e5ee281 100644
--- a/code/modules/reagents/reagent_containers/borghydro.dm
+++ b/code/modules/reagents/reagent_containers/borghydro.dm
@@ -111,7 +111,7 @@ Borg Hypospray
var/list/injected = list()
for(var/datum/reagent/RG in R.reagent_list)
injected += RG.name
- add_logs(user, M, "injected", src, "(CHEMICALS: [english_list(injected)])")
+ log_combat(user, M, "injected", src, "(CHEMICALS: [english_list(injected)])")
/obj/item/reagent_containers/borghypo/attack_self(mob/user)
var/chosen_reagent = modes[input(user, "What reagent do you want to dispense?") as null|anything in reagent_ids]
@@ -197,6 +197,7 @@ Borg Shaker
RG.add_reagent(reagent_ids[valueofi], 5)
/obj/item/reagent_containers/borghypo/borgshaker/afterattack(obj/target, mob/user, proximity)
+ . = ..()
if(!proximity)
return
@@ -251,6 +252,6 @@ Borg Shaker
/obj/item/reagent_containers/borghypo/epi
name = "epinephrine injector"
- desc = "An advanced chemical synthesizer and injection system, designed to stabilize patients.."
+ desc = "An advanced chemical synthesizer and injection system, designed to stabilize patients."
reagent_ids = list("epinephrine")
accepts_reagent_upgrades = FALSE
diff --git a/code/modules/reagents/reagent_containers/bottle.dm b/code/modules/reagents/reagent_containers/bottle.dm
index 4d737657f8..66befb1bb6 100644
--- a/code/modules/reagents/reagent_containers/bottle.dm
+++ b/code/modules/reagents/reagent_containers/bottle.dm
@@ -302,3 +302,105 @@
name = "BVAK bottle"
desc = "A small bottle containing Bio Virus Antidote Kit."
list_reagents = list("atropine" = 5, "epinephrine" = 5, "salbutamol" = 10, "spaceacillin" = 10)
+
+//Oldstation.dmm chemical storage bottles
+
+/obj/item/reagent_containers/glass/bottle/hydrogen
+ name = "hydrogen bottle"
+ list_reagents = list("hydrogen" = 30)
+
+/obj/item/reagent_containers/glass/bottle/lithium
+ name = "lithium bottle"
+ list_reagents = list("lithium" = 30)
+
+/obj/item/reagent_containers/glass/bottle/carbon
+ name = "carbon bottle"
+ list_reagents = list("carbon" = 30)
+
+/obj/item/reagent_containers/glass/bottle/nitrogen
+ name = "nitrogen bottle"
+ list_reagents = list("nitrogen" = 30)
+
+/obj/item/reagent_containers/glass/bottle/oxygen
+ name = "oxygen bottle"
+ list_reagents = list("oxygen" = 30)
+
+/obj/item/reagent_containers/glass/bottle/fluorine
+ name = "fluorine bottle"
+ list_reagents = list("fluorine" = 30)
+
+/obj/item/reagent_containers/glass/bottle/sodium
+ name = "sodium bottle"
+ list_reagents = list("sodium" = 30)
+
+/obj/item/reagent_containers/glass/bottle/aluminium
+ name = "aluminium bottle"
+ list_reagents = list("aluminium" = 30)
+
+/obj/item/reagent_containers/glass/bottle/silicon
+ name = "silicon bottle"
+ list_reagents = list("silicon" = 30)
+
+/obj/item/reagent_containers/glass/bottle/phosphorus
+ name = "phosphorus bottle"
+ list_reagents = list("phosphorus" = 30)
+
+/obj/item/reagent_containers/glass/bottle/sulfur
+ name = "sulfur bottle"
+ list_reagents = list("sulfur" = 30)
+
+/obj/item/reagent_containers/glass/bottle/chlorine
+ name = "chlorine bottle"
+ list_reagents = list("chlorine" = 30)
+
+/obj/item/reagent_containers/glass/bottle/potassium
+ name = "potassium bottle"
+ list_reagents = list("potassium" = 30)
+
+/obj/item/reagent_containers/glass/bottle/iron
+ name = "iron bottle"
+ list_reagents = list("iron" = 30)
+
+/obj/item/reagent_containers/glass/bottle/copper
+ name = "copper bottle"
+ list_reagents = list("copper" = 30)
+
+/obj/item/reagent_containers/glass/bottle/mercury
+ name = "mercury bottle"
+ list_reagents = list("mercury" = 30)
+
+/obj/item/reagent_containers/glass/bottle/radium
+ name = "radium bottle"
+ list_reagents = list("radium" = 30)
+
+/obj/item/reagent_containers/glass/bottle/water
+ name = "water bottle"
+ list_reagents = list("water" = 30)
+
+/obj/item/reagent_containers/glass/bottle/ethanol
+ name = "ethanol bottle"
+ list_reagents = list("ethanol" = 30)
+
+/obj/item/reagent_containers/glass/bottle/sugar
+ name = "sugar bottle"
+ list_reagents = list("sugar" = 30)
+
+/obj/item/reagent_containers/glass/bottle/sacid
+ name = "sulphuric acid bottle"
+ list_reagents = list("sacid" = 30)
+
+/obj/item/reagent_containers/glass/bottle/welding_fuel
+ name = "welding fuel bottle"
+ list_reagents = list("welding_fuel" = 30)
+
+/obj/item/reagent_containers/glass/bottle/silver
+ name = "silver bottle"
+ list_reagents = list("silver" = 30)
+
+/obj/item/reagent_containers/glass/bottle/iodine
+ name = "iodine bottle"
+ list_reagents = list("iodine" = 30)
+
+/obj/item/reagent_containers/glass/bottle/bromine
+ name = "bromine bottle"
+ list_reagents = list("bromine" = 30)
diff --git a/code/modules/reagents/reagent_containers/dropper.dm b/code/modules/reagents/reagent_containers/dropper.dm
index 144db32a6c..5907c9e534 100644
--- a/code/modules/reagents/reagent_containers/dropper.dm
+++ b/code/modules/reagents/reagent_containers/dropper.dm
@@ -9,6 +9,7 @@
container_type = TRANSPARENT
/obj/item/reagent_containers/dropper/afterattack(obj/target, mob/user , proximity)
+ . = ..()
if(!proximity)
return
if(!target.reagents)
@@ -68,7 +69,7 @@
for(var/datum/reagent/A in src.reagents.reagent_list)
R += A.id + " ("
R += num2text(A.volume) + "),"
- add_logs(user, M, "squirted", R)
+ log_combat(user, M, "squirted", R)
trans = src.reagents.trans_to(target, amount_per_transfer_from_this)
to_chat(user, "
You transfer [trans] unit\s of the solution. ")
diff --git a/code/modules/reagents/reagent_containers/glass.dm b/code/modules/reagents/reagent_containers/glass.dm
index 596973a2c4..1912e9fe9d 100644
--- a/code/modules/reagents/reagent_containers/glass.dm
+++ b/code/modules/reagents/reagent_containers/glass.dm
@@ -29,11 +29,10 @@
R += A.id + " ("
R += num2text(A.volume) + "),"
if(isturf(target) && reagents.reagent_list.len && thrownby)
- add_logs(thrownby, target, "splashed (thrown) [english_list(reagents.reagent_list)]", "in [AREACOORD(target)]")
- log_game("[key_name(thrownby)] splashed (thrown) [english_list(reagents.reagent_list)] on [target] in [AREACOORD(target)].")
+ log_combat(thrownby, target, "splashed (thrown) [english_list(reagents.reagent_list)]")
message_admins("[ADMIN_LOOKUPFLW(thrownby)] splashed (thrown) [english_list(reagents.reagent_list)] on [target] at [ADMIN_VERBOSEJMP(target)].")
reagents.reaction(M, TOUCH)
- add_logs(user, M, "splashed", R)
+ log_combat(user, M, "splashed", R)
reagents.clear_reagents()
else
if(M != user)
@@ -44,7 +43,7 @@
if(!reagents || !reagents.total_volume)
return // The drink might be empty after the delay, such as by spam-feeding
M.visible_message("
[user] feeds something to [M]. ", "
[user] feeds something to you. ")
- add_logs(user, M, "fed", reagents.log_list())
+ log_combat(user, M, "fed", reagents.log_list())
else
to_chat(user, "
You swallow a gulp of [src]. ")
var/fraction = min(5/reagents.total_volume, 1)
@@ -53,6 +52,7 @@
playsound(M.loc,'sound/items/drink.ogg', rand(10,50), 1)
/obj/item/reagent_containers/glass/afterattack(obj/target, mob/user, proximity)
+ . = ..()
if((!proximity) || !check_allowed_items(target,target_self=1))
return
@@ -172,7 +172,7 @@
materials = list(MAT_GLASS=2500, MAT_PLASTIC=3000)
volume = 120
amount_per_transfer_from_this = 10
- possible_transfer_amounts = list(10,15,20,25,30,60,120)
+ possible_transfer_amounts = list(5,10,15,20,25,30,60,120)
/obj/item/reagent_containers/glass/beaker/plastic/update_icon()
icon_state = "beakerlarge" // hack to lets us reuse the large beaker reagent fill states
@@ -186,7 +186,7 @@
materials = list(MAT_GLASS=2500, MAT_PLASTIC=3000, MAT_GOLD=1000, MAT_TITANIUM=1000)
volume = 180
amount_per_transfer_from_this = 10
- possible_transfer_amounts = list(10,15,20,25,30,60,120,180)
+ possible_transfer_amounts = list(5,10,15,20,25,30,60,120,180)
/obj/item/reagent_containers/glass/beaker/noreact
name = "cryostasis beaker"
@@ -251,7 +251,7 @@
materials = list(MAT_METAL=200)
w_class = WEIGHT_CLASS_NORMAL
amount_per_transfer_from_this = 20
- possible_transfer_amounts = list(10,15,20,25,30,50,70)
+ possible_transfer_amounts = list(5,10,15,20,25,30,50,70)
volume = 70
flags_inv = HIDEHAIR
slot_flags = ITEM_SLOT_HEAD
@@ -330,103 +330,3 @@
/obj/item/reagent_containers/glass/beaker/waterbottle/large/empty
list_reagents = list()
-
-/obj/item/reagent_containers/glass/beaker/large/hydrogen
- name = "hydrogen beaker"
- list_reagents = list("hydrogen" = 50)
-
-/obj/item/reagent_containers/glass/beaker/large/lithium
- name = "lithium beaker"
- list_reagents = list("lithium" = 50)
-
-/obj/item/reagent_containers/glass/beaker/large/carbon
- name = "carbon beaker"
- list_reagents = list("carbon" = 50)
-
-/obj/item/reagent_containers/glass/beaker/large/nitrogen
- name = "nitrogen beaker"
- list_reagents = list("nitrogen" = 50)
-
-/obj/item/reagent_containers/glass/beaker/large/oxygen
- name = "oxygen beaker"
- list_reagents = list("oxygen" = 50)
-
-/obj/item/reagent_containers/glass/beaker/large/fluorine
- name = "fluorine beaker"
- list_reagents = list("fluorine" = 50)
-
-/obj/item/reagent_containers/glass/beaker/large/sodium
- name = "sodium beaker"
- list_reagents = list("sodium" = 50)
-
-/obj/item/reagent_containers/glass/beaker/large/aluminium
- name = "aluminium beaker"
- list_reagents = list("aluminium" = 50)
-
-/obj/item/reagent_containers/glass/beaker/large/silicon
- name = "silicon beaker"
- list_reagents = list("silicon" = 50)
-
-/obj/item/reagent_containers/glass/beaker/large/phosphorus
- name = "phosphorus beaker"
- list_reagents = list("phosphorus" = 50)
-
-/obj/item/reagent_containers/glass/beaker/large/sulfur
- name = "sulfur beaker"
- list_reagents = list("sulfur" = 50)
-
-/obj/item/reagent_containers/glass/beaker/large/chlorine
- name = "chlorine beaker"
- list_reagents = list("chlorine" = 50)
-
-/obj/item/reagent_containers/glass/beaker/large/potassium
- name = "potassium beaker"
- list_reagents = list("potassium" = 50)
-
-/obj/item/reagent_containers/glass/beaker/large/iron
- name = "iron beaker"
- list_reagents = list("iron" = 50)
-
-/obj/item/reagent_containers/glass/beaker/large/copper
- name = "copper beaker"
- list_reagents = list("copper" = 50)
-
-/obj/item/reagent_containers/glass/beaker/large/mercury
- name = "mercury beaker"
- list_reagents = list("mercury" = 50)
-
-/obj/item/reagent_containers/glass/beaker/large/radium
- name = "radium beaker"
- list_reagents = list("radium" = 50)
-
-/obj/item/reagent_containers/glass/beaker/large/water
- name = "water beaker"
- list_reagents = list("water" = 50)
-
-/obj/item/reagent_containers/glass/beaker/large/ethanol
- name = "ethanol beaker"
- list_reagents = list("ethanol" = 50)
-
-/obj/item/reagent_containers/glass/beaker/large/sugar
- name = "sugar beaker"
- list_reagents = list("sugar" = 50)
-
-/obj/item/reagent_containers/glass/beaker/large/sacid
- name = "sulphuric acid beaker"
- list_reagents = list("sacid" = 50)
-
-/obj/item/reagent_containers/glass/beaker/large/welding_fuel
- name = "welding fuel beaker"
- list_reagents = list("welding_fuel" = 50)
-
-/obj/item/reagent_containers/glass/beaker/large/silver
- name = "silver beaker"
- list_reagents = list("silver" = 50)
-
-/obj/item/reagent_containers/glass/beaker/large/iodine
- name = "iodine beaker"
- list_reagents = list("iodine" = 50)
-
-/obj/item/reagent_containers/glass/beaker/large/bromine
- name = "bromine beaker"
- list_reagents = list("bromine" = 50)
diff --git a/code/modules/reagents/reagent_containers/hypospray.dm b/code/modules/reagents/reagent_containers/hypospray.dm
index b03dcedd1a..1fa8b408cd 100644
--- a/code/modules/reagents/reagent_containers/hypospray.dm
+++ b/code/modules/reagents/reagent_containers/hypospray.dm
@@ -30,7 +30,7 @@
for(var/datum/reagent/R in reagents.reagent_list)
injected += R.name
var/contained = english_list(injected)
- add_logs(user, M, "attempted to inject", src, "([contained])")
+ log_combat(user, M, "attempted to inject", src, "([contained])")
if(reagents.total_volume && (ignore_flags || M.can_inject(user, 1))) // Ignore flag should be checked first or there will be an error message.
to_chat(M, "
You feel a tiny prick! ")
@@ -48,7 +48,7 @@
to_chat(user, "
[trans] unit\s injected. [reagents.total_volume] unit\s remaining in [src]. ")
- add_logs(user, M, "injected", src, "([contained])")
+ log_combat(user, M, "injected", src, "([contained])")
/obj/item/reagent_containers/hypospray/CMO
list_reagents = list("omnizine" = 30)
diff --git a/code/modules/reagents/reagent_containers/medspray.dm b/code/modules/reagents/reagent_containers/medspray.dm
index fdb6a60894..8631c14ac0 100644
--- a/code/modules/reagents/reagent_containers/medspray.dm
+++ b/code/modules/reagents/reagent_containers/medspray.dm
@@ -46,7 +46,7 @@
to_chat(M, "
You [apply_method] yourself with [src]. ")
else
- add_logs(user, M, "attempted to apply", src, reagents.log_list())
+ log_combat(user, M, "attempted to apply", src, reagents.log_list())
M.visible_message("
[user] attempts to [apply_method] [src] on [M]. ", \
"
[user] attempts to [apply_method] [src] on [M]. ")
if(!do_mob(user, M))
@@ -60,7 +60,7 @@
return
else
- add_logs(user, M, "applied", src, reagents.log_list())
+ log_combat(user, M, "applied", src, reagents.log_list())
playsound(src, 'sound/effects/spray2.ogg', 50, 1, -6)
var/fraction = min(amount_per_transfer_from_this/reagents.total_volume, 1)
reagents.reaction(M, apply_type, fraction)
diff --git a/code/modules/reagents/reagent_containers/patch.dm b/code/modules/reagents/reagent_containers/patch.dm
index ac064c9c15..b11ef878b6 100644
--- a/code/modules/reagents/reagent_containers/patch.dm
+++ b/code/modules/reagents/reagent_containers/patch.dm
@@ -9,9 +9,7 @@
apply_type = PATCH
apply_method = "apply"
self_delay = 30 // three seconds
-
-/obj/item/reagent_containers/pill/patch/afterattack(obj/target, mob/user , proximity)
- return // thanks inheritance again
+ dissolvable = FALSE
/obj/item/reagent_containers/pill/patch/attack(mob/living/L, mob/user)
if(ishuman(L))
diff --git a/code/modules/reagents/reagent_containers/pill.dm b/code/modules/reagents/reagent_containers/pill.dm
index d2f840cda3..8941724274 100644
--- a/code/modules/reagents/reagent_containers/pill.dm
+++ b/code/modules/reagents/reagent_containers/pill.dm
@@ -13,6 +13,7 @@
var/apply_method = "swallow"
var/roundstart = 0
var/self_delay = 0 //pills are instant, this is because patches inheret their aplication from pills
+ var/dissolvable = TRUE
/obj/item/reagent_containers/pill/Initialize()
. = ..()
@@ -46,7 +47,7 @@
"
[user] forces [M] to [apply_method] [src]. ")
- add_logs(user, M, "fed", reagents.log_list())
+ log_combat(user, M, "fed", reagents.log_list())
if(reagents.total_volume)
reagents.reaction(M, apply_type)
reagents.trans_to(M, reagents.total_volume)
@@ -55,22 +56,24 @@
/obj/item/reagent_containers/pill/afterattack(obj/target, mob/user , proximity)
+ . = ..()
if(!proximity)
return
- if(target.is_refillable())
- if(target.is_drainable() && !target.reagents.total_volume)
- to_chat(user, "
[target] is empty! There's nothing to dissolve [src] in. ")
- return
+ if(!dissolvable || !target.is_refillable())
+ return
+ if(target.is_drainable() && !target.reagents.total_volume)
+ to_chat(user, "
[target] is empty! There's nothing to dissolve [src] in. ")
+ return
- if(target.reagents.holder_full())
- to_chat(user, "
[target] is full. ")
- return
+ if(target.reagents.holder_full())
+ to_chat(user, "
[target] is full. ")
+ return
- to_chat(user, "
You dissolve [src] in [target]. ")
- for(var/mob/O in viewers(2, user)) //viewers is necessary here because of the small radius
- to_chat(O, "
[user] slips something into [target]! ")
- reagents.trans_to(target, reagents.total_volume)
- qdel(src)
+ to_chat(user, "
You dissolve [src] in [target]. ")
+ for(var/mob/O in viewers(2, user)) //viewers is necessary here because of the small radius
+ to_chat(O, "
[user] slips something into [target]! ")
+ reagents.trans_to(target, reagents.total_volume)
+ qdel(src)
/obj/item/reagent_containers/pill/tox
name = "toxins pill"
@@ -151,7 +154,7 @@
icon_state = "pill18"
list_reagents = list("insulin" = 50)
roundstart = 1
-///////////////////////////////////////// this pill is used only in a legion mob drop
+///////////////////////////////////////// this pill is used only in a legion mob drop
/obj/item/reagent_containers/pill/shadowtoxin
name = "black pill"
desc = "I wouldn't eat this if I were you."
@@ -178,5 +181,18 @@
name = "speedy pill"
list_reagents = list("aranesp" = 10)
+/obj/item/reagent_containers/pill/floorpill
+ name = "floorpill"
+ desc = "A strange pill found in the depths of maintenance"
+ icon_state = "pill21"
+ var/static/list/names = list("maintenance pill","floorpill","mystery pill","suspicious pill","strange pill")
+ var/static/list/descs = list("Your feeling is telling you no, but...","Drugs are expensive, you can't afford not to eat any pills that you find."\
+ , "Surely, there's no way this could go bad.")
+/obj/item/reagent_containers/pill/floorpill/Initialize()
+ list_reagents = list(get_random_reagent_id() = rand(10,50))
+ . = ..()
+ name = pick(names)
+ if(prob(20))
+ desc = pick(descs)
diff --git a/code/modules/reagents/reagent_containers/spray.dm b/code/modules/reagents/reagent_containers/spray.dm
index 91a22ff1fb..e3576b3625 100644
--- a/code/modules/reagents/reagent_containers/spray.dm
+++ b/code/modules/reagents/reagent_containers/spray.dm
@@ -24,6 +24,7 @@
possible_transfer_amounts = list(5,10,15,20,25,30,50,100)
/obj/item/reagent_containers/spray/afterattack(atom/A, mob/user)
+ . = ..()
if(istype(A, /obj/structure/sink) || istype(A, /obj/structure/janitorialcart) || istype(A, /obj/machinery/hydroponics))
return
@@ -195,7 +196,7 @@
/obj/item/reagent_containers/spray/pepper/afterattack(atom/A as mob|obj, mob/user)
if (A.loc == user)
return
- ..()
+ . = ..()
//water flower
/obj/item/reagent_containers/spray/waterflower
@@ -273,7 +274,7 @@
// Make it so the bioterror spray doesn't spray yourself when you click your inventory items
if (A.loc == user)
return
- ..()
+ . = ..()
/obj/item/reagent_containers/spray/chemsprayer/spray(atom/A)
var/direction = get_dir(src, A)
diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm
index 64ef740278..edec0daa17 100644
--- a/code/modules/reagents/reagent_containers/syringes.dm
+++ b/code/modules/reagents/reagent_containers/syringes.dm
@@ -48,6 +48,7 @@
return
/obj/item/reagent_containers/syringe/afterattack(atom/target, mob/user , proximity)
+ . = ..()
if(busy)
return
if(!proximity)
@@ -110,7 +111,7 @@
if(SYRINGE_INJECT)
// Always log attemped injections for admins
var/contained = reagents.log_list()
- add_logs(user, L, "attemped to inject", src, addition="which had [contained]")
+ log_combat(user, target, "attempted to inject", src, addition="which had [contained]")
if(!reagents.total_volume)
to_chat(user, "
[src] is empty. ")
@@ -140,11 +141,9 @@
"
[user] injects [L] with the syringe! ")
if(L != user)
- add_logs(user, L, "injected", src, addition="which had [contained]")
+ log_combat(user, L, "injected", src, addition="which had [contained]")
else
- log_attack("
[key_name(user)] injected [key_name(L)] with [src.name], which had [contained] (INTENT: [uppertext(user.a_intent)]) ")
- L.log_message("
Injected themselves ([contained]) with [src.name]. ", INDIVIDUAL_ATTACK_LOG)
-
+ L.log_message("injected themselves ([contained]) with [src.name]", LOG_ATTACK, color="orange")
var/fraction = min(amount_per_transfer_from_this/reagents.total_volume, 1)
reagents.reaction(L, INJECT, fraction)
reagents.trans_to(target, amount_per_transfer_from_this)
@@ -224,7 +223,7 @@
list_reagents = list("chloralhydrate" = 50)
/obj/item/reagent_containers/syringe/lethal/execution
- list_reagents = list("amatoxin" = 15, "formaldehyde" = 15, "cyanide" = 10, "facid" = 10)
+ list_reagents = list("plasma" = 15, "formaldehyde" = 15, "cyanide" = 10, "facid" = 10)
/obj/item/reagent_containers/syringe/mulligan
name = "Mulligan"
@@ -260,10 +259,3 @@
desc = "A diamond-tipped syringe that pierces armor when launched at high velocity. It can hold up to 10 units."
volume = 10
proj_piercing = 1
-
-/obj/item/reagent_containers/syringe/alien
- name = "Hive's Blessing"
- desc = "A syringe filled with a strange viscous liquid. It might be best to leave it alone."
- amount_per_transfer_from_this = 1
- volume = 1
- list_reagents = list("xenomicrobes" = 1)
\ No newline at end of file
diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm
index 072c1b051f..45154a70f4 100644
--- a/code/modules/reagents/reagent_dispenser.dm
+++ b/code/modules/reagents/reagent_dispenser.dm
@@ -88,9 +88,7 @@
var/boom_message = "[ADMIN_LOOKUPFLW(P.firer)] triggered a fueltank explosion via projectile."
GLOB.bombers += boom_message
message_admins(boom_message)
- var/log_message = "triggered a fueltank explosion via projectile."
- P.firer.log_message(log_message, INDIVIDUAL_ATTACK_LOG)
- log_attack("[key_name(P.firer)] [log_message]")
+ P.firer.log_message("triggered a fueltank explosion via projectile.", LOG_ATTACK)
boom()
/obj/structure/reagent_dispensers/fueltank/attackby(obj/item/I, mob/living/user, params)
@@ -110,13 +108,12 @@
else
var/turf/T = get_turf(src)
user.visible_message("
[user] catastrophically fails at refilling [user.p_their()] [W.name]! ", "
That was stupid of you. ")
+
var/message_admins = "[ADMIN_LOOKUPFLW(user)] triggered a fueltank explosion via welding tool at [ADMIN_VERBOSEJMP(T)]."
GLOB.bombers += message_admins
message_admins(message_admins)
- var/message_log = "triggered a fueltank explosion via welding tool at [AREACOORD(T)]."
- user.log_message(message_log, INDIVIDUAL_ATTACK_LOG)
- log_game("[key_name(user)] [message_log]")
- log_attack("[key_name(user)] [message_log]")
+
+ user.log_message("triggered a fueltank explosion via welding tool.", LOG_ATTACK)
boom()
return
return ..()
diff --git a/code/modules/recycling/conveyor2.dm b/code/modules/recycling/conveyor2.dm
index 8acecf25a1..5875e821a3 100644
--- a/code/modules/recycling/conveyor2.dm
+++ b/code/modules/recycling/conveyor2.dm
@@ -343,6 +343,7 @@ GLOBAL_LIST_EMPTY(conveyors_by_id)
id = C.id
/obj/item/conveyor_construct/afterattack(atom/A, mob/user, proximity)
+ . = ..()
if(!proximity || user.stat || !isfloorturf(A) || istype(A, /area/shuttle))
return
var/cdir = get_dir(A, user)
@@ -366,6 +367,7 @@ GLOBAL_LIST_EMPTY(conveyors_by_id)
id = "[rand()]" //this couldn't possibly go wrong
/obj/item/conveyor_switch_construct/afterattack(atom/A, mob/user, proximity)
+ . = ..()
if(!proximity || user.stat || !isfloorturf(A) || istype(A, /area/shuttle))
return
var/found = 0
diff --git a/code/modules/recycling/disposal/bin.dm b/code/modules/recycling/disposal/bin.dm
index 737f50859d..fd98c54b81 100644
--- a/code/modules/recycling/disposal/bin.dm
+++ b/code/modules/recycling/disposal/bin.dm
@@ -136,7 +136,7 @@
user.visible_message("[user] climbs into [src].", "
You climb into [src]. ")
else
target.visible_message("
[user] has placed [target] in [src]. ", "
[user] has placed [target] in [src]. ")
- add_logs(user, target, "stuffed", addition="into [src]")
+ log_combat(user, target, "stuffed", addition="into [src]")
target.LAssailant = user
update_icon()
@@ -453,7 +453,7 @@
..()
flush()
-/obj/machinery/disposal/deliveryChute/CollidedWith(atom/movable/AM) //Go straight into the chute
+/obj/machinery/disposal/deliveryChute/Bumped(atom/movable/AM) //Go straight into the chute
if(!AM.CanEnterDisposals())
return
switch(dir)
diff --git a/code/modules/recycling/disposal/holder.dm b/code/modules/recycling/disposal/holder.dm
index 65a5dce1f5..2c0a6aa646 100644
--- a/code/modules/recycling/disposal/holder.dm
+++ b/code/modules/recycling/disposal/holder.dm
@@ -47,6 +47,7 @@
var/atom/movable/AM = A
if(AM == src)
continue
+ SEND_SIGNAL(AM, COMSIG_MOVABLE_DISPOSING, src, D)
AM.forceMove(src)
if(istype(AM, /obj/structure/bigDelivery) && !hasmob)
var/obj/structure/bigDelivery/T = AM
diff --git a/code/modules/recycling/sortingmachinery.dm b/code/modules/recycling/sortingmachinery.dm
index bd2dae4fd9..fa2eaa22d4 100644
--- a/code/modules/recycling/sortingmachinery.dm
+++ b/code/modules/recycling/sortingmachinery.dm
@@ -146,13 +146,7 @@
desc = "Used to set the destination of properly wrapped packages."
icon = 'icons/obj/device.dmi'
icon_state = "cargotagger"
- var/currTag = 0
- //The whole system for the sorttype var is determined based on the order of this list,
- //disposals must always be 1, since anything that's untagged will automatically go to disposals, or sorttype = 1 --Superxpdude
-
- //If you don't want to fuck up disposals, add to this list, and don't change the order.
- //If you insist on changing the order, you'll have to change every sort junction to reflect the new order. --Pete
-
+ var/currTag = 0 //Destinations are stored in code\globalvars\lists\flavor_misc.dm
w_class = WEIGHT_CLASS_TINY
item_state = "electronic"
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
diff --git a/code/modules/research/designs.dm b/code/modules/research/designs.dm
index 997d957a4e..491aa63e56 100644
--- a/code/modules/research/designs.dm
+++ b/code/modules/research/designs.dm
@@ -44,6 +44,8 @@ other types of metals and chemistry for reagents).
var/dangerous_construction = FALSE //notify and log for admin investigations if this is printed.
var/departmental_flags = ALL //bitflags for deplathes.
var/list/datum/techweb_node/unlocked_by = list()
+ var/research_icon //Replaces the item icon in the research console
+ var/research_icon_state
var/icon_cache
/datum/design/Destroy()
diff --git a/code/modules/research/designs/AI_module_designs.dm b/code/modules/research/designs/AI_module_designs.dm
index eb92bb3737..6e6bd740bb 100644
--- a/code/modules/research/designs/AI_module_designs.dm
+++ b/code/modules/research/designs/AI_module_designs.dm
@@ -14,7 +14,7 @@
name = "Module Design (Safeguard)"
desc = "Allows for the construction of a Safeguard AI Module."
id = "safeguard_module"
- materials = list(MAT_GLASS = 1000, MAT_GOLD = 100)
+ materials = list(MAT_GLASS = 1000, MAT_GOLD = 2000)
build_path = /obj/item/aiModule/supplied/safeguard
category = list("AI Modules")
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
@@ -23,7 +23,7 @@
name = "Module Design (OneHuman)"
desc = "Allows for the construction of a OneHuman AI Module."
id = "onehuman_module"
- materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 100)
+ materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 6000)
build_path = /obj/item/aiModule/zeroth/oneHuman
category = list("AI Modules")
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
@@ -32,7 +32,7 @@
name = "Module Design (ProtectStation)"
desc = "Allows for the construction of a ProtectStation AI Module."
id = "protectstation_module"
- materials = list(MAT_GLASS = 1000, MAT_GOLD = 100)
+ materials = list(MAT_GLASS = 1000, MAT_GOLD = 2000)
build_path = /obj/item/aiModule/supplied/protectStation
category = list("AI Modules")
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
@@ -41,7 +41,7 @@
name = "Module Design (Quarantine)"
desc = "Allows for the construction of a Quarantine AI Module."
id = "quarantine_module"
- materials = list(MAT_GLASS = 1000, MAT_GOLD = 100)
+ materials = list(MAT_GLASS = 1000, MAT_GOLD = 2000)
build_path = /obj/item/aiModule/supplied/quarantine
category = list("AI Modules")
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
@@ -50,7 +50,7 @@
name = "Module Design (OxygenIsToxicToHumans)"
desc = "Allows for the construction of a Safeguard AI Module."
id = "oxygen_module"
- materials = list(MAT_GLASS = 1000, MAT_GOLD = 100)
+ materials = list(MAT_GLASS = 1000, MAT_GOLD = 2000)
build_path = /obj/item/aiModule/supplied/oxygen
category = list("AI Modules")
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
@@ -59,7 +59,7 @@
name = "Module Design (Freeform)"
desc = "Allows for the construction of a Freeform AI Module."
id = "freeform_module"
- materials = list(MAT_GLASS = 1000, MAT_GOLD = 100)
+ materials = list(MAT_GLASS = 1000, MAT_GOLD = 10000)//Custom inputs should be more expensive to get
build_path = /obj/item/aiModule/supplied/freeform
category = list("AI Modules")
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
@@ -68,7 +68,7 @@
name = "Module Design (Reset)"
desc = "Allows for the construction of a Reset AI Module."
id = "reset_module"
- materials = list(MAT_GLASS = 1000, MAT_GOLD = 100)
+ materials = list(MAT_GLASS = 1000, MAT_GOLD = 2000)
build_path = /obj/item/aiModule/reset
category = list("AI Modules")
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
@@ -77,7 +77,7 @@
name = "Module Design (Purge)"
desc = "Allows for the construction of a Purge AI Module."
id = "purge_module"
- materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 100)
+ materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 2000)
build_path = /obj/item/aiModule/reset/purge
category = list("AI Modules")
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
@@ -86,7 +86,7 @@
name = "Module Design (Law Removal)"
desc = "Allows for the construction of a Law Removal AI Core Module."
id = "remove_module"
- materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 100)
+ materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 2000)
build_path = /obj/item/aiModule/remove
category = list("AI Modules")
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
@@ -95,7 +95,7 @@
name = "AI Core Module (Freeform)"
desc = "Allows for the construction of a Freeform AI Core Module."
id = "freeformcore_module"
- materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 100)
+ materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 10000)//Ditto
build_path = /obj/item/aiModule/core/freeformcore
category = list("AI Modules")
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
@@ -104,7 +104,7 @@
name = "Core Module Design (Asimov)"
desc = "Allows for the construction of an Asimov AI Core Module."
id = "asimov_module"
- materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 100)
+ materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 2000)
build_path = /obj/item/aiModule/core/full/asimov
category = list("AI Modules")
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
@@ -114,7 +114,7 @@
desc = "Allows for the construction of a P.A.L.A.D.I.N. AI Core Module."
id = "paladin_module"
build_type = IMPRINTER
- materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 100)
+ materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 2000)
build_path = /obj/item/aiModule/core/full/paladin
category = list("AI Modules")
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
@@ -123,7 +123,7 @@
name = "Core Module Design (T.Y.R.A.N.T.)"
desc = "Allows for the construction of a T.Y.R.A.N.T. AI Module."
id = "tyrant_module"
- materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 100)
+ materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 2000)
build_path = /obj/item/aiModule/core/full/tyrant
category = list("AI Modules")
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
@@ -132,7 +132,7 @@
name = "Core Module Design (Corporate)"
desc = "Allows for the construction of a Corporate AI Core Module."
id = "corporate_module"
- materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 100)
+ materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 2000)
build_path = /obj/item/aiModule/core/full/corp
category = list("AI Modules")
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
@@ -141,7 +141,7 @@
name = "Core Module Design (Default)"
desc = "Allows for the construction of a Default AI Core Module."
id = "default_module"
- materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 100)
+ materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 2000)
build_path = /obj/item/aiModule/core/full/custom
category = list("AI Modules")
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
diff --git a/code/modules/research/designs/autolathe_designs.dm b/code/modules/research/designs/autolathe_designs.dm
index 0a36394bc9..2050e96f5e 100644
--- a/code/modules/research/designs/autolathe_designs.dm
+++ b/code/modules/research/designs/autolathe_designs.dm
@@ -34,6 +34,14 @@
build_path = /obj/item/extinguisher
category = list("initial","Tools")
+/datum/design/pocketfireextinguisher
+ name = "Pocket Fire Extinguisher"
+ id = "pocketfireextinguisher"
+ build_type = AUTOLATHE
+ materials = list(MAT_METAL = 50, MAT_GLASS = 40)
+ build_path = /obj/item/extinguisher/mini
+ category = list("initial","Tools")
+
/datum/design/multitool
name = "Multitool"
id = "multitool"
@@ -229,10 +237,10 @@
/datum/design/rglass
name = "Reinforced Glass"
id = "rglass"
- build_type = AUTOLATHE | SMELTER
+ build_type = AUTOLATHE | SMELTER | PROTOLATHE
materials = list(MAT_METAL = 1000, MAT_GLASS = MINERAL_MATERIAL_AMOUNT)
build_path = /obj/item/stack/sheet/rglass
- category = list("initial","Construction")
+ category = list("initial","Construction","Stock Parts")
maxstack = 50
/datum/design/rods
diff --git a/code/modules/research/designs/biogenerator_designs.dm b/code/modules/research/designs/biogenerator_designs.dm
index e2d5652a35..3d48dd51ec 100644
--- a/code/modules/research/designs/biogenerator_designs.dm
+++ b/code/modules/research/designs/biogenerator_designs.dm
@@ -137,7 +137,7 @@
build_type = BIOGENERATOR
materials = list(MAT_BIOMASS = 300)
build_path = /obj/item/storage/belt/security
- category = list("initial","Leather and Cloth")
+ category = list("initial","Organic Materials")
/datum/design/medbelt
name = "Medical Belt"
@@ -145,7 +145,7 @@
build_type = BIOGENERATOR
materials = list(MAT_BIOMASS = 300)
build_path = /obj/item/storage/belt/medical
- category = list("initial","Leather and Cloth")
+ category = list("initial","Organic Materials")
/datum/design/janibelt
name = "Janitorial Belt"
@@ -153,7 +153,7 @@
build_type = BIOGENERATOR
materials = list(MAT_BIOMASS = 300)
build_path = /obj/item/storage/belt/janitor
- category = list("initial","Leather and Cloth")
+ category = list("initial","Organic Materials")
/datum/design/s_holster
name = "Shoulder Holster"
@@ -161,7 +161,7 @@
build_type = BIOGENERATOR
materials = list(MAT_BIOMASS = 400)
build_path = /obj/item/storage/belt/holster
- category = list("initial","Leather and Cloth")
+ category = list("initial","Organic Materials")
/datum/design/rice_hat
name = "Rice Hat"
@@ -169,4 +169,4 @@
build_type = BIOGENERATOR
materials = list(MAT_BIOMASS = 300)
build_path = /obj/item/clothing/head/rice_hat
- category = list("initial","Leather and Cloth")
+ category = list("initial","Organic Materials")
diff --git a/code/modules/research/designs/comp_board_designs.dm b/code/modules/research/designs/comp_board_designs.dm
index bbea1ce267..68aee66d62 100644
--- a/code/modules/research/designs/comp_board_designs.dm
+++ b/code/modules/research/designs/comp_board_designs.dm
@@ -50,6 +50,7 @@
name = "Computer Design (AI Upload)"
desc = "Allows for the construction of circuit boards used to build an AI Upload Console."
id = "aiupload"
+ materials = list(MAT_GLASS = 1000, MAT_GOLD = 2000)
build_path = /obj/item/circuitboard/computer/aiupload
category = list("Computer Boards")
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
@@ -58,6 +59,7 @@
name = "Computer Design (Cyborg Upload)"
desc = "Allows for the construction of circuit boards used to build a Cyborg Upload Console."
id = "borgupload"
+ materials = list(MAT_GLASS = 1000, MAT_GOLD = 2000)
build_path = /obj/item/circuitboard/computer/borgupload
category = list("Computer Boards")
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
diff --git a/code/modules/research/designs/machine_designs.dm b/code/modules/research/designs/machine_designs.dm
index 1ebadd2e34..93dfd3d2bb 100644
--- a/code/modules/research/designs/machine_designs.dm
+++ b/code/modules/research/designs/machine_designs.dm
@@ -306,6 +306,38 @@
category = list("Research Machinery")
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
+/datum/design/board/nanite_chamber
+ name = "Machine Design (Nanite Chamber Board)"
+ desc = "The circuit board for a Nanite Chamber."
+ id = "nanite_chamber"
+ build_path = /obj/item/circuitboard/machine/nanite_chamber
+ category = list("Research Machinery")
+ departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
+
+/datum/design/board/public_nanite_chamber
+ name = "Machine Design (Public Nanite Chamber Board)"
+ desc = "The circuit board for a Public Nanite Chamber."
+ id = "public_nanite_chamber"
+ build_path = /obj/item/circuitboard/machine/public_nanite_chamber
+ category = list("Research Machinery")
+ departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
+
+/datum/design/board/nanite_programmer
+ name = "Machine Design (Nanite Programmer Board)"
+ desc = "The circuit board for a Nanite Programmer."
+ id = "nanite_programmer"
+ build_path = /obj/item/circuitboard/machine/nanite_programmer
+ category = list("Research Machinery")
+ departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
+
+/datum/design/board/nanite_program_hub
+ name = "Machine Design (Nanite Program Hub Board)"
+ desc = "The circuit board for a Nanite Program Hub."
+ id = "nanite_program_hub"
+ build_path = /obj/item/circuitboard/machine/nanite_program_hub
+ category = list("Research Machinery")
+ departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
+
/datum/design/board/microwave
name = "Machine Design (Microwave Board)"
desc = "The circuit board for a microwave."
@@ -398,7 +430,7 @@
name = "Machine Design (Weapon Recharger Board)"
desc = "The circuit board for a Weapon Recharger."
id = "recharger"
- materials = list(MAT_GLASS = 1000, MAT_GOLD = 100)
+ materials = list(MAT_GLASS = 1000, MAT_GOLD = 2000)
build_path = /obj/item/circuitboard/machine/recharger
category = list("Misc. Machinery")
departmental_flags = DEPARTMENTAL_FLAG_ALL
@@ -475,6 +507,14 @@
category = list("Medical Machinery")
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL
+/datum/design/board/harvester
+ name = "Machine Design (Organ Harvester Board)"
+ desc = "The circuit board for an organ harvester."
+ id = "harvester"
+ build_path = /obj/item/circuitboard/machine/harvester
+ category = list("Medical Machinery")
+ departmental_flags = DEPARTMENTAL_FLAG_MEDICAL
+
/datum/design/board/deepfryer
name = "Machine Design (Deep Fryer)"
desc = "The circuit board for a Deep Fryer."
@@ -523,34 +563,10 @@
category = list ("Misc. Machinery")
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_CARGO | DEPARTMENTAL_FLAG_ENGINEERING
-/datum/design/board/nanite_chamber
- name = "Machine Design (Nanite Chamber Board)"
- desc = "The circuit board for a Nanite Chamber."
- id = "nanite_chamber"
- build_path = /obj/item/circuitboard/machine/nanite_chamber
- category = list("Research Machinery")
- departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
-
-/datum/design/board/public_nanite_chamber
- name = "Machine Design (Public Nanite Chamber Board)"
- desc = "The circuit board for a Public Nanite Chamber."
- id = "public_nanite_chamber"
- build_path = /obj/item/circuitboard/machine/public_nanite_chamber
- category = list("Research Machinery")
- departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
-
-/datum/design/board/nanite_programmer
- name = "Machine Design (Nanite Programmer Board)"
- desc = "The circuit board for a Nanite Programmer."
- id = "nanite_programmer"
- build_path = /obj/item/circuitboard/machine/nanite_programmer
- category = list("Research Machinery")
- departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
-
-/datum/design/board/nanite_program_hub
- name = "Machine Design (Nanite Program Hub Board)"
- desc = "The circuit board for a Nanite Program Hub."
- id = "nanite_program_hub"
- build_path = /obj/item/circuitboard/machine/nanite_program_hub
- category = list("Research Machinery")
- departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
+/datum/design/board/ore_silo
+ name = "Machine Design (Ore Silo)"
+ desc = "The circuit board for an ore silo."
+ id = "ore_silo"
+ build_path = /obj/item/circuitboard/machine/ore_silo
+ category = list ("Research Machinery")
+ departmental_flags = DEPARTMENTAL_FLAG_CARGO
diff --git a/code/modules/research/designs/mechfabricator_designs.dm b/code/modules/research/designs/mechfabricator_designs.dm
index e4ba942c84..97c769170d 100644
--- a/code/modules/research/designs/mechfabricator_designs.dm
+++ b/code/modules/research/designs/mechfabricator_designs.dm
@@ -704,6 +704,15 @@
materials = list(MAT_METAL=15000, MAT_GLASS=15000, MAT_SILVER=10000, MAT_GOLD=10000, MAT_TITANIUM=5000, MAT_DIAMOND=5000)
construction_time = 120
category = list("Cyborg Upgrade Modules")
+
+/datum/design/borg_upgrade_surgicalprocessor
+ name = "Cyborg Upgrade (Surgical Processor)"
+ id = "borg_upgrade_surgicalprocessor"
+ build_type = MECHFAB
+ build_path = /obj/item/borg/upgrade/processor
+ materials = list(MAT_METAL=15000, MAT_GLASS=15000, MAT_SILVER=10000)
+ construction_time = 120
+ category = list("Cyborg Upgrade Modules")
/datum/design/borg_upgrade_trashofholding
name = "Cyborg Upgrade (Trash Bag of Holding)"
diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm
index 5f1e5ea520..4b28ecd048 100644
--- a/code/modules/research/designs/medical_designs.dm
+++ b/code/modules/research/designs/medical_designs.dm
@@ -122,6 +122,16 @@
category = list("Medical Designs")
departmental_flags = DEPARTMENTAL_FLAG_ALL
+/datum/design/crewpinpointer
+ name = "Crew Pinpointer"
+ desc = "Allows tracking of someone's location if their suit sensors are turned to tracking beacon."
+ id = "crewpinpointer"
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL = 3000, MAT_GLASS = 1500, MAT_GOLD = 200)
+ build_path = /obj/item/pinpointer/crew
+ category = list("Medical Designs")
+ departmental_flags = DEPARTMENTAL_FLAG_MEDICAL
+
/datum/design/defibrillator
name = "Defibrillator"
id = "defibrillator"
@@ -200,7 +210,7 @@
materials = list(MAT_METAL = 2000, MAT_SILVER = 1500, MAT_PLASMA = 500, MAT_TITANIUM = 1500)
category = list("Medical Designs")
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL
-
+
/datum/design/healthanalyzer_advanced
name = "advanced health analyzer"
desc = "A hand-held body scanner able to distinguish vital signs of the subject with high accuracy."
@@ -210,7 +220,7 @@
materials = list(MAT_METAL = 5000, MAT_GLASS = 2500, MAT_SILVER = 2000, MAT_GOLD = 1500)
category = list("Medical Designs")
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL
-
+
/////////////////////////////////////////
//////////Cybernetic Implants////////////
/////////////////////////////////////////
@@ -486,102 +496,93 @@
category = list("Medical Designs")
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL
-/datum/design/surgery_lobotomy
- name = "Lobotomy Surgery Disk"
- desc = "A disk containing the instructions for a Lobotomy surgery."
+/////////////////////
+///Surgery Designs///
+/////////////////////
+/datum/design/surgery
+ name = "Surgery Design"
+ desc = "what"
+ id = "surgery_parent"
+ research_icon = 'icons/obj/surgery.dmi'
+ research_icon_state = "surgery_any"
+ var/surgery
+
+/datum/design/surgery/lobotomy
+ name = "Lobotomy"
+ desc = "An invasive surgical procedure which guarantees removal of almost all brain traumas, but might cause another permanent trauma in return."
id = "surgery_lobotomy"
- build_type = PROTOLATHE
- materials = list(MAT_METAL = 300, MAT_GLASS = 100)
- build_path = /obj/item/disk/surgery/lobotomy
- category = list("Medical Designs")
- departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE
+ surgery = /datum/surgery/advanced/lobotomy
+ research_icon_state = "surgery_head"
-/datum/design/surgery_pacify
- name = "Pacification Surgery Disk"
- desc = "A disk containing the instructions for a Pacification surgery."
+/datum/design/surgery/pacify
+ name = "Pacification"
+ desc = "A surgical procedure which permanently inhibits the aggression center of the brain, making the patient unwilling to cause direct harm."
id = "surgery_pacify"
- build_type = PROTOLATHE
- materials = list(MAT_METAL = 300, MAT_GLASS = 100)
- build_path = /obj/item/disk/surgery/pacification
- category = list("Medical Designs")
- departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE
+ surgery = /datum/surgery/advanced/pacify
+ research_icon_state = "surgery_head"
-/datum/design/surgery_viral_bonding
- name = "Viral Bonding Surgery Disk"
- desc = "A disk containing the instructions for a Viral Bonding surgery."
+/datum/design/surgery/viral_bonding
+ name = "Viral Bonding"
+ desc = "A surgical procedure that forces a symbiotic relationship between a virus and its host. The patient must be dosed with spaceacillin, virus food, and formaldehyde."
id = "surgery_viral_bond"
- build_type = PROTOLATHE
- materials = list(MAT_METAL = 300, MAT_GLASS = 100)
- build_path = /obj/item/disk/surgery/viral_bonding
- category = list("Medical Designs")
- departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE
+ surgery = /datum/surgery/advanced/viral_bonding
+ research_icon_state = "surgery_chest"
-/datum/design/surgery_reconstruction
- name = "Reconstruction Surgery Disk"
- desc = "A disk containing the instructions for a Reconstruction surgery."
+/datum/design/surgery/reconstruction
+ name = "Reconstruction"
+ desc = "A surgical procedure that gradually repairs damage done to a body without the assistance of chemicals. Unlike classic medicine, it is effective on corpses."
id = "surgery_reconstruction"
- build_type = PROTOLATHE
- materials = list(MAT_METAL = 300, MAT_GLASS = 100)
- build_path = /obj/item/disk/surgery/reconstruction
- category = list("Medical Designs")
- departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE
+ surgery = /datum/surgery/advanced/reconstruction
+ research_icon_state = "surgery_chest"
-/datum/design/surgery_revival
- name = "Revival Surgery Disk"
- desc = "A disk containing the instructions for a Revival surgery."
+/datum/design/surgery/revival
+ name = "Revival"
+ desc = "An experimental surgical procedure which involves reconstruction and reactivation of the patient's brain even long after death. The body must still be able to sustain life."
id = "surgery_revival"
- build_type = PROTOLATHE
- materials = list(MAT_METAL = 300, MAT_GLASS = 100)
- build_path = /obj/item/disk/surgery/revival
- category = list("Medical Designs")
- departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE
+ surgery = /datum/surgery/advanced/revival
+ research_icon_state = "surgery_head"
-/datum/design/surgery_brainwashing
- name = "Brainwashing Surgery Disk"
- desc = "A disk containing the instructions for a Brainwashing surgery."
+/datum/design/surgery/brainwashing
+ name = "Brainwashing"
+ desc = "A surgical procedure which directly implants a directive into the patient's brain, making it their absolute priority. It can be cleared using a mindshield implant."
id = "surgery_brainwashing"
- build_type = PROTOLATHE
- materials = list(MAT_METAL = 300, MAT_GLASS = 100)
- build_path = /obj/item/disk/surgery/brainwashing
- category = list("Medical Designs")
- departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE
+ surgery = /datum/surgery/advanced/brainwashing
+ research_icon_state = "surgery_head"
-/datum/design/surgery_nerve_splicing
- name = "Nerve Splicing Surgery Disk"
- desc = "A disk containing the instructions for a Nerve Splicing surgery."
+/datum/design/surgery/nerve_splicing
+ name = "Nerve Splicing"
+ desc = "A surgical procedure which splices the patient's nerves, making them more resistant to stuns."
id = "surgery_nerve_splice"
- build_type = PROTOLATHE
- materials = list(MAT_METAL = 300, MAT_GLASS = 100)
- build_path = /obj/item/disk/surgery/nerve_splicing
- category = list("Medical Designs")
- departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE
+ surgery = /datum/surgery/advanced/bioware/nerve_splicing
+ research_icon_state = "surgery_chest"
-/datum/design/surgery_nerve_grounding
- name = "Nerve Grounding Surgery Disk"
- desc = "A disk containing the instructions for a Nerve Grounding surgery."
+/datum/design/surgery/nerve_grounding
+ name = "Nerve Grounding"
+ desc = "A surgical procedure which makes the patient's nerves act as grounding rods, protecting them from electrical shocks."
id = "surgery_nerve_ground"
- build_type = PROTOLATHE
- materials = list(MAT_METAL = 300, MAT_GLASS = 100)
- build_path = /obj/item/disk/surgery/nerve_grounding
- category = list("Medical Designs")
- departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE
+ surgery = /datum/surgery/advanced/bioware/nerve_grounding
+ research_icon_state = "surgery_chest"
-/datum/design/surgery_vein_threading
- name = "Vein Threading Surgery Disk"
- desc = "A disk containing the instructions for a Vein Threading surgery."
+/datum/design/surgery/vein_threading
+ name = "Vein Threading"
+ desc = "A surgical procedure which severely reduces the amount of blood lost in case of injury."
id = "surgery_vein_thread"
- build_type = PROTOLATHE
- materials = list(MAT_METAL = 300, MAT_GLASS = 100)
- build_path = /obj/item/disk/surgery/vein_threading
- category = list("Medical Designs")
- departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE
+ surgery = /datum/surgery/advanced/bioware/vein_threading
+ research_icon_state = "surgery_chest"
-/datum/design/surgery_necrotic_revival
- name = "Necrotic Revival Surgery Disk"
- desc = "A disk containing the instructions for a Necrotic Revival surgery."
+/datum/design/surgery/necrotic_revival
+ name = "Necrotic Revival"
+ desc = "An experimental surgical procedure that stimulates the growth of a Romerol tumor inside the patient's brain. Requires zombie powder or rezadone."
id = "surgery_zombie"
+ surgery = /datum/surgery/advanced/necrotic_revival
+ research_icon_state = "surgery_head"
+
+/datum/design/holobarrier_med
+ name = "PENLITE holobarrier projector"
+ desc = "PENLITE holobarriers, a device that halts individuals with malicious diseases."
build_type = PROTOLATHE
- materials = list(MAT_METAL = 300, MAT_GLASS = 100)
- build_path = /obj/item/disk/surgery/necrotic_revival
+ build_path = /obj/item/holosign_creator/medical
+ materials = list(MAT_METAL = 500, MAT_GLASS = 500, MAT_SILVER = 100) //a hint of silver since it can troll 2 antags (bad viros and sentient disease)
+ id = "holobarrier_med"
category = list("Medical Designs")
- departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE
+ departmental_flags = DEPARTMENTAL_FLAG_MEDICAL
\ No newline at end of file
diff --git a/code/modules/research/designs/misc_designs.dm b/code/modules/research/designs/misc_designs.dm
index d8f25968db..17fc64867b 100644
--- a/code/modules/research/designs/misc_designs.dm
+++ b/code/modules/research/designs/misc_designs.dm
@@ -157,6 +157,16 @@
category = list("Equipment")
departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING
+/datum/design/forcefield_projector
+ name = "Forcefield Projector"
+ desc = "A device which can project temporary forcefields to seal off an area."
+ id = "forcefield_projector"
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL = 2500, MAT_GLASS = 1000)
+ build_path = /obj/item/forcefield_projector
+ category = list("Equipment")
+ departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING
+
/datum/design/sci_goggles
name = "Science Goggles"
desc = "Goggles fitted with a portable analyzer capable of determining the research worth of an item or components of a machine."
@@ -267,6 +277,16 @@
category = list("Equipment")
departmental_flags = DEPARTMENTAL_FLAG_SERVICE
+/datum/design/locator
+ name = "Bluespace locator"
+ desc = "Used to track portable teleportation beacons and targets with embedded tracking implants."
+ id = "locator"
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL=1000, MAT_GLASS=500, MAT_SILVER = 500)
+ build_path = /obj/item/locator
+ category = list("Equipment")
+ departmental_flags = DEPARTMENTAL_FLAG_SECURITY
+
/////////////////////////////////////////
////////////Janitor Designs//////////////
/////////////////////////////////////////
diff --git a/code/modules/research/designs/nanite_designs.dm b/code/modules/research/designs/nanite_designs.dm
index 02f543af1e..177d7073a1 100644
--- a/code/modules/research/designs/nanite_designs.dm
+++ b/code/modules/research/designs/nanite_designs.dm
@@ -5,8 +5,8 @@
build_type = NANITE_COMPILER
construction_time = 50
category = list()
- //research_icon = 'icons/obj/device.dmi'
- //research_icon_state = "nanite_program"
+ research_icon = 'icons/obj/device.dmi'
+ research_icon_state = "nanite_program"
var/program_type = /datum/nanite_program
////////////////////UTILITY NANITES//////////////////////////////////////
diff --git a/code/modules/research/designs/smelting_designs.dm b/code/modules/research/designs/smelting_designs.dm
index 79a9748b08..f2f21396d2 100644
--- a/code/modules/research/designs/smelting_designs.dm
+++ b/code/modules/research/designs/smelting_designs.dm
@@ -3,51 +3,63 @@
/datum/design/plasteel_alloy
name = "Plasma + Iron alloy"
id = "plasteel"
- build_type = SMELTER
+ build_type = SMELTER | PROTOLATHE
materials = list(MAT_METAL = MINERAL_MATERIAL_AMOUNT, MAT_PLASMA = MINERAL_MATERIAL_AMOUNT)
build_path = /obj/item/stack/sheet/plasteel
- category = list("initial")
+ category = list("initial", "Stock Parts")
+ departmental_flags = DEPARTMENTAL_FLAG_CARGO | DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING
+ maxstack = 50
/datum/design/plastitanium_alloy
name = "Plasma + Titanium alloy"
id = "plastitanium"
- build_type = SMELTER
+ build_type = SMELTER | PROTOLATHE
materials = list(MAT_TITANIUM = MINERAL_MATERIAL_AMOUNT, MAT_PLASMA = MINERAL_MATERIAL_AMOUNT)
build_path = /obj/item/stack/sheet/mineral/plastitanium
- category = list("initial")
+ category = list("initial", "Stock Parts")
+ departmental_flags = DEPARTMENTAL_FLAG_CARGO | DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING
+ maxstack = 50
/datum/design/plaglass_alloy
name = "Plasma + Glass alloy"
id = "plasmaglass"
- build_type = SMELTER
+ build_type = SMELTER | PROTOLATHE
materials = list(MAT_PLASMA = MINERAL_MATERIAL_AMOUNT * 0.5, MAT_GLASS = MINERAL_MATERIAL_AMOUNT)
build_path = /obj/item/stack/sheet/plasmaglass
- category = list("initial")
+ category = list("initial", "Stock Parts")
+ departmental_flags = DEPARTMENTAL_FLAG_CARGO | DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING
+ maxstack = 50
/datum/design/plasmarglass_alloy
name = "Plasma + Metal + Glass alloy"
id = "plasmareinforcedglass"
- build_type = SMELTER
+ build_type = SMELTER | PROTOLATHE
materials = list(MAT_PLASMA = MINERAL_MATERIAL_AMOUNT * 0.5, MAT_METAL = MINERAL_MATERIAL_AMOUNT * 0.5, MAT_GLASS = MINERAL_MATERIAL_AMOUNT)
build_path = /obj/item/stack/sheet/plasmarglass
- category = list("initial")
+ category = list("initial", "Stock Parts")
+ departmental_flags = DEPARTMENTAL_FLAG_CARGO | DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING
+ maxstack = 50
/datum/design/titaniumglass_alloy
name = "Titanium + Glass alloy"
id = "titaniumglass"
- build_type = SMELTER
+ build_type = SMELTER | PROTOLATHE
materials = list(MAT_TITANIUM = MINERAL_MATERIAL_AMOUNT * 0.5, MAT_GLASS = MINERAL_MATERIAL_AMOUNT)
build_path = /obj/item/stack/sheet/titaniumglass
- category = list("initial")
+ category = list("initial", "Stock Parts")
+ departmental_flags = DEPARTMENTAL_FLAG_CARGO | DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING
+ maxstack = 50
/datum/design/plastitaniumglass_alloy
name = "Plasma + Titanium + Glass alloy"
id = "plastitaniumglass"
- build_type = SMELTER
+ build_type = SMELTER | PROTOLATHE
materials = list(MAT_PLASMA = MINERAL_MATERIAL_AMOUNT * 0.5, MAT_TITANIUM = MINERAL_MATERIAL_AMOUNT * 0.5, MAT_GLASS = MINERAL_MATERIAL_AMOUNT)
build_path = /obj/item/stack/sheet/plastitaniumglass
- category = list("initial")
+ category = list("initial", "Stock Parts")
+ departmental_flags = DEPARTMENTAL_FLAG_CARGO | DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING
+ maxstack = 50
/datum/design/alienalloy
name = "Alien Alloy"
diff --git a/code/modules/research/designs/weapon_designs.dm b/code/modules/research/designs/weapon_designs.dm
index 77c6cf85a0..0c7b7327fa 100644
--- a/code/modules/research/designs/weapon_designs.dm
+++ b/code/modules/research/designs/weapon_designs.dm
@@ -148,7 +148,7 @@
materials = list(MAT_METAL = 3000)
build_path = /obj/item/grenade/chem_grenade/large
category = list("Weapons")
- departmental_flags = DEPARTMENTAL_FLAG_SECURITY | DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE
+ departmental_flags = DEPARTMENTAL_FLAG_SECURITY | DEPARTMENTAL_FLAG_MEDICAL
/datum/design/pyro_grenade
name = "Pyro Grenade"
diff --git a/code/modules/research/destructive_analyzer.dm b/code/modules/research/destructive_analyzer.dm
index 37a83eb04f..5cc26381dd 100644
--- a/code/modules/research/destructive_analyzer.dm
+++ b/code/modules/research/destructive_analyzer.dm
@@ -36,10 +36,6 @@ Note: Must be placed within 3 tiles of the R&D Console
. = 1
if(!is_insertion_ready(user))
return
- if(istype (O, /obj/item/bodybag/bluespace)) // CITADEL ADD. STOP PUTTING FUCKING BORGS INTO THE ANALYZER
- if(O.contents.len != 0)
- to_chat(user, "
\The [O] has dangerous levels of activity, you cannot put it in the [src.name]! ")
- return
if(!user.transferItemToLoc(O, src))
to_chat(user, "
\The [O] is stuck to your hand, you cannot put it in the [src.name]! ")
return
@@ -63,11 +59,14 @@ Note: Must be placed within 3 tiles of the R&D Console
/obj/machinery/rnd/destructive_analyzer/proc/reclaim_materials_from(obj/item/thing)
. = 0
- if(linked_console && linked_console.linked_lathe) //Also sends salvaged materials to a linked protolathe, if any.
+ var/datum/component/material_container/storage = linked_console?.linked_lathe?.materials.mat_container
+ if(storage) //Also sends salvaged materials to a linked protolathe, if any.
for(var/material in thing.materials)
- var/can_insert = min((linked_console.linked_lathe.materials.max_amount - linked_console.linked_lathe.materials.total_amount), (max(thing.materials[material]*(decon_mod/10), thing.materials[material])))
- linked_console.linked_lathe.materials.insert_amount(can_insert, material)
+ var/can_insert = min((storage.max_amount - storage.total_amount), (max(thing.materials[material]*(decon_mod/10), thing.materials[material])))
+ storage.insert_amount(can_insert, material)
. += can_insert
+ if (.)
+ linked_console.linked_lathe.materials.silo_log(src, "reclaimed", 1, "[thing.name]", thing.materials)
/obj/machinery/rnd/destructive_analyzer/proc/destroy_item(obj/item/thing, innermode = FALSE)
if(QDELETED(thing) || QDELETED(src) || QDELETED(linked_console))
diff --git a/code/modules/research/experimentor.dm b/code/modules/research/experimentor.dm
index ccd9872b46..ebd386513e 100644
--- a/code/modules/research/experimentor.dm
+++ b/code/modules/research/experimentor.dm
@@ -583,7 +583,7 @@
/obj/item/relic/proc/corgicannon(mob/user)
playsound(src, "sparks", rand(25,50), 1)
var/mob/living/simple_animal/pet/dog/corgi/C = new/mob/living/simple_animal/pet/dog/corgi(get_turf(user))
- C.throw_at(pick(oview(10,user)), 10, rand(3,8), callback = CALLBACK(src, .throwSmoke, C))
+ C.throw_at(pick(oview(10,user)), 10, rand(3,8), callback = CALLBACK(src, .proc/throwSmoke, C))
warn_admins(user, "Corgi Cannon", 0)
/obj/item/relic/proc/clean(mob/user)
diff --git a/code/modules/research/machinery/_production.dm b/code/modules/research/machinery/_production.dm
index 75ed53e936..d3cd399635 100644
--- a/code/modules/research/machinery/_production.dm
+++ b/code/modules/research/machinery/_production.dm
@@ -6,7 +6,7 @@
var/consoleless_interface = FALSE //Whether it can be used without a console.
var/efficiency_coeff = 1 //Materials needed / coeff = actual.
var/list/categories = list()
- var/datum/component/material_container/materials //Store for hyper speed!
+ var/datum/component/remote_materials/materials
var/allowed_department_flags = ALL
var/production_animation //What's flick()'d on print.
var/allowed_buildtypes = NONE
@@ -19,19 +19,16 @@
var/screen = RESEARCH_FABRICATOR_SCREEN_MAIN
var/selected_category
-/obj/machinery/rnd/production/Initialize()
+/obj/machinery/rnd/production/Initialize(mapload)
. = ..()
create_reagents(0)
- materials = AddComponent(/datum/component/material_container,
- list(MAT_METAL, MAT_GLASS, MAT_SILVER, MAT_GOLD, MAT_DIAMOND, MAT_PLASMA, MAT_URANIUM, MAT_BANANIUM, MAT_TITANIUM, MAT_BLUESPACE, MAT_PLASTIC), 0,
- TRUE, list(/obj/item/stack), CALLBACK(src, .proc/is_insertion_ready), CALLBACK(src, .proc/AfterMaterialInsert))
- materials.precise_insertion = TRUE
- RefreshParts()
matching_designs = list()
cached_designs = list()
stored_research = new
host_research = SSresearch.science_tech
update_research()
+ materials = AddComponent(/datum/component/remote_materials, "lathe", mapload)
+ RefreshParts()
/obj/machinery/rnd/production/proc/update_research()
host_research.copy_research_to(stored_research, TRUE)
@@ -47,9 +44,6 @@
/obj/machinery/rnd/production/RefreshParts()
calculate_efficiency()
-/obj/machinery/rnd/production/attack_hand(mob/user)
- interact(user) //remove this snowflake shit when the refactor of storage components or some other pr that unsnowflakes attack_hand on machinery is in
-
/obj/machinery/rnd/production/ui_interact(mob/user)
if(!consoleless_interface)
return ..()
@@ -70,9 +64,10 @@
reagents.maximum_volume += G.volume
G.reagents.trans_to(src, G.reagents.total_volume)
if(materials)
- materials.max_amount = 0
+ var/total_storage = 0
for(var/obj/item/stock_parts/matter_bin/M in component_parts)
- materials.max_amount += M.rating * 75000
+ total_storage += M.rating * 75000
+ materials.set_local_size(total_storage)
var/total_rating = 0
for(var/obj/item/stock_parts/manipulator/M in component_parts)
total_rating += M.rating
@@ -83,7 +78,6 @@
/obj/machinery/rnd/production/on_deconstruction()
for(var/obj/item/reagent_containers/glass/G in component_parts)
reagents.trans_to(G, G.reagents.maximum_volume)
- materials.retrieve_all()
return ..()
/obj/machinery/rnd/production/proc/do_print(path, amount, list/matlist, notify_admins)
@@ -92,18 +86,26 @@
message_admins("[ADMIN_LOOKUPFLW(usr)] has built [amount] of [path] at a [src]([type]).")
for(var/i in 1 to amount)
var/obj/item/I = new path(get_turf(src))
- if(!istype(I, /obj/item/stack/sheet) && !istype(I, /obj/item/stack/ore/bluespace_crystal))
+ if(efficient_with(I.type))
I.materials = matlist.Copy()
SSblackbox.record_feedback("nested tally", "item_printed", amount, list("[type]", "[path]"))
/obj/machinery/rnd/production/proc/check_mat(datum/design/being_built, M) // now returns how many times the item can be built with the material
+ if (!materials.mat_container) // no connected silo
+ return 0
var/list/all_materials = being_built.reagents_list + being_built.materials
- var/A = materials.amount(M)
+ var/A = materials.mat_container.amount(M)
if(!A)
A = reagents.get_reagent_amount(M)
- return round(A / max(1, (all_materials[M]/efficiency_coeff)))
+ // these types don't have their .materials set in do_print, so don't allow
+ // them to be constructed efficiently
+ var/ef = efficient_with(being_built.build_path) ? efficiency_coeff : 1
+ return round(A / max(1, all_materials[M] / ef))
+
+/obj/machinery/rnd/production/proc/efficient_with(path)
+ return !ispath(path, /obj/item/stack/sheet) && !ispath(path, /obj/item/stack/ore/bluespace_crystal)
/obj/machinery/rnd/production/proc/user_try_print_id(id, amount)
if((!istype(linked_console) && requires_console) || !id)
@@ -121,25 +123,33 @@
if(D.build_type && !(D.build_type & allowed_buildtypes))
say("This machine does not have the necessary manipulation systems for this design. Please contact Nanotrasen Support!")
return FALSE
+ if(!materials.mat_container)
+ say("No connection to material storage, please contact the quartermaster.")
+ return FALSE
+ if(materials.on_hold())
+ say("Mineral access is on hold, please contact the quartermaster.")
+ return FALSE
var/power = 1000
amount = CLAMP(amount, 1, 50)
for(var/M in D.materials)
power += round(D.materials[M] * amount / 35)
power = min(3000, power)
use_power(power)
+ var/coeff = efficient_with(D.build_path) ? efficiency_coeff : 1
var/list/efficient_mats = list()
for(var/MAT in D.materials)
- efficient_mats[MAT] = D.materials[MAT]/efficiency_coeff
- if(!materials.has_materials(efficient_mats, amount))
+ efficient_mats[MAT] = D.materials[MAT]/coeff
+ if(!materials.mat_container.has_materials(efficient_mats, amount))
say("Not enough materials to complete prototype[amount > 1? "s" : ""].")
return FALSE
for(var/R in D.reagents_list)
- if(!reagents.has_reagent(R, D.reagents_list[R]*amount/efficiency_coeff))
+ if(!reagents.has_reagent(R, D.reagents_list[R]*amount/coeff))
say("Not enough reagents to complete prototype[amount > 1? "s" : ""].")
return FALSE
- materials.use_amount(efficient_mats, amount)
+ materials.mat_container.use_amount(efficient_mats, amount)
+ materials.silo_log(src, "built", -amount, "[D.name]", efficient_mats)
for(var/R in D.reagents_list)
- reagents.remove_reagent(R, D.reagents_list[R]*amount/efficiency_coeff)
+ reagents.remove_reagent(R, D.reagents_list[R]*amount/coeff)
busy = TRUE
if(production_animation)
flick(production_animation, src)
@@ -181,17 +191,23 @@
var/list/l = list()
l += "
[RDSCREEN_NOBREAK]"
return l
/obj/machinery/rnd/production/proc/ui_screen_materials()
+ if (!materials.mat_container)
+ screen = RESEARCH_FABRICATOR_SCREEN_MAIN
+ return ui_screen_main()
var/list/l = list()
l += "
Material Storage: "
- for(var/mat_id in materials.materials)
- var/datum/material/M = materials.materials[mat_id]
+ for(var/mat_id in materials.mat_container.materials)
+ var/datum/material/M = materials.mat_container.materials[mat_id]
l += "* [M.amount] of [M.name]: "
if(M.amount >= MINERAL_MATERIAL_AMOUNT) l += "
Eject [RDSCREEN_NOBREAK]"
if(M.amount >= MINERAL_MATERIAL_AMOUNT*5) l += "
5x [RDSCREEN_NOBREAK]"
@@ -230,6 +246,8 @@
return
if(!coeff)
coeff = efficiency_coeff
+ if(!efficient_with(D.build_path))
+ coeff = 1
var/list/l = list()
var/temp_material
var/c = 50
@@ -281,9 +299,23 @@
if(ls["disposeall"]) //Causes the protolathe to dispose of all it's reagents.
reagents.clear_reagents()
if(ls["ejectsheet"]) //Causes the protolathe to eject a sheet of material
- materials.retrieve_sheets(text2num(ls["eject_amt"]), ls["ejectsheet"])
+ eject_sheets(ls["ejectsheet"], ls["eject_amt"])
updateUsrDialog()
+/obj/machinery/rnd/production/proc/eject_sheets(eject_sheet, eject_amt)
+ var/datum/component/material_container/mat_container = materials.mat_container
+ if (!mat_container)
+ say("No access to material storage, please contact the quartermaster.")
+ return 0
+ if (materials.on_hold())
+ say("Mineral access is on hold, please contact the quartermaster.")
+ return 0
+ var/count = mat_container.retrieve_sheets(text2num(eject_amt), eject_sheet, drop_location())
+ var/list/matlist = list()
+ matlist[eject_sheet] = MINERAL_MATERIAL_AMOUNT
+ materials.silo_log(src, "ejected", -count, "sheets", matlist)
+ return count
+
/obj/machinery/rnd/production/proc/ui_screen_main()
var/list/l = list()
l += "
\
diff --git a/code/modules/research/nanites/nanite_chamber.dm b/code/modules/research/nanites/nanite_chamber.dm
index 6f58cb2ad9..1fc2633989 100644
--- a/code/modules/research/nanites/nanite_chamber.dm
+++ b/code/modules/research/nanites/nanite_chamber.dm
@@ -4,12 +4,12 @@
circuit = /obj/item/circuitboard/machine/nanite_chamber
icon = 'icons/obj/machines/nanite_chamber.dmi'
icon_state = "nanite_chamber"
+ layer = ABOVE_WINDOW_LAYER
use_power = IDLE_POWER_USE
anchored = TRUE
density = TRUE
idle_power_usage = 50
active_power_usage = 300
- occupant_typecache = list(/mob/living)
var/obj/machinery/computer/nanite_chamber_control/console
var/locked = FALSE
@@ -20,6 +20,10 @@
var/busy_message
var/message_cooldown = 0
+/obj/machinery/nanite_chamber/Initialize()
+ . = ..()
+ occupant_typecache = GLOB.typecache_living
+
/obj/machinery/nanite_chamber/RefreshParts()
scan_level = 0
for(var/obj/item/stock_parts/scanning_module/P in component_parts)
@@ -224,4 +228,4 @@
/obj/machinery/nanite_chamber/MouseDrop_T(mob/target, mob/user)
if(user.stat || user.lying || !Adjacent(user) || !user.Adjacent(target) || !iscarbon(target) || !user.IsAdvancedToolUser())
return
- close_machine(target)
\ No newline at end of file
+ close_machine(target)
diff --git a/code/modules/research/nanites/nanite_cloud_controller.dm b/code/modules/research/nanites/nanite_cloud_controller.dm
index 8218f250bb..081b1a7a4c 100644
--- a/code/modules/research/nanites/nanite_cloud_controller.dm
+++ b/code/modules/research/nanites/nanite_cloud_controller.dm
@@ -167,7 +167,7 @@
if(backup)
playsound(src, 'sound/machines/terminal_prompt.ogg', 50, 0)
var/datum/component/nanites/nanites = backup.nanites
- nanites.add_program(disk.program.copy())
+ nanites.add_program(null, disk.program.copy())
investigate_log("[key_name(usr)] uploaded program [disk.program.name] to cloud #[current_view]", INVESTIGATE_NANITES)
. = TRUE
if("remove_program")
diff --git a/code/modules/research/nanites/nanite_hijacker.dm b/code/modules/research/nanites/nanite_hijacker.dm
index 8c5ac0a84b..dba47a4ac2 100644
--- a/code/modules/research/nanites/nanite_hijacker.dm
+++ b/code/modules/research/nanites/nanite_hijacker.dm
@@ -15,6 +15,11 @@
if(disk)
eject()
+/obj/item/nanite_hijacker/examine(mob/user)
+ . = ..()
+ if(disk)
+ to_chat(user, "Alt-click [src] to eject the disk. ")
+
/obj/item/nanite_hijacker/attackby(obj/item/I, mob/user)
if(istype(I, /obj/item/disk/nanite_program))
var/obj/item/disk/nanite_program/N = I
diff --git a/code/modules/research/nanites/nanite_programs.dm b/code/modules/research/nanites/nanite_programs.dm
index 00581154d9..f691c828c9 100644
--- a/code/modules/research/nanites/nanite_programs.dm
+++ b/code/modules/research/nanites/nanite_programs.dm
@@ -224,7 +224,7 @@
if(5) //Program is scrambled and does something different
var/rogue_type = pick(rogue_types)
var/datum/nanite_program/rogue = new rogue_type
- nanites.add_program(rogue, src)
+ nanites.add_program(null, rogue, src)
qdel(src)
/datum/nanite_program/proc/receive_signal(code, source)
diff --git a/code/modules/research/nanites/nanite_programs/buffing.dm b/code/modules/research/nanites/nanite_programs/buffing.dm
index e97f3b834d..a26cb7bd68 100644
--- a/code/modules/research/nanites/nanite_programs/buffing.dm
+++ b/code/modules/research/nanites/nanite_programs/buffing.dm
@@ -12,12 +12,6 @@
var/mob/living/carbon/human/H = host_mob
H.physiology.stun_mod *= 0.5
-/datum/nanite_program/nervous/active_effect()
- . = ..()
- if(iscarbon(host_mob))
- var/mob/living/carbon/C = host_mob
- C.adjustStaminaLoss(-2)
-
/datum/nanite_program/nervous/disable_passive_effect()
. = ..()
if(ishuman(host_mob))
@@ -133,4 +127,4 @@
/datum/nanite_program/mindshield/disable_passive_effect()
. = ..()
host_mob.remove_trait(TRAIT_MINDSHIELD, "nanites")
- host_mob.sec_hud_set_implants()
+ host_mob.sec_hud_set_implants()
\ No newline at end of file
diff --git a/code/modules/research/nanites/nanite_programs/healing.dm b/code/modules/research/nanites/nanite_programs/healing.dm
index 418039757c..df32a5d127 100644
--- a/code/modules/research/nanites/nanite_programs/healing.dm
+++ b/code/modules/research/nanites/nanite_programs/healing.dm
@@ -128,7 +128,7 @@
return
var/update = FALSE
for(var/obj/item/bodypart/L in parts)
- if(L.heal_damage(1/parts.len, 1/parts.len))
+ if(L.heal_damage(1/parts.len, 1/parts.len, only_robotic = TRUE, only_organic = FALSE))
update = TRUE
if(update)
host_mob.update_damage_overlays()
@@ -208,10 +208,8 @@
if(!..())
return
- playsound(host_mob, 'sound/machines/defib_zap.ogg', 75, 1, -1)
- if(check_revivable())
- host_mob.notify_ghost_cloning("Your heart is being defibrillated. Re-enter your corpse if you want to be revived!", source = src)
- addtimer(CALLBACK(src, .proc/zap), 30)
+ host_mob.notify_ghost_cloning("Your heart is being defibrillated by nanites. Re-enter your corpse if you want to be revived!")
+ addtimer(CALLBACK(src, .proc/zap), 50)
/datum/nanite_program/triggered/defib/proc/check_revivable()
if(!iscarbon(host_mob)) //nonstandard biology
@@ -219,22 +217,25 @@
var/mob/living/carbon/C = host_mob
if(C.suiciding || C.has_trait(TRAIT_NOCLONE) || C.hellbound) //can't revive
return FALSE
- if((world.time - C.timeofdeath) < 1800) //too late
+ if((world.time - C.timeofdeath) > 1800) //too late
return FALSE
- if((C.getBruteLoss() > 180) || (C.getFireLoss() > 180)) //too damaged
+ if((C.getBruteLoss() > 180) || (C.getFireLoss() > 180) || !C.can_be_revived()) //too damaged
return FALSE
if(!C.getorgan(/obj/item/organ/heart)) //what are we even shocking
return FALSE
var/obj/item/organ/brain/BR = C.getorgan(/obj/item/organ/brain)
if(QDELETED(BR) || BR.damaged_brain)
return FALSE
- if(!C.ckey && !C.get_ghost())
+ if(C.get_ghost())
return FALSE
return TRUE
/datum/nanite_program/triggered/defib/proc/zap()
- if(check_revivable() && host_mob.ckey)
- var/mob/living/carbon/C = host_mob
+ var/mob/living/carbon/C = host_mob
+ playsound(C, 'sound/machines/defib_charge.ogg', 50, 0)
+ sleep(30)
+ playsound(C, 'sound/machines/defib_zap.ogg', 50, 0)
+ if(check_revivable())
playsound(C, 'sound/machines/defib_success.ogg', 50, 0)
C.set_heartattack(FALSE)
C.revive()
@@ -246,5 +247,5 @@
C.adjustBrainLoss( max(0, ((1800 - tplus) / 1800 * 150)), 150)
log_game("[C] has been successfully defibrillated by nanites.")
else
- playsound(host_mob, 'sound/machines/defib_failed.ogg', 50, 0)
+ playsound(C, 'sound/machines/defib_failed.ogg', 50, 0)
diff --git a/code/modules/research/nanites/nanite_programs/suppression.dm b/code/modules/research/nanites/nanite_programs/suppression.dm
index 9d501e626a..3d89baba68 100644
--- a/code/modules/research/nanites/nanite_programs/suppression.dm
+++ b/code/modules/research/nanites/nanite_programs/suppression.dm
@@ -147,7 +147,7 @@
if(host_mob.stat == DEAD)
return
to_chat(host_mob, "You feel compelled to speak... ")
- host_mob.say(sentence)
+ host_mob.say(sentence, forced = "nanite speech")
/datum/nanite_program/triggered/voice
name = "Skull Echo"
diff --git a/code/modules/research/nanites/public_chamber.dm b/code/modules/research/nanites/public_chamber.dm
index 9630e6d1c8..c0d19e0375 100644
--- a/code/modules/research/nanites/public_chamber.dm
+++ b/code/modules/research/nanites/public_chamber.dm
@@ -4,12 +4,12 @@
circuit = /obj/item/circuitboard/machine/public_nanite_chamber
icon = 'icons/obj/machines/nanite_chamber.dmi'
icon_state = "nanite_chamber"
+ layer = ABOVE_WINDOW_LAYER
use_power = IDLE_POWER_USE
anchored = TRUE
density = TRUE
idle_power_usage = 50
active_power_usage = 300
- occupant_typecache = list(/mob/living)
var/cloud_id = 1
var/locked = FALSE
@@ -18,6 +18,10 @@
var/busy_icon_state
var/message_cooldown = 0
+/obj/machinery/public_nanite_chamber/Initialize()
+ . = ..()
+ occupant_typecache = GLOB.typecache_living
+
/obj/machinery/public_nanite_chamber/RefreshParts()
var/obj/item/circuitboard/machine/public_nanite_chamber/board = circuit
if(board)
@@ -171,4 +175,4 @@
/obj/machinery/public_nanite_chamber/MouseDrop_T(mob/target, mob/user)
if(user.stat || user.lying || !Adjacent(user) || !user.Adjacent(target) || !iscarbon(target) || !user.IsAdvancedToolUser())
return
- close_machine(target)
\ No newline at end of file
+ close_machine(target)
diff --git a/code/modules/research/rdconsole.dm b/code/modules/research/rdconsole.dm
index 0ba9312314..fb3c5908f5 100644
--- a/code/modules/research/rdconsole.dm
+++ b/code/modules/research/rdconsole.dm
@@ -197,6 +197,11 @@ Nothing else in the console has ID requirements.
locked = FALSE
return ..()
+/obj/machinery/computer/rdconsole/multitool_act(mob/user, obj/item/multitool/I)
+ var/lathe = linked_lathe && linked_lathe.multitool_act(user, I)
+ var/print = linked_imprinter && linked_imprinter.multitool_act(user, I)
+ return lathe || print
+
/obj/machinery/computer/rdconsole/proc/list_categories(list/categories, menu_num as num)
if(!categories)
return
@@ -268,7 +273,10 @@ Nothing else in the console has ID requirements.
/obj/machinery/computer/rdconsole/proc/ui_protolathe_header()
var/list/l = list()
l += ""
return l
@@ -277,7 +285,6 @@ Nothing else in the console has ID requirements.
var/list/l = list()
l += ui_protolathe_header()
l += "Browsing [selected_category]: "
- var/coeff = linked_lathe.efficiency_coeff
for(var/v in stored_research.researched_designs)
var/datum/design/D = stored_research.researched_designs[v]
if(!(selected_category in D.category)|| !(D.build_type & PROTOLATHE))
@@ -286,11 +293,13 @@ Nothing else in the console has ID requirements.
continue
var/temp_material
var/c = 50
- var/t
+ var/coeff = linked_lathe.efficiency_coeff
+ if(!linked_lathe.efficient_with(D.build_path))
+ coeff = 1
var/all_materials = D.materials + D.reagents_list
for(var/M in all_materials)
- t = linked_lathe.check_mat(D, M)
+ var/t = linked_lathe.check_mat(D, M)
temp_material += " | "
if (t < 1)
temp_material += "
[all_materials[M]/coeff] [CallMaterialName(M)] "
@@ -332,16 +341,17 @@ Nothing else in the console has ID requirements.
RDSCREEN_UI_LATHE_CHECK
var/list/l = list()
l += ui_protolathe_header()
- var/coeff = linked_lathe.efficiency_coeff
for(var/datum/design/D in matching_designs)
if(!(isnull(linked_lathe.allowed_department_flags) || (D.departmental_flags & linked_lathe.allowed_department_flags)))
continue
var/temp_material
var/c = 50
- var/t
var/all_materials = D.materials + D.reagents_list
+ var/coeff = linked_lathe.efficiency_coeff
+ if(!linked_lathe.efficient_with(D.build_path))
+ coeff = 1
for(var/M in all_materials)
- t = linked_lathe.check_mat(D, M)
+ var/t = linked_lathe.check_mat(D, M)
temp_material += " | "
if (t < 1)
temp_material += "
[all_materials[M]/coeff] [CallMaterialName(M)] "
@@ -364,11 +374,15 @@ Nothing else in the console has ID requirements.
/obj/machinery/computer/rdconsole/proc/ui_protolathe_materials() //Legacy code
RDSCREEN_UI_LATHE_CHECK
+ var/datum/component/material_container/mat_container = linked_lathe.materials.mat_container
+ if (!mat_container)
+ screen = RDSCREEN_PROTOLATHE
+ return ui_protolathe()
var/list/l = list()
l += ui_protolathe_header()
l += "
Material Storage: "
- for(var/mat_id in linked_lathe.materials.materials)
- var/datum/material/M = linked_lathe.materials.materials[mat_id]
+ for(var/mat_id in mat_container.materials)
+ var/datum/material/M = mat_container.materials[mat_id]
l += "* [M.amount] of [M.name]: "
if(M.amount >= MINERAL_MATERIAL_AMOUNT) l += "
Eject [RDSCREEN_NOBREAK]"
if(M.amount >= MINERAL_MATERIAL_AMOUNT*5) l += "
5x [RDSCREEN_NOBREAK]"
@@ -392,7 +406,10 @@ Nothing else in the console has ID requirements.
/obj/machinery/computer/rdconsole/proc/ui_circuit_header() //Legacy Code
var/list/l = list()
l += "
"
return l
@@ -419,7 +436,6 @@ Nothing else in the console has ID requirements.
l += ui_circuit_header()
l += "
Browsing [selected_category]: "
- var/coeff = linked_imprinter.efficiency_coeff
for(var/v in stored_research.researched_designs)
var/datum/design/D = stored_research.researched_designs[v]
if(!(selected_category in D.category) || !(D.build_type & IMPRINTER))
@@ -430,6 +446,9 @@ Nothing else in the console has ID requirements.
var/check_materials = TRUE
var/all_materials = D.materials + D.reagents_list
+ var/coeff = linked_imprinter.efficiency_coeff
+ if(!linked_imprinter.efficient_with(D.build_path))
+ coeff = 1
for(var/M in all_materials)
temp_materials += " | "
@@ -451,13 +470,15 @@ Nothing else in the console has ID requirements.
l += ui_circuit_header()
l += "
Search results: "
- var/coeff = linked_imprinter.efficiency_coeff
for(var/datum/design/D in matching_designs)
if(!(isnull(linked_imprinter.allowed_department_flags) || (D.departmental_flags & linked_imprinter.allowed_department_flags)))
continue
var/temp_materials
var/check_materials = TRUE
var/all_materials = D.materials + D.reagents_list
+ var/coeff = linked_imprinter.efficiency_coeff
+ if(!linked_imprinter.efficient_with(D.build_path))
+ coeff = 1
for(var/M in all_materials)
temp_materials += " | "
if (!linked_imprinter.check_mat(D, M))
@@ -485,11 +506,15 @@ Nothing else in the console has ID requirements.
/obj/machinery/computer/rdconsole/proc/ui_circuit_materials() //Legacy code!
RDSCREEN_UI_IMPRINTER_CHECK
+ var/datum/component/material_container/mat_container = linked_imprinter.materials.mat_container
+ if (!mat_container)
+ screen = RDSCREEN_IMPRINTER
+ return ui_circuit()
var/list/l = list()
l += ui_circuit_header()
l += "
Material Storage:"
- for(var/mat_id in linked_imprinter.materials.materials)
- var/datum/material/M = linked_imprinter.materials.materials[mat_id]
+ for(var/mat_id in mat_container.materials)
+ var/datum/material/M = mat_container.materials[mat_id]
l += "* [M.amount] of [M.name]: "
if(M.amount >= MINERAL_MATERIAL_AMOUNT) l += "
Eject [RDSCREEN_NOBREAK]"
if(M.amount >= MINERAL_MATERIAL_AMOUNT*5) l += "
5x [RDSCREEN_NOBREAK]"
@@ -906,7 +931,10 @@ Nothing else in the console has ID requirements.
if(QDELETED(linked_lathe))
say("No Protolathe Linked!")
return
- linked_lathe.materials.retrieve_sheets(text2num(ls["eject_amt"]), ls["ejectsheet"])
+ if(!linked_lathe.materials.mat_container)
+ say("No material storage linked to protolathe!")
+ return
+ linked_lathe.eject_sheets(ls["ejectsheet"], ls["eject_amt"])
//Circuit Imprinter Materials
if(ls["disposeI"]) //Causes the circuit imprinter to dispose of a single reagent (all of it)
if(QDELETED(linked_imprinter))
@@ -922,7 +950,10 @@ Nothing else in the console has ID requirements.
if(QDELETED(linked_imprinter))
say("No Circuit Imprinter Linked!")
return
- linked_imprinter.materials.retrieve_sheets(text2num(ls["eject_amt"]), ls["imprinter_ejectsheet"])
+ if(!linked_imprinter.materials.mat_container)
+ say("No material storage linked to circuit imprinter!")
+ return
+ linked_imprinter.eject_sheets(ls["imprinter_ejectsheet"], ls["eject_amt"])
if(ls["disk_slot"])
disk_slot_selected = text2num(ls["disk_slot"])
if(ls["research_node"])
diff --git a/code/modules/research/techweb/_techweb.dm b/code/modules/research/techweb/_techweb.dm
index 7c23609c21..f11b0ad709 100644
--- a/code/modules/research/techweb/_techweb.dm
+++ b/code/modules/research/techweb/_techweb.dm
@@ -355,5 +355,4 @@
allowed_buildtypes = SMELTER
/datum/techweb/specialized/autounlocking/exofab
- node_autounlock_ids = list("robotics", "mmi", "cyborg", "mecha_odysseus", "mech_gygax", "mech_durand", "mecha_phazon", "mecha", "mech_tools", "clown")
allowed_buildtypes = MECHFAB
diff --git a/code/modules/research/techweb/_techweb_node.dm b/code/modules/research/techweb/_techweb_node.dm
index f5fad6f3dd..875329a89c 100644
--- a/code/modules/research/techweb/_techweb_node.dm
+++ b/code/modules/research/techweb/_techweb_node.dm
@@ -15,7 +15,7 @@
var/list/boost_item_paths = list() //Associative list, path = list(point type = point_value).
var/autounlock_by_boost = TRUE //boosting this will autounlock this node.
var/export_price = 0 //Cargo export price.
- var/list/research_costs = list() //Point cost to research. type = amount
+ var/list/research_costs = 0 //Point cost to research. type = amount
var/category = "Misc" //Category
/datum/techweb_node/proc/get_price(datum/techweb/host)
diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm
index e5c29b366b..c704ede60c 100644
--- a/code/modules/research/techweb/all_nodes.dm
+++ b/code/modules/research/techweb/all_nodes.dm
@@ -1,16 +1,47 @@
//Current rate: 132500 research points in 90 minutes
-//Base Node
+//Base Nodes
/datum/techweb_node/base
id = "base"
starting_node = TRUE
display_name = "Basic Research Technology"
description = "NT default research technologies."
+ // Default research tech, prevents bricking
design_ids = list("basic_matter_bin", "basic_cell", "basic_scanning", "basic_capacitor", "basic_micro_laser", "micro_mani",
"destructive_analyzer", "circuit_imprinter", "experimentor", "rdconsole", "design_disk", "tech_disk", "rdserver", "rdservercontrol", "mechfab",
- "space_heater", "xlarge_beaker", "sec_rshot", "sec_bshot", "sec_slug", "sec_Islug", "sec_dart", "sec_38") //Default research tech, prevents bricking
+ "space_heater", "xlarge_beaker", "sec_rshot", "sec_bshot", "sec_slug", "sec_Islug", "sec_dart", "sec_38",
+ "rglass","plasteel","plastitanium","plasmaglass","plasmareinforcedglass","titaniumglass","plastitaniumglass")
+/datum/techweb_node/mmi
+ id = "mmi"
+ starting_node = TRUE
+ display_name = "Man Machine Interface"
+ description = "A slightly Frankensteinian device that allows human brains to interface natively with software APIs."
+ design_ids = list("mmi")
+
+/datum/techweb_node/cyborg
+ id = "cyborg"
+ starting_node = TRUE
+ display_name = "Cyborg Construction"
+ description = "Sapient robots with preloaded tool modules and programmable laws."
+ design_ids = list("robocontrol", "sflash", "borg_suit", "borg_head", "borg_chest", "borg_r_arm", "borg_l_arm", "borg_r_leg", "borg_l_leg", "borgupload",
+ "cyborgrecharger", "borg_upgrade_restart", "borg_upgrade_rename")
+
+/datum/techweb_node/mech
+ id = "mecha"
+ starting_node = TRUE
+ display_name = "Mechanical Exosuits"
+ description = "Mechanized exosuits that are several magnitudes stronger and more powerful than the average human."
+ design_ids = list("mecha_tracking", "mechacontrol", "mechapower", "mech_recharger", "ripley_chassis", "firefighter_chassis", "ripley_torso", "ripley_left_arm", "ripley_right_arm", "ripley_left_leg", "ripley_right_leg",
+ "ripley_main", "ripley_peri", "mech_hydraulic_clamp")
+
+/datum/techweb_node/mech_tools
+ id = "mech_tools"
+ starting_node = TRUE
+ display_name = "Basic Exosuit Equipment"
+ description = "Various tools fit for basic mech units"
+ design_ids = list("mech_drill", "mech_mscanner", "mech_extinguisher", "mech_cable_layer")
/////////////////////////Biotech/////////////////////////
/datum/techweb_node/biotech
@@ -27,7 +58,7 @@
display_name = "Advanced Biotechnology"
description = "Advanced Biotechnology"
prereq_ids = list("biotech")
- design_ids = list("piercesyringe", "smoke_machine", "plasmarefiller", "limbgrower", "defibrillator", "meta_beaker", "healthanalyzer_advanced")
+ design_ids = list("piercesyringe", "crewpinpointer", "smoke_machine", "plasmarefiller", "limbgrower", "defibrillator", "meta_beaker", "healthanalyzer_advanced","harvester","holobarrier_med")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
export_price = 5000
@@ -103,7 +134,7 @@
display_name = "Advanced Engineering"
description = "Pushing the boundaries of physics, one chainsaw-fist at a time."
prereq_ids = list("engineering", "emp_basic")
- design_ids = list("engine_goggles", "magboots", "weldingmask")
+ design_ids = list("engine_goggles", "magboots", "forcefield_projector", "weldingmask")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
export_price = 5000
@@ -159,7 +190,7 @@
display_name = "Applied Bluespace Research"
description = "Using bluespace to make things faster and better."
prereq_ids = list("bluespace_basic", "engineering")
- design_ids = list("bs_rped","minerbag_holding", "bluespacebeaker", "bluespacesyringe", "bluespacebodybag", "phasic_scanning", "roastingstick")
+ design_ids = list("bs_rped","minerbag_holding", "bluespacebeaker", "bluespacesyringe", "bluespacebodybag", "phasic_scanning", "roastingstick", "ore_silo")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 5000)
export_price = 5000
@@ -219,39 +250,20 @@
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
export_price = 5000
-/datum/techweb_node/mmi
- id = "mmi"
- display_name = "Man Machine Interface"
- description = "A slightly Frankensteinian device that allows human brains to interface natively with software APIs."
- prereq_ids = list("neural_programming")
- design_ids = list("mmi")
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
- export_price = 5000
-
/datum/techweb_node/posibrain
id = "posibrain"
display_name = "Positronic Brain"
description = "Applied usage of neural technology allowing for autonomous AI units based on special metallic cubes with conductive and processing circuits."
- prereq_ids = list("neural_programming", "mmi")
+ prereq_ids = list("neural_programming")
design_ids = list("mmi_posi")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
export_price = 5000
-/datum/techweb_node/cyborg
- id = "cyborg"
- display_name = "Cyborg Construction"
- description = "Sapient robots with preloaded tool modules and programmable laws."
- prereq_ids = list("mmi", "robotics")
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1000)
- export_price = 5000
- design_ids = list("robocontrol", "sflash", "borg_suit", "borg_head", "borg_chest", "borg_r_arm", "borg_l_arm", "borg_r_leg", "borg_l_leg", "borgupload",
- "cyborgrecharger", "borg_upgrade_restart", "borg_upgrade_rename")
-
/datum/techweb_node/cyborg_upg_util
id = "cyborg_upg_util"
display_name = "Cyborg Upgrades: Utility"
- description = "Utility upgrades for cybogs."
- prereq_ids = list("engineering", "cyborg")
+ description = "Utility upgrades for cyborgs."
+ prereq_ids = list("engineering")
design_ids = list("borg_upgrade_holding", "borg_upgrade_lavaproof", "borg_upgrade_thrusters", "borg_upgrade_selfrepair", "borg_upgrade_expand", "borg_upgrade_rped")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2000)
export_price = 5000
@@ -260,8 +272,8 @@
id = "cyborg_upg_med"
display_name = "Cyborg Upgrades: Medical"
description = "Medical upgrades for cyborgs."
- prereq_ids = list("adv_biotech", "cyborg")
- design_ids = list("borg_upgrade_defibrillator", "borg_upgrade_piercinghypospray", "borg_upgrade_highstrengthsynthesiser", "borg_upgrade_expandedsynthesiser", "borg_upgrade_pinpointer")
+ prereq_ids = list("adv_biotech")
+ design_ids = list("borg_upgrade_defibrillator", "borg_upgrade_piercinghypospray", "borg_upgrade_highstrengthsynthesiser", "borg_upgrade_expandedsynthesiser", "borg_upgrade_pinpointer", "borg_upgrade_surgicalprocessor")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2000)
export_price = 5000
@@ -415,7 +427,7 @@
display_name = "Subdermal Implants"
description = "Electronic implants buried beneath the skin."
prereq_ids = list("biotech")
- design_ids = list("implanter", "implantcase", "implant_chem", "implant_tracking")
+ design_ids = list("implanter", "implantcase", "implant_chem", "implant_tracking", "locator")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
export_price = 5000
@@ -423,7 +435,7 @@
id = "cyber_organs"
display_name = "Cybernetic Organs"
description = "We have the technology to rebuild him."
- prereq_ids = list("adv_biotech", "cyborg")
+ prereq_ids = list("adv_biotech")
design_ids = list("cybernetic_heart", "cybernetic_liver", "cybernetic_liver_u", "cybernetic_lungs")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
export_price = 5000
@@ -432,7 +444,7 @@
id = "cyber_implants"
display_name = "Cybernetic Implants"
description = "Electronic implants that improve humans."
- prereq_ids = list("adv_biotech", "cyborg", "adv_datatheory")
+ prereq_ids = list("adv_biotech", "adv_datatheory")
design_ids = list("ci-nutriment", "ci-breather", "ci-gloweyes", "ci-welding", "ci-medhud", "ci-sechud")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
export_price = 5000
@@ -501,15 +513,6 @@
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
export_price = 5000
-/datum/techweb_node/engine_hardsuit
- id = "engi_hardsuit"
- display_name = "EVA Construction Equipment"
- description = "Production of EVA construction equipment."
- design_ids = list("chardsuit", "hardsuitjpack")
- prereq_ids = list("adv_engi", "exp_tools")
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 5000)
- export_price = 5000
-
/////////////////////////weaponry tech/////////////////////////
/datum/techweb_node/weaponry
id = "weaponry"
@@ -611,21 +614,11 @@
export_price = 5000
////////////////////////mech technology////////////////////////
-/datum/techweb_node/mech
- id = "mecha"
- display_name = "Mechanical Exosuits"
- description = "Mechanized exosuits that are several magnitudes stronger and more powerful than the average human."
- prereq_ids = list("robotics", "adv_engi")
- design_ids = list("mecha_tracking", "mechacontrol", "mechapower", "mech_recharger", "ripley_chassis", "firefighter_chassis", "ripley_torso", "ripley_left_arm", "ripley_right_arm", "ripley_left_leg", "ripley_right_leg",
- "ripley_main", "ripley_peri", "mech_hydraulic_clamp")
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
- export_price = 5000
-
/datum/techweb_node/adv_mecha
id = "adv_mecha"
display_name = "Advanced Exosuits"
description = "For when you just aren't Gundam enough."
- prereq_ids = list("adv_robotics", "mecha")
+ prereq_ids = list("adv_robotics")
design_ids = list("mech_repair_droid")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
export_price = 5000
@@ -634,7 +627,7 @@
id = "mecha_odysseus"
display_name = "EXOSUIT: Odysseus"
description = "Odysseus exosuit designs"
- prereq_ids = list("mecha")
+ prereq_ids = list("base")
design_ids = list("odysseus_chassis", "odysseus_torso", "odysseus_head", "odysseus_left_arm", "odysseus_right_arm" ,"odysseus_left_leg", "odysseus_right_leg",
"odysseus_main", "odysseus_peri")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
@@ -670,20 +663,11 @@
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
export_price = 5000
-/datum/techweb_node/mech_tools
- id = "mech_tools"
- display_name = "Basic Exosuit Equipment"
- description = "Various tools fit for basic mech units"
- prereq_ids = list("mecha")
- design_ids = list("mech_drill", "mech_mscanner", "mech_extinguisher", "mech_cable_layer")
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
- export_price = 5000
-
/datum/techweb_node/adv_mecha_tools
id = "adv_mecha_tools"
display_name = "Advanced Exosuit Equipment"
description = "Tools for high level mech suits"
- prereq_ids = list("adv_mecha", "mech_tools")
+ prereq_ids = list("adv_mecha")
design_ids = list("mech_rcd")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
export_price = 5000
@@ -692,7 +676,7 @@
id = "med_mech_tools"
display_name = "Medical Exosuit Equipment"
description = "Tools for high level mech suits"
- prereq_ids = list("mecha", "adv_biotech", "mech_tools")
+ prereq_ids = list("adv_biotech")
design_ids = list("mech_sleeper", "mech_syringe_gun", "mech_medi_beam")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
export_price = 5000
@@ -710,7 +694,7 @@
id = "mecha_tools"
display_name = "Exosuit Weapon (LBX AC 10 \"Scattershot\")"
description = "An advanced piece of mech weaponry"
- prereq_ids = list("mecha", "ballistic_weapons")
+ prereq_ids = list("ballistic_weapons")
design_ids = list("mech_scattershot")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
export_price = 5000
@@ -719,7 +703,7 @@
id = "mech_carbine"
display_name = "Exosuit Weapon (FNX-99 \"Hades\" Carbine)"
description = "An advanced piece of mech weaponry"
- prereq_ids = list("mecha", "ballistic_weapons")
+ prereq_ids = list("ballistic_weapons")
design_ids = list("mech_carbine")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
export_price = 5000
@@ -728,7 +712,7 @@
id = "mmech_ion"
display_name = "Exosuit Weapon (MKIV Ion Heavy Cannon)"
description = "An advanced piece of mech weaponry"
- prereq_ids = list("mecha", "electronic_weapons", "emp_adv")
+ prereq_ids = list("electronic_weapons", "emp_adv")
design_ids = list("mech_ion")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
export_price = 5000
@@ -737,7 +721,7 @@
id = "mech_tesla"
display_name = "Exosuit Weapon (MKI Tesla Cannon)"
description = "An advanced piece of mech weaponry"
- prereq_ids = list("mecha", "electronic_weapons", "adv_power")
+ prereq_ids = list("electronic_weapons", "adv_power")
design_ids = list("mech_tesla")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
export_price = 5000
@@ -746,7 +730,7 @@
id = "mech_laser"
display_name = "Exosuit Weapon (CH-PS \"Immolator\" Laser)"
description = "A basic piece of mech weaponry"
- prereq_ids = list("mecha", "beam_weapons")
+ prereq_ids = list("beam_weapons")
design_ids = list("mech_laser")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
export_price = 5000
@@ -755,7 +739,7 @@
id = "mech_laser_heavy"
display_name = "Exosuit Weapon (CH-LC \"Solaris\" Laser Cannon)"
description = "An advanced piece of mech weaponry"
- prereq_ids = list("mecha", "adv_beam_weapons")
+ prereq_ids = list("adv_beam_weapons")
design_ids = list("mech_laser_heavy")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
export_price = 5000
@@ -764,7 +748,7 @@
id = "mech_grenade_launcher"
display_name = "Exosuit Weapon (SGL-6 Grenade Launcher)"
description = "An advanced piece of mech weaponry"
- prereq_ids = list("mecha", "explosive_weapons")
+ prereq_ids = list("explosive_weapons")
design_ids = list("mech_grenade_launcher")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
export_price = 5000
@@ -773,7 +757,7 @@
id = "mech_missile_rack"
display_name = "Exosuit Weapon (SRM-8 Missile Rack)"
description = "An advanced piece of mech weaponry"
- prereq_ids = list("mecha", "explosive_weapons")
+ prereq_ids = list("explosive_weapons")
design_ids = list("mech_missile_rack")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
export_price = 5000
@@ -782,7 +766,7 @@
id = "clusterbang_launcher"
display_name = "Exosuit Module (SOB-3 Clusterbang Launcher)"
description = "An advanced piece of mech weaponry"
- prereq_ids = list("mecha", "explosive_weapons")
+ prereq_ids = list("explosive_weapons")
design_ids = list("clusterbang_launcher")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
export_price = 5000
@@ -791,7 +775,7 @@
id = "mech_teleporter"
display_name = "Exosuit Module (Teleporter Module)"
description = "An advanced piece of mech Equipment"
- prereq_ids = list("mech_tools", "adv_bluespace")
+ prereq_ids = list("adv_bluespace")
design_ids = list("mech_teleporter")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
export_price = 5000
@@ -800,7 +784,7 @@
id = "mech_wormhole_gen"
display_name = "Exosuit Module (Localized Wormhole Generator)"
description = "An advanced piece of mech weaponry"
- prereq_ids = list("mecha", "mech_tools", "adv_bluespace")
+ prereq_ids = list("adv_bluespace")
design_ids = list("mech_wormhole_gen")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
export_price = 5000
@@ -809,7 +793,7 @@
id = "mech_taser"
display_name = "Exosuit Weapon (PBT \"Pacifier\" Mounted Taser)"
description = "A basic piece of mech weaponry"
- prereq_ids = list("mecha", "electronic_weapons")
+ prereq_ids = list("electronic_weapons")
design_ids = list("mech_taser")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
export_price = 5000
@@ -818,7 +802,7 @@
id = "mech_lmg"
display_name = "Exosuit Weapon (\"Ultra AC 2\" LMG)"
description = "An advanced piece of mech weaponry"
- prereq_ids = list("mecha", "ballistic_weapons")
+ prereq_ids = list("ballistic_weapons")
design_ids = list("mech_lmg")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
export_price = 5000
@@ -827,7 +811,7 @@
id = "mech_diamond_drill"
display_name = "Exosuit Diamond Drill"
description = "A diamond drill fit for a large exosuit"
- prereq_ids = list("mecha", "adv_mining")
+ prereq_ids = list("adv_mining")
design_ids = list("mech_diamond_drill")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
export_price = 5000
diff --git a/code/modules/research/xenobiology/crossbreeding/_corecross.dm b/code/modules/research/xenobiology/crossbreeding/_corecross.dm
index 43f23d013d..d9d58083cb 100644
--- a/code/modules/research/xenobiology/crossbreeding/_corecross.dm
+++ b/code/modules/research/xenobiology/crossbreeding/_corecross.dm
@@ -38,7 +38,7 @@ To add a crossbreed:
throw_range = 6
/obj/item/slimecross/Initialize()
- ..()
+ . = ..()
name = effect + " " + colour + " extract"
var/itemcolor = "#FFFFFF"
switch(colour)
@@ -94,7 +94,7 @@ To add a crossbreed:
var/list/list_reagents
/obj/item/slimecrossbeaker/Initialize()
- ..()
+ . = ..()
create_reagents(50)
if(list_reagents)
for(var/reagent in list_reagents)
diff --git a/code/modules/research/xenobiology/crossbreeding/_status_effects.dm b/code/modules/research/xenobiology/crossbreeding/_status_effects.dm
index ca334497a7..f56aa11bae 100644
--- a/code/modules/research/xenobiology/crossbreeding/_status_effects.dm
+++ b/code/modules/research/xenobiology/crossbreeding/_status_effects.dm
@@ -431,8 +431,9 @@ datum/status_effect/stabilized/blue/on_remove()
if(owner.fire_stacks <= 0)
to_chat(owner, "
[linked_extract] coats you in a watery goo, extinguishing the flames. ")
var/obj/O = owner.get_active_held_item()
- O.extinguish() //All shamelessly copied from water's reaction_obj, since I didn't seem to be able to get it here for some reason.
- O.acid_level = 0
+ if(O)
+ O.extinguish() //All shamelessly copied from water's reaction_obj, since I didn't seem to be able to get it here for some reason.
+ O.acid_level = 0
// Monkey cube
if(istype(O, /obj/item/reagent_containers/food/snacks/monkeycube))
to_chat(owner, "
[linked_extract] kept your hands wet! It makes [O] expand! ")
diff --git a/code/modules/research/xenobiology/crossbreeding/burning.dm b/code/modules/research/xenobiology/crossbreeding/burning.dm
index ba50229d9c..3b2131dd81 100644
--- a/code/modules/research/xenobiology/crossbreeding/burning.dm
+++ b/code/modules/research/xenobiology/crossbreeding/burning.dm
@@ -11,7 +11,7 @@ Burning extracts:
icon_state = "burning"
/obj/item/slimecross/burning/Initialize()
- ..()
+ . = ..()
create_reagents(10)
/obj/item/slimecross/burning/attack_self(mob/user)
@@ -300,7 +300,7 @@ Burning extracts:
if(!on || !pictures_left || !isturf(target.loc))
return
new /obj/effect/timestop(get_turf(target), 2, 50, list(user))
- ..()
+ . = ..()
var/text = "The camera fades away"
if(disk)
text += ", leaving the disk behind!"
diff --git a/code/modules/research/xenobiology/crossbreeding/charged.dm b/code/modules/research/xenobiology/crossbreeding/charged.dm
index 8ae7bf8d54..b664380d9e 100644
--- a/code/modules/research/xenobiology/crossbreeding/charged.dm
+++ b/code/modules/research/xenobiology/crossbreeding/charged.dm
@@ -12,7 +12,7 @@ Charged extracts:
icon_state = "charged"
/obj/item/slimecross/charged/Initialize()
- ..()
+ . = ..()
create_reagents(10)
/obj/item/slimecross/charged/attack_self(mob/user)
@@ -196,7 +196,7 @@ Charged extracts:
/obj/item/slimecross/charged/gold/Destroy()
STOP_PROCESSING(SSobj, src)
- ..()
+ return ..()
/obj/item/slimecross/charged/oil
colour = "oil"
@@ -315,7 +315,7 @@ Charged extracts:
var/uses = 2
/obj/item/slimepotion/spaceproof/afterattack(obj/item/clothing/C, mob/user, proximity)
- ..()
+ . = ..()
if(!uses)
qdel(src)
return
@@ -352,7 +352,7 @@ Charged extracts:
var/uses = 2
/obj/item/slimepotion/lavaproof/afterattack(obj/item/C, mob/user, proximity)
- ..()
+ . = ..()
if(!uses)
qdel(src)
return ..()
diff --git a/code/modules/research/xenobiology/crossbreeding/consuming.dm b/code/modules/research/xenobiology/crossbreeding/consuming.dm
index 2f6bf2a4f6..b51a7b2553 100644
--- a/code/modules/research/xenobiology/crossbreeding/consuming.dm
+++ b/code/modules/research/xenobiology/crossbreeding/consuming.dm
@@ -278,7 +278,7 @@ Consuming extracts:
var/colour = "#FFFFFF"
/obj/item/slime_cookie/pyrite/Initialize()
- ..()
+ . = ..()
var/tastemessage = "paint remover"
switch(rand(1,7))
if(1)
diff --git a/code/modules/research/xenobiology/crossbreeding/industrial.dm b/code/modules/research/xenobiology/crossbreeding/industrial.dm
index 95f87ede0a..2ab39eb06f 100644
--- a/code/modules/research/xenobiology/crossbreeding/industrial.dm
+++ b/code/modules/research/xenobiology/crossbreeding/industrial.dm
@@ -21,7 +21,7 @@ Industrial extracts:
return
/obj/item/slimecross/industrial/Initialize()
- ..()
+ . = ..()
create_reagents(100)
START_PROCESSING(SSobj,src)
diff --git a/code/modules/research/xenobiology/crossbreeding/prismatic.dm b/code/modules/research/xenobiology/crossbreeding/prismatic.dm
index 41063de3d1..65e66aa0e2 100644
--- a/code/modules/research/xenobiology/crossbreeding/prismatic.dm
+++ b/code/modules/research/xenobiology/crossbreeding/prismatic.dm
@@ -22,6 +22,7 @@ Prismatic extracts:
desc = "It's constantly wet with a pungent-smelling, clear chemical."
/obj/item/slimecross/prismatic/grey/afterattack(turf/target, mob/user, proximity)
+ . = ..()
if(!proximity)
return
if(istype(target) && target.color != initial(target.color))
diff --git a/code/modules/research/xenobiology/crossbreeding/recurring.dm b/code/modules/research/xenobiology/crossbreeding/recurring.dm
index 8a6e5c3cbb..b8ea51bae5 100644
--- a/code/modules/research/xenobiology/crossbreeding/recurring.dm
+++ b/code/modules/research/xenobiology/crossbreeding/recurring.dm
@@ -14,7 +14,7 @@ Recurring extracts:
var/max_cooldown = 5 //In sets of 2 seconds.
/obj/item/slimecross/recurring/Initialize()
- ..()
+ . = ..()
extract = new extract_type(src.loc)
visible_message("
[src] wraps a layer of goo around itself! ")
extract.name = name
@@ -39,7 +39,7 @@ Recurring extracts:
qdel(src)
/obj/item/slimecross/recurring/Destroy()
- ..()
+ . = ..()
STOP_PROCESSING(SSobj,src)
/obj/item/slimecross/recurring/grey
diff --git a/code/modules/research/xenobiology/crossbreeding/regenerative.dm b/code/modules/research/xenobiology/crossbreeding/regenerative.dm
index 7c8d8fa78f..6c7ea102bc 100644
--- a/code/modules/research/xenobiology/crossbreeding/regenerative.dm
+++ b/code/modules/research/xenobiology/crossbreeding/regenerative.dm
@@ -16,6 +16,7 @@ Regenerative extracts:
/obj/item/slimecross/regenerative/afterattack(atom/target,mob/user,prox)
+ . = ..()
if(!prox || !isliving(target))
return
var/mob/living/H = target
@@ -119,7 +120,7 @@ Regenerative extracts:
C.name = "fireproofed [C.name]"
C.remove_atom_colour(WASHABLE_COLOUR_PRIORITY)
C.add_atom_colour("#000080", FIXED_COLOUR_PRIORITY)
- C.max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT
+ C.max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
C.heat_protection = C.body_parts_covered
C.resistance_flags |= FIRE_PROOF
@@ -141,7 +142,7 @@ Regenerative extracts:
do_sparks(5,FALSE,target)
/obj/item/slimecross/regenerative/bluespace/Initialize()
- ..()
+ . = ..()
T = get_turf(src)
/obj/item/slimecross/regenerative/sepia
diff --git a/code/modules/research/xenobiology/crossbreeding/selfsustaining.dm b/code/modules/research/xenobiology/crossbreeding/selfsustaining.dm
index 93e5a8e739..0b27b1e26f 100644
--- a/code/modules/research/xenobiology/crossbreeding/selfsustaining.dm
+++ b/code/modules/research/xenobiology/crossbreeding/selfsustaining.dm
@@ -15,7 +15,7 @@ Self-sustaining extracts:
//Just divides into the actual item.
/obj/item/slimecross/selfsustaining/Initialize()
- . = ..()
+ ..()
visible_message("
The [src] shudders, and splits into four smaller extracts. ")
for(var/i = 0, i < 4, i++)
var/obj/item/autoslime/A = new /obj/item/autoslime(src.loc)
@@ -24,11 +24,11 @@ Self-sustaining extracts:
A.icon = icon
A.icon_state = icon_state
A.color = color
- qdel(src)
+ return INITIALIZE_HINT_QDEL
/obj/item/autoslime/Initialize()
name = "self-sustaining " + extract.name
- ..()
+ return ..()
/obj/item/autoslime/attack_self(mob/user)
var/reagentselect = input(user, "Choose the reagent the extract will produce.", "Self-sustaining Reaction") as null|anything in extract.activate_reagents
diff --git a/code/modules/research/xenobiology/crossbreeding/stabilized.dm b/code/modules/research/xenobiology/crossbreeding/stabilized.dm
index fe1b498667..0d589954d9 100644
--- a/code/modules/research/xenobiology/crossbreeding/stabilized.dm
+++ b/code/modules/research/xenobiology/crossbreeding/stabilized.dm
@@ -15,7 +15,7 @@ Stabilized extracts:
var/mob/living/owner
/obj/item/slimecross/stabilized/Initialize()
- ..()
+ . = ..()
START_PROCESSING(SSobj,src)
/obj/item/slimecross/stabilized/Destroy()
@@ -111,7 +111,7 @@ Stabilized extracts:
mob_type = pick(mob_spawn_pets)
/obj/item/slimecross/stabilized/gold/Initialize()
- ..()
+ . = ..()
generate_mobtype()
/obj/item/slimecross/stabilized/gold/attack_self(mob/user)
diff --git a/code/modules/research/xenobiology/xenobio_camera.dm b/code/modules/research/xenobiology/xenobio_camera.dm
index 26eefa0a68..5aebd1bafa 100644
--- a/code/modules/research/xenobiology/xenobio_camera.dm
+++ b/code/modules/research/xenobiology/xenobio_camera.dm
@@ -50,7 +50,7 @@
scan_action = new
potion_action = new
stored_slimes = list()
- listener = AddComponent(/datum/component/redirect, COMSIG_ATOM_CONTENTS_DEL, CALLBACK(src, .proc/on_contents_del))
+ listener = AddComponent(/datum/component/redirect, list(COMSIG_ATOM_CONTENTS_DEL = CALLBACK(src, .proc/on_contents_del)))
/obj/machinery/computer/camera_advanced/xenobio/Destroy()
stored_slimes = null
diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm
index b8cc61df97..e48e18c34c 100644
--- a/code/modules/research/xenobiology/xenobiology.dm
+++ b/code/modules/research/xenobiology/xenobiology.dm
@@ -391,7 +391,7 @@
/obj/item/slime_extract/lightpink/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type)
switch(activation_type)
if(SLIME_ACTIVATE_MINOR)
- var/obj/item/slimepotion/slime/docility/O = new(null, 1)
+ var/obj/item/slimepotion/slime/renaming/O = new(null, 1)
if(!user.put_in_active_hand(O))
O.forceMove(user.drop_location())
playsound(user, 'sound/effects/splat.ogg', 50, 1)
@@ -615,6 +615,7 @@
w_class = WEIGHT_CLASS_TINY
/obj/item/slimepotion/afterattack(obj/item/reagent_containers/target, mob/user , proximity)
+ . = ..()
if (istype(target))
to_chat(user, "
You cannot transfer [src] to [target]! It appears the potion must be given directly to a slime to absorb. " )
return
@@ -671,7 +672,7 @@
to_chat(user, "
You offer [src] to [SM]... ")
being_used = TRUE
- var/list/candidates = pollCandidatesForMob("Do you want to play as [SM.name]?", ROLE_ALIEN, null, ROLE_ALIEN, 50, SM, POLL_IGNORE_SENTIENCE_POTION) // see poll_ignore.dm
+ var/list/candidates = pollCandidatesForMob("Do you want to play as [SM.name]?", ROLE_SENTIENCE, null, ROLE_SENTIENCE, 50, SM, POLL_IGNORE_SENTIENCE_POTION) // see poll_ignore.dm
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
SM.key = C.key
@@ -725,11 +726,11 @@
if(SM.sentience_type != animal_type)
to_chat(user, "
You cannot transfer your consciousness to [SM]. " )
return ..()
- var/jb = jobban_isbanned(user, ROLE_ALIEN)
+ var/jb = jobban_isbanned(user, ROLE_MIND_TRANSFER)
if(QDELETED(src) || QDELETED(M) || QDELETED(user))
return
- if(jb) //ideally sentience and trasnference potions should be their own unique role.
+ if(jb)
to_chat(user, "
Your mind goes blank as you attempt to use the potion. ")
return
@@ -833,7 +834,7 @@
icon_state = "potyellow"
/obj/item/slimepotion/speed/afterattack(obj/C, mob/user)
- ..()
+ . = ..()
if(!istype(C))
to_chat(user, "
The potion can only be used on items or vehicles! ")
return
@@ -866,21 +867,21 @@
var/uses = 3
/obj/item/slimepotion/fireproof/afterattack(obj/item/clothing/C, mob/user)
- ..()
+ . = ..()
if(!uses)
qdel(src)
return
if(!istype(C))
to_chat(user, "
The potion can only be used on clothing! ")
return
- if(C.max_heat_protection_temperature == FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT)
+ if(C.max_heat_protection_temperature >= FIRE_IMMUNITY_MAX_TEMP_PROTECT)
to_chat(user, "
The [C] is already fireproof! ")
return ..()
to_chat(user, "
You slather the blue gunk over the [C], fireproofing it. ")
C.name = "fireproofed [C.name]"
C.remove_atom_colour(WASHABLE_COLOUR_PRIORITY)
C.add_atom_colour("#000080", FIXED_COLOUR_PRIORITY)
- C.max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT
+ C.max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
C.heat_protection = C.body_parts_covered
C.resistance_flags |= FIRE_PROOF
uses --
@@ -911,6 +912,39 @@
L.regenerate_icons()
qdel(src)
+/obj/item/slimepotion/slime/renaming
+ name = "renaming potion"
+ desc = "A potion that allows a self-aware being to change what name it subconciously presents to the world."
+ icon = 'icons/obj/chemical.dmi'
+ icon_state = "potgreen"
+
+ var/being_used = FALSE
+
+/obj/item/slimepotion/slime/renaming/attack(mob/living/M, mob/user)
+ if(being_used || !ismob(M))
+ return
+ if(!M.ckey) //only works on animals that aren't player controlled
+ to_chat(user, "
[M] is not self aware, and cannot pick its own name. ")
+ return
+
+ being_used = TRUE
+
+ to_chat(user, "
You offer [src] to [user]... ")
+
+ var/new_name = stripped_input(M, "What would you like your name to be?", "Input a name", M.real_name, MAX_NAME_LEN)
+
+ if(!new_name || QDELETED(src) || QDELETED(M) || new_name == M.real_name || !M.Adjacent(user))
+ being_used = FALSE
+ return
+
+ M.visible_message("
[M] has a new name, [new_name] .", "
Your old name of [M.real_name] fades away, and your new name [new_name] anchors itself in your mind. ")
+ message_admins("[ADMIN_LOOKUPFLW(user)] used [src] on [ADMIN_LOOKUPFLW(M)], letting them rename themselves into [new_name].")
+
+ // pass null as first arg to not update records or ID/PDA
+ M.fully_replace_character_name(null, new_name)
+
+ qdel(src)
+
/obj/item/slimepotion/slime/slimeradio
name = "bluespace radio potion"
desc = "A strange chemical that grants those who ingest it the ability to broadcast and receive subscape radio waves."
diff --git a/code/modules/ruins/lavalandruin_code/pizzaparty.dm b/code/modules/ruins/lavalandruin_code/pizzaparty.dm
new file mode 100644
index 0000000000..a7776f4e6a
--- /dev/null
+++ b/code/modules/ruins/lavalandruin_code/pizzaparty.dm
@@ -0,0 +1,9 @@
+//lavaland_surface_pizzaparty.dmm
+
+/obj/effect/spawner/lootdrop/pizzaparty
+ name = "pizza bomb spawner"
+ loot = list(/obj/item/pizzabox/margherita = 3,
+ /obj/item/pizzabox/meat = 3,
+ /obj/item/pizzabox/mushroom = 3,
+ /obj/item/pizzabox/bomb = 1)
+ lootdoubles = FALSE
diff --git a/code/modules/ruins/lavalandruin_code/puzzle.dm b/code/modules/ruins/lavalandruin_code/puzzle.dm
index 9e798507da..70b0545ded 100644
--- a/code/modules/ruins/lavalandruin_code/puzzle.dm
+++ b/code/modules/ruins/lavalandruin_code/puzzle.dm
@@ -154,6 +154,8 @@
var/tile_count = width * height
//Generate per tile icons
+ var/icon/base_icon = get_base_icon()
+
for(var/id in 1 to tile_count)
var/y = width - round((id - 1) / width)
var/x = ((id - 1) % width) + 1
@@ -163,7 +165,7 @@
var/y_start = 1 + ((y - 1) * world.icon_size)
var/y_end = y_start + world.icon_size - 1
- var/icon/T = get_base_icon()
+ var/icon/T = new(base_icon)
T.Crop(x_start,y_start,x_end,y_end)
puzzle_pieces["[id]"] = T
left_ids += id
@@ -290,10 +292,10 @@
prisoner.notransform = FALSE
prisoner = null
-//Some armor so it's harder to kill someone by mistake. EDITED - Hugboxed
+//Some armor so it's harder to kill someone by mistake.
/obj/structure/puzzle_element/prison
- resistance_flags = INDESTRUCTIBLE | ACID_PROOF | FIRE_PROOF | LAVA_PROOF
-
+ armor = list("melee" = 50, "bullet" = 50, "laser" = 50, "energy" = 50, "bomb" = 50, "bio" = 50, "rad" = 50, "fire" = 50, "acid" = 50)
+
/obj/structure/puzzle_element/prison/relaymove(mob/user)
return
@@ -348,4 +350,4 @@
//Move them into random block
var/obj/structure/puzzle_element/E = pick(cube.elements)
prisoner.forceMove(E)
- return TRUE
+ return TRUE
\ No newline at end of file
diff --git a/code/modules/ruins/lavalandruin_code/syndicate_base.dm b/code/modules/ruins/lavalandruin_code/syndicate_base.dm
new file mode 100644
index 0000000000..514ea35e8e
--- /dev/null
+++ b/code/modules/ruins/lavalandruin_code/syndicate_base.dm
@@ -0,0 +1,22 @@
+//lavaland_surface_syndicate_base1.dmm
+
+/obj/machinery/vending/syndichem
+ name = "\improper SyndiChem"
+ desc = "A vending machine full of grenades and grenade accessories. Sponsored by DonkCo(tm)."
+ req_access = list(ACCESS_SYNDICATE)
+ products = list(/obj/item/stack/cable_coil/random = 5,
+ /obj/item/assembly/igniter = 20,
+ /obj/item/assembly/prox_sensor = 5,
+ /obj/item/assembly/signaler = 5,
+ /obj/item/assembly/timer = 5,
+ /obj/item/assembly/voice = 5,
+ /obj/item/assembly/health = 5,
+ /obj/item/assembly/infra = 5,
+ /obj/item/grenade/chem_grenade = 5,
+ /obj/item/grenade/chem_grenade/large = 5,
+ /obj/item/grenade/chem_grenade/pyro = 5,
+ /obj/item/grenade/chem_grenade/cryo = 5,
+ /obj/item/grenade/chem_grenade/adv_release = 5,
+ /obj/item/reagent_containers/food/drinks/bottle/holywater = 1)
+ product_slogans = "It's not pyromania if you're getting paid!;You smell that? Plasma, son. Nothing else in the world smells like that.;I love the smell of Plasma in the morning."
+ resistance_flags = FIRE_PROOF
diff --git a/code/modules/ruins/objects_and_mobs/sin_ruins.dm b/code/modules/ruins/objects_and_mobs/sin_ruins.dm
index 7ad8a136f9..caa2ca7adf 100644
--- a/code/modules/ruins/objects_and_mobs/sin_ruins.dm
+++ b/code/modules/ruins/objects_and_mobs/sin_ruins.dm
@@ -129,7 +129,7 @@
hitsound = 'sound/weapons/bladeslice.ogg'
/obj/item/kitchen/knife/envy/afterattack(atom/movable/AM, mob/living/carbon/human/user, proximity)
- ..()
+ . = ..()
if(!proximity)
return
if(!istype(user))
diff --git a/code/modules/ruins/spaceruin_code/miracle.dm b/code/modules/ruins/spaceruin_code/miracle.dm
new file mode 100644
index 0000000000..075d43df25
--- /dev/null
+++ b/code/modules/ruins/spaceruin_code/miracle.dm
@@ -0,0 +1,4 @@
+/////////// miracle ruin
+
+/obj/item/storage/backpack/satchel/flat/secret/miracle_ruin
+ reward_all_of_these = list(/obj/item/gun/energy/pulse/prize)
diff --git a/code/modules/ruins/spaceruin_code/oldstation.dm b/code/modules/ruins/spaceruin_code/oldstation.dm
index b216145023..e72dbea044 100644
--- a/code/modules/ruins/spaceruin_code/oldstation.dm
+++ b/code/modules/ruins/spaceruin_code/oldstation.dm
@@ -47,3 +47,7 @@
SIGNIFICANT EVENTS OF NOTE 1: The primary radiation detectors were taken offline after 112 years due to power failure, secondary radiation detectors showed no residual \
radiation on station. Deduction, primarily detector was malfunctioning and was producing a radiation signal when there was none.
2: A data burst from a nearby Nanotrasen Space \
Station was received, this data burst contained research data that has been uploaded to our RnD labs.
3: Unknown invasion force has occupied Delta station."
+
+/obj/item/paper/fluff/ruins/oldstation/generator_manual
+ name = "S.U.P.E.R.P.A.C.M.A.N.-type portable generator manual"
+ info = "You can barely make out a faded sentence...
Wrench down the generator on top of a wire node connected to either a SMES input terminal or the power grid."
diff --git a/code/modules/shuttle/arrivals.dm b/code/modules/shuttle/arrivals.dm
index 60998215de..12e82e4212 100644
--- a/code/modules/shuttle/arrivals.dm
+++ b/code/modules/shuttle/arrivals.dm
@@ -11,6 +11,8 @@
callTime = INFINITY
ignitionTime = 50
+ movement_force = list("KNOCKDOWN" = 3, "THROW" = 0)
+
var/sound_played
var/damaged //too damaged to undock?
var/list/areas //areas in our shuttle
@@ -18,6 +20,7 @@
var/obj/machinery/requests_console/console
var/force_depart = FALSE
var/perma_docked = FALSE //highlander with RESPAWN??? OH GOD!!!
+ var/obj/docking_port/stationary/target_dock // for badminry
/obj/docking_port/mobile/arrivals/Initialize(mapload)
. = ..()
@@ -175,7 +178,10 @@
if(mode == SHUTTLE_IDLE)
if(console)
console.say(pickingup ? "Departing immediately for new employee pickup." : "Shuttle departing.")
- request(SSshuttle.getDock("arrivals_stationary")) //we will intentionally never return SHUTTLE_ALREADY_DOCKED
+ var/obj/docking_port/stationary/target = target_dock
+ if(QDELETED(target))
+ target = SSshuttle.getDock("arrivals_stationary")
+ request(target) //we will intentionally never return SHUTTLE_ALREADY_DOCKED
/obj/docking_port/mobile/arrivals/proc/RequireUndocked(mob/user)
if(mode == SHUTTLE_CALL || damaged)
diff --git a/code/modules/shuttle/assault_pod.dm b/code/modules/shuttle/assault_pod.dm
index 43b273914c..86132cf834 100644
--- a/code/modules/shuttle/assault_pod.dm
+++ b/code/modules/shuttle/assault_pod.dm
@@ -1,12 +1,11 @@
/obj/docking_port/mobile/assault_pod
name = "assault pod"
id = "steel_rain"
- timid = FALSE
dwidth = 3
width = 7
height = 7
-/obj/docking_port/mobile/assault_pod/request()
+/obj/docking_port/mobile/assault_pod/request(obj/docking_port/stationary/S)
if(!(z in SSmapping.levels_by_trait(ZTRAIT_STATION))) //No launching pods that have already launched
return ..()
diff --git a/code/modules/shuttle/computer.dm b/code/modules/shuttle/computer.dm
index ee491f17be..0c957919a1 100644
--- a/code/modules/shuttle/computer.dm
+++ b/code/modules/shuttle/computer.dm
@@ -69,3 +69,6 @@
obj_flags |= EMAGGED
to_chat(user, "
You fried the consoles ID checking system. ")
+/obj/machinery/computer/shuttle/proc/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock, idnum, override=FALSE)
+ if(port && (shuttleId == initial(shuttleId) || override))
+ shuttleId = port.id
\ No newline at end of file
diff --git a/code/modules/shuttle/docking.dm b/code/modules/shuttle/docking.dm
index d881396092..7295ab2832 100644
--- a/code/modules/shuttle/docking.dm
+++ b/code/modules/shuttle/docking.dm
@@ -1,4 +1,4 @@
-//this is the main proc. It instantly moves our mobile port to stationary port new_dock
+/// This is the main proc. It instantly moves our mobile port to stationary port `new_dock`.
/obj/docking_port/mobile/proc/initiate_docking(obj/docking_port/stationary/new_dock, movement_direction, force=FALSE)
// Crashing this ship with NO SURVIVORS
@@ -8,8 +8,10 @@
if(!force)
if(!check_dock(new_dock))
+ remove_ripples()
return DOCKING_BLOCKED
if(!canMove())
+ remove_ripples()
return DOCKING_IMMOBILIZED
var/obj/docking_port/stationary/old_dock = get_docked()
@@ -32,12 +34,7 @@
// The underlying old area is the area assumed to be under the shuttle's starting location
// If it no longer/has never existed it will be created
- var/area/underlying_old_area
- for(var/i in GLOB.sortedAreas) // Locate grabs subtypes and we want a particular type
- var/area/place = i
- if(place.type == underlying_area_type)
- underlying_old_area = place
- break
+ var/area/underlying_old_area = GLOB.areas_by_type[underlying_area_type]
if(!underlying_old_area)
underlying_old_area = new underlying_area_type(null)
@@ -54,10 +51,9 @@
var/list/moved_atoms = list() //Everything not a turf that gets moved in the shuttle
var/list/areas_to_move = list() //unique assoc list of areas on turfs being moved
- remove_ripples()
-
. = preflight_check(old_turfs, new_turfs, areas_to_move, rotation)
if(.)
+ remove_ripples()
return
/*******************************************Hiding turfs if necessary*******************************************/
@@ -75,10 +71,15 @@
if(!force)
if(!check_dock(new_dock))
+ remove_ripples()
return DOCKING_BLOCKED
if(!canMove())
+ remove_ripples()
return DOCKING_IMMOBILIZED
+ // Moving to the new location will trample the ripples there at the exact
+ // same time any mobs there are trampled, to avoid any discrepancy where
+ // the ripples go away before it is safe.
takeoff(old_turfs, new_turfs, moved_atoms, rotation, movement_direction, old_dock, underlying_old_area)
CHECK_TICK
@@ -97,6 +98,8 @@
new_dock.last_dock_time = world.time
setDir(new_dock.dir)
+ // remove any stragglers just in case, and clear the list
+ remove_ripples()
return DOCKING_SUCCESS
/obj/docking_port/mobile/proc/preflight_check(list/old_turfs, list/new_turfs, list/areas_to_move, rotation)
diff --git a/code/modules/shuttle/elevator.dm b/code/modules/shuttle/elevator.dm
index 959cc2dae7..de5d88ee17 100644
--- a/code/modules/shuttle/elevator.dm
+++ b/code/modules/shuttle/elevator.dm
@@ -1,11 +1,10 @@
/obj/docking_port/mobile/elevator
name = "elevator"
id = "elevator"
- timid = FALSE
dwidth = 3
width = 7
height = 7
movement_force = list("KNOCKDOWN" = 0, "THROW" = 0)
/obj/docking_port/mobile/elevator/request(obj/docking_port/stationary/S) //No transit, no ignition, just a simple up/down platform
- initiate_docking(S, TRUE)
\ No newline at end of file
+ initiate_docking(S, force=TRUE)
diff --git a/code/modules/shuttle/emergency.dm b/code/modules/shuttle/emergency.dm
index 20b8c26d83..cbc6810143 100644
--- a/code/modules/shuttle/emergency.dm
+++ b/code/modules/shuttle/emergency.dm
@@ -245,7 +245,7 @@
/obj/docking_port/mobile/emergency/proc/is_hijacked()
var/has_people = FALSE
-
+ var/hijacker_present = FALSE
for(var/mob/living/player in GLOB.player_list)
if(player.mind)
if(player.stat != DEAD)
@@ -258,10 +258,23 @@
if(shuttle_areas[get_area(player)])
has_people = TRUE
var/location = get_turf(player.mind.current)
- if(!(player.mind.has_antag_datum(/datum/antagonist)) && !istype(location, /turf/open/floor/plasteel/shuttle/red) && !istype(location, /turf/open/floor/mineral/plastitanium/brig))
+ //Non-antag present. Can't hijack.
+ if(!(player.mind.has_antag_datum(/datum/antagonist)) && !istype(location, /turf/open/floor/plasteel/shuttle/red) && !istype(location, /turf/open/floor/mineral/plastitanium/red/brig))
+ return FALSE
+ //Antag present, doesn't stop but let's see if we actually want to hijack
+ var/prevent = FALSE
+ for(var/datum/antagonist/A in player.mind.antag_datums)
+ if(A.can_hijack == HIJACK_HIJACKER)
+ hijacker_present = TRUE
+ prevent = FALSE
+ break //If we have both prevent and hijacker antags assume we want to hijack.
+ else if(A.can_hijack == HIJACK_PREVENT)
+ prevent = TRUE
+ if(prevent)
return FALSE
- return has_people
+
+ return has_people && hijacker_present
/obj/docking_port/mobile/emergency/proc/ShuttleDBStuff()
set waitfor = FALSE
@@ -409,17 +422,16 @@
/obj/docking_port/mobile/pod
name = "escape pod"
id = "pod"
- timid = FALSE
dwidth = 1
width = 3
height = 4
launch_status = UNLAUNCHED
-/obj/docking_port/mobile/pod/request()
- var/obj/machinery/computer/shuttle/S = getControlConsole()
- if(!istype(S, /obj/machinery/computer/shuttle/pod))
+/obj/docking_port/mobile/pod/request(obj/docking_port/stationary/S)
+ var/obj/machinery/computer/shuttle/C = getControlConsole()
+ if(!istype(C, /obj/machinery/computer/shuttle/pod))
return ..()
- if(GLOB.security_level >= SEC_LEVEL_RED || (S && (S.obj_flags & EMAGGED)))
+ if(GLOB.security_level >= SEC_LEVEL_RED || (C && (C.obj_flags & EMAGGED)))
if(launch_status == UNLAUNCHED)
launch_status = EARLY_LAUNCHED
return ..()
@@ -427,33 +439,12 @@
to_chat(usr, "
Escape pods will only launch during \"Code Red\" security alert. ")
return TRUE
-/obj/docking_port/mobile/pod/Initialize()
- . = ..()
- var/static/i = 1
- if(id == initial(id))
- id = "[initial(id)][i]"
- if(name == initial(name))
- name = "[initial(name)] [i]"
-
- for(var/k in shuttle_areas)
- var/area/place = k
- for(var/obj/machinery/computer/shuttle/pod/pod_comp in place)
- if(pod_comp.shuttleId == initial(pod_comp.shuttleId))
- pod_comp.shuttleId = id
- if(pod_comp.possible_destinations == initial(pod_comp.possible_destinations))
- pod_comp.possible_destinations = "pod_lavaland[i]"
-
- i++
-
-
-
/obj/docking_port/mobile/pod/cancel()
return
/obj/machinery/computer/shuttle/pod
name = "pod control computer"
admin_controlled = 1
- shuttleId = "pod"
possible_destinations = "pod_asteroid"
icon = 'icons/obj/terminals.dmi'
icon_state = "dorm_available"
@@ -470,6 +461,11 @@
obj_flags |= EMAGGED
to_chat(user, "
You fry the pod's alert level checking system. ")
+/obj/machinery/computer/shuttle/pod/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock, idnum, override=FALSE)
+ . = ..()
+ if(possible_destinations == initial(possible_destinations) || override)
+ possible_destinations = "pod_lavaland[idnum]"
+
/obj/docking_port/stationary/random
name = "escape pod"
id = "pod"
@@ -575,6 +571,10 @@
SSshuttle.emergency = current_emergency
SSshuttle.backup_shuttle = src
+/obj/docking_port/mobile/emergency/shuttle_build/register()
+ . = ..()
+ initiate_docking(SSshuttle.getDock("emergency_home"))
+
#undef TIME_LEFT
#undef ENGINES_START_TIME
#undef ENGINES_STARTED
diff --git a/code/modules/shuttle/manipulator.dm b/code/modules/shuttle/manipulator.dm
index ee12d7cf37..dcfb0793b5 100644
--- a/code/modules/shuttle/manipulator.dm
+++ b/code/modules/shuttle/manipulator.dm
@@ -40,6 +40,10 @@
add_overlay(hologram_projection)
add_overlay(hologram_ship)
+/obj/machinery/shuttle_manipulator/can_interact(mob/user)
+ // Only admins can use this, but they can use it from anywhere
+ return user.client && check_rights_for(user.client, R_ADMIN)
+
/obj/machinery/shuttle_manipulator/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.admin_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
@@ -107,17 +111,23 @@
data["shuttles"] = list()
for(var/i in SSshuttle.mobile)
var/obj/docking_port/mobile/M = i
+ var/timeleft = M.timeLeft(1)
var/list/L = list()
L["name"] = M.name
L["id"] = M.id
L["timer"] = M.timer
L["timeleft"] = M.getTimerStr()
- var/can_fast_travel = FALSE
- if(M.timer && M.timeLeft() >= 50)
- can_fast_travel = TRUE
- L["can_fast_travel"] = can_fast_travel
- L["mode"] = capitalize(shuttlemode2str(M.mode))
- L["status"] = M.getStatusText()
+ if (timeleft > 1 HOURS)
+ L["timeleft"] = "Infinity"
+ L["can_fast_travel"] = M.timer && timeleft >= 50
+ L["can_fly"] = TRUE
+ if(istype(M, /obj/docking_port/mobile/emergency))
+ L["can_fly"] = FALSE
+ else if(!M.destination)
+ L["can_fast_travel"] = FALSE
+ if (M.mode != SHUTTLE_IDLE)
+ L["mode"] = capitalize(shuttlemode2str(M.mode))
+ L["status"] = M.getDbgStatusText()
if(M == existing_shuttle)
data["existing_shuttle"] = L
@@ -149,10 +159,19 @@
user.forceMove(get_turf(M))
. = TRUE
break
+
+ if("fly")
+ for(var/i in SSshuttle.mobile)
+ var/obj/docking_port/mobile/M = i
+ if(M.id == params["id"])
+ . = TRUE
+ M.admin_fly_shuttle(user)
+ break
+
if("fast_travel")
for(var/i in SSshuttle.mobile)
var/obj/docking_port/mobile/M = i
- if(M.id == params["id"] && M.timer && M.timeLeft() >= 50)
+ if(M.id == params["id"] && M.timer && M.timeLeft(1) >= 50)
M.setTimer(50)
. = TRUE
message_admins("[key_name_admin(usr)] fast travelled [M]")
@@ -194,7 +213,8 @@
preview_template = null
if(!preview_shuttle)
- load_template(loading_template)
+ if(load_template(loading_template))
+ preview_shuttle.linkup(loading_template, destination_port)
preview_template = loading_template
// get the existing shuttle information, if any
@@ -210,8 +230,7 @@
D = existing_shuttle.get_docked()
if(!D)
- var/m = "No dock found for preview shuttle ([preview_template.name]), aborting."
- throw EXCEPTION(m)
+ CRASH("No dock found for preview shuttle ([preview_template.name]), aborting.")
var/result = preview_shuttle.canDock(D)
// truthy value means that it cannot dock for some reason
@@ -228,7 +247,7 @@
preview_shuttle.movement_force = list("KNOCKDOWN" = 0, "THROW" = 0)
preview_shuttle.initiate_docking(D)
preview_shuttle.movement_force = force_memory
-
+
. = preview_shuttle
// Shuttle state involves a mode and a timer based on world.time, so
@@ -245,11 +264,11 @@
existing_shuttle = null
selected = null
-/obj/machinery/shuttle_manipulator/proc/load_template(
- datum/map_template/shuttle/S)
+/obj/machinery/shuttle_manipulator/proc/load_template(datum/map_template/shuttle/S)
+ . = FALSE
// load shuttle template, centred at shuttle import landmark,
var/turf/landmark_turf = get_turf(locate(/obj/effect/landmark/shuttle_import) in GLOB.landmarks_list)
- S.load(landmark_turf, centered = TRUE)
+ S.load(landmark_turf, centered = TRUE, register = FALSE)
var/affected = S.get_affected_turfs(landmark_turf, centered=TRUE)
@@ -262,18 +281,10 @@
for(var/T in affected)
for(var/obj/docking_port/P in T)
if(istype(P, /obj/docking_port/mobile))
- var/obj/docking_port/mobile/M = P
found++
if(found > 1)
qdel(P, force=TRUE)
log_world("Map warning: Shuttle Template [S.mappath] has multiple mobile docking ports.")
- else if(!M.timid)
- // The shuttle template we loaded isn't "timid" which means
- // it's already registered with the shuttles subsystem.
- // This is a bad thing.
- stack_trace("Template [S] is non-timid! Unloading.")
- M.jumpToNullSpace()
- return
else
preview_shuttle = P
if(istype(P, /obj/docking_port/stationary))
@@ -289,8 +300,74 @@
return
//Everything fine
S.on_bought()
+ return TRUE
/obj/machinery/shuttle_manipulator/proc/unload_preview()
if(preview_shuttle)
preview_shuttle.jumpToNullSpace()
preview_shuttle = null
+
+/obj/docking_port/mobile/proc/admin_fly_shuttle(mob/user)
+ var/list/options = list()
+
+ for(var/port in SSshuttle.stationary)
+ if (istype(port, /obj/docking_port/stationary/transit))
+ continue // please don't do this
+ var/obj/docking_port/stationary/S = port
+ if (canDock(S) == SHUTTLE_CAN_DOCK)
+ options[S.name || S.id] = S
+
+ options += "--------"
+ options += "Infinite Transit"
+ options += "Delete Shuttle"
+ options += "Into The Sunset (delete & greentext 'escape')"
+
+ var/selection = input(user, "Select where to fly [name || id]:", "Fly Shuttle") as null|anything in options
+ if(!selection)
+ return
+
+ switch(selection)
+ if("Infinite Transit")
+ destination = null
+ mode = SHUTTLE_IGNITING
+ setTimer(ignitionTime)
+
+ if("Delete Shuttle")
+ if(alert(user, "Really delete [name || id]?", "Delete Shuttle", "Cancel", "Really!") != "Really!")
+ return
+ jumpToNullSpace()
+
+ if("Into The Sunset (delete & greentext 'escape')")
+ if(alert(user, "Really delete [name || id] and greentext escape objectives?", "Delete Shuttle", "Cancel", "Really!") != "Really!")
+ return
+ intoTheSunset()
+
+ else
+ if(options[selection])
+ request(options[selection])
+
+/obj/docking_port/mobile/emergency/admin_fly_shuttle(mob/user)
+ return // use the existing verbs for this
+
+/obj/docking_port/mobile/arrivals/admin_fly_shuttle(mob/user)
+ switch(alert(user, "Would you like to fly the arrivals shuttle once or change its destination?", "Fly Shuttle", "Fly", "Retarget", "Cancel"))
+ if("Cancel")
+ return
+ if("Fly")
+ return ..()
+
+ var/list/options = list()
+
+ for(var/port in SSshuttle.stationary)
+ if (istype(port, /obj/docking_port/stationary/transit))
+ continue // please don't do this
+ var/obj/docking_port/stationary/S = port
+ if (canDock(S) == SHUTTLE_CAN_DOCK)
+ options[S.name || S.id] = S
+
+ var/selection = input(user, "Select the new arrivals destination:", "Fly Shuttle") as null|anything in options
+ if(!selection)
+ return
+ target_dock = options[selection]
+ if(!QDELETED(target_dock))
+ destination = target_dock
diff --git a/code/modules/shuttle/navigation_computer.dm b/code/modules/shuttle/navigation_computer.dm
index 19ef427f27..1b24823743 100644
--- a/code/modules/shuttle/navigation_computer.dm
+++ b/code/modules/shuttle/navigation_computer.dm
@@ -57,10 +57,9 @@
shuttle_port = null
return
- eyeobj = new /mob/camera/aiEye/remote/shuttle_docker()
+ eyeobj = new /mob/camera/aiEye/remote/shuttle_docker(null, src)
var/mob/camera/aiEye/remote/shuttle_docker/the_eye = eyeobj
- the_eye.origin = src
- the_eye.dir = shuttle_port.dir
+ the_eye.setDir(shuttle_port.dir)
var/turf/origin = locate(shuttle_port.x + x_offset, shuttle_port.y + y_offset, shuttle_port.z)
for(var/V in shuttle_port.shuttle_areas)
var/area/A = V
@@ -73,7 +72,7 @@
I.loc = locate(origin.x + x_off, origin.y + y_off, origin.z) //we have to set this after creating the image because it might be null, and images created in nullspace are immutable.
I.layer = ABOVE_NORMAL_TURF_LAYER
I.plane = 0
- I.mouse_opacity = 0
+ I.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
the_eye.placement_images[I] = list(x_off, y_off)
/obj/machinery/computer/camera_advanced/shuttle_docker/give_eye_control(mob/user)
@@ -137,7 +136,7 @@
my_port.dheight = shuttle_port.dheight
my_port.dwidth = shuttle_port.dwidth
my_port.hidden = shuttle_port.hidden
- my_port.dir = the_eye.dir
+ my_port.setDir(the_eye.dir)
my_port.forceMove(locate(eyeobj.x - x_offset, eyeobj.y - y_offset, eyeobj.z))
if(current_user.client)
current_user.client.images -= the_eye.placed_images
@@ -166,7 +165,7 @@
/obj/machinery/computer/camera_advanced/shuttle_docker/proc/rotateLandingSpot()
var/mob/camera/aiEye/remote/shuttle_docker/the_eye = eyeobj
var/list/image_cache = the_eye.placement_images
- the_eye.dir = turn(the_eye.dir, -90)
+ the_eye.setDir(turn(the_eye.dir, -90))
for(var/i in 1 to image_cache.len)
var/image/pic = image_cache[i]
var/list/coords = image_cache[pic]
@@ -247,12 +246,23 @@
current_user.client.images -= remove_images
current_user.client.images += add_images
+/obj/machinery/computer/camera_advanced/shuttle_docker/proc/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock, idnum, override=FALSE)
+ if(port && (shuttleId == initial(shuttleId) || override))
+ shuttleId = port.id
+ shuttlePortId = "[port.id]_custom"
+ if(dock)
+ jumpto_ports += dock.id
+
/mob/camera/aiEye/remote/shuttle_docker
visible_icon = FALSE
- use_static = FALSE
+ use_static = USE_STATIC_NONE
var/list/placement_images = list()
var/list/placed_images = list()
+/mob/camera/aiEye/remote/shuttle_docker/Initialize(mapload, obj/machinery/computer/camera_advanced/origin)
+ src.origin = origin
+ return ..()
+
/mob/camera/aiEye/remote/shuttle_docker/setLoc(T)
..()
var/obj/machinery/computer/camera_advanced/shuttle_docker/console = origin
diff --git a/code/modules/shuttle/on_move.dm b/code/modules/shuttle/on_move.dm
index 9767334166..9942446868 100644
--- a/code/modules/shuttle/on_move.dm
+++ b/code/modules/shuttle/on_move.dm
@@ -183,10 +183,12 @@ All ShuttleMove procs go here
/obj/machinery/door/airlock/afterShuttleMove(turf/oldT, list/movement_force, shuttle_dir, shuttle_preferred_direction, move_dir, rotation)
. = ..()
+ var/current_area = get_area(src)
for(var/obj/machinery/door/airlock/A in orange(1, src)) // does not include src
- // Cycle linking is only disabled if we are actually adjacent to another airlock
- shuttledocked = TRUE
- A.shuttledocked = TRUE
+ if(get_area(A) != current_area) // does not include double-wide airlocks unless actually docked
+ // Cycle linking is only disabled if we are actually adjacent to another airlock
+ shuttledocked = TRUE
+ A.shuttledocked = TRUE
/obj/machinery/camera/beforeShuttleMove(turf/newT, rotation, move_mode, obj/docking_port/mobile/moving_dock)
. = ..()
diff --git a/code/modules/shuttle/shuttle.dm b/code/modules/shuttle/shuttle.dm
index 32c186023b..307e714316 100644
--- a/code/modules/shuttle/shuttle.dm
+++ b/code/modules/shuttle/shuttle.dm
@@ -253,11 +253,6 @@
var/list/movement_force = list("KNOCKDOWN" = 3, "THROW" = 2)
- // A timid shuttle will not register itself with the shuttle subsystem
- // All shuttle templates MUST be timid, imports will fail if they're not
- // Shuttle defined already on the map MUST NOT be timid, or they won't work
- var/timid = TRUE
-
var/list/ripples = list()
var/engine_coeff = 1 //current engine coeff
var/current_engines = 0 //current engine power
@@ -280,8 +275,6 @@
/obj/docking_port/mobile/Initialize(mapload)
. = ..()
- if(!timid)
- register()
if(!id)
id = "[SSshuttle.mobile.len]"
@@ -303,6 +296,25 @@
highlight("#0f0")
#endif
+// Called after the shuttle is loaded from template
+/obj/docking_port/mobile/proc/linkup(datum/map_template/shuttle/template, obj/docking_port/stationary/dock)
+ var/list/static/shuttle_id = list()
+ var/idnum = ++shuttle_id[template]
+ if(idnum > 1)
+ if(id == initial(id))
+ id = "[id][idnum]"
+ if(name == initial(name))
+ name = "[name] [idnum]"
+ for(var/i in shuttle_areas)
+ var/area/place = i
+ for(var/obj/machinery/computer/shuttle/comp in place)
+ comp.connect_to_shuttle(src, dock, idnum)
+ for(var/obj/machinery/computer/camera_advanced/shuttle_docker/comp in place)
+ comp.connect_to_shuttle(src, dock, idnum)
+ for(var/obj/machinery/status_display/shuttle/sd in place)
+ sd.connect_to_shuttle(src, dock, idnum)
+
+
//this is a hook for custom behaviour. Maybe at some point we could add checks to see if engines are intact
/obj/docking_port/mobile/proc/canMove()
return TRUE
@@ -399,8 +411,9 @@
mode = SHUTTLE_IDLE
return
previous = null
-// if(!destination)
-// return
+ if(!destination)
+ // sent to transit with no destination -> unlimited timer
+ timer = INFINITY
var/obj/docking_port/stationary/S0 = get_docked()
var/obj/docking_port/stationary/S1 = assigned_transit
if(S1)
@@ -425,12 +438,7 @@
var/list/old_turfs = return_ordered_turfs(x, y, z, dir)
- var/area/underlying_area
- for(var/i in GLOB.sortedAreas)
- var/area/place = i
- if(place.type == underlying_area_type)
- underlying_area = place
- break
+ var/area/underlying_area = GLOB.areas_by_type[underlying_area_type]
if(!underlying_area)
underlying_area = new underlying_area_type(null)
@@ -452,6 +460,22 @@
qdel(src, force=TRUE)
+/obj/docking_port/mobile/proc/intoTheSunset()
+ // Loop over mobs
+ for(var/t in return_turfs())
+ var/turf/T = t
+ for(var/mob/living/M in T.GetAllContents())
+ // If they have a mind and they're not in the brig, they escaped
+ if(M.mind && !istype(t, /turf/open/floor/plasteel/shuttle/red) && !istype(t, /turf/open/floor/mineral/plastitanium/red/brig))
+ M.mind.force_escaped = TRUE
+ // Ghostize them and put them in nullspace stasis (for stat & possession checks)
+ M.notransform = TRUE
+ M.ghostize(FALSE)
+ M.moveToNullspace()
+
+ // Now that mobs are stowed, delete the shuttle
+ jumpToNullSpace()
+
/obj/docking_port/mobile/proc/create_ripples(obj/docking_port/stationary/S1, animate_time)
var/list/turfs = ripple_area(S1)
for(var/t in turfs)
@@ -630,7 +654,9 @@
return "--:--"
var/timeleft = timeLeft()
- if(timeleft > 0)
+ if(timeleft > 1 HOURS)
+ return "--:--"
+ else if(timeleft > 0)
return "[add_zero(num2text((timeleft / 60) % 60),2)]:[add_zero(num2text(timeleft % 60), 2)]"
else
return "00:00"
@@ -638,6 +664,23 @@
/obj/docking_port/mobile/proc/getStatusText()
var/obj/docking_port/stationary/dockedAt = get_docked()
+
+ if(istype(dockedAt, /obj/docking_port/stationary/transit))
+ if (timeLeft() > 1 HOURS)
+ return "hyperspace"
+ else
+ var/obj/docking_port/stationary/dst
+ if(mode == SHUTTLE_RECALL)
+ dst = previous
+ else
+ dst = destination
+ . = "transit towards [dst?.name || "unknown location"] ([getTimerStr()])"
+ else
+ return dockedAt?.name || "unknown"
+
+
+/obj/docking_port/mobile/proc/getDbgStatusText()
+ var/obj/docking_port/stationary/dockedAt = get_docked()
. = (dockedAt && dockedAt.name) ? dockedAt.name : "unknown"
if(istype(dockedAt, /obj/docking_port/stationary/transit))
var/obj/docking_port/stationary/dst
@@ -645,7 +688,14 @@
dst = previous
else
dst = destination
- . += " towards [dst ? dst.name : "unknown location"] ([timeLeft(600)] minutes)"
+ if(dst)
+ . = "(transit to) [dst.name || dst.id]"
+ else
+ . = "(transit to) nowhere"
+ else if(dockedAt)
+ . = dockedAt.name || dockedAt.id
+ else
+ . = "unknown"
// attempts to locate /obj/machinery/computer/shuttle with matching ID inside the shuttle
diff --git a/code/modules/shuttle/shuttle_rotate.dm b/code/modules/shuttle/shuttle_rotate.dm
index cc7eacec36..d0478db45c 100644
--- a/code/modules/shuttle/shuttle_rotate.dm
+++ b/code/modules/shuttle/shuttle_rotate.dm
@@ -106,11 +106,13 @@ If ever any of these procs are useful for non-shuttles, rename it to proc/rotate
/obj/machinery/door/airlock/shuttleRotate(rotation, params)
. = ..()
- if(cyclelinkeddir)
+ if(cyclelinkeddir && (params & ROTATE_DIR))
cyclelinkeddir = angle2dir(rotation+dir2angle(cyclelinkeddir))
- cyclelinkairlock()
+ // If we update the linked airlock here, the partner airlock might
+ // not be present yet, so don't do that. Just assume we're still
+ // partnered with the same airlock as before.
/obj/machinery/porta_turret/shuttleRotate(rotation, params)
. = ..()
if(wall_turret_direction && (params & ROTATE_DIR))
- wall_turret_direction = turn(wall_turret_direction,rotation)
+ wall_turret_direction = turn(wall_turret_direction,rotation)
\ No newline at end of file
diff --git a/code/modules/shuttle/special.dm b/code/modules/shuttle/special.dm
index 0f005f69f7..61e4a1bb46 100644
--- a/code/modules/shuttle/special.dm
+++ b/code/modules/shuttle/special.dm
@@ -223,7 +223,7 @@
#define LUXURY_MESSAGE_COOLDOWN 100
-/obj/effect/forcefield/luxury_shuttle/CollidedWith(atom/movable/AM)
+/obj/effect/forcefield/luxury_shuttle/Bumped(atom/movable/AM)
if(!isliving(AM))
return ..()
diff --git a/code/modules/shuttle/supply.dm b/code/modules/shuttle/supply.dm
index 6f2997a078..b54fceb3e7 100644
--- a/code/modules/shuttle/supply.dm
+++ b/code/modules/shuttle/supply.dm
@@ -6,7 +6,8 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list(
/obj/item/disk/nuclear,
/obj/machinery/nuclearbomb,
/obj/item/beacon,
- /obj/singularity,
+ /obj/singularity/narsie,
+ /obj/singularity/wizard,
/obj/machinery/teleport/station,
/obj/machinery/teleport/hub,
/obj/machinery/quantumpad,
@@ -22,7 +23,8 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list(
/obj/item/projectile/beam/wormhole,
/obj/effect/portal,
/obj/item/shared_storage,
- /obj/structure/extraction_point
+ /obj/structure/extraction_point,
+ /obj/machinery/syndicatebomb
)))
/obj/docking_port/mobile/supply
@@ -37,9 +39,9 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list(
height = 7
movement_force = list("KNOCKDOWN" = 0, "THROW" = 0)
- // When TRUE, these vars allow exporting emagged/contraband items, and add some special interactions to existing exports.
- var/contraband = FALSE
- var/emagged = FALSE
+
+ //Export categories for this run, this is set by console sending the shuttle.
+ var/export_categories = EXPORT_CARGO
/obj/docking_port/mobile/supply/register()
. = ..()
@@ -60,7 +62,7 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list(
return FALSE
return TRUE
-/obj/docking_port/mobile/supply/request()
+/obj/docking_port/mobile/supply/request(obj/docking_port/stationary/S)
if(mode != SHUTTLE_IDLE)
return 2
return ..()
@@ -116,7 +118,8 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list(
var/msg = ""
var/matched_bounty = FALSE
- var/sold_atoms = ""
+
+ var/datum/export_report/ex = new
for(var/place in shuttle_areas)
var/area/shuttle/shuttle_area = place
@@ -126,23 +129,21 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list(
if(bounty_ship_item_and_contents(AM, dry_run = FALSE))
matched_bounty = TRUE
if(!AM.anchored || istype(AM, /obj/mecha))
- sold_atoms += export_item_and_contents(AM, contraband, emagged, dry_run = FALSE)
+ export_item_and_contents(AM, export_categories , dry_run = FALSE, external_report = ex)
- if(sold_atoms)
- sold_atoms += "."
+ if(ex.exported_atoms)
+ ex.exported_atoms += "." //ugh
if(matched_bounty)
msg += "Bounty items received. An update has been sent to all bounty consoles. "
- for(var/a in GLOB.exports_list)
- var/datum/export/E = a
- var/export_text = E.total_printout()
+ for(var/datum/export/E in ex.total_amount)
+ var/export_text = E.total_printout(ex)
if(!export_text)
continue
msg += export_text + "\n"
- SSshuttle.points += E.total_cost
- E.export_end()
+ SSshuttle.points += ex.total_value[E]
SSshuttle.centcom_message = msg
- investigate_log("Shuttle contents sold for [SSshuttle.points - presale_points] credits. Contents: [sold_atoms || "none."] Message: [SSshuttle.centcom_message || "none."]", INVESTIGATE_CARGO)
+ investigate_log("Shuttle contents sold for [SSshuttle.points - presale_points] credits. Contents: [ex.exported_atoms || "none."] Message: [SSshuttle.centcom_message || "none."]", INVESTIGATE_CARGO)
diff --git a/code/modules/shuttle/white_ship.dm b/code/modules/shuttle/white_ship.dm
index 6264588a3a..bca16c35f1 100644
--- a/code/modules/shuttle/white_ship.dm
+++ b/code/modules/shuttle/white_ship.dm
@@ -5,11 +5,24 @@
shuttleId = "whiteship"
possible_destinations = "whiteship_away;whiteship_home;whiteship_z4;whiteship_lavaland;whiteship_custom"
+/obj/machinery/computer/shuttle/white_ship/pod
+ name = "Salvage Pod Console"
+ desc = "Used to control the Salvage Pod."
+ circuit = /obj/item/circuitboard/computer/white_ship/pod
+ shuttleId = "whiteship_pod"
+ possible_destinations = "whiteship_pod_home;whiteship_pod_custom"
+
+/obj/machinery/computer/shuttle/white_ship/pod/recall
+ name = "Salvage Pod Recall Console"
+ desc = "Used to recall the Salvage Pod."
+ circuit = /obj/item/circuitboard/computer/white_ship/pod/recall
+ possible_destinations = "whiteship_pod_home"
+
/obj/machinery/computer/camera_advanced/shuttle_docker/whiteship
name = "White Ship Navigation Computer"
desc = "Used to designate a precise transit location for the White Ship."
shuttleId = "whiteship"
- lock_override = CAMERA_LOCK_STATION
+ lock_override = NONE
shuttlePortId = "whiteship_custom"
shuttlePortName = "Custom Location"
jumpto_ports = list("whiteship_away" = 1, "whiteship_home" = 1, "whiteship_z4" = 1)
@@ -18,6 +31,18 @@
y_offset = -10
designate_time = 100
+/obj/machinery/computer/camera_advanced/shuttle_docker/whiteship/pod
+ name = "Salvage Pod Navigation Computer"
+ desc = "Used to designate a precise transit location for the Salvage Pod."
+ shuttleId = "whiteship_pod"
+ shuttlePortId = "whiteship_pod_custom"
+ shuttlePortName = "Custom Location"
+ jumpto_ports = list("whiteship_pod_home" = 1)
+ view_range = 7
+ x_offset = -2
+ y_offset = 0
+ designate_time = 0
+
/obj/machinery/computer/camera_advanced/shuttle_docker/whiteship/Initialize()
. = ..()
GLOB.jam_on_wardec += src
@@ -25,3 +50,9 @@
/obj/machinery/computer/camera_advanced/shuttle_docker/whiteship/Destroy()
GLOB.jam_on_wardec -= src
return ..()
+
+/obj/effect/spawner/lootdrop/whiteship_cere_ripley
+ name = "25% mech 75% wreckage ripley spawner"
+ loot = list(/obj/mecha/working/ripley/mining = 1,
+ /obj/structure/mecha_wreckage/ripley = 5)
+ lootdoubles = FALSE
diff --git a/code/modules/spells/spell.dm b/code/modules/spells/spell.dm
index 4e6ef7ed9b..98eb6f56bf 100644
--- a/code/modules/spells/spell.dm
+++ b/code/modules/spells/spell.dm
@@ -237,9 +237,9 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th
switch(invocation_type)
if("shout")
if(prob(50))//Auto-mute? Fuck that noise
- user.say(invocation)
+ user.say(invocation, forced = "spell")
else
- user.say(replacetext(invocation," ","`"))
+ user.say(replacetext(invocation," ","`"), forced = "spell")
if("whisper")
if(prob(50))
user.whisper(invocation)
@@ -289,7 +289,7 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th
before_cast(targets)
invocation(user)
if(user && user.ckey)
- user.log_message("
cast the spell [name]. ", INDIVIDUAL_ATTACK_LOG)
+ user.log_message("
cast the spell [name]. ", LOG_ATTACK)
if(recharge)
recharging = TRUE
if(sound)
diff --git a/code/modules/spells/spell_types/area_teleport.dm b/code/modules/spells/spell_types/area_teleport.dm
index 23fdc1ee54..ab399f4e2a 100644
--- a/code/modules/spells/spell_types/area_teleport.dm
+++ b/code/modules/spells/spell_types/area_teleport.dm
@@ -77,12 +77,12 @@
else
switch(invocation_type)
if("shout")
- user.say("[invocation] [uppertext(chosenarea.name)]")
+ user.say("[invocation] [uppertext(chosenarea.name)]", forced = "spell")
if(user.gender==MALE)
playsound(user.loc, pick('sound/misc/null.ogg','sound/misc/null.ogg'), 100, 1)
else
playsound(user.loc, pick('sound/misc/null.ogg','sound/misc/null.ogg'), 100, 1)
if("whisper")
- user.whisper("[invocation] [uppertext(chosenarea.name)]")
+ user.whisper("[invocation] [uppertext(chosenarea.name)]", forced = "spell")
return
diff --git a/code/modules/spells/spell_types/construct_spells.dm b/code/modules/spells/spell_types/construct_spells.dm
index 8e1957f524..39066ae4a9 100644
--- a/code/modules/spells/spell_types/construct_spells.dm
+++ b/code/modules/spells/spell_types/construct_spells.dm
@@ -79,7 +79,7 @@
/obj/effect/proc_holder/spell/aoe_turf/conjure/soulstone
name = "Summon Soulstone"
- desc = "This spell reaches into Nar-Sie's realm, summoning one of the legendary fragments across time and space."
+ desc = "This spell reaches into Nar'Sie's realm, summoning one of the legendary fragments across time and space."
school = "conjuration"
charge_max = 2400
@@ -220,7 +220,7 @@
/obj/effect/proc_holder/spell/targeted/dominate
name = "Dominate"
- desc = "This spell dominates the mind of a lesser creature to the will of Nar'sie, allying it only to her direct followers."
+ desc = "This spell dominates the mind of a lesser creature to the will of Nar'Sie, allying it only to her direct followers."
charge_max = 600
range = 7
diff --git a/code/modules/spells/spell_types/godhand.dm b/code/modules/spells/spell_types/godhand.dm
index 27cfc9208b..919d87b4c7 100644
--- a/code/modules/spells/spell_types/godhand.dm
+++ b/code/modules/spells/spell_types/godhand.dm
@@ -24,7 +24,8 @@
..()
/obj/item/melee/touch_attack/afterattack(atom/target, mob/user, proximity)
- user.say(catchphrase)
+ . = ..()
+ user.say(catchphrase, forced = "spell")
playsound(get_turf(user), on_use_sound,50,1)
charges--
if(charges <= 0)
@@ -63,10 +64,9 @@
var/obj/item/bodypart/part = user.get_holding_bodypart_of_item(src)
if(part)
part.dismember()
- ..()
- return
+ return ..()
M.gib()
- ..()
+ return ..()
/obj/item/melee/touch_attack/fleshtostone
name = "\improper petrifying touch"
@@ -93,4 +93,4 @@
return
M.Stun(40)
M.petrify()
- ..()
+ return ..()
diff --git a/code/modules/spells/spell_types/rightandwrong.dm b/code/modules/spells/spell_types/rightandwrong.dm
index 414aa10933..bc1fc980bb 100644
--- a/code/modules/spells/spell_types/rightandwrong.dm
+++ b/code/modules/spells/spell_types/rightandwrong.dm
@@ -96,7 +96,7 @@ GLOBAL_VAR_INIT(summon_magic_triggered, FALSE)
SSticker.mode.traitors += H.mind
H.mind.add_antag_datum(/datum/antagonist/survivalist/guns)
- H.log_message("
Was made into a survivalist, and trusts no one! ", INDIVIDUAL_ATTACK_LOG)
+ H.log_message("was made into a survivalist, and trusts no one!", LOG_ATTACK, color="red")
var/gun_type = pick(GLOB.summoned_guns)
var/obj/item/gun/G = new gun_type(get_turf(H))
@@ -116,7 +116,7 @@ GLOBAL_VAR_INIT(summon_magic_triggered, FALSE)
if(prob(GLOB.summon_magic_triggered) && !(H.mind.has_antag_datum(/datum/antagonist)))
H.mind.add_antag_datum(/datum/antagonist/survivalist/magic)
- H.log_message("
Was made into a survivalist, and trusts no one! ", INDIVIDUAL_ATTACK_LOG)
+ H.log_message("was made into a survivalist, and trusts no one!", LOG_ATTACK, color="red")
var/magic_type = pick(GLOB.summoned_magic)
var/lucky = FALSE
diff --git a/code/modules/spells/spell_types/spacetime_distortion.dm b/code/modules/spells/spell_types/spacetime_distortion.dm
index c46e931aa7..7fd857dc51 100644
--- a/code/modules/spells/spell_types/spacetime_distortion.dm
+++ b/code/modules/spells/spell_types/spacetime_distortion.dm
@@ -81,8 +81,7 @@
/obj/effect/cross_action/spacetime_dist/Initialize(mapload)
. = ..()
- sound = "sound/guitar/[safepick(GLOB.guitar_notes)]"
- dir = pick(GLOB.cardinals)
+ setDir(pick(GLOB.cardinals))
/obj/effect/cross_action/spacetime_dist/proc/walk_link(atom/movable/AM)
if(ismob(AM))
diff --git a/code/modules/spells/spell_types/summonitem.dm b/code/modules/spells/spell_types/summonitem.dm
index 0bb79eba21..2bc347b185 100644
--- a/code/modules/spells/spell_types/summonitem.dm
+++ b/code/modules/spells/spell_types/summonitem.dm
@@ -57,7 +57,7 @@
var/obj/item/organ/organ = item_to_retrieve
if(organ.owner)
// If this code ever runs I will be happy
- add_logs(L, organ.owner, "magically removed [organ.name] from", addition="INTENT: [uppertext(L.a_intent)]")
+ log_combat(L, organ.owner, "magically removed [organ.name] from", addition="INTENT: [uppertext(L.a_intent)]")
organ.Remove(organ.owner)
else
while(!isturf(item_to_retrieve.loc) && infinite_recursion < 10) //if it's in something you get the whole thing.
diff --git a/code/modules/spells/spell_types/wizard.dm b/code/modules/spells/spell_types/wizard.dm
index 3d7894f98d..aec0e7806b 100644
--- a/code/modules/spells/spell_types/wizard.dm
+++ b/code/modules/spells/spell_types/wizard.dm
@@ -374,4 +374,4 @@
. = ..()
if(ishuman(thrower))
var/mob/living/carbon/human/H = thrower
- H.say("LIGHTNINGBOLT!!")
+ H.say("LIGHTNINGBOLT!!", forced = "spell")
diff --git a/code/modules/station_goals/bsa.dm b/code/modules/station_goals/bsa.dm
index be2c4f92bd..7b8caf28c8 100644
--- a/code/modules/station_goals/bsa.dm
+++ b/code/modules/station_goals/bsa.dm
@@ -168,12 +168,12 @@
top_layer = top_layer || mutable_appearance(icon, layer = ABOVE_MOB_LAYER)
switch(cannon_direction)
if(WEST)
- dir = WEST
+ setDir(WEST)
pixel_x = -192
top_layer.icon_state = "top_west"
icon_state = "cannon_west"
if(EAST)
- dir = EAST
+ setDir(EAST)
top_layer.icon_state = "top_east"
icon_state = "cannon_east"
add_overlay(top_layer)
diff --git a/code/modules/station_goals/dna_vault.dm b/code/modules/station_goals/dna_vault.dm
index 0601ff28e1..e97627f16f 100644
--- a/code/modules/station_goals/dna_vault.dm
+++ b/code/modules/station_goals/dna_vault.dm
@@ -78,7 +78,7 @@
dna = list()
/obj/item/dna_probe/afterattack(atom/target, mob/user, proximity)
- ..()
+ . = ..()
if(!proximity || !target)
return
//tray plants
diff --git a/code/modules/surgery/advanced/bioware/nerve_grounding.dm b/code/modules/surgery/advanced/bioware/nerve_grounding.dm
index 494cfaf59b..f4b23c89b1 100644
--- a/code/modules/surgery/advanced/bioware/nerve_grounding.dm
+++ b/code/modules/surgery/advanced/bioware/nerve_grounding.dm
@@ -1,10 +1,6 @@
-/obj/item/disk/surgery/nerve_grounding
- name = "Nerve Grounding Surgery Disk"
- desc = "The disk provides instructions on how to reroute the nervous system to ground electric shocks."
- surgeries = list(/datum/surgery/advanced/bioware/nerve_grounding)
-
/datum/surgery/advanced/bioware/nerve_grounding
- name = "nerve grounding"
+ name = "Nerve Grounding"
+ desc = "A surgical procedure which makes the patient's nerves act as grounding rods, protecting them from electrical shocks."
steps = list(/datum/surgery_step/incise,
/datum/surgery_step/retract_skin,
/datum/surgery_step/clamp_bleeders,
diff --git a/code/modules/surgery/advanced/bioware/nerve_splicing.dm b/code/modules/surgery/advanced/bioware/nerve_splicing.dm
index 944a05ed0e..6192786cc4 100644
--- a/code/modules/surgery/advanced/bioware/nerve_splicing.dm
+++ b/code/modules/surgery/advanced/bioware/nerve_splicing.dm
@@ -1,10 +1,6 @@
-/obj/item/disk/surgery/nerve_splicing
- name = "Nerve Splicing Surgery Disk"
- desc = "The disk provides instructions on how to splice the circulatory system to counter stuns and paralysis."
- surgeries = list(/datum/surgery/advanced/bioware/nerve_splicing)
-
/datum/surgery/advanced/bioware/nerve_splicing
- name = "nerve splicing"
+ name = "Nerve Splicing"
+ desc = "A surgical procedure which splices the patient's nerves, making them more resistant to stuns."
steps = list(/datum/surgery_step/incise,
/datum/surgery_step/retract_skin,
/datum/surgery_step/clamp_bleeders,
diff --git a/code/modules/surgery/advanced/bioware/vein_threading.dm b/code/modules/surgery/advanced/bioware/vein_threading.dm
index d618100a47..7a03833c51 100644
--- a/code/modules/surgery/advanced/bioware/vein_threading.dm
+++ b/code/modules/surgery/advanced/bioware/vein_threading.dm
@@ -1,10 +1,6 @@
-/obj/item/disk/surgery/vein_threading
- name = "Vein Threading Surgery Disk"
- desc = "The disk provides instructions on how to modify the circulatory system to greatly slow down bleeding."
- surgeries = list(/datum/surgery/advanced/bioware/vein_threading)
-
/datum/surgery/advanced/bioware/vein_threading
- name = "vein threading"
+ name = "Vein Threading"
+ desc = "A surgical procedure which severely reduces the amount of blood lost in case of injury."
steps = list(/datum/surgery_step/incise,
/datum/surgery_step/retract_skin,
/datum/surgery_step/clamp_bleeders,
diff --git a/code/modules/surgery/advanced/brainwashing.dm b/code/modules/surgery/advanced/brainwashing.dm
index eb40298a48..f4db9ddbee 100644
--- a/code/modules/surgery/advanced/brainwashing.dm
+++ b/code/modules/surgery/advanced/brainwashing.dm
@@ -4,7 +4,8 @@
surgeries = list(/datum/surgery/advanced/brainwashing)
/datum/surgery/advanced/brainwashing
- name = "brainwashing"
+ name = "Brainwashing"
+ desc = "A surgical procedure which directly implants a directive into the patient's brain, making it their absolute priority. It can be cleared using a mindshield implant."
steps = list(
/datum/surgery_step/incise,
/datum/surgery_step/retract_skin,
diff --git a/code/modules/surgery/advanced/lobotomy.dm b/code/modules/surgery/advanced/lobotomy.dm
index 94eea5d8ff..f74346b193 100644
--- a/code/modules/surgery/advanced/lobotomy.dm
+++ b/code/modules/surgery/advanced/lobotomy.dm
@@ -1,10 +1,6 @@
-/obj/item/disk/surgery/lobotomy
- name = "Lobotomy Surgery Disk"
- desc = "The disk provides instructions on how to perform a lobotomy, to cure the most resilient brain ailments."
- surgeries = list(/datum/surgery/advanced/lobotomy)
-
/datum/surgery/advanced/lobotomy
- name = "lobotomy"
+ name = "Lobotomy"
+ desc = "An invasive surgical procedure which guarantees removal of almost all brain traumas, but might cause another permanent trauma in return."
steps = list(
/datum/surgery_step/incise,
/datum/surgery_step/retract_skin,
diff --git a/code/modules/surgery/advanced/necrotic_revival.dm b/code/modules/surgery/advanced/necrotic_revival.dm
index 55f502a3d0..a88bb51b31 100644
--- a/code/modules/surgery/advanced/necrotic_revival.dm
+++ b/code/modules/surgery/advanced/necrotic_revival.dm
@@ -1,10 +1,6 @@
-/obj/item/disk/surgery/necrotic_revival
- name = "Necrotic Revival Surgery Disk"
- desc = "The disk provides instructions on how to make bodies keep working past death."
- surgeries = list(/datum/surgery/advanced/necrotic_revival)
-
/datum/surgery/advanced/necrotic_revival
- name = "necrotic revival"
+ name = "Necrotic Revival"
+ desc = "An experimental surgical procedure that stimulates the growth of a Romerol tumor inside the patient's brain. Requires zombie powder or rezadone."
steps = list(/datum/surgery_step/incise,
/datum/surgery_step/retract_skin,
/datum/surgery_step/saw,
diff --git a/code/modules/surgery/advanced/pacification.dm b/code/modules/surgery/advanced/pacification.dm
index ed3da3665f..15e34d003c 100644
--- a/code/modules/surgery/advanced/pacification.dm
+++ b/code/modules/surgery/advanced/pacification.dm
@@ -1,10 +1,6 @@
-/obj/item/disk/surgery/pacification
- name = "Pacification Surgery Disk"
- desc = "The disk provides instructions on how to suppress violence by manipulating the patient's brain."
- surgeries = list(/datum/surgery/advanced/pacify)
-
/datum/surgery/advanced/pacify
- name = "violence neutralization"
+ name = "Pacification"
+ desc = "A surgical procedure which permanently inhibits the aggression center of the brain, making the patient unwilling to cause direct harm."
steps = list(/datum/surgery_step/incise,
/datum/surgery_step/retract_skin,
/datum/surgery_step/saw,
diff --git a/code/modules/surgery/advanced/reconstruction.dm b/code/modules/surgery/advanced/reconstruction.dm
index 9fbf97070c..84d9f5b9f8 100644
--- a/code/modules/surgery/advanced/reconstruction.dm
+++ b/code/modules/surgery/advanced/reconstruction.dm
@@ -1,10 +1,6 @@
-/obj/item/disk/surgery/reconstruction
- name = "Reconstruction Surgery Disk"
- desc = "The disk provides instructions on how to repair a body without the use of chemicals."
- surgeries = list(/datum/surgery/advanced/reconstruction)
-
/datum/surgery/advanced/reconstruction
- name = "body reconstruction"
+ name = "Reconstruction"
+ desc = "A surgical procedure that gradually repairs damage done to a body without the assistance of chemicals. Unlike classic medicine, it is effective on corpses."
steps = list(/datum/surgery_step/incise,
/datum/surgery_step/incise,
/datum/surgery_step/retract_skin,
diff --git a/code/modules/surgery/advanced/revival.dm b/code/modules/surgery/advanced/revival.dm
index cd602eabdf..085253e4c9 100644
--- a/code/modules/surgery/advanced/revival.dm
+++ b/code/modules/surgery/advanced/revival.dm
@@ -1,10 +1,6 @@
-/obj/item/disk/surgery/revival
- name = "Revival Surgery Disk"
- desc = "The disk provides instructions on how to bring a corpse back to life."
- surgeries = list(/datum/surgery/advanced/revival)
-
/datum/surgery/advanced/revival
- name = "revival"
+ name = "Revival"
+ desc = "An experimental surgical procedure which involves reconstruction and reactivation of the patient's brain even long after death. The body must still be able to sustain life."
steps = list(/datum/surgery_step/incise,
/datum/surgery_step/retract_skin,
/datum/surgery_step/saw,
diff --git a/code/modules/surgery/advanced/viral_bonding.dm b/code/modules/surgery/advanced/viral_bonding.dm
index 61f7185db6..115f8a2eed 100644
--- a/code/modules/surgery/advanced/viral_bonding.dm
+++ b/code/modules/surgery/advanced/viral_bonding.dm
@@ -1,10 +1,6 @@
-/obj/item/disk/surgery/viral_bonding
- name = "Viral Bonding Surgery Disk"
- desc = "The disk provides instructions on how to force symbiosis between a virus and its host."
- surgeries = list(/datum/surgery/advanced/viral_bonding)
-
/datum/surgery/advanced/viral_bonding
- name = "viral bonding"
+ name = "Viral Bonding"
+ desc = "A surgical procedure that forces a symbiotic relationship between a virus and its host. The patient must be dosed with spaceacillin, virus food, and formaldehyde."
steps = list(/datum/surgery_step/incise,
/datum/surgery_step/retract_skin,
/datum/surgery_step/clamp_bleeders,
diff --git a/code/modules/surgery/bodyparts/bodyparts.dm b/code/modules/surgery/bodyparts/bodyparts.dm
index 8e0c6ee4cf..d8918b609f 100644
--- a/code/modules/surgery/bodyparts/bodyparts.dm
+++ b/code/modules/surgery/bodyparts/bodyparts.dm
@@ -10,20 +10,31 @@
var/mob/living/carbon/owner = null
var/mob/living/carbon/original_owner = null
var/status = BODYPART_ORGANIC
+ var/needs_processing = FALSE
+
var/body_zone //BODY_ZONE_CHEST, BODY_ZONE_L_ARM, etc , used for def_zone
var/aux_zone // used for hands
var/aux_layer
var/body_part = null //bitflag used to check which clothes cover this bodypart
var/use_digitigrade = NOT_DIGITIGRADE //Used for alternate legs, useless elsewhere
+ var/list/embedded_objects = list()
+ var/held_index = 0 //are we a hand? if so, which one!
+ var/is_pseudopart = FALSE //For limbs that don't really exist, eg chainsaws
+
+ var/disabled = FALSE //If TRUE, limb is as good as missing
+ var/body_damage_coeff = 1 //Multiplier of the limb's damage that gets applied to the mob
+ var/stam_damage_coeff = 0.5
var/brutestate = 0
var/burnstate = 0
var/brute_dam = 0
var/burn_dam = 0
var/stamina_dam = 0
+ var/max_stamina_damage = 0
var/max_damage = 0
- var/list/embedded_objects = list()
- var/held_index = 0 //are we a hand? if so, which one!
- var/is_pseudopart = FALSE //For limbs that don't really exist, eg chainsaws
+ var/stam_heal_tick = 3 //per Life().
+
+ var/brute_reduction = 0 //Subtracted to brute damage taken
+ var/burn_reduction = 0 //Subtracted to burn damage taken
//Coloring and proper item icon update
var/skin_tone = ""
@@ -55,9 +66,9 @@
/obj/item/bodypart/examine(mob/user)
..()
- if(brute_dam > 0)
+ if(brute_dam > DAMAGE_PRECISION)
to_chat(user, "
This limb has [brute_dam > 30 ? "severe" : "minor"] bruising. ")
- if(burn_dam > 0)
+ if(burn_dam > DAMAGE_PRECISION)
to_chat(user, "
This limb has [burn_dam > 30 ? "severe" : "minor"] burns. ")
/obj/item/bodypart/blob_act()
@@ -114,6 +125,20 @@
for(var/obj/item/I in src)
I.forceMove(T)
+/obj/item/bodypart/proc/consider_processing()
+ if(stamina_dam > DAMAGE_PRECISION)
+ . = TRUE
+ //else if.. else if.. so on.
+ else
+ . = FALSE
+ needs_processing = .
+
+//Return TRUE to get whatever mob this is in to update health.
+/obj/item/bodypart/proc/on_life()
+ if(stamina_dam > DAMAGE_PRECISION) //DO NOT update health here, it'll be done in the carbon's life.
+ if(heal_damage(brute = 0, burn = 0, stamina = stam_heal_tick, only_robotic = FALSE, only_organic = FALSE, updating_health = FALSE))
+ . |= BODYPART_LIFE_UPDATE_HEALTH
+
//Applies brute and burn damage to the organ. Returns 1 if the damage-icon states changed at all.
//Damage will not exceed max_damage using this proc
//Cannot apply negative damage
@@ -121,22 +146,21 @@
if(owner && (owner.status_flags & GODMODE))
return FALSE //godmode
var/dmg_mlt = CONFIG_GET(number/damage_multiplier)
- brute = max(brute * dmg_mlt, 0)
- burn = max(burn * dmg_mlt, 0)
- stamina = max(stamina * dmg_mlt, 0)
- if(status == BODYPART_ROBOTIC) //This makes robolimbs not damageable by chems and makes it stronger
- brute = max(0, brute - 5)
- burn = max(0, burn - 4)
- //No stamina scaling.. for now..
+ brute = round(max(brute * dmg_mlt, 0),DAMAGE_PRECISION)
+ burn = round(max(burn * dmg_mlt, 0),DAMAGE_PRECISION)
+ stamina = round(max(stamina * dmg_mlt, 0),DAMAGE_PRECISION)
+ brute = max(0, brute - brute_reduction)
+ burn = max(0, burn - burn_reduction)
+ //No stamina scaling.. for now..
if(!brute && !burn && !stamina)
return FALSE
switch(animal_origin)
- if(ALIEN_BODYPART,LARVA_BODYPART) //aliens take double burn
+ if(ALIEN_BODYPART,LARVA_BODYPART) //aliens take double burn //nothing can burn with so much snowflake code around
burn *= 2
- var/can_inflict = max_damage - (brute_dam + burn_dam)
+ var/can_inflict = max_damage - get_damage()
if(can_inflict <= 0)
return FALSE
@@ -144,21 +168,23 @@
if(total_damage > can_inflict)
var/excess = total_damage - can_inflict
- brute = brute * (excess / total_damage)
- burn = burn * (excess / total_damage)
+ brute = round(brute * (excess / total_damage),DAMAGE_PRECISION)
+ burn = round(burn * (excess / total_damage),DAMAGE_PRECISION)
brute_dam += brute
burn_dam += burn
//We've dealt the physical damages, if there's room lets apply the stamina damage.
- var/current_damage = brute_dam + burn_dam + stamina_dam //This time around, count stamina loss too.
+ var/current_damage = get_damage(TRUE) //This time around, count stamina loss too.
var/available_damage = max_damage - current_damage
- stamina_dam += CLAMP(stamina, 0, available_damage)
+ stamina_dam += round(CLAMP(stamina, 0, min(max_stamina_damage - stamina_dam, available_damage)), DAMAGE_PRECISION)
if(owner && updating_health)
owner.updatehealth()
- if(stamina)
- owner.update_stamina()
+ if(stamina > DAMAGE_PRECISION)
+ owner.update_stamina()
+ consider_processing()
+ check_disabled()
return update_bodypart_damage_state()
//Heals brute and burn damage for the organ. Returns 1 if the damage-icon states changed at all.
@@ -172,18 +198,38 @@
if(only_organic && status != BODYPART_ORGANIC) //This makes robolimbs not healable by chems.
return
- brute_dam = max(brute_dam - brute, 0)
- burn_dam = max(burn_dam - burn, 0)
- stamina_dam = max(stamina_dam - stamina, 0)
+ brute_dam = round(max(brute_dam - brute, 0), DAMAGE_PRECISION)
+ burn_dam = round(max(burn_dam - burn, 0), DAMAGE_PRECISION)
+ stamina_dam = round(max(stamina_dam - stamina, 0), DAMAGE_PRECISION)
if(owner && updating_health)
owner.updatehealth()
+ consider_processing()
+ check_disabled()
return update_bodypart_damage_state()
+//Returns total damage.
+/obj/item/bodypart/proc/get_damage(include_stamina = FALSE)
+ var/total = brute_dam + burn_dam
+ if(include_stamina)
+ total += stamina_dam
+ return total
-//Returns total damage...kinda pointless really
-/obj/item/bodypart/proc/get_damage()
- return brute_dam + burn_dam
+//Checks disabled status thresholds
+/obj/item/bodypart/proc/check_disabled()
+ if(!can_dismember() || owner.has_trait(TRAIT_NODISMEMBER))
+ return
+ if(!disabled && (get_damage(TRUE) >= max_damage))
+ set_disabled(TRUE)
+ else if(disabled && (get_damage(TRUE) <= (max_damage * 0.5)))
+ set_disabled(FALSE)
+/obj/item/bodypart/proc/set_disabled(new_disabled = TRUE)
+ if(disabled == new_disabled)
+ return
+ disabled = new_disabled
+ owner.update_health_hud() //update the healthdoll
+ owner.update_body()
+ owner.update_canmove()
//Updates an organ's brute/burn states for use by update_damage_overlays()
//Returns 1 if we need to update overlays. 0 otherwise.
@@ -196,8 +242,6 @@
return TRUE
return FALSE
-
-
//Change organ status
/obj/item/bodypart/proc/change_bodypart_status(new_limb_status, heal_limb, change_icon_to_default)
status = new_limb_status
@@ -350,6 +394,7 @@
limb.icon_state = "[species_id]_[body_zone]_[icon_gender]"
else
limb.icon_state = "[species_id]_[body_zone]"
+
// Citadel Start
if(should_draw_citadel)
limb.icon = 'modular_citadel/icons/mob/mutant_bodyparts.dmi'
@@ -358,6 +403,7 @@
else
limb.icon_state = "[species_id]_[body_zone]"
// Citadel End
+
if(aux_zone)
aux = image(limb.icon, "[species_id]_[aux_zone]", -aux_layer, image_dir)
. += aux
@@ -394,6 +440,8 @@
body_part = CHEST
px_x = 0
px_y = 0
+ stam_damage_coeff = 1
+ max_stamina_damage = 100
var/obj/item/cavity_item
/obj/item/bodypart/chest/Destroy()
@@ -440,13 +488,28 @@
icon_state = "default_human_l_arm"
attack_verb = list("slapped", "punched")
max_damage = 50
- body_zone =BODY_ZONE_L_ARM
+ max_stamina_damage = 50
+ body_zone = BODY_ZONE_L_ARM
body_part = ARM_LEFT
aux_zone = BODY_ZONE_PRECISE_L_HAND
aux_layer = HANDS_PART_LAYER
+ body_damage_coeff = 0.75
held_index = 1
px_x = -6
px_y = 0
+ stam_heal_tick = 2
+
+/obj/item/bodypart/l_arm/set_disabled(new_disabled = TRUE)
+ ..()
+ if(disabled)
+ to_chat(owner, "
Your [name] is too damaged to function! ")
+ owner.emote("scream")
+ if(held_index)
+ owner.dropItemToGround(owner.get_item_for_held_index(held_index))
+ if(owner.hud_used)
+ var/obj/screen/inventory/hand/L = owner.hud_used.hand_slots["[held_index]"]
+ if(L)
+ L.update_icon()
/obj/item/bodypart/l_arm/monkey
icon = 'icons/mob/animal_parts.dmi'
@@ -480,9 +543,24 @@
body_part = ARM_RIGHT
aux_zone = BODY_ZONE_PRECISE_R_HAND
aux_layer = HANDS_PART_LAYER
+ body_damage_coeff = 0.75
held_index = 2
px_x = 6
px_y = 0
+ stam_heal_tick = 2
+ max_stamina_damage = 50
+
+/obj/item/bodypart/r_arm/set_disabled(new_disabled = TRUE)
+ ..()
+ if(disabled)
+ to_chat(owner, "
Your [name] is too damaged to function! ")
+ owner.emote("scream")
+ if(held_index)
+ owner.dropItemToGround(owner.get_item_for_held_index(held_index))
+ if(owner.hud_used)
+ var/obj/screen/inventory/hand/R = owner.hud_used.hand_slots["[held_index]"]
+ if(R)
+ R.update_icon()
/obj/item/bodypart/r_arm/monkey
icon = 'icons/mob/animal_parts.dmi'
@@ -514,8 +592,17 @@
max_damage = 50
body_zone = BODY_ZONE_L_LEG
body_part = LEG_LEFT
+ body_damage_coeff = 0.75
px_x = -2
px_y = 12
+ stam_heal_tick = 2
+ max_stamina_damage = 50
+
+/obj/item/bodypart/l_leg/set_disabled(new_disabled = TRUE)
+ ..()
+ if(disabled)
+ to_chat(owner, "
Your [name] is too damaged to function! ")
+ owner.emote("scream")
/obj/item/bodypart/l_leg/digitigrade
name = "left digitigrade leg"
@@ -552,8 +639,17 @@
max_damage = 50
body_zone = BODY_ZONE_R_LEG
body_part = LEG_RIGHT
+ body_damage_coeff = 0.75
px_x = 2
px_y = 12
+ max_stamina_damage = 50
+ stam_heal_tick = 2
+
+/obj/item/bodypart/r_leg/set_disabled(new_disabled = TRUE)
+ ..()
+ if(disabled)
+ to_chat(owner, "
Your [name] is too damaged to function! ")
+ owner.emote("scream")
/obj/item/bodypart/r_leg/digitigrade
name = "right digitigrade leg"
diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm
index 87e13955fe..73d0ab305e 100644
--- a/code/modules/surgery/bodyparts/dismemberment.dm
+++ b/code/modules/surgery/bodyparts/dismemberment.dm
@@ -1,7 +1,7 @@
/obj/item/bodypart/proc/can_dismember(obj/item/I)
if(dismemberable)
- . = (get_damage() >= (max_damage - I.armour_penetration/2))
+ return TRUE
//Dismember a limb
/obj/item/bodypart/proc/dismember(dam_type = BRUTE)
@@ -16,7 +16,7 @@
return FALSE
var/obj/item/bodypart/affecting = C.get_bodypart(BODY_ZONE_CHEST)
- affecting.receive_damage(CLAMP(brute_dam/2, 15, 50), CLAMP(burn_dam/2, 0, 50)) //Damage the chest based on limb's existing damage
+ affecting.receive_damage(CLAMP(brute_dam/2 * affecting.body_damage_coeff, 15, 50), CLAMP(burn_dam/2 * affecting.body_damage_coeff, 0, 50)) //Damage the chest based on limb's existing damage
C.visible_message("
[C]'s [src.name] has been violently dismembered! ")
C.emote("scream")
SEND_SIGNAL(C, COMSIG_ADD_MOOD_EVENT, "dismembered", /datum/mood_event/dismembered)
@@ -51,7 +51,7 @@
return FALSE
if(C.has_trait(TRAIT_NODISMEMBER))
return FALSE
-
+ . = list()
var/organ_spilled = 0
var/turf/T = get_turf(C)
C.add_splatter_floor(T)
@@ -64,14 +64,15 @@
O.Remove(C)
O.forceMove(T)
organ_spilled = 1
+ . += X
if(cavity_item)
cavity_item.forceMove(T)
+ . += cavity_item
cavity_item = null
organ_spilled = 1
if(organ_spilled)
C.visible_message("
[C]'s internal organs spill out onto the floor! ")
- return 1
@@ -158,7 +159,8 @@
..()
/obj/item/bodypart/chest/drop_limb(special)
- return
+ if(special)
+ ..()
/obj/item/bodypart/r_arm/drop_limb(special)
var/mob/living/carbon/C = owner
@@ -242,16 +244,11 @@
name = "[owner.real_name]'s head"
..()
-
-
-
-
-
//Attach a limb to a human and drop any existing limb of that type.
/obj/item/bodypart/proc/replace_limb(mob/living/carbon/C, special)
if(!istype(C))
return
- var/obj/item/bodypart/O = locate(src.type) in C.bodyparts
+ var/obj/item/bodypart/O = C.get_bodypart(body_zone)
if(O)
O.drop_limb(1)
attach_limb(C, special)
@@ -259,7 +256,7 @@
/obj/item/bodypart/head/replace_limb(mob/living/carbon/C, special)
if(!istype(C))
return
- var/obj/item/bodypart/head/O = locate(src.type) in C.bodyparts
+ var/obj/item/bodypart/head/O = C.get_bodypart(body_zone)
if(O)
if(!special)
return
diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm
index 57b1c09cab..c08e6e5673 100644
--- a/code/modules/surgery/bodyparts/head.dm
+++ b/code/modules/surgery/bodyparts/head.dm
@@ -11,6 +11,8 @@
throw_range = 2 //No head bowling
px_x = 0
px_y = -8
+ stam_damage_coeff = 1
+ max_stamina_damage = 100
var/mob/living/brain/brainmob = null //The current occupant.
var/obj/item/organ/brain/brain = null //The brain organ
@@ -31,6 +33,11 @@
var/lip_style = null
var/lip_color = "white"
+/obj/item/bodypart/head/can_dismember(obj/item/I)
+ if(!((owner.stat == DEAD) || owner.InFullCritical()))
+ return FALSE
+ return ..()
+
/obj/item/bodypart/head/drop_organs(mob/user)
var/turf/T = get_turf(src)
if(status != BODYPART_ROBOTIC)
diff --git a/code/modules/surgery/bodyparts/helpers.dm b/code/modules/surgery/bodyparts/helpers.dm
index 91336331c4..1921f68165 100644
--- a/code/modules/surgery/bodyparts/helpers.dm
+++ b/code/modules/surgery/bodyparts/helpers.dm
@@ -13,33 +13,35 @@
/mob/living/carbon/has_hand_for_held_index(i)
if(i)
var/obj/item/bodypart/L = hand_bodyparts[i]
- if(L)
+ if(L && !L.disabled)
return L
return FALSE
-/mob/proc/has_left_hand()
+/mob/proc/has_left_hand(check_disabled = TRUE)
return TRUE
-/mob/living/carbon/has_left_hand()
+/mob/living/carbon/has_left_hand(check_disabled = TRUE)
for(var/obj/item/bodypart/L in hand_bodyparts)
if(L.held_index % 2)
- return TRUE
+ if(!check_disabled || !L.disabled)
+ return TRUE
return FALSE
/mob/living/carbon/alien/larva/has_left_hand()
return 1
-/mob/proc/has_right_hand()
+/mob/proc/has_right_hand(check_disabled = TRUE)
return TRUE
-/mob/living/carbon/has_right_hand()
+/mob/living/carbon/has_right_hand(check_disabled = TRUE)
for(var/obj/item/bodypart/L in hand_bodyparts)
if(!(L.held_index % 2))
- return TRUE
+ if(!check_disabled || !L.disabled)
+ return TRUE
return FALSE
/mob/living/carbon/alien/larva/has_right_hand()
@@ -48,17 +50,19 @@
//Limb numbers
-/mob/proc/get_num_arms()
+/mob/proc/get_num_arms(check_disabled = TRUE)
return 2
-/mob/living/carbon/get_num_arms()
+/mob/living/carbon/get_num_arms(check_disabled = TRUE)
. = 0
for(var/X in bodyparts)
var/obj/item/bodypart/affecting = X
if(affecting.body_part == ARM_RIGHT)
- .++
+ if(!check_disabled || !affecting.disabled)
+ .++
if(affecting.body_part == ARM_LEFT)
- .++
+ if(!check_disabled || !affecting.disabled)
+ .++
//sometimes we want to ignore that we don't have the required amount of arms.
@@ -69,17 +73,19 @@
return 1 //so we can still handcuff larvas.
-/mob/proc/get_num_legs()
+/mob/proc/get_num_legs(check_disabled = TRUE)
return 2
-/mob/living/carbon/get_num_legs()
+/mob/living/carbon/get_num_legs(check_disabled = TRUE)
. = 0
for(var/X in bodyparts)
var/obj/item/bodypart/affecting = X
if(affecting.body_part == LEG_RIGHT)
- .++
+ if(!check_disabled || !affecting.disabled)
+ .++
if(affecting.body_part == LEG_LEFT)
- .++
+ if(!check_disabled || !affecting.disabled)
+ .++
//sometimes we want to ignore that we don't have the required amount of legs.
/mob/proc/get_leg_ignore()
@@ -110,6 +116,27 @@
full -= zone
return full
+/mob/living/proc/get_disabled_limbs()
+ return list()
+
+/mob/living/carbon/get_disabled_limbs()
+ var/list/full = list(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG)
+ var/list/disabled = list()
+ for(var/zone in full)
+ var/obj/item/bodypart/affecting = get_bodypart(zone)
+ if(affecting && affecting.disabled)
+ disabled += zone
+ return disabled
+
+/mob/living/carbon/alien/larva/get_disabled_limbs()
+ var/list/full = list(BODY_ZONE_HEAD, BODY_ZONE_CHEST)
+ var/list/disabled = list()
+ for(var/zone in full)
+ var/obj/item/bodypart/affecting = get_bodypart(zone)
+ if(affecting && affecting.disabled)
+ disabled += zone
+ return disabled
+
//Remove all embedded objects from all limbs on the carbon mob
/mob/living/carbon/proc/remove_all_embedded_objects()
var/turf/T = get_turf(src)
diff --git a/code/modules/surgery/bodyparts/robot_bodyparts.dm b/code/modules/surgery/bodyparts/robot_bodyparts.dm
index f8e939dbcd..c535ef4a20 100644
--- a/code/modules/surgery/bodyparts/robot_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/robot_bodyparts.dm
@@ -19,6 +19,9 @@
flags_1 = CONDUCT_1
icon_state = "borg_l_arm"
status = BODYPART_ROBOTIC
+
+ brute_reduction = 5
+ burn_reduction = 4
light_brute_msg = ROBOTIC_LIGHT_BRUTE_MSG
medium_brute_msg = ROBOTIC_MEDIUM_BRUTE_MSG
@@ -37,6 +40,9 @@
flags_1 = CONDUCT_1
icon_state = "borg_r_arm"
status = BODYPART_ROBOTIC
+
+ brute_reduction = 5
+ burn_reduction = 4
light_brute_msg = ROBOTIC_LIGHT_BRUTE_MSG
medium_brute_msg = ROBOTIC_MEDIUM_BRUTE_MSG
@@ -55,6 +61,9 @@
flags_1 = CONDUCT_1
icon_state = "borg_l_leg"
status = BODYPART_ROBOTIC
+
+ brute_reduction = 5
+ burn_reduction = 4
light_brute_msg = ROBOTIC_LIGHT_BRUTE_MSG
medium_brute_msg = ROBOTIC_MEDIUM_BRUTE_MSG
@@ -73,6 +82,9 @@
flags_1 = CONDUCT_1
icon_state = "borg_r_leg"
status = BODYPART_ROBOTIC
+
+ brute_reduction = 5
+ burn_reduction = 4
light_brute_msg = ROBOTIC_LIGHT_BRUTE_MSG
medium_brute_msg = ROBOTIC_MEDIUM_BRUTE_MSG
@@ -90,6 +102,9 @@
flags_1 = CONDUCT_1
icon_state = "borg_chest"
status = BODYPART_ROBOTIC
+
+ brute_reduction = 5
+ burn_reduction = 4
light_brute_msg = ROBOTIC_LIGHT_BRUTE_MSG
medium_brute_msg = ROBOTIC_MEDIUM_BRUTE_MSG
@@ -149,6 +164,9 @@
flags_1 = CONDUCT_1
icon_state = "borg_head"
status = BODYPART_ROBOTIC
+
+ brute_reduction = 5
+ burn_reduction = 4
light_brute_msg = ROBOTIC_LIGHT_BRUTE_MSG
medium_brute_msg = ROBOTIC_MEDIUM_BRUTE_MSG
@@ -222,24 +240,32 @@
name = "surplus prosthetic left arm"
desc = "A skeletal, robotic limb. Outdated and fragile, but it's still better than nothing."
icon = 'icons/mob/augmentation/surplus_augments.dmi'
+ brute_reduction = 0
+ burn_reduction = 0
max_damage = 20
/obj/item/bodypart/r_arm/robot/surplus
name = "surplus prosthetic right arm"
desc = "A skeletal, robotic limb. Outdated and fragile, but it's still better than nothing."
icon = 'icons/mob/augmentation/surplus_augments.dmi'
+ brute_reduction = 0
+ burn_reduction = 0
max_damage = 20
/obj/item/bodypart/l_leg/robot/surplus
name = "surplus prosthetic left leg"
desc = "A skeletal, robotic limb. Outdated and fragile, but it's still better than nothing."
icon = 'icons/mob/augmentation/surplus_augments.dmi'
+ brute_reduction = 0
+ burn_reduction = 0
max_damage = 20
/obj/item/bodypart/r_leg/robot/surplus
name = "surplus prosthetic right leg"
desc = "A skeletal, robotic limb. Outdated and fragile, but it's still better than nothing."
icon = 'icons/mob/augmentation/surplus_augments.dmi'
+ brute_reduction = 0
+ burn_reduction = 0
max_damage = 20
diff --git a/code/modules/surgery/dental_implant.dm b/code/modules/surgery/dental_implant.dm
index 4e1d8eb743..87e6d096b3 100644
--- a/code/modules/surgery/dental_implant.dm
+++ b/code/modules/surgery/dental_implant.dm
@@ -32,7 +32,7 @@
if(!..())
return 0
to_chat(owner, "
You grit your teeth and burst the implanted [target.name]! ")
- add_logs(owner, null, "swallowed an implanted pill", target)
+ log_combat(owner, null, "swallowed an implanted pill", target)
if(target.reagents.total_volume)
target.reagents.reaction(owner, INGEST)
target.reagents.trans_to(owner, target.reagents.total_volume)
diff --git a/code/modules/surgery/helpers.dm b/code/modules/surgery/helpers.dm
index d74d7b6691..59440cc3ee 100644
--- a/code/modules/surgery/helpers.dm
+++ b/code/modules/surgery/helpers.dm
@@ -68,7 +68,7 @@
user.visible_message("[user] drapes [I] over [M]'s [parse_zone(selected_zone)] to prepare for \an [procedure.name].", \
"
You drape [I] over [M]'s [parse_zone(selected_zone)] to prepare for \an [procedure.name]. ")
- add_logs(user, M, "operated on", null, "(OPERATION TYPE: [procedure.name]) (TARGET AREA: [selected_zone])")
+ log_combat(user, M, "operated on", null, "(OPERATION TYPE: [procedure.name]) (TARGET AREA: [selected_zone])")
else
to_chat(user, "
You need to expose [M]'s [parse_zone(selected_zone)] first! ")
diff --git a/code/modules/surgery/limb_augmentation.dm b/code/modules/surgery/limb_augmentation.dm
index 10ef58ff18..46fe262189 100644
--- a/code/modules/surgery/limb_augmentation.dm
+++ b/code/modules/surgery/limb_augmentation.dm
@@ -14,14 +14,14 @@
user.visible_message("[user] begins to sever the muscles on [target]'s [parse_zone(user.zone_selected)].", "
You begin to sever the muscles on [target]'s [parse_zone(user.zone_selected)]... ")
-/datum/surgery_step/add_limb
+/datum/surgery_step/replace_limb
name = "replace limb"
implements = list(/obj/item/bodypart = 100, /obj/item/organ_storage = 100)
time = 32
var/obj/item/bodypart/L = null // L because "limb"
-/datum/surgery_step/add_limb/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+/datum/surgery_step/replace_limb/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
if(istype(tool, /obj/item/organ_storage) && istype(tool.contents[1], /obj/item/bodypart))
tool = tool.contents[1]
var/obj/item/bodypart/aug = tool
@@ -42,29 +42,24 @@
/datum/surgery/augmentation
name = "augmentation"
- steps = list(/datum/surgery_step/incise, /datum/surgery_step/clamp_bleeders, /datum/surgery_step/retract_skin, /datum/surgery_step/replace, /datum/surgery_step/saw, /datum/surgery_step/add_limb)
+ steps = list(/datum/surgery_step/incise, /datum/surgery_step/clamp_bleeders, /datum/surgery_step/retract_skin, /datum/surgery_step/replace, /datum/surgery_step/saw, /datum/surgery_step/replace_limb)
species = list(/mob/living/carbon/human)
possible_locs = list(BODY_ZONE_R_ARM,BODY_ZONE_L_ARM,BODY_ZONE_R_LEG,BODY_ZONE_L_LEG,BODY_ZONE_CHEST,BODY_ZONE_HEAD)
requires_real_bodypart = TRUE
//SURGERY STEP SUCCESSES
-/datum/surgery_step/add_limb/success(mob/user, mob/living/carbon/target, target_zone, obj/item/bodypart/tool, datum/surgery/surgery)
+/datum/surgery_step/replace_limb/success(mob/user, mob/living/carbon/target, target_zone, obj/item/bodypart/tool, datum/surgery/surgery)
if(L)
- user.visible_message("[user] successfully augments [target]'s [parse_zone(target_zone)]!", "
You successfully augment [target]'s [parse_zone(target_zone)]. ")
if(istype(tool, /obj/item/organ_storage))
tool.icon_state = initial(tool.icon_state)
tool.desc = initial(tool.desc)
tool.cut_overlays()
tool = tool.contents[1]
- L.change_bodypart_status(BODYPART_ROBOTIC, TRUE)
- L.icon = tool.icon
- L.max_damage = tool.max_damage
- qdel(tool)
- target.update_body_parts()
- target.updatehealth()
- target.update_hair()
- add_logs(user, target, "augmented", addition="by giving him new [parse_zone(target_zone)] INTENT: [uppertext(user.a_intent)]")
+ if(istype(tool) && user.temporarilyRemoveItemFromInventory(tool))
+ tool.replace_limb(target, TRUE)
+ user.visible_message("[user] successfully augments [target]'s [parse_zone(target_zone)]!", "
You successfully augment [target]'s [parse_zone(target_zone)]. ")
+ log_combat(user, target, "augmented", addition="by giving him new [parse_zone(target_zone)] INTENT: [uppertext(user.a_intent)]")
else
to_chat(user, "
[target] has no organic [parse_zone(target_zone)] there! ")
- return 1
+ return TRUE
diff --git a/code/modules/surgery/organ_manipulation.dm b/code/modules/surgery/organ_manipulation.dm
index 3b260017d3..553de45c60 100644
--- a/code/modules/surgery/organ_manipulation.dm
+++ b/code/modules/surgery/organ_manipulation.dm
@@ -141,7 +141,7 @@
if(I && I.owner == target)
user.visible_message("[user] successfully extracts [I] from [target]'s [parse_zone(target_zone)]!",
"
You successfully extract [I] from [target]'s [parse_zone(target_zone)]. ")
- add_logs(user, target, "surgically removed [I.name] from", addition="INTENT: [uppertext(user.a_intent)]")
+ log_combat(user, target, "surgically removed [I.name] from", addition="INTENT: [uppertext(user.a_intent)]")
I.Remove(target)
I.forceMove(get_turf(target))
else
diff --git a/code/modules/surgery/organic_steps.dm b/code/modules/surgery/organic_steps.dm
index beed1cce7f..01eb751f39 100644
--- a/code/modules/surgery/organic_steps.dm
+++ b/code/modules/surgery/organic_steps.dm
@@ -89,7 +89,7 @@
//drill bone
/datum/surgery_step/drill
name = "drill bone"
- implements = list(/obj/item/surgicaldrill = 100, /obj/item/pickaxe/drill = 60, /obj/item/mecha_parts/mecha_equipment/drill = 60, TOOL_SCREWDRIVER = 20)
+ implements = list(/obj/item/surgicaldrill = 100, /obj/item/screwdriver/power = 80, /obj/item/pickaxe/drill = 60, /obj/item/mecha_parts/mecha_equipment/drill = 60, TOOL_SCREWDRIVER = 20)
time = 30
/datum/surgery_step/drill/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
diff --git a/code/modules/surgery/organs/augments_arms.dm b/code/modules/surgery/organs/augments_arms.dm
index ea2385ce5f..ad07ce7238 100644
--- a/code/modules/surgery/organs/augments_arms.dm
+++ b/code/modules/surgery/organs/augments_arms.dm
@@ -42,6 +42,9 @@
to_chat(user, "
[src] is assembled in the [zone == BODY_ZONE_R_ARM ? "right" : "left"] arm configuration. You can use a screwdriver to reassemble it. ")
/obj/item/organ/cyberimp/arm/screwdriver_act(mob/living/user, obj/item/I)
+ . = ..()
+ if(.)
+ return TRUE
I.play_tool_sound(src)
if(zone == BODY_ZONE_R_ARM)
zone = BODY_ZONE_L_ARM
diff --git a/code/modules/surgery/organs/augments_internal.dm b/code/modules/surgery/organs/augments_internal.dm
index 1a3f5a6ae9..38967711fe 100644
--- a/code/modules/surgery/organs/augments_internal.dm
+++ b/code/modules/surgery/organs/augments_internal.dm
@@ -106,7 +106,7 @@
..()
if(crit_fail)
return
- owner.adjustStaminaLoss(-3.5)
+
if(owner.AmountStun() > STUN_SET_AMOUNT)
owner.SetStun(STUN_SET_AMOUNT)
if(owner.AmountKnockdown() > STUN_SET_AMOUNT)
diff --git a/code/modules/surgery/organs/autosurgeon.dm b/code/modules/surgery/organs/autosurgeon.dm
index 787a599428..cf22a633cf 100644
--- a/code/modules/surgery/organs/autosurgeon.dm
+++ b/code/modules/surgery/organs/autosurgeon.dm
@@ -58,6 +58,8 @@
return ..()
/obj/item/autosurgeon/screwdriver_act(mob/living/user, obj/item/I)
+ if(..())
+ return TRUE
if(!storedorgan)
to_chat(user, "
There's no implant in [src] for you to remove. ")
else
diff --git a/code/modules/surgery/organs/eyes.dm b/code/modules/surgery/organs/eyes.dm
index d38e76ae6e..bfe1b2dc3b 100644
--- a/code/modules/surgery/organs/eyes.dm
+++ b/code/modules/surgery/organs/eyes.dm
@@ -242,7 +242,7 @@
if (mobhook && mobhook.parent != M)
QDEL_NULL(mobhook)
if (!mobhook)
- mobhook = M.AddComponent(/datum/component/redirect, list(COMSIG_ATOM_DIR_CHANGE), CALLBACK(src, .proc/update_visuals))
+ mobhook = M.AddComponent(/datum/component/redirect, list(COMSIG_ATOM_DIR_CHANGE = CALLBACK(src, .proc/update_visuals)))
/obj/item/organ/eyes/robotic/glow/Remove(mob/living/carbon/M)
. = ..()
@@ -266,7 +266,7 @@
active = FALSE
remove_mob_overlay()
-/obj/item/organ/eyes/robotic/glow/proc/update_visuals(olddir, newdir)
+/obj/item/organ/eyes/robotic/glow/proc/update_visuals(datum/source, olddir, newdir)
if((LAZYLEN(eye_lighting) < light_beam_distance) || !on_mob)
regenerate_light_effects()
var/turf/scanfrom = get_turf(owner)
diff --git a/code/modules/surgery/organs/heart.dm b/code/modules/surgery/organs/heart.dm
index 5424486649..52904b8af1 100644
--- a/code/modules/surgery/organs/heart.dm
+++ b/code/modules/surgery/organs/heart.dm
@@ -54,11 +54,11 @@
var/sound/fastbeat = sound('sound/health/fastbeat.ogg', repeat = TRUE)
var/mob/living/carbon/H = owner
- if(H.health <= HEALTH_THRESHOLD_CRIT && beat != BEAT_SLOW)
+ if(H.health <= H.crit_threshold && beat != BEAT_SLOW)
beat = BEAT_SLOW
H.playsound_local(get_turf(H), slowbeat,40,0, channel = CHANNEL_HEARTBEAT)
to_chat(owner, "
You feel your heart slow down... ")
- if(beat == BEAT_SLOW && H.health > HEALTH_THRESHOLD_CRIT)
+ if(beat == BEAT_SLOW && H.health > H.crit_threshold)
H.stop_sound_channel(CHANNEL_HEARTBEAT)
beat = BEAT_NONE
diff --git a/code/modules/surgery/organs/liver.dm b/code/modules/surgery/organs/liver.dm
index cd9e60d3d1..ee767566e6 100755
--- a/code/modules/surgery/organs/liver.dm
+++ b/code/modules/surgery/organs/liver.dm
@@ -40,7 +40,7 @@
C.reagents.metabolize(C, can_overdose=TRUE)
if(damage > 10 && prob(damage/3))//the higher the damage the higher the probability
- to_chat(C, "
You feel [pick("nauseated", "a dull pain in your lower body", "confused")]. ")
+ to_chat(C, "
You feel a dull pain in your abdomen. ")
if(damage > maxHealth)//cap liver damage
damage = maxHealth
diff --git a/code/modules/surgery/organs/lungs.dm b/code/modules/surgery/organs/lungs.dm
index 9e53a902a2..9f71f58ff8 100644
--- a/code/modules/surgery/organs/lungs.dm
+++ b/code/modules/surgery/organs/lungs.dm
@@ -64,7 +64,7 @@
if(!breath || (breath.total_moles() == 0))
if(H.reagents.has_reagent(crit_stabilizing_reagent))
return
- if(H.health >= HEALTH_THRESHOLD_CRIT)
+ if(H.health >= H.crit_threshold)
H.adjustOxyLoss(HUMAN_MAX_OXYLOSS)
else if(!H.has_trait(TRAIT_NOCRITDAMAGE))
H.adjustOxyLoss(HUMAN_CRIT_MAX_OXYLOSS)
@@ -111,7 +111,7 @@
H.throw_alert("not_enough_oxy", /obj/screen/alert/not_enough_oxy)
else
H.failed_last_breath = FALSE
- if(H.health >= HEALTH_THRESHOLD_CRIT)
+ if(H.health >= H.crit_threshold)
H.adjustOxyLoss(-5)
gas_breathed = breath_gases[/datum/gas/oxygen][MOLES]
H.clear_alert("not_enough_oxy")
@@ -139,7 +139,7 @@
H.throw_alert("nitro", /obj/screen/alert/not_enough_nitro)
else
H.failed_last_breath = FALSE
- if(H.health >= HEALTH_THRESHOLD_CRIT)
+ if(H.health >= H.crit_threshold)
H.adjustOxyLoss(-5)
gas_breathed = breath_gases[/datum/gas/nitrogen][MOLES]
H.clear_alert("nitro")
@@ -176,7 +176,7 @@
H.throw_alert("not_enough_co2", /obj/screen/alert/not_enough_co2)
else
H.failed_last_breath = FALSE
- if(H.health >= HEALTH_THRESHOLD_CRIT)
+ if(H.health >= H.crit_threshold)
H.adjustOxyLoss(-5)
gas_breathed = breath_gases[/datum/gas/carbon_dioxide][MOLES]
H.clear_alert("not_enough_co2")
@@ -206,7 +206,7 @@
H.throw_alert("not_enough_tox", /obj/screen/alert/not_enough_tox)
else
H.failed_last_breath = FALSE
- if(H.health >= HEALTH_THRESHOLD_CRIT)
+ if(H.health >= H.crit_threshold)
H.adjustOxyLoss(-5)
gas_breathed = breath_gases[/datum/gas/plasma][MOLES]
H.clear_alert("not_enough_tox")
@@ -270,12 +270,61 @@
H.reagents.add_reagent("no2",1)
breath_gases[/datum/gas/nitryl][MOLES]-=gas_breathed
+
// Stimulum
gas_breathed = breath_gases[/datum/gas/stimulum][MOLES]
if (gas_breathed > gas_stimulation_min)
var/existing = H.reagents.get_reagent_amount("stimulum")
H.reagents.add_reagent("stimulum",max(0, 1 - existing))
breath_gases[/datum/gas/stimulum][MOLES]-=gas_breathed
+
+ // Miasma
+ if (breath_gases[/datum/gas/miasma])
+ var/miasma_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/miasma][MOLES])
+
+ //Miasma sickness
+ if(prob(0.5 * miasma_pp))
+ var/datum/disease/advance/miasma_disease = new /datum/disease/advance/random(2,3)
+ miasma_disease.name = "Unknown"
+ miasma_disease.try_infect(owner)
+
+ // Miasma side effects
+ switch(miasma_pp)
+ if(1 to 5)
+ // At lower pp, give out a little warning
+ SEND_SIGNAL(owner, COMSIG_CLEAR_MOOD_EVENT, "smell")
+ if(prob(5))
+ to_chat(owner, "
There is an unpleasant smell in the air. ")
+ if(5 to 15)
+ //At somewhat higher pp, warning becomes more obvious
+ if(prob(15))
+ to_chat(owner, "
You smell something horribly decayed inside this room. ")
+ SEND_SIGNAL(owner, COMSIG_ADD_MOOD_EVENT, "smell", /datum/mood_event/disgust/bad_smell)
+ if(15 to 30)
+ //Small chance to vomit. By now, people have internals on anyway
+ if(prob(5))
+ to_chat(owner, "
The stench of rotting carcasses is unbearable! ")
+ SEND_SIGNAL(owner, COMSIG_ADD_MOOD_EVENT, "smell", /datum/mood_event/disgust/nauseating_stench)
+ owner.vomit()
+ if(30 to INFINITY)
+ //Higher chance to vomit. Let the horror start
+ if(prob(15))
+ to_chat(owner, "
The stench of rotting carcasses is unbearable! ")
+ SEND_SIGNAL(owner, COMSIG_ADD_MOOD_EVENT, "smell", /datum/mood_event/disgust/nauseating_stench)
+ owner.vomit()
+ else
+ SEND_SIGNAL(owner, COMSIG_CLEAR_MOOD_EVENT, "smell")
+
+ // In a full miasma atmosphere with 101.34 pKa, about 10 disgust per breath, is pretty low compared to threshholds
+ // Then again, this is a purely hypothetical scenario and hardly reachable
+ owner.adjust_disgust(0.1 * miasma_pp)
+
+ breath_gases[/datum/gas/miasma][MOLES]-=gas_breathed
+
+ // Clear out moods when no miasma at all
+ else
+ SEND_SIGNAL(owner, COMSIG_CLEAR_MOOD_EVENT, "smell")
+
handle_breath_temperature(breath, H)
breath.garbage_collect()
return TRUE
diff --git a/code/modules/surgery/organs/stomach.dm b/code/modules/surgery/organs/stomach.dm
index cb7833a373..24132bcf04 100755
--- a/code/modules/surgery/organs/stomach.dm
+++ b/code/modules/surgery/organs/stomach.dm
@@ -42,13 +42,13 @@
SEND_SIGNAL(H, COMSIG_CLEAR_MOOD_EVENT, "disgust")
if(DISGUST_LEVEL_GROSS to DISGUST_LEVEL_VERYGROSS)
H.throw_alert("disgust", /obj/screen/alert/gross)
- SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "disgust", /datum/mood_event/disgust/gross)
+ SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "disgust", /datum/mood_event/gross)
if(DISGUST_LEVEL_VERYGROSS to DISGUST_LEVEL_DISGUSTED)
H.throw_alert("disgust", /obj/screen/alert/verygross)
- SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "disgust", /datum/mood_event/disgust/verygross)
+ SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "disgust", /datum/mood_event/verygross)
if(DISGUST_LEVEL_DISGUSTED to INFINITY)
H.throw_alert("disgust", /obj/screen/alert/disgusted)
- SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "disgust", /datum/mood_event/disgust/disgusted)
+ SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "disgust", /datum/mood_event/disgusted)
/obj/item/organ/stomach/Remove(mob/living/carbon/M, special = 0)
var/mob/living/carbon/human/H = owner
diff --git a/code/modules/surgery/organs/tails.dm b/code/modules/surgery/organs/tails.dm
index 8b05cbef08..b6701643a9 100644
--- a/code/modules/surgery/organs/tails.dm
+++ b/code/modules/surgery/organs/tails.dm
@@ -10,9 +10,8 @@
/obj/item/organ/tail/Remove(mob/living/carbon/human/H, special = 0)
..()
- if(istype(H))
- H.endTailWag()
-
+ if(H && H.dna && H.dna.species)
+ H.dna.species.stop_wagging_tail(H)
/obj/item/organ/tail/cat
name = "cat tail"
diff --git a/code/modules/surgery/organs/tongue.dm b/code/modules/surgery/organs/tongue.dm
index 59db1fb804..d5b2d16e67 100644
--- a/code/modules/surgery/organs/tongue.dm
+++ b/code/modules/surgery/organs/tongue.dm
@@ -83,7 +83,7 @@
//Hacks
var/mob/living/carbon/human/user = usr
var/rendered = "
[user.name]: [message]"
- log_talk(user,"ABDUCTOR:[key_name(user)] : [rendered]",LOGSAY)
+ user.log_talk(message, LOG_SAY, tag="abductor")
for(var/mob/living/carbon/human/H in GLOB.alive_mob_list)
var/obj/item/organ/tongue/T = H.getorganslot(ORGAN_SLOT_TONGUE)
if(!T || T.type != type)
diff --git a/code/modules/surgery/surgery.dm b/code/modules/surgery/surgery.dm
index 4dda6d7591..c9a3871511 100644
--- a/code/modules/surgery/surgery.dm
+++ b/code/modules/surgery/surgery.dm
@@ -1,5 +1,6 @@
/datum/surgery
var/name = "surgery"
+ var/desc = "surgery description"
var/status = 1
var/list/steps = list() //Steps in a surgery
var/step_in_progress = 0 //Actively performing a Surgery
@@ -92,7 +93,15 @@
var/datum/species/abductor/S = H.dna.species
if(S.scientist)
return TRUE
-
+
+ if(iscyborg(user))
+ var/mob/living/silicon/robot/R = user
+ var/obj/item/surgical_processor/SP = locate() in R.module.modules
+ if(!SP)
+ return FALSE
+ if(type in SP.advanced_surgeries)
+ return TRUE
+
var/turf/T = get_turf(target)
var/obj/structure/table/optable/table = locate(/obj/structure/table/optable, T)
if(!table || !table.computer)
@@ -129,7 +138,6 @@
//TODO
//specific steps for some surgeries (fluff text)
-//R&D researching new surgeries (especially for non-humans)
//more interesting failure options
//randomised complications
//more surgeries!
diff --git a/code/modules/surgery/surgery_step.dm b/code/modules/surgery/surgery_step.dm
index bc6c04bca7..f670b80167 100644
--- a/code/modules/surgery/surgery_step.dm
+++ b/code/modules/surgery/surgery_step.dm
@@ -14,6 +14,8 @@
if(accept_hand)
if(!tool)
success = TRUE
+ if(iscyborg(user))
+ success = TRUE
if(accept_any_item)
if(tool && tool_check(user, tool))
diff --git a/code/modules/surgery/tools.dm b/code/modules/surgery/tools.dm
index 7afee36543..aaf859ee52 100644
--- a/code/modules/surgery/tools.dm
+++ b/code/modules/surgery/tools.dm
@@ -198,6 +198,7 @@
icon_state = "evidenceobj"
/obj/item/organ_storage/afterattack(obj/item/I, mob/user, proximity)
+ . = ..()
if(!proximity)
return
if(contents.len)
@@ -234,3 +235,29 @@
else
to_chat(user, "[src] is empty.")
return
+
+/obj/item/surgical_processor //allows medical cyborgs to scan and initiate advanced surgeries
+ name = "\improper Surgical Processor"
+ desc = "A device for scanning and initiating surgeries from a disk or operating computer."
+ icon = 'icons/obj/device.dmi'
+ icon_state = "spectrometer"
+ item_flags = NOBLUDGEON
+ var/list/advanced_surgeries = list()
+
+/obj/item/surgical_processor/afterattack(obj/item/O, mob/user, proximity)
+ . = ..()
+ if(!proximity)
+ return
+ if(istype(O, /obj/item/disk/surgery))
+ to_chat(user, "
You load the surgery protocol from [O] into [src]. ")
+ var/obj/item/disk/surgery/D = O
+ if(do_after(user, 10, target = O))
+ advanced_surgeries |= D.surgeries
+ return TRUE
+ if(istype(O, /obj/machinery/computer/operating))
+ to_chat(user, "
You copy surgery protocols from [O] into [src]. ")
+ var/obj/machinery/computer/operating/OC = O
+ if(do_after(user, 10, target = O))
+ advanced_surgeries |= OC.advanced_surgeries
+ return TRUE
+ return
diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm
index 59cf27bbf3..8da5d7c113 100644
--- a/code/modules/unit_tests/_unit_tests.dm
+++ b/code/modules/unit_tests/_unit_tests.dm
@@ -1,9 +1,11 @@
//include unit test files in this module in this ifdef
-// CITADEL EDIT add vore_tests.dm
+//Keep this sorted alphabetically
+
#ifdef UNIT_TESTS
-#include "unit_test.dm"
-#include "reagent_recipe_collisions.dm"
#include "reagent_id_typos.dm"
-//#include "vore_tests.dm"
+#include "reagent_recipe_collisions.dm"
+#include "spawn_humans.dm"
#include "subsystem_init.dm"
+#include "timer_sanity.dm"
+#include "unit_test.dm"
#endif
diff --git a/code/modules/unit_tests/spawn_humans.dm b/code/modules/unit_tests/spawn_humans.dm
new file mode 100644
index 0000000000..7189e87277
--- /dev/null
+++ b/code/modules/unit_tests/spawn_humans.dm
@@ -0,0 +1,7 @@
+/datum/unit_test/spawn_humans/Run()
+ var/locs = block(run_loc_bottom_left, run_loc_top_right)
+
+ for(var/I in 1 to 5)
+ new /mob/living/carbon/human(pick(locs))
+
+ sleep(50)
diff --git a/code/modules/unit_tests/timer_sanity.dm b/code/modules/unit_tests/timer_sanity.dm
new file mode 100644
index 0000000000..1e5b022b19
--- /dev/null
+++ b/code/modules/unit_tests/timer_sanity.dm
@@ -0,0 +1,3 @@
+/datum/unit_test/timer_sanity/Run()
+ if(SStimer.bucket_count < 0)
+ Fail("SStimer is going into negative bucket count from something")
\ No newline at end of file
diff --git a/code/modules/uplink/uplink_items.dm b/code/modules/uplink/uplink_items.dm
index a58d8a6269..a38b27af6c 100644
--- a/code/modules/uplink/uplink_items.dm
+++ b/code/modules/uplink/uplink_items.dm
@@ -202,6 +202,15 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
surplus = 40
include_modes = list(/datum/game_mode/nuclear)
+/datum/uplink_item/dangerous/carbine
+ name = "M-90gl Carbine"
+ desc = "A fully-loaded, specialized three-round burst carbine that fires 5.56mm ammunition from a 30 round magazine \
+ with a togglable 40mm under-barrel grenade launcher."
+ item = /obj/item/gun/ballistic/automatic/m90
+ cost = 18
+ surplus = 50
+ include_modes = list(/datum/game_mode/nuclear)
+
/datum/uplink_item/dangerous/machinegun
name = "L6 Squad Automatic Weapon"
desc = "A fully-loaded Aussec Armoury belt-fed machine gun. \
@@ -395,6 +404,15 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
player_minimum = 25
restricted = TRUE
+/datum/uplink_item/dangerous/buzzkill
+ name = "Buzzkill Grenade Box"
+ desc = "A box with three grenades that release a swarm of angry bees upon activation. These bees indiscriminately attack friend or foe \
+ with random toxins. Courtesy of the BLF and Tiger Cooperative."
+ item = /obj/item/storage/box/syndie_kit/bee_grenades
+ cost = 15
+ surplus = 35
+ include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops)
+
// Ammunition
/datum/uplink_item/ammo
category = "Ammunition"
@@ -509,6 +527,22 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
cost = 20
include_modes = list(/datum/game_mode/nuclear)
+/datum/uplink_item/ammo/carbine
+ name = "5.56mm Toploader Magazine"
+ desc = "An additional 30-round 5.56mm magazine; suitable for use with the M-90gl carbine. \
+ These bullets pack less punch than 1.95mm rounds, but they still offer more power than .45 ammo."
+ item = /obj/item/ammo_box/magazine/m556
+ cost = 4
+ include_modes = list(/datum/game_mode/nuclear)
+
+/datum/uplink_item/ammo/a40mm
+ name = "40mm Grenade"
+ desc = "A 40mm HE grenade for use with the M-90gl's under-barrel grenade launcher. \
+ Your teammates will ask you to not shoot these down small hallways."
+ item = /obj/item/ammo_casing/a40mm
+ cost = 2
+ include_modes = list(/datum/game_mode/nuclear)
+
/datum/uplink_item/ammo/machinegun
cost = 6
surplus = 0
@@ -531,6 +565,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
desc = "A 50-round magazine of 1.95x129mm ammunition for use in the L6 SAW; equipped with special properties \
to puncture even the most durable armor."
item = /obj/item/ammo_box/magazine/mm195x129/ap
+ cost = 9
/datum/uplink_item/ammo/machinegun/incen
name = "1.95x129mm (Incendiary) Box Magazine"
@@ -1002,8 +1037,10 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
/datum/uplink_item/device_tools/ai_detector
name = "Artificial Intelligence Detector"
- desc = "A functional multitool that turns red when it detects an artificial intelligence watching it or its \
- holder. Knowing when an artificial intelligence is watching you is useful for knowing when to maintain cover."
+ desc = "A functional multitool that turns red when it detects an artificial intelligence watching it, and can be \
+ activated to display their exact viewing location and nearby security camera blind spots. Knowing when \
+ an artificial intelligence is watching you is useful for knowing when to maintain cover, and finding nearby \
+ blind spots can help you identify escape routes."
item = /obj/item/multitool/ai_detect
cost = 1
@@ -1016,16 +1053,12 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
/datum/uplink_item/device_tools/briefcase_launchpad
name = "Briefcase Launchpad"
- desc = "A briefcase containing a launchpad, a device able to teleport items and people to and from targets up to three tiles away from the briefcase. \
- Also includes a remote control. Touch the briefcase with the remote to link it."
+ desc = "A briefcase containing a launchpad, a device able to teleport items and people to and from targets up to eight tiles away from the briefcase. \
+ Also includes a remote control, disguised as an ordinary folder. Touch the briefcase with the remote to link it."
surplus = 0
- item = /obj/item/briefcase_launchpad
+ item = /obj/item/storage/briefcase/launchpad
cost = 6
-/datum/uplink_item/device_tools/briefcase_launchpad/purchase(mob/user, datum/component/uplink/U)
- spawn_item(/obj/item/launchpad_remote, user) //free remote
- ..()
-
/datum/uplink_item/device_tools/magboots
name = "Blood-Red Magboots"
desc = "A pair of magnetic boots with a Syndicate paintjob that assist with freer movement in space or on-station \
@@ -1256,6 +1289,12 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
cost = 4
restricted = TRUE
+/datum/uplink_item/implants/stealthimplant
+ name = "Stealth Implant"
+ desc = "This one-of-a-kind implant will make you almost invisible if you play your cards right."
+ item = /obj/item/implanter/stealth
+ cost = 8
+
// Cybernetics
/datum/uplink_item/cyber_implants
category = "Cybernetic Implants"
@@ -1293,12 +1332,6 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
cost = 40
cant_discount = TRUE
-/datum/uplink_item/cyber_implants/stealthimplant
- name = "Stealth Implant"
- desc = "This one-of-a-kind implant will make you almost invisible if you play your cards right."
- item = /obj/item/implanter/stealth
- cost = 8
-
// Role-specific items
/datum/uplink_item/role_restricted
category = "Role-Restricted"
@@ -1455,6 +1488,17 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
cost = 15
restricted_roles = list("Clown")
+/datum/uplink_item/role_restricted/clowncar
+ name = "Clown Car"
+ desc = "The Clown Car is the ultimate transportation method for any worthy clown! \
+ Simply insert your bikehorn and get in, and get ready to have the funniest ride of your life! \
+ You can ram any spacemen you come across and stuff them into your car, kidnapping them and locking them inside until \
+ someone saves them or they manage to crawl out. Be sure not to ram into any walls or vending machines, as the springloaded seats \
+ are very sensetive. Now with our included lube defense mechanism which will protect you against any angry shitcurity!"
+ item = /obj/vehicle/sealed/car/clowncar
+ cost = 15
+ restricted_roles = list("Clown")
+
// Pointless
/datum/uplink_item/badass
category = "(Pointless) Badassery"
@@ -1572,7 +1616,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
/datum/uplink_item/badass/random
name = "Random Item"
desc = "Picking this will purchase a random item. Useful if you have some TC to spare or if you haven't decided on a strategy yet."
- item = /obj/item/paper
+ item = /obj/effect/gibspawner/generic // non-tangible item because techwebs use this path to determine illegal tech
cost = 0
cant_discount = TRUE
diff --git a/code/modules/vehicles/_vehicle.dm b/code/modules/vehicles/_vehicle.dm
index ef529ab849..be59a6df65 100644
--- a/code/modules/vehicles/_vehicle.dm
+++ b/code/modules/vehicles/_vehicle.dm
@@ -1,6 +1,3 @@
-#define VEHICLE_CONTROL_PERMISSION 1
-#define VEHICLE_CONTROL_DRIVE 2
-
/obj/vehicle
name = "generic vehicle"
desc = "Yell at coderbus."
@@ -72,8 +69,8 @@
return FALSE
occupants[M] = NONE
add_control_flags(M, control_flags)
- grant_passenger_actions(M)
after_add_occupant(M)
+ grant_passenger_actions(M)
return TRUE
/obj/vehicle/proc/after_add_occupant(mob/M)
@@ -119,8 +116,12 @@
step(trailer, dir_to_move)
return did_move
else
+ after_move(direction)
return step(src, direction)
+/obj/vehicle/proc/after_move(direction)
+ return
+
/obj/vehicle/proc/add_control_flags(mob/controller, flags)
if(!istype(controller) || !flags)
return FALSE
@@ -139,12 +140,12 @@
remove_controller_actions_by_flag(controller, i)
return TRUE
-/obj/vehicle/Collide(atom/movable/M)
+/obj/vehicle/Bump(atom/movable/M)
. = ..()
if(emulate_door_bumps)
- if(istype(M, /obj/machinery/door) && has_buckled_mobs())
+ if(istype(M, /obj/machinery/door))
for(var/m in occupants)
- M.CollidedWith(m)
+ M.Bumped(m)
/obj/vehicle/Move(newloc, dir)
. = ..()
diff --git a/code/modules/vehicles/cars/car.dm b/code/modules/vehicles/cars/car.dm
new file mode 100644
index 0000000000..9e9b41bd80
--- /dev/null
+++ b/code/modules/vehicles/cars/car.dm
@@ -0,0 +1,78 @@
+/obj/vehicle/sealed/car
+ layer = ABOVE_MOB_LAYER
+ anchored = TRUE
+ var/car_traits = NONE //Bitflag for special behavior such as kidnapping
+ var/engine_sound = 'sound/vehicles/carrev.ogg'
+ var/last_enginesound_time
+ var/engine_sound_length = 20 //Set this to the length of the engine sound
+ var/escape_time = 200 //Time it takes to break out of the car
+
+/obj/vehicle/sealed/car/generate_actions()
+ . = ..()
+ initialize_controller_action_type(/datum/action/vehicle/sealed/remove_key, VEHICLE_CONTROL_DRIVE)
+ if(car_traits & CAN_KIDNAP)
+ initialize_controller_action_type(/datum/action/vehicle/sealed/DumpKidnappedMobs, VEHICLE_CONTROL_DRIVE)
+
+/obj/vehicle/sealed/car/MouseDrop_T(atom/dropping, mob/M)
+ if(!M.canmove || M.stat || M.restrained())
+ return FALSE
+ if(isliving(dropping) && M != dropping)
+ var/mob/living/L = dropping
+ L.visible_message("
[M] starts forcing [L] into [src]! ")
+ mob_try_forced_enter(M, L)
+ return ..()
+
+/obj/vehicle/sealed/car/mob_try_exit(mob/M, mob/user, silent = FALSE)
+ if(M == user && (occupants[M] & VEHICLE_CONTROL_KIDNAPPED))
+ to_chat(user, "
You push against the back of [src] trunk to try and get out. ")
+ if(!do_after(user, escape_time, target = src))
+ return FALSE
+ to_chat(user,"
[user] gets out of [src] ")
+ mob_exit(M, silent)
+ return TRUE
+ mob_exit(M, silent)
+ return TRUE
+
+/obj/vehicle/sealed/car/after_move(direction)
+ if(world.time < last_enginesound_time + engine_sound_length)
+ return
+ last_enginesound_time = world.time
+ playsound(src, engine_sound, 100, TRUE)
+
+/obj/vehicle/sealed/car/attacked_by(obj/item/I, mob/living/user)
+ if(!I.force)
+ return
+ if(occupants[user])
+ to_chat(user, "
Your attack bounces off of the car's padded interior. ")
+ return
+ return ..()
+
+/obj/vehicle/sealed/car/attack_hand(mob/living/user)
+ . = ..()
+ if(!(car_traits & CAN_KIDNAP))
+ return
+ if(occupants[user])
+ return
+ to_chat(user, "
You start opening [src]'s trunk. ")
+ if(do_after(user, 30))
+ if(return_amount_of_controllers_with_flag(VEHICLE_CONTROL_KIDNAPPED))
+ to_chat(user, "
The people stuck in [src]'s trunk all come tumbling out. ")
+ DumpSpecificMobs(VEHICLE_CONTROL_KIDNAPPED)
+ else
+ to_chat(user, "
It seems [src]'s trunk was empty. ")
+
+/obj/vehicle/sealed/car/proc/mob_try_forced_enter(mob/forcer, mob/M, silent = FALSE)
+ if(!istype(M))
+ return FALSE
+ if(occupant_amount() >= max_occupants)
+ return FALSE
+ if(do_mob(forcer, get_enter_delay(M), target = src))
+ mob_forced_enter(M, silent)
+ return TRUE
+ return FALSE
+
+/obj/vehicle/sealed/car/proc/mob_forced_enter(mob/M, silent = FALSE)
+ if(!silent)
+ M.visible_message("
[M] is forced into \the [src]! ")
+ M.forceMove(src)
+ add_occupant(M, VEHICLE_CONTROL_KIDNAPPED)
diff --git a/code/modules/vehicles/cars/clowncar.dm b/code/modules/vehicles/cars/clowncar.dm
new file mode 100644
index 0000000000..3485c0f378
--- /dev/null
+++ b/code/modules/vehicles/cars/clowncar.dm
@@ -0,0 +1,130 @@
+/obj/vehicle/sealed/car/clowncar
+ name = "clown car"
+ desc = "How someone could even fit in there is beyond me."
+ icon_state = "clowncar"
+ max_integrity = 150
+ armor = list("melee" = 70, "bullet" = 40, "laser" = 40, "energy" = 0, "bomb" = 30, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 80)
+ enter_delay = 20
+ max_occupants = 50
+ movedelay = 0.6
+ car_traits = CAN_KIDNAP
+ key_type = /obj/item/bikehorn
+ key_type_exact = FALSE
+ var/droppingoil = FALSE
+ var/RTDcooldown = 150
+ var/lastRTDtime = 0
+
+/obj/vehicle/sealed/car/clowncar/generate_actions()
+ . = ..()
+ initialize_controller_action_type(/datum/action/vehicle/sealed/horn/clowncar, VEHICLE_CONTROL_DRIVE)
+
+/obj/vehicle/sealed/car/clowncar/auto_assign_occupant_flags(mob/M)
+ if(ishuman(M))
+ var/mob/living/carbon/human/H = M
+ if(H.mind && H.mind.assigned_role == "Clown") //Ensures only clowns can drive the car. (Including more at once)
+ add_control_flags(M, VEHICLE_CONTROL_DRIVE|VEHICLE_CONTROL_PERMISSION)
+ return
+ add_control_flags(M, VEHICLE_CONTROL_KIDNAPPED)
+
+/obj/vehicle/sealed/car/clowncar/mob_forced_enter(mob/M, silent = FALSE)
+ . = ..()
+ playsound(src, pick('sound/vehicles/clowncar_load1.ogg', 'sound/vehicles/clowncar_load2.ogg'), 75)
+
+/obj/vehicle/sealed/car/clowncar/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir)
+ . = ..()
+ if(prob(33))
+ visible_message("
[src] spews out a ton of space lube! ")
+ new /obj/effect/particle_effect/foam(loc) //YEET
+
+/obj/vehicle/sealed/car/clowncar/attacked_by(obj/item/I, mob/living/user)
+ . = ..()
+ if(istype(I, /obj/item/reagent_containers/food/snacks/grown/banana))
+ var/obj/item/reagent_containers/food/snacks/grown/banana/banana = I
+ obj_integrity += min(banana.seed.potency, max_integrity-obj_integrity)
+ to_chat(user, "
You use the [banana] to repair the [src]! ")
+ qdel(banana)
+
+/obj/vehicle/sealed/car/clowncar/Bump(atom/movable/M)
+ . = ..()
+ if(isliving(M))
+ if(ismegafauna(M))
+ return
+ var/mob/living/L = M
+ if(iscarbon(L))
+ var/mob/living/carbon/C = L
+ C.Knockdown(40) //I play to make sprites go horizontal
+ L.visible_message("
[src] rams into [L] and sucks him up! ") //fuck off shezza this isn't ERP.
+ mob_forced_enter(L)
+ playsound(src, pick('sound/vehicles/clowncar_ram1.ogg', 'sound/vehicles/clowncar_ram2.ogg', 'sound/vehicles/clowncar_ram3.ogg'), 75)
+ else if(istype(M, /turf/closed))
+ visible_message("
[src] rams into [M] and crashes! ")
+ playsound(src, pick('sound/vehicles/clowncar_crash1.ogg', 'sound/vehicles/clowncar_crash2.ogg'), 75)
+ playsound(src, 'sound/vehicles/clowncar_crashpins.ogg', 75)
+ DumpMobs(TRUE)
+
+/obj/vehicle/sealed/car/clowncar/emag_act(mob/user)
+ if(obj_flags & EMAGGED)
+ return
+ obj_flags |= EMAGGED
+ to_chat(user, "
You scramble the clowncar child safety lock and a panel with 6 colorful buttons appears! ")
+ initialize_controller_action_type(/datum/action/vehicle/sealed/RollTheDice, VEHICLE_CONTROL_DRIVE)
+
+/obj/vehicle/sealed/car/clowncar/Destroy()
+ playsound(src, 'sound/vehicles/clowncar_fart.ogg', 100)
+ return ..()
+
+/obj/vehicle/sealed/car/clowncar/after_move(direction)
+ . = ..()
+ if(droppingoil)
+ new /obj/effect/decal/cleanable/oil/slippery(loc)
+
+/obj/vehicle/sealed/car/clowncar/proc/RollTheDice(mob/user)
+ if(world.time - lastRTDtime < RTDcooldown)
+ to_chat(user, "
The button panel is currently recharging. ")
+ return
+ lastRTDtime = world.time
+ var/randomnum = rand(1,6)
+ switch(randomnum)
+ if(1)
+ visible_message("
[user] has pressed one of the colorful buttons on [src] and a special banana peel drops out of it. ")
+ new /obj/item/grown/bananapeel/specialpeel(loc)
+ if(2)
+ visible_message("
[user] has pressed one of the colorful buttons on [src] and unknown chemicals flood out of it. ")
+ var/datum/reagents/R = new/datum/reagents(300)
+ R.my_atom = src
+ R.add_reagent(get_random_reagent_id(), 100)
+ var/datum/effect_system/foam_spread/foam = new
+ foam.set_up(200, loc, R)
+ foam.start()
+ if(3)
+ visible_message("
[user] has pressed one of the colorful buttons on [src] and the clown car turns on its singularity disguise system. ")
+ icon = 'icons/obj/singularity.dmi'
+ icon_state = "singularity_s1"
+ addtimer(CALLBACK(src, .proc/ResetIcon), 100)
+ if(4)
+ visible_message("
[user] has pressed one of the colorful buttons on [src] and the clown car spews out a cloud of laughing gas. ")
+ var/datum/reagents/R = new/datum/reagents(300)
+ R.my_atom = src
+ R.add_reagent("superlaughter", 50)
+ var/datum/effect_system/smoke_spread/chem/smoke = new()
+ smoke.set_up(R, 4)
+ smoke.attach(src)
+ smoke.start()
+ if(5)
+ visible_message("
[user] has pressed one of the colorful buttons on [src] and the clown car starts dropping an oil trail. ")
+ droppingoil = TRUE
+ addtimer(CALLBACK(src, .proc/StopDroppingOil), 30)
+ if(6)
+ visible_message("
[user] has pressed one of the colorful buttons on [src] and the clown car lets out a comedic toot. ")
+ playsound(src, 'sound/vehicles/clowncar_fart.ogg', 100)
+ for(var/mob/living/L in orange(loc, 6))
+ L.emote("laughs")
+ for(var/mob/living/L in occupants)
+ L.emote("laughs")
+
+/obj/vehicle/sealed/car/clowncar/proc/ResetIcon()
+ icon = initial(icon)
+ icon_state = initial(icon_state)
+
+/obj/vehicle/sealed/car/clowncar/proc/StopDroppingOil()
+ droppingoil = FALSE
diff --git a/code/modules/vehicles/entered.dm b/code/modules/vehicles/entered.dm
index d0fb93f7f0..30ae49ecd2 100644
--- a/code/modules/vehicles/entered.dm
+++ b/code/modules/vehicles/entered.dm
@@ -35,22 +35,74 @@
if(!istype(M))
return FALSE
if(!silent)
- M.visible_message("
[M] climbs into \the [src]! ")
+ M.visible_message("
[M] climbs into \the [src]! ")
M.forceMove(src)
add_occupant(M)
return TRUE
-/obj/vehicle/sealed/proc/mob_try_exit(mob/M, mob/user, silent = FALSE)
- mob_exit(M, silent)
+/obj/vehicle/sealed/proc/mob_try_exit(mob/M, mob/user, silent = FALSE, randomstep = FALSE)
+ mob_exit(M, silent, randomstep)
-/obj/vehicle/sealed/proc/mob_exit(mob/M, silent = FALSE)
+/obj/vehicle/sealed/proc/mob_exit(mob/M, silent = FALSE, randomstep = FALSE)
if(!istype(M))
return FALSE
remove_occupant(M)
M.forceMove(exit_location(M))
+ if(randomstep)
+ var/turf/target_turf = get_step(exit_location(M), pick(GLOB.cardinals))
+ M.throw_at(target_turf, 5, 10)
+
if(!silent)
- M.visible_message("
[M] drops out of \the [src]! ")
+ M.visible_message("
[M] drops out of \the [src]! ")
return TRUE
/obj/vehicle/sealed/proc/exit_location(M)
return drop_location()
+
+/obj/vehicle/sealed/attackby(obj/item/I, mob/user, params)
+ if(key_type && !is_key(inserted_key) && is_key(I))
+ if(user.transferItemToLoc(I, src))
+ to_chat(user, "
You insert [I] into [src]. ")
+ if(inserted_key) //just in case there's an invalid key
+ inserted_key.forceMove(drop_location())
+ inserted_key = I
+ else
+ to_chat(user, "
[I] seems to be stuck to your hand! ")
+ return
+ return ..()
+
+/obj/vehicle/sealed/proc/remove_key(mob/user)
+ if(!inserted_key)
+ to_chat(user, "
There is no key in [src]! ")
+ return
+ if(!is_occupant(user) || !(occupants[user] & VEHICLE_CONTROL_DRIVE))
+ to_chat(user, "
You must be driving [src] to remove [src]'s key! ")
+ return
+ to_chat(user, "
You remove [inserted_key] from [src]. ")
+ inserted_key.forceMove(drop_location())
+ user.put_in_hands(inserted_key)
+ inserted_key = null
+
+/obj/vehicle/sealed/Destroy()
+ DumpMobs()
+ explosion(loc, 0, 1, 2, 3, 0)
+ return ..()
+
+/obj/vehicle/sealed/proc/DumpMobs(randomstep = TRUE)
+ for(var/i in occupants)
+ mob_exit(i, null, randomstep)
+ if(iscarbon(i))
+ var/mob/living/carbon/Carbon = i
+ Carbon.Knockdown(40)
+
+/obj/vehicle/sealed/proc/DumpSpecificMobs(flag, randomstep = TRUE)
+ for(var/i in occupants)
+ if((occupants[i] & flag))
+ mob_exit(i, null, randomstep)
+ if(iscarbon(i))
+ var/mob/living/carbon/C = i
+ C.Knockdown(40)
+
+
+/obj/vehicle/sealed/AllowDrop()
+ return FALSE
diff --git a/code/modules/vehicles/ridden.dm b/code/modules/vehicles/ridden.dm
index b15b570550..25a337bc22 100644
--- a/code/modules/vehicles/ridden.dm
+++ b/code/modules/vehicles/ridden.dm
@@ -14,7 +14,10 @@
/obj/vehicle/ridden/examine(mob/user)
. = ..()
if(key_type)
- to_chat(user, "
Put a key inside it by clicking it with the key. If there's a key inside, you can remove it via Alt-Click! ")
+ if(!inserted_key)
+ to_chat(user, "
Put a key inside it by clicking it with the key. ")
+ else
+ to_chat(user, "
Alt-click [src] to remove the key. ")
/obj/vehicle/ridden/generate_action_type(actiontype)
var/datum/action/vehicle/ridden/A = ..()
diff --git a/code/modules/vehicles/scooter.dm b/code/modules/vehicles/scooter.dm
index 2daf3897c5..9af5b13a7d 100644
--- a/code/modules/vehicles/scooter.dm
+++ b/code/modules/vehicles/scooter.dm
@@ -26,7 +26,7 @@
. = ..()
for(var/m in buckled_mobs)
var/mob/living/buckled_mob = m
- if(buckled_mob.get_num_legs() > 0)
+ if(buckled_mob.get_num_legs(FALSE) > 0)
buckled_mob.pixel_y = 5
else
buckled_mob.pixel_y = -4
@@ -41,14 +41,15 @@
/obj/vehicle/ridden/scooter/skateboard
name = "skateboard"
- desc = "An unfinished scooter which can only barely be called a skateboard. It's still rideable, but probably unsafe. Looks like you'll need to add a few rods to make handlebars."
+ desc = "An unfinished scooter which can only barely be called a skateboard. It's still rideable, but probably unsafe. Looks like you'll need to add a few rods to make handlebars. Alt-click to adjust speed."
icon_state = "skateboard"
density = FALSE
+ var/adjusted_speed = FALSE
/obj/vehicle/ridden/scooter/skateboard/Initialize()
. = ..()
var/datum/component/riding/D = LoadComponent(/datum/component/riding)
- D.vehicle_move_delay = 0
+ D.vehicle_move_delay = 1
D.set_vehicle_dir_layer(SOUTH, ABOVE_MOB_LAYER)
D.set_vehicle_dir_layer(NORTH, OBJ_LAYER)
D.set_vehicle_dir_layer(EAST, OBJ_LAYER)
@@ -63,7 +64,7 @@
density = FALSE
return ..()
-/obj/vehicle/ridden/scooter/skateboard/Collide(atom/A)
+/obj/vehicle/ridden/scooter/skateboard/Bump(atom/A)
. = ..()
if(A.density && has_buckled_mobs())
var/mob/living/H = buckled_mobs[1]
@@ -92,6 +93,17 @@
M.put_in_hands(board)
qdel(src)
+/obj/vehicle/ridden/scooter/skateboard/AltClick(mob/user)
+ var/datum/component/riding/R = src.GetComponent(/datum/component/riding)
+ if (!adjusted_speed)
+ R.vehicle_move_delay = 0
+ to_chat(user, "
You adjust the wheels on [src] to make it go faster. ")
+ adjusted_speed = TRUE
+ else
+ R.vehicle_move_delay = 1
+ to_chat(user, "
You adjust the wheels on [src] to make it go slower. ")
+ adjusted_speed = FALSE
+
//CONSTRUCTION
/obj/item/scooter_frame
name = "scooter frame"
@@ -136,6 +148,8 @@
return ..()
/obj/vehicle/ridden/scooter/skateboard/screwdriver_act(mob/living/user, obj/item/I)
+ if(..())
+ return TRUE
to_chat(user, "
You begin to deconstruct and remove the wheels on [src]... ")
if(I.use_tool(src, user, 20, volume=50))
to_chat(user, "
You deconstruct the wheels on [src]. ")
@@ -176,7 +190,7 @@
to_chat(M, "
You pop out the Wheely-Heel's wheels. ")
return ..()
-/obj/vehicle/ridden/scooter/wheelys/Collide(atom/A)
+/obj/vehicle/ridden/scooter/wheelys/Bump(atom/A)
. = ..()
if(A.density && has_buckled_mobs())
var/mob/living/H = buckled_mobs[1]
diff --git a/code/modules/vehicles/speedbike.dm b/code/modules/vehicles/speedbike.dm
index e05504b0dc..6526e6d89a 100644
--- a/code/modules/vehicles/speedbike.dm
+++ b/code/modules/vehicles/speedbike.dm
@@ -47,7 +47,7 @@
var/static/mutable_appearance/overlay = mutable_appearance(icon, "speedwagon_cover", ABOVE_MOB_LAYER)
max_buckled_mobs = 4
var/crash_all = FALSE //CHAOS
- pixel_y = -48 //to fix the offset when Initialized()
+ pixel_y = -48
pixel_x = -48
/obj/vehicle/ridden/space/speedwagon/Initialize()
@@ -59,10 +59,14 @@
D.set_riding_offsets(2, list(TEXT_NORTH = list(19, -5, 4), TEXT_SOUTH = list(-13, 3, 4), TEXT_EAST = list(-4, -3, 4.1), TEXT_WEST = list(4, 28, 3.9)))
D.set_riding_offsets(3, list(TEXT_NORTH = list(-10, -18, 4.2), TEXT_SOUTH = list(16, 25, 3.9), TEXT_EAST = list(-22, 30), TEXT_WEST = list(22, -3, 4.1)))
D.set_riding_offsets(4, list(TEXT_NORTH = list(19, -18, 4.2), TEXT_SOUTH = list(-13, 25, 3.9), TEXT_EAST = list(-22, 3, 3.9), TEXT_WEST = list(22, 28)))
+ D.set_vehicle_dir_offsets(NORTH, -48, -48)
+ D.set_vehicle_dir_offsets(SOUTH, -48, -48)
+ D.set_vehicle_dir_offsets(EAST, -48, -48)
+ D.set_vehicle_dir_offsets(WEST, -48, -48)
for(var/i in GLOB.cardinals)
D.set_vehicle_dir_layer(i, BELOW_MOB_LAYER)
-/obj/vehicle/ridden/space/speedwagon/Collide(atom/movable/A)
+/obj/vehicle/ridden/space/speedwagon/Bump(atom/movable/A)
. = ..()
if(A.density && has_buckled_mobs())
var/atom/throw_target = get_edge_target_turf(A, dir)
@@ -85,4 +89,4 @@
if(has_buckled_mobs())
for(var/atom/A in range(2, src))
if(!(A in buckled_mobs))
- Collide(A)
+ Bump(A)
diff --git a/code/modules/vehicles/vehicle_actions.dm b/code/modules/vehicles/vehicle_actions.dm
index 905be5b3ba..4ff6770e85 100644
--- a/code/modules/vehicles/vehicle_actions.dm
+++ b/code/modules/vehicles/vehicle_actions.dm
@@ -103,6 +103,7 @@
/datum/action/vehicle/sealed/climb_out
name = "Climb Out"
desc = "Climb out of your vehicle!"
+ button_icon_state = "car_eject"
/datum/action/vehicle/sealed/climb_out/Trigger()
if(..() && istype(vehicle_entered_target))
@@ -110,3 +111,56 @@
/datum/action/vehicle/ridden
var/obj/vehicle/ridden/vehicle_ridden_target
+
+/datum/action/vehicle/sealed/remove_key
+ name = "Remove key"
+ desc = "Take your key out of the vehicle's ignition"
+ button_icon_state = "car_removekey"
+
+/datum/action/vehicle/sealed/remove_key/Trigger()
+ vehicle_entered_target.remove_key(owner)
+
+//CLOWN CAR ACTION DATUMS
+/datum/action/vehicle/sealed/horn
+ name = "Honk Horn"
+ desc = "Honk your classy horn."
+ button_icon_state = "car_horn"
+ var/hornsound = 'sound/items/carhorn.ogg'
+ var/last_honk_time
+
+/datum/action/vehicle/sealed/horn/Trigger()
+ if(world.time - last_honk_time > 20)
+ vehicle_entered_target.visible_message("
[vehicle_entered_target] loudly honks ")
+ to_chat(owner, "
You press the vehicle's horn. ")
+ playsound(vehicle_entered_target, hornsound, 75)
+ last_honk_time = world.time
+
+/datum/action/vehicle/sealed/horn/clowncar/Trigger()
+ if(world.time - last_honk_time > 20)
+ vehicle_entered_target.visible_message("
[vehicle_entered_target] loudly honks ")
+ to_chat(owner, "
You press the vehicle's horn. ")
+ last_honk_time = world.time
+ if(vehicle_target.inserted_key)
+ vehicle_target.inserted_key.attack_self(owner) //The key plays a sound
+ else
+ playsound(vehicle_entered_target, hornsound, 75)
+
+/datum/action/vehicle/sealed/DumpKidnappedMobs
+ name = "Dump kidnapped mobs"
+ desc = "Dump all objects and people in your car on the floor."
+ button_icon_state = "car_dump"
+
+/datum/action/vehicle/sealed/DumpKidnappedMobs/Trigger()
+ vehicle_entered_target.visible_message("
[vehicle_entered_target] starts dumping the people inside of it. ")
+ vehicle_entered_target.DumpSpecificMobs(VEHICLE_CONTROL_KIDNAPPED)
+
+
+/datum/action/vehicle/sealed/RollTheDice
+ name = "Press a colorful button"
+ desc = "Press one of those colorful buttons on your display panel!"
+ button_icon_state = "car_rtd"
+
+/datum/action/vehicle/sealed/RollTheDice/Trigger()
+ if(istype(vehicle_entered_target, /obj/vehicle/sealed/car/clowncar))
+ var/obj/vehicle/sealed/car/clowncar/C = vehicle_entered_target
+ C.RollTheDice(owner)
diff --git a/code/modules/vending/boozeomat.dm b/code/modules/vending/boozeomat.dm
index 0d193c451d..735967440f 100644
--- a/code/modules/vending/boozeomat.dm
+++ b/code/modules/vending/boozeomat.dm
@@ -63,6 +63,9 @@
/obj/item/reagent_containers/food/drinks/drinkingglass/shotglass = 4);
req_access = list(ACCESS_CAPTAIN)
+/obj/machinery/vending/boozeomat/syndicate_access
+ req_access = list(ACCESS_SYNDICATE)
+
/obj/item/vending_refill/boozeomat
machine_name = "Booze-O-Mat"
icon_state = "refill_booze"
diff --git a/code/modules/vending/cigarette.dm b/code/modules/vending/cigarette.dm
index 6b9334362d..36434f6b03 100644
--- a/code/modules/vending/cigarette.dm
+++ b/code/modules/vending/cigarette.dm
@@ -20,6 +20,16 @@
/obj/item/storage/fancy/cigarettes/cigars/cohiba = 1)
refill_canister = /obj/item/vending_refill/cigarette
+/obj/machinery/vending/cigarette/syndicate
+ products = list(/obj/item/storage/fancy/cigarettes/cigpack_syndicate = 7,
+ /obj/item/storage/fancy/cigarettes/cigpack_uplift = 3,
+ /obj/item/storage/fancy/cigarettes/cigpack_robust = 2,
+ /obj/item/storage/fancy/cigarettes/cigpack_carp = 3,
+ /obj/item/storage/fancy/cigarettes/cigpack_midori = 1,
+ /obj/item/storage/box/matches = 10,
+ /obj/item/lighter/greyscale = 4,
+ /obj/item/storage/fancy/rollingpapers = 5)
+
/obj/machinery/vending/cigarette/beach //Used in the lavaland_biodome_beach.dmm ruin
name = "\improper ShadyCigs Ultra"
desc = "Now with extra premium products!"
diff --git a/code/modules/vending/medical.dm b/code/modules/vending/medical.dm
index bea449845c..5ff07cc842 100644
--- a/code/modules/vending/medical.dm
+++ b/code/modules/vending/medical.dm
@@ -38,3 +38,7 @@
/obj/item/vending_refill/medical
machine_name = "NanoMed Plus"
icon_state = "refill_medical"
+
+/obj/machinery/vending/medical/syndicate_access
+ name = "\improper SyndiMed Plus"
+ req_access = list(ACCESS_SYNDICATE)
\ No newline at end of file
diff --git a/code/modules/vending/medical_wall.dm b/code/modules/vending/medical_wall.dm
index ef19ce3b9b..84c4891589 100644
--- a/code/modules/vending/medical_wall.dm
+++ b/code/modules/vending/medical_wall.dm
@@ -20,3 +20,9 @@
/obj/item/vending_refill/wallmed
machine_name = "NanoMed"
icon_state = "refill_medical"
+
+/obj/machinery/vending/wallmed/pubby
+ products = list(/obj/item/reagent_containers/syringe = 3,
+ /obj/item/reagent_containers/pill/patch/styptic = 1,
+ /obj/item/reagent_containers/pill/patch/silver_sulf = 1,
+ /obj/item/reagent_containers/medspray/sterilizine = 1)
diff --git a/code/modules/zombie/items.dm b/code/modules/zombie/items.dm
index 03d2ac3921..8b56ccf00d 100644
--- a/code/modules/zombie/items.dm
+++ b/code/modules/zombie/items.dm
@@ -31,11 +31,11 @@
return
else if(isliving(target))
if(ishuman(target))
- check_infection(target, user)
+ try_to_zombie_infect(target)
else
check_feast(target, user)
-/obj/item/zombie_hand/proc/check_infection(mob/living/carbon/human/target, mob/user)
+/proc/try_to_zombie_infect(mob/living/carbon/human/target)
CHECK_DNA_AND_SPECIES(target)
if(NOZOMBIE in target.dna.species.species_traits)
@@ -50,6 +50,7 @@
infection.Insert(target)
+
/obj/item/zombie_hand/suicide_act(mob/user)
user.visible_message("
[user] is ripping [user.p_their()] brains out! It looks like [user.p_theyre()] trying to commit suicide! ")
if(isliving(user))
diff --git a/code/modules/zombie/organs.dm b/code/modules/zombie/organs.dm
index 12aa2e41f6..329cc127ab 100644
--- a/code/modules/zombie/organs.dm
+++ b/code/modules/zombie/organs.dm
@@ -4,6 +4,7 @@
zone = BODY_ZONE_HEAD
slot = ORGAN_SLOT_ZOMBIE
icon_state = "blacktumor"
+ var/causes_damage = TRUE
var/datum/species/old_species = /datum/species/human
var/living_transformation_time = 30
var/converts_living = FALSE
@@ -44,7 +45,10 @@
return
if(!(src in owner.internal_organs))
Remove(owner)
-
+ if (causes_damage && !iszombie(owner) && owner.stat != DEAD)
+ owner.adjustToxLoss(1)
+ if (prob(10))
+ to_chat(owner, "
You feel sick... ")
if(timer_id)
return
if(owner.suiciding)
@@ -64,16 +68,21 @@
/obj/item/organ/zombie_infection/proc/zombify()
timer_id = null
+ if(!converts_living && owner.stat != DEAD)
+ return
+
if(!iszombie(owner))
old_species = owner.dna.species.type
owner.set_species(/datum/species/zombie/infectious)
- if(!converts_living && owner.stat != DEAD)
- return
-
var/stand_up = (owner.stat == DEAD) || (owner.stat == UNCONSCIOUS)
- if(!owner.revive(full_heal = TRUE))
+ //Fully heal the zombie's damage the first time they rise
+ owner.setToxLoss(0, 0)
+ owner.setOxyLoss(0, 0)
+ owner.heal_overall_damage(INFINITY, INFINITY, INFINITY, FALSE, FALSE, TRUE)
+
+ if(!owner.revive())
return
owner.grab_ghost()
@@ -82,3 +91,6 @@
owner.do_jitter_animation(living_transformation_time)
owner.Stun(living_transformation_time)
to_chat(owner, "
You are now a zombie! ")
+
+/obj/item/organ/zombie_infection/nodamage
+ causes_damage = FALSE
diff --git a/modular_citadel/code/modules/mob/living/carbon/human/species_types/furrypeople.dm b/modular_citadel/code/modules/mob/living/carbon/human/species_types/furrypeople.dm
index ebf114c2de..4eba44598c 100644
--- a/modular_citadel/code/modules/mob/living/carbon/human/species_types/furrypeople.dm
+++ b/modular_citadel/code/modules/mob/living/carbon/human/species_types/furrypeople.dm
@@ -13,9 +13,34 @@
liked_food = MEAT | FRIED
disliked_food = TOXIC
+//Curiosity killed the cat's wagging tail.
/datum/species/mammal/spec_death(gibbed, mob/living/carbon/human/H)
if(H)
- H.endTailWag()
+ stop_wagging_tail(H)
+
+/datum/species/mammal/spec_stun(mob/living/carbon/human/H,amount)
+ if(H)
+ stop_wagging_tail(H)
+ . = ..()
+
+/datum/species/mammal/can_wag_tail(mob/living/carbon/human/H)
+ return ("mam_tail" in mutant_bodyparts) || ("mam_waggingtail" in mutant_bodyparts)
+
+/datum/species/mammal/is_wagging_tail(mob/living/carbon/human/H)
+ return ("mam_waggingtail" in mutant_bodyparts)
+
+/datum/species/mammal/start_wagging_tail(mob/living/carbon/human/H)
+ if("tail_human" in mutant_bodyparts)
+ mutant_bodyparts -= "mam_tail"
+ mutant_bodyparts |= "mam_waggingtail"
+ H.update_body()
+
+/datum/species/mammal/stop_wagging_tail(mob/living/carbon/human/H)
+ if("mam_waggingtail" in mutant_bodyparts)
+ mutant_bodyparts -= "mam_waggingtail"
+ mutant_bodyparts |= "mam_tail"
+ H.update_body()
+
/datum/species/mammal/qualifies_for_rank(rank, list/features)
return TRUE
@@ -45,7 +70,30 @@
/datum/species/avian/spec_death(gibbed, mob/living/carbon/human/H)
if(H)
- H.endTailWag()
+ stop_wagging_tail(H)
+
+/datum/species/avian/spec_stun(mob/living/carbon/human/H,amount)
+ if(H)
+ stop_wagging_tail(H)
+ . = ..()
+
+/datum/species/avian/can_wag_tail(mob/living/carbon/human/H)
+ return ("mam_tail" in mutant_bodyparts) || ("mam_waggingtail" in mutant_bodyparts)
+
+/datum/species/avian/is_wagging_tail(mob/living/carbon/human/H)
+ return ("mam_waggingtail" in mutant_bodyparts)
+
+/datum/species/avian/start_wagging_tail(mob/living/carbon/human/H)
+ if("tail_human" in mutant_bodyparts)
+ mutant_bodyparts -= "mam_tail"
+ mutant_bodyparts |= "mam_waggingtail"
+ H.update_body()
+
+/datum/species/avian/stop_wagging_tail(mob/living/carbon/human/H)
+ if("mam_waggingtail" in mutant_bodyparts)
+ mutant_bodyparts -= "mam_waggingtail"
+ mutant_bodyparts |= "mam_tail"
+ H.update_body()
/datum/species/avian/qualifies_for_rank(rank, list/features)
return TRUE
@@ -76,7 +124,30 @@
/datum/species/aquatic/spec_death(gibbed, mob/living/carbon/human/H)
if(H)
- H.endTailWag()
+ stop_wagging_tail(H)
+
+/datum/species/aquatic/spec_stun(mob/living/carbon/human/H,amount)
+ if(H)
+ stop_wagging_tail(H)
+ . = ..()
+
+/datum/species/aquatic/can_wag_tail(mob/living/carbon/human/H)
+ return ("mam_tail" in mutant_bodyparts) || ("mam_waggingtail" in mutant_bodyparts)
+
+/datum/species/aquatic/is_wagging_tail(mob/living/carbon/human/H)
+ return ("mam_waggingtail" in mutant_bodyparts)
+
+/datum/species/aquatic/start_wagging_tail(mob/living/carbon/human/H)
+ if("tail_human" in mutant_bodyparts)
+ mutant_bodyparts -= "mam_tail"
+ mutant_bodyparts |= "mam_waggingtail"
+ H.update_body()
+
+/datum/species/aquatic/stop_wagging_tail(mob/living/carbon/human/H)
+ if("mam_waggingtail" in mutant_bodyparts)
+ mutant_bodyparts -= "mam_waggingtail"
+ mutant_bodyparts |= "mam_tail"
+ H.update_body()
/datum/species/aquatic/qualifies_for_rank(rank, list/features)
return TRUE
@@ -106,7 +177,30 @@
/datum/species/insect/spec_death(gibbed, mob/living/carbon/human/H)
if(H)
- H.endTailWag()
+ stop_wagging_tail(H)
+
+/datum/species/insect/spec_stun(mob/living/carbon/human/H,amount)
+ if(H)
+ stop_wagging_tail(H)
+ . = ..()
+
+/datum/species/insect/can_wag_tail(mob/living/carbon/human/H)
+ return ("mam_tail" in mutant_bodyparts) || ("mam_waggingtail" in mutant_bodyparts)
+
+/datum/species/insect/is_wagging_tail(mob/living/carbon/human/H)
+ return ("mam_waggingtail" in mutant_bodyparts)
+
+/datum/species/insect/start_wagging_tail(mob/living/carbon/human/H)
+ if("tail_human" in mutant_bodyparts)
+ mutant_bodyparts -= "mam_tail"
+ mutant_bodyparts |= "mam_waggingtail"
+ H.update_body()
+
+/datum/species/insect/stop_wagging_tail(mob/living/carbon/human/H)
+ if("mam_waggingtail" in mutant_bodyparts)
+ mutant_bodyparts -= "mam_waggingtail"
+ mutant_bodyparts |= "mam_tail"
+ H.update_body()
/datum/species/insect/qualifies_for_rank(rank, list/features)
return TRUE
diff --git a/modular_citadel/code/modules/mob/living/carbon/human/species_types/jellypeople.dm b/modular_citadel/code/modules/mob/living/carbon/human/species_types/jellypeople.dm
index c91be1c7ac..59c4bc5ef1 100644
--- a/modular_citadel/code/modules/mob/living/carbon/human/species_types/jellypeople.dm
+++ b/modular_citadel/code/modules/mob/living/carbon/human/species_types/jellypeople.dm
@@ -94,7 +94,7 @@
organ.forceMove(get_turf(H))
qdel(organ)
H.update_body()
-
+
else if (select_alteration == "Ears")
var/list/snowflake_ears_list = list("Normal" = null)
for(var/path in GLOB.mam_ears_list)
@@ -108,7 +108,7 @@
if(new_ears)
H.dna.features["mam_ears"] = new_ears
H.update_body()
-
+
else if (select_alteration == "Tail")
var/list/snowflake_tails_list = list("Normal" = null)
for(var/path in GLOB.mam_tails_list)
@@ -124,7 +124,7 @@
if(new_tail != "None")
H.dna.features["taur"] = "None"
H.update_body()
-
+
else if (select_alteration == "Taur body")
var/list/snowflake_taur_list = list("Normal" = null)
for(var/path in GLOB.taur_list)