[alias]"
- temp_html += "
[mut_desc]
"
- if(active && !storage_slot)
- if(HM?.can_chromosome && (HM in viable_occupant.dna.mutations))
- var/i = viable_occupant.dna.mutations.Find(HM)
- var/chromosome_name = "
----"
- if(HM.chromosome_name)
- chromosome_name = HM.chromosome_name
- temp_html += "
Chromosome status: [chromosome_name]
"
- temp_html += "
Sequence:
"
- if(!scrambled)
- for(var/block in 1 to A.blocks)
- var/whole_sequence = get_valid_gene_string(mutation)
- var/sequence = copytext(whole_sequence, 1+(block-1)*(DNA_SEQUENCE_LENGTH*2),(DNA_SEQUENCE_LENGTH*2*block+1))
- temp_html += "
"
- for(var/i in 1 to DNA_SEQUENCE_LENGTH)
- var/num = 1+(i-1)*2
- var/genenum = num+(DNA_SEQUENCE_LENGTH*2*(block-1))
- temp_html += " | "
- temp_html += "
"
- for(var/i in 1 to DNA_SEQUENCE_LENGTH)
- temp_html += "| | "
- temp_html += "
"
- for(var/i in 1 to DNA_SEQUENCE_LENGTH)
- var/num = i*2
- var/genenum = num+(DNA_SEQUENCE_LENGTH*2*(block-1))
- temp_html += " | "
- temp_html += "
"
- temp_html += "
"
- else
- temp_html = "
Sequence unreadable due to unpredictable mutation.
"
- if((active || storage_slot) && (injectorready < world.time) && !scrambled)
- temp_html += "
Print Activator"
- temp_html += "
Print Mutator"
- else
- temp_html += "
Print Activator"
- temp_html += "
Print Mutator"
- temp_html += "
"
- if(storage_slot)
- temp_html += "
Delete"
- if((LAZYLEN(stored_mutations) < max_storage) && diskette && !diskette.read_only)
- temp_html += "
Export"
- else
- temp_html += "
Export"
- temp_html += "
Back"
- else if(active && !scrambled)
- temp_html += "
Store"
- temp_html += "
Adv. Injector"
- if(extra || scrambled)
- temp_html += "
Nullify"
- else
- temp_html += "
Nullify"
- temp_html += "
"
- return temp_html
-
-/obj/machinery/computer/scan_consolenew/Topic(href, href_list)
+/obj/machinery/computer/scan_consolenew/ui_act(action, var/list/params)
if(..())
- return
- if(!isturf(usr.loc))
- return
- if(!((isturf(loc) && in_range(src, usr)) || hasSiliconAccessInArea(usr)))
- return
- if(current_screen == "working")
- return
+ return TRUE
+
+ . = TRUE
add_fingerprint(usr)
usr.set_machine(src)
- var/mob/living/carbon/viable_occupant = get_viable_occupant()
+ switch(action)
+ // Connect this DNA Console to a nearby DNA Scanner
+ // Usually only activate as an option if there is no connected scanner
+ if("connect_scanner")
+ connect_to_scanner()
+ return
- //Basic Tasks///////////////////////////////////////////
- var/num = round(text2num(href_list["num"]))
- var/last_change
- switch(href_list["task"])
- if("togglelock")
- if(connected)
- connected.locked = !connected.locked
- if("toggleopen")
- if(connected)
- connected.toggle_open(usr)
- if("setduration")
- if(!num)
- num = round(input(usr, "Choose pulse duration:", "Input an Integer", null) as num|null)
- if(num)
- radduration = WRAP(num, 1, RADIATION_DURATION_MAX+1)
- if("setstrength")
- if(!num)
- num = round(input(usr, "Choose pulse strength:", "Input an Integer", null) as num|null)
- if(num)
- radstrength = WRAP(num, 1, RADIATION_STRENGTH_MAX+1)
- if("screen")
- current_screen = href_list["text"]
- if("scramble")
- if(viable_occupant && (scrambleready < world.time))
- viable_occupant.dna.remove_all_mutations(list(MUT_NORMAL, MUT_EXTRA))
- viable_occupant.dna.generate_dna_blocks()
- scrambleready = world.time + SCRAMBLE_TIMEOUT
- to_chat(usr,"
DNA scrambled.")
- viable_occupant.radiation += RADIATION_STRENGTH_MULTIPLIER*50/(connected.damage_coeff ** 2)
- if("setbufferlabel")
- var/text = sanitize(input(usr, "Input a new label:", "Input a Text", null) as text|null)
- if(num && text)
- num = clamp(num, 1, NUMBER_OF_BUFFERS)
- var/list/buffer_slot = buffer[num]
- if(istype(buffer_slot))
- buffer_slot["label"] = text
- if("setbuffer")
- if(num && viable_occupant)
- num = clamp(num, 1, NUMBER_OF_BUFFERS)
- buffer[num] = list(
- "label"="Buffer[num]:[viable_occupant.real_name]",
- "UI"=viable_occupant.dna.uni_identity,
- "UE"=viable_occupant.dna.unique_enzymes,
- "name"=viable_occupant.real_name,
- "blood_type"=viable_occupant.dna.blood_type
- )
- if("clearbuffer")
- if(num)
- num = clamp(num, 1, NUMBER_OF_BUFFERS)
- var/list/buffer_slot = buffer[num]
- if(istype(buffer_slot))
- buffer_slot.Cut()
- if("transferbuffer")
- if(num && viable_occupant)
- switch(href_list["text"]) //Numbers are this high because other way upgrading laser is just not worth the hassle, and i cant think of anything better to inmrove
- if("ui")
- apply_buffer(SCANNER_ACTION_UI,num)
- if("ue")
- apply_buffer(SCANNER_ACTION_UE,num)
- if("mixed")
- apply_buffer(SCANNER_ACTION_MIXED,num)
- if("injector")
- if(num && injectorready < world.time)
- num = clamp(num, 1, NUMBER_OF_BUFFERS)
- var/list/buffer_slot = buffer[num]
- if(istype(buffer_slot))
- var/obj/item/dnainjector/timed/I
- switch(href_list["text"])
- if("ui")
- if(buffer_slot["UI"])
- I = new /obj/item/dnainjector/timed(loc)
- I.fields = list("UI"=buffer_slot["UI"])
- if(connected)
- I.damage_coeff = connected.damage_coeff
- if("ue")
- if(buffer_slot["name"] && buffer_slot["UE"] && buffer_slot["blood_type"])
- I = new /obj/item/dnainjector/timed(loc)
- I.fields = list("name"=buffer_slot["name"], "UE"=buffer_slot["UE"], "blood_type"=buffer_slot["blood_type"])
- if(connected)
- I.damage_coeff = connected.damage_coeff
- if("mixed")
- if(buffer_slot["UI"] && buffer_slot["name"] && buffer_slot["UE"] && buffer_slot["blood_type"])
- I = new /obj/item/dnainjector/timed(loc)
- I.fields = list("UI"=buffer_slot["UI"],"name"=buffer_slot["name"], "UE"=buffer_slot["UE"], "blood_type"=buffer_slot["blood_type"])
- if(connected)
- I.damage_coeff = connected.damage_coeff
- if(I)
- injectorready = world.time + INJECTOR_TIMEOUT
- if("loaddisk")
- if(num && diskette && diskette.fields)
- num = clamp(num, 1, NUMBER_OF_BUFFERS)
- buffer[num] = diskette.fields.Copy()
- if("savedisk")
- if(num && diskette && !diskette.read_only)
- num = clamp(num, 1, NUMBER_OF_BUFFERS)
- var/list/buffer_slot = buffer[num]
- if(istype(buffer_slot))
- diskette.name = "data disk \[[buffer_slot["label"]]\]"
- diskette.fields = buffer_slot.Copy()
- if("ejectdisk")
- if(diskette)
- diskette.forceMove(drop_location())
- diskette = null
- if("setdelayed")
- if(num)
- delayed_action = list("action"=text2num(href_list["delayaction"]),"buffer"=num)
- if("pulseui")
- if(num && viable_occupant && connected)
- radduration = WRAP(radduration, 1, RADIATION_DURATION_MAX+1)
- radstrength = WRAP(radstrength, 1, RADIATION_STRENGTH_MAX+1)
+ // Toggle the door open/closed status on attached DNA Scanner
+ if("toggle_door")
+ // GUARD CHECK - Scanner still connected and operational?
+ if(!scanner_operational())
+ return
- var/locked_state = connected.locked
- connected.locked = TRUE
+ connected_scanner.toggle_open(usr)
+ return
- current_screen = "working"
- ui_interact(usr)
+ // Toggle the door bolts on the attached DNA Scanner
+ if("toggle_lock")
+ // GUARD CHECK - Scanner still connected and operational?
+ if(!scanner_operational())
+ return
- sleep(radduration*10)
- current_screen = "ui"
+ connected_scanner.locked = !connected_scanner.locked
+ return
- if(viable_occupant && connected && connected.occupant==viable_occupant)
- viable_occupant.radiation += (RADIATION_IRRADIATION_MULTIPLIER*radduration*radstrength)/(connected.damage_coeff ** 2) //Read comment in "transferbuffer" section above for explanation
- switch(href_list["task"]) //Same thing as there but values are even lower, on best part they are about 0.0*, effectively no damage
- if("pulseui")
- var/len = length_char(viable_occupant.dna.uni_identity)
- num = WRAP(num, 1, len+1)
- num = randomize_radiation_accuracy(num, radduration + (connected.precision_coeff ** 2), len) //Each manipulator level above 1 makes randomization as accurate as selected time + manipulator lvl^2
- //Value is this high for the same reason as with laser - not worth the hassle of upgrading if the bonus is low
- var/block = round((num-1)/DNA_BLOCK_SIZE)+1
- var/subblock = num - block*DNA_BLOCK_SIZE
- last_change = "UI #[block]-[subblock]; "
+ // Scramble scanner occupant's DNA
+ if("scramble_dna")
+ // GUARD CHECK - Can we genetically modify the occupant? Includes scanner
+ // operational guard checks.
+ // GUARD CHECK - Is scramble DNA actually ready?
+ if(!can_modify_occupant() || !(scrambleready < world.time))
+ return
- var/hex = copytext_char(viable_occupant.dna.uni_identity, num, num+1)
- last_change += "[hex]"
- hex = scramble(hex, radstrength, radduration)
- last_change += "->[hex]"
+ scanner_occupant.dna.remove_all_mutations(list(MUT_NORMAL, MUT_EXTRA))
+ scanner_occupant.dna.generate_dna_blocks()
+ scrambleready = world.time + SCRAMBLE_TIMEOUT
+ to_chat(usr,"
DNA scrambled.")
+ scanner_occupant.radiation += RADIATION_STRENGTH_MULTIPLIER*50/(connected_scanner.damage_coeff ** 2)
+ return
- viable_occupant.dna.uni_identity = copytext_char(viable_occupant.dna.uni_identity, 1, num) + hex + copytext_char(viable_occupant.dna.uni_identity, num + 1)
- viable_occupant.updateappearance(mutations_overlay_update=1)
+ // Check whether a specific mutation is eligible for discovery within the
+ // scanner occupant
+ // This is additionally done when a mutation's tab is selected in the tgui
+ // interface. This is because some mutations, such as Monkified on monkeys,
+ // are infact completed by default but not yet discovered. Likewise, all
+ // mutations can have their sequence completed while Monkified is still an
+ // active mutation and thus won't immediately be discovered but could be
+ // discovered when Monkified is removed
+ // ---------------------------------------------------------------------- //
+ // params["alias"] - Alias of a mutation. The alias is the "hidden" name of
+ // the mutation, for example "Mutation 5" or "Mutation 33"
+ if("check_discovery")
+ // GUARD CHECK - Can we genetically modify the occupant? Includes scanner
+ // operational guard checks.
+ if(!can_modify_occupant())
+ return
+
+ // GUARD CHECK - Have we somehow cheekily swapped occupants? This is
+ // unexpected.
+ if(!(scanner_occupant == connected_scanner.occupant))
+ return
+
+ check_discovery(params["alias"])
+ return
+
+ // Check all mutations of the occupant and check if any are discovered.
+ // This is called when the Genetic Sequencer is selected. It'll do things
+ // like immediately discover Monkified without needing to click through
+ // the mutation tabs and handle cases where mutations are solved but not
+ // discovered due to the Monkified mutation being active then removed.
+ if("all_check_discovery")
+ // GUARD CHECK - Can we genetically modify the occupant? Includes scanner
+ // operational guard checks.
+ if(!can_modify_occupant())
+ return
+
+ // GUARD CHECK - Have we somehow cheekily swapped occupants? This is
+ // unexpected.
+ if(!(scanner_occupant == connected_scanner.occupant))
+ return
+
+ // Go over all standard mutations and check if they've been discovered.
+ for(var/mutation_type in scanner_occupant.dna.mutation_index)
+ var/datum/mutation/human/HM = GET_INITIALIZED_MUTATION(mutation_type)
+ check_discovery(HM.alias)
+
+ return
+
+ // Set a gene in a mutation's genetic sequence. Will also check for mutations
+ // discovery as part of the process.
+ // ---------------------------------------------------------------------- //
+ // params["alias"] - Alias of a mutation. The alias is the "hidden" name of
+ // the mutation, for example "Mutation 5" or "Mutation 33"
+ // params["gene"] - The letter of the new gene
+ // params["pos"] - The BYOND index of the letter in the gene sequence to be
+ // changed. Expects a text string from TGUI and will convert to a number
+ if("pulse_gene")
+ // GUARD CHECK - Can we genetically modify the occupant? Includes scanner
+ // operational guard checks.
+ if(!can_modify_occupant())
+ return
+
+ // GUARD CHECK - Have we somehow cheekily swapped occupants? This is
+ // unexpected.
+ if(!(scanner_occupant == connected_scanner.occupant))
+ return
+
+ // GUARD CHECK - Is the occupant currently undergoing some form of
+ // transformation? If so, we don't want to be pulsing genes.
+ if(scanner_occupant.transformation_timer)
+ to_chat(usr,"
Gene pulse failed: The scanner occupant undergoing a transformation.")
+ return
+
+ // Resolve mutation's BYOND path from the alias
+ var/alias = params["alias"]
+ var/path = GET_MUTATION_TYPE_FROM_ALIAS(alias)
+ // Make sure the occupant still has this mutation
+ if(!(path in scanner_occupant.dna.mutation_index))
+ return
+
+ // Resolve BYOND path to genome sequence of scanner occupant
+ var/sequence = GET_GENE_STRING(path, scanner_occupant.dna)
+
+ var/newgene = params["gene"]
+ var/genepos = text2num(params["pos"])
+
+ // If the new gene is J, this means we're dealing with a JOKER
+ // GUARD CHECK - Is JOKER actually ready?
+ if((newgene == "J") && (jokerready < world.time))
+ var/truegenes = GET_SEQUENCE(path)
+ newgene = truegenes[genepos]
+ jokerready = world.time + JOKER_TIMEOUT - (JOKER_UPGRADE * (connected_scanner.precision_coeff-1))
+
+ // If the gene is an X, we want to update the default genes with the new
+ // X to allow highlighting logic to work on the tgui interface.
+ if(newgene == "X")
+ var/defaultseq = scanner_occupant.dna.default_mutation_genes[path]
+ defaultseq = copytext_char(defaultseq, 1, genepos) + newgene + copytext_char(defaultseq, genepos + 1)
+ scanner_occupant.dna.default_mutation_genes[path] = defaultseq
+
+ // Copy genome to scanner occupant and do some basic mutation checks as
+ // we've increased the occupant rads
+ sequence = copytext_char(sequence, 1, genepos) + newgene + copytext_char(sequence, genepos + 1)
+ scanner_occupant.dna.mutation_index[path] = sequence
+ scanner_occupant.radiation += RADIATION_STRENGTH_MULTIPLIER/connected_scanner.damage_coeff
+ scanner_occupant.domutcheck()
+
+ // GUARD CHECK - Modifying genetics can lead to edge cases where the
+ // scanner occupant is qdel'd and replaced with a different entity.
+ // Examples of this include adding/removing the Monkified mutation which
+ // qdels the previous entity and creates a brand new one in its place.
+ // We should redo all of our occupant modification checks again, although
+ // it is less than ideal.
+ if(!can_modify_occupant())
+ return
+
+ // Check if we cracked a mutation
+ check_discovery(alias)
+
+ return
+
+ // Apply a chromosome to a specific mutation.
+ // ---------------------------------------------------------------------- //
+ // params["mutref"] - ATOM Ref of specific mutation to apply the chromo to
+ // params["chromo"] - Name of the chromosome to apply to the mutation
+ if("apply_chromo")
+ // GUARD CHECK - Can we genetically modify the occupant? Includes scanner
+ // operational guard checks.
+ if(!can_modify_occupant())
+ return
+
+ // GUARD CHECK - Have we somehow cheekily swapped occupants? This is
+ // unexpected.
+ if(!(scanner_occupant == connected_scanner.occupant))
+ return
+
+ var/bref = params["mutref"]
+
+ // GUARD CHECK - Only search occupant for this specific ref, since your
+ // can only apply chromosomes to mutations occupants.
+ var/datum/mutation/human/HM = get_mut_by_ref(bref, SEARCH_OCCUPANT)
+
+ // GUARD CHECK - This should not be possible. Unexpected result
+ if(!HM)
+ return
+
+ // Look through our stored chromos and compare names to find a
+ // stored chromo we can apply.
+ for(var/obj/item/chromosome/CM in stored_chromosomes)
+ if(CM.can_apply(HM) && (CM.name == params["chromo"]))
+ stored_chromosomes -= CM
+ CM.apply(HM)
+
+ return
+
+ // Print any type of standard injector, limited right now to activators that
+ // activate a dormant mutation and mutators that forcibly create a new
+ // MUT_EXTRA mutation
+ // ---------------------------------------------------------------------- //
+ // params["mutref"] - ATOM Ref of specific mutation to create an injector of
+ // params["is_activator"] - Is this an "Activator" style injector, also
+ // referred to as a "Research" type. Expects a string with 0 or 1, which
+ // then gets converted to a number.
+ // params["source"] - The source the request came from.
+ // Expected results:
+ // "occupant" - From genetic sequencer
+ // "console" - From DNA Console storage
+ // "disk" - From inserted diskette
+ if("print_injector")
+ // Because printing mutators and activators share a bunch of code,
+ // it makes sense to keep them both together and set unique vars
+ // later in the code
+
+ // As a side note, because mutations can contain unique metadata,
+ // this system uses BYOND Atom Refs to safely and accurately
+ // identify mutations from big ol' lists
+
+ // GUARD CHECK - Is the injector actually ready?
+ if(world.time < injectorready)
+ return
+
+ var/search_flags = 0
+
+ switch(params["source"])
+ if("occupant")
+ // GUARD CHECK - Make sure we can modify the occupant before we
+ // attempt to search them for any given mutation refs. This could
+ // lead to no search flags being passed to get_mut_by_ref and this
+ // is intended functionality to prevent any cheese or abuse
+ if(can_modify_occupant())
+ search_flags |= SEARCH_OCCUPANT
+ if("console")
+ search_flags |= SEARCH_STORED
+ if("disk")
+ search_flags |= SEARCH_DISKETTE
+
+ var/bref = params["mutref"]
+ var/datum/mutation/human/HM = get_mut_by_ref(bref, search_flags)
+
+ // GUARD CHECK - This should not be possible. Unexpected result
+ if(!HM)
+ return
+
+ // Create a new DNA Injector and add the appropriate mutations to it
+ var/obj/item/dnainjector/activator/I = new /obj/item/dnainjector/activator(loc)
+ I.add_mutations += new HM.type(copymut = HM)
+
+ var/is_activator = text2num(params["is_activator"])
+
+ // Activators are also called "research" injectors and are used to create
+ // chromosomes by recycling at the DNA Console
+ if(is_activator)
+ I.name = "[HM.name] activator"
+ I.research = TRUE
+ // If there's an operational connected scanner, we can use its upgrades
+ // to improve our injector's radiation generation
+ if(scanner_operational())
+ I.damage_coeff = connected_scanner.damage_coeff*4
+ injectorready = world.time + INJECTOR_TIMEOUT * (1 - 0.1 * connected_scanner.precision_coeff)
else
- current_screen = "mainmenu"
-
- if(connected)
- connected.locked = locked_state
- if("inspect")
- if(viable_occupant)
- var/list/mutations = get_mutation_list(TRUE)
- if(current_mutation == mutations[num])
- current_mutation = null
+ injectorready = world.time + INJECTOR_TIMEOUT
+ else
+ I.name = "[HM.name] mutator"
+ I.doitanyway = TRUE
+ // If there's an operational connected scanner, we can use its upgrades
+ // to improve our injector's radiation generation
+ if(scanner_operational())
+ I.damage_coeff = connected_scanner.damage_coeff
+ injectorready = world.time + INJECTOR_TIMEOUT * 5 * (1 - 0.1 * connected_scanner.precision_coeff)
else
- current_mutation = mutations[num]
+ injectorready = world.time + INJECTOR_TIMEOUT * 5
+
+ return
+
+ // Save a mutation to the console's storage buffer.
+ // ---------------------------------------------------------------------- //
+ // params["mutref"] - ATOM Ref of specific mutation to store
+ // params["source"] - The source the request came from.
+ // Expected results:
+ // "occupant" - From genetic sequencer
+ // "disk" - From inserted diskette
+ if("save_console")
+ var/search_flags = 0
+
+ switch(params["source"])
+ if("occupant")
+ // GUARD CHECK - Make sure we can modify the occupant before we
+ // attempt to search them for any given mutation refs. This could
+ // lead to no search flags being passed to get_mut_by_ref and this
+ // is intended functionality to prevent any cheese or abuse
+ if(can_modify_occupant())
+ search_flags |= SEARCH_OCCUPANT
+ if("disk")
+ search_flags |= SEARCH_DISKETTE
+
+ // GUARD CHECK - Is mutation storage full?
+ if(LAZYLEN(stored_mutations) >= max_storage)
+ to_chat(usr,"
Mutation storage is full.")
+ return
+
+ var/bref = params["mutref"]
+ var/datum/mutation/human/HM = get_mut_by_ref(bref, search_flags)
+
+ // GUARD CHECK - This should not be possible. Unexpected result
+ if(!HM)
+ return
+
+ var/datum/mutation/human/A = new HM.type()
+ A.copy_mutation(HM)
+ stored_mutations += A
+ to_chat(usr,"
Mutation successfully stored.")
+ return
+
+ // Save a mutation to the diskette's storage buffer.
+ // ---------------------------------------------------------------------- //
+ // params["mutref"] - ATOM Ref of specific mutation to store
+ // params["source"] - The source the request came from
+ // Expected results:
+ // "occupant" - From genetic sequencer
+ // "console" - From DNA Console storage
+ if("save_disk")
+ // GUARD CHECK - This code shouldn't even be callable without a diskette
+ // inserted. Unexpected result
+ if(!diskette)
+ return
+
+ // GUARD CHECK - Make sure the disk is not full
+ if(LAZYLEN(diskette.mutations) >= diskette.max_mutations)
+ to_chat(usr,"
Disk storage is full.")
+ return
+
+ // GUARD CHECK - Make sure the disk isn't set to read only, as we're
+ // attempting to write to it
+ if(diskette.read_only)
+ to_chat(usr,"
Disk is set to read only mode.")
+ return
+
+ var/search_flags = 0
+
+ switch(params["source"])
+ if("occupant")
+ // GUARD CHECK - Make sure we can modify the occupant before we
+ // attempt to search them for any given mutation refs. This could
+ // lead to no search flags being passed to get_mut_by_ref and this
+ // is intended functionality to prevent any cheese or abuse
+ if(can_modify_occupant())
+ search_flags |= SEARCH_OCCUPANT
+ if("console")
+ search_flags |= SEARCH_STORED
+
+ var/bref = params["mutref"]
+ var/datum/mutation/human/HM = get_mut_by_ref(bref, search_flags)
+
+ // GUARD CHECK - This should not be possible. Unexpected result
+ if(!HM)
+ return
+
+ var/datum/mutation/human/A = new HM.type()
+ A.copy_mutation(HM)
+ diskette.mutations += A
+ to_chat(usr,"
Mutation successfully stored to disk.")
+ return
+
+ // Completely removes a MUT_EXTRA mutation or mutation with corrupt gene
+ // sequence from the scanner occupant
+ // ---------------------------------------------------------------------- //
+ // params["mutref"] - ATOM Ref of specific mutation to nullify
+ if("nullify")
+ // GUARD CHECK - Can we genetically modify the occupant? Includes scanner
+ // operational guard checks.
+ if(!can_modify_occupant())
+ return
+
+ var/bref = params["mutref"]
+ var/datum/mutation/human/HM = get_mut_by_ref(bref, SEARCH_OCCUPANT)
+
+ // GUARD CHECK - This should not be possible. Unexpected result
+ if(!HM)
+ return
+
+ // GUARD CHECK - Nullify should only be used on scrambled or "extra"
+ // mutations.
+ if(!HM.scrambled && !(HM.class == MUT_EXTRA))
+ return
+
+ scanner_occupant.dna.remove_mutation(HM.type)
+ return
+
+ // Deletes saved mutation from console buffer.
+ // ---------------------------------------------------------------------- //
+ // params["mutref"] - ATOM Ref of specific mutation to delete
+ if("delete_console_mut")
+ var/bref = params["mutref"]
+ var/datum/mutation/human/HM = get_mut_by_ref(bref, SEARCH_STORED)
- if("inspectstorage")
- current_storage = num
- current_screen = "info"
- if("savemut")
- if(viable_occupant)
- var/succes
- if(LAZYLEN(stored_mutations) < max_storage)
- var/mutation = text2path(href_list["path"])
- if(ispath(mutation, /datum/mutation/human)) //sanity checks
- var/datum/mutation/human/HM = viable_occupant.dna.get_mutation(mutation)
- if(HM)
- var/datum/mutation/human/A = new HM.type()
- A.copy_mutation(HM)
- succes = TRUE
- stored_mutations += A
- to_chat(usr,"
Mutation succesfully stored.")
- if(!succes) //we can exactly return here
- to_chat(usr,"
Mutation storage is full.")
- if("deletemut")
- var/datum/mutation/human/HM = stored_mutations[num]
if(HM)
stored_mutations.Remove(HM)
qdel(HM)
- current_screen = "mutations"
- if("activator")
- if(injectorready < world.time)
- var/mutation = text2path(href_list["path"])
- if(ispath(mutation, /datum/mutation/human))
- var/datum/mutation/human/HM = get_valid_mutation(mutation)
- if(HM)
- var/obj/item/dnainjector/activator/I = new /obj/item/dnainjector/activator(loc)
- I.add_mutations += new HM.type (copymut = HM)
- I.name = "[HM.name] activator"
- I.research = TRUE
- if(connected)
- I.damage_coeff = connected.damage_coeff*4
- injectorready = world.time + INJECTOR_TIMEOUT * (1 - 0.1 * connected.precision_coeff) //precision_coeff being the matter bin rating
- else
- injectorready = world.time + INJECTOR_TIMEOUT
- if("mutator")
- if(injectorready < world.time)
- var/mutation = text2path(href_list["path"])
- if(ispath(mutation, /datum/mutation/human))
- var/datum/mutation/human/HM = get_valid_mutation(mutation)
- if(HM)
- var/obj/item/dnainjector/activator/I = new /obj/item/dnainjector/activator(loc)
- I.add_mutations += new HM.type (copymut = HM)
- I.doitanyway = TRUE
- I.name = "[HM.name] injector"
- if(connected)
- I.damage_coeff = connected.damage_coeff
- injectorready = world.time + INJECTOR_TIMEOUT * 5 * (1 - 0.1 * connected.precision_coeff)
- else
- injectorready = world.time + INJECTOR_TIMEOUT * 5
- if("advinjector")
- var/selection = href_list["injector"]
- if(injectorready < world.time)
- if(injector_selection.Find(selection))
- var/list/true_selection = injector_selection[selection]
- if(LAZYLEN(injector_selection))
- var/obj/item/dnainjector/activator/I = new /obj/item/dnainjector/activator(loc)
- for(var/A in true_selection)
- var/datum/mutation/human/HM = A
- I.add_mutations += new HM.type (copymut = HM)
- I.doitanyway = TRUE
- I.name = "Advanced [selection] injector"
- if(connected)
- I.damage_coeff = connected.damage_coeff
- injectorready = world.time + INJECTOR_TIMEOUT * 8 * (1 - 0.1 * connected.precision_coeff)
- else
- injectorready = world.time + INJECTOR_TIMEOUT * 8
- if("nullify")
- if(viable_occupant)
- var/datum/mutation/human/A = viable_occupant.dna.get_mutation(current_mutation)
- if(A && (!viable_occupant.dna.mutation_in_sequence(current_mutation) || A.scrambled))
- viable_occupant.dna.remove_mutation(current_mutation)
- current_screen = "mainmenu"
- current_mutation = null
- if("pulsegene")
- if(current_screen != "info")
- var/path = text2path(href_list["path"])
- if(viable_occupant && num && (path in viable_occupant.dna.mutation_index))
- var/list/genes = list("A","T","G","C","X")
- if(jokerready < world.time)
- genes += "JOKER"
- var/sequence = GET_GENE_STRING(path, viable_occupant.dna)
- var/original = sequence[num]
- var/new_gene = input("From [original] to-", "New block", original) as null|anything in genes
- if(!new_gene)
- new_gene = original
- if(viable_occupant == get_viable_occupant()) //No cheesing
- if((new_gene == "JOKER") && (jokerready < world.time))
- var/true_genes = GET_SEQUENCE(current_mutation)
- new_gene = true_genes[num]
- jokerready = world.time + JOKER_TIMEOUT - (JOKER_UPGRADE * (connected.precision_coeff-1))
- sequence = copytext(sequence, 1, num) + new_gene + copytext(sequence, num+1, length(sequence)+1)
- viable_occupant.dna.mutation_index[path] = sequence
- viable_occupant.radiation += RADIATION_STRENGTH_MULTIPLIER/connected.damage_coeff
- viable_occupant.domutcheck()
- if("exportdiskmut")
- if(diskette && !diskette.read_only)
- var/path = text2path(href_list["path"])
- if(ispath(path, /datum/mutation/human))
- var/datum/mutation/human/A = get_valid_mutation(path)
- if(A && diskette && (LAZYLEN(diskette.mutations) < diskette.max_mutations))
- var/datum/mutation/human/HM = new A.type()
- diskette.mutations += HM
- HM.copy_mutation(A)
- to_chat(usr, "
Succesfully written [A.name] to [diskette.name].")
- if("deletediskmut")
- if(diskette && !diskette.read_only)
- if(num && (LAZYLEN(diskette.mutations) >= num))
- var/datum/mutation/human/A = diskette.mutations[num]
- diskette.mutations.Remove(A)
- qdel(A)
- if("importdiskmut")
- if(diskette && (LAZYLEN(diskette.mutations) >= num))
- if(LAZYLEN(stored_mutations) < max_storage)
- var/datum/mutation/human/A = diskette.mutations[num]
- var/datum/mutation/human/HM = new A.type()
- HM.copy_mutation(A)
- stored_mutations += HM
- to_chat(usr,"
Succesfully written [A.name] to storage.")
- if("combine")
- if(num && (LAZYLEN(stored_mutations) >= num))
- if(LAZYLEN(stored_mutations) < max_storage)
- var/datum/mutation/human/A = stored_mutations[num]
- var/path = A.type
- if(combine)
- var/result_path = get_mixed_mutation(combine, path)
- if(result_path)
- stored_mutations += new result_path()
- to_chat(usr, "Succes! New mutation has been added to storage")
- discover(result_path)
- combine = null
- else
- to_chat(usr, "Failed. No mutation could be created.")
- combine = null
- else
- combine = path
- to_chat(usr,"Selected [A.name] for combining")
- else
- to_chat(usr, "Not enough space to store potential mutation.")
- if("ejectchromosome")
- if(LAZYLEN(stored_chromosomes) <= num)
- var/obj/item/chromosome/CM = stored_chromosomes[num]
- CM.forceMove(drop_location())
- adjust_item_drop_location(CM)
- stored_chromosomes -= CM
- if("applychromosome")
- if(viable_occupant && (LAZYLEN(viable_occupant.dna.mutations) <= num))
- var/datum/mutation/human/HM = viable_occupant.dna.mutations[num]
- var/list/chromosomes = list()
- for(var/obj/item/chromosome/CM in stored_chromosomes)
- if(CM.can_apply(HM))
- chromosomes += CM
- if(chromosomes.len)
- var/obj/item/chromosome/CM = input("Select a chromosome to apply", "Apply Chromosome") as null|anything in sortNames(chromosomes)
- if(CM)
- to_chat(usr, "You apply [CM] to [HM.name].")
- stored_chromosomes -= CM
- CM.apply(HM)
- if("expand_advinjector")
- var/mutation = text2path(href_list["path"])
- var/datum/mutation/human/HM = get_valid_mutation(mutation)
- if(HM && LAZYLEN(injector_selection))
- var/which_injector = input(usr, "Select Adv. Injector", "Advanced Injectors") as null|anything in injector_selection
- if(injector_selection.Find(which_injector))
- var/list/true_selection = injector_selection[which_injector]
- var/total_instability
- for(var/B in true_selection)
- var/datum/mutation/human/mootacion = B
- total_instability += mootacion.instability
- total_instability += HM.instability
- if((total_instability > max_injector_instability) || (true_selection.len + 1) > max_injector_mutations)
- to_chat(usr, "Adding more mutations would make the advanced injector too unstable!")
- else
- true_selection += HM //reminder that this works. because I keep forgetting this works
- if("remove_from_advinjector")
- var/mutation = text2path(href_list["path"])
- var/selection = href_list["injector"]
- if(injector_selection.Find(selection))
- var/list/true_selection = injector_selection[selection]
- for(var/B in true_selection)
- var/datum/mutation/human/HM = B
- if(HM.type == mutation)
- true_selection -= HM
- break
+ return
- if("remove_advinjector")
- var/selection = href_list["injector"]
- for(selection in injector_selection)
- if(selection == selection)
- injector_selection.Remove(selection)
+ // Deletes saved mutation from disk buffer.
+ // ---------------------------------------------------------------------- //
+ // params["mutref"] - ATOM Ref of specific mutation to delete
+ if("delete_disk_mut")
+ // GUARD CHECK - This code shouldn't even be callable without a diskette
+ // inserted. Unexpected result
+ if(!diskette)
+ return
- if("add_advinjector")
- if(LAZYLEN(injector_selection) < max_injector_selections)
- var/new_selection = input(usr, "Enter Adv. Injector name", "Advanced Injectors") as text|null
- if(new_selection && !(new_selection in injector_selection))
- injector_selection[new_selection] = list()
+ // GUARD CHECK - Make sure the disk isn't set to read only, as we're
+ // attempting to write to it (via deletion)
+ if(diskette.read_only)
+ to_chat(usr,"Disk is set to read only mode.")
+ return
+ var/bref = params["mutref"]
+ var/datum/mutation/human/HM = get_mut_by_ref(bref, SEARCH_DISKETTE)
- ui_interact(usr,last_change)
+ if(HM)
+ diskette.mutations.Remove(HM)
+ qdel(HM)
-/obj/machinery/computer/scan_consolenew/proc/scramble(input,rs,rd) //hexadecimal genetics. dont confuse with scramble button
+ return
+
+ // Ejects a stored chromosome from the DNA Console
+ // ---------------------------------------------------------------------- //
+ // params["chromo"] - Text string of the chromosome name
+ if("eject_chromo")
+ var/chromname = params["chromo"]
+
+ for(var/obj/item/chromosome/CM in stored_chromosomes)
+ if(chromname == CM.name)
+ CM.forceMove(drop_location())
+ adjust_item_drop_location(CM)
+ stored_chromosomes -= CM
+ return
+
+ return
+
+ // Combines two mutations from the console to try and create a new mutation
+ // ---------------------------------------------------------------------- //
+ // params["firstref"] - ATOM Ref of first mutation for combination
+ // params["secondref"] - ATOM Ref of second mutation for combination
+ // mutation
+ if("combine_console")
+ // GUaRD CHECK - Make sure mutation storage isn't full. If it is, we won't
+ // be able to store the new combo mutation
+ if(LAZYLEN(stored_mutations) >= max_storage)
+ to_chat(usr,"Mutation storage is full.")
+ return
+
+ // GUARD CHECK - We're running a research-type operation. If, for some
+ // reason, somehow the DNA Console has been disconnected from the research
+ // network - Or was never in it to begin with - don't proceed
+ if(!stored_research)
+ return
+
+ var/first_bref = params["firstref"]
+ var/second_bref = params["secondref"]
+
+ // GUARD CHECK - Find the source and destination mutations on the console
+ // and make sure they actually exist.
+ var/datum/mutation/human/source_mut = get_mut_by_ref(first_bref, SEARCH_STORED | SEARCH_DISKETTE)
+ if(!source_mut)
+ return
+
+ var/datum/mutation/human/dest_mut = get_mut_by_ref(second_bref, SEARCH_STORED | SEARCH_DISKETTE)
+ if(!dest_mut)
+ return
+
+ // Attempt to mix the two mutations to get a new type
+ var/result_path = get_mixed_mutation(source_mut.type, dest_mut.type)
+
+ if(!result_path)
+ return
+
+ // If we got a new type, add it to our storage
+ stored_mutations += new result_path()
+ to_chat(usr, "Success! New mutation has been added to console storage.")
+
+ // If it's already discovered, end here. Otherwise, add it to the list of
+ // discovered mutations.
+ // We've already checked for stored_research earlier
+ if(result_path in stored_research.discovered_mutations)
+ return
+
+ var/datum/mutation/human/HM = GET_INITIALIZED_MUTATION(result_path)
+ stored_research.discovered_mutations += result_path
+ say("Successfully mutated [HM.name].")
+ return
+
+ // Combines two mutations from the disk to try and create a new mutation
+ // ---------------------------------------------------------------------- //
+ // params["firstref"] - ATOM Ref of first mutation for combination
+ // params["secondref"] - ATOM Ref of second mutation for combination
+ // mutation
+ if("combine_disk")
+ // GUARD CHECK - This code shouldn't even be callable without a diskette
+ // inserted. Unexpected result
+ if(!diskette)
+ return
+
+ // GUARD CHECK - Make sure the disk is not full.
+ if(LAZYLEN(diskette.mutations) >= diskette.max_mutations)
+ to_chat(usr,"Disk storage is full.")
+ return
+
+ // GUARD CHECK - Make sure the disk isn't set to read only, as we're
+ // attempting to write to it
+ if(diskette.read_only)
+ to_chat(usr,"Disk is set to read only mode.")
+ return
+
+ // GUARD CHECK - We're running a research-type operation. If, for some
+ // reason, somehow the DNA Console has been disconnected from the research
+ // network - Or was never in it to begin with - don't proceed
+ if(!stored_research)
+ return
+
+ var/first_bref = params["firstref"]
+ var/second_bref = params["secondref"]
+
+ // GUARD CHECK - Find the source and destination mutations on the console
+ // and make sure they actually exist.
+ var/datum/mutation/human/source_mut = get_mut_by_ref(first_bref, SEARCH_STORED | SEARCH_DISKETTE)
+ if(!source_mut)
+ return
+
+ var/datum/mutation/human/dest_mut = get_mut_by_ref(second_bref, SEARCH_STORED | SEARCH_DISKETTE)
+ if(!dest_mut)
+ return
+
+ // Attempt to mix the two mutations to get a new type
+ var/result_path = get_mixed_mutation(source_mut.type, dest_mut.type)
+
+ if(!result_path)
+ return
+
+ // If we got a new type, add it to our storage
+ diskette.mutations += new result_path()
+ to_chat(usr, "Success! New mutation has been added to the disk.")
+
+ // If it's already discovered, end here. Otherwise, add it to the list of
+ // discovered mutations
+ // We've already checked for stored_research earlier
+ if(result_path in stored_research.discovered_mutations)
+ return
+
+ var/datum/mutation/human/HM = GET_INITIALIZED_MUTATION(result_path)
+ stored_research.discovered_mutations += result_path
+ say("Successfully mutated [HM.name].")
+ return
+
+ // Sets the Genetic Makeup pulse strength.
+ // ---------------------------------------------------------------------- //
+ // params["val"] - New strength value as text string, converted to number
+ // later on in code
+ if("set_pulse_strength")
+ var/value = round(text2num(params["val"]))
+ radstrength = WRAP(value, 1, RADIATION_STRENGTH_MAX+1)
+ return
+
+ // Sets the Genetic Makeup pulse duration
+ // ---------------------------------------------------------------------- //
+ // params["val"] - New strength value as text string, converted to number
+ // later on in code
+ if("set_pulse_duration")
+ var/value = round(text2num(params["val"]))
+ radduration = WRAP(value, 1, RADIATION_DURATION_MAX+1)
+ return
+
+ // Saves Genetic Makeup information to disk
+ // ---------------------------------------------------------------------- //
+ // params["index"] - The BYOND index of the console genetic makeup buffer to
+ // copy to disk
+ if("save_makeup_disk")
+ // GUARD CHECK - This code shouldn't even be callable without a diskette
+ // inserted. Unexpected result
+ if(!diskette)
+ return
+
+ // GUARD CHECK - Make sure the disk isn't set to read only, as we're
+ // attempting to write to it
+ if(diskette.read_only)
+ to_chat(usr,"Disk is set to read only mode.")
+ return
+
+ // Convert the index to a number and clamp within the array range
+ var/buffer_index = text2num(params["index"])
+ buffer_index = clamp(buffer_index, 1, NUMBER_OF_BUFFERS)
+
+ var/list/buffer_slot = genetic_makeup_buffer[buffer_index]
+
+ // GUARD CHECK - This should not be possible to activate on a buffer slot
+ // that doesn't have any genetic data. Unexpected result
+ if(!istype(buffer_slot))
+ return
+
+ diskette.genetic_makeup_buffer = buffer_slot.Copy()
+ return
+
+ // Loads Genetic Makeup from disk to a console buffer
+ // ---------------------------------------------------------------------- //
+ // params["index"] - The BYOND index of the console genetic makeup buffer to
+ // copy to. Expected as text string, converted to number later
+ if("load_makeup_disk")
+ // GUARD CHECK - This code shouldn't even be callable without a diskette
+ // inserted. Unexpected result
+ if(!diskette)
+ return
+
+ // GUARD CHECK - This should not be possible to activate on a diskette
+ // that doesn't have any genetic data. Unexpected result
+ if(LAZYLEN(diskette.genetic_makeup_buffer) == 0)
+ return
+
+ // Convert the index to a number and clamp within the array range, then
+ // copy the data from the disk to that buffer
+ var/buffer_index = text2num(params["index"])
+ buffer_index = clamp(buffer_index, 1, NUMBER_OF_BUFFERS)
+ genetic_makeup_buffer[buffer_index] = diskette.genetic_makeup_buffer.Copy()
+ return
+
+ // Deletes genetic makeup buffer from the inserted diskette
+ if("del_makeup_disk")
+ // GUARD CHECK - This code shouldn't even be callable without a diskette
+ // inserted. Unexpected result
+ if(!diskette)
+ return
+
+ // GUARD CHECK - Make sure the disk isn't set to read only, as we're
+ // attempting to write (via deletion) to it
+ if(diskette.read_only)
+ to_chat(usr,"Disk is set to read only mode.")
+ return
+
+ diskette.genetic_makeup_buffer.Cut()
+ return
+
+ // Saves the scanner occupant's genetic makeup to a given console buffer
+ // ---------------------------------------------------------------------- //
+ // params["index"] - The BYOND index of the console genetic makeup buffer to
+ // save the new genetic data to. Expected as text string, converted to
+ // number later
+ if("save_makeup_console")
+ // GUARD CHECK - Can we genetically modify the occupant? Includes scanner
+ // operational guard checks.
+ if(!can_modify_occupant())
+ return
+
+ // Convert the index to a number and clamp within the array range, then
+ // copy the data from the disk to that buffer
+ var/buffer_index = text2num(params["index"])
+ buffer_index = clamp(buffer_index, 1, NUMBER_OF_BUFFERS)
+
+ // Set the new information
+ genetic_makeup_buffer[buffer_index] = list(
+ "label"="Slot [buffer_index]:[scanner_occupant.real_name]",
+ "UI"=scanner_occupant.dna.uni_identity,
+ "UE"=scanner_occupant.dna.unique_enzymes,
+ "name"=scanner_occupant.real_name,
+ "blood_type"=scanner_occupant.dna.blood_type)
+
+ return
+
+ // Deleted genetic makeup data from a console buffer slot
+ // ---------------------------------------------------------------------- //
+ // params["index"] - The BYOND index of the console genetic makeup buffer to
+ // delete the genetic data from. Expected as text string, converted to
+ // number later
+ if("del_makeup_console")
+ // Convert the index to a number and clamp within the array range, then
+ // copy the data from the disk to that buffer
+ var/buffer_index = text2num(params["index"])
+ buffer_index = clamp(buffer_index, 1, NUMBER_OF_BUFFERS)
+ var/list/buffer_slot = genetic_makeup_buffer[buffer_index]
+
+ // GUARD CHECK - This shouldn't be possible to execute this on a null
+ // buffer. Unexpected resut
+ if(!istype(buffer_slot))
+ return
+
+ genetic_makeup_buffer[buffer_index] = null
+ return
+
+ // Eject stored diskette from console
+ if("eject_disk")
+ eject_disk(usr)
+ return
+
+ // Create a Genetic Makeup injector. These injectors are timed and thus are
+ // only temporary
+ // ---------------------------------------------------------------------- //
+ // params["index"] - The BYOND index of the console genetic makeup buffer to
+ // create the makeup injector from. Expected as text string, converted to
+ // number later
+ // params["type"] - Type of injector to create
+ // Expected results:
+ // "ue" - Unique Enzyme, changes name and blood type
+ // "ui" - Unique Identity, changes looks
+ // "mixed" - Combination of both ue and ui
+ if("makeup_injector")
+ // Convert the index to a number and clamp within the array range, then
+ // copy the data from the disk to that buffer
+ var/buffer_index = text2num(params["index"])
+ buffer_index = clamp(buffer_index, 1, NUMBER_OF_BUFFERS)
+ var/list/buffer_slot = genetic_makeup_buffer[buffer_index]
+
+ // GUARD CHECK - This shouldn't be possible to execute this on a null
+ // buffer. Unexpected resut
+ if(!istype(buffer_slot))
+ return
+
+ var/type = params["type"]
+ var/obj/item/dnainjector/timed/I
+
+ switch(type)
+ if("ui")
+ // GUARD CHECK - There's currently no way to save partial genetic data.
+ // However, if this is the case, we can't make a complete injector and
+ // this catches that edge case
+ if(!buffer_slot["UI"])
+ to_chat(usr,"Genetic data corrupted, unable to create injector.")
+ return
+
+ I = new /obj/item/dnainjector/timed(loc)
+ I.fields = list("UI"=buffer_slot["UI"])
+
+ // If there is a connected scanner, we can use its upgrades to reduce
+ // the radiation generated by this injector
+ if(scanner_operational())
+ I.damage_coeff = connected_scanner.damage_coeff
+ if("ue")
+ // GUARD CHECK - There's currently no way to save partial genetic data.
+ // However, if this is the case, we can't make a complete injector and
+ // this catches that edge case
+ if(!buffer_slot["name"] || !buffer_slot["UE"] || !buffer_slot["blood_type"])
+ to_chat(usr,"Genetic data corrupted, unable to create injector.")
+ return
+
+ I = new /obj/item/dnainjector/timed(loc)
+ I.fields = list("name"=buffer_slot["name"], "UE"=buffer_slot["UE"], "blood_type"=buffer_slot["blood_type"])
+
+ // If there is a connected scanner, we can use its upgrades to reduce
+ // the radiation generated by this injector
+ if(scanner_operational())
+ I.damage_coeff = connected_scanner.damage_coeff
+ if("mixed")
+ // GUARD CHECK - There's currently no way to save partial genetic data.
+ // However, if this is the case, we can't make a complete injector and
+ // this catches that edge case
+ if(!buffer_slot["UI"] || !buffer_slot["name"] || !buffer_slot["UE"] || !buffer_slot["blood_type"])
+ to_chat(usr,"Genetic data corrupted, unable to create injector.")
+ return
+
+ I = new /obj/item/dnainjector/timed(loc)
+ I.fields = list("UI"=buffer_slot["UI"],"name"=buffer_slot["name"], "UE"=buffer_slot["UE"], "blood_type"=buffer_slot["blood_type"])
+
+ // If there is a connected scanner, we can use its upgrades to reduce
+ // the radiation generated by this injector
+ if(scanner_operational())
+ I.damage_coeff = connected_scanner.damage_coeff
+
+ // If we successfully created an injector, don't forget to set the new
+ // ready timer.
+ if(I)
+ injectorready = world.time + INJECTOR_TIMEOUT
+
+ return
+
+ // Applies a genetic makeup buffer to the scanner occupant
+ // ---------------------------------------------------------------------- //
+ // params["index"] - The BYOND index of the console genetic makeup buffer to
+ // apply to the scanner occupant. Expected as text string, converted to
+ // number later
+ // params["type"] - Type of genetic makeup copy to implement
+ // Expected results:
+ // "ue" - Unique Enzyme, changes name and blood type
+ // "ui" - Unique Identity, changes looks
+ // "mixed" - Combination of both ue and ui
+ if("makeup_apply")
+ // GUARD CHECK - Can we genetically modify the occupant? Includes scanner
+ // operational guard checks.
+ if(!can_modify_occupant())
+ return
+
+ // Convert the index to a number and clamp within the array range, then
+ // copy the data from the disk to that buffer
+ var/buffer_index = text2num(params["index"])
+ buffer_index = clamp(buffer_index, 1, NUMBER_OF_BUFFERS)
+ var/list/buffer_slot = genetic_makeup_buffer[buffer_index]
+
+ // GUARD CHECK - This shouldn't be possible to execute this on a null
+ // buffer. Unexpected resut
+ if(!istype(buffer_slot))
+ return
+
+ var/type = params["type"]
+
+ apply_genetic_makeup(type, buffer_slot)
+ return
+
+ // Applies a genetic makeup buffer to the next scanner occupant. This sets
+ // some code that will run when the connected DNA Scanner door is next
+ // closed
+ // This allows people to self-modify their genetic makeup, as tgui
+ // interfaces can not be accessed while inside the DNA Scanner and genetic
+ // makeup injectors are only temporary
+ // ---------------------------------------------------------------------- //
+ // params["index"] - The BYOND index of the console genetic makeup buffer to
+ // apply to the scanner occupant. Expected as text string, converted to
+ // number later
+ // params["type"] - Type of genetic makeup copy to implement
+ // Expected results:
+ // "ue" - Unique Enzyme, changes name and blood type
+ // "ui" - Unique Identity, changes looks
+ // "mixed" - Combination of both ue and ui
+ if("makeup_delay")
+ // Convert the index to a number and clamp within the array range, then
+ // copy the data from the disk to that buffer
+ var/buffer_index = text2num(params["index"])
+ buffer_index = clamp(buffer_index, 1, NUMBER_OF_BUFFERS)
+ var/list/buffer_slot = genetic_makeup_buffer[buffer_index]
+
+ // GUARD CHECK - This shouldn't be possible to execute this on a null
+ // buffer. Unexpected resut
+ if(!istype(buffer_slot))
+ return
+
+ var/type = params["type"]
+
+ // Set the delayed action. The next time the scanner door is closed,
+ // unless this is cancelled in the UI, the action will happen
+ delayed_action = list("type" = type, "buffer_slot" = buffer_slot)
+ return
+
+ // Attempts to modify the indexed element of the Unique Identity string
+ // This is a time delayed action that is handled in process()
+ // ---------------------------------------------------------------------- //
+ // params["index"] - The BYOND index of the Unique Identity string to
+ // attempt to modify
+ if("makeup_pulse")
+ // GUARD CHECK - Can we genetically modify the occupant? Includes scanner
+ // operational guard checks.
+ if(!can_modify_occupant())
+ return
+
+ // Set the appropriate timer and index to pulse. This is then managed
+ // later on in process()
+ var/len = length_char(scanner_occupant.dna.uni_identity)
+ rad_pulse_timer = world.time + (radduration*10)
+ rad_pulse_index = WRAP(text2num(params["index"]), 1, len+1)
+ START_PROCESSING(SSobj, src)
+ return
+
+ // Cancels the delayed action - In this context it is not the radiation
+ // pulse from "makeup_pulse", which can not be cancelled. It is instead
+ // the delayed genetic transfer from "makeup_delay"
+ if("cancel_delay")
+ delayed_action = null
+ return
+
+ // Creates a new advanced injector storage buffer in the console
+ // ---------------------------------------------------------------------- //
+ // params["name"] - The name to apply to the new injector
+ if("new_adv_inj")
+ // GUARD CHECK - Make sure we can make a new injector. This code should
+ // not be called if we're already maxed out and this is an Unexpected
+ // result
+ if(!(LAZYLEN(injector_selection) < max_injector_selections))
+ return
+
+ // GUARD CHECK - Sanitise and trim the proposed name. This prevents HTML
+ // injection and equivalent as tgui input is not stripped
+ var/inj_name = params["name"]
+ inj_name = trim(sanitize(inj_name))
+
+ // GUARD CHECK - If the name is null or blank, or the name is already in
+ // the list of advanced injectors, we want to reject it as we can't have
+ // duplicate named advanced injectors
+ if(!inj_name || (inj_name in injector_selection))
+ return
+
+ injector_selection[inj_name] = list()
+ return
+
+ // Deleted an advanced injector storage buffer from the console
+ // ---------------------------------------------------------------------- //
+ // params["name"] - The name of the injector to delete
+ if("del_adv_inj")
+ var/inj_name = params["name"]
+
+ // GUARD CHECK - If the name is null or blank, reject.
+ // GUARD CHECK - If the name isn't in the list of advanced injectors, we
+ // want to reject this as it shouldn't be possible ever do this.
+ // Unexpected result
+ if(!inj_name || !(inj_name in injector_selection))
+ return
+
+ injector_selection.Remove(inj_name)
+ return
+
+ // Creates an injector from an advanced injector buffer
+ // ---------------------------------------------------------------------- //
+ // params["name"] - The name of the injector to print
+ if("print_adv_inj")
+ // As a side note, because mutations can contain unique metadata,
+ // this system uses BYOND Atom Refs to safely and accurately
+ // identify mutations from big ol' lists.
+
+ // GUARD CHECK - Is the injector actually ready?
+ if(world.time < injectorready)
+ return
+
+ var/inj_name = params["name"]
+
+ // GUARD CHECK - If the name is null or blank, reject.
+ // GUARD CHECK - If the name isn't in the list of advanced injectors, we
+ // want to reject this as it shouldn't be possible ever do this.
+ // Unexpected result
+ if(!inj_name || !(inj_name in injector_selection))
+ return
+
+ var/list/injector = injector_selection[inj_name]
+ var/obj/item/dnainjector/activator/I = new /obj/item/dnainjector/activator(loc)
+
+ // Run through each mutation in our Advanced Injector and add them to a
+ // new injector
+ for(var/A in injector)
+ var/datum/mutation/human/HM = A
+ I.add_mutations += new HM.type(copymut=HM)
+
+ // Force apply any mutations, this is functionality similar to mutators
+ I.doitanyway = TRUE
+ I.name = "Advanced [inj_name] injector"
+
+ // If there's an operational connected scanner, we can use its upgrades
+ // to improve our injector's radiation generation
+ if(scanner_operational())
+ I.damage_coeff = connected_scanner.damage_coeff
+ injectorready = world.time + INJECTOR_TIMEOUT * 8 * (1 - 0.1 * connected_scanner.precision_coeff)
+ else
+ injectorready = world.time + INJECTOR_TIMEOUT * 8
+
+ return
+
+ // Adds a mutation to an advanced injector
+ // ---------------------------------------------------------------------- //
+ // params["mutref"] - ATOM Ref of specific mutation to add to the injector
+ // params["advinj"] - Name of the advanced injector to add the mutation to
+ if("add_advinj_mut")
+ // GUARD CHECK - Can we genetically modify the occupant? Includes scanner
+ // operational guard checks.
+ // This is needed because this operation can only be completed from the
+ // genetic sequencer.
+ if(!can_modify_occupant())
+ return
+
+ var/adv_inj = params["advinj"]
+
+ // GUARD CHECK - Make sure our advanced injector actually exists. This
+ // should not be possible. Unexpected result
+ if(!(adv_inj in injector_selection))
+ return
+
+ // GUARD CHECK - Make sure we limit the number of mutations appropriately
+ if(LAZYLEN(injector_selection[adv_inj]) >= max_injector_mutations)
+ to_chat(usr,"Advanced injector mutation storage is full.")
+ return
+
+ var/mut_source = params["source"]
+ var/search_flag = 0
+
+ switch(mut_source)
+ if("disk")
+ search_flag = SEARCH_DISKETTE
+ if("occupant")
+ search_flag = SEARCH_OCCUPANT
+ if("console")
+ search_flag = SEARCH_STORED
+
+ if(!search_flag)
+ return
+
+ var/bref = params["mutref"]
+ // We've already made sure we can modify the occupant, so this is safe to
+ // call
+ var/datum/mutation/human/HM = get_mut_by_ref(bref, search_flag)
+
+ // GUARD CHECK - This should not be possible. Unexpected result
+ if(!HM)
+ return
+
+ // We want to make sure we stick within the instability limit.
+ // We start with the instability of the mutation we're intending to add.
+ var/instability_total = HM.instability
+
+ // We then add the instabilities of all other mutations in the injector,
+ // remembering to apply the Stabilizer chromosome modifiers
+ for(var/datum/mutation/human/I in injector_selection[adv_inj])
+ instability_total += I.instability * GET_MUTATION_STABILIZER(I)
+
+ // If this would take us over the max instability, we inform the user.
+ if(instability_total > max_injector_instability)
+ to_chat(usr,"Extra mutation would make the advanced injector too instable.")
+ return
+
+ // If we've got here, all our checks are passed and we can successfully
+ // add the mutation to the advanced injector.
+ var/datum/mutation/human/A = new HM.type()
+ A.copy_mutation(HM)
+ injector_selection[adv_inj] += A
+ to_chat(usr,"Mutation successfully added to advanced injector.")
+ return
+
+ // Deletes a mutation from an advanced injector
+ // ---------------------------------------------------------------------- //
+ // params["mutref"] - ATOM Ref of specific mutation to del from the injector
+ if("delete_injector_mut")
+ var/bref = params["mutref"]
+
+ var/datum/mutation/human/HM = get_mut_by_ref(bref, SEARCH_ADV_INJ)
+
+ // GUARD CHECK - This should not be possible. Unexpected result
+ if(!HM)
+ return
+
+ // Check Advanced Injectors to find and remove the mutation
+ for(var/I in injector_selection)
+ if(injector_selection["[I]"].Remove(HM))
+ qdel(HM)
+ return
+
+ return
+
+ // Sets a new tgui view state
+ // ---------------------------------------------------------------------- //
+ // params["id"] - Key for the state to set
+ // params[...] - Every other element is used to set state variables
+ if("set_view")
+ for (var/key in params)
+ if(key == "src")
+ continue
+ tgui_view_state[key] = params[key]
+ return TRUE
+ return FALSE
+
+/**
+ * Applies the enzyme buffer to the current scanner occupant
+ *
+ * Applies the type of a specific genetic makeup buffer to the current scanner
+ * occupant
+ *
+ * Arguments:
+ * * type - "ui"/"ue"/"mixed" - Which part of the enzyme buffer to apply
+ * * buffer_slot - Index of the enzyme buffer to apply
+ */
+/obj/machinery/computer/scan_consolenew/proc/apply_genetic_makeup(type, buffer_slot)
+ // Note - This proc is only called from code that has already performed the
+ // necessary occupant guard checks. If you call this code yourself, please
+ // apply can_modify_occupant() or equivalent checks first.
+
+ // Pre-calc the rad increase since we'll be using it in all the possible
+ // operations
+ var/rad_increase = rand(100/(connected_scanner.damage_coeff ** 2),250/(connected_scanner.damage_coeff ** 2))
+
+ switch(type)
+ if("ui")
+ // GUARD CHECK - There's currently no way to save partial genetic data.
+ // However, if this is the case, we can't make a complete injector and
+ // this catches that edge case
+ if(!buffer_slot["UI"])
+ to_chat(usr,"Genetic data corrupted, unable to apply genetic data.")
+ return FALSE
+ scanner_occupant.dna.uni_identity = buffer_slot["UI"]
+ scanner_occupant.updateappearance(mutations_overlay_update=1)
+ scanner_occupant.radiation += rad_increase
+ scanner_occupant.domutcheck()
+ return TRUE
+ if("ue")
+ // GUARD CHECK - There's currently no way to save partial genetic data.
+ // However, if this is the case, we can't make a complete injector and
+ // this catches that edge case
+ if(!buffer_slot["name"] || !buffer_slot["UE"] || !buffer_slot["blood_type"])
+ to_chat(usr,"Genetic data corrupted, unable to apply genetic data.")
+ return FALSE
+ scanner_occupant.real_name = buffer_slot["name"]
+ scanner_occupant.name = buffer_slot["name"]
+ scanner_occupant.dna.unique_enzymes = buffer_slot["UE"]
+ scanner_occupant.dna.blood_type = buffer_slot["blood_type"]
+ scanner_occupant.radiation += rad_increase
+ scanner_occupant.domutcheck()
+ return TRUE
+ if("mixed")
+ // GUARD CHECK - There's currently no way to save partial genetic data.
+ // However, if this is the case, we can't make a complete injector and
+ // this catches that edge case
+ if(!buffer_slot["UI"] || !buffer_slot["name"] || !buffer_slot["UE"] || !buffer_slot["blood_type"])
+ to_chat(usr,"Genetic data corrupted, unable to apply genetic data.")
+ return FALSE
+ scanner_occupant.dna.uni_identity = buffer_slot["UI"]
+ scanner_occupant.updateappearance(mutations_overlay_update=1)
+ scanner_occupant.real_name = buffer_slot["name"]
+ scanner_occupant.name = buffer_slot["name"]
+ scanner_occupant.dna.unique_enzymes = buffer_slot["UE"]
+ scanner_occupant.dna.blood_type = buffer_slot["blood_type"]
+ scanner_occupant.radiation += rad_increase
+ scanner_occupant.domutcheck()
+ return TRUE
+
+ return FALSE
+/**
+ * Checks if there is a connected DNA Scanner that is operational
+ */
+/obj/machinery/computer/scan_consolenew/proc/scanner_operational()
+ if(!connected_scanner)
+ return FALSE
+
+ return (connected_scanner && connected_scanner.is_operational())
+
+/**
+ * Checks if there is a valid DNA Scanner occupant for genetic modification
+ *
+ * Checks if there is a valid subject in the DNA Scanner that can be genetically
+ * modified. Will set the scanner occupant var as part of this check.
+ * Requires that the scanner can be operated and will return early if it can't
+ */
+/obj/machinery/computer/scan_consolenew/proc/can_modify_occupant()
+ // GUARD CHECK - We always want to perform the scanner operational check as
+ // part of checking if we can modify the occupant.
+ // We can never modify the occupant of a broken scanner.
+ if(!scanner_operational())
+ return FALSE
+
+ if(!connected_scanner.occupant)
+ return FALSE
+
+ scanner_occupant = connected_scanner.occupant
+
+ // Check validity of occupent for DNA Modification
+ // DNA Modification:
+ // requires DNA
+ // this DNA can not be bad
+ // is done via radiation bursts, so radiation immune carbons are not viable
+ // And the DNA Scanner itself must have a valid scan level
+ if(scanner_occupant.has_dna() && !HAS_TRAIT(scanner_occupant, TRAIT_RADIMMUNE) && !HAS_TRAIT(scanner_occupant, TRAIT_NOCLONE) || (connected_scanner.scan_level == 3))
+ return TRUE
+
+ return FALSE
+
+/**
+ * Checks for adjacent DNA scanners and connects when it finds a viable one
+ *
+ * Seearches cardinal directions in order. Stops when it finds a viable DNA Scanner.
+ * Will connect to a broken scanner if no functional scanner is available.
+ * Links itself to the DNA Scanner to receive door open and close events.
+ */
+/obj/machinery/computer/scan_consolenew/proc/connect_to_scanner()
+ var/obj/machinery/dna_scannernew/test_scanner = null
+ var/obj/machinery/dna_scannernew/broken_scanner = null
+
+ // Look in each cardinal direction and try and find a DNA Scanner
+ // If you find a DNA Scanner, check to see if it broken or working
+ // If it's working, set the current scanner and return early
+ // If it's not working, remember it anyway as a broken scanner
+ for(var/direction in GLOB.cardinals)
+ test_scanner = locate(/obj/machinery/dna_scannernew, get_step(src, direction))
+ if(!isnull(test_scanner))
+ if(test_scanner.is_operational())
+ connected_scanner = test_scanner
+ connected_scanner.linked_console = src
+ return
+ else
+ broken_scanner = test_scanner
+
+ // Ultimately, if we have a broken scanner, we'll attempt to connect to it as
+ // a fallback case, but the code above will prefer a working scanner
+ if(!isnull(broken_scanner))
+ connected_scanner = broken_scanner
+ connected_scanner.linked_console = src
+
+/**
+ * Called by connected DNA Scanners when their doors close.
+ *
+ * Sets the new scanner occupant and completes delayed enzyme transfer if one
+ * is queued.
+ */
+/obj/machinery/computer/scan_consolenew/proc/on_scanner_close()
+ // Set the appropriate occupant now the scanner is closed
+ if(connected_scanner.occupant)
+ scanner_occupant = connected_scanner.occupant
+ else
+ scanner_occupant = null
+
+ // If we have a delayed action - In this case the only delayed action is
+ // applying a genetic makeup buffer the next time the DNA Scanner is closed -
+ // we want to perform it.
+ // GUARD CHECK - Make sure we can modify the occupant, apply_genetic_makeup()
+ // assumes we've already done this.
+ if(delayed_action && can_modify_occupant())
+ var/type = delayed_action["type"]
+ var/buffer_slot = delayed_action["buffer_slot"]
+ if(apply_genetic_makeup(type, buffer_slot))
+ to_chat(connected_scanner.occupant, "[src] activates!")
+ delayed_action = null
+
+/**
+ * Called by connected DNA Scanners when their doors open.
+ *
+ * Clears enzyme pulse operations, stops processing and nulls the current
+ * scanner occupant var.
+ */
+/obj/machinery/computer/scan_consolenew/proc/on_scanner_open()
+ // If we had a radiation pulse action ongoing, we want to stop this.
+ // Imagine it being like a microwave stopping when you open the door.
+ rad_pulse_index = 0
+ rad_pulse_timer = 0
+ STOP_PROCESSING(SSobj, src)
+ scanner_occupant = null
+
+/**
+ * Builds the genetic makeup list which will be sent to tgui interface.
+ */
+/obj/machinery/computer/scan_consolenew/proc/build_genetic_makeup_list()
+ // No code will ever null this list, we can safely Cut it.
+ tgui_genetic_makeup.Cut()
+
+ for(var/i=1, i <= NUMBER_OF_BUFFERS, i++)
+ if(genetic_makeup_buffer[i])
+ tgui_genetic_makeup["[i]"] = genetic_makeup_buffer[i].Copy()
+ else
+ tgui_genetic_makeup["[i]"] = null
+
+/**
+ * Builds the genetic makeup list which will be sent to tgui interface.
+ *
+ * Will iterate over the connected scanner occupant, DNA Console, inserted
+ * diskette and chromosomes and any advanced injectors, building the main data
+ * structures which get passed to the tgui interface.
+ */
+/obj/machinery/computer/scan_consolenew/proc/build_mutation_list(can_modify_occ)
+ // No code will ever null these lists. We can safely Cut them.
+ tgui_occupant_mutations.Cut()
+ tgui_diskette_mutations.Cut()
+ tgui_console_mutations.Cut()
+ tgui_console_chromosomes.Cut()
+ tgui_advinjector_mutations.Cut()
+
+ // ------------------------------------------------------------------------ //
+ // GUARD CHECK - Can we genetically modify the occupant? This check will have
+ // previously included checks to make sure the DNA Scanner is still
+ // operational
+ if(can_modify_occ)
+ // ---------------------------------------------------------------------- //
+ // Start cataloguing all mutations that the occupant has by default
+ for(var/mutation_type in scanner_occupant.dna.mutation_index)
+ var/datum/mutation/human/HM = GET_INITIALIZED_MUTATION(mutation_type)
+
+ var/list/mutation_data = list()
+ var/text_sequence = scanner_occupant.dna.mutation_index[mutation_type]
+ var/default_sequence = scanner_occupant.dna.default_mutation_genes[mutation_type]
+ var/discovered = (stored_research && (mutation_type in stored_research.discovered_mutations))
+
+ mutation_data["Alias"] = HM.alias
+ mutation_data["Sequence"] = text_sequence
+ mutation_data["DefaultSeq"] = default_sequence
+ mutation_data["Discovered"] = discovered
+ mutation_data["Source"] = "occupant"
+
+ // We only want to pass this information along to the tgui interface if
+ // the mutation has been discovered. Prevents people being able to cheese
+ // or "hack" their way to figuring out what undiscovered mutations are
+ if(discovered)
+ mutation_data["Name"] = HM.name
+ mutation_data["Description"] = HM.desc
+ mutation_data["Instability"] = HM.instability * GET_MUTATION_STABILIZER(HM)
+ mutation_data["Quality"] = HM.quality
+
+ // Assume the mutation is normal unless assigned otherwise.
+ var/mut_class = MUT_NORMAL
+
+ // Check if the mutation is currently activated. If it is, we can add even
+ // MORE information to send to tgui.
+ var/datum/mutation/human/A = scanner_occupant.dna.get_mutation(mutation_type)
+ if(A)
+ mutation_data["Active"] = TRUE
+ mutation_data["Scrambled"] = A.scrambled
+ mutation_data["Class"] = A.class
+ mut_class = A.class
+ mutation_data["CanChromo"] = A.can_chromosome
+ mutation_data["ByondRef"] = REF(A)
+ mutation_data["Type"] = A.type
+ if(A.can_chromosome)
+ mutation_data["ValidChromos"] = jointext(A.valid_chrom_list, ", ")
+ mutation_data["AppliedChromo"] = A.chromosome_name
+ mutation_data["ValidStoredChromos"] = build_chrom_list(A)
+ else
+ mutation_data["Active"] = FALSE
+ mutation_data["Scrambled"] = FALSE
+ mutation_data["Class"] = MUT_NORMAL
+
+ // Technically NONE of these mutations should be MUT_EXTRA but this will
+ // catch any weird edge cases
+ // Assign icons by priority - MUT_EXTRA will ALSO be discovered, so it
+ // has a higher priority for icon/image assignment
+ if (mut_class == MUT_EXTRA)
+ mutation_data["Image"] = "dna_extra.gif"
+ else if(discovered)
+ mutation_data["Image"] = "dna_discovered.gif"
+ else
+ mutation_data["Image"] = "dna_undiscovered.gif"
+
+ tgui_occupant_mutations += list(mutation_data)
+
+ // ---------------------------------------------------------------------- //
+ // Now get additional/"extra" mutations that they shouldn't have by default
+ for(var/datum/mutation/human/HM in scanner_occupant.dna.mutations)
+ // If it's in the mutation index array, we've already catalogued this
+ // mutation and can safely skip over it. It really shouldn't be, but this
+ // will catch any weird edge cases
+ if(HM.type in scanner_occupant.dna.mutation_index)
+ continue
+
+ var/list/mutation_data = list()
+ var/text_sequence = GET_SEQUENCE(HM.type)
+
+ // These will all be active mutations. They're added by injector and their
+ // sequencing code can't be changed. They can only be nullified, which
+ // completely removes them.
+ var/datum/mutation/human/A = GET_INITIALIZED_MUTATION(HM.type)
+
+ mutation_data["Alias"] = A.alias
+ mutation_data["Sequence"] = text_sequence
+ mutation_data["Discovered"] = TRUE
+ mutation_data["Quality"] = HM.quality
+ mutation_data["Source"] = "occupant"
+
+ mutation_data["Name"] = HM.name
+ mutation_data["Description"] = HM.desc
+ mutation_data["Instability"] = HM.instability * GET_MUTATION_STABILIZER(HM)
+
+ mutation_data["Active"] = TRUE
+ mutation_data["Scrambled"] = HM.scrambled
+ mutation_data["Class"] = HM.class
+ mutation_data["CanChromo"] = HM.can_chromosome
+ mutation_data["ByondRef"] = REF(HM)
+ mutation_data["Type"] = HM.type
+
+ if(HM.can_chromosome)
+ mutation_data["ValidChromos"] = jointext(HM.valid_chrom_list, ", ")
+ mutation_data["AppliedChromo"] = HM.chromosome_name
+ mutation_data["ValidStoredChromos"] = build_chrom_list(HM)
+
+ // Nothing in this list should be undiscovered. Technically nothing
+ // should be anything but EXTRA. But we're just handling some edge cases.
+ if (HM.class == MUT_EXTRA)
+ mutation_data["Image"] = "dna_extra.gif"
+ else
+ mutation_data["Image"] = "dna_discovered.gif"
+
+ tgui_occupant_mutations += list(mutation_data)
+
+ // ------------------------------------------------------------------------ //
+ // Build the list of mutations stored within the DNA Console
+ for(var/datum/mutation/human/HM in stored_mutations)
+ var/list/mutation_data = list()
+
+ var/datum/mutation/human/A = GET_INITIALIZED_MUTATION(HM.type)
+
+ mutation_data["Alias"] = A.alias
+ mutation_data["Name"] = HM.name
+ mutation_data["Source"] = "console"
+ mutation_data["Active"] = TRUE
+ mutation_data["Description"] = HM.desc
+ mutation_data["Instability"] = HM.instability * GET_MUTATION_STABILIZER(HM)
+ mutation_data["ByondRef"] = REF(HM)
+ mutation_data["Type"] = HM.type
+
+ mutation_data["CanChromo"] = HM.can_chromosome
+ if(HM.can_chromosome)
+ mutation_data["ValidChromos"] = jointext(HM.valid_chrom_list, ", ")
+ mutation_data["AppliedChromo"] = HM.chromosome_name
+ mutation_data["ValidStoredChromos"] = build_chrom_list(HM)
+
+ tgui_console_mutations += list(mutation_data)
+
+ // ------------------------------------------------------------------------ //
+ // Build the list of chromosomes stored within the DNA Console
+ var/chrom_index = 1
+ for(var/obj/item/chromosome/CM in stored_chromosomes)
+ var/list/chromo_data = list()
+
+ chromo_data["Name"] = CM.name
+ chromo_data["Description"] = CM.desc
+ chromo_data["Index"] = chrom_index
+
+ tgui_console_chromosomes += list(chromo_data)
+ ++chrom_index
+
+ // ------------------------------------------------------------------------ //
+ // Build the list of mutations stored on any inserted diskettes
+ if(diskette)
+ for(var/datum/mutation/human/HM in diskette.mutations)
+ var/list/mutation_data = list()
+
+ var/datum/mutation/human/A = GET_INITIALIZED_MUTATION(HM.type)
+
+ mutation_data["Alias"] = A.alias
+ mutation_data["Name"] = HM.name
+ mutation_data["Active"] = TRUE
+ //mutation_data["Sequence"] = GET_SEQUENCE(HM.type)
+ mutation_data["Source"] = "disk"
+ mutation_data["Description"] = HM.desc
+ mutation_data["Instability"] = HM.instability * GET_MUTATION_STABILIZER(HM)
+ mutation_data["ByondRef"] = REF(HM)
+ mutation_data["Type"] = HM.type
+
+ mutation_data["CanChromo"] = HM.can_chromosome
+ if(HM.can_chromosome)
+ mutation_data["ValidChromos"] = jointext(HM.valid_chrom_list, ", ")
+ mutation_data["AppliedChromo"] = HM.chromosome_name
+ mutation_data["ValidStoredChromos"] = build_chrom_list(HM)
+
+ tgui_diskette_mutations += list(mutation_data)
+
+ // ------------------------------------------------------------------------ //
+ // Build the list of mutations stored within any Advanced Injectors
+ if(LAZYLEN(injector_selection))
+ for(var/I in injector_selection)
+ var/list/mutations = list()
+ for(var/datum/mutation/human/HM in injector_selection[I])
+ var/list/mutation_data = list()
+
+ var/datum/mutation/human/A = GET_INITIALIZED_MUTATION(HM.type)
+
+ mutation_data["Alias"] = A.alias
+ mutation_data["Name"] = HM.name
+ mutation_data["Active"] = TRUE
+ //mutation_data["Sequence"] = GET_SEQUENCE(HM.type)
+ mutation_data["Source"] = "injector"
+ mutation_data["Description"] = HM.desc
+ mutation_data["Instability"] = HM.instability * GET_MUTATION_STABILIZER(HM)
+ mutation_data["ByondRef"] = REF(HM)
+ mutation_data["Type"] = HM.type
+
+ if(HM.can_chromosome)
+ mutation_data["AppliedChromo"] = HM.chromosome_name
+
+ mutations += list(mutation_data)
+ tgui_advinjector_mutations += list(list(
+ "name" = "[I]",
+ "mutations" = mutations,
+ ))
+
+/**
+ * Takes any given chromosome and calculates chromosome compatibility
+ *
+ * Will iterate over the stored chromosomes in the DNA Console and will check
+ * whether it can be applied to the supplied mutation. Then returns a list of
+ * names of chromosomes that were compatible.
+ *
+ * Arguments:
+ * * mutation - The mutation to check chromosome compatibility with
+ */
+/obj/machinery/computer/scan_consolenew/proc/build_chrom_list(mutation)
+ var/list/chromosomes = list()
+
+ for(var/obj/item/chromosome/CM in stored_chromosomes)
+ if(CM.can_apply(mutation))
+ chromosomes += CM.name
+
+ return chromosomes
+
+/**
+ * Checks whether a mutation alias has been discovered
+ *
+ * Checks whether a given mutation's genetic sequence has been completed and
+ * discovers it if appropriate
+ *
+ * Arguments:
+ * * alias - Alias of the mutation to check (ie "Mutation 51" or "Mutation 12")
+ */
+/obj/machinery/computer/scan_consolenew/proc/check_discovery(alias)
+ // Note - All code paths that call this have already done checks on the
+ // current occupant to prevent cheese and other abuses. If you call this
+ // proc please also do the following checks first:
+ // if(!can_modify_occupant())
+ // return
+ // if(!(scanner_occupant == connected_scanner.occupant))
+ // return
+
+ // Turn the alias ("Mutation 1", "Mutation 35") into a mutation path
+ var/path = GET_MUTATION_TYPE_FROM_ALIAS(alias)
+
+ // Check to see if this mutation is in the active mutation list. If it isn't,
+ // then the mutation isn't eligible for discovery. If it is but is scrambled,
+ // then the mutation isn't eligible for discovery. Finally, check if the
+ // mutation is in discovered mutations - If it isn't, add it to discover.
+ var/datum/mutation/human/M = scanner_occupant.dna.get_mutation(path)
+ if(!M)
+ return FALSE
+ if(M.scrambled)
+ return FALSE
+ if(stored_research && !(path in stored_research.discovered_mutations))
+ var/datum/mutation/human/HM = GET_INITIALIZED_MUTATION(path)
+ stored_research.discovered_mutations += path
+ say("Successfully discovered [HM.name].")
+ return TRUE
+
+ return FALSE
+
+/**
+ * Find a mutation from various storage locations via ATOM ref
+ *
+ * Takes an ATOM Ref and searches the appropriate mutation buffers and storage
+ * vars to try and find the associated mutation.
+ *
+ * Arguments:
+ * * ref - ATOM ref of the mutation to locate
+ * * target_flags - Flags for storage mediums to search, see #defines
+ */
+/obj/machinery/computer/scan_consolenew/proc/get_mut_by_ref(ref, target_flags)
+ var/mutation
+
+ // Assume the occupant is valid and the check has been carried out before
+ // calling this proc with the relevant flags.
+ if(target_flags & SEARCH_OCCUPANT)
+ mutation = (locate(ref) in scanner_occupant.dna.mutations)
+ if(mutation)
+ return mutation
+
+ if(target_flags & SEARCH_STORED)
+ mutation = (locate(ref) in stored_mutations)
+ if(mutation)
+ return mutation
+
+ if(diskette && (target_flags & SEARCH_DISKETTE))
+ mutation = (locate(ref) in diskette.mutations)
+ if(mutation)
+ return mutation
+
+ if(injector_selection && (target_flags & SEARCH_ADV_INJ))
+ for(var/I in injector_selection)
+ mutation = (locate(ref) in injector_selection["[I]"])
+ if(mutation)
+ return mutation
+
+ return null
+
+/**
+ * Creates a randomised accuracy value for the enzyme pulse functionality.
+ *
+ * Donor code from previous DNA Console iteration.
+ *
+ * Arguments:
+ * * position - Index of the intended enzyme element to pulse
+ * * radduration - Duration of intended radiation pulse
+ * * number_of_blocks - Number of individual data blocks in the pulsed enzyme
+ */
+/obj/machinery/computer/scan_consolenew/proc/randomize_radiation_accuracy(position, radduration, number_of_blocks)
+ var/val = round(gaussian(0, RADIATION_ACCURACY_MULTIPLIER/radduration) + position, 1)
+ return WRAP(val, 1, number_of_blocks+1)
+
+/**
+ * Scrambles an enzyme element value for the enzyme pulse functionality.
+ *
+ * Donor code from previous DNA Console iteration.
+ *
+ * Arguments:
+ * * input - Enzyme identity element to scramble, expected hex value
+ * * rs - Strength of radiation pulse, increases the range of possible outcomes
+ */
+/obj/machinery/computer/scan_consolenew/proc/scramble(input,rs)
var/length = length(input)
var/ran = gaussian(0, rs*RADIATION_STRENGTH_MULTIPLIER)
if(ran == 0)
@@ -940,98 +1953,71 @@
ran = -round(-ran) //positive, so ceiling it
return num2hex(WRAP(hex2num(input)+ran, 0, 16**length), length)
-/obj/machinery/computer/scan_consolenew/proc/randomize_radiation_accuracy(position, radduration, number_of_blocks)
- var/val = round(gaussian(0, RADIATION_ACCURACY_MULTIPLIER/radduration) + position, 1)
- return WRAP(val, 1, number_of_blocks+1)
+ /**
+ * Performs the enzyme radiation pulse.
+ *
+ * Donor code from previous DNA Console iteration. Called from process() when
+ * there is a radiation pulse in progress. Ends processing.
+ */
+/obj/machinery/computer/scan_consolenew/proc/rad_pulse()
+ // GUARD CHECK - Can we genetically modify the occupant? Includes scanner
+ // operational guard checks.
+ // If we can't, abort the procedure.
+ if(!can_modify_occupant())
+ rad_pulse_index = 0
+ STOP_PROCESSING(SSobj, src)
+ return
-/obj/machinery/computer/scan_consolenew/proc/get_viable_occupant()
- var/mob/living/carbon/viable_occupant = null
- if(connected)
- viable_occupant = connected.occupant
- if(!istype(viable_occupant) || !viable_occupant.dna || HAS_TRAIT_NOT_FROM(viable_occupant, TRAIT_RADIMMUNE,BLOODSUCKER_TRAIT) || HAS_TRAIT(viable_occupant, TRAIT_NOCLONE))
- viable_occupant = null
- return viable_occupant
+ var/len = length_char(scanner_occupant.dna.uni_identity)
+ var/num = randomize_radiation_accuracy(rad_pulse_index, radduration + (connected_scanner.precision_coeff ** 2), len) //Each manipulator level above 1 makes randomization as accurate as selected time + manipulator lvl^2 //Value is this high for the same reason as with laser - not worth the hassle of upgrading if the bonus is low
+ var/hex = copytext_char(scanner_occupant.dna.uni_identity, num, num+1)
+ hex = scramble(hex, radstrength, radduration)
-/obj/machinery/computer/scan_consolenew/proc/apply_buffer(action,buffer_num)
- buffer_num = clamp(buffer_num, 1, NUMBER_OF_BUFFERS)
- var/list/buffer_slot = buffer[buffer_num]
- var/mob/living/carbon/viable_occupant = get_viable_occupant()
- if(istype(buffer_slot))
- viable_occupant.radiation += rand(100/(connected.damage_coeff ** 2),250/(connected.damage_coeff ** 2))
- //15 and 40 are just magic numbers that were here before so i didnt touch them, they are initial boundaries of damage
- //Each laser level reduces damage by lvl^2, so no effect on 1 lvl, 4 times less damage on 2 and 9 times less damage on 3
- //Numbers are this high because other way upgrading laser is just not worth the hassle, and i cant think of anything better to inmrove
- switch(action)
- if(SCANNER_ACTION_UI)
- if(buffer_slot["UI"])
- viable_occupant.dna.uni_identity = buffer_slot["UI"]
- viable_occupant.updateappearance(mutations_overlay_update=1)
- if(SCANNER_ACTION_UE)
- if(buffer_slot["name"] && buffer_slot["UE"] && buffer_slot["blood_type"])
- viable_occupant.real_name = buffer_slot["name"]
- viable_occupant.name = buffer_slot["name"]
- viable_occupant.dna.unique_enzymes = buffer_slot["UE"]
- viable_occupant.dna.blood_type = buffer_slot["blood_type"]
- if(SCANNER_ACTION_MIXED)
- if(buffer_slot["UI"])
- viable_occupant.dna.uni_identity = buffer_slot["UI"]
- viable_occupant.updateappearance(mutations_overlay_update=1)
- if(buffer_slot["name"] && buffer_slot["UE"] && buffer_slot["blood_type"])
- viable_occupant.real_name = buffer_slot["name"]
- viable_occupant.name = buffer_slot["name"]
- viable_occupant.dna.unique_enzymes = buffer_slot["UE"]
- viable_occupant.dna.blood_type = buffer_slot["blood_type"]
+ scanner_occupant.dna.uni_identity = copytext_char(scanner_occupant.dna.uni_identity, 1, num) + hex + copytext_char(scanner_occupant.dna.uni_identity, num + 1)
+ scanner_occupant.updateappearance(mutations_overlay_update=1)
-/obj/machinery/computer/scan_consolenew/proc/on_scanner_close()
- if(delayed_action && get_viable_occupant())
- to_chat(connected.occupant, "[src] activates!")
- apply_buffer(delayed_action["action"],delayed_action["buffer"])
- delayed_action = null //or make it stick + reset button ?
+ rad_pulse_index = 0
+ STOP_PROCESSING(SSobj, src)
+ return
-/obj/machinery/computer/scan_consolenew/proc/get_valid_mutation(mutation)
- var/mob/living/carbon/C = get_viable_occupant()
- if(C)
- var/datum/mutation/human/HM = C.dna.get_mutation(mutation)
- if(HM)
- return HM
- for(var/datum/mutation/human/A in stored_mutations)
- if(A.type == mutation)
- return A
+/**
+ * Sets the default state for the tgui interface.
+ */
+/obj/machinery/computer/scan_consolenew/proc/set_default_state()
+ tgui_view_state["consoleMode"] = "storage"
+ tgui_view_state["storageMode"] = "console"
+ tgui_view_state["storageConsSubMode"] = "mutations"
+ tgui_view_state["storageDiskSubMode"] = "mutations"
+/**
+ * Ejects the DNA Disk from the console.
+ *
+ * Will insert into the user's hand if possible, otherwise will drop it at the
+ * console's location.
+ *
+ * Arguments:
+ * * user - The mob that is attempting to eject the diskette.
+ */
+/obj/machinery/computer/scan_consolenew/proc/eject_disk(mob/user)
+ // Check for diskette.
+ if(!diskette)
+ return
-/obj/machinery/computer/scan_consolenew/proc/get_mutation_list(include_storage) //Returns a list of the mutation index types and any extra mutations
- var/mob/living/carbon/viable_occupant = get_viable_occupant()
- var/list/paths = list()
- if(viable_occupant)
- for(var/A in viable_occupant.dna.mutation_index)
- paths += A
- for(var/datum/mutation/human/A in viable_occupant.dna.mutations)
- if(A.class == MUT_EXTRA)
- paths += A.type
- if(include_storage)
- for(var/datum/mutation/human/A in stored_mutations)
- paths += A.type
- return paths
+ to_chat(user, "You eject [diskette] from [src].")
-/obj/machinery/computer/scan_consolenew/proc/get_valid_gene_string(mutation)
- var/mob/living/carbon/C = get_viable_occupant()
- if(C && (mutation in C.dna.mutation_index))
- return GET_GENE_STRING(mutation, C.dna)
- else if(C && (LAZYLEN(C.dna.mutations)))
- for(var/datum/mutation/human/A in C.dna.mutations)
- if(A.type == mutation)
- return GET_SEQUENCE(mutation)
- for(var/datum/mutation/human/A in stored_mutations)
- if(A.type == mutation)
- return GET_SEQUENCE(mutation)
+ // Reset the state to console storage.
+ tgui_view_state["storageMode"] = "console"
+
+ // If the disk shouldn't pop into the user's hand for any reason, drop it on the console instead.
+ if(!istype(user) || !Adjacent(user) || !user.put_in_active_hand(diskette))
+ diskette.forceMove(drop_location())
+ diskette = null
-/obj/machinery/computer/scan_consolenew/proc/discover(mutation)
- if(stored_research && !(mutation in stored_research.discovered_mutations))
- stored_research.discovered_mutations += mutation
- return TRUE
-/////////////////////////// DNA MACHINES
#undef INJECTOR_TIMEOUT
#undef NUMBER_OF_BUFFERS
+#undef SCRAMBLE_TIMEOUT
+#undef JOKER_TIMEOUT
+#undef JOKER_UPGRADE
#undef RADIATION_STRENGTH_MAX
#undef RADIATION_STRENGTH_MULTIPLIER
@@ -1041,11 +2027,9 @@
#undef RADIATION_IRRADIATION_MULTIPLIER
-#undef SCANNER_ACTION_SE
-#undef SCANNER_ACTION_UI
-#undef SCANNER_ACTION_UE
-#undef SCANNER_ACTION_MIXED
+#undef STATUS_TRANSFORMING
-//#undef BAD_MUTATION_DIFFICULTY
-//#undef GOOD_MUTATION_DIFFICULTY
-//#undef OP_MUTATION_DIFFICULTY
+#undef SEARCH_OCCUPANT
+#undef SEARCH_STORED
+#undef SEARCH_DISKETTE
+#undef SEARCH_ADV_INJ
diff --git a/code/game/machinery/computer/launchpad_control.dm b/code/game/machinery/computer/launchpad_control.dm
index 1924cd9f23..fdbe2f1060 100644
--- a/code/game/machinery/computer/launchpad_control.dm
+++ b/code/game/machinery/computer/launchpad_control.dm
@@ -56,7 +56,7 @@
/obj/machinery/computer/launchpad/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, "launchpad_console", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, ui_key, "LaunchpadConsole", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/computer/launchpad/ui_data(mob/user)
diff --git a/code/game/machinery/computer/prisoner/gulag_teleporter.dm b/code/game/machinery/computer/prisoner/gulag_teleporter.dm
index ca75ff1dd0..dd925fbe3b 100644
--- a/code/game/machinery/computer/prisoner/gulag_teleporter.dm
+++ b/code/game/machinery/computer/prisoner/gulag_teleporter.dm
@@ -25,7 +25,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, "gulag_console", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, ui_key, "GulagTeleporterConsole", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/computer/prisoner/gulag_teleporter_computer/ui_data(mob/user)
diff --git a/code/game/machinery/computer/robot.dm b/code/game/machinery/computer/robot.dm
index 2621616759..4fd9665b1d 100644
--- a/code/game/machinery/computer/robot.dm
+++ b/code/game/machinery/computer/robot.dm
@@ -5,163 +5,123 @@
icon_keyboard = "rd_key"
req_access = list(ACCESS_ROBOTICS)
circuit = /obj/item/circuitboard/computer/robotics
- var/temp = null
-
light_color = LIGHT_COLOR_PINK
+ ui_x = 500
+ ui_y = 460
/obj/machinery/computer/robotics/proc/can_control(mob/user, mob/living/silicon/robot/R)
+ . = FALSE
if(!istype(R))
- return FALSE
+ return
if(isAI(user))
- if (R.connected_ai != user)
- return FALSE
+ if(R.connected_ai != user)
+ return
if(iscyborg(user))
- if (R != user)
- return FALSE
+ if(R != user)
+ return
if(R.scrambledcodes)
- return FALSE
- if (hasSiliconAccessInArea(user) && !issilicon(user))
- if (!Adjacent(user))
- return FALSE
+ return
return TRUE
-/obj/machinery/computer/robotics/ui_interact(mob/user)
- . = ..()
- if (src.z > 6)
- to_chat(user, "Unable to establish a connection: \black You're too far away from the station!")
- return
- user.set_machine(src)
- var/dat
- var/robots = 0
+/obj/machinery/computer/robotics/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, "RoboticsControlConsole", name, ui_x, ui_y, master_ui, state)
+ ui.open()
+
+/obj/machinery/computer/robotics/ui_data(mob/user)
+ var/list/data = list()
+
+ data["can_hack"] = FALSE
+ if(issilicon(user))
+ var/mob/living/silicon/S = user
+ if(S.hack_software)
+ data["can_hack"] = TRUE
+ else if(IsAdminGhost(user))
+ data["can_hack"] = TRUE
+
+ data["cyborgs"] = list()
for(var/mob/living/silicon/robot/R in GLOB.silicon_mobs)
if(!can_control(user, R))
continue
- robots++
- dat += "[R.name] |"
- if(R.stat)
- dat += " Not Responding |"
- else if(R.locked_down)
- dat += " Locked Down |"
- else
- dat += " Operating Normally |"
- if(R.cell)
- dat += " Battery Installed ([R.cell.charge]/[R.cell.maxcharge]) |"
- else
- dat += " No Cell Installed |"
- if(R.module)
- dat += " Module Installed ([R.module.name]) |"
- else
- dat += " No Module Installed |"
- if(R.connected_ai)
- dat += " Slaved to [R.connected_ai.name] |"
- else
- dat += " Independent from AI |"
- if(issilicon(user) && user != R)
- var/mob/living/silicon/S = user
- if(is_servant_of_ratvar(S))
- dat += "(Convert) "
- else if(S.hack_software && !R.emagged)
- dat += "(Hack) "
- else if(IsAdminGhost(user) && !R.emagged)
- dat += "(Hack) "
- dat += "([R.locked_down? "Lockdown" : "Release"]) "
- dat += "(Destroy)"
- dat += "
"
+ if(z != (get_turf(R)).z)
+ continue
+ var/list/cyborg_data = list(
+ name = R.name,
+ locked_down = R.locked_down,
+ status = R.stat,
+ charge = R.cell ? round(R.cell.percent()) : null,
+ module = R.module ? "[R.module.name] Module" : "No Module Detected",
+ synchronization = R.connected_ai,
+ emagged = R.emagged,
+ ref = REF(R)
+ )
+ data["cyborgs"] += list(cyborg_data)
- if(!robots)
- dat += "No Cyborg Units detected within access parameters."
- dat += "
"
-
- var/drones = 0
+ data["drones"] = list()
for(var/mob/living/simple_animal/drone/D in GLOB.drones_list)
if(D.hacked)
continue
- drones++
- dat += "[D.name] |"
- if(D.stat)
- dat += " Not Responding |"
- dat += "(Destroy)"
- dat += "
"
+ if(z != (get_turf(D)).z)
+ continue
+ var/list/drone_data = list(
+ name = D.name,
+ status = D.stat,
+ ref = REF(D)
+ )
+ data["drones"] += list(drone_data)
- if(!drones)
- dat += "No Drone Units detected within access parameters."
+ return data
- var/datum/browser/popup = new(user, "computer", "Cyborg Control Console", 400, 500)
- popup.set_content(dat)
- popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
- popup.open()
- return
-
-/obj/machinery/computer/robotics/Topic(href, href_list)
+/obj/machinery/computer/robotics/ui_act(action, params)
if(..())
return
- if (href_list["temp"])
- src.temp = null
-
- else if (href_list["killbot"])
- if(src.allowed(usr))
- var/mob/living/silicon/robot/R = locate(href_list["killbot"]) in GLOB.silicon_mobs
- if(can_control(usr, R))
- var/choice = input("Are you certain you wish to detonate [R.name]?") in list("Confirm", "Abort")
- if(choice == "Confirm" && can_control(usr, R) && !..())
+ switch(action)
+ if("killbot")
+ if(allowed(usr))
+ var/mob/living/silicon/robot/R = locate(params["ref"]) in GLOB.silicon_mobs
+ if(can_control(usr, R) && !..())
var/turf/T = get_turf(R)
message_admins("[ADMIN_LOOKUPFLW(usr)] detonated [key_name_admin(R, R.client)] at [ADMIN_VERBOSEJMP(T)]!")
log_game("\[key_name(usr)] detonated [key_name(R)]!")
if(R.connected_ai)
to_chat(R.connected_ai, "
ALERT - Cyborg detonation detected: [R.name]
")
R.self_destruct()
- else
- to_chat(usr, "Access Denied.")
-
- else if (href_list["stopbot"])
- if(src.allowed(usr))
- var/mob/living/silicon/robot/R = locate(href_list["stopbot"]) in GLOB.silicon_mobs
- if(can_control(usr, R))
- var/choice = input("Are you certain you wish to [!R.locked_down ? "lock down" : "release"] [R.name]?") in list("Confirm", "Abort")
- if(choice == "Confirm" && can_control(usr, R) && !..())
- message_admins("[ADMIN_LOOKUPFLW(usr)] [!R.locked_down ? "locked down" : "released"] [key_name(R, R.client)][ADMIN_LOOKUPFLW(R)]!")
+ else
+ to_chat(usr, "Access Denied.")
+ if("stopbot")
+ if(allowed(usr))
+ var/mob/living/silicon/robot/R = locate(params["ref"]) in GLOB.silicon_mobs
+ if(can_control(usr, R) && !..())
+ message_admins("[ADMIN_LOOKUPFLW(usr)] [!R.locked_down ? "locked down" : "released"] [ADMIN_LOOKUPFLW(R)]!")
log_game("[key_name(usr)] [!R.locked_down ? "locked down" : "released"] [key_name(R)]!")
R.SetLockdown(!R.locked_down)
to_chat(R, "[!R.locked_down ? "Your lockdown has been lifted!" : "You have been locked down!"]")
if(R.connected_ai)
to_chat(R.connected_ai, "[!R.locked_down ? "NOTICE - Cyborg lockdown lifted" : "ALERT - Cyborg lockdown detected"]: [R.name]
")
-
- else
- to_chat(usr, "Access Denied.")
-
- else if (href_list["magbot"])
- var/mob/living/silicon/S = usr
- if((istype(S) && S.hack_software) || IsAdminGhost(usr))
- var/mob/living/silicon/robot/R = locate(href_list["magbot"]) in GLOB.silicon_mobs
- if(istype(R) && !R.emagged && (R.connected_ai == usr || IsAdminGhost(usr)) && !R.scrambledcodes && can_control(usr, R))
- log_game("[key_name(usr)] emagged [key_name(R)] using robotic console!")
- message_admins("[ADMIN_LOOKUPFLW(usr)] emagged cyborg [key_name_admin(R)] using robotic console!")
- R.SetEmagged(1)
-
- else if(href_list["convert"])
- if(isAI(usr) && is_servant_of_ratvar(usr))
- var/mob/living/silicon/robot/R = locate(href_list["convert"]) in GLOB.silicon_mobs
- if(istype(R) && !is_servant_of_ratvar(R) && R.connected_ai == usr)
- log_game("[key_name(usr)] converted [key_name(R)] using robotic console!")
- message_admins("[ADMIN_LOOKUPFLW(usr)] converted cyborg [key_name_admin(R)] using robotic console!")
- add_servant_of_ratvar(R)
-
- else if (href_list["killdrone"])
- if(src.allowed(usr))
- var/mob/living/simple_animal/drone/D = locate(href_list["killdrone"]) in GLOB.mob_list
- if(D.hacked)
- to_chat(usr, "ERROR: [D] is not responding to external commands.")
else
- var/turf/T = get_turf(D)
- message_admins("[ADMIN_LOOKUPFLW(usr)] detonated [key_name_admin(D)] at [ADMIN_VERBOSEJMP(T)]!")
- log_game("[key_name(usr)] detonated [key_name(D)]!")
- var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
- s.set_up(3, 1, D)
- s.start()
- D.visible_message("\the [D] self destructs!")
- D.gib()
-
-
- src.updateUsrDialog()
- return
+ to_chat(usr, "Access Denied.")
+ if("magbot")
+ var/mob/living/silicon/S = usr
+ if((istype(S) && S.hack_software) || IsAdminGhost(usr))
+ var/mob/living/silicon/robot/R = locate(params["ref"]) in GLOB.silicon_mobs
+ if(istype(R) && !R.emagged && (R.connected_ai == usr || IsAdminGhost(usr)) && !R.scrambledcodes && can_control(usr, R))
+ log_game("[key_name(usr)] emagged [key_name(R)] using robotic console!")
+ message_admins("[ADMIN_LOOKUPFLW(usr)] emagged cyborg [key_name_admin(R)] using robotic console!")
+ R.SetEmagged(TRUE)
+ if("killdrone")
+ if(allowed(usr))
+ var/mob/living/simple_animal/drone/D = locate(params["ref"]) in GLOB.mob_list
+ if(D.hacked)
+ to_chat(usr, "ERROR: [D] is not responding to external commands.")
+ else
+ var/turf/T = get_turf(D)
+ message_admins("[ADMIN_LOOKUPFLW(usr)] detonated [key_name_admin(D)] at [ADMIN_VERBOSEJMP(T)]!")
+ log_game("[key_name(usr)] detonated [key_name(D)]!")
+ var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
+ s.set_up(3, TRUE, D)
+ s.start()
+ D.visible_message("\the [D] self destructs!")
+ D.gib()
diff --git a/code/game/machinery/computer/security.dm b/code/game/machinery/computer/security.dm
index 25f4237439..5c280eeace 100644
--- a/code/game/machinery/computer/security.dm
+++ b/code/game/machinery/computer/security.dm
@@ -662,7 +662,7 @@ What a mess.*/
GLOB.data_core.removeMajorCrime(active1.fields["id"], href_list["cdataid"])
if("notes")
if(istype(active2, /datum/data/record))
- var/t1 = stripped_input(usr, "Please summarize notes:", "Secure. records", active2.fields["notes"], null)
+ var/t1 = stripped_multiline_input(usr, "Please summarize notes:", "Secure records", active2.fields["notes"], 8192)
if(!canUseSecurityRecordsConsole(usr, t1, null, a2))
return
active2.fields["notes"] = t1
diff --git a/code/game/machinery/computer/station_alert.dm b/code/game/machinery/computer/station_alert.dm
index fcb18b4dff..b4340b9350 100644
--- a/code/game/machinery/computer/station_alert.dm
+++ b/code/game/machinery/computer/station_alert.dm
@@ -20,7 +20,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, "station_alert", name, 325, 500, master_ui, state)
+ ui = new(user, src, ui_key, "StationAlertConsole", name, 325, 500, master_ui, state)
ui.open()
/obj/machinery/computer/station_alert/ui_data(mob/user)
diff --git a/code/game/machinery/computer/teleporter.dm b/code/game/machinery/computer/teleporter.dm
index 6710258626..e7957a5b75 100644
--- a/code/game/machinery/computer/teleporter.dm
+++ b/code/game/machinery/computer/teleporter.dm
@@ -38,7 +38,7 @@ obj/machinery/computer/teleporter/ui_interact(mob/user, ui_key = "main", datum/t
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, "teleporter", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, ui_key, "Teleporter", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/computer/teleporter/ui_data(mob/user)
diff --git a/code/game/machinery/defibrillator_mount.dm b/code/game/machinery/defibrillator_mount.dm
index 5c13bfdf5d..f91fd66fb5 100644
--- a/code/game/machinery/defibrillator_mount.dm
+++ b/code/game/machinery/defibrillator_mount.dm
@@ -59,7 +59,7 @@
return defib.get_cell()
//defib interaction
-/obj/machinery/defibrillator_mount/attack_hand(mob/living/user)
+/obj/machinery/defibrillator_mount/attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
if(!defib)
to_chat(user, "There's no defibrillator unit loaded!")
return
diff --git a/code/game/machinery/dish_drive.dm b/code/game/machinery/dish_drive.dm
index 31e6a3cfeb..5bf16d4638 100644
--- a/code/game/machinery/dish_drive.dm
+++ b/code/game/machinery/dish_drive.dm
@@ -31,7 +31,7 @@
if(user.Adjacent(src))
. += "Alt-click it to beam its contents to any nearby disposal bins."
-/obj/machinery/dish_drive/attack_hand(mob/living/user)
+/obj/machinery/dish_drive/attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
if(!contents.len)
to_chat(user, "There's nothing in [src]!")
return
diff --git a/code/game/machinery/dna_scanner.dm b/code/game/machinery/dna_scanner.dm
index 4b2ba85d11..95552d931b 100644
--- a/code/game/machinery/dna_scanner.dm
+++ b/code/game/machinery/dna_scanner.dm
@@ -15,6 +15,7 @@
var/precision_coeff
var/message_cooldown
var/breakout_time = 1200
+ var/obj/machinery/computer/scan_consolenew/linked_console = null
/obj/machinery/dna_scannernew/RefreshParts()
scan_level = 0
@@ -22,8 +23,8 @@
precision_coeff = 0
for(var/obj/item/stock_parts/scanning_module/P in component_parts)
scan_level += P.rating
- for(var/obj/item/stock_parts/matter_bin/P in component_parts)
- precision_coeff = P.rating
+ for(var/obj/item/stock_parts/matter_bin/M in component_parts)
+ precision_coeff = M.rating
for(var/obj/item/stock_parts/micro_laser/P in component_parts)
damage_coeff = P.rating
@@ -31,11 +32,8 @@
. = ..()
if(in_range(user, src) || isobserver(user))
. += "The status display reads: Radiation pulse accuracy increased by factor [precision_coeff**2].
Radiation pulse damage decreased by factor [damage_coeff**2]."
- if(scan_level >= 3)
- . += "Scanner has been upgraded to support autoprocessing."
/obj/machinery/dna_scannernew/update_icon_state()
-
//no power or maintenance
if(stat & (NOPOWER|BROKEN))
icon_state = initial(icon_state)+ (state_open ? "_open" : "") + "_unpowered"
@@ -53,10 +51,6 @@
//running
icon_state = initial(icon_state)+ (state_open ? "_open" : "")
-/obj/machinery/dna_scannernew/power_change()
- ..()
- update_icon()
-
/obj/machinery/dna_scannernew/proc/toggle_open(mob/user)
if(panel_open)
to_chat(user, "Close the maintenance panel first.")
@@ -80,7 +74,7 @@
user.last_special = world.time + CLICK_CD_BREAKOUT
user.visible_message("You see [user] kicking against the door of [src]!", \
"You lean on the back of [src] and start pushing the door open... (this will take about [DisplayTimeText(breakout_time)].)", \
- "You hear a metallic creaking from [src].")
+ "You hear a metallic creaking from [src].")
if(do_after(user,(breakout_time), target = src))
if(!user || user.stat != CONSCIOUS || user.loc != src || state_open || !locked)
return
@@ -96,33 +90,28 @@
return C
return null
-/obj/machinery/dna_scannernew/close_machine(atom/movable/target)
+/obj/machinery/dna_scannernew/close_machine(mob/living/carbon/user)
if(!state_open)
return FALSE
- ..(target)
-
- // search for ghosts, if the corpse is empty and the scanner is connected to a cloner
- var/mob/living/mob_occupant = get_mob_or_brainmob(occupant)
- if(istype(mob_occupant))
- if(locate_computer(/obj/machinery/computer/cloning))
- if(!mob_occupant.suiciding && !(HAS_TRAIT(mob_occupant, TRAIT_NOCLONE)) && !mob_occupant.hellbound)
- mob_occupant.notify_ghost_cloning("Your corpse has been placed into a cloning scanner. Re-enter your corpse if you want to be cloned!", source = src)
+ ..(user)
// DNA manipulators cannot operate on severed heads or brains
- if(isliving(occupant))
- var/obj/machinery/computer/scan_consolenew/console = locate_computer(/obj/machinery/computer/scan_consolenew)
- if(console)
- console.on_scanner_close()
+ if(iscarbon(occupant))
+ if(linked_console)
+ linked_console.on_scanner_close()
return TRUE
/obj/machinery/dna_scannernew/open_machine()
- if(state_open || panel_open)
+ if(state_open)
return FALSE
..()
+ if(linked_console)
+ linked_console.on_scanner_open()
+
return TRUE
/obj/machinery/dna_scannernew/relaymove(mob/user as mob)
@@ -133,51 +122,49 @@
return
open_machine()
-/obj/machinery/dna_scannernew/screwdriver_act(mob/living/user, obj/item/I)
- . = TRUE
- if(..())
- return
- if(occupant)
- to_chat(user, "[src] is currently occupied!")
- return
- if(state_open)
- to_chat(user, "[src] must be closed to [panel_open ? "close" : "open"] its maintenance hatch!")
- return
- if(default_deconstruction_screwdriver(user, icon_state, icon_state, I)) //sent icon_state is irrelevant...
- update_icon() //..since we're updating the icon here, since the scanner can be unpowered when opened/closed
- return
- return FALSE
+/obj/machinery/dna_scannernew/attackby(obj/item/I, mob/user, params)
-/obj/machinery/dna_scannernew/wrench_act(mob/living/user, obj/item/I)
- . = ..()
- if(default_change_direction_wrench(user, I))
- return TRUE
+ if(!occupant && default_deconstruction_screwdriver(user, icon_state, icon_state, I))//sent icon_state is irrelevant...
+ update_icon()//..since we're updating the icon here, since the scanner can be unpowered when opened/closed
+ return
-/obj/machinery/dna_scannernew/crowbar_act(mob/living/user, obj/item/I)
- . = ..()
if(default_pry_open(I))
- return TRUE
- if(default_deconstruction_crowbar(I))
- return TRUE
+ return
-/obj/machinery/dna_scannernew/default_pry_open(obj/item/I) //wew
- . = !(state_open || panel_open || (flags_1 & NODECONSTRUCT_1)) && I.tool_behaviour == TOOL_CROWBAR
- if(.)
- I.play_tool_sound(src, 50)
- visible_message("[usr] pries open [src].", "You pry open [src].")
- open_machine()
+ if(default_deconstruction_crowbar(I))
+ return
+
+ return ..()
/obj/machinery/dna_scannernew/interact(mob/user)
toggle_open(user)
-/obj/machinery/dna_scannernew/AltClick(mob/user)
- . = ..()
- if(!user.canUseTopic(src, !hasSiliconAccessInArea(user)))
- return
- interact(user)
- return TRUE
-
/obj/machinery/dna_scannernew/MouseDrop_T(mob/target, mob/user)
- if(user.stat || user.lying || !Adjacent(user) || !user.Adjacent(target) || !iscarbon(target) || !user.IsAdvancedToolUser())
+ var/mob/living/L = user
+ if(user.stat || (isliving(user) && (!(L.mobility_flags & MOBILITY_STAND) || !(L.mobility_flags & MOBILITY_UI))) || !Adjacent(user) || !user.Adjacent(target) || !iscarbon(target) || !user.IsAdvancedToolUser())
return
close_machine(target)
+
+
+//Just for transferring between genetics machines.
+/obj/item/disk/data
+ name = "DNA data disk"
+ icon_state = "datadisk0" //Gosh I hope syndies don't mistake them for the nuke disk.
+ var/list/genetic_makeup_buffer = list()
+ var/list/fields = list()
+ var/list/mutations = list()
+ var/max_mutations = 6
+ var/read_only = FALSE //Well,it's still a floppy disk
+
+/obj/item/disk/data/Initialize()
+ . = ..()
+ icon_state = "datadisk[rand(0,6)]"
+ add_overlay("datadisk_gene")
+
+/obj/item/disk/data/attack_self(mob/user)
+ read_only = !read_only
+ to_chat(user, "You flip the write-protect tab to [read_only ? "protected" : "unprotected"].")
+
+/obj/item/disk/data/examine(mob/user)
+ . = ..()
+ . += "The write-protect tab is set to [read_only ? "protected" : "unprotected"]."
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index 635522fa41..a950cb7e7d 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -93,7 +93,7 @@
var/shuttledocked = 0
var/delayed_close_requested = FALSE // TRUE means the door will automatically close the next time it's opened.
- var/air_tight = FALSE //TRUE means density will be set as soon as the door begins to close
+ air_tight = FALSE
var/prying_so_hard = FALSE
rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE
@@ -157,6 +157,10 @@
. = ..()
AddComponent(/datum/component/ntnet_interface)
+/obj/machinery/door/airlock/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock, idnum, override=FALSE)
+ if(id_tag)
+ id_tag = "[idnum][id_tag]"
+
/obj/machinery/door/airlock/proc/update_other_id()
for(var/obj/machinery/door/airlock/A in GLOB.airlocks)
if(A.closeOtherId == closeOtherId && A != src)
@@ -188,7 +192,7 @@
/obj/machinery/door/airlock/vv_edit_var(var_name)
. = ..()
switch (var_name)
- if ("cyclelinkeddir")
+ if (NAMEOF(src, cyclelinkeddir))
cyclelinkairlock()
/obj/machinery/door/airlock/check_access_ntnet(datum/netdata/data)
@@ -759,7 +763,7 @@
/obj/machinery/door/airlock/attack_paw(mob/user)
return attack_hand(user)
-/obj/machinery/door/airlock/attack_hand(mob/user)
+/obj/machinery/door/airlock/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
@@ -1058,11 +1062,11 @@
to_chat(user, "The airlock's bolts prevent it from being forced!")
else if( !welded && !operating)
if(!beingcrowbarred) //being fireaxe'd
- var/obj/item/twohanded/fireaxe/F = I
- if(F.wielded)
- INVOKE_ASYNC(src, (density ? .proc/open : .proc/close), 2)
- else
- to_chat(user, "You need to be wielding the fire axe to do that!")
+ var/obj/item/fireaxe/axe = I
+ if(!axe.wielded)
+ to_chat(user, "You need to be wielding \the [axe] to do that!")
+ return
+ INVOKE_ASYNC(src, (density ? .proc/open : .proc/close), 2)
else
INVOKE_ASYNC(src, (density ? .proc/open : .proc/close), 2)
@@ -1440,7 +1444,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "ai_airlock", name, 500, 390, master_ui, state)
+ ui = new(user, src, ui_key, "AiAirlock", name, 500, 390, master_ui, state)
ui.open()
return TRUE
diff --git a/code/game/machinery/doors/airlock_electronics.dm b/code/game/machinery/doors/airlock_electronics.dm
index ae6930f5fb..a0ce3954c3 100644
--- a/code/game/machinery/doors/airlock_electronics.dm
+++ b/code/game/machinery/doors/airlock_electronics.dm
@@ -2,10 +2,14 @@
name = "airlock electronics"
req_access = list(ACCESS_MAINT_TUNNELS)
custom_price = PRICE_CHEAP
-
+ /// A list of all granted accesses
var/list/accesses = list()
+ /// If the airlock should require ALL or only ONE of the listed accesses
var/one_access = 0
- var/unres_sides = 0 //unrestricted sides, or sides of the airlock that will open regardless of access
+ /// Unrestricted sides, or sides of the airlock that will open regardless of access
+ var/unres_sides = 0
+ /// A holder of the electronics, in case of them working as an integrated part
+ var/holder
/obj/item/electronics/airlock/examine(mob/user)
. = ..()
@@ -13,31 +17,37 @@
/obj/item/electronics/airlock/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state)
- SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "airlock_electronics", name, 420, 485, master_ui, state)
+ ui = new(user, src, ui_key, "AirlockElectronics", name, 420, 485, master_ui, state)
ui.open()
+/obj/item/electronics/airlock/ui_static_data(mob/user)
+ var/list/data = list()
+ var/list/regions = list()
+ for(var/i in 1 to 7)
+ var/list/accesses = list()
+ for(var/access in get_region_accesses(i))
+ if (get_access_desc(access))
+ accesses += list(list(
+ "desc" = replacetext(get_access_desc(access), " ", " "),
+ "ref" = access,
+ ))
+
+ regions += list(list(
+ "name" = get_region_accesses_name(i),
+ "regid" = i,
+ "accesses" = accesses
+ ))
+
+ data["regions"] = regions
+ return data
+
/obj/item/electronics/airlock/ui_data()
var/list/data = list()
- var/list/regions = list()
-
- for(var/i in 1 to 7)
- var/list/region = list()
- var/list/accesses = list()
- for(var/j in get_region_accesses(i))
- var/list/access = list()
- access["name"] = get_access_desc(j)
- access["id"] = j
- access["req"] = (j in src.accesses)
- accesses[++accesses.len] = access
- region["name"] = get_region_accesses_name(i)
- region["accesses"] = accesses
- regions[++regions.len] = region
- data["regions"] = regions
+ data["accesses"] = accesses
data["oneAccess"] = one_access
data["unres_direction"] = unres_sides
-
return data
/obj/item/electronics/airlock/ui_act(action, params)
@@ -48,12 +58,12 @@
accesses = list()
one_access = 0
. = TRUE
- if("one_access")
- one_access = !one_access
- . = TRUE
if("grant_all")
accesses = get_all_accesses()
. = TRUE
+ if("one_access")
+ one_access = !one_access
+ . = TRUE
if("set")
var/access = text2num(params["access"])
if (!(access in accesses))
@@ -65,3 +75,20 @@
var/unres_direction = text2num(params["unres_direction"])
unres_sides ^= unres_direction //XOR, toggles only the bit that was clicked
. = TRUE
+ if("grant_region")
+ var/region = text2num(params["region"])
+ if(isnull(region))
+ return
+ accesses |= get_region_accesses(region)
+ . = TRUE
+ if("deny_region")
+ var/region = text2num(params["region"])
+ if(isnull(region))
+ return
+ accesses -= get_region_accesses(region)
+ . = TRUE
+
+/obj/item/electronics/airlock/ui_host()
+ if(holder)
+ return holder
+ return src
diff --git a/code/game/machinery/doors/brigdoors.dm b/code/game/machinery/doors/brigdoors.dm
index 1d39372dec..5a1ed7dd19 100644
--- a/code/game/machinery/doors/brigdoors.dm
+++ b/code/game/machinery/doors/brigdoors.dm
@@ -144,7 +144,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, "brig_timer", name, 300, 138, master_ui, state)
+ ui = new(user, src, ui_key, "BrigTimer", name, 300, 138, master_ui, state)
ui.open()
//icon update function
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index 1fb50e13c6..baf8c35f46 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -17,8 +17,9 @@
interaction_flags_atom = INTERACT_ATOM_UI_INTERACT
var/secondsElectrified = 0
+ var/air_tight = FALSE //TRUE means density will be set as soon as the door begins to close
var/shockedby
- var/visible = TRUE
+ var/visible = TRUE // To explain: Whether the door can block line of sight when closed or not.
var/operating = FALSE
var/glass = FALSE
var/welded = FALSE
@@ -139,7 +140,7 @@
do_animate("deny")
return
-/obj/machinery/door/attack_hand(mob/user)
+/obj/machinery/door/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
@@ -161,7 +162,7 @@
open()
else
close()
- return
+ return TRUE
if(density)
do_animate("deny")
@@ -181,11 +182,36 @@
/obj/machinery/door/proc/try_to_crowbar(obj/item/I, mob/user)
return
+/obj/machinery/door/proc/is_holding_pressure()
+ var/turf/open/T = loc
+ if(!T)
+ return FALSE
+ if(!density)
+ return FALSE
+ // alrighty now we check for how much pressure we're holding back
+ var/min_moles = T.air.total_moles()
+ var/max_moles = min_moles
+ // okay this is a bit hacky. First, we set density to 0 and recalculate our adjacent turfs
+ density = FALSE
+ T.ImmediateCalculateAdjacentTurfs()
+ // then we use those adjacent turfs to figure out what the difference between the lowest and highest pressures we'd be holding is
+ for(var/turf/open/T2 in T.atmos_adjacent_turfs)
+ if((flags_1 & ON_BORDER_1) && get_dir(src, T2) != dir)
+ continue
+ var/moles = T2.air.total_moles()
+ if(moles < min_moles)
+ min_moles = moles
+ if(moles > max_moles)
+ max_moles = moles
+ density = TRUE
+ T.ImmediateCalculateAdjacentTurfs() // alright lets put it back
+ return max_moles - min_moles > 20
+
/obj/machinery/door/attackby(obj/item/I, mob/user, params)
- if(user.a_intent != INTENT_HARM && (istype(I, /obj/item/crowbar) || istype(I, /obj/item/twohanded/fireaxe)))
+ if(user.a_intent != INTENT_HARM && (I.tool_behaviour == TOOL_CROWBAR || istype(I, /obj/item/fireaxe)))
try_to_crowbar(I, user)
return 1
- else if(istype(I, /obj/item/weldingtool))
+ else if(I.tool_behaviour == TOOL_WELDER)
try_to_weld(I, user)
return 1
else if(!(I.item_flags & NOBLUDGEON) && user.a_intent != INTENT_HARM)
@@ -223,13 +249,13 @@
if(prob(20/severity) && (istype(src, /obj/machinery/door/airlock) || istype(src, /obj/machinery/door/window)) )
INVOKE_ASYNC(src, .proc/open)
if(prob(severity*10 - 20))
- if(secondsElectrified == 0)
- secondsElectrified = -1
+ if(secondsElectrified == MACHINE_NOT_ELECTRIFIED)
+ secondsElectrified = MACHINE_ELECTRIFIED_PERMANENT
LAZYADD(shockedby, "\[[TIME_STAMP("hh:mm:ss", FALSE)]\]EM Pulse")
addtimer(CALLBACK(src, .proc/unelectrify), 300)
/obj/machinery/door/proc/unelectrify()
- secondsElectrified = 0
+ secondsElectrified = MACHINE_NOT_ELECTRIFIED
/obj/machinery/door/update_icon_state()
if(density)
@@ -289,8 +315,11 @@
return
operating = TRUE
+
do_animate("closing")
layer = closingLayer
+ if(air_tight)
+ density = TRUE
sleep(5)
density = TRUE
sleep(5)
@@ -302,7 +331,7 @@
update_freelook_sight()
if(safe)
CheckForMobs()
- else
+ else if(!(flags_1 & ON_BORDER_1))
crush()
return 1
diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm
index b52cf1a891..4855280b86 100644
--- a/code/game/machinery/doors/firedoor.dm
+++ b/code/game/machinery/doors/firedoor.dm
@@ -23,6 +23,8 @@
assemblytype = /obj/structure/firelock_frame
armor = list("melee" = 30, "bullet" = 30, "laser" = 20, "energy" = 20, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 95, "acid" = 70)
interaction_flags_machine = INTERACT_MACHINE_WIRES_IF_OPEN | INTERACT_MACHINE_ALLOW_SILICON | INTERACT_MACHINE_OPEN_SILICON | INTERACT_MACHINE_REQUIRES_SILICON | INTERACT_MACHINE_OPEN
+ air_tight = TRUE
+ var/emergency_close_timer = 0
var/nextstate = null
var/boltslocked = TRUE
var/list/affecting_areas
@@ -68,10 +70,14 @@
return ..()
/obj/machinery/door/firedoor/Bumped(atom/movable/AM)
- if(panel_open || operating)
+ if(panel_open || operating || welded)
return
- if(!density)
- return ..()
+ if(ismob(AM))
+ var/mob/user = AM
+ if(density && !welded && !operating && !(stat & NOPOWER) && (!density || allow_hand_open(user)))
+ add_fingerprint(user)
+ open()
+ return TRUE
return FALSE
@@ -82,10 +88,19 @@
else
stat |= NOPOWER
-/obj/machinery/door/firedoor/attack_hand(mob/user)
+/obj/machinery/door/firedoor/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
+
+ if(!welded && !operating && !(stat & NOPOWER) && (!density || allow_hand_open(user)))
+ add_fingerprint(user)
+ if(density)
+ emergency_close_timer = world.time + 30 // prevent it from instaclosing again if in space
+ open()
+ else
+ close()
+ return TRUE
if(operating || !density)
return
user.changeNext_move(CLICK_CD_MELEE)
@@ -100,7 +115,7 @@
return
if(welded)
- if(istype(C, /obj/item/wrench))
+ if(C.tool_behaviour == TOOL_WRENCH)
if(boltslocked)
to_chat(user, "There are screws locking the bolts in place!")
return
@@ -114,7 +129,7 @@
"You undo [src]'s floor bolts.")
deconstruct(TRUE)
return
- if(istype(C, /obj/item/screwdriver))
+ if(C.tool_behaviour == TOOL_SCREWDRIVER)
user.visible_message("[user] [boltslocked ? "unlocks" : "locks"] [src]'s bolts.", \
"You [boltslocked ? "unlock" : "lock"] [src]'s floor bolts.")
C.play_tool_sound(src)
@@ -140,10 +155,27 @@
return
if(density)
+ if(is_holding_pressure())
+ // tell the user that this is a bad idea, and have a do_after as well
+ to_chat(user, "As you begin crowbarring \the [src] a gush of air blows in your face... maybe you should reconsider?")
+ if(!do_after(user, 15, TRUE, src)) // give them a few seconds to reconsider their decision.
+ return
+ log_game("[key_name_admin(user)] has opened a firelock with a pressure difference at [AREACOORD(loc)]") // there bibby I made it logged just for you. Enjoy.
+ // since we have high-pressure-ness, close all other firedoors on the tile
+ whack_a_mole()
+ if(welded || operating || !density)
+ return // in case things changed during our do_after
+ emergency_close_timer = world.time + 60 // prevent it from instaclosing again if in space
open()
else
close()
+/obj/machinery/door/firedoor/proc/allow_hand_open(mob/user)
+ var/area/A = get_area(src)
+ if(A && A.fire)
+ return FALSE
+ return !is_holding_pressure()
+
/obj/machinery/door/firedoor/attack_ai(mob/user)
add_fingerprint(user)
if(welded || operating || stat & NOPOWER)
@@ -171,20 +203,16 @@
if("closing")
flick("door_closing", src)
-/obj/machinery/door/firedoor/update_icon_state()
+/obj/machinery/door/firedoor/update_icon()
+ cut_overlays()
if(density)
icon_state = "door_closed"
+ if(welded)
+ add_overlay("welded")
else
icon_state = "door_open"
-
-/obj/machinery/door/firedoor/update_overlays()
- . = ..()
- if(!welded)
- return
- if(density)
- . += "welded"
- else
- . += "welded_open"
+ if(welded)
+ add_overlay("welded_open")
/obj/machinery/door/firedoor/open()
. = ..()
@@ -194,6 +222,61 @@
. = ..()
latetoggle()
+/obj/machinery/door/firedoor/proc/whack_a_mole(reconsider_immediately = FALSE)
+ set waitfor = 0
+ for(var/cdir in GLOB.cardinals)
+ if((flags_1 & ON_BORDER_1) && cdir != dir)
+ continue
+ whack_a_mole_part(get_step(src, cdir), reconsider_immediately)
+ if(flags_1 & ON_BORDER_1)
+ whack_a_mole_part(get_turf(src), reconsider_immediately)
+
+/obj/machinery/door/firedoor/proc/whack_a_mole_part(turf/start_point, reconsider_immediately)
+ set waitfor = 0
+ var/list/doors_to_close = list()
+ var/list/turfs = list()
+ turfs[start_point] = 1
+ for(var/i = 1; (i <= turfs.len && i <= 11); i++) // check up to 11 turfs.
+ var/turf/open/T = turfs[i]
+ if(istype(T, /turf/open/space))
+ return -1
+ for(var/T2 in T.atmos_adjacent_turfs)
+ if(turfs[T2])
+ continue
+ var/is_cut_by_unopen_door = FALSE
+ for(var/obj/machinery/door/firedoor/FD in T2)
+ if((FD.flags_1 & ON_BORDER_1) && get_dir(T2, T) != FD.dir)
+ continue
+ if(FD.operating || FD == src || FD.welded || FD.density)
+ continue
+ doors_to_close += FD
+ is_cut_by_unopen_door = TRUE
+
+ for(var/obj/machinery/door/firedoor/FD in T)
+ if((FD.flags_1 & ON_BORDER_1) && get_dir(T, T2) != FD.dir)
+ continue
+ if(FD.operating || FD == src || FD.welded || FD.density)
+ continue
+ doors_to_close += FD
+ is_cut_by_unopen_door= TRUE
+ if(!is_cut_by_unopen_door)
+ turfs[T2] = 1
+ if(turfs.len > 10)
+ return // too big, don't bother
+ for(var/obj/machinery/door/firedoor/FD in doors_to_close)
+ FD.emergency_pressure_stop(FALSE)
+ if(reconsider_immediately)
+ var/turf/open/T = FD.loc
+ if(istype(T))
+ T.ImmediateCalculateAdjacentTurfs()
+
+/obj/machinery/door/firedoor/proc/emergency_pressure_stop(consider_timer = TRUE)
+ set waitfor = 0
+ if(density || operating || welded)
+ return
+ if(world.time >= emergency_close_timer || !consider_timer)
+ close()
+
/obj/machinery/door/firedoor/deconstruct(disassembled = TRUE)
if(!(flags_1 & NODECONSTRUCT_1))
var/obj/structure/firelock_frame/F = new assemblytype(get_turf(src))
@@ -227,6 +310,59 @@
opacity = TRUE
density = TRUE
+/obj/machinery/door/firedoor/border_only/close()
+ if(density)
+ return TRUE
+ if(operating || welded)
+ return
+ var/turf/T1 = get_turf(src)
+ var/turf/T2 = get_step(T1, dir)
+ for(var/mob/living/M in T1)
+ if(M.stat == CONSCIOUS && M.pulling && M.pulling.loc == T2 && !M.pulling.anchored && M.pulling.move_resist <= M.move_force)
+ var/mob/living/M2 = M.pulling
+ if(!istype(M2) || !M2.buckled || !M2.buckled.buckle_prevents_pull)
+ to_chat(M, "You pull [M.pulling] through [src] right as it closes")
+ M.pulling.forceMove(T1)
+ M.start_pulling(M2)
+
+ for(var/mob/living/M in T2)
+ if(M.stat == CONSCIOUS && M.pulling && M.pulling.loc == T1 && !M.pulling.anchored && M.pulling.move_resist <= M.move_force)
+ var/mob/living/M2 = M.pulling
+ if(!istype(M2) || !M2.buckled || !M2.buckled.buckle_prevents_pull)
+ to_chat(M, "You pull [M.pulling] through [src] right as it closes")
+ M.pulling.forceMove(T2)
+ M.start_pulling(M2)
+ . = ..()
+
+/obj/machinery/door/firedoor/border_only/allow_hand_open(mob/user)
+ var/area/A = get_area(src)
+ if((!A || !A.fire) && !is_holding_pressure())
+ return TRUE
+ whack_a_mole(TRUE) // WOOP WOOP SIDE EFFECTS
+ var/turf/T = loc
+ var/turf/T2 = get_step(T, dir)
+ if(!T || !T2)
+ return
+ var/status1 = check_door_side(T)
+ var/status2 = check_door_side(T2)
+ if((status1 == 1 && status2 == -1) || (status1 == -1 && status2 == 1))
+ to_chat(user, "Access denied. Try closing another firedoor to minimize decompression, or using a crowbar.")
+ return FALSE
+ return TRUE
+
+/obj/machinery/door/firedoor/border_only/proc/check_door_side(turf/open/start_point)
+ var/list/turfs = list()
+ turfs[start_point] = 1
+ for(var/i = 1; (i <= turfs.len && i <= 11); i++) // check up to 11 turfs.
+ var/turf/open/T = turfs[i]
+ if(istype(T, /turf/open/space))
+ return -1
+ for(var/T2 in T.atmos_adjacent_turfs)
+ turfs[T2] = 1
+ if(turfs.len <= 10)
+ return 0 // not big enough to matter
+ return start_point.air.return_pressure() < 20 ? -1 : 1
+
/obj/machinery/door/firedoor/border_only/CanPass(atom/movable/mover, turf/target)
if(istype(mover) && (mover.pass_flags & PASSGLASS))
return TRUE
@@ -257,6 +393,18 @@
assemblytype = /obj/structure/firelock_frame/heavy
max_integrity = 550
+/obj/machinery/door/firedoor/window
+ name = "window shutter"
+ icon = 'icons/obj/doors/doorfirewindow.dmi'
+ desc = "A second window that slides in when the original window is broken, designed to protect against hull breaches. Truly a work of genius by NT engineers."
+ glass = TRUE
+ explosion_block = 0
+ max_integrity = 50
+ resistance_flags = 0 // not fireproof
+ heat_proof = FALSE
+
+/obj/machinery/door/firedoor/window/allow_hand_open()
+ return TRUE
/obj/item/electronics/firelock
name = "firelock circuitry"
@@ -294,7 +442,7 @@
/obj/structure/firelock_frame/attackby(obj/item/C, mob/user)
switch(constructionStep)
if(CONSTRUCTION_PANEL_OPEN)
- if(istype(C, /obj/item/crowbar))
+ if(C.tool_behaviour == TOOL_CROWBAR)
C.play_tool_sound(src)
user.visible_message("[user] starts prying something out from [src]...", \
"You begin prying out the wire cover...")
@@ -308,7 +456,7 @@
constructionStep = CONSTRUCTION_WIRES_EXPOSED
update_icon()
return
- if(istype(C, /obj/item/wrench))
+ if(C.tool_behaviour == TOOL_WRENCH)
if(locate(/obj/machinery/door/firedoor) in get_turf(src))
to_chat(user, "There's already a firelock there.")
return
@@ -350,7 +498,7 @@
return
if(CONSTRUCTION_WIRES_EXPOSED)
- if(istype(C, /obj/item/wirecutters))
+ if(C.tool_behaviour == TOOL_WIRECUTTER)
C.play_tool_sound(src)
user.visible_message("[user] starts cutting the wires from [src]...", \
"You begin removing [src]'s wires...")
@@ -364,7 +512,7 @@
constructionStep = CONSTRUCTION_GUTTED
update_icon()
return
- if(istype(C, /obj/item/crowbar))
+ if(C.tool_behaviour == TOOL_CROWBAR)
C.play_tool_sound(src)
user.visible_message("[user] starts prying a metal plate into [src]...", \
"You begin prying the cover plate back onto [src]...")
@@ -379,7 +527,7 @@
update_icon()
return
if(CONSTRUCTION_GUTTED)
- if(istype(C, /obj/item/crowbar))
+ if(C.tool_behaviour == TOOL_CROWBAR)
user.visible_message("[user] begins removing the circuit board from [src]...", \
"You begin prying out the circuit board from [src]...")
if(!C.use_tool(src, user, 50, volume=50))
@@ -401,7 +549,7 @@
"You begin adding wires to [src]...")
playsound(get_turf(src), 'sound/items/deconstruct.ogg', 50, 1)
if(do_after(user, 60, target = src))
- if(constructionStep != CONSTRUCTION_GUTTED || !B.use_tool(src, user, 0, 5))
+ if(constructionStep != CONSTRUCTION_GUTTED || B.get_amount() < 5 || !B)
return
user.visible_message("[user] adds wires to [src].", \
"You wire [src].")
@@ -410,7 +558,7 @@
update_icon()
return
if(CONSTRUCTION_NOCIRCUIT)
- if(istype(C, /obj/item/weldingtool))
+ if(C.tool_behaviour == TOOL_WELDER)
if(!C.tool_start_check(user, amount=1))
return
user.visible_message("[user] begins cutting apart [src]'s frame...", \
diff --git a/code/game/machinery/doors/poddoor.dm b/code/game/machinery/doors/poddoor.dm
index c032ded6b0..4226d8a439 100644
--- a/code/game/machinery/doors/poddoor.dm
+++ b/code/game/machinery/doors/poddoor.dm
@@ -16,6 +16,9 @@
damage_deflection = 70
poddoor = TRUE
+/obj/machinery/door/poddoor/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock, idnum, override=FALSE)
+ id = "[idnum][id]"
+
/obj/machinery/door/poddoor/preopen
icon_state = "open"
density = FALSE
diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm
index 492e90720c..c9c577231e 100644
--- a/code/game/machinery/doors/windowdoor.dm
+++ b/code/game/machinery/doors/windowdoor.dm
@@ -54,6 +54,15 @@
else
icon_state = "[src.base_state]open"
+/obj/machinery/door/window/update_atom_colour()
+ if((color && (color_hex2num(color) < 255)))
+ visible = TRUE
+ if(density)
+ set_opacity(TRUE)
+ else
+ visible = FALSE
+ set_opacity(density && visible)
+
/obj/machinery/door/window/proc/open_and_close()
open()
if(src.check_access(null))
@@ -143,16 +152,18 @@
do_animate("opening")
playsound(src.loc, 'sound/machines/windowdoor.ogg', 100, 1)
src.icon_state ="[src.base_state]open"
- sleep(10)
+ addtimer(CALLBACK(src, .proc/finish_opening), 10)
+ return TRUE
+/obj/machinery/door/window/proc/finish_opening()
+ operating = FALSE
density = FALSE
-// src.sd_set_opacity(0) //TODO: why is this here? Opaque windoors? ~Carn
+ if(visible)
+ set_opacity(FALSE)
air_update_turf(1)
update_freelook_sight()
-
if(operating == 1) //emag again
operating = FALSE
- return 1
/obj/machinery/door/window/close(forced=0)
if (src.operating)
@@ -171,10 +182,13 @@
density = TRUE
air_update_turf(1)
update_freelook_sight()
- sleep(10)
+ addtimer(CALLBACK(src, .proc/finish_closing), 10)
+ return TRUE
+/obj/machinery/door/window/proc/finish_closing()
+ if(visible)
+ set_opacity(TRUE)
operating = FALSE
- return 1
/obj/machinery/door/window/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
switch(damage_type)
diff --git a/code/game/machinery/exp_cloner.dm b/code/game/machinery/exp_cloner.dm
deleted file mode 100644
index 2c669aac80..0000000000
--- a/code/game/machinery/exp_cloner.dm
+++ /dev/null
@@ -1,298 +0,0 @@
-//Experimental cloner; clones a body regardless of the owner's status, letting a ghost control it instead
-/obj/machinery/clonepod/experimental
- name = "experimental cloning pod"
- desc = "An ancient cloning pod. It seems to be an early prototype of the experimental cloners used in Nanotrasen Stations."
- icon = 'icons/obj/machines/cloning.dmi'
- icon_state = "pod_0"
- req_access = null
- circuit = /obj/item/circuitboard/machine/clonepod/experimental
- internal_radio = FALSE
-
-//Start growing a human clone in the pod!
-/obj/machinery/clonepod/experimental/growclone(clonename, ui, mutation_index, mindref, last_death, blood_type, datum/species/mrace, list/features, factions, list/quirks)
- if(panel_open)
- return FALSE
- if(mess || attempting)
- return FALSE
-
- attempting = TRUE //One at a time!!
- countdown.start()
-
- var/mob/living/carbon/human/H = new /mob/living/carbon/human(src)
-
- H.hardset_dna(ui, mutation_index, H.real_name, blood_type, mrace, features)
-
- if(efficiency > 2)
- var/list/unclean_mutations = (GLOB.not_good_mutations|GLOB.bad_mutations)
- H.dna.remove_mutation_group(unclean_mutations)
- if(efficiency > 5 && prob(20))
- H.easy_randmut(POSITIVE)
- if(efficiency < 3 && prob(50))
- var/mob/M = H.easy_randmut(NEGATIVE+MINOR_NEGATIVE)
- if(ismob(M))
- H = M
-
- H.silent = 20 //Prevents an extreme edge case where clones could speak if they said something at exactly the right moment.
- occupant = H
-
- if(!clonename) //to prevent null names
- clonename = "clone ([rand(1,999)])"
- H.real_name = clonename
-
- icon_state = "pod_1"
- //Get the clone body ready
- maim_clone(H)
- ADD_TRAIT(H, TRAIT_STABLEHEART, "cloning")
- ADD_TRAIT(H, TRAIT_EMOTEMUTE, "cloning")
- ADD_TRAIT(H, TRAIT_MUTE, "cloning")
- ADD_TRAIT(H, TRAIT_NOBREATH, "cloning")
- ADD_TRAIT(H, TRAIT_NOCRITDAMAGE, "cloning")
- H.Unconscious(80)
-
- var/list/candidates = pollCandidatesForMob("Do you want to play as [clonename]'s defective clone?", null, null, null, 100, H)
- if(LAZYLEN(candidates))
- var/mob/C = pick(candidates)
- H.key = C.key
-
- if(grab_ghost_when == CLONER_FRESH_CLONE)
- H.grab_ghost()
- to_chat(H, "Consciousness slowly creeps over you as your body regenerates.
So this is what cloning feels like?")
-
- if(grab_ghost_when == CLONER_MATURE_CLONE)
- H.ghostize(TRUE) //Only does anything if they were still in their old body and not already a ghost
- to_chat(H.get_ghost(TRUE), "Your body is beginning to regenerate in a cloning pod. You will become conscious when it is complete.")
-
- if(H)
- H.faction |= factions
-
- H.set_cloned_appearance()
-
- H.suiciding = FALSE
- attempting = FALSE
- return TRUE
-
-
-//Prototype cloning console, much more rudimental and lacks modern functions such as saving records, autocloning, or safety checks.
-/obj/machinery/computer/prototype_cloning
- name = "prototype cloning console"
- desc = "Used to operate an experimental cloner."
- icon_screen = "dna"
- icon_keyboard = "med_key"
- circuit = /obj/item/circuitboard/computer/prototype_cloning
- var/obj/machinery/dna_scannernew/scanner = null //Linked scanner. For scanning.
- var/list/pods //Linked experimental cloning pods
- var/temp = "Inactive"
- var/scantemp = "Ready to Scan"
- var/loading = FALSE // Nice loading text
-
- light_color = LIGHT_COLOR_BLUE
-
-/obj/machinery/computer/prototype_cloning/Initialize()
- . = ..()
- updatemodules(TRUE)
-
-/obj/machinery/computer/prototype_cloning/Destroy()
- if(pods)
- for(var/P in pods)
- DetachCloner(P)
- pods = null
- return ..()
-
-/obj/machinery/computer/prototype_cloning/proc/GetAvailablePod(mind = null)
- if(pods)
- for(var/P in pods)
- var/obj/machinery/clonepod/experimental/pod = P
- if(pod.is_operational() && !(pod.occupant || pod.mess))
- return pod
-
-/obj/machinery/computer/prototype_cloning/proc/updatemodules(findfirstcloner)
- scanner = findscanner()
- if(findfirstcloner && !LAZYLEN(pods))
- findcloner()
-
-/obj/machinery/computer/prototype_cloning/proc/findscanner()
- var/obj/machinery/dna_scannernew/scannerf = null
-
- // Loop through every direction
- for(var/direction in GLOB.cardinals)
- // Try to find a scanner in that direction
- scannerf = locate(/obj/machinery/dna_scannernew, get_step(src, direction))
- // If found and operational, return the scanner
- if (!isnull(scannerf) && scannerf.is_operational())
- return scannerf
-
- // If no scanner was found, it will return null
- return null
-
-/obj/machinery/computer/prototype_cloning/proc/findcloner()
- var/obj/machinery/clonepod/experimental/podf = null
- for(var/direction in GLOB.cardinals)
- podf = locate(/obj/machinery/clonepod/experimental, get_step(src, direction))
- if (!isnull(podf) && podf.is_operational())
- AttachCloner(podf)
-
-/obj/machinery/computer/prototype_cloning/proc/AttachCloner(obj/machinery/clonepod/experimental/pod)
- if(!pod.connected)
- pod.connected = src
- LAZYADD(pods, pod)
-
-/obj/machinery/computer/prototype_cloning/proc/DetachCloner(obj/machinery/clonepod/experimental/pod)
- pod.connected = null
- LAZYREMOVE(pods, pod)
-
-/obj/machinery/computer/prototype_cloning/attackby(obj/item/W, mob/user, params)
- if(istype(W, /obj/item/multitool))
- var/obj/item/multitool/P = W
-
- if(istype(P.buffer, /obj/machinery/clonepod/experimental))
- if(get_area(P.buffer) != get_area(src))
- to_chat(user, "-% Cannot link machines across power zones. Buffer cleared %-")
- P.buffer = null
- return
- to_chat(user, "-% Successfully linked [P.buffer] with [src] %-")
- var/obj/machinery/clonepod/experimental/pod = P.buffer
- if(pod.connected)
- pod.connected.DetachCloner(pod)
- AttachCloner(pod)
- else
- P.buffer = src
- to_chat(user, "-% Successfully stored [REF(P.buffer)] [P.buffer.name] in buffer %-")
- return
- else
- return ..()
-
-/obj/machinery/computer/prototype_cloning/attack_hand(mob/user)
- if(..())
- return
- interact(user)
-
-/obj/machinery/computer/prototype_cloning/interact(mob/user)
- user.set_machine(src)
- add_fingerprint(user)
-
- if(..())
- return
-
- updatemodules(TRUE)
-
- var/dat = ""
- dat += "Refresh"
-
- dat += "Cloning Pod Status
"
- dat += "[temp]
"
-
- if (isnull(src.scanner) || !LAZYLEN(pods))
- dat += "Modules
"
- //dat += "Reload Modules"
- if (isnull(src.scanner))
- dat += "ERROR: No Scanner detected!
"
- if (!LAZYLEN(pods))
- dat += "ERROR: No Pod detected
"
-
- // Scan-n-Clone
- if (!isnull(src.scanner))
- var/mob/living/scanner_occupant = get_mob_or_brainmob(scanner.occupant)
-
- dat += "Cloning
"
-
- dat += ""
- if(!scanner_occupant)
- dat += "Scanner Unoccupied"
- else if(loading)
- dat += "[scanner_occupant] => Scanning..."
- else
- scantemp = "Ready to Clone"
- dat += "[scanner_occupant] => [scantemp]"
- dat += "
"
-
- if(scanner_occupant)
- dat += "Clone"
- dat += "
[src.scanner.locked ? "Unlock Scanner" : "Lock Scanner"]"
- else
- dat += "Clone"
-
- var/datum/browser/popup = new(user, "cloning", "Prototype Cloning System Control")
- popup.set_content(dat)
- popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
- popup.open()
-
-/obj/machinery/computer/prototype_cloning/Topic(href, href_list)
- if(..())
- return
-
- if(loading)
- return
-
- else if ((href_list["clone"]) && !isnull(scanner) && scanner.is_operational())
- scantemp = ""
-
- loading = TRUE
- updateUsrDialog()
- playsound(src, 'sound/machines/terminal_prompt.ogg', 50, 0)
- say("Initiating scan...")
-
- spawn(20)
- clone_occupant(scanner.occupant)
- loading = FALSE
- updateUsrDialog()
- playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
-
- //No locking an open scanner.
- else if ((href_list["lock"]) && !isnull(scanner) && scanner.is_operational())
- if ((!scanner.locked) && (scanner.occupant))
- scanner.locked = TRUE
- playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
- else
- scanner.locked = FALSE
- playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
-
- else if (href_list["refresh"])
- updateUsrDialog()
- playsound(src, "terminal_type", 25, 0)
-
- add_fingerprint(usr)
- updateUsrDialog()
- return
-
-/obj/machinery/computer/prototype_cloning/proc/clone_occupant(occupant)
- var/mob/living/mob_occupant = get_mob_or_brainmob(occupant)
- var/datum/dna/dna
- if(ishuman(mob_occupant))
- var/mob/living/carbon/C = mob_occupant
- dna = C.has_dna()
- if(isbrain(mob_occupant))
- var/mob/living/brain/B = mob_occupant
- dna = B.stored_dna
-
- if(!istype(dna))
- scantemp = "Unable to locate valid genetic data."
- playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
- return
- if((HAS_TRAIT(mob_occupant, TRAIT_NOCLONE)) && (src.scanner.scan_level < 2))
- scantemp = "Subject no longer contains the fundamental materials required to create a living clone."
- playsound(src, 'sound/machines/terminal_alert.ogg', 50, 0)
- return
-
- var/clone_species
- if(dna.species)
- clone_species = dna.species
- else
- var/datum/species/rando_race = pick(GLOB.roundstart_races)
- clone_species = rando_race.type
-
- var/obj/machinery/clonepod/pod = GetAvailablePod()
- //Can't clone without someone to clone. Or a pod. Or if the pod is busy. Or full of gibs.
- if(!LAZYLEN(pods))
- temp = "No Clonepods detected."
- playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
- else if(!pod)
- temp = "No Clonepods available."
- playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
- else if(pod.occupant)
- temp = "Cloning cycle already in progress."
- playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
- else
- pod.growclone(mob_occupant.real_name, dna.uni_identity, dna.mutation_index, null, null, dna.blood_type, clone_species, dna.features, mob_occupant.faction)
- temp = "[mob_occupant.real_name] => Cloning data sent to pod."
- playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
-
diff --git a/code/game/machinery/firealarm.dm b/code/game/machinery/firealarm.dm
index 0b23e650d7..4c68d0b0b5 100644
--- a/code/game/machinery/firealarm.dm
+++ b/code/game/machinery/firealarm.dm
@@ -141,7 +141,7 @@
if(user)
log_game("[user] reset a fire alarm at [COORD(src)]")
-/obj/machinery/firealarm/attack_hand(mob/user)
+/obj/machinery/firealarm/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(buildstage != 2)
return ..()
add_fingerprint(user)
diff --git a/code/game/machinery/flasher.dm b/code/game/machinery/flasher.dm
index f4f1aa0637..c0e1122140 100644
--- a/code/game/machinery/flasher.dm
+++ b/code/game/machinery/flasher.dm
@@ -36,6 +36,9 @@
else
bulb = new(src)
+/obj/machinery/flasher/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock, idnum, override=FALSE)
+ id = "[idnum][id]"
+
/obj/machinery/flasher/Destroy()
QDEL_NULL(bulb)
return ..()
diff --git a/code/game/machinery/gulag_item_reclaimer.dm b/code/game/machinery/gulag_item_reclaimer.dm
index 55b1e34022..45484caa77 100644
--- a/code/game/machinery/gulag_item_reclaimer.dm
+++ b/code/game/machinery/gulag_item_reclaimer.dm
@@ -31,7 +31,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, "gulag_item_reclaimer", name, 300, 400, master_ui, state)
+ ui = new(user, src, ui_key, "GulagItemReclaimer", name, 300, 400, master_ui, state)
ui.open()
/obj/machinery/gulag_item_reclaimer/ui_data(mob/user)
diff --git a/code/game/machinery/harvester.dm b/code/game/machinery/harvester.dm
index 141f261688..8cb7ca1e8d 100644
--- a/code/game/machinery/harvester.dm
+++ b/code/game/machinery/harvester.dm
@@ -50,7 +50,7 @@
harvesting = FALSE
warming_up = FALSE
-/obj/machinery/harvester/attack_hand(mob/user)
+/obj/machinery/harvester/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(state_open)
close_machine()
else if(!harvesting)
diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm
index 621e486e90..f43114f7cb 100644
--- a/code/game/machinery/hologram.dm
+++ b/code/game/machinery/hologram.dm
@@ -78,7 +78,7 @@ GLOBAL_LIST_EMPTY(network_holopads)
new_disk.forceMove(src)
disk = new_disk
-/obj/machinery/holopad/tutorial/attack_hand(mob/user)
+/obj/machinery/holopad/tutorial/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(!istype(user))
return
if(user.incapacitated() || !is_operational())
diff --git a/code/game/machinery/hypnochair.dm b/code/game/machinery/hypnochair.dm
index 1b57f61b79..5677a09f12 100644
--- a/code/game/machinery/hypnochair.dm
+++ b/code/game/machinery/hypnochair.dm
@@ -37,7 +37,7 @@
/obj/machinery/hypnochair/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.notcontained_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "hypnochair", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, ui_key, "HypnoChair", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/hypnochair/ui_data()
diff --git a/code/game/machinery/igniter.dm b/code/game/machinery/igniter.dm
index 099b51db82..7cf21ed767 100644
--- a/code/game/machinery/igniter.dm
+++ b/code/game/machinery/igniter.dm
@@ -26,7 +26,7 @@
on = TRUE
icon_state = "igniter1"
-/obj/machinery/igniter/attack_hand(mob/user)
+/obj/machinery/igniter/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
@@ -53,6 +53,9 @@
else
icon_state = "igniter0"
+/obj/machinery/igniter/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock, idnum, override=FALSE)
+ id = "[idnum][id]"
+
// Wall mounted remote-control igniter.
/obj/machinery/sparker
diff --git a/code/game/machinery/iv_drip.dm b/code/game/machinery/iv_drip.dm
index cd9e9dc83f..5eb3de3e3b 100644
--- a/code/game/machinery/iv_drip.dm
+++ b/code/game/machinery/iv_drip.dm
@@ -158,7 +158,7 @@
attached.transfer_blood_to(beaker, amount)
update_icon()
-/obj/machinery/iv_drip/attack_hand(mob/user)
+/obj/machinery/iv_drip/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/machinery/launch_pad.dm b/code/game/machinery/launch_pad.dm
index c8219e9ebf..63ef40e045 100644
--- a/code/game/machinery/launch_pad.dm
+++ b/code/game/machinery/launch_pad.dm
@@ -285,8 +285,7 @@
/obj/item/launchpad_remote/ui_interact(mob/user, ui_key = "launchpad_remote", 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, "launchpad_remote", "Briefcase Launchpad Remote", 300, 240, master_ui, state) //width, height
- ui.set_style("syndicate")
+ ui = new(user, src, ui_key, "LaunchpadRemote", "Briefcase Launchpad Remote", 300, 240, master_ui, state) //width, height
ui.open()
ui.set_autoupdate(TRUE)
diff --git a/code/game/machinery/mass_driver.dm b/code/game/machinery/mass_driver.dm
index ab0a0534ab..a8fa31d5fb 100644
--- a/code/game/machinery/mass_driver.dm
+++ b/code/game/machinery/mass_driver.dm
@@ -11,6 +11,8 @@
var/id = 1
var/drive_range = 50 //this is mostly irrelevant since current mass drivers throw into space, but you could make a lower-range mass driver for interstation transport or something I guess.
+/obj/machinery/mass_driver/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock, idnum, override=FALSE)
+ id = "[idnum][id]"
/obj/machinery/mass_driver/proc/drive(amount)
if(stat & (BROKEN|NOPOWER))
diff --git a/code/game/machinery/porta_turret/portable_turret_construct.dm b/code/game/machinery/porta_turret/portable_turret_construct.dm
index 9d86e3792d..bf70ee8a9d 100644
--- a/code/game/machinery/porta_turret/portable_turret_construct.dm
+++ b/code/game/machinery/porta_turret/portable_turret_construct.dm
@@ -168,7 +168,7 @@
return ..()
-/obj/machinery/porta_turret_construct/attack_hand(mob/user)
+/obj/machinery/porta_turret_construct/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/machinery/porta_turret/portable_turret_cover.dm b/code/game/machinery/porta_turret/portable_turret_cover.dm
index 7fdb9b38be..3899f07685 100644
--- a/code/game/machinery/porta_turret/portable_turret_cover.dm
+++ b/code/game/machinery/porta_turret/portable_turret_cover.dm
@@ -31,7 +31,7 @@
return parent_turret.attack_ai(user)
-/obj/machinery/porta_turret_cover/attack_hand(mob/user)
+/obj/machinery/porta_turret_cover/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/machinery/recharger.dm b/code/game/machinery/recharger.dm
old mode 100644
new mode 100755
index 2fff2011c1..5d0d39e3a4
--- a/code/game/machinery/recharger.dm
+++ b/code/game/machinery/recharger.dm
@@ -19,7 +19,7 @@
/obj/item/modular_computer,
/obj/item/ammo_casing/mws_batt,
/obj/item/ammo_box/magazine/mws_mag,
- /obj/item/twohanded/electrostaff,
+ /obj/item/electrostaff,
/obj/item/gun/ballistic/automatic/magrifle))
/obj/machinery/recharger/RefreshParts()
@@ -108,7 +108,7 @@
return ..()
-/obj/machinery/recharger/attack_hand(mob/user)
+/obj/machinery/recharger/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/machinery/recycler.dm b/code/game/machinery/recycler.dm
index 8680aa69e6..706d2bb9bf 100644
--- a/code/game/machinery/recycler.dm
+++ b/code/game/machinery/recycler.dm
@@ -225,6 +225,6 @@
/obj/item/paper/guides/recycler
name = "paper - 'garbage duty instructions'"
- info = "New Assignment
You have been assigned to collect garbage from trash bins, located around the station. The crewmembers will put their trash into it and you will collect the said trash.
There is a recycling machine near your closet, inside maintenance; use it to recycle the trash for a small chance to get useful minerals. Then deliver these minerals to cargo or engineering. You are our last hope for a clean station, do not screw this up!"
+ info = "_New Assignment_\n\n You have been assigned to collect garbage from trash bins, located around the station. The crewmembers will put their trash into it and you will collect the said trash.
There is a recycling machine near your closet, inside maintenance; use it to recycle the trash for a small chance to get useful minerals. Then deliver these minerals to cargo or engineering. You are our last hope for a clean station, do not screw this up!"
#undef SAFETY_COOLDOWN
diff --git a/code/game/machinery/sheetifier.dm b/code/game/machinery/sheetifier.dm
new file mode 100644
index 0000000000..7b83401194
--- /dev/null
+++ b/code/game/machinery/sheetifier.dm
@@ -0,0 +1,44 @@
+/obj/machinery/sheetifier
+ name = "Sheet-meister 2000"
+ desc = "A very sheety machine"
+ icon = 'icons/obj/machines/sheetifier.dmi'
+ icon_state = "base_machine"
+ density = TRUE
+ use_power = IDLE_POWER_USE
+ idle_power_usage = 10
+ active_power_usage = 100
+ circuit = /obj/item/circuitboard/machine/sheetifier
+ layer = BELOW_OBJ_LAYER
+ var/busy_processing = FALSE
+
+/obj/machinery/sheetifier/Initialize()
+ . = ..()
+ AddComponent(/datum/component/material_container, list(/datum/material/meat), MINERAL_MATERIAL_AMOUNT * MAX_STACK_SIZE * 2, TRUE, /obj/item/reagent_containers/food/snacks/meat/slab, CALLBACK(src, .proc/CanInsertMaterials), CALLBACK(src, .proc/AfterInsertMaterials))
+
+/obj/machinery/sheetifier/update_overlays()
+ . = ..()
+ if(stat & (BROKEN|NOPOWER))
+ return
+ var/mutable_appearance/on_overlay = mutable_appearance(icon, "buttons_on")
+ . += on_overlay
+
+/obj/machinery/sheetifier/update_icon_state()
+ icon_state = "base_machine[busy_processing ? "_processing" : ""]"
+
+/obj/machinery/sheetifier/proc/CanInsertMaterials()
+ return !busy_processing
+
+/obj/machinery/sheetifier/proc/AfterInsertMaterials(item_inserted, id_inserted, amount_inserted)
+ busy_processing = TRUE
+ update_icon()
+ var/datum/material/last_inserted_material = id_inserted
+ var/mutable_appearance/processing_overlay = mutable_appearance(icon, "processing")
+ processing_overlay.color = last_inserted_material.color
+ flick_overlay_static(processing_overlay, src, 64)
+ addtimer(CALLBACK(src, .proc/finish_processing), 64)
+
+/obj/machinery/sheetifier/proc/finish_processing()
+ busy_processing = FALSE
+ update_icon()
+ var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
+ materials.retrieve_all() //Returns all as sheets
diff --git a/code/game/machinery/shuttle/shuttle_engine.dm b/code/game/machinery/shuttle/shuttle_engine.dm
index 1b2ce686d2..c3de384c58 100644
--- a/code/game/machinery/shuttle/shuttle_engine.dm
+++ b/code/game/machinery/shuttle/shuttle_engine.dm
@@ -130,7 +130,7 @@
var/deltaTemperature = req_power / heat_cap
if(deltaTemperature < 0)
return
- env.temperature += deltaTemperature
+ env.set_temperature(env.return_temperature(),deltaTemperature)
air_update_turf()
/obj/machinery/shuttle/engine/default_change_direction_wrench(mob/user, obj/item/I)
diff --git a/code/game/machinery/shuttle/shuttle_heater.dm b/code/game/machinery/shuttle/shuttle_heater.dm
index 3c36e53b5a..56854d2d71 100644
--- a/code/game/machinery/shuttle/shuttle_heater.dm
+++ b/code/game/machinery/shuttle/shuttle_heater.dm
@@ -89,8 +89,8 @@
var/datum/gas_mixture/air_contents = airs[1]
if(!air_contents)
return
- air_contents.volume = gas_capacity
- air_contents.temperature = T20C
+ air_contents.set_volume(gas_capacity)
+ air_contents.set_temperature(T20C)
/obj/machinery/atmospherics/components/unary/shuttle/heater/proc/hasFuel(var/required)
var/datum/gas_mixture/air_contents = airs[1]
diff --git a/code/game/machinery/spaceheater.dm b/code/game/machinery/spaceheater.dm
index 1730569e10..e6137c08c2 100644
--- a/code/game/machinery/spaceheater.dm
+++ b/code/game/machinery/spaceheater.dm
@@ -83,9 +83,9 @@
var/datum/gas_mixture/env = L.return_air()
var/newMode = HEATER_MODE_STANDBY
- if(setMode != HEATER_MODE_COOL && env.temperature < targetTemperature - temperatureTolerance)
+ if(setMode != HEATER_MODE_COOL && env.return_temperature() < targetTemperature - temperatureTolerance)
newMode = HEATER_MODE_HEAT
- else if(setMode != HEATER_MODE_HEAT && env.temperature > targetTemperature + temperatureTolerance)
+ else if(setMode != HEATER_MODE_HEAT && env.return_temperature() > targetTemperature + temperatureTolerance)
newMode = HEATER_MODE_COOL
if(mode != newMode)
@@ -96,7 +96,7 @@
return
var/heat_capacity = env.heat_capacity()
- var/requiredPower = abs(env.temperature - targetTemperature) * heat_capacity
+ var/requiredPower = abs(env.return_temperature() - targetTemperature) * heat_capacity
requiredPower = min(requiredPower, heatingPower)
if(requiredPower < 1)
@@ -106,7 +106,7 @@
if(mode == HEATER_MODE_COOL)
deltaTemperature *= -1
if(deltaTemperature)
- env.temperature += deltaTemperature
+ env.set_temperature(env.return_temperature() + deltaTemperature)
air_update_turf()
cell.use(requiredPower / efficiency)
else
@@ -175,7 +175,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, "space_heater", name, 400, 305, master_ui, state)
+ ui = new(user, src, ui_key, "SpaceHeater", name, 400, 305, master_ui, state)
ui.open()
/obj/machinery/space_heater/ui_data()
@@ -194,9 +194,9 @@
var/curTemp
if(istype(L))
var/datum/gas_mixture/env = L.return_air()
- curTemp = env.temperature
+ curTemp = env.return_temperature()
else if(isturf(L))
- curTemp = L.temperature
+ curTemp = L.return_temperature()
if(isnull(curTemp))
data["currentTemp"] = "N/A"
else
diff --git a/code/game/machinery/status_display.dm b/code/game/machinery/status_display.dm
index 3bb67e2a1c..8937106601 100644
--- a/code/game/machinery/status_display.dm
+++ b/code/game/machinery/status_display.dm
@@ -287,7 +287,7 @@
if(!.)
return
switch(var_name)
- if("shuttle_id")
+ if(NAMEOF(src, shuttle_id))
update()
/obj/machinery/status_display/shuttle/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock, idnum, override)
diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm
index cf6b2b4bf4..7de414fd63 100644
--- a/code/game/machinery/suit_storage_unit.dm
+++ b/code/game/machinery/suit_storage_unit.dm
@@ -383,7 +383,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.notcontained_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "suit_storage_unit", name, 400, 305, master_ui, state)
+ ui = new(user, src, ui_key, "SuitStorageUnit", name, 400, 305, master_ui, state)
ui.open()
/obj/machinery/suit_storage_unit/ui_data()
diff --git a/code/game/machinery/syndicatebeacon.dm b/code/game/machinery/syndicatebeacon.dm
index e300afe6b9..8b9aa64c58 100644
--- a/code/game/machinery/syndicatebeacon.dm
+++ b/code/game/machinery/syndicatebeacon.dm
@@ -68,7 +68,7 @@ GLOBAL_VAR_INIT(singularity_counter, 0)
/obj/machinery/power/singularity_beacon/attack_ai(mob/user)
return
-/obj/machinery/power/singularity_beacon/attack_hand(mob/user)
+/obj/machinery/power/singularity_beacon/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/machinery/telecomms/computers/logbrowser.dm b/code/game/machinery/telecomms/computers/logbrowser.dm
index fdf6ba121c..9867a1c133 100644
--- a/code/game/machinery/telecomms/computers/logbrowser.dm
+++ b/code/game/machinery/telecomms/computers/logbrowser.dm
@@ -21,7 +21,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, "tcommsserver", "Telecomms Server Monitor", 575, 400, master_ui, state)
+ ui = new(user, src, ui_key, "TelecommsLogBrowser", "Telecomms Server Monitor", 575, 400, master_ui, state)
ui.open()
/obj/machinery/computer/telecomms/server/ui_data(mob/user)
diff --git a/code/game/machinery/telecomms/computers/message.dm b/code/game/machinery/telecomms/computers/message.dm
index 41d417967b..11d50a2858 100644
--- a/code/game/machinery/telecomms/computers/message.dm
+++ b/code/game/machinery/telecomms/computers/message.dm
@@ -41,7 +41,7 @@
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "telepdalog", name, 727, 510, master_ui, state)
+ ui = new(user, src, ui_key, "TelecommsPDALog", name, 727, 510, master_ui, state)
ui.open()
/obj/machinery/computer/message_monitor/ui_static_data(mob/user)
@@ -389,7 +389,6 @@
/obj/item/paper/monitorkey/proc/print(obj/machinery/telecomms/message_server/server)
info = "Daily Key Reset
The new message monitor key is '[server.decryptkey]'.
Please keep this a secret and away from the clown.
If necessary, change the password to a more secure one."
- info_links = info
add_overlay("paper_words")
/obj/item/paper/monitorkey/LateInitialize()
diff --git a/code/game/machinery/telecomms/computers/telemonitor.dm b/code/game/machinery/telecomms/computers/telemonitor.dm
index bca16a0de4..11312fcfaf 100644
--- a/code/game/machinery/telecomms/computers/telemonitor.dm
+++ b/code/game/machinery/telecomms/computers/telemonitor.dm
@@ -22,7 +22,7 @@
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "telemonitor", name, 575, 400, master_ui, state)
+ ui = new(user, src, ui_key, "TelecommsMonitor", name, 575, 400, master_ui, state)
ui.open()
/obj/machinery/computer/telecomms/monitor/ui_data(mob/user)
diff --git a/code/game/machinery/telecomms/machine_interactions.dm b/code/game/machinery/telecomms/machine_interactions.dm
index 6d299f4413..86aa7905d5 100644
--- a/code/game/machinery/telecomms/machine_interactions.dm
+++ b/code/game/machinery/telecomms/machine_interactions.dm
@@ -36,7 +36,7 @@
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "teleinteract", "[name] Access", 520, 500, master_ui, state)
+ ui = new(user, src, ui_key, "TelecommsInteraction", "[name] Access", 520, 500, master_ui, state)
ui.open()
/obj/machinery/telecomms/ui_data(mob/user)
diff --git a/code/game/machinery/turnstile.dm b/code/game/machinery/turnstile.dm
index 9bc5193bbb..f30ef22dd7 100644
--- a/code/game/machinery/turnstile.dm
+++ b/code/game/machinery/turnstile.dm
@@ -18,9 +18,6 @@
/obj/machinery/turnstile/CanAtmosPass(turf/T)
return TRUE
-/obj/machinery/turnstile/bullet_act(obj/item/projectile/P, def_zone)
- return BULLET_ACT_FORCE_PIERCE //Pass through!
-
/obj/machinery/turnstile/proc/allowed_access(var/mob/B)
if(B.pulledby && ismob(B.pulledby))
return allowed(B.pulledby) | allowed(B)
@@ -28,6 +25,8 @@
return allowed(B)
/obj/machinery/turnstile/CanPass(atom/movable/AM, turf/T)
+ if(istype(AM, /obj/item/projectile))
+ return TRUE
if(ismob(AM))
var/mob/B = AM
if(isliving(AM))
@@ -60,6 +59,8 @@
return FALSE
/obj/machinery/turnstile/CheckExit(atom/movable/AM as mob|obj, target)
+ if(istype(AM, /obj/item/projectile))
+ return TRUE
if(isliving(AM))
var/mob/living/M = AM
var/outdir = dir
@@ -81,4 +82,4 @@
M.last_bumped = world.time
return canexit
else
- return TRUE
\ No newline at end of file
+ return TRUE
diff --git a/code/game/machinery/washing_machine.dm b/code/game/machinery/washing_machine.dm
index 26220d4d89..9e277b9d8e 100644
--- a/code/game/machinery/washing_machine.dm
+++ b/code/game/machinery/washing_machine.dm
@@ -297,7 +297,7 @@ GLOBAL_LIST_INIT(dye_registry, list(
else
return ..()
-/obj/machinery/washing_machine/attack_hand(mob/user)
+/obj/machinery/washing_machine/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/machinery/wishgranter.dm b/code/game/machinery/wishgranter.dm
index ee28f118fa..dcd86c9f24 100644
--- a/code/game/machinery/wishgranter.dm
+++ b/code/game/machinery/wishgranter.dm
@@ -108,7 +108,7 @@
killwish.health = killwish.maxHealth
killwish.vine_grab_distance = 6
killwish.melee_damage_upper = 30
- killwish.loot = list(/obj/item/twohanded/dualsaber/hypereutactic)
+ killwish.loot = list(/obj/item/dualsaber/hypereutactic)
charges--
insisting = FALSE
if(!charges)
diff --git a/code/game/mecha/combat/honker.dm b/code/game/mecha/combat/honker.dm
index 717884e9a4..89b641ccc6 100644
--- a/code/game/mecha/combat/honker.dm
+++ b/code/game/mecha/combat/honker.dm
@@ -21,7 +21,7 @@
var/cell_charge = get_charge()
var/datum/gas_mixture/int_tank_air = internal_tank.return_air()
var/tank_pressure = internal_tank ? round(int_tank_air.return_pressure(),0.01) : "None"
- var/tank_temperature = internal_tank ? int_tank_air.temperature : "Unknown"
+ var/tank_temperature = internal_tank ? int_tank_air.return_temperature() : "Unknown"
var/cabin_pressure = round(return_pressure(),0.01)
var/output = {"[report_internal_damage()]
[integrity<30?"DAMAGE LEVEL CRITICAL
":null]
@@ -155,4 +155,4 @@
var/color=""
for (var/i=0;i<6;i++)
color = color+pick(colors)
- return color
\ No newline at end of file
+ return color
diff --git a/code/game/mecha/equipment/tools/other_tools.dm b/code/game/mecha/equipment/tools/other_tools.dm
index 4ddb5281ea..8039aabd1f 100644
--- a/code/game/mecha/equipment/tools/other_tools.dm
+++ b/code/game/mecha/equipment/tools/other_tools.dm
@@ -388,7 +388,7 @@
/obj/item/mecha_parts/mecha_equipment/generator/get_equip_info()
var/output = ..()
if(output)
- return "[output] \[[fuel]: [round(fuel.amount*fuel.mats_per_stack,0.1)] cm3\] - [equip_ready?"A":"Dea"]ctivate"
+ return "[output] \[[fuel]: [round(fuel.amount*MINERAL_MATERIAL_AMOUNT,0.1)] cm3\] - [equip_ready?"A":"Dea"]ctivate"
/obj/item/mecha_parts/mecha_equipment/generator/action(target)
if(chassis)
@@ -398,9 +398,9 @@
/obj/item/mecha_parts/mecha_equipment/generator/proc/load_fuel(var/obj/item/stack/sheet/P)
if(P.type == fuel.type && P.amount > 0)
- var/to_load = max(max_fuel - fuel.amount*fuel.mats_per_stack,0)
+ var/to_load = max(max_fuel - fuel.amount*MINERAL_MATERIAL_AMOUNT,0)
if(to_load)
- var/units = min(max(round(to_load / P.mats_per_stack),1),P.amount)
+ var/units = min(max(round(to_load / MINERAL_MATERIAL_AMOUNT),1),P.amount)
fuel.amount += units
P.use(units)
occupant_message("[units] unit\s of [fuel] successfully loaded.")
@@ -422,13 +422,13 @@
return
var/datum/gas_mixture/GM = new
if(prob(10))
- GM.gases[/datum/gas/plasma] += 100
- GM.temperature = 1500+T0C //should be enough to start a fire
+ GM.adjust_moles(/datum/gas/plasma,100)
+ GM.set_temperature(1500+T0C) //should be enough to start a fire
T.visible_message("[src] suddenly disgorges a cloud of heated plasma.")
qdel(src)
else
- GM.gases[/datum/gas/plasma] += 5
- GM.temperature = istype(T) ? T.air.return_temperature() : T20C
+ GM.adjust_moles(/datum/gas/plasma,5)
+ GM.set_temperature(istype(T) ? T.air.return_temperature() : T20C)
T.visible_message("[src] suddenly disgorges a cloud of plasma.")
T.assume_air(GM)
return
@@ -454,7 +454,7 @@
if(cur_charge < chassis.cell.maxcharge)
use_fuel = fuel_per_cycle_active
chassis.give_power(power_per_cycle)
- fuel.amount -= min(use_fuel/fuel.mats_per_stack,fuel.amount)
+ fuel.amount -= min(use_fuel/MINERAL_MATERIAL_AMOUNT,fuel.amount)
update_equip_info()
return 1
diff --git a/code/game/mecha/mech_bay.dm b/code/game/mecha/mech_bay.dm
index bcf1b948e2..dcd8a12b38 100644
--- a/code/game/mecha/mech_bay.dm
+++ b/code/game/mecha/mech_bay.dm
@@ -84,7 +84,7 @@
/obj/machinery/computer/mech_bay_power_console/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, "mech_bay_power_console", "Mech Bay Power Control Console", 400, 200, master_ui, state)
+ ui = new(user, src, ui_key, "MechBayPowerConsole", "Mech Bay Power Control Console", 400, 200, master_ui, state)
ui.open()
/obj/machinery/computer/mech_bay_power_console/ui_act(action, params)
diff --git a/code/game/mecha/mech_fabricator.dm b/code/game/mecha/mech_fabricator.dm
index 93228c7fee..ca481f0d80 100644
--- a/code/game/mecha/mech_fabricator.dm
+++ b/code/game/mecha/mech_fabricator.dm
@@ -26,6 +26,7 @@
"Firefighter",
"Odysseus",
"Gygax",
+ "Medical-Spec Gygax",
"Durand",
"H.O.N.K",
"Phazon",
diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm
index 32be825489..d2340a15ea 100644
--- a/code/game/mecha/mecha.dm
+++ b/code/game/mecha/mecha.dm
@@ -247,10 +247,10 @@
/obj/mecha/proc/add_cabin()
cabin_air = new
- cabin_air.temperature = T20C
- cabin_air.volume = 200
- cabin_air.gases[/datum/gas/oxygen] = O2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature)
- cabin_air.gases[/datum/gas/nitrogen] = N2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature)
+ cabin_air.set_temperature(T20C)
+ cabin_air.set_volume(200)
+ cabin_air.set_moles(/datum/gas/oxygen,O2STANDARD*cabin_air.return_volume()/(R_IDEAL_GAS_EQUATION*cabin_air.return_temperature()))
+ cabin_air.set_moles(/datum/gas/nitrogen,N2STANDARD*cabin_air.return_volume()/(R_IDEAL_GAS_EQUATION*cabin_air.return_temperature()))
return cabin_air
/obj/mecha/proc/add_radio()
@@ -302,9 +302,9 @@
if(int_tank_air.return_pressure() > internal_tank.maximum_pressure && !(internal_damage & MECHA_INT_TANK_BREACH))
setInternalDamage(MECHA_INT_TANK_BREACH)
if(int_tank_air && int_tank_air.return_volume() > 0) //heat the air_contents
- int_tank_air.temperature = min(6000+T0C, int_tank_air.temperature+rand(10,15))
+ int_tank_air.set_temperature(min(6000+T0C, int_tank_air.return_temperature()+rand(10,15)))
if(cabin_air && cabin_air.return_volume()>0)
- cabin_air.temperature = min(6000+T0C, cabin_air.return_temperature()+rand(10,15))
+ cabin_air.set_temperature(min(6000+T0C, cabin_air.return_temperature()+rand(10,15)))
if(cabin_air.return_temperature() > max_temperature/2)
take_damage(4/round(max_temperature/cabin_air.return_temperature(),0.1), BURN, 0, 0)
@@ -329,8 +329,8 @@
if(internal_temp_regulation)
if(cabin_air && cabin_air.return_volume() > 0)
- var/delta = cabin_air.temperature - T20C
- cabin_air.temperature -= max(-10, min(10, round(delta/4,0.1)))
+ var/delta = cabin_air.return_temperature() - T20C
+ cabin_air.set_temperature(cabin_air.return_temperature() - max(-10, min(10, round(delta/4,0.1))))
if(internal_tank)
var/datum/gas_mixture/tank_air = internal_tank.return_air()
diff --git a/code/game/mecha/mecha_construction_paths.dm b/code/game/mecha/mecha_construction_paths.dm
index 5160282180..34cb57f661 100644
--- a/code/game/mecha/mecha_construction_paths.dm
+++ b/code/game/mecha/mecha_construction_paths.dm
@@ -642,6 +642,304 @@
user.visible_message("[user] unfastens Gygax Armor Plates.", "You unfasten Gygax Armor Plates.")
return TRUE
+//Begin Medigax
+/datum/component/construction/unordered/mecha_chassis/medigax
+ result = /datum/component/construction/mecha/medigax
+ steps = list(
+ /obj/item/mecha_parts/part/medigax_torso,
+ /obj/item/mecha_parts/part/medigax_left_arm,
+ /obj/item/mecha_parts/part/medigax_right_arm,
+ /obj/item/mecha_parts/part/medigax_left_leg,
+ /obj/item/mecha_parts/part/medigax_right_leg,
+ /obj/item/mecha_parts/part/medigax_head
+ )
+
+/datum/component/construction/mecha/medigax
+ result = /obj/mecha/medical/medigax
+ base_icon = "medigax"
+ steps = list(
+ //1
+ list(
+ "key" = TOOL_WRENCH,
+ "desc" = "The hydraulic systems are disconnected."
+ ),
+
+ //2
+ list(
+ "key" = TOOL_SCREWDRIVER,
+ "back_key" = TOOL_WRENCH,
+ "desc" = "The hydraulic systems are connected."
+ ),
+
+ //3
+ list(
+ "key" = /obj/item/stack/cable_coil,
+ "amount" = 5,
+ "back_key" = TOOL_SCREWDRIVER,
+ "desc" = "The hydraulic systems are active."
+ ),
+
+ //4
+ list(
+ "key" = TOOL_WIRECUTTER,
+ "back_key" = TOOL_SCREWDRIVER,
+ "desc" = "The wiring is added."
+ ),
+
+ //5
+ list(
+ "key" = /obj/item/circuitboard/mecha/gygax/main,
+ "action" = ITEM_DELETE,
+ "back_key" = TOOL_SCREWDRIVER,
+ "desc" = "The wiring is adjusted."
+ ),
+
+ //6
+ list(
+ "key" = TOOL_SCREWDRIVER,
+ "back_key" = TOOL_CROWBAR,
+ "desc" = "Central control module is installed."
+ ),
+
+ //7
+ list(
+ "key" = /obj/item/circuitboard/mecha/gygax/peripherals,
+ "action" = ITEM_DELETE,
+ "back_key" = TOOL_SCREWDRIVER,
+ "desc" = "Central control module is secured."
+ ),
+
+ //8
+ list(
+ "key" = TOOL_SCREWDRIVER,
+ "back_key" = TOOL_CROWBAR,
+ "desc" = "Peripherals control module is installed."
+ ),
+
+ //9
+ list(
+ "key" = /obj/item/circuitboard/mecha/gygax/targeting,
+ "action" = ITEM_DELETE,
+ "back_key" = TOOL_SCREWDRIVER,
+ "desc" = "Peripherals control module is secured."
+ ),
+
+ //10
+ list(
+ "key" = TOOL_SCREWDRIVER,
+ "back_key" = TOOL_CROWBAR,
+ "desc" = "Weapon control module is installed."
+ ),
+
+ //11
+ list(
+ "key" = /obj/item/stock_parts/scanning_module,
+ "action" = ITEM_MOVE_INSIDE,
+ "back_key" = TOOL_SCREWDRIVER,
+ "desc" = "Weapon control module is secured."
+ ),
+
+ //12
+ list(
+ "key" = TOOL_SCREWDRIVER,
+ "back_key" = TOOL_CROWBAR,
+ "desc" = "Scanner module is installed."
+ ),
+
+ //13
+ list(
+ "key" = /obj/item/stock_parts/capacitor,
+ "action" = ITEM_MOVE_INSIDE,
+ "back_key" = TOOL_SCREWDRIVER,
+ "desc" = "Scanner module is secured."
+ ),
+
+ //14
+ list(
+ "key" = TOOL_SCREWDRIVER,
+ "back_key" = TOOL_CROWBAR,
+ "desc" = "Capacitor is installed."
+ ),
+
+ //15
+ list(
+ "key" = /obj/item/stock_parts/cell,
+ "action" = ITEM_MOVE_INSIDE,
+ "back_key" = TOOL_SCREWDRIVER,
+ "desc" = "Capacitor is secured."
+ ),
+
+ //16
+ list(
+ "key" = TOOL_SCREWDRIVER,
+ "back_key" = TOOL_CROWBAR,
+ "desc" = "The power cell is installed."
+ ),
+
+ //17
+ list(
+ "key" = /obj/item/stack/sheet/metal,
+ "amount" = 5,
+ "back_key" = TOOL_SCREWDRIVER,
+ "desc" = "The power cell is secured."
+ ),
+
+ //18
+ list(
+ "key" = TOOL_WRENCH,
+ "back_key" = TOOL_CROWBAR,
+ "desc" = "Internal armor is installed."
+ ),
+
+ //19
+ list(
+ "key" = TOOL_WELDER,
+ "back_key" = TOOL_WRENCH,
+ "desc" = "Internal armor is wrenched."
+ ),
+
+ //20
+ list(
+ "key" = /obj/item/mecha_parts/part/medigax_armor,
+ "action" = ITEM_DELETE,
+ "back_key" = TOOL_WELDER,
+ "desc" = "Internal armor is welded."
+ ),
+
+ //21
+ list(
+ "key" = TOOL_WRENCH,
+ "back_key" = TOOL_CROWBAR,
+ "desc" = "External armor is installed."
+ ),
+
+ //22
+ list(
+ "key" = TOOL_WELDER,
+ "back_key" = TOOL_WRENCH,
+ "desc" = "External armor is wrenched."
+ ),
+
+ )
+
+/datum/component/construction/mecha/medigax/action(datum/source, atom/used_atom, mob/user)
+ return check_step(used_atom,user)
+
+/datum/component/construction/mecha/medigax/custom_action(obj/item/I, mob/living/user, diff)
+ if(!..())
+ return FALSE
+
+ switch(index)
+ if(1)
+ user.visible_message("[user] connects [parent] hydraulic systems", "You connect [parent] hydraulic systems.")
+ if(2)
+ if(diff==FORWARD)
+ user.visible_message("[user] activates [parent] hydraulic systems.", "You activate [parent] hydraulic systems.")
+ else
+ user.visible_message("[user] disconnects [parent] hydraulic systems", "You disconnect [parent] hydraulic systems.")
+ if(3)
+ if(diff==FORWARD)
+ user.visible_message("[user] adds the wiring to [parent].", "You add the wiring to [parent].")
+ else
+ user.visible_message("[user] deactivates [parent] hydraulic systems.", "You deactivate [parent] hydraulic systems.")
+ if(4)
+ if(diff==FORWARD)
+ user.visible_message("[user] adjusts the wiring of [parent].", "You adjust the wiring of [parent].")
+ else
+ user.visible_message("[user] removes the wiring from [parent].", "You remove the wiring from [parent].")
+ if(5)
+ if(diff==FORWARD)
+ user.visible_message("[user] installs [I] into [parent].", "You install [I] into [parent].")
+ else
+ user.visible_message("[user] disconnects the wiring of [parent].", "You disconnect the wiring of [parent].")
+ if(6)
+ if(diff==FORWARD)
+ user.visible_message("[user] secures the mainboard.", "You secure the mainboard.")
+ else
+ user.visible_message("[user] removes the central control module from [parent].", "You remove the central computer mainboard from [parent].")
+ if(7)
+ if(diff==FORWARD)
+ user.visible_message("[user] installs [I] into [parent].", "You install [I] into [parent].")
+ else
+ user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.")
+ if(8)
+ if(diff==FORWARD)
+ user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.")
+ else
+ user.visible_message("[user] removes the peripherals control module from [parent].", "You remove the peripherals control module from [parent].")
+ if(9)
+ if(diff==FORWARD)
+ user.visible_message("[user] installs [I] into [parent].", "You install [I] into [parent].")
+ else
+ user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.")
+ if(10)
+ if(diff==FORWARD)
+ user.visible_message("[user] secures the weapon control module.", "You secure the weapon control module.")
+ else
+ user.visible_message("[user] removes the weapon control module from [parent].", "You remove the weapon control module from [parent].")
+ if(11)
+ if(diff==FORWARD)
+ user.visible_message("[user] installs [I] to [parent].", "You install [I] to [parent].")
+ else
+ user.visible_message("[user] unfastens the weapon control module.", "You unfasten the weapon control module.")
+ if(12)
+ if(diff==FORWARD)
+ user.visible_message("[user] secures the scanner module.", "You secure the scanner module.")
+ else
+ user.visible_message("[user] removes the scanner module from [parent].", "You remove the scanner module from [parent].")
+ if(13)
+ if(diff==FORWARD)
+ user.visible_message("[user] installs [I] to [parent].", "You install [I] to [parent].")
+ else
+ user.visible_message("[user] unfastens the scanner module.", "You unfasten the scanner module.")
+ if(14)
+ if(diff==FORWARD)
+ user.visible_message("[user] secures the capacitor.", "You secure the capacitor.")
+ else
+ user.visible_message("[user] removes the capacitor from [parent].", "You remove the capacitor from [parent].")
+ if(15)
+ if(diff==FORWARD)
+ user.visible_message("[user] installs [I] into [parent].", "You install [I] into [parent].")
+ else
+ user.visible_message("[user] unfastens the capacitor.", "You unfasten the capacitor.")
+ if(16)
+ if(diff==FORWARD)
+ user.visible_message("[user] secures the power cell.", "You secure the power cell.")
+ else
+ user.visible_message("[user] pries the power cell from [parent].", "You pry the power cell from [parent].")
+ if(17)
+ if(diff==FORWARD)
+ user.visible_message("[user] installs the internal armor layer to [parent].", "You install the internal armor layer to [parent].")
+ else
+ user.visible_message("[user] unfastens the power cell.", "You unfasten the power cell.")
+ if(18)
+ if(diff==FORWARD)
+ user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.")
+ else
+ user.visible_message("[user] pries internal armor layer from [parent].", "You pry internal armor layer from [parent].")
+ if(19)
+ if(diff==FORWARD)
+ user.visible_message("[user] welds the internal armor layer to [parent].", "You weld the internal armor layer to [parent].")
+ else
+ user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.")
+ if(20)
+ if(diff==FORWARD)
+ user.visible_message("[user] installs [I] to [parent].", "You install [I] to [parent].")
+ else
+ user.visible_message("[user] cuts the internal armor layer from [parent].", "You cut the internal armor layer from [parent].")
+ if(21)
+ if(diff==FORWARD)
+ user.visible_message("[user] secures Gygax Armor Plates.", "You secure Medical Gygax Armor Plates.")
+ else
+ user.visible_message("[user] pries Gygax Armor Plates from [parent].", "You pry Medical Gygax Armor Plates from [parent].")
+ if(22)
+ if(diff==FORWARD)
+ user.visible_message("[user] welds Gygax Armor Plates to [parent].", "You weld Medical Gygax Armor Plates to [parent].")
+ else
+ user.visible_message("[user] unfastens Gygax Armor Plates.", "You unfasten Medical Gygax Armor Plates.")
+ return TRUE
+// End Medigax
+
/datum/component/construction/unordered/mecha_chassis/firefighter
result = /datum/component/construction/mecha/firefighter
steps = list(
diff --git a/code/game/mecha/mecha_control_console.dm b/code/game/mecha/mecha_control_console.dm
index 91c97ff14a..c7db051331 100644
--- a/code/game/mecha/mecha_control_console.dm
+++ b/code/game/mecha/mecha_control_console.dm
@@ -5,62 +5,74 @@
icon_keyboard = "tech_key"
req_access = list(ACCESS_ROBOTICS)
circuit = /obj/item/circuitboard/computer/mecha_control
- var/list/located = list()
- var/screen = 0
- var/stored_data
+ ui_x = 500
+ ui_y = 500
-/obj/machinery/computer/mecha/ui_interact(mob/user)
- . = ..()
- var/dat = "[src.name]"
- if(screen == 0)
- dat += "Tracking beacons data
"
- var/list/trackerlist = list()
- for(var/obj/mecha/MC in GLOB.mechas_list)
- trackerlist += MC.trackers
- for(var/obj/item/mecha_parts/mecha_tracking/TR in trackerlist)
- var/answer = TR.get_mecha_info()
- if(answer)
- dat += {"
[answer]
- Send message
- Show exosuit log
- [TR.recharging?"Recharging EMP Pulse...
":"(EMP Pulse)
"]"}
+/obj/machinery/computer/mecha/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, "ExosuitControlConsole", name, ui_x, ui_y, master_ui, state)
+ ui.open()
- if(screen==1)
- dat += "Log contents
"
- dat += "Return
"
- dat += "[stored_data]"
+/obj/machinery/computer/mecha/ui_data(mob/user)
+ var/list/data = list()
- dat += "(Refresh)
"
- dat += ""
+ var/list/trackerlist = list()
+ for(var/obj/mecha/MC in GLOB.mechas_list)
+ trackerlist += MC.trackers
- user << browse(dat, "window=computer;size=400x500")
- onclose(user, "computer")
+ data["mechs"] = list()
+ for(var/obj/item/mecha_parts/mecha_tracking/MT in trackerlist)
+ if(!MT.chassis)
+ continue
+ var/obj/mecha/M = MT.chassis
+ var/list/mech_data = list(
+ name = M.name,
+ integrity = round((M.obj_integrity / M.max_integrity) * 100),
+ charge = M.cell ? round(M.cell.percent()) : null,
+ airtank = M.internal_tank ? M.return_pressure() : null,
+ pilot = M.occupant,
+ location = get_area_name(M, TRUE),
+ active_equipment = M.selected,
+ emp_recharging = MT.recharging,
+ tracker_ref = REF(MT)
+ )
+ if(istype(M, /obj/mecha/working/ripley))
+ var/obj/mecha/working/ripley/RM = M
+ mech_data += list(
+ cargo_space = round((RM.cargo.len / RM.cargo_capacity) * 100)
+ )
-/obj/machinery/computer/mecha/Topic(href, href_list)
+ data["mechs"] += list(mech_data)
+
+ return data
+
+/obj/machinery/computer/mecha/ui_act(action, params)
if(..())
return
- if(href_list["send_message"])
- var/obj/item/mecha_parts/mecha_tracking/MT = locate(href_list["send_message"])
- if (!istype(MT))
- return
- var/message = stripped_input(usr,"Input message","Transmit message")
- var/obj/mecha/M = MT.in_mecha()
- if(trim(message) && M)
- M.occupant_message(message)
- return
- if(href_list["shock"])
- var/obj/item/mecha_parts/mecha_tracking/MT = locate(href_list["shock"])
- if (istype(MT))
- MT.shock()
- if(href_list["get_log"])
- var/obj/item/mecha_parts/mecha_tracking/MT = locate(href_list["get_log"])
- if(istype(MT))
- stored_data = MT.get_mecha_log()
- screen = 1
- if(href_list["return"])
- screen = 0
- updateUsrDialog()
- return
+
+ switch(action)
+ if("send_message")
+ var/obj/item/mecha_parts/mecha_tracking/MT = locate(params["tracker_ref"])
+ if(!istype(MT))
+ return
+ var/message = stripped_input(usr, "Input message", "Transmit message")
+ var/obj/mecha/M = MT.chassis
+ if(trim(message) && M)
+ M.occupant_message(message)
+ to_chat(usr, "Message sent.")
+ . = TRUE
+ if("shock")
+ var/obj/item/mecha_parts/mecha_tracking/MT = locate(params["tracker_ref"])
+ if(!istype(MT))
+ return
+ var/obj/mecha/M = MT.chassis
+ if(M)
+ MT.shock()
+ log_game("[key_name(usr)] has activated remote EMP on exosuit [M], located at [loc_name(M)], which is currently [M.occupant? "being piloted by [key_name(M.occupant)]." : "without a pilot."] ")
+ message_admins("[key_name_admin(usr)][ADMIN_FLW(usr)] has activated remote EMP on exosuit [M][ADMIN_JMP(M)], which is currently [M.occupant ? "being piloted by [key_name_admin(M.occupant)][ADMIN_FLW(M.occupant)]." : "without a pilot."] ")
+ . = TRUE
/obj/item/mecha_parts/mecha_tracking
name = "exosuit tracking beacon"
@@ -68,24 +80,31 @@
icon = 'icons/obj/device.dmi'
icon_state = "motion2"
w_class = WEIGHT_CLASS_SMALL
- var/ai_beacon = FALSE //If this beacon allows for AI control. Exists to avoid using istype() on checking.
- var/recharging = 0
+ /// If this beacon allows for AI control. Exists to avoid using istype() on checking
+ var/ai_beacon = FALSE
+ /// Cooldown variable for EMP pulsing
+ var/recharging = FALSE
+ /// The Mecha that this tracking beacon is attached to
+ var/obj/mecha/chassis
+/**
+ * Returns a html formatted string describing attached mech status
+ */
/obj/item/mecha_parts/mecha_tracking/proc/get_mecha_info()
- if(!in_mecha())
- return 0
- var/obj/mecha/M = src.loc
- var/cell_charge = M.get_charge()
- var/answer = {"Name: [M.name]
-Integrity: [M.obj_integrity/M.max_integrity*100]%
-Cell charge: [isnull(cell_charge)?"Not found":"[M.cell.percent()]%"]
-Airtank: [M.return_pressure()]kPa
-Pilot: [M.occupant||"None"]
-Location: [get_area(M)||"Unknown"]
-Active equipment: [M.selected||"None"] "}
- if(istype(M, /obj/mecha/working/ripley))
- var/obj/mecha/working/ripley/RM = M
- answer += "Used cargo space: [RM.cargo.len/RM.cargo_capacity*100]%
"
+ if(!chassis)
+ return FALSE
+
+ var/cell_charge = chassis.get_charge()
+ var/answer = {"Name: [chassis.name]
+ Integrity: [round((chassis.obj_integrity/chassis.max_integrity * 100), 0.01)]%
+ Cell Charge: [isnull(cell_charge) ? "Not Found":"[chassis.cell.percent()]%"]
+ Airtank: [chassis.internal_tank ? "[round(chassis.return_pressure(), 0.01)]" : "Not Equipped"] kPa
+ Pilot: [chassis.occupant || "None"]
+ Location: [get_area_name(chassis, TRUE) || "Unknown"]
+ Active Equipment: [chassis.selected || "None"]"}
+ if(istype(chassis, /obj/mecha/working/ripley))
+ var/obj/mecha/working/ripley/RM = chassis
+ answer += "
Used Cargo Space: [round((RM.cargo.len / RM.cargo_capacity * 100), 0.01)]%"
return answer
@@ -95,42 +114,41 @@
qdel(src)
/obj/item/mecha_parts/mecha_tracking/Destroy()
- if(ismecha(loc))
- var/obj/mecha/M = loc
- if(src in M.trackers)
- M.trackers -= src
+ if(chassis)
+ if(src in chassis.trackers)
+ chassis.trackers -= src
+ chassis = null
return ..()
-/obj/item/mecha_parts/mecha_tracking/proc/in_mecha()
- if(ismecha(loc))
- return loc
- return 0
+/obj/item/mecha_parts/mecha_tracking/try_attach_part(mob/user, obj/mecha/M)
+ if(!..())
+ return
+ M.trackers += src
+ M.diag_hud_set_mechtracking()
+ chassis = M
+/**
+ * Attempts to EMP mech that the tracker is attached to, if there is one and tracker is not on cooldown
+ */
/obj/item/mecha_parts/mecha_tracking/proc/shock()
if(recharging)
return
- var/obj/mecha/M = in_mecha()
- if(M)
- M.emp_act(EMP_HEAVY)
- addtimer(CALLBACK(src, /obj/item/mecha_parts/mecha_tracking/proc/recharge), 15 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE)
- recharging = 1
+ if(chassis)
+ chassis.emp_act(EMP_HEAVY)
+ addtimer(CALLBACK(src, /obj/item/mecha_parts/mecha_tracking/proc/recharge), 5 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE)
+ recharging = TRUE
+/**
+ * Resets recharge variable, allowing tracker to be EMP pulsed again
+ */
/obj/item/mecha_parts/mecha_tracking/proc/recharge()
- recharging = 0
-
-/obj/item/mecha_parts/mecha_tracking/proc/get_mecha_log()
- if(!ismecha(loc))
- return 0
- var/obj/mecha/M = src.loc
- return M.get_log_html()
-
+ recharging = FALSE
/obj/item/mecha_parts/mecha_tracking/ai_control
name = "exosuit AI control beacon"
desc = "A device used to transmit exosuit data. Also allows active AI units to take control of said exosuit."
ai_beacon = TRUE
-
/obj/item/storage/box/mechabeacons
name = "exosuit tracking beacons"
diff --git a/code/game/mecha/mecha_defense.dm b/code/game/mecha/mecha_defense.dm
index 395ac810f4..dedbef1906 100644
--- a/code/game/mecha/mecha_defense.dm
+++ b/code/game/mecha/mecha_defense.dm
@@ -54,7 +54,7 @@
. *= booster_damage_modifier
-/obj/mecha/attack_hand(mob/living/user)
+/obj/mecha/attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/mecha/mecha_parts.dm b/code/game/mecha/mecha_parts.dm
index 811ffdef35..8a9e4e641f 100644
--- a/code/game/mecha/mecha_parts.dm
+++ b/code/game/mecha/mecha_parts.dm
@@ -9,6 +9,13 @@
w_class = WEIGHT_CLASS_GIGANTIC
flags_1 = CONDUCT_1
+/obj/item/mecha_parts/proc/try_attach_part(mob/user, obj/mecha/M) //For attaching parts to a finished mech
+ if(!user.transferItemToLoc(src, M))
+ to_chat(user, "\The [src] is stuck to your hand, you cannot put it in \the [M]!")
+ return FALSE
+ user.visible_message("[user] attaches [src] to [M].", "You attach [src] to [M].")
+ return TRUE
+
/obj/item/mecha_parts/chassis
name = "Mecha Chassis"
icon_state = "backbone"
@@ -129,6 +136,47 @@
desc = "A set of armor plates designed for the Gygax. Designed to effectively deflect damage with a lightweight construction."
icon_state = "gygax_armor"
+///////// Medical Gygax
+
+/obj/item/mecha_parts/chassis/medigax
+ name = "\improper Medical Gygax chassis"
+ construct_type = /datum/component/construction/unordered/mecha_chassis/medigax
+
+/obj/item/mecha_parts/part/medigax_torso
+ name = "\improper Medical Gygax torso"
+ desc = "A torso part of Gygax. Contains power unit, processing core and life support systems."
+ icon_state = "medigax_harness"
+
+/obj/item/mecha_parts/part/medigax_head
+ name = "\improper Medical Gygax head"
+ desc = "A Gygax head. Houses advanced surveillance and targeting sensors."
+ icon_state = "medigax_head"
+
+/obj/item/mecha_parts/part/medigax_left_arm
+ name = "\improper Medical Gygax left arm"
+ desc = "A Gygax left arm. Data and power sockets are compatible with most exosuit tools and weapons."
+ icon_state = "medigax_l_arm"
+
+/obj/item/mecha_parts/part/medigax_right_arm
+ name = "\improper Medical Gygax right arm"
+ desc = "A Gygax right arm. Data and power sockets are compatible with most exosuit tools and weapons."
+ icon_state = "medigax_r_arm"
+
+/obj/item/mecha_parts/part/medigax_left_leg
+ name = "\improper Medical Gygax left leg"
+ desc = "A Gygax left leg. Constructed with advanced servomechanisms and actuators to enable faster speed."
+ icon_state = "medigax_l_leg"
+
+/obj/item/mecha_parts/part/medigax_right_leg
+ name = "\improper Medical Gygax right leg"
+ desc = "A Gygax right leg. Constructed with advanced servomechanisms and actuators to enable faster speed."
+ icon_state = "medigax_r_leg"
+
+/obj/item/mecha_parts/part/medigax_armor
+ gender = PLURAL
+ name = "\improper Medical Gygax armor plates"
+ desc = "A set of armor plates designed for the Gygax. Designed to effectively deflect damage with a lightweight construction."
+ icon_state = "medigax_armor"
//////////// Durand
diff --git a/code/game/mecha/mecha_topic.dm b/code/game/mecha/mecha_topic.dm
index b1ab944b49..067cffd319 100644
--- a/code/game/mecha/mecha_topic.dm
+++ b/code/game/mecha/mecha_topic.dm
@@ -75,7 +75,7 @@
var/cell_charge = get_charge()
var/datum/gas_mixture/int_tank_air = internal_tank.return_air()
var/tank_pressure = internal_tank ? round(int_tank_air.return_pressure(),0.01) : "None"
- var/tank_temperature = internal_tank ? int_tank_air.temperature : "Unknown"
+ var/tank_temperature = internal_tank ? int_tank_air.return_temperature() : "Unknown"
var/cabin_pressure = round(return_pressure(),0.01)
. = {"[report_internal_damage()]
[integrity<30?"DAMAGE LEVEL CRITICAL
":null]
@@ -256,7 +256,7 @@
output_access_dialog(id_card, usr)
if(href_list["del_req_access"] && add_req_access)
- operation_req_access -= text2num(href_list["add_req_access"])
+ operation_req_access -= text2num(href_list["del_req_access"])
output_access_dialog(id_card, usr)
if(href_list["finish_req_access"])
diff --git a/code/game/mecha/mecha_wreckage.dm b/code/game/mecha/mecha_wreckage.dm
index ecf39bcb0b..9175489c89 100644
--- a/code/game/mecha/mecha_wreckage.dm
+++ b/code/game/mecha/mecha_wreckage.dm
@@ -125,6 +125,24 @@
name = "\improper Dark Gygax wreckage"
icon_state = "darkgygax-broken"
+/obj/structure/mecha_wreckage/medigax
+ name = "\improper Medical Gygax wreckage"
+ icon_state = "medigax-broken"
+
+/obj/structure/mecha_wreckage/medigax/Initialize()
+ . = ..()
+ var/list/parts = list(/obj/item/mecha_parts/part/medigax_torso,
+ /obj/item/mecha_parts/part/medigax_head,
+ /obj/item/mecha_parts/part/medigax_left_arm,
+ /obj/item/mecha_parts/part/medigax_right_arm,
+ /obj/item/mecha_parts/part/medigax_left_leg,
+ /obj/item/mecha_parts/part/medigax_right_leg)
+ for(var/i = 0; i < 2; i++)
+ if(parts.len && prob(40))
+ var/part = pick(parts)
+ welder_salvage += part
+ parts -= part
+
/obj/structure/mecha_wreckage/marauder
name = "\improper Marauder wreckage"
icon_state = "marauder-broken"
diff --git a/code/game/mecha/medical/medigax.dm b/code/game/mecha/medical/medigax.dm
new file mode 100644
index 0000000000..98b7c9455b
--- /dev/null
+++ b/code/game/mecha/medical/medigax.dm
@@ -0,0 +1,34 @@
+/obj/mecha/medical/medigax
+ desc = "A Gygax with it's actuator overload stripped and a slick white paint scheme, for medical use, These exosuits are developed and produced by Vey-Med. (© All rights reserved)."
+ name = "\improper Medical Gygax"
+ icon_state = "medigax"
+ step_in = 1.75 // a little faster than an odysseus
+ max_temperature = 25000
+ max_integrity = 250
+ wreckage = /obj/structure/mecha_wreckage/odysseus
+ armor = list("melee" = 25, "bullet" = 20, "laser" = 30, "energy" = 15, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100)
+ internal_damage_threshold = 35
+ deflect_chance = 15
+ step_energy_drain = 6
+ infra_luminosity = 6
+
+
+/obj/mecha/medical/medigax/moved_inside(mob/living/carbon/human/H)
+ . = ..()
+ if(.)
+ var/datum/atom_hud/hud = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED]
+ hud.add_hud_to(H)
+
+/obj/mecha/medical/medigax/go_out()
+ if(isliving(occupant))
+ var/mob/living/carbon/human/L = occupant
+ var/datum/atom_hud/hud = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED]
+ hud.remove_hud_from(L)
+ ..()
+
+/obj/mecha/medical/medigax/mmi_moved_inside(obj/item/mmi/mmi_as_oc, mob/user)
+ . = ..()
+ if(.)
+ var/datum/atom_hud/hud = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED]
+ var/mob/living/brain/B = mmi_as_oc.brainmob
+ hud.add_hud_to(B)
diff --git a/code/game/objects/buckling.dm b/code/game/objects/buckling.dm
index bacb8c6669..eabbdfad88 100644
--- a/code/game/objects/buckling.dm
+++ b/code/game/objects/buckling.dm
@@ -9,7 +9,7 @@
var/buckle_prevents_pull = FALSE
//Interaction
-/atom/movable/attack_hand(mob/living/user)
+/atom/movable/attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/objects/effects/contraband.dm b/code/game/objects/effects/contraband.dm
index c56731b3d3..e4f1c854d0 100644
--- a/code/game/objects/effects/contraband.dm
+++ b/code/game/objects/effects/contraband.dm
@@ -101,7 +101,7 @@
to_chat(user, "You carefully remove the poster from the wall.")
roll_and_drop(user.loc)
-/obj/structure/sign/poster/attack_hand(mob/user)
+/obj/structure/sign/poster/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
@@ -212,6 +212,11 @@
desc = "A heretical poster depicting the titular star of an equally heretical book."
icon_state = "poster4"
+/obj/structure/sign/poster/contraband/post_ratvar
+ name = "Post This Ratvar"
+ desc = "Oh what in the hell? Those cultists have animated paper technology and they use it for a meme?"
+ icon_state = "postvar"
+
/obj/structure/sign/poster/contraband/syndicate_recruitment
name = "Syndicate Recruitment"
desc = "See the galaxy! Shatter corrupt megacorporations! Join today!"
diff --git a/code/game/objects/effects/decals/cleanable/humans.dm b/code/game/objects/effects/decals/cleanable/humans.dm
index a5acc7d394..fa16a95faf 100644
--- a/code/game/objects/effects/decals/cleanable/humans.dm
+++ b/code/game/objects/effects/decals/cleanable/humans.dm
@@ -91,7 +91,7 @@
var/mob/living/carbon/human/H = O
var/obj/item/clothing/shoes/S = H.shoes
if(S && S.bloody_shoes[blood_state])
- if(color != bloodtype_to_color(S.last_bloodtype))
+ if(color != S.last_blood_color)
return
S.bloody_shoes[blood_state] = max(S.bloody_shoes[blood_state] - BLOOD_LOSS_PER_STEP, 0)
shoe_types |= S.type
@@ -104,7 +104,7 @@
var/mob/living/carbon/human/H = O
var/obj/item/clothing/shoes/S = H.shoes
if(S && S.bloody_shoes[blood_state])
- if(color != bloodtype_to_color(S.last_bloodtype))//last entry - we check its color
+ if(color != S.last_blood_color)//last entry - we check its color
return
S.bloody_shoes[blood_state] = max(S.bloody_shoes[blood_state] - BLOOD_LOSS_PER_STEP, 0)
shoe_types |= S.type
diff --git a/code/game/objects/effects/decals/cleanable/misc.dm b/code/game/objects/effects/decals/cleanable/misc.dm
index 2e45bbfca5..01f0b6f957 100644
--- a/code/game/objects/effects/decals/cleanable/misc.dm
+++ b/code/game/objects/effects/decals/cleanable/misc.dm
@@ -137,7 +137,7 @@
random_icon_states = list("vomit_1", "vomit_2", "vomit_3", "vomit_4")
beauty = -150
-/obj/effect/decal/cleanable/vomit/attack_hand(mob/user)
+/obj/effect/decal/cleanable/vomit/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/objects/effects/effect_system/effects_foam.dm b/code/game/objects/effects/effect_system/effects_foam.dm
index f98b4937ef..0cf95ea263 100644
--- a/code/game/objects/effects/effect_system/effects_foam.dm
+++ b/code/game/objects/effects/effect_system/effects_foam.dm
@@ -40,12 +40,11 @@
if(hotspot && istype(T) && T.air)
qdel(hotspot)
var/datum/gas_mixture/G = T.air
- var/plas_amt = min(30,G.gases[/datum/gas/plasma]) //Absorb some plasma
- G.gases[/datum/gas/plasma] -= plas_amt
+ var/plas_amt = min(30,G.get_moles(/datum/gas/plasma)) //Absorb some plasma
+ G.adjust_moles(/datum/gas/plasma,-plas_amt)
absorbed_plasma += plas_amt
- if(G.temperature > T20C)
- G.temperature = max(G.temperature/2,T20C)
- GAS_GARBAGE_COLLECT(G.gases)
+ if(G.return_temperature() > T20C)
+ G.set_temperature(max(G.return_temperature()/2,T20C))
T.air_update_turf()
/obj/effect/particle_effect/foam/firefighting/kill_foam()
@@ -285,7 +284,7 @@
/obj/structure/foamedmetal/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
playsound(src.loc, 'sound/weapons/tap.ogg', 100, 1)
-/obj/structure/foamedmetal/attack_hand(mob/user)
+/obj/structure/foamedmetal/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
@@ -317,15 +316,13 @@
O.ClearWet()
if(O.air)
var/datum/gas_mixture/G = O.air
- G.temperature = 293.15
+ G.set_temperature(293.15)
for(var/obj/effect/hotspot/H in O)
qdel(H)
- var/list/G_gases = G.gases
- for(var/I in G_gases)
+ for(var/I in G.get_gases())
if(I == /datum/gas/oxygen || I == /datum/gas/nitrogen)
continue
- G_gases[I] = 0
- GAS_GARBAGE_COLLECT(G.gases)
+ G.set_moles(I, 0)
O.air_update_turf()
for(var/obj/machinery/atmospherics/components/unary/U in O)
if(!U.welded)
diff --git a/code/game/objects/effects/effect_system/effects_smoke.dm b/code/game/objects/effects/effect_system/effects_smoke.dm
index 80c0db2b7d..9a11f0e1d7 100644
--- a/code/game/objects/effects/effect_system/effects_smoke.dm
+++ b/code/game/objects/effects/effect_system/effects_smoke.dm
@@ -166,15 +166,13 @@
if(T.air)
var/datum/gas_mixture/G = T.air
if(!distcheck || get_dist(T, location) < blast) // Otherwise we'll get silliness like people using Nanofrost to kill people through walls with cold air
- G.temperature = temperature
+ G.set_temperature(temperature)
T.air_update_turf()
for(var/obj/effect/hotspot/H in T)
qdel(H)
- var/list/G_gases = G.gases
- if(G_gases[/datum/gas/plasma])
- G_gases[/datum/gas/nitrogen] += (G_gases[/datum/gas/plasma])
- G_gases[/datum/gas/plasma] = 0
- GAS_GARBAGE_COLLECT(G.gases)
+ if(G.get_moles(/datum/gas/plasma))
+ G.adjust_moles(/datum/gas/nitrogen, G.get_moles(/datum/gas/plasma))
+ G.set_moles(/datum/gas/plasma, 0)
if (weldvents)
for(var/obj/machinery/atmospherics/components/unary/U in T)
if(!isnull(U.welded) && !U.welded) //must be an unwelded vent pump or vent scrubber.
diff --git a/code/game/objects/effects/mines.dm b/code/game/objects/effects/mines.dm
index 40104d5ea8..d5f53b2f1c 100644
--- a/code/game/objects/effects/mines.dm
+++ b/code/game/objects/effects/mines.dm
@@ -146,14 +146,13 @@
spawn(0)
new /datum/hallucination/delusion(victim, TRUE, "demon",duration,0)
- var/obj/item/twohanded/required/chainsaw/doomslayer/chainsaw = new(victim.loc)
+ var/obj/item/chainsaw/doomslayer/chainsaw = new(victim.loc)
victim.log_message("entered a blood frenzy", LOG_ATTACK)
ADD_TRAIT(chainsaw, TRAIT_NODROP, CHAINSAW_FRENZY_TRAIT)
victim.drop_all_held_items()
victim.put_in_hands(chainsaw, forced = TRUE)
chainsaw.attack_self(victim)
- chainsaw.wield(victim)
victim.reagents.add_reagent(/datum/reagent/medicine/adminordrazine,25)
to_chat(victim, "KILL, KILL, KILL! YOU HAVE NO ALLIES ANYMORE, KILL THEM ALL!")
diff --git a/code/game/objects/effects/portals.dm b/code/game/objects/effects/portals.dm
index 504b931685..e363529c46 100644
--- a/code/game/objects/effects/portals.dm
+++ b/code/game/objects/effects/portals.dm
@@ -60,7 +60,7 @@
/obj/effect/portal/attack_tk(mob/user)
return
-/obj/effect/portal/attack_hand(mob/user)
+/obj/effect/portal/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/objects/effects/spawners/bombspawner.dm b/code/game/objects/effects/spawners/bombspawner.dm
index a0ae300c79..b5aaff5746 100644
--- a/code/game/objects/effects/spawners/bombspawner.dm
+++ b/code/game/objects/effects/spawners/bombspawner.dm
@@ -19,11 +19,11 @@
var/obj/item/tank/internals/plasma/PT = new(V)
var/obj/item/tank/internals/oxygen/OT = new(V)
- PT.air_contents.gases[/datum/gas/plasma] = pressure_p*PT.volume/(R_IDEAL_GAS_EQUATION*CELSIUS_TO_KELVIN(temp_p))
- PT.air_contents.temperature = CELSIUS_TO_KELVIN(temp_p)
+ PT.air_contents.set_moles(/datum/gas/plasma, pressure_p*PT.volume/(R_IDEAL_GAS_EQUATION*CELSIUS_TO_KELVIN(temp_p)))
+ PT.air_contents.set_temperature(CELSIUS_TO_KELVIN(temp_p))
- OT.air_contents.gases[/datum/gas/oxygen] = pressure_o*OT.volume/(R_IDEAL_GAS_EQUATION*CELSIUS_TO_KELVIN(temp_o))
- OT.air_contents.temperature = CELSIUS_TO_KELVIN(temp_o)
+ OT.air_contents.set_moles(/datum/gas/oxygen, pressure_o*OT.volume/(R_IDEAL_GAS_EQUATION*CELSIUS_TO_KELVIN(temp_o)))
+ OT.air_contents.set_temperature(CELSIUS_TO_KELVIN(temp_o))
V.tank_one = PT
V.tank_two = OT
diff --git a/code/game/objects/effects/spawners/lootdrop.dm b/code/game/objects/effects/spawners/lootdrop.dm
index 57b25a8901..8944b55cc4 100644
--- a/code/game/objects/effects/spawners/lootdrop.dm
+++ b/code/game/objects/effects/spawners/lootdrop.dm
@@ -451,7 +451,7 @@
lootcount = 1
spawn_on_turf = FALSE
//Note this is out of a 100 - Meaning the number you see is also the percent its going to pick that
-//This is ment for "low" loot that anyone could fine in a toilet, for better gear use high loot toilet
+//This is meant for "low" loot that anyone could find in a toilet, for better gear use high loot toilet
loot = list("" = 30,
/obj/item/lighter = 2,
/obj/item/tape/random = 1,
@@ -466,7 +466,7 @@
/obj/effect/spawner/lootdrop/cigars_cases/no_turf = 2,
/obj/effect/spawner/lootdrop/space_cash/no_turf = 5,
/obj/item/reagent_containers/food/snacks/grown/cannabis = 5,
- /obj/item/storage/pill_bottle/dice = 5,
+ /obj/item/storage/box/dice = 5,
/obj/item/toy/cards/deck = 5,
/obj/effect/spawner/lootdrop/druggie_pill/no_turf = 5
)
@@ -476,7 +476,7 @@
lootcount = 1
spawn_on_turf = FALSE
//Note this is out of a 100 - Meaning the number you see is also the percent its going to pick that
-//This is ment for "prison" loot that is rather rare and ment for "prisoners if they get a crowbar to fine, or sec.
+//This is meant for "prison" loot that is rather rare and meant for "prisoners if they get a crowbar to fine, or sec.
loot = list("" = 10,
/obj/item/lighter = 5,
/obj/item/poster/random_contraband = 5,
@@ -485,7 +485,7 @@
/obj/effect/spawner/lootdrop/cig_packs/no_turf = 10,
/obj/effect/spawner/lootdrop/cigars_cases/no_turf = 5,
/obj/item/reagent_containers/food/snacks/grown/cannabis = 5,
- /obj/item/storage/pill_bottle/dice = 5,
+ /obj/item/storage/box/dice = 5,
/obj/item/toy/cards/deck = 5,
/obj/effect/spawner/lootdrop/druggie_pill/no_turf = 5,
/obj/item/kitchen/knife = 5,
@@ -666,28 +666,18 @@
lootcount = 1
spawn_on_turf = FALSE
loot = list("" = 50,
- /obj/item/weaponcrafting/improvised_parts/barrel_rifle = 10,
- /obj/item/weaponcrafting/improvised_parts/barrel_shotgun = 5,
- /obj/item/weaponcrafting/improvised_parts/barrel_pistol = 5,
- /obj/item/weaponcrafting/improvised_parts/rifle_receiver = 10,
- /obj/item/weaponcrafting/improvised_parts/shotgun_receiver = 3,
- /obj/item/weaponcrafting/improvised_parts/pistol_receiver = 3,
- /obj/item/weaponcrafting/improvised_parts/laser_receiver = 1,
- /obj/item/weaponcrafting/improvised_parts/trigger_assembly = 10,
- /obj/item/weaponcrafting/improvised_parts/makeshift_lens = 3,
+ /obj/item/weaponcrafting/improvised_parts/rifle_receiver = 13,
+ /obj/item/weaponcrafting/improvised_parts/shotgun_receiver = 13,
+ /obj/item/weaponcrafting/improvised_parts/trigger_assembly = 12,
)
/obj/effect/spawner/lootdrop/weapon_parts
- name = "random weapon parts spawner 25%"
+ name = "random weapon parts spawner 20%"
lootcount = 1
spawn_on_turf = FALSE
- loot = list("" = 75,
- /obj/item/weaponcrafting/improvised_parts/barrel_rifle = 5,
- /obj/item/weaponcrafting/improvised_parts/barrel_pistol = 5,
+ loot = list("" = 80,
/obj/item/weaponcrafting/improvised_parts/rifle_receiver = 5,
- /obj/item/weaponcrafting/improvised_parts/pistol_receiver = 2,
/obj/item/weaponcrafting/improvised_parts/trigger_assembly = 5,
- /obj/item/weaponcrafting/improvised_parts/makeshift_lens = 3,
)
/obj/effect/spawner/lootdrop/ammo
@@ -695,8 +685,6 @@
lootcount = 1
spawn_on_turf = FALSE
loot = list("" = 25,
- /obj/item/ammo_box/c32mm = 15,
- /obj/item/ammo_box/r32mm = 15,
/obj/item/ammo_box/magazine/wt550m9 = 1,
/obj/item/ammo_casing/shotgun/buckshot = 7,
/obj/item/ammo_casing/shotgun/rubbershot = 7,
@@ -709,8 +697,6 @@
lootcount = 1
spawn_on_turf = FALSE
loot = list("" = 50,
- /obj/item/ammo_box/c32mm = 7,
- /obj/item/ammo_box/r32mm = 7,
/obj/item/ammo_box/magazine/wt550m9 = 2,
/obj/item/ammo_casing/shotgun/buckshot = 10,
/obj/item/ammo_casing/shotgun/rubbershot = 10,
diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm
index 14b3c4e73e..745b281d2a 100644
--- a/code/game/objects/effects/spiders.dm
+++ b/code/game/objects/effects/spiders.dm
@@ -69,6 +69,7 @@
if(prob(50))
to_chat(mover, "You get stuck in \the [src] for a moment.")
return FALSE
+ return TRUE
else if(istype(mover, /obj/item/projectile))
return prob(30)
@@ -152,7 +153,7 @@
else
..()
-/obj/structure/spider/spiderling/attack_hand(mob/user)
+/obj/structure/spider/spiderling/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(user.a_intent != INTENT_HELP)
user.changeNext_move(CLICK_CD_MELEE)
diff --git a/code/game/objects/effects/temporary_visuals/miscellaneous.dm b/code/game/objects/effects/temporary_visuals/miscellaneous.dm
index 53adc46239..396f463136 100644
--- a/code/game/objects/effects/temporary_visuals/miscellaneous.dm
+++ b/code/game/objects/effects/temporary_visuals/miscellaneous.dm
@@ -486,4 +486,15 @@
addtimer(CALLBACK(src, .proc/end), 15)
/obj/effect/constructing_effect/proc/end()
- qdel(src)
\ No newline at end of file
+ qdel(src)
+
+/obj/effect/temp_visual/dir_setting/space_wind
+ icon = 'icons/effects/atmospherics.dmi'
+ icon_state = "space_wind"
+ layer = FLY_LAYER
+ duration = 20
+ mouse_opacity = 0
+
+/obj/effect/temp_visual/dir_setting/space_wind/Initialize(mapload, set_dir, set_alpha = 255)
+ . = ..()
+ alpha = set_alpha
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 60bae4016a..a3bbf95413 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -68,6 +68,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
var/click_delay = CLICK_CD_MELEE
var/slot_flags = 0 //This is used to determine on which slots an item can fit.
+ var/current_equipped_slot
pass_flags = PASSTABLE
pressure_resistance = 4
var/obj/item/master = null
@@ -179,6 +180,11 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
if(damtype == "brute")
hitsound = "swing_hit"
+ if(used_skills)
+ for(var/path in used_skills)
+ var/datum/skill/S = GLOB.skill_datums[path]
+ LAZYADD(used_skills[path], S.skill_traits)
+
/obj/item/Destroy()
item_flags &= ~DROPDEL //prevent reqdels
if(ismob(loc))
@@ -307,7 +313,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
add_fingerprint(usr)
return ..()
-/obj/item/attack_hand(mob/user)
+/obj/item/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
@@ -315,6 +321,10 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
return
if(anchored)
return
+ if(loc == user && current_equipped_slot && current_equipped_slot != SLOT_HANDS)
+ if(current_equipped_slot in user.check_obscured_slots())
+ to_chat(src, "You are unable to unequip that while wearing other garments over it!")
+ return FALSE
if(resistance_flags & ON_FIRE)
var/mob/living/carbon/C = user
@@ -336,7 +346,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
C.update_damage_overlays()
return
- if(acid_level > 20 && !ismob(loc))// so we can still remove the clothes on us that have acid.
+ if(acid_level > 20 && ismob(loc))// so we can still remove the clothes on us that have acid.
var/mob/living/carbon/C = user
if(istype(C))
if(!C.gloves || (!(C.gloves.resistance_flags & (UNACIDABLE|ACID_PROOF))))
@@ -379,6 +389,11 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
return
if(anchored)
return
+ if(loc == user && current_equipped_slot && current_equipped_slot != SLOT_HANDS)
+ if(current_equipped_slot in user.check_obscured_slots())
+ to_chat(src, "You are unable to unequip that while wearing other garments over it!")
+ return FALSE
+
SEND_SIGNAL(loc, COMSIG_TRY_STORAGE_TAKE, src, user.loc, TRUE)
@@ -423,6 +438,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
/obj/item/proc/dropped(mob/user)
SHOULD_CALL_PARENT(TRUE)
+ current_equipped_slot = null
for(var/X in actions)
var/datum/action/A = X
A.Remove(user)
@@ -470,7 +486,9 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
// for items that can be placed in multiple slots
// note this isn't called during the initial dressing of a player
/obj/item/proc/equipped(mob/user, slot)
+ SHOULD_CALL_PARENT(TRUE)
. = SEND_SIGNAL(src, COMSIG_ITEM_EQUIPPED, user, slot)
+ current_equipped_slot = slot
if(!(. & COMPONENT_NO_GRANT_ACTIONS))
for(var/X in actions)
var/datum/action/A = X
@@ -496,11 +514,11 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
//if this is being done by a mob other than M, it will include the mob equipper, who is trying to equip the item to mob M. equipper will be null otherwise.
//If you are making custom procs but would like to retain partial or complete functionality of this one, include a 'return ..()' to where you want this to happen.
//Set disable_warning to TRUE if you wish it to not give you outputs.
-/obj/item/proc/mob_can_equip(mob/living/M, mob/living/equipper, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE)
+/obj/item/proc/mob_can_equip(mob/living/M, mob/living/equipper, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE, clothing_check = FALSE, list/return_warning)
if(!M)
return FALSE
- return M.can_equip(src, slot, disable_warning, bypass_equip_delay_self)
+ return M.can_equip(src, slot, disable_warning, bypass_equip_delay_self, clothing_check, return_warning)
/obj/item/verb/verb_pickup()
set src in oview(1)
@@ -778,6 +796,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
..()
/obj/item/proc/microwave_act(obj/machinery/microwave/M)
+ SEND_SIGNAL(src, COMSIG_ITEM_MICROWAVE_ACT, M)
if(istype(M) && M.dirty < 100)
M.dirty++
@@ -834,7 +853,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
// Called when a mob tries to use the item as a tool.
// Handles most checks.
-/obj/item/proc/use_tool(atom/target, mob/living/user, delay, amount=0, volume=0, datum/callback/extra_checks, skill_gain_mult = 1, max_level = INFINITY)
+/obj/item/proc/use_tool(atom/target, mob/living/user, delay, amount=0, volume=0, datum/callback/extra_checks, skill_gain_mult = STD_USE_TOOL_MULT)
// No delay means there is no start message, and no reason to call tool_start_check before use_tool.
// Run the start check here so we wouldn't have to call it manually.
if(!delay && !tool_start_check(user, amount))
@@ -879,7 +898,8 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
for(var/skill in used_skills)
if(!(SKILL_TRAINING_TOOL in used_skills[skill]))
continue
- user.mind.auto_gain_experience(skill, gain*skill_gain_mult, GET_STANDARD_LVL(max_level))
+ var/datum/skill/S = GLOB.skill_datums[skill]
+ user.mind.auto_gain_experience(skill, gain*skill_gain_mult*S.item_skill_gain_multi)
return TRUE
@@ -1093,4 +1113,4 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
jostle_pain_mult = (!isnull(embedding["jostle_pain_mult"]) ? embedding["jostle_pain_mult"] : EMBEDDED_JOSTLE_PAIN_MULTIPLIER),\
pain_stam_pct = (!isnull(embedding["pain_stam_pct"]) ? embedding["pain_stam_pct"] : EMBEDDED_PAIN_STAM_PCT),\
embed_chance_turf_mod = (!isnull(embedding["embed_chance_turf_mod"]) ? embedding["embed_chance_turf_mod"] : EMBED_CHANCE_TURF_MOD))
- return TRUE
\ No newline at end of file
+ return TRUE
diff --git a/code/game/objects/items/RCL.dm b/code/game/objects/items/RCL.dm
index f3ea461c74..6e305c30ee 100644
--- a/code/game/objects/items/RCL.dm
+++ b/code/game/objects/items/RCL.dm
@@ -1,4 +1,4 @@
-/obj/item/twohanded/rcl
+/obj/item/rcl
name = "rapid cable layer"
desc = "A device used to rapidly deploy cables. It has screws on the side which can be removed to slide off the cables. Do not use without insulation!"
icon = 'icons/obj/tools.dmi'
@@ -23,15 +23,26 @@
var/datum/radial_menu/persistent/wiring_gui_menu
var/mob/listeningTo
-/obj/item/twohanded/rcl/Initialize()
+/obj/item/rcl/Initialize()
. = ..()
+ RegisterSignal(src, COMSIG_TWOHANDED_WIELD, .proc/on_wield)
+ RegisterSignal(src, COMSIG_TWOHANDED_UNWIELD, .proc/on_unwield)
update_icon()
-/obj/item/twohanded/rcl/ComponentInitialize()
+/obj/item/rcl/ComponentInitialize()
. = ..()
AddElement(/datum/element/update_icon_updates_onmob)
+ AddComponent(/datum/component/two_handed)
-/obj/item/twohanded/rcl/attackby(obj/item/W, mob/user)
+/// triggered on wield of two handed item
+/obj/item/rcl/proc/on_wield(obj/item/source, mob/user)
+ active = TRUE
+
+/// triggered on unwield of two handed item
+/obj/item/rcl/proc/on_unwield(obj/item/source, mob/user)
+ active = FALSE
+
+/obj/item/rcl/attackby(obj/item/W, mob/user)
if(istype(W, /obj/item/stack/cable_coil))
var/obj/item/stack/cable_coil/C = W
@@ -86,26 +97,26 @@
else
..()
-/obj/item/twohanded/rcl/examine(mob/user)
+/obj/item/rcl/examine(mob/user)
. = ..()
if(loaded)
. += "It contains [loaded.amount]/[max_amount] cables."
-/obj/item/twohanded/rcl/Destroy()
+/obj/item/rcl/Destroy()
QDEL_NULL(loaded)
last = null
listeningTo = null
QDEL_NULL(wiring_gui_menu)
return ..()
-/obj/item/twohanded/rcl/update_icon_state()
+/obj/item/rcl/update_icon_state()
icon_state = initial(icon_state)
item_state = initial(item_state)
if(!loaded || !loaded.amount)
icon_state += "-empty"
item_state += "-0"
-/obj/item/twohanded/rcl/update_overlays()
+/obj/item/rcl/update_overlays()
. = ..()
if(!loaded || !loaded.amount)
return
@@ -113,7 +124,7 @@
cable_overlay.color = GLOB.cable_colors[colors[current_color_index]]
. += cable_overlay
-/obj/item/twohanded/rcl/worn_overlays(isinhands, icon_file, used_state, style_flags = NONE)
+/obj/item/rcl/worn_overlays(isinhands, icon_file, used_state, style_flags = NONE)
. = ..()
if(!isinhands || !(loaded?.amount))
return
@@ -121,7 +132,7 @@
cable_overlay.color = GLOB.cable_colors[colors[current_color_index]]
. += cable_overlay
-/obj/item/twohanded/rcl/proc/is_empty(mob/user, loud = 1)
+/obj/item/rcl/proc/is_empty(mob/user, loud = 1)
update_icon()
if(!loaded || !loaded.amount)
if(loud)
@@ -130,26 +141,23 @@
QDEL_NULL(loaded)
loaded = null
QDEL_NULL(wiring_gui_menu)
- unwield(user)
- active = wielded
return TRUE
return FALSE
-/obj/item/twohanded/rcl/pickup(mob/user)
+/obj/item/rcl/pickup(mob/user)
..()
getMobhook(user)
-/obj/item/twohanded/rcl/dropped(mob/wearer)
+/obj/item/rcl/dropped(mob/wearer)
..()
UnregisterSignal(wearer, COMSIG_MOVABLE_MOVED)
listeningTo = null
last = null
-/obj/item/twohanded/rcl/attack_self(mob/user)
+/obj/item/rcl/attack_self(mob/user)
..()
- active = wielded
if(!active)
last = null
else if(!last)
@@ -158,7 +166,7 @@
last = C
break
-obj/item/twohanded/rcl/proc/getMobhook(mob/to_hook)
+obj/item/rcl/proc/getMobhook(mob/to_hook)
if(listeningTo == to_hook)
return
if(listeningTo)
@@ -166,7 +174,7 @@ obj/item/twohanded/rcl/proc/getMobhook(mob/to_hook)
RegisterSignal(to_hook, COMSIG_MOVABLE_MOVED, .proc/trigger)
listeningTo = to_hook
-/obj/item/twohanded/rcl/proc/trigger(mob/user)
+/obj/item/rcl/proc/trigger(mob/user)
if(active)
layCable(user)
if(wiring_gui_menu) //update the wire options as you move
@@ -174,7 +182,7 @@ obj/item/twohanded/rcl/proc/getMobhook(mob/to_hook)
//previous contents of trigger(), lays cable each time the player moves
-/obj/item/twohanded/rcl/proc/layCable(mob/user)
+/obj/item/rcl/proc/layCable(mob/user)
if(!isturf(user.loc))
return
if(is_empty(user, 0))
@@ -207,7 +215,7 @@ obj/item/twohanded/rcl/proc/getMobhook(mob/to_hook)
update_icon()
//searches the current tile for a stub cable of the same colour
-/obj/item/twohanded/rcl/proc/findLinkingCable(mob/user)
+/obj/item/rcl/proc/findLinkingCable(mob/user)
var/turf/T
if(!isturf(user.loc))
return
@@ -223,10 +231,8 @@ obj/item/twohanded/rcl/proc/getMobhook(mob/to_hook)
continue
if(C.d1 == 0)
return C
- return
-
-/obj/item/twohanded/rcl/proc/wiringGuiGenerateChoices(mob/user)
+/obj/item/rcl/proc/wiringGuiGenerateChoices(mob/user)
var/fromdir = 0
var/obj/structure/cable/linkingCable = findLinkingCable(user)
if(linkingCable)
@@ -243,12 +249,12 @@ obj/item/twohanded/rcl/proc/getMobhook(mob/to_hook)
wiredirs[icondir] = img
return wiredirs
-/obj/item/twohanded/rcl/proc/showWiringGui(mob/user)
+/obj/item/rcl/proc/showWiringGui(mob/user)
var/list/choices = wiringGuiGenerateChoices(user)
wiring_gui_menu = show_radial_menu_persistent(user, src , choices, select_proc = CALLBACK(src, .proc/wiringGuiReact, user), radius = 42)
-/obj/item/twohanded/rcl/proc/wiringGuiUpdate(mob/user)
+/obj/item/rcl/proc/wiringGuiUpdate(mob/user)
if(!wiring_gui_menu)
return
@@ -259,7 +265,7 @@ obj/item/twohanded/rcl/proc/getMobhook(mob/to_hook)
//Callback used to respond to interactions with the wiring menu
-/obj/item/twohanded/rcl/proc/wiringGuiReact(mob/living/user,choice)
+/obj/item/rcl/proc/wiringGuiReact(mob/living/user,choice)
if(!choice) //close on a null choice (the center button)
QDEL_NULL(wiring_gui_menu)
return
@@ -290,7 +296,7 @@ obj/item/twohanded/rcl/proc/getMobhook(mob/to_hook)
wiringGuiUpdate(user)
-/obj/item/twohanded/rcl/ui_action_click(mob/user, action)
+/obj/item/rcl/ui_action_click(mob/user, action)
if(istype(action, /datum/action/item_action/rcl_col))
current_color_index++;
if (current_color_index > colors.len)
@@ -308,13 +314,13 @@ obj/item/twohanded/rcl/proc/getMobhook(mob/to_hook)
else //open the menu
showWiringGui(user)
-/obj/item/twohanded/rcl/pre_loaded/Initialize() //Comes preloaded with cable, for testing stuff
+/obj/item/rcl/pre_loaded/Initialize() //Comes preloaded with cable, for testing stuff
loaded = new()
loaded.max_amount = max_amount
loaded.amount = max_amount
return ..()
-/obj/item/twohanded/rcl/ghetto
+/obj/item/rcl/ghetto
actions_types = list()
max_amount = 30
name = "makeshift rapid cable layer"
diff --git a/code/game/objects/items/RPD.dm b/code/game/objects/items/RPD.dm
index 665082d1d2..93d73b2b5f 100644
--- a/code/game/objects/items/RPD.dm
+++ b/code/game/objects/items/RPD.dm
@@ -249,7 +249,7 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list(
var/datum/asset/assets = get_asset_datum(/datum/asset/spritesheet/pipes)
assets.send(user)
- ui = new(user, src, ui_key, "rpd", name, 425, 472, master_ui, state)
+ ui = new(user, src, ui_key, "RapidPipeDispenser", name, 425, 515, master_ui, state)
ui.open()
/obj/item/pipe_dispenser/ui_data(mob/user)
diff --git a/code/game/objects/items/RSF.dm b/code/game/objects/items/RSF.dm
index 01b351edd0..8fd2ed2377 100644
--- a/code/game/objects/items/RSF.dm
+++ b/code/game/objects/items/RSF.dm
@@ -26,6 +26,7 @@ RSF
/obj/item/rsf/cyborg
matter = 30
+
/obj/item/rsf/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/rcd_ammo))
if((matter + 10) > 30)
@@ -64,15 +65,16 @@ RSF
return
if (!(istype(A, /obj/structure/table) || isfloorturf(A)))
return
-
- if(matter < 1)
- to_chat(user, "\The [src] doesn't have enough matter left.")
- return
if(iscyborg(user))
+ matter = 30 //borgs dont actually use the matter so this is mostly just so it doesnt fail the next check incase of shennanigans
var/mob/living/silicon/robot/R = user
if(!R.cell || R.cell.charge < 200)
to_chat(user, "You do not have enough power to use [src].")
return
+ if(matter < 1)
+ to_chat(user, "\The [src] doesn't have enough matter left.")
+ return
+
var/turf/T = get_turf(A)
playsound(src.loc, 'sound/machines/click.ogg', 10, 1)
@@ -91,7 +93,7 @@ RSF
use_matter(50, user)
if(4)
to_chat(user, "Dispensing Dice Pack...")
- new /obj/item/storage/pill_bottle/dice(T)
+ new /obj/item/storage/box/dice(T)
use_matter(200, user)
if(5)
to_chat(user, "Dispensing Cigarette...")
diff --git a/code/game/objects/items/airlock_painter.dm b/code/game/objects/items/airlock_painter.dm
index c9a31a48c2..4f3761f899 100644
--- a/code/game/objects/items/airlock_painter.dm
+++ b/code/game/objects/items/airlock_painter.dm
@@ -183,7 +183,7 @@
/obj/item/airlock_painter/decal/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, "decal_painter", name, 500, 400, master_ui, state)
+ ui = new(user, src, ui_key, "DecalPainter", name, 500, 400, master_ui, state)
ui.open()
/obj/item/airlock_painter/decal/ui_data(mob/user)
@@ -233,4 +233,4 @@
/obj/item/airlock_painter/decal/debug/Initialize()
. = ..()
- ink = new /obj/item/toner/extreme(src)
\ No newline at end of file
+ ink = new /obj/item/toner/extreme(src)
diff --git a/code/game/objects/items/binoculars.dm b/code/game/objects/items/binoculars.dm
new file mode 100644
index 0000000000..347f0ad3a7
--- /dev/null
+++ b/code/game/objects/items/binoculars.dm
@@ -0,0 +1,65 @@
+/obj/item/binoculars
+ name = "binoculars"
+ desc = "Used for long-distance surveillance."
+ item_state = "binoculars"
+ icon_state = "binoculars"
+ lefthand_file = 'icons/mob/inhands/items_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/items_righthand.dmi'
+ slot_flags = ITEM_SLOT_BELT
+ w_class = WEIGHT_CLASS_SMALL
+ var/mob/listeningTo
+ var/zoom_out_amt = 6
+ var/zoom_amt = 10
+
+/obj/item/binoculars/Initialize()
+ . = ..()
+ RegisterSignal(src, COMSIG_TWOHANDED_WIELD, .proc/on_wield)
+ RegisterSignal(src, COMSIG_TWOHANDED_UNWIELD, .proc/on_unwield)
+
+/obj/item/binoculars/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/two_handed, force_unwielded=8, force_wielded=12)
+
+/obj/item/binoculars/Destroy()
+ listeningTo = null
+ return ..()
+
+/obj/item/binoculars/proc/on_wield(obj/item/source, mob/user)
+ RegisterSignal(user, COMSIG_MOVABLE_MOVED, .proc/unwield)
+ listeningTo = user
+ user.visible_message("[user] holds [src] up to [user.p_their()] eyes.", "You hold [src] up to your eyes.")
+ item_state = "binoculars_wielded"
+ user.regenerate_icons()
+ if(!user?.client)
+ return
+ var/client/C = user.client
+ var/_x = 0
+ var/_y = 0
+ switch(user.dir)
+ if(NORTH)
+ _y = zoom_amt
+ if(EAST)
+ _x = zoom_amt
+ if(SOUTH)
+ _y = -zoom_amt
+ if(WEST)
+ _x = -zoom_amt
+ C.change_view(world.view + zoom_out_amt)
+ C.pixel_x = world.icon_size*_x
+ C.pixel_y = world.icon_size*_y
+/obj/item/binoculars/proc/on_unwield(obj/item/source, mob/user)
+ unwield(user)
+
+/obj/item/binoculars/proc/unwield(mob/user)
+ if(listeningTo)
+ UnregisterSignal(listeningTo, COMSIG_MOVABLE_MOVED)
+ listeningTo = null
+ user.visible_message("[user] lowers [src].", "You lower [src].")
+ item_state = "binoculars"
+ user.regenerate_icons()
+ if(user && user.client)
+ user.regenerate_icons()
+ var/client/C = user.client
+ C.change_view(CONFIG_GET(string/default_view))
+ user.client.pixel_x = 0
+ user.client.pixel_y = 0
diff --git a/code/game/objects/items/broom.dm b/code/game/objects/items/broom.dm
new file mode 100644
index 0000000000..225644109f
--- /dev/null
+++ b/code/game/objects/items/broom.dm
@@ -0,0 +1,69 @@
+/obj/item/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."
+ 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
+ attack_verb = list("swept", "brushed off", "bludgeoned", "whacked")
+ resistance_flags = FLAMMABLE
+
+/obj/item/broom/Initialize()
+ . = ..()
+ RegisterSignal(src, COMSIG_TWOHANDED_WIELD, .proc/on_wield)
+ RegisterSignal(src, COMSIG_TWOHANDED_UNWIELD, .proc/on_unwield)
+
+/obj/item/broom/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/two_handed, force_unwielded=8, force_wielded=12, icon_wielded="broom1")
+
+/obj/item/broom/update_icon_state()
+ icon_state = "broom0"
+
+/// triggered on wield of two handed item
+/obj/item/broom/proc/on_wield(obj/item/source, mob/user)
+ to_chat(user, "You brace the [src] against the ground in a firm sweeping stance.")
+ RegisterSignal(user, COMSIG_MOVABLE_MOVED, .proc/sweep)
+
+/// triggered on unwield of two handed item
+/obj/item/broom/proc/on_unwield(obj/item/source, mob/user)
+ UnregisterSignal(user, COMSIG_MOVABLE_MOVED)
+
+/obj/item/broom/afterattack(atom/A, mob/user, proximity)
+ . = ..()
+ if(!proximity)
+ return
+ sweep(user, A, FALSE)
+
+/obj/item/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 (!isturf(target))
+ return
+ 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', 30, TRUE, -1)
+
+/obj/item/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()
diff --git a/code/game/objects/items/cardboard_cutouts.dm b/code/game/objects/items/cardboard_cutouts.dm
index 6648db46c4..3d444b7eca 100644
--- a/code/game/objects/items/cardboard_cutouts.dm
+++ b/code/game/objects/items/cardboard_cutouts.dm
@@ -44,7 +44,7 @@
))
//ATTACK HAND IGNORING PARENT RETURN VALUE
-/obj/item/cardboard_cutout/attack_hand(mob/living/user)
+/obj/item/cardboard_cutout/attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
if(user.a_intent == INTENT_HELP || pushed_over)
return ..()
user.visible_message("[user] pushes over [src]!", "You push over [src]!")
diff --git a/code/game/objects/items/cards_ids.dm b/code/game/objects/items/cards_ids.dm
index 58d907e14b..5b1b86b9e4 100644
--- a/code/game/objects/items/cards_ids.dm
+++ b/code/game/objects/items/cards_ids.dm
@@ -113,6 +113,26 @@
. = ..()
. += "It has [uses ? uses : "no"] charges left."
+/obj/item/card/id/examine_more(mob/user)
+ var/list/msg = list("You examine [src] closer, and note the following...")
+
+ if(mining_points)
+ msg += "There's [mining_points] mining equipment redemption point\s loaded onto this card."
+ if(registered_account)
+ msg += "The account linked to the ID belongs to '[registered_account.account_holder]' and reports a balance of [registered_account.account_balance] cr."
+ if(registered_account.account_job)
+ var/datum/bank_account/D = SSeconomy.get_dep_account(registered_account.account_job.paycheck_department)
+ if(D)
+ msg += "The [D.account_holder] reports a balance of [D.account_balance] cr."
+ msg += "Alt-Click the ID to pull money from the linked account in the form of holochips."
+ msg += "You can insert credits into the linked account by pressing holochips, cash, or coins against the ID."
+ if(registered_account.account_holder == user.real_name)
+ msg += "If you lose this ID card, you can reclaim your account by Alt-Clicking a blank ID card while holding it and entering your account ID number."
+ else
+ msg += "There is no registered account linked to this card. Alt-Click to add one."
+
+ return msg
+
/obj/item/card/emag/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/emagrecharge))
var/obj/item/emagrecharge/ER = W
@@ -204,7 +224,7 @@
. = ..()
if(.)
switch(var_name)
- if("assignment","registered_name")
+ if(NAMEOF(src, assignment),NAMEOF(src, registered_name)) //,NAMEOF(src, registered_age))
update_label()
/obj/item/card/id/attack_self(mob/user)
@@ -230,15 +250,13 @@
return ..()
/obj/item/card/id/proc/insert_money(obj/item/I, mob/user, physical_currency)
+ if(!registered_account)
+ to_chat(user, "[src] doesn't have a linked account to deposit [I] into!")
+ return
var/cash_money = I.get_item_credit_value()
if(!cash_money)
to_chat(user, "[I] doesn't seem to be worth anything!")
return
-
- if(!registered_account)
- to_chat(user, "[src] doesn't have a linked account to deposit [I] into!")
- return
-
registered_account.adjust_money(cash_money)
if(physical_currency)
to_chat(user, "You stuff [I] into [src]. It disappears in a small puff of bluespace smoke, adding [cash_money] credits to the linked account.")
@@ -249,17 +267,20 @@
qdel(I)
/obj/item/card/id/proc/mass_insert_money(list/money, mob/user)
+ if(!registered_account)
+ to_chat(user, "[src] doesn't have a linked account to deposit into!")
+ return FALSE
+
if (!money || !money.len)
return FALSE
var/total = 0
for (var/obj/item/physical_money in money)
- var/cash_money = physical_money.get_item_credit_value()
+ total += physical_money.get_item_credit_value()
+ CHECK_TICK
- total += cash_money
-
- registered_account.adjust_money(cash_money)
+ registered_account.adjust_money(total)
QDEL_LIST(money)
diff --git a/code/game/objects/items/chainsaw.dm b/code/game/objects/items/chainsaw.dm
new file mode 100644
index 0000000000..f382aa1ed3
--- /dev/null
+++ b/code/game/objects/items/chainsaw.dm
@@ -0,0 +1,93 @@
+
+// CHAINSAW
+/obj/item/chainsaw
+ name = "chainsaw"
+ desc = "A versatile power tool. Useful for limbing trees and delimbing humans."
+ icon_state = "chainsaw_off"
+ lefthand_file = 'icons/mob/inhands/weapons/chainsaw_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/weapons/chainsaw_righthand.dmi'
+ flags_1 = CONDUCT_1
+ force = 13
+ var/force_on = 24
+ w_class = WEIGHT_CLASS_HUGE
+ throwforce = 13
+ throw_speed = 2
+ throw_range = 4
+ custom_materials = list(/datum/material/iron=13000)
+ attack_verb = list("sawed", "torn", "cut", "chopped", "diced")
+ hitsound = "swing_hit"
+ sharpness = IS_SHARP
+ actions_types = list(/datum/action/item_action/startchainsaw)
+ tool_behaviour = TOOL_SAW
+ toolspeed = 0.5
+ var/on = FALSE
+ var/wielded = FALSE // track wielded status on item
+
+/obj/item/chainsaw/Initialize()
+ . = ..()
+ RegisterSignal(src, COMSIG_TWOHANDED_WIELD, .proc/on_wield)
+ RegisterSignal(src, COMSIG_TWOHANDED_UNWIELD, .proc/on_unwield)
+
+/obj/item/chainsaw/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/butchering, 30, 100, 0, 'sound/weapons/chainsawhit.ogg', TRUE)
+ AddComponent(/datum/component/two_handed, require_twohands=TRUE)
+ AddElement(/datum/element/update_icon_updates_onmob)
+
+/// triggered on wield of two handed item
+/obj/item/chainsaw/proc/on_wield(obj/item/source, mob/user)
+ wielded = TRUE
+
+/// triggered on unwield of two handed item
+/obj/item/chainsaw/proc/on_unwield(obj/item/source, mob/user)
+ wielded = FALSE
+
+/obj/item/chainsaw/suicide_act(mob/living/carbon/user)
+ if(on)
+ user.visible_message("[user] begins to tear [user.p_their()] head off with [src]! It looks like [user.p_theyre()] trying to commit suicide!")
+ playsound(src, 'sound/weapons/chainsawhit.ogg', 100, 1)
+ var/obj/item/bodypart/head/myhead = user.get_bodypart(BODY_ZONE_HEAD)
+ if(myhead)
+ myhead.dismember()
+ else
+ user.visible_message("[user] smashes [src] into [user.p_their()] neck, destroying [user.p_their()] esophagus! It looks like [user.p_theyre()] trying to commit suicide!")
+ playsound(src, 'sound/weapons/genhit1.ogg', 100, 1)
+ return(BRUTELOSS)
+
+/obj/item/chainsaw/attack_self(mob/user)
+ on = !on
+ to_chat(user, "As you pull the starting cord dangling from [src], [on ? "it begins to whirr." : "the chain stops moving."]")
+ force = on ? force_on : initial(force)
+ throwforce = on ? force_on : force
+ update_icon()
+ var/datum/component/butchering/butchering = src.GetComponent(/datum/component/butchering)
+ butchering.butchering_enabled = on
+
+ if(on)
+ hitsound = 'sound/weapons/chainsawhit.ogg'
+ else
+ hitsound = "swing_hit"
+
+/obj/item/chainsaw/update_icon_state()
+ icon_state = "chainsaw_[on ? "on" : "off"]"
+
+/obj/item/chainsaw/get_dismemberment_chance()
+ if(wielded)
+ . = ..()
+
+/obj/item/chainsaw/doomslayer
+ name = "THE GREAT COMMUNICATOR"
+ desc = "VRRRRRRR!!!"
+ armour_penetration = 100
+ force_on = 30
+
+/obj/item/chainsaw/doomslayer/check_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
+ block_return[BLOCK_RETURN_REFLECT_PROJECTILE_CHANCE] = 100
+ return ..()
+
+/obj/item/chainsaw/doomslayer/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
+ if(attack_type & ATTACK_TYPE_PROJECTILE)
+ owner.visible_message("Ranged attacks just make [owner] angrier!")
+ playsound(src, pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 75, 1)
+ return BLOCK_SUCCESS | BLOCK_PHYSICAL_EXTERNAL
+ return ..()
diff --git a/code/game/objects/items/chromosome.dm b/code/game/objects/items/chromosome.dm
index 1340e7f35b..8330a8e202 100644
--- a/code/game/objects/items/chromosome.dm
+++ b/code/game/objects/items/chromosome.dm
@@ -53,32 +53,32 @@
/obj/item/chromosome/stabilizer
name = "stabilizer chromosome"
- desc = "A chromosome that adjusts to the body to reduce genetic damage by 20%."
+ desc = "A chromosome that reduces mutation instability by 20%."
icon_state = "stabilizer"
stabilizer_coeff = 0.8
weight = 1
/obj/item/chromosome/synchronizer
name = "synchronizer chromosome"
- desc = "A chromosome that gives the mind more controle over the mutation, reducing knockback and downsides by 50%."
+ desc = "A chromosome that reduces mutation knockback and downsides by 50%."
icon_state = "synchronizer"
synchronizer_coeff = 0.5
/obj/item/chromosome/power
name = "power chromosome"
- desc = "A power chromosome for boosting certain mutation's power by 50%."
+ desc = "A chromosome that increases mutation power by 50%."
icon_state = "power"
power_coeff = 1.5
/obj/item/chromosome/energy
name = "energetic chromosome"
- desc = "A chromosome that reduces cooldown on action based mutations by 50%."
+ desc = "A chromosome that reduces action based mutation cooldowns by by 50%."
icon_state = "energy"
energy_coeff = 0.5
/obj/item/chromosome/reinforcer
name = "reinforcement chromosome"
- desc = "Renders the mutation immune to mutadone."
+ desc = "A chromosome that renders mutations immune to mutadone."
icon_state = "reinforcer"
weight = 3
diff --git a/code/game/objects/items/chrono_eraser.dm b/code/game/objects/items/chrono_eraser.dm
index 766c3d8636..16bc6897c8 100644
--- a/code/game/objects/items/chrono_eraser.dm
+++ b/code/game/objects/items/chrono_eraser.dm
@@ -246,9 +246,9 @@
/obj/effect/chrono_field/return_air() //we always have nominal air and temperature
var/datum/gas_mixture/GM = new
- GM.gases[/datum/gas/oxygen] = MOLES_O2STANDARD
- GM.gases[/datum/gas/nitrogen] = MOLES_N2STANDARD
- GM.temperature = T20C
+ GM.set_moles(/datum/gas/oxygen, MOLES_O2STANDARD)
+ GM.set_moles(/datum/gas/nitrogen, MOLES_N2STANDARD)
+ GM.set_temperature(T20C)
return GM
/obj/effect/chrono_field/Move()
diff --git a/code/game/objects/items/cigs_lighters.dm b/code/game/objects/items/cigs_lighters.dm
index bfbacb1a34..de32375642 100644
--- a/code/game/objects/items/cigs_lighters.dm
+++ b/code/game/objects/items/cigs_lighters.dm
@@ -794,6 +794,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM
reagents.clear_reagents()
/obj/item/clothing/mask/vape/equipped(mob/user, slot)
+ . = ..()
if(slot == SLOT_WEAR_MASK)
if(!screw)
to_chat(user, "You start puffing on the vape.")
diff --git a/code/game/objects/items/circuitboards/computer_circuitboards.dm b/code/game/objects/items/circuitboards/computer_circuitboards.dm
index f94c8f3513..2fa48582f7 100644
--- a/code/game/objects/items/circuitboards/computer_circuitboards.dm
+++ b/code/game/objects/items/circuitboards/computer_circuitboards.dm
@@ -116,9 +116,9 @@
build_path = /obj/machinery/computer/cloning
var/list/records = list()
-/obj/item/circuitboard/computer/prototype_cloning
+/obj/item/circuitboard/computer/cloning/prototype
name = "Prototype Cloning (Computer Board)"
- build_path = /obj/machinery/computer/prototype_cloning
+ build_path = /obj/machinery/computer/cloning/prototype
/obj/item/circuitboard/computer/arcade/battle
name = "Arcade Battle (Computer Board)"
diff --git a/code/game/objects/items/circuitboards/machine_circuitboards.dm b/code/game/objects/items/circuitboards/machine_circuitboards.dm
index ea839ae12d..56eb25f953 100644
--- a/code/game/objects/items/circuitboards/machine_circuitboards.dm
+++ b/code/game/objects/items/circuitboards/machine_circuitboards.dm
@@ -61,6 +61,14 @@
name = "Experimental Clone Pod (Machine Board)"
build_path = /obj/machinery/clonepod/experimental
+/obj/item/circuitboard/machine/sheetifier
+ name = "Sheet-meister 2000 (Machine Board)"
+ icon_state = "supply"
+ build_path = /obj/machinery/sheetifier
+ req_components = list(
+ /obj/item/stock_parts/manipulator = 2,
+ /obj/item/stock_parts/matter_bin = 2)
+
/obj/item/circuitboard/machine/abductor
name = "alien board (Report This)"
icon_state = "abductor_mod"
diff --git a/code/game/objects/items/clown_items.dm b/code/game/objects/items/clown_items.dm
index 050427702d..f5477ae5f6 100644
--- a/code/game/objects/items/clown_items.dm
+++ b/code/game/objects/items/clown_items.dm
@@ -88,6 +88,7 @@
var/obj/effect/decal/cleanable/C = locate() in target
qdel(C)
target.remove_atom_colour(WASHABLE_COLOUR_PRIORITY)
+ target.clean_blood()
SEND_SIGNAL(target, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_MEDIUM)
target.wash_cream()
return
@@ -180,4 +181,4 @@
name = "Canned Laughter"
desc = "Just looking at this makes you want to giggle."
icon_state = "laughter"
- list_reagents = list(/datum/reagent/consumable/laughter = 50)
\ No newline at end of file
+ list_reagents = list(/datum/reagent/consumable/laughter = 50)
diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm
index a4ed8dedd1..620fbbf100 100644
--- a/code/game/objects/items/crayons.dm
+++ b/code/game/objects/items/crayons.dm
@@ -152,7 +152,7 @@
SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "crayon", name, 600, 600,
+ ui = new(user, src, ui_key, "Crayon", name, 600, 600,
master_ui, state)
ui.open()
@@ -737,12 +737,7 @@
to_chat(usr, "A color that dark on an object like this? Surely not...")
return FALSE
-
- if(istype(target, /obj/structure/window))
- var/obj/structure/window/W = target
- W.spraycan_paint(paint_color)
- else
- target.add_atom_colour(paint_color, WASHABLE_COLOUR_PRIORITY)
+ target.add_atom_colour(paint_color, WASHABLE_COLOUR_PRIORITY)
. = use_charges(user, 2)
var/fraction = min(1, . / reagents.maximum_volume)
diff --git a/code/game/objects/items/defib.dm b/code/game/objects/items/defib.dm
index 7cc76da312..e15dc72838 100644
--- a/code/game/objects/items/defib.dm
+++ b/code/game/objects/items/defib.dm
@@ -19,7 +19,7 @@
var/on = FALSE //if the paddles are equipped (1) or on the defib (0)
var/safety = TRUE //if you can zap people with the defibs on harm mode
var/powered = FALSE //if there's a cell in the defib with enough power for a revive, blocks paddles from reviving otherwise
- var/obj/item/twohanded/shockpaddles/paddles
+ var/obj/item/shockpaddles/paddles
var/obj/item/stock_parts/cell/cell
var/combat = FALSE //can we revive through space suits?
var/grab_ghost = FALSE // Do we pull the ghost back into their body?
@@ -78,7 +78,7 @@
toggle_paddles()
//ATTACK HAND IGNORING PARENT RETURN VALUE
-/obj/item/defibrillator/attack_hand(mob/user)
+/obj/item/defibrillator/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(loc == user)
if(slot_flags == ITEM_SLOT_BACK)
if(user.get_item_by_slot(SLOT_BACK) == src)
@@ -106,7 +106,6 @@
/obj/item/defibrillator/attackby(obj/item/W, mob/user, params)
if(W == paddles)
- paddles.unwield()
toggle_paddles()
else if(istype(W, /obj/item/stock_parts/cell))
var/obj/item/stock_parts/cell/C = W
@@ -170,7 +169,6 @@
return
else
//Remove from their hands and back onto the defib unit
- paddles.unwield()
remove_paddles(user)
update_power()
@@ -179,7 +177,7 @@
A.UpdateButtonIcon()
/obj/item/defibrillator/proc/make_paddles()
- return new /obj/item/twohanded/shockpaddles(src)
+ return new /obj/item/shockpaddles(src)
/obj/item/defibrillator/equipped(mob/user, slot)
..()
@@ -256,13 +254,12 @@
/obj/item/defibrillator/compact/combat/loaded/attackby(obj/item/W, mob/user, params)
if(W == paddles)
- paddles.unwield()
toggle_paddles()
return
//paddles
-/obj/item/twohanded/shockpaddles
+/obj/item/shockpaddles
name = "defibrillator paddles"
desc = "A pair of plastic-gripped paddles with flat metal surfaces that are used to deliver powerful electric shocks."
icon = 'icons/obj/items_and_weapons.dmi'
@@ -284,24 +281,48 @@
var/grab_ghost = FALSE
var/tlimit = DEFIB_TIME_LIMIT * 10
var/disarm_shock_time = 10
+ var/wielded = FALSE // track wielded status on item
- var/mob/listeningTo
+/obj/item/shockpaddles/Initialize()
+ . = ..()
+ RegisterSignal(src, COMSIG_TWOHANDED_WIELD, .proc/on_wield)
+ RegisterSignal(src, COMSIG_TWOHANDED_UNWIELD, .proc/on_unwield)
+ if(!req_defib)
+ return //If it doesn't need a defib, just say it exists
+ if (!loc || !istype(loc, /obj/item/defibrillator)) //To avoid weird issues from admin spawns
+ return INITIALIZE_HINT_QDEL
+ defib = loc
+ busy = FALSE
+ update_icon()
-/obj/item/twohanded/shockpaddles/ComponentInitialize()
+/obj/item/shockpaddles/ComponentInitialize()
. = ..()
AddElement(/datum/element/update_icon_updates_onmob)
+ AddComponent(/datum/component/two_handed, force_unwielded=8, force_wielded=12)
-/obj/item/twohanded/shockpaddles/equipped(mob/user, slot)
+/// triggered on wield of two handed item
+/obj/item/shockpaddles/proc/on_wield(obj/item/source, mob/user)
+ wielded = TRUE
+
+/// triggered on unwield of two handed item
+/obj/item/shockpaddles/proc/on_unwield(obj/item/source, mob/user)
+ wielded = FALSE
+
+/obj/item/shockpaddles/Destroy()
+ defib = null
+ return ..()
+
+/obj/item/shockpaddles/equipped(mob/user, slot)
. = ..()
if(!req_defib)
return
RegisterSignal(user, COMSIG_MOVABLE_MOVED, .proc/check_range)
-/obj/item/twohanded/shockpaddles/Moved()
+/obj/item/shockpaddles/Moved()
. = ..()
check_range()
-/obj/item/twohanded/shockpaddles/proc/check_range()
+/obj/item/shockpaddles/proc/check_range()
if(!req_defib || !defib)
return
if(!in_range(src,defib))
@@ -312,7 +333,7 @@
visible_message("[src] snap back into [defib].")
snap_back()
-/obj/item/twohanded/shockpaddles/proc/recharge(var/time)
+/obj/item/shockpaddles/proc/recharge(var/time)
if(req_defib || !time)
return
cooldown = TRUE
@@ -324,57 +345,36 @@
cooldown = FALSE
update_icon()
-/obj/item/twohanded/shockpaddles/New(mainunit)
- ..()
- if(check_defib_exists(mainunit, src) && req_defib)
- defib = mainunit
- forceMove(defib)
- busy = FALSE
- update_icon()
-
-/obj/item/twohanded/shockpaddles/update_icon_state()
- icon_state = "defibpaddles[wielded]"
- item_state = "defibpaddles[wielded]"
- if(cooldown)
- icon_state = "defibpaddles[wielded]_cooldown"
-
-/obj/item/twohanded/shockpaddles/suicide_act(mob/user)
+/obj/item/shockpaddles/suicide_act(mob/user)
user.visible_message("[user] is putting the live paddles on [user.p_their()] chest! It looks like [user.p_theyre()] trying to commit suicide!")
if(req_defib)
defib.deductcharge(revivecost)
playsound(src, 'sound/machines/defib_zap.ogg', 50, 1, -1)
return (OXYLOSS)
-/obj/item/twohanded/shockpaddles/dropped(mob/user)
+/obj/item/shockpaddles/update_icon_state()
+ icon_state = "defibpaddles[wielded]"
+ item_state = "defibpaddles[wielded]"
+ if(cooldown)
+ icon_state = "defibpaddles[wielded]_cooldown"
+
+/obj/item/shockpaddles/dropped(mob/user)
if(!req_defib)
return ..()
if(user)
UnregisterSignal(user, COMSIG_MOVABLE_MOVED)
- var/obj/item/twohanded/offhand/O = user.get_inactive_held_item()
- if(istype(O))
- O.unwield()
if(user != loc)
to_chat(user, "The paddles snap back into the main unit.")
snap_back()
- return unwield(user)
-/obj/item/twohanded/shockpaddles/proc/snap_back()
+/obj/item/shockpaddles/proc/snap_back()
if(!defib)
return
defib.on = FALSE
forceMove(defib)
defib.update_power()
-/obj/item/twohanded/shockpaddles/proc/check_defib_exists(mainunit, mob/living/carbon/M, obj/O)
- if(!req_defib)
- return TRUE //If it doesn't need a defib, just say it exists
- if (!mainunit || !istype(mainunit, /obj/item/defibrillator)) //To avoid weird issues from admin spawns
- qdel(O)
- return FALSE
- else
- return TRUE
-
-/obj/item/twohanded/shockpaddles/attack(mob/M, mob/user)
+/obj/item/shockpaddles/attack(mob/M, mob/user)
if(busy)
return
@@ -426,7 +426,7 @@
do_help(H, user)
-/obj/item/twohanded/shockpaddles/proc/shock_touching(dmg, mob/H)
+/obj/item/shockpaddles/proc/shock_touching(dmg, mob/H)
if(!H.pulledby || !isliving(H.pulledby))
return
if(req_defib && defib.pullshocksafely)
@@ -437,7 +437,7 @@
M.visible_message("[M] is electrocuted by [M.p_their()] contact with [H]!")
M.emote("scream")
-/obj/item/twohanded/shockpaddles/proc/do_disarm(mob/living/M, mob/living/user)
+/obj/item/shockpaddles/proc/do_disarm(mob/living/M, mob/living/user)
if(req_defib && defib.safety)
return
if(!req_defib && !combat)
@@ -465,7 +465,7 @@
else
recharge(60)
-/obj/item/twohanded/shockpaddles/proc/do_harm(mob/living/carbon/H, mob/living/user)
+/obj/item/shockpaddles/proc/do_harm(mob/living/carbon/H, mob/living/user)
if(req_defib && defib.safety)
return
if(!req_defib && !combat)
@@ -520,7 +520,7 @@
busy = FALSE
update_icon()
-/obj/item/twohanded/shockpaddles/proc/do_help(mob/living/carbon/H, mob/living/user)
+/obj/item/shockpaddles/proc/do_help(mob/living/carbon/H, mob/living/user)
user.visible_message("[user] begins to place [src] on [H]'s chest.", "You begin to place [src] on [H]'s chest...")
busy = TRUE
update_icon()
@@ -619,6 +619,13 @@
if(req_defib)
if(defib.healdisk)
H.heal_overall_damage(25, 25)
+ var/list/policies = CONFIG_GET(keyed_list/policyconfig)
+ var/timelimit = CONFIG_GET(number/defib_cmd_time_limit)
+ var/late = timelimit && (tplus > timelimit)
+ var/policy = late? policies[POLICYCONFIG_ON_DEFIB_LATE] : policies[POLICYCONFIG_ON_DEFIB_INTACT]
+ if(policy)
+ to_chat(H, policy)
+ H.log_message("revived using a defibrillator, [tplus] deciseconds from time of death, considered [late? "late" : "memory-intact"] revival under configured policy limits.", LOG_GAME)
if(req_defib)
defib.deductcharge(revivecost)
cooldown = 1
@@ -677,14 +684,14 @@
return TRUE
return ..()
-/obj/item/twohanded/shockpaddles/cyborg
+/obj/item/shockpaddles/cyborg
name = "cyborg defibrillator paddles"
icon = 'icons/obj/items_and_weapons.dmi'
icon_state = "defibpaddles0"
item_state = "defibpaddles0"
req_defib = FALSE
-/obj/item/twohanded/shockpaddles/cyborg/attack(mob/M, mob/user)
+/obj/item/shockpaddles/cyborg/attack(mob/M, mob/user)
if(iscyborg(user))
var/mob/living/silicon/robot/R = user
if(R.emagged)
@@ -696,7 +703,7 @@
. = ..()
-/obj/item/twohanded/shockpaddles/syndicate
+/obj/item/shockpaddles/syndicate
name = "syndicate defibrillator paddles"
desc = "A pair of paddles used to revive deceased operatives. It possesses both the ability to penetrate armor and to deliver powerful shocks offensively."
combat = TRUE
diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm
index 2dabcca29f..d706f5468a 100644
--- a/code/game/objects/items/devices/PDA/PDA.dm
+++ b/code/game/objects/items/devices/PDA/PDA.dm
@@ -437,7 +437,6 @@ GLOBAL_LIST_EMPTY(PDAs)
dat += "Unable to obtain a reading.
"
else
var/datum/gas_mixture/environment = T.return_air()
- var/list/env_gases = environment.gases
var/pressure = environment.return_pressure()
var/total_moles = environment.total_moles()
@@ -445,12 +444,12 @@ GLOBAL_LIST_EMPTY(PDAs)
dat += "Air Pressure: [round(pressure,0.1)] kPa
"
if (total_moles)
- for(var/id in env_gases)
- var/gas_level = env_gases[id]/total_moles
+ for(var/id in environment.get_gases())
+ var/gas_level = environment.get_moles(id)/total_moles
if(gas_level > 0)
dat += "[GLOB.meta_gas_names[id]]: [round(gas_level*100, 0.01)]%
"
- dat += "Temperature: [round(environment.temperature-T0C)]°C
"
+ dat += "Temperature: [round(environment.return_temperature()-T0C)]°C
"
dat += "
"
else//Else it links to the cart menu proc. Although, it really uses menu hub 4--menu 4 doesn't really exist as it simply redirects to hub.
dat += cartridge.generate_menu()
diff --git a/code/game/objects/items/devices/aicard.dm b/code/game/objects/items/devices/aicard.dm
index 38b117b132..bfb9348d97 100644
--- a/code/game/objects/items/devices/aicard.dm
+++ b/code/game/objects/items/devices/aicard.dm
@@ -58,7 +58,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, "intellicard", name, 500, 500, master_ui, state)
+ ui = new(user, src, ui_key, "Intellicard", name, 500, 500, master_ui, state)
ui.open()
/obj/item/aicard/ui_data()
diff --git a/code/game/objects/items/devices/geiger_counter.dm b/code/game/objects/items/devices/geiger_counter.dm
index 5cab9e33de..f5d3014d35 100644
--- a/code/game/objects/items/devices/geiger_counter.dm
+++ b/code/game/objects/items/devices/geiger_counter.dm
@@ -18,6 +18,7 @@
righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
w_class = WEIGHT_CLASS_SMALL
slot_flags = ITEM_SLOT_BELT
+ rad_flags = RAD_NO_CONTAMINATE
custom_materials = list(/datum/material/iron = 150, /datum/material/glass = 150)
var/grace = RAD_GRACE_PERIOD
diff --git a/code/game/objects/items/devices/gps.dm b/code/game/objects/items/devices/gps.dm
index 981a811bc3..ee26d81a0d 100644
--- a/code/game/objects/items/devices/gps.dm
+++ b/code/game/objects/items/devices/gps.dm
@@ -76,7 +76,7 @@ GLOBAL_LIST_EMPTY(GPS_list)
// 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 = 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)
diff --git a/code/game/objects/items/devices/polycircuit.dm b/code/game/objects/items/devices/polycircuit.dm
index fb15ce45dc..a9f7cd46c8 100644
--- a/code/game/objects/items/devices/polycircuit.dm
+++ b/code/game/objects/items/devices/polycircuit.dm
@@ -11,7 +11,7 @@
/obj/item/stack/circuit_stack/attack_self(mob/user)// Prevents the crafting menu, and tells you how to use it.
to_chat(user, "You can't use [src] by itself, you'll have to try and remove one of these circuits by hand... carefully.")
-/obj/item/stack/circuit_stack/attack_hand(mob/user)
+/obj/item/stack/circuit_stack/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
var/mob/living/carbon/human/H = user
if(!user.get_inactive_held_item() == src)
return ..()
diff --git a/code/game/objects/items/devices/powersink.dm b/code/game/objects/items/devices/powersink.dm
index 5802909201..ec59009794 100644
--- a/code/game/objects/items/devices/powersink.dm
+++ b/code/game/objects/items/devices/powersink.dm
@@ -97,7 +97,7 @@ GLOBAL_LIST_EMPTY(power_sinks)
/obj/item/powersink/attack_ai()
return
-/obj/item/powersink/attack_hand(mob/user)
+/obj/item/powersink/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/objects/items/devices/radio/electropack.dm b/code/game/objects/items/devices/radio/electropack.dm
index e94d14a57d..cdb8c09527 100644
--- a/code/game/objects/items/devices/radio/electropack.dm
+++ b/code/game/objects/items/devices/radio/electropack.dm
@@ -16,6 +16,9 @@
var/on = TRUE
var/shock_cooldown = FALSE
+ var/ui_x = 260
+ var/ui_y = 137
+
/obj/item/electropack/suicide_act(mob/living/carbon/user)
user.visible_message("[user] hooks [user.p_them()]self to the electropack and spams the trigger! It looks like [user.p_theyre()] trying to commit suicide!")
return (FIRELOSS)
@@ -29,7 +32,7 @@
. = ..()
//ATTACK HAND IGNORING PARENT RETURN VALUE
-/obj/item/electropack/attack_hand(mob/user)
+/obj/item/electropack/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(iscarbon(user))
var/mob/living/carbon/C = user
if(src == C.back)
@@ -128,22 +131,12 @@
if(!ishuman(user))
return
- user.set_machine(src)
- var/dat = {"
-
-Turned [on ? "On" : "Off"] - Toggle
-Frequency/Code for electropack:
-Frequency:
-[format_frequency(src.frequency)]
-Set
-
-Code:
-[src.code]
-Set
-"}
- user << browse(dat, "window=radio")
- onclose(user, "radio")
- return
+/obj/item/electropack/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
+ 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, "Electropack", name, ui_x, ui_y, master_ui, state)
+ ui.open()
/obj/item/electropack/shockcollar
name = "shock collar"
@@ -169,7 +162,7 @@ Code:
materials = list(/datum/material/iron = 5000, /datum/material/glass =2000)
category = list("hacked", "Misc")
-/obj/item/electropack/shockcollar/attack_hand(mob/user)
+/obj/item/electropack/shockcollar/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(loc == user && user.get_item_by_slot(SLOT_NECK))
to_chat(user, "The collar is fastened tight! You'll need help taking this off!")
return
diff --git a/code/game/objects/items/devices/radio/headset.dm b/code/game/objects/items/devices/radio/headset.dm
index 5728a97dda..6ac2d310a1 100644
--- a/code/game/objects/items/devices/radio/headset.dm
+++ b/code/game/objects/items/devices/radio/headset.dm
@@ -258,6 +258,9 @@ GLOBAL_LIST_INIT(channel_tokens, list(
name = "\proper mini Integrated Subspace Transceiver "
subspace_transmission = FALSE
+/obj/item/radio/headset/silicon/pai/ComponentInitialize()
+ . = ..()
+ AddElement(/datum/element/empprotection, EMP_PROTECT_WIRES)
/obj/item/radio/headset/silicon/ai
name = "\proper Integrated Subspace Transceiver "
diff --git a/code/game/objects/items/devices/radio/intercom.dm b/code/game/objects/items/devices/radio/intercom.dm
index f4c317c8ba..ec19c0c05e 100644
--- a/code/game/objects/items/devices/radio/intercom.dm
+++ b/code/game/objects/items/devices/radio/intercom.dm
@@ -86,7 +86,7 @@
/obj/item/radio/intercom/attack_ai(mob/user)
interact(user)
-/obj/item/radio/intercom/attack_hand(mob/user)
+/obj/item/radio/intercom/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm
index 5fe9de75aa..e26b29289a 100644
--- a/code/game/objects/items/devices/radio/radio.dm
+++ b/code/game/objects/items/devices/radio/radio.dm
@@ -119,7 +119,7 @@
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 = 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/reverse_bear_trap.dm b/code/game/objects/items/devices/reverse_bear_trap.dm
index 20107373d3..46b9547e9d 100644
--- a/code/game/objects/items/devices/reverse_bear_trap.dm
+++ b/code/game/objects/items/devices/reverse_bear_trap.dm
@@ -45,7 +45,7 @@
to_chat(loc, "*ding*")
addtimer(CALLBACK(src, .proc/snap), 2)
-/obj/item/reverse_bear_trap/attack_hand(mob/user)
+/obj/item/reverse_bear_trap/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(iscarbon(user))
var/mob/living/carbon/C = user
if(C.get_item_by_slot(SLOT_HEAD) == src)
diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm
index 28e1e1294f..bc6a721d13 100644
--- a/code/game/objects/items/devices/scanners.dm
+++ b/code/game/objects/items/devices/scanners.dm
@@ -556,41 +556,38 @@ GENETICS SCANNER
else
to_chat(user, "Pressure: [round(pressure, 0.01)] kPa")
if(total_moles)
- var/list/env_gases = environment.gases
- var/o2_concentration = env_gases[/datum/gas/oxygen]/total_moles
- var/n2_concentration = env_gases[/datum/gas/nitrogen]/total_moles
- var/co2_concentration = env_gases[/datum/gas/carbon_dioxide]/total_moles
- var/plasma_concentration = env_gases[/datum/gas/plasma]/total_moles
+ var/o2_concentration = environment.get_moles(/datum/gas/oxygen)/total_moles
+ var/n2_concentration = environment.get_moles(/datum/gas/nitrogen)/total_moles
+ var/co2_concentration = environment.get_moles(/datum/gas/carbon_dioxide)/total_moles
+ var/plasma_concentration = environment.get_moles(/datum/gas/plasma)/total_moles
if(abs(n2_concentration - N2STANDARD) < 20)
- to_chat(user, "Nitrogen: [round(n2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/nitrogen], 0.01)] mol)")
+ to_chat(user, "Nitrogen: [round(n2_concentration*100, 0.01)] % ([round(environment.get_moles(/datum/gas/nitrogen), 0.01)] mol)")
else
- to_chat(user, "Nitrogen: [round(n2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/nitrogen], 0.01)] mol)")
+ to_chat(user, "Nitrogen: [round(n2_concentration*100, 0.01)] % ([round(environment.get_moles(/datum/gas/nitrogen), 0.01)] mol)")
if(abs(o2_concentration - O2STANDARD) < 2)
- to_chat(user, "Oxygen: [round(o2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/oxygen], 0.01)] mol)")
+ to_chat(user, "Oxygen: [round(o2_concentration*100, 0.01)] % ([round(environment.get_moles(/datum/gas/oxygen), 0.01)] mol)")
else
- to_chat(user, "Oxygen: [round(o2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/oxygen], 0.01)] mol)")
+ to_chat(user, "Oxygen: [round(o2_concentration*100, 0.01)] % ([round(environment.get_moles(/datum/gas/oxygen), 0.01)] mol)")
if(co2_concentration > 0.01)
- to_chat(user, "CO2: [round(co2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/carbon_dioxide], 0.01)] mol)")
+ to_chat(user, "CO2: [round(co2_concentration*100, 0.01)] % ([round(environment.get_moles(/datum/gas/carbon_dioxide), 0.01)] mol)")
else
- to_chat(user, "CO2: [round(co2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/carbon_dioxide], 0.01)] mol)")
+ to_chat(user, "CO2: [round(co2_concentration*100, 0.01)] % ([round(environment.get_moles(/datum/gas/carbon_dioxide), 0.01)] mol)")
if(plasma_concentration > 0.005)
- to_chat(user, "Plasma: [round(plasma_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/plasma], 0.01)] mol)")
+ to_chat(user, "Plasma: [round(plasma_concentration*100, 0.01)] % ([round(environment.get_moles(/datum/gas/plasma), 0.01)] mol)")
else
- to_chat(user, "Plasma: [round(plasma_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/plasma], 0.01)] mol)")
+ to_chat(user, "Plasma: [round(plasma_concentration*100, 0.01)] % ([round(environment.get_moles(/datum/gas/plasma), 0.01)] mol)")
- GAS_GARBAGE_COLLECT(environment.gases)
-
- for(var/id in env_gases)
+ for(var/id in environment.get_gases())
if(id in GLOB.hardcoded_gases)
continue
- var/gas_concentration = env_gases[id]/total_moles
- to_chat(user, "[GLOB.meta_gas_names[id]]: [round(gas_concentration*100, 0.01)] % ([round(env_gases[id], 0.01)] mol)")
- to_chat(user, "Temperature: [round(environment.temperature-T0C, 0.01)] °C ([round(environment.temperature, 0.01)] K)")
+ var/gas_concentration = environment.get_moles(id)/total_moles
+ to_chat(user, "[GLOB.meta_gas_names[id]]: [round(gas_concentration*100, 0.01)] % ([round(environment.get_moles(id), 0.01)] mol)")
+ to_chat(user, "Temperature: [round(environment.return_temperature()-T0C, 0.01)] °C ([round(environment.return_temperature(), 0.01)] K)")
/obj/item/analyzer/AltClick(mob/user) //Barometer output for measuring when the next storm happens
. = ..()
@@ -669,8 +666,8 @@ GENETICS SCANNER
var/total_moles = air_contents.total_moles()
var/pressure = air_contents.return_pressure()
- var/volume = air_contents.return_volume() //could just do mixture.volume... but safety, I guess?
- var/temperature = air_contents.temperature
+ var/volume = air_contents.return_volume()
+ var/temperature = air_contents.return_temperature()
var/cached_scan_results = air_contents.analyzer_results
if(total_moles > 0)
@@ -678,10 +675,9 @@ GENETICS SCANNER
to_chat(user, "Volume: [volume] L")
to_chat(user, "Pressure: [round(pressure,0.01)] kPa")
- var/list/cached_gases = air_contents.gases
- for(var/id in cached_gases)
- var/gas_concentration = cached_gases[id]/total_moles
- to_chat(user, "[GLOB.meta_gas_names[id]]: [round(gas_concentration*100, 0.01)] % ([round(cached_gases[id], 0.01)] mol)")
+ for(var/id in air_contents.get_gases())
+ var/gas_concentration = air_contents.get_moles(id)/total_moles
+ to_chat(user, "[GLOB.meta_gas_names[id]]: [round(gas_concentration*100, 0.01)] % ([round(air_contents.get_moles(id), 0.01)] mol)")
to_chat(user, "Temperature: [round(temperature - T0C,0.01)] °C ([round(temperature, 0.01)] K)")
else
diff --git a/code/game/objects/items/devices/taperecorder.dm b/code/game/objects/items/devices/taperecorder.dm
index 78a1a3bfda..5765ac71f1 100644
--- a/code/game/objects/items/devices/taperecorder.dm
+++ b/code/game/objects/items/devices/taperecorder.dm
@@ -55,7 +55,7 @@
..()
//ATTACK HAND IGNORING PARENT RETURN VALUE
-/obj/item/taperecorder/attack_hand(mob/user)
+/obj/item/taperecorder/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(loc == user)
if(mytape)
if(!user.is_holding(src))
diff --git a/code/game/objects/items/devices/traitordevices.dm b/code/game/objects/items/devices/traitordevices.dm
index b6261b9060..4d255fd339 100644
--- a/code/game/objects/items/devices/traitordevices.dm
+++ b/code/game/objects/items/devices/traitordevices.dm
@@ -76,6 +76,9 @@ effective or pretty fucking useless.
var/used = 0 // is it cooling down?
var/stealth = FALSE
+ var/ui_x = 320
+ var/ui_y = 335
+
/obj/item/healthanalyzer/rad_laser/attack(mob/living/M, mob/living/user)
if(!stealth || !irradiate)
..()
@@ -111,7 +114,12 @@ effective or pretty fucking useless.
ui_interact(user)
/obj/item/healthanalyzer/rad_laser/ui_interact(mob/user)
- . = ..()
+/obj/item/healthanalyzer/rad_laser/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
+ 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, "RadioactiveMicrolaser", "Radioactive Microlaser", ui_x, ui_y, master_ui, state)
+ ui.open()
var/dat = "Irradiation: [irradiate ? "On" : "Off"]
"
dat += "Stealth Mode (NOTE: Deactivates automatically while Irradiation is off): [stealth ? "On" : "Off"]
"
diff --git a/code/game/objects/items/devices/transfer_valve.dm b/code/game/objects/items/devices/transfer_valve.dm
index b929642f33..ccf9dcd253 100644
--- a/code/game/objects/items/devices/transfer_valve.dm
+++ b/code/game/objects/items/devices/transfer_valve.dm
@@ -13,6 +13,8 @@
var/mob/attacher = null
var/valve_open = FALSE
var/toggle = 1
+ var/ui_x = 310
+ var/ui_y = 320
/obj/item/transfer_valve/IsAssemblyHolder()
return TRUE
@@ -168,8 +170,8 @@
target_self = TRUE
if(change_volume)
if(!target_self)
- target.volume += tank_two.volume
- target.volume += tank_one.air_contents.volume
+ target.set_volume(target.return_volume() + tank_two.volume)
+ target.set_volume(target.return_volume() + tank_one.air_contents.return_volume())
var/datum/gas_mixture/temp
temp = tank_one.air_contents.remove_ratio(1)
target.merge(temp)
@@ -180,11 +182,11 @@
/obj/item/transfer_valve/proc/split_gases()
if (!valve_open || !tank_one || !tank_two)
return
- var/ratio1 = tank_one.air_contents.volume/tank_two.air_contents.volume
+ var/ratio1 = tank_one.air_contents.return_volume()/tank_two.air_contents.return_volume()
var/datum/gas_mixture/temp
temp = tank_two.air_contents.remove_ratio(ratio1)
tank_one.air_contents.merge(temp)
- tank_two.air_contents.volume -= tank_one.air_contents.volume
+ tank_two.air_contents.set_volume(tank_two.air_contents.return_volume() - tank_one.air_contents.return_volume())
/*
Exadv1: I know this isn't how it's going to work, but this was just to check
@@ -235,3 +237,52 @@
// eventually maybe have it update icon to show state (timer, prox etc.) like old bombs
/obj/item/transfer_valve/proc/c_state()
return
+
+/obj/item/transfer_valve/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
+ 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, "TransferValve", name, ui_x, ui_y, master_ui, state)
+ ui.open()
+
+/obj/item/transfer_valve/ui_data(mob/user)
+ var/list/data = list()
+ data["tank_one"] = tank_one
+ data["tank_two"] = tank_two
+ data["attached_device"] = attached_device
+ data["valve"] = valve_open
+ return data
+
+/obj/item/transfer_valve/ui_act(action, params)
+ if(..())
+ return
+
+ switch(action)
+ if("tankone")
+ if(tank_one)
+ split_gases()
+ valve_open = FALSE
+ tank_one.forceMove(drop_location())
+ tank_one = null
+ . = TRUE
+ if("tanktwo")
+ if(tank_two)
+ split_gases()
+ valve_open = FALSE
+ tank_two.forceMove(drop_location())
+ tank_two = null
+ . = TRUE
+ if("toggle")
+ toggle_valve()
+ . = TRUE
+ if("device")
+ if(attached_device)
+ attached_device.attack_self(usr)
+ . = TRUE
+ if("remove_device")
+ if(attached_device)
+ attached_device.on_detach()
+ attached_device = null
+ . = TRUE
+
+ update_icon()
diff --git a/code/game/objects/items/dice.dm b/code/game/objects/items/dice.dm
index 4fdb862288..2fe4c67362 100644
--- a/code/game/objects/items/dice.dm
+++ b/code/game/objects/items/dice.dm
@@ -1,10 +1,10 @@
-/obj/item/storage/pill_bottle/dice
+/obj/item/storage/box/dice
name = "bag of dice"
desc = "Contains all the luck you'll ever need."
icon = 'icons/obj/dice.dmi'
icon_state = "dicebag"
-/obj/item/storage/pill_bottle/dice/Initialize()
+/obj/item/storage/box/dice/Initialize()
. = ..()
var/special_die = pick("1","2","fudge","space","00","8bd20","4dd6","100")
if(special_die == "1")
@@ -30,7 +30,7 @@
if(special_die == "100")
new /obj/item/dice/d100(src)
-/obj/item/storage/pill_bottle/dice/suicide_act(mob/user)
+/obj/item/storage/box/dice/suicide_act(mob/user)
user.visible_message("[user] is gambling with death! It looks like [user.p_theyre()] trying to commit suicide!")
return (OXYLOSS)
diff --git a/code/game/objects/items/dualsaber.dm b/code/game/objects/items/dualsaber.dm
new file mode 100644
index 0000000000..8c7049c713
--- /dev/null
+++ b/code/game/objects/items/dualsaber.dm
@@ -0,0 +1,353 @@
+/*
+ * Double-Bladed Energy Swords - Cheridan
+ */
+/obj/item/dualsaber
+ icon_state = "dualsaber0"
+ lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
+ name = "double-bladed energy sword"
+ desc = "Handle with care."
+ force = 3
+ throwforce = 5
+ throw_speed = 3
+ throw_range = 5
+ w_class = WEIGHT_CLASS_SMALL
+ item_flags = SLOWS_WHILE_IN_HAND
+ var/w_class_on = WEIGHT_CLASS_BULKY
+ hitsound = "swing_hit"
+ var/hitsound_on = 'sound/weapons/blade1.ogg'
+ armour_penetration = 35
+ var/saber_color = "green"
+ light_color = "#00ff00"//green
+ attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
+ max_integrity = 200
+ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 70)
+ resistance_flags = FIRE_PROOF
+ block_parry_data = /datum/block_parry_data/dual_esword
+ var/hacked = FALSE
+ /// Can this reflect all energy projectiles?
+ var/can_reflect = TRUE
+ var/brightness_on = 6 //TWICE AS BRIGHT AS A REGULAR ESWORD
+ var/list/possible_colors = list("red", "blue", "green", "purple")
+ var/list/rainbow_colors = list(LIGHT_COLOR_RED, LIGHT_COLOR_GREEN, LIGHT_COLOR_LIGHT_CYAN, LIGHT_COLOR_LAVENDER)
+ var/spinnable = TRUE
+ total_mass = 0.4 //Survival flashlights typically weigh around 5 ounces.
+ var/total_mass_on = 3.4
+ var/wielded = FALSE // track wielded status on item
+ var/slowdown_wielded = 0
+
+/datum/block_parry_data/dual_esword
+ block_damage_absorption = 2
+ block_damage_multiplier = 0.15
+ block_damage_multiplier_override = list(
+ ATTACK_TYPE_MELEE = 0.25
+ )
+ block_start_delay = 0 // instantaneous block
+ block_stamina_cost_per_second = 2.5
+ block_stamina_efficiency = 3
+ block_lock_sprinting = TRUE
+ // no attacking while blocking
+ block_lock_attacking = TRUE
+ block_projectile_mitigation = 75
+
+ parry_time_windup = 0
+ parry_time_active = 8
+ parry_time_spindown = 0
+ // we want to signal to players the most dangerous phase, the time when automatic counterattack is a thing.
+ parry_time_windup_visual_override = 1
+ parry_time_active_visual_override = 3
+ parry_time_spindown_visual_override = 4
+ parry_flags = PARRY_DEFAULT_HANDLE_FEEDBACK // esword users can attack while parrying.
+ parry_time_perfect = 2 // first ds isn't perfect
+ parry_time_perfect_leeway = 1
+ parry_imperfect_falloff_percent = 10
+ parry_efficiency_to_counterattack = 100
+ parry_efficiency_considered_successful = 25 // VERY generous
+ parry_efficiency_perfect = 90
+ parry_failed_stagger_duration = 3 SECONDS
+ parry_failed_clickcd_duration = CLICK_CD_MELEE
+
+ // more efficient vs projectiles
+ block_stamina_efficiency_override = list(
+ TEXT_ATTACK_TYPE_PROJECTILE = 4
+ )
+
+/obj/item/dualsaber/Initialize()
+ . = ..()
+ RegisterSignal(src, COMSIG_TWOHANDED_WIELD, .proc/on_wield)
+ RegisterSignal(src, COMSIG_TWOHANDED_UNWIELD, .proc/on_unwield)
+
+/obj/item/dualsaber/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/two_handed, force_unwielded=3, force_wielded=34, \
+ wieldsound='sound/weapons/saberon.ogg', unwieldsound='sound/weapons/saberoff.ogg')
+
+/obj/item/dualsaber/Initialize()
+ . = ..()
+ if(LAZYLEN(possible_colors))
+ saber_color = pick(possible_colors)
+ switch(saber_color)
+ if("red")
+ light_color = LIGHT_COLOR_RED
+ if("green")
+ light_color = LIGHT_COLOR_GREEN
+ if("blue")
+ light_color = LIGHT_COLOR_LIGHT_CYAN
+ if("purple")
+ light_color = LIGHT_COLOR_LAVENDER
+
+/// Triggered on wield of two handed item
+/// Specific hulk checks due to reflection chance for balance issues and switches hitsounds.
+/obj/item/dualsaber/proc/on_wield(obj/item/source, mob/living/carbon/user)
+ if(user.has_dna() && user.dna.check_mutation(HULK))
+ to_chat(user, "You lack the grace to wield this!")
+ return COMPONENT_TWOHANDED_BLOCK_WIELD
+ wielded = TRUE
+ sharpness = IS_SHARP
+ w_class = w_class_on
+ total_mass = total_mass_on
+ hitsound = 'sound/weapons/blade1.ogg'
+ slowdown += slowdown_wielded
+ START_PROCESSING(SSobj, src)
+ set_light(brightness_on)
+ AddElement(/datum/element/sword_point)
+ item_flags |= (ITEM_CAN_BLOCK|ITEM_CAN_PARRY)
+
+/// Triggered on unwield of two handed item
+/// switch hitsounds
+/obj/item/dualsaber/proc/on_unwield(obj/item/source, mob/living/carbon/user)
+ sharpness = initial(sharpness)
+ w_class = initial(w_class)
+ total_mass = initial(total_mass)
+ wielded = FALSE
+ hitsound = "swing_hit"
+ slowdown_wielded -= slowdown_wielded
+ STOP_PROCESSING(SSobj, src)
+ set_light(0)
+ RemoveElement(/datum/element/sword_point)
+ item_flags &= ~(ITEM_CAN_BLOCK|ITEM_CAN_PARRY)
+
+/obj/item/dualsaber/Destroy()
+ STOP_PROCESSING(SSobj, src)
+ . = ..()
+
+/obj/item/dualsaber/update_icon_state()
+ if(wielded)
+ icon_state = "dualsaber[saber_color][wielded]"
+ else
+ icon_state = "dualsaber0"
+ clean_blood()
+
+/obj/item/dualsaber/suicide_act(mob/living/carbon/user)
+ if(wielded)
+ user.visible_message("[user] begins spinning way too fast! It looks like [user.p_theyre()] trying to commit suicide!")
+ var/obj/item/bodypart/head/myhead = user.get_bodypart(BODY_ZONE_HEAD)//stole from chainsaw code
+ var/obj/item/organ/brain/B = user.getorganslot(ORGAN_SLOT_BRAIN)
+ B.organ_flags &= ~ORGAN_VITAL //this cant possibly be a good idea
+ var/randdir
+ for(var/i in 1 to 24)//like a headless chicken!
+ if(user.is_holding(src))
+ randdir = pick(GLOB.alldirs)
+ user.Move(get_step(user, randdir),randdir)
+ user.emote("spin")
+ if (i == 3 && myhead)
+ myhead.drop_limb()
+ sleep(3)
+ else
+ user.visible_message("[user] panics and starts choking to death!")
+ return OXYLOSS
+ else
+ user.visible_message("[user] begins beating [user.p_them()]self to death with \the [src]'s handle! It probably would've been cooler if [user.p_they()] turned it on first!")
+ return BRUTELOSS
+
+/obj/item/dualsaber/attack(mob/target, mob/living/carbon/human/user)
+ if(user.has_dna() && user.dna.check_mutation(HULK))
+ to_chat(user, "You grip the blade too hard and accidentally drop it!")
+ user.dropItemToGround(src)
+ return
+ ..()
+ if(HAS_TRAIT(user, TRAIT_CLUMSY) && (wielded) && prob(40))
+ impale(user)
+ return
+ if(spinnable && (wielded) && prob(50))
+ INVOKE_ASYNC(src, .proc/jedi_spin, user)
+
+/obj/item/dualsaber/proc/jedi_spin(mob/living/user)
+ for(var/i in list(NORTH,SOUTH,EAST,WEST,EAST,SOUTH,NORTH,SOUTH,EAST,WEST,EAST,SOUTH))
+ user.setDir(i)
+ if(i == WEST)
+ user.emote("flip")
+ sleep(1)
+
+/obj/item/dualsaber/proc/impale(mob/living/user)
+ to_chat(user, "You twirl around a bit before losing your balance and impaling yourself on [src].")
+ if (force)
+ user.take_bodypart_damage(20,25)
+ else
+ user.adjustStaminaLoss(25)
+
+/obj/item/dualsaber/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
+ if(!wielded)
+ return NONE
+ if(can_reflect && is_energy_reflectable_projectile(object) && (attack_type & ATTACK_TYPE_PROJECTILE))
+ block_return[BLOCK_RETURN_REDIRECT_METHOD] = REDIRECT_METHOD_RETURN_TO_SENDER //no you
+ return BLOCK_SHOULD_REDIRECT | BLOCK_SUCCESS | BLOCK_REDIRECTED
+ return ..()
+
+/obj/item/dualsaber/attack_hulk(mob/living/carbon/human/user, does_attack_animation = 0) //In case thats just so happens that it is still activated on the groud, prevents hulk from picking it up
+ if(wielded)
+ to_chat(user, "You can't pick up such dangerous item with your meaty hands without losing fingers, better not to!")
+ return 1
+
+/obj/item/dualsaber/process()
+ if(wielded)
+ if(hacked)
+ rainbow_process()
+ open_flame()
+ else
+ STOP_PROCESSING(SSobj, src)
+
+/obj/item/dualsaber/proc/rainbow_process()
+ light_color = pick(rainbow_colors)
+
+/obj/item/dualsaber/ignition_effect(atom/A, mob/user)
+ // same as /obj/item/melee/transforming/energy, mostly
+ if(!wielded)
+ return ""
+ var/in_mouth = ""
+ if(iscarbon(user))
+ var/mob/living/carbon/C = user
+ if(C.wear_mask)
+ in_mouth = ", barely missing [user.p_their()] nose"
+ . = "[user] swings [user.p_their()] [name][in_mouth]. [user.p_they(TRUE)] light[user.p_s()] [user.p_their()] [A.name] in the process."
+ playsound(loc, hitsound, get_clamped_volume(), 1, -1)
+ add_fingerprint(user)
+ // Light your candles while spinning around the room
+ if(spinnable)
+ INVOKE_ASYNC(src, .proc/jedi_spin, user)
+
+/obj/item/dualsaber/green
+ possible_colors = list("green")
+
+/obj/item/dualsaber/red
+ possible_colors = list("red")
+
+/obj/item/dualsaber/blue
+ possible_colors = list("blue")
+
+/obj/item/dualsaber/purple
+ possible_colors = list("purple")
+
+/obj/item/dualsaber/attackby(obj/item/W, mob/user, params)
+ if(istype(W, /obj/item/multitool))
+ if(!hacked)
+ hacked = TRUE
+ to_chat(user, "2XRNBW_ENGAGE")
+ saber_color = "rainbow"
+ update_icon()
+ else
+ to_chat(user, "It's starting to look like a triple rainbow - no, nevermind.")
+ else
+ return ..()
+
+/////////////////////////////////////////////////////
+// HYPEREUTACTIC Blades /////////////////////////
+/////////////////////////////////////////////////////
+
+/obj/item/dualsaber/hypereutactic
+ icon = 'icons/obj/1x2.dmi'
+ icon_state = "hypereutactic"
+ lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/64x64_righthand.dmi'
+ item_state = "hypereutactic"
+ inhand_x_dimension = 64
+ inhand_y_dimension = 64
+ name = "hypereutactic blade"
+ desc = "A supermassive weapon envisioned to cleave the very fabric of space and time itself in twain, the hypereutactic blade dynamically flash-forges a hypereutactic crystaline nanostructure capable of passing through most known forms of matter like a hot knife through butter."
+ force = 7
+ hitsound_on = 'sound/weapons/nebhit.ogg'
+ armour_penetration = 60
+ light_color = "#37FFF7"
+ rainbow_colors = list("#FF0000", "#FFFF00", "#00FF00", "#00FFFF", "#0000FF","#FF00FF", "#3399ff", "#ff9900", "#fb008b", "#9800ff", "#00ffa3", "#ccff00")
+ attack_verb = list("attacked", "slashed", "stabbed", "sliced", "destroyed", "ripped", "devastated", "shredded")
+ spinnable = FALSE
+ total_mass_on = 4
+ slowdown_wielded = 1
+
+/obj/item/dualsaber/hypereutactic/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/two_handed, force_unwielded=7, force_wielded=40, \
+ wieldsound='sound/weapons/nebon.ogg', unwieldsound='sound/weapons/nebhit.ogg')
+ AddElement(/datum/element/update_icon_updates_onmob)
+
+/obj/item/dualsaber/hypereutactic/update_icon_state()
+ return
+
+/obj/item/dualsaber/hypereutactic/update_overlays()
+ . = ..()
+ var/mutable_appearance/blade_overlay = mutable_appearance(icon, "hypereutactic_blade")
+ var/mutable_appearance/gem_overlay = mutable_appearance(icon, "hypereutactic_gem")
+
+ if(light_color)
+ blade_overlay.color = light_color
+ gem_overlay.color = light_color
+
+ . += gem_overlay
+
+ if(wielded)
+ . += blade_overlay
+
+ clean_blood()
+
+/obj/item/dualsaber/hypereutactic/AltClick(mob/living/user)
+ . = ..()
+ if(!user.canUseTopic(src, BE_CLOSE, FALSE) || hacked)
+ return
+ if(user.incapacitated() || !istype(user))
+ to_chat(user, "You can't do that right now!")
+ return
+ if(alert("Are you sure you want to recolor your blade?", "Confirm Repaint", "Yes", "No") == "Yes")
+ var/energy_color_input = input(usr,"","Choose Energy Color",light_color) as color|null
+ if(!energy_color_input || !user.canUseTopic(src, BE_CLOSE, FALSE) || hacked)
+ return
+ light_color = sanitize_hexcolor(energy_color_input, desired_format=6, include_crunch=1)
+ update_icon()
+ update_light()
+ return TRUE
+
+/obj/item/dualsaber/hypereutactic/worn_overlays(isinhands, icon_file, used_state, style_flags = NONE)
+ . = ..()
+ if(isinhands)
+ var/mutable_appearance/gem_inhand = mutable_appearance(icon_file, "hypereutactic_gem")
+ gem_inhand.color = light_color
+ . += gem_inhand
+ if(wielded)
+ var/mutable_appearance/blade_inhand = mutable_appearance(icon_file, "hypereutactic_blade")
+ blade_inhand.color = light_color
+ . += blade_inhand
+
+/obj/item/dualsaber/hypereutactic/examine(mob/user)
+ . = ..()
+ if(!hacked)
+ . += "Alt-click to recolor it."
+
+/obj/item/dualsaber/hypereutactic/rainbow_process()
+ . = ..()
+ update_icon()
+ update_light()
+
+/obj/item/dualsaber/hypereutactic/chaplain
+ name = "divine lightblade"
+ desc = "A giant blade of bright and holy light, said to cut down the wicked with ease."
+ force = 5
+ block_chance = 50
+ armour_penetration = 0
+ var/chaplain_spawnable = TRUE
+ can_reflect = FALSE
+ obj_flags = UNIQUE_RENAME
+
+/obj/item/dualsaber/hypereutactic/chaplain/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/two_handed, force_unwielded=5, force_wielded=20, \
+ wieldsound='sound/weapons/nebon.ogg', unwieldsound='sound/weapons/nebhit.ogg')
+ AddComponent(/datum/component/anti_magic, TRUE, TRUE, FALSE, null, null, FALSE)
diff --git a/code/game/objects/items/eightball.dm b/code/game/objects/items/eightball.dm
index c4a15a1871..837f57ceb5 100644
--- a/code/game/objects/items/eightball.dm
+++ b/code/game/objects/items/eightball.dm
@@ -196,7 +196,7 @@
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "eightball", name, 400, 600, master_ui, state)
+ ui = new(user, src, ui_key, "EightBallVote", name, 400, 600, master_ui, state)
ui.open()
/obj/item/toy/eightball/haunted/ui_data(mob/user)
diff --git a/code/game/objects/items/electrostaff.dm b/code/game/objects/items/electrostaff.dm
new file mode 100644
index 0000000000..8d1fe4ebd1
--- /dev/null
+++ b/code/game/objects/items/electrostaff.dm
@@ -0,0 +1,263 @@
+
+/obj/item/electrostaff
+ icon = 'icons/obj/items_and_weapons.dmi'
+ icon_state = "electrostaff"
+ item_state = "electrostaff"
+ lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi'
+ name = "riot suppression electrostaff"
+ desc = "A large quarterstaff, with massive silver electrodes mounted at the end."
+ w_class = WEIGHT_CLASS_HUGE
+ slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_OCLOTHING
+ throwforce = 15 //if you are a madman and finish someone off with this, power to you.
+ throw_speed = 1
+ item_flags = NO_MAT_REDEMPTION
+ attack_verb = list("struck", "beaten", "thwacked", "pulped")
+ total_mass = 5 //yeah this is a heavy thing, beating people with it while it's off is not going to do you any favors. (to curb stun-kill rampaging without it being on)
+ block_parry_data = /datum/block_parry_data/electrostaff
+ var/obj/item/stock_parts/cell/cell = /obj/item/stock_parts/cell/high
+ var/on = FALSE
+ var/can_block_projectiles = FALSE //can't block guns
+ var/lethal_cost = 400 //10000/400*20 = 500. decent enough?
+ var/lethal_damage = 20
+ var/lethal_stam_cost = 4
+ var/stun_cost = 333 //10000/333*25 = 750. stunbatons are at time of writing 10000/1000*49 = 490.
+ var/stun_status_effect = STATUS_EFFECT_ELECTROSTAFF //a small slowdown effect
+ var/stun_stamdmg = 40
+ var/stun_status_duration = 25
+ var/stun_stam_cost = 3.5
+ var/wielded = FALSE // track wielded status on item
+
+// haha security desword time /s
+/datum/block_parry_data/electrostaff
+ block_damage_absorption = 0
+ block_damage_multiplier = 1
+ can_block_attack_types = ~ATTACK_TYPE_PROJECTILE // only able to parry non projectiles
+ block_damage_multiplier_override = list(
+ TEXT_ATTACK_TYPE_MELEE = 0.5, // only useful on melee and unarmed
+ TEXT_ATTACK_TYPE_UNARMED = 0.3
+ )
+ block_start_delay = 0.5 // near instantaneous block
+ block_stamina_cost_per_second = 3
+ block_stamina_efficiency = 2 // haha this is a horrible idea
+ // more slowdown that deswords because security
+ block_slowdown = 2
+ // no attacking while blocking
+ block_lock_attacking = TRUE
+
+ parry_time_windup = 1
+ parry_time_active = 5
+ parry_time_spindown = 0
+ parry_time_spindown_visual_override = 1
+ parry_flags = PARRY_DEFAULT_HANDLE_FEEDBACK | PARRY_LOCK_ATTACKING // no attacking while parrying
+ parry_time_perfect = 0
+ parry_time_perfect_leeway = 0.5
+ parry_efficiency_perfect = 100
+ parry_imperfect_falloff_percent = 1
+ parry_imperfect_falloff_percent_override = list(
+ TEXT_ATTACK_TYPE_PROJECTILE = 45 // really crappy vs projectiles
+ )
+ parry_time_perfect_leeway_override = list(
+ TEXT_ATTACK_TYPE_PROJECTILE = 1 // extremely harsh window for projectiles
+ )
+ // not extremely punishing to fail, but no spamming the parry.
+ parry_cooldown = 2.5 SECONDS
+ parry_failed_stagger_duration = 1.5 SECONDS
+ parry_failed_clickcd_duration = 1 SECONDS
+
+/obj/item/electrostaff/Initialize(mapload)
+ . = ..()
+ if(ispath(cell))
+ cell = new cell
+ RegisterSignal(src, COMSIG_TWOHANDED_WIELD, .proc/turn_on)
+ RegisterSignal(src, COMSIG_TWOHANDED_UNWIELD, .proc/turn_off)
+
+/obj/item/electrostaff/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/two_handed, force_multiplier=2, wieldsound="sparks", unwieldsound="sparks")
+
+/obj/item/electrostaff/Destroy()
+ STOP_PROCESSING(SSobj, src)
+ QDEL_NULL(cell)
+ return ..()
+
+/obj/item/electrostaff/get_cell()
+ . = cell
+ if(iscyborg(loc))
+ var/mob/living/silicon/robot/R = loc
+ . = R.get_cell()
+
+/obj/item/electrostaff/proc/min_hitcost()
+ return min(stun_cost, lethal_cost)
+
+/obj/item/electrostaff/proc/turn_on(obj/item/source, mob/user)
+ wielded = TRUE
+ item_flags |= (ITEM_CAN_BLOCK|ITEM_CAN_PARRY)
+ if(!cell)
+ if(user)
+ to_chat(user, "[src] has no cell.")
+ return
+ if(cell.charge < min_hitcost())
+ if(user)
+ to_chat(user, "[src] is out of charge.")
+ return
+ on = TRUE
+ START_PROCESSING(SSobj, src)
+ if(user)
+ to_chat(user, "You turn [src] on.")
+
+/obj/item/electrostaff/proc/turn_off(obj/item/source, mob/user)
+ wielded = FALSE
+ item_flags &= ~(ITEM_CAN_BLOCK|ITEM_CAN_PARRY)
+ if(user)
+ to_chat(user, "You turn [src] off.")
+ on = FALSE
+ STOP_PROCESSING(SSobj, src)
+
+/obj/item/electrostaff/update_icon_state()
+ if(!wielded)
+ icon_state = item_state = "electrostaff"
+ else
+ icon_state = item_state = (on? "electrostaff_1" : "electrostaff_0")
+ set_light(7, on? 1 : 0, LIGHT_COLOR_CYAN)
+
+/obj/item/electrostaff/examine(mob/living/user)
+ . = ..()
+ if(cell)
+ . += "The cell charge is [round(cell.percent())]%."
+ else
+ . += "There is no cell installed!"
+
+/obj/item/electrostaff/attackby(obj/item/W, mob/user, params)
+ if(istype(W, /obj/item/stock_parts/cell))
+ var/obj/item/stock_parts/cell/C = W
+ if(cell)
+ to_chat(user, "[src] already has a cell!")
+ else
+ if(C.maxcharge < min_hit_cost())
+ to_chat(user, "[src] requires a higher capacity cell.")
+ return
+ if(!user.transferItemToLoc(W, src))
+ return
+ cell = C
+ to_chat(user, "You install a cell in [src].")
+
+ else if(W.tool_behaviour == TOOL_SCREWDRIVER)
+ if(cell)
+ cell.update_icon()
+ cell.forceMove(get_turf(src))
+ cell = null
+ to_chat(user, "You remove the cell from [src].")
+ turn_off(user, TRUE)
+ else
+ return ..()
+
+/obj/item/electrostaff/process()
+ deductcharge(50) //Wasteful!
+
+/obj/item/electrostaff/proc/min_hit_cost()
+ return min(lethal_cost, stun_cost)
+
+/obj/item/electrostaff/proc/deductcharge(amount)
+ var/obj/item/stock_parts/cell/C = get_cell()
+ if(!C)
+ turn_off()
+ return FALSE
+ C.use(min(amount, C.charge))
+ if(QDELETED(src))
+ return FALSE
+ if(C.charge < min_hit_cost())
+ turn_off()
+
+/obj/item/electrostaff/attack(mob/living/target, mob/living/user)
+ if(IS_STAMCRIT(user))//CIT CHANGE - makes it impossible to baton in stamina softcrit
+ to_chat(user, "You're too exhausted to use [src] properly.")//CIT CHANGE - ditto
+ return //CIT CHANGE - ditto
+ if(on && HAS_TRAIT(user, TRAIT_CLUMSY) && prob(50))
+ clowning_around(user) //ouch!
+ return
+ if(iscyborg(target))
+ return ..()
+ var/list/return_list = list()
+ if(target.mob_run_block(src, 0, "[user]'s [name]", ATTACK_TYPE_MELEE, 0, user, null, return_list) & BLOCK_SUCCESS) //No message; run_block() handles that
+ playsound(target, 'sound/weapons/genhit.ogg', 50, 1)
+ return FALSE
+ if(user.a_intent != INTENT_HARM)
+ if(stun_act(target, user, null, return_list))
+ user.do_attack_animation(target)
+ user.adjustStaminaLossBuffered(stun_stam_cost)
+ return
+ else if(!harm_act(target, user, null, return_list))
+ return ..() //if you can't fry them just beat them with it
+ else //we did harm act them
+ user.do_attack_animation(target)
+ user.adjustStaminaLossBuffered(lethal_stam_cost)
+
+/obj/item/electrostaff/proc/stun_act(mob/living/target, mob/living/user, no_charge_and_force = FALSE, list/block_return = list())
+ var/stunforce = block_calculate_resultant_damage(stun_stamdmg, block_return)
+ if(!no_charge_and_force)
+ if(!on)
+ target.visible_message("[user] has bapped [target] with [src]. Luckily it was off.", \
+ "[user] has bapped you with [src]. Luckily it was off")
+ turn_off() //if it wasn't already off
+ return FALSE
+ var/obj/item/stock_parts/cell/C = get_cell()
+ var/chargeleft = C.charge
+ deductcharge(stun_cost)
+ if(QDELETED(src) || QDELETED(C)) //boom
+ return FALSE
+ if(chargeleft < stun_cost)
+ stunforce *= round(chargeleft/stun_cost, 0.1)
+ target.adjustStaminaLoss(stunforce)
+ target.apply_effect(EFFECT_STUTTER, stunforce)
+ SEND_SIGNAL(target, COMSIG_LIVING_MINOR_SHOCK)
+ if(user)
+ target.lastattacker = user.real_name
+ target.lastattackerckey = user.ckey
+ target.visible_message("[user] has shocked [target] with [src]!", \
+ "[user] has shocked you with [src]!")
+ log_combat(user, target, "stunned with an electrostaff")
+ playsound(src, 'sound/weapons/staff.ogg', 50, 1, -1)
+ target.apply_status_effect(stun_status_effect, stun_status_duration)
+ if(ishuman(user))
+ var/mob/living/carbon/human/H = user
+ H.forcesay(GLOB.hit_appends)
+ return TRUE
+
+/obj/item/electrostaff/proc/harm_act(mob/living/target, mob/living/user, no_charge_and_force = FALSE, list/block_return = list())
+ var/lethal_force = block_calculate_resultant_damage(lethal_damage, block_return)
+ if(!no_charge_and_force)
+ if(!on)
+ return FALSE //standard item attack
+ var/obj/item/stock_parts/cell/C = get_cell()
+ var/chargeleft = C.charge
+ deductcharge(lethal_cost)
+ if(QDELETED(src) || QDELETED(C)) //boom
+ return FALSE
+ if(chargeleft < stun_cost)
+ lethal_force *= round(chargeleft/lethal_cost, 0.1)
+ target.adjustFireLoss(lethal_force) //good against ointment spam
+ SEND_SIGNAL(target, COMSIG_LIVING_MINOR_SHOCK)
+ if(user)
+ target.lastattacker = user.real_name
+ target.lastattackerckey = user.ckey
+ target.visible_message("[user] has seared [target] with [src]!", \
+ "[user] has seared you with [src]!")
+ log_combat(user, target, "burned with an electrostaff")
+ playsound(src, 'sound/weapons/sear.ogg', 50, 1, -1)
+ return TRUE
+
+/obj/item/electrostaff/proc/clowning_around(mob/living/user)
+ 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)
+ harm_act(user, user, TRUE)
+ stun_act(user, user, TRUE)
+ deductcharge(lethal_cost)
+
+/obj/item/electrostaff/emp_act(severity)
+ . = ..()
+ if (!(. & EMP_PROTECT_SELF))
+ turn_off()
+ if(!iscyborg(loc))
+ deductcharge(1000 / severity, TRUE, FALSE)
diff --git a/code/game/objects/items/fireaxe.dm b/code/game/objects/items/fireaxe.dm
new file mode 100644
index 0000000000..41c1cbe915
--- /dev/null
+++ b/code/game/objects/items/fireaxe.dm
@@ -0,0 +1,71 @@
+/*
+ * Fireaxe
+ */
+/obj/item/fireaxe // DEM AXES MAN, marker -Agouri
+ icon_state = "fireaxe0"
+ lefthand_file = 'icons/mob/inhands/weapons/axes_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/weapons/axes_righthand.dmi'
+ name = "fire axe"
+ desc = "Truly, the weapon of a madman. Who would think to fight fire with an axe?"
+ force = 5
+ throwforce = 15
+ w_class = WEIGHT_CLASS_BULKY
+ slot_flags = ITEM_SLOT_BACK
+ attack_verb = list("attacked", "chopped", "cleaved", "torn", "cut")
+ hitsound = 'sound/weapons/bladeslice.ogg'
+ sharpness = IS_SHARP
+ max_integrity = 200
+ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 30)
+ resistance_flags = FIRE_PROOF
+ var/wielded = FALSE // track wielded status on item
+
+/obj/item/fireaxe/Initialize()
+ . = ..()
+ RegisterSignal(src, COMSIG_TWOHANDED_WIELD, .proc/on_wield)
+ RegisterSignal(src, COMSIG_TWOHANDED_UNWIELD, .proc/on_unwield)
+
+/obj/item/fireaxe/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/butchering, 100, 80, 0 , hitsound) //axes are not known for being precision butchering tools
+ AddComponent(/datum/component/two_handed, force_unwielded=5, force_wielded=24, icon_wielded="fireaxe1")
+
+/// triggered on wield of two handed item
+/obj/item/fireaxe/proc/on_wield(obj/item/source, mob/user)
+ wielded = TRUE
+
+/// triggered on unwield of two handed item
+/obj/item/fireaxe/proc/on_unwield(obj/item/source, mob/user)
+ wielded = FALSE
+
+/obj/item/fireaxe/update_icon_state()
+ icon_state = "fireaxe0"
+
+/obj/item/fireaxe/suicide_act(mob/user)
+ user.visible_message("[user] axes [user.p_them()]self from head to toe! It looks like [user.p_theyre()] trying to commit suicide!")
+ return (BRUTELOSS)
+
+/obj/item/fireaxe/afterattack(atom/A, mob/living/user, proximity)
+ . = ..()
+ if(!proximity || !wielded || IS_STAMCRIT(user))
+ return
+ if(istype(A, /obj/structure/window)) //destroys windows and grilles in one hit (or more if it has a ton of health like plasmaglass)
+ var/obj/structure/window/W = A
+ W.take_damage(200, BRUTE, "melee", 0)
+ else if(istype(A, /obj/structure/grille))
+ var/obj/structure/grille/G = A
+ G.take_damage(40, BRUTE, "melee", 0)
+
+/*
+ * Bone Axe
+ */
+/obj/item/fireaxe/boneaxe // Blatant imitation of the fireaxe, but made out of bone.
+ icon_state = "bone_axe0"
+ name = "bone axe"
+ desc = "A large, vicious axe crafted out of several sharpened bone plates and crudely tied together. Made of monsters, by killing monsters, for killing monsters."
+
+/obj/item/fireaxe/boneaxe/update_icon_state()
+ icon_state = "bone_axe0"
+
+/obj/item/fireaxe/boneaxe/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/two_handed, force_unwielded=5, force_wielded=23, icon_wielded="bone_axe1")
diff --git a/code/game/objects/items/flamethrower.dm b/code/game/objects/items/flamethrower.dm
index c785c22813..f293e6579a 100644
--- a/code/game/objects/items/flamethrower.dm
+++ b/code/game/objects/items/flamethrower.dm
@@ -204,11 +204,10 @@
//TODO: DEFERRED Consider checking to make sure tank pressure is high enough before doing this...
//Transfer 5% of current tank air contents to turf
var/datum/gas_mixture/air_transfer = ptank.air_contents.remove_ratio(release_amount)
- if(air_transfer.gases[/datum/gas/plasma])
- air_transfer.gases[/datum/gas/plasma] *= 5
+ air_transfer.set_moles(/datum/gas/plasma, air_transfer.get_moles(/datum/gas/plasma) * 5)
target.assume_air(air_transfer)
//Burn it based on transfered gas
- target.hotspot_expose((ptank.air_contents.temperature*2) + 380,500)
+ target.hotspot_expose((ptank.air_contents.return_temperature()*2) + 380,500)
//location.hotspot_expose(1000,500,1)
SSair.add_to_active(target, 0)
diff --git a/code/game/objects/items/granters.dm b/code/game/objects/items/granters.dm
index bafffa18e3..4ad71f7c46 100644
--- a/code/game/objects/items/granters.dm
+++ b/code/game/objects/items/granters.dm
@@ -510,7 +510,7 @@
oneuse = FALSE
remarks = list("So that is how icing is made!", "Placing fruit on top? How simple...", "Huh layering cake seems harder then this...", "This book smells like candy", "A clown must have made this page, or they forgot to spell check it before printing...", "Wait, a way to cook slime to be safe?")
-/obj/item/book/granter/crafting_recipe/coldcooking //IceCream
+/obj/item/book/granter/crafting_recipe/coldcooking //Icecream
name = "Cooking with Ice"
desc = "A cook book that teaches you many old icecream treats."
crafting_recipe_types = list(/datum/crafting_recipe/food/banana_split, /datum/crafting_recipe/food/root_float, /datum/crafting_recipe/food/bluecharrie_float, /datum/crafting_recipe/food/charrie_float)
diff --git a/code/game/objects/items/grenades/chem_grenade.dm b/code/game/objects/items/grenades/chem_grenade.dm
index 4ce0e811c3..f06dd634c6 100644
--- a/code/game/objects/items/grenades/chem_grenade.dm
+++ b/code/game/objects/items/grenades/chem_grenade.dm
@@ -97,7 +97,7 @@
to_chat(user, "You add [A] to the [initial(name)] assembly.")
else if(stage == EMPTY && istype(I, /obj/item/stack/cable_coil))
- if (I.use_tool(src, user, 0, 1, max_level = JOB_SKILL_BASIC))
+ if (I.use_tool(src, user, 0, 1, skill_gain_mult = TRIVIAL_USE_TOOL_MULT))
det_time = 50 // In case the cable_coil was removed and readded.
stage_change(WIRED)
to_chat(user, "You rig the [initial(name)] assembly.")
diff --git a/code/game/objects/items/handcuffs.dm b/code/game/objects/items/handcuffs.dm
index c7c9fa37a9..e3388b12eb 100644
--- a/code/game/objects/items/handcuffs.dm
+++ b/code/game/objects/items/handcuffs.dm
@@ -320,7 +320,7 @@
do_sparks(1, TRUE, src)
qdel(src)
-/obj/item/restraints/legcuffs/beartrap/energy/attack_hand(mob/user)
+/obj/item/restraints/legcuffs/beartrap/energy/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
Crossed(user) //honk
. = ..()
diff --git a/code/game/objects/items/holy_weapons.dm b/code/game/objects/items/holy_weapons.dm
index 381257721e..d6396f9902 100644
--- a/code/game/objects/items/holy_weapons.dm
+++ b/code/game/objects/items/holy_weapons.dm
@@ -168,6 +168,7 @@
icon_state = "witchhunterhat"
item_state = "witchhunterhat"
flags_cover = HEADCOVERSEYES
+ flags_inv = HIDEHAIR
/obj/item/storage/box/holy/follower
name = "Followers of the Chaplain Kit"
diff --git a/code/game/objects/items/kitchen.dm b/code/game/objects/items/kitchen.dm
index dda41494ff..2c00265ce8 100644
--- a/code/game/objects/items/kitchen.dm
+++ b/code/game/objects/items/kitchen.dm
@@ -54,6 +54,14 @@
else
return ..()
+/obj/item/kitchen/fork/throwing
+ name = "throwing fork"
+ desc = "A fork, sharpened to perfection, making it a great weapon for throwing."
+ throwforce = 15
+ throw_speed = 4
+ throw_range = 6
+ embedding = list("pain_mult" = 2, "embed_chance" = 100, "fall_chance" = 0, "embed_chance_turf_mod" = 15)
+ sharpness = IS_SHARP
/obj/item/kitchen/knife
name = "kitchen knife"
diff --git a/code/game/objects/items/melee/energy.dm b/code/game/objects/items/melee/energy.dm
index aec3c333b7..20960da7c6 100644
--- a/code/game/objects/items/melee/energy.dm
+++ b/code/game/objects/items/melee/energy.dm
@@ -369,7 +369,7 @@
return
else
to_chat(user, "You combine the two light swords, making a single supermassive blade! You're cool.")
- new /obj/item/twohanded/dualsaber/hypereutactic(user.drop_location())
+ new /obj/item/dualsaber/hypereutactic(user.drop_location())
qdel(W)
qdel(src)
else
diff --git a/code/game/objects/items/melee/misc.dm b/code/game/objects/items/melee/misc.dm
index 741607edc3..b2e463e89e 100644
--- a/code/game/objects/items/melee/misc.dm
+++ b/code/game/objects/items/melee/misc.dm
@@ -621,7 +621,7 @@
to_chat(user, "[target] doesn't seem to want to get on [src]!")
update_icon()
-/obj/item/melee/roastingstick/attack_hand(mob/user)
+/obj/item/melee/roastingstick/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
..()
if (held_sausage)
user.put_in_hands(held_sausage)
@@ -689,7 +689,7 @@
item_state = "mace_greyscale"
lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
- material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS | MATERIAL_EFFECTS //Material type changes the prefix as well as the color.
+ material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS //Material type changes the prefix as well as the color.
custom_materials = list(/datum/material/iron = 12000) //Defaults to an Iron Mace.
slot_flags = ITEM_SLOT_BELT
force = 14
diff --git a/code/game/objects/items/miscellaneous.dm b/code/game/objects/items/miscellaneous.dm
index 7237a1788f..a7f7e3152c 100644
--- a/code/game/objects/items/miscellaneous.dm
+++ b/code/game/objects/items/miscellaneous.dm
@@ -18,6 +18,8 @@
icon = 'icons/obj/device.dmi'
icon_state = "gangtool-blue"
item_state = "radio"
+ var/list/stored_options
+ var/force_refresh = FALSE //if set to true, the beacon will recalculate its display options whenever opened
/obj/item/choice_beacon/attack_self(mob/user)
if(canUseBeacon(user))
@@ -34,14 +36,15 @@
return FALSE
/obj/item/choice_beacon/proc/generate_options(mob/living/M)
- var/list/display_names = generate_display_names()
- if(!display_names.len)
+ if(!stored_options || force_refresh)
+ stored_options = generate_display_names()
+ if(!stored_options.len)
return
- var/choice = input(M,"Which item would you like to order?","Select an Item") as null|anything in display_names
+ var/choice = input(M,"Which item would you like to order?","Select an Item") as null|anything in stored_options
if(!choice || !M.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
return
- spawn_option(display_names[choice],M)
+ spawn_option(stored_options[choice],M)
qdel(src)
/obj/item/choice_beacon/proc/spawn_option(obj/choice,mob/living/M)
@@ -153,6 +156,51 @@
new choice(get_turf(M))
to_chat(M, "You hear something crackle from the beacon for a moment before a voice speaks. \"Please stand by for a message from S.E.L.F. Message as follows: Item request received. Your package has been transported, use the autosurgeon supplied to apply the upgrade. Message ends.\"")
+/obj/item/choice_beacon/box
+ name = "choice box (default)"
+ desc = "Think really hard about what you want, and then rip it open!"
+ icon = 'icons/obj/storage.dmi'
+ icon_state = "deliverypackage3"
+ item_state = "deliverypackage3"
+
+/obj/item/choice_beacon/box/spawn_option(obj/choice,mob/living/M)
+ to_chat(M, "The box opens, revealing the [choice]!")
+ playsound(src.loc, 'sound/items/poster_ripped.ogg', 50, 1)
+ M.temporarilyRemoveItemFromInventory(src, TRUE)
+ M.put_in_hands(new choice)
+ qdel(src)
+
+/obj/item/choice_beacon/box/plushie
+ name = "choice box (plushie)"
+ desc = "Using the power of quantum entanglement, this box contains every plush, until the moment it is opened!"
+ icon = 'icons/obj/plushes.dmi'
+ icon_state = "box"
+ item_state = "box"
+
+/obj/item/choice_beacon/box/spawn_option(choice,mob/living/M)
+ if(ispath(choice, /obj/item/toy/plush))
+ ..() //regular plush, spawn it naturally
+ else
+ //snowflake plush
+ var/obj/item/toy/plush/snowflake_plushie = new(get_turf(M))
+ snowflake_plushie.set_snowflake_from_config(choice)
+ M.temporarilyRemoveItemFromInventory(src, TRUE)
+ M.put_in_hands(new choice)
+ qdel(src)
+
+/obj/item/choice_beacon/box/plushie/generate_display_names()
+ var/list/plushie_list = list()
+ //plushie set 1: just subtypes of /obj/item/toy/plush
+ var/list/plushies_set_one = subtypesof(/obj/item/toy/plush) - list(/obj/item/toy/plush/narplush, /obj/item/toy/plush/awakenedplushie, /obj/item/toy/plush/random_snowflake, /obj/item/toy/plush/random) //don't allow these special ones (you can still get narplush/hugbox)
+ for(var/V in plushies_set_one)
+ var/atom/A = V
+ plushie_list[initial(A.name)] = A
+ //plushie set 2: snowflake plushies
+ var/list/plushies_set_two = CONFIG_GET(keyed_list/snowflake_plushies)
+ for(var/V in plushies_set_two)
+ plushie_list[V] = V //easiest way to do this which works with how selecting options works, despite being snowflakey to have the key equal the value
+ return plushie_list
+
/obj/item/skub
desc = "It's skub."
name = "skub"
diff --git a/code/game/objects/items/pet_carrier.dm b/code/game/objects/items/pet_carrier.dm
index 9d9409acf0..a60aa02ce1 100644
--- a/code/game/objects/items/pet_carrier.dm
+++ b/code/game/objects/items/pet_carrier.dm
@@ -22,6 +22,11 @@
var/occupant_weight = 0
var/max_occupants = 3 //Hard-cap so you can't have infinite mice or something in one carrier
var/max_occupant_weight = MOB_SIZE_SMALL //This is calculated from the mob sizes of occupants
+ var/entrance_name = "door" //name of the entrance to the item
+ var/escape_time = 200 //how long it takes for mobs above small sizes to escape (for small sizes, its randomly 1.5 to 2x this)
+ var/load_time = 30 //how long it takes for mobs to be loaded into the pet carrier
+ var/has_lock_sprites = TRUE //whether to load the lock overlays or not
+ var/allows_hostiles = FALSE //does the pet carrier allow hostile entities to be held within it?
/obj/item/pet_carrier/Destroy()
if(occupants.len)
@@ -51,20 +56,20 @@
else
. += "It has nothing inside."
if(user.canUseTopic(src))
- . += "Activate it in your hand to [open ? "close" : "open"] its door."
+ . += "Activate it in your hand to [open ? "close" : "open"] its [entrance_name]."
if(!open)
- . += "Alt-click to [locked ? "unlock" : "lock"] its door."
+ . += "Alt-click to [locked ? "unlock" : "lock"] its [entrance_name]."
/obj/item/pet_carrier/attack_self(mob/living/user)
if(open)
- to_chat(user, "You close [src]'s door.")
+ to_chat(user, "You close [src]'s [entrance_name].")
playsound(user, 'sound/effects/bin_close.ogg', 50, TRUE)
open = FALSE
else
if(locked)
to_chat(user, "[src] is locked!")
return
- to_chat(user, "You open [src]'s door.")
+ to_chat(user, "You open [src]'s [entrance_name].")
playsound(user, 'sound/effects/bin_open.ogg', 50, TRUE)
open = TRUE
update_icon()
@@ -86,7 +91,7 @@
if(user.a_intent == INTENT_HARM)
return ..()
if(!open)
- to_chat(user, "You need to open [src]'s door!")
+ to_chat(user, "You need to open [src]'s [entrance_name]!")
return
if(target.mob_size > max_occupant_weight)
if(ishuman(target))
@@ -94,13 +99,15 @@
if(iscatperson(H))
to_chat(user, "You'd need a lot of catnip and treats, plus maybe a laser pointer, for that to work.")
else
- to_chat(user, "Humans, generally, do not fit into pet carriers.")
+ to_chat(user, "Humans, generally, do not fit into [name]s.")
else
to_chat(user, "You get the feeling [target] isn't meant for a [name].")
return
if(user == target)
to_chat(user, "Why would you ever do that?")
return
+ if(ishostile(target) && (!allows_hostiles || istype(target, /mob/living/simple_animal/hostile/carp/cayenne)) || target.move_resist < MOVE_FORCE_VERY_STRONG) //don't allow goliaths into pet carriers, but let cayenne in!
+ to_chat(user, "You have a feeling you shouldn't keep this as a pet.")
load_occupant(user, target)
/obj/item/pet_carrier/relaymove(mob/living/user, direction)
@@ -110,8 +117,8 @@
remove_occupant(user)
return
else if(!locked)
- loc.visible_message("[user] pushes open the door to [src]!", \
- "[user] pushes open the door of [src]!")
+ loc.visible_message("[user] pushes open the [entrance_name] to [src]!", \
+ "[user] pushes open the [entrance_name] of [src]!")
open = TRUE
update_icon()
return
@@ -119,12 +126,19 @@
container_resist(user)
/obj/item/pet_carrier/container_resist(mob/living/user)
+ //don't do the whole resist timer thing if it's open!
+ if(open)
+ loc.visible_message("[user] climbs out of [src]!", \
+ "[user] jumps out of [src]!")
+ remove_occupant(user)
+ return
+
user.changeNext_move(CLICK_CD_BREAKOUT)
user.last_special = world.time + CLICK_CD_BREAKOUT
if(user.mob_size <= MOB_SIZE_SMALL)
- to_chat(user, "You poke a limb through [src]'s bars and start fumbling for the lock switch... (This will take some time.)")
- to_chat(loc, "You see [user] reach through the bars and fumble for the lock switch!")
- if(!do_after(user, rand(300, 400), target = user) || open || !locked || !(user in occupants))
+ to_chat(user, "You begin to try escaping the [src] and start fumbling for the lock switch... (This will take some time.)")
+ to_chat(loc, "You see [user] attempting to unlock the [src]!")
+ if(!do_after(user, rand(escape_time * 1.5, escape_time * 2), target = user) || open || !locked || !(user in occupants))
return
loc.visible_message("[user] flips the lock switch on [src] by reaching through!", null, null, null, user)
to_chat(user, "Bingo! The lock pops open!")
@@ -132,12 +146,12 @@
playsound(src, 'sound/machines/boltsup.ogg', 30, TRUE)
update_icon()
else
- loc.visible_message("[src] starts rattling as something pushes against the door!", null, null, null, user)
+ loc.visible_message("[src] starts rattling as something pushes against the [entrance_name]!", null, null, null, user)
to_chat(user, "You start pushing out of [src]... (This will take about 20 seconds.)")
- if(!do_after(user, 200, target = user) || open || !locked || !(user in occupants))
+ if(!do_after(user, escape_time, target = user) || open || !locked || !(user in occupants))
return
loc.visible_message("[user] shoves out of [src]!", null, null, null, user)
- to_chat(user, "You shove open [src]'s door against the lock's resistance and fall out!")
+ to_chat(user, "You shove open [src]'s [entrance_name] against the lock's resistance and fall out!")
locked = FALSE
open = TRUE
update_icon()
@@ -151,7 +165,7 @@
/obj/item/pet_carrier/update_overlays()
. = ..()
- if(!open)
+ if(!open && has_lock_sprites)
. += "[locked ? "" : "un"]locked"
/obj/item/pet_carrier/MouseDrop(atom/over_atom)
@@ -170,7 +184,7 @@
user.visible_message("[user] starts loading [target] into [src].", \
"You start loading [target] into [src]...", null, null, target)
to_chat(target, "[user] starts loading you into [user.p_their()] [name]!")
- if(!do_mob(user, target, 30))
+ if(!do_mob(user, target, load_time))
return
if(target in occupants)
return
@@ -192,9 +206,75 @@
/obj/item/pet_carrier/proc/remove_occupant(mob/living/occupant, turf/new_turf)
if(!(occupant in occupants) || !istype(occupant))
return
- occupant.forceMove(new_turf ? new_turf : drop_location())
+ occupant.forceMove(new_turf ? new_turf : get_turf(src))
occupants -= occupant
occupant_weight -= occupant.mob_size
occupant.setDir(SOUTH)
+//bluespace jar, a reskin of the pet carrier that can fit people and smashes when thrown
+/obj/item/pet_carrier/bluespace
+ name = "bluespace jar"
+ desc = "A jar, that seems to be bigger on the inside, somehow allowing lifeforms to fit through its narrow entrance."
+ open = FALSE //starts closed so it looks better on menus
+ icon_state = "bluespace_jar"
+ item_state = "bluespace_jar"
+ lefthand_file = ""
+ righthand_file = ""
+ max_occupant_weight = MOB_SIZE_HUMAN //can fit people, like a bluespace bodybag!
+ load_time = 40 //loading things into a jar takes longer than a regular pet carrier
+ entrance_name = "lid"
+ w_class = WEIGHT_CLASS_SMALL //it's a jar
+ throw_speed = 3
+ throw_range = 7
+ max_occupants = 1 //far less than a regular carrier or bluespace bodybag, because it can be thrown to release the contents
+ allows_hostiles = TRUE //can fit hostile creatures, with the move resist restrictions in place, this means they still cannot take things like legions/goliaths/etc regardless
+ has_lock_sprites = FALSE //jar doesn't show the regular lock overlay
+ custom_materials = list(/datum/material/glass = 1000, /datum/material/bluespace = 600)
+ escape_time = 10 //half the time of a bluespace bodybag
+ var/datum/gas_mixture/occupant_gas_supply
+
+/obj/item/pet_carrier/bluespace/update_icon_state()
+ if(open)
+ icon_state = "bluespace_jar_open"
+ else
+ icon_state = "bluespace_jar"
+
+/obj/item/pet_carrier/bluespace/throw_impact()
+ . = ..()
+ //delete the item upon impact, releasing the creature inside (this is handled by its deletion)
+ if(occupants.len)
+ loc.visible_message("The bluespace jar smashes, releasing [occupants[1]]!")
+ playsound(src, "shatter", 70, 1)
+ qdel(src)
+
+/obj/item/pet_carrier/bluespace/add_occupant(mob/living/occupant) //update the gas supply as required, this acts like magical internals
+ . = ..()
+ if(!occupant_gas_supply)
+ occupant_gas_supply = new
+ if(isanimal(occupant))
+ var/mob/living/simple_animal/animal = occupant
+ occupant_gas_supply[/datum/gas/oxygen] = 0.0064 //make sure it has some gas in so it isn't depressurized
+ occupant_gas_supply.set_temperature(animal.minbodytemp) //simple animals only care about temperature/pressure when their turf isnt a location
+ else
+ if(ishuman(occupant)) //humans require resistance to cold/heat and living in no air while inside, and lose this when outside
+ ADD_TRAIT(occupant, TRAIT_RESISTCOLD, "bluespace_container_cold_resist")
+ ADD_TRAIT(occupant, TRAIT_RESISTHEAT, "bluespace_container_heat_resist")
+ ADD_TRAIT(occupant, TRAIT_NOBREATH, "bluespace_container_no_breath")
+ ADD_TRAIT(occupant, TRAIT_RESISTHIGHPRESSURE, "bluespace_container_resist_high_pressure")
+ ADD_TRAIT(occupant, TRAIT_RESISTLOWPRESSURE, "bluespace_container_resist_low_pressure")
+
+/obj/item/pet_carrier/bluespace/remove_occupant(mob/living/occupant)
+ . = ..()
+ if(ishuman(occupant))
+ REMOVE_TRAIT(occupant, TRAIT_RESISTCOLD, "bluespace_container_cold_resist")
+ REMOVE_TRAIT(occupant, TRAIT_RESISTHEAT, "bluespace_container_heat_resist")
+ REMOVE_TRAIT(occupant, TRAIT_NOBREATH, "bluespace_container_no_breath")
+ REMOVE_TRAIT(occupant, TRAIT_RESISTHIGHPRESSURE, "bluespace_container_resist_high_pressure")
+ REMOVE_TRAIT(occupant, TRAIT_RESISTLOWPRESSURE, "bluespace_container_resist_low_pressure")
+
+/obj/item/pet_carrier/bluespace/return_air()
+ if(!occupant_gas_supply)
+ occupant_gas_supply = new
+ return occupant_gas_supply
+
#undef pet_carrier_full
diff --git a/code/game/objects/items/pitchfork.dm b/code/game/objects/items/pitchfork.dm
new file mode 100644
index 0000000000..49d0b64498
--- /dev/null
+++ b/code/game/objects/items/pitchfork.dm
@@ -0,0 +1,101 @@
+/obj/item/pitchfork
+ icon_state = "pitchfork0"
+ lefthand_file = 'icons/mob/inhands/weapons/polearms_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/weapons/polearms_righthand.dmi'
+ name = "pitchfork"
+ desc = "A simple tool used for moving hay."
+ force = 7
+ throwforce = 15
+ w_class = WEIGHT_CLASS_BULKY
+ attack_verb = list("attacked", "impaled", "pierced")
+ hitsound = 'sound/weapons/bladeslice.ogg'
+ sharpness = IS_SHARP
+ max_integrity = 200
+ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 30)
+ resistance_flags = FIRE_PROOF
+ var/wielded = FALSE // track wielded status on item
+
+/obj/item/pitchfork/Initialize()
+ . = ..()
+ RegisterSignal(src, COMSIG_TWOHANDED_WIELD, .proc/on_wield)
+ RegisterSignal(src, COMSIG_TWOHANDED_UNWIELD, .proc/on_unwield)
+
+/obj/item/pitchfork/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/two_handed, force_unwielded=7, force_wielded=15, icon_wielded="pitchfork1")
+ AddElement(/datum/element/sword_point)
+
+/// triggered on wield of two handed item
+/obj/item/pitchfork/proc/on_wield(obj/item/source, mob/user)
+ wielded = TRUE
+
+/// triggered on unwield of two handed item
+/obj/item/pitchfork/proc/on_unwield(obj/item/source, mob/user)
+ wielded = FALSE
+
+/obj/item/pitchfork/update_icon_state()
+ icon_state = "pitchfork0"
+
+/obj/item/pitchfork/demonic
+ name = "demonic pitchfork"
+ desc = "A red pitchfork, it looks like the work of the devil."
+ force = 19
+ throwforce = 24
+
+/obj/item/pitchfork/demonic/Initialize()
+ . = ..()
+ set_light(3,6,LIGHT_COLOR_RED)
+
+/obj/item/pitchfork/demonic/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/two_handed, force_unwielded=19, force_wielded=25)
+
+/obj/item/pitchfork/demonic/greater
+ force = 24
+ throwforce = 50
+
+/obj/item/pitchfork/demonic/greater/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/two_handed, force_unwielded=24, force_wielded=34)
+
+/obj/item/pitchfork/demonic/ascended
+ force = 100
+ throwforce = 100
+
+/obj/item/pitchfork/demonic/ascended/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/two_handed, force_unwielded=100, force_wielded=500000) // Kills you DEAD
+
+/obj/item/pitchfork/suicide_act(mob/user)
+ user.visible_message("[user] impales [user.p_them()]self in [user.p_their()] abdomen with [src]! It looks like [user.p_theyre()] trying to commit suicide!")
+ return (BRUTELOSS)
+
+/obj/item/pitchfork/demonic/pickup(mob/living/user)
+ . = ..()
+ if(isliving(user) && user.mind && user.owns_soul() && !is_devil(user))
+ var/mob/living/U = user
+ U.visible_message("As [U] picks [src] up, [U]'s arms briefly catch fire.", \
+ "\"As you pick up [src] your arms ignite, reminding you of all your past sins.\"")
+ if(ishuman(U))
+ var/mob/living/carbon/human/H = U
+ H.apply_damage(rand(force/2, force), BURN, pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
+ else
+ U.adjustFireLoss(rand(force/2,force))
+
+/obj/item/pitchfork/demonic/attack(mob/target, mob/living/carbon/human/user)
+ if(user.mind && user.owns_soul() && !is_devil(user))
+ to_chat(user, "[src] burns in your hands.")
+ user.apply_damage(rand(force/2, force), BURN, pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
+ ..()
+
+/obj/item/pitchfork/demonic/ascended/afterattack(atom/target, mob/user, proximity)
+ . = ..()
+ if(!proximity || !wielded)
+ return
+ if(iswallturf(target))
+ var/turf/closed/wall/W = target
+ user.visible_message("[user] blasts \the [target] with \the [src]!")
+ playsound(target, 'sound/magic/disintegrate.ogg', 100, TRUE)
+ W.break_wall()
+ W.ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
+ return
diff --git a/code/game/objects/items/robot/robot_items.dm b/code/game/objects/items/robot/robot_items.dm
index 4b8728e426..4b2e022da7 100644
--- a/code/game/objects/items/robot/robot_items.dm
+++ b/code/game/objects/items/robot/robot_items.dm
@@ -355,6 +355,7 @@
emaggedhitdamage = 0
/obj/item/borg/lollipop/equipped()
+ . = ..()
check_amount()
/obj/item/borg/lollipop/dropped(mob/user)
@@ -745,8 +746,8 @@
***********************************************************************/
/obj/item/weapon/gripper
- name = "circuit gripper"
- desc = "A simple grasping tool for inserting circuitboards into machinary."
+ name = "engineering gripper"
+ desc = "A simple grasping tool for interacting with various engineering related items, such as circuits, gas tanks and conveyer belts. Alt click to drop instead of use."
icon = 'icons/obj/device.dmi'
icon_state = "gripper"
@@ -754,18 +755,36 @@
//Has a list of items that it can hold.
var/list/can_hold = list(
- /obj/item/circuitboard
+ /obj/item/circuitboard,
+ /obj/item/light,
+ /obj/item/electronics,
+ /obj/item/tank,
+ /obj/item/conveyor_switch_construct,
+ /obj/item/stack/conveyor,
+ /obj/item/wallframe,
+ /obj/item/vending_refill,
+ /obj/item/stack/sheet,
+ /obj/item/stack/tile,
+ /obj/item/stack/rods,
+ /obj/item/stock_parts
+ )
+ //Basically a blacklist for any subtypes above we dont want
+ var/list/cannot_hold = list(
+ /obj/item/stack/sheet/mineral/plasma,
+ /obj/item/stack/sheet/plasteel
)
var/obj/item/wrapped = null // Item currently being held.
-/obj/item/weapon/gripper/attack_self()
+//Used to interact with UI's of held items, such as gas tanks and airlock electronics.
+/obj/item/weapon/gripper/AltClick(mob/user)
if(wrapped)
wrapped.forceMove(get_turf(wrapped))
+ to_chat(user, "You drop the [wrapped].")
wrapped = null
return ..()
-/obj/item/weapon/gripper/afterattack(var/atom/target, var/mob/living/user, proximity, params)
+/obj/item/weapon/gripper/afterattack(var/atom/target, var/mob/living/silicon/robot/user, proximity, params)
if(!proximity)
return
@@ -791,18 +810,21 @@
return
else if(istype(target,/obj/item))
-
var/obj/item/I = target
-
var/grab = 0
+
for(var/typepath in can_hold)
if(istype(I,typepath))
grab = 1
- break
+ for(var/badpath in cannot_hold)
+ if(istype(I,badpath))
+ if(!user.emagged)
+ grab = 0
+ continue
//We can grab the item, finally.
if(grab)
- to_chat(user, "You collect \the [I].")
+ to_chat(user, "You collect \the [I].")
I.loc = src
wrapped = I
return
@@ -811,19 +833,12 @@
/obj/item/weapon/gripper/mining
name = "shelter capsule deployer"
- desc = "A simple grasping tool for carrying and deploying shelter capsules."
+ desc = "A simple grasping tool for carrying and deploying shelter capsules. Alt click to drop instead of use."
icon_state = "gripper_mining"
can_hold = list(
/obj/item/survivalcapsule
)
-/obj/item/weapon/gripper/mining/attack_self()
- if(wrapped)
- wrapped.forceMove(get_turf(wrapped))
- wrapped.attack_self()
- wrapped = null
- return
-
/obj/item/gun/energy/plasmacutter/cyborg
name = "cyborg plasma cutter"
desc = "A basic variation of the plasma cutter, compressed into a cyborg chassis. Less effective than normal plasma cutters."
diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm
index bf82ee1ea9..9fbedb33cd 100644
--- a/code/game/objects/items/robot/robot_upgrades.dm
+++ b/code/game/objects/items/robot/robot_upgrades.dm
@@ -459,7 +459,7 @@
/obj/item/borg/upgrade/defib/deactivate(mob/living/silicon/robot/R, user = usr)
. = ..()
if (.)
- var/obj/item/twohanded/shockpaddles/cyborg/S = locate() in R.module
+ var/obj/item/shockpaddles/cyborg/S = locate() in R.module
R.module.remove_module(S, TRUE)
/obj/item/borg/upgrade/processor
@@ -566,7 +566,7 @@
/obj/item/borg/upgrade/expand/deactivate(mob/living/silicon/robot/R, user = usr)
. = ..()
- if (.)
+ if (. && R.hasExpanded)
R.resize = 0.5
R.hasExpanded = FALSE
R.update_transform()
diff --git a/code/game/objects/items/sharpener.dm b/code/game/objects/items/sharpener.dm
index 014d4cb159..6bf0b27fb4 100644
--- a/code/game/objects/items/sharpener.dm
+++ b/code/game/objects/items/sharpener.dm
@@ -24,24 +24,22 @@
if(istype(I, /obj/item/melee/transforming/energy))
to_chat(user, "You don't think \the [I] will be the thing getting modified if you use it on \the [src]!")
return
- if(istype(I, /obj/item/twohanded))//some twohanded items should still be sharpenable, but handle force differently. therefore i need this stuff
- var/obj/item/twohanded/TH = I
- if(TH.force_wielded >= max)
- to_chat(user, "[TH] is much too powerful to sharpen further!")
- return
- if(TH.wielded)
- to_chat(user, "[TH] must be unwielded before it can be sharpened!")
- return
- if(TH.force_wielded > initial(TH.force_wielded))
- to_chat(user, "[TH] has already been refined before. It cannot be sharpened further!")
- return
- TH.force_wielded = clamp(TH.force_wielded + increment, 0, max)//wieldforce is increased since normal force wont stay
- if(I.force > initial(I.force))
+
+ var/signal_out = SEND_SIGNAL(I, COMSIG_ITEM_SHARPEN_ACT, increment, max)
+ if(signal_out & COMPONENT_BLOCK_SHARPEN_MAXED)
+ to_chat(user, "[I] is much too powerful to sharpen further!")
+ return
+ if(signal_out & COMPONENT_BLOCK_SHARPEN_BLOCKED)
+ to_chat(user, "[I] is not able to be sharpened right now!")
+ return
+ if((signal_out & COMPONENT_BLOCK_SHARPEN_ALREADY) || (I.force > initial(I.force) && !signal_out))
to_chat(user, "[I] has already been refined before. It cannot be sharpened further!")
return
+ if(!(signal_out & COMPONENT_BLOCK_SHARPEN_APPLIED))
+ I.force = clamp(I.force + increment, 0, max)
+
user.visible_message("[user] sharpens [I] with [src]!", "You sharpen [I], making it much more deadly than before.")
I.sharpness = IS_SHARP_ACCURATE
- I.force = clamp(I.force + increment, 0, max)
I.throwforce = clamp(I.throwforce + increment, 0, max)
I.name = "[prefix] [I.name]"
name = "worn out [name]"
diff --git a/code/game/objects/items/shields.dm b/code/game/objects/items/shields.dm
index aefa5d3cc8..992c173d3b 100644
--- a/code/game/objects/items/shields.dm
+++ b/code/game/objects/items/shields.dm
@@ -26,7 +26,7 @@
/datum/block_parry_data/shield
block_damage_multiplier = 0.25
block_stamina_efficiency = 2.5
- block_stamina_cost_per_second = 3.5
+ block_stamina_cost_per_second = 2.5
block_slowdown = 0
block_lock_attacking = FALSE
block_lock_sprinting = TRUE
@@ -386,7 +386,7 @@ obj/item/shield/riot/bullet_proof
max_integrity = 100
obj_integrity = 100
can_shatter = FALSE
- item_flags = SLOWS_WHILE_IN_HAND
+ item_flags = SLOWS_WHILE_IN_HAND | ITEM_CAN_BLOCK
var/recharge_timerid
var/recharge_delay = 15 SECONDS
@@ -446,6 +446,12 @@ obj/item/shield/riot/bullet_proof
return BLOCK_SUCCESS | BLOCK_REDIRECTED | BLOCK_SHOULD_REDIRECT
return ..()
+/obj/item/shield/energy/active_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return, override_direction)
+ if((attack_type & ATTACK_TYPE_PROJECTILE) && is_energy_reflectable_projectile(object))
+ block_return[BLOCK_RETURN_REDIRECT_METHOD] = REDIRECT_METHOD_DEFLECT
+ return BLOCK_SUCCESS | BLOCK_REDIRECTED | BLOCK_SHOULD_REDIRECT
+ return ..()
+
/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]!")
diff --git a/code/game/objects/items/shooting_range.dm b/code/game/objects/items/shooting_range.dm
index 2f40604719..7fd37053b1 100644
--- a/code/game/objects/items/shooting_range.dm
+++ b/code/game/objects/items/shooting_range.dm
@@ -31,7 +31,7 @@
to_chat(user, "You slice off [src]'s uneven chunks of aluminium and scorch marks.")
return TRUE
-/obj/item/target/attack_hand(mob/user)
+/obj/item/target/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/objects/items/singularityhammer.dm b/code/game/objects/items/singularityhammer.dm
index dc761ee3bf..7a6c159160 100644
--- a/code/game/objects/items/singularityhammer.dm
+++ b/code/game/objects/items/singularityhammer.dm
@@ -1,4 +1,4 @@
-/obj/item/twohanded/singularityhammer
+/obj/item/singularityhammer
name = "singularity hammer"
desc = "The pinnacle of close combat technology, the hammer harnesses the power of a miniaturized singularity to deal crushing blows."
icon_state = "mjollnir0"
@@ -7,35 +7,47 @@
flags_1 = CONDUCT_1
slot_flags = ITEM_SLOT_BACK
force = 5
- force_unwielded = 5
- force_wielded = 20
throwforce = 15
throw_range = 1
w_class = WEIGHT_CLASS_HUGE
- var/charged = 5
armor = list("melee" = 50, "bullet" = 50, "laser" = 50, "energy" = 0, "bomb" = 50, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100)
resistance_flags = FIRE_PROOF | ACID_PROOF
force_string = "LORD SINGULOTH HIMSELF"
total_mass = TOTAL_MASS_MEDIEVAL_WEAPON
+ var/charged = 5
+ var/wielded = FALSE // track wielded status on item
-/obj/item/twohanded/singularityhammer/New()
+/obj/item/singularityhammer/New()
..()
+ RegisterSignal(src, COMSIG_TWOHANDED_WIELD, .proc/on_wield)
+ RegisterSignal(src, COMSIG_TWOHANDED_UNWIELD, .proc/on_unwield)
START_PROCESSING(SSobj, src)
-/obj/item/twohanded/singularityhammer/Destroy()
+/obj/item/singularityhammer/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/two_handed, force_multiplier=4, icon_wielded="mjollnir1")
+
+/// triggered on wield of two handed item
+/obj/item/singularityhammer/proc/on_wield(obj/item/source, mob/user)
+ wielded = TRUE
+
+/// triggered on unwield of two handed item
+/obj/item/singularityhammer/proc/on_unwield(obj/item/source, mob/user)
+ wielded = FALSE
+
+/obj/item/singularityhammer/update_icon_state()
+ icon_state = "mjollnir0"
+
+/obj/item/singularityhammer/Destroy()
STOP_PROCESSING(SSobj, src)
return ..()
-/obj/item/twohanded/singularityhammer/process()
+/obj/item/singularityhammer/process()
if(charged < 5)
charged++
return
-/obj/item/twohanded/singularityhammer/update_icon_state() //Currently only here to fuck with the on-mob icons.
- icon_state = "mjollnir[wielded]"
- return
-
-/obj/item/twohanded/singularityhammer/proc/vortex(turf/pull, mob/wielder)
+/obj/item/singularityhammer/proc/vortex(turf/pull, mob/wielder)
for(var/atom/X in orange(5,pull))
if(ismovable(X))
var/atom/movable/A = X
@@ -55,9 +67,8 @@
step_towards(H,pull)
step_towards(H,pull)
step_towards(H,pull)
- return
-/obj/item/twohanded/singularityhammer/afterattack(atom/A as mob|obj|turf|area, mob/user, proximity)
+/obj/item/singularityhammer/afterattack(atom/A as mob|obj|turf|area, mob/user, proximity)
. = ..()
if(!proximity)
return
@@ -71,7 +82,7 @@
var/turf/target = get_turf(A)
vortex(target,user)
-/obj/item/twohanded/mjollnir
+/obj/item/mjollnir
name = "Mjolnir"
desc = "A weapon worthy of a god, able to strike with the force of a lightning bolt. It crackles with barely contained energy."
icon_state = "mjollnir0"
@@ -80,14 +91,33 @@
flags_1 = CONDUCT_1
slot_flags = ITEM_SLOT_BACK
force = 5
- force_unwielded = 5
- force_wielded = 25
throwforce = 30
throw_range = 7
w_class = WEIGHT_CLASS_HUGE
total_mass = TOTAL_MASS_MEDIEVAL_WEAPON
+ var/wielded = FALSE // track wielded status on item
-/obj/item/twohanded/mjollnir/proc/shock(mob/living/target)
+/obj/item/mjollnir/Initialize()
+ . = ..()
+ RegisterSignal(src, COMSIG_TWOHANDED_WIELD, .proc/on_wield)
+ RegisterSignal(src, COMSIG_TWOHANDED_UNWIELD, .proc/on_unwield)
+
+/obj/item/mjollnir/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/two_handed, force_multiplier=5, icon_wielded="mjollnir1", attacksound="sparks")
+
+/// triggered on wield of two handed item
+/obj/item/mjollnir/proc/on_wield(obj/item/source, mob/user)
+ wielded = TRUE
+
+/// triggered on unwield of two handed item
+/obj/item/mjollnir/proc/on_unwield(obj/item/source, mob/user)
+ wielded = FALSE
+
+/obj/item/mjollnir/update_icon_state()
+ icon_state = "mjollnir0"
+
+/obj/item/mjollnir/proc/shock(mob/living/target)
target.Stun(60)
var/datum/effect_system/lightning_spread/s = new /datum/effect_system/lightning_spread
s.set_up(5, 1, target.loc)
@@ -99,17 +129,12 @@
target.throw_at(throw_target, 200, 4)
return
-/obj/item/twohanded/mjollnir/attack(mob/living/M, mob/user)
+/obj/item/mjollnir/attack(mob/living/M, mob/user)
..()
if(wielded)
- playsound(src.loc, "sparks", 50, 1)
shock(M)
-/obj/item/twohanded/mjollnir/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
+/obj/item/mjollnir/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
. = ..()
if(isliving(hit_atom))
shock(hit_atom)
-
-/obj/item/twohanded/mjollnir/update_icon_state() //Currently only here to fuck with the on-mob icons.
- icon_state = "mjollnir[wielded]"
- return
diff --git a/code/game/objects/items/spear.dm b/code/game/objects/items/spear.dm
new file mode 100644
index 0000000000..376362d7c3
--- /dev/null
+++ b/code/game/objects/items/spear.dm
@@ -0,0 +1,185 @@
+//spears
+/obj/item/spear
+ icon_state = "spearglass0"
+ lefthand_file = 'icons/mob/inhands/weapons/polearms_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/weapons/polearms_righthand.dmi'
+ name = "spear"
+ desc = "A haphazardly-constructed yet still deadly weapon of ancient design."
+ force = 10
+ w_class = WEIGHT_CLASS_BULKY
+ slot_flags = ITEM_SLOT_BACK
+ throwforce = 20
+ throw_speed = 4
+ embedding = list("impact_pain_mult" = 3)
+ armour_penetration = 10
+ custom_materials = list(/datum/material/iron=1150, /datum/material/glass=2075)
+ hitsound = 'sound/weapons/bladeslice.ogg'
+ attack_verb = list("attacked", "poked", "jabbed", "torn", "gored")
+ sharpness = IS_SHARP
+ max_integrity = 200
+ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 30)
+ var/obj/item/grenade/explosive = null
+ var/war_cry = "AAAAARGH!!!"
+ var/icon_prefix = "spearglass"
+ var/wielded = FALSE // track wielded status on item
+
+/obj/item/spear/Initialize()
+ . = ..()
+ RegisterSignal(src, COMSIG_TWOHANDED_WIELD, .proc/on_wield)
+ RegisterSignal(src, COMSIG_TWOHANDED_UNWIELD, .proc/on_unwield)
+
+/obj/item/spear/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/butchering, 100, 70) //decent in a pinch, but pretty bad.
+ AddComponent(/datum/component/jousting)
+ AddElement(/datum/element/sword_point)
+ AddComponent(/datum/component/two_handed, force_unwielded=10, force_wielded=18, icon_wielded="[icon_prefix]1")
+
+/// triggered on wield of two handed item
+/obj/item/spear/proc/on_wield(obj/item/source, mob/user)
+ wielded = TRUE
+
+/// triggered on unwield of two handed item
+/obj/item/spear/proc/on_unwield(obj/item/source, mob/user)
+ wielded = FALSE
+
+/obj/item/spear/rightclick_attack_self(mob/user)
+ if(explosive)
+ explosive.attack_self(user)
+ return
+ . = ..()
+
+/obj/item/spear/update_icon_state()
+ icon_state = "[icon_prefix]0"
+
+/obj/item/spear/update_overlays()
+ . = ..()
+ if(explosive)
+ . += "spearbomb_overlay"
+
+/obj/item/spear/suicide_act(mob/living/carbon/user)
+ user.visible_message("[user] begins to sword-swallow \the [src]! It looks like [user.p_theyre()] trying to commit suicide!")
+ if(explosive) //Citadel Edit removes qdel and explosive.forcemove(AM)
+ user.say("[war_cry]", forced="spear warcry")
+ explosive.prime()
+ user.gib()
+ return BRUTELOSS
+ return BRUTELOSS
+
+/obj/item/spear/examine(mob/user)
+ . = ..()
+ if(explosive)
+ . += "Alt-click to set your war cry."
+ . += "Right-click in combat mode to activate the attached explosive."
+
+/obj/item/spear/afterattack(atom/movable/AM, mob/user, proximity)
+ . = ..()
+ if(!proximity)
+ return
+ if(isopenturf(AM)) //So you can actually melee with it
+ return
+ if(explosive && wielded) //Citadel edit removes qdel and explosive.forcemove(AM)
+ user.say("[war_cry]", forced="spear warcry")
+ explosive.prime()
+
+/obj/item/spear/grenade_prime_react(obj/item/grenade/nade) //Citadel edit, removes throw_impact because memes
+ nade.forceMove(get_turf(src))
+ qdel(src)
+
+/obj/item/spear/AltClick(mob/user)
+ . = ..()
+ if(user.canUseTopic(src, BE_CLOSE))
+ ..()
+ if(!explosive)
+ return
+ if(istype(user) && loc == user)
+ var/input = stripped_input(user,"What do you want your war cry to be? You will shout it when you hit someone in melee.", ,"", 50)
+ if(input)
+ src.war_cry = input
+ return TRUE
+
+/obj/item/spear/CheckParts(list/parts_list)
+ var/obj/item/shard/tip = locate() in parts_list
+ if (istype(tip, /obj/item/shard/plasma))
+ throwforce = 21
+ embedding = list(embed_chance = 75, pain_mult = 1.5) //plasmaglass spears are sharper
+ updateEmbedding()
+ icon_prefix = "spearplasma"
+ AddComponent(/datum/component/two_handed, force_unwielded=11, force_wielded=19, icon_wielded="[icon_prefix]1")
+ qdel(tip)
+ var/obj/item/spear/S = locate() in parts_list
+ if(S)
+ if(S.explosive)
+ S.explosive.forceMove(get_turf(src))
+ S.explosive = null
+ parts_list -= S
+ qdel(S)
+ ..()
+ var/obj/item/grenade/G = locate() in contents
+ if(G)
+ explosive = G
+ name = "explosive lance"
+ embedding = list(embed_chance = 0, pain_mult = 1)//elances should not be embeddable
+ updateEmbedding()
+ desc = "A makeshift spear with \a [G] attached to it."
+ update_icon()
+
+//GREY TIDE
+/obj/item/spear/grey_tide
+ icon_state = "spearglass0"
+ name = "\improper Grey Tide"
+ desc = "Recovered from the aftermath of a revolt aboard Defense Outpost Theta Aegis, in which a seemingly endless tide of Assistants caused heavy casualities among Nanotrasen military forces."
+ throwforce = 20
+ throw_speed = 4
+ attack_verb = list("gored")
+ var/clonechance = 50
+ var/clonedamage = 12
+ var/clonespeed = 0
+ var/clone_replication_chance = 30
+ var/clone_lifespan = 100
+
+/obj/item/spear/grey_tide/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/two_handed, force_unwielded=15, force_wielded=25, icon_wielded="[icon_prefix]1")
+
+/obj/item/spear/grey_tide/afterattack(atom/movable/AM, mob/living/user, proximity)
+ . = ..()
+ if(!proximity)
+ return
+ user.faction |= "greytide([REF(user)])"
+ if(isliving(AM))
+ var/mob/living/L = AM
+ if(istype (L, /mob/living/simple_animal/hostile/illusion))
+ return
+ if(!L.stat && prob(clonechance))
+ var/mob/living/simple_animal/hostile/illusion/M = new(user.loc)
+ M.faction = user.faction.Copy()
+ M.set_varspeed(clonespeed)
+ M.Copy_Parent(user, clone_lifespan, user.health/2.5, clonedamage, clone_replication_chance)
+ M.GiveTarget(L)
+
+/*
+ * Bone Spear
+ */
+/obj/item/spear/bonespear //Blatant imitation of spear, but made out of bone. Not valid for explosive modification.
+ icon_state = "bone_spear0"
+ lefthand_file = 'icons/mob/inhands/weapons/polearms_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/weapons/polearms_righthand.dmi'
+ name = "bone spear"
+ desc = "A haphazardly-constructed yet still deadly weapon. The pinnacle of modern technology."
+ force = 11
+ w_class = WEIGHT_CLASS_BULKY
+ slot_flags = ITEM_SLOT_BACK
+ reach = 2
+ throwforce = 22
+ embedding = list("embedded_impact_pain_multiplier" = 3)
+ armour_penetration = 15 //Enhanced armor piercing
+ custom_materials = null
+ hitsound = 'sound/weapons/bladeslice.ogg'
+ attack_verb = list("attacked", "poked", "jabbed", "torn", "gored")
+ sharpness = IS_SHARP
+ icon_prefix = "bone_spear"
+
+/obj/item/spear/bonespear/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/two_handed, force_unwielded=11, force_wielded=20, icon_wielded="[icon_prefix]1")
diff --git a/code/game/objects/items/stacks/bscrystal.dm b/code/game/objects/items/stacks/bscrystal.dm
index 00e48fd12a..c142db6530 100644
--- a/code/game/objects/items/stacks/bscrystal.dm
+++ b/code/game/objects/items/stacks/bscrystal.dm
@@ -75,7 +75,7 @@
to_chat(user, "You cannot crush the polycrystal in-hand, try breaking one off.")
//ATTACK HAND IGNORING PARENT RETURN VALUE
-/obj/item/stack/sheet/bluespace_crystal/attack_hand(mob/user)
+/obj/item/stack/sheet/bluespace_crystal/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(user.get_inactive_held_item() == src)
if(zero_amount())
return
diff --git a/code/game/objects/items/stacks/cash.dm b/code/game/objects/items/stacks/cash.dm
index ce0bc6591a..954950f5e6 100644
--- a/code/game/objects/items/stacks/cash.dm
+++ b/code/game/objects/items/stacks/cash.dm
@@ -11,6 +11,7 @@
w_class = WEIGHT_CLASS_TINY
full_w_class = WEIGHT_CLASS_TINY
resistance_flags = FLAMMABLE
+ grind_results = list(/datum/reagent/cellulose = 10)
var/value = 0
/obj/item/stack/spacecash/Initialize()
diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm
index e6403e83cc..e4b67d4da5 100644
--- a/code/game/objects/items/stacks/medical.dm
+++ b/code/game/objects/items/stacks/medical.dm
@@ -207,6 +207,16 @@
/obj/item/stack/medical/suture/one
amount = 1
+/obj/item/stack/medical/suture/medicated
+ name = "medicated suture"
+ icon_state = "suture_purp"
+ desc = "A suture infused with drugs that speed up wound healing of the treated laceration."
+ heal_brute = 15
+ grind_results = list(/datum/reagent/medicine/polypyr = 2)
+
+/obj/item/stack/medical/suture/one
+ amount = 1
+
/obj/item/stack/medical/suture/heal(mob/living/M, mob/user)
. = ..()
if(M.stat == DEAD)
@@ -246,12 +256,30 @@
/obj/item/stack/medical/mesh/one
amount = 1
+/obj/item/stack/medical/mesh/advanced
+ name = "advanced regenerative mesh"
+ desc = "An advanced mesh made with aloe extracts and sterilizing chemicals, used to treat burns."
+ gender = PLURAL
+ singular_name = "advanced regenerative mesh"
+ icon_state = "aloe_mesh"
+ heal_burn = 15
+ grind_results = list(/datum/reagent/consumable/aloejuice = 1)
+
+/obj/item/stack/medical/mesh/advanced/one
+ amount = 1
+
/obj/item/stack/medical/mesh/Initialize()
. = ..()
if(amount == max_amount) //only seal full mesh packs
is_open = FALSE
update_icon()
+/obj/item/stack/medical/mesh/advanced/update_icon_state()
+ if(!is_open)
+ icon_state = "aloe_mesh_closed"
+ else
+ return ..()
+
/obj/item/stack/medical/mesh/update_icon_state()
if(!is_open)
icon_state = "regen_mesh_closed"
@@ -280,7 +308,7 @@
return
. = ..()
-/obj/item/stack/medical/mesh/attack_hand(mob/user)
+/obj/item/stack/medical/mesh/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(!is_open & user.get_inactive_held_item() == src)
to_chat(user, "You need to open [src] first.")
return
@@ -294,3 +322,37 @@
playsound(src, 'sound/items/poster_ripped.ogg', 20, TRUE)
return
. = ..()
+
+/obj/item/stack/medical/aloe
+ name = "aloe cream"
+ desc = "A healing paste you can apply on wounds."
+
+ icon_state = "aloe_paste"
+ self_delay = 20
+ other_delay = 10
+ novariants = TRUE
+ amount = 20
+ max_amount = 20
+ var/heal = 3
+ grind_results = list(/datum/reagent/consumable/aloejuice = 1)
+
+/obj/item/stack/medical/aloe/heal(mob/living/M, mob/user)
+ . = ..()
+ if(M.stat == DEAD)
+ to_chat(user, "[M] is dead! You can not help [M.p_them()].")
+ return FALSE
+ if(iscarbon(M))
+ return heal_carbon(M, user, heal, heal)
+ if(isanimal(M))
+ var/mob/living/simple_animal/critter = M
+ if (!(critter.healable))
+ to_chat(user, "You cannot use \the [src] on [M]!")
+ return FALSE
+ else if (critter.health == critter.maxHealth)
+ to_chat(user, "[M] is at full health.")
+ return FALSE
+ user.visible_message("[user] applies \the [src] on [M].", "You apply \the [src] on [M].")
+ M.heal_bodypart_damage(heal, heal)
+ return TRUE
+
+ to_chat(user, "You can't heal [M] with the \the [src]!")
\ No newline at end of file
diff --git a/code/game/objects/items/stacks/rods.dm b/code/game/objects/items/stacks/rods.dm
index 6fdea26683..efcc075110 100644
--- a/code/game/objects/items/stacks/rods.dm
+++ b/code/game/objects/items/stacks/rods.dm
@@ -17,7 +17,6 @@ GLOBAL_LIST_INIT(rod_recipes, list ( \
throw_speed = 3
throw_range = 7
custom_materials = list(/datum/material/iron=1000)
- mats_per_stack = 1000
max_amount = 50
attack_verb = list("hit", "bludgeoned", "whacked")
hitsound = 'sound/weapons/grenadelaunch.ogg'
diff --git a/code/game/objects/items/stacks/sheets/glass.dm b/code/game/objects/items/stacks/sheets/glass.dm
index 57af862b69..d4baea2487 100644
--- a/code/game/objects/items/stacks/sheets/glass.dm
+++ b/code/game/objects/items/stacks/sheets/glass.dm
@@ -69,7 +69,7 @@ GLOBAL_LIST_INIT(glass_recipes, list ( \
if (get_amount() < 1 || CC.get_amount() < 5)
to_chat(user, "You attach wire to the [name].")
var/obj/item/stack/light_w/new_tile = new(user.loc)
diff --git a/code/game/objects/items/stacks/sheets/mineral.dm b/code/game/objects/items/stacks/sheets/mineral.dm
index d28ae52b52..7692278ba3 100644
--- a/code/game/objects/items/stacks/sheets/mineral.dm
+++ b/code/game/objects/items/stacks/sheets/mineral.dm
@@ -39,9 +39,11 @@ GLOBAL_LIST_INIT(sandstone_recipes, list ( \
item_state = "sheet-sandstone"
throw_speed = 3
throw_range = 5
- custom_materials = list(/datum/material/glass=MINERAL_MATERIAL_AMOUNT)
+ custom_materials = list(/datum/material/sandstone=MINERAL_MATERIAL_AMOUNT)
sheettype = "sandstone"
merge_type = /obj/item/stack/sheet/mineral/sandstone
+ walltype = /turf/closed/wall/mineral/sandstone
+ material_type = /datum/material/sandstone
/obj/item/stack/sheet/mineral/sandstone/get_main_recipes()
. = ..()
@@ -107,6 +109,7 @@ GLOBAL_LIST_INIT(sandbag_recipes, list ( \
point_value = 25
merge_type = /obj/item/stack/sheet/mineral/diamond
material_type = /datum/material/diamond
+ walltype = /turf/closed/wall/mineral/diamond
GLOBAL_LIST_INIT(diamond_recipes, list ( \
new/datum/stack_recipe("diamond door", /obj/structure/mineral_door/transparent/diamond, 10, one_per_turf = 1, on_floor = 1), \
@@ -135,6 +138,7 @@ GLOBAL_LIST_INIT(diamond_recipes, list ( \
point_value = 20
merge_type = /obj/item/stack/sheet/mineral/uranium
material_type = /datum/material/uranium
+ walltype = /turf/closed/wall/mineral/uranium
GLOBAL_LIST_INIT(uranium_recipes, list ( \
new/datum/stack_recipe("uranium door", /obj/structure/mineral_door/uranium, 10, one_per_turf = 1, on_floor = 1), \
@@ -163,6 +167,7 @@ GLOBAL_LIST_INIT(uranium_recipes, list ( \
point_value = 20
merge_type = /obj/item/stack/sheet/mineral/plasma
material_type = /datum/material/plasma
+ walltype = /turf/closed/wall/mineral/plasma
/obj/item/stack/sheet/mineral/plasma/suicide_act(mob/living/carbon/user)
user.visible_message("[user] begins licking \the [src]! It looks like [user.p_theyre()] trying to commit suicide!")
@@ -205,6 +210,7 @@ GLOBAL_LIST_INIT(plasma_recipes, list ( \
point_value = 20
merge_type = /obj/item/stack/sheet/mineral/gold
material_type = /datum/material/gold
+ walltype = /turf/closed/wall/mineral/gold
GLOBAL_LIST_INIT(gold_recipes, list ( \
new/datum/stack_recipe("golden door", /obj/structure/mineral_door/gold, 10, one_per_turf = 1, on_floor = 1), \
@@ -236,6 +242,7 @@ GLOBAL_LIST_INIT(gold_recipes, list ( \
merge_type = /obj/item/stack/sheet/mineral/silver
material_type = /datum/material/silver
tableVariant = /obj/structure/table/optable
+ walltype = /turf/closed/wall/mineral/silver
GLOBAL_LIST_INIT(silver_recipes, list ( \
new/datum/stack_recipe("silver door", /obj/structure/mineral_door/silver, 10, one_per_turf = 1, on_floor = 1), \
@@ -266,6 +273,7 @@ GLOBAL_LIST_INIT(silver_recipes, list ( \
point_value = 50
merge_type = /obj/item/stack/sheet/mineral/bananium
material_type = /datum/material/bananium
+ walltype = /turf/closed/wall/mineral/bananium
GLOBAL_LIST_INIT(bananium_recipes, list ( \
new/datum/stack_recipe("bananium tile", /obj/item/stack/tile/mineral/bananium, 1, 4, 20), \
@@ -294,6 +302,7 @@ GLOBAL_LIST_INIT(bananium_recipes, list ( \
point_value = 20
merge_type = /obj/item/stack/sheet/mineral/titanium
material_type = /datum/material/titanium
+ walltype = /turf/closed/wall/mineral/titanium
GLOBAL_LIST_INIT(titanium_recipes, list ( \
new/datum/stack_recipe("titanium tile", /obj/item/stack/tile/mineral/titanium, 1, 4, 20), \
@@ -324,6 +333,7 @@ GLOBAL_LIST_INIT(titanium_recipes, list ( \
custom_materials = list(/datum/material/titanium=MINERAL_MATERIAL_AMOUNT, /datum/material/plasma=MINERAL_MATERIAL_AMOUNT)
point_value = 45
merge_type = /obj/item/stack/sheet/mineral/plastitanium
+ walltype = /turf/closed/wall/mineral/plastitanium
/obj/item/stack/sheet/mineral/plastitanium/fifty
amount = 50
@@ -390,11 +400,14 @@ GLOBAL_LIST_INIT(adamantine_recipes, list(
name = "snow"
icon_state = "sheet-snow"
item_state = "sheet-snow"
+ custom_materials = list(/datum/material/snow = MINERAL_MATERIAL_AMOUNT)
singular_name = "snow block"
force = 1
throwforce = 2
grind_results = list(/datum/reagent/consumable/ice = 20)
merge_type = /obj/item/stack/sheet/mineral/snow
+ walltype = /turf/closed/wall/mineral/snow
+ material_type = /datum/material/snow
GLOBAL_LIST_INIT(snow_recipes, list ( \
new/datum/stack_recipe("Snow Wall", /turf/closed/wall/mineral/snow, 5, one_per_turf = 1, on_floor = 1), \
@@ -417,6 +430,7 @@ GLOBAL_LIST_INIT(snow_recipes, list ( \
singular_name = "alien alloy sheet"
sheettype = "abductor"
merge_type = /obj/item/stack/sheet/mineral/abductor
+ walltype = /turf/closed/wall/mineral/abductor
GLOBAL_LIST_INIT(abductor_recipes, list ( \
new/datum/stack_recipe("alien bed", /obj/structure/bed/abductor, 2, one_per_turf = 1, on_floor = 1), \
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index c11b619431..0383c3f1ee 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -205,7 +205,7 @@ GLOBAL_LIST_INIT(plasteel_recipes, list ( \
desc = "This sheet is an alloy of iron and plasma."
icon_state = "sheet-plasteel"
item_state = "sheet-metal"
- custom_materials = list(/datum/material/iron=2000, /datum/material/plasma=2000)
+ custom_materials = list(/datum/material/iron=MINERAL_MATERIAL_AMOUNT, /datum/material/plasma=MINERAL_MATERIAL_AMOUNT)
throwforce = 10
flags_1 = CONDUCT_1
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 80)
@@ -240,11 +240,11 @@ GLOBAL_LIST_INIT(wood_recipes, list ( \
new /datum/stack_recipe("pew (right)", /obj/structure/chair/pew/right, 3, one_per_turf = TRUE, on_floor = TRUE),\
)),
null, \
- new/datum/stack_recipe("wooden firearm body", /obj/item/weaponcrafting/improvised_parts/wooden_body, 10, time = 40), \
- new/datum/stack_recipe("rifle stock", /obj/item/weaponcrafting/stock, 10, time = 40), \
- new/datum/stack_recipe("pistol grip", /obj/item/weaponcrafting/improvised_parts/wooden_grip, 5, time = 40), \
+ new/datum/stack_recipe("wooden firearm body", /obj/item/weaponcrafting/improvised_parts/wooden_body, 10, time = 20), \
+ new/datum/stack_recipe("rifle stock", /obj/item/weaponcrafting/stock, 10, time = 20), \
new/datum/stack_recipe("rolling pin", /obj/item/kitchen/rollingpin, 2, time = 30), \
new/datum/stack_recipe("wooden bucket", /obj/item/reagent_containers/glass/bucket/wood, 2, time = 30), \
+ new/datum/stack_recipe("painting frame", /obj/item/wallframe/painting, 1, time = 10),\
new/datum/stack_recipe("wooden buckler", /obj/item/shield/riot/buckler, 20, time = 40), \
new/datum/stack_recipe("baseball bat", /obj/item/melee/baseball_bat, 5, time = 15),\
null, \
@@ -288,7 +288,8 @@ GLOBAL_LIST_INIT(wood_recipes, list ( \
merge_type = /obj/item/stack/sheet/mineral/wood
novariants = TRUE
material_type = /datum/material/wood
- grind_results = list(/datum/reagent/carbon = 20)
+ grind_results = list(/datum/reagent/cellulose = 20)
+ walltype = /turf/closed/wall/mineral/wood
/obj/item/stack/sheet/mineral/wood/attackby(obj/item/W, mob/user, params) // NOTE: sheet_types.dm is where the WOOD stack lives. Maybe move this over there.
// Taken from /obj/item/stack/rods/attackby in [rods.dm]
@@ -344,11 +345,13 @@ GLOBAL_LIST_INIT(bamboo_recipes, list ( \
icon_state = "sheet-bamboo"
item_state = "sheet-bamboo"
icon = 'icons/obj/stack_objects.dmi'
+ custom_materials = list(/datum/material/bamboo = MINERAL_MATERIAL_AMOUNT)
throwforce = 15
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 0)
resistance_flags = FLAMMABLE
merge_type = /obj/item/stack/sheet/mineral/bamboo
- grind_results = list(/datum/reagent/carbon = 5)
+ grind_results = list(/datum/reagent/cellulose = 10)
+ material_type = /datum/material/bamboo
/obj/item/stack/sheet/mineral/bamboo/get_main_recipes()
. = ..()
@@ -379,6 +382,7 @@ GLOBAL_LIST_INIT(cloth_recipes, list ( \
new/datum/stack_recipe("chemistry bag", /obj/item/storage/bag/chemistry, 4), \
new/datum/stack_recipe("bio bag", /obj/item/storage/bag/bio, 4), \
null, \
+ new/datum/stack_recipe("string", /obj/item/weaponcrafting/string, 1, time = 10), \
new/datum/stack_recipe("improvised gauze", /obj/item/stack/medical/gauze/improvised, 1, 2, 6), \
new/datum/stack_recipe("rag", /obj/item/reagent_containers/rag, 1), \
new/datum/stack_recipe("towel", /obj/item/reagent_containers/rag/towel, 3), \
@@ -426,7 +430,6 @@ GLOBAL_LIST_INIT(durathread_recipes, list ( \
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), \
new/datum/stack_recipe("durathread bandana", /obj/item/clothing/mask/bandana/durathread, 1, time = 25), \
- new/datum/stack_recipe("durathread string", /obj/item/weaponcrafting/durathread_string, 1, time = 40), \
))
/obj/item/stack/sheet/durathread
@@ -513,12 +516,14 @@ GLOBAL_LIST_INIT(cardboard_recipes, list ( \
desc = "Large sheets of card, like boxes folded flat."
singular_name = "cardboard sheet"
icon_state = "sheet-card"
+ custom_materials = list(/datum/material/cardboard = MINERAL_MATERIAL_AMOUNT)
item_state = "sheet-card"
resistance_flags = FLAMMABLE
force = 0
throwforce = 0
merge_type = /obj/item/stack/sheet/cardboard
novariants = TRUE
+ material_type = /datum/material/cardboard
/obj/item/stack/sheet/cardboard/get_main_recipes()
. = ..()
@@ -558,10 +563,12 @@ GLOBAL_LIST_INIT(runed_metal_recipes, list ( \
icon_state = "sheet-runed"
item_state = "sheet-runed"
icon = 'icons/obj/stack_objects.dmi'
+ custom_materials = list(/datum/material/runedmetal = MINERAL_MATERIAL_AMOUNT)
sheettype = "runed"
merge_type = /obj/item/stack/sheet/runed_metal
novariants = TRUE
grind_results = list(/datum/reagent/iron = 5, /datum/reagent/blood = 15)
+ material_type = /datum/material/runedmetal
/obj/item/stack/sheet/runed_metal/ratvar_act()
new /obj/item/stack/tile/brass(loc, amount)
@@ -680,6 +687,7 @@ GLOBAL_LIST_INIT(bronze_recipes, list ( \
icon_state = "sheet-brass"
item_state = "sheet-brass"
icon = 'icons/obj/stack_objects.dmi'
+ custom_materials = list(/datum/material/bronze = MINERAL_MATERIAL_AMOUNT)
resistance_flags = FIRE_PROOF | ACID_PROOF
throwforce = 10
max_amount = 50
@@ -690,6 +698,7 @@ GLOBAL_LIST_INIT(bronze_recipes, list ( \
grind_results = list(/datum/reagent/iron = 5, /datum/reagent/copper = 3) //we have no "tin" reagent so this is the closest thing
merge_type = /obj/item/stack/tile/bronze
tableVariant = /obj/structure/table/bronze
+ material_type = /datum/material/bronze
/obj/item/stack/tile/bronze/attack_self(mob/living/user)
if(is_servant_of_ratvar(user)) //still lets them build with it, just gives a message
@@ -737,6 +746,7 @@ GLOBAL_LIST_INIT(bone_recipes, list(
icon = 'icons/obj/mining.dmi'
icon_state = "bone"
item_state = "sheet-bone"
+ custom_materials = list(/datum/material/bone = MINERAL_MATERIAL_AMOUNT)
singular_name = "bone"
desc = "Someone's been drinking their milk."
force = 7
@@ -747,6 +757,7 @@ GLOBAL_LIST_INIT(bone_recipes, list(
throw_range = 3
grind_results = list(/datum/reagent/carbon = 10)
merge_type = /obj/item/stack/sheet/bone
+ material_type = /datum/material/bone
/obj/item/stack/sheet/bone/get_main_recipes()
. = ..()
@@ -774,6 +785,7 @@ GLOBAL_LIST_INIT(plastic_recipes, list(
custom_materials = list(/datum/material/plastic=MINERAL_MATERIAL_AMOUNT)
throwforce = 7
grind_results = list(/datum/reagent/glitter/white = 60)
+ material_type = /datum/material/plastic
merge_type = /obj/item/stack/sheet/plastic
/obj/item/stack/sheet/plastic/fifty
@@ -799,9 +811,11 @@ new /datum/stack_recipe("paper frame door", /obj/structure/mineral_door/paperfra
singular_name = "paper frame"
icon_state = "sheet-paper"
item_state = "sheet-paper"
+ custom_materials = list(/datum/material/paper = MINERAL_MATERIAL_AMOUNT)
merge_type = /obj/item/stack/sheet/paperframes
resistance_flags = FLAMMABLE
merge_type = /obj/item/stack/sheet/paperframes
+ material_type = /datum/material/paper
/obj/item/stack/sheet/paperframes/get_main_recipes()
. = ..()
@@ -842,3 +856,55 @@ new /datum/stack_recipe("paper frame door", /obj/structure/mineral_door/paperfra
merge_type = /obj/item/stack/sheet/cotton/durathread
pull_effort = 70
loom_result = /obj/item/stack/sheet/durathread
+
+/obj/item/stack/sheet/meat
+ name = "meat sheets"
+ desc = "Something's bloody meat compressed into a nice solid sheet"
+ singular_name = "meat sheet"
+ icon_state = "sheet-meat"
+ material_flags = MATERIAL_COLOR
+ custom_materials = list(/datum/material/meat = MINERAL_MATERIAL_AMOUNT)
+ merge_type = /obj/item/stack/sheet/meat
+ material_type = /datum/material/meat
+ material_modifier = 1 //None of that wussy stuff
+
+/obj/item/stack/sheet/meat/fifty
+ amount = 50
+/obj/item/stack/sheet/meat/twenty
+ amount = 20
+/obj/item/stack/sheet/meat/five
+ amount = 5
+
+/obj/item/stack/sheet/pizza
+ name = "pepperoni sheetzzas"
+ desc = "It's a delicious pepperoni sheetzza!"
+ singular_name = "pepperoni sheetzza"
+ icon_state = "sheet-pizza"
+ custom_materials = list(/datum/material/pizza = MINERAL_MATERIAL_AMOUNT)
+ merge_type = /obj/item/stack/sheet/pizza
+ material_type = /datum/material/pizza
+ material_modifier = 1
+
+/obj/item/stack/sheet/pizza/fifty
+ amount = 50
+/obj/item/stack/sheet/pizza/twenty
+ amount = 20
+/obj/item/stack/sheet/pizza/five
+ amount = 5
+
+/obj/item/stack/sheet/sandblock
+ name = "blocks of sand"
+ desc = "You're too old to be playing with sandcastles. Now you build... sandstations."
+ singular_name = "block of sand"
+ icon_state = "sheet-sandstone"
+ custom_materials = list(/datum/material/sand = MINERAL_MATERIAL_AMOUNT)
+ merge_type = /obj/item/stack/sheet/sandblock
+ material_type = /datum/material/sand
+ material_modifier = 1
+
+/obj/item/stack/sheet/sandblock/fifty
+ amount = 50
+/obj/item/stack/sheet/sandblock/twenty
+ amount = 20
+/obj/item/stack/sheet/sandblock/five
+ amount = 5
diff --git a/code/game/objects/items/stacks/sheets/sheets.dm b/code/game/objects/items/stacks/sheets/sheets.dm
index dfba533247..57c8ba75d8 100644
--- a/code/game/objects/items/stacks/sheets/sheets.dm
+++ b/code/game/objects/items/stacks/sheets/sheets.dm
@@ -10,10 +10,14 @@
throw_range = 3
attack_verb = list("bashed", "battered", "bludgeoned", "thrashed", "smashed")
novariants = FALSE
- mats_per_stack = MINERAL_MATERIAL_AMOUNT
- var/sheettype = null //this is used for girders in the creation of walls/false walls
- var/point_value = 0 //turn-in value for the gulag stacker - loosely relative to its rarity
- var/shard_type // the shard debris typepath left over by solar panels and windows etc.
+ ///this is used for girders in the creation of walls/false walls
+ var/sheettype = null
+ ///turn-in value for the gulag stacker - loosely relative to its rarity
+ var/point_value = 0
+ /// the shard debris typepath left over by solar panels and windows etc.
+ var/shard_type
+ ///What type of wall does this sheet spawn
+ var/walltype
/obj/item/stack/sheet/Initialize(mapload, new_amount, merge)
. = ..()
diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm
index 2c8e700316..df9807b524 100644
--- a/code/game/objects/items/stacks/stack.dm
+++ b/code/game/objects/items/stacks/stack.dm
@@ -22,7 +22,7 @@
var/merge_type = null // This path and its children should merge with this stack, defaults to src.type
var/full_w_class = WEIGHT_CLASS_NORMAL //The weight class the stack should have at amount > 2/3rds max_amount
var/novariants = TRUE //Determines whether the item should update it's sprites based on amount.
- var/mats_per_stack = 0
+ var/list/mats_per_unit //list that tells you how much is in a single unit.
///Datum material type that this stack is made of
var/material_type
//NOTE: When adding grind_results, the amounts should be for an INDIVIDUAL ITEM - these amounts will be multiplied by the stack size in on_grind()
@@ -47,8 +47,11 @@
if(!merge_type)
merge_type = type
if(custom_materials && custom_materials.len)
+ mats_per_unit = list()
+ var/in_process_mat_list = custom_materials.Copy()
for(var/i in custom_materials)
- custom_materials[SSmaterials.GetMaterialRef(i)] = mats_per_stack * amount
+ mats_per_unit[SSmaterials.GetMaterialRef(i)] = in_process_mat_list[i]
+ custom_materials[i] *= amount
. = ..()
if(merge)
for(var/obj/item/stack/S in loc)
@@ -60,7 +63,7 @@
var/datum/material/M = SSmaterials.GetMaterialRef(material_type) //First/main material
for(var/i in M.categories)
switch(i)
- if(MAT_CATEGORY_RIGID)
+ if(MAT_CATEGORY_BASE_RECIPES)
var/list/temp = SSmaterials.rigid_stack_recipes.Copy()
recipes += temp
update_weight()
@@ -315,10 +318,13 @@
if (amount < used)
return FALSE
amount -= used
- if(check)
- zero_amount()
- for(var/i in custom_materials)
- custom_materials[i] = amount * mats_per_stack
+ if(check && zero_amount())
+ return TRUE
+ if(length(mats_per_unit))
+ var/temp_materials = custom_materials.Copy()
+ for(var/i in mats_per_unit)
+ temp_materials[i] = mats_per_unit[i] * src.amount
+ set_custom_materials(temp_materials)
update_icon()
update_weight()
return TRUE
@@ -350,10 +356,11 @@
source.add_charge(amount * cost)
else
src.amount += amount
- if(custom_materials && custom_materials.len)
- for(var/i in custom_materials)
- custom_materials[SSmaterials.GetMaterialRef(i)] = MINERAL_MATERIAL_AMOUNT * src.amount
- set_custom_materials() //Refresh
+ if(length(mats_per_unit))
+ var/temp_materials = custom_materials.Copy()
+ for(var/i in mats_per_unit)
+ temp_materials[i] = mats_per_unit[i] * src.amount
+ set_custom_materials(temp_materials)
update_icon()
update_weight()
@@ -383,7 +390,7 @@
. = ..()
//ATTACK HAND IGNORING PARENT RETURN VALUE
-/obj/item/stack/attack_hand(mob/user)
+/obj/item/stack/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(user.get_inactive_held_item() == src)
if(zero_amount())
return
diff --git a/code/game/objects/items/stacks/tape.dm b/code/game/objects/items/stacks/tape.dm
index 177260febb..b66d053807 100644
--- a/code/game/objects/items/stacks/tape.dm
+++ b/code/game/objects/items/stacks/tape.dm
@@ -11,10 +11,14 @@
amount = 5
max_amount = 5
resistance_flags = FLAMMABLE
+ grind_results = list(/datum/reagent/cellulose = 5)
var/list/conferred_embed = EMBED_HARMLESS
var/overwrite_existing = FALSE
+ var/endless = FALSE
+ var/apply_time = 30
+
/obj/item/stack/sticky_tape/afterattack(obj/item/I, mob/living/user)
if(!istype(I))
return
@@ -25,17 +29,24 @@
user.visible_message("[user] begins wrapping [I] with [src].", "You begin wrapping [I] with [src].")
- if(do_after(user, 30, target=I))
+ if(do_after(user, apply_time, target=I))
I.embedding = conferred_embed
I.updateEmbedding()
to_chat(user, "You finish wrapping [I] with [src].")
- use(1)
+ if(!endless)
+ use(1)
I.name = "[prefix] [I.name]"
if(istype(I, /obj/item/grenade))
var/obj/item/grenade/sticky_bomb = I
sticky_bomb.sticky = TRUE
+/obj/item/stack/sticky_tape/infinite //endless tape that applies far faster, for maximum honks
+ name = "endless sticky tape"
+ desc = "This roll of sticky tape somehow has no end."
+ endless = TRUE
+ apply_time = 10
+
/obj/item/stack/sticky_tape/super
name = "super sticky tape"
singular_name = "super sticky tape"
diff --git a/code/game/objects/items/stacks/telecrystal.dm b/code/game/objects/items/stacks/telecrystal.dm
index 9b5ca2b066..54824940c1 100644
--- a/code/game/objects/items/stacks/telecrystal.dm
+++ b/code/game/objects/items/stacks/telecrystal.dm
@@ -4,6 +4,7 @@
singular_name = "telecrystal"
icon = 'icons/obj/telescience.dmi'
icon_state = "telecrystal"
+ grind_results = list(/datum/reagent/telecrystal = 20)
w_class = WEIGHT_CLASS_TINY
max_amount = 50
item_flags = NOBLUDGEON
diff --git a/code/game/objects/items/stacks/tickets.dm b/code/game/objects/items/stacks/tickets.dm
new file mode 100644
index 0000000000..22cb895277
--- /dev/null
+++ b/code/game/objects/items/stacks/tickets.dm
@@ -0,0 +1,31 @@
+/obj/item/stack/arcadeticket
+ name = "arcade tickets"
+ desc = "Wow! With enough of these, you could buy a bike! ...Pssh, yeah right."
+ singular_name = "arcade ticket"
+ icon_state = "arcade-ticket"
+ item_state = "tickets"
+ w_class = WEIGHT_CLASS_TINY
+ max_amount = 30
+
+/obj/item/stack/arcadeticket/Initialize(mapload, new_amount, merge = TRUE)
+ . = ..()
+ update_icon()
+
+/obj/item/stack/arcadeticket/update_icon()
+ var/amount = get_amount()
+ if((amount >= 12) && (amount > 0))
+ icon_state = "arcade-ticket_4"
+ else if((amount >= 6) && (amount > 0))
+ icon_state = "arcade-ticket_3"
+ else if((amount >= 2) && (amount > 0))
+ icon_state = "arcade-ticket_2"
+ else
+ icon_state = "arcade-ticket"
+
+/obj/item/stack/arcadeticket/proc/pay_tickets()
+ amount -= 2
+ if (amount == 0)
+ qdel(src)
+
+/obj/item/stack/arcadeticket/thirty
+ amount = 30
diff --git a/code/game/objects/items/stacks/tiles/tile_types.dm b/code/game/objects/items/stacks/tiles/tile_types.dm
index 0635c55ca3..13fca1e8fe 100644
--- a/code/game/objects/items/stacks/tiles/tile_types.dm
+++ b/code/game/objects/items/stacks/tiles/tile_types.dm
@@ -9,16 +9,35 @@
throw_speed = 3
throw_range = 7
max_amount = 60
- mats_per_stack = 500
var/turf_type = null
var/mineralType = null
novariants = TRUE
+ var/human_maxHealth = 100
/obj/item/stack/tile/Initialize(mapload, amount)
. = ..()
pixel_x = rand(-3, 3)
pixel_y = rand(-3, 3) //randomize a little
+/obj/item/stack/tile/examine(mob/user)
+ . = ..()
+ if(throwforce && !is_cyborg) //do not want to divide by zero or show the message to borgs who can't throw
+ var/verb
+ switch(CEILING(human_maxHealth / throwforce, 1)) //throws to crit a human
+ if(1 to 3)
+ verb = "superb"
+ if(4 to 6)
+ verb = "great"
+ if(7 to 9)
+ verb = "good"
+ if(10 to 12)
+ verb = "fairly decent"
+ if(13 to 15)
+ verb = "mediocre"
+ if(!verb)
+ return
+ . += "Those could work as a [verb] throwing weapon."
+
/obj/item/stack/tile/attackby(obj/item/W, mob/user, params)
if (istype(W, /obj/item/weldingtool))
@@ -470,7 +489,7 @@
/obj/item/stack/tile/plasteel
name = "floor tile"
singular_name = "floor tile"
- desc = "Those could work as a pretty decent throwing weapon."
+ desc = "The ground you walk on."
icon_state = "tile"
force = 6
custom_materials = list(/datum/material/iron=500)
@@ -482,7 +501,15 @@
resistance_flags = FIRE_PROOF
/obj/item/stack/tile/plasteel/cyborg
- desc = "The ground you walk on." //Not the usual floor tile desc as that refers to throwing, Cyborgs can't do that - RR
custom_materials = null // All other Borg versions of items have no Metal or Glass - RR
is_cyborg = 1
cost = 125
+
+/obj/item/stack/tile/material
+ name = "floor tile"
+ singular_name = "floor tile"
+ desc = "The ground you walk on."
+ throwforce = 10
+ icon_state = "material_tile"
+ turf_type = /turf/open/floor/material
+ material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
diff --git a/code/game/objects/items/stacks/wrap.dm b/code/game/objects/items/stacks/wrap.dm
index 10240e902b..6ae63e640f 100644
--- a/code/game/objects/items/stacks/wrap.dm
+++ b/code/game/objects/items/stacks/wrap.dm
@@ -35,6 +35,7 @@
amount = 25
max_amount = 25
resistance_flags = FLAMMABLE
+ grind_results = list(/datum/reagent/cellulose = 5)
/obj/item/stack/packageWrap/suicide_act(mob/living/user)
user.visible_message("[user] begins wrapping [user.p_them()]self in \the [src]! It looks like [user.p_theyre()] trying to commit suicide!")
diff --git a/code/game/objects/items/storage/backpack.dm b/code/game/objects/items/storage/backpack.dm
index 3d5f0dc924..99dbde2364 100644
--- a/code/game/objects/items/storage/backpack.dm
+++ b/code/game/objects/items/storage/backpack.dm
@@ -502,7 +502,6 @@
new /obj/item/retractor/advanced(src)
new /obj/item/surgicaldrill/advanced(src)
new /obj/item/surgical_drapes(src)
- new /obj/item/storage/firstaid/tactical(src)
new /obj/item/clothing/suit/straight_jacket(src)
new /obj/item/clothing/mask/muzzle(src)
new /obj/item/mmi/syndie(src)
@@ -648,3 +647,9 @@ obj/item/storage/backpack/duffelbag/syndie/shredderbundle
new /obj/item/gun/ballistic/automatic/flechette/shredder(src)
new /obj/item/storage/belt/military(src)
new /obj/item/clothing/suit/space/hardsuit/syndi/elite(src)
+
+/obj/item/storage/backpack/snail
+ name = "snail shell"
+ desc = "Worn by snails as armor and storage compartment."
+ icon_state = "snailshell"
+ item_state = "snailshell"
diff --git a/code/game/objects/items/storage/bags.dm b/code/game/objects/items/storage/bags.dm
index ee515d321f..b64aa60cac 100644
--- a/code/game/objects/items/storage/bags.dm
+++ b/code/game/objects/items/storage/bags.dm
@@ -48,7 +48,8 @@
STR.max_w_class = WEIGHT_CLASS_SMALL
STR.max_combined_w_class = 30
STR.max_items = 30
- STR.cant_hold = typecacheof(list(/obj/item/disk/nuclear))
+ STR.can_hold_extra = typecacheof(list(/obj/item/organ/lungs, /obj/item/organ/liver, /obj/item/organ/stomach, /obj/item/clothing/shoes)) - typesof(/obj/item/clothing/shoes/magboots, /obj/item/clothing/shoes/clown_shoes, /obj/item/clothing/shoes/jackboots, /obj/item/clothing/shoes/workboots)
+ STR.cant_hold = typecacheof(list(/obj/item/disk/nuclear, /obj/item/storage/wallet, /obj/item/organ/brain))
STR.limited_random_access = TRUE
STR.limited_random_access_stack_position = 3
@@ -326,6 +327,7 @@
w_class = WEIGHT_CLASS_BULKY
flags_1 = CONDUCT_1
custom_materials = list(/datum/material/iron=3000)
+ var/max_items = 7
/obj/item/storage/bag/tray/ComponentInitialize()
. = ..()
@@ -333,6 +335,7 @@
STR.max_w_class = WEIGHT_CLASS_NORMAL
STR.can_hold = typecacheof(list(/obj/item/reagent_containers/food, /obj/item/reagent_containers/glass, /datum/reagent/consumable, /obj/item/kitchen/knife, /obj/item/kitchen/rollingpin, /obj/item/kitchen/fork, /obj/item/storage/box)) //Should cover: Bottles, Beakers, Bowls, Booze, Glasses, Food, Kitchen Tools, and ingredient boxes.
STR.insert_preposition = "on"
+ STR.max_items = max_items
/obj/item/storage/bag/tray/attack(mob/living/M, mob/living/user)
. = ..()
@@ -373,6 +376,14 @@
. = ..()
update_icon()
+//bluespace tray, holds more items
+/obj/item/storage/bag/tray/bluespace
+ name = "bluespace tray"
+ icon_state = "bluespace_tray"
+ desc = "A tray created using bluespace technology to fit more food on it."
+ max_items = 30 // far more items
+ custom_materials = list(/datum/material/iron = 2000, /datum/material/bluespace = 500)
+
/*
* Chemistry bag
*/
@@ -444,4 +455,22 @@
STR.max_combined_w_class = 30
STR.max_items = 3
STR.display_numerical_stacking = FALSE
- STR.can_hold = typecacheof(list(/obj/item/ammo_box/magazine, /obj/item/ammo_casing))
\ No newline at end of file
+ STR.can_hold = typecacheof(list(/obj/item/ammo_box/magazine, /obj/item/ammo_casing))
+
+/obj/item/storage/bag/material
+ name = "material pouch"
+ desc = "A pouch for sheets and RCD ammunition that manages to hang where you would normally put things in your pocket."
+ icon = 'icons/obj/items_and_weapons.dmi'
+ icon_state = "materialpouch"
+ slot_flags = ITEM_SLOT_POCKET
+ w_class = WEIGHT_CLASS_BULKY
+ resistance_flags = FLAMMABLE
+
+/obj/item/storage/bag/material/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_w_class = WEIGHT_CLASS_NORMAL
+ STR.max_combined_w_class = INFINITY
+ STR.max_items = 2
+ STR.display_numerical_stacking = TRUE
+ STR.can_hold = typecacheof(list(/obj/item/rcd_ammo, /obj/item/stack/sheet))
diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm
index e7898f9dfd..d92452a7e8 100755
--- a/code/game/objects/items/storage/belt.dm
+++ b/code/game/objects/items/storage/belt.dm
@@ -586,7 +586,7 @@
/obj/item/key/janitor,
/obj/item/clothing/gloves,
/obj/item/melee/flyswatter,
- /obj/item/twohanded/broom,
+ /obj/item/broom,
/obj/item/paint/paint_remover,
/obj/item/assembly/mousetrap,
/obj/item/screwdriver,
diff --git a/code/game/objects/items/storage/book.dm b/code/game/objects/items/storage/book.dm
index e3f590aa2a..5d5f0eaa24 100644
--- a/code/game/objects/items/storage/book.dm
+++ b/code/game/objects/items/storage/book.dm
@@ -173,12 +173,12 @@ 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) || istype(A, /obj/item/melee/cultblade) && !iscultist(user))
+ if(istype(A, /obj/item/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 = A))
playsound(src,'sound/effects/pray_chaplain.ogg',60,1)
- if(istype(A, /obj/item/twohanded/required/cult_bastard))
+ if(istype(A, /obj/item/cult_bastard))
for(var/obj/item/soulstone/SS in A.contents)
SS.usability = TRUE
for(var/mob/living/simple_animal/shade/EX in SS)
diff --git a/code/game/objects/items/storage/boxes.dm b/code/game/objects/items/storage/boxes.dm
index cdd3781748..02ae867240 100644
--- a/code/game/objects/items/storage/boxes.dm
+++ b/code/game/objects/items/storage/boxes.dm
@@ -1418,3 +1418,9 @@ obj/item/storage/box/stingbangs
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)
+
+/obj/item/storage/box/strange_seeds_5pack
+
+/obj/item/storage/box/strange_seeds_5pack/PopulateContents()
+ for(var/i in 1 to 5)
+ new /obj/item/seeds/random(src)
\ No newline at end of file
diff --git a/code/game/objects/items/storage/briefcase.dm b/code/game/objects/items/storage/briefcase.dm
index 826a00b90d..454475625d 100644
--- a/code/game/objects/items/storage/briefcase.dm
+++ b/code/game/objects/items/storage/briefcase.dm
@@ -106,10 +106,10 @@
/obj/item/storage/briefcase/medical
name = "medical briefcase"
icon_state = "medbriefcase"
- desc = "A white with a blue cross brieface, this is ment to hold medical gear that would not be able to normally fit in a bag."
+ desc = "A white with a blue cross brieface, this is meant to hold medical gear that would not be able to normally fit in a bag."
/obj/item/storage/briefcase/medical/PopulateContents()
new /obj/item/clothing/neck/stethoscope(src)
new /obj/item/healthanalyzer(src)
- ..() //In case of paperwork
+ ..() //Incase of paperwork
diff --git a/code/game/objects/items/storage/fancy.dm b/code/game/objects/items/storage/fancy.dm
index 821fd52bd1..927a29407c 100644
--- a/code/game/objects/items/storage/fancy.dm
+++ b/code/game/objects/items/storage/fancy.dm
@@ -207,18 +207,18 @@
cig_position++
/obj/item/storage/fancy/cigarettes/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob)
- if(!ismob(M))
- return
+ if(M != user || !istype(M))
+ return ..()
var/obj/item/clothing/mask/cigarette/cig = locate(/obj/item/clothing/mask/cigarette) in contents
if(cig)
- if(M == user && contents.len > 0 && !user.wear_mask)
+ if(!user.wear_mask && !(SLOT_WEAR_MASK in M.check_obscured_slots()))
var/obj/item/clothing/mask/cigarette/W = cig
SEND_SIGNAL(src, COMSIG_TRY_STORAGE_TAKE, W, M)
M.equip_to_slot_if_possible(W, SLOT_WEAR_MASK)
contents -= W
to_chat(user, "You take \a [W] out of the pack.")
else
- ..()
+ return ..()
else
to_chat(user, "There are no [icon_type]s left in the pack.")
diff --git a/code/game/objects/items/storage/firstaid.dm b/code/game/objects/items/storage/firstaid.dm
index 37b6baaa0b..ea88f80304 100644
--- a/code/game/objects/items/storage/firstaid.dm
+++ b/code/game/objects/items/storage/firstaid.dm
@@ -229,6 +229,25 @@
STR.click_gather = TRUE
STR.can_hold = typecacheof(list(/obj/item/reagent_containers/pill, /obj/item/dice))
+/obj/item/storage/pill_bottle/AltClick(mob/living/carbon/user)
+ if(!istype(user) || !user.canUseTopic(src, BE_CLOSE, ismonkey(user)))
+ return
+ if(!length(user.get_empty_held_indexes()))
+ to_chat(user, "Your hands are full!")
+ return
+ var/obj/item/reagent_containers/pill/P = locate() in contents
+ if(P)
+ SEND_SIGNAL(src, COMSIG_TRY_STORAGE_TAKE, P, user)
+ if(!user.put_in_hands(P))
+ P.forceMove(user.drop_location()) // make sure it's not stuck in the user if the put in hands somehow fails
+ to_chat(user, "[P] drops to the floor!")
+ else
+ to_chat(user, "You take \a [P] out of [src].")
+ else
+ to_chat(user, "There are no pills left in the bottle.")
+ return TRUE
+
+
/obj/item/storage/pill_bottle/suicide_act(mob/user)
user.visible_message("[user] is trying to get the cap off [src]! It looks like [user.p_theyre()] trying to commit suicide!")
return (TOXLOSS)
@@ -370,6 +389,14 @@
for(var/i in 1 to 7)
new /obj/item/reagent_containers/pill/breast_enlargement(src)
+/obj/item/storage/pill_bottle/neurine
+ name = "bottle of neurine pills"
+ desc = "Contains pills to treat non-severe mental traumas."
+
+/obj/item/storage/pill_bottle/neurine/PopulateContents()
+ for(var/i in 1 to 5)
+ new /obj/item/reagent_containers/pill/neurine(src)
+
/////////////
//Organ Box//
/////////////
diff --git a/code/game/objects/items/storage/secure.dm b/code/game/objects/items/storage/secure.dm
index 0e9a9fda61..6061feb893 100644
--- a/code/game/objects/items/storage/secure.dm
+++ b/code/game/objects/items/storage/secure.dm
@@ -205,7 +205,7 @@
new /obj/item/paper(src)
new /obj/item/pen(src)
-/obj/item/storage/secure/safe/attack_hand(mob/user)
+/obj/item/storage/secure/safe/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/objects/items/storage/toolbox.dm b/code/game/objects/items/storage/toolbox.dm
index 7e89ddbd8d..fd50bd022f 100644
--- a/code/game/objects/items/storage/toolbox.dm
+++ b/code/game/objects/items/storage/toolbox.dm
@@ -25,7 +25,7 @@ GLOBAL_LIST_EMPTY(rubber_toolbox_icons)
icon_state = "toolbox_default"
item_state = "toolbox_default"
can_rubberify = FALSE
- material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS | MATERIAL_EFFECTS
+ material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
/obj/item/storage/toolbox/Initialize(mapload)
if(has_latches)
diff --git a/code/game/objects/items/storage/uplink_kits.dm b/code/game/objects/items/storage/uplink_kits.dm
index ffa3d83304..3b66e32d0a 100644
--- a/code/game/objects/items/storage/uplink_kits.dm
+++ b/code/game/objects/items/storage/uplink_kits.dm
@@ -117,7 +117,7 @@
new /obj/item/pizzabox/bomb
if("darklord") //20 tc + tk + summon item close enough for now
- new /obj/item/twohanded/dualsaber(src)
+ new /obj/item/dualsaber(src)
new /obj/item/dnainjector/telemut/darkbundle(src)
new /obj/item/clothing/suit/hooded/chaplain_hoodie(src)
new /obj/item/card/id/syndicate(src)
diff --git a/code/game/objects/items/tanks/jetpack.dm b/code/game/objects/items/tanks/jetpack.dm
index 817258d1c7..7cf25098e1 100644
--- a/code/game/objects/items/tanks/jetpack.dm
+++ b/code/game/objects/items/tanks/jetpack.dm
@@ -21,7 +21,7 @@
/obj/item/tank/jetpack/populate_gas()
if(gas_type)
- air_contents.gases[gas_type] = ((6 * ONE_ATMOSPHERE) * volume / (R_IDEAL_GAS_EQUATION * T20C))
+ air_contents.set_moles(gas_type, ((6 * ONE_ATMOSPHERE) * volume / (R_IDEAL_GAS_EQUATION * T20C)))
/obj/item/tank/jetpack/ui_action_click(mob/user, action)
diff --git a/code/game/objects/items/tanks/tank_types.dm b/code/game/objects/items/tanks/tank_types.dm
index b4c8d8adf0..1c90a83c0a 100644
--- a/code/game/objects/items/tanks/tank_types.dm
+++ b/code/game/objects/items/tanks/tank_types.dm
@@ -18,8 +18,9 @@
force = 10
dog_fashion = /datum/dog_fashion/back
+
/obj/item/tank/internals/oxygen/populate_gas()
- air_contents.gases[/datum/gas/oxygen] = (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)
+ air_contents.set_moles(/datum/gas/oxygen, (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C))
return
@@ -47,8 +48,9 @@
force = 10
/obj/item/tank/internals/anesthetic/populate_gas()
- air_contents.gases[/datum/gas/oxygen] = (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * O2STANDARD
- air_contents.gases[/datum/gas/nitrous_oxide] = (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * N2STANDARD
+ air_contents.set_moles(/datum/gas/oxygen, (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * O2STANDARD)
+ air_contents.set_moles(/datum/gas/nitrous_oxide, (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * N2STANDARD)
+ return
/*
* Air
@@ -62,8 +64,9 @@
dog_fashion = /datum/dog_fashion/back
/obj/item/tank/internals/air/populate_gas()
- air_contents.gases[/datum/gas/oxygen] = (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * O2STANDARD
- air_contents.gases[/datum/gas/nitrogen] = (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * N2STANDARD
+ air_contents.set_moles(/datum/gas/oxygen, (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * O2STANDARD)
+ air_contents.set_moles(/datum/gas/nitrogen, (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * N2STANDARD)
+ return
/*
@@ -79,7 +82,8 @@
/obj/item/tank/internals/plasma/populate_gas()
- air_contents.gases[/datum/gas/plasma] = (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)
+ air_contents.set_moles(/datum/gas/plasma, (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C))
+ return
/obj/item/tank/internals/plasma/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/flamethrower))
@@ -93,12 +97,16 @@
F.update_icon()
else
return ..()
+
+/obj/item/tank/internals/plasma/full/populate_gas()
+ air_contents.set_moles(/datum/gas/plasma, (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C))
+
//Makes empty oxygen tanks spawn without gas
/obj/item/tank/internals/plasma/empty/populate_gas()
return
/obj/item/tank/internals/plasma/full/populate_gas()
- air_contents.gases[/datum/gas/plasma] = (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)
+ air_contents.set_moles(/datum/gas/plasma, (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C))
/*
* Plasmaman Plasma Tank
@@ -113,10 +121,11 @@
distribute_pressure = TANK_DEFAULT_RELEASE_PRESSURE
/obj/item/tank/internals/plasmaman/populate_gas()
- air_contents.gases[/datum/gas/plasma] = (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)
+ air_contents.set_moles(/datum/gas/plasma, (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C))
+ return
/obj/item/tank/internals/plasmaman/full/populate_gas()
- air_contents.gases[/datum/gas/plasma] = (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)
+ air_contents.set_moles(/datum/gas/plasma, (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C))
return
@@ -129,7 +138,7 @@
w_class = WEIGHT_CLASS_SMALL //thanks i forgot this
/obj/item/tank/internals/plasmaman/belt/full/populate_gas()
- air_contents.gases[/datum/gas/plasma] = (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)
+ air_contents.set_moles(/datum/gas/plasma, (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C))
return
//makes empty plasma tanks spawn without gas.
@@ -152,7 +161,7 @@
/obj/item/tank/internals/emergency_oxygen/populate_gas()
- air_contents.gases[/datum/gas/oxygen] = (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)
+ air_contents.set_moles(/datum/gas/oxygen, (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C))
return
/obj/item/tank/internals/emergency_oxygen/empty/populate_gas()
@@ -172,4 +181,4 @@
volume = 10
/obj/item/tank/internals/emergency_oxygen/double/empty/populate_gas()
- return
\ No newline at end of file
+ return
diff --git a/code/game/objects/items/tanks/tanks.dm b/code/game/objects/items/tanks/tanks.dm
index 00fbc41516..b601e4310e 100644
--- a/code/game/objects/items/tanks/tanks.dm
+++ b/code/game/objects/items/tanks/tanks.dm
@@ -64,7 +64,7 @@
. = ..()
air_contents = new(volume) //liters
- air_contents.temperature = T20C
+ air_contents.set_temperature(T20C)
populate_gas()
@@ -92,7 +92,7 @@
. += "The pressure gauge reads [round(src.air_contents.return_pressure(),0.01)] kPa."
- var/celsius_temperature = src.air_contents.temperature-T0C
+ var/celsius_temperature = src.air_contents.return_temperature()-T0C
var/descriptive
if (celsius_temperature < 20)
@@ -168,7 +168,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, 400, 120, master_ui, state)
+ ui = new(user, src, ui_key, "Tank", name, 400, 120, master_ui, state)
ui.open()
/obj/item/tank/ui_data(mob/user)
@@ -235,7 +235,7 @@
if(tank_pressure < distribute_pressure)
distribute_pressure = tank_pressure
- var/moles_needed = distribute_pressure*volume_to_return/(R_IDEAL_GAS_EQUATION*air_contents.temperature)
+ var/moles_needed = distribute_pressure*volume_to_return/(R_IDEAL_GAS_EQUATION*air_contents.return_temperature())
return remove_air(moles_needed)
diff --git a/code/game/objects/items/tanks/watertank.dm b/code/game/objects/items/tanks/watertank.dm
index b1c0d3dfbb..0a7eefb786 100644
--- a/code/game/objects/items/tanks/watertank.dm
+++ b/code/game/objects/items/tanks/watertank.dm
@@ -72,7 +72,7 @@
QDEL_NULL(noz)
return ..()
-/obj/item/watertank/attack_hand(mob/user)
+/obj/item/watertank/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if (user.get_item_by_slot(user.getBackSlot()) == src)
toggle_mister(user)
else
diff --git a/code/game/objects/items/tools/saw.dm b/code/game/objects/items/tools/saw.dm
deleted file mode 100644
index aab59c00be..0000000000
--- a/code/game/objects/items/tools/saw.dm
+++ /dev/null
@@ -1,47 +0,0 @@
-/obj/item/hatchet/saw
- name = "handsaw"
- desc = "A very sharp handsaw, it's compact."
- icon = 'icons/obj/tools.dmi'
- icon_state = "saw"
- item_state = "sawhandle_greyscale"
- lefthand_file = 'icons/mob/inhands/equipment/kitchen_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/equipment/kitchen_righthand.dmi'
- tool_behaviour = TOOL_SAW
- force = 10
- throwforce = 8
- throw_speed = 3
- throw_range = 5
- custom_materials = list(/datum/material/iron = 5000)
- attack_verb = list("sawed", "sliced", "cut")
- hitsound = 'sound/weapons/bladeslice.ogg'
- sharpness = IS_SHARP
- var/random_color = TRUE //code taken from screwdrivers.dm; cool handles are cool.
- var/static/list/saw_colors = list(
- "blue" = rgb(24, 97, 213),
- "red" = rgb(255, 0, 0),
- "pink" = rgb(213, 24, 141),
- "brown" = rgb(160, 82, 18),
- "green" = rgb(14, 127, 27),
- "cyan" = rgb(24, 162, 213),
- "yellow" = rgb(255, 165, 0)
- )
-
-/obj/item/hatchet/saw/Initialize()
- . = ..()
- if(random_color)
- icon_state = "sawhandle_greyscale"
- var/our_color = pick(saw_colors)
- add_atom_colour(saw_colors[our_color], FIXED_COLOUR_PRIORITY)
- update_icon()
- if(prob(75))
- pixel_y = rand(-8, 8)
-
-/obj/item/hatchet/saw/update_overlays()
- . = ..()
- if(!random_color) //icon override
- return
- var/mutable_appearance/base_overlay = mutable_appearance(icon, "sawblade")
- base_overlay.appearance_flags = RESET_COLOR
- . += base_overlay
-
-// END
\ No newline at end of file
diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm
index cd630a2c82..fe2e0df2eb 100644
--- a/code/game/objects/items/toys.dm
+++ b/code/game/objects/items/toys.dm
@@ -272,7 +272,7 @@
return
else
to_chat(user, "You attach the ends of the two plastic swords, making a single double-bladed toy! You're fake-cool.")
- var/obj/item/twohanded/dualsaber/toy/newSaber = new /obj/item/twohanded/dualsaber/toy(user.loc)
+ var/obj/item/dualsaber/toy/newSaber = new /obj/item/dualsaber/toy(user.loc)
if(hacked) // That's right, we'll only check the "original" "sword".
newSaber.hacked = TRUE
qdel(W)
@@ -363,7 +363,7 @@
return
else
to_chat(user, "You combine the two plastic swords, making a single supermassive toy! You're fake-cool.")
- new /obj/item/twohanded/dualsaber/hypereutactic/toy(user.loc)
+ new /obj/item/dualsaber/hypereutactic/toy(user.loc)
qdel(W)
qdel(src)
else
@@ -437,41 +437,44 @@
/*
* Subtype of Double-Bladed Energy Swords
*/
-/obj/item/twohanded/dualsaber/toy
+/obj/item/dualsaber/toy
name = "double-bladed toy sword"
desc = "A cheap, plastic replica of TWO energy swords. Double the fun!"
force = 0
throwforce = 0
throw_speed = 3
throw_range = 5
- force_unwielded = 0
- force_wielded = 0
block_parry_data = null
attack_verb = list("attacked", "struck", "hit")
total_mass_on = TOTAL_MASS_TOY_SWORD
sharpness = IS_BLUNT
-/obj/item/twohanded/dualsaber/toy/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
+/obj/item/dualsaber/toy/ComponentInitialize()
+ AddComponent(/datum/component/two_handed, force_unwielded=0, force_wielded=0, wieldsound='sound/weapons/saberon.ogg', unwieldsound='sound/weapons/saberoff.ogg')
+
+/obj/item/dualsaber/toy/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
return BLOCK_NONE
-/obj/item/twohanded/dualsaber/hypereutactic/toy
+/obj/item/dualsaber/hypereutactic/toy
name = "\improper DX Hyper-Euplastic LightSword"
desc = "A supermassive toy envisioned to cleave the very fabric of space and time itself in twain. Realistic visuals and sounds! Ages 8 and up."
force = 0
throwforce = 0
throw_speed = 3
throw_range = 5
- force_unwielded = 0
- force_wielded = 0
+
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/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
+/obj/item/dualsaber/hypereutactic/toy/ComponentInitialize()
+ AddComponent(/datum/component/two_handed, force_unwielded=0, force_wielded=0, wieldsound='sound/weapons/saberon.ogg', unwieldsound='sound/weapons/saberoff.ogg')
+
+/obj/item/dualsaber/hypereutactic/toy/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
return BLOCK_NONE
-/obj/item/twohanded/dualsaber/hypereutactic/toy/rainbow
+/obj/item/dualsaber/hypereutactic/toy/rainbow
name = "\improper Hyper-Euclidean Reciprocating Trigonometric Zweihander"
desc = "A custom-built toy with fancy rainbow lights built-in."
hacked = TRUE
@@ -571,7 +574,7 @@
else
. = ..()
-/obj/item/toy/prize/attack_hand(mob/user)
+/obj/item/toy/prize/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
@@ -810,7 +813,7 @@
//ATTACK HAND IGNORING PARENT RETURN VALUE
//ATTACK HAND NOT CALLING PARENT
-/obj/item/toy/cards/deck/attack_hand(mob/user)
+/obj/item/toy/cards/deck/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
draw_card(user)
/obj/item/toy/cards/deck/proc/draw_card(mob/user)
@@ -823,12 +826,11 @@
var/obj/item/toy/cards/singlecard/H = new/obj/item/toy/cards/singlecard(user.loc)
if(holo)
holo.spawned += H // track them leaving the holodeck
- choice = cards[1]
+ choice = popleft(cards)
H.cardname = choice
H.parentdeck = src
var/O = src
H.apply_card_vars(H,O)
- src.cards -= choice
H.pickup(user)
user.put_in_hands(H)
user.visible_message("[user] draws a card from the deck.", "You draw a card from the deck.")
diff --git a/code/game/objects/items/twohanded.dm b/code/game/objects/items/twohanded.dm
deleted file mode 100644
index 8c9cde5b76..0000000000
--- a/code/game/objects/items/twohanded.dm
+++ /dev/null
@@ -1,1411 +0,0 @@
-/* Two-handed Weapons
- * Contains:
- * Twohanded
- * Fireaxe
- * Double-Bladed Energy Swords
- * Spears
- * CHAINSAWS
- * Bone Axe and Spear
- * And more
- */
-
-/*##################################################################
-##################### TWO HANDED WEAPONS BE HERE~ -Agouri :3 ########
-####################################################################*/
-
-//Rewrote TwoHanded weapons stuff and put it all here. Just copypasta fireaxe to make new ones ~Carn
-//This rewrite means we don't have two variables for EVERY item which are used only by a few weapons.
-//It also tidies stuff up elsewhere.
-
-
-
-
-/*
- * Twohanded
- */
-/obj/item/twohanded
- var/wielded = FALSE
- var/force_unwielded // default to null, the number force will be set to on unwield()
- var/force_wielded // same as above but for wield()
- var/wieldsound = null
- var/unwieldsound = null
- var/slowdown_wielded = 0
- /// Do we need to be wielded to actively block/parry?
- var/requires_wield_to_block_parry = TRUE
- item_flags = SLOWS_WHILE_IN_HAND
-
-/obj/item/twohanded/proc/unwield(mob/living/carbon/user, show_message = TRUE)
- if(!wielded || !user)
- return
- wielded = 0
- if(!isnull(force_unwielded))
- force = force_unwielded
- var/sf = findtext(name, " (Wielded)", -10)//10 == length(" (Wielded)")
- if(sf)
- name = copytext(name, 1, sf)
- else //something wrong
- name = "[initial(name)]"
- update_icon()
- if(user.get_item_by_slot(SLOT_BACK) == src)
- user.update_inv_back()
- else
- user.update_inv_hands()
- if(show_message)
- if(iscyborg(user))
- to_chat(user, "You free up your module.")
- else
- to_chat(user, "You are now carrying [src] with one hand.")
- if(unwieldsound)
- playsound(loc, unwieldsound, 50, 1)
- var/obj/item/twohanded/offhand/O = user.get_inactive_held_item()
- if(O && istype(O))
- O.unwield()
- set_slowdown(slowdown - slowdown_wielded)
-
-/obj/item/twohanded/proc/wield(mob/living/carbon/user)
- if(wielded)
- return
- if(ismonkey(user))
- to_chat(user, "It's too heavy for you to wield fully.")
- return
- if(user.get_inactive_held_item())
- to_chat(user, "You need your other hand to be empty!")
- return
- if(user.get_num_arms() < 2)
- to_chat(user, "You don't have enough intact hands.")
- return
- wielded = 1
- if(!isnull(force_wielded))
- force = force_wielded
- name = "[name] (Wielded)"
- update_icon()
- if(iscyborg(user))
- to_chat(user, "You dedicate your module to [src].")
- else
- to_chat(user, "You grab [src] with both hands.")
- if (wieldsound)
- playsound(loc, wieldsound, 50, 1)
- var/obj/item/twohanded/offhand/O = new(user) ////Let's reserve his other hand~
- O.name = "[name] - offhand"
- O.desc = "Your second grip on [src]."
- O.wielded = TRUE
- user.put_in_inactive_hand(O)
- set_slowdown(slowdown + slowdown_wielded)
-
-/obj/item/twohanded/can_active_block()
- return ..() && (!requires_wield_to_block_parry || wielded)
-
-/obj/item/twohanded/can_active_parry()
- return ..() && (!requires_wield_to_block_parry || wielded)
-
-/obj/item/twohanded/dropped(mob/user)
- . = ..()
- //handles unwielding a twohanded weapon when dropped as well as clearing up the offhand
- if(!wielded)
- return
- unwield(user)
-
-/obj/item/twohanded/attack_self(mob/user)
- . = ..()
- if(wielded) //Trying to unwield it
- unwield(user)
- else //Trying to wield it
- wield(user)
-
-/obj/item/twohanded/equip_to_best_slot(mob/M)
- if(..())
- if(istype(src, /obj/item/twohanded/required))
- return // unwield forces twohanded-required items to be dropped.
- unwield(M)
- return
-
-/obj/item/twohanded/equipped(mob/user, slot)
- ..()
- if(!user.is_holding(src) && wielded && !istype(src, /obj/item/twohanded/required))
- unwield(user)
-
-///////////OFFHAND///////////////
-/obj/item/twohanded/offhand
- name = "offhand"
- icon_state = "offhand"
- w_class = WEIGHT_CLASS_HUGE
- item_flags = ABSTRACT
- resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
-
-/obj/item/twohanded/offhand/Destroy()
- wielded = FALSE
- return ..()
-
-/obj/item/twohanded/offhand/dropped(mob/living/user, show_message = TRUE) //Only utilized by dismemberment since you can't normally switch to the offhand to drop it.
- . = ..()
- var/obj/I = user.get_active_held_item()
- if(I && istype(I, /obj/item/twohanded))
- var/obj/item/twohanded/thw = I
- thw.unwield(user, show_message)
- if(istype(thw, /obj/item/twohanded/required))
- user.dropItemToGround(thw)
- if(!QDELETED(src))
- qdel(src)
-
-/obj/item/twohanded/offhand/unwield()
- if(wielded)//Only delete if we're wielded
- wielded = FALSE
- qdel(src)
-
-/obj/item/twohanded/offhand/wield()
- if(wielded)//Only delete if we're wielded
- wielded = FALSE
- qdel(src)
-
-/obj/item/twohanded/offhand/attack_self(mob/living/carbon/user) //You should never be able to do this in standard use of two handed items. This is a backup for lingering offhands.
- var/obj/item/twohanded/O = user.get_inactive_held_item()
- if (istype(O) && !istype(O, /obj/item/twohanded/offhand/)) //If you have a proper item in your other hand that the offhand is for, do nothing. This should never happen.
- return
- if (QDELETED(src))
- return
- qdel(src) //If it's another offhand, or literally anything else, qdel. If I knew how to add logging messages I'd put one here.
-
-///////////Two hand required objects///////////////
-//This is for objects that require two hands to even pick up
-/obj/item/twohanded/required
- w_class = WEIGHT_CLASS_HUGE
-
-/obj/item/twohanded/required/attack_self()
- return
-
-/obj/item/twohanded/required/mob_can_equip(mob/M, mob/equipper, slot, disable_warning = 0)
- if(wielded && !slot_flags)
- if(!disable_warning)
- to_chat(M, "[src] is too cumbersome to carry with anything but your hands!")
- return 0
- return ..()
-
-/obj/item/twohanded/required/attack_hand(mob/user)//Can't even pick it up without both hands empty
- var/obj/item/twohanded/required/H = user.get_inactive_held_item()
- if(get_dist(src,user) > 1)
- return
- if(H != null)
- to_chat(user, "[src] is too cumbersome to carry in one hand!")
- return
- if(loc != user)
- wield(user)
- . = ..()
-
-/obj/item/twohanded/required/equipped(mob/user, slot)
- ..()
- var/slotbit = slotdefine2slotbit(slot)
- if(slot_flags & slotbit)
- var/datum/O = user.is_holding_item_of_type(/obj/item/twohanded/offhand)
- if(!O || QDELETED(O))
- return
- qdel(O)
- return
- if(slot == SLOT_HANDS)
- wield(user)
- else
- unwield(user)
-
-/obj/item/twohanded/required/dropped(mob/living/user, show_message = TRUE)
- unwield(user, show_message)
- ..()
-
-/obj/item/twohanded/required/wield(mob/living/carbon/user)
- ..()
- if(!wielded)
- user.dropItemToGround(src)
-
-/obj/item/twohanded/required/unwield(mob/living/carbon/user, show_message = TRUE)
- if(!wielded)
- return
- if(show_message)
- to_chat(user, "You drop [src].")
- ..(user, FALSE)
-
-/*
- * Fireaxe
- */
-/obj/item/twohanded/fireaxe // DEM AXES MAN, marker -Agouri
- icon_state = "fireaxe0"
- lefthand_file = 'icons/mob/inhands/weapons/axes_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/weapons/axes_righthand.dmi'
- name = "fire axe"
- desc = "Truly, the weapon of a madman. Who would think to fight fire with an axe?"
- force = 5
- throwforce = 15
- w_class = WEIGHT_CLASS_BULKY
- slot_flags = ITEM_SLOT_BACK
- force_unwielded = 5
- force_wielded = 24
- attack_verb = list("attacked", "chopped", "cleaved", "torn", "cut")
- hitsound = 'sound/weapons/bladeslice.ogg'
- sharpness = IS_SHARP
- max_integrity = 200
- armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 30)
- resistance_flags = FIRE_PROOF
-
-/obj/item/twohanded/fireaxe/Initialize()
- . = ..()
- AddComponent(/datum/component/butchering, 100, 80, 0 , hitsound) //axes are not known for being precision butchering tools
-
-/obj/item/twohanded/fireaxe/update_icon_state() //Currently only here to fuck with the on-mob icons.
- icon_state = "fireaxe[wielded]"
- return
-
-/obj/item/twohanded/fireaxe/suicide_act(mob/user)
- user.visible_message("[user] axes [user.p_them()]self from head to toe! It looks like [user.p_theyre()] trying to commit suicide!")
- return (BRUTELOSS)
-
-/obj/item/twohanded/fireaxe/afterattack(atom/A, mob/living/user, proximity)
- . = ..()
- if(!proximity || IS_STAMCRIT(user)) //don't make stamcrit message they'll already have gotten one from the primary attack.
- return
- if(wielded) //destroys windows and grilles in one hit (or more if it has a ton of health like plasmaglass)
- if(istype(A, /obj/structure/window))
- var/obj/structure/window/W = A
- W.take_damage(200, BRUTE, "melee", 0)
- else if(istype(A, /obj/structure/grille))
- var/obj/structure/grille/G = A
- G.take_damage(40, BRUTE, "melee", 0)
-
-
-/*
- * Double-Bladed Energy Swords - Cheridan
- */
-/obj/item/twohanded/dualsaber
- icon_state = "dualsaber0"
- lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
- name = "double-bladed energy sword"
- desc = "Handle with care."
- force = 3
- throwforce = 5
- throw_speed = 3
- throw_range = 5
- w_class = WEIGHT_CLASS_SMALL
- var/w_class_on = WEIGHT_CLASS_BULKY
- item_flags = ITEM_CAN_PARRY | SLOWS_WHILE_IN_HAND | ITEM_CAN_BLOCK
- block_parry_data = /datum/block_parry_data/dual_esword
- force_unwielded = 3
- force_wielded = 34
- wieldsound = 'sound/weapons/saberon.ogg'
- unwieldsound = 'sound/weapons/saberoff.ogg'
- hitsound = "swing_hit"
- var/hitsound_on = 'sound/weapons/blade1.ogg'
- armour_penetration = 35
- var/saber_color = "green"
- light_color = "#00ff00"//green
- attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
- max_integrity = 200
- armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 70)
- resistance_flags = FIRE_PROOF
- var/hacked = FALSE
- /// Can this reflect all energy projectiles?
- var/can_reflect = TRUE
- var/brightness_on = 6 //TWICE AS BRIGHT AS A REGULAR ESWORD
- var/list/possible_colors = list("red", "blue", "green", "purple")
- var/list/rainbow_colors = list(LIGHT_COLOR_RED, LIGHT_COLOR_GREEN, LIGHT_COLOR_LIGHT_CYAN, LIGHT_COLOR_LAVENDER)
- var/spinnable = TRUE
- total_mass = 0.4 //Survival flashlights typically weigh around 5 ounces.
- var/total_mass_on = 3.4
-
-/datum/block_parry_data/dual_esword
- block_damage_absorption = 2
- block_damage_multiplier = 0.15
- block_damage_multiplier_override = list(
- ATTACK_TYPE_MELEE = 0.25
- )
- block_start_delay = 0 // instantaneous block
- block_stamina_cost_per_second = 2.5
- block_stamina_efficiency = 3
- block_lock_sprinting = TRUE
- // no attacking while blocking
- block_lock_attacking = TRUE
- block_projectile_mitigation = 75
-
- parry_time_windup = 0
- parry_time_active = 8
- parry_time_spindown = 0
- // we want to signal to players the most dangerous phase, the time when automatic counterattack is a thing.
- parry_time_windup_visual_override = 1
- parry_time_active_visual_override = 3
- parry_time_spindown_visual_override = 4
- parry_flags = PARRY_DEFAULT_HANDLE_FEEDBACK // esword users can attack while parrying.
- parry_time_perfect = 2 // first ds isn't perfect
- parry_time_perfect_leeway = 1
- parry_imperfect_falloff_percent = 10
- parry_efficiency_to_counterattack = 100
- parry_efficiency_considered_successful = 25 // VERY generous
- parry_efficiency_perfect = 90
- parry_failed_stagger_duration = 3 SECONDS
- parry_failed_clickcd_duration = CLICK_CD_MELEE
-
- // more efficient vs projectiles
- block_stamina_efficiency_override = list(
- TEXT_ATTACK_TYPE_PROJECTILE = 4
- )
-
-/obj/item/twohanded/dualsaber/suicide_act(mob/living/carbon/user)
- if(wielded)
- user.visible_message("[user] begins spinning way too fast! It looks like [user.p_theyre()] trying to commit suicide!")
-
- var/obj/item/bodypart/head/myhead = user.get_bodypart(BODY_ZONE_HEAD)//stole from chainsaw code
- var/obj/item/organ/brain/B = user.getorganslot(ORGAN_SLOT_BRAIN)
- B.organ_flags &= ~ORGAN_VITAL //this cant possibly be a good idea
- var/randdir
- for(var/i in 1 to 24)//like a headless chicken!
- if(user.is_holding(src))
- randdir = pick(GLOB.alldirs)
- user.Move(get_step(user, randdir),randdir)
- user.emote("spin")
- if (i == 3 && myhead)
- myhead.drop_limb()
- sleep(3)
- else
- user.visible_message("[user] panics and starts choking to death!")
- return OXYLOSS
-
-
- else
- user.visible_message("[user] begins beating [user.p_them()]self to death with \the [src]'s handle! It probably would've been cooler if [user.p_they()] turned it on first!")
- return BRUTELOSS
-
-/obj/item/twohanded/dualsaber/Initialize()
- . = ..()
- if(LAZYLEN(possible_colors))
- saber_color = pick(possible_colors)
- switch(saber_color)
- if("red")
- light_color = LIGHT_COLOR_RED
- if("green")
- light_color = LIGHT_COLOR_GREEN
- if("blue")
- light_color = LIGHT_COLOR_LIGHT_CYAN
- if("purple")
- light_color = LIGHT_COLOR_LAVENDER
-
-/obj/item/twohanded/dualsaber/Destroy()
- STOP_PROCESSING(SSobj, src)
- . = ..()
-
-/obj/item/twohanded/dualsaber/update_icon_state()
- if(wielded)
- icon_state = "dualsaber[saber_color][wielded]"
- else
- icon_state = "dualsaber0"
- clean_blood()
-
-/obj/item/twohanded/dualsaber/attack(mob/target, mob/living/carbon/human/user)
- if(user.has_dna())
- if(user.dna.check_mutation(HULK))
- to_chat(user, "You grip the blade too hard and accidentally close it!")
- unwield()
- return
- ..()
- if(HAS_TRAIT(user, TRAIT_CLUMSY) && (wielded) && prob(40))
- impale(user)
- return
- if(spinnable && (wielded) && prob(50))
- INVOKE_ASYNC(src, .proc/jedi_spin, user)
-
-/obj/item/twohanded/dualsaber/proc/jedi_spin(mob/living/user)
- for(var/i in list(NORTH,SOUTH,EAST,WEST,EAST,SOUTH,NORTH,SOUTH,EAST,WEST,EAST,SOUTH))
- user.setDir(i)
- if(i == WEST)
- user.emote("flip")
- sleep(1)
-
-/obj/item/twohanded/dualsaber/proc/impale(mob/living/user)
- to_chat(user, "You twirl around a bit before losing your balance and impaling yourself on [src].")
- if (force_wielded)
- user.take_bodypart_damage(20,25)
- else
- user.adjustStaminaLoss(25)
-
-/obj/item/twohanded/dualsaber/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
- if(!wielded)
- return NONE
- if(can_reflect && is_energy_reflectable_projectile(object) && (attack_type & ATTACK_TYPE_PROJECTILE))
- block_return[BLOCK_RETURN_REDIRECT_METHOD] = REDIRECT_METHOD_RETURN_TO_SENDER //no you
- return BLOCK_SHOULD_REDIRECT | BLOCK_SUCCESS | BLOCK_REDIRECTED
- return ..()
-
-/obj/item/twohanded/dualsaber/attack_hulk(mob/living/carbon/human/user, does_attack_animation = 0) //In case thats just so happens that it is still activated on the groud, prevents hulk from picking it up
- if(wielded)
- to_chat(user, "You can't pick up such dangerous item with your meaty hands without losing fingers, better not to!")
- return 1
-
-/obj/item/twohanded/dualsaber/wield(mob/living/carbon/M) //Specific wield () hulk checks due to reflection chance for balance issues and switches hitsounds.
- if(M.has_dna())
- if(M.dna.check_mutation(HULK))
- to_chat(M, "You lack the grace to wield this!")
- return
- ..()
- if(wielded)
- sharpness = IS_SHARP
- w_class = w_class_on
- total_mass = total_mass_on
- hitsound = 'sound/weapons/blade1.ogg'
- START_PROCESSING(SSobj, src)
- set_light(brightness_on)
- AddElement(/datum/element/sword_point)
-
-/obj/item/twohanded/dualsaber/unwield() //Specific unwield () to switch hitsounds.
- sharpness = initial(sharpness)
- w_class = initial(w_class)
- total_mass = initial(total_mass)
- ..()
- hitsound = "swing_hit"
- STOP_PROCESSING(SSobj, src)
- set_light(0)
- RemoveElement(/datum/element/sword_point)
-
-/obj/item/twohanded/dualsaber/process()
- if(wielded)
- if(hacked)
- rainbow_process()
- open_flame()
- else
- STOP_PROCESSING(SSobj, src)
-
-/obj/item/twohanded/dualsaber/proc/rainbow_process()
- light_color = pick(rainbow_colors)
-
-/obj/item/twohanded/dualsaber/ignition_effect(atom/A, mob/user)
- // same as /obj/item/melee/transforming/energy, mostly
- if(!wielded)
- return ""
- var/in_mouth = ""
- if(iscarbon(user))
- var/mob/living/carbon/C = user
- if(C.wear_mask)
- in_mouth = ", barely missing [user.p_their()] nose"
- . = "[user] swings [user.p_their()] [name][in_mouth]. [user.p_they(TRUE)] light[user.p_s()] [user.p_their()] [A.name] in the process."
- playsound(loc, hitsound, get_clamped_volume(), 1, -1)
- add_fingerprint(user)
- // Light your candles while spinning around the room
- if(spinnable)
- INVOKE_ASYNC(src, .proc/jedi_spin, user)
-
-/obj/item/twohanded/dualsaber/green
- possible_colors = list("green")
-
-/obj/item/twohanded/dualsaber/red
- possible_colors = list("red")
-
-/obj/item/twohanded/dualsaber/blue
- possible_colors = list("blue")
-
-/obj/item/twohanded/dualsaber/purple
- possible_colors = list("purple")
-
-/obj/item/twohanded/dualsaber/attackby(obj/item/W, mob/user, params)
- if(istype(W, /obj/item/multitool))
- if(!hacked)
- hacked = TRUE
- to_chat(user, "2XRNBW_ENGAGE")
- saber_color = "rainbow"
- update_icon()
- else
- to_chat(user, "It's starting to look like a triple rainbow - no, nevermind.")
- else
- return ..()
-
-/////////////////////////////////////////////////////
-// HYPEREUTACTIC Blades /////////////////////////
-/////////////////////////////////////////////////////
-
-/obj/item/twohanded/dualsaber/hypereutactic
- icon = 'icons/obj/1x2.dmi'
- icon_state = "hypereutactic"
- lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/64x64_righthand.dmi'
- item_state = "hypereutactic"
- inhand_x_dimension = 64
- inhand_y_dimension = 64
- name = "hypereutactic blade"
- desc = "A supermassive weapon envisioned to cleave the very fabric of space and time itself in twain, the hypereutactic blade dynamically flash-forges a hypereutactic crystaline nanostructure capable of passing through most known forms of matter like a hot knife through butter."
- force = 7
- force_unwielded = 7
- force_wielded = 40
- wieldsound = 'sound/weapons/nebon.ogg'
- unwieldsound = 'sound/weapons/neboff.ogg'
- hitsound_on = 'sound/weapons/nebhit.ogg'
- slowdown_wielded = 1
- armour_penetration = 60
- light_color = "#37FFF7"
- rainbow_colors = list("#FF0000", "#FFFF00", "#00FF00", "#00FFFF", "#0000FF","#FF00FF", "#3399ff", "#ff9900", "#fb008b", "#9800ff", "#00ffa3", "#ccff00")
- attack_verb = list("attacked", "slashed", "stabbed", "sliced", "destroyed", "ripped", "devastated", "shredded")
- spinnable = FALSE
- total_mass_on = 4
-
-/obj/item/twohanded/dualsaber/hypereutactic/ComponentInitialize()
- . = ..()
- AddElement(/datum/element/update_icon_updates_onmob)
-
-/obj/item/twohanded/dualsaber/hypereutactic/update_icon_state()
- return
-
-/obj/item/twohanded/dualsaber/hypereutactic/update_overlays()
- . = ..()
- var/mutable_appearance/blade_overlay = mutable_appearance(icon, "hypereutactic_blade")
- var/mutable_appearance/gem_overlay = mutable_appearance(icon, "hypereutactic_gem")
-
- if(light_color)
- blade_overlay.color = light_color
- gem_overlay.color = light_color
-
- . += gem_overlay
-
- if(wielded)
- . += blade_overlay
-
- clean_blood()
-
-/obj/item/twohanded/dualsaber/hypereutactic/AltClick(mob/living/user)
- . = ..()
- if(!user.canUseTopic(src, BE_CLOSE, FALSE) || hacked)
- return
- if(user.incapacitated() || !istype(user))
- to_chat(user, "You can't do that right now!")
- return
- if(alert("Are you sure you want to recolor your blade?", "Confirm Repaint", "Yes", "No") == "Yes")
- var/energy_color_input = input(usr,"","Choose Energy Color",light_color) as color|null
- if(!energy_color_input || !user.canUseTopic(src, BE_CLOSE, FALSE) || hacked)
- return
- light_color = sanitize_hexcolor(energy_color_input, desired_format=6, include_crunch=1)
- update_icon()
- update_light()
- return TRUE
-
-/obj/item/twohanded/dualsaber/hypereutactic/worn_overlays(isinhands, icon_file, used_state, style_flags = NONE)
- . = ..()
- if(isinhands)
- var/mutable_appearance/gem_inhand = mutable_appearance(icon_file, "hypereutactic_gem")
- gem_inhand.color = light_color
- . += gem_inhand
- if(wielded)
- var/mutable_appearance/blade_inhand = mutable_appearance(icon_file, "hypereutactic_blade")
- blade_inhand.color = light_color
- . += blade_inhand
-
-/obj/item/twohanded/dualsaber/hypereutactic/examine(mob/user)
- . = ..()
- if(!hacked)
- . += "Alt-click to recolor it."
-
-/obj/item/twohanded/dualsaber/hypereutactic/rainbow_process()
- . = ..()
- update_icon()
- update_light()
-
-/obj/item/twohanded/dualsaber/hypereutactic/chaplain
- name = "divine lightblade"
- desc = "A giant blade of bright and holy light, said to cut down the wicked with ease."
- force = 5
- force_unwielded = 5
- force_wielded = 20
- block_chance = 50
- armour_penetration = 0
- var/chaplain_spawnable = TRUE
- can_reflect = FALSE
- obj_flags = UNIQUE_RENAME
-
-/obj/item/twohanded/dualsaber/hypereutactic/chaplain/ComponentInitialize()
- . = ..()
- AddComponent(/datum/component/anti_magic, TRUE, TRUE, FALSE, null, null, FALSE)
-
-//spears
-/obj/item/twohanded/spear
- icon_state = "spearglass0"
- lefthand_file = 'icons/mob/inhands/weapons/polearms_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/weapons/polearms_righthand.dmi'
- name = "spear"
- desc = "A haphazardly-constructed yet still deadly weapon of ancient design."
- force = 10
- w_class = WEIGHT_CLASS_BULKY
- slot_flags = ITEM_SLOT_BACK
- force_unwielded = 10
- force_wielded = 18
- throwforce = 20
- throw_speed = 4
- embedding = list("impact_pain_mult" = 3)
- armour_penetration = 10
- custom_materials = list(/datum/material/iron=1150, /datum/material/glass=2075)
- hitsound = 'sound/weapons/bladeslice.ogg'
- attack_verb = list("attacked", "poked", "jabbed", "torn", "gored")
- sharpness = IS_SHARP
- max_integrity = 200
- armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 30)
- var/obj/item/grenade/explosive = null
- var/war_cry = "AAAAARGH!!!"
- var/icon_prefix = "spearglass"
-
-/obj/item/twohanded/spear/Initialize()
- . = ..()
- AddComponent(/datum/component/butchering, 100, 70) //decent in a pinch, but pretty bad.
- AddComponent(/datum/component/jousting)
- AddElement(/datum/element/sword_point)
-
-/obj/item/twohanded/spear/attack_self(mob/user)
- if(explosive)
- explosive.attack_self(user)
- return
- . = ..()
-
-//Citadel additions : attack_self and rightclick_attack_self
-
-/obj/item/twohanded/rightclick_attack_self(mob/user)
- if(wielded) //Trying to unwield it
- unwield(user)
- else //Trying to wield it
- wield(user)
- return TRUE
-
-/obj/item/twohanded/spear/suicide_act(mob/living/carbon/user)
- user.visible_message("[user] begins to sword-swallow \the [src]! It looks like [user.p_theyre()] trying to commit suicide!")
- if(explosive) //Citadel Edit removes qdel and explosive.forcemove(AM)
- user.say("[war_cry]", forced="spear warcry")
- explosive.prime()
- user.gib()
- return BRUTELOSS
- return BRUTELOSS
-
-/obj/item/twohanded/spear/examine(mob/user)
- . = ..()
- if(explosive)
- . += "Use in your hands to activate the attached explosive.
Alt-click to set your war cry.
Right-click in combat mode to wield"
-
-/obj/item/twohanded/spear/update_icon_state()
- if(explosive)
- icon_state = "spearbomb[wielded]"
- else
- icon_state = "[icon_prefix][wielded]"
-
-/obj/item/twohanded/spear/afterattack(atom/movable/AM, mob/user, proximity)
- . = ..()
- if(!proximity)
- return
- if(isopenturf(AM)) //So you can actually melee with it
- return
- if(explosive && wielded) //Citadel edit removes qdel and explosive.forcemove(AM)
- user.say("[war_cry]", forced="spear warcry")
- explosive.prime()
-
-/obj/item/twohanded/spear/grenade_prime_react(obj/item/grenade/nade) //Citadel edit, removes throw_impact because memes
- nade.forceMove(get_turf(src))
- qdel(src)
-
-/obj/item/twohanded/spear/AltClick(mob/user)
- . = ..()
- if(user.canUseTopic(src, BE_CLOSE))
- ..()
- if(!explosive)
- return
- if(istype(user) && loc == user)
- var/input = stripped_input(user,"What do you want your war cry to be? You will shout it when you hit someone in melee.", ,"", 50)
- if(input)
- src.war_cry = input
- return TRUE
-
-/obj/item/twohanded/spear/CheckParts(list/parts_list)
- var/obj/item/shard/tip = locate() in parts_list
- if (istype(tip, /obj/item/shard/plasma))
- force_wielded = 19
- force_unwielded = 11
- throwforce = 21
- embedding = list(embed_chance = 75, pain_mult = 1.5) //plasmaglass spears are sharper
- icon_prefix = "spearplasma"
- qdel(tip)
- var/obj/item/twohanded/spear/S = locate() in parts_list
- if(S)
- if(S.explosive)
- S.explosive.forceMove(get_turf(src))
- S.explosive = null
- parts_list -= S
- qdel(S)
- ..()
- var/obj/item/grenade/G = locate() in contents
- if(G)
- explosive = G
- name = "explosive lance"
- embedding = list(embed_chance = 0, pain_mult = 1)//elances should not be embeddable
- desc = "A makeshift spear with [G] attached to it."
- update_icon()
-
-// CHAINSAW
-/obj/item/twohanded/required/chainsaw
- name = "chainsaw"
- desc = "A versatile power tool. Useful for limbing trees and delimbing humans."
- icon_state = "chainsaw_off"
- lefthand_file = 'icons/mob/inhands/weapons/chainsaw_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/weapons/chainsaw_righthand.dmi'
- flags_1 = CONDUCT_1
- force = 13
- var/force_on = 24
- w_class = WEIGHT_CLASS_HUGE
- throwforce = 13
- throw_speed = 2
- throw_range = 4
- custom_materials = list(/datum/material/iron=13000)
- attack_verb = list("sawed", "torn", "cut", "chopped", "diced")
- hitsound = "swing_hit"
- sharpness = IS_SHARP
- actions_types = list(/datum/action/item_action/startchainsaw)
- var/on = FALSE
- tool_behaviour = TOOL_SAW
- toolspeed = 0.5
-
-/obj/item/twohanded/required/chainsaw/ComponentInitialize()
- . = ..()
- AddComponent(/datum/component/butchering, 30, 100, 0, 'sound/weapons/chainsawhit.ogg', TRUE)
- AddElement(/datum/element/update_icon_updates_onmob)
-
-/obj/item/twohanded/required/chainsaw/suicide_act(mob/living/carbon/user)
- if(on)
- user.visible_message("[user] begins to tear [user.p_their()] head off with [src]! It looks like [user.p_theyre()] trying to commit suicide!")
- playsound(src, 'sound/weapons/chainsawhit.ogg', 100, 1)
- var/obj/item/bodypart/head/myhead = user.get_bodypart(BODY_ZONE_HEAD)
- if(myhead)
- myhead.dismember()
- else
- user.visible_message("[user] smashes [src] into [user.p_their()] neck, destroying [user.p_their()] esophagus! It looks like [user.p_theyre()] trying to commit suicide!")
- playsound(src, 'sound/weapons/genhit1.ogg', 100, 1)
- return(BRUTELOSS)
-
-/obj/item/twohanded/required/chainsaw/attack_self(mob/user)
- on = !on
- to_chat(user, "As you pull the starting cord dangling from [src], [on ? "it begins to whirr." : "the chain stops moving."]")
- force = on ? force_on : initial(force)
- throwforce = on ? force_on : force
- update_icon()
- var/datum/component/butchering/butchering = src.GetComponent(/datum/component/butchering)
- butchering.butchering_enabled = on
-
- if(on)
- hitsound = 'sound/weapons/chainsawhit.ogg'
- else
- hitsound = "swing_hit"
-
-/obj/item/twohanded/required/chainsaw/update_icon_state()
- icon_state = "chainsaw_[on ? "on" : "off"]"
-
-/obj/item/twohanded/required/chainsaw/get_dismemberment_chance()
- if(wielded)
- . = ..()
-
-/obj/item/twohanded/required/chainsaw/doomslayer
- name = "THE GREAT COMMUNICATOR"
- desc = "VRRRRRRR!!!"
- armour_penetration = 100
- force_on = 30
-
-/obj/item/twohanded/required/chainsaw/doomslayer/check_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
- block_return[BLOCK_RETURN_REFLECT_PROJECTILE_CHANCE] = 100
- return ..()
-
-/obj/item/twohanded/required/chainsaw/doomslayer/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
- if(attack_type & ATTACK_TYPE_PROJECTILE)
- owner.visible_message("Ranged attacks just make [owner] angrier!")
- playsound(src, pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 75, 1)
- return BLOCK_SUCCESS | BLOCK_PHYSICAL_EXTERNAL
- return ..()
-
-//GREY TIDE
-/obj/item/twohanded/spear/grey_tide
- icon_state = "spearglass0"
- name = "\improper Grey Tide"
- desc = "Recovered from the aftermath of a revolt aboard Defense Outpost Theta Aegis, in which a seemingly endless tide of Assistants caused heavy casualities among Nanotrasen military forces."
- force_unwielded = 15
- force_wielded = 25
- throwforce = 20
- throw_speed = 4
- attack_verb = list("gored")
- var/clonechance = 50
- var/clonedamage = 12
- var/clonespeed = 0
- var/clone_replication_chance = 30
- var/clone_lifespan = 100
-
-/obj/item/twohanded/spear/grey_tide/afterattack(atom/movable/AM, mob/living/user, proximity)
- . = ..()
- if(!proximity)
- return
- user.faction |= "greytide([REF(user)])"
- if(isliving(AM))
- var/mob/living/L = AM
- if(istype (L, /mob/living/simple_animal/hostile/illusion))
- return
- if(!L.stat && prob(clonechance))
- var/mob/living/simple_animal/hostile/illusion/M = new(user.loc)
- M.faction = user.faction.Copy()
- M.set_varspeed(clonespeed)
- M.Copy_Parent(user, clone_lifespan, user.health/2.5, clonedamage, clone_replication_chance)
- M.GiveTarget(L)
-
-/obj/item/twohanded/pitchfork
- icon_state = "pitchfork0"
- lefthand_file = 'icons/mob/inhands/weapons/polearms_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/weapons/polearms_righthand.dmi'
- name = "pitchfork"
- desc = "A simple tool used for moving hay."
- force = 7
- throwforce = 15
- w_class = WEIGHT_CLASS_BULKY
- force_unwielded = 7
- force_wielded = 15
- attack_verb = list("attacked", "impaled", "pierced")
- hitsound = 'sound/weapons/bladeslice.ogg'
- sharpness = IS_SHARP
- max_integrity = 200
- armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 30)
- resistance_flags = FIRE_PROOF
-
-/obj/item/twohanded/pitchfork/Initialize(mapload)
- AddElement(/datum/element/sword_point)
-
-/obj/item/twohanded/pitchfork/demonic
- name = "demonic pitchfork"
- desc = "A red pitchfork, it looks like the work of the devil."
- force = 19
- throwforce = 24
- force_unwielded = 19
- force_wielded = 25
-
-/obj/item/twohanded/pitchfork/demonic/Initialize()
- . = ..()
- set_light(3,6,LIGHT_COLOR_RED)
-
-/obj/item/twohanded/pitchfork/demonic/greater
- force = 24
- throwforce = 50
- force_unwielded = 24
- force_wielded = 34
-
-/obj/item/twohanded/pitchfork/demonic/ascended
- force = 100
- throwforce = 100
- force_unwielded = 100
- force_wielded = 500000 // Kills you DEAD.
-
-/obj/item/twohanded/pitchfork/update_icon_state()
- icon_state = "pitchfork[wielded]"
-
-/obj/item/twohanded/pitchfork/suicide_act(mob/user)
- user.visible_message("[user] impales [user.p_them()]self in [user.p_their()] abdomen with [src]! It looks like [user.p_theyre()] trying to commit suicide!")
- return (BRUTELOSS)
-
-/obj/item/twohanded/pitchfork/demonic/pickup(mob/living/user)
- . = ..()
- if(isliving(user) && user.mind && user.owns_soul() && !is_devil(user))
- var/mob/living/U = user
- U.visible_message("As [U] picks [src] up, [U]'s arms briefly catch fire.", \
- "\"As you pick up [src] your arms ignite, reminding you of all your past sins.\"")
- if(ishuman(U))
- var/mob/living/carbon/human/H = U
- H.apply_damage(rand(force/2, force), BURN, pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
- else
- U.adjustFireLoss(rand(force/2,force))
-
-/obj/item/twohanded/pitchfork/demonic/attack(mob/target, mob/living/carbon/human/user)
- if(user.mind && user.owns_soul() && !is_devil(user))
- to_chat(user, "[src] burns in your hands.")
- user.apply_damage(rand(force/2, force), BURN, pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
- ..()
-
-/obj/item/twohanded/pitchfork/demonic/ascended/afterattack(atom/target, mob/user, proximity)
- . = ..()
- if(!proximity || !wielded)
- return
- if(iswallturf(target))
- var/turf/closed/wall/W = target
- user.visible_message("[user] blasts \the [target] with \the [src]!")
- playsound(target, 'sound/magic/disintegrate.ogg', 100, 1)
- W.break_wall()
- W.ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
- return
-
-//HF blade
-
-/obj/item/twohanded/vibro_weapon
- icon_state = "hfrequency0"
- lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
- name = "vibro sword"
- desc = "A potent weapon capable of cutting through nearly anything. Wielding it in two hands will allow you to deflect gunfire."
- force_unwielded = 20
- force_wielded = 40
- armour_penetration = 100
- block_chance = 40
- throwforce = 20
- throw_speed = 4
- sharpness = IS_SHARP
- attack_verb = list("cut", "sliced", "diced")
- w_class = WEIGHT_CLASS_BULKY
- slot_flags = ITEM_SLOT_BACK
- hitsound = 'sound/weapons/bladeslice.ogg'
-
-/obj/item/twohanded/vibro_weapon/Initialize()
- . = ..()
- AddComponent(/datum/component/butchering, 20, 105)
- AddElement(/datum/element/sword_point)
-
-/obj/item/twohanded/vibro_weapon/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
- if(wielded)
- final_block_chance *= 2
- if(wielded || !(attack_type & ATTACK_TYPE_PROJECTILE))
- if(prob(final_block_chance))
- if(attack_type & ATTACK_TYPE_PROJECTILE)
- owner.visible_message("[owner] deflects [attack_text] with [src]!")
- playsound(src, pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 75, 1)
- block_return[BLOCK_RETURN_REDIRECT_METHOD] = REDIRECT_METHOD_DEFLECT
- return BLOCK_SUCCESS | BLOCK_REDIRECTED | BLOCK_SHOULD_REDIRECT | BLOCK_PHYSICAL_EXTERNAL
- else
- owner.visible_message("[owner] parries [attack_text] with [src]!")
- return BLOCK_SUCCESS | BLOCK_PHYSICAL_EXTERNAL
- return NONE
-
-/obj/item/twohanded/vibro_weapon/update_icon_state()
- icon_state = "hfrequency[wielded]"
-
-/*
- * Bone Axe
- */
-/obj/item/twohanded/fireaxe/boneaxe // Blatant imitation of the fireaxe, but made out of bone.
- icon_state = "bone_axe0"
- name = "bone axe"
- desc = "A large, vicious axe crafted out of several sharpened bone plates and crudely tied together. Made of monsters, by killing monsters, for killing monsters."
- force_wielded = 23
-
-/obj/item/twohanded/fireaxe/boneaxe/update_icon_state()
- icon_state = "bone_axe[wielded]"
-
-/*
- * Bone Spear
- */
-/obj/item/twohanded/bonespear //Blatant imitation of spear, but made out of bone. Not valid for explosive modification.
- icon_state = "bone_spear0"
- lefthand_file = 'icons/mob/inhands/weapons/polearms_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/weapons/polearms_righthand.dmi'
- name = "bone spear"
- desc = "A haphazardly-constructed yet still deadly weapon. The pinnacle of modern technology."
- force = 11
- w_class = WEIGHT_CLASS_BULKY
- slot_flags = ITEM_SLOT_BACK
- force_unwielded = 11
- force_wielded = 20 //I have no idea how to balance
- reach = 2
- throwforce = 22
- throw_speed = 4
- embedding = list("embedded_impact_pain_multiplier" = 3)
- armour_penetration = 15 //Enhanced armor piercing
- hitsound = 'sound/weapons/bladeslice.ogg'
- attack_verb = list("attacked", "poked", "jabbed", "torn", "gored")
- sharpness = IS_SHARP
-
-/obj/item/twohanded/bonespear/update_icon_state()
- icon_state = "bone_spear[wielded]"
-
-/obj/item/twohanded/binoculars
- name = "binoculars"
- desc = "Used for long-distance surveillance."
- item_state = "binoculars"
- icon_state = "binoculars"
- lefthand_file = 'icons/mob/inhands/items_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/items_righthand.dmi'
- slot_flags = ITEM_SLOT_BELT
- w_class = WEIGHT_CLASS_SMALL
- var/mob/listeningTo
- var/zoom_out_amt = 6
- var/zoom_amt = 10
-
-/obj/item/twohanded/binoculars/Destroy()
- listeningTo = null
- return ..()
-
-/obj/item/twohanded/binoculars/wield(mob/user)
- . = ..()
- if(!wielded)
- return
- RegisterSignal(user, COMSIG_MOVABLE_MOVED, .proc/unwield)
- listeningTo = user
- user.visible_message("[user] holds [src] up to [user.p_their()] eyes.","You hold [src] up to your eyes.")
- item_state = "binoculars_wielded"
- user.regenerate_icons()
- if(!user?.client)
- return
- var/client/C = user.client
- var/_x = 0
- var/_y = 0
- switch(user.dir)
- if(NORTH)
- _y = zoom_amt
- if(EAST)
- _x = zoom_amt
- if(SOUTH)
- _y = -zoom_amt
- if(WEST)
- _x = -zoom_amt
- C.change_view(world.view + zoom_out_amt)
- C.pixel_x = world.icon_size*_x
- C.pixel_y = world.icon_size*_y
-
-/obj/item/twohanded/binoculars/unwield(mob/user)
- . = ..()
- UnregisterSignal(listeningTo, COMSIG_MOVABLE_MOVED)
- listeningTo = null
- user.visible_message("[user] lowers [src].","You lower [src].")
- item_state = "binoculars"
- user.regenerate_icons()
- if(user && user.client)
- user.regenerate_icons()
- var/client/C = user.client
- C.change_view(CONFIG_GET(string/default_view))
- user.client.pixel_x = 0
- user.client.pixel_y = 0
-
-/obj/item/twohanded/electrostaff
- icon = 'icons/obj/items_and_weapons.dmi'
- icon_state = "electrostaff"
- item_state = "electrostaff"
- lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi'
- name = "riot suppression electrostaff"
- desc = "A large quarterstaff, with massive silver electrodes mounted at the end."
- w_class = WEIGHT_CLASS_HUGE
- slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_OCLOTHING
- force_unwielded = 5
- force_wielded = 10
- throwforce = 15 //if you are a madman and finish someone off with this, power to you.
- throw_speed = 1
- item_flags = NO_MAT_REDEMPTION | SLOWS_WHILE_IN_HAND | ITEM_CAN_BLOCK | ITEM_CAN_PARRY
- block_parry_data = /datum/block_parry_data/electrostaff
- attack_verb = list("struck", "beaten", "thwacked", "pulped")
- total_mass = 5 //yeah this is a heavy thing, beating people with it while it's off is not going to do you any favors. (to curb stun-kill rampaging without it being on)
- var/obj/item/stock_parts/cell/cell = /obj/item/stock_parts/cell/high
- var/on = FALSE
- var/lethal_cost = 400 //10000/400*20 = 500. decent enough?
- var/lethal_damage = 20
- var/lethal_stam_cost = 4
- var/stun_cost = 333 //10000/333*25 = 750. stunbatons are at time of writing 10000/1000*49 = 490.
- var/stun_status_effect = STATUS_EFFECT_ELECTROSTAFF //a small slowdown effect
- var/stun_stamdmg = 40
- var/stun_status_duration = 25
- var/stun_stam_cost = 3.5
-
-// haha security desword time /s
-/datum/block_parry_data/electrostaff
- block_damage_absorption = 0
- block_damage_multiplier = 1
- can_block_attack_types = ~ATTACK_TYPE_PROJECTILE // only able to parry non projectiles
- block_damage_multiplier_override = list(
- TEXT_ATTACK_TYPE_MELEE = 0.5, // only useful on melee and unarmed
- TEXT_ATTACK_TYPE_UNARMED = 0.3
- )
- block_start_delay = 0.5 // near instantaneous block
- block_stamina_cost_per_second = 3
- block_stamina_efficiency = 2 // haha this is a horrible idea
- // more slowdown that deswords because security
- block_slowdown = 2
- // no attacking while blocking
- block_lock_attacking = TRUE
-
- parry_time_windup = 1
- parry_time_active = 5
- parry_time_spindown = 0
- parry_time_spindown_visual_override = 1
- parry_flags = PARRY_DEFAULT_HANDLE_FEEDBACK | PARRY_LOCK_ATTACKING // no attacking while parrying
- parry_time_perfect = 0
- parry_time_perfect_leeway = 0.5
- parry_efficiency_perfect = 100
- parry_imperfect_falloff_percent = 1
- parry_imperfect_falloff_percent_override = list(
- TEXT_ATTACK_TYPE_PROJECTILE = 45 // really crappy vs projectiles
- )
- parry_time_perfect_leeway_override = list(
- TEXT_ATTACK_TYPE_PROJECTILE = 1 // extremely harsh window for projectiles
- )
- // not extremely punishing to fail, but no spamming the parry.
- parry_cooldown = 2.5 SECONDS
- parry_failed_stagger_duration = 1.5 SECONDS
- parry_failed_clickcd_duration = 1 SECONDS
-
-/obj/item/twohanded/electrostaff/Initialize(mapload)
- . = ..()
- if(ispath(cell))
- cell = new cell
-
-/obj/item/twohanded/electrostaff/Destroy()
- STOP_PROCESSING(SSobj, src)
- return ..()
-
-/obj/item/twohanded/electrostaff/get_cell()
- . = cell
- if(iscyborg(loc))
- var/mob/living/silicon/robot/R = loc
- . = R.get_cell()
-
-/obj/item/twohanded/electrostaff/proc/min_hitcost()
- return min(stun_cost, lethal_cost)
-
-/obj/item/twohanded/electrostaff/proc/turn_on(mob/user, silent = FALSE)
- if(on)
- return
- if(!cell)
- if(user)
- to_chat(user, "[src] has no cell.")
- return
- if(cell.charge < min_hitcost())
- if(user)
- to_chat(user, "[src] is out of charge.")
- return
- on = TRUE
- START_PROCESSING(SSobj, src)
- if(user)
- to_chat(user, "You turn [src] on.")
- update_icon()
- if(!silent)
- playsound(src, "sparks", 75, 1, -1)
-
-/obj/item/twohanded/electrostaff/proc/turn_off(mob/user, silent = FALSE)
- if(!on)
- return
- if(user)
- to_chat(user, "You turn [src] off.")
- on = FALSE
- STOP_PROCESSING(SSobj, src)
- update_icon()
- if(!silent)
- playsound(src, "sparks", 75, 1, -1)
-
-/obj/item/twohanded/electrostaff/proc/toggle(mob/user, silent = FALSE)
- if(on)
- turn_off(user, silent)
- else
- turn_on(user, silent)
-
-/obj/item/twohanded/electrostaff/wield(mob/user)
- . = ..()
- if(wielded)
- turn_on(user)
- add_fingerprint(user)
-
-/obj/item/twohanded/electrostaff/unwield(mob/user)
- . = ..()
- if(!wielded)
- turn_off(user)
- add_fingerprint(user)
-
-/obj/item/twohanded/electrostaff/update_icon_state()
- . = ..()
- if(!wielded)
- icon_state = "electrostaff"
- item_state = "electrostaff"
- else
- icon_state = item_state = (on? "electrostaff_1" : "electrostaff_0")
- set_light(7, on? 1 : 0, LIGHT_COLOR_CYAN)
-
-/obj/item/twohanded/electrostaff/examine(mob/living/user)
- . = ..()
- if(cell)
- . += "The cell charge is [round(cell.percent())]%."
- else
- . += "There is no cell installed!"
-
-/obj/item/twohanded/electrostaff/attackby(obj/item/W, mob/user, params)
- if(istype(W, /obj/item/stock_parts/cell))
- var/obj/item/stock_parts/cell/C = W
- if(cell)
- to_chat(user, "[src] already has a cell!")
- else
- if(C.maxcharge < min_hit_cost())
- to_chat(user, "[src] requires a higher capacity cell.")
- return
- if(!user.transferItemToLoc(W, src))
- return
- cell = C
- to_chat(user, "You install a cell in [src].")
-
- else if(W.tool_behaviour == TOOL_SCREWDRIVER)
- if(cell)
- cell.update_icon()
- cell.forceMove(get_turf(src))
- cell = null
- to_chat(user, "You remove the cell from [src].")
- turn_off(user, TRUE)
- else
- return ..()
-
-/obj/item/twohanded/electrostaff/process()
- deductcharge(50) //Wasteful!
-
-/obj/item/twohanded/electrostaff/proc/min_hit_cost()
- return min(lethal_cost, stun_cost)
-
-/obj/item/twohanded/electrostaff/proc/deductcharge(amount)
- var/obj/item/stock_parts/cell/C = get_cell()
- if(!C)
- turn_off()
- return FALSE
- C.use(min(amount, C.charge))
- if(QDELETED(src))
- return FALSE
- if(C.charge < min_hit_cost())
- turn_off()
-
-/obj/item/twohanded/electrostaff/attack(mob/living/target, mob/living/user)
- if(IS_STAMCRIT(user))//CIT CHANGE - makes it impossible to baton in stamina softcrit
- to_chat(user, "You're too exhausted to use [src] properly.")//CIT CHANGE - ditto
- return //CIT CHANGE - ditto
- if(on && HAS_TRAIT(user, TRAIT_CLUMSY) && prob(50))
- clowning_around(user) //ouch!
- return
- if(iscyborg(target))
- return ..()
- var/list/return_list = list()
- if(target.mob_run_block(src, 0, "[user]'s [name]", ATTACK_TYPE_MELEE, 0, user, null, return_list) & BLOCK_SUCCESS) //No message; run_block() handles that
- playsound(target, 'sound/weapons/genhit.ogg', 50, 1)
- return FALSE
- if(user.a_intent != INTENT_HARM)
- if(stun_act(target, user, null, return_list))
- user.do_attack_animation(target)
- user.adjustStaminaLossBuffered(stun_stam_cost)
- return
- else if(!harm_act(target, user, null, return_list))
- return ..() //if you can't fry them just beat them with it
- else //we did harm act them
- user.do_attack_animation(target)
- user.adjustStaminaLossBuffered(lethal_stam_cost)
-
-/obj/item/twohanded/electrostaff/proc/stun_act(mob/living/target, mob/living/user, no_charge_and_force = FALSE, list/block_return = list())
- var/stunforce = block_calculate_resultant_damage(stun_stamdmg, block_return)
- if(!no_charge_and_force)
- if(!on)
- target.visible_message("[user] has bapped [target] with [src]. Luckily it was off.", \
- "[user] has bapped you with [src]. Luckily it was off")
- turn_off() //if it wasn't already off
- return FALSE
- var/obj/item/stock_parts/cell/C = get_cell()
- var/chargeleft = C.charge
- deductcharge(stun_cost)
- if(QDELETED(src) || QDELETED(C)) //boom
- return FALSE
- if(chargeleft < stun_cost)
- stunforce *= round(chargeleft/stun_cost, 0.1)
- target.adjustStaminaLoss(stunforce)
- target.apply_effect(EFFECT_STUTTER, stunforce)
- SEND_SIGNAL(target, COMSIG_LIVING_MINOR_SHOCK)
- if(user)
- target.lastattacker = user.real_name
- target.lastattackerckey = user.ckey
- target.visible_message("[user] has shocked [target] with [src]!", \
- "[user] has shocked you with [src]!")
- log_combat(user, target, "stunned with an electrostaff")
- playsound(src, 'sound/weapons/staff.ogg', 50, 1, -1)
- target.apply_status_effect(stun_status_effect, stun_status_duration)
- if(ishuman(user))
- var/mob/living/carbon/human/H = user
- H.forcesay(GLOB.hit_appends)
- return TRUE
-
-/obj/item/twohanded/electrostaff/proc/harm_act(mob/living/target, mob/living/user, no_charge_and_force = FALSE, list/block_return = list())
- var/lethal_force = block_calculate_resultant_damage(lethal_damage, block_return)
- if(!no_charge_and_force)
- if(!on)
- return FALSE //standard item attack
- var/obj/item/stock_parts/cell/C = get_cell()
- var/chargeleft = C.charge
- deductcharge(lethal_cost)
- if(QDELETED(src) || QDELETED(C)) //boom
- return FALSE
- if(chargeleft < stun_cost)
- lethal_force *= round(chargeleft/lethal_cost, 0.1)
- target.adjustFireLoss(lethal_force) //good against ointment spam
- SEND_SIGNAL(target, COMSIG_LIVING_MINOR_SHOCK)
- if(user)
- target.lastattacker = user.real_name
- target.lastattackerckey = user.ckey
- target.visible_message("[user] has seared [target] with [src]!", \
- "[user] has seared you with [src]!")
- log_combat(user, target, "burned with an electrostaff")
- playsound(src, 'sound/weapons/sear.ogg', 50, 1, -1)
- return TRUE
-
-/obj/item/twohanded/electrostaff/proc/clowning_around(mob/living/user)
- 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)
- harm_act(user, user, TRUE)
- stun_act(user, user, TRUE)
- deductcharge(lethal_cost)
-
-/obj/item/twohanded/electrostaff/emp_act(severity)
- . = ..()
- if (!(. & EMP_PROTECT_SELF))
- turn_off()
- if(!iscyborg(loc))
- deductcharge(1000 / severity, TRUE, FALSE)
-
-/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
- if(!isturf(target)) //read: Mob inventories.
- return
- 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()
diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm
index 67dcd71e65..7170ba8be7 100644
--- a/code/game/objects/items/weaponry.dm
+++ b/code/game/objects/items/weaponry.dm
@@ -263,7 +263,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
/obj/item/wirerod/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/shard))
- var/obj/item/twohanded/spear/S = new /obj/item/twohanded/spear
+ var/obj/item/spear/S = new /obj/item/spear
remove_item_from_storage(user)
if (!user.transferItemToLoc(I, S))
@@ -475,7 +475,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
/obj/item/mounted_chainsaw/Destroy()
var/obj/item/bodypart/part
- new /obj/item/twohanded/required/chainsaw(get_turf(src))
+ new /obj/item/chainsaw(get_turf(src))
if(iscarbon(loc))
var/mob/living/carbon/holder = loc
var/index = holder.get_held_index_of_item(src)
@@ -706,6 +706,92 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
item_flags = DROPDEL | ABSTRACT
attack_verb = list("bopped")
+/obj/item/circlegame/Initialize()
+ . = ..()
+ var/mob/living/owner = loc
+ if(!istype(owner))
+ return
+ RegisterSignal(owner, COMSIG_PARENT_EXAMINE, .proc/ownerExamined)
+
+/obj/item/circlegame/Destroy()
+ var/mob/owner = loc
+ if(istype(owner))
+ UnregisterSignal(owner, COMSIG_PARENT_EXAMINE)
+ return ..()
+
+/obj/item/circlegame/dropped(mob/user)
+ UnregisterSignal(user, COMSIG_PARENT_EXAMINE) //loc will have changed by the time this is called, so Destroy() can't catch it
+ // this is a dropdel item.
+ return ..()
+
+/// Stage 1: The mistake is made
+/obj/item/circlegame/proc/ownerExamined(mob/living/owner, mob/living/sucker)
+ if(!istype(sucker) || !in_range(owner, sucker))
+ return
+ addtimer(CALLBACK(src, .proc/waitASecond, owner, sucker), 4)
+
+/// Stage 2: Fear sets in
+/obj/item/circlegame/proc/waitASecond(mob/living/owner, mob/living/sucker)
+ if(QDELETED(sucker) || QDELETED(src) || QDELETED(owner))
+ return
+
+ if(owner == sucker) // big mood
+ to_chat(owner, "Wait a second... you just looked at your own [src.name]!")
+ addtimer(CALLBACK(src, .proc/selfGottem, owner), 10)
+ else
+ to_chat(sucker, "Wait a second... was that a-")
+ addtimer(CALLBACK(src, .proc/GOTTEM, owner, sucker), 6)
+
+/// Stage 3A: We face our own failures
+/obj/item/circlegame/proc/selfGottem(mob/living/owner)
+ if(QDELETED(src) || QDELETED(owner))
+ return
+
+ playsound(get_turf(owner), 'sound/effects/hit_punch.ogg', 50, TRUE, -1)
+ owner.visible_message("[owner] shamefully bops [owner.p_them()]self with [owner.p_their()] [src.name].", "You shamefully bop yourself with your [src.name].", \
+ "You hear a dull thud!")
+ log_combat(owner, owner, "bopped", src.name, "(self)")
+ owner.do_attack_animation(owner)
+ owner.apply_damage(100, STAMINA)
+ owner.Knockdown(10)
+ qdel(src)
+
+/// Stage 3B: We face our reckoning (unless we moved away or they're incapacitated)
+/obj/item/circlegame/proc/GOTTEM(mob/living/owner, mob/living/sucker)
+ if(QDELETED(sucker))
+ return
+
+ if(QDELETED(src) || QDELETED(owner))
+ to_chat(sucker, "Nevermind... must've been your imagination...")
+ return
+
+ if(!in_range(owner, sucker) || !(owner.mobility_flags & MOBILITY_USE))
+ to_chat(sucker, "Phew... you moved away before [owner] noticed you saw [owner.p_their()] [src.name]...")
+ return
+
+ to_chat(owner, "[sucker] looks down at your [src.name] before trying to avert [sucker.p_their()] eyes, but it's too late!")
+ to_chat(sucker, "[owner] sees the fear in your eyes as you try to look away from [owner.p_their()] [src.name]!")
+
+ playsound(get_turf(owner), 'sound/effects/hit_punch.ogg', 50, TRUE, -1)
+ owner.do_attack_animation(sucker)
+
+ if(HAS_TRAIT(owner, TRAIT_HULK))
+ owner.visible_message("[owner] bops [sucker] with [owner.p_their()] [src.name] much harder than intended, sending [sucker.p_them()] flying!", \
+ "You bop [sucker] with your [src.name] much harder than intended, sending [sucker.p_them()] flying!", "You hear a sickening sound of flesh hitting flesh!", ignored_mobs=list(sucker))
+ to_chat(sucker, "[owner] bops you incredibly hard with [owner.p_their()] [src.name], sending you flying!")
+ sucker.apply_damage(50, STAMINA)
+ sucker.Knockdown(50)
+ log_combat(owner, sucker, "bopped", src.name, "(setup- Hulk)")
+ var/atom/throw_target = get_edge_target_turf(sucker, owner.dir)
+ sucker.throw_at(throw_target, 6, 3, owner)
+ else
+ owner.visible_message("[owner] bops [sucker] with [owner.p_their()] [src.name]!", "You bop [sucker] with your [src.name]!", \
+ "You hear a dull thud!", ignored_mobs=list(sucker))
+ sucker.apply_damage(15, STAMINA)
+ log_combat(owner, sucker, "bopped", src.name, "(setup)")
+ to_chat(sucker, "[owner] bops you with [owner.p_their()] [src.name]!")
+ qdel(src)
+
/obj/item/slapper
name = "slapper"
desc = "This is how real men fight."
@@ -759,3 +845,59 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
to_chat(user, "[M] is too close to use [src] on.")
return
M.attack_hand(user)
+
+//HF blade
+
+/obj/item/vibro_weapon
+ icon_state = "hfrequency0"
+ lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
+ name = "vibro sword"
+ desc = "A potent weapon capable of cutting through nearly anything. Wielding it in two hands will allow you to deflect gunfire."
+ armour_penetration = 100
+ block_chance = 40
+ throwforce = 20
+ throw_speed = 4
+ sharpness = IS_SHARP
+ attack_verb = list("cut", "sliced", "diced")
+ w_class = WEIGHT_CLASS_BULKY
+ slot_flags = ITEM_SLOT_BACK
+ hitsound = 'sound/weapons/bladeslice.ogg'
+ var/wielded = FALSE // track wielded status on item
+
+/obj/item/vibro_weapon/Initialize()
+ . = ..()
+ RegisterSignal(src, COMSIG_TWOHANDED_WIELD, .proc/on_wield)
+ RegisterSignal(src, COMSIG_TWOHANDED_UNWIELD, .proc/on_unwield)
+
+/obj/item/vibro_weapon/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/butchering, 20, 105)
+ AddComponent(/datum/component/two_handed, force_multiplier=2, icon_wielded="hfrequency1")
+ AddElement(/datum/element/sword_point)
+
+/// triggered on wield of two handed item
+/obj/item/vibro_weapon/proc/on_wield(obj/item/source, mob/user)
+ wielded = TRUE
+
+/// triggered on unwield of two handed item
+/obj/item/vibro_weapon/proc/on_unwield(obj/item/source, mob/user)
+ wielded = FALSE
+
+/obj/item/vibro_weapon/update_icon_state()
+ icon_state = "hfrequency0"
+
+/obj/item/vibro_weapon/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
+ if(wielded)
+ final_block_chance *= 2
+ if(wielded || !(attack_type & ATTACK_TYPE_PROJECTILE))
+ if(prob(final_block_chance))
+ if(attack_type & ATTACK_TYPE_PROJECTILE)
+ owner.visible_message("[owner] deflects [attack_text] with [src]!")
+ playsound(src, pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 75, 1)
+ block_return[BLOCK_RETURN_REDIRECT_METHOD] = REDIRECT_METHOD_DEFLECT
+ return BLOCK_SUCCESS | BLOCK_REDIRECTED | BLOCK_SHOULD_REDIRECT | BLOCK_PHYSICAL_EXTERNAL
+ else
+ owner.visible_message("[owner] parries [attack_text] with [src]!")
+ return BLOCK_SUCCESS | BLOCK_PHYSICAL_EXTERNAL
+ return NONE
diff --git a/code/game/objects/obj_defense.dm b/code/game/objects/obj_defense.dm
index 54ac2a5559..1203c5a1df 100644
--- a/code/game/objects/obj_defense.dm
+++ b/code/game/objects/obj_defense.dm
@@ -103,6 +103,8 @@
take_damage(400, BRUTE, "melee", 0, get_dir(src, B))
/obj/proc/attack_generic(mob/user, damage_amount = 0, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, armor_penetration = 0) //used by attack_alien, attack_animal, and attack_slime
+ if(SEND_SIGNAL(src, COMSIG_OBJ_ATTACK_GENERIC, user, damage_amount, damage_type, damage_flag, sound_effect, armor_penetration) & COMPONENT_STOP_GENERIC_ATTACK)
+ return FALSE
user.do_attack_animation(src)
user.changeNext_move(CLICK_CD_MELEE)
return take_damage(damage_amount, damage_type, damage_flag, sound_effect, get_dir(src, user), armor_penetration)
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index ba3eba9bd3..fb3b1535ed 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -37,13 +37,9 @@
if("anchored")
setAnchored(vval)
return TRUE
- if("obj_flags")
+ if(NAMEOF(src, obj_flags))
if ((obj_flags & DANGEROUS_POSSESSION) && !(vval & DANGEROUS_POSSESSION))
return FALSE
- if("control_object")
- var/obj/O = vval
- if(istype(O) && (O.obj_flags & DANGEROUS_POSSESSION))
- return FALSE
return ..()
/obj/Initialize()
diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm
index d30f617919..cce4955210 100644
--- a/code/game/objects/structures.dm
+++ b/code/game/objects/structures.dm
@@ -28,7 +28,7 @@
queue_smooth_neighbors(src)
return ..()
-/obj/structure/attack_hand(mob/user)
+/obj/structure/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/objects/structures/aliens.dm b/code/game/objects/structures/aliens.dm
index a793459c5d..a6cd8df746 100644
--- a/code/game/objects/structures/aliens.dm
+++ b/code/game/objects/structures/aliens.dm
@@ -249,7 +249,7 @@
/obj/structure/alien/egg/attack_alien(mob/living/carbon/alien/user)
return attack_hand(user)
-/obj/structure/alien/egg/attack_hand(mob/living/user)
+/obj/structure/alien/egg/attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/objects/structures/artstuff.dm b/code/game/objects/structures/artstuff.dm
index fcc26d1313..827e9a7b0a 100644
--- a/code/game/objects/structures/artstuff.dm
+++ b/code/game/objects/structures/artstuff.dm
@@ -80,7 +80,7 @@
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "canvas", name, ui_x, ui_y, master_ui, state)
+ ui = new(user, src, ui_key, "Canvas", name, ui_x, ui_y, master_ui, state)
ui.set_autoupdate(FALSE)
ui.open()
diff --git a/code/game/objects/structures/barsigns.dm b/code/game/objects/structures/barsigns.dm
index 821409c26b..7ea678cae4 100644
--- a/code/game/objects/structures/barsigns.dm
+++ b/code/game/objects/structures/barsigns.dm
@@ -52,7 +52,7 @@
/obj/structure/sign/barsign/attack_ai(mob/user)
return attack_hand(user)
-/obj/structure/sign/barsign/attack_hand(mob/user)
+/obj/structure/sign/barsign/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/objects/structures/beds_chairs/chair.dm b/code/game/objects/structures/beds_chairs/chair.dm
index 2225c4c0c2..299ba7b659 100644
--- a/code/game/objects/structures/beds_chairs/chair.dm
+++ b/code/game/objects/structures/beds_chairs/chair.dm
@@ -155,7 +155,7 @@
///Material chair
/obj/structure/chair/greyscale
icon_state = "chair_greyscale"
- material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS | MATERIAL_EFFECTS
+ material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
item_chair = /obj/item/chair/greyscale
buildstacktype = null //Custom mats handle this
@@ -384,7 +384,7 @@
/obj/item/chair/greyscale
icon_state = "chair_greyscale_toppled"
item_state = "chair_greyscale"
- material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS | MATERIAL_EFFECTS
+ material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
origin_type = /obj/structure/chair/greyscale
/obj/item/chair/stool
diff --git a/code/game/objects/structures/bedsheet_bin.dm b/code/game/objects/structures/bedsheet_bin.dm
index a4ee2f16e2..e45ddf650c 100644
--- a/code/game/objects/structures/bedsheet_bin.dm
+++ b/code/game/objects/structures/bedsheet_bin.dm
@@ -311,7 +311,7 @@ LINEN BINS
/obj/structure/bedsheetbin/attack_paw(mob/user)
return attack_hand(user)
-/obj/structure/bedsheetbin/attack_hand(mob/user)
+/obj/structure/bedsheetbin/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm
index 5da04a6686..4b9251d8ec 100644
--- a/code/game/objects/structures/crates_lockers/closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets.dm
@@ -458,7 +458,7 @@
return
container_resist(user)
-/obj/structure/closet/attack_hand(mob/user)
+/obj/structure/closet/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/objects/structures/crates_lockers/closets/genpop.dm b/code/game/objects/structures/crates_lockers/closets/genpop.dm
index 2b263bb1ed..04c2f37b0f 100644
--- a/code/game/objects/structures/crates_lockers/closets/genpop.dm
+++ b/code/game/objects/structures/crates_lockers/closets/genpop.dm
@@ -91,7 +91,7 @@
locked = TRUE
return ..()
-/obj/structure/closet/secure_closet/genpop/attack_hand(mob/user)
+/obj/structure/closet/secure_closet/genpop/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(user.lying && get_dist(src, user) > 0)
return
@@ -114,4 +114,4 @@
return
- ..()
\ No newline at end of file
+ ..()
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 9f4da351fa..ad7680f2f9 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm
@@ -273,8 +273,8 @@
icon_state = "tac"
/obj/structure/closet/secure_closet/lethalshots/PopulateContents()
..()
- new /obj/item/twohanded/electrostaff(src)
- new /obj/item/twohanded/electrostaff(src)
+ new /obj/item/electrostaff(src)
+ new /obj/item/electrostaff(src)
for(var/i in 1 to 3)
new /obj/item/storage/box/lethalshot(src)
diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm
index 2c977a34d6..99bce305f8 100644
--- a/code/game/objects/structures/crates_lockers/crates.dm
+++ b/code/game/objects/structures/crates_lockers/crates.dm
@@ -41,7 +41,7 @@
if(manifest)
. += "manifest"
-/obj/structure/closet/crate/attack_hand(mob/user)
+/obj/structure/closet/crate/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/objects/structures/crates_lockers/crates/large.dm b/code/game/objects/structures/crates_lockers/crates/large.dm
index 880460a23c..d8c2f7f91c 100644
--- a/code/game/objects/structures/crates_lockers/crates/large.dm
+++ b/code/game/objects/structures/crates_lockers/crates/large.dm
@@ -8,7 +8,7 @@
delivery_icon = "deliverybox"
integrity_failure = 0 //Makes the crate break when integrity reaches 0, instead of opening and becoming an invisible sprite.
-/obj/structure/closet/crate/large/attack_hand(mob/user)
+/obj/structure/closet/crate/large/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
add_fingerprint(user)
if(manifest)
tear_manifest(user)
@@ -40,4 +40,4 @@
else
to_chat(user, "You need a crowbar to pry this open!")
return FALSE //Just stop. Do nothing. Don't turn into an invisible sprite. Don't open like a locker.
- //The large crate has no non-attack interactions other than the crowbar, anyway.
\ No newline at end of file
+ //The large crate has no non-attack interactions other than the crowbar, anyway.
diff --git a/code/game/objects/structures/displaycase.dm b/code/game/objects/structures/displaycase.dm
index c332a07edf..4db889b392 100644
--- a/code/game/objects/structures/displaycase.dm
+++ b/code/game/objects/structures/displaycase.dm
@@ -157,7 +157,7 @@
/obj/structure/displaycase/attack_paw(mob/user)
return attack_hand(user)
-/obj/structure/displaycase/attack_hand(mob/user)
+/obj/structure/displaycase/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/objects/structures/divine.dm b/code/game/objects/structures/divine.dm
index bca96e67d1..5afe97f669 100644
--- a/code/game/objects/structures/divine.dm
+++ b/code/game/objects/structures/divine.dm
@@ -7,7 +7,7 @@
density = FALSE
can_buckle = 1
-/obj/structure/sacrificealtar/attack_hand(mob/living/user)
+/obj/structure/sacrificealtar/attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
@@ -30,7 +30,7 @@
var/time_between_uses = 1800
var/last_process = 0
-/obj/structure/healingfountain/attack_hand(mob/living/user)
+/obj/structure/healingfountain/attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
@@ -48,4 +48,4 @@
if(last_process + time_between_uses > world.time)
icon_state = "fountain"
else
- icon_state = "fountain-red"
\ No newline at end of file
+ icon_state = "fountain-red"
diff --git a/code/game/objects/structures/dresser.dm b/code/game/objects/structures/dresser.dm
index 528153324d..bacb6fc92b 100644
--- a/code/game/objects/structures/dresser.dm
+++ b/code/game/objects/structures/dresser.dm
@@ -19,7 +19,7 @@
new /obj/item/stack/sheet/mineral/wood(drop_location(), 10)
qdel(src)
-/obj/structure/dresser/attack_hand(mob/user)
+/obj/structure/dresser/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(. || !ishuman(user) || !user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
return
diff --git a/code/game/objects/structures/extinguisher.dm b/code/game/objects/structures/extinguisher.dm
index 3bc84deb4d..a279070e69 100644
--- a/code/game/objects/structures/extinguisher.dm
+++ b/code/game/objects/structures/extinguisher.dm
@@ -69,7 +69,7 @@
return ..()
-/obj/structure/extinguisher_cabinet/attack_hand(mob/user)
+/obj/structure/extinguisher_cabinet/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/objects/structures/false_walls.dm b/code/game/objects/structures/false_walls.dm
index 679a755321..eaa6e574cd 100644
--- a/code/game/objects/structures/false_walls.dm
+++ b/code/game/objects/structures/false_walls.dm
@@ -41,7 +41,7 @@
new /obj/structure/falsewall/brass(loc)
qdel(src)
-/obj/structure/falsewall/attack_hand(mob/user)
+/obj/structure/falsewall/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(opening)
return
. = ..()
@@ -180,7 +180,7 @@
radiate()
return ..()
-/obj/structure/falsewall/uranium/attack_hand(mob/user)
+/obj/structure/falsewall/uranium/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
radiate()
. = ..()
diff --git a/code/game/objects/structures/femur_breaker.dm b/code/game/objects/structures/femur_breaker.dm
index 7331285161..1fa21d119e 100644
--- a/code/game/objects/structures/femur_breaker.dm
+++ b/code/game/objects/structures/femur_breaker.dm
@@ -32,7 +32,7 @@
if (LAZYLEN(buckled_mobs))
. += "Someone appears to be strapped in. You can help them unbuckle, or activate the femur breaker."
-/obj/structure/femur_breaker/attack_hand(mob/user)
+/obj/structure/femur_breaker/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
add_fingerprint(user)
// Currently being used
diff --git a/code/game/objects/structures/fence.dm b/code/game/objects/structures/fence.dm
index 35891fa607..5803fb1306 100644
--- a/code/game/objects/structures/fence.dm
+++ b/code/game/objects/structures/fence.dm
@@ -120,7 +120,7 @@
open = TRUE
density = TRUE
-/obj/structure/fence/door/attack_hand(mob/user)
+/obj/structure/fence/door/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(can_open(user))
toggle(user)
@@ -156,4 +156,4 @@
#undef NO_HOLE
#undef MEDIUM_HOLE
#undef LARGE_HOLE
-#undef MAX_HOLE_SIZE
\ No newline at end of file
+#undef MAX_HOLE_SIZE
diff --git a/code/game/objects/structures/fireaxe.dm b/code/game/objects/structures/fireaxe.dm
index f4c1dd5ab9..de88372b1e 100644
--- a/code/game/objects/structures/fireaxe.dm
+++ b/code/game/objects/structures/fireaxe.dm
@@ -11,7 +11,7 @@
integrity_failure = 0.33
var/locked = TRUE
var/open = FALSE
- var/obj/item/twohanded/fireaxe/fireaxe
+ var/obj/item/fireaxe/fireaxe
/obj/structure/fireaxecabinet/Initialize()
. = ..()
@@ -50,8 +50,8 @@
obj_integrity = max_integrity
update_icon()
else if(open || broken)
- if(istype(I, /obj/item/twohanded/fireaxe) && !fireaxe)
- var/obj/item/twohanded/fireaxe/F = I
+ if(istype(I, /obj/item/fireaxe) && !fireaxe)
+ var/obj/item/fireaxe/F = I
if(F.wielded)
to_chat(user, "Unwield the [F.name] first.")
return
@@ -105,7 +105,7 @@
fireaxe = null
qdel(src)
-/obj/structure/fireaxecabinet/attack_hand(mob/user)
+/obj/structure/fireaxecabinet/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/objects/structures/flora.dm b/code/game/objects/structures/flora.dm
index c1f8af43f0..6d72bb58b2 100644
--- a/code/game/objects/structures/flora.dm
+++ b/code/game/objects/structures/flora.dm
@@ -65,7 +65,7 @@
var/gift_type = /obj/item/a_gift/anything
var/list/ckeys_that_took = list()
-/obj/structure/flora/tree/pine/xmas/presents/attack_hand(mob/living/user)
+/obj/structure/flora/tree/pine/xmas/presents/attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
@@ -288,7 +288,7 @@
icon_state = "fullgrass_[rand(1, 3)]"
. = ..()
-/obj/item/twohanded/required/kirbyplants
+/obj/item/kirbyplants
name = "potted plant"
icon = 'icons/obj/flora/plants.dmi'
icon_state = "plant-01"
@@ -300,24 +300,25 @@
throw_speed = 2
throw_range = 4
-/obj/item/twohanded/required/kirbyplants/Initialize()
+/obj/item/kirbyplants/ComponentInitialize()
. = ..()
AddElement(/datum/element/tactical)
addtimer(CALLBACK(src, /datum.proc/_AddElement, list(/datum/element/beauty, 500)), 0)
+ AddComponent(/datum/component/two_handed, require_twohands=TRUE, force_unwielded=10, force_wielded=10)
-/obj/item/twohanded/required/kirbyplants/random
+/obj/item/kirbyplants/random
icon = 'icons/obj/flora/_flora.dmi'
icon_state = "random_plant"
var/list/static/states
-/obj/item/twohanded/required/kirbyplants/random/Initialize()
+/obj/item/kirbyplants/random/Initialize()
. = ..()
icon = 'icons/obj/flora/plants.dmi'
if(!states)
generate_states()
icon_state = pick(states)
-/obj/item/twohanded/required/kirbyplants/random/proc/generate_states()
+/obj/item/kirbyplants/random/proc/generate_states()
states = list()
for(var/i in 1 to 25)
var/number
@@ -329,12 +330,12 @@
states += "applebush"
-/obj/item/twohanded/required/kirbyplants/dead
+/obj/item/kirbyplants/dead
name = "RD's potted plant"
desc = "A gift from the botanical staff, presented after the RD's reassignment. There's a tag on it that says \"Y'all come back now, y'hear?\"\nIt doesn't look very healthy..."
icon_state = "plant-25"
-/obj/item/twohanded/required/kirbyplants/photosynthetic
+/obj/item/kirbyplants/photosynthetic
name = "photosynthetic potted plant"
desc = "A bioluminescent plant."
icon_state = "plant-09"
diff --git a/code/game/objects/structures/fluff.dm b/code/game/objects/structures/fluff.dm
index 736e58143e..1dcda6eb50 100644
--- a/code/game/objects/structures/fluff.dm
+++ b/code/game/objects/structures/fluff.dm
@@ -110,7 +110,7 @@
desc = "Space Jesus is my copilot."
icon_state = "driverseat"
-/obj/structure/fluff/bus/passable/seat/driver/attack_hand(mob/user)
+/obj/structure/fluff/bus/passable/seat/driver/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
playsound(src, 'sound/items/carhorn.ogg', 50, 1)
. = ..()
diff --git a/code/game/objects/structures/ghost_role_spawners.dm b/code/game/objects/structures/ghost_role_spawners.dm
index 4455c8820d..03bcdde57c 100644
--- a/code/game/objects/structures/ghost_role_spawners.dm
+++ b/code/game/objects/structures/ghost_role_spawners.dm
@@ -186,7 +186,7 @@
else
new_spawn.mind.assigned_role = "Free Golem"
-/obj/effect/mob_spawn/human/golem/attack_hand(mob/user)
+/obj/effect/mob_spawn/human/golem/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/objects/structures/girders.dm b/code/game/objects/structures/girders.dm
index cacf361722..ff62b9cc48 100644
--- a/code/game/objects/structures/girders.dm
+++ b/code/game/objects/structures/girders.dm
@@ -170,7 +170,7 @@
qdel(src)
return
- if(S.sheettype && S.sheettype != "runed")
+ if(S.sheettype != "runed")
var/M = S.sheettype
if(state == GIRDER_DISPLACED)
var/F = text2path("/obj/structure/falsewall/[M]")
@@ -188,9 +188,13 @@
transfer_fingerprints_to(FW)
qdel(src)
else
- var/F = text2path("/turf/closed/wall/mineral/[M]")
+ var/list/material_list
+ var/F = S.walltype
if(!F)
- return
+ F = /turf/closed/wall/material
+ if(S.material_type)
+ material_list = list()
+ material_list[SSmaterials.GetMaterialRef(S.material_type)] = MINERAL_MATERIAL_AMOUNT * 2
if(S.get_amount() < 2)
to_chat(user, "You need at least two sheets to add plating!")
return
@@ -201,7 +205,9 @@
S.use(2)
to_chat(user, "You add the plating.")
var/turf/T = get_turf(src)
- T.PlaceOnTop(F)
+ var/turf/newturf = T.PlaceOnTop(F)
+ if(material_list)
+ newturf.set_custom_materials(material_list)
transfer_fingerprints_to(T)
qdel(src)
return
diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm
index 20151a0bdb..efd1b486d0 100644
--- a/code/game/objects/structures/grille.dm
+++ b/code/game/objects/structures/grille.dm
@@ -99,7 +99,7 @@
..(user, 1)
return TRUE
-/obj/structure/grille/attack_hand(mob/living/user)
+/obj/structure/grille/attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/objects/structures/guillotine.dm b/code/game/objects/structures/guillotine.dm
index 0a5ba6a68c..a335906744 100644
--- a/code/game/objects/structures/guillotine.dm
+++ b/code/game/objects/structures/guillotine.dm
@@ -51,7 +51,7 @@
if (LAZYLEN(buckled_mobs))
. += "Someone appears to be strapped in. You can help them out, or you can harm them by activating the guillotine."
-/obj/structure/guillotine/attack_hand(mob/user)
+/obj/structure/guillotine/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
add_fingerprint(user)
// Currently being used by something
@@ -256,4 +256,4 @@
#undef GUILLOTINE_ACTIVATE_DELAY
#undef GUILLOTINE_WRENCH_DELAY
#undef GUILLOTINE_ACTION_INUSE
-#undef GUILLOTINE_ACTION_WRENCH
\ No newline at end of file
+#undef GUILLOTINE_ACTION_WRENCH
diff --git a/code/game/objects/structures/guncase.dm b/code/game/objects/structures/guncase.dm
index ede7e31e0d..1cab600fb1 100644
--- a/code/game/objects/structures/guncase.dm
+++ b/code/game/objects/structures/guncase.dm
@@ -53,7 +53,7 @@
else
return ..()
-/obj/structure/guncase/attack_hand(mob/user)
+/obj/structure/guncase/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/objects/structures/headpike.dm b/code/game/objects/structures/headpike.dm
index 581ce850de..7a67387a8e 100644
--- a/code/game/objects/structures/headpike.dm
+++ b/code/game/objects/structures/headpike.dm
@@ -6,7 +6,7 @@
density = FALSE
anchored = TRUE
var/bonespear = FALSE
- var/obj/item/twohanded/spear/spear
+ var/obj/item/spear/spear
var/obj/item/bodypart/head/victim
/obj/structure/headpike/bone //for bone spears
@@ -20,9 +20,9 @@
name = "[victim.name] on a spear"
update_icon()
if(bonespear)
- spear = locate(/obj/item/twohanded/bonespear) in parts_list
+ spear = locate(/obj/item/spear/bonespear) in parts_list
else
- spear = locate(/obj/item/twohanded/spear) in parts_list
+ spear = locate(/obj/item/spear) in parts_list
/obj/structure/headpike/Initialize()
. = ..()
@@ -37,7 +37,7 @@
MA.pixel_y = 12
. += H
-/obj/structure/headpike/attack_hand(mob/user)
+/obj/structure/headpike/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
@@ -47,4 +47,4 @@
victim = null
spear.forceMove(drop_location())
spear = null
- qdel(src)
\ No newline at end of file
+ qdel(src)
diff --git a/code/game/objects/structures/holosign.dm b/code/game/objects/structures/holosign.dm
index 7ac3aba246..844c2ff8fd 100644
--- a/code/game/objects/structures/holosign.dm
+++ b/code/game/objects/structures/holosign.dm
@@ -25,7 +25,7 @@
projector = null
return ..()
-/obj/structure/holosign/attack_hand(mob/living/user)
+/obj/structure/holosign/attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
@@ -94,7 +94,7 @@
alpha = 150
resistance_flags = FIRE_PROOF
-/obj/structure/holosign/barrier/firelock/blocksTemperature()
+/obj/structure/holosign/barrier/firelock/BlockSuperconductivity()
return TRUE
/obj/structure/holosign/barrier/combifan
@@ -110,7 +110,7 @@
CanAtmosPass = ATMOS_PASS_NO
resistance_flags = FIRE_PROOF
-/obj/structure/holosign/barrier/combifan/blocksTemperature()
+/obj/structure/holosign/barrier/combifan/BlockSuperconductivity()
return TRUE
/obj/structure/holosign/barrier/combifan/Initialize()
@@ -162,7 +162,7 @@
return TRUE //nice or benign diseases!
return TRUE
-/obj/structure/holosign/barrier/medical/attack_hand(mob/living/user)
+/obj/structure/holosign/barrier/medical/attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
if(CanPass(user) && user.a_intent == INTENT_HELP)
force_allaccess = !force_allaccess
to_chat(user, "You [force_allaccess ? "deactivate" : "activate"] the biometric scanners.") //warning spans because you can make the station sick!
@@ -182,7 +182,7 @@
/obj/structure/holosign/barrier/cyborg/hacked/proc/cooldown()
shockcd = FALSE
-/obj/structure/holosign/barrier/cyborg/hacked/attack_hand(mob/living/user)
+/obj/structure/holosign/barrier/cyborg/hacked/attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/objects/structures/janicart.dm b/code/game/objects/structures/janicart.dm
index 38133d9089..72487ddc16 100644
--- a/code/game/objects/structures/janicart.dm
+++ b/code/game/objects/structures/janicart.dm
@@ -8,7 +8,7 @@
//copypaste sorry
var/obj/item/storage/bag/trash/mybag
var/obj/item/mop/mymop
- var/obj/item/twohanded/broom/mybroom
+ var/obj/item/broom/mybroom
var/obj/item/reagent_containers/spray/cleaner/myspray
var/obj/item/lightreplacer/myreplacer
var/signs = 0
@@ -48,9 +48,9 @@
m.janicart_insert(user, src)
else
to_chat(user, fail_msg)
- else if(istype(I, /obj/item/twohanded/broom))
+ else if(istype(I, /obj/item/broom))
if(!mybroom)
- var/obj/item/twohanded/broom/b=I
+ var/obj/item/broom/b=I
b.janicart_insert(user,src)
else
to_chat(user, fail_msg)
@@ -91,7 +91,7 @@
else
return ..()
-/obj/structure/janitorialcart/attack_hand(mob/user)
+/obj/structure/janitorialcart/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/objects/structures/kitchen_spike.dm b/code/game/objects/structures/kitchen_spike.dm
index 05a7e1c958..5706f79192 100644
--- a/code/game/objects/structures/kitchen_spike.dm
+++ b/code/game/objects/structures/kitchen_spike.dm
@@ -61,7 +61,7 @@
return TRUE
//ATTACK HAND IGNORING PARENT RETURN VALUE
-/obj/structure/kitchenspike/attack_hand(mob/user)
+/obj/structure/kitchenspike/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(VIABLE_MOB_CHECK(user.pulling) && user.a_intent == INTENT_GRAB && !has_buckled_mobs())
var/mob/living/L = user.pulling
if(HAS_TRAIT(user, TRAIT_PACIFISM) && L.stat != DEAD)
diff --git a/code/game/objects/structures/ladders.dm b/code/game/objects/structures/ladders.dm
index f321498bfd..6037183cc9 100644
--- a/code/game/objects/structures/ladders.dm
+++ b/code/game/objects/structures/ladders.dm
@@ -122,7 +122,7 @@
return FALSE
return TRUE
-/obj/structure/ladder/attack_hand(mob/user)
+/obj/structure/ladder/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/objects/structures/life_candle.dm b/code/game/objects/structures/life_candle.dm
index a64dc6c6a9..ddd334a9e4 100644
--- a/code/game/objects/structures/life_candle.dm
+++ b/code/game/objects/structures/life_candle.dm
@@ -24,7 +24,7 @@
var/respawn_time = 50
var/respawn_sound = 'sound/magic/staff_animation.ogg'
-/obj/structure/life_candle/attack_hand(mob/user)
+/obj/structure/life_candle/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/objects/structures/mineral_doors.dm b/code/game/objects/structures/mineral_doors.dm
index f52fa0576a..cca41b4e11 100644
--- a/code/game/objects/structures/mineral_doors.dm
+++ b/code/game/objects/structures/mineral_doors.dm
@@ -50,7 +50,7 @@
/obj/structure/mineral_door/attack_paw(mob/user)
return attack_hand(user)
-/obj/structure/mineral_door/attack_hand(mob/user)
+/obj/structure/mineral_door/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/objects/structures/mirror.dm b/code/game/objects/structures/mirror.dm
index 65e1b7dd9a..b1829054ce 100644
--- a/code/game/objects/structures/mirror.dm
+++ b/code/game/objects/structures/mirror.dm
@@ -15,7 +15,7 @@
if(icon_state == "mirror_broke" && !broken)
obj_break(null, mapload)
-/obj/structure/mirror/attack_hand(mob/user)
+/obj/structure/mirror/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
@@ -118,7 +118,7 @@
choosable_races += S.id
..()
-/obj/structure/mirror/magic/attack_hand(mob/user)
+/obj/structure/mirror/magic/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/objects/structures/morgue.dm b/code/game/objects/structures/morgue.dm
index 79a7ce0519..9639ffae51 100644
--- a/code/game/objects/structures/morgue.dm
+++ b/code/game/objects/structures/morgue.dm
@@ -58,7 +58,7 @@ GLOBAL_LIST_EMPTY(bodycontainers) //Let them act as spawnpoints for revenants an
/obj/structure/bodycontainer/attack_paw(mob/user)
return attack_hand(user)
-/obj/structure/bodycontainer/attack_hand(mob/user)
+/obj/structure/bodycontainer/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
@@ -225,7 +225,15 @@ GLOBAL_LIST_EMPTY(crematoriums)
GLOB.crematoriums.Add(src)
..()
-/obj/structure/bodycontainer/crematorium/update_icon_state()
+/obj/structure/bodycontainer/crematorium/Initialize()
+ . = ..()
+ connected = new /obj/structure/tray/c_tray(src)
+ connected.connected = src
+
+/obj/structure/bodycontainer/crematorium/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock, idnum, override=FALSE)
+ id = "[idnum][id]"
+
+/obj/structure/bodycontainer/crematorium/update_icon()
if(!connected || connected.loc != src)
icon_state = "crema0"
else
@@ -322,7 +330,7 @@ GLOBAL_LIST_EMPTY(crematoriums)
/obj/structure/tray/attack_paw(mob/user)
return attack_hand(user)
-/obj/structure/tray/attack_hand(mob/user)
+/obj/structure/tray/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/objects/structures/spirit_board.dm b/code/game/objects/structures/spirit_board.dm
index 4a9a1bdce6..3a415b84a9 100644
--- a/code/game/objects/structures/spirit_board.dm
+++ b/code/game/objects/structures/spirit_board.dm
@@ -14,7 +14,7 @@
desc = "[initial(desc)] The planchette is sitting at \"[planchette]\"."
return ..()
-/obj/structure/spirit_board/attack_hand(mob/user)
+/obj/structure/spirit_board/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
@@ -76,4 +76,4 @@
to_chat(M, "There aren't enough people to use the [src.name]!")
return 0
- return 1
\ No newline at end of file
+ return 1
diff --git a/code/game/objects/structures/statues.dm b/code/game/objects/structures/statues.dm
index 466912f93e..1d7ba1d993 100644
--- a/code/game/objects/structures/statues.dm
+++ b/code/game/objects/structures/statues.dm
@@ -74,7 +74,7 @@
radiate()
..()
-/obj/structure/statue/uranium/attack_hand(mob/user)
+/obj/structure/statue/uranium/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
radiate()
. = ..()
@@ -240,7 +240,7 @@
honk()
return ..()
-/obj/structure/statue/bananium/attack_hand(mob/user)
+/obj/structure/statue/bananium/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
honk()
. = ..()
diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm
index 012d92e103..589b21c6d4 100644
--- a/code/game/objects/structures/tables_racks.dm
+++ b/code/game/objects/structures/tables_racks.dm
@@ -60,7 +60,7 @@
/obj/structure/table/attack_paw(mob/user)
return attack_hand(user)
-/obj/structure/table/attack_hand(mob/living/user)
+/obj/structure/table/attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
if(Adjacent(user) && user.pulling)
if(isliving(user.pulling))
var/mob/living/pushed_mob = user.pulling
@@ -134,6 +134,8 @@
if(!ishuman(pushed_mob))
return
var/mob/living/carbon/human/H = pushed_mob
+ if(iscatperson(H))
+ H.emote("nya")
SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "table", /datum/mood_event/table)
/obj/structure/table/shove_act(mob/living/target, mob/living/user)
@@ -218,7 +220,7 @@
/obj/structure/table/greyscale
icon = 'icons/obj/smooth_structures/table_greyscale.dmi'
icon_state = "table"
- material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS | MATERIAL_EFFECTS
+ material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
buildstack = null //No buildstack, so generate from mat datums
///Table on wheels
@@ -674,7 +676,7 @@
/obj/structure/rack/attack_paw(mob/living/user)
attack_hand(user)
-/obj/structure/rack/attack_hand(mob/living/user)
+/obj/structure/rack/attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/objects/structures/tank_dispenser.dm b/code/game/objects/structures/tank_dispenser.dm
index 6a8175b921..e2298ac6c5 100644
--- a/code/game/objects/structures/tank_dispenser.dm
+++ b/code/game/objects/structures/tank_dispenser.dm
@@ -71,9 +71,12 @@
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, 103, master_ui, state)
+ ui = new(user, src, ui_key, "TankDispenser", name, 275, 103, master_ui, state)
ui.open()
+/obj/structure/tank_dispenser/attack_robot(mob/user)
+ ui_interact(user)
+
/obj/structure/tank_dispenser/ui_data(mob/user)
var/list/data = list()
data["oxygen"] = oxygentanks
diff --git a/code/game/objects/structures/target_stake.dm b/code/game/objects/structures/target_stake.dm
index 9dcfec43bf..a08d5c95c1 100644
--- a/code/game/objects/structures/target_stake.dm
+++ b/code/game/objects/structures/target_stake.dm
@@ -48,7 +48,7 @@
handle_density()
to_chat(user, "You slide the target into the stake.")
-/obj/structure/target_stake/attack_hand(mob/user)
+/obj/structure/target_stake/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/objects/structures/transit_tubes/station.dm b/code/game/objects/structures/transit_tubes/station.dm
index c3b388fc29..7f597857d9 100644
--- a/code/game/objects/structures/transit_tubes/station.dm
+++ b/code/game/objects/structures/transit_tubes/station.dm
@@ -58,7 +58,7 @@
qdel(R)
-/obj/structure/transit_tube/station/attack_hand(mob/user)
+/obj/structure/transit_tube/station/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
@@ -152,8 +152,8 @@
pod_moving = 0
if(!QDELETED(pod))
var/datum/gas_mixture/floor_mixture = loc.return_air()
- ARCHIVE(floor_mixture)
- ARCHIVE(pod.air_contents)
+ floor_mixture.archive()
+ pod.air_contents.archive()
pod.air_contents.share(floor_mixture, 1) //mix the pod's gas mixture with the tile it's on
air_update_turf()
diff --git a/code/game/objects/structures/transit_tubes/transit_tube_pod.dm b/code/game/objects/structures/transit_tubes/transit_tube_pod.dm
index 04f113a0c8..36539ae1e4 100644
--- a/code/game/objects/structures/transit_tubes/transit_tube_pod.dm
+++ b/code/game/objects/structures/transit_tubes/transit_tube_pod.dm
@@ -10,9 +10,9 @@
/obj/structure/transit_tube_pod/Initialize()
. = ..()
- air_contents.gases[/datum/gas/oxygen] = MOLES_O2STANDARD
- air_contents.gases[/datum/gas/nitrogen] = MOLES_N2STANDARD
- air_contents.temperature = T20C
+ air_contents.set_moles(/datum/gas/oxygen, MOLES_O2STANDARD)
+ air_contents.set_moles(/datum/gas/nitrogen, MOLES_N2STANDARD)
+ air_contents.set_temperature(T20C)
/obj/structure/transit_tube_pod/Destroy()
@@ -181,4 +181,4 @@
return
/obj/structure/transit_tube_pod/return_temperature()
- return air_contents.temperature
+ return air_contents.return_temperature()
diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm
index 5eecc6962a..3dacd49fc9 100644
--- a/code/game/objects/structures/watercloset.dm
+++ b/code/game/objects/structures/watercloset.dm
@@ -26,7 +26,7 @@
AM.forceMove(loc)
return ..()
-/obj/structure/toilet/attack_hand(mob/living/user)
+/obj/structure/toilet/attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
@@ -157,7 +157,6 @@
secret_type = /obj/effect/spawner/lootdrop/prison_loot_toilet
/obj/structure/toilet/greyscale
-
material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR
buildstacktype = null
@@ -175,7 +174,7 @@
..()
hiddenitem = new /obj/item/reagent_containers/food/urinalcake
-/obj/structure/urinal/attack_hand(mob/user)
+/obj/structure/urinal/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
@@ -493,7 +492,7 @@
var/buildstacktype = /obj/item/stack/sheet/metal
var/buildstackamount = 1
-/obj/structure/sink/attack_hand(mob/living/user)
+/obj/structure/sink/attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
@@ -582,6 +581,12 @@
G.use(1)
return
+ if(istype(O, /obj/item/stack/ore/glass))
+ new /obj/item/stack/sheet/sandblock(loc)
+ to_chat(user, "You wet the sand in the sink and form it into a block.")
+ O.use(1)
+ return
+
if(!istype(O))
return
if(O.item_flags & ABSTRACT) //Abstract items like grabs won't wash. No-drop items will though because it's still technically an item in your hand.
@@ -673,7 +678,7 @@
if(steps == 4 && istype(S, /obj/item/stack/sheet/mineral/wood))
if(S.use(3))
steps = 5
- desc = "A dug out well, A dug out well with out rope. Just add some cloth!"
+ desc = "A dug out well, A dug out well without rope. Just add some cloth!"
icon_state = "well_4"
return TRUE
else
@@ -702,11 +707,6 @@
icon_state = "puddle"
resistance_flags = UNACIDABLE
-/obj/structure/sink/greyscale
- icon_state = "sink_greyscale"
- material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR
- buildstacktype = null
-
//ATTACK HAND IGNORING PARENT RETURN VALUE
/obj/structure/sink/puddle/attack_hand(mob/M)
icon_state = "puddle-splash"
@@ -722,6 +722,7 @@
qdel(src)
/obj/structure/sink/greyscale
+ icon_state = "sink_greyscale"
material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR
buildstacktype = null
@@ -783,7 +784,7 @@
return TRUE
-/obj/structure/curtain/attack_hand(mob/user)
+/obj/structure/curtain/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm
index e896f8072b..38655df39e 100644
--- a/code/game/objects/structures/window.dm
+++ b/code/game/objects/structures/window.dm
@@ -167,7 +167,7 @@ GLOBAL_LIST_EMPTY(electrochromatic_window_lookup)
return 1
. = ..()
-/obj/structure/window/attack_hand(mob/user)
+/obj/structure/window/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
@@ -271,29 +271,27 @@ GLOBAL_LIST_EMPTY(electrochromatic_window_lookup)
air_update_turf(TRUE)
update_nearby_icons()
-/obj/structure/window/proc/spraycan_paint(paint_color)
- if(color_hex2num(paint_color) < 255)
- set_opacity(255)
- else
- set_opacity(initial(opacity))
- add_atom_colour(paint_color, WASHABLE_COLOUR_PRIORITY)
-
/obj/structure/window/proc/electrochromatic_dim()
if(electrochromatic_status == ELECTROCHROMATIC_DIMMED)
return
electrochromatic_status = ELECTROCHROMATIC_DIMMED
- animate(src, color = "#222222", time = 2)
- set_opacity(TRUE)
+ var/current = color
+ add_atom_colour("#222222", FIXED_COLOUR_PRIORITY)
+ var/newcolor = color
+ if(color != current)
+ color = current
+ animate(src, color = newcolor, time = 2)
/obj/structure/window/proc/electrochromatic_off()
if(electrochromatic_status == ELECTROCHROMATIC_OFF)
return
electrochromatic_status = ELECTROCHROMATIC_OFF
var/current = color
- update_atom_colour()
+ remove_atom_colour(FIXED_COLOUR_PRIORITY, "#222222")
var/newcolor = color
- color = current
- animate(src, color = newcolor, time = 2)
+ if(color != current)
+ color = current
+ animate(src, color = newcolor, time = 2)
/obj/structure/window/proc/remove_electrochromatic()
electrochromatic_off()
@@ -348,11 +346,9 @@ GLOBAL_LIST_EMPTY(electrochromatic_window_lookup)
GLOB.electrochromatic_window_lookup[electrochromatic_id] |= src
/obj/structure/window/update_atom_colour()
- if((electrochromatic_status != ELECTROCHROMATIC_OFF) && (electrochromatic_status != ELECTROCHROMATIC_DIMMED))
- return FALSE
. = ..()
- if(color && (color_hex2num(color) < 255))
- set_opacity(255)
+ if(electrochromatic_status == ELECTROCHROMATIC_DIMMED || (color && (color_hex2num(color) < 255)))
+ set_opacity(TRUE)
else
set_opacity(FALSE)
@@ -840,7 +836,7 @@ GLOBAL_LIST_EMPTY(electrochromatic_window_lookup)
for (var/i in 1 to rand(1,4))
. += new /obj/item/paper/natural(location)
-/obj/structure/window/paperframe/attack_hand(mob/user)
+/obj/structure/window/paperframe/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/say.dm b/code/game/say.dm
index 35a1b1d072..a5691ad11f 100644
--- a/code/game/say.dm
+++ b/code/game/say.dm
@@ -110,6 +110,8 @@ GLOBAL_LIST_INIT(freqtospan, list(
/// Quirky citadel proc for our custom sayverbs to strip the verb out. Snowflakey as hell, say rewrite 3.0 when?
/atom/movable/proc/quoteless_say_quote(input, list/spans = list(speech_span), message_mode)
+ if((input[1] == "!") && (length_char(input) > 1))
+ return ""
var/pos = findtext(input, "*")
return pos? copytext(input, pos + 1) : input
@@ -144,6 +146,8 @@ GLOBAL_LIST_INIT(freqtospan, list(
return "[copytext_char("[freq]", 1, 4)].[copytext_char("[freq]", 4, 5)]"
/atom/movable/proc/attach_spans(input, list/spans)
+ if((input[1] == "!") && (length(input) > 2))
+ return
var/customsayverb = findtext(input, "*")
if(customsayverb)
input = capitalize(copytext(input, customsayverb + length(input[customsayverb])))
diff --git a/code/game/turfs/change_turf.dm b/code/game/turfs/change_turf.dm
index 38e0839617..66e7ebd424 100644
--- a/code/game/turfs/change_turf.dm
+++ b/code/game/turfs/change_turf.dm
@@ -148,6 +148,7 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list(
. = ..()
if (!.) // changeturf failed or didn't do anything
QDEL_NULL(stashed_air)
+ update_air_ref()
return
var/turf/open/newTurf = .
newTurf.air.copy_from(stashed_air)
@@ -308,24 +309,14 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list(
return
var/datum/gas_mixture/total = new//Holders to assimilate air from nearby turfs
- var/list/total_gases = total.gases
for(var/T in atmos_adjacent_turfs)
var/turf/open/S = T
if(!S.air)
continue
- var/list/S_gases = S.air.gases
- for(var/id in S_gases)
- total_gases[id] += S_gases[id]
- total.temperature += S.air.temperature
+ total.merge(S.air)
- air.copy_from(total)
-
- var/list/air_gases = air.gases
- for(var/id in air_gases)
- air_gases[id] /= turf_count //Averages contents of the turfs, ignoring walls and the like
-
- air.temperature /= turf_count
+ air.copy_from(total.remove_ratio(1/turf_count))
SSair.add_to_active(src)
/turf/proc/ReplaceWithLattice()
diff --git a/code/game/turfs/closed.dm b/code/game/turfs/closed.dm
index 9fe23d78d7..b78826b23b 100644
--- a/code/game/turfs/closed.dm
+++ b/code/game/turfs/closed.dm
@@ -7,9 +7,14 @@
rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE
rad_insulation = RAD_MEDIUM_INSULATION
+/turf/closed/Initialize()
+ . = ..()
+ update_air_ref()
+
/turf/closed/AfterChange()
. = ..()
SSair.high_pressure_delta -= src
+ update_air_ref()
/turf/closed/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir)
return FALSE
@@ -78,7 +83,7 @@
. = ..()
if(.)
switch(var_name)
- if("icon")
+ if(NAMEOF(src, icon))
SStitle.icon = icon
/turf/closed/indestructible/riveted
diff --git a/code/game/turfs/open.dm b/code/game/turfs/open.dm
index b81c5be44c..6552e29246 100644
--- a/code/game/turfs/open.dm
+++ b/code/game/turfs/open.dm
@@ -199,7 +199,7 @@
flash_color(L, flash_color = "#C80000", flash_time = 10)
/turf/open/Initalize_Atmos(times_fired)
- excited = 0
+ set_excited(FALSE)
update_visuals()
current_cycle = times_fired
@@ -207,19 +207,19 @@
for(var/i in atmos_adjacent_turfs)
var/turf/open/enemy_tile = i
var/datum/gas_mixture/enemy_air = enemy_tile.return_air()
- if(!excited && air.compare(enemy_air))
+ if(!get_excited() && air.compare(enemy_air))
//testing("Active turf found. Return value of compare(): [is_active]")
- excited = TRUE
+ set_excited(TRUE)
SSair.active_turfs |= src
/turf/open/proc/GetHeatCapacity()
. = air.heat_capacity()
/turf/open/proc/GetTemperature()
- . = air.temperature
+ . = air.return_temperature()
/turf/open/proc/TakeTemperature(temp)
- air.temperature += temp
+ air.set_temperature(air.return_temperature() + temp)
air_update_turf()
/turf/open/proc/freon_gas_act()
@@ -304,9 +304,8 @@
/turf/open/rad_act(pulse_strength)
. = ..()
- if (air.gases[/datum/gas/carbon_dioxide] && air.gases[/datum/gas/oxygen])
- pulse_strength = min(pulse_strength,air.gases[/datum/gas/carbon_dioxide]*1000,air.gases[/datum/gas/oxygen]*2000) //Ensures matter is conserved properly
- air.gases[/datum/gas/carbon_dioxide]=max(air.gases[/datum/gas/carbon_dioxide]-(pulse_strength/1000),0)
- air.gases[/datum/gas/oxygen]=max(air.gases[/datum/gas/oxygen]-(pulse_strength/2000),0)
- air.gases[/datum/gas/pluoxium]+=(pulse_strength/4000)
- GAS_GARBAGE_COLLECT(air.gases)
+ if (air.get_moles(/datum/gas/carbon_dioxide) && air.get_moles(/datum/gas/oxygen))
+ pulse_strength = min(pulse_strength,air.get_moles(/datum/gas/carbon_dioxide)*1000,air.get_moles(/datum/gas/oxygen)*2000) //Ensures matter is conserved properly
+ air.set_moles(/datum/gas/carbon_dioxide, max(air.get_moles(/datum/gas/carbon_dioxide)-(pulse_strength/1000),0))
+ air.set_moles(/datum/gas/oxygen, max(air.get_moles(/datum/gas/oxygen)-(pulse_strength/2000),0))
+ air.adjust_moles(/datum/gas/pluoxium, pulse_strength/4000)
diff --git a/code/game/turfs/simulated/floor.dm b/code/game/turfs/simulated/floor.dm
index 9f5ce679f9..bdca384bd0 100644
--- a/code/game/turfs/simulated/floor.dm
+++ b/code/game/turfs/simulated/floor.dm
@@ -189,9 +189,12 @@
if(user && !silent)
to_chat(user, "You remove the floor tile.")
if(floor_tile && make_tile)
- new floor_tile(src)
+ spawn_tile()
return make_plating()
+/turf/open/floor/proc/spawn_tile()
+ new floor_tile(src)
+
/turf/open/floor/singularity_pull(S, current_size)
. = ..()
switch(current_size)
@@ -205,7 +208,7 @@
if(floor_tile)
if(prob(70))
remove_tile()
- else if(prob(50))
+ else if(prob(50) && (/turf/open/space in baseturfs))
ReplaceWithLattice()
/turf/open/floor/narsie_act(force, ignore_mobs, probability = 20)
@@ -293,3 +296,13 @@
return TRUE
return FALSE
+
+/turf/open/floor/material
+ name = "floor"
+ icon_state = "materialfloor"
+ material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
+
+/turf/open/floor/material/spawn_tile()
+ for(var/i in custom_materials)
+ var/datum/material/M = i
+ new M.sheet_type(src, FLOOR(custom_materials[M] / MINERAL_MATERIAL_AMOUNT, 1))
diff --git a/code/game/turfs/simulated/floor/fancy_floor.dm b/code/game/turfs/simulated/floor/fancy_floor.dm
index 2f4e2e0eee..f940761ff8 100644
--- a/code/game/turfs/simulated/floor/fancy_floor.dm
+++ b/code/game/turfs/simulated/floor/fancy_floor.dm
@@ -157,7 +157,7 @@
planetary_atmos = TRUE
floor_tile = null
initial_gas_mix = FROZEN_ATMOS
- slowdown = 1.5 //So digging it out paths are usefull.
+ slowdown = 1.5 //So digging it out paths are useful.
bullet_sizzle = TRUE
footstep = FOOTSTEP_SAND
barefootstep = FOOTSTEP_SAND
diff --git a/code/game/turfs/simulated/floor/light_floor.dm b/code/game/turfs/simulated/floor/light_floor.dm
index f9376c0672..4f7aa9e492 100644
--- a/code/game/turfs/simulated/floor/light_floor.dm
+++ b/code/game/turfs/simulated/floor/light_floor.dm
@@ -52,7 +52,7 @@
set_light(0)
return ..()
-/turf/open/floor/light/attack_hand(mob/user)
+/turf/open/floor/light/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/turfs/simulated/floor/mineral_floor.dm b/code/game/turfs/simulated/floor/mineral_floor.dm
index f0ac0053ce..9a227d2594 100644
--- a/code/game/turfs/simulated/floor/mineral_floor.dm
+++ b/code/game/turfs/simulated/floor/mineral_floor.dm
@@ -149,7 +149,7 @@
if(!.)
honk()
-/turf/open/floor/mineral/bananium/attack_hand(mob/user)
+/turf/open/floor/mineral/bananium/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
.=..()
if(!.)
honk()
@@ -202,7 +202,7 @@
if(!.)
radiate()
-/turf/open/floor/mineral/uranium/attack_hand(mob/user)
+/turf/open/floor/mineral/uranium/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
.=..()
if(!.)
radiate()
diff --git a/code/game/turfs/simulated/floor/plating.dm b/code/game/turfs/simulated/floor/plating.dm
index 8e0eace9cd..f26a4b827a 100644
--- a/code/game/turfs/simulated/floor/plating.dm
+++ b/code/game/turfs/simulated/floor/plating.dm
@@ -76,11 +76,15 @@
var/obj/item/stack/tile/W = C
if(!W.use(1))
return
- var/turf/open/floor/T = PlaceOnTop(W.turf_type, flags = CHANGETURF_INHERIT_AIR)
- if(istype(W, /obj/item/stack/tile/light)) //TODO: get rid of this ugly check somehow
- var/obj/item/stack/tile/light/L = W
- var/turf/open/floor/light/F = T
- F.state = L.state
+ if(istype(W, /obj/item/stack/tile/material))
+ var/turf/newturf = PlaceOnTop(/turf/open/floor/material, flags = CHANGETURF_INHERIT_AIR)
+ newturf.set_custom_materials(W.custom_materials)
+ else if(W.turf_type)
+ var/turf/open/floor/T = PlaceOnTop(W.turf_type, flags = CHANGETURF_INHERIT_AIR)
+ if(istype(W, /obj/item/stack/tile/light)) //TODO: get rid of this ugly check somehow
+ var/obj/item/stack/tile/light/L = W
+ var/turf/open/floor/light/F = T
+ F.state = L.state
playsound(src, 'sound/weapons/genhit.ogg', 50, 1)
else
to_chat(user, "This section is too damaged to support a tile! Use a welder to fix the damage.")
diff --git a/code/game/turfs/simulated/floor/reinf_floor.dm b/code/game/turfs/simulated/floor/reinf_floor.dm
index 7816341a0a..b050d8fb89 100644
--- a/code/game/turfs/simulated/floor/reinf_floor.dm
+++ b/code/game/turfs/simulated/floor/reinf_floor.dm
@@ -89,7 +89,7 @@
/turf/open/floor/engine/attack_paw(mob/user)
return attack_hand(user)
-/turf/open/floor/engine/attack_hand(mob/user)
+/turf/open/floor/engine/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/turfs/simulated/minerals.dm b/code/game/turfs/simulated/minerals.dm
index 048394b942..0f1ec6fa85 100644
--- a/code/game/turfs/simulated/minerals.dm
+++ b/code/game/turfs/simulated/minerals.dm
@@ -803,7 +803,7 @@
stage = GIBTONITE_DETONATE
explosion(bombturf,1,2,5, adminlog = 0)
if(stage == GIBTONITE_STABLE) //Gibtonite deposit is now benign and extractable. Depending on how close you were to it blowing up before defusing, you get better quality ore.
- var/obj/item/twohanded/required/gibtonite/G = new (src)
+ var/obj/item/gibtonite/G = new (src)
if(det_time <= 0)
G.quality = 3
G.icon_state = "Gibtonite ore 3"
diff --git a/code/game/turfs/simulated/wall/material_walls.dm b/code/game/turfs/simulated/wall/material_walls.dm
new file mode 100644
index 0000000000..d3952609e0
--- /dev/null
+++ b/code/game/turfs/simulated/wall/material_walls.dm
@@ -0,0 +1,22 @@
+/turf/closed/wall/material
+ name = "wall"
+ desc = "A huge chunk of material used to separate rooms."
+ icon = 'icons/turf/walls/materialwall.dmi'
+ icon_state = "wall"
+ canSmoothWith = list(/turf/closed/wall/material)
+ smooth = SMOOTH_TRUE
+ material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
+
+/turf/closed/wall/material/break_wall()
+ for(var/i in custom_materials)
+ var/datum/material/M = i
+ new M.sheet_type(src, FLOOR(custom_materials[M] / MINERAL_MATERIAL_AMOUNT, 1))
+ return new girder_type(src)
+
+/turf/closed/wall/material/devastate_wall()
+ for(var/i in custom_materials)
+ var/datum/material/M = i
+ new M.sheet_type(src, FLOOR(custom_materials[M] / MINERAL_MATERIAL_AMOUNT, 1))
+
+/turf/closed/wall/material/mat_update_desc(mat)
+ desc = "A huge chunk of [mat] used to separate rooms."
diff --git a/code/game/turfs/simulated/wall/mineral_walls.dm b/code/game/turfs/simulated/wall/mineral_walls.dm
index 5c74eb4ecc..c8afe628db 100644
--- a/code/game/turfs/simulated/wall/mineral_walls.dm
+++ b/code/game/turfs/simulated/wall/mineral_walls.dm
@@ -72,7 +72,7 @@
return
return
-/turf/closed/wall/mineral/uranium/attack_hand(mob/user)
+/turf/closed/wall/mineral/uranium/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
radiate()
. = ..()
@@ -136,7 +136,7 @@
/turf/closed/wall/mineral/wood/attackby(obj/item/W, mob/user)
if(W.sharpness && W.force)
var/duration = (48/W.force) * 2 //In seconds, for now.
- if(istype(W, /obj/item/hatchet) || istype(W, /obj/item/twohanded/fireaxe))
+ if(istype(W, /obj/item/hatchet) || istype(W, /obj/item/fireaxe))
duration /= 4 //Much better with hatchets and axes.
var/src_type = type
if(do_after(user, duration*10, target=src) && type == src_type) //Into deciseconds.
diff --git a/code/game/turfs/simulated/walls.dm b/code/game/turfs/simulated/walls.dm
index b01a1df2c0..df63cf93e6 100644
--- a/code/game/turfs/simulated/walls.dm
+++ b/code/game/turfs/simulated/walls.dm
@@ -137,7 +137,7 @@
to_chat(user, text("You punch the wall."))
return TRUE
-/turf/closed/wall/attack_hand(mob/user)
+/turf/closed/wall/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/game/turfs/space/space.dm b/code/game/turfs/space/space.dm
index 26daa32107..0e028ed4af 100644
--- a/code/game/turfs/space/space.dm
+++ b/code/game/turfs/space/space.dm
@@ -27,6 +27,7 @@
/turf/open/space/Initialize()
icon_state = SPACE_ICON_STATE
air = space_gas
+ update_air_ref()
vis_contents.Cut() //removes inherited overlays
visibilityChanged()
@@ -132,10 +133,13 @@
else
to_chat(user, "The plating is going to need some support! Place metal rods first.")
-/turf/open/space/Entered(atom/movable/A)
- ..()
- if ((!(A) || src != A.loc))
- return
+/turf/open/space/Entered(atom/movable/A, atom/OldLoc)
+ . = ..()
+
+ var/turf/old = get_turf(OldLoc)
+ if(!isspaceturf(old) && ismob(A))
+ var/mob/M = A
+ M.update_gravity(M.mob_has_gravity())
if(destination_z && destination_x && destination_y && !(A.pulledby || !A.can_be_z_moved))
var/tx = destination_x
@@ -169,6 +173,12 @@
stoplag()//Let a diagonal move finish, if necessary
A.newtonian_move(A.inertia_dir)
+/turf/open/space/Exited(atom/movable/AM, atom/OldLoc)
+ . = ..()
+ var/turf/old = get_turf(OldLoc)
+ if(!isspaceturf(old) && ismob(AM))
+ var/mob/M = AM
+ M.update_gravity(M.mob_has_gravity())
/turf/open/space/MakeSlippery(wet_setting, min_wet_time, wet_time_to_add, max_wet_time, permanent)
return
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index 88f6dd6962..02c12a9744 100755
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -85,6 +85,9 @@
if (opacity)
has_opaque_atom = TRUE
+ // apply materials properly from the default custom_materials value
+ set_custom_materials(custom_materials)
+
ComponentInitialize()
return INITIALIZE_HINT_NORMAL
@@ -119,7 +122,7 @@
requires_activation = FALSE
..()
-/turf/attack_hand(mob/user)
+/turf/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
@@ -179,7 +182,7 @@
target.zImpact(A, levels, src)
return TRUE
-/turf/proc/handleRCL(obj/item/twohanded/rcl/C, mob/user)
+/turf/proc/handleRCL(obj/item/rcl/C, mob/user)
if(C.loaded)
for(var/obj/structure/cable/LC in src)
if(!LC.d1 || !LC.d2)
@@ -202,7 +205,7 @@
coil.place_turf(src, user)
return TRUE
- else if(istype(C, /obj/item/twohanded/rcl))
+ else if(istype(C, /obj/item/rcl))
handleRCL(C, user)
return FALSE
@@ -274,7 +277,7 @@
/turf/open/Entered(atom/movable/AM)
..()
//melting
- if(isobj(AM) && air && air.temperature > T0C)
+ if(isobj(AM) && air && air.return_temperature() > T0C)
var/obj/O = AM
if(O.obj_flags & FROZEN)
O.make_unfrozen()
diff --git a/code/game/world.dm b/code/game/world.dm
index 83e82403e0..55333fb3e6 100644
--- a/code/game/world.dm
+++ b/code/game/world.dm
@@ -9,9 +9,8 @@ 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()
- var/extools = world.GetConfig("env", "EXTOOLS_DLL") || "./byond-extools.dll"
- if (fexists(extools))
- call(extools, "maptick_initialize")()
+ if (fexists(EXTOOLS))
+ call(EXTOOLS, "maptick_initialize")()
enable_debugger()
world.Profile(PROFILE_START)
@@ -132,11 +131,13 @@ 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.world_paper_log = "[GLOB.log_directory]/paper.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"
GLOB.click_log = "[GLOB.log_directory]/click.log"
+ GLOB.world_asset_log = "[GLOB.log_directory]/asset.log"
#ifdef UNIT_TESTS
@@ -276,6 +277,15 @@ GLOBAL_LIST(topic_status_cache)
shutdown_logging() // Past this point, no logging procs can be used, at risk of data loss.
..()
+/world/Del()
+ // memory leaks bad
+ var/num_deleted = 0
+ for(var/datum/gas_mixture/GM)
+ GM.__gasmixture_unregister()
+ num_deleted++
+ log_world("Deallocated [num_deleted] gas mixtures")
+ ..()
+
/world/proc/update_status()
var/list/features = list()
@@ -344,3 +354,6 @@ GLOBAL_LIST(topic_status_cache)
maxz++
SSmobs.MaxZChanged()
SSidlenpcpool.MaxZChanged()
+ world.refresh_atmos_grid()
+
+/world/proc/refresh_atmos_grid()
diff --git a/code/modules/NTNet/relays.dm b/code/modules/NTNet/relays.dm
index 0d855f15bb..2101a72960 100644
--- a/code/modules/NTNet/relays.dm
+++ b/code/modules/NTNet/relays.dm
@@ -69,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", ui_x, ui_y, master_ui, state)
+ ui = new(user, src, ui_key, "NtnetRelay", "NTNet Quantum Relay", ui_x, ui_y, master_ui, state)
ui.open()
diff --git a/code/modules/VR/vr_sleeper.dm b/code/modules/VR/vr_sleeper.dm
index 26ae200990..9953fe4bc5 100644
--- a/code/modules/VR/vr_sleeper.dm
+++ b/code/modules/VR/vr_sleeper.dm
@@ -71,22 +71,17 @@
/obj/machinery/vr_sleeper/update_icon_state()
icon_state = "[initial(icon_state)][state_open ? "-open" : ""]"
-/obj/machinery/vr_sleeper/open_machine()
- if(state_open)
- return
- if(occupant)
- SStgui.close_user_uis(occupant, src)
- return ..()
/obj/machinery/vr_sleeper/MouseDrop_T(mob/target, mob/user)
if(user.lying || !iscarbon(target) || !Adjacent(target) || !user.canUseTopic(src, BE_CLOSE, TRUE, NO_TK))
return
close_machine(target)
+ ui_interact(user)
-/obj/machinery/vr_sleeper/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)
+/obj/machinery/vr_sleeper/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_contained_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "vr_sleeper", "VR Sleeper", 475, 340, master_ui, state)
+ ui = new(user, src, ui_key, "VrSleeper", "VR Sleeper", 475, 340, master_ui, state)
ui.open()
/obj/machinery/vr_sleeper/ui_act(action, params)
@@ -130,11 +125,13 @@
/obj/machinery/vr_sleeper/ui_data(mob/user)
var/list/data = list()
+ var/is_living
if(vr_mob && !QDELETED(vr_mob))
+ is_living = isliving(vr_mob)
data["can_delete_avatar"] = TRUE
data["vr_avatar"] = list("name" = vr_mob.name)
- data["isliving"] = istype(vr_mob)
- if(data["isliving"])
+ data["isliving"] = is_living
+ if(is_living)
var/status
switch(vr_mob.stat)
if(CONSCIOUS)
@@ -146,6 +143,11 @@
if(SOFT_CRIT)
status = "Barely Conscious"
data["vr_avatar"] += list("status" = status, "health" = vr_mob.health, "maxhealth" = vr_mob.maxHealth)
+ else
+ data["can_delete_avatar"] = FALSE
+ data["vr_avatar"] = FALSE
+ data["isliving"] = FALSE
+
data["toggle_open"] = state_open
data["emagged"] = you_die_in_the_game_you_die_for_real
data["isoccupant"] = (user == occupant)
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 289abbe250..718f9d4246 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -62,7 +62,7 @@ GLOBAL_PROTECT(admin_verbs_admin)
/client/proc/cmd_admin_check_player_exp, /* shows players by playtime */
/client/proc/toggle_combo_hud, // toggle display of the combination pizza antag and taco sci/med/eng hud
/client/proc/toggle_AI_interact, /*toggle admin ability to interact with machines as an AI*/
- /client/proc/open_shuttle_manipulator, /* Opens shuttle manipulator UI */
+ /datum/admins/proc/open_shuttlepanel, /* Opens shuttle manipulator UI */
/client/proc/respawn_character,
/client/proc/secrets,
/client/proc/toggle_hear_radio, /*allows admins to hide all radio output*/
diff --git a/code/modules/admin/chat_commands.dm b/code/modules/admin/chat_commands.dm
index 9b15729a9e..7664d85b7d 100644
--- a/code/modules/admin/chat_commands.dm
+++ b/code/modules/admin/chat_commands.dm
@@ -84,7 +84,7 @@ GLOBAL_LIST(round_end_notifiees)
if(!SSticker.IsRoundInProgress() && SSticker.HasRoundStarted())
return "[sender.mention], the round has already ended!"
LAZYINITLIST(GLOB.round_end_notifiees)
- GLOB.round_end_notifiees["<@[sender.mention]>"] = TRUE
+ GLOB.round_end_notifiees[sender.mention] = TRUE
return "I will notify [sender.mention] when the round ends."
/datum/tgs_chat_command/sdql
diff --git a/code/modules/admin/fun_balloon.dm b/code/modules/admin/fun_balloon.dm
index 44dcfc0ae6..417663fcb7 100644
--- a/code/modules/admin/fun_balloon.dm
+++ b/code/modules/admin/fun_balloon.dm
@@ -126,7 +126,7 @@
L.forceMove(LA)
L.hallucination = 0
to_chat(L, "The battle is won. Your bloodlust subsides.")
- for(var/obj/item/twohanded/required/chainsaw/doomslayer/chainsaw in L)
+ for(var/obj/item/chainsaw/doomslayer/chainsaw in L)
qdel(chainsaw)
else
to_chat(L, "You are not yet worthy of passing. Drag a severed head to the barrier to be allowed entry to the hall of champions.")
diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2.dm b/code/modules/admin/verbs/SDQL2/SDQL_2.dm
index bff431a6c7..b757028ea2 100644
--- a/code/modules/admin/verbs/SDQL2/SDQL_2.dm
+++ b/code/modules/admin/verbs/SDQL2/SDQL_2.dm
@@ -666,8 +666,8 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null
obj_count_finished = select_refs
var/n = 0
for(var/i in found)
- if(++n == 20000)
- text_list += "
TRUNCATED - 20000 OBJECT LIMIT HIT"
+ if(++n == 2500)
+ text_list += "
TRUNCATED - 2500 OBJECT LIMIT HIT"
SDQL_print(i, text_list, print_nulls)
select_refs[REF(i)] = TRUE
SDQL2_TICK_CHECK
@@ -909,10 +909,9 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null
assoc = SDQL_expression(object, expressions_list[expression_list])
if(assoc != null)
// Need to insert the key like this to prevent duplicate keys fucking up.
- var/list/dummy = list()
- dummy[result] = assoc
- result = dummy
- val += result
+ val[result] = assoc
+ else
+ val += list(result)
else
val = world.SDQL_var(object, expression, i, object, superuser, src)
i = expression.len
diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm b/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm
index 24149e7e6c..4f518b7f8e 100644
--- a/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm
+++ b/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm
@@ -234,3 +234,7 @@
for(var/turf/T in v)
. += T
return pick(.)
+
+/proc/__nan()
+ var/list/L = json_decode("{\"value\":NaN}")
+ return L["value"]
diff --git a/code/modules/admin/verbs/adminpm.dm b/code/modules/admin/verbs/adminpm.dm
index bbd6b55e4f..68b9a8e341 100644
--- a/code/modules/admin/verbs/adminpm.dm
+++ b/code/modules/admin/verbs/adminpm.dm
@@ -55,7 +55,7 @@
if(AH)
message_admins("[key_name_admin(src)] has started replying to [key_name(C, 0, 0)]'s admin help.")
- var/msg = input(src,"Message:", "Private message to [key_name(C, 0, 0)]") as message|null
+ var/msg = stripped_multiline_input(src,"Message:", "Private message to [key_name(C, 0, 0)]")
if (!msg)
message_admins("[key_name_admin(src)] has cancelled their reply to [key_name(C, 0, 0)]'s admin help.")
return
@@ -87,10 +87,10 @@
if(irc)
- if(!ircreplyamount) //to prevent people from spamming irc
+ if(!ircreplyamount) //to prevent people from spamming irc/discord
return
if(!msg)
- msg = input(src,"Message:", "Private message to Administrator") as text|null
+ msg = stripped_multiline_input(src,"Message:", "Private message to Administrator")
if(!msg)
return
@@ -112,7 +112,7 @@
//get message text, limit it's length.and clean/escape html
if(!msg)
- msg = input(src,"Message:", "Private message to [key_name(recipient, 0, 0)]") as message|null
+ msg = stripped_multiline_input(src,"Message:", "Private message to [key_name(recipient, 0, 0)]")
msg = trim(msg)
if(!msg)
return
@@ -191,7 +191,7 @@
spawn() //so we don't hold the caller proc up
var/sender = src
var/sendername = key
- var/reply = input(recipient, msg,"Admin PM from-[sendername]", "") as text|null //show message and await a reply
+ var/reply = stripped_multiline_input(recipient, msg,"Admin PM from-[sendername]", "") //show message and await a reply
if(recipient && reply)
if(sender)
recipient.cmd_admin_pm(sender,reply) //sender is still about, let's reply to them
diff --git a/code/modules/admin/verbs/borgpanel.dm b/code/modules/admin/verbs/borgpanel.dm
index 869e44e4f5..35f4ddb3e5 100644
--- a/code/modules/admin/verbs/borgpanel.dm
+++ b/code/modules/admin/verbs/borgpanel.dm
@@ -36,7 +36,7 @@
/datum/borgpanel/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.admin_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "borgopanel", "Borg Panel", 700, 700, master_ui, state)
+ ui = new(user, src, ui_key, "BorgPanel", "Borg Panel", 700, 700, master_ui, state)
ui.open()
/datum/borgpanel/ui_data(mob/user)
diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm
index 8406248164..f068f05a4a 100644
--- a/code/modules/admin/verbs/debug.dm
+++ b/code/modules/admin/verbs/debug.dm
@@ -573,7 +573,7 @@
if(Rad.anchored)
if(!Rad.loaded_tank)
var/obj/item/tank/internals/plasma/Plasma = new/obj/item/tank/internals/plasma(Rad)
- Plasma.air_contents.gases[/datum/gas/plasma] = 70
+ Plasma.air_contents.set_moles(/datum/gas/plasma,70)
Rad.drainratio = 0
Rad.loaded_tank = Plasma
Plasma.forceMove(Rad)
diff --git a/code/modules/admin/verbs/diagnostics.dm b/code/modules/admin/verbs/diagnostics.dm
index 072fbaa123..b3bea2201c 100644
--- a/code/modules/admin/verbs/diagnostics.dm
+++ b/code/modules/admin/verbs/diagnostics.dm
@@ -1,15 +1,14 @@
/proc/show_air_status_to(turf/target, mob/user)
var/datum/gas_mixture/env = target.return_air()
- var/list/env_gases = env.gases
var/burning = FALSE
if(isopenturf(target))
var/turf/open/T = target
if(T.active_hotspot)
burning = TRUE
- var/list/lines = list("[AREACOORD(target)]: [env.temperature] K ([env.temperature - T0C] C), [env.return_pressure()] kPa[(burning)?(", burning"):(null)]")
- for(var/id in env_gases)
- var/moles = env_gases[id]
+ var/list/lines = list("[AREACOORD(target)]: [env.return_temperature()] K ([env.return_temperature() - T0C] C), [env.return_pressure()] kPa[(burning)?(", burning"):(null)]")
+ for(var/id in env.get_gases())
+ var/moles = env.get_moles(id)
if (moles >= 0.00001)
lines += "[GLOB.meta_gas_names[id]]: [moles] mol"
to_chat(usr, lines.Join("\n"))
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index d335cfb171..5027e8fe51 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -421,7 +421,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
new_character.real_name = record_found.fields["name"]
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"])
+ new_character.hardset_dna(record_found.fields["identity"], record_found.fields["enzymes"], null, record_found.fields["name"], record_found.fields["blood_type"], new record_found.fields["species"], record_found.fields["features"])
else
var/datum/preferences/A = new()
A.copy_to(new_character)
@@ -1058,13 +1058,6 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
var/datum/atom_hud/A = GLOB.huds[ANTAG_HUD_TRAITOR]
return A.hudusers[mob]
-/client/proc/open_shuttle_manipulator()
- set category = "Admin"
- set name = "Shuttle Manipulator"
- set desc = "Opens the shuttle manipulator UI."
-
- for(var/obj/machinery/shuttle_manipulator/M in GLOB.machines)
- M.ui_interact(usr)
/client/proc/run_weather()
set category = "Fun"
@@ -1276,7 +1269,19 @@ 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_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/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,
+ ADMIN_PUNISHMENT_PICKLE,
+ ADMIN_PUNISHMENT_FRY)
var/punishment = input("Choose a punishment", "DIVINE SMITING") as null|anything in punishment_list
@@ -1341,7 +1346,7 @@ 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)
+ if(ADMIN_PUNISHMENT_CUSTOM_PIE)
var/obj/item/reagent_containers/food/snacks/pie/cream/nostun/A = new()
if(!A.reagents)
var/amount = input(usr, "Specify the reagent size of [A]", "Set Reagent Size", 50) as num|null
@@ -1354,6 +1359,10 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
if(amount)
A.reagents.add_reagent(chosen_id, amount)
A.splat(target)
+ if(ADMIN_PUNISHMENT_PICKLE)
+ target.turn_into_pickle()
+ if(ADMIN_PUNISHMENT_FRY)
+ target.fry()
punish_log(target, punishment)
diff --git a/code/modules/admin/verbs/shuttlepanel.dm b/code/modules/admin/verbs/shuttlepanel.dm
new file mode 100644
index 0000000000..a80eafae02
--- /dev/null
+++ b/code/modules/admin/verbs/shuttlepanel.dm
@@ -0,0 +1,76 @@
+/datum/admins/proc/open_shuttlepanel()
+ set category = "Admin"
+ set name = "Shuttle Manipulator"
+ set desc = "Opens the shuttle manipulator UI."
+
+ if(!check_rights(R_ADMIN))
+ return
+
+ SSshuttle.ui_interact(usr)
+
+
+/obj/docking_port/mobile/proc/admin_fly_shuttle(mob/user)
+ var/list/options = list()
+
+ for(var/port in SSshuttle.stationary)
+ if (istype(port, /obj/docking_port/stationary/transit))
+ continue // please don't do this
+ var/obj/docking_port/stationary/S = port
+ if (canDock(S) == SHUTTLE_CAN_DOCK)
+ options[S.name || S.id] = S
+
+ options += "--------"
+ options += "Infinite Transit"
+ options += "Delete Shuttle"
+ options += "Into The Sunset (delete & greentext 'escape')"
+
+ var/selection = input(user, "Select where to fly [name || id]:", "Fly Shuttle") as null|anything in options
+ if(!selection)
+ return
+
+ switch(selection)
+ if("Infinite Transit")
+ destination = null
+ mode = SHUTTLE_IGNITING
+ setTimer(ignitionTime)
+
+ if("Delete Shuttle")
+ if(alert(user, "Really delete [name || id]?", "Delete Shuttle", "Cancel", "Really!") != "Really!")
+ return
+ jumpToNullSpace()
+
+ if("Into The Sunset (delete & greentext 'escape')")
+ if(alert(user, "Really delete [name || id] and greentext escape objectives?", "Delete Shuttle", "Cancel", "Really!") != "Really!")
+ return
+ intoTheSunset()
+
+ else
+ if(options[selection])
+ request(options[selection])
+
+/obj/docking_port/mobile/emergency/admin_fly_shuttle(mob/user)
+ return // use the existing verbs for this
+
+/obj/docking_port/mobile/arrivals/admin_fly_shuttle(mob/user)
+ switch(alert(user, "Would you like to fly the arrivals shuttle once or change its destination?", "Fly Shuttle", "Fly", "Retarget", "Cancel"))
+ if("Cancel")
+ return
+ if("Fly")
+ return ..()
+
+ var/list/options = list()
+
+ for(var/port in SSshuttle.stationary)
+ if (istype(port, /obj/docking_port/stationary/transit))
+ continue // please don't do this
+ var/obj/docking_port/stationary/S = port
+ if (canDock(S) == SHUTTLE_CAN_DOCK)
+ options[S.name || S.id] = S
+
+ var/selection = input(user, "Select the new arrivals destination:", "Fly Shuttle") as null|anything in options
+ if(!selection)
+ return
+ target_dock = options[selection]
+ if(!QDELETED(target_dock))
+ destination = target_dock
+
diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm
index b5ed6c18df..8bff37ab03 100644
--- a/code/modules/antagonists/_common/antag_datum.dm
+++ b/code/modules/antagonists/_common/antag_datum.dm
@@ -23,6 +23,7 @@ GLOBAL_LIST_EMPTY(antagonists)
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.
var/threat = 0 // Amount of threat this antag poses, for dynamic mode
+ var/show_to_ghosts = FALSE // Should this antagonist be shown as antag to ghosts? Shouldn't be used for stealthy antagonists like traitors
var/list/skill_modifiers
@@ -94,6 +95,9 @@ GLOBAL_LIST_EMPTY(antagonists)
if(skill_modifiers)
for(var/A in skill_modifiers)
ADD_SINGLETON_SKILL_MODIFIER(owner, A, type)
+ var/datum/skill_modifier/job/M = GLOB.skill_modifiers[GET_SKILL_MOD_ID(A, type)]
+ if(istype(M))
+ M.name = "[name] Training"
SEND_SIGNAL(owner.current, COMSIG_MOB_ANTAG_ON_GAIN, src)
/datum/antagonist/proc/is_banned(mob/M)
diff --git a/code/modules/antagonists/abductor/abductor.dm b/code/modules/antagonists/abductor/abductor.dm
index 9132288415..b4fc5b5948 100644
--- a/code/modules/antagonists/abductor/abductor.dm
+++ b/code/modules/antagonists/abductor/abductor.dm
@@ -7,6 +7,7 @@
job_rank = ROLE_ABDUCTOR
show_in_antagpanel = FALSE //should only show subtypes
threat = 5
+ show_to_ghosts = TRUE
var/datum/team/abductor_team/team
var/sub_role
var/outfit
diff --git a/code/modules/antagonists/abductor/machinery/console.dm b/code/modules/antagonists/abductor/machinery/console.dm
index b8d5b1bf6d..9c9715ee43 100644
--- a/code/modules/antagonists/abductor/machinery/console.dm
+++ b/code/modules/antagonists/abductor/machinery/console.dm
@@ -24,7 +24,7 @@
var/obj/machinery/computer/camera_advanced/abductor/camera
var/list/datum/icon_snapshot/disguises = list()
-/obj/machinery/abductor/console/attack_hand(mob/user)
+/obj/machinery/abductor/console/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/modules/antagonists/abductor/machinery/dispenser.dm b/code/modules/antagonists/abductor/machinery/dispenser.dm
index 17fa311c8f..ef423b6379 100644
--- a/code/modules/antagonists/abductor/machinery/dispenser.dm
+++ b/code/modules/antagonists/abductor/machinery/dispenser.dm
@@ -22,7 +22,7 @@
gland_colors[i] = random_color()
amounts[i] = rand(1,5)
-/obj/machinery/abductor/gland_dispenser/attack_hand(mob/user)
+/obj/machinery/abductor/gland_dispenser/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/modules/antagonists/abductor/machinery/experiment.dm b/code/modules/antagonists/abductor/machinery/experiment.dm
index f92cb987d0..b94912b635 100644
--- a/code/modules/antagonists/abductor/machinery/experiment.dm
+++ b/code/modules/antagonists/abductor/machinery/experiment.dm
@@ -21,7 +21,7 @@
return
close_machine(target)
-/obj/machinery/abductor/experiment/attack_hand(mob/user)
+/obj/machinery/abductor/experiment/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/modules/antagonists/blob/blob.dm b/code/modules/antagonists/blob/blob.dm
index 1b076c9302..9a82bb546b 100644
--- a/code/modules/antagonists/blob/blob.dm
+++ b/code/modules/antagonists/blob/blob.dm
@@ -2,6 +2,7 @@
name = "Blob"
roundend_category = "blobs"
antagpanel_category = "Blob"
+ show_to_ghosts = TRUE
job_rank = ROLE_BLOB
threat = 20
var/datum/action/innate/blobpop/pop_action
diff --git a/code/modules/antagonists/bloodsucker/items/bloodsucker_organs.dm b/code/modules/antagonists/bloodsucker/items/bloodsucker_organs.dm
index 640c53946f..25de64fe34 100644
--- a/code/modules/antagonists/bloodsucker/items/bloodsucker_organs.dm
+++ b/code/modules/antagonists/bloodsucker/items/bloodsucker_organs.dm
@@ -31,10 +31,6 @@
beating = 0
var/fakingit = 0
-/obj/item/organ/heart/vampheart/prepare_eat()
- ..()
- // Do cool stuff for eating vamp heart?
-
/obj/item/organ/heart/vampheart/Restart()
beating = 0 // DONT run ..(). We don't want to start beating again.
return 0
diff --git a/code/modules/antagonists/bloodsucker/objects/bloodsucker_coffin.dm b/code/modules/antagonists/bloodsucker/objects/bloodsucker_coffin.dm
index e85d3af5a0..a555677719 100644
--- a/code/modules/antagonists/bloodsucker/objects/bloodsucker_coffin.dm
+++ b/code/modules/antagonists/bloodsucker/objects/bloodsucker_coffin.dm
@@ -110,7 +110,7 @@
if (bloodsuckerdatum && bloodsuckerdatum.coffin == src)
bloodsuckerdatum.coffin = null
bloodsuckerdatum.lair = null
- to_chat(resident, "You sense that the link with your coffin, your sacred place of rest, has been brokem! You will need to seek another.")
+ to_chat(resident, "You sense that the link with your coffin, your sacred place of rest, has been broken! You will need to seek another.")
resident = null // Remove resident. Because this object isnt removed from the game immediately (GC?) we need to give them a way to see they don't have a home anymore.
/obj/structure/closet/crate/coffin/can_open(mob/living/user)
diff --git a/code/modules/antagonists/bloodsucker/objects/bloodsucker_crypt.dm b/code/modules/antagonists/bloodsucker/objects/bloodsucker_crypt.dm
index 090ef45d89..9301d0c239 100644
--- a/code/modules/antagonists/bloodsucker/objects/bloodsucker_crypt.dm
+++ b/code/modules/antagonists/bloodsucker/objects/bloodsucker_crypt.dm
@@ -217,7 +217,7 @@
return FALSE
return ..()
-/obj/structure/bloodsucker/vassalrack/attack_hand(mob/user)
+/obj/structure/bloodsucker/vassalrack/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
//. = ..() // Taken from sacrificial altar in divine.dm
//if(.)
// return
@@ -469,7 +469,7 @@
. += {"This is a magical candle which drains at the sanity of the fools who havent yet accepted your master, as long as it is active.\n
You can turn it on and off by clicking on it while you are next to it"} */
-/obj/structure/bloodsucker/candelabrum/attack_hand(mob/user)
+/obj/structure/bloodsucker/candelabrum/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
var/datum/antagonist/vassal/T = user.mind.has_antag_datum(ANTAG_DATUM_VASSAL)
if(AmBloodsucker(user) || istype(T))
toggle()
diff --git a/code/modules/antagonists/bloodsucker/powers/veil.dm b/code/modules/antagonists/bloodsucker/powers/veil.dm
index 422d645ad2..b170b9d442 100644
--- a/code/modules/antagonists/bloodsucker/powers/veil.dm
+++ b/code/modules/antagonists/bloodsucker/powers/veil.dm
@@ -101,7 +101,7 @@
H.update_hair()
H.update_body_parts()
- // Wait here til we deactivate power or go unconscious
+ // Wait here until we deactivate power or go unconscious
var/datum/antagonist/bloodsucker/bloodsuckerdatum = owner.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
while (ContinueActive(owner) && istype(bloodsuckerdatum))//active && owner && owner.stat == CONSCIOUS)
bloodsuckerdatum.AddBloodVolume(-0.2)
diff --git a/code/modules/antagonists/changeling/cellular_emporium.dm b/code/modules/antagonists/changeling/cellular_emporium.dm
index b2c1a52a4a..6abefeefe7 100644
--- a/code/modules/antagonists/changeling/cellular_emporium.dm
+++ b/code/modules/antagonists/changeling/cellular_emporium.dm
@@ -16,7 +16,7 @@
/datum/cellular_emporium/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, 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)
- ui = new(user, src, ui_key, "cellular_emporium", name, 900, 480, master_ui, state)
+ ui = new(user, src, ui_key, "CellularEmporium", name, 900, 480, master_ui, state)
ui.open()
/datum/cellular_emporium/ui_data(mob/user)
diff --git a/code/modules/antagonists/changeling/changeling.dm b/code/modules/antagonists/changeling/changeling.dm
index 35639bfd97..3267f2bdc1 100644
--- a/code/modules/antagonists/changeling/changeling.dm
+++ b/code/modules/antagonists/changeling/changeling.dm
@@ -397,20 +397,31 @@
escape_objective_possible = FALSE
break
var/changeling_objective = rand(1,3)
+ var/generic_absorb_objective = FALSE
+ var/multiple_lings = length(get_antag_minds(/datum/antagonist/changeling,TRUE)) > 1
switch(changeling_objective)
if(1)
- var/datum/objective/absorb/absorb_objective = new
- absorb_objective.owner = owner
- absorb_objective.gen_amount_goal(6, 8)
- objectives += absorb_objective
+ generic_absorb_objective = TRUE
if(2)
- var/datum/objective/absorb_changeling/ac = new
- ac.owner = owner
- objectives += ac
+ if(multiple_lings)
+ var/datum/objective/absorb_changeling/ac = new
+ ac.owner = owner
+ objectives += ac
+ else
+ generic_absorb_objective = TRUE
if(3)
- var/datum/objective/absorb_most/ac = new
- ac.owner = owner
- objectives += ac
+ if(multiple_lings)
+ var/datum/objective/absorb_most/ac = new
+ ac.owner = owner
+ objectives += ac
+ else
+ generic_absorb_objective = TRUE
+
+ if(generic_absorb_objective)
+ var/datum/objective/absorb/absorb_objective = new
+ absorb_objective.owner = owner
+ absorb_objective.gen_amount_goal(6, 8)
+ objectives += absorb_objective
if(prob(60))
if(prob(85))
diff --git a/code/modules/antagonists/changeling/powers/spiders.dm b/code/modules/antagonists/changeling/powers/spiders.dm
index 6bd15fea92..69900ea8f9 100644
--- a/code/modules/antagonists/changeling/powers/spiders.dm
+++ b/code/modules/antagonists/changeling/powers/spiders.dm
@@ -1,7 +1,7 @@
/obj/effect/proc_holder/changeling/spiders
name = "Spread Infestation"
desc = "Our form divides, creating arachnids which will grow into deadly beasts."
- helptext = "The spiders are thoughtless creatures, and may attack their creators when fully grown. Requires at least 3 DNA gained through Absorb, and not through DNA sting. This ability is very loud, and will guarantee that our blood will react violently to heat."
+ helptext = "The spiders are thoughtless creatures, and may attack their creators when fully grown. Requires at least 3 DNA gained through Absorb (regardless of current amount), and not through DNA sting. This ability is very loud, and will guarantee that our blood will react violently to heat."
chemical_cost = 45
dna_cost = 1
loudness = 4
diff --git a/code/modules/antagonists/changeling/powers/transform.dm b/code/modules/antagonists/changeling/powers/transform.dm
index 795ba772d6..c0979b6936 100644
--- a/code/modules/antagonists/changeling/powers/transform.dm
+++ b/code/modules/antagonists/changeling/powers/transform.dm
@@ -18,7 +18,7 @@
//ATTACK HAND IGNORING PARENT RETURN VALUE
-/obj/item/clothing/glasses/changeling/attack_hand(mob/user)
+/obj/item/clothing/glasses/changeling/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(loc == user && user.mind && user.mind.has_antag_datum(/datum/antagonist/changeling))
to_chat(user, "You reabsorb [src] into your body.")
qdel(src)
@@ -34,7 +34,7 @@
//ATTACK HAND IGNORING PARENT RETURN VALUE
-/obj/item/clothing/under/changeling/attack_hand(mob/user)
+/obj/item/clothing/under/changeling/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(loc == user && user.mind && user.mind.has_antag_datum(/datum/antagonist/changeling))
to_chat(user, "You reabsorb [src] into your body.")
qdel(src)
@@ -51,7 +51,7 @@
//ATTACK HAND IGNORING PARENT RETURN VALUE
-/obj/item/clothing/suit/changeling/attack_hand(mob/user)
+/obj/item/clothing/suit/changeling/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(loc == user && user.mind && user.mind.has_antag_datum(/datum/antagonist/changeling))
to_chat(user, "You reabsorb [src] into your body.")
qdel(src)
@@ -66,7 +66,7 @@
ADD_TRAIT(src, TRAIT_NODROP, CHANGELING_TRAIT)
//ATTACK HAND IGNORING PARENT RETURN VALUE
-/obj/item/clothing/head/changeling/attack_hand(mob/user)
+/obj/item/clothing/head/changeling/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(loc == user && user.mind && user.mind.has_antag_datum(/datum/antagonist/changeling))
to_chat(user, "You reabsorb [src] into your body.")
qdel(src)
@@ -82,7 +82,7 @@
//ATTACK HAND IGNORING PARENT RETURN VALUE
-/obj/item/clothing/shoes/changeling/attack_hand(mob/user)
+/obj/item/clothing/shoes/changeling/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(loc == user && user.mind && user.mind.has_antag_datum(/datum/antagonist/changeling))
to_chat(user, "You reabsorb [src] into your body.")
qdel(src)
@@ -98,7 +98,7 @@
//ATTACK HAND IGNORING PARENT RETURN VALUE
-/obj/item/clothing/gloves/changeling/attack_hand(mob/user)
+/obj/item/clothing/gloves/changeling/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(loc == user && user.mind && user.mind.has_antag_datum(/datum/antagonist/changeling))
to_chat(user, "You reabsorb [src] into your body.")
qdel(src)
@@ -114,7 +114,7 @@
//ATTACK HAND IGNORING PARENT RETURN VALUE
-/obj/item/clothing/mask/changeling/attack_hand(mob/user)
+/obj/item/clothing/mask/changeling/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(loc == user && user.mind && user.mind.has_antag_datum(/datum/antagonist/changeling))
to_chat(user, "You reabsorb [src] into your body.")
qdel(src)
@@ -132,7 +132,7 @@
//ATTACK HAND IGNORING PARENT RETURN VALUE
-/obj/item/changeling/attack_hand(mob/user)
+/obj/item/changeling/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(loc == user && user.mind && user.mind.has_antag_datum(/datum/antagonist/changeling))
to_chat(user, "You reabsorb [src] into your body.")
qdel(src)
diff --git a/code/modules/antagonists/clockcult/clock_effects/clock_sigils.dm b/code/modules/antagonists/clockcult/clock_effects/clock_sigils.dm
index 559c211943..344e8472ee 100644
--- a/code/modules/antagonists/clockcult/clock_effects/clock_sigils.dm
+++ b/code/modules/antagonists/clockcult/clock_effects/clock_sigils.dm
@@ -28,7 +28,7 @@
return //you can't tk stomp sigils, but you can hit them with something
//ATTACK HAND IGNORING PARENT RETURN VALUE
-/obj/effect/clockwork/sigil/attack_hand(mob/user)
+/obj/effect/clockwork/sigil/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(iscarbon(user) && !user.stat)
if(is_servant_of_ratvar(user) && user.a_intent != INTENT_HARM)
return ..()
diff --git a/code/modules/antagonists/clockcult/clock_effects/spatial_gateway.dm b/code/modules/antagonists/clockcult/clock_effects/spatial_gateway.dm
index 293625d9a7..528fe44601 100644
--- a/code/modules/antagonists/clockcult/clock_effects/spatial_gateway.dm
+++ b/code/modules/antagonists/clockcult/clock_effects/spatial_gateway.dm
@@ -71,7 +71,7 @@
..()
//ATTACK HAND IGNORING PARENT RETURN VALUE
-/obj/effect/clockwork/spatial_gateway/attack_hand(mob/living/user)
+/obj/effect/clockwork/spatial_gateway/attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
if(!uses)
return FALSE
if(user.pulling && user.a_intent == INTENT_GRAB && isliving(user.pulling))
diff --git a/code/modules/antagonists/clockcult/clock_helpers/scripture_checks.dm b/code/modules/antagonists/clockcult/clock_helpers/scripture_checks.dm
index e5497d7c9f..66e20b6e87 100644
--- a/code/modules/antagonists/clockcult/clock_helpers/scripture_checks.dm
+++ b/code/modules/antagonists/clockcult/clock_helpers/scripture_checks.dm
@@ -38,10 +38,11 @@
set_slab.update_quickbind()
/proc/generate_all_scripture()
- if(!GLOB.all_scripture.len)
- for(var/V in sortList(subtypesof(/datum/clockwork_scripture), /proc/cmp_clockscripture_priority))
- var/datum/clockwork_scripture/S = new V
- GLOB.all_scripture[S.type] = S
+ if(GLOB.all_scripture.len)
+ return
+ for(var/V in sortList(subtypesof(/datum/clockwork_scripture) - list(/datum/clockwork_scripture/channeled, /datum/clockwork_scripture/create_object, /datum/clockwork_scripture/create_object/construct), /proc/cmp_clockscripture_priority))
+ var/datum/clockwork_scripture/S = new V
+ GLOB.all_scripture[S.type] = S
//changes construction value
/proc/change_construction_value(amount)
diff --git a/code/modules/antagonists/clockcult/clock_helpers/slab_abilities.dm b/code/modules/antagonists/clockcult/clock_helpers/slab_abilities.dm
index 81ad7ddc26..89ed669e7b 100644
--- a/code/modules/antagonists/clockcult/clock_helpers/slab_abilities.dm
+++ b/code/modules/antagonists/clockcult/clock_helpers/slab_abilities.dm
@@ -113,8 +113,7 @@
for(var/i in 1 to healseverity)
new /obj/effect/temp_visual/heal(targetturf, "#1E8CE1")
if(totaldamage)
- L.adjustBruteLoss(-brutedamage)
- L.adjustFireLoss(-burndamage)
+ L.heal_overall_damage(brutedamage, burndamage, only_organic = FALSE) //Maybe a machine god shouldn't murder augmented followers instead of healing them
L.adjustOxyLoss(-oxydamage)
L.adjustToxLoss(totaldamage * 0.5, TRUE, TRUE)
clockwork_say(ranged_ability_user, text2ratvar("[has_holy_water ? "Heal tainted" : "Mend wounded"] flesh!"))
diff --git a/code/modules/antagonists/clockcult/clock_items/clockwork_slab.dm b/code/modules/antagonists/clockcult/clock_items/clockwork_slab.dm
index cc4176ed57..9e35a42a4e 100644
--- a/code/modules/antagonists/clockcult/clock_items/clockwork_slab.dm
+++ b/code/modules/antagonists/clockcult/clock_items/clockwork_slab.dm
@@ -1,7 +1,7 @@
/obj/item/clockwork/slab //Clockwork slab: The most important tool in Ratvar's arsenal. Allows scripture recital, tutorials, and generates components.
name = "clockwork slab"
desc = "A strange metal tablet. A clock in the center turns around and around."
- clockwork_desc = "A link between you and the Celestial Derelict. It contains information, recites scripture, and is your most vital tool as a Servant.
\
+ clockwork_desc = "A link between you and the Celestial Derelict. It contains information, recites scripture, and is your most vital tool as a Servant.\
It can be used to link traps and triggers by attacking them with the slab. Keep in mind that traps linked with one another will activate in tandem!"
icon_state = "dread_ipad"
@@ -15,16 +15,19 @@
var/busy //If the slab is currently being used by something
var/no_cost = FALSE //If the slab is admin-only and needs no components and has no scripture locks
var/speed_multiplier = 1 //multiples how fast this slab recites scripture
- var/selected_scripture = SCRIPTURE_DRIVER
+ // var/selected_scripture = SCRIPTURE_DRIVER //handled UI side
var/obj/effect/proc_holder/slab/slab_ability //the slab's current bound ability, for certain scripture
- var/recollecting = FALSE //if we're looking at fancy recollection
+ var/recollecting = TRUE //if we're looking at fancy recollection. tutorial enabled by default
var/recollection_category = "Default"
var/list/quickbound = list(/datum/clockwork_scripture/spatial_gateway, \
/datum/clockwork_scripture/ranged_ability/kindle, /datum/clockwork_scripture/ranged_ability/hateful_manacles) //quickbound scripture, accessed by index
var/maximum_quickbound = 5 //how many quickbound scriptures we can have
+ var/ui_x = 800
+ var/ui_z = 420
+
var/obj/structure/destructible/clockwork/trap/linking //If we're linking traps together, which ones we're doing
/obj/item/clockwork/slab/internal //an internal motor for mobs running scripture
@@ -55,7 +58,7 @@
return ..()
//ATTACK HAND IGNORING PARENT RETURN VALUE
-/obj/item/clockwork/slab/debug/attack_hand(mob/living/user)
+/obj/item/clockwork/slab/debug/attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
if(!is_servant_of_ratvar(user))
add_servant_of_ratvar(user)
return ..()
@@ -146,8 +149,8 @@
if(!quickbound[i])
continue
var/datum/clockwork_scripture/quickbind_slot = quickbound[i]
- . += "Quickbind button: [initial(quickbind_slot.name)]."
- . += "Available power: [DisplayPower(get_clockwork_power())]."
+ . += "Quickbind button: [initial(quickbind_slot.name)]."
+ . += "Available power: [DisplayPower(get_clockwork_power())]."
//Slab actions; Hierophant, Quickbind
/obj/item/clockwork/slab/ui_action_click(mob/user, action)
@@ -165,18 +168,19 @@
user.emote("scream")
user.apply_damage(5, BURN, BODY_ZONE_L_ARM)
user.apply_damage(5, BURN, BODY_ZONE_R_ARM)
- return 0
+ return FALSE
if(!is_servant_of_ratvar(user))
to_chat(user, "The information on [src]'s display shifts rapidly. After a moment, your head begins to pound, and you tear your eyes away.")
- user.confused += 5
- user.dizziness += 5
- return 0
+ if(user.confused || user.dizziness)
+ user.confused += 5
+ user.dizziness += 5
+ return FALSE
if(busy)
to_chat(user, "[src] refuses to work, displaying the message: \"[busy]!\"")
- return 0
+ return FALSE
if(!no_cost && !can_recite_scripture(user))
to_chat(user, "[src] hums fitfully in your hands, but doesn't seem to do anything...")
- return 0
+ return FALSE
access_display(user)
/obj/item/clockwork/slab/AltClick(mob/living/user)
@@ -195,9 +199,7 @@
/obj/item/clockwork/slab/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.inventory_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
- ui = new(user, src, ui_key, "clockwork_slab", name, 800, 420, master_ui, state)
- ui.set_autoupdate(FALSE) //we'll update this occasionally, but not as often as possible
- ui.set_style("clockwork")
+ ui = new(user, src, ui_key, "ClockworkSlab", name, ui_x, ui_z, master_ui, state)
ui.open()
/obj/item/clockwork/slab/proc/recite_scripture(datum/clockwork_scripture/scripture, mob/living/user)
@@ -207,10 +209,12 @@
to_chat(user, "You need to hold the slab in your active hand to recite scripture!")
return FALSE
var/initial_tier = initial(scripture.tier)
- if(initial_tier != SCRIPTURE_PERIPHERAL)
- if(!GLOB.ratvar_awakens && !no_cost && !SSticker.scripture_states[initial_tier])
- to_chat(user, "That scripture is not unlocked, and cannot be recited!")
- return FALSE
+ if(initial_tier == SCRIPTURE_PERIPHERAL)
+ to_chat(user, "Nice try using href exploits")
+ return
+ if(!GLOB.ratvar_awakens && !no_cost && !SSticker.scripture_states[initial_tier])
+ to_chat(user, "That scripture is not unlocked, and cannot be recited!")
+ return FALSE
var/datum/clockwork_scripture/scripture_to_recite = new scripture
scripture_to_recite.slab = src
scripture_to_recite.invoker = user
@@ -218,26 +222,6 @@
return TRUE
-//Guide to Serving Ratvar
-/obj/item/clockwork/slab/proc/recollection()
- var/list/textlist = list("If you're seeing this, file a bug report.")
- if(GLOB.ratvar_awakens)
- textlist = list("")
- for(var/i in 1 to 100)
- textlist += "HONOR RATVAR "
- textlist += ""
- else
- textlist = list("[text2ratvar("Purge all untruths and honor Engine.")]
\
- \
- NOTICE: This information is out of date. Read the Ark & You primer in your backpack or read the wiki page for current info.
\
-
\
- These pages serve as the archives of Ratvar, the Clockwork Justiciar. This section of your slab has information on being as a Servant, advice for what to do next, and \
- pointers for serving the master well. You should recommended that you check this area for help if you get stuck or need guidance on what to do next.
\
- \
- Disclaimer: Many objects, terms, and phrases, such as Servant, Cache, and Slab, are capitalized like proper nouns. This is a quirk of the Ratvarian language; \
- do not let it confuse you! You are free to use the names in pronoun form when speaking in normal languages.
")
- return textlist.Join()
-
//Gets text for a certain section. "Default" is used for when you first open Recollection.
//Current sections (make sure to update this if you add one:
//- Basics
@@ -246,261 +230,98 @@
//- Scripture
//- Power
//- Conversion
-/obj/item/clockwork/slab/proc/get_recollection_text(section)
- var/list/dat = list()
- switch(section)
- if("Default")
- dat += "You can browse the above sections as you please. They're designed to be read in order, but feel free to pick and choose between them."
- if("Getting Started")
- dat += "Getting Started
"
- dat += "Welcome, Servant! This section houses the utmost basics of being a Servant of Ratvar, and is much more informal than the other sections. Being a Servant of \
- Ratvar is a very complex role, with many systems, objects, and resources to use effectively and creatively.
"
- dat += "This section of your clockwork slab covers everything that Servants have to be aware of, but is a long read because of how in-depth the systems are. Knowing \
- how to use the tools at your disposal makes all the difference between a clueless Servant and a great one.
"
- dat += "If this is your first time being a Servant, relax. It's very much possible that you'll fail, but it's impossible to learn without making mistakes. For the time \
- being, use the Hierophant Network button in the top left-hand corner of your screen to try and get in touch with your fellow Servants; ignore the others for now. This button \
- will let you send messages across space and time to all other Servants. This makes it great for coordinating, and you should use it often! Note: Using \
- this will cause you to whisper your message aloud, so doing so in a public place is very suspicious and you should try to restrict it to private use.
"
- dat += "If you aren't willing or don't have the time to read through every section, you can still help your teammates! Ask if they've set up a base. If they have, head there \
- and ask however you can help; chances are there's always something. If not, it's your job as a Servant to get one up and running! Try to find a secluded, low-traffic area, \
- like the auxiliary base or somewhere deep in maintenance. You'll want to go into the Drivers section of the slab and look for Tinkerer's Cache. Find a nice spot and \
- create one. This serves as a storage for components, the cult's primary resource. (Your slab's probably produced a few by now.) By attacking that cache with this \
- slab, you'll offload all your components into it, and all Servants will be able to use those components from any distance - all Tinkerer's Caches are linked!
"
- dat += "Once you have a base up and running, contact your fellows and let them know. You should come back here often to drop off the slab's components, and your fellows \
- should do the same, either in this cache or in ones of their own.
"
- dat += "If you think you're confident in taking further steps to help the cult, feel free to move onto the other sections. If not, let your allies know that you're new and \
- would appreciate the help they might offer you. Most experienced Servants would be happy to help; if everyone is inexperienced, then you'll have to step out of your comfort \
- zone and read onto the other sections. It's very likely that you might fail, but don't worry too much about it; you can't learn effectively without making mistakes.
"
- dat += "For now, welcome! If you're looking to learn, you should start with the Basics section, then move onto Components and Scripture. At the very \
- least, you should read the Conversion section, as it outlines the most important aspects of being a Servant. Good luck!
"
- dat += "-=-=-=-=-=-"
- if("Basics")
- dat += "Servant Basics
"
- dat += "The first thing any Servant should know is their slab, inside and out. The clockwork slab is by far your most important tool. It allows you to speak with your \
- fellow Servants, create components that fuel many of your abilities, use those abilities, and should be kept safe and hidden on your person at all times. If you have not \
- done so already, it's a good idea to check for any fellow Servants using the Hierophant Network button in the top-left corner of your screen; due to the cult's nature, \
- teamwork is an instrumental component of your success.
" //get it? component? ha!
- dat += "As a Servant of Ratvar, the tools you are given focus around building and maintaining bases and outposts. A great deal of your power comes from stationary \
- structures, and without constructing a base somewhere, it's essentially impossible to succeed. Finding a good spot to build a base can be difficult, and it's recommended \
- that you choose an area in low-traffic part of the station (such as the auxiliary base). Make sure to disconnect any cameras in the area beforehand.
"
- dat += "Because of how complex being a Servant is, it isn't possible to fit much information into this section. It's highly recommended that you read the Components \
- and Scripture sections next. Not knowing how these two systems work will cripple both you and your fellows, and lead to a frustrating experience for everyone.
"
- dat += "-=-=-=-=-=-"
- if("Terminology")
- dat += "Common Servant Terminology
"
- dat += "This isn't intended to be read all at once; you are advised to treat it moreso as a glossary.
"
- dat += "General
"
- dat += "Servant: A person or robot who serves Ratvar. You are one of these.
"
- dat += "Cache: A Tinkerer's Cache, which is a structure that stores and creates components.
"
- dat += "CV: Construction Value. All clockwork structures, floors, and walls increase this number.
"
- dat += "Vitality: Used for healing effects, produced by Ratvarian spear attacks and Vitality Matrices.
"
- dat += "Geis: An important scripture used to make normal crew and robots into Servants of Ratvar.
"
- dat += "Ark: The cult's win condition, a huge structure that needs to be defended.
"
- dat += "Items
"
- dat += "Slab: A clockwork slab, a Servant's most important tool. You're holding one! Keep it safe and hidden.
"
- dat += "Visor: A judicial visor, which is a pair of glasses that can smite an area for a brief stun and delayed explosion.
"
- dat += "Wraith Specs: Wraith spectacles, which provide true sight (X-ray, night vision) but damage the wearer's eyes.
"
- dat += "Spear: A Ratvarian spear, which is a very powerful melee weapon that produces Vitality.
"
- dat += "Fabricator: A replica fabricator, which converts objects into clockwork versions.
"
- dat += "Constructs
"
- dat += "Marauder: A clockwork marauder, which is a powerful bodyguard that hides in its owner.
"
- dat += "Structures (* = requires power)
"
- dat += "Warden: An ocular warden, which is a ranged turret that damages non-Servants that see it.
"
- dat += "Prism*: A prolonging prism, which delays the shuttle for two minutes at a huge power cost.
"
- dat += "Motor*: A mania motor, which serves as area-denial through negative effects and eventual conversion.
"
- dat += "Daemon*: A tinkerer's daemon, which quickly creates components.
"
- dat += "Obelisk*: A clockwork obelisk, which can broadcast large messages and allows limited teleportation.
"
- dat += "Sigils
"
- dat += "Note: Sigils can be stacked on top of one another, making certain sigils very effective when paired!
"
- dat += "Transgression: Stuns the first non-Servant to cross it for ten seconds and blinds others nearby. Disappears on use.
"
- dat += "Submission: Converts the first non-Servant to stand on the sigil for seven seconds. Disappears on use.
"
- dat += "Matrix: Drains health from non-Servants, producing Vitality. Can heal and revive Servants.
"
- dat += "Accession: Identical to the Sigil of Submission, but doesn't disappear on use. It can also convert a single mindshielded target, but will disappear after doing this.
"
- dat += "Transmission: Drains and stores power for clockwork structures. Feeding it brass sheets will create additional power.
"
- dat += "-=-=-=-=-=-"
- if("Components")
- dat += "Components & Their Uses
"
- dat += "Components are your primary resource as a Servant. There are five types of component, with each one being used in different roles:
"
- dat += "Although this is a good rule of thumb, their effects become much more nuanced when used together. For instance, a turret might have both belligerent eyes and \
- vanguard cogwheels as construction requirements, because it defends its allies by harming its enemies.
"
- dat += "Components' primary use is fueling scripture (covered in its own section), and they can be created through various ways. This clockwork slab, for instance, \
- will make a random component of every type - or a specific one, if you choose a target component from the interface - every remove me already. This number will increase \
- as the amount of Servants in the covenant increase; additionally, slabs can only produce components when held by a Servant, and holding more than one slab will cause both \
- of them to halt progress until one of them is removed from their person.
"
- dat += "Your slab has an internal storage of components, but it isn't meant to be the main one. Instead, there's a global storage of components that can be \
- added to through various ways. Anything that needs components will first draw them from the global storage before attempting to draw them from the slab. Most methods of \
- component production add to the global storage. You can also offload components from your slab into the global storage by using it on a Tinkerer's Cache, a structure whose \
- primary purpose is to do just that (although it will also slowly produce components when placed near a brass wall.)
"
- dat += "-=-=-=-=-=-"
- if("Scripture")
- dat += "The Ancient Scripture
"
- dat += "If you have experience with the Nar'Sian cult (or the \"blood cult\") then you will know of runes. They are the manifestations of the Geometer's power, and where most \
- of the cult's supernatural ability comes from. The Servant equivalent of runes is called scripture, and unlike runes, scripture is loaded into your clockwork slab.
"
- dat += "Each piece of scripture has widely-varying effects. Your most important scripture, Geis, is obvious and suspicious, but charges your slab with energy and allows \
- you to attack a non-Servant in melee range to restrain them and begin converting them into a Servant. This is just one example; each piece of scripture can be simple or \
- complex, be obvious or have hidden mechanics that can only be found through trial and error.
"
- dat += "Any given piece of scripture has a component cost listed in its \"Recite\" button. The acronyms for the components should be obvious if you've read about components \
- already; reciting this piece of scripture will consume the listed components, first from the global storage and then from your slab. Note that failing to recite a piece of \
- scripture will not consume the components required to recite it.
"
- dat += "It should also be noted that some scripture cannot be recited alone. Especially with more powerful scripture, you may need multiple Servants to recite a piece of \
- scripture; both of you will need to stand still until the recital completes. Only human and silicon Servants are valid for scripture recital! Constructs cannot help \
- in reciting scripture.
"
- dat += "Finally, scripture is separated into three \"tiers\" based on power: Drivers, Scripts, and Applications.[prob(1) ? " (The Revenant tier was removed a long time ago. \
- Get with the times.)" : ""] You can view the requirements to unlock each tier in its scripture list. Once a tier is unlocked, it's unlocked permanently; the cult only needs to fill the \
- requirement for unlocking a tier once!
"
- dat += "-=-=-=-=-=-"
- if("Power")
- dat += "Power! Unlimited Power!
"
- dat += "In the early stages of the cult, the only resource that must be actively worried about is components. However, as new scripture is unlocked, a new resource \
- becomes necessary: power. Almost all clockwork structures require power to function in some way. There is nothing special about this power; it's mere electricity, \
- and can be harnessed in several ways.
"
- dat += "To begin with, if there is no other source of power nearby, structures will draw from the area's APC, assuming it has one. This is inefficient and ill-advised as \
- anything but a last resort. Instead, it is recommended that a Sigil of Transmission is created. This sigil serves as both battery and power generator for nearby clockwork \
- structures, and those structures will happily draw power from the sigil before they resort to APCs.
"
- dat += "Generating power is less easy. The most reliable and efficient way is using brass sheets; attacking a sigil of transmission with brass sheets will convert them \
- to power, at a rate of [DisplayPower(POWER_FLOOR)] per sheet. (Brass sheets are created from replica fabricators, which are explained more in detail in the Conversion section.) \
- Activating a sigil of transmission will also cause it to drain power from the nearby area, which, while effective, serves as an obvious tell that there is something wrong.
"
- dat += "Without power, many structures will not function, making a base vulnerable to attack. For this reason, it is critical that you keep an eye on your power reserves and \
- ensure that they remain comfortably high.
"
- dat += "-=-=-=-=-=-"
- if("Conversion")
- dat += "Growing the Ranks
"
- dat += "Because the Servants of Ratvar are a cult, the main method to gain more power is to \"enlighten\" normal crew into new Servants. When a crewmember is converted, \
- they become a full-fledged Servant, ready and willing to serve the cause of Ratvar. It should also be noted that silicon crew, such as cyborgs and the AI, can be \
- converted just like normal crew and will gain special abilities; this is covered later. This section will also cover converting the station's structure itself; walls, \
- floors, windows, tables, and other objects can all be converted into clockwork versions, and serve an important purpose.
"
- dat += "A Note on Geis: There are several ways to convert humans and silicons. However, the most important tool to making them work is \
- Geis, a Driver-tier scripture. Using it whispers an invocation very quickly and charges your slab with power. In addition to making the slab visible in your hand, \
- you can now use it on a target within melee range to bind and mute them. It is by far your most reliable tool for capturing potential converts and targets, though it is incredibly \
- obvious. In addition, you are unable to take any actions other than moving while your target is bound. The binding will last for 25 seconds and mute for about 13 seconds, though \
- allies can use Geis to refresh these effects.
"
- dat += "Converting: The two methods of conversion are the sigil of submission, whose purpose is to do so, and the mania motor. \
- The sigil of submission is a sigil that, when stood on by a non-Servant for eight seconds, will convert that non-Servant. This is the only practical way to convert targets. \
- Sigils of submission are cheap, early, and permanent! Make sure sigils of submission are placed only in bases or otherwise hidden spots, or with a sigil of transgression on them. \
- The mania motor, however, is generally unreliable and unlocked later, only converting those who stand near it for an extended period.
"
- dat += "Converting Humans: For obvious reasons, humans are the most common conversion target. Because every crew member is different, and \
- may be armed with different equipment, you should take precautions to ensure that they aren't able to resist. If able, removing a headset is essential, as is restraining \
- them through handcuffs, cable ties, or other restraints. Some crew, like security, are also implanted with mindshield implants; these will prevent conversion and must be \
- surgically removed before they are an eligible convert. Note: The captain is never an eligible convert and should instead be killed or imprisoned. If security \
- begins administering mindshield implants, this will greatly inhibit conversion. Also note that mindshield implants can be broken by a sigil of accession automatically, but \
- the sigil will disappear.
"
- dat += "Converting Silicons: Due to their robotic nature, silicons are generally more predictable than humans in terms of conversion. \
- However, they are also much, much harder to subdue, especially cyborgs. The easiest way to convert a cyborg is by using Geis to restrain them, then dragging them to a sigil \
- of submission. If you stack a sigil of transgression and a sigil of submission, a crossing cyborg will be stunned and helpless to escape before they are converted.
"
- dat += "Converting AIs is very often the hardest task of the cult, and has been the downfall of countless successful Servants. Their omnipresence across the station, \
- coupled with their secure location and ability to lock themselves securely, makes them a powerful target. However, once the AI itself is reached, it is usually completely \
- helpless to resist its own conversion. A very common tactic is to take advantage of a converted cyborg to rush the AI before it is able to react.
"
- dat += "Even once an AI is converted, care must be taken to ensure that it remains hidden. Not only does the AI's core become brassy and thus obvious to an outside \
- observer, but the AI loses the ability to speak in anything but Ratvarian. For this reason, it has to remain completely silent over common radio channels if stealth \
- is at all a priority. This is suspicious and will rapidly lead to the crew checking on it, which usually results in the cult's outing. It is, however, necessary to convert \
- all AIs present on the station before the Ark becomes invokable, so this must be done at some point.
"
- dat += "Converting the Station: Converted objects all serve a purpose and are important to the cult's success. To convert objects, \
- a Servant needs to use a replica fabricator, a handheld tool that uses power to replace objects with clockwork versions. Different clockwork objects have different \
- effects and are often crucial. The most noteworthy are clockwork walls, which automatically \"link\" to any nearby Tinkerer's Caches, causing them to slowly \
- generate components. This is incredibly useful for obvious reasons, and creating a clockwork wall near every Tinkerer's Cache should be prioritized. Clockwork floors \
- will slowly heal any toxin damage suffered by Servants standing on them, and clockwork airlocks can only be opened by Servants.
"
- dat += "The replica fabricator itself is also worth noting. In addition to replacing objects, it can also create brass sheets at the cost of power by using the \
- fabricator in-hand. It can also be used to repair any damaged clockwork structures.
"
- dat += "Replacing objects is almost as, if not as important as, converting new Servants. A base is impossible to manage without clockwork walls at the very least, and \
- once the cult has been outed and the crew are actively searching, there is little reason not to use as many as possible.
"
- dat += "-=-=-=-=-=-"
- else
- dat += "404: [section ? section : "Section"] Not Found!
\
- One of the cogscarabs must've misplaced this section, because the game wasn't able to find any info regarding it. Report this to the coders!"
- return "
[dat.Join()]
"
-//Gets the quickbound scripture as a text block.
-/obj/item/clockwork/slab/proc/get_recollection_quickbinds()
- var/list/dat = list()
- dat += "Quickbound Scripture
\
- You can have up to five scriptures bound to action buttons for easy use.
"
- if(LAZYLEN(quickbound))
+/obj/item/clockwork/slab/ui_data(mob/user) //we display a lot of data via TGUI
+ . = list()
+ .["recollection"] = recollecting
+ .["power"] = DisplayPower(get_clockwork_power())
+ .["power_unformatted"] = get_clockwork_power()
+ // .["rec_text"] = recollection() handled TGUI side
+ .["HONOR_RATVAR"] = GLOB.ratvar_awakens
+ .["scripture"] = list()
+ for(var/s in GLOB.all_scripture)
+ var/datum/clockwork_scripture/S = GLOB.all_scripture[s]
+ if(S.tier == SCRIPTURE_PERIPHERAL) //yes, tiers are the tabs.
+ continue
+
+ var/list/data = list()
+ data["name"] = S.name
+ data["descname"] = S.descname
+ data["tip"] = "[S.desc]\n[S.usage_tip]"
+ data["required"] = "([DisplayPower(S.power_cost)][S.special_power_text ? "+ [replacetext(S.special_power_text, "POWERCOST", "[DisplayPower(S.special_power_cost)]")]" : ""])"
+ data["required_unformatted"] = S.power_cost
+ data["type"] = "[S.type]"
+ data["quickbind"] = S.quickbind //this is if it cant quickbind
+ data["fontcolor"] = get_component_color_bright(S.primary_component)
+ data["important"] = S.important //italic!
+
+ var/found = quickbound.Find(S.type)
+ if(found)
+ data["bound"] = found //number (pos) on where is it on the list
+ if(S.invokers_required > 1)
+ data["invokers"] = "Invokers: [S.invokers_required]"
+
+ .["rec_binds"] = list()
for(var/i in 1 to maximum_quickbound)
+ if(GLOB.ratvar_awakens)
+ return
if(LAZYLEN(quickbound) < i || !quickbound[i])
- dat += "A Quickbind slot, currently set to Nothing.
"
+ .["rec_binds"] += list(list())
else
var/datum/clockwork_scripture/quickbind_slot = quickbound[i]
dat += "A Quickbind slot, currently set to [initial(quickbind_slot.name)].
"
- return dat.Join()
-
-
-/obj/item/clockwork/slab/ui_data(mob/user) //we display a lot of data via TGUI
- var/list/data = list()
- data["power"] = "[DisplayPower(get_clockwork_power())] power is available for scripture and other consumers."
+ .["power"] = "[DisplayPower(get_clockwork_power())] power is available for scripture and other consumers."
switch(selected_scripture) //display info based on selected scripture tier
if(SCRIPTURE_DRIVER)
- data["tier_info"] = "These scriptures are permanently unlocked."
+ .["tier_info"] = "These scriptures are permanently unlocked."
if(SCRIPTURE_SCRIPT)
if(SSticker.scripture_states[SCRIPTURE_SCRIPT])
- data["tier_info"] = "These scriptures are permanently unlocked."
+ .["tier_info"] = "These scriptures are permanently unlocked."
else
- data["tier_info"] = "These scriptures will automatically unlock when the Ark is halfway ready or if [DisplayPower(SCRIPT_UNLOCK_THRESHOLD)] of power is reached."
+ .["tier_info"] = "These scriptures will automatically unlock when the Ark is halfway ready or if [DisplayPower(SCRIPT_UNLOCK_THRESHOLD)] of power is reached."
if(SCRIPTURE_APPLICATION)
if(SSticker.scripture_states[SCRIPTURE_APPLICATION])
- data["tier_info"] = "These scriptures are permanently unlocked."
+ .["tier_info"] = "These scriptures are permanently unlocked."
else
- data["tier_info"] = "Unlock these optional scriptures by converting another servant or if [DisplayPower(APPLICATION_UNLOCK_THRESHOLD)] of power is reached.."
+ .["tier_info"] = "Unlock these optional scriptures by converting another servant or if [DisplayPower(APPLICATION_UNLOCK_THRESHOLD)] of power is reached.."
if(SCRIPTURE_JUDGEMENT)
if(SSticker.scripture_states[SCRIPTURE_JUDGEMENT])
- data["tier_info"] = "These scriptures are permanently unlocked."
+ .["tier_info"] = "These scriptures are permanently unlocked."
else
- data["tier_info"] = "Unlock creation of powerful equipment and structures by gaining five members of the cult.."
+ .["tier_info"] = "Unlock creation of powerful equipment and structures by gaining five members of the cult.."
- data["selected"] = selected_scripture
- data["scripturecolors"] = "Scriptures in yellow are related to construction and building.
\
+ .["selected"] = selected_scripture
+ .["scripturecolors"] = "Scriptures in yellow are related to construction and building.
\
Scriptures in red are related to attacking and offense.
\
Scriptures in blue are related to healing and defense.
\
Scriptures in purple are niche but still important!
\
Scriptures with italicized names are important to success."
generate_all_scripture()
-
- data["scripture"] = list()
- for(var/s in GLOB.all_scripture)
- var/datum/clockwork_scripture/S = GLOB.all_scripture[s]
- if(S.tier == selected_scripture) //display only scriptures of the selected tier
- var/scripture_color = get_component_color_bright(S.primary_component)
- var/list/temp_info = list("name" = "[S.name]",
- "descname" = "([S.descname])",
- "tip" = "[S.desc]\n[S.usage_tip]",
- "required" = "([DisplayPower(S.power_cost)][S.special_power_text ? "+ [replacetext(S.special_power_text, "POWERCOST", "[DisplayPower(S.special_power_cost)]")]" : ""])",
- "type" = "[S.type]",
- "quickbind" = S.quickbind)
- if(S.important)
- temp_info["name"] = "[temp_info["name"]]"
- var/found = quickbound.Find(S.type)
- if(found)
- temp_info["bound"] = "[found]"
- if(S.invokers_required > 1)
- temp_info["invokers"] = "Invokers: [S.invokers_required]"
- data["scripture"] += list(temp_info)
- data["recollection"] = recollecting
- if(recollecting)
- data["recollection_categories"] = GLOB.ratvar_awakens ? list() : list(\
- list("name" = "Getting Started", "desc" = "First-time servant? Read this first."), \
- list("name" = "Basics", "desc" = "A primer on how to play as a servant."), \
- list("name" = "Terminology", "desc" = "Common acronyms, words, and terms."), \
- list("name" = "Components", "desc" = "Information on components, your primary resource."), \
- list("name" = "Scripture", "desc" = "Information on scripture, ancient tools used by the cult."), \
- list("name" = "Power", "desc" = "The power system that certain objects use to function."), \
- list("name" = "Conversion", "desc" = "Converting the crew, cyborgs, and very walls to your cause."), \
- )
- data["rec_text"] = recollection()
- data["rec_section"] = GLOB.ratvar_awakens ? "" : get_recollection_text(recollection_category)
- data["rec_binds"] = GLOB.ratvar_awakens ? "" : get_recollection_quickbinds()
- return data
+ .["recollection_categories"] = GLOB.ratvar_awakens ? list() : list(
+ list("name" = "Getting Started", "desc" = "First-time servant? Read this first."),
+ list("name" = "Basics", "desc" = "A primer on how to play as a servant."),
+ list("name" = "Terminology", "desc" = "Common acronyms, words, and terms."),
+ list("name" = "Components", "desc" = "Information on components, your primary resource."),
+ list("name" = "Scripture", "desc" = "Information on scripture, ancient tools used by the cult."),
+ list("name" = "Power", "desc" = "The power system that certain objects use to function."),
+ list("name" = "Conversion", "desc" = "Converting the crew, cyborgs, and very walls to your cause.")
+ )
+ // .["rec_section"]["title"] //this is here if ever we decided to return these back.
+ // .["rec_section"]["info"]// wall of info for the thing
/obj/item/clockwork/slab/ui_act(action, params)
switch(action)
if("toggle")
recollecting = !recollecting
if("recite")
- INVOKE_ASYNC(src, .proc/recite_scripture, text2path(params["category"]), usr, FALSE)
- if("select")
- selected_scripture = params["category"]
+ INVOKE_ASYNC(src, .proc/recite_scripture, text2path(params["script"]), usr, FALSE)
if("bind")
- var/datum/clockwork_scripture/path = text2path(params["category"]) //we need a path and not a string
+ var/datum/clockwork_scripture/path = text2path(params["script"]) //we need a path and not a string
+ if(!ispath(path, /datum/clockwork_scripture) || !initial(path.quickbind) || initial(path.tier) == SCRIPTURE_PERIPHERAL) //fuck you href bus
+ to_chat(usr, "Nice try using href exploits")
+ return
var/found_index = quickbound.Find(path)
if(found_index) //hey, we already HAVE this bound
if(LAZYLEN(quickbound) == found_index) //if it's the last scripture, remove it instead of leaving a null
@@ -518,8 +339,8 @@
quickbind_to_slot(path, target_index)
if("rec_category")
recollection_category = params["category"]
- ui_interact(usr)
- return 1
+ update_static_data()
+ return TRUE
/obj/item/clockwork/slab/proc/quickbind_to_slot(datum/clockwork_scripture/scripture, index) //takes a typepath(typecast for initial()) and binds it to a slot
if(!ispath(scripture) || !scripture || (scripture in quickbound))
diff --git a/code/modules/antagonists/clockcult/clock_items/construct_chassis.dm b/code/modules/antagonists/clockcult/clock_items/construct_chassis.dm
index 43c05b8556..7578d41b15 100644
--- a/code/modules/antagonists/clockcult/clock_items/construct_chassis.dm
+++ b/code/modules/antagonists/clockcult/clock_items/construct_chassis.dm
@@ -32,7 +32,7 @@
clockwork_desc = initial(clockwork_desc)
//ATTACK HAND IGNORING PARENT RETURN VALUE
-/obj/item/clockwork/construct_chassis/attack_hand(mob/living/user)
+/obj/item/clockwork/construct_chassis/attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
if(w_class >= WEIGHT_CLASS_HUGE)
to_chat(user, "[src] is too cumbersome to carry! Drag it around instead!")
return
diff --git a/code/modules/antagonists/clockcult/clock_structures/clockwork_obelisk.dm b/code/modules/antagonists/clockcult/clock_structures/clockwork_obelisk.dm
index d9c3b047ca..3ca0e8692b 100644
--- a/code/modules/antagonists/clockcult/clock_structures/clockwork_obelisk.dm
+++ b/code/modules/antagonists/clockcult/clock_structures/clockwork_obelisk.dm
@@ -46,7 +46,7 @@
SG.ex_act(EXPLODE_DEVASTATE)
return ..()
-/obj/structure/destructible/clockwork/powered/clockwork_obelisk/attack_hand(mob/living/user)
+/obj/structure/destructible/clockwork/powered/clockwork_obelisk/attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/modules/antagonists/clockcult/clock_structures/eminence_spire.dm b/code/modules/antagonists/clockcult/clock_structures/eminence_spire.dm
index db49d75678..ec1bdddf21 100644
--- a/code/modules/antagonists/clockcult/clock_structures/eminence_spire.dm
+++ b/code/modules/antagonists/clockcult/clock_structures/eminence_spire.dm
@@ -11,7 +11,7 @@
var/selection_timer //Timer ID; this is canceled if the vote is canceled
var/kingmaking
-/obj/structure/destructible/clockwork/eminence_spire/attack_hand(mob/living/user)
+/obj/structure/destructible/clockwork/eminence_spire/attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/modules/antagonists/clockcult/clock_structures/heralds_beacon.dm b/code/modules/antagonists/clockcult/clock_structures/heralds_beacon.dm
index 65fe34bf8b..c8217584b5 100644
--- a/code/modules/antagonists/clockcult/clock_structures/heralds_beacon.dm
+++ b/code/modules/antagonists/clockcult/clock_structures/heralds_beacon.dm
@@ -60,7 +60,7 @@
. += "There are [time_remaining] second[time_remaining != 1 ? "s" : ""] remaining to vote."
. += "There are [voters.len]/[votes_needed] votes to activate the beacon!"
-/obj/structure/destructible/clockwork/heralds_beacon/attack_hand(mob/living/user)
+/obj/structure/destructible/clockwork/heralds_beacon/attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/modules/antagonists/clockcult/clock_structures/mania_motor.dm b/code/modules/antagonists/clockcult/clock_structures/mania_motor.dm
index 5fbaf9fd57..24d0651444 100644
--- a/code/modules/antagonists/clockcult/clock_structures/mania_motor.dm
+++ b/code/modules/antagonists/clockcult/clock_structures/mania_motor.dm
@@ -30,7 +30,7 @@
toggle()
return TRUE
-/obj/structure/destructible/clockwork/powered/mania_motor/attack_hand(mob/living/user)
+/obj/structure/destructible/clockwork/powered/mania_motor/attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/modules/antagonists/clockcult/clock_structures/trap_triggers/lever.dm b/code/modules/antagonists/clockcult/clock_structures/trap_triggers/lever.dm
index 12e4b62a65..d4a02cc3e1 100644
--- a/code/modules/antagonists/clockcult/clock_structures/trap_triggers/lever.dm
+++ b/code/modules/antagonists/clockcult/clock_structures/trap_triggers/lever.dm
@@ -6,7 +6,7 @@
max_integrity = 75
icon_state = "lever"
-/obj/structure/destructible/clockwork/trap/trigger/lever/attack_hand(mob/living/user)
+/obj/structure/destructible/clockwork/trap/trigger/lever/attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/modules/antagonists/clockcult/clock_structures/trap_triggers/repeater.dm b/code/modules/antagonists/clockcult/clock_structures/trap_triggers/repeater.dm
index f5ed91ac15..e7d4e18c43 100644
--- a/code/modules/antagonists/clockcult/clock_structures/trap_triggers/repeater.dm
+++ b/code/modules/antagonists/clockcult/clock_structures/trap_triggers/repeater.dm
@@ -6,7 +6,7 @@
max_integrity = 15 //Fragile!
icon_state = "repeater"
-/obj/structure/destructible/clockwork/trap/trigger/repeater/attack_hand(mob/living/user)
+/obj/structure/destructible/clockwork/trap/trigger/repeater/attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/modules/antagonists/cult/blood_magic.dm b/code/modules/antagonists/cult/blood_magic.dm
index 52ead9a1e7..68a890028e 100644
--- a/code/modules/antagonists/cult/blood_magic.dm
+++ b/code/modules/antagonists/cult/blood_magic.dm
@@ -801,7 +801,7 @@
var/turf/T = get_turf(user)
qdel(src)
var/datum/action/innate/cult/spear/S = new(user)
- var/obj/item/twohanded/cult_spear/rite = new(T)
+ var/obj/item/cult_spear/rite = new(T)
S.Grant(user, rite)
rite.spear_act = S
if(user.put_in_hands(rite))
diff --git a/code/modules/antagonists/cult/cult_items.dm b/code/modules/antagonists/cult/cult_items.dm
index 8f0f9a658c..5b2dd7d007 100644
--- a/code/modules/antagonists/cult/cult_items.dm
+++ b/code/modules/antagonists/cult/cult_items.dm
@@ -100,7 +100,7 @@
user.apply_damage(30, BRUTE, pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
user.dropItemToGround(src)
-/obj/item/twohanded/required/cult_bastard
+/obj/item/cult_bastard
name = "bloody bastard sword"
desc = "An enormous sword used by Nar'Sien cultists to rapidly harvest the souls of non-believers."
w_class = WEIGHT_CLASS_HUGE
@@ -127,31 +127,35 @@
var/spin_cooldown = 250
var/dash_toggled = TRUE
-/obj/item/twohanded/required/cult_bastard/Initialize()
+/obj/item/cult_bastard/Initialize()
. = ..()
set_light(4)
jaunt = new(src)
linked_action = new(src)
- AddComponent(/datum/component/butchering, 50, 80)
-/obj/item/twohanded/required/cult_bastard/examine(mob/user)
+/obj/item/cult_bastard/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/butchering, 50, 80)
+ AddComponent(/datum/component/two_handed, require_twohands=TRUE)
+
+/obj/item/cult_bastard/examine(mob/user)
. = ..()
if(contents.len)
. += "
There are [contents.len] souls trapped within the sword's core."
else
. += "
The sword appears to be quite lifeless."
-/obj/item/twohanded/required/cult_bastard/can_be_pulled(user)
+/obj/item/cult_bastard/can_be_pulled(user)
return FALSE
-/obj/item/twohanded/required/cult_bastard/attack_self(mob/user)
+/obj/item/cult_bastard/attack_self(mob/user)
dash_toggled = !dash_toggled
if(dash_toggled)
to_chat(loc, "You raise [src] and prepare to jaunt with it.")
else
to_chat(loc, "You lower [src] and prepare to swing it normally.")
-/obj/item/twohanded/required/cult_bastard/pickup(mob/living/user)
+/obj/item/cult_bastard/pickup(mob/living/user)
. = ..()
if(!iscultist(user))
if(!is_servant_of_ratvar(user))
@@ -171,13 +175,13 @@
linked_action.Grant(user, src)
user.update_icons()
-/obj/item/twohanded/required/cult_bastard/dropped(mob/user)
+/obj/item/cult_bastard/dropped(mob/user)
. = ..()
linked_action.Remove(user)
jaunt.Remove(user)
user.update_icons()
-/obj/item/twohanded/required/cult_bastard/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
+/obj/item/cult_bastard/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
if(spinning && is_energy_reflectable_projectile(object) && (attack_type & ATTACK_TYPE_PROJECTILE))
playsound(src, pick('sound/weapons/effects/ric1.ogg', 'sound/weapons/effects/ric2.ogg', 'sound/weapons/effects/ric3.ogg', 'sound/weapons/effects/ric4.ogg', 'sound/weapons/effects/ric5.ogg'), 100, 1)
return BLOCK_SUCCESS | BLOCK_PHYSICAL_EXTERNAL | BLOCK_REDIRECTED | BLOCK_SHOULD_REDIRECT
@@ -192,7 +196,7 @@
return BLOCK_SUCCESS | BLOCK_PHYSICAL_EXTERNAL
return BLOCK_NONE
-/obj/item/twohanded/required/cult_bastard/afterattack(atom/target, mob/user, proximity, click_parameters)
+/obj/item/cult_bastard/afterattack(atom/target, mob/user, proximity, click_parameters)
. = ..()
if(dash_toggled && !proximity)
jaunt.Teleport(user, target)
@@ -235,7 +239,7 @@
button_icon_state = "sintouch"
var/cooldown = 0
var/mob/living/carbon/human/holder
- var/obj/item/twohanded/required/cult_bastard/sword
+ var/obj/item/cult_bastard/sword
/datum/action/innate/cult/spin2win/Grant(mob/user, obj/bastard)
. = ..()
@@ -687,7 +691,7 @@
to_chat(user, "\The [src] can only transport items!")
-/obj/item/twohanded/cult_spear
+/obj/item/cult_spear
name = "blood halberd"
desc = "A sickening spear composed entirely of crystallized blood."
icon_state = "bloodspear0"
@@ -695,8 +699,6 @@
righthand_file = 'icons/mob/inhands/weapons/polearms_righthand.dmi'
slot_flags = 0
force = 17
- force_unwielded = 17
- force_wielded = 24
throwforce = 40
throw_speed = 2
armour_penetration = 30
@@ -705,20 +707,36 @@
sharpness = IS_SHARP
hitsound = 'sound/weapons/bladeslice.ogg'
var/datum/action/innate/cult/spear/spear_act
+ var/wielded = FALSE // track wielded status on item
-/obj/item/twohanded/cult_spear/Initialize()
+
+/obj/item/cult_spear/Initialize()
+ . = ..()
+ RegisterSignal(src, COMSIG_TWOHANDED_WIELD, .proc/on_wield)
+ RegisterSignal(src, COMSIG_TWOHANDED_UNWIELD, .proc/on_unwield)
+
+/obj/item/cult_spear/ComponentInitialize()
. = ..()
AddComponent(/datum/component/butchering, 100, 90)
+ AddComponent(/datum/component/two_handed, force_unwielded=17, force_wielded=24, icon_wielded="bloodspear1")
-/obj/item/twohanded/cult_spear/Destroy()
+/// triggered on wield of two handed item
+/obj/item/cult_spear/proc/on_wield(obj/item/source, mob/user)
+ wielded = TRUE
+
+/// triggered on unwield of two handed item
+/obj/item/cult_spear/proc/on_unwield(obj/item/source, mob/user)
+ wielded = FALSE
+
+/obj/item/cult_spear/update_icon_state()
+ icon_state = "bloodspear0"
+
+/obj/item/cult_spear/Destroy()
if(spear_act)
qdel(spear_act)
..()
-/obj/item/twohanded/cult_spear/update_icon_state()
- icon_state = "bloodspear[wielded]"
-
-/obj/item/twohanded/cult_spear/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
+/obj/item/cult_spear/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
var/turf/T = get_turf(hit_atom)
if(isliving(hit_atom))
var/mob/living/L = hit_atom
@@ -741,7 +759,7 @@
else
..()
-/obj/item/twohanded/cult_spear/proc/break_spear(turf/T)
+/obj/item/cult_spear/proc/break_spear(turf/T)
if(src)
if(!T)
T = get_turf(src)
@@ -752,7 +770,7 @@
playsound(T, 'sound/effects/glassbr3.ogg', 100)
qdel(src)
-/obj/item/twohanded/cult_spear/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
+/obj/item/cult_spear/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
if(wielded)
final_block_chance *= 2
if(prob(final_block_chance))
@@ -771,7 +789,7 @@
desc = "Call the blood spear back to your hand!"
background_icon_state = "bg_demon"
button_icon_state = "bloodspear"
- var/obj/item/twohanded/cult_spear/spear
+ var/obj/item/cult_spear/spear
var/cooldown = 0
/datum/action/innate/cult/spear/Grant(mob/user, obj/blood_spear)
diff --git a/code/modules/antagonists/cult/runes.dm b/code/modules/antagonists/cult/runes.dm
index b0538d6521..9b77e70a73 100644
--- a/code/modules/antagonists/cult/runes.dm
+++ b/code/modules/antagonists/cult/runes.dm
@@ -67,7 +67,7 @@ Runes can either be invoked by one's self or with many different cultists. Each
to_chat(user, "You disrupt the magic of [src] with [I].")
qdel(src)
-/obj/effect/rune/attack_hand(mob/living/user)
+/obj/effect/rune/attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
diff --git a/code/modules/antagonists/devil/devil.dm b/code/modules/antagonists/devil/devil.dm
index c12259778e..3b6dc68986 100644
--- a/code/modules/antagonists/devil/devil.dm
+++ b/code/modules/antagonists/devil/devil.dm
@@ -92,6 +92,7 @@ GLOBAL_LIST_INIT(devil_suffix, list(" the Red", " the Soulless", " the Master",
//Don't delete upon mind destruction, otherwise soul re-selling will break.
delete_on_mind_deletion = FALSE
threat = 5
+ show_to_ghosts = TRUE
var/obligation
var/ban
var/bane
diff --git a/code/modules/antagonists/disease/disease_datum.dm b/code/modules/antagonists/disease/disease_datum.dm
index 7de0330ad6..b8e906064c 100644
--- a/code/modules/antagonists/disease/disease_datum.dm
+++ b/code/modules/antagonists/disease/disease_datum.dm
@@ -2,6 +2,7 @@
name = "Sentient Disease"
roundend_category = "diseases"
antagpanel_category = "Disease"
+ show_to_ghosts = TRUE
var/disease_name = ""
/datum/antagonist/disease/on_gain()
diff --git a/code/modules/antagonists/ert/ert.dm b/code/modules/antagonists/ert/ert.dm
index 1d773627c7..295616d052 100644
--- a/code/modules/antagonists/ert/ert.dm
+++ b/code/modules/antagonists/ert/ert.dm
@@ -12,6 +12,7 @@
var/list/name_source
threat = -5
show_in_antagpanel = FALSE
+ show_to_ghosts = TRUE
antag_moodlet = /datum/mood_event/focused
/datum/antagonist/ert/on_gain()
diff --git a/code/modules/antagonists/monkey/monkey.dm b/code/modules/antagonists/monkey/monkey.dm
index ebb39c814e..971532958f 100644
--- a/code/modules/antagonists/monkey/monkey.dm
+++ b/code/modules/antagonists/monkey/monkey.dm
@@ -9,6 +9,7 @@
roundend_category = "monkeys"
antagpanel_category = "Monkey"
threat = 3
+ show_to_ghosts = TRUE
var/datum/team/monkey/monkey_team
var/monkey_only = TRUE
diff --git a/code/modules/antagonists/nightmare/nightmare.dm b/code/modules/antagonists/nightmare/nightmare.dm
index 837b6e4216..f5b10de5c2 100644
--- a/code/modules/antagonists/nightmare/nightmare.dm
+++ b/code/modules/antagonists/nightmare/nightmare.dm
@@ -3,3 +3,4 @@
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
threat = 5
+ show_to_ghosts = TRUE
diff --git a/code/modules/antagonists/ninja/ninja.dm b/code/modules/antagonists/ninja/ninja.dm
index 2615822dd8..414f7dd6b0 100644
--- a/code/modules/antagonists/ninja/ninja.dm
+++ b/code/modules/antagonists/ninja/ninja.dm
@@ -3,6 +3,7 @@
antagpanel_category = "Ninja"
job_rank = ROLE_NINJA
show_name_in_check_antagonists = TRUE
+ show_to_ghosts = TRUE
antag_moodlet = /datum/mood_event/focused
threat = 8
var/helping_station = FALSE
diff --git a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
index a11ecaa3df..5c011b6318 100644
--- a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
+++ b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
@@ -7,6 +7,9 @@
density = TRUE
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
+ ui_x = 350
+ ui_y = 442
+
var/timer_set = 90
var/default_timer_set = 90
var/minimum_timer_set = 90
@@ -262,8 +265,7 @@
/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, 350, 442, master_ui, state)
- ui.set_style(ui_style)
+ ui = new(user, src, ui_key, "NuclearBomb", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/nuclearbomb/ui_data(mob/user)
diff --git a/code/modules/antagonists/nukeop/nukeop.dm b/code/modules/antagonists/nukeop/nukeop.dm
index 454cde6d72..652b19a8e7 100644
--- a/code/modules/antagonists/nukeop/nukeop.dm
+++ b/code/modules/antagonists/nukeop/nukeop.dm
@@ -6,6 +6,7 @@
antag_moodlet = /datum/mood_event/focused
threat = 10
skill_modifiers = list(/datum/skill_modifier/job/level/wiring)
+ show_to_ghosts = TRUE
var/datum/team/nuclear/nuke_team
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.
diff --git a/code/modules/antagonists/official/official.dm b/code/modules/antagonists/official/official.dm
index 1d340253c4..1ec64cb2b6 100644
--- a/code/modules/antagonists/official/official.dm
+++ b/code/modules/antagonists/official/official.dm
@@ -4,6 +4,7 @@
show_in_antagpanel = FALSE
var/datum/objective/mission
var/datum/team/ert/ert_team
+ show_to_ghosts = TRUE
/datum/antagonist/official/greet()
to_chat(owner, "You are a CentCom Official.")
diff --git a/code/modules/antagonists/pirate/pirate.dm b/code/modules/antagonists/pirate/pirate.dm
index 01f3c6068e..e6d350064d 100644
--- a/code/modules/antagonists/pirate/pirate.dm
+++ b/code/modules/antagonists/pirate/pirate.dm
@@ -4,6 +4,7 @@
roundend_category = "space pirates"
antagpanel_category = "Pirate"
threat = 5
+ show_to_ghosts = TRUE
var/datum/team/pirate/crew
/datum/antagonist/pirate/greet()
diff --git a/code/modules/antagonists/revenant/revenant.dm b/code/modules/antagonists/revenant/revenant.dm
index 3ddb4e67ee..d0ef5a83ce 100644
--- a/code/modules/antagonists/revenant/revenant.dm
+++ b/code/modules/antagonists/revenant/revenant.dm
@@ -107,9 +107,8 @@
mind.add_antag_datum(/datum/antagonist/revenant)
//Life, Stat, Hud Updates, and Say
-/mob/living/simple_animal/revenant/BiologicalLife(seconds, times_fired)
- if(!(. = ..()))
- return
+/mob/living/simple_animal/revenant/Life(seconds, times_fired)
+ . = ..()
if(stasis)
return
if(revealed && essence <= 0)
diff --git a/code/modules/antagonists/revenant/revenant_antag.dm b/code/modules/antagonists/revenant/revenant_antag.dm
index 46c1176533..c93291797a 100644
--- a/code/modules/antagonists/revenant/revenant_antag.dm
+++ b/code/modules/antagonists/revenant/revenant_antag.dm
@@ -3,6 +3,7 @@
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
threat = 5
+ show_to_ghosts = TRUE
/datum/antagonist/revenant/greet()
owner.announce_objectives()
diff --git a/code/modules/antagonists/santa/santa.dm b/code/modules/antagonists/santa/santa.dm
index f58a21ba42..ff7dae98f6 100644
--- a/code/modules/antagonists/santa/santa.dm
+++ b/code/modules/antagonists/santa/santa.dm
@@ -1,6 +1,8 @@
/datum/antagonist/santa
name = "Santa"
show_in_antagpanel = FALSE
+ show_name_in_check_antagonists = TRUE
+ show_to_ghosts = TRUE
/datum/antagonist/santa/on_gain()
. = ..()
diff --git a/code/modules/antagonists/slaughter/slaughter_antag.dm b/code/modules/antagonists/slaughter/slaughter_antag.dm
index 04f7167fa5..87db9772b7 100644
--- a/code/modules/antagonists/slaughter/slaughter_antag.dm
+++ b/code/modules/antagonists/slaughter/slaughter_antag.dm
@@ -6,6 +6,7 @@
threat = 10
job_rank = ROLE_ALIEN
show_in_antagpanel = FALSE
+ show_to_ghosts = TRUE
/datum/antagonist/slaughter/on_gain()
forge_objectives()
diff --git a/code/modules/antagonists/survivalist/survivalist.dm b/code/modules/antagonists/survivalist/survivalist.dm
index 04ad53f65b..296369fe3b 100644
--- a/code/modules/antagonists/survivalist/survivalist.dm
+++ b/code/modules/antagonists/survivalist/survivalist.dm
@@ -23,6 +23,18 @@
/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, and don't let anyone take them!"
+/datum/antagonist/survivalist/guns/forge_objectives()
+ var/datum/objective/steal_five_of_type/summon_guns/guns = new
+ guns.owner = owner
+ objectives += guns
+ ..()
+
/datum/antagonist/survivalist/magic
name = "Amateur Magician"
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!"
+
+/datum/antagonist/survivalist/magic/forge_objectives()
+ var/datum/objective/steal_five_of_type/summon_magic/magic = new
+ magic.owner = owner
+ objectives += magic
+ ..()
diff --git a/code/modules/antagonists/swarmer/swarmer.dm b/code/modules/antagonists/swarmer/swarmer.dm
index d0e36394ab..9a8b9c8d59 100644
--- a/code/modules/antagonists/swarmer/swarmer.dm
+++ b/code/modules/antagonists/swarmer/swarmer.dm
@@ -36,7 +36,7 @@
if(A)
notify_ghosts("A swarmer shell has been created in [A.name].", 'sound/effects/bin_close.ogg', source = src, action = NOTIFY_ATTACK, flashwindow = FALSE, ignore_dnr_observers = TRUE)
-/obj/effect/mob_spawn/swarmer/attack_hand(mob/living/user)
+/obj/effect/mob_spawn/swarmer/attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
@@ -399,13 +399,13 @@
return FALSE
/obj/structure/lattice/catwalk/swarmer_act(mob/living/simple_animal/hostile/swarmer/S)
- . = ..()
var/turf/here = get_turf(src)
for(var/A in here.contents)
var/obj/structure/cable/C = A
if(istype(C))
to_chat(S, "Disrupting the power grid would bring no benefit to us. Aborting.")
return FALSE
+ return ..()
/obj/item/deactivated_swarmer/IntegrateAmount()
return 50
@@ -486,7 +486,7 @@
var/obj/O = target
if(O.resistance_flags & INDESTRUCTIBLE)
return FALSE
- for(var/mob/living/L in GetAllContents())
+ for(var/mob/living/L in target.GetAllContents())
if(!ispAI(L) && !isbrain(L))
to_chat(src, "An organism has been detected inside this object. Aborting.")
return FALSE
diff --git a/code/modules/antagonists/traitor/equipment/Malf_Modules.dm b/code/modules/antagonists/traitor/equipment/Malf_Modules.dm
index 6616eea006..8e9a54a69a 100644
--- a/code/modules/antagonists/traitor/equipment/Malf_Modules.dm
+++ b/code/modules/antagonists/traitor/equipment/Malf_Modules.dm
@@ -257,6 +257,8 @@ GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list(
return
if (active)
return //prevent the AI from activating an already active doomsday
+ if (owner_AI.shunted)
+ return //prevent AI from activating doomsday while shunted.
active = TRUE
set_us_up_the_bomb(owner)
diff --git a/code/modules/antagonists/traitor/equipment/contractor.dm b/code/modules/antagonists/traitor/equipment/contractor.dm
index 6c5d5766e4..94a3059b5f 100644
--- a/code/modules/antagonists/traitor/equipment/contractor.dm
+++ b/code/modules/antagonists/traitor/equipment/contractor.dm
@@ -1,4 +1,4 @@
-// Support unit gets it's own very basic antag datum for admin logging.
+/// Support unit gets it's own very basic antag datum for admin logging.
/datum/antagonist/traitor/contractor_support
name = "Contractor Support Unit"
antag_moodlet = /datum/mood_event/focused
@@ -8,11 +8,13 @@
should_equip = FALSE /// Don't give them an uplink.
var/datum/team/contractor_team/contractor_team
-/datum/team/contractor_team // Team for storing both the contractor and their support unit - only really for the HUD and admin logging.
+/// Team for storing both the contractor and their support unit - only really for the HUD and admin logging.
+/datum/team/contractor_team
show_roundend_report = FALSE
/datum/antagonist/traitor/contractor_support/forge_traitor_objectives()
var/datum/objective/generic_objective = new
+
generic_objective.name = "Follow Contractor's Orders"
generic_objective.explanation_text = "Follow your orders. Assist agents in this mission area."
generic_objective.completed = TRUE
@@ -25,7 +27,9 @@
var/static/list/contractor_items = typecacheof(/datum/contractor_item/, TRUE)
var/datum/syndicate_contract/current_contract
var/list/datum/syndicate_contract/assigned_contracts = list()
+
var/list/assigned_targets = list() // used as a blacklist to make sure we're not assigning targets already assigned
+ var/contracts_completed = 0
var/contract_TC_payed_out = 0 // Keeping track for roundend reporting
var/contract_TC_to_redeem = 0 // Used internally and roundend reporting - what TC we have available to cashout.
@@ -34,7 +38,8 @@
var/datum/contractor_item/contractor_item = new path
hub_items.Add(contractor_item)
-/datum/contractor_hub/proc/create_contracts(datum/mind/owner) // 6 initial contracts
+/datum/contractor_hub/proc/create_contracts(datum/mind/owner)
+ // 6 initial contracts
var/list/to_generate = list(
CONTRACT_PAYOUT_LARGE,
CONTRACT_PAYOUT_MEDIUM,
@@ -44,61 +49,74 @@
CONTRACT_PAYOUT_SMALL
)
- var/lowest_TC_threshold = 30 // We don't want the sum of all the payouts to be under this amount
+ //What the fuck
+ if(length(to_generate) > length(GLOB.data_core.locked))
+ to_generate.Cut(1, length(GLOB.data_core.locked))
+ // We don't want the sum of all the payouts to be under this amount
+ var/lowest_TC_threshold = 30
+
var/total = 0
var/lowest_paying_sum = 0
var/datum/syndicate_contract/lowest_paying_contract
- to_generate = shuffle(to_generate) // Randomise order, so we don't have contracts always in payout order.
- var/start_index = 1 // Support contract generation happening multiple times
- if(assigned_contracts.len != 0)
+ // Randomise order, so we don't have contracts always in payout order.
+ to_generate = shuffle(to_generate)
+ // Support contract generation happening multiple times
+ var/start_index = 1
+ if (assigned_contracts.len != 0)
start_index = assigned_contracts.len + 1
- for(var/i = 1; i <= to_generate.len; i++) // Generate contracts, and find the lowest paying.
+ // Generate contracts, and find the lowest paying.
+ for (var/i = 1; i <= to_generate.len; i++)
var/datum/syndicate_contract/contract_to_add = new(owner, assigned_targets, to_generate[i])
var/contract_payout_total = contract_to_add.contract.payout + contract_to_add.contract.payout_bonus
assigned_targets.Add(contract_to_add.contract.target)
- if(!lowest_paying_contract || (contract_payout_total < lowest_paying_sum))
+ if (!lowest_paying_contract || (contract_payout_total < lowest_paying_sum))
lowest_paying_sum = contract_payout_total
lowest_paying_contract = contract_to_add
total += contract_payout_total
contract_to_add.id = start_index
assigned_contracts.Add(contract_to_add)
start_index++
- if(total < lowest_TC_threshold) // If the threshold for TC payouts isn't reached, boost the lowest paying contract
+
+ // If the threshold for TC payouts isn't reached, boost the lowest paying contract
+ if (total < lowest_TC_threshold)
lowest_paying_contract.contract.payout_bonus += (lowest_TC_threshold - total)
/datum/contractor_item
var/name // Name of item
var/desc // description of item
var/item // item path, no item path means the purchase needs it's own handle_purchase()
- var/item_icon = "fa-broadcast-tower" // fontawesome icon to use inside the hub - https://fontawesome.com/icons/
+ var/item_icon = "broadcast-tower" // fontawesome icon to use inside the hub - https://fontawesome.com/icons/
var/limited = -1 // Any number above 0 for how many times it can be bought in a round for a single traitor. -1 is unlimited.
var/cost // Cost of the item in contract rep.
/datum/contractor_item/contract_reroll
name = "Contract Reroll"
desc = "Request a reroll of your current contract list. Will generate a new target, payment, and dropoff for the contracts you currently have available."
- item_icon = "fa-dice"
+ item_icon = "dice"
limited = 2
cost = 0
/datum/contractor_item/contract_reroll/handle_purchase(var/datum/contractor_hub/hub)
. = ..()
if (.)
- var/list/new_target_list = list() // We're not regenerating already completed/aborted/extracting contracts, but we don't want to repeat their targets.
+ /// We're not regenerating already completed/aborted/extracting contracts, but we don't want to repeat their targets.
+ var/list/new_target_list = list()
for(var/datum/syndicate_contract/contract_check in hub.assigned_contracts)
if (contract_check.status != CONTRACT_STATUS_ACTIVE && contract_check.status != CONTRACT_STATUS_INACTIVE)
if (contract_check.contract.target)
new_target_list.Add(contract_check.contract.target)
continue
- for(var/datum/syndicate_contract/rerolling_contract in hub.assigned_contracts) // Reroll contracts without duplicates
+ /// Reroll contracts without duplicates
+ for(var/datum/syndicate_contract/rerolling_contract in hub.assigned_contracts)
if (rerolling_contract.status != CONTRACT_STATUS_ACTIVE && rerolling_contract.status != CONTRACT_STATUS_INACTIVE)
continue
rerolling_contract.generate(new_target_list)
new_target_list.Add(rerolling_contract.contract.target)
- hub.assigned_targets = new_target_list // Set our target list with the new set we've generated.
+ /// Set our target list with the new set we've generated.
+ hub.assigned_targets = new_target_list
/datum/contractor_item/contractor_pinpointer
name = "Contractor Pinpointer"
desc = "A pinpointer that finds targets even without active suit sensors. Due to taking advantage of an exploit within the system, it can't pinpoint to the same accuracy as the traditional models. Becomes permanently locked to the user that first activates it."
@@ -125,20 +143,25 @@
/datum/contractor_item/contractor_partner/handle_purchase(var/datum/contractor_hub/hub, mob/living/user)
. = ..()
+
if (.)
to_chat(user, "The uplink vibrates quietly, connecting to nearby agents...")
- var/list/mob/candidates = pollGhostCandidates("Do you want to play as the Contractor Support Unit for [user.real_name]?", ROLE_PAI, null, FALSE, 100, POLL_IGNORE_CONTRACTOR_SUPPORT)
+
+ var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the Contractor Support Unit for [user.real_name]?", ROLE_PAI, null, FALSE, 100, POLL_IGNORE_CONTRACTOR_SUPPORT)
+
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
spawn_contractor_partner(user, C.key)
else
to_chat(user, "No available agents at this time, please try again later.")
- limited += 1 // refund and add the limit back.
+ // refund and add the limit back.
+ limited += 1
hub.contract_rep += cost
hub.purchased_items -= src
/datum/outfit/contractor_partner
name = "Contractor Support Unit"
+
uniform = /obj/item/clothing/under/chameleon
suit = /obj/item/clothing/suit/chameleon
back = /obj/item/storage/backpack
@@ -148,28 +171,35 @@
ears = /obj/item/radio/headset/chameleon
id = /obj/item/card/id/syndicate
r_hand = /obj/item/storage/toolbox/syndicate
+
backpack_contents = list(/obj/item/storage/box/survival, /obj/item/implanter/uplink, /obj/item/clothing/mask/chameleon,
/obj/item/storage/fancy/cigarettes/cigpack_syndicate, /obj/item/lighter)
/datum/outfit/contractor_partner/post_equip(mob/living/carbon/human/H, visualsOnly)
. = ..()
- var/obj/item/clothing/mask/cigarette/syndicate/cig = H.get_item_by_slot(SLOT_WEAR_MASK)
- cig.light() // pre-light their cig for extra badass
+ var/obj/item/clothing/mask/cigarette/syndicate/cig = H.get_item_by_slot(ITEM_SLOT_MASK)
+ // pre-light their cig
+ cig.light()
/datum/contractor_item/contractor_partner/proc/spawn_contractor_partner(mob/living/user, key)
var/mob/living/carbon/human/partner = new()
var/datum/outfit/contractor_partner/partner_outfit = new()
+
partner_outfit.equip(partner)
+
var/obj/structure/closet/supplypod/arrival_pod = new()
+
arrival_pod.style = STYLE_SYNDICATE
arrival_pod.explosionSize = list(0,0,0,1)
arrival_pod.bluespace = TRUE
var/turf/free_location = find_obstruction_free_location(2, user)
- if (!free_location) // We really want to send them - if we can't find a nice location just land it on top of them.
+ // We really want to send them - if we can't find a nice location just land it on top of them.
+ if (!free_location)
free_location = get_turf(user)
partner.forceMove(arrival_pod)
partner.ckey = key
- partner_mind = partner.mind // We give a reference to the mind that'll be the support unit
+ /// We give a reference to the mind that'll be the support unit
+ partner_mind = partner.mind
partner_mind.make_Contractor_Support()
to_chat(partner_mind.current, "\n[user.real_name] is your superior. Follow any, and all orders given by them. You're here to support their mission only.")
to_chat(partner_mind.current, "Should they perish, or be otherwise unavailable, you're to assist other active agents in this mission area to the best of your ability.\n\n")
@@ -186,7 +216,7 @@
. = ..()
if (.)
power_fail(35, 50)
- priority_announce("Abnormal activity detected in [station_name()]'s powernet. As a precautionary measure, the station's power will be shut off for an indeterminate duration.", "Critical Power Failure", "poweroff")
+ priority_announce("Abnormal activity detected in [station_name()]'s powernet. As a precautionary measure, the station's power will be shut off for an indeterminate duration.", "Critical Power Failure", "poweroff.ogg")
// Subtract cost, and spawn if it's an item.
/datum/contractor_item/proc/handle_purchase(var/datum/contractor_hub/hub, mob/living/user)
@@ -199,6 +229,7 @@
else if (limited == 0)
return FALSE
hub.purchased_items.Add(src)
+ user.playsound_local(user, 'sound/machines/uplinkpurchase.ogg', 100)
if (item && ispath(item))
var/atom/item_to_create = new item(get_turf(user))
diff --git a/code/modules/antagonists/traitor/syndicate_contract.dm b/code/modules/antagonists/traitor/syndicate_contract.dm
index 70ff59eee0..0f67616a32 100644
--- a/code/modules/antagonists/traitor/syndicate_contract.dm
+++ b/code/modules/antagonists/traitor/syndicate_contract.dm
@@ -4,47 +4,70 @@
var/datum/objective/contract/contract = new()
var/target_rank
var/ransom = 0
- var/payout_type = null
+ var/payout_type
+ var/wanted_message
+
var/list/victim_belongings = list()
/datum/syndicate_contract/New(contract_owner, blacklist, type=CONTRACT_PAYOUT_SMALL)
contract.owner = contract_owner
payout_type = type
+
generate(blacklist)
/datum/syndicate_contract/proc/generate(blacklist)
contract.find_target(null, blacklist)
- var/datum/data/record/record = find_record("name", contract.target.name, GLOB.data_core.general)
- if(record)
+
+ var/datum/data/record/record
+ if (contract.target)
+ record = find_record("name", contract.target.name, GLOB.data_core.general)
+
+ if (record)
target_rank = record.fields["rank"]
else
target_rank = "Unknown"
+
if (payout_type == CONTRACT_PAYOUT_LARGE)
contract.payout_bonus = rand(9,13)
- else if(payout_type == CONTRACT_PAYOUT_MEDIUM)
+ else if (payout_type == CONTRACT_PAYOUT_MEDIUM)
contract.payout_bonus = rand(6,8)
else
contract.payout_bonus = rand(2,4)
+
contract.payout = rand(0, 2)
contract.generate_dropoff()
+
ransom = 100 * rand(18, 45)
+ var/base = pick_list(WANTED_FILE, "basemessage")
+ var/verb_string = pick_list(WANTED_FILE, "verb")
+ var/noun = pick_list_weighted(WANTED_FILE, "noun")
+ var/location = pick_list_weighted(WANTED_FILE, "location")
+ wanted_message = "[base] [verb_string] [noun] [location]."
+
/datum/syndicate_contract/proc/handle_extraction(var/mob/living/user)
if (contract.target && contract.dropoff_check(user, contract.target.current))
+
var/turf/free_location = find_obstruction_free_location(3, user, contract.dropoff)
- if(free_location) // We've got a valid location, launch.
+
+ if (free_location)
+ // We've got a valid location, launch.
launch_extraction_pod(free_location)
return TRUE
+
return FALSE
// Launch the pod to collect our victim.
/datum/syndicate_contract/proc/launch_extraction_pod(turf/empty_pod_turf)
var/obj/structure/closet/supplypod/extractionpod/empty_pod = new()
+
RegisterSignal(empty_pod, COMSIG_ATOM_ENTERED, .proc/enter_check)
+
empty_pod.stay_after_drop = TRUE
empty_pod.reversing = TRUE
empty_pod.explosionSize = list(0,0,0,1)
empty_pod.leavingSound = 'sound/effects/podwoosh.ogg'
+
new /obj/effect/abstract/DPtarget(empty_pod_turf, empty_pod)
/datum/syndicate_contract/proc/enter_check(datum/source, sent_mob)
@@ -52,37 +75,55 @@
if(isliving(sent_mob))
var/mob/living/M = sent_mob
var/datum/antagonist/traitor/traitor_data = contract.owner.has_antag_datum(/datum/antagonist/traitor)
+
if(M == contract.target.current)
traitor_data.contractor_hub.contract_TC_to_redeem += contract.payout
+ traitor_data.contractor_hub.contracts_completed += 1
+
if(M.stat != DEAD)
traitor_data.contractor_hub.contract_TC_to_redeem += contract.payout_bonus
+
status = CONTRACT_STATUS_COMPLETE
+
if(traitor_data.contractor_hub.current_contract == src)
traitor_data.contractor_hub.current_contract = null
+
traitor_data.contractor_hub.contract_rep += 2
else
status = CONTRACT_STATUS_ABORTED // Sending a target that wasn't even yours is as good as just aborting it
+
if(traitor_data.contractor_hub.current_contract == src)
traitor_data.contractor_hub.current_contract = null
+
if(iscarbon(M))
for(var/obj/item/W in M)
if(ishuman(M))
var/mob/living/carbon/human/H = M
- if(W == H.w_uniform || W == H.shoes)
- continue //So all they're left with are shoes and uniform.
+ if(W == H.w_uniform)
+ continue //So all they're left with are shoes and uniform.
+ if(W == H.shoes)
+ continue
+
+
M.transferItemToLoc(W)
victim_belongings.Add(W)
+
var/obj/structure/closet/supplypod/extractionpod/pod = source
- pod.send_up(pod) // Handle the pod returning
+
+ // Handle the pod returning
+ pod.send_up(pod)
+
if(ishuman(M))
- var/mob/living/carbon/human/target = M // After we remove items, at least give them what they need to live.
+ var/mob/living/carbon/human/target = M
+
+ // After we remove items, at least give them what they need to live.
target.dna.species.give_important_for_life(target)
handleVictimExperience(M) // After pod is sent we start the victim narrative/heal.
var/datum/bank_account/D = SSeconomy.get_dep_account(ACCOUNT_CAR)
var/points_to_check = min(D.account_balance, ransom)
D.adjust_money(min(points_to_check, ransom))
priority_announce("One of your crew was captured by a rival organisation - we've needed to pay their ransom to bring them back. \
- As is policy we've taken a portion of the station's funds to offset the overall cost.", null, "attention", null, "Nanotrasen Asset Protection")
+ As is policy we've taken a portion of the station's funds to offset the overall cost.", null, "attention", null, "Nanotrasen Asset Protection")
sleep(30)
@@ -128,13 +169,18 @@
M.Dizzy(15)
M.confused += 20
-/datum/syndicate_contract/proc/returnVictim(var/mob/living/M) // We're returning the victim
+// We're returning the victim
+/datum/syndicate_contract/proc/returnVictim(var/mob/living/M)
var/list/possible_drop_loc = list()
+
for(var/turf/possible_drop in contract.dropoff.contents)
- if(!is_blocked_turf(possible_drop))
- possible_drop_loc.Add(possible_drop)
+ if(!isspaceturf(possible_drop) && !isclosedturf(possible_drop))
+ if(!is_blocked_turf(possible_drop))
+ possible_drop_loc.Add(possible_drop)
+
if(possible_drop_loc.len > 0)
var/pod_rand_loc = rand(1, possible_drop_loc.len)
+
var/obj/structure/closet/supplypod/return_pod = new()
return_pod.bluespace = TRUE
return_pod.explosionSize = list(0,0,0,0)
@@ -144,8 +190,10 @@
for(var/obj/item/W in M)
if(ishuman(M))
var/mob/living/carbon/human/H = M
- if(W == H.w_uniform || W == H.shoes)
+ if(W == H.w_uniform)
continue //So all they're left with are shoes and uniform.
+ if(W == H.shoes)
+ continue
M.dropItemToGround(W)
for(var/obj/item/W in victim_belongings)
W.forceMove(return_pod)
diff --git a/code/modules/antagonists/wizard/equipment/artefact.dm b/code/modules/antagonists/wizard/equipment/artefact.dm
index eaef7a35f5..2701a2d006 100644
--- a/code/modules/antagonists/wizard/equipment/artefact.dm
+++ b/code/modules/antagonists/wizard/equipment/artefact.dm
@@ -234,7 +234,7 @@
H.equip_to_slot_or_del(new /obj/item/clothing/shoes/roman(H), SLOT_SHOES)
H.put_in_hands(new /obj/item/shield/riot/roman(H), TRUE)
H.put_in_hands(new /obj/item/claymore(H), TRUE)
- H.equip_to_slot_or_del(new /obj/item/twohanded/spear(H), SLOT_BACK)
+ H.equip_to_slot_or_del(new /obj/item/spear(H), SLOT_BACK)
/obj/item/voodoo
diff --git a/code/modules/antagonists/wizard/equipment/spellbook.dm b/code/modules/antagonists/wizard/equipment/spellbook.dm
index a9bc64a932..4c29c53a2e 100644
--- a/code/modules/antagonists/wizard/equipment/spellbook.dm
+++ b/code/modules/antagonists/wizard/equipment/spellbook.dm
@@ -430,12 +430,12 @@
/datum/spellbook_entry/item/mjolnir
name = "Mjolnir"
desc = "A mighty hammer on loan from Thor, God of Thunder. It crackles with barely contained power."
- item_path = /obj/item/twohanded/mjollnir
+ item_path = /obj/item/mjollnir
/datum/spellbook_entry/item/singularity_hammer
name = "Singularity Hammer"
desc = "A hammer that creates an intensely powerful field of gravity where it strikes, pulling everything nearby to the point of impact."
- item_path = /obj/item/twohanded/singularityhammer
+ item_path = /obj/item/singularityhammer
/datum/spellbook_entry/item/battlemage
name = "Battlemage Armour"
@@ -503,6 +503,7 @@
name = "Summon Guns"
desc = "Nothing could possibly go wrong with arming a crew of lunatics just itching for an excuse to kill you. Just be careful not to stand still too long!"
dynamic_requirement = 60
+ limit = 1
/datum/spellbook_entry/summon/guns/IsAvailible()
if(!SSticker.mode) // In case spellbook is placed on map
@@ -521,6 +522,7 @@
name = "Summon Magic"
desc = "Share the wonders of magic with the crew and show them why they aren't to be trusted with it at the same time."
dynamic_requirement = 60
+ limit = 1
/datum/spellbook_entry/summon/magic/IsAvailible()
if(!SSticker.mode) // In case spellbook is placed on map
@@ -560,6 +562,27 @@
. += "You cast it [times] times.
"
return .
+/datum/spellbook_entry/summon/curse_of_madness
+ name = "Curse of Madness"
+ desc = "Curses the station, warping the minds of everyone inside, causing lasting traumas. Warning: this spell can affect you if not cast from a safe distance."
+ cost = 4
+
+/datum/spellbook_entry/summon/curse_of_madness/Buy(mob/living/carbon/human/user, obj/item/spellbook/book)
+ SSblackbox.record_feedback("tally", "wizard_spell_learned", 1, name)
+ active = TRUE
+ var/message = stripped_input(user, "Whisper a secret truth to drive your victims to madness.", "Whispers of Madness")
+ if(!message)
+ return FALSE
+ curse_of_madness(user, message)
+ to_chat(user, "You have cast the curse of insanity!")
+ playsound(user, 'sound/magic/mandswap.ogg', 50, 1)
+ return TRUE
+
+/datum/spellbook_entry/summon/curse_of_madness/IsAvailible()
+ if(!SSticker.mode) // In case spellbook is placed on map
+ return FALSE
+ return (!CONFIG_GET(flag/no_summon_traumas) && ..())
+
/obj/item/spellbook
name = "spell book"
desc = "An unearthly tome that glows with power."
diff --git a/code/modules/antagonists/wizard/wizard.dm b/code/modules/antagonists/wizard/wizard.dm
index 70adafd3fb..7263793f7f 100644
--- a/code/modules/antagonists/wizard/wizard.dm
+++ b/code/modules/antagonists/wizard/wizard.dm
@@ -13,6 +13,7 @@
var/move_to_lair = TRUE
var/outfit_type = /datum/outfit/wizard
var/wiz_age = WIZARD_AGE_MIN /* Wizards by nature cannot be too young. */
+ show_to_ghosts = TRUE
/datum/antagonist/wizard/on_gain()
register()
diff --git a/code/modules/antagonists/xeno/xeno.dm b/code/modules/antagonists/xeno/xeno.dm
index 7c4c5351df..2cc8e34b99 100644
--- a/code/modules/antagonists/xeno/xeno.dm
+++ b/code/modules/antagonists/xeno/xeno.dm
@@ -12,6 +12,7 @@
name = "Xenomorph"
job_rank = ROLE_ALIEN
show_in_antagpanel = FALSE
+ show_to_ghosts = TRUE
var/datum/team/xeno/xeno_team
threat = 3
diff --git a/code/modules/arousal/genitals.dm b/code/modules/arousal/genitals.dm
index fb254a2dcc..4d2e5e6fef 100644
--- a/code/modules/arousal/genitals.dm
+++ b/code/modules/arousal/genitals.dm
@@ -1,7 +1,7 @@
/obj/item/organ/genital
color = "#fcccb3"
w_class = WEIGHT_CLASS_SMALL
- organ_flags = ORGAN_NO_DISMEMBERMENT
+ organ_flags = ORGAN_NO_DISMEMBERMENT|ORGAN_EDIBLE
var/shape
var/sensitivity = 1 // wow if this were ever used that'd be cool but it's not but i'm keeping it for my unshit code
var/genital_flags //see citadel_defines.dm
diff --git a/code/modules/assembly/bomb.dm b/code/modules/assembly/bomb.dm
index a40a4c1a42..36c444f02d 100644
--- a/code/modules/assembly/bomb.dm
+++ b/code/modules/assembly/bomb.dm
@@ -53,8 +53,8 @@
return
if(I.use_tool(src, user, 0, volume=40))
status = TRUE
- GLOB.bombers += "[key_name(user)] welded a single tank bomb. Temp: [bombtank.air_contents.temperature-T0C]"
- message_admins("[ADMIN_LOOKUPFLW(user)] welded a single tank bomb. Temp: [bombtank.air_contents.temperature-T0C]")
+ GLOB.bombers += "[key_name(user)] welded a single tank bomb. Temp: [bombtank.air_contents.return_temperature()-T0C]"
+ message_admins("[ADMIN_LOOKUPFLW(user)] welded a single tank bomb. Temp: [bombtank.air_contents.return_temperature()-T0C]")
to_chat(user, "A pressure hole has been bored to [bombtank] valve. \The [bombtank] can now be ignited.")
add_fingerprint(user)
return TRUE
@@ -145,8 +145,7 @@
return
/obj/item/tank/proc/ignite() //This happens when a bomb is told to explode
- var/fuel_moles = air_contents.gases[/datum/gas/plasma] + air_contents.gases[/datum/gas/oxygen]/6
- GAS_GARBAGE_COLLECT(air_contents.gases)
+ var/fuel_moles = air_contents.get_moles(/datum/gas/plasma) + air_contents.get_moles(/datum/gas/oxygen)/6
var/datum/gas_mixture/bomb_mixture = air_contents.copy()
var/strength = 1
@@ -156,7 +155,7 @@
qdel(master)
qdel(src)
- if(bomb_mixture.temperature > (T0C + 400))
+ if(bomb_mixture.return_temperature() > (T0C + 400))
strength = (fuel_moles/15)
if(strength >=1)
@@ -169,7 +168,7 @@
ground_zero.assume_air(bomb_mixture)
ground_zero.hotspot_expose(1000, 125)
- else if(bomb_mixture.temperature > (T0C + 250))
+ else if(bomb_mixture.return_temperature() > (T0C + 250))
strength = (fuel_moles/20)
if(strength >=1)
@@ -180,7 +179,7 @@
ground_zero.assume_air(bomb_mixture)
ground_zero.hotspot_expose(1000, 125)
- else if(bomb_mixture.temperature > (T0C + 100))
+ else if(bomb_mixture.return_temperature() > (T0C + 100))
strength = (fuel_moles/25)
if (strength >=1)
diff --git a/code/modules/assembly/health.dm b/code/modules/assembly/health.dm
index cddc4fb08f..0af6c85fb6 100644
--- a/code/modules/assembly/health.dm
+++ b/code/modules/assembly/health.dm
@@ -4,7 +4,6 @@
icon_state = "health"
custom_materials = list(/datum/material/iron=800, /datum/material/glass=200)
attachable = TRUE
- secured = FALSE
var/scanning = FALSE
var/health_scan
@@ -12,7 +11,8 @@
/obj/item/assembly/health/examine(mob/user)
. = ..()
- . += "Use a multitool to swap between \"detect death\" mode and \"detect critical state\" mode."
+ . += "Use it in hand to turn it off/on and Alt-click to swap between \"detect death\" mode and \"detect critical state\" mode."
+ . += "[src.scanning ? "The sensor is on and you can see [health_scan] displayed on the screen" : "The sensor is off"]."
/obj/item/assembly/health/activate()
if(!..())
@@ -30,14 +30,13 @@
update_icon()
return secured
-/obj/item/assembly/health/multitool_act(mob/living/user, obj/item/I)
+/obj/item/assembly/health/AltClick(mob/living/user)
if(alarm_health == HEALTH_THRESHOLD_CRIT)
alarm_health = HEALTH_THRESHOLD_DEAD
to_chat(user, "You toggle [src] to \"detect death\" mode.")
else
alarm_health = HEALTH_THRESHOLD_CRIT
to_chat(user, "You toggle [src] to \"detect critical state\" mode.")
- return TRUE
/obj/item/assembly/health/process()
if(!scanning || !secured)
@@ -46,7 +45,6 @@
var/atom/A = src
if(connected && connected.holder)
A = connected.holder
-
for(A, A && !ismob(A), A=A.loc);
// like get_turf(), but for mobs.
var/mob/living/M = A
@@ -71,36 +69,7 @@
STOP_PROCESSING(SSobj, src)
return
-/obj/item/assembly/health/ui_interact(mob/user as mob)//TODO: Change this to the wires thingy
+/obj/item/assembly/health/attack_self(mob/user)
. = ..()
- if(!secured)
- user.show_message("The [name] is unsecured!")
- return FALSE
- var/dat = "Health Sensor"
- dat += "
[scanning?"On":"Off"]"
- if(scanning && health_scan)
- dat += "
Health: [health_scan]"
- user << browse(dat, "window=hscan")
- onclose(user, "hscan")
-
-/obj/item/assembly/health/Topic(href, href_list)
- ..()
- if(!ismob(usr))
- return
-
- var/mob/user = usr
-
- if(!user.canUseTopic(src))
- usr << browse(null, "window=hscan")
- onclose(usr, "hscan")
- return
-
- if(href_list["scanning"])
- toggle_scan()
-
- if(href_list["close"])
- usr << browse(null, "window=hscan")
- return
-
- attack_self(user)
- return
+ to_chat(user, "You toggle [src] [src.scanning ? "off" : "on"].")
+ toggle_scan()
diff --git a/code/modules/assembly/infrared.dm b/code/modules/assembly/infrared.dm
index 33c6d46045..8cb6eb66fb 100644
--- a/code/modules/assembly/infrared.dm
+++ b/code/modules/assembly/infrared.dm
@@ -4,7 +4,8 @@
icon_state = "infrared"
custom_materials = list(/datum/material/iron=1000, /datum/material/glass=500)
is_position_sensitive = TRUE
-
+ var/ui_x = 225
+ var/ui_y = 110
var/on = FALSE
var/visible = FALSE
var/maxlength = 8
@@ -38,7 +39,7 @@
/obj/item/assembly/infra/activate()
if(!..())
- return FALSE//Cooldown check
+ return FALSE //Cooldown check
on = !on
refreshBeam()
update_icon()
@@ -69,7 +70,7 @@
holder.update_icon()
return
-/obj/item/assembly/infra/dropped(mob/user)
+/obj/item/assembly/infra/dropped()
. = ..()
if(holder)
holder_movement() //sync the dir of the device as well if it's contained in a TTV or an assembly holder
@@ -133,7 +134,7 @@
. = ..()
setDir(t)
-/obj/item/assembly/infra/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback)
+/obj/item/assembly/infra/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, gentle = FALSE)
. = ..()
olddir = dir
@@ -176,55 +177,56 @@
return
return refreshBeam()
-/obj/item/assembly/infra/ui_interact(mob/user)//TODO: change this this to the wire control panel
- . = ..()
- if(is_secured(user))
- user.set_machine(src)
- var/dat = "Infrared Laser"
- dat += "
Status: [on ? "On" : "Off"]"
- dat += "
Visibility: [visible ? "Visible" : "Invisible"]"
- dat += "
Refresh"
- dat += "
Close"
- user << browse(dat, "window=infra")
- onclose(user, "infra")
- return
-
-/obj/item/assembly/infra/Topic(href, href_list)
- ..()
- if(usr.incapacitated() || !in_range(loc, usr))
- usr << browse(null, "window=infra")
- onclose(usr, "infra")
- return
- if(href_list["state"])
- on = !(on)
- update_icon()
- refreshBeam()
- if(href_list["visible"])
- visible = !(visible)
- update_icon()
- refreshBeam()
- if(href_list["close"])
- usr << browse(null, "window=infra")
- return
- if(usr)
- attack_self(usr)
-
/obj/item/assembly/infra/setDir()
. = ..()
refreshBeam()
+/obj/item/assembly/infra/ui_status(mob/user)
+ if(is_secured(user))
+ return ..()
+ return UI_CLOSE
+
+/obj/item/assembly/infra/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
+ 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, "InfraredEmitter", name, ui_x, ui_y, master_ui, state)
+ ui.open()
+
+/obj/item/assembly/infra/ui_data(mob/user)
+ var/list/data = list()
+ data["on"] = on
+ data["visible"] = visible
+ return data
+
+/obj/item/assembly/infra/ui_act(action, params)
+ if(..())
+ return
+
+ switch(action)
+ if("power")
+ on = !on
+ . = TRUE
+ if("visibility")
+ visible = !visible
+ . = TRUE
+
+ update_icon()
+ refreshBeam()
+
/***************************IBeam*********************************/
/obj/effect/beam/i_beam
name = "infrared beam"
icon = 'icons/obj/projectiles.dmi'
icon_state = "ibeam"
- var/obj/item/assembly/infra/master
anchored = TRUE
density = FALSE
pass_flags = PASSTABLE|PASSGLASS|PASSGRILLE|LETPASSTHROW
+ var/obj/item/assembly/infra/master
/obj/effect/beam/i_beam/Crossed(atom/movable/AM as mob|obj)
+ . = ..()
if(istype(AM, /obj/effect/beam))
return
if (isitem(AM))
diff --git a/code/modules/assembly/proximity.dm b/code/modules/assembly/proximity.dm
index f1a4ce47cc..f0d8f28688 100644
--- a/code/modules/assembly/proximity.dm
+++ b/code/modules/assembly/proximity.dm
@@ -4,7 +4,8 @@
icon_state = "prox"
custom_materials = list(/datum/material/iron=800, /datum/material/glass=200)
attachable = TRUE
-
+ var/ui_x = 250
+ var/ui_y = 185
var/scanning = FALSE
var/timing = FALSE
var/time = 10
@@ -26,7 +27,7 @@
/obj/item/assembly/prox_sensor/activate()
if(!..())
- return FALSE//Cooldown check
+ return FALSE //Cooldown check
if(!scanning)
timing = !timing
else
@@ -41,7 +42,6 @@
else
proximity_monitor.SetHost(src,src)
-
/obj/item/assembly/prox_sensor/toggle_secure()
secured = !secured
if(!secured)
@@ -56,8 +56,6 @@
update_icon()
return secured
-
-
/obj/item/assembly/prox_sensor/HasProximity(atom/movable/AM as mob|obj)
if (istype(AM, /obj/effect/beam))
return
@@ -75,7 +73,6 @@
next_activate = world.time + 30
return TRUE
-
/obj/item/assembly/prox_sensor/process()
if(!timing)
return
@@ -111,50 +108,47 @@
holder.update_icon()
return
-/obj/item/assembly/prox_sensor/ui_interact(mob/user)//TODO: Change this to the wires thingy
- . = ..()
+/obj/item/assembly/prox_sensor/ui_status(mob/user)
if(is_secured(user))
- var/second = time % 60
- var/minute = (time - second) / 60
- var/dat = "Proximity Sensor"
- if(!scanning)
- dat += "
[(timing ? "Arming" : "Not Arming")] [minute]:[second]"
- dat += "
- - + +"
- dat += "
Armed":"1'>Unarmed (Movement sensor active when armed!)"]"
- dat += "
Detection range: - [sensitivity] +"
- dat += "
Refresh"
- dat += "
Close"
- user << browse(dat, "window=prox")
- onclose(user, "prox")
+ return ..()
+ return UI_CLOSE
+
+/obj/item/assembly/prox_sensor/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
+ 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, "ProximitySensor", name, ui_x, ui_y, master_ui, state)
+ ui.open()
+
+/obj/item/assembly/prox_sensor/ui_data(mob/user)
+ var/list/data = list()
+ data["seconds"] = round(time % 60)
+ data["minutes"] = round((time - data["seconds"]) / 60)
+ data["timing"] = timing
+ data["scanning"] = scanning
+ data["sensitivity"] = sensitivity
+ return data
+
+/obj/item/assembly/prox_sensor/ui_act(action, params)
+ if(..())
return
-
-/obj/item/assembly/prox_sensor/Topic(href, href_list)
- ..()
- if(usr.incapacitated() || !in_range(loc, usr))
- usr << browse(null, "window=prox")
- onclose(usr, "prox")
- return
-
- if(href_list["sense"])
- sensitivity_change(((href_list["sense"] == "up") ? 1 : -1))
-
- if(href_list["scanning"])
- toggle_scan(text2num(href_list["scanning"]))
-
- if(href_list["time"])
- timing = text2num(href_list["time"])
- update_icon()
-
- if(href_list["tp"])
- var/tp = text2num(href_list["tp"])
- time += tp
- time = min(max(round(time), 0), 600)
-
- if(href_list["close"])
- usr << browse(null, "window=prox")
- return
-
- if(usr)
- attack_self(usr)
-
+ switch(action)
+ if("scanning")
+ toggle_scan(!scanning)
+ . = TRUE
+ if("sense")
+ var/value = text2num(params["range"])
+ if(value)
+ sensitivity_change(value)
+ . = TRUE
+ if("time")
+ timing = !timing
+ update_icon()
+ . = TRUE
+ if("input")
+ var/value = text2num(params["adjust"])
+ if(value)
+ value = round(time + value)
+ time = clamp(value, 0, 600)
+ . = TRUE
diff --git a/code/modules/assembly/signaler.dm b/code/modules/assembly/signaler.dm
index e70b6e5c74..fe548d662d 100644
--- a/code/modules/assembly/signaler.dm
+++ b/code/modules/assembly/signaler.dm
@@ -8,7 +8,8 @@
custom_materials = list(/datum/material/iron=400, /datum/material/glass=120)
wires = WIRE_RECEIVE | WIRE_PULSE | WIRE_RADIO_PULSE | WIRE_RADIO_RECEIVE
attachable = TRUE
-
+ var/ui_x = 280
+ var/ui_y = 132
var/code = DEFAULT_SIGNALER_CODE
var/frequency = FREQ_SIGNALER
var/datum/radio_frequency/radio_connection
@@ -47,14 +48,16 @@
holder.update_icon()
return
-/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
+/obj/item/assembly/signaler/ui_status(mob/user)
+ if(is_secured(user))
+ return ..()
+ return UI_CLOSE
+
+/obj/item/assembly/signaler/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
+ 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)
- var/ui_width = 280
- var/ui_height = 132
- ui = new(user, src, ui_key, "signaler", name, ui_width, ui_height, master_ui, state)
+ ui = new(user, src, ui_key, "Signaler", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/item/assembly/signaler/ui_data(mob/user)
@@ -231,4 +234,4 @@
/obj/item/assembly/signaler/cyborg/attackby(obj/item/W, mob/user, params)
return
/obj/item/assembly/signaler/cyborg/screwdriver_act(mob/living/user, obj/item/I)
- return
\ No newline at end of file
+ return
diff --git a/code/modules/assembly/timer.dm b/code/modules/assembly/timer.dm
index bbcddbdb93..d0f0e1e593 100644
--- a/code/modules/assembly/timer.dm
+++ b/code/modules/assembly/timer.dm
@@ -4,7 +4,8 @@
icon_state = "timer"
custom_materials = list(/datum/material/iron=500, /datum/material/glass=50)
attachable = TRUE
-
+ var/ui_x = 275
+ var/ui_y = 115
var/timing = FALSE
var/time = 5
var/saved_time = 5
@@ -41,7 +42,6 @@
update_icon()
return TRUE
-
/obj/item/assembly/timer/toggle_secure()
secured = !secured
if(secured)
@@ -52,7 +52,6 @@
update_icon()
return secured
-
/obj/item/assembly/timer/proc/timer_end()
if(!secured || next_activate > world.time)
return FALSE
@@ -66,7 +65,6 @@
timing = TRUE
update_icon()
-
/obj/item/assembly/timer/process()
if(!timing)
return
@@ -76,7 +74,6 @@
timer_end()
time = saved_time
-
/obj/item/assembly/timer/update_icon()
cut_overlays()
attached_overlays = list()
@@ -86,50 +83,44 @@
if(holder)
holder.update_icon()
-
-/obj/item/assembly/timer/ui_interact(mob/user)//TODO: Have this use the wires
- . = ..()
+/obj/item/assembly/timer/ui_status(mob/user)
if(is_secured(user))
- var/second = time % 60
- var/minute = (time - second) / 60
- var/dat = "Timing Unit"
- dat += "
[(timing ? "Timing" : "Not Timing")] [minute]:[second]"
- dat += "
- - + +"
- dat += "
Stop repeating" : "1'>Set to repeat")]"
- dat += "
Refresh"
- dat += "
Close"
- var/datum/browser/popup = new(user, "timer", name)
- popup.set_content(dat)
- popup.open()
+ return ..()
+ return UI_CLOSE
+/obj/item/assembly/timer/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
+ 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, "Timer", name, ui_x, ui_y, master_ui, state)
+ ui.open()
-/obj/item/assembly/timer/Topic(href, href_list)
- ..()
- if(!usr.canUseTopic(src, BE_CLOSE))
- usr << browse(null, "window=timer")
- onclose(usr, "timer")
+/obj/item/assembly/timer/ui_data(mob/user)
+ var/list/data = list()
+ data["seconds"] = round(time % 60)
+ data["minutes"] = round((time - data["seconds"]) / 60)
+ data["timing"] = timing
+ data["loop"] = loop
+ return data
+
+/obj/item/assembly/timer/ui_act(action, params)
+ if(..())
return
- if(href_list["time"])
- timing = text2num(href_list["time"])
- if(timing && istype(holder, /obj/item/transfer_valve))
- var/timer_message = "[ADMIN_LOOKUPFLW(usr)] activated [src] attachment on [holder]."
- message_admins(timer_message)
- GLOB.bombers += timer_message
- log_game("[key_name(usr)] activated [src] attachment on [holder]")
- update_icon()
- if(href_list["repeat"])
- loop = text2num(href_list["repeat"])
-
- if(href_list["tp"])
- var/tp = text2num(href_list["tp"])
- time += tp
- time = min(max(round(time), 1), 600)
- saved_time = time
-
- if(href_list["close"])
- usr << browse(null, "window=timer")
- return
-
- if(usr)
- attack_self(usr)
+ switch(action)
+ if("time")
+ timing = !timing
+ if(timing && istype(holder, /obj/item/transfer_valve))
+ log_game(usr, "activated a", src, "attachment on [holder]")
+ update_icon()
+ . = TRUE
+ if("repeat")
+ loop = !loop
+ . = TRUE
+ if("input")
+ var/value = text2num(params["adjust"])
+ if(value)
+ value = round(time + value)
+ time = clamp(value, 1, 600)
+ saved_time = time
+ . = TRUE
diff --git a/code/modules/asset_cache/asset_cache.dm b/code/modules/asset_cache/asset_cache.dm
index 7a12e5e9f6..f702bf714e 100644
--- a/code/modules/asset_cache/asset_cache.dm
+++ b/code/modules/asset_cache/asset_cache.dm
@@ -1,10 +1,12 @@
/*
Asset cache quick users guide:
+
Make a datum in asset_list_items.dm with your assets for your thing.
Checkout asset_list.dm for the helper subclasses
The simple subclass will most like be of use for most cases.
Then call get_asset_datum() with the type of the datum you created and store the return
Then call .send(client) on that stored return value.
+
Note: If your code uses output() with assets you will need to call asset_flush on the client and wait for it to return before calling output(). You only need do this if .send(client) returned TRUE
*/
@@ -98,3 +100,4 @@ Note: If your code uses output() with assets you will need to call asset_flush o
//The same asset will always lead to the same asset name
/proc/generate_asset_name(file)
return "asset.[md5(fcopy_rsc(file))]"
+
diff --git a/code/modules/asset_cache/asset_cache_client.dm b/code/modules/asset_cache/asset_cache_client.dm
index 50a5685ded..0f51520f13 100644
--- a/code/modules/asset_cache/asset_cache_client.dm
+++ b/code/modules/asset_cache/asset_cache_client.dm
@@ -1,11 +1,4 @@
-/client
- var/list/sent_assets = list() // List of all asset filenames sent to this client by the asset cache, along with their assoicated md5s
- var/list/completed_asset_jobs = list() /// List of all completed blocking send jobs awaiting acknowledgement by send_asset
-
- var/last_asset_job = 0 /// Last asset send job id.
- var/last_completed_asset_job = 0
-
/// Process asset cache client topic calls for "asset_cache_confirm_arrival=[INT]"
/client/proc/asset_cache_confirm_arrival(job_id)
var/asset_cache_job = round(text2num(job_id))
@@ -50,7 +43,7 @@
var/t = 0
var/timeout_time = timeout
src << browse({""}, "window=asset_cache_browser&file=asset_cache_send_verify.htm")
-
+
while(!completed_asset_jobs["[job]"] && t < timeout_time) // Reception is handled in Topic()
stoplag(1) // Lock up the caller until this is received.
t++
diff --git a/code/modules/asset_cache/asset_list.dm b/code/modules/asset_cache/asset_list.dm
index fe9859f238..2e5881c67f 100644
--- a/code/modules/asset_cache/asset_list.dm
+++ b/code/modules/asset_cache/asset_list.dm
@@ -226,3 +226,5 @@ GLOBAL_LIST_EMPTY(asset_datums)
/datum/asset/simple/icon_states/multiple_icons/register()
for(var/i in icons)
..(i)
+
+
diff --git a/code/modules/asset_cache/asset_list_items.dm b/code/modules/asset_cache/asset_list_items.dm
index 13a1803188..57d8e55e07 100644
--- a/code/modules/asset_cache/asset_list_items.dm
+++ b/code/modules/asset_cache/asset_list_items.dm
@@ -1,27 +1,10 @@
//DEFINITIONS FOR ASSET DATUMS START HERE.
-/* uncomment this and delete the tgui def bellow this for the new tgui
+
/datum/asset/simple/tgui
assets = list(
"tgui.bundle.js" = 'tgui/packages/tgui/public/tgui.bundle.js',
"tgui.bundle.css" = 'tgui/packages/tgui/public/tgui.bundle.css',
)
-*/
-/datum/asset/simple/tgui
- assets = list(
- // Old 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.bundle.js" = 'tgui-next/packages/tgui/public/tgui.bundle.js',
- "tgui.bundle.css" = 'tgui-next/packages/tgui/public/tgui.bundle.css',
- // Old TGUI compatability
- "tgui-fallback.html" = 'tgui-next/packages/tgui/public/tgui-fallback.html',
- "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(
@@ -56,17 +39,17 @@
"smmon_3.gif" = 'icons/program_icons/smmon_3.gif',
"smmon_4.gif" = 'icons/program_icons/smmon_4.gif',
"smmon_5.gif" = 'icons/program_icons/smmon_5.gif',
- "smmon_6.gif" = 'icons/program_icons/smmon_6.gif'
- //"borg_mon.gif" = 'icons/program_icons/borg_mon.gif'
+ "smmon_6.gif" = 'icons/program_icons/smmon_6.gif',
+ "borg_mon.gif" = 'icons/program_icons/borg_mon.gif'
)
-/* uncomment if you're porting the new ntnet app
+
/datum/asset/simple/radar_assets
assets = list(
"ntosradarbackground.png" = 'icons/UI_Icons/tgui/ntosradar_background.png',
"ntosradarpointer.png" = 'icons/UI_Icons/tgui/ntosradar_pointer.png',
"ntosradarpointerS.png" = 'icons/UI_Icons/tgui/ntosradar_pointer_S.png'
)
-*/
+
/datum/asset/spritesheet/simple/pda
name = "pda"
assets = list(
@@ -95,7 +78,6 @@
"refresh" = 'icons/pda_icons/pda_refresh.png',
"scanner" = 'icons/pda_icons/pda_scanner.png',
"signaler" = 'icons/pda_icons/pda_signaler.png',
- //"skills" = 'icons/pda_icons/pda_skills.png',
"status" = 'icons/pda_icons/pda_status.png',
"dronephone" = 'icons/pda_icons/pda_dronephone.png',
"emoji" = 'icons/pda_icons/pda_emoji.png'
@@ -115,12 +97,9 @@
"stamp-cap" = 'icons/stamp_icons/large_stamp-cap.png',
"stamp-qm" = 'icons/stamp_icons/large_stamp-qm.png',
"stamp-law" = 'icons/stamp_icons/large_stamp-law.png'
- //"stamp-chap" = 'icons/stamp_icons/large_stamp-chap.png',
- //"stamp-mime" = 'icons/stamp_icons/large_stamp-mime.png',
- //"stamp-centcom" = 'icons/stamp_icons/large_stamp-centcom.png',
- //"stamp-syndicate" = 'icons/stamp_icons/large_stamp-syndicate.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',
@@ -168,12 +147,13 @@
"jquery.min.js" = 'code/modules/goonchat/browserassets/js/jquery.min.js',
)
+
/datum/asset/simple/goonchat
assets = list(
"json2.min.js" = 'code/modules/goonchat/browserassets/js/json2.min.js',
"browserOutput.js" = 'code/modules/goonchat/browserassets/js/browserOutput.js',
"browserOutput.css" = 'code/modules/goonchat/browserassets/css/browserOutput.css',
- "browserOutput_dark.css" = 'code/modules/goonchat/browserassets/css/browserOutput_dark.css', //dark theme, cit specific
+ "browserOutput_dark.css" = 'code/modules/goonchat/browserassets/css/browserOutput_dark.css',
"browserOutput_light.css" = 'code/modules/goonchat/browserassets/css/browserOutput_light.css'
)
@@ -192,6 +172,7 @@
/datum/asset/spritesheet/goonchat/register()
InsertAll("emoji", 'icons/emoji.dmi')
+ InsertAll("emoji", 'icons/emoji_32.dmi')
// pre-loading all lanugage icons also helps to avoid meta
InsertAll("language", 'icons/misc/language.dmi')
@@ -218,6 +199,16 @@
"none_button.png" = 'html/none_button.png',
)
+/datum/asset/simple/arcade
+ assets = list(
+ "boss1.gif" = 'icons/UI_Icons/Arcade/boss1.gif',
+ "boss2.gif" = 'icons/UI_Icons/Arcade/boss2.gif',
+ "boss3.gif" = 'icons/UI_Icons/Arcade/boss3.gif',
+ "boss4.gif" = 'icons/UI_Icons/Arcade/boss4.gif',
+ "boss5.gif" = 'icons/UI_Icons/Arcade/boss5.gif',
+ "boss6.gif" = 'icons/UI_Icons/Arcade/boss6.gif',
+ )
+
/datum/asset/spritesheet/simple/minesweeper
name = "minesweeper"
assets = list(
@@ -236,45 +227,7 @@
"minehit" = 'icons/UI_Icons/minesweeper_tiles/minehit.png'
)
-/* Port the app game thing
-/datum/asset/simple/arcade
- assets = list(
- "boss1.gif" = 'icons/UI_Icons/Arcade/boss1.gif',
- "boss2.gif" = 'icons/UI_Icons/Arcade/boss2.gif',
- "boss3.gif" = 'icons/UI_Icons/Arcade/boss3.gif',
- "boss4.gif" = 'icons/UI_Icons/Arcade/boss4.gif',
- "boss5.gif" = 'icons/UI_Icons/Arcade/boss5.gif',
- "boss6.gif" = 'icons/UI_Icons/Arcade/boss6.gif',
- )
-*/
-/*
-/datum/asset/spritesheet/simple/achievements
- name ="achievements"
- assets = list(
- "default" = 'icons/UI_Icons/Achievements/default.png',
- "basemisc" = 'icons/UI_Icons/Achievements/basemisc.png',
- "baseboss" = 'icons/UI_Icons/Achievements/baseboss.png',
- "baseskill" = 'icons/UI_Icons/Achievements/baseskill.png',
- "bbgum" = 'icons/UI_Icons/Achievements/Boss/bbgum.png',
- "colossus" = 'icons/UI_Icons/Achievements/Boss/colossus.png',
- "hierophant" = 'icons/UI_Icons/Achievements/Boss/hierophant.png',
- "legion" = 'icons/UI_Icons/Achievements/Boss/legion.png',
- "miner" = 'icons/UI_Icons/Achievements/Boss/miner.png',
- "swarmer" = 'icons/UI_Icons/Achievements/Boss/swarmer.png',
- "tendril" = 'icons/UI_Icons/Achievements/Boss/tendril.png',
- "featofstrength" = 'icons/UI_Icons/Achievements/Misc/featofstrength.png',
- "helbital" = 'icons/UI_Icons/Achievements/Misc/helbital.png',
- "jackpot" = 'icons/UI_Icons/Achievements/Misc/jackpot.png',
- "meteors" = 'icons/UI_Icons/Achievements/Misc/meteors.png',
- "timewaste" = 'icons/UI_Icons/Achievements/Misc/timewaste.png',
- "upgrade" = 'icons/UI_Icons/Achievements/Misc/upgrade.png',
- "clownking" = 'icons/UI_Icons/Achievements/Misc/clownking.png',
- "clownthanks" = 'icons/UI_Icons/Achievements/Misc/clownthanks.png',
- "rule8" = 'icons/UI_Icons/Achievements/Misc/rule8.png',
- "snail" = 'icons/UI_Icons/Achievements/Misc/snail.png',
- "mining" = 'icons/UI_Icons/Achievements/Skills/mining.png',
- )
-*/
+
/datum/asset/spritesheet/simple/pills
name ="pills"
assets = list(
@@ -312,8 +265,8 @@
/datum/asset/spritesheet/pipes
name = "pipes"
-/datum/asset/spritesheet/pipes/register() //we do not have chempipes
- for (var/each in list('icons/obj/atmospherics/pipes/pipe_item.dmi', 'icons/obj/atmospherics/pipes/disposal.dmi', 'icons/obj/atmospherics/pipes/transit_tube.dmi'))
+/datum/asset/spritesheet/pipes/register()
+ for (var/each in list('icons/obj/atmospherics/pipes/pipe_item.dmi', 'icons/obj/atmospherics/pipes/disposal.dmi'))
InsertAll("", each, GLOB.alldirs)
..()
@@ -322,7 +275,7 @@
name = "design"
/datum/asset/spritesheet/research_designs/register()
- for(var/path in subtypesof(/datum/design))
+ for (var/path in subtypesof(/datum/design))
var/datum/design/D = path
var/icon_file
@@ -380,9 +333,9 @@
name = "vending"
/datum/asset/spritesheet/vending/register()
- for(var/k in GLOB.vending_products)
+ for (var/k in GLOB.vending_products)
var/atom/item = k
- if(!ispath(item, /atom))
+ if (!ispath(item, /atom))
continue
var/icon_file = initial(item.icon)
@@ -393,12 +346,12 @@
if(icon_state in icon_states_list)
I = icon(icon_file, icon_state, SOUTH)
var/c = initial(item.color)
- if(!isnull(c) && c != "#FFFFFF")
+ if (!isnull(c) && c != "#FFFFFF")
I.Blend(c, ICON_MULTIPLY)
else
var/icon_states_string
- for(var/an_icon_state in icon_states_list)
- if(!icon_states_string)
+ for (var/an_icon_state in icon_states_list)
+ if (!icon_states_string)
icon_states_string = "[json_encode(an_icon_state)](\ref[an_icon_state])"
else
icon_states_string += ", [json_encode(an_icon_state)](\ref[an_icon_state])"
@@ -417,7 +370,12 @@
"dna_extra.gif" = 'html/dna_extra.gif'
)
-/datum/asset/simple/vv
+/datum/asset/simple/orbit
assets = list(
- "view_variables.css" = 'html/admin/view_variables.css'
+ "ghost.png" = 'html/ghost.png'
)
+
+ assets = list(
+ "ghost.png" = 'html/ghost.png'
+ )
+
diff --git a/code/modules/asset_cache/validate_assets.html b/code/modules/asset_cache/validate_assets.html
index 205a7f4dad..b27a266c00 100644
--- a/code/modules/asset_cache/validate_assets.html
+++ b/code/modules/asset_cache/validate_assets.html
@@ -23,6 +23,7 @@
}
};
xhr.send(null);
-
-