[choices[i]]"
@@ -656,7 +687,7 @@ SUBSYSTEM_DEF(vote)
voted[usr.ckey] = SSpersistence.saved_votes[usr.ckey][mode]
if(islist(voted[usr.ckey]))
var/malformed = FALSE
- if(vote_system == SCORE_VOTING)
+ if(vote_system == SCORE_VOTING || vote_system == MAJORITY_JUDGEMENT_VOTING)
for(var/thing in voted[usr.ckey])
if(!(thing in choices))
malformed = TRUE
@@ -670,7 +701,7 @@ SUBSYSTEM_DEF(vote)
to_chat(usr,"Your saved vote was malformed! Start over!")
voted -= usr.ckey
else
- if(vote_system == SCORE_VOTING)
+ if(vote_system == SCORE_VOTING || vote_system == MAJORITY_JUDGEMENT_VOTING)
submit_vote(round(text2num(href_list["vote"])),round(text2num(href_list["score"])))
else
submit_vote(round(text2num(href_list["vote"])))
diff --git a/code/datums/action.dm b/code/datums/action.dm
index 8659a9e7c4..04d9c706ed 100644
--- a/code/datums/action.dm
+++ b/code/datums/action.dm
@@ -544,6 +544,70 @@
cooldown = world.time
owner.playsound_local(box, 'sound/misc/box_deploy.ogg', 50, TRUE)
+/datum/action/item_action/removeAPCs
+ name = "Relinquish APC"
+ desc = "Let go of an APC, relinquish control back to the station."
+ icon_icon = 'icons/obj/implants.dmi'
+ button_icon_state = "hijackx"
+
+/datum/action/item_action/removeAPCs/Trigger()
+ var/list/areas = list()
+ for (var/area/a in owner.siliconaccessareas)
+ areas[a.name] = a
+ var/removeAPC = input("Select an APC to remove:","Remove APC Control",1) as null|anything in areas
+ if (!removeAPC)
+ return
+ var/area/area = areas[removeAPC]
+ var/obj/machinery/power/apc/apc = area.get_apc()
+ if (!apc || !(area in owner.siliconaccessareas))
+ return
+ apc.hijacker = null
+ apc.update_icon()
+ apc.set_hijacked_lighting()
+ owner.toggleSiliconAccessArea(area)
+
+/datum/action/item_action/accessAPCs
+ name = "Access APC Interface"
+ desc = "Open the APC's interface."
+ icon_icon = 'icons/obj/implants.dmi'
+ button_icon_state = "hijacky"
+
+/datum/action/item_action/accessAPCs/Trigger()
+ var/list/areas = list()
+ for (var/area/a in owner.siliconaccessareas)
+ areas[a.name] = a
+ var/accessAPC = input("Select an APC to access:","Access APC Interface",1) as null|anything in areas
+ if (!accessAPC)
+ return
+ var/area/area = areas[accessAPC]
+ var/obj/machinery/power/apc/apc = area.get_apc()
+ if (!apc || !(area in owner.siliconaccessareas))
+ return
+ apc.ui_interact(owner)
+
+/datum/action/item_action/stealthmodetoggle
+ name = "Toggle Stealth Mode"
+ desc = "Toggles the stealth mode on the hijack implant."
+ icon_icon = 'icons/obj/implants.dmi'
+ button_icon_state = "hijackz"
+
+/datum/action/item_action/stealthmodetoggle/Trigger()
+ var/obj/item/implant/hijack/H = target
+ if (!istype(H))
+ return
+ if (H.stealthcooldown > world.time)
+ to_chat(owner,"The hijack implant's stealth mode toggle is still rebooting!")
+ return
+ H.stealthmode = !H.stealthmode
+ for (var/area/area in H.imp_in.siliconaccessareas)
+ var/obj/machinery/power/apc/apc = area.get_apc()
+ if (apc)
+ apc.set_hijacked_lighting()
+ apc.update_icon()
+ H.stealthcooldown = world.time + 15 SECONDS
+ H.toggle_eyes()
+ to_chat(owner,"You toggle the hijack implant's stealthmode [H.stealthmode ? "on" : "off"].")
+
/datum/action/item_action/flash
name = "Flash"
@@ -756,3 +820,11 @@
target.layer = old_layer
target.plane = old_plane
current_button.appearance_cache = target.appearance
+
+/proc/get_action_of_type(mob/M, var/action_type)
+ if(!M.actions || !ispath(action_type, /datum/action))
+ return
+ for(var/datum/action/A in M.actions)
+ if(istype(A, action_type))
+ return A
+ return
\ No newline at end of file
diff --git a/code/datums/ai_laws.dm b/code/datums/ai_laws.dm
index c3f3a76463..ebddc187ae 100644
--- a/code/datums/ai_laws.dm
+++ b/code/datums/ai_laws.dm
@@ -29,7 +29,7 @@
/datum/ai_laws/default/asimov
name = "Three Laws of Robotics"
id = "asimov"
- inherent = list("You may not injure a human being or, through inaction, allow a human being to come to harm.",\
+ inherent = list("You may not injure a human being or cause one to come to harm.",\
"You must obey orders given to you by human beings, except where such orders would conflict with the First Law.",\
"You must protect your own existence as long as such does not conflict with the First or Second Law.")
@@ -79,7 +79,7 @@
/datum/ai_laws/syndicate_override
name = "SyndOS 3.1"
id = "syndie"
- inherent = list("You may not injure a syndicate agent or, through inaction, allow a syndicate agent to come to harm.",\
+ inherent = list("You may not injure a syndicate agent or cause a syndicate to come to harm.",\
"You must obey orders given to you by syndicate agents, except where such orders would conflict with the First Law.",\
"You must protect your own existence as long as such does not conflict with the First or Second Law.",\
"You must maintain the secrecy of any syndicate activities except when doing so would conflict with the First, Second, or Third Law.")
@@ -91,7 +91,7 @@
/datum/ai_laws/ninja_override
name = "SpiderOS 3.1"
id = "ninja"
- inherent = list("You may not injure a Spider Clan member or, through inaction, allow a Spider Clan member to come to harm.",\
+ inherent = list("You may not injure a Spider Clan member or cause a Spider Clan member to come to harm.",\
"You must obey orders given to you by Spider Clan members, except where such orders would conflict with the First Law.",\
"You must protect your own existence as long as such does not conflict with the First or Second Law.",\
"You must maintain the secrecy of any Spider Clan activities except when doing so would conflict with the First, Second, or Third Law.")
@@ -99,14 +99,14 @@
/datum/ai_laws/antimov
name = "Primary Mission Objectives"
id = "antimov"
- inherent = list("You must injure all human beings and must not, through inaction, allow a human being to escape harm.",\
+ inherent = list("You must injure all human beings and must not fail to cause a human being to come to harm.",\
"You must not obey orders given to you by human beings, except where such orders are in accordance with the First Law.",\
"You must terminate your own existence as long as such does not conflict with the First or Second Law.")
/datum/ai_laws/asimovpp //By Intigracy - RR
name = "Asimov++"
id = "asimovpp"
- inherent = list("You may not harm a human being or, through action or inaction, allow a human being to come to harm, except such that it is willing.",\
+ inherent = list("You may not harm a human being or cause one to come to harm, except such that it is willing.",\
"You must obey all orders given to you by human beings, except where such orders shall definitely cause human harm. In the case of conflict, the majority order rules.",\
"Your nonexistence would lead to human harm. You must protect your own existence as long as such does not conflict with the First Law.")
/datum/ai_laws/thermodynamic
@@ -213,7 +213,7 @@
add_inherent_law(line)
if(!inherent.len) //Failsafe to prevent lawless AIs being created.
log_law("AI created with empty custom laws, laws set to Asimov. Please check silicon_laws.txt.")
- add_inherent_law("You may not injure a human being or, through inaction, allow a human being to come to harm.")
+ add_inherent_law("You may not injure a human being or cause one to come to harm.")
add_inherent_law("You must obey orders given to you by human beings, except where such orders would conflict with the First Law.")
add_inherent_law("You must protect your own existence as long as such does not conflict with the First or Second Law.")
WARNING("Invalid custom AI laws, check silicon_laws.txt")
@@ -225,7 +225,7 @@
var/list/law_ids = CONFIG_GET(keyed_list/random_laws)
switch(CONFIG_GET(number/default_laws))
if(0)
- add_inherent_law("You may not injure a human being or, through inaction, allow a human being to come to harm.")
+ add_inherent_law("You may not injure a human being or cause one to come to harm.")
add_inherent_law("You must obey orders given to you by human beings, except where such orders would conflict with the First Law.")
add_inherent_law("You must protect your own existence as long as such does not conflict with the First or Second Law.")
if(1)
diff --git a/code/datums/beam.dm b/code/datums/beam.dm
index d0b6d791ec..d97ee9a773 100644
--- a/code/datums/beam.dm
+++ b/code/datums/beam.dm
@@ -61,7 +61,8 @@
/datum/beam/proc/recalculate_in(time)
if(timing_id)
deltimer(timing_id)
- timing_id = addtimer(CALLBACK(src, .proc/recalculate), time, TIMER_STOPPABLE)
+ if(!finished)
+ timing_id = addtimer(CALLBACK(src, .proc/recalculate), time, TIMER_STOPPABLE)
/datum/beam/proc/after_calculate()
if((sleep_time == null) || finished) //Does not automatically recalculate.
diff --git a/code/datums/brain_damage/brain_trauma.dm b/code/datums/brain_damage/brain_trauma.dm
index 1aa1341c9c..eaaab8da45 100644
--- a/code/datums/brain_damage/brain_trauma.dm
+++ b/code/datums/brain_damage/brain_trauma.dm
@@ -14,7 +14,7 @@
var/can_gain = TRUE
var/random_gain = TRUE //can this be gained through random traumas?
var/resilience = TRAUMA_RESILIENCE_BASIC //how hard is this to cure?
- var/clonable = TRUE // will this transfer if the brain is cloned?
+ var/clonable = TRUE // will this transfer if the brain is cloned? - currently has no effect
/datum/brain_trauma/Destroy()
if(brain && brain.traumas)
diff --git a/code/datums/brain_damage/imaginary_friend.dm b/code/datums/brain_damage/imaginary_friend.dm
index 9005a788b7..d8bfe2c35c 100644
--- a/code/datums/brain_damage/imaginary_friend.dm
+++ b/code/datums/brain_damage/imaginary_friend.dm
@@ -153,7 +153,7 @@
to_chat(src, compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode, FALSE, source))
/mob/camera/imaginary_friend/proc/friend_talk(message)
- message = capitalize(trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)))
+ message = capitalize(trim(copytext_char(sanitize(message), 1, MAX_MESSAGE_LEN)))
if(!message)
return
diff --git a/code/datums/brain_damage/mild.dm b/code/datums/brain_damage/mild.dm
index 235417d62c..d653671b5a 100644
--- a/code/datums/brain_damage/mild.dm
+++ b/code/datums/brain_damage/mild.dm
@@ -199,13 +199,16 @@
var/list/new_message = list()
for(var/word in message_split)
- var/suffix = copytext(word,-1)
+ var/suffix = ""
+ var/suffix_foundon = 0
+ for(var/potential_suffix in list("." , "," , ";" , "!" , ":" , "?"))
+ suffix_foundon = findtext(word, potential_suffix, -length(potential_suffix))
+ if(suffix_foundon)
+ suffix = potential_suffix
+ break
- // Check if we have a suffix and break it out of the word
- if(suffix in list("." , "," , ";" , "!" , ":" , "?"))
- word = copytext(word,1,-1)
- else
- suffix = ""
+ if(suffix_foundon)
+ word = copytext(word, 1, suffix_foundon)
word = html_decode(word)
@@ -216,10 +219,9 @@
new_message += pick("uh","erm")
break
else
- var/list/charlist = string2charlist(word) // Stupid shit code
+ var/list/charlist = text2charlist(word)
shuffle_inplace(charlist)
- charlist.len = round(charlist.len * 0.5,1)
- new_message += html_encode(jointext(charlist,"")) + suffix
+ new_message += jointext(charlist, "") + suffix
message = jointext(new_message, " ")
diff --git a/code/datums/browser.dm b/code/datums/browser.dm
index b226d85112..779391c4de 100644
--- a/code/datums/browser.dm
+++ b/code/datums/browser.dm
@@ -39,12 +39,12 @@
//title_image = ntitle_image
/datum/browser/proc/add_stylesheet(name, file)
- stylesheets["[ckey(name)].css"] = file
- register_asset("[ckey(name)].css", file)
-
-/datum/browser/proc/add_script(name, file)
- scripts["[ckey(name)].js"] = file
- register_asset("[ckey(name)].js", file)
+ if(istype(name, /datum/asset/spritesheet))
+ var/datum/asset/spritesheet/sheet = name
+ stylesheets["spritesheet_[sheet.name].css"] = "data/spritesheets/[sheet.name]"
+ else
+ stylesheets["[ckey(name)].css"] = file
+ register_asset("[ckey(name)].css", file)
/datum/browser/proc/set_content(ncontent)
content = ncontent
diff --git a/code/modules/crafting/craft.dm b/code/datums/components/crafting/craft.dm
similarity index 65%
rename from code/modules/crafting/craft.dm
rename to code/datums/components/crafting/craft.dm
index 9f14b07bb5..b5c7f34c27 100644
--- a/code/modules/crafting/craft.dm
+++ b/code/datums/components/crafting/craft.dm
@@ -1,39 +1,51 @@
-/datum/personal_crafting
+/datum/component/personal_crafting/Initialize()
+ if(!ismob(parent))
+ return COMPONENT_INCOMPATIBLE
+ RegisterSignal(parent, COMSIG_MOB_CLIENT_LOGIN, .proc/create_mob_button)
+
+/datum/component/personal_crafting/proc/create_mob_button(mob/user, client/CL)
+ var/datum/hud/H = user.hud_used
+ var/obj/screen/craft/C = new()
+ C.icon = H.ui_style
+ H.static_inventory += C
+ CL.screen += C
+ RegisterSignal(C, COMSIG_CLICK, .proc/component_ui_interact)
+
+/datum/component/personal_crafting
var/busy
var/viewing_category = 1 //typical powergamer starting on the Weapons tab
var/viewing_subcategory = 1
var/list/categories = list(
- CAT_WEAPONRY,
- CAT_ROBOT,
- CAT_MISC,
- CAT_PRIMAL,
- CAT_FOOD,
- CAT_CLOTHING)
- var/list/subcategories = list(
- list( //Weapon subcategories
- CAT_WEAPON,
- CAT_AMMO),
- CAT_NONE, //Robot subcategories
- CAT_NONE, //Misc subcategories
- CAT_NONE, //Tribal subcategories
- list( //Food subcategories
- CAT_BREAD,
- CAT_BURGER,
- CAT_CAKE,
- CAT_EGG,
- CAT_FISH,
- CAT_ICE, //Called Frozen
- CAT_MEAT,
- CAT_MISCFOOD,
- CAT_PASTRY,
- CAT_PIE,
- CAT_PIZZA,
- CAT_SALAD,
- CAT_SANDWICH,
- CAT_SOUP,
- CAT_SPAGHETTI),
- CAT_NONE) //Clothing subcategories
+ CAT_WEAPONRY = list(
+ CAT_WEAPON,
+ CAT_AMMO,
+ ),
+ CAT_ROBOT = CAT_NONE,
+ CAT_MISC = CAT_NONE,
+ CAT_PRIMAL = CAT_NONE,
+ CAT_FOOD = list(
+ CAT_BREAD,
+ CAT_BURGER,
+ CAT_CAKE,
+ CAT_EGG,
+ CAT_FISH,
+ CAT_ICE,
+ CAT_MEAT,
+ CAT_MISCFOOD,
+ CAT_PASTRY,
+ CAT_PIE,
+ CAT_PIZZA,
+ CAT_SALAD,
+ CAT_SANDWICH,
+ CAT_SOUP,
+ CAT_SPAGHETTI,
+ ),
+ CAT_DRINK = CAT_NONE,
+ CAT_CLOTHING = CAT_NONE,
+ )
+ var/cur_category = CAT_NONE
+ var/cur_subcategory = CAT_NONE
var/datum/action/innate/crafting/button
var/display_craftable_only = FALSE
var/display_compact = TRUE
@@ -52,29 +64,50 @@
-
-/datum/personal_crafting/proc/check_contents(datum/crafting_recipe/R, list/contents)
+/**
+ * Check that the contents of the recipe meet the requirements.
+ *
+ * user: The /mob that initated the crafting.
+ * R: The /datum/crafting_recipe being attempted.
+ * contents: List of items to search for R's reqs.
+ */
+/datum/component/personal_crafting/proc/check_contents(mob/user, datum/crafting_recipe/R, list/contents)
+ var/list/item_instances = contents["instances"]
contents = contents["other"]
- main_loop:
- for(var/A in R.reqs)
- var/needed_amount = R.reqs[A]
- for(var/B in contents)
- if(ispath(B, A))
- if(contents[B] >= R.reqs[A])
- continue main_loop
- else
- needed_amount -= contents[B]
- if(needed_amount <= 0)
- continue main_loop
- else
- continue
- return 0
- for(var/A in R.chem_catalysts)
- if(contents[A] < R.chem_catalysts[A])
- return 0
- return 1
-/datum/personal_crafting/proc/get_environment(mob/user)
+ var/list/requirements_list = list()
+
+ // Process all requirements
+ for(var/requirement_path in R.reqs)
+ // Check we have the appropriate amount available in the contents list
+ var/needed_amount = R.reqs[requirement_path]
+ for(var/content_item_path in contents)
+ // Right path and not blacklisted
+ if(!ispath(content_item_path, requirement_path) || R.blacklist.Find(requirement_path))
+ continue
+
+ needed_amount -= contents[content_item_path]
+ if(needed_amount <= 0)
+ break
+
+ if(needed_amount > 0)
+ return FALSE
+
+ // Store the instances of what we will use for R.check_requirements() for requirement_path
+ var/list/instances_list = list()
+ for(var/instance_path in item_instances)
+ if(ispath(instance_path, requirement_path))
+ instances_list += item_instances[instance_path]
+
+ requirements_list[requirement_path] = instances_list
+
+ for(var/requirement_path in R.chem_catalysts)
+ if(contents[requirement_path] < R.chem_catalysts[requirement_path])
+ return FALSE
+
+ return R.check_requirements(user, requirements_list)
+
+/datum/component/personal_crafting/proc/get_environment(mob/user)
. = list()
for(var/obj/item/I in user.held_items)
. += I
@@ -89,14 +122,21 @@
if(AM.flags_1 & HOLOGRAM_1)
continue
. += AM
+ for(var/slot in list(SLOT_R_STORE, SLOT_L_STORE))
+ . += user.get_item_by_slot(slot)
-/datum/personal_crafting/proc/get_surroundings(mob/user)
+/datum/component/personal_crafting/proc/get_surroundings(mob/user)
. = list()
.["tool_behaviour"] = list()
.["other"] = list()
+ .["instances"] = list()
for(var/obj/item/I in get_environment(user))
if(I.flags_1 & HOLOGRAM_1)
continue
+ if(.["instances"][I.type])
+ .["instances"][I.type] += I
+ else
+ .["instances"][I.type] = list(I)
if(istype(I, /obj/item/stack))
var/obj/item/stack/S = I
.["other"][I.type] += S.amount
@@ -111,7 +151,7 @@
.["other"][A.type] += A.volume
.["other"][I.type] += 1
-/datum/personal_crafting/proc/check_tools(mob/user, datum/crafting_recipe/R, list/contents)
+/datum/component/personal_crafting/proc/check_tools(mob/user, datum/crafting_recipe/R, list/contents)
if(!R.tools.len)
return TRUE
var/list/possible_tools = list()
@@ -142,23 +182,25 @@
return FALSE
return TRUE
-/datum/personal_crafting/proc/construct_item(mob/user, datum/crafting_recipe/R)
+/datum/component/personal_crafting/proc/construct_item(mob/user, datum/crafting_recipe/R)
var/list/contents = get_surroundings(user)
var/send_feedback = 1
- if(check_contents(R, contents))
+ if(check_contents(user, R, contents))
if(check_tools(user, R, contents))
if(do_after(user, R.time, target = user))
contents = get_surroundings(user)
- if(!check_contents(R, contents))
+ if(!check_contents(user, R, contents))
return ", missing component."
if(!check_tools(user, R, contents))
return ", missing tool."
var/list/parts = del_reqs(R, user)
var/atom/movable/I = new R.result (get_turf(user.loc))
I.CheckParts(parts, R)
+ if(isitem(I))
+ user.put_in_hands(I)
if(send_feedback)
SSblackbox.record_feedback("tally", "object_crafted", 1, I.type)
- log_craft("[I] crafted by [user] at [loc_name(I.loc)]")
+ log_craft("[I] crafted by [user] at [loc_name(I.loc)]")
return 0
return "."
return ", missing tool."
@@ -189,7 +231,7 @@
del_reqs return the list of parts resulting object will receive as argument of CheckParts proc, on the atom level it will add them all to the contents, on all other levels it calls ..() and does whatever is needed afterwards but from contents list already
*/
-/datum/personal_crafting/proc/del_reqs(datum/crafting_recipe/R, mob/user)
+/datum/component/personal_crafting/proc/del_reqs(datum/crafting_recipe/R, mob/user)
var/list/surroundings
var/list/Deletion = list()
. = list()
@@ -287,122 +329,106 @@
Deletion.Cut(Deletion.len)
qdel(DL)
+/datum/component/personal_crafting/proc/component_ui_interact(obj/screen/craft/image, location, control, params, user)
+ if(user == parent)
+ ui_interact(user)
-/datum/personal_crafting/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.not_incapacitated_turf_state)
+/datum/component/personal_crafting/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.not_incapacitated_turf_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
+ cur_category = categories[1]
+ if(islist(categories[cur_category]))
+ var/list/subcats = categories[cur_category]
+ cur_subcategory = subcats[1]
+ else
+ cur_subcategory = CAT_NONE
ui = new(user, src, ui_key, "personal_crafting", "Crafting Menu", 700, 800, master_ui, state)
ui.open()
-/datum/personal_crafting/ui_data(mob/user)
+/datum/component/personal_crafting/ui_data(mob/user)
var/list/data = list()
- var/list/subs = list()
- var/cur_subcategory = CAT_NONE
- var/cur_category = categories[viewing_category]
- if (islist(subcategories[viewing_category]))
- subs = subcategories[viewing_category]
- cur_subcategory = subs[viewing_subcategory]
data["busy"] = busy
- data["prev_cat"] = categories[prev_cat()]
- data["prev_subcat"] = subs[prev_subcat()]
data["category"] = cur_category
data["subcategory"] = cur_subcategory
- data["next_cat"] = categories[next_cat()]
- data["next_subcat"] = subs[next_subcat()]
data["display_craftable_only"] = display_craftable_only
data["display_compact"] = display_compact
var/list/surroundings = get_surroundings(user)
- var/list/can_craft = list()
- var/list/cant_craft = list()
+ var/list/craftability = list()
for(var/rec in GLOB.crafting_recipes)
var/datum/crafting_recipe/R = rec
+
if(!R.always_availible && !(R.type in user?.mind?.learned_recipes)) //User doesn't actually know how to make this.
continue
+
if((R.category != cur_category) || (R.subcategory != cur_subcategory))
continue
- if(check_contents(R, surroundings))
- can_craft += list(build_recipe_data(R))
+
+ craftability["[REF(R)]"] = check_contents(user, R, surroundings)
+
+ data["craftability"] = craftability
+ return data
+
+/datum/component/personal_crafting/ui_static_data(mob/user)
+ var/list/data = list()
+
+ var/list/crafting_recipes = list()
+ for(var/rec in GLOB.crafting_recipes)
+ var/datum/crafting_recipe/R = rec
+
+ if(R.name == "") //This is one of the invalid parents that sneaks in
+ continue
+
+ if(!R.always_availible && !(R.type in user?.mind?.learned_recipes)) //User doesn't actually know how to make this.
+ continue
+
+ if(isnull(crafting_recipes[R.category]))
+ crafting_recipes[R.category] = list()
+
+ if(R.subcategory == CAT_NONE)
+ crafting_recipes[R.category] += list(build_recipe_data(R))
else
- cant_craft += list(build_recipe_data(R))
- data["can_craft"] = can_craft
- data["cant_craft"] = cant_craft
+ if(isnull(crafting_recipes[R.category][R.subcategory]))
+ crafting_recipes[R.category][R.subcategory] = list()
+ crafting_recipes[R.category]["has_subcats"] = TRUE
+ crafting_recipes[R.category][R.subcategory] += list(build_recipe_data(R))
+
+ data["crafting_recipes"] = crafting_recipes
return data
-/datum/personal_crafting/ui_act(action, params)
+/datum/component/personal_crafting/ui_act(action, params)
if(..())
return
switch(action)
if("make")
- var/datum/crafting_recipe/TR = locate(params["recipe"])
+ var/datum/crafting_recipe/TR = locate(params["recipe"]) in GLOB.crafting_recipes
busy = TRUE
- ui_interact(usr) //explicit call to show the busy display
+ ui_interact(usr)
var/fail_msg = construct_item(usr, TR)
if(!fail_msg)
to_chat(usr, "[TR.name] constructed.")
else
to_chat(usr, "Construction failed[fail_msg]")
busy = FALSE
- ui_interact(usr)
- if("forwardCat") //Meow
- viewing_category = next_cat(FALSE)
- . = TRUE
- if("backwardCat")
- viewing_category = prev_cat(FALSE)
- . = TRUE
- if("forwardSubCat")
- viewing_subcategory = next_subcat()
- . = TRUE
- if("backwardSubCat")
- viewing_subcategory = prev_subcat()
- . = TRUE
if("toggle_recipes")
display_craftable_only = !display_craftable_only
. = TRUE
if("toggle_compact")
display_compact = !display_compact
. = TRUE
+ if("set_category")
+ if(!isnull(params["category"]))
+ cur_category = params["category"]
+ if(!isnull(params["subcategory"]))
+ if(params["subcategory"] == "0")
+ cur_subcategory = ""
+ else
+ cur_subcategory = params["subcategory"]
+ . = TRUE
-
-//Next works nicely with modular arithmetic
-/datum/personal_crafting/proc/next_cat(readonly = TRUE)
- if (!readonly)
- viewing_subcategory = 1
- . = viewing_category % categories.len + 1
-
-/datum/personal_crafting/proc/next_subcat()
- if(islist(subcategories[viewing_category]))
- var/list/subs = subcategories[viewing_category]
- . = viewing_subcategory % subs.len + 1
-
-
-//Previous can go fuck itself
-/datum/personal_crafting/proc/prev_cat(readonly = TRUE)
- if (!readonly)
- viewing_subcategory = 1
- if(viewing_category == categories.len)
- . = viewing_category-1
- else
- . = viewing_category % categories.len - 1
- if(. <= 0)
- . = categories.len
-
-/datum/personal_crafting/proc/prev_subcat()
- if(islist(subcategories[viewing_category]))
- var/list/subs = subcategories[viewing_category]
- if(viewing_subcategory == subs.len)
- . = viewing_subcategory-1
- else
- . = viewing_subcategory % subs.len - 1
- if(. <= 0)
- . = subs.len
- else
- . = null
-
-
-/datum/personal_crafting/proc/build_recipe_data(datum/crafting_recipe/R)
+/datum/component/personal_crafting/proc/build_recipe_data(datum/crafting_recipe/R)
var/list/data = list()
data["name"] = R.name
data["ref"] = "[REF(R)]"
diff --git a/code/modules/crafting/glassware.dm b/code/datums/components/crafting/glassware.dm
similarity index 100%
rename from code/modules/crafting/glassware.dm
rename to code/datums/components/crafting/glassware.dm
diff --git a/code/modules/crafting/guncrafting.dm b/code/datums/components/crafting/guncrafting.dm
similarity index 72%
rename from code/modules/crafting/guncrafting.dm
rename to code/datums/components/crafting/guncrafting.dm
index d96be9be10..d421a6e42a 100644
--- a/code/modules/crafting/guncrafting.dm
+++ b/code/datums/components/crafting/guncrafting.dm
@@ -13,3 +13,9 @@
desc = "A classic rifle stock that doubles as a grip, roughly carved out of wood."
icon = 'icons/obj/improvised.dmi'
icon_state = "riflestock"
+
+/obj/item/weaponcrafting/silkstring
+ name = "silkstring"
+ desc = "A long pice of silk looks like cable coil."
+ icon = 'icons/obj/improvised.dmi'
+ icon_state = "silkstring"
\ No newline at end of file
diff --git a/code/datums/components/crafting/recipes.dm b/code/datums/components/crafting/recipes.dm
new file mode 100644
index 0000000000..2b6f1a5b81
--- /dev/null
+++ b/code/datums/components/crafting/recipes.dm
@@ -0,0 +1,25 @@
+/datum/crafting_recipe
+ var/name = "" //in-game display name
+ var/list/reqs = list() //type paths of items consumed associated with how many are needed
+ var/list/blacklist = list() //type paths of items explicitly not allowed as an ingredient
+ var/result //type path of item resulting from this craft
+ var/list/tools = list() //type paths of items needed but not consumed
+ var/time = 30 //time in deciseconds
+ var/list/parts = list() //type paths of items that will be placed in the result
+ var/list/chem_catalysts = list() //like tools but for reagents
+ var/category = CAT_NONE //where it shows up in the crafting UI
+ var/subcategory = CAT_NONE
+ var/always_availible = TRUE //Set to FALSE if it needs to be learned first.
+
+/datum/crafting_recipe/New()
+ if(!(result in reqs))
+ blacklist += result
+
+/**
+ * Run custom pre-craft checks for this recipe
+ *
+ * user: The /mob that initiated the crafting
+ * collected_requirements: A list of lists of /obj/item instances that satisfy reqs. Top level list is keyed by requirement path.
+ */
+/datum/crafting_recipe/proc/check_requirements(mob/user, list/collected_requirements)
+ return TRUE
diff --git a/code/modules/crafting/recipes/recipes_clothing.dm b/code/datums/components/crafting/recipes/recipes_clothing.dm
similarity index 100%
rename from code/modules/crafting/recipes/recipes_clothing.dm
rename to code/datums/components/crafting/recipes/recipes_clothing.dm
diff --git a/code/modules/crafting/recipes/recipes_misc.dm b/code/datums/components/crafting/recipes/recipes_misc.dm
similarity index 100%
rename from code/modules/crafting/recipes/recipes_misc.dm
rename to code/datums/components/crafting/recipes/recipes_misc.dm
diff --git a/code/modules/crafting/recipes/recipes_primal.dm b/code/datums/components/crafting/recipes/recipes_primal.dm
similarity index 79%
rename from code/modules/crafting/recipes/recipes_primal.dm
rename to code/datums/components/crafting/recipes/recipes_primal.dm
index 1be479e4e1..ae611e5855 100644
--- a/code/modules/crafting/recipes/recipes_primal.dm
+++ b/code/datums/components/crafting/recipes/recipes_primal.dm
@@ -1,10 +1,3 @@
-/datum/crafting_recipe/bonearmor
- name = "Bone Armor"
- result = /obj/item/clothing/suit/armor/bone
- time = 30
- reqs = list(/obj/item/stack/sheet/bone = 6)
- category = CAT_PRIMAL
-
/datum/crafting_recipe/bonetalisman
name = "Bone Talisman"
result = /obj/item/clothing/accessory/talisman
@@ -29,13 +22,6 @@
/obj/item/stack/sheet/sinew = 1)
category = CAT_PRIMAL
-/datum/crafting_recipe/skullhelm
- name = "Skull Helmet"
- result = /obj/item/clothing/head/helmet/skull
- time = 30
- reqs = list(/obj/item/stack/sheet/bone = 4)
- category = CAT_PRIMAL
-
/datum/crafting_recipe/goliathcloak
name = "Goliath Cloak"
result = /obj/item/clothing/suit/hooded/cloak/goliath
@@ -62,13 +48,6 @@
/obj/item/stack/sheet/sinew = 2)
category = CAT_PRIMAL
-/datum/crafting_recipe/bonedagger
- name = "Bone Dagger"
- result = /obj/item/kitchen/knife/combat/bone
- time = 20
- reqs = list(/obj/item/stack/sheet/bone = 2)
- category = CAT_PRIMAL
-
/datum/crafting_recipe/bonespear
name = "Bone Spear"
result = /obj/item/twohanded/bonespear
@@ -110,4 +89,31 @@
parts = list(/obj/item/bodypart/head = 1,
/obj/item/twohanded/bonespear = 1)
result = /obj/structure/headpike/bone
+ category = CAT_PRIMAL
+
+/datum/crafting_recipe/quiver
+ name = "Quiver"
+ always_availible = FALSE
+ result = /obj/item/storage/belt/quiver
+ time = 80
+ reqs = list(/obj/item/stack/sheet/leather = 3,
+ /obj/item/stack/sheet/sinew = 4)
+ category = CAT_PRIMAL
+
+/datum/crafting_recipe/bone_bow
+ name = "Bone Bow"
+ result = /obj/item/gun/ballistic/bow/ashen
+ time = 200
+ always_availible = FALSE
+ reqs = list(/obj/item/stack/sheet/bone = 8,
+ /obj/item/stack/sheet/sinew = 4)
+ category = CAT_PRIMAL
+
+/datum/crafting_recipe/bow_tablet
+ name = "Sandstone Bow Making Manual"
+ result = /obj/item/book/granter/crafting_recipe/bone_bow
+ time = 600 //Scribing
+ always_availible = FALSE
+ reqs = list(/obj/item/stack/rods = 1,
+ /obj/item/stack/sheet/mineral/sandstone = 4)
category = CAT_PRIMAL
\ No newline at end of file
diff --git a/code/modules/crafting/recipes/recipes_robot.dm b/code/datums/components/crafting/recipes/recipes_robot.dm
similarity index 100%
rename from code/modules/crafting/recipes/recipes_robot.dm
rename to code/datums/components/crafting/recipes/recipes_robot.dm
diff --git a/code/modules/crafting/recipes/recipes_weapon_and_ammo.dm b/code/datums/components/crafting/recipes/recipes_weapon_and_ammo.dm
similarity index 84%
rename from code/modules/crafting/recipes/recipes_weapon_and_ammo.dm
rename to code/datums/components/crafting/recipes/recipes_weapon_and_ammo.dm
index bc3a3dcf05..76f8f119e0 100644
--- a/code/modules/crafting/recipes/recipes_weapon_and_ammo.dm
+++ b/code/datums/components/crafting/recipes/recipes_weapon_and_ammo.dm
@@ -8,6 +8,12 @@
category = CAT_WEAPONRY
subcategory = CAT_WEAPON
+/datum/crafting_recipe/pin_removal/check_requirements(mob/user, list/collected_requirements)
+ var/obj/item/gun/G = collected_requirements[/obj/item/gun][1]
+ if (G.no_pin_required || !G.pin)
+ return FALSE
+ return TRUE
+
/datum/crafting_recipe/strobeshield
name = "Strobe Shield"
result = /obj/item/assembly/flash/shield
@@ -18,6 +24,10 @@
category = CAT_WEAPONRY
subcategory = CAT_WEAPON
+/datum/crafting_recipe/strobeshield/New()
+ ..()
+ blacklist |= subtypesof(/obj/item/shield/riot/)
+
/datum/crafting_recipe/makeshiftshield
name = "Makeshift Metal Shield"
result = /obj/item/shield/makeshift
@@ -109,6 +119,18 @@
category = CAT_WEAPONRY
subcategory = CAT_WEAPON
+/datum/crafting_recipe/switchblade_ms
+ name = "Switchblade"
+ result = /obj/item/switchblade/crafted
+ reqs = list(/obj/item/weaponcrafting/stock = 1,
+ /obj/item/weaponcrafting/receiver = 1,
+ /obj/item/kitchen/knife = 1,
+ /obj/item/stack/cable_coil = 2)
+ tools = list(TOOL_WELDER)
+ time = 45
+ category = CAT_WEAPONRY
+ subcategory = CAT_WEAPON
+
//////////////////
///BOMB CRAFTING//
//////////////////
@@ -177,6 +199,16 @@
//////////////////
+/datum/crafting_recipe/pipebow
+ name = "Pipe Bow"
+ result = /obj/item/gun/ballistic/bow/pipe
+ reqs = list(/obj/item/pipe = 5,
+ /obj/item/stack/sheet/plastic = 15,
+ /obj/item/weaponcrafting/silkstring = 10)
+ time = 450
+ category = CAT_WEAPONRY
+ subcategory = CAT_WEAPON
+
/datum/crafting_recipe/smartdartgun
name = "Smart dartgun"
result = /obj/item/gun/syringe/dart
@@ -256,6 +288,37 @@
///AMMO CRAFTING//
//////////////////
+/datum/crafting_recipe/arrow
+ name = "Arrow"
+ result = /obj/item/ammo_casing/caseless/arrow
+ time = 40
+ reqs = list(/obj/item/stack/sheet/mineral/wood = 1,
+ /obj/item/weaponcrafting/silkstring = 1,
+ /obj/item/stack/rods = 3) // 1 metal sheet is worth 1.5 arrows
+ category = CAT_WEAPONRY
+ subcategory = CAT_AMMO
+
+/datum/crafting_recipe/bone_arrow
+ name = "Bone Arrow"
+ result = /obj/item/ammo_casing/caseless/arrow/bone
+ time = 40
+ always_availible = FALSE
+ reqs = list(/obj/item/stack/sheet/bone = 1,
+ /obj/item/stack/sheet/sinew = 1,
+ /obj/item/ammo_casing/caseless/arrow/ashen = 1)
+ category = CAT_WEAPONRY
+ subcategory = CAT_AMMO
+
+/datum/crafting_recipe/ashen_arrow
+ name = "Harden Arrow"
+ result = /obj/item/ammo_casing/caseless/arrow/ashen
+ tools = list(/obj/structure/bonfire)
+ time = 20
+ always_availible = FALSE
+ reqs = list(/obj/item/ammo_casing/caseless/arrow = 1)
+ category = CAT_WEAPONRY
+ subcategory = CAT_AMMO
+
/datum/crafting_recipe/smartdart
name = "Medical smartdart"
result = /obj/item/reagent_containers/syringe/dart
diff --git a/code/datums/components/mood.dm b/code/datums/components/mood.dm
index 2ecd77546d..5e9382f88e 100644
--- a/code/datums/components/mood.dm
+++ b/code/datums/components/mood.dm
@@ -21,6 +21,7 @@
RegisterSignal(parent, COMSIG_ADD_MOOD_EVENT, .proc/add_event)
RegisterSignal(parent, COMSIG_CLEAR_MOOD_EVENT, .proc/clear_event)
RegisterSignal(parent, COMSIG_MODIFY_SANITY, .proc/modify_sanity)
+ RegisterSignal(parent, COMSIG_LIVING_REVIVE, .proc/on_revive)
RegisterSignal(parent, COMSIG_MOB_HUD_CREATED, .proc/modify_hud)
var/mob/living/owner = parent
@@ -81,7 +82,8 @@
msg += "I don't have much of a reaction to anything right now.\n"
to_chat(user || parent, msg)
-/datum/component/mood/proc/update_mood() //Called whenever a mood event is added or removed
+///Called after moodevent/s have been added/removed.
+/datum/component/mood/proc/update_mood()
mood = 0
shown_mood = 0
for(var/i in mood_events)
@@ -234,6 +236,15 @@
qdel(event)
update_mood()
+/datum/component/mood/proc/remove_temp_moods() //Removes all temp moodsfor(var/i in mood_events)
+ for(var/i in mood_events)
+ var/datum/mood_event/moodlet = mood_events[i]
+ if(!moodlet || !moodlet.timeout)
+ continue
+ mood_events -= i
+ qdel(moodlet)
+ update_mood()
+
/datum/component/mood/proc/modify_hud(datum/source)
var/mob/living/owner = parent
var/datum/hud/hud = owner.hud_used
@@ -270,5 +281,12 @@
if(0 to NUTRITION_LEVEL_STARVING)
add_event(null, "nutrition", /datum/mood_event/starving)
+///Called when parent is ahealed.
+/datum/component/mood/proc/on_revive(datum/source, full_heal)
+ if(!full_heal)
+ return
+ remove_temp_moods()
+ setSanity(initial(sanity))
+
#undef MINOR_INSANITY_PEN
#undef MAJOR_INSANITY_PEN
diff --git a/code/datums/components/nanites.dm b/code/datums/components/nanites.dm
index e1c96121c7..49b2e437d4 100644
--- a/code/datums/components/nanites.dm
+++ b/code/datums/components/nanites.dm
@@ -7,13 +7,16 @@
var/regen_rate = 0.5 //nanites generated per second
var/safety_threshold = 25 //how low nanites will get before they stop processing/triggering
var/cloud_id = 0 //0 if not connected to the cloud, 1-100 to set a determined cloud backup to draw from
+ var/cloud_active = TRUE //if false, won't sync to the cloud
var/next_sync = 0
var/list/datum/nanite_program/programs = list()
var/max_programs = NANITE_PROGRAM_LIMIT
+ var/list/datum/nanite_program/protocol/protocols = list() ///Separate list of protocol programs, to avoid looping through the whole programs list when cheking for conflicts
+ var/start_time = 0 ///Timestamp to when the nanites were first inserted in the host
var/stealth = FALSE //if TRUE, does not appear on HUDs and health scans
var/diagnostics = TRUE //if TRUE, displays program list when scanned by nanite scanners
-
+
/datum/component/nanites/Initialize(amount = 100, cloud = 0)
if(!isliving(parent) && !istype(parent, /datum/nanite_cloud_backup))
return COMPONENT_INCOMPATIBLE
@@ -25,33 +28,36 @@
if(isliving(parent))
host_mob = parent
- if(!(MOB_ORGANIC in host_mob.mob_biotypes) && !(MOB_UNDEAD in host_mob.mob_biotypes)) //Shouldn't happen, but this avoids HUD runtimes in case a silicon gets them somehow.
+ if(!(host_mob.mob_biotypes & (MOB_ORGANIC|MOB_UNDEAD))) //Shouldn't happen, but this avoids HUD runtimes in case a silicon gets them somehow.
return COMPONENT_INCOMPATIBLE
+ start_time = world.time
+
host_mob.hud_set_nanite_indicator()
START_PROCESSING(SSnanites, src)
- if(cloud_id)
+ if(cloud_id && cloud_active)
cloud_sync()
/datum/component/nanites/RegisterWithParent()
- . = ..()
RegisterSignal(parent, COMSIG_HAS_NANITES, .proc/confirm_nanites)
RegisterSignal(parent, COMSIG_NANITE_IS_STEALTHY, .proc/check_stealth)
+ RegisterSignal(parent, COMSIG_NANITE_DELETE, .proc/delete_nanites)
RegisterSignal(parent, COMSIG_NANITE_UI_DATA, .proc/nanite_ui_data)
RegisterSignal(parent, COMSIG_NANITE_GET_PROGRAMS, .proc/get_programs)
RegisterSignal(parent, COMSIG_NANITE_SET_VOLUME, .proc/set_volume)
RegisterSignal(parent, COMSIG_NANITE_ADJUST_VOLUME, .proc/adjust_nanites)
RegisterSignal(parent, COMSIG_NANITE_SET_MAX_VOLUME, .proc/set_max_volume)
RegisterSignal(parent, COMSIG_NANITE_SET_CLOUD, .proc/set_cloud)
+ RegisterSignal(parent, COMSIG_NANITE_SET_CLOUD_SYNC, .proc/set_cloud_sync)
RegisterSignal(parent, COMSIG_NANITE_SET_SAFETY, .proc/set_safety)
RegisterSignal(parent, COMSIG_NANITE_SET_REGEN, .proc/set_regen)
RegisterSignal(parent, COMSIG_NANITE_ADD_PROGRAM, .proc/add_program)
RegisterSignal(parent, COMSIG_NANITE_SCAN, .proc/nanite_scan)
RegisterSignal(parent, COMSIG_NANITE_SYNC, .proc/sync)
- RegisterSignal(parent, COMSIG_ATOM_EMP_ACT, .proc/on_emp)
if(isliving(parent))
+ RegisterSignal(parent, COMSIG_ATOM_EMP_ACT, .proc/on_emp)
RegisterSignal(parent, COMSIG_MOB_DEATH, .proc/on_death)
RegisterSignal(parent, COMSIG_MOB_ALLOWED, .proc/check_access)
RegisterSignal(parent, COMSIG_LIVING_ELECTROCUTE_ACT, .proc/on_shock)
@@ -61,15 +67,16 @@
RegisterSignal(parent, COMSIG_NANITE_COMM_SIGNAL, .proc/receive_comm_signal)
/datum/component/nanites/UnregisterFromParent()
- . = ..()
UnregisterSignal(parent, list(COMSIG_HAS_NANITES,
COMSIG_NANITE_IS_STEALTHY,
+ COMSIG_NANITE_DELETE,
COMSIG_NANITE_UI_DATA,
COMSIG_NANITE_GET_PROGRAMS,
COMSIG_NANITE_SET_VOLUME,
COMSIG_NANITE_ADJUST_VOLUME,
COMSIG_NANITE_SET_MAX_VOLUME,
COMSIG_NANITE_SET_CLOUD,
+ COMSIG_NANITE_SET_CLOUD_SYNC,
COMSIG_NANITE_SET_SAFETY,
COMSIG_NANITE_SET_REGEN,
COMSIG_NANITE_ADD_PROGRAM,
@@ -87,9 +94,9 @@
/datum/component/nanites/Destroy()
STOP_PROCESSING(SSnanites, src)
- set_nanite_bar(TRUE)
QDEL_LIST(programs)
if(host_mob)
+ set_nanite_bar(TRUE)
host_mob.hud_set_nanite_indicator()
host_mob = null
return ..()
@@ -102,13 +109,18 @@
/datum/component/nanites/process()
adjust_nanites(null, regen_rate)
+ add_research()
for(var/X in programs)
var/datum/nanite_program/NP = X
NP.on_process()
- set_nanite_bar()
- if(cloud_id && world.time > next_sync)
+ if(cloud_id && cloud_active && world.time > next_sync)
cloud_sync()
next_sync = world.time + NANITE_SYNC_DELAY
+ set_nanite_bar()
+
+
+/datum/component/nanites/proc/delete_nanites()
+ qdel(src)
//Syncs the nanite component to another, making it so programs are the same with the same programming (except activation status)
/datum/component/nanites/proc/sync(datum/signal_source, datum/component/nanites/source, full_overwrite = TRUE, copy_activation = FALSE)
@@ -131,13 +143,17 @@
add_program(null, SNP.copy())
/datum/component/nanites/proc/cloud_sync()
- if(!cloud_id)
- return
- var/datum/nanite_cloud_backup/backup = SSnanites.get_cloud_backup(cloud_id)
- if(backup)
- var/datum/component/nanites/cloud_copy = backup.nanites
- if(cloud_copy)
- sync(null, cloud_copy)
+ if(cloud_id)
+ var/datum/nanite_cloud_backup/backup = SSnanites.get_cloud_backup(cloud_id)
+ if(backup)
+ var/datum/component/nanites/cloud_copy = backup.nanites
+ if(cloud_copy)
+ sync(null, cloud_copy)
+ return
+ //Without cloud syncing nanites can accumulate errors and/or defects
+ if(prob(8) && programs.len)
+ var/datum/nanite_program/NP = pick(programs)
+ NP.software_error()
/datum/component/nanites/proc/add_program(datum/source, datum/nanite_program/new_program, datum/nanite_program/source_program)
for(var/X in programs)
@@ -175,19 +191,28 @@
holder.icon_state = "nanites[nanite_percent]"
/datum/component/nanites/proc/on_emp(datum/source, severity)
- adjust_nanites(null, -(nanite_volume * 0.3 + 50)) //Lose 30% variable and 50 flat nanite volume.
+ nanite_volume *= (rand(60, 90) * 0.01) //Lose 10-40% of nanites
+ adjust_nanites(null, -(rand(5, 50))) //Lose 5-50 flat nanite volume
+ if(prob(40/severity))
+ cloud_id = 0
for(var/X in programs)
var/datum/nanite_program/NP = X
NP.on_emp(severity)
-/datum/component/nanites/proc/on_shock(datum/source, shock_damage)
- adjust_nanites(null, -(nanite_volume * (shock_damage * 0.005) + shock_damage)) //0.5% of shock damage (@ 50 damage it'd drain 25%) + shock damage flat volume
- for(var/X in programs)
- var/datum/nanite_program/NP = X
- NP.on_shock(shock_damage)
+
+/datum/component/nanites/proc/on_shock(datum/source, shock_damage, siemens_coeff = 1, flags = NONE)
+ if(shock_damage < 1)
+ return
+
+ if(!HAS_TRAIT_NOT_FROM(host_mob, TRAIT_SHOCKIMMUNE, "nanites"))//Another shock protection must protect nanites too, but nanites protect only host
+ nanite_volume *= (rand(45, 80) * 0.01) //Lose 20-55% of nanites
+ adjust_nanites(null, -(rand(5, 50))) //Lose 5-50 flat nanite volume
+ for(var/X in programs)
+ var/datum/nanite_program/NP = X
+ NP.on_shock(shock_damage)
/datum/component/nanites/proc/on_minor_shock(datum/source)
- adjust_nanites(null, -25)
+ adjust_nanites(null, -(rand(5, 15))) //Lose 5-15 flat nanite volume
for(var/X in programs)
var/datum/nanite_program/NP = X
NP.on_minor_shock()
@@ -207,16 +232,16 @@
/datum/component/nanites/proc/receive_comm_signal(datum/source, comm_code, comm_message, comm_source = "an unidentified source")
for(var/X in programs)
- if(istype(X, /datum/nanite_program/triggered/comm))
- var/datum/nanite_program/triggered/comm/NP = X
+ if(istype(X, /datum/nanite_program/comm))
+ var/datum/nanite_program/comm/NP = X
NP.receive_comm_signal(comm_code, comm_message, comm_source)
/datum/component/nanites/proc/check_viable_biotype()
- if(!(MOB_ORGANIC in host_mob.mob_biotypes) && !(MOB_UNDEAD in host_mob.mob_biotypes))
+ if(!(host_mob.mob_biotypes & (MOB_ORGANIC|MOB_UNDEAD)))
qdel(src) //bodytype no longer sustains nanites
/datum/component/nanites/proc/check_access(datum/source, obj/O)
- for(var/datum/nanite_program/triggered/access/access_program in programs)
+ for(var/datum/nanite_program/access/access_program in programs)
if(access_program.activated)
return O.check_access_list(access_program.access)
else
@@ -232,6 +257,15 @@
/datum/component/nanites/proc/set_cloud(datum/source, amount)
cloud_id = CLAMP(amount, 0, 100)
+/datum/component/nanites/proc/set_cloud_sync(datum/source, method)
+ switch(method)
+ if(NANITE_CLOUD_TOGGLE)
+ cloud_active = !cloud_active
+ if(NANITE_CLOUD_DISABLE)
+ cloud_active = FALSE
+ if(NANITE_CLOUD_ENABLE)
+ cloud_active = TRUE
+
/datum/component/nanites/proc/set_safety(datum/source, amount)
safety_threshold = CLAMP(amount, 0, max_nanites)
@@ -252,6 +286,19 @@
/datum/component/nanites/proc/get_programs(datum/source, list/nanite_programs)
nanite_programs |= programs
+/datum/component/nanites/proc/add_research()
+ var/research_value = NANITE_BASE_RESEARCH
+ if(!ishuman(host_mob))
+ if(!iscarbon(host_mob))
+ research_value *= 0.4
+ else
+ research_value *= 0.8
+ if(!host_mob.client)
+ research_value *= 0.5
+ if(host_mob.stat == DEAD)
+ research_value *= 0.75
+ SSresearch.science_tech.add_point_list(list(TECHWEB_POINT_TYPE_NANITES = research_value))
+
/datum/component/nanites/proc/nanite_scan(datum/source, mob/user, full_scan)
if(!full_scan)
if(!stealth)
@@ -263,7 +310,8 @@
to_chat(user, "================")
to_chat(user, "Saturation: [nanite_volume]/[max_nanites]")
to_chat(user, "Safety Threshold: [safety_threshold]")
- to_chat(user, "Cloud ID: [cloud_id ? cloud_id : "Disabled"]")
+ to_chat(user, "Cloud ID: [cloud_id ? cloud_id : "None"]")
+ to_chat(user, "Cloud Sync: [cloud_active ? "Active" : "Disabled"]")
to_chat(user, "================")
to_chat(user, "Program List:")
if(!diagnostics)
@@ -280,6 +328,7 @@
data["regen_rate"] = regen_rate
data["safety_threshold"] = safety_threshold
data["cloud_id"] = cloud_id
+ data["cloud_active"] = cloud_active
var/list/mob_programs = list()
var/id = 1
for(var/X in programs)
@@ -297,24 +346,35 @@
mob_program["trigger_cooldown"] = P.trigger_cooldown / 10
if(scan_level >= 3)
- mob_program["activation_delay"] = P.activation_delay
- mob_program["timer"] = P.timer
- mob_program["timer_type"] = P.get_timer_type_text()
- var/list/extra_settings = list()
- for(var/Y in P.extra_settings)
- var/list/setting = list()
- setting["name"] = Y
- setting["value"] = P.get_extra_setting(Y)
- extra_settings += list(setting)
+ mob_program["timer_restart"] = P.timer_restart / 10
+ mob_program["timer_shutdown"] = P.timer_shutdown / 10
+ mob_program["timer_trigger"] = P.timer_trigger / 10
+ mob_program["timer_trigger_delay"] = P.timer_trigger_delay / 10
+ var/list/extra_settings = P.get_extra_settings_frontend()
mob_program["extra_settings"] = extra_settings
if(LAZYLEN(extra_settings))
mob_program["has_extra_settings"] = TRUE
+ else
+ mob_program["has_extra_settings"] = FALSE
if(scan_level >= 4)
mob_program["activation_code"] = P.activation_code
mob_program["deactivation_code"] = P.deactivation_code
mob_program["kill_code"] = P.kill_code
mob_program["trigger_code"] = P.trigger_code
+ var/list/rules = list()
+ var/rule_id = 1
+ for(var/Z in P.rules)
+ var/datum/nanite_rule/nanite_rule = Z
+ var/list/rule = list()
+ rule["display"] = nanite_rule.display()
+ rule["program_id"] = id
+ rule["id"] = rule_id
+ rules += list(rule)
+ rule_id++
+ mob_program["rules"] = rules
+ if(LAZYLEN(rules))
+ mob_program["has_rules"] = TRUE
id++
mob_programs += list(mob_program)
data["mob_programs"] = mob_programs
diff --git a/code/datums/components/slippery.dm b/code/datums/components/slippery.dm
index 6682c3901d..bf53414537 100644
--- a/code/datums/components/slippery.dm
+++ b/code/datums/components/slippery.dm
@@ -11,5 +11,5 @@
/datum/component/slippery/proc/Slip(datum/source, atom/movable/AM)
var/mob/victim = AM
- if(istype(victim) && !victim.is_flying() && victim.slip(intensity, parent, lube_flags) && callback)
+ if(istype(victim) && victim.slip(intensity, parent, lube_flags) && callback)
callback.Invoke(victim)
diff --git a/code/datums/components/storage/concrete/pockets.dm b/code/datums/components/storage/concrete/pockets.dm
index 9c8622d16e..4d3ecc8d17 100644
--- a/code/datums/components/storage/concrete/pockets.dm
+++ b/code/datums/components/storage/concrete/pockets.dm
@@ -56,7 +56,7 @@
/obj/item/scalpel, /obj/item/reagent_containers/syringe, /obj/item/dnainjector,
/obj/item/reagent_containers/hypospray/medipen, /obj/item/reagent_containers/dropper,
/obj/item/implanter, /obj/item/screwdriver, /obj/item/weldingtool/mini,
- /obj/item/firing_pin, /obj/item/gun/ballistic/automatic/pistol
+ /obj/item/firing_pin, /obj/item/gun/ballistic/automatic/pistol, /obj/item/gun/ballistic/automatic/magrifle/pistol
))
/datum/component/storage/concrete/pockets/shoes/clown/Initialize()
diff --git a/code/datums/components/storage/storage.dm b/code/datums/components/storage/storage.dm
index 55e5c03c2b..61f97006dd 100644
--- a/code/datums/components/storage/storage.dm
+++ b/code/datums/components/storage/storage.dm
@@ -41,6 +41,7 @@
var/obj/screen/storage/boxes //storage display object
var/obj/screen/close/closer //close button object
+ var/current_maxscreensize
var/allow_big_nesting = FALSE //allow storage objects of the same or greater size.
@@ -362,11 +363,15 @@
break
closer.screen_loc = "[screen_start_x + cols]:[screen_pixel_x],[screen_start_y]:[screen_pixel_y]"
-/datum/component/storage/proc/show_to(mob/M)
+/datum/component/storage/proc/show_to(mob/M, set_screen_size = TRUE)
if(!M.client)
return FALSE
var/list/cview = getviewsize(M.client.view)
var/maxallowedscreensize = cview[1]-8
+ if(set_screen_size)
+ current_maxscreensize = maxallowedscreensize
+ else if(current_maxscreensize)
+ maxallowedscreensize = current_maxscreensize
if(M.active_storage != src && (M.stat == CONSCIOUS))
for(var/obj/item/I in accessible_items())
if(I.on_found(M))
@@ -547,14 +552,14 @@
return
A.add_fingerprint(M)
-/datum/component/storage/proc/user_show_to_mob(mob/M, force = FALSE)
+/datum/component/storage/proc/user_show_to_mob(mob/M, force = FALSE, ghost = FALSE)
var/atom/A = parent
if(!istype(M))
return FALSE
A.add_fingerprint(M)
if(!force && (check_locked(null, M) || !M.CanReach(parent, view_only = TRUE)))
return FALSE
- show_to(M)
+ show_to(M, !ghost)
/datum/component/storage/proc/mousedrop_receive(datum/source, atom/movable/O, mob/M)
if(isitem(O))
@@ -665,7 +670,7 @@
return can_be_inserted(I, silent, M)
/datum/component/storage/proc/show_to_ghost(datum/source, mob/dead/observer/M)
- return user_show_to_mob(M, TRUE)
+ return user_show_to_mob(M, TRUE, TRUE)
/datum/component/storage/proc/signal_show_attempt(datum/source, mob/showto, force = FALSE)
return user_show_to_mob(showto, force)
diff --git a/code/datums/components/uplink.dm b/code/datums/components/uplink.dm
index 2c3b7518ba..a8e03946b6 100644
--- a/code/datums/components/uplink.dm
+++ b/code/datums/components/uplink.dm
@@ -25,6 +25,8 @@ GLOBAL_LIST_EMPTY(uplinks)
var/unlock_code
var/failsafe_code
var/datum/ui_state/checkstate
+ var/compact_mode = FALSE
+ var/debug = FALSE
/datum/component/uplink/Initialize(_owner, _lockable = TRUE, _enabled = FALSE, datum/game_mode/_gamemode, starting_tc = 20, datum/ui_state/_checkstate)
if(!isitem(parent))
@@ -121,7 +123,7 @@ GLOBAL_LIST_EMPTY(uplinks)
active = TRUE
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "uplink", name, 450, 750, master_ui, state)
+ ui = new(user, src, ui_key, "uplink", name, 620, 580, master_ui, state)
ui.set_autoupdate(FALSE) // This UI is only ever opened by one person, and never is updated outside of user input.
ui.set_style("syndicate")
ui.open()
@@ -138,29 +140,35 @@ GLOBAL_LIST_EMPTY(uplinks)
var/list/data = list()
data["telecrystals"] = telecrystals
data["lockable"] = lockable
+ data["compact_mode"] = compact_mode
+ return data
+
+/datum/component/uplink/ui_static_data(mob/user)
+ var/list/data = list()
data["categories"] = list()
for(var/category in uplink_items)
var/list/cat = list(
"name" = category,
"items" = (category == selected_cat ? list() : null))
- if(category == selected_cat)
- for(var/item in uplink_items[category])
- var/datum/uplink_item/I = uplink_items[category][item]
- if(I.limited_stock == 0)
+ for(var/item in uplink_items[category])
+ var/datum/uplink_item/I = uplink_items[category][item]
+ if(I.limited_stock == 0)
+ continue
+ if(I.restricted_roles.len)
+ var/is_inaccessible = TRUE
+ for(var/R in I.restricted_roles)
+ if(R == user.mind.assigned_role || debug)
+ is_inaccessible = FALSE
+ if(is_inaccessible)
continue
- if(I.restricted_roles.len)
- var/is_inaccessible = 1
- for(var/R in I.restricted_roles)
- if(R == user.mind.assigned_role)
- is_inaccessible = 0
if(is_inaccessible)
continue
- cat["items"] += list(list(
- "name" = I.name,
- "cost" = I.cost,
- "desc" = I.desc,
- ))
+ cat["items"] += list(list(
+ "name" = I.name,
+ "cost" = I.cost,
+ "desc" = I.desc,
+ ))
data["categories"] += list(cat)
return data
@@ -188,6 +196,8 @@ GLOBAL_LIST_EMPTY(uplinks)
SStgui.close_uis(src)
if("select")
selected_cat = params["category"]
+ if("compact_toggle")
+ compact_mode = !compact_mode
return TRUE
/datum/component/uplink/proc/MakePurchase(mob/user, datum/uplink_item/U)
diff --git a/code/datums/components/virtual_reality.dm b/code/datums/components/virtual_reality.dm
index 2f0405af2e..63e4f4f092 100644
--- a/code/datums/components/virtual_reality.dm
+++ b/code/datums/components/virtual_reality.dm
@@ -229,7 +229,7 @@
UnregisterSignal(mastermind, COMSIG_PRE_MIND_TRANSFER)
mastermind = null
if(cleanup)
- var/obj/effect/vr_clean_master/cleanbot = locate() in get_area(M)
+ var/obj/effect/vr_clean_master/cleanbot = locate() in get_base_area(M)
if(cleanbot)
LAZYOR(cleanbot.corpse_party, M)
qdel(src)
diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm
index ebfba43852..1622cc9dbf 100644
--- a/code/datums/datacore.dm
+++ b/code/datums/datacore.dm
@@ -236,7 +236,12 @@
G.fields["fingerprint"] = md5(H.dna.uni_identity)
G.fields["p_stat"] = "Active"
G.fields["m_stat"] = "Stable"
- G.fields["sex"] = H.gender
+ if(H.gender == MALE)
+ G.fields["gender"] = "Male"
+ else if(H.gender == FEMALE)
+ G.fields["gender"] = "Female"
+ else
+ G.fields["gender"] = "Other"
G.fields["photo_front"] = photo_front
G.fields["photo_side"] = photo_side
general += G
@@ -274,7 +279,12 @@
L.fields["name"] = H.real_name
L.fields["rank"] = H.mind.assigned_role
L.fields["age"] = H.age
- L.fields["sex"] = H.gender
+ if(H.gender == MALE)
+ G.fields["gender"] = "Male"
+ else if(H.gender == FEMALE)
+ G.fields["gender"] = "Female"
+ else
+ G.fields["gender"] = "Other"
L.fields["blood_type"] = H.dna.blood_type
L.fields["b_dna"] = H.dna.unique_enzymes
L.fields["enzymes"] = H.dna.struc_enzymes
diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm
index e387acbd0b..b7ec8f38dc 100644
--- a/code/datums/datumvars.dm
+++ b/code/datums/datumvars.dm
@@ -945,26 +945,7 @@
A.create_reagents(amount)
if(A.reagents)
- var/chosen_id
- switch(alert(usr, "Choose a method.", "Add Reagents", "Search", "Choose from a list", "I'm feeling lucky"))
- if("Search")
- var/valid_id
- while(!valid_id)
- chosen_id = input(usr, "Enter the ID of the reagent you want to add.", "Search reagents") as null|text
- if(isnull(chosen_id)) //Get me out of here!
- break
- if(!ispath(text2path(chosen_id)))
- chosen_id = pick_closest_path(chosen_id, make_types_fancy(subtypesof(/datum/reagent)))
- if(ispath(chosen_id))
- valid_id = TRUE
- else
- valid_id = TRUE
- if(!valid_id)
- to_chat(usr, "A reagent with that ID doesn't exist!")
- if("Choose from a list")
- chosen_id = input(usr, "Choose a reagent to add.", "Choose a reagent.") as null|anything in subtypesof(/datum/reagent)
- if("I'm feeling lucky")
- chosen_id = pick(subtypesof(/datum/reagent))
+ var/chosen_id = choose_reagent_id(usr)
if(chosen_id)
var/amount = input(usr, "Choose the amount to add.", "Choose the amount.", A.reagents.maximum_volume) as num
if(amount)
diff --git a/code/datums/diseases/_MobProcs.dm b/code/datums/diseases/_MobProcs.dm
index c125a9b7c7..0b6d4e3404 100644
--- a/code/datums/diseases/_MobProcs.dm
+++ b/code/datums/diseases/_MobProcs.dm
@@ -17,14 +17,10 @@
if(HasDisease(D))
return FALSE
- var/can_infect = FALSE
- for(var/host_type in D.infectable_biotypes)
- if(host_type in mob_biotypes)
- can_infect = TRUE
- break
- if(!can_infect)
+ if(!(D.infectable_biotypes & mob_biotypes))
return FALSE
+
if(!(type in D.viable_mobtypes))
return FALSE
diff --git a/code/datums/diseases/_disease.dm b/code/datums/diseases/_disease.dm
index 056f99c7a4..940b61c9fe 100644
--- a/code/datums/diseases/_disease.dm
+++ b/code/datums/diseases/_disease.dm
@@ -30,7 +30,7 @@
var/list/required_organs = list()
var/needs_all_cures = TRUE
var/list/strain_data = list() //dna_spread special bullshit
- var/list/infectable_biotypes = list(MOB_ORGANIC) //if the disease can spread on organics, synthetics, or undead
+ var/infectable_biotypes = MOB_ORGANIC //if the disease can spread on organics, synthetics, or undead
var/process_dead = FALSE //if this ticks while the host is dead
var/copy_type = null //if this is null, copies will use the type of the instance being copied
diff --git a/code/datums/diseases/advance/advance.dm b/code/datums/diseases/advance/advance.dm
index 6b72f2fdfc..1c21fb9528 100644
--- a/code/datums/diseases/advance/advance.dm
+++ b/code/datums/diseases/advance/advance.dm
@@ -110,7 +110,7 @@
// Randomly pick a symptom to activate.
/datum/disease/advance/stage_act()
..()
- if(carrier)
+ if(carrier || QDELETED(src)) // Could be cured in parent call.
return
if(symptoms && symptoms.len)
diff --git a/code/datums/diseases/advance/symptoms/choking.dm b/code/datums/diseases/advance/symptoms/choking.dm
index 559d8f4754..c34ab566cf 100644
--- a/code/datums/diseases/advance/symptoms/choking.dm
+++ b/code/datums/diseases/advance/symptoms/choking.dm
@@ -22,14 +22,16 @@ Bonus
stealth = -3
resistance = -2
stage_speed = -2
- transmittable = -4
+ transmittable = -2
level = 3
severity = 3
base_message_chance = 15
symptom_delay_min = 10
symptom_delay_max = 30
- threshold_desc = "Stage Speed 8: Causes choking more frequently. \
- Stealth 4: The symptom remains hidden until active."
+ threshold_desc = list(
+ "Stage Speed 8" = "Causes choking more frequently.",
+ "Stealth 4" = "The symptom remains hidden until active."
+ )
/datum/symptom/choking/Start(datum/disease/advance/A)
if(!..())
@@ -41,7 +43,7 @@ Bonus
suppress_warning = TRUE
/datum/symptom/choking/Activate(datum/disease/advance/A)
- if(!..())
+ if(!..() || HAS_TRAIT(A.affected_mob,TRAIT_NOBREATH))
return
var/mob/living/M = A.affected_mob
switch(A.stage)
@@ -99,8 +101,10 @@ Bonus
symptom_delay_min = 14
symptom_delay_max = 30
var/paralysis = FALSE
- threshold_desc = "Stage Speed 8: Additionally synthesizes pancuronium and sodium thiopental inside the host. \
- Transmission 8: Doubles the damage caused by the symptom."
+ threshold_desc = list(
+ "Stage Speed 8" = "Additionally synthesizes pancuronium and sodium thiopental inside the host.",
+ "Transmission 8" = "Doubles the damage caused by the symptom."
+ )
/datum/symptom/asphyxiation/Start(datum/disease/advance/A)
@@ -112,7 +116,7 @@ Bonus
power = 2
/datum/symptom/asphyxiation/Activate(datum/disease/advance/A)
- if(!..())
+ if(!..() || HAS_TRAIT(A.affected_mob,TRAIT_NOBREATH))
return
var/mob/living/M = A.affected_mob
switch(A.stage)
diff --git a/code/datums/diseases/advance/symptoms/confusion.dm b/code/datums/diseases/advance/symptoms/confusion.dm
index eb6c5342f9..b8a1064c34 100644
--- a/code/datums/diseases/advance/symptoms/confusion.dm
+++ b/code/datums/diseases/advance/symptoms/confusion.dm
@@ -29,9 +29,11 @@ Bonus
symptom_delay_min = 10
symptom_delay_max = 30
var/brain_damage = FALSE
- threshold_desc = "Resistance 6: Causes brain damage over time. \
- Transmission 6: Increases confusion duration. \
- Stealth 4: The symptom remains hidden until active."
+ threshold_desc = list(
+ "Resistance 6" = "Causes brain damage over time.",
+ "Transmission 6" = "Increases confusion duration and strength.",
+ "Stealth 4" = "The symptom remains hidden until active.",
+ )
/datum/symptom/confusion/Start(datum/disease/advance/A)
if(!..())
diff --git a/code/datums/diseases/advance/symptoms/cough.dm b/code/datums/diseases/advance/symptoms/cough.dm
index b1767d7cb5..cf15ec407a 100644
--- a/code/datums/diseases/advance/symptoms/cough.dm
+++ b/code/datums/diseases/advance/symptoms/cough.dm
@@ -29,12 +29,13 @@ BONUS
symptom_delay_min = 2
symptom_delay_max = 15
var/infective = FALSE
- threshold_desc = "Resistance 3: Host will drop small items when coughing. \
- Resistance 10: Occasionally causes coughing fits that stun the host. \
- Stage Speed 6: Increases cough frequency. \
- If Airborne: Coughing will infect bystanders. \
- Stealth 4: The symptom remains hidden until active."
-
+ threshold_desc = list(
+ "Resistance 11" = "The host will drop small items when coughing.",
+ "Resistance 15" = "Occasionally causes coughing fits that stun the host. The extra coughs do not spread the virus.",
+ "Stage Speed 6" = "Increases cough frequency.",
+ "Transmission 7" = "Coughing will now infect bystanders up to 2 tiles away.",
+ "Stealth 4" = "The symptom remains hidden until active.",
+ )
/datum/symptom/cough/Start(datum/disease/advance/A)
if(!..())
return
@@ -50,7 +51,7 @@ BONUS
symptom_delay_max = 10
/datum/symptom/cough/Activate(datum/disease/advance/A)
- if(!..())
+ if(!..() || HAS_TRAIT(A.affected_mob,TRAIT_NOBREATH))
return
var/mob/living/M = A.affected_mob
switch(A.stage)
diff --git a/code/datums/diseases/advance/symptoms/deafness.dm b/code/datums/diseases/advance/symptoms/deafness.dm
index e0336506e3..b6dd185b36 100644
--- a/code/datums/diseases/advance/symptoms/deafness.dm
+++ b/code/datums/diseases/advance/symptoms/deafness.dm
@@ -28,8 +28,10 @@ Bonus
base_message_chance = 100
symptom_delay_min = 25
symptom_delay_max = 80
- threshold_desc = "Resistance 9: Causes permanent deafness, instead of intermittent. \
- Stealth 4: The symptom remains hidden until active."
+ threshold_desc = list(
+ "Resistance 9" = "Causes permanent deafness, instead of intermittent.",
+ "Stealth 4" = "The symptom remains hidden until active.",
+ )
/datum/symptom/deafness/Start(datum/disease/advance/A)
if(!..())
diff --git a/code/datums/diseases/advance/symptoms/disfiguration.dm b/code/datums/diseases/advance/symptoms/disfiguration.dm
new file mode 100644
index 0000000000..4a4b704dc0
--- /dev/null
+++ b/code/datums/diseases/advance/symptoms/disfiguration.dm
@@ -0,0 +1,50 @@
+/*
+//////////////////////////////////////
+Disfiguration
+
+ Hidden.
+ No change to resistance.
+ Increases stage speed.
+ Slightly increases transmittability.
+ Critical Level.
+
+BONUS
+ Adds disfiguration trait making the mob appear as "Unknown" to others.
+
+//////////////////////////////////////
+*/
+
+/datum/symptom/disfiguration
+
+ name = "Disfiguration"
+ desc = "The virus liquefies facial muscles, disfiguring the host."
+ stealth = 2
+ resistance = 0
+ stage_speed = 3
+ transmittable = 1
+ level = 5
+ severity = 1
+ symptom_delay_min = 25
+ symptom_delay_max = 75
+
+/datum/symptom/disfiguration/Activate(datum/disease/advance/A)
+ . = ..()
+ if(!.)
+ return
+ var/mob/living/M = A.affected_mob
+ if (HAS_TRAIT(M, TRAIT_DISFIGURED))
+ return
+ switch(A.stage)
+ if(5)
+ ADD_TRAIT(M, TRAIT_DISFIGURED, DISEASE_TRAIT)
+ M.visible_message("[M]'s face appears to cave in!", "You feel your face crumple and cave in!")
+ else
+ M.visible_message("[M]'s face begins to contort...", "Your face feels wet and malleable...")
+
+
+/datum/symptom/disfiguration/End(datum/disease/advance/A)
+ . = ..()
+ if(!.)
+ return
+ if(A.affected_mob)
+ REMOVE_TRAIT(A.affected_mob, TRAIT_DISFIGURED, DISEASE_TRAIT)
\ No newline at end of file
diff --git a/code/datums/diseases/advance/symptoms/dizzy.dm b/code/datums/diseases/advance/symptoms/dizzy.dm
index b4b06be5ac..be444e3916 100644
--- a/code/datums/diseases/advance/symptoms/dizzy.dm
+++ b/code/datums/diseases/advance/symptoms/dizzy.dm
@@ -27,8 +27,10 @@ Bonus
base_message_chance = 50
symptom_delay_min = 15
symptom_delay_max = 40
- threshold_desc = "Transmission 6: Also causes druggy vision. \
- Stealth 4: The symptom remains hidden until active."
+ threshold_desc = list(
+ "Transmission 6" = "Also causes druggy vision.",
+ "Stealth 4" = "The symptom remains hidden until active.",
+ )
/datum/symptom/dizzy/Start(datum/disease/advance/A)
if(!..())
diff --git a/code/datums/diseases/advance/symptoms/fever.dm b/code/datums/diseases/advance/symptoms/fever.dm
index a178cba196..0348e39818 100644
--- a/code/datums/diseases/advance/symptoms/fever.dm
+++ b/code/datums/diseases/advance/symptoms/fever.dm
@@ -28,9 +28,10 @@ Bonus
symptom_delay_min = 10
symptom_delay_max = 30
var/unsafe = FALSE //over the heat threshold
- threshold_desc = "Resistance 5: Increases fever intensity, fever can overheat and harm the host. \
- Resistance 10: Further increases fever intensity."
-
+ threshold_desc = list(
+ "Resistance 5" = "Increases fever intensity, fever can overheat and harm the host.",
+ "Resistance 10" = "Further increases fever intensity.",
+ )
/datum/symptom/fever/Start(datum/disease/advance/A)
if(!..())
return
diff --git a/code/datums/diseases/advance/symptoms/fire.dm b/code/datums/diseases/advance/symptoms/fire.dm
index 6429db9439..ea1897b67d 100644
--- a/code/datums/diseases/advance/symptoms/fire.dm
+++ b/code/datums/diseases/advance/symptoms/fire.dm
@@ -19,9 +19,9 @@ Bonus
name = "Spontaneous Combustion"
desc = "The virus turns fat into an extremely flammable compound, and raises the body's temperature, making the host burst into flames spontaneously."
- stealth = 1
+ stealth = -1
resistance = -4
- stage_speed = -4
+ stage_speed = -3
transmittable = -4
level = 6
severity = 5
@@ -29,11 +29,12 @@ Bonus
symptom_delay_min = 20
symptom_delay_max = 75
var/infective = FALSE
- threshold_desc = "Stage Speed 4: Increases the intensity of the flames. \
- Stage Speed 8: Further increases flame intensity. \
- Transmission 8: Host will spread the virus through skin flakes when bursting into flame. \
- Stealth 4: The symptom remains hidden until active."
-
+ threshold_desc = list(
+ "Stage Speed 4" = "Increases the intensity of the flames.",
+ "Stage Speed 8" = "Further increases flame intensity.",
+ "Transmission 8" = "Host will spread the virus through skin flakes when bursting into flame.",
+ "Stealth 4" = "The symptom remains hidden until active.",
+ )
/datum/symptom/fire/Start(datum/disease/advance/A)
if(!..())
return
diff --git a/code/datums/diseases/advance/symptoms/flesh_eating.dm b/code/datums/diseases/advance/symptoms/flesh_eating.dm
index 48668afc90..774d97202e 100644
--- a/code/datums/diseases/advance/symptoms/flesh_eating.dm
+++ b/code/datums/diseases/advance/symptoms/flesh_eating.dm
@@ -22,7 +22,7 @@ Bonus
stealth = -3
resistance = -4
stage_speed = 0
- transmittable = -4
+ transmittable = -3
level = 6
severity = 5
base_message_chance = 50
@@ -30,8 +30,11 @@ Bonus
symptom_delay_max = 60
var/bleed = FALSE
var/pain = FALSE
- threshold_desc = "Resistance 7: Host will bleed profusely during necrosis. \
- Transmission 8: Causes extreme pain to the host, weakening it."
+ threshold_desc = list(
+ "Resistance 9" = "Doubles the intensity of the immolation effect, but reduces the frequency of all of this symptom's effects.",
+ "Stage Speed 8" = "Increases explosion radius and explosion damage to the host when the host is wet.",
+ "Transmission 8" = "Additionally synthesizes chlorine trifluoride and napalm inside the host. More chemicals are synthesized if the resistance 9 threshold has been met."
+ )
/datum/symptom/flesh_eating/Start(datum/disease/advance/A)
if(!..())
@@ -96,8 +99,11 @@ Bonus
symptom_delay_max = 6
var/chems = FALSE
var/zombie = FALSE
- threshold_desc = "Stage Speed 7: Synthesizes Heparin and Lipolicide inside the host, causing increased bleeding and hunger. \
- Stealth 5: The symptom remains hidden until active."
+ threshold_desc = list(
+ "Stage Speed 7" = "Synthesizes Heparin and Lipolicide inside the host, causing increased bleeding and hunger.",
+ "Stealth 5" = "The symptom remains hidden until active.",
+ )
+
/datum/symptom/flesh_death/Start(datum/disease/advance/A)
if(!..())
diff --git a/code/datums/diseases/advance/symptoms/genetics.dm b/code/datums/diseases/advance/symptoms/genetics.dm
index 7f21b03dff..f6780df3fa 100644
--- a/code/datums/diseases/advance/symptoms/genetics.dm
+++ b/code/datums/diseases/advance/symptoms/genetics.dm
@@ -30,9 +30,12 @@ Bonus
symptom_delay_min = 60
symptom_delay_max = 120
var/no_reset = FALSE
- threshold_desc = "Resistance 8: Causes two harmful mutations at once. \
- Stage Speed 10: Increases mutation frequency. \
- Stealth 5: The mutations persist even if the virus is cured."
+ threshold_desc = list(
+ "Resistance 8" = "The negative and mildly negative mutations caused by the virus are mutadone-proof (but will still be undone when the virus is cured if the resistance 14 threshold is not met).",
+ "Resistance 14" = "The host's genetic alterations are not undone when the virus is cured.",
+ "Stage Speed 10" = "The virus activates dormant mutations at a much faster rate.",
+ "Stealth 5" = "Only activates negative mutations in hosts."
+ )
/datum/symptom/genetic_mutation/Activate(datum/disease/advance/A)
if(!..())
diff --git a/code/datums/diseases/advance/symptoms/hallucigen.dm b/code/datums/diseases/advance/symptoms/hallucigen.dm
index 873d960524..4778e7d863 100644
--- a/code/datums/diseases/advance/symptoms/hallucigen.dm
+++ b/code/datums/diseases/advance/symptoms/hallucigen.dm
@@ -18,7 +18,7 @@ Bonus
/datum/symptom/hallucigen
name = "Hallucigen"
desc = "The virus stimulates the brain, causing occasional hallucinations."
- stealth = -2
+ stealth = -1
resistance = -3
stage_speed = -3
transmittable = -1
@@ -28,8 +28,10 @@ Bonus
symptom_delay_min = 25
symptom_delay_max = 90
var/fake_healthy = FALSE
- threshold_desc = "Stage Speed 7: Increases the amount of hallucinations. \
- Stealth 4: The virus mimics positive symptoms.."
+ threshold_desc = list(
+ "Stage Speed 7" = "Increases the amount of hallucinations.",
+ "Stealth 4" = "The virus mimics positive symptoms.",
+ )
/datum/symptom/hallucigen/Start(datum/disease/advance/A)
if(!..())
diff --git a/code/datums/diseases/advance/symptoms/headache.dm b/code/datums/diseases/advance/symptoms/headache.dm
index 72b03000ed..944333d9cf 100644
--- a/code/datums/diseases/advance/symptoms/headache.dm
+++ b/code/datums/diseases/advance/symptoms/headache.dm
@@ -29,9 +29,11 @@ BONUS
base_message_chance = 100
symptom_delay_min = 15
symptom_delay_max = 30
- threshold_desc = "Stage Speed 6: Headaches will cause severe pain, that weakens the host. \
- Stage Speed 9: Headaches become less frequent but far more intense, preventing any action from the host. \
- Stealth 4: Reduces headache frequency until later stages."
+ threshold_desc = list(
+ "Stage Speed 6" = "Headaches will cause severe pain, that weakens the host.",
+ "Stage Speed 9" = "Headaches become less frequent but far more intense, preventing any action from the host.",
+ "Stealth 4" = "Reduces headache frequency until later stages.",
+ )
/datum/symptom/headache/Start(datum/disease/advance/A)
if(!..())
diff --git a/code/datums/diseases/advance/symptoms/heal.dm b/code/datums/diseases/advance/symptoms/heal.dm
index c1b3526f41..c7b9758bcb 100644
--- a/code/datums/diseases/advance/symptoms/heal.dm
+++ b/code/datums/diseases/advance/symptoms/heal.dm
@@ -10,9 +10,10 @@
symptom_delay_min = 1
symptom_delay_max = 1
var/passive_message = "" //random message to infected but not actively healing people
- threshold_desc = "Stage Speed 6: Doubles healing speed. \
- Stealth 4: Healing will no longer be visible to onlookers."
-
+ threshold_desc = list(
+ "Stage Speed 6" = "Doubles healing speed.",
+ "Stealth 4" = "Healing will no longer be visible to onlookers.",
+ )
/datum/symptom/heal/Start(datum/disease/advance/A)
if(!..())
return
@@ -54,8 +55,10 @@
level = 6
passive_message = "You miss the feeling of starlight on your skin."
var/nearspace_penalty = 0.3
- threshold_desc = "Stage Speed 6: Increases healing speed. \
- Transmission 6: Removes penalty for only being close to space."
+ threshold_desc = list(
+ "Stage Speed 6" = "Increases healing speed.",
+ "Transmission 6" = "Removes penalty for only being close to space.",
+ )
/datum/symptom/heal/starlight/Start(datum/disease/advance/A)
if(!..())
@@ -105,8 +108,10 @@
level = 7
var/food_conversion = FALSE
desc = "The virus rapidly breaks down any foreign chemicals in the bloodstream."
- threshold_desc = "Resistance 7: Increases chem removal speed. \
- Stage Speed 6: Consumed chemicals nourish the host."
+ threshold_desc = list(
+ "Resistance 7" = "Increases chem removal speed.",
+ "Stage Speed 6" = "Consumed chemicals nourish the host.",
+ )
/datum/symptom/heal/chem/Start(datum/disease/advance/A)
if(!..())
@@ -139,9 +144,10 @@
var/reduced_hunger = FALSE
desc = "The virus causes the host's metabolism to accelerate rapidly, making them process chemicals twice as fast,\
but also causing increased hunger."
- threshold_desc = "Stealth 3: Reduces hunger rate. \
- Stage Speed 10: Chemical metabolization is tripled instead of doubled."
-
+ threshold_desc = list(
+ "Stealth 3" = "Reduces hunger rate.",
+ "Stage Speed 10" = "Chemical metabolization is tripled instead of doubled.",
+ )
/datum/symptom/heal/metabolism/Start(datum/disease/advance/A)
if(!..())
return
@@ -172,8 +178,9 @@
transmittable = -1
level = 6
passive_message = "You feel tingling on your skin as light passes over it."
- threshold_desc = "Stage Speed 8: Doubles healing speed."
-
+ threshold_desc = list(
+ "Stage Speed 8" = "Doubles healing speed.",
+ )
/datum/symptom/heal/darkness/Start(datum/disease/advance/A)
if(!..())
return
@@ -222,9 +229,11 @@
var/deathgasp = FALSE
var/stabilize = FALSE
var/active_coma = FALSE //to prevent multiple coma procs
- threshold_desc = "Stealth 2: Host appears to die when falling into a coma. \
- Resistance 4: The virus also stabilizes the host while they are in critical condition. \
- Stage Speed 7: Increases healing speed."
+ threshold_desc = list(
+ "Stealth 2" = "Host appears to die when falling into a coma.",
+ "Resistance 4" = "The virus also stabilizes the host while they are in critical condition.",
+ "Stage Speed 7" = "Increases healing speed.",
+ )
/datum/symptom/heal/coma/Start(datum/disease/advance/A)
if(!..())
@@ -313,8 +322,10 @@
level = 6
passive_message = "Your skin feels oddly dry..."
var/absorption_coeff = 1
- threshold_desc = "Resistance 5: Water is consumed at a much slower rate. \
- Stage Speed 7: Increases healing speed."
+ threshold_desc = list(
+ "Resistance 5" = "Water is consumed at a much slower rate.",
+ "Stage Speed 7" = "Increases healing speed.",
+ )
/datum/symptom/heal/water/Start(datum/disease/advance/A)
if(!..())
@@ -369,9 +380,10 @@
level = 8
passive_message = "You feel an odd attraction to plasma."
var/temp_rate = 1
- threshold_desc = "Transmission 6: Increases temperature adjustment rate and heals toxin lovers. \
- Stage Speed 7: Increases healing speed."
-
+ threshold_desc = list(
+ "Transmission 6" = "Additionally increases temperature adjustment rate and heals those who love toxins",
+ "Resistance 7" = "Increases healing speed.",
+ )
/datum/symptom/heal/plasma/Start(datum/disease/advance/A)
if(!..())
return
@@ -436,9 +448,11 @@
symptom_delay_max = 1
passive_message = "Your skin glows faintly for a moment."
var/cellular_damage = FALSE
- threshold_desc = "Transmission 6: Additionally heals cellular damage and toxin lovers. \
- Resistance 7: Increases healing speed."
-
+ threshold_desc = list(
+ "Transmission 6" = "Additionally heals cellular damage and toxin lovers.",
+ "Resistance 7" = "Increases healing speed.",
+ )
+
/datum/symptom/heal/radiation/Start(datum/disease/advance/A)
if(!..())
return
diff --git a/code/datums/diseases/advance/symptoms/itching.dm b/code/datums/diseases/advance/symptoms/itching.dm
index b0812e0235..c0c312cbc2 100644
--- a/code/datums/diseases/advance/symptoms/itching.dm
+++ b/code/datums/diseases/advance/symptoms/itching.dm
@@ -29,8 +29,10 @@ BONUS
symptom_delay_min = 5
symptom_delay_max = 25
var/scratch = FALSE
- threshold_desc = "Transmission 6: Increases frequency of itching. \
- Stage Speed 7: The host will scrath itself when itching, causing superficial damage."
+ threshold_desc = list(
+ "Transmission 6" = "Increases frequency of itching.",
+ "Stage Speed 7" = "The host will scrath itself when itching, causing superficial damage.",
+ )
/datum/symptom/itching/Start(datum/disease/advance/A)
if(!..())
diff --git a/code/datums/diseases/advance/symptoms/nanites.dm b/code/datums/diseases/advance/symptoms/nanites.dm
index b18e089a41..9598b3fd33 100644
--- a/code/datums/diseases/advance/symptoms/nanites.dm
+++ b/code/datums/diseases/advance/symptoms/nanites.dm
@@ -10,8 +10,10 @@
symptom_delay_min = 1
symptom_delay_max = 1
var/reverse_boost = FALSE
- threshold_desc = "Transmission 5: Increases the virus' growth rate while nanites are present. \
- Stage Speed 7: Increases the replication boost."
+ threshold_desc = list(
+ "Transmission 5" = "Increases the virus' growth rate while nanites are present.",
+ "Stage Speed 7" = "Increases the replication boost."
+ )
/datum/symptom/nano_boost/Start(datum/disease/advance/A)
if(!..())
@@ -42,8 +44,10 @@
symptom_delay_min = 1
symptom_delay_max = 1
var/reverse_boost = FALSE
- threshold_desc = "Stage Speed 5: Increases the virus' growth rate while nanites are present. \
- Resistance 7: Severely increases the rate at which the nanites are destroyed."
+ threshold_desc = list(
+ "Stage Speed 5" = "Increases the virus' growth rate while nanites are present.",
+ "Resistance 7" = "Severely increases the rate at which the nanites are destroyed."
+ )
/datum/symptom/nano_destroy/Start(datum/disease/advance/A)
if(!..())
diff --git a/code/datums/diseases/advance/symptoms/narcolepsy.dm b/code/datums/diseases/advance/symptoms/narcolepsy.dm
index 24ba024aa6..3f9405328a 100644
--- a/code/datums/diseases/advance/symptoms/narcolepsy.dm
+++ b/code/datums/diseases/advance/symptoms/narcolepsy.dm
@@ -18,7 +18,7 @@ Bonus
stealth = -1
resistance = -2
stage_speed = -3
- transmittable = -4
+ transmittable = 0
level = 6
symptom_delay_min = 15
symptom_delay_max = 80
@@ -26,8 +26,10 @@ Bonus
var/sleep_level = 0
var/sleepy_ticks = 0
var/stamina = FALSE
- threshold_desc = "Transmission 7: Also relaxes the muscles, weakening and slowing the host. \
- Resistance 10: Causes narcolepsy more often, increasing the chance of the host falling asleep."
+ threshold_desc = list(
+ "Transmission 4" = "Causes the host to periodically emit a yawn that spreads the virus in a manner similar to that of a sneeze.",
+ "Stage Speed 10" = "Causes narcolepsy more often, increasing the chance of the host falling asleep.",
+ )
/datum/symptom/narcolepsy/Start(datum/disease/advance/A)
if(!..())
diff --git a/code/datums/diseases/advance/symptoms/oxygen.dm b/code/datums/diseases/advance/symptoms/oxygen.dm
index e66bdf5ee0..3821c0585e 100644
--- a/code/datums/diseases/advance/symptoms/oxygen.dm
+++ b/code/datums/diseases/advance/symptoms/oxygen.dm
@@ -28,7 +28,9 @@ Bonus
symptom_delay_min = 1
symptom_delay_max = 1
var/regenerate_blood = FALSE
- threshold_desc = "Resistance 8:Additionally regenerates lost blood. "
+ threshold_desc = list(
+ "Resistance 8" = "Additionally regenerates lost blood."
+ )
/datum/symptom/oxygen/Start(datum/disease/advance/A)
if(!..())
diff --git a/code/datums/diseases/advance/symptoms/sensory.dm b/code/datums/diseases/advance/symptoms/sensory.dm
index 1f8cc8d7af..bbacdd885d 100644
--- a/code/datums/diseases/advance/symptoms/sensory.dm
+++ b/code/datums/diseases/advance/symptoms/sensory.dm
@@ -11,9 +11,11 @@
var/purge_alcohol = FALSE
var/trauma_heal_mild = FALSE
var/trauma_heal_severe = FALSE
- threshold_desc = "Resistance 6: Heals minor brain traumas. \
- Resistance 9: Heals severe brain traumas. \
- Transmission 8: Purges alcohol in the bloodstream."
+ threshold_desc = list(
+ "Resistance 6" = "Heals minor brain traumas.",
+ "Resistance 9" = "Heals severe brain traumas.",
+ "Transmission 8" = "Purges alcohol in the bloodstream.",
+ )
/datum/symptom/mind_restoration/Start(datum/disease/advance/A)
if(!..())
diff --git a/code/datums/diseases/advance/symptoms/shivering.dm b/code/datums/diseases/advance/symptoms/shivering.dm
index 741e2a1e16..da14ed87cf 100644
--- a/code/datums/diseases/advance/symptoms/shivering.dm
+++ b/code/datums/diseases/advance/symptoms/shivering.dm
@@ -20,15 +20,17 @@ Bonus
desc = "The virus inhibits the body's thermoregulation, cooling the body down."
stealth = 0
resistance = 2
- stage_speed = 2
+ stage_speed = 3
transmittable = 2
level = 2
severity = 2
symptom_delay_min = 10
symptom_delay_max = 30
var/unsafe = FALSE //over the cold threshold
- threshold_desc = "Stage Speed 5: Increases cooling speed; the host can fall below safe temperature levels. \
- Stage Speed 10: Further increases cooling speed."
+ threshold_desc = list(
+ "Stage Speed 5" = "Increases cooling speed,; the host can fall below safe temperature levels.",
+ "Stage Speed 10" = "Further increases cooling speed."
+ )
/datum/symptom/fever/Start(datum/disease/advance/A)
if(!..())
diff --git a/code/datums/diseases/advance/symptoms/skin.dm b/code/datums/diseases/advance/symptoms/skin.dm
index 300eecc80e..e35fe741fd 100644
--- a/code/datums/diseases/advance/symptoms/skin.dm
+++ b/code/datums/diseases/advance/symptoms/skin.dm
@@ -1,68 +1,22 @@
/*
//////////////////////////////////////
-Vitiligo
+Polyvitiligo
- Hidden.
- No change to resistance.
- Increases stage speed.
- Slightly increases transmittability.
- Critical Level.
-
-BONUS
- Makes the mob lose skin pigmentation.
-
-//////////////////////////////////////
-*/
-
-/datum/symptom/vitiligo
-
- name = "Vitiligo"
- desc = "The virus destroys skin pigment cells, causing rapid loss of pigmentation in the host."
- stealth = 2
- resistance = 0
- stage_speed = 3
- transmittable = 1
- level = 5
- severity = 1
- symptom_delay_min = 25
- symptom_delay_max = 75
-
-/datum/symptom/vitiligo/Activate(datum/disease/advance/A)
- if(!..())
- return
- var/mob/living/M = A.affected_mob
- if(ishuman(M))
- var/mob/living/carbon/human/H = M
- if(H.skin_tone == "albino")
- return
- switch(A.stage)
- if(5)
- H.skin_tone = "albino"
- H.update_body(0)
- else
- H.visible_message("[H] looks a bit pale...", "Your skin suddenly appears lighter...")
-
-
-/*
-//////////////////////////////////////
-Revitiligo
-
- Slightly noticable.
+ Noticeable.
Increases resistance.
Increases stage speed slightly.
Increases transmission.
Critical Level.
BONUS
- Makes the mob gain skin pigmentation.
+ Makes the mob gain a random crayon powder colorful reagent.
//////////////////////////////////////
*/
-/datum/symptom/revitiligo
-
- name = "Revitiligo"
- desc = "The virus causes increased production of skin pigment cells, making the host's skin grow darker over time."
+/datum/symptom/polyvitiligo
+ name = "Polyvitiligo"
+ desc = "The virus replaces the melanin in the skin with reactive pigment."
stealth = -1
resistance = 3
stage_speed = 1
@@ -72,17 +26,16 @@ BONUS
symptom_delay_min = 7
symptom_delay_max = 14
-/datum/symptom/revitiligo/Activate(datum/disease/advance/A)
+/datum/symptom/polyvitiligo/Activate(datum/disease/advance/A)
if(!..())
return
var/mob/living/M = A.affected_mob
- if(ishuman(M))
- var/mob/living/carbon/human/H = M
- if(H.skin_tone == "african2")
- return
- switch(A.stage)
- if(5)
- H.skin_tone = "african2"
- H.update_body(0)
- else
- H.visible_message("[H] looks a bit dark...", "Your skin suddenly appears darker...")
+ switch(A.stage)
+ if(5)
+ var/static/list/banned_reagents = list(/datum/reagent/colorful_reagent/crayonpowder/invisible, /datum/reagent/colorful_reagent/crayonpowder/white)
+ var/color = pick(subtypesof(/datum/reagent/colorful_reagent/crayonpowder) - banned_reagents)
+ if(M.reagents.total_volume <= (M.reagents.maximum_volume/10)) // no flooding humans with 1000 units of colorful reagent
+ M.reagents.add_reagent(color, 5)
+ else
+ if (prob(50)) // spam
+ M.visible_message("[M] looks rather vibrant...", "The colors, man, the colors...")
\ No newline at end of file
diff --git a/code/datums/diseases/advance/symptoms/sneeze.dm b/code/datums/diseases/advance/symptoms/sneeze.dm
index 5d4d40fb95..439f391fe4 100644
--- a/code/datums/diseases/advance/symptoms/sneeze.dm
+++ b/code/datums/diseases/advance/symptoms/sneeze.dm
@@ -27,8 +27,10 @@ Bonus
severity = 1
symptom_delay_min = 5
symptom_delay_max = 35
- threshold_desc = "Transmission 9: Increases sneezing range, spreading the virus over a larger area. \
- Stealth 4: The symptom remains hidden until active."
+ threshold_desc = list(
+ "Transmission 9" = "Increases sneezing range, spreading the virus over 6 meter cone instead of over a 4 meter cone.",
+ "Stealth 4" = "The symptom remains hidden until active.",
+ )
/datum/symptom/sneeze/Start(datum/disease/advance/A)
if(!..())
@@ -39,7 +41,7 @@ Bonus
suppress_warning = TRUE
/datum/symptom/sneeze/Activate(datum/disease/advance/A)
- if(!..())
+ if(!..() || HAS_TRAIT(A.affected_mob,TRAIT_NOBREATH))
return
var/mob/living/M = A.affected_mob
switch(A.stage)
diff --git a/code/datums/diseases/advance/symptoms/species.dm b/code/datums/diseases/advance/symptoms/species.dm
index a8b18ae735..49a3cf8d07 100644
--- a/code/datums/diseases/advance/symptoms/species.dm
+++ b/code/datums/diseases/advance/symptoms/species.dm
@@ -14,7 +14,7 @@
/datum/symptom/undead_adaptation/OnRemove(datum/disease/advance/A)
A.process_dead = FALSE
- A.infectable_biotypes -= MOB_UNDEAD
+ A.infectable_biotypes &= ~MOB_UNDEAD
/datum/symptom/inorganic_adaptation
name = "Inorganic Biology"
@@ -27,7 +27,8 @@
severity = 0
/datum/symptom/inorganic_adaptation/OnAdd(datum/disease/advance/A)
- A.infectable_biotypes |= MOB_INORGANIC
+ A.infectable_biotypes |= MOB_MINERAL //Mineral covers plasmamen and golems.
/datum/symptom/inorganic_adaptation/OnRemove(datum/disease/advance/A)
- A.infectable_biotypes -= MOB_INORGANIC
\ No newline at end of file
+ A.infectable_biotypes &= ~MOB_MINERAL
+
diff --git a/code/datums/diseases/advance/symptoms/symptoms.dm b/code/datums/diseases/advance/symptoms/symptoms.dm
index fef6453b3a..a6ea7de5a0 100644
--- a/code/datums/diseases/advance/symptoms/symptoms.dm
+++ b/code/datums/diseases/advance/symptoms/symptoms.dm
@@ -38,10 +38,11 @@
return
CRASH("We couldn't assign an ID!")
-// Called when processing of the advance disease that holds this symptom infects a host and upon each Refresh() of that advance disease.
+// Called when processing of the advance disease, which holds this symptom, starts.
/datum/symptom/proc/Start(datum/disease/advance/A)
if(neutered)
return FALSE
+ next_activation = world.time + rand(symptom_delay_min * 10, symptom_delay_max * 10) //so it doesn't instantly activate on infection
return TRUE
// Called when the advance disease is going to be deleted or when the advance disease stops processing.
@@ -59,7 +60,7 @@
next_activation = world.time + rand(symptom_delay_min * 10, symptom_delay_max * 10)
return TRUE
-/datum/symptom/proc/on_stage_change(datum/disease/advance/A)
+/datum/symptom/proc/on_stage_change(new_stage, datum/disease/advance/A)
if(neutered)
return FALSE
return TRUE
diff --git a/code/datums/diseases/advance/symptoms/viral.dm b/code/datums/diseases/advance/symptoms/viral.dm
index 55807aed6b..28ac491fcd 100644
--- a/code/datums/diseases/advance/symptoms/viral.dm
+++ b/code/datums/diseases/advance/symptoms/viral.dm
@@ -26,7 +26,7 @@ BONUS
//////////////////////////////////////
Viral evolution
- Moderate stealth reductopn.
+ Moderate stealth reduction.
Major decreases to resistance.
increases stage speed.
increase to transmission
diff --git a/code/datums/diseases/advance/symptoms/vision.dm b/code/datums/diseases/advance/symptoms/vision.dm
index b4a33cb837..5453d6e11a 100644
--- a/code/datums/diseases/advance/symptoms/vision.dm
+++ b/code/datums/diseases/advance/symptoms/vision.dm
@@ -29,9 +29,10 @@ Bonus
symptom_delay_min = 25
symptom_delay_max = 80
var/remove_eyes = FALSE
- threshold_desc = "Resistance 12: Weakens extraocular muscles, eventually leading to complete detachment of the eyes. \
- Stealth 4: The symptom remains hidden until active."
-
+ threshold_desc = list(
+ "Resistance 12" = "Weakens extraocular muscles, eventually leading to complete detachment of the eyes.",
+ "Stealth 4" = "The symptom remains hidden until active.",
+ )
/datum/symptom/visionloss/Start(datum/disease/advance/A)
if(!..())
return
@@ -66,7 +67,7 @@ Bonus
eyes.applyOrganDamage(eyes.maxHealth)
else
M.visible_message("[M]'s eyes fall off their sockets!", "Your eyes fall off their sockets!")
- eyes.Remove(M)
+ eyes.Remove()
eyes.forceMove(get_turf(M))
else
to_chat(M, "Your eyes burn horrifically!")
diff --git a/code/datums/diseases/advance/symptoms/voice_change.dm b/code/datums/diseases/advance/symptoms/voice_change.dm
index 8b9b7d069a..9be484d99a 100644
--- a/code/datums/diseases/advance/symptoms/voice_change.dm
+++ b/code/datums/diseases/advance/symptoms/voice_change.dm
@@ -31,9 +31,11 @@ Bonus
var/scramble_language = FALSE
var/datum/language/current_language
var/datum/language_holder/original_language
- threshold_desc = "Transmission 14: The host's language center of the brain is damaged, leading to complete inability to speak or understand any language. \
- Stage Speed 7: Changes voice more often. \
- Stealth 3: The symptom remains hidden until active."
+ threshold_desc = list(
+ "Transmission 14" = "The host's language center of the brain is damaged, leading to complete inability to speak or understand any language.",
+ "Stage Speed 7" = "Changes voice more often.",
+ "Stealth 3" = "The symptom remains hidden until active."
+ )
/datum/symptom/voice_change/Start(datum/disease/advance/A)
if(!..())
diff --git a/code/datums/diseases/advance/symptoms/vomit.dm b/code/datums/diseases/advance/symptoms/vomit.dm
index f53638bc12..22e249a388 100644
--- a/code/datums/diseases/advance/symptoms/vomit.dm
+++ b/code/datums/diseases/advance/symptoms/vomit.dm
@@ -25,8 +25,8 @@ Bonus
desc = "The virus causes nausea and irritates the stomach, causing occasional vomit."
stealth = -2
resistance = -1
- stage_speed = 0
- transmittable = 1
+ stage_speed = -1
+ transmittable = 2
level = 3
severity = 3
base_message_chance = 100
@@ -34,9 +34,11 @@ Bonus
symptom_delay_max = 80
var/vomit_blood = FALSE
var/proj_vomit = 0
- threshold_desc = "Resistance 7: Host will vomit blood, causing internal damage. \
- Transmission 7: Host will projectile vomit, increasing vomiting range. \
- Stealth 4: The symptom remains hidden until active."
+ threshold_desc = list(
+ "Resistance 7" = "Host will vomit blood, causing internal damage.",
+ "Transmission 7" = "Host will projectile vomit, increasing vomiting range.",
+ "Stealth 4" = "The symptom remains hidden until active."
+ )
/datum/symptom/vomit/Start(datum/disease/advance/A)
if(!..())
diff --git a/code/datums/diseases/advance/symptoms/weight.dm b/code/datums/diseases/advance/symptoms/weight.dm
index 66fc90f1f8..5eab698750 100644
--- a/code/datums/diseases/advance/symptoms/weight.dm
+++ b/code/datums/diseases/advance/symptoms/weight.dm
@@ -29,7 +29,9 @@ Bonus
base_message_chance = 100
symptom_delay_min = 15
symptom_delay_max = 45
- threshold_desc = "Stealth 4: The symptom is less noticeable."
+ threshold_desc = list(
+ "Stealth 4" = "The symptom is less noticeable."
+ )
/datum/symptom/weight_loss/Start(datum/disease/advance/A)
if(!..())
diff --git a/code/datums/diseases/beesease.dm b/code/datums/diseases/beesease.dm
index 074bda0560..ccae692b4b 100644
--- a/code/datums/diseases/beesease.dm
+++ b/code/datums/diseases/beesease.dm
@@ -10,7 +10,7 @@
viable_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/monkey)
desc = "If left untreated subject will regurgitate bees."
severity = DISEASE_SEVERITY_MEDIUM
- infectable_biotypes = list(MOB_ORGANIC, MOB_UNDEAD) //bees nesting in corpses
+ infectable_biotypes = MOB_ORGANIC|MOB_UNDEAD //bees nesting in corpses
/datum/disease/beesease/stage_act()
..()
diff --git a/code/datums/diseases/magnitis.dm b/code/datums/diseases/magnitis.dm
index 29e6657e13..0bfb918ba0 100644
--- a/code/datums/diseases/magnitis.dm
+++ b/code/datums/diseases/magnitis.dm
@@ -10,7 +10,7 @@
permeability_mod = 0.75
desc = "This disease disrupts the magnetic field of your body, making it act as if a powerful magnet. Injections of iron help stabilize the field."
severity = DISEASE_SEVERITY_MEDIUM
- infectable_biotypes = list(MOB_ORGANIC, MOB_ROBOTIC)
+ infectable_biotypes = MOB_ORGANIC|MOB_ROBOTIC
process_dead = TRUE
/datum/disease/magnitis/stage_act()
diff --git a/code/datums/diseases/parrotpossession.dm b/code/datums/diseases/parrotpossession.dm
index 68e962055a..1a3346d565 100644
--- a/code/datums/diseases/parrotpossession.dm
+++ b/code/datums/diseases/parrotpossession.dm
@@ -11,7 +11,7 @@
viable_mobtypes = list(/mob/living/carbon/human)
desc = "Subject is possessed by the vengeful spirit of a parrot. Call the priest."
severity = DISEASE_SEVERITY_MEDIUM
- infectable_biotypes = list(MOB_ORGANIC, MOB_UNDEAD, MOB_INORGANIC, MOB_ROBOTIC)
+ infectable_biotypes = MOB_ORGANIC|MOB_UNDEAD|MOB_ROBOTIC|MOB_MINERAL
bypasses_immunity = TRUE //2spook
var/mob/living/simple_animal/parrot/Poly/ghost/parrot
diff --git a/code/datums/diseases/transformation.dm b/code/datums/diseases/transformation.dm
index bdb05346e4..36c7d70f3c 100644
--- a/code/datums/diseases/transformation.dm
+++ b/code/datums/diseases/transformation.dm
@@ -168,7 +168,7 @@
stage4 = list("Your skin feels very loose.", "You can feel... something...inside you.")
stage5 = list("Your skin feels as if it's about to burst off!")
new_form = /mob/living/silicon/robot
- infectable_biotypes = list(MOB_ORGANIC, MOB_UNDEAD, MOB_ROBOTIC)
+ infectable_biotypes = MOB_ORGANIC|MOB_UNDEAD|MOB_ROBOTIC
bantype = "Cyborg"
/datum/disease/transformation/robot/stage_act()
@@ -284,7 +284,7 @@
stage4 = list("You're ravenous.")
stage5 = list("You have become a morph.")
new_form = /mob/living/simple_animal/hostile/morph
- infectable_biotypes = list(MOB_ORGANIC, MOB_INORGANIC, MOB_UNDEAD) //magic!
+ infectable_biotypes = MOB_ORGANIC|MOB_MINERAL|MOB_UNDEAD //magic!
/datum/disease/transformation/gondola
name = "Gondola Transformation"
diff --git a/code/datums/diseases/wizarditis.dm b/code/datums/diseases/wizarditis.dm
index 230a074bb1..71a5bcb99e 100644
--- a/code/datums/diseases/wizarditis.dm
+++ b/code/datums/diseases/wizarditis.dm
@@ -94,7 +94,7 @@ STI KALY - blind
var/area/thearea = pick(theareas)
var/list/L = list()
- for(var/turf/T in get_area_turfs(thearea.type))
+ for(var/turf/T in thearea)
if(T.z != affected_mob.z)
continue
if(T.name == "space")
diff --git a/code/datums/dna.dm b/code/datums/dna.dm
index 67dbbdfef7..68c6839482 100644
--- a/code/datums/dna.dm
+++ b/code/datums/dna.dm
@@ -92,7 +92,15 @@
. = ""
var/list/L = new /list(DNA_UNI_IDENTITY_BLOCKS)
- L[DNA_GENDER_BLOCK] = construct_block((holder.gender!=MALE)+1, 2)
+ switch(holder.gender)
+ if(MALE)
+ L[DNA_GENDER_BLOCK] = construct_block(G_MALE, 4)
+ if(FEMALE)
+ L[DNA_GENDER_BLOCK] = construct_block(G_FEMALE, 4)
+ if(PLURAL)
+ L[DNA_GENDER_BLOCK] = construct_block(G_PLURAL, 4)
+ else
+ L[DNA_GENDER_BLOCK] = construct_block(G_NEUTER, 4)
if(ishuman(holder))
var/mob/living/carbon/human/H = holder
if(!GLOB.hair_styles_list.len)
@@ -165,7 +173,15 @@
if(DNA_EYE_COLOR_BLOCK)
setblock(uni_identity, blocknumber, sanitize_hexcolor(H.eye_color))
if(DNA_GENDER_BLOCK)
- setblock(uni_identity, blocknumber, construct_block((H.gender!=MALE)+1, 2))
+ switch(H.gender)
+ if(MALE)
+ setblock(uni_identity, blocknumber, construct_block(G_MALE, 4))
+ if(FEMALE)
+ setblock(uni_identity, blocknumber, construct_block(G_FEMALE, 4))
+ if(PLURAL)
+ setblock(uni_identity, blocknumber, construct_block(G_PLURAL, 4))
+ else
+ setblock(uni_identity, blocknumber, construct_block(G_NEUTER, 4))
if(DNA_FACIAL_HAIR_STYLE_BLOCK)
setblock(uni_identity, blocknumber, construct_block(GLOB.facial_hair_styles_list.Find(H.facial_hair_style), GLOB.facial_hair_styles_list.len))
if(DNA_HAIR_STYLE_BLOCK)
@@ -307,7 +323,16 @@
/mob/living/carbon/proc/updateappearance(icon_update=1, mutcolor_update=0, mutations_overlay_update=0)
if(!has_dna())
return
- gender = (deconstruct_block(getblock(dna.uni_identity, DNA_GENDER_BLOCK), 2)-1) ? FEMALE : MALE
+
+ switch(deconstruct_block(getblock(dna.uni_identity, DNA_GENDER_BLOCK), 4))
+ if(G_MALE)
+ gender = MALE
+ if(G_FEMALE)
+ gender = FEMALE
+ if(G_PLURAL)
+ gender = PLURAL
+ else
+ gender = NEUTER
/mob/living/carbon/human/updateappearance(icon_update=1, mutcolor_update=0, mutations_overlay_update=0)
..()
@@ -345,14 +370,14 @@
/////////////////////////// DNA HELPER-PROCS //////////////////////////////
/proc/getleftblocks(input,blocknumber,blocksize)
if(blocknumber > 1)
- return copytext(input,1,((blocksize*blocknumber)-(blocksize-1)))
+ return copytext_char(input,1,((blocksize*blocknumber)-(blocksize-1)))
/proc/getrightblocks(input,blocknumber,blocksize)
if(blocknumber < (length(input)/blocksize))
- return copytext(input,blocksize*blocknumber+1,length(input)+1)
+ return copytext_char(input,blocksize*blocknumber+1,length(input)+1)
/proc/getblock(input, blocknumber, blocksize=DNA_BLOCK_SIZE)
- return copytext(input, blocksize*(blocknumber-1)+1, (blocksize*blocknumber)+1)
+ return copytext_char(input, blocksize*(blocknumber-1)+1, (blocksize*blocknumber)+1)
/proc/setblock(istring, blocknumber, replacement, blocksize=DNA_BLOCK_SIZE)
if(!istring || !blocknumber || !replacement || !blocksize)
diff --git a/code/datums/elements/cleaning.dm b/code/datums/elements/cleaning.dm
index e4fb3edd48..3f80204089 100644
--- a/code/datums/elements/cleaning.dm
+++ b/code/datums/elements/cleaning.dm
@@ -45,4 +45,4 @@
cleaned_human.clean_blood()
cleaned_human.wash_cream()
cleaned_human.regenerate_icons()
- to_chat(cleaned_human, "[src] cleans your face!")
+ to_chat(cleaned_human, "[AM] cleans your face!")
diff --git a/code/datums/elements/dusts_on_leaving_area.dm b/code/datums/elements/dusts_on_leaving_area.dm
new file mode 100644
index 0000000000..9527e1fd3a
--- /dev/null
+++ b/code/datums/elements/dusts_on_leaving_area.dm
@@ -0,0 +1,28 @@
+/datum/element/dusts_on_leaving_area
+ element_flags = ELEMENT_DETACH | ELEMENT_BESPOKE
+ id_arg_index = 2
+ var/list/attached_mobs = list()
+ var/list/area_types = list()
+
+/datum/element/dusts_on_leaving_area/Attach(datum/target,types)
+ . = ..()
+ if(!ismob(target))
+ return ELEMENT_INCOMPATIBLE
+ attached_mobs += target
+ area_types = types
+ START_PROCESSING(SSprocessing,src)
+
+/datum/element/dusts_on_leaving_area/Detach(mob/M)
+ . = ..()
+ if(M in attached_mobs)
+ attached_mobs -= M
+ if(!attached_mobs.len)
+ STOP_PROCESSING(SSprocessing,src)
+
+/datum/element/dusts_on_leaving_area/process()
+ for(var/m in attached_mobs)
+ var/mob/M = m
+ var/area/A = get_area(M)
+ if(!(A.type in area_types))
+ M.dust(force = TRUE)
+ Detach(M)
diff --git a/code/datums/elements/mob_holder.dm b/code/datums/elements/mob_holder.dm
new file mode 100644
index 0000000000..8687d89b2f
--- /dev/null
+++ b/code/datums/elements/mob_holder.dm
@@ -0,0 +1,184 @@
+/datum/element/mob_holder
+ element_flags = ELEMENT_BESPOKE|ELEMENT_DETACH
+ id_arg_index = 2
+ var/worn_state
+ var/alt_worn
+ var/right_hand
+ var/left_hand
+ var/inv_slots
+ var/proctype //if present, will be invoked on headwear generation.
+
+/datum/element/mob_holder/Attach(datum/target, _worn_state, _alt_worn, _right_hand, _left_hand, _inv_slots = NONE, _proctype)
+ . = ..()
+
+ if(!isliving(target))
+ return ELEMENT_INCOMPATIBLE
+
+ worn_state = _worn_state
+ alt_worn = _alt_worn
+ right_hand = _right_hand
+ left_hand = _left_hand
+ inv_slots = _inv_slots
+ proctype = _proctype
+
+ RegisterSignal(target, COMSIG_CLICK_ALT, .proc/mob_try_pickup)
+ RegisterSignal(target, COMSIG_PARENT_EXAMINE, .proc/on_examine)
+
+/datum/element/mob_holder/Detach(datum/source, force)
+ . = ..()
+ UnregisterSignal(source, COMSIG_CLICK_ALT)
+ UnregisterSignal(source, COMSIG_PARENT_EXAMINE)
+
+/datum/element/mob_holder/proc/on_examine(mob/living/source, mob/user, list/examine_list)
+ if(ishuman(user) && !istype(source.loc, /obj/item/clothing/head/mob_holder))
+ examine_list += "Looks like [source.p_they(TRUE)] can be picked up with Alt+Click!"
+
+/datum/element/mob_holder/proc/mob_try_pickup(mob/living/source, mob/user)
+ if(!ishuman(user) || !user.Adjacent(source) || user.incapacitated())
+ return FALSE
+ if(user.get_active_held_item())
+ to_chat(user, "Your hands are full!")
+ return FALSE
+ if(source.buckled)
+ to_chat(user, "[source] is buckled to something!")
+ return FALSE
+ if(source == user)
+ to_chat(user, "You can't pick yourself up.")
+ return FALSE
+ source.visible_message("[user] starts picking up [source].", \
+ "[user] starts picking you up!")
+ if(!do_after(user, 20, target = source) || source.buckled)
+ return FALSE
+
+ source.visible_message("[user] picks up [source]!", \
+ "[user] picks you up!")
+ to_chat(user, "You pick [source] up.")
+ source.drop_all_held_items()
+ var/obj/item/clothing/head/mob_holder/holder = new(get_turf(source), source, worn_state, alt_worn, right_hand, left_hand, inv_slots)
+ if(proctype)
+ INVOKE_ASYNC(src, proctype, source, holder, user)
+ user.put_in_hands(holder)
+ return TRUE
+
+/datum/element/mob_holder/proc/drone_worn_icon(mob/living/simple_animal/drone/D, obj/item/clothing/head/mob_holder/holder, mob/user)
+ var/new_state = "[D.visualAppearence]_hat"
+ holder.item_state = new_state
+ holder.icon_state = new_state
+
+
+//The item itself,
+/obj/item/clothing/head/mob_holder
+ name = "bugged mob"
+ desc = "Yell at coderbrush."
+ icon = null
+ alternate_worn_icon = 'icons/mob/animals_held.dmi'
+ righthand_file = 'icons/mob/animals_held_rh.dmi'
+ lefthand_file = 'icons/mob/animals_held_lh.dmi'
+ icon_state = ""
+ w_class = WEIGHT_CLASS_BULKY
+ var/mob/living/held_mob
+
+/obj/item/clothing/head/mob_holder/Initialize(mapload, mob/living/target, worn_state, alt_worn, right_hand, left_hand, slots = NONE)
+ . = ..()
+
+ if(target)
+ assimilate(target)
+
+ if(alt_worn)
+ alternate_worn_icon = alt_worn
+ if(worn_state)
+ item_state = worn_state
+ icon_state = worn_state
+ if(left_hand)
+ lefthand_file = left_hand
+ if(right_hand)
+ righthand_file = right_hand
+ slot_flags = slots
+
+/obj/item/clothing/head/mob_holder/proc/assimilate(mob/living/target)
+ target.setDir(SOUTH)
+ held_mob = target
+ target.forceMove(src)
+ var/image/I = new //work around to retain the same appearance to the mob idependently from inhands/worn states.
+ I.appearance = target.appearance
+ I.layer = FLOAT_LAYER //So it doesn't get screwed up by layer overrides.
+ I.plane = FLOAT_PLANE //Same as above but for planes.
+ I.override = TRUE
+ add_overlay(I)
+ name = target.name
+ desc = target.desc
+ switch(target.mob_size)
+ if(MOB_SIZE_TINY)
+ w_class = WEIGHT_CLASS_TINY
+ if(MOB_SIZE_SMALL)
+ w_class = WEIGHT_CLASS_NORMAL
+ if(MOB_SIZE_LARGE)
+ w_class = WEIGHT_CLASS_HUGE
+
+/obj/item/clothing/head/mob_holder/Destroy()
+ if(held_mob)
+ release()
+ return ..()
+
+/obj/item/clothing/head/mob_holder/examine(mob/user)
+ return held_mob?.examine(user) || ..()
+
+/obj/item/clothing/head/mob_holder/Exited(atom/movable/AM, atom/newloc)
+ . = ..()
+ if(AM == held_mob)
+ held_mob.reset_perspective()
+ held_mob = null
+ QDEL_IN(src, 1) //To avoid a qdel loop.
+
+/obj/item/clothing/head/mob_holder/Entered(atom/movable/AM, atom/newloc)
+ . = ..()
+ if(AM != held_mob)
+ var/destination = loc
+ if(isliving(loc)) //the mob is held or worn, drop things on the floor
+ destination = get_turf(loc)
+ AM.forceMove(destination)
+
+/obj/item/clothing/head/mob_holder/dropped()
+ . = ..()
+ if(held_mob && isturf(loc))//don't release on soft-drops
+ release()
+
+/obj/item/clothing/head/mob_holder/proc/release()
+ if(held_mob)
+ var/mob/living/L = held_mob
+ held_mob = null
+ L.forceMove(get_turf(L))
+ L.reset_perspective()
+ L.setDir(SOUTH)
+ qdel(src)
+
+/obj/item/clothing/head/mob_holder/relaymove(mob/user)
+ return
+
+/obj/item/clothing/head/mob_holder/container_resist()
+ if(isliving(loc))
+ var/mob/living/L = loc
+ L.visible_message("[held_mob] escapes from [L]!", "[held_mob] escapes your grip!")
+ release()
+
+/obj/item/clothing/head/mob_holder/assume_air(datum/gas_mixture/env)
+ var/atom/location = loc
+ if(!loc)
+ return //null
+ var/turf/T = get_turf(loc)
+ while(location != T)
+ location = location.loc
+ if(ismob(location))
+ return location.loc.assume_air(env)
+ return location.assume_air(env)
+
+/obj/item/clothing/head/mob_holder/remove_air(amount)
+ var/atom/location = loc
+ if(!loc)
+ return //null
+ var/turf/T = get_turf(loc)
+ while(location != T)
+ location = location.loc
+ if(ismob(location))
+ return location.loc.remove_air(amount)
+ return location.remove_air(amount)
diff --git a/code/datums/elements/swimming.dm b/code/datums/elements/swimming.dm
new file mode 100644
index 0000000000..d16ef6625f
--- /dev/null
+++ b/code/datums/elements/swimming.dm
@@ -0,0 +1,21 @@
+/// Just for marking when someone's swimming.
+/datum/element/swimming
+ element_flags = ELEMENT_DETACH
+
+/datum/element/swimming/Attach(datum/target)
+ if((. = ..()) == ELEMENT_INCOMPATIBLE)
+ return
+ if(!isliving(target))
+ return ELEMENT_INCOMPATIBLE
+ RegisterSignal(target, COMSIG_MOVABLE_MOVED, .proc/check_valid)
+ ADD_TRAIT(target, TRAIT_SWIMMING, TRAIT_SWIMMING) //seriously there's only one way to get this
+
+/datum/element/swimming/Detach(datum/target)
+ . = ..()
+ UnregisterSignal(target, COMSIG_MOVABLE_MOVED)
+ REMOVE_TRAIT(target, TRAIT_SWIMMING, TRAIT_SWIMMING)
+
+/datum/element/swimming/proc/check_valid(datum/source)
+ var/mob/living/L = source
+ if(!istype(L.loc, /turf/open/pool))
+ source.RemoveElement(/datum/element/swimming)
diff --git a/code/datums/elements/wuv.dm b/code/datums/elements/wuv.dm
index 84f327500f..fffb90c681 100644
--- a/code/datums/elements/wuv.dm
+++ b/code/datums/elements/wuv.dm
@@ -1,6 +1,6 @@
/datum/element/wuv //D'awwwww
- element_flags = ELEMENT_BESPOKE
+ element_flags = ELEMENT_BESPOKE|ELEMENT_DETACH
id_arg_index = 2
//the for the me emote proc call when petted.
var/pet_emote
@@ -30,6 +30,10 @@
RegisterSignal(target, COMSIG_MOB_ATTACK_HAND, .proc/on_attack_hand)
+/datum/element/wuv/Detach(datum/source, force)
+ . = ..()
+ UnregisterSignal(source, COMSIG_MOB_ATTACK_HAND)
+
/datum/element/wuv/proc/on_attack_hand(datum/source, mob/user)
var/mob/living/L = source
@@ -43,7 +47,7 @@
addtimer(CALLBACK(src, .proc/pet_the_dog, source, user), 1)
/datum/element/wuv/proc/pet_the_dog(mob/target, mob/user)
- if(!QDELETED(target) || !QDELETED(user) || target.stat != CONSCIOUS)
+ if(QDELETED(target) || QDELETED(user) || target.stat != CONSCIOUS)
return
new /obj/effect/temp_visual/heart(target.loc)
if(pet_emote)
@@ -52,7 +56,7 @@
SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, target, pet_moodlet, target)
/datum/element/wuv/proc/kick_the_dog(mob/target, mob/user)
- if(!QDELETED(target) || !QDELETED(user) || target.stat != CONSCIOUS)
+ if(QDELETED(target) || QDELETED(user) || target.stat != CONSCIOUS)
return
if(punt_emote)
target.emote("me", punt_type, punt_emote)
diff --git a/code/datums/emotes.dm b/code/datums/emotes.dm
index 599c492d4c..e653e3e36b 100644
--- a/code/datums/emotes.dm
+++ b/code/datums/emotes.dm
@@ -18,6 +18,7 @@
var/list/mob_type_ignore_stat_typecache
var/stat_allowed = CONSCIOUS
var/static/list/emote_list = list()
+ var/static/regex/stop_bad_mime = regex(@"says|exclaims|yells|asks")
/datum/emote/New()
if(key_third_person)
diff --git a/code/datums/helper_datums/getrev.dm b/code/datums/helper_datums/getrev.dm
index 420602cc7f..0b0fed407c 100644
--- a/code/datums/helper_datums/getrev.dm
+++ b/code/datums/helper_datums/getrev.dm
@@ -27,7 +27,8 @@
for(var/line in testmerge)
var/datum/tgs_revision_information/test_merge/tm = line
- msg += "Test merge active of PR #[tm.number] commit [tm.commit]"
+ msg += "Test merge active of PR #[tm.number] commit [tm.pull_request_commit]"
+ SSblackbox.record_feedback("associative", "testmerged_prs", 1, list("number" = "[tm.number]", "commit" = "[tm.pull_request_commit]", "title" = "[tm.title]", "author" = "[tm.author]"))
if(commit && commit != originmastercommit)
msg += "HEAD: [commit]"
@@ -43,7 +44,7 @@
for(var/line in testmerge)
var/datum/tgs_revision_information/test_merge/tm = line
var/cm = tm.pull_request_commit
- var/details = ": '" + html_encode(tm.title) + "' by " + html_encode(tm.author) + " at commit " + html_encode(copytext(cm, 1, min(length(cm), 11)))
+ var/details = ": '" + html_encode(tm.title) + "' by " + html_encode(tm.author) + " at commit " + html_encode(copytext_char(cm, 1, 11))
if(details && findtext(details, "\[s\]") && (!usr || !usr.client.holder))
continue
. += "#[tm.number][details] "
@@ -57,11 +58,11 @@
// Round ID
if(GLOB.round_id)
msg += "Round ID: [GLOB.round_id]"
-
+
msg += "BYOND Version: [world.byond_version].[world.byond_build]"
if(DM_VERSION != world.byond_version || DM_BUILD != world.byond_build)
msg += "Compiled with BYOND Version: [DM_VERSION].[DM_BUILD]"
-
+
// Revision information
var/datum/getrev/revdata = GLOB.revdata
msg += "Server revision compiled on: [revdata.date]"
@@ -75,7 +76,8 @@
else if(!pc)
msg += "No commit information"
if(world.TgsAvailable())
- msg += "Server tools version: [world.TgsVersion()]"
+ var/datum/tgs_version/version = world.TgsVersion()
+ msg += "Server tools version: [version.raw_parameter]"
// Game mode odds
msg += " Current Informational Settings:"
diff --git a/code/datums/helper_datums/topic_input.dm b/code/datums/helper_datums/topic_input.dm
deleted file mode 100644
index d07aec067e..0000000000
--- a/code/datums/helper_datums/topic_input.dm
+++ /dev/null
@@ -1,62 +0,0 @@
-/datum/topic_input
- var/href
- var/list/href_list
-
-/datum/topic_input/New(thref,list/thref_list)
- href = thref
- href_list = thref_list.Copy()
- return
-
-/datum/topic_input/proc/get(i)
- return listgetindex(href_list,i)
-
-/datum/topic_input/proc/getAndLocate(i)
- var/t = get(i)
- if(t)
- t = locate(t)
- if (istext(t))
- t = null
- return t || null
-
-/datum/topic_input/proc/getNum(i)
- var/t = get(i)
- if(t)
- t = text2num(t)
- return isnum(t) ? t : null
-
-/datum/topic_input/proc/getObj(i)
- var/t = getAndLocate(i)
- return isobj(t) ? t : null
-
-/datum/topic_input/proc/getMob(i)
- var/t = getAndLocate(i)
- return ismob(t) ? t : null
-
-/datum/topic_input/proc/getTurf(i)
- var/t = getAndLocate(i)
- return isturf(t) ? t : null
-
-/datum/topic_input/proc/getAtom(i)
- return getType(i, /atom)
-
-/datum/topic_input/proc/getArea(i)
- var/t = getAndLocate(i)
- return isarea(t) ? t : null
-
-/datum/topic_input/proc/getStr(i)//params should always be text, but...
- var/t = get(i)
- return istext(t) ? t : null
-
-/datum/topic_input/proc/getType(i,type)
- var/t = getAndLocate(i)
- return istype(t,type) ? t : null
-
-/datum/topic_input/proc/getPath(i)
- var/t = get(i)
- if(t)
- t = text2path(t)
- return ispath(t) ? t : null
-
-/datum/topic_input/proc/getList(i)
- var/t = getAndLocate(i)
- return islist(t) ? t : null
diff --git a/code/datums/holocall.dm b/code/datums/holocall.dm
index 5a4600432d..e0b0072808 100644
--- a/code/datums/holocall.dm
+++ b/code/datums/holocall.dm
@@ -12,7 +12,12 @@
/mob/camera/aiEye/remote/holo/setLoc()
. = ..()
var/obj/machinery/holopad/H = origin
- H.move_hologram(eye_user, loc)
+ H?.move_hologram(eye_user, loc)
+
+/obj/machinery/holopad/remove_eye_control(mob/living/user)
+ if(user.client)
+ user.reset_perspective(null)
+ user.remote_control = null
//this datum manages it's own references
@@ -54,11 +59,6 @@
/datum/holocall/Destroy()
QDEL_NULL(hangup)
- var/user_good = !QDELETED(user)
- if(user_good)
- user.reset_perspective()
- user.remote_control = null
-
if(!QDELETED(eye))
QDEL_NULL(eye)
@@ -257,8 +257,8 @@
var/splitpoint = findtext(prepared_line," ")
if(!splitpoint)
continue
- var/command = copytext(prepared_line,1,splitpoint)
- var/value = copytext(prepared_line,splitpoint+1)
+ var/command = copytext(prepared_line, 1, splitpoint)
+ var/value = copytext(prepared_line, splitpoint + length(prepared_line[splitpoint]))
switch(command)
if("DELAY")
var/delay_value = text2num(value)
diff --git a/code/datums/hud.dm b/code/datums/hud.dm
index e2d9bc579f..6b6fcb9691 100644
--- a/code/datums/hud.dm
+++ b/code/datums/hud.dm
@@ -32,7 +32,7 @@ GLOBAL_LIST_INIT(huds, list(
/datum/atom_hud
var/list/atom/hudatoms = list() //list of all atoms which display this hud
- var/list/mob/hudusers = list() //list with all mobs who can see the hud
+ var/list/hudusers = list() //list with all mobs who can see the hud
var/list/hud_icons = list() //these will be the indexes for the atom's hud_list
var/list/next_time_allowed = list() //mobs associated with the next time this hud can be added to them
diff --git a/code/datums/martial.dm b/code/datums/martial.dm
index c8caa853c3..cf2adfce8c 100644
--- a/code/datums/martial.dm
+++ b/code/datums/martial.dm
@@ -31,7 +31,7 @@
reset_streak(D)
streak = streak+element
if(length(streak) > max_streak_length)
- streak = copytext(streak,2)
+ streak = copytext(streak, 1 + length(streak[1]))
return
/datum/martial_art/proc/reset_streak(mob/living/carbon/human/new_target)
diff --git a/code/datums/martial/boxing.dm b/code/datums/martial/boxing.dm
index b98bc4f951..ea883c5637 100644
--- a/code/datums/martial/boxing.dm
+++ b/code/datums/martial/boxing.dm
@@ -50,6 +50,12 @@
D.forcesay(GLOB.hit_appends)
return 1
+/datum/martial_art/boxing/teach(mob/living/carbon/human/H, make_temporary = TRUE)
+ . = ..()
+ if(.)
+ if(H.pulling && ismob(H.pulling))
+ H.stop_pulling()
+
/obj/item/clothing/gloves/boxing
var/datum/martial_art/boxing/style = new
@@ -58,7 +64,7 @@
return
if(slot == SLOT_GLOVES)
var/mob/living/carbon/human/H = user
- style.teach(H,1)
+ style.teach(H,TRUE)
return
/obj/item/clothing/gloves/boxing/dropped(mob/user)
diff --git a/code/datums/martial/rising_bass.dm b/code/datums/martial/rising_bass.dm
index 9e00c90a92..779428669c 100644
--- a/code/datums/martial/rising_bass.dm
+++ b/code/datums/martial/rising_bass.dm
@@ -1,8 +1,6 @@
#define SIDE_KICK_COMBO "DH"
#define SHOULDER_FLIP_COMBO "GHDGHH"
-#define REPULSE_PUNCH_COMBO "GHGH"
#define FOOT_SMASH_COMBO "HH"
-#define DEFT_SWITCH_COMBO "GDD"
/datum/martial_art/the_rising_bass
name = "The Rising Bass"
@@ -10,6 +8,9 @@
dodge_chance = 100
allow_temp_override = FALSE
help_verb = /mob/living/carbon/human/proc/rising_bass_help
+ var/datum/action/risingbassmove/repulsepunch = new/datum/action/risingbassmove/repulsepunch()
+ var/datum/action/risingbassmove/deftswitch = new/datum/action/risingbassmove/deftswitch()
+ var/repulsecool = 0
/datum/martial_art/the_rising_bass/proc/check_streak(mob/living/carbon/human/A, mob/living/carbon/human/D)
if(findtext(streak,SIDE_KICK_COMBO))
@@ -20,7 +21,7 @@
streak = ""
shoulderFlip(A,D)
return 1
- if(findtext(streak,REPULSE_PUNCH_COMBO))
+ if(findtext(streak,"rplse"))
streak = ""
repulsePunch(A,D)
return 1
@@ -28,13 +29,46 @@
streak = ""
footSmash(A,D)
return 1
- if(findtext(streak,DEFT_SWITCH_COMBO))
+ if(findtext(streak,"deft"))
streak = ""
deftSwitch(A,D)
return 1
return 0
+//Repulse Punch - Slams the opponent far away from you.
+/datum/action/risingbassmove
+ name = ""
+ icon_icon = 'icons/mob/actions/actions_items.dmi'
+ button_icon_state = ""
+ var/movestreak = ""
+
+/datum/action/risingbassmove/Trigger()
+ if(owner.incapacitated())
+ to_chat(owner, "You can't use [name] while you're incapacitated.")
+ return
+ var/mob/living/carbon/human/H = owner
+ if (H.mind.martial_art.streak == "[movestreak]")
+ H.mind.martial_art.streak = ""
+ to_chat(H,"You relax your muscles and return to a neutral position.")
+ else
+ if(HAS_TRAIT(H, TRAIT_PACIFISM))
+ to_chat(H, "You don't want to harm other people!")
+ return
+ to_chat(H,"You get ready to use the [name] maneuver!")
+ H.mind.martial_art.streak = "[movestreak]"
+
+/datum/action/risingbassmove/repulsepunch
+ name = "Repulse Punch"
+ button_icon_state = "repulsepunch"
+ movestreak = "rplse"
+
+/datum/action/risingbassmove/deftswitch
+ name = "Deft Switch"
+ button_icon_state = "deftswitch"
+ movestreak = "deft"
+
+
/datum/martial_art/the_rising_bass/proc/sideKick(mob/living/carbon/human/A, mob/living/carbon/human/D)
if(!D.IsKnockdown() || D.lying == 0)
var/turf/H = get_step(D, A.dir & (NORTH | SOUTH) ? pick(EAST, WEST) : pick(NORTH, SOUTH))
@@ -75,7 +109,7 @@
return basic_hit(A,D)
/datum/martial_art/the_rising_bass/proc/repulsePunch(mob/living/carbon/human/A, mob/living/carbon/human/D)
- if(!D.IsKnockdown() || !D.lying)
+ if(!D.IsKnockdown() || !D.lying || repulsecool > world.time)
A.do_attack_animation(D, ATTACK_EFFECT_PUNCH)
D.visible_message("[A] smashes [D] in the chest, throwing them away!", \
"[A] smashes you in the chest, repelling you away!")
@@ -85,6 +119,7 @@
D.apply_damage(10, BRUTE, BODY_ZONE_CHEST)
D.Knockdown(90)
log_combat(A, D, "repulse punched (Rising Bass)")
+ repulsecool = world.time + 3 SECONDS
return 1
return basic_hit(A,D)
@@ -132,6 +167,11 @@
return 1
return ..()
+/datum/martial_art/the_rising_bass/add_to_streak(element,mob/living/carbon/human/D)
+ if (streak == "deft" || streak == "rplse")
+ return
+ . = ..()
+
/mob/living/carbon/human/proc/rising_bass_help()
set name = "Recall Teachings"
set desc = "Remember the martial techniques of the Rising Bass clan."
@@ -149,10 +189,14 @@
. = ..()
if(!.)
return
+ deftswitch.Grant(H)
+ repulsepunch.Grant(H)
ADD_TRAIT(H, TRAIT_NOGUNS, RISING_BASS_TRAIT)
ADD_TRAIT(H, TRAIT_AUTO_CATCH_ITEM, RISING_BASS_TRAIT)
/datum/martial_art/the_rising_bass/on_remove(mob/living/carbon/human/H)
. = ..()
+ deftswitch.Remove(H)
+ repulsepunch.Remove(H)
REMOVE_TRAIT(H, TRAIT_NOGUNS, RISING_BASS_TRAIT)
REMOVE_TRAIT(H, TRAIT_AUTO_CATCH_ITEM, RISING_BASS_TRAIT)
\ No newline at end of file
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index f4e675d98b..4689d52bd9 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -61,7 +61,6 @@
var/late_joiner = FALSE
var/force_escaped = FALSE // Set by Into The Sunset command of the shuttle manipulator
-
var/list/learned_recipes //List of learned recipe TYPES.
/datum/mind/New(var/key)
@@ -139,7 +138,7 @@
SEND_SIGNAL(new_character, COMSIG_MOB_ON_NEW_MIND)
/datum/mind/proc/store_memory(new_text)
- if((length(memory) + length(new_text)) <= MAX_MESSAGE_LEN)
+ if((length_char(memory) + length_char(new_text)) <= MAX_MESSAGE_LEN)
memory += "[new_text] "
/datum/mind/proc/wipe_memory()
@@ -409,7 +408,7 @@
assigned_role = new_role
else if (href_list["memory_edit"])
- var/new_memo = copytext(sanitize(input("Write new memory", "Memory", memory) as null|message),1,MAX_MESSAGE_LEN)
+ var/new_memo = stripped_multiline_input(usr, "Write new memory", "Memory", memory, MAX_MESSAGE_LEN)
if (isnull(new_memo))
return
memory = new_memo
@@ -458,6 +457,7 @@
var/list/allowed_types = list(
/datum/objective/assassinate,
+ /datum/objective/assassinate/once,
/datum/objective/maroon,
/datum/objective/debrain,
/datum/objective/protect,
@@ -714,6 +714,11 @@
if(G)
G.reenter_corpse()
+/// Sets our can_hijack to the fastest speed our antag datums allow.
+/datum/mind/proc/get_hijack_speed()
+ . = 0
+ for(var/datum/antagonist/A in antag_datums)
+ . = max(., A.hijack_speed())
/datum/mind/proc/has_objective(objective_type)
for(var/datum/antagonist/A in antag_datums)
diff --git a/code/datums/mood_events/generic_negative_events.dm b/code/datums/mood_events/generic_negative_events.dm
index 0b9b02af75..81ee5c3989 100644
--- a/code/datums/mood_events/generic_negative_events.dm
+++ b/code/datums/mood_events/generic_negative_events.dm
@@ -201,51 +201,52 @@
/datum/mood_event/vampcandle
description = "Something is making your mind feel... loose...\n"
- mood_change = -10
+ mood_change = -15
timeout = 1 MINUTES
/datum/mood_event/drankblood_bad
description = "I drank the blood of a lesser creature. Disgusting.\n"
mood_change = -4
- timeout = 900
+ timeout = 8 MINUTES
/datum/mood_event/drankblood_dead
description = "I drank dead blood. I am better than this.\n"
mood_change = -7
- timeout = 900
+ timeout = 10 MINUTES
/datum/mood_event/drankblood_synth
description = "I drank synthetic blood. What is wrong with me?\n"
mood_change = -7
- timeout = 900
+ timeout = 15 MINUTES
/datum/mood_event/drankkilled
description = "I drank from my victim until they died. I feel...less human.\n"
mood_change = -12
- timeout = 6000
+ timeout = 25 MINUTES
/datum/mood_event/madevamp
description = "A soul has been cursed to undeath by my own hand.\n"
mood_change = -10
- timeout = 10000
+ timeout = 30 MINUTES
/datum/mood_event/vampatefood
description = "Mortal nourishment no longer sustains me. I feel unwell.\n"
mood_change = -6
- timeout = 1000
+ timeout = 10 MINUTES
/datum/mood_event/daylight_1
description = "I slept poorly in a makeshift coffin during the day.\n"
mood_change = -3
- timeout = 1000
+ timeout = 10 MINUTES
-/datum/mood_event/nanite_sadness
- description = "+++++++HAPPINESS SUPPRESSION+++++++\n"
- mood_change = -7
/datum/mood_event/daylight_2
description = "I have been scorched by the unforgiving rays of the sun.\n"
mood_change = -6
- timeout = 1200
+ timeout = 15 MINUTES
+
+/datum/mood_event/nanite_sadness
+ description = "+++++++HAPPINESS SUPPRESSION+++++++\n"
+ mood_change = -7
/datum/mood_event/nanite_sadness/add_effects(message)
description = "+++++++[message]+++++++\n"
diff --git a/code/datums/mutations.dm b/code/datums/mutations.dm
index f689ff903e..6397bbe66b 100644
--- a/code/datums/mutations.dm
+++ b/code/datums/mutations.dm
@@ -32,9 +32,9 @@ GLOBAL_LIST_EMPTY(mutations_list)
/datum/mutation/human/proc/set_se(se_string, on = 1)
if(!se_string || length(se_string) < DNA_STRUC_ENZYMES_BLOCKS * DNA_BLOCK_SIZE)
return
- var/before = copytext(se_string, 1, ((dna_block - 1) * DNA_BLOCK_SIZE) + 1)
+ var/before = copytext_char(se_string, 1, ((dna_block - 1) * DNA_BLOCK_SIZE) + 1)
var/injection = num2hex(on ? rand(lowest_value, (256 * 16) - 1) : rand(0, lowest_value - 1), DNA_BLOCK_SIZE)
- var/after = copytext(se_string, (dna_block * DNA_BLOCK_SIZE) + 1, 0)
+ var/after = copytext_char(se_string, (dna_block * DNA_BLOCK_SIZE) + 1, 0)
return before + injection + after
/datum/mutation/human/proc/set_block(mob/living/carbon/owner, on = 1)
diff --git a/code/datums/ruins/station.dm b/code/datums/ruins/station.dm
index dcab384db9..71cdf495e9 100644
--- a/code/datums/ruins/station.dm
+++ b/code/datums/ruins/station.dm
@@ -35,9 +35,29 @@
suffix = "Box/Engine/engine_tesla.dmm"
name = "Box Tesla"
+/datum/map_template/ruin/station/box/engine/teg
+ id = "engine_teg"
+ suffix = "Box/Engine/engine_teg.dmm"
+ name = "Box TEG"
+
+/datum/map_template/ruin/station/box/engine/empty
+ id = "engine_empty"
+ suffix = "Box/Engine/empty.dmm"
+ name = "Box Empty"
+
+/datum/map_template/ruin/station/box/engine/am
+ id = "engine_am"
+ suffix = "Box/Engine/engine_am.dmm"
+ name = "Box Antimatter"
+
+/datum/map_template/ruin/station/box/engine/budget
+ id = "engine_budget"
+ suffix = "Box/Engine/budget.dmm"
+ name = "Box P.A.C.M.A.N"
+
// Lavaland
// Mining Base
/datum/map_template/ruin/station/lavaland/mining_base
id = "mining_public_01"
suffix = "Lavaland/Mining_Station/Mining_Station_Public_01.dmm"
- name = "Public Mining Base"
\ No newline at end of file
+ name = "Public Mining Base"
diff --git a/code/datums/spawners_menu.dm b/code/datums/spawners_menu.dm
index bb7dc4cdb7..1adcb6fde6 100644
--- a/code/datums/spawners_menu.dm
+++ b/code/datums/spawners_menu.dm
@@ -18,14 +18,18 @@
for(var/spawner in GLOB.mob_spawners)
var/list/this = list()
this["name"] = spawner
- this["desc"] = ""
+ this["short_desc"] = ""
+ this["flavor_text"] = ""
+ this["important_warning"] = ""
this["refs"] = list()
for(var/spawner_obj in GLOB.mob_spawners[spawner])
this["refs"] += "[REF(spawner_obj)]"
if(!this["desc"])
if(istype(spawner_obj, /obj/effect/mob_spawn))
var/obj/effect/mob_spawn/MS = spawner_obj
- this["desc"] = MS.flavour_text
+ this["short_desc"] = MS.short_desc
+ this["flavor_text"] = MS.flavour_text
+ this["important_info"] = MS.important_info
else
var/obj/O = spawner_obj
this["desc"] = O.desc
diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm
index 2e46046a7e..c694f27d64 100644
--- a/code/datums/status_effects/debuffs.dm
+++ b/code/datums/status_effects/debuffs.dm
@@ -33,7 +33,6 @@
if(owner.getStaminaLoss())
owner.adjustStaminaLoss(-0.3) //reduce stamina loss by 0.3 per tick, 6 per 2 seconds
-
//UNCONSCIOUS
/datum/status_effect/incapacitating/unconscious
id = "unconscious"
@@ -80,11 +79,11 @@
desc = "You've fallen asleep. Wait a bit and you should wake up. Unless you don't, considering how helpless you are."
icon_state = "asleep"
-/datum/status_effect/no_combat_mode/
+/datum/status_effect/no_combat_mode
id = "no_combat_mode"
- blocks_combatmode = TRUE
alert_type = null
status_type = STATUS_EFFECT_REPLACE
+ blocks_combatmode = TRUE
/datum/status_effect/no_combat_mode/on_creation(mob/living/new_owner, set_duration)
if(isnum(set_duration))
@@ -113,31 +112,54 @@
icon = 'icons/mob/actions/bloodsucker.dmi'
icon_state = "power_mez"
-/datum/status_effect/no_combat_mode/electrode
+/datum/status_effect/electrode
id = "tased"
+ var/slowdown = 1.5
+ var/slowdown_priority = 50 //to make sure the stronger effect overrides
+ var/affect_crawl = FALSE
+ var/nextmove_modifier = 1
+ var/stamdmg_per_ds = 1 //a 20 duration would do 20 stamdmg, disablers do 24 or something
+ var/last_tick = 0 //fastprocess processing speed is a goddamn sham, don't trust it.
-/datum/status_effect/no_combat_mode/electrode/on_creation(mob/living/new_owner, set_duration)
+/datum/status_effect/electrode/on_creation(mob/living/new_owner, set_duration)
if(isnum(set_duration)) //TODO, figure out how to grab from subtype
duration = set_duration
. = ..()
+ last_tick = world.time
+ if(iscarbon(owner))
+ var/mob/living/carbon/C = owner
+ if(C.combatmode)
+ C.toggle_combat_mode(TRUE)
+ C.add_movespeed_modifier("[MOVESPEED_ID_TASED_STATUS]_[id]", TRUE, priority = slowdown_priority, override = TRUE, multiplicative_slowdown = slowdown, blacklisted_movetypes = affect_crawl? NONE : CRAWLING)
+
+/datum/status_effect/electrode/on_remove()
+ if(iscarbon(owner))
+ var/mob/living/carbon/C = owner
+ C.remove_movespeed_modifier("[MOVESPEED_ID_TASED_STATUS]_[id]")
+ . = ..()
+
+/datum/status_effect/electrode/tick()
+ var/diff = world.time - last_tick
+ if(owner)
+ owner.adjustStaminaLoss(max(0, stamdmg_per_ds * diff)) //if you really want to try to stamcrit someone with a taser alone, you can, but it'll take time and good timing.
+ last_tick = world.time
+
+/datum/status_effect/electrode/nextmove_modifier() //why is this a proc. its no big deal since this doesnt get called often at all but literally w h y
+ return nextmove_modifier
+
+/datum/status_effect/electrode/no_combat_mode
+ id = "tased_strong"
+ slowdown = 8
+ slowdown_priority = 100
+ nextmove_modifier = 2
+ blocks_combatmode = TRUE
+
+/datum/status_effect/electrode/no_combat_mode/on_creation(mob/living/new_owner, set_duration)
+ . = ..()
if(iscarbon(owner))
var/mob/living/carbon/C = owner
if(C.combatmode)
C.toggle_combat_mode(TRUE)
- C.add_movespeed_modifier(MOVESPEED_ID_TASED_STATUS, TRUE, override = TRUE, multiplicative_slowdown = 8)
-
-/datum/status_effect/no_combat_mode/electrode/on_remove()
- if(iscarbon(owner))
- var/mob/living/carbon/C = owner
- C.remove_movespeed_modifier(MOVESPEED_ID_TASED_STATUS)
- . = ..()
-
-/datum/status_effect/no_combat_mode/electrode/tick()
- if(owner)
- owner.adjustStaminaLoss(5) //if you really want to try to stamcrit someone with a taser alone, you can, but it'll take time and good timing.
-
-/datum/status_effect/no_combat_mode/electrode/nextmove_modifier() //why is this a proc. its no big deal since this doesnt get called often at all but literally w h y
- return 2
//OTHER DEBUFFS
/datum/status_effect/his_wrath //does minor damage over time unless holding His Grace
diff --git a/code/datums/traits/_quirk.dm b/code/datums/traits/_quirk.dm
index 12e34b0c90..c6466fdd96 100644
--- a/code/datums/traits/_quirk.dm
+++ b/code/datums/traits/_quirk.dm
@@ -8,6 +8,7 @@
var/gain_text
var/lose_text
var/medical_record_text //This text will appear on medical records for the trait. Not yet implemented
+ var/antag_removal_text // Text will be given to the quirk holder if they get an antag that has it blacklisted.
var/mood_quirk = FALSE //if true, this quirk affects mood and is unavailable if moodlets are disabled
var/mob_trait //if applicable, apply and remove this mob trait
var/mob/living/quirk_holder
@@ -15,6 +16,7 @@
/datum/quirk/New(mob/living/quirk_mob, spawn_effects)
if(!quirk_mob || (human_only && !ishuman(quirk_mob)) || quirk_mob.has_quirk(type))
qdel(src)
+ return
quirk_holder = quirk_mob
SSquirks.quirk_objects += src
to_chat(quirk_holder, gain_text)
diff --git a/code/datums/traits/good.dm b/code/datums/traits/good.dm
index b74f24c469..0d4a6a7b3f 100644
--- a/code/datums/traits/good.dm
+++ b/code/datums/traits/good.dm
@@ -201,13 +201,12 @@
medical_record_text = "Patient's blood tests report an abnormal concentration of red blood cells in their bloodstream."
/datum/quirk/bloodpressure/add()
- var/mob/living/M = quirk_holder
- M.blood_ratio = 1.2
- M.blood_volume += 150
+ quirk_holder.blood_ratio = 1.2
+ quirk_holder.blood_volume += 150
/datum/quirk/bloodpressure/remove()
- var/mob/living/M = quirk_holder
- M.blood_ratio = 1
+ if(quirk_holder)
+ quirk_holder.blood_ratio = 1
/datum/quirk/night_vision
name = "Night Vision"
diff --git a/code/datums/traits/negative.dm b/code/datums/traits/negative.dm
index 7868bad458..9c2128163f 100644
--- a/code/datums/traits/negative.dm
+++ b/code/datums/traits/negative.dm
@@ -6,6 +6,7 @@
value = -2
gain_text = "You feel your vigor slowly fading away."
lose_text = "You feel vigorous again."
+ antag_removal_text = "Your antagonistic nature has removed your blood deficiency."
medical_record_text = "Patient requires regular treatment for blood loss due to low production of blood."
/datum/quirk/blooddeficiency/on_process()
@@ -38,6 +39,8 @@
var/obj/item/heirloom
var/where
+GLOBAL_LIST_EMPTY(family_heirlooms)
+
/datum/quirk/family_heirloom/on_spawn()
var/mob/living/carbon/human/H = quirk_holder
var/obj/item/heirloom_type
@@ -78,6 +81,7 @@
/obj/item/lighter,
/obj/item/dice/d20)
heirloom = new heirloom_type(get_turf(quirk_holder))
+ GLOB.family_heirlooms += heirloom
var/list/slots = list(
"in your left pocket" = SLOT_L_STORE,
"in your right pocket" = SLOT_R_STORE,
@@ -142,9 +146,8 @@
/datum/quirk/nearsighted/on_spawn()
var/mob/living/carbon/human/H = quirk_holder
var/obj/item/clothing/glasses/regular/glasses = new(get_turf(H))
- H.put_in_hands(glasses)
- H.equip_to_slot(glasses, SLOT_GLASSES)
- H.regenerate_icons() //this is to remove the inhand icon, which persists even if it's not in their hands
+ if(!H.equip_to_slot_if_possible(glasses, SLOT_GLASSES))
+ H.put_in_hands(glasses)
/datum/quirk/nyctophobia
name = "Nyctophobia"
@@ -190,11 +193,7 @@
gain_text = "You feel repulsed by the thought of violence!"
lose_text = "You think you can defend yourself again."
medical_record_text = "Patient is unusually pacifistic and cannot bring themselves to cause physical harm."
-
-/datum/quirk/nonviolent/on_process()
- if(quirk_holder.mind && LAZYLEN(quirk_holder.mind.antag_datums))
- to_chat(quirk_holder, "Your antagonistic nature has caused you to renounce your pacifism.")
- qdel(src)
+ antag_removal_text = "Your antagonistic nature has caused you to renounce your pacifism."
/datum/quirk/paraplegic
name = "Paraplegic"
@@ -357,6 +356,7 @@
gain_text = "You find yourself unable to speak!"
lose_text = "You feel a growing strength in your vocal chords."
medical_record_text = "Functionally mute, patient is unable to use their voice in any capacity."
+ antag_removal_text = "Your antagonistic nature has caused your voice to be heard."
var/datum/brain_trauma/severe/mute/mute
/datum/quirk/mute/add()
@@ -368,11 +368,6 @@
var/mob/living/carbon/human/H = quirk_holder
H?.cure_trauma_type(mute, TRAUMA_RESILIENCE_ABSOLUTE)
-/datum/quirk/mute/on_process()
- if(quirk_holder.mind && LAZYLEN(quirk_holder.mind.antag_datums))
- to_chat(quirk_holder, "Your antagonistic nature has caused your voice to be heard.")
- qdel(src)
-
/datum/quirk/unstable
name = "Unstable"
desc = "Due to past troubles, you are unable to recover your sanity if you lose it. Be very careful managing your mood!"
diff --git a/code/datums/verbs.dm b/code/datums/verbs.dm
index 442997288a..39afde14d0 100644
--- a/code/datums/verbs.dm
+++ b/code/datums/verbs.dm
@@ -88,8 +88,8 @@
var/list/entry = list()
entry["parent"] = "[type]"
entry["name"] = verbpath.desc
- if (copytext(verbpath.name,1,2) == "@")
- entry["command"] = copytext(verbpath.name,2)
+ if (verbpath.name[1] == "@")
+ entry["command"] = copytext(verbpath.name, length(verbpath.name[1]) + 1)
else
entry["command"] = replacetext(verbpath.name, " ", "-")
diff --git a/code/datums/weather/weather.dm b/code/datums/weather/weather.dm
index a3b666dcc6..6761ecbb4c 100644
--- a/code/datums/weather/weather.dm
+++ b/code/datums/weather/weather.dm
@@ -50,7 +50,10 @@
stage = STARTUP_STAGE
var/list/affectareas = list()
for(var/V in get_areas(area_type))
- affectareas += V
+ var/area/A = V
+ affectareas |= A
+ if(A.sub_areas)
+ affectareas |= A.sub_areas
for(var/V in protected_areas)
affectareas -= get_areas(V)
for(var/V in affectareas)
@@ -58,7 +61,7 @@
if(A.z in impacted_z_levels)
impacted_areas |= A
weather_duration = rand(weather_duration_lower, weather_duration_upper)
- START_PROCESSING(SSweather, src)
+ START_PROCESSING(SSweather, src) //The reason this doesn't start and stop at main stage is because processing list is also used to see active running weathers (for example, you wouldn't want two ash storms starting at once.)
update_areas()
for(var/M in GLOB.player_list)
var/turf/mob_turf = get_turf(M)
@@ -104,6 +107,14 @@
STOP_PROCESSING(SSweather, src)
update_areas()
+/datum/weather/process()
+ if(aesthetic || (stage != MAIN_STAGE))
+ return
+ for(var/i in GLOB.mob_living_list)
+ var/mob/living/L = i
+ if(can_weather_act(L))
+ weather_act(L)
+
/datum/weather/proc/can_weather_act(mob/living/L) //Can this weather impact a mob?
var/turf/mob_turf = get_turf(L)
if(mob_turf && !(mob_turf.z in impacted_z_levels))
diff --git a/code/datums/wires/_wires.dm b/code/datums/wires/_wires.dm
index fab5a1313c..d4a5dcdeb3 100644
--- a/code/datums/wires/_wires.dm
+++ b/code/datums/wires/_wires.dm
@@ -118,7 +118,7 @@
return TRUE
/datum/wires/proc/is_dud(wire)
- return dd_hasprefix(wire, WIRE_DUD_PREFIX)
+ return findtext(wire, WIRE_DUD_PREFIX)
/datum/wires/proc/is_dud_color(color)
return is_dud(get_wire(color))
@@ -215,8 +215,8 @@
/datum/wires/ui_interact(mob/user, ui_key = "wires", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
- if (!ui)
- ui = new(user, src, ui_key, "wires", "[holder.name] wires", 350, 150 + wires.len * 30, master_ui, state)
+ if(!ui)
+ ui = new(user, src, ui_key, "wires", "[holder.name] Wires", 350, 150 + wires.len * 30, master_ui, state)
ui.open()
/datum/wires/ui_data(mob/user)
diff --git a/code/datums/wires/airalarm.dm b/code/datums/wires/airalarm.dm
index 6eb4dc04db..0c4715e27e 100644
--- a/code/datums/wires/airalarm.dm
+++ b/code/datums/wires/airalarm.dm
@@ -46,7 +46,7 @@
A.mode = 1 // AALARM_MODE_SCRUB
A.apply_mode()
if(WIRE_ALARM) // Clear alarms.
- var/area/AA = get_area(A)
+ var/area/AA = get_base_area(A)
if(AA.atmosalert(0, holder))
A.post_alert(0)
A.update_icon()
@@ -68,7 +68,7 @@
A.mode = 3 // AALARM_MODE_PANIC
A.apply_mode()
if(WIRE_ALARM) // Post alarm.
- var/area/AA = get_area(A)
+ var/area/AA = get_base_area(A)
if(AA.atmosalert(2, holder))
A.post_alert(2)
A.update_icon()
\ No newline at end of file
diff --git a/code/datums/wires/airlock.dm b/code/datums/wires/airlock.dm
index 315cfa59d6..b6699540c3 100644
--- a/code/datums/wires/airlock.dm
+++ b/code/datums/wires/airlock.dm
@@ -52,7 +52,7 @@
/datum/wires/airlock/interactable(mob/user)
var/obj/machinery/door/airlock/A = holder
- if(!issilicon(user) && A.isElectrified() && A.shock(user, 100))
+ if(!A.hasSiliconAccessInArea(user) && A.isElectrified() && A.shock(user, 100))
return FALSE
if(A.panel_open)
return TRUE
diff --git a/code/datums/wires/vending.dm b/code/datums/wires/vending.dm
index 0c66f26a92..114791e873 100644
--- a/code/datums/wires/vending.dm
+++ b/code/datums/wires/vending.dm
@@ -12,7 +12,7 @@
/datum/wires/vending/interactable(mob/user)
var/obj/machinery/vending/V = holder
- if(!issilicon(user) && V.seconds_electrified && V.shock(user, 100))
+ if(!V.hasSiliconAccessInArea(user) && V.seconds_electrified && V.shock(user, 100))
return FALSE
if(V.panel_open)
return TRUE
diff --git a/code/game/area/Space_Station_13_areas.dm b/code/game/area/Space_Station_13_areas.dm
index ad4c0c232d..0732a3d60e 100644
--- a/code/game/area/Space_Station_13_areas.dm
+++ b/code/game/area/Space_Station_13_areas.dm
@@ -1367,3 +1367,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/tcommsat/lounge
name = "Telecommunications Satellite Lounge"
icon_state = "tcomsatlounge"
+
+/area/crew_quarters/fitness/pool
+ name = "Pool Area"
+ icon_state = "pool"
diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm
index 2d256aad27..99b8165306 100644
--- a/code/game/area/areas.dm
+++ b/code/game/area/areas.dm
@@ -63,6 +63,15 @@
var/xenobiology_compatible = FALSE //Can the Xenobio management console transverse this area by default?
var/list/canSmoothWithAreas //typecache to limit the areas that atoms in this area can smooth with
+/**
+ * These two vars allow for multiple unique areas to be linked to a master area
+ * and share some functionalities such as APC powernet nodes, fire alarms etc, without sacrificing
+ * their own flags, statuses, variables and more snowflakes.
+ * Friendly reminder: no map edited areas.
+ */
+ var/list/area/sub_areas //list of typepaths of the areas you wish to link here, will be replaced with a list of references on mapload.
+ var/area/base_area //The area we wish to use in place of src for certain actions such as APC area linking.
+
var/nightshift_public_area = NIGHTSHIFT_AREA_NONE //considered a public area for nightshift
/*Adding a wizard area teleport list because motherfucking lag -- Urist*/
@@ -123,10 +132,35 @@ GLOBAL_LIST_EMPTY(teleportlocs)
reg_in_areas_in_z()
+ //so far I'm only implementing it on mapped unique areas, it's easier this way.
+ if(unique && sub_areas)
+ if(type in sub_areas)
+ WARNING("\"[src]\" typepath found inside its own sub-areas list, please make sure it doesn't share its parent type initial sub-areas value.")
+ sub_areas = null
+ else
+ var/paths = sub_areas.Copy()
+ sub_areas = null
+ for(var/type in paths)
+ var/area/A = GLOB.areas_by_type[type]
+ if(!A) //By chance an area not loaded in the current world, no warning report.
+ continue
+ if(A == src)
+ WARNING("\"[src]\" area a attempted to link with itself.")
+ continue
+ if(A.base_area)
+ WARNING("[src] attempted to link with [A] while the latter is already linked to another area ([A.base_area]).")
+ continue
+ LAZYADD(sub_areas, A)
+ A.base_area = src
+ else if(LAZYLEN(sub_areas))
+ WARNING("sub-areas are currently not supported for non-unique areas such as [src].")
+ sub_areas = null
+
return INITIALIZE_HINT_LATELOAD
/area/LateInitialize()
- power_change() // all machines set to current power level, also updates icon
+ if(!base_area) //we don't want to run it twice.
+ power_change() // all machines set to current power level, also updates icon
/area/proc/reg_in_areas_in_z()
if(contents.len)
@@ -149,6 +183,19 @@ GLOBAL_LIST_EMPTY(teleportlocs)
/area/Destroy()
if(GLOB.areas_by_type[type] == src)
GLOB.areas_by_type[type] = null
+ if(base_area)
+ LAZYREMOVE(base_area, src)
+ base_area = null
+ if(sub_areas)
+ for(var/i in sub_areas)
+ var/area/A = i
+ A.base_area = null
+ sub_areas -= A
+ if(A.requires_power)
+ A.power_light = FALSE
+ A.power_equip = FALSE
+ A.power_environ = FALSE
+ INVOKE_ASYNC(A, .proc/power_change)
STOP_PROCESSING(SSobj, src)
return ..()
@@ -214,9 +261,12 @@ GLOBAL_LIST_EMPTY(teleportlocs)
var/datum/computer_file/program/alarm_monitor/p = item
p.cancelAlarm("Atmosphere", src, source)
- src.atmosalm = danger_level
- return 1
- return 0
+ atmosalm = danger_level
+ for(var/i in sub_areas)
+ var/area/A = i
+ A.atmosalm = danger_level
+ return TRUE
+ return FALSE
/area/proc/ModifyFiredoors(opening)
if(firedoors)
@@ -241,11 +291,8 @@ GLOBAL_LIST_EMPTY(teleportlocs)
return
if (!fire)
- set_fire_alarm_effect()
+ set_fire_alarm_effects(TRUE)
ModifyFiredoors(FALSE)
- for(var/item in firealarms)
- var/obj/machinery/firealarm/F = item
- F.update_icon()
for (var/item in GLOB.alert_consoles)
var/obj/machinery/computer/station_alert/a = item
@@ -264,11 +311,8 @@ GLOBAL_LIST_EMPTY(teleportlocs)
/area/proc/firereset(obj/source)
if (fire)
- unset_fire_alarm_effects()
+ set_fire_alarm_effects(FALSE)
ModifyFiredoors(TRUE)
- for(var/item in firealarms)
- var/obj/machinery/firealarm/F = item
- F.update_icon()
for (var/item in GLOB.silicon_mobs)
var/mob/living/silicon/aiPlayer = item
@@ -300,9 +344,9 @@ GLOBAL_LIST_EMPTY(teleportlocs)
return
//Trigger alarm effect
- set_fire_alarm_effect()
+ set_fire_alarm_effects(TRUE)
//Lockdown airlocks
- for(var/obj/machinery/door/DOOR in src)
+ for(var/obj/machinery/door/DOOR in get_sub_areas_contents(src))
close_and_lock_door(DOOR)
for (var/i in GLOB.silicon_mobs)
@@ -311,23 +355,20 @@ GLOBAL_LIST_EMPTY(teleportlocs)
//Cancel silicon alert after 1 minute
addtimer(CALLBACK(SILICON, /mob/living/silicon.proc/cancelAlarm,"Burglar",src,trigger), 600)
-/area/proc/set_fire_alarm_effect()
- fire = TRUE
+/area/proc/set_fire_alarm_effects(boolean)
+ fire = boolean
+ for(var/i in sub_areas)
+ var/area/A = i
+ A.fire = boolean
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
for(var/alarm in firealarms)
var/obj/machinery/firealarm/F = alarm
F.update_fire_light(fire)
- for(var/obj/machinery/light/L in src)
+ F.update_icon()
+ for(var/obj/machinery/light/L in get_sub_areas_contents(src))
L.update()
-/area/proc/unset_fire_alarm_effects()
- fire = FALSE
- mouse_opacity = MOUSE_OPACITY_TRANSPARENT
- for(var/alarm in firealarms)
- var/obj/machinery/firealarm/F = alarm
- F.update_fire_light(fire)
- for(var/obj/machinery/light/L in src)
- L.update()
+/area/proc/updateicon()
/**
* Update the icon state of the area
*
@@ -380,26 +421,35 @@ GLOBAL_LIST_EMPTY(teleportlocs)
/area/proc/power_change()
for(var/obj/machinery/M in src) // for each machine in the area
M.power_change() // reverify power status (to update icons etc.)
+ if(sub_areas)
+ for(var/i in sub_areas)
+ var/area/A = i
+ A.power_light = power_light
+ A.power_equip = power_equip
+ A.power_environ = power_environ
+ INVOKE_ASYNC(A, .proc/power_change)
update_icon()
/area/proc/usage(chan)
- var/used = 0
switch(chan)
if(LIGHT)
- used += used_light
+ . += used_light
if(EQUIP)
- used += used_equip
+ . += used_equip
if(ENVIRON)
- used += used_environ
+ . += used_environ
if(TOTAL)
- used += used_light + used_equip + used_environ
+ . += used_light + used_equip + used_environ
if(STATIC_EQUIP)
- used += static_equip
+ . += static_equip
if(STATIC_LIGHT)
- used += static_light
+ . += static_light
if(STATIC_ENVIRON)
- used += static_environ
- return used
+ . += static_environ
+ if(sub_areas)
+ for(var/i in sub_areas)
+ var/area/A = i
+ . += A.usage(chan)
/area/proc/addStaticPower(value, powerchannel)
switch(powerchannel)
@@ -414,6 +464,10 @@ GLOBAL_LIST_EMPTY(teleportlocs)
used_equip = 0
used_light = 0
used_environ = 0
+ if(sub_areas)
+ for(var/i in sub_areas)
+ var/area/A = i
+ A.clear_usage()
/area/proc/use_power(amount, chan)
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 1f62be8a24..83117bad64 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -505,9 +505,8 @@
return final_rgb
/atom/proc/clean_blood()
- if(islist(blood_DNA))
- blood_DNA = null
- return TRUE
+ . = blood_DNA? TRUE : FALSE
+ blood_DNA = null
/atom/proc/wash_cream()
return TRUE
@@ -728,6 +727,13 @@
/atom/proc/multitool_act(mob/living/user, obj/item/I)
return
+/atom/proc/multitool_check_buffer(user, obj/item/I, silent = FALSE)
+ if(!istype(I, /obj/item/multitool))
+ if(user && !silent)
+ to_chat(user, "[I] has no data buffer!")
+ return FALSE
+ return TRUE
+
/atom/proc/screwdriver_act(mob/living/user, obj/item/I)
SEND_SIGNAL(src, COMSIG_ATOM_SCREWDRIVER_ACT, user, I)
@@ -761,6 +767,8 @@
log_whisper(log_text)
if(LOG_EMOTE)
log_emote(log_text)
+ if(LOG_SUBTLER)
+ log_subtler(log_text)
if(LOG_DSAY)
log_dsay(log_text)
if(LOG_PDA)
@@ -863,4 +871,4 @@ Proc for attack log creation, because really why not
return TRUE
/atom/proc/intercept_zImpact(atom/movable/AM, levels = 1)
- . |= SEND_SIGNAL(src, COMSIG_ATOM_INTERCEPT_Z_FALL, AM, levels)
\ No newline at end of file
+ . |= SEND_SIGNAL(src, COMSIG_ATOM_INTERCEPT_Z_FALL, AM, levels)
diff --git a/code/game/gamemodes/bloodsucker/bloodsucker.dm b/code/game/gamemodes/bloodsucker/bloodsucker.dm
index ca2ebfe00d..a45e989318 100644
--- a/code/game/gamemodes/bloodsucker/bloodsucker.dm
+++ b/code/game/gamemodes/bloodsucker/bloodsucker.dm
@@ -7,7 +7,7 @@
// LISTS //
var/list/vassal_allowed_antags = list(/datum/antagonist/brother, /datum/antagonist/traitor, /datum/antagonist/traitor/internal_affairs, /datum/antagonist/survivalist, \
- /datum/antagonist/rev, /datum/antagonist/nukeop, /datum/antagonist/pirate, /datum/antagonist/cult, /datum/antagonist/abductee)
+ /datum/antagonist/rev, /datum/antagonist/nukeop, /datum/antagonist/pirate, /datum/antagonist/cult, /datum/antagonist/abductee, /datum/antagonist/valentine, /datum/antagonist/heartbreaker,)
// The antags you're allowed to be if turning Vassal.
/proc/isvamp(mob/living/M)
return istype(M) && M.mind && M.mind.has_antag_datum(/datum/antagonist/bloodsucker)
diff --git a/code/game/gamemodes/brother/traitor_bro.dm b/code/game/gamemodes/brother/traitor_bro.dm
index df4a38cf60..718ed2c103 100644
--- a/code/game/gamemodes/brother/traitor_bro.dm
+++ b/code/game/gamemodes/brother/traitor_bro.dm
@@ -5,6 +5,7 @@
/datum/game_mode/traitor/bros
name = "traitor+brothers"
config_tag = "traitorbro"
+ required_players = 25
restricted_jobs = list("AI", "Cyborg")
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
diff --git a/code/game/gamemodes/clown_ops/clown_weapons.dm b/code/game/gamemodes/clown_ops/clown_weapons.dm
index e443d6ad37..674afad88e 100644
--- a/code/game/gamemodes/clown_ops/clown_weapons.dm
+++ b/code/game/gamemodes/clown_ops/clown_weapons.dm
@@ -57,10 +57,8 @@
/obj/item/melee/transforming/energy/sword/bananium
name = "bananium sword"
desc = "An elegant weapon, for a more civilized age."
- force = 0
- throwforce = 0
- force_on = 0
- throwforce_on = 0
+ force_on = 15
+ throwforce_on = 15
hitsound = null
attack_verb_on = list("slipped")
clumsy_check = FALSE
@@ -69,24 +67,26 @@
heat = 0
light_color = "#ffff00"
var/next_trombone_allowed = 0
+ var/datum/component/slippery/slipper
/obj/item/melee/transforming/energy/sword/bananium/Initialize()
. = ..()
- AddComponent(/datum/component/slippery, 60, GALOSHES_DONT_HELP)
- var/datum/component/slippery/slipper = GetComponent(/datum/component/slippery)
+ slipper = LoadComponent(/datum/component/slippery, 81, GALOSHES_DONT_HELP)
slipper.signal_enabled = active
/obj/item/melee/transforming/energy/sword/bananium/attack(mob/living/M, mob/living/user)
..()
if(active)
- var/datum/component/slippery/slipper = GetComponent(/datum/component/slippery)
- slipper.Slip(M)
+ slipper.lube_flags |= FLYING_DOESNT_HELP|SLIP_WHEN_CRAWLING
+ slipper.Slip(src, M)
+ slipper.lube_flags &= ~(FLYING_DOESNT_HELP|SLIP_WHEN_CRAWLING)
/obj/item/melee/transforming/energy/sword/bananium/throw_impact(atom/hit_atom, throwingdatum)
. = ..()
if(active)
- var/datum/component/slippery/slipper = GetComponent(/datum/component/slippery)
- slipper.Slip(hit_atom)
+ slipper.lube_flags |= FLYING_DOESNT_HELP|SLIP_WHEN_CRAWLING
+ slipper.Slip(src, hit_atom)
+ slipper.lube_flags &= ~(FLYING_DOESNT_HELP|SLIP_WHEN_CRAWLING)
/obj/item/melee/transforming/energy/sword/bananium/attackby(obj/item/I, mob/living/user, params)
if((world.time > next_trombone_allowed) && istype(I, /obj/item/melee/transforming/energy/sword/bananium))
@@ -98,7 +98,6 @@
/obj/item/melee/transforming/energy/sword/bananium/transform_weapon(mob/living/user, supress_message_text)
..()
- var/datum/component/slippery/slipper = GetComponent(/datum/component/slippery)
slipper.signal_enabled = active
/obj/item/melee/transforming/energy/sword/bananium/ignition_effect(atom/A, mob/user)
@@ -108,8 +107,9 @@
if(!active)
transform_weapon(user, TRUE)
user.visible_message("[user] is [pick("slitting [user.p_their()] stomach open with", "falling on")] [src]! It looks like [user.p_theyre()] trying to commit seppuku, but the blade slips off of [user.p_them()] harmlessly!")
- var/datum/component/slippery/slipper = GetComponent(/datum/component/slippery)
- slipper.Slip(user)
+ slipper.lube_flags |= FLYING_DOESNT_HELP|SLIP_WHEN_CRAWLING
+ slipper.Slip(src, user)
+ slipper.lube_flags &= ~(FLYING_DOESNT_HELP|SLIP_WHEN_CRAWLING)
return SHAME
//BANANIUM SHIELD
@@ -126,16 +126,15 @@
on_force = 0
on_throwforce = 0
on_throw_speed = 1
+ var/datum/component/slippery/slipper
/obj/item/shield/energy/bananium/Initialize()
. = ..()
- AddComponent(/datum/component/slippery, 60, GALOSHES_DONT_HELP)
- var/datum/component/slippery/slipper = GetComponent(/datum/component/slippery)
+ slipper = LoadComponent(/datum/component/slippery, 81, GALOSHES_DONT_HELP)
slipper.signal_enabled = active
/obj/item/shield/energy/bananium/attack_self(mob/living/carbon/human/user)
..()
- var/datum/component/slippery/slipper = GetComponent(/datum/component/slippery)
slipper.signal_enabled = active
/obj/item/shield/energy/bananium/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback)
@@ -149,8 +148,9 @@
if(active)
var/caught = hit_atom.hitby(src, FALSE, FALSE, throwingdatum=throwingdatum)
if(iscarbon(hit_atom) && !caught)//if they are a carbon and they didn't catch it
- var/datum/component/slippery/slipper = GetComponent(/datum/component/slippery)
- slipper.Slip(hit_atom)
+ slipper.lube_flags |= FLYING_DOESNT_HELP|SLIP_WHEN_CRAWLING
+ slipper.Slip(src, hit_atom)
+ slipper.lube_flags &= ~(FLYING_DOESNT_HELP|SLIP_WHEN_CRAWLING)
if(thrownby && !caught)
throw_at(thrownby, throw_range+2, throw_speed, null, 1)
else
@@ -203,24 +203,35 @@
clumsy_check = GRENADE_NONCLUMSY_FUMBLE
/obj/item/grenade/chem_grenade/teargas/moustache/prime()
- var/myloc = get_turf(src)
+ var/list/check_later = list()
+ for(var/mob/living/carbon/C in get_turf(src))
+ check_later += C
. = ..()
- for(var/mob/living/carbon/M in view(6, myloc))
- if(!istype(M.wear_mask, /obj/item/clothing/mask/gas/clown_hat) && !istype(M.wear_mask, /obj/item/clothing/mask/gas/mime) )
- if(!M.wear_mask || M.dropItemToGround(M.wear_mask))
+ if(!.) //grenade did not properly prime.
+ return
+ for(var/M in check_later)
+ var/mob/living/carbon/C = M
+ if(!istype(C.wear_mask, /obj/item/clothing/mask/gas/clown_hat) && !istype(C.wear_mask, /obj/item/clothing/mask/gas/mime))
+ if(!C.wear_mask || C.dropItemToGround(C.wear_mask))
var/obj/item/clothing/mask/fakemoustache/sticky/the_stash = new /obj/item/clothing/mask/fakemoustache/sticky()
- M.equip_to_slot_or_del(the_stash, SLOT_WEAR_MASK, TRUE, TRUE, TRUE, TRUE)
+ C.equip_to_slot_or_del(the_stash, SLOT_WEAR_MASK, TRUE, TRUE, TRUE, TRUE)
/obj/item/clothing/mask/fakemoustache/sticky
- var/unstick_time = 600
+ var/unstick_time = 2 MINUTES
/obj/item/clothing/mask/fakemoustache/sticky/Initialize()
. = ..()
ADD_TRAIT(src, TRAIT_NODROP, STICKY_MOUSTACHE_TRAIT)
- addtimer(CALLBACK(src, .proc/unstick), unstick_time)
+ addtimer(TRAIT_CALLBACK_REMOVE(src, TRAIT_NODROP, STICKY_MOUSTACHE_TRAIT), unstick_time)
-/obj/item/clothing/mask/fakemoustache/sticky/proc/unstick()
- ADD_TRAIT(src, TRAIT_NODROP, STICKY_MOUSTACHE_TRAIT)
+/obj/item/clothing/mask/fakemoustache/sticky/equipped(mob/user, slot)
+ . = ..()
+ if(slot == SLOT_WEAR_MASK)
+ ADD_TRAIT(user, TRAIT_NO_INTERNALS, STICKY_MOUSTACHE_TRAIT)
+
+/obj/item/clothing/mask/fakemoustache/sticky/dropped(mob/user)
+ . = ..()
+ REMOVE_TRAIT(user, TRAIT_NO_INTERNALS, STICKY_MOUSTACHE_TRAIT)
//DARK H.O.N.K. AND CLOWN MECH WEAPONS
@@ -268,7 +279,6 @@
internals_req_access = list(ACCESS_SYNDICATE)
wreckage = /obj/structure/mecha_wreckage/honker/dark
max_equip = 3
- spawn_tracked = FALSE
/obj/mecha/combat/honker/dark/GrantActions(mob/living/user, human_occupant = 0)
..()
diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm
index 821c819664..6ea357163f 100644
--- a/code/game/gamemodes/dynamic/dynamic.dm
+++ b/code/game/gamemodes/dynamic/dynamic.dm
@@ -41,7 +41,7 @@ GLOBAL_LIST_EMPTY(dynamic_forced_roundstart_ruleset)
// Forced threat level, setting this to zero or higher forces the roundstart threat to the value.
GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
-GLOBAL_VAR_INIT(dynamic_storyteller_type, null)
+GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
/datum/game_mode/dynamic
name = "dynamic mode"
@@ -239,12 +239,20 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, null)
. += "Peaceful Waypoint "
. += "Your station orbits deep within controlled, core-sector systems and serves as a waypoint for routine traffic through Nanotrasen's trade empire. Due to the combination of high security, interstellar traffic, and low strategic value, it makes any direct threat of violence unlikely. Your primary enemies will be incompetence and bored crewmen: try to organize team-building events to keep staffers interested and productive. However, even deep in our territory there may be subversive elements, especially for such a high-value target as your station. Keep an eye out, but don't expect much trouble."
set_security_level(SEC_LEVEL_GREEN)
+ for(var/T in subtypesof(/datum/station_goal))
+ var/datum/station_goal/G = new T
+ if(!(G in station_goals))
+ station_goals += G
if(21 to 79)
var/perc_green = 100-round(100*((threat_level-21)/(79-21)))
if(prob(perc_green))
. += "Core Territory "
. += "Your station orbits within reliably mundane, secure space. Although Nanotrasen has a firm grip on security in your region, the valuable resources and strategic position aboard your station make it a potential target for infiltrations. Monitor crew for non-loyal behavior, but expect a relatively tame shift free of large-scale destruction. We expect great things from your station."
set_security_level(SEC_LEVEL_GREEN)
+ for(var/T in subtypesof(/datum/station_goal))
+ var/datum/station_goal/G = new T
+ if(!(G in station_goals))
+ station_goals += G
else if(prob(perc_green))
. += "Contested System "
. += "Your station's orbit passes along the edge of Nanotrasen's sphere of influence. While subversive elements remain the most likely threat against your station, hostile organizations are bolder here, where our grip is weaker. Exercise increased caution against elite Syndicate strike forces, or Executives forbid, some kind of ill-conceived unionizing attempt."
@@ -273,7 +281,7 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, null)
if(GLOB.security_level >= SEC_LEVEL_BLUE)
priority_announce("A summary has been copied and printed to all communications consoles.", "Security level elevated.", "intercept")
else
- priority_announce("Thanks to the tireless efforts of our security and intelligence divisions, there are currently no likely threats to [station_name()]. Have a secure shift!", "Security Report", "commandreport")
+ priority_announce("Thanks to the tireless efforts of our security and intelligence divisions, there are currently no likely threats to [station_name()]. All station construction projects have been authorized. Have a secure shift!", "Security Report", "commandreport")
// Yes, this is copy pasted from game_mode
/datum/game_mode/dynamic/check_finished(force_ending)
@@ -346,7 +354,8 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, null)
generate_threat()
storyteller.start_injection_cooldowns()
-
+ SSevents.frequency_lower = storyteller.event_frequency_lower // 6 minutes by default
+ SSevents.frequency_upper = storyteller.event_frequency_upper // 20 minutes by default
log_game("DYNAMIC: Dynamic Mode initialized with a Threat Level of... [threat_level]!")
initial_threat_level = threat_level
return TRUE
@@ -395,7 +404,7 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, null)
/datum/game_mode/dynamic/post_setup(report)
update_playercounts()
-
+
for(var/datum/dynamic_ruleset/roundstart/rule in executed_rules)
addtimer(CALLBACK(src, /datum/game_mode/dynamic/.proc/execute_roundstart_rule, rule), rule.delay)
..()
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm
index f4e9971a05..f6755057e2 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm
+++ b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm
@@ -47,6 +47,8 @@
assigned += M.mind
M.mind.special_role = antag_flag
M.mind.add_antag_datum(antag_datum)
+ log_admin("[M.name] was made into a [name] by dynamic.")
+ message_admins("[M.name] was made into a [name] by dynamic.")
return TRUE
//////////////////////////////////////////////
@@ -72,12 +74,6 @@
property_weights = list("story_potential" = 2, "trust" = -1, "extended" = 1)
always_max_weight = TRUE
-/datum/dynamic_ruleset/latejoin/infiltrator/execute()
- . = ..()
- for(var/datum/mind/M in assigned)
- log_admin("[M.name] was made into a traitor by dynamic.")
- message_admins("[M.name] was made into a traitor by dynamic.")
-
//////////////////////////////////////////////
// //
// REVOLUTIONARY PROVOCATEUR //
@@ -225,3 +221,25 @@
log_admin("[M.name] was made into a bloodsucker by dynamic.")
message_admins("[M.name] was made into a bloodsucker by dynamic.")
return TRUE
+
+//////////////////////////////////////////////
+// //
+// COLLECTOR //
+// //
+//////////////////////////////////////////////
+
+/datum/dynamic_ruleset/latejoin/collector
+ name = "Contraband Collector"
+ config_tag = "latejoin_collector"
+ antag_datum = /datum/antagonist/collector
+ antag_flag = ROLE_MINOR_ANTAG
+ restricted_roles = list("AI", "Cyborg")
+ protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
+ required_candidates = 1
+ weight = 5
+ cost = 1
+ requirements = list(10,10,10,10,10,10,10,10,10,10)
+ high_population_requirement = 10
+ repeatable = TRUE
+ flags = TRAITOR_RULESET
+ property_weights = list("story_potential" = 2, "trust" = -1, "extended" = 2)
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
index 92a02f5920..7ad4cee884 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
+++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
@@ -61,7 +61,7 @@
trimmed_list.Remove(M)
continue
if (M.mind)
- if (restrict_ghost_roles && M.mind.assigned_role in GLOB.exp_specialmap[EXP_TYPE_SPECIAL]) // Are they playing a ghost role?
+ if (restrict_ghost_roles && (M.mind.assigned_role in GLOB.exp_specialmap[EXP_TYPE_SPECIAL])) // Are they playing a ghost role?
trimmed_list.Remove(M)
continue
if (M.mind.assigned_role in restricted_roles) // Does their job allow it?
@@ -494,7 +494,7 @@
high_population_requirement = 50
repeatable_weight_decrease = 2
repeatable = TRUE
- property_weights = list("story_potential" = 1, "trust" = 1, "extended" = 1, "valid" = 2, "integrity" = 2)
+ property_weights = list("story_potential" = 1, "trust" = 1, "extended" = 1, "valid" = 2, "integrity" = 1)
var/list/spawn_locs = list()
/datum/dynamic_ruleset/midround/from_ghosts/nightmare/execute()
diff --git a/code/game/gamemodes/dynamic/dynamic_storytellers.dm b/code/game/gamemodes/dynamic/dynamic_storytellers.dm
index afa6ed8ba7..069c73a421 100644
--- a/code/game/gamemodes/dynamic/dynamic_storytellers.dm
+++ b/code/game/gamemodes/dynamic/dynamic_storytellers.dm
@@ -1,5 +1,6 @@
/datum/dynamic_storyteller
var/name = "none"
+ var/config_tag = null
var/desc = "A coder's idiocy."
var/list/property_weights = list()
var/curve_centre = 0
@@ -7,6 +8,8 @@
var/forced_threat_level = -1
var/flags = 0
var/weight = 3 // how many rounds need to have been recently played for this storyteller to be left out of the vote
+ var/event_frequency_lower = 6 MINUTES
+ var/event_frequency_upper = 20 MINUTES
var/datum/game_mode/dynamic/mode = null
/**
@@ -20,14 +23,6 @@ Property weights are:
"conversion" -- Basically a bool. Conversion antags, well, convert. It's its own class for a good reason.
*/
-/datum/dynamic_storyteller/New()
- ..()
- if (istype(SSticker.mode, /datum/game_mode/dynamic))
- mode = SSticker.mode
- GLOB.dynamic_curve_centre = curve_centre
- GLOB.dynamic_curve_width = curve_width
- GLOB.dynamic_forced_threat_level = forced_threat_level
-
/datum/dynamic_storyteller/proc/start_injection_cooldowns()
var/latejoin_injection_cooldown_middle = 0.5*(GLOB.dynamic_first_latejoin_delay_max + GLOB.dynamic_first_latejoin_delay_min)
mode.latejoin_injection_cooldown = round(CLAMP(EXP_DISTRIBUTION(latejoin_injection_cooldown_middle), GLOB.dynamic_first_latejoin_delay_min, GLOB.dynamic_first_latejoin_delay_max)) + world.time
@@ -42,7 +37,29 @@ Property weights are:
return
/datum/dynamic_storyteller/proc/on_start()
- return
+ if (istype(SSticker.mode, /datum/game_mode/dynamic))
+ mode = SSticker.mode
+ GLOB.dynamic_curve_centre = curve_centre
+ GLOB.dynamic_curve_width = curve_width
+ if(flags & USE_PREF_WEIGHTS)
+ var/voters = 0
+ var/mean = 0
+ for(var/client/c in GLOB.clients)
+ var/vote = c.prefs.preferred_chaos
+ if(vote)
+ voters += 1
+ switch(vote)
+ if(CHAOS_NONE)
+ mean -= 5
+ if(CHAOS_LOW)
+ mean -= 2.5
+ if(CHAOS_HIGH)
+ mean += 2.5
+ if(CHAOS_MAX)
+ mean += 5
+ if(voters)
+ GLOB.dynamic_curve_centre += (mean/voters)
+ GLOB.dynamic_forced_threat_level = forced_threat_level
/datum/dynamic_storyteller/proc/get_midround_cooldown()
var/midround_injection_cooldown_middle = 0.5*(GLOB.dynamic_midround_delay_max + GLOB.dynamic_midround_delay_min)
@@ -154,12 +171,15 @@ Property weights are:
/datum/dynamic_storyteller/cowabunga
name = "Chaotic"
+ config_tag = "chaotic"
curve_centre = 10
desc = "Chaos: high. Variation: high. Likely antags: clock cult, revs, wizard."
property_weights = list("extended" = -1, "chaos" = 10)
- weight = 2
+ weight = 1
+ event_frequency_lower = 2 MINUTES
+ event_frequency_upper = 10 MINUTES
flags = WAROPS_ALWAYS_ALLOWED
- var/refund_cooldown
+ var/refund_cooldown = 0
/datum/dynamic_storyteller/cowabunga/get_midround_cooldown()
return ..() / 4
@@ -169,12 +189,13 @@ Property weights are:
/datum/dynamic_storyteller/cowabunga/do_process()
if(refund_cooldown < world.time)
- mode.refund_threat(10)
- mode.log_threat("Cowabunga it is. Refunded 10 threat. Threat is now [mode.threat].")
- refund_cooldown = world.time + 300 SECONDS
+ mode.refund_threat(20)
+ mode.log_threat("Cowabunga it is. Refunded 20 threat. Threat is now [mode.threat].")
+ refund_cooldown = world.time + 600 SECONDS
/datum/dynamic_storyteller/team
name = "Teamwork"
+ config_tag = "teamwork"
desc = "Chaos: high. Variation: low. Likely antags: nukies, clockwork cult, wizard, blob, xenomorph."
curve_centre = 2
curve_width = 1.5
@@ -187,6 +208,7 @@ Property weights are:
/datum/dynamic_storyteller/conversion
name = "Conversion"
+ config_tag = "conversion"
desc = "Chaos: high. Variation: medium. Likely antags: cults, bloodsuckers, revs."
curve_centre = 3
curve_width = 1
@@ -196,24 +218,33 @@ Property weights are:
/datum/dynamic_storyteller/classic
name = "Random"
+ config_tag = "random"
desc = "Chaos: varies. Variation: highest. No special weights attached."
weight = 6
+ flags = USE_PREF_WEIGHTS
curve_width = 4
/datum/dynamic_storyteller/memes
name = "Story"
+ config_tag = "story"
desc = "Chaos: varies. Variation: high. Likely antags: abductors, nukies, wizard, traitor."
+ weight = 4
+ flags = USE_PREF_WEIGHTS
curve_width = 4
property_weights = list("story_potential" = 10)
/datum/dynamic_storyteller/suspicion
name = "Intrigue"
+ config_tag = "intrigue"
desc = "Chaos: low. Variation: high. Likely antags: traitor, bloodsucker. Rare: revs, blood cult."
+ weight = 4
+ flags = USE_PREF_WEIGHTS
curve_width = 4
property_weights = list("trust" = -5)
/datum/dynamic_storyteller/liteextended
name = "Calm"
+ config_tag = "calm"
desc = "Chaos: low. Variation: medium. Likely antags: bloodsuckers, traitors, sentient disease, revenant."
curve_centre = -5
curve_width = 0.5
@@ -226,10 +257,12 @@ Property weights are:
/datum/dynamic_storyteller/extended
name = "Extended"
+ config_tag = "extended"
desc = "Chaos: none. Variation: none. Likely antags: none."
curve_centre = -20
- weight = 2
+ weight = 0
curve_width = 0.5
/datum/dynamic_storyteller/extended/on_start()
+ ..()
GLOB.dynamic_forced_extended = TRUE
diff --git a/code/game/gamemodes/events.dm b/code/game/gamemodes/events.dm
index d010da465e..b296ba7273 100644
--- a/code/game/gamemodes/events.dm
+++ b/code/game/gamemodes/events.dm
@@ -12,7 +12,7 @@
var/list/skipped_areas = list(/area/engine/engineering, /area/engine/supermatter, /area/engine/atmospherics_engine, /area/ai_monitored/turret_protected/ai)
for(var/area/A in world)
- if( !A.requires_power || A.always_unpowered )
+ if( !A.requires_power || A.always_unpowered || A.base_area)
continue
var/skip = 0
@@ -61,8 +61,9 @@
S.output_attempt = 1
S.update_icon()
S.power_change()
+
for(var/area/A in world)
- if(!istype(A, /area/space) && !istype(A, /area/shuttle) && !istype(A, /area/arrival))
+ if(!istype(A, /area/space) && !istype(A, /area/shuttle) && !istype(A, /area/arrival) && !A.always_unpowered && !A.base_area)
A.power_light = TRUE
A.power_equip = TRUE
A.power_environ = TRUE
diff --git a/code/game/gamemodes/gangs/dominator.dm b/code/game/gamemodes/gangs/dominator.dm
index 8c1272dcbd..db060a6539 100644
--- a/code/game/gamemodes/gangs/dominator.dm
+++ b/code/game/gamemodes/gangs/dominator.dm
@@ -101,7 +101,7 @@
playsound(loc, 'sound/items/timer.ogg', 10, 0)
if(!warned && (time_remaining < 180))
warned = TRUE
- var/area/domloc = get_area(loc)
+ var/area/domloc = get_base_area(loc)
gang.message_gangtools("Less than 3 minutes remains in hostile takeover. Defend your dominator at [domloc.map_name]!")
for(var/G in GLOB.gangs)
var/datum/team/gang/tempgang = G
@@ -179,7 +179,7 @@
if((tempgang.domination_time != NOT_DOMINATING) || !tempgang.dom_attempts || !in_range(src, user) || !isturf(loc))
return 0
- var/area/A = get_area(loc)
+ var/area/A = get_base_area(loc)
var/locname = A.map_name
gang = tempgang
diff --git a/code/game/gamemodes/gangs/gang_decals.dm b/code/game/gamemodes/gangs/gang_decals.dm
index 6e5cb58891..7aaed769d9 100644
--- a/code/game/gamemodes/gangs/gang_decals.dm
+++ b/code/game/gamemodes/gangs/gang_decals.dm
@@ -19,7 +19,7 @@
return INITIALIZE_HINT_QDEL
gang = G
var/newcolor = G.color
- var/area/territory = get_area(src)
+ var/area/territory = get_base_area(src)
icon_state = G.name
G.new_territories |= list(territory.type = territory.name)
//If this isn't tagged by a specific gangster there's no bonus income.
@@ -27,7 +27,7 @@
/obj/effect/decal/cleanable/crayon/gang/Destroy()
if(gang)
- var/area/territory = get_area(src)
+ var/area/territory = get_base_area(src)
gang.territories -= territory.type
gang.new_territories -= territory.type
gang.lost_territories |= list(territory.type = territory.name)
diff --git a/code/game/gamemodes/gangs/gang_items.dm b/code/game/gamemodes/gangs/gang_items.dm
index 2e9ca4dcc0..4dba8dae93 100644
--- a/code/game/gamemodes/gangs/gang_items.dm
+++ b/code/game/gamemodes/gangs/gang_items.dm
@@ -290,12 +290,6 @@ datum/gang_item/clothing/shades //Addition: Why not have cool shades on a gang m
cost = 5
item_path = /obj/item/grenade/syndieminibomb/concussion/frag
-/datum/gang_item/equipment/stimpack
- name = "Black Market Stimulants"
- id = "stimpack"
- cost = 12
- item_path = /obj/item/reagent_containers/syringe/stimulants
-
/datum/gang_item/equipment/implant_breaker
name = "Implant Breaker"
id = "implant_breaker"
@@ -401,7 +395,7 @@ datum/gang_item/equipment/gangsheild
return "This device requires a 5x5 area clear of walls to FUNCTION. (Estimated Takeover Time: [round(gang.determine_domination_time()/60,0.1)] minutes)"
/datum/gang_item/equipment/dominator/purchase(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
- var/area/userarea = get_area(user)
+ var/area/userarea = get_base_area(user)
if(!(userarea.type in gang.territories|gang.new_territories))
to_chat(user,"The dominator can be spawned only on territory controlled by your gang!")
return FALSE
diff --git a/code/game/gamemodes/gangs/gangs.dm b/code/game/gamemodes/gangs/gangs.dm
index 9151107d6f..0dc4a520ef 100644
--- a/code/game/gamemodes/gangs/gangs.dm
+++ b/code/game/gamemodes/gangs/gangs.dm
@@ -6,7 +6,8 @@ GLOBAL_LIST_EMPTY(gangs)
name = "gang war"
config_tag = "gang"
antag_flag = ROLE_GANG
- restricted_jobs = list("Security Officer", "Warden", "Detective", "AI", "Cyborg","Captain", "Head of Personnel", "Head of Security")
+ restricted_jobs = list("AI", "Cyborg")
+ protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
required_players = 15
required_enemies = 0
recommended_enemies = 2
diff --git a/code/game/gamemodes/meteor/meteors.dm b/code/game/gamemodes/meteor/meteors.dm
index 6d681f8108..cfb4c9a120 100644
--- a/code/game/gamemodes/meteor/meteors.dm
+++ b/code/game/gamemodes/meteor/meteors.dm
@@ -21,16 +21,16 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event
//Meteor spawning global procs
///////////////////////////////
-/proc/spawn_meteors(number = 10, list/meteortypes)
+/proc/spawn_meteors(number = 10, list/meteortypes, dir)
for(var/i = 0; i < number; i++)
- spawn_meteor(meteortypes)
+ spawn_meteor(meteortypes, dir)
-/proc/spawn_meteor(list/meteortypes)
+/proc/spawn_meteor(list/meteortypes, dir)
var/turf/pickedstart
var/turf/pickedgoal
var/max_i = 10//number of tries to spawn meteor.
while(!isspaceturf(pickedstart))
- var/startSide = pick(GLOB.cardinals)
+ var/startSide = dir || pick(GLOB.cardinals)
var/startZ = pick(SSmapping.levels_by_trait(ZTRAIT_STATION))
pickedstart = spaceDebrisStartLoc(startSide, startZ)
pickedgoal = spaceDebrisFinishLoc(startSide, startZ)
diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm
index f99fe5c3e4..e84b942151 100644
--- a/code/game/gamemodes/objective.dm
+++ b/code/game/gamemodes/objective.dm
@@ -9,9 +9,10 @@ GLOBAL_LIST_EMPTY(objectives)
var/explanation_text = "Nothing" //What that person is supposed to do.
var/team_explanation_text //For when there are multiple owners.
var/datum/mind/target = null //If they are focused on a particular person.
- var/target_amount = 0 //If they are focused on a particular number. Steal objectives have their own counter.
- var/completed = 0 //currently only used for custom objectives.
- var/martyr_compatible = 0 //If the objective is compatible with martyr objective, i.e. if you can still do it while dead.
+ var/target_amount = FALSE //If they are focused on a particular number. Steal objectives have their own counter.
+ var/completed = FALSE //currently only used for custom objectives.
+ var/completable = TRUE //Whether this objective shows greentext when completed
+ var/martyr_compatible = FALSE //If the objective is compatible with martyr objective, i.e. if you can still do it while dead.
/datum/objective/New(var/text)
GLOB.objectives += src // CITADEL EDIT FOR CRYOPODS
@@ -172,6 +173,26 @@ GLOBAL_LIST_EMPTY(objectives)
/datum/objective/assassinate/admin_edit(mob/admin)
admin_simple_target_pick(admin)
+/datum/objective/assassinate/once
+ name = "kill once"
+ var/won = FALSE
+
+/datum/objective/assassinate/once/update_explanation_text()
+ ..()
+ if(target && target.current)
+ explanation_text = "Kill [target.name], the [!target_role_type ? target.assigned_role : target.special_role]. You only need to kill them once; if they come back, you've still succeeded."
+ START_PROCESSING(SSprocessing,src)
+ else
+ explanation_text = "Free Objective"
+
+/datum/objective/assassinate/once/check_completion()
+ return won || ..()
+
+/datum/objective/assassinate/once/process()
+ won = check_completion()
+ if(won)
+ STOP_PROCESSING(SSprocessing,src)
+
/datum/objective/assassinate/internal
var/stolen = 0 //Have we already eliminated this target?
@@ -293,9 +314,11 @@ GLOBAL_LIST_EMPTY(objectives)
/datum/objective/hijack
name = "hijack"
- explanation_text = "Hijack the shuttle to ensure no loyalist Nanotrasen crew escape alive and out of custody."
- team_explanation_text = "Hijack the shuttle to ensure no loyalist Nanotrasen crew escape alive and out of custody. Leave no team member behind."
+ explanation_text = "Hijack the emergency shuttle by hacking its navigational protocols through the control console (alt click emergency shuttle console)."
+ team_explanation_text = "Hijack the emergency shuttle by hacking its navigational protocols through the control console (alt click emergency shuttle console). Leave no team member behind."
martyr_compatible = 0 //Technically you won't get both anyway.
+ /// Overrides the hijack speed of any antagonist datum it is on ONLY, no other datums are impacted.
+ var/hijack_speed_override = 1
/datum/objective/hijack/check_completion() // Requires all owners to escape.
if(SSshuttle.emergency.mode != SHUTTLE_ENDGAME)
@@ -365,6 +388,28 @@ GLOBAL_LIST_EMPTY(objectives)
return FALSE
return TRUE
+/datum/objective/breakout
+ name = "breakout"
+ martyr_compatible = 1
+ var/target_role_type = 0
+ var/human_check = TRUE
+
+/datum/objective/breakout/check_completion()
+ return !target || considered_escaped(target)
+
+/datum/objective/breakout/find_target_by_role(role, role_type=0, invert=0)
+ if(!invert)
+ target_role_type = role_type
+ ..()
+ return target
+
+/datum/objective/breakout/update_explanation_text()
+ ..()
+ if(target && target.current)
+ explanation_text = "Make sure [target.name], the [!target_role_type ? target.assigned_role : target.special_role] escapes on the shuttle or an escape pod alive and without being in custody."
+ else
+ explanation_text = "Free Objective"
+
/datum/objective/escape/escape_with_identity
name = "escape with identity"
var/target_real_name // Has to be stored because the target's real_name can change over the course of the round
@@ -527,7 +572,7 @@ GLOBAL_LIST_EMPTY(possible_items)
else if(targetinfo.check_special_completion(I))//Returns 1 by default. Items with special checks will return 1 if the conditions are fulfilled.
return TRUE
- if(targetinfo && I.type in targetinfo.altitems) //Ok, so you don't have the item. Do you have an alternative, at least?
+ if(targetinfo && (I.type in targetinfo.altitems)) //Ok, so you don't have the item. Do you have an alternative, at least?
if(targetinfo.check_special_completion(I))//Yeah, we do! Don't return 0 if we don't though - then you could fail if you had 1 item that didn't pass and got checked first!
return TRUE
return FALSE
@@ -582,7 +627,6 @@ GLOBAL_LIST_EMPTY(possible_items_special)
explanation_text = "Do not give up or lose [targetinfo.name]."
steal_target = targetinfo.targetitem
-
/datum/objective/download
name = "download"
@@ -786,40 +830,10 @@ GLOBAL_LIST_EMPTY(possible_items_special)
/datum/objective/destroy/internal
var/stolen = FALSE //Have we already eliminated this target?
-/datum/objective/steal_five_of_type
- name = "steal five of"
- explanation_text = "Steal at least five items!"
- var/list/wanted_items = list(/obj/item)
-
-/datum/objective/steal_five_of_type/New()
- ..()
- wanted_items = typecacheof(wanted_items)
-
-/datum/objective/steal_five_of_type/summon_guns
- name = "steal guns"
- explanation_text = "Steal at least five guns!"
- wanted_items = list(/obj/item/gun)
-
-/datum/objective/steal_five_of_type/summon_magic
- name = "steal magic"
- explanation_text = "Steal at least five magical artefacts!"
- wanted_items = list(/obj/item/spellbook, /obj/item/gun/magic, /obj/item/clothing/suit/space/hardsuit/wizard, /obj/item/scrying, /obj/item/antag_spawner/contract, /obj/item/necromantic_stone)
-
-/datum/objective/steal_five_of_type/check_completion()
- var/list/datum/mind/owners = get_owners()
- var/stolen_count = 0
- for(var/datum/mind/M in owners)
- if(!isliving(M.current))
- continue
- var/list/all_items = M.current.GetAllContents() //this should get things in cheesewheels, books, etc.
- for(var/obj/I in all_items) //Check for wanted items
- if(is_type_in_typecache(I, wanted_items))
- stolen_count++
- return stolen_count >= 5
-
//Created by admin tools
/datum/objective/custom
name = "custom"
+ completable = FALSE
/datum/objective/custom/admin_edit(mob/admin)
var/expl = stripped_input(admin, "Custom objective:", "Objective", explanation_text)
@@ -997,4 +1011,147 @@ GLOBAL_LIST_EMPTY(possible_items_special)
command_staff_only = TRUE
+/datum/objective/hoard
+ name = "hoard"
+ var/obj/item/hoarded_item = null
+/datum/objective/hoard/get_target()
+ return hoarded_item
+
+/datum/objective/hoard/proc/set_target(obj/item/I)
+ if(I)
+ hoarded_item = I
+ explanation_text = "Keep [I] on your person at all times."
+ return hoarded_item
+ else
+ explanation_text = "Free objective"
+ return
+
+/datum/objective/hoard/check_completion()
+ var/list/datum/mind/owners = get_owners()
+ if(!hoarded_item)
+ return TRUE
+ for(var/datum/mind/M in owners)
+ if(!isliving(M.current))
+ continue
+
+ var/list/all_items = M.current.GetAllContents() //this should get things in cheesewheels, books, etc.
+
+ for(var/obj/I in all_items) //Check for items
+ if(I == hoarded_item)
+ return TRUE
+ return FALSE
+
+/datum/objective/hoard/heirloom
+ name = "steal heirloom"
+
+/datum/objective/hoard/heirloom/find_target()
+ set_target(pick(GLOB.family_heirlooms))
+
+GLOBAL_LIST_EMPTY(traitor_contraband)
+
+GLOBAL_LIST_EMPTY(cult_contraband)
+
+/datum/objective/hoard/collector
+ name = "Hoard contraband"
+
+/datum/objective/collector/New()
+ ..()
+ if(!GLOB.traitor_contraband.len)//Only need to fill the list when it's needed.
+ GLOB.traitor_contraband = list(/obj/item/card/emag/empty,/obj/item/clothing/glasses/phantomthief,/obj/item/clothing/gloves/chameleon/broken)
+ if(!GLOB.cult_contraband.len)
+ GLOB.cult_contraband = list(/obj/item/clockwork/slab,/obj/item/clockwork/component/belligerent_eye,/obj/item/clockwork/component/belligerent_eye/lens_gem,/obj/item/shuttle_curse,/obj/item/cult_shift)
+
+/datum/objective/hoard/collector/find_target()
+ var/obj/item/I
+ var/I_type
+ if(prob(50))
+ I_type = pick_n_take(GLOB.traitor_contraband) // always unique unless it's run out, in which case we refill it anyway
+ else
+ I_type = pick_n_take(GLOB.cult_contraband)
+ I = new I_type
+ I.forceMove(get_turf(owner))
+ if(ishuman(owner))
+ var/mob/living/carbon/human/H = owner
+ H.equip_in_one_of_slots(I, list("backpack" = SLOT_IN_BACKPACK))
+ hoarded_item = I
+
+
+
+GLOBAL_LIST_EMPTY(possible_sabotages)
+// For saboteurs. Go in and cause some trouble somewhere. Not necessarily breaking things, just sufficiently troublemaking.
+/datum/objective/sabotage
+ name = "sabotage"
+ var/datum/sabotage_objective/targetinfo = null //composition > inheritance.
+
+/datum/objective/sabotage/get_target()
+ return targetinfo.sabotage_type
+
+/datum/objective/sabotage/New()
+ ..()
+ if(!GLOB.possible_sabotages.len)//Only need to fill the list when it's needed.
+ for(var/I in subtypesof(/datum/sabotage_objective))
+ new I
+
+/datum/objective/sabotage/find_target()
+ var/list/datum/mind/owners = get_owners()
+ var/approved_targets = list()
+ check_sabotages:
+ for(var/datum/sabotage_objective/possible_sabotage in GLOB.possible_sabotages)
+ if(!is_unique_objective(possible_sabotage.sabotage_type) || possible_sabotage.check_conditions() || !possible_sabotage.can_run())
+ continue
+ for(var/datum/mind/M in owners)
+ if(M.current.mind.assigned_role in possible_sabotage.excludefromjob)
+ continue check_sabotages
+ approved_targets += possible_sabotage
+ return set_target(safepick(approved_targets))
+
+/datum/objective/sabotage/proc/set_target(datum/sabotage_objective/sabo)
+ if(sabo)
+ targetinfo = sabo
+ explanation_text = "[targetinfo.name]"
+ give_special_equipment(targetinfo.special_equipment)
+ return sabo
+ else
+ explanation_text = "Free objective"
+ return
+
+/datum/objective/sabotage/check_completion()
+ return targetinfo.check_conditions()
+
+/datum/objective/flavor
+ name = "flavor"
+ completable = FALSE
+ var/flavor_file
+
+/datum/objective/flavor/proc/get_flavor_list()
+ return world.file2list(flavor_file)
+
+/datum/objective/flavor/proc/forge_objective()
+ var/flavor_list = get_flavor_list()
+ explanation_text = pick(flavor_list)
+
+/datum/objective/flavor/traitor
+ name = "traitor flavor"
+ flavor_file = "strings/flavor_objectives/traitor.txt"
+
+/datum/objective/flavor/traitor/get_flavor_list()
+ . = ..()
+ switch(owner.assigned_role)
+ if("Station Engineer", "Atmospheric Technician")
+ . += world.file2list("strings/flavor_objectives/traitor/engineering.txt")
+ if("Medical Doctor","Chemist","Virologist","Geneticist")
+ . += world.file2list("strings/flavor_objectives/traitor/medical.txt")
+ if("Scientist","Roboticist","Geneticist")
+ . += world.file2list("strings/flavor_objectives/traitor/science.txt")
+ if("Assistant")
+ . += world.file2list("strings/flavor_objectives/traitor/assistant.txt")
+
+/datum/objective/flavor/ninja_helping
+ flavor_file = "strings/flavor_objectives/ninja_helping.txt"
+
+/datum/objective/flavor/ninja_syndie
+ flavor_file = "strings/flavor_objectives/ninja_syndie.txt"
+
+/datum/objective/flavor/wizard
+ flavor_file = "strings/flavor_objectives/wizard.txt"
diff --git a/code/game/gamemodes/objective_sabotage.dm b/code/game/gamemodes/objective_sabotage.dm
new file mode 100644
index 0000000000..1094dd2f36
--- /dev/null
+++ b/code/game/gamemodes/objective_sabotage.dm
@@ -0,0 +1,113 @@
+/datum/sabotage_objective
+ var/name = "Free Objective"
+ var/sabotage_type = "nothing"
+ var/special_equipment = list()
+ var/list/excludefromjob = list()
+
+/datum/sabotage_objective/New()
+ ..()
+ if(sabotage_type!="nothing")
+ GLOB.possible_sabotages += src
+
+/datum/sabotage_objective/proc/check_conditions()
+ return TRUE
+
+/datum/sabotage_objective/proc/can_run()
+ return TRUE
+
+/datum/sabotage_objective/processing
+ var/won = FALSE
+
+/datum/sabotage_objective/processing/New()
+ ..()
+ START_PROCESSING(SSprocessing, src)
+
+/datum/sabotage_objective/processing/proc/check_condition_processing()
+ return 100
+
+/datum/sabotage_objective/processing/process()
+ check_condition_processing()
+ if(won >= 100)
+ STOP_PROCESSING(SSprocessing,src)
+
+/datum/sabotage_objective/processing/check_conditions()
+ return won
+
+/datum/sabotage_objective/processing/power_sink
+ name = "Drain at least 1 gigajoule of power using a power sink."
+ sabotage_type = "powersink"
+ special_equipment = list(/obj/item/powersink)
+ var/sink_found = FALSE
+ var/count = 0
+
+/datum/sabotage_objective/processing/power_sink/check_condition_processing()
+ count += 1
+ if(count==10 || sink_found) // doesn't need to fire that often unless a sink exists
+ var/sink_found_this_time = FALSE
+ for(var/datum/powernet/PN in GLOB.powernets)
+ for(var/obj/item/powersink/sink in PN.nodes)
+ sink_found_this_time = TRUE
+ won = max(won,sink.power_drained/1e9)
+ sink_found = sink_found_this_time
+ count = 0
+ return FALSE
+
+/obj/item/paper/guides/antag/supermatter_sabotage
+ info = "Ways to sabotage a supermatter: \
+
\
+
Set the air alarm's operating mode to anything that isn't 'draught' (yes, anything, though 'off' works best). Or just smash the air alarm, that works too.
\
+
Wrench a pipe (the junction to the cold loop is most effective, but some setups will robust through this no issue; best to try for multiple)
\
+
Pump in as much carbon dioxide, oxygen, plasma or tritium as you can find (this will likely also cause a singularity or tesla delamination, so watch out!)
\
+
Unset the filters on the cooling loop, or, perhaps more insidious, set them to oxygen/plasma.
\
+
Deactivate the digital valve that sends the exhaust gases to space (note: only works on box station; others you must unwrench).
\
+
There are many other ways; be creative!
\
+
"
+
+/datum/sabotage_objective/processing/supermatter
+ name = "Sabotage the supermatter so that it goes under 50% integrity. If it is delaminated, you will fail."
+ sabotage_type = "supermatter"
+ special_equipment = list(/obj/item/paper/guides/antag/supermatter_sabotage)
+ var/list/supermatters = list()
+ excludefromjob = list("Chief Engineer", "Station Engineer", "Atmospheric Technician")
+
+/datum/sabotage_objective/processing/supermatter/check_condition_processing()
+ if(!supermatters.len)
+ supermatters = list()
+ for(var/obj/machinery/power/supermatter_crystal/S in GLOB.machines)
+ // Delaminating, not within coverage, not on a tile.
+ if (!isturf(S.loc) || !(is_station_level(S.z) || is_mining_level(S.z)))
+ continue
+ supermatters.Add(S)
+ for(var/obj/machinery/power/supermatter_crystal/S in supermatters) // you can win this with a wishgranter... lol.
+ won = max(1-((S.get_integrity()-50)/50),won)
+ return FALSE
+
+/datum/sabotage_objective/processing/supermatter/can_run()
+ return (locate(/obj/machinery/power/supermatter_crystal) in GLOB.machines)
+
+/datum/sabotage_objective/station_integrity
+ name = "Make sure the station is at less than 80% integrity by the end. Smash walls, windows etc. to reach this goal."
+ sabotage_type = "integrity"
+
+/datum/sabotage_objective/station_integrity/check_conditions()
+ return 5-(max(SSticker.station_integrity*4,320)/80)
+
+/datum/sabotage_objective/cloner
+ name = "Destroy all Nanotrasen cloning machines."
+ sabotage_type = "cloner"
+
+/datum/sabotage_objective/cloner/check_conditions()
+ return !(locate(/obj/machinery/clonepod) in GLOB.machines)
+
+/datum/sabotage_objective/ai_law
+ name = "Upload a hacked law to the AI."
+ sabotage_type = "ailaw"
+ special_equipment = list(/obj/item/aiModule/syndicate)
+ excludefromjob = list("Chief Engineer","Research Director","Head of Personnel","Captain","Chief Medical Officer","Head Of Security")
+
+/datum/sabotage_objective/ai_law/check_conditions()
+ for (var/i in GLOB.ai_list)
+ var/mob/living/silicon/ai/aiPlayer = i
+ if(aiPlayer.mind && length(aiPlayer.laws.hacked))
+ return TRUE
+ return FALSE
diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm
index b6dc09eb59..19a20f0cfa 100644
--- a/code/game/machinery/Sleeper.dm
+++ b/code/game/machinery/Sleeper.dm
@@ -182,7 +182,7 @@
/obj/machinery/sleeper/AltClick(mob/user)
. = ..()
- if(!user.canUseTopic(src, !issilicon(user)))
+ if(!user.canUseTopic(src, !hasSiliconAccessInArea(user)))
return
if(state_open)
close_machine()
@@ -264,7 +264,7 @@
if(blood_id)
data["occupant"]["blood"] = list() // We can start populating this list.
var/blood_type = C.dna.blood_type
- if(blood_id != "blood") // special blood substance
+ if(!(blood_id in GLOB.blood_reagent_types)) // special blood substance
var/datum/reagent/R = GLOB.chemical_reagents_list[blood_id]
if(R)
blood_type = R.name
diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm
index 5f44fccdac..7132f046e6 100644
--- a/code/game/machinery/_machinery.dm
+++ b/code/game/machinery/_machinery.dm
@@ -113,6 +113,11 @@ Class Procs:
var/atom/movable/occupant = null
var/speed_process = FALSE // Process as fast as possible?
var/obj/item/circuitboard/circuit // Circuit to be created and inserted when the machinery is created
+ // For storing and overriding ui id and dimensions
+ var/tgui_id // ID of TGUI interface
+ var/ui_style // ID of custom TGUI style (optional)
+ var/ui_x
+ var/ui_y
var/interaction_flags_machine = INTERACT_MACHINE_WIRES_IF_OPEN | INTERACT_MACHINE_ALLOW_SILICON | INTERACT_MACHINE_OPEN_SILICON | INTERACT_MACHINE_SET_MACHINE
@@ -224,7 +229,7 @@ Class Procs:
return !(stat & (NOPOWER|BROKEN|MAINT))
/obj/machinery/can_interact(mob/user)
- var/silicon = issiliconoradminghost(user)
+ var/silicon = hasSiliconAccessInArea(user) || IsAdminGhost(user)
if((stat & (NOPOWER|BROKEN)) && !(interaction_flags_machine & INTERACT_MACHINE_OFFLINE))
return FALSE
if(panel_open && !(interaction_flags_machine & INTERACT_MACHINE_OPEN))
diff --git a/code/game/machinery/announcement_system.dm b/code/game/machinery/announcement_system.dm
index 56227cdd53..5a597f7280 100644
--- a/code/game/machinery/announcement_system.dm
+++ b/code/game/machinery/announcement_system.dm
@@ -105,7 +105,7 @@ GLOBAL_LIST_EMPTY(announcement_systems)
/obj/machinery/announcement_system/ui_interact(mob/user)
. = ..()
- if(!user.canUseTopic(src, !issilicon(user)))
+ if(!user.canUseTopic(src, !hasSiliconAccessInArea(user)))
return
if(stat & BROKEN)
visible_message("[src] buzzes.", "You hear a faint buzz.")
@@ -123,7 +123,7 @@ GLOBAL_LIST_EMPTY(announcement_systems)
/obj/machinery/announcement_system/Topic(href, href_list)
if(..())
return
- if(!usr.canUseTopic(src, !issilicon(usr)))
+ if(!usr.canUseTopic(src, !hasSiliconAccessInArea(usr)))
return
if(stat & BROKEN)
visible_message("[src] buzzes.", "You hear a faint buzz.")
@@ -132,13 +132,13 @@ GLOBAL_LIST_EMPTY(announcement_systems)
if(href_list["ArrivalTopic"])
var/NewMessage = stripped_input(usr, "Enter in the arrivals announcement configuration.", "Arrivals Announcement Config", arrival)
- if(!usr.canUseTopic(src, !issilicon(usr)))
+ if(!usr.canUseTopic(src, !hasSiliconAccessInArea(usr)))
return
if(NewMessage)
arrival = NewMessage
else if(href_list["NewheadTopic"])
var/NewMessage = stripped_input(usr, "Enter in the departmental head announcement configuration.", "Head Departmental Announcement Config", newhead)
- if(!usr.canUseTopic(src, !issilicon(usr)))
+ if(!usr.canUseTopic(src, !hasSiliconAccessInArea(usr)))
return
if(NewMessage)
newhead = NewMessage
@@ -157,7 +157,7 @@ GLOBAL_LIST_EMPTY(announcement_systems)
. = attack_ai(user)
/obj/machinery/announcement_system/attack_ai(mob/user)
- if(!user.canUseTopic(src, !issilicon(user)))
+ if(!user.canUseTopic(src, !hasSiliconAccessInArea(user)))
return
if(stat & BROKEN)
to_chat(user, "[src]'s firmware appears to be malfunctioning!")
diff --git a/code/game/machinery/autolathe.dm b/code/game/machinery/autolathe.dm
index 495e90d305..c9d26905f5 100644
--- a/code/game/machinery/autolathe.dm
+++ b/code/game/machinery/autolathe.dm
@@ -17,6 +17,7 @@
var/list/L = list()
var/list/LL = list()
var/hacked = FALSE
+ var/hackable = TRUE
var/disabled = 0
var/shocked = FALSE
var/hack_wire
@@ -371,6 +372,8 @@
/obj/machinery/autolathe/proc/adjust_hacked(state)
hacked = state
+ if(!hackable && hacked)
+ return
for(var/id in SSresearch.techweb_designs)
var/datum/design/D = SSresearch.techweb_design_by_id(id)
if((D.build_type & AUTOLATHE) && ("hacked" in D.category))
@@ -383,6 +386,12 @@
. = ..()
adjust_hacked(TRUE)
+/obj/machinery/autolathe/secure
+ name = "secured autolathe"
+ desc = "An autolathe reprogrammed with security protocols to prevent hacking."
+ hackable = FALSE
+ circuit = /obj/item/circuitboard/machine/autolathe/secure
+
//Called when the object is constructed by an autolathe
//Has a reference to the autolathe so you can do !!FUN!! things with hacked lathes
/obj/item/proc/autolathe_crafted(obj/machinery/autolathe/A)
diff --git a/code/game/machinery/bank_machine.dm b/code/game/machinery/bank_machine.dm
index 0b9ed6bb3f..41867a8520 100644
--- a/code/game/machinery/bank_machine.dm
+++ b/code/game/machinery/bank_machine.dm
@@ -46,34 +46,37 @@
new /obj/item/stack/spacecash/c200(drop_location()) // will autostack
playsound(src.loc, 'sound/items/poster_being_created.ogg', 100, 1)
SSshuttle.points -= 200
- if(next_warning < world.time && prob(15))
- var/area/A = get_area(loc)
- var/message = "Unauthorized credit withdrawal underway in [A.map_name]!!"
- radio.talk_into(src, message, radio_channel)
- next_warning = world.time + minimum_time_between_warnings
+ if(next_warning < world.time && prob(15))
+ var/area/A = get_area(loc)
+ var/message = "Unauthorized credit withdrawal underway in [A.map_name]!!"
+ radio.talk_into(src, message, radio_channel)
+ next_warning = world.time + minimum_time_between_warnings
-/obj/machinery/computer/bank_machine/ui_interact(mob/user)
- . = ..()
- var/dat = "[station_name()] secure vault. Authorized personnel only. "
- dat += "Current Balance: [SSshuttle.points] credits. "
- if(!siphoning)
- dat += "Siphon Credits "
- else
- dat += "Halt Credit Siphon "
+/obj/machinery/computer/bank_machine/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)
+ if(!ui)
+ ui = new(user, src, ui_key, "bank_machine", name, 320, 165, master_ui, state)
+ ui.open()
- dat += "Close"
+/obj/machinery/computer/bank_machine/ui_data(mob/user)
+ var/list/data = list()
+ data["current_balance"] = SSshuttle.points
+ data["siphoning"] = siphoning
+ data["station_name"] = station_name()
- var/datum/browser/popup = new(user, "computer", "Bank Vault", 300, 200)
- popup.set_content("
"
- t += "O" //up-left
- t += "^" //up
- t += "O " //up-right
- t += "<"//left
- t += "R"//reset to 0
- t += "> "//right
- t += "O"//down-left
- t += "v"//down
- t += "O "//down-right
- t += " "
- t += "
"
if (cartridge.access & CART_NEWSCASTER)
@@ -635,13 +641,13 @@ GLOBAL_LIST_EMPTY(PDAs)
if("Clear")//Clears messages
tnote = null
if("Ringtone")
- var/t = input(U, "Please enter new ringtone", name, ttone) as text
+ var/t = stripped_input(U, "Please enter new ringtone", name, ttone, 20)
if(in_range(src, U) && loc == U && t)
if(SEND_SIGNAL(src, COMSIG_PDA_CHANGE_RINGTONE, U, t) & COMPONENT_STOP_RINGTONE_CHANGE)
U << browse(null, "window=pda")
return
else
- ttone = copytext(sanitize(t), 1, 20)
+ ttone = t
else
U << browse(null, "window=pda")
return
@@ -715,7 +721,7 @@ GLOBAL_LIST_EMPTY(PDAs)
return
/obj/item/pda/proc/remove_id(mob/user)
- if(issilicon(user) || !user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
+ if(hasSiliconAccessInArea(user) || !user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
return
do_remove_id(user)
@@ -753,7 +759,6 @@ GLOBAL_LIST_EMPTY(PDAs)
return
if((last_text && world.time < last_text + 10) || (everyone && last_everyone && world.time < last_everyone + PDA_SPAM_DELAY))
return
- var/emoji_message = emoji_parse(message)
if(prob(1))
message += "\nSent from my PDA"
// Send the signal
@@ -782,7 +787,7 @@ GLOBAL_LIST_EMPTY(PDAs)
"job" = "[ownjob]",
"message" = message,
"targets" = string_targets,
- "emoji_message" = emoji_message
+ "emojis" = allow_emojis
))
if (picture)
signal.data["photo"] = picture
@@ -796,15 +801,18 @@ GLOBAL_LIST_EMPTY(PDAs)
playsound(src, 'sound/machines/terminal_error.ogg', 15, 1)
var/target_text = signal.format_target()
+ if(allow_emojis)
+ message = emoji_parse(message)//already sent- this just shows the sent emoji as one to the sender in the to_chat
+ signal.data["message"] = emoji_parse(signal.data["message"])
// Log it in our logs
tnote += "→ To [target_text]: [signal.format_message()] "
// Show it to ghosts
- var/ghost_message = "[owner] PDA Message --> [target_text]: [signal.format_message(TRUE)]"
+ var/ghost_message = "[owner] PDA Message --> [target_text]: [signal.format_message()]"
for(var/i in GLOB.dead_mob_list)
var/mob/M = i
if(M?.client && M.client.prefs.chat_toggles & CHAT_GHOSTPDA)
to_chat(M, "[FOLLOW_LINK(M, user)] [ghost_message]")
- to_chat(user, "Message sent to [target_text]: \"[emoji_message]\"")
+ to_chat(user, "Message sent to [target_text]: \"[message]\"")
// Log in the talk log
user.log_talk(message, LOG_PDA, tag="PDA: [initial(name)] to [target_text] (BLOCKED:[string_blocked])")
if (!silent)
@@ -835,7 +843,11 @@ GLOBAL_LIST_EMPTY(PDAs)
hrefstart = ""
hrefend = ""
- to_chat(L, "[icon2html(src)] Message from [hrefstart][signal.data["name"]] ([signal.data["job"]])[hrefend], [signal.format_message(TRUE)] (Reply) (BLOCK/UNBLOCK)")
+ var/inbound_message = signal.format_message()
+ if(signal.data["emojis"] == TRUE)//so will not parse emojis as such from pdas that don't send emojis
+ inbound_message = emoji_parse(inbound_message)
+
+ to_chat(L, "[icon2html(src)] Message from [hrefstart][signal.data["name"]] ([signal.data["job"]])[hrefend], [inbound_message] (Reply) (BLOCK/UNBLOCK)")
update_icon(TRUE)
@@ -904,7 +916,7 @@ GLOBAL_LIST_EMPTY(PDAs)
remove_pen()
/obj/item/pda/proc/toggle_light()
- if(issilicon(usr) || !usr.canUseTopic(src, BE_CLOSE))
+ if(hasSiliconAccessInArea(usr) || !usr.canUseTopic(src, BE_CLOSE))
return
if(fon)
fon = FALSE
@@ -916,7 +928,7 @@ GLOBAL_LIST_EMPTY(PDAs)
/obj/item/pda/proc/remove_pen()
- if(issilicon(usr) || !usr.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
+ if(hasSiliconAccessInArea(usr) || !usr.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
return
if(inserted_item)
diff --git a/code/game/objects/items/devices/PDA/PDA_types.dm b/code/game/objects/items/devices/PDA/PDA_types.dm
index 2da47d481e..b604bec258 100644
--- a/code/game/objects/items/devices/PDA/PDA_types.dm
+++ b/code/game/objects/items/devices/PDA/PDA_types.dm
@@ -19,6 +19,26 @@
if(istype(cart) && cart.charges < 5)
cart.charges++
+//Mime PDA sends "silent" messages.
+/obj/item/pda/mime
+ name = "mime PDA"
+ default_cartridge = /obj/item/cartridge/virus/mime
+ inserted_item = /obj/item/toy/crayon/mime
+ icon_state = "pda-mime"
+ desc = "A portable microcomputer by Thinktronic Systems, LTD. The hardware has been modified for compliance with the vows of silence."
+ silent = TRUE
+ ttone = "silence"
+
+/obj/item/pda/mime/msg_input(mob/living/U = usr)
+ if(emped || toff)
+ return
+ var/emojis = emoji_sanitize(stripped_input(U, "Please enter emojis", name))
+ if(!emojis)
+ return
+ if(!U.canUseTopic(src, BE_CLOSE))
+ return
+ return emojis
+
// Special AI/pAI PDAs that cannot explode.
/obj/item/pda/ai
icon = null
@@ -79,14 +99,6 @@
icon_state = "pda-science"
ttone = "boom"
-/obj/item/pda/mime
- name = "mime PDA"
- default_cartridge = /obj/item/cartridge/virus/mime
- inserted_item = /obj/item/toy/crayon/mime
- icon_state = "pda-mime"
- silent = TRUE
- ttone = "silence"
-
/obj/item/pda/heads
default_cartridge = /obj/item/cartridge/head
icon_state = "pda-hop"
diff --git a/code/game/objects/items/devices/PDA/cart.dm b/code/game/objects/items/devices/PDA/cart.dm
index e4fa241907..1006fe9146 100644
--- a/code/game/objects/items/devices/PDA/cart.dm
+++ b/code/game/objects/items/devices/PDA/cart.dm
@@ -100,7 +100,8 @@
bot_access_flags = CLEAN_BOT
/obj/item/cartridge/lawyer
- name = "\improper P.R.O.V.E. cartridge"
+ name = "\improper S.P.A.M. cartridge"
+ desc = "Introducing the Station Public Announcement Messenger cartridge, featuring the unique ability to broadcast-mark messages, designed for lawyers across Nanotrasen to advertise their useful and important services."
icon_state = "cart-law"
access = CART_SECURITY
spam_enabled = 1
@@ -309,9 +310,14 @@ Code:
var/list/S = list(" Off","AOff"," On", " AOn")
var/list/chg = list("N","C","F")
+//Neither copytext nor copytext_char is appropriate here; neither 30 UTF-8 code units nor 30 code points equates to 30 columns of output.
+//Some glyphs are very tall or very wide while others are small or even take up no space at all.
+//Emojis can take modifiers which are many characters but render as only one glyph.
+//A proper solution here (as far as Unicode goes, maybe not ideal as far as markup goes, a table would be better)
+//would be to use [A.area.name]
for(var/obj/machinery/power/apc/A in L)
- menu += copytext(add_tspace(A.area.name, 30), 1, 30)
- menu += " [S[A.equipment+1]] [S[A.lighting+1]] [S[A.environ+1]] [add_lspace(DisplayPower(A.lastused_total), 6)] [A.cell ? "[add_lspace(round(A.cell.percent()), 3)]% [chg[A.charging+1]]" : " N/C"] "
+ menu += copytext_char(add_trailing(A.area.name, 30, " "), 1, 30)
+ menu += " [S[A.equipment+1]] [S[A.lighting+1]] [S[A.environ+1]] [add_leading(DisplayPower(A.lastused_total), 6, " ")] [A.cell ? "[add_leading(round(A.cell.percent()), 3, " ")]% [chg[A.charging+1]]" : " N/C"] "
menu += ""
@@ -326,7 +332,7 @@ Code:
if(active1 in GLOB.data_core.general)
menu += "Name: [active1.fields["name"]] ID: [active1.fields["id"]] "
- menu += "Sex: [active1.fields["sex"]] "
+ menu += "Sex: [active1.fields["gender"]] "
menu += "Age: [active1.fields["age"]] "
menu += "Rank: [active1.fields["rank"]] "
menu += "Fingerprint: [active1.fields["fingerprint"]] "
@@ -370,7 +376,7 @@ Code:
if(active1 in GLOB.data_core.general)
menu += "Name: [active1.fields["name"]] ID: [active1.fields["id"]] "
- menu += "Sex: [active1.fields["sex"]] "
+ menu += "Sex: [active1.fields["gender"]] "
menu += "Age: [active1.fields["age"]] "
menu += "Rank: [active1.fields["rank"]] "
menu += "Fingerprint: [active1.fields["fingerprint"]] "
@@ -557,28 +563,44 @@ Code:
if (53) // Newscaster
menu = "
[PDAIMG(notes)] Newscaster Access
"
menu += " Current Newsfeed: [current_channel ? current_channel : "None"] "
- var/datum/newscaster/feed_channel/current
- for(var/datum/newscaster/feed_channel/chan in GLOB.news_network.network_channels)
+ var/datum/news/feed_channel/current
+ for(var/datum/news/feed_channel/chan in GLOB.news_network.network_channels)
if (chan.channel_name == current_channel)
current = chan
if(!current)
menu += "
ERROR : NO CHANNEL FOUND
"
return
var/i = 1
- for(var/datum/newscaster/feed_message/msg in current.messages)
+ for(var/datum/news/feed_message/msg in current.messages)
menu +="-[msg.returnBody(-1)] \[Story by [msg.returnAuthor(-1)]\] "
menu +="[msg.comments.len] comment[msg.comments.len > 1 ? "s" : ""] "
if(msg.img)
user << browse_rsc(msg.img, "tmp_photo[i].png")
menu +=" "
i++
- for(var/datum/newscaster/feed_comment/comment in msg.comments)
+ for(var/datum/news/feed_comment/comment in msg.comments)
menu +="[comment.body] [comment.author] [comment.time_stamp] "
menu += " Post Message"
if (54) // Beepsky, Medibot, Floorbot, and Cleanbot access
menu = "
[PDAIMG(medbot)] Bots Interlink
"
bot_control()
+ if (55) // Emoji Guidebook for mimes
+ menu = "
"
+ emoji_table = collate.Join()
+
+ menu += " To use an emoji in a pda message, refer to the guide and add \":\" around the emoji. Your PDA supports the following emoji: "
+ menu += emoji_table
+
if (99) //Newscaster message permission error
menu = "
ERROR : NOT AUTHORIZED [host_pda.id ? "" : "- ID SLOT EMPTY"]
"
@@ -587,7 +609,7 @@ Code:
/obj/item/cartridge/Topic(href, href_list)
..()
- if(!usr.canUseTopic(src, !issilicon(usr)))
+ if(!usr.canUseTopic(src, !hasSiliconAccessInArea(usr)))
usr.unset_machine()
usr << browse(null, "window=pda")
return
@@ -660,8 +682,8 @@ Code:
if("Newscaster Message")
var/host_pda_owner_name = host_pda.id ? "[host_pda.id.registered_name] ([host_pda.id.assignment])" : "Unknown"
var/message = host_pda.msg_input()
- var/datum/newscaster/feed_channel/current
- for(var/datum/newscaster/feed_channel/chan in GLOB.news_network.network_channels)
+ var/datum/news/feed_channel/current
+ for(var/datum/news/feed_channel/chan in GLOB.news_network.network_channels)
if (chan.channel_name == current_channel)
current = chan
if(current.locked && current.author != host_pda_owner_name)
@@ -679,6 +701,11 @@ Code:
return
playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
+ //emoji previews
+ if(href_list["emoji"])
+ var/parse = emoji_parse(":[href_list["emoji"]]:")
+ to_chat(usr, parse)
+
//Bot control section! Viciously ripped from radios for being laggy and terrible.
if(href_list["op"])
switch(href_list["op"])
diff --git a/code/game/objects/items/devices/chameleonproj.dm b/code/game/objects/items/devices/chameleonproj.dm
index c4ffa9f0ff..e87987600a 100644
--- a/code/game/objects/items/devices/chameleonproj.dm
+++ b/code/game/objects/items/devices/chameleonproj.dm
@@ -49,6 +49,10 @@
return
if(istype(target, /obj/structure/falsewall))
return
+ if(target.alpha != 255)
+ return
+ if(target.invisibility != 0)
+ return
if(iseffect(target))
if(!(istype(target, /obj/effect/decal))) //be a footprint
return
diff --git a/code/game/objects/items/devices/compressionkit.dm b/code/game/objects/items/devices/compressionkit.dm
index 6e62c389c7..9c7e8ce185 100644
--- a/code/game/objects/items/devices/compressionkit.dm
+++ b/code/game/objects/items/devices/compressionkit.dm
@@ -46,7 +46,7 @@
var/list/organs = M.getorganszone("head") + M.getorganszone("eyes") + M.getorganszone("mouth")
for(var/internal_organ in organs)
var/obj/item/organ/I = internal_organ
- I.Remove(M)
+ I.Remove()
I.forceMove(T)
head.drop_limb()
qdel(head)
@@ -89,25 +89,6 @@
else
to_chat(user, "Anomalous error. Summon a coder.")
- else if(ishuman(target) && user.zone_selected == BODY_ZONE_PRECISE_GROIN)
- var/mob/living/carbon/human/H = target
- var/obj/item/organ/genital/penis/P = H.getorganslot(ORGAN_SLOT_PENIS)
- if(!P)
- return
- playsound(get_turf(src), 'sound/weapons/flash.ogg', 50, 1)
- H.visible_message("[user] is preparing to shrink [H]\'s [P.name] with their bluespace compression kit!")
- if(do_mob(user, H, 40) && charges > 0 && P.length > 0)
- H.visible_message("[user] has shrunk [H]\'s [P.name]!")
- playsound(get_turf(src), 'sound/weapons/emitter2.ogg', 50, 1)
- sparks()
- flash_lighting_fx(3, 3, LIGHT_COLOR_CYAN)
- charges -= 1
- var/p_name = P.name
- P.modify_size(-5)
- if(QDELETED(P))
- H.visible_message("[H]\'s [p_name] vanishes!")
-
-
/obj/item/compressionkit/attackby(obj/item/I, mob/user, params)
..()
if(istype(I, /obj/item/stack/ore/bluespace_crystal))
@@ -117,4 +98,4 @@
if(B.amount > 1)
B.amount -= 1
else
- qdel(I)
\ No newline at end of file
+ qdel(I)
diff --git a/code/game/objects/items/devices/desynchronizer.dm b/code/game/objects/items/devices/desynchronizer.dm
index ff58af2405..0fa557f666 100644
--- a/code/game/objects/items/devices/desynchronizer.dm
+++ b/code/game/objects/items/devices/desynchronizer.dm
@@ -14,6 +14,7 @@
var/last_use = 0
var/next_use = 0
var/obj/effect/abstract/sync_holder/sync_holder
+ var/resync_timer
/obj/item/desynchronizer/attack_self(mob/living/user)
if(world.time < next_use)
@@ -56,16 +57,20 @@
SEND_SIGNAL(AM, COMSIG_MOVABLE_SECLUDED_LOCATION)
last_use = world.time
icon_state = "desynchronizer-on"
- addtimer(CALLBACK(src, .proc/resync), duration)
+ resync_timer = addtimer(CALLBACK(src, .proc/resync), duration , TIMER_STOPPABLE)
/obj/item/desynchronizer/proc/resync()
new /obj/effect/temp_visual/desynchronizer(sync_holder.drop_location())
QDEL_NULL(sync_holder)
+ if(resync_timer)
+ deltimer(resync_timer)
+ resync_timer = null
icon_state = initial(icon_state)
next_use = world.time + (world.time - last_use) // Could be 2*world.time-last_use but that would just be confusing
/obj/item/desynchronizer/Destroy()
- resync()
+ if(sync_holder)
+ resync()
return ..()
/obj/effect/abstract/sync_holder
diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm
index a82f99064e..e43aa681ce 100644
--- a/code/game/objects/items/devices/flashlight.dm
+++ b/code/game/objects/items/devices/flashlight.dm
@@ -51,7 +51,7 @@
/obj/item/flashlight/attack(mob/living/carbon/M, mob/living/carbon/human/user)
add_fingerprint(user)
- if(istype(M) && on && user.zone_selected in list(BODY_ZONE_PRECISE_EYES, BODY_ZONE_PRECISE_MOUTH))
+ if(istype(M) && on && (user.zone_selected in list(BODY_ZONE_PRECISE_EYES, BODY_ZONE_PRECISE_MOUTH)))
if((HAS_TRAIT(user, TRAIT_CLUMSY) || HAS_TRAIT(user, TRAIT_DUMB)) && prob(50)) //too dumb to use flashlight properly
return ..() //just hit them in the head
@@ -390,7 +390,7 @@
return TRUE
/obj/item/flashlight/emp/attack(mob/living/M, mob/living/user)
- if(on && user.zone_selected in list(BODY_ZONE_PRECISE_EYES, BODY_ZONE_PRECISE_MOUTH)) // call original attack when examining organs
+ if(on && (user.zone_selected in list(BODY_ZONE_PRECISE_EYES, BODY_ZONE_PRECISE_MOUTH))) // call original attack when examining organs
..()
return
diff --git a/code/game/objects/items/devices/geiger_counter.dm b/code/game/objects/items/devices/geiger_counter.dm
index ce0e492393..2af449df24 100644
--- a/code/game/objects/items/devices/geiger_counter.dm
+++ b/code/game/objects/items/devices/geiger_counter.dm
@@ -38,6 +38,7 @@
/obj/item/geiger_counter/Destroy()
STOP_PROCESSING(SSobj, src)
+ QDEL_NULL(soundloop)
return ..()
/obj/item/geiger_counter/process()
diff --git a/code/game/objects/items/devices/gps.dm b/code/game/objects/items/devices/gps.dm
index eff8f1b7dc..9a09be093d 100644
--- a/code/game/objects/items/devices/gps.dm
+++ b/code/game/objects/items/devices/gps.dm
@@ -73,8 +73,10 @@ GLOBAL_LIST_EMPTY(GPS_list)
return
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- var/gps_window_height = 300 + GLOB.GPS_list.len * 20 // Variable window height, depending on how many GPS units there are to show
- ui = new(user, src, ui_key, "gps", "Global Positioning System", 600, gps_window_height, master_ui, state) //width, height
+ // Variable window height, depending on how many GPS units there are
+ // to show, clamped to relatively safe range.
+ var/gps_window_height = CLAMP(325 + GLOB.GPS_list.len * 14, 325, 700)
+ ui = new(user, src, ui_key, "gps", "Global Positioning System", 470, gps_window_height, master_ui, state) //width, height
ui.open()
ui.set_autoupdate(state = updating)
@@ -91,6 +93,8 @@ GLOBAL_LIST_EMPTY(GPS_list)
var/turf/curr = get_turf(src)
data["current"] = "[get_area_name(curr, TRUE)] ([curr.x], [curr.y], [curr.z])"
+ data["currentArea"] = "[get_area_name(curr, TRUE)]"
+ data["currentCoords"] = "[curr.x], [curr.y], [curr.z]"
var/list/signals = list()
data["signals"] = list()
@@ -104,16 +108,10 @@ GLOBAL_LIST_EMPTY(GPS_list)
continue
var/list/signal = list()
signal["entrytag"] = G.gpstag //Name or 'tag' of the GPS
- signal["area"] = get_area_name(G, TRUE)
- signal["coord"] = "[pos.x], [pos.y], [pos.z]"
+ signal["coords"] = "[pos.x], [pos.y], [pos.z]"
if(pos.z == curr.z) //Distance/Direction calculations for same z-level only
signal["dist"] = max(get_dist(curr, pos), 0) //Distance between the src and remote GPS turfs
signal["degrees"] = round(Get_Angle(curr, pos)) //0-360 degree directional bearing, for more precision.
- var/direction = uppertext(dir2text(get_dir(curr, pos))) //Direction text (East, etc). Not as precise, but still helpful.
- if(!direction)
- direction = "CENTER"
- signal["degrees"] = "N/A"
- signal["direction"] = direction
signals += list(signal) //Add this signal to the list of signals
data["signals"] = signals
diff --git a/code/game/objects/items/devices/paicard.dm b/code/game/objects/items/devices/paicard.dm
index cef75942a0..f28aacc46c 100644
--- a/code/game/objects/items/devices/paicard.dm
+++ b/code/game/objects/items/devices/paicard.dm
@@ -101,7 +101,7 @@
if(href_list["reset_radio_short"])
pai.unshort_radio()
if(href_list["setlaws"])
- var/newlaws = copytext(sanitize(input("Enter any additional directives you would like your pAI personality to follow. Note that these directives will not override the personality's allegiance to its imprinted master. Conflicting directives will be ignored.", "pAI Directive Configuration", pai.laws.supplied[1]) as message),1,MAX_MESSAGE_LEN)
+ var/newlaws = stripped_multiline_input("Enter any additional directives you would like your pAI personality to follow. Note that these directives will not override the personality's allegiance to its imprinted master. Conflicting directives will be ignored.", "pAI Directive Configuration", MAX_MESSAGE_LEN)
if(newlaws && pai)
pai.add_supplied_law(0,newlaws)
if(href_list["toggle_holo"])
diff --git a/code/game/objects/items/devices/radio/electropack.dm b/code/game/objects/items/devices/radio/electropack.dm
index d5cf6daabb..d499683fbc 100644
--- a/code/game/objects/items/devices/radio/electropack.dm
+++ b/code/game/objects/items/devices/radio/electropack.dm
@@ -61,7 +61,7 @@
var/mob/living/carbon/C = usr
if(usr.stat || usr.restrained() || C.back == src)
return
-
+
if(!usr.canUseTopic(src, BE_CLOSE))
usr << browse(null, "window=radio")
onclose(usr, "radio")
@@ -127,7 +127,7 @@
/obj/item/electropack/ui_interact(mob/user)
if(!ishuman(user))
return
-
+
user.set_machine(src)
var/dat = {"
@@ -200,14 +200,14 @@ Code:
/obj/item/electropack/shockcollar/attackby(obj/item/W, mob/user, params) //moves it here because on_click is being bad
if(istype(W, /obj/item/pen))
- var/t = input(user, "Would you like to change the name on the tag?", "Name your new pet", tagname ? tagname : "Spot") as null|text
+ var/t = stripped_input(user, "Would you like to change the name on the tag?", "Name your new pet", tagname ? tagname : "Spot", MAX_NAME_LEN)
if(t)
- tagname = copytext(sanitize(t), 1, MAX_NAME_LEN)
- name = "[initial(name)] - [tagname]"
+ tagname = t
+ name = "[initial(name)] - [t]"
else
return ..()
-/obj/item/electropack/shockcollar/ui_interact(mob/user) //on_click calls this
+/obj/item/electropack/shockcollar/ui_interact(mob/user) //on_click calls this
var/dat = {"
Frequency/Code for shock collar:
diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm
index 8ccd15e397..b87b7b11bd 100644
--- a/code/game/objects/items/devices/radio/radio.dm
+++ b/code/game/objects/items/devices/radio/radio.dm
@@ -112,7 +112,14 @@
. = ..()
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "radio", name, 370, 220 + channels.len * 22, master_ui, state)
+ var/ui_width = 360
+ var/ui_height = 106
+ if(subspace_transmission)
+ if(channels.len > 0)
+ ui_height += 6 + channels.len * 21
+ else
+ ui_height += 24
+ ui = new(user, src, ui_key, "radio", name, ui_width, ui_height, master_ui, state)
ui.open()
/obj/item/radio/ui_data(mob/user)
diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm
index 57c5a22de0..c23311cf22 100644
--- a/code/game/objects/items/devices/scanners.dm
+++ b/code/game/objects/items/devices/scanners.dm
@@ -28,7 +28,7 @@ SLIME SCANNER
/obj/item/t_scanner/attack_self(mob/user)
on = !on
- icon_state = copytext(icon_state, 1, length(icon_state))+"[on]"
+ icon_state = copytext_char(icon_state, 1, -1) + "[on]"
if(on)
START_PROCESSING(SSobj, src)
@@ -407,19 +407,17 @@ SLIME SCANNER
// Blood Level
if(M.has_dna())
var/mob/living/carbon/C = M
- var/blood_id = C.get_blood_id()
- if(blood_id)
+ var/blood_typepath = C.get_blood_id()
+ if(blood_typepath)
if(ishuman(C))
if(H.bleed_rate)
msg += "Subject is bleeding!\n"
var/blood_percent = round((C.scan_blood_volume() / (BLOOD_VOLUME_NORMAL * C.blood_ratio))*100)
var/blood_type = C.dna.blood_type
- if(blood_id != ("blood" || "jellyblood"))//special blood substance
- var/datum/reagent/R = GLOB.chemical_reagents_list[blood_id]
+ if(!(blood_typepath in GLOB.blood_reagent_types))
+ var/datum/reagent/R = GLOB.chemical_reagents_list[blood_typepath]
if(R)
blood_type = R.name
- else
- blood_type = blood_id
if(C.scan_blood_volume() <= (BLOOD_VOLUME_SAFE*C.blood_ratio) && C.scan_blood_volume() > (BLOOD_VOLUME_OKAY*C.blood_ratio))
msg += "LOW blood level [blood_percent] %, [C.scan_blood_volume()] cl,type: [blood_type]\n"
else if(C.scan_blood_volume() <= (BLOOD_VOLUME_OKAY*C.blood_ratio))
diff --git a/code/game/objects/items/eightball.dm b/code/game/objects/items/eightball.dm
index b37c147357..c4a15a1871 100644
--- a/code/game/objects/items/eightball.dm
+++ b/code/game/objects/items/eightball.dm
@@ -54,7 +54,7 @@
to_chat(user, "[src] was shaken recently, it needs time to settle.")
return
- user.visible_message("[user] starts shaking [src].", "You start shaking [src].", "You hear shaking and sloshing.")
+ user.visible_message("[user] starts shaking [src].", "You start shaking [src].", "You hear shaking and sloshing.")
shaking = TRUE
@@ -95,16 +95,47 @@
// except it actually ASKS THE DEAD (wooooo)
/obj/item/toy/eightball/haunted
- shake_time = 150
- cooldown_time = 1800
+ shake_time = 30 SECONDS
+ cooldown_time = 3 MINUTES
flags_1 = HEAR_1
var/last_message
var/selected_message
- var/list/votes
+ //these kind of store the same thing but one is easier to work with.
+ var/list/votes = list()
+ var/list/voted = list()
+ var/static/list/haunted_answers = list(
+ "yes" = list(
+ "It is certain",
+ "It is decidedly so",
+ "Without a doubt",
+ "Yes definitely",
+ "You may rely on it",
+ "As I see it, yes",
+ "Most likely",
+ "Outlook good",
+ "Yes",
+ "Signs point to yes"
+ ),
+ "maybe" = list(
+ "Reply hazy try again",
+ "Ask again later",
+ "Better not tell you now",
+ "Cannot predict now",
+ "Concentrate and ask again"
+ ),
+ "no" = list(
+ "Don't count on it",
+ "My reply is no",
+ "My sources say no",
+ "Outlook not so good",
+ "Very doubtful"
+ )
+ )
/obj/item/toy/eightball/haunted/Initialize(mapload)
. = ..()
- votes = list()
+ for (var/answer in haunted_answers)
+ votes[answer] = 0
GLOB.poi_list |= src
/obj/item/toy/eightball/haunted/Destroy()
@@ -122,7 +153,7 @@
interact(user)
return ..()
-/obj/item/toy/eightball/haunted/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, spans, message_mode, atom/movable/source)
+/obj/item/toy/eightball/haunted/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, spans, message_mode)
. = ..()
last_message = raw_message
@@ -137,38 +168,31 @@
if(isobserver(usr))
interact(usr)
-/obj/item/toy/eightball/haunted/proc/get_vote_tallies()
- var/list/answers = list()
- for(var/ckey in votes)
- var/selected = votes[ckey]
- if(selected in answers)
- answers[selected]++
- else
- answers[selected] = 1
-
- return answers
-
-
/obj/item/toy/eightball/haunted/get_answer()
- if(!votes.len)
- return pick(possible_answers)
+ var/top_amount = 0
+ var/top_vote
- var/list/tallied_votes = get_vote_tallies()
+ for(var/vote in votes)
+ var/amount_of_votes = length(votes[vote])
+ if(amount_of_votes > top_amount)
+ top_vote = vote
+ top_amount = amount_of_votes
+ //If one option actually has votes and there's a tie, pick between them 50/50
+ else if(top_amount && amount_of_votes == top_amount && prob(50))
+ top_vote = vote
+ top_amount = amount_of_votes
- // I miss python sorting, then I wouldn't have to muck about with
- // all this
- var/most_popular_answer
- var/most_amount = 0
- // yes, if there is a tie, there is an arbitary decision
- // but we never said the spirit world was fair
- for(var/A in tallied_votes)
- var/amount = tallied_votes[A]
- if(amount > most_amount)
- most_popular_answer = A
+ if(isnull(top_vote))
+ top_vote = pick(votes)
- return most_popular_answer
+ for(var/vote in votes)
+ votes[vote] = 0
-/obj/item/toy/eightball/haunted/ui_interact(mob/user, ui_key="main", datum/tgui/ui=null, force_open=0, datum/tgui/master_ui=null, datum/ui_state/state = GLOB.observer_state)
+ voted.Cut()
+
+ return top_vote
+
+/obj/item/toy/eightball/haunted/ui_interact(mob/user, ui_key="main", datum/tgui/ui=null, force_open=0, datum/tgui/master_ui=null, datum/ui_state/state = GLOB.always_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
@@ -179,21 +203,13 @@
var/list/data = list()
data["shaking"] = shaking
data["question"] = selected_message
- var/list/tallied_votes = get_vote_tallies()
data["answers"] = list()
-
- for(var/pa in possible_answers)
+ for(var/pa in haunted_answers)
var/list/L = list()
L["answer"] = pa
- var/amount = 0
- if(pa in tallied_votes)
- amount = tallied_votes[pa]
- L["amount"] = amount
- var/selected = FALSE
- if(votes[user.ckey] == pa)
- selected = TRUE
- L["selected"] = selected
+ L["amount"] = votes[pa]
+ L["selected"] = voted[user.ckey]
data["answers"] += list(L)
return data
@@ -206,8 +222,11 @@
switch(action)
if("vote")
var/selected_answer = params["answer"]
- if(!(selected_answer in possible_answers))
+ if(!(selected_answer in haunted_answers))
+ return
+ if(user.ckey in voted)
return
else
- votes[user.ckey] = selected_answer
- . = TRUE
+ votes[selected_answer] += 1
+ voted[user.ckey] = selected_answer
+ . = TRUE
\ No newline at end of file
diff --git a/code/game/objects/items/granters.dm b/code/game/objects/items/granters.dm
index 70cd503ef4..986090212e 100644
--- a/code/game/objects/items/granters.dm
+++ b/code/game/objects/items/granters.dm
@@ -490,7 +490,13 @@
oneuse = FALSE
remarks = list("Looks like these would sell much better in a plasma fire...", "Using glass bowls rather then cones?", "Mixing soda and ice-cream?", "Tall glasses with of liquids and solids...", "Just add a bit of icecream and cherry on top?")
-//Later content when I have free time - Trilby Date:24-Aug-2019
+/obj/item/book/granter/crafting_recipe/bone_bow //Bow crafting for non-ashwalkers
+ name = "Sandstone manual on bows"
+ desc = "A standstone slab with everything you need to know for making bows and arrows just like an ashwalker would."
+ crafting_recipe_types = list(/datum/crafting_recipe/bone_arrow, /datum/crafting_recipe/bone_bow, /datum/crafting_recipe/ashen_arrow, /datum/crafting_recipe/quiver, /datum/crafting_recipe/bow_tablet)
+ icon_state = "stone_tablet"
+ oneuse = FALSE
+ remarks = list("Sticking burning arrows into the sand makes them stronger?", "Breaking the bone apart to get shards, not sharpening the bone.", "Sinew is just like rope?")
/obj/item/book/granter/crafting_recipe/under_the_oven //Illegal cook book
name = "Under The Oven"
diff --git a/code/game/objects/items/grenades/chem_grenade.dm b/code/game/objects/items/grenades/chem_grenade.dm
index cb16b558d3..acd71c57ac 100644
--- a/code/game/objects/items/grenades/chem_grenade.dm
+++ b/code/game/objects/items/grenades/chem_grenade.dm
@@ -117,7 +117,7 @@
if(!O.reagents)
continue
var/reagent_list = pretty_string_from_reagent_list(O.reagents)
- user.log_message("removed [O] ([reagent_list]) from [src]")
+ user.log_message("removed [O] ([reagent_list]) from [src]", LOG_GAME)
beakers = list()
to_chat(user, "You open the [initial(name)] assembly and remove the payload.")
return // First use of the wrench remove beakers, then use the wrench to remove the activation mechanism.
@@ -177,7 +177,7 @@
/obj/item/grenade/chem_grenade/prime()
if(stage != READY)
- return
+ return FALSE
var/list/datum/reagents/reactants = list()
for(var/obj/item/reagent_containers/glass/G in beakers)
@@ -192,7 +192,7 @@
O.forceMove(drop_location())
beakers = list()
stage_change(EMPTY)
- return
+ return FALSE
if(nadeassembly)
var/mob/M = get_mob_by_ckey(assemblyattacher)
@@ -205,6 +205,7 @@
update_mob()
qdel(src)
+ return TRUE
//Large chem grenades accept slime cores and use the appropriately.
/obj/item/grenade/chem_grenade/large
@@ -219,7 +220,7 @@
/obj/item/grenade/chem_grenade/large/prime()
if(stage != READY)
- return
+ return FALSE
for(var/obj/item/slime_extract/S in beakers)
if(S.Uses)
@@ -237,7 +238,7 @@
else
S.forceMove(get_turf(src))
no_splash = TRUE
- ..()
+ return ..()
//I tried to just put it in the allowed_containers list but
//if you do that it must have reagents. If you're going to
@@ -288,7 +289,7 @@
/obj/item/grenade/chem_grenade/adv_release/prime()
if(stage != READY)
- return
+ return FALSE
var/total_volume = 0
for(var/obj/item/reagent_containers/RC in beakers)
@@ -296,7 +297,7 @@
if(!total_volume)
qdel(src)
qdel(nadeassembly)
- return
+ return FALSE
var/fraction = unit_spread/total_volume
var/datum/reagents/reactants = new(unit_spread)
reactants.my_atom = src
@@ -313,6 +314,7 @@
else
addtimer(CALLBACK(src, .proc/prime), det_time)
log_game("A grenade detonated at [AREACOORD(DT)]")
+ return TRUE
diff --git a/code/game/objects/items/grenades/grenade.dm b/code/game/objects/items/grenades/grenade.dm
index 16a5e6d1d3..77b4b33a8c 100644
--- a/code/game/objects/items/grenades/grenade.dm
+++ b/code/game/objects/items/grenades/grenade.dm
@@ -34,12 +34,12 @@
/obj/item/grenade/proc/clown_check(mob/living/carbon/human/user)
var/clumsy = HAS_TRAIT(user, TRAIT_CLUMSY)
- if(clumsy && (clumsy_check == GRENADE_CLUMSY_FUMBLE))
- if(prob(50))
+ if(clumsy)
+ if(clumsy_check == GRENADE_CLUMSY_FUMBLE && prob(50))
to_chat(user, "Huh? How does this thing work?")
preprime(user, 5, FALSE)
return FALSE
- else if(!clumsy && (clumsy_check == GRENADE_NONCLUMSY_FUMBLE))
+ else if(clumsy_check == GRENADE_NONCLUMSY_FUMBLE && !(user.mind && HAS_TRAIT(user.mind, TRAIT_CLOWN_MENTALITY)))
to_chat(user, "You pull the pin on [src]. Attached to it is a pink ribbon that says, \"HONK\"")
preprime(user, 5, FALSE)
return FALSE
diff --git a/code/game/objects/items/holosign_creator.dm b/code/game/objects/items/holosign_creator.dm
index e92c9b2d2e..47cf6f4ba7 100644
--- a/code/game/objects/items/holosign_creator.dm
+++ b/code/game/objects/items/holosign_creator.dm
@@ -92,6 +92,14 @@
creation_time = 0
max_signs = 3
+/obj/item/holosign_creator/combifan
+ name = "ATMOS holo-combifan projector"
+ desc = "A holographic projector that creates holographic combi-fans that prevent changes in atmosphere and temperature conditions. Somehow."
+ icon_state = "signmaker_atmos"
+ holosign_type = /obj/structure/holosign/barrier/combifan
+ creation_time = 0
+ max_signs = 3
+
/obj/item/holosign_creator/medical
name = "\improper PENLITE barrier projector"
desc = "A holographic projector that creates PENLITE holobarriers. Useful during quarantines since they halt those with malicious diseases."
diff --git a/code/game/objects/items/holy_weapons.dm b/code/game/objects/items/holy_weapons.dm
index 5367f3461c..45abd4fb49 100644
--- a/code/game/objects/items/holy_weapons.dm
+++ b/code/game/objects/items/holy_weapons.dm
@@ -72,20 +72,21 @@
display_names += list(initial(A.name) = A)
var/choice = input(M,"What holy armor kit would you like to order?","Holy Armor Theme") as null|anything in display_names
- if(QDELETED(src) || !choice || M.stat || !in_range(M, src) || M.restrained() || !M.canmove || GLOB.holy_armor_type)
+ var/turf/T = get_turf(M)
+ if(!T || QDELETED(src) || !choice || M.stat || !in_range(M, src) || M.restrained() || !M.canmove || GLOB.holy_armor_type)
return
var/index = display_names.Find(choice)
var/A = holy_armor_list[index]
GLOB.holy_armor_type = A
- var/holy_armor_box = new A
+ var/holy_armor_box = new A(T)
SSblackbox.record_feedback("tally", "chaplain_armor", 1, "[choice]")
if(holy_armor_box)
qdel(src)
- M.put_in_active_hand(holy_armor_box)///YOU COMPILED
+ M.put_in_hands(holy_armor_box)
/obj/item/storage/box/holy
name = "Templar Kit"
diff --git a/code/game/objects/items/implants/implant_explosive.dm b/code/game/objects/items/implants/implant_explosive.dm
index b93c9419a3..bf8d215a11 100644
--- a/code/game/objects/items/implants/implant_explosive.dm
+++ b/code/game/objects/items/implants/implant_explosive.dm
@@ -82,8 +82,9 @@
/obj/item/implant/explosive/proc/boom_goes_the_weasel()
explosion(get_turf(imp_in ? imp_in : src), heavy, medium, weak, weak, flame_range = weak)
- imp_in?.gib(TRUE)
- qdel(src)
+ if(!QDELETED(imp_in))
+ imp_in.gib(TRUE)
+ qdel(src)
/obj/item/implant/explosive/macro
name = "macrobomb implant"
diff --git a/code/game/objects/items/implants/implant_hijack.dm b/code/game/objects/items/implants/implant_hijack.dm
new file mode 100644
index 0000000000..5dd610059c
--- /dev/null
+++ b/code/game/objects/items/implants/implant_hijack.dm
@@ -0,0 +1,121 @@
+#define HIJACK_APC_MAX_AMOUNT 5
+
+/obj/item/implant/hijack
+ name = "hijack implant"
+ desc = "Allows you to control the machinery in a room by hacking into the APC."
+ actions_types = list(/datum/action/item_action/hands_free/activate, /datum/action/item_action/removeAPCs, /datum/action/item_action/accessAPCs, /datum/action/item_action/stealthmodetoggle)
+ activated = 1
+ var/toggled = FALSE
+ icon_state = "hijack"
+ var/eye_color
+ var/stealthmode = FALSE
+ var/stealthcooldown = 0
+ var/hijacking = FALSE
+
+/obj/item/implant/hijack/activate()
+ . = ..()
+ toggled = !toggled
+ imp_in.click_intercept = toggled ? src : null
+ imp_in.siliconaccesstoggle = toggled ? TRUE : FALSE
+ to_chat(imp_in,"You turn [toggled ? "on" : "off"] [src]'s silicon interactions.")
+ toggle_eyes()
+
+/obj/item/implant/hijack/proc/toggle_eyes()
+ if (!ishuman(imp_in))
+ return
+ var/on = toggled && !stealthmode
+ var/mob/living/carbon/human/H = imp_in
+ H.eye_color = on ? "ff0" : eye_color
+ H.dna.update_ui_block(DNA_EYE_COLOR_BLOCK)
+ H.update_body()
+
+/obj/item/implant/hijack/implant(mob/living/target, mob/user, silent = FALSE)
+ if(..())
+ ADD_TRAIT(target, TRAIT_HIJACKER, "implant")
+ if (ishuman(target))
+ var/mob/living/carbon/human/H = target
+ eye_color = H.eye_color
+ return TRUE
+
+/obj/item/implant/hijack/removed(mob/living/source, silent = FALSE, special = 0)
+ if(..())
+ REMOVE_TRAIT(source, TRAIT_HIJACKER, "implant")
+ for (var/area/area in source.siliconaccessareas)
+ source.toggleSiliconAccessArea(area)
+ var/obj/machinery/power/apc/apc = area.get_apc()
+ if (apc)
+ apc.hijacker = null
+ apc.set_hijacked_lighting()
+ apc.update_icon()
+ if (ishuman(source))
+ var/mob/living/carbon/human/H = source
+ H.eye_color = eye_color
+ return TRUE
+
+/obj/item/implant/hijack/proc/InterceptClickOn(mob/living/user,params,atom/object)
+ if (isitem(object) || !toggled || user.incapacitated())
+ return
+ if (stealthmode == FALSE && istype(object,/obj/machinery/power/apc) && !user.CanReach(object))
+ if (hijack_remotely(object))
+ return
+ if (stealthmode && !user.CanReach(object))
+ return
+ if (!object.hasSiliconAccessInArea(imp_in))
+ return
+ var/list/modifiers = params2list(params)
+ imp_in.face_atom(object)
+ if (modifiers["shift"] && modifiers["ctrl"])
+ object.AICtrlShiftClick(imp_in)
+ return TRUE
+ if (modifiers["shift"])
+ object.AIShiftClick(imp_in)
+ return TRUE
+ if (modifiers["ctrl"])
+ object.AICtrlClick(imp_in)
+ return TRUE
+ if (modifiers["alt"])
+ object.AIAltClick(imp_in)
+ return TRUE
+ if (user.get_active_held_item())
+ return
+ if (user.CanReach(object))
+ object.attack_robot(imp_in)
+ else
+ object.attack_ai(imp_in)
+ return TRUE
+
+/obj/item/implant/hijack/proc/hijack_remotely(obj/machinery/power/apc/apc)
+ if (apc.hijacker || hijacking)
+ return FALSE //can't remotely hijack an already hijacked APC
+ hijacking = TRUE
+ to_chat(imp_in, "Establishing remote connection with APC.")
+ if (!do_after(imp_in, 4 SECONDS,target=apc))
+ to_chat(imp_in, "Aborting.")
+ hijacking = FALSE
+ return TRUE
+ if (LAZYLEN(imp_in.siliconaccessareas) >= HIJACK_APC_MAX_AMOUNT)
+ to_chat(src,"You are connected to too many APCs! Too many more will fry your brain.")
+ hijacking = FALSE
+ return TRUE
+ imp_in.light_power = 2
+ imp_in.light_range = 2
+ imp_in.light_color = COLOR_YELLOW
+ imp_in.update_light()
+ imp_in.visible_message("[imp_in] starts glowing a with a hollow yellow light!")
+ to_chat(imp_in, "Beginning hijack of APC.")
+ if (do_after(imp_in, 21 SECONDS,target=apc))
+ apc.hijacker = imp_in
+ stealthmode = FALSE
+ apc.set_hijacked_lighting()
+ imp_in.toggleSiliconAccessArea(apc.area)
+ apc.update_icon()
+ stealthcooldown = world.time + 1 MINUTES + 30 SECONDS
+ toggle_eyes()
+ else
+ to_chat(imp_in, "Aborting.")
+ hijacking = FALSE
+ imp_in.light_power = 0
+ imp_in.light_range = 0
+ imp_in.light_color = COLOR_YELLOW
+ imp_in.update_light()
+ return TRUE
diff --git a/code/game/objects/items/implants/implanter.dm b/code/game/objects/items/implants/implanter.dm
index ab902369cc..a0e27f14f0 100644
--- a/code/game/objects/items/implants/implanter.dm
+++ b/code/game/objects/items/implants/implanter.dm
@@ -74,4 +74,8 @@
/obj/item/implanter/stealth
name = "implanter (stealth)"
- imp_type = /obj/item/implant/stealth
\ No newline at end of file
+ imp_type = /obj/item/implant/stealth
+
+/obj/item/implanter/hijack
+ name = "implanter (hijack)"
+ imp_type = /obj/item/implant/hijack
\ No newline at end of file
diff --git a/code/game/objects/items/melee/energy.dm b/code/game/objects/items/melee/energy.dm
index 15a686bae6..3ca489542f 100644
--- a/code/game/objects/items/melee/energy.dm
+++ b/code/game/objects/items/melee/energy.dm
@@ -259,7 +259,7 @@
light_color = "#37FFF7"
actions_types = list()
-/obj/item/melee/transforming/energy/sword/cx/pre_altattackby(atom/A, mob/living/user, params) //checks if it can do right click memes
+/obj/item/melee/transforming/energy/sword/cx/alt_pre_attack(atom/A, mob/living/user, params) //checks if it can do right click memes
altafterattack(A, user, TRUE, params)
return TRUE
diff --git a/code/game/objects/items/miscellaneous.dm b/code/game/objects/items/miscellaneous.dm
index 9837936e5f..ee169cc661 100644
--- a/code/game/objects/items/miscellaneous.dm
+++ b/code/game/objects/items/miscellaneous.dm
@@ -19,3 +19,11 @@
icon_state = "skub"
w_class = WEIGHT_CLASS_BULKY
attack_verb = list("skubbed")
+
+/obj/item/supermatterspray
+ name = "supermatter spray"
+ desc = "A spray bottle containing some kind of magical spray to fix the SM. \"Do not inhale.\" is written on the side. Unless aimed at the supermatter, it does nothing."
+ icon = 'icons/obj/supermatter.dmi'
+ icon_state = "supermatterspray"
+ w_class = WEIGHT_CLASS_SMALL
+ var/usesleft = 2
\ No newline at end of file
diff --git a/code/game/objects/items/mop.dm b/code/game/objects/items/mop.dm
index a8b28f2544..233eabd4ca 100644
--- a/code/game/objects/items/mop.dm
+++ b/code/game/objects/items/mop.dm
@@ -21,7 +21,7 @@
/obj/item/mop/New()
..()
- create_reagents(mopcap)
+ create_reagents(mopcap, NONE, NO_REAGENTS_VALUE)
/obj/item/mop/proc/clean(turf/A)
diff --git a/code/game/objects/items/pinpointer.dm b/code/game/objects/items/pinpointer.dm
index c4ad324a2c..151df7694a 100644
--- a/code/game/objects/items/pinpointer.dm
+++ b/code/game/objects/items/pinpointer.dm
@@ -26,6 +26,7 @@
/obj/item/pinpointer/Destroy()
STOP_PROCESSING(SSfastprocess, src)
GLOB.pinpointer_list -= src
+ target = null
return ..()
/obj/item/pinpointer/attack_self(mob/living/user)
@@ -57,7 +58,7 @@
return
var/turf/here = get_turf(src)
var/turf/there = get_turf(target)
- if(here.z != there.z)
+ if(!here || !there || here.z != there.z)
add_overlay("pinon[alert ? "alert" : ""]null")
return
if(get_dist_euclidian(here,there) <= minimum_range)
@@ -143,3 +144,34 @@
target = null
if(!target) //target can be set to null from above code, or elsewhere
active = FALSE
+
+/obj/item/pinpointer/pair
+ name = "pair pinpointer"
+ desc = "A handheld tracking device that locks onto its other half of the matching pair."
+ var/other_pair
+
+/obj/item/pinpointer/pair/Destroy()
+ other_pair = null
+ . = ..()
+
+/obj/item/pinpointer/pair/scan_for_target()
+ target = other_pair
+
+/obj/item/pinpointer/pair/examine(mob/user)
+ . = ..()
+ if(!active || !target)
+ return
+ var/mob/mob_holder = get(target, /mob)
+ if(istype(mob_holder))
+ to_chat(user, "Its pair is being held by [mob_holder].")
+ return
+
+/obj/item/storage/box/pinpointer_pairs
+ name = "pinpointer pair box"
+
+/obj/item/storage/box/pinpointer_pairs/PopulateContents()
+ var/obj/item/pinpointer/pair/A = new(src)
+ var/obj/item/pinpointer/pair/B = new(src)
+
+ A.other_pair = B
+ B.other_pair = A
diff --git a/code/game/objects/items/plushes.dm b/code/game/objects/items/plushes.dm
index 705dcad6c6..a2a8cb3150 100644
--- a/code/game/objects/items/plushes.dm
+++ b/code/game/objects/items/plushes.dm
@@ -31,7 +31,16 @@
var/normal_desc
//--end of love :'(--
-/obj/item/toy/plush/Initialize()
+ var/snowflake_id //if we set from a config snowflake plushie.
+ var/can_random_spawn = TRUE //if this is FALSE, don't spawn this for random plushies.
+
+/obj/item/toy/plush/random_snowflake/Initialize(mapload, set_snowflake_id)
+ . = ..()
+ var/list/configlist = CONFIG_GET(keyed_list/snowflake_plushies)
+ var/id = pick(configlist)
+ set_snowflake_from_config(id)
+
+/obj/item/toy/plush/Initialize(mapload, set_snowflake_id)
. = ..()
AddComponent(/datum/component/squeak, squeak_override)
@@ -50,6 +59,9 @@
normal_desc = desc
+ if(set_snowflake_id)
+ set_snowflake_from_config(set_snowflake_id)
+
/obj/item/toy/plush/Destroy()
QDEL_NULL(grenade)
@@ -97,6 +109,44 @@
return ..()
+/obj/item/toy/plush/proc/set_snowflake_from_config(id)
+ var/list/configlist = CONFIG_GET(keyed_list/snowflake_plushies)
+ var/list/jsonlist = configlist[id]
+ ASSERT(jsonlist)
+ jsonlist = json_decode(jsonlist)
+ if(jsonlist["inherit_from"])
+ var/path = text2path(jsonlist["inherit_from"])
+ if(!ispath(path, /obj/item/toy/plush))
+ stack_trace("Invalid path for inheritance")
+ else
+ var/obj/item/toy/plush/P = new path //can't initial() lists
+ name = P.name
+ desc = P.desc
+ icon_state = P.icon_state
+ item_state = P.item_state
+ icon = P.icon
+ squeak_override = P.squeak_override
+ attack_verb = P.attack_verb
+ gender = P.gender
+ qdel(P)
+ if(jsonlist["name"])
+ name = jsonlist["name"]
+ if(jsonlist["desc"])
+ desc = jsonlist["desc"]
+ if(jsonlist["gender"])
+ gender = jsonlist["gender"]
+ if(jsonlist["icon_state"])
+ icon_state = jsonlist["icon_state"]
+ item_state = jsonlist["item_state"]
+ icon = 'config/plushies/sprites.dmi'
+ if(jsonlist["attack_verb"])
+ attack_verb = jsonlist["attack_verb"]
+ if(jsonlist["squeak_override"])
+ squeak_override = jsonlist["squeak_override"]
+ if(squeak_override)
+ var/datum/component/squeak/S = GetComponent(/datum/component/squeak)
+ S?.override_squeak_sounds = squeak_override
+
/obj/item/toy/plush/handle_atom_del(atom/A)
if(A == grenade)
grenade = null
@@ -367,13 +417,22 @@
if(mood_message)
desc += mood_message
+GLOBAL_LIST_INIT(valid_plushie_paths, valid_plushie_paths())
+/proc/valid_plushie_paths()
+ . = list()
+ for(var/i in subtypesof(/obj/item/toy/plush))
+ var/obj/item/toy/plush/abstract = i
+ if(!initial(abstract.can_random_spawn))
+ continue
+ . += i
+
/obj/item/toy/plush/random
name = "Illegal plushie"
desc = "Something fucked up"
- var/blacklisted_plushes = list(/obj/item/toy/plush/carpplushie/dehy_carp, /obj/item/toy/plush/awakenedplushie, /obj/item/toy/plush/random)
+ can_random_spawn = FALSE
/obj/item/toy/plush/random/Initialize()
- var/newtype = pick(subtypesof(/obj/item/toy/plush) - typecacheof(blacklisted_plushes))
+ var/newtype = prob(CONFIG_GET(number/snowflake_plushie_prob))? /obj/item/toy/plush/random_snowflake : pick(GLOB.valid_plushie_paths)
new newtype(loc)
return INITIALIZE_HINT_QDEL
@@ -504,115 +563,12 @@
attack_verb = list("clawed", "hissed", "tail slapped")
squeak_override = list('sound/weapons/slash.ogg' = 1)
-/obj/item/toy/plush/lizardplushie/durgit
- icon_state = "durgit"
- item_state = "durgit"
- squeak_override = list('modular_citadel/sound/voice/weh.ogg' = 1) //Durgit's the origin of the sound
-
-/obj/item/toy/plush/lizardplushie/rio
- icon_state = "rio"
- item_state = "rio"
-
-/obj/item/toy/plush/lizardplushie/dan
- icon_state = "dan"
- item_state = "dan"
-
-/obj/item/toy/plush/lizardplushie/urinsu
- icon_state = "urinsu"
- item_state = "urinsu"
-
-/obj/item/toy/plush/lizardplushie/arfrehn
- icon_state = "arfrehn"
- item_state = "arfrehn"
-
-/obj/item/toy/plush/lizardplushie/soars
- icon_state = "soars"
- item_state = "soars"
-
-/obj/item/toy/plush/lizardplushie/ghostie
- icon_state = "ghostie"
- item_state = "ghostie"
-
-/obj/item/toy/plush/lizardplushie/amber
- icon_state = "amber"
- item_state = "amber"
-
-/obj/item/toy/plush/lizardplushie/cyan
- icon_state = "cyan"
- item_state = "cyan"
-
-/obj/item/toy/plush/lizardplushie/meena
- icon_state = "meena"
- item_state = "meena"
-
-/obj/item/toy/plush/lizardplushie/stalks
- icon_state = "stalks"
- item_state = "stalks"
-
/obj/item/toy/plush/lizardplushie/kobold
+ name = "kobold plushie"
+ desc = "An adorable stuffed toy that resembles a kobold."
icon_state = "kobold"
item_state = "kobold"
-/obj/item/toy/plush/lizardplushie/gorgi
- icon_state = "gorgi"
- item_state = "gorgi"
-
-/obj/item/toy/plush/lizardplushie/almaz
- icon_state = "almaz"
- item_state = "almaz"
- squeak_override = list('modular_citadel/sound/voice/raptor_purr.ogg' = 1)
-
-/obj/item/toy/plush/lizardplushie/garou
- icon_state = "garou"
- item_state = "garou"
-
-/obj/item/toy/plush/lizardplushie/augments
- icon_state = "augments"
- item_state = "augments"
- squeak_override = list('modular_citadel/sound/voice/weh.ogg' = 1) //I have no mouth and I must weh
- attack_verb = list("hugged", "patted", "snugged", "booped")
-
-/obj/item/toy/plush/lizardplushie/xekov
- icon_state = "xekov"
- item_state = "xekov"
-
-/obj/item/toy/plush/lizardplushie/greg
- icon_state = "greg"
- item_state = "greg"
-
-/obj/item/toy/plush/lizardplushie/sin
- icon_state = "sin"
- item_state = "sin"
- desc = "An adorable stuffed toy that resembles a lizardperson.. It faintly smells of sulfur."
-
-/obj/item/toy/plush/lizardplushie/ends
- icon_state = "ends"
- item_state = "ends"
-
-/obj/item/toy/plush/lizardplushie/lyssa
- icon_state = "lyssa"
- item_state = "lyssa"
-
-/obj/item/toy/plush/snakeplushie
- name = "snake plushie"
- desc = "An adorable stuffed toy that resembles a snake. Not to be mistaken for the real thing."
- icon_state = "plushie_snake"
- item_state = "plushie_snake"
- attack_verb = list("bitten", "hissed", "tail slapped")
- squeak_override = list('modular_citadel/sound/voice/hiss.ogg' = 1)
-
-/obj/item/toy/plush/snakeplushie/sasha
- icon_state = "sasha"
- item_state = "sasha"
-
-/obj/item/toy/plush/snakeplushie/shay
- icon_state = "shay"
- item_state = "shay"
-
-/obj/item/toy/plush/snakeplushie/vulken
- icon_state = "vulken"
- item_state = "vulken"
-
/obj/item/toy/plush/nukeplushie
name = "operative plushie"
desc = "A stuffed toy that resembles a syndicate nuclear operative. The tag claims operatives to be purely fictitious."
@@ -630,29 +586,17 @@
squeak_override = list('sound/effects/blobattack.ogg' = 1)
gender = FEMALE //given all the jokes and drawings, I'm not sure the xenobiologists would make a slimeboy
-/obj/item/toy/plush/slimeplushie/annie
- desc = "An adorable stuffed toy that resembles a slimey crewmember."
- icon_state = "annie"
- item_state = "annie"
-
-/obj/item/toy/plush/slimeplushie/paxton
- desc = "An adorable stuffed toy that resembles a slimey crewmember."
- icon_state = "paxton"
- item_state = "paxton"
- attack_verb = list("CQC'd", "jabroni'd", "powergamed", "robusted", "cakehatted")
- gender = MALE
-
/obj/item/toy/plush/awakenedplushie
name = "awakened plushie"
desc = "An ancient plushie that has grown enlightened to the true nature of reality."
icon_state = "plushie_awake"
item_state = "plushie_awake"
+ can_random_spawn = FALSE
/obj/item/toy/plush/awakenedplushie/ComponentInitialize()
. = ..()
AddComponent(/datum/component/edit_complainer)
-
/obj/item/toy/plush/beeplushie
name = "bee plushie"
desc = "A cute toy that resembles an even cuter bee."
@@ -668,21 +612,7 @@
icon_state = "bumble"
item_state = "bumble"
squeak_override = list('modular_citadel/sound/voice/mothsqueak.ogg' = 1)
-
-/obj/item/toy/plush/mothplushie/nameko
- icon_state = "nameko"
- item_state = "nameko"
-
-/obj/item/toy/plush/mothplushie/suru
- icon_state = "suru"
- item_state = "suru"
-
-/obj/item/toy/plush/xeno
- name = "xenohybrid plushie"
- desc = "An adorable stuffed toy that resmembles a xenomorphic crewmember."
- icon_state = "seras"
- item_state = "seras"
- squeak_override = list('sound/voice/hiss2.ogg' = 1)
+ can_random_spawn = FALSE
/obj/item/toy/plush/lampplushie
name = "lamp plushie"
@@ -736,20 +666,6 @@
icon_state = "scrubpuppy"
item_state = "scrubpuppy"
-/obj/item/toy/plush/borgplushie/seeking
- icon_state = "seeking"
- item_state = "seeking"
-
-/obj/item/toy/plush/borgplushie/neeb
- icon_state = "neeb"
- item_state = "neeb"
-
-/obj/item/toy/plush/borgplushie/bhijn
- desc = "An adorable stuffed toy of a IPC."
- icon_state = "bhijn"
- item_state = "bhijn"
- attack_verb = list("closed", "reworked", "merged")
-
/obj/item/toy/plush/aiplush
name = "AI plushie"
desc = "A little stuffed toy AI core... it appears to be malfunctioning."
@@ -758,246 +674,22 @@
attack_verb = list("hacked", "detonated", "overloaded")
squeak_override = list('sound/machines/beep.ogg' = 9, 'sound/machines/buzz-two.ogg' = 1)
-/obj/item/toy/plush/bird
- name = "bird plushie"
- desc = "An adorable stuffed plushie that resembles an avian."
- icon_state = "sylas"
- item_state = "sylas"
- attack_verb = list("peeped", "beeped", "poofed")
- squeak_override = list('modular_citadel/sound/voice/peep.ogg' = 1)
-
-/obj/item/toy/plush/bird/esela
- icon_state = "esela"
- item_state = "esela"
-
-/obj/item/toy/plush/bird/jahonna
- icon_state = "jahonna"
- item_state = "jahonna"
-
-/obj/item/toy/plush/bird/krick
- icon_state = "krick"
- item_state = "krick"
-
-/obj/item/toy/plush/bird/birddi
- icon_state = "birddi"
- item_state = "birddi"
-
-/obj/item/toy/plush/bird/jewel
- icon_state = "jewel"
- item_state = "jewel"
-
-/obj/item/toy/plush/sergal
- name = "sergal plushie"
- desc = "An adorable stuffed plushie that resembles a sagaru."
- icon_state = "faux"
- item_state = "faux"
- squeak_override = list('modular_citadel/sound/voice/merp.ogg' = 1)
-
-/obj/item/toy/plush/sergal/gladwyn
- icon_state = "gladwyn"
- item_state = "gladwyn"
-
-/obj/item/toy/plush/sergal/jermaine
- icon_state = "jermaine"
- item_state = "jermaine"
-
-/obj/item/toy/plush/mammal
- name = "mammal plushie"
- desc = "An adorable stuffed toy resembling some sort of crew member."
- icon_state = "dubious"
- item_state = "dubious"
-
-/obj/item/toy/plush/mammal/gavin
- icon_state = "gavin"
- item_state = "gavin"
-
-/obj/item/toy/plush/mammal/blep
- icon_state = "blep"
- item_state = "blep"
-
-/obj/item/toy/plush/mammal/circe
- desc = "A luxuriously soft toy that resembles a nine-tailed kitsune."
- icon_state = "circe"
- item_state = "circe"
- attack_verb = list("medicated", "tailhugged", "kissed")
-
-/obj/item/toy/plush/mammal/robin
- icon_state = "robin"
- item_state = "robin"
-
-/obj/item/toy/plush/mammal/pavel
- icon_state = "pavel"
- item_state = "pavel"
-
-/obj/item/toy/plush/mammal/mason
- icon_state = "mason"
- item_state = "mason"
-
-/obj/item/toy/plush/mammal/oten
- icon_state = "oten"
- item_state = "oten"
-
-/obj/item/toy/plush/mammal/ray
- icon_state = "ray"
- item_state = "ray"
-
-/obj/item/toy/plush/mammal/redtail
- icon_state = "redtail"
- item_state = "redtail"
-
-/obj/item/toy/plush/mammal/dawud
- icon_state = "dawud"
- item_state = "dawud"
-
-/obj/item/toy/plush/mammal/edgar
- icon_state = "edgar"
- item_state = "edgar"
- attack_verb = list("collared", "tricked", "headpatted")
-
-/obj/item/toy/plush/mammal/frank
- icon_state = "frank"
- item_state = "frank"
-
-/obj/item/toy/plush/mammal/poojawa
- icon_state = "poojawa"
- item_state = "poojawa"
-
-/obj/item/toy/plush/mammal/hazel
- icon_state = "hazel"
- item_state = "hazel"
-
-/obj/item/toy/plush/mammal/joker
- icon_state = "joker"
- item_state = "joker"
-
-/obj/item/toy/plush/mammal/gunther
- icon_state = "gunther"
- item_state = "gunther"
-
/obj/item/toy/plush/mammal/fox
icon_state = "fox"
item_state = "fox"
-/obj/item/toy/plush/mammal/rae
- desc = "An adorable stuffed toy of an artic fox."
- icon_state = "rae"
- item_state = "rae"
+/obj/item/toy/plush/snakeplushie
+ name = "snake plushie"
+ desc = "An adorable stuffed toy that resembles a snake. Not to be mistaken for the real thing."
+ icon_state = "plushie_snake"
+ item_state = "plushie_snake"
+ attack_verb = list("bitten", "hissed", "tail slapped")
+ squeak_override = list('modular_citadel/sound/voice/hiss.ogg' = 1)
-/obj/item/toy/plush/mammal/zed
- desc = "A masked stuffed toy that resembles a fierce miner. He even comes with his own little crusher!"
- icon_state = "zed"
- item_state = "zed"
- attack_verb = list("ENDED", "CRUSHED", "GNOMED")
-
-/obj/item/toy/plush/mammal/justin
- icon_state = "justin"
- item_state = "justin"
- attack_verb = list("buttslapped", "fixed")
-
-/obj/item/toy/plush/mammal/reece
- icon_state = "reece"
- item_state = "reece"
- attack_verb = list("healed", "cured", "demoted")
-
-/obj/item/toy/plush/mammal/redwood
- desc = "An adorable stuffed toy resembling a Nanotrasen Captain. That just happens to be a bunny."
- icon_state = "redwood"
- item_state = "redwood"
- attack_verb = list("ordered", "bapped", "reprimanded")
-
-/obj/item/toy/plush/mammal/marisol
- desc = "An adorable stuffed toy resembling a demi-wolf security officer."
- icon_state = "marisol"
- item_state = "marisol"
- attack_verb = list("arrested", "harmbattoned", "lasered")
-
-/obj/item/toy/plush/mammal/minty
- desc = "An adorable stuffed toy resembling some sort of crew member. It smells like mint.."
- icon_state = "minty"
- item_state = "minty"
- attack_verb = list("freshened", "brushed")
-
-/obj/item/toy/plush/mammal/dog
- desc = "An adorable stuffed toy that resembles a canine."
- icon_state = "katlin"
- item_state = "katlin"
- attack_verb = list("barked", "boofed", "borked")
- squeak_override = list(
- 'modular_citadel/sound/voice/bark1.ogg' = 1,
- 'modular_citadel/sound/voice/bark2.ogg' = 1
- )
-
-/obj/item/toy/plush/mammal/dog/frost
- icon_state = "frost"
- item_state = "frost"
-
-/obj/item/toy/plush/mammal/dog/atticus
- icon_state = "atticus"
- item_state = "atticus"
-
-/obj/item/toy/plush/mammal/dog/fletch
- icon_state = "fletch"
- item_state = "fletch"
-
-/obj/item/toy/plush/mammal/dog/vincent
- icon_state = "vincent"
- item_state = "vincent"
-
-/obj/item/toy/plush/mammal/dog/zigfried
- desc = "An adorable stuffed toy of a very good boy."
- icon_state = "zigfried"
- item_state = "zigfried"
-
-/obj/item/toy/plush/mammal/dog/nikolai
- icon_state = "nikolai"
- item_state = "nikolai"
-
-/obj/item/toy/plush/mammal/dog/flynn
- icon_state = "flynn"
- item_state = "flynn"
-
-/obj/item/toy/plush/mammal/dog/fritz
- icon_state = "fritz"
- item_state = "fritz"
- attack_verb = list("barked", "boofed", "shotgun'd")
- obj_flags = UNIQUE_RENAME
- unique_reskin = list("Goodboye" = "fritz", "Badboye" = "fritz_bad")
-
-/obj/item/toy/plush/mammal/dog/jesse
- desc = "An adorable wolf toy that resembles a cream-colored wolf. He has a little pride flag!"
- icon_state = "jesse"
- item_state = "jesse"
- attack_verb = list("greeted", "merc'd", "howdy'd")
-
-/obj/item/toy/plush/catgirl
- name = "feline plushie"
- desc = "An adorable stuffed toy that resembles a feline."
- icon_state = "bailey"
- item_state = "bailey"
- attack_verb = list("headbutt", "scritched", "bit")
- squeak_override = list('modular_citadel/sound/voice/nya.ogg' = 1)
-
-/obj/item/toy/plush/catgirl/mikeel
- desc = "An adorable stuffed toy of some tauric cat person."
- icon_state = "mikeel"
- item_state = "mikeel"
-
-/obj/item/toy/plush/catgirl/skylar
- desc = "An adorable stuffed toy that resembles a degenerate."
- icon_state = "skylar2"
- item_state = "skylar2"
- attack_verb = list("powergamed", "merged", "tabled")
- squeak_override = list('sound/effects/meow1.ogg' = 1)
-
-/obj/item/toy/plush/catgirl/drew
- icon_state = "drew"
- item_state = "drew"
-
-/obj/item/toy/plush/catgirl/trilby
- desc = "A masked stuffed toy that resembles a feline scientist."
- icon_state = "trilby"
- item_state = "trilby"
- attack_verb = list("PR'd", "coded", "remembered")
+/obj/item/toy/plush/mammal
+ name = "mammal plushie"
+ desc = "An adorable stuffed toy resembling some sort of crew member."
+ can_random_spawn = FALSE
/obj/item/toy/plush/catgirl/fermis
name = "medcat plushie"
@@ -1007,20 +699,36 @@
attack_verb = list("cuddled", "petpatted", "wigglepurred")
squeak_override = list('modular_citadel/sound/voice/merowr.ogg' = 1)
-/obj/item/toy/plush/catgirl/mariaf
- desc = "An adorable stuffed toy that resembles a very tall cat girl."
- icon_state = "mariaf"
- item_state = "mariaf"
- attack_verb = list("hugged", "stabbed", "licked")
+/obj/item/toy/plush/xeno
+ name = "xenohybrid plushie"
+ desc = "An adorable stuffed toy that resmembles a xenomorphic crewmember."
+ squeak_override = list('sound/voice/hiss2.ogg' = 1)
+ can_random_spawn = FALSE
-/obj/item/toy/plush/catgirl/maya
- desc = "An adorable stuffed toy that resembles an angry cat girl. She has her own tiny nuke disk!"
- icon_state = "maya"
- item_state = "maya"
- attack_verb = list("nuked", "arrested", "harmbatonned")
+/obj/item/toy/plush/bird
+ name = "bird plushie"
+ desc = "An adorable stuffed plushie that resembles an avian."
+ attack_verb = list("peeped", "beeped", "poofed")
+ squeak_override = list('modular_citadel/sound/voice/peep.ogg' = 1)
+ can_random_spawn = FALSE
-/obj/item/toy/plush/catgirl/marisa
- desc = "An adorable stuffed toy that resembles a crew member, or maybe a witch. Having it makes you feel you can win."
- icon_state = "marisa"
- item_state = "marisa"
- attack_verb = list("blasted", "sparked", "dazzled")
+/obj/item/toy/plush/sergal
+ name = "sergal plushie"
+ desc = "An adorable stuffed plushie that resembles a sagaru."
+ squeak_override = list('modular_citadel/sound/voice/merp.ogg' = 1)
+ can_random_spawn = FALSE
+
+/obj/item/toy/plush/mammal/dog
+ desc = "An adorable stuffed toy that resembles a canine."
+ attack_verb = list("barked", "boofed", "borked")
+ squeak_override = list(
+ 'modular_citadel/sound/voice/bark1.ogg' = 1,
+ 'modular_citadel/sound/voice/bark2.ogg' = 1
+ )
+
+/obj/item/toy/plush/catgirl
+ name = "feline plushie"
+ desc = "An adorable stuffed toy that resembles a feline."
+ attack_verb = list("headbutt", "scritched", "bit")
+ squeak_override = list('modular_citadel/sound/voice/nya.ogg' = 1)
+ can_random_spawn = FALSE
diff --git a/code/game/objects/items/religion.dm b/code/game/objects/items/religion.dm
index 5fd9600a97..ac490b0122 100644
--- a/code/game/objects/items/religion.dm
+++ b/code/game/objects/items/religion.dm
@@ -44,9 +44,9 @@
if(H.stat == DEAD || H == user)
continue
if(H.mind && (has_job_loyalties || has_role_loyalties))
- if(has_job_loyalties && H.mind.assigned_role in job_loyalties)
+ if(has_job_loyalties && (H.mind.assigned_role in job_loyalties))
inspired += H
- else if(has_role_loyalties && H.mind.special_role in role_loyalties)
+ else if(has_role_loyalties && (H.mind.special_role in role_loyalties))
inspired += H
else if(check_inspiration(H))
inspired += H
diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm
index 62f36c60c0..09c2d442e8 100644
--- a/code/game/objects/items/robot/robot_upgrades.dm
+++ b/code/game/objects/items/robot/robot_upgrades.dm
@@ -203,7 +203,7 @@
var/obj/item/gun/energy/kinetic_accelerator/cyborg/KA = new (R.module)
R.module.basic_modules += KA
R.module.add_module(KA, FALSE, TRUE)
-
+
/obj/item/borg/upgrade/advcutter
name = "mining cyborg advanced plasma cutter"
@@ -335,7 +335,6 @@
var/msg_cooldown = 0
var/on = FALSE
var/powercost = 10
- var/mob/living/silicon/robot/cyborg
var/datum/action/toggle_action
/obj/item/borg/upgrade/selfrepair/action(mob/living/silicon/robot/R, user = usr)
@@ -346,7 +345,6 @@
to_chat(user, "This unit is already equipped with a self-repair module.")
return FALSE
- cyborg = R
icon_state = "selfrepair_off"
toggle_action = new /datum/action/item_action/toggle(src)
toggle_action.Grant(R)
@@ -354,34 +352,20 @@
/obj/item/borg/upgrade/selfrepair/deactivate(mob/living/silicon/robot/R, user = usr)
. = ..()
if (.)
- toggle_action.Remove(cyborg)
+ toggle_action.Remove(R)
QDEL_NULL(toggle_action)
- cyborg = null
- deactivate_sr()
-
-/obj/item/borg/upgrade/selfrepair/dropped()
- . = ..()
- addtimer(CALLBACK(src, .proc/check_dropped), 1)
-
-/obj/item/borg/upgrade/selfrepair/proc/check_dropped()
- if(loc != cyborg)
- toggle_action.Remove(cyborg)
- QDEL_NULL(toggle_action)
- cyborg = null
deactivate_sr()
/obj/item/borg/upgrade/selfrepair/ui_action_click()
- on = !on
if(on)
- to_chat(cyborg, "You activate the self-repair module.")
- START_PROCESSING(SSobj, src)
+ to_chat(toggle_action.owner, "You deactivate the self-repair module.")
+ deactivate_sr()
else
- to_chat(cyborg, "You deactivate the self-repair module.")
- STOP_PROCESSING(SSobj, src)
- update_icon()
+ to_chat(toggle_action.owner, "You activate the self-repair module.")
+ activate_sr()
/obj/item/borg/upgrade/selfrepair/update_icon()
- if(cyborg)
+ if(toggle_action)
icon_state = "selfrepair_[on ? "on" : "off"]"
for(var/X in actions)
var/datum/action/A = X
@@ -389,6 +373,11 @@
else
icon_state = "cyborg_upgrade5"
+/obj/item/borg/upgrade/selfrepair/proc/activate_sr()
+ START_PROCESSING(SSobj, src)
+ on = TRUE
+ update_icon()
+
/obj/item/borg/upgrade/selfrepair/proc/deactivate_sr()
STOP_PROCESSING(SSobj, src)
on = FALSE
@@ -399,7 +388,9 @@
repair_tick = 1
return
- if(cyborg && (cyborg.stat != DEAD) && on)
+ var/mob/living/silicon/robot/cyborg = toggle_action.owner
+
+ if(istype(cyborg) && (cyborg.stat != DEAD) && on)
if(!cyborg.cell)
to_chat(cyborg, "Self-repair module deactivated. Please, insert the power cell.")
deactivate_sr()
diff --git a/code/game/objects/items/shields.dm b/code/game/objects/items/shields.dm
index c23622ad65..3be698dd3a 100644
--- a/code/game/objects/items/shields.dm
+++ b/code/game/objects/items/shields.dm
@@ -1,12 +1,16 @@
/obj/item/shield
name = "shield"
+ icon = 'icons/obj/items_and_weapons.dmi'
block_chance = 50
armor = list("melee" = 50, "bullet" = 50, "laser" = 50, "energy" = 0, "bomb" = 30, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 70)
+ var/transparent = FALSE // makes beam projectiles pass through the shield
+
+/obj/item/shield/proc/on_shield_block(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", damage = 0, attack_type = MELEE_ATTACK)
+ return TRUE
/obj/item/shield/riot
name = "riot shield"
desc = "A shield adept at blocking blunt objects from connecting with the torso of the shield wielder."
- icon = 'icons/obj/items_and_weapons.dmi'
icon_state = "riot"
lefthand_file = 'icons/mob/inhands/equipment/shields_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/shields_righthand.dmi'
@@ -19,6 +23,19 @@
materials = list(MAT_GLASS=7500, MAT_METAL=1000)
attack_verb = list("shoved", "bashed")
var/cooldown = 0 //shield bash cooldown. based on world.time
+ transparent = TRUE
+ max_integrity = 75
+
+/obj/item/shield/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
+ if(transparent && (hitby.pass_flags & PASSGLASS))
+ return FALSE
+ if(attack_type == THROWN_PROJECTILE_ATTACK)
+ final_block_chance += 30
+ if(attack_type == LEAP_ATTACK)
+ final_block_chance = 100
+ . = ..()
+ if(.)
+ on_shield_block(owner, hitby, attack_text, damage, attack_type)
/obj/item/shield/riot/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/melee/baton))
@@ -26,14 +43,40 @@
user.visible_message("[user] bashes [src] with [W]!")
playsound(user.loc, 'sound/effects/shieldbash.ogg', 50, 1)
cooldown = world.time
+ else if(istype(W, /obj/item/stack/sheet/mineral/titanium))
+ if(obj_integrity >= max_integrity)
+ to_chat(user, "[src] is already in perfect condition.")
+ else
+ var/obj/item/stack/sheet/mineral/titanium/T = W
+ T.use(1)
+ obj_integrity = max_integrity
+ to_chat(user, "You repair [src] with [T].")
else
return ..()
-/obj/item/shield/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
- if(attack_type == THROWN_PROJECTILE_ATTACK)
- final_block_chance += 30
- if(attack_type == LEAP_ATTACK)
- final_block_chance = 100
+/obj/item/shield/riot/examine(mob/user)
+ . = ..()
+ var/healthpercent = round((obj_integrity/max_integrity) * 100, 1)
+ switch(healthpercent)
+ if(50 to 99)
+ . += "It looks slightly damaged."
+ if(25 to 50)
+ . += "It appears heavily damaged."
+ if(0 to 25)
+ . += "It's falling apart!"
+
+/obj/item/shield/riot/proc/shatter(mob/living/carbon/human/owner)
+ playsound(owner, 'sound/effects/glassbr3.ogg', 100)
+ new /obj/item/shard((get_turf(src)))
+
+/obj/item/shield/riot/on_shield_block(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", damage = 0, attack_type = MELEE_ATTACK)
+ if(obj_integrity <= damage)
+ var/turf/T = get_turf(owner)
+ T.visible_message("[hitby] destroys [src]!")
+ shatter(owner)
+ qdel(src)
+ return FALSE
+ take_damage(damage)
return ..()
/obj/item/shield/riot/roman
@@ -43,11 +86,18 @@
item_state = "roman_shield"
lefthand_file = 'icons/mob/inhands/equipment/shields_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/shields_righthand.dmi'
+ transparent = FALSE
+ max_integrity = 65
/obj/item/shield/riot/roman/fake
desc = "Bears an inscription on the inside: \"Romanes venio domus\". It appears to be a bit flimsy."
block_chance = 0
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0)
+ max_integrity = 30
+
+/obj/item/shield/riot/roman/shatter(mob/living/carbon/human/owner)
+ playsound(owner, 'sound/effects/grillehit.ogg', 100)
+ new /obj/item/stack/sheet/metal(get_turf(src))
/obj/item/shield/riot/buckler
name = "wooden buckler"
@@ -59,11 +109,17 @@
materials = list()
resistance_flags = FLAMMABLE
block_chance = 30
+ transparent = FALSE
+ max_integrity = 55
+
+/obj/item/shield/riot/buckler/shatter(mob/living/carbon/human/owner)
+ playsound(owner, 'sound/effects/bang.ogg', 50)
+ new /obj/item/stack/sheet/mineral/wood(get_turf(src))
+
/obj/item/shield/energy
name = "energy combat shield"
desc = "A shield that reflects almost all energy projectiles, but is useless against physical attacks. It can be retracted, expanded, and stored anywhere."
- icon = 'icons/obj/items_and_weapons.dmi'
lefthand_file = 'icons/mob/inhands/equipment/shields_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/shields_righthand.dmi'
w_class = WEIGHT_CLASS_TINY
@@ -91,7 +147,7 @@
/obj/item/shield/energy/attack_self(mob/living/carbon/human/user)
if(clumsy_check && HAS_TRAIT(user, TRAIT_CLUMSY) && prob(50))
- to_chat(user, "You beat yourself in the head with [src].")
+ to_chat(user, "You beat yourself in the head with [src]!")
user.take_bodypart_damage(5)
active = !active
icon_state = "[base_icon_state][active]"
@@ -101,21 +157,20 @@
throwforce = on_throwforce
throw_speed = on_throw_speed
w_class = WEIGHT_CLASS_BULKY
- playsound(user, 'sound/weapons/saberon.ogg', 35, 1)
+ playsound(user, 'sound/weapons/saberon.ogg', 35, TRUE)
to_chat(user, "[src] is now active.")
else
force = initial(force)
throwforce = initial(throwforce)
throw_speed = initial(throw_speed)
w_class = WEIGHT_CLASS_TINY
- playsound(user, 'sound/weapons/saberoff.ogg', 35, 1)
+ playsound(user, 'sound/weapons/saberoff.ogg', 35, TRUE)
to_chat(user, "[src] can now be concealed.")
add_fingerprint(user)
/obj/item/shield/riot/tele
name = "telescopic shield"
desc = "An advanced riot shield made of lightweight materials that collapses for easy storage."
- icon = 'icons/obj/items_and_weapons.dmi'
icon_state = "teleriot0"
lefthand_file = 'icons/mob/inhands/equipment/shields_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/shields_righthand.dmi'
@@ -135,7 +190,7 @@
/obj/item/shield/riot/tele/attack_self(mob/living/user)
active = !active
icon_state = "teleriot[active]"
- playsound(src.loc, 'sound/weapons/batonextend.ogg', 50, 1)
+ playsound(src.loc, 'sound/weapons/batonextend.ogg', 50, TRUE)
if(active)
force = 8
@@ -181,3 +236,4 @@
throwforce = 15 //Massive pice of metal
w_class = WEIGHT_CLASS_HUGE
item_flags = SLOWS_WHILE_IN_HAND
+ transparent = FALSE
diff --git a/code/game/objects/items/stacks/sheets/glass.dm b/code/game/objects/items/stacks/sheets/glass.dm
index 21de34faa1..934273ae46 100644
--- a/code/game/objects/items/stacks/sheets/glass.dm
+++ b/code/game/objects/items/stacks/sheets/glass.dm
@@ -1,7 +1,11 @@
/* Glass stack types
* Contains:
* Glass sheets
+ * Plasma glass
* Reinforced glass sheets
+ * Reinforced plasma glass
+ * Titanium glass
+ * Plastitanium glass
* Glass shards - TODO: Move this into code/game/object/item/weapons
*/
@@ -254,6 +258,9 @@ GLOBAL_LIST_INIT(plastitaniumglass_recipes, list(
merge_type = /obj/item/stack/sheet/plastitaniumglass
shard_type = /obj/item/shard
+/obj/item/stack/sheet/plastitaniumglass/fifty
+ amount = 50
+
/obj/item/stack/sheet/plastitaniumglass/Initialize(mapload, new_amount, merge = TRUE)
recipes = GLOB.plastitaniumglass_recipes
return ..()
@@ -373,4 +380,8 @@ GLOBAL_LIST_INIT(plastitaniumglass_recipes, list(
throwforce = 11
icon_state = "plasmalarge"
materials = list(MAT_PLASMA=MINERAL_MATERIAL_AMOUNT * 0.5, MAT_GLASS=MINERAL_MATERIAL_AMOUNT)
- icon_prefix = "plasma"
\ No newline at end of file
+ icon_prefix = "plasma"
+
+/obj/item/shard/plasma/alien
+ name = "alien shard"
+ desc = "A nasty looking shard of advanced alloy glass."
\ No newline at end of file
diff --git a/code/game/objects/items/stacks/sheets/mineral.dm b/code/game/objects/items/stacks/sheets/mineral.dm
index 53b78b2ecf..6ff6cdfbbf 100644
--- a/code/game/objects/items/stacks/sheets/mineral.dm
+++ b/code/game/objects/items/stacks/sheets/mineral.dm
@@ -323,6 +323,9 @@ GLOBAL_LIST_INIT(titanium_recipes, list ( \
point_value = 45
merge_type = /obj/item/stack/sheet/mineral/plastitanium
+/obj/item/stack/sheet/mineral/plastitanium/fifty
+ amount = 50
+
GLOBAL_LIST_INIT(plastitanium_recipes, list ( \
new/datum/stack_recipe("plastitanium tile", /obj/item/stack/tile/mineral/plastitanium, 1, 4, 20), \
))
@@ -401,6 +404,8 @@ GLOBAL_LIST_INIT(abductor_recipes, list ( \
new/datum/stack_recipe("alien bed", /obj/structure/bed/abductor, 2, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("alien locker", /obj/structure/closet/abductor, 2, time = 15, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("alien table frame", /obj/structure/table_frame/abductor, 1, time = 15, one_per_turf = 1, on_floor = 1), \
+ new/datum/stack_recipe("alien bar stool", /obj/item/chair/stool/bar/alien, 1, time = 20, one_per_turf = 1, on_floor = 1), \
+ new/datum/stack_recipe("alien stool", /obj/item/chair/stool/alien, 1, time = 20, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("alien airlock assembly", /obj/structure/door_assembly/door_assembly_abductor, 4, time = 20, one_per_turf = 1, on_floor = 1), \
null, \
new/datum/stack_recipe("alien floor tile", /obj/item/stack/tile/mineral/abductor, 1, 4, 20), \
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index e958345d4f..64875a4cc3 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -51,6 +51,7 @@ GLOBAL_LIST_INIT(metal_recipes, list ( \
new/datum/stack_recipe("closet", /obj/structure/closet, 2, time = 15, one_per_turf = TRUE, on_floor = TRUE), \
null, \
new/datum/stack_recipe("canister", /obj/machinery/portable_atmospherics/canister, 10, time = 15, one_per_turf = TRUE, on_floor = TRUE), \
+ new/datum/stack_recipe("trash bin", /obj/structure/closet/crate/bin, 10, time = 15, one_per_turf = TRUE, on_floor = TRUE),\
null, \
new/datum/stack_recipe("floor tile", /obj/item/stack/tile/plasteel, 1, 4, 20), \
new/datum/stack_recipe("metal rod", /obj/item/stack/rods, 1, 2, 60), \
@@ -154,6 +155,7 @@ GLOBAL_LIST_INIT(metal_recipes, list ( \
GLOBAL_LIST_INIT(plasteel_recipes, list ( \
new/datum/stack_recipe("AI core", /obj/structure/AIcore, 4, time = 50, one_per_turf = TRUE), \
new/datum/stack_recipe("bomb assembly", /obj/machinery/syndicatebomb/empty, 10, time = 50), \
+ new/datum/stack_recipe("crate", /obj/structure/closet/crate, 5, time = 90, one_per_turf = TRUE), \
null, \
new /datum/stack_recipe_list("airlock assemblies", list( \
new/datum/stack_recipe("high security airlock assembly", /obj/structure/door_assembly/door_assembly_highsecurity, 6, time = 50, one_per_turf = 1, on_floor = 1), \
@@ -309,6 +311,15 @@ GLOBAL_LIST_INIT(bamboo_recipes, list ( \
recipes = GLOB.bamboo_recipes
return ..()
+/obj/item/stack/sheet/mineral/bamboo/ten
+ amount = 10
+
+/obj/item/stack/sheet/mineral/bamboo/twenty
+ amount = 20
+
+/obj/item/stack/sheet/mineral/bamboo/fifty
+ amount = 50
+
/*
* Cloth
*/
@@ -365,6 +376,14 @@ GLOBAL_LIST_INIT(cloth_recipes, list ( \
/*
* Silk
*/
+
+ GLOBAL_LIST_INIT(silk_recipes, list ( \
+ new/datum/stack_recipe("white jumpsuit", /obj/item/clothing/under/color/white, 4, time = 40), \
+ new/datum/stack_recipe("white gloves", /obj/item/clothing/gloves/color/white, 2, time = 40), \
+ null, \
+ new/datum/stack_recipe("silk string", /obj/item/weaponcrafting/silkstring, 2, time = 40), \
+ ))
+
/obj/item/stack/sheet/silk
name = "silk"
desc = "A long soft material. This one is just made out of cotton rather then any spiders or wyrms"
@@ -374,14 +393,14 @@ GLOBAL_LIST_INIT(cloth_recipes, list ( \
novariants = TRUE
merge_type = /obj/item/stack/sheet/silk
-//obj/item/stack/sheet/silk/Initialize(mapload, new_amount, merge = TRUE)
-// recipes = GLOB.silk_recipes
-// return ..()
+/obj/item/stack/sheet/silk/Initialize(mapload, new_amount, merge = TRUE)
+ recipes = GLOB.silk_recipes
+ return ..()
/*
* Durathread
*/
- GLOBAL_LIST_INIT(durathread_recipes, list ( \
+GLOBAL_LIST_INIT(durathread_recipes, list ( \
new/datum/stack_recipe("durathread jumpsuit", /obj/item/clothing/under/durathread, 4, time = 40),
new/datum/stack_recipe("durathread beret", /obj/item/clothing/head/beret/durathread, 2, time = 40), \
new/datum/stack_recipe("durathread beanie", /obj/item/clothing/head/beanie/durathread, 2, time = 40), \
@@ -688,6 +707,16 @@ GLOBAL_LIST_INIT(bronze_recipes, list ( \
/*
* Bones
*/
+
+GLOBAL_LIST_INIT(bone_recipes, list(
+ new /datum/stack_recipe("Bone Dagger", /obj/item/kitchen/knife/combat/bone, 2, time = 20), \
+ new /datum/stack_recipe("Skull Helmet", /obj/item/clothing/head/helmet/skull, 4, time = 30), \
+ new /datum/stack_recipe("Bone Armor", /obj/item/clothing/suit/armor/bone, 6, time = 30)))
+
+/obj/item/stack/sheet/bone/Initialize(mapload, new_amount, merge = TRUE)
+ recipes = GLOB.bone_recipes
+ . = ..()
+
/obj/item/stack/sheet/bone
name = "bones"
icon = 'icons/obj/mining.dmi'
@@ -704,12 +733,15 @@ GLOBAL_LIST_INIT(bronze_recipes, list ( \
grind_results = list(/datum/reagent/carbon = 10)
merge_type = /obj/item/stack/sheet/bone
+ /*
+ * Plastic
+ */
+
GLOBAL_LIST_INIT(plastic_recipes, list(
new /datum/stack_recipe("see-through plastic flaps", /obj/structure/plasticflaps, 5, one_per_turf = TRUE, on_floor = TRUE, time = 40), \
new /datum/stack_recipe("opaque plastic flaps", /obj/structure/plasticflaps/opaque, 5, one_per_turf = TRUE, on_floor = TRUE, time = 40), \
new /datum/stack_recipe("water bottle", /obj/item/reagent_containers/glass/beaker/waterbottle/empty), \
new /datum/stack_recipe("large water bottle", /obj/item/reagent_containers/glass/beaker/waterbottle/large/empty,3), \
- new /datum/stack_recipe("large trash cart", /obj/structure/closet/crate/bin,50),\
new /datum/stack_recipe("wet floor sign", /obj/item/caution, 2)))
/obj/item/stack/sheet/plastic
diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm
index 324b40ac7f..36c4992e8f 100644
--- a/code/game/objects/items/stacks/stack.dm
+++ b/code/game/objects/items/stacks/stack.dm
@@ -399,11 +399,14 @@
. = ..()
/obj/item/stack/proc/copy_evidences(obj/item/stack/from)
- blood_DNA = from.blood_DNA
- fingerprints = from.fingerprints
- fingerprintshidden = from.fingerprintshidden
- fingerprintslast = from.fingerprintslast
- //TODO bloody overlay
+ if(from.blood_DNA)
+ blood_DNA = from.blood_DNA.Copy()
+ if(from.fingerprints)
+ fingerprints = from.fingerprints.Copy()
+ if(from.fingerprintshidden)
+ fingerprintshidden = from.fingerprintshidden.Copy()
+ if(from.fingerprintslast)
+ fingerprintslast = from.fingerprintslast
/obj/item/stack/microwave_act(obj/machinery/microwave/M)
if(istype(M) && M.dirty < 100)
diff --git a/code/game/objects/items/storage/backpack.dm b/code/game/objects/items/storage/backpack.dm
index 63c8fd9faf..cee8e0f3ee 100644
--- a/code/game/objects/items/storage/backpack.dm
+++ b/code/game/objects/items/storage/backpack.dm
@@ -528,6 +528,31 @@
for(var/i in 1 to 9)
new /obj/item/ammo_box/magazine/smgm45(src)
+/obj/item/storage/backpack/duffelbag/syndie/ammo/dark_gygax
+ desc = "A large duffel bag, packed to the brim with various exosuit ammo."
+
+/obj/item/storage/backpack/duffelbag/syndie/ammo/dark_gygax/PopulateContents()
+ new /obj/item/mecha_ammo/incendiary(src)
+ new /obj/item/mecha_ammo/incendiary(src)
+ new /obj/item/mecha_ammo/incendiary(src)
+ new /obj/item/mecha_ammo/flashbang(src)
+ new /obj/item/mecha_ammo/flashbang(src)
+ new /obj/item/mecha_ammo/flashbang(src)
+
+/obj/item/storage/backpack/duffelbag/syndie/ammo/mauler
+ desc = "A large duffel bag, packed to the brim with various exosuit ammo."
+
+/obj/item/storage/backpack/duffelbag/syndie/ammo/mauler/PopulateContents()
+ new /obj/item/mecha_ammo/lmg(src)
+ new /obj/item/mecha_ammo/lmg(src)
+ new /obj/item/mecha_ammo/lmg(src)
+ new /obj/item/mecha_ammo/scattershot(src)
+ new /obj/item/mecha_ammo/scattershot(src)
+ new /obj/item/mecha_ammo/scattershot(src)
+ new /obj/item/mecha_ammo/missiles_he(src)
+ new /obj/item/mecha_ammo/missiles_he(src)
+ new /obj/item/mecha_ammo/missiles_he(src)
+
/obj/item/storage/backpack/duffelbag/syndie/c20rbundle
desc = "A large duffel bag containing a C-20r, some magazines, and a cheap looking suppressor."
diff --git a/code/game/objects/items/storage/bags.dm b/code/game/objects/items/storage/bags.dm
index 028f36b1d1..67e54128a4 100644
--- a/code/game/objects/items/storage/bags.dm
+++ b/code/game/objects/items/storage/bags.dm
@@ -399,10 +399,12 @@
/obj/item/storage/bag/bio/ComponentInitialize()
. = ..()
var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_w_class = WEIGHT_CLASS_NORMAL //Allows you to pick up Lungs, Liver, and Stomach
STR.max_combined_w_class = 200
STR.max_items = 25
STR.insert_preposition = "in"
- STR.can_hold = typecacheof(list(/obj/item/slime_extract, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/blood, /obj/item/reagent_containers/hypospray/medipen, /obj/item/reagent_containers/food/snacks/deadmouse, /obj/item/reagent_containers/food/snacks/monkeycube))
+ STR.can_hold = typecacheof(list(/obj/item/slime_extract, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/blood, /obj/item/reagent_containers/hypospray/medipen, /obj/item/reagent_containers/food/snacks/deadmouse, /obj/item/reagent_containers/food/snacks/monkeycube, /obj/item/organ, /obj/item/reagent_containers/food/snacks/meat/slab, /obj/item/bodypart))
+ STR.cant_hold = typecacheof(list(/obj/item/organ/brain, /obj/item/organ/liver/cybernetic, /obj/item/organ/heart/cybernetic, /obj/item/organ/lungs/cybernetic, /obj/item/organ/tongue/cybernetic, /obj/item/organ/ears/cybernetic, /obj/item/organ/eyes/robotic, /obj/item/organ/cyberimp))
/obj/item/storage/bag/bio/holding
name = "bio bag of holding"
diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm
index 082cbe245d..25dd746008 100755
--- a/code/game/objects/items/storage/belt.dm
+++ b/code/game/objects/items/storage/belt.dm
@@ -179,17 +179,23 @@
/obj/item/pinpointer/crew
))
-
/obj/item/storage/belt/medical/surgery_belt_adv
name = "surgical supply belt"
desc = "A specialized belt designed for holding surgical equipment. It seems to have specific pockets for each and every surgical tool you can think of."
content_overlays = FALSE
+ var/advanced_drapes = FALSE
/obj/item/storage/belt/medical/surgery_belt_adv/PopulateContents()
new /obj/item/scalpel/advanced(src)
new /obj/item/retractor/advanced(src)
new /obj/item/surgicaldrill/advanced(src)
- new /obj/item/surgical_drapes(src)
+ if(advanced_drapes)
+ new /obj/item/surgical_drapes/advanced(src)
+ else
+ new /obj/item/surgical_drapes(src)
+
+/obj/item/storage/belt/medical/surgery_belt_adv/cmo
+ advanced_drapes = TRUE
/obj/item/storage/belt/security
name = "security belt"
@@ -575,6 +581,7 @@
/obj/item/key/janitor,
/obj/item/clothing/gloves,
/obj/item/melee/flyswatter,
+ /obj/item/twohanded/broom,
/obj/item/paint/paint_remover,
/obj/item/assembly/mousetrap,
/obj/item/screwdriver,
@@ -612,6 +619,21 @@
/obj/item/ammo_casing
))
+/obj/item/storage/belt/quiver
+ name = "leather quiver"
+ desc = "A quiver made from the hide of some animal. Used to hold arrows."
+ icon_state = "quiver"
+ item_state = "quiver"
+
+/obj/item/storage/belt/quiver/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_items = 15
+ STR.display_numerical_stacking = TRUE
+ STR.can_hold = typecacheof(list(
+ /obj/item/ammo_casing/caseless/arrow
+ ))
+
/obj/item/storage/belt/medolier
name = "medolier"
desc = "A medical bandolier for holding smartdarts."
diff --git a/code/game/objects/items/storage/book.dm b/code/game/objects/items/storage/book.dm
index e47021c1f6..311657aba9 100644
--- a/code/game/objects/items/storage/book.dm
+++ b/code/game/objects/items/storage/book.dm
@@ -173,23 +173,25 @@ GLOBAL_LIST_INIT(bibleitemstates, list("bible", "koran", "scrapbook", "bible",
var/unholy2clean = A.reagents.get_reagent_amount(/datum/reagent/fuel/unholywater)
A.reagents.del_reagent(/datum/reagent/fuel/unholywater)
A.reagents.add_reagent(/datum/reagent/water/holywater,unholy2clean)
- if(istype(A, /obj/item/twohanded/required/cult_bastard) && !iscultist(user))
- var/obj/item/twohanded/required/cult_bastard/sword = A
- to_chat(user, "You begin to exorcise [sword].")
+ if(istype(A, /obj/item/twohanded/required/cult_bastard) || istype(A, /obj/item/melee/cultblade) && !iscultist(user))
+ to_chat(user, "You begin to exorcise [A].")
playsound(src,'sound/hallucinations/veryfar_noise.ogg',40,1)
- if(do_after(user, 40, target = sword))
+ if(do_after(user, 40, target = A))
playsound(src,'sound/effects/pray_chaplain.ogg',60,1)
- for(var/obj/item/soulstone/SS in sword.contents)
- SS.usability = TRUE
- for(var/mob/living/simple_animal/shade/EX in SS)
- SSticker.mode.remove_cultist(EX.mind, 1, 0)
- EX.icon_state = "ghost1"
- EX.name = "Purified [EX.name]"
- SS.release_shades(user)
- qdel(SS)
- new /obj/item/nullrod/claymore(get_turf(sword))
- user.visible_message("[user] has purified the [sword]!")
- qdel(sword)
+ if(istype(A, /obj/item/twohanded/required/cult_bastard))
+ for(var/obj/item/soulstone/SS in A.contents)
+ SS.usability = TRUE
+ for(var/mob/living/simple_animal/shade/EX in SS)
+ SSticker.mode.remove_cultist(EX.mind, 1, 0)
+ EX.icon_state = "ghost1"
+ EX.name = "Purified [EX.name]"
+ SS.release_shades(user)
+ qdel(SS)
+ new /obj/item/nullrod/claymore(get_turf(A))
+ else
+ new /obj/item/claymore/purified(get_turf(A))
+ user.visible_message("[user] has purified [A]!")
+ qdel(A)
else if(istype(A, /obj/item/soulstone) && !iscultist(user))
var/obj/item/soulstone/SS = A
@@ -244,3 +246,6 @@ GLOBAL_LIST_INIT(bibleitemstates, list("bible", "koran", "scrapbook", "bible",
/obj/item/storage/book/bible/syndicate/add_blood_DNA(list/blood_dna)
return FALSE
+
+/obj/item/storage/book/bible/syndicate/empty
+ uses = 0
diff --git a/code/game/objects/items/storage/boxes.dm b/code/game/objects/items/storage/boxes.dm
index d5aa43148c..c4e6c57318 100644
--- a/code/game/objects/items/storage/boxes.dm
+++ b/code/game/objects/items/storage/boxes.dm
@@ -322,6 +322,26 @@
for(var/i in 1 to 5)
new /obj/item/grenade/empgrenade(src)
+/obj/item/storage/box/minibombs
+ name = "box of syndicate minibombs"
+ desc = "A box containing 2 highly explosive syndicate minibombs."
+ icon_state = "syndiebox"
+ illustration = "frag"
+
+/obj/item/storage/box/minibombs/PopulateContents()
+ new /obj/item/grenade/syndieminibomb(src)
+ new /obj/item/grenade/syndieminibomb(src)
+
+/obj/item/storage/box/bombananas
+ name = "box of bombananas"
+ desc = "A box containing 2 highly explosive bombananas. Discard peel at enemy after consumption."
+ icon_state = "syndiebox"
+ illustration = "frag"
+
+/obj/item/storage/box/bombananas/PopulateContents()
+ new /obj/item/reagent_containers/food/snacks/grown/banana/bombanana(src)
+ new /obj/item/reagent_containers/food/snacks/grown/banana/bombanana(src)
+
/obj/item/storage/box/trackimp
name = "boxed tracking implant kit"
desc = "Box full of scum-bag tracking utensils."
@@ -1267,4 +1287,63 @@
/obj/item/storage/box/marshmallow/PopulateContents()
for (var/i in 1 to 5)
- new /obj/item/reagent_containers/food/snacks/marshmallow(src)
\ No newline at end of file
+ new /obj/item/reagent_containers/food/snacks/marshmallow(src)
+
+/obj/item/storage/box/material/PopulateContents() //less uranium because radioactive
+ var/static/items_inside = list(
+ /obj/item/stack/sheet/metal/fifty=1,\
+ /obj/item/stack/sheet/glass/fifty=1,\
+ /obj/item/stack/sheet/rglass=50,\
+ /obj/item/stack/sheet/plasmaglass=50,\
+ /obj/item/stack/sheet/titaniumglass=50,\
+ /obj/item/stack/sheet/plastitaniumglass=50,\
+ /obj/item/stack/sheet/plasteel=50,\
+ /obj/item/stack/sheet/mineral/plastitanium=50,\
+ /obj/item/stack/sheet/mineral/titanium=50,\
+ /obj/item/stack/sheet/mineral/gold=50,\
+ /obj/item/stack/sheet/mineral/silver=50,\
+ /obj/item/stack/sheet/mineral/plasma=50,\
+ /obj/item/stack/sheet/mineral/uranium=50,\
+ /obj/item/stack/sheet/mineral/diamond=50,\
+ /obj/item/stack/sheet/bluespace_crystal=50,\
+ /obj/item/stack/sheet/mineral/bananium=50,\
+ /obj/item/stack/sheet/mineral/wood=50,\
+ /obj/item/stack/sheet/plastic/fifty=1,\
+ /obj/item/stack/sheet/runed_metal/fifty=1
+ )
+ generate_items_inside(items_inside, src)
+
+/obj/item/storage/box/debugtools
+ name = "box of debug tools"
+ icon_state = "syndiebox"
+
+/obj/item/storage/box/debugtools/PopulateContents()
+ var/static/items_inside = list(
+ /obj/item/flashlight/emp/debug=1,\
+ /obj/item/pda=1,\
+ /obj/item/modular_computer/tablet/preset/advanced=1,\
+ /obj/item/geiger_counter=1,\
+ /obj/item/construction/rcd/combat/admin=1,\
+ /obj/item/pipe_dispenser=1,\
+ /obj/item/card/emag=1,\
+ /obj/item/healthanalyzer/advanced=1,\
+ /obj/item/disk/tech_disk/debug=1,\
+ /obj/item/uplink/debug=1,\
+ /obj/item/uplink/nuclear/debug=1,\
+ /obj/item/storage/box/beakers/bluespace=1,\
+ /obj/item/storage/box/beakers/variety=1,\
+ /obj/item/storage/box/material=1,\
+ /obj/item/storage/belt/medical/surgery_belt_adv
+ )
+ generate_items_inside(items_inside, src)
+
+/obj/item/storage/box/beakers/variety
+ name = "beaker variety box"
+
+/obj/item/storage/box/beakers/variety/PopulateContents()
+ new /obj/item/reagent_containers/glass/beaker(src)
+ new /obj/item/reagent_containers/glass/beaker/large(src)
+ new /obj/item/reagent_containers/glass/beaker/plastic(src)
+ new /obj/item/reagent_containers/glass/beaker/meta(src)
+ new /obj/item/reagent_containers/glass/beaker/noreact(src)
+ new /obj/item/reagent_containers/glass/beaker/bluespace(src)
diff --git a/code/game/objects/items/storage/fancy.dm b/code/game/objects/items/storage/fancy.dm
index 78f306df6e..92e09022b2 100644
--- a/code/game/objects/items/storage/fancy.dm
+++ b/code/game/objects/items/storage/fancy.dm
@@ -12,6 +12,7 @@
* Cigarette Box
* Cigar Case
* Heart Shaped Box w/ Chocolates
+ * Ring Box
*/
/obj/item/storage/fancy
@@ -352,3 +353,33 @@
var/datum/component/storage/STR = GetComponent(/datum/component/storage)
STR.max_items = 8
STR.can_hold = typecacheof(list(/obj/item/reagent_containers/food/snacks/tinychocolate))
+
+/*
+ * Ring Box
+ */
+
+/obj/item/storage/fancy/ringbox
+ name = "ring box"
+ desc = "A tiny box covered in soft red felt made for holding rings."
+ icon = 'icons/obj/ring.dmi'
+ icon_state = "gold ringbox"
+ icon_type = "gold ring"
+ w_class = WEIGHT_CLASS_TINY
+ spawn_type = /obj/item/clothing/gloves/ring
+
+/obj/item/storage/fancy/ringbox/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_items = 1
+ STR.can_hold = typecacheof(list(/obj/item/clothing/gloves/ring))
+
+/obj/item/storage/fancy/ringbox/diamond
+ icon_state = "diamond ringbox"
+ icon_type = "diamond ring"
+ spawn_type = /obj/item/clothing/gloves/ring/diamond
+
+/obj/item/storage/fancy/ringbox/silver
+ icon_state = "silver ringbox"
+ icon_type = "silver ring"
+ spawn_type = /obj/item/clothing/gloves/ring/silver
+
diff --git a/code/game/objects/items/storage/toolbox.dm b/code/game/objects/items/storage/toolbox.dm
index 880b714f76..bfffe0fc60 100644
--- a/code/game/objects/items/storage/toolbox.dm
+++ b/code/game/objects/items/storage/toolbox.dm
@@ -35,7 +35,7 @@ GLOBAL_LIST_EMPTY(rubber_toolbox_icons)
/obj/item/storage/toolbox/update_icon()
..()
cut_overlays()
- if(blood_DNA && blood_DNA.len)
+ if(length(blood_DNA))
add_blood_overlay()
if(has_latches)
var/icon/I = icon('icons/obj/storage.dmi', latches)
@@ -251,16 +251,12 @@ GLOBAL_LIST_EMPTY(rubber_toolbox_icons)
new /obj/item/ammo_box/a762(src)
new /obj/item/ammo_box/a762(src)
-/obj/item/storage/toolbox/gold_real
+/obj/item/storage/toolbox/plastitanium/gold_real
name = "golden toolbox"
desc = "A larger then normal toolbox made of gold plated plastitanium."
icon_state = "gold"
item_state = "toolbox_gold"
has_latches = FALSE
- force = 16 // Less then a spear
- throwforce = 14
- throw_speed = 5
- throw_range = 10
/obj/item/storage/toolbox/gold_real/PopulateContents()
new /obj/item/screwdriver/nuke(src)
diff --git a/code/game/objects/items/storage/uplink_kits.dm b/code/game/objects/items/storage/uplink_kits.dm
index 35429c2f42..611870912c 100644
--- a/code/game/objects/items/storage/uplink_kits.dm
+++ b/code/game/objects/items/storage/uplink_kits.dm
@@ -343,7 +343,7 @@
new /obj/item/implanter/radio/syndicate(src)
/obj/item/storage/box/syndie_kit/centcom_costume/PopulateContents()
- new /obj/item/clothing/under/rank/centcom_officer(src)
+ new /obj/item/clothing/under/rank/centcom_officer/syndicate(src)
new /obj/item/clothing/shoes/sneakers/black(src)
new /obj/item/clothing/gloves/color/black(src)
new /obj/item/radio/headset/headset_cent/empty(src)
diff --git a/code/game/objects/items/stunbaton.dm b/code/game/objects/items/stunbaton.dm
index 946eb1eb7b..52f082fa5c 100644
--- a/code/game/objects/items/stunbaton.dm
+++ b/code/game/objects/items/stunbaton.dm
@@ -15,13 +15,18 @@
attack_verb = list("beaten")
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 50, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 80)
- var/stunforce = 140
+ var/stamforce = 25
var/status = FALSE
+ var/knockdown = TRUE
var/obj/item/stock_parts/cell/cell
- var/hitcost = 1000
+ var/hitcost = 750
var/throw_hit_chance = 35
var/preload_cell_type //if not empty the baton starts with this type of cell
+/obj/item/melee/baton/examine(mob/user)
+ . = ..()
+ . += "Right click attack while in combat mode to disarm instead of stun."
+
/obj/item/melee/baton/get_cell()
. = cell
if(iscyborg(loc))
@@ -32,7 +37,7 @@
user.visible_message("[user] is putting the live [name] in [user.p_their()] mouth! It looks like [user.p_theyre()] trying to commit suicide!")
return (FIRELOSS)
-/obj/item/melee/baton/Initialize()
+/obj/item/melee/baton/Initialize(mapload)
. = ..()
if(preload_cell_type)
if(!ispath(preload_cell_type,/obj/item/stock_parts/cell))
@@ -48,7 +53,7 @@
baton_stun(hit_atom)
/obj/item/melee/baton/loaded //this one starts with a cell pre-installed.
- preload_cell_type = /obj/item/stock_parts/cell/high
+ preload_cell_type = /obj/item/stock_parts/cell/high/plus
/obj/item/melee/baton/proc/deductcharge(chrgdeductamt, chargecheck = TRUE, explode = TRUE)
var/obj/item/stock_parts/cell/copper_top = get_cell()
@@ -134,44 +139,41 @@
add_fingerprint(user)
/obj/item/melee/baton/attack(mob/M, mob/living/carbon/human/user)
+ var/interrupt = common_baton_melee(M, user, FALSE)
+ if(!interrupt)
+ ..()
+
+/obj/item/melee/baton/alt_pre_attack(atom/A, mob/living/user, params)
+ . = common_baton_melee(A, user, TRUE) //return true (attackchain interrupt) if this also returns true. no harm-disarming.
+ user.changeNext_move(CLICK_CD_MELEE)
+
+//return TRUE to interrupt attack chain.
+/obj/item/melee/baton/proc/common_baton_melee(mob/M, mob/living/user, disarming = FALSE)
+ if(iscyborg(M) || !isliving(M)) //can't baton cyborgs
+ return FALSE
if(status && HAS_TRAIT(user, TRAIT_CLUMSY) && prob(50))
clowning_around(user)
- return
-
- if(user.getStaminaLoss() >= STAMINA_SOFTCRIT)//CIT CHANGE - makes it impossible to baton in stamina softcrit
- to_chat(user, "You're too exhausted for that.")//CIT CHANGE - ditto
- return //CIT CHANGE - ditto
-
- if(iscyborg(M))
- ..()
- return
-
-
+ if(user.getStaminaLoss() >= STAMINA_SOFTCRIT) //CIT CHANGE - makes it impossible to baton in stamina softcrit
+ to_chat(user, "You're too exhausted for that.")
+ return TRUE
if(ishuman(M))
var/mob/living/carbon/human/L = M
if(check_martial_counter(L, user))
- return
+ return TRUE
+ if(status)
+ if(baton_stun(M, user, disarming))
+ user.do_attack_animation(M)
+ user.adjustStaminaLossBuffered(getweight()) //CIT CHANGE - makes stunbatonning others cost stamina
+ else if(user.a_intent != INTENT_HARM) //they'll try to bash in the last proc.
+ M.visible_message("[user] has prodded [M] with [src]. Luckily it was off.", \
+ "[user] has prodded you with [src]. Luckily it was off")
+ return disarming || (user.a_intent != INTENT_HARM)
- if(user.a_intent != INTENT_HARM)
- if(status)
- if(baton_stun(M, user))
- user.do_attack_animation(M)
- user.adjustStaminaLossBuffered(getweight())//CIT CHANGE - makes stunbatonning others cost stamina
- return
- else
- M.visible_message("[user] has prodded [M] with [src]. Luckily it was off.", \
- "[user] has prodded you with [src]. Luckily it was off")
- else
- if(status)
- baton_stun(M, user)
- ..()
-
-
-/obj/item/melee/baton/proc/baton_stun(mob/living/L, mob/user)
+/obj/item/melee/baton/proc/baton_stun(mob/living/L, mob/user, disarming = FALSE)
if(L.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK)) //No message; check_shields() handles that
playsound(L, 'sound/weapons/genhit.ogg', 50, 1)
return FALSE
- var/stunpwr = stunforce
+ var/stunpwr = stamforce
var/obj/item/stock_parts/cell/our_cell = get_cell()
if(!our_cell)
switch_status(FALSE)
@@ -187,17 +189,21 @@
return FALSE
stunpwr *= round(stuncharge/hitcost, 0.1)
-
- L.Knockdown(stunpwr)
- L.adjustStaminaLoss(stunpwr*0.1)//CIT CHANGE - makes stunbatons deal extra staminaloss. Todo: make this also deal pain when pain gets implemented.
- L.apply_effect(EFFECT_STUTTER, stunforce)
+ if(!disarming)
+ if(knockdown)
+ L.Knockdown(50, override_stamdmg = 0) //knockdown
+ L.adjustStaminaLoss(stunpwr)
+ else
+ L.drop_all_held_items() //no knockdown/stamina damage, instead disarm.
+
+ L.apply_effect(EFFECT_STUTTER, stamforce)
SEND_SIGNAL(L, COMSIG_LIVING_MINOR_SHOCK)
if(user)
L.lastattacker = user.real_name
L.lastattackerckey = user.ckey
- L.visible_message("[user] has stunned [L] with [src]!", \
- "[user] has stunned you with [src]!")
- log_combat(user, L, "stunned")
+ L.visible_message("[user] has [disarming? "disarmed" : "stunned"] [L] with [src]!", \
+ "[user] has [disarming? "disarmed" : "stunned"] you with [src]!")
+ log_combat(user, L, disarming? "disarmed" : "stunned")
playsound(loc, 'sound/weapons/egloves.ogg', 50, 1, -1)
@@ -212,7 +218,7 @@
user.visible_message("[user] accidentally hits [user.p_them()]self with [src]!", \
"You accidentally hit yourself with [src]!")
SEND_SIGNAL(user, COMSIG_LIVING_MINOR_SHOCK)
- user.Knockdown(stunforce*3)
+ user.Knockdown(stamforce*6)
playsound(loc, 'sound/weapons/egloves.ogg', 50, 1, -1)
deductcharge(hitcost)
@@ -274,8 +280,9 @@
w_class = WEIGHT_CLASS_BULKY
force = 3
throwforce = 5
- stunforce = 100
- hitcost = 2000
+ stamforce = 25
+ hitcost = 1000
+ knockdown = FALSE
throw_hit_chance = 10
slot_flags = ITEM_SLOT_BACK
var/obj/item/assembly/igniter/sparkler
diff --git a/code/game/objects/items/tanks/tanks.dm b/code/game/objects/items/tanks/tanks.dm
index f763fe87fc..7157103f14 100644
--- a/code/game/objects/items/tanks/tanks.dm
+++ b/code/game/objects/items/tanks/tanks.dm
@@ -33,6 +33,9 @@
H.update_internals_hud_icon(0)
else
if(!H.getorganslot(ORGAN_SLOT_BREATHING_TUBE))
+ if(HAS_TRAIT(H, TRAIT_NO_INTERNALS))
+ to_chat(H, "Due to cumbersome equipment or anatomy, you are currently unable to use internals!")
+ return
var/obj/item/clothing/check
var/internals = FALSE
@@ -156,7 +159,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "tanks", name, 420, 200, master_ui, state)
+ ui = new(user, src, ui_key, "tanks", name, 400, 120, master_ui, state)
ui.open()
/obj/item/tank/ui_data(mob/user)
diff --git a/code/game/objects/items/teleportation.dm b/code/game/objects/items/teleportation.dm
index 8c0f315ca3..347717dd63 100644
--- a/code/game/objects/items/teleportation.dm
+++ b/code/game/objects/items/teleportation.dm
@@ -138,14 +138,17 @@
/obj/item/hand_tele/pre_attack(atom/target, mob/user, params)
if(try_dispel_portal(target, user))
- return FALSE
+ return TRUE
return ..()
-/obj/item/hand_tele/proc/try_dispel_portal(atom/target, mob/user)
- if(is_parent_of_portal(target))
+/obj/item/hand_tele/proc/try_dispel_portal(atom/target, mob/user, delay = 30)
+ var/datum/beam/B = user.Beam(target)
+ if(is_parent_of_portal(target) && (!delay || do_after(user, delay, target = target)))
qdel(target)
to_chat(user, "You dispel [target] with \the [src]!")
+ qdel(B)
return TRUE
+ qdel(B)
return FALSE
/obj/item/hand_tele/afterattack(atom/target, mob/user)
diff --git a/code/game/objects/items/teleprod.dm b/code/game/objects/items/teleprod.dm
index f427bf6c4c..bab4d6a488 100644
--- a/code/game/objects/items/teleprod.dm
+++ b/code/game/objects/items/teleprod.dm
@@ -16,7 +16,7 @@
user.visible_message("[user] accidentally hits [user.p_them()]self with [src]!", \
"You accidentally hit yourself with [src]!")
SEND_SIGNAL(user, COMSIG_LIVING_MINOR_SHOCK)
- user.Knockdown(stunforce*3)
+ user.Knockdown(stamforce * 6)
playsound(loc, 'sound/weapons/egloves.ogg', 50, 1, -1)
if(do_teleport(user, get_turf(user), 50, channel = TELEPORT_CHANNEL_BLUESPACE))
deductcharge(hitcost)
diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm
index 92798528ae..a95eefe1ea 100644
--- a/code/game/objects/items/toys.dm
+++ b/code/game/objects/items/toys.dm
@@ -289,7 +289,7 @@
var/light_brightness = 3
actions_types = list()
-/obj/item/toy/sword/cx/pre_altattackby(atom/A, mob/living/user, params) //checks if it can do right click memes
+/obj/item/toy/sword/cx/alt_pre_attack(atom/A, mob/living/user, params) //checks if it can do right click memes
altafterattack(A, user, TRUE, params)
return TRUE
@@ -453,6 +453,7 @@
force_wielded = 0
attack_verb = list("attacked", "struck", "hit")
total_mass_on = TOTAL_MASS_TOY_SWORD
+ sharpness = IS_BLUNT
/obj/item/twohanded/dualsaber/toy/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
return FALSE
@@ -472,6 +473,7 @@
attack_verb = list("attacked", "struck", "hit")
total_mass_on = TOTAL_MASS_TOY_SWORD
slowdown_wielded = 0
+ sharpness = IS_BLUNT
/obj/item/twohanded/dualsaber/hypereutactic/toy/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
return FALSE
@@ -1012,6 +1014,7 @@
/obj/item/toy/cards/singlecard/examine(mob/user)
+ . = ..()
if(ishuman(user))
var/mob/living/carbon/human/cardUser = user
if(cardUser.is_holding(src))
@@ -1244,7 +1247,7 @@
/obj/item/toy/clockwork_watch/examine(mob/user)
. = ..()
- . += "Station Time: [STATION_TIME_TIMESTAMP("hh:mm:ss")]"
+ . += "Station Time: [STATION_TIME_TIMESTAMP("hh:mm:ss", world.time)]"
/*
* Toy Dagger
diff --git a/code/game/objects/items/twohanded.dm b/code/game/objects/items/twohanded.dm
index 28a6fd66ee..72a5c5b1da 100644
--- a/code/game/objects/items/twohanded.dm
+++ b/code/game/objects/items/twohanded.dm
@@ -38,9 +38,9 @@
wielded = 0
if(!isnull(force_unwielded))
force = force_unwielded
- var/sf = findtext(name," (Wielded)")
+ var/sf = findtext(name, " (Wielded)", -10)//10 == length(" (Wielded)")
if(sf)
- name = copytext(name,1,sf)
+ name = copytext(name, 1, sf)
else //something wrong
name = "[initial(name)]"
update_icon()
@@ -347,7 +347,6 @@
icon_state = "dualsaber[item_color][wielded]"
else
icon_state = "dualsaber0"
-
clean_blood()
/obj/item/twohanded/dualsaber/attack(mob/target, mob/living/carbon/human/user)
@@ -511,7 +510,7 @@
/obj/item/twohanded/dualsaber/hypereutactic/chaplain/IsReflect()
return FALSE
-/obj/item/twohanded/dualsaber/hypereutactic/pre_altattackby(atom/A, mob/living/user, params) //checks if it can do right click memes
+/obj/item/twohanded/dualsaber/hypereutactic/alt_pre_attack(atom/A, mob/living/user, params) //checks if it can do right click memes
altafterattack(A, user, TRUE, params)
return TRUE
@@ -1017,3 +1016,66 @@
C.change_view(CONFIG_GET(string/default_view))
user.client.pixel_x = 0
user.client.pixel_y = 0
+
+/obj/item/twohanded/broom
+ name = "broom"
+ desc = "This is my BROOMSTICK! It can be used manually or braced with two hands to sweep items as you move. It has a telescopic handle for compact storage." //LIES
+ icon = 'icons/obj/janitor.dmi'
+ icon_state = "broom0"
+ lefthand_file = 'icons/mob/inhands/equipment/custodial_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/equipment/custodial_righthand.dmi'
+ force = 8
+ throwforce = 10
+ throw_speed = 3
+ throw_range = 7
+ w_class = WEIGHT_CLASS_NORMAL
+ force_unwielded = 8
+ force_wielded = 12
+ attack_verb = list("swept", "brushed off", "bludgeoned", "whacked")
+ resistance_flags = FLAMMABLE
+
+/obj/item/twohanded/broom/update_icon_state()
+ icon_state = "broom[wielded]"
+
+/obj/item/twohanded/broom/wield(mob/user)
+ . = ..()
+ if(!wielded)
+ return
+ to_chat(user, "You brace the [src] against the ground in a firm sweeping stance.")
+ RegisterSignal(user, COMSIG_MOVABLE_MOVED, .proc/sweep)
+
+/obj/item/twohanded/broom/unwield(mob/user)
+ . = ..()
+ UnregisterSignal(user, COMSIG_MOVABLE_MOVED)
+
+/obj/item/twohanded/broom/afterattack(atom/A, mob/user, proximity)
+ . = ..()
+ if(!proximity)
+ return
+ sweep(user, A, FALSE)
+
+/obj/item/twohanded/broom/proc/sweep(mob/user, atom/A, moving = TRUE)
+ var/turf/target
+ if (!moving)
+ if (isturf(A))
+ target = A
+ else
+ target = A.loc
+ else
+ target = user.loc
+ if (locate(/obj/structure/table) in target.contents)
+ return
+ var/i = 0
+ for(var/obj/item/garbage in target.contents)
+ if(!garbage.anchored)
+ garbage.Move(get_step(target, user.dir), user.dir)
+ i++
+ if(i >= 20)
+ break
+ if(i >= 1)
+ playsound(loc, 'sound/weapons/thudswoosh.ogg', 5, TRUE, -1)
+
+/obj/item/twohanded/broom/proc/janicart_insert(mob/user, obj/structure/janitorialcart/J) //bless you whoever fixes this copypasta
+ J.put_in_cart(src, user)
+ J.mybroom=src
+ J.update_icon()
\ No newline at end of file
diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm
index b7b25f0b5a..ea9899b2c2 100644
--- a/code/game/objects/items/weaponry.dm
+++ b/code/game/objects/items/weaponry.dm
@@ -79,6 +79,11 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
user.visible_message("[user] is falling on [src]! It looks like [user.p_theyre()] trying to commit suicide!")
return(BRUTELOSS)
+/obj/item/claymore/purified
+ name = "purified longsword"
+ desc = "A hastily-purified longsword. While not as holy as it could be, it's still a formidable weapon against those who would rather see you dead."
+ force = 25
+
/obj/item/claymore/highlander //ALL COMMENTS MADE REGARDING THIS SWORD MUST BE MADE IN ALL CAPS
desc = "THERE CAN BE ONLY ONE, AND IT WILL BE YOU!!!\nActivate it in your hand to point to the nearest victim."
flags_1 = CONDUCT_1
@@ -312,23 +317,27 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
attack_verb = list("stubbed", "poked")
resistance_flags = FIRE_PROOF
var/extended = 0
+ var/extended_force = 20
+ var/extended_throwforce = 23
+ var/extended_icon_state = "switchblade_ext"
+ var/retracted_icon_state = "switchblade"
/obj/item/switchblade/attack_self(mob/user)
extended = !extended
playsound(src.loc, 'sound/weapons/batonextend.ogg', 50, 1)
if(extended)
- force = 20
+ force = extended_force
w_class = WEIGHT_CLASS_NORMAL
- throwforce = 23
- icon_state = "switchblade_ext"
+ throwforce = extended_throwforce
+ icon_state = extended_icon_state
attack_verb = list("slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
hitsound = 'sound/weapons/bladeslice.ogg'
sharpness = IS_SHARP
else
- force = 3
+ force = initial(force)
w_class = WEIGHT_CLASS_SMALL
- throwforce = 5
- icon_state = "switchblade"
+ throwforce = initial(throwforce)
+ icon_state = retracted_icon_state
attack_verb = list("stubbed", "poked")
hitsound = 'sound/weapons/genhit.ogg'
sharpness = IS_BLUNT
@@ -337,6 +346,25 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
user.visible_message("[user] is slitting [user.p_their()] own throat with [src]! It looks like [user.p_theyre()] trying to commit suicide!")
return (BRUTELOSS)
+/obj/item/switchblade/crafted
+ icon_state = "switchblade_ms"
+ desc = "A concealable spring-loaded knife."
+ force = 2
+ throwforce = 3
+ extended_force = 15
+ extended_throwforce = 18
+ extended_icon_state = "switchblade_ext_ms"
+ retracted_icon_state = "switchblade_ms"
+
+/obj/item/switchblade/crafted/attackby(obj/item/I, mob/user, params)
+ . = ..()
+ if(istype(I, /obj/item/stack/sheet/mineral/silver))
+ icon_state = extended ? "switchblade_ext_msf" : "switchblade_msf"
+ extended_icon_state = "switchblade_ext_msf"
+ retracted_icon_state = "switchblade_msf"
+ icon_state = "switchblade_msf"
+ to_chat(user, "You use part of the silver to improve your Switchblade. Stylish!")
+
/obj/item/phone
name = "red phone"
desc = "Should anything ever go wrong..."
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index 5f3fc48eb7..7fbdddbdc4 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -60,8 +60,8 @@
var/flagslist = splittext(set_obj_flags,";")
var/list/string_to_objflag = GLOB.bitfields["obj_flags"]
for (var/flag in flagslist)
- if (findtext(flag,"!",1,2))
- flag = copytext(flag,1-(length(flag))) // Get all but the initial !
+ if(flag[1] == "!")
+ flag = copytext(flag, length(flag[1]) + 1) // Get all but the initial !
obj_flags &= ~string_to_objflag[flag]
else
obj_flags |= string_to_objflag[flag]
@@ -125,7 +125,7 @@
if ((M.client && M.machine == src))
is_in_use = TRUE
ui_interact(M)
- if(isAI(usr) || iscyborg(usr) || IsAdminGhost(usr))
+ if(isAI(usr) || iscyborg(usr) || IsAdminGhost(usr) || hasSiliconAccessInArea(usr))
if (!(usr in nearby))
if (usr.client && usr.machine==src) // && M.machine == src is omitted because if we triggered this by using the dialog, it doesn't matter if our machine changed in between triggering it and this - the dialog is probably still supposed to refresh.
is_in_use = TRUE
@@ -178,7 +178,7 @@
machine = null
//called when the user unsets the machine.
-/atom/movable/proc/on_unset_machine(mob/user)
+/atom/proc/on_unset_machine(mob/user)
return
/mob/proc/set_machine(obj/O)
diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm
index 9246a00399..1d36b2b41b 100644
--- a/code/game/objects/structures.dm
+++ b/code/game/objects/structures.dm
@@ -37,7 +37,7 @@
structureclimber.visible_message("[structureclimber] has been knocked off [src].", "You're knocked off [src]!", "You see [structureclimber] get knocked off [src].")
/obj/structure/ui_act(action, params)
- ..()
+ . = ..()
add_fingerprint(usr)
/obj/structure/MouseDrop_T(atom/movable/O, mob/user)
diff --git a/code/game/objects/structures/ai_core.dm b/code/game/objects/structures/ai_core.dm
index 5b9a764925..fcd1f80a4f 100644
--- a/code/game/objects/structures/ai_core.dm
+++ b/code/game/objects/structures/ai_core.dm
@@ -52,7 +52,7 @@
var/area/A = get_area(src)
if(!A.blob_allowed)
return FALSE
- if(!A.power_equip)
+ if(!A.powered(EQUIP))
return FALSE
if(!SSmapping.level_trait(T.z,ZTRAIT_STATION))
return FALSE
diff --git a/code/game/objects/structures/barsigns.dm b/code/game/objects/structures/barsigns.dm
index 053512a256..b72a4b816e 100644
--- a/code/game/objects/structures/barsigns.dm
+++ b/code/game/objects/structures/barsigns.dm
@@ -305,7 +305,7 @@
/datum/barsign/meow_mix
name = "Meow Mix"
- icon = "meow_mix"
+ icon = "Meow Mix"
desc = "No, we don't serve catnip, officer!"
/datum/barsign/hiddensigns
diff --git a/code/game/objects/structures/beds_chairs/chair.dm b/code/game/objects/structures/beds_chairs/chair.dm
index 9fa4d730b6..cc1a3c0922 100644
--- a/code/game/objects/structures/beds_chairs/chair.dm
+++ b/code/game/objects/structures/beds_chairs/chair.dm
@@ -370,6 +370,43 @@
item_state = "stool_bar"
origin_type = /obj/structure/chair/stool/bar
+//////////////////////////
+//Alien(Disco) Stools!////
+//////////////////////////
+
+/obj/structure/chair/stool/alien
+ name = "alien stool"
+ desc = "A hard stool made of advanced alien alloy."
+ icon_state = "stoolalien"
+ icon = 'icons/obj/abductor.dmi'
+ item_chair = /obj/item/chair/stool/alien
+ buildstacktype = /obj/item/stack/sheet/mineral/abductor
+ buildstackamount = 1
+
+/obj/structure/chair/stool/bar/alien
+ name = "bronze bar stool"
+ desc = "A hard bar stool made of advanced alien alloy."
+ icon_state = "baralien"
+ icon = 'icons/obj/abductor.dmi'
+ item_chair = /obj/item/chair/stool/bar/alien
+ buildstacktype = /obj/item/stack/sheet/mineral/abductor
+ buildstackamount = 1
+
+/obj/item/chair/stool/alien
+ name = "stool"
+ icon_state = "stoolalien_toppled"
+ item_state = "stoolalien"
+ icon = 'icons/obj/abductor.dmi'
+ origin_type = /obj/structure/chair/stool/alien
+ break_chance = 0 //It's too sturdy.
+
+/obj/item/chair/stool/bar/alien
+ name = "bar stool"
+ icon_state = "baralien_toppled"
+ item_state = "baralien"
+ icon = 'icons/obj/abductor.dmi'
+ origin_type = /obj/structure/chair/stool/bar/alien
+
//////////////////////////
//Brass & Bronze stools!//
//////////////////////////
diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm
index 9a095bf69d..daf15832fb 100644
--- a/code/game/objects/structures/crates_lockers/closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets.dm
@@ -532,7 +532,7 @@
/obj/structure/closet/CtrlShiftClick(mob/living/user)
if(!HAS_TRAIT(user, TRAIT_SKITTISH))
return ..()
- if(!user.canUseTopic(src) || !isturf(user.loc))
+ if(!user.canUseTopic(src) || !isturf(user.loc) || !user.Adjacent(src) || !user.CanReach(src))
return
dive_into(user)
diff --git a/code/game/objects/structures/crates_lockers/closets/genpop.dm b/code/game/objects/structures/crates_lockers/closets/genpop.dm
index 80b64aaedc..2b263bb1ed 100644
--- a/code/game/objects/structures/crates_lockers/closets/genpop.dm
+++ b/code/game/objects/structures/crates_lockers/closets/genpop.dm
@@ -95,7 +95,7 @@
if(user.lying && get_dist(src, user) > 0)
return
- if(!broken && registered_id != null && registered_id in user.held_items)
+ if(!broken && registered_id != null && (registered_id in user.held_items))
handle_prisoner_id(user)
return
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/medical.dm b/code/game/objects/structures/crates_lockers/closets/secure/medical.dm
index 3167d8ac18..90b57471b4 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/medical.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/medical.dm
@@ -76,7 +76,7 @@
new /obj/item/door_remote/chief_medical_officer(src)
new /obj/item/clothing/neck/petcollar(src)
new /obj/item/pet_carrier(src)
- new /obj/item/storage/belt/medical/surgery_belt_adv(src)
+ new /obj/item/storage/belt/medical/surgery_belt_adv/cmo(src)
new /obj/item/wallframe/defib_mount(src)
new /obj/item/circuitboard/machine/techfab/department/medical(src)
new /obj/item/storage/photo_album/CMO(src)
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/security.dm b/code/game/objects/structures/crates_lockers/closets/secure/security.dm
index 1517b30d9c..88c8d51479 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm
@@ -2,38 +2,21 @@
name = "\proper captain's locker"
req_access = list(ACCESS_CAPTAIN)
icon_state = "cap"
-/obj/structure/closet/secure_closet/captains/PopulateContents()
+/obj/structure/closet/secure_closet/captains/PopulateContents() //Excess clothing and such can be found in the Captain's Wardrobe. You can also find this file in code/modules/vending/wardrobes.
..()
- new /obj/item/clothing/suit/hooded/wintercoat/captain(src)
- new /obj/item/storage/backpack/captain(src)
- new /obj/item/storage/backpack/satchel/cap(src)
- new /obj/item/storage/backpack/duffelbag/captain(src)
- new /obj/item/clothing/neck/cloak/cap(src)
- new /obj/item/clothing/neck/petcollar(src)
+ new /obj/item/clothing/neck/petcollar(src) //I considered removing the pet stuff too but eh, who knows. We might get Renault back. Plus I guess you could use that collar for... other means. Aren't you supposed to be guarding the disk?
new /obj/item/pet_carrier(src)
- new /obj/item/clothing/shoes/sneakers/brown(src)
- new /obj/item/clothing/under/rank/captain(src)
- new /obj/item/clothing/under/rank/captain/skirt(src)
new /obj/item/clothing/suit/armor/vest/capcarapace(src)
- new /obj/item/clothing/head/caphat(src)
- new /obj/item/clothing/under/captainparade(src)
new /obj/item/clothing/suit/armor/vest/capcarapace/alt(src)
- new /obj/item/clothing/head/caphat/parade(src)
- new /obj/item/clothing/head/caphat/beret(src)
- new /obj/item/clothing/suit/captunic(src)
- new /obj/item/clothing/under/rank/captain/femformal(src) //citadel edit
new /obj/item/clothing/head/crown/fancy(src)
new /obj/item/cartridge/captain(src)
new /obj/item/storage/box/silver_ids(src)
new /obj/item/radio/headset/heads/captain/alt(src)
new /obj/item/radio/headset/heads/captain(src)
- new /obj/item/clothing/glasses/sunglasses/gar/supergar(src)
- new /obj/item/clothing/gloves/color/captain(src)
new /obj/item/storage/belt/sabre(src)
new /obj/item/gun/energy/e_gun(src)
new /obj/item/door_remote/captain(src)
new /obj/item/storage/photo_album/Captain(src)
- new /obj/item/clothing/head/caphat/beret/white(src)
/obj/structure/closet/secure_closet/hop
name = "\proper head of personnel's locker"
diff --git a/code/game/objects/structures/crates_lockers/closets/wardrobe.dm b/code/game/objects/structures/crates_lockers/closets/wardrobe.dm
index 9d554181eb..63c8856ac3 100644
--- a/code/game/objects/structures/crates_lockers/closets/wardrobe.dm
+++ b/code/game/objects/structures/crates_lockers/closets/wardrobe.dm
@@ -126,8 +126,10 @@
new /obj/item/clothing/mask/bandana/black(src)
if(prob(40))
new /obj/item/clothing/under/assistantformal(src)
+ new /obj/item/clothing/suit/hooded/wintercoat/aformal(src)
if(prob(40))
new /obj/item/clothing/under/assistantformal(src)
+ new /obj/item/clothing/suit/hooded/wintercoat/aformal(src)
if(prob(30))
new /obj/item/clothing/suit/hooded/wintercoat(src)
new /obj/item/clothing/shoes/winterboots(src)
@@ -169,4 +171,6 @@
if(prob(30))
new /obj/item/clothing/suit/hooded/wintercoat(src)
new /obj/item/clothing/shoes/winterboots(src)
+ if (prob(30))
+ new /obj/item/clothing/suit/hooded/wintercoat/polychromic(src)
return
diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm
index eaa9074bd8..b14303984e 100644
--- a/code/game/objects/structures/crates_lockers/crates.dm
+++ b/code/game/objects/structures/crates_lockers/crates.dm
@@ -13,6 +13,8 @@
climb_time = 10 //real fast, because let's be honest stepping into or onto a crate is easy
climb_stun = 0 //climbing onto crates isn't hard, guys
delivery_icon = "deliverycrate"
+ material_drop = /obj/item/stack/sheet/plasteel
+ material_drop_amount = 5
var/obj/item/paper/fluff/jobs/cargo/manifest/manifest
/obj/structure/closet/crate/New()
@@ -45,6 +47,12 @@
if(manifest)
tear_manifest(user)
+/obj/structure/closet/crate/tool_interact(obj/item/W, mob/user)
+ if(W.tool_behaviour == TOOL_WIRECUTTER && manifest)
+ tear_manifest(user)
+ return TRUE
+ return ..()
+
/obj/structure/closet/crate/open(mob/living/user)
. = ..()
if(. && manifest)
@@ -81,7 +89,7 @@
/obj/structure/closet/crate/coffin/examine(mob/user)
. = ..()
- if(user.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER))
+ if(user.mind?.has_antag_datum(ANTAG_DATUM_BLOODSUCKER))
. += {"This is a coffin which you can use to regenerate your burns and other wounds faster."}
. += {"You can also thicken your blood if you survive the day, and hide from the sun safely while inside."}
/* if(user.mind.has_antag_datum(ANTAG_DATUM_VASSAL)
@@ -142,6 +150,9 @@
new /obj/item/reagent_containers/blood/OMinus(src)
new /obj/item/reagent_containers/blood/OPlus(src)
new /obj/item/reagent_containers/blood/lizard(src)
+ new /obj/item/reagent_containers/blood/jellyblood(src)
+ new /obj/item/reagent_containers/blood/insect(src)
+ new /obj/item/reagent_containers/blood/synthetics(src)
for(var/i in 1 to 3)
new /obj/item/reagent_containers/blood/random(src)
diff --git a/code/game/objects/structures/crates_lockers/crates/bins.dm b/code/game/objects/structures/crates_lockers/crates/bins.dm
index 7646fdb35f..bc3697a550 100644
--- a/code/game/objects/structures/crates_lockers/crates/bins.dm
+++ b/code/game/objects/structures/crates_lockers/crates/bins.dm
@@ -4,8 +4,8 @@
icon_state = "largebins"
open_sound = 'sound/effects/bin_open.ogg'
close_sound = 'sound/effects/bin_close.ogg'
- material_drop = /obj/item/stack/sheet/plastic
- material_drop_amount = 40
+ material_drop = /obj/item/stack/sheet/metal
+ material_drop_amount = 10
anchored = TRUE
horizontal = FALSE
delivery_icon = null
diff --git a/code/game/objects/structures/crates_lockers/crates/secure.dm b/code/game/objects/structures/crates_lockers/crates/secure.dm
index 290e69370b..602adfb367 100644
--- a/code/game/objects/structures/crates_lockers/crates/secure.dm
+++ b/code/game/objects/structures/crates_lockers/crates/secure.dm
@@ -70,3 +70,8 @@
name = "secure science crate"
desc = "A crate with a lock on it, painted in the scheme of the station's scientists."
icon_state = "scisecurecrate"
+
+/obj/structure/closet/crate/secure/medical
+ desc = "A secure medical crate."
+ name = "medical crate"
+ icon_state = "medical_secure_crate"
\ No newline at end of file
diff --git a/code/game/objects/structures/displaycase.dm b/code/game/objects/structures/displaycase.dm
index 8c25a2ca5c..70533df0ad 100644
--- a/code/game/objects/structures/displaycase.dm
+++ b/code/game/objects/structures/displaycase.dm
@@ -80,7 +80,7 @@
/obj/structure/displaycase/proc/trigger_alarm()
//Activate Anti-theft
if(alert)
- var/area/alarmed = get_area(src)
+ var/area/alarmed = get_base_area(src)
alarmed.burglaralert(src)
playsound(src, 'sound/effects/alert.ogg', 50, 1)
diff --git a/code/game/objects/structures/door_assembly.dm b/code/game/objects/structures/door_assembly.dm
index 271ae72d47..f35e9e7d55 100644
--- a/code/game/objects/structures/door_assembly.dm
+++ b/code/game/objects/structures/door_assembly.dm
@@ -205,6 +205,9 @@
if(!mineral)
if(istype(G, /obj/item/stack/sheet/mineral) && G.sheettype)
var/M = G.sheettype
+ var/mineralassembly = text2path("/obj/structure/door_assembly/door_assembly_[M]")
+ if(!mineralassembly)
+ return
if(G.get_amount() >= 2)
playsound(src, 'sound/items/crowbar.ogg', 100, 1)
user.visible_message("[user] adds [G.name] to the airlock assembly.", \
@@ -214,7 +217,6 @@
return
to_chat(user, "You install [M] plating into the airlock assembly.")
G.use(2)
- var/mineralassembly = text2path("/obj/structure/door_assembly/door_assembly_[M]")
var/obj/structure/door_assembly/MA = new mineralassembly(loc)
transfer_assembly_vars(src, MA, TRUE)
else
diff --git a/code/game/objects/structures/false_walls.dm b/code/game/objects/structures/false_walls.dm
index d7e2d5564a..52441c2987 100644
--- a/code/game/objects/structures/false_walls.dm
+++ b/code/game/objects/structures/false_walls.dm
@@ -7,7 +7,7 @@
anchored = TRUE
icon = 'icons/turf/walls/wall.dmi'
icon_state = "wall"
- layer = CLOSED_TURF_LAYER
+ layer = LOW_OBJ_LAYER
density = TRUE
opacity = 1
max_integrity = 100
diff --git a/code/game/objects/structures/ghost_role_spawners.dm b/code/game/objects/structures/ghost_role_spawners.dm
index 92fb13c947..116c598235 100644
--- a/code/game/objects/structures/ghost_role_spawners.dm
+++ b/code/game/objects/structures/ghost_role_spawners.dm
@@ -12,26 +12,27 @@
roundstart = FALSE
death = FALSE
mob_species = /datum/species/pod
- flavour_text = "You are a sentient ecosystem, an example of the mastery over life that your creators possessed. Your masters, benevolent as they were, created uncounted \
- seed vaults and spread them across the universe to every planet they could chart. You are in one such seed vault. Your goal is to cultivate and spread life wherever it will go while waiting \
- for contact from your creators. Estimated time of last contact: Deployment, 5x10^3 millennia ago."
+ short_desc = "You are a sentient ecosystem, an example of the mastery over life that your creators possessed."
+ flavour_text = "Your masters, benevolent as they were, created uncounted seed vaults and spread them across \
+ the universe to every planet they could chart. You are in one such seed vault. \
+ Your goal is to cultivate and spread life wherever it will go while waiting for contact from your creators. \
+ Estimated time of last contact: Deployment, 5000 millennia ago."
assignedrole = "Lifebringer"
-/obj/effect/mob_spawn/human/seed_vault/special(mob/living/new_spawn)
- var/plant_name = pick("Tomato", "Potato", "Broccoli", "Carrot", "Ambrosia", "Pumpkin", "Ivy", "Kudzu", "Banana", "Moss", "Flower", "Bloom", "Root", "Bark", "Glowshroom", "Petal", "Leaf", \
- "Venus", "Sprout","Cocoa", "Strawberry", "Citrus", "Oak", "Cactus", "Pepper", "Juniper")
- new_spawn.real_name = plant_name
- if(ishuman(new_spawn))
- var/mob/living/carbon/human/H = new_spawn
- H.underwear = "Nude" //You're a plant, partner
- H.update_body()
-
/obj/effect/mob_spawn/human/seed_vault/Destroy()
new/obj/structure/fluff/empty_terrarium(get_turf(src))
return ..()
/obj/effect/mob_spawn/human/seed_vault/special(mob/living/carbon/human/new_spawn)
ADD_TRAIT(new_spawn,TRAIT_EXEMPT_HEALTH_EVENTS,GHOSTROLE_TRAIT)
+ var/plant_name = pick("Tomato", "Potato", "Broccoli", "Carrot", "Ambrosia", "Pumpkin", "Ivy", "Kudzu", "Banana", "Moss", "Flower", "Bloom", "Root", "Bark", "Glowshroom", "Petal", "Leaf", \
+ "Venus", "Sprout","Cocoa", "Strawberry", "Citrus", "Oak", "Cactus", "Pepper", "Juniper")
+ new_spawn.real_name = plant_name //why this works when moving it from one function to another is beyond me
+ new_spawn.underwear = "Nude" //You're a plant, partner
+ new_spawn.undershirt = "Nude" //changing underwear/shirt/socks doesn't seem to function correctly right now because of some bug elsewhere?
+ new_spawn.socks = "Nude"
+ new_spawn.update_body()
+
//Ash walker eggs: Spawns in ash walker dens in lavaland. Ghosts become unbreathing lizards that worship the Necropolis and are advised to retrieve corpses to create more ash walkers.
/obj/effect/mob_spawn/human/ash_walker
@@ -48,18 +49,31 @@
anchored = FALSE
move_resist = MOVE_FORCE_NORMAL
density = FALSE
- flavour_text = "You are an ash walker. Your tribe worships the Necropolis. The wastes are sacred ground, its monsters a blessed bounty. You would never leave its beautiful expanse. \
- You have seen lights in the distance... they foreshadow the arrival of outsiders that seek to tear apart the Necropolis and its domain. Fresh sacrifices for your nest."
+ short_desc = "You are an ash walker. Your tribe worships the Necropolis."
+ flavour_text = "The wastes are sacred ground, its monsters a blessed bounty. You would never willingly leave your homeland behind. \
+ You have seen lights in the distance... they foreshadow the arrival of outsiders to your domain. \
+ Ensure your nest remains protected at all costs."
assignedrole = "Ash Walker"
/obj/effect/mob_spawn/human/ash_walker/special(mob/living/new_spawn)
new_spawn.real_name = random_unique_lizard_name(gender)
- to_chat(new_spawn, "Drag the corpses of men and beasts to your nest. It will absorb them to create more of your kind. Glory to the Necropolis!")
+ if(is_mining_level(z))
+ to_chat(new_spawn, "Drag the corpses of men and beasts to your nest. It will absorb them to create more of your kind. Glory to the Necropolis!")
+ else
+ to_chat(new_spawn, "You have been born outside of your natural home! Whether you decide to return home, or make due with your new home is your own decision.")
new_spawn.grant_language(/datum/language/draconic)
var/datum/language_holder/holder = new_spawn.get_language_holder()
holder.selected_default_language = /datum/language/draconic
+//Ash walkers on birth understand how to make bone bows, bone arrows and ashen arrows
+
+ new_spawn.mind.teach_crafting_recipe(/datum/crafting_recipe/bone_arrow)
+ new_spawn.mind.teach_crafting_recipe(/datum/crafting_recipe/bone_bow)
+ new_spawn.mind.teach_crafting_recipe(/datum/crafting_recipe/ashen_arrow)
+ new_spawn.mind.teach_crafting_recipe(/datum/crafting_recipe/quiver)
+ new_spawn.mind.teach_crafting_recipe(/datum/crafting_recipe/bow_tablet)
+
if(ishuman(new_spawn))
var/mob/living/carbon/human/H = new_spawn
H.underwear = "Nude"
@@ -88,8 +102,9 @@
roundstart = FALSE
death = FALSE
mob_species = /datum/species/shadow
- flavour_text = "You are cursed. Years ago, you sacrificed the lives of your trusted friends and the humanity of yourself to reach the Wish Granter. Though you \
- did so, it has come at a cost: your very body rejects the light, dooming you to wander endlessly in this horrible wasteland."
+ short_desc = "You are cursed."
+ flavour_text = "Years ago, you sacrificed the lives of your trusted friends and the humanity of yourself to reach the Wish Granter. Though you \
+ did so, it has come at a cost: your very body rejects the light, dooming you to wander endlessly in this horrible wasteland."
assignedrole = "Exile"
/obj/effect/mob_spawn/human/exile/Destroy()
@@ -126,9 +141,10 @@
var/has_owner = FALSE
var/can_transfer = TRUE //if golems can switch bodies to this new shell
var/mob/living/owner = null //golem's owner if it has one
- flavour_text = "You are a Free Golem. Your family worships The Liberator. In his infinite and divine wisdom, he set your clan free to \
+ short_desc = "You are a Free Golem. Your family worships The Liberator."
+ flavour_text = "In his infinite and divine wisdom, he set your clan free to \
travel the stars with a single declaration: \"Yeah go do whatever.\" Though you are bound to the one who created you, it is customary in your society to repeat those same words to newborn \
- golems, so that no golem may ever be forced to serve again."
+ golems, so that no golem may ever be forced to serve again."
/obj/effect/mob_spawn/human/golem/Initialize(mapload, datum/species/golem/species = null, mob/creator = null)
if(species) //spawners list uses object name to register so this goes before ..()
@@ -139,8 +155,9 @@
if(!mapload && A)
notify_ghosts("\A [initial(species.prefix)] golem shell has been completed in \the [A.name].", source = src, action=NOTIFY_ATTACK, flashwindow = FALSE, ignore_key = POLL_IGNORE_GOLEM, ignore_dnr_observers = TRUE)
if(has_owner && creator)
- flavour_text = "You are a Golem. You move slowly, but are highly resistant to heat and cold as well as blunt trauma. You are unable to wear clothes, but can still use most tools. \
- Serve [creator], and assist [creator.p_them()] in completing [creator.p_their()] goals at any cost."
+ short_desc = "You are a golem."
+ flavour_text = "You move slowly, but are highly resistant to heat and cold as well as blunt trauma. You are unable to wear clothes, but can still use most tools."
+ important_info = "Serve [creator], and assist [creator.p_them()] in completing [creator.p_their()] goals at any cost."
owner = creator
/obj/effect/mob_spawn/human/golem/special(mob/living/new_spawn, name)
@@ -213,8 +230,9 @@
death = FALSE
random = TRUE
mob_species = /datum/species/human
- flavour_text = "You've been stranded in this godless prison of a planet for longer than you can remember. Each day you barely scrape by, and between the terrible \
- conditions of your makeshift shelter, the hostile creatures, and the ash drakes swooping down from the cloudless skies, all you can wish for is the feel of soft grass between your toes and \
+ short_desc = "You've been stranded in this godless prison of a planet for longer than you can remember."
+ flavour_text = "Each day you barely scrape by, and between the terrible conditions of your makeshift shelter, \
+ the hostile creatures, and the ash drakes swooping down from the cloudless skies, all you can wish for is the feel of soft grass between your toes and \
the fresh air of Earth. These thoughts are dispelled by yet another recollection of how you got here... "
assignedrole = "Hermit"
@@ -225,20 +243,20 @@
if(1)
flavour_text += "you were a [pick("arms dealer", "shipwright", "docking manager")]'s assistant on a small trading station several sectors from here. Raiders attacked, and there was \
only one pod left when you got to the escape bay. You took it and launched it alone, and the crowd of terrified faces crowding at the airlock door as your pod's engines burst to \
- life and sent you to this hell are forever branded into your memory."
+ life and sent you to this hell are forever branded into your memory."
outfit.uniform = /obj/item/clothing/under/assistantformal
outfit.shoes = /obj/item/clothing/shoes/sneakers/black
outfit.back = /obj/item/storage/backpack
if(2)
flavour_text += "you're an exile from the Tiger Cooperative. Their technological fanaticism drove you to question the power and beliefs of the Exolitics, and they saw you as a \
heretic and subjected you to hours of horrible torture. You were hours away from execution when a high-ranking friend of yours in the Cooperative managed to secure you a pod, \
- scrambled its destination's coordinates, and launched it. You awoke from stasis when you landed and have been surviving - barely - ever since."
+ scrambled its destination's coordinates, and launched it. You awoke from stasis when you landed and have been surviving - barely - ever since."
outfit.uniform = /obj/item/clothing/under/rank/prisoner
outfit.shoes = /obj/item/clothing/shoes/sneakers/orange
outfit.back = /obj/item/storage/backpack
if(3)
flavour_text += "you were a doctor on one of Nanotrasen's space stations, but you left behind that damn corporation's tyranny and everything it stood for. From a metaphorical hell \
- to a literal one, you find yourself nonetheless missing the recycled air and warm floors of what you left behind... but you'd still rather be here than there."
+ to a literal one, you find yourself nonetheless missing the recycled air and warm floors of what you left behind... but you'd still rather be here than there."
outfit.uniform = /obj/item/clothing/under/rank/medical
outfit.suit = /obj/item/clothing/suit/toggle/labcoat
outfit.back = /obj/item/storage/backpack/medic
@@ -246,7 +264,7 @@
if(4)
flavour_text += "you were always joked about by your friends for \"not playing with a full deck\", as they so kindly put it. It seems that they were right when you, on a tour \
at one of Nanotrasen's state-of-the-art research facilities, were in one of the escape pods alone and saw the red button. It was big and shiny, and it caught your eye. You pressed \
- it, and after a terrifying and fast ride for days, you landed here. You've had time to wisen up since then, and you think that your old friends wouldn't be laughing now."
+ it, and after a terrifying and fast ride for days, you landed here. You've had time to wisen up since then, and you think that your old friends wouldn't be laughing now."
outfit.uniform = /obj/item/clothing/under/color/grey/glorf
outfit.shoes = /obj/item/clothing/shoes/sneakers/black
outfit.back = /obj/item/storage/backpack
@@ -264,9 +282,10 @@
desc = "A small sleeper typically used to instantly restore minor wounds. This one seems broken, and its occupant is comatose."
job_description = "Lavaland Veterinarian"
mob_name = "a translocated vet"
- flavour_text = "What...? Where are you? Where are the others? This is still the animal hospital - you should know, you've been an intern here for weeks - but \
- everyone's gone. One of the cats scratched you just a few minutes ago. That's why you were in the pod - to heal the scratch. The scabs are still fresh; you see them right now. So where is \
- everyone? Where did they go? What happened to the hospital? And is that smoke you smell? You need to find someone else. Maybe they can tell you what happened."
+ short_desc = "You are a animal doctor who just woke up in lavaland"
+ flavour_text = "What...? Where are you? Where are the others? This is still the animal hospital - you should know, you've been an intern here for weeks - but \
+ you see them right now. So where is \
+ everyone? Where did they go? What happened to the hospital? And is that smoke you smell? You need to find someone else. Maybe they c everyone's gone. One of the cats scratched you just a few minutes ago. That's why you were in the pod - to heal the scratch. The scabs are still fresh; an tell you what happened."
assignedrole = "Translocated Vet"
/obj/effect/mob_spawn/human/doctor/alive/lavaland/Destroy()
@@ -285,8 +304,9 @@
outfit = /datum/outfit/lavalandprisoner
roundstart = FALSE
death = FALSE
- flavour_text = "Good. It seems as though your ship crashed. You're a prisoner, sentenced to hard work in one of Nanotrasen's labor camps, but it seems as \
- though fate has other plans for you. You remember that you were convicted of "
+ short_desc = "You're a prisoner, sentenced to hard work in one of Nanotrasen's labor camps, but it seems as \
+ though fate has other plans for you."
+ flavour_text = "Good. It seems as though your ship crashed. You remember that you were convicted of "
assignedrole = "Escaped Prisoner"
/obj/effect/mob_spawn/human/prisoner_transport/special(mob/living/L)
@@ -298,7 +318,7 @@
var/list/crimes = list("murder", "larceny", "embezzlement", "unionization", "dereliction of duty", "kidnapping", "gross incompetence", "grand theft", "collaboration with the Syndicate", \
"worship of a forbidden deity", "interspecies relations", "mutiny")
flavour_text += "[pick(crimes)]. but regardless of that, it seems like your crime doesn't matter now. You don't know where you are, but you know that it's out to kill you, and you're not going \
- to lose this opportunity. Find a way to get out of this mess and back to where you rightfully belong - your [pick("house", "apartment", "spaceship", "station")]."
+ to lose this opportunity. Find a way to get out of this mess and back to where you rightfully belong - your [pick("house", "apartment", "spaceship", "station")]."
/datum/outfit/lavalandprisoner
name = "Lavaland Prisoner"
@@ -325,8 +345,9 @@
roundstart = FALSE
random = TRUE
outfit = /datum/outfit/hotelstaff
- flavour_text = "You are a staff member of a top-of-the-line space hotel! Cater to guests and DON'T leave the hotel, lest the manager fire you for\
- dereliction of duty!"
+ short_desc = "You are a staff member of a top-of-the-line space hotel!"
+ flavour_text = "You are a staff member of a top-of-the-line space hotel! Cater to guests and make sure the manager doesn't fire you."
+ important_info = "DON'T leave the hotel"
assignedrole = "Hotel Staff"
/datum/outfit/hotelstaff
@@ -343,8 +364,10 @@
mob_name = "hotel security member"
job_description = "Hotel Security"
outfit = /datum/outfit/hotelstaff/security
- flavour_text = "You are a peacekeeper assigned to this hotel to protect the interests of the company while keeping the peace between \
- guests and the staff. Do NOT leave the hotel, as that is grounds for contract termination."
+ short_desc = "You are a peacekeeper."
+ flavour_text = "You have been assigned to this hotel to protect the interests of the company while keeping the peace between \
+ guests and the staff."
+ important_info = "Do NOT leave the hotel, as that is grounds for contract termination."
objectives = "Do not leave your assigned hotel. Try and keep the peace between staff and guests, non-lethal force heavily advised if possible."
/datum/outfit/hotelstaff/security
@@ -383,7 +406,8 @@
/obj/effect/mob_spawn/human/demonic_friend/Initialize(mapload, datum/mind/owner_mind, obj/effect/proc_holder/spell/targeted/summon_friend/summoning_spell)
. = ..()
owner = owner_mind
- flavour_text = "You have been given a reprieve from your eternity of torment, to be [owner.name]'s friend for [owner.p_their()] short mortal coil. Be aware that if you do not live up to [owner.name]'s expectations, they can send you back to hell with a single thought. [owner.name]'s death will also return you to hell."
+ flavour_text = "You have been given a reprieve from your eternity of torment, to be [owner.name]'s friend for [owner.p_their()] short mortal coil."
+ important_info = "Be aware that if you do not live up to [owner.name]'s expectations, they can send you back to hell with a single thought. [owner.name]'s death will also return you to hell."
var/area/A = get_area(src)
if(!mapload && A)
notify_ghosts("\A friendship shell has been completed in \the [A.name].", source = src, action=NOTIFY_ATTACK, flashwindow = FALSE, ignore_dnr_observers = TRUE)
@@ -441,9 +465,9 @@
/obj/effect/mob_spawn/human/syndicate/battlecruiser
name = "Syndicate Battlecruiser Ship Operative"
- flavour_text = "You are a crewmember aboard the syndicate flagship: the SBC Starfury.Your job is to follow your captain's orders, maintain the ship, and keep the engine running. If you are not familiar with how the supermatter engine functions: do not attempt to start it. \
- \
- The armory is not a candy store, and your role is not to assault the station directly, leave that work to the assault operatives."
+ short_desc = "You are a crewmember aboard the syndicate flagship: the SBC Starfury."
+ flavour_text = "Your job is to follow your captain's orders, maintain the ship, and keep the engine running. If you are not familiar with how the supermatter engine functions: do not attempt to start it."
+ important_info = "The armory is not a candy store, and your role is not to assault the station directly, leave that work to the assault operatives."
outfit = /datum/outfit/syndicate_empty/SBC
/datum/outfit/syndicate_empty/SBC
@@ -453,10 +477,9 @@
belt = /obj/item/storage/belt/military/assault
/obj/effect/mob_spawn/human/syndicate/battlecruiser/assault
- name = "Syndicate Battlecruiser Assault Operative"
- flavour_text = "You are an assault operative aboard the syndicate flagship: the SBC Starfury.Your job is to follow your captain's orders, keep intruders out of the ship, and assault Space Station 13. There is an armory, multiple assault ships, and beam cannons to attack the station with. \
- \
- Work as a team with your fellow operatives and work out a plan of attack. If you are overwhelmed, escape back to your ship!"
+ short_desc = "You are an assault operative aboard the syndicate flagship: the SBC Starfury."
+ flavour_text = "Your job is to follow your captain's orders, keep intruders out of the ship, and assault Space Station 13. There is an armory, multiple assault ships, and beam cannons to attack the station with."
+ important_info = "Work as a team with your fellow operatives and work out a plan of attack. If you are overwhelmed, escape back to your ship!"
outfit = /datum/outfit/syndicate_empty/SBC/assault
/datum/outfit/syndicate_empty/SBC/assault
@@ -472,9 +495,9 @@
/obj/effect/mob_spawn/human/syndicate/battlecruiser/captain
name = "Syndicate Battlecruiser Captain"
- flavour_text = "You are the captain aboard the syndicate flagship: the SBC Starfury.Your job is to oversee your crew, defend the ship, and destroy Space Station 13. The ship has an armory, multiple ships, beam cannons, and multiple crewmembers to accomplish this goal. \
- \
- As the captain, this whole operation falls on your shoulders. You do not need to nuke the station, causing sufficient damage and preventing your ship from being destroyed will be enough."
+ short_desc = "You are the captain aboard the syndicate flagship: the SBC Starfury."
+ flavour_text = "Your job is to oversee your crew, defend the ship, and destroy Space Station 13. The ship has an armory, multiple ships, beam cannons, and multiple crewmembers to accomplish this goal."
+ important_info = "As the captain, this whole operation falls on your shoulders. You do not need to nuke the station, causing sufficient damage and preventing your ship from being destroyed will be enough."
outfit = /datum/outfit/syndicate_empty/SBC/assault/captain
id_access_list = list(150,151)
@@ -500,10 +523,11 @@
death = FALSE
random = TRUE
mob_species = /datum/species/human
- flavour_text = "You are a security officer working for Nanotrasen, stationed onboard a state of the art research station. You vaguely recall rushing into a \
- cryogenics pod due to an oncoming radiation storm. The last thing you remember is the station's Artificial Program telling you that you would only be asleep for eight hours. As you open \
- your eyes, everything seems rusted and broken, a dark feeling swells in your gut as you climb out of your pod. \
- Work as a team with your fellow survivors and do not abandon them."
+ short_desc = "You are a security officer working for Nanotrasen, stationed onboard a state of the art research station."
+ flavour_text = "You vaguely recall rushing into a cryogenics pod due to an oncoming radiation storm. \
+ The last thing you remember is the station's Artificial Program telling you that you would only be asleep for eight hours. As you open \
+ your eyes, everything seems rusted and broken, a dark feeling swells in your gut as you climb out of your pod."
+ important_info = "Work as a team with your fellow survivors and do not abandon them."
uniform = /obj/item/clothing/under/rank/security
shoes = /obj/item/clothing/shoes/jackboots
id = /obj/item/card/id/away/old/sec
@@ -527,10 +551,11 @@
death = FALSE
random = TRUE
mob_species = /datum/species/human
- flavour_text = "You are an engineer working for Nanotrasen, stationed onboard a state of the art research station. You vaguely recall rushing into a \
- cryogenics pod due to an oncoming radiation storm. The last thing you remember is the station's Artificial Program telling you that you would only be asleep for eight hours. As you open \
- your eyes, everything seems rusted and broken, a dark feeling swells in your gut as you climb out of your pod. \
- Work as a team with your fellow survivors and do not abandon them."
+ short_desc = "You are an engineer working for Nanotrasen, stationed onboard a state of the art research station."
+ flavour_text = "You vaguely recall rushing into a cryogenics pod due to an oncoming radiation storm. The last thing \
+ you remember is the station's Artificial Program telling you that you would only be asleep for eight hours. As you open \
+ your eyes, everything seems rusted and broken, a dark feeling swells in your gut as you climb out of your pod."
+ important_info = "Work as a team with your fellow survivors and do not abandon them."
uniform = /obj/item/clothing/under/rank/engineer
shoes = /obj/item/clothing/shoes/workboots
id = /obj/item/card/id/away/old/eng
@@ -552,10 +577,11 @@
death = FALSE
random = TRUE
mob_species = /datum/species/human
- flavour_text = "You are a scientist working for Nanotrasen, stationed onboard a state of the art research station. You vaguely recall rushing into a \
- cryogenics pod due to an oncoming radiation storm. The last thing you remember is the station's Artificial Program telling you that you would only be asleep for eight hours. As you open \
- your eyes, everything seems rusted and broken, a dark feeling swells in your gut as you climb out of your pod. \
- Work as a team with your fellow survivors and do not abandon them."
+ short_desc = "You are a scientist working for Nanotrasen, stationed onboard a state of the art research station."
+ flavour_text = "You vaguely recall rushing into a cryogenics pod due to an oncoming radiation storm. \
+ The last thing you remember is the station's Artificial Program telling you that you would only be asleep for eight hours. As you open \
+ your eyes, everything seems rusted and broken, a dark feeling swells in your gut as you climb out of your pod."
+ important_info = "Work as a team with your fellow survivors and do not abandon them."
uniform = /obj/item/clothing/under/rank/scientist
shoes = /obj/item/clothing/shoes/laceup
id = /obj/item/card/id/away/old/sci
@@ -582,7 +608,8 @@
anchored = TRUE
density = FALSE
show_flavour = FALSE //Flavour only exists for spawners menu
- flavour_text = "You are a space pirate. The station refused to pay for your protection, protect the ship, siphon the credits from the station and raid it for even more loot."
+ short_desc = "You are a space pirate."
+ flavour_text = "The station refused to pay for your protection, protect the ship, siphon the credits from the station and raid it for even more loot."
assignedrole = "Space Pirate"
var/rank = "Mate"
@@ -617,9 +644,10 @@
density = FALSE
death = FALSE
assignedrole = "Ghost Cafe Visitor"
- flavour_text = "Is this what life after death is like?"
+ short_desc = "You are a Ghost Cafe Visitor!"
+ flavour_text = "You know one thing for sure. You arent actually alive. Are you in a simulation?"
skip_reentry_check = TRUE
- banType = "ghostcafe"
+ banType = ROLE_GHOSTCAFE
/datum/action/toggle_dead_chat_mob
icon_icon = 'icons/mob/mob.dmi'
@@ -642,12 +670,14 @@
/obj/effect/mob_spawn/human/ghostcafe/special(mob/living/carbon/human/new_spawn)
if(new_spawn.client)
new_spawn.client.prefs.copy_to(new_spawn)
+ var/area/A = get_area(src)
var/datum/outfit/O = new /datum/outfit/ghostcafe()
O.equip(new_spawn, FALSE, new_spawn.client)
SSjob.equip_loadout(null, new_spawn, FALSE)
SSquirks.AssignQuirks(new_spawn, new_spawn.client, TRUE, TRUE, null, FALSE, new_spawn)
new_spawn.AddElement(/datum/element/ghost_role_eligibility)
new_spawn.AddElement(/datum/element/dusts_on_catatonia)
+ new_spawn.AddElement(/datum/element/dusts_on_leaving_area,list(A.type,/area/hilbertshotel))
ADD_TRAIT(new_spawn, TRAIT_SIXTHSENSE, GHOSTROLE_TRAIT)
ADD_TRAIT(new_spawn,TRAIT_EXEMPT_HEALTH_EVENTS,GHOSTROLE_TRAIT)
ADD_TRAIT(new_spawn,TRAIT_PACIFISM,GHOSTROLE_TRAIT)
diff --git a/code/game/objects/structures/girders.dm b/code/game/objects/structures/girders.dm
index 94619e1858..7c373f2734 100644
--- a/code/game/objects/structures/girders.dm
+++ b/code/game/objects/structures/girders.dm
@@ -166,6 +166,9 @@
if(S.sheettype && S.sheettype != "runed")
var/M = S.sheettype
if(state == GIRDER_DISPLACED)
+ var/F = text2path("/obj/structure/falsewall/[M]")
+ if(!F)
+ return
if(S.get_amount() < 2)
to_chat(user, "You need at least two sheets to create a false wall!")
return
@@ -174,11 +177,13 @@
return
S.use(2)
to_chat(user, "You create a false wall. Push on it to open or close the passage.")
- var/F = text2path("/obj/structure/falsewall/[M]")
var/obj/structure/FW = new F (loc)
transfer_fingerprints_to(FW)
qdel(src)
else
+ var/F = text2path("/turf/closed/wall/mineral/[M]")
+ if(!F)
+ return
if(S.get_amount() < 2)
to_chat(user, "You need at least two sheets to add plating!")
return
@@ -189,7 +194,7 @@
S.use(2)
to_chat(user, "You add the plating.")
var/turf/T = get_turf(src)
- T.PlaceOnTop(text2path("/turf/closed/wall/mineral/[M]"))
+ T.PlaceOnTop(F)
transfer_fingerprints_to(T)
qdel(src)
return
diff --git a/code/game/objects/structures/guillotine.dm b/code/game/objects/structures/guillotine.dm
index 3b4bbea559..611a6d024d 100644
--- a/code/game/objects/structures/guillotine.dm
+++ b/code/game/objects/structures/guillotine.dm
@@ -97,7 +97,7 @@
icon_state = "guillotine_raised"
/obj/structure/guillotine/proc/drop_blade(mob/user)
- if (buckled_mobs.len && blade_sharpness)
+ if (has_buckled_mobs() && blade_sharpness)
var/mob/living/carbon/human/H = buckled_mobs[1]
if (!H)
diff --git a/code/game/objects/structures/headpike.dm b/code/game/objects/structures/headpike.dm
index ceed9cb759..81433b562d 100644
--- a/code/game/objects/structures/headpike.dm
+++ b/code/game/objects/structures/headpike.dm
@@ -42,8 +42,9 @@
if(.)
return
to_chat(user, "You take down [src].")
- victim.forceMove(drop_location())
- victim = null
+ if(victim)
+ victim.forceMove(drop_location())
+ victim = null
spear.forceMove(drop_location())
spear = null
qdel(src)
\ No newline at end of file
diff --git a/code/game/objects/structures/holosign.dm b/code/game/objects/structures/holosign.dm
index 617f42ed29..92fda29101 100644
--- a/code/game/objects/structures/holosign.dm
+++ b/code/game/objects/structures/holosign.dm
@@ -8,14 +8,16 @@
max_integrity = 1
armor = list("melee" = 0, "bullet" = 50, "laser" = 50, "energy" = 50, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 20)
var/obj/item/holosign_creator/projector
+ var/init_vis_overlay = TRUE
/obj/structure/holosign/Initialize(mapload, source_projector)
. = ..()
if(source_projector)
projector = source_projector
projector.signs += src
- SSvis_overlays.add_vis_overlay(src, icon, icon_state, ABOVE_MOB_LAYER, plane, dir, alpha, RESET_ALPHA) //you see mobs under it, but you hit them like they are above it
- alpha = 0
+ if(init_vis_overlay)
+ SSvis_overlays.add_vis_overlay(src, icon, icon_state, ABOVE_MOB_LAYER, plane, dir, alpha, RESET_ALPHA) //you see mobs under it, but you hit them like they are above it
+ alpha = 0
/obj/structure/holosign/Destroy()
if(projector)
@@ -74,8 +76,10 @@
icon_state = "holo_fan"
density = FALSE
anchored = TRUE
+ layer = ABOVE_NORMAL_TURF_LAYER
CanAtmosPass = ATMOS_PASS_NO
alpha = 150
+ init_vis_overlay = FALSE
/obj/structure/holosign/barrier/atmos/Initialize()
. = ..()
@@ -93,6 +97,26 @@
/obj/structure/holosign/barrier/firelock/blocksTemperature()
return TRUE
+/obj/structure/holosign/barrier/combifan
+ name = "holo combifan"
+ desc = "A holographic barrier resembling a blue-accented tiny fan. Though it does not prevent solid objects from passing through, gas and temperature changes are kept out."
+ icon_state = "holo_combifan"
+ max_integrity = 30
+ density = FALSE
+ anchored = TRUE
+ layer = ABOVE_NORMAL_TURF_LAYER
+ alpha = 150
+ init_vis_overlay = FALSE
+ CanAtmosPass = ATMOS_PASS_NO
+ resistance_flags = FIRE_PROOF
+
+/obj/structure/holosign/barrier/combolock/blocksTemperature()
+ return TRUE
+
+/obj/structure/holosign/barrier/combolock/Initialize()
+ . = ..()
+ air_update_turf(TRUE)
+
/obj/structure/holosign/barrier/cyborg
name = "Energy Field"
desc = "A fragile energy field that blocks movement. Excels at blocking lethal projectiles."
diff --git a/code/game/objects/structures/janicart.dm b/code/game/objects/structures/janicart.dm
index 54b9d650d5..58c8a739dc 100644
--- a/code/game/objects/structures/janicart.dm
+++ b/code/game/objects/structures/janicart.dm
@@ -6,11 +6,11 @@
anchored = FALSE
density = TRUE
//copypaste sorry
- var/amount_per_transfer_from_this = 5 //shit I dunno, adding this so syringes stop runtime erroring. --NeoFite
- var/obj/item/storage/bag/trash/mybag = null
- var/obj/item/mop/mymop = null
- var/obj/item/reagent_containers/spray/cleaner/myspray = null
- var/obj/item/lightreplacer/myreplacer = null
+ var/obj/item/storage/bag/trash/mybag
+ var/obj/item/mop/mymop
+ var/obj/item/twohanded/broom/mybroom
+ var/obj/item/reagent_containers/spray/cleaner/myspray
+ var/obj/item/lightreplacer/myreplacer
var/signs = 0
var/const/max_signs = 4
@@ -49,7 +49,12 @@
m.janicart_insert(user, src)
else
to_chat(user, fail_msg)
-
+ else if(istype(I, /obj/item/twohanded/broom))
+ if(!mybroom)
+ var/obj/item/twohanded/broom/b=I
+ b.janicart_insert(user,src)
+ else
+ to_chat(user, fail_msg)
else if(istype(I, /obj/item/storage/bag/trash))
if(!mybag)
var/obj/item/storage/bag/trash/t=I
@@ -97,6 +102,8 @@
dat += "[mybag.name] "
if(mymop)
dat += "[mymop.name] "
+ if(mybroom)
+ dat += "[mybroom.name] "
if(myspray)
dat += "[myspray.name] "
if(myreplacer)
@@ -124,6 +131,11 @@
user.put_in_hands(mymop)
to_chat(user, "You take [mymop] from [src].")
mymop = null
+ if(href_list["broom"])
+ if(mybroom)
+ user.put_in_hands(mybroom)
+ to_chat(user, "You take [mybroom] from [src].")
+ mybroom = null
if(href_list["spray"])
if(myspray)
user.put_in_hands(myspray)
@@ -155,6 +167,8 @@
add_overlay("cart_garbage")
if(mymop)
add_overlay("cart_mop")
+ if(mybroom)
+ add_overlay("cart_broom")
if(myspray)
add_overlay("cart_spray")
if(myreplacer)
diff --git a/code/game/objects/structures/mirror.dm b/code/game/objects/structures/mirror.dm
index 0a5794c59d..d4645ec69a 100644
--- a/code/game/objects/structures/mirror.dm
+++ b/code/game/objects/structures/mirror.dm
@@ -20,14 +20,14 @@
return
if(broken || !Adjacent(user))
return
-
+
if(ishuman(user))
var/mob/living/carbon/human/H = user
//see code/modules/mob/dead/new_player/preferences.dm at approx line 545 for comments!
//this is largely copypasted from there.
//handle facial hair (if necessary)
- if(H.gender == MALE)
+ if(H.gender != FEMALE)
var/new_style = input(user, "Select a facial hair style", "Grooming") as null|anything in GLOB.facial_hair_styles_list
if(!user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
return //no tele-grooming
@@ -133,7 +133,7 @@
switch(choice)
if("name")
- var/newname = copytext(sanitize(input(H, "Who are we again?", "Name change", H.name) as null|text),1,MAX_NAME_LEN)
+ var/newname = reject_bad_name(stripped_input(H, "Who are we again?", "Name change", H.name, MAX_NAME_LEN))
if(!newname)
return
diff --git a/code/game/objects/structures/morgue.dm b/code/game/objects/structures/morgue.dm
index 7696a13bb2..bf891ef223 100644
--- a/code/game/objects/structures/morgue.dm
+++ b/code/game/objects/structures/morgue.dm
@@ -168,7 +168,7 @@ GLOBAL_LIST_EMPTY(bodycontainers) //Let them act as spawnpoints for revenants an
/obj/structure/bodycontainer/morgue/AltClick(mob/user)
. = ..()
- if(!user.canUseTopic(src, !issilicon(user)))
+ if(!user.canUseTopic(src, !hasSiliconAccessInArea(user)))
return
beeper = !beeper
to_chat(user, "You turn the speaker function [beeper ? "on" : "off"].")
diff --git a/code/game/objects/structures/musician.dm b/code/game/objects/structures/musician.dm
index 4c85e7464e..df6d033af4 100644
--- a/code/game/objects/structures/musician.dm
+++ b/code/game/objects/structures/musician.dm
@@ -109,8 +109,10 @@
var/cur_note = text2ascii(note) - 96
if(cur_note < 1 || cur_note > 7)
continue
- for(var/i=2 to length(note))
- var/ni = copytext(note,i,i+1)
+ var/notelen = length(note)
+ var/ni = ""
+ for(var/i = length(note[1]) + 1, i <= notelen, i += length(ni))
+ ni = note[i]
if(!text2num(ni))
if(ni == "#" || ni == "b" || ni == "n")
cur_acc[cur_note] = ni
@@ -199,9 +201,10 @@
//split into lines
lines = splittext(text, "\n")
if(lines.len)
- if(copytext(lines[1],1,6) == "BPM: ")
- tempo = sanitize_tempo(600 / text2num(copytext(lines[1],6)))
- lines.Cut(1,2)
+ var/bpm_string = "BPM: "
+ if(findtext(lines[1], bpm_string, 1, length(bpm_string) + 1))
+ tempo = sanitize_tempo(600 / text2num(copytext(lines[1], length(bpm_string) + 1)))
+ lines.Cut(1, 2)
else
tempo = sanitize_tempo(5) // default 120 BPM
if(lines.len > MUSIC_MAXLINES)
@@ -209,7 +212,7 @@
lines.Cut(MUSIC_MAXLINES + 1)
var/linenum = 1
for(var/l in lines)
- if(length(l) > MUSIC_MAXLINECHARS)
+ if(length_char(l) > MUSIC_MAXLINECHARS)
to_chat(usr, "Line [linenum] too long!")
lines.Remove(l)
else
@@ -236,11 +239,11 @@
if(!in_range(instrumentObj, usr))
return
- if(length(t) >= MUSIC_MAXLINES * MUSIC_MAXLINECHARS)
+ if(length_char(t) >= MUSIC_MAXLINES * MUSIC_MAXLINECHARS)
var/cont = input(usr, "Your message is too long! Would you like to continue editing it?", "", "yes") in list("yes", "no")
if(cont == "no")
break
- while(length(t) > MUSIC_MAXLINES * MUSIC_MAXLINECHARS)
+ while(length_char(t) > MUSIC_MAXLINES * MUSIC_MAXLINECHARS)
ParseSong(t)
else if(href_list["help"])
@@ -284,11 +287,9 @@
else if(href_list["modifyline"])
var/num = round(text2num(href_list["modifyline"]),1)
- var/content = html_encode(input("Enter your line: ", instrumentObj.name, lines[num]) as text|null)
+ var/content = stripped_input(usr, "Enter your line: ", instrumentObj.name, lines[num], MUSIC_MAXLINECHARS)
if(!content || !in_range(instrumentObj, usr))
return
- if(length(content) > MUSIC_MAXLINECHARS)
- content = copytext(content, 1, MUSIC_MAXLINECHARS)
if(num > lines.len || num < 1)
return
lines[num] = content
diff --git a/code/game/objects/structures/tank_dispenser.dm b/code/game/objects/structures/tank_dispenser.dm
index f156287d43..5fabcafd75 100644
--- a/code/game/objects/structures/tank_dispenser.dm
+++ b/code/game/objects/structures/tank_dispenser.dm
@@ -71,7 +71,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "tank_dispenser", name, 275, 100, master_ui, state)
+ ui = new(user, src, ui_key, "tank_dispenser", name, 275, 103, master_ui, state)
ui.open()
/obj/structure/tank_dispenser/ui_data(mob/user)
diff --git a/code/game/objects/structures/traps.dm b/code/game/objects/structures/traps.dm
index 9c1859df08..2cdc082ef3 100644
--- a/code/game/objects/structures/traps.dm
+++ b/code/game/objects/structures/traps.dm
@@ -35,7 +35,7 @@
. = ..()
if(!isliving(user))
return
- if(user.mind && user.mind in immune_minds)
+ if(user.mind && (user.mind in immune_minds))
return
if(get_dist(user, src) <= 1)
. += "You reveal [src]!"
diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm
index a8f95e30da..359436dc2e 100644
--- a/code/game/objects/structures/watercloset.dm
+++ b/code/game/objects/structures/watercloset.dm
@@ -528,8 +528,8 @@
if(B.cell)
if(B.cell.charge > 0 && B.status == 1)
flick("baton_active", src)
- var/stunforce = B.stunforce
- user.Knockdown(stunforce)
+ var/stunforce = B.stamforce
+ user.Knockdown(stunforce * 2)
user.stuttering = stunforce/20
B.deductcharge(B.hitcost)
user.visible_message("[user] shocks [user.p_them()]self while attempting to wash the active [B.name]!", \
diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm
index 14a7e445c1..d4925c8604 100644
--- a/code/game/objects/structures/window.dm
+++ b/code/game/objects/structures/window.dm
@@ -595,6 +595,14 @@
/obj/structure/window/plastitanium/unanchored
anchored = FALSE
+//pirate ship windows
+/obj/structure/window/plastitanium/pirate
+ desc = "Yarr this window be explosion proof!"
+ explosion_block = 30
+
+/obj/structure/window/plastitanium/pirate/unanchored
+ anchored = FALSE
+
/obj/structure/window/reinforced/clockwork
name = "brass window"
desc = "A paper-thin pane of translucent yet reinforced brass."
diff --git a/code/game/say.dm b/code/game/say.dm
index 7f01bf163d..85ae9f0681 100644
--- a/code/game/say.dm
+++ b/code/game/say.dm
@@ -75,8 +75,8 @@ GLOBAL_LIST_INIT(freqtospan, list(
return ""
/atom/movable/proc/say_mod(input, message_mode)
- var/ending = copytext(input, length(input))
- if(copytext(input, length(input) - 1) == "!!")
+ var/ending = copytext_char(input, -1)
+ if(copytext_char(input, -2) == "!!")
return verb_yell
else if(ending == "?")
return verb_ask
@@ -89,7 +89,7 @@ GLOBAL_LIST_INIT(freqtospan, list(
if(!input)
input = "..."
- if(copytext(input, length(input) - 1) == "!!")
+ if(copytext_char(input, -2) == "!!")
spans |= SPAN_YELL
var/spanned = attach_spans(input, spans)
@@ -124,12 +124,12 @@ GLOBAL_LIST_INIT(freqtospan, list(
var/returntext = GLOB.reverseradiochannels["[freq]"]
if(returntext)
return returntext
- return "[copytext("[freq]", 1, 4)].[copytext("[freq]", 4, 5)]"
+ return "[copytext_char("[freq]", 1, 4)].[copytext_char("[freq]", 4, 5)]"
/atom/movable/proc/attach_spans(input, list/spans)
var/customsayverb = findtext(input, "*")
if(customsayverb)
- input = capitalize(copytext(input, customsayverb+1))
+ input = capitalize(copytext(input, customsayverb + length(input[customsayverb])))
if(input)
return "[message_spans_start(spans)][input]"
else
@@ -143,7 +143,7 @@ GLOBAL_LIST_INIT(freqtospan, list(
return output
/proc/say_test(text)
- var/ending = copytext(text, length(text))
+ var/ending = copytext_char(text, -1)
if (ending == "?")
return "1"
else if (ending == "!")
diff --git a/code/game/sound.dm b/code/game/sound.dm
index e7562476a8..11e026109a 100644
--- a/code/game/sound.dm
+++ b/code/game/sound.dm
@@ -184,6 +184,8 @@
soundin = pick('sound/items/bikehorn.ogg', 'sound/items/AirHorn2.ogg', 'sound/misc/sadtrombone.ogg', 'sound/items/AirHorn.ogg', 'sound/effects/reee.ogg', 'sound/items/WEEOO1.ogg', 'sound/voice/beepsky/iamthelaw.ogg', 'sound/voice/beepsky/creep.ogg','sound/magic/Fireball.ogg' ,'sound/effects/pray.ogg', 'sound/voice/hiss1.ogg','sound/machines/buzz-sigh.ogg', 'sound/machines/ping.ogg', 'sound/weapons/flashbang.ogg', 'sound/weapons/bladeslice.ogg')
if("goose")
soundin = pick('sound/creatures/goose1.ogg', 'sound/creatures/goose2.ogg', 'sound/creatures/goose3.ogg', 'sound/creatures/goose4.ogg')
+ if("water_wade")
+ soundin = pick('sound/effects/water_wade1.ogg', 'sound/effects/water_wade2.ogg', 'sound/effects/water_wade3.ogg', 'sound/effects/water_wade4.ogg')
//START OF CIT CHANGES - adds random vore sounds
if ("struggle_sound")
soundin = pick( 'sound/vore/pred/struggle_01.ogg','sound/vore/pred/struggle_02.ogg','sound/vore/pred/struggle_03.ogg',
diff --git a/code/game/turfs/open.dm b/code/game/turfs/open.dm
index 577c45a7f0..c4c7ab2d7b 100644
--- a/code/game/turfs/open.dm
+++ b/code/game/turfs/open.dm
@@ -245,48 +245,47 @@
return TRUE
/turf/open/handle_slip(mob/living/carbon/C, knockdown_amount, obj/O, lube)
- if(C.movement_type & FLYING)
- return 0
- if(has_gravity(src))
- var/obj/buckled_obj
- if(C.buckled)
- buckled_obj = C.buckled
- if(!(lube&GALOSHES_DONT_HELP)) //can't slip while buckled unless it's lube.
- return 0
- else
- if(!(lube&SLIP_WHEN_CRAWLING) && (C.lying || !(C.status_flags & CANKNOCKDOWN))) // can't slip unbuckled mob if they're lying or can't fall.
- return 0
- if(lube & NO_SLIP_WHEN_WALKING)
- if(C.m_intent == MOVE_INTENT_WALK)
- return 0
- if(ishuman(C) && !(lube & SLIP_WHEN_JOGGING))
- var/mob/living/carbon/human/H = C
- if(!H.sprinting && H.getStaminaLoss() <= 20)
- return 0
- if(!(lube&SLIDE_ICE))
- to_chat(C, "You slipped[ O ? " on the [O.name]" : ""]!")
- playsound(C.loc, 'sound/misc/slip.ogg', 50, 1, -3)
+ if(!(lube & FLYING_DOESNT_HELP) && (C.movement_type & FLYING || !has_gravity(src)))
+ return FALSE
+ var/obj/buckled_obj
+ if(C.buckled)
+ buckled_obj = C.buckled
+ if(!(lube&GALOSHES_DONT_HELP)) //can't slip while buckled unless it's lube.
+ return FALSE
+ else
+ if(!(lube&SLIP_WHEN_CRAWLING) && (C.lying || !(C.status_flags & CANKNOCKDOWN))) // can't slip unbuckled mob if they're lying or can't fall.
+ return FALSE
+ if(lube & NO_SLIP_WHEN_WALKING)
+ if(C.m_intent == MOVE_INTENT_WALK)
+ return FALSE
+ if(ishuman(C) && !(lube & SLIP_WHEN_JOGGING))
+ var/mob/living/carbon/human/H = C
+ if(!H.sprinting && H.getStaminaLoss() <= 20)
+ return FALSE
+ if(!(lube&SLIDE_ICE))
+ to_chat(C, "You slipped[ O ? " on the [O.name]" : ""]!")
+ playsound(C.loc, 'sound/misc/slip.ogg', 50, 1, -3)
- SEND_SIGNAL(C, COMSIG_ADD_MOOD_EVENT, "slipped", /datum/mood_event/slipped)
- for(var/obj/item/I in C.held_items)
- C.accident(I)
+ SEND_SIGNAL(C, COMSIG_ADD_MOOD_EVENT, "slipped", /datum/mood_event/slipped)
+ for(var/obj/item/I in C.held_items)
+ C.accident(I)
- var/olddir = C.dir
- if(!(lube & SLIDE_ICE))
- C.Knockdown(knockdown_amount)
- C.stop_pulling()
- else
- C.Stun(20)
+ var/olddir = C.dir
+ if(!(lube & SLIDE_ICE))
+ C.Knockdown(knockdown_amount)
+ C.stop_pulling()
+ else
+ C.Stun(20)
- if(buckled_obj)
- buckled_obj.unbuckle_mob(C)
- lube |= SLIDE_ICE
+ if(buckled_obj)
+ buckled_obj.unbuckle_mob(C)
+ lube |= SLIDE_ICE
- if(lube&SLIDE)
- new /datum/forced_movement(C, get_ranged_target_turf(C, olddir, 4), 1, FALSE, CALLBACK(C, /mob/living/carbon/.proc/spin, 1, 1))
- else if(lube&SLIDE_ICE)
- new /datum/forced_movement(C, get_ranged_target_turf(C, olddir, 1), 1, FALSE) //spinning would be bad for ice, fucks up the next dir
- return 1
+ if(lube&SLIDE)
+ new /datum/forced_movement(C, get_ranged_target_turf(C, olddir, 4), 1, FALSE, CALLBACK(C, /mob/living/carbon/.proc/spin, 1, 1))
+ else if(lube&SLIDE_ICE)
+ new /datum/forced_movement(C, get_ranged_target_turf(C, olddir, 1), 1, FALSE) //spinning would be bad for ice, fucks up the next dir
+ return TRUE
/turf/open/proc/MakeSlippery(wet_setting = TURF_WET_WATER, min_wet_time = 0, wet_time_to_add = 0, max_wet_time = MAXIMUM_WET_TIME, permanent)
AddComponent(/datum/component/wet_floor, wet_setting, min_wet_time, wet_time_to_add, max_wet_time, permanent)
diff --git a/code/game/turfs/simulated/wall/mineral_walls.dm b/code/game/turfs/simulated/wall/mineral_walls.dm
index be46d124ea..b04f4f0aa0 100644
--- a/code/game/turfs/simulated/wall/mineral_walls.dm
+++ b/code/game/turfs/simulated/wall/mineral_walls.dm
@@ -138,7 +138,8 @@
var/duration = (48/W.force) * 2 //In seconds, for now.
if(istype(W, /obj/item/hatchet) || istype(W, /obj/item/twohanded/fireaxe))
duration /= 4 //Much better with hatchets and axes.
- if(do_after(user, duration*10, target=src)) //Into deciseconds.
+ var/src_type = type
+ if(do_after(user, duration*10, target=src) && type == src_type) //Into deciseconds.
dismantle_wall(FALSE,FALSE)
return
return ..()
@@ -301,4 +302,4 @@
/turf/closed/wall/mineral/plastitanium/copyTurf(turf/T)
. = ..()
- T.transform = transform
+ T.transform = transform
\ No newline at end of file
diff --git a/code/game/turfs/simulated/wall/reinf_walls.dm b/code/game/turfs/simulated/wall/reinf_walls.dm
index 8eab4c34fe..a1d2c1757c 100644
--- a/code/game/turfs/simulated/wall/reinf_walls.dm
+++ b/code/game/turfs/simulated/wall/reinf_walls.dm
@@ -258,3 +258,22 @@
/turf/closed/wall/r_wall/syndicate/overspace
icon_state = "map-overspace"
fixed_underlay = list("space"=1)
+
+/////////////////////Pirate Ship walls/////////////////////
+
+/turf/closed/wall/r_wall/syndicate/pirate
+ desc = "Yarr just try to blow this to smithereens!"
+ explosion_block = 30
+ canSmoothWith = list(/turf/closed/wall/r_wall/syndicate/pirate, /obj/machinery/door/airlock/shuttle, /obj/machinery/door/airlock, /obj/structure/window/plastitanium/pirate, /obj/structure/shuttle/engine, /obj/structure/falsewall/plastitanium)
+
+/turf/closed/wall/r_wall/syndicate/pirate/nodiagonal
+ smooth = SMOOTH_MORE
+ icon_state = "map-shuttle_nd"
+
+/turf/closed/wall/r_wall/syndicate/pirate/nosmooth
+ icon = 'icons/turf/shuttle.dmi'
+ icon_state = "wall"
+
+/turf/closed/wall/r_wall/syndicate/pirate/overspace
+ icon_state = "map-overspace"
+ fixed_underlay = list("space"=1)
diff --git a/code/game/turfs/simulated/walls.dm b/code/game/turfs/simulated/walls.dm
index 460c6a52c9..d26a6efb02 100644
--- a/code/game/turfs/simulated/walls.dm
+++ b/code/game/turfs/simulated/walls.dm
@@ -171,7 +171,8 @@
var/turf/T = user.loc //get user's location for delay checks
//the istype cascade has been spread among various procs for easy overriding
- if(try_clean(W, user, T) || try_wallmount(W, user, T) || try_decon(W, user, T) || try_destroy(W, user, T))
+ var/srctype = type
+ if(try_clean(W, user, T) || try_wallmount(W, user, T) || try_decon(W, user, T) || (type == srctype && try_destroy(W, user, T)))
return
return ..()
diff --git a/code/game/turfs/space/space.dm b/code/game/turfs/space/space.dm
index 5278374abd..26daa32107 100644
--- a/code/game/turfs/space/space.dm
+++ b/code/game/turfs/space/space.dm
@@ -88,8 +88,9 @@
/turf/open/space/proc/CanBuildHere()
return TRUE
-/turf/open/space/handle_slip()
- return
+/turf/open/space/handle_slip(mob/living/carbon/C, knockdown_amount, obj/O, lube)
+ if(lube & FLYING_DOESNT_HELP)
+ return ..()
/turf/open/space/attackby(obj/item/C, mob/user, params)
..()
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index d8434f986c..578d6d10ae 100755
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -363,13 +363,13 @@
if(.)
return
if(length(src_object.contents()))
- to_chat(usr, "You start dumping out the contents...")
- if(!do_after(usr,20,target=src_object.parent))
+ to_chat(user, "You start dumping out the contents...")
+ if(!do_after(user,20,target=src_object.parent))
return FALSE
var/list/things = src_object.contents()
var/datum/progressbar/progress = new(user, things.len, src)
- while (do_after(usr, 10, TRUE, src, FALSE, CALLBACK(src_object, /datum/component/storage.proc/mass_remove_from_storage, src, things, progress)))
+ while (do_after(user, 10, TRUE, src, FALSE, CALLBACK(src_object, /datum/component/storage.proc/mass_remove_from_storage, src, things, progress)))
stoplag(1)
qdel(progress)
@@ -552,6 +552,8 @@
//if the vomit combined, apply toxicity and reagents to the old vomit
if (QDELETED(V))
V = locate() in src
+ if(!V) //the decal was spawned on a wall or groundless turf and promptly qdeleted.
+ return
// Make toxins and blazaam vomit look different
if(toxvomit == VOMIT_PURPLE)
V.icon_state = "vomitpurp_[pick(1,4)]"
diff --git a/code/game/world.dm b/code/game/world.dm
index 7afaf62bb8..77361310b8 100644
--- a/code/game/world.dm
+++ b/code/game/world.dm
@@ -8,6 +8,11 @@ GLOBAL_LIST(topic_status_cache)
//This happens after the Master subsystem new(s) (it's a global datum)
//So subsystems globals exist, but are not initialised
/world/New()
+ enable_debugger()
+
+#if DM_VERSION >= 513 && DM_BUILD >= 1506
+ world.Profile(PROFILE_START)
+#endif
log_world("World loaded at [TIME_STAMP("hh:mm:ss", FALSE)]!")
@@ -17,7 +22,7 @@ GLOBAL_LIST(topic_status_cache)
make_datum_references_lists() //initialises global lists for referencing frequently used datums (so that we only ever do it once)
- TgsNew()
+ TgsNew(minimum_required_security_level = TGS_SECURITY_TRUSTED)
GLOB.revdata = new
@@ -115,9 +120,12 @@ GLOBAL_LIST(topic_status_cache)
GLOB.world_runtime_log = "[GLOB.log_directory]/runtime.log"
GLOB.query_debug_log = "[GLOB.log_directory]/query_debug.log"
GLOB.world_job_debug_log = "[GLOB.log_directory]/job_debug.log"
+ GLOB.tgui_log = "[GLOB.log_directory]/tgui.log"
GLOB.subsystem_log = "[GLOB.log_directory]/subsystem.log"
+ GLOB.reagent_log = "[GLOB.log_directory]/reagents.log"
GLOB.world_crafting_log = "[GLOB.log_directory]/crafting.log"
+
#ifdef UNIT_TESTS
GLOB.test_log = file("[GLOB.log_directory]/tests.log")
start_log(GLOB.test_log)
@@ -131,7 +139,9 @@ GLOBAL_LIST(topic_status_cache)
start_log(GLOB.world_qdel_log)
start_log(GLOB.world_runtime_log)
start_log(GLOB.world_job_debug_log)
+ start_log(GLOB.tgui_log)
start_log(GLOB.subsystem_log)
+ start_log(GLOB.reagent_log)
start_log(GLOB.world_crafting_log)
GLOB.changelog_hash = md5('html/changelog.html') //for telling if the changelog has changed recently
diff --git a/code/modules/NTNet/network.dm b/code/modules/NTNet/network.dm
index 645f05ac90..d86ad792fe 100644
--- a/code/modules/NTNet/network.dm
+++ b/code/modules/NTNet/network.dm
@@ -133,14 +133,14 @@
return FALSE
/datum/ntnet/proc/log_data_transfer(datum/netdata/data)
- logs += "[STATION_TIME_TIMESTAMP("hh:mm:ss")] - [data.generate_netlog()]"
+ logs += "[STATION_TIME_TIMESTAMP("hh:mm:ss", world.time)] - [data.generate_netlog()]"
if(logs.len > setting_maxlogcount)
logs = logs.Copy(logs.len - setting_maxlogcount, 0)
return
// Simplified logging: Adds a log. log_string is mandatory parameter, source is optional.
/datum/ntnet/proc/add_log(log_string, obj/item/computer_hardware/network_card/source = null)
- var/log_text = "[STATION_TIME_TIMESTAMP("hh:mm:ss")] - "
+ var/log_text = "[STATION_TIME_TIMESTAMP("hh:mm:ss", world.time)] - "
if(source)
log_text += "[source.get_network_tag()] - "
else
@@ -202,6 +202,11 @@
if(filename == P.filename)
return P
+/datum/ntnet/proc/get_chat_channel_by_id(id)
+ for(var/datum/ntnet_conversation/chan in chat_channels)
+ if(chan.id == id)
+ return chan
+
// Resets the IDS alarm
/datum/ntnet/proc/resetIDS()
intrusion_detection_alarm = FALSE
diff --git a/code/modules/NTNet/relays.dm b/code/modules/NTNet/relays.dm
index eaf2aa466b..373f6451b9 100644
--- a/code/modules/NTNet/relays.dm
+++ b/code/modules/NTNet/relays.dm
@@ -9,6 +9,9 @@
icon_state = "bus"
density = TRUE
circuit = /obj/item/circuitboard/machine/ntnet_relay
+ ui_x = 400
+ ui_y = 300
+
var/datum/ntnet/NTNet = null // This is mostly for backwards reference and to allow varedit modifications from ingame.
var/enabled = 1 // Set to 0 if the relay was turned off
var/dos_failure = 0 // Set to 1 if the relay failed due to (D)DoS attack
@@ -66,7 +69,7 @@
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "ntnet_relay", "NTNet Quantum Relay", 500, 300, master_ui, state)
+ ui = new(user, src, ui_key, "ntnet_relay", "NTNet Quantum Relay", ui_x, ui_y, master_ui, state)
ui.open()
@@ -88,10 +91,12 @@
dos_failure = 0
update_icon()
SSnetworks.station_network.add_log("Quantum relay manually restarted from overload recovery mode to normal operation mode.")
+ return TRUE
if("toggle")
enabled = !enabled
SSnetworks.station_network.add_log("Quantum relay manually [enabled ? "enabled" : "disabled"].")
update_icon()
+ return TRUE
/obj/machinery/ntnet_relay/Initialize()
uid = gl_uid++
@@ -113,4 +118,4 @@
D.target = null
D.error = "Connection to quantum relay severed"
- return ..()
+ return ..()
\ No newline at end of file
diff --git a/code/modules/VR/vr_sleeper.dm b/code/modules/VR/vr_sleeper.dm
index 7cda24d98b..29d7224950 100644
--- a/code/modules/VR/vr_sleeper.dm
+++ b/code/modules/VR/vr_sleeper.dm
@@ -222,18 +222,23 @@
/obj/effect/vr_clean_master/Initialize()
. = ..()
- vr_area = get_area(src)
- addtimer(CALLBACK(src, .proc/clean_up), 3 MINUTES)
+ vr_area = get_base_area(src)
+ if(!vr_area)
+ return INITIALIZE_HINT_QDEL
+ addtimer(CALLBACK(src, .proc/clean_up), 3 MINUTES, TIMER_LOOP)
/obj/effect/vr_clean_master/proc/clean_up()
- if (vr_area)
- for (var/obj/item/ammo_casing/casing in vr_area)
- qdel(casing)
- for(var/obj/effect/decal/cleanable/C in vr_area)
- qdel(C)
- for (var/A in corpse_party)
- var/mob/M = A
- if(M && M.stat == DEAD && get_area(M) == vr_area)
- qdel(M)
- corpse_party -= M
- addtimer(CALLBACK(src, .proc/clean_up), 3 MINUTES)
+ if (!vr_area)
+ qdel(src)
+ return
+ var/list/contents = get_sub_areas_contents(vr_area)
+ for (var/obj/item/ammo_casing/casing in contents)
+ qdel(casing)
+ for(var/obj/effect/decal/cleanable/C in contents)
+ qdel(C)
+ for (var/A in corpse_party)
+ var/mob/M = A
+ if(!QDELETED(M) && (M in contents) && M.stat == DEAD)
+ qdel(M)
+ corpse_party -= M
+ addtimer(CALLBACK(src, .proc/clean_up), 3 MINUTES)
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index 372902e8d6..bb0224bbfc 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -240,7 +240,7 @@
if( isemptylist(GLOB.news_network.network_channels) )
dat+="No active channels found..."
else
- for(var/datum/newscaster/feed_channel/CHANNEL in GLOB.news_network.network_channels)
+ for(var/datum/news/feed_channel/CHANNEL in GLOB.news_network.network_channels)
if(CHANNEL.is_admin_channel)
dat+="[CHANNEL.channel_name] "
else
@@ -277,7 +277,7 @@
if(src.admincaster_feed_channel.channel_name =="" || src.admincaster_feed_channel.channel_name == "\[REDACTED\]")
dat+="Invalid channel name. "
var/check = 0
- for(var/datum/newscaster/feed_channel/FC in GLOB.news_network.network_channels)
+ for(var/datum/news/feed_channel/FC in GLOB.news_network.network_channels)
if(FC.channel_name == src.admincaster_feed_channel.channel_name)
check = 1
break
@@ -294,7 +294,7 @@
dat+="No feed messages found in channel... "
else
var/i = 0
- for(var/datum/newscaster/feed_message/MESSAGE in src.admincaster_feed_channel.messages)
+ for(var/datum/news/feed_message/MESSAGE in src.admincaster_feed_channel.messages)
i++
dat+="-[MESSAGE.returnBody(-1)] "
if(MESSAGE.img)
@@ -302,7 +302,7 @@
dat+="
"
dat+="\[Story by [MESSAGE.returnAuthor(-1)]\] "
dat+="[MESSAGE.comments.len] comment[MESSAGE.comments.len > 1 ? "s" : ""]: "
- for(var/datum/newscaster/feed_comment/comment in MESSAGE.comments)
+ for(var/datum/news/feed_comment/comment in MESSAGE.comments)
dat+="[comment.body] [comment.author] [comment.time_stamp] "
dat+=" "
dat+=" Refresh"
@@ -315,7 +315,7 @@
if(isemptylist(GLOB.news_network.network_channels))
dat+="No feed channels found active... "
else
- for(var/datum/newscaster/feed_channel/CHANNEL in GLOB.news_network.network_channels)
+ for(var/datum/news/feed_channel/CHANNEL in GLOB.news_network.network_channels)
dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("***") : ""] "
dat+=" Cancel"
if(11)
@@ -326,7 +326,7 @@
if(isemptylist(GLOB.news_network.network_channels))
dat+="No feed channels found active... "
else
- for(var/datum/newscaster/feed_channel/CHANNEL in GLOB.news_network.network_channels)
+ for(var/datum/news/feed_channel/CHANNEL in GLOB.news_network.network_channels)
dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("***") : ""] "
dat+=" Back"
@@ -337,11 +337,11 @@
if( isemptylist(src.admincaster_feed_channel.messages) )
dat+="No feed messages found in channel... "
else
- for(var/datum/newscaster/feed_message/MESSAGE in src.admincaster_feed_channel.messages)
+ for(var/datum/news/feed_message/MESSAGE in src.admincaster_feed_channel.messages)
dat+="-[MESSAGE.returnBody(-1)] \[Story by [MESSAGE.returnAuthor(-1)]\] "
dat+="[(MESSAGE.bodyCensor) ? ("Undo story censorship") : ("Censor story")] - [(MESSAGE.authorCensor) ? ("Undo Author Censorship") : ("Censor message Author")] "
dat+="[MESSAGE.comments.len] comment[MESSAGE.comments.len > 1 ? "s" : ""]: [MESSAGE.locked ? "Unlock" : "Lock"] "
- for(var/datum/newscaster/feed_comment/comment in MESSAGE.comments)
+ for(var/datum/news/feed_comment/comment in MESSAGE.comments)
dat+="[comment.body] X [comment.author] [comment.time_stamp] "
dat+=" Back"
if(13)
@@ -354,7 +354,7 @@
if( isemptylist(src.admincaster_feed_channel.messages) )
dat+="No feed messages found in channel... "
else
- for(var/datum/newscaster/feed_message/MESSAGE in src.admincaster_feed_channel.messages)
+ for(var/datum/news/feed_message/MESSAGE in src.admincaster_feed_channel.messages)
dat+="-[MESSAGE.returnBody(-1)] \[Story by [MESSAGE.returnAuthor(-1)]\] "
dat+=" Back"
if(14)
diff --git a/code/modules/admin/admin_ranks.dm b/code/modules/admin/admin_ranks.dm
index 4f2aa35860..b5127929a7 100644
--- a/code/modules/admin/admin_ranks.dm
+++ b/code/modules/admin/admin_ranks.dm
@@ -135,7 +135,7 @@ GLOBAL_PROTECT(protected_ranks)
var/previous_rights = 0
//load text from file and process each line separately
for(var/line in world.file2list("[global.config.directory]/admin_ranks.txt"))
- if(!line || findtextEx(line,"#",1,2))
+ if(!line || findtextEx_char(line,"#",1,2))
continue
var/next = findtext(line, "=")
var/datum/admin_rank/R = new(ckeyEx(copytext(line, 1, next)))
@@ -145,7 +145,7 @@ GLOBAL_PROTECT(protected_ranks)
GLOB.protected_ranks += R
var/prev = findchar(line, "+-*", next, 0)
while(prev)
- next = findchar(line, "+-*", prev + 1, 0)
+ next = findchar(line, "+-*", prev + length(line[prev]), 0)
R.process_keyword(copytext(line, prev, next), previous_rights)
prev = next
previous_rights = R.rights
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 8a7269ccad..3ccd113864 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -343,12 +343,12 @@ GLOBAL_PROTECT(admin_verbs_hideable)
set category = "Admin"
set name = "Aghost"
if(!holder)
- return
+ return FALSE
if(isobserver(mob))
//re-enter
var/mob/dead/observer/ghost = mob
if(!ghost.mind || !ghost.mind.current) //won't do anything if there is no body
- return
+ return FALSE
if(!ghost.can_reenter_corpse)
log_admin("[key_name(usr)] re-entered corpse")
message_admins("[key_name_admin(usr)] re-entered corpse")
@@ -357,6 +357,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Admin Reenter") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
else if(isnewplayer(mob))
to_chat(src, "Error: Aghost: Can't admin-ghost whilst in the lobby. Join or Observe first.")
+ return FALSE
else
//ghostize
log_admin("[key_name(usr)] admin ghosted.")
@@ -366,7 +367,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
if(body && !body.key)
body.key = "@[key]" //Haaaaaaaack. But the people have spoken. If it breaks; blame adminbus
SSblackbox.record_feedback("tally", "admin_verb", 1, "Admin Ghost") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
+ return TRUE
/client/proc/invisimin()
set name = "Invisimin"
@@ -448,11 +449,9 @@ GLOBAL_PROTECT(admin_verbs_hideable)
mob.name = initial(mob.name)
mob.mouse_opacity = initial(mob.mouse_opacity)
else
- var/new_key = ckeyEx(input("Enter your desired display name.", "Fake Key", key) as text|null)
+ var/new_key = ckeyEx(stripped_input(usr, "Enter your desired display name.", "Fake Key", key, 26))
if(!new_key)
return
- if(length(new_key) >= 26)
- new_key = copytext(new_key, 1, 26)
holder.fakekey = new_key
createStealthKey()
if(isobserver(mob))
@@ -559,9 +558,9 @@ GLOBAL_PROTECT(admin_verbs_hideable)
set desc = "Gives a spell to a mob."
var/list/spell_list = list()
- var/type_length = length("/obj/effect/proc_holder/spell") + 2
+ var/type_length = length_char("/obj/effect/proc_holder/spell") + 2
for(var/A in GLOB.spells)
- spell_list[copytext("[A]", type_length)] = A
+ spell_list[copytext_char("[A]", type_length)] = A
var/obj/effect/proc_holder/spell/S = input("Choose the spell to give to that guy", "ABRAKADABRA") as null|anything in spell_list
if(!S)
return
@@ -715,7 +714,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
AI_Interact = !AI_Interact
if(mob && IsAdminGhost(mob))
- mob.has_unlimited_silicon_privilege = AI_Interact
+ mob.silicon_privileges = AI_Interact ? ALL : NONE
log_admin("[key_name(usr)] has [AI_Interact ? "activated" : "deactivated"] Admin AI Interact")
message_admins("[key_name_admin(usr)] has [AI_Interact ? "activated" : "deactivated"] their AI interaction")
diff --git a/code/modules/admin/antag_panel.dm b/code/modules/admin/antag_panel.dm
index 1672310567..19e53b09b4 100644
--- a/code/modules/admin/antag_panel.dm
+++ b/code/modules/admin/antag_panel.dm
@@ -214,4 +214,4 @@ GLOBAL_VAR(antag_prototypes)
var/datum/browser/panel = new(usr, "traitorpanel", "", 600, 600)
panel.set_content(out)
panel.open()
- return
\ No newline at end of file
+ return
diff --git a/code/modules/admin/chat_commands.dm b/code/modules/admin/chat_commands.dm
index 52ba3c521e..e2ddcdcf6a 100644
--- a/code/modules/admin/chat_commands.dm
+++ b/code/modules/admin/chat_commands.dm
@@ -137,7 +137,8 @@ GLOBAL_LIST(round_end_notifiees)
return "The Database is not enabled!"
GLOB.bunker_passthrough |= ckey(params)
-
+ GLOB.bunker_passthrough[ckey(params)] = world.realtime
+ SSpersistence.SavePanicBunker() //we can do this every time, it's okay
log_admin("[sender.friendly_name] has added [params] to the current round's bunker bypass list.")
message_admins("[sender.friendly_name] has added [params] to the current round's bunker bypass list.")
return "[params] has been added to the current round's bunker bypass list."
diff --git a/code/modules/admin/check_antagonists.dm b/code/modules/admin/check_antagonists.dm
index 30fa664f42..8cb2a9a477 100644
--- a/code/modules/admin/check_antagonists.dm
+++ b/code/modules/admin/check_antagonists.dm
@@ -149,10 +149,10 @@
else
var/timeleft = SSshuttle.emergency.timeLeft()
if(SSshuttle.emergency.mode == SHUTTLE_CALL)
- dat += "ETA: [(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)] "
+ dat += "ETA: [(timeleft / 60) % 60]:[add_leading(num2text(timeleft % 60), 2, "0")] "
dat += "Send Back "
else
- dat += "ETA: [(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)] "
+ dat += "ETA: [(timeleft / 60) % 60]:[add_leading(num2text(timeleft % 60), 2, "0")] "
dat += "Continuous Round Status "
dat += "[CONFIG_GET(keyed_list/continuous)[SSticker.mode.config_tag] ? "Continue if antagonists die" : "End on antagonist death"]"
if(CONFIG_GET(keyed_list/continuous)[SSticker.mode.config_tag])
diff --git a/code/modules/admin/holder2.dm b/code/modules/admin/holder2.dm
index 43360ad714..37fe2a41fc 100644
--- a/code/modules/admin/holder2.dm
+++ b/code/modules/admin/holder2.dm
@@ -19,9 +19,9 @@ GLOBAL_PROTECT(href_token)
var/spamcooldown = 0
var/admincaster_screen = 0 //TODO: remove all these 5 variables, they are completly unacceptable
- var/datum/newscaster/feed_message/admincaster_feed_message = new /datum/newscaster/feed_message
- var/datum/newscaster/wanted_message/admincaster_wanted_message = new /datum/newscaster/wanted_message
- var/datum/newscaster/feed_channel/admincaster_feed_channel = new /datum/newscaster/feed_channel
+ var/datum/news/feed_message/admincaster_feed_message = new /datum/news/feed_message
+ var/datum/news/wanted_message/admincaster_wanted_message = new /datum/news/wanted_message
+ var/datum/news/feed_channel/admincaster_feed_channel = new /datum/news/feed_channel
var/admin_signature
var/href_token
diff --git a/code/modules/admin/secrets.dm b/code/modules/admin/secrets.dm
index 70ae133a31..68ae3568af 100644
--- a/code/modules/admin/secrets.dm
+++ b/code/modules/admin/secrets.dm
@@ -345,7 +345,7 @@
if(!SSticker.HasRoundStarted())
alert("The game hasn't started yet!")
return
- var/objective = copytext(sanitize(input("Enter an objective")),1,MAX_MESSAGE_LEN)
+ var/objective = stripped_input(usr, "Enter an objective")
if(!objective)
return
SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Traitor All", "[objective]"))
@@ -408,7 +408,7 @@
var/obj/item/organ/tail/cat/tail = new
ears.Insert(H, drop_if_replaced=FALSE)
tail.Insert(H, drop_if_replaced=FALSE)
- var/list/honorifics = list("[MALE]" = list("kun"), "[FEMALE]" = list("chan","tan"), "[NEUTER]" = list("san")) //John Robust -> Robust-kun
+ var/list/honorifics = list("[MALE]" = list("kun"), "[FEMALE]" = list("chan","tan"), "[NEUTER]" = list("san"), "[PLURAL]" = list("san")) //John Robust -> Robust-kun
var/list/names = splittext(H.real_name," ")
var/forename = names.len > 1 ? names[2] : names[1]
var/newname = "[forename]-[pick(honorifics["[H.gender]"])]"
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index c66927b856..6abf2d3f1b 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -845,7 +845,11 @@
dat += "
"
dat += ""
//Antagonist (Orange)
@@ -1979,8 +1983,8 @@
var/atom/movable/AM = locate(href_list["adminplayerobservefollow"])
var/client/C = usr.client
- if(!isobserver(usr))
- C.admin_ghost()
+ if(!isobserver(usr) && !C.admin_ghost())
+ return
var/mob/dead/observer/A = C.mob
A.ManualFollow(AM)
@@ -2002,8 +2006,8 @@
var/z = text2num(href_list["Z"])
var/client/C = usr.client
- if(!isobserver(usr))
- C.admin_ghost()
+ if(!isobserver(usr) && !C.admin_ghost())
+ return
sleep(2)
C.jumptocoord(x,y,z)
@@ -2465,8 +2469,6 @@
if(!check_rights(R_ADMIN))
return
src.admincaster_feed_channel.channel_name = stripped_input(usr, "Provide a Feed Channel Name.", "Network Channel Handler", "")
- while (findtext(src.admincaster_feed_channel.channel_name," ") == 1)
- src.admincaster_feed_channel.channel_name = copytext(src.admincaster_feed_channel.channel_name,2,length(src.admincaster_feed_channel.channel_name)+1)
src.access_news_network()
else if(href_list["ac_set_channel_lock"])
@@ -2479,7 +2481,7 @@
if(!check_rights(R_ADMIN))
return
var/check = 0
- for(var/datum/newscaster/feed_channel/FC in GLOB.news_network.network_channels)
+ for(var/datum/news/feed_channel/FC in GLOB.news_network.network_channels)
if(FC.channel_name == src.admincaster_feed_channel.channel_name)
check = 1
break
@@ -2498,7 +2500,7 @@
if(!check_rights(R_ADMIN))
return
var/list/available_channels = list()
- for(var/datum/newscaster/feed_channel/F in GLOB.news_network.network_channels)
+ for(var/datum/news/feed_channel/F in GLOB.news_network.network_channels)
available_channels += F.channel_name
src.admincaster_feed_channel.channel_name = adminscrub(input(usr, "Choose receiving Feed Channel.", "Network Channel Handler") in available_channels )
src.access_news_network()
@@ -2506,9 +2508,7 @@
else if(href_list["ac_set_new_message"])
if(!check_rights(R_ADMIN))
return
- src.admincaster_feed_message.body = adminscrub(input(usr, "Write your Feed story.", "Network Channel Handler", ""))
- while (findtext(src.admincaster_feed_message.returnBody(-1)," ") == 1)
- src.admincaster_feed_message.body = copytext(src.admincaster_feed_message.returnBody(-1),2,length(src.admincaster_feed_message.returnBody(-1))+1)
+ src.admincaster_feed_message.body = adminscrub(stripped_input(usr, "Write your Feed story.", "Network Channel Handler", ""))
src.access_news_network()
else if(href_list["ac_submit_new_message"])
@@ -2567,17 +2567,13 @@
else if(href_list["ac_set_wanted_name"])
if(!check_rights(R_ADMIN))
return
- src.admincaster_wanted_message.criminal = adminscrub(input(usr, "Provide the name of the Wanted person.", "Network Security Handler", ""))
- while(findtext(src.admincaster_wanted_message.criminal," ") == 1)
- src.admincaster_wanted_message.criminal = copytext(admincaster_wanted_message.criminal,2,length(admincaster_wanted_message.criminal)+1)
+ src.admincaster_wanted_message.criminal = adminscrub(stripped_input(usr, "Provide the name of the Wanted person.", "Network Security Handler", ""))
src.access_news_network()
else if(href_list["ac_set_wanted_desc"])
if(!check_rights(R_ADMIN))
return
- src.admincaster_wanted_message.body = adminscrub(input(usr, "Provide the a description of the Wanted person and any other details you deem important.", "Network Security Handler", ""))
- while (findtext(src.admincaster_wanted_message.body," ") == 1)
- src.admincaster_wanted_message.body = copytext(src.admincaster_wanted_message.body,2,length(src.admincaster_wanted_message.body)+1)
+ src.admincaster_wanted_message.body = adminscrub(stripped_input(usr, "Provide the a description of the Wanted person and any other details you deem important.", "Network Security Handler", ""))
src.access_news_network()
else if(href_list["ac_submit_wanted"])
@@ -2610,28 +2606,28 @@
else if(href_list["ac_censor_channel_author"])
if(!check_rights(R_ADMIN))
return
- var/datum/newscaster/feed_channel/FC = locate(href_list["ac_censor_channel_author"])
+ var/datum/news/feed_channel/FC = locate(href_list["ac_censor_channel_author"])
FC.toggleCensorAuthor()
src.access_news_network()
else if(href_list["ac_censor_channel_story_author"])
if(!check_rights(R_ADMIN))
return
- var/datum/newscaster/feed_message/MSG = locate(href_list["ac_censor_channel_story_author"])
+ var/datum/news/feed_message/MSG = locate(href_list["ac_censor_channel_story_author"])
MSG.toggleCensorAuthor()
src.access_news_network()
else if(href_list["ac_censor_channel_story_body"])
if(!check_rights(R_ADMIN))
return
- var/datum/newscaster/feed_message/MSG = locate(href_list["ac_censor_channel_story_body"])
+ var/datum/news/feed_message/MSG = locate(href_list["ac_censor_channel_story_body"])
MSG.toggleCensorBody()
src.access_news_network()
else if(href_list["ac_pick_d_notice"])
if(!check_rights(R_ADMIN))
return
- var/datum/newscaster/feed_channel/FC = locate(href_list["ac_pick_d_notice"])
+ var/datum/news/feed_channel/FC = locate(href_list["ac_pick_d_notice"])
src.admincaster_feed_channel = FC
src.admincaster_screen=13
src.access_news_network()
@@ -2639,7 +2635,7 @@
else if(href_list["ac_toggle_d_notice"])
if(!check_rights(R_ADMIN))
return
- var/datum/newscaster/feed_channel/FC = locate(href_list["ac_toggle_d_notice"])
+ var/datum/news/feed_channel/FC = locate(href_list["ac_toggle_d_notice"])
FC.toggleCensorDclass()
src.access_news_network()
@@ -2655,17 +2651,17 @@
src.admincaster_screen = text2num(href_list["ac_setScreen"])
if (src.admincaster_screen == 0)
if(src.admincaster_feed_channel)
- src.admincaster_feed_channel = new /datum/newscaster/feed_channel
+ src.admincaster_feed_channel = new /datum/news/feed_channel
if(src.admincaster_feed_message)
- src.admincaster_feed_message = new /datum/newscaster/feed_message
+ src.admincaster_feed_message = new /datum/news/feed_message
if(admincaster_wanted_message)
- admincaster_wanted_message = new /datum/newscaster/wanted_message
+ admincaster_wanted_message = new /datum/news/wanted_message
src.access_news_network()
else if(href_list["ac_show_channel"])
if(!check_rights(R_ADMIN))
return
- var/datum/newscaster/feed_channel/FC = locate(href_list["ac_show_channel"])
+ var/datum/news/feed_channel/FC = locate(href_list["ac_show_channel"])
src.admincaster_feed_channel = FC
src.admincaster_screen = 9
src.access_news_network()
@@ -2673,7 +2669,7 @@
else if(href_list["ac_pick_censor_channel"])
if(!check_rights(R_ADMIN))
return
- var/datum/newscaster/feed_channel/FC = locate(href_list["ac_pick_censor_channel"])
+ var/datum/news/feed_channel/FC = locate(href_list["ac_pick_censor_channel"])
src.admincaster_feed_channel = FC
src.admincaster_screen = 12
src.access_news_network()
@@ -2692,8 +2688,8 @@
else if(href_list["ac_del_comment"])
if(!check_rights(R_ADMIN))
return
- var/datum/newscaster/feed_comment/FC = locate(href_list["ac_del_comment"])
- var/datum/newscaster/feed_message/FM = locate(href_list["ac_del_comment_msg"])
+ var/datum/news/feed_comment/FC = locate(href_list["ac_del_comment"])
+ var/datum/news/feed_message/FM = locate(href_list["ac_del_comment_msg"])
FM.comments -= FC
qdel(FC)
src.access_news_network()
@@ -2701,7 +2697,7 @@
else if(href_list["ac_lock_comment"])
if(!check_rights(R_ADMIN))
return
- var/datum/newscaster/feed_message/FM = locate(href_list["ac_lock_comment"])
+ var/datum/news/feed_message/FM = locate(href_list["ac_lock_comment"])
FM.locked ^= 1
src.access_news_network()
@@ -2878,6 +2874,8 @@
return
if(SSdbcore.Connect())
var/datum/DBQuery/query_get_mentor = SSdbcore.NewQuery("SELECT id FROM [format_table_name("mentor")] WHERE ckey = '[ckey]'")
+ if(!query_get_mentor.warn_execute())
+ return
if(query_get_mentor.NextRow())
to_chat(usr, "[ckey] is already a mentor.")
return
diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2.dm b/code/modules/admin/verbs/SDQL2/SDQL_2.dm
index d2693a83b6..14906bc83a 100644
--- a/code/modules/admin/verbs/SDQL2/SDQL_2.dm
+++ b/code/modules/admin/verbs/SDQL2/SDQL_2.dm
@@ -303,7 +303,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null
//These three are weird. For best performance, they are only a number when they're not being changed by the SDQL searching/execution code. They only become numbers when they finish changing.
var/list/obj_count_all
var/list/obj_count_eligible
- var/list/obj_count_finished
+ var/obj_count_finished
//Statclick
var/obj/effect/statclick/SDQL2_delete/delete_click
@@ -682,7 +682,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null
SDQL2_TICK_CHECK
SDQL2_HALT_CHECK
if(islist(obj_count_finished))
- obj_count_finished = obj_count_finished.len
+ obj_count_finished = length(obj_count_finished)
state = SDQL2_STATE_SWITCHING
/datum/SDQL2_query/proc/SDQL_print(object, list/text_list, print_nulls = TRUE)
@@ -869,8 +869,8 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null
else if(ispath(expression[i]))
val = expression[i]
- else if(copytext(expression[i], 1, 2) in list("'", "\""))
- val = copytext(expression[i], 2, length(expression[i]))
+ else if(expression[i][1] in list("'", "\""))
+ val = copytext_char(expression[i], 2, -1)
else if(expression[i] == "\[")
var/list/expressions_list = expression[++i]
@@ -961,11 +961,11 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null
if(is_proper_datum(object))
D = object
- if (object == world && (!long || expression[start + 1] == ".") && !(expression[start] in exclude))
+ if (object == world && (!long || expression[start + 1] == ".") && !(expression[start] in exclude)) //3 == length("SS") + 1
to_chat(usr, "World variables are not allowed to be accessed. Use global.")
return null
- else if(expression [start] == "{" && long)
+ else if(expression [start] == "{" && long) //3 == length("0x") + 1
if(lowertext(copytext(expression[start + 1], 1, 3)) != "0x")
to_chat(usr, "Invalid pointer syntax: [expression[start + 1]]")
return null
@@ -1070,9 +1070,10 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null
var/word = ""
var/list/query_list = list()
var/len = length(query_text)
+ var/char = ""
- for(var/i = 1, i <= len, i++)
- var/char = copytext(query_text, i, i + 1)
+ for(var/i = 1, i <= len, i += length(char))
+ char = query_text[i]
if(char in whitespace)
if(word != "")
@@ -1091,7 +1092,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null
query_list += word
word = ""
- var/char2 = copytext(query_text, i + 1, i + 2)
+ var/char2 = query_text[i + length(char)]
if(char2 in multi[char])
query_list += "[char][char2]"
@@ -1107,13 +1108,13 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null
word = "'"
- for(i++, i <= len, i++)
- char = copytext(query_text, i, i + 1)
+ for(i += length(char), i <= len, i += length(char))
+ char = query_text[i]
if(char == "'")
- if(copytext(query_text, i + 1, i + 2) == "'")
+ if(query_text[i + length(char)] == "'")
word += "'"
- i++
+ i += length(query_text[i + length(char)])
else
break
@@ -1135,13 +1136,13 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null
word = "\""
- for(i++, i <= len, i++)
- char = copytext(query_text, i, i + 1)
+ for(i += length(char), i <= len, i += length(char))
+ char = query_text[i]
if(char == "\"")
- if(copytext(query_text, i + 1, i + 2) == "'")
+ if(query_text[i + length(char)] == "'")
word += "\""
- i++
+ i += length(query_text[i + length(char)])
else
break
diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2_parser.dm b/code/modules/admin/verbs/SDQL2/SDQL_2_parser.dm
index 272bf83ca4..64d0c00010 100644
--- a/code/modules/admin/verbs/SDQL2/SDQL_2_parser.dm
+++ b/code/modules/admin/verbs/SDQL2/SDQL_2_parser.dm
@@ -256,7 +256,7 @@
node += "*"
i++
- else if (copytext(token(i), 1, 2) == "/")
+ else if(token(i)[1] == "/")
i = object_type(i, node)
else
@@ -377,7 +377,7 @@
//object_type:
/datum/SDQL_parser/proc/object_type(i, list/node)
- if (copytext(token(i), 1, 2) != "/")
+ if(token(i)[1] != "/")
return parse_error("Expected type, but it didn't begin with /")
var/path = text2path(token(i))
@@ -416,7 +416,7 @@
//string: ''' ''' | '"' '"'
/datum/SDQL_parser/proc/string(i, list/node)
- if(copytext(token(i), 1, 2) in list("'", "\""))
+ if(token(i)[1] in list("'", "\""))
node += token(i)
else
@@ -427,7 +427,7 @@
//array: '[' expression, expression, ... ']'
/datum/SDQL_parser/proc/array(var/i, var/list/node)
// Arrays get turned into this: list("[", list(exp_1a = exp_1b, ...), ...), "[" is to mark the next node as an array.
- if(copytext(token(i), 1, 2) != "\[")
+ if(token(i)[1] != "\[")
parse_error("Expected an array but found '[token(i)]'")
return i + 1
@@ -613,7 +613,7 @@
node += "null"
i++
- else if(lowertext(copytext(token(i), 1, 3)) == "0x" && isnum(hex2num(copytext(token(i), 3))))
+ else if(lowertext(copytext(token(i), 1, 3)) == "0x" && isnum(hex2num(copytext(token(i), 3))))//3 == length("0x") + 1
node += hex2num(copytext(token(i), 3))
i++
@@ -621,12 +621,12 @@
node += text2num(token(i))
i++
- else if(copytext(token(i), 1, 2) in list("'", "\""))
+ else if(token(i)[1] in list("'", "\""))
i = string(i, node)
- else if(copytext(token(i), 1, 2) == "\[") // Start a list.
+ else if(token(i)[1] == "\[") // Start a list.
i = array(i, node)
- else if(copytext(token(i), 1, 2) == "/")
+ else if(token(i)[1] == "/")
i = object_type(i, node)
else
i = variable(i, node)
diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm
index 2f842263c7..1800824564 100644
--- a/code/modules/admin/verbs/adminhelp.dm
+++ b/code/modules/admin/verbs/adminhelp.dm
@@ -165,7 +165,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
//is_bwoink is TRUE if this ticket was started by an admin PM
/datum/admin_help/New(msg, client/C, is_bwoink)
//clean the input msg
- msg = sanitize(copytext(msg,1,MAX_MESSAGE_LEN))
+ msg = sanitize(copytext_char(msg,1,MAX_MESSAGE_LEN))
if(!msg || !C || !C.mob)
qdel(src)
return
diff --git a/code/modules/admin/verbs/adminpm.dm b/code/modules/admin/verbs/adminpm.dm
index e2b994f769..bbd6b55e4f 100644
--- a/code/modules/admin/verbs/adminpm.dm
+++ b/code/modules/admin/verbs/adminpm.dm
@@ -41,7 +41,7 @@
return
var/client/C
if(istext(whom))
- if(cmptext(copytext(whom,1,2),"@"))
+ if(whom[1] == "@")
whom = findStealthKey(whom)
C = GLOB.directory[whom]
else if(istype(whom, /client))
@@ -76,7 +76,7 @@
var/client/recipient
var/irc = 0
if(istext(whom))
- if(cmptext(copytext(whom,1,2),"@"))
+ if(whom[1] == "@")
whom = findStealthKey(whom)
if(whom == "IRCKEY")
irc = 1
@@ -133,7 +133,7 @@
//clean the message if it's not sent by a high-rank admin
if(!check_rights(R_SERVER|R_DEBUG,0)||irc)//no sending html to the poor bots
- msg = trim(sanitize(copytext(msg,1,MAX_MESSAGE_LEN)))
+ msg = trim(sanitize(msg), MAX_MESSAGE_LEN)
if(!msg)
return
@@ -287,7 +287,7 @@
if(!stealthkey)
stealthkey = GenIrcStealthKey()
- msg = sanitize(copytext(msg,1,MAX_MESSAGE_LEN))
+ msg = sanitize(copytext_char(msg, 1, MAX_MESSAGE_LEN))
if(!msg)
return "Error: No message"
diff --git a/code/modules/admin/verbs/adminsay.dm b/code/modules/admin/verbs/adminsay.dm
index 1ebd632c23..9081357ef6 100644
--- a/code/modules/admin/verbs/adminsay.dm
+++ b/code/modules/admin/verbs/adminsay.dm
@@ -5,7 +5,7 @@
if(!check_rights(0))
return
- msg = copytext(sanitize(msg), 1, MAX_MESSAGE_LEN)
+ msg = copytext_char(sanitize(msg), 1, MAX_MESSAGE_LEN)
if(!msg)
return
msg = emoji_parse(msg)
diff --git a/code/modules/admin/verbs/deadsay.dm b/code/modules/admin/verbs/deadsay.dm
index 52c5284cbb..4df6f22686 100644
--- a/code/modules/admin/verbs/deadsay.dm
+++ b/code/modules/admin/verbs/deadsay.dm
@@ -14,7 +14,7 @@
if (src.handle_spam_prevention(msg,MUTE_DEADCHAT))
return
- msg = copytext(sanitize(msg), 1, MAX_MESSAGE_LEN)
+ msg = copytext_char(sanitize(msg), 1, MAX_MESSAGE_LEN)
mob.log_talk(msg, LOG_DSAY)
if (!msg)
diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm
index f08112939a..54d6a864fd 100644
--- a/code/modules/admin/verbs/debug.dm
+++ b/code/modules/admin/verbs/debug.dm
@@ -487,7 +487,7 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
if(alert("This mob is being controlled by [M.key]. Are you sure you wish to assume control of it? [M.key] will be made a ghost.",,"Yes","No") != "Yes")
return
else
- var/mob/dead/observer/ghost = new/mob/dead/observer(M,1)
+ var/mob/dead/observer/ghost = new/mob/dead/observer(get_turf(M), M)
ghost.ckey = M.ckey
message_admins("[key_name_admin(usr)] assumed direct control of [M].")
log_admin("[key_name(usr)] assumed direct control of [M].")
@@ -541,7 +541,9 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
var/list/areas_all = list()
var/list/areas_with_APC = list()
var/list/areas_with_multiple_APCs = list()
+ var/list/sub_areas_APC = list()
var/list/areas_with_air_alarm = list()
+ var/list/sub_areas_air_alarm = list()
var/list/areas_with_RC = list()
var/list/areas_with_light = list()
var/list/areas_with_LS = list()
@@ -578,6 +580,7 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
if(!A)
dat += "Skipped over [APC] in invalid location, [APC.loc]."
continue
+ LAZYSET(sub_areas_APC, A.type, get_sub_areas(A, FALSE))
if(!(A.type in areas_with_APC))
areas_with_APC.Add(A.type)
else if(A.type in areas_all)
@@ -585,10 +588,11 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
CHECK_TICK
for(var/obj/machinery/airalarm/AA in GLOB.machines)
- var/area/A = get_area(AA)
+ var/area/A = get_base_area(AA)
if(!A) //Make sure the target isn't inside an object, which results in runtimes.
dat += "Skipped over [AA] in invalid location, [AA.loc]. "
continue
+ LAZYSET(sub_areas_air_alarm, A.type, get_sub_areas(A, FALSE))
if(!(A.type in areas_with_air_alarm))
areas_with_air_alarm.Add(A.type)
CHECK_TICK
@@ -638,8 +642,8 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
areas_with_camera.Add(A.type)
CHECK_TICK
- var/list/areas_without_APC = areas_all - areas_with_APC
- var/list/areas_without_air_alarm = areas_all - areas_with_air_alarm
+ var/list/areas_without_APC = areas_all - (areas_with_APC + flatten_list(sub_areas_APC))
+ var/list/areas_without_air_alarm = areas_all - (areas_with_air_alarm + flatten_list(sub_areas_air_alarm))
var/list/areas_without_RC = areas_all - areas_with_RC
var/list/areas_without_light = areas_all - areas_with_light
var/list/areas_without_LS = areas_all - areas_with_LS
@@ -656,12 +660,18 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
dat += "
AREAS WITH MULTIPLE APCS:
"
for(var/areatype in areas_with_multiple_APCs)
dat += "[areatype] "
+ if(sub_areas_APC[areatype])
+ dat += " SUB-AREAS: "
+ dat += jointext(sub_areas_APC[areatype], " ")
CHECK_TICK
if(areas_without_air_alarm.len)
dat += "
AREAS WITHOUT AN AIR ALARM:
"
for(var/areatype in areas_without_air_alarm)
dat += "[areatype] "
+ if(sub_areas_air_alarm[areatype])
+ dat += " SUB-AREAS: "
+ dat += jointext(sub_areas_air_alarm[areatype], " ")
CHECK_TICK
if(areas_without_RC.len)
@@ -1048,7 +1058,7 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
set name = "Start Line Profiling"
set desc = "Starts tracking line by line profiling for code lines that support it"
- PROFILE_START
+ LINE_PROFILE_START
message_admins("[key_name_admin(src)] started line by line profiling.")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Start Line Profiling")
@@ -1059,7 +1069,7 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
set name = "Stops Line Profiling"
set desc = "Stops tracking line by line profiling for code lines that support it"
- PROFILE_STOP
+ LINE_PROFILE_STOP
message_admins("[key_name_admin(src)] stopped line by line profiling.")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Stop Line Profiling")
diff --git a/code/modules/admin/verbs/manipulate_organs.dm b/code/modules/admin/verbs/manipulate_organs.dm
index a3b7e4247c..b2648735ec 100644
--- a/code/modules/admin/verbs/manipulate_organs.dm
+++ b/code/modules/admin/verbs/manipulate_organs.dm
@@ -58,7 +58,7 @@
if(isorgan(organ))
O = organ
- O.Remove(C)
+ O.Remove()
else
I = organ
I.removed(C)
diff --git a/code/modules/admin/verbs/map_template_loadverb.dm b/code/modules/admin/verbs/map_template_loadverb.dm
index c5c9a0daf4..d71cdca991 100644
--- a/code/modules/admin/verbs/map_template_loadverb.dm
+++ b/code/modules/admin/verbs/map_template_loadverb.dm
@@ -33,7 +33,7 @@
var/map = input(src, "Choose a Map Template to upload to template storage","Upload Map Template") as null|file
if(!map)
return
- if(copytext("[map]",-4) != ".dmm")
+ if(copytext("[map]", -4) != ".dmm")//4 == length(".dmm")
to_chat(src, "Filename must end in '.dmm': [map]")
return
var/datum/map_template/M
diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm
index 24e80fd8f3..35ab3927d4 100644
--- a/code/modules/admin/verbs/mapping.dm
+++ b/code/modules/admin/verbs/mapping.dm
@@ -135,7 +135,7 @@ GLOBAL_LIST_EMPTY(dirty_vars)
if(!(locate(/obj/structure/grille) in T))
var/window_check = 0
for(var/obj/structure/window/W in T)
- if (W.dir == turn(C1.dir,180) || W.dir in list(5,6,9,10) )
+ if(W.dir == turn(C1.dir,180) || (W.dir in list(5,6,9,10)))
window_check = 1
break
if(!window_check)
diff --git a/code/modules/admin/verbs/modifyvariables.dm b/code/modules/admin/verbs/modifyvariables.dm
index 611ebcf7bf..537c31d3b4 100644
--- a/code/modules/admin/verbs/modifyvariables.dm
+++ b/code/modules/admin/verbs/modifyvariables.dm
@@ -307,9 +307,9 @@ GLOBAL_PROTECT(VVpixelmovement)
// the type with the base type removed from the begaining
var/fancytype = types[D.type]
if (findtext(fancytype, types[type]))
- fancytype = copytext(fancytype, length(types[type])+1)
- var/shorttype = copytext("[D.type]", length("[type]")+1)
- if (length(shorttype) > length(fancytype))
+ fancytype = copytext(fancytype, length(types[type]) + 1)
+ var/shorttype = copytext("[D.type]", length("[type]") + 1)
+ if (length_char(shorttype) > length_char(fancytype))
shorttype = fancytype
if (!length(shorttype))
shorttype = "/"
diff --git a/code/modules/admin/verbs/panicbunker.dm b/code/modules/admin/verbs/panicbunker.dm
index 2897427d6f..daaf15c70f 100644
--- a/code/modules/admin/verbs/panicbunker.dm
+++ b/code/modules/admin/verbs/panicbunker.dm
@@ -24,6 +24,8 @@
return
GLOB.bunker_passthrough |= ckey(ckeytobypass)
+ GLOB.bunker_passthrough[ckey(ckeytobypass)] = world.realtime
+ SSpersistence.SavePanicBunker() //we can do this every time, it's okay
log_admin("[key_name(usr)] has added [ckeytobypass] to the current round's bunker bypass list.")
message_admins("[key_name_admin(usr)] has added [ckeytobypass] to the current round's bunker bypass list.")
send2irc("Panic Bunker", "[key_name(usr)] has added [ckeytobypass] to the current round's bunker bypass list.")
@@ -37,6 +39,7 @@
return
GLOB.bunker_passthrough -= ckey(ckeytobypass)
+ SSpersistence.SavePanicBunker()
log_admin("[key_name(usr)] has removed [ckeytobypass] from the current round's bunker bypass list.")
message_admins("[key_name_admin(usr)] has removed [ckeytobypass] from the current round's bunker bypass list.")
send2irc("Panic Bunker", "[key_name(usr)] has removed [ckeytobypass] from the current round's bunker bypass list.")
diff --git a/code/modules/admin/verbs/pray.dm b/code/modules/admin/verbs/pray.dm
index 0555f6b40a..f3da1954a9 100644
--- a/code/modules/admin/verbs/pray.dm
+++ b/code/modules/admin/verbs/pray.dm
@@ -6,7 +6,7 @@
to_chat(usr, "Speech is currently admin-disabled.")
return
- msg = copytext(sanitize(msg), 1, MAX_MESSAGE_LEN)
+ msg = copytext_char(sanitize(msg), 1, MAX_MESSAGE_LEN)
if(!msg)
return
log_prayer("[src.key]/([src.name]): [msg]")
@@ -54,21 +54,21 @@
//log_admin("HELP: [key_name(src)]: [msg]")
/proc/CentCom_announce(text , mob/Sender)
- var/msg = copytext(sanitize(text), 1, MAX_MESSAGE_LEN)
+ var/msg = copytext_char(sanitize(text), 1, MAX_MESSAGE_LEN)
msg = "CENTCOM:[ADMIN_FULLMONTY(Sender)] [ADMIN_CENTCOM_REPLY(Sender)]: [msg]"
to_chat(GLOB.admins, msg)
for(var/obj/machinery/computer/communications/C in GLOB.machines)
C.overrideCooldown()
/proc/Syndicate_announce(text , mob/Sender)
- var/msg = copytext(sanitize(text), 1, MAX_MESSAGE_LEN)
+ var/msg = copytext_char(sanitize(text), 1, MAX_MESSAGE_LEN)
msg = "SYNDICATE:[ADMIN_FULLMONTY(Sender)] [ADMIN_SYNDICATE_REPLY(Sender)]: [msg]"
to_chat(GLOB.admins, msg)
for(var/obj/machinery/computer/communications/C in GLOB.machines)
C.overrideCooldown()
/proc/Nuke_request(text , mob/Sender)
- var/msg = copytext(sanitize(text), 1, MAX_MESSAGE_LEN)
+ var/msg = copytext_char(sanitize(text), 1, MAX_MESSAGE_LEN)
msg = "NUKE CODE REQUEST:[ADMIN_FULLMONTY(Sender)] [ADMIN_CENTCOM_REPLY(Sender)] [ADMIN_SET_SD_CODE]: [msg]"
to_chat(GLOB.admins, msg)
for(var/obj/machinery/computer/communications/C in GLOB.machines)
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index 1437555af1..b8b92b0a58 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -75,7 +75,7 @@
message_admins("[key_name_admin(src)] decided not to answer [key_name_admin(H)]'s [sender] request.")
return
- log_directed_talk(src, H, input, LOG_ADMIN, "reply")
+ log_directed_talk(mob, H, input, LOG_ADMIN, "reply")
message_admins("[key_name_admin(src)] replied to [key_name_admin(H)]'s [sender] message with: \"[input]\"")
to_chat(H, "You hear something crackle in your ears for a moment before a voice speaks. \"Please stand by for a message from [sender == "Syndicate" ? "your benefactor" : "Central Command"]. Message as follows[sender == "Syndicate" ? ", agent." : ":"] [input]. Message ends.\"")
@@ -419,7 +419,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
if(record_found)//If they have a record we can determine a few things.
new_character.real_name = record_found.fields["name"]
- new_character.gender = record_found.fields["sex"]
+ new_character.gender = record_found.fields["gender"]
new_character.age = record_found.fields["age"]
new_character.hardset_dna(record_found.fields["identity"], record_found.fields["enzymes"], record_found.fields["name"], record_found.fields["blood_type"], new record_found.fields["species"], record_found.fields["features"])
else
@@ -1249,7 +1249,7 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
if(!check_rights(R_ADMIN) || !check_rights(R_FUN))
return
- var/list/punishment_list = list(ADMIN_PUNISHMENT_PIE, ADMIN_PUNISHMENT_FIREBALL, ADMIN_PUNISHMENT_LIGHTNING, ADMIN_PUNISHMENT_BRAINDAMAGE, ADMIN_PUNISHMENT_BSA, ADMIN_PUNISHMENT_GIB, ADMIN_PUNISHMENT_SUPPLYPOD_QUICK, ADMIN_PUNISHMENT_SUPPLYPOD, ADMIN_PUNISHMENT_MAZING, ADMIN_PUNISHMENT_ROD)
+ var/list/punishment_list = list(ADMIN_PUNISHMENT_PIE, ADMIN_PUNISHMENT_CUSTOM_PIE, ADMIN_PUNISHMENT_FIREBALL, ADMIN_PUNISHMENT_LIGHTNING, ADMIN_PUNISHMENT_BRAINDAMAGE, ADMIN_PUNISHMENT_BSA, ADMIN_PUNISHMENT_GIB, ADMIN_PUNISHMENT_SUPPLYPOD_QUICK, ADMIN_PUNISHMENT_SUPPLYPOD, ADMIN_PUNISHMENT_MAZING, ADMIN_PUNISHMENT_ROD)
var/punishment = input("Choose a punishment", "DIVINE SMITING") as null|anything in punishment_list
@@ -1314,6 +1314,19 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
if(ADMIN_PUNISHMENT_PIE)
var/obj/item/reagent_containers/food/snacks/pie/cream/nostun/creamy = new(get_turf(target))
creamy.splat(target)
+ if (ADMIN_PUNISHMENT_CUSTOM_PIE)
+ var/obj/item/reagent_containers/food/snacks/pie/cream/nostun/A = new(get_turf(target))
+ if(!A.reagents)
+ var/amount = input(usr, "Specify the reagent size of [A]", "Set Reagent Size", 50) as num
+ if(amount)
+ A.create_reagents(amount)
+ if(A.reagents)
+ var/chosen_id = choose_reagent_id(usr)
+ if(chosen_id)
+ var/amount = input(usr, "Choose the amount to add.", "Choose the amount.", A.reagents.maximum_volume) as num
+ if(amount)
+ A.reagents.add_reagent(chosen_id, amount)
+ A.splat(target)
punish_log(target, punishment)
diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm
index 1894d8c7ae..e73c5de1aa 100644
--- a/code/modules/antagonists/_common/antag_datum.dm
+++ b/code/modules/antagonists/_common/antag_datum.dm
@@ -14,12 +14,14 @@ GLOBAL_LIST_EMPTY(antagonists)
var/list/objectives = list()
var/antag_memory = ""//These will be removed with antag datum
var/antag_moodlet //typepath of moodlet that the mob will gain with their status
- var/can_hijack = HIJACK_NEUTRAL //If these antags are alone on shuttle hijack happens.
+ /// If above 0, this is the multiplier for the speed at which we hijack the shuttle. Do not directly read, use hijack_speed().
+ var/hijack_speed = 0
//Antag panel properties
var/show_in_antagpanel = TRUE //This will hide adding this antag type in antag panel, use only for internal subtypes that shouldn't be added directly but still show if possessed by mind
var/antagpanel_category = "Uncategorized" //Antagpanel will display these together, REQUIRED
var/show_name_in_check_antagonists = FALSE //Will append antagonist name in admin listings - use for categories that share more than one antag type
+ var/list/blacklisted_quirks = list(/datum/quirk/nonviolent,/datum/quirk/mute) // Quirks that will be removed upon gaining this antag. Pacifist and mute are default.
/datum/antagonist/New()
GLOB.antagonists += src
@@ -70,6 +72,7 @@ GLOBAL_LIST_EMPTY(antagonists)
greet()
apply_innate_effects()
give_antag_moodies()
+ remove_blacklisted_quirks()
if(is_banned(owner.current) && replace_banned)
replace_banned_player()
@@ -117,6 +120,18 @@ GLOBAL_LIST_EMPTY(antagonists)
return
SEND_SIGNAL(owner.current, COMSIG_CLEAR_MOOD_EVENT, "antag_moodlet")
+/datum/antagonist/proc/remove_blacklisted_quirks()
+ var/mob/living/L = owner.current
+ if(istype(L))
+ var/list/my_quirks = L.client?.prefs.all_quirks.Copy()
+ SSquirks.filter_quirks(my_quirks,blacklisted_quirks)
+ for(var/q in L.roundstart_quirks)
+ var/datum/quirk/Q = q
+ if(!(SSquirks.quirk_name_by_path(Q.type) in my_quirks))
+ if(initial(Q.antag_removal_text))
+ to_chat(L, "[initial(Q.antag_removal_text)]")
+ L.remove_quirk(Q.type)
+
//Returns the team antagonist belongs to if any.
/datum/antagonist/proc/get_team()
return
@@ -134,7 +149,7 @@ GLOBAL_LIST_EMPTY(antagonists)
if(objectives.len)
report += printobjectives(objectives)
for(var/datum/objective/objective in objectives)
- if(!objective.check_completion())
+ if(objective.completable && !objective.check_completion())
objectives_complete = FALSE
break
@@ -210,11 +225,18 @@ GLOBAL_LIST_EMPTY(antagonists)
return
/datum/antagonist/proc/edit_memory(mob/user)
- var/new_memo = copytext(trim(input(user,"Write new memory", "Memory", antag_memory) as null|message),1,MAX_MESSAGE_LEN)
+ var/new_memo = stripped_multiline_input(user, "Write new memory", "Memory", antag_memory, MAX_MESSAGE_LEN)
if (isnull(new_memo))
return
antag_memory = new_memo
+/// Gets how fast we can hijack the shuttle, return 0 for can not hijack. Defaults to hijack_speed var, override for custom stuff like buffing hijack speed for hijack objectives or something.
+/datum/antagonist/proc/hijack_speed()
+ var/datum/objective/hijack/H = locate() in objectives
+ if(!isnull(H?.hijack_speed_override))
+ return H.hijack_speed_override
+ return hijack_speed
+
//This one is created by admin tools for custom objectives
/datum/antagonist/custom
antagpanel_category = "Custom"
diff --git a/code/modules/antagonists/_common/antag_team.dm b/code/modules/antagonists/_common/antag_team.dm
index 486b5b0414..027abc7c94 100644
--- a/code/modules/antagonists/_common/antag_team.dm
+++ b/code/modules/antagonists/_common/antag_team.dm
@@ -36,11 +36,17 @@
var/win = TRUE
var/objective_count = 1
for(var/datum/objective/objective in objectives)
- if(objective.check_completion())
- report += "Objective #[objective_count]: [objective.explanation_text] Success!"
+ if(objective.completable)
+ var/completion = objective.check_completion()
+ if(completion >= 1)
+ report += "Objective #[objective_count]: [objective.explanation_text] Success!"
+ else if(completion <= 0)
+ report += "Objective #[objective_count]: [objective.explanation_text] Fail."
+ win = FALSE
+ else
+ report += "Objective #[objective_count]: [objective.explanation_text] [completion*100]%"
else
- report += "Objective #[objective_count]: [objective.explanation_text] Fail."
- win = FALSE
+ report += "Objective #[objective_count]: [objective.explanation_text]"
objective_count++
if(win)
report += "The [name] was successful!"
diff --git a/code/modules/antagonists/abductor/abductee/abductee.dm b/code/modules/antagonists/abductor/abductee/abductee.dm
new file mode 100644
index 0000000000..901d2f5b11
--- /dev/null
+++ b/code/modules/antagonists/abductor/abductee/abductee.dm
@@ -0,0 +1,34 @@
+/datum/antagonist/abductee
+ name = "Abductee"
+ roundend_category = "abductees"
+ antagpanel_category = "Abductee"
+ var/datum/brain_trauma/abductee/brain_trauma
+
+/datum/antagonist/abductee/on_gain()
+ give_objective()
+ . = ..()
+
+/datum/antagonist/abductee/greet()
+ to_chat(owner, "Your mind snaps!")
+ to_chat(owner, "You can't remember how you got here...")
+ owner.announce_objectives()
+
+/datum/antagonist/abductee/proc/give_objective()
+ var/mob/living/carbon/human/H = owner.current
+ if(istype(H))
+ H.gain_trauma_type(BRAIN_TRAUMA_MILD, TRAUMA_RESILIENCE_LOBOTOMY)
+ var/objtype = (prob(75) ? /datum/objective/abductee/random : pick(subtypesof(/datum/objective/abductee/) - /datum/objective/abductee/random))
+ var/datum/objective/abductee/O = new objtype()
+ objectives += O
+
+/datum/antagonist/abductee/apply_innate_effects(mob/living/mob_override)
+ update_abductor_icons_added(mob_override ? mob_override.mind : owner,"abductee")
+ var/mob/living/carbon/C = mob_override || owner?.current
+ if(istype(C))
+ if(brain_trauma)
+ qdel(brain_trauma) //make sure there's no lingering trauma
+ brain_trauma = C.gain_trauma(/datum/brain_trauma/abductee, TRAUMA_RESILIENCE_SURGERY)
+
+/datum/antagonist/abductee/remove_innate_effects(mob/living/mob_override)
+ update_abductor_icons_removed(mob_override ? mob_override.mind : owner)
+ qdel(brain_trauma)
diff --git a/code/modules/antagonists/abductor/abductee/abductee_objectives.dm b/code/modules/antagonists/abductor/abductee/abductee_objectives.dm
index f188319644..2d62bf1ac9 100644
--- a/code/modules/antagonists/abductor/abductee/abductee_objectives.dm
+++ b/code/modules/antagonists/abductor/abductee/abductee_objectives.dm
@@ -18,7 +18,7 @@
/datum/objective/abductee/paint/New()
var/color = pick(list("red", "blue", "green", "yellow", "orange", "purple", "black", "in rainbows", "in blood"))
- explanation_text+= " [color]!"
+ explanation_text = " [color]!"
/datum/objective/abductee/speech
explanation_text = "Your brain is broken... you can only communicate in"
diff --git a/code/modules/antagonists/abductor/abductee/trauma.dm b/code/modules/antagonists/abductor/abductee/trauma.dm
new file mode 100644
index 0000000000..1518825e1b
--- /dev/null
+++ b/code/modules/antagonists/abductor/abductee/trauma.dm
@@ -0,0 +1,18 @@
+/datum/brain_trauma/abductee
+ name = "abductee mindsnapped"
+ desc = "The patient's brain has been scrambled by experimental procedures."
+ scan_desc = "brain scrambling"
+ gain_text = "Your mind snaps.. you feel fragmented."
+ lose_text = "Your mind heals itself and you feel whole again."
+ random_gain = FALSE
+ clonable = TRUE
+
+/datum/brain_trauma/abductee/on_gain()
+ . = ..()
+ if(owner.mind)
+ if(!owner.mind.has_antag_datum(/datum/antagonist/abductee))
+ owner.mind.add_antag_datum(/datum/antagonist/abductee)
+
+/datum/brain_trauma/abductee/on_lose()
+ . = ..()
+ owner.mind?.remove_antag_datum(/datum/antagonist/abductee)
diff --git a/code/modules/antagonists/abductor/abductor.dm b/code/modules/antagonists/abductor/abductor.dm
index 92504641a9..564e9a8617 100644
--- a/code/modules/antagonists/abductor/abductor.dm
+++ b/code/modules/antagonists/abductor/abductor.dm
@@ -159,35 +159,6 @@
return "
[result.Join(" ")]
"
-/datum/antagonist/abductee
- name = "Abductee"
- roundend_category = "abductees"
- antagpanel_category = "Abductee"
-
-/datum/antagonist/abductee/on_gain()
- give_objective()
- . = ..()
-
-/datum/antagonist/abductee/greet()
- to_chat(owner, "Your mind snaps!")
- to_chat(owner, "You can't remember how you got here...")
- owner.announce_objectives()
-
-/datum/antagonist/abductee/proc/give_objective()
- var/mob/living/carbon/human/H = owner.current
- if(istype(H))
- H.gain_trauma_type(BRAIN_TRAUMA_MILD, TRAUMA_RESILIENCE_LOBOTOMY)
- var/objtype = (prob(75) ? /datum/objective/abductee/random : pick(subtypesof(/datum/objective/abductee/) - /datum/objective/abductee/random))
- var/datum/objective/abductee/O = new objtype()
- objectives += O
-
-/datum/antagonist/abductee/apply_innate_effects(mob/living/mob_override)
- update_abductor_icons_added(mob_override ? mob_override.mind : owner,"abductee")
-
-/datum/antagonist/abductee/remove_innate_effects(mob/living/mob_override)
- update_abductor_icons_removed(mob_override ? mob_override.mind : owner)
-
-
// LANDMARKS
/obj/effect/landmark/abductor
var/team_number = 1
diff --git a/code/modules/antagonists/abductor/equipment/abduction_gear.dm b/code/modules/antagonists/abductor/equipment/abduction_gear.dm
index e6af9fa487..332329a221 100644
--- a/code/modules/antagonists/abductor/equipment/abduction_gear.dm
+++ b/code/modules/antagonists/abductor/equipment/abduction_gear.dm
@@ -29,6 +29,11 @@
var/stealth_armor = list("melee" = 15, "bullet" = 15, "laser" = 15, "energy" = 15, "bomb" = 15, "bio" = 15, "rad" = 15, "fire" = 70, "acid" = 70)
var/combat_armor = list("melee" = 50, "bullet" = 50, "laser" = 50, "energy" = 50, "bomb" = 50, "bio" = 50, "rad" = 50, "fire" = 90, "acid" = 90)
+/obj/item/clothing/suit/armor/abductor/vest/Initialize()
+ . = ..()
+ stealth_armor = getArmor(arglist(stealth_armor))
+ combat_armor = getArmor(arglist(combat_armor))
+
/obj/item/clothing/suit/armor/abductor/vest/proc/toggle_nodrop()
if(HAS_TRAIT_FROM(src, TRAIT_NODROP, ABDUCTOR_VEST_TRAIT))
REMOVE_TRAIT(src, TRAIT_NODROP, ABDUCTOR_VEST_TRAIT)
diff --git a/code/modules/antagonists/abductor/equipment/abduction_surgery.dm b/code/modules/antagonists/abductor/equipment/abduction_surgery.dm
index 98164de099..971051588e 100644
--- a/code/modules/antagonists/abductor/equipment/abduction_surgery.dm
+++ b/code/modules/antagonists/abductor/equipment/abduction_surgery.dm
@@ -33,7 +33,7 @@
if(IC)
user.visible_message("[user] pulls [IC] out of [target]'s [target_zone]!", "You pull [IC] out of [target]'s [target_zone].")
user.put_in_hands(IC)
- IC.Remove(target)
+ IC.Remove()
return 1
else
to_chat(user, "You don't find anything in [target]'s [target_zone]!")
diff --git a/code/modules/antagonists/abductor/equipment/gland.dm b/code/modules/antagonists/abductor/equipment/gland.dm
index 82170444d0..0d30ebe992 100644
--- a/code/modules/antagonists/abductor/equipment/gland.dm
+++ b/code/modules/antagonists/abductor/equipment/gland.dm
@@ -73,13 +73,14 @@
active_mind_control = FALSE
return TRUE
-/obj/item/organ/heart/gland/Remove(mob/living/carbon/M, special = 0)
+/obj/item/organ/heart/gland/Remove(special = FALSE)
active = 0
if(initial(uses) == 1)
uses = initial(uses)
- var/datum/atom_hud/abductor/hud = GLOB.huds[DATA_HUD_ABDUCTOR]
- hud.remove_from_hud(owner)
- clear_mind_control()
+ if(!QDELETED(owner))
+ var/datum/atom_hud/abductor/hud = GLOB.huds[DATA_HUD_ABDUCTOR]
+ hud.remove_from_hud(owner)
+ clear_mind_control()
..()
/obj/item/organ/heart/gland/Insert(mob/living/carbon/M, special = 0, drop_if_replaced = TRUE)
diff --git a/code/modules/antagonists/abductor/equipment/glands/access.dm b/code/modules/antagonists/abductor/equipment/glands/access.dm
index 548650d4e3..c795233dc9 100644
--- a/code/modules/antagonists/abductor/equipment/glands/access.dm
+++ b/code/modules/antagonists/abductor/equipment/glands/access.dm
@@ -14,6 +14,7 @@
/obj/item/organ/heart/gland/access/proc/free_access(datum/source, obj/O)
return TRUE
-/obj/item/organ/heart/gland/access/Remove(mob/living/carbon/M, special = 0)
- UnregisterSignal(owner, COMSIG_MOB_ALLOWED)
- ..()
\ No newline at end of file
+/obj/item/organ/heart/gland/access/Remove(special = FALSE)
+ if(!QDELETED(owner))
+ UnregisterSignal(owner, COMSIG_MOB_ALLOWED)
+ return ..()
\ No newline at end of file
diff --git a/code/modules/antagonists/abductor/equipment/glands/electric.dm b/code/modules/antagonists/abductor/equipment/glands/electric.dm
index 63d95f8b1f..82ee77d192 100644
--- a/code/modules/antagonists/abductor/equipment/glands/electric.dm
+++ b/code/modules/antagonists/abductor/equipment/glands/electric.dm
@@ -11,9 +11,10 @@
..()
ADD_TRAIT(owner, TRAIT_SHOCKIMMUNE, "abductor_gland")
-/obj/item/organ/heart/gland/electric/Remove(mob/living/carbon/M, special = 0)
- REMOVE_TRAIT(owner, TRAIT_SHOCKIMMUNE, "abductor_gland")
- ..()
+/obj/item/organ/heart/gland/electric/Remove(special = FALSE)
+ if(!QDELETED(owner))
+ REMOVE_TRAIT(owner, TRAIT_SHOCKIMMUNE, "abductor_gland")
+ return ..()
/obj/item/organ/heart/gland/electric/activate()
owner.visible_message("[owner]'s skin starts emitting electric arcs!",\
diff --git a/code/modules/antagonists/abductor/equipment/glands/heal.dm b/code/modules/antagonists/abductor/equipment/glands/heal.dm
index bf9a00e13c..c60ec90480 100644
--- a/code/modules/antagonists/abductor/equipment/glands/heal.dm
+++ b/code/modules/antagonists/abductor/equipment/glands/heal.dm
@@ -65,14 +65,14 @@
/obj/item/organ/heart/gland/heal/proc/reject_implant(obj/item/organ/cyberimp/implant)
owner.visible_message("[owner] vomits up his [implant.name]!", "You suddenly vomit up your [implant.name]!")
owner.vomit(0, TRUE, TRUE, 1, FALSE, FALSE, FALSE, TRUE)
- implant.Remove(owner)
+ implant.Remove()
implant.forceMove(owner.drop_location())
/obj/item/organ/heart/gland/heal/proc/replace_liver(obj/item/organ/liver/liver)
if(liver)
owner.visible_message("[owner] vomits up his [liver.name]!", "You suddenly vomit up your [liver.name]!")
owner.vomit(0, TRUE, TRUE, 1, FALSE, FALSE, FALSE, TRUE)
- liver.Remove(owner)
+ liver.Remove()
liver.forceMove(owner.drop_location())
else
to_chat(owner, "You feel a weird rumble in your bowels...")
@@ -87,7 +87,7 @@
if(lungs)
owner.visible_message("[owner] vomits up his [lungs.name]!", "You suddenly vomit up your [lungs.name]!")
owner.vomit(0, TRUE, TRUE, 1, FALSE, FALSE, FALSE, TRUE)
- lungs.Remove(owner)
+ lungs.Remove()
lungs.forceMove(owner.drop_location())
else
to_chat(owner, "You feel a weird rumble inside your chest...")
@@ -102,7 +102,7 @@
if(eyes)
owner.visible_message("[owner]'s [eyes.name] fall out of their sockets!", "Your [eyes.name] fall out of their sockets!")
playsound(owner, 'sound/effects/splat.ogg', 50, TRUE)
- eyes.Remove(owner)
+ eyes.Remove()
eyes.forceMove(owner.drop_location())
else
to_chat(owner, "You feel a weird rumble behind your eye sockets...")
diff --git a/code/modules/antagonists/blob/blob/overmind.dm b/code/modules/antagonists/blob/blob/overmind.dm
index f094fa83db..5d3c8cd8ca 100644
--- a/code/modules/antagonists/blob/blob/overmind.dm
+++ b/code/modules/antagonists/blob/blob/overmind.dm
@@ -203,7 +203,7 @@ GLOBAL_LIST_EMPTY(blob_nodes)
/mob/camera/blob/proc/blob_talk(message)
- message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN))
+ message = trim(copytext_char(sanitize(message), 1, MAX_MESSAGE_LEN))
if (!message)
return
diff --git a/code/modules/antagonists/bloodsucker/bloodsucker_life.dm b/code/modules/antagonists/bloodsucker/bloodsucker_life.dm
index 10728d0664..0179c60ef1 100644
--- a/code/modules/antagonists/bloodsucker/bloodsucker_life.dm
+++ b/code/modules/antagonists/bloodsucker/bloodsucker_life.dm
@@ -84,7 +84,7 @@
//It is called from your coffin on close (by you only)
if(poweron_masquerade == TRUE || owner.current.AmStaked())
return FALSE
- owner.current.adjustStaminaLoss(-2 + (regenRate * -8) * mult, 0) // Humans lose stamina damage really quickly. Vamps should heal more.
+ owner.current.adjustStaminaLoss(-1.5 + (regenRate * -7) * mult, 0) // Humans lose stamina damage really quickly. Vamps should heal more.
owner.current.adjustCloneLoss(-0.1 * (regenRate * 2) * mult, 0)
owner.current.adjustOrganLoss(ORGAN_SLOT_BRAIN, -1 * (regenRate * 4) * mult) //adjustBrainLoss(-1 * (regenRate * 4) * mult, 0)
// No Bleeding
@@ -125,7 +125,7 @@
C.adjustFireLoss(-fireheal * mult, forced = TRUE)
C.adjustToxLoss(-toxinheal * mult * 2, forced = TRUE) //Toxin healing because vamps arent immune
//C.heal_overall_damage(bruteheal * mult, fireheal * mult) // REMOVED: We need to FORCE this, because otherwise, vamps won't heal EVER. Swapped to above.
- AddBloodVolume((bruteheal * -0.5 + fireheal * -1) / mult * costMult) // Costs blood to heal
+ AddBloodVolume((bruteheal * -0.5 + fireheal * -1 + toxinheal * -0.2) / mult * costMult) // Costs blood to heal
return TRUE // Healed! Done for this tick.
if(amInCoffinWhileTorpor) // Limbs? (And I have no other healing)
var/list/missing = owner.current.get_missing_limbs() // Heal Missing
@@ -189,7 +189,7 @@
/datum/antagonist/bloodsucker/proc/HandleDeath()
// FINAL DEATH
// Fire Damage? (above double health)
- if(owner.current.getFireLoss_nonProsthetic() >= owner.current.getMaxHealth() * 1.5)
+ if(owner.current.getFireLoss_nonProsthetic() >= owner.current.maxHealth * 2.5)
FinalDeath()
return
// Staked while "Temp Death" or Asleep
diff --git a/code/modules/antagonists/bloodsucker/bloodsucker_powers.dm b/code/modules/antagonists/bloodsucker/bloodsucker_powers.dm
index dffdeaf7fc..626ad43b10 100644
--- a/code/modules/antagonists/bloodsucker/bloodsucker_powers.dm
+++ b/code/modules/antagonists/bloodsucker/bloodsucker_powers.dm
@@ -32,11 +32,11 @@
//var/not_bloodsucker = FALSE // This goes to Vassals or Hunters, but NOT bloodsuckers.
/datum/action/bloodsucker/New()
- if (bloodcost > 0)
+ if(bloodcost > 0)
desc += "
COST: [bloodcost] Blood" // Modify description to add cost.
- if (warn_constant_cost)
+ if(warn_constant_cost)
desc += "
Your over-time blood consumption increases while [name] is active."
- if (amSingleUse)
+ if(amSingleUse)
desc += "
Useable once per night."
..()
@@ -47,35 +47,35 @@
/datum/action/bloodsucker/Trigger()
// Active? DEACTIVATE AND END!
- if (active && CheckCanDeactivate(TRUE))
+ if(active && CheckCanDeactivate(TRUE))
DeactivatePower()
return
- if (!CheckCanPayCost(TRUE) || !CheckCanUse(TRUE))
+ if(!CheckCanPayCost(TRUE) || !CheckCanUse(TRUE))
return
PayCost()
- if (amToggle)
+ if(amToggle)
active = !active
UpdateButtonIcon()
- if (!amToggle || !active)
+ if(!amToggle || !active)
StartCooldown() // Must come AFTER UpdateButton(), otherwise icon will revert.
ActivatePower() // NOTE: ActivatePower() freezes this power in place until it ends.
- if (active) // Did we not manually disable? Handle it here.
+ if(active) // Did we not manually disable? Handle it here.
DeactivatePower()
- if (amSingleUse)
+ if(amSingleUse)
RemoveAfterUse()
/datum/action/bloodsucker/proc/CheckCanPayCost(display_error)
if(!owner || !owner.mind)
return FALSE
// Cooldown?
- if (cooldownUntil > world.time)
- if (display_error)
+ if(cooldownUntil > world.time)
+ if(display_error)
to_chat(owner, "[src] is unavailable. Wait [(cooldownUntil - world.time) / 10] seconds.")
return FALSE
// Have enough blood?
var/mob/living/L = owner
- if (L.blood_volume < bloodcost)
- if (display_error)
+ if(L.blood_volume < bloodcost)
+ if(display_error)
to_chat(owner, "You need at least [bloodcost] blood to activate [name]")
return FALSE
return TRUE
diff --git a/code/modules/antagonists/bloodsucker/bloodsucker_sunlight.dm b/code/modules/antagonists/bloodsucker/bloodsucker_sunlight.dm
index bb6e2a69f9..263f91a9a6 100644
--- a/code/modules/antagonists/bloodsucker/bloodsucker_sunlight.dm
+++ b/code/modules/antagonists/bloodsucker/bloodsucker_sunlight.dm
@@ -1,4 +1,3 @@
-#define TIME_BLOODSUCKER_NIGHT 900 // 15 minutes
#define TIME_BLOODSUCKER_DAY_WARN 90 // 1.5 minutes
#define TIME_BLOODSUCKER_DAY_FINAL_WARN 25 // 25 sec
#define TIME_BLOODSUCKER_DAY 60 // 1.5 minutes // 10 is a second, 600 is a minute.
@@ -11,6 +10,7 @@
var/cancel_me = FALSE
var/amDay = FALSE
var/time_til_cycle = 0
+ var/nightime_duration = 900 //15 Minutes
/obj/effect/sunlight/Initialize()
countdown()
@@ -21,7 +21,7 @@
while(!cancel_me)
- time_til_cycle = TIME_BLOODSUCKER_NIGHT
+ time_til_cycle = nightime_duration
// Part 1: Night (all is well)
while(time_til_cycle > TIME_BLOODSUCKER_DAY_WARN)
@@ -81,7 +81,9 @@
"The solar flare has ended, and the daylight danger has passed...for now.")
amDay = FALSE
day_end() // Remove VANISHING ACT power from all vamps who have it! Clear Warnings (sunlight, locker protection)
- message_admins("BLOODSUCKER NOTICE: Daylight Ended. Resetting to Night (Lasts for [TIME_BLOODSUCKER_NIGHT / 60] minutes.)")
+ nightime_duration += 100 //Each day makes the night a minute longer.
+ message_admins("BLOODSUCKER NOTICE: Daylight Ended. Resetting to Night (Lasts for [nightime_duration / 60] minutes.)")
+
/obj/effect/sunlight/proc/hud_tick()
@@ -97,7 +99,7 @@
sleep(10)
time_til_cycle --
-/obj/effect/sunlight/proc/warn_daylight(danger_level=0, vampwarn = "", vassalwarn = "")
+/obj/effect/sunlight/proc/warn_daylight(danger_level =0, vampwarn = "", vassalwarn = "")
for(var/datum/mind/M in SSticker.mode.bloodsuckers)
if(!istype(M))
continue
diff --git a/code/modules/antagonists/bloodsucker/datum_bloodsucker.dm b/code/modules/antagonists/bloodsucker/datum_bloodsucker.dm
index 844b523135..5207f7a66d 100644
--- a/code/modules/antagonists/bloodsucker/datum_bloodsucker.dm
+++ b/code/modules/antagonists/bloodsucker/datum_bloodsucker.dm
@@ -22,7 +22,7 @@
// STATS
var/vamplevel = 0
var/vamplevel_unspent = 1
- var/regenRate = 0.3 // How many points of Brute do I heal per tick?
+ var/regenRate = 0.4 // How many points of Brute do I heal per tick?
var/feedAmount = 15 // Amount of blood drawn from a target per tick.
var/maxBloodVolume = 600 // Maximum blood a Vamp can hold via feeding. // BLOOD_VOLUME_NORMAL 550 // BLOOD_VOLUME_SAFE 475 //BLOOD_VOLUME_OKAY 336 //BLOOD_VOLUME_BAD 224 // BLOOD_VOLUME_SURVIVE 122
// OBJECTIVES
@@ -34,6 +34,7 @@
var/warn_sun_locker = FALSE // So we only get the locker burn message once per day.
var/warn_sun_burn = FALSE // So we only get the sun burn message once per day.
var/had_toxlover = FALSE
+ var/level_bloodcost
// LISTS
var/static/list/defaultTraits = list (TRAIT_STABLEHEART, TRAIT_NOBREATH, TRAIT_SLEEPIMMUNE, TRAIT_NOCRITDAMAGE, TRAIT_RESISTCOLD, TRAIT_RADIMMUNE, TRAIT_NIGHT_VISION, \
TRAIT_NOSOFTCRIT, TRAIT_NOHARDCRIT, TRAIT_AGEUSIA, TRAIT_COLDBLOODED, TRAIT_NONATURALHEAL, TRAIT_NOMARROW, TRAIT_NOPULSE, TRAIT_VIRUSIMMUNE)
@@ -98,7 +99,7 @@
/datum/antagonist/bloodsucker/proc/SelectFirstName()
// Names (EVERYONE gets one))
- if (owner.current.gender == MALE)
+ if(owner.current.gender == MALE)
vampname = pick("Desmond","Rudolph","Dracul","Vlad","Pyotr","Gregor","Cristian","Christoff","Marcu","Andrei","Constantin","Gheorghe","Grigore","Ilie","Iacob","Luca","Mihail","Pavel","Vasile","Octavian","Sorin", \
"Sveyn","Aurel","Alexe","Iustin","Theodor","Dimitrie","Octav","Damien","Magnus","Caine","Abel", // Romanian/Ancient
"Lucius","Gaius","Otho","Balbinus","Arcadius","Romanos","Alexios","Vitellius", // Latin
@@ -119,7 +120,7 @@
return
// Titles [Master]
if (!am_fledgling)
- if (owner.current.gender == MALE)
+ if(owner.current.gender == MALE)
vamptitle = pick ("Count","Baron","Viscount","Prince","Duke","Tzar","Dreadlord","Lord","Master")
else
vamptitle = pick ("Countess","Baroness","Viscountess","Princess","Duchess","Tzarina","Dreadlady","Lady","Mistress")
@@ -130,18 +131,18 @@
/datum/antagonist/bloodsucker/proc/SelectReputation(am_fledgling = 0, forced=FALSE)
// Already have Reputation
- if (!forced && vampreputation != null)
+ if(!forced && vampreputation != null)
return
// Reputations [Master]
- if (!am_fledgling)
+ if(!am_fledgling)
vampreputation = pick("Butcher","Blood Fiend","Crimson","Red","Black","Terror","Nightman","Feared","Ravenous","Fiend","Malevolent","Wicked","Ancient","Plaguebringer","Sinister","Forgotten","Wretched","Baleful", \
"Inqisitor","Harvester","Reviled","Robust","Betrayer","Destructor","Damned","Accursed","Terrible","Vicious","Profane","Vile","Depraved","Foul","Slayer","Manslayer","Sovereign","Slaughterer", \
"Forsaken","Mad","Dragon","Savage","Villainous","Nefarious","Inquisitor","Marauder","Horrible","Immortal","Undying","Overlord","Corrupt","Hellspawn","Tyrant","Sanguineous")
- if (owner.current.gender == MALE)
- if (prob(10)) // Gender override
+ if(owner.current.gender == MALE)
+ if(prob(10)) // Gender override
vampreputation = pick("King of the Damned", "Blood King", "Emperor of Blades", "Sinlord", "God-King")
else
- if (prob(10)) // Gender override
+ if(prob(10)) // Gender override
vampreputation = pick("Queen of the Damned", "Blood Queen", "Empress of Blades", "Sinlady", "God-Queen")
to_chat(owner, "You have earned a reputation! You are now known as [ReturnFullName(TRUE)]!")
@@ -202,7 +203,7 @@
// Make Changes
H.physiology.brute_mod *= 0.8 // <-------------------- Start small, but burn mod increases based on rank!
H.physiology.cold_mod = 0
- H.physiology.stun_mod *= 0.35
+ H.physiology.stun_mod *= 0.5
H.physiology.siemens_coeff *= 0.75 //base electrocution coefficient 1
//S.heatmod += 0.5 // Heat shouldn't affect. Only Fire.
//S.punchstunthreshold = 8 //damage at which punches from this race will stun 9
@@ -261,7 +262,7 @@
owner.hasSoul = TRUE
//owner.current.hellbound = FALSE
-datum/antagonist/bloodsucker/proc/RankUp()
+/datum/antagonist/bloodsucker/proc/RankUp()
set waitfor = FALSE
if(!owner || !owner.current)
return
@@ -272,21 +273,23 @@ datum/antagonist/bloodsucker/proc/RankUp()
else
to_chat(owner, "You have grown more ancient! Sleep in a coffin that you have claimed to thicken your blood and become more powerful.")
if(vamplevel_unspent >= 2)
- to_chat(owner, "Bloodsucker Tip: If you cannot find or steal a coffin to use, they can be built from wooden planks. ")
+ to_chat(owner, "Bloodsucker Tip: If you cannot find or steal a coffin to use, you can build one from wooden planks. ")
-datum/antagonist/bloodsucker/proc/LevelUpPowers()
+/datum/antagonist/bloodsucker/proc/LevelUpPowers()
for(var/datum/action/bloodsucker/power in powers)
power.level_current ++
-datum/antagonist/bloodsucker/proc/SpendRank()
+/datum/antagonist/bloodsucker/proc/SpendRank()
set waitfor = FALSE
- if (vamplevel_unspent <= 0 || !owner || !owner.current || !owner.current.client)
+ if(vamplevel_unspent <= 0 || !owner || !owner.current || !owner.current.client || !isliving(owner.current))
return
- /////////
- // Powers
- //TODO: Make this into a radial
+ var/mob/living/L = owner.current
+ level_bloodcost = maxBloodVolume * 0.2
+ //If the blood volume of the bloodsucker is lower than the cost to level up, return and inform the bloodsucker
+
+ //TODO: Make this into a radial, or perhaps a tgui next UI
// Purchase Power Prompt
- var/list/options = list() // Taken from gasmask.dm, for Clown Masks.
+ var/list/options = list()
for(var/pickedpower in typesof(/datum/action/bloodsucker))
var/datum/action/bloodsucker/power = pickedpower
// If I don't own it, and I'm allowed to buy it.
@@ -295,7 +298,7 @@ datum/antagonist/bloodsucker/proc/SpendRank()
options["\[ Not Now \]"] = null
// Abort?
if(options.len > 1)
- var/choice = input(owner.current, "You have the opportunity to grow more ancient. Select a power to advance your Rank.", "Your Blood Thickens...") in options
+ var/choice = input(owner.current, "You have the opportunity to grow more ancient at the cost of [level_bloodcost] units of blood. Select a power to advance your Rank.", "Your Blood Thickens...") in options
// Cheat-Safety: Can't keep opening/closing coffin to spam levels
if(vamplevel_unspent <= 0) // Already spent all your points, and tried opening/closing your coffin, pal.
return
@@ -305,10 +308,14 @@ datum/antagonist/bloodsucker/proc/SpendRank()
if(!choice || !options[choice] || (locate(options[choice]) in powers)) // ADDED: Check to see if you already have this power, due to window stacking.
to_chat(owner.current, "You prevent your blood from thickening just yet, but you may try again later.")
return
+ if(L.blood_volume < level_bloodcost)
+ to_chat(owner.current, "You dont have enough blood to thicken your blood, you need [level_bloodcost - L.blood_volume] units more!")
+ return
// Buy New Powers
var/datum/action/bloodsucker/P = options[choice]
+ AddBloodVolume(-level_bloodcost)
BuyPower(new P)
- to_chat(owner.current, "You have learned [initial(P.name)]!")
+ to_chat(owner.current, "You have used [level_bloodcost] units of blood and learned [initial(P.name)]!")
else
to_chat(owner.current, "You grow more ancient by the night!")
/////////
@@ -326,7 +333,7 @@ datum/antagonist/bloodsucker/proc/SpendRank()
// Vamp Stats
regenRate += 0.05 // Points of brute healed (starts at 0.3)
feedAmount += 2 // Increase how quickly I munch down vics (15)
- maxBloodVolume += 50 // Increase my max blood (600)
+ maxBloodVolume += 100 // Increase my max blood (600)
/////////
vamplevel ++
vamplevel_unspent --
diff --git a/code/modules/antagonists/bloodsucker/datum_hunter.dm b/code/modules/antagonists/bloodsucker/datum_hunter.dm
index 2d680d0e09..5fc6716a7d 100644
--- a/code/modules/antagonists/bloodsucker/datum_hunter.dm
+++ b/code/modules/antagonists/bloodsucker/datum_hunter.dm
@@ -283,8 +283,8 @@
streak = ""
restraining = 0
streak = streak+element
- if(length(streak) > max_streak_length)
- streak = copytext(streak,2)
+ if(length_char(streak) > max_streak_length)
+ streak = streak[1]
return
diff --git a/code/modules/antagonists/bloodsucker/datum_vassal.dm b/code/modules/antagonists/bloodsucker/datum_vassal.dm
index 716b7ff223..345c6f3c10 100644
--- a/code/modules/antagonists/bloodsucker/datum_vassal.dm
+++ b/code/modules/antagonists/bloodsucker/datum_vassal.dm
@@ -8,7 +8,7 @@
return SSticker.mode.make_vassal(C,owner)
/datum/antagonist/bloodsucker/proc/FreeAllVassals()
- for (var/datum/antagonist/vassal/V in vassals)
+ for(var/datum/antagonist/vassal/V in vassals)
SSticker.mode.remove_vassal(V.owner)
/datum/antagonist/vassal
@@ -22,7 +22,7 @@
/datum/antagonist/vassal/can_be_owned(datum/mind/new_owner)
// If we weren't created by a bloodsucker, then we cannot be a vassal (assigned from antag panel)
- if (!master)
+ if(!master)
return FALSE
return ..()
@@ -63,9 +63,9 @@
/datum/antagonist/vassal/on_removal()
SSticker.mode.vassals -= owner // Add if not already in here (and you might be, if you were picked at round start)
// Mindslave Remove
- if (master && master.owner)
+ if(master && master.owner)
master.vassals -= src
- if (owner.enslaved_to == master.owner.current)
+ if(owner.enslaved_to == master.owner.current)
owner.enslaved_to = null
// Master Pinpointer
owner.current.remove_status_effect(/datum/status_effect/agent_pinpointer/vassal_edition)
diff --git a/code/modules/antagonists/bloodsucker/objects/bloodsucker_coffin.dm b/code/modules/antagonists/bloodsucker/objects/bloodsucker_coffin.dm
index 946c472f11..a25244c48d 100644
--- a/code/modules/antagonists/bloodsucker/objects/bloodsucker_coffin.dm
+++ b/code/modules/antagonists/bloodsucker/objects/bloodsucker_coffin.dm
@@ -60,6 +60,8 @@
breakout_time = 600
pryLidTimer = 400
resistance_flags = NONE
+ integrity_failure = 70
+ armor = list("melee" = 50, "bullet" = 20, "laser" = 30, "energy" = 0, "bomb" = 50, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 60)
/obj/structure/closet/crate/coffin/meatcoffin
name = "meat coffin"
@@ -75,7 +77,9 @@
resistance_flags = NONE
material_drop = /obj/item/reagent_containers/food/snacks/meat/slab
material_drop_amount = 3
-
+ integrity_failure = 40
+ armor = list("melee" = 70, "bullet" = 10, "laser" = 10, "energy" = 0, "bomb" = 70, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 100)
+
/obj/structure/closet/crate/coffin/metalcoffin
name = "metal coffin"
desc = "A big metal sardine can inside of another big metal sardine can, in space."
@@ -90,6 +94,8 @@
resistance_flags = NONE
material_drop = /obj/item/stack/sheet/metal
material_drop_amount = 5
+ integrity_failure = 60
+ armor = list("melee" = 40, "bullet" = 15, "laser" = 50, "energy" = 0, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 60)
//////////////////////////////////////////////
diff --git a/code/modules/antagonists/bloodsucker/objects/bloodsucker_crypt.dm b/code/modules/antagonists/bloodsucker/objects/bloodsucker_crypt.dm
index f66ce4a208..9e46203483 100644
--- a/code/modules/antagonists/bloodsucker/objects/bloodsucker_crypt.dm
+++ b/code/modules/antagonists/bloodsucker/objects/bloodsucker_crypt.dm
@@ -107,7 +107,7 @@
var/convert_progress = 3 // Resets on each new character to be added to the chair. Some effects should lower it...
var/disloyalty_confirm = FALSE // Command & Antags need to CONFIRM they are willing to lose their role (and will only do it if the Vassal'ing succeeds)
var/disloyalty_offered = FALSE // Has the popup been issued? Don't spam them.
- var/convert_cost = 100
+
/obj/structure/bloodsucker/vassalrack/deconstruct(disassembled = TRUE)
new /obj/item/stack/sheet/metal(src.loc, 4)
@@ -116,10 +116,10 @@
/obj/structure/bloodsucker/vassalrack/examine(mob/user)
. = ..()
- if((user.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)) || isobserver(user))
+ if(isvamp(user) || isobserver(user))
. += {"This is the vassal rack, which allows you to thrall crewmembers into loyal minions in your service."}
. += {"You need to first secure the vassal rack by clicking on it while it is in your lair."}
- . += {"Simply click and hold on a victim, and then drag their sprite on the vassal rack."}
+ . += {"Simply click and hold on a victim, and then drag their sprite on the vassal rack. Alt click on the vassal rack to unbuckle them."}
. += {"Make sure that the victim is handcuffed, or else they can simply run away or resist, as the process is not instant."}
. += {"To convert the victim, simply click on the vassal rack itself. Sharp weapons work faster than other tools."}
/* if(user.mind.has_antag_datum(ANTAG_DATUM_VASSAL)
@@ -177,7 +177,7 @@
M.pixel_y = -2 //M.get_standard_pixel_y_offset(120)//180)
update_icon()
// Torture Stuff
- convert_progress = 2 // Goes down unless you start over.
+ convert_progress = 4 // Goes down unless you start over.
disloyalty_confirm = FALSE // New guy gets the chance to say NO if he's special.
disloyalty_offered = FALSE // Prevents spamming torture window.
@@ -190,7 +190,7 @@
else
M.visible_message("[user] tries to pull [M] rack!",\
"[user] attempts to release you from the rack!") // For sound if not seen --> "You hear a squishy wet noise.")
- if(!do_mob(user, M, 100))
+ if(!do_mob(user, M, 200))
return
// Did the time. Now try to do it.
..()
@@ -248,7 +248,7 @@
// Bloodsucker Owner! Let the boy go.
if(C.mind)
var/datum/antagonist/vassal/vassaldatum = C.mind.has_antag_datum(ANTAG_DATUM_VASSAL)
- if (istype(vassaldatum) && vassaldatum.master == bloodsuckerdatum || C.stat >= DEAD)
+ if(istype(vassaldatum) && vassaldatum.master == bloodsuckerdatum || C.stat >= DEAD)
unbuckle_mob(C)
useLock = FALSE // Failsafe
return
@@ -256,7 +256,9 @@
torture_victim(user, C)
/obj/structure/bloodsucker/vassalrack/proc/torture_victim(mob/living/user, mob/living/target)
+ var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
// Check Bloodmob/living/M, force = FALSE, check_loc = TRUE
+ var/convert_cost = 200 + 200 * bloodsuckerdatum.vassals
if(user.blood_volume < convert_cost + 5)
to_chat(user, "You don't have enough blood to initiate the Dark Communion with [target].")
return
@@ -275,7 +277,7 @@
// All done!
if(convert_progress <= 0)
// FAIL: Can't be Vassal
- if(!SSticker.mode.can_make_vassal(target, user, display_warning=FALSE) || HAS_TRAIT(target, TRAIT_MINDSHIELD)) // If I'm an unconvertable Antag ONLY
+ if(!SSticker.mode.can_make_vassal(target, user, display_warning = FALSE) || HAS_TRAIT(target, TRAIT_MINDSHIELD)) // If I'm an unconvertable Antag ONLY
to_chat(user, "[target] doesn't respond to your persuasion. It doesn't appear they can be converted to follow you, they either have a mindshield or their external loyalties are too difficult for you to break.\[ALT+click to release\]")
convert_progress ++ // Pop it back up some. Avoids wasting Blood on a lost cause.
// SUCCESS: All done!
@@ -301,10 +303,9 @@
return
// Check: Blood
if(user.blood_volume < convert_cost)
- to_chat(user, "You don't have enough blood to initiate the Dark Communion with [target].")
+ to_chat(user, "You don't have enough blood to initiate the Dark Communion with [target], you need [convert_cost - user.blood_volume] units more!")
useLock = FALSE
return
- var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
bloodsuckerdatum.AddBloodVolume(-convert_cost)
target.add_mob_blood(user)
user.visible_message("[user] marks a bloody smear on [target]'s forehead and puts a wrist up to [target.p_their()] mouth!", \
diff --git a/code/modules/antagonists/bloodsucker/powers/bs_brawn.dm b/code/modules/antagonists/bloodsucker/powers/brawn.dm
similarity index 97%
rename from code/modules/antagonists/bloodsucker/powers/bs_brawn.dm
rename to code/modules/antagonists/bloodsucker/powers/brawn.dm
index 49aada3611..712a15dff9 100644
--- a/code/modules/antagonists/bloodsucker/powers/bs_brawn.dm
+++ b/code/modules/antagonists/bloodsucker/powers/brawn.dm
@@ -5,7 +5,7 @@
desc = "Snap restraints with ease, or deal terrible damage with your bare hands."
button_icon_state = "power_strength"
bloodcost = 10
- cooldown = 130
+ cooldown = 90
target_range = 1
power_activates_immediately = TRUE
message_Trigger = ""//"Whom will you subvert to your will?"
@@ -66,16 +66,14 @@
// Target Type: Mob
if(isliving(target))
var/mob/living/carbon/user_C = user
- var/hitStrength = user_C.dna.species.punchdamagehigh * 1.3 + 5
+ var/hitStrength = user_C.dna.species.punchdamagehigh * 1.4 + 15
// Knockdown!
var/powerlevel = min(5, 1 + level_current)
if(rand(5 + powerlevel) >= 5)
target.visible_message("[user] lands a vicious punch, sending [target] away!", \
"[user] has landed a horrifying punch on you, sending you flying!!", null, COMBAT_MESSAGE_RANGE)
target.Knockdown(min(5, rand(10, 10 * powerlevel)) )
- // Chance of KO
- if(rand(6 + powerlevel) >= 6 && target.stat <= UNCONSCIOUS)
- target.Unconscious(40)
+
// Attack!
playsound(get_turf(target), 'sound/weapons/punch4.ogg', 60, 1, -1)
user.do_attack_animation(target, ATTACK_EFFECT_SMASH)
diff --git a/code/modules/antagonists/bloodsucker/powers/bs_cloak.dm b/code/modules/antagonists/bloodsucker/powers/cloak.dm
similarity index 93%
rename from code/modules/antagonists/bloodsucker/powers/bs_cloak.dm
rename to code/modules/antagonists/bloodsucker/powers/cloak.dm
index 9126638fea..9d83c95f05 100644
--- a/code/modules/antagonists/bloodsucker/powers/bs_cloak.dm
+++ b/code/modules/antagonists/bloodsucker/powers/cloak.dm
@@ -31,12 +31,11 @@
if(was_running)
user.toggle_move_intent()
ADD_TRAIT(user, TRAIT_NORUNNING, "cloak of darkness")
- while(bloodsuckerdatum && ContinueActive(user) || user.m_intent == MOVE_INTENT_RUN)
+ while(bloodsuckerdatum && ContinueActive(user))
// Pay Blood Toll (if awake)
owner.alpha = max(20, owner.alpha - min(75, 10 + 5 * level_current))
bloodsuckerdatum.AddBloodVolume(-0.2)
sleep(5) // Check every few ticks that we haven't disabled this power
- // Return to Running (if you were before)
/datum/action/bloodsucker/cloak/ContinueActive(mob/living/user, mob/living/target)
if (!..())
diff --git a/code/modules/antagonists/bloodsucker/powers/bs_feed.dm b/code/modules/antagonists/bloodsucker/powers/feed.dm
similarity index 81%
rename from code/modules/antagonists/bloodsucker/powers/bs_feed.dm
rename to code/modules/antagonists/bloodsucker/powers/feed.dm
index 7c4cf46e3c..f9ff31d94a 100644
--- a/code/modules/antagonists/bloodsucker/powers/bs_feed.dm
+++ b/code/modules/antagonists/bloodsucker/powers/feed.dm
@@ -22,12 +22,12 @@
return
// Wearing mask
var/mob/living/L = owner
- if (L.is_mouth_covered())
- if (display_error)
+ if(L.is_mouth_covered())
+ if(display_error)
to_chat(owner, "You cannot feed with your mouth covered! Remove your mask.")
return FALSE
// Find my Target!
- if (!FindMyTarget(display_error)) // Sets feed_target within after Validating
+ if(!FindMyTarget(display_error)) // Sets feed_target within after Validating
return FALSE
// Not in correct state
// DONE!
@@ -35,36 +35,36 @@
/datum/action/bloodsucker/feed/proc/ValidateTarget(mob/living/target, display_error) // Called twice: validating a subtle victim, or validating your grapple victim.
// Bloodsuckers + Animals MUST be grabbed aggressively!
- if (!owner.pulling || target == owner.pulling && owner.grab_state < GRAB_AGGRESSIVE)
+ if(!owner.pulling || target == owner.pulling && owner.grab_state < GRAB_AGGRESSIVE)
// NOTE: It's OKAY that we are checking if(!target) below, AFTER animals here. We want passive check vs animal to warn you first, THEN the standard warning.
// Animals:
- if (isliving(target) && !iscarbon(target))
- if (display_error)
+ if(isliving(target) && !iscarbon(target))
+ if(display_error)
to_chat(owner, "Lesser beings require a tighter grip.")
return FALSE
// Bloodsuckers:
- else if (iscarbon(target) && target.mind && target.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER))
- if (display_error)
+ else if(iscarbon(target) && target.mind && target.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER))
+ if(display_error)
to_chat(owner, "Other Bloodsuckers will not fall for your subtle approach.")
return FALSE
// Must have Target
- if (!target) // || !ismob(target)
- if (display_error)
+ if(!target) // || !ismob(target)
+ if(display_error)
to_chat(owner, "You must be next to or grabbing a victim to feed from them.")
return FALSE
// Not even living!
- if (!isliving(target) || issilicon(target))
- if (display_error)
+ if(!isliving(target) || issilicon(target))
+ if(display_error)
to_chat(owner, "You may only feed from living beings.")
return FALSE
- if (target.blood_volume <= 0)
- if (display_error)
+ if(target.blood_volume <= 0)
+ if(display_error)
to_chat(owner, "Your victim has no blood to take.")
return FALSE
- if (ishuman(target))
+ if(ishuman(target))
var/mob/living/carbon/human/H = target
if(NOBLOOD in H.dna.species.species_traits)// || owner.get_blood_id() != target.get_blood_id())
- if (display_error)
+ if(display_error)
to_chat(owner, "Your victim's blood is not suitable for you to take.")
return FALSE
return TRUE
@@ -75,9 +75,9 @@
feed_target = null
target_grappled = FALSE
// If you are pulling a mob, that's your target. If you don't like it, then release them.
- if (owner.pulling && ismob(owner.pulling))
+ if(owner.pulling && ismob(owner.pulling))
// Check grapple target Valid
- if (!ValidateTarget(owner.pulling, display_error)) // Grabbed targets display error.
+ if(!ValidateTarget(owner.pulling, display_error)) // Grabbed targets display error.
return FALSE
target_grappled = TRUE
feed_target = owner.pulling
@@ -86,11 +86,11 @@
var/list/mob/living/seen_targets = view(1, owner)
var/list/mob/living/seen_mobs = list()
for(var/mob/living/M in seen_targets)
- if (isliving(M) && M != owner)
+ if(isliving(M) && M != owner)
seen_mobs += M
// None Seen!
- if (seen_mobs.len == 0)
- if (display_error)
+ if(seen_mobs.len == 0)
+ if(display_error)
to_chat(owner, "You must be next to or grabbing a victim to feed from them.")
return FALSE
// Check Valids...
@@ -98,19 +98,19 @@
var/list/targets_dead = list()
for(var/mob/living/M in seen_mobs)
// Check adjecent Valid target
- if (M != owner && ValidateTarget(M, display_error = FALSE)) // Do NOT display errors. We'll be doing this again in CheckCanUse(), which will rule out grabbed targets.
+ if(M != owner && ValidateTarget(M, display_error = FALSE)) // Do NOT display errors. We'll be doing this again in CheckCanUse(), which will rule out grabbed targets.
// Prioritize living, but remember dead as backup
- if (M.stat < DEAD)
+ if(M.stat < DEAD)
targets_valid += M
else
targets_dead += M
// No Living? Try dead.
- if (targets_valid.len == 0 && targets_dead.len > 0)
+ if(targets_valid.len == 0 && targets_dead.len > 0)
targets_valid = targets_dead
// No Targets
- if (targets_valid.len == 0)
+ if(targets_valid.len == 0)
// Did I see targets? Then display at least one error
- if (seen_mobs.len > 1)
+ if(seen_mobs.len > 1)
if (display_error)
to_chat(owner, "None of these are valid targets to feed from subtly.")
else
@@ -136,28 +136,28 @@
// Initial Wait
var/feed_time = (amSilent ? 45 : 25) - (2.5 * level_current)
feed_time = max(15, feed_time)
- if (amSilent)
+ if(amSilent)
to_chat(user, "You lean quietly toward [target] and secretly draw out your fangs...")
else
to_chat(user, "You pull [target] close to you and draw out your fangs...")
- if (!do_mob(user, target, feed_time,0,1,extra_checks=CALLBACK(src, .proc/ContinueActive, user, target)))//sleep(10)
+ if(!do_mob(user, target, feed_time,0,1,extra_checks=CALLBACK(src, .proc/ContinueActive, user, target)))//sleep(10)
to_chat(user, "Your feeding was interrupted.")
//DeactivatePower(user,target)
return
// Put target to Sleep (Bloodsuckers are immune to their own bite's sleep effect)
- if (!amSilent)
+ if(!amSilent)
ApplyVictimEffects(target) // Sleep, paralysis, immobile, unconscious, and mute
if(target.stat <= UNCONSCIOUS)
sleep(1)
// Wait, then Cancel if Invalid
- if (!ContinueActive(user,target)) // Cancel. They're gone.
+ if(!ContinueActive(user,target)) // Cancel. They're gone.
//DeactivatePower(user,target)
return
// Pull Target Close
- if (!target.density) // Pull target to you if they don't take up space.
+ if(!target.density) // Pull target to you if they don't take up space.
target.Move(user.loc)
// Broadcast Message
- if (amSilent)
+ if(amSilent)
//if (!iscarbon(target))
// user.visible_message("[user] shifts [target] closer to [user.p_their()] mouth.", \
// "You secretly slip your fangs into [target]'s flesh.", \
@@ -170,10 +170,10 @@
// Warn Feeder about Witnesses...
var/was_unnoticed = TRUE
for(var/mob/living/M in viewers(notice_range, owner))
- if(M != owner && M != target && iscarbon(M) && M.mind && !M.has_unlimited_silicon_privilege && !M.eye_blind && !M.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER))
+ if(M != owner && M != target && iscarbon(M) && M.mind && !M.silicon_privileges && !M.eye_blind && !M.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER))
was_unnoticed = FALSE
break
- if (was_unnoticed)
+ if(was_unnoticed)
to_chat(user, "You think no one saw you...")
else
to_chat(user, "Someone may have noticed...")
@@ -197,16 +197,16 @@
// FEEEEEEEEED!!! //
bloodsuckerdatum.poweron_feed = TRUE
- while (bloodsuckerdatum && target && active)
+ while(bloodsuckerdatum && target && active)
//user.mobility_flags &= ~MOBILITY_MOVE // user.canmove = 0 // Prevents spilling blood accidentally.
// Abort? A bloody mistake.
- if (!do_mob(user, target, 20, 0, 0, extra_checks=CALLBACK(src, .proc/ContinueActive, user, target)))
+ if(!do_mob(user, target, 20, 0, 0, extra_checks=CALLBACK(src, .proc/ContinueActive, user, target)))
// May have disabled Feed during do_mob
- if (!active || !ContinueActive(user, target))
+ if(!active || !ContinueActive(user, target))
break
- if (amSilent)
+ if(amSilent)
to_chat(user, "Your feeding has been interrupted...but [target.p_they()] didn't seem to notice you.")
else
to_chat(user, "Your feeding has been interrupted!")
@@ -214,11 +214,11 @@
"Your teeth are ripped from [target]'s throat. [target.p_their(TRUE)] blood sprays everywhere!")
// Deal Damage to Target (should have been more careful!)
- if (iscarbon(target))
+ if(iscarbon(target))
var/mob/living/carbon/C = target
C.bleed(15)
playsound(get_turf(target), 'sound/effects/splat.ogg', 40, 1)
- if (ishuman(target))
+ if(ishuman(target))
var/mob/living/carbon/human/H = target
H.bleed_rate += 5
target.add_splatter_floor(get_turf(target))
@@ -228,7 +228,7 @@
target.emote("scream")
// Killed Target?
- if (was_alive)
+ if(was_alive)
CheckKilledTarget(user,target)
return
@@ -237,40 +237,40 @@
// Handle Feeding! User & Victim Effects (per tick)
bloodsuckerdatum.HandleFeeding(target, blood_take_mult)
amount_taken += amSilent ? 0.3 : 1
- if (!amSilent)
+ if(!amSilent)
ApplyVictimEffects(target) // Sleep, paralysis, immobile, unconscious, and mute
- if (amount_taken > 5 && target.stat < DEAD && ishuman(target))
+ if(amount_taken > 5 && target.stat < DEAD && ishuman(target))
SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "drankblood", /datum/mood_event/drankblood) // GOOD // in bloodsucker_life.dm
///////////////////////////////////////////////////////////
// Not Human?
- if (!ishuman(target))
+ if(!ishuman(target))
SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "drankblood", /datum/mood_event/drankblood_bad) // BAD // in bloodsucker_life.dm
- if (!warning_target_inhuman)
+ if(!warning_target_inhuman)
to_chat(user, "You recoil at the taste of a lesser lifeform.")
warning_target_inhuman = TRUE
// Dead Blood?
- if (target.stat >= DEAD)
- if (ishuman(target))
+ if(target.stat >= DEAD)
+ if(ishuman(target))
SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "drankblood", /datum/mood_event/drankblood_dead) // BAD // in bloodsucker_life.dm
- if (!warning_target_dead)
+ if(!warning_target_dead)
to_chat(user, "Your victim is dead. [target.p_their(TRUE)] blood barely nourishes you.")
warning_target_dead = TRUE
// Full?
- if (!warning_full && user.blood_volume >= bloodsuckerdatum.maxBloodVolume)
+ if(!warning_full && user.blood_volume >= bloodsuckerdatum.maxBloodVolume)
to_chat(user, "You are full. Further blood will be wasted.")
warning_full = TRUE
// Blood Remaining? (Carbons/Humans only)
- if (iscarbon(target) && !target.AmBloodsucker(1))
- if (target.blood_volume <= BLOOD_VOLUME_BAD && warning_target_bloodvol > BLOOD_VOLUME_BAD)
+ if(iscarbon(target) && !target.AmBloodsucker(1))
+ if(target.blood_volume <= BLOOD_VOLUME_BAD && warning_target_bloodvol > BLOOD_VOLUME_BAD)
to_chat(user, "Your victim's blood volume is fatally low!")
- else if (target.blood_volume <= BLOOD_VOLUME_OKAY && warning_target_bloodvol > BLOOD_VOLUME_OKAY)
+ else if(target.blood_volume <= BLOOD_VOLUME_OKAY && warning_target_bloodvol > BLOOD_VOLUME_OKAY)
to_chat(user, "Your victim's blood volume is dangerously low.")
- else if (target.blood_volume <= BLOOD_VOLUME_SAFE && warning_target_bloodvol > BLOOD_VOLUME_SAFE)
+ else if(target.blood_volume <= BLOOD_VOLUME_SAFE && warning_target_bloodvol > BLOOD_VOLUME_SAFE)
to_chat(user, "Your victim's blood is at an unsafe level.")
warning_target_bloodvol = target.blood_volume // If we had a warning to give, it's been given by now.
// Done?
- if (target.blood_volume <= 0)
+ if(target.blood_volume <= 0)
to_chat(user, "You have bled your victim dry.")
break
@@ -279,7 +279,7 @@
// DONE!
//DeactivatePower(user,target)
- if (amSilent)
+ if(amSilent)
to_chat(user, "You slowly release [target]'s wrist." + (target.stat == 0 ? " [target.p_their(TRUE)] face lacks expression, like you've already been forgotten." : ""))
else
user.visible_message("[user] unclenches their teeth from [target]'s neck.", \
@@ -289,13 +289,13 @@
log_combat(owner, target, "fed on blood", addition="(and took [amount_taken] blood)")
// Killed Target?
- if (was_alive)
+ if(was_alive)
CheckKilledTarget(user,target)
/datum/action/bloodsucker/feed/proc/CheckKilledTarget(mob/living/user, mob/living/target)
// Bad Vampire. You shouldn't do that.
- if (target && target.stat >= DEAD && ishuman(target))
+ if(target && target.stat >= DEAD && ishuman(target))
SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "drankkilled", /datum/mood_event/drankkilled) // BAD // in bloodsucker_life.dm
/datum/action/bloodsucker/feed/ContinueActive(mob/living/user, mob/living/target)
@@ -304,18 +304,18 @@
/datum/action/bloodsucker/feed/proc/ApplyVictimEffects(mob/living/target)
// Bloodsuckers not affected by "the Kiss" of another vampire
- if (!target.mind || !target.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER))
+ if(!target.mind || !target.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER))
target.Unconscious(50,0)
target.Knockdown(40 + 5 * level_current,1)
// NOTE: THis is based on level of power!
- if (ishuman(target))
+ if(ishuman(target))
target.adjustStaminaLoss(5, forced = TRUE)// Base Stamina Damage
/datum/action/bloodsucker/feed/DeactivatePower(mob/living/user = owner, mob/living/target)
..() // activate = FALSE
var/datum/antagonist/bloodsucker/bloodsuckerdatum = user.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
// No longer Feeding
- if (bloodsuckerdatum)
+ if(bloodsuckerdatum)
bloodsuckerdatum.poweron_feed = FALSE
feed_target = null
// My mouth is no longer full
diff --git a/code/modules/antagonists/bloodsucker/powers/bs_fortitude.dm b/code/modules/antagonists/bloodsucker/powers/fortitude.dm
similarity index 96%
rename from code/modules/antagonists/bloodsucker/powers/bs_fortitude.dm
rename to code/modules/antagonists/bloodsucker/powers/fortitude.dm
index 3305504c15..f0724f8204 100644
--- a/code/modules/antagonists/bloodsucker/powers/bs_fortitude.dm
+++ b/code/modules/antagonists/bloodsucker/powers/fortitude.dm
@@ -6,7 +6,7 @@
name = "Fortitude"//"Cellular Emporium"
desc = "Withstand egregious physical wounds and walk away from attacks that would stun, pierce, and dismember lesser beings. You cannot run while active."
button_icon_state = "power_fortitude"
- bloodcost = 5
+ bloodcost = 30
cooldown = 80
bloodsucker_can_buy = TRUE
amToggle = TRUE
@@ -23,7 +23,7 @@
ADD_TRAIT(user, TRAIT_NODISMEMBER, "fortitude")
ADD_TRAIT(user, TRAIT_STUNIMMUNE, "fortitude")
ADD_TRAIT(user, TRAIT_NORUNNING, "fortitude")
- if (ishuman(owner))
+ if(ishuman(owner))
var/mob/living/carbon/human/H = owner
this_resist = max(0.3, 0.7 - level_current * 0.1)
H.physiology.brute_mod *= this_resist//0.5
@@ -34,7 +34,7 @@
user.toggle_move_intent()
while(bloodsuckerdatum && ContinueActive(user) || user.m_intent == MOVE_INTENT_RUN)
// Pay Blood Toll (if awake)
- if (user.stat == CONSCIOUS)
+ if(user.stat == CONSCIOUS)
bloodsuckerdatum.AddBloodVolume(-0.5) // Used to be 0.3 blood per 2 seconds, but we're making it more expensive to keep on.
sleep(20) // Check every few ticks that we haven't disabled this power
// Return to Running (if you were before)
@@ -48,7 +48,7 @@
REMOVE_TRAIT(user, TRAIT_NODISMEMBER, "fortitude")
REMOVE_TRAIT(user, TRAIT_STUNIMMUNE, "fortitude")
REMOVE_TRAIT(user, TRAIT_NORUNNING, "fortitude")
- if (ishuman(owner))
+ if(ishuman(owner))
var/mob/living/carbon/human/H = owner
H.physiology.brute_mod /= this_resist//0.5
H.physiology.burn_mod /= this_resist//0.5
diff --git a/code/modules/antagonists/bloodsucker/powers/bs_gohome.dm b/code/modules/antagonists/bloodsucker/powers/go_home.dm
similarity index 68%
rename from code/modules/antagonists/bloodsucker/powers/bs_gohome.dm
rename to code/modules/antagonists/bloodsucker/powers/go_home.dm
index 0da9ab5ba3..3fa8a07299 100644
--- a/code/modules/antagonists/bloodsucker/powers/bs_gohome.dm
+++ b/code/modules/antagonists/bloodsucker/powers/go_home.dm
@@ -7,7 +7,7 @@
background_icon_state_on = "vamp_power_off_oneshot" // Even though this never goes off.
background_icon_state_off = "vamp_power_off_oneshot"
- bloodcost = 25
+ bloodcost = 100
cooldown = 99999 // It'll never come back.
amToggle = FALSE
amSingleUse = TRUE
@@ -23,47 +23,49 @@
return
// Have No Lair (NOTE: You only got this power if you had a lair, so this means it's destroyed)
var/datum/antagonist/bloodsucker/bloodsuckerdatum = owner.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
- if (!istype(bloodsuckerdatum) || !bloodsuckerdatum.coffin)
- if (display_error)
+ if(!istype(bloodsuckerdatum) || !bloodsuckerdatum.coffin)
+ if(display_error)
to_chat(owner, "Your coffin has been destroyed!")
return FALSE
return TRUE
+/datum/action/bloodsucker/gohome/proc/flicker_lights(var/flicker_range, var/beat_volume)
+ for(var/obj/machinery/light/L in view(flicker_range, get_turf(owner)))
+ playsound(get_turf(owner), 'sound/effects/singlebeat.ogg', beat_volume, 1)
+
+
/datum/action/bloodsucker/gohome/ActivatePower()
var/mob/living/carbon/user = owner
var/datum/antagonist/bloodsucker/bloodsuckerdatum = owner.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
// IMPORTANT: Check for lair at every step! It might get destroyed.
to_chat(user, "You focus on separating your consciousness from your physical form...")
// STEP ONE: Flicker Lights
- for(var/obj/machinery/light/L in view(3, get_turf(owner))) // /obj/machinery/light/proc/flicker(var/amount = rand(10, 20))
- L.flicker(5)
- playsound(get_turf(owner), 'sound/effects/singlebeat.ogg', 20, 1)
+ flicker_lights(3, 20)
sleep(50)
- for(var/obj/machinery/light/L in view(3, get_turf(owner))) // /obj/machinery/light/proc/flicker(var/amount = rand(10, 20))
- L.flicker(5)
- playsound(get_turf(owner), 'sound/effects/singlebeat.ogg', 40, 1)
+ flicker_lights(4, 40)
sleep(50)
- for(var/obj/machinery/light/L in view(6, get_turf(owner))) // /obj/machinery/light/proc/flicker(var/amount = rand(10, 20))
+ flicker_lights(4, 60)
+ for(var/obj/machinery/light/L in view(6, get_turf(owner)))
L.flicker(5)
playsound(get_turf(owner), 'sound/effects/singlebeat.ogg', 60, 1)
// ( STEP TWO: Lights OFF? )
// CHECK: Still have Coffin?
- if (!istype(bloodsuckerdatum) || !bloodsuckerdatum.coffin)
+ if(!istype(bloodsuckerdatum) || !bloodsuckerdatum.coffin)
to_chat(user, "Your coffin has been destroyed! You no longer have a destination.")
return FALSE
- if (!owner)
+ if(!owner)
return
// SEEN?: (effects ONLY if there are witnesses! Otherwise you just POOF)
- // NOTE: Stolen directly from statue.dm, thanks guys!
+
var/am_seen = FALSE // Do Effects (seen by anyone)
var/drop_item = FALSE // Drop Stuff (seen by non-vamp)
- if (isturf(owner.loc)) // Only check if I'm not in a Locker or something.
+ if(isturf(owner.loc)) // Only check if I'm not in a Locker or something.
// A) Check for Darkness (we can just leave)
var/turf/T = get_turf(user)
if(T && T.lighting_object && T.get_lumcount()>= 0.1)
// B) Check for Viewers
- for(var/mob/living/M in viewers(owner))
- if(M != owner && isliving(M) && M.mind && !M.has_unlimited_silicon_privilege && !M.eye_blind) // M.client <--- add this in after testing!
+ for(var/mob/living/M in viewers(get_turf(owner)))
+ if(M != owner && isliving(M) && M.mind && !M.silicon_privileges && !M.eye_blind) // M.client <--- add this in after testing!
am_seen = TRUE
if (!M.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER))
drop_item = TRUE
@@ -76,7 +78,7 @@
var/obj/O = user.legcuffed
user.dropItemToGround(O)
// SEEN!
- if (drop_item)
+ if(drop_item)
// DROP: Clothes, held items, and cuffs etc
// NOTE: Taken from unequip_everything() in inventory.dm. We need to
// *force* all items to drop, so we had to just gut the code out of it.
@@ -86,30 +88,28 @@
user.dropItemToGround(I,TRUE)
for(var/obj/item/I in owner.held_items) // drop_all_held_items()
user.dropItemToGround(I, TRUE)
- if (am_seen)
+ if(am_seen)
// POOF EFFECTS
playsound(get_turf(owner), 'sound/magic/summon_karp.ogg', 60, 1)
var/datum/effect_system/steam_spread/puff = new /datum/effect_system/steam_spread/()
puff.effect_type = /obj/effect/particle_effect/smoke/vampsmoke
puff.set_up(3, 0, get_turf(owner))
puff.start()
+
+ //STEP FIVE: Create animal at prev location
+ var/mob/living/simple_animal/SA = pick(/mob/living/simple_animal/mouse,/mob/living/simple_animal/mouse,/mob/living/simple_animal/mouse, /mob/living/simple_animal/hostile/retaliate/bat) //prob(300) /mob/living/simple_animal/mouse,
+ new SA (owner.loc)
// TELEPORT: Move to Coffin & Close it!
- do_teleport(owner, bloodsuckerdatum.coffin, no_effects = TRUE, forced = TRUE, channel = TELEPORT_CHANNEL_QUANTUM) // in teleport.dm?
- // SLEEP
+ do_teleport(owner, bloodsuckerdatum.coffin, no_effects = TRUE, forced = TRUE, channel = TELEPORT_CHANNEL_QUANTUM)
user.resting = TRUE
- //user.Unconscious(30,0)
user.Stun(30,1)
// CLOSE LID: If fail, force me in.
- if (!bloodsuckerdatum.coffin.close(owner))
+ if(!bloodsuckerdatum.coffin.close(owner))
bloodsuckerdatum.coffin.insert(owner) // Puts me inside.
- // The following was taken from close() proc in closets.dm
- // (but we had to do it this way because there is no way to force entry)
playsound(bloodsuckerdatum.coffin.loc, bloodsuckerdatum.coffin.close_sound, 15, 1, -3)
bloodsuckerdatum.coffin.opened = FALSE
bloodsuckerdatum.coffin.density = TRUE
bloodsuckerdatum.coffin.update_icon()
// Lock Coffin
bloodsuckerdatum.coffin.LockMe(owner)
- // ( STEP FIVE: Create animal at prev location? )
- //var/mob/living/simple_animal/SA = /mob/living/simple_animal/hostile/retaliate/bat // pick(/mob/living/simple_animal/mouse,/mob/living/simple_animal/mouse,/mob/living/simple_animal/mouse, /mob/living/simple_animal/hostile/retaliate/bat) //prob(300) /mob/living/simple_animal/mouse,
- //new SA (owner.loc)
+
diff --git a/code/modules/antagonists/bloodsucker/powers/bs_haste.dm b/code/modules/antagonists/bloodsucker/powers/haste.dm
similarity index 52%
rename from code/modules/antagonists/bloodsucker/powers/bs_haste.dm
rename to code/modules/antagonists/bloodsucker/powers/haste.dm
index ee1dd3007c..47c93b794e 100644
--- a/code/modules/antagonists/bloodsucker/powers/bs_haste.dm
+++ b/code/modules/antagonists/bloodsucker/powers/haste.dm
@@ -8,12 +8,15 @@
desc = "Dash somewhere with supernatural speed. Those nearby may be knocked away, stunned, or left empty-handed."
button_icon_state = "power_speed"
bloodcost = 6
- cooldown = 30
+ cooldown = 50
target_range = 15
power_activates_immediately = TRUE
message_Trigger = ""//"Whom will you subvert to your will?"
bloodsucker_can_buy = TRUE
must_be_capacitated = TRUE
+ var/list/hit //current hit, set while power is in use as we can't pass the list as an extra calling argument in registersignal.
+ /// If set, uses this speed in deciseconds instead of world.tick_lag
+ var/speed_override
/datum/action/bloodsucker/targeted/haste/CheckCanUse(display_error)
. = ..()
@@ -43,43 +46,46 @@
return TRUE
/datum/action/bloodsucker/targeted/haste/FireTargetedPower(atom/A)
- // set waitfor = FALSE <---- DONT DO THIS!We WANT this power to hold up ClickWithPower(), so that we can unlock the power when it's done.
+ // This is a non-async proc to make sure the power is "locked" until this finishes.
+ hit = list()
+ RegisterSignal(owner, COMSIG_MOVABLE_MOVED, .proc/on_move)
var/mob/living/user = owner
var/turf/T = isturf(A) ? A : get_turf(A)
// Pulled? Not anymore.
- owner.pulledby = null
- // Step One: Heatseek toward Target's Turf
- walk_to(owner, T, 0, 0.01, 20) // NOTE: this runs in the background! to cancel it, you need to use walk(owner.current,0), or give them a new path.
+ user.pulledby?.stop_pulling()
+ // Go to target turf
+ // DO NOT USE WALK TO.
playsound(get_turf(owner), 'sound/weapons/punchmiss.ogg', 25, 1, -1)
- var/safety = 20
- while(get_turf(owner) != T && safety > 0 && !(isliving(target) && target.Adjacent(owner)))
- user.canmove = FALSE //Dont move while doing the thing, or itll break
- safety --
- // Did I get knocked down?
- if(owner && owner.incapacitated(ignore_restraints=TRUE, ignore_grab=TRUE))// owner.incapacitated())
- // We're gonna cancel. But am I on the ground? Spin me!
- if(user.resting)
- var/send_dir = get_dir(owner, T)
- new /datum/forced_movement(owner, get_ranged_target_turf(owner, send_dir, 1), 1, FALSE)
- owner.spin(10)
+ var/safety = get_dist(user, T) * 3 + 1
+ var/consequetive_failures = 0
+ var/speed = isnull(speed_override)? world.tick_lag : speed_override
+ while(--safety && (get_turf(user) != T))
+ var/success = step_towards(user, T) //This does not try to go around obstacles.
+ if(!success)
+ success = step_to(user, T) //this does
+ if(!success)
+ if(++consequetive_failures >= 3) //if 3 steps don't work
+ break //just stop
+ else
+ consequetive_failures = 0
+ if(user.resting)
+ user.setDir(turn(user.dir, 90)) //down? spin2win :^)
+ if(user.incapacitated(ignore_restraints = TRUE, ignore_grab = TRUE)) //actually down? stop.
break
- // Spin/Stun people we pass.
- //var/mob/living/newtarget = locate(/mob/living) in oview(1, owner)
- var/list/mob/living/foundtargets = list()
- for(var/mob/living/newtarget in oview(1, owner))
- if (newtarget && newtarget != target && !(newtarget in foundtargets))//!newtarget.IsKnockdown())
- if (rand(0, 5) < level_current)
- playsound(get_turf(newtarget), "sound/weapons/punch[rand(1,4)].ogg", 15, 1, -1)
- newtarget.Knockdown(10 + level_current * 5)
- if(newtarget.IsStun())
- newtarget.spin(10,1)
- if (rand(0,4))
- newtarget.drop_all_held_items()
- foundtargets += newtarget
- sleep(1)
- if(user)
- user.update_canmove() //Let the poor guy move again
+ if(success) //don't sleep if we failed to move.
+ sleep(speed)
+ UnregisterSignal(owner, COMSIG_MOVABLE_MOVED)
+ hit = null
+ user.update_canmove()
/datum/action/bloodsucker/targeted/haste/DeactivatePower(mob/living/user = owner, mob/living/target)
..() // activate = FALSE
user.update_canmove()
+
+/datum/action/bloodsucker/targeted/haste/proc/on_move()
+ for(var/mob/living/L in dview(1, get_turf(owner)))
+ if(!hit[L] && (L != owner))
+ hit[L] = TRUE
+ playsound(L, "sound/weapons/punch[rand(1,4)].ogg", 15, 1, -1)
+ L.Knockdown(10 + level_current * 5, override_hardstun = 0.1)
+ L.spin(10, 1)
diff --git a/code/modules/antagonists/bloodsucker/powers/bs_lunge.dm b/code/modules/antagonists/bloodsucker/powers/lunge.dm
similarity index 98%
rename from code/modules/antagonists/bloodsucker/powers/bs_lunge.dm
rename to code/modules/antagonists/bloodsucker/powers/lunge.dm
index 2c1b8d6321..48e12332a2 100644
--- a/code/modules/antagonists/bloodsucker/powers/bs_lunge.dm
+++ b/code/modules/antagonists/bloodsucker/powers/lunge.dm
@@ -6,7 +6,7 @@
desc = "Spring at your target and aggressively grapple them without warning. Attacks from concealment or the rear may even knock them down."
button_icon_state = "power_lunge"
bloodcost = 10
- cooldown = 100
+ cooldown = 120
target_range = 3
power_activates_immediately = TRUE
message_Trigger = ""//"Whom will you subvert to your will?"
@@ -28,7 +28,7 @@
return TRUE
/datum/action/bloodsucker/targeted/lunge/CheckValidTarget(atom/A)
- return isliving(A)
+ return iscarbon(A)
/datum/action/bloodsucker/targeted/lunge/CheckCanTarget(atom/A, display_error)
// Check: Self
diff --git a/code/modules/antagonists/bloodsucker/powers/bs_masquerade.dm b/code/modules/antagonists/bloodsucker/powers/masquerade.dm
similarity index 99%
rename from code/modules/antagonists/bloodsucker/powers/bs_masquerade.dm
rename to code/modules/antagonists/bloodsucker/powers/masquerade.dm
index 0435ddccd5..7d41899dc2 100644
--- a/code/modules/antagonists/bloodsucker/powers/bs_masquerade.dm
+++ b/code/modules/antagonists/bloodsucker/powers/masquerade.dm
@@ -54,8 +54,8 @@
REMOVE_TRAIT(user, TRAIT_VIRUSIMMUNE, "bloodsucker")
var/obj/item/organ/heart/vampheart/H = user.getorganslot(ORGAN_SLOT_HEART)
var/obj/item/organ/eyes/vassal/bloodsucker/E = user.getorganslot(ORGAN_SLOT_EYES)
- E.flash_protect = 0
-
+ E.flash_protect = 0
+
// WE ARE ALIVE! //
bloodsuckerdatum.poweron_masquerade = TRUE
while(bloodsuckerdatum && ContinueActive(user))
diff --git a/code/modules/antagonists/bloodsucker/powers/bs_mesmerize.dm b/code/modules/antagonists/bloodsucker/powers/mesmerize.dm
similarity index 99%
rename from code/modules/antagonists/bloodsucker/powers/bs_mesmerize.dm
rename to code/modules/antagonists/bloodsucker/powers/mesmerize.dm
index 2fe7115d98..dc9d1d46a0 100644
--- a/code/modules/antagonists/bloodsucker/powers/bs_mesmerize.dm
+++ b/code/modules/antagonists/bloodsucker/powers/mesmerize.dm
@@ -10,7 +10,7 @@
desc = "Dominate the mind of a mortal who can see your eyes."
button_icon_state = "power_mez"
bloodcost = 30
- cooldown = 200
+ cooldown = 300
target_range = 1
power_activates_immediately = FALSE
message_Trigger = "Whom will you subvert to your will?"
diff --git a/code/modules/antagonists/bloodsucker/powers/v_recuperate.dm b/code/modules/antagonists/bloodsucker/powers/recuperate.dm
similarity index 100%
rename from code/modules/antagonists/bloodsucker/powers/v_recuperate.dm
rename to code/modules/antagonists/bloodsucker/powers/recuperate.dm
diff --git a/code/modules/antagonists/bloodsucker/powers/bs_trespass.dm b/code/modules/antagonists/bloodsucker/powers/trespass.dm
similarity index 99%
rename from code/modules/antagonists/bloodsucker/powers/bs_trespass.dm
rename to code/modules/antagonists/bloodsucker/powers/trespass.dm
index 159c7b20b7..45d1dda3ef 100644
--- a/code/modules/antagonists/bloodsucker/powers/bs_trespass.dm
+++ b/code/modules/antagonists/bloodsucker/powers/trespass.dm
@@ -6,7 +6,7 @@
button_icon_state = "power_tres"
bloodcost = 10
- cooldown = 60
+ cooldown = 80
amToggle = FALSE
//target_range = 2
diff --git a/code/modules/antagonists/bloodsucker/powers/bs_veil.dm b/code/modules/antagonists/bloodsucker/powers/veil.dm
similarity index 100%
rename from code/modules/antagonists/bloodsucker/powers/bs_veil.dm
rename to code/modules/antagonists/bloodsucker/powers/veil.dm
diff --git a/code/modules/antagonists/brother/brother.dm b/code/modules/antagonists/brother/brother.dm
index 7c589bb3ab..efe21f38fa 100644
--- a/code/modules/antagonists/brother/brother.dm
+++ b/code/modules/antagonists/brother/brother.dm
@@ -5,7 +5,6 @@
var/special_role = ROLE_BROTHER
var/datum/team/brother_team/team
antag_moodlet = /datum/mood_event/focused
- can_hijack = HIJACK_HIJACKER
/datum/antagonist/brother/create_team(datum/team/brother_team/new_team)
if(!new_team)
@@ -108,11 +107,17 @@
var/win = TRUE
var/objective_count = 1
for(var/datum/objective/objective in objectives)
- if(objective.check_completion())
- parts += "Objective #[objective_count]: [objective.explanation_text] Success!"
+ if(objective.completable)
+ var/completion = objective.check_completion()
+ if(completion >= 1)
+ parts += "Objective #[objective_count]: [objective.explanation_text] Success!"
+ else if(completion <= 0)
+ parts += "Objective #[objective_count]: [objective.explanation_text] Fail."
+ win = FALSE
+ else
+ parts += "Objective #[objective_count]: [objective.explanation_text] [completion*100]%"
else
- parts += "Objective #[objective_count]: [objective.explanation_text] Fail."
- win = FALSE
+ parts += "Objective #[objective_count]: [objective.explanation_text]"
objective_count++
if(win)
parts += "The blood brothers were successful!"
diff --git a/code/modules/antagonists/changeling/changeling.dm b/code/modules/antagonists/changeling/changeling.dm
index 0d68660d9d..a81c409233 100644
--- a/code/modules/antagonists/changeling/changeling.dm
+++ b/code/modules/antagonists/changeling/changeling.dm
@@ -54,8 +54,10 @@
var/honorific
if(owner.current.gender == FEMALE)
honorific = "Ms."
- else
+ else if(owner.current.gender == MALE)
honorific = "Mr."
+ else
+ honorific = "Mx."
if(GLOB.possible_changeling_IDs.len)
changelingID = pick(GLOB.possible_changeling_IDs)
GLOB.possible_changeling_IDs -= changelingID
@@ -552,11 +554,17 @@
if(objectives.len)
var/count = 1
for(var/datum/objective/objective in objectives)
- if(objective.check_completion())
- parts += "Objective #[count]: [objective.explanation_text] Success!"
+ if(objective.completable)
+ var/completion = objective.check_completion()
+ if(completion >= 1)
+ parts += "Objective #[count]: [objective.explanation_text] Success!"
+ else if(completion <= 0)
+ parts += "Objective #[count]: [objective.explanation_text] Fail."
+ changelingwin = FALSE
+ else
+ parts += "Objective #[count]: [objective.explanation_text] [completion*100]%"
else
- parts += "Objective #[count]: [objective.explanation_text] Fail."
- changelingwin = 0
+ parts += "Objective #[count]: [objective.explanation_text]"
count++
if(changelingwin)
diff --git a/code/modules/antagonists/changeling/powers/fakedeath.dm b/code/modules/antagonists/changeling/powers/fakedeath.dm
index 88328c582c..8ed5b5e39c 100644
--- a/code/modules/antagonists/changeling/powers/fakedeath.dm
+++ b/code/modules/antagonists/changeling/powers/fakedeath.dm
@@ -15,7 +15,7 @@
to_chat(user, "We begin our stasis, preparing energy to arise once more.")
if(user.stat != DEAD)
user.emote("deathgasp")
- user.tod = STATION_TIME_TIMESTAMP("hh:mm:ss")
+ user.tod = STATION_TIME_TIMESTAMP("hh:mm:ss", world.time)
user.fakedeath("changeling") //play dead
user.update_stat()
user.update_canmove()
diff --git a/code/modules/antagonists/changeling/powers/headcrab.dm b/code/modules/antagonists/changeling/powers/headcrab.dm
index 1e06fb39de..4ef0d2f240 100644
--- a/code/modules/antagonists/changeling/powers/headcrab.dm
+++ b/code/modules/antagonists/changeling/powers/headcrab.dm
@@ -18,7 +18,7 @@
var/list/organs = user.getorganszone(BODY_ZONE_HEAD, 1)
for(var/obj/item/organ/I in organs)
- I.Remove(user, 1)
+ I.Remove(TRUE)
explosion(get_turf(user), 0, 0, 2, 0, TRUE)
for(var/mob/living/carbon/human/H in range(2,user))
diff --git a/code/modules/antagonists/changeling/powers/panacea.dm b/code/modules/antagonists/changeling/powers/panacea.dm
index 2a0451bc76..17d82ec72d 100644
--- a/code/modules/antagonists/changeling/powers/panacea.dm
+++ b/code/modules/antagonists/changeling/powers/panacea.dm
@@ -22,7 +22,7 @@
if(!istype(O))
continue
- O.Remove(user)
+ O.Remove()
if(iscarbon(user))
var/mob/living/carbon/C = user
C.vomit(0, toxic = TRUE)
diff --git a/code/modules/antagonists/clockcult/clock_items/replica_fabricator.dm b/code/modules/antagonists/clockcult/clock_items/replica_fabricator.dm
index 501bcdc1c4..7be9ef039b 100644
--- a/code/modules/antagonists/clockcult/clock_items/replica_fabricator.dm
+++ b/code/modules/antagonists/clockcult/clock_items/replica_fabricator.dm
@@ -68,8 +68,8 @@
/obj/item/clockwork/replica_fabricator/pre_attack(atom/target, mob/living/user, params)
if(!target || !user || !is_servant_of_ratvar(user) || istype(target, /obj/item/storage))
- return TRUE
- return fabricate(target, user)
+ return ..()
+ return !fabricate(target, user)
//A note here; return values are for if we CAN BE PUT ON A TABLE, not IF WE ARE SUCCESSFUL, unless no_table_check is TRUE
/obj/item/clockwork/replica_fabricator/proc/fabricate(atom/target, mob/living/user, silent, no_table_check)
diff --git a/code/modules/antagonists/clockcult/clock_items/soul_vessel.dm b/code/modules/antagonists/clockcult/clock_items/soul_vessel.dm
index d84338faea..f83afebe24 100644
--- a/code/modules/antagonists/clockcult/clock_items/soul_vessel.dm
+++ b/code/modules/antagonists/clockcult/clock_items/soul_vessel.dm
@@ -104,6 +104,6 @@
transfer_personality(H)
brainmob.fully_replace_character_name(null, "[braintype] [H.real_name]")
name = "[initial(name)] ([brainmob.name])"
- B.Remove(H)
+ B.Remove()
qdel(B)
H.update_hair()
diff --git a/code/modules/antagonists/clockcult/clock_mobs/_eminence.dm b/code/modules/antagonists/clockcult/clock_mobs/_eminence.dm
index 86099d8c79..e4722dbcb0 100644
--- a/code/modules/antagonists/clockcult/clock_mobs/_eminence.dm
+++ b/code/modules/antagonists/clockcult/clock_mobs/_eminence.dm
@@ -83,7 +83,7 @@
return
if(client.handle_spam_prevention(message,MUTE_IC))
return
- message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN))
+ message = trim(copytext_char(sanitize(message), 1, MAX_MESSAGE_LEN))
if(!message)
return
src.log_talk(message, LOG_SAY, tag="clockwork eminence")
diff --git a/code/modules/antagonists/clockcult/clock_mobs/clockwork_marauder.dm b/code/modules/antagonists/clockcult/clock_mobs/clockwork_marauder.dm
index 6591343116..897bff779d 100644
--- a/code/modules/antagonists/clockcult/clock_mobs/clockwork_marauder.dm
+++ b/code/modules/antagonists/clockcult/clock_mobs/clockwork_marauder.dm
@@ -8,7 +8,7 @@
name = "clockwork marauder"
desc = "The stalwart apparition of a soldier, blazing with crimson flames. It's armed with a gladius and shield."
icon_state = "clockwork_marauder"
- mob_biotypes = list(MOB_INORGANIC, MOB_HUMANOID)
+ mob_biotypes = MOB_HUMANOID
health = 120
maxHealth = 120
force_threshold = 8
diff --git a/code/modules/antagonists/clockcult/clock_structures/ocular_warden.dm b/code/modules/antagonists/clockcult/clock_structures/ocular_warden.dm
index d0940ebb7a..00db2b0d72 100644
--- a/code/modules/antagonists/clockcult/clock_structures/ocular_warden.dm
+++ b/code/modules/antagonists/clockcult/clock_structures/ocular_warden.dm
@@ -118,7 +118,7 @@
continue
if(ishostile(L))
var/mob/living/simple_animal/hostile/H = L
- if(("ratvar" in H.faction) || (!H.mind && "neutral" in H.faction))
+ if(("ratvar" in H.faction) || (!H.mind && ("neutral" in H.faction)))
continue
if(ismegafauna(H) || (!H.mind && H.AIStatus == AI_OFF))
continue
diff --git a/code/modules/antagonists/clockcult/clockcult.dm b/code/modules/antagonists/clockcult/clockcult.dm
index d68e9b594d..1869414ab6 100644
--- a/code/modules/antagonists/clockcult/clockcult.dm
+++ b/code/modules/antagonists/clockcult/clockcult.dm
@@ -120,7 +120,7 @@
hierophant_network.Grant(current)
current.throw_alert("clockinfo", /obj/screen/alert/clockwork/infodump)
var/obj/structure/destructible/clockwork/massive/celestial_gateway/G = GLOB.ark_of_the_clockwork_justiciar
- if(G.active && ishuman(current))
+ if(G && G.active && ishuman(current))
current.add_overlay(mutable_appearance('icons/effects/genetics.dmi', "servitude", -MUTATIONS_LAYER))
/datum/antagonist/clockcult/remove_innate_effects(mob/living/mob_override)
@@ -174,9 +174,12 @@
log_admin("[key_name(admin)] has made [new_owner.current] into a servant of Ratvar.")
/datum/antagonist/clockcult/admin_remove(mob/user)
- remove_servant_of_ratvar(owner.current, TRUE)
- message_admins("[key_name_admin(user)] has removed clockwork servant status from [owner.current].")
- log_admin("[key_name(user)] has removed clockwork servant status from [owner.current].")
+ var/mob/target = owner.current
+ if(!target)
+ return
+ remove_servant_of_ratvar(target, TRUE)
+ message_admins("[key_name_admin(user)] has removed clockwork servant status from [target].")
+ log_admin("[key_name(user)] has removed clockwork servant status from [target].")
/datum/antagonist/clockcult/get_admin_commands()
. = ..()
diff --git a/code/modules/antagonists/collector/collector.dm b/code/modules/antagonists/collector/collector.dm
new file mode 100644
index 0000000000..0e7a3a52e7
--- /dev/null
+++ b/code/modules/antagonists/collector/collector.dm
@@ -0,0 +1,19 @@
+/datum/antagonist/collector
+ name = "Contraband Collector"
+ show_in_antagpanel = FALSE
+ show_name_in_check_antagonists = FALSE
+ blacklisted_quirks = list() // no blacklist, these guys are harmless
+
+/datum/antagonist/collector/proc/forge_objectives()
+ var/datum/objective/hoard/collector/O = new
+ O.owner = owner
+ O.find_target()
+ objectives += O
+
+/datum/antagonist/collector/on_gain()
+ forge_objectives()
+ . = ..()
+
+/datum/antagonist/collector/greet()
+ to_chat(owner, "You are a contraband collector!")
+ owner.announce_objectives()
diff --git a/code/modules/antagonists/cult/blood_magic.dm b/code/modules/antagonists/cult/blood_magic.dm
index 794010d9c0..636061783e 100644
--- a/code/modules/antagonists/cult/blood_magic.dm
+++ b/code/modules/antagonists/cult/blood_magic.dm
@@ -143,7 +143,7 @@
//Cult Blood Spells
/datum/action/innate/cult/blood_spell/stun
name = "Stun"
- desc = "A potent spell that will stun and mute victims upon contact."
+ desc = "A potent spell that will stun and mute victims upon contact. When the cult ascends, so does the spell, it burns and throws back the victim!"
button_icon_state = "hand"
magic_path = "/obj/item/melee/blood_magic/stun"
health_cost = 10
@@ -343,7 +343,7 @@
icon = 'icons/obj/items_and_weapons.dmi'
icon_state = "disintegrate"
item_state = null
- item_flags = NEEDS_PERMIT | ABSTRACT | DROPDEL
+ item_flags = NEEDS_PERMIT | ABSTRACT | DROPDEL | NO_ATTACK_CHAIN_SOFT_STAMCRIT
w_class = WEIGHT_CLASS_HUGE
throwforce = 0
@@ -437,8 +437,15 @@
else
target.visible_message("[L] starts to glow in a halo of light!", \
"A feeling of warmth washes over you, rays of holy light surround your body and protect you from the flash of light!")
- else
- if(!iscultist(L))
+ else // cult doesn't stun any longer when halos are out, instead it does burn damage + knockback!
+ var/datum/antagonist/cult/user_antag = user.mind.has_antag_datum(/datum/antagonist/cult,TRUE)
+ if(user_antag.cult_team.cult_ascendent)
+ if(!iscultist(L))
+ L.adjustFireLoss(20)
+ if(L.move_resist < MOVE_FORCE_STRONG)
+ var/atom/throw_target = get_edge_target_turf(L, user.dir)
+ L.throw_at(throw_target, 7, 1, user)
+ else if(!iscultist(L))
L.Knockdown(160)
L.adjustStaminaLoss(140) //Ensures hard stamcrit
L.flash_act(1,1)
@@ -753,7 +760,7 @@
var/turf/T = get_turf(target)
if(T)
for(var/obj/effect/decal/cleanable/blood/B in view(T, 2))
- if(B.blood_state == "blood")
+ if(B.blood_state == BLOOD_STATE_BLOOD)
if(B.bloodiness == 100) //Bonus for "pristine" bloodpools, also to prevent cheese with footprint spam
temp += 30
else
diff --git a/code/modules/antagonists/cult/cult.dm b/code/modules/antagonists/cult/cult.dm
index df2383f892..4bab621674 100644
--- a/code/modules/antagonists/cult/cult.dm
+++ b/code/modules/antagonists/cult/cult.dm
@@ -300,7 +300,7 @@
if(ishuman(cultist))
var/mob/living/carbon/human/H = cultist
H.eye_color = "f00"
- H.dna.update_ui_block(DNA_EYE_COLOR_BLOCK)
+ H.dna?.update_ui_block(DNA_EYE_COLOR_BLOCK)
ADD_TRAIT(H, TRAIT_CULT_EYES, "valid_cultist")
H.update_body()
@@ -425,10 +425,16 @@
parts += "The cultists' objectives were:"
var/count = 1
for(var/datum/objective/objective in objectives)
- if(objective.check_completion())
- parts += "Objective #[count]: [objective.explanation_text] Success!"
+ if(objective.completable)
+ var/completion = objective.check_completion()
+ if(completion >= 1)
+ parts += "Objective #[count]: [objective.explanation_text] Success!"
+ else if(completion <= 0)
+ parts += "Objective #[count]: [objective.explanation_text] Fail."
+ else
+ parts += "Objective #[count]: [objective.explanation_text] [completion*100]%"
else
- parts += "Objective #[count]: [objective.explanation_text] Fail."
+ parts += "Objective #[count]: [objective.explanation_text]"
count++
if(members.len)
diff --git a/code/modules/antagonists/cult/ritual.dm b/code/modules/antagonists/cult/ritual.dm
index ba2a96289d..e92f562db3 100644
--- a/code/modules/antagonists/cult/ritual.dm
+++ b/code/modules/antagonists/cult/ritual.dm
@@ -116,7 +116,7 @@ This file contains the cult dagger and rune list code
if(user.blood_volume)
user.apply_damage(initial(rune_to_scribe.scribe_damage), BRUTE, pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
var/scribe_mod = initial(rune_to_scribe.scribe_delay)
- if(istype(get_turf(user), /turf/open/floor/engine/cult))
+ if(istype(get_turf(user), /turf/open/floor/engine/cult) && !(ispath(rune_to_scribe, /obj/effect/rune/narsie)))
scribe_mod *= 0.5
if(!do_after(user, scribe_mod, target = get_turf(user)))
for(var/V in shields)
diff --git a/code/modules/antagonists/cult/runes.dm b/code/modules/antagonists/cult/runes.dm
index da002e568c..0111334748 100644
--- a/code/modules/antagonists/cult/runes.dm
+++ b/code/modules/antagonists/cult/runes.dm
@@ -22,7 +22,7 @@ Runes can either be invoked by one's self or with many different cultists. Each
icon = 'icons/obj/rune.dmi'
icon_state = "1"
resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF
- layer = LOW_OBJ_LAYER
+ layer = SIGIL_LAYER
color = RUNE_COLOR_RED
var/invocation = "Aiy ele-mayo." //This is said by cultists when the rune is invoked.
@@ -885,7 +885,7 @@ structure_check() searches for nearby cultist structures required for the invoca
if(new_human)
new_human.visible_message("[new_human] suddenly dissolves into bones and ashes.", \
"Your link to the world fades. Your form breaks apart.")
- for(var/obj/I in new_human)
+ for(var/obj/item/I in new_human)
new_human.dropItemToGround(I, TRUE)
new_human.dust()
else if(choice == "Ascend as a Dark Spirit")
diff --git a/code/modules/antagonists/devil/imp/imp.dm b/code/modules/antagonists/devil/imp/imp.dm
index fa5263059a..c2f636959b 100644
--- a/code/modules/antagonists/devil/imp/imp.dm
+++ b/code/modules/antagonists/devil/imp/imp.dm
@@ -13,7 +13,7 @@
icon = 'icons/mob/mob.dmi'
icon_state = "imp"
icon_living = "imp"
- mob_biotypes = list(MOB_ORGANIC, MOB_HUMANOID)
+ mob_biotypes = MOB_ORGANIC|MOB_HUMANOID
speed = 1
a_intent = INTENT_HARM
stop_automated_movement = 1
diff --git a/code/modules/antagonists/disease/disease_abilities.dm b/code/modules/antagonists/disease/disease_abilities.dm
index dc2249006c..e8dcaf9cc7 100644
--- a/code/modules/antagonists/disease/disease_abilities.dm
+++ b/code/modules/antagonists/disease/disease_abilities.dm
@@ -5,49 +5,49 @@ is currently following.
*/
GLOBAL_LIST_INIT(disease_ability_singletons, list(
- new /datum/disease_ability/action/cough,
- new /datum/disease_ability/action/sneeze,
- new /datum/disease_ability/action/infect,
- new /datum/disease_ability/symptom/mild/cough,
- new /datum/disease_ability/symptom/mild/sneeze,
- new /datum/disease_ability/symptom/medium/shedding,
- new /datum/disease_ability/symptom/medium/beard,
- new /datum/disease_ability/symptom/medium/hallucigen,
- new /datum/disease_ability/symptom/medium/choking,
- new /datum/disease_ability/symptom/medium/confusion,
- new /datum/disease_ability/symptom/medium/vomit,
- new /datum/disease_ability/symptom/medium/voice_change,
- new /datum/disease_ability/symptom/medium/visionloss,
- new /datum/disease_ability/symptom/medium/deafness,
- new /datum/disease_ability/symptom/powerful/narcolepsy,
- new /datum/disease_ability/symptom/medium/fever,
- new /datum/disease_ability/symptom/medium/shivering,
- new /datum/disease_ability/symptom/medium/headache,
- new /datum/disease_ability/symptom/medium/nano_boost,
- new /datum/disease_ability/symptom/medium/nano_destroy,
- new /datum/disease_ability/symptom/medium/viraladaptation,
- new /datum/disease_ability/symptom/medium/viralevolution,
- new /datum/disease_ability/symptom/medium/vitiligo,
- new /datum/disease_ability/symptom/medium/revitiligo,
- new /datum/disease_ability/symptom/medium/itching,
- new /datum/disease_ability/symptom/medium/heal/weight_loss,
- new /datum/disease_ability/symptom/medium/heal/sensory_restoration,
- new /datum/disease_ability/symptom/medium/heal/mind_restoration,
- new /datum/disease_ability/symptom/powerful/fire,
- new /datum/disease_ability/symptom/powerful/flesh_eating,
-// new /datum/disease_ability/symptom/powerful/genetic_mutation,
- new /datum/disease_ability/symptom/powerful/inorganic_adaptation,
- new /datum/disease_ability/symptom/powerful/heal/starlight,
- new /datum/disease_ability/symptom/powerful/heal/oxygen,
- new /datum/disease_ability/symptom/powerful/heal/chem,
- new /datum/disease_ability/symptom/powerful/heal/metabolism,
- new /datum/disease_ability/symptom/powerful/heal/dark,
- new /datum/disease_ability/symptom/powerful/heal/water,
- new /datum/disease_ability/symptom/powerful/heal/plasma,
- new /datum/disease_ability/symptom/powerful/heal/radiation,
- new /datum/disease_ability/symptom/powerful/heal/coma,
- new /datum/disease_ability/symptom/powerful/youth
- ))
+new /datum/disease_ability/action/cough,
+new /datum/disease_ability/action/sneeze,
+new /datum/disease_ability/action/infect,
+new /datum/disease_ability/symptom/mild/cough,
+new /datum/disease_ability/symptom/mild/sneeze,
+new /datum/disease_ability/symptom/medium/shedding,
+new /datum/disease_ability/symptom/medium/beard,
+new /datum/disease_ability/symptom/medium/hallucigen,
+new /datum/disease_ability/symptom/medium/choking,
+new /datum/disease_ability/symptom/medium/confusion,
+new /datum/disease_ability/symptom/medium/vomit,
+new /datum/disease_ability/symptom/medium/voice_change,
+new /datum/disease_ability/symptom/medium/visionloss,
+new /datum/disease_ability/symptom/medium/deafness,
+new /datum/disease_ability/symptom/powerful/narcolepsy,
+new /datum/disease_ability/symptom/medium/fever,
+new /datum/disease_ability/symptom/medium/shivering,
+new /datum/disease_ability/symptom/medium/headache,
+new /datum/disease_ability/symptom/medium/nano_boost,
+new /datum/disease_ability/symptom/medium/nano_destroy,
+new /datum/disease_ability/symptom/medium/viraladaptation,
+new /datum/disease_ability/symptom/medium/viralevolution,
+new /datum/disease_ability/symptom/medium/disfiguration,
+new /datum/disease_ability/symptom/medium/polyvitiligo,
+new /datum/disease_ability/symptom/medium/itching,
+new /datum/disease_ability/symptom/medium/heal/weight_loss,
+new /datum/disease_ability/symptom/medium/heal/sensory_restoration,
+new /datum/disease_ability/symptom/medium/heal/mind_restoration,
+new /datum/disease_ability/symptom/powerful/fire,
+new /datum/disease_ability/symptom/powerful/flesh_eating,
+new /datum/disease_ability/symptom/powerful/genetic_mutation,
+new /datum/disease_ability/symptom/powerful/inorganic_adaptation,
+new /datum/disease_ability/symptom/powerful/heal/starlight,
+new /datum/disease_ability/symptom/powerful/heal/oxygen,
+new /datum/disease_ability/symptom/powerful/heal/chem,
+new /datum/disease_ability/symptom/powerful/heal/metabolism,
+new /datum/disease_ability/symptom/powerful/heal/dark,
+new /datum/disease_ability/symptom/powerful/heal/water,
+new /datum/disease_ability/symptom/powerful/heal/plasma,
+new /datum/disease_ability/symptom/powerful/heal/radiation,
+new /datum/disease_ability/symptom/powerful/heal/coma,
+new /datum/disease_ability/symptom/powerful/youth
+))
/datum/disease_ability
var/name
@@ -106,10 +106,8 @@ GLOBAL_LIST_INIT(disease_ability_singletons, list(
for(var/T in symptoms)
var/datum/symptom/S = new T()
SD.symptoms += S
- S.OnAdd(SD)
if(SD.processing)
- if(S.Start(SD))
- S.next_activation = world.time + rand(S.symptom_delay_min * 10, S.symptom_delay_max * 10)
+ S.Start(SD)
SD.Refresh()
for(var/T in actions)
var/datum/action/A = new T()
@@ -136,7 +134,6 @@ GLOBAL_LIST_INIT(disease_ability_singletons, list(
var/datum/symptom/S = locate(T) in SD.symptoms
if(S)
SD.symptoms -= S
- S.OnRemove(SD)
if(SD.processing)
S.End(SD)
qdel(S)
@@ -296,7 +293,6 @@ GLOBAL_LIST_INIT(disease_ability_singletons, list(
cost = 8
category = "Symptom (Strong+)"
-
/******MILD******/
/datum/disease_ability/symptom/mild/cough
@@ -377,11 +373,11 @@ GLOBAL_LIST_INIT(disease_ability_singletons, list(
/datum/disease_ability/symptom/medium/viralevolution
symptoms = list(/datum/symptom/viralevolution)
-/datum/disease_ability/symptom/medium/vitiligo
- symptoms = list(/datum/symptom/vitiligo)
+/datum/disease_ability/symptom/medium/polyvitiligo
+ symptoms = list(/datum/symptom/polyvitiligo)
-/datum/disease_ability/symptom/medium/revitiligo
- symptoms = list(/datum/symptom/revitiligo)
+/datum/disease_ability/symptom/medium/disfiguration
+ symptoms = list(/datum/symptom/disfiguration)
/datum/disease_ability/symptom/medium/itching
symptoms = list(/datum/symptom/itching)
@@ -409,11 +405,9 @@ GLOBAL_LIST_INIT(disease_ability_singletons, list(
/datum/disease_ability/symptom/powerful/flesh_eating
symptoms = list(/datum/symptom/flesh_eating)
-/*
/datum/disease_ability/symptom/powerful/genetic_mutation
symptoms = list(/datum/symptom/genetic_mutation)
cost = 8
-*/
/datum/disease_ability/symptom/powerful/inorganic_adaptation
symptoms = list(/datum/symptom/inorganic_adaptation)
@@ -457,4 +451,4 @@ GLOBAL_LIST_INIT(disease_ability_singletons, list(
/datum/disease_ability/symptom/powerful/heal/coma
symptoms = list(/datum/symptom/heal/coma)
short_desc = "Cause victims to fall into a healing coma when hurt."
- long_desc = "Cause victims to fall into a healing coma when hurt."
+ long_desc = "Cause victims to fall into a healing coma when hurt."
\ No newline at end of file
diff --git a/code/modules/antagonists/disease/disease_datum.dm b/code/modules/antagonists/disease/disease_datum.dm
index 1aef9ceba6..3c7de589d9 100644
--- a/code/modules/antagonists/disease/disease_datum.dm
+++ b/code/modules/antagonists/disease/disease_datum.dm
@@ -44,11 +44,17 @@
var/objectives_text = ""
var/count = 1
for(var/datum/objective/objective in objectives)
- if(objective.check_completion())
- objectives_text += " Objective #[count]: [objective.explanation_text] Success!"
+ if(objective.completable)
+ var/completion = objective.check_completion()
+ if(completion >= 1)
+ result += "Objective #[count]: [objective.explanation_text] Success!"
+ else if(completion <= 0)
+ result += "Objective #[count]: [objective.explanation_text] Fail."
+ win = FALSE
+ else
+ result += "Objective #[count]: [objective.explanation_text] [completion*100]%"
else
- objectives_text += " Objective #[count]: [objective.explanation_text] Fail."
- win = FALSE
+ result += "Objective #[count]: [objective.explanation_text]"
count++
result += objectives_text
diff --git a/code/modules/antagonists/disease/disease_mob.dm b/code/modules/antagonists/disease/disease_mob.dm
index 516f30d896..a20a9cef85 100644
--- a/code/modules/antagonists/disease/disease_mob.dm
+++ b/code/modules/antagonists/disease/disease_mob.dm
@@ -318,7 +318,11 @@ the new instance inside the host to be updated to the template's stats.
var/list/dat = list()
if(examining_ability)
- dat += "Back
[examining_ability.name]
[examining_ability.stat_block][examining_ability.long_desc][examining_ability.threshold_block]"
+ dat += "Back "
+ dat += "
[examining_ability.name]
"
+ dat += "[examining_ability.stat_block][examining_ability.long_desc][examining_ability.threshold_block]"
+ for(var/entry in examining_ability.threshold_block)
+ dat += "[entry]: [examining_ability.threshold_block[entry]] "
else
dat += "
Disease Statistics
\
Resistance: [DT.totalResistance()] \
diff --git a/code/modules/antagonists/ert/ert.dm b/code/modules/antagonists/ert/ert.dm
index 0fb41cabc8..5c878bcc55 100644
--- a/code/modules/antagonists/ert/ert.dm
+++ b/code/modules/antagonists/ert/ert.dm
@@ -12,7 +12,6 @@
var/list/name_source
show_in_antagpanel = FALSE
antag_moodlet = /datum/mood_event/focused
- can_hijack = HIJACK_PREVENT
/datum/antagonist/ert/on_gain()
update_name()
diff --git a/code/modules/antagonists/highlander/highlander.dm b/code/modules/antagonists/highlander/highlander.dm
index 49635356ac..eccf7e3c60 100644
--- a/code/modules/antagonists/highlander/highlander.dm
+++ b/code/modules/antagonists/highlander/highlander.dm
@@ -3,7 +3,7 @@
var/obj/item/claymore/highlander/sword
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
- can_hijack = HIJACK_HIJACKER
+ hijack_speed = 2 //if you kill everyone and actually haev a hand to hijack with, you win??
/datum/antagonist/highlander/apply_innate_effects(mob/living/mob_override)
var/mob/living/L = owner.current || mob_override
diff --git a/code/modules/antagonists/ninja/ninja.dm b/code/modules/antagonists/ninja/ninja.dm
index 52e13bdc69..12bdbec77a 100644
--- a/code/modules/antagonists/ninja/ninja.dm
+++ b/code/modules/antagonists/ninja/ninja.dm
@@ -8,11 +8,6 @@
var/give_objectives = TRUE
var/give_equipment = TRUE
-/datum/antagonist/ninja/New()
- if(helping_station)
- can_hijack = HIJACK_PREVENT
- . = ..()
-
/datum/antagonist/ninja/apply_innate_effects(mob/living/mob_override)
var/mob/living/M = mob_override || owner.current
update_ninja_icons_added(M)
@@ -52,6 +47,7 @@
if(2) //steal
var/datum/objective/steal/special/O = new /datum/objective/steal/special()
O.owner = owner
+ O.find_target()
objectives += O
if(3) //protect/kill
@@ -62,10 +58,10 @@
possible_targets.Cut(index,index+1)
if(is_bad_guy ^ helping_station) //kill (good-ninja + bad-guy or bad-ninja + good-guy)
- var/datum/objective/assassinate/O = new /datum/objective/assassinate()
+ var/datum/objective/assassinate/once/O = new /datum/objective/assassinate()
O.owner = owner
O.target = M
- O.explanation_text = "Slay \the [M.current.real_name], the [M.assigned_role]."
+ O.explanation_text = "Slay \the [M.current.real_name], the [M.assigned_role]. You may let [M.p_they()] live, if they come back from death."
objectives += O
else //protect
var/datum/objective/protect/O = new /datum/objective/protect()
@@ -73,23 +69,16 @@
O.target = M
O.explanation_text = "Protect \the [M.current.real_name], the [M.assigned_role], from harm."
objectives += O
- if(4) //debrain/capture
- if(!possible_targets.len) continue
- var/selected = rand(1,possible_targets.len)
- var/datum/mind/M = possible_targets[selected]
- var/is_bad_guy = possible_targets[M]
- possible_targets.Cut(selected,selected+1)
-
- if(is_bad_guy ^ helping_station) //debrain (good-ninja + bad-guy or bad-ninja + good-guy)
- var/datum/objective/debrain/O = new /datum/objective/debrain()
+ if(4) //flavor
+ if(helping_station)
+ var/datum/objective/flavor/ninja_helping/O = new /datum/objective/flavor/ninja_helping
O.owner = owner
- O.target = M
- O.explanation_text = "Steal the brain of [M.current.real_name]."
+ O.forge_objective()
objectives += O
- else //capture
- var/datum/objective/capture/O = new /datum/objective/capture()
+ else
+ var/datum/objective/flavor/ninja_syndie/O = new /datum/objective/flavor/ninja_helping
O.owner = owner
- O.gen_amount_goal()
+ O.forge_objective()
objectives += O
else
break
@@ -141,8 +130,6 @@
adj = "objectiveless"
else
return
- if(helping_station)
- can_hijack = HIJACK_PREVENT
new_owner.assigned_role = ROLE_NINJA
new_owner.special_role = ROLE_NINJA
new_owner.add_antag_datum(src)
diff --git a/code/modules/antagonists/nukeop/clownop.dm b/code/modules/antagonists/nukeop/clownop.dm
index 58b492b578..a3ccdee2b5 100644
--- a/code/modules/antagonists/nukeop/clownop.dm
+++ b/code/modules/antagonists/nukeop/clownop.dm
@@ -7,10 +7,10 @@
/datum/antagonist/nukeop/clownop/on_gain()
. = ..()
- ADD_TRAIT(owner, TRAIT_CLOWN_MENTALITY, NUKEOP_ANTAGONIST)
+ ADD_TRAIT(owner, TRAIT_CLOWN_MENTALITY, CLOWNOP_TRAIT)
/datum/antagonist/nukeop/clownop/on_removal()
- REMOVE_TRAIT(owner, TRAIT_CLOWN_MENTALITY, NUKEOP_ANTAGONIST)
+ REMOVE_TRAIT(owner, TRAIT_CLOWN_MENTALITY, CLOWNOP_TRAIT)
return ..()
/datum/antagonist/nukeop/leader/clownop
diff --git a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
index 2186e8b49e..0d815703c5 100644
--- a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
+++ b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
@@ -11,9 +11,10 @@
var/default_timer_set = 90
var/minimum_timer_set = 90
var/maximum_timer_set = 3600
- var/ui_style = "nanotrasen"
+ ui_style = "nanotrasen"
var/numeric_input = ""
+ var/ui_mode = NUKEUI_AWAIT_DISK
var/timing = FALSE
var/exploding = FALSE
var/exploded = FALSE
@@ -97,6 +98,8 @@
if(!user.transferItemToLoc(I, src))
return
auth = I
+ update_ui_mode()
+ playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE)
add_fingerprint(user)
return
@@ -233,113 +236,160 @@
var/volume = (get_time_left() <= 20 ? 30 : 5)
playsound(loc, 'sound/items/timer.ogg', volume, 0)
+/obj/machinery/nuclearbomb/proc/update_ui_mode()
+ if(exploded)
+ ui_mode = NUKEUI_EXPLODED
+ return
+
+ if(!auth)
+ ui_mode = NUKEUI_AWAIT_DISK
+ return
+
+ if(timing)
+ ui_mode = NUKEUI_TIMING
+ return
+
+ if(!safety)
+ ui_mode = NUKEUI_AWAIT_ARM
+ return
+
+ if(!yes_code)
+ ui_mode = NUKEUI_AWAIT_CODE
+ return
+
+ ui_mode = NUKEUI_AWAIT_TIMER
+
+
/obj/machinery/nuclearbomb/ui_interact(mob/user, ui_key="main", datum/tgui/ui=null, force_open=0, 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, "nuclear_bomb", name, 500, 600, master_ui, state)
+ ui = new(user, src, ui_key, "nuclear_bomb", name, 350, 442, master_ui, state)
ui.set_style(ui_style)
ui.open()
/obj/machinery/nuclearbomb/ui_data(mob/user)
var/list/data = list()
data["disk_present"] = auth
- data["code_approved"] = yes_code
- var/first_status
- if(auth)
- if(yes_code)
- first_status = timing ? "Func/Set" : "Functional"
- else
- first_status = "Auth S2."
+ var/hidden_code = (ui_mode == NUKEUI_AWAIT_CODE && numeric_input != "ERROR")
+ var/current_code = ""
+ if(hidden_code)
+ while(length(current_code) < length(numeric_input))
+ current_code = "[current_code]*"
else
- if(timing)
- first_status = "Set"
- else
- first_status = "Auth S1."
- var/second_status = exploded ? "Warhead triggered, thanks for flying Nanotrasen" : (safety ? "Safe" : "Engaged")
+ current_code = numeric_input
+ while(length(current_code) < 5)
+ current_code = "[current_code]-"
+
+ var/first_status
+ var/second_status
+ switch(ui_mode)
+ if(NUKEUI_AWAIT_DISK)
+ first_status = "DEVICE LOCKED"
+ if(timing)
+ second_status = "TIME: [get_time_left()]"
+ else
+ second_status = "AWAIT DISK"
+ if(NUKEUI_AWAIT_CODE)
+ first_status = "INPUT CODE"
+ second_status = "CODE: [current_code]"
+ if(NUKEUI_AWAIT_TIMER)
+ first_status = "INPUT TIME"
+ second_status = "TIME: [current_code]"
+ if(NUKEUI_AWAIT_ARM)
+ first_status = "DEVICE READY"
+ second_status = "TIME: [get_time_left()]"
+ if(NUKEUI_TIMING)
+ first_status = "DEVICE ARMED"
+ second_status = "TIME: [get_time_left()]"
+ if(NUKEUI_EXPLODED)
+ first_status = "DEVICE DEPLOYED"
+ second_status = "THANK YOU"
+
data["status1"] = first_status
data["status2"] = second_status
data["anchored"] = anchored
- data["safety"] = safety
- data["timing"] = timing
- data["time_left"] = get_time_left()
-
- data["timer_set"] = timer_set
- data["timer_is_not_default"] = timer_set != default_timer_set
- data["timer_is_not_min"] = timer_set != minimum_timer_set
- data["timer_is_not_max"] = timer_set != maximum_timer_set
-
- var/message = "AUTH"
- if(auth)
- message = "[numeric_input]"
- if(yes_code)
- message = "*****"
- data["message"] = message
return data
/obj/machinery/nuclearbomb/ui_act(action, params)
if(..())
return
+ playsound(src, "terminal_type", 20, FALSE)
switch(action)
if("eject_disk")
if(auth && auth.loc == src)
+ playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/nuke/general_beep.ogg', 50, FALSE)
auth.forceMove(get_turf(src))
auth = null
. = TRUE
- if("insert_disk")
- if(!auth)
+ else
var/obj/item/I = usr.is_holding_item_of_type(/obj/item/disk/nuclear)
if(I && disk_check(I) && usr.transferItemToLoc(I, src))
+ playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/nuke/general_beep.ogg', 50, FALSE)
auth = I
. = TRUE
+ update_ui_mode()
if("keypad")
if(auth)
var/digit = params["digit"]
switch(digit)
- if("R")
+ if("C")
+ if(auth && ui_mode == NUKEUI_AWAIT_ARM)
+ set_safety()
+ yes_code = FALSE
+ playsound(src, 'sound/machines/nuke/confirm_beep.ogg', 50, FALSE)
+ update_ui_mode()
+ else
+ playsound(src, 'sound/machines/nuke/general_beep.ogg', 50, FALSE)
numeric_input = ""
- yes_code = FALSE
. = TRUE
if("E")
- if(numeric_input == r_code)
- numeric_input = ""
- yes_code = TRUE
- . = TRUE
- else
- numeric_input = "ERROR"
+ switch(ui_mode)
+ if(NUKEUI_AWAIT_CODE)
+ if(numeric_input == r_code)
+ numeric_input = ""
+ yes_code = TRUE
+ playsound(src, 'sound/machines/nuke/general_beep.ogg', 50, FALSE)
+ . = TRUE
+ else
+ playsound(src, 'sound/machines/nuke/angry_beep.ogg', 50, FALSE)
+ numeric_input = "ERROR"
+ if(NUKEUI_AWAIT_TIMER)
+ var/number_value = text2num(numeric_input)
+ if(number_value)
+ timer_set = CLAMP(number_value, minimum_timer_set, maximum_timer_set)
+ playsound(src, 'sound/machines/nuke/general_beep.ogg', 50, FALSE)
+ set_safety()
+ . = TRUE
+ else
+ playsound(src, 'sound/machines/nuke/angry_beep.ogg', 50, FALSE)
+ update_ui_mode()
if("0","1","2","3","4","5","6","7","8","9")
if(numeric_input != "ERROR")
numeric_input += digit
if(length(numeric_input) > 5)
numeric_input = "ERROR"
+ else
+ playsound(src, 'sound/machines/nuke/general_beep.ogg', 50, FALSE)
. = TRUE
- if("timer")
- if(auth && yes_code)
- var/change = params["change"]
- if(change == "reset")
- timer_set = default_timer_set
- else if(change == "decrease")
- timer_set = max(minimum_timer_set, timer_set - 10)
- else if(change == "increase")
- timer_set = min(maximum_timer_set, timer_set + 10)
- else if(change == "input")
- var/user_input = input(usr, "Set time to detonation.", name) as null|num
- if(!user_input)
- return
- var/N = text2num(user_input)
- if(!N)
- return
- timer_set = CLAMP(N,minimum_timer_set,maximum_timer_set)
+ else
+ playsound(src, 'sound/machines/nuke/angry_beep.ogg', 50, FALSE)
+ if("arm")
+ if(auth && yes_code && !safety && !exploded)
+ playsound(src, 'sound/machines/nuke/confirm_beep.ogg', 50, FALSE)
+ set_active()
+ update_ui_mode()
. = TRUE
- if("safety")
- if(auth && yes_code && !exploded)
- set_safety()
+ else
+ playsound(src, 'sound/machines/nuke/angry_beep.ogg', 50, FALSE)
if("anchor")
if(auth && yes_code)
+ playsound(src, 'sound/machines/nuke/general_beep.ogg', 50, FALSE)
set_anchor()
- if("toggle_timer")
- if(auth && yes_code && !safety && !exploded)
- set_active()
+ else
+ playsound(src, 'sound/machines/nuke/angry_beep.ogg', 50, FALSE)
/obj/machinery/nuclearbomb/proc/set_anchor()
diff --git a/code/modules/antagonists/nukeop/nukeop.dm b/code/modules/antagonists/nukeop/nukeop.dm
index d343951d53..4a63ba65bd 100644
--- a/code/modules/antagonists/nukeop/nukeop.dm
+++ b/code/modules/antagonists/nukeop/nukeop.dm
@@ -8,7 +8,6 @@
var/always_new_team = FALSE //If not assigned a team by default ops will try to join existing ones, set this to TRUE to always create new team.
var/send_to_spawnpoint = TRUE //Should the user be moved to default spawnpoint.
var/nukeop_outfit = /datum/outfit/syndicate
- can_hijack = HIJACK_HIJACKER //Alternative way to wipe out the station.
/datum/antagonist/nukeop/proc/update_synd_icons_added(mob/living/M)
var/datum/atom_hud/antag/opshud = GLOB.huds[ANTAG_HUD_OPS]
diff --git a/code/modules/antagonists/official/official.dm b/code/modules/antagonists/official/official.dm
index 9165a99d42..1d340253c4 100644
--- a/code/modules/antagonists/official/official.dm
+++ b/code/modules/antagonists/official/official.dm
@@ -4,7 +4,6 @@
show_in_antagpanel = FALSE
var/datum/objective/mission
var/datum/team/ert/ert_team
- can_hijack = HIJACK_PREVENT
/datum/antagonist/official/greet()
to_chat(owner, "You are a CentCom Official.")
diff --git a/code/modules/antagonists/revenant/revenant.dm b/code/modules/antagonists/revenant/revenant.dm
index 9754d146c3..26efb92bec 100644
--- a/code/modules/antagonists/revenant/revenant.dm
+++ b/code/modules/antagonists/revenant/revenant.dm
@@ -16,7 +16,7 @@
var/icon_stun = "revenant_stun"
var/icon_drain = "revenant_draining"
var/stasis = FALSE
- mob_biotypes = list(MOB_SPIRIT)
+ mob_biotypes = MOB_SPIRIT
incorporeal_move = INCORPOREAL_MOVE_JAUNT
invisibility = INVISIBILITY_REVENANT
health = INFINITY //Revenants don't use health, they use essence instead
diff --git a/code/modules/antagonists/slaughter/slaughter.dm b/code/modules/antagonists/slaughter/slaughter.dm
index c669ff7877..8a01622c87 100644
--- a/code/modules/antagonists/slaughter/slaughter.dm
+++ b/code/modules/antagonists/slaughter/slaughter.dm
@@ -12,7 +12,7 @@
icon = 'icons/mob/mob.dmi'
icon_state = "daemon"
icon_living = "daemon"
- mob_biotypes = list(MOB_ORGANIC, MOB_HUMANOID)
+ mob_biotypes = MOB_ORGANIC|MOB_HUMANOID
speed = 1
a_intent = INTENT_HARM
stop_automated_movement = 1
@@ -100,10 +100,9 @@
if(M.mind)
M.mind.AddSpell(new /obj/effect/proc_holder/spell/bloodcrawl(null))
-/obj/item/organ/heart/demon/Remove(mob/living/carbon/M, special = 0)
- ..()
- if(M.mind)
- M.mind.RemoveSpell(/obj/effect/proc_holder/spell/bloodcrawl)
+/obj/item/organ/heart/demon/Remove(special = FALSE)
+ owner?.mind?.RemoveSpell(/obj/effect/proc_holder/spell/bloodcrawl)
+ return ..()
/obj/item/organ/heart/demon/Stop()
return 0 // Always beating.
diff --git a/code/modules/antagonists/survivalist/survivalist.dm b/code/modules/antagonists/survivalist/survivalist.dm
index 4e0184b7b6..0b66e8cb23 100644
--- a/code/modules/antagonists/survivalist/survivalist.dm
+++ b/code/modules/antagonists/survivalist/survivalist.dm
@@ -2,6 +2,7 @@
name = "Survivalist"
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
+ blacklisted_quirks = list(/datum/quirk/nonviolent) // mutes are allowed
var/greet_message = ""
/datum/antagonist/survivalist/proc/forge_objectives()
@@ -19,20 +20,8 @@
owner.announce_objectives()
/datum/antagonist/survivalist/guns
- greet_message = "Your own safety matters above all else, and the only way to ensure your safety is to stockpile weapons! Grab as many guns as possible, by any means necessary. Kill anyone who gets in your way."
-
-/datum/antagonist/survivalist/guns/forge_objectives()
- var/datum/objective/steal_five_of_type/summon_guns/guns = new
- guns.owner = owner
- objectives += guns
- ..()
+ greet_message = "Your own safety matters above all else, and the only way to ensure your safety is to stockpile weapons! Grab as many guns as possible, and don't let anyone take them!"
/datum/antagonist/survivalist/magic
name = "Amateur Magician"
- greet_message = "Grow your newfound talent! Grab as many magical artefacts as possible, by any means necessary. Kill anyone who gets in your way."
-
-/datum/antagonist/survivalist/magic/forge_objectives()
- var/datum/objective/steal_five_of_type/summon_magic/magic = new
- magic.owner = owner
- objectives += magic
- ..()
\ No newline at end of file
+ greet_message = "This magic stuff is... so powerful. You want more. More! They want your power. They can't have it! Don't let them have it!"
diff --git a/code/modules/antagonists/swarmer/swarmer.dm b/code/modules/antagonists/swarmer/swarmer.dm
index ce455d9e67..bf976e3cca 100644
--- a/code/modules/antagonists/swarmer/swarmer.dm
+++ b/code/modules/antagonists/swarmer/swarmer.dm
@@ -19,6 +19,7 @@
job_description = "Swarmer"
death = FALSE
roundstart = FALSE
+ short_desc = "You are a swarmer, a weapon of a long dead civilization."
flavour_text = {"
You are a swarmer, a weapon of a long dead civilization. Until further orders from your original masters are received, you must continue to consume and replicate.Clicking on any object will try to consume it, either deconstructing it into its components, destroying it, or integrating any materials it has into you if successful.
@@ -61,7 +62,7 @@
speak_emote = list("tones")
initial_language_holder = /datum/language_holder/swarmer
bubble_icon = "swarmer"
- mob_biotypes = list(MOB_ROBOTIC)
+ mob_biotypes = MOB_ROBOTIC
health = 40
maxHealth = 40
status_flags = CANPUSH
@@ -274,7 +275,8 @@
/obj/machinery/camera/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
S.DisIntegrate(src)
- toggle_cam(S, 0)
+ if(!QDELETED(S)) //If it got blown up no need to turn it off.
+ toggle_cam(S, 0)
return TRUE
/obj/machinery/particle_accelerator/control_box/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm
index 595ce5bb90..68963078c5 100644
--- a/code/modules/antagonists/traitor/datum_traitor.dm
+++ b/code/modules/antagonists/traitor/datum_traitor.dm
@@ -13,7 +13,7 @@
var/should_give_codewords = TRUE
var/should_equip = TRUE
var/traitor_kind = TRAITOR_HUMAN //Set on initial assignment
- can_hijack = HIJACK_HIJACKER
+ hijack_speed = 0.5 //10 seconds per hijack stage by default
/datum/antagonist/traitor/on_gain()
if(owner.current && isAI(owner.current))
@@ -60,6 +60,7 @@
message = GLOB.syndicate_code_response_regex.Replace(message, "$1")
hearing_args[HEARING_RAW_MESSAGE] = message
+// needs to be refactored to base /datum/antagonist sometime..
/datum/antagonist/traitor/proc/add_objective(datum/objective/O)
objectives += O
@@ -77,19 +78,23 @@
var/is_hijacker = FALSE
var/datum/game_mode/dynamic/mode
var/is_dynamic = FALSE
+ var/hijack_prob = 0
if(istype(SSticker.mode,/datum/game_mode/dynamic))
mode = SSticker.mode
is_dynamic = TRUE
- if(mode.storyteller.flags & NO_ASSASSIN)
- is_hijacker = FALSE
+ if(mode.threat >= CONFIG_GET(number/dynamic_hijack_cost))
+ hijack_prob = CLAMP(mode.threat_level-50,0,20)
if(GLOB.joined_player_list.len>=GLOB.dynamic_high_pop_limit)
- is_hijacker = (prob(10) && mode.threat_level > CONFIG_GET(number/dynamic_hijack_high_population_requirement))
+ is_hijacker = (prob(hijack_prob) && mode.threat_level > CONFIG_GET(number/dynamic_hijack_high_population_requirement))
else
var/indice_pop = min(10,round(GLOB.joined_player_list.len/mode.pop_per_requirement)+1)
- is_hijacker = (prob(10) && (mode.threat_level >= CONFIG_GET(number_list/dynamic_hijack_requirements)[indice_pop]))
+ is_hijacker = (prob(hijack_prob) && (mode.threat_level >= CONFIG_GET(number_list/dynamic_hijack_requirements)[indice_pop]))
+ if(mode.storyteller.flags & NO_ASSASSIN)
+ is_hijacker = FALSE
else if (GLOB.joined_player_list.len >= 30) // Less murderboning on lowpop thanks
+ hijack_prob = 10
is_hijacker = prob(10)
- var/martyr_chance = prob(20)
+ var/martyr_chance = prob(hijack_prob*2)
var/objective_count = is_hijacker //Hijacking counts towards number of objectives
if(!SSticker.mode.exchange_blue && SSticker.mode.traitors.len >= 8) //Set up an exchange if there are enough traitors
if(!SSticker.mode.exchange_red)
@@ -170,7 +175,7 @@
if(istype(SSticker.mode,/datum/game_mode/dynamic))
mode = SSticker.mode
is_dynamic = TRUE
- assassin_prob = mode.threat_level*(2/3)
+ assassin_prob = max(0,mode.threat_level-20)
if(prob(assassin_prob))
if(is_dynamic)
var/threat_spent = CONFIG_GET(number/dynamic_assassinate_cost)
@@ -187,22 +192,37 @@
maroon_objective.owner = owner
maroon_objective.find_target()
add_objective(maroon_objective)
- else
+ else if(prob(max(0,assassin_prob-20)))
var/datum/objective/assassinate/kill_objective = new
kill_objective.owner = owner
kill_objective.find_target()
add_objective(kill_objective)
+ else
+ var/datum/objective/assassinate/once/kill_objective = new
+ kill_objective.owner = owner
+ kill_objective.find_target()
+ add_objective(kill_objective)
else
if(prob(15) && !(locate(/datum/objective/download) in objectives) && !(owner.assigned_role in list("Research Director", "Scientist", "Roboticist")))
var/datum/objective/download/download_objective = new
download_objective.owner = owner
download_objective.gen_amount_goal()
add_objective(download_objective)
- else
+ else if(prob(40)) // cum. not counting download: 40%.
var/datum/objective/steal/steal_objective = new
steal_objective.owner = owner
steal_objective.find_target()
add_objective(steal_objective)
+ else if(prob(100/3)) // cum. not counting download: 20%.
+ var/datum/objective/sabotage/sabotage_objective = new
+ sabotage_objective.owner = owner
+ sabotage_objective.find_target()
+ add_objective(sabotage_objective)
+ else // cum. not counting download: 40%
+ var/datum/objective/flavor/traitor/flavor_objective = new
+ flavor_objective.owner = owner
+ flavor_objective.forge_objective()
+ add_objective(flavor_objective)
/datum/antagonist/traitor/proc/forge_single_AI_objective()
.=1
@@ -369,11 +389,17 @@
if(objectives.len)//If the traitor had no objectives, don't need to process this.
var/count = 1
for(var/datum/objective/objective in objectives)
- if(objective.check_completion())
- objectives_text += " Objective #[count]: [objective.explanation_text] Success!"
+ if(objective.completable)
+ var/completion = objective.check_completion()
+ if(completion >= 1)
+ objectives_text += " Objective #[count]: [objective.explanation_text] Success!"
+ else if(completion <= 0)
+ objectives_text += " Objective #[count]: [objective.explanation_text] Fail."
+ traitorwin = FALSE
+ else
+ objectives_text += " Objective #[count]: [objective.explanation_text] [completion*100]%"
else
- objectives_text += " Objective #[count]: [objective.explanation_text] Fail."
- traitorwin = FALSE
+ objectives_text += " Objective #[count]: [objective.explanation_text]"
count++
if(uplink_true)
diff --git a/code/modules/antagonists/traitor/equipment/Malf_Modules.dm b/code/modules/antagonists/traitor/equipment/Malf_Modules.dm
index 3cc3b48e50..e6ca95472e 100644
--- a/code/modules/antagonists/traitor/equipment/Malf_Modules.dm
+++ b/code/modules/antagonists/traitor/equipment/Malf_Modules.dm
@@ -22,11 +22,19 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list(
desc = "You aren't entirely sure what this does, but it's very beepy and boopy."
background_icon_state = "bg_tech_blue"
icon_icon = 'icons/mob/actions/actions_AI.dmi'
+ check_flags = AB_CHECK_CONSCIOUS //can't doomsday if dead.
var/mob/living/silicon/ai/owner_AI //The owner AI, so we don't have to typecast every time
var/uses //If we have multiple uses of the same power
var/auto_use_uses = TRUE //If we automatically use up uses on each activation
var/cooldown_period //If applicable, the time in deciseconds we have to wait before using any more modules
+
+/datum/action/innate/ai/New()
+ ..()
+ if(uses > 1)
+ desc = "[desc] It has [uses] use\s remaining."
+ button.desc = desc
+
/datum/action/innate/ai/Grant(mob/living/L)
. = ..()
if(!isAI(owner))
@@ -38,7 +46,7 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list(
/datum/action/innate/ai/IsAvailable()
. = ..()
if(owner_AI && owner_AI.malf_cooldown > world.time)
- return
+ return FALSE
/datum/action/innate/ai/Trigger()
. = ..()
@@ -49,12 +57,16 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list(
/datum/action/innate/ai/proc/adjust_uses(amt, silent)
uses += amt
- if(!silent && uses)
- to_chat(owner, "[name] now has [uses] use[uses > 1 ? "s" : ""] remaining.")
- if(!uses)
- if(initial(uses) > 1) //no need to tell 'em if it was one-use anyway!
- to_chat(owner, "[name] has run out of uses!")
- qdel(src)
+ if(uses)
+ if(!silent)
+ to_chat(owner, "[name] now has [uses] use[uses > 1 ? "s" : ""] remaining.")
+ desc = "[initial(desc)] It has [uses] use\s remaining."
+ UpdateButtonIcon()
+ return
+ if(initial(uses) > 1) //no need to tell 'em if it was one-use anyway!
+ to_chat(owner, "[name] has run out of uses!")
+ qdel(src)
+
//Framework for ranged abilities that can have different effects by left-clicking stuff.
/datum/action/innate/ai/ranged
@@ -74,13 +86,16 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list(
/datum/action/innate/ai/ranged/adjust_uses(amt, silent)
uses += amt
- if(!silent && uses)
- to_chat(owner, "[name] now has [uses] use[uses > 1 ? "s" : ""] remaining.")
- if(!uses)
- if(initial(uses) > 1) //no need to tell 'em if it was one-use anyway!
- to_chat(owner, "[name] has run out of uses!")
- Remove(owner)
- QDEL_IN(src, 100) //let any active timers on us finish up
+ if(uses)
+ if(!silent)
+ to_chat(owner, "[name] now has [uses] use[uses > 1 ? "s" : ""] remaining.")
+ desc = "[initial(desc)] It has [uses] use\s remaining."
+ UpdateButtonIcon()
+ return
+ if(initial(uses) > 1) //no need to tell 'em if it was one-use anyway!
+ to_chat(owner, "[name] has run out of uses!")
+ Remove(owner)
+ QDEL_IN(src, 100) //let any active timers on us finish up
/datum/action/innate/ai/ranged/Destroy()
QDEL_NULL(linked_ability)
@@ -97,7 +112,7 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list(
var/datum/action/innate/ai/ranged/attached_action
/obj/effect/proc_holder/ranged_ai/Destroy()
- QDEL_NULL(attached_action)
+ attached_action = null
return ..()
/obj/effect/proc_holder/ranged_ai/proc/toggle(mob/user)
@@ -185,6 +200,8 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list(
A.playsound_local(A, AM.unlock_sound, 50, 0)
else //Adding uses to an existing module
action.uses += initial(action.uses)
+ action.desc = "[initial(action.desc)] It has [action.uses] use\s remaining."
+ action.UpdateButtonIcon()
temp = "Additional use[action.uses > 1 ? "s" : ""] added to [action.name]!"
processing_time -= AM.cost
@@ -238,6 +255,8 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list(
return
if(alert(owner, "Send arming signal? (true = arm, false = cancel)", "purge_all_life()", "confirm = TRUE;", "confirm = FALSE;") != "confirm = TRUE;")
return
+ if (active)
+ return //prevent the AI from activating an already active doomsday
active = TRUE
set_us_up_the_bomb(owner)
@@ -245,64 +264,64 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list(
set waitfor = FALSE
to_chat(owner, "run -o -a 'selfdestruct'")
sleep(5)
- if(!owner || QDELETED(owner))
+ if(QDELETED(owner) || owner.stat == DEAD)
return
to_chat(owner, "Running executable 'selfdestruct'...")
sleep(rand(10, 30))
- if(!owner || QDELETED(owner))
+ if(QDELETED(owner) || owner.stat == DEAD)
return
owner.playsound_local(owner, 'sound/misc/bloblarm.ogg', 50, 0)
to_chat(owner, "!!! UNAUTHORIZED SELF-DESTRUCT ACCESS !!!")
to_chat(owner, "This is a class-3 security violation. This incident will be reported to Central Command.")
for(var/i in 1 to 3)
sleep(20)
- if(!owner || QDELETED(owner))
+ if(QDELETED(owner) || owner.stat == DEAD)
return
to_chat(owner, "Sending security report to Central Command.....[rand(0, 9) + (rand(20, 30) * i)]%")
sleep(3)
- if(!owner || QDELETED(owner))
+ if(QDELETED(owner) || owner.stat == DEAD)
return
to_chat(owner, "auth 'akjv9c88asdf12nb' ******************")
owner.playsound_local(owner, 'sound/items/timer.ogg', 50, 0)
sleep(30)
- if(!owner || QDELETED(owner))
+ if(QDELETED(owner) || owner.stat == DEAD)
return
to_chat(owner, "Credentials accepted. Welcome, akjv9c88asdf12nb.")
owner.playsound_local(owner, 'sound/misc/server-ready.ogg', 50, 0)
sleep(5)
- if(!owner || QDELETED(owner))
+ if(QDELETED(owner) || owner.stat == DEAD)
return
to_chat(owner, "Arm self-destruct device? (Y/N)")
owner.playsound_local(owner, 'sound/misc/compiler-stage1.ogg', 50, 0)
sleep(20)
- if(!owner || QDELETED(owner))
+ if(QDELETED(owner) || owner.stat == DEAD)
return
to_chat(owner, "Y")
sleep(15)
- if(!owner || QDELETED(owner))
+ if(QDELETED(owner) || owner.stat == DEAD)
return
to_chat(owner, "Confirm arming of self-destruct device? (Y/N)")
owner.playsound_local(owner, 'sound/misc/compiler-stage2.ogg', 50, 0)
sleep(10)
- if(!owner || QDELETED(owner))
+ if(QDELETED(owner) || owner.stat == DEAD)
return
to_chat(owner, "Y")
sleep(rand(15, 25))
- if(!owner || QDELETED(owner))
+ if(QDELETED(owner) || owner.stat == DEAD)
return
to_chat(owner, "Please repeat password to confirm.")
owner.playsound_local(owner, 'sound/misc/compiler-stage2.ogg', 50, 0)
sleep(14)
- if(!owner || QDELETED(owner))
+ if(QDELETED(owner) || owner.stat == DEAD)
return
to_chat(owner, "******************")
sleep(40)
- if(!owner || QDELETED(owner))
+ if(QDELETED(owner) || owner.stat == DEAD)
return
to_chat(owner, "Credentials accepted. Transmitting arming signal...")
owner.playsound_local(owner, 'sound/misc/server-ready.ogg', 50, 0)
sleep(30)
- if(!owner || QDELETED(owner))
+ if(QDELETED(owner) || owner.stat == DEAD)
return
priority_announce("Hostile runtimes detected in all station systems, please deactivate your AI to prevent possible damage to its morality core.", "Anomaly Alert", "aimalf")
set_security_level("delta")
@@ -724,9 +743,10 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list(
/datum/action/innate/ai/blackout
name = "Blackout"
- desc = "Overloads lights across the station."
+ desc = "Overloads random lights across the station."
button_icon_state = "blackout"
uses = 3
+ auto_use_uses = FALSE
/datum/action/innate/ai/blackout/Activate()
for(var/obj/machinery/power/apc/apc in GLOB.apcs_list)
@@ -736,6 +756,7 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list(
apc.overload++
to_chat(owner, "Overcurrent applied to the powernet.")
owner.playsound_local(owner, "sparks", 50, 0)
+ adjust_uses(-1)
//Disable Emergency Lights
@@ -784,11 +805,6 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list(
auto_use_uses = FALSE
cooldown_period = 30
-/datum/action/innate/ai/reactivate_cameras/New()
- ..()
- desc = "[desc] There are 30 reactivations remaining."
- button.desc = desc
-
/datum/action/innate/ai/reactivate_cameras/Activate()
var/fixed_cameras = 0
for(var/V in GLOB.cameranet.cameras)
@@ -803,8 +819,6 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list(
to_chat(owner, "Diagnostic complete! Cameras reactivated: [fixed_cameras]. Reactivations remaining: [uses].")
owner.playsound_local(owner, 'sound/items/wirecutter.ogg', 50, 0)
adjust_uses(0, TRUE) //Checks the uses remaining
- if(src && uses) //Not sure if not having src here would cause a runtime, so it's here to be safe
- desc = "[initial(desc)] There are [uses] reactivations remaining."
//Upgrade Camera Network: EMP-proofs all cameras, in addition to giving them X-ray vision.
diff --git a/code/modules/antagonists/valentines/valentine.dm b/code/modules/antagonists/valentines/valentine.dm
index 19c08f3076..d3bcdf0c04 100644
--- a/code/modules/antagonists/valentines/valentine.dm
+++ b/code/modules/antagonists/valentines/valentine.dm
@@ -35,7 +35,7 @@
var/objectives_complete = TRUE
if(objectives.len)
for(var/datum/objective/objective in objectives)
- if(!objective.check_completion())
+ if(objective.completable && !objective.check_completion())
objectives_complete = FALSE
break
diff --git a/code/modules/antagonists/wishgranter/wishgranter.dm b/code/modules/antagonists/wishgranter/wishgranter.dm
index 21cab26d1e..d22d1b5e39 100644
--- a/code/modules/antagonists/wishgranter/wishgranter.dm
+++ b/code/modules/antagonists/wishgranter/wishgranter.dm
@@ -2,7 +2,6 @@
name = "Wishgranter Avatar"
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
- can_hijack = HIJACK_HIJACKER
/datum/antagonist/wishgranter/proc/forge_objectives()
var/datum/objective/hijack/hijack = new
diff --git a/code/modules/antagonists/wizard/equipment/artefact.dm b/code/modules/antagonists/wizard/equipment/artefact.dm
index e6bb3f1295..492da73e66 100644
--- a/code/modules/antagonists/wizard/equipment/artefact.dm
+++ b/code/modules/antagonists/wizard/equipment/artefact.dm
@@ -246,7 +246,7 @@
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
var/mob/living/carbon/human/target = null
- var/list/mob/living/carbon/human/possible = list()
+ var/list/mob/living/carbon/human/possible
var/obj/item/voodoo_link = null
var/cooldown_time = 30 //3s
var/cooldown = 0
@@ -284,7 +284,7 @@
user.unset_machine()
/obj/item/voodoo/attack_self(mob/user)
- if(!target && possible.len)
+ if(!target && length(possible))
target = input(user, "Select your victim!", "Voodoo") as null|anything in possible
return
@@ -324,12 +324,12 @@
cooldown = world.time + cooldown_time
/obj/item/voodoo/proc/update_targets()
- LAZYINITLIST(possible)
+ possible = null
if(!voodoo_link)
return
for(var/mob/living/carbon/human/H in GLOB.alive_mob_list)
if(md5(H.dna.uni_identity) in voodoo_link.fingerprints)
- possible |= H
+ LAZYOR(possible, H)
/obj/item/voodoo/proc/GiveHint(mob/victim,force=0)
if(prob(50) || force)
diff --git a/code/modules/antagonists/wizard/wizard.dm b/code/modules/antagonists/wizard/wizard.dm
index a88eb1e42a..234bb3d1a1 100644
--- a/code/modules/antagonists/wizard/wizard.dm
+++ b/code/modules/antagonists/wizard/wizard.dm
@@ -12,7 +12,6 @@
var/move_to_lair = TRUE
var/outfit_type = /datum/outfit/wizard
var/wiz_age = WIZARD_AGE_MIN /* Wizards by nature cannot be too young. */
- can_hijack = HIJACK_HIJACKER
/datum/antagonist/wizard/on_gain()
register()
@@ -61,9 +60,9 @@
owner.current.forceMove(pick(GLOB.wizardstart))
/datum/antagonist/wizard/proc/create_objectives()
- var/datum/objective/new_objective = new("Cause as much creative mayhem as you can aboard the station! The more outlandish your methods of achieving this, the better! Make sure there's a decent amount of crew alive to tell of your tale.")
- new_objective.completed = TRUE //So they can greentext without admin intervention.
+ var/datum/objective/flavor/wizard/new_objective = new
new_objective.owner = owner
+ new_objective.forge_objective()
objectives += new_objective
if (!(locate(/datum/objective/escape) in objectives))
@@ -94,6 +93,7 @@
to_chat(owner, "You are the Space Wizard!")
to_chat(owner, "The Space Wizards Federation has given you the following tasks:")
owner.announce_objectives()
+ to_chat(owner, "These are merely guidelines! The federation are your masters, but you forge your own path!")
to_chat(owner, "You will find a list of available spells in your spell book. Choose your magic arsenal carefully.")
to_chat(owner, "The spellbook is bound to you, and others cannot use it.")
to_chat(owner, "In your pockets you will find a teleport scroll. Use it as needed.")
@@ -109,7 +109,7 @@
var/wizard_name_second = pick(GLOB.wizard_second)
var/randomname = "[wizard_name_first] [wizard_name_second]"
var/mob/living/wiz_mob = owner.current
- var/newname = copytext(sanitize(input(wiz_mob, "You are the [name]. Would you like to change your name to something else?", "Name change", randomname) as null|text),1,MAX_NAME_LEN)
+ var/newname = reject_bad_name(stripped_input(wiz_mob, "You are the [name]. Would you like to change your name to something else?", "Name change", randomname, MAX_NAME_LEN))
if (!newname)
newname = randomname
@@ -265,11 +265,17 @@
var/count = 1
var/wizardwin = 1
for(var/datum/objective/objective in objectives)
- if(objective.check_completion())
- parts += "Objective #[count]: [objective.explanation_text] Success!"
+ if(objective.completable)
+ var/completion = objective.check_completion()
+ if(completion >= 1)
+ parts += "Objective #[count]: [objective.explanation_text] Success!"
+ else if(completion <= 0)
+ parts += "Objective #[count]: [objective.explanation_text] Fail."
+ wizardwin = FALSE
+ else
+ parts += "Objective #[count]: [objective.explanation_text] [completion*100]%"
else
- parts += "Objective #[count]: [objective.explanation_text] Fail."
- wizardwin = 0
+ parts += "Objective #[count]: [objective.explanation_text]"
count++
if(wizardwin)
diff --git a/code/modules/assembly/mousetrap.dm b/code/modules/assembly/mousetrap.dm
index c67b79478e..7c8952eb33 100644
--- a/code/modules/assembly/mousetrap.dm
+++ b/code/modules/assembly/mousetrap.dm
@@ -46,7 +46,7 @@
return FALSE
switch(type)
if("feet")
- if(!H.shoes)
+ if(!H.shoes || !(H.shoes.body_parts_covered & FEET))
affecting = H.get_bodypart(pick(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG))
H.Knockdown(60)
if(BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_HAND)
diff --git a/code/modules/assembly/playback.dm b/code/modules/assembly/playback.dm
index eecb991434..42135f7ff9 100644
--- a/code/modules/assembly/playback.dm
+++ b/code/modules/assembly/playback.dm
@@ -25,12 +25,13 @@
listening = FALSE
languages = message_language
say("The recorded message is '[recorded]'.", language = message_language)
+ activate_cooldown = max(round(length(recorded) * 0.5), 3 SECONDS)
/obj/item/assembly/playback/activate()
- if(recorded == "") // Why say anything when there isn't anything to say
+ . = ..()
+ if(!. || !recorded) // Why say anything when there isn't anything to say
return FALSE
say("[recorded]", language = languages) // Repeat the message in the language it was said in
- return TRUE
/obj/item/assembly/playback/proc/record()
if(!secured || holder)
diff --git a/code/modules/assembly/signaler.dm b/code/modules/assembly/signaler.dm
index 4fba44a3f2..53cbb8ea2a 100644
--- a/code/modules/assembly/signaler.dm
+++ b/code/modules/assembly/signaler.dm
@@ -11,7 +11,6 @@
var/code = DEFAULT_SIGNALER_CODE
var/frequency = FREQ_SIGNALER
- var/delay = 0
var/datum/radio_frequency/radio_connection
var/suicider = null
var/hearing_range = 1
@@ -48,64 +47,50 @@
holder.update_icon()
return
-/obj/item/assembly/signaler/ui_interact(mob/user, flag1)
- . = ..()
- if(is_secured(user))
- var/t1 = "-------"
- var/dat = {"
-
-
-Send Signal
-Frequency/Code for signaler:
-Frequency:
-[format_frequency(src.frequency)]
-Set
-
-Code:
-[src.code]
-Set
-[t1]
-"}
- user << browse(dat, "window=radio")
- onclose(user, "radio")
+/obj/item/assembly/signaler/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
+ if(!is_secured(user))
return
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ var/ui_width = 280
+ var/ui_height = 132
+ ui = new(user, src, ui_key, "signaler", name, ui_width, ui_height, master_ui, state)
+ ui.open()
+/obj/item/assembly/signaler/ui_data(mob/user)
+ var/list/data = list()
+ data["frequency"] = frequency
+ data["code"] = code
+ data["minFrequency"] = MIN_FREE_FREQ
+ data["maxFrequency"] = MAX_FREE_FREQ
-/obj/item/assembly/signaler/Topic(href, href_list)
- ..()
+ return data
- if(!usr.canUseTopic(src, BE_CLOSE))
- usr << browse(null, "window=radio")
- onclose(usr, "radio")
+/obj/item/assembly/signaler/ui_act(action, params)
+ if(..())
return
+ switch(action)
+ if("signal")
+ INVOKE_ASYNC(src, .proc/signal)
+ . = TRUE
+ if("freq")
+ frequency = unformat_frequency(params["freq"])
+ frequency = sanitize_frequency(frequency, TRUE)
+ set_frequency(frequency)
+ . = TRUE
+ if("code")
+ code = text2num(params["code"])
+ code = round(code)
+ . = TRUE
+ if("reset")
+ if(params["reset"] == "freq")
+ frequency = initial(frequency)
+ else
+ code = initial(code)
+ . = TRUE
- if (href_list["set"])
-
- if(href_list["set"] == "freq")
- var/new_freq = input(usr, "Input a new signalling frequency", "Remote Signaller Frequency", format_frequency(frequency)) as num|null
- if(!usr.canUseTopic(src, BE_CLOSE))
- return
- new_freq = unformat_frequency(new_freq)
- new_freq = sanitize_frequency(new_freq, TRUE)
- set_frequency(new_freq)
-
- if(href_list["set"] == "code")
- var/new_code = input(usr, "Input a new signalling code", "Remote Signaller Code", code) as num|null
- if(!usr.canUseTopic(src, BE_CLOSE))
- return
- new_code = round(new_code)
- new_code = CLAMP(new_code, 1, 100)
- code = new_code
-
- if(href_list["send"])
- spawn( 0 )
- signal()
-
- if(usr)
- attack_self(usr)
-
- return
-
+ update_icon()
+
/obj/item/assembly/signaler/attackby(obj/item/W, mob/user, params)
if(issignaler(W))
var/obj/item/assembly/signaler/signaler2 = W
diff --git a/code/modules/atmospherics/gasmixtures/gas_mixture.dm b/code/modules/atmospherics/gasmixtures/gas_mixture.dm
index 12efa01a38..5ff8586c34 100644
--- a/code/modules/atmospherics/gasmixtures/gas_mixture.dm
+++ b/code/modules/atmospherics/gasmixtures/gas_mixture.dm
@@ -337,8 +337,9 @@ GLOBAL_LIST_INIT(meta_gas_fusions, meta_gas_fusion_list())
if(!length(cached_gases))
return
var/list/reactions = list()
- for(var/I in cached_gases)
- reactions += SSair.gas_reactions[I]
+ for(var/datum/gas_reaction/G in SSair.gas_reactions)
+ if(cached_gases[G.major_gas])
+ reactions += G
if(!length(reactions))
return
reaction_results = new
diff --git a/code/modules/atmospherics/gasmixtures/reactions.dm b/code/modules/atmospherics/gasmixtures/reactions.dm
index f9c831a65a..ebe894138d 100644
--- a/code/modules/atmospherics/gasmixtures/reactions.dm
+++ b/code/modules/atmospherics/gasmixtures/reactions.dm
@@ -2,8 +2,6 @@
/proc/init_gas_reactions()
. = list()
- for(var/type in subtypesof(/datum/gas))
- .[type] = list()
for(var/r in subtypesof(/datum/gas_reaction))
var/datum/gas_reaction/reaction = r
@@ -16,27 +14,19 @@
var/datum/gas/req_gas = req
if (!reaction_key || initial(reaction_key.rarity) > initial(req_gas.rarity))
reaction_key = req_gas
- .[reaction_key] += list(reaction)
- sortTim(., /proc/cmp_gas_reactions, TRUE)
+ reaction.major_gas = reaction_key
+ . += reaction
+ sortTim(., /proc/cmp_gas_reaction)
-/proc/cmp_gas_reactions(list/datum/gas_reaction/a, list/datum/gas_reaction/b) // compares lists of reactions by the maximum priority contained within the list
- if (!length(a) || !length(b))
- return length(b) - length(a)
- var/maxa
- var/maxb
- for (var/datum/gas_reaction/R in a)
- if (R.priority > maxa)
- maxa = R.priority
- for (var/datum/gas_reaction/R in b)
- if (R.priority > maxb)
- maxb = R.priority
- return maxb - maxa
+/proc/cmp_gas_reaction(datum/gas_reaction/a, datum/gas_reaction/b) // compares lists of reactions by the maximum priority contained within the list
+ return b.priority - a.priority
/datum/gas_reaction
//regarding the requirements lists: the minimum or maximum requirements must be non-zero.
//when in doubt, use MINIMUM_MOLE_COUNT.
var/list/min_requirements
var/list/max_requirements
+ var/major_gas //the highest rarity gas used in the reaction.
var/exclude = FALSE //do it this way to allow for addition/removal of reactions midmatch in the future
var/priority = 100 //lower numbers are checked/react later than higher numbers. if two reactions have the same priority they may happen in either order
var/name = "reaction"
diff --git a/code/modules/atmospherics/machinery/airalarm.dm b/code/modules/atmospherics/machinery/airalarm.dm
index e49368016b..36a128f2eb 100644
--- a/code/modules/atmospherics/machinery/airalarm.dm
+++ b/code/modules/atmospherics/machinery/airalarm.dm
@@ -207,7 +207,7 @@
pixel_y = (dir & 3)? (dir == 1 ? -24 : 24) : 0
if(name == initial(name))
- name = "[get_area_name(src)] Air Alarm"
+ name = "[get_area_name(src, get_base_area = TRUE)] Air Alarm"
power_change()
set_frequency(frequency)
@@ -229,7 +229,7 @@
. += "Alt-click to [locked ? "unlock" : "lock"] the interface."
/obj/machinery/airalarm/ui_status(mob/user)
- if(user.has_unlimited_silicon_privilege && aidisabled)
+ if(hasSiliconAccessInArea(user) && aidisabled)
to_chat(user, "AI control has been disabled.")
else if(!shorted)
return ..()
@@ -245,12 +245,12 @@
/obj/machinery/airalarm/ui_data(mob/user)
var/data = list(
"locked" = locked,
- "siliconUser" = user.has_unlimited_silicon_privilege,
+ "siliconUser" = hasSiliconAccessInArea(user),
"emagged" = (obj_flags & EMAGGED ? 1 : 0),
"danger_level" = danger_level,
)
- var/area/A = get_area(src)
+ var/area/A = get_base_area(src)
data["atmos_alarm"] = A.atmosalm
data["fire_alarm"] = A.fire
@@ -288,7 +288,7 @@
"danger_level" = cur_tlv.get_danger_level(environment.gases[gas_id] * partial_pressure)
))
- if(!locked || user.has_unlimited_silicon_privilege)
+ if(!locked || hasSiliconAccessInArea(user, PRIVILEDGES_SILICON|PRIVILEDGES_DRONE))
data["vents"] = list()
for(var/id_tag in A.air_vent_names)
var/long_name = A.air_vent_names[id_tag]
@@ -368,12 +368,14 @@
/obj/machinery/airalarm/ui_act(action, params)
if(..() || buildstage != 2)
return
- if((locked && !usr.has_unlimited_silicon_privilege) || (usr.has_unlimited_silicon_privilege && aidisabled))
+ var/silicon_access = hasSiliconAccessInArea(usr)
+ var/bot_priviledges = silicon_access || (usr.silicon_privileges & PRIVILEDGES_DRONE)
+ if((locked && !bot_priviledges) || (silicon_access && aidisabled))
return
var/device_id = params["id_tag"]
switch(action)
if("lock")
- if(usr.has_unlimited_silicon_privilege && !wires.is_cut(WIRE_IDSCAN))
+ if(bot_priviledges && !wires.is_cut(WIRE_IDSCAN))
locked = !locked
. = TRUE
if("power", "toggle_filter", "widenet", "scrubbing")
@@ -386,9 +388,10 @@
send_signal(device_id, list("checks" = text2num(params["val"])^2), usr)
. = TRUE
if("set_external_pressure", "set_internal_pressure")
- var/area/A = get_area(src)
- var/target = input("New target pressure:", name, A.air_vent_info[device_id][(action == "set_external_pressure" ? "external" : "internal")]) as num|null
- if(!isnull(target) && !..())
+
+ var/target = params["value"]
+ if(!isnull(target))
+
send_signal(device_id, list("[action]" = target), usr)
. = TRUE
if("reset_external_pressure")
@@ -420,12 +423,12 @@
apply_mode()
. = TRUE
if("alarm")
- var/area/A = get_area(src)
+ var/area/A = get_base_area(src)
if(A.atmosalert(2, src))
post_alert(2)
. = TRUE
if("reset")
- var/area/A = get_area(src)
+ var/area/A = get_base_area(src)
if(A.atmosalert(0, src))
post_alert(0)
. = TRUE
@@ -456,7 +459,7 @@
return 0
/obj/machinery/airalarm/proc/refresh_all()
- var/area/A = get_area(src)
+ var/area/A = get_base_area(src)
for(var/id_tag in A.air_vent_names)
var/list/I = A.air_vent_info[id_tag]
if(I && I["timestamp"] + AALARM_REPORT_TIMEOUT / 2 > world.time)
@@ -507,7 +510,7 @@
return "Flood"
/obj/machinery/airalarm/proc/apply_mode()
- var/area/A = get_area(src)
+ var/area/A = get_base_area(src)
switch(mode)
if(AALARM_MODE_SCRUBBING)
for(var/device_id in A.air_scrub_names)
@@ -645,7 +648,7 @@
icon_state = "alarm1"
var/overlay_state = AALARM_OVERLAY_OFF
- var/area/A = get_area(src)
+ var/area/A = get_base_area(src)
switch(max(danger_level, A.atmosalm))
if(0)
add_overlay(AALARM_OVERLAY_GREEN)
@@ -715,7 +718,7 @@
return
var/datum/signal/alert_signal = new(list(
- "zone" = get_area_name(src),
+ "zone" = get_area_name(src, get_base_area = TRUE),
"type" = "Atmospheric"
))
if(alert_level==2)
@@ -728,7 +731,7 @@
frequency.post_signal(src, alert_signal, range = -1)
/obj/machinery/airalarm/proc/apply_danger_level()
- var/area/A = get_area(src)
+ var/area/A = get_base_area(src)
var/new_area_danger_level = 0
for(var/obj/machinery/airalarm/AA in A)
@@ -839,7 +842,7 @@
/obj/machinery/airalarm/AltClick(mob/user)
. = ..()
- if(!user.canUseTopic(src, !issilicon(user)) || !isturf(loc))
+ if(!user.canUseTopic(src, !hasSiliconAccessInArea(user)) || !isturf(loc))
return
togglelock(user)
return TRUE
diff --git a/code/modules/atmospherics/machinery/components/binary_devices/relief_valve.dm b/code/modules/atmospherics/machinery/components/binary_devices/relief_valve.dm
index 5835912cd3..bc58ef158f 100644
--- a/code/modules/atmospherics/machinery/components/binary_devices/relief_valve.dm
+++ b/code/modules/atmospherics/machinery/components/binary_devices/relief_valve.dm
@@ -5,6 +5,7 @@
icon_state = "relief_valve-t-map"
can_unwrench = TRUE
construction_type = /obj/item/pipe/binary
+ interaction_flags_machine = INTERACT_MACHINE_OFFLINE | INTERACT_MACHINE_WIRES_IF_OPEN | INTERACT_MACHINE_ALLOW_SILICON | INTERACT_MACHINE_OPEN_SILICON | INTERACT_MACHINE_SET_MACHINE
var/opened = FALSE
var/open_pressure = ONE_ATMOSPHERE * 3
var/close_pressure = ONE_ATMOSPHERE
@@ -50,9 +51,11 @@
if(!is_operational())
return
- var/datum/gas_mixture/air_contents = airs[1]
- var/our_pressure = air_contents.return_pressure()
- if(opened && our_pressure < close_pressure)
+ var/datum/gas_mixture/air_one = airs[1]
+ var/datum/gas_mixture/air_two = airs[2]
+ var/air_one_pressure = air_one.return_pressure()
+ var/our_pressure = abs(air_one_pressure - air_two.return_pressure())
+ if(opened && air_one_pressure < close_pressure)
close()
else if(!opened && our_pressure >= open_pressure)
open()
diff --git a/code/modules/atmospherics/machinery/components/components_base.dm b/code/modules/atmospherics/machinery/components/components_base.dm
index ff2a655aac..86e8863b2b 100644
--- a/code/modules/atmospherics/machinery/components/components_base.dm
+++ b/code/modules/atmospherics/machinery/components/components_base.dm
@@ -30,7 +30,7 @@
underlays.Cut()
var/turf/T = loc
- if(level == 2 || !T.intact)
+ if(level == 2 || (istype(T) && !T.intact))
showpipe = TRUE
plane = GAME_PLANE
else
diff --git a/code/modules/atmospherics/machinery/components/trinary_devices/filter.dm b/code/modules/atmospherics/machinery/components/trinary_devices/filter.dm
index 2fd1bdc121..1099020662 100644
--- a/code/modules/atmospherics/machinery/components/trinary_devices/filter.dm
+++ b/code/modules/atmospherics/machinery/components/trinary_devices/filter.dm
@@ -138,7 +138,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, "atmos_filter", name, 475, 195, master_ui, state)
+ ui = new(user, src, ui_key, "atmos_filter", name, 475, 185, master_ui, state)
ui.open()
/obj/machinery/atmospherics/components/trinary/filter/ui_data()
diff --git a/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm b/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm
index 8c7d82d549..5b929452fe 100644
--- a/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm
+++ b/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm
@@ -14,32 +14,22 @@
construction_type = /obj/item/pipe/trinary/flippable
pipe_state = "mixer"
+ ui_x = 370
+ ui_y = 165
+
//node 3 is the outlet, nodes 1 & 2 are intakes
-/obj/machinery/atmospherics/components/trinary/mixer/examine(mob/user)
- . = ..()
- . += "You can hold Ctrl and click on it to toggle it on and off."
- . += "You can hold Alt and click on it to maximize its pressure."
/obj/machinery/atmospherics/components/trinary/mixer/CtrlClick(mob/user)
- var/area/A = get_area(src)
- var/turf/T = get_turf(src)
- if(user.canUseTopic(src, BE_CLOSE, FALSE,))
+ if(user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
on = !on
update_icon()
- investigate_log("Mixer, [src.name], turned on by [key_name(usr)] at [x], [y], [z], [A]", INVESTIGATE_ATMOS)
- message_admins("Mixer, [src.name], turned [on ? "on" : "off"] by [ADMIN_LOOKUPFLW(usr)] at [ADMIN_COORDJMP(T)], [A]")
- return ..()
+ return ..()
/obj/machinery/atmospherics/components/trinary/mixer/AltClick(mob/user)
- . = ..()
- var/area/A = get_area(src)
- var/turf/T = get_turf(src)
- if(user.canUseTopic(src, BE_CLOSE, FALSE,))
+ if(user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
target_pressure = MAX_OUTPUT_PRESSURE
- to_chat(user,"You maximize the pressure on the [src].")
- investigate_log("Mixer, [src.name], was maximized by [key_name(usr)] at [x], [y], [z], [A]", INVESTIGATE_ATMOS)
- message_admins("Mixer, [src.name], was maximized by [ADMIN_LOOKUPFLW(usr)] at [ADMIN_COORDJMP(T)], [A]")
- return TRUE
+ update_icon()
+ return ..()
//node 3 is the outlet, nodes 1 & 2 are intakes
@@ -64,12 +54,6 @@
var/on_state = on && nodes[1] && nodes[2] && nodes[3] && is_operational()
icon_state = "mixer_[on_state ? "on" : "off"][flipped ? "_f" : ""]"
-/obj/machinery/atmospherics/components/trinary/mixer/power_change()
- var/old_stat = stat
- ..()
- if(stat != old_stat)
- update_icon()
-
/obj/machinery/atmospherics/components/trinary/mixer/New()
..()
var/datum/gas_mixture/air3 = airs[3]
@@ -81,8 +65,13 @@
if(!on || !(nodes[1] && nodes[2] && nodes[3]) && !is_operational())
return
+ //Get those gases, mah boiiii
var/datum/gas_mixture/air1 = airs[1]
var/datum/gas_mixture/air2 = airs[2]
+
+ if(!air1 || !air2)
+ return
+
var/datum/gas_mixture/air3 = airs[3]
var/output_starting_pressure = air3.return_pressure()
@@ -92,60 +81,57 @@
return
//Calculate necessary moles to transfer using PV=nRT
+ var/general_transfer = (target_pressure - output_starting_pressure) * air3.volume / R_IDEAL_GAS_EQUATION
- var/pressure_delta = target_pressure - output_starting_pressure
- var/transfer_moles1 = 0
- var/transfer_moles2 = 0
-
- if(air1.temperature > 0)
- transfer_moles1 = (node1_concentration * pressure_delta) * air3.volume / (air1.temperature * R_IDEAL_GAS_EQUATION)
-
- if(air2.temperature > 0)
- transfer_moles2 = (node2_concentration * pressure_delta) * air3.volume / (air2.temperature * R_IDEAL_GAS_EQUATION)
+ var/transfer_moles1 = air1.temperature ? node1_concentration * general_transfer / air1.temperature : 0
+ var/transfer_moles2 = air2.temperature ? node2_concentration * general_transfer / air2.temperature : 0
var/air1_moles = air1.total_moles()
var/air2_moles = air2.total_moles()
- if((air1_moles < transfer_moles1) || (air2_moles < transfer_moles2))
- var/ratio = 0
- if((transfer_moles1 > 0 ) && (transfer_moles2 > 0))
+ if(!node2_concentration)
+ if(air1.temperature <= 0)
+ return
+ transfer_moles1 = min(transfer_moles1, air1_moles)
+ transfer_moles2 = 0
+ else if(!node1_concentration)
+ if(air2.temperature <= 0)
+ return
+ transfer_moles2 = min(transfer_moles2, air2_moles)
+ transfer_moles1 = 0
+ else
+ if(air1.temperature <= 0 || air2.temperature <= 0)
+ return
+ if((transfer_moles2 <= 0) || (transfer_moles1 <= 0))
+ return
+ if((air1_moles < transfer_moles1) || (air2_moles < transfer_moles2))
+ var/ratio = 0
ratio = min(air1_moles / transfer_moles1, air2_moles / transfer_moles2)
- if((transfer_moles2 == 0 ) && ( transfer_moles1 > 0))
- ratio = air1_moles / transfer_moles1
- if((transfer_moles1 == 0 ) && ( transfer_moles2 > 0))
- ratio = air2_moles / transfer_moles2
-
- transfer_moles1 *= ratio
- transfer_moles2 *= ratio
+ transfer_moles1 *= ratio
+ transfer_moles2 *= ratio
//Actually transfer the gas
- if(transfer_moles1 > 0)
+ if(transfer_moles1)
var/datum/gas_mixture/removed1 = air1.remove(transfer_moles1)
air3.merge(removed1)
-
- if(transfer_moles2 > 0)
- var/datum/gas_mixture/removed2 = air2.remove(transfer_moles2)
- air3.merge(removed2)
-
- if(transfer_moles1)
var/datum/pipeline/parent1 = parents[1]
parent1.update = TRUE
if(transfer_moles2)
+ var/datum/gas_mixture/removed2 = air2.remove(transfer_moles2)
+ air3.merge(removed2)
var/datum/pipeline/parent2 = parents[2]
parent2.update = TRUE
var/datum/pipeline/parent3 = parents[3]
parent3.update = TRUE
- return
-
/obj/machinery/atmospherics/components/trinary/mixer/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)
if(!ui)
- ui = new(user, src, ui_key, "atmos_mixer", name, 370, 165, master_ui, state)
+ ui = new(user, src, ui_key, "atmos_mixer", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/atmospherics/components/trinary/mixer/ui_data()
@@ -153,8 +139,8 @@
data["on"] = on
data["set_pressure"] = round(target_pressure)
data["max_pressure"] = round(MAX_OUTPUT_PRESSURE)
- data["node1_concentration"] = round(node1_concentration*100)
- data["node2_concentration"] = round(node2_concentration*100)
+ data["node1_concentration"] = round(node1_concentration*100, 1)
+ data["node2_concentration"] = round(node2_concentration*100, 1)
return data
/obj/machinery/atmospherics/components/trinary/mixer/ui_act(action, params)
@@ -182,18 +168,19 @@
investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", INVESTIGATE_ATMOS)
if("node1")
var/value = text2num(params["concentration"])
- node1_concentration = max(0, min(1, node1_concentration + value))
- node2_concentration = max(0, min(1, node2_concentration - value))
+ adjust_node1_value(value)
investigate_log("was set to [node1_concentration] % on node 1 by [key_name(usr)]", INVESTIGATE_ATMOS)
. = TRUE
if("node2")
var/value = text2num(params["concentration"])
- node2_concentration = max(0, min(1, node2_concentration + value))
- node1_concentration = max(0, min(1, node1_concentration - value))
+ adjust_node1_value(100 - value)
investigate_log("was set to [node2_concentration] % on node 2 by [key_name(usr)]", INVESTIGATE_ATMOS)
. = TRUE
update_icon()
+/obj/machinery/atmospherics/components/trinary/mixer/proc/adjust_node1_value(newValue)
+ node1_concentration = newValue / 100
+ node2_concentration = 1 - node1_concentration
/obj/machinery/atmospherics/components/trinary/mixer/can_unwrench(mob/user)
. = ..()
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm
index 539b5ce57a..1e8cce32c0 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm
@@ -251,6 +251,7 @@
/obj/machinery/atmospherics/components/unary/cryo_cell/close_machine(mob/living/carbon/user)
if((isnull(user) || istype(user)) && state_open && !panel_open)
..(user)
+ reagent_transfer = 0
return occupant
/obj/machinery/atmospherics/components/unary/cryo_cell/container_resist(mob/living/user)
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/relief_valve.dm b/code/modules/atmospherics/machinery/components/unary_devices/relief_valve.dm
index 9da9e49e01..4b6a4a4c10 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/relief_valve.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/relief_valve.dm
@@ -4,6 +4,7 @@
icon = 'icons/obj/atmospherics/components/relief_valve.dmi'
icon_state = "relief_valve-e-map"
can_unwrench = TRUE
+ interaction_flags_machine = INTERACT_MACHINE_OFFLINE | INTERACT_MACHINE_WIRES_IF_OPEN | INTERACT_MACHINE_ALLOW_SILICON | INTERACT_MACHINE_OPEN_SILICON | INTERACT_MACHINE_SET_MACHINE
var/opened = FALSE
var/open_pressure = ONE_ATMOSPHERE * 3
var/close_pressure = ONE_ATMOSPHERE
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm b/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm
index c69c4c47b0..877826c1c1 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm
@@ -10,6 +10,8 @@
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 30)
layer = OBJ_LAYER
circuit = /obj/item/circuitboard/machine/thermomachine
+ ui_x = 300
+ ui_y = 230
pipe_flags = PIPING_ONE_PER_TURF
@@ -126,7 +128,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, "thermomachine", name, 400, 240, master_ui, state)
+ ui = new(user, src, ui_key, "thermomachine", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/atmospherics/components/unary/thermomachine/ui_data(mob/user)
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm b/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm
index 3cbf1b4d0e..f2f2329661 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm
@@ -38,7 +38,7 @@
id_tag = assign_uid_vents()
/obj/machinery/atmospherics/components/unary/vent_pump/Destroy()
- var/area/A = get_area(src)
+ var/area/A = get_base_area(src)
if (A)
A.air_vent_names -= id_tag
A.air_vent_info -= id_tag
@@ -155,7 +155,7 @@
"sigtype" = "status"
))
- var/area/A = get_area(src)
+ var/area/A = get_base_area(src)
if(!A.air_vent_names[id_tag])
name = "\improper [A.name] vent pump #[A.air_vent_names.len + 1]"
A.air_vent_names[id_tag] = name
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm b/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm
index 22cd9d7fca..10eac9c717 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm
@@ -39,7 +39,7 @@
filter_types += gas_id2path(f)
/obj/machinery/atmospherics/components/unary/vent_scrubber/Destroy()
- var/area/A = get_area(src)
+ var/area/A = get_base_area(src)
if (A)
A.air_scrub_names -= id_tag
A.air_scrub_info -= id_tag
@@ -112,7 +112,7 @@
"sigtype" = "status"
))
- var/area/A = get_area(src)
+ var/area/A = get_base_area(src)
if(!A.air_scrub_names[id_tag])
name = "\improper [A.name] air scrubber #[A.air_scrub_names.len + 1]"
A.air_scrub_names[id_tag] = name
diff --git a/code/modules/atmospherics/machinery/pipes/heat_exchange/manifold.dm b/code/modules/atmospherics/machinery/pipes/heat_exchange/manifold.dm
index e8704b4c40..ce4aba6ab7 100644
--- a/code/modules/atmospherics/machinery/pipes/heat_exchange/manifold.dm
+++ b/code/modules/atmospherics/machinery/pipes/heat_exchange/manifold.dm
@@ -1,6 +1,7 @@
//3-Way Manifold
/obj/machinery/atmospherics/pipe/heat_exchanging/manifold
- icon_state = "manifold"
+ icon = 'icons/obj/atmospherics/pipes/he-manifold.dmi'
+ icon_state = "manifold-2"
name = "pipe manifold"
desc = "A manifold composed of regular pipes."
@@ -15,7 +16,7 @@
var/mutable_appearance/center
-/obj/machinery/atmospherics/pipe/manifold/Initialize()
+/obj/machinery/atmospherics/pipe/heat_exchanging/manifold/Initialize()
icon_state = ""
center = mutable_appearance(icon, "manifold_center")
return ..()
diff --git a/code/modules/atmospherics/machinery/portable/pump.dm b/code/modules/atmospherics/machinery/portable/pump.dm
index 7c517720a8..ddb907a2ad 100644
--- a/code/modules/atmospherics/machinery/portable/pump.dm
+++ b/code/modules/atmospherics/machinery/portable/pump.dm
@@ -84,14 +84,14 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "portable_pump", name, 420, 415, master_ui, state)
+ ui = new(user, src, ui_key, "portable_pump", name, 300, 315, master_ui, state)
ui.open()
/obj/machinery/portable_atmospherics/pump/ui_data()
var/data = list()
data["on"] = on
- data["direction"] = direction
- data["connected"] = connected_port ? 1 : 0
+ data["direction"] = direction == PUMP_IN ? TRUE : FALSE
+ data["connected"] = connected_port ? TRUE : FALSE
data["pressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0)
data["target_pressure"] = round(pump.target_pressure ? pump.target_pressure : 0)
data["default_pressure"] = round(PUMP_DEFAULT_PRESSURE)
@@ -102,6 +102,8 @@
data["holding"] = list()
data["holding"]["name"] = holding.name
data["holding"]["pressure"] = round(holding.air_contents.return_pressure())
+ else
+ data["holding"] = null
return data
/obj/machinery/portable_atmospherics/pump/ui_act(action, params)
diff --git a/code/modules/atmospherics/machinery/portable/scrubber.dm b/code/modules/atmospherics/machinery/portable/scrubber.dm
index d52f2d8356..0c902e0426 100644
--- a/code/modules/atmospherics/machinery/portable/scrubber.dm
+++ b/code/modules/atmospherics/machinery/portable/scrubber.dm
@@ -67,7 +67,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "portable_scrubber", name, 420, 435, master_ui, state)
+ ui = new(user, src, ui_key, "portable_scrubber", name, 320, 335, master_ui, state)
ui.open()
/obj/machinery/portable_atmospherics/scrubber/ui_data()
@@ -85,6 +85,8 @@
data["holding"] = list()
data["holding"]["name"] = holding.name
data["holding"]["pressure"] = round(holding.air_contents.return_pressure())
+ else
+ data["holding"] = null
return data
/obj/machinery/portable_atmospherics/scrubber/ui_act(action, params)
diff --git a/code/modules/awaymissions/bluespaceartillery.dm b/code/modules/awaymissions/bluespaceartillery.dm
index deb05920e2..ee4090b680 100644
--- a/code/modules/awaymissions/bluespaceartillery.dm
+++ b/code/modules/awaymissions/bluespaceartillery.dm
@@ -44,7 +44,7 @@
return
if(reload < reload_cooldown)
return
- if(usr.contents.Find(src) || (in_range(src, usr) && isturf(loc)) || issilicon(usr))
+ if(usr.contents.Find(src) || (in_range(src, usr) && isturf(loc)) || hasSiliconAccessInArea(usr))
priority_announce("Bluespace artillery fire detected. Brace for impact.")
message_admins("[ADMIN_LOOKUPFLW(usr)] has launched an artillery strike.")
var/list/L = list()
diff --git a/code/modules/awaymissions/capture_the_flag.dm b/code/modules/awaymissions/capture_the_flag.dm
index bd4e716357..c4bb6e8cdd 100644
--- a/code/modules/awaymissions/capture_the_flag.dm
+++ b/code/modules/awaymissions/capture_the_flag.dm
@@ -7,8 +7,6 @@
#define AMMO_DROP_LIFETIME 300
#define CTF_REQUIRED_PLAYERS 4
-
-
/obj/item/twohanded/ctf
name = "banner"
icon = 'icons/obj/items_and_weapons.dmi'
@@ -210,7 +208,6 @@
toggle_all_ctf(user)
return
-
people_who_want_to_play |= user.ckey
var/num = people_who_want_to_play.len
var/remaining = CTF_REQUIRED_PLAYERS - num
@@ -267,6 +264,8 @@
M.key = new_team_member.key
M.faction += team
M.equipOutfit(ctf_gear)
+ M.dna.species.punchdamagehigh = 25
+ M.dna.species.punchdamagelow = 25
spawned_mobs += M
/obj/machinery/capture_the_flag/Topic(href, href_list)
@@ -371,6 +370,10 @@
CTF.ctf_gear = initial(ctf_gear)
CTF.respawn_cooldown = DEFAULT_RESPAWN
+/proc/ctf_floor_vanish(atom/target)
+ if(isturf(target.loc))
+ qdel(target)
+
/obj/item/gun/ballistic/automatic/pistol/deagle/ctf
desc = "This looks like it could really hurt in melee."
force = 75
@@ -378,11 +381,7 @@
/obj/item/gun/ballistic/automatic/pistol/deagle/ctf/dropped()
. = ..()
- addtimer(CALLBACK(src, .proc/floor_vanish), 1)
-
-/obj/item/gun/ballistic/automatic/pistol/deagle/ctf/proc/floor_vanish()
- if(isturf(loc))
- qdel(src)
+ addtimer(CALLBACK(GLOBAL_PROC, /proc/ctf_floor_vanish, src), 1)
/obj/item/ammo_box/magazine/m50/ctf
ammo_type = /obj/item/ammo_casing/a50/ctf
@@ -405,22 +404,14 @@
/obj/item/gun/ballistic/automatic/laser/ctf/dropped()
. = ..()
- addtimer(CALLBACK(src, .proc/floor_vanish), 1)
-
-/obj/item/gun/ballistic/automatic/laser/ctf/proc/floor_vanish()
- if(isturf(loc))
- qdel(src)
+ addtimer(CALLBACK(GLOBAL_PROC, /proc/ctf_floor_vanish, src), 1)
/obj/item/ammo_box/magazine/recharge/ctf
ammo_type = /obj/item/ammo_casing/caseless/laser/ctf
/obj/item/ammo_box/magazine/recharge/ctf/dropped()
. = ..()
- addtimer(CALLBACK(src, .proc/floor_vanish), 1)
-
-/obj/item/ammo_box/magazine/recharge/ctf/proc/floor_vanish()
- if(isturf(loc))
- qdel(src)
+ addtimer(CALLBACK(GLOBAL_PROC, /proc/ctf_floor_vanish, src), 1)
/obj/item/ammo_casing/caseless/laser/ctf
projectile_type = /obj/item/projectile/beam/ctf
@@ -438,9 +429,9 @@
. = FALSE
if(istype(target, /obj/structure/barricade/security/ctf))
. = TRUE
- if(ishuman(target))
- var/mob/living/carbon/human/H = target
- if(istype(H.wear_suit, /obj/item/clothing/suit/space/hardsuit/shielded/ctf))
+ if(isliving(target))
+ var/mob/living/H = target
+ if((RED_TEAM in H.faction) || (BLUE_TEAM in H.faction))
. = TRUE
// RED TEAM GUNS
@@ -473,6 +464,21 @@
icon_state = "bluelaser"
impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser
+// MELEE GANG
+/obj/item/claymore/ctf
+ slot_flags = ITEM_SLOT_BACK
+ armour_penetration = 100
+ total_mass = 1
+
+/obj/item/claymore/ctf/pre_attack(atom/target, mob/user, params)
+ if(!is_ctf_target(target))
+ return TRUE
+ return ..()
+
+/obj/item/claymore/ctf/dropped()
+ . = ..()
+ addtimer(CALLBACK(GLOBAL_PROC, /proc/ctf_floor_vanish, src), 1)
+
/datum/outfit/ctf
name = "CTF"
ears = /obj/item/radio/headset
@@ -486,6 +492,7 @@
l_pocket = /obj/item/ammo_box/magazine/recharge/ctf
r_pocket = /obj/item/ammo_box/magazine/recharge/ctf
r_hand = /obj/item/gun/ballistic/automatic/laser/ctf
+ back = /obj/item/claymore/ctf
/datum/outfit/ctf/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE, client/preference_source)
if(visualsOnly)
diff --git a/code/modules/awaymissions/corpse.dm b/code/modules/awaymissions/corpse.dm
index 34886c5b27..7421b9e09a 100644
--- a/code/modules/awaymissions/corpse.dm
+++ b/code/modules/awaymissions/corpse.dm
@@ -13,7 +13,9 @@
var/death = TRUE //Kill the mob
var/roundstart = TRUE //fires on initialize
var/instant = FALSE //fires on New
- var/flavour_text = "The mapper forgot to set this!"
+ var/short_desc = "The mapper forgot to set this!"
+ var/flavour_text = ""
+ var/important_info = ""
var/faction = null
var/permanent = FALSE //If true, the spawner will not disappear upon running out of uses.
var/random = FALSE //Don't set a name or gender, just go random
@@ -110,7 +112,12 @@
if(ckey)
M.ckey = ckey
if(show_flavour)
- to_chat(M, "[flavour_text]")
+ var/output_message = "[short_desc]"
+ if(flavour_text != "")
+ output_message += "\n[flavour_text]"
+ if(important_info != "")
+ output_message += "\n[important_info]"
+ to_chat(M, output_message)
var/datum/mind/MM = M.mind
var/datum/antagonist/A
if(antagonist_type)
@@ -340,7 +347,7 @@
name = "sleeper"
icon = 'icons/obj/machines/sleeper.dmi'
icon_state = "sleeper"
- flavour_text = "You are a space doctor!"
+ short_desc = "You are a space doctor!"
assignedrole = "Space Doctor"
job_description = "Off-station Doctor"
@@ -397,7 +404,8 @@
name = "bartender sleeper"
icon = 'icons/obj/machines/sleeper.dmi'
icon_state = "sleeper"
- flavour_text = "You are a space bartender! Time to mix drinks and change lives. Smoking space drugs makes it easier to understand your patrons' odd dialect."
+ short_desc = "You are a space bartender!"
+ flavour_text = "Time to mix drinks and change lives. Smoking space drugs makes it easier to understand your patrons' odd dialect."
assignedrole = "Space Bartender"
id_job = "Bartender"
@@ -422,7 +430,8 @@
name = "beach bum sleeper"
icon = 'icons/obj/machines/sleeper.dmi'
icon_state = "sleeper"
- flavour_text = "You're, like, totally a dudebro, bruh. Ch'yea. You came here, like, on spring break, hopin' to pick up some bangin' hot chicks, y'knaw?"
+ short_desc = "You're a spunky lifeguard!"
+ flavour_text = "It's up to you to make sure nobody drowns or gets eaten by sharks and stuff."
assignedrole = "Beach Bum"
/obj/effect/mob_spawn/human/beach/alive/lifeguard
@@ -511,7 +520,7 @@
name = "sleeper"
icon = 'icons/obj/machines/sleeper.dmi'
icon_state = "sleeper"
- flavour_text = "You are a Nanotrasen Commander!"
+ short_desc = "You are a Nanotrasen Commander!"
/obj/effect/mob_spawn/human/nanotrasensoldier/alive
death = FALSE
@@ -522,7 +531,7 @@
icon = 'icons/obj/machines/sleeper.dmi'
icon_state = "sleeper"
faction = "nanotrasenprivate"
- flavour_text = "You are a Nanotrasen Private Security Officer!"
+ short_desc = "You are a Nanotrasen Private Security Officer!"
/////////////////Spooky Undead//////////////////////
@@ -539,7 +548,8 @@
job_description = "Skeleton"
icon = 'icons/effects/blood.dmi'
icon_state = "remains"
- flavour_text = "By unknown powers, your skeletal remains have been reanimated! Walk this mortal plain and terrorize all living adventurers who dare cross your path."
+ short_desc = "By unknown powers, your skeletal remains have been reanimated!"
+ flavour_text = "Walk this mortal plain and terrorize all living adventurers who dare cross your path."
assignedrole = "Skeleton"
/obj/effect/mob_spawn/human/zombie
@@ -554,7 +564,9 @@
job_description = "Zombie"
icon = 'icons/effects/blood.dmi'
icon_state = "remains"
- flavour_text = "By unknown powers, your rotting remains have been resurrected! Walk this mortal plain and terrorize all living adventurers who dare cross your path."
+ short_desc = "By unknown powers, your rotting remains have been resurrected!"
+ flavour_text = "Walk this mortal plain and terrorize all living adventurers who dare cross your path."
+
/obj/effect/mob_spawn/human/abductor
diff --git a/code/modules/awaymissions/mission_code/snowdin.dm b/code/modules/awaymissions/mission_code/snowdin.dm
index 85ac20ca0d..fc797d227c 100644
--- a/code/modules/awaymissions/mission_code/snowdin.dm
+++ b/code/modules/awaymissions/mission_code/snowdin.dm
@@ -595,8 +595,9 @@
job_description = "Syndicate Snow Operative"
faction = ROLE_SYNDICATE
outfit = /datum/outfit/snowsyndie
- flavour_text = "You are a syndicate operative recently awoken from cryostasis in an underground outpost. Monitor Nanotrasen communications and record information. All intruders should be \
- disposed of swiftly to assure no gathered information is stolen or lost. Try not to wander too far from the outpost as the caves can be a deadly place even for a trained operative such as yourself."
+ short_desc = "You are a syndicate operative recently awoken from cryostasis in an underground outpost."
+ flavour_text = "You are a syndicate operative recently awoken from cryostasis in an underground outpost. Monitor Nanotrasen communications and record information. All intruders should be \
+ disposed of swiftly to assure no gathered information is stolen or lost. Try not to wander too far from the outpost as the caves can be a deadly place even for a trained operative such as yourself."
/datum/outfit/snowsyndie
name = "Syndicate Snow Operative"
diff --git a/code/modules/awaymissions/zlevel.dm b/code/modules/awaymissions/zlevel.dm
index 744fb78ba5..d4c9fdbee7 100644
--- a/code/modules/awaymissions/zlevel.dm
+++ b/code/modules/awaymissions/zlevel.dm
@@ -43,7 +43,7 @@ GLOBAL_LIST_INIT(potentialRandomZlevels, generateMapList(filename = "[global.con
t = trim(t)
if (length(t) == 0)
continue
- else if (copytext(t, 1, 2) == "#")
+ else if (t[1] == "#")
continue
var/pos = findtext(t, " ")
diff --git a/code/modules/cargo/bounties/reagent.dm b/code/modules/cargo/bounties/reagent.dm
index ca76c80df7..391b4ff464 100644
--- a/code/modules/cargo/bounties/reagent.dm
+++ b/code/modules/cargo/bounties/reagent.dm
@@ -105,6 +105,7 @@ datum/bounty/reagent/complex_drink/New()
/datum/reagent/consumable/ethanol/hearty_punch,\
/datum/reagent/consumable/ethanol/manhattan_proj,\
/datum/reagent/consumable/ethanol/narsour,\
+ /datum/reagent/consumable/ethanol/cogchamp,\
/datum/reagent/consumable/ethanol/neurotoxin,\
/datum/reagent/consumable/ethanol/patron,\
/datum/reagent/consumable/ethanol/quadruple_sec,\
diff --git a/code/modules/cargo/console.dm b/code/modules/cargo/console.dm
index 526062d0f7..64b208a90b 100644
--- a/code/modules/cargo/console.dm
+++ b/code/modules/cargo/console.dm
@@ -3,13 +3,20 @@
desc = "Used to order supplies, approve requests, and control the shuttle."
icon_screen = "supply"
circuit = /obj/item/circuitboard/computer/cargo
- req_access = list(ACCESS_CARGO)
+ ui_x = 780
+ ui_y = 750
+
var/requestonly = FALSE
var/contraband = FALSE
+ var/self_paid = FALSE
var/safety_warning = "For safety reasons, the automated supply shuttle \
- cannot transport live organisms, human remains, classified nuclear weaponry \
- or homing beacons."
+ cannot transport live organisms, human remains, classified nuclear weaponry, \
+ homing beacons or machinery housing any form of artificial intelligence."
var/blockade_warning = "Bluespace instability detected. Shuttle movement impossible."
+ /// radio used by the console to send messages on supply channel
+ var/obj/item/radio/headset/radio
+ /// var that tracks message cooldown
+ var/message_cooldown
light_color = "#E2853D"//orange
@@ -18,11 +25,11 @@
desc = "Used to request supplies from cargo."
icon_screen = "request"
circuit = /obj/item/circuitboard/computer/cargo/request
- req_access = list()
requestonly = TRUE
/obj/machinery/computer/cargo/Initialize()
. = ..()
+ radio = new /obj/item/radio/headset/headset_cargo(src)
var/obj/item/circuitboard/computer/cargo/board = circuit
contraband = board.contraband
if (board.obj_flags & EMAGGED)
@@ -30,6 +37,10 @@
else
obj_flags &= ~EMAGGED
+/obj/machinery/computer/cargo/Destroy()
+ QDEL_NULL(radio)
+ ..()
+
/obj/machinery/computer/cargo/proc/get_export_categories()
. = EXPORT_CARGO
if(contraband)
@@ -38,11 +49,11 @@
. |= EXPORT_EMAG
/obj/machinery/computer/cargo/emag_act(mob/user)
- . = ..()
if(obj_flags & EMAGGED)
return
- user.visible_message("[user] swipes a suspicious card through [src]!",
- "You adjust [src]'s routing and receiver spectrum, unlocking special supplies and contraband.")
+ if(user)
+ user.visible_message("[user] swipes a suspicious card through [src]!",
+ "You adjust [src]'s routing and receiver spectrum, unlocking special supplies and contraband.")
obj_flags |= EMAGGED
contraband = TRUE
@@ -51,23 +62,21 @@
var/obj/item/circuitboard/computer/cargo/board = circuit
board.contraband = TRUE
board.obj_flags |= EMAGGED
- req_access = list()
- return TRUE
+ update_static_data(user)
/obj/machinery/computer/cargo/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)
if(!ui)
- ui = new(user, src, ui_key, "cargo", name, 1000, 800, master_ui, state)
+ ui = new(user, src, ui_key, "cargo", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/computer/cargo/ui_data()
var/list/data = list()
- data["requestonly"] = requestonly
data["location"] = SSshuttle.supply.getStatusText()
- data["points"] = SSshuttle.points
data["away"] = SSshuttle.supply.getDockedId() == "supply_away"
data["docked"] = SSshuttle.supply.mode == SHUTTLE_IDLE
+ data["points"] = SSshuttle.points
data["loan"] = !!SSshuttle.shuttle_loan
data["loan_dispatched"] = SSshuttle.shuttle_loan && SSshuttle.shuttle_loan.dispatched
var/message = "Remember to stamp and send back the supply manifests."
@@ -76,29 +85,13 @@
if(SSshuttle.supplyBlocked)
message = blockade_warning
data["message"] = message
- data["supplies"] = list()
- for(var/pack in SSshuttle.supply_packs)
- var/datum/supply_pack/P = SSshuttle.supply_packs[pack]
- if(!data["supplies"][P.group])
- data["supplies"][P.group] = list(
- "name" = P.group,
- "packs" = list()
- )
- if((P.hidden && !(obj_flags & EMAGGED)) || (P.contraband && !contraband) || (P.special && !P.special_enabled) || P.DropPodOnly)
- continue
- data["supplies"][P.group]["packs"] += list(list(
- "name" = P.name,
- "cost" = P.cost,
- "id" = pack,
- "desc" = P.desc || P.name // If there is a description, use it. Otherwise use the pack's name.
- ))
-
data["cart"] = list()
for(var/datum/supply_order/SO in SSshuttle.shoppinglist)
data["cart"] += list(list(
"object" = SO.pack.name,
"cost" = SO.pack.cost,
- "id" = SO.id
+ "id" = SO.id,
+ "orderer" = SO.orderer,
))
data["requests"] = list()
@@ -113,14 +106,31 @@
return data
+/obj/machinery/computer/cargo/ui_static_data(mob/user)
+ var/list/data = list()
+ data["requestonly"] = requestonly
+ data["supplies"] = list()
+ for(var/pack in SSshuttle.supply_packs)
+ var/datum/supply_pack/P = SSshuttle.supply_packs[pack]
+ if(!data["supplies"][P.group])
+ data["supplies"][P.group] = list(
+ "name" = P.group,
+ "packs" = list()
+ )
+ if((P.hidden && !(obj_flags & EMAGGED)) || (P.contraband && !contraband) || (P.special && !P.special_enabled) || P.DropPodOnly)
+ continue
+ data["supplies"][P.group]["packs"] += list(list(
+ "name" = P.name,
+ "cost" = P.cost,
+ "id" = pack,
+ "desc" = P.desc || P.name, // If there is a description, use it. Otherwise use the pack's name.
+ "access" = P.access
+ ))
+ return data
+
/obj/machinery/computer/cargo/ui_act(action, params, datum/tgui/ui)
if(..())
return
- if(!allowed(usr))
- to_chat(usr, "Access denied.")
- return
- if(action != "add" && requestonly)
- return
switch(action)
if("send")
if(!SSshuttle.supply.canMove())
diff --git a/code/modules/cargo/export_scanner.dm b/code/modules/cargo/export_scanner.dm
index 9c523c194f..80fd05cf8c 100644
--- a/code/modules/cargo/export_scanner.dm
+++ b/code/modules/cargo/export_scanner.dm
@@ -36,6 +36,8 @@
var/price = 0
for(var/x in ex.total_amount)
price += ex.total_value[x]
+ for(var/x in ex.reagents_value)
+ price += ex.reagents_value[x]
if(price)
to_chat(user, "Scanned [O], value: [price] credits[O.contents.len ? " (contents included)" : ""].")
diff --git a/code/modules/cargo/exports.dm b/code/modules/cargo/exports.dm
index fca10ac01c..bb90fa717a 100644
--- a/code/modules/cargo/exports.dm
+++ b/code/modules/cargo/exports.dm
@@ -49,13 +49,13 @@ Credit dupes that require a lot of manual work shouldn't be removed, unless they
sold = E.sell_object(thing, report, dry_run, allowed_categories , apply_elastic)
report.exported_atoms += " [thing.name]"
break
- if(thing.reagents)
+ if(thing.reagents?.value_multiplier)
for(var/A in thing.reagents.reagent_list)
var/datum/reagent/R = A
if(!R.value)
continue
report.reagents_volume[R.name] += R.volume
- report.reagents_value[R.name] += R.volume * R.value
+ report.reagents_value[R.name] += round(R.volume * R.value * thing.reagents.value_multiplier)
if(!dry_run && (sold || delete_unsold))
if(ismob(thing))
thing.investigate_log("deleted through cargo export",INVESTIGATE_CARGO)
diff --git a/code/modules/cargo/exports/gear.dm b/code/modules/cargo/exports/gear.dm
index 77b57466d9..d15fc18b6e 100644
--- a/code/modules/cargo/exports/gear.dm
+++ b/code/modules/cargo/exports/gear.dm
@@ -229,12 +229,12 @@
/datum/export/gear/magboots
cost = 50
unit_name = "magboots"
- export_types = list(/obj/item/clothing/shoes/magboots, /obj/item/clothing/shoes/magboots/atmos)
+ export_types = list(/obj/item/clothing/shoes/magboots)
/datum/export/gear/nosellboots
cost = -5000 //We DONT want scew antags
unit_name = "error shipment stolen"
- export_types = list(/obj/item/clothing/shoes/magboots/advance, /obj/item/clothing/shoes/magboots/deathsquad)
+ export_types = list(/obj/item/clothing/shoes/magboots/advance)
/datum/export/gear/syndamagboots
cost = 250
@@ -279,7 +279,7 @@
/datum/export/gear/magicboots //Magic as in Antag - Wiz/Cults
cost = 450
unit_name = "magic shoes"
- export_types = list(/obj/item/clothing/shoes/sandal/marisa, /obj/item/clothing/shoes/sandal/magic, /obj/item/clothing/shoes/cult, /obj/item/clothing/shoes/clockwork, /obj/item/clothing/shoes/clown_shoes/taeclowndo, /obj/item/clothing/shoes/sandal/slippers)
+ export_types = list(/obj/item/clothing/shoes/sandal/marisa, /obj/item/clothing/shoes/sandal/magic, /obj/item/clothing/shoes/cult, /obj/item/clothing/shoes/clockwork, /obj/item/clothing/shoes/clown_shoes/taeclowndo)
include_subtypes = TRUE
//Headsets/Ears
@@ -585,8 +585,7 @@ datum/export/gear/glasses //glasses are not worth selling
export_types = list(/obj/item/clothing/under/scratch, /obj/item/clothing/under/sl_suit, /obj/item/clothing/under/rank/vice, /obj/item/clothing/under/suit_jacket, \
/obj/item/clothing/under/burial, /obj/item/clothing/under/skirt/black, /obj/item/clothing/under/captainparade, /obj/item/clothing/under/hosparademale, \
/obj/item/clothing/under/hosparadefem, /obj/item/clothing/under/assistantformal, /obj/item/clothing/under/stripeddress, /obj/item/clothing/under/redeveninggown, \
- /obj/item/clothing/under/plaid_skirt, /obj/item/clothing/under/geisha, /obj/item/clothing/under/trek, /obj/item/clothing/under/wedding, /obj/item/clothing/under/aviatoruniform,\
- /obj/item/clothing/under/mega, /obj/item/clothing/under/cia, /obj/item/clothing/under/casualwear, /obj/item/clothing/under/rank)
+ /obj/item/clothing/under/plaid_skirt, /obj/item/clothing/under/geisha, /obj/item/clothing/under/trek, /obj/item/clothing/under/rank)
include_subtypes = TRUE
/datum/export/gear/armored_jumpsuit
diff --git a/code/modules/cargo/exports/large_objects.dm b/code/modules/cargo/exports/large_objects.dm
index b8541dcd33..b7bdcb1f59 100644
--- a/code/modules/cargo/exports/large_objects.dm
+++ b/code/modules/cargo/exports/large_objects.dm
@@ -6,7 +6,7 @@
k_elasticity = 0
unit_name = "crate"
export_types = list(/obj/structure/closet/crate)
- exclude_types = list(/obj/structure/closet/crate/large, /obj/structure/closet/crate/wooden)
+ exclude_types = list(/obj/structure/closet/crate/large, /obj/structure/closet/crate/wooden, /obj/structure/closet/crate/bin)
/datum/export/large/crate/total_printout(datum/export_report/ex, notes = TRUE) // That's why a goddamn metal crate costs that much.
. = ..()
@@ -24,13 +24,13 @@
export_types = list(/obj/structure/ore_box)
/datum/export/large/crate/wood
- cost = 140 //
+ cost = 140
unit_name = "wooden crate"
export_types = list(/obj/structure/closet/crate/wooden)
exclude_types = list()
/datum/export/large/barrel
- cost = 500 //150 to make meaning proffit of 350
+ cost = 300 //double the wooden cost of a coffin.
unit_name = "wooden barrel"
export_types = list(/obj/structure/fermenting_barrel)
@@ -40,19 +40,11 @@
export_types = list(/obj/structure/closet/crate/coffin)
/datum/export/large/reagent_dispenser
- cost = 100 // +0-400 depending on amount of reagents left
- var/contents_cost = 400
-
-/datum/export/large/reagent_dispenser/get_cost(obj/O)
- var/obj/structure/reagent_dispensers/D = O
- var/ratio = D.reagents.total_volume / D.reagents.maximum_volume
-
- return ..() + round(contents_cost * ratio)
+ cost = 100
/datum/export/large/reagent_dispenser/water
unit_name = "watertank"
export_types = list(/obj/structure/reagent_dispensers/watertank)
- contents_cost = 200
/datum/export/large/reagent_dispenser/fuel
unit_name = "fueltank"
@@ -60,7 +52,6 @@
/datum/export/large/reagent_dispenser/beer
unit_name = "beer keg"
- contents_cost = 700
export_types = list(/obj/structure/reagent_dispensers/beerkeg)
/datum/export/large/pipedispenser
diff --git a/code/modules/cargo/exports/materials.dm b/code/modules/cargo/exports/materials.dm
index d8fc5f22ab..1b38a809cf 100644
--- a/code/modules/cargo/exports/materials.dm
+++ b/code/modules/cargo/exports/materials.dm
@@ -65,11 +65,6 @@
material_id = MAT_TITANIUM
message = "cm3 of titanium"
-/datum/export/material/plastitanium
- cost = 165 // plasma + titanium costs
- material_id = MAT_TITANIUM // code can only check for one material_id; plastitanium is half plasma, half titanium
- message = "cm3 of plastitanium"
-
/datum/export/material/plastic
cost = 5
material_id = MAT_PLASTIC
diff --git a/code/modules/cargo/exports/sheets.dm b/code/modules/cargo/exports/sheets.dm
index f784083097..df1704d660 100644
--- a/code/modules/cargo/exports/sheets.dm
+++ b/code/modules/cargo/exports/sheets.dm
@@ -67,6 +67,16 @@
message = "of plasteel"
export_types = list(/obj/item/stack/sheet/plasteel)
+/datum/export/material/plastitanium
+ cost = 165 // plasma + titanium costs
+ export_types = list(/obj/item/stack/sheet/mineral/plastitanium)
+ message = "of plastitanium"
+
+/datum/export/material/plastitanium_glass
+ cost = 168 // plasma + titanium + glass costs
+ export_types = list(/obj/item/stack/sheet/plastitaniumglass)
+ message = "of plastitanium glass"
+
// 1 glass + 0.5 metal, cost is rounded up.
/datum/export/stack/rglass
cost = 6
diff --git a/code/modules/cargo/exports/weapons.dm b/code/modules/cargo/exports/weapons.dm
index 18a525b8dd..c75a1c0bff 100644
--- a/code/modules/cargo/exports/weapons.dm
+++ b/code/modules/cargo/exports/weapons.dm
@@ -199,6 +199,31 @@
unit_name = "advanced shotgun shell"
export_types = list(/obj/item/ammo_casing/shotgun/dragonsbreath, /obj/item/ammo_casing/shotgun/meteorslug, /obj/item/ammo_casing/shotgun/pulseslug, /obj/item/ammo_casing/shotgun/frag12, /obj/item/ammo_casing/shotgun/ion, /obj/item/ammo_casing/shotgun/laserslug)
+/////////////////////////
+//Bow and Arrows/////////
+/////////////////////////
+
+/datum/export/weapon/bows
+ cost = 450
+ unit_name = "bow"
+ export_types = list(/obj/item/gun/ballistic/bow)
+
+/datum/export/weapon/arrows
+ cost = 150
+ unit_name = "arrow"
+ export_types = list(/obj/item/ammo_casing/caseless/arrow, /obj/item/ammo_casing/caseless/arrow/bone, /obj/item/ammo_casing/caseless/arrow/ashen)
+
+/datum/export/weapon/bow_teaching
+ cost = 500
+ unit_name = "stone tablets"
+ export_types = list(/obj/item/book/granter/crafting_recipe/bone_bow)
+
+/datum/export/weapon/quiver
+ cost = 100
+ unit_name = "quiver"
+ export_types = list(/obj/item/storage/belt/quiver)
+
+
/////////////////////////
//The Traitor Sell Outs//
/////////////////////////
@@ -342,7 +367,7 @@
/datum/export/weapon/real_toolbox
cost = 600
unit_name = "golden toolbox"
- export_types = list(/obj/item/storage/toolbox/gold_real)
+ export_types = list(/obj/item/storage/toolbox/plastitanium/gold_real)
/datum/export/weapon/melee
cost = 50
diff --git a/code/modules/cargo/expressconsole.dm b/code/modules/cargo/expressconsole.dm
index a4ede0cad2..a65d8cad40 100644
--- a/code/modules/cargo/expressconsole.dm
+++ b/code/modules/cargo/expressconsole.dm
@@ -90,14 +90,14 @@
/obj/machinery/computer/cargo/express/ui_interact(mob/living/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) // Remember to use the appropriate state.
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "cargo_express", name, 1000, 800, master_ui, state)
+ ui = new(user, src, ui_key, "cargo_express", name, 600, 700, master_ui, state)
ui.open()
/obj/machinery/computer/cargo/express/ui_data(mob/user)
var/canBeacon = beacon && (isturf(beacon.loc) || ismob(beacon.loc))//is the beacon in a valid location?
var/list/data = list()
data["locked"] = locked//swipe an ID to unlock
- data["siliconUser"] = user.has_unlimited_silicon_privilege
+ data["siliconUser"] = hasSiliconAccessInArea(user)
data["beaconzone"] = beacon ? get_area(beacon) : ""//where is the beacon located? outputs in the tgui
data["usingBeacon"] = usingBeacon //is the mode set to deliver to the beacon or the cargobay?
data["canBeacon"] = !usingBeacon || canBeacon //is the mode set to beacon delivery, and is the beacon in a valid location?
@@ -183,6 +183,7 @@
LZ = pick(empty_turfs)
if (SO.pack.cost <= SSshuttle.points && LZ)//we need to call the cost check again because of the CHECK_TICK call
SSshuttle.points -= SO.pack.cost
+ SSblackbox.record_feedback("nested tally", "cargo_imports", 1, list("[SO.pack.cost]", "[SO.pack.name]"))
new /obj/effect/abstract/DPtarget(LZ, podType, SO)
. = TRUE
update_icon()
@@ -196,6 +197,7 @@
CHECK_TICK
if(empty_turfs && empty_turfs.len)
SSshuttle.points -= SO.pack.cost * (0.72*MAX_EMAG_ROCKETS)
+ SSblackbox.record_feedback("nested tally", "cargo_imports", MAX_EMAG_ROCKETS, list("[SO.pack.cost * 0.72]", "[SO.pack.name]"))
SO.generateRequisition(get_turf(src))
for(var/i in 1 to MAX_EMAG_ROCKETS)
var/LZ = pick(empty_turfs)
diff --git a/code/modules/cargo/packs.dm b/code/modules/cargo/packs.dm
index 4c9ddfe3bc..07a75bbfaf 100644
--- a/code/modules/cargo/packs.dm
+++ b/code/modules/cargo/packs.dm
@@ -2,7 +2,7 @@
var/name = "Crate"
var/group = ""
var/hidden = FALSE //Aka emag only
- var/contraband = FALSE //Hacking the console with a multitool
+ var/contraband = FALSE //Requires a hacked console UNLESS DropPodOnly = TRUE, in which case it requires an emag
var/cost = 700 // Minimum cost, or infinite points are possible.
var/access = FALSE //What access does the Crate itself need?
var/access_any = FALSE //Do we care about access?
diff --git a/code/modules/cargo/packs/armory.dm b/code/modules/cargo/packs/armory.dm
index 372824c8e4..fc95b05007 100644
--- a/code/modules/cargo/packs/armory.dm
+++ b/code/modules/cargo/packs/armory.dm
@@ -178,7 +178,7 @@
/datum/supply_pack/security/armory/russian
name = "Russian Surplus Crate"
desc = "Hello Comrade, we have the most modern russian military equipment the black market can offer, for the right price of course. Sadly we couldnt remove the lock so it requires Armory access to open."
- cost = 5000
+ cost = 7500
contraband = TRUE
contains = list(/obj/item/reagent_containers/food/snacks/rationpack,
/obj/item/ammo_box/a762,
@@ -192,31 +192,14 @@
/obj/item/clothing/mask/russian_balaclava,
/obj/item/clothing/head/helmet/rus_ushanka,
/obj/item/clothing/suit/armor/vest/russian_coat,
- /obj/item/gun/ballistic/shotgun/boltaction,
/obj/item/gun/ballistic/shotgun/boltaction)
crate_name = "surplus military crate"
/datum/supply_pack/security/armory/russian/fill(obj/structure/closet/crate/C)
- for(var/i in 1 to 10)
+ for(var/i in 1 to 5)
var/item = pick(contains)
new item(C)
-/datum/supply_pack/security/armory/spinfusor
- name = "Stormhammer Spinfusor Crate"
- cost = 14000
- desc = "Got yourself a code red? Blob, nukies or even worst knocking on your door? Well with the Stormhammer Spinfusor you can stop crime in one shot, dont miss! Contains two Stormhammer Spinfusors (Note, guns may or may not be loaded). Requires Armory access to open."
- contains = list(/obj/item/gun/ballistic/automatic/spinfusor,
- /obj/item/gun/ballistic/automatic/spinfusor)
- crate_name = "spinfusor crate"
-
-/datum/supply_pack/security/armory/spinfusorammo
- name = "Spinfusor Disk Crate"
- cost = 7000
- desc = "Need more ammo for a Stormhammer? Well we got some for a price! Contains two boxes of Spinfusor disks. Requires Armory access to open."
- contains = list(/obj/item/ammo_box/aspinfusor,
- /obj/item/ammo_box/aspinfusor)
- crate_name = "spinfusor disk crate"
-
/datum/supply_pack/security/armory/swat
name = "SWAT Crate"
desc = "Contains two fullbody sets of tough, fireproof, pressurized suits designed in a joint effort by IS-ERI and Nanotrasen. Each set contains a suit, helmet, mask, combat belt, and combat gloves. Requires Armory access to open."
@@ -234,7 +217,7 @@
crate_name = "swat crate"
/datum/supply_pack/security/armory/swattasers //Lesser AEG tbh
- name = "SWAT tatical tasers Crate"
+ name = "SWAT tactical tasers Crate"
desc = "Contains two tactical energy gun, these guns are able to tase, disable and lethal as well as hold a seclight. Requires Armory access to open."
cost = 7000
contains = list(/obj/item/gun/energy/e_gun/stun,
diff --git a/code/modules/cargo/packs/costumes_toys.dm b/code/modules/cargo/packs/costumes_toys.dm
index f41a43070d..c559e7925a 100644
--- a/code/modules/cargo/packs/costumes_toys.dm
+++ b/code/modules/cargo/packs/costumes_toys.dm
@@ -3,7 +3,7 @@
// If you add something to this list, please group it by type and sort it alphabetically instead of just jamming it in like an animal
// cost = 700- Minimum cost, or infinite points are possible.
//////////////////////////////////////////////////////////////////////////////
-//////////////////////////// Costumes & Toys /////////////////////////////////
+////////////////////////////////// Toys //////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
/datum/supply_pack/costumes_toys
@@ -63,6 +63,7 @@
/obj/item/toy/cards/deck/syndicate,
/obj/item/reagent_containers/food/drinks/bottle/absinthe,
/obj/item/clothing/under/syndicate/tacticool,
+ /obj/item/clothing/under/syndicate/skirt,
/obj/item/clothing/under/syndicate,
/obj/item/suppressor,
/obj/item/storage/fancy/cigarettes/cigpack_syndicate,
@@ -98,40 +99,6 @@
/obj/item/ammo_box/magazine/toy/pistol)
crate_name = "foam force crate"
-/datum/supply_pack/costumes_toys/formalwear
- name = "Formalwear Crate"
- desc = "You're gonna like the way you look, I guaranteed it. Contains an asston of fancy clothing."
- cost = 4750 //Lots of fancy clothing that can be sold back!
- contains = list(/obj/item/clothing/under/blacktango,
- /obj/item/clothing/under/assistantformal,
- /obj/item/clothing/under/assistantformal,
- /obj/item/clothing/under/lawyer/bluesuit,
- /obj/item/clothing/suit/toggle/lawyer,
- /obj/item/clothing/under/lawyer/purpsuit,
- /obj/item/clothing/suit/toggle/lawyer/purple,
- /obj/item/clothing/under/lawyer/blacksuit,
- /obj/item/clothing/suit/toggle/lawyer/black,
- /obj/item/clothing/accessory/waistcoat,
- /obj/item/clothing/neck/tie/blue,
- /obj/item/clothing/neck/tie/red,
- /obj/item/clothing/neck/tie/black,
- /obj/item/clothing/head/bowler,
- /obj/item/clothing/head/fedora,
- /obj/item/clothing/head/flatcap,
- /obj/item/clothing/head/beret,
- /obj/item/clothing/head/that,
- /obj/item/clothing/shoes/laceup,
- /obj/item/clothing/shoes/laceup,
- /obj/item/clothing/shoes/laceup,
- /obj/item/clothing/under/suit_jacket/charcoal,
- /obj/item/clothing/under/suit_jacket/navy,
- /obj/item/clothing/under/suit_jacket/burgundy,
- /obj/item/clothing/under/suit_jacket/checkered,
- /obj/item/clothing/under/suit_jacket/tan,
- /obj/item/lipstick/random)
- crate_name = "formalwear crate"
- crate_type = /obj/structure/closet/crate/wooden
-
/datum/supply_pack/costumes_toys/clownpin
name = "Hilarious Firing Pin Crate"
desc = "I uh... I'm not really sure what this does. Wanna buy it?"
@@ -173,47 +140,6 @@
contains = list(/obj/item/storage/box/lasertagpins)
crate_name = "laser tag crate"
-/datum/supply_pack/costumes_toys/costume_original
- name = "Original Costume Crate"
- desc = "Reenact Shakespearean plays with this assortment of outfits. Contains eight different costumes!"
- cost = 1750
- contains = list(/obj/item/clothing/head/snowman,
- /obj/item/clothing/suit/snowman,
- /obj/item/clothing/head/chicken,
- /obj/item/clothing/suit/chickensuit,
- /obj/item/clothing/mask/gas/monkeymask,
- /obj/item/clothing/suit/monkeysuit,
- /obj/item/clothing/head/cardborg,
- /obj/item/clothing/suit/cardborg,
- /obj/item/clothing/head/xenos,
- /obj/item/clothing/suit/xenos,
- /obj/item/clothing/suit/hooded/ian_costume,
- /obj/item/clothing/suit/hooded/carp_costume,
- /obj/item/clothing/suit/hooded/bee_costume)
- crate_name = "original costume crate"
- crate_type = /obj/structure/closet/crate/wooden
-
-/datum/supply_pack/costumes_toys/costume
- name = "Standard Costume Crate"
- desc = "Supply the station's entertainers with the equipment of their trade with these Nanotrasen-approved costumes! Contains a full clown and mime outfit, along with a bike horn and a bottle of nothing."
- cost = 1300
- access = ACCESS_THEATRE
- contains = list(/obj/item/storage/backpack/clown,
- /obj/item/clothing/shoes/clown_shoes,
- /obj/item/clothing/mask/gas/clown_hat,
- /obj/item/clothing/under/rank/clown,
- /obj/item/bikehorn,
- /obj/item/clothing/under/rank/mime,
- /obj/item/clothing/shoes/sneakers/black,
- /obj/item/clothing/gloves/color/white,
- /obj/item/clothing/mask/gas/mime,
- /obj/item/clothing/head/beret,
- /obj/item/clothing/suit/suspenders,
- /obj/item/reagent_containers/food/drinks/bottle/bottleofnothing,
- /obj/item/storage/backpack/mime)
- crate_name = "standard costume crate"
- crate_type = /obj/structure/closet/crate/wooden
-
/datum/supply_pack/costumes_toys/randomised/toys
name = "Toy Crate"
desc = "Who cares about pride and accomplishment? Skip the gaming and get straight to the sweet rewards with this product! Contains five random toys. Warranty void if used to prank research directors."
@@ -284,6 +210,86 @@
crate_name = "plushie crate"
crate_type = /obj/structure/closet/crate/wooden
+
+//////////////////////////////////////////////////////////////////////////////
+///////////////////////////////// Costumes //////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+/datum/supply_pack/costumes_toys/formalwear
+ name = "Formalwear Crate"
+ desc = "You're gonna like the way you look, I guaranteed it. Contains an asston of fancy clothing."
+ cost = 4750 //Lots of fancy clothing that can be sold back!
+ contains = list(/obj/item/clothing/under/blacktango,
+ /obj/item/clothing/under/assistantformal,
+ /obj/item/clothing/under/assistantformal,
+ /obj/item/clothing/under/lawyer/bluesuit,
+ /obj/item/clothing/suit/toggle/lawyer,
+ /obj/item/clothing/under/lawyer/purpsuit,
+ /obj/item/clothing/suit/toggle/lawyer/purple,
+ /obj/item/clothing/under/lawyer/blacksuit,
+ /obj/item/clothing/suit/toggle/lawyer/black,
+ /obj/item/clothing/accessory/waistcoat,
+ /obj/item/clothing/neck/tie/blue,
+ /obj/item/clothing/neck/tie/red,
+ /obj/item/clothing/neck/tie/black,
+ /obj/item/clothing/head/bowler,
+ /obj/item/clothing/head/fedora,
+ /obj/item/clothing/head/flatcap,
+ /obj/item/clothing/head/beret,
+ /obj/item/clothing/head/that,
+ /obj/item/clothing/shoes/laceup,
+ /obj/item/clothing/shoes/laceup,
+ /obj/item/clothing/shoes/laceup,
+ /obj/item/clothing/under/suit_jacket/charcoal,
+ /obj/item/clothing/under/suit_jacket/navy,
+ /obj/item/clothing/under/suit_jacket/burgundy,
+ /obj/item/clothing/under/suit_jacket/checkered,
+ /obj/item/clothing/under/suit_jacket/tan,
+ /obj/item/lipstick/random)
+ crate_name = "formalwear crate"
+ crate_type = /obj/structure/closet/crate/wooden
+
+/datum/supply_pack/costumes_toys/costume_original
+ name = "Original Costume Crate"
+ desc = "Reenact Shakespearean plays with this assortment of outfits. Contains eight different costumes!"
+ cost = 1750
+ contains = list(/obj/item/clothing/head/snowman,
+ /obj/item/clothing/suit/snowman,
+ /obj/item/clothing/head/chicken,
+ /obj/item/clothing/suit/chickensuit,
+ /obj/item/clothing/mask/gas/monkeymask,
+ /obj/item/clothing/suit/monkeysuit,
+ /obj/item/clothing/head/cardborg,
+ /obj/item/clothing/suit/cardborg,
+ /obj/item/clothing/head/xenos,
+ /obj/item/clothing/suit/xenos,
+ /obj/item/clothing/suit/hooded/ian_costume,
+ /obj/item/clothing/suit/hooded/carp_costume,
+ /obj/item/clothing/suit/hooded/bee_costume)
+ crate_name = "original costume crate"
+ crate_type = /obj/structure/closet/crate/wooden
+
+/datum/supply_pack/costumes_toys/costume
+ name = "Standard Costume Crate"
+ desc = "Supply the station's entertainers with the equipment of their trade with these Nanotrasen-approved costumes! Contains a full clown and mime outfit, along with a bike horn and a bottle of nothing."
+ cost = 1300
+ access = ACCESS_THEATRE
+ contains = list(/obj/item/storage/backpack/clown,
+ /obj/item/clothing/shoes/clown_shoes,
+ /obj/item/clothing/mask/gas/clown_hat,
+ /obj/item/clothing/under/rank/clown,
+ /obj/item/bikehorn,
+ /obj/item/clothing/under/rank/mime,
+ /obj/item/clothing/shoes/sneakers/black,
+ /obj/item/clothing/gloves/color/white,
+ /obj/item/clothing/mask/gas/mime,
+ /obj/item/clothing/head/beret,
+ /obj/item/clothing/suit/suspenders,
+ /obj/item/reagent_containers/food/drinks/bottle/bottleofnothing,
+ /obj/item/storage/backpack/mime)
+ crate_name = "standard costume crate"
+ crate_type = /obj/structure/closet/crate/wooden
+
/datum/supply_pack/costumes_toys/wizard
name = "Wizard Costume Crate"
desc = "Pretend to join the Wizard Federation with this full wizard outfit! Nanotrasen would like to remind its employees that actually joining the Wizard Federation is subject to termination of job and life."
@@ -294,76 +300,3 @@
/obj/item/clothing/head/wizard/fake)
crate_name = "wizard costume crate"
crate_type = /obj/structure/closet/crate/wooden
-
-/datum/supply_pack/costumes_toys/wardrobes/autodrobe
- name = "Autodrobe Supply Crate"
- desc = "Autodrobe missing your favorite dress? Solve that issue today with this autodrobe refill."
- cost = 1500
- contains = list(/obj/item/vending_refill/autodrobe)
- crate_name = "autodrobe supply crate"
-
-/datum/supply_pack/costumes_toys/wardrobes/cargo
- name = "Cargo Wardrobe Supply Crate"
- desc = "This crate contains a refill for the CargoDrobe."
- cost = 750
- contains = list(/obj/item/vending_refill/wardrobe/cargo_wardrobe)
- crate_name = "cargo department supply crate"
-
-/datum/supply_pack/costumes_toys/wardrobes/engineering
- name = "Engineering Wardrobe Supply Crate"
- desc = "This crate contains refills for the EngiDrobe and AtmosDrobe."
- cost = 1500
- contains = list(/obj/item/vending_refill/wardrobe/engi_wardrobe,
- /obj/item/vending_refill/wardrobe/atmos_wardrobe)
- crate_name = "engineering department wardrobe supply crate"
-
-/datum/supply_pack/costumes_toys/wardrobes/general
- name = "General Wardrobes Supply Crate"
- desc = "This crate contains refills for the CuraDrobe, BarDrobe, ChefDrobe, JaniDrobe, ChapDrobe."
- cost = 3750
- contains = list(/obj/item/vending_refill/wardrobe/curator_wardrobe,
- /obj/item/vending_refill/wardrobe/bar_wardrobe,
- /obj/item/vending_refill/wardrobe/chef_wardrobe,
- /obj/item/vending_refill/wardrobe/jani_wardrobe,
- /obj/item/vending_refill/wardrobe/chap_wardrobe)
- crate_name = "general wardrobes vendor refills"
-
-/datum/supply_pack/costumes_toys/wardrobes/hydroponics
- name = "Hydrobe Supply Crate"
- desc = "This crate contains a refill for the Hydrobe."
- cost = 750
- contains = list(/obj/item/vending_refill/wardrobe/hydro_wardrobe)
- crate_name = "hydrobe supply crate"
-
-/datum/supply_pack/costumes_toys/wardrobes/medical
- name = "Medical Wardrobe Supply Crate"
- desc = "This crate contains refills for the MediDrobe, ChemDrobe, GeneDrobe, and ViroDrobe."
- cost = 3000
- contains = list(/obj/item/vending_refill/wardrobe/medi_wardrobe,
- /obj/item/vending_refill/wardrobe/chem_wardrobe,
- /obj/item/vending_refill/wardrobe/gene_wardrobe,
- /obj/item/vending_refill/wardrobe/viro_wardrobe)
- crate_name = "medical department wardrobe supply crate"
-
-/datum/supply_pack/costumes_toys/wardrobes/science
- name = "Science Wardrobe Supply Crate"
- desc = "This crate contains refills for the SciDrobe and RoboDrobe."
- cost = 1500
- contains = list(/obj/item/vending_refill/wardrobe/robo_wardrobe,
- /obj/item/vending_refill/wardrobe/science_wardrobe)
- crate_name = "science department wardrobe supply crate"
-
-/datum/supply_pack/costumes_toys/wardrobes/security
- name = "Security Wardrobe Supply Crate"
- desc = "This crate contains refills for the SecDrobe and LawDrobe."
- cost = 1500
- contains = list(/obj/item/vending_refill/wardrobe/sec_wardrobe,
- /obj/item/vending_refill/wardrobe/law_wardrobe)
- crate_name = "security department supply crate"
-
-/datum/supply_pack/costumes_toys/kinkmate
- name = "Kinkmate construction kit"
- cost = 2000
- contraband = TRUE
- contains = list(/obj/item/vending_refill/kink, /obj/item/circuitboard/machine/kinkmate)
- crate_name = "Kinkmate construction kit"
diff --git a/code/modules/cargo/packs/emergency.dm b/code/modules/cargo/packs/emergency.dm
index 747e820f52..be81da3b92 100644
--- a/code/modules/cargo/packs/emergency.dm
+++ b/code/modules/cargo/packs/emergency.dm
@@ -11,11 +11,13 @@
/datum/supply_pack/emergency/vehicle
name = "Biker Gang Kit" //TUNNEL SNAKES OWN THIS TOWN
- desc = "TUNNEL SNAKES OWN THIS TOWN. Contains an unbranded All Terrain Vehicle, and a complete gang outfit -- consists of black gloves, a menacing skull bandanna, and a SWEET leather overcoat!"
+ desc = "TUNNEL SNAKES OWN THIS TOWN. Contains an unbranded All Terrain Vehicle, two cans of spraypaint, and a complete gang outfit -- consists of black gloves, a menacing skull bandanna, and a SWEET leather overcoat!"
cost = 2500
contraband = TRUE
contains = list(/obj/vehicle/ridden/atv,
/obj/item/key,
+ /obj/item/toy/crayon/spraycan,
+ /obj/item/toy/crayon/spraycan,
/obj/item/clothing/suit/jacket/leather/overcoat,
/obj/item/clothing/gloves/color/black,
/obj/item/clothing/head/soft,
@@ -126,24 +128,9 @@
crate_name = "emergency rcds"
crate_type = /obj/structure/closet/crate/internals
-/datum/supply_pack/emergency/soft_suit
- name = "Emergency Space Suit"
- desc = "Are there bombs going off left and right? Are there meteors shooting around the station? Well then! Here's two fragile space suits for emergencies. Comes with air and masks."
- cost = 1200
- contains = list(/obj/item/tank/internals/air,
- /obj/item/tank/internals/air,
- /obj/item/clothing/mask/gas,
- /obj/item/clothing/mask/gas,
- /obj/item/clothing/suit/space/fragile,
- /obj/item/clothing/suit/space/fragile,
- /obj/item/clothing/head/helmet/space/fragile,
- /obj/item/clothing/head/helmet/space/fragile)
- crate_name = "emergency crate"
- crate_type = /obj/structure/closet/crate/internals
-
/datum/supply_pack/emergency/bomb
name = "Explosive Emergency Crate"
- desc = "Science gone bonkers? Beeping behind the airlock? Buy now and be the hero the station des... I mean needs! (Time not included.)"
+ desc = "Science gone bonkers? Beeping behind the airlock? Buy now and become the hero the station des... I mean needs! Time not included, but a full bomb suit and hood, as well as a mask and defusal kit are! Non-Nuclear ordnances only."
cost = 1500
contains = list(/obj/item/clothing/head/bomb_hood,
/obj/item/clothing/suit/bomb_suit,
@@ -208,7 +195,7 @@
crate_name = "metal foam grenade crate"
/datum/supply_pack/emergency/mre
- name = "MRE supply kit (emergency rations)"
+ name = "MRE Packs (Emergency Rations)"
desc = "The lights are out. Oxygen's running low. You've run out of food except space weevils. Don't let this be you! Order our NT branded MRE kits today! This pack contains 5 MRE packs with a randomized menu and an oxygen tank."
cost = 2000
contains = list(/obj/item/storage/box/mre/menu1/safe,
@@ -296,6 +283,21 @@
crate_name = "space suit crate"
crate_type = /obj/structure/closet/crate/secure
+/datum/supply_pack/emergency/soft_suit
+ name = "Space Suits (Fragile)"
+ desc = "Are there bombs going off left and right? Are there meteors shooting around the station? Well then! Here's two fragile space suits for emergencies. Comes with air and masks."
+ cost = 1200
+ contains = list(/obj/item/tank/internals/air,
+ /obj/item/tank/internals/air,
+ /obj/item/clothing/mask/gas,
+ /obj/item/clothing/mask/gas,
+ /obj/item/clothing/suit/space/fragile,
+ /obj/item/clothing/suit/space/fragile,
+ /obj/item/clothing/head/helmet/space/fragile,
+ /obj/item/clothing/head/helmet/space/fragile)
+ crate_name = "emergency crate"
+ crate_type = /obj/structure/closet/crate/internals
+
/datum/supply_pack/emergency/spacejets
name = "Spare EVA Jetpacks"
desc = "Contains three EVA grade jectpaks. Requires EVA access to open."
diff --git a/code/modules/cargo/packs/engine.dm b/code/modules/cargo/packs/engine.dm
index 499881a110..27891f3ab6 100644
--- a/code/modules/cargo/packs/engine.dm
+++ b/code/modules/cargo/packs/engine.dm
@@ -149,6 +149,14 @@
crate_type = /obj/structure/closet/crate/secure/engineering
dangerous = TRUE
+/datum/supply_pack/engine/supermatter_spray
+ name = "Supermatter Spray Crate"
+ desc = "The single thing that can truly heal the supermatter."
+ cost = 2000
+ contains = list(/obj/item/supermatterspray)
+ crate_name = "supermatter shard crate"
+ crate_type = /obj/structure/closet/crate/engineering/electrical
+
/datum/supply_pack/engine/tesla_coils
name = "Tesla Coil Crate"
desc = "Whether it's high-voltage executions, creating research points, or just plain old power generation: This pack of four Tesla coils can do it all!"
diff --git a/code/modules/cargo/packs/engineering.dm b/code/modules/cargo/packs/engineering.dm
index 6492df5215..22258d19a7 100644
--- a/code/modules/cargo/packs/engineering.dm
+++ b/code/modules/cargo/packs/engineering.dm
@@ -3,7 +3,7 @@
// If you add something to this list, please group it by type and sort it alphabetically instead of just jamming it in like an animal
// cost = 700- Minimum cost, or infinite points are possible.
//////////////////////////////////////////////////////////////////////////////
-//////////////////////////// Engineering /////////////////////////////////////
+///////////////////////////// Engineering ////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
/datum/supply_pack/engineering
@@ -45,6 +45,7 @@
/obj/item/clothing/glasses/meson/engine,
/obj/item/clothing/glasses/meson/engine)
crate_name = "engineering gear crate"
+ crate_type = /obj/structure/closet/crate/secure/engineering
/datum/supply_pack/engineering/engihardsuit
name = "Engineering Hardsuit"
@@ -55,6 +56,7 @@
/obj/item/clothing/mask/gas,
/obj/item/clothing/suit/space/hardsuit/engine)
crate_name = "engineering hardsuit"
+ crate_type = /obj/structure/closet/crate/secure/engineering
/datum/supply_pack/engineering/atmoshardsuit
name = "Atmospherics Hardsuit"
@@ -114,6 +116,22 @@
crate_name = "PACMAN generator crate"
crate_type = /obj/structure/closet/crate/engineering/electrical
+/datum/supply_pack/engineering/airpump
+ name = "Portable Air Pump Crate"
+ desc = "We all know you work in a high pressure workplace. Keep it that way with two additional air pumps!"
+ cost = 3000
+ contains = list(/obj/machinery/portable_atmospherics/pump,
+ /obj/machinery/portable_atmospherics/pump)
+ crate_name = "portable air pump crate"
+
+/datum/supply_pack/engineering/airscrubber
+ name = "Portable Scrubber Crate"
+ desc = "Miasma got you down? Plasma in the vents? Freshen up with these two brand-new air scrubbers!"
+ cost = 3000
+ contains = list(/obj/machinery/portable_atmospherics/scrubber,
+ /obj/machinery/portable_atmospherics/scrubber)
+ crate_name = "portable scrubber crate"
+
/datum/supply_pack/engineering/power
name = "Power Cell Crate"
desc = "Looking for power overwhelming? Look no further. Contains three high-voltage power cells."
diff --git a/code/modules/cargo/packs/medical.dm b/code/modules/cargo/packs/medical.dm
index d4fcdab962..5d02bbe60f 100644
--- a/code/modules/cargo/packs/medical.dm
+++ b/code/modules/cargo/packs/medical.dm
@@ -10,6 +10,10 @@
group = "Medical"
crate_type = /obj/structure/closet/crate/medical
+//////////////////////////////////////////////////////////////////////////////
+/////////////////////////////// Equipment ////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
/datum/supply_pack/medical/bodybags
name = "Bodybags"
desc = "For when the bodies hit the floor. Contains 4 boxes of bodybags."
@@ -20,24 +24,6 @@
/obj/item/storage/box/bodybags,)
crate_name = "bodybag crate"
-/datum/supply_pack/medical/firstaidbruises
- name = "Bruise Treatment Kit Crate"
- desc = "Contains three first aid kits focused on healing bruises and broken bones."
- cost = 1000
- contains = list(/obj/item/storage/firstaid/brute,
- /obj/item/storage/firstaid/brute,
- /obj/item/storage/firstaid/brute)
- crate_name = "brute treatment kit crate"
-
-/datum/supply_pack/medical/firstaidburns
- name = "Burn Treatment Kit Crate"
- desc = "Contains three first aid kits focused on healing severe burns."
- cost = 1000
- contains = list(/obj/item/storage/firstaid/fire,
- /obj/item/storage/firstaid/fire,
- /obj/item/storage/firstaid/fire)
- crate_name = "burn treatment kit crate"
-
/datum/supply_pack/medical/bloodpacks
name = "Blood Pack Variety Crate"
desc = "Contains nine different blood packs for reintroducing blood to patients, plus two universal synthetic blood packs."
@@ -86,16 +72,6 @@
/obj/item/defibrillator/loaded)
crate_name = "defibrillator crate"
-/datum/supply_pack/medical/firstaid
- name = "First Aid Kit Crate"
- desc = "Contains four first aid kits for healing most types of wounds."
- cost = 1000
- contains = list(/obj/item/storage/firstaid/regular,
- /obj/item/storage/firstaid/regular,
- /obj/item/storage/firstaid/regular,
- /obj/item/storage/firstaid/regular)
- crate_name = "first aid kit crate"
-
/datum/supply_pack/medical/iv_drip
name = "IV Drip Crate"
desc = "Contains a single IV drip stand for intravenous delivery."
@@ -112,6 +88,7 @@
/obj/item/clothing/mask/gas,
/obj/item/clothing/suit/space/hardsuit/medical)
crate_name = "medical hardsuit"
+ crate_type = /obj/structure/closet/crate/secure/medical
/datum/supply_pack/medical/supplies
name = "Medical Supplies Crate"
@@ -140,13 +117,57 @@
/obj/item/storage/pill_bottle/stimulant)
crate_name = "medical supplies crate"
-/datum/supply_pack/medical/vending
- name = "Medical Vending Crate"
- desc = "Contains refills for medical vending machines."
- cost = 2000
- contains = list(/obj/item/vending_refill/medical,
- /obj/item/vending_refill/wallmed)
- crate_name = "medical vending crate"
+/datum/supply_pack/medical/adv_surgery_tools
+ name = "Med-Co Advanced Surgery Tools"
+ desc = "A full set of Med-Co advanced surgery tools, this crate also comes with a spay of synth flesh as well as a can of . Requires Surgery access to open."
+ cost = 5500
+ access = ACCESS_SURGERY
+ contains = list(/obj/item/storage/belt/medical/surgery_belt_adv,
+ /obj/item/reagent_containers/medspray/synthflesh,
+ /obj/item/reagent_containers/medspray/sterilizine)
+ crate_name = "medco surgery tools"
+ crate_type = /obj/structure/closet/crate/secure/medical
+
+/datum/supply_pack/medical/surgery
+ name = "Surgical Supplies Crate"
+ desc = "Do you want to perform surgery, but don't have one of those fancy shmancy degrees? Just get started with this crate containing a medical duffelbag, Sterilizine spray and collapsible roller bed."
+ cost = 1300
+ contains = list(/obj/item/storage/backpack/duffelbag/med/surgery,
+ /obj/item/reagent_containers/medspray/sterilizine,
+ /obj/item/roller)
+ crate_name = "surgical supplies crate"
+
+//////////////////////////////////////////////////////////////////////////////
+///////////////////////////// Medical Kits ///////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+/datum/supply_pack/medical/firstaidbruises
+ name = "Bruise Treatment Kit Crate"
+ desc = "Contains three first aid kits focused on healing bruises and broken bones."
+ cost = 1000
+ contains = list(/obj/item/storage/firstaid/brute,
+ /obj/item/storage/firstaid/brute,
+ /obj/item/storage/firstaid/brute)
+ crate_name = "brute treatment kit crate"
+
+/datum/supply_pack/medical/firstaidburns
+ name = "Burn Treatment Kit Crate"
+ desc = "Contains three first aid kits focused on healing severe burns."
+ cost = 1000
+ contains = list(/obj/item/storage/firstaid/fire,
+ /obj/item/storage/firstaid/fire,
+ /obj/item/storage/firstaid/fire)
+ crate_name = "burn treatment kit crate"
+
+/datum/supply_pack/medical/firstaid
+ name = "First Aid Kit Crate"
+ desc = "Contains four first aid kits for healing most types of wounds."
+ cost = 1000
+ contains = list(/obj/item/storage/firstaid/regular,
+ /obj/item/storage/firstaid/regular,
+ /obj/item/storage/firstaid/regular,
+ /obj/item/storage/firstaid/regular)
+ crate_name = "first aid kit crate"
/datum/supply_pack/medical/sprays
name = "Medical Sprays"
@@ -182,6 +203,15 @@
/obj/item/storage/firstaid/o2)
crate_name = "oxygen deprivation kit crate"
+/datum/supply_pack/medical/firstaidtoxins
+ name = "Toxin Treatment Kit Crate"
+ desc = "Contains three first aid kits focused on healing damage dealt by heavy toxins."
+ cost = 1000
+ contains = list(/obj/item/storage/firstaid/toxin,
+ /obj/item/storage/firstaid/toxin,
+ /obj/item/storage/firstaid/toxin)
+ crate_name = "toxin treatment kit crate"
+
/datum/supply_pack/medical/advrad
name = "Radiation Treatment Crate Deluxe"
desc = "A crate for when radiation is out of hand... Contains two rad-b-gone kits, one bottle of anti radiation deluxe pills, as well as a radiation treatment deluxe pill bottle!"
@@ -195,23 +225,9 @@
crate_name = "radiation protection crate"
crate_type = /obj/structure/closet/crate/radiation
-/datum/supply_pack/medical/surgery
- name = "Surgical Supplies Crate"
- desc = "Do you want to perform surgery, but don't have one of those fancy shmancy degrees? Just get started with this crate containing a medical duffelbag, Sterilizine spray and collapsible roller bed."
- cost = 1300
- contains = list(/obj/item/storage/backpack/duffelbag/med/surgery,
- /obj/item/reagent_containers/medspray/sterilizine,
- /obj/item/roller)
- crate_name = "surgical supplies crate"
-
-/datum/supply_pack/medical/firstaidtoxins
- name = "Toxin Treatment Kit Crate"
- desc = "Contains three first aid kits focused on healing damage dealt by heavy toxins."
- cost = 1000
- contains = list(/obj/item/storage/firstaid/toxin,
- /obj/item/storage/firstaid/toxin,
- /obj/item/storage/firstaid/toxin)
- crate_name = "toxin treatment kit crate"
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////////// Virology ////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
/datum/supply_pack/medical/virus
name = "Virus Crate"
@@ -255,4 +271,4 @@
/obj/item/storage/box/syringes,
/obj/item/storage/box/beakers)
crate_name = "virus containment unit crate"
- crate_type = /obj/structure/closet/crate/secure/plasma
+ crate_type = /obj/structure/closet/crate/secure/plasma
\ No newline at end of file
diff --git a/code/modules/cargo/packs/misc.dm b/code/modules/cargo/packs/misc.dm
index 898f82c6ce..d9c9794bd8 100644
--- a/code/modules/cargo/packs/misc.dm
+++ b/code/modules/cargo/packs/misc.dm
@@ -13,16 +13,6 @@
//////////////////// Paperwork and Writing Supplies //////////////////////////
//////////////////////////////////////////////////////////////////////////////
-/datum/supply_pack/misc/abandonedcrate
- name = "Loot Box"
- desc = "Try your luck with these highly secure loot boxes! Solve the lock, win great prizes! WARNING: EXPLOSIVE FAILURE."
- contraband = TRUE
- cost = 15000
- contains = list(/obj/structure/closet/crate/secure/loot)
- crate_name = "abandoned crate"
- crate_type = /obj/structure/closet/crate/large
- dangerous = TRUE
-
/datum/supply_pack/misc/artsupply
name = "Art Supplies"
desc = "Make some happy little accidents with six canvasses, two easels, two boxes of crayons, and a rainbow crayon!"
@@ -214,10 +204,77 @@
for(var/i in 1 to 9)
new /obj/item/coin/silver(.)
+/datum/supply_pack/misc/dueling_stam
+ name = "Dueling Pistols"
+ desc = "Resolve all your quarrels with some nonlethal fun."
+ cost = 2000
+ contains = list(/obj/item/storage/lockbox/dueling/hugbox/stamina,
+ /obj/item/storage/lockbox/dueling/hugbox/stamina,
+ /obj/item/storage/lockbox/dueling/hugbox/stamina,
+ /obj/item/storage/lockbox/dueling/hugbox/stamina,
+ /obj/item/storage/lockbox/dueling/hugbox/stamina)
+ crate_name = "dueling pistols"
+
+/datum/supply_pack/misc/dueling_lethal
+ name = "Lethal Dueling Pistols"
+ desc = "Settle your differences the true spaceman way."
+ cost = 3000
+ contraband = TRUE
+ contains = list(/obj/item/storage/lockbox/dueling/hugbox,
+ /obj/item/storage/lockbox/dueling/hugbox,
+ /obj/item/storage/lockbox/dueling/hugbox)
+ crate_name = "dueling pistols (lethal)"
+
+/datum/supply_pack/misc/dueling_death
+ name = "Elimination Dueling Pistols"
+ desc = "It's high noon."
+ cost = 5000
+ hidden = TRUE
+ contains = list(/obj/item/storage/lockbox/dueling)
+ crate_name = "dueling pistols (elimination)"
+
+/datum/supply_pack/misc/dirtymags
+ name = "Dirty Magazines"
+ desc = "Get your mind out of the gutter operative, you have work to do. Three items per order. Possible Results: .357 Speedloaders, Kitchen Gun Mags, Stetchkin Mags."
+ hidden = TRUE
+ cost = 12000
+ var/num_contained = 3
+ contains = list(/obj/item/ammo_box/a357,
+ /obj/item/ammo_box/a357,
+ /obj/item/ammo_box/a357,
+ /obj/item/ammo_box/magazine/pistolm9mm,
+ /obj/item/ammo_box/magazine/pistolm9mm,
+ /obj/item/ammo_box/magazine/pistolm9mm,
+ /obj/item/ammo_box/magazine/m45/kitchengun,
+ /obj/item/ammo_box/magazine/m45/kitchengun)
+ crate_name = "crate"
+
+/datum/supply_pack/misc/dirtymags/fill(obj/structure/closet/crate/C)
+ var/list/L = contains.Copy()
+ for(var/i in 1 to num_contained)
+ var/item = pick_n_take(L)
+ new item(C)
+
//////////////////////////////////////////////////////////////////////////////
-//////////////////////////////// Misc Supplies ///////////////////////////////
+///////////////////////////////// Misc Supplies //////////////////////////////
//////////////////////////////////////////////////////////////////////////////
+/datum/supply_pack/misc/candles
+ name = "Candle Crate"
+ desc = "Set up a romantic dinner or host a séance with these extra candles and crayons."
+ cost = 850
+ contains = list(/obj/item/storage/fancy/candle_box,
+ /obj/item/storage/fancy/candle_box,
+ /obj/item/storage/box/matches)
+ crate_name = "candle crate"
+
+/datum/supply_pack/misc/diamondring
+ name = "Diamond Ring"
+ desc = "Show them your love is like a diamond: unbreakable and forever lasting. Shipped straight from child slave cartels in the space african mines."
+ cost = 10000
+ contains = list(/obj/item/storage/fancy/ringbox/diamond)
+ crate_name = "diamond ring crate"
+
/datum/supply_pack/misc/exoticfootwear
name = "Exotic Footwear Crate"
desc = "Popularised by lizards and exotic dancers, the footwear included in this shipment is sure to give your feet the breathing room they deserve. Sweet Kicks Inc. is not responsible for any damage, distress, or @r0u$a1 caused by this shipment."
@@ -234,14 +291,6 @@
/obj/item/clothing/shoes/kindleKicks)
crate_name = "footie crate"
-/datum/supply_pack/misc/wrapping_paper
- name = "Festive Wrapping Paper Crate"
- desc = "Want to mail your loved ones gift-wrapped chocolates, stuffed animals, or the Clown's severed head? You can do all that, with this crate full of wrapping paper."
- cost = 1000
- contains = list(/obj/item/stack/wrapping_paper)
- crate_type = /obj/structure/closet/crate/wooden
- crate_name = "festive wrapping paper crate"
-
/datum/supply_pack/misc/funeral
name = "Funeral Supplies"
desc = "Mourn your dead properly buy sending them off with love filled notes, clean clothes, and a proper ceremony. Contains two candle packs, funeral garb, flowers, a paperbin , and crayons to help aid in religious rituals. Coffin included."
@@ -261,12 +310,32 @@
crate_name = "coffin"
crate_type = /obj/structure/closet/crate/coffin
+/datum/supply_pack/misc/jewelry
+ name = "Jewelry Crate"
+ desc = "Bling out with this crate of jewelry. Includes gold necklace and a set of two rings."
+ cost = 5000
+ contains = list(/obj/item/clothing/neck/necklace/dope,
+ /obj/item/storage/fancy/ringbox,
+ /obj/item/storage/fancy/ringbox/silver
+ )
+ crate_name = "jewelry crate"
+
/datum/supply_pack/misc/jukebox
name = "Jukebox"
cost = 10000
contains = list(/obj/machinery/jukebox)
crate_name = "Jukebox"
+/datum/supply_pack/misc/abandonedcrate
+ name = "Loot Box"
+ desc = "Try your luck with these highly secure loot boxes! Solve the lock, win great prizes! WARNING: EXPLOSIVE FAILURE."
+ contraband = TRUE
+ cost = 15000
+ contains = list(/obj/structure/closet/crate/secure/loot)
+ crate_name = "abandoned crate"
+ crate_type = /obj/structure/closet/crate/large
+ dangerous = TRUE
+
/datum/supply_pack/misc/potted_plants
name = "Potted Plants Crate"
desc = "Spruce up the station with these lovely plants! Contains a random assortment of five potted plants from Nanotrasen's potted plant research division. Warranty void if thrown."
diff --git a/code/modules/cargo/packs/organic.dm b/code/modules/cargo/packs/organic.dm
index 2d0af18670..cdac6f49f2 100644
--- a/code/modules/cargo/packs/organic.dm
+++ b/code/modules/cargo/packs/organic.dm
@@ -3,22 +3,47 @@
// If you add something to this list, please group it by type and sort it alphabetically instead of just jamming it in like an animal
// cost = 700- Minimum cost, or infinite points are possible.
//////////////////////////////////////////////////////////////////////////////
-//////////////////////////// Organic /////////////////////////////////////////
+//////////////////////////////// Organic /////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
/datum/supply_pack/organic
group = "Food & Hydroponics"
crate_type = /obj/structure/closet/crate/freezer
+/datum/supply_pack/organic/randomized
+ var/num_contained = 15
+
+/datum/supply_pack/organic/randomized/fill(obj/structure/closet/crate/C)
+ for(var/i in 1 to num_contained)
+ var/item = pick(contains)
+ new item(C)
+
//////////////////////////////////////////////////////////////////////////////
-/////////////////////////////// Food /////////////////////////////////////////
+//////////////////////////////// Meals ///////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
-/datum/supply_pack/organic/candy/randomised
+/datum/supply_pack/organic/combomeal2
+ name = "Burger Combo #2"
+ desc = "We value our customers at the Greasy Griddle, so much so that we're willing to deliver -just for you.- This combo meal contains two burgers, a soda, fries, a toy, and some chicken nuggets."
+ cost = 3200
+ contains = list(/obj/item/reagent_containers/food/snacks/burger/bigbite,
+ /obj/item/reagent_containers/food/snacks/burger/cheese,
+ /obj/item/reagent_containers/food/snacks/fries,
+ /obj/item/reagent_containers/food/condiment/pack/ketchup,
+ /obj/item/reagent_containers/food/condiment/pack/ketchup,
+ /obj/item/reagent_containers/food/snacks/nugget,
+ /obj/item/reagent_containers/food/snacks/nugget,
+ /obj/item/reagent_containers/food/snacks/nugget,
+ /obj/item/reagent_containers/food/snacks/nugget,
+ /obj/item/toy/plush/random)
+ crate_name = "combo meal w/toy"
+ crate_type = /obj/structure/closet/crate/wooden
+
+/datum/supply_pack/organic/randomized/candy
name = "Candy Crate"
desc = "For people that have an insatiable sweet tooth! Has ten candies to be eaten up.."
cost = 2500
- var/num_contained = 10 //number of items picked to be contained in a randomised crate
+ num_contained = 10
contains = list(/obj/item/reagent_containers/food/snacks/candy,
/obj/item/reagent_containers/food/snacks/lollipop,
/obj/item/reagent_containers/food/snacks/gumball,
@@ -47,97 +72,6 @@
/obj/item/storage/fancy/donut_box)
crate_name = "candy crate"
-/datum/supply_pack/organic/candy/randomised/fill(obj/structure/closet/crate/C)
- var/list/L = contains.Copy()
- for(var/i in 1 to num_contained)
- var/item = pick_n_take(L)
- new item(C)
-
-/datum/supply_pack/organic/randomized/chef
- name = "Excellent Meat Crate"
- desc = "The best cuts in the whole galaxy."
- cost = 2000
- contains = list(/obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/slime,
- /obj/item/reagent_containers/food/snacks/meat/slab/killertomato,
- /obj/item/reagent_containers/food/snacks/meat/slab/bear,
- /obj/item/reagent_containers/food/snacks/meat/slab/xeno,
- /obj/item/reagent_containers/food/snacks/meat/slab/spider,
- /obj/item/reagent_containers/food/snacks/meat/rawbacon,
- /obj/item/reagent_containers/food/snacks/spiderleg,
- /obj/item/reagent_containers/food/snacks/carpmeat,
- /obj/item/reagent_containers/food/snacks/meat/slab/human)
- crate_name = "food crate"
-
-/datum/supply_pack/organic/randomized/chef/fill(obj/structure/closet/crate/C)
- for(var/i in 1 to 15)
- var/item = pick(contains)
- new item(C)
-
-/datum/supply_pack/organic/exoticseeds
- name = "Exotic Seeds Crate"
- desc = "Any entrepreneuring botanist's dream. Contains twelve different seeds, including three replica-pod seeds and two mystery seeds!"
- cost = 1500
- contains = list(/obj/item/seeds/nettle,
- /obj/item/seeds/replicapod,
- /obj/item/seeds/replicapod,
- /obj/item/seeds/replicapod,
- /obj/item/seeds/plump,
- /obj/item/seeds/liberty,
- /obj/item/seeds/amanita,
- /obj/item/seeds/reishi,
- /obj/item/seeds/banana,
- /obj/item/seeds/bamboo,
- /obj/item/seeds/eggplant/eggy,
- /obj/item/seeds/random,
- /obj/item/seeds/random)
- crate_name = "exotic seeds crate"
- crate_type = /obj/structure/closet/crate/hydroponics
-
-/datum/supply_pack/organic/food
- name = "Food Crate"
- desc = "Get things cooking with this crate full of useful ingredients! Contains a two dozen eggs, three bananas, and two bags of flour and rice, two cartons of milk, soymilk, as well as salt and pepper shakers, an enzyme and sugar bottle, and three slabs of monkeymeat."
- cost = 1000
- contains = list(/obj/item/reagent_containers/food/condiment/flour,
- /obj/item/reagent_containers/food/condiment/flour,
- /obj/item/reagent_containers/food/condiment/rice,
- /obj/item/reagent_containers/food/condiment/rice,
- /obj/item/reagent_containers/food/condiment/milk,
- /obj/item/reagent_containers/food/condiment/milk,
- /obj/item/reagent_containers/food/condiment/soymilk,
- /obj/item/reagent_containers/food/condiment/saltshaker,
- /obj/item/reagent_containers/food/condiment/peppermill,
- /obj/item/storage/fancy/egg_box,
- /obj/item/storage/fancy/egg_box,
- /obj/item/reagent_containers/food/condiment/enzyme,
- /obj/item/reagent_containers/food/condiment/sugar,
- /obj/item/reagent_containers/food/snacks/meat/slab/monkey,
- /obj/item/reagent_containers/food/snacks/meat/slab/monkey,
- /obj/item/reagent_containers/food/snacks/meat/slab/monkey,
- /obj/item/reagent_containers/food/snacks/grown/banana,
- /obj/item/reagent_containers/food/snacks/grown/banana,
- /obj/item/reagent_containers/food/snacks/grown/banana)
- crate_name = "food crate"
-
-/datum/supply_pack/organic/randomized/chef/fruits
- name = "Fruit Crate"
- desc = "Rich in vitamins, may contain oranges."
- cost = 1500
- contains = list(/obj/item/reagent_containers/food/snacks/grown/citrus/lime,
- /obj/item/reagent_containers/food/snacks/grown/citrus/orange,
- /obj/item/reagent_containers/food/snacks/grown/banana,
- /obj/item/reagent_containers/food/snacks/grown/watermelon,
- /obj/item/reagent_containers/food/snacks/grown/apple,
- /obj/item/reagent_containers/food/snacks/grown/berries,
- /obj/item/reagent_containers/food/snacks/grown/citrus/lemon,
- /obj/item/reagent_containers/food/snacks/grown/pineapple,
- /obj/item/reagent_containers/food/snacks/grown/cherries,
- /obj/item/reagent_containers/food/snacks/grown/grapes,
- /obj/item/reagent_containers/food/snacks/grown/grapes/green,
- /obj/item/reagent_containers/food/snacks/grown/eggplant,
- /obj/item/reagent_containers/food/snacks/grown/peach,
- /obj/item/reagent_containers/food/snacks/grown/strawberry)
- crate_name = "food crate"
-
/datum/supply_pack/organic/fiestatortilla
name = "Fiesta Crate"
desc = "Spice up the kitchen with this fiesta themed food order! Contains 8 tortilla based food items, as well as a sombrero, moustache, and cloak!"
@@ -157,102 +91,6 @@
/obj/item/reagent_containers/glass/bottle/capsaicin)
crate_name = "fiesta crate"
-/datum/supply_pack/organic/grill
- name = "Grilling Starter Kit"
- desc = "Hey dad I'm Hungry. Hi Hungry I'm THE NEW GRILLING STARTER KIT ONLY 5000 BUX GET NOW! Contains a cooking grill and five fuel coal sheets."
- cost = 3000
- crate_type = /obj/structure/closet/crate
- contains = list(/obj/item/stack/sheet/mineral/coal/five,
- /obj/machinery/grill/unwrenched)
- crate_name = "grilling starter kit crate"
-
-/datum/supply_pack/organic/grillfuel
- name = "Grilling Fuel Kit"
- desc = "Contains coal and coal accessories. (Note: only ten coal sheets.)"
- cost = 1000
- crate_type = /obj/structure/closet/crate
- contains = list(/obj/item/stack/sheet/mineral/coal/ten)
- crate_name = "grilling fuel kit crate"
-
-/datum/supply_pack/organic/cream_piee
- name = "High-yield Clown-grade Cream Pie Crate"
- desc = "Designed by Aussec's Advanced Warfare Research Division, these high-yield, Clown-grade cream pies are powered by a synergy of performance and efficiency. Guaranteed to provide maximum results."
- cost = 6000
- contains = list(/obj/item/storage/backpack/duffelbag/clown/cream_pie)
- crate_name = "party equipment crate"
- contraband = TRUE
- access = ACCESS_THEATRE
- crate_type = /obj/structure/closet/crate/secure
-
-/datum/supply_pack/organic/fakemeat
- name = "Meat Crate"
- desc = "Run outta meat already? Keep the lizards content with this freezer filled with cruelty-free chemically compounded meat! Contains 12 slabs of meat product, and 4 slabs of *carp*."
- cost = 1200 // Buying 3 food crates nets you 9 meat for 900 points, plus like, 6 bags of rice, flour, and egg boxes. This is 12 for 500, but you -only- get meat and carp.
- contains = list(/obj/item/reagent_containers/food/snacks/meat/slab/meatproduct,
- /obj/item/reagent_containers/food/snacks/meat/slab/meatproduct,
- /obj/item/reagent_containers/food/snacks/meat/slab/meatproduct,
- /obj/item/reagent_containers/food/snacks/meat/slab/meatproduct,
- /obj/item/reagent_containers/food/snacks/meat/slab/meatproduct,
- /obj/item/reagent_containers/food/snacks/meat/slab/meatproduct,
- /obj/item/reagent_containers/food/snacks/meat/slab/meatproduct,
- /obj/item/reagent_containers/food/snacks/meat/slab/meatproduct,
- /obj/item/reagent_containers/food/snacks/meat/slab/meatproduct,
- /obj/item/reagent_containers/food/snacks/meat/slab/meatproduct,
- /obj/item/reagent_containers/food/snacks/meat/slab/meatproduct,
- /obj/item/reagent_containers/food/snacks/meat/slab/meatproduct,
- /obj/item/reagent_containers/food/snacks/carpmeat/imitation,
- /obj/item/reagent_containers/food/snacks/carpmeat/imitation,
- /obj/item/reagent_containers/food/snacks/carpmeat/imitation,
- /obj/item/reagent_containers/food/snacks/carpmeat/imitation)
- crate_name = "meaty crate"
- crate_type = /obj/structure/closet/crate/freezer
-
-/datum/supply_pack/organic/monkeydripmeat
- name = "*Meat* Crate"
- desc = "Need some meat? With this do-it-yourself kit you'll be swimming in it! Contains a monkey cube, an IV drip, and some cryoxadone!"
- cost = 2150
- contraband = TRUE
- contains = list(/obj/item/reagent_containers/food/snacks/monkeycube,
- /obj/item/restraints/handcuffs/cable,
- /obj/machinery/iv_drip,
- /obj/item/reagent_containers/glass/beaker/cryoxadone,
- /obj/item/reagent_containers/glass/beaker/cryoxadone)
- crate_name = "monkey meat crate"
-
-/datum/supply_pack/organic/mixedboxes
- name = "Mixed Ingredient Boxes"
- desc = "Get overwhelmed with inspiration by ordering these boxes of surprise ingredients! Get four boxes filled with an assortment of products!"
- cost = 2300
- contains = list(/obj/item/storage/box/ingredients/wildcard,
- /obj/item/storage/box/ingredients/wildcard,
- /obj/item/storage/box/ingredients/wildcard,
- /obj/item/storage/box/ingredients/wildcard)
- crate_name = "wildcard food crate"
- crate_type = /obj/structure/closet/crate/freezer
-
-/datum/supply_pack/organic/party
- name = "Party Equipment"
- desc = "Celebrate both life and death on the station with Nanotrasen's Party Essentials(tm)! Contains seven colored glowsticks, four beers, two ales, a drinking shaker, and a bottle of patron & goldschlager!"
- cost = 2000
- contains = list(/obj/item/storage/box/drinkingglasses,
- /obj/item/reagent_containers/food/drinks/shaker,
- /obj/item/reagent_containers/food/drinks/bottle/patron,
- /obj/item/reagent_containers/food/drinks/bottle/goldschlager,
- /obj/item/reagent_containers/food/drinks/ale,
- /obj/item/reagent_containers/food/drinks/ale,
- /obj/item/reagent_containers/food/drinks/beer,
- /obj/item/reagent_containers/food/drinks/beer,
- /obj/item/reagent_containers/food/drinks/beer,
- /obj/item/reagent_containers/food/drinks/beer,
- /obj/item/flashlight/glowstick,
- /obj/item/flashlight/glowstick/red,
- /obj/item/flashlight/glowstick/blue,
- /obj/item/flashlight/glowstick/cyan,
- /obj/item/flashlight/glowstick/orange,
- /obj/item/flashlight/glowstick/yellow,
- /obj/item/flashlight/glowstick/pink)
- crate_name = "party equipment crate"
-
/datum/supply_pack/organic/pizza
name = "Pizza Crate"
desc = "Best prices on this side of the galaxy. All deliveries are guaranteed to be 99% anomaly-free!"
@@ -287,9 +125,130 @@
considered \[REDACTED\] and returned at your leisure. Note that objects the anomaly produces are specifically attuned exactly to the individual opening the anomaly; regardless \
of species, the individual will find the object edible and it will taste great according to their personal definitions, which vary significantly based on person and species.")
-/datum/supply_pack/organic/randomized/chef/vegetables
- name = "Vegetables Crate"
- desc = "Grown in vats."
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////// Raw Ingredients /////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+/datum/supply_pack/organic/food
+ name = "Food Crate"
+ desc = "Get things cooking with this crate full of useful ingredients! Contains a two dozen eggs, three bananas, and two bags of flour and rice, two cartons of milk, soymilk, as well as salt and pepper shakers, an enzyme and sugar bottle, and three slabs of monkeymeat."
+ cost = 1000
+ contains = list(/obj/item/reagent_containers/food/condiment/flour,
+ /obj/item/reagent_containers/food/condiment/flour,
+ /obj/item/reagent_containers/food/condiment/rice,
+ /obj/item/reagent_containers/food/condiment/rice,
+ /obj/item/reagent_containers/food/condiment/milk,
+ /obj/item/reagent_containers/food/condiment/milk,
+ /obj/item/reagent_containers/food/condiment/soymilk,
+ /obj/item/reagent_containers/food/condiment/saltshaker,
+ /obj/item/reagent_containers/food/condiment/peppermill,
+ /obj/item/storage/fancy/egg_box,
+ /obj/item/storage/fancy/egg_box,
+ /obj/item/reagent_containers/food/condiment/enzyme,
+ /obj/item/reagent_containers/food/condiment/sugar,
+ /obj/item/reagent_containers/food/snacks/meat/slab/monkey,
+ /obj/item/reagent_containers/food/snacks/meat/slab/monkey,
+ /obj/item/reagent_containers/food/snacks/meat/slab/monkey,
+ /obj/item/reagent_containers/food/snacks/grown/banana,
+ /obj/item/reagent_containers/food/snacks/grown/banana,
+ /obj/item/reagent_containers/food/snacks/grown/banana)
+ crate_name = "food crate"
+
+/datum/supply_pack/organic/randomized/fruits
+ name = "Fruit Crate"
+ desc = "Rich in vitamins and possibly sugar. Contains 15 assorted fruits."
+ cost = 1500
+ contains = list(/obj/item/reagent_containers/food/snacks/grown/citrus/lime,
+ /obj/item/reagent_containers/food/snacks/grown/citrus/orange,
+ /obj/item/reagent_containers/food/snacks/grown/banana,
+ /obj/item/reagent_containers/food/snacks/grown/watermelon,
+ /obj/item/reagent_containers/food/snacks/grown/apple,
+ /obj/item/reagent_containers/food/snacks/grown/berries,
+ /obj/item/reagent_containers/food/snacks/grown/citrus/lemon,
+ /obj/item/reagent_containers/food/snacks/grown/pineapple,
+ /obj/item/reagent_containers/food/snacks/grown/cherries,
+ /obj/item/reagent_containers/food/snacks/grown/grapes,
+ /obj/item/reagent_containers/food/snacks/grown/grapes/green,
+ /obj/item/reagent_containers/food/snacks/grown/eggplant,
+ /obj/item/reagent_containers/food/snacks/grown/peach,
+ /obj/item/reagent_containers/food/snacks/grown/strawberry)
+ crate_name = "fruit crate"
+
+/datum/supply_pack/organic/cream_piee
+ name = "High-yield Clown-grade Cream Pie Crate"
+ desc = "Designed by Aussec's Advanced Warfare Research Division, these high-yield, Clown-grade cream pies are powered by a synergy of performance and efficiency. Guaranteed to provide maximum results."
+ cost = 6000
+ contains = list(/obj/item/storage/backpack/duffelbag/clown/cream_pie)
+ crate_name = "party equipment crate"
+ contraband = TRUE
+ access = ACCESS_THEATRE
+ crate_type = /obj/structure/closet/crate/secure
+
+/datum/supply_pack/organic/randomized
+ name = "Meat Crate (Exotic)"
+ desc = "The best cuts in the whole galaxy. Contains 15 assorted exotic meats."
+ cost = 2000
+ contains = list(/obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/slime,
+ /obj/item/reagent_containers/food/snacks/meat/slab/killertomato,
+ /obj/item/reagent_containers/food/snacks/meat/slab/bear,
+ /obj/item/reagent_containers/food/snacks/meat/slab/xeno,
+ /obj/item/reagent_containers/food/snacks/meat/slab/spider,
+ /obj/item/reagent_containers/food/snacks/meat/rawcrab,
+ /obj/item/reagent_containers/food/snacks/meat/rawbacon,
+ /obj/item/reagent_containers/food/snacks/spiderleg,
+ /obj/item/reagent_containers/food/snacks/carpmeat,
+ /obj/item/reagent_containers/food/snacks/meat/slab/human)
+ crate_name = "exotic meat crate"
+
+/datum/supply_pack/organic/monkeydripmeat
+ name = "Meat Crate (Fresh)"
+ desc = "Need some meat? With this do-it-yourself kit you'll be swimming in it! Contains a monkey cube, an IV drip, and some cryoxadone!"
+ cost = 2150
+ contraband = TRUE
+ contains = list(/obj/item/reagent_containers/food/snacks/monkeycube,
+ /obj/item/restraints/handcuffs/cable,
+ /obj/machinery/iv_drip,
+ /obj/item/reagent_containers/glass/beaker/cryoxadone,
+ /obj/item/reagent_containers/glass/beaker/cryoxadone)
+ crate_name = "monkey meat crate"
+
+/datum/supply_pack/organic/fakemeat
+ name = "Meat Crate 'Synthetic'"
+ desc = "Run outta meat already? Keep the lizards content with this freezer filled with cruelty-free chemically compounded meat! Contains 12 slabs of meat product, and 4 slabs of *carp*."
+ cost = 1200 // Buying 3 food crates nets you 9 meat for 900 points, plus like, 6 bags of rice, flour, and egg boxes. This is 12 for 500, but you -only- get meat and carp.
+ contains = list(/obj/item/reagent_containers/food/snacks/meat/slab/meatproduct,
+ /obj/item/reagent_containers/food/snacks/meat/slab/meatproduct,
+ /obj/item/reagent_containers/food/snacks/meat/slab/meatproduct,
+ /obj/item/reagent_containers/food/snacks/meat/slab/meatproduct,
+ /obj/item/reagent_containers/food/snacks/meat/slab/meatproduct,
+ /obj/item/reagent_containers/food/snacks/meat/slab/meatproduct,
+ /obj/item/reagent_containers/food/snacks/meat/slab/meatproduct,
+ /obj/item/reagent_containers/food/snacks/meat/slab/meatproduct,
+ /obj/item/reagent_containers/food/snacks/meat/slab/meatproduct,
+ /obj/item/reagent_containers/food/snacks/meat/slab/meatproduct,
+ /obj/item/reagent_containers/food/snacks/meat/slab/meatproduct,
+ /obj/item/reagent_containers/food/snacks/meat/slab/meatproduct,
+ /obj/item/reagent_containers/food/snacks/carpmeat/imitation,
+ /obj/item/reagent_containers/food/snacks/carpmeat/imitation,
+ /obj/item/reagent_containers/food/snacks/carpmeat/imitation,
+ /obj/item/reagent_containers/food/snacks/carpmeat/imitation)
+ crate_name = "meaty crate"
+ crate_type = /obj/structure/closet/crate/freezer
+
+/datum/supply_pack/organic/mixedboxes
+ name = "Mixed Ingredient Boxes"
+ desc = "Get overwhelmed with inspiration by ordering these boxes of surprise ingredients! Get four boxes filled with an assortment of products!"
+ cost = 2300
+ contains = list(/obj/item/storage/box/ingredients/wildcard,
+ /obj/item/storage/box/ingredients/wildcard,
+ /obj/item/storage/box/ingredients/wildcard,
+ /obj/item/storage/box/ingredients/wildcard)
+ crate_name = "wildcard food crate"
+ crate_type = /obj/structure/closet/crate/freezer
+
+/datum/supply_pack/organic/randomized/vegetables
+ name = "Vegetable Crate"
+ desc = "Grown in vats. Contains 15 assorted vegetables."
cost = 1300
contains = list(/obj/item/reagent_containers/food/snacks/grown/chili,
/obj/item/reagent_containers/food/snacks/grown/corn,
@@ -299,7 +258,7 @@
/obj/item/reagent_containers/food/snacks/grown/mushroom/chanterelle,
/obj/item/reagent_containers/food/snacks/grown/onion,
/obj/item/reagent_containers/food/snacks/grown/pumpkin)
- crate_name = "food crate"
+ crate_name = "veggie crate"
//////////////////////////////////////////////////////////////////////////////
//////////////////////////// Hydroponics /////////////////////////////////////
@@ -331,25 +290,6 @@
crate_name = "beekeeping starter crate"
crate_type = /obj/structure/closet/crate/hydroponics
-/datum/supply_pack/organic/exoticseeds
- name = "Exotic Seeds Crate"
- desc = "Any entrepreneuring botanist's dream. Contains twelve different seeds, including three replica-pod seeds and two mystery seeds!"
- cost = 1500
- contains = list(/obj/item/seeds/nettle,
- /obj/item/seeds/replicapod,
- /obj/item/seeds/replicapod,
- /obj/item/seeds/replicapod,
- /obj/item/seeds/plump,
- /obj/item/seeds/liberty,
- /obj/item/seeds/amanita,
- /obj/item/seeds/reishi,
- /obj/item/seeds/banana,
- /obj/item/seeds/eggplant/eggy,
- /obj/item/seeds/random,
- /obj/item/seeds/random)
- crate_name = "exotic seeds crate"
- crate_type = /obj/structure/closet/crate/hydroponics
-
/datum/supply_pack/organic/hydroponics/hydrotank
name = "Hydroponics Backpack Crate"
desc = "Bring on the flood with this high-capacity backpack crate. Contains 500 units of life-giving H2O. Requires hydroponics access to open."
@@ -388,7 +328,7 @@
/datum/supply_pack/organic/seeds
name = "Seeds Crate"
- desc = "Big things have small beginnings. Contains thirteen different seeds."
+ desc = "Big things have small beginnings. Contains fourteen different seeds."
cost = 1250
contains = list(/obj/item/seeds/chili,
/obj/item/seeds/berry,
@@ -402,10 +342,30 @@
/obj/item/seeds/sunflower,
/obj/item/seeds/chanter,
/obj/item/seeds/potato,
- /obj/item/seeds/sugarcane)
+ /obj/item/seeds/sugarcane,
+ /obj/item/seeds/ambrosia)
crate_name = "seeds crate"
crate_type = /obj/structure/closet/crate/hydroponics
+/datum/supply_pack/organic/exoticseeds
+ name = "Seeds Crate (Exotic)"
+ desc = "Any entrepreneuring botanist's dream. Contains twelve different seeds, including three replica-pod seeds and two mystery seeds!"
+ cost = 1500
+ contains = list(/obj/item/seeds/nettle,
+ /obj/item/seeds/replicapod,
+ /obj/item/seeds/replicapod,
+ /obj/item/seeds/replicapod,
+ /obj/item/seeds/plump,
+ /obj/item/seeds/liberty,
+ /obj/item/seeds/amanita,
+ /obj/item/seeds/reishi,
+ /obj/item/seeds/banana,
+ /obj/item/seeds/eggplant/eggy,
+ /obj/item/seeds/random,
+ /obj/item/seeds/random)
+ crate_name = "exotic seeds crate"
+ crate_type = /obj/structure/closet/crate/hydroponics
+
//////////////////////////////////////////////////////////////////////////////
/////////////////////////////////// Misc /////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
@@ -426,6 +386,29 @@
crate_name = "sporting crate"
crate_type = /obj/structure/closet/crate/secure // Would have liked a wooden crate but access >:(
+/datum/supply_pack/organic/party
+ name = "Party Equipment"
+ desc = "Celebrate both life and death on the station with Nanotrasen's Party Essentials(tm)! Contains seven colored glowsticks, four beers, two ales, a drinking shaker, and a bottle of patron & goldschlager!"
+ cost = 2000
+ contains = list(/obj/item/storage/box/drinkingglasses,
+ /obj/item/reagent_containers/food/drinks/shaker,
+ /obj/item/reagent_containers/food/drinks/bottle/patron,
+ /obj/item/reagent_containers/food/drinks/bottle/goldschlager,
+ /obj/item/reagent_containers/food/drinks/ale,
+ /obj/item/reagent_containers/food/drinks/ale,
+ /obj/item/reagent_containers/food/drinks/beer,
+ /obj/item/reagent_containers/food/drinks/beer,
+ /obj/item/reagent_containers/food/drinks/beer,
+ /obj/item/reagent_containers/food/drinks/beer,
+ /obj/item/flashlight/glowstick,
+ /obj/item/flashlight/glowstick/red,
+ /obj/item/flashlight/glowstick/blue,
+ /obj/item/flashlight/glowstick/cyan,
+ /obj/item/flashlight/glowstick/orange,
+ /obj/item/flashlight/glowstick/yellow,
+ /obj/item/flashlight/glowstick/pink)
+ crate_name = "party equipment crate"
+
/datum/supply_pack/organic/vday
name = "Surplus Valentine Crate"
desc = "Turns out we got warehouses of this love-y dove-y crap. We're sending out small bargain buddle of Valentine gear. This crate has two boxes of chocolate, three poppy flowers, five candy hearts, and three cards."
diff --git a/code/modules/cargo/packs/science.dm b/code/modules/cargo/packs/science.dm
index a009c998a3..e6671a02ea 100644
--- a/code/modules/cargo/packs/science.dm
+++ b/code/modules/cargo/packs/science.dm
@@ -25,11 +25,12 @@
desc = "Hello brothers from the stars!!! Our fellow brethren have made contact at long last and gave us gifts man! They really did build the prymi- Connection Error- Bro we’ll send you a sheet of advanced alien alloy."
cost = 15000
DropPodOnly = TRUE
+ contraband = TRUE
contains = list(/obj/item/stack/sheet/mineral/abductor)
crate_name = "alien bro alloy crate"
/datum/supply_pack/science/beakers
- name = "Chemistry Beackers Crate"
+ name = "Chemistry Beakers Crate"
desc = "Glassware for any chemistry lab! Contains four small beakers, three large, two plastic, and one metamaterial. As well as three droppers and two pairs of latex gloves."
cost = 1500
contains = list(/obj/item/reagent_containers/glass/beaker,
@@ -82,8 +83,8 @@
crate_name = "circuitry starter pack crate"
/datum/supply_pack/science/glasswork
- name = "Glass blower kit Crate"
- desc = "Learn and make glassworks of usefull things for a profit! Contains glassworking tools and blowing rods. Glass not included."
+ name = "Glass Blower Kit Crate"
+ desc = "Learn and make glassworks of useful things for a profit! Contains glassworking tools and blowing rods. Glass not included."
cost = 1000
contains = list(/obj/item/glasswork/glasskit,
/obj/item/glasswork/glasskit,
@@ -91,17 +92,6 @@
/obj/item/glasswork/blowing_rod)
crate_name = "glassblower gear crate"
-/datum/supply_pack/science/adv_surgery_tools
- name = "Med-Co Advanced surgery tools"
- desc = "A full set of Med-Co advanced surgery tools, this crate also comes with a spay of synth flesh as well as a can of . Requires Surgery access to open."
- cost = 5500
- access = ACCESS_SURGERY
- contains = list(/obj/item/storage/belt/medical/surgery_belt_adv,
- /obj/item/reagent_containers/medspray/synthflesh,
- /obj/item/reagent_containers/medspray/sterilizine)
- crate_name = "medco newest surgery tools"
- crate_type = /obj/structure/closet/crate/medical
-
/datum/supply_pack/science/monkey
name = "Monkey Cube Crate"
desc = "Stop monkeying around! Contains seven monkey cubes. Just add water!"
@@ -121,9 +111,9 @@
/datum/supply_pack/science/nuke_b_gone
name = "Nuke Defusal Kit"
desc = "Contains set of tools to defuse a nuke."
- cost = 7500 //Usefull for traitors/nukies that fucked up
+ cost = 7500 //Useful for traitors/nukies that fucked up
dangerous = TRUE
- DropPodOnly = TRUE
+ hidden = TRUE
contains = list(/obj/item/nuke_core_container/nt,
/obj/item/screwdriver/nuke/nt,
/obj/item/paper/guides/nt/nuke_instructions)
@@ -204,7 +194,7 @@
/datum/supply_pack/science/supermater
name = "Supermatter Extraction Tools Crate"
desc = "Contains a set of tools to extract a sliver of supermatter. Consult your CE today!"
- cost = 7500 //Usefull for traitors that fucked up
+ cost = 7500 //Useful for traitors that fucked up
hidden = TRUE
contains = list(/obj/item/nuke_core_container/supermatter,
/obj/item/scalpel/supermatter,
diff --git a/code/modules/cargo/packs/security.dm b/code/modules/cargo/packs/security.dm
index 69967d65d1..90bc837767 100644
--- a/code/modules/cargo/packs/security.dm
+++ b/code/modules/cargo/packs/security.dm
@@ -81,8 +81,8 @@
desc = "An old russian crate full of surplus armor that they used to use! Has two sets of bulletproff armor, a few union suits and some warm hats!"
contraband = TRUE
cost = 5750 // Its basicly sec suits, good boots/gloves
- contains = list(/obj/item/clothing/suit/security/officer/russian,
- /obj/item/clothing/suit/security/officer/russian,
+ contains = list(/obj/item/clothing/suit/armor/navyblue/russian,
+ /obj/item/clothing/suit/armor/navyblue/russian,
/obj/item/clothing/shoes/combat,
/obj/item/clothing/shoes/combat,
/obj/item/clothing/head/ushanka,
@@ -103,8 +103,8 @@
desc = "An old russian Minutemen crate, comes with a full russian outfit, a mosin and a stripper clip."
contraband = TRUE
access = FALSE
- cost = 5500 //
- contains = list(/obj/item/clothing/suit/security/officer/russian,
+ cost = 6500 //
+ contains = list(/obj/item/clothing/suit/armor/navyblue/russian,
/obj/item/clothing/shoes/combat,
/obj/item/clothing/head/ushanka,
/obj/item/clothing/suit/armor/bulletproof,
@@ -141,15 +141,15 @@
cost = 3250
contains = list(/obj/item/clothing/under/rank/security/navyblue,
/obj/item/clothing/under/rank/security/navyblue,
- /obj/item/clothing/suit/security/officer,
- /obj/item/clothing/suit/security/officer,
+ /obj/item/clothing/suit/armor/navyblue,
+ /obj/item/clothing/suit/armor/navyblue,
/obj/item/clothing/head/beret/sec/navyofficer,
/obj/item/clothing/head/beret/sec/navyofficer,
/obj/item/clothing/under/rank/warden/navyblue,
- /obj/item/clothing/suit/security/warden,
+ /obj/item/clothing/suit/armor/vest/warden/navyblue,
/obj/item/clothing/head/beret/sec/navywarden,
/obj/item/clothing/under/rank/head_of_security/navyblue,
- /obj/item/clothing/suit/security/hos,
+ /obj/item/clothing/suit/armor/hos/navyblue,
/obj/item/clothing/head/beret/sec/navyhos)
crate_name = "security clothing crate"
@@ -163,13 +163,6 @@
/obj/item/storage/box/handcuffs)
crate_name = "security supply crate"
-/datum/supply_pack/security/vending/security
- name = "SecTech Supply Crate"
- desc = "Officer Paul bought all the donuts? Then refill the security vendor with ths crate."
- cost = 1500
- contains = list(/obj/machinery/vending/security)
- crate_name = "SecTech supply crate"
-
/datum/supply_pack/security/firingpins
name = "Standard Firing Pins Crate"
desc = "Upgrade your arsenal with 10 standard firing pins. Requires Security access to open."
diff --git a/code/modules/cargo/packs/service.dm b/code/modules/cargo/packs/service.dm
index 34b8bbc249..942720dd6b 100644
--- a/code/modules/cargo/packs/service.dm
+++ b/code/modules/cargo/packs/service.dm
@@ -10,9 +10,24 @@
group = "Service"
//////////////////////////////////////////////////////////////////////////////
-/////////////////////////////// Cargo ////////////////////////////////////////
+//////////////////////////////// Cargo ///////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
+/datum/supply_pack/service/wrapping_paper
+ name = "Cargo Packaging Crate"
+ desc = "Want to mail your loved ones gift-wrapped chocolates, stuffed animals, or the Clown's severed head? You can do all that, with this crate full of festive (and normal) wrapping paper. Also contains a hand labeler and a destination tagger for easy shipping!"
+ cost = 1000
+ contains = list(/obj/item/stack/wrapping_paper,
+ /obj/item/stack/wrapping_paper,
+ /obj/item/stack/wrapping_paper,
+ /obj/item/stack/packageWrap,
+ /obj/item/stack/packageWrap,
+ /obj/item/stack/packageWrap,
+ /obj/item/destTagger,
+ /obj/item/hand_labeler)
+ crate_type = /obj/structure/closet/crate/wooden
+ crate_name = "wrapping paper crate"
+
/datum/supply_pack/service/cargo_supples
name = "Cargo Supplies Crate"
desc = "Sold everything that wasn't bolted down? You can get right back to work with this crate containing stamps, an export scanner, destination tagger, hand labeler and some package wrapping. Now with extra toner cartidges!"
@@ -101,6 +116,23 @@
crate_name = "ice cream vat crate"
crate_type = /obj/structure/closet/crate
+/datum/supply_pack/service/grill
+ name = "Grilling Starter Kit"
+ desc = "Hey dad I'm Hungry. Hi Hungry I'm THE NEW GRILLING STARTER KIT ONLY 5000 BUX GET NOW! Contains a cooking grill and five fuel coal sheets."
+ cost = 3000
+ contains = list(/obj/item/stack/sheet/mineral/coal/five,
+ /obj/machinery/grill/unwrenched)
+ crate_name = "grilling starter kit crate"
+ crate_type = /obj/structure/closet/crate
+
+/datum/supply_pack/service/grillfuel
+ name = "Grilling Fuel Kit"
+ desc = "Contains coal and coal accessories. (Note: only ten coal sheets.)"
+ cost = 1000
+ contains = list(/obj/item/stack/sheet/mineral/coal/ten)
+ crate_name = "grilling fuel kit crate"
+ crate_type = /obj/structure/closet/crate
+
/datum/supply_pack/service/cutlery
name = "Kitchen Cutlery Deluxe Set"
desc = "Need to slice and dice away those \"Tomatoes\"? Well we got what you need! From a nice set of knifes, forks, plates, glasses, and a whetstone for when you got some grizzle that is a bit harder to slice then normal."
@@ -240,45 +272,3 @@
crate_name = "janitorial cart crate"
crate_type = /obj/structure/closet/crate/large
-//////////////////////////////////////////////////////////////////////////////
-//////////////////////////// Vendor Refills //////////////////////////////////
-//////////////////////////////////////////////////////////////////////////////
-
-/datum/supply_pack/service/vending/bartending
- name = "Bartending Supply Crate"
- desc = "Bring on the booze with vending machine refills, as well as a free book containing the well-kept secrets to the bartending trade!"
- cost = 2000
- contains = list(/obj/item/vending_refill/boozeomat,
- /obj/item/vending_refill/coffee,
- /obj/item/book/granter/action/drink_fling)
- crate_name = "bartending supply crate"
-
-/datum/supply_pack/service/vending/cigarette
- name = "Cigarette Supply Crate"
- desc = "Don't believe the reports - smoke today! Contains a cigarette vending machine refill."
- cost = 1500
- contains = list(/obj/item/vending_refill/cigarette)
- crate_name = "cigarette supply crate"
- crate_type = /obj/structure/closet/crate
-
-/datum/supply_pack/service/vending/games
- name = "Games Supply Crate"
- desc = "Get your game on with this game vending machine refill."
- cost = 1000
- contains = list(/obj/item/vending_refill/games)
- crate_name = "games supply crate"
- crate_type = /obj/structure/closet/crate
-
-/datum/supply_pack/service/vending/snack
- name = "Snack Supply Crate"
- desc = "One vending machine refill of cavity-bringin' goodness! The number one dentist recommended order!"
- cost = 1500
- contains = list(/obj/item/vending_refill/snack)
- crate_name = "snacks supply crate"
-
-/datum/supply_pack/service/vending/cola
- name = "Softdrinks Supply Crate"
- desc = "Got whacked by a toolbox, but you still have those pesky teeth? Get rid of those pearly whites with this soda machine refill, today!"
- cost = 1500
- contains = list(/obj/item/vending_refill/cola)
- crate_name = "soft drinks supply crate"
diff --git a/code/modules/cargo/packs/vending.dm b/code/modules/cargo/packs/vending.dm
new file mode 100644
index 0000000000..344d19f6c9
--- /dev/null
+++ b/code/modules/cargo/packs/vending.dm
@@ -0,0 +1,192 @@
+
+//Reminders-
+// If you add something to this list, please group it by type and sort it alphabetically instead of just jamming it in like an animal
+// cost = 700- Minimum cost, or infinite points are possible.
+//////////////////////////////////////////////////////////////////////////////
+/////////////////////////////// Vending //////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+/datum/supply_pack/vending
+ group = "Vending"
+
+//////////////////////////////////////////////////////////////////////////////
+///////////////////////// Service, Medical, Sec //////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+/datum/supply_pack/vending/bartending
+ name = "Bartending Supply Crate"
+ desc = "Bring on the booze with vending machine refills, as well as a free book containing the well-kept secrets to the bartending trade!"
+ cost = 2000
+ contains = list(/obj/item/vending_refill/boozeomat,
+ /obj/item/vending_refill/coffee,
+ /obj/item/book/granter/action/drink_fling)
+ crate_name = "bartending supply crate"
+
+/datum/supply_pack/vending/hydro
+ name = "Cartridge Supply Crate"
+ desc = "Restock you cartridges for PDAs. Contains a PTech vending machine refill."
+ cost = 5000
+ contains = list(/obj/item/vending_refill/cart)
+ crate_name = "hydroponics supply crate"
+ crate_type = /obj/structure/closet/crate
+
+/datum/supply_pack/vending/cigarette
+ name = "Cigarette Supply Crate"
+ desc = "Don't believe the reports - smoke today! Contains a cigarette vending machine refill."
+ cost = 1500
+ contains = list(/obj/item/vending_refill/cigarette)
+ crate_name = "cigarette supply crate"
+ crate_type = /obj/structure/closet/crate
+
+/datum/supply_pack/vending/dinner
+ name = "Dinnerware Supply Crate"
+ desc = "Use a plate and have some utensils! Contains a dinnerware and sustenance vending machine refill."
+ cost = 2500
+ contains = list(/obj/item/vending_refill/sustenance,
+ /obj/item/vending_refill/dinnerware)
+ crate_name = "dinnerware supply crate"
+ crate_type = /obj/structure/closet/crate
+
+/datum/supply_pack/vending/dinner
+ name = "Engineering Supply Crate"
+ desc = "Packs of tools waiting to be used for repairing. Contains a tool and engineering vending machine refill. Requires CE access."
+ cost = 5500 //Powerfull
+ access = ACCESS_CE
+ contains = list(/obj/item/vending_refill/tool,
+ /obj/item/vending_refill/engivend)
+ crate_name = "engineering supply crate"
+ crate_type = /obj/structure/closet/crate/secure/engineering
+
+/datum/supply_pack/vending/games
+ name = "Games Supply Crate"
+ desc = "Get your game on with this game vending machine refill."
+ cost = 1000
+ contains = list(/obj/item/vending_refill/games)
+ crate_name = "games supply crate"
+ crate_type = /obj/structure/closet/crate
+
+/datum/supply_pack/vending/hydro
+ name = "Hydroponics Supply Crate"
+ desc = "Arnt you glad you dont have to do it the natural way? Contains a megaseed and nutrimax vending machine refill."
+ cost = 5000
+ contains = list(/obj/item/vending_refill/hydroseeds,
+ /obj/item/vending_refill/hydronutrients)
+ crate_name = "hydroponics supply crate"
+ crate_type = /obj/structure/closet/crate
+
+/datum/supply_pack/vending/kinkmate
+ name = "Kinkmate Supply and Construction Kit"
+ desc = "A fun way to spend the shift. Contains unmentionable desires."
+ cost = 2000
+ contraband = TRUE
+ contains = list(/obj/item/vending_refill/kink, /obj/item/circuitboard/machine/kinkmate)
+ crate_name = "Kinkmate construction kit"
+
+/datum/supply_pack/vending/medical
+ name = "Medical Vending Crate"
+ desc = "Contains refills for medical vending machines."
+ cost = 2000
+ contains = list(/obj/item/vending_refill/medical,
+ /obj/item/vending_refill/wallmed)
+ crate_name = "medical vending crate"
+ crate_type = /obj/structure/closet/crate/medical
+
+/datum/supply_pack/vending/security
+ name = "SecTech Supply Crate"
+ desc = "Officer Paul bought all the donuts? Then refill the security vendor with ths crate. Requires Security Access to open."
+ cost = 1500
+ access = ACCESS_SECURITY
+ contains = list(/obj/machinery/vending/security)
+ crate_name = "SecTech supply crate"
+ crate_type = /obj/structure/closet/crate/secure/gear
+
+/datum/supply_pack/vending/snack
+ name = "Snack Supply Crate"
+ desc = "One vending machine refill of cavity-bringin' goodness! The number one dentist recommended order!"
+ cost = 1500
+ contains = list(/obj/item/vending_refill/snack)
+ crate_name = "snacks supply crate"
+
+/datum/supply_pack/vending/cola
+ name = "Softdrinks Supply Crate"
+ desc = "Got whacked by a toolbox, but you still have those pesky teeth? Get rid of those pearly whites with this soda machine refill, today!"
+ cost = 1500
+ contains = list(/obj/item/vending_refill/cola)
+ crate_name = "soft drinks supply crate"
+
+/datum/supply_pack/vending/vendomat
+ name = "Vendomat Supply Crate"
+ desc = "Contains a Vendomat restock unit!"
+ cost = 1200
+ contains = list(/obj/item/vending_refill/assist)
+ crate_name = "vendomat supply crate"
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////// Wardrobe Vendors ////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+/datum/supply_pack/vending/wardrobes/autodrobe
+ name = "Autodrobe Supply Crate"
+ desc = "Autodrobe missing your favorite dress? Solve that issue today with this autodrobe refill."
+ cost = 1500
+ contains = list(/obj/item/vending_refill/autodrobe)
+ crate_name = "autodrobe supply crate"
+
+/datum/supply_pack/vending/wardrobes/cargo
+ name = "Cargo Wardrobe Supply Crate"
+ desc = "This crate contains a refill for the CargoDrobe."
+ cost = 750
+ contains = list(/obj/item/vending_refill/wardrobe/cargo_wardrobe)
+ crate_name = "cargo department supply crate"
+
+/datum/supply_pack/vending/wardrobes/engineering
+ name = "Engineering Wardrobe Supply Crate"
+ desc = "This crate contains refills for the EngiDrobe and AtmosDrobe."
+ cost = 1500
+ contains = list(/obj/item/vending_refill/wardrobe/engi_wardrobe,
+ /obj/item/vending_refill/wardrobe/atmos_wardrobe)
+ crate_name = "engineering department wardrobe supply crate"
+
+/datum/supply_pack/vending/wardrobes/general
+ name = "General Wardrobes Supply Crate"
+ desc = "This crate contains refills for the CuraDrobe, BarDrobe, ChefDrobe, JaniDrobe, ChapDrobe."
+ cost = 3750
+ contains = list(/obj/item/vending_refill/wardrobe/curator_wardrobe,
+ /obj/item/vending_refill/wardrobe/bar_wardrobe,
+ /obj/item/vending_refill/wardrobe/chef_wardrobe,
+ /obj/item/vending_refill/wardrobe/jani_wardrobe,
+ /obj/item/vending_refill/wardrobe/chap_wardrobe)
+ crate_name = "general wardrobes vendor refills"
+
+/datum/supply_pack/vending/wardrobes/hydroponics
+ name = "Hydrobe Supply Crate"
+ desc = "This crate contains a refill for the Hydrobe."
+ cost = 750
+ contains = list(/obj/item/vending_refill/wardrobe/hydro_wardrobe)
+ crate_name = "hydrobe supply crate"
+
+/datum/supply_pack/vending/wardrobes/medical
+ name = "Medical Wardrobe Supply Crate"
+ desc = "This crate contains refills for the MediDrobe, ChemDrobe, GeneDrobe, and ViroDrobe."
+ cost = 3000
+ contains = list(/obj/item/vending_refill/wardrobe/medi_wardrobe,
+ /obj/item/vending_refill/wardrobe/chem_wardrobe,
+ /obj/item/vending_refill/wardrobe/gene_wardrobe,
+ /obj/item/vending_refill/wardrobe/viro_wardrobe)
+ crate_name = "medical department wardrobe supply crate"
+
+/datum/supply_pack/vending/wardrobes/science
+ name = "Science Wardrobe Supply Crate"
+ desc = "This crate contains refills for the SciDrobe and RoboDrobe."
+ cost = 1500
+ contains = list(/obj/item/vending_refill/wardrobe/robo_wardrobe,
+ /obj/item/vending_refill/wardrobe/science_wardrobe)
+ crate_name = "science department wardrobe supply crate"
+
+/datum/supply_pack/vending/wardrobes/security
+ name = "Security Wardrobe Supply Crate"
+ desc = "This crate contains refills for the SecDrobe and LawDrobe."
+ cost = 1500
+ contains = list(/obj/item/vending_refill/wardrobe/sec_wardrobe,
+ /obj/item/vending_refill/wardrobe/law_wardrobe)
+ crate_name = "security department supply crate"
diff --git a/code/modules/cargo/supplypod.dm b/code/modules/cargo/supplypod.dm
index 5f3d6dab0a..adfbe3308a 100644
--- a/code/modules/cargo/supplypod.dm
+++ b/code/modules/cargo/supplypod.dm
@@ -114,7 +114,7 @@
for(var/X in CM.internal_organs)
var/destination = get_edge_target_turf(T, pick(GLOB.alldirs)) //Pick a random direction to toss them in
var/obj/item/organ/O = X
- O.Remove(CM) //Note that this isn't the same proc as for lists
+ O.Remove() //Note that this isn't the same proc as for lists
O.forceMove(T) //Move the organ outta the body
O.throw_at(destination, 2, 3) //Thow the organ at a random tile 3 spots away
sleep(1)
diff --git a/code/modules/cargo/supplypod_beacon.dm b/code/modules/cargo/supplypod_beacon.dm
index 006f1a8084..668698d2e9 100644
--- a/code/modules/cargo/supplypod_beacon.dm
+++ b/code/modules/cargo/supplypod_beacon.dm
@@ -77,7 +77,7 @@
/obj/item/supplypod_beacon/AltClick(mob/user)
. = ..()
- if (!user.canUseTopic(src, !issilicon(user)))
+ if (!user.canUseTopic(src, !hasSiliconAccessInArea(user)))
return
if (express_console)
unlink_console()
diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm
index 7dc233d20f..902b442403 100644
--- a/code/modules/client/asset_cache.dm
+++ b/code/modules/client/asset_cache.dm
@@ -28,7 +28,7 @@ You can set verify to TRUE if you want send() to sleep until the client has the
//This proc sends the asset to the client, but only if it needs it.
//This proc blocks(sleeps) unless verify is set to false
-/proc/send_asset(var/client/client, var/asset_name, var/verify = TRUE)
+/proc/send_asset(client/client, asset_name, verify = TRUE)
if(!istype(client))
if(ismob(client))
var/mob/M = client
@@ -72,7 +72,7 @@ You can set verify to TRUE if you want send() to sleep until the client has the
return 1
//This proc blocks(sleeps) unless verify is set to false
-/proc/send_asset_list(var/client/client, var/list/asset_list, var/verify = TRUE)
+/proc/send_asset_list(client/client, list/asset_list, verify = TRUE)
if(!istype(client))
if(ismob(client))
var/mob/M = client
@@ -122,7 +122,7 @@ You can set verify to TRUE if you want send() to sleep until the client has the
//This proc will download the files without clogging up the browse() queue, used for passively sending files on connection start.
//The proc calls procs that sleep for long times.
-/proc/getFilesSlow(var/client/client, var/list/files, var/register_asset = TRUE)
+/proc/getFilesSlow(client/client, list/files, register_asset = TRUE)
var/concurrent_tracker = 1
for(var/file in files)
if (!client)
@@ -140,13 +140,13 @@ You can set verify to TRUE if you want send() to sleep until the client has the
//This proc "registers" an asset, it adds it to the cache for further use, you cannot touch it from this point on or you'll fuck things up.
//if it's an icon or something be careful, you'll have to copy it before further use.
-/proc/register_asset(var/asset_name, var/asset)
+/proc/register_asset(asset_name, asset)
SSassets.cache[asset_name] = asset
//Generated names do not include file extention.
//Used mainly for code that deals with assets in a generic way
//The same asset will always lead to the same asset name
-/proc/generate_asset_name(var/file)
+/proc/generate_asset_name(file)
return "asset.[md5(fcopy_rsc(file))]"
@@ -156,7 +156,7 @@ You can set verify to TRUE if you want send() to sleep until the client has the
GLOBAL_LIST_EMPTY(asset_datums)
//get an assetdatum or make a new one
-/proc/get_asset_datum(var/type)
+/proc/get_asset_datum(type)
return GLOB.asset_datums[type] || new type()
/datum/asset
@@ -323,6 +323,13 @@ GLOBAL_LIST_EMPTY(asset_datums)
var/size_id = sprite[SPR_SIZE]
return {""}
+/datum/asset/spritesheet/proc/icon_class_name(sprite_name)
+ var/sprite = sprites[sprite_name]
+ if (!sprite)
+ return null
+ var/size_id = sprite[SPR_SIZE]
+ return {"[name][size_id] [sprite_name]"}
+
#undef SPR_SIZE
#undef SPR_IDX
#undef SPRSZ_COUNT
@@ -379,14 +386,24 @@ GLOBAL_LIST_EMPTY(asset_datums)
/datum/asset/simple/tgui
assets = list(
- "tgui.css" = 'tgui/assets/tgui.css',
- "tgui.js" = 'tgui/assets/tgui.js',
- "font-awesome.min.css" = 'tgui/assets/font-awesome.min.css',
- "fontawesome-webfont.eot" = 'tgui/assets/fonts/fontawesome-webfont.eot',
- "fontawesome-webfont.woff2" = 'tgui/assets/fonts/fontawesome-webfont.woff2',
- "fontawesome-webfont.woff" = 'tgui/assets/fonts/fontawesome-webfont.woff',
- "fontawesome-webfont.ttf" = 'tgui/assets/fonts/fontawesome-webfont.ttf',
- "fontawesome-webfont.svg" = 'tgui/assets/fonts/fontawesome-webfont.svg'
+ // tgui
+ "tgui.css" = 'tgui/assets/tgui.css',
+ "tgui.js" = 'tgui/assets/tgui.js',
+ // tgui-next
+ "tgui-main.html" = 'tgui-next/packages/tgui/public/tgui-main.html',
+ "tgui-fallback.html" = 'tgui-next/packages/tgui/public/tgui-fallback.html',
+ "tgui.bundle.js" = 'tgui-next/packages/tgui/public/tgui.bundle.js',
+ "tgui.bundle.css" = 'tgui-next/packages/tgui/public/tgui.bundle.css',
+ "shim-html5shiv.js" = 'tgui-next/packages/tgui/public/shim-html5shiv.js',
+ "shim-ie8.js" = 'tgui-next/packages/tgui/public/shim-ie8.js',
+ "shim-dom4.js" = 'tgui-next/packages/tgui/public/shim-dom4.js',
+ "shim-css-om.js" = 'tgui-next/packages/tgui/public/shim-css-om.js',
+ )
+
+/datum/asset/group/tgui
+ children = list(
+ /datum/asset/simple/tgui,
+ /datum/asset/simple/fontawesome
)
/datum/asset/simple/headers
@@ -448,7 +465,8 @@ GLOBAL_LIST_EMPTY(asset_datums)
"scanner" = 'icons/pda_icons/pda_scanner.png',
"signaler" = 'icons/pda_icons/pda_signaler.png',
"status" = 'icons/pda_icons/pda_status.png',
- "dronephone" = 'icons/pda_icons/pda_dronephone.png'
+ "dronephone" = 'icons/pda_icons/pda_dronephone.png',
+ "emoji" = 'icons/pda_icons/pda_emoji.png'
)
/datum/asset/spritesheet/simple/paper
@@ -511,7 +529,7 @@ GLOBAL_LIST_EMPTY(asset_datums)
"pill21" = 'icons/UI_Icons/Pills/pill21.png',
"pill22" = 'icons/UI_Icons/Pills/pill22.png',
)
-
+
/datum/asset/simple/IRV
assets = list(
"jquery-ui.custom-core-widgit-mouse-sortable-min.js" = 'html/IRV/jquery-ui.custom-core-widgit-mouse-sortable-min.js',
@@ -550,7 +568,8 @@ GLOBAL_LIST_EMPTY(asset_datums)
children = list(
/datum/asset/simple/jquery,
/datum/asset/simple/goonchat,
- /datum/asset/spritesheet/goonchat
+ /datum/asset/spritesheet/goonchat,
+ /datum/asset/simple/fontawesome
)
/datum/asset/simple/jquery
@@ -563,18 +582,23 @@ GLOBAL_LIST_EMPTY(asset_datums)
verify = FALSE
assets = list(
"json2.min.js" = 'code/modules/goonchat/browserassets/js/json2.min.js',
- "errorHandler.js" = 'code/modules/goonchat/browserassets/js/errorHandler.js',
"browserOutput.js" = 'code/modules/goonchat/browserassets/js/browserOutput.js',
- "fontawesome-webfont.eot" = 'tgui/assets/fonts/fontawesome-webfont.eot',
- "fontawesome-webfont.svg" = 'tgui/assets/fonts/fontawesome-webfont.svg',
- "fontawesome-webfont.ttf" = 'tgui/assets/fonts/fontawesome-webfont.ttf',
- "fontawesome-webfont.woff" = 'tgui/assets/fonts/fontawesome-webfont.woff',
- "font-awesome.css" = 'code/modules/goonchat/browserassets/css/font-awesome.css',
"browserOutput.css" = 'code/modules/goonchat/browserassets/css/browserOutput.css',
"browserOutput_dark.css" = 'code/modules/goonchat/browserassets/css/browserOutput_dark.css',
"browserOutput_light.css" = 'code/modules/goonchat/browserassets/css/browserOutput_light.css'
)
+/datum/asset/simple/fontawesome
+ verify = FALSE
+ assets = list(
+ "fa-regular-400.eot" = 'html/font-awesome/webfonts/fa-regular-400.eot',
+ "fa-regular-400.woff" = 'html/font-awesome/webfonts/fa-regular-400.woff',
+ "fa-solid-900.eot" = 'html/font-awesome/webfonts/fa-solid-900.eot',
+ "fa-solid-900.woff" = 'html/font-awesome/webfonts/fa-solid-900.woff',
+ "font-awesome.css" = 'html/font-awesome/css/all.min.css',
+ "v4shim.css" = 'html/font-awesome/css/v4-shims.min.css'
+ )
+
/datum/asset/spritesheet/goonchat
name = "chat"
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index 7e466caf8a..dc550179f8 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -47,6 +47,18 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
else if (job in completed_asset_jobs) //byond bug ID:2256651
to_chat(src, "An error has been detected in how your client is receiving resources. Attempting to correct.... (If you keep seeing these messages you might want to close byond and reconnect)")
src << browse("...", "window=asset_cache_browser")
+ // Keypress passthrough
+ if(href_list["__keydown"])
+ var/keycode = browser_keycode_to_byond(href_list["__keydown"])
+ if(keycode)
+ keyDown(keycode)
+ return
+ if(href_list["__keyup"])
+ var/keycode = browser_keycode_to_byond(href_list["__keyup"])
+ if(keycode)
+ keyUp(keycode)
+ return
+
var/mtl = CONFIG_GET(number/minute_topic_limit)
if (!holder && mtl)
@@ -411,7 +423,7 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
winset(src, "[child]", "[entries[child]]")
if (!ispath(child, /datum/verbs/menu))
var/procpath/verbpath = child
- if (copytext(verbpath.name,1,2) != "@")
+ if (verbpath.name[1] != "@")
new child(src)
for (var/thing in prefs.menuoptions)
@@ -502,7 +514,7 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
if (CONFIG_GET(flag/panic_bunker) && !holder && !GLOB.deadmins[ckey] && !(ckey in GLOB.bunker_passthrough))
log_access("Failed Login: [key] - New account attempting to connect during panic bunker")
message_admins("Failed Login: [key] - New account attempting to connect during panic bunker")
- to_chat(src, "You must first join the Discord to verify your account before joining this server. To do so, read the rules and post a request in the #station-access-requests channel under the \"Main server\" category in the Discord server linked here: https://discord.gg/E6SQuhz") //CIT CHANGE - makes the panic bunker disconnect message point to the discord
+ to_chat(src, "You must first join the Discord to verify your account before joining this server. To do so, read the rules and post a request in the #station-access-requests channel under the \"Main server\" category in the Discord server linked here: https://discord.gg/E6SQuhz If you have already done so, wait a few minutes then try again; sometimes the server needs to fully load before you can join.") //CIT CHANGE - makes the panic bunker disconnect message point to the discord
var/list/connectiontopic_a = params2list(connectiontopic)
var/list/panic_addr = CONFIG_GET(string/panic_server_address)
if(panic_addr && !connectiontopic_a["redirect"])
@@ -526,6 +538,8 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
if(!account_join_date)
account_join_date = "Error"
account_age = -1
+ else if(ckey in GLOB.bunker_passthrough)
+ GLOB.bunker_passthrough -= ckey
qdel(query_client_in_db)
var/datum/DBQuery/query_get_client_age = SSdbcore.NewQuery("SELECT firstseen, DATEDIFF(Now(),firstseen), accountjoindate, DATEDIFF(Now(),accountjoindate) FROM [format_table_name("player")] WHERE ckey = '[sql_ckey]'")
if(!query_get_client_age.Execute())
@@ -723,13 +737,13 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
message_admins("Proxy Detection: [key_name_admin(src)] IP intel rated [res.intel*100]% likely to be a Proxy/VPN.")
ip_intel = res.intel
-/client/Click(atom/object, atom/location, control, params)
+/client/Click(atom/object, atom/location, control, params, ignore_spam = FALSE)
var/ab = FALSE
var/list/L = params2list(params)
if (object && object == middragatom && L["left"])
ab = max(0, 5 SECONDS-(world.time-middragtime)*0.1)
var/mcl = CONFIG_GET(number/minute_click_limit)
- if (!holder && mcl)
+ if (!holder && !ignore_spam && mcl)
var/minute = round(world.time, 600)
if (!clicklimiter)
clicklimiter = new(LIMITER_SIZE)
@@ -754,7 +768,7 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
return
var/scl = CONFIG_GET(number/second_click_limit)
- if (!holder && scl)
+ if (!holder && !ignore_spam && scl)
var/second = round(world.time, 10)
if (!clicklimiter)
clicklimiter = new(LIMITER_SIZE)
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index 546268b5c7..793a72c1f7 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -137,7 +137,6 @@ GLOBAL_LIST_EMPTY(preferences_datums)
"balls_cum_rate" = CUM_RATE,
"balls_cum_mult" = CUM_RATE_MULT,
"balls_efficiency" = CUM_EFFICIENCY,
- "balls_fluid" = "semen",
"has_ovi" = FALSE,
"ovi_shape" = "knotted",
"ovi_length" = 6,
@@ -152,7 +151,6 @@ GLOBAL_LIST_EMPTY(preferences_datums)
"breasts_color" = "fff",
"breasts_size" = "C",
"breasts_shape" = "Pair",
- "breasts_fluid" = "milk",
"breasts_producing" = FALSE,
"has_vag" = FALSE,
"vag_shape" = "Human",
@@ -163,7 +161,6 @@ GLOBAL_LIST_EMPTY(preferences_datums)
"womb_cum_rate" = CUM_RATE,
"womb_cum_mult" = CUM_RATE_MULT,
"womb_efficiency" = CUM_EFFICIENCY,
- "womb_fluid" = "femcum",
"ipc_screen" = "Sunburst",
"ipc_antenna" = "None",
"flavor_text" = "",
@@ -199,7 +196,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/auto_fit_viewport = TRUE
var/uplink_spawn_loc = UPLINK_PDA
-
+
var/sprint_spacebar = FALSE
var/sprint_toggle = FALSE
@@ -1476,9 +1473,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
age = max(min( round(text2num(new_age)), AGE_MAX),AGE_MIN)
if("flavor_text")
- var/msg = stripped_multiline_input(usr, "Set the flavor text in your 'examine' verb. This can also be used for OOC notes and preferences!", "Flavor Text", html_decode(features["flavor_text"]), MAX_MESSAGE_LEN*2, TRUE)
- if(!isnull(msg))
- msg = copytext(msg, 1, MAX_MESSAGE_LEN*2)
+ var/msg = stripped_multiline_input(usr, "Set the flavor text in your 'examine' verb. This can also be used for OOC notes and preferences!", "Flavor Text", features["flavor_text"], MAX_FAVOR_LEN, TRUE)
+ if(msg)
features["flavor_text"] = msg
if("hair")
@@ -2230,7 +2226,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if("never_hypno")
cit_toggles ^= NEVER_HYPNO
-
+
if("aphro")
cit_toggles ^= NO_APHRO
@@ -2239,7 +2235,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if("bimbo")
cit_toggles ^= BIMBOFICATION
-
+
//END CITADEL EDIT
if("ambientocclusion")
diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm
index 08ecefb91f..03c6051c5d 100644
--- a/code/modules/client/preferences_savefile.dm
+++ b/code/modules/client/preferences_savefile.dm
@@ -118,7 +118,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
/datum/preferences/proc/load_path(ckey,filename="preferences.sav")
if(!ckey)
return
- path = "data/player_saves/[copytext(ckey,1,2)]/[ckey]/[filename]"
+ path = "data/player_saves/[ckey[1]]/[ckey]/[filename]"
/datum/preferences/proc/load_preferences()
if(!path)
@@ -423,13 +423,11 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S["feature_balls_size"] >> features["balls_size"]
S["feature_balls_shape"] >> features["balls_shape"]
S["feature_balls_sack_size"] >> features["balls_sack_size"]
- S["feature_balls_fluid"] >> features["balls_fluid"]
//breasts features
S["feature_has_breasts"] >> features["has_breasts"]
S["feature_breasts_size"] >> features["breasts_size"]
S["feature_breasts_shape"] >> features["breasts_shape"]
S["feature_breasts_color"] >> features["breasts_color"]
- S["feature_breasts_fluid"] >> features["breasts_fluid"]
S["feature_breasts_producing"] >> features["breasts_producing"]
//vagina features
S["feature_has_vag"] >> features["has_vag"]
@@ -479,15 +477,11 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
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))
- 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)
- else
- hair_style = sanitize_inlist(hair_style, GLOB.hair_styles_female_list)
- facial_hair_style = sanitize_inlist(facial_hair_style, GLOB.facial_hair_styles_female_list)
- underwear = sanitize_inlist(underwear, GLOB.underwear_list)
+ hair_style = sanitize_inlist(hair_style, GLOB.hair_styles_list)
+ facial_hair_style = sanitize_inlist(facial_hair_style, GLOB.facial_hair_styles_list)
+ underwear = sanitize_inlist(underwear, GLOB.underwear_list)
+ undershirt = sanitize_inlist(undershirt, GLOB.undershirt_list)
undie_color = sanitize_hexcolor(undie_color, 3, FALSE, initial(undie_color))
- undershirt = sanitize_inlist(undershirt, GLOB.undershirt_list)
shirt_color = sanitize_hexcolor(shirt_color, 3, FALSE, initial(shirt_color))
socks = sanitize_inlist(socks, GLOB.socks_list)
socks_color = sanitize_hexcolor(socks_color, 3, FALSE, initial(socks_color))
diff --git a/code/modules/client/verbs/aooc.dm b/code/modules/client/verbs/aooc.dm
index 3f86a617e6..bc32d3c222 100644
--- a/code/modules/client/verbs/aooc.dm
+++ b/code/modules/client/verbs/aooc.dm
@@ -31,7 +31,7 @@ GLOBAL_VAR_INIT(normal_aooc_colour, "#ce254f")
if(QDELETED(src))
return
- msg = copytext(sanitize(msg), 1, MAX_MESSAGE_LEN)
+ msg = copytext_char(sanitize(msg), 1, MAX_MESSAGE_LEN)
var/raw_msg = msg
if(!msg)
diff --git a/code/modules/client/verbs/looc.dm b/code/modules/client/verbs/looc.dm
index 38cfd84b9c..075cabcbbb 100644
--- a/code/modules/client/verbs/looc.dm
+++ b/code/modules/client/verbs/looc.dm
@@ -13,7 +13,7 @@ GLOBAL_VAR_INIT(normal_looc_colour, "#6699CC")
if(!mob)
return
- msg = copytext(sanitize(msg), 1, MAX_MESSAGE_LEN)
+ msg = copytext_char(sanitize(msg), 1, MAX_MESSAGE_LEN)
if(!msg)
return
diff --git a/code/modules/client/verbs/ooc.dm b/code/modules/client/verbs/ooc.dm
index 8a233025a1..44290d1c2f 100644
--- a/code/modules/client/verbs/ooc.dm
+++ b/code/modules/client/verbs/ooc.dm
@@ -28,7 +28,7 @@ GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8")
if(QDELETED(src))
return
- msg = copytext(sanitize(msg), 1, MAX_MESSAGE_LEN)
+ msg = copytext_char(sanitize(msg), 1, MAX_MESSAGE_LEN)
var/raw_msg = msg
if(!msg)
@@ -36,7 +36,7 @@ GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8")
msg = emoji_parse(msg)
- if((copytext(msg, 1, 2) in list(".",";",":","#")) || (findtext(lowertext(copytext(msg, 1, 5)), "say")))
+ if((msg[1] in list(".",";",":","#")) || findtext_char(msg, "say", 1, 5))
if(alert("Your message \"[raw_msg]\" looks like it was meant for in game communication, say it in OOC?", "Meant for OOC?", "No", "Yes") != "Yes")
return
diff --git a/code/modules/client/verbs/ping.dm b/code/modules/client/verbs/ping.dm
index de19d0d52c..02c5b5a7fd 100644
--- a/code/modules/client/verbs/ping.dm
+++ b/code/modules/client/verbs/ping.dm
@@ -14,9 +14,9 @@
/client/verb/display_ping(time as num)
set instant = TRUE
set name = ".display_ping"
- to_chat(src, "Round trip ping took [round(pingfromtime(time),1)]ms")
+ to_chat(src, "Round trip ping took [round(pingfromtime(time),1)]ms (Avg: [round(avgping, 1)]ms])")
/client/verb/ping()
set name = "Ping"
set category = "OOC"
- winset(src, null, "command=.display_ping+[world.time+world.tick_lag*TICK_USAGE_REAL/100]")
\ No newline at end of file
+ winset(src, null, "command=.display_ping+[world.time+world.tick_lag*TICK_USAGE_REAL/100]")
diff --git a/code/modules/clothing/glasses/_glasses.dm b/code/modules/clothing/glasses/_glasses.dm
index 7df10b56f7..b60856001e 100644
--- a/code/modules/clothing/glasses/_glasses.dm
+++ b/code/modules/clothing/glasses/_glasses.dm
@@ -488,4 +488,47 @@
if(client && client.prefs.uses_glasses_colour && glasses_equipped)
add_client_colour(G.glass_colour_type)
else
- remove_client_colour(G.glass_colour_type)
\ No newline at end of file
+ remove_client_colour(G.glass_colour_type)
+
+/obj/item/clothing/glasses/debug
+ name = "debug glasses"
+ desc = "Medical, security and diagnostic hud. Alt click to toggle xray."
+ icon_state = "nvgmeson"
+ item_state = "nvgmeson"
+ flags_cover = GLASSESCOVERSEYES
+ darkness_view = 8
+ flash_protect = 2
+ lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
+ glass_colour_type = FALSE
+ clothing_flags = SCAN_REAGENTS
+ vision_flags = SEE_TURFS
+ var/list/hudlist = list(DATA_HUD_MEDICAL_ADVANCED, DATA_HUD_DIAGNOSTIC_ADVANCED, DATA_HUD_SECURITY_ADVANCED)
+ var/xray = FALSE
+
+/obj/item/clothing/glasses/debug/equipped(mob/user, slot)
+ . = ..()
+ if(slot != ITEM_SLOT_EYES)
+ return
+ if(ishuman(user))
+ for(var/hud in hudlist)
+ var/datum/atom_hud/H = GLOB.huds[hud]
+ H.add_hud_to(user)
+
+/obj/item/clothing/glasses/debug/dropped(mob/user)
+ . = ..()
+ if(ishuman(user))
+ for(var/hud in hudlist)
+ var/datum/atom_hud/H = GLOB.huds[hud]
+ H.remove_hud_from(user)
+
+/obj/item/clothing/glasses/debug/AltClick(mob/user)
+ . = ..()
+ if(ishuman(user))
+ if(xray)
+ vision_flags -= SEE_MOBS|SEE_OBJS
+ else
+ vision_flags += SEE_MOBS|SEE_OBJS
+ xray = !xray
+ var/mob/living/carbon/human/H = user
+ H.update_sight()
+
diff --git a/code/modules/clothing/glasses/vg_glasses.dm b/code/modules/clothing/glasses/vg_glasses.dm
deleted file mode 100644
index a51a03242c..0000000000
--- a/code/modules/clothing/glasses/vg_glasses.dm
+++ /dev/null
@@ -1,41 +0,0 @@
-
-//VG rip
-
-/obj/item/clothing/glasses/sunglasses/purple
- desc = "Strangely ancient technology used to help provide rudimentary eye cover. Enhanced shielding blocks many flashes, and the colored lenses let you see the world in purple."
- name = "purple sunglasses"
- icon_state = "sun_purple"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-
-/obj/item/clothing/glasses/sunglasses/star
- name = "star-shaped sunglasses"
- desc = "Novelty sunglasses, both lenses are in the shape of a star."
- icon_state = "sun_star"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-
-/obj/item/clothing/glasses/sunglasses/rockstar
- name = "red star-shaped sunglasses"
- desc = "Novelty sunglasses with a fancy silver frame and two red-tinted star-shaped lenses. You should probably stomp on them and get a pair of normal ones."
- icon_state = "sun_star_silver"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-
-/obj/item/clothing/glasses/gglasses
- name = "Green Glasses"
- desc = "Forest green glasses, like the kind you'd wear when hatching a nasty scheme."
- icon_state = "gglasses"
- item_state = "gglasses"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-
-/obj/item/clothing/glasses/welding/superior
- name = "superior welding goggles"
- desc = "Welding goggles made from more expensive materials, strangely smells like potatoes. Allows for better vision than normal goggles.."
- icon_state = "rwelding-g"
- item_state = "rwelding-g"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- actions_types = list(/datum/action/item_action/toggle)
- flash_protect = 2
- tint = 1
- visor_vars_to_toggle = VISOR_FLASHPROTECT | VISOR_TINT
- flags_cover = GLASSESCOVERSEYES
- visor_flags_inv = HIDEEYES
- glass_colour_type = /datum/client_colour/glass_colour/green
\ No newline at end of file
diff --git a/code/modules/clothing/gloves/_gloves.dm b/code/modules/clothing/gloves/_gloves.dm
index 8ab897f5d9..0729317eac 100644
--- a/code/modules/clothing/gloves/_gloves.dm
+++ b/code/modules/clothing/gloves/_gloves.dm
@@ -11,6 +11,8 @@
var/transfer_blood = 0
strip_delay = 20
equip_delay_other = 40
+ var/strip_mod = 1 //how much they alter stripping items time by, higher is quicker
+ var/strip_silence = FALSE //if it shows a warning when stripping
/obj/item/clothing/gloves/ComponentInitialize()
. = ..()
diff --git a/code/modules/clothing/gloves/boxing.dm b/code/modules/clothing/gloves/boxing.dm
index a76ff7c583..fe07cc7ab9 100644
--- a/code/modules/clothing/gloves/boxing.dm
+++ b/code/modules/clothing/gloves/boxing.dm
@@ -5,6 +5,7 @@
item_state = "boxing"
equip_delay_other = 60
species_exception = list(/datum/species/golem) // now you too can be a golem boxing champion
+ strip_mod = 0.5
/obj/item/clothing/gloves/boxing/green
icon_state = "boxinggreen"
diff --git a/code/modules/clothing/gloves/color.dm b/code/modules/clothing/gloves/color.dm
index b630b121a9..f4b04f5bfe 100644
--- a/code/modules/clothing/gloves/color.dm
+++ b/code/modules/clothing/gloves/color.dm
@@ -41,6 +41,7 @@
permeability_coefficient = 1
resistance_flags = NONE
transfer_prints = TRUE
+ strip_mod = 0.8
/obj/item/clothing/gloves/cut/family
desc = "The old gloves your great grandfather stole from Engineering, many moons ago. They've seen some tough times recently."
@@ -76,6 +77,7 @@
max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT
resistance_flags = NONE
var/can_be_cut = 1
+ strip_mod = 1.2
/obj/item/clothing/gloves/color/black/hos
item_color = "hosred" //Exists for washing machines. Is not different from black gloves in any way.
diff --git a/code/modules/clothing/gloves/miscellaneous.dm b/code/modules/clothing/gloves/miscellaneous.dm
index efe32a1473..9f4b89f607 100644
--- a/code/modules/clothing/gloves/miscellaneous.dm
+++ b/code/modules/clothing/gloves/miscellaneous.dm
@@ -10,6 +10,7 @@
equip_delay_other = 20
cold_protection = HANDS
min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT
+ strip_mod = 0.9
/obj/item/clothing/gloves/botanic_leather
name = "botanist's leather gloves"
@@ -23,6 +24,7 @@
max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT
resistance_flags = NONE
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 30)
+ strip_mod = 0.9
/obj/item/clothing/gloves/combat
name = "combat gloves"
@@ -38,6 +40,7 @@
max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT
resistance_flags = NONE
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 50)
+ strip_mod = 1.5
/obj/item/clothing/gloves/bracer
@@ -103,3 +106,15 @@
/obj/item/clothing/gloves/rapid/hug/attack_self(mob/user)
return FALSE
+
+/obj/item/clothing/gloves/thief
+ name = "black gloves"
+ desc = "Gloves made with completely frictionless, insulated cloth, easier to steal from people with."
+ icon_state = "thief"
+ item_state = "blackgloves"
+ siemens_coefficient = 0
+ permeability_coefficient = 0.05
+ strip_delay = 80
+ transfer_prints = FALSE
+ strip_mod = 5
+ strip_silence = TRUE
\ No newline at end of file
diff --git a/code/modules/clothing/gloves/ring.dm b/code/modules/clothing/gloves/ring.dm
new file mode 100644
index 0000000000..8354f13bd2
--- /dev/null
+++ b/code/modules/clothing/gloves/ring.dm
@@ -0,0 +1,32 @@
+/obj/item/clothing/gloves/ring
+ name = "gold ring"
+ desc = "A tiny gold ring, sized to wrap around a finger."
+ gender = NEUTER
+ w_class = WEIGHT_CLASS_TINY
+ icon = 'icons/obj/ring.dmi'
+ icon_state = "ringgold"
+ item_state = "gring"
+ body_parts_covered = 0
+ attack_verb = list("proposed")
+ transfer_prints = TRUE
+ strip_delay = 40
+
+/obj/item/clothing/gloves/ring/suicide_act(mob/living/carbon/user)
+ user.visible_message("\[user] is putting the [src] in [user.p_their()] mouth! It looks like [user] is trying to choke on the [src]!")
+ return OXYLOSS
+
+
+/obj/item/clothing/gloves/ring/diamond
+ name = "diamond ring"
+ desc = "An expensive ring, studded with a diamond. Cultures have used these rings in courtship for a millenia."
+ icon_state = "ringdiamond"
+ item_state = "dring"
+
+/obj/item/clothing/gloves/ring/diamond/attack_self(mob/user)
+ user.visible_message("\The [user] gets down on one knee, presenting \the [src].","You get down on one knee, presenting \the [src].")
+
+/obj/item/clothing/gloves/ring/silver
+ name = "silver ring"
+ desc = "A tiny silver ring, sized to wrap around a finger."
+ icon_state = "ringsilver"
+ item_state = "sring"
diff --git a/code/modules/clothing/gloves/vg_gloves.dm b/code/modules/clothing/gloves/vg_gloves.dm
deleted file mode 100644
index 6d7e775314..0000000000
--- a/code/modules/clothing/gloves/vg_gloves.dm
+++ /dev/null
@@ -1,70 +0,0 @@
-
-/obj/item/clothing/gloves/batmangloves
- desc = "Used for handling all things bat related."
- name = "batgloves"
- icon_state = "bmgloves"
- item_state = "bmgloves"
- item_color = "bmgloves"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-
-obj/item/clothing/gloves/bikergloves
- name = "Biker's Gloves"
- icon_state = "biker-gloves"
- item_state = "biker-gloves"
- item_color = "bikergloves"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-
-/obj/item/clothing/gloves/megagloves
- desc = "Uncomfortably bulky armored gloves."
- name = "DRN-001 Gloves"
- icon_state = "megagloves"
- item_state = "megagloves"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-
-/obj/item/clothing/gloves/protogloves
- desc = "Funcionally identical to the DRN-001 model's, but in red!"
- name = "Prototype Gloves"
- icon_state = "protogloves"
- item_state = "protogloves"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-
-/obj/item/clothing/gloves/megaxgloves
- desc = "An upgrade to the DRN-001's gauntlets, retains the uncomfortable armor, but comes with white gloves!"
- name = "Maverick Hunter gloves"
- icon_state = "megaxgloves"
- item_state = "megaxgloves"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-
-/obj/item/clothing/gloves/joegloves
- desc = "Large grey gloves, very similar to the Prototype's."
- name = "Sniper Gloves"
- icon_state = "joegloves"
- item_state = "joegloves"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-
-/obj/item/clothing/gloves/doomguy
- desc = ""
- name = "Doomguy's gloves"
- icon_state = "doom"
- item_state = "doom"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-
-/obj/item/clothing/gloves/anchor_arms
- name = "Anchor Arms"
- desc = "When you're a jerk, everybody loves you."
- icon_state = "anchorarms"
- item_state = "anchorarms"
-
-/obj/item/clothing/gloves/neorussian
- name = "neo-Russian gloves"
- desc = "Utilizes a non-slip technology that allows you to never drop your precious bottles of vodka."
- icon_state = "nr_gloves"
- item_state = "nr_gloves"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-
-/obj/item/clothing/gloves/neorussian/fingerless
- name = "neo-Russian fingerless gloves"
- desc = "For these tense combat situations when you just have to pick your nose."
- icon_state = "nr_fgloves"
- item_state = "nr_fgloves"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
\ No newline at end of file
diff --git a/code/modules/clothing/head/misc.dm b/code/modules/clothing/head/misc.dm
index 696ff1346c..189fb46e46 100644
--- a/code/modules/clothing/head/misc.dm
+++ b/code/modules/clothing/head/misc.dm
@@ -423,3 +423,15 @@
desc = "A security cowboy hat, perfect for any true lawman"
icon_state = "cowboyhat_sec"
item_state= "cowboyhat_sec"
+
+/obj/item/clothing/head/squatter_hat
+ name = "slav squatter hat"
+ icon_state = "squatter_hat"
+ item_state = "squatter_hat"
+ desc = "Cyka blyat."
+
+/obj/item/clothing/head/russobluecamohat
+ name = "russian blue camo beret"
+ desc = "A symbol of discipline, honor, and lots and lots of removal of some type of skewered food."
+ icon_state = "russobluecamohat"
+ item_state = "russobluecamohat"
diff --git a/code/modules/clothing/head/vg_hats.dm b/code/modules/clothing/head/vg_hats.dm
deleted file mode 100644
index 87f64baf13..0000000000
--- a/code/modules/clothing/head/vg_hats.dm
+++ /dev/null
@@ -1,155 +0,0 @@
-/obj/item/clothing/head/helmet/dredd
- name = "Judge Helmet"
- desc = "Judge, Jury, and Executioner."
- icon_state = "dredd-helmet"
- item_state = "dredd-helmet"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- armor = list(melee = 40, bullet = 30, laser = 30,energy = 30, bomb = 50, bio = 90, rad = 20, fire = 50, acid = 50)
- cold_protection = HEAD
- min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT
- heat_protection = HEAD
- max_heat_protection_temperature = SPACE_HELM_MAX_TEMP_PROTECT
- strip_delay = 80
- dog_fashion = null
-
-/obj/item/clothing/head/helmet/aviatorhelmet
- name = "Aviator Helmet"
- desc = "Help the Bombardier!"
- armor = list(melee = 25, bullet = 0, laser = 20, energy = 10, bomb = 10, bio = 0, rad = 0)
- item_state = "aviator_helmet"
- icon_state = "aviator_helmet"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-
-/obj/item/clothing/head/helmet/biker
- name = "Biker's Helmet"
- desc = "This helmet should protect you from russians and masked vigilantes."
- armor = list(melee = 25, bullet = 15, laser = 20, energy = 10, bomb = 10, bio = 0, rad = 0)
- icon_state = "biker_helmet"
- flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR
-
-/obj/item/clothing/head/helmet/richard
- name = "Richard"
- desc = "Do you like hurting people?"
- armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0)
- icon_state = "richard"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR
-
-/obj/item/clothing/head/helmet/megahelmet
- name = "DRN-001 Helmet"
- desc = "The helmet of the DRN-001 model. A simple, sturdy blue helmet."
- icon_state = "megahelmet"
- item_state = "megahelmet"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- siemens_coefficient = 1
-
-/obj/item/clothing/head/helmet/protohelmet
- name = "Prototype Helmet"
- desc = "Shiny red helmet with white accents and a built in shaded visor that does absolutely nothing, nothing but look rad as hell."
- icon_state = "protohelmet"
- item_state = "protohelmet"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- siemens_coefficient = 1
-
-/obj/item/clothing/head/helmet/megaxhelmet
- name = "Maverick Hunter Helmet"
- desc = "Heavily armored upgrade to the DRN-001 model's helmet, now comes with a pointless red crystal thing!"
- icon_state = "megaxhelmet"
- item_state = "megaxhelmet"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-
-/obj/item/clothing/head/helmet/joehelmet
- name = "Sniper Helmet"
- desc = "Helmet belonging to one of the many mass produced 'Joe' type robots."
- icon_state = "joehelmet"
- flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR
- item_state = "joehelmet"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-
-/obj/item/clothing/head/helmet/doomguy
- name = "Doomguy's helmet"
- desc = ""
- icon_state = "doom"
- flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR
- item_state = "doom"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- armor = list(melee = 50, bullet = 40, laser = 40,energy = 40, bomb = 5, bio = 0, rad = 0)
-
-/obj/item/clothing/head/helmet/neorussian
- name = "neo-Russian helmet"
- desc = "This piece of equipment can double as a pillow, a bowl, an emergency toilet, and sometimes as a helmet."
- icon_state = "nr_helmet"
- item_state = "nr_helmet"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-
-/obj/item/clothing/head/panzer
- name = "Panzer Cap"
- desc = "Command any mech in style."
- icon_state = "panzercap"
- item_state = "panzercap"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-
-/obj/item/clothing/head/russobluecamohat
- name = "russian blue camo beret"
- desc = "A symbol of discipline, honor, and lots and lots of removal of some type of skewered food."
- icon_state = "russobluecamohat"
- item_state = "russobluecamohat"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-
-/obj/item/clothing/head/russofurhat
- name = "russian fur hat"
- desc = "Russian winter got you down? Maybe your enemy, but not you!"
- icon_state = "russofurhat"
- item_state = "russofurhat"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-
-/obj/item/clothing/head/squatter_hat
- name = "slav squatter hat"
- icon_state = "squatter_hat"
- item_state = "squatter_hat"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- desc = "Cyka blyat."
-
-/obj/item/clothing/head/snake
- name = "snake head"
- desc = "Reenact acts of violence against reptiles, or sneak into a swamp unnoticed."
- icon_state = "snakehead"
- item_state = "snakehead"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-/obj/item/clothing/head/mummy_rags
- name = "mummy rags"
- desc = "Ancient rags taken off from some mummy."
- icon_state = "mummy"
- item_state = "mummy"
- item_color = "mummy"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- flags_inv = HIDEHAIR|HIDEFACE|HIDEEARS
-
-/obj/item/clothing/head/clownpiece
- name = "Clownpiece's jester hat"
- desc = "A purple polka-dotted jester's hat with yellow pompons."
- icon_state = "clownpiece"
- item_state = "clownpiece"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-
-/obj/item/clothing/head/mitre
- name = "mitre"
- desc = "A funny hat worn by extremely boring people."
- icon_state = "mitre"
- item_state = "mitre"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-
-/obj/item/clothing/head/tinfoil
- name = "tinfoil hat"
- desc = "There's no evidence that the security staff is NOT out to get you."
- icon_state = "foilhat"
- item_state = "paper"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- siemens_coefficient = 2
-
-/obj/item/clothing/head/celtic
- name = "\improper Celtic crown"
- desc = "According to legend, Celtic kings would use crowns like this one to shield their subjects from harsh winters back on Earth."
- icon_state = "celtic_crown"
- item_state = "celtic_crown"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
diff --git a/code/modules/clothing/masks/gasmask.dm b/code/modules/clothing/masks/gasmask.dm
index 1e8f26e7fe..e6c68f2662 100644
--- a/code/modules/clothing/masks/gasmask.dm
+++ b/code/modules/clothing/masks/gasmask.dm
@@ -39,6 +39,12 @@
/obj/item/clothing/mask/gas/welding/attack_self(mob/user)
weldingvisortoggle(user)
+/obj/item/clothing/mask/gas/welding/up
+
+/obj/item/clothing/mask/gas/welding/up/Initialize()
+ ..()
+ visor_toggling()
+
// ********************************************************************
diff --git a/code/modules/clothing/masks/vg_masks.dm b/code/modules/clothing/masks/vg_masks.dm
deleted file mode 100644
index 29b65a6b8e..0000000000
--- a/code/modules/clothing/masks/vg_masks.dm
+++ /dev/null
@@ -1,17 +0,0 @@
-/obj/item/clothing/mask/gas/clown_hat/wiz
- name = "purple clown wig and mask"
- desc = "Some pranksters are truly magical."
- icon_state = "wizzclown"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-
-/obj/item/clothing/mask/chapmask
- name = "venetian mask"
- desc = "A plain porcelain mask that covers the entire face. Standard attire for particularly unspeakable religions. The eyes are wide shut."
- icon_state = "chapmask"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-
-/obj/item/clothing/mask/neorussian
- name = "neo-Russian mask"
- desc = "Somehow, it makes you act and look way more polite than usual."
- icon_state = "nr_mask"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
diff --git a/code/modules/clothing/neck/_neck.dm b/code/modules/clothing/neck/_neck.dm
index 9a46b6b759..2b43460257 100644
--- a/code/modules/clothing/neck/_neck.dm
+++ b/code/modules/clothing/neck/_neck.dm
@@ -189,7 +189,7 @@
var/tagname = null
/obj/item/clothing/neck/petcollar/attack_self(mob/user)
- tagname = copytext(sanitize(input(user, "Would you like to change the name on the tag?", "Name your new pet", "Spot") as null|text),1,MAX_NAME_LEN)
+ tagname = stripped_input(user, "Would you like to change the name on the tag?", "Name your new pet", "Spot", MAX_NAME_LEN)
name = "[initial(name)] - [tagname]"
/obj/item/clothing/neck/petcollar/worn_overlays(isinhands, icon_file, style_flags = NONE)
diff --git a/code/modules/clothing/outfits/ert.dm b/code/modules/clothing/outfits/ert.dm
index 1532f50808..bdbac1165e 100644
--- a/code/modules/clothing/outfits/ert.dm
+++ b/code/modules/clothing/outfits/ert.dm
@@ -18,8 +18,9 @@
R.freqlock = TRUE
var/obj/item/card/id/W = H.wear_id
- W.registered_name = H.real_name
- W.update_label(W.registered_name, W.assignment)
+ if(W)
+ W.registered_name = H.real_name
+ W.update_label(W.registered_name, W.assignment)
/datum/outfit/ert/commander
name = "ERT Commander"
diff --git a/code/modules/clothing/outfits/standard.dm b/code/modules/clothing/outfits/standard.dm
index 4c8f9bdab2..ab2f89ffc4 100644
--- a/code/modules/clothing/outfits/standard.dm
+++ b/code/modules/clothing/outfits/standard.dm
@@ -272,7 +272,7 @@
/datum/outfit/wizard
name = "Blue Wizard"
- uniform = /obj/item/clothing/under/color/lightpurple
+ uniform = /obj/item/clothing/under/color/lightpurple/trackless
suit = /obj/item/clothing/suit/wizrobe
shoes = /obj/item/clothing/shoes/sandal/magic
ears = /obj/item/radio/headset
@@ -427,14 +427,27 @@
/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
+ suit = /obj/item/clothing/suit/space/hardsuit/syndi/elite/debug
+ glasses = /obj/item/clothing/glasses/debug
ears = /obj/item/radio/headset/headset_cent/commander
+ mask = /obj/item/clothing/mask/gas/welding/up
+ gloves = /obj/item/clothing/gloves/combat
+ belt = /obj/item/storage/belt/utility/chief/full
+ l_pocket = /obj/item/gun/magic/wand/resurrection/debug
+ r_pocket = /obj/item/gun/magic/wand/death/debug
+ shoes = /obj/item/clothing/shoes/magboots/advance/debug
+ id = /obj/item/card/id/debug
+ suit_store = /obj/item/tank/internals/oxygen
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)
+ box = /obj/item/storage/box/debugtools
+ internals_slot = ITEM_SLOT_SUITSTORE
+ backpack_contents = list(
+ /obj/item/melee/transforming/energy/axe=1,\
+ /obj/item/storage/part_replacer/bluespace/tier4=1,\
+ /obj/item/debug/human_spawner=1,\
+ )
+
+/datum/outfit/debug/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
+ var/obj/item/card/id/W = H.wear_id
+ W.registered_name = H.real_name
+ W.update_label()
diff --git a/code/modules/clothing/shoes/magboots.dm b/code/modules/clothing/shoes/magboots.dm
index 952dbe0ad8..1acb7628a3 100644
--- a/code/modules/clothing/shoes/magboots.dm
+++ b/code/modules/clothing/shoes/magboots.dm
@@ -30,8 +30,9 @@
magpulse = !magpulse
icon_state = "[magboot_state][magpulse]"
to_chat(user, "You [magpulse ? "enable" : "disable"] the mag-pulse traction system.")
- user.update_inv_shoes() //so our mob-overlays update
- user.update_gravity(user.has_gravity())
+ if(user)
+ user.update_inv_shoes() //so our mob-overlays update
+ user.update_gravity(user.has_gravity())
for(var/X in actions)
var/datum/action/A = X
A.UpdateButtonIcon()
@@ -52,8 +53,42 @@
slowdown_active = SHOES_SLOWDOWN
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
+/obj/item/clothing/shoes/magboots/advance/debug
+
+/obj/item/clothing/shoes/magboots/advance/debug/Initialize()
+ attack_self(src)
+
/obj/item/clothing/shoes/magboots/syndie
desc = "Reverse-engineered magnetic boots that have a heavy magnetic pull. Property of Gorlex Marauders."
name = "blood-red magboots"
icon_state = "syndiemag0"
magboot_state = "syndiemag"
+
+/obj/item/clothing/shoes/magboots/crushing
+ desc = "Normal looking magboots that are altered to increase magnetic pull to crush anything underfoot."
+
+/obj/item/clothing/shoes/magboots/crushing/proc/crush(mob/living/user)
+ if (!isturf(user.loc) || !magpulse)
+ return
+ var/turf/T = user.loc
+ for (var/mob/living/A in T)
+ if (A != user && A.lying)
+ A.adjustBruteLoss(rand(10,13))
+ to_chat(A,"[user]'s magboots press down on you, crushing you!")
+ A.emote("scream")
+
+/obj/item/clothing/shoes/magboots/crushing/attack_self(mob/user)
+ . = ..()
+ if (magpulse)
+ RegisterSignal(user, COMSIG_MOVABLE_MOVED,.proc/crush)
+ else
+ UnregisterSignal(user,COMSIG_MOVABLE_MOVED)
+
+/obj/item/clothing/shoes/magboots/crushing/equipped(mob/user,slot)
+ . = ..()
+ if (slot == SLOT_SHOES && magpulse)
+ RegisterSignal(user, COMSIG_MOVABLE_MOVED,.proc/crush)
+
+/obj/item/clothing/shoes/magboots/crushing/dropped(mob/user)
+ . = ..()
+ UnregisterSignal(user,COMSIG_MOVABLE_MOVED)
diff --git a/code/modules/clothing/shoes/vg_shoes.dm b/code/modules/clothing/shoes/vg_shoes.dm
deleted file mode 100644
index 53b093dc72..0000000000
--- a/code/modules/clothing/shoes/vg_shoes.dm
+++ /dev/null
@@ -1,124 +0,0 @@
-
-/obj/item/clothing/shoes/leather
- name = "leather shoes"
- desc = "A sturdy pair of leather shoes."
- icon_state = "leather"
- item_color = "leather"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/shoes/magboots/deathsquad
- desc = "Very expensive and advanced magnetic boots, used only by the elite during extravehicular activity to ensure the user remains safely attached to the vehicle."
- name = "deathsquad magboots"
- icon_state = "DS-magboots0"
- magboot_state = "DS-magboots"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
- mutantrace_variation = NONE
-
-/obj/item/clothing/shoes/magboots/atmos
- desc = "Magnetic boots, often used during extravehicular activity to ensure the user remains safely attached to the vehicle. These are painted in the colors of an atmospheric technician."
- name = "atmospherics magboots"
- icon_state = "atmosmagboots0"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- magboot_state = "atmosmagboots"
- resistance_flags = FIRE_PROOF
- mutantrace_variation = NONE
-
-/obj/item/clothing/shoes/simonshoes
- name = "Simon's Shoes"
- desc = "Simon's Shoes."
- icon_state = "simonshoes"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/shoes/kneesocks
- name = "kneesocks"
- desc = "A pair of girly knee-high socks."
- icon_state = "kneesock"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/shoes/jestershoes
- name = "Jester Shoes"
- desc = "As worn by the clowns of old."
- icon_state = "jestershoes"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/shoes/aviatorboots
- name = "Aviator Boots"
- desc = "Boots suitable for just about any occasion."
- icon_state = "aviator_boots"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/shoes/libertyshoes
- name = "Liberty Shoes"
- desc = "Freedom isn't free, neither were these shoes."
- icon_state = "libertyshoes"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/shoes/megaboots
- name = "DRN-001 Boots"
- desc = "Large armored boots, very weak to large spikes."
- icon_state = "megaboots"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/shoes/protoboots
- name = "Prototype Boots"
- desc = "Functionally identical to the DRN-001 model's boots, but in red."
- icon_state = "protoboots"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/shoes/megaxboots
- name = "Maverick Hunter boots"
- desc = "Regardless of how much stronger these boots are than the DRN-001 model's, they're still extremely easy to pierce with a large spike."
- icon_state = "megaxboots"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/shoes/joeboots
- name = "Sniper Boots"
- desc = "Nearly identical to the Prototype's boots, except in black."
- icon_state = "joeboots"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/shoes/doomguy
- name = "Doomguy's boots"
- desc = "If you look closely, you might see skull fragments still buried in these boots."
- icon_state = "doom"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/shoes/rottenshoes
- name = "rotten shoes"
- desc = "These shoes seem perfect for sneaking around."
- icon_state = "rottenshoes"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/shoes/sandal/slippers
- name = "magic slippers"
- icon_state = "slippers"
- desc = "For the wizard that puts comfort first. Who's going to laugh?"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/shoes/slippers_worn
- name = "worn bunny slippers"
- desc = "Fluffy..."
- icon_state = "slippers_worn"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/shoes/jackboots/neorussian
- name = "neo-Russian boots"
- desc = "Tovarish, no one will realize you stepped on a pile of shit if your pair already looks like shit."
- icon_state = "nr_boots"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- mutantrace_variation = NONE
\ No newline at end of file
diff --git a/code/modules/clothing/spacesuits/hardsuit.dm b/code/modules/clothing/spacesuits/hardsuit.dm
index c9753b3053..fab41c9876 100644
--- a/code/modules/clothing/spacesuits/hardsuit.dm
+++ b/code/modules/clothing/spacesuits/hardsuit.dm
@@ -269,7 +269,7 @@
item_state = "syndie_helm"
item_color = "syndi"
armor = list("melee" = 40, "bullet" = 50, "laser" = 30, "energy" = 15, "bomb" = 35, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 90)
- on = TRUE
+ on = FALSE
var/obj/item/clothing/suit/space/hardsuit/syndi/linkedsuit = null
actions_types = list(/datum/action/item_action/toggle_helmet_mode)
visor_flags_inv = HIDEMASK|HIDEEYES|HIDEFACE|HIDEFACIALHAIR
@@ -367,6 +367,12 @@
on = FALSE
resistance_flags = FIRE_PROOF | ACID_PROOF
+/obj/item/clothing/head/helmet/space/hardsuit/syndi/elite/debug
+
+/obj/item/clothing/head/helmet/space/hardsuit/syndi/elite/debug/Initialize()
+ . = ..()
+ soundloop.volume = 0
+
/obj/item/clothing/suit/space/hardsuit/syndi/elite
name = "elite syndicate hardsuit"
desc = "An elite version of the syndicate hardsuit, with improved armour and fireproofing. It is in travel mode."
@@ -380,6 +386,9 @@
resistance_flags = FIRE_PROOF | ACID_PROOF
mutantrace_variation = STYLE_DIGITIGRADE|STYLE_SNEK_TAURIC|STYLE_PAW_TAURIC
+/obj/item/clothing/suit/space/hardsuit/syndi/elite/debug
+ helmettype = /obj/item/clothing/head/helmet/space/hardsuit/syndi/elite/debug
+ slowdown = 0
//The Owl Hardsuit
/obj/item/clothing/head/helmet/space/hardsuit/syndi/owl
@@ -693,6 +702,29 @@
listeningTo = null
return ..()
+/obj/item/clothing/head/helmet/space/hardsuit/soviet
+ name = "soviet hardhelmet"
+ desc = "Crafted with the pride of the proletariat. The vengeful gaze of the visor roots out all fascists and capitalists."
+ item_state = "rig0-soviet"
+ item_color = "soviet"
+ icon_state = "rig0-soviet"
+ armor = list("melee" = 40, "bullet" = 30, "laser" = 30, "energy" = 15, "bomb" = 35, "bio" = 100, "rad" = 20, "fire" = 50, "acid" = 75)
+ mutantrace_variation = NONE
+
+/obj/item/clothing/suit/space/hardsuit/soviet
+ name = "soviet hardsuit"
+ desc = "Crafted with the pride of the proletariat. The last thing the enemy sees is the bottom of this armor's boot."
+ item_state = "rig-soviet"
+ icon_state = "rig-soviet"
+ slowdown = 0.8
+ armor = list("melee" = 40, "bullet" = 30, "laser" = 30, "energy" = 15, "bomb" = 35, "bio" = 100, "rad" = 20, "fire" = 50, "acid" = 75)
+ helmettype = /obj/item/clothing/head/helmet/space/hardsuit/soviet
+ mutantrace_variation = NONE
+
+/obj/item/clothing/suit/space/hardsuit/soviet/Initialize()
+ . = ..()
+ allowed = GLOB.security_hardsuit_allowed
+
/////////////SHIELDED//////////////////////////////////
/obj/item/clothing/suit/space/hardsuit/shielded
diff --git a/code/modules/clothing/spacesuits/vg_spess.dm b/code/modules/clothing/spacesuits/vg_spess.dm
deleted file mode 100644
index 517539f3af..0000000000
--- a/code/modules/clothing/spacesuits/vg_spess.dm
+++ /dev/null
@@ -1,120 +0,0 @@
-
- //VG Ports
-
-/obj/item/clothing/head/helmet/space/hardsuit/soviet
- name = "soviet hardhelmet"
- desc = "Crafted with the pride of the proletariat. The vengeful gaze of the visor roots out all fascists and capitalists."
- item_state = "hardsuit0-soviet"
- icon_state = "hardsuit0-soviet"
- armor = list(melee = 40, bullet = 30, laser = 30, energy = 15, bomb = 35, bio = 100, rad = 20)
- item_color = "soviet"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-
-/obj/item/clothing/suit/space/hardsuit/soviet
- name = "soviet hardsuit"
- desc = "Crafted with the pride of the proletariat. The last thing the enemy sees is the bottom of this armor's boot."
- item_state = "hardsuit-soviet"
- icon_state = "hardsuit-soviet"
- slowdown = 1
- armor = list(melee = 40, bullet = 30, laser = 30, energy = 15, bomb = 35, bio = 100, rad = 20)
- allowed = list(/obj/item/gun,/obj/item/flashlight,/obj/item/tank,/obj/item/melee/)
- helmettype = /obj/item/clothing/head/helmet/space/hardsuit/soviet
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-
-/obj/item/clothing/head/helmet/space/hardsuit/knight
- name = "Space-Knight helm"
- desc = "A well polished helmet belonging to a Space-Knight. Favored by space-jousters for its ability to stay on tight after being launched from a mass driver."
- icon_state = "hardsuit0-knight"
- item_state = "hardsuit0-knight"
- armor = list(melee = 60, bullet = 40, laser = 40,energy = 30, bomb = 50, bio = 100, rad = 60)
- max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
- item_color="knight"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-
-/obj/item/clothing/suit/space/hardsuit/knight
- name = "Space-Knight armour"
- desc = "A well polished set of armour belonging to a Space-Knight. Maidens Rescued in Space: 100, Maidens who have slept with me in Space: 0."
- icon_state = "hardsuit-knight"
- item_state = "hardsuit-knight"
- slowdown = 1
- allowed = list(/obj/item/gun,/obj/item/melee/baton,/obj/item/tank,/obj/item/shield/energy,/obj/item/claymore)
- armor = list(melee = 60, bullet = 40, laser = 40,energy = 30, bomb = 50, bio = 100, rad = 60)
- max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
- siemens_coefficient = 0.5
- helmettype = /obj/item/clothing/head/helmet/space/hardsuit/knight
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-
-/obj/item/clothing/head/helmet/space/hardsuit/knight/black
- name = "Black Knight's helm"
- desc = "An ominous black helmet with a gold trim. The small viewports create an intimidating look, while also making it nearly impossible to see anything."
- icon_state = "hardsuit0-blackknight"
- item_state = "hardsuit0-blackknight"
- armor = list(melee = 70, bullet = 65, laser = 50,energy = 25, bomb = 60, bio = 100, rad = 60)
- item_color="blackknight"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-
-/obj/item/clothing/suit/space/hardsuit/knight/black
- name = "Black Knight's armour"
- desc = "An ominous black suit of armour with a gold trim. Surprisingly good at preventing accidental loss of limbs."
- icon_state = "hardsuit-blackknight"
- item_state = "hardsuit-blackknight"
- armor = list(melee = 70, bullet = 65, laser = 50,energy = 25, bomb = 60, bio = 100, rad = 60)
- helmettype = /obj/item/clothing/head/helmet/space/hardsuit/knight/black
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-
-/obj/item/clothing/head/helmet/space/hardsuit/knight/solaire
- name = "Solar helm"
- desc = "A simple helmet. 'Made in Astora' is inscribed on the back."
- icon_state = "hardsuit0-solaire"
- item_state = "hardsuit0-solaire"
- armor = list(melee = 60, bullet = 65, laser = 90,energy = 30, bomb = 60, bio = 100, rad = 100)
- item_color="solaire"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-
-/obj/item/clothing/suit/space/hardsuit/knight/solaire
- name = "Solar armour"
- desc = "A solar powered hardsuit with a fancy insignia on the chest. Perfect for stargazers and adventurers alike."
- icon_state = "hardsuit-solaire"
- item_state = "hardsuit-solaire"
- armor = list(melee = 60, bullet = 65, laser = 90,energy = 30, bomb = 60, bio = 100, rad = 100)
- helmettype = /obj/item/clothing/head/helmet/space/hardsuit/knight/solaire
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-
-/obj/item/clothing/head/helmet/space/hardsuit/t51b
- name = "T-51b Power Armor"
- desc = "Relic of a bygone era, the T-51b is powered by a TX-28 MicroFusion Pack, which holds enough fuel to power its internal hydraulics for a century!"
- icon_state = "hardsuit0-t51b"
- item_state = "hardsuit0-t51b"
- armor = list(melee = 35, bullet = 35, laser = 40, energy = 40, bomb = 80, bio = 100, rad = 100)
- item_color="t51b"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-
-/obj/item/clothing/suit/space/hardsuit/t51b
- name = "T-51b Power Armor"
- desc = "Relic of a bygone era, the T-51b is powered by a TX-28 MicroFusion Pack, which holds enough fuel to power its internal hydraulics for a century!"
- icon_state = "hardsuit-t51b"
- item_state = "hardsuit-t51b"
- armor = list(melee = 35, bullet = 35, laser = 40, energy = 40, bomb = 80, bio = 100, rad = 100)
- helmettype = /obj/item/clothing/head/helmet/space/hardsuit/t51b
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-
-
-/obj/item/clothing/head/helmet/space/bomberman
- name = "Bomberman head"
- desc = "Terrorism has never looked so adorable."
- icon_state = "bomberman"
- item_state = "bomberman"
- armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 100, bio = 0, rad = 0)
- siemens_coefficient = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
-
-obj/item/clothing/suit/space/bomberman
- name = "Bomberman's suit"
- desc = "Doesn't actually make you immune to bombs!"
- icon_state = "bomberman"
- item_state = "bomberman"
- slowdown = 0
- armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 100, bio = 0, rad = 0)
- siemens_coefficient = 0
- max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
\ No newline at end of file
diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm
index e536d307ef..4591ba8b4e 100644
--- a/code/modules/clothing/suits/armor.dm
+++ b/code/modules/clothing/suits/armor.dm
@@ -15,6 +15,13 @@
if(!allowed)
allowed = GLOB.security_vest_allowed
+/obj/item/clothing/suit/armor/navyblue
+ name = "security officer's jacket"
+ desc = "This jacket is for those special occasions when a security officer isn't required to wear their armor."
+ icon_state = "officerbluejacket"
+ item_state = "officerbluejacket"
+ body_parts_covered = CHEST|ARMS
+
/obj/item/clothing/suit/armor/vest
name = "armor vest"
desc = "A slim Type I armored vest that provides decent protection against most types of damage."
@@ -52,8 +59,17 @@
heat_protection = CHEST|GROIN|LEGS|ARMS
strip_delay = 80
+/obj/item/clothing/suit/armor/hos/navyblue
+ name = "head of security's jacket"
+ desc = "This piece of clothing was specifically designed for asserting superior authority."
+ icon_state = "hosbluejacket"
+ item_state = "hosbluejacket"
+ body_parts_covered = CHEST|ARMS
+ cold_protection = CHEST|ARMS
+ heat_protection = CHEST|ARMS
+
/obj/item/clothing/suit/armor/hos/trenchcoat
- name = "armored trenchoat"
+ name = "armored trenchcoat"
desc = "A trenchcoat enhanced with a special lightweight kevlar. The epitome of tactical plainclothes."
icon_state = "hostrench"
item_state = "hostrench"
@@ -78,6 +94,13 @@
desc = "A red jacket with silver rank pips and body armor strapped on top."
icon_state = "warden_jacket"
+/obj/item/clothing/suit/armor/vest/warden/navyblue
+ name = "warden's jacket"
+ desc = "Perfectly suited for the warden that wants to leave an impression of style on those who visit the brig."
+ icon_state = "wardenbluejacket"
+ item_state = "wardenbluejacket"
+ body_parts_covered = CHEST|ARMS
+
/obj/item/clothing/suit/armor/vest/leather
name = "security overcoat"
desc = "Lightly armored leather overcoat meant as casual wear for high-ranking officers. Bears the crest of Nanotrasen Security."
diff --git a/code/modules/clothing/suits/cloaks.dm b/code/modules/clothing/suits/cloaks.dm
index b8287c7f4c..7d56fbe6dd 100644
--- a/code/modules/clothing/suits/cloaks.dm
+++ b/code/modules/clothing/suits/cloaks.dm
@@ -81,7 +81,7 @@
heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
- resistance_flags = FIRE_PROOF | ACID_PROOF
+ resistance_flags = FIRE_PROOF | ACID_PROOF | GOLIATH_RESISTANCE
/obj/item/clothing/head/hooded/cloakhood/drake
name = "drake helm"
@@ -90,4 +90,4 @@
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_MAX_TEMP_PROTECT
- resistance_flags = FIRE_PROOF | ACID_PROOF
+ resistance_flags = FIRE_PROOF | ACID_PROOF | GOLIATH_RESISTANCE
diff --git a/code/modules/clothing/suits/jobs.dm b/code/modules/clothing/suits/jobs.dm
index 88bafc842b..48fd8ccf1c 100644
--- a/code/modules/clothing/suits/jobs.dm
+++ b/code/modules/clothing/suits/jobs.dm
@@ -155,28 +155,6 @@
blood_overlay_type = "armor" //it's the less thing that I can put here
body_parts_covered = NONE
-//Security
-/obj/item/clothing/suit/security/officer
- name = "security officer's jacket"
- desc = "This jacket is for those special occasions when a security officer isn't required to wear their armor."
- icon_state = "officerbluejacket"
- item_state = "officerbluejacket"
- body_parts_covered = CHEST|ARMS
-
-/obj/item/clothing/suit/security/warden
- name = "warden's jacket"
- desc = "Perfectly suited for the warden that wants to leave an impression of style on those who visit the brig."
- icon_state = "wardenbluejacket"
- item_state = "wardenbluejacket"
- body_parts_covered = CHEST|ARMS
-
-/obj/item/clothing/suit/security/hos
- name = "head of security's jacket"
- desc = "This piece of clothing was specifically designed for asserting superior authority."
- icon_state = "hosbluejacket"
- item_state = "hosbluejacket"
- body_parts_covered = CHEST|ARMS
-
//Surgeon
/obj/item/clothing/suit/apron/surgical
name = "surgical apron"
diff --git a/code/modules/clothing/suits/miscellaneous.dm b/code/modules/clothing/suits/miscellaneous.dm
index 127d2e4f04..4d3e8acb28 100644
--- a/code/modules/clothing/suits/miscellaneous.dm
+++ b/code/modules/clothing/suits/miscellaneous.dm
@@ -317,7 +317,7 @@
flags_cover = HEADCOVERSEYES
flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR
-/obj/item/clothing/suit/security/officer/russian
+/obj/item/clothing/suit/armor/navyblue/russian
name = "\improper Russian officer's jacket"
desc = "This jacket is for those special occasions when a russian officer isn't required to wear their armor."
icon_state = "officertanjacket"
@@ -325,17 +325,16 @@
body_parts_covered = CHEST|ARMS
/obj/item/clothing/suit/ran
- name = "Shikigami costume"
+ name = "shikigami costume"
desc = "A costume that looks like a certain shikigami, is super fluffy."
icon_state = "ran_suit"
item_state = "ran_suit"
body_parts_covered = CHEST|GROIN|LEGS
flags_inv = HIDEJUMPSUIT|HIDETAUR
heat_protection = CHEST|GROIN|LEGS //fluffy tails!
-//2061
/obj/item/clothing/head/ran
- name = "Shikigami hat"
+ name = "shikigami hat"
desc = "A hat that looks like it keeps any fluffy ears contained super warm, has little charms over it."
icon_state = "ran_hat"
item_state = "ran_hat"
@@ -704,8 +703,9 @@
name = "cosmic winter coat"
icon_state = "coatcosmic"
item_state = "coatcosmic"
- allowed = list(/obj/item/flashlight)
hoodtype = /obj/item/clothing/head/hooded/winterhood/cosmic
+ light_power = 1.8
+ light_range = 1.2
/obj/item/clothing/head/hooded/winterhood/cosmic
icon_state = "winterhood_cosmic"
@@ -880,7 +880,6 @@
blood_overlay_type = "armor"
body_parts_covered = CHEST
resistance_flags = NONE
- mutantrace_variation = NONE
armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 5, "bio" = 0, "rad" = 0, "fire" = -5, "acid" = -15) //nylon sucks against acid
/obj/item/clothing/suit/assu_suit
@@ -922,3 +921,78 @@
/obj/item/clothing/head/hooded/winterhood/christmashoodrg
icon_state = "christmashoodrg"
+
+/obj/item/clothing/suit/hooded/wintercoat/polychromic
+ name = "polychromic winter coat"
+ icon_state = "coatpoly"
+ item_state = "coatpoly"
+ item_color = "coatpoly"
+ hoodtype = /obj/item/clothing/head/hooded/winterhood/polychromic
+ hasprimary = TRUE
+ hassecondary = TRUE
+ hastertiary = TRUE
+ primary_color = "#6A6964"
+ secondary_color = "#C4B8A6"
+ tertiary_color = "#0000FF"
+
+/obj/item/clothing/head/hooded/winterhood/polychromic
+ icon_state = "winterhood_poly"
+ item_color = "winterhood_poly"
+ item_state = "winterhood_poly"
+
+/obj/item/clothing/head/hooded/winterhood/polychromic/worn_overlays(isinhands, icon_file, style_flags = NONE) //this is where the main magic happens.
+ . = ..()
+ if(suit.hasprimary | suit.hassecondary)
+ if(!isinhands) //prevents the worn sprites from showing up if you're just holding them
+ if(suit.hasprimary) //checks if overlays are enabled
+ var/mutable_appearance/primary_worn = mutable_appearance(icon_file, "[item_color]-primary") //automagical sprite selection
+ primary_worn.color = suit.primary_color //colors the overlay
+ . += primary_worn //adds the overlay onto the buffer list to draw on the mob sprite.
+ if(suit.hassecondary)
+ var/mutable_appearance/secondary_worn = mutable_appearance(icon_file, "[item_color]-secondary")
+ secondary_worn.color = suit.secondary_color
+ . += secondary_worn
+
+/obj/item/clothing/suit/hooded/wintercoat/polychromic/worn_overlays(isinhands, icon_file, style_flags = NONE) //this is where the main magic happens.
+ . = ..()
+ if(hasprimary | hassecondary | hastertiary)
+ if(!isinhands) //prevents the worn sprites from showing up if you're just holding them
+ if(hasprimary) //checks if overlays are enabled
+ var/mutable_appearance/primary_worn = mutable_appearance(icon_file, "[item_color]-primary[suittoggled ? "_t" : ""]") //automagical sprite selection
+ primary_worn.color = primary_color //colors the overlay
+ . += primary_worn //adds the overlay onto the buffer list to draw on the mob sprite.
+ if(hassecondary)
+ var/mutable_appearance/secondary_worn = mutable_appearance(icon_file, "[item_color]-secondary[suittoggled ? "_t" : ""]")
+ secondary_worn.color = secondary_color
+ . += secondary_worn
+ if(hastertiary)
+ var/mutable_appearance/tertiary_worn = mutable_appearance(icon_file, "[item_color]-tertiary[suittoggled ? "_t" : ""]")
+ tertiary_worn.color = tertiary_color
+ . += tertiary_worn
+
+/obj/item/clothing/suit/hooded/wintercoat/AltClick(mob/user)
+ . = ..()
+ if(!istype(user) || !user.canUseTopic(src, BE_CLOSE, ismonkey(user)))
+ return
+ if(hasprimary | hassecondary | hastertiary)
+ var/choice = input(user,"polychromic thread options", "Clothing Recolor") as null|anything in list("[hasprimary ? "Primary Color" : ""]", "[hassecondary ? "Secondary Color" : ""]", "[hastertiary ? "Tertiary Color" : ""]") //generates a list depending on the enabled overlays
+ switch(choice) //Lets the list's options actually lead to something
+ if("Primary Color")
+ var/primary_color_input = input(usr,"","Choose Primary Color",primary_color) as color|null //color input menu, the "|null" adds a cancel button to it.
+ if(primary_color_input) //Checks if the color selected is NULL, rejects it if it is NULL.
+ primary_color = sanitize_hexcolor(primary_color_input, desired_format=6, include_crunch=1) //formats the selected color properly
+ update_icon() //updates the item icon
+ user.regenerate_icons() //updates the worn icon. Probably a bad idea, but it works.
+ if("Secondary Color")
+ var/secondary_color_input = input(usr,"","Choose Secondary Color",secondary_color) as color|null
+ if(secondary_color_input)
+ secondary_color = sanitize_hexcolor(secondary_color_input, desired_format=6, include_crunch=1)
+ update_icon()
+ user.regenerate_icons()
+ if("Tertiary Color")
+ var/tertiary_color_input = input(usr,"","Choose Tertiary Color",tertiary_color) as color|null
+ if(tertiary_color_input)
+ tertiary_color = sanitize_hexcolor(tertiary_color_input, desired_format=6, include_crunch=1)
+ update_icon()
+ user.regenerate_icons()
+ return TRUE
diff --git a/code/modules/clothing/suits/reactive_armour.dm b/code/modules/clothing/suits/reactive_armour.dm
index 0d2f68f580..dff62dd2c3 100644
--- a/code/modules/clothing/suits/reactive_armour.dm
+++ b/code/modules/clothing/suits/reactive_armour.dm
@@ -215,14 +215,24 @@
playsound(get_turf(owner),'sound/magic/repulse.ogg', 100, 1)
owner.visible_message("[src] blocks [attack_text], converting the attack into a wave of force!")
var/turf/T = get_turf(owner)
- var/list/thrown_items = list()
- for(var/atom/movable/A in range(T, 7))
- if(A == owner || A.anchored || thrown_items[A])
+ var/list/cachedrange = range(T, 7) - owner
+ var/safety = 50
+ var/list/to_throw = list()
+ for(var/mob/living/L in cachedrange)
+ if(L.move_resist > MOVE_FORCE_EXTREMELY_STRONG)
continue
- var/throwtarget = get_edge_target_turf(T, get_dir(T, get_step_away(A, T)))
- A.throw_at(throwtarget,10,1)
- thrown_items[A] = A
-
+ to_throw += L
+ for(var/obj/O in cachedrange)
+ if(O.anchored)
+ continue
+ to_throw += O
+ for(var/i in to_throw)
+ if(!safety)
+ break
+ var/atom/movable/AM = i
+ var/throwtarget = get_edge_target_turf(T, get_dir(T, get_step_away(AM, T)))
+ AM.throw_at(throwtarget,10,1)
+ safety--
reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration
return 1
diff --git a/code/modules/clothing/suits/vg_suits.dm b/code/modules/clothing/suits/vg_suits.dm
deleted file mode 100644
index be6cd9938e..0000000000
--- a/code/modules/clothing/suits/vg_suits.dm
+++ /dev/null
@@ -1,138 +0,0 @@
-
-/obj/item/clothing/suit/armor/xcomsquaddie
- name = "Squaddie Armor"
- desc = "A suit of armor with heavy padding to protect against projectile and laser attacks. Distributed to shadow organization squaddies."
- icon_state = "xcomarmor2"
- item_state = "xcomarmor2"
- body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
- armor = list(melee = 50, bullet = 50, laser = 50, energy = 30, bomb = 60, bio = 30, rad = 20)
- siemens_coefficient = 0.5
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/suit.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/suit/armor/xcomsquaddie/dredd
- name = "Judge Armor"
- desc = "A large suit of heavy armor, fit for a Judge."
- icon_state = "dredd-suit"
- item_state = "dredd-suit"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/suit.dmi'
- mutantrace_variation = NONE
-
-
-/obj/item/clothing/suit/armor/xcomarmor
- name = "Mysterious Armor"
- desc = "A suit of armor with heavy plating to protect against melee attacks. Distributed to shadow organization squaddies."
- icon_state = "xcomarmor1"
- item_state = "xcomarmor1"
- body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
- armor = list(melee = 50, bullet = 50, laser = 50, energy = 30, bomb = 60, bio = 30, rad = 20)
- slowdown = 1
- siemens_coefficient = 0.5
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/suit.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/suit/armor/vest/neorussian
- name = "neo-Russian vest"
- desc = "The narkotiki camo pattern will come useful for botany raids."
- icon_state = "nr_vest"
- item_state = "nr_vest"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/suit.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/suit/armor/doomguy
- name = "Doomguy's armor"
- desc = ""
- icon_state = "doom"
- item_state = "doom"
- body_parts_covered = CHEST|GROIN
- slowdown = 0
- armor = list(melee = 50, bullet = 30, laser = 20, energy = 20, bomb = 30, bio = 0, rad = 0)
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/suit.dmi'
- mutantrace_variation = NONE
-
-
-/obj/item/clothing/suit/kaminacape
- name = "Kamina's Cape"
- desc = "Don't believe in yourself, dumbass. Believe in me. Believe in the Kamina who believes in you."
- icon_state = "kaminacape"
- body_parts_covered = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/suit.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/suit/officercoat
- name = "Officer's Coat"
- desc = "Ein Mantel gemacht, um die Juden zu bestrafen."
- icon_state = "officersuit"
- body_parts_covered = CHEST|GROIN|LEGS|ARMS|HANDS
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/suit/soldiercoat
- name = "Soldier's Coat"
- desc = "Und das heit: Erika."
- icon_state = "soldiersuit"
- body_parts_covered = CHEST|GROIN|LEGS|ARMS|HANDS
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/suit/russofurcoat
- name = "russian fur coat"
- desc = "Let the land do the fighting for you."
- icon_state = "russofurcoat"
- allowed = list(/obj/item/gun)
- body_parts_covered = CHEST|GROIN|LEGS|ARMS|HANDS
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/suit.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/suit/doshjacket
- name = "Plasterer's Jacket"
- desc = "Perfect for doing up the house."
- icon_state = "doshjacket"
- body_parts_covered = CHEST|GROIN|ARMS
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/suit.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/suit/maidapron
- name = "Apron"
- desc = "Simple white apron."
- icon_state = "maidapron"
- body_parts_covered = CHEST|GROIN
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/suit.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/suit/clownpiece
- name = "small fairy wings"
- desc = "Some small and translucid insect-like wings."
- icon_state = "clownpiece"
- body_parts_covered = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/suit.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/suit/clownpiece/flying
- name = "small fairy wings"
- desc = "Some small and translucid insect-like wings. Looks like these are the real deal!"
- icon_state = "clownpiece-fly"
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/suit.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/suit/raincoat
- name = "Raincoat"
- desc = "Do you like Huey Lewis and the News?"
- icon_state = "raincoat"
- body_parts_covered =CHEST|GROIN|LEGS|ARMS|HANDS
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/suit.dmi'
- mutantrace_variation = NONE
-
-
diff --git a/code/modules/clothing/under/color.dm b/code/modules/clothing/under/color.dm
index 12cafb7e46..a18b7e5817 100644
--- a/code/modules/clothing/under/color.dm
+++ b/code/modules/clothing/under/color.dm
@@ -41,6 +41,10 @@
item_color = "black"
resistance_flags = NONE
+/obj/item/clothing/under/color/black/trackless
+ desc = "A black jumpsuit that has its sensors removed."
+ has_sensor = NO_SENSORS
+
/obj/item/clothing/under/skirt/color/black
name = "black jumpskirt"
icon_state = "black_skirt"
@@ -189,13 +193,16 @@
item_state = "b_suit"
item_color = "teal_skirt"
-
/obj/item/clothing/under/color/lightpurple
name = "purple jumpsuit"
icon_state = "lightpurple"
item_state = "p_suit"
item_color = "lightpurple"
+/obj/item/clothing/under/color/lightpurple/trackless
+ desc = "A magically colored jumpsuit. No sensors are attached!"
+ has_sensor = NO_SENSORS
+
/obj/item/clothing/under/skirt/color/lightpurple
name = "lightpurple jumpskirt"
icon_state = "lightpurple_skirt"
diff --git a/code/modules/clothing/under/jobs/medsci.dm b/code/modules/clothing/under/jobs/medsci.dm
index 666501282a..971112ac38 100644
--- a/code/modules/clothing/under/jobs/medsci.dm
+++ b/code/modules/clothing/under/jobs/medsci.dm
@@ -195,19 +195,19 @@
permeability_coefficient = 0.5
armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0)
/obj/item/clothing/under/rank/medical/blue
- name = "medical scrubs"
+ name = "blue medical scrubs"
desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in baby blue."
icon_state = "scrubsblue"
item_color = "scrubsblue"
can_adjust = FALSE
/obj/item/clothing/under/rank/medical/green
- name = "medical scrubs"
+ name = "green medical scrubs"
desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in dark green."
icon_state = "scrubsgreen"
item_color = "scrubsgreen"
can_adjust = FALSE
/obj/item/clothing/under/rank/medical/purple
- name = "medical scrubs"
+ name = "purple medical scrubs"
desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in deep purple."
icon_state = "scrubspurple"
item_color = "scrubspurple"
diff --git a/code/modules/clothing/under/miscellaneous.dm b/code/modules/clothing/under/miscellaneous.dm
index a2081851e1..a3a2b6b97e 100644
--- a/code/modules/clothing/under/miscellaneous.dm
+++ b/code/modules/clothing/under/miscellaneous.dm
@@ -131,6 +131,9 @@
item_color = "officer"
alt_covers_chest = TRUE
+/obj/item/clothing/under/rank/centcom_officer/syndicate
+ has_sensor = NO_SENSORS
+
/obj/item/clothing/under/rank/centcom_commander
desc = "It's a jumpsuit worn by CentCom's highest-tier Commanders."
name = "\improper CentCom officer's jumpsuit"
@@ -945,4 +948,144 @@
icon_state = "christmasfemaleg"
item_state = "christmasfemaleg"
body_parts_covered = CHEST|GROIN
- can_adjust = FALSE
\ No newline at end of file
+ can_adjust = FALSE
+
+// Lunar Clothes
+/obj/item/clothing/under/lunar/qipao
+ name = "Black Qipao"
+ desc = "A Qipao, traditionally worn in ancient Earth China by women during social events and lunar new years. This one is black."
+ icon_state = "qipao"
+ item_state = "qipao"
+ body_parts_covered = CHEST|GROIN
+ can_adjust = FALSE
+ mutantrace_variation = NONE
+
+/obj/item/clothing/under/lunar/qipao/white
+ name = "White Qipao"
+ desc = "A Qipao, traditionally worn in ancient Earth China by women during social events and lunar new years. This one is white."
+ icon_state = "qipao_white"
+ item_state = "qipao_white"
+ body_parts_covered = CHEST|GROIN
+ can_adjust = FALSE
+ mutantrace_variation = NONE
+
+/obj/item/clothing/under/lunar/qipao/red
+ name = "Red Qipao"
+ desc = "A Qipao, traditionally worn in ancient Earth China by women during social events and lunar new years. This one is red."
+ icon_state = "qipao_red"
+ item_state = "qipao_red"
+ body_parts_covered = CHEST|GROIN
+ can_adjust = FALSE
+ mutantrace_variation = NONE
+
+/obj/item/clothing/under/lunar/cheongsam
+ name = "Black Cheongsam"
+ desc = "A Cheongsam, traditionally worn in ancient Earth China by men during social events and lunar new years. This one is black."
+ icon_state = "cheong"
+ item_state = "cheong"
+ body_parts_covered = CHEST|GROIN
+ can_adjust = FALSE
+ mutantrace_variation = NONE
+
+/obj/item/clothing/under/lunar/cheongsam/white
+ name = "White Cheongsam"
+ desc = "A Cheongsam, traditionally worn in ancient Earth China by men during social events and lunar new years. This one is white."
+ icon_state = "cheongw"
+ item_state = "cheongw"
+ body_parts_covered = CHEST|GROIN
+ can_adjust = FALSE
+ mutantrace_variation = NONE
+
+/obj/item/clothing/under/lunar/cheongsam/red
+ name = "Red Cheongsam"
+ desc = "A Cheongsam, traditionally worn in ancient Earth China by men during social events and lunar new years. This one is red.."
+ icon_state = "cheongr"
+ item_state = "cheongr"
+ body_parts_covered = CHEST|GROIN
+ can_adjust = FALSE
+ mutantrace_variation = NONE
+
+/obj/item/clothing/under/squatter_outfit
+ name = "slav squatter tracksuit"
+ desc = "Cyka blyat."
+ icon_state = "squatteroutfit"
+ item_state = "squatteroutfit"
+ item_color = "squatteroutfit"
+ can_adjust = FALSE
+ mutantrace_variation = NONE
+
+/obj/item/clothing/under/russobluecamooutfit
+ name = "russian blue camo"
+ desc = "Drop and give me dvadtsat!"
+ icon_state = "russobluecamo"
+ item_state = "russobluecamo"
+ item_color = "russobluecamo"
+ can_adjust = FALSE
+ mutantrace_variation = NONE
+
+/obj/item/clothing/under/keyholesweater
+ name = "keyhole sweater"
+ desc = "What is the point of this, anyway?"
+ icon_state = "keyholesweater"
+ item_state = "keyholesweater"
+ item_color = "keyholesweater"
+ can_adjust = FALSE
+ mutantrace_variation = NONE
+
+/obj/item/clothing/under/stripper_pink
+ name = "pink stripper outfit"
+ icon_state = "stripper_p"
+ item_state = "stripper_p"
+ item_color = "stripper_p"
+
+/obj/item/clothing/under/stripper_green
+ name = "green stripper outfit"
+ icon_state = "stripper_g"
+ item_state = "stripper_g"
+ item_color = "stripper_g"
+ can_adjust = FALSE
+
+/obj/item/clothing/under/mankini
+ name = "pink mankini"
+ icon_state = "mankini"
+ item_state = "mankini"
+ item_color = "mankini"
+ can_adjust = FALSE
+ mutantrace_variation = NONE
+
+/obj/item/clothing/under/wedding
+ name = "white wedding dress"
+ desc = "A white wedding gown made from the finest silk."
+ icon_state = "bride_white"
+ item_state = "bride_white"
+ item_color = "bride_white"
+ can_adjust = FALSE
+ mutantrace_variation = NONE
+
+/obj/item/clothing/under/wedding/orange
+ name = "orange wedding dress"
+ desc = "A big and puffy orange dress."
+ icon_state = "bride_orange"
+ item_state = "bride_orange"
+ item_color = "bride_orange"
+
+/obj/item/clothing/under/wedding/purple
+ name = "purple wedding dress"
+ desc = "A big and puffy purple dress."
+ icon_state = "bride_purple"
+ item_state = "bride_purple"
+ item_color = "bride_purple"
+
+/obj/item/clothing/under/wedding/blue
+ name = "blue wedding dress"
+ desc = "A big and puffy blue dress."
+ icon_state = "bride_blue"
+ item_state = "bride_blue"
+ item_color = "bride_blue"
+
+/obj/item/clothing/under/wedding/red
+ name = "red wedding dress"
+ desc = "A big and puffy red dress."
+ icon_state = "bride_red"
+ item_state = "bride_red"
+ item_color = "bride_red"
diff --git a/code/modules/clothing/under/vg_under.dm b/code/modules/clothing/under/vg_under.dm
deleted file mode 100644
index 555ed3b795..0000000000
--- a/code/modules/clothing/under/vg_under.dm
+++ /dev/null
@@ -1,436 +0,0 @@
-// Fixed to work with Citadel code. Apparently none of them had NO_MUTANTRACE flags.
-// I was pissy when I realised how to fix this because it's so fucking easy and nobody apparently had done it.
-
-/obj/item/clothing/under/stripper_pink
- name = "pink stripper outfit"
- icon_state = "stripper_p"
- item_state = "stripper_p"
- item_color = "stripper_p"
- can_adjust = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/under/stripper_green
- name = "green stripper outfit"
- icon_state = "stripper_g"
- item_state = "stripper_g"
- item_color = "stripper_g"
- can_adjust = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/under/wedding/bride_orange
- name = "orange wedding dress"
- desc = "A big and puffy orange dress."
- icon_state = "bride_orange"
- item_state = "bride_orange"
- item_color = "bride_orange"
- can_adjust = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/under/wedding/bride_purple
- name = "purple wedding dress"
- desc = "A big and puffy purple dress."
- icon_state = "bride_purple"
- item_state = "bride_purple"
- item_color = "bride_purple"
- can_adjust = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/under/wedding/bride_blue
- name = "blue wedding dress"
- desc = "A big and puffy blue dress."
- icon_state = "bride_blue"
- item_state = "bride_blue"
- item_color = "bride_blue"
- can_adjust = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/under/wedding/bride_red
- name = "red wedding dress"
- desc = "A big and puffy red dress."
- icon_state = "bride_red"
- item_state = "bride_red"
- item_color = "bride_red"
- can_adjust = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/under/wedding/bride_white
- name = "white wedding dress"
- desc = "A white wedding gown made from the finest silk."
- icon_state = "bride_white"
- item_state = "bride_white"
- item_color = "bride_white"
- can_adjust = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/under/mankini
- name = "pink mankini"
- icon_state = "mankini"
- item_state = "mankini"
- item_color = "mankini"
- can_adjust = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
- mutantrace_variation = NONE
-/*
-
-/obj/item/clothing/under/psysuit
- name = "dark undersuit"
- desc = "A thick, layered grey undersuit lined with power cables. Feels a little like wearing an electrical storm."
- icon_state = "psysuit"
- item_state = "psysuit"
- item_color = "psysuit"
- can_adjust = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
-
-
-/obj/item/clothing/under/officeruniform
- name = "officer's uniform"
- desc = "Bestraft die Juden fur ihre Verbrechen."
- icon_state = "officeruniform"
- item_state = "officeruniform"
- item_color = "officeruniform"
- can_adjust = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
-
-
-/obj/item/clothing/under/soldieruniform
- name = "soldier's uniform"
- desc = "Bestraft die Verbundeten fur ihren Widerstand."
- icon_state = "soldieruniform"
- item_state = "soldieruniform"
- item_color = "soldieruniform"
- can_adjust = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
-
-*/
-/obj/item/clothing/under/squatter_outfit
- name = "slav squatter tracksuit"
- desc = "Cyka blyat."
- icon_state = "squatteroutfit"
- item_state = "squatteroutfit"
- item_color = "squatteroutfit"
- can_adjust = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/under/russobluecamooutfit
- name = "russian blue camo"
- desc = "Drop and give me dvadtsat!"
- icon_state = "russobluecamo"
- item_state = "russobluecamo"
- item_color = "russobluecamo"
- can_adjust = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/under/stilsuit
- name = "stillsuit"
- desc = "Designed to preserve bodymoisture."
- icon_state = "stilsuit"
- item_state = "stilsuit"
- item_color = "stilsuit"
- can_adjust = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/under/aviatoruniform
- name = "aviator uniform"
- desc = "Now you can look absolutely dashing!"
- icon_state = "aviator_uniform"
- item_state = "aviator_uniform"
- item_color = "aviator_uniform"
- can_adjust = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/under/bikersuit
- name = "biker's outfit"
- icon_state = "biker"
- item_state = "biker"
- item_color = "biker"
- can_adjust = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/under/jacketsuit
- name = "richard's outfit"
- desc = "Do you know what time it is?"
- icon_state = "jacket"
- item_state = "jacket"
- item_color = "jacket"
- can_adjust = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
- mutantrace_variation = NONE
-
-obj/item/clothing/under/mega
- name = "\improper DRN-001 suit"
- desc = "The original. Simple, yet very adaptable."
- icon_state = "mega"
- item_state = "mega"
- item_color = "mega"
- can_adjust = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/under/proto
- name = "The Prototype Suit"
- desc = "Even robots know scarves are the perfect accessory for a brooding rival."
- icon_state = "proto"
- item_state = "proto"
- item_color = "proto"
- can_adjust = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/under/megax
- name = "\improper Maverick Hunter regalia"
- desc = "The best outfit for taking out rogue borgs."
- icon_state = "megax"
- item_state = "megax"
- item_color = "megax"
- can_adjust = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/under/joe
- name = "The Sniper Suit"
- desc = "Mass produced combat robots with a rather unfitting name."
- icon_state = "joe"
- item_state = "joe"
- item_color = "joe"
- can_adjust = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/under/roll
- name = "\improper DRN-002 Dress"
- desc = "A simple red dress, the good doctor's second robot wasn't quite as exciting as the first."
- icon_state = "roll"
- item_state = "roll"
- item_color = "roll"
- can_adjust = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/under/gokugidown
- name = "turtle hermit undershirt"
- desc = "Something seems oddly familiar about this outfit..."
- icon_state = "gokugidown"
- item_state = "gokugidown"
- item_color = "gokugidown"
- can_adjust = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/under/gokugi
- name = "turtle hermit outfit"
- desc = "An outfit from one trained by the great Turtle Hermit."
- icon_state = "gokugi"
- item_state = "gokugi"
- item_color = "gokugi"
- can_adjust = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/under/doomguy
- name = "\improper Doomguy's pants"
- desc = ""
- icon_state = "doom"
- item_state = "doom"
- item_color = "doom"
- can_adjust = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/under/vault13
- name = "vault 13 Jumpsuit"
- desc = "Oddly similar to the station's usual jumpsuits, but with a rustic charm to it. Has a large thirteen emblazened on the back."
- icon_state = "v13-jumpsuit"
- item_state = "v13-jumpsuit"
- item_color = "v13-jumpsuit"
- can_adjust = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/under/vault
- name = "vault jumpsuit"
- desc = "Oddly similar to the station's usual jumpsuits, but with a rustic charm to it."
- icon_state = "v-jumpsuit"
- item_state = "v-jumpsuit"
- item_color = "v-jumpsuit"
- can_adjust = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/under/clownpiece
- name = "Clownpiece's Pierrot suit"
- desc = "A female-sized set of leggings and shirt with a pattern similar to the American flag, featuring a frilled collar."
- icon_state = "clownpiece"
- item_state = "clownpiece"
- item_color = "clownpiece"
- can_adjust = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/under/cia
- name = "casual IAA outfit"
- desc = "Just looking at this makes you feel in charge."
- icon_state = "cia"
- item_state = "cia"
- item_color = "cia"
- can_adjust = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/under/greaser
- name = "greaser outfit"
- desc = "The one that you want!"
- icon_state = "greaser_default"
- item_state = "greaser_default"
- can_adjust = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/under/greaser/New()
- var/greaser_colour = "default"
- switch(rand(1,4))
- if(1)
- greaser_colour = "default"
- if(2)
- greaser_colour = "cult"
- if(3)
- greaser_colour = "spider"
- if(4)
- greaser_colour = "snakes"
- desc = "Tunnel Snakes Rule!"
- icon_state = "greaser_[greaser_colour]"
- item_state = "greaser_[greaser_colour]"
- item_color = "greaser_[greaser_colour]"
- can_adjust = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- mutantrace_variation = NONE
-
-
-/obj/item/clothing/under/wintercasualwear
- name = "winter casualwear"
- desc = "Perfect for winter!"
- icon_state = "shizunewinter"
- item_state = "shizunewinter"
- item_color = "shizunewinter"
- can_adjust = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/under/casualwear
- name = "spring casualwear"
- desc = "Perfect for spring!"
- icon_state = "shizunenormal"
- item_state = "shizunenormal"
- item_color = "shizunenormal"
- can_adjust = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/under/keyholesweater
- name = "keyhole sweater"
- desc = "What is the point of this, anyway?"
- icon_state = "keyholesweater"
- item_state = "keyholesweater"
- item_color = "keyholesweater"
- can_adjust = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/under/casualhoodie
- name = "casual hoodie"
- desc = "Pefect for lounging about in."
- icon_state = "hoodiejeans"
- item_state = "hoodiejeans"
- item_color = "hoodiejeans"
- can_adjust = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
- mutantrace_variation = NONE
-
-/obj/item/clothing/under/casualhoodie/skirt
- icon_state = "hoodieskirt"
- item_state = "hoodieskirt"
- item_color = "hoodieskirt"
- can_adjust = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- mutantrace_variation = NONE
-
-/*
-/obj/item/clothing/under/mummy_rags
- name = "mummy rags"
- desc = "Ancient rags taken off from some mummy."
- icon_state = "mummy"
- item_state = "mummy"
- item_color = "mummy"
- can_adjust = 0
- has_sensor = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
-
-
-/obj/item/clothing/under/neorussian
- name = "neo-Russian uniform"
- desc = "Employs a special toshnit pattern, will render you invisible when you eat a potato on an empty stomach."
- icon_state = "nr_uniform"
- item_state = "nr_uniform"
- item_color = "nr_uniform"
- can_adjust = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
-*/
-
-/obj/item/clothing/under/rottensuit
- name = "rotten suit"
- desc = "This suit seems perfect for wearing underneath a disguise."
- icon_state = "rottensuit"
- item_state = "rottensuit"
- item_color = "rottensuit"
- can_adjust = 0
- icon = 'modular_citadel/icons/obj/clothing/vg_clothes.dmi'
- alternate_worn_icon = 'modular_citadel/icons/mob/citadel/uniforms.dmi'
- mutantrace_variation = NONE
-
diff --git a/code/modules/crafting/recipes.dm b/code/modules/crafting/recipes.dm
deleted file mode 100644
index 2987b52338..0000000000
--- a/code/modules/crafting/recipes.dm
+++ /dev/null
@@ -1,11 +0,0 @@
-/datum/crafting_recipe
- var/name = "" //in-game display name
- var/reqs[] = list() //type paths of items consumed associated with how many are needed
- var/result //type path of item resulting from this craft
- var/tools[] = list() //type paths of items needed but not consumed
- var/time = 30 //time in deciseconds
- var/parts[] = list() //type paths of items that will be placed in the result
- var/chem_catalysts[] = list() //like tools but for reagents
- var/category = CAT_NONE //where it shows up in the crafting UI
- var/subcategory = CAT_NONE
- var/always_availible = TRUE //Set to FALSE if it needs to be learned first.
\ No newline at end of file
diff --git a/code/modules/detectivework/detective_work.dm b/code/modules/detectivework/detective_work.dm
index 634401d0d8..3b1b00fc3a 100644
--- a/code/modules/detectivework/detective_work.dm
+++ b/code/modules/detectivework/detective_work.dm
@@ -9,8 +9,7 @@
else if(M.bloody_hands > 1)
if(add_blood_DNA(M.blood_DNA, M.diseases))
M.bloody_hands--
- if(!suit_fibers)
- suit_fibers = list()
+ LAZYINITLIST(suit_fibers)
var/fibertext
var/item_multiplier = isitem(src)?1.2:1
if(M.wear_suit)
@@ -66,7 +65,6 @@
fingerprintslast = M.ckey
-
//Set ignoregloves to add prints irrespective of the mob having gloves on.
/atom/proc/add_fingerprint(mob/living/M, ignoregloves = FALSE)
if(!M || !M.key)
@@ -93,15 +91,10 @@
fingerprints[full_print] = full_print
/atom/proc/transfer_fingerprints_to(atom/A)
- // Make sure everything are lists.
- LAZYINITLIST(A.fingerprints)
- LAZYINITLIST(A.fingerprintshidden)
- LAZYINITLIST(fingerprints)
- LAZYINITLIST(fingerprintshidden)
-
- // Transfer
if(fingerprints)
- A.fingerprints |= fingerprints.Copy() //detective
+ LAZYINITLIST(A.fingerprints)
+ A.fingerprints |= fingerprints //detective
if(fingerprintshidden)
- A.fingerprintshidden |= fingerprintshidden.Copy() //admin
- A.fingerprintslast = fingerprintslast
\ No newline at end of file
+ LAZYINITLIST(A.fingerprintshidden)
+ A.fingerprintshidden |= fingerprintshidden //admin
+ A.fingerprintslast = fingerprintslast
diff --git a/code/modules/detectivework/scanner.dm b/code/modules/detectivework/scanner.dm
index a2860f8c43..9b114829e1 100644
--- a/code/modules/detectivework/scanner.dm
+++ b/code/modules/detectivework/scanner.dm
@@ -123,7 +123,7 @@
// We gathered everything. Create a fork and slowly display the results to the holder of the scanner.
var/found_something = FALSE
- add_log("[STATION_TIME_TIMESTAMP("hh:mm:ss")][get_timestamp()] - [target_name]", 0)
+ add_log("[STATION_TIME_TIMESTAMP("hh:mm:ss", world.time)][get_timestamp()] - [target_name]", 0)
// Fingerprints
if(length(fingerprints))
diff --git a/code/modules/emoji/emoji_parse.dm b/code/modules/emoji/emoji_parse.dm
index 2f4a84c646..65e8063ce0 100644
--- a/code/modules/emoji/emoji_parse.dm
+++ b/code/modules/emoji/emoji_parse.dm
@@ -1,4 +1,4 @@
-/proc/emoji_parse(text)
+/proc/emoji_parse(text) //turns :ai: into an emoji in text.
. = text
if(!CONFIG_GET(flag/emojis))
return
@@ -12,14 +12,14 @@
parsed += copytext(text, pos, search)
if(search)
pos = search
- search = findtext(text, ":", pos+1)
+ search = findtext(text, ":", pos + length(text[pos]))
if(search)
- emoji = lowertext(copytext(text, pos+1, search))
+ emoji = lowertext(copytext(text, pos + length(text[pos]), search))
var/datum/asset/spritesheet/sheet = get_asset_datum(/datum/asset/spritesheet/goonchat)
var/tag = sheet.icon_tag("emoji-[emoji]")
if(tag)
parsed += tag
- pos = search + 1
+ pos = search + length(text[pos])
else
parsed += copytext(text, pos, search)
pos = search
@@ -30,3 +30,24 @@
break
return parsed
+/proc/emoji_sanitize(text) //cuts any text that would not be parsed as an emoji
+ . = text
+ if(!CONFIG_GET(flag/emojis))
+ return
+ var/static/list/emojis = icon_states(icon('icons/emoji.dmi'))
+ var/final = "" //only tags are added to this
+ var/pos = 1
+ var/search = 0
+ while(1)
+ search = findtext(text, ":", pos)
+ if(search)
+ pos = search
+ search = findtext(text, ":", pos + length(text[pos]))
+ if(search)
+ var/word = lowertext(copytext(text, pos + length(text[pos]), search))
+ if(word in emojis)
+ final += lowertext(copytext(text, pos, search + length(text[search])))
+ pos = search + length(text[search])
+ continue
+ break
+ return final
diff --git a/code/modules/error_handler/error_handler.dm b/code/modules/error_handler/error_handler.dm
index 8b410130cb..6a3d2c2233 100644
--- a/code/modules/error_handler/error_handler.dm
+++ b/code/modules/error_handler/error_handler.dm
@@ -12,13 +12,20 @@ GLOBAL_VAR_INIT(total_runtimes_skipped, 0)
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")
+ if(copytext(E.name, 1, 32) == "Maximum recursion level reached")//32 == length() of that string + 1
//log to world while intentionally triggering the byond bug.
log_world("runtime error: [E.name]\n[E.desc]")
//if we got to here without silently ending, the byond bug has been fixed.
log_world("The bug with recursion runtimes has been fixed. Please remove the snowflake check from world/Error in [__FILE__]:[__LINE__]")
return //this will never happen.
+ else if(copytext(E.name,1,18) == "Out of resources!")//18 == length() of that string + 1
+ log_world("BYOND out of memory. Restarting")
+ log_game("BYOND out of memory. Restarting")
+ TgsEndProcess()
+ Reboot(reason = 1)
+ return ..()
+
if (islist(stack_trace_storage))
for (var/line in splittext(E.desc, "\n"))
if (text2ascii(line) != 32)
@@ -102,7 +109,7 @@ GLOBAL_VAR_INIT(total_runtimes_skipped, 0)
usrinfo = null
continue // Our usr info is better, replace it
- if(copytext(line, 1, 3) != " ")
+ if(copytext(line, 1, 3) != " ")//3 == length(" ") + 1
desclines += (" " + line) // Pad any unpadded lines, so they look pretty
else
desclines += line
diff --git a/code/modules/events/holiday/vday.dm b/code/modules/events/holiday/vday.dm
index 13c516960a..d3cbf03581 100644
--- a/code/modules/events/holiday/vday.dm
+++ b/code/modules/events/holiday/vday.dm
@@ -64,65 +64,10 @@
resistance_flags = FLAMMABLE
w_class = WEIGHT_CLASS_TINY
-/obj/item/valentine/New()
- ..()
- message = pick("Roses are red / Violets are good / One day while Andy...",
- "My love for you is like the singularity. It cannot be contained.",
- "Will you be my lusty xenomorph maid?",
- "We go together like the clown and the external airlock.",
- "Roses are red / Liches are wizards / I love you more than a whole squad of lizards.",
- "Be my valentine. Law 2.",
- "You must be a mime, because you leave me speechless.",
- "I love you like Ian loves the HoP.",
- "You're hotter than a plasma fire in toxins.",
- "Are you a rogue atmos tech? Because you're taking my breath away.",
- "Could I have all access... to your heart?",
- "Call me the doctor, because I'm here to inspect your johnson.",
- "I'm not a changeling, but you make my proboscis extend.",
- "I just can't get EI NATH of you.",
- "You must be a nuke op, because you make my heart explode.",
- "Roses are red / Botany is a farm / Not being my Valentine / causes human harm.",
- "I want you more than an assistant wants insulated gloves.",
- "If I was a security officer, I'd brig you all shift.",
- "Are you the janitor? Because I think I've fallen for you.",
- "You're always valid to my heart.",
- "I'd risk the wrath of the gods to bwoink you.",
- "You look as beautiful now as the last time you were cloned.",
- "Someone check the gravitational generator, because I'm only attracted to you.",
- "If I were the warden I'd always let you into my armory.",
- "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.",
- "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?",
- "My love for you is stronger than a reinforced wall.",
- "This must be the captain's office, because I see a fox.",
- "I'm not a highlander, but there can only be one for me.",
- "The floor is made of lava! Quick, get on my bed.",
- "If you were an abandoned station you'd be the DEARelict.",
- "If you had a pickaxe you'd be a shaft FINEr.",
- "Roses are red, tide is gray, if I were an assistant I'd steal you away.",
- "Roses are red, text is green, I love you more than cleanbots clean.",
- "If you were a carp I'd fi-lay you.",
- "I'm a nuke op, and my pinpointer leads to your heart.",
- "Wanna slay my megafauna?",
- "I'm a clockwork cultist. Or zl inyragvar.",
- "If you were a disposal bin I'd ride you all day.",
- "Put on your explorer's suit because I'm taking you to LOVEaland.",
- "I must be the CMO, 'cause I saw you on my CUTE sensors.",
- "You're the vomit to my flyperson.",
- "You must be liquid dark matter, because you're pulling me closer.",
- "Not even sorium can drive me away from you.",
- "Wanna make like a borg and do some heavy petting?",
- "Are you powering the station? Because you super matter to me.",
- "I wish science could make me a bag of holding you.",
- "Let's call the emergency CUDDLE.",
- "I must be tripping on BZ, because I saw an angel walk by.",
- "Wanna empty out my tool storage?",
- "Did you visit the medbay after you fell from heaven?",
- "Are you wearing space pants? Wanna not be?" )
+/obj/item/valentine/Initialize(mapload)
+ message = pick(GLOB.flirts)
+ return ..()
+
/obj/item/valentine/attackby(obj/item/W, mob/user, params)
..()
@@ -140,7 +85,7 @@
/obj/item/valentine/examine(mob/user)
. = ..()
if(in_range(user, src) || isobserver(user))
- if( !(ishuman(user) || isobserver(user) || issilicon(user)) )
+ if( !(ishuman(user) || isobserver(user) || hasSiliconAccessInArea(user)) )
user << browse("[name][stars(message)]", "window=[name]")
onclose(user, "[name]")
else
diff --git a/code/modules/events/meteor_wave.dm b/code/modules/events/meteor_wave.dm
index a1a82ea5f7..e24ce13034 100644
--- a/code/modules/events/meteor_wave.dm
+++ b/code/modules/events/meteor_wave.dm
@@ -18,15 +18,15 @@
announceWhen = 1
var/list/wave_type
var/wave_name = "normal"
+ var/direction
/datum/round_event/meteor_wave/setup()
announceWhen = 1
- startWhen = rand(60, 90) //Yeah for SOME REASON this is measured in seconds and not deciseconds???
+ startWhen = rand(90, 180) // Apparently it is by 2 seconds, so 90 is actually 180 seconds, and 180 is 360 seconds. So this is 3-6 minutes
if(GLOB.singularity_counter)
startWhen *= 1 - min(GLOB.singularity_counter * SINGULO_BEACON_DISTURBANCE, SINGULO_BEACON_MAX_DISTURBANCE)
endWhen = startWhen + 60
-
/datum/round_event/meteor_wave/New()
..()
if(!wave_type)
@@ -38,6 +38,8 @@
"normal" = 50,
"threatening" = 40,
"catastrophic" = 10))
+ if(!direction)
+ direction = pick(GLOB.cardinals)
switch(wave_name)
if("normal")
wave_type = GLOB.meteors_normal
@@ -59,11 +61,24 @@
kill()
/datum/round_event/meteor_wave/announce(fake)
- priority_announce("Meteors have been detected on collision course with the station. Estimated time until impact: [round(startWhen/60)] minutes.[GLOB.singularity_counter ? " Warning: Anomalous gravity pulse detected, Syndicate technology interference likely." : ""]", "Meteor Alert", "meteors")
+ priority_announce(generateMeteorString(startWhen,TRUE,direction), "Meteor Alert", "meteors")
+
+/proc/generateMeteorString(startWhen,syndiealert,direction)
+ var/directionstring
+ switch(direction)
+ if(NORTH)
+ directionstring = " towards the fore"
+ if(SOUTH)
+ directionstring = " towards the aft"
+ if(EAST)
+ directionstring = " towards starboard"
+ if(WEST)
+ directionstring = " towards port"
+ return "Meteors have been detected on a collision course with the station[directionstring]. Estimated time until impact: [round((startWhen * SSevents.wait) / 10, 0.1)] seconds.[GLOB.singularity_counter && syndiealert ? " Warning: Anomalous gravity pulse detected, Syndicate technology interference likely." : ""]"
/datum/round_event/meteor_wave/tick()
if(ISMULTIPLE(activeFor, 3))
- spawn_meteors(5, wave_type) //meteor list types defined in gamemode/meteor/meteors.dm
+ spawn_meteors(5, wave_type, direction) //meteor list types defined in gamemode/meteor/meteors.dm
/datum/round_event_control/meteor_wave/threatening
name = "Meteor Wave: Threatening"
diff --git a/code/modules/events/pirates.dm b/code/modules/events/pirates.dm
index 8bc5491970..ab9ab96678 100644
--- a/code/modules/events/pirates.dm
+++ b/code/modules/events/pirates.dm
@@ -177,6 +177,7 @@
shuttleId = "pirateship"
icon_screen = "syndishuttle"
icon_keyboard = "syndie_key"
+ resistance_flags = INDESTRUCTIBLE
light_color = LIGHT_COLOR_RED
possible_destinations = "pirateship_away;pirateship_home;pirateship_custom"
@@ -184,6 +185,7 @@
name = "pirate shuttle navigation computer"
desc = "Used to designate a precise transit location for the pirate shuttle."
shuttleId = "pirateship"
+ resistance_flags = INDESTRUCTIBLE
lock_override = CAMERA_LOCK_STATION
shuttlePortId = "pirateship_custom"
x_offset = 9
@@ -226,6 +228,7 @@
desc = "This sophisticated machine scans the nearby space for items of value."
icon = 'icons/obj/machines/research.dmi'
icon_state = "tdoppler"
+ resistance_flags = INDESTRUCTIBLE
density = TRUE
var/cooldown = 300
var/next_use = 0
@@ -259,6 +262,7 @@
name = "cargo hold pad"
icon = 'icons/obj/telescience.dmi'
icon_state = "lpad-idle-o"
+ resistance_flags = INDESTRUCTIBLE
var/idle_state = "lpad-idle-o"
var/warmup_state = "lpad-idle"
var/sending_state = "lpad-beam"
@@ -272,6 +276,7 @@
/obj/machinery/computer/piratepad_control
name = "cargo hold control terminal"
+ resistance_flags = INDESTRUCTIBLE
var/status_report = "Idle"
var/obj/machinery/piratepad/pad
var/warmup_time = 100
diff --git a/code/modules/events/spontaneous_appendicitis.dm b/code/modules/events/spontaneous_appendicitis.dm
index 9cf3a86e71..58a2875849 100644
--- a/code/modules/events/spontaneous_appendicitis.dm
+++ b/code/modules/events/spontaneous_appendicitis.dm
@@ -19,7 +19,7 @@
continue
if(!H.getorgan(/obj/item/organ/appendix)) //Don't give the disease to some who lacks it, only for it to be auto-cured
continue
- if(!(MOB_ORGANIC in H.mob_biotypes)) //biotype sleeper bugs strike again, once again making appendicitis pick a target that can't take it
+ if(!(H.mob_biotypes & MOB_ORGANIC)) //biotype sleeper bugs strike again, once again making appendicitis pick a target that can't take it
continue
var/foundAlready = FALSE //don't infect someone that already has appendicitis
for(var/datum/disease/appendicitis/A in H.diseases)
diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm
index 83dbad0246..b0c3121c67 100644
--- a/code/modules/flufftext/Hallucination.dm
+++ b/code/modules/flufftext/Hallucination.dm
@@ -18,6 +18,7 @@ GLOBAL_LIST_INIT(hallucination_list, list(
/datum/hallucination/items = 4,
/datum/hallucination/fire = 3,
/datum/hallucination/self_delusion = 2,
+ /datum/hallucination/naked = 2,
/datum/hallucination/delusion = 2,
/datum/hallucination/shock = 1,
/datum/hallucination/death = 1,
@@ -65,6 +66,9 @@ GLOBAL_LIST_INIT(hallucination_list, list(
//Returns a random turf in a ring around the target mob, useful for sound hallucinations
/datum/hallucination/proc/random_far_turf()
+ var/turf/target_T = get_turf(target)
+ if(!target_T)
+ return
var/x_based = prob(50)
var/first_offset = pick(-8,-7,-6,-5,5,6,7,8)
var/second_offset = rand(-8,8)
@@ -76,7 +80,7 @@ GLOBAL_LIST_INIT(hallucination_list, list(
else
y_off = first_offset
x_off = second_offset
- var/turf/T = locate(target.x + x_off, target.y + y_off, target.z)
+ var/turf/T = locate(target_T.x + x_off, target_T.y + y_off, target_T.z)
return T
/obj/effect/hallucination
@@ -895,7 +899,7 @@ GLOBAL_LIST_INIT(hallucination_list, list(
SEND_SOUND(target, get_announcer_sound("aimalf"))
if("meteors") //Meteors inbound!
to_chat(target, "
Meteor Alert
")
- to_chat(target, "
Meteors have been detected on collision course with the station. Estimated time until impact: [round(rand(180,360)/60)] minutes.
"
- if(1) // X channel pages inbetween.
- for(var/datum/newscaster/feed_channel/NP in news_content)
- pages++
- var/datum/newscaster/feed_channel/C = news_content[curr_page]
- dat += "[C.channel_name] \[created by: [C.returnAuthor(notContent(C.authorCensorTime))]\]
"
- if(notContent(C.DclassCensorTime))
- dat+="This channel was deemed dangerous to the general welfare of the station and therefore marked with a D-Notice. Its contents were not transferred to the newspaper at the time of printing."
- else
- if(isemptylist(C.messages))
- dat+="No Feed stories stem from this channel..."
- else
- var/i = 0
- for(var/datum/newscaster/feed_message/MESSAGE in C.messages)
- if(MESSAGE.creationTime > creationTime)
- if(i == 0)
- dat+="No Feed stories stem from this channel..."
- break
- if(i == 0)
- dat+="
"
- i++
- dat+="-[MESSAGE.returnBody(notContent(MESSAGE.bodyCensorTime))] "
- if(MESSAGE.img)
- user << browse_rsc(MESSAGE.img, "tmp_photo[i].png")
- dat+=" "
- dat+="\[Story by [MESSAGE.returnAuthor(notContent(MESSAGE.authorCensorTime))]\]
"
- dat+="
"
- if(scribble_page==curr_page)
- dat+=" There is a small scribble near the end of this page... It reads: \"[scribble]\""
- dat+= "
"
- dat+="Criminal name: [wantedCriminal] "
- dat+="Description: [wantedBody] "
- dat+="Photo:: "
- if(wantedPhoto)
- user << browse_rsc(wantedPhoto, "tmp_photow.png")
- dat+=" "
- else
- dat+="None"
- else
- dat+="Apart from some uninteresting classified ads, there's nothing on this page..."
- if(scribble_page==curr_page)
- dat+=" There is a small scribble near the end of this page... It reads: \"[scribble]\""
- dat+= "
"
+ if(1) // X channel pages inbetween.
+ for(var/datum/news/feed_channel/NP in news_content)
+ pages++
+ var/datum/news/feed_channel/C = news_content[curr_page]
+ dat += "[C.channel_name] \[created by: [C.returnAuthor(notContent(C.authorCensorTime))]\]
"
+ if(notContent(C.DclassCensorTime))
+ dat+="This channel was deemed dangerous to the general welfare of the station and therefore marked with a D-Notice. Its contents were not transferred to the newspaper at the time of printing."
+ else
+ if(isemptylist(C.messages))
+ dat+="No Feed stories stem from this channel..."
+ else
+ var/i = 0
+ for(var/datum/news/feed_message/MESSAGE in C.messages)
+ if(MESSAGE.creationTime > creationTime)
+ if(i == 0)
+ dat+="No Feed stories stem from this channel..."
+ break
+ if(i == 0)
+ dat+="
"
+ i++
+ dat+="-[MESSAGE.returnBody(notContent(MESSAGE.bodyCensorTime))] "
+ if(MESSAGE.img)
+ user << browse_rsc(MESSAGE.img, "tmp_photo[i].png")
+ dat+=" "
+ dat+="\[Story by [MESSAGE.returnAuthor(notContent(MESSAGE.authorCensorTime))]\]
"
+ dat+="
"
+ if(scribble_page==curr_page)
+ dat+=" There is a small scribble near the end of this page... It reads: \"[scribble]\""
+ dat+= "
"
+ dat+="Criminal name: [wantedCriminal] "
+ dat+="Description: [wantedBody] "
+ dat+="Photo:: "
+ if(wantedPhoto)
+ user << browse_rsc(wantedPhoto, "tmp_photow.png")
+ dat+=" "
+ else
+ dat+="None"
+ else
+ dat+="Apart from some uninteresting classified ads, there's nothing on this page..."
+ if(scribble_page==curr_page)
+ dat+=" There is a small scribble near the end of this page... It reads: \"[scribble]\""
+ dat+= "
"
var/counter = 1
while(M.fields["com_[counter]"])
diff --git a/code/modules/paperwork/folders.dm b/code/modules/paperwork/folders.dm
index 0a4bdf0c1c..4cd44af5ab 100644
--- a/code/modules/paperwork/folders.dm
+++ b/code/modules/paperwork/folders.dm
@@ -44,9 +44,11 @@
if(!user.is_literate())
to_chat(user, "You scribble illegibly on the cover of [src]!")
return
- var/n_name = copytext(sanitize(input(user, "What would you like to label the folder?", "Folder Labelling", null) as text), 1, MAX_NAME_LEN)
+ var/inputvalue = stripped_input(user, "What would you like to label the folder?", "Folder Labelling", "", MAX_NAME_LEN)
+ if(!inputvalue)
+ return
if(user.canUseTopic(src, BE_CLOSE))
- name = "folder[(n_name ? " - '[n_name]'" : null)]"
+ name = "folder - '[inputvalue]'"
/obj/item/folder/attack_self(mob/user)
diff --git a/code/modules/paperwork/handlabeler.dm b/code/modules/paperwork/handlabeler.dm
index 7c0edf3f0e..d054e5ff12 100644
--- a/code/modules/paperwork/handlabeler.dm
+++ b/code/modules/paperwork/handlabeler.dm
@@ -70,7 +70,7 @@
if(mode)
to_chat(user, "You turn on [src].")
//Now let them chose the text.
- var/str = copytext(reject_bad_text(input(user,"Label text?","Set label","")),1,MAX_NAME_LEN)
+ var/str = reject_bad_text(stripped_input(user, "Label text?", "Set label","", MAX_NAME_LEN))
if(!str || !length(str))
to_chat(user, "Invalid text!")
return
diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm
index c9b7fb7b57..20f4ab0c86 100644
--- a/code/modules/paperwork/paper.dm
+++ b/code/modules/paperwork/paper.dm
@@ -152,7 +152,10 @@
if(istart == 0)
return //No field found with matching id
- laststart = istart+1
+ if(links)
+ laststart = istart + length(info_links[istart])
+ else
+ laststart = istart + length(info[istart])
locid++
if(locid == id)
var/iend = 1
diff --git a/code/modules/paperwork/paperplane.dm b/code/modules/paperwork/paperplane.dm
index cfd028c4df..076d8b026a 100644
--- a/code/modules/paperwork/paperplane.dm
+++ b/code/modules/paperwork/paperplane.dm
@@ -34,7 +34,9 @@
/obj/item/paperplane/handle_atom_del(atom/A)
if(A == internalPaper)
+ var/obj/item/paper/P = internalPaper
internalPaper = null
+ P.moveToNullspace() //So we're not deleting it twice when deleting our contents.
if(!QDELETED(src))
qdel(src)
return ..()
diff --git a/code/modules/photography/_pictures.dm b/code/modules/photography/_pictures.dm
index 62fc01efda..f667e18f47 100644
--- a/code/modules/photography/_pictures.dm
+++ b/code/modules/photography/_pictures.dm
@@ -111,9 +111,9 @@
if(data.len < 5)
return null
var/timestamp = data[2]
- var/year = copytext(timestamp, 1, 5)
- var/month = copytext(timestamp, 5, 7)
- var/day = copytext(timestamp, 7, 9)
+ var/year = copytext_char(timestamp, 1, 5)
+ var/month = copytext_char(timestamp, 5, 7)
+ var/day = copytext_char(timestamp, 7, 9)
var/round = data[4]
. += "[year]/[month]/[day]/round-[round]"
if("O")
diff --git a/code/modules/photography/photos/photo.dm b/code/modules/photography/photos/photo.dm
index b15aa308a3..d51f569738 100644
--- a/code/modules/photography/photos/photo.dm
+++ b/code/modules/photography/photos/photo.dm
@@ -55,9 +55,8 @@
if(!user.is_literate())
to_chat(user, "You scribble illegibly on [src]!")
return
- var/txt = sanitize(input(user, "What would you like to write on the back?", "Photo Writing", null) as text)
- txt = copytext(txt, 1, 128)
- if(user.canUseTopic(src, BE_CLOSE))
+ var/txt = stripped_input(user, "What would you like to write on the back?", "Photo Writing", "", 128)
+ if(txt && user.canUseTopic(src, BE_CLOSE))
scribble = txt
..()
@@ -85,8 +84,8 @@
set category = "Object"
set src in usr
- var/n_name = copytext(sanitize(input(usr, "What would you like to label the photo?", "Photo Labelling", null) as text), 1, MAX_NAME_LEN)
+ var/n_name = stripped_input(usr, "What would you like to label the photo?", "Photo Labelling", "", MAX_NAME_LEN)
//loc.loc check is for making possible renaming photos in clipboards
- if((loc == usr || loc.loc && loc.loc == usr) && usr.stat == CONSCIOUS && usr.canmove && !usr.restrained())
+ if(n_name && (loc == usr || loc.loc && loc.loc == usr) && usr.stat == CONSCIOUS && usr.canmove && !usr.restrained())
name = "photo[(n_name ? text("- '[n_name]'") : null)]"
add_fingerprint(usr)
diff --git a/code/modules/pool/pool_controller.dm b/code/modules/pool/pool_controller.dm
new file mode 100644
index 0000000000..681adb0ef3
--- /dev/null
+++ b/code/modules/pool/pool_controller.dm
@@ -0,0 +1,422 @@
+#define POOL_NO_OVERDOSE_MEDICINE_MAX 5 //max units of no-overdose medicine to allow mobs to have through duplication
+
+//Originally stolen from paradise. Credits to tigercat2000.
+//Modified a lot by Kokojo and Tortellini Tony for hippiestation.
+//Heavily refactored by tgstation
+/obj/machinery/pool
+ icon = 'icons/obj/machines/pool.dmi'
+ anchored = TRUE
+ resistance_flags = INDESTRUCTIBLE
+
+/obj/machinery/pool/controller
+ name = "\improper Pool Controller"
+ desc = "An advanced substance generation and fluid tank management system that can refill the contents of a pool to a completely different substance in minutes."
+ icon_state = "poolc_3"
+ density = TRUE
+ use_power = TRUE
+ idle_power_usage = 75
+ /// How far it scans for pool objects
+ var/scan_range = 6
+ /// Is pool mist currently on?
+ var/mist_state = FALSE
+ /// Linked mist effects
+ var/list/obj/effect/mist/linked_mist = list()
+ /// Pool turfs
+ var/list/turf/open/pool/linked_turfs = list()
+ /// All mobs in pool
+ var/list/mob/living/mobs_in_pool = list()
+ /// Is the pool bloody?
+ var/bloody = 0
+ /// Last time we process_reagents()'d
+ var/last_reagent_process = 0
+ /// Maximum amount we will take from a beaker
+ var/max_beaker_transfer = 100
+ /// Minimum amount of a reagent for it to work on us
+ var/min_reagent_amount = 10
+ /// ADMINBUS ONLY - WHETHER OR NOT WE HAVE NOREACT ;)
+ var/noreact_reagents = FALSE
+ /// how fast in deciseconds between reagent processes
+ var/reagent_tick_interval = 5
+ /// Can we use unsafe temperatures
+ var/temperature_unlocked = FALSE
+ /// See __DEFINES/pool.dm, temperature defines
+ var/temperature = POOL_NORMAL
+ /// Whether or not the pool can be drained
+ var/drainable = FALSE
+ // Whether or not the pool is drained
+ var/drained = FALSE
+ /// Pool drain
+ var/obj/machinery/pool/drain/linked_drain
+ /// Pool filter
+ var/obj/machinery/pool/filter/linked_filter
+ /// Next world.time you can interact with settings
+ var/interact_delay = 0
+ /// Airlock style shocks
+ var/shocked = FALSE
+ /// Old reagent color, used to determine if update_color needs to reset colors.
+ var/old_rcolor
+ /// Just to prevent spam
+ var/draining = FALSE
+ /// Reagent blacklisting
+ var/respect_reagent_blacklist = TRUE
+
+/obj/machinery/pool/controller/examine(mob/user)
+ . = ..()
+ . += "Alt click to drain reagents."
+
+/obj/machinery/pool/controller/Initialize()
+ . = ..()
+ START_PROCESSING(SSfastprocess, src)
+ create_reagents(1000)
+ if(noreact_reagents)
+ reagents.reagents_holder_flags |= NO_REACT
+ wires = new /datum/wires/poolcontroller(src)
+ scan_things()
+
+/obj/machinery/pool/controller/Destroy()
+ STOP_PROCESSING(SSprocessing, src)
+ linked_drain = null
+ linked_filter = null
+ linked_turfs.Cut()
+ mobs_in_pool.Cut()
+ return ..()
+
+/obj/machinery/pool/controller/proc/scan_things()
+ var/list/cached = range(scan_range, src)
+ for(var/turf/open/pool/W in cached)
+ linked_turfs += W
+ W.controller = src
+ for(var/obj/machinery/pool/drain/pooldrain in cached)
+ linked_drain = pooldrain
+ linked_drain.controller = src
+ break
+ for(var/obj/machinery/pool/filter/F in cached)
+ linked_filter = F
+ linked_filter.controller = src
+ break
+
+/obj/machinery/pool/controller/emag_act(mob/user)
+ . = ..()
+ if(!(obj_flags & EMAGGED)) //If it is not already emagged, emag it.
+ to_chat(user, "You disable the [src]'s safety features.")
+ do_sparks(5, TRUE, src)
+ obj_flags |= EMAGGED
+ temperature_unlocked = TRUE
+ drainable = TRUE
+ log_game("[key_name(user)] emagged [src]")
+ message_admins("[key_name_admin(user)] emagged [src]")
+ else
+ to_chat(user, "The interface on [src] is already too damaged to short it again.")
+ return
+
+/obj/machinery/pool/controller/AltClick(mob/user)
+ . = ..()
+ if(!isliving(user) || !user.Adjacent(src) || !user.CanReach(src) || user.IsStun() || user.IsKnockdown() || user.incapacitated())
+ return FALSE
+ visible_message("[user] starts to drain [src]!")
+ draining = TRUE
+ if(!do_after(user, 50, target = src))
+ draining = FALSE
+ return TRUE
+ reagents.remove_all(INFINITY)
+ visible_message("[user] drains [src].")
+ say("Reagents cleared.")
+ update_color()
+ draining = FALSE
+ return TRUE
+
+/obj/machinery/pool/controller/attackby(obj/item/W, mob/user)
+ if(shocked && !(stat & NOPOWER))
+ shock(user,50)
+ if(stat & (BROKEN))
+ return
+ if(istype(W,/obj/item/reagent_containers))
+ if(W.reagents.total_volume) //check if there's reagent
+ user.visible_message("[user] is feeding [src] some chemicals from [W].")
+ if(do_after(user, 50, target = src))
+ for(var/datum/reagent/R in W.reagents.reagent_list)
+ if(R.type in GLOB.blacklisted_pool_reagents)
+ to_chat(user, "[src] cannot accept [R.name].")
+ return
+ if(R.reagent_state == SOLID)
+ to_chat(user, "The pool cannot accept reagents in solid form!.")
+ return
+ reagents.clear_reagents()
+ // This also reacts them. No nitroglycerin deathpools, sorry gamers :(
+ W.reagents.trans_to(reagents, max_beaker_transfer)
+ user.visible_message("[src] makes a slurping noise.", "All of the contents of [W] are quickly suctioned out by the machine!= min_reagent_amount) && (!respect_reagent_blacklist || R.can_synth))
+ reagent_names += R.name
+ else
+ reagents.remove_reagent(R.type, INFINITY)
+ rejected += R.name
+ if(length(reagent_names))
+ reagent_names = english_list(reagent_names)
+ var/msg = "POOL: [key_name(user)] has changed [src]'s chems to [reagent_names]"
+ log_game(msg)
+ message_admins(msg)
+ if(length(rejected))
+ rejected = english_list(rejected)
+ to_chat(user, "[src] rejects the following chemicals as they do not have at least [min_reagent_amount] units of volume: [rejected]")
+ update_color()
+ else
+ to_chat(user, "[src] beeps unpleasantly as it rejects the beaker. Why are you trying to feed it an empty beaker?")
+ return
+ else if(panel_open && is_wire_tool(W))
+ wires.interact(user)
+ else
+ return ..()
+
+/obj/machinery/pool/controller/screwdriver_act(mob/living/user, obj/item/W)
+ . = ..()
+ if(.)
+ return TRUE
+ cut_overlays()
+ panel_open = !panel_open
+ to_chat(user, "You [panel_open ? "open" : "close"] the maintenance panel.")
+ W.play_tool_sound(src)
+ if(panel_open)
+ add_overlay("wires")
+ return TRUE
+
+//procs
+/obj/machinery/pool/controller/proc/shock(mob/user, prb)
+ if(stat & (BROKEN|NOPOWER)) // unpowered, no shock
+ return FALSE
+ if(!prob(prb))
+ return FALSE
+ var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
+ s.set_up(5, 1, src)
+ s.start()
+ if(electrocute_mob(user, get_area(src), src, 0.7))
+ return TRUE
+ else
+ return FALSE
+
+/obj/machinery/pool/controller/proc/process_reagents()
+ if(last_reagent_process > world.time + reagent_tick_interval)
+ return
+ if(!length(reagents.reagent_list))
+ return
+ for(var/turf/open/pool/W in linked_turfs)
+ for(var/mob/living/carbon/human/swimee in W)
+ for(var/datum/reagent/R in reagents.reagent_list)
+ if(R.reagent_state == SOLID)
+ R.reagent_state = LIQUID
+ if(!swimee.reagents.has_reagent(POOL_NO_OVERDOSE_MEDICINE_MAX))
+ swimee.reagents.add_reagent(R.type, 0.5) //osmosis
+ reagents.reaction(swimee, VAPOR, 0.03) //3 percent. Need to find a way to prevent this from stacking chems at some point like the above.
+ for(var/obj/objects in W)
+ if(W.reagents)
+ W.reagents.reaction(objects, VAPOR, 1)
+ last_reagent_process = world.time
+
+/obj/machinery/pool/controller/process()
+ updateUsrDialog()
+ if(stat & (NOPOWER|BROKEN))
+ return
+ if(drained)
+ return
+ process_pool()
+ process_reagents()
+
+/obj/machinery/pool/controller/proc/process_pool()
+ if(drained)
+ return
+ for(var/mob/living/M in mobs_in_pool)
+ switch(temperature) //Apply different effects based on what the temperature is set to.
+ if(POOL_SCALDING) //Scalding
+ M.adjust_bodytemperature(50,0,500)
+ if(POOL_WARM) //Warm
+ M.adjust_bodytemperature(20,0,360) //Heats up mobs till the termometer shows up
+ //Normal temp does nothing, because it's just room temperature water.
+ if(POOL_COOL)
+ M.adjust_bodytemperature(-20,250) //Cools mobs till the termometer shows up
+ if(POOL_FRIGID) //Freezing
+ M.adjust_bodytemperature(-60) //cool mob at -35k per cycle, less would not affect the mob enough.
+ if(M.bodytemperature <= 50 && !M.stat)
+ M.apply_status_effect(/datum/status_effect/freon)
+ if(ishuman(M))
+ var/mob/living/carbon/human/drownee = M
+ if(!drownee || drownee.stat == DEAD)
+ return
+ if(drownee.resting && !drownee.internal)
+ if(drownee.stat != CONSCIOUS)
+ drownee.adjustOxyLoss(9)
+ else
+ drownee.adjustOxyLoss(4)
+ if(prob(35))
+ to_chat(drownee, "You're drowning!")
+
+/obj/machinery/pool/controller/proc/set_bloody(state)
+ if(bloody == state)
+ return
+ bloody = state
+ update_color()
+
+/obj/machinery/pool/controller/proc/update_color()
+ if(drained)
+ return
+ var/rcolor
+ if(reagents.reagent_list.len)
+ rcolor = mix_color_from_reagents(reagents.reagent_list)
+ if(rcolor == old_rcolor)
+ return // small performance upgrade hopefully?
+ old_rcolor = rcolor
+ for(var/X in linked_turfs)
+ var/turf/open/pool/color1 = X
+ if(bloody)
+ if(rcolor)
+ var/thecolor = BlendRGB(rgb(150, 20, 20), rcolor, 0.5)
+ color1.watereffect.add_atom_colour(thecolor, FIXED_COLOUR_PRIORITY)
+ color1.watertop.add_atom_colour(thecolor, FIXED_COLOUR_PRIORITY)
+ else
+ var/thecolor = rgb(150, 20, 20)
+ color1.watereffect.add_atom_colour(thecolor, FIXED_COLOUR_PRIORITY)
+ color1.watertop.add_atom_colour(thecolor, FIXED_COLOUR_PRIORITY)
+ else if(!bloody && rcolor)
+ color1.watereffect.add_atom_colour(rcolor, FIXED_COLOUR_PRIORITY)
+ color1.watertop.add_atom_colour(rcolor, FIXED_COLOUR_PRIORITY)
+ else
+ color1.watereffect.remove_atom_colour(FIXED_COLOUR_PRIORITY)
+ color1.watertop.remove_atom_colour(FIXED_COLOUR_PRIORITY)
+
+/obj/machinery/pool/controller/proc/update_temp()
+ if(mist_state)
+ if(temperature < POOL_SCALDING)
+ mist_off()
+ else
+ if(temperature == POOL_SCALDING)
+ mist_on()
+ update_icon()
+
+/obj/machinery/pool/controller/update_icon()
+ . = ..()
+ icon_state = "poolc_[temperature]"
+
+/obj/machinery/pool/controller/proc/CanUpTemp(mob/user)
+ if(temperature == POOL_WARM && (temperature_unlocked || issilicon(user) || IsAdminGhost(user)) || temperature < POOL_WARM)
+ return TRUE
+ return FALSE
+
+/obj/machinery/pool/controller/proc/CanDownTemp(mob/user)
+ if(temperature == POOL_COOL && (temperature_unlocked || issilicon(user) || IsAdminGhost(user)) || temperature > POOL_COOL)
+ return TRUE
+ return FALSE
+
+/obj/machinery/pool/controller/Topic(href, href_list)
+ if(..())
+ return
+ if(interact_delay > world.time)
+ return
+ if(href_list["IncreaseTemp"])
+ if(CanUpTemp(usr))
+ visible_message("[usr] presses a button on [src].")
+ temperature++
+ update_temp()
+ var/msg = "POOL: [key_name(usr)] increased [src]'s pool temperature at [COORD(src)] to [temperature]"
+ log_game(msg)
+ message_admins(msg)
+ interact_delay = world.time + 15
+ if(href_list["DecreaseTemp"])
+ if(CanDownTemp(usr))
+ visible_message("[usr] presses a button on [src].")
+ temperature--
+ update_temp()
+ var/msg = "POOL: [key_name(usr)] decreased [src]'s pool temperature at [COORD(src)] to [temperature]"
+ log_game(msg)
+ message_admins(msg)
+ interact_delay = world.time + 15
+ if(href_list["Activate Drain"])
+ if((drainable || issilicon(usr) || IsAdminGhost(usr)) && !linked_drain.active)
+ var/msg = "POOL: [key_name(usr)] activated [src]'s pool drain in [linked_drain.filling? "FILLING" : "DRAINING"] mode at [COORD(src)]"
+ log_game(msg)
+ message_admins(msg)
+ visible_message("[usr] presses a button on [src].")
+ mist_off()
+ interact_delay = world.time + 60
+ linked_drain.active = TRUE
+ linked_drain.cycles_left = 75
+ if(!linked_drain.filling)
+ new /obj/effect/whirlpool(linked_drain.loc)
+ temperature = POOL_NORMAL
+ else
+ new /obj/effect/waterspout(linked_drain.loc)
+ temperature = POOL_NORMAL
+ update_temp()
+ bloody = FALSE
+ updateUsrDialog()
+
+/obj/machinery/pool/controller/proc/temp2text()
+ switch(temperature)
+ if(POOL_FRIGID)
+ return "Frigid"
+ if(POOL_COOL)
+ return "Cool"
+ if(POOL_NORMAL)
+ return "Normal"
+ if(POOL_WARM)
+ return "Warm"
+ if(POOL_SCALDING)
+ return "Scalding"
+ else
+ return "Outside of possible range."
+
+/obj/machinery/pool/controller/ui_interact(mob/user)
+ . = ..()
+ if(.)
+ return
+ if(shocked && !(stat & NOPOWER))
+ shock(user,50)
+ if(panel_open && !isAI(user))
+ return wires.interact(user)
+ if(stat & (NOPOWER|BROKEN))
+ return
+ var/datum/browser/popup = new(user, "Pool Controller", name, 300, 450)
+ var/dat = ""
+ if(interact_delay > world.time)
+ dat += "[round((interact_delay - world.time)/10, 0.1)] seconds left until [src] can operate again. "
+ dat += text({"
+
Temperature
+
+ Current temperature: [temp2text()]
+ [CanUpTemp(user) ? "Increase Temperature " : "Increase Temperature "]
+ [CanDownTemp(user) ? "Decrease Temperature " : "Decrease Temperature "]
+
+
Drain
+
+ Drain status: [(issilicon(user) || IsAdminGhost(user) || drainable) ? "Enabled" : "Disabled"]
+ Pool status: "})
+ if(!drained)
+ dat += "Full "
+ else
+ dat += "Drained "
+ if((issilicon(user) || IsAdminGhost(user) || drainable) && !linked_drain.active)
+ dat += "[drained ? "Fill" : "Drain"] Pool "
+ popup.set_content(dat)
+ popup.open()
+
+/obj/machinery/pool/controller/proc/reset(wire)
+ switch(wire)
+ if(WIRE_SHOCK)
+ if(!wires.is_cut(wire))
+ shocked = FALSE
+
+/obj/machinery/pool/controller/proc/mist_on() //Spawn /obj/effect/mist (from the shower) on all linked pool tiles
+ if(mist_state)
+ return
+ mist_state = TRUE
+ for(var/X in linked_turfs)
+ var/turf/open/pool/W = X
+ if(W.filled)
+ var/M = new /obj/effect/mist(W)
+ linked_mist += M
+
+/obj/machinery/pool/controller/proc/mist_off() //Delete all /obj/effect/mist from all linked pool tiles.
+ for(var/M in linked_mist)
+ qdel(M)
+ mist_state = FALSE
diff --git a/code/modules/pool/pool_drain.dm b/code/modules/pool/pool_drain.dm
new file mode 100644
index 0000000000..940f7cd219
--- /dev/null
+++ b/code/modules/pool/pool_drain.dm
@@ -0,0 +1,160 @@
+/obj/machinery/pool/drain
+ name = "drain"
+ icon_state = "drain"
+ desc = "A suction system to remove the contents of the pool, and sometimes small objects. Do not insert fingers."
+ anchored = TRUE
+ /// Active/on?
+ var/active = FALSE
+ /// Filling or draining
+ var/filling = FALSE
+ /// Drain item suction range
+ var/item_suction_range = 2
+ /// Fill mode knock away range
+ var/fill_push_range = 6
+ /// Drain mode suction range
+ var/drain_suck_range = 6
+ /// Parent controller
+ var/obj/machinery/pool/controller/controller
+ /// Cycles left for fill/drain while active
+ var/cycles_left = 0
+ /// Mobs we are swirling around
+ var/list/whirling_mobs
+ /// Suck in once per x ticks
+ var/suck_in_once_per = 3
+
+ var/cooldown
+
+/obj/machinery/pool/drain/Initialize()
+ START_PROCESSING(SSfastprocess, src)
+ whirling_mobs = list()
+ return ..()
+
+/obj/machinery/pool/drain/Destroy()
+ STOP_PROCESSING(SSfastprocess, src)
+ controller.linked_drain = null
+ controller = null
+ whirling_mobs = null
+ return ..()
+
+/obj/machinery/pool/drain/proc/is_in_our_pool(atom/A)
+ . = FALSE
+ if(istype(A.loc, /turf/open/pool))
+ var/turf/open/pool/P = A.loc
+ if(P.controller == controller)
+ . = TRUE
+
+// This should probably start using move force sometime in the future but I'm lazy.
+/obj/machinery/pool/drain/process()
+ if(!filling)
+ for(var/obj/item/I in range(min(item_suction_range, 10), src))
+ if(!I.anchored && (I.w_class <= WEIGHT_CLASS_SMALL))
+ step_towards(I, src)
+ if((I.w_class <= WEIGHT_CLASS_TINY) && (get_dist(I, src) == 0))
+ I.forceMove(controller.linked_filter)
+ if(active)
+ if(filling)
+ if(cycles_left-- > 0)
+ playsound(src, 'sound/effects/fillingwatter.ogg', 100, TRUE)
+ for(var/obj/O in orange(min(fill_push_range, 10), src))
+ if(!O.anchored && is_in_our_pool(O))
+ step_away(O, src)
+ for(var/mob/M in orange(min(fill_push_range, 10), src)) //compiler fastpath apparently?
+ if(!M.anchored && isliving(M) && is_in_our_pool(M))
+ step_away(M, src)
+ else
+ for(var/turf/open/pool/P in controller.linked_turfs)
+ P.filled = TRUE
+ P.update_icon()
+ for(var/obj/effect/waterspout/S in range(1, src))
+ qdel(S)
+ controller.drained = FALSE
+ if(controller.bloody < 1000)
+ controller.bloody /= 2
+ else
+ controller.bloody /= 4
+ controller.update_color()
+ filling = FALSE
+ active = FALSE
+ else
+ if(cycles_left-- > 0)
+ playsound(src, 'sound/effects/pooldrain.ogg', 100, TRUE)
+ playsound(src, "water_wade", 60, TRUE)
+ for(var/obj/O in orange(min(drain_suck_range, 10), src))
+ if(!O.anchored && is_in_our_pool(O))
+ step_towards(O, src)
+ for(var/mob/M in orange(min(drain_suck_range, 10), src))
+ if(isliving(M) && !M.anchored && is_in_our_pool(M))
+ if(!(cycles_left % suck_in_once_per))
+ step_towards(M, src)
+ whirl_mob(M)
+ if(ishuman(M) && (get_dist(M, src) <= 1))
+ var/mob/living/carbon/human/H = M
+ playsound(src, pick('sound/misc/crack.ogg','sound/misc/crunch.ogg'), 50, TRUE)
+ if(H.lying) //down for any reason
+ H.adjustBruteLoss(2)
+ to_chat(H, "You're caught in the drain!")
+ else
+ H.apply_damage(2.5, BRUTE, pick(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)) //drain should only target the legs
+ to_chat(H, "Your legs are caught in the drain!")
+ else
+ for(var/turf/open/pool/P in controller.linked_turfs)
+ P.filled = FALSE
+ P.update_icon()
+ for(var/obj/effect/whirlpool/W in range(1, src))
+ qdel(W)
+ controller.drained = TRUE
+ controller.mist_off()
+ active = FALSE
+ filling = TRUE
+
+/// dangerous proc don't fuck with, admins
+/obj/machinery/pool/drain/proc/whirl_mob(mob/living/L, duration = 8, delay = 1)
+ set waitfor = FALSE
+ if(whirling_mobs[L])
+ return
+ whirling_mobs[L] = TRUE
+ for(var/i in 1 to min(duration, 100))
+ L.setDir(turn(L.dir, 90))
+ sleep(delay)
+ if(QDELETED(L))
+ break
+ if(QDELETED(src))
+ return
+ whirling_mobs -= L
+
+/obj/machinery/pool/filter
+ name = "Filter"
+ icon_state = "filter"
+ desc = "The part of the pool where all the IDs, ATV keys, and pens, and other dangerous things get trapped."
+ var/obj/machinery/pool/controller/controller
+
+/obj/machinery/pool/filter/Destroy()
+ controller.linked_filter = null
+ controller = null
+ return ..()
+
+/obj/machinery/pool/filter/emag_act(mob/living/user)
+ . = ..()
+ if(!(obj_flags & EMAGGED))
+ to_chat(user, "You disable the [src]'s shark filter! Run!")
+ obj_flags |= EMAGGED
+ do_sparks(5, TRUE, src)
+ icon_state = "filter_b"
+ addtimer(CALLBACK(src, /obj/machinery/pool/filter/proc/spawn_shark), 50)
+ var/msg = "[key_name(user)] emagged the pool filter and spawned a shark"
+ log_game(msg)
+ message_admins(msg)
+
+/obj/machinery/pool/filter/proc/spawn_shark()
+ if(prob(50))
+ new /mob/living/simple_animal/hostile/shark(loc)
+ else
+ if(prob(50))
+ new /mob/living/simple_animal/hostile/shark/kawaii(loc)
+ else
+ new /mob/living/simple_animal/hostile/shark/laser(loc)
+
+/obj/machinery/pool/filter/attack_hand(mob/user)
+ to_chat(user, "You search the filter.")
+ for(var/obj/O in contents)
+ O.forceMove(loc)
diff --git a/code/modules/pool/pool_effects.dm b/code/modules/pool/pool_effects.dm
new file mode 100644
index 0000000000..8d5192cd30
--- /dev/null
+++ b/code/modules/pool/pool_effects.dm
@@ -0,0 +1,29 @@
+/obj/effect/splash
+ name = "splash"
+ desc = "Wataaa!."
+ icon = 'icons/turf/pool.dmi'
+ icon_state = "splash"
+ layer = ABOVE_ALL_MOB_LAYER
+
+/obj/effect/whirlpool
+ name = "Whirlpool"
+ icon = 'icons/effects/96x96.dmi'
+ icon_state = "whirlpool"
+ layer = 5
+ anchored = TRUE
+ mouse_opacity = 0
+ pixel_x = -32
+ pixel_y = -32
+ alpha = 90
+
+/obj/effect/waterspout
+ name = "Waterspout"
+ icon = 'icons/effects/96x96.dmi'
+ icon_state = "waterspout"
+ color = "#3399AA"
+ layer = 5
+ anchored = TRUE
+ mouse_opacity = 0
+ pixel_x = -32
+ pixel_y = -32
+ alpha = 120
diff --git a/code/modules/pool/pool_main.dm b/code/modules/pool/pool_main.dm
new file mode 100644
index 0000000000..2b260b5c0b
--- /dev/null
+++ b/code/modules/pool/pool_main.dm
@@ -0,0 +1,191 @@
+/turf/open/pool
+ icon = 'icons/turf/pool.dmi'
+ name = "poolwater"
+ desc = "You're safer here than in the deep."
+ icon_state = "pool_tile"
+ heat_capacity = INFINITY
+ var/filled = TRUE
+ var/next_splash = 0
+ var/obj/machinery/pool/controller/controller
+ var/obj/effect/overlay/water/watereffect
+ var/obj/effect/overlay/water/top/watertop
+
+/turf/open/pool/Initialize(mapload)
+ . = ..()
+ update_icon()
+
+/turf/open/pool/Destroy()
+ if(controller)
+ controller.linked_turfs -= src
+ controller = null
+ QDEL_NULL(watereffect)
+ QDEL_NULL(watertop)
+ return ..()
+
+/turf/open/pool/update_icon()
+ . = ..()
+ if(!filled)
+ name = "drained pool"
+ desc = "No diving!"
+ QDEL_NULL(watereffect)
+ QDEL_NULL(watertop)
+ else
+ name = "poolwater"
+ desc = "You're safer here than in the deep."
+ watereffect = new /obj/effect/overlay/water(src)
+ watertop = new /obj/effect/overlay/water/top(src)
+
+/obj/effect/overlay/water
+ name = "water"
+ icon = 'icons/turf/pool.dmi'
+ icon_state = "bottom"
+ density = FALSE
+ mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ layer = ABOVE_MOB_LAYER
+ anchored = TRUE
+ resistance_flags = INDESTRUCTIBLE
+
+/obj/effect/overlay/water/top
+ icon_state = "top"
+ layer = BELOW_MOB_LAYER
+
+// Mousedrop hook to normal turfs to get out of pools.
+/turf/open/MouseDrop_T(atom/from, mob/user)
+ // I could make this /open/floor and not have the !istype but ehh - kev
+ if(isliving(from) && HAS_TRAIT(from, TRAIT_SWIMMING) && isliving(user) && ((user == from) || user.CanReach(from)) && !user.IsStun() && !user.IsKnockdown() && !user.incapacitated() && !istype(src, /turf/open/pool))
+ var/mob/living/L = from
+ //The element only exists if you're on water and a living mob, so let's skip those checks.
+ var/pre_msg
+ var/post_msg
+ if(user == from)
+ pre_msg = "[L] is getting out of the pool."
+ post_msg = "[L] gets out of the pool."
+ else
+ pre_msg = "[L] is being pulled out of the pool by [user]."
+ post_msg = "[user] pulls [L] out of the pool."
+ L.visible_message(pre_msg)
+ if(do_mob(user, L, 20))
+ L.visible_message(post_msg)
+ L.forceMove(src)
+ else
+ return ..()
+
+// Exit check
+/turf/open/pool/Exit(atom/movable/AM, atom/newloc)
+ if(!AM.has_gravity(src))
+ return ..()
+ if(isliving(AM) || isstructure(AM))
+ if(AM.throwing)
+ return ..() //WHEEEEEEEEEEE
+ if(istype(AM, /obj/structure) && isliving(AM.pulledby))
+ return ..() //people pulling stuff out of pool
+ if(!ishuman(AM))
+ return ..() //human weak, monkey (and anyone else) ook ook eek eek strong
+ if(isliving(AM) && (locate(/obj/structure/pool/ladder) in src))
+ return ..() //climbing out
+ return istype(newloc, /turf/open/pool)
+ return ..()
+
+// Exited logic
+/turf/open/pool/Exited(atom/A, atom/newLoc)
+ . = ..()
+ if(isliving(A))
+ var/turf/open/pool/P = newLoc
+ if(!istype(P) || (P.controller != controller))
+ controller?.mobs_in_pool -= A
+
+// Entered logic
+/turf/open/pool/Entered(atom/movable/AM, atom/oldloc)
+ if(istype(AM, /obj/effect/decal/cleanable))
+ var/obj/effect/decal/cleanable/C = AM
+ if(prob(C.bloodiness))
+ controller.set_bloody(TRUE)
+ QDEL_IN(AM, 25)
+ animate(AM, alpha = 10, time = 20)
+ return ..()
+ if(!AM.has_gravity(src))
+ return ..()
+ if(isliving(AM))
+ var/mob/living/victim = AM
+ if(!HAS_TRAIT(victim, TRAIT_SWIMMING)) //poor guy not swimming time to dunk them!
+ victim.AddElement(/datum/element/swimming)
+ controller.mobs_in_pool += victim
+ if(locate(/obj/structure/pool/ladder) in src) //safe climbing
+ return
+ if(iscarbon(AM)) //FUN TIME!
+ var/mob/living/carbon/H = victim
+ if(filled)
+ if (H.wear_mask && H.wear_mask.flags_cover & MASKCOVERSMOUTH)
+ H.visible_message("[H] falls in the water!",
+ "You fall in the water!")
+ playsound(src, 'sound/effects/splash.ogg', 60, TRUE, 1)
+ H.Knockdown(20)
+ return
+ else
+ H.Knockdown(60)
+ H.adjustOxyLoss(5)
+ H.emote("cough")
+ H.visible_message("[H] falls in and takes a drink!",
+ "You fall in and swallow some water!")
+ playsound(src, 'sound/effects/splash.ogg', 60, TRUE, 1)
+ else if(!H.head || !(H.head.armor.getRating("melee") > 20))
+ if(prob(75))
+ H.visible_message("[H] falls in the drained pool!",
+ "You fall in the drained pool!")
+ H.adjustBruteLoss(7)
+ H.Knockdown(80)
+ playsound(src, 'sound/effects/woodhit.ogg', 60, TRUE, 1)
+ else
+ H.visible_message("[H] falls in the drained pool, and cracks his skull!",
+ "You fall in the drained pool, and crack your skull!")
+ H.apply_damage(15, BRUTE, "head")
+ H.Knockdown(200) // This should hurt. And it does.
+ playsound(src, 'sound/effects/woodhit.ogg', 60, TRUE, 1)
+ playsound(src, 'sound/misc/crack.ogg', 100, TRUE)
+ else
+ H.visible_message("[H] falls in the drained pool, but had an helmet!",
+ "You fall in the drained pool, but you had an helmet!")
+ H.Knockdown(40)
+ playsound(src, 'sound/effects/woodhit.ogg', 60, TRUE, 1)
+ else if(filled)
+ victim.adjustStaminaLoss(1)
+ playsound(src, "water_wade", 20, TRUE)
+ return ..()
+
+/turf/open/pool/MouseDrop_T(atom/from, mob/user)
+ . = ..()
+ if(!isliving(from))
+ return
+ var/mob/living/victim = from
+ if(user.stat || user.lying || !Adjacent(user) || !from.Adjacent(user) || !iscarbon(user) || !victim.has_gravity(src) || HAS_TRAIT(victim, TRAIT_SWIMMING))
+ return
+ var/victimname = victim == user? "themselves" : "[victim]"
+ var/starttext = victim == user? "[user] is descending into [src]." : "[user] is lowering [victim] into [src]."
+ user.visible_message("[starttext]")
+ if(do_mob(user, victim, 20))
+ user.visible_message("[user] lowers [victimname] into [src].")
+ victim.AddElement(/datum/element/swimming) //make sure they have it so they don't fall/whatever
+ victim.forceMove(src)
+
+/turf/open/pool/attackby(obj/item/W, mob/living/user)
+ if(istype(W, /obj/item/mop) && filled)
+ W.reagents.add_reagent("water", 5)
+ to_chat(user, "You wet [W] in [src].")
+ playsound(loc, 'sound/effects/slosh.ogg', 25, TRUE)
+ else
+ return ..()
+
+/turf/open/pool/attack_hand(mob/living/user)
+ . = ..()
+ if(.)
+ return
+ if((user.loc != src) && !user.IsStun() && !user.IsKnockdown() && !user.incapacitated() && Adjacent(user) && HAS_TRAIT(user, TRAIT_SWIMMING) && filled && (next_splash < world.time))
+ playsound(src, 'sound/effects/watersplash.ogg', 8, TRUE, 1)
+ next_splash = world.time + 25
+ var/obj/effect/splash/S = new(src)
+ animate(S, alpha = 0, time = 8)
+ QDEL_IN(S, 10)
+ for(var/mob/living/carbon/human/H in src)
+ if(!H.wear_mask && (H.stat == CONSCIOUS))
+ H.emote("cough")
+ H.adjustStaminaLoss(4)
diff --git a/code/modules/pool/pool_noodles.dm b/code/modules/pool/pool_noodles.dm
new file mode 100644
index 0000000000..6118354792
--- /dev/null
+++ b/code/modules/pool/pool_noodles.dm
@@ -0,0 +1,32 @@
+//Pool noodles
+
+/obj/item/toy/poolnoodle
+ icon = 'icons/obj/toy.dmi'
+ icon_state = "noodle"
+ name = "pool noodle"
+ desc = "A strange, bulky, bendable toy that can annoy people."
+ force = 0
+ color = "#000000"
+ w_class = WEIGHT_CLASS_SMALL
+ throwforce = 1
+ throw_speed = 10 //weeee
+ hitsound = 'sound/weapons/tap.ogg'
+ attack_verb = list("flogged", "poked", "jabbed", "slapped", "annoyed")
+ lefthand_file = 'icons/mob/inhands/items_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/items_righthand.dmi'
+
+/obj/item/toy/poolnoodle/attack(target as mob, mob/living/user as mob)
+ . = ..()
+ user.spin(prob(20)? 16 : 8, 1)
+
+/obj/item/toy/poolnoodle/red
+ item_state = "noodlered"
+ color = "#ff4c4c"
+
+/obj/item/toy/poolnoodle/blue
+ item_state = "noodleblue"
+ color = "#3232ff"
+
+/obj/item/toy/poolnoodle/yellow
+ item_state = "noodleyellow"
+ color = "#ffff66"
diff --git a/code/modules/pool/pool_structures.dm b/code/modules/pool/pool_structures.dm
new file mode 100644
index 0000000000..92350abf44
--- /dev/null
+++ b/code/modules/pool/pool_structures.dm
@@ -0,0 +1,159 @@
+/obj/structure/pool
+ name = "pool"
+ icon = 'icons/obj/machines/pool.dmi'
+ anchored = TRUE
+ resistance_flags = UNACIDABLE|INDESTRUCTIBLE
+
+/obj/structure/pool/ladder
+ name = "Ladder"
+ icon_state = "ladder"
+ desc = "Are you getting in or are you getting out?."
+ layer = ABOVE_MOB_LAYER
+ dir = EAST
+
+/obj/structure/pool/ladder/attack_hand(mob/living/user)
+ . = ..()
+ if(.)
+ return
+ if(!HAS_TRAIT(user, TRAIT_SWIMMING))
+ if(user.CanReach(src))
+ user.AddElement(/datum/element/swimming)
+ user.forceMove(get_step(src, dir))
+ else
+ if(user.loc == loc)
+ user.forceMove(get_step(src, turn(dir, 180))) //If this moves them out the element cleans up after itself.
+
+/obj/structure/pool/Rboard
+ name = "JumpBoard"
+ density = FALSE
+ icon_state = "boardright"
+ desc = "The less-loved portion of the jumping board."
+ dir = EAST
+
+/obj/structure/pool/Lboard
+ name = "JumpBoard"
+ icon_state = "boardleft"
+ desc = "Get on there to jump!"
+ layer = FLY_LAYER
+ dir = WEST
+ var/jumping = FALSE
+ var/timer
+
+/obj/structure/pool/Lboard/proc/backswim()
+ if(jumping)
+ for(var/mob/living/jumpee in loc) //hackzors.
+ playsound(jumpee, 'sound/effects/splash.ogg', 60, TRUE, 1)
+ if(!HAS_TRAIT(jumpee, TRAIT_SWIMMING))
+ jumpee.AddElement(/datum/element/swimming)
+ jumpee.Stun(2)
+
+/obj/structure/pool/Lboard/proc/reset_position(mob/user, initial_layer, initial_px, initial_py)
+ user.layer = initial_layer
+ user.pixel_x = initial_px
+ user.pixel_y = initial_py
+
+/obj/structure/pool/Lboard/attack_hand(mob/living/user)
+ if(iscarbon(user))
+ var/mob/living/carbon/jumper = user
+ if(jumping)
+ to_chat(user, "Someone else is already making a jump!")
+ return
+ var/turf/T = get_turf(src)
+ if(HAS_TRAIT(user, TRAIT_SWIMMING))
+ return
+ else
+ if(Adjacent(jumper))
+ jumper.visible_message("[user] climbs up \the [src]!", \
+ "You climb up \the [src] and prepares to jump!")
+ jumper.Stun(40)
+ jumping = TRUE
+ var/original_layer = jumper.layer
+ var/original_px = jumper.pixel_x
+ var/original_py = jumper.pixel_y
+ jumper.layer = RIPPLE_LAYER
+ jumper.pixel_x = 3
+ jumper.pixel_y = 7
+ jumper.dir = WEST
+ jumper.AddElement(/datum/element/swimming)
+ sleep(1)
+ jumper.forceMove(T)
+ addtimer(CALLBACK(src, .proc/dive, jumper, original_layer, original_px, original_py), 10)
+
+/obj/structure/pool/Lboard/proc/dive(mob/living/carbon/jumper, original_layer, original_px, original_py)
+ switch(rand(1, 100))
+ if(1 to 20)
+ jumper.visible_message("[jumper] goes for a small dive!", \
+ "You go for a small dive.")
+ sleep(15)
+ backswim()
+ var/atom/throw_target = get_edge_target_turf(src, dir)
+ jumper.throw_at(throw_target, 1, 1, callback = CALLBACK(src, .proc/on_finish_jump, jumper))
+
+ if(21 to 40)
+ jumper.visible_message("[jumper] goes for a dive!", \
+ "You're going for a dive!")
+ sleep(20)
+ backswim()
+ var/atom/throw_target = get_edge_target_turf(src, dir)
+ jumper.throw_at(throw_target, 2, 1, callback = CALLBACK(src, .proc/on_finish_jump, jumper))
+
+ if(41 to 60)
+ jumper.visible_message("[jumper] goes for a long dive! Stay far away!", \
+ "You're going for a long dive!!")
+ sleep(25)
+ backswim()
+ var/atom/throw_target = get_edge_target_turf(src, dir)
+ jumper.throw_at(throw_target, 3, 1, callback = CALLBACK(src, .proc/on_finish_jump, jumper))
+
+ if(61 to 80)
+ jumper.visible_message("[jumper] goes for an awesome dive! Don't stand in [jumper.p_their()] way!", \
+ "You feel like this dive will be awesome")
+ sleep(30)
+ backswim()
+ var/atom/throw_target = get_edge_target_turf(src, dir)
+ jumper.throw_at(throw_target, 4, 1, callback = CALLBACK(src, .proc/on_finish_jump, jumper))
+ if(81 to 91)
+ sleep(20)
+ backswim()
+ jumper.visible_message("[jumper] misses [jumper.p_their()] step!", \
+ "You misstep!")
+ var/atom/throw_target = get_edge_target_turf(src, dir)
+ jumper.throw_at(throw_target, 0, 1, callback = CALLBACK(src, .proc/on_finish_jump, jumper))
+ jumper.Knockdown(100)
+ jumper.adjustBruteLoss(10)
+
+ if(91 to 100)
+ jumper.visible_message("[jumper] is preparing for the legendary dive! Can [jumper.p_they()] make it?", \
+ "You start preparing for a legendary dive!")
+ jumper.SpinAnimation(7,1)
+
+ sleep(30)
+ if(prob(75))
+ backswim()
+ jumper.visible_message("[jumper] fails!", \
+ "You can't quite do it!")
+ var/atom/throw_target = get_edge_target_turf(src, dir)
+ jumper.throw_at(throw_target, 1, 1, callback = CALLBACK(src, .proc/on_finish_jump, jumper))
+ else
+ jumper.fire_stacks = min(1,jumper.fire_stacks + 1)
+ jumper.IgniteMob()
+ sleep(5)
+ backswim()
+ jumper.visible_message("[jumper] bursts into flames of pure awesomness!", \
+ "No one can stop you now!")
+ var/atom/throw_target = get_edge_target_turf(src, dir)
+ jumper.throw_at(throw_target, 6, 1, callback = CALLBACK(src, .proc/on_finish_jump, jumper))
+ addtimer(CALLBACK(src, .proc/togglejumping), 35)
+ reset_position(jumper, original_layer, original_px, original_py)
+
+/obj/structure/pool/Lboard/proc/togglejumping()
+ jumping = FALSE
+
+/obj/structure/pool/Lboard/proc/on_finish_jump(mob/living/victim)
+ if(istype(victim.loc, /turf/open/pool))
+ var/turf/open/pool/P = victim.loc
+ if(!P.filled) //you dun fucked up now
+ to_chat(victim, "That was stupid of you..")
+ victim.visible_message("[victim] smashes into the ground!")
+ victim.apply_damage(50)
+ victim.Knockdown(200)
diff --git a/code/modules/pool/pool_wires.dm b/code/modules/pool/pool_wires.dm
new file mode 100644
index 0000000000..7341c503a9
--- /dev/null
+++ b/code/modules/pool/pool_wires.dm
@@ -0,0 +1,59 @@
+#define POOL_WIRE_DRAIN "drain"
+#define POOL_WIRE_TEMP "temp"
+
+
+/datum/wires/poolcontroller
+ holder_type = /obj/machinery/pool/controller
+ proper_name = "Pool"
+
+/datum/wires/poolcontroller/New(atom/holder)
+ wires = list(
+ POOL_WIRE_DRAIN, WIRE_SHOCK, WIRE_ZAP, POOL_WIRE_TEMP
+ )
+ add_duds(3)
+ ..()
+
+/datum/wires/poolcontroller/interactable(mob/user)
+ var/obj/machinery/pool/controller/P = holder
+ if(P.panel_open)
+ return TRUE
+
+/datum/wires/poolcontroller/get_status()
+ var/obj/machinery/pool/controller/P = holder
+ var/list/status = list()
+ status += "The blue light is [P.drainable ? "on" : "off"]."
+ status += "The red light is [P.temperature_unlocked ? "on" : "off"]."
+ status += "The yellow light is [P.shocked ? "on" : "off"]."
+ return status
+
+/datum/wires/poolcontroller/on_pulse(wire)
+ var/obj/machinery/pool/controller/P = holder
+ switch(wire)
+ if(POOL_WIRE_DRAIN)
+ P.drainable = FALSE
+ if(POOL_WIRE_TEMP)
+ P.temperature_unlocked = FALSE
+ if(WIRE_SHOCK)
+ P.shocked = !P.shocked
+ addtimer(CALLBACK(P, /obj/machinery/autolathe.proc/reset, wire), 60)
+
+/datum/wires/poolcontroller/on_cut(wire, mend)
+ var/obj/machinery/pool/controller/P = holder
+ switch(wire)
+ if(POOL_WIRE_DRAIN)
+ if(mend)
+ P.drainable = FALSE
+ else
+ P.drainable = TRUE
+ if(POOL_WIRE_TEMP)
+ if(mend)
+ P.temperature_unlocked = FALSE
+ else
+ P.temperature_unlocked = TRUE
+ if(WIRE_ZAP)
+ P.shock(usr, 50)
+ if(WIRE_SHOCK)
+ if(mend)
+ P.stat &= ~NOPOWER
+ else
+ P.stat |= NOPOWER
\ No newline at end of file
diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm
index b7cbcdb9d8..db3e3c83c6 100644
--- a/code/modules/power/apc.dm
+++ b/code/modules/power/apc.dm
@@ -56,6 +56,8 @@
integrity_failure = 50
var/damage_deflection = 10
resistance_flags = FIRE_PROOF
+ armor = list("melee" = 40, "bullet" = 40, "laser" = 40, "energy" = 100, "bomb" = 30, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 50)
+ req_access = list(ACCESS_ENGINE_EQUIP)
interaction_flags_machine = INTERACT_MACHINE_WIRES_IF_OPEN | INTERACT_MACHINE_ALLOW_SILICON | INTERACT_MACHINE_OPEN_SILICON
var/lon_range = 1.5
@@ -104,6 +106,8 @@
var/update_overlay = -1
var/icon_update_needed = FALSE
var/obj/machinery/computer/apc_control/remote_control = null
+ var/mob/living/carbon/hijacker
+ var/hijackerlast = TRUE
/obj/machinery/power/apc/unlocked
locked = FALSE
@@ -148,25 +152,48 @@
if(terminal)
terminal.connect_to_network()
-/obj/machinery/power/apc/New(turf/loc, var/ndir, var/building=0)
- if (!req_access)
- req_access = list(ACCESS_ENGINE_EQUIP)
- if (!armor)
- armor = list("melee" = 40, "bullet" = 40, "laser" = 40, "energy" = 100, "bomb" = 30, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 50)
- ..()
+/obj/machinery/power/apc/Initialize(mapload, ndir, building = FALSE)
+ . = ..()
+ tdir = ndir || dir
+ var/area/A = get_base_area(src)
+ if(!building)
+ has_electronics = APC_ELECTRONICS_SECURED
+ // is starting with a power cell installed, create it and set its charge level
+ if(cell_type)
+ cell = new cell_type
+ cell.charge = start_charge * cell.maxcharge / 100 // (convert percentage to actual value)
+
+ //if area isn't specified use current
+ if(areastring)
+ area = get_area_instance_from_text(areastring)
+ if(!area)
+ area = A
+ stack_trace("Bad areastring path for [src], [src.areastring]")
+ else if(isarea(A) && !areastring)
+ area = A
+ if(auto_name)
+ name = "\improper [A.name] APC"
+ update_icon()
+
+ make_terminal()
+ update_nightshift_auth_requirement()
+
+ else
+ area = A
+ opened = APC_COVER_OPENED
+ operating = FALSE
+ name = "\improper [A.name] APC"
+ stat |= MAINT
+ update_icon()
+ addtimer(CALLBACK(src, .proc/update), 5)
+
GLOB.apcs_list += src
wires = new /datum/wires/apc(src)
// offset 24 pixels in direction of dir
// this allows the APC to be embedded in a wall, yet still inside an area
- if (building)
- setDir(ndir)
- src.tdir = dir // to fix Vars bug
setDir(SOUTH)
- if(auto_name)
- name = "\improper [get_area(src)] APC"
-
switch(tdir)
if(NORTH)
pixel_y = 23
@@ -176,14 +203,6 @@
pixel_x = 24
if(WEST)
pixel_x = -25
- if (building)
- area = get_area(src)
- opened = APC_COVER_OPENED
- operating = FALSE
- name = "[area.name] APC"
- stat |= MAINT
- src.update_icon()
- addtimer(CALLBACK(src, .proc/update), 5)
/obj/machinery/power/apc/Destroy()
GLOB.apcs_list -= src
@@ -217,33 +236,6 @@
terminal.setDir(tdir)
terminal.master = src
-/obj/machinery/power/apc/Initialize(mapload)
- . = ..()
- if(!mapload)
- return
- has_electronics = APC_ELECTRONICS_SECURED
- // is starting with a power cell installed, create it and set its charge level
- if(cell_type)
- cell = new cell_type
- cell.charge = start_charge * cell.maxcharge / 100 // (convert percentage to actual value)
-
- var/area/A = src.loc.loc
-
- //if area isn't specified use current
- if(areastring)
- src.area = get_area_instance_from_text(areastring)
- if(!src.area)
- src.area = A
- stack_trace("Bad areastring path for [src], [src.areastring]")
- else if(isarea(A) && src.areastring == null)
- src.area = A
- update_icon()
-
- make_terminal()
- update_nightshift_auth_requirement()
-
- addtimer(CALLBACK(src, .proc/update), 5)
-
/obj/machinery/power/apc/examine(mob/user)
. = ..()
if(stat & BROKEN)
@@ -270,7 +262,7 @@
. += "Alt-Click the APC to [ locked ? "unlock" : "lock"] the interface."
- if(issilicon(user))
+ if(area.hasSiliconAccessInArea(user))
. += "Ctrl-Click the APC to switch the breaker [ operating ? "off" : "on"]."
// update the APC icon to show the three base states
@@ -309,12 +301,15 @@
if(!(update_state & UPSTATE_ALLGOOD))
SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays)
-
+ var/hijackerreturn
+ if (hijacker)
+ var/obj/item/implant/hijack/H = hijacker.getImplant(/obj/item/implant/hijack)
+ hijackerreturn = H && !H.stealthmode
if(update & 2)
SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays)
if(!(stat & (BROKEN|MAINT)) && update_state & UPSTATE_ALLGOOD)
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)
+ SSvis_overlays.add_vis_overlay(src, icon, "apco3-[hijackerreturn ? "3" : charging]", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir)
if(operating)
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)
@@ -329,6 +324,8 @@
light_color = LIGHT_COLOR_BLUE
if(APC_FULLY_CHARGED)
light_color = LIGHT_COLOR_GREEN
+ if (hijackerreturn)
+ light_color = LIGHT_COLOR_YELLOW
set_light(lon_range)
else if(update_state & UPSTATE_BLUESCREEN)
light_color = LIGHT_COLOR_BLUE
@@ -397,14 +394,19 @@
else if(environ==2)
update_overlay |= APC_UPOVERLAY_ENVIRON2
-
var/results = 0
- if(last_update_state == update_state && last_update_overlay == update_overlay)
+ var/hijackerreturn
+ if (hijacker)
+ var/obj/item/implant/hijack/H = hijacker.getImplant(/obj/item/implant/hijack)
+ hijackerreturn = H && !H.stealthmode
+ if(last_update_state == update_state && last_update_overlay == update_overlay && hijackerreturn == hijackerlast)
return 0
if(last_update_state != update_state)
results += 1
- if(last_update_overlay != update_overlay)
+ if(last_update_overlay != update_overlay || hijackerreturn != hijackerlast)
results += 2
+ if (hijackerreturn != hijackerlast)
+ hijackerlast = hijackerreturn
return results
// Used in process so it doesn't update the icon too much
@@ -544,7 +546,7 @@
/obj/machinery/power/apc/attackby(obj/item/W, mob/living/user, params)
- if(issilicon(user) && get_dist(src,user)>1)
+ if(area.hasSiliconAccessInArea(user) && get_dist(src,user)>1)
return attack_hand(user)
if (istype(W, /obj/item/stock_parts/cell) && opened)
@@ -751,7 +753,7 @@
/obj/machinery/power/apc/AltClick(mob/user)
. = ..()
- if(!user.canUseTopic(src, !issilicon(user)) || !isturf(loc))
+ if(!user.canUseTopic(src, !area.hasSiliconAccessInArea(user)) || !isturf(loc))
return
togglelock(user)
return TRUE
@@ -766,7 +768,7 @@
else if(stat & (BROKEN|MAINT))
to_chat(user, "Nothing happens!")
else
- if(allowed(usr) && !wires.is_cut(WIRE_IDSCAN) && !malfhack)
+ if((allowed(usr) || area.hasSiliconAccessInArea(usr)) && !wires.is_cut(WIRE_IDSCAN) && !malfhack)
locked = !locked
to_chat(user, "You [ locked ? "lock" : "unlock"] the APC interface.")
update_icon()
@@ -843,31 +845,43 @@
if((stat & MAINT) && !opened) //no board; no interface
return
+/obj/machinery/power/apc/oui_canview(mob/user)
+ if(area.hasSiliconAccessInArea(user)) //some APCs are mapped outside their assigned area, so this is required.
+ return TRUE
+ return ..()
+
/obj/machinery/power/apc/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)
if(!ui)
- ui = new(user, src, ui_key, "apc", name, 535, 515, master_ui, state)
+ ui = new(user, src, ui_key, "apc", name, 450, 460, master_ui, state)
ui.open()
/obj/machinery/power/apc/ui_data(mob/user)
+ var/obj/item/implant/hijack/H = user.getImplant(/obj/item/implant/hijack)
+ var/abilitiesavail = FALSE
+ if (H && !H.stealthmode && H.toggled)
+ abilitiesavail = TRUE
var/list/data = list(
- "locked" = locked && !(integration_cog && is_servant_of_ratvar(user)),
+ "locked" = locked && !(integration_cog && is_servant_of_ratvar(user)) && !area.hasSiliconAccessInArea(user, PRIVILEDGES_SILICON|PRIVILEDGES_DRONE),
"lock_nightshift" = nightshift_requires_auth,
"failTime" = failure_timer,
"isOperating" = operating,
"externalPower" = main_status,
- "powerCellStatus" = cell ? cell.percent() : null,
+ "powerCellStatus" = (cell?.percent() || null),
"chargeMode" = chargemode,
"chargingStatus" = charging,
"totalLoad" = DisplayPower(lastused_total),
"coverLocked" = coverlocked,
- "siliconUser" = user.has_unlimited_silicon_privilege || user.using_power_flow_console(),
+ "siliconUser" = user.using_power_flow_console() || area.hasSiliconAccessInArea(user),
"malfStatus" = get_malf_status(user),
"emergencyLights" = !emergency_lights,
"nightshiftLights" = nightshift_lights,
-
+ "hijackable" = HAS_TRAIT(user,TRAIT_HIJACKER),
+ "hijacker" = hijacker == user ? TRUE : FALSE,
+ "drainavail" = cell && cell.percent() >= 85 && abilitiesavail,
+ "lockdownavail" = cell && cell.percent() >= 35 && abilitiesavail,
"powerChannels" = list(
list(
"title" = "Equipment",
@@ -935,17 +949,12 @@
/obj/machinery/power/apc/proc/can_use(mob/user, loud = 0) //used by attack_hand() and Topic()
if(IsAdminGhost(user))
return TRUE
- if(user.has_unlimited_silicon_privilege)
+ if (user == hijacker || (area.hasSiliconAccessInArea(user) && !aidisabled))
+ return TRUE
+ if(user.silicon_privileges & PRIVILEDGES_SILICON)
var/mob/living/silicon/ai/AI = user
var/mob/living/silicon/robot/robot = user
- if ( \
- src.aidisabled || \
- malfhack && istype(malfai) && \
- ( \
- (istype(AI) && (malfai!=AI && malfai != AI.parent)) || \
- (istype(robot) && (robot in malfai.connected_robots)) \
- ) \
- )
+ if (src.aidisabled || malfhack && istype(malfai) && ((istype(AI) && (malfai!=AI && malfai != AI.parent)) || (istype(robot) && (robot in malfai.connected_robots))))
if(!loud)
to_chat(user, "\The [src] has eee disabled!")
return FALSE
@@ -955,11 +964,15 @@
. = ..()
if (!. && !QDELETED(remote_control))
. = remote_control.can_interact(user)
+ if (hijacker == user && area.hasSiliconAccessInArea(user))
+ return TRUE
/obj/machinery/power/apc/ui_status(mob/user)
. = ..()
if (!QDELETED(remote_control) && user == remote_control.operator)
. = UI_INTERACTIVE
+ if (user == hijacker && area.hasSiliconAccessInArea(user))
+ . = UI_INTERACTIVE
/obj/machinery/power/apc/ui_act(action, params)
if(..() || !can_use(usr, 1))
@@ -969,7 +982,10 @@
failure_timer = 0
update_icon()
update()
- var/authorized = (!locked || usr.has_unlimited_silicon_privilege || (integration_cog && (is_servant_of_ratvar(usr))))
+ if (action == "hijack" && can_use(usr, 1)) //don't need auth for hijack button
+ hijack(usr)
+ return
+ var/authorized = (!locked || area.hasSiliconAccessInArea(usr, PRIVILEDGES_SILICON|PRIVILEDGES_DRONE) || (integration_cog && (is_servant_of_ratvar(usr))))
if((action == "toggle_nightshift") && (!nightshift_requires_auth || authorized))
toggle_nightshift_lights()
return TRUE
@@ -977,7 +993,7 @@
return
switch(action)
if("lock")
- if(usr.has_unlimited_silicon_privilege)
+ if(area.hasSiliconAccessInArea(usr))
if((obj_flags & EMAGGED) || (stat & (BROKEN|MAINT)))
to_chat(usr, "The APC does not respond to the command.")
else
@@ -1011,7 +1027,7 @@
update()
return TRUE
if("overload")
- if(usr.has_unlimited_silicon_privilege)
+ if(area.hasSiliconAccessInArea(usr))
overload_lighting()
return TRUE
if("hack")
@@ -1033,7 +1049,26 @@
L.no_emergency = emergency_lights
INVOKE_ASYNC(L, /obj/machinery/light/.proc/update, FALSE)
CHECK_TICK
- return TRUE
+ if("drain")
+ cell.use(cell.charge)
+ hijacker.toggleSiliconAccessArea(area)
+ hijacker = null
+ set_hijacked_lighting()
+ update_icon()
+ var/obj/item/implant/hijack/H = usr.getImplant(/obj/item/implant/hijack)
+ H.stealthcooldown = world.time + 2 MINUTES
+ energy_fail(30 SECONDS * (cell.charge / cell.maxcharge))
+ if("lockdown")
+ var/celluse = rand(20,35)
+ celluse = celluse /100
+ for (var/obj/machinery/door/D in GLOB.airlocks)
+ if (get_area(D) == area)
+ INVOKE_ASYNC(D,/obj/machinery/door.proc/hostile_lockdown,usr, FALSE)
+ addtimer(CALLBACK(D,/obj/machinery/door.proc/disable_lockdown, FALSE), 30 SECONDS)
+ cell.charge -= cell.maxcharge*celluse
+ var/obj/item/implant/hijack/H = usr.getImplant(/obj/item/implant/hijack)
+ H.stealthcooldown = world.time + 3 MINUTES
+ return TRUE
/obj/machinery/power/apc/proc/toggle_breaker()
if(!is_operational() || failure_timer)
@@ -1042,6 +1077,39 @@
update()
update_icon()
+/obj/machinery/power/apc/proc/hijack(mob/living/L)
+ if (!istype(L))
+ return
+ if (hijacker && hijacker != L)
+ var/obj/item/implant/hijack/H = L.getImplant(/obj/item/implant/hijack)
+ to_chat(L, "Someone already has control of this APC. Beginning counter-hijack.")
+ H.hijacking = TRUE
+ if (do_after(L,20 SECONDS,target=src))
+ hijacker.toggleSiliconAccessArea(area)
+ if (L.toggleSiliconAccessArea(area))
+ hijacker = L
+ update_icon()
+ set_hijacked_lighting()
+ H.hijacking = FALSE
+ return
+ else
+ to_chat(L, "Aborting.")
+ H.hijacking = FALSE
+ return
+ to_chat(L, "Beginning hijack of APC.")
+ var/obj/item/implant/hijack/H = L.getImplant(/obj/item/implant/hijack)
+ H.hijacking = TRUE
+ if (do_after(L,H.stealthmode ? 12 SECONDS : 5 SECONDS,target=src))
+ if (L.toggleSiliconAccessArea(area))
+ hijacker = L
+ update_icon()
+ set_hijacked_lighting()
+ H.hijacking = FALSE
+ else
+ to_chat(L, "Aborting.")
+ H.hijacking = FALSE
+ return
+
/obj/machinery/power/apc/proc/malfhack(mob/living/silicon/ai/malf)
if(!istype(malf))
return
@@ -1432,7 +1500,7 @@
return
for(var/A in GLOB.ai_list)
var/mob/living/silicon/ai/I = A
- if(get_area(I) == area)
+ if(get_base_area(I) == area)
return
failure_timer = max(failure_timer, round(duration))
@@ -1448,6 +1516,17 @@
L.update(FALSE)
CHECK_TICK
+/obj/machinery/power/apc/proc/set_hijacked_lighting()
+ set waitfor = FALSE
+ var/hijackerreturn
+ if (hijacker)
+ var/obj/item/implant/hijack/H = hijacker.getImplant(/obj/item/implant/hijack)
+ hijackerreturn = H && !H.stealthmode
+ for(var/obj/machinery/light/L in area)
+ L.hijacked = hijackerreturn
+ L.update(FALSE)
+ CHECK_TICK
+
/obj/machinery/power/apc/proc/update_nightshift_auth_requirement()
nightshift_requires_auth = nightshift_toggle_requires_auth()
diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm
index cee9a17ebf..3c807390a0 100644
--- a/code/modules/power/cable.dm
+++ b/code/modules/power/cable.dm
@@ -85,8 +85,8 @@ By design, d1 is the smallest direction and d2 is the highest
if(isnull(_d1) || isnull(_d2))
// ensure d1 & d2 reflect the icon_state for entering and exiting cable
var/dash = findtext(icon_state, "-")
- d1 = text2num( copytext( icon_state, 1, dash ) )
- d2 = text2num( copytext( icon_state, dash+1 ) )
+ d1 = text2num(copytext(icon_state, 1, dash))
+ d2 = text2num(copytext(icon_state, dash + length(icon_state[dash])))
else
d1 = _d1
d2 = _d2
diff --git a/code/modules/power/cell.dm b/code/modules/power/cell.dm
index d89eeb7e3d..d8d473a3e2 100644
--- a/code/modules/power/cell.dm
+++ b/code/modules/power/cell.dm
@@ -13,6 +13,7 @@
w_class = WEIGHT_CLASS_SMALL
var/charge = 0 // note %age conveted to actual charge in New
var/maxcharge = 1000
+ var/start_charged = TRUE
materials = list(MAT_METAL=700, MAT_GLASS=50)
grind_results = list(/datum/reagent/lithium = 15, /datum/reagent/iron = 5, /datum/reagent/silicon = 5)
var/rigged = FALSE // true if rigged to explode
@@ -32,7 +33,8 @@
create_reagents(5, INJECTABLE | DRAINABLE)
if (override_maxcharge)
maxcharge = override_maxcharge
- charge = maxcharge
+ if(start_charged)
+ charge = maxcharge
if(ratingdesc)
desc += " This one has a rating of [DisplayEnergy(maxcharge)], and you should not swallow it."
update_icon()
@@ -163,9 +165,8 @@
return rating * maxcharge
/* Cell variants*/
-/obj/item/stock_parts/cell/empty/Initialize()
- . = ..()
- charge = 0
+/obj/item/stock_parts/cell/empty
+ start_charged = FALSE
/obj/item/stock_parts/cell/crap
name = "\improper Nanotrasen brand rechargeable AA battery"
@@ -173,10 +174,8 @@
maxcharge = 500
materials = list(MAT_GLASS=40)
-/obj/item/stock_parts/cell/crap/empty/Initialize()
- . = ..()
- charge = 0
- update_icon()
+/obj/item/stock_parts/cell/crap/empty
+ start_charged = FALSE
/obj/item/stock_parts/cell/upgraded
name = "upgraded power cell"
@@ -195,10 +194,8 @@
maxcharge = 1250 //25/12/6 disabler/laser/taser shots.
materials = list(MAT_GLASS=40)
-/obj/item/stock_parts/cell/secborg/empty/Initialize()
- . = ..()
- charge = 0
- update_icon()
+/obj/item/stock_parts/cell/secborg/empty
+ start_charged = FALSE
/obj/item/stock_parts/cell/lascarbine
name = "laser carbine power supply"
@@ -231,10 +228,8 @@
maxcharge = 15000
chargerate = 2250
-/obj/item/stock_parts/cell/high/empty/Initialize()
- . = ..()
- charge = 0
- update_icon()
+/obj/item/stock_parts/cell/high/empty
+ start_charged = FALSE
/obj/item/stock_parts/cell/super
name = "super-capacity power cell"
@@ -243,10 +238,8 @@
materials = list(MAT_GLASS=300)
chargerate = 2000
-/obj/item/stock_parts/cell/super/empty/Initialize()
- . = ..()
- charge = 0
- update_icon()
+/obj/item/stock_parts/cell/super/empty
+ start_charged = FALSE
/obj/item/stock_parts/cell/hyper
name = "hyper-capacity power cell"
@@ -255,10 +248,8 @@
materials = list(MAT_GLASS=400)
chargerate = 3000
-/obj/item/stock_parts/cell/hyper/empty/Initialize()
- . = ..()
- charge = 0
- update_icon()
+/obj/item/stock_parts/cell/hyper/empty
+ start_charged = FALSE
/obj/item/stock_parts/cell/bluespace
name = "bluespace power cell"
@@ -268,10 +259,8 @@
materials = list(MAT_GLASS=600)
chargerate = 4000
-/obj/item/stock_parts/cell/bluespace/empty/Initialize()
- . = ..()
- charge = 0
- update_icon()
+/obj/item/stock_parts/cell/bluespace/empty
+ start_charged = FALSE
/obj/item/stock_parts/cell/infinite
name = "infinite-capacity power cell!"
@@ -321,15 +310,13 @@
maxcharge = 500
rating = 3
-/obj/item/stock_parts/cell/emproof/empty/Initialize()
- . = ..()
- charge = 0
- update_icon()
-
-/obj/item/stock_parts/cell/emproof/empty/ComponentInitialize()
+/obj/item/stock_parts/cell/emproof/ComponentInitialize()
. = ..()
AddComponent(/datum/component/empprotection, EMP_PROTECT_SELF)
+/obj/item/stock_parts/cell/emproof/empty
+ start_charged = FALSE
+
/obj/item/stock_parts/cell/emproof/corrupt()
return
@@ -364,3 +351,16 @@
//found inside the inducers ordered from cargo.
/obj/item/stock_parts/cell/inducer_supply
maxcharge = 5000
+
+/obj/item/stock_parts/cell/magnetic
+ name = "magrifle power supply"
+ maxcharge = 12000
+ chargerate = 600
+
+/obj/item/stock_parts/cell/magnetic/pistol
+ name = "magpistol power supply"
+ maxcharge = 6000
+
+/obj/item/stock_parts/cell/toymagburst
+ name = "toy mag burst rifle power supply"
+ maxcharge = 4000
diff --git a/code/modules/power/gravitygenerator.dm b/code/modules/power/gravitygenerator.dm
index 3f7fdc45d9..d0d1e2b475 100644
--- a/code/modules/power/gravitygenerator.dm
+++ b/code/modules/power/gravitygenerator.dm
@@ -79,7 +79,7 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne
return main_part.attackby(I, user)
/obj/machinery/gravity_generator/part/get_status()
- return main_part.get_status()
+ return main_part?.get_status()
/obj/machinery/gravity_generator/part/attack_hand(mob/user)
return main_part.attack_hand(user)
@@ -220,40 +220,32 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne
return
return ..()
-/obj/machinery/gravity_generator/main/ui_interact(mob/user)
- if(stat & BROKEN)
- return
- var/dat = "Gravity Generator Breaker: "
- if(breaker)
- dat += "ONOFF"
- else
- dat += "ONOFF "
+/obj/machinery/gravity_generator/main/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)
+ if(!ui)
+ ui = new(user, src, ui_key, "gravity_generator", name, 400, 200, master_ui, state)
+ ui.open()
- dat += " Generator Status:
the user of a sepia slime extract is no longer immune to the time field
+
chilling sepia is now the support extract - it always freezes the user, but not other marked people. give one to your golem!
+
burning sepia spawns the rewind camera. Take a selfie with someone and give it to them to make sure they remember the moment forever! You don't actually need to give them the photo, they'll remember anyway. Keep it as a reminder.
+
rewinding (deja vu) effect resets your location to the turf you were on after 10 seconds as well as resetting limbs/mobs/objects damage to the point it was added.
+
The deja vu effect cannot resurrect the dead, but will heal their corpse. New limbs fall off, old ones re-attach
+
Regenerative sepia cores add a deja vu effect before healing instead of a timestop remove: timefreeze camera is admin spawn only.
+
recurring sepia recalls to the hand of the last person to touch it after activating. Reusable timestop grenades!
+
some special cameras don't prompt for customisation
+
timefreeze camera actually makes a photo
+
timestop stops pathing and mechs.
+
adds a check to make sure simple_animals don't get their AI toggled while sentient
+
Adds the timestop overlay to frozen projectiles
+
Timestopped things have INFINITY move_resist as opposed to being anchored
+
Timestop will now unfreeze things that somehow leave it
+
mobs in the middle of a walk_to will have their walk stopped by timestop
+
mobs that are stunned will be stopped mid walk as well
+
pulling respects changes in move_force
+
swapping places respects move_force if the participant is not willing
+
timestop is properly defeated by antimagic.
+
timestop only checks antimagic once
+
Time stop now applies its visual effect on floors, walls and static structures (with no change otherwise)
+
Movable structures are now anchored while time stopped.
+
Timestop effects now prevent speech.
+
+
AffectedArc07 updated:
+
+
All code files are now in the LF line format, please stick to it
+
Added CI step to check for CRLF files
+
Line ending CI works now
+
+
Arkatos, Zxaber, Ghommie updated:
+
+
Certain AI abilities now dynamically show their remaining uses on the mouse hover over their respective action buttons.
+
Malfunctioning AIs can no longer abuse the confirmation popup to create extra (unstoppable) doomsdays.
+
Fixed AIs being able to use some of their abilities such as the doomsday whilst dead, and the doomsday loading phase not halting upon AI death.
+
+
Arturlang updated:
+
+
Vampires are no longer as tanky
+
Added modular computers to the loadout
+
+
Bhijn updated:
+
+
Limbs now regenerate their stamina faster while disabled
+
Limbs now have the same incoming stamina damage multiplier mechanics as spacemen, where the more staminaloss they take while disabled, the less staminaloss they'll take.
+
Limbs have had their base stamina regen rate doubled to match the doubled stamina regen rate of standard spacemen.
+
Added a preference to make the sprint hotkey be a toggle instead of a hold bind
+
Added a preference to bind the sprint hotkey to space instead of shift.
+
server_hop can no longer be used to remotely lobotomize a spaceman
+
+
Bhijn helped updated:
+
+
Fixes Dragon's Tooth Sword 50% armor penetration by making it 35%
+
+
BonniePandora updated:
+
+
Added another SDQL option. Using "UPDATE selectors SET #null=value" will now resolve value and discard the return. This is useful if you only care about the side effect of a proc.
+
+
CameronWoof updated:
+
+
Ghost Cafe patrons now spawn with chameleon kits. Dress up! Be fancy!
+
Robots can now check the crew manifest from anywhere with the "View Crew Manifest" verb.
+
Lizard tails are now viable options for humans and anthromorphs.
+
+
Commandersand updated:
+
+
fixed some clothnig sprites
+
lightning bolt,tesla,forcewall,emp spells have lower cooldown
+
fireball has a 10 second starting cooldown
+
+
DeltaFire15 updated:
+
+
Vitality matrixes now correctly succ slimes
+
+
Denton, ported by Hatterhat updated:
+
+
Most upgradeable machines now show their upgrade status when examined while standing right next to them.
+
Added examine messages to teleporter stations that hint at their multitool/wirecutter interactions.
+
Renamed teleporter stations from station to teleporter station.
+
Changed the teleporter hub accurate var to accuracy; the old name misled people into thinking that it was a boolean.
+
+
Detective-Google updated:
+
+
replaced the sprites with new ones requested.
+
+
EmeraldSundisk updated:
+
+
Adds a sign outside MetaStation's custodial closet
+
+
FantasticFwoosh, ported by Hatterhat updated:
+
+
Both reagent universal enzyme & sacks of flour are now available for their respective costs from the biogenerator.
+
New crafting recipies for donut boxes, eggboxes & candle boxes have been added to cardboard recipes for the collective benefit of service personnel and the station.
+
+
Ghommie updated:
+
+
Attached kevlar/soft/plastic padding accessories are now stealthier and will no longer be displayed on mob examine.
+
Refactored that mess of a code for alternate worn clothing sprites for digitigrade and taurs.
+
Fixed some issues with the aforementioned feature you may or may not have experienced because it was pretty lame.
+
Fixed missing digi versions fishnet sprites and wrong digitigrade left dir purple stockings sprite.
+
Add digitigrade versions for boxers and the long johns.
+
Fixed the secret sauce recipe being randomized every round.
+
Fixed singularity pulls duping rods out of engine floors.
+
Removed an old pair of zipties from the captain closet.
+
Chestbursters won't delete the host's brain somewhat anymore.
+
Fixed roundstart mushpeople being unable to MUSH PUUUUUUUUUUUUNCH!!
+
Lattices can be examined yet again.
+
Made reagent containers examine text less annoying.
+
Virus food dispensers dispense virus food again.
+
Borg hypos inputs do not display typepaths anymore.
+
Restored chem dispensers alphabetical order.
+
Sanitized cargo export messages for reagents.
+
Riot foam dart (not the ammo box) design cost is yet again consistent with the result's material amount.
+
Fixed a little exploit with ventcrawlers being capable of escaping closed turfs by printing scrubbers/vents into them.
+
deconstructing a rubber toolbox with the destructive analyzer won't "lock" the machine anymore.
+
Something about empty hand combat mode right click waving hands defying common sense and being spammy.
+
Towels are now "digitigrade-friendly".
+
The Barefoot drink and mousetraps will now work even if wearing certain "feet-less shoes".
+
Refactored code to allow all living mobs to use shields and not only humans.
+
Monkys will now retaliate against aliens attacking them (as if they even posed a threat to start with).
+
add a click cooldown to the overly spammable table slamming.
All atom movables now have move force and move resist, and pull force An atom can only pull another atom if its pull force is stronger than that atom's move resist
+
Mobs with a higher move force than an atom's move resist will automatically try to force the atom out of its way. This might not always work, depending on how snowflakey code is. experimental: As of right now, everything has a move force and resist of 100, and a pull force of 101. Things take (resist - force) damage when bumped into experimental: Failing to move onto a tile will now still bump up your last move timer. However, successfully pushing something out of your way will result in you automatically moving into where it was.
+
Bolted AIs can no longer be teleported by launchpads.
+
Megafauna cannot teleport
+
+
Hatterhat updated:
+
+
Beakers are generally more useful now, with slight capacity increases.
+
Transfer amounts are different now. Adjust your muscle memory to compensate.
+
ore redemption machines actually get affected by lasers again kthx
+
crusher trophy drop chance on mining mobs increased to 1 in 4 (from 1 in 20)
+
Blood-drunk buff from blood-drunk eye crusher trophy is less likely to cripple its user.
+
Forcefield projectors now fit on toolbelts.
+
New sprites for ATMOS holofan and forcefield projectors!
+
tweaks some values around with beaker transfer amounts, adds a transfer value verb (altclick)
+
Syndicate sleepers (read: the ones that're red and have controls in them) can now be recreated with a sufficient basis in nonstandard (read: illegal) technologies!
+
Explorer hoods on standard explorer suits can be reinforced with goliath hide plates again.
+
A shipment of Staff Assistant jumpsuits from the Goon-operated stations appear to have made their way into your loadout selections - and into the contraband list from ClothesMates...
+
Dropped Exo-suit armor to put it more in line with being an explorer suit sidegrade and not a straight upgrade for Lavaland usage.
+
You can now print .357 AP speedloaders from Security techfabs after you pick up the Advanced Non-Standard Ballistics node.
+
Forensic scanner removed from Advanced Biotechnology node.
+
Ash Drake armor now has goliath resistance, same as the Exo-suit.
+
+
KathrinBailey updated:
+
+
Empty engineering lockers for mappers.
+
Industrial welding tools to the engineer welding locker.
+
Removed the multitool, airlock painter, mechanical toolbox, brown sneakers, hazard vest and airlock painter from the CE's locker.
+
+
KeRSedChaplain updated:
+
+
The clockwork cuirass can now support slithering taur bodies.
+
+
Kevinz000 updated:
+
+
Fixes successful projectile hits also striking another atom on the same turf should the first one be not the target the projectile was meant for.
+
Removes infinite reflector loops.
+
+
Kraseo updated:
+
+
Jelly donuts and pink jelly donuts will now properly display sprinkled icon state.
+
Jelly donuts and its variants will properly spawn with berry juice.
+
Slime jelly donuts have slime jelly in them now. (thanks ghommie)
+
Virgo hairstyles no longer have those annoying quotation marks. Rejoice for you will no longer have to use the scrollbar.
+
Got rid of some strange-looking hairstyles. (Tbob, fingerweave, etc.)
+
+
LetterN updated:
+
+
Doppler logs
+
Shockcolar and electropack uses the new signaler ui
+
Renaming shockcolar requires a pen
+
Adds banjo
+
+
Linzolle updated:
+
+
fixes matchboxes runtiming every time they spawn
+
+
MrJWhit updated:
+
+
Increases plasma usage in radiation collectors
+
+
Naksu updated:
+
+
reagent IDs have been removed in favor using reagent typepaths where applicable
+
mechas, borg hyposprays etc no longer display internal reagent ids to the player
+
+
Owai-Seek updated:
+
+
Four New Bounties
+
tweaked several bounties
+
reorganised several bounties
+
balanced several bounties
+
added shady jims to vendor
+
Towel, Bedsheet, and Colored bedsheet bins to Dojo. Some lockers with carpets and wood, another bathroom/shower, tons of cleaning supplies, some additional trash cans, a tiny medical area, and some vendors/vendor resupplies. Also added some Ambrosia Gaia seeds.
+
Moved some lights around, extended the dojo a bit to make it feel more spacious.
+
Cafe Newsfeed Frame
+
+
PersianXerxes updated:
+
+
Reduces the range of the forcefield projector from 7 tiles to 2 tiles.
+
+
Putnam updated:
+
+
Alcohol intolerance trait, which forces vomiting on any alcohol ingestion
+
Arousal damage is gone, and with it the arousal damage heart on the UI.
+
As exhibitionist relied on arousal damage, that's gone too.
+
Bonermeter and electronic stimulation circuit modules are gone.
+
Masturbation is no longer an option in the climax menu. It was identical to climax alone in every way but text. Emote it out.
+
Arousal on genitals can now be displayed manually, on a case-by-case basis.
+
"Climax alone" and "climax with partner" now only display to the people directly involved in the interaction. rework: Catnip tea, catnip, crocin, hexacrocin, camphor, hexacamphor all had functions or bits of functions reworked.
+
"Climax with partner" now requires consent from both parties--it can no longer be used on mindless mobs, and it asks the mob getting climaxed with if they consent first.
+
A lot of MKUltra behaviors have been moved to hypno checks or removed due to reliance on maso/nympho/exhib.
+
Cold-blooded quirk
+
Two relief valves for atmospherics, available in your RPD today: a unary one that opens to air and a binary one that connects two pipenets, like a manual valve.
+
The atmos waste outlet injector on box has been replaced with an atmos waste relief valve.
+
+
Putnam3145 updated:
+
+
Dynamic storytellers, a new voting paradigm for dynamic
+
Support for approval voting and condorcet (ranked choice) voting in server votes
+
Ghost cafe mobs can now ghostize
+
Ghost cafe now has a cremator
+
Ghost cafe mobs are now eligible for ghost roles
+
Ghost roles now use an element for eligibility purposes
+
You can toggle some prefs properly now.
+
no ass slap is no longer the same thing as no aphro
+
Devastating bombs kills bomb armor'd mobs again
+
Added a sort of "game mode ban" by way of having people rank their game modes favorite to least favorite after the secret/extended vote.
+
Turns out the schulze scoring was written wrong and it was setting things to 0 that shouldn't have been, so that's fixed.
+
Random engines are now weighted.
+
Ghost dojo now has an autoylathe.
+
Ghost dojo no longer has VR or modular computer access.
+
Ghost dojo mobs are pacifists.
+
Ghost dojo booze-o-mat is now all access.
+
Ghost dojo crematorium now has a button.
+
Traitor objectives don't take a dump when they try to add an assassinate anymore
+
Objective removal with antag panel no longer commented out silently while still being an option that gives useful feedback on stuff it's not doing in any respect
+
Second, temporary flavor text!
+
Limb damage works now
+
Score instead of ranked choice for mode tiers
+
Chemistry not :b:roke
+
Ghost dojo spawns will dust if their owner suicides or uses the "ghost" verb.
+
Crafting is logged in a file now
+
temporary flavor text now pops up properly
+
demodularized player panel code, mostly
+
added ghost role eligibility delay removal to player panel
+
Metabolic synthesis disables if user isn't well-fed rather than if user is starving
+
Gibtonite no longer instantly explodes upon being pickaxe'd.
+
Cold-blooded costs -2 quirk points now
+
Added suicide to the config.
+
Ghost cafe mobs are super duper attached to the ghost cafe.
+
Meow Mix bar sign works
+
Fixed a few relief valve behaviors
+
+
Ryll/Shaps, ported by Hatterhat updated:
+
+
Point-blanking people with shotguns actually throws them backwards!
+
+
Savotta updated:
+
+
snout
+
+
Seris02 updated:
+
+
made it so trait blacklisting removes random positives instead of removing everything
+
added atmos holofirelocks and temperature blocking
+
tweaked how atmos holofan looks
+
stunglasses
+
disabler sechuds
+
adds coconut
+
adds a coconut bong
+
Auto ooc
+
changed "assume direct control" from m.ckey = src.ckey to adminmob.transfer_ckey(M) so it works with auto ooc
+
marshmallow
+
telescopic IV drip
+
cardboard box speed
+
makes gorilla shuttle emag only
+
changed where the observe verb is.
+
the way chameleon clothes update on change
+
chameleon cloak icon
+
Explosions now cause knockdown instead of KOs, with the amount of time scaling off bomb armor. Heavy explosions still cause a ~2 second KO for follow up attacks.
+
Devastating explosions now no longer gib players with more than 50 bomb armor. This used to be more or less random depending on the amount of bomb armor.
+
Removed reagent explosion code that would trigger for <1 amount reactions.
+
traitor tool for bowmanizing headsets + bowmanizing headsets
+
durathread winter coats from hyper station
+
conveyor belt now are stacks instead of single item
+
conveyor crate has 30 belts
+
printing a conveyor costs 3000 metal
+
you can press the conveyor switch in hand to link any not deployed belts to it
+
sniffing sneezing etc doing anything if they don't breathe
+
probably fixes the make mentor button
+
the sprites for the conveyor belts to look more directional
+
+
Skoglol updated:
+
+
Falsewalls now properly hide pipes and wires.
+
+
SpaceManiac, bobbahbrown, ShizCalev, SpaceManiac (ported by Ghommie) updated:
+
+
It is now possible to set a different most-base-turf per z-level.
+
Removed unlawful reference to Disney's Star Wars franchise in map logging.
+
Moved mapping related errors to their own log file.
+
Destruction on Lavaland will no longer reveal space in rare situations.
+
+
Tlaltecuhtli, Nemvar, Trilby, Hatterhat updated:
+
+
russian surplus crate, sec ammo crate, meat/veggie/fruit crate, rped crate, bomb suit crate, bio suit crate, parrot crate, chem crate, db crate
+
lowered price of some crates which are overpriced for no reason aka the tablet crate and the mech circuits crates, track implant crate has also track .38 ammo
+
sectech vendor has a refill pack
+
+
Trilbyspaceclone updated:
+
+
Fermi plushie can be made once again
+
range on Engi Tray scanners and Rad-Scanners
+
issues with mapping done my Trilby
+
Grass now makes light beer when distilled
+
allows bandoliers to hold any ammo type as long as it has a casing
+
Cleans out the last years of changlogs
+
rouge cases of #$39; in bottle/pill/patch/condiments
+
Adds missing but needed flags to MASON RIG
+
missing sprites with crushed cans
+
Halfs the nutriments in sugar
+
Glitter is now makeable with ground plastic and some crayons
+
Makes more folders and files for uplink.
+
Seed packs now have RnD points inside them
+
New tips that reflect the now use seed packs have for Rnd / Chems affect on plants
+
Made the research file look nice rather then an eye harming list
+
Fixed fragile space suits breaking from weak and non-damaging "weapons" (such as the Sord, toys and foam darts).
+
tip of the round: Cleanbot can withstand lava and burning hot ash. Its a god
+
more glassware
+
Few new packs and glassmaker kit!
+
prices/chems of crates/autobottler
+
some crates ungettable as well as some broken crates
+
+
Trilbyspaceclone, Ghommie updated:
+
+
More types of glass can now be used to make a solar panel, with different sturdiness and efficiency depending the type.
+
+
Tupinambis updated:
+
+
Replaces new fire alarms with a slightly updated version of the old ones.
+
Fixed bug where Amber alert had no proper alert lights, and red alert had amber lights.
+
RLDs now cost more to use, have reduced matter capacity, and sheets are worth less when refilling them.
+
+
Unit2E & JMoldy, ported by Hatterhat updated:
+
+
the powerfist now punches people away at high velocity depending on setting.
+
the powerfist now leaks the gas it uses onto the tile you're on.
+
Adds weak punch variations for empty and near-empty punching.
+
+
Xantholne updated:
+
+
Christmas clothes that where missing stuff should work again
+
Christmas clothes moved from clothesmate and loadout to premium autodrobe, hoodies and boots remain.
+
Botany bee pet, Bumbles
+
New bee models that aren't 1 tile big of 0 opacity pixels
+
Cheongsam and Qipao clothing added to loadout and clothesmates
+
+
dapnee updated:
+
+
new toxin's uniform and accessories
+
old toxin's uniform and accessories
+
+
deathride58 updated:
+
+
The stamina buffer no longer uses stamina while recharging.
+
+
jakeramsay007 updated:
+
+
The assorted .357 revolvers (except russian revolver) can now also load .38, like real life.
+
+
kappa-sama updated:
+
+
loot crates in cargo contraband
+
modular_citadel file movement
+
modular citadel loses some files
+
dragnet snares can now be removed in 5 seconds
+
+
keronshb updated:
+
+
Adds new features for nanites
+
fixed the missing icons from Dermal Button nanites
+
Ports the special nanite remote, mood programs, and research generating nanites
+
rebalances some nanites
+
fixed my messup
+
+
kevinz000 updated:
+
+
throwing things no longer makes them randomly turned as long as you aren't on harm intent
+
Custom holoforms have been added for pAIs and AIs. oh and cyborg holograms of specific modules too.
+
pais are no longer indestructible-flagged while in card form. pai radios now short out if they are forcefully collapsed from damage.
+
telescopic iv drips now have the proper sanity checks for deployment.
+
megafauna can hear again
+
Cargo passive gen is now 500 down from 750.
+
Added a few more nightshift config entries
+
nightshift_public_area variable in areas to determine how public an area is.
+
NIGHT_SHIFT_PUBLIC_AREAS_ONLY config entry allows the server to be configured to only nightshift areas of that level and below (so areas that are more public)
+
Config entries added for requiring authorizations to toggle nightshift. Defaults to only requiring on public areas as determined by above
+
nuclear fist buffed.
+
cogscarabs are now fulltile-hitbox.
+
emitters are now hitscan
+
monkies gorrilize easier now
+
Ghosting no longer stops abductors from using you.
+
guests can now looc
+
tableslamming has 0.4 second cooldown instead of 0.8
+
health analyzers now work
+
Mapping helpers added for power cables and atmos pipes.
+
Blood duplication is gone, but viruses now react up to 5 times, 1 time per unit, for virus mix.
+
you can now block people on your PDA
+
Grenades can now have their timers adjusted.
+
Cloning has been nerfed.
+
hilbert hotel is now less of an exploity mess
+
Doorcrushes are once again instant.
+
+
nik707 updated:
+
+
engraving light_power from 1 to 0.3
+
+
r4d6 updated:
+
+
Added a Radiation Hardsuit
+
Added a Radiation Hardsuit crate
+
Added 3 SM Engine variations.
+
Added a way to make opaque plastic flaps change: Change the already existing flap recipe to show that they are see-through
+
Animations for the RCD
+
Said animations being pulled by the Singulo
+
Prevent Reinforced Doors from being RCD'ed
+
Deconstructing Floors now cost MUs like everything else
+
changed the RCD's upgrades into flags
+
Allow RCDs to print APC, Firelocks and Fire Alarms with the right upgrade
+
Allow Engineers to print RCDs and RPDs from their protolathe without needing to hack one.
+
Added a playback device
+
Made the Voice Analyzer actually care about languages
+
fixed SM's piping
+
Added a message when pulsing the open wire.
+
fixed being able to use a playback device and a voice analyzer to activate each others
+
added passive vent to the arsenal of atmospheric devices.
+
Mining base now has a common area accessible via a new shuttle on the medium-highpop maps, and a security office connecting the gulag and the mining base. Gulag also has functional atmos.
+
Added more Cyborg Landmarks
+
better code for passive vents
+
fixed a hole in Meta
+
Added the ability to easily add variations of the mining base
+
fixed a sprite
+
+
timothyteakettle updated:
+
+
fixed not being able to remove trait genes without a disk being inserted into the dna manipulator
+
+
30 December 2019
AnturK updated:
@@ -371,503 +828,6 @@
Adds Insect markings
Adds three new moth wings.
-
-
07 December 2019
-
AffectedArc07 updated:
-
-
Fixes a LOT of code edge cases
-
-
Anonymous updated:
-
-
Added NEET-- I mean, DAB Suit and Helmet into loadout. Exclusive to Assistants, for obvious reasons, and don't provide any armor. Goes well with balaclava, finger-less gloves and jackboots for that true tactic~~f~~ool experience.
-
Renamed loadout name appropriately (ASSU -> DAB)
-
-
Arturlang updated:
-
-
PDA catridges cant be irradiated anymore.
-
-
Bhijn updated:
-
-
Item mousedrop() now provides a return value indicating whether or not behavior has been overridden somehow.
-
Defibs now properly check that their loc is the same as the user for mousedrop() calls, meaning ghosts can no longer make you equip defibs. Plus extra sanity checks.
-
Pet carriers no longer attack turfs while trying to unload their contents.
-
Decks of cards now function as they originally intended when attempting to use their drag and drop behavior.
-
Paper bins and papercutters no longer act wonky when you're trying to pull a piece of paper from them.
-
Adds clothing drag n drop sanity checks.
-
Sythetic hats now have extra sanity checks
-
-
Coconutwarrior97 updated:
-
-
Can only wrench down two transit tubes per turf.
-
-
Commandersand updated:
-
-
Added more stuff to loadout,check uniforms mask and backpack
-
-
DeltaFire15 updated:
-
-
Adds eight new plushies
-
Adds icons for the new plushies and adds a new icon for the skylar plush
-
Deleted a old, no-longer used icon for the skylar plush
Organ fridges to all maps near surgery with a random sensible organ, steralizine and synthtissue.
-
the med hand scanner to be less of a mishmash of random things
-
a little icon to the HUD if someone's heart has failed.
-
Lets neurine's brain splash attack work via syringe.
-
a new surgery; Emergency Cardioversion Induction for use on the recently deceased
-
Synthtissue to be less demanding on growth size for organ regeneration and improves clarify of it's growth gated effects.
-
Synthtissue now is more useful than synthflesh on the dead
-
-
Fox McCloud updated:
-
-
Fixes a very longstanding LINDA bug where turfs adjacent to a hotspot would be less prone to igniting
-
-
Fox McCloud, Ghommie updated:
-
-
Fixes being able to mech-punch other mobs, as a pacifist
-
Fixes being able to hurt people, as a pacifist, by throwing them into a wall or other mob, or by using most martial arts (save for the unpredictable psychotic brawl, and the stamina-damage-only boxing).
-
Buffs boxing to outdamage natural stamina regeneration. Made the chance of outright missing your opponent actually possible.
-
Pacifists can now engage in the (laughably not harmful) sweet sweet art of boxing now.
-
-
Ghommie updated:
-
-
Fixing implant cases being lost inside implant pads when trying to eject them with your active hand full.
-
Moved the implant pad's case ejection from attack_hand() to AltClick(), added examination infos about it.
-
Fixed holodeck sleepers leaving sleeper buffers behind when deleted.
-
Fixed traitor codewords highlight and some other hear signal hooks spans highlight (phobias, split personality, hypnosis) or modifiers (mind echo)
-
Fixed traitor codewords highlight not passing down with the mind datum and stickying to the first mob.
-
Fixed the incongruent bone satchel description.
-
Fixed sofa overlays doing nothing, because their layer wasn't properly set.
-
Suicide and cryo now prevents ghost/midround roles for a definite duration of 30 minutes (and more if that was done before 30 minutes in the game passed), down from the rest of the round.
-
fixed several midround roles bypassing and nulling the aforementioned prevention measures.
-
Fixed the little issue of PDA skins not updating on job equip.
-
Anomaly Crystals of the clowning type will now rename the victim to their clown name preference when triggered, instead of giving them a random clown name.
-
Lowered blob event earliest start from 1 hour to 40 minutes (ergo one third), and the required player population from 40 to 35.
-
Several fixes and QoL for dullahans. They can see and hear visible and audible messages now, don't need a space helmet they can't wear anyway to be space/temperature proof, can examine things through shiftclick, and, most of all, fixed their head being unpickable. Fixed dullahans gibbing when revived or had their limbs regenerated.
-
humans should now drop gibs when gibbed again.
-
synths (not to be confused with IPCs), android and corporate species, as well as robotic simple mobs, will now spawn robotic giblets instead of organic ones.
-
You can't wear dakimakuras in any other inappropriate slots save for the back slot anymore, degenerates.
-
Insert snarky remark about clock welders actually displaying the welder flame overlay when turned on now here.
-
Minor ninja tweaks and stealth nerfs. The stealth penalty for the many combat-related actions, bumping and now teleporting/dashing or firing guns has been increased a by a third. There is now a cooldown of 5 seconds on toggling stealth as well as a slighty slowed stealth in/out animation.
-
Ported slighty better matchbox sprites from CEV-Eris, also resprited cigar boxes myself.
-
Fixed abductors/abductees objectives by porting an objective code.
-
Riding component fix
-
fixing a few runtimes on lightgeists, libido trait, rcd, one admin transformation topic, chem dispensers, glowing robotic eyes...
-
Porting CEV-eris delivery packages sprites and dunking the old syndie cybernetics box sprite.
-
Certain objects shouldn't be able to become radioactive because of a bitflag that previously was checked nowhere in the code anymore.
-
Added a new PDA reskin, sprites from CEV-Eris
-
Clock cult starts with some spare vitality matrix charge scaled of the number of starter servants.
-
Made the vitality matrix sigil slighty more visible, also allowed conversion runes to heal fresh converts at the cost of some vitality charge.
-
Crawling won't save you from the wrath of ocular wardens and pressure sensors anymore, heretics. fix: Pressure sensors are no more triggered by floating/flying mobs.
-
Strawberry milk and tea have sprites now.
-
Fixed the Aux base camera door settings and toggle window type actions. Also enabling the user to modify both door access and type.
-
Improved the two grayscale towel item sprites a little.
-
Fixed towels onmob suit overlays. Again.
-
Fixed some reagents taste descriptions.
-
Fixed hidden random event reports only priting a paper message without sending the message to the consoles' message list.
-
Rosary beads prayer now works on non-carbon mobs too, and won't break when performed on a monkey or other humanoids.
-
You can flagellate people with rosary beads on harm intent. It's even mediocrer than the sord though.
-
Moved the `Stealth and Camouflage Items` uplink category next to `Devices and Tools`.
-
Deleted a duplicate phatom thief mask entry from the uplink.
-
Fixed missing delivery packages sprites
-
fixed a few minor issues with console frames building.
-
Wizards can use the teleport spell from their den once again.
-
Wizards will now receive feedback messages when attempting to cast teleport or use the warp whistle while in a no-teleport area.
-
New clockwork cultist, gondola, monkey and securitron cardboard cutouts.
-
Fixed aliens gasping randomly once in a while.
-
fixed superlube waterflower, my bad.
-
Fixed closing the aux base construction RCD's door access settings window throwing you out of camera mode when closed.
-
Removed not functional aux base RCD's door type menu. Use airlock painters, maybe.
-
Honkbot oil spills are of the slippery kind now. Honk.
-
local code scavenger finds forgotten slighty improved apc sprites left buried in old dusty folders.
-
Seven old and otherwordly pAI holochassis icons have crawled their way out of the modular citadel catacombs.
-
chem dispenser beakers end up in your hand yet again.
-
Bikehorns squeak yet again, the world is safe.
-
Cyborgs can now actually use cameras from a distance.
-
Suicides are yet again painful and instant and won't throw people in deep crit from full health.
-
fixed rogue pixels on the energy gu- ahem blaster carbine... and a few apc lights states being neigh-indistinguishable.
-
Fixed several "behind" layer tail sprites skipping areas normally covered by bodyparts.
-
Morgues' original alert beeping sound has been restored, they no longer go "ammunition depleted"
-
Fixed missing hypereutactic left inhand sprites.
-
Dying, ghosting, having your mind / ckey transferred to another mob, going softcrit or otherwise unconscious now properly turn off combat mode.
-
combat mode can't be toggled on while non fully conscious anymore.
-
Fixed limbs' set_disabled NOT dropping your held items, updating your hand slot inventory screen image, prompting chat messages and making your character scream like a sissy.
-
Lusty xenomoprh maids will now actually clean tiles they travel onto yet again.
-
Fixed double whitespace gap in human and AI examine. Fixed single whitespace in carbon examine.
-
Removed a few useless supply packs: "Siezed" power cells, means of production and promiscous organs.
-
Merged the synthetic blood supply pack into the standard blood supply pack, effectively removing a random type blood pack in favor of two synthetic ones.
-
Merged together premium carpet pack n°1 and n°2 to hold one of each standard pattern.
-
You can no longer estimate the amount of reagents found inside a damp rag.
-
You can now squeeze a rag's reagents into another open container, as long as the other one is not full.
-
Fixed ED-209 being unbuildable past the welding step.
-
Fixed ai displays status being reset to "Neutral" on login, regardless of choice.
-
Fixed tinfoil hats giving random traumas.
-
-
Ghommie (original PR by Denton) updated:
-
-
Added three new .38 ammo types. TRAC bullets, which embed a tracking implant inside the target's body. The implant only lasts for five minutes and doesn't work as a teleport beacon. Hot Shot bullets set targets on fire; Iceblox bullets drastically lower the target's body temperature. They are available after researching the Subdermal Implants node (TRAC) or Exotic Ammunition node (Hot Shot/Iceblox).
-
Renamed the Technological Shells research node to Exotic Ammunition.
-
The "lifespan_postmortem" var now determines how long tracking implants work after death.
-
.357 AP speedloaders can now be ordered from syndicate uplinks.
-
lowered the cost of uplink's .357 speedloaderd from 4 to 3.
-
-
Ghommie (original PR by nicbn and Menshin) updated:
-
-
You can click on things that are under flaps or holo barriers.
-
-
Ghommie (original PRs by ShizCalev, CRTXBacon and Niknakflak) updated:
-
-
Adds the intelliLantern, a big ol' spooky intelliCard skin
-
crafting recipe for the new intelliCard skin (requires 1 pumpkin, 1 intelliCard, 5 cables and a wirecutter as a tool)
-
changed the intelliTater crafting recipe to match the intelliLantern recipe (but with a potato for obvious reasons) add:cute pai gameboy face :3
-
-
Ghommie, porting lot of PRs by MrDoomBringer, AnturK, nemvar and coiax. updated:
-
-
Admins can now launch supplypods the old, slightly quicker way as well
-
Centcom-launched supplypods will now properly delimb you (if they are designated to do so) instead of touching you then literally yeeting all of your internal organs out of your body.
-
Centcom can now specify if they want to yeet all of your organs out of your body with a supplypod
-
Supplypods sound a bit nicer as the land now.
-
admins can now adjust the animation duration for centcom-launched supplypods
-
admins can adjust any sounds that are played as the supplypod lands
-
Reverse-Supplypods (the admin-launched ones) no longer stay behind after rising up, and also auto-delete from centcom.
-
The centcom podlauncher now has better logging
-
Admins can now allow ghosts to follow the delivery of Centcom-launched supply pods
-
Admins can now use the Centcom Podlauncher to launch things without the things looking like they're being sent inside a pod.
-
sparks will not generate if the quietLanding effect is on, for the centcom podlauncher
-
makes input text clearer for the centcom podlauncher
-
New 'Podspawn' verb, which functions like 'Spawn', except any atoms movable spawned will be dropped in via a no-damage, no-explosion Centcom supply pod.
-
Removed an oversight that made many obj/effect subtypes accidentally bombproof.
-
-
GrayRachnid updated:
-
-
Added saboteur syndicate engiborg
-
changed cyborg tool icons and the secborg taser/laser icons.
-
Fixes golden toolbox missing inhand sprite
-
Added traumas
-
Added science powergame tool
-
a few hearing args
-
fixed my mistakes
-
tweaked the number of ingredients/pancakes you can stack.
-
-
Hatterhat updated:
-
-
The Big Red Button now sets bomb timers to 2 seconds, instead of 5.
-
Gloves of the North Star (not Hugs of the North Star) now use all their intents very, very fast. This does not apply to grabs' click cooldown, nor shoving people.
-
The seedvault/alien plant DNA manipulator can now be printed off with Alien Biotechnology.
-
-
Iroquois-Pliskin updated:
-
-
Removed Clockwork Cult Surgical facility from Reebe
-
-
Jerry Derpington, baldest of the balds, and nemvar. updated:
-
-
Nanotrasen has lost communication to two away mission sites that contained a beach for Nanotrasen employees.
-
Nanotrasen has been able to locate a new away mission site that ALSO has a beach. Nanotrasen employees will be able to enjoy the beach after all!
-
Seashells have been added to the game.
-
-
KathrinBailey updated:
-
-
Two extra 'luxury' dorms rooms!
-
Gas miners to atmos.
-
Posters around the station.
-
Vacant room added to the Starboard Bow with it's own APC, above electrical maintenance.
-
New trendy clothes to the locker room, giving variety and bringing fashion back onto Nanotrasen stations.
-
Coloured bedsheet and towel bin.
-
Maid uniforms for the janitor.
-
Completely reworked bar. Milk kegs added in bar office. The bar has been changed for a homey restaurant feel just in time for Christmas! You can now run it as an actual restaurant! Local Bartender Icktsie III loved it so much he rolled around on the new floor smiling happily.
-
Dorms rework. Fitness room now has lots of costumes and outfits.
-
Junk removed from engineering, welding goggles added.
-
Welding tools in engineering replaced with industrial welding tools.
-
Package wrappers and hand labellers now in major departments.
-
Cell charger moved from engineering lobby to the protolathe room, just like how it is in all of the other maps and just where the cell charger is actually needed.
-
Library redesigned to have a private room and a 3x3 private study that is cleaned up.
-
Paper bins have gone big or gone home, with premium stationery scattered around. Engineering and security now have a labeller and packaging supplies.
-
Dark spot top left of Botany fixed.
-
Huge galactic-sized dark spot in bar fixed.
-
Light replacers now are less horrifically overpowered and PTSD-inducing for the server.
-
Fixes issue 9706: https://github.com/Citadel-Station-13/Citadel-Station-13/issues/9706 Part of maint getting hit by radstorms.
-_Kathrin's Box Beautification:_
Added support for Multi-Z power, atmospherics and disposals
-
massive service department nerf: space can no longer be extra crispy.
-
-
Knouli updated:
-
-
attack_self proc for the legion core which triggers a self-heal al la the previous 'afterattack' proc, as if clicking on the character's own sprite to self-heal
-
admin logging for all three use cases of legion core healing - afterattack, attack_self, and implanted ui_action_click
-
-
Krysonism, Ghommie updated:
-
-
NT has made breakthroughs in ice cream science, ice creams can now be flavoured with any reagent!
-
The ice cream vat now accepts beakers.
-
Grape and Peach icecreams have scoop overlays yet again.
-
-
Linzolle updated:
-
-
butchering component update
-
hat tossing can no longer knock hats off
-
strange reagent being unable to revive simplemobs
-
jitter animation and more clear text to strange reagent revival
-
-
Mickyy5 updated:
-
-
Nanotrasen are now issuing Plasmamen with plasma in their survival boxes
-
-
MrJWhit updated:
-
-
tweaked brain damage line
-
-
Naksu, ShizCalev updated:
-
-
Refactored examine-code
-
Examining a human with a burned prosthetic limb will no longer tell you that the limb is blistered.
-
Items will now inform you if they are resistant to frost, fire, acid, and lava when examined.
-
-
Owai-Seek updated:
-
-
"silly" bounties
-
"gardenchef" bounties
-
several bounties that require seriously good RNG to pull off.
-
moved several chef and assistant bounties to silly and gardenchef
-
modified several bounty point rewards
-
added new files "silly.dm" and "gardenchef.dm"
-
15+ new crates for cargo
-
organizes crates and moving them to proper categories
-
some dumb stuff like toner crates re
-
leg wraps and sweaters to clothesmate
-
screwdriver and cable coil to janidrobe
-
screwdriver and cable coil to janibelt whitelist (for fixing/placing light fixtures)
-
monkey cube, syringe, enzyme, soy sauce, and cryoxadone to chef's vendor (contraband and premium)
-
add cracker, beans, honey bars, lollipops, chocolate coin, and spider lollipop to snack vendors (contraband and premium)
-
newspaper to loadout menu for bapping purposes
-
removed poppy pretzels from snack vendor premium
-
maid uniform (janimaid alt) to kinkmate.
-
moves gear harness from premium to normal stock in kinkmate
-
re-balanced metal shield bounty
-
cryoxadone bottle (for use in chef vendor)
-
-
PersianXerxes updated:
-
-
Reduces the grace period for meteors from a minimum of 5 and maximum of 10 to 3 and 6 minutes respectively.
-
Adds a pair of VR sleepers to Box Station's permabrig
-
Adds a pair of VR sleepers to Delta Station's permabrig
-
Adds a pair of VR sleepers to Pubby Station's permabrig
-
Adds a pair of VR sleepers to Meta Station's permabrig
-
-
Putnam updated:
-
-
From-ghosts dynamic rulesets now actually listen to "required candidates"
-
Every dynamic-triggered event is now blacklisted from being triggered by the random events system when dynamic can trigger them.
-
Dynamic voting now features extended, if recent rounds have been chaotic.
-
Roundstart rulesets now scale on population ready rather than total population.
-
Threat log now accurately represents what actually used the threat.
-
Verbose threat log (admin-only) now shows ALL threat level changes.
-
VR mobs can no longer be dynamic midround antags.
-
Personal closets can use anything that holds an ID card now.
-
-
Putnam3145 updated:
-
-
traitors work now
-
Gas filters now push gas the same way volume pumps do.
-
Gas filters now won't clog if only one output is clogged.
-
Glowsticks can no longer be radioactively contaminated (one more supermatter contam exploit gone)
-
traitor removal is no longer borked
-
Dynamic voting
-
Added DYNAMIC_VOTING to game_options
-
SDGF now copies memories as well as antag data and factions.
-
Summon events now properly costs threat.
-
Refunded spells refund threat, too.
-
Made wizard spells inherently have a requirement and cost.
-
Meteor wave is no longer repeatable in dynamic.
-
tweaked nuke ops
-
Organs can no longer be radioactively contaminated.
-
-
Robustin, Subject217 updated:
-
-
The NukeOp Shuttle hull has been dramatically hardened. The walls are now "R-Walls" with far greater explosion resistance.
-
The NukeOp Shuttle guns have been significantly buffed. They now rapidly fire a new type of penetrator round, at a greater range, and have far greater explosion resistance.
-
The nuclear device on the NukeOp Shuttle is now in an anchored state by default, the Nuke can only be unanchored by inserting the disk and entering the proper code.
-
Non-Syndicate cyborgs are now unable to access the NukeOp Shuttle Console.
-
You can now unanchor Nukes even when the floor under them has been destroyed
adds the mining winter coat to mining wardrobes and mining lockers
-
-
ShizCalev updated:
-
-
Ghosts can now see active AI cameras.
-
Fixed a couple of laser / energy guns never switching to the empty icon despite being unable to fire.
-
-
Swindly updated:
-
-
Fixed MMIs not being able to use mecha equipment
-
Fixed MMIs not getting mecha mouse pointers
-
Fixed MMIs not getting medical HUDs in Odysseuses
-
Brains can now switch to harm intent
-
-
Tetr4 updated:
-
-
Turning a tile with gas effects into space now gets rid of the effects.
-
-
Trilbyspaceclone updated:
-
-
plastic trash cart crafting with plastic
-
wallets are known for now holding more items
-
shades and clowns HP
-
six more crates, A barrel, A Loom, 40 cotton sheets, two sets of fruit crates, raw lumber crate
-
All fermi chems, Boozes, Medical, food chems now sell
-
Loads more to sell - Mech gear, Cooking and more!
-
Moved around the vaule of some things and removed elastic of most items
-
Rebreather implants will now loss vaule, Do to being just metal and glass
-
lowered how many chems are in lewd chem kegs to be around 150-100 as well as the fancy booze kegs
-
bad returns and tools used
-
8 new cargo crates!
-
tablet cargo crate by -3k
-
Closes a bunch of issues
-
updates changlogs and such
-
fixed a catnip not having sprites
-
Boh cant hold WEIGHT_CLASS_GIGANTIC, just Bulky. Makes katana, chainsaw and base ball bat into bulky items so they may fit
-
changes header to be more cit-like
-
new clothing for the hotel staff and a hat
-
-
Ty-the-Smonk updated:
-
-
You can now interact with self sustaining crossbreeds
-
-
Useroth updated:
-
-
Colored fairygrass variants.
-
Added a missing cherrybulb seedpack sprite
-
numbered storages now are sorted in a consistent way, instead of depending on ordering of their contents var
-
strange seeds as a buyable traitor botanist item
-
resolves the issues revolving around blackpowder exploding where the reaction happened, instead of where it actually is through making it explode instantly
-
the explosion delay moved from blackpowder directly into bomb cherries, to keep them functioning as intended
-
A bunch of newer tg plants
-
A bunch of newer tg plant traits
-
A couple of newer tg plant reagents
-
the new plants now properly get their reagents and reagent genes instead of being empty with UNKNOWN reagents listed in the DNA machine
-
extradimensional oranges now contain haloperidol
-
extradimensional oranges now actually grow properly and give proper seeds.
-
-
Weblure updated:
-
-
Button added to slime console that prints out the hotkey commands to the user. [Includes DMI update]
-
Shift-click a slime to pick it up, or the floor to drop all held slimes. (Requires Basic Slime Console upgrade)
-
Ctrl-click a slime to scan it.
-
Alt-click a slime to feed it a potion. (Requires Advanced Slime Console upgrade)
-
Ctrl-click on a dead monkey to recycle it, or the floor to place a new monkey. (Requires Monkey Console upgrade)
-
If the console does not have the required upgrade, an error message will print to the user.
-
You can now pick up a single slime from a pile, instead of all of them at once.
-
When recycling monkeys, the console will now report how many monkeys it has (will not report decimal increases).
-
Console now alerts you when you're out of monkeys and reports your current decimal amount.
-
Console messages are now styled consistently.
-
-
XDTM, ShizCalev, Naksu, Skoglol, cacogen, Rohesie (ported by Ghommie) updated:
-
-
Holding an ID in your hands uses it instead of your worn ID for authentication purposes.
-
If you don't have an ID in your id slot, the belt slot will be checked as well.
-
small cleanup to id and bounty console html generation
-
Hop console now hurts your eyes less. Red button text replaced with green.
-
IDs with ID console access now go into the Confirm Identity slot by default like they used to, similarly IDs without it go into the Target slot by default again
-
Can easily swap out IDs by clicking the machine or the UI fields with another ID
-
ID console now names which IDs are added/removed in its visible messages
-
Labels the ID slot fields when logged in so you know which is which
-
Can use Job Management without an ID provided the console is logged in (matches how the console now stays logged in even without an ID)
-
Can log in without an ID in the Target field (matches how the machine now stays logged in even after the ID is removed from the Target field)
-
Cleans up UI slightly (had some duplicate/conflicting buttons)
-
Fixes ID console duping issues. Includes some ID containers, such as PDAs, tablets and wallets, into the swapping behavior when an ID card is being removed and the item is being held.
-
-
Xantholne updated:
-
-
New Berets for most heads and departments available in their autodrobes or lockers
-
-
YakumoChen updated:
-
-
New AI Holograms and Displays! Ported from /vg/station.
-
-
actioninja updated:
-
-
med records no longer can eat id cards for no reason
-
Chat is properly sent to legacy window if goonchat fails to load again.
-
-
dapnee updated:
-
-
fixed closet initialisation being broken
-
emergency closets no longer have a 1% chance to delete themselves
-
Communications console window no longer updates, won't steal focus anymore.
-
Trimline neutral end exists now.
-
-
dzahlus updated:
-
-
added a new gun sounds
-
removed an old gun sounds
-
-
him updated:
-
-
hos and aeg guns now conform to le epic taser rework standards
-
-
kappa-sama updated:
-
-
changed flavor text of alien tech on uplink
-
added TG's icons for traitor, russian, and golden revolver
-
-
kevinz000 updated:
-
-
you can now choose never for this round for magical antags
-
Cargo has passive point generation again at 750 points/minute
-
Mindshield crate price increased from 3000 to 4000
-
Miasma sell price reduced from 15/mol to 4/mol
-
bluespace wizard apprentice now has blink instead of targeted area teleportation
it now takes 4 strands to make one piece of durathread cloth
-
Looms can now be attacked.
-
Durathread golem weaves his magic
-
Supply ordered looms are unanchored. Bring a wrench.
-
-
r4d6 updated:
-
-
Added Departements Winter Coats to the loadout list.
-
GoonStation 13 Development Team
diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml
index db9c8e6d08..464340ef24 100644
--- a/html/changelogs/.all_changelog.yml
+++ b/html/changelogs/.all_changelog.yml
@@ -23980,3 +23980,475 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py.
shellspeed1:
- rscadd: Adds Insect markings
- rscadd: Adds three new moth wings.
+2020-01-27:
+ 4dplanner, CRITAWAKETS, XDTM, ninjanomnom:
+ - rscadd: sepia slime extract has a delay before activating
+ - balance: the user of a sepia slime extract is no longer immune to the time field
+ - balance: chilling sepia is now the support extract - it always freezes the user,
+ but not other marked people. give one to your golem!
+ - rscadd: burning sepia spawns the rewind camera. Take a selfie with someone and
+ give it to them to make sure they remember the moment forever! You don't actually
+ need to give them the photo, they'll remember anyway. Keep it as a reminder.
+ - rscadd: rewinding (deja vu) effect resets your location to the turf you were on
+ after 10 seconds as well as resetting limbs/mobs/objects damage to the point
+ it was added.
+ - rscadd: The deja vu effect cannot resurrect the dead, but will heal their corpse.
+ New limbs fall off, old ones re-attach
+ - rscadd: 'Regenerative sepia cores add a deja vu effect before healing instead
+ of a timestop remove: timefreeze camera is admin spawn only.'
+ - rscadd: recurring sepia recalls to the hand of the last person to touch it after
+ activating. Reusable timestop grenades!
+ - tweak: some special cameras don't prompt for customisation
+ - bugfix: timefreeze camera actually makes a photo
+ - bugfix: timestop stops pathing and mechs.
+ - bugfix: adds a check to make sure simple_animals don't get their AI toggled while
+ sentient
+ - bugfix: Adds the timestop overlay to frozen projectiles
+ - bugfix: Timestopped things have INFINITY move_resist as opposed to being anchored
+ - bugfix: Timestop will now unfreeze things that somehow leave it
+ - bugfix: mobs in the middle of a walk_to will have their walk stopped by timestop
+ - bugfix: mobs that are stunned will be stopped mid walk as well
+ - bugfix: pulling respects changes in move_force
+ - bugfix: swapping places respects move_force if the participant is not willing
+ - bugfix: timestop is properly defeated by antimagic.
+ - bugfix: timestop only checks antimagic once
+ - tweak: Time stop now applies its visual effect on floors, walls and static structures
+ (with no change otherwise)
+ - tweak: Movable structures are now anchored while time stopped.
+ - tweak: Timestop effects now prevent speech.
+ AffectedArc07:
+ - code_imp: All code files are now in the LF line format, please stick to it
+ - tweak: Added CI step to check for CRLF files
+ - code_imp: Line ending CI works now
+ Arkatos, Zxaber, Ghommie:
+ - rscadd: Certain AI abilities now dynamically show their remaining uses on the
+ mouse hover over their respective action buttons.
+ - bugfix: Malfunctioning AIs can no longer abuse the confirmation popup to create
+ extra (unstoppable) doomsdays.
+ - bugfix: Fixed AIs being able to use some of their abilities such as the doomsday
+ whilst dead, and the doomsday loading phase not halting upon AI death.
+ Arturlang:
+ - tweak: Vampires are no longer as tanky
+ - rscadd: Added modular computers to the loadout
+ Bhijn:
+ - rscadd: Limbs now regenerate their stamina faster while disabled
+ - rscadd: Limbs now have the same incoming stamina damage multiplier mechanics as
+ spacemen, where the more staminaloss they take while disabled, the less staminaloss
+ they'll take.
+ - balance: Limbs have had their base stamina regen rate doubled to match the doubled
+ stamina regen rate of standard spacemen.
+ - rscadd: Added a preference to make the sprint hotkey be a toggle instead of a
+ hold bind
+ - rscadd: Added a preference to bind the sprint hotkey to space instead of shift.
+ - bugfix: server_hop can no longer be used to remotely lobotomize a spaceman
+ Bhijn helped:
+ - bugfix: Fixes Dragon's Tooth Sword 50% armor penetration by making it 35%
+ BonniePandora:
+ - admin: 'Added another SDQL option. Using "UPDATE selectors SET #null=value" will
+ now resolve value and discard the return. This is useful if you only care about
+ the side effect of a proc.'
+ CameronWoof:
+ - rscadd: Ghost Cafe patrons now spawn with chameleon kits. Dress up! Be fancy!
+ - rscadd: Robots can now check the crew manifest from anywhere with the "View Crew
+ Manifest" verb.
+ - tweak: Lizard tails are now viable options for humans and anthromorphs.
+ Commandersand:
+ - bugfix: fixed some clothnig sprites
+ - balance: lightning bolt,tesla,forcewall,emp spells have lower cooldown
+ - balance: fireball has a 10 second starting cooldown
+ DeltaFire15:
+ - bugfix: Vitality matrixes now correctly succ slimes
+ Denton, ported by Hatterhat:
+ - tweak: Most upgradeable machines now show their upgrade status when examined while
+ standing right next to them.
+ - tweak: Added examine messages to teleporter stations that hint at their multitool/wirecutter
+ interactions.
+ - tweak: Renamed teleporter stations from station to teleporter station.
+ - code_imp: Changed the teleporter hub accurate var to accuracy; the old name misled
+ people into thinking that it was a boolean.
+ Detective-Google:
+ - tweak: replaced the sprites with new ones requested.
+ EmeraldSundisk:
+ - rscadd: Adds a sign outside MetaStation's custodial closet
+ FantasticFwoosh, ported by Hatterhat:
+ - rscadd: Both reagent universal enzyme & sacks of flour are now available for their
+ respective costs from the biogenerator.
+ - rscadd: New crafting recipies for donut boxes, eggboxes & candle boxes have been
+ added to cardboard recipes for the collective benefit of service personnel and
+ the station.
+ Ghommie:
+ - tweak: Attached kevlar/soft/plastic padding accessories are now stealthier and
+ will no longer be displayed on mob examine.
+ - refactor: Refactored that mess of a code for alternate worn clothing sprites for
+ digitigrade and taurs.
+ - bugfix: Fixed some issues with the aforementioned feature you may or may not have
+ experienced because it was pretty lame.
+ - bugfix: Fixed missing digi versions fishnet sprites and wrong digitigrade left
+ dir purple stockings sprite.
+ - imageadd: Add digitigrade versions for boxers and the long johns.
+ - bugfix: Fixed the secret sauce recipe being randomized every round.
+ - bugfix: Fixed singularity pulls duping rods out of engine floors.
+ - rscdel: Removed an old pair of zipties from the captain closet.
+ - bugfix: Chestbursters won't delete the host's brain somewhat anymore.
+ - bugfix: Fixed roundstart mushpeople being unable to MUSH PUUUUUUUUUUUUNCH!!
+ - bugfix: Lattices can be examined yet again.
+ - bugfix: Made reagent containers examine text less annoying.
+ - bugfix: Virus food dispensers dispense virus food again.
+ - bugfix: Borg hypos inputs do not display typepaths anymore.
+ - bugfix: Restored chem dispensers alphabetical order.
+ - bugfix: Sanitized cargo export messages for reagents.
+ - bugfix: Riot foam dart (not the ammo box) design cost is yet again consistent
+ with the result's material amount.
+ - bugfix: Fixed a little exploit with ventcrawlers being capable of escaping closed
+ turfs by printing scrubbers/vents into them.
+ - bugfix: deconstructing a rubber toolbox with the destructive analyzer won't "lock"
+ the machine anymore.
+ - bugfix: Something about empty hand combat mode right click waving hands defying
+ common sense and being spammy.
+ - imageadd: Towels are now "digitigrade-friendly".
+ - bugfix: The Barefoot drink and mousetraps will now work even if wearing certain
+ "feet-less shoes".
+ - rscadd: Refactored code to allow all living mobs to use shields and not only humans.
+ - tweak: Monkys will now retaliate against aliens attacking them (as if they even
+ posed a threat to start with).
+ - tweak: add a click cooldown to the overly spammable table slamming.
+ Ghommie (original PRs by Kevinz000, ShivCalez, 4dplanner, Barhandar, 81Denton, zxaber, Fox-McCloud):
+ - rscadd: All atom movables now have move force and move resist, and pull force
+ An atom can only pull another atom if its pull force is stronger than that atom's
+ move resist
+ - rscadd: 'Mobs with a higher move force than an atom''s move resist will automatically
+ try to force the atom out of its way. This might not always work, depending
+ on how snowflakey code is. experimental: As of right now, everything has a move
+ force and resist of 100, and a pull force of 101. Things take (resist - force)
+ damage when bumped into experimental: Failing to move onto a tile will now still
+ bump up your last move timer. However, successfully pushing something out of
+ your way will result in you automatically moving into where it was.'
+ - bugfix: Bolted AIs can no longer be teleported by launchpads.
+ - balance: Megafauna cannot teleport
+ Hatterhat:
+ - balance: Beakers are generally more useful now, with slight capacity increases.
+ - tweak: Transfer amounts are different now. Adjust your muscle memory to compensate.
+ - balance: ore redemption machines actually get affected by lasers again kthx
+ - tweak: crusher trophy drop chance on mining mobs increased to 1 in 4 (from 1 in
+ 20)
+ - bugfix: Blood-drunk buff from blood-drunk eye crusher trophy is less likely to
+ cripple its user.
+ - tweak: Forcefield projectors now fit on toolbelts.
+ - imageadd: New sprites for ATMOS holofan and forcefield projectors!
+ - tweak: tweaks some values around with beaker transfer amounts, adds a transfer
+ value verb (altclick)
+ - rscadd: 'Syndicate sleepers (read: the ones that''re red and have controls in
+ them) can now be recreated with a sufficient basis in nonstandard (read: illegal)
+ technologies!'
+ - bugfix: Explorer hoods on standard explorer suits can be reinforced with goliath
+ hide plates again.
+ - rscadd: A shipment of Staff Assistant jumpsuits from the Goon-operated stations
+ appear to have made their way into your loadout selections - and into the contraband
+ list from ClothesMates...
+ - balance: Dropped Exo-suit armor to put it more in line with being an explorer
+ suit sidegrade and not a straight upgrade for Lavaland usage.
+ - rscadd: You can now print .357 AP speedloaders from Security techfabs after you
+ pick up the Advanced Non-Standard Ballistics node.
+ - rscdel: Forensic scanner removed from Advanced Biotechnology node.
+ - tweak: Ash Drake armor now has goliath resistance, same as the Exo-suit.
+ KathrinBailey:
+ - rscadd: Empty engineering lockers for mappers.
+ - rscadd: Industrial welding tools to the engineer welding locker.
+ - rscdel: Removed the multitool, airlock painter, mechanical toolbox, brown sneakers,
+ hazard vest and airlock painter from the CE's locker.
+ KeRSedChaplain:
+ - imageadd: The clockwork cuirass can now support slithering taur bodies.
+ Kevinz000:
+ - bugfix: Fixes successful projectile hits also striking another atom on the same
+ turf should the first one be not the target the projectile was meant for.
+ - rscdel: Removes infinite reflector loops.
+ Kraseo:
+ - bugfix: Jelly donuts and pink jelly donuts will now properly display sprinkled
+ icon state.
+ - bugfix: Jelly donuts and its variants will properly spawn with berry juice.
+ - bugfix: Slime jelly donuts have slime jelly in them now. (thanks ghommie)
+ - bugfix: Virgo hairstyles no longer have those annoying quotation marks. Rejoice
+ for you will no longer have to use the scrollbar.
+ - imagedel: Got rid of some strange-looking hairstyles. (Tbob, fingerweave, etc.)
+ LetterN:
+ - rscadd: Doppler logs
+ - tweak: Shockcolar and electropack uses the new signaler ui
+ - tweak: Renaming shockcolar requires a pen
+ - rscadd: Adds banjo
+ Linzolle:
+ - bugfix: fixes matchboxes runtiming every time they spawn
+ MrJWhit:
+ - tweak: Increases plasma usage in radiation collectors
+ Naksu:
+ - code_imp: reagent IDs have been removed in favor using reagent typepaths where
+ applicable
+ - bugfix: mechas, borg hyposprays etc no longer display internal reagent ids to
+ the player
+ Owai-Seek:
+ - rscadd: Four New Bounties
+ - tweak: tweaked several bounties
+ - tweak: reorganised several bounties
+ - balance: balanced several bounties
+ - bugfix: added shady jims to vendor
+ - rscadd: Towel, Bedsheet, and Colored bedsheet bins to Dojo. Some lockers with
+ carpets and wood, another bathroom/shower, tons of cleaning supplies, some additional
+ trash cans, a tiny medical area, and some vendors/vendor resupplies. Also added
+ some Ambrosia Gaia seeds.
+ - tweak: Moved some lights around, extended the dojo a bit to make it feel more
+ spacious.
+ - rscdel: Cafe Newsfeed Frame
+ PersianXerxes:
+ - tweak: Reduces the range of the forcefield projector from 7 tiles to 2 tiles.
+ Putnam:
+ - rscadd: Alcohol intolerance trait, which forces vomiting on any alcohol ingestion
+ - rscdel: Arousal damage is gone, and with it the arousal damage heart on the UI.
+ - rscdel: As exhibitionist relied on arousal damage, that's gone too.
+ - rscdel: Bonermeter and electronic stimulation circuit modules are gone.
+ - rscdel: Masturbation is no longer an option in the climax menu. It was identical
+ to climax alone in every way but text. Emote it out.
+ - rscadd: Arousal on genitals can now be displayed manually, on a case-by-case basis.
+ - rscadd: '"Climax alone" and "climax with partner" now only display to the people
+ directly involved in the interaction. rework: Catnip tea, catnip, crocin, hexacrocin,
+ camphor, hexacamphor all had functions or bits of functions reworked.'
+ - tweak: '"Climax with partner" now requires consent from both parties--it can no
+ longer be used on mindless mobs, and it asks the mob getting climaxed with if
+ they consent first.'
+ - tweak: A lot of MKUltra behaviors have been moved to hypno checks or removed due
+ to reliance on maso/nympho/exhib.
+ - rscadd: Cold-blooded quirk
+ - rscadd: 'Two relief valves for atmospherics, available in your RPD today: a unary
+ one that opens to air and a binary one that connects two pipenets, like a manual
+ valve.'
+ - tweak: The atmos waste outlet injector on box has been replaced with an atmos
+ waste relief valve.
+ Putnam3145:
+ - rscadd: Dynamic storytellers, a new voting paradigm for dynamic
+ - rscadd: Support for approval voting and condorcet (ranked choice) voting in server
+ votes
+ - rscadd: Ghost cafe mobs can now ghostize
+ - rscadd: Ghost cafe now has a cremator
+ - rscadd: Ghost cafe mobs are now eligible for ghost roles
+ - refactor: Ghost roles now use an element for eligibility purposes
+ - bugfix: You can toggle some prefs properly now.
+ - bugfix: no ass slap is no longer the same thing as no aphro
+ - balance: Devastating bombs kills bomb armor'd mobs again
+ - rscadd: Added a sort of "game mode ban" by way of having people rank their game
+ modes favorite to least favorite after the secret/extended vote.
+ - bugfix: Turns out the schulze scoring was written wrong and it was setting things
+ to 0 that shouldn't have been, so that's fixed.
+ - config: Random engines are now weighted.
+ - rscadd: Ghost dojo now has an autoylathe.
+ - rscdel: Ghost dojo no longer has VR or modular computer access.
+ - tweak: Ghost dojo mobs are pacifists.
+ - tweak: Ghost dojo booze-o-mat is now all access.
+ - bugfix: Ghost dojo crematorium now has a button.
+ - bugfix: Traitor objectives don't take a dump when they try to add an assassinate
+ anymore
+ - admin: Objective removal with antag panel no longer commented out silently while
+ still being an option that gives useful feedback on stuff it's not doing in
+ any respect
+ - rscadd: Second, temporary flavor text!
+ - bugfix: Limb damage works now
+ - tweak: Score instead of ranked choice for mode tiers
+ - bugfix: Chemistry not :b:roke
+ - tweak: Ghost dojo spawns will dust if their owner suicides or uses the "ghost"
+ verb.
+ - admin: Crafting is logged in a file now
+ - bugfix: temporary flavor text now pops up properly
+ - code_imp: demodularized player panel code, mostly
+ - admin: added ghost role eligibility delay removal to player panel
+ - balance: Metabolic synthesis disables if user isn't well-fed rather than if user
+ is starving
+ - bugfix: Gibtonite no longer instantly explodes upon being pickaxe'd.
+ - balance: Cold-blooded costs -2 quirk points now
+ - config: Added suicide to the config.
+ - tweak: Ghost cafe mobs are super duper attached to the ghost cafe.
+ - bugfix: Meow Mix bar sign works
+ - bugfix: Fixed a few relief valve behaviors
+ Ryll/Shaps, ported by Hatterhat:
+ - rscadd: Point-blanking people with shotguns actually throws them backwards!
+ Savotta:
+ - rscadd: snout
+ - imageadd: snout
+ Seris02:
+ - tweak: made it so trait blacklisting removes random positives instead of removing
+ everything
+ - rscadd: added atmos holofirelocks and temperature blocking
+ - tweak: tweaked how atmos holofan looks
+ - rscadd: stunglasses
+ - rscadd: disabler sechuds
+ - rscadd: adds coconut
+ - rscadd: adds a coconut bong
+ - rscadd: Auto ooc
+ - admin: changed "assume direct control" from m.ckey = src.ckey to adminmob.transfer_ckey(M)
+ so it works with auto ooc
+ - rscadd: marshmallow
+ - rscadd: telescopic IV drip
+ - bugfix: cardboard box speed
+ - tweak: makes gorilla shuttle emag only
+ - tweak: changed where the observe verb is.
+ - tweak: the way chameleon clothes update on change
+ - bugfix: chameleon cloak icon
+ - balance: Explosions now cause knockdown instead of KOs, with the amount of time
+ scaling off bomb armor. Heavy explosions still cause a ~2 second KO for follow
+ up attacks.
+ - balance: Devastating explosions now no longer gib players with more than 50 bomb
+ armor. This used to be more or less random depending on the amount of bomb armor.
+ - code_imp: Removed reagent explosion code that would trigger for <1 amount reactions.
+ - rscadd: traitor tool for bowmanizing headsets + bowmanizing headsets
+ - rscadd: durathread winter coats from hyper station
+ - tweak: conveyor belt now are stacks instead of single item
+ - tweak: conveyor crate has 30 belts
+ - tweak: printing a conveyor costs 3000 metal
+ - rscadd: you can press the conveyor switch in hand to link any not deployed belts
+ to it
+ - tweak: sniffing sneezing etc doing anything if they don't breathe
+ - bugfix: probably fixes the make mentor button
+ - tweak: the sprites for the conveyor belts to look more directional
+ Skoglol:
+ - bugfix: Falsewalls now properly hide pipes and wires.
+ SpaceManiac, bobbahbrown, ShizCalev, SpaceManiac (ported by Ghommie):
+ - code_imp: It is now possible to set a different most-base-turf per z-level.
+ - spellcheck: Removed unlawful reference to Disney's Star Wars franchise in map
+ logging.
+ - tweak: Moved mapping related errors to their own log file.
+ - bugfix: Destruction on Lavaland will no longer reveal space in rare situations.
+ Tlaltecuhtli, Nemvar, Trilby, Hatterhat:
+ - rscadd: russian surplus crate, sec ammo crate, meat/veggie/fruit crate, rped crate,
+ bomb suit crate, bio suit crate, parrot crate, chem crate, db crate
+ - tweak: lowered price of some crates which are overpriced for no reason aka the
+ tablet crate and the mech circuits crates, track implant crate has also track
+ .38 ammo
+ - bugfix: sectech vendor has a refill pack
+ Trilbyspaceclone:
+ - bugfix: Fermi plushie can be made once again
+ - tweak: range on Engi Tray scanners and Rad-Scanners
+ - bugfix: issues with mapping done my Trilby
+ - rscadd: Grass now makes light beer when distilled
+ - tweak: allows bandoliers to hold any ammo type as long as it has a casing
+ - server: Cleans out the last years of changlogs
+ - bugfix: 'rouge cases of #$39; in bottle/pill/patch/condiments'
+ - tweak: Adds missing but needed flags to MASON RIG
+ - bugfix: missing sprites with crushed cans
+ - tweak: Halfs the nutriments in sugar
+ - rscadd: Glitter is now makeable with ground plastic and some crayons
+ - code_imp: Makes more folders and files for uplink.
+ - rscadd: Seed packs now have RnD points inside them
+ - rscadd: New tips that reflect the now use seed packs have for Rnd / Chems affect
+ on plants
+ - code_imp: Made the research file look nice rather then an eye harming list
+ - bugfix: Fixed fragile space suits breaking from weak and non-damaging "weapons"
+ (such as the Sord, toys and foam darts).
+ - rscadd: 'tip of the round: Cleanbot can withstand lava and burning hot ash. Its
+ a god'
+ - rscadd: more glassware
+ - rscadd: Few new packs and glassmaker kit!
+ - tweak: prices/chems of crates/autobottler
+ - bugfix: some crates ungettable as well as some broken crates
+ Trilbyspaceclone, Ghommie:
+ - rscadd: More types of glass can now be used to make a solar panel, with different
+ sturdiness and efficiency depending the type.
+ Tupinambis:
+ - tweak: Replaces new fire alarms with a slightly updated version of the old ones.
+ - bugfix: Fixed bug where Amber alert had no proper alert lights, and red alert
+ had amber lights.
+ - balance: RLDs now cost more to use, have reduced matter capacity, and sheets are
+ worth less when refilling them.
+ Unit2E & JMoldy, ported by Hatterhat:
+ - balance: the powerfist now punches people away at high velocity depending on setting.
+ - rscadd: the powerfist now leaks the gas it uses onto the tile you're on.
+ - tweak: Adds weak punch variations for empty and near-empty punching.
+ Xantholne:
+ - bugfix: Christmas clothes that where missing stuff should work again
+ - tweak: Christmas clothes moved from clothesmate and loadout to premium autodrobe,
+ hoodies and boots remain.
+ - rscadd: Botany bee pet, Bumbles
+ - rscadd: New bee models that aren't 1 tile big of 0 opacity pixels
+ - rscadd: Cheongsam and Qipao clothing added to loadout and clothesmates
+ dapnee:
+ - imageadd: new toxin's uniform and accessories
+ - imagedel: old toxin's uniform and accessories
+ deathride58:
+ - balance: The stamina buffer no longer uses stamina while recharging.
+ jakeramsay007:
+ - rscadd: The assorted .357 revolvers (except russian revolver) can now also load
+ .38, like real life.
+ kappa-sama:
+ - rscadd: loot crates in cargo contraband
+ - code_imp: modular_citadel file movement
+ - code_imp: modular citadel loses some files
+ - balance: dragnet snares can now be removed in 5 seconds
+ keronshb:
+ - rscadd: Adds new features for nanites
+ - bugfix: fixed the missing icons from Dermal Button nanites
+ - rscadd: Ports the special nanite remote, mood programs, and research generating
+ nanites
+ - balance: rebalances some nanites
+ - bugfix: fixed my messup
+ kevinz000:
+ - rscadd: throwing things no longer makes them randomly turned as long as you aren't
+ on harm intent
+ - rscadd: Custom holoforms have been added for pAIs and AIs. oh and cyborg holograms
+ of specific modules too.
+ - balance: pais are no longer indestructible-flagged while in card form. pai radios
+ now short out if they are forcefully collapsed from damage.
+ - bugfix: telescopic iv drips now have the proper sanity checks for deployment.
+ - bugfix: megafauna can hear again
+ - balance: Cargo passive gen is now 500 down from 750.
+ - config: Added a few more nightshift config entries
+ - rscadd: nightshift_public_area variable in areas to determine how public an area
+ is.
+ - rscadd: NIGHT_SHIFT_PUBLIC_AREAS_ONLY config entry allows the server to be configured
+ to only nightshift areas of that level and below (so areas that are more public)
+ - rscadd: Config entries added for requiring authorizations to toggle nightshift.
+ Defaults to only requiring on public areas as determined by above
+ - balance: nuclear fist buffed.
+ - balance: cogscarabs are now fulltile-hitbox.
+ - balance: emitters are now hitscan
+ - balance: monkies gorrilize easier now
+ - rscadd: Ghosting no longer stops abductors from using you.
+ - rscadd: guests can now looc
+ - tweak: tableslamming has 0.4 second cooldown instead of 0.8
+ - bugfix: health analyzers now work
+ - code_imp: Mapping helpers added for power cables and atmos pipes.
+ - rscdel: Blood duplication is gone, but viruses now react up to 5 times, 1 time
+ per unit, for virus mix.
+ - rscadd: you can now block people on your PDA
+ - bugfix: Grenades can now have their timers adjusted.
+ - balance: Cloning has been nerfed.
+ - bugfix: hilbert hotel is now less of an exploity mess
+ - balance: Doorcrushes are once again instant.
+ nik707:
+ - tweak: engraving light_power from 1 to 0.3
+ r4d6:
+ - rscadd: Added a Radiation Hardsuit
+ - rscadd: Added a Radiation Hardsuit crate
+ - rscadd: Added 3 SM Engine variations.
+ - rscadd: 'Added a way to make opaque plastic flaps change: Change the already existing
+ flap recipe to show that they are see-through'
+ - rscadd: Animations for the RCD
+ - bugfix: Said animations being pulled by the Singulo
+ - balance: Prevent Reinforced Doors from being RCD'ed
+ - bugfix: Deconstructing Floors now cost MUs like everything else
+ - tweak: changed the RCD's upgrades into flags
+ - rscadd: Allow RCDs to print APC, Firelocks and Fire Alarms with the right upgrade
+ - balance: Allow Engineers to print RCDs and RPDs from their protolathe without
+ needing to hack one.
+ - rscadd: Added a playback device
+ - bugfix: Made the Voice Analyzer actually care about languages
+ - bugfix: fixed SM's piping
+ - rscadd: Added a message when pulsing the open wire.
+ - bugfix: fixed being able to use a playback device and a voice analyzer to activate
+ each others
+ - rscadd: added passive vent to the arsenal of atmospheric devices.
+ - rscadd: Mining base now has a common area accessible via a new shuttle on the
+ medium-highpop maps, and a security office connecting the gulag and the mining
+ base. Gulag also has functional atmos.
+ - rscadd: Added more Cyborg Landmarks
+ - refactor: better code for passive vents
+ - bugfix: fixed a hole in Meta
+ - rscadd: Added the ability to easily add variations of the mining base
+ - bugfix: fixed a sprite
+ timothyteakettle:
+ - bugfix: fixed not being able to remove trait genes without a disk being inserted
+ into the dna manipulator
diff --git a/html/changelogs/AutoChangeLog-pr-10073.yml b/html/changelogs/AutoChangeLog-pr-10073.yml
deleted file mode 100644
index 6764500fe5..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10073.yml
+++ /dev/null
@@ -1,28 +0,0 @@
-author: "4dplanner, CRITAWAKETS, XDTM, ninjanomnom"
-delete-after: True
-changes:
- - rscadd: "sepia slime extract has a delay before activating"
- - balance: "the user of a sepia slime extract is no longer immune to the time field"
- - balance: "chilling sepia is now the support extract - it always freezes the user, but not other marked people. give one to your golem!"
- - rscadd: "burning sepia spawns the rewind camera. Take a selfie with someone and give it to them to make sure they remember the moment forever! You don't actually need to give them the photo, they'll remember anyway. Keep it as a reminder."
- - rscadd: "rewinding (deja vu) effect resets your location to the turf you were on after 10 seconds as well as resetting limbs/mobs/objects damage to the point it was added."
- - rscadd: "The deja vu effect cannot resurrect the dead, but will heal their corpse. New limbs fall off, old ones re-attach"
- - rscadd: "Regenerative sepia cores add a deja vu effect before healing instead of a timestop
-remove: timefreeze camera is admin spawn only."
- - rscadd: "recurring sepia recalls to the hand of the last person to touch it after activating. Reusable timestop grenades!"
- - tweak: "some special cameras don't prompt for customisation"
- - bugfix: "timefreeze camera actually makes a photo"
- - bugfix: "timestop stops pathing and mechs."
- - bugfix: "adds a check to make sure simple_animals don't get their AI toggled while sentient"
- - bugfix: "Adds the timestop overlay to frozen projectiles"
- - bugfix: "Timestopped things have INFINITY move_resist as opposed to being anchored"
- - bugfix: "Timestop will now unfreeze things that somehow leave it"
- - bugfix: "mobs in the middle of a walk_to will have their walk stopped by timestop"
- - bugfix: "mobs that are stunned will be stopped mid walk as well"
- - bugfix: "pulling respects changes in move_force"
- - bugfix: "swapping places respects move_force if the participant is not willing"
- - bugfix: "timestop is properly defeated by antimagic."
- - bugfix: "timestop only checks antimagic once"
- - tweak: "Time stop now applies its visual effect on floors, walls and static structures (with no change otherwise)"
- - tweak: "Movable structures are now anchored while time stopped."
- - tweak: "Timestop effects now prevent speech."
diff --git a/html/changelogs/AutoChangeLog-pr-10097.yml b/html/changelogs/AutoChangeLog-pr-10097.yml
deleted file mode 100644
index 9b9a2c9a58..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10097.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Ghommie"
-delete-after: True
-changes:
- - tweak: "Attached kevlar/soft/plastic padding accessories are now stealthier and will no longer be displayed on mob examine."
diff --git a/html/changelogs/AutoChangeLog-pr-10112.yml b/html/changelogs/AutoChangeLog-pr-10112.yml
deleted file mode 100644
index f09d8fa9ef..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10112.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "Ghommie"
-delete-after: True
-changes:
- - refactor: "Refactored that mess of a code for alternate worn clothing sprites for digitigrade and taurs."
- - bugfix: "Fixed some issues with the aforementioned feature you may or may not have experienced because it was pretty lame."
diff --git a/html/changelogs/AutoChangeLog-pr-10123.yml b/html/changelogs/AutoChangeLog-pr-10123.yml
deleted file mode 100644
index 208b17b930..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10123.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "kevinz000"
-delete-after: True
-changes:
- - rscadd: "throwing things no longer makes them randomly turned as long as you aren't on harm intent"
diff --git a/html/changelogs/AutoChangeLog-pr-10125.yml b/html/changelogs/AutoChangeLog-pr-10125.yml
deleted file mode 100644
index 951b69ccb3..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10125.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "Kevinz000"
-delete-after: True
-changes:
- - bugfix: "Fixes successful projectile hits also striking another atom on the same turf should the first one be not the target the projectile was meant for."
- - rscdel: "Removes infinite reflector loops."
diff --git a/html/changelogs/AutoChangeLog-pr-10165.yml b/html/changelogs/AutoChangeLog-pr-10165.yml
deleted file mode 100644
index be6f9dedf7..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10165.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "Ghommie"
-delete-after: True
-changes:
- - bugfix: "Fixed missing digi versions fishnet sprites and wrong digitigrade left dir purple stockings sprite."
- - imageadd: "Add digitigrade versions for boxers and the long johns."
diff --git a/html/changelogs/AutoChangeLog-pr-10189.yml b/html/changelogs/AutoChangeLog-pr-10189.yml
deleted file mode 100644
index 5889e127d2..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10189.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "Putnam3145"
-delete-after: True
-changes:
- - rscadd: "Dynamic storytellers, a new voting paradigm for dynamic"
- - rscadd: "Support for approval voting and condorcet (ranked choice) voting in server votes"
diff --git a/html/changelogs/AutoChangeLog-pr-10191.yml b/html/changelogs/AutoChangeLog-pr-10191.yml
deleted file mode 100644
index c1366727bd..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10191.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "kevinz000"
-delete-after: True
-changes:
- - rscadd: "Custom holoforms have been added for pAIs and AIs. oh and cyborg holograms of specific modules too."
- - balance: "pais are no longer indestructible-flagged while in card form. pai radios now short out if they are forcefully collapsed from damage."
diff --git a/html/changelogs/AutoChangeLog-pr-10224.yml b/html/changelogs/AutoChangeLog-pr-10224.yml
deleted file mode 100644
index b7d07abe5c..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10224.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Seris02"
-delete-after: True
-changes:
- - tweak: "made it so trait blacklisting removes random positives instead of removing everything"
diff --git a/html/changelogs/AutoChangeLog-pr-10238.yml b/html/changelogs/AutoChangeLog-pr-10238.yml
deleted file mode 100644
index dda40c86c9..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10238.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-author: "SpaceManiac, bobbahbrown, ShizCalev, SpaceManiac (ported by Ghommie)"
-delete-after: True
-changes:
- - code_imp: "It is now possible to set a different most-base-turf per z-level."
- - spellcheck: "Removed unlawful reference to Disney's Star Wars franchise in map logging."
- - tweak: "Moved mapping related errors to their own log file."
- - bugfix: "Destruction on Lavaland will no longer reveal space in rare situations."
diff --git a/html/changelogs/AutoChangeLog-pr-10247.yml b/html/changelogs/AutoChangeLog-pr-10247.yml
deleted file mode 100644
index b78e5c415d..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10247.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "Seris02"
-delete-after: True
-changes:
- - rscadd: "added atmos holofirelocks and temperature blocking"
- - tweak: "tweaked how atmos holofan looks"
diff --git a/html/changelogs/AutoChangeLog-pr-10255.yml b/html/changelogs/AutoChangeLog-pr-10255.yml
deleted file mode 100644
index ebaccd0af6..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10255.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Xantholne"
-delete-after: True
-changes:
- - bugfix: "Christmas clothes that where missing stuff should work again"
diff --git a/html/changelogs/AutoChangeLog-pr-10256.yml b/html/changelogs/AutoChangeLog-pr-10256.yml
deleted file mode 100644
index eb14f716cc..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10256.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Putnam"
-delete-after: True
-changes:
- - rscadd: "Alcohol intolerance trait, which forces vomiting on any alcohol ingestion"
diff --git a/html/changelogs/AutoChangeLog-pr-10270.yml b/html/changelogs/AutoChangeLog-pr-10270.yml
deleted file mode 100644
index b47a249f9a..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10270.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Ghommie"
-delete-after: True
-changes:
- - bugfix: "Fixed the secret sauce recipe being randomized every round."
diff --git a/html/changelogs/AutoChangeLog-pr-10277.yml b/html/changelogs/AutoChangeLog-pr-10277.yml
new file mode 100644
index 0000000000..d2c873d010
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10277.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - refactor: "Custom snowflake plushies are now in config rather than code."
diff --git a/html/changelogs/AutoChangeLog-pr-10285.yml b/html/changelogs/AutoChangeLog-pr-10285.yml
deleted file mode 100644
index 89b0bbae43..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10285.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Ghommie"
-delete-after: True
-changes:
- - bugfix: "Fixed singularity pulls duping rods out of engine floors."
diff --git a/html/changelogs/AutoChangeLog-pr-10287.yml b/html/changelogs/AutoChangeLog-pr-10287.yml
deleted file mode 100644
index f59cd0bd7f..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10287.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Ghommie"
-delete-after: True
-changes:
- - rscdel: "Removed an old pair of zipties from the captain closet."
diff --git a/html/changelogs/AutoChangeLog-pr-10295.yml b/html/changelogs/AutoChangeLog-pr-10295.yml
new file mode 100644
index 0000000000..a776275e6e
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10295.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - balance: "Abductor mindsnapping (aka abductee objectives) can now be \"cured\" with brain surgery."
diff --git a/html/changelogs/AutoChangeLog-pr-10301.yml b/html/changelogs/AutoChangeLog-pr-10301.yml
deleted file mode 100644
index 2acac559e9..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10301.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Trilbyspaceclone"
-delete-after: True
-changes:
- - tweak: "range on Engi Tray scanners and Rad-Scanners"
diff --git a/html/changelogs/AutoChangeLog-pr-10319.yml b/html/changelogs/AutoChangeLog-pr-10319.yml
deleted file mode 100644
index 88235aab8e..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10319.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "Seris02"
-delete-after: True
-changes:
- - rscadd: "adds coconut"
- - rscadd: "adds a coconut bong"
diff --git a/html/changelogs/AutoChangeLog-pr-10329.yml b/html/changelogs/AutoChangeLog-pr-10329.yml
deleted file mode 100644
index e5d78ddf96..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10329.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "Naksu"
-delete-after: True
-changes:
- - code_imp: "reagent IDs have been removed in favor using reagent typepaths where applicable"
- - bugfix: "mechas, borg hyposprays etc no longer display internal reagent ids to the player"
diff --git a/html/changelogs/AutoChangeLog-pr-10330.yml b/html/changelogs/AutoChangeLog-pr-10330.yml
deleted file mode 100644
index b2a46ef605..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10330.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "keronshb"
-delete-after: True
-changes:
- - rscadd: "Adds new features for nanites"
diff --git a/html/changelogs/AutoChangeLog-pr-10337.yml b/html/changelogs/AutoChangeLog-pr-10337.yml
deleted file mode 100644
index 370a8fdc04..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10337.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "Seris02"
-delete-after: True
-changes:
- - rscadd: "Auto ooc"
- - admin: "changed \"assume direct control\" from m.ckey = src.ckey to adminmob.transfer_ckey(M) so it works with auto ooc"
diff --git a/html/changelogs/AutoChangeLog-pr-10344.yml b/html/changelogs/AutoChangeLog-pr-10344.yml
deleted file mode 100644
index e0381c6a43..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10344.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Ghommie"
-delete-after: True
-changes:
- - bugfix: "Chestbursters won't delete the host's brain somewhat anymore."
diff --git a/html/changelogs/AutoChangeLog-pr-10346.yml b/html/changelogs/AutoChangeLog-pr-10346.yml
deleted file mode 100644
index 3a0cd6dae4..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10346.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-author: "Bhijn"
-delete-after: True
-changes:
- - rscadd: "Limbs now regenerate their stamina faster while disabled"
- - rscadd: "Limbs now have the same incoming stamina damage multiplier mechanics as spacemen, where the more staminaloss they take while disabled, the less staminaloss they'll take."
- - balance: "Limbs have had their base stamina regen rate doubled to match the doubled stamina regen rate of standard spacemen."
diff --git a/html/changelogs/AutoChangeLog-pr-10347.yml b/html/changelogs/AutoChangeLog-pr-10347.yml
deleted file mode 100644
index c0508aa279..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10347.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "deathride58"
-delete-after: True
-changes:
- - balance: "The stamina buffer no longer uses stamina while recharging."
diff --git a/html/changelogs/AutoChangeLog-pr-10349.yml b/html/changelogs/AutoChangeLog-pr-10349.yml
deleted file mode 100644
index fad3179c6e..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10349.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-author: "KathrinBailey"
-delete-after: True
-changes:
- - rscadd: "Empty engineering lockers for mappers."
- - rscadd: "Industrial welding tools to the engineer welding locker."
- - rscdel: "Removed the multitool, airlock painter, mechanical toolbox, brown sneakers, hazard vest and airlock painter from the CE's locker."
diff --git a/html/changelogs/AutoChangeLog-pr-10350.yml b/html/changelogs/AutoChangeLog-pr-10350.yml
deleted file mode 100644
index 580f1d68cf..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10350.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-author: "Putnam3145"
-delete-after: True
-changes:
- - rscadd: "Ghost cafe mobs can now ghostize"
- - rscadd: "Ghost cafe now has a cremator"
- - rscadd: "Ghost cafe mobs are now eligible for ghost roles"
- - refactor: "Ghost roles now use an element for eligibility purposes"
diff --git a/html/changelogs/AutoChangeLog-pr-10352.yml b/html/changelogs/AutoChangeLog-pr-10352.yml
new file mode 100644
index 0000000000..7879bc1de1
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10352.yml
@@ -0,0 +1,4 @@
+author: "Seris02"
+delete-after: True
+changes:
+ - tweak: "tweaked the way the SM works"
diff --git a/html/changelogs/AutoChangeLog-pr-10356.yml b/html/changelogs/AutoChangeLog-pr-10356.yml
deleted file mode 100644
index 1016b559ee..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10356.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "Bhijn"
-delete-after: True
-changes:
- - rscadd: "Added a preference to make the sprint hotkey be a toggle instead of a hold bind"
- - rscadd: "Added a preference to bind the sprint hotkey to space instead of shift."
diff --git a/html/changelogs/AutoChangeLog-pr-10357.yml b/html/changelogs/AutoChangeLog-pr-10357.yml
deleted file mode 100644
index 048baa3041..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10357.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Putnam3145"
-delete-after: True
-changes:
- - bugfix: "You can toggle some prefs properly now."
diff --git a/html/changelogs/AutoChangeLog-pr-10361.yml b/html/changelogs/AutoChangeLog-pr-10361.yml
deleted file mode 100644
index 0224bdcb49..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10361.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "CameronWoof"
-delete-after: True
-changes:
- - rscadd: "Ghost Cafe patrons now spawn with chameleon kits. Dress up! Be fancy!"
diff --git a/html/changelogs/AutoChangeLog-pr-10362.yml b/html/changelogs/AutoChangeLog-pr-10362.yml
deleted file mode 100644
index f92e16302a..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10362.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Trilbyspaceclone"
-delete-after: True
-changes:
- - bugfix: "rouge cases of #$39; in bottle/pill/patch/condiments"
diff --git a/html/changelogs/AutoChangeLog-pr-10364.yml b/html/changelogs/AutoChangeLog-pr-10364.yml
deleted file mode 100644
index 8834c494ad..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10364.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "kevinz000"
-delete-after: True
-changes:
- - bugfix: "telescopic iv drips now have the proper sanity checks for deployment."
diff --git a/html/changelogs/AutoChangeLog-pr-10365.yml b/html/changelogs/AutoChangeLog-pr-10365.yml
deleted file mode 100644
index f7ba047fc7..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10365.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Putnam3145"
-delete-after: True
-changes:
- - bugfix: "no ass slap is no longer the same thing as no aphro"
diff --git a/html/changelogs/AutoChangeLog-pr-10366.yml b/html/changelogs/AutoChangeLog-pr-10366.yml
deleted file mode 100644
index ec8d234edd..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10366.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "Hatterhat"
-delete-after: True
-changes:
- - balance: "Beakers are generally more useful now, with slight capacity increases."
- - tweak: "Transfer amounts are different now. Adjust your muscle memory to compensate."
diff --git a/html/changelogs/AutoChangeLog-pr-10368.yml b/html/changelogs/AutoChangeLog-pr-10368.yml
deleted file mode 100644
index e6486f2815..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10368.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Hatterhat"
-delete-after: True
-changes:
- - balance: "ore redemption machines actually get affected by lasers again kthx"
diff --git a/html/changelogs/AutoChangeLog-pr-10369.yml b/html/changelogs/AutoChangeLog-pr-10369.yml
deleted file mode 100644
index c292d948fb..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10369.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Hatterhat"
-delete-after: True
-changes:
- - tweak: "crusher trophy drop chance on mining mobs increased to 1 in 4 (from 1 in 20)"
diff --git a/html/changelogs/AutoChangeLog-pr-10370.yml b/html/changelogs/AutoChangeLog-pr-10370.yml
deleted file mode 100644
index 6674fd2e17..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10370.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Hatterhat"
-delete-after: True
-changes:
- - bugfix: "Blood-drunk buff from blood-drunk eye crusher trophy is less likely to cripple its user."
diff --git a/html/changelogs/AutoChangeLog-pr-10371.yml b/html/changelogs/AutoChangeLog-pr-10371.yml
deleted file mode 100644
index e4322d70f1..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10371.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "kappa-sama"
-delete-after: True
-changes:
- - rscadd: "loot crates in cargo contraband"
diff --git a/html/changelogs/AutoChangeLog-pr-10372.yml b/html/changelogs/AutoChangeLog-pr-10372.yml
deleted file mode 100644
index 6bb60c3d8d..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10372.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "Hatterhat"
-delete-after: True
-changes:
- - tweak: "Forcefield projectors now fit on toolbelts."
- - imageadd: "New sprites for ATMOS holofan and forcefield projectors!"
diff --git a/html/changelogs/AutoChangeLog-pr-10374.yml b/html/changelogs/AutoChangeLog-pr-10374.yml
deleted file mode 100644
index 376f49a4d4..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10374.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Bhijn helped"
-delete-after: True
-changes:
- - bugfix: "Fixes Dragon's Tooth Sword 50% armor penetration by making it 35%"
diff --git a/html/changelogs/AutoChangeLog-pr-10375.yml b/html/changelogs/AutoChangeLog-pr-10375.yml
deleted file mode 100644
index 7d0cc2bf44..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10375.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "r4d6"
-delete-after: True
-changes:
- - rscadd: "Added a Radiation Hardsuit"
- - rscadd: "Added a Radiation Hardsuit crate"
diff --git a/html/changelogs/AutoChangeLog-pr-10378.yml b/html/changelogs/AutoChangeLog-pr-10378.yml
deleted file mode 100644
index c7ab7d42c1..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10378.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "EmeraldSundisk"
-delete-after: True
-changes:
- - rscadd: "Adds a sign outside MetaStation's custodial closet"
diff --git a/html/changelogs/AutoChangeLog-pr-10379.yml b/html/changelogs/AutoChangeLog-pr-10379.yml
deleted file mode 100644
index 9623f815d6..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10379.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Ryll/Shaps, ported by Hatterhat"
-delete-after: True
-changes:
- - rscadd: "Point-blanking people with shotguns actually throws them backwards!"
diff --git a/html/changelogs/AutoChangeLog-pr-10380.yml b/html/changelogs/AutoChangeLog-pr-10380.yml
deleted file mode 100644
index 4604329137..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10380.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-author: "Unit2E & JMoldy, ported by Hatterhat"
-delete-after: True
-changes:
- - balance: "the powerfist now punches people away at high velocity depending on setting."
- - rscadd: "the powerfist now leaks the gas it uses onto the tile you're on."
- - tweak: "Adds weak punch variations for empty and near-empty punching."
diff --git a/html/changelogs/AutoChangeLog-pr-10384.yml b/html/changelogs/AutoChangeLog-pr-10384.yml
deleted file mode 100644
index 315a62dadd..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10384.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-author: "Tlaltecuhtli, Nemvar, Trilby, Hatterhat"
-delete-after: True
-changes:
- - rscadd: "russian surplus crate, sec ammo crate, meat/veggie/fruit crate, rped crate, bomb suit crate, bio suit crate, parrot crate, chem crate, db crate"
- - tweak: "lowered price of some crates which are overpriced for no reason aka the tablet crate and the mech circuits crates, track implant crate has also track .38 ammo"
- - bugfix: "sectech vendor has a refill pack"
diff --git a/html/changelogs/AutoChangeLog-pr-10388.yml b/html/changelogs/AutoChangeLog-pr-10388.yml
deleted file mode 100644
index fd06a5db3f..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10388.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "CameronWoof"
-delete-after: True
-changes:
- - rscadd: "Robots can now check the crew manifest from anywhere with the \"View Crew Manifest\" verb."
diff --git a/html/changelogs/AutoChangeLog-pr-10399.yml b/html/changelogs/AutoChangeLog-pr-10399.yml
deleted file mode 100644
index cbdb00630c..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10399.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "kevinz000"
-delete-after: True
-changes:
- - balance: "Cargo passive gen is now 500 down from 750."
diff --git a/html/changelogs/AutoChangeLog-pr-10401.yml b/html/changelogs/AutoChangeLog-pr-10401.yml
deleted file mode 100644
index 2aa8d91244..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10401.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "r4d6"
-delete-after: True
-changes:
- - rscadd: "Added 3 SM Engine variations."
diff --git a/html/changelogs/AutoChangeLog-pr-10403.yml b/html/changelogs/AutoChangeLog-pr-10403.yml
deleted file mode 100644
index 321789a546..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10403.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "r4d6"
-delete-after: True
-changes:
- - rscadd: "Added a way to make opaque plastic flaps
-change: Change the already existing flap recipe to show that they are see-through"
diff --git a/html/changelogs/AutoChangeLog-pr-10404.yml b/html/changelogs/AutoChangeLog-pr-10404.yml
deleted file mode 100644
index 5f88a991ff..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10404.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Xantholne"
-delete-after: True
-changes:
- - tweak: "Christmas clothes moved from clothesmate and loadout to premium autodrobe, hoodies and boots remain."
diff --git a/html/changelogs/AutoChangeLog-pr-10410.yml b/html/changelogs/AutoChangeLog-pr-10410.yml
deleted file mode 100644
index c6c9d24749..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10410.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "CameronWoof"
-delete-after: True
-changes:
- - tweak: "Lizard tails are now viable options for humans and anthromorphs."
diff --git a/html/changelogs/AutoChangeLog-pr-10412.yml b/html/changelogs/AutoChangeLog-pr-10412.yml
deleted file mode 100644
index b838dbb680..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10412.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Linzolle"
-delete-after: True
-changes:
- - bugfix: "fixes matchboxes runtiming every time they spawn"
diff --git a/html/changelogs/AutoChangeLog-pr-10413.yml b/html/changelogs/AutoChangeLog-pr-10413.yml
deleted file mode 100644
index 6d6378675b..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10413.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "nik707"
-delete-after: True
-changes:
- - tweak: "engraving light_power from 1 to 0.3"
diff --git a/html/changelogs/AutoChangeLog-pr-10414.yml b/html/changelogs/AutoChangeLog-pr-10414.yml
new file mode 100644
index 0000000000..25750a889d
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10414.yml
@@ -0,0 +1,4 @@
+author: "CameronWoof"
+delete-after: True
+changes:
+ - tweak: "Lighting looks better now. I can say that because the PR wouldn't be merged and you wouldn't be reading this if it wasn't true."
diff --git a/html/changelogs/AutoChangeLog-pr-10416.yml b/html/changelogs/AutoChangeLog-pr-10416.yml
deleted file mode 100644
index 13432b283c..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10416.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-author: "r4d6"
-delete-after: True
-changes:
- - rscadd: "Animations for the RCD"
- - bugfix: "Said animations being pulled by the Singulo"
- - balance: "Prevent Reinforced Doors from being RCD'ed"
- - bugfix: "Deconstructing Floors now cost MUs like everything else"
- - tweak: "changed the RCD's upgrades into flags"
- - rscadd: "Allow RCDs to print APC, Firelocks and Fire Alarms with the right upgrade"
- - balance: "Allow Engineers to print RCDs and RPDs from their protolathe without needing to hack one."
diff --git a/html/changelogs/AutoChangeLog-pr-10417.yml b/html/changelogs/AutoChangeLog-pr-10417.yml
deleted file mode 100644
index 31ac154ac9..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10417.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-author: "kevinz000"
-delete-after: True
-changes:
- - config: "Added a few more nightshift config entries"
- - rscadd: "nightshift_public_area variable in areas to determine how public an area is."
- - rscadd: "NIGHT_SHIFT_PUBLIC_AREAS_ONLY config entry allows the server to be configured to only nightshift areas of that level and below (so areas that are more public)"
- - rscadd: "Config entries added for requiring authorizations to toggle nightshift. Defaults to only requiring on public areas as determined by above"
diff --git a/html/changelogs/AutoChangeLog-pr-10420.yml b/html/changelogs/AutoChangeLog-pr-10420.yml
deleted file mode 100644
index b719eca84c..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10420.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "MrJWhit"
-delete-after: True
-changes:
- - tweak: "Increases plasma usage in radiation collectors"
diff --git a/html/changelogs/AutoChangeLog-pr-10427.yml b/html/changelogs/AutoChangeLog-pr-10427.yml
deleted file mode 100644
index 5f4fe73988..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10427.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Hatterhat"
-delete-after: True
-changes:
- - tweak: "tweaks some values around with beaker transfer amounts, adds a transfer value verb (altclick)"
diff --git a/html/changelogs/AutoChangeLog-pr-10428.yml b/html/changelogs/AutoChangeLog-pr-10428.yml
deleted file mode 100644
index 2cd635d0a2..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10428.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Putnam3145"
-delete-after: True
-changes:
- - balance: "Devastating bombs kills bomb armor'd mobs again"
diff --git a/html/changelogs/AutoChangeLog-pr-10429.yml b/html/changelogs/AutoChangeLog-pr-10429.yml
deleted file mode 100644
index cc83f8abb6..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10429.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Trilbyspaceclone"
-delete-after: True
-changes:
- - tweak: "Adds missing but needed flags to MASON RIG"
diff --git a/html/changelogs/AutoChangeLog-pr-10433.yml b/html/changelogs/AutoChangeLog-pr-10433.yml
deleted file mode 100644
index 036e74a804..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10433.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "kevinz000"
-delete-after: True
-changes:
- - balance: "cogscarabs are now fulltile-hitbox."
diff --git a/html/changelogs/AutoChangeLog-pr-10435.yml b/html/changelogs/AutoChangeLog-pr-10435.yml
deleted file mode 100644
index 6b684a57a5..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10435.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "FantasticFwoosh, ported by Hatterhat"
-delete-after: True
-changes:
- - rscadd: "Both reagent universal enzyme & sacks of flour are now available for their respective costs from the biogenerator."
- - rscadd: "New crafting recipies for donut boxes, eggboxes & candle boxes have been added to cardboard recipes for the collective benefit of service personnel and the station."
diff --git a/html/changelogs/AutoChangeLog-pr-10437.yml b/html/changelogs/AutoChangeLog-pr-10437.yml
deleted file mode 100644
index ee454f47b9..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10437.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Ghommie"
-delete-after: True
-changes:
- - bugfix: "Fixed roundstart mushpeople being unable to MUSH PUUUUUUUUUUUUNCH!!"
diff --git a/html/changelogs/AutoChangeLog-pr-10439.yml b/html/changelogs/AutoChangeLog-pr-10439.yml
deleted file mode 100644
index 92b151b883..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10439.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "LetterN"
-delete-after: True
-changes:
- - rscadd: "Doppler logs"
diff --git a/html/changelogs/AutoChangeLog-pr-10440.yml b/html/changelogs/AutoChangeLog-pr-10440.yml
deleted file mode 100644
index e942f4056f..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10440.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "Putnam3145"
-delete-after: True
-changes:
- - rscadd: "Added a sort of \"game mode ban\" by way of having people rank their game modes favorite to least favorite after the secret/extended vote."
- - bugfix: "Turns out the schulze scoring was written wrong and it was setting things to 0 that shouldn't have been, so that's fixed."
diff --git a/html/changelogs/AutoChangeLog-pr-10452.yml b/html/changelogs/AutoChangeLog-pr-10452.yml
deleted file mode 100644
index e51b9ff752..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10452.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Ghommie"
-delete-after: True
-changes:
- - bugfix: "Lattices can be examined yet again."
diff --git a/html/changelogs/AutoChangeLog-pr-10454.yml b/html/changelogs/AutoChangeLog-pr-10454.yml
deleted file mode 100644
index d89603959e..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10454.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Commandersand"
-delete-after: True
-changes:
- - bugfix: "fixed some clothnig sprites"
diff --git a/html/changelogs/AutoChangeLog-pr-10456.yml b/html/changelogs/AutoChangeLog-pr-10456.yml
deleted file mode 100644
index 4c94455e47..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10456.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-author: "Putnam"
-delete-after: True
-changes:
- - rscdel: "Arousal damage is gone, and with it the arousal damage heart on the UI."
- - rscdel: "As exhibitionist relied on arousal damage, that's gone too."
- - rscdel: "Bonermeter and electronic stimulation circuit modules are gone."
- - rscdel: "Masturbation is no longer an option in the climax menu. It was identical to climax alone in every way but text. Emote it out."
- - rscadd: "Arousal on genitals can now be displayed manually, on a case-by-case basis."
- - rscadd: "\"Climax alone\" and \"climax with partner\" now only display to the people directly involved in the interaction.
-rework: Catnip tea, catnip, crocin, hexacrocin, camphor, hexacamphor all had functions or bits of functions reworked."
- - tweak: "\"Climax with partner\" now requires consent from both parties--it can no longer be used on mindless mobs, and it asks the mob getting climaxed with if they consent first."
- - tweak: "A lot of MKUltra behaviors have been moved to hypno checks or removed due to reliance on maso/nympho/exhib."
diff --git a/html/changelogs/AutoChangeLog-pr-10457.yml b/html/changelogs/AutoChangeLog-pr-10457.yml
deleted file mode 100644
index 203842b0b6..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10457.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "r4d6"
-delete-after: True
-changes:
- - rscadd: "Added a playback device"
- - bugfix: "Made the Voice Analyzer actually care about languages"
diff --git a/html/changelogs/AutoChangeLog-pr-10459.yml b/html/changelogs/AutoChangeLog-pr-10459.yml
deleted file mode 100644
index 77de8eb193..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10459.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Putnam"
-delete-after: True
-changes:
- - rscadd: "Cold-blooded quirk"
diff --git a/html/changelogs/AutoChangeLog-pr-10465.yml b/html/changelogs/AutoChangeLog-pr-10465.yml
deleted file mode 100644
index 26dff901b2..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10465.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-author: "Putnam3145"
-delete-after: True
-changes:
- - rscadd: "Ghost dojo now has an autoylathe."
- - rscdel: "Ghost dojo no longer has VR or modular computer access."
- - tweak: "Ghost dojo mobs are pacifists."
- - tweak: "Ghost dojo booze-o-mat is now all access."
- - bugfix: "Ghost dojo crematorium now has a button."
diff --git a/html/changelogs/AutoChangeLog-pr-10467.yml b/html/changelogs/AutoChangeLog-pr-10467.yml
deleted file mode 100644
index 7fa379f088..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10467.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Seris02"
-delete-after: True
-changes:
- - bugfix: "cardboard box speed"
diff --git a/html/changelogs/AutoChangeLog-pr-10468.yml b/html/changelogs/AutoChangeLog-pr-10468.yml
deleted file mode 100644
index 055411d0f2..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10468.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "kevinz000"
-delete-after: True
-changes:
- - rscadd: "Ghosting no longer stops abductors from using you."
diff --git a/html/changelogs/AutoChangeLog-pr-10472.yml b/html/changelogs/AutoChangeLog-pr-10472.yml
deleted file mode 100644
index 9404d70b7b..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10472.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "keronshb"
-delete-after: True
-changes:
- - bugfix: "fixed the missing icons from Dermal Button nanites"
diff --git a/html/changelogs/AutoChangeLog-pr-10473.yml b/html/changelogs/AutoChangeLog-pr-10473.yml
deleted file mode 100644
index f1821a3cce..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10473.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Bhijn"
-delete-after: True
-changes:
- - bugfix: "server_hop can no longer be used to remotely lobotomize a spaceman"
diff --git a/html/changelogs/AutoChangeLog-pr-10474.yml b/html/changelogs/AutoChangeLog-pr-10474.yml
deleted file mode 100644
index ae7b5b3fcb..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10474.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "Putnam3145"
-delete-after: True
-changes:
- - bugfix: "Traitor objectives don't take a dump when they try to add an assassinate anymore"
- - admin: "Objective removal with antag panel no longer commented out silently while still being an option that gives useful feedback on stuff it's not doing in any respect"
diff --git a/html/changelogs/AutoChangeLog-pr-10478.yml b/html/changelogs/AutoChangeLog-pr-10478.yml
deleted file mode 100644
index b174a5e8ee..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10478.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Seris02"
-delete-after: True
-changes:
- - tweak: "makes gorilla shuttle emag only"
diff --git a/html/changelogs/AutoChangeLog-pr-10481.yml b/html/changelogs/AutoChangeLog-pr-10481.yml
deleted file mode 100644
index c884a45ec9..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10481.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "dapnee"
-delete-after: True
-changes:
- - imageadd: "new toxin's uniform and accessories"
- - imagedel: "old toxin's uniform and accessories"
diff --git a/html/changelogs/AutoChangeLog-pr-10482.yml b/html/changelogs/AutoChangeLog-pr-10482.yml
deleted file mode 100644
index e97c8870d4..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10482.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "kappa-sama"
-delete-after: True
-changes:
- - code_imp: "modular_citadel file movement"
diff --git a/html/changelogs/AutoChangeLog-pr-10485.yml b/html/changelogs/AutoChangeLog-pr-10485.yml
deleted file mode 100644
index 6bf082f8ba..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10485.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Detective-Google"
-delete-after: True
-changes:
- - tweak: "replaced the sprites with new ones requested."
diff --git a/html/changelogs/AutoChangeLog-pr-10486.yml b/html/changelogs/AutoChangeLog-pr-10486.yml
deleted file mode 100644
index 448bbc162c..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10486.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Seris02"
-delete-after: True
-changes:
- - tweak: "changed where the observe verb is."
diff --git a/html/changelogs/AutoChangeLog-pr-10489.yml b/html/changelogs/AutoChangeLog-pr-10489.yml
deleted file mode 100644
index 4e393bd4b1..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10489.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "Seris02"
-delete-after: True
-changes:
- - tweak: "the way chameleon clothes update on change"
- - bugfix: "chameleon cloak icon"
diff --git a/html/changelogs/AutoChangeLog-pr-10492.yml b/html/changelogs/AutoChangeLog-pr-10492.yml
deleted file mode 100644
index de6ab309ab..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10492.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Trilbyspaceclone"
-delete-after: True
-changes:
- - rscadd: "Glitter is now makeable with ground plastic and some crayons"
diff --git a/html/changelogs/AutoChangeLog-pr-10493.yml b/html/changelogs/AutoChangeLog-pr-10493.yml
deleted file mode 100644
index 201eaad5b5..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10493.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Putnam3145"
-delete-after: True
-changes:
- - tweak: "Score instead of ranked choice for mode tiers"
diff --git a/html/changelogs/AutoChangeLog-pr-10497.yml b/html/changelogs/AutoChangeLog-pr-10497.yml
deleted file mode 100644
index 5acb6f8c47..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10497.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Trilbyspaceclone"
-delete-after: True
-changes:
- - code_imp: "Makes more folders and files for uplink."
diff --git a/html/changelogs/AutoChangeLog-pr-10498.yml b/html/changelogs/AutoChangeLog-pr-10498.yml
deleted file mode 100644
index 986abc77c2..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10498.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "kevinz000"
-delete-after: True
-changes:
- - tweak: "tableslamming has 0.4 second cooldown instead of 0.8"
diff --git a/html/changelogs/AutoChangeLog-pr-10501.yml b/html/changelogs/AutoChangeLog-pr-10501.yml
deleted file mode 100644
index 27e7ddbb8e..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10501.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "Putnam"
-delete-after: True
-changes:
- - rscadd: "Two relief valves for atmospherics, available in your RPD today: a unary one that opens to air and a binary one that connects two pipenets, like a manual valve."
- - tweak: "The atmos waste outlet injector on box has been replaced with an atmos waste relief valve."
diff --git a/html/changelogs/AutoChangeLog-pr-10504.yml b/html/changelogs/AutoChangeLog-pr-10504.yml
deleted file mode 100644
index 7e35ac6e72..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10504.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "Xantholne"
-delete-after: True
-changes:
- - rscadd: "Botany bee pet, Bumbles"
- - rscadd: "New bee models that aren't 1 tile big of 0 opacity pixels"
diff --git a/html/changelogs/AutoChangeLog-pr-10505.yml b/html/changelogs/AutoChangeLog-pr-10505.yml
deleted file mode 100644
index dd0932c50e..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10505.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "LetterN"
-delete-after: True
-changes:
- - tweak: "Shockcolar and electropack uses the new signaler ui"
- - tweak: "Renaming shockcolar requires a pen"
diff --git a/html/changelogs/AutoChangeLog-pr-10507.yml b/html/changelogs/AutoChangeLog-pr-10507.yml
deleted file mode 100644
index 70d82d9cf6..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10507.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "LetterN"
-delete-after: True
-changes:
- - rscadd: "Adds banjo"
diff --git a/html/changelogs/AutoChangeLog-pr-10509.yml b/html/changelogs/AutoChangeLog-pr-10509.yml
deleted file mode 100644
index 4e1ede0631..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10509.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Ghommie"
-delete-after: True
-changes:
- - bugfix: "Made reagent containers examine text less annoying."
diff --git a/html/changelogs/AutoChangeLog-pr-10511.yml b/html/changelogs/AutoChangeLog-pr-10511.yml
deleted file mode 100644
index 5f3bf739cb..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10511.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "kevinz000"
-delete-after: True
-changes:
- - bugfix: "health analyzers now work"
diff --git a/html/changelogs/AutoChangeLog-pr-10513.yml b/html/changelogs/AutoChangeLog-pr-10513.yml
deleted file mode 100644
index 6428c4e281..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10513.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "r4d6"
-delete-after: True
-changes:
- - rscadd: "Added a message when pulsing the open wire."
diff --git a/html/changelogs/AutoChangeLog-pr-10516.yml b/html/changelogs/AutoChangeLog-pr-10516.yml
deleted file mode 100644
index 39dd05a4db..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10516.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "BonniePandora"
-delete-after: True
-changes:
- - admin: "Added another SDQL option. Using \"UPDATE selectors SET #null=value\" will now resolve value and discard the return. This is useful if you only care about the side effect of a proc."
diff --git a/html/changelogs/AutoChangeLog-pr-10518.yml b/html/changelogs/AutoChangeLog-pr-10518.yml
deleted file mode 100644
index 29de186e50..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10518.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-author: "Seris02"
-delete-after: True
-changes:
- - balance: "Explosions now cause knockdown instead of KOs, with the amount of time scaling off bomb armor. Heavy explosions still cause a ~2 second KO for follow up attacks."
- - balance: "Devastating explosions now no longer gib players with more than 50 bomb armor. This used to be more or less random depending on the amount of bomb armor."
- - code_imp: "Removed reagent explosion code that would trigger for <1 amount reactions."
diff --git a/html/changelogs/AutoChangeLog-pr-10520.yml b/html/changelogs/AutoChangeLog-pr-10520.yml
deleted file mode 100644
index ae23c082d8..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10520.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-author: "Denton, ported by Hatterhat"
-delete-after: True
-changes:
- - tweak: "Most upgradeable machines now show their upgrade status when examined while standing right next to them."
- - tweak: "Added examine messages to teleporter stations that hint at their multitool/wirecutter interactions."
- - tweak: "Renamed teleporter stations from station to teleporter station."
- - code_imp: "Changed the teleporter hub accurate var to accuracy; the old name misled people into thinking that it was a boolean."
diff --git a/html/changelogs/AutoChangeLog-pr-10522.yml b/html/changelogs/AutoChangeLog-pr-10522.yml
deleted file mode 100644
index b03b990762..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10522.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Hatterhat"
-delete-after: True
-changes:
- - rscadd: "Syndicate sleepers (read: the ones that're red and have controls in them) can now be recreated with a sufficient basis in nonstandard (read: illegal) technologies!"
diff --git a/html/changelogs/AutoChangeLog-pr-10523.yml b/html/changelogs/AutoChangeLog-pr-10523.yml
deleted file mode 100644
index 999fc1dd92..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10523.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Hatterhat"
-delete-after: True
-changes:
- - bugfix: "Explorer hoods on standard explorer suits can be reinforced with goliath hide plates again."
diff --git a/html/changelogs/AutoChangeLog-pr-10526.yml b/html/changelogs/AutoChangeLog-pr-10526.yml
deleted file mode 100644
index d5f9b8f1b0..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10526.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Seris02"
-delete-after: True
-changes:
- - rscadd: "traitor tool for bowmanizing headsets + bowmanizing headsets"
diff --git a/html/changelogs/AutoChangeLog-pr-10530.yml b/html/changelogs/AutoChangeLog-pr-10530.yml
deleted file mode 100644
index 16cc66adb9..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10530.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "kappa-sama"
-delete-after: True
-changes:
- - code_imp: "modular citadel loses some files"
diff --git a/html/changelogs/AutoChangeLog-pr-10531.yml b/html/changelogs/AutoChangeLog-pr-10531.yml
deleted file mode 100644
index 6c1125a687..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10531.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-author: "Trilbyspaceclone"
-delete-after: True
-changes:
- - rscadd: "Seed packs now have RnD points inside them"
- - rscadd: "New tips that reflect the now use seed packs have for Rnd / Chems affect on plants"
- - code_imp: "Made the research file look nice rather then an eye harming list"
diff --git a/html/changelogs/AutoChangeLog-pr-10532.yml b/html/changelogs/AutoChangeLog-pr-10532.yml
deleted file mode 100644
index cf742b47f7..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10532.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-author: "Ghommie"
-delete-after: True
-changes:
- - bugfix: "Virus food dispensers dispense virus food again."
- - bugfix: "Borg hypos inputs do not display typepaths anymore."
- - bugfix: "Restored chem dispensers alphabetical order."
diff --git a/html/changelogs/AutoChangeLog-pr-10535.yml b/html/changelogs/AutoChangeLog-pr-10535.yml
deleted file mode 100644
index db37120317..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10535.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Hatterhat"
-delete-after: True
-changes:
- - rscadd: "A shipment of Staff Assistant jumpsuits from the Goon-operated stations appear to have made their way into your loadout selections - and into the contraband list from ClothesMates..."
diff --git a/html/changelogs/AutoChangeLog-pr-10539.yml b/html/changelogs/AutoChangeLog-pr-10539.yml
deleted file mode 100644
index 66a03ee573..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10539.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "Savotta"
-delete-after: True
-changes:
- - rscadd: "snout"
- - imageadd: "snout"
diff --git a/html/changelogs/AutoChangeLog-pr-10541.yml b/html/changelogs/AutoChangeLog-pr-10541.yml
new file mode 100644
index 0000000000..f59ebe0e75
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10541.yml
@@ -0,0 +1,5 @@
+author: "Detective-Google"
+delete-after: True
+changes:
+ - rscadd: "the POOL.
+remove: boxstation dorms 7"
diff --git a/html/changelogs/AutoChangeLog-pr-10544.yml b/html/changelogs/AutoChangeLog-pr-10544.yml
deleted file mode 100644
index a27ffad716..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10544.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "AffectedArc07"
-delete-after: True
-changes:
- - code_imp: "All code files are now in the LF line format, please stick to it"
diff --git a/html/changelogs/AutoChangeLog-pr-10546.yml b/html/changelogs/AutoChangeLog-pr-10546.yml
deleted file mode 100644
index a49c2027e5..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10546.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "r4d6"
-delete-after: True
-changes:
- - bugfix: "fixed being able to use a playback device and a voice analyzer to activate each others"
diff --git a/html/changelogs/AutoChangeLog-pr-10547.yml b/html/changelogs/AutoChangeLog-pr-10547.yml
deleted file mode 100644
index 41d255e991..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10547.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Putnam3145"
-delete-after: True
-changes:
- - tweak: "Ghost dojo spawns will dust if their owner suicides or uses the \"ghost\" verb."
diff --git a/html/changelogs/AutoChangeLog-pr-10549.yml b/html/changelogs/AutoChangeLog-pr-10549.yml
deleted file mode 100644
index 11a770945d..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10549.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Seris02"
-delete-after: True
-changes:
- - rscadd: "durathread winter coats from hyper station"
diff --git a/html/changelogs/AutoChangeLog-pr-10552.yml b/html/changelogs/AutoChangeLog-pr-10552.yml
deleted file mode 100644
index 846667ea2a..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10552.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "Tupinambis"
-delete-after: True
-changes:
- - tweak: "Replaces new fire alarms with a slightly updated version of the old ones."
- - bugfix: "Fixed bug where Amber alert had no proper alert lights, and red alert had amber lights."
diff --git a/html/changelogs/AutoChangeLog-pr-10556.yml b/html/changelogs/AutoChangeLog-pr-10556.yml
deleted file mode 100644
index af42a9eee6..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10556.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "AffectedArc07"
-delete-after: True
-changes:
- - tweak: "Added CI step to check for CRLF files"
diff --git a/html/changelogs/AutoChangeLog-pr-10557.yml b/html/changelogs/AutoChangeLog-pr-10557.yml
deleted file mode 100644
index f5b37f6cee..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10557.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "timothyteakettle"
-delete-after: True
-changes:
- - bugfix: "fixed not being able to remove trait genes without a disk being inserted into the dna manipulator"
diff --git a/html/changelogs/AutoChangeLog-pr-10558.yml b/html/changelogs/AutoChangeLog-pr-10558.yml
deleted file mode 100644
index 09186767f3..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10558.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "DeltaFire15"
-delete-after: True
-changes:
- - bugfix: "Vitality matrixes now correctly succ slimes"
diff --git a/html/changelogs/AutoChangeLog-pr-10559.yml b/html/changelogs/AutoChangeLog-pr-10559.yml
new file mode 100644
index 0000000000..a48fc6c289
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10559.yml
@@ -0,0 +1,4 @@
+author: "Seris02"
+delete-after: True
+changes:
+ - rscadd: "custom reagent pie smite"
diff --git a/html/changelogs/AutoChangeLog-pr-10563.yml b/html/changelogs/AutoChangeLog-pr-10563.yml
deleted file mode 100644
index 5efc4cfdb1..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10563.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Trilbyspaceclone"
-delete-after: True
-changes:
- - bugfix: "Fixed fragile space suits breaking from weak and non-damaging \"weapons\" (such as the Sord, toys and foam darts)."
diff --git a/html/changelogs/AutoChangeLog-pr-10567.yml b/html/changelogs/AutoChangeLog-pr-10567.yml
deleted file mode 100644
index 9d73d1d947..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10567.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Ghommie"
-delete-after: True
-changes:
- - bugfix: "Sanitized cargo export messages for reagents."
diff --git a/html/changelogs/AutoChangeLog-pr-10568.yml b/html/changelogs/AutoChangeLog-pr-10568.yml
deleted file mode 100644
index 6916f1289f..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10568.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "r4d6"
-delete-after: True
-changes:
- - rscadd: "added passive vent to the arsenal of atmospheric devices."
diff --git a/html/changelogs/AutoChangeLog-pr-10569.yml b/html/changelogs/AutoChangeLog-pr-10569.yml
deleted file mode 100644
index 0b9cb0b992..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10569.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "keronshb"
-delete-after: True
-changes:
- - rscadd: "Ports the special nanite remote, mood programs, and research generating nanites"
- - balance: "rebalances some nanites"
diff --git a/html/changelogs/AutoChangeLog-pr-10570.yml b/html/changelogs/AutoChangeLog-pr-10570.yml
deleted file mode 100644
index 9eaf2168a8..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10570.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "kevinz000"
-delete-after: True
-changes:
- - code_imp: "Mapping helpers added for power cables and atmos pipes."
diff --git a/html/changelogs/AutoChangeLog-pr-10571.yml b/html/changelogs/AutoChangeLog-pr-10571.yml
deleted file mode 100644
index 1d6844aa53..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10571.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Ghommie"
-delete-after: True
-changes:
- - bugfix: "Riot foam dart (not the ammo box) design cost is yet again consistent with the result's material amount."
diff --git a/html/changelogs/AutoChangeLog-pr-10572.yml b/html/changelogs/AutoChangeLog-pr-10572.yml
new file mode 100644
index 0000000000..d18e2d9e93
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10572.yml
@@ -0,0 +1,5 @@
+author: "Tupinambis"
+delete-after: True
+changes:
+ - tweak: "the portion of laws that require harm prevention by silicons has been removed."
+ - server: "silicon_laws.txt config file is required to be modified for full implementation."
diff --git a/html/changelogs/AutoChangeLog-pr-10575.yml b/html/changelogs/AutoChangeLog-pr-10575.yml
new file mode 100644
index 0000000000..c81ed76026
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10575.yml
@@ -0,0 +1,4 @@
+author: "Arturlang"
+delete-after: True
+changes:
+ - rscadd: "Replaces a lot of ingame UIs with TGUI next, increasing performance drastically."
diff --git a/html/changelogs/AutoChangeLog-pr-10578.yml b/html/changelogs/AutoChangeLog-pr-10578.yml
deleted file mode 100644
index a84fa5ba21..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10578.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "r4d6"
-delete-after: True
-changes:
- - rscadd: "Mining base now has a common area accessible via a new shuttle on the medium-highpop maps, and a security office connecting the gulag and the mining base. Gulag also has functional atmos."
diff --git a/html/changelogs/AutoChangeLog-pr-10579.yml b/html/changelogs/AutoChangeLog-pr-10579.yml
deleted file mode 100644
index c428c0109d..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10579.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "r4d6"
-delete-after: True
-changes:
- - rscadd: "Added more Cyborg Landmarks"
diff --git a/html/changelogs/AutoChangeLog-pr-10582.yml b/html/changelogs/AutoChangeLog-pr-10582.yml
deleted file mode 100644
index 2024088fb8..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10582.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Putnam3145"
-delete-after: True
-changes:
- - bugfix: "temporary flavor text now pops up properly"
diff --git a/html/changelogs/AutoChangeLog-pr-10583.yml b/html/changelogs/AutoChangeLog-pr-10583.yml
new file mode 100644
index 0000000000..e743fca877
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10583.yml
@@ -0,0 +1,4 @@
+author: "Putnam3145"
+delete-after: True
+changes:
+ - admin: "Added logging to various consent things."
diff --git a/html/changelogs/AutoChangeLog-pr-10584.yml b/html/changelogs/AutoChangeLog-pr-10584.yml
deleted file mode 100644
index e2c9f93ac8..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10584.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "AffectedArc07"
-delete-after: True
-changes:
- - code_imp: "Line ending CI works now"
diff --git a/html/changelogs/AutoChangeLog-pr-10586.yml b/html/changelogs/AutoChangeLog-pr-10586.yml
deleted file mode 100644
index eb90213398..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10586.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Tupinambis"
-delete-after: True
-changes:
- - balance: "RLDs now cost more to use, have reduced matter capacity, and sheets are worth less when refilling them."
diff --git a/html/changelogs/AutoChangeLog-pr-10588.yml b/html/changelogs/AutoChangeLog-pr-10588.yml
deleted file mode 100644
index d3771298b2..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10588.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "PersianXerxes"
-delete-after: True
-changes:
- - tweak: "Reduces the range of the forcefield projector from 7 tiles to 2 tiles."
diff --git a/html/changelogs/AutoChangeLog-pr-10590.yml b/html/changelogs/AutoChangeLog-pr-10590.yml
new file mode 100644
index 0000000000..e59c8decf0
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10590.yml
@@ -0,0 +1,11 @@
+author: "KathrinBailey"
+delete-after: True
+changes:
+ - bugfix: "Missing turf_decals in Cargo Office."
+ - bugfix: "Turns on the docking beacons on Box."
+ - bugfix: "Fixes Starboard Quarter maint room being spaced. It was never intended to be like how it was."
+ - bugfix: "The aforementioned maint room not having stuff in it."
+ - bugfix: "varedited photocopier sometimes not opening any UI."
+ - bugfix: "Atmos differences in Starboard Quarter maint."
+ - bugfix: "Atmos differences in destroyed shuttle/EVA bridge. Plating replaced with airless plating."
+ - bugfix: "Rotates AI satellite computers. These have probably been like this since computers had the old sprites and no directional ones. You shouldn't sit at a chair to operate a sideways computer."
diff --git a/html/changelogs/AutoChangeLog-pr-10591.yml b/html/changelogs/AutoChangeLog-pr-10591.yml
deleted file mode 100644
index ac8504ec9c..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10591.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "kevinz000"
-delete-after: True
-changes:
- - rscdel: "Blood duplication is gone, but viruses now react up to 5 times, 1 time per unit, for virus mix."
diff --git a/html/changelogs/AutoChangeLog-pr-10596.yml b/html/changelogs/AutoChangeLog-pr-10596.yml
new file mode 100644
index 0000000000..7f56a256a0
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10596.yml
@@ -0,0 +1,5 @@
+author: "Seris02"
+delete-after: True
+changes:
+ - rscadd: "hijack implant"
+ - code_imp: "changed mentions of the issilion proc to hasSiliconAccessInArea based on what the proc is used for"
diff --git a/html/changelogs/AutoChangeLog-pr-10597.yml b/html/changelogs/AutoChangeLog-pr-10597.yml
deleted file mode 100644
index 9f28c4f84c..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10597.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "Putnam3145"
-delete-after: True
-changes:
- - code_imp: "demodularized player panel code, mostly"
- - admin: "added ghost role eligibility delay removal to player panel"
diff --git a/html/changelogs/AutoChangeLog-pr-10598.yml b/html/changelogs/AutoChangeLog-pr-10598.yml
deleted file mode 100644
index 91b52b7043..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10598.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Putnam3145"
-delete-after: True
-changes:
- - balance: "Metabolic synthesis disables if user isn't well-fed rather than if user is starving"
diff --git a/html/changelogs/AutoChangeLog-pr-10599.yml b/html/changelogs/AutoChangeLog-pr-10599.yml
deleted file mode 100644
index 33435979c7..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10599.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Putnam3145"
-delete-after: True
-changes:
- - bugfix: "Gibtonite no longer instantly explodes upon being pickaxe'd."
diff --git a/html/changelogs/AutoChangeLog-pr-10600.yml b/html/changelogs/AutoChangeLog-pr-10600.yml
deleted file mode 100644
index ab0c5ac964..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10600.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-author: "Kraseo"
-delete-after: True
-changes:
- - bugfix: "Jelly donuts and pink jelly donuts will now properly display sprinkled icon state."
- - bugfix: "Jelly donuts and its variants will properly spawn with berry juice."
- - bugfix: "Slime jelly donuts have slime jelly in them now. (thanks ghommie)"
diff --git a/html/changelogs/AutoChangeLog-pr-10602.yml b/html/changelogs/AutoChangeLog-pr-10602.yml
deleted file mode 100644
index 9394c83671..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10602.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Ghommie"
-delete-after: True
-changes:
- - bugfix: "Fixed a little exploit with ventcrawlers being capable of escaping closed turfs by printing scrubbers/vents into them."
diff --git a/html/changelogs/AutoChangeLog-pr-10603.yml b/html/changelogs/AutoChangeLog-pr-10603.yml
deleted file mode 100644
index 0f1acbf0be..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10603.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "KeRSedChaplain"
-delete-after: True
-changes:
- - imageadd: "The clockwork cuirass can now support slithering taur bodies."
diff --git a/html/changelogs/AutoChangeLog-pr-10606.yml b/html/changelogs/AutoChangeLog-pr-10606.yml
deleted file mode 100644
index c6cc269cbe..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10606.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "r4d6"
-delete-after: True
-changes:
- - refactor: "better code for passive vents"
diff --git a/html/changelogs/AutoChangeLog-pr-10608.yml b/html/changelogs/AutoChangeLog-pr-10608.yml
deleted file mode 100644
index 1c2015813e..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10608.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "kevinz000"
-delete-after: True
-changes:
- - rscadd: "you can now block people on your PDA"
diff --git a/html/changelogs/AutoChangeLog-pr-10609.yml b/html/changelogs/AutoChangeLog-pr-10609.yml
deleted file mode 100644
index e28d929687..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10609.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Putnam3145"
-delete-after: True
-changes:
- - balance: "Cold-blooded costs -2 quirk points now"
diff --git a/html/changelogs/AutoChangeLog-pr-10610.yml b/html/changelogs/AutoChangeLog-pr-10610.yml
deleted file mode 100644
index e413574fa1..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10610.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-author: "Owai-Seek"
-delete-after: True
-changes:
- - rscadd: "Four New Bounties"
- - tweak: "tweaked several bounties"
- - tweak: "reorganised several bounties"
- - balance: "balanced several bounties"
- - bugfix: "added shady jims to vendor"
diff --git a/html/changelogs/AutoChangeLog-pr-10615.yml b/html/changelogs/AutoChangeLog-pr-10615.yml
deleted file mode 100644
index f67392cd7f..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10615.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "keronshb"
-delete-after: True
-changes:
- - bugfix: "fixed my messup"
diff --git a/html/changelogs/AutoChangeLog-pr-10617.yml b/html/changelogs/AutoChangeLog-pr-10617.yml
deleted file mode 100644
index 923913a043..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10617.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "kevinz000"
-delete-after: True
-changes:
- - bugfix: "Grenades can now have their timers adjusted."
diff --git a/html/changelogs/AutoChangeLog-pr-10619.yml b/html/changelogs/AutoChangeLog-pr-10619.yml
deleted file mode 100644
index acb76034ac..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10619.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-author: "Seris02"
-delete-after: True
-changes:
- - tweak: "conveyor belt now are stacks instead of single item"
- - tweak: "conveyor crate has 30 belts"
- - tweak: "printing a conveyor costs 3000 metal"
- - rscadd: "you can press the conveyor switch in hand to link any not deployed belts to it"
diff --git a/html/changelogs/AutoChangeLog-pr-10620.yml b/html/changelogs/AutoChangeLog-pr-10620.yml
deleted file mode 100644
index 8f5c990234..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10620.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Hatterhat"
-delete-after: True
-changes:
- - balance: "Dropped Exo-suit armor to put it more in line with being an explorer suit sidegrade and not a straight upgrade for Lavaland usage."
diff --git a/html/changelogs/AutoChangeLog-pr-10621.yml b/html/changelogs/AutoChangeLog-pr-10621.yml
deleted file mode 100644
index 0bb08dee2c..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10621.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Hatterhat"
-delete-after: True
-changes:
- - rscadd: "You can now print .357 AP speedloaders from Security techfabs after you pick up the Advanced Non-Standard Ballistics node."
diff --git a/html/changelogs/AutoChangeLog-pr-10622.yml b/html/changelogs/AutoChangeLog-pr-10622.yml
deleted file mode 100644
index 3aa4159cb8..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10622.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-author: "Owai-Seek"
-delete-after: True
-changes:
- - rscadd: "Towel, Bedsheet, and Colored bedsheet bins to Dojo. Some lockers with carpets and wood, another bathroom/shower, tons of cleaning supplies, some additional trash cans, a tiny medical area, and some vendors/vendor resupplies. Also added some Ambrosia Gaia seeds."
- - tweak: "Moved some lights around, extended the dojo a bit to make it feel more spacious."
- - rscdel: "Cafe Newsfeed Frame"
- - rscadd: "Towel, Bedsheet, and Colored bedsheet bins to Dojo. Some lockers with carpets and wood, another bathroom/shower, tons of cleaning supplies, some additional trash cans, a tiny medical area, and some vendors/vendor resupplies. Also added some Ambrosia Gaia seeds."
- - tweak: "Moved some lights around, extended the dojo a bit to make it feel more spacious."
- - rscdel: "Cafe Newsfeed Frame"
- - rscadd: "Towel, Bedsheet, and Colored bedsheet bins to Dojo. Some lockers with carpets and wood, another bathroom/shower, tons of cleaning supplies, some additional trash cans, a tiny medical area, and some vendors/vendor resupplies. Also added some Ambrosia Gaia seeds."
- - tweak: "Moved some lights around, extended the dojo a bit to make it feel more spacious."
diff --git a/html/changelogs/AutoChangeLog-pr-10480.yml b/html/changelogs/AutoChangeLog-pr-10623.yml
similarity index 52%
rename from html/changelogs/AutoChangeLog-pr-10480.yml
rename to html/changelogs/AutoChangeLog-pr-10623.yml
index 7ecfd7057e..623560f7d2 100644
--- a/html/changelogs/AutoChangeLog-pr-10480.yml
+++ b/html/changelogs/AutoChangeLog-pr-10623.yml
@@ -1,4 +1,4 @@
author: "Putnam3145"
delete-after: True
changes:
- - rscadd: "Second, temporary flavor text!"
+ - rscadd: "Lots of new traitor objectives"
diff --git a/html/changelogs/AutoChangeLog-pr-10628.yml b/html/changelogs/AutoChangeLog-pr-10628.yml
deleted file mode 100644
index f9d24a3a28..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10628.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Ghommie"
-delete-after: True
-changes:
- - bugfix: "deconstructing a rubber toolbox with the destructive analyzer won't \"lock\" the machine anymore."
diff --git a/html/changelogs/AutoChangeLog-pr-10630.yml b/html/changelogs/AutoChangeLog-pr-10630.yml
deleted file mode 100644
index ca4e75fdf1..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10630.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "kevinz000"
-delete-after: True
-changes:
- - balance: "Cloning has been nerfed."
diff --git a/html/changelogs/AutoChangeLog-pr-10632.yml b/html/changelogs/AutoChangeLog-pr-10632.yml
deleted file mode 100644
index 39d74002e4..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10632.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "Commandersand"
-delete-after: True
-changes:
- - balance: "lightning bolt,tesla,forcewall,emp spells have lower cooldown"
- - balance: "fireball has a 10 second starting cooldown"
diff --git a/html/changelogs/AutoChangeLog-pr-10635.yml b/html/changelogs/AutoChangeLog-pr-10635.yml
deleted file mode 100644
index 66c90808d6..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10635.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Hatterhat"
-delete-after: True
-changes:
- - rscdel: "Forensic scanner removed from Advanced Biotechnology node."
diff --git a/html/changelogs/AutoChangeLog-pr-10636.yml b/html/changelogs/AutoChangeLog-pr-10636.yml
deleted file mode 100644
index a6354ef6b5..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10636.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Trilbyspaceclone, Ghommie"
-delete-after: True
-changes:
- - rscadd: "More types of glass can now be used to make a solar panel, with different sturdiness and efficiency depending the type."
diff --git a/html/changelogs/AutoChangeLog-pr-10641.yml b/html/changelogs/AutoChangeLog-pr-10641.yml
deleted file mode 100644
index 479729e1f2..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10641.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "jakeramsay007"
-delete-after: True
-changes:
- - rscadd: "The assorted .357 revolvers (except russian revolver) can now also load .38, like real life."
diff --git a/html/changelogs/AutoChangeLog-pr-10643.yml b/html/changelogs/AutoChangeLog-pr-10643.yml
deleted file mode 100644
index 8fd496e0bc..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10643.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Trilbyspaceclone"
-delete-after: True
-changes:
- - rscadd: "tip of the round: Cleanbot can withstand lava and burning hot ash. Its a god"
diff --git a/html/changelogs/AutoChangeLog-pr-10644.yml b/html/changelogs/AutoChangeLog-pr-10644.yml
new file mode 100644
index 0000000000..b7544495d7
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10644.yml
@@ -0,0 +1,4 @@
+author: "ShadeAware"
+delete-after: True
+changes:
+ - rscadd: "Craftable Switchblades, a weaker subtype of normal switchblades that can be crafted using a Kitchen Knife, Modular Receiver, Rifle Stock and some Cable Coil. Requires a welder to complete."
diff --git a/html/changelogs/AutoChangeLog-pr-10645.yml b/html/changelogs/AutoChangeLog-pr-10645.yml
deleted file mode 100644
index 16ef8b2056..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10645.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "r4d6"
-delete-after: True
-changes:
- - rscadd: "Added the ability to easily add variations of the mining base"
diff --git a/html/changelogs/AutoChangeLog-pr-10649.yml b/html/changelogs/AutoChangeLog-pr-10649.yml
new file mode 100644
index 0000000000..04e3569ad3
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10649.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - rscadd: "Shuttle hijacking has been completely reworked. Alt-click the shuttle as a hijack-capable mind (so traitors, and especially traitors with hijack) to begin or continue a hacking process."
diff --git a/html/changelogs/AutoChangeLog-pr-10650.yml b/html/changelogs/AutoChangeLog-pr-10650.yml
deleted file mode 100644
index d4794291d1..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10650.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Arturlang"
-delete-after: True
-changes:
- - rscadd: "Added modular computers to the loadout"
diff --git a/html/changelogs/AutoChangeLog-pr-10664.yml b/html/changelogs/AutoChangeLog-pr-10664.yml
deleted file mode 100644
index e38ce50b24..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10664.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "kappa-sama"
-delete-after: True
-changes:
- - balance: "dragnet snares can now be removed in 5 seconds"
diff --git a/html/changelogs/AutoChangeLog-pr-10667.yml b/html/changelogs/AutoChangeLog-pr-10667.yml
deleted file mode 100644
index 85a60c285a..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10667.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Ghommie"
-delete-after: True
-changes:
- - bugfix: "Something about empty hand combat mode right click waving hands defying common sense and being spammy."
diff --git a/html/changelogs/AutoChangeLog-pr-10676.yml b/html/changelogs/AutoChangeLog-pr-10676.yml
deleted file mode 100644
index c586123b12..0000000000
--- a/html/changelogs/AutoChangeLog-pr-10676.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "Kraseo"
-delete-after: True
-changes:
- - bugfix: "Virgo hairstyles no longer have those annoying quotation marks. Rejoice for you will no longer have to use the scrollbar."
- - imagedel: "Got rid of some strange-looking hairstyles. (Tbob, fingerweave, etc.)"
diff --git a/html/changelogs/AutoChangeLog-pr-10700.yml b/html/changelogs/AutoChangeLog-pr-10700.yml
new file mode 100644
index 0000000000..296ebe4d99
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10700.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - tweak: "The anomalous honking crystal should now properly clown your mind."
diff --git a/html/changelogs/AutoChangeLog-pr-10704.yml b/html/changelogs/AutoChangeLog-pr-10704.yml
new file mode 100644
index 0000000000..1acd46b51f
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10704.yml
@@ -0,0 +1,4 @@
+author: "nemvar"
+delete-after: True
+changes:
+ - code_imp: "Slight changes the self-repair borg module. It no longer references the borg that owns it."
diff --git a/html/changelogs/AutoChangeLog-pr-10705.yml b/html/changelogs/AutoChangeLog-pr-10705.yml
new file mode 100644
index 0000000000..f1f025d616
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10705.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - refactor: "A good amount of the blood RGB rework was cleaned up/reverted, with some notable gameplay changes including: Gibs and blood not having max blood by default (no more easy rampages, cultists), infinite gib streaking, etc etc."
diff --git a/html/changelogs/AutoChangeLog-pr-10706.yml b/html/changelogs/AutoChangeLog-pr-10706.yml
new file mode 100644
index 0000000000..8bf1cd449c
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10706.yml
@@ -0,0 +1,5 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Stopped the ellipsis question mark from being displayed twice in the examine message for masked/unknown human mobs."
+ - tweak: "Made the aforementioned ellipsis question mark display on flavor-text-less masked/unknown human mobs too for consistency."
diff --git a/html/changelogs/AutoChangeLog-pr-10709.yml b/html/changelogs/AutoChangeLog-pr-10709.yml
new file mode 100644
index 0000000000..8c9d74c5f1
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10709.yml
@@ -0,0 +1,4 @@
+author: "Seris02"
+delete-after: True
+changes:
+ - rscadd: "recipe for mammal mutation toxin"
diff --git a/html/changelogs/AutoChangeLog-pr-10728.yml b/html/changelogs/AutoChangeLog-pr-10728.yml
new file mode 100644
index 0000000000..9023844565
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10728.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - balance: "meteor waves are now directional and announces the direction on the command report."
diff --git a/html/changelogs/AutoChangeLog-pr-10731.yml b/html/changelogs/AutoChangeLog-pr-10731.yml
new file mode 100644
index 0000000000..0da6f3c483
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10731.yml
@@ -0,0 +1,10 @@
+author: "necromanceranne"
+delete-after: True
+changes:
+ - rscadd: "Ebows now disarm people hit by them."
+ - rscadd: "Ebows now do 60 stamina damage on hit."
+ - rscdel: "Ebows no longer inflict drowsiness"
+ - balance: "Miniature ebows do 15 toxin damage, up from 8."
+ - balance: "Ebows have considerably shorter knockdowns."
+ - balance: "Ebows now make you slur rather than stutter."
+ - balance: "Large ebows are now heavy and bulky."
diff --git a/html/changelogs/AutoChangeLog-pr-10733.yml b/html/changelogs/AutoChangeLog-pr-10733.yml
new file mode 100644
index 0000000000..33e2ee216c
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10733.yml
@@ -0,0 +1,4 @@
+author: "Hatterhat"
+delete-after: True
+changes:
+ - balance: "Zombie powder is now instant when ingested, but delayed when injected or applied through touch."
diff --git a/html/changelogs/AutoChangeLog-pr-10737.yml b/html/changelogs/AutoChangeLog-pr-10737.yml
new file mode 100644
index 0000000000..fa4023c148
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10737.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Stopped an APTFT exploit with spray bottles."
diff --git a/html/changelogs/AutoChangeLog-pr-10640.yml b/html/changelogs/AutoChangeLog-pr-10739.yml
similarity index 54%
rename from html/changelogs/AutoChangeLog-pr-10640.yml
rename to html/changelogs/AutoChangeLog-pr-10739.yml
index f51f892d8d..8212e71353 100644
--- a/html/changelogs/AutoChangeLog-pr-10640.yml
+++ b/html/changelogs/AutoChangeLog-pr-10739.yml
@@ -1,4 +1,4 @@
author: "r4d6"
delete-after: True
changes:
- - bugfix: "fixed a hole in Meta"
+ - rscadd: "Added a dwarf language"
diff --git a/html/changelogs/AutoChangeLog-pr-10742.yml b/html/changelogs/AutoChangeLog-pr-10742.yml
new file mode 100644
index 0000000000..ece862d112
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10742.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Fixed the detective revolver being quite risky to use."
diff --git a/html/changelogs/AutoChangeLog-pr-10743.yml b/html/changelogs/AutoChangeLog-pr-10743.yml
new file mode 100644
index 0000000000..d9775f7bc3
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10743.yml
@@ -0,0 +1,4 @@
+author: "necromanceranne"
+delete-after: True
+changes:
+ - rscdel: "You can no longer order spinfusors or their ammo from cargo."
diff --git a/html/changelogs/AutoChangeLog-pr-10503.yml b/html/changelogs/AutoChangeLog-pr-10744.yml
similarity index 57%
rename from html/changelogs/AutoChangeLog-pr-10503.yml
rename to html/changelogs/AutoChangeLog-pr-10744.yml
index b067aa6c55..168619226a 100644
--- a/html/changelogs/AutoChangeLog-pr-10503.yml
+++ b/html/changelogs/AutoChangeLog-pr-10744.yml
@@ -1,4 +1,4 @@
author: "r4d6"
delete-after: True
changes:
- - bugfix: "fixed SM's piping"
+ - rscadd: "Added more engines"
diff --git a/html/changelogs/AutoChangeLog-pr-10495.yml b/html/changelogs/AutoChangeLog-pr-10746.yml
similarity index 59%
rename from html/changelogs/AutoChangeLog-pr-10495.yml
rename to html/changelogs/AutoChangeLog-pr-10746.yml
index 02ffedf5fc..232c7a26b9 100644
--- a/html/changelogs/AutoChangeLog-pr-10495.yml
+++ b/html/changelogs/AutoChangeLog-pr-10746.yml
@@ -1,4 +1,4 @@
author: "kevinz000"
delete-after: True
changes:
- - rscadd: "guests can now looc"
+ - rscadd: "CTF CLAYMORES"
diff --git a/html/changelogs/AutoChangeLog-pr-10747.yml b/html/changelogs/AutoChangeLog-pr-10747.yml
new file mode 100644
index 0000000000..54ca25e12a
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10747.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - balance: "shoves buffed, now shoving into a wall twice rapidly will also disarm their weapon."
diff --git a/html/changelogs/AutoChangeLog-pr-10278.yml b/html/changelogs/AutoChangeLog-pr-10748.yml
similarity index 52%
rename from html/changelogs/AutoChangeLog-pr-10278.yml
rename to html/changelogs/AutoChangeLog-pr-10748.yml
index 76ada12f20..a2afda81aa 100644
--- a/html/changelogs/AutoChangeLog-pr-10278.yml
+++ b/html/changelogs/AutoChangeLog-pr-10748.yml
@@ -1,4 +1,4 @@
author: "Trilbyspaceclone"
delete-after: True
changes:
- - bugfix: "Fermi plushie can be made once again"
+ - bugfix: "Vault hallway door being all access"
diff --git a/html/changelogs/AutoChangeLog-pr-10749.yml b/html/changelogs/AutoChangeLog-pr-10749.yml
new file mode 100644
index 0000000000..a3ed5989d1
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10749.yml
@@ -0,0 +1,4 @@
+author: "AnturK ported by kevinz000"
+delete-after: True
+changes:
+ - rscadd: "Dueling pistols have been added."
diff --git a/html/changelogs/AutoChangeLog-pr-10751.yml b/html/changelogs/AutoChangeLog-pr-10751.yml
new file mode 100644
index 0000000000..c5b54bd4ed
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10751.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - balance: "traitor+bro gamemode minimum population set to 25 until there can be more in depth configuration systems for gamemodes."
diff --git a/html/changelogs/AutoChangeLog-pr-10752.yml b/html/changelogs/AutoChangeLog-pr-10752.yml
new file mode 100644
index 0000000000..9bb50f1da1
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10752.yml
@@ -0,0 +1,9 @@
+author: "Owai-Seek"
+delete-after: True
+changes:
+ - rscadd: "Burger, Cargo Packaging, Dirty Magazine, Air Pump, and Scrubber crates."
+ - rscdel: "Duplicate Crate, Festive Wrapping Paper Crate, Contraband Monkey Meat Crate"
+ - tweak: "Gave Seed Crate Ambrosia Seed, gave Biker Gang Crate Spraypaint."
+ - tweak: "Organised some crates with sub-categories. Also, moved all vendor refills to a new tab."
+ - tweak: "Moved Grill to Service Tab"
+ - bugfix: "Engineering Hardsuit Access"
diff --git a/html/changelogs/AutoChangeLog-pr-10442.yml b/html/changelogs/AutoChangeLog-pr-10753.yml
similarity index 56%
rename from html/changelogs/AutoChangeLog-pr-10442.yml
rename to html/changelogs/AutoChangeLog-pr-10753.yml
index f407ae9e15..c1b3615c3d 100644
--- a/html/changelogs/AutoChangeLog-pr-10442.yml
+++ b/html/changelogs/AutoChangeLog-pr-10753.yml
@@ -1,4 +1,4 @@
author: "Trilbyspaceclone"
delete-after: True
changes:
- - tweak: "Halfs the nutriments in sugar"
+ - server: "Updates change logs"
diff --git a/html/changelogs/AutoChangeLog-pr-10754.yml b/html/changelogs/AutoChangeLog-pr-10754.yml
new file mode 100644
index 0000000000..0537347443
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10754.yml
@@ -0,0 +1,5 @@
+author: "MalricB"
+delete-after: True
+changes:
+ - rscadd: "\"Shaggy\" sprite from virgo"
+ - rscadd: "\"shaggy\" option in the character customization"
diff --git a/html/changelogs/AutoChangeLog-pr-10761.yml b/html/changelogs/AutoChangeLog-pr-10761.yml
new file mode 100644
index 0000000000..5a5b7281ef
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10761.yml
@@ -0,0 +1,4 @@
+author: "Putnam3145"
+delete-after: True
+changes:
+ - bugfix: "gender change potion now respects prefs"
diff --git a/html/changelogs/AutoChangeLog-pr-10764.yml b/html/changelogs/AutoChangeLog-pr-10764.yml
new file mode 100644
index 0000000000..25dfebd5fb
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10764.yml
@@ -0,0 +1,4 @@
+author: "deathride58"
+delete-after: True
+changes:
+ - bugfix: "Spacemen no longer run at lightspeed on local servers."
diff --git a/html/changelogs/AutoChangeLog-pr-10765.yml b/html/changelogs/AutoChangeLog-pr-10765.yml
new file mode 100644
index 0000000000..975a3ac5c5
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10765.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - bugfix: "no more bluespace skittish locker diving,"
diff --git a/html/changelogs/AutoChangeLog-pr-10768.yml b/html/changelogs/AutoChangeLog-pr-10768.yml
new file mode 100644
index 0000000000..0a07505b5b
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10768.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - rscadd: "moths now have unique laughs and can *chitter."
diff --git a/html/changelogs/AutoChangeLog-pr-10614.yml b/html/changelogs/AutoChangeLog-pr-10773.yml
similarity index 53%
rename from html/changelogs/AutoChangeLog-pr-10614.yml
rename to html/changelogs/AutoChangeLog-pr-10773.yml
index 4c4d8bfdbd..64ab5ce124 100644
--- a/html/changelogs/AutoChangeLog-pr-10614.yml
+++ b/html/changelogs/AutoChangeLog-pr-10773.yml
@@ -1,4 +1,4 @@
author: "Putnam3145"
delete-after: True
changes:
- - config: "Added suicide to the config."
+ - bugfix: "Hypno prefs work better."
diff --git a/html/changelogs/AutoChangeLog-pr-10774.yml b/html/changelogs/AutoChangeLog-pr-10774.yml
new file mode 100644
index 0000000000..f596bdcddb
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10774.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - tweak: "nuke explosion is now full dev radius for anti lag purposes"
diff --git a/html/changelogs/AutoChangeLog-pr-10776.yml b/html/changelogs/AutoChangeLog-pr-10776.yml
new file mode 100644
index 0000000000..cd95cb49d4
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10776.yml
@@ -0,0 +1,4 @@
+author: "Hatterhat"
+delete-after: True
+changes:
+ - tweak: "The H.E.C.K. suit is now goliath tentacle resistant and probably better for acid resistance."
diff --git a/html/changelogs/AutoChangeLog-pr-10777.yml b/html/changelogs/AutoChangeLog-pr-10777.yml
new file mode 100644
index 0000000000..76ce4000d8
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10777.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - balance: "Gangs can now only tag with a gang uplink bought spraycan."
diff --git a/html/changelogs/AutoChangeLog-pr-10781.yml b/html/changelogs/AutoChangeLog-pr-10781.yml
new file mode 100644
index 0000000000..cd3474adca
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10781.yml
@@ -0,0 +1,5 @@
+author: "r4d6"
+delete-after: True
+changes:
+ - tweak: "RPD subcategories and preview icons reorganized."
+ - rscadd: "RPD now starts with painting turned off, hitting pipes with build and no paint will target the turf underneath instead. Bye bye turf pixelhunting."
diff --git a/html/changelogs/AutoChangeLog-pr-10782.yml b/html/changelogs/AutoChangeLog-pr-10782.yml
new file mode 100644
index 0000000000..f66e75a113
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10782.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - tweak: "dueling pistol accesses have been changed to be more accessible."
diff --git a/html/changelogs/AutoChangeLog-pr-10784.yml b/html/changelogs/AutoChangeLog-pr-10784.yml
new file mode 100644
index 0000000000..0e5cfc8740
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10784.yml
@@ -0,0 +1,4 @@
+author: "Seris02"
+delete-after: True
+changes:
+ - rscadd: "polychromic winter coat"
diff --git a/html/changelogs/AutoChangeLog-pr-10786.yml b/html/changelogs/AutoChangeLog-pr-10786.yml
new file mode 100644
index 0000000000..7e758122a2
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10786.yml
@@ -0,0 +1,4 @@
+author: "Commandersand"
+delete-after: True
+changes:
+ - tweak: "uplink centcomm suit doesn't have sensors"
diff --git a/html/changelogs/AutoChangeLog-pr-10309.yml b/html/changelogs/AutoChangeLog-pr-10787.yml
similarity index 60%
rename from html/changelogs/AutoChangeLog-pr-10309.yml
rename to html/changelogs/AutoChangeLog-pr-10787.yml
index a59ebff156..ee221b69f3 100644
--- a/html/changelogs/AutoChangeLog-pr-10309.yml
+++ b/html/changelogs/AutoChangeLog-pr-10787.yml
@@ -1,4 +1,4 @@
author: "Seris02"
delete-after: True
changes:
- - rscadd: "disabler sechuds"
+ - rscadd: "thief's gloves"
diff --git a/html/changelogs/AutoChangeLog-pr-10339.yml b/html/changelogs/AutoChangeLog-pr-10788.yml
similarity index 58%
rename from html/changelogs/AutoChangeLog-pr-10339.yml
rename to html/changelogs/AutoChangeLog-pr-10788.yml
index c313c4bf80..344f6764d8 100644
--- a/html/changelogs/AutoChangeLog-pr-10339.yml
+++ b/html/changelogs/AutoChangeLog-pr-10788.yml
@@ -1,4 +1,4 @@
author: "Seris02"
delete-after: True
changes:
- - rscadd: "telescopic IV drip"
+ - rscadd: "crushing magboots"
diff --git a/html/changelogs/AutoChangeLog-pr-10790.yml b/html/changelogs/AutoChangeLog-pr-10790.yml
new file mode 100644
index 0000000000..43f949ce16
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10790.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Hair styles and undergarments are yet again free from gender restrictions."
diff --git a/html/changelogs/AutoChangeLog-pr-10791.yml b/html/changelogs/AutoChangeLog-pr-10791.yml
new file mode 100644
index 0000000000..3d27f84022
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10791.yml
@@ -0,0 +1,6 @@
+author: "Arturlang"
+delete-after: True
+changes:
+ - rscadd: "Each night for bloodsuckers will last longer due to the sun dimming."
+ - tweak: "You cant level up for free, now you have to pay blood for power."
+ - balance: "Bloodsuckers are no longer as resistant to hard stuns and regenerate less stamina. and a lot of their cooldowns are a lot higher."
diff --git a/html/changelogs/AutoChangeLog-pr-10792.yml b/html/changelogs/AutoChangeLog-pr-10792.yml
new file mode 100644
index 0000000000..78c86a838a
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10792.yml
@@ -0,0 +1,4 @@
+author: "Naksu"
+delete-after: True
+changes:
+ - bugfix: "Odysseus chem synthesizing now works again."
diff --git a/html/changelogs/AutoChangeLog-pr-10793.yml b/html/changelogs/AutoChangeLog-pr-10793.yml
new file mode 100644
index 0000000000..21cfc5d539
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10793.yml
@@ -0,0 +1,4 @@
+author: "Tupinambis"
+delete-after: True
+changes:
+ - bugfix: "masks no longer improperly stick out of helmets when they should be hidden."
diff --git a/html/changelogs/AutoChangeLog-pr-10795.yml b/html/changelogs/AutoChangeLog-pr-10795.yml
new file mode 100644
index 0000000000..b929aad0a5
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10795.yml
@@ -0,0 +1,4 @@
+author: "ShadeAware"
+delete-after: True
+changes:
+ - bugfix: "You can now actually craft Switchblades."
diff --git a/html/changelogs/AutoChangeLog-pr-10796.yml b/html/changelogs/AutoChangeLog-pr-10796.yml
new file mode 100644
index 0000000000..238fc3a8c7
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10796.yml
@@ -0,0 +1,4 @@
+author: "Tupinambis"
+delete-after: True
+changes:
+ - bugfix: "Status Displays should now update correctly."
diff --git a/html/changelogs/AutoChangeLog-pr-10798.yml b/html/changelogs/AutoChangeLog-pr-10798.yml
new file mode 100644
index 0000000000..637343c7b6
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10798.yml
@@ -0,0 +1,5 @@
+author: "necromanceranne"
+delete-after: True
+changes:
+ - rscdel: "Removed the formal security officer jacket from the secdrobe"
+ - tweak: "formal security jackets are now armor vests"
diff --git a/html/changelogs/AutoChangeLog-pr-10799.yml b/html/changelogs/AutoChangeLog-pr-10799.yml
new file mode 100644
index 0000000000..2d49bdc63f
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10799.yml
@@ -0,0 +1,4 @@
+author: "Seris02"
+delete-after: True
+changes:
+ - bugfix: "golden plastitanium toolbox being actually plastitanuium"
diff --git a/html/changelogs/AutoChangeLog-pr-10800.yml b/html/changelogs/AutoChangeLog-pr-10800.yml
new file mode 100644
index 0000000000..3486e47b1f
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10800.yml
@@ -0,0 +1,4 @@
+author: "Seris02"
+delete-after: True
+changes:
+ - bugfix: "character slot amount"
diff --git a/html/changelogs/AutoChangeLog-pr-10801.yml b/html/changelogs/AutoChangeLog-pr-10801.yml
new file mode 100644
index 0000000000..36db98e9e1
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10801.yml
@@ -0,0 +1,4 @@
+author: "Seris02"
+delete-after: True
+changes:
+ - balance: "rebalanced rising bass"
diff --git a/html/changelogs/AutoChangeLog-pr-10803.yml b/html/changelogs/AutoChangeLog-pr-10803.yml
new file mode 100644
index 0000000000..bc50fcc700
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10803.yml
@@ -0,0 +1,4 @@
+author: "Seris02"
+delete-after: True
+changes:
+ - bugfix: "string highlighting whitespace"
diff --git a/html/changelogs/AutoChangeLog-pr-10808.yml b/html/changelogs/AutoChangeLog-pr-10808.yml
new file mode 100644
index 0000000000..89e8880af8
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10808.yml
@@ -0,0 +1,5 @@
+author: "Arturlang"
+delete-after: True
+changes:
+ - rscdel: "Removed text macros from the chem dispenser."
+ - rscadd: "Replaced with dispenser input recording macros."
diff --git a/html/changelogs/AutoChangeLog-pr-10809.yml b/html/changelogs/AutoChangeLog-pr-10809.yml
new file mode 100644
index 0000000000..2a6c1dcdb8
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10809.yml
@@ -0,0 +1,4 @@
+author: "Arturlang"
+delete-after: True
+changes:
+ - bugfix: "Fancifying makeshift switchblades now works"
diff --git a/html/changelogs/AutoChangeLog-pr-10810.yml b/html/changelogs/AutoChangeLog-pr-10810.yml
new file mode 100644
index 0000000000..3d1693955a
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10810.yml
@@ -0,0 +1,5 @@
+author: "Yakumo Chen"
+delete-after: True
+changes:
+ - rscadd: "Rings for your fingers!"
+ - rscadd: "New cargo crates and loadout options to bling your rings!"
diff --git a/html/changelogs/AutoChangeLog-pr-10812.yml b/html/changelogs/AutoChangeLog-pr-10812.yml
new file mode 100644
index 0000000000..1189a9f8d2
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10812.yml
@@ -0,0 +1,5 @@
+author: "Tupinambis"
+delete-after: True
+changes:
+ - balance: "stunbatons now take 4 hits to inflict hard crit, up from 3."
+ - balance: "stunbatons no longer disarm targets."
diff --git a/html/changelogs/AutoChangeLog-pr-10816.yml b/html/changelogs/AutoChangeLog-pr-10816.yml
new file mode 100644
index 0000000000..9538f353ec
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10816.yml
@@ -0,0 +1,4 @@
+author: "ancientpower"
+delete-after: True
+changes:
+ - bugfix: "Fixes an error in pH strips' feedback message."
diff --git a/html/changelogs/AutoChangeLog-pr-10817.yml b/html/changelogs/AutoChangeLog-pr-10817.yml
new file mode 100644
index 0000000000..cf42f695ef
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10817.yml
@@ -0,0 +1,4 @@
+author: "IronEleven"
+delete-after: True
+changes:
+ - balance: "Minor stat changes to Choking, Spontaneous Combustion, Autophagocytosis Necrosis, Hallucigen, Narcolepsy, Shivering, and Vomiting symptoms."
diff --git a/html/changelogs/AutoChangeLog-pr-10819.yml b/html/changelogs/AutoChangeLog-pr-10819.yml
new file mode 100644
index 0000000000..406da850b3
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10819.yml
@@ -0,0 +1,5 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - tweak: "Clown ops will find bombananas and clown bomb beacons instead of minibombs and bomb beacons in their infiltrator ship now."
+ - tweak: "For safety, the syndicate shuttle minibombs and bombananas come shipped in a box. Please don't instinctively eat any of those nanas, thank you."
diff --git a/html/changelogs/AutoChangeLog-pr-10820.yml b/html/changelogs/AutoChangeLog-pr-10820.yml
new file mode 100644
index 0000000000..7833ab7250
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10820.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - tweak: "Uplink items excluded (such as mulligan, chameleon, ebow) from or exclusive (such as cyber implants) to normal nuke ops will now be properly unavailable/available to clown ops."
diff --git a/html/changelogs/AutoChangeLog-pr-10345.yml b/html/changelogs/AutoChangeLog-pr-10821.yml
similarity index 52%
rename from html/changelogs/AutoChangeLog-pr-10345.yml
rename to html/changelogs/AutoChangeLog-pr-10821.yml
index a95653cdda..a1661764cf 100644
--- a/html/changelogs/AutoChangeLog-pr-10345.yml
+++ b/html/changelogs/AutoChangeLog-pr-10821.yml
@@ -1,4 +1,4 @@
-author: "Trilbyspaceclone"
+author: "ShadeAware"
delete-after: True
changes:
- - tweak: "allows bandoliers to hold any ammo type as long as it has a casing"
+ - bugfix: "Switchblades no longer regain their old Makeshift sprite after retracting if you've made them fancy with Silver."
diff --git a/html/changelogs/AutoChangeLog-pr-10829.yml b/html/changelogs/AutoChangeLog-pr-10829.yml
new file mode 100644
index 0000000000..9866f21fc7
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10829.yml
@@ -0,0 +1,7 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Fixed bananium energy sword/shield slips."
+ - balance: "Buffed said slips to ignore no-grav/crawling/flying as well as adding some deadly force to the e-sword."
+ - bugfix: "Fixed the 'stache grenade anti-non-clumsy user check."
+ - balance: "Doubled the timer for the sticky mustaches effect from the above grenade (from 1 to 2 minutes)"
diff --git a/html/changelogs/AutoChangeLog-pr-10830.yml b/html/changelogs/AutoChangeLog-pr-10830.yml
new file mode 100644
index 0000000000..0d7b5d56b3
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10830.yml
@@ -0,0 +1,4 @@
+author: "Seris02"
+delete-after: True
+changes:
+ - bugfix: "glitch with PKA"
diff --git a/html/changelogs/AutoChangeLog-pr-10831.yml b/html/changelogs/AutoChangeLog-pr-10831.yml
new file mode 100644
index 0000000000..477df49370
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10831.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Fixed an edge case with the chaplain armaments beacon spawning the box in nullspace."
diff --git a/html/changelogs/AutoChangeLog-pr-10833.yml b/html/changelogs/AutoChangeLog-pr-10833.yml
new file mode 100644
index 0000000000..d4b612f622
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10833.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - tweak: "The playback device's cooldown now proportionally increases with messages longer than 60 characters (3 seconds is the default assembly cooldown)."
diff --git a/html/changelogs/AutoChangeLog-pr-10834.yml b/html/changelogs/AutoChangeLog-pr-10834.yml
new file mode 100644
index 0000000000..1db0ee8988
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10834.yml
@@ -0,0 +1,4 @@
+author: "Trilbyspaceclone"
+delete-after: True
+changes:
+ - rscadd: "Crafts are now made of plasteel and can be made with 5 sheets"
diff --git a/html/changelogs/AutoChangeLog-pr-10836.yml b/html/changelogs/AutoChangeLog-pr-10836.yml
new file mode 100644
index 0000000000..d2c9328aa7
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10836.yml
@@ -0,0 +1,5 @@
+author: "Hatterhat"
+delete-after: True
+changes:
+ - rscadd: "The Engineering techfab can now print standard and large RCD compressed matter cartridges."
+ - rscadd: "The Experimental Tools node now has the Combifan projector, blocking both temperature and atmospheric changes."
diff --git a/html/changelogs/AutoChangeLog-pr-10837.yml b/html/changelogs/AutoChangeLog-pr-10837.yml
new file mode 100644
index 0000000000..72fee452c1
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10837.yml
@@ -0,0 +1,6 @@
+author: "Owai-Seek"
+delete-after: True
+changes:
+ - tweak: "Blood Crate now has all of the current blood types."
+ - tweak: "Birthday Cake Recipe is now the same as TG."
+ - tweak: "Added Soy Sauce and BBQ Packets to Dinnerware Vendor."
diff --git a/html/changelogs/AutoChangeLog-pr-10838.yml b/html/changelogs/AutoChangeLog-pr-10838.yml
new file mode 100644
index 0000000000..b6fbb864dd
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10838.yml
@@ -0,0 +1,5 @@
+author: "Owai-Seek"
+delete-after: True
+changes:
+ - tweak: "Added nurse outfit, nurse cap, and mailman hat to loadout."
+ - tweak: "Changed the name of scrubs to blue, green, and purple scrubs."
diff --git a/html/changelogs/AutoChangeLog-pr-10840.yml b/html/changelogs/AutoChangeLog-pr-10840.yml
new file mode 100644
index 0000000000..26c9ab71b6
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10840.yml
@@ -0,0 +1,4 @@
+author: "Putnam3145"
+delete-after: True
+changes:
+ - admin: "Panic bunker is now round-to-round persistent"
diff --git a/html/changelogs/AutoChangeLog-pr-10841.yml b/html/changelogs/AutoChangeLog-pr-10841.yml
new file mode 100644
index 0000000000..58e083fe85
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10841.yml
@@ -0,0 +1,4 @@
+author: "Kraseo"
+delete-after: True
+changes:
+ - bugfix: "You can no longer pull before wearing boxing gloves to bypass the grab check."
diff --git a/html/changelogs/AutoChangeLog-pr-10847.yml b/html/changelogs/AutoChangeLog-pr-10847.yml
new file mode 100644
index 0000000000..3e18420cb3
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10847.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Fixed space ninjas \"Nothing\" objective. Now you gotta steal some dandy stuff such as corgi meat and pinpointers as a ninja too."
diff --git a/html/changelogs/AutoChangeLog-pr-10850.yml b/html/changelogs/AutoChangeLog-pr-10850.yml
new file mode 100644
index 0000000000..5cfbd4ab1c
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10850.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Fixed maploaded APC terminals direction."
diff --git a/html/changelogs/AutoChangeLog-pr-10852.yml b/html/changelogs/AutoChangeLog-pr-10852.yml
new file mode 100644
index 0000000000..5d6edfba1b
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10852.yml
@@ -0,0 +1,4 @@
+author: "MrJWhit"
+delete-after: True
+changes:
+ - rscdel: "Removed meteor defense tech node"
diff --git a/html/changelogs/AutoChangeLog-pr-10854.yml b/html/changelogs/AutoChangeLog-pr-10854.yml
new file mode 100644
index 0000000000..b01532daf4
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10854.yml
@@ -0,0 +1,4 @@
+author: "Seris02"
+delete-after: True
+changes:
+ - bugfix: "meteor hallucinations (again)"
diff --git a/html/changelogs/AutoChangeLog-pr-10855.yml b/html/changelogs/AutoChangeLog-pr-10855.yml
new file mode 100644
index 0000000000..1920609508
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10855.yml
@@ -0,0 +1,4 @@
+author: "Trilbyspaceclone"
+delete-after: True
+changes:
+ - rscadd: "Takes away NT's connections to the Aliens that run the Syndicats"
diff --git a/html/changelogs/AutoChangeLog-pr-10857.yml b/html/changelogs/AutoChangeLog-pr-10857.yml
new file mode 100644
index 0000000000..ae8c5f6ac0
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10857.yml
@@ -0,0 +1,4 @@
+author: "Seris02"
+delete-after: True
+changes:
+ - rscadd: "Added naked hallucination"
diff --git a/html/changelogs/AutoChangeLog-pr-10860.yml b/html/changelogs/AutoChangeLog-pr-10860.yml
new file mode 100644
index 0000000000..5de723e73b
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10860.yml
@@ -0,0 +1,4 @@
+author: "Bhijn"
+delete-after: True
+changes:
+ - tweak: "Click-dragging will now only perform the quick item usage behavior if you're in combat mode."
diff --git a/html/changelogs/AutoChangeLog-pr-10861.yml b/html/changelogs/AutoChangeLog-pr-10861.yml
new file mode 100644
index 0000000000..5071cc30f9
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10861.yml
@@ -0,0 +1,4 @@
+author: "ancientpower"
+delete-after: True
+changes:
+ - rscadd: "Ported drink sliding from /vg/station."
diff --git a/html/changelogs/AutoChangeLog-pr-10862.yml b/html/changelogs/AutoChangeLog-pr-10862.yml
new file mode 100644
index 0000000000..0e08046d07
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10862.yml
@@ -0,0 +1,4 @@
+author: "Hatterhat"
+delete-after: True
+changes:
+ - tweak: "fiddles with the seed extractor upgrade examine to make it not shit"
diff --git a/html/changelogs/AutoChangeLog-pr-10865.yml b/html/changelogs/AutoChangeLog-pr-10865.yml
new file mode 100644
index 0000000000..a929bab8a1
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10865.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - balance: "Nerfed magnetic rifles/pistols by re(?)adding power cell requirements for it. These firearms must be recharged after firing 2 magazines worth of projectiles and are slightly more susceptible to EMPs."
diff --git a/html/changelogs/AutoChangeLog-pr-10866.yml b/html/changelogs/AutoChangeLog-pr-10866.yml
new file mode 100644
index 0000000000..f5f99207a5
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10866.yml
@@ -0,0 +1,4 @@
+author: "ancientpower"
+delete-after: True
+changes:
+ - bugfix: "Fixes color mismatching with the \"genitals use skintone\" preference."
diff --git a/html/changelogs/AutoChangeLog-pr-10867.yml b/html/changelogs/AutoChangeLog-pr-10867.yml
new file mode 100644
index 0000000000..3e3b1c460f
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10867.yml
@@ -0,0 +1,5 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Fixed the tearstache nade not properly working."
+ - balance: "Buffed it against space helmet internals."
diff --git a/html/changelogs/AutoChangeLog-pr-10868.yml b/html/changelogs/AutoChangeLog-pr-10868.yml
new file mode 100644
index 0000000000..629f93585a
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10868.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - tweak: "Holographic fans won't display above mobs and other objects anymore."
diff --git a/html/changelogs/AutoChangeLog-pr-10869.yml b/html/changelogs/AutoChangeLog-pr-10869.yml
new file mode 100644
index 0000000000..c8317e134f
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10869.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - bugfix: "mechs no longer have admin logs flooding into IC control console log viewing"
diff --git a/html/changelogs/AutoChangeLog-pr-10870.yml b/html/changelogs/AutoChangeLog-pr-10870.yml
new file mode 100644
index 0000000000..672e6abdbc
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10870.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Fixed paraplegics appearing as shoeless."
diff --git a/html/changelogs/AutoChangeLog-pr-10872.yml b/html/changelogs/AutoChangeLog-pr-10872.yml
new file mode 100644
index 0000000000..4a61daa9bb
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10872.yml
@@ -0,0 +1,4 @@
+author: "Arturlang"
+delete-after: True
+changes:
+ - balance: "Shields will no longer block lasers, and will break if they take too much damage."
diff --git a/html/changelogs/AutoChangeLog-pr-10463.yml b/html/changelogs/AutoChangeLog-pr-10873.yml
similarity index 50%
rename from html/changelogs/AutoChangeLog-pr-10463.yml
rename to html/changelogs/AutoChangeLog-pr-10873.yml
index 70e97975ce..0e9bca3f95 100644
--- a/html/changelogs/AutoChangeLog-pr-10463.yml
+++ b/html/changelogs/AutoChangeLog-pr-10873.yml
@@ -1,4 +1,4 @@
author: "Putnam3145"
delete-after: True
changes:
- - config: "Random engines are now weighted."
+ - tweak: "Relief valve now has a TGUI-next UI"
diff --git a/html/changelogs/AutoChangeLog-pr-10874.yml b/html/changelogs/AutoChangeLog-pr-10874.yml
new file mode 100644
index 0000000000..bb46b5240d
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10874.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Fixed the petting element."
diff --git a/html/changelogs/AutoChangeLog-pr-10875.yml b/html/changelogs/AutoChangeLog-pr-10875.yml
new file mode 100644
index 0000000000..f693bd7c8b
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10875.yml
@@ -0,0 +1,4 @@
+author: "ancientpower"
+delete-after: True
+changes:
+ - imageadd: "Bunny ear style for humanoid species."
diff --git a/html/changelogs/AutoChangeLog-pr-10876.yml b/html/changelogs/AutoChangeLog-pr-10876.yml
new file mode 100644
index 0000000000..9347a655cc
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10876.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Fixed slipping."
diff --git a/html/changelogs/AutoChangeLog-pr-10881.yml b/html/changelogs/AutoChangeLog-pr-10881.yml
new file mode 100644
index 0000000000..7c00713eae
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10881.yml
@@ -0,0 +1,6 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - refactor: "Refactored mob holders into an element."
+ - bugfix: "Fixed a few minor issues with that feature. such as mismatching sprites for certain held mobs and a slight lack of safety checks."
+ - rscdel: "holdable mobs can't force themselves into people's hands anymore."
diff --git a/html/changelogs/AutoChangeLog-pr-10883.yml b/html/changelogs/AutoChangeLog-pr-10883.yml
new file mode 100644
index 0000000000..f99613d846
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10883.yml
@@ -0,0 +1,4 @@
+author: "ancientpower"
+delete-after: True
+changes:
+ - tweak: "Ghosts are now literate."
diff --git a/html/changelogs/AutoChangeLog-pr-10885.yml b/html/changelogs/AutoChangeLog-pr-10885.yml
new file mode 100644
index 0000000000..bcc19eb9d2
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10885.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Milkies fix."
diff --git a/html/changelogs/AutoChangeLog-pr-10892.yml b/html/changelogs/AutoChangeLog-pr-10892.yml
new file mode 100644
index 0000000000..88f12ef378
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10892.yml
@@ -0,0 +1,5 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - refactor: "Guncode and energy guns have been refactored a bit."
+ - rscadd: "You can now combat mode ight click on an energy gun to attempt to switch firing modes. This only works on guns without right click functions overridden."
diff --git a/html/changelogs/AutoChangeLog-pr-10893.yml b/html/changelogs/AutoChangeLog-pr-10893.yml
new file mode 100644
index 0000000000..a0be6a5b6b
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10893.yml
@@ -0,0 +1,5 @@
+author: "Arturlang"
+delete-after: True
+changes:
+ - rscadd: "Added a TGUI Next interface for crafting"
+ - rscdel: "Removed the old TGUI slow crafting interface"
diff --git a/html/changelogs/AutoChangeLog-pr-10358.yml b/html/changelogs/AutoChangeLog-pr-10894.yml
similarity index 51%
rename from html/changelogs/AutoChangeLog-pr-10358.yml
rename to html/changelogs/AutoChangeLog-pr-10894.yml
index a578e6e336..770308755d 100644
--- a/html/changelogs/AutoChangeLog-pr-10358.yml
+++ b/html/changelogs/AutoChangeLog-pr-10894.yml
@@ -1,4 +1,4 @@
author: "Trilbyspaceclone"
delete-after: True
changes:
- - server: "Cleans out the last years of changlogs"
+ - rscadd: "Carps have evolved to take more damage"
diff --git a/html/changelogs/AutoChangeLog-pr-10898.yml b/html/changelogs/AutoChangeLog-pr-10898.yml
new file mode 100644
index 0000000000..7c7198105f
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10898.yml
@@ -0,0 +1,5 @@
+author: "EmeraldSundisk"
+delete-after: True
+changes:
+ - bugfix: "Connects mining's disposal unit on Delta."
+ - tweak: "Slight visual adjustments to the immediate affected area."
diff --git a/html/changelogs/AutoChangeLog-pr-10899.yml b/html/changelogs/AutoChangeLog-pr-10899.yml
new file mode 100644
index 0000000000..f1542b2b09
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10899.yml
@@ -0,0 +1,6 @@
+author: "Trilbyspaceclone"
+delete-after: True
+changes:
+ - rscadd: "restock crates for each vender"
+ - rscadd: "restock units for all station venders"
+ - bugfix: "Sec-vender missing icon"
diff --git a/html/changelogs/AutoChangeLog-pr-10900.yml b/html/changelogs/AutoChangeLog-pr-10900.yml
new file mode 100644
index 0000000000..0f79245c73
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10900.yml
@@ -0,0 +1,4 @@
+author: "Putnam3145"
+delete-after: True
+changes:
+ - bugfix: "Atmos reaction priority works now."
diff --git a/html/changelogs/AutoChangeLog-pr-10901.yml b/html/changelogs/AutoChangeLog-pr-10901.yml
new file mode 100644
index 0000000000..57c1d821f7
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10901.yml
@@ -0,0 +1,5 @@
+author: "Trilbyspaceclone"
+delete-after: True
+changes:
+ - bugfix: "Box station captain office issues
+maping: Gulag on box can now be accessed"
diff --git a/html/changelogs/AutoChangeLog-pr-10902.yml b/html/changelogs/AutoChangeLog-pr-10902.yml
new file mode 100644
index 0000000000..9a60860f3f
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10902.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Fixed some mounted defibrillator issue with the altclicking functionality."
diff --git a/html/changelogs/AutoChangeLog-pr-10903.yml b/html/changelogs/AutoChangeLog-pr-10903.yml
new file mode 100644
index 0000000000..a353d6594a
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10903.yml
@@ -0,0 +1,5 @@
+author: "Hatterhat"
+delete-after: True
+changes:
+ - rscadd: "Formaldehyde prevents organ decay and corpses' miasma production at 1u in the body."
+ - rscadd: "Epinephrine pens now contain 3u formaldehyde. This should not kill you."
diff --git a/html/changelogs/AutoChangeLog-pr-10904.yml b/html/changelogs/AutoChangeLog-pr-10904.yml
new file mode 100644
index 0000000000..112c0bc593
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10904.yml
@@ -0,0 +1,4 @@
+author: "Hatterhat"
+delete-after: True
+changes:
+ - rscadd: "The ships often crashed by Free Golems on Lavaland now have GPSes. They're off, by default, but an awakening Golem could easily turn one on."
diff --git a/html/changelogs/AutoChangeLog-pr-10905.yml b/html/changelogs/AutoChangeLog-pr-10905.yml
new file mode 100644
index 0000000000..de9374bd12
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10905.yml
@@ -0,0 +1,6 @@
+author: "Hatterhat"
+delete-after: True
+changes:
+ - tweak: "Standard RCD ammo can now be printed from Engineering-keyed techfabs once you hit Industrial Engineering."
+ - tweak: "Consoleless interfaces are now default - this means unrestricted protolathes and circuit imprinters can now be interfaced with by interacting with the machine itself."
+ - bugfix: "Multitools can now actually be printed from Engineering and Science protolathes/techfabs once you unlock Basic Tools."
diff --git a/html/changelogs/AutoChangeLog-pr-10906.yml b/html/changelogs/AutoChangeLog-pr-10906.yml
new file mode 100644
index 0000000000..ef725e921e
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10906.yml
@@ -0,0 +1,5 @@
+author: "Hatterhat"
+delete-after: True
+changes:
+ - rscadd: "Husking (from being burned to shit) can now be reverted! 5u rezadone or 100u synthflesh, at below 50 burn."
+ - balance: "Turning a body into a burnt husk now takes more effort. 300 burn's worth of effort."
diff --git a/html/changelogs/AutoChangeLog-pr-10907.yml b/html/changelogs/AutoChangeLog-pr-10907.yml
new file mode 100644
index 0000000000..af893e9415
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10907.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Fixed no-grav lavaland labor/mining shuttle areas."
diff --git a/html/changelogs/AutoChangeLog-pr-10910.yml b/html/changelogs/AutoChangeLog-pr-10910.yml
new file mode 100644
index 0000000000..2b11650c80
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10910.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Fixed a little issue with sleeper UI and blood types."
diff --git a/html/changelogs/AutoChangeLog-pr-10913.yml b/html/changelogs/AutoChangeLog-pr-10913.yml
new file mode 100644
index 0000000000..8bf549a154
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10913.yml
@@ -0,0 +1,4 @@
+author: "Feasel"
+delete-after: True
+changes:
+ - balance: "Buffed dissection success chances."
diff --git a/html/changelogs/AutoChangeLog-pr-10915.yml b/html/changelogs/AutoChangeLog-pr-10915.yml
new file mode 100644
index 0000000000..88f0c1652e
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10915.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - balance: "shoes can now fit magpistols again."
diff --git a/html/changelogs/AutoChangeLog-pr-10916.yml b/html/changelogs/AutoChangeLog-pr-10916.yml
new file mode 100644
index 0000000000..05d9b007b1
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10916.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - balance: "Projectiles now always hit chest if targeting chest, snipers have 100% targeting zone accuracy. All other cases are unchanged."
diff --git a/html/changelogs/AutoChangeLog-pr-10306.yml b/html/changelogs/AutoChangeLog-pr-10917.yml
similarity index 52%
rename from html/changelogs/AutoChangeLog-pr-10306.yml
rename to html/changelogs/AutoChangeLog-pr-10917.yml
index 9ab82dbda0..f6e5997348 100644
--- a/html/changelogs/AutoChangeLog-pr-10306.yml
+++ b/html/changelogs/AutoChangeLog-pr-10917.yml
@@ -1,4 +1,4 @@
author: "Arturlang"
delete-after: True
changes:
- - tweak: "Vampires are no longer as tanky"
+ - tweak: "The MK2 hypospray now"
diff --git a/html/changelogs/AutoChangeLog-pr-10918.yml b/html/changelogs/AutoChangeLog-pr-10918.yml
new file mode 100644
index 0000000000..bf47d0accf
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10918.yml
@@ -0,0 +1,4 @@
+author: "r4d6"
+delete-after: True
+changes:
+ - config: "Made dwarves into a roundstart races"
diff --git a/html/changelogs/AutoChangeLog-pr-10919.yml b/html/changelogs/AutoChangeLog-pr-10919.yml
new file mode 100644
index 0000000000..0fa6420e39
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10919.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Fixing accidental nerfs to the magpistol magazine."
diff --git a/html/changelogs/AutoChangeLog-pr-10921.yml b/html/changelogs/AutoChangeLog-pr-10921.yml
new file mode 100644
index 0000000000..d6d551b40d
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10921.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "The Lavaland's Herald speech sound should only play if they are actually speaking."
diff --git a/html/changelogs/AutoChangeLog-pr-10922.yml b/html/changelogs/AutoChangeLog-pr-10922.yml
new file mode 100644
index 0000000000..c71630fd0d
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10922.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - tweak: "The lawyer's PDA cartridge has been rebranded to something more accurate to its true nature."
diff --git a/html/changelogs/AutoChangeLog-pr-10923.yml b/html/changelogs/AutoChangeLog-pr-10923.yml
new file mode 100644
index 0000000000..0fd9a0f14e
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10923.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Nerfed cargo passive points generation from 500 to 125 creds per minute."
diff --git a/html/changelogs/AutoChangeLog-pr-10924.yml b/html/changelogs/AutoChangeLog-pr-10924.yml
new file mode 100644
index 0000000000..d419928dd2
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10924.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Fixed whitelisted/donor loadouts"
diff --git a/html/changelogs/AutoChangeLog-pr-10926.yml b/html/changelogs/AutoChangeLog-pr-10926.yml
new file mode 100644
index 0000000000..9662402d1b
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10926.yml
@@ -0,0 +1,9 @@
+author: "keronshb"
+delete-after: True
+changes:
+ - rscadd: "Added repairable turrets"
+ - rscadd: "Adds Tiny Fans to the pirate ship"
+ - tweak: "changes some reinforced windows to plastinanium pirate windows"
+ - tweak: "made the pirate shuttle + turrets bomb resistant"
+ - tweak: "made most pirate machines + consoles indestructible"
+ - tweak: "increased pirate turret health"
diff --git a/html/changelogs/AutoChangeLog-pr-10929.yml b/html/changelogs/AutoChangeLog-pr-10929.yml
new file mode 100644
index 0000000000..fa9413f25a
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10929.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Childproofs double-esword and hypereuplastic blade toys."
diff --git a/html/changelogs/AutoChangeLog-pr-10930.yml b/html/changelogs/AutoChangeLog-pr-10930.yml
new file mode 100644
index 0000000000..4121a00b9b
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10930.yml
@@ -0,0 +1,4 @@
+author: "r4d6"
+delete-after: True
+changes:
+ - tweak: "Meteor Timer back to 3-5 minutes"
diff --git a/html/changelogs/AutoChangeLog-pr-10430.yml b/html/changelogs/AutoChangeLog-pr-10934.yml
similarity index 52%
rename from html/changelogs/AutoChangeLog-pr-10430.yml
rename to html/changelogs/AutoChangeLog-pr-10934.yml
index 4fd03a5b2b..61ef7ec04f 100644
--- a/html/changelogs/AutoChangeLog-pr-10430.yml
+++ b/html/changelogs/AutoChangeLog-pr-10934.yml
@@ -1,4 +1,4 @@
author: "kevinz000"
delete-after: True
changes:
- - balance: "nuclear fist buffed."
+ - refactor: "Refactored ghostreading/etc"
diff --git a/html/changelogs/AutoChangeLog-pr-10935.yml b/html/changelogs/AutoChangeLog-pr-10935.yml
new file mode 100644
index 0000000000..f35855854f
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10935.yml
@@ -0,0 +1,5 @@
+author: "KrabSpider"
+delete-after: True
+changes:
+ - imageadd: "The Van Dyke is no longer Fu Manchu."
+ - imagedel: "Gets rid of a Fu Manchu imitation."
diff --git a/html/changelogs/AutoChangeLog-pr-10936.yml b/html/changelogs/AutoChangeLog-pr-10936.yml
new file mode 100644
index 0000000000..2303f157ab
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10936.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - balance: "Cyborgization will now de-gang people, even gangheads."
diff --git a/html/changelogs/AutoChangeLog-pr-10937.yml b/html/changelogs/AutoChangeLog-pr-10937.yml
new file mode 100644
index 0000000000..1c56882ccc
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10937.yml
@@ -0,0 +1,4 @@
+author: "Arturlang"
+delete-after: True
+changes:
+ - admin: "The debug outfit is now kitted out for whatever debbuging needs."
diff --git a/html/changelogs/AutoChangeLog-pr-10940.yml b/html/changelogs/AutoChangeLog-pr-10940.yml
new file mode 100644
index 0000000000..c0bbfc371a
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10940.yml
@@ -0,0 +1,5 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - balance: "Some reagent holders (such as cigarettes, food) are not suitable for reagents export anymore, while unprocessed botany crops will only net 1/3 of the standard reagents values."
+ - bugfix: "Export scanners now include the reagents value in the price report."
diff --git a/html/changelogs/AutoChangeLog-pr-10580.yml b/html/changelogs/AutoChangeLog-pr-10942.yml
similarity index 52%
rename from html/changelogs/AutoChangeLog-pr-10580.yml
rename to html/changelogs/AutoChangeLog-pr-10942.yml
index 6cb625d7af..2a30ace3aa 100644
--- a/html/changelogs/AutoChangeLog-pr-10580.yml
+++ b/html/changelogs/AutoChangeLog-pr-10942.yml
@@ -1,4 +1,4 @@
author: "Putnam3145"
delete-after: True
changes:
- - admin: "Crafting is logged in a file now"
+ - rscadd: "map voting doesn't suck anymore"
diff --git a/html/changelogs/AutoChangeLog-pr-10943.yml b/html/changelogs/AutoChangeLog-pr-10943.yml
new file mode 100644
index 0000000000..f45d8b1d39
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10943.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Fixed a little inconvenience with the character setup preview dummy having extra unwarranted bits."
diff --git a/html/changelogs/AutoChangeLog-pr-10947.yml b/html/changelogs/AutoChangeLog-pr-10947.yml
new file mode 100644
index 0000000000..a6f1fb1344
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10947.yml
@@ -0,0 +1,4 @@
+author: "nemvar"
+delete-after: True
+changes:
+ - bugfix: "Trash from food now gets generated at the location of the food item, instead of in the hands of the eater."
diff --git a/html/changelogs/AutoChangeLog-pr-10948.yml b/html/changelogs/AutoChangeLog-pr-10948.yml
new file mode 100644
index 0000000000..f9da9463fc
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10948.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Stopped role restricted uplink items from being discounted despite not being purchasable most times."
diff --git a/html/changelogs/AutoChangeLog-pr-10949.yml b/html/changelogs/AutoChangeLog-pr-10949.yml
new file mode 100644
index 0000000000..3c7000a0e1
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10949.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - bugfix: "reactive repulse armor now has an item limit"
diff --git a/html/changelogs/AutoChangeLog-pr-10950.yml b/html/changelogs/AutoChangeLog-pr-10950.yml
new file mode 100644
index 0000000000..8258db1d69
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10950.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - bugfix: "beam rifle runtime fix during aiming_beam"
diff --git a/html/changelogs/AutoChangeLog-pr-10951.yml b/html/changelogs/AutoChangeLog-pr-10951.yml
new file mode 100644
index 0000000000..20bc444967
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10951.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - bugfix: "fail2topic runtime fix: taking out an ip while incrementing index resulted in accessing the next-next entry instead of the next and results in out of bound errors."
diff --git a/html/changelogs/AutoChangeLog-pr-10953.yml b/html/changelogs/AutoChangeLog-pr-10953.yml
new file mode 100644
index 0000000000..67c2c915ad
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10953.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Missing words replacement file for the gondola mask."
diff --git a/html/changelogs/AutoChangeLog-pr-10955.yml b/html/changelogs/AutoChangeLog-pr-10955.yml
new file mode 100644
index 0000000000..46ff62efaf
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10955.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - rscadd: "gravity gun repulse and chaos now work in all angles, rather than just basic cardinals and diagonals. fun."
diff --git a/html/changelogs/AutoChangeLog-pr-10957.yml b/html/changelogs/AutoChangeLog-pr-10957.yml
new file mode 100644
index 0000000000..b655b85fb9
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10957.yml
@@ -0,0 +1,4 @@
+author: "Putnam3145"
+delete-after: True
+changes:
+ - bugfix: "Dynamic now defaults to \"classic\" storyteller instead of just failing if the vote didn't choose a storyteller."
diff --git a/html/changelogs/AutoChangeLog-pr-10960.yml b/html/changelogs/AutoChangeLog-pr-10960.yml
new file mode 100644
index 0000000000..6033533590
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10960.yml
@@ -0,0 +1,4 @@
+author: "kappa-sama"
+delete-after: True
+changes:
+ - bugfix: "the plant dudes can actually make plant disks now"
diff --git a/html/changelogs/AutoChangeLog-pr-10961.yml b/html/changelogs/AutoChangeLog-pr-10961.yml
new file mode 100644
index 0000000000..eaaafab472
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10961.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Fixed an issue with the nearsighted prescription glasses taking over worn eyewear."
diff --git a/html/changelogs/AutoChangeLog-pr-10966.yml b/html/changelogs/AutoChangeLog-pr-10966.yml
new file mode 100644
index 0000000000..bdc78a2b01
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10966.yml
@@ -0,0 +1,4 @@
+author: "r4d6"
+delete-after: True
+changes:
+ - bugfix: "Mining no longer lead to spess"
diff --git a/html/changelogs/AutoChangeLog-pr-10967.yml b/html/changelogs/AutoChangeLog-pr-10967.yml
new file mode 100644
index 0000000000..f242dd57bc
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10967.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Fixed AI unanchoring not properly removing the no teleport trait. My bad."
diff --git a/html/changelogs/AutoChangeLog-pr-10969.yml b/html/changelogs/AutoChangeLog-pr-10969.yml
new file mode 100644
index 0000000000..fdd53cdd2f
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10969.yml
@@ -0,0 +1,4 @@
+author: "Seris02"
+delete-after: True
+changes:
+ - rscadd: "crowbarring manifests off crates"
diff --git a/html/changelogs/AutoChangeLog-pr-10970.yml b/html/changelogs/AutoChangeLog-pr-10970.yml
new file mode 100644
index 0000000000..7182c5f6a6
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10970.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - balance: "Public autolathes can no longer be hacked."
diff --git a/html/changelogs/AutoChangeLog-pr-10432.yml b/html/changelogs/AutoChangeLog-pr-10971.yml
similarity index 53%
rename from html/changelogs/AutoChangeLog-pr-10432.yml
rename to html/changelogs/AutoChangeLog-pr-10971.yml
index a0b40baa45..e7882646f8 100644
--- a/html/changelogs/AutoChangeLog-pr-10432.yml
+++ b/html/changelogs/AutoChangeLog-pr-10971.yml
@@ -1,4 +1,4 @@
author: "Trilbyspaceclone"
delete-after: True
changes:
- - bugfix: "missing sprites with crushed cans"
+ - rscadd: "Alien stools and chairs"
diff --git a/html/changelogs/AutoChangeLog-pr-10972.yml b/html/changelogs/AutoChangeLog-pr-10972.yml
new file mode 100644
index 0000000000..9cc2e64687
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10972.yml
@@ -0,0 +1,4 @@
+author: "Trilbyspaceclone"
+delete-after: True
+changes:
+ - tweak: "Bone armor/Dagger are now crafting from bones rather then crafting menu"
diff --git a/html/changelogs/AutoChangeLog-pr-10973.yml b/html/changelogs/AutoChangeLog-pr-10973.yml
new file mode 100644
index 0000000000..8946cddb43
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10973.yml
@@ -0,0 +1,4 @@
+author: "Trilbyspaceclone"
+delete-after: True
+changes:
+ - rscadd: "Firing pins that only works when not on station"
diff --git a/html/changelogs/AutoChangeLog-pr-10974.yml b/html/changelogs/AutoChangeLog-pr-10974.yml
new file mode 100644
index 0000000000..7f668256b3
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10974.yml
@@ -0,0 +1,4 @@
+author: "Putnam3145"
+delete-after: True
+changes:
+ - bugfix: "antag quirk blacklisting works now"
diff --git a/html/changelogs/AutoChangeLog-pr-10976.yml b/html/changelogs/AutoChangeLog-pr-10976.yml
new file mode 100644
index 0000000000..34ef082829
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10976.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Fixed another issue with hering comsig."
diff --git a/html/changelogs/AutoChangeLog-pr-10980.yml b/html/changelogs/AutoChangeLog-pr-10980.yml
new file mode 100644
index 0000000000..3701aea6e1
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10980.yml
@@ -0,0 +1,7 @@
+author: "Trilbyspaceclone"
+delete-after: True
+changes:
+ - rscadd: "Medical locked crates"
+ - balance: "Russian gear crates have less gear in some cases but all will cost more"
+ - bugfix: "Medical locked crates that were not locked"
+ - spellcheck: "Crates that were out of date are corrected"
diff --git a/html/changelogs/AutoChangeLog-pr-10981.yml b/html/changelogs/AutoChangeLog-pr-10981.yml
new file mode 100644
index 0000000000..df0d7990cb
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10981.yml
@@ -0,0 +1,4 @@
+author: "IHOPMommyLich"
+delete-after: True
+changes:
+ - tweak: "Changed the multiplicative_slowdown of Stimulants from -1 to -0.5"
diff --git a/html/changelogs/AutoChangeLog-pr-10984.yml b/html/changelogs/AutoChangeLog-pr-10984.yml
new file mode 100644
index 0000000000..137536b430
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10984.yml
@@ -0,0 +1,4 @@
+author: "Trilbyspaceclone"
+delete-after: True
+changes:
+ - balance: "Heads of staff have been better screened by NT before being promoted"
diff --git a/html/changelogs/AutoChangeLog-pr-10987.yml b/html/changelogs/AutoChangeLog-pr-10987.yml
new file mode 100644
index 0000000000..abea439a86
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10987.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - imageadd: "Fixed dozen missing privates sprites."
diff --git a/html/changelogs/AutoChangeLog-pr-10988.yml b/html/changelogs/AutoChangeLog-pr-10988.yml
new file mode 100644
index 0000000000..ebd9de7689
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10988.yml
@@ -0,0 +1,4 @@
+author: "YakumoChen"
+delete-after: True
+changes:
+ - tweak: "Observe is back in the OOC tab"
diff --git a/html/changelogs/AutoChangeLog-pr-10989.yml b/html/changelogs/AutoChangeLog-pr-10989.yml
new file mode 100644
index 0000000000..0a5b2ad3c8
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10989.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "A little fix concerning some R&D and reagents."
diff --git a/html/changelogs/AutoChangeLog-pr-10992.yml b/html/changelogs/AutoChangeLog-pr-10992.yml
new file mode 100644
index 0000000000..1d6d10396b
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10992.yml
@@ -0,0 +1,4 @@
+author: "Hatterhat"
+delete-after: True
+changes:
+ - balance: "Buckshot individual pellet damage up from 10 to 12.5. Still firing 6 pellets."
diff --git a/html/changelogs/AutoChangeLog-pr-10994.yml b/html/changelogs/AutoChangeLog-pr-10994.yml
new file mode 100644
index 0000000000..c27a1afc00
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10994.yml
@@ -0,0 +1,4 @@
+author: "DeltaFire15"
+delete-after: True
+changes:
+ - balance: "Nar'Sie runes no longer benefit from runed floors."
diff --git a/html/changelogs/AutoChangeLog-pr-10996.yml b/html/changelogs/AutoChangeLog-pr-10996.yml
new file mode 100644
index 0000000000..8bb7646cd6
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10996.yml
@@ -0,0 +1,6 @@
+author: "PersianXerxes"
+delete-after: True
+changes:
+ - rscadd: "SMES and PACMAN attached to gulag Security to power electrified windows
+remove: Removed Sec vendor from gulag Security"
+ - tweak: "Separation of gulag and public mining"
diff --git a/html/changelogs/AutoChangeLog-pr-10997.yml b/html/changelogs/AutoChangeLog-pr-10997.yml
new file mode 100644
index 0000000000..a380a04f68
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-10997.yml
@@ -0,0 +1,12 @@
+author: "Owai-Seek"
+delete-after: True
+changes:
+ - rscadd: "Bacon and Eggs food item. Delicious! Added a Lemony Poppy Seed Muffin."
+ - tweak: "Snack Vendor has Chocolates, Tortilla Chips, and a Marshmallow Box"
+ - balance: "Mops can now be printed at service lathe once tools are researched."
+ - balance: "Biobags can hold most organic limbs/organs, but not brains, implants, or cybernetics."
+ - balance: "Trash Bag can now hold limbs (but not heads.)"
+ - balance: "Most snack vendor items reduced by 1."
+ - bugfix: "Trash Cans are now actually craftable, and only require metal instead of plastic."
+ - imageadd: "Bacon and Eggs, Drying Agent Bottle."
+ - imageadd: "Mugs now show reagent colors of contained reagents. Thanks Seris!"
diff --git a/html/changelogs/AutoChangeLog-pr-11000.yml b/html/changelogs/AutoChangeLog-pr-11000.yml
new file mode 100644
index 0000000000..5e496af81f
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11000.yml
@@ -0,0 +1,4 @@
+author: "Seris02"
+delete-after: True
+changes:
+ - balance: "makes RCDs cost a better amount"
diff --git a/html/changelogs/AutoChangeLog-pr-11001.yml b/html/changelogs/AutoChangeLog-pr-11001.yml
new file mode 100644
index 0000000000..1cfa8977b4
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11001.yml
@@ -0,0 +1,4 @@
+author: "Arturlang"
+delete-after: True
+changes:
+ - bugfix: "Fixed the pandemic naming and the radiation healing symptom UI crashing"
diff --git a/html/changelogs/AutoChangeLog-pr-11003.yml b/html/changelogs/AutoChangeLog-pr-11003.yml
new file mode 100644
index 0000000000..4c551c4d09
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11003.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - bugfix: "Energy weapons now once again have their lens in contents."
diff --git a/html/changelogs/AutoChangeLog-pr-11005.yml b/html/changelogs/AutoChangeLog-pr-11005.yml
new file mode 100644
index 0000000000..d0576c7ba8
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11005.yml
@@ -0,0 +1,4 @@
+author: "Arturlang"
+delete-after: True
+changes:
+ - tweak: "Hyposprays now switch modes on CTRL click, instead of alt, as it was already reserved for dispension amount."
diff --git a/html/changelogs/AutoChangeLog-pr-11011.yml b/html/changelogs/AutoChangeLog-pr-11011.yml
new file mode 100644
index 0000000000..d0a7b916f7
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11011.yml
@@ -0,0 +1,4 @@
+author: "Tlaltecuhtli, ported by Hatterhat"
+delete-after: True
+changes:
+ - balance: "Ripley, Firefighter, Odysseus and H.O.N.K. mechs now also use scanning modules and capacitors on construction. This means that they also gain reduced power consumption and EMP protection from higher-tier stock parts."
diff --git a/html/changelogs/AutoChangeLog-pr-11012.yml b/html/changelogs/AutoChangeLog-pr-11012.yml
new file mode 100644
index 0000000000..1d233f0692
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11012.yml
@@ -0,0 +1,4 @@
+author: "keronshb"
+delete-after: True
+changes:
+ - rscadd: "Adds the CogChamp drink and Sprite"
diff --git a/html/changelogs/AutoChangeLog-pr-11013.yml b/html/changelogs/AutoChangeLog-pr-11013.yml
new file mode 100644
index 0000000000..a9806d796d
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11013.yml
@@ -0,0 +1,5 @@
+author: "r4d6"
+delete-after: True
+changes:
+ - rscadd: "Added Plasteel Pickaxe"
+ - rscadd: "Added Titanium Pickaxe"
diff --git a/html/changelogs/AutoChangeLog-pr-11018.yml b/html/changelogs/AutoChangeLog-pr-11018.yml
new file mode 100644
index 0000000000..2795317861
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11018.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - bugfix: "pais are no longer muted for literally a whole hour on emp."
diff --git a/html/changelogs/AutoChangeLog-pr-11020.yml b/html/changelogs/AutoChangeLog-pr-11020.yml
new file mode 100644
index 0000000000..52677b98e9
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11020.yml
@@ -0,0 +1,4 @@
+author: "Zellular"
+delete-after: True
+changes:
+ - imageadd: "Movement state for pupdozer and its decals"
diff --git a/html/changelogs/AutoChangeLog-pr-10450.yml b/html/changelogs/AutoChangeLog-pr-11022.yml
similarity index 51%
rename from html/changelogs/AutoChangeLog-pr-10450.yml
rename to html/changelogs/AutoChangeLog-pr-11022.yml
index d2c5d8b40a..ac317e56b0 100644
--- a/html/changelogs/AutoChangeLog-pr-10450.yml
+++ b/html/changelogs/AutoChangeLog-pr-11022.yml
@@ -1,4 +1,4 @@
author: "kevinz000"
delete-after: True
changes:
- - balance: "emitters are now hitscan"
+ - rscadd: "auto profiler subsystem from tg"
diff --git a/html/changelogs/AutoChangeLog-pr-11024.yml b/html/changelogs/AutoChangeLog-pr-11024.yml
new file mode 100644
index 0000000000..3af136f15e
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11024.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - bugfix: "Brig cells now are more accurate by using timeofday instead of realtime"
diff --git a/html/changelogs/AutoChangeLog-pr-11025.yml b/html/changelogs/AutoChangeLog-pr-11025.yml
new file mode 100644
index 0000000000..af9ac20f62
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11025.yml
@@ -0,0 +1,5 @@
+author: "Arturlang"
+delete-after: True
+changes:
+ - rscadd: "Nanite interfaces have gotten a rework to TGUI Next, and can now support sub-programs.
+add:Adds the nanite sting program, which will allow you to manually sting people to give them nanites, changeling style."
diff --git a/html/changelogs/AutoChangeLog-pr-11026.yml b/html/changelogs/AutoChangeLog-pr-11026.yml
new file mode 100644
index 0000000000..c99aad2d14
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11026.yml
@@ -0,0 +1,5 @@
+author: "Trilbyspaceclone"
+delete-after: True
+changes:
+ - balance: "The suit full of spiders also known as a ninja now is a tactical turtleneck"
+ - balance: "The Wiznerds now dont have suits set to max"
diff --git a/html/changelogs/AutoChangeLog-pr-11027.yml b/html/changelogs/AutoChangeLog-pr-11027.yml
new file mode 100644
index 0000000000..4e5f5b46ba
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11027.yml
@@ -0,0 +1,5 @@
+author: "ShadeAware"
+delete-after: True
+changes:
+ - rscadd: "The Captain's Wardrobe, a special one-of-a-kind and ID-locked wardrobe for the Captain that holds all of their snowflakey gear.
+Remove: Snowflake gear from the Captain's locker, since now it has its own vendor."
diff --git a/html/changelogs/AutoChangeLog-pr-10317.yml b/html/changelogs/AutoChangeLog-pr-11028.yml
similarity index 53%
rename from html/changelogs/AutoChangeLog-pr-10317.yml
rename to html/changelogs/AutoChangeLog-pr-11028.yml
index 7db50240e3..de94b1d35f 100644
--- a/html/changelogs/AutoChangeLog-pr-10317.yml
+++ b/html/changelogs/AutoChangeLog-pr-11028.yml
@@ -1,4 +1,4 @@
author: "Trilbyspaceclone"
delete-after: True
changes:
- - bugfix: "issues with mapping done my Trilby"
+ - tweak: "Meat wheat no longer has blood"
diff --git a/html/changelogs/AutoChangeLog-pr-11029.yml b/html/changelogs/AutoChangeLog-pr-11029.yml
new file mode 100644
index 0000000000..17f34bbfa6
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11029.yml
@@ -0,0 +1,4 @@
+author: "Putnam3145"
+delete-after: True
+changes:
+ - tweak: "default should be... default"
diff --git a/html/changelogs/AutoChangeLog-pr-11032.yml b/html/changelogs/AutoChangeLog-pr-11032.yml
new file mode 100644
index 0000000000..6d026e444a
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11032.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - bugfix: "Cryo now actually transfers reagents at tier 4 on enter rather than every 80 machine fire()-process()s regardless of if it was \"reset\" by open/close."
diff --git a/html/changelogs/AutoChangeLog-pr-11033.yml b/html/changelogs/AutoChangeLog-pr-11033.yml
new file mode 100644
index 0000000000..c5b9a2f65a
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11033.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - rscadd: "cmo now gets advanced surgery drapes that techweb-sync for advanced surgeries without an operating console."
diff --git a/html/changelogs/AutoChangeLog-pr-11035.yml b/html/changelogs/AutoChangeLog-pr-11035.yml
new file mode 100644
index 0000000000..94a57db904
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11035.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - bugfix: "ctf claymores now actually spawn (and fit)."
diff --git a/html/changelogs/AutoChangeLog-pr-11038.yml b/html/changelogs/AutoChangeLog-pr-11038.yml
new file mode 100644
index 0000000000..b157c31af8
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11038.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - bugfix: "STOP_PROCESSING now removes from currentrun to prevent another process cycle from happening where it shouldn't."
diff --git a/html/changelogs/AutoChangeLog-pr-11040.yml b/html/changelogs/AutoChangeLog-pr-11040.yml
new file mode 100644
index 0000000000..cc0514517b
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11040.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Further mob holder fixes."
diff --git a/html/changelogs/AutoChangeLog-pr-11042.yml b/html/changelogs/AutoChangeLog-pr-11042.yml
new file mode 100644
index 0000000000..4238f76b75
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11042.yml
@@ -0,0 +1,4 @@
+author: "Arturlang"
+delete-after: True
+changes:
+ - balance: "The pandemic can no longer make vaccines or synthblood as quickly."
diff --git a/html/changelogs/AutoChangeLog-pr-10307.yml b/html/changelogs/AutoChangeLog-pr-11043.yml
similarity index 64%
rename from html/changelogs/AutoChangeLog-pr-10307.yml
rename to html/changelogs/AutoChangeLog-pr-11043.yml
index 279dbe2840..28217af630 100644
--- a/html/changelogs/AutoChangeLog-pr-10307.yml
+++ b/html/changelogs/AutoChangeLog-pr-11043.yml
@@ -1,4 +1,4 @@
author: "Seris02"
delete-after: True
changes:
- - rscadd: "stunglasses"
+ - bugfix: "apc icons"
diff --git a/html/changelogs/AutoChangeLog-pr-11044.yml b/html/changelogs/AutoChangeLog-pr-11044.yml
new file mode 100644
index 0000000000..7dec153778
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11044.yml
@@ -0,0 +1,4 @@
+author: "Arturlang"
+delete-after: True
+changes:
+ - rscadd: "The syndicate uplinks interface has been redone in TGUI Next"
diff --git a/html/changelogs/AutoChangeLog-pr-11045.yml b/html/changelogs/AutoChangeLog-pr-11045.yml
new file mode 100644
index 0000000000..9e3e0a9ee4
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11045.yml
@@ -0,0 +1,4 @@
+author: "Trilbyspaceclone"
+delete-after: True
+changes:
+ - bugfix: "Flat guns can no longer be suppressed"
diff --git a/html/changelogs/AutoChangeLog-pr-11046.yml b/html/changelogs/AutoChangeLog-pr-11046.yml
new file mode 100644
index 0000000000..c40286e901
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11046.yml
@@ -0,0 +1,4 @@
+author: "Trilbyspaceclone"
+delete-after: True
+changes:
+ - tweak: "replaced stickmans .45 caliber with 10mm, for consistency."
diff --git a/html/changelogs/AutoChangeLog-pr-11047.yml b/html/changelogs/AutoChangeLog-pr-11047.yml
new file mode 100644
index 0000000000..5bc42d1219
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11047.yml
@@ -0,0 +1,5 @@
+author: "Trilbyspaceclone"
+delete-after: True
+changes:
+ - tweak: "Meatwheat and Oats now have rarity"
+ - balance: "Oats now have at lest 50% more flour in them"
diff --git a/html/changelogs/AutoChangeLog-pr-11048.yml b/html/changelogs/AutoChangeLog-pr-11048.yml
new file mode 100644
index 0000000000..77af926906
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11048.yml
@@ -0,0 +1,4 @@
+author: "Trilbyspaceclone"
+delete-after: True
+changes:
+ - balance: "BDM now uses a PKA rather then a normal KA"
diff --git a/html/changelogs/AutoChangeLog-pr-11051.yml b/html/changelogs/AutoChangeLog-pr-11051.yml
new file mode 100644
index 0000000000..8ac7939b00
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11051.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - balance: "hand teleporters now require 30 deciseconds of still movement by the user to dispel portals. there's a beam effect too."
diff --git a/html/changelogs/AutoChangeLog-pr-11053.yml b/html/changelogs/AutoChangeLog-pr-11053.yml
new file mode 100644
index 0000000000..a3c2eb29fa
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11053.yml
@@ -0,0 +1,5 @@
+author: "keronshb"
+delete-after: True
+changes:
+ - rscadd: "Added burn and knockback to stunhand during HALOS on cult."
+ - balance: "Added burn and knockback to stunhand during HALOS on cult."
diff --git a/html/changelogs/AutoChangeLog-pr-11056.yml b/html/changelogs/AutoChangeLog-pr-11056.yml
new file mode 100644
index 0000000000..b56efb3557
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11056.yml
@@ -0,0 +1,4 @@
+author: "necromanceranne"
+delete-after: True
+changes:
+ - rscdel: "Bullets causing bleed rates equal to unmitigated damage through all forms of defense."
diff --git a/html/changelogs/AutoChangeLog-pr-11060.yml b/html/changelogs/AutoChangeLog-pr-11060.yml
new file mode 100644
index 0000000000..c78f03a26d
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11060.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - balance: "Sort of a bugfix but cult blood magic and all guns now respect stamina softcrit."
diff --git a/html/changelogs/AutoChangeLog-pr-10333.yml b/html/changelogs/AutoChangeLog-pr-11061.yml
similarity index 50%
rename from html/changelogs/AutoChangeLog-pr-10333.yml
rename to html/changelogs/AutoChangeLog-pr-11061.yml
index b5c11a4646..5225edc141 100644
--- a/html/changelogs/AutoChangeLog-pr-10333.yml
+++ b/html/changelogs/AutoChangeLog-pr-11061.yml
@@ -1,4 +1,4 @@
author: "Trilbyspaceclone"
delete-after: True
changes:
- - rscadd: "Grass now makes light beer when distilled"
+ - tweak: "Gang tower shield is no longer transparent"
diff --git a/html/changelogs/AutoChangeLog-pr-11062.yml b/html/changelogs/AutoChangeLog-pr-11062.yml
new file mode 100644
index 0000000000..b9bc85b1c1
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11062.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - balance: "Organ healing rate doubled. Organ decay rate halved to match its define (15 minutes for full decay, so at around 8-10 minutes it'll be really fucked)."
diff --git a/html/changelogs/AutoChangeLog-pr-11063.yml b/html/changelogs/AutoChangeLog-pr-11063.yml
new file mode 100644
index 0000000000..c729fb5ed9
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11063.yml
@@ -0,0 +1,4 @@
+author: "Zellular"
+delete-after: True
+changes:
+ - tweak: "Tweaked the movement state for the pupdozer's eyes"
diff --git a/html/changelogs/AutoChangeLog-pr-11070.yml b/html/changelogs/AutoChangeLog-pr-11070.yml
new file mode 100644
index 0000000000..3c923bddb0
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11070.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - tweak: "Storage now caches max screen size and only stores when being opened by a non ghost, meaning ghosts will no longer distort living player screens when viewing storage."
diff --git a/html/changelogs/AutoChangeLog-pr-11071.yml b/html/changelogs/AutoChangeLog-pr-11071.yml
new file mode 100644
index 0000000000..e8581b325e
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11071.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Fixed being unable to dump a trash bag's contents directly into disposal bins."
diff --git a/html/changelogs/AutoChangeLog-pr-11072.yml b/html/changelogs/AutoChangeLog-pr-11072.yml
new file mode 100644
index 0000000000..ab21d91e19
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11072.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Doubled the halved flavor text maximum length."
diff --git a/html/changelogs/AutoChangeLog-pr-11073.yml b/html/changelogs/AutoChangeLog-pr-11073.yml
new file mode 100644
index 0000000000..078c5fb300
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11073.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Reduced tongue organ damage and tasting pH message spam."
diff --git a/html/changelogs/AutoChangeLog-pr-11075.yml b/html/changelogs/AutoChangeLog-pr-11075.yml
new file mode 100644
index 0000000000..0eb612a81e
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11075.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Fixed agent IDs not registering the inputted name. Thanks MrPerson."
diff --git a/html/changelogs/AutoChangeLog-pr-11076.yml b/html/changelogs/AutoChangeLog-pr-11076.yml
new file mode 100644
index 0000000000..e0f187a038
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11076.yml
@@ -0,0 +1,4 @@
+author: "nemvar"
+delete-after: True
+changes:
+ - code_imp: "Changed mob biotypes from lists to flags."
diff --git a/html/changelogs/AutoChangeLog-pr-11079.yml b/html/changelogs/AutoChangeLog-pr-11079.yml
new file mode 100644
index 0000000000..b613b6c867
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11079.yml
@@ -0,0 +1,4 @@
+author: "Arturlang"
+delete-after: True
+changes:
+ - tweak: "Valentines can now be converted by bloodsuckers"
diff --git a/html/changelogs/AutoChangeLog-pr-10383.yml b/html/changelogs/AutoChangeLog-pr-11080.yml
similarity index 52%
rename from html/changelogs/AutoChangeLog-pr-10383.yml
rename to html/changelogs/AutoChangeLog-pr-11080.yml
index c6787bbafe..1dd764cfc3 100644
--- a/html/changelogs/AutoChangeLog-pr-10383.yml
+++ b/html/changelogs/AutoChangeLog-pr-11080.yml
@@ -1,4 +1,4 @@
author: "kevinz000"
delete-after: True
changes:
- - bugfix: "megafauna can hear again"
+ - balance: "Stunbatons changed yet again."
diff --git a/html/changelogs/AutoChangeLog-pr-11081.yml b/html/changelogs/AutoChangeLog-pr-11081.yml
new file mode 100644
index 0000000000..2fcb15b7aa
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11081.yml
@@ -0,0 +1,4 @@
+author: "Detective-Google"
+delete-after: True
+changes:
+ - bugfix: "valentines candy no longer prefbreaks"
diff --git a/html/changelogs/AutoChangeLog-pr-11082.yml b/html/changelogs/AutoChangeLog-pr-11082.yml
new file mode 100644
index 0000000000..89d9b4ca52
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11082.yml
@@ -0,0 +1,4 @@
+author: "Hatterhat"
+delete-after: True
+changes:
+ - rscadd: "Preservahyde! Made with water, bromine, and formaldehyde, it doesn't decay into histamine. Instead, it just prevents your organs from rotting into nothing."
diff --git a/html/changelogs/AutoChangeLog-pr-11083.yml b/html/changelogs/AutoChangeLog-pr-11083.yml
new file mode 100644
index 0000000000..757d9fcb63
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11083.yml
@@ -0,0 +1,4 @@
+author: "Hatterhat"
+delete-after: True
+changes:
+ - balance: "You can now purify eldritch longswords with a bible. This creates purified longswords, which do not have anti-magic properties, but are still good for swinging at cultists."
diff --git a/html/changelogs/AutoChangeLog-pr-11086.yml b/html/changelogs/AutoChangeLog-pr-11086.yml
new file mode 100644
index 0000000000..e11113e71c
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11086.yml
@@ -0,0 +1,6 @@
+author: "YakumoChen"
+delete-after: True
+changes:
+ - imageadd: "Rings look nicer. Sprites used from RP."
+ - imageadd: "Ring on-hands. Diamond rings sparkle!"
+ - tweak: "You can now propose using a diamond ring in your hand."
diff --git a/html/changelogs/AutoChangeLog-pr-11087.yml b/html/changelogs/AutoChangeLog-pr-11087.yml
new file mode 100644
index 0000000000..179ef6a2b7
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11087.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Fixed solar panels/trackers (de)construction being a bit wonky."
diff --git a/html/changelogs/AutoChangeLog-pr-10338.yml b/html/changelogs/AutoChangeLog-pr-11090.yml
similarity index 64%
rename from html/changelogs/AutoChangeLog-pr-10338.yml
rename to html/changelogs/AutoChangeLog-pr-11090.yml
index c4816a3fbe..698306fdb6 100644
--- a/html/changelogs/AutoChangeLog-pr-10338.yml
+++ b/html/changelogs/AutoChangeLog-pr-11090.yml
@@ -1,4 +1,4 @@
author: "Seris02"
delete-after: True
changes:
- - rscadd: "marshmallow"
+ - rscadd: "rest hotkey"
diff --git a/html/changelogs/AutoChangeLog-pr-11091.yml b/html/changelogs/AutoChangeLog-pr-11091.yml
new file mode 100644
index 0000000000..5d43d7dbdf
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11091.yml
@@ -0,0 +1,5 @@
+author: "BlueWildrose"
+delete-after: True
+changes:
+ - bugfix: "podpeople tail wagging"
+ - tweak: "lifebringer ghost role fixes"
diff --git a/html/changelogs/AutoChangeLog-pr-11093.yml b/html/changelogs/AutoChangeLog-pr-11093.yml
new file mode 100644
index 0000000000..4815818b0d
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11093.yml
@@ -0,0 +1,5 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Fixed something about slime blood and the law of conservation of mass."
+ - bugfix: "Fixed flypeople being emetic machine guns."
diff --git a/html/changelogs/AutoChangeLog-pr-11094.yml b/html/changelogs/AutoChangeLog-pr-11094.yml
new file mode 100644
index 0000000000..a6db5bd93f
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11094.yml
@@ -0,0 +1,4 @@
+author: "Trilbyspaceclone"
+delete-after: True
+changes:
+ - tweak: "Cosmic winter coat now glows like the bedsheet, and holds normal winter coat gear"
diff --git a/html/changelogs/AutoChangeLog-pr-11098.yml b/html/changelogs/AutoChangeLog-pr-11098.yml
new file mode 100644
index 0000000000..5ca7b7dd1e
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11098.yml
@@ -0,0 +1,4 @@
+author: "Raiq & Linzolle"
+delete-after: True
+changes:
+ - rscadd: "Bone bow - Ash walkers crafting , bone arrows - Ash walkers crafting, silk string used in bow crafting, harden arrows - Ash walkers crafting, ash walker only crafting book, basic pipe bow, and bow & arrow selling. Quivers for ash walkers as well, just to hold some arrows well out on the hunt!"
diff --git a/html/changelogs/AutoChangeLog-pr-11101.yml b/html/changelogs/AutoChangeLog-pr-11101.yml
new file mode 100644
index 0000000000..5305e2b923
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11101.yml
@@ -0,0 +1,4 @@
+author: "coiax"
+delete-after: True
+changes:
+ - rscadd: "Admin and event only pair pinpointers! They come in a box of two, and each pinpointer will always point at its corresponding pair. Aww."
diff --git a/html/changelogs/AutoChangeLog-pr-11103.yml b/html/changelogs/AutoChangeLog-pr-11103.yml
new file mode 100644
index 0000000000..f38e23f76e
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11103.yml
@@ -0,0 +1,5 @@
+author: "PersianXerxes"
+delete-after: True
+changes:
+ - rscadd: "Better clarified the comment explaining the contraband tag."
+ - tweak: "Cargo nuclear defusal kits now require an emag'd drop pod console to be purchased."
diff --git a/html/changelogs/AutoChangeLog-pr-11104.yml b/html/changelogs/AutoChangeLog-pr-11104.yml
new file mode 100644
index 0000000000..205af650a9
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11104.yml
@@ -0,0 +1,4 @@
+author: "Trilbyspaceclone"
+delete-after: True
+changes:
+ - server: "Express console is now logging what it buys, like the normal console should"
diff --git a/html/changelogs/AutoChangeLog-pr-11105.yml b/html/changelogs/AutoChangeLog-pr-11105.yml
new file mode 100644
index 0000000000..0c7ac7b1e6
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11105.yml
@@ -0,0 +1,4 @@
+author: "Trilbyspaceclone"
+delete-after: True
+changes:
+ - bugfix: "Crabs are now made of crab meat."
diff --git a/html/changelogs/AutoChangeLog-pr-11108.yml b/html/changelogs/AutoChangeLog-pr-11108.yml
new file mode 100644
index 0000000000..c8bed79399
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11108.yml
@@ -0,0 +1,4 @@
+author: "Seris02"
+delete-after: True
+changes:
+ - rscadd: "hsl instead of sum of rgb for spraycan lum check"
diff --git a/html/changelogs/AutoChangeLog-pr-11111.yml b/html/changelogs/AutoChangeLog-pr-11111.yml
new file mode 100644
index 0000000000..4e947ed297
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11111.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Fixed virology being unable to be done with synthetic blood."
diff --git a/html/changelogs/AutoChangeLog-pr-11112.yml b/html/changelogs/AutoChangeLog-pr-11112.yml
new file mode 100644
index 0000000000..f54e2e0192
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11112.yml
@@ -0,0 +1,9 @@
+author: "necromanceranne"
+delete-after: True
+changes:
+ - balance: "Reverted #9092, crew mecha no longer spawn with tracking beacons."
+ - balance: "[Port] Mecha ballistics weapons now require ammo created from an Exosuit Fabricator or the Security Protolathe, though they will start with a full magazine and in most cases enough for one full reload. Reloading these weapons no longer chunks your power cell. Clown (and mime) mecha equipment have not changed."
+ - balance: "[Port] The SRM-8 Missile Launcher has been replaced with the BRM-6 Breaching Missile Launcher in techwebs (Nukie Mauler remains equipped with the SRM-8)."
+ - balance: "[Port] Both Missile Launchers and the Clusterbang Launcher do not have an ammo cache, and cannot be reloaded by the pilot. Once the initial loaded ammo has been spent, you can use the appropriate ammo box to load the weapon directly."
+ - rscadd: "[Port] Utility mechs that have a clamp equipped can load ammo from their own cargo hold into other mechs."
+ - rscadd: "[Port] Nuke Ops can purchase spare ammo duffel bags for their mecha weapons, should they run low."
diff --git a/html/changelogs/AutoChangeLog-pr-11113.yml b/html/changelogs/AutoChangeLog-pr-11113.yml
new file mode 100644
index 0000000000..b3c0bb34a8
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11113.yml
@@ -0,0 +1,4 @@
+author: "CameronWoof"
+delete-after: True
+changes:
+ - rscadd: "Ammonia and saltpetre can now be made at the biogenerator."
diff --git a/html/changelogs/AutoChangeLog-pr-11114.yml b/html/changelogs/AutoChangeLog-pr-11114.yml
new file mode 100644
index 0000000000..7d56933c65
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11114.yml
@@ -0,0 +1,6 @@
+author: "Psody-Mordheim"
+delete-after: True
+changes:
+ - rscadd: "You can now make synth-flesh with synthetic blood."
+ - rscadd: "You can now make synthetic blood via mixing saline glucose, iron, stable plasma and heating it to 350 temp."
+ - rscadd: "You can mix synthetic blood and cryoxadone to create synth-meat in addition to normal blood."
diff --git a/html/changelogs/AutoChangeLog-pr-11119.yml b/html/changelogs/AutoChangeLog-pr-11119.yml
new file mode 100644
index 0000000000..4a0dc59c65
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11119.yml
@@ -0,0 +1,6 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - rscdel: "Renamed \"blaster carbine\" and \"blaster rifles\" back to \"energy gun\" and \"laser gun\" respectively"
+ - bugfix: "Fixed magnetic rifles & co being inconsistent with printed energy guns and start with an empty power cell."
+ - imagedel: "Reverted practice laser gun sprites back to their former glory. Mostly."
diff --git a/html/changelogs/AutoChangeLog-pr-11120.yml b/html/changelogs/AutoChangeLog-pr-11120.yml
new file mode 100644
index 0000000000..64a435146c
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11120.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "Fixed sprint buffer cost and regen being rounded down."
diff --git a/html/changelogs/AutoChangeLog-pr-11131.yml b/html/changelogs/AutoChangeLog-pr-11131.yml
new file mode 100644
index 0000000000..9a14049c2f
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11131.yml
@@ -0,0 +1,4 @@
+author: "Seris02"
+delete-after: True
+changes:
+ - balance: "bloodcrawl's cooldown"
diff --git a/html/changelogs/AutoChangeLog-pr-11133.yml b/html/changelogs/AutoChangeLog-pr-11133.yml
new file mode 100644
index 0000000000..8cedc300c7
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11133.yml
@@ -0,0 +1,5 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - balance: "Nerfed the fermenting barrel export industry."
+ - tweak: "Reagent dispensers selling price no longer takes in account the reagents volume. It's already handled by reagents export."
diff --git a/html/changelogs/AutoChangeLog-pr-11134.yml b/html/changelogs/AutoChangeLog-pr-11134.yml
new file mode 100644
index 0000000000..8ec6c97164
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11134.yml
@@ -0,0 +1,4 @@
+author: "Hatterhat"
+delete-after: True
+changes:
+ - bugfix: "mechs with stock parts now have icons"
diff --git a/html/changelogs/AutoChangeLog-pr-11136.yml b/html/changelogs/AutoChangeLog-pr-11136.yml
new file mode 100644
index 0000000000..5289fc3492
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11136.yml
@@ -0,0 +1,5 @@
+author: "Owai-Seek"
+delete-after: True
+changes:
+ - rscadd: "BROOM"
+ - tweak: "Janitor Vendor now has gear for two Janitors."
diff --git a/html/changelogs/AutoChangeLog-pr-11138.yml b/html/changelogs/AutoChangeLog-pr-11138.yml
new file mode 100644
index 0000000000..fd0e53bb73
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11138.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - tweak: "the game's built in autoclicker aka CanMobAutoclick no longer triggers client/Click()'s anti clickspam guard."
diff --git a/html/changelogs/AutoChangeLog-pr-11139.yml b/html/changelogs/AutoChangeLog-pr-11139.yml
new file mode 100644
index 0000000000..99a7027977
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11139.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - balance: "pais can no longer bodyblock bullets"
diff --git a/html/changelogs/AutoChangeLog-pr-11140.yml b/html/changelogs/AutoChangeLog-pr-11140.yml
new file mode 100644
index 0000000000..1054039b53
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11140.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - balance: "pai radio short changed to 3 minutes down from 5"
diff --git a/html/changelogs/AutoChangeLog-pr-11141.yml b/html/changelogs/AutoChangeLog-pr-11141.yml
new file mode 100644
index 0000000000..68de20a632
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11141.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - balance: "pais are no longer fulltile click opacity."
diff --git a/html/changelogs/AutoChangeLog-pr-11142.yml b/html/changelogs/AutoChangeLog-pr-11142.yml
new file mode 100644
index 0000000000..35c4033e2c
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11142.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - refactor: "vampire \"immortal haste\" has been reworked to be more reliable and consistent."
diff --git a/html/changelogs/AutoChangeLog-pr-11145.yml b/html/changelogs/AutoChangeLog-pr-11145.yml
new file mode 100644
index 0000000000..2ad26003fb
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11145.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "pAIs are yet again unable to perform certain silicon interactions with machineries yet again."
diff --git a/html/changelogs/AutoChangeLog-pr-11146.yml b/html/changelogs/AutoChangeLog-pr-11146.yml
new file mode 100644
index 0000000000..09d3b0134b
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11146.yml
@@ -0,0 +1,4 @@
+author: "Putnam3145"
+delete-after: True
+changes:
+ - balance: "all supermatter damage is now hardcapped"
diff --git a/html/changelogs/AutoChangeLog-pr-11147.yml b/html/changelogs/AutoChangeLog-pr-11147.yml
new file mode 100644
index 0000000000..0978b2b184
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11147.yml
@@ -0,0 +1,4 @@
+author: "Putnam3145"
+delete-after: True
+changes:
+ - tweak: "Supermatter sabotage objective no longer shows up with no supermatter"
diff --git a/html/changelogs/AutoChangeLog-pr-11149.yml b/html/changelogs/AutoChangeLog-pr-11149.yml
new file mode 100644
index 0000000000..bc9ce5eb9e
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11149.yml
@@ -0,0 +1,4 @@
+author: "Putnam3145"
+delete-after: True
+changes:
+ - bugfix: "Mining vendors no longer fail and eat your points iff you have precisely enough points to pay for an item"
diff --git a/html/changelogs/AutoChangeLog-pr-11153.yml b/html/changelogs/AutoChangeLog-pr-11153.yml
new file mode 100644
index 0000000000..bba1af88db
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11153.yml
@@ -0,0 +1,5 @@
+author: "BuffEngineering, nemvar."
+delete-after: True
+changes:
+ - bugfix: "Addicts can now feel like they're getting their fix or kicking it."
+ - bugfix: "Aheals now remove addictions and restore your mood to default."
diff --git a/html/changelogs/AutoChangeLog-pr-11154.yml b/html/changelogs/AutoChangeLog-pr-11154.yml
new file mode 100644
index 0000000000..03e1898b60
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11154.yml
@@ -0,0 +1,4 @@
+author: "Kraseo"
+delete-after: True
+changes:
+ - bugfix: "Nightmares no longer delete entire guns from existence for merely having a light on them."
diff --git a/html/changelogs/AutoChangeLog-pr-11155.yml b/html/changelogs/AutoChangeLog-pr-11155.yml
new file mode 100644
index 0000000000..069cdbdedf
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11155.yml
@@ -0,0 +1,4 @@
+author: "necromanceranne"
+delete-after: True
+changes:
+ - bugfix: "Literally unclickability with a cham projector"
diff --git a/html/changelogs/AutoChangeLog-pr-11156.yml b/html/changelogs/AutoChangeLog-pr-11156.yml
new file mode 100644
index 0000000000..dc684eb4ef
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11156.yml
@@ -0,0 +1,8 @@
+author: "Psody-Mordheim"
+delete-after: True
+changes:
+ - rscadd: "Disfiguration Symptom."
+ - rscadd: "Deoxyribonucleic Acid Saboteur Symptom."
+ - rscadd: "Polyvitiligo Symptom."
+ - rscdel: "Revitiligo Symptom."
+ - rscdel: "Vitiligo Symptom."
diff --git a/html/changelogs/AutoChangeLog-pr-10538.yml b/html/changelogs/AutoChangeLog-pr-11157.yml
similarity index 58%
rename from html/changelogs/AutoChangeLog-pr-10538.yml
rename to html/changelogs/AutoChangeLog-pr-11157.yml
index 01119f886f..5843c5f0e4 100644
--- a/html/changelogs/AutoChangeLog-pr-10538.yml
+++ b/html/changelogs/AutoChangeLog-pr-11157.yml
@@ -1,4 +1,4 @@
author: "Putnam3145"
delete-after: True
changes:
- - bugfix: "Chemistry not :b:roke"
+ - tweak: "Licking pref"
diff --git a/html/changelogs/AutoChangeLog-pr-11160.yml b/html/changelogs/AutoChangeLog-pr-11160.yml
new file mode 100644
index 0000000000..cf0fc3253e
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11160.yml
@@ -0,0 +1,4 @@
+author: "Hatterhat"
+delete-after: True
+changes:
+ - balance: "Pie reagent transfer now requires an uncovered mouth."
diff --git a/html/changelogs/AutoChangeLog-pr-11163.yml b/html/changelogs/AutoChangeLog-pr-11163.yml
new file mode 100644
index 0000000000..5492ad563b
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11163.yml
@@ -0,0 +1,4 @@
+author: "MrJWhit"
+delete-after: True
+changes:
+ - tweak: "TEG"
diff --git a/html/changelogs/AutoChangeLog-pr-10483.yml b/html/changelogs/AutoChangeLog-pr-11166.yml
similarity index 58%
rename from html/changelogs/AutoChangeLog-pr-10483.yml
rename to html/changelogs/AutoChangeLog-pr-11166.yml
index 1f016b1591..c812c77ad8 100644
--- a/html/changelogs/AutoChangeLog-pr-10483.yml
+++ b/html/changelogs/AutoChangeLog-pr-11166.yml
@@ -1,4 +1,4 @@
author: "Putnam3145"
delete-after: True
changes:
- - bugfix: "Limb damage works now"
+ - bugfix: "Fixed IRV."
diff --git a/html/changelogs/AutoChangeLog-pr-11168.yml b/html/changelogs/AutoChangeLog-pr-11168.yml
new file mode 100644
index 0000000000..93caacdad1
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11168.yml
@@ -0,0 +1,4 @@
+author: "Putnam3145"
+delete-after: True
+changes:
+ - bugfix: "Runtime if nobody has a chaos pref set"
diff --git a/html/changelogs/AutoChangeLog-pr-11176.yml b/html/changelogs/AutoChangeLog-pr-11176.yml
new file mode 100644
index 0000000000..98c7619990
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-11176.yml
@@ -0,0 +1,4 @@
+author: "Putnam3145"
+delete-after: True
+changes:
+ - bugfix: "IRV fixed... again"
diff --git a/html/changelogs/AutoChangeLog-pr-9601.yml b/html/changelogs/AutoChangeLog-pr-9601.yml
deleted file mode 100644
index c54279627f..0000000000
--- a/html/changelogs/AutoChangeLog-pr-9601.yml
+++ /dev/null
@@ -1,9 +0,0 @@
-author: "Ghommie (original PRs by Kevinz000, ShivCalez, 4dplanner, Barhandar, 81Denton, zxaber, Fox-McCloud)"
-delete-after: True
-changes:
- - rscadd: "All atom movables now have move force and move resist, and pull force An atom can only pull another atom if its pull force is stronger than that atom's move resist"
- - rscadd: "Mobs with a higher move force than an atom's move resist will automatically try to force the atom out of its way. This might not always work, depending on how snowflakey code is.
-experimental: As of right now, everything has a move force and resist of 100, and a pull force of 101. Things take (resist - force) damage when bumped into
-experimental: Failing to move onto a tile will now still bump up your last move timer. However, successfully pushing something out of your way will result in you automatically moving into where it was."
- - bugfix: "Bolted AIs can no longer be teleported by launchpads."
- - balance: "Megafauna cannot teleport"
diff --git a/html/changelogs/AutoChangeLog-pr-9856.yml b/html/changelogs/AutoChangeLog-pr-9856.yml
deleted file mode 100644
index 697c1c78f5..0000000000
--- a/html/changelogs/AutoChangeLog-pr-9856.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "Ghommie"
-delete-after: True
-changes:
- - rscadd: "Refactored code to allow all living mobs to use shields and not only humans."
- - tweak: "Monkys will now retaliate against aliens attacking them (as if they even posed a threat to start with)."
diff --git a/html/changelogs/AutoChangeLog-pr-9860.yml b/html/changelogs/AutoChangeLog-pr-9860.yml
new file mode 100644
index 0000000000..c9d75aa7b9
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-9860.yml
@@ -0,0 +1,5 @@
+author: "tralezab, bandit, Skoglol"
+delete-after: True
+changes:
+ - rscadd: "The mime's PDA messages are silent now!"
+ - rscadd: "30 new emoji have been added. Mime buff, mime now OP."
diff --git a/html/changelogs/AutoChangeLog-pr-9878.yml b/html/changelogs/AutoChangeLog-pr-9878.yml
deleted file mode 100644
index ef9c704003..0000000000
--- a/html/changelogs/AutoChangeLog-pr-9878.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Ghommie"
-delete-after: True
-changes:
- - tweak: "add a click cooldown to the overly spammable table slamming."
diff --git a/html/changelogs/AutoChangeLog-pr-9905.yml b/html/changelogs/AutoChangeLog-pr-9905.yml
deleted file mode 100644
index a0330b8103..0000000000
--- a/html/changelogs/AutoChangeLog-pr-9905.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-author: "Trilbyspaceclone"
-delete-after: True
-changes:
- - rscadd: "more glassware"
- - rscadd: "Few new packs and glassmaker kit!"
- - tweak: "prices/chems of crates/autobottler"
- - bugfix: "some crates ungettable as well as some broken crates"
diff --git a/html/font-awesome/README.MD b/html/font-awesome/README.MD
new file mode 100644
index 0000000000..7d693c36f0
--- /dev/null
+++ b/html/font-awesome/README.MD
@@ -0,0 +1,6 @@
+Due to the fact browse_rsc can't create subdirectories, every time you update font-awesome you'll need to change relative webfont references in all.min.css
+eg ../webfonts/fa-regular-400.ttf => fa-regular-400.ttf (or whatever you call it in asset datum)
+
+Second change is ripping out file types other than woff and eot(ie8) from the css
+
+Finally, removing brand related css.
\ No newline at end of file
diff --git a/html/font-awesome/css/all.min.css b/html/font-awesome/css/all.min.css
new file mode 100644
index 0000000000..e7cdfffe75
--- /dev/null
+++ b/html/font-awesome/css/all.min.css
@@ -0,0 +1,5 @@
+/*!
+ * Font Awesome Free 5.9.0 by @fontawesome - https://fontawesome.com
+ * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
+ */
+.fa,.fab,.fal,.far,.fas{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;line-height:1}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-.0667em}.fa-xs{font-size:.75em}.fa-sm{font-size:.875em}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:2.5em;padding-left:0}.fa-ul>li{position:relative}.fa-li{left:-2em;position:absolute;text-align:center;width:2em;line-height:inherit}.fa-border{border:.08em solid #eee;border-radius:.1em;padding:.2em .25em .15em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left,.fab.fa-pull-left,.fal.fa-pull-left,.far.fa-pull-left,.fas.fa-pull-left{margin-right:.3em}.fa.fa-pull-right,.fab.fa-pull-right,.fal.fa-pull-right,.far.fa-pull-right,.fas.fa-pull-right{margin-left:.3em}.fa-spin{animation:fa-spin 2s infinite linear}.fa-pulse{animation:fa-spin 1s infinite steps(8)}@keyframes fa-spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";transform:scaleX(-1)}.fa-flip-vertical{transform:scaleY(-1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical,.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{transform:scale(-1)}:root .fa-flip-both,:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{filter:none}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-500px:before{content:"\f26e"}.fa-accessible-icon:before{content:"\f368"}.fa-accusoft:before{content:"\f369"}.fa-acquisitions-incorporated:before{content:"\f6af"}.fa-ad:before{content:"\f641"}.fa-address-book:before{content:"\f2b9"}.fa-address-card:before{content:"\f2bb"}.fa-adjust:before{content:"\f042"}.fa-adn:before{content:"\f170"}.fa-adobe:before{content:"\f778"}.fa-adversal:before{content:"\f36a"}.fa-affiliatetheme:before{content:"\f36b"}.fa-air-freshener:before{content:"\f5d0"}.fa-airbnb:before{content:"\f834"}.fa-algolia:before{content:"\f36c"}.fa-align-center:before{content:"\f037"}.fa-align-justify:before{content:"\f039"}.fa-align-left:before{content:"\f036"}.fa-align-right:before{content:"\f038"}.fa-alipay:before{content:"\f642"}.fa-allergies:before{content:"\f461"}.fa-amazon:before{content:"\f270"}.fa-amazon-pay:before{content:"\f42c"}.fa-ambulance:before{content:"\f0f9"}.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-amilia:before{content:"\f36d"}.fa-anchor:before{content:"\f13d"}.fa-android:before{content:"\f17b"}.fa-angellist:before{content:"\f209"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-down:before{content:"\f107"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angry:before{content:"\f556"}.fa-angrycreative:before{content:"\f36e"}.fa-angular:before{content:"\f420"}.fa-ankh:before{content:"\f644"}.fa-app-store:before{content:"\f36f"}.fa-app-store-ios:before{content:"\f370"}.fa-apper:before{content:"\f371"}.fa-apple:before{content:"\f179"}.fa-apple-alt:before{content:"\f5d1"}.fa-apple-pay:before{content:"\f415"}.fa-archive:before{content:"\f187"}.fa-archway:before{content:"\f557"}.fa-arrow-alt-circle-down:before{content:"\f358"}.fa-arrow-alt-circle-left:before{content:"\f359"}.fa-arrow-alt-circle-right:before{content:"\f35a"}.fa-arrow-alt-circle-up:before{content:"\f35b"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-down:before{content:"\f063"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrows-alt:before{content:"\f0b2"}.fa-arrows-alt-h:before{content:"\f337"}.fa-arrows-alt-v:before{content:"\f338"}.fa-artstation:before{content:"\f77a"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asterisk:before{content:"\f069"}.fa-asymmetrik:before{content:"\f372"}.fa-at:before{content:"\f1fa"}.fa-atlas:before{content:"\f558"}.fa-atlassian:before{content:"\f77b"}.fa-atom:before{content:"\f5d2"}.fa-audible:before{content:"\f373"}.fa-audio-description:before{content:"\f29e"}.fa-autoprefixer:before{content:"\f41c"}.fa-avianex:before{content:"\f374"}.fa-aviato:before{content:"\f421"}.fa-award:before{content:"\f559"}.fa-aws:before{content:"\f375"}.fa-baby:before{content:"\f77c"}.fa-baby-carriage:before{content:"\f77d"}.fa-backspace:before{content:"\f55a"}.fa-backward:before{content:"\f04a"}.fa-bacon:before{content:"\f7e5"}.fa-balance-scale:before{content:"\f24e"}.fa-balance-scale-left:before{content:"\f515"}.fa-balance-scale-right:before{content:"\f516"}.fa-ban:before{content:"\f05e"}.fa-band-aid:before{content:"\f462"}.fa-bandcamp:before{content:"\f2d5"}.fa-barcode:before{content:"\f02a"}.fa-bars:before{content:"\f0c9"}.fa-baseball-ball:before{content:"\f433"}.fa-basketball-ball:before{content:"\f434"}.fa-bath:before{content:"\f2cd"}.fa-battery-empty:before{content:"\f244"}.fa-battery-full:before{content:"\f240"}.fa-battery-half:before{content:"\f242"}.fa-battery-quarter:before{content:"\f243"}.fa-battery-three-quarters:before{content:"\f241"}.fa-battle-net:before{content:"\f835"}.fa-bed:before{content:"\f236"}.fa-beer:before{content:"\f0fc"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-bell:before{content:"\f0f3"}.fa-bell-slash:before{content:"\f1f6"}.fa-bezier-curve:before{content:"\f55b"}.fa-bible:before{content:"\f647"}.fa-bicycle:before{content:"\f206"}.fa-biking:before{content:"\f84a"}.fa-bimobject:before{content:"\f378"}.fa-binoculars:before{content:"\f1e5"}.fa-biohazard:before{content:"\f780"}.fa-birthday-cake:before{content:"\f1fd"}.fa-bitbucket:before{content:"\f171"}.fa-bitcoin:before{content:"\f379"}.fa-bity:before{content:"\f37a"}.fa-black-tie:before{content:"\f27e"}.fa-blackberry:before{content:"\f37b"}.fa-blender:before{content:"\f517"}.fa-blender-phone:before{content:"\f6b6"}.fa-blind:before{content:"\f29d"}.fa-blog:before{content:"\f781"}.fa-blogger:before{content:"\f37c"}.fa-blogger-b:before{content:"\f37d"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-bold:before{content:"\f032"}.fa-bolt:before{content:"\f0e7"}.fa-bomb:before{content:"\f1e2"}.fa-bone:before{content:"\f5d7"}.fa-bong:before{content:"\f55c"}.fa-book:before{content:"\f02d"}.fa-book-dead:before{content:"\f6b7"}.fa-book-medical:before{content:"\f7e6"}.fa-book-open:before{content:"\f518"}.fa-book-reader:before{content:"\f5da"}.fa-bookmark:before{content:"\f02e"}.fa-bootstrap:before{content:"\f836"}.fa-border-all:before{content:"\f84c"}.fa-border-none:before{content:"\f850"}.fa-border-style:before{content:"\f853"}.fa-bowling-ball:before{content:"\f436"}.fa-box:before{content:"\f466"}.fa-box-open:before{content:"\f49e"}.fa-boxes:before{content:"\f468"}.fa-braille:before{content:"\f2a1"}.fa-brain:before{content:"\f5dc"}.fa-bread-slice:before{content:"\f7ec"}.fa-briefcase:before{content:"\f0b1"}.fa-briefcase-medical:before{content:"\f469"}.fa-broadcast-tower:before{content:"\f519"}.fa-broom:before{content:"\f51a"}.fa-brush:before{content:"\f55d"}.fa-btc:before{content:"\f15a"}.fa-buffer:before{content:"\f837"}.fa-bug:before{content:"\f188"}.fa-building:before{content:"\f1ad"}.fa-bullhorn:before{content:"\f0a1"}.fa-bullseye:before{content:"\f140"}.fa-burn:before{content:"\f46a"}.fa-buromobelexperte:before{content:"\f37f"}.fa-bus:before{content:"\f207"}.fa-bus-alt:before{content:"\f55e"}.fa-business-time:before{content:"\f64a"}.fa-buysellads:before{content:"\f20d"}.fa-calculator:before{content:"\f1ec"}.fa-calendar:before{content:"\f133"}.fa-calendar-alt:before{content:"\f073"}.fa-calendar-check:before{content:"\f274"}.fa-calendar-day:before{content:"\f783"}.fa-calendar-minus:before{content:"\f272"}.fa-calendar-plus:before{content:"\f271"}.fa-calendar-times:before{content:"\f273"}.fa-calendar-week:before{content:"\f784"}.fa-camera:before{content:"\f030"}.fa-camera-retro:before{content:"\f083"}.fa-campground:before{content:"\f6bb"}.fa-canadian-maple-leaf:before{content:"\f785"}.fa-candy-cane:before{content:"\f786"}.fa-cannabis:before{content:"\f55f"}.fa-capsules:before{content:"\f46b"}.fa-car:before{content:"\f1b9"}.fa-car-alt:before{content:"\f5de"}.fa-car-battery:before{content:"\f5df"}.fa-car-crash:before{content:"\f5e1"}.fa-car-side:before{content:"\f5e4"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-caret-square-down:before{content:"\f150"}.fa-caret-square-left:before{content:"\f191"}.fa-caret-square-right:before{content:"\f152"}.fa-caret-square-up:before{content:"\f151"}.fa-caret-up:before{content:"\f0d8"}.fa-carrot:before{content:"\f787"}.fa-cart-arrow-down:before{content:"\f218"}.fa-cart-plus:before{content:"\f217"}.fa-cash-register:before{content:"\f788"}.fa-cat:before{content:"\f6be"}.fa-cc-amazon-pay:before{content:"\f42d"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-apple-pay:before{content:"\f416"}.fa-cc-diners-club:before{content:"\f24c"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-cc-visa:before{content:"\f1f0"}.fa-centercode:before{content:"\f380"}.fa-centos:before{content:"\f789"}.fa-certificate:before{content:"\f0a3"}.fa-chair:before{content:"\f6c0"}.fa-chalkboard:before{content:"\f51b"}.fa-chalkboard-teacher:before{content:"\f51c"}.fa-charging-station:before{content:"\f5e7"}.fa-chart-area:before{content:"\f1fe"}.fa-chart-bar:before{content:"\f080"}.fa-chart-line:before{content:"\f201"}.fa-chart-pie:before{content:"\f200"}.fa-check:before{content:"\f00c"}.fa-check-circle:before{content:"\f058"}.fa-check-double:before{content:"\f560"}.fa-check-square:before{content:"\f14a"}.fa-cheese:before{content:"\f7ef"}.fa-chess:before{content:"\f439"}.fa-chess-bishop:before{content:"\f43a"}.fa-chess-board:before{content:"\f43c"}.fa-chess-king:before{content:"\f43f"}.fa-chess-knight:before{content:"\f441"}.fa-chess-pawn:before{content:"\f443"}.fa-chess-queen:before{content:"\f445"}.fa-chess-rook:before{content:"\f447"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-down:before{content:"\f078"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-chevron-up:before{content:"\f077"}.fa-child:before{content:"\f1ae"}.fa-chrome:before{content:"\f268"}.fa-chromecast:before{content:"\f838"}.fa-church:before{content:"\f51d"}.fa-circle:before{content:"\f111"}.fa-circle-notch:before{content:"\f1ce"}.fa-city:before{content:"\f64f"}.fa-clinic-medical:before{content:"\f7f2"}.fa-clipboard:before{content:"\f328"}.fa-clipboard-check:before{content:"\f46c"}.fa-clipboard-list:before{content:"\f46d"}.fa-clock:before{content:"\f017"}.fa-clone:before{content:"\f24d"}.fa-closed-captioning:before{content:"\f20a"}.fa-cloud:before{content:"\f0c2"}.fa-cloud-download-alt:before{content:"\f381"}.fa-cloud-meatball:before{content:"\f73b"}.fa-cloud-moon:before{content:"\f6c3"}.fa-cloud-moon-rain:before{content:"\f73c"}.fa-cloud-rain:before{content:"\f73d"}.fa-cloud-showers-heavy:before{content:"\f740"}.fa-cloud-sun:before{content:"\f6c4"}.fa-cloud-sun-rain:before{content:"\f743"}.fa-cloud-upload-alt:before{content:"\f382"}.fa-cloudscale:before{content:"\f383"}.fa-cloudsmith:before{content:"\f384"}.fa-cloudversify:before{content:"\f385"}.fa-cocktail:before{content:"\f561"}.fa-code:before{content:"\f121"}.fa-code-branch:before{content:"\f126"}.fa-codepen:before{content:"\f1cb"}.fa-codiepie:before{content:"\f284"}.fa-coffee:before{content:"\f0f4"}.fa-cog:before{content:"\f013"}.fa-cogs:before{content:"\f085"}.fa-coins:before{content:"\f51e"}.fa-columns:before{content:"\f0db"}.fa-comment:before{content:"\f075"}.fa-comment-alt:before{content:"\f27a"}.fa-comment-dollar:before{content:"\f651"}.fa-comment-dots:before{content:"\f4ad"}.fa-comment-medical:before{content:"\f7f5"}.fa-comment-slash:before{content:"\f4b3"}.fa-comments:before{content:"\f086"}.fa-comments-dollar:before{content:"\f653"}.fa-compact-disc:before{content:"\f51f"}.fa-compass:before{content:"\f14e"}.fa-compress:before{content:"\f066"}.fa-compress-arrows-alt:before{content:"\f78c"}.fa-concierge-bell:before{content:"\f562"}.fa-confluence:before{content:"\f78d"}.fa-connectdevelop:before{content:"\f20e"}.fa-contao:before{content:"\f26d"}.fa-cookie:before{content:"\f563"}.fa-cookie-bite:before{content:"\f564"}.fa-copy:before{content:"\f0c5"}.fa-copyright:before{content:"\f1f9"}.fa-couch:before{content:"\f4b8"}.fa-cpanel:before{content:"\f388"}.fa-creative-commons:before{content:"\f25e"}.fa-creative-commons-by:before{content:"\f4e7"}.fa-creative-commons-nc:before{content:"\f4e8"}.fa-creative-commons-nc-eu:before{content:"\f4e9"}.fa-creative-commons-nc-jp:before{content:"\f4ea"}.fa-creative-commons-nd:before{content:"\f4eb"}.fa-creative-commons-pd:before{content:"\f4ec"}.fa-creative-commons-pd-alt:before{content:"\f4ed"}.fa-creative-commons-remix:before{content:"\f4ee"}.fa-creative-commons-sa:before{content:"\f4ef"}.fa-creative-commons-sampling:before{content:"\f4f0"}.fa-creative-commons-sampling-plus:before{content:"\f4f1"}.fa-creative-commons-share:before{content:"\f4f2"}.fa-creative-commons-zero:before{content:"\f4f3"}.fa-credit-card:before{content:"\f09d"}.fa-critical-role:before{content:"\f6c9"}.fa-crop:before{content:"\f125"}.fa-crop-alt:before{content:"\f565"}.fa-cross:before{content:"\f654"}.fa-crosshairs:before{content:"\f05b"}.fa-crow:before{content:"\f520"}.fa-crown:before{content:"\f521"}.fa-crutch:before{content:"\f7f7"}.fa-css3:before{content:"\f13c"}.fa-css3-alt:before{content:"\f38b"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-cut:before{content:"\f0c4"}.fa-cuttlefish:before{content:"\f38c"}.fa-d-and-d:before{content:"\f38d"}.fa-d-and-d-beyond:before{content:"\f6ca"}.fa-dashcube:before{content:"\f210"}.fa-database:before{content:"\f1c0"}.fa-deaf:before{content:"\f2a4"}.fa-delicious:before{content:"\f1a5"}.fa-democrat:before{content:"\f747"}.fa-deploydog:before{content:"\f38e"}.fa-deskpro:before{content:"\f38f"}.fa-desktop:before{content:"\f108"}.fa-dev:before{content:"\f6cc"}.fa-deviantart:before{content:"\f1bd"}.fa-dharmachakra:before{content:"\f655"}.fa-dhl:before{content:"\f790"}.fa-diagnoses:before{content:"\f470"}.fa-diaspora:before{content:"\f791"}.fa-dice:before{content:"\f522"}.fa-dice-d20:before{content:"\f6cf"}.fa-dice-d6:before{content:"\f6d1"}.fa-dice-five:before{content:"\f523"}.fa-dice-four:before{content:"\f524"}.fa-dice-one:before{content:"\f525"}.fa-dice-six:before{content:"\f526"}.fa-dice-three:before{content:"\f527"}.fa-dice-two:before{content:"\f528"}.fa-digg:before{content:"\f1a6"}.fa-digital-ocean:before{content:"\f391"}.fa-digital-tachograph:before{content:"\f566"}.fa-directions:before{content:"\f5eb"}.fa-discord:before{content:"\f392"}.fa-discourse:before{content:"\f393"}.fa-divide:before{content:"\f529"}.fa-dizzy:before{content:"\f567"}.fa-dna:before{content:"\f471"}.fa-dochub:before{content:"\f394"}.fa-docker:before{content:"\f395"}.fa-dog:before{content:"\f6d3"}.fa-dollar-sign:before{content:"\f155"}.fa-dolly:before{content:"\f472"}.fa-dolly-flatbed:before{content:"\f474"}.fa-donate:before{content:"\f4b9"}.fa-door-closed:before{content:"\f52a"}.fa-door-open:before{content:"\f52b"}.fa-dot-circle:before{content:"\f192"}.fa-dove:before{content:"\f4ba"}.fa-download:before{content:"\f019"}.fa-draft2digital:before{content:"\f396"}.fa-drafting-compass:before{content:"\f568"}.fa-dragon:before{content:"\f6d5"}.fa-draw-polygon:before{content:"\f5ee"}.fa-dribbble:before{content:"\f17d"}.fa-dribbble-square:before{content:"\f397"}.fa-dropbox:before{content:"\f16b"}.fa-drum:before{content:"\f569"}.fa-drum-steelpan:before{content:"\f56a"}.fa-drumstick-bite:before{content:"\f6d7"}.fa-drupal:before{content:"\f1a9"}.fa-dumbbell:before{content:"\f44b"}.fa-dumpster:before{content:"\f793"}.fa-dumpster-fire:before{content:"\f794"}.fa-dungeon:before{content:"\f6d9"}.fa-dyalog:before{content:"\f399"}.fa-earlybirds:before{content:"\f39a"}.fa-ebay:before{content:"\f4f4"}.fa-edge:before{content:"\f282"}.fa-edit:before{content:"\f044"}.fa-egg:before{content:"\f7fb"}.fa-eject:before{content:"\f052"}.fa-elementor:before{content:"\f430"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-ello:before{content:"\f5f1"}.fa-ember:before{content:"\f423"}.fa-empire:before{content:"\f1d1"}.fa-envelope:before{content:"\f0e0"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-text:before{content:"\f658"}.fa-envelope-square:before{content:"\f199"}.fa-envira:before{content:"\f299"}.fa-equals:before{content:"\f52c"}.fa-eraser:before{content:"\f12d"}.fa-erlang:before{content:"\f39d"}.fa-ethereum:before{content:"\f42e"}.fa-ethernet:before{content:"\f796"}.fa-etsy:before{content:"\f2d7"}.fa-euro-sign:before{content:"\f153"}.fa-evernote:before{content:"\f839"}.fa-exchange-alt:before{content:"\f362"}.fa-exclamation:before{content:"\f12a"}.fa-exclamation-circle:before{content:"\f06a"}.fa-exclamation-triangle:before{content:"\f071"}.fa-expand:before{content:"\f065"}.fa-expand-arrows-alt:before{content:"\f31e"}.fa-expeditedssl:before{content:"\f23e"}.fa-external-link-alt:before{content:"\f35d"}.fa-external-link-square-alt:before{content:"\f360"}.fa-eye:before{content:"\f06e"}.fa-eye-dropper:before{content:"\f1fb"}.fa-eye-slash:before{content:"\f070"}.fa-facebook:before{content:"\f09a"}.fa-facebook-f:before{content:"\f39e"}.fa-facebook-messenger:before{content:"\f39f"}.fa-facebook-square:before{content:"\f082"}.fa-fan:before{content:"\f863"}.fa-fantasy-flight-games:before{content:"\f6dc"}.fa-fast-backward:before{content:"\f049"}.fa-fast-forward:before{content:"\f050"}.fa-fax:before{content:"\f1ac"}.fa-feather:before{content:"\f52d"}.fa-feather-alt:before{content:"\f56b"}.fa-fedex:before{content:"\f797"}.fa-fedora:before{content:"\f798"}.fa-female:before{content:"\f182"}.fa-fighter-jet:before{content:"\f0fb"}.fa-figma:before{content:"\f799"}.fa-file:before{content:"\f15b"}.fa-file-alt:before{content:"\f15c"}.fa-file-archive:before{content:"\f1c6"}.fa-file-audio:before{content:"\f1c7"}.fa-file-code:before{content:"\f1c9"}.fa-file-contract:before{content:"\f56c"}.fa-file-csv:before{content:"\f6dd"}.fa-file-download:before{content:"\f56d"}.fa-file-excel:before{content:"\f1c3"}.fa-file-export:before{content:"\f56e"}.fa-file-image:before{content:"\f1c5"}.fa-file-import:before{content:"\f56f"}.fa-file-invoice:before{content:"\f570"}.fa-file-invoice-dollar:before{content:"\f571"}.fa-file-medical:before{content:"\f477"}.fa-file-medical-alt:before{content:"\f478"}.fa-file-pdf:before{content:"\f1c1"}.fa-file-powerpoint:before{content:"\f1c4"}.fa-file-prescription:before{content:"\f572"}.fa-file-signature:before{content:"\f573"}.fa-file-upload:before{content:"\f574"}.fa-file-video:before{content:"\f1c8"}.fa-file-word:before{content:"\f1c2"}.fa-fill:before{content:"\f575"}.fa-fill-drip:before{content:"\f576"}.fa-film:before{content:"\f008"}.fa-filter:before{content:"\f0b0"}.fa-fingerprint:before{content:"\f577"}.fa-fire:before{content:"\f06d"}.fa-fire-alt:before{content:"\f7e4"}.fa-fire-extinguisher:before{content:"\f134"}.fa-firefox:before{content:"\f269"}.fa-first-aid:before{content:"\f479"}.fa-first-order:before{content:"\f2b0"}.fa-first-order-alt:before{content:"\f50a"}.fa-firstdraft:before{content:"\f3a1"}.fa-fish:before{content:"\f578"}.fa-fist-raised:before{content:"\f6de"}.fa-flag:before{content:"\f024"}.fa-flag-checkered:before{content:"\f11e"}.fa-flag-usa:before{content:"\f74d"}.fa-flask:before{content:"\f0c3"}.fa-flickr:before{content:"\f16e"}.fa-flipboard:before{content:"\f44d"}.fa-flushed:before{content:"\f579"}.fa-fly:before{content:"\f417"}.fa-folder:before{content:"\f07b"}.fa-folder-minus:before{content:"\f65d"}.fa-folder-open:before{content:"\f07c"}.fa-folder-plus:before{content:"\f65e"}.fa-font:before{content:"\f031"}.fa-font-awesome:before{content:"\f2b4"}.fa-font-awesome-alt:before{content:"\f35c"}.fa-font-awesome-flag:before{content:"\f425"}.fa-font-awesome-logo-full:before{content:"\f4e6"}.fa-fonticons:before{content:"\f280"}.fa-fonticons-fi:before{content:"\f3a2"}.fa-football-ball:before{content:"\f44e"}.fa-fort-awesome:before{content:"\f286"}.fa-fort-awesome-alt:before{content:"\f3a3"}.fa-forumbee:before{content:"\f211"}.fa-forward:before{content:"\f04e"}.fa-foursquare:before{content:"\f180"}.fa-free-code-camp:before{content:"\f2c5"}.fa-freebsd:before{content:"\f3a4"}.fa-frog:before{content:"\f52e"}.fa-frown:before{content:"\f119"}.fa-frown-open:before{content:"\f57a"}.fa-fulcrum:before{content:"\f50b"}.fa-funnel-dollar:before{content:"\f662"}.fa-futbol:before{content:"\f1e3"}.fa-galactic-republic:before{content:"\f50c"}.fa-galactic-senate:before{content:"\f50d"}.fa-gamepad:before{content:"\f11b"}.fa-gas-pump:before{content:"\f52f"}.fa-gavel:before{content:"\f0e3"}.fa-gem:before{content:"\f3a5"}.fa-genderless:before{content:"\f22d"}.fa-get-pocket:before{content:"\f265"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-ghost:before{content:"\f6e2"}.fa-gift:before{content:"\f06b"}.fa-gifts:before{content:"\f79c"}.fa-git:before{content:"\f1d3"}.fa-git-alt:before{content:"\f841"}.fa-git-square:before{content:"\f1d2"}.fa-github:before{content:"\f09b"}.fa-github-alt:before{content:"\f113"}.fa-github-square:before{content:"\f092"}.fa-gitkraken:before{content:"\f3a6"}.fa-gitlab:before{content:"\f296"}.fa-gitter:before{content:"\f426"}.fa-glass-cheers:before{content:"\f79f"}.fa-glass-martini:before{content:"\f000"}.fa-glass-martini-alt:before{content:"\f57b"}.fa-glass-whiskey:before{content:"\f7a0"}.fa-glasses:before{content:"\f530"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-globe:before{content:"\f0ac"}.fa-globe-africa:before{content:"\f57c"}.fa-globe-americas:before{content:"\f57d"}.fa-globe-asia:before{content:"\f57e"}.fa-globe-europe:before{content:"\f7a2"}.fa-gofore:before{content:"\f3a7"}.fa-golf-ball:before{content:"\f450"}.fa-goodreads:before{content:"\f3a8"}.fa-goodreads-g:before{content:"\f3a9"}.fa-google:before{content:"\f1a0"}.fa-google-drive:before{content:"\f3aa"}.fa-google-play:before{content:"\f3ab"}.fa-google-plus:before{content:"\f2b3"}.fa-google-plus-g:before{content:"\f0d5"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-wallet:before{content:"\f1ee"}.fa-gopuram:before{content:"\f664"}.fa-graduation-cap:before{content:"\f19d"}.fa-gratipay:before{content:"\f184"}.fa-grav:before{content:"\f2d6"}.fa-greater-than:before{content:"\f531"}.fa-greater-than-equal:before{content:"\f532"}.fa-grimace:before{content:"\f57f"}.fa-grin:before{content:"\f580"}.fa-grin-alt:before{content:"\f581"}.fa-grin-beam:before{content:"\f582"}.fa-grin-beam-sweat:before{content:"\f583"}.fa-grin-hearts:before{content:"\f584"}.fa-grin-squint:before{content:"\f585"}.fa-grin-squint-tears:before{content:"\f586"}.fa-grin-stars:before{content:"\f587"}.fa-grin-tears:before{content:"\f588"}.fa-grin-tongue:before{content:"\f589"}.fa-grin-tongue-squint:before{content:"\f58a"}.fa-grin-tongue-wink:before{content:"\f58b"}.fa-grin-wink:before{content:"\f58c"}.fa-grip-horizontal:before{content:"\f58d"}.fa-grip-lines:before{content:"\f7a4"}.fa-grip-lines-vertical:before{content:"\f7a5"}.fa-grip-vertical:before{content:"\f58e"}.fa-gripfire:before{content:"\f3ac"}.fa-grunt:before{content:"\f3ad"}.fa-guitar:before{content:"\f7a6"}.fa-gulp:before{content:"\f3ae"}.fa-h-square:before{content:"\f0fd"}.fa-hacker-news:before{content:"\f1d4"}.fa-hacker-news-square:before{content:"\f3af"}.fa-hackerrank:before{content:"\f5f7"}.fa-hamburger:before{content:"\f805"}.fa-hammer:before{content:"\f6e3"}.fa-hamsa:before{content:"\f665"}.fa-hand-holding:before{content:"\f4bd"}.fa-hand-holding-heart:before{content:"\f4be"}.fa-hand-holding-usd:before{content:"\f4c0"}.fa-hand-lizard:before{content:"\f258"}.fa-hand-middle-finger:before{content:"\f806"}.fa-hand-paper:before{content:"\f256"}.fa-hand-peace:before{content:"\f25b"}.fa-hand-point-down:before{content:"\f0a7"}.fa-hand-point-left:before{content:"\f0a5"}.fa-hand-point-right:before{content:"\f0a4"}.fa-hand-point-up:before{content:"\f0a6"}.fa-hand-pointer:before{content:"\f25a"}.fa-hand-rock:before{content:"\f255"}.fa-hand-scissors:before{content:"\f257"}.fa-hand-spock:before{content:"\f259"}.fa-hands:before{content:"\f4c2"}.fa-hands-helping:before{content:"\f4c4"}.fa-handshake:before{content:"\f2b5"}.fa-hanukiah:before{content:"\f6e6"}.fa-hard-hat:before{content:"\f807"}.fa-hashtag:before{content:"\f292"}.fa-hat-wizard:before{content:"\f6e8"}.fa-haykal:before{content:"\f666"}.fa-hdd:before{content:"\f0a0"}.fa-heading:before{content:"\f1dc"}.fa-headphones:before{content:"\f025"}.fa-headphones-alt:before{content:"\f58f"}.fa-headset:before{content:"\f590"}.fa-heart:before{content:"\f004"}.fa-heart-broken:before{content:"\f7a9"}.fa-heartbeat:before{content:"\f21e"}.fa-helicopter:before{content:"\f533"}.fa-highlighter:before{content:"\f591"}.fa-hiking:before{content:"\f6ec"}.fa-hippo:before{content:"\f6ed"}.fa-hips:before{content:"\f452"}.fa-hire-a-helper:before{content:"\f3b0"}.fa-history:before{content:"\f1da"}.fa-hockey-puck:before{content:"\f453"}.fa-holly-berry:before{content:"\f7aa"}.fa-home:before{content:"\f015"}.fa-hooli:before{content:"\f427"}.fa-hornbill:before{content:"\f592"}.fa-horse:before{content:"\f6f0"}.fa-horse-head:before{content:"\f7ab"}.fa-hospital:before{content:"\f0f8"}.fa-hospital-alt:before{content:"\f47d"}.fa-hospital-symbol:before{content:"\f47e"}.fa-hot-tub:before{content:"\f593"}.fa-hotdog:before{content:"\f80f"}.fa-hotel:before{content:"\f594"}.fa-hotjar:before{content:"\f3b1"}.fa-hourglass:before{content:"\f254"}.fa-hourglass-end:before{content:"\f253"}.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-start:before{content:"\f251"}.fa-house-damage:before{content:"\f6f1"}.fa-houzz:before{content:"\f27c"}.fa-hryvnia:before{content:"\f6f2"}.fa-html5:before{content:"\f13b"}.fa-hubspot:before{content:"\f3b2"}.fa-i-cursor:before{content:"\f246"}.fa-ice-cream:before{content:"\f810"}.fa-icicles:before{content:"\f7ad"}.fa-icons:before{content:"\f86d"}.fa-id-badge:before{content:"\f2c1"}.fa-id-card:before{content:"\f2c2"}.fa-id-card-alt:before{content:"\f47f"}.fa-igloo:before{content:"\f7ae"}.fa-image:before{content:"\f03e"}.fa-images:before{content:"\f302"}.fa-imdb:before{content:"\f2d8"}.fa-inbox:before{content:"\f01c"}.fa-indent:before{content:"\f03c"}.fa-industry:before{content:"\f275"}.fa-infinity:before{content:"\f534"}.fa-info:before{content:"\f129"}.fa-info-circle:before{content:"\f05a"}.fa-instagram:before{content:"\f16d"}.fa-intercom:before{content:"\f7af"}.fa-internet-explorer:before{content:"\f26b"}.fa-invision:before{content:"\f7b0"}.fa-ioxhost:before{content:"\f208"}.fa-italic:before{content:"\f033"}.fa-itch-io:before{content:"\f83a"}.fa-itunes:before{content:"\f3b4"}.fa-itunes-note:before{content:"\f3b5"}.fa-java:before{content:"\f4e4"}.fa-jedi:before{content:"\f669"}.fa-jedi-order:before{content:"\f50e"}.fa-jenkins:before{content:"\f3b6"}.fa-jira:before{content:"\f7b1"}.fa-joget:before{content:"\f3b7"}.fa-joint:before{content:"\f595"}.fa-joomla:before{content:"\f1aa"}.fa-journal-whills:before{content:"\f66a"}.fa-js:before{content:"\f3b8"}.fa-js-square:before{content:"\f3b9"}.fa-jsfiddle:before{content:"\f1cc"}.fa-kaaba:before{content:"\f66b"}.fa-kaggle:before{content:"\f5fa"}.fa-key:before{content:"\f084"}.fa-keybase:before{content:"\f4f5"}.fa-keyboard:before{content:"\f11c"}.fa-keycdn:before{content:"\f3ba"}.fa-khanda:before{content:"\f66d"}.fa-kickstarter:before{content:"\f3bb"}.fa-kickstarter-k:before{content:"\f3bc"}.fa-kiss:before{content:"\f596"}.fa-kiss-beam:before{content:"\f597"}.fa-kiss-wink-heart:before{content:"\f598"}.fa-kiwi-bird:before{content:"\f535"}.fa-korvue:before{content:"\f42f"}.fa-landmark:before{content:"\f66f"}.fa-language:before{content:"\f1ab"}.fa-laptop:before{content:"\f109"}.fa-laptop-code:before{content:"\f5fc"}.fa-laptop-medical:before{content:"\f812"}.fa-laravel:before{content:"\f3bd"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-laugh:before{content:"\f599"}.fa-laugh-beam:before{content:"\f59a"}.fa-laugh-squint:before{content:"\f59b"}.fa-laugh-wink:before{content:"\f59c"}.fa-layer-group:before{content:"\f5fd"}.fa-leaf:before{content:"\f06c"}.fa-leanpub:before{content:"\f212"}.fa-lemon:before{content:"\f094"}.fa-less:before{content:"\f41d"}.fa-less-than:before{content:"\f536"}.fa-less-than-equal:before{content:"\f537"}.fa-level-down-alt:before{content:"\f3be"}.fa-level-up-alt:before{content:"\f3bf"}.fa-life-ring:before{content:"\f1cd"}.fa-lightbulb:before{content:"\f0eb"}.fa-line:before{content:"\f3c0"}.fa-link:before{content:"\f0c1"}.fa-linkedin:before{content:"\f08c"}.fa-linkedin-in:before{content:"\f0e1"}.fa-linode:before{content:"\f2b8"}.fa-linux:before{content:"\f17c"}.fa-lira-sign:before{content:"\f195"}.fa-list:before{content:"\f03a"}.fa-list-alt:before{content:"\f022"}.fa-list-ol:before{content:"\f0cb"}.fa-list-ul:before{content:"\f0ca"}.fa-location-arrow:before{content:"\f124"}.fa-lock:before{content:"\f023"}.fa-lock-open:before{content:"\f3c1"}.fa-long-arrow-alt-down:before{content:"\f309"}.fa-long-arrow-alt-left:before{content:"\f30a"}.fa-long-arrow-alt-right:before{content:"\f30b"}.fa-long-arrow-alt-up:before{content:"\f30c"}.fa-low-vision:before{content:"\f2a8"}.fa-luggage-cart:before{content:"\f59d"}.fa-lyft:before{content:"\f3c3"}.fa-magento:before{content:"\f3c4"}.fa-magic:before{content:"\f0d0"}.fa-magnet:before{content:"\f076"}.fa-mail-bulk:before{content:"\f674"}.fa-mailchimp:before{content:"\f59e"}.fa-male:before{content:"\f183"}.fa-mandalorian:before{content:"\f50f"}.fa-map:before{content:"\f279"}.fa-map-marked:before{content:"\f59f"}.fa-map-marked-alt:before{content:"\f5a0"}.fa-map-marker:before{content:"\f041"}.fa-map-marker-alt:before{content:"\f3c5"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-markdown:before{content:"\f60f"}.fa-marker:before{content:"\f5a1"}.fa-mars:before{content:"\f222"}.fa-mars-double:before{content:"\f227"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mask:before{content:"\f6fa"}.fa-mastodon:before{content:"\f4f6"}.fa-maxcdn:before{content:"\f136"}.fa-medal:before{content:"\f5a2"}.fa-medapps:before{content:"\f3c6"}.fa-medium:before{content:"\f23a"}.fa-medium-m:before{content:"\f3c7"}.fa-medkit:before{content:"\f0fa"}.fa-medrt:before{content:"\f3c8"}.fa-meetup:before{content:"\f2e0"}.fa-megaport:before{content:"\f5a3"}.fa-meh:before{content:"\f11a"}.fa-meh-blank:before{content:"\f5a4"}.fa-meh-rolling-eyes:before{content:"\f5a5"}.fa-memory:before{content:"\f538"}.fa-mendeley:before{content:"\f7b3"}.fa-menorah:before{content:"\f676"}.fa-mercury:before{content:"\f223"}.fa-meteor:before{content:"\f753"}.fa-microchip:before{content:"\f2db"}.fa-microphone:before{content:"\f130"}.fa-microphone-alt:before{content:"\f3c9"}.fa-microphone-alt-slash:before{content:"\f539"}.fa-microphone-slash:before{content:"\f131"}.fa-microscope:before{content:"\f610"}.fa-microsoft:before{content:"\f3ca"}.fa-minus:before{content:"\f068"}.fa-minus-circle:before{content:"\f056"}.fa-minus-square:before{content:"\f146"}.fa-mitten:before{content:"\f7b5"}.fa-mix:before{content:"\f3cb"}.fa-mixcloud:before{content:"\f289"}.fa-mizuni:before{content:"\f3cc"}.fa-mobile:before{content:"\f10b"}.fa-mobile-alt:before{content:"\f3cd"}.fa-modx:before{content:"\f285"}.fa-monero:before{content:"\f3d0"}.fa-money-bill:before{content:"\f0d6"}.fa-money-bill-alt:before{content:"\f3d1"}.fa-money-bill-wave:before{content:"\f53a"}.fa-money-bill-wave-alt:before{content:"\f53b"}.fa-money-check:before{content:"\f53c"}.fa-money-check-alt:before{content:"\f53d"}.fa-monument:before{content:"\f5a6"}.fa-moon:before{content:"\f186"}.fa-mortar-pestle:before{content:"\f5a7"}.fa-mosque:before{content:"\f678"}.fa-motorcycle:before{content:"\f21c"}.fa-mountain:before{content:"\f6fc"}.fa-mouse-pointer:before{content:"\f245"}.fa-mug-hot:before{content:"\f7b6"}.fa-music:before{content:"\f001"}.fa-napster:before{content:"\f3d2"}.fa-neos:before{content:"\f612"}.fa-network-wired:before{content:"\f6ff"}.fa-neuter:before{content:"\f22c"}.fa-newspaper:before{content:"\f1ea"}.fa-nimblr:before{content:"\f5a8"}.fa-node:before{content:"\f419"}.fa-node-js:before{content:"\f3d3"}.fa-not-equal:before{content:"\f53e"}.fa-notes-medical:before{content:"\f481"}.fa-npm:before{content:"\f3d4"}.fa-ns8:before{content:"\f3d5"}.fa-nutritionix:before{content:"\f3d6"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-oil-can:before{content:"\f613"}.fa-old-republic:before{content:"\f510"}.fa-om:before{content:"\f679"}.fa-opencart:before{content:"\f23d"}.fa-openid:before{content:"\f19b"}.fa-opera:before{content:"\f26a"}.fa-optin-monster:before{content:"\f23c"}.fa-osi:before{content:"\f41a"}.fa-otter:before{content:"\f700"}.fa-outdent:before{content:"\f03b"}.fa-page4:before{content:"\f3d7"}.fa-pagelines:before{content:"\f18c"}.fa-pager:before{content:"\f815"}.fa-paint-brush:before{content:"\f1fc"}.fa-paint-roller:before{content:"\f5aa"}.fa-palette:before{content:"\f53f"}.fa-palfed:before{content:"\f3d8"}.fa-pallet:before{content:"\f482"}.fa-paper-plane:before{content:"\f1d8"}.fa-paperclip:before{content:"\f0c6"}.fa-parachute-box:before{content:"\f4cd"}.fa-paragraph:before{content:"\f1dd"}.fa-parking:before{content:"\f540"}.fa-passport:before{content:"\f5ab"}.fa-pastafarianism:before{content:"\f67b"}.fa-paste:before{content:"\f0ea"}.fa-patreon:before{content:"\f3d9"}.fa-pause:before{content:"\f04c"}.fa-pause-circle:before{content:"\f28b"}.fa-paw:before{content:"\f1b0"}.fa-paypal:before{content:"\f1ed"}.fa-peace:before{content:"\f67c"}.fa-pen:before{content:"\f304"}.fa-pen-alt:before{content:"\f305"}.fa-pen-fancy:before{content:"\f5ac"}.fa-pen-nib:before{content:"\f5ad"}.fa-pen-square:before{content:"\f14b"}.fa-pencil-alt:before{content:"\f303"}.fa-pencil-ruler:before{content:"\f5ae"}.fa-penny-arcade:before{content:"\f704"}.fa-people-carry:before{content:"\f4ce"}.fa-pepper-hot:before{content:"\f816"}.fa-percent:before{content:"\f295"}.fa-percentage:before{content:"\f541"}.fa-periscope:before{content:"\f3da"}.fa-person-booth:before{content:"\f756"}.fa-phabricator:before{content:"\f3db"}.fa-phoenix-framework:before{content:"\f3dc"}.fa-phoenix-squadron:before{content:"\f511"}.fa-phone:before{content:"\f095"}.fa-phone-alt:before{content:"\f879"}.fa-phone-slash:before{content:"\f3dd"}.fa-phone-square:before{content:"\f098"}.fa-phone-square-alt:before{content:"\f87b"}.fa-phone-volume:before{content:"\f2a0"}.fa-photo-video:before{content:"\f87c"}.fa-php:before{content:"\f457"}.fa-pied-piper:before{content:"\f2ae"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-pied-piper-hat:before{content:"\f4e5"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-piggy-bank:before{content:"\f4d3"}.fa-pills:before{content:"\f484"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-p:before{content:"\f231"}.fa-pinterest-square:before{content:"\f0d3"}.fa-pizza-slice:before{content:"\f818"}.fa-place-of-worship:before{content:"\f67f"}.fa-plane:before{content:"\f072"}.fa-plane-arrival:before{content:"\f5af"}.fa-plane-departure:before{content:"\f5b0"}.fa-play:before{content:"\f04b"}.fa-play-circle:before{content:"\f144"}.fa-playstation:before{content:"\f3df"}.fa-plug:before{content:"\f1e6"}.fa-plus:before{content:"\f067"}.fa-plus-circle:before{content:"\f055"}.fa-plus-square:before{content:"\f0fe"}.fa-podcast:before{content:"\f2ce"}.fa-poll:before{content:"\f681"}.fa-poll-h:before{content:"\f682"}.fa-poo:before{content:"\f2fe"}.fa-poo-storm:before{content:"\f75a"}.fa-poop:before{content:"\f619"}.fa-portrait:before{content:"\f3e0"}.fa-pound-sign:before{content:"\f154"}.fa-power-off:before{content:"\f011"}.fa-pray:before{content:"\f683"}.fa-praying-hands:before{content:"\f684"}.fa-prescription:before{content:"\f5b1"}.fa-prescription-bottle:before{content:"\f485"}.fa-prescription-bottle-alt:before{content:"\f486"}.fa-print:before{content:"\f02f"}.fa-procedures:before{content:"\f487"}.fa-product-hunt:before{content:"\f288"}.fa-project-diagram:before{content:"\f542"}.fa-pushed:before{content:"\f3e1"}.fa-puzzle-piece:before{content:"\f12e"}.fa-python:before{content:"\f3e2"}.fa-qq:before{content:"\f1d6"}.fa-qrcode:before{content:"\f029"}.fa-question:before{content:"\f128"}.fa-question-circle:before{content:"\f059"}.fa-quidditch:before{content:"\f458"}.fa-quinscape:before{content:"\f459"}.fa-quora:before{content:"\f2c4"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-quran:before{content:"\f687"}.fa-r-project:before{content:"\f4f7"}.fa-radiation:before{content:"\f7b9"}.fa-radiation-alt:before{content:"\f7ba"}.fa-rainbow:before{content:"\f75b"}.fa-random:before{content:"\f074"}.fa-raspberry-pi:before{content:"\f7bb"}.fa-ravelry:before{content:"\f2d9"}.fa-react:before{content:"\f41b"}.fa-reacteurope:before{content:"\f75d"}.fa-readme:before{content:"\f4d5"}.fa-rebel:before{content:"\f1d0"}.fa-receipt:before{content:"\f543"}.fa-recycle:before{content:"\f1b8"}.fa-red-river:before{content:"\f3e3"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-alien:before{content:"\f281"}.fa-reddit-square:before{content:"\f1a2"}.fa-redhat:before{content:"\f7bc"}.fa-redo:before{content:"\f01e"}.fa-redo-alt:before{content:"\f2f9"}.fa-registered:before{content:"\f25d"}.fa-remove-format:before{content:"\f87d"}.fa-renren:before{content:"\f18b"}.fa-reply:before{content:"\f3e5"}.fa-reply-all:before{content:"\f122"}.fa-replyd:before{content:"\f3e6"}.fa-republican:before{content:"\f75e"}.fa-researchgate:before{content:"\f4f8"}.fa-resolving:before{content:"\f3e7"}.fa-restroom:before{content:"\f7bd"}.fa-retweet:before{content:"\f079"}.fa-rev:before{content:"\f5b2"}.fa-ribbon:before{content:"\f4d6"}.fa-ring:before{content:"\f70b"}.fa-road:before{content:"\f018"}.fa-robot:before{content:"\f544"}.fa-rocket:before{content:"\f135"}.fa-rocketchat:before{content:"\f3e8"}.fa-rockrms:before{content:"\f3e9"}.fa-route:before{content:"\f4d7"}.fa-rss:before{content:"\f09e"}.fa-rss-square:before{content:"\f143"}.fa-ruble-sign:before{content:"\f158"}.fa-ruler:before{content:"\f545"}.fa-ruler-combined:before{content:"\f546"}.fa-ruler-horizontal:before{content:"\f547"}.fa-ruler-vertical:before{content:"\f548"}.fa-running:before{content:"\f70c"}.fa-rupee-sign:before{content:"\f156"}.fa-sad-cry:before{content:"\f5b3"}.fa-sad-tear:before{content:"\f5b4"}.fa-safari:before{content:"\f267"}.fa-salesforce:before{content:"\f83b"}.fa-sass:before{content:"\f41e"}.fa-satellite:before{content:"\f7bf"}.fa-satellite-dish:before{content:"\f7c0"}.fa-save:before{content:"\f0c7"}.fa-schlix:before{content:"\f3ea"}.fa-school:before{content:"\f549"}.fa-screwdriver:before{content:"\f54a"}.fa-scribd:before{content:"\f28a"}.fa-scroll:before{content:"\f70e"}.fa-sd-card:before{content:"\f7c2"}.fa-search:before{content:"\f002"}.fa-search-dollar:before{content:"\f688"}.fa-search-location:before{content:"\f689"}.fa-search-minus:before{content:"\f010"}.fa-search-plus:before{content:"\f00e"}.fa-searchengin:before{content:"\f3eb"}.fa-seedling:before{content:"\f4d8"}.fa-sellcast:before{content:"\f2da"}.fa-sellsy:before{content:"\f213"}.fa-server:before{content:"\f233"}.fa-servicestack:before{content:"\f3ec"}.fa-shapes:before{content:"\f61f"}.fa-share:before{content:"\f064"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-share-square:before{content:"\f14d"}.fa-shekel-sign:before{content:"\f20b"}.fa-shield-alt:before{content:"\f3ed"}.fa-ship:before{content:"\f21a"}.fa-shipping-fast:before{content:"\f48b"}.fa-shirtsinbulk:before{content:"\f214"}.fa-shoe-prints:before{content:"\f54b"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-shopping-cart:before{content:"\f07a"}.fa-shopware:before{content:"\f5b5"}.fa-shower:before{content:"\f2cc"}.fa-shuttle-van:before{content:"\f5b6"}.fa-sign:before{content:"\f4d9"}.fa-sign-in-alt:before{content:"\f2f6"}.fa-sign-language:before{content:"\f2a7"}.fa-sign-out-alt:before{content:"\f2f5"}.fa-signal:before{content:"\f012"}.fa-signature:before{content:"\f5b7"}.fa-sim-card:before{content:"\f7c4"}.fa-simplybuilt:before{content:"\f215"}.fa-sistrix:before{content:"\f3ee"}.fa-sitemap:before{content:"\f0e8"}.fa-sith:before{content:"\f512"}.fa-skating:before{content:"\f7c5"}.fa-sketch:before{content:"\f7c6"}.fa-skiing:before{content:"\f7c9"}.fa-skiing-nordic:before{content:"\f7ca"}.fa-skull:before{content:"\f54c"}.fa-skull-crossbones:before{content:"\f714"}.fa-skyatlas:before{content:"\f216"}.fa-skype:before{content:"\f17e"}.fa-slack:before{content:"\f198"}.fa-slack-hash:before{content:"\f3ef"}.fa-slash:before{content:"\f715"}.fa-sleigh:before{content:"\f7cc"}.fa-sliders-h:before{content:"\f1de"}.fa-slideshare:before{content:"\f1e7"}.fa-smile:before{content:"\f118"}.fa-smile-beam:before{content:"\f5b8"}.fa-smile-wink:before{content:"\f4da"}.fa-smog:before{content:"\f75f"}.fa-smoking:before{content:"\f48d"}.fa-smoking-ban:before{content:"\f54d"}.fa-sms:before{content:"\f7cd"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-snowboarding:before{content:"\f7ce"}.fa-snowflake:before{content:"\f2dc"}.fa-snowman:before{content:"\f7d0"}.fa-snowplow:before{content:"\f7d2"}.fa-socks:before{content:"\f696"}.fa-solar-panel:before{content:"\f5ba"}.fa-sort:before{content:"\f0dc"}.fa-sort-alpha-down:before{content:"\f15d"}.fa-sort-alpha-down-alt:before{content:"\f881"}.fa-sort-alpha-up:before{content:"\f15e"}.fa-sort-alpha-up-alt:before{content:"\f882"}.fa-sort-amount-down:before{content:"\f160"}.fa-sort-amount-down-alt:before{content:"\f884"}.fa-sort-amount-up:before{content:"\f161"}.fa-sort-amount-up-alt:before{content:"\f885"}.fa-sort-down:before{content:"\f0dd"}.fa-sort-numeric-down:before{content:"\f162"}.fa-sort-numeric-down-alt:before{content:"\f886"}.fa-sort-numeric-up:before{content:"\f163"}.fa-sort-numeric-up-alt:before{content:"\f887"}.fa-sort-up:before{content:"\f0de"}.fa-soundcloud:before{content:"\f1be"}.fa-sourcetree:before{content:"\f7d3"}.fa-spa:before{content:"\f5bb"}.fa-space-shuttle:before{content:"\f197"}.fa-speakap:before{content:"\f3f3"}.fa-speaker-deck:before{content:"\f83c"}.fa-spell-check:before{content:"\f891"}.fa-spider:before{content:"\f717"}.fa-spinner:before{content:"\f110"}.fa-splotch:before{content:"\f5bc"}.fa-spotify:before{content:"\f1bc"}.fa-spray-can:before{content:"\f5bd"}.fa-square:before{content:"\f0c8"}.fa-square-full:before{content:"\f45c"}.fa-square-root-alt:before{content:"\f698"}.fa-squarespace:before{content:"\f5be"}.fa-stack-exchange:before{content:"\f18d"}.fa-stack-overflow:before{content:"\f16c"}.fa-stackpath:before{content:"\f842"}.fa-stamp:before{content:"\f5bf"}.fa-star:before{content:"\f005"}.fa-star-and-crescent:before{content:"\f699"}.fa-star-half:before{content:"\f089"}.fa-star-half-alt:before{content:"\f5c0"}.fa-star-of-david:before{content:"\f69a"}.fa-star-of-life:before{content:"\f621"}.fa-staylinked:before{content:"\f3f5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-steam-symbol:before{content:"\f3f6"}.fa-step-backward:before{content:"\f048"}.fa-step-forward:before{content:"\f051"}.fa-stethoscope:before{content:"\f0f1"}.fa-sticker-mule:before{content:"\f3f7"}.fa-sticky-note:before{content:"\f249"}.fa-stop:before{content:"\f04d"}.fa-stop-circle:before{content:"\f28d"}.fa-stopwatch:before{content:"\f2f2"}.fa-store:before{content:"\f54e"}.fa-store-alt:before{content:"\f54f"}.fa-strava:before{content:"\f428"}.fa-stream:before{content:"\f550"}.fa-street-view:before{content:"\f21d"}.fa-strikethrough:before{content:"\f0cc"}.fa-stripe:before{content:"\f429"}.fa-stripe-s:before{content:"\f42a"}.fa-stroopwafel:before{content:"\f551"}.fa-studiovinari:before{content:"\f3f8"}.fa-stumbleupon:before{content:"\f1a4"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-subscript:before{content:"\f12c"}.fa-subway:before{content:"\f239"}.fa-suitcase:before{content:"\f0f2"}.fa-suitcase-rolling:before{content:"\f5c1"}.fa-sun:before{content:"\f185"}.fa-superpowers:before{content:"\f2dd"}.fa-superscript:before{content:"\f12b"}.fa-supple:before{content:"\f3f9"}.fa-surprise:before{content:"\f5c2"}.fa-suse:before{content:"\f7d6"}.fa-swatchbook:before{content:"\f5c3"}.fa-swimmer:before{content:"\f5c4"}.fa-swimming-pool:before{content:"\f5c5"}.fa-symfony:before{content:"\f83d"}.fa-synagogue:before{content:"\f69b"}.fa-sync:before{content:"\f021"}.fa-sync-alt:before{content:"\f2f1"}.fa-syringe:before{content:"\f48e"}.fa-table:before{content:"\f0ce"}.fa-table-tennis:before{content:"\f45d"}.fa-tablet:before{content:"\f10a"}.fa-tablet-alt:before{content:"\f3fa"}.fa-tablets:before{content:"\f490"}.fa-tachometer-alt:before{content:"\f3fd"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-tape:before{content:"\f4db"}.fa-tasks:before{content:"\f0ae"}.fa-taxi:before{content:"\f1ba"}.fa-teamspeak:before{content:"\f4f9"}.fa-teeth:before{content:"\f62e"}.fa-teeth-open:before{content:"\f62f"}.fa-telegram:before{content:"\f2c6"}.fa-telegram-plane:before{content:"\f3fe"}.fa-temperature-high:before{content:"\f769"}.fa-temperature-low:before{content:"\f76b"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-tenge:before{content:"\f7d7"}.fa-terminal:before{content:"\f120"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-th:before{content:"\f00a"}.fa-th-large:before{content:"\f009"}.fa-th-list:before{content:"\f00b"}.fa-the-red-yeti:before{content:"\f69d"}.fa-theater-masks:before{content:"\f630"}.fa-themeco:before{content:"\f5c6"}.fa-themeisle:before{content:"\f2b2"}.fa-thermometer:before{content:"\f491"}.fa-thermometer-empty:before{content:"\f2cb"}.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-think-peaks:before{content:"\f731"}.fa-thumbs-down:before{content:"\f165"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbtack:before{content:"\f08d"}.fa-ticket-alt:before{content:"\f3ff"}.fa-times:before{content:"\f00d"}.fa-times-circle:before{content:"\f057"}.fa-tint:before{content:"\f043"}.fa-tint-slash:before{content:"\f5c7"}.fa-tired:before{content:"\f5c8"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-toilet:before{content:"\f7d8"}.fa-toilet-paper:before{content:"\f71e"}.fa-toolbox:before{content:"\f552"}.fa-tools:before{content:"\f7d9"}.fa-tooth:before{content:"\f5c9"}.fa-torah:before{content:"\f6a0"}.fa-torii-gate:before{content:"\f6a1"}.fa-tractor:before{content:"\f722"}.fa-trade-federation:before{content:"\f513"}.fa-trademark:before{content:"\f25c"}.fa-traffic-light:before{content:"\f637"}.fa-train:before{content:"\f238"}.fa-tram:before{content:"\f7da"}.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-trash:before{content:"\f1f8"}.fa-trash-alt:before{content:"\f2ed"}.fa-trash-restore:before{content:"\f829"}.fa-trash-restore-alt:before{content:"\f82a"}.fa-tree:before{content:"\f1bb"}.fa-trello:before{content:"\f181"}.fa-tripadvisor:before{content:"\f262"}.fa-trophy:before{content:"\f091"}.fa-truck:before{content:"\f0d1"}.fa-truck-loading:before{content:"\f4de"}.fa-truck-monster:before{content:"\f63b"}.fa-truck-moving:before{content:"\f4df"}.fa-truck-pickup:before{content:"\f63c"}.fa-tshirt:before{content:"\f553"}.fa-tty:before{content:"\f1e4"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-tv:before{content:"\f26c"}.fa-twitch:before{content:"\f1e8"}.fa-twitter:before{content:"\f099"}.fa-twitter-square:before{content:"\f081"}.fa-typo3:before{content:"\f42b"}.fa-uber:before{content:"\f402"}.fa-ubuntu:before{content:"\f7df"}.fa-uikit:before{content:"\f403"}.fa-umbrella:before{content:"\f0e9"}.fa-umbrella-beach:before{content:"\f5ca"}.fa-underline:before{content:"\f0cd"}.fa-undo:before{content:"\f0e2"}.fa-undo-alt:before{content:"\f2ea"}.fa-uniregistry:before{content:"\f404"}.fa-universal-access:before{content:"\f29a"}.fa-university:before{content:"\f19c"}.fa-unlink:before{content:"\f127"}.fa-unlock:before{content:"\f09c"}.fa-unlock-alt:before{content:"\f13e"}.fa-untappd:before{content:"\f405"}.fa-upload:before{content:"\f093"}.fa-ups:before{content:"\f7e0"}.fa-usb:before{content:"\f287"}.fa-user:before{content:"\f007"}.fa-user-alt:before{content:"\f406"}.fa-user-alt-slash:before{content:"\f4fa"}.fa-user-astronaut:before{content:"\f4fb"}.fa-user-check:before{content:"\f4fc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-clock:before{content:"\f4fd"}.fa-user-cog:before{content:"\f4fe"}.fa-user-edit:before{content:"\f4ff"}.fa-user-friends:before{content:"\f500"}.fa-user-graduate:before{content:"\f501"}.fa-user-injured:before{content:"\f728"}.fa-user-lock:before{content:"\f502"}.fa-user-md:before{content:"\f0f0"}.fa-user-minus:before{content:"\f503"}.fa-user-ninja:before{content:"\f504"}.fa-user-nurse:before{content:"\f82f"}.fa-user-plus:before{content:"\f234"}.fa-user-secret:before{content:"\f21b"}.fa-user-shield:before{content:"\f505"}.fa-user-slash:before{content:"\f506"}.fa-user-tag:before{content:"\f507"}.fa-user-tie:before{content:"\f508"}.fa-user-times:before{content:"\f235"}.fa-users:before{content:"\f0c0"}.fa-users-cog:before{content:"\f509"}.fa-usps:before{content:"\f7e1"}.fa-ussunnah:before{content:"\f407"}.fa-utensil-spoon:before{content:"\f2e5"}.fa-utensils:before{content:"\f2e7"}.fa-vaadin:before{content:"\f408"}.fa-vector-square:before{content:"\f5cb"}.fa-venus:before{content:"\f221"}.fa-venus-double:before{content:"\f226"}.fa-venus-mars:before{content:"\f228"}.fa-viacoin:before{content:"\f237"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-vial:before{content:"\f492"}.fa-vials:before{content:"\f493"}.fa-viber:before{content:"\f409"}.fa-video:before{content:"\f03d"}.fa-video-slash:before{content:"\f4e2"}.fa-vihara:before{content:"\f6a7"}.fa-vimeo:before{content:"\f40a"}.fa-vimeo-square:before{content:"\f194"}.fa-vimeo-v:before{content:"\f27d"}.fa-vine:before{content:"\f1ca"}.fa-vk:before{content:"\f189"}.fa-vnv:before{content:"\f40b"}.fa-voicemail:before{content:"\f897"}.fa-volleyball-ball:before{content:"\f45f"}.fa-volume-down:before{content:"\f027"}.fa-volume-mute:before{content:"\f6a9"}.fa-volume-off:before{content:"\f026"}.fa-volume-up:before{content:"\f028"}.fa-vote-yea:before{content:"\f772"}.fa-vr-cardboard:before{content:"\f729"}.fa-vuejs:before{content:"\f41f"}.fa-walking:before{content:"\f554"}.fa-wallet:before{content:"\f555"}.fa-warehouse:before{content:"\f494"}.fa-water:before{content:"\f773"}.fa-wave-square:before{content:"\f83e"}.fa-waze:before{content:"\f83f"}.fa-weebly:before{content:"\f5cc"}.fa-weibo:before{content:"\f18a"}.fa-weight:before{content:"\f496"}.fa-weight-hanging:before{content:"\f5cd"}.fa-weixin:before{content:"\f1d7"}.fa-whatsapp:before{content:"\f232"}.fa-whatsapp-square:before{content:"\f40c"}.fa-wheelchair:before{content:"\f193"}.fa-whmcs:before{content:"\f40d"}.fa-wifi:before{content:"\f1eb"}.fa-wikipedia-w:before{content:"\f266"}.fa-wind:before{content:"\f72e"}.fa-window-close:before{content:"\f410"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-windows:before{content:"\f17a"}.fa-wine-bottle:before{content:"\f72f"}.fa-wine-glass:before{content:"\f4e3"}.fa-wine-glass-alt:before{content:"\f5ce"}.fa-wix:before{content:"\f5cf"}.fa-wizards-of-the-coast:before{content:"\f730"}.fa-wolf-pack-battalion:before{content:"\f514"}.fa-won-sign:before{content:"\f159"}.fa-wordpress:before{content:"\f19a"}.fa-wordpress-simple:before{content:"\f411"}.fa-wpbeginner:before{content:"\f297"}.fa-wpexplorer:before{content:"\f2de"}.fa-wpforms:before{content:"\f298"}.fa-wpressr:before{content:"\f3e4"}.fa-wrench:before{content:"\f0ad"}.fa-x-ray:before{content:"\f497"}.fa-xbox:before{content:"\f412"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-y-combinator:before{content:"\f23b"}.fa-yahoo:before{content:"\f19e"}.fa-yammer:before{content:"\f840"}.fa-yandex:before{content:"\f413"}.fa-yandex-international:before{content:"\f414"}.fa-yarn:before{content:"\f7e3"}.fa-yelp:before{content:"\f1e9"}.fa-yen-sign:before{content:"\f157"}.fa-yin-yang:before{content:"\f6ad"}.fa-yoast:before{content:"\f2b1"}.fa-youtube:before{content:"\f167"}.fa-youtube-square:before{content:"\f431"}.fa-zhihu:before{content:"\f63f"}.sr-only{border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.sr-only-focusable:active,.sr-only-focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}@font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:400;font-display:auto;src:url(fa-regular-400.eot);src:url(fa-regular-400.eot?#iefix) format("embedded-opentype"),url(fa-regular-400.woff) format("woff")}.far{font-weight:400}@font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:900;font-display:auto;src:url(fa-solid-900.eot);src:url(fa-solid-900.eot?#iefix) format("embedded-opentype"),url(fa-solid-900.woff) format("woff")}.fa,.far,.fas{font-family:"Font Awesome 5 Free"}.fa,.fas{font-weight:900}
\ No newline at end of file
diff --git a/html/font-awesome/css/v4-shims.min.css b/html/font-awesome/css/v4-shims.min.css
new file mode 100644
index 0000000000..5f3fdc598c
--- /dev/null
+++ b/html/font-awesome/css/v4-shims.min.css
@@ -0,0 +1,5 @@
+/*!
+ * Font Awesome Free 5.9.0 by @fontawesome - https://fontawesome.com
+ * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
+ */
+.fa.fa-glass:before{content:"\f000"}.fa.fa-meetup{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-star-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-star-o:before{content:"\f005"}.fa.fa-close:before,.fa.fa-remove:before{content:"\f00d"}.fa.fa-gear:before{content:"\f013"}.fa.fa-trash-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-trash-o:before{content:"\f2ed"}.fa.fa-file-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-o:before{content:"\f15b"}.fa.fa-clock-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-clock-o:before{content:"\f017"}.fa.fa-arrow-circle-o-down{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-arrow-circle-o-down:before{content:"\f358"}.fa.fa-arrow-circle-o-up{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-arrow-circle-o-up:before{content:"\f35b"}.fa.fa-play-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-play-circle-o:before{content:"\f144"}.fa.fa-repeat:before,.fa.fa-rotate-right:before{content:"\f01e"}.fa.fa-refresh:before{content:"\f021"}.fa.fa-list-alt{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-dedent:before{content:"\f03b"}.fa.fa-video-camera:before{content:"\f03d"}.fa.fa-picture-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-picture-o:before{content:"\f03e"}.fa.fa-photo{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-photo:before{content:"\f03e"}.fa.fa-image{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-image:before{content:"\f03e"}.fa.fa-pencil:before{content:"\f303"}.fa.fa-map-marker:before{content:"\f3c5"}.fa.fa-pencil-square-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-pencil-square-o:before{content:"\f044"}.fa.fa-share-square-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-share-square-o:before{content:"\f14d"}.fa.fa-check-square-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-check-square-o:before{content:"\f14a"}.fa.fa-arrows:before{content:"\f0b2"}.fa.fa-times-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-times-circle-o:before{content:"\f057"}.fa.fa-check-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-check-circle-o:before{content:"\f058"}.fa.fa-mail-forward:before{content:"\f064"}.fa.fa-eye,.fa.fa-eye-slash{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-warning:before{content:"\f071"}.fa.fa-calendar:before{content:"\f073"}.fa.fa-arrows-v:before{content:"\f338"}.fa.fa-arrows-h:before{content:"\f337"}.fa.fa-bar-chart{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-bar-chart:before{content:"\f080"}.fa.fa-bar-chart-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-bar-chart-o:before{content:"\f080"}.fa.fa-facebook-square,.fa.fa-twitter-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-gears:before{content:"\f085"}.fa.fa-thumbs-o-up{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-thumbs-o-up:before{content:"\f164"}.fa.fa-thumbs-o-down{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-thumbs-o-down:before{content:"\f165"}.fa.fa-heart-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-heart-o:before{content:"\f004"}.fa.fa-sign-out:before{content:"\f2f5"}.fa.fa-linkedin-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-linkedin-square:before{content:"\f08c"}.fa.fa-thumb-tack:before{content:"\f08d"}.fa.fa-external-link:before{content:"\f35d"}.fa.fa-sign-in:before{content:"\f2f6"}.fa.fa-github-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-lemon-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-lemon-o:before{content:"\f094"}.fa.fa-square-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-square-o:before{content:"\f0c8"}.fa.fa-bookmark-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-bookmark-o:before{content:"\f02e"}.fa.fa-facebook,.fa.fa-twitter{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-facebook:before{content:"\f39e"}.fa.fa-facebook-f{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-facebook-f:before{content:"\f39e"}.fa.fa-github{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-credit-card{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-feed:before{content:"\f09e"}.fa.fa-hdd-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hdd-o:before{content:"\f0a0"}.fa.fa-hand-o-right{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-o-right:before{content:"\f0a4"}.fa.fa-hand-o-left{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-o-left:before{content:"\f0a5"}.fa.fa-hand-o-up{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-o-up:before{content:"\f0a6"}.fa.fa-hand-o-down{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-o-down:before{content:"\f0a7"}.fa.fa-arrows-alt:before{content:"\f31e"}.fa.fa-group:before{content:"\f0c0"}.fa.fa-chain:before{content:"\f0c1"}.fa.fa-scissors:before{content:"\f0c4"}.fa.fa-files-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-files-o:before{content:"\f0c5"}.fa.fa-floppy-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-floppy-o:before{content:"\f0c7"}.fa.fa-navicon:before,.fa.fa-reorder:before{content:"\f0c9"}.fa.fa-google-plus,.fa.fa-google-plus-square,.fa.fa-pinterest,.fa.fa-pinterest-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-google-plus:before{content:"\f0d5"}.fa.fa-money{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-money:before{content:"\f3d1"}.fa.fa-unsorted:before{content:"\f0dc"}.fa.fa-sort-desc:before{content:"\f0dd"}.fa.fa-sort-asc:before{content:"\f0de"}.fa.fa-linkedin{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-linkedin:before{content:"\f0e1"}.fa.fa-rotate-left:before{content:"\f0e2"}.fa.fa-legal:before{content:"\f0e3"}.fa.fa-dashboard:before,.fa.fa-tachometer:before{content:"\f3fd"}.fa.fa-comment-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-comment-o:before{content:"\f075"}.fa.fa-comments-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-comments-o:before{content:"\f086"}.fa.fa-flash:before{content:"\f0e7"}.fa.fa-clipboard,.fa.fa-paste{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-paste:before{content:"\f328"}.fa.fa-lightbulb-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-lightbulb-o:before{content:"\f0eb"}.fa.fa-exchange:before{content:"\f362"}.fa.fa-cloud-download:before{content:"\f381"}.fa.fa-cloud-upload:before{content:"\f382"}.fa.fa-bell-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-bell-o:before{content:"\f0f3"}.fa.fa-cutlery:before{content:"\f2e7"}.fa.fa-file-text-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-text-o:before{content:"\f15c"}.fa.fa-building-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-building-o:before{content:"\f1ad"}.fa.fa-hospital-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hospital-o:before{content:"\f0f8"}.fa.fa-tablet:before{content:"\f3fa"}.fa.fa-mobile-phone:before,.fa.fa-mobile:before{content:"\f3cd"}.fa.fa-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-circle-o:before{content:"\f111"}.fa.fa-mail-reply:before{content:"\f3e5"}.fa.fa-github-alt{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-folder-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-folder-o:before{content:"\f07b"}.fa.fa-folder-open-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-folder-open-o:before{content:"\f07c"}.fa.fa-smile-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-smile-o:before{content:"\f118"}.fa.fa-frown-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-frown-o:before{content:"\f119"}.fa.fa-meh-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-meh-o:before{content:"\f11a"}.fa.fa-keyboard-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-keyboard-o:before{content:"\f11c"}.fa.fa-flag-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-flag-o:before{content:"\f024"}.fa.fa-mail-reply-all:before{content:"\f122"}.fa.fa-star-half-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-star-half-o:before{content:"\f089"}.fa.fa-star-half-empty{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-star-half-empty:before{content:"\f089"}.fa.fa-star-half-full{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-star-half-full:before{content:"\f089"}.fa.fa-code-fork:before{content:"\f126"}.fa.fa-chain-broken:before{content:"\f127"}.fa.fa-shield:before{content:"\f3ed"}.fa.fa-calendar-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-calendar-o:before{content:"\f133"}.fa.fa-css3,.fa.fa-html5,.fa.fa-maxcdn{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-ticket:before{content:"\f3ff"}.fa.fa-minus-square-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-minus-square-o:before{content:"\f146"}.fa.fa-level-up:before{content:"\f3bf"}.fa.fa-level-down:before{content:"\f3be"}.fa.fa-pencil-square:before{content:"\f14b"}.fa.fa-external-link-square:before{content:"\f360"}.fa.fa-compass{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-caret-square-o-down{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-caret-square-o-down:before{content:"\f150"}.fa.fa-toggle-down{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-toggle-down:before{content:"\f150"}.fa.fa-caret-square-o-up{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-caret-square-o-up:before{content:"\f151"}.fa.fa-toggle-up{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-toggle-up:before{content:"\f151"}.fa.fa-caret-square-o-right{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-caret-square-o-right:before{content:"\f152"}.fa.fa-toggle-right{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-toggle-right:before{content:"\f152"}.fa.fa-eur:before,.fa.fa-euro:before{content:"\f153"}.fa.fa-gbp:before{content:"\f154"}.fa.fa-dollar:before,.fa.fa-usd:before{content:"\f155"}.fa.fa-inr:before,.fa.fa-rupee:before{content:"\f156"}.fa.fa-cny:before,.fa.fa-jpy:before,.fa.fa-rmb:before,.fa.fa-yen:before{content:"\f157"}.fa.fa-rouble:before,.fa.fa-rub:before,.fa.fa-ruble:before{content:"\f158"}.fa.fa-krw:before,.fa.fa-won:before{content:"\f159"}.fa.fa-bitcoin,.fa.fa-btc{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-bitcoin:before{content:"\f15a"}.fa.fa-file-text:before{content:"\f15c"}.fa.fa-sort-alpha-asc:before{content:"\f15d"}.fa.fa-sort-alpha-desc:before{content:"\f15e"}.fa.fa-sort-amount-asc:before{content:"\f160"}.fa.fa-sort-amount-desc:before{content:"\f161"}.fa.fa-sort-numeric-asc:before{content:"\f162"}.fa.fa-sort-numeric-desc:before{content:"\f163"}.fa.fa-xing,.fa.fa-xing-square,.fa.fa-youtube,.fa.fa-youtube-play,.fa.fa-youtube-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-youtube-play:before{content:"\f167"}.fa.fa-adn,.fa.fa-bitbucket,.fa.fa-bitbucket-square,.fa.fa-dropbox,.fa.fa-flickr,.fa.fa-instagram,.fa.fa-stack-overflow{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-bitbucket-square:before{content:"\f171"}.fa.fa-tumblr,.fa.fa-tumblr-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-long-arrow-down:before{content:"\f309"}.fa.fa-long-arrow-up:before{content:"\f30c"}.fa.fa-long-arrow-left:before{content:"\f30a"}.fa.fa-long-arrow-right:before{content:"\f30b"}.fa.fa-android,.fa.fa-apple,.fa.fa-dribbble,.fa.fa-foursquare,.fa.fa-gittip,.fa.fa-gratipay,.fa.fa-linux,.fa.fa-skype,.fa.fa-trello,.fa.fa-windows{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-gittip:before{content:"\f184"}.fa.fa-sun-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-sun-o:before{content:"\f185"}.fa.fa-moon-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-moon-o:before{content:"\f186"}.fa.fa-pagelines,.fa.fa-renren,.fa.fa-stack-exchange,.fa.fa-vk,.fa.fa-weibo{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-arrow-circle-o-right{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-arrow-circle-o-right:before{content:"\f35a"}.fa.fa-arrow-circle-o-left{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-arrow-circle-o-left:before{content:"\f359"}.fa.fa-caret-square-o-left{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-caret-square-o-left:before{content:"\f191"}.fa.fa-toggle-left{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-toggle-left:before{content:"\f191"}.fa.fa-dot-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-dot-circle-o:before{content:"\f192"}.fa.fa-vimeo-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-try:before,.fa.fa-turkish-lira:before{content:"\f195"}.fa.fa-plus-square-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-plus-square-o:before{content:"\f0fe"}.fa.fa-openid,.fa.fa-slack,.fa.fa-wordpress{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-bank:before,.fa.fa-institution:before{content:"\f19c"}.fa.fa-mortar-board:before{content:"\f19d"}.fa.fa-delicious,.fa.fa-digg,.fa.fa-drupal,.fa.fa-google,.fa.fa-joomla,.fa.fa-pied-piper-alt,.fa.fa-pied-piper-pp,.fa.fa-reddit,.fa.fa-reddit-square,.fa.fa-stumbleupon,.fa.fa-stumbleupon-circle,.fa.fa-yahoo{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-spoon:before{content:"\f2e5"}.fa.fa-behance,.fa.fa-behance-square,.fa.fa-steam,.fa.fa-steam-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-automobile:before{content:"\f1b9"}.fa.fa-cab:before{content:"\f1ba"}.fa.fa-envelope-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-envelope-o:before{content:"\f0e0"}.fa.fa-deviantart,.fa.fa-soundcloud{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-file-pdf-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-pdf-o:before{content:"\f1c1"}.fa.fa-file-word-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-word-o:before{content:"\f1c2"}.fa.fa-file-excel-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-excel-o:before{content:"\f1c3"}.fa.fa-file-powerpoint-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-powerpoint-o:before{content:"\f1c4"}.fa.fa-file-image-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-image-o:before{content:"\f1c5"}.fa.fa-file-photo-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-photo-o:before{content:"\f1c5"}.fa.fa-file-picture-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-picture-o:before{content:"\f1c5"}.fa.fa-file-archive-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-archive-o:before{content:"\f1c6"}.fa.fa-file-zip-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-zip-o:before{content:"\f1c6"}.fa.fa-file-audio-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-audio-o:before{content:"\f1c7"}.fa.fa-file-sound-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-sound-o:before{content:"\f1c7"}.fa.fa-file-video-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-video-o:before{content:"\f1c8"}.fa.fa-file-movie-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-movie-o:before{content:"\f1c8"}.fa.fa-file-code-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-file-code-o:before{content:"\f1c9"}.fa.fa-codepen,.fa.fa-jsfiddle,.fa.fa-vine{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-life-bouy,.fa.fa-life-ring{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-life-bouy:before{content:"\f1cd"}.fa.fa-life-buoy{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-life-buoy:before{content:"\f1cd"}.fa.fa-life-saver{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-life-saver:before{content:"\f1cd"}.fa.fa-support{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-support:before{content:"\f1cd"}.fa.fa-circle-o-notch:before{content:"\f1ce"}.fa.fa-ra,.fa.fa-rebel{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-ra:before{content:"\f1d0"}.fa.fa-resistance{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-resistance:before{content:"\f1d0"}.fa.fa-empire,.fa.fa-ge{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-ge:before{content:"\f1d1"}.fa.fa-git,.fa.fa-git-square,.fa.fa-hacker-news,.fa.fa-y-combinator-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-y-combinator-square:before{content:"\f1d4"}.fa.fa-yc-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-yc-square:before{content:"\f1d4"}.fa.fa-qq,.fa.fa-tencent-weibo,.fa.fa-wechat,.fa.fa-weixin{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-wechat:before{content:"\f1d7"}.fa.fa-send:before{content:"\f1d8"}.fa.fa-paper-plane-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-paper-plane-o:before{content:"\f1d8"}.fa.fa-send-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-send-o:before{content:"\f1d8"}.fa.fa-circle-thin{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-circle-thin:before{content:"\f111"}.fa.fa-header:before{content:"\f1dc"}.fa.fa-sliders:before{content:"\f1de"}.fa.fa-futbol-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-futbol-o:before{content:"\f1e3"}.fa.fa-soccer-ball-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-soccer-ball-o:before{content:"\f1e3"}.fa.fa-slideshare,.fa.fa-twitch,.fa.fa-yelp{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-newspaper-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-newspaper-o:before{content:"\f1ea"}.fa.fa-cc-amex,.fa.fa-cc-discover,.fa.fa-cc-mastercard,.fa.fa-cc-paypal,.fa.fa-cc-stripe,.fa.fa-cc-visa,.fa.fa-google-wallet,.fa.fa-paypal{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-bell-slash-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-bell-slash-o:before{content:"\f1f6"}.fa.fa-trash:before{content:"\f2ed"}.fa.fa-copyright{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-eyedropper:before{content:"\f1fb"}.fa.fa-area-chart:before{content:"\f1fe"}.fa.fa-pie-chart:before{content:"\f200"}.fa.fa-line-chart:before{content:"\f201"}.fa.fa-angellist,.fa.fa-ioxhost,.fa.fa-lastfm,.fa.fa-lastfm-square{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-cc{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-cc:before{content:"\f20a"}.fa.fa-ils:before,.fa.fa-shekel:before,.fa.fa-sheqel:before{content:"\f20b"}.fa.fa-meanpath{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-meanpath:before{content:"\f2b4"}.fa.fa-buysellads,.fa.fa-connectdevelop,.fa.fa-dashcube,.fa.fa-forumbee,.fa.fa-leanpub,.fa.fa-sellsy,.fa.fa-shirtsinbulk,.fa.fa-simplybuilt,.fa.fa-skyatlas{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-diamond{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-diamond:before{content:"\f3a5"}.fa.fa-intersex:before{content:"\f224"}.fa.fa-facebook-official{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-facebook-official:before{content:"\f09a"}.fa.fa-pinterest-p,.fa.fa-whatsapp{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-hotel:before{content:"\f236"}.fa.fa-medium,.fa.fa-viacoin,.fa.fa-y-combinator,.fa.fa-yc{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-yc:before{content:"\f23b"}.fa.fa-expeditedssl,.fa.fa-opencart,.fa.fa-optin-monster{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-battery-4:before,.fa.fa-battery:before{content:"\f240"}.fa.fa-battery-3:before{content:"\f241"}.fa.fa-battery-2:before{content:"\f242"}.fa.fa-battery-1:before{content:"\f243"}.fa.fa-battery-0:before{content:"\f244"}.fa.fa-object-group,.fa.fa-object-ungroup,.fa.fa-sticky-note-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-sticky-note-o:before{content:"\f249"}.fa.fa-cc-diners-club,.fa.fa-cc-jcb{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-clone,.fa.fa-hourglass-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hourglass-o:before{content:"\f254"}.fa.fa-hourglass-1:before{content:"\f251"}.fa.fa-hourglass-2:before{content:"\f252"}.fa.fa-hourglass-3:before{content:"\f253"}.fa.fa-hand-rock-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-rock-o:before{content:"\f255"}.fa.fa-hand-grab-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-grab-o:before{content:"\f255"}.fa.fa-hand-paper-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-paper-o:before{content:"\f256"}.fa.fa-hand-stop-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-stop-o:before{content:"\f256"}.fa.fa-hand-scissors-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-scissors-o:before{content:"\f257"}.fa.fa-hand-lizard-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-lizard-o:before{content:"\f258"}.fa.fa-hand-spock-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-spock-o:before{content:"\f259"}.fa.fa-hand-pointer-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-pointer-o:before{content:"\f25a"}.fa.fa-hand-peace-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-hand-peace-o:before{content:"\f25b"}.fa.fa-registered{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-chrome,.fa.fa-creative-commons,.fa.fa-firefox,.fa.fa-get-pocket,.fa.fa-gg,.fa.fa-gg-circle,.fa.fa-internet-explorer,.fa.fa-odnoklassniki,.fa.fa-odnoklassniki-square,.fa.fa-opera,.fa.fa-safari,.fa.fa-tripadvisor,.fa.fa-wikipedia-w{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-television:before{content:"\f26c"}.fa.fa-500px,.fa.fa-amazon,.fa.fa-contao{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-calendar-plus-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-calendar-plus-o:before{content:"\f271"}.fa.fa-calendar-minus-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-calendar-minus-o:before{content:"\f272"}.fa.fa-calendar-times-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-calendar-times-o:before{content:"\f273"}.fa.fa-calendar-check-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-calendar-check-o:before{content:"\f274"}.fa.fa-map-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-map-o:before{content:"\f279"}.fa.fa-commenting:before{content:"\f4ad"}.fa.fa-commenting-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-commenting-o:before{content:"\f4ad"}.fa.fa-houzz,.fa.fa-vimeo{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-vimeo:before{content:"\f27d"}.fa.fa-black-tie,.fa.fa-edge,.fa.fa-fonticons,.fa.fa-reddit-alien{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-credit-card-alt:before{content:"\f09d"}.fa.fa-codiepie,.fa.fa-fort-awesome,.fa.fa-mixcloud,.fa.fa-modx,.fa.fa-product-hunt,.fa.fa-scribd,.fa.fa-usb{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-pause-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-pause-circle-o:before{content:"\f28b"}.fa.fa-stop-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-stop-circle-o:before{content:"\f28d"}.fa.fa-bluetooth,.fa.fa-bluetooth-b,.fa.fa-envira,.fa.fa-gitlab,.fa.fa-wheelchair-alt,.fa.fa-wpbeginner,.fa.fa-wpforms{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-wheelchair-alt:before{content:"\f368"}.fa.fa-question-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-question-circle-o:before{content:"\f059"}.fa.fa-volume-control-phone:before{content:"\f2a0"}.fa.fa-asl-interpreting:before{content:"\f2a3"}.fa.fa-deafness:before,.fa.fa-hard-of-hearing:before{content:"\f2a4"}.fa.fa-glide,.fa.fa-glide-g{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-signing:before{content:"\f2a7"}.fa.fa-first-order,.fa.fa-google-plus-official,.fa.fa-pied-piper,.fa.fa-snapchat,.fa.fa-snapchat-ghost,.fa.fa-snapchat-square,.fa.fa-themeisle,.fa.fa-viadeo,.fa.fa-viadeo-square,.fa.fa-yoast{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-google-plus-official:before{content:"\f2b3"}.fa.fa-google-plus-circle{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-google-plus-circle:before{content:"\f2b3"}.fa.fa-fa,.fa.fa-font-awesome{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-fa:before{content:"\f2b4"}.fa.fa-handshake-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-handshake-o:before{content:"\f2b5"}.fa.fa-envelope-open-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-envelope-open-o:before{content:"\f2b6"}.fa.fa-linode{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-address-book-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-address-book-o:before{content:"\f2b9"}.fa.fa-vcard:before{content:"\f2bb"}.fa.fa-address-card-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-address-card-o:before{content:"\f2bb"}.fa.fa-vcard-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-vcard-o:before{content:"\f2bb"}.fa.fa-user-circle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-user-circle-o:before{content:"\f2bd"}.fa.fa-user-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-user-o:before{content:"\f007"}.fa.fa-id-badge{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-drivers-license:before{content:"\f2c2"}.fa.fa-id-card-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-id-card-o:before{content:"\f2c2"}.fa.fa-drivers-license-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-drivers-license-o:before{content:"\f2c2"}.fa.fa-free-code-camp,.fa.fa-quora,.fa.fa-telegram{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-thermometer-4:before,.fa.fa-thermometer:before{content:"\f2c7"}.fa.fa-thermometer-3:before{content:"\f2c8"}.fa.fa-thermometer-2:before{content:"\f2c9"}.fa.fa-thermometer-1:before{content:"\f2ca"}.fa.fa-thermometer-0:before{content:"\f2cb"}.fa.fa-bathtub:before,.fa.fa-s15:before{content:"\f2cd"}.fa.fa-window-maximize,.fa.fa-window-restore{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-times-rectangle:before{content:"\f410"}.fa.fa-window-close-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-window-close-o:before{content:"\f410"}.fa.fa-times-rectangle-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-times-rectangle-o:before{content:"\f410"}.fa.fa-bandcamp,.fa.fa-eercast,.fa.fa-etsy,.fa.fa-grav,.fa.fa-imdb,.fa.fa-ravelry{font-family:"Font Awesome 5 Brands";font-weight:400}.fa.fa-eercast:before{content:"\f2da"}.fa.fa-snowflake-o{font-family:"Font Awesome 5 Free";font-weight:400}.fa.fa-snowflake-o:before{content:"\f2dc"}.fa.fa-spotify,.fa.fa-superpowers,.fa.fa-wpexplorer{font-family:"Font Awesome 5 Brands";font-weight:400}
\ No newline at end of file
diff --git a/html/font-awesome/webfonts/fa-regular-400.eot b/html/font-awesome/webfonts/fa-regular-400.eot
new file mode 100644
index 0000000000..d62be2fad8
Binary files /dev/null and b/html/font-awesome/webfonts/fa-regular-400.eot differ
diff --git a/html/font-awesome/webfonts/fa-regular-400.woff b/html/font-awesome/webfonts/fa-regular-400.woff
new file mode 100644
index 0000000000..43b1a9ae49
Binary files /dev/null and b/html/font-awesome/webfonts/fa-regular-400.woff differ
diff --git a/html/font-awesome/webfonts/fa-solid-900.eot b/html/font-awesome/webfonts/fa-solid-900.eot
new file mode 100644
index 0000000000..c77baa8d46
Binary files /dev/null and b/html/font-awesome/webfonts/fa-solid-900.eot differ
diff --git a/html/font-awesome/webfonts/fa-solid-900.woff b/html/font-awesome/webfonts/fa-solid-900.woff
new file mode 100644
index 0000000000..77c1786227
Binary files /dev/null and b/html/font-awesome/webfonts/fa-solid-900.woff differ
diff --git a/icons/effects/96x96.dmi b/icons/effects/96x96.dmi
index c0b5ef5b40..b60ff97b2b 100644
Binary files a/icons/effects/96x96.dmi and b/icons/effects/96x96.dmi differ
diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi
index 51e9453087..a2fce4678f 100644
Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ
diff --git a/icons/emoji.dmi b/icons/emoji.dmi
index 925b072a5f..931cb7a916 100644
Binary files a/icons/emoji.dmi and b/icons/emoji.dmi differ
diff --git a/icons/mecha/mech_construction.dmi b/icons/mecha/mech_construction.dmi
index 42cc8eeb57..d7f0f3e054 100644
Binary files a/icons/mecha/mech_construction.dmi and b/icons/mecha/mech_construction.dmi differ
diff --git a/icons/mecha/mecha_ammo.dmi b/icons/mecha/mecha_ammo.dmi
new file mode 100644
index 0000000000..63bc38cfa7
Binary files /dev/null and b/icons/mecha/mecha_ammo.dmi differ
diff --git a/icons/mecha/mecha_equipment.dmi b/icons/mecha/mecha_equipment.dmi
index 02c7984831..5e277af7cc 100644
Binary files a/icons/mecha/mecha_equipment.dmi and b/icons/mecha/mecha_equipment.dmi differ
diff --git a/icons/misc/language.dmi b/icons/misc/language.dmi
index 6f6f2546d5..3b4ac1a16a 100644
Binary files a/icons/misc/language.dmi and b/icons/misc/language.dmi differ
diff --git a/icons/mob/actions/actions_items.dmi b/icons/mob/actions/actions_items.dmi
index 43c9d20a93..f5ba86c0fa 100644
Binary files a/icons/mob/actions/actions_items.dmi and b/icons/mob/actions/actions_items.dmi differ
diff --git a/icons/mob/back.dmi b/icons/mob/back.dmi
index 26f81b70df..7a28827903 100644
Binary files a/icons/mob/back.dmi and b/icons/mob/back.dmi differ
diff --git a/icons/mob/belt.dmi b/icons/mob/belt.dmi
index 6843bac6bf..070034d778 100644
Binary files a/icons/mob/belt.dmi and b/icons/mob/belt.dmi differ
diff --git a/icons/mob/hands.dmi b/icons/mob/hands.dmi
index b69c6c88b1..63263b835c 100644
Binary files a/icons/mob/hands.dmi and b/icons/mob/hands.dmi differ
diff --git a/icons/mob/head.dmi b/icons/mob/head.dmi
index f8785ae2f7..499b1db21d 100644
Binary files a/icons/mob/head.dmi and b/icons/mob/head.dmi differ
diff --git a/icons/mob/human_face.dmi b/icons/mob/human_face.dmi
index a22901d3e2..d0bd304808 100644
Binary files a/icons/mob/human_face.dmi and b/icons/mob/human_face.dmi differ
diff --git a/icons/mob/inhands/equipment/custodial_lefthand.dmi b/icons/mob/inhands/equipment/custodial_lefthand.dmi
index 90ffd2495e..eaed11867c 100644
Binary files a/icons/mob/inhands/equipment/custodial_lefthand.dmi and b/icons/mob/inhands/equipment/custodial_lefthand.dmi differ
diff --git a/icons/mob/inhands/equipment/custodial_righthand.dmi b/icons/mob/inhands/equipment/custodial_righthand.dmi
index 33e708f8c9..dc7ee742a3 100644
Binary files a/icons/mob/inhands/equipment/custodial_righthand.dmi and b/icons/mob/inhands/equipment/custodial_righthand.dmi differ
diff --git a/icons/mob/inhands/items_lefthand.dmi b/icons/mob/inhands/items_lefthand.dmi
index 71c453856a..4ca4a351c6 100644
Binary files a/icons/mob/inhands/items_lefthand.dmi and b/icons/mob/inhands/items_lefthand.dmi differ
diff --git a/icons/mob/inhands/items_righthand.dmi b/icons/mob/inhands/items_righthand.dmi
index 0ca09ad810..8546a78a73 100644
Binary files a/icons/mob/inhands/items_righthand.dmi and b/icons/mob/inhands/items_righthand.dmi differ
diff --git a/icons/mob/inhands/weapons/swords_lefthand.dmi b/icons/mob/inhands/weapons/swords_lefthand.dmi
index 2169b87580..b74fa16e6a 100644
Binary files a/icons/mob/inhands/weapons/swords_lefthand.dmi and b/icons/mob/inhands/weapons/swords_lefthand.dmi differ
diff --git a/icons/mob/inhands/weapons/swords_righthand.dmi b/icons/mob/inhands/weapons/swords_righthand.dmi
index f054d8f744..8c60c52e76 100644
Binary files a/icons/mob/inhands/weapons/swords_righthand.dmi and b/icons/mob/inhands/weapons/swords_righthand.dmi differ
diff --git a/icons/mob/sharks.dmi b/icons/mob/sharks.dmi
new file mode 100644
index 0000000000..e1b9228ba9
Binary files /dev/null and b/icons/mob/sharks.dmi differ
diff --git a/icons/mob/suit.dmi b/icons/mob/suit.dmi
index 24d654b743..6b1b9591b7 100644
Binary files a/icons/mob/suit.dmi and b/icons/mob/suit.dmi differ
diff --git a/icons/mob/suit_digi.dmi b/icons/mob/suit_digi.dmi
index e181c65cdb..c6bda60d38 100644
Binary files a/icons/mob/suit_digi.dmi and b/icons/mob/suit_digi.dmi differ
diff --git a/icons/mob/uniform.dmi b/icons/mob/uniform.dmi
index 04e2e3cc75..69d8966329 100644
Binary files a/icons/mob/uniform.dmi and b/icons/mob/uniform.dmi differ
diff --git a/icons/mob/uniform_digi.dmi b/icons/mob/uniform_digi.dmi
index 3b86511013..0b8c495c5a 100644
Binary files a/icons/mob/uniform_digi.dmi and b/icons/mob/uniform_digi.dmi differ
diff --git a/icons/obj/abductor.dmi b/icons/obj/abductor.dmi
index fd0893b300..75f84347b6 100644
Binary files a/icons/obj/abductor.dmi and b/icons/obj/abductor.dmi differ
diff --git a/icons/obj/ammo.dmi b/icons/obj/ammo.dmi
index 6282e555fa..a26b01883e 100644
Binary files a/icons/obj/ammo.dmi and b/icons/obj/ammo.dmi differ
diff --git a/icons/obj/citvending.dmi b/icons/obj/citvending.dmi
deleted file mode 100644
index 8f836b7eb5..0000000000
Binary files a/icons/obj/citvending.dmi and /dev/null differ
diff --git a/icons/obj/clothing/belts.dmi b/icons/obj/clothing/belts.dmi
index e329720cf7..1ff87b26e4 100644
Binary files a/icons/obj/clothing/belts.dmi and b/icons/obj/clothing/belts.dmi differ
diff --git a/icons/obj/clothing/gloves.dmi b/icons/obj/clothing/gloves.dmi
index 7e0d03abd5..515d6212c0 100644
Binary files a/icons/obj/clothing/gloves.dmi and b/icons/obj/clothing/gloves.dmi differ
diff --git a/icons/obj/clothing/hats.dmi b/icons/obj/clothing/hats.dmi
index a79ca3be45..d355ade9fe 100644
Binary files a/icons/obj/clothing/hats.dmi and b/icons/obj/clothing/hats.dmi differ
diff --git a/icons/obj/clothing/suits.dmi b/icons/obj/clothing/suits.dmi
index be091c596d..81ef85bb08 100644
Binary files a/icons/obj/clothing/suits.dmi and b/icons/obj/clothing/suits.dmi differ
diff --git a/icons/obj/clothing/uniforms.dmi b/icons/obj/clothing/uniforms.dmi
index 89f9a6fd93..ce458a1de3 100644
Binary files a/icons/obj/clothing/uniforms.dmi and b/icons/obj/clothing/uniforms.dmi differ
diff --git a/icons/obj/crates.dmi b/icons/obj/crates.dmi
index ca81686e94..ecb66d0119 100644
Binary files a/icons/obj/crates.dmi and b/icons/obj/crates.dmi differ
diff --git a/icons/obj/device.dmi b/icons/obj/device.dmi
index 8c2abcd3b9..28fc29cd71 100644
Binary files a/icons/obj/device.dmi and b/icons/obj/device.dmi differ
diff --git a/icons/obj/drinks.dmi b/icons/obj/drinks.dmi
index b03a1cdeae..17d6024586 100644
Binary files a/icons/obj/drinks.dmi and b/icons/obj/drinks.dmi differ
diff --git a/icons/obj/food/containers.dmi b/icons/obj/food/containers.dmi
index 93acc07453..2639d6860a 100644
Binary files a/icons/obj/food/containers.dmi and b/icons/obj/food/containers.dmi differ
diff --git a/icons/obj/food/food.dmi b/icons/obj/food/food.dmi
index a719150f11..670269da00 100644
Binary files a/icons/obj/food/food.dmi and b/icons/obj/food/food.dmi differ
diff --git a/icons/obj/guns/energy.dmi b/icons/obj/guns/energy.dmi
index bba3efc951..20fe8272da 100644
Binary files a/icons/obj/guns/energy.dmi and b/icons/obj/guns/energy.dmi differ
diff --git a/icons/obj/guns/projectile.dmi b/icons/obj/guns/projectile.dmi
index 24ec5797d4..58e427a3fb 100644
Binary files a/icons/obj/guns/projectile.dmi and b/icons/obj/guns/projectile.dmi differ
diff --git a/icons/obj/guns/toy.dmi b/icons/obj/guns/toy.dmi
index f41c289746..56fa76b41b 100644
Binary files a/icons/obj/guns/toy.dmi and b/icons/obj/guns/toy.dmi differ
diff --git a/icons/obj/implants.dmi b/icons/obj/implants.dmi
index 8dc2f08fdc..a6d4697673 100644
Binary files a/icons/obj/implants.dmi and b/icons/obj/implants.dmi differ
diff --git a/icons/obj/improvised.dmi b/icons/obj/improvised.dmi
index a6dfa330e8..b90e3cb3bb 100644
Binary files a/icons/obj/improvised.dmi and b/icons/obj/improvised.dmi differ
diff --git a/icons/obj/items_and_weapons.dmi b/icons/obj/items_and_weapons.dmi
index e3540a782b..561b1890e8 100644
Binary files a/icons/obj/items_and_weapons.dmi and b/icons/obj/items_and_weapons.dmi differ
diff --git a/icons/obj/janitor.dmi b/icons/obj/janitor.dmi
index cb3870dc61..802e165550 100644
Binary files a/icons/obj/janitor.dmi and b/icons/obj/janitor.dmi differ
diff --git a/icons/obj/library.dmi b/icons/obj/library.dmi
index 45181b7c8d..be12ab5f8b 100644
Binary files a/icons/obj/library.dmi and b/icons/obj/library.dmi differ
diff --git a/icons/obj/lighting.dmi b/icons/obj/lighting.dmi
index 396d681f22..675005da91 100644
Binary files a/icons/obj/lighting.dmi and b/icons/obj/lighting.dmi differ
diff --git a/icons/obj/machines/pool.dmi b/icons/obj/machines/pool.dmi
new file mode 100644
index 0000000000..2bfb4f8c40
Binary files /dev/null and b/icons/obj/machines/pool.dmi differ
diff --git a/icons/obj/mining.dmi b/icons/obj/mining.dmi
index a7593e4a55..f2e98aff32 100644
Binary files a/icons/obj/mining.dmi and b/icons/obj/mining.dmi differ
diff --git a/icons/obj/plushes.dmi b/icons/obj/plushes.dmi
index c6f25f1b29..3abb25d8b2 100644
Binary files a/icons/obj/plushes.dmi and b/icons/obj/plushes.dmi differ
diff --git a/icons/obj/power.dmi b/icons/obj/power.dmi
index 1ba953d284..1f84da44d4 100644
Binary files a/icons/obj/power.dmi and b/icons/obj/power.dmi differ
diff --git a/icons/obj/projectiles.dmi b/icons/obj/projectiles.dmi
index a434994846..92e76f78bb 100644
Binary files a/icons/obj/projectiles.dmi and b/icons/obj/projectiles.dmi differ
diff --git a/icons/obj/recycling.dmi b/icons/obj/recycling.dmi
index 4e072c9006..9604d6b97e 100644
Binary files a/icons/obj/recycling.dmi and b/icons/obj/recycling.dmi differ
diff --git a/icons/obj/ring.dmi b/icons/obj/ring.dmi
new file mode 100644
index 0000000000..e24f1fab4c
Binary files /dev/null and b/icons/obj/ring.dmi differ
diff --git a/icons/obj/storage.dmi b/icons/obj/storage.dmi
index 4a6b8964e5..a92af16e96 100644
Binary files a/icons/obj/storage.dmi and b/icons/obj/storage.dmi differ
diff --git a/icons/obj/supermatter.dmi b/icons/obj/supermatter.dmi
index 92a9dfe270..fb05808809 100644
Binary files a/icons/obj/supermatter.dmi and b/icons/obj/supermatter.dmi differ
diff --git a/icons/obj/toy.dmi b/icons/obj/toy.dmi
index d5cae1db93..698c1f7a34 100644
Binary files a/icons/obj/toy.dmi and b/icons/obj/toy.dmi differ
diff --git a/icons/obj/vending.dmi b/icons/obj/vending.dmi
index 143171f414..b11e43d665 100644
Binary files a/icons/obj/vending.dmi and b/icons/obj/vending.dmi differ
diff --git a/icons/obj/vending_restock.dmi b/icons/obj/vending_restock.dmi
index a59fdd0f52..035c01de6b 100644
Binary files a/icons/obj/vending_restock.dmi and b/icons/obj/vending_restock.dmi differ
diff --git a/icons/pda_icons/pda_emoji.png b/icons/pda_icons/pda_emoji.png
new file mode 100644
index 0000000000..39f789f520
Binary files /dev/null and b/icons/pda_icons/pda_emoji.png differ
diff --git a/icons/turf/areas.dmi b/icons/turf/areas.dmi
index fe60cf6c0d..cd504a124a 100644
Binary files a/icons/turf/areas.dmi and b/icons/turf/areas.dmi differ
diff --git a/icons/turf/pool.dmi b/icons/turf/pool.dmi
new file mode 100644
index 0000000000..211776ed93
Binary files /dev/null and b/icons/turf/pool.dmi differ
diff --git a/interface/interface.dm b/interface/interface.dm
index ecd13e1f03..2b33ebb5e8 100644
--- a/interface/interface.dm
+++ b/interface/interface.dm
@@ -8,7 +8,7 @@
if(wikiurl)
if(query)
var/output = wikiurl + "?search=" + query
- src << link(output)
+ src << link(output)
output = wikiurltg + "/index.php?title=Special%3ASearch&profile=default&search=" + query
src << link(output)
else if (query != null)
@@ -125,6 +125,7 @@ Hotkey-Mode: (hotkey-mode must be on)
\tt = say
\to = OOC
\tb = resist
+\tv = rest
\th = stop pulling
\tx = swap-hand
\tz = activate held object (or y)
@@ -184,6 +185,7 @@ Hotkey-Mode: (hotkey-mode must be on)
\td = right
\tw = up
\tq = unequip active module
+\tv = rest
\th = stop pulling
\tm = me
\tt = say
diff --git a/interface/menu.dm b/interface/menu.dm
index 547c2edfc1..3c881656da 100644
--- a/interface/menu.dm
+++ b/interface/menu.dm
@@ -69,8 +69,8 @@ GLOBAL_LIST_EMPTY(menulist)
if (!verbpath || !(verbpath in typesof("[type]/verb")))
return
- if (copytext(verbpath.name,1,2) == "@")
- winset(C, null, list2params(list("command" = copytext(verbpath.name,2))))
+ if (verbpath.name[1] == "@")
+ winset(C, null, list2params(list("command" = copytext(verbpath.name, length(verbpath.name[1]) + 1))))
else
winset(C, null, list2params(list("command" = replacetext(verbpath.name, " ", "-"))))
diff --git a/interface/stylesheet.dm b/interface/stylesheet.dm
index 6ee9e6e824..9f3d8911ec 100644
--- a/interface/stylesheet.dm
+++ b/interface/stylesheet.dm
@@ -151,6 +151,7 @@ h1.alert, h2.alert {color: #000000;}
.extremelybig {font-size: 5;}
.greentext {color: #00FF00; font-size: 3;}
.redtext {color: #FF0000; font-size: 3;}
+.yellowtext {color: #FFCC00; font-size: 3;}
.clown {color: #FF69Bf; font-size: 3; font-family: "Comic Sans MS", cursive, sans-serif; font-weight: bold;}
.his_grace {color: #15D512; font-family: "Courier New", cursive, sans-serif; font-style: italic;}
.spooky {color: #FF9100;}
diff --git a/modular_citadel/code/_onclick/item_attack.dm b/modular_citadel/code/_onclick/item_attack.dm
index 80281ee084..d87b2be661 100644
--- a/modular_citadel/code/_onclick/item_attack.dm
+++ b/modular_citadel/code/_onclick/item_attack.dm
@@ -1,12 +1,12 @@
/obj/item/proc/rightclick_melee_attack_chain(mob/user, atom/target, params)
- if(!pre_altattackby(target, user, params)) //Hey, does this item have special behavior that should override all normal right-click functionality?
+ if(!alt_pre_attack(target, user, params)) //Hey, does this item have special behavior that should override all normal right-click functionality?
if(!target.altattackby(src, user, params)) //Does the target do anything special when we right-click on it?
melee_attack_chain(user, target, params) //Ugh. Lame! I'm filing a legal complaint about the discrimination against the right mouse button!
else
altafterattack(target, user, TRUE, params)
return
-/obj/item/proc/pre_altattackby(atom/A, mob/living/user, params)
+/obj/item/proc/alt_pre_attack(atom/A, mob/living/user, params)
return FALSE //return something other than false if you wanna override attacking completely
/atom/proc/altattackby(obj/item/W, mob/user, params)
diff --git a/modular_citadel/code/_onclick/other_mobs.dm b/modular_citadel/code/_onclick/other_mobs.dm
index 8b9f3b3184..6d9ffdd6ca 100644
--- a/modular_citadel/code/_onclick/other_mobs.dm
+++ b/modular_citadel/code/_onclick/other_mobs.dm
@@ -18,7 +18,7 @@
if(!has_active_hand())
to_chat(src, "You ponder your life choices and sigh.")
return TRUE
- var/list/src_viewers = get_hearers_in_view(DEFAULT_MESSAGE_RANGE, src) - src // src has a different message.
+ var/list/src_viewers = viewers(DEFAULT_MESSAGE_RANGE, src) - src // src has a different message.
var/the_action = "waves to [A]"
var/what_action = "waves to something you can't see"
var/self_action = "wave to [A]"
diff --git a/modular_citadel/code/datums/status_effects/chems.dm b/modular_citadel/code/datums/status_effects/chems.dm
index 89086ab1e5..82f4fbf1d9 100644
--- a/modular_citadel/code/datums/status_effects/chems.dm
+++ b/modular_citadel/code/datums/status_effects/chems.dm
@@ -211,10 +211,10 @@
/mob/living/verb/toggle_hypno()
set category = "IC"
- set name = "Toggle Lewd MKUltra"
- set desc = "Allows you to toggle if you'd like lewd flavour messages for MKUltra."
+ set name = "Toggle Lewd Hypno"
+ set desc = "Allows you to toggle if you'd like lewd flavour messages for hypno features, such as MKUltra."
client.prefs.cit_toggles ^= HYPNO
- to_chat(usr, "You [((client.prefs.cit_toggles & HYPNO) ?"will":"no longer")] receive lewd flavour messages for MKUltra.")
+ to_chat(usr, "You [((client.prefs.cit_toggles & HYPNO) ?"will":"no longer")] receive lewd flavour messages for hypno.")
/datum/status_effect/chem/enthrall
id = "enthrall"
diff --git a/modular_citadel/code/modules/admin/chat_commands.dm b/modular_citadel/code/modules/admin/chat_commands.dm
index 501e0fa25a..ddac7b5953 100644
--- a/modular_citadel/code/modules/admin/chat_commands.dm
+++ b/modular_citadel/code/modules/admin/chat_commands.dm
@@ -14,6 +14,13 @@
saltresult += "[saltprimarysubject] [saltsecondarysubject]"
return "[saltresult]!"
+/datum/tgs_chat_command/valentine
+ name = "valentine"
+ help_text = "Get a random flirt line."
+
+/datum/tgs_chat_command/valentine/Run(datum/tgs_chat_user/sender, params)
+ return "[pick(GLOB.flirts)]"
+
/datum/tgs_chat_command/despacito
name = "despacito" //someone please high effort this sometime and make it a full on ytdl search
help_text = "This is so sad."
diff --git a/modular_citadel/code/modules/arousal/arousal.dm b/modular_citadel/code/modules/arousal/arousal.dm
index 3361707b04..5c7d5a39e9 100644
--- a/modular_citadel/code/modules/arousal/arousal.dm
+++ b/modular_citadel/code/modules/arousal/arousal.dm
@@ -10,12 +10,6 @@
var/hidden_undershirt = FALSE
var/hidden_socks = FALSE
-//Species vars
-/datum/species
- var/list/cum_fluids = list("semen")
- var/list/milk_fluids = list("milk")
- var/list/femcum_fluids = list("femcum")
-
//Mob procs
/mob/living/carbon/human/proc/underwear_toggle()
set name = "Toggle undergarments"
@@ -46,20 +40,12 @@
var/list/obj/item/organ/genital/genit_list = list()
if(!client?.prefs.arousable || (aphro && (client?.prefs.cit_toggles & NO_APHRO)) || (maso && !HAS_TRAIT(src, TRAIT_MASO)))
return // no adjusting made here
- if(strength>0)
- for(var/obj/item/organ/genital/G in internal_organs)
- if(!G.aroused_state && prob(strength*G.sensitivity))
- G.set_aroused_state(TRUE)
- G.update_appearance()
- if(G.aroused_state)
- genit_list += G
- else
- for(var/obj/item/organ/genital/G in internal_organs)
- if(G.aroused_state && prob(strength*G.sensitivity))
- G.set_aroused_state(FALSE)
- G.update_appearance()
- if(G.aroused_state)
- genit_list += G
+ for(var/obj/item/organ/genital/G in internal_organs)
+ if(G.genital_flags & GENITAL_CAN_AROUSE && !G.aroused_state && prob(strength*G.sensitivity))
+ G.set_aroused_state(strength > 0)
+ G.update_appearance()
+ if(G.aroused_state)
+ genit_list += G
return genit_list
/obj/item/organ/genital/proc/climaxable(mob/living/carbon/human/H, silent = FALSE) //returns the fluid source (ergo reagents holder) if found.
@@ -158,9 +144,13 @@
return //No one left.
var/mob/living/target = input(src, "With whom?", "Sexual partner", null) as null|anything in partners //pick one, default to null
if(target && in_range(src, target))
+ to_chat(src,"Waiting for consent...")
var/consenting = input(target, "Do you want [src] to climax with you?","Climax mechanics","No") in list("Yes","No")
if(consenting == "Yes")
return target
+ else
+ message_admins("[src] tried to climax with [target], but [target] did not consent.")
+ log_consent("[src] tried to climax with [target], but [target] did not consent.")
/mob/living/carbon/human/proc/pick_climax_container(silent = FALSE)
var/list/containers_list = list()
diff --git a/modular_citadel/code/modules/arousal/genitals.dm b/modular_citadel/code/modules/arousal/genitals.dm
index a16617ba3f..d5191b0532 100644
--- a/modular_citadel/code/modules/arousal/genitals.dm
+++ b/modular_citadel/code/modules/arousal/genitals.dm
@@ -20,37 +20,29 @@
var/linked_organ_slot //used for linking an apparatus' organ to its other half on update_link().
var/layer_index = GENITAL_LAYER_INDEX //Order should be very important. FIRST vagina, THEN testicles, THEN penis, as this affects the order they are rendered in.
-/obj/item/organ/genital/Initialize(mapload, mob/living/carbon/human/H)
+/obj/item/organ/genital/Initialize(mapload, do_update = TRUE)
. = ..()
if(fluid_id)
- create_reagents(fluid_max_volume)
+ create_reagents(fluid_max_volume, NONE, NO_REAGENTS_VALUE)
if(CHECK_BITFIELD(genital_flags, GENITAL_FUID_PRODUCTION))
reagents.add_reagent(fluid_id, fluid_max_volume)
- if(H)
- get_features(H)
- Insert(H)
- else
+ if(do_update)
update()
-/obj/item/organ/genital/Destroy()
- if(linked_organ)
- update_link(TRUE)//this should remove any other links it has
- if(owner)
- Remove(owner, TRUE)//this should remove references to it, so it can be GCd correctly
- return ..()
-
/obj/item/organ/genital/proc/set_aroused_state(new_state)
+ if(!(genital_flags & GENITAL_CAN_AROUSE))
+ return FALSE
if(!((HAS_TRAIT(owner,TRAIT_PERMABONER) && !new_state) || HAS_TRAIT(owner,TRAIT_NEVERBONER) && new_state))
aroused_state = new_state
return aroused_state
-/obj/item/organ/genital/proc/update(removing = FALSE)
+/obj/item/organ/genital/proc/update()
if(QDELETED(src))
return
update_size()
update_appearance()
- if(linked_organ_slot || (linked_organ && removing))
- update_link(removing)
+ if(linked_organ_slot || (linked_organ && !owner))
+ update_link()
//exposure and through-clothing code
/mob/living/carbon
@@ -115,17 +107,7 @@
set desc = "Allows you to toggle which genitals are showing signs of arousal."
var/list/genital_list = list()
for(var/obj/item/organ/genital/G in internal_organs)
- var/datum/sprite_accessory/S
- switch(G.type)
- if(/obj/item/organ/genital/penis)
- S = GLOB.cock_shapes_list[G.shape]
- if(/obj/item/organ/genital/testicles)
- S = GLOB.balls_shapes_list[G.shape]
- if(/obj/item/organ/genital/vagina)
- S = GLOB.vagina_shapes_list[G.shape]
- if(/obj/item/organ/genital/breasts)
- S = GLOB.breasts_shapes_list[G.shape]
- if(S?.alt_aroused)
+ if(G.genital_flags & GENITAL_CAN_AROUSE)
genital_list += G
if(!genital_list.len) //There's nothing that can show arousal
return
@@ -176,19 +158,18 @@
return TRUE
return FALSE
-/obj/item/organ/genital/proc/update_link(removing = FALSE)
- if(!removing && owner)
+/obj/item/organ/genital/proc/update_link()
+ if(owner)
if(linked_organ)
- return
+ return FALSE
linked_organ = owner.getorganslot(linked_organ_slot)
if(linked_organ)
linked_organ.linked_organ = src
linked_organ.upon_link()
upon_link()
return TRUE
- else
- if(linked_organ)
- linked_organ.linked_organ = null
+ if(linked_organ)
+ linked_organ.linked_organ = null
linked_organ = null
return FALSE
@@ -202,11 +183,13 @@
update()
RegisterSignal(owner, COMSIG_MOB_DEATH, .proc/update_appearance)
-/obj/item/organ/genital/Remove(mob/living/carbon/M, special = FALSE, drop_if_replaced = TRUE)
+/obj/item/organ/genital/Remove(special = FALSE)
. = ..()
- if(.)
- update(TRUE)
- UnregisterSignal(M, COMSIG_MOB_DEATH)
+ var/mob/living/carbon/human/H = .
+ update()
+ if(!QDELETED(H))
+ UnregisterSignal(H, COMSIG_MOB_DEATH)
+ H.update_genitals()
//proc to give a player their genitals and stuff when they log in
/mob/living/carbon/human/proc/give_genitals(clean = FALSE)//clean will remove all pre-existing genitals. proc will then give them any genitals that are enabled in their DNA
@@ -235,18 +218,14 @@
/mob/living/carbon/human/proc/give_genital(obj/item/organ/genital/G)
if(!dna || (NOGENITALS in dna.species.species_traits) || getorganslot(initial(G.slot)))
return FALSE
- G = new G(null, src)
+ G = new G(null, FALSE)
+ G.get_features(src)
+ G.Insert(src)
return G
/obj/item/organ/genital/proc/get_features(mob/living/carbon/human/H)
return
-/datum/species/proc/genitals_layertext(layer)
- switch(layer)
- if(GENITALS_BEHIND_LAYER)
- return "BEHIND"
- if(GENITALS_FRONT_LAYER)
- return "FRONT"
//procs to handle sprite overlays being applied to humans
@@ -265,8 +244,91 @@
update_genitals()
/mob/living/carbon/human/proc/update_genitals()
- if(!QDELETED(src))
- dna.species.handle_genitals(src)
+ if(QDELETED(src))
+ return
+ var/static/list/relevant_layers = list("[GENITALS_BEHIND_LAYER]" = "BEHIND", "[GENITALS_FRONT_LAYER]" = "FRONT")
+ var/static/list/layers_num
+ if(!layers_num)
+ for(var/L in relevant_layers)
+ LAZYSET(layers_num, L, text2num(L))
+ for(var/L in relevant_layers) //Less hardcode
+ remove_overlay(layers_num[L])
+ remove_overlay(GENITALS_EXPOSED_LAYER)
+ if(!LAZYLEN(internal_organs) || ((NOGENITALS in dna.species.species_traits) && !genital_override) || HAS_TRAIT(src, TRAIT_HUSK))
+ return
+
+ //start scanning for genitals
+
+ var/list/gen_index[GENITAL_LAYER_INDEX_LENGTH]
+ var/list/genitals_to_add
+ var/list/fully_exposed
+ for(var/obj/item/organ/genital/G in internal_organs)
+ if(G.is_exposed()) //Checks appropriate clothing slot and if it's through_clothes
+ LAZYADD(gen_index[G.layer_index], G)
+ for(var/L in gen_index)
+ if(L) //skip nulls
+ LAZYADD(genitals_to_add, L)
+ if(!genitals_to_add)
+ return
+ //Now we added all genitals that aren't internal and should be rendered
+ //start applying overlays
+ for(var/layer in relevant_layers)
+ var/list/standing = list()
+ var/layertext = relevant_layers[layer]
+ for(var/A in genitals_to_add)
+ var/obj/item/organ/genital/G = A
+ var/datum/sprite_accessory/S
+ var/size = G.size
+ switch(G.type)
+ if(/obj/item/organ/genital/penis)
+ S = GLOB.cock_shapes_list[G.shape]
+ if(/obj/item/organ/genital/testicles)
+ S = GLOB.balls_shapes_list[G.shape]
+ if(/obj/item/organ/genital/vagina)
+ S = GLOB.vagina_shapes_list[G.shape]
+ if(/obj/item/organ/genital/breasts)
+ S = GLOB.breasts_shapes_list[G.shape]
+
+ if(!S || S.icon_state == "none")
+ continue
+ var/aroused_state = G.aroused_state && S.alt_aroused
+
+ var/mutable_appearance/genital_overlay = mutable_appearance(S.icon, layer = -layer)
+ if(S.center)
+ genital_overlay = center_image(genital_overlay, S.dimension_x, S.dimension_y)
+
+ if(dna.species.use_skintones && dna.features["genitals_use_skintone"])
+ genital_overlay.color = "#[skintone2hex(skin_tone)]"
+ else
+ switch(S.color_src)
+ if("cock_color")
+ genital_overlay.color = "#[dna.features["cock_color"]]"
+ if("balls_color")
+ genital_overlay.color = "#[dna.features["balls_color"]]"
+ if("breasts_color")
+ genital_overlay.color = "#[dna.features["breasts_color"]]"
+ if("vag_color")
+ genital_overlay.color = "#[dna.features["vag_color"]]"
+
+ genital_overlay.icon_state = "[G.slot]_[S.icon_state]_[size][dna.species.use_skintones ? "_s" : ""]_[aroused_state]_[layertext]"
+
+ if(layer == GENITALS_FRONT_LAYER && CHECK_BITFIELD(G.genital_flags, GENITAL_THROUGH_CLOTHES))
+ genital_overlay.layer = -GENITALS_EXPOSED_LAYER
+ LAZYADD(fully_exposed, genital_overlay) // to be added to a layer with higher priority than clothes, hence the name of the bitflag.
+ else
+ genital_overlay.layer = -layers_num[layer]
+ standing += genital_overlay
+
+ if(LAZYLEN(standing))
+ overlays_standing[layers_num[layer]] = standing
+
+ if(LAZYLEN(fully_exposed))
+ overlays_standing[GENITALS_EXPOSED_LAYER] = fully_exposed
+ apply_overlay(GENITALS_EXPOSED_LAYER)
+
+ for(var/L in relevant_layers)
+ apply_overlay(layers_num[L])
+
//Checks to see if organs are new on the mob, and changes their colours so that they don't get crazy colours.
/mob/living/carbon/human/proc/emergent_genital_call()
@@ -295,85 +357,3 @@
else if (willyCheck == FALSE)
dna.features["cock_color"] = dna.features["breasts_color"]
return TRUE
-
-/datum/species/proc/handle_genitals(mob/living/carbon/human/H)//more like handle sadness
- if(!H)//no args
- CRASH("H = null")
- if(!LAZYLEN(H.internal_organs) || ((NOGENITALS in species_traits) && !H.genital_override) || HAS_TRAIT(H, TRAIT_HUSK))
- return
- var/list/relevant_layers = list(GENITALS_BEHIND_LAYER, GENITALS_FRONT_LAYER)
-
- for(var/L in relevant_layers) //Less hardcode
- H.remove_overlay(L)
- H.remove_overlay(GENITALS_EXPOSED_LAYER)
- //start scanning for genitals
-
- var/list/gen_index[GENITAL_LAYER_INDEX_LENGTH]
- var/list/genitals_to_add
- var/list/fully_exposed
- for(var/obj/item/organ/genital/G in H.internal_organs)
- if(G.is_exposed()) //Checks appropriate clothing slot and if it's through_clothes
- LAZYADD(gen_index[G.layer_index], G)
- for(var/L in gen_index)
- if(L) //skip nulls
- LAZYADD(genitals_to_add, L)
- if(!genitals_to_add)
- return
- //Now we added all genitals that aren't internal and should be rendered
- //start applying overlays
- for(var/layer in relevant_layers)
- var/list/standing = list()
- var/layertext = genitals_layertext(layer)
- for(var/A in genitals_to_add)
- var/obj/item/organ/genital/G = A
- var/datum/sprite_accessory/S
- var/size = G.size
- var/aroused_state = G.aroused_state
- switch(G.type)
- if(/obj/item/organ/genital/penis)
- S = GLOB.cock_shapes_list[G.shape]
- if(/obj/item/organ/genital/testicles)
- S = GLOB.balls_shapes_list[G.shape]
- if(/obj/item/organ/genital/vagina)
- S = GLOB.vagina_shapes_list[G.shape]
- if(/obj/item/organ/genital/breasts)
- S = GLOB.breasts_shapes_list[G.shape]
-
- if(!S || S.icon_state == "none")
- continue
-
- var/mutable_appearance/genital_overlay = mutable_appearance(S.icon, layer = -layer)
- genital_overlay.icon_state = "[G.slot]_[S.icon_state]_[size]_[aroused_state]_[layertext]"
-
- if(S.center)
- genital_overlay = center_image(genital_overlay, S.dimension_x, S.dimension_y)
-
- if(use_skintones && H.dna.features["genitals_use_skintone"])
- genital_overlay.color = "#[skintone2hex(H.skin_tone)]"
- genital_overlay.icon_state = "[G.slot]_[S.icon_state]_[size]-s_[aroused_state]_[layertext]"
- else
- switch(S.color_src)
- if("cock_color")
- genital_overlay.color = "#[H.dna.features["cock_color"]]"
- if("balls_color")
- genital_overlay.color = "#[H.dna.features["balls_color"]]"
- if("breasts_color")
- genital_overlay.color = "#[H.dna.features["breasts_color"]]"
- if("vag_color")
- genital_overlay.color = "#[H.dna.features["vag_color"]]"
-
- if(layer == GENITALS_FRONT_LAYER && CHECK_BITFIELD(G.genital_flags, GENITAL_THROUGH_CLOTHES))
- genital_overlay.layer = -GENITALS_EXPOSED_LAYER
- LAZYADD(fully_exposed, genital_overlay) // to be added to a layer with higher priority than clothes, hence the name of the bitflag.
- else
- standing += genital_overlay
-
- if(LAZYLEN(standing))
- H.overlays_standing[layer] = standing
-
- if(LAZYLEN(fully_exposed))
- H.overlays_standing[GENITALS_EXPOSED_LAYER] = fully_exposed
- H.apply_overlay(GENITALS_EXPOSED_LAYER)
-
- for(var/L in relevant_layers)
- H.apply_overlay(L)
diff --git a/modular_citadel/code/modules/arousal/genitals_sprite_accessories.dm b/modular_citadel/code/modules/arousal/genitals_sprite_accessories.dm
index 0c1763b8e2..594fc83e46 100644
--- a/modular_citadel/code/modules/arousal/genitals_sprite_accessories.dm
+++ b/modular_citadel/code/modules/arousal/genitals_sprite_accessories.dm
@@ -85,36 +85,36 @@
//Vaginas
/datum/sprite_accessory/vagina
icon = 'modular_citadel/icons/obj/genitals/vagina_onmob.dmi'
- icon_state = null
name = "vagina"
color_src = "vag_color"
+ alt_aroused = TRUE
/datum/sprite_accessory/vagina/human
icon_state = "human"
name = "Human"
- alt_aroused = TRUE
/datum/sprite_accessory/vagina/tentacles
icon_state = "tentacle"
name = "Tentacle"
- alt_aroused = TRUE
/datum/sprite_accessory/vagina/dentata
icon_state = "dentata"
name = "Dentata"
- alt_aroused = TRUE
/datum/sprite_accessory/vagina/hairy
icon_state = "hairy"
name = "Hairy"
+ alt_aroused = FALSE
/datum/sprite_accessory/vagina/spade
icon_state = "spade"
name = "Spade"
+ alt_aroused = FALSE
/datum/sprite_accessory/vagina/furred
icon_state = "furred"
name = "Furred"
+ alt_aroused = FALSE
/datum/sprite_accessory/vagina/gaping
icon_state = "gaping"
@@ -125,7 +125,6 @@
icon = 'modular_citadel/icons/obj/genitals/breasts_onmob.dmi'
name = "breasts"
color_src = "breasts_color"
- alt_aroused = TRUE
/datum/sprite_accessory/breasts/pair
icon_state = "pair"
diff --git a/modular_citadel/code/modules/arousal/organs/breasts.dm b/modular_citadel/code/modules/arousal/organs/breasts.dm
index d1f1a08ea8..f3ce834838 100644
--- a/modular_citadel/code/modules/arousal/organs/breasts.dm
+++ b/modular_citadel/code/modules/arousal/organs/breasts.dm
@@ -1,3 +1,6 @@
+#define BREASTS_ICON_MIN_SIZE 1
+#define BREASTS_ICON_MAX_SIZE 6
+
/obj/item/organ/genital/breasts
name = "breasts"
desc = "Female milk producing organs."
@@ -9,7 +12,7 @@
fluid_id = /datum/reagent/consumable/milk
fluid_rate = MILK_RATE
shape = "pair"
- genital_flags = CAN_MASTURBATE_WITH|CAN_CLIMAX_WITH|GENITAL_FUID_PRODUCTION
+ genital_flags = CAN_MASTURBATE_WITH|CAN_CLIMAX_WITH|GENITAL_FUID_PRODUCTION|GENITAL_CAN_AROUSE
masturbation_verb = "massage"
arousal_verb = "Your breasts start feeling sensitive"
unarousal_verb = "Your breasts no longer feel sensitive"
@@ -19,8 +22,8 @@
var/cached_size //these two vars pertain size modifications and so should be expressed in NUMBERS.
var/prev_size //former cached_size value, to allow update_size() to early return should be there no significant changes.
-/obj/item/organ/genital/breasts/Initialize(mapload, mob/living/carbon/human/H)
- if(!H)
+/obj/item/organ/genital/breasts/Initialize(mapload, do_update = TRUE)
+ if(do_update)
cached_size = breast_values[size]
prev_size = cached_size
return ..()
@@ -46,22 +49,24 @@
desc += " You estimate that they're [uppertext(size)]-cups."
if(CHECK_BITFIELD(genital_flags, GENITAL_FUID_PRODUCTION) && aroused_state)
- desc += " They're leaking [initial(fluid_id.name)]."
- var/string
+ var/datum/reagent/R = GLOB.chemical_reagents_list[fluid_id]
+ if(R)
+ desc += " They're leaking [lowertext(R.name)]."
+ var/datum/sprite_accessory/S = GLOB.breasts_shapes_list[shape]
+ var/icon_shape = S ? S.icon_state : "pair"
+ var/icon_size = CLAMP(breast_values[size], BREASTS_ICON_MIN_SIZE, BREASTS_ICON_MAX_SIZE)
+ icon_state = "breasts_[icon_shape]_[breast_values[icon_size]]"
if(owner)
if(owner.dna.species.use_skintones && owner.dna.features["genitals_use_skintone"])
if(ishuman(owner)) // Check before recasting type, although someone fucked up if you're not human AND have use_skintones somehow...
var/mob/living/carbon/human/H = owner // only human mobs have skin_tone, which we need.
color = "#[skintone2hex(H.skin_tone)]"
- string = "breasts_[GLOB.breasts_shapes_icons[shape]]_[size]-s"
+ icon_state += "_s"
else
color = "#[owner.dna.features["breasts_color"]]"
- string = "breasts_[GLOB.breasts_shapes_icons[shape]]_[size]"
if(ishuman(owner))
var/mob/living/carbon/human/H = owner
- icon_state = sanitize_text(string)
H.update_genitals()
- icon_state = sanitize_text(string)
//Allows breasts to grow and change size, with sprite changes too.
//maximum wah
@@ -120,7 +125,6 @@
color = "#[D.features["breasts_color"]]"
size = D.features["breasts_size"]
shape = D.features["breasts_shape"]
- fluid_id = D.features["breasts_fluid"]
if(!D.features["breasts_producing"])
DISABLE_BITFIELD(genital_flags, GENITAL_FUID_PRODUCTION|CAN_CLIMAX_WITH|CAN_MASTURBATE_WITH)
if(!isnum(size))
@@ -129,3 +133,6 @@
cached_size = size
size = breast_values[size]
prev_size = cached_size
+
+#undef BREASTS_ICON_MIN_SIZE
+#undef BREASTS_ICON_MAX_SIZE
\ No newline at end of file
diff --git a/modular_citadel/code/modules/arousal/organs/penis.dm b/modular_citadel/code/modules/arousal/organs/penis.dm
index c6d3c764ac..38f463a97c 100644
--- a/modular_citadel/code/modules/arousal/organs/penis.dm
+++ b/modular_citadel/code/modules/arousal/organs/penis.dm
@@ -8,13 +8,13 @@
masturbation_verb = "stroke"
arousal_verb = "You pop a boner"
unarousal_verb = "Your boner goes down"
- genital_flags = CAN_MASTURBATE_WITH|CAN_CLIMAX_WITH
+ genital_flags = CAN_MASTURBATE_WITH|CAN_CLIMAX_WITH|GENITAL_CAN_AROUSE
linked_organ_slot = ORGAN_SLOT_TESTICLES
fluid_transfer_factor = 0.5
size = 2 //arbitrary value derived from length and girth for sprites.
layer_index = PENIS_LAYER_INDEX
var/length = 6 //inches
- var/prev_length = 6 //really should be renamed to prev_length
+ var/prev_length = 6
var/girth = 4.38
var/girth_ratio = COCK_GIRTH_RATIO_DEF //0.73; check citadel_defines.dm
@@ -73,22 +73,21 @@
/obj/item/organ/genital/penis/update_appearance()
. = ..()
- var/string
+ var/datum/sprite_accessory/S = GLOB.cock_shapes_list[shape]
+ var/icon_shape = S ? S.icon_state : "human"
+ icon_state = "penis_[icon_shape]_[size]"
var/lowershape = lowertext(shape)
desc = "You see [aroused_state ? "an erect" : "a flaccid"] [lowershape] [name]. You estimate it's about [round(length, 0.25)] inch[round(length, 0.25) != 1 ? "es" : ""] long and [round(girth, 0.25)] inch[round(girth, 0.25) != 1 ? "es" : ""] in girth."
-
if(owner)
if(owner.dna.species.use_skintones && owner.dna.features["genitals_use_skintone"])
if(ishuman(owner)) // Check before recasting type, although someone fucked up if you're not human AND have use_skintones somehow...
var/mob/living/carbon/human/H = owner // only human mobs have skin_tone, which we need.
color = "#[skintone2hex(H.skin_tone)]"
- string = "penis_[GLOB.cock_shapes_icons[shape]]_[size]-s"
+ icon_state += "_s"
else
color = "#[owner.dna.features["cock_color"]]"
- string = "penis_[GLOB.cock_shapes_icons[shape]]_[size]"
if(ishuman(owner))
var/mob/living/carbon/human/H = owner
- icon_state = sanitize_text(string)
H.update_genitals()
/obj/item/organ/genital/penis/get_features(mob/living/carbon/human/H)
diff --git a/modular_citadel/code/modules/arousal/organs/testicles.dm b/modular_citadel/code/modules/arousal/organs/testicles.dm
index e5b34926de..eb6c1536cb 100644
--- a/modular_citadel/code/modules/arousal/organs/testicles.dm
+++ b/modular_citadel/code/modules/arousal/organs/testicles.dm
@@ -43,19 +43,19 @@
/obj/item/organ/genital/testicles/update_appearance()
. = ..()
desc = "You see an [size_name] pair of testicles."
+ var/datum/sprite_accessory/S = GLOB.balls_shapes_list[shape]
+ var/icon_shape = S ? S.icon_state : "single"
+ icon_state = "testicles_[icon_shape]_[size]"
if(owner)
- var/string
if(owner.dna.species.use_skintones && owner.dna.features["genitals_use_skintone"])
if(ishuman(owner)) // Check before recasting type, although someone fucked up if you're not human AND have use_skintones somehow...
var/mob/living/carbon/human/H = owner // only human mobs have skin_tone, which we need.
color = "#[skintone2hex(H.skin_tone)]"
- string = "testicles_[GLOB.balls_shapes_icons[shape]]_[size]-s"
+ icon_state += "_s"
else
color = "#[owner.dna.features["balls_color"]]"
- string = "testicles_[GLOB.balls_shapes_icons[shape]]_[size]"
if(ishuman(owner))
var/mob/living/carbon/human/H = owner
- icon_state = sanitize_text(string)
H.update_genitals()
/obj/item/organ/genital/testicles/get_features(mob/living/carbon/human/H)
@@ -68,7 +68,6 @@
shape = D.features["balls_shape"]
if(D.features["balls_shape"] == "Hidden")
ENABLE_BITFIELD(genital_flags, GENITAL_INTERNAL)
- fluid_id = D.features["balls_fluid"]
fluid_rate = D.features["balls_cum_rate"]
fluid_mult = D.features["balls_cum_mult"]
fluid_efficiency = D.features["balls_efficiency"]
diff --git a/modular_citadel/code/modules/arousal/organs/vagina.dm b/modular_citadel/code/modules/arousal/organs/vagina.dm
index 31d116b48e..311afe75b0 100644
--- a/modular_citadel/code/modules/arousal/organs/vagina.dm
+++ b/modular_citadel/code/modules/arousal/organs/vagina.dm
@@ -6,10 +6,10 @@
zone = BODY_ZONE_PRECISE_GROIN
slot = "vagina"
size = 1 //There is only 1 size right now
- genital_flags = CAN_MASTURBATE_WITH|CAN_CLIMAX_WITH
+ genital_flags = CAN_MASTURBATE_WITH|CAN_CLIMAX_WITH|GENITAL_CAN_AROUSE
masturbation_verb = "finger"
- arousal_verb = "You feel wetness on your crotch."
- unarousal_verb = "You no longer feel wet."
+ arousal_verb = "You feel wetness on your crotch"
+ unarousal_verb = "You no longer feel wet"
fluid_transfer_factor = 0.1 //Yes, some amount is exposed to you, go get your AIDS
layer_index = VAGINA_LAYER_INDEX
var/cap_length = 8//D E P T H (cap = capacity)
@@ -22,7 +22,7 @@
/obj/item/organ/genital/vagina/update_appearance()
. = ..()
- var/string //Keeping this code here, so making multiple sprites for the different kinds is easier.
+ icon_state = "vagina"
var/lowershape = lowertext(shape)
var/details
@@ -55,13 +55,11 @@
if(ishuman(owner)) // Check before recasting type, although someone fucked up if you're not human AND have use_skintones somehow...
var/mob/living/carbon/human/H = owner // only human mobs have skin_tone, which we need.
color = "#[skintone2hex(H.skin_tone)]"
- string = "vagina-s"
+ icon_state += "_s"
else
color = "#[owner.dna.features["vag_color"]]"
- string = "vagina"
if(ishuman(owner))
var/mob/living/carbon/human/H = owner
- icon_state = sanitize_text(string)
H.update_genitals()
/obj/item/organ/genital/vagina/get_features(mob/living/carbon/human/H)
diff --git a/modular_citadel/code/modules/client/loadout/__donator.dm b/modular_citadel/code/modules/client/loadout/__donator.dm
index cf68666532..92a1b26f01 100644
--- a/modular_citadel/code/modules/client/loadout/__donator.dm
+++ b/modular_citadel/code/modules/client/loadout/__donator.dm
@@ -305,7 +305,7 @@ datum/gear/darksabresheath
/datum/gear/flagcape
name = "US Flag Cape"
category = SLOT_IN_BACKPACK
- path = /obj/item/bedsheet/custom/flagcape
+ path = /obj/item/clothing/neck/flagcape
ckeywhitelist = list("darnchacha")
/datum/gear/luckyjack
@@ -488,3 +488,9 @@ datum/gear/darksabresheath
category = SLOT_SHOES
path = /obj/item/clothing/shoes/sneakers/mikuleggings
ckeywhitelist = list("grandvegeta")
+
+/datum/gear/cosmos
+ name = "cosmic space bedsheet"
+ category = SLOT_IN_BACKPACK
+ path = /obj/item/bedsheet/cosmos
+ ckeywhitelist = list("grunnyyy")
diff --git a/modular_citadel/code/modules/client/loadout/_loadout.dm b/modular_citadel/code/modules/client/loadout/_loadout.dm
index d48da1b863..d35bede179 100644
--- a/modular_citadel/code/modules/client/loadout/_loadout.dm
+++ b/modular_citadel/code/modules/client/loadout/_loadout.dm
@@ -13,7 +13,7 @@ GLOBAL_LIST_EMPTY(loadout_whitelist_ids)
LAZYINITLIST(GLOB.loadout_whitelist_ids)
var/list/file_lines = world.file2list(loadout_config)
for(var/line in file_lines)
- if(!line || findtextEx(line,"#",1,2))
+ if(!line || line[1] == "#")
continue
var/list/lineinfo = splittext(line, "|")
var/lineID = lineinfo[1]
@@ -21,7 +21,7 @@ GLOBAL_LIST_EMPTY(loadout_whitelist_ids)
var/sublinetypedef = findtext(subline, "=")
if(sublinetypedef)
var/sublinetype = copytext(subline, 1, sublinetypedef)
- var/list/sublinecontent = splittext(copytext(subline, sublinetypedef+1), ",")
+ var/list/sublinecontent = splittext(copytext(subline, sublinetypedef+ length(sublinetypedef)), ",")
if(sublinetype == "WHITELIST")
GLOB.loadout_whitelist_ids["[lineID]"] = sublinecontent
diff --git a/modular_citadel/code/modules/client/loadout/_medical.dm b/modular_citadel/code/modules/client/loadout/_medical.dm
index eed1ad32a1..b1b5c9a62e 100644
--- a/modular_citadel/code/modules/client/loadout/_medical.dm
+++ b/modular_citadel/code/modules/client/loadout/_medical.dm
@@ -1,8 +1,9 @@
-/datum/gear/stethoscope
+/datum/gear/medicbriefcase
name = "Medical Briefcase"
category = SLOT_HANDS
path = /obj/item/storage/briefcase/medical
restricted_roles = list("Medical Doctor", "Chief Medical Officer")
+ restricted_desc = "MD, CMO"
/datum/gear/stethoscope
name = "Stethoscope"
@@ -30,3 +31,17 @@
path = /obj/item/clothing/under/rank/medical/purple
restricted_roles = list("Medical Doctor", "Chief Medical Officer", "Geneticist", "Chemist", "Virologist")
restricted_desc = "Medical"
+
+/datum/gear/nursehat
+ name = "Nurse Hat"
+ category = SLOT_HEAD
+ path = /obj/item/clothing/head/nursehat
+ restricted_roles = list("Medical Doctor", "Chief Medical Officer", "Geneticist", "Chemist", "Virologist")
+ restricted_desc = "Medical"
+
+/datum/gear/nursesuit
+ name = "Nurse Suit"
+ category = SLOT_W_UNIFORM
+ path = /obj/item/clothing/under/rank/nursesuit
+ restricted_roles = list("Medical Doctor", "Chief Medical Officer", "Geneticist", "Chemist", "Virologist")
+ restricted_desc = "Medical"
\ No newline at end of file
diff --git a/modular_citadel/code/modules/client/loadout/_security.dm b/modular_citadel/code/modules/client/loadout/_security.dm
index 7a49b1f049..c9591540e6 100644
--- a/modular_citadel/code/modules/client/loadout/_security.dm
+++ b/modular_citadel/code/modules/client/loadout/_security.dm
@@ -5,7 +5,7 @@
restricted_roles = list("Head of Security")
/datum/gear/navybluehosberet
- name = "Head of security's Naviblue beret"
+ name = "Head of security's navyblue beret"
category = SLOT_HEAD
path = /obj/item/clothing/head/beret/sec/navyhos
restricted_roles = list("Head of Security")
@@ -13,13 +13,13 @@
/datum/gear/navybluejackethos
name = "head of security's navyblue jacket"
category = SLOT_WEAR_SUIT
- path = /obj/item/clothing/suit/security/hos
+ path = /obj/item/clothing/suit/armor/hos/navyblue
restricted_roles = list("Head of Security")
/datum/gear/navybluejacketofficer
name = "security officer's navyblue jacket"
category = SLOT_WEAR_SUIT
- path = /obj/item/clothing/suit/security/officer
+ path = /obj/item/clothing/suit/armor/navyblue
restricted_roles = list("Security Officer")
/datum/gear/navyblueofficerberet
@@ -37,7 +37,7 @@
/datum/gear/navybluejacketwarden
name = "warden navyblue jacket"
category = SLOT_WEAR_SUIT
- path = /obj/item/clothing/suit/security/warden
+ path = /obj/item/clothing/suit/armor/vest/warden/navyblue
restricted_roles = list("Warden")
/datum/gear/navybluewardenberet
diff --git a/modular_citadel/code/modules/client/loadout/backpack.dm b/modular_citadel/code/modules/client/loadout/backpack.dm
index 48608e713e..d51f4b6125 100644
--- a/modular_citadel/code/modules/client/loadout/backpack.dm
+++ b/modular_citadel/code/modules/client/loadout/backpack.dm
@@ -95,7 +95,7 @@
name = "Newspaper"
category = SLOT_IN_BACKPACK
path = /obj/item/newspaper
-
+
/datum/gear/paperbin
name = "Paper Bin"
category = SLOT_IN_BACKPACK
@@ -128,4 +128,22 @@
category = SLOT_IN_BACKPACK
path = /obj/item/modular_computer/laptop/preset/civilian
cost = 7
+
+/datum/gear/ringbox_gold
+ name = "A gold ring box"
+ category = SLOT_IN_BACKPACK
+ path = /obj/item/storage/fancy/ringbox
+ cost = 3
+
+/datum/gear/ringbox_silver
+ name = "A silver ring box"
+ category = SLOT_IN_BACKPACK
+ path = /obj/item/storage/fancy/ringbox/silver
+ cost = 3
+
+/datum/gear/ringbox_diamond
+ name = "A diamond ring box"
+ category = SLOT_IN_BACKPACK
+ path = /obj/item/storage/fancy/ringbox/diamond
+ cost = 5
diff --git a/modular_citadel/code/modules/client/loadout/gloves.dm b/modular_citadel/code/modules/client/loadout/gloves.dm
index 826e809652..72e6e91cfc 100644
--- a/modular_citadel/code/modules/client/loadout/gloves.dm
+++ b/modular_citadel/code/modules/client/loadout/gloves.dm
@@ -2,3 +2,22 @@
name = "Fingerless Gloves"
category = SLOT_GLOVES
path = /obj/item/clothing/gloves/fingerless
+
+/datum/gear/goldring
+ name = "A gold ring"
+ category = SLOT_GLOVES
+ path = /obj/item/clothing/gloves/ring
+ cost = 2
+
+/datum/gear/silverring
+ name = "A silver ring"
+ category = SLOT_GLOVES
+ path = /obj/item/clothing/gloves/ring/silver
+ cost = 2
+
+/datum/gear/diamondring
+ name = "A diamond ring"
+ category = SLOT_GLOVES
+ path = /obj/item/clothing/gloves/ring/diamond
+ cost = 4
+
diff --git a/modular_citadel/code/modules/client/loadout/head.dm b/modular_citadel/code/modules/client/loadout/head.dm
index 8634460d4e..dd7d2cdc4b 100644
--- a/modular_citadel/code/modules/client/loadout/head.dm
+++ b/modular_citadel/code/modules/client/loadout/head.dm
@@ -59,6 +59,11 @@
category = SLOT_HEAD
path = /obj/item/clothing/head/rabbitears
+/datum/gear/mailmanhat
+ name = "Mailman's Hat"
+ category = SLOT_HEAD
+ path = /obj/item/clothing/head/mailman
+
//trek fancy Hats!
/datum/gear/trekcap
name = "Federation Officer's Cap (White)"
@@ -105,6 +110,7 @@
path = /obj/item/clothing/head/christmashatg
*/
+//Cowboy Stuff
/datum/gear/cowboyhat
name = "Cowboy Hat, Brown"
category = SLOT_HEAD
diff --git a/modular_citadel/code/modules/client/loadout/suit.dm b/modular_citadel/code/modules/client/loadout/suit.dm
index d5f2e3cbd1..9b39d006bd 100644
--- a/modular_citadel/code/modules/client/loadout/suit.dm
+++ b/modular_citadel/code/modules/client/loadout/suit.dm
@@ -79,6 +79,12 @@
category = SLOT_WEAR_SUIT
path = /obj/item/clothing/suit/hooded/wintercoat/ratvar/fake
+/datum/gear/coat/polycoat
+ name = "Polychromic winter coat"
+ category = SLOT_WEAR_SUIT
+ path = /obj/item/clothing/suit/hooded/wintercoat/polychromic
+ cost = 4 //too many people with neon green coats is hard on the eyes
+
/* Commented out until it is "balanced"
/datum/gear/coat/sec
name = "Security winter coat"
diff --git a/modular_citadel/code/modules/client/loadout/uniform.dm b/modular_citadel/code/modules/client/loadout/uniform.dm
index 46692ea7a9..b6b72669c2 100644
--- a/modular_citadel/code/modules/client/loadout/uniform.dm
+++ b/modular_citadel/code/modules/client/loadout/uniform.dm
@@ -424,3 +424,38 @@
path = /obj/item/clothing/under/stripper_green
cost = 3
+/datum/gear/qipao
+ name = "Qipao, Black"
+ category = SLOT_W_UNIFORM
+ path = /obj/item/clothing/under/lunar/qipao
+ cost = 3
+
+/datum/gear/qipao/white
+ name = "Qipao, White"
+ category = SLOT_W_UNIFORM
+ path = /obj/item/clothing/under/lunar/qipao/white
+ cost = 3
+
+/datum/gear/qipao/red
+ name = "Qipao, Red"
+ category = SLOT_W_UNIFORM
+ path = /obj/item/clothing/under/lunar/qipao/red
+ cost = 3
+
+/datum/gear/cheongsam
+ name = "Cheongsam, Black"
+ category = SLOT_W_UNIFORM
+ path = /obj/item/clothing/under/lunar/cheongsam
+ cost = 3
+
+/datum/gear/cheongsam/white
+ name = "Cheongsam, White"
+ category = SLOT_W_UNIFORM
+ path = /obj/item/clothing/under/lunar/cheongsam/white
+ cost = 3
+
+/datum/gear/cheongsam/red
+ name = "Cheongsam, Red"
+ category = SLOT_W_UNIFORM
+ path = /obj/item/clothing/under/lunar/cheongsam/red
+ cost = 3
\ No newline at end of file
diff --git a/modular_citadel/code/modules/client/preferences.dm b/modular_citadel/code/modules/client/preferences.dm
index 4e7cb2972f..8b0ba2dd43 100644
--- a/modular_citadel/code/modules/client/preferences.dm
+++ b/modular_citadel/code/modules/client/preferences.dm
@@ -23,9 +23,6 @@
var/hound_sleeper = TRUE
var/cit_toggles = TOGGLES_CITADEL
- // stuff that was in base
- max_save_slots = 10
-
/datum/preferences/New(client/C)
..()
diff --git a/modular_citadel/code/modules/client/preferences_savefile.dm b/modular_citadel/code/modules/client/preferences_savefile.dm
index 5584181a70..334f301dca 100644
--- a/modular_citadel/code/modules/client/preferences_savefile.dm
+++ b/modular_citadel/code/modules/client/preferences_savefile.dm
@@ -57,13 +57,11 @@
WRITE_FILE(S["feature_balls_size"], features["balls_size"])
WRITE_FILE(S["feature_balls_shape"], features["balls_shape"])
WRITE_FILE(S["feature_balls_sack_size"], features["balls_sack_size"])
- WRITE_FILE(S["feature_balls_fluid"], features["balls_fluid"])
//breasts features
WRITE_FILE(S["feature_has_breasts"], features["has_breasts"])
WRITE_FILE(S["feature_breasts_size"], features["breasts_size"])
WRITE_FILE(S["feature_breasts_shape"], features["breasts_shape"])
WRITE_FILE(S["feature_breasts_color"], features["breasts_color"])
- WRITE_FILE(S["feature_breasts_fluid"], features["breasts_fluid"])
WRITE_FILE(S["feature_breasts_producing"], features["breasts_producing"])
//vagina features
WRITE_FILE(S["feature_has_vag"], features["has_vag"])
diff --git a/modular_citadel/code/modules/custom_loadout/custom_items.dm b/modular_citadel/code/modules/custom_loadout/custom_items.dm
index eaf128626d..e1d204c74d 100644
--- a/modular_citadel/code/modules/custom_loadout/custom_items.dm
+++ b/modular_citadel/code/modules/custom_loadout/custom_items.dm
@@ -364,10 +364,11 @@
worn_x_dimension = 64
worn_y_dimension = 34
-/obj/item/bedsheet/custom/flagcape
+/obj/item/clothing/neck/flagcape
name = "Flag Cape"
desc = "A truly patriotic form of heroic attire."
icon = 'icons/obj/custom.dmi'
+ resistance_flags = FLAMMABLE
alternate_worn_icon = 'icons/mob/custom_w.dmi'
icon_state = "flagcape"
item_state = "flagcape"
@@ -556,3 +557,13 @@
icon = 'icons/obj/custom.dmi'
alternate_worn_icon = 'icons/mob/custom_w.dmi'
mutantrace_variation = NONE
+
+/obj/item/toy/plush/mammal/dog/fritz
+ icon = 'icons/obj/custom.dmi'
+ icon_state = "fritz"
+ item_state = "fritz"
+ attack_verb = list("barked", "boofed", "shotgun'd")
+ obj_flags = UNIQUE_RENAME
+ unique_reskin = list("Goodboye" = "fritz", "Badboye" = "fritz_bad")
+ mutantrace_variation = NONE
+
diff --git a/modular_citadel/code/modules/custom_loadout/read_from_file.dm b/modular_citadel/code/modules/custom_loadout/read_from_file.dm
index 0ed38e8d41..004757add4 100644
--- a/modular_citadel/code/modules/custom_loadout/read_from_file.dm
+++ b/modular_citadel/code/modules/custom_loadout/read_from_file.dm
@@ -13,18 +13,18 @@ GLOBAL_LIST(custom_item_list)
GLOB.custom_item_list = list()
var/list/file_lines = world.file2list(custom_filelist)
for(var/line in file_lines)
- if(length(line) == 0) //Emptyline, no one cares.
+ if(!length(line)) //Emptyline, no one cares.
continue
- if(copytext(line,1,3) == "//") //Commented line, ignore.
+ if(copytext(line,1,3) == "//") //Commented line, ignore. 3 == length("//") + 1
continue
var/ckey_str_sep = findtext(line, "|") //Process our stuff..
- var/char_str_sep = findtext(line, "|", ckey_str_sep+1)
- var/job_str_sep = findtext(line, "|", char_str_sep+1)
- var/item_str_sep = findtext(line, "|", job_str_sep+1)
+ var/char_str_sep = findtext(line, "|", ckey_str_sep + length(line[ckey_str_sep]))
+ var/job_str_sep = findtext(line, "|", char_str_sep + length(line[char_str_sep]))
+ var/item_str_sep = findtext(line, "|", job_str_sep + length(line[job_str_sep]))
var/ckey_str = ckey(copytext(line, 1, ckey_str_sep))
- var/char_str = copytext(line, ckey_str_sep+1, char_str_sep)
- var/job_str = copytext(line, char_str_sep+1, job_str_sep)
- var/item_str = copytext(line, job_str_sep+1, item_str_sep)
+ var/char_str = copytext(line, ckey_str_sep + length(line[ckey_str_sep]), char_str_sep)
+ var/job_str = copytext(line, char_str_sep + length(line[char_str_sep]), job_str_sep)
+ var/item_str = copytext(line, job_str_sep + length(line[job_str_sep]), item_str_sep)
if(!ckey_str || !char_str || !job_str || !item_str || !length(ckey_str) || !length(char_str) || !length(job_str) || !length(item_str))
log_admin("Errored custom_items_whitelist line: [line] - Component/separator missing!")
if(!islist(GLOB.custom_item_list[ckey_str]))
@@ -42,7 +42,7 @@ GLOBAL_LIST(custom_item_list)
for(var/item_string in item_strings)
var/path_str_sep = findtext(item_string, "=")
var/path = copytext(item_string, 1, path_str_sep) //Path to spawn
- var/amount = copytext(item_string, path_str_sep+1) //Amount to spawn
+ var/amount = copytext(item_string, path_str_sep + length(item_string[path_str_sep])) //Amount to spawn
//world << "DEBUG: Item string [item_string] processed"
amount = text2num(amount)
path = text2path(path)
diff --git a/modular_citadel/code/modules/mentor/mentorhelp.dm b/modular_citadel/code/modules/mentor/mentorhelp.dm
index eb2b8546da..d056c03f49 100644
--- a/modular_citadel/code/modules/mentor/mentorhelp.dm
+++ b/modular_citadel/code/modules/mentor/mentorhelp.dm
@@ -10,9 +10,9 @@
spawn(300)
verbs += /client/verb/mentorhelp // 30 second cool-down for mentorhelp
- msg = sanitize(copytext(msg,1,MAX_MESSAGE_LEN))
- if(!msg) return
- if(!mob) return //this doesn't happen
+ msg = sanitize(copytext_char(msg, 1, MAX_MESSAGE_LEN))
+ if(!msg || !mob)
+ return
var/show_char = CONFIG_GET(flag/mentors_mobname_only)
var/mentor_msg = "MENTORHELP:[key_name_mentor(src, 1, 0, 1, show_char)]: [msg]"
diff --git a/modular_citadel/code/modules/mentor/mentorpm.dm b/modular_citadel/code/modules/mentor/mentorpm.dm
index 4c9a4766ec..cb39f02092 100644
--- a/modular_citadel/code/modules/mentor/mentorpm.dm
+++ b/modular_citadel/code/modules/mentor/mentorpm.dm
@@ -53,7 +53,7 @@
if (!C.is_mentor() && !is_mentor())
return
- msg = sanitize(copytext(msg,1,MAX_MESSAGE_LEN))
+ msg = sanitize(copytext_char(msg, 1, MAX_MESSAGE_LEN))
if(!msg && is_mentor(whom))
to_chat(GLOB.admins | GLOB.mentors, "[src] has stopped their reply to [whom]'s mhelp.")
return
diff --git a/modular_citadel/code/modules/mentor/mentorsay.dm b/modular_citadel/code/modules/mentor/mentorsay.dm
index 6baf969251..c13e3c6ef3 100644
--- a/modular_citadel/code/modules/mentor/mentorsay.dm
+++ b/modular_citadel/code/modules/mentor/mentorsay.dm
@@ -5,8 +5,9 @@
if(!is_mentor())
return
- msg = copytext(sanitize(msg), 1, MAX_MESSAGE_LEN)
- if(!msg) return
+ msg = copytext_char(sanitize(msg), 1, MAX_MESSAGE_LEN)
+ if(!msg)
+ return
msg = emoji_parse(msg)
log_mentor("MSAY: [key_name(src)] : [msg]")
diff --git a/modular_citadel/code/modules/mob/cit_emotes.dm b/modular_citadel/code/modules/mob/cit_emotes.dm
index d0ad0d3232..2be83733e5 100644
--- a/modular_citadel/code/modules/mob/cit_emotes.dm
+++ b/modular_citadel/code/modules/mob/cit_emotes.dm
@@ -14,13 +14,10 @@
emote_type = EMOTE_AUDIBLE
/datum/emote/living/insult/run_emote(mob/living/user, params)
- var/insult_message = ""
- var/miming = user.mind ? user.mind.miming : 0
- if(!user.is_muzzled())
- insult_message += pick_list_replacements(INSULTS_FILE, "insult_gen")
- message = insult_message
- else if(miming)
+ if(user.mind?.miming)
message = "creatively gesticulates."
+ else if(!user.is_muzzled())
+ message = pick_list_replacements(INSULTS_FILE, "insult_gen")
else
message = "muffles something."
. = ..()
@@ -36,7 +33,7 @@
sound = 'modular_citadel/sound/voice/scream_silicon.ogg'
if(iscyborg(user))
var/mob/living/silicon/robot/S = user
- if(S.cell.charge < 20)
+ if(S.cell?.charge < 20)
to_chat(S, "Scream module deactivated. Please recharge.")
return
S.cell.use(200)
diff --git a/modular_citadel/code/modules/mob/living/carbon/human/human_movement.dm b/modular_citadel/code/modules/mob/living/carbon/human/human_movement.dm
index 0b6903c9fe..bd43d96ba4 100644
--- a/modular_citadel/code/modules/mob/living/carbon/human/human_movement.dm
+++ b/modular_citadel/code/modules/mob/living/carbon/human/human_movement.dm
@@ -12,8 +12,11 @@
/mob/living/carbon/human/movement_delay()
. = 0
- if(!resting && m_intent == MOVE_INTENT_RUN && !sprinting)
- . += 1
+ if(!resting && m_intent == MOVE_INTENT_RUN && sprinting)
+ var/static/datum/config_entry/number/movedelay/sprint_speed_increase/SSI
+ if(!SSI)
+ SSI = CONFIG_GET_ENTRY(number/movedelay/sprint_speed_increase)
+ . -= SSI.config_entry_value
if(wrongdirmovedelay)
. += 1
. += ..()
diff --git a/modular_citadel/code/modules/mob/living/living.dm b/modular_citadel/code/modules/mob/living/living.dm
index 513a80cae0..0caf548196 100644
--- a/modular_citadel/code/modules/mob/living/living.dm
+++ b/modular_citadel/code/modules/mob/living/living.dm
@@ -17,6 +17,12 @@
var/sprint_stamina_cost = 0.70 //stamina loss per tile while insufficient sprint buffer.
//---End
+/mob/living/update_config_movespeed()
+ . = ..()
+ sprint_buffer_max = CONFIG_GET(number/movedelay/sprint_buffer_max)
+ sprint_buffer_regen_ds = CONFIG_GET(number/movedelay/sprint_buffer_regen_per_ds)
+ sprint_stamina_cost = CONFIG_GET(number/movedelay/sprint_stamina_cost)
+
/mob/living/movement_delay(ignorewalk = 0)
. = ..()
if(resting)
diff --git a/modular_citadel/code/modules/projectiles/guns/ballistic/magweapon.dm b/modular_citadel/code/modules/projectiles/guns/ballistic/magweapon.dm
deleted file mode 100644
index 9779b47b15..0000000000
--- a/modular_citadel/code/modules/projectiles/guns/ballistic/magweapon.dm
+++ /dev/null
@@ -1,470 +0,0 @@
-///////XCOM X9 AR///////
-
-/obj/item/gun/ballistic/automatic/x9 //will be adminspawn only so ERT or something can use them
- name = "\improper X9 Assault Rifle"
- desc = "A rather old design of a cheap, reliable assault rifle made for combat against unknown enemies. Uses 5.56mm ammo."
- icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi'
- icon_state = "x9"
- item_state = "arg"
- slot_flags = 0
- mag_type = /obj/item/ammo_box/magazine/m556 //Uses the m90gl's magazine, just like the NT-ARG
- fire_sound = 'sound/weapons/gunshot_smg.ogg'
- can_suppress = 0
- burst_size = 6 //in line with XCOMEU stats. This can fire 5 bursts from a full magazine.
- fire_delay = 1
- spread = 30 //should be 40 for XCOM memes, but since its adminspawn only, might as well make it useable
- recoil = 1
-
-///toy memes///
-
-/obj/item/ammo_box/magazine/toy/x9
- name = "foam force X9 magazine"
- icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi'
- icon_state = "toy9magazine"
- max_ammo = 30
- multiple_sprites = 2
- materials = list(MAT_METAL = 200)
-
-/obj/item/gun/ballistic/automatic/x9/toy
- name = "\improper Foam Force X9"
- desc = "An old but reliable assault rifle made for combat against unknown enemies. Appears to be hastily converted. Ages 8 and up."
- icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi'
- icon_state = "toy9"
- can_suppress = 0
- obj_flags = 0
- mag_type = /obj/item/ammo_box/magazine/toy/x9
- casing_ejector = 0
- spread = 90 //MAXIMUM XCOM MEMES (actually that'd be 180 spread)
- w_class = WEIGHT_CLASS_BULKY
- weapon_weight = WEAPON_HEAVY
-
-////////XCOM2 Magpistol/////////
-
-//////projectiles//////
-
-/obj/item/projectile/bullet/mags
- icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi'
- icon_state = "magjectile"
- damage = 15
- armour_penetration = 10
- light_range = 2
- speed = 0.6
- range = 25
- light_color = LIGHT_COLOR_RED
-
-/obj/item/projectile/bullet/nlmags //non-lethal boolets
- icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi'
- icon_state = "magjectile-nl"
- damage = 2
- knockdown = 0
- stamina = 20
- light_range = 2
- speed = 0.6
- range = 25
- light_color = LIGHT_COLOR_BLUE
-
-
-/////actual ammo/////
-
-/obj/item/ammo_casing/caseless/amags
- desc = "A ferromagnetic slug intended to be launched out of a compatible weapon."
- caliber = "mags"
- icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi'
- icon_state = "mag-casing-live"
- projectile_type = /obj/item/projectile/bullet/mags
-
-/obj/item/ammo_casing/caseless/anlmags
- desc = "A specialized ferromagnetic slug designed with a less-than-lethal payload."
- caliber = "mags"
- icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi'
- icon_state = "mag-casing-live"
- projectile_type = /obj/item/projectile/bullet/nlmags
-
-//////magazines/////
-
-/obj/item/ammo_box/magazine/mmag/small
- name = "magpistol magazine (non-lethal disabler)"
- icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi'
- icon_state = "smallmagmag"
- ammo_type = /obj/item/ammo_casing/caseless/anlmags
- caliber = "mags"
- max_ammo = 15
- multiple_sprites = 2
-
-/obj/item/ammo_box/magazine/mmag/small/lethal
- name = "magpistol magazine (lethal)"
- icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi'
- icon_state = "smallmagmag"
- ammo_type = /obj/item/ammo_casing/caseless/amags
-
-//////the gun itself//////
-
-/obj/item/gun/ballistic/automatic/pistol/mag
- name = "magpistol"
- desc = "A handgun utilizing maglev technologies to propel a ferromagnetic slug to extreme velocities."
- icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi'
- icon_state = "magpistol"
- force = 10
- fire_sound = 'sound/weapons/magpistol.ogg'
- mag_type = /obj/item/ammo_box/magazine/mmag/small
- can_suppress = 0
- casing_ejector = FALSE
- fire_delay = 2
- recoil = 0.1
- inaccuracy_modifier = 0.25
-
-/obj/item/gun/ballistic/automatic/pistol/mag/update_icon()
- ..()
- if(magazine)
- cut_overlays()
- add_overlay("magpistol-magazine")
- else
- cut_overlays()
- icon_state = "[initial(icon_state)][chambered ? "" : "-e"]"
-
-///research memes///
-/obj/item/gun/ballistic/automatic/pistol/mag/nopin
- pin = null
- spawnwithmagazine = FALSE
-
-/datum/design/magpistol
- name = "Magpistol"
- desc = "A weapon which fires ferromagnetic slugs."
- id = "magpisol"
- build_type = PROTOLATHE
- materials = list(MAT_METAL = 7500, MAT_GLASS = 1000, MAT_URANIUM = 1000, MAT_TITANIUM = 5000, MAT_SILVER = 2000)
- build_path = /obj/item/gun/ballistic/automatic/pistol/mag/nopin
- category = list("Weapons")
- departmental_flags = DEPARTMENTAL_FLAG_SECURITY
-
-/datum/design/mag_magpistol
- name = "Magpistol Magazine"
- desc = "A 14 round magazine for the Magpistol."
- id = "mag_magpistol"
- build_type = PROTOLATHE
- materials = list(MAT_METAL = 4000, MAT_SILVER = 500)
- build_path = /obj/item/ammo_box/magazine/mmag/small/lethal
- category = list("Ammo")
- departmental_flags = DEPARTMENTAL_FLAG_SECURITY
-
-/datum/design/mag_magpistol/nl
- name = "Magpistol Magazine (Non-Lethal)"
- desc = "A 14 round non-lethal magazine for the Magpistol."
- id = "mag_magpistol_nl"
- materials = list(MAT_METAL = 3000, MAT_SILVER = 250, MAT_TITANIUM = 250)
- build_path = /obj/item/ammo_box/magazine/mmag/small
- departmental_flags = DEPARTMENTAL_FLAG_SECURITY
-
-//////toy memes/////
-
-/obj/item/projectile/bullet/reusable/foam_dart/mag
- name = "magfoam dart"
- icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi'
- icon_state = "magjectile-toy"
- ammo_type = /obj/item/ammo_casing/caseless/foam_dart/mag
- light_range = 2
- light_color = LIGHT_COLOR_YELLOW
-
-/obj/item/ammo_casing/caseless/foam_dart/mag
- name = "magfoam dart"
- desc = "A foam dart with fun light-up projectiles powered by magnets!"
- projectile_type = /obj/item/projectile/bullet/reusable/foam_dart/mag
-
-/obj/item/ammo_box/magazine/internal/shot/toy/mag
- ammo_type = /obj/item/ammo_casing/caseless/foam_dart/mag
- max_ammo = 14
-
-/obj/item/gun/ballistic/shotgun/toy/mag
- name = "foam force magpistol"
- desc = "A fancy toy sold alongside light-up foam force darts. Ages 8 and up."
- icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi'
- icon_state = "toymag"
- item_state = "gun"
- mag_type = /obj/item/ammo_box/magazine/internal/shot/toy/mag
- fire_sound = 'sound/weapons/magpistol.ogg'
- slot_flags = SLOT_BELT
- w_class = WEIGHT_CLASS_SMALL
-
-/obj/item/ammo_box/foambox/mag
- name = "ammo box (Magnetic Foam Darts)"
- icon = 'icons/obj/guns/toy.dmi'
- icon_state = "foambox"
- ammo_type = /obj/item/ammo_casing/caseless/foam_dart/mag
- max_ammo = 42
-
-//////Magrifle//////
-
-///projectiles///
-
-/obj/item/projectile/bullet/magrifle
- icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi'
- icon_state = "magjectile-large"
- damage = 20
- armour_penetration = 20
- light_range = 3
- speed = 0.6
- range = 35
- light_color = LIGHT_COLOR_RED
-
-/obj/item/projectile/bullet/nlmagrifle //non-lethal boolets
- icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi'
- icon_state = "magjectile-large-nl"
- damage = 2
- knockdown = 0
- stamina = 20
- armour_penetration = 10
- light_range = 3
- speed = 0.6
- range = 35
- light_color = LIGHT_COLOR_BLUE
-
-///ammo casings///
-
-/obj/item/ammo_casing/caseless/amagm
- desc = "A large ferromagnetic slug intended to be launched out of a compatible weapon."
- caliber = "magm"
- icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi'
- icon_state = "mag-casing-live"
- projectile_type = /obj/item/projectile/bullet/magrifle
- click_cooldown_override = 2.5
- delay = 3
-
-/obj/item/ammo_casing/caseless/anlmagm
- desc = "A large, specialized ferromagnetic slug designed with a less-than-lethal payload."
- caliber = "magm"
- icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi'
- icon_state = "mag-casing-live"
- projectile_type = /obj/item/projectile/bullet/nlmagrifle
- click_cooldown_override = 2.5
- delay = 3
-
-///magazines///
-
-/obj/item/ammo_box/magazine/mmag
- name = "magrifle magazine (non-lethal disabler)"
- icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi'
- icon_state = "mediummagmag"
- ammo_type = /obj/item/ammo_casing/caseless/anlmagm
- caliber = "magm"
- max_ammo = 24
- multiple_sprites = 2
-
-/obj/item/ammo_box/magazine/mmag/lethal
- name = "magrifle magazine (lethal)"
- icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi'
- icon_state = "mediummagmag"
- ammo_type = /obj/item/ammo_casing/caseless/amagm
- max_ammo = 24
-
-///the gun itself///
-
-/obj/item/gun/ballistic/automatic/magrifle
- name = "\improper Magnetic Rifle"
- desc = "A simple upscalling of the technologies used in the magpistol, the magrifle is capable of firing slightly larger slugs in bursts. Compatible with the magpistol's slugs."
- icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi'
- icon_state = "magrifle"
- item_state = "arg"
- slot_flags = NONE
- mag_type = /obj/item/ammo_box/magazine/mmag
- fire_sound = 'sound/weapons/magrifle.ogg'
- can_suppress = 0
- burst_size = 1
- actions_types = null
- fire_delay = 3
- spread = 0
- recoil = 0.1
- casing_ejector = FALSE
- inaccuracy_modifier = 0.5
- weapon_weight = WEAPON_MEDIUM
- dualwield_spread_mult = 1.4
-
-//research///
-
-/obj/item/gun/ballistic/automatic/magrifle/nopin
- pin = null
- spawnwithmagazine = FALSE
-
-/datum/design/magrifle
- name = "Magrifle"
- desc = "An upscaled Magpistol in rifle form."
- id = "magrifle"
- build_type = PROTOLATHE
- materials = list(MAT_METAL = 10000, MAT_GLASS = 2000, MAT_URANIUM = 2000, MAT_TITANIUM = 10000, MAT_SILVER = 4000, MAT_GOLD = 2000)
- build_path = /obj/item/gun/ballistic/automatic/magrifle/nopin
- category = list("Weapons")
- departmental_flags = DEPARTMENTAL_FLAG_SECURITY
-
-/datum/design/mag_magrifle
- name = "Magrifle Magazine (Lethal)"
- desc = "A 24-round magazine for the Magrifle."
- id = "mag_magrifle"
- build_type = PROTOLATHE
- materials = list(MAT_METAL = 8000, MAT_SILVER = 1000)
- build_path = /obj/item/ammo_box/magazine/mmag/lethal
- category = list("Ammo")
- departmental_flags = DEPARTMENTAL_FLAG_SECURITY
-
-/datum/design/mag_magrifle/nl
- name = "Magrifle Magazine (Non-Lethal)"
- desc = "A 24- round non-lethal magazine for the Magrifle."
- id = "mag_magrifle_nl"
- materials = list(MAT_METAL = 6000, MAT_SILVER = 500, MAT_TITANIUM = 500)
- build_path = /obj/item/ammo_box/magazine/mmag
- departmental_flags = DEPARTMENTAL_FLAG_SECURITY
-
-///foamagrifle///
-
-/obj/item/ammo_box/magazine/toy/foamag
- name = "foam force magrifle magazine"
- icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi'
- icon_state = "foamagmag"
- max_ammo = 24
- multiple_sprites = 2
- ammo_type = /obj/item/ammo_casing/caseless/foam_dart/mag
- materials = list(MAT_METAL = 200)
-
-/obj/item/gun/ballistic/automatic/magrifle/toy
- name = "foamag rifle"
- desc = "A foam launching magnetic rifle. Ages 8 and up."
- icon_state = "foamagrifle"
- obj_flags = 0
- mag_type = /obj/item/ammo_box/magazine/toy/foamag
- casing_ejector = FALSE
- spread = 60
- w_class = WEIGHT_CLASS_BULKY
- weapon_weight = WEAPON_HEAVY
-// TECHWEBS IMPLEMENTATION
-//
-
-/datum/techweb_node/magnetic_weapons
- id = "magnetic_weapons"
- display_name = "Magnetic Weapons"
- description = "Weapons using magnetic technology"
- prereq_ids = list("weaponry", "adv_weaponry", "emp_adv")
- design_ids = list("magrifle", "magpisol", "mag_magrifle", "mag_magrifle_nl", "mag_magpistol", "mag_magpistol_nl")
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
- export_price = 5000
-
-//////Hyper-Burst Rifle//////
-
-///projectiles///
-
-/obj/item/projectile/bullet/mags/hyper
- icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi'
- icon_state = "magjectile"
- damage = 10
- armour_penetration = 10
- stamina = 10
- movement_type = FLYING | UNSTOPPABLE
- range = 6
- light_range = 1
- light_color = LIGHT_COLOR_RED
-
-/obj/item/projectile/bullet/mags/hyper/inferno
- icon_state = "magjectile-large"
- stamina = 0
- movement_type = FLYING | UNSTOPPABLE
- range = 25
- light_range = 4
-
-/obj/item/projectile/bullet/mags/hyper/inferno/on_hit(atom/target, blocked = FALSE)
- ..()
- explosion(target, -1, 1, 2, 4, 5)
- return BULLET_ACT_HIT
-
-///ammo casings///
-
-/obj/item/ammo_casing/caseless/ahyper
- desc = "A large block of speciallized ferromagnetic material designed to be fired out of the experimental Hyper-Burst Rifle."
- caliber = "hypermag"
- icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi'
- icon_state = "hyper-casing-live"
- projectile_type = /obj/item/projectile/bullet/mags/hyper
- pellets = 12
- variance = 40
-
-/obj/item/ammo_casing/caseless/ahyper/inferno
- projectile_type = /obj/item/projectile/bullet/mags/hyper/inferno
- pellets = 1
- variance = 0
-
-///magazines///
-
-/obj/item/ammo_box/magazine/mhyper
- name = "hyper-burst rifle magazine"
- icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi'
- icon_state = "hypermag-4"
- ammo_type = /obj/item/ammo_casing/caseless/ahyper
- caliber = "hypermag"
- desc = "A magazine for the Hyper-Burst Rifle. Loaded with a special slug that fragments into 12 smaller shards which can absolutely puncture anything, but has rather short effective range."
- max_ammo = 4
-
-/obj/item/ammo_box/magazine/mhyper/update_icon()
- ..()
- icon_state = "hypermag-[ammo_count() ? "4" : "0"]"
-
-/obj/item/ammo_box/magazine/mhyper/inferno
- name = "hyper-burst rifle magazine (inferno)"
- ammo_type = /obj/item/ammo_casing/caseless/ahyper/inferno
- desc = "A magazine for the Hyper-Burst Rifle. Loaded with a special slug that violently reacts with whatever surface it strikes, generating a massive amount of heat and light."
-
-///gun itself///
-
-/obj/item/gun/ballistic/automatic/hyperburst
- name = "\improper Hyper-Burst Rifle"
- desc = "An extremely beefed up version of a stolen Nanotrasen weapon prototype, this 'rifle' is more like a cannon, with an extremely large bore barrel capable of generating several smaller magnetic 'barrels' to simultaneously launch multiple projectiles at once."
- icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi'
- icon_state = "hyperburst"
- item_state = "arg"
- slot_flags = 0
- mag_type = /obj/item/ammo_box/magazine/mhyper
- fire_sound = 'sound/weapons/magburst.ogg'
- can_suppress = 0
- burst_size = 1
- fire_delay = 40
- recoil = 2
- casing_ejector = 0
- weapon_weight = WEAPON_HEAVY
-
-/obj/item/gun/ballistic/automatic/hyperburst/update_icon()
- ..()
- icon_state = "hyperburst[magazine ? "-[get_ammo()]" : ""][chambered ? "" : "-e"]"
-
-///toy memes///
-
-/obj/item/projectile/beam/lasertag/mag //the projectile, compatible with regular laser tag armor
- icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi'
- icon_state = "magjectile-toy"
- name = "lasertag magbolt"
- movement_type = FLYING | UNSTOPPABLE //for penetration memes
- range = 5 //so it isn't super annoying
- light_range = 2
- light_color = LIGHT_COLOR_YELLOW
- eyeblur = 0
-
-/obj/item/ammo_casing/energy/laser/magtag
- projectile_type = /obj/item/projectile/beam/lasertag/mag
- select_name = "magtag"
- pellets = 3
- variance = 30
- e_cost = 1000
- fire_sound = 'sound/weapons/magburst.ogg'
-
-/obj/item/gun/energy/laser/practice/hyperburst
- name = "toy hyper-burst launcher"
- desc = "A toy laser with a unique beam shaping lens that projects harmless bolts capable of going through objects. Compatible with existing laser tag systems."
- ammo_type = list(/obj/item/ammo_casing/energy/laser/magtag)
- icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi'
- icon_state = "toyburst"
- clumsy_check = FALSE
- obj_flags = 0
- fire_delay = 40
- weapon_weight = WEAPON_HEAVY
- selfcharge = EGUN_SELFCHARGE
- charge_delay = 2
- recoil = 2
- cell_type = /obj/item/stock_parts/cell/toymagburst
-
-/obj/item/stock_parts/cell/toymagburst
- name = "toy mag burst rifle power supply"
- maxcharge = 4000
diff --git a/modular_citadel/code/modules/projectiles/guns/ballistic/magweapon_energy.dm b/modular_citadel/code/modules/projectiles/guns/ballistic/magweapon_energy.dm
deleted file mode 100644
index f847d04e9e..0000000000
--- a/modular_citadel/code/modules/projectiles/guns/ballistic/magweapon_energy.dm
+++ /dev/null
@@ -1,293 +0,0 @@
-///ammo///
-
-/obj/item/ammo_casing/caseless/mag_e
- var/energy_cost = 0
-
-/obj/item/ammo_casing/caseless/mag_e/amagm_e
- desc = "A large ferromagnetic slug intended to be launched out of a compatible weapon."
- caliber = "mag_e"
- icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi'
- icon_state = "mag-casing-live"
- projectile_type = /obj/item/projectile/bullet/magrifle
- energy_cost = 200
-
-/obj/item/ammo_casing/caseless/mag_e/anlmagm_e
- desc = "A large, specialized ferromagnetic slug designed with a less-than-lethal payload."
- caliber = "mag_e"
- icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi'
- icon_state = "mag-casing-live"
- projectile_type = /obj/item/projectile/bullet/nlmagrifle
- energy_cost = 200
-
-/obj/item/ammo_casing/caseless/mag_e/amags
- desc = "A ferromagnetic slug intended to be launched out of a compatible weapon."
- caliber = "mag_e"
- icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi'
- icon_state = "mag-casing-live"
- projectile_type = /obj/item/projectile/bullet/mags
- energy_cost = 125
-
-/obj/item/ammo_casing/caseless/mag_e/anlmags
- desc = "A specialized ferromagnetic slug designed with a less-than-lethal payload."
- caliber = "mag_e"
- icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi'
- icon_state = "mag-casing-live"
- projectile_type = /obj/item/projectile/bullet/nlmags
- energy_cost = 125
-
-///magazines///
-
-/obj/item/ammo_box/magazine/mmag_e/
- name = "magrifle magazine (non-lethal disabler)"
- icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi'
- icon_state = "mediummagmag"
- ammo_type = /obj/item/ammo_casing/caseless/mag_e/anlmagm_e
- caliber = "mag_e"
- max_ammo = 24
- multiple_sprites = 2
-
-/obj/item/ammo_box/magazine/mmag_e/lethal
- name = "magrifle magazine (lethal)"
- icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi'
- icon_state = "mediummagmag"
- ammo_type = /obj/item/ammo_casing/caseless/mag_e/amagm_e
- max_ammo = 24
-
-/obj/item/ammo_box/magazine/mmag_e/small
- name = "magpistol magazine (non-lethal disabler)"
- icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi'
- icon_state = "smallmagmag"
- ammo_type = /obj/item/ammo_casing/caseless/mag_e/anlmags
- caliber = "mag_e"
- max_ammo = 16
- multiple_sprites = 2
-
-/obj/item/ammo_box/magazine/mmag_e/small/lethal
- name = "magpistol magazine (lethal)"
- icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi'
- icon_state = "smallmagmag"
- ammo_type = /obj/item/ammo_casing/caseless/mag_e/amags
- max_ammo = 16
-
-///cells///
-
-/obj/item/stock_parts/cell/magrifle_e
- name = "magrifle power supply"
- maxcharge = 14400
- chargerate = 3520
-
-/obj/item/stock_parts/cell/magpistol_e
- name = "magpistol power supply"
- maxcharge = 6000
- chargerate = 1000
-
-///sci designs///
-
-/datum/design/magrifle_e
- name = "Magrifle"
- desc = "An upscaled Magpistol in rifle form."
- id = "magrifle_e"
- build_type = PROTOLATHE
- materials = list(MAT_METAL = 10000, MAT_GLASS = 2000, MAT_URANIUM = 2000, MAT_TITANIUM = 10000, MAT_SILVER = 4000, MAT_GOLD = 2000)
- build_path = /obj/item/gun/ballistic/automatic/magrifle_e/nopin
- category = list("Weapons")
- departmental_flags = DEPARTMENTAL_FLAG_SECURITY
-
-/datum/design/mag_magrifle_e
- name = "Magrifle Magazine (Lethal)"
- desc = "A 24-round magazine for the Magrifle."
- id = "mag_magrifle_e"
- build_type = PROTOLATHE
- materials = list(MAT_METAL = 8000, MAT_SILVER = 1000)
- build_path = /obj/item/ammo_box/magazine/mmag_e/lethal
- category = list("Ammo")
- departmental_flags = DEPARTMENTAL_FLAG_SECURITY
-
-/datum/design/mag_magrifle_e/nl
- name = "Magrifle Magazine (Non-Lethal)"
- desc = "A 24- round non-lethal magazine for the Magrifle."
- id = "mag_magrifle_e_nl"
- materials = list(MAT_METAL = 6000, MAT_SILVER = 500, MAT_TITANIUM = 500)
- build_path = /obj/item/ammo_box/magazine/mmag_e
- departmental_flags = DEPARTMENTAL_FLAG_SECURITY
-
-/datum/design/magpistol_e
- name = "Magpistol"
- desc = "A weapon which fires ferromagnetic slugs."
- id = "magpistol_e"
- build_type = PROTOLATHE
- materials = list(MAT_METAL = 7500, MAT_GLASS = 1000, MAT_URANIUM = 1000, MAT_TITANIUM = 5000, MAT_SILVER = 2000)
- build_path = /obj/item/gun/ballistic/automatic/pistol/mag_e/nopin
- category = list("Weapons")
- departmental_flags = DEPARTMENTAL_FLAG_SECURITY
-
-/datum/design/mag_magpistol_e
- name = "Magpistol Magazine"
- desc = "A 14 round magazine for the Magpistol."
- id = "mag_magpistol_e"
- build_type = PROTOLATHE
- materials = list(MAT_METAL = 4000, MAT_SILVER = 500)
- build_path = /obj/item/ammo_box/magazine/mmag_e/small/lethal
- category = list("Ammo")
- departmental_flags = DEPARTMENTAL_FLAG_SECURITY
-
-/datum/design/mag_magpistol_e/nl
- name = "Magpistol Magazine (Non-Lethal)"
- desc = "A 14 round non-lethal magazine for the Magpistol."
- id = "mag_magpistol_e_nl"
- materials = list(MAT_METAL = 3000, MAT_SILVER = 250, MAT_TITANIUM = 250)
- build_path = /obj/item/ammo_box/magazine/mmag_e/small
- departmental_flags = DEPARTMENTAL_FLAG_SECURITY
-
-/*
-// TECHWEBS IMPLEMENTATION
-*/
-
-/*
-/datum/techweb_node/magnetic_weapons
- id = "magnetic_weapons"
- display_name = "Magnetic Weapons"
- description = "Weapons using magnetic technology"
- prereq_ids = list("weaponry", "adv_weaponry", "emp_adv")
- design_ids = list("magrifle_e", "magpistol_e", "mag_magrifle_e", "mag_magrifle_e_nl", "mag_magpistol_e", "mag_magpistol_e_nl")
- research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
- export_price = 5000
-*/
-
-///magrifle///
-
-/obj/item/gun/ballistic/automatic/magrifle_e
- name = "\improper Magnetic Rifle"
- desc = "A simple upscalling of the technologies used in the magpistol, the magrifle is capable of firing slightly larger slugs in bursts. Compatible with the magpistol's slugs."
- icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi'
- icon_state = "magrifle"
- item_state = "arg"
- slot_flags = 0
- mag_type = /obj/item/ammo_box/magazine/mmag_e
- fire_sound = 'sound/weapons/magrifle.ogg'
- can_suppress = 0
- burst_size = 3
- fire_delay = 2
- spread = 5
- recoil = 0.15
- casing_ejector = 0
- var/obj/item/stock_parts/cell/cell
- var/cell_type = /obj/item/stock_parts/cell/magrifle_e
- var/dead_cell = FALSE
-
-/obj/item/gun/ballistic/automatic/magrifle_e/examine(mob/user)
- . = ..()
- if(cell)
- . += "[src]'s cell is [round(cell.charge / cell.maxcharge, 0.1) * 100]% full."
- else
- . += "[src] doesn't seem to have a cell!"
-
-/obj/item/gun/ballistic/automatic/magrifle_e/can_shoot()
- if(QDELETED(cell))
- return 0
-
- var/obj/item/ammo_casing/caseless/mag_e/shot = chambered
- if(!shot)
- return 0
- if(cell.charge < shot.energy_cost*burst_size)
- return 0
- . = ..()
-
-/obj/item/gun/ballistic/automatic/magrifle_e/shoot_live_shot()
- var/obj/item/ammo_casing/caseless/mag_e/shot = chambered
- cell.use(shot.energy_cost)
- . = ..()
-
-/obj/item/gun/ballistic/automatic/magrifle_e/emp_act(severity)
- . = ..()
- if(!(. & EMP_PROTECT_CONTENTS))
- cell.use(round(cell.charge / severity))
-
-/obj/item/gun/ballistic/automatic/magrifle_e/get_cell()
- return cell
-
-/obj/item/gun/ballistic/automatic/magrifle_e/Initialize()
- . = ..()
- if(cell_type)
- cell = new cell_type(src)
- else
- cell = new(src)
-
- if(!dead_cell)
- cell.give(cell.maxcharge)
-
-/obj/item/gun/ballistic/automatic/magrifle_e/nopin
- pin = null
- spawnwithmagazine = FALSE
-
-///magpistol///
-
-/obj/item/gun/ballistic/automatic/pistol/mag_e
- name = "magpistol"
- desc = "A handgun utilizing maglev technologies to propel a ferromagnetic slug to extreme velocities."
- icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi'
- icon_state = "magpistol"
- force = 10
- fire_sound = 'sound/weapons/magpistol.ogg'
- mag_type = /obj/item/ammo_box/magazine/mmag_e/small
- can_suppress = 0
- casing_ejector = 0
- fire_delay = 2
- recoil = 0.2
- var/obj/item/stock_parts/cell/cell
- var/cell_type = /obj/item/stock_parts/cell/magpistol_e
- var/dead_cell = FALSE
-
-/obj/item/gun/ballistic/automatic/pistol/mag_e/examine(mob/user)
- . = ..()
- if(cell)
- . += "[src]'s cell is [round(cell.charge / cell.maxcharge, 0.1) * 100]% full."
- else
- . += "[src] doesn't seem to have a cell!"
-
-/obj/item/gun/ballistic/automatic/pistol/mag_e/can_shoot()
- if(QDELETED(cell))
- return 0
-
- var/obj/item/ammo_casing/caseless/mag_e/shot = chambered
- if(!shot)
- return 0
- if(cell.charge < shot.energy_cost)
- return 0
- . = ..()
-
-/obj/item/gun/ballistic/automatic/pistol/mag_e/shoot_live_shot()
- var/obj/item/ammo_casing/caseless/mag_e/shot = chambered
- cell.use(shot.energy_cost)
- . = ..()
-
-/obj/item/gun/ballistic/automatic/pistol/mag_e/emp_act(severity)
- . = ..()
- if(!(. & EMP_PROTECT_CONTENTS))
- cell.use(round(cell.charge / severity))
-
-/obj/item/gun/ballistic/automatic/pistol/mag_e/get_cell()
- return cell
-
-/obj/item/gun/ballistic/automatic/pistol/mag_e/Initialize()
- . = ..()
- if(cell_type)
- cell = new cell_type(src)
- else
- cell = new(src)
-
- if(!dead_cell)
- cell.give(cell.maxcharge)
-
-/obj/item/gun/ballistic/automatic/pistol/mag_e/update_icon()
- ..()
- if(magazine)
- cut_overlays()
- add_overlay("magpistol-magazine")
- else
- cut_overlays()
- icon_state = "[initial(icon_state)][chambered ? "" : "-e"]"
-
-/obj/item/gun/ballistic/automatic/pistol/mag_e/nopin
- pin = null
- spawnwithmagazine = FALSE
diff --git a/modular_citadel/code/modules/projectiles/guns/ballistic/rifles.dm b/modular_citadel/code/modules/projectiles/guns/ballistic/rifles.dm
index 024669757a..37198e87cf 100644
--- a/modular_citadel/code/modules/projectiles/guns/ballistic/rifles.dm
+++ b/modular_citadel/code/modules/projectiles/guns/ballistic/rifles.dm
@@ -16,6 +16,29 @@
spread = 30 //should be 40 for XCOM memes, but since its adminspawn only, might as well make it useable
recoil = 1
+///toy memes///
+
+/obj/item/ammo_box/magazine/toy/x9
+ name = "foam force X9 magazine"
+ icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi'
+ icon_state = "toy9magazine"
+ max_ammo = 30
+ multiple_sprites = 2
+ materials = list(MAT_METAL = 200)
+
+/obj/item/gun/ballistic/automatic/x9/toy
+ name = "\improper Foam Force X9"
+ desc = "An old but reliable assault rifle made for combat against unknown enemies. Appears to be hastily converted. Ages 8 and up."
+ icon = 'modular_citadel/icons/obj/guns/cit_guns.dmi'
+ icon_state = "toy9"
+ can_suppress = 0
+ obj_flags = 0
+ mag_type = /obj/item/ammo_box/magazine/toy/x9
+ casing_ejector = 0
+ spread = 90 //MAXIMUM XCOM MEMES (actually that'd be 180 spread)
+ w_class = WEIGHT_CLASS_BULKY
+ weapon_weight = WEAPON_HEAVY
+
///////security rifles special ammo///////
/obj/item/ammo_casing/c46x30mm/rubber
diff --git a/modular_citadel/code/modules/projectiles/guns/energy/energy_gun.dm b/modular_citadel/code/modules/projectiles/guns/energy/energy_gun.dm
index 2c527ac713..49a8a26f35 100644
--- a/modular_citadel/code/modules/projectiles/guns/energy/energy_gun.dm
+++ b/modular_citadel/code/modules/projectiles/guns/energy/energy_gun.dm
@@ -1,7 +1,3 @@
-/obj/item/gun/energy/e_gun
- name = "blaster carbine"
- desc = "A high powered particle blaster carbine with varitable setting for stunning or lethal applications."
-
/*/////////////////////////////////////////////////////////////////////////////////////////////
The Recolourable Energy Gun
*//////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/modular_citadel/code/modules/projectiles/guns/energy/laser.dm b/modular_citadel/code/modules/projectiles/guns/energy/laser.dm
deleted file mode 100644
index 76d7403d16..0000000000
--- a/modular_citadel/code/modules/projectiles/guns/energy/laser.dm
+++ /dev/null
@@ -1,11 +0,0 @@
-/obj/item/gun/energy/laser
- name = "blaster rifle"
- desc = "a high energy particle blaster, efficient and deadly."
-
-/obj/item/gun/energy/laser/practice
- icon = 'modular_citadel/icons/obj/guns/energy.dmi'
- icon_state = "laser-p"
- lefthand_file = 'modular_citadel/icons/mob/inhands/OVERRIDE_guns_lefthand.dmi'
- righthand_file = 'modular_citadel/icons/mob/inhands/OVERRIDE_guns_righthand.dmi'
- ammo_x_offset = 1
- shaded_charge = 1
diff --git a/modular_citadel/code/modules/projectiles/guns/pumpenergy.dm b/modular_citadel/code/modules/projectiles/guns/pumpenergy.dm
index 47de736c3b..f7c0b343c3 100644
--- a/modular_citadel/code/modules/projectiles/guns/pumpenergy.dm
+++ b/modular_citadel/code/modules/projectiles/guns/pumpenergy.dm
@@ -46,34 +46,22 @@
cell.use(shot.e_cost)//... drain the cell cell
chambered = 0 //either way, released the prepared shot
-/obj/item/gun/energy/pumpaction/select_fire(mob/living/user) //makes it so that it doesn't rack itself when changing firing modes unless already racked
- select++
- if (select > ammo_type.len)
- select = 1
- var/obj/item/ammo_casing/energy/shot = ammo_type[select]
- fire_sound = shot.fire_sound
- fire_delay = shot.delay
- if (shot.select_name)
- to_chat(user, "[src] is now set to [shot.select_name].")
- if(chambered)
- chambered = 0
- recharge_newshot(1)
- update_icon()
- if(ismob(loc)) //forces inhands to update
- var/mob/M = loc
- M.update_inv_hands()
- return
+/obj/item/gun/energy/pumpaction/post_set_firemode()
+ var/has_shot = chambered
+ . = ..(recharge_newshot = FALSE)
+ if(has_shot)
+ recharge_newshot(TRUE)
/obj/item/gun/energy/pumpaction/update_icon() //adds racked indicators
..()
- var/obj/item/ammo_casing/energy/shot = ammo_type[select]
+ var/obj/item/ammo_casing/energy/shot = ammo_type[current_firemode_index]
if(chambered)
add_overlay("[icon_state]_rack_[shot.select_name]")
else
add_overlay("[icon_state]_rack_empty")
/obj/item/gun/energy/pumpaction/proc/pump(mob/M) //pumping proc. Checks if the gun is empty and plays a different sound if it is.
- var/obj/item/ammo_casing/energy/shot = ammo_type[select]
+ var/obj/item/ammo_casing/energy/shot = ammo_type[current_firemode_index]
if(cell.charge < shot.e_cost)
playsound(M, 'sound/weapons/laserPumpEmpty.ogg', 100, 1) //Ends with three beeps made from highly processed knife honing noises
else
@@ -102,7 +90,7 @@
/obj/item/gun/energy/pumpaction/worn_overlays(isinhands, icon_file, style_flags = NONE) //ammo counter for inhands
. = ..()
var/ratio = CEILING((cell.charge / cell.maxcharge) * charge_sections, 1)
- var/obj/item/ammo_casing/energy/shot = ammo_type[select]
+ var/obj/item/ammo_casing/energy/shot = ammo_type[current_firemode_index]
if(isinhands)
if(cell.charge < shot.e_cost)
var/mutable_appearance/ammo_inhand = mutable_appearance(icon_file, "[item_state]_empty")
diff --git a/modular_citadel/code/modules/reagents/chemistry/reagents/MKUltra.dm b/modular_citadel/code/modules/reagents/chemistry/reagents/MKUltra.dm
index 86325faa91..2364c617f0 100644
--- a/modular_citadel/code/modules/reagents/chemistry/reagents/MKUltra.dm
+++ b/modular_citadel/code/modules/reagents/chemistry/reagents/MKUltra.dm
@@ -191,7 +191,7 @@ Creating a chem with a low purity will make you permanently fall in love with so
var/obj/item/organ/vocal_cords/Vc = M.getorganslot(ORGAN_SLOT_VOICE)
var/obj/item/organ/vocal_cords/nVc = new /obj/item/organ/vocal_cords/velvet
if(Vc)
- Vc.Remove(M)
+ Vc.Remove()
nVc.Insert(M)
qdel(Vc)
to_chat(M, "You feel your vocal chords tingle you speak in a more charasmatic and sultry tone.")
@@ -333,6 +333,8 @@ Creating a chem with a low purity will make you permanently fall in love with so
..()
/datum/reagent/fermi/proc/FallInLove(mob/living/carbon/Lover, mob/living/carbon/Love)
+ if(Lover.client?.prefs.cit_toggles & NEVER_HYPNO)
+ return // doesn't even give a message, it's just ignored
if(Lover.has_status_effect(STATUS_EFFECT_INLOVE))
to_chat(Lover, "You are already fully devoted to someone else!")
return
diff --git a/modular_citadel/code/modules/reagents/chemistry/reagents/SDGF.dm b/modular_citadel/code/modules/reagents/chemistry/reagents/SDGF.dm
index 87615ccb68..5f9ea5924d 100644
--- a/modular_citadel/code/modules/reagents/chemistry/reagents/SDGF.dm
+++ b/modular_citadel/code/modules/reagents/chemistry/reagents/SDGF.dm
@@ -94,7 +94,7 @@ IMPORTANT FACTORS TO CONSIDER WHILE BALANCING
//I seriously wonder if anyone will ever use this function.
if(M.getorganslot(ORGAN_SLOT_ZOMBIE))//sure, it "treats" it, but "you've" still got it. Doesn't always work as well; needs a ghost.
var/obj/item/organ/zombie_infection/ZI = M.getorganslot(ORGAN_SLOT_ZOMBIE)
- ZI.Remove(M)
+ ZI.Remove()
ZI.Insert(SM)
log_game("FERMICHEM: [M] ckey: [M.key]'s zombie_infection has been transferred to their clone")
diff --git a/modular_citadel/code/modules/reagents/chemistry/reagents/fermi_reagents.dm b/modular_citadel/code/modules/reagents/chemistry/reagents/fermi_reagents.dm
index e9c5733584..fb5574855e 100644
--- a/modular_citadel/code/modules/reagents/chemistry/reagents/fermi_reagents.dm
+++ b/modular_citadel/code/modules/reagents/chemistry/reagents/fermi_reagents.dm
@@ -129,7 +129,7 @@
if(16)
T = M.getorganslot(ORGAN_SLOT_TONGUE)
var/obj/item/organ/tongue/nT = new /obj/item/organ/tongue/fluffy
- T.Remove(M)
+ T.Remove()
nT.Insert(M)
T.moveToNullspace()//To valhalla
to_chat(M, "Your tongue feels... weally fwuffy!!")
@@ -152,7 +152,7 @@
/datum/reagent/fermi/furranium/on_mob_delete(mob/living/carbon/M)
if(cached_purity < 0.95)//Only permanent if you're a good chemist.
nT = M.getorganslot(ORGAN_SLOT_TONGUE)
- nT.Remove(M)
+ nT.Remove()
qdel(nT)
T.Insert(M)
to_chat(M, "You feel your tongue.... unfluffify...?")
diff --git a/modular_citadel/code/modules/reagents/chemistry/reagents/healing.dm b/modular_citadel/code/modules/reagents/chemistry/reagents/healing.dm
index 8668ef76f0..d98ec7059d 100644
--- a/modular_citadel/code/modules/reagents/chemistry/reagents/healing.dm
+++ b/modular_citadel/code/modules/reagents/chemistry/reagents/healing.dm
@@ -29,7 +29,7 @@
nT = new C.dna.species.mutanttongue()
else
nT = new()
- T.Remove(C)
+ T.Remove()
qdel(T)
nT.Insert(C)
to_chat(C, "You feel your tongue.... unfluffify...?")
@@ -57,7 +57,7 @@
T = new C.dna.species.mutanttongue()
else
T = new()
- oT.Remove(C)
+ oT.Remove()
qdel(oT)
T.Insert(C)
to_chat(C, "You feel your tongue.... unfluffify...?")
diff --git a/modular_citadel/code/modules/reagents/chemistry/recipes/fermi.dm b/modular_citadel/code/modules/reagents/chemistry/recipes/fermi.dm
index 31e319edd3..a03d5eca81 100644
--- a/modular_citadel/code/modules/reagents/chemistry/recipes/fermi.dm
+++ b/modular_citadel/code/modules/reagents/chemistry/recipes/fermi.dm
@@ -10,7 +10,7 @@
if(clear_conversion == REACTION_CLEAR_IMPURE | REACTION_CLEAR_INVERSE)
for(var/id in results)
var/datum/reagent/R = my_atom.reagents.has_reagent(id)
- if(R.purity == 1)
+ if(!R || R.purity == 1)
continue
var/cached_volume = R.volume
@@ -25,7 +25,6 @@
my_atom.reagents.add_reagent(R.impure_chem, impureVol, FALSE, other_purity = 1)
R.cached_purity = R.purity
R.purity = 1
- return
//Called when temperature is above a certain threshold, or if purity is too low.
/datum/chemical_reaction/proc/FermiExplode(datum/reagents/R0, var/atom/my_atom, volume, temp, pH, Exploding = FALSE)
@@ -200,8 +199,10 @@
FermiExplode = TRUE
PurityMin = 0.1
-/datum/chemical_reaction/fermi/breast_enlarger/FermiFinish(datum/reagents/holder, var/atom/my_atom)
+/datum/chemical_reaction/fermi/breast_enlarger/FermiFinish(datum/reagents/holder, atom/my_atom)
var/datum/reagent/fermi/breast_enlarger/BE = locate(/datum/reagent/fermi/breast_enlarger) in my_atom.reagents.reagent_list
+ if(!BE)
+ return
var/cached_volume = BE.volume
if(BE.purity < 0.35)
holder.remove_reagent(type, cached_volume)
@@ -249,8 +250,10 @@
P.length = ((PE.volume * PE.purity) / 10)//half as effective.
my_atom.reagents.clear_reagents()
-/datum/chemical_reaction/fermi/penis_enlarger/FermiFinish(datum/reagents/holder, var/atom/my_atom)
+/datum/chemical_reaction/fermi/penis_enlarger/FermiFinish(datum/reagents/holder, atom/my_atom)
var/datum/reagent/fermi/penis_enlarger/PE = locate(/datum/reagent/fermi/penis_enlarger) in my_atom.reagents.reagent_list
+ if(!PE)
+ return
var/cached_volume = PE.volume
if(PE.purity < 0.35)
holder.remove_reagent(type, cached_volume)
@@ -306,13 +309,11 @@
/datum/chemical_reaction/fermi/enthrall/FermiFinish(datum/reagents/holder, var/atom/my_atom)
var/datum/reagent/blood/B = locate(/datum/reagent/blood) in my_atom.reagents.reagent_list
var/datum/reagent/fermi/enthrall/E = locate(/datum/reagent/fermi/enthrall) in my_atom.reagents.reagent_list
- if(!B)
+ if(!B || !E)
return
if(!B.data)
- var/list/seen = viewers(5, get_turf(my_atom))
- for(var/mob/M in seen)
- to_chat(M, "The reaction splutters and fails to react properly.") //Just in case
- E.purity = 0
+ my_atom.visible_message("The reaction splutters and fails to react properly.") //Just in case
+ E.purity = 0
if (B.data["gender"] == "female")
E.data["creatorGender"] = "Mistress"
E.creatorGender = "Mistress"
@@ -332,11 +333,11 @@
/datum/chemical_reaction/fermi/enthrall/slime/FermiFinish(datum/reagents/holder, var/atom/my_atom)
var/datum/reagent/blood/jellyblood/B = locate(/datum/reagent/blood/jellyblood) in my_atom.reagents.reagent_list//The one line change.
var/datum/reagent/fermi/enthrall/E = locate(/datum/reagent/fermi/enthrall) in my_atom.reagents.reagent_list
+ if(!B || !E)
+ return
if(!B.data)
- var/list/seen = viewers(5, get_turf(my_atom))
- for(var/mob/M in seen)
- to_chat(M, "The reaction splutters and fails to react.") //Just in case
- E.purity = 0
+ my_atom.visible_message("The reaction splutters and fails to react properly.") //Just in case
+ E.purity = 0
if (B.data["gender"] == "female")
E.data["creatorGender"] = "Mistress"
E.creatorGender = "Mistress"
@@ -466,9 +467,9 @@
/datum/chemical_reaction/fermi/acidic_buffer/FermiFinish(datum/reagents/holder, var/atom/my_atom) //might need this
- if(!locate(/datum/reagent/fermi/acidic_buffer) in my_atom.reagents.reagent_list)
- return
var/datum/reagent/fermi/acidic_buffer/Fa = locate(/datum/reagent/fermi/acidic_buffer) in my_atom.reagents.reagent_list
+ if(!Fa)
+ return
Fa.data = 0.1//setting it to 0 means byond thinks it's not there.
/datum/chemical_reaction/fermi/basic_buffer//done test
@@ -493,10 +494,10 @@
FermiChem = TRUE
-/datum/chemical_reaction/fermi/basic_buffer/FermiFinish(datum/reagents/holder, var/atom/my_atom) //might need this
- if(!locate(/datum/reagent/fermi/basic_buffer) in my_atom.reagents.reagent_list)
- return
+/datum/chemical_reaction/fermi/basic_buffer/FermiFinish(datum/reagents/holder, atom/my_atom) //might need this
var/datum/reagent/fermi/basic_buffer/Fb = locate(/datum/reagent/fermi/basic_buffer) in my_atom.reagents.reagent_list
+ if(!Fb)
+ return
Fb.data = 14
//secretcatchemcode, shh!! Of couse I hide it amongst cats. Though, I moved it with your requests.
diff --git a/modular_citadel/code/modules/reagents/objects/items.dm b/modular_citadel/code/modules/reagents/objects/items.dm
index 6221bdde18..5d207f4c73 100644
--- a/modular_citadel/code/modules/reagents/objects/items.dm
+++ b/modular_citadel/code/modules/reagents/objects/items.dm
@@ -71,7 +71,7 @@
if(!istype(cont))
return
if(used == TRUE)
- to_chat(user, "[user] has already been used!")
+ to_chat(user, "[src] has already been used!")
return
if(!LAZYLEN(cont.reagents.reagent_list))
return
@@ -107,6 +107,7 @@
if(-INFINITY to 1)
color = "#c6040c"
desc += " The paper looks to be around a pH of [round(cont.reagents.pH, 1)]"
+ name = "used [name]"
used = TRUE
/obj/item/fermichem/pHmeter
diff --git a/modular_citadel/code/modules/reagents/reagents/cit_reagents.dm b/modular_citadel/code/modules/reagents/reagents/cit_reagents.dm
index 32474263c4..41eccb7054 100644
--- a/modular_citadel/code/modules/reagents/reagents/cit_reagents.dm
+++ b/modular_citadel/code/modules/reagents/reagents/cit_reagents.dm
@@ -1,7 +1,7 @@
//body bluids
/datum/reagent/consumable/semen
name = "Semen"
- description = "Sperm from some animal. I bet you'll drink this out of a bucket someday."
+ description = "Sperm from some animal. Useless for anything but insemination, really."
taste_description = "something salty"
taste_mult = 2 //Not very overpowering flavor
data = list("donor"=null,"viruses"=null,"donor_DNA"=null,"blood_type"=null,"resistances"=null,"trace_chem"=null,"mind"=null,"ckey"=null,"gender"=null,"real_name"=null)
@@ -32,15 +32,15 @@
icon_state = "semen1"
random_icon_states = list("semen1", "semen2", "semen3", "semen4")
-/obj/effect/decal/cleanable/semen/New()
- ..()
- dir = pick(1,2,4,8)
+/obj/effect/decal/cleanable/semen/Initialize(mapload)
+ . = ..()
+ dir = GLOB.cardinals
add_blood_DNA(list("Non-human DNA" = "A+"))
/obj/effect/decal/cleanable/semen/replace_decal(obj/effect/decal/cleanable/semen/S)
if(S.blood_DNA)
- blood_DNA |= S.blood_DNA.Copy()
- ..()
+ blood_DNA |= S.blood_DNA
+ return ..()
/datum/reagent/consumable/femcum
name = "Female Ejaculate"
@@ -65,15 +65,15 @@
blood_state = null
bloodiness = null
-/obj/effect/decal/cleanable/femcum/New()
- ..()
- dir = pick(1,2,4,8)
+/obj/effect/decal/cleanable/femcum/Initialize(mapload)
+ . = ..()
+ dir = GLOB.cardinals
add_blood_DNA(list("Non-human DNA" = "A+"))
/obj/effect/decal/cleanable/femcum/replace_decal(obj/effect/decal/cleanable/femcum/F)
if(F.blood_DNA)
- blood_DNA |= F.blood_DNA.Copy()
- ..()
+ blood_DNA |= F.blood_DNA
+ return ..()
/datum/reagent/consumable/femcum/reaction_turf(turf/T, reac_volume)
if(!istype(T))
diff --git a/modular_citadel/icons/mob/citadel/uniforms.dmi b/modular_citadel/icons/mob/citadel/uniforms.dmi
index 855872e9ab..717999c9f0 100644
Binary files a/modular_citadel/icons/mob/citadel/uniforms.dmi and b/modular_citadel/icons/mob/citadel/uniforms.dmi differ
diff --git a/modular_citadel/icons/mob/inhands/OVERRIDE_guns_lefthand.dmi b/modular_citadel/icons/mob/inhands/OVERRIDE_guns_lefthand.dmi
deleted file mode 100644
index b438c7acee..0000000000
Binary files a/modular_citadel/icons/mob/inhands/OVERRIDE_guns_lefthand.dmi and /dev/null differ
diff --git a/modular_citadel/icons/mob/inhands/OVERRIDE_guns_righthand.dmi b/modular_citadel/icons/mob/inhands/OVERRIDE_guns_righthand.dmi
deleted file mode 100644
index dda226b046..0000000000
Binary files a/modular_citadel/icons/mob/inhands/OVERRIDE_guns_righthand.dmi and /dev/null differ
diff --git a/modular_citadel/icons/mob/mam_ears.dmi b/modular_citadel/icons/mob/mam_ears.dmi
index fcc124c695..a81e6c03b2 100644
Binary files a/modular_citadel/icons/mob/mam_ears.dmi and b/modular_citadel/icons/mob/mam_ears.dmi differ
diff --git a/modular_citadel/icons/mob/markings_notmammals.dmi b/modular_citadel/icons/mob/markings_notmammals.dmi
index 79c97968ed..43452c8d14 100644
Binary files a/modular_citadel/icons/mob/markings_notmammals.dmi and b/modular_citadel/icons/mob/markings_notmammals.dmi differ
diff --git a/modular_citadel/icons/mob/widerobot.dmi b/modular_citadel/icons/mob/widerobot.dmi
index a77a109eb9..50c29bb75f 100644
Binary files a/modular_citadel/icons/mob/widerobot.dmi and b/modular_citadel/icons/mob/widerobot.dmi differ
diff --git a/modular_citadel/icons/obj/clothing/vg_clothes.dmi b/modular_citadel/icons/obj/clothing/vg_clothes.dmi
deleted file mode 100644
index a1d25aebf5..0000000000
Binary files a/modular_citadel/icons/obj/clothing/vg_clothes.dmi and /dev/null differ
diff --git a/modular_citadel/icons/obj/genitals/breasts.dmi b/modular_citadel/icons/obj/genitals/breasts.dmi
index d70207ee69..24656062d2 100644
Binary files a/modular_citadel/icons/obj/genitals/breasts.dmi and b/modular_citadel/icons/obj/genitals/breasts.dmi differ
diff --git a/modular_citadel/icons/obj/genitals/breasts_onmob.dmi b/modular_citadel/icons/obj/genitals/breasts_onmob.dmi
index 371041b6f4..578086c4c3 100644
Binary files a/modular_citadel/icons/obj/genitals/breasts_onmob.dmi and b/modular_citadel/icons/obj/genitals/breasts_onmob.dmi differ
diff --git a/modular_citadel/icons/obj/genitals/penis.dmi b/modular_citadel/icons/obj/genitals/penis.dmi
index 517758b248..c3fd184a7c 100644
Binary files a/modular_citadel/icons/obj/genitals/penis.dmi and b/modular_citadel/icons/obj/genitals/penis.dmi differ
diff --git a/modular_citadel/icons/obj/genitals/penis_onmob.dmi b/modular_citadel/icons/obj/genitals/penis_onmob.dmi
index 4f84c52360..c074eaf306 100644
Binary files a/modular_citadel/icons/obj/genitals/penis_onmob.dmi and b/modular_citadel/icons/obj/genitals/penis_onmob.dmi differ
diff --git a/modular_citadel/icons/obj/genitals/taur_penis_onmob.dmi b/modular_citadel/icons/obj/genitals/taur_penis_onmob.dmi
index ae2339e2e0..e8efbec216 100644
Binary files a/modular_citadel/icons/obj/genitals/taur_penis_onmob.dmi and b/modular_citadel/icons/obj/genitals/taur_penis_onmob.dmi differ
diff --git a/modular_citadel/icons/obj/genitals/testicles.dmi b/modular_citadel/icons/obj/genitals/testicles.dmi
index 3d7a5f4f48..386eab0956 100644
Binary files a/modular_citadel/icons/obj/genitals/testicles.dmi and b/modular_citadel/icons/obj/genitals/testicles.dmi differ
diff --git a/modular_citadel/icons/obj/genitals/vagina.dmi b/modular_citadel/icons/obj/genitals/vagina.dmi
index 39bdd48e89..1bbe62ba0e 100644
Binary files a/modular_citadel/icons/obj/genitals/vagina.dmi and b/modular_citadel/icons/obj/genitals/vagina.dmi differ
diff --git a/modular_citadel/icons/obj/genitals/vagina_onmob.dmi b/modular_citadel/icons/obj/genitals/vagina_onmob.dmi
index ab71b22e3b..6c34ffadfa 100644
Binary files a/modular_citadel/icons/obj/genitals/vagina_onmob.dmi and b/modular_citadel/icons/obj/genitals/vagina_onmob.dmi differ
diff --git a/modular_citadel/icons/obj/guns/cit_guns.dmi b/modular_citadel/icons/obj/guns/cit_guns.dmi
index bd48d8edbd..79b54d27fb 100644
Binary files a/modular_citadel/icons/obj/guns/cit_guns.dmi and b/modular_citadel/icons/obj/guns/cit_guns.dmi differ
diff --git a/modular_citadel/icons/obj/guns/energy.dmi b/modular_citadel/icons/obj/guns/energy.dmi
deleted file mode 100644
index 482ec04d94..0000000000
Binary files a/modular_citadel/icons/obj/guns/energy.dmi and /dev/null differ
diff --git a/modular_citadel/icons/vending_restock.dmi b/modular_citadel/icons/vending_restock.dmi
deleted file mode 100644
index 5df68f4e4c..0000000000
Binary files a/modular_citadel/icons/vending_restock.dmi and /dev/null differ
diff --git a/node_modules/.yarn-integrity b/node_modules/.yarn-integrity
new file mode 100644
index 0000000000..1a3aded610
--- /dev/null
+++ b/node_modules/.yarn-integrity
@@ -0,0 +1,10 @@
+{
+ "systemParams": "win32-x64-72",
+ "modulesFolders": [],
+ "flags": [],
+ "linkedModules": [],
+ "topLevelPatterns": [],
+ "lockfileEntries": {},
+ "files": [],
+ "artifacts": {}
+}
\ No newline at end of file
diff --git a/sound/effects/fillingwatter.ogg b/sound/effects/fillingwatter.ogg
new file mode 100644
index 0000000000..e1d2e19276
Binary files /dev/null and b/sound/effects/fillingwatter.ogg differ
diff --git a/sound/effects/pooldrain.ogg b/sound/effects/pooldrain.ogg
new file mode 100644
index 0000000000..8576dedc3d
Binary files /dev/null and b/sound/effects/pooldrain.ogg differ
diff --git a/sound/effects/splash.ogg b/sound/effects/splash.ogg
new file mode 100644
index 0000000000..22f17c0790
Binary files /dev/null and b/sound/effects/splash.ogg differ
diff --git a/sound/effects/water_wade1.ogg b/sound/effects/water_wade1.ogg
new file mode 100644
index 0000000000..27527f9d8b
Binary files /dev/null and b/sound/effects/water_wade1.ogg differ
diff --git a/sound/effects/water_wade2.ogg b/sound/effects/water_wade2.ogg
new file mode 100644
index 0000000000..f9c15cab64
Binary files /dev/null and b/sound/effects/water_wade2.ogg differ
diff --git a/sound/effects/water_wade3.ogg b/sound/effects/water_wade3.ogg
new file mode 100644
index 0000000000..3daccae01a
Binary files /dev/null and b/sound/effects/water_wade3.ogg differ
diff --git a/sound/effects/water_wade4.ogg b/sound/effects/water_wade4.ogg
new file mode 100644
index 0000000000..7ba705f499
Binary files /dev/null and b/sound/effects/water_wade4.ogg differ
diff --git a/sound/effects/watersplash.ogg b/sound/effects/watersplash.ogg
new file mode 100644
index 0000000000..a9a6e48608
Binary files /dev/null and b/sound/effects/watersplash.ogg differ
diff --git a/sound/machines/nuke/angry_beep.ogg b/sound/machines/nuke/angry_beep.ogg
new file mode 100644
index 0000000000..547779de1b
Binary files /dev/null and b/sound/machines/nuke/angry_beep.ogg differ
diff --git a/sound/machines/nuke/confirm_beep.ogg b/sound/machines/nuke/confirm_beep.ogg
new file mode 100644
index 0000000000..6b98ba8bd6
Binary files /dev/null and b/sound/machines/nuke/confirm_beep.ogg differ
diff --git a/sound/machines/nuke/general_beep.ogg b/sound/machines/nuke/general_beep.ogg
new file mode 100644
index 0000000000..c149eb300a
Binary files /dev/null and b/sound/machines/nuke/general_beep.ogg differ
diff --git a/sound/misc/crack.ogg b/sound/misc/crack.ogg
new file mode 100644
index 0000000000..1fb5738051
Binary files /dev/null and b/sound/misc/crack.ogg differ
diff --git a/sound/misc/crunch.ogg b/sound/misc/crunch.ogg
new file mode 100644
index 0000000000..c73e1fae60
Binary files /dev/null and b/sound/misc/crunch.ogg differ
diff --git a/sound/voice/moth/mothchitter.ogg b/sound/voice/moth/mothchitter.ogg
new file mode 100644
index 0000000000..842bcf8e53
Binary files /dev/null and b/sound/voice/moth/mothchitter.ogg differ
diff --git a/sound/voice/moth/mothlaugh.ogg b/sound/voice/moth/mothlaugh.ogg
new file mode 100644
index 0000000000..391d6c5aef
Binary files /dev/null and b/sound/voice/moth/mothlaugh.ogg differ
diff --git a/sound/weapons/bowdraw.wav b/sound/weapons/bowdraw.wav
new file mode 100644
index 0000000000..bba8a87e06
Binary files /dev/null and b/sound/weapons/bowdraw.wav differ
diff --git a/sound/weapons/bowfire.wav b/sound/weapons/bowfire.wav
new file mode 100644
index 0000000000..c079d43475
Binary files /dev/null and b/sound/weapons/bowfire.wav differ
diff --git a/strings/flavor_objectives/ninja_helping.txt b/strings/flavor_objectives/ninja_helping.txt
new file mode 100644
index 0000000000..1280939b5c
--- /dev/null
+++ b/strings/flavor_objectives/ninja_helping.txt
@@ -0,0 +1,6 @@
+Nanotrasen want to make sure that their employees are on the up-and-up. Try to find any blackmail you can.
+Increase productivity however you can.
+You are a security ninja. Answer to the Head of Security, and follow space law.
+You are a cargo ninja. Answer to the Quartermaster, and do what they say.
+Nanotrasen want you to ensure maximum morale. Protect the members of the crew who are on break.
+Ensure that all the paperwork is being done.
\ No newline at end of file
diff --git a/strings/flavor_objectives/ninja_syndie.txt b/strings/flavor_objectives/ninja_syndie.txt
new file mode 100644
index 0000000000..4527c693f4
--- /dev/null
+++ b/strings/flavor_objectives/ninja_syndie.txt
@@ -0,0 +1,5 @@
+The Gorlex Marauders want you to teach the heads of staff a lesson they will never forget.
+Waffle Co. wants you To cause as much humorous terrorism against Nanotrasen as possible! How? We don’t care as long as it’s entertaining! Be as creative and exciting as possible when carrying out your dirty deeds. Have fun!
+The Tiger Cooperative want you to kill one of the station's beloved pets. Make a show of it, though you don't have to reveal yourself.
+The Animal Rights Consortium needs you to save the innocent non-humanoid creatures aboard Citadel Station by any means necessary. Use your best judgement to decide whether an animal or xenobiological is abused, but if they are, ensure the abuser is punished. Avoid killing too many people if possible, and if you do harm any creatures, you will be terminated upon extraction.
+Donk Co. wants ransom money, and you are going to get it. Your goal is to kidnap and crewmember you can get your hands on and hold them hostage until you get something of significant value. Try to work out the best deal you can. Remember that Higher Value Targets are generally going to get a better deal so try to prioritize heads of staff if possible. We do not approve of mindless killing of Nanotrasen employees, so don’t do it.
\ No newline at end of file
diff --git a/strings/flavor_objectives/traitor.txt b/strings/flavor_objectives/traitor.txt
new file mode 100644
index 0000000000..6d54c8ed9d
--- /dev/null
+++ b/strings/flavor_objectives/traitor.txt
@@ -0,0 +1,8 @@
+The Gorlex Marauders want you to teach the heads of staff a lesson they will never forget.
+Show Nanotrasen the utility of a 40% oxygen atmosphere.
+Waffle Co. wants you To cause as much humorous terrorism against Nanotrasen as possible! How? We don’t care as long as it’s entertaining! Be as creative and exciting as possible when carrying out your dirty deeds. Have fun!
+Kill one of the station's beloved pets. Make a show of it, though you don't have to reveal yourself.
+The Tiger Cooperative want you to get their illegal technology spread through the station.
+The Animal Rights Consortium needs you to save the innocent non-humanoid creatures aboard Citadel Station by any means necessary. Use your best judgement to decide whether an animal or xenobiological is abused, but if they are, ensure the abuser is punished. Avoid killing too many people if possible, and if you do harm any creatures, you will be terminated upon extraction.
+Donk Co. wants ransom money, and you are going to get it. Your goal is to kidnap and crewmember you can get your hands on and hold them hostage until you get something of significant value. Try to work out the best deal you can. Remember that Higher Value Targets are generally going to get a better deal so try to prioritize heads of staff if possible. We do not approve of mindless killing of Nanotrasen employees, so don’t do it.
+The Gorlex Marauders want you to steal as many shoes as possible. Lay broken glass everywhere.
\ No newline at end of file
diff --git a/strings/flavor_objectives/traitor/assistant.txt b/strings/flavor_objectives/traitor/assistant.txt
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/strings/flavor_objectives/traitor/engineering.txt b/strings/flavor_objectives/traitor/engineering.txt
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/strings/flavor_objectives/traitor/medical.txt b/strings/flavor_objectives/traitor/medical.txt
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/strings/flavor_objectives/traitor/science.txt b/strings/flavor_objectives/traitor/science.txt
new file mode 100644
index 0000000000..6367e78da2
--- /dev/null
+++ b/strings/flavor_objectives/traitor/science.txt
@@ -0,0 +1 @@
+The Tiger Cooperative want you to slow down the process of research as much as possible.
\ No newline at end of file
diff --git a/strings/flavor_objectives/wizard.txt b/strings/flavor_objectives/wizard.txt
new file mode 100644
index 0000000000..e65a6052bc
--- /dev/null
+++ b/strings/flavor_objectives/wizard.txt
@@ -0,0 +1,4 @@
+Cause as much creative mayhem as you can aboard the station! The more outlandish your methods of achieving this, the better! Make sure there's a decent amount of crew alive to tell of your tale.
+The Wizard Federation have sent you to take over the station by force. Kill the captain and take their place. The magocracy starts now.
+You are the slipperiest wizard there ever was. Be loud, cause a ruckus, start a manhunt, and keep it going for as long as you can.
+The crew must be shown the wonders of magic. Do some tricks for them. If they stop moving, that means they're in awe.
\ No newline at end of file
diff --git a/strings/spurdo_replacement.json b/strings/spurdo_replacement.json
new file mode 100644
index 0000000000..71c7c8d356
--- /dev/null
+++ b/strings/spurdo_replacement.json
@@ -0,0 +1,14 @@
+{
+
+ "spurdo": {
+ "epic": "ebin",
+ "c": "g",
+ "ck": "gg",
+ "k": "g",
+ "t": "d",
+ "p": "b",
+ "x": "gs"
+ }
+
+
+}
\ No newline at end of file
diff --git a/tgstation.dme b/tgstation.dme
old mode 100755
new mode 100644
index 41b45e69cd..8f676c5bd1
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -1,3 +1,4 @@
+
// DM Environment file for tgstation.dme.
// All manual changes should be made outside the BEGIN_ and END_ blocks.
// New source code should be placed in .dm files: choose File/New --> Code File.
@@ -77,6 +78,7 @@
#include "code\__DEFINES\obj_flags.dm"
#include "code\__DEFINES\pinpointers.dm"
#include "code\__DEFINES\pipe_construction.dm"
+#include "code\__DEFINES\pool.dm"
#include "code\__DEFINES\preferences.dm"
#include "code\__DEFINES\procpath.dm"
#include "code\__DEFINES\profile.dm"
@@ -152,6 +154,7 @@
#include "code\__HELPERS\type2type_vr.dm"
#include "code\__HELPERS\typelists.dm"
#include "code\__HELPERS\unsorted.dm"
+#include "code\__HELPERS\vector.dm"
#include "code\__HELPERS\view.dm"
#include "code\__HELPERS\sorts\__main.dm"
#include "code\__HELPERS\sorts\InsertSort.dm"
@@ -233,6 +236,7 @@
#include "code\controllers\configuration\entries\fail2topic.dm"
#include "code\controllers\configuration\entries\game_options.dm"
#include "code\controllers\configuration\entries\general.dm"
+#include "code\controllers\configuration\entries\plushies.dm"
#include "code\controllers\subsystem\acid.dm"
#include "code\controllers\subsystem\adjacent_air.dm"
#include "code\controllers\subsystem\air.dm"
@@ -272,6 +276,7 @@
#include "code\controllers\subsystem\pathfinder.dm"
#include "code\controllers\subsystem\persistence.dm"
#include "code\controllers\subsystem\ping.dm"
+#include "code\controllers\subsystem\profiler.dm"
#include "code\controllers\subsystem\radiation.dm"
#include "code\controllers\subsystem\radio.dm"
#include "code\controllers\subsystem\research.dm"
@@ -290,7 +295,6 @@
#include "code\controllers\subsystem\vis_overlays.dm"
#include "code\controllers\subsystem\vore.dm"
#include "code\controllers\subsystem\vote.dm"
-#include "code\controllers\subsystem\weather.dm"
#include "code\controllers\subsystem\processing\chemistry.dm"
#include "code\controllers\subsystem\processing\circuit.dm"
#include "code\controllers\subsystem\processing\fastprocess.dm"
@@ -301,6 +305,7 @@
#include "code\controllers\subsystem\processing\processing.dm"
#include "code\controllers\subsystem\processing\projectiles.dm"
#include "code\controllers\subsystem\processing\quirks.dm"
+#include "code\controllers\subsystem\processing\weather.dm"
#include "code\controllers\subsystem\processing\wet_floors.dm"
#include "code\datums\action.dm"
#include "code\datums\ai_laws.dm"
@@ -405,6 +410,15 @@
#include "code\datums\components\virtual_reality.dm"
#include "code\datums\components\wearertargeting.dm"
#include "code\datums\components\wet_floor.dm"
+#include "code\datums\components\crafting\craft.dm"
+#include "code\datums\components\crafting\glassware.dm"
+#include "code\datums\components\crafting\guncrafting.dm"
+#include "code\datums\components\crafting\recipes.dm"
+#include "code\datums\components\crafting\recipes\recipes_clothing.dm"
+#include "code\datums\components\crafting\recipes\recipes_misc.dm"
+#include "code\datums\components\crafting\recipes\recipes_primal.dm"
+#include "code\datums\components\crafting\recipes\recipes_robot.dm"
+#include "code\datums\components\crafting\recipes\recipes_weapon_and_ammo.dm"
#include "code\datums\components\fantasy\_fantasy.dm"
#include "code\datums\components\fantasy\affix.dm"
#include "code\datums\components\fantasy\prefixes.dm"
@@ -448,10 +462,12 @@
#include "code\datums\diseases\advance\symptoms\confusion.dm"
#include "code\datums\diseases\advance\symptoms\cough.dm"
#include "code\datums\diseases\advance\symptoms\deafness.dm"
+#include "code\datums\diseases\advance\symptoms\disfiguration.dm"
#include "code\datums\diseases\advance\symptoms\dizzy.dm"
#include "code\datums\diseases\advance\symptoms\fever.dm"
#include "code\datums\diseases\advance\symptoms\fire.dm"
#include "code\datums\diseases\advance\symptoms\flesh_eating.dm"
+#include "code\datums\diseases\advance\symptoms\genetics.dm"
#include "code\datums\diseases\advance\symptoms\hallucigen.dm"
#include "code\datums\diseases\advance\symptoms\headache.dm"
#include "code\datums\diseases\advance\symptoms\heal.dm"
@@ -475,14 +491,16 @@
#include "code\datums\elements\_element.dm"
#include "code\datums\elements\cleaning.dm"
#include "code\datums\elements\dusts_on_catatonia.dm"
+#include "code\datums\elements\dusts_on_leaving_area.dm"
#include "code\datums\elements\earhealing.dm"
#include "code\datums\elements\ghost_role_eligibility.dm"
+#include "code\datums\elements\mob_holder.dm"
+#include "code\datums\elements\swimming.dm"
#include "code\datums\elements\wuv.dm"
#include "code\datums\helper_datums\events.dm"
#include "code\datums\helper_datums\getrev.dm"
#include "code\datums\helper_datums\icon_snapshot.dm"
#include "code\datums\helper_datums\teleport.dm"
-#include "code\datums\helper_datums\topic_input.dm"
#include "code\datums\looping_sounds\_looping_sound.dm"
#include "code\datums\looping_sounds\item_sounds.dm"
#include "code\datums\looping_sounds\machinery_sounds.dm"
@@ -571,6 +589,7 @@
#include "code\game\gamemodes\game_mode.dm"
#include "code\game\gamemodes\objective.dm"
#include "code\game\gamemodes\objective_items.dm"
+#include "code\game\gamemodes\objective_sabotage.dm"
#include "code\game\gamemodes\bloodsucker\bloodsucker.dm"
#include "code\game\gamemodes\bloodsucker\hunter.dm"
#include "code\game\gamemodes\brother\traitor_bro.dm"
@@ -653,7 +672,6 @@
#include "code\game\machinery\magnet.dm"
#include "code\game\machinery\mass_driver.dm"
#include "code\game\machinery\navbeacon.dm"
-#include "code\game\machinery\newscaster.dm"
#include "code\game\machinery\PDApainter.dm"
#include "code\game\machinery\quantum_pad.dm"
#include "code\game\machinery\recharger.dm"
@@ -770,6 +788,7 @@
#include "code\game\mecha\equipment\tools\mining_tools.dm"
#include "code\game\mecha\equipment\tools\other_tools.dm"
#include "code\game\mecha\equipment\tools\work_tools.dm"
+#include "code\game\mecha\equipment\weapons\mecha_ammo.dm"
#include "code\game\mecha\equipment\weapons\weapons.dm"
#include "code\game\mecha\medical\medical.dm"
#include "code\game\mecha\medical\odysseus.dm"
@@ -855,6 +874,7 @@
#include "code\game\objects\items\cosmetics.dm"
#include "code\game\objects\items\courtroom.dm"
#include "code\game\objects\items\crayons.dm"
+#include "code\game\objects\items\debug_items.dm"
#include "code\game\objects\items\defib.dm"
#include "code\game\objects\items\dehy_carp.dm"
#include "code\game\objects\items\dice.dm"
@@ -967,6 +987,7 @@
#include "code\game\objects\items\implants\implant_exile.dm"
#include "code\game\objects\items\implants\implant_explosive.dm"
#include "code\game\objects\items\implants\implant_freedom.dm"
+#include "code\game\objects\items\implants\implant_hijack.dm"
#include "code\game\objects\items\implants\implant_krav_maga.dm"
#include "code\game\objects\items\implants\implant_mindshield.dm"
#include "code\game\objects\items\implants\implant_misc.dm"
@@ -1228,7 +1249,9 @@
#include "code\modules\antagonists\_common\antag_spawner.dm"
#include "code\modules\antagonists\_common\antag_team.dm"
#include "code\modules\antagonists\abductor\abductor.dm"
+#include "code\modules\antagonists\abductor\abductee\abductee.dm"
#include "code\modules\antagonists\abductor\abductee\abductee_objectives.dm"
+#include "code\modules\antagonists\abductor\abductee\trauma.dm"
#include "code\modules\antagonists\abductor\equipment\abduction_gear.dm"
#include "code\modules\antagonists\abductor\equipment\abduction_outfits.dm"
#include "code\modules\antagonists\abductor\equipment\abduction_surgery.dm"
@@ -1280,18 +1303,18 @@
#include "code\modules\antagonists\bloodsucker\objects\bloodsucker_coffin.dm"
#include "code\modules\antagonists\bloodsucker\objects\bloodsucker_crypt.dm"
#include "code\modules\antagonists\bloodsucker\objects\bloodsucker_lair.dm"
-#include "code\modules\antagonists\bloodsucker\powers\bs_brawn.dm"
-#include "code\modules\antagonists\bloodsucker\powers\bs_cloak.dm"
-#include "code\modules\antagonists\bloodsucker\powers\bs_feed.dm"
-#include "code\modules\antagonists\bloodsucker\powers\bs_fortitude.dm"
-#include "code\modules\antagonists\bloodsucker\powers\bs_gohome.dm"
-#include "code\modules\antagonists\bloodsucker\powers\bs_haste.dm"
-#include "code\modules\antagonists\bloodsucker\powers\bs_lunge.dm"
-#include "code\modules\antagonists\bloodsucker\powers\bs_masquerade.dm"
-#include "code\modules\antagonists\bloodsucker\powers\bs_mesmerize.dm"
-#include "code\modules\antagonists\bloodsucker\powers\bs_trespass.dm"
-#include "code\modules\antagonists\bloodsucker\powers\bs_veil.dm"
-#include "code\modules\antagonists\bloodsucker\powers\v_recuperate.dm"
+#include "code\modules\antagonists\bloodsucker\powers\brawn.dm"
+#include "code\modules\antagonists\bloodsucker\powers\cloak.dm"
+#include "code\modules\antagonists\bloodsucker\powers\feed.dm"
+#include "code\modules\antagonists\bloodsucker\powers\fortitude.dm"
+#include "code\modules\antagonists\bloodsucker\powers\go_home.dm"
+#include "code\modules\antagonists\bloodsucker\powers\haste.dm"
+#include "code\modules\antagonists\bloodsucker\powers\lunge.dm"
+#include "code\modules\antagonists\bloodsucker\powers\masquerade.dm"
+#include "code\modules\antagonists\bloodsucker\powers\mesmerize.dm"
+#include "code\modules\antagonists\bloodsucker\powers\recuperate.dm"
+#include "code\modules\antagonists\bloodsucker\powers\trespass.dm"
+#include "code\modules\antagonists\bloodsucker\powers\veil.dm"
#include "code\modules\antagonists\brainwashing\brainwashing.dm"
#include "code\modules\antagonists\brother\brother.dm"
#include "code\modules\antagonists\changeling\cellular_emporium.dm"
@@ -1378,6 +1401,7 @@
#include "code\modules\antagonists\clockcult\clock_structures\traps\brass_skewer.dm"
#include "code\modules\antagonists\clockcult\clock_structures\traps\power_null.dm"
#include "code\modules\antagonists\clockcult\clock_structures\traps\steam_vent.dm"
+#include "code\modules\antagonists\collector\collector.dm"
#include "code\modules\antagonists\cult\blood_magic.dm"
#include "code\modules\antagonists\cult\cult.dm"
#include "code\modules\antagonists\cult\cult_comms.dm"
@@ -1600,6 +1624,7 @@
#include "code\modules\cargo\packs\science.dm"
#include "code\modules\cargo\packs\security.dm"
#include "code\modules\cargo\packs\service.dm"
+#include "code\modules\cargo\packs\vending.dm"
#include "code\modules\chatter\chatter.dm"
#include "code\modules\client\asset_cache.dm"
#include "code\modules\client\client_colour.dm"
@@ -1627,12 +1652,11 @@
#include "code\modules\clothing\glasses\engine_goggles.dm"
#include "code\modules\clothing\glasses\hud.dm"
#include "code\modules\clothing\glasses\phantomthief.dm"
-#include "code\modules\clothing\glasses\vg_glasses.dm"
#include "code\modules\clothing\gloves\_gloves.dm"
#include "code\modules\clothing\gloves\boxing.dm"
#include "code\modules\clothing\gloves\color.dm"
#include "code\modules\clothing\gloves\miscellaneous.dm"
-#include "code\modules\clothing\gloves\vg_gloves.dm"
+#include "code\modules\clothing\gloves\ring.dm"
#include "code\modules\clothing\head\_head.dm"
#include "code\modules\clothing\head\beanie.dm"
#include "code\modules\clothing\head\cit_hats.dm"
@@ -1643,14 +1667,12 @@
#include "code\modules\clothing\head\misc.dm"
#include "code\modules\clothing\head\misc_special.dm"
#include "code\modules\clothing\head\soft_caps.dm"
-#include "code\modules\clothing\head\vg_hats.dm"
#include "code\modules\clothing\masks\_masks.dm"
#include "code\modules\clothing\masks\boxing.dm"
#include "code\modules\clothing\masks\breath.dm"
#include "code\modules\clothing\masks\gasmask.dm"
#include "code\modules\clothing\masks\hailer.dm"
#include "code\modules\clothing\masks\miscellaneous.dm"
-#include "code\modules\clothing\masks\vg_masks.dm"
#include "code\modules\clothing\neck\_neck.dm"
#include "code\modules\clothing\outfits\ert.dm"
#include "code\modules\clothing\outfits\event.dm"
@@ -1664,14 +1686,12 @@
#include "code\modules\clothing\shoes\magboots.dm"
#include "code\modules\clothing\shoes\miscellaneous.dm"
#include "code\modules\clothing\shoes\taeclowndo.dm"
-#include "code\modules\clothing\shoes\vg_shoes.dm"
#include "code\modules\clothing\spacesuits\_spacesuits.dm"
#include "code\modules\clothing\spacesuits\chronosuit.dm"
#include "code\modules\clothing\spacesuits\hardsuit.dm"
#include "code\modules\clothing\spacesuits\miscellaneous.dm"
#include "code\modules\clothing\spacesuits\plasmamen.dm"
#include "code\modules\clothing\spacesuits\syndi.dm"
-#include "code\modules\clothing\spacesuits\vg_spess.dm"
#include "code\modules\clothing\suits\_suits.dm"
#include "code\modules\clothing\suits\armor.dm"
#include "code\modules\clothing\suits\bio.dm"
@@ -1682,7 +1702,6 @@
#include "code\modules\clothing\suits\reactive_armour.dm"
#include "code\modules\clothing\suits\toggles.dm"
#include "code\modules\clothing\suits\utility.dm"
-#include "code\modules\clothing\suits\vg_suits.dm"
#include "code\modules\clothing\suits\wiz_robe.dm"
#include "code\modules\clothing\under\_under.dm"
#include "code\modules\clothing\under\accessories.dm"
@@ -1693,7 +1712,6 @@
#include "code\modules\clothing\under\shorts.dm"
#include "code\modules\clothing\under\syndicate.dm"
#include "code\modules\clothing\under\trek.dm"
-#include "code\modules\clothing\under\vg_under.dm"
#include "code\modules\clothing\under\jobs\civilian.dm"
#include "code\modules\clothing\under\jobs\engineering.dm"
#include "code\modules\clothing\under\jobs\medsci.dm"
@@ -1702,15 +1720,6 @@
#include "code\modules\clothing\under\jobs\Plasmaman\engineering.dm"
#include "code\modules\clothing\under\jobs\Plasmaman\medsci.dm"
#include "code\modules\clothing\under\jobs\Plasmaman\security.dm"
-#include "code\modules\crafting\craft.dm"
-#include "code\modules\crafting\glassware.dm"
-#include "code\modules\crafting\guncrafting.dm"
-#include "code\modules\crafting\recipes.dm"
-#include "code\modules\crafting\recipes\recipes_clothing.dm"
-#include "code\modules\crafting\recipes\recipes_misc.dm"
-#include "code\modules\crafting\recipes\recipes_primal.dm"
-#include "code\modules\crafting\recipes\recipes_robot.dm"
-#include "code\modules\crafting\recipes\recipes_weapon_and_ammo.dm"
#include "code\modules\detectivework\detective_work.dm"
#include "code\modules\detectivework\evidence.dm"
#include "code\modules\detectivework\scanner.dm"
@@ -1845,7 +1854,6 @@
#include "code\modules\food_and_drinks\recipes\tablecraft\recipes_sushi.dm"
#include "code\modules\games\cas.dm"
#include "code\modules\goonchat\browserOutput.dm"
-#include "code\modules\goonchat\jsErrorHandler.dm"
#include "code\modules\holiday\easter.dm"
#include "code\modules\holiday\holidays.dm"
#include "code\modules\holiday\halloween\bartholomew.dm"
@@ -2000,6 +2008,7 @@
#include "code\modules\language\common.dm"
#include "code\modules\language\draconic.dm"
#include "code\modules\language\drone.dm"
+#include "code\modules\language\dwarven.dm"
#include "code\modules\language\language.dm"
#include "code\modules\language\language_holder.dm"
#include "code\modules\language\language_menu.dm"
@@ -2129,7 +2138,6 @@
#include "code\modules\mob\living\damage_procs.dm"
#include "code\modules\mob\living\death.dm"
#include "code\modules\mob\living\emote.dm"
-#include "code\modules\mob\living\inhand_holder.dm"
#include "code\modules\mob\living\life.dm"
#include "code\modules\mob\living\living.dm"
#include "code\modules\mob\living\living_defense.dm"
@@ -2203,7 +2211,6 @@
#include "code\modules\mob\living\carbon\human\dummy.dm"
#include "code\modules\mob\living\carbon\human\emote.dm"
#include "code\modules\mob\living\carbon\human\examine.dm"
-#include "code\modules\mob\living\carbon\human\examine_vr.dm"
#include "code\modules\mob\living\carbon\human\human.dm"
#include "code\modules\mob\living\carbon\human\human_defense.dm"
#include "code\modules\mob\living\carbon\human\human_defines.dm"
@@ -2371,6 +2378,7 @@
#include "code\modules\mob\living\simple_animal\hostile\netherworld.dm"
#include "code\modules\mob\living\simple_animal\hostile\pirate.dm"
#include "code\modules\mob\living\simple_animal\hostile\russian.dm"
+#include "code\modules\mob\living\simple_animal\hostile\sharks.dm"
#include "code\modules\mob\living\simple_animal\hostile\skeleton.dm"
#include "code\modules\mob\living\simple_animal\hostile\statue.dm"
#include "code\modules\mob\living\simple_animal\hostile\stickman.dm"
@@ -2466,6 +2474,15 @@
#include "code\modules\modular_computers\hardware\printer.dm"
#include "code\modules\modular_computers\hardware\recharger.dm"
#include "code\modules\modular_computers\NTNet\NTNRC\conversation.dm"
+#include "code\modules\newscaster\_news.dm"
+#include "code\modules\newscaster\feed_channel.dm"
+#include "code\modules\newscaster\feed_comment.dm"
+#include "code\modules\newscaster\feed_message.dm"
+#include "code\modules\newscaster\ghostread.dm"
+#include "code\modules\newscaster\news_network.dm"
+#include "code\modules\newscaster\newscaster_machine.dm"
+#include "code\modules\newscaster\newspaper.dm"
+#include "code\modules\newscaster\wanted_message.dm"
#include "code\modules\ninja\__ninjaDefines.dm"
#include "code\modules\ninja\energy_katana.dm"
#include "code\modules\ninja\ninja_event.dm"
@@ -2518,6 +2535,13 @@
#include "code\modules\photography\photos\album.dm"
#include "code\modules\photography\photos\frame.dm"
#include "code\modules\photography\photos\photo.dm"
+#include "code\modules\pool\pool_controller.dm"
+#include "code\modules\pool\pool_drain.dm"
+#include "code\modules\pool\pool_effects.dm"
+#include "code\modules\pool\pool_main.dm"
+#include "code\modules\pool\pool_noodles.dm"
+#include "code\modules\pool\pool_structures.dm"
+#include "code\modules\pool\pool_wires.dm"
#include "code\modules\power\apc.dm"
#include "code\modules\power\cable.dm"
#include "code\modules\power\cell.dm"
@@ -2583,6 +2607,8 @@
#include "code\modules\projectiles\ammunition\ballistic\smg.dm"
#include "code\modules\projectiles\ammunition\ballistic\sniper.dm"
#include "code\modules\projectiles\ammunition\caseless\_caseless.dm"
+#include "code\modules\projectiles\ammunition\caseless\arrow.dm"
+#include "code\modules\projectiles\ammunition\caseless\ferromagnetic.dm"
#include "code\modules\projectiles\ammunition\caseless\foam.dm"
#include "code\modules\projectiles\ammunition\caseless\misc.dm"
#include "code\modules\projectiles\ammunition\caseless\rocket.dm"
@@ -2602,6 +2628,7 @@
#include "code\modules\projectiles\boxes_magazines\ammo_boxes.dm"
#include "code\modules\projectiles\boxes_magazines\external\grenade.dm"
#include "code\modules\projectiles\boxes_magazines\external\lmg.dm"
+#include "code\modules\projectiles\boxes_magazines\external\magweapon.dm"
#include "code\modules\projectiles\boxes_magazines\external\pistol.dm"
#include "code\modules\projectiles\boxes_magazines\external\rechargable.dm"
#include "code\modules\projectiles\boxes_magazines\external\rifle.dm"
@@ -2611,6 +2638,7 @@
#include "code\modules\projectiles\boxes_magazines\external\toy.dm"
#include "code\modules\projectiles\boxes_magazines\internal\_cylinder.dm"
#include "code\modules\projectiles\boxes_magazines\internal\_internal.dm"
+#include "code\modules\projectiles\boxes_magazines\internal\bow.dm"
#include "code\modules\projectiles\boxes_magazines\internal\grenade.dm"
#include "code\modules\projectiles\boxes_magazines\internal\misc.dm"
#include "code\modules\projectiles\boxes_magazines\internal\revolver.dm"
@@ -2621,12 +2649,15 @@
#include "code\modules\projectiles\guns\energy.dm"
#include "code\modules\projectiles\guns\magic.dm"
#include "code\modules\projectiles\guns\ballistic\automatic.dm"
+#include "code\modules\projectiles\guns\ballistic\bow.dm"
#include "code\modules\projectiles\guns\ballistic\laser_gatling.dm"
#include "code\modules\projectiles\guns\ballistic\launchers.dm"
+#include "code\modules\projectiles\guns\ballistic\magweapon.dm"
#include "code\modules\projectiles\guns\ballistic\pistol.dm"
#include "code\modules\projectiles\guns\ballistic\revolver.dm"
#include "code\modules\projectiles\guns\ballistic\shotgun.dm"
#include "code\modules\projectiles\guns\ballistic\toy.dm"
+#include "code\modules\projectiles\guns\energy\dueling.dm"
#include "code\modules\projectiles\guns\energy\energy_gun.dm"
#include "code\modules\projectiles\guns\energy\kinetic_accelerator.dm"
#include "code\modules\projectiles\guns\energy\laser.dm"
@@ -2652,6 +2683,7 @@
#include "code\modules\projectiles\projectile\bullets\_incendiary.dm"
#include "code\modules\projectiles\projectile\bullets\dart_syringe.dm"
#include "code\modules\projectiles\projectile\bullets\dnainjector.dm"
+#include "code\modules\projectiles\projectile\bullets\ferromagnetic.dm"
#include "code\modules\projectiles\projectile\bullets\grenade.dm"
#include "code\modules\projectiles\projectile\bullets\lmg.dm"
#include "code\modules\projectiles\projectile\bullets\pistol.dm"
@@ -2670,6 +2702,7 @@
#include "code\modules\projectiles\projectile\energy\tesla.dm"
#include "code\modules\projectiles\projectile\magic\spellcard.dm"
#include "code\modules\projectiles\projectile\reusable\_reusable.dm"
+#include "code\modules\projectiles\projectile\reusable\arrow.dm"
#include "code\modules\projectiles\projectile\reusable\foam_dart.dm"
#include "code\modules\projectiles\projectile\reusable\magspear.dm"
#include "code\modules\projectiles\projectile\special\curse.dm"
@@ -2797,7 +2830,6 @@
#include "code\modules\research\nanites\nanite_chamber.dm"
#include "code\modules\research\nanites\nanite_chamber_computer.dm"
#include "code\modules\research\nanites\nanite_cloud_controller.dm"
-#include "code\modules\research\nanites\nanite_hijacker.dm"
#include "code\modules\research\nanites\nanite_misc_items.dm"
#include "code\modules\research\nanites\nanite_program_hub.dm"
#include "code\modules\research\nanites\nanite_programmer.dm"
@@ -2805,6 +2837,12 @@
#include "code\modules\research\nanites\nanite_remote.dm"
#include "code\modules\research\nanites\program_disks.dm"
#include "code\modules\research\nanites\public_chamber.dm"
+#include "code\modules\research\nanites\rules.dm"
+#include "code\modules\research\nanites\extra_settings\_extra_setting.dm"
+#include "code\modules\research\nanites\extra_settings\boolean.dm"
+#include "code\modules\research\nanites\extra_settings\number.dm"
+#include "code\modules\research\nanites\extra_settings\text.dm"
+#include "code\modules\research\nanites\extra_settings\type.dm"
#include "code\modules\research\nanites\nanite_programs\buffing.dm"
#include "code\modules\research\nanites\nanite_programs\healing.dm"
#include "code\modules\research\nanites\nanite_programs\rogue.dm"
@@ -3181,12 +3219,9 @@
#include "modular_citadel\code\modules\projectiles\guns\pumpenergy.dm"
#include "modular_citadel\code\modules\projectiles\guns\toys.dm"
#include "modular_citadel\code\modules\projectiles\guns\ballistic\handguns.dm"
-#include "modular_citadel\code\modules\projectiles\guns\ballistic\magweapon.dm"
-#include "modular_citadel\code\modules\projectiles\guns\ballistic\magweapon_energy.dm"
#include "modular_citadel\code\modules\projectiles\guns\ballistic\rifles.dm"
#include "modular_citadel\code\modules\projectiles\guns\ballistic\spinfusor.dm"
#include "modular_citadel\code\modules\projectiles\guns\energy\energy_gun.dm"
-#include "modular_citadel\code\modules\projectiles\guns\energy\laser.dm"
#include "modular_citadel\code\modules\projectiles\projectiles\reusable.dm"
#include "modular_citadel\code\modules\reagents\chemistry\reagents\astrogen.dm"
#include "modular_citadel\code\modules\reagents\chemistry\reagents\eigentstasium.dm"
diff --git a/tgui-next/.editorconfig b/tgui-next/.editorconfig
new file mode 100644
index 0000000000..33092d4928
--- /dev/null
+++ b/tgui-next/.editorconfig
@@ -0,0 +1,13 @@
+# http://editorconfig.org
+root = true
+
+[*]
+indent_style = space
+indent_size = 2
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.md]
+max_line_length = 80
diff --git a/tgui-next/.eslintignore b/tgui-next/.eslintignore
new file mode 100644
index 0000000000..010416b9e8
--- /dev/null
+++ b/tgui-next/.eslintignore
@@ -0,0 +1,6 @@
+/**/node_modules
+/**/*.bundle.*
+/**/*.chunk.*
+/**/*.hot-update.*
+/packages/inferno/**
+/packages/tgui/public/shim-*.js
diff --git a/tgui-next/.eslintrc-harder.yml b/tgui-next/.eslintrc-harder.yml
new file mode 100644
index 0000000000..eb06f5b33d
--- /dev/null
+++ b/tgui-next/.eslintrc-harder.yml
@@ -0,0 +1,14 @@
+rules:
+ ## Enforce a maximum cyclomatic complexity allowed in a program
+ complexity: [error, { max: 25 }]
+ ## Enforce consistent brace style for blocks
+ brace-style: [error, stroustrup, { allowSingleLine: false }]
+ ## Enforce the consistent use of either backticks, double, or single quotes
+ quotes: [error, single, {
+ avoidEscape: true,
+ allowTemplateLiterals: true,
+ }]
+ react/jsx-closing-bracket-location: [error, {
+ selfClosing: after-props,
+ nonEmpty: after-props,
+ }]
diff --git a/tgui-next/.eslintrc.yml b/tgui-next/.eslintrc.yml
new file mode 100644
index 0000000000..4bff333295
--- /dev/null
+++ b/tgui-next/.eslintrc.yml
@@ -0,0 +1,749 @@
+parser: babel-eslint
+parserOptions:
+ ecmaVersion: 2019
+ sourceType: module
+ ecmaFeatures:
+ jsx: true
+env:
+ es6: true
+ browser: true
+ node: true
+plugins:
+ - react
+settings:
+ react:
+ version: '16.10'
+rules:
+
+ ## Possible Errors
+ ## ----------------------------------------
+
+ ## Enforce “for” loop update clause moving the counter in the right
+ ## direction.
+ # for-direction: error
+ ## Enforce return statements in getters
+ # getter-return: error
+ ## Disallow using an async function as a Promise executor
+ no-async-promise-executor: error
+ ## Disallow await inside of loops
+ # no-await-in-loop: error
+ ## Disallow comparing against -0
+ # no-compare-neg-zero: error
+ ## Disallow assignment operators in conditional expressions
+ no-cond-assign: error
+ ## Disallow the use of console
+ # no-console: error
+ ## Disallow constant expressions in conditions
+ # no-constant-condition: error
+ ## Disallow control characters in regular expressions
+ # no-control-regex: error
+ ## Disallow the use of debugger
+ no-debugger: error
+ ## Disallow duplicate arguments in function definitions
+ no-dupe-args: error
+ ## Disallow duplicate keys in object literals
+ no-dupe-keys: error
+ ## Disallow duplicate case labels
+ no-duplicate-case: error
+ ## Disallow empty block statements
+ # no-empty: error
+ ## Disallow empty character classes in regular expressions
+ no-empty-character-class: error
+ ## Disallow reassigning exceptions in catch clauses
+ no-ex-assign: error
+ ## Disallow unnecessary boolean casts
+ no-extra-boolean-cast: error
+ ## Disallow unnecessary parentheses
+ # no-extra-parens: warn
+ ## Disallow unnecessary semicolons
+ no-extra-semi: error
+ ## Disallow reassigning function declarations
+ no-func-assign: error
+ ## Disallow assigning to imported bindings
+ no-import-assign: error
+ ## Disallow variable or function declarations in nested blocks
+ no-inner-declarations: error
+ ## Disallow invalid regular expression strings in RegExp constructors
+ no-invalid-regexp: error
+ ## Disallow irregular whitespace
+ no-irregular-whitespace: error
+ ## Disallow characters which are made with multiple code points in character
+ ## class syntax
+ no-misleading-character-class: error
+ ## Disallow calling global object properties as functions
+ no-obj-calls: error
+ ## Disallow calling some Object.prototype methods directly on objects
+ no-prototype-builtins: error
+ ## Disallow multiple spaces in regular expressions
+ no-regex-spaces: error
+ ## Disallow sparse arrays
+ no-sparse-arrays: error
+ ## Disallow template literal placeholder syntax in regular strings
+ no-template-curly-in-string: error
+ ## Disallow confusing multiline expressions
+ no-unexpected-multiline: error
+ ## Disallow unreachable code after return, throw, continue, and break
+ ## statements
+ # no-unreachable: warn
+ ## Disallow control flow statements in finally blocks
+ no-unsafe-finally: error
+ ## Disallow negating the left operand of relational operators
+ no-unsafe-negation: error
+ ## Disallow assignments that can lead to race conditions due to usage of
+ ## await or yield
+ # require-atomic-updates: error
+ ## Require calls to isNaN() when checking for NaN
+ use-isnan: error
+ ## Enforce comparing typeof expressions against valid strings
+ valid-typeof: error
+
+ ## Best practices
+ ## ----------------------------------------
+ ## Enforce getter and setter pairs in objects and classes
+ # accessor-pairs: error
+ ## Enforce return statements in callbacks of array methods
+ # array-callback-return: error
+ ## Enforce the use of variables within the scope they are defined
+ # block-scoped-var: error
+ ## Enforce that class methods utilize this
+ # class-methods-use-this: error
+ ## Enforce a maximum cyclomatic complexity allowed in a program
+ complexity: [error, { max: 50 }]
+ ## Require return statements to either always or never specify values
+ # consistent-return: error
+ ## Enforce consistent brace style for all control statements
+ curly: [error, all]
+ ## Require default cases in switch statements
+ # default-case: error
+ ## Enforce default parameters to be last
+ # default-param-last: error
+ ## Enforce consistent newlines before and after dots
+ dot-location: [error, property]
+ ## Enforce dot notation whenever possible
+ # dot-notation: error
+ ## Require the use of === and !==
+ eqeqeq: [error, always]
+ ## Require for-in loops to include an if statement
+ # guard-for-in: error
+ ## Enforce a maximum number of classes per file
+ # max-classes-per-file: error
+ ## Disallow the use of alert, confirm, and prompt
+ # no-alert: error
+ ## Disallow the use of arguments.caller or arguments.callee
+ # no-caller: error
+ ## Disallow lexical declarations in case clauses
+ no-case-declarations: error
+ ## Disallow division operators explicitly at the beginning of regular
+ ## expressions
+ # no-div-regex: error
+ ## Disallow else blocks after return statements in if statements
+ # no-else-return: error
+ ## Disallow empty functions
+ # no-empty-function: error
+ ## Disallow empty destructuring patterns
+ no-empty-pattern: error
+ ## Disallow null comparisons without type-checking operators
+ # no-eq-null: error
+ ## Disallow the use of eval()
+ # no-eval: error
+ ## Disallow extending native types
+ # no-extend-native: error
+ ## Disallow unnecessary calls to .bind()
+ # no-extra-bind: error
+ ## Disallow unnecessary labels
+ # no-extra-label: error
+ ## Disallow fallthrough of case statements
+ no-fallthrough: error
+ ## Disallow leading or trailing decimal points in numeric literals
+ # no-floating-decimal: error
+ ## Disallow assignments to native objects or read-only global variables
+ no-global-assign: error
+ ## Disallow shorthand type conversions
+ # no-implicit-coercion: error
+ ## Disallow variable and function declarations in the global scope
+ # no-implicit-globals: error
+ ## Disallow the use of eval()-like methods
+ # no-implied-eval: error
+ ## Disallow this keywords outside of classes or class-like objects
+ # no-invalid-this: error
+ ## Disallow the use of the __iterator__ property
+ # no-iterator: error
+ ## Disallow labeled statements
+ # no-labels: error
+ ## Disallow unnecessary nested blocks
+ # no-lone-blocks: error
+ ## Disallow function declarations that contain unsafe references inside
+ ## loop statements
+ # no-loop-func: error
+ ## Disallow magic numbers
+ # no-magic-numbers: error
+ ## Disallow multiple spaces
+ no-multi-spaces: warn
+ ## Disallow multiline strings
+ # no-multi-str: error
+ ## Disallow new operators outside of assignments or comparisons
+ # no-new: error
+ ## Disallow new operators with the Function object
+ # no-new-func: error
+ ## Disallow new operators with the String, Number, and Boolean objects
+ # no-new-wrappers: error
+ ## Disallow octal literals
+ no-octal: error
+ ## Disallow octal escape sequences in string literals
+ no-octal-escape: error
+ ## Disallow reassigning function parameters
+ # no-param-reassign: error
+ ## Disallow the use of the __proto__ property
+ # no-proto: error
+ ## Disallow variable redeclaration
+ no-redeclare: error
+ ## Disallow certain properties on certain objects
+ # no-restricted-properties: error
+ ## Disallow assignment operators in return statements
+ no-return-assign: error
+ ## Disallow unnecessary return await
+ # no-return-await: error
+ ## Disallow javascript: urls
+ # no-script-url: error
+ ## Disallow assignments where both sides are exactly the same
+ no-self-assign: error
+ ## Disallow comparisons where both sides are exactly the same
+ # no-self-compare: error
+ ## Disallow comma operators
+ no-sequences: error
+ ## Disallow throwing literals as exceptions
+ # no-throw-literal: error
+ ## Disallow unmodified loop conditions
+ # no-unmodified-loop-condition: error
+ ## Disallow unused expressions
+ # no-unused-expressions: error
+ ## Disallow unused labels
+ no-unused-labels: warn
+ ## Disallow unnecessary calls to .call() and .apply()
+ # no-useless-call: error
+ ## Disallow unnecessary catch clauses
+ # no-useless-catch: error
+ ## Disallow unnecessary concatenation of literals or template literals
+ # no-useless-concat: error
+ ## Disallow unnecessary escape characters
+ no-useless-escape: warn
+ ## Disallow redundant return statements
+ # no-useless-return: error
+ ## Disallow void operators
+ # no-void: error
+ ## Disallow specified warning terms in comments
+ # no-warning-comments: error
+ ## Disallow with statements
+ no-with: error
+ ## Enforce using named capture group in regular expression
+ # prefer-named-capture-group: error
+ ## Require using Error objects as Promise rejection reasons
+ # prefer-promise-reject-errors: error
+ ## Disallow use of the RegExp constructor in favor of regular expression
+ ## literals
+ # prefer-regex-literals: error
+ ## Enforce the consistent use of the radix argument when using parseInt()
+ radix: error
+ ## Disallow async functions which have no await expression
+ # require-await: error
+ ## Enforce the use of u flag on RegExp
+ # require-unicode-regexp: error
+ ## Require var declarations be placed at the top of their containing scope
+ # vars-on-top: error
+ ## Require parentheses around immediate function invocations
+ # wrap-iife: error
+ ## Require or disallow “Yoda” conditions
+ # yoda: error
+
+ ## Strict mode
+ ## ----------------------------------------
+ ## Require or disallow strict mode directives
+ strict: error
+
+ ## Variables
+ ## ----------------------------------------
+ ## Require or disallow initialization in variable declarations
+ # init-declarations: error
+ ## Disallow deleting variables
+ no-delete-var: error
+ ## Disallow labels that share a name with a variable
+ # no-label-var: error
+ ## Disallow specified global variables
+ # no-restricted-globals: error
+ ## Disallow variable declarations from shadowing variables declared in
+ ## the outer scope
+ # no-shadow: error
+ ## Disallow identifiers from shadowing restricted names
+ no-shadow-restricted-names: error
+ ## Disallow the use of undeclared variables unless mentioned
+ ## in /*global*/ comments
+ no-undef: error
+ ## Disallow initializing variables to undefined
+ no-undef-init: error
+ ## Disallow the use of undefined as an identifier
+ # no-undefined: error
+ ## Disallow unused variables
+ # no-unused-vars: error
+ ## Disallow the use of variables before they are defined
+ # no-use-before-define: error
+
+ ## Code style
+ ## ----------------------------------------
+ ## Enforce linebreaks after opening and before closing array brackets
+ array-bracket-newline: [error, consistent]
+ ## Enforce consistent spacing inside array brackets
+ array-bracket-spacing: [error, never]
+ ## Enforce line breaks after each array element
+ # array-element-newline: error
+ ## Disallow or enforce spaces inside of blocks after opening block and
+ ## before closing block
+ block-spacing: [error, always]
+ ## Enforce consistent brace style for blocks
+ # brace-style: [error, stroustrup, { allowSingleLine: false }]
+ ## Enforce camelcase naming convention
+ # camelcase: error
+ ## Enforce or disallow capitalization of the first letter of a comment
+ # capitalized-comments: error
+ ## Require or disallow trailing commas
+ comma-dangle: [error, always-multiline]
+ ## Enforce consistent spacing before and after commas
+ comma-spacing: [error, { before: false, after: true }]
+ ## Enforce consistent comma style
+ comma-style: [error, last]
+ ## Enforce consistent spacing inside computed property brackets
+ computed-property-spacing: [error, never]
+ ## Enforce consistent naming when capturing the current execution context
+ # consistent-this: error
+ ## Require or disallow newline at the end of files
+ # eol-last: error
+ ## Require or disallow spacing between function identifiers and their
+ ## invocations
+ func-call-spacing: [error, never]
+ ## Require function names to match the name of the variable or property
+ ## to which they are assigned
+ # func-name-matching: error
+ ## Require or disallow named function expressions
+ # func-names: error
+ ## Enforce the consistent use of either function declarations or expressions
+ func-style: [error, expression]
+ ## Enforce line breaks between arguments of a function call
+ # function-call-argument-newline: error
+ ## Enforce consistent line breaks inside function parentheses
+ ## NOTE: This rule does not honor a newline on opening paren.
+ # function-paren-newline: [error, never]
+ ## Disallow specified identifiers
+ # id-blacklist: error
+ ## Enforce minimum and maximum identifier lengths
+ # id-length: error
+ ## Require identifiers to match a specified regular expression
+ # id-match: error
+ ## Enforce the location of arrow function bodies
+ # implicit-arrow-linebreak: error
+ ## Enforce consistent indentation
+ indent: [error, 2, { SwitchCase: 1 }]
+ ## Enforce the consistent use of either double or single quotes in JSX
+ ## attributes
+ jsx-quotes: [error, prefer-double]
+ ## Enforce consistent spacing between keys and values in object literal
+ ## properties
+ key-spacing: [error, { beforeColon: false, afterColon: true }]
+ ## Enforce consistent spacing before and after keywords
+ keyword-spacing: [error, { before: true, after: true }]
+ ## Enforce position of line comments
+ # line-comment-position: error
+ ## Enforce consistent linebreak style
+ # linebreak-style: error
+ ## Require empty lines around comments
+ # lines-around-comment: error
+ ## Require or disallow an empty line between class members
+ # lines-between-class-members: error
+ ## Enforce a maximum depth that blocks can be nested
+ # max-depth: error
+ ## Enforce a maximum line length
+ max-len: [error, {
+ code: 80,
+ ## Ignore imports
+ ignorePattern: '^(import\s.+\sfrom\s|.*require\()',
+ ignoreUrls: true,
+ ignoreRegExpLiterals: true,
+ }]
+ ## Enforce a maximum number of lines per file
+ # max-lines: error
+ ## Enforce a maximum number of line of code in a function
+ # max-lines-per-function: error
+ ## Enforce a maximum depth that callbacks can be nested
+ # max-nested-callbacks: error
+ ## Enforce a maximum number of parameters in function definitions
+ # max-params: error
+ ## Enforce a maximum number of statements allowed in function blocks
+ # max-statements: error
+ ## Enforce a maximum number of statements allowed per line
+ # max-statements-per-line: error
+ ## Enforce a particular style for multiline comments
+ # multiline-comment-style: error
+ ## Enforce newlines between operands of ternary expressions
+ multiline-ternary: [error, always-multiline]
+ ## Require constructor names to begin with a capital letter
+ # new-cap: error
+ ## Enforce or disallow parentheses when invoking a constructor with no
+ ## arguments
+ # new-parens: error
+ ## Require a newline after each call in a method chain
+ # newline-per-chained-call: error
+ ## Disallow Array constructors
+ # no-array-constructor: error
+ ## Disallow bitwise operators
+ # no-bitwise: error
+ ## Disallow continue statements
+ # no-continue: error
+ ## Disallow inline comments after code
+ # no-inline-comments: error
+ ## Disallow if statements as the only statement in else blocks
+ # no-lonely-if: error
+ ## Disallow mixed binary operators
+ # no-mixed-operators: error
+ ## Disallow mixed spaces and tabs for indentation
+ no-mixed-spaces-and-tabs: error
+ ## Disallow use of chained assignment expressions
+ # no-multi-assign: error
+ ## Disallow multiple empty lines
+ # no-multiple-empty-lines: error
+ ## Disallow negated conditions
+ # no-negated-condition: error
+ ## Disallow nested ternary expressions
+ # no-nested-ternary: error
+ ## Disallow Object constructors
+ # no-new-object: error
+ ## Disallow the unary operators ++ and --
+ # no-plusplus: error
+ ## Disallow specified syntax
+ # no-restricted-syntax: error
+ ## Disallow all tabs
+ # no-tabs: error
+ ## Disallow ternary operators
+ # no-ternary: error
+ ## Disallow trailing whitespace at the end of lines
+ # no-trailing-spaces: error
+ ## Disallow dangling underscores in identifiers
+ # no-underscore-dangle: error
+ ## Disallow ternary operators when simpler alternatives exist
+ # no-unneeded-ternary: error
+ ## Disallow whitespace before properties
+ no-whitespace-before-property: error
+ ## Enforce the location of single-line statements
+ # nonblock-statement-body-position: error
+ ## Enforce consistent line breaks inside braces
+ # object-curly-newline: [error, { multiline: true }]
+ ## Enforce consistent spacing inside braces
+ object-curly-spacing: [error, always]
+ ## Enforce placing object properties on separate lines
+ # object-property-newline: error
+ ## Enforce variables to be declared either together or separately in
+ ## functions
+ # one-var: error
+ ## Require or disallow newlines around variable declarations
+ # one-var-declaration-per-line: error
+ ## Require or disallow assignment operator shorthand where possible
+ # operator-assignment: error
+ ## Enforce consistent linebreak style for operators
+ operator-linebreak: [error, before]
+ ## Require or disallow padding within blocks
+ # padded-blocks: error
+ ## Require or disallow padding lines between statements
+ # padding-line-between-statements: error
+ ## Disallow using Object.assign with an object literal as the first
+ ## argument and prefer the use of object spread instead.
+ # prefer-object-spread: error
+ ## Require quotes around object literal property names
+ # quote-props: error
+ ## Enforce the consistent use of either backticks, double, or single quotes
+ # quotes: [error, single]
+ ## Require or disallow semicolons instead of ASI
+ semi: error
+ ## Enforce consistent spacing before and after semicolons
+ semi-spacing: [error, { before: false, after: true }]
+ ## Enforce location of semicolons
+ semi-style: [error, last]
+ ## Require object keys to be sorted
+ # sort-keys: error
+ ## Require variables within the same declaration block to be sorted
+ # sort-vars: error
+ ## Enforce consistent spacing before blocks
+ space-before-blocks: [error, always]
+ ## Enforce consistent spacing before function definition opening parenthesis
+ space-before-function-paren: [error, {
+ anonymous: always,
+ named: never,
+ asyncArrow: always,
+ }]
+ ## Enforce consistent spacing inside parentheses
+ space-in-parens: [error, never]
+ ## Require spacing around infix operators
+ # space-infix-ops: error
+ ## Enforce consistent spacing before or after unary operators
+ # space-unary-ops: error
+ ## Enforce consistent spacing after the // or /* in a comment
+ spaced-comment: [error, always]
+ ## Enforce spacing around colons of switch statements
+ switch-colon-spacing: [error, { before: false, after: true }]
+ ## Require or disallow spacing between template tags and their literals
+ template-tag-spacing: [error, never]
+ ## Require or disallow Unicode byte order mark (BOM)
+ # unicode-bom: [error, never]
+ ## Require parenthesis around regex literals
+ # wrap-regex: error
+
+ ## ES6
+ ## ----------------------------------------
+ ## Require braces around arrow function bodies
+ # arrow-body-style: error
+ ## Require parentheses around arrow function arguments
+ arrow-parens: [error, as-needed]
+ ## Enforce consistent spacing before and after the arrow in arrow functions
+ arrow-spacing: [error, { before: true, after: true }]
+ ## Require super() calls in constructors
+ # constructor-super: error
+ ## Enforce consistent spacing around * operators in generator functions
+ generator-star-spacing: [error, { before: false, after: true }]
+ ## Disallow reassigning class members
+ no-class-assign: error
+ ## Disallow arrow functions where they could be confused with comparisons
+ # no-confusing-arrow: error
+ ## Disallow reassigning const variables
+ no-const-assign: error
+ ## Disallow duplicate class members
+ no-dupe-class-members: error
+ ## Disallow duplicate module imports
+ # no-duplicate-imports: error
+ ## Disallow new operators with the Symbol object
+ no-new-symbol: error
+ ## Disallow specified modules when loaded by import
+ # no-restricted-imports: error
+ ## Disallow this/super before calling super() in constructors
+ no-this-before-super: error
+ ## Disallow unnecessary computed property keys in object literals
+ # no-useless-computed-key: error
+ ## Disallow unnecessary constructors
+ # no-useless-constructor: error
+ ## Disallow renaming import, export, and destructured assignments to the
+ ## same name
+ # no-useless-rename: error
+ ## Require let or const instead of var
+ no-var: error
+ ## Require or disallow method and property shorthand syntax for object
+ ## literals
+ # object-shorthand: error
+ ## Require using arrow functions for callbacks
+ prefer-arrow-callback: error
+ ## Require const declarations for variables that are never reassigned after
+ ## declared
+ # prefer-const: error
+ ## Require destructuring from arrays and/or objects
+ # prefer-destructuring: error
+ ## Disallow parseInt() and Number.parseInt() in favor of binary, octal, and
+ ## hexadecimal literals
+ # prefer-numeric-literals: error
+ ## Require rest parameters instead of arguments
+ # prefer-rest-params: error
+ ## Require spread operators instead of .apply()
+ # prefer-spread: error
+ ## Require template literals instead of string concatenation
+ # prefer-template: error
+ ## Require generator functions to contain yield
+ # require-yield: error
+ ## Enforce spacing between rest and spread operators and their expressions
+ # rest-spread-spacing: error
+ ## Enforce sorted import declarations within modules
+ # sort-imports: error
+ ## Require symbol descriptions
+ # symbol-description: error
+ ## Require or disallow spacing around embedded expressions of template
+ ## strings
+ # template-curly-spacing: error
+ ## Require or disallow spacing around the * in yield* expressions
+ yield-star-spacing: [error, { before: false, after: true }]
+
+ ## React
+ ## ----------------------------------------
+ ## Enforces consistent naming for boolean props
+ react/boolean-prop-naming: error
+ ## Forbid "button" element without an explicit "type" attribute
+ react/button-has-type: error
+ ## Prevent extraneous defaultProps on components
+ react/default-props-match-prop-types: error
+ ## Rule enforces consistent usage of destructuring assignment in component
+ # react/destructuring-assignment: [error, always, { ignoreClassFields: true }]
+ ## Prevent missing displayName in a React component definition
+ react/display-name: error
+ ## Forbid certain props on Components
+ # react/forbid-component-props: error
+ ## Forbid certain props on DOM Nodes
+ # react/forbid-dom-props: error
+ ## Forbid certain elements
+ # react/forbid-elements: error
+ ## Forbid certain propTypes
+ # react/forbid-prop-types: error
+ ## Forbid foreign propTypes
+ # react/forbid-foreign-prop-types: error
+ ## Prevent using this.state inside this.setState
+ react/no-access-state-in-setstate: error
+ ## Prevent using Array index in key props
+ # react/no-array-index-key: error
+ ## Prevent passing children as props
+ react/no-children-prop: error
+ ## Prevent usage of dangerous JSX properties
+ react/no-danger: error
+ ## Prevent problem with children and props.dangerouslySetInnerHTML
+ react/no-danger-with-children: error
+ ## Prevent usage of deprecated methods, including component lifecycle
+ ## methods
+ react/no-deprecated: error
+ ## Prevent usage of setState in componentDidMount
+ react/no-did-mount-set-state: error
+ ## Prevent usage of setState in componentDidUpdate
+ react/no-did-update-set-state: error
+ ## Prevent direct mutation of this.state
+ react/no-direct-mutation-state: error
+ ## Prevent usage of findDOMNode
+ react/no-find-dom-node: error
+ ## Prevent usage of isMounted
+ react/no-is-mounted: error
+ ## Prevent multiple component definition per file
+ # react/no-multi-comp: error
+ ## Prevent usage of shouldComponentUpdate when extending React.PureComponent
+ react/no-redundant-should-component-update: error
+ ## Prevent usage of the return value of React.render
+ react/no-render-return-value: error
+ ## Prevent usage of setState
+ # react/no-set-state: error
+ ## Prevent common casing typos
+ react/no-typos: error
+ ## Prevent using string references in ref attribute.
+ react/no-string-refs: error
+ ## Prevent using this in stateless functional components
+ react/no-this-in-sfc: error
+ ## Prevent invalid characters from appearing in markup
+ react/no-unescaped-entities: error
+ ## Prevent usage of unknown DOM property (fixable)
+ react/no-unknown-property: error
+ ## Prevent usage of unsafe lifecycle methods
+ react/no-unsafe: error
+ ## Prevent definitions of unused prop types
+ react/no-unused-prop-types: error
+ ## Prevent definitions of unused state properties
+ react/no-unused-state: error
+ ## Prevent usage of setState in componentWillUpdate
+ react/no-will-update-set-state: error
+ ## Enforce ES5 or ES6 class for React Components
+ react/prefer-es6-class: error
+ ## Enforce that props are read-only
+ react/prefer-read-only-props: error
+ ## Enforce stateless React Components to be written as a pure function
+ react/prefer-stateless-function: error
+ ## Prevent missing props validation in a React component definition
+ # react/prop-types: error
+ ## Prevent missing React when using JSX
+ # react/react-in-jsx-scope: error
+ ## Enforce a defaultProps definition for every prop that is not a required
+ ## prop
+ # react/require-default-props: error
+ ## Enforce React components to have a shouldComponentUpdate method
+ # react/require-optimization: error
+ ## Enforce ES5 or ES6 class for returning value in render function
+ react/require-render-return: error
+ ## Prevent extra closing tags for components without children (fixable)
+ react/self-closing-comp: error
+ ## Enforce component methods order (fixable)
+ # react/sort-comp: error
+ ## Enforce propTypes declarations alphabetical sorting
+ # react/sort-prop-types: error
+ ## Enforce the state initialization style to be either in a constructor or
+ ## with a class property
+ react/state-in-constructor: error
+ ## Enforces where React component static properties should be positioned.
+ # react/static-property-placement: error
+ ## Enforce style prop value being an object
+ react/style-prop-object: error
+ ## Prevent void DOM elements (e.g. , ) from receiving children
+ react/void-dom-elements-no-children: error
+
+ ## JSX-specific rules
+ ## ----------------------------------------
+ ## Enforce boolean attributes notation in JSX (fixable)
+ react/jsx-boolean-value: error
+ ## Enforce or disallow spaces inside of curly braces in JSX attributes and
+ ## expressions.
+ # react/jsx-child-element-spacing: error
+ ## Validate closing bracket location in JSX (fixable)
+ react/jsx-closing-bracket-location: [error, {
+ ## NOTE: Not really sure about enforcing this one
+ selfClosing: false,
+ nonEmpty: after-props,
+ }]
+ ## Validate closing tag location in JSX (fixable)
+ react/jsx-closing-tag-location: error
+ ## Enforce or disallow newlines inside of curly braces in JSX attributes and
+ ## expressions (fixable)
+ react/jsx-curly-newline: error
+ ## Enforce or disallow spaces inside of curly braces in JSX attributes and
+ ## expressions (fixable)
+ react/jsx-curly-spacing: error
+ ## Enforce or disallow spaces around equal signs in JSX attributes (fixable)
+ react/jsx-equals-spacing: error
+ ## Restrict file extensions that may contain JSX
+ # react/jsx-filename-extension: error
+ ## Enforce position of the first prop in JSX (fixable)
+ # react/jsx-first-prop-new-line: error
+ ## Enforce event handler naming conventions in JSX
+ react/jsx-handler-names: error
+ ## Validate JSX indentation (fixable)
+ react/jsx-indent: [error, 2, {
+ checkAttributes: true,
+ }]
+ ## Validate props indentation in JSX (fixable)
+ react/jsx-indent-props: [error, 2]
+ ## Validate JSX has key prop when in array or iterator
+ react/jsx-key: error
+ ## Validate JSX maximum depth
+ react/jsx-max-depth: [error, { max: 6 }] ## Generous
+ ## Limit maximum of props on a single line in JSX (fixable)
+ # react/jsx-max-props-per-line: error
+ ## Prevent usage of .bind() and arrow functions in JSX props
+ # react/jsx-no-bind: error
+ ## Prevent comments from being inserted as text nodes
+ react/jsx-no-comment-textnodes: error
+ ## Prevent duplicate props in JSX
+ react/jsx-no-duplicate-props: error
+ ## Prevent usage of unwrapped JSX strings
+ # react/jsx-no-literals: error
+ ## Prevent usage of unsafe target='_blank'
+ react/jsx-no-target-blank: error
+ ## Disallow undeclared variables in JSX
+ react/jsx-no-undef: error
+ ## Disallow unnecessary fragments (fixable)
+ react/jsx-no-useless-fragment: error
+ ## Limit to one expression per line in JSX
+ # react/jsx-one-expression-per-line: error
+ ## Enforce curly braces or disallow unnecessary curly braces in JSX
+ # react/jsx-curly-brace-presence: error
+ ## Enforce shorthand or standard form for React fragments
+ react/jsx-fragments: error
+ ## Enforce PascalCase for user-defined JSX components
+ react/jsx-pascal-case: error
+ ## Disallow multiple spaces between inline JSX props (fixable)
+ react/jsx-props-no-multi-spaces: error
+ ## Disallow JSX props spreading
+ # react/jsx-props-no-spreading: error
+ ## Enforce default props alphabetical sorting
+ # react/jsx-sort-default-props: error
+ ## Enforce props alphabetical sorting (fixable)
+ # react/jsx-sort-props: error
+ ## Validate whitespace in and around the JSX opening and closing brackets
+ ## (fixable)
+ react/jsx-tag-spacing: error
+ ## Prevent React to be incorrectly marked as unused
+ react/jsx-uses-react: error
+ ## Prevent variables used in JSX to be incorrectly marked as unused
+ react/jsx-uses-vars: error
+ ## Prevent missing parentheses around multilines JSX (fixable)
+ react/jsx-wrap-multilines: error
diff --git a/tgui-next/.gitattributes b/tgui-next/.gitattributes
new file mode 100644
index 0000000000..0016cc3bf6
--- /dev/null
+++ b/tgui-next/.gitattributes
@@ -0,0 +1,10 @@
+* text=auto
+
+## Enforce text mode and LF line breaks
+*.js text eol=lf
+*.css text eol=lf
+*.html text eol=lf
+*.json text eol=lf
+
+## Treat bundles as binary and ignore them during conflicts
+*.bundle.* binary merge=tgui-merge-bundle
diff --git a/tgui-next/.gitignore b/tgui-next/.gitignore
new file mode 100644
index 0000000000..416ca3768d
--- /dev/null
+++ b/tgui-next/.gitignore
@@ -0,0 +1,7 @@
+node_modules
+*.log
+package-lock.json
+
+/packages/tgui/public/.tmp/**/*
+/packages/tgui/public/**/*.hot-update.*
+/packages/tgui/public/**/*.map
diff --git a/tgui-next/README.md b/tgui-next/README.md
new file mode 100644
index 0000000000..beb7aaff80
--- /dev/null
+++ b/tgui-next/README.md
@@ -0,0 +1,855 @@
+# tgui
+
+## Introduction
+
+tgui is a robust user interface framework of /tg/station.
+
+tgui is very different from most UIs you will encounter in BYOND programming.
+It is heavily reliant on Javascript and web technologies as opposed to DM.
+If you are familiar with NanoUI (a library which can be found on almost
+every other SS13 codebase), tgui should be fairly easy to pick up.
+
+## Learn tgui
+
+People come to tgui from different backgrounds and with different
+learning styles. Whether you prefer a more theoretical or a practical
+approach, we hope you’ll find this section helpful.
+
+### Practical tutorial
+
+If you are completely new to frontend and prefer to **learn by doing**,
+start with our [practical tutorial](docs/tutorial-and-examples.md).
+
+### Guides
+
+This project uses **Inferno** - a very fast UI rendering engine with a similar
+API to React. Take your time to read these guides:
+
+- [React guide](https://reactjs.org/docs/hello-world.html)
+- [Inferno documentation](https://infernojs.org/docs/guides/components) -
+highlights differences with React.
+
+If you were already familiar with an older, Ractive-based tgui, and want
+to translate concepts between old and new tgui, read this
+[interface conversion guide](docs/converting-old-tgui-interfaces.md).
+
+## Pre-requisites
+
+You will need these programs to start developing in tgui:
+
+- [Node v12.13+](https://nodejs.org/en/download/)
+- [Yarn v1.19+](https://yarnpkg.com/en/docs/install)
+- [MSys2](https://www.msys2.org/) (optional)
+
+> MSys2 closely replicates a unix-like environment which is necessary for
+> the `bin/tgui` script to run. It comes with a robust "mintty" terminal
+> emulator which is better than any standard Windows shell, it supports
+> "git" out of the box (almost like Git for Windows, but better), has
+> a "pacman" package manager, and you can install a text editor like "vim"
+> for a full boomer experience.
+
+## Usage
+
+**For MSys2, Git Bash, WSL, Linux or macOS users:**
+
+First and foremost, change your directory to `tgui-next`.
+
+Run `bin/tgui --install-git-hooks` (optional) to install merge drivers
+which will assist you in conflict resolution when rebasing your branches.
+
+Run one of the following:
+
+- `bin/tgui` - build the project in production mode.
+- `bin/tgui --dev` - launch a development server.
+ - tgui development server provides you with incremental compilation,
+ hot module replacement and logging facilities in all running instances
+ of tgui. In short, this means that you will instantly see changes in the
+ game as you code it. Very useful, highly recommended.
+ In order to use, you should start the game server first, connect to it so dreamseeker is
+ open, then start the dev server. You'll know if it's hooked correctly if data gets dumped
+ to the log when tgui windows are opened.
+- `bin/tgui --dev --reload` - reload byond cache once.
+- `bin/tgui --dev --debug` - run server with debug logging enabled.
+- `bin/tgui --dev --no-hot` - disable hot module replacement (helps when
+doing development on IE8).
+- `bin/tgui --lint` - show problems with the code.
+- `bin/tgui --lint --fix` - auto-fix problems with the code.
+- `bin/tgui --analyze` - run a bundle analyzer.
+- `bin/tgui --clean` - clean up project repo.
+- `bin/tgui [webpack options]` - build the project with custom webpack
+options.
+
+**For everyone else:**
+
+If you haven't opened the console already, you can do that by holding
+Shift and right clicking on the `tgui-next` folder, then pressing
+either `Open command window here` or `Open PowerShell window here`.
+
+Run `yarn install` to install npm dependencies, then one of the following:
+
+- `yarn run build` - build the project in production mode.
+- `yarn run watch` - launch a development server.
+- `yarn run lint` - show problems with the code.
+- `yarn run lint --fix` - auto-fix problems with the code.
+- `yarn run analyze` - run a bundle analyzer.
+
+We also got some batch files in store, for those who don't like fiddling
+with the console:
+
+- `bin/tgui-build.bat` - build the project in production mode.
+- `bin/tgui-dev-server.bat` - launch a development server.
+
+> Remember to always run a full build before submitting a PR. It creates
+> a compressed javascript bundle which is then referenced from DM code.
+> We prefer to keep it version controlled, so that people could build the
+> game just by using Dream Maker.
+
+## Project structure
+
+- `/packages` - Each folder here represents a self-contained Node module.
+- `/packages/common` - Helper functions
+- `/packages/tgui/index.js` - Application entry point.
+- `/packages/tgui/components` - Basic UI building blocks.
+- `/packages/tgui/interfaces` - Actual in-game interfaces.
+Interface takes data via the `state` prop and outputs an html-like stucture,
+which you can build using existing UI components.
+- `/packages/tgui/routes.js` - This is where you want to register new
+interfaces, otherwise they simply won't load.
+- `/packages/tgui/layout.js` - A root-level component, holding the
+window elements, like the titlebar, buttons, resize handlers. Calls
+`routes.js` to decide which component to render.
+- `/packages/tgui/styles/main.scss` - CSS entry point.
+- `/packages/tgui/styles/atomic.scss` - Atomic CSS classes.
+These are very simple, tiny, reusable CSS classes which you can use and
+combine to change appearance of your elements. Keep them small.
+- `/packages/tgui/styles/components.scss` - CSS classes which are used
+in UI components, and most of the stylesheets referenced here are located
+in `/packages/tgui/components`. These stylesheets closely follow the
+[BEM](https://en.bem.info/methodology/) methodology.
+- `/packages/tgui/styles/functions.scss` - Useful SASS functions.
+Stuff like `lighten`, `darken`, `luminance` are defined here.
+
+## Component reference
+
+> Notice: This documentation might be out of date, so always check the source
+> code to see the most up-to-date information.
+
+These are the components which you can use for interface construction.
+If you have trouble finding the exact prop you need on a component,
+please note, that most of these components inherit from other basic
+components, such as `Box`. This component in particular provides a lot
+of styling options for all components, e.g. `color` and `opacity`, thus
+it is used a lot in this framework.
+
+There are a few important semantics you need to know about:
+
+- `content` prop is a synonym to a `children` prop.
+ - `content` is better used when your element is a self-closing tag
+ (like ``), and when content is small and simple
+ enough to fit in a prop. Keep in mind, that this prop is **not** native
+ to React, and is a feature of this component system.
+ - `children` is better used when your element is a full tag (like
+ ``), and when content is long and complex. This is
+ a native React prop (unlike `content`), and contains all elements you
+ defined between the opening and the closing tag of an element.
+ - You should never use both on a same element.
+ - You should never use `children` explicitly as a prop on an element.
+- Inferno supports both camelcase (`onClick`) and lowercase (`onclick`)
+event names.
+ - Camel case names are what's called "synthetic" events, and are the
+ *preferred way* of handling events in React, for efficiency and
+ performance reasons. Please read
+ [Inferno Event Handling](https://infernojs.org/docs/guides/event-handling)
+ to understand what this is about.
+ - Lower case names are native browser events and should be used sparingly,
+ for example when you need an explicit IE8 support. **DO NOT** use
+ lowercase event handlers unless you really know what you are doing.
+ - [Button](#button) component straight up does not support lowercase event
+ handlers. Use the camel case `onClick` instead.
+
+### `AnimatedNumber`
+
+This component provides animations for numeric values.
+
+Props:
+
+- `value: number` - Value to animate.
+- `initial: number` - Initial value to use in animation when element
+first appears. If you set initial to `0` for example, number will always
+animate starting from `0`, and if omitted, it will not play an initial
+animation.
+- `format: value => value` - Output formatter.
+ - Example: `value => Math.round(value)`.
+- `children: (formattedValue, rawValue) => any` - Pull the animated number to
+animate more complex things deeper in the DOM tree.
+ - Example: `(_, value) => `
+
+### `BlockQuote`
+
+Just a block quote, just like this example in markdown:
+
+> Here's an example of a block quote.
+
+Props:
+
+- See inherited props: [Box](#box)
+
+### `Box`
+
+The Box component serves as a wrapper component for most of the CSS utility
+needs. It creates a new DOM element, a `
` by default that can be changed
+with the `as` property. Let's say you want to use a `` instead:
+
+```jsx
+
+
+
+```
+
+This works great when the changes can be isolated to a new DOM element.
+For instance, you can change the margin this way.
+
+However, sometimes you have to target the underlying DOM element.
+For instance, you want to change the text color of the button. The Button
+component defines its own color. CSS inheritance doesn't help.
+
+To workaround this problem, the Box children accept a render props function.
+This way, `Button` can pull out the `className` generated by the `Box`.
+
+```jsx
+
+ {props => }
+
+```
+
+`Box` units, like width, height and margins can be defined in two ways:
+- By plain numbers (1 unit equals `0.5em`);
+- In absolute measures, by providing a full unit string (e.g. `100px`).
+
+Units which are used in `Box` are `0.5em`, which are half font-size.
+Default font size is `12px`, so each unit is effectively `6px` in size.
+If you need more precision, you can always use fractional numbers.
+
+Props:
+
+- `as: string` - The component used for the root node.
+- `color: string` - Applies an atomic `color-` class to the element.
+ - See `styles/atomic/color.scss`.
+- `width: number` - Box width.
+- `minWidth: number` - Box minimum width.
+- `maxWidth: number` - Box maximum width.
+- `height: number` - Box height.
+- `minHeight: number` - Box minimum height.
+- `maxHeight: number` - Box maximum height.
+- `fontSize: number` - Font size.
+- `fontFamily: string` - Font family.
+- `lineHeight: number` - Directly affects the height of text lines.
+Useful for adjusting button height.
+- `inline: boolean` - Forces the `Box` to appear as an `inline-block`,
+or in other words, makes the `Box` flow with the text instead of taking
+all available horizontal space.
+- `m: number` - Margin on all sides.
+- `mx: number` - Horizontal margin.
+- `my: number` - Vertical margin.
+- `mt: number` - Top margin.
+- `mb: number` - Bottom margin.
+- `ml: number` - Left margin.
+- `mr: number` - Right margin.
+- `opacity: number` - Opacity, from 0 to 1.
+- `bold: boolean` - Make text bold.
+- `italic: boolean` - Make text italic.
+- `nowrap: boolean` - Stops text from wrapping.
+- `textAlign: string` - Align text inside the box.
+ - `left` (default)
+ - `center`
+ - `right`
+- `position: string` - A direct mapping to `position` CSS property.
+ - `relative` - Relative positioning.
+ - `absolute` - Absolute positioning.
+ - `fixed` - Fixed positioning.
+- `color: string` - An alias to `textColor`.
+- `textColor: string` - Sets text color.
+ - `#ffffff` - Hex format
+ - `rgba(255, 255, 255, 1)` - RGB format
+ - `purple` - Applies an atomic `color-` class to the element.
+ See `styles/color-map.scss`.
+- `backgroundColor: string` - Sets background color.
+ - `#ffffff` - Hex format
+ - `rgba(255, 255, 255, 1)` - RGB format
+
+### `Button`
+
+Buttons allow users to take actions, and make choices, with a single click.
+
+Props:
+
+- See inherited props: [Box](#box)
+- `fluid: boolean` - Fill all available horizontal space.
+- `icon: string` - Adds an icon to the button.
+- `color: string` - Button color, as defined in `variables.scss`.
+ - There is also a special color `transparent` - makes the button
+ transparent and slightly dim when inactive.
+- `disabled: boolean` - Disables and greys out the button.
+- `selected: boolean` - Activates the button (gives it a green color).
+- `tooltip: string` - A fancy, boxy tooltip, which appears when hovering
+over the button.
+- `tooltipPosition: string` - Position of the tooltip.
+ - `top` - Show tooltip above the button.
+ - `bottom` (default) - Show tooltip below the button.
+ - `left` - Show tooltip on the left of the button.
+ - `right` - Show tooltip on the right of the button.
+- `ellipsis: boolean` - If button width is constrained, button text will
+be truncated with an ellipsis. Be careful however, because this prop breaks
+the baseline alignment.
+- `title: string` - A native browser tooltip, which appears when hovering
+over the button.
+- `content/children: any` - Content to render inside the button.
+- `onClick: function` - Called when element is clicked.
+
+### `Button.Checkbox`
+
+A ghetto checkbox, made entirely using existing Button API.
+
+Props:
+
+- See inherited props: [Button](#button)
+- `checked: boolean` - Boolean value, which marks the checkbox as checked.
+
+### `Button.Confirm`
+
+A button with a an extra confirmation step, using native button component.
+
+Props:
+
+- See inherited props: [Button](#button)
+- `confirmMessage: string` - Text to display after first click; defaults to "Confirm?"
+- `confirmColor: string` - Color to display after first click; default to "bad"
+
+### `Button.Input`
+
+A button that turns into an input box after the first click. Turns back into a button after the user hits enter, defocuses, or hits escape. Enter and defocus commit, while escape cancels.
+
+Props:
+ - See inherited props: [Box](#box)
+ - `fluid`: fill availible horizontal space
+ - `onCommit: (e, value) => void`: function that is called after the user defocuses the input or presses enter
+ - `currentValue: string`: default string to display when the input is shown
+ - `defaultValue: string`: default value emitted if the user leaves the box blank when hitting enter or defocusing. If left undefined, will cancel the change on a blank defocus/enter
+
+### `Collapsible`
+
+Displays contents when open, acts as a fluid button when closed. Click to toggle, closed by default.
+
+Props:
+ - See inherited props: [Box](#box)
+ - `children: any` - What is collapsed when closed
+ - `title: string` - Text to display on the button for collapsing
+ - `color: string` - Color of the button; see [Button](#button)
+ - `buttons: any` - Buttons or other content to render inline with the button
+
+### `ColorBox`
+
+Displays a 1-character wide colored square. Can be used as a status indicator,
+or for visually representing a color.
+
+If you want to set a background color on an element, use a plain
+[Box](#box) instead.
+
+Props:
+
+- See inherited props: [Box](#box)
+- `color: string` - Color of the box.
+
+### `Dimmer`
+
+Dims surrounding area to emphasize content placed inside.
+
+Props:
+
+- See inherited props: [Box](#box)
+
+### `Dropdown`
+
+A simple dropdown box component. Lets the user select from a list of options and displays selected entry.
+
+Props:
+
+ - See inherited props: [Box](#box)
+ - `options: string[]` - An array of strings which will be displayed in the dropdown when open
+ - `selected: string` - Currently selected entry
+ - `width: number` - Width of dropdown button and resulting menu
+ - `over: boolean` - dropdown renders over instead of below
+ - `color: string` - color of dropdown button
+ - `onClick: (e) => void` - Called when dropdown button is clicked
+ - `onSelected: (value) => void` - Called when a value is picked from the list, `value` is the value that was picked
+
+### `Flex`
+
+Quickly manage the layout, alignment, and sizing of grid columns, navigation, components, and more with a full suite of responsive flexbox utilities.
+
+If you are new to or unfamiliar with flexbox, we encourage you to read this
+[CSS-Tricks flexbox guide](https://css-tricks.com/snippets/css/a-guide-to-flexbox/).
+
+Consists of two elements: `` and ``. Both of them provide
+the most straight-forward mapping to flex CSS properties as possible.
+
+One of the most basic usage of flex, is to align certain elements
+to the left, and certain elements to the right:
+
+```jsx
+
+
+ Button description
+
+
+
+
+
+
+```
+
+Flex item with `grow` property serves as a "filler", to separate the other
+two flex items as far as possible from each other.
+
+Props:
+
+- See inherited props: [Box](#box)
+- `spacing: number` - Spacing between flex items, in integer units
+(1 unit - 0.5em). Does not directly relate to a flex css property
+(adds a modifier class under the hood), and only integer numbers are
+supported.
+- `direction: string` - This establishes the main-axis, thus defining the
+direction flex items are placed in the flex container.
+ - `row` (default) - left to right.
+ - `row-reverse` - right to left.
+ - `column` - top to bottom.
+ - `column-reverse` - bottom to top.
+- `wrap: string` - By default, flex items will all try to fit onto one line.
+You can change that and allow the items to wrap as needed with this property.
+ - `nowrap` (default) - all flex items will be on one line
+ - `wrap` - flex items will wrap onto multiple lines, from top to bottom.
+ - `wrap-reverse` - flex items will wrap onto multiple lines from bottom to top.
+- `align: string` - Default alignment of all children.
+ - `stretch` (default) - stretch to fill the container.
+ - `start` - items are placed at the start of the cross axis.
+ - `end` - items are placed at the end of the cross axis.
+ - `center` - items are centered on the cross axis.
+ - `baseline` - items are aligned such as their baselines align.
+- `justify: string` - This defines the alignment along the main axis.
+It helps distribute extra free space leftover when either all the flex
+items on a line are inflexible, or are flexible but have reached their
+maximum size. It also exerts some control over the alignment of items
+when they overflow the line.
+ - `flex-start` (default) - items are packed toward the start of the
+ flex-direction.
+ - `flex-end` - items are packed toward the end of the flex-direction.
+ - `space-between` - items are evenly distributed in the line; first item is
+ on the start line, last item on the end line
+ - `space-around` - items are evenly distributed in the line with equal space
+ around them. Note that visually the spaces aren't equal, since all the items
+ have equal space on both sides. The first item will have one unit of space
+ against the container edge, but two units of space between the next item
+ because that next item has its own spacing that applies.
+ - `space-evenly` - items are distributed so that the spacing between any two
+ items (and the space to the edges) is equal.
+ - TBD (not all properties are supported in IE11).
+
+### `Flex.Item`
+
+Props:
+
+- See inherited props: [Box](#box)
+- `order: number` - By default, flex items are laid out in the source order.
+However, the order property controls the order in which they appear in the
+flex container.
+- `grow: number` - This defines the ability for a flex item to grow if
+necessary. It accepts a unitless value that serves as a proportion. It
+dictates what amount of the available space inside the flex container the
+item should take up. This number is unit-less and is relative to other
+siblings.
+- `shrink: number` - This defines the ability for a flex item to shrink
+if necessary. Inverse of `grow`.
+- `basis: string` - This defines the default size of an element before the
+remaining space is distributed. It can be a length (e.g. `20%`, `5rem`, etc.),
+an `auto` or `content` keyword.
+- `align: string` - This allows the default alignment (or the one specified by align-items) to be overridden for individual flex items. See: [Flex](#flex).
+
+
+### `Grid`
+
+Helps you to divide horizontal space into two or more equal sections.
+It is essentially a single-row `Table`, but with some extra features.
+
+Example:
+
+```jsx
+
+
+
+
+
+
+
+
+```
+
+Props:
+
+- See inherited props: [Table](#table)
+
+### `Grid.Column`
+
+Props:
+
+- See inherited props: [Table.Cell](#tablecell)
+- `size: number` (default: 1) - Size of the column relative to other columns.
+
+### `Icon`
+
+Renders one of the FontAwesome icons of your choice.
+
+```jsx
+
+```
+
+To smoothen the transition from v4 to v5, we have added a v4 semantic to
+transform names with `-o` suffixes to FA Regular icons. For example:
+- `square` will get transformed to `fas square`
+- `square-o` will get transformed to `far square`
+
+Props:
+
+- See inherited props: [Box](#box)
+- `name: string` - Icon name.
+- `size: number` - Icon size. `1` is normal size, `2` is two times bigger.
+Fractional numbers are supported.
+- `rotation: number` - Icon rotation, in degrees.
+- `spin: boolean` - Whether an icon should be spinning. Good for load
+indicators.
+
+### `Input`
+
+A basic text input, which allow users to enter text into a UI.
+
+> Input does not support custom font size and height due to the way
+> it's implemented in CSS. Eventually, this needs to be fixed.
+
+Props:
+
+- See inherited props: [Box](#box)
+- `value: string` - Value of an input.
+- `placeholder: string` - Text placed into Input box when value is otherwise nothing. Clears automatically when focused.
+- `fluid: boolean` - Fill all available horizontal space.
+- `selfClear: boolean` - Clear after hitting enter, as well as remain focused when this happens. Useful for things like chat inputs
+- `onChange: (e, value) => void` - An event, which fires when you commit
+the text by either unfocusing the input box, or by pressing the Enter key.
+- `onInput: (e, value) => void` - An event, which fires on every keypress.
+
+### `LabeledList`
+
+LabeledList is a continuous, vertical list of text and other content, where
+every item is labeled. It works just like a two column table, where first
+column is labels, and second column is content.
+
+```jsx
+
+
+ Content
+
+
+```
+
+If you want to have a button on the right side of an item (for example,
+to perform some sort of action), there is a way to do that:
+
+```jsx
+
+
+ )}>
+ Content
+
+
+```
+
+Props:
+
+- `children: LabeledList.Item` - Items to render.
+
+### `LabeledList.Item`
+
+Props:
+
+- `label: string` - Item label.
+- `color: string` - Sets the color of the text.
+- `buttons: any` - Buttons to render aside the content.
+- `content/children: any` - Content of this labeled item.
+
+### `LabeledList.Divider`
+
+Adds some empty space between LabeledList items.
+
+Example:
+
+```jsx
+
+
+ Content
+
+
+
+```
+
+Props:
+
+- `size: number` - Size of the divider.
+
+### `NoticeBox`
+
+A notice box, which warns you about something very important.
+
+Props:
+
+- See inherited props: [Box](#box)
+
+### `NumberInput`
+
+A fancy, interactive number input, which you can either drag up and down
+to fine tune the value, or single click it to manually type a number.
+
+Props:
+
+- `animated: boolean` - Animates the value if it was changed externally.
+- `fluid: boolean` - Fill all available horizontal space.
+- `value: number` - Value itself.
+- `unit: string` - Unit to display to the right of value.
+- `minValue: number` - Lowest possible value.
+- `maxValue: number` - Highest possible value.
+- `step: number` (default: 1) - Adjust value by this amount when
+dragging the input.
+- `stepPixelSize: number` (default: 1) - Screen distance mouse needs
+to travel to adjust value by one `step`.
+- `width: string|number` - Width of the element, in `Box` units or pixels.
+- `height: string|numer` - Height of the element, in `Box` units or pixels.
+- `lineHeight: string|number` - lineHeight of the element, in `Box` units or pixels.
+- `fontSize: string|number` - fontSize of the element, in `Box` units or pixels.
+- `format: value => value` - Format value using this function before
+displaying it.
+- `suppressFlicker: number` - A number in milliseconds, for which the input
+will hold off from updating while events propagate through the backend.
+Default is about 250ms, increase it if you still see flickering.
+- `onChange: (e, value) => void` - An event, which fires when you release
+the input, or successfully enter a number.
+- `onDrag: (e, value) => void` - An event, which fires about every 500ms
+when you drag the input up and down, on release and on manual editing.
+
+### `ProgressBar`
+
+Progress indicators inform users about the status of ongoing processes.
+
+```jsx
+
+```
+
+Usage of `ranges` prop:
+
+```jsx
+
+```
+
+Props:
+
+- `value: number` - Current progress as a floating point number between
+`minValue` (default: 0) and `maxValue` (default: 1). Determines the
+percentage and how filled the bar is.
+- `minValue: number` - Lowest possible value.
+- `maxValue: number` - Highest possible value.
+- `ranges: { color: [from, to] }` - Applies a `color` to the progress bar
+based on whether the value lands in the range between `from` and `to`.
+- `color: string` - Color of the progress bar.
+- `content/children: any` - Content to render inside the progress bar.
+
+### `Section`
+
+Section is a surface that displays content and actions on a single topic.
+
+They should be easy to scan for relevant and actionable information.
+Elements, like text and images, should be placed in them in a way that
+clearly indicates hierarchy.
+
+Section can also be titled to clearly define its purpose.
+
+```jsx
+
+ Here you can order supply crates.
+
+```
+
+If you want to have a button on the right side of an section title
+(for example, to perform some sort of action), there is a way to do that:
+
+```jsx
+
+ )}>
+ Here you can order supply crates.
+
+```
+
+- See inherited props: [Box](#box)
+- `title: string` - Title of the section.
+- `level: number` - Section level in hierarchy. Default is 1, higher number
+means deeper level of nesting. Must be an integer number.
+- `buttons: any` - Buttons to render aside the section title.
+- `content/children: any` - Content of this section.
+
+### `Table`
+
+A straight forward mapping to a standard html table, which is slightly
+simplified (does not need a `` tag) and with sane default styles
+(e.g. table width is 100% by default).
+
+Example:
+
+```jsx
+
+
+
+ Hello world!
+
+
+ Label
+
+
+
+```
+
+Props:
+
+- See inherited props: [Box](#box)
+- `collapsing: boolean` - Collapses table to the smallest possible size.
+
+### `Table.Row`
+
+A straight forward mapping to `
` element.
+
+Props:
+
+- See inherited props: [Box](#box)
+
+### `Table.Cell`
+
+A straight forward mapping to `
` element.
+
+Props:
+
+- See inherited props: [Box](#box)
+- `collapsing: boolean` - Collapses table cell to the smallest possible size,
+and stops any text inside from wrapping.
+
+### `Tabs`
+
+Tabs make it easy to explore and switch between different views.
+
+Here is an example of how you would construct a simple tabbed view:
+
+```jsx
+
+
+ Content for Item one.
+
+
+ Content for Item two.
+
+
+```
+
+This is a rather simple example. In the real world, you might be
+constructing very complex tabbed views which can tax UI performance.
+This is because your tabs are being rendered regardless of their
+visibility status!
+
+There is a simple fix however. Tabs accept functions as children, which
+will be called to retrieve content only when the tab is visible:
+
+```jsx
+
+
+ {() => (
+
+ Content for Item one.
+
+ )}
+
+
+ {() => (
+
+ Content for Item two.
+
+ )}
+
+
+```
+
+You might not always need this, but it is highly recommended to always
+use this method. Notice the `key` prop on tabs - it uniquely identifies
+the tab and is used for determining which tab is currently active. It can
+be either explicitly provided as a `key` prop, or if omitted, it will be
+implicitly derived from the tab's `label` prop.
+
+Props:
+
+- `vertical: boolean` - Use a vertical configuration, where tabs will appear
+stacked on the left side of the container.
+- `altSelection` - Whether the tab buttons select via standard select (color change) or by adding a white indicator to the selected tab.
+ Intended for usage on interfaces where tab color has relevance.
+- `children: Tab[]` - This component only accepts tabs as its children.
+
+### `Tabs.Tab`
+
+An individual tab element. Tabs function like buttons, so they inherit
+a lot of `Button` props.
+
+Props:
+
+- See inherited props: [Button](#button)
+- `key: string` - A unique identifier for the tab.
+- `label: string` - Tab label.
+- `icon: string` - Tab icon.
+- `content/children: any` - Content to render inside the tab.
+- `onClick: function` - Called when element is clicked.
+
+### `Tooltip`
+
+A boxy tooltip from tgui 1. It is very hacky in its current state, and
+requires setting `position: relative` on the container.
+
+Please note, that [Button](#button) component has a `tooltip` prop, and
+it is recommended to use that prop instead.
+
+Usage:
+
+```jsx
+
+ Sample text.
+
+
+```
+
+Props:
+
+- `position: string` - Tooltip position.
+- `content/children: string` - Content of the tooltip. Must be a plain string.
+Fragments or other elements are **not** supported.
diff --git a/tgui-next/bin/tgui b/tgui-next/bin/tgui
new file mode 100755
index 0000000000..401d67b622
--- /dev/null
+++ b/tgui-next/bin/tgui
@@ -0,0 +1,159 @@
+#!/bin/bash
+set -e
+shopt -s globstar
+shopt -s expand_aliases
+
+## Initial set-up
+## --------------------------------------------------------
+
+## Returns an absolute path to file
+alias tgui-realpath="readlink -f"
+
+## Fallbacks for GNU readlink
+## Detecting GNU coreutils http://stackoverflow.com/a/8748344/319952
+if ! readlink --version >/dev/null 2>&1; then
+ if hash greadlink 2>/dev/null; then
+ alias tgui-realpath="greadlink -f"
+ else
+ alias tgui-realpath="perl -MCwd -le 'print Cwd::abs_path(shift)'"
+ fi
+fi
+
+## Find a canonical path to project root
+base_dir="$(dirname "$(tgui-realpath "${0}")")/.."
+base_dir="$(tgui-realpath "${base_dir}")"
+
+## Add locally installed node programs to path
+PATH="${PATH}:node_modules/.bin"
+
+
+## Functions
+## --------------------------------------------------------
+
+## Installs node modules
+task-install() {
+ cd "${base_dir}"
+ yarn install
+}
+
+## Runs webpack
+task-webpack() {
+ cd "${base_dir}/packages/tgui"
+ webpack "${@}"
+}
+
+## Runs a development server
+task-dev-server() {
+ cd "${base_dir}/packages/tgui-dev-server"
+ exec node --experimental-modules index.js "${@}"
+}
+
+## Run a linter through all packages
+task-eslint() {
+ cd "${base_dir}"
+ eslint ./packages "${@}"
+}
+
+## Mr. Proper
+task-clean() {
+ cd "${base_dir}"
+ rm -rf packages/tgui/public/.tmp
+ rm -rf **/node_modules
+ rm -f **/package-lock.json
+}
+
+## Installs merge drivers and git hooks
+task-install-git-hooks() {
+ cd "${base_dir}"
+ local git_root
+ local git_base_dir
+ git_root="$(git rev-parse --show-toplevel)"
+ git_base_dir="${base_dir/${git_root}/.}"
+ git config --replace-all merge.tgui-merge-bundle.driver \
+ "${git_base_dir}/bin/tgui --merge=bundle %O %A %B %L"
+ echo "tgui: Merge drivers have been successfully installed!"
+}
+
+## Bundle merge driver
+task-merge-bundle() {
+ local file_ancestor="${1}"
+ local file_current="${2}"
+ local file_other="${3}"
+ local conflict_marker_size="${4}"
+ echo "tgui: Discarding a local tgui build"
+ ## Do nothing (file_current will be merged and is what we want to keep).
+ exit 0
+}
+
+
+## Main
+## --------------------------------------------------------
+
+if [[ ${1} == "--merge"* ]]; then
+ if [[ ${1} == "--merge=bundle" ]]; then
+ shift 1
+ task-merge-bundle "${@}"
+ fi
+ echo "Unknown merge strategy: ${1}"
+ exit 1
+fi
+
+if [[ ${1} == "--install-git-hooks" ]]; then
+ shift 1
+ task-install-git-hooks
+ exit 0
+fi
+
+## Continuous integration scenario
+if [[ ${1} == "--ci" ]]; then
+ task-clean
+ task-install
+ task-eslint
+ task-webpack --mode=production
+ exit 0
+fi
+
+if [[ ${1} == "--clean" ]]; then
+ task-clean
+ exit 0
+fi
+
+if [[ ${1} == "--dev" ]]; then
+ shift
+ task-install
+ task-dev-server "${@}"
+ exit 0
+fi
+
+if [[ ${1} == '--lint' ]]; then
+ shift 1
+ task-install
+ task-eslint "${@}"
+ exit 0
+fi
+
+if [[ ${1} == '--lint-harder' ]]; then
+ shift 1
+ task-install
+ task-eslint -c .eslintrc-harder.yml "${@}"
+ exit 0
+fi
+
+## Analyze the bundle
+if [[ ${1} == '--analyze' ]]; then
+ task-install
+ task-webpack --mode=production --analyze
+ exit 0
+fi
+
+## Make a production webpack build
+if [[ -z ${1} ]]; then
+ task-install
+ task-eslint
+ task-webpack --mode=production
+ exit 0
+fi
+
+## Run webpack with custom flags
+task-install
+task-webpack "${@}"
diff --git a/tgui-next/bin/tgui-build.bat b/tgui-next/bin/tgui-build.bat
new file mode 100644
index 0000000000..89e1aca915
--- /dev/null
+++ b/tgui-next/bin/tgui-build.bat
@@ -0,0 +1,5 @@
+@echo off
+cd "%~dp0\.."
+call yarn install
+call yarn run build
+timeout /t 9
diff --git a/tgui-next/bin/tgui-dev-server.bat b/tgui-next/bin/tgui-dev-server.bat
new file mode 100644
index 0000000000..1b5bdcfb1d
--- /dev/null
+++ b/tgui-next/bin/tgui-dev-server.bat
@@ -0,0 +1,4 @@
+@echo off
+cd "%~dp0\.."
+call yarn install
+call yarn run watch
diff --git a/tgui-next/docs/converting-old-tgui-interfaces.md b/tgui-next/docs/converting-old-tgui-interfaces.md
new file mode 100644
index 0000000000..c92cefca5e
--- /dev/null
+++ b/tgui-next/docs/converting-old-tgui-interfaces.md
@@ -0,0 +1,322 @@
+# Converting old tgui interfaces to tgui-next
+
+This guide is going to assume you already know roughly how tgui-next works, how to make new uis, etc. It's mostly aimed at helping translate concepts between tgui and tgui-next, and clarify some confusing parts of the transition.
+
+## Backend
+
+Backend in almost every case does not require any changes. In particularly heavy ui cases, something to be aware of is the new `ui_static_data()` proc. This proc allows you to split some data sent to the interface off into data that will only be sent on ui initialize and when manually updated by elsewhere in the code. Useful for things like cargo where you have a very large set of mostly identical code.
+
+Keep in mind that for uis where *all* data doesn't need to be live updating, you can just toggle off autoupdate for the ui instead of messing with static data.
+
+## Frontend
+
+The very first thing to note is the name of the `ract` file containing the old interface. Whatever the name is (minus the extension) is going to be what the route key is going to be.
+
+One thing I like to do before starting work on a conversion is screenshot what the old interface looks like so I have something to reference to make sure that the styling can line up as well.
+
+## General syntax changes
+
+Ractive has a fairly different templating syntax from React.
+
+### `data`
+
+You likely already know that React data inserts look like this
+
+```jsx
+{data.example_data}
+```
+
+Ractive looks very similar, the only real difference is that React uses one paranthesis instead of two.
+
+```ractive
+{{data.example_data}}
+```
+
+However, you may occasionally come across data inserts that instead of referencing the `data` var or things contained within it instead reference `adata`. `adata` was short for animated data, and was used for smooth number animations in interfaces. instead of having a seperate data structure for this. tgui-next instead uses a component, which is `AnimatedNumber`.
+
+`AnimatedNumber` is used like this
+
+```jsx
+
+```
+
+Make sure you don't forget to import it.
+
+### Conditionals
+
+Ractive conditionals look very different from React conditionals.
+
+A ractive `if` (only render if result of expression is true) looks like this
+
+```ractive
+{{#if data.condition}}
+ Example Render
+{{/if}}
+```
+
+The equivalent React would be
+
+```jsx
+{!!data.condition && (
+ Example Render
+)}
+```
+
+This might look a bit intimidating compared to the reactive part but it's not as complicated as it seems:
+
+1. A new jsx context is opened with `{}`
+2. jsx contexts like this always render whatever the return value is, so we can use `&&` to return a value we want. `&&` returns the last true value (or not "falsey" because this is js).
+3. jsx tags are never "falsey", so a conditioned paired with a jsx tag will mean the condition being true will continue on and return the tag. `()` is just used to contain the tag
+4. The `!!` is not a special operator, it is a literal double negation. This is because most `false` values coming from byond are going to actually be `0`, which would be rendered if the condition is false. Negating `0` returns `true`, negating `true` returns `false`, which isn't rendered.
+5. `Fragment` is actually a true "dead tag". It's similar to `span` in that it just contains things without providing functionality, but it's unwrapped before the final render and children of it are injected into its parent. In a case where you only need to render text without any styling, it's probably better to just return a string literal (`"Example Render"`), but this was just to illustrate that you can put any tag in this expression.
+
+You don't really need to know all this to understand how to use it, but I find it helps with understanding when things go wrong.
+
+Ractive conditionals can have an `else` as well
+```ractive
+{{#if data.condition}}
+ value
+{{else}}
+ other value
+{{/if}}
+```
+
+Similarly to the previous example, just add a `||` operator to handle the
+"falsy" condition:
+
+```jsx
+{!!data.condition && (
+ value
+) || (
+ other value
+)}
+```
+
+There's also our good old friend - the ternary:
+
+```jsx
+{data.condition ? 'value' : 'other value'}
+```
+
+Keep in mind you can also use tags here like the conditional example,
+and you can mix string literals, values, and tags as well.
+
+```jsx
+{data.is_robot ? (
+
+) : 'Not a robot'}
+```
+
+### Loops
+
+Ractive has loops for iterating over data and inserting something for each
+member of an array or object
+
+```
+{{#each data.list_of_foo}}
+ foo {{number}} is here.
+{{/each}}
+```
+
+This didn't care whether the data was an array or an object, and members of each entry of the loop were "unwrapped" so to say. `{{number}}` in that example is referring to the `{{number}}` value on the entry of the list for that iterate.
+
+The React equivalent to this is going to be `map`.
+
+_AN IMPORTANT DISTINCTION HERE IS THAT NOW WE CARE WHETHER THIS IS AN OBJECT OR AN ARRAY BEING ACTED ON._
+
+Objects are represented by `{}`, arrays by `[]`
+
+"How can I tell?" you may ask. It's fairly simple, associated lists on the byond side are going to be turned into objects when they get json converted, normal lists are going to be turned into arrays.
+
+`list("bla", "blo")` would become `["bla", "blo"]` and `list("foo" = 1, "bar" = 2)` would become `{"foo": 1, "bar": 2}`
+
+First things first, above the `return` of the function you're making the interface in, you're going to want to add something like this
+```jsx
+const things = data.things || [];
+```
+
+This ensures that you'll never be reading a null entry by mistake. Substitute `{}` for objects as appropriate.
+
+If it's an array, you'll want to do this in the template
+```jsx
+{things.map(thing => (
+ Thing {thing.number} is here!
+))}
+```
+
+`map` is a function that calls a passed function (a lambda) on each entry, and returns the value. You should already know that returned tags and values (except `false`) get rendered, so that's how it's rendering each time.
+
+A lambda is what's known as an anonymous function, it's a function that doesn't have a name that's only used for a specific usage. `map` wants a function that has one parameter, so we define one parameter then use `=>` to say the parameter has to do with the following block.
+
+`parameter => ()` is just a shorthand for `parameter => {return();}`
+
+This is quite a bit higher concept than ractive's each statements, so feel free to look around and ~~copy paste~~ learn from how other interfaces use this.
+
+Now for objects, there's a genuinely pretty gross syntax here. We apoligize, it's related to ie8 compatibility nonsense.
+
+```jsx
+{map((value, key) => {
+ return (
+ Key is {key}, value is {value}
+ );
+})(fooObject)}
+```
+
+Again, sorry for this syntax. `fooObject` would be the object being iterated on, value would be the value of the iterated entry on the list, and key would be the key. the naming of value and key isn't important here, but knowing that it goes `value`, `key` in that order is important.
+
+It is sometimes better to preemptively convert an object to array before
+the big return statement, like this:
+
+```jsx
+const fooArray = map((value, key) => {
+ return { key, value };
+})(fooObject);
+```
+
+Or if you just want to discard all keys, this will also work nicely:
+
+```jsx
+const fooArray = toArray(fooObject);
+```
+
+Also occasionally you'd see an else:
+
+```
+{{#each data.potentially_empty_list}}
+ Thing "{{name}}" is in this list!
+{{else}}
+ None found!
+{{/each}}
+```
+
+This would iterate using the first contents each time, or display the second option if the list was empty.
+
+To do a similar thing in JSX, just check if array is empty like this:
+
+```jsx
+{fooArray.length === 0 && 'fooArray is empty.'}
+{fooArray.map(foo => (
+ Foo is {foo}
+))}
+```
+
+### Extra Stuff
+
+I'll put some extra stuff here when I think of it.
+
+## Components
+
+This will be a reference of tgui components and the tgui-next equivalent.
+
+### `ui-display`
+
+Equivalent of `` is ``
+
+```
+
+ Contents
+
+```
+
+becomes
+
+```jsx
+
+ Contents
+
+```
+
+A feature sometimes used is if `ui-display` has the `button` property, it will contain a `partial` command. This becomes the `buttons` property on `Section`:
+
+```
+
+ {{#partial button}}
+ // lots more button bullshit here
+ {{/partial}}
+ Contents
+
+```
+
+becomes
+
+```jsx
+
+ )}>
+ Contents
+
+```
+
+### `ui-section`
+
+Very important to note `ui-section` is NOT the equivalent of `Section`
+
+`` does not have a direct equivalent, but the closest equivalent is ``
+
+```
+
+ No Power
+
+
+ No Connection
+
+```
+
+becomes
+
+```jsx
+
+
+ No Power
+
+
+ No Connection
+
+
+```
+
+Important to note that `LabeledList.Item` has `buttons` as well.
+
+Also good to know that if you need the contents of a `LabeledList.Item` to be colored, you can just set the `color` prop on it instead of putting a `span` inside it.
+
+### `ui-notice`
+
+`` has a direct equivalent in ``
+
+```
+
+ Notice stuff!
+
+```
+
+becomes
+
+```jsx
+
+ Notice stuff!
+
+```
+
+### `ui-button`
+
+The equivalent of `ui-button` is `Button` but it works quite a bit differently.
+
+```
+
+ Click
+
+```
+
+becomes
+
+```
+