[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"
+ if(extra || scrambled)
+ temp_html += "
Nullify"
+ else
+ temp_html += "
Nullify"
+ temp_html += "
"
+ return temp_html
/obj/machinery/computer/scan_consolenew/Topic(href, href_list)
if(..())
@@ -340,13 +547,16 @@
radstrength = WRAP(num, 1, RADIATION_STRENGTH_MAX+1)
if("screen")
current_screen = href_list["text"]
- if("rejuv")
- if(viable_occupant && viable_occupant.reagents)
- var/potassiodide_amount = viable_occupant.reagents.get_reagent_amount("potass_iodide")
- var/can_add = max(min(REJUVENATORS_MAX - potassiodide_amount, REJUVENATORS_INJECT), 0)
- viable_occupant.reagents.add_reagent("potass_iodide", can_add)
+ 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 an Text", null) as text|null)
+ 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]
@@ -358,7 +568,6 @@
buffer[num] = list(
"label"="Buffer[num]:[viable_occupant.real_name]",
"UI"=viable_occupant.dna.uni_identity,
- "SE"=viable_occupant.dna.struc_enzymes,
"UE"=viable_occupant.dna.unique_enzymes,
"name"=viable_occupant.real_name,
"blood_type"=viable_occupant.dna.blood_type
@@ -372,8 +581,6 @@
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("se")
- apply_buffer(SCANNER_ACTION_SE,num)
if("ui")
apply_buffer(SCANNER_ACTION_UI,num)
if("ue")
@@ -387,28 +594,6 @@
if(istype(buffer_slot))
var/obj/item/dnainjector/timed/I
switch(href_list["text"])
- if("se")
- if(buffer_slot["SE"])
- I = new /obj/item/dnainjector/timed(loc)
- var/powers = 0
- for(var/datum/mutation/human/HM in GLOB.good_mutations + GLOB.bad_mutations + GLOB.not_good_mutations)
- if(HM.check_block_string(buffer_slot["SE"]))
- I.add_mutations.Add(HM)
- if(HM in GLOB.good_mutations)
- powers += 1
- if(HM in GLOB.bad_mutations + GLOB.not_good_mutations)
- powers -= 1 //To prevent just unlocking everything to get all powers to a syringe for max tech
- else
- I.remove_mutations.Add(HM)
- var/time_coeff
- for(var/datum/mutation/human/HM in I.add_mutations)
- if(!time_coeff)
- time_coeff = HM.time_coeff
- continue
- time_coeff = min(time_coeff,HM.time_coeff)
- if(connected)
- I.duration = I.duration * time_coeff * connected.damage_coeff
- I.damage_coeff = connected.damage_coeff
if("ui")
if(buffer_slot["UI"])
I = new /obj/item/dnainjector/timed(loc)
@@ -447,7 +632,7 @@
if("setdelayed")
if(num)
delayed_action = list("action"=text2num(href_list["delayaction"]),"buffer"=num)
- if("pulseui","pulsese")
+ if("pulseui")
if(num && viable_occupant && connected)
radduration = WRAP(radduration, 1, RADIATION_DURATION_MAX+1)
radstrength = WRAP(radstrength, 1, RADIATION_STRENGTH_MAX+1)
@@ -459,7 +644,7 @@
ui_interact(usr)
sleep(radduration*10)
- current_screen = "mainmenu"
+ current_screen = "ui"
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
@@ -480,35 +665,173 @@
viable_occupant.dna.uni_identity = copytext(viable_occupant.dna.uni_identity, 1, num) + hex + copytext(viable_occupant.dna.uni_identity, num+1, 0)
viable_occupant.updateappearance(mutations_overlay_update=1)
- if("pulsese")
- var/len = length(viable_occupant.dna.struc_enzymes)
- num = WRAP(num, 1, len+1)
- num = randomize_radiation_accuracy(num, radduration + (connected.precision_coeff ** 2), len)
-
- var/block = round((num-1)/DNA_BLOCK_SIZE)+1
- var/subblock = num - block*DNA_BLOCK_SIZE
- last_change = "SE #[block]-[subblock]; "
-
- var/hex = copytext(viable_occupant.dna.struc_enzymes, num, num+1)
- last_change += "[hex]"
- hex = scramble(hex, radstrength, radduration)
- last_change += "->[hex]"
-
- viable_occupant.dna.struc_enzymes = copytext(viable_occupant.dna.struc_enzymes, 1, num) + hex + copytext(viable_occupant.dna.struc_enzymes, num+1, 0)
- viable_occupant.domutcheck()
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
+ else
+ current_mutation = mutations[num]
+
+ 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("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)
+
ui_interact(usr,last_change)
-/obj/machinery/computer/scan_consolenew/proc/scramble(input,rs,rd)
+/obj/machinery/computer/scan_consolenew/proc/scramble(input,rs,rd) //hexadecimal genetics. dont confuse with scramble button
var/length = length(input)
var/ran = gaussian(0, rs*RADIATION_STRENGTH_MULTIPLIER)
if(ran == 0)
- ran = pick(-1,1) //hacky, statistically should almost never happen. 0-change makes people mad though
+ ran = pick(-1,1) //hacky, statistically should almost never happen. 0-chance makes people mad though
else if(ran < 0)
ran = round(ran) //negative, so floor it
else
@@ -537,10 +860,6 @@
//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_SE)
- if(buffer_slot["SE"])
- viable_occupant.dna.struc_enzymes = buffer_slot["SE"]
- viable_occupant.domutcheck()
if(SCANNER_ACTION_UI)
if(buffer_slot["UI"])
viable_occupant.dna.uni_identity = buffer_slot["UI"]
@@ -562,15 +881,54 @@
viable_occupant.dna.blood_type = buffer_slot["blood_type"]
/obj/machinery/computer/scan_consolenew/proc/on_scanner_close()
- if(delayed_action && connected)
+ 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 ?
+/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
+
+
+/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
+
+/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)
+
+/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 REJUVENATORS_INJECT
-#undef REJUVENATORS_MAX
#undef NUMBER_OF_BUFFERS
#undef RADIATION_STRENGTH_MAX
diff --git a/code/game/machinery/exp_cloner.dm b/code/game/machinery/exp_cloner.dm
index e8364d22..ffe66e68 100644
--- a/code/game/machinery/exp_cloner.dm
+++ b/code/game/machinery/exp_cloner.dm
@@ -26,9 +26,9 @@
var/list/unclean_mutations = (GLOB.not_good_mutations|GLOB.bad_mutations)
H.dna.remove_mutation_group(unclean_mutations)
if(efficiency > 5 && prob(20))
- H.randmutvg()
+ H.easy_randmut(POSITIVE)
if(efficiency < 3 && prob(50))
- var/mob/M = H.randmutb()
+ var/mob/M = H.easy_randmut(NEGATIVE+MINOR_NEGATIVE)
if(ismob(M))
H = M
@@ -292,6 +292,6 @@
temp = "Cloning cycle already in progress."
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
else
- pod.growclone(null, mob_occupant.real_name, dna.uni_identity, dna.struc_enzymes, clone_species, dna.features, mob_occupant.faction)
+ pod.growclone(null, mob_occupant.real_name, dna.uni_identity, dna.mutation_index, 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/mecha/equipment/tools/other_tools.dm b/code/game/mecha/equipment/tools/other_tools.dm
index 7fb86b9d..f152b651 100644
--- a/code/game/mecha/equipment/tools/other_tools.dm
+++ b/code/game/mecha/equipment/tools/other_tools.dm
@@ -284,12 +284,12 @@
if(equip_ready) //disabled
return
var/area/A = get_area(chassis)
- var/pow_chan = get_power_channel(A)
+ var/pow_chan = GET_MUTATION_POWER_channel(A)
if(pow_chan)
return 1000 //making magic
-/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/proc/get_power_channel(var/area/A)
+/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/proc/GET_MUTATION_POWER_channel(var/area/A)
var/pow_chan
if(A)
for(var/c in use_channels)
diff --git a/code/game/objects/items/blueprints.dm b/code/game/objects/items/blueprints.dm
index 699efaac..a0537a66 100644
--- a/code/game/objects/items/blueprints.dm
+++ b/code/game/objects/items/blueprints.dm
@@ -153,6 +153,8 @@
/area/centcom,
/area/asteroid,
/area/tdome,
+ /area/hilbertshotel,
+ /area/hilbertshotelstorage,
/area/wizard_station
)
for (var/type in SPECIALS)
diff --git a/code/game/objects/items/chromosome.dm b/code/game/objects/items/chromosome.dm
new file mode 100644
index 00000000..01c22bb4
--- /dev/null
+++ b/code/game/objects/items/chromosome.dm
@@ -0,0 +1,92 @@
+/obj/item/chromosome
+ name = "blank chromosome"
+ icon = 'icons/obj/chromosomes.dmi'
+ icon_state = ""
+ desc = "A tube holding chromosomic data."
+ force = 0
+ w_class = WEIGHT_CLASS_SMALL
+
+ var/stabilizer_coeff = 1 //lower is better, affects genetic stability
+ var/synchronizer_coeff = 1 //lower is better, affects chance to backfire
+ var/power_coeff = 1 //higher is better, affects "strength"
+ var/energy_coeff = 1 //lower is better. affects recharge time
+
+ var/weight = 5
+
+/obj/item/chromosome/proc/can_apply(datum/mutation/human/HM)
+ if(!HM || !(HM.can_chromosome == CHROMOSOME_NONE))
+ return FALSE
+ if((stabilizer_coeff != 1) && (HM.stabilizer_coeff != -1)) //if the chromosome is 1, we dont change anything. If the mutation is -1, we cant change it. sorry
+ return TRUE
+ if((synchronizer_coeff != 1) && (HM.synchronizer_coeff != -1))
+ return TRUE
+ if((power_coeff != 1) && (HM.power_coeff != -1))
+ return TRUE
+ if((energy_coeff != 1) && (HM.energy_coeff != -1))
+ return TRUE
+
+/obj/item/chromosome/proc/apply(datum/mutation/human/HM)
+ if(HM.stabilizer_coeff != -1)
+ HM.stabilizer_coeff = stabilizer_coeff
+ if(HM.synchronizer_coeff != -1)
+ HM.synchronizer_coeff = synchronizer_coeff
+ if(HM.power_coeff != -1)
+ HM.power_coeff = power_coeff
+ if(HM.energy_coeff != -1)
+ HM.energy_coeff = energy_coeff
+ HM.can_chromosome = 2
+ HM.chromosome_name = name
+ HM.modify()
+ qdel(src)
+
+/proc/generate_chromosome()
+ var/static/list/chromosomes
+ if(!chromosomes)
+ chromosomes = list()
+ for(var/A in subtypesof(/obj/item/chromosome))
+ var/obj/item/chromosome/CM = A
+ if(!initial(CM.weight))
+ break
+ chromosomes[A] = initial(CM.weight)
+ return pickweight(chromosomes)
+
+
+/obj/item/chromosome/stabilizer
+ name = "stabilizer chromosome"
+ desc = "A chromosome that adjusts to the body to reduce genetic damage 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%."
+ 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%."
+ 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%."
+ icon_state = "energy"
+ energy_coeff = 0.5
+
+/obj/item/chromosome/reinforcer
+ name = "reinforcement chromosome"
+ desc = "Renders the mutation immune to mutadone."
+ icon_state = "reinforcer"
+ weight = 3
+
+/obj/item/chromosome/reinforcer/can_apply(datum/mutation/human/HM)
+ if(!HM || !(HM.can_chromosome == CHROMOSOME_NONE))
+ return FALSE
+ return !HM.mutadone_proof
+
+/obj/item/chromosome/reinforcer/apply(datum/mutation/human/HM)
+ HM.mutadone_proof = TRUE
+ ..()
\ No newline at end of file
diff --git a/code/game/objects/items/circuitboards/machine_circuitboards.dm b/code/game/objects/items/circuitboards/machine_circuitboards.dm
index 0496b282..dcf3cd12 100644
--- a/code/game/objects/items/circuitboards/machine_circuitboards.dm
+++ b/code/game/objects/items/circuitboards/machine_circuitboards.dm
@@ -65,7 +65,7 @@
build_path = /obj/machinery/dna_scannernew
req_components = list(
/obj/item/stock_parts/scanning_module = 1,
- /obj/item/stock_parts/manipulator = 1,
+ /obj/item/stock_parts/matter_bin = 1,
/obj/item/stock_parts/micro_laser = 1,
/obj/item/stack/sheet/glass = 1,
/obj/item/stack/cable_coil = 2)
diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm
index c3009a41..ff5c7b86 100644
--- a/code/game/objects/items/devices/scanners.dm
+++ b/code/game/objects/items/devices/scanners.dm
@@ -6,6 +6,8 @@ T-RAY
HEALTH ANALYZER
GAS ANALYZER
SLIME SCANNER
+NANITE SCANNER
+GENE SCANNER
*/
/obj/item/t_scanner
@@ -291,7 +293,8 @@ SLIME SCANNER
var/ldamage = H.return_liver_damage()
if(ldamage > 10)
msg += "\t[ldamage > 45 ? "Severe" : "Minor"] liver damage detected.\n"
-
+ if(advanced && H.has_dna())
+ to_chat(user, "\tGenetic Stability: [H.dna.stability]%.")
// Body part damage report
if(iscarbon(M) && mode == 1)
var/mob/living/carbon/C = M
@@ -764,3 +767,62 @@ SLIME SCANNER
var/response = SEND_SIGNAL(M, COMSIG_NANITE_SCAN, user, TRUE)
if(!response)
to_chat(user, "No nanites detected in the subject.")
+
+/obj/item/sequence_scanner
+ name = "genetic sequence scanner"
+ icon = 'icons/obj/device.dmi'
+ icon_state = "gene"
+ item_state = "healthanalyzer"
+ lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
+ desc = "A hand-held scanner able to swiftly scan someone for potential mutations. Hold near a DNA console to update from their database."
+ flags_1 = CONDUCT_1
+ item_flags = NOBLUDGEON
+ slot_flags = ITEM_SLOT_BELT
+ throwforce = 3
+ w_class = WEIGHT_CLASS_TINY
+ throw_speed = 3
+ throw_range = 7
+ materials = list(MAT_METAL=200)
+ var/list/discovered = list() //hit a dna console to update the scanners database
+
+/obj/item/sequence_scanner/attack(mob/living/M, mob/living/carbon/human/user)
+ user.visible_message("[user] has analyzed [M]'s genetic sequence.")
+
+ add_fingerprint(user)
+
+ gene_scan(M, user, src)
+
+/obj/item/sequence_scanner/afterattack(obj/O, mob/user, proximity)
+ . = ..()
+ if(!istype(O) || !proximity)
+ return
+
+ if(istype(O, /obj/machinery/computer/scan_consolenew))
+ var/obj/machinery/computer/scan_consolenew/C = O
+ if(C.stored_research)
+ to_chat(user, "[name] database updated.")
+ discovered = C.stored_research.discovered_mutations
+ else
+ to_chat(user,"No database to update from.")
+
+/proc/gene_scan(mob/living/carbon/C, mob/living/user, obj/item/sequence_scanner/G)
+ if(!iscarbon(C) || !C.has_dna())
+ return
+ to_chat(user, "[C.name]'s potential mutations.")
+ for(var/A in C.dna.mutation_index)
+ var/datum/mutation/human/HM = GET_INITIALIZED_MUTATION(A)
+ var/mut_name
+ if(G && (A in G.discovered))
+ mut_name = "[HM.name] ([HM.alias])"
+ else
+ mut_name = HM.alias
+ var/temp = GET_GENE_STRING(HM.type, C.dna)
+ var/display
+ for(var/i in 0 to length(temp) / DNA_MUTATION_BLOCKS-1)
+ if(i)
+ display += "-"
+ display += copytext(temp, 1 + i*DNA_MUTATION_BLOCKS, DNA_MUTATION_BLOCKS*(1+i) + 1)
+
+
+ to_chat(user, "- [mut_name] > [display]")
\ No newline at end of file
diff --git a/code/game/objects/items/dna_injector.dm b/code/game/objects/items/dna_injector.dm
index a03447c0..8849ac51 100644
--- a/code/game/objects/items/dna_injector.dm
+++ b/code/game/objects/items/dna_injector.dm
@@ -14,33 +14,25 @@
var/list/add_mutations = list()
var/list/remove_mutations = list()
- var/list/add_mutations_static = list()
- var/list/remove_mutations_static = list()
-
var/used = 0
/obj/item/dnainjector/attack_paw(mob/user)
return attack_hand(user)
-/obj/item/dnainjector/proc/prepare()
- for(var/mut_key in add_mutations_static)
- add_mutations.Add(GLOB.mutations_list[mut_key])
- for(var/mut_key in remove_mutations_static)
- remove_mutations.Add(GLOB.mutations_list[mut_key])
-
/obj/item/dnainjector/proc/inject(mob/living/carbon/M, mob/user)
- prepare()
-
if(M.has_dna() && !HAS_TRAIT(M, TRAIT_RADIMMUNE) && !HAS_TRAIT(M, TRAIT_NOCLONE))
M.radiation += rand(20/(damage_coeff ** 2),50/(damage_coeff ** 2))
var/log_msg = "[key_name(user)] injected [key_name(M)] with the [name]"
- for(var/datum/mutation/human/HM in remove_mutations)
- HM.force_lose(M)
- for(var/datum/mutation/human/HM in add_mutations)
- if(HM.name == RACEMUT)
+ for(var/HM in remove_mutations)
+ M.dna.remove_mutation(HM)
+ for(var/HM in add_mutations)
+ if(HM == RACEMUT)
message_admins("[ADMIN_LOOKUPFLW(user)] injected [key_name_admin(M)] with the [name] (MONKEY)")
log_msg += " (MONKEY)"
- HM.force_give(M)
+ if(M.dna.mutation_in_sequence(HM))
+ M.dna.activate_mutation(HM)
+ else
+ M.dna.add_mutation(HM, MUT_EXTRA)
if(fields)
if(fields["name"] && fields["UE"] && fields["blood_type"])
M.real_name = fields["name"]
@@ -90,123 +82,123 @@
/obj/item/dnainjector/antihulk
name = "\improper DNA injector (Anti-Hulk)"
desc = "Cures green skin."
- remove_mutations_static = list(HULK)
+ remove_mutations = list(HULK)
/obj/item/dnainjector/hulkmut
name = "\improper DNA injector (Hulk)"
desc = "This will make you big and strong, but give you a bad skin condition."
- add_mutations_static = list(HULK)
+ add_mutations = list(HULK)
/obj/item/dnainjector/xraymut
name = "\improper DNA injector (X-ray)"
desc = "Finally you can see what the Captain does."
- add_mutations_static = list(XRAY)
+ add_mutations = list(XRAY)
/obj/item/dnainjector/antixray
name = "\improper DNA injector (Anti-X-ray)"
desc = "It will make you see harder."
- remove_mutations_static = list(XRAY)
+ remove_mutations = list(XRAY)
/////////////////////////////////////
/obj/item/dnainjector/antiglasses
name = "\improper DNA injector (Anti-Glasses)"
desc = "Toss away those glasses!"
- remove_mutations_static = list(BADSIGHT)
+ remove_mutations = list(BADSIGHT)
/obj/item/dnainjector/glassesmut
name = "\improper DNA injector (Glasses)"
desc = "Will make you need dorkish glasses."
- add_mutations_static = list(BADSIGHT)
+ add_mutations = list(BADSIGHT)
/obj/item/dnainjector/epimut
name = "\improper DNA injector (Epi.)"
desc = "Shake shake shake the room!"
- add_mutations_static = list(EPILEPSY)
+ add_mutations = list(EPILEPSY)
/obj/item/dnainjector/antiepi
name = "\improper DNA injector (Anti-Epi.)"
desc = "Will fix you up from shaking the room."
- remove_mutations_static = list(EPILEPSY)
+ remove_mutations = list(EPILEPSY)
////////////////////////////////////
/obj/item/dnainjector/anticough
name = "\improper DNA injector (Anti-Cough)"
desc = "Will stop that awful noise."
- remove_mutations_static = list(COUGH)
+ remove_mutations = list(COUGH)
/obj/item/dnainjector/coughmut
name = "\improper DNA injector (Cough)"
desc = "Will bring forth a sound of horror from your throat."
- add_mutations_static = list(COUGH)
+ add_mutations = list(COUGH)
/obj/item/dnainjector/antidwarf
name = "\improper DNA injector (Anti-Dwarfism)"
desc = "Helps you grow big and strong."
- remove_mutations_static = list(DWARFISM)
+ remove_mutations = list(DWARFISM)
/obj/item/dnainjector/dwarf
name = "\improper DNA injector (Dwarfism)"
desc = "It's a small world after all."
- add_mutations_static = list(DWARFISM)
+ add_mutations = list(DWARFISM)
/obj/item/dnainjector/clumsymut
name = "\improper DNA injector (Clumsy)"
desc = "Makes clown minions."
- add_mutations_static = list(CLOWNMUT)
+ add_mutations = list(CLOWNMUT)
/obj/item/dnainjector/anticlumsy
name = "\improper DNA injector (Anti-Clumsy)"
desc = "Apply this for Security Clown."
- remove_mutations_static = list(CLOWNMUT)
+ remove_mutations = list(CLOWNMUT)
/obj/item/dnainjector/antitour
name = "\improper DNA injector (Anti-Tour.)"
desc = "Will cure Tourette's."
- remove_mutations_static = list(TOURETTES)
+ remove_mutations = list(TOURETTES)
/obj/item/dnainjector/tourmut
name = "\improper DNA injector (Tour.)"
desc = "Gives you a nasty case of Tourette's."
- add_mutations_static = list(TOURETTES)
+ add_mutations = list(TOURETTES)
/obj/item/dnainjector/stuttmut
name = "\improper DNA injector (Stutt.)"
desc = "Makes you s-s-stuttterrr."
- add_mutations_static = list(NERVOUS)
+ add_mutations = list(NERVOUS)
/obj/item/dnainjector/antistutt
name = "\improper DNA injector (Anti-Stutt.)"
desc = "Fixes that speaking impairment."
- remove_mutations_static = list(NERVOUS)
+ remove_mutations = list(NERVOUS)
/obj/item/dnainjector/antifire
name = "\improper DNA injector (Anti-Fire)"
desc = "Cures fire."
- remove_mutations_static = list(COLDRES)
+ remove_mutations = list(SPACEMUT)
/obj/item/dnainjector/firemut
name = "\improper DNA injector (Fire)"
desc = "Gives you fire."
- add_mutations_static = list(COLDRES)
+ add_mutations = list(SPACEMUT)
/obj/item/dnainjector/blindmut
name = "\improper DNA injector (Blind)"
desc = "Makes you not see anything."
- add_mutations_static = list(BLINDMUT)
+ add_mutations = list(BLINDMUT)
/obj/item/dnainjector/antiblind
name = "\improper DNA injector (Anti-Blind)"
desc = "IT'S A MIRACLE!!!"
- remove_mutations_static = list(BLINDMUT)
+ remove_mutations = list(BLINDMUT)
/obj/item/dnainjector/antitele
name = "\improper DNA injector (Anti-Tele.)"
desc = "Will make you not able to control your mind."
- remove_mutations_static = list(TK)
+ remove_mutations = list(TK)
/obj/item/dnainjector/telemut
name = "\improper DNA injector (Tele.)"
desc = "Super brain man!"
- add_mutations_static = list(TK)
+ add_mutations = list(TK)
/obj/item/dnainjector/telemut/darkbundle
name = "\improper DNA injector"
@@ -215,100 +207,171 @@
/obj/item/dnainjector/deafmut
name = "\improper DNA injector (Deaf)"
desc = "Sorry, what did you say?"
- add_mutations_static = list(DEAFMUT)
+ add_mutations = list(DEAFMUT)
/obj/item/dnainjector/antideaf
name = "\improper DNA injector (Anti-Deaf)"
desc = "Will make you hear once more."
- remove_mutations_static = list(DEAFMUT)
+ remove_mutations = list(DEAFMUT)
/obj/item/dnainjector/h2m
name = "\improper DNA injector (Human > Monkey)"
desc = "Will make you a flea bag."
- add_mutations_static = list(RACEMUT)
+ add_mutations = list(RACEMUT)
/obj/item/dnainjector/m2h
name = "\improper DNA injector (Monkey > Human)"
desc = "Will make you...less hairy."
- remove_mutations_static = list(RACEMUT)
+ remove_mutations = list(RACEMUT)
/obj/item/dnainjector/antichameleon
name = "\improper DNA injector (Anti-Chameleon)"
- remove_mutations_static = list(CHAMELEON)
+ remove_mutations = list(CHAMELEON)
/obj/item/dnainjector/chameleonmut
name = "\improper DNA injector (Chameleon)"
- add_mutations_static = list(CHAMELEON)
+ add_mutations = list(CHAMELEON)
/obj/item/dnainjector/antiwacky
name = "\improper DNA injector (Anti-Wacky)"
- remove_mutations_static = list(WACKY)
+ remove_mutations = list(WACKY)
/obj/item/dnainjector/wackymut
name = "\improper DNA injector (Wacky)"
- add_mutations_static = list(WACKY)
+ add_mutations = list(WACKY)
/obj/item/dnainjector/antimute
name = "\improper DNA injector (Anti-Mute)"
- remove_mutations_static = list(MUT_MUTE)
+ remove_mutations = list(MUT_MUTE)
/obj/item/dnainjector/mutemut
name = "\improper DNA injector (Mute)"
- add_mutations_static = list(MUT_MUTE)
+ add_mutations = list(MUT_MUTE)
/obj/item/dnainjector/antismile
name = "\improper DNA injector (Anti-Smile)"
- remove_mutations_static = list(SMILE)
+ remove_mutations = list(SMILE)
/obj/item/dnainjector/smilemut
name = "\improper DNA injector (Smile)"
- add_mutations_static = list(SMILE)
+ add_mutations = list(SMILE)
/obj/item/dnainjector/unintelligiblemut
name = "\improper DNA injector (Unintelligible)"
- add_mutations_static = list(UNINTELLIGIBLE)
+ add_mutations = list(UNINTELLIGIBLE)
/obj/item/dnainjector/antiunintelligible
name = "\improper DNA injector (Anti-Unintelligible)"
- remove_mutations_static = list(UNINTELLIGIBLE)
+ remove_mutations = list(UNINTELLIGIBLE)
/obj/item/dnainjector/swedishmut
name = "\improper DNA injector (Swedish)"
- add_mutations_static = list(SWEDISH)
+ add_mutations = list(SWEDISH)
/obj/item/dnainjector/antiswedish
name = "\improper DNA injector (Anti-Swedish)"
- remove_mutations_static = list(SWEDISH)
+ remove_mutations = list(SWEDISH)
/obj/item/dnainjector/chavmut
name = "\improper DNA injector (Chav)"
- add_mutations_static = list(CHAV)
+ add_mutations = list(CHAV)
/obj/item/dnainjector/antichav
name = "\improper DNA injector (Anti-Chav)"
- remove_mutations_static = list(CHAV)
+ remove_mutations = list(CHAV)
/obj/item/dnainjector/elvismut
name = "\improper DNA injector (Elvis)"
- add_mutations_static = list(ELVIS)
+ add_mutations = list(ELVIS)
/obj/item/dnainjector/antielvis
name = "\improper DNA injector (Anti-Elvis)"
- remove_mutations_static = list(ELVIS)
+ remove_mutations = list(ELVIS)
/obj/item/dnainjector/lasereyesmut
name = "\improper DNA injector (Laser Eyes)"
- add_mutations_static = list(LASEREYES)
+ add_mutations = list(LASEREYES)
/obj/item/dnainjector/antilasereyes
name = "\improper DNA injector (Anti-Laser Eyes)"
- remove_mutations_static = list(LASEREYES)
+ remove_mutations = list(LASEREYES)
+
+/obj/item/dnainjector/thermalmut
+ name = "\improper DNA injector (Thermal Vision)"
+ add_mutations = list(THERMAL)
+
+/obj/item/dnainjector/antithermal
+ name = "\improper DNA injector (Anti-Thermal Vision)"
+ remove_mutations = list(THERMAL)
+
+/obj/item/dnainjector/telepathymut
+ name = "\improper DNA injector (Telepathy)"
+ add_mutations = list(TELEPATHY)
+
+/obj/item/dnainjector/antitelepathy
+ name = "\improper DNA injector (Anti-Telepathy)"
+ remove_mutations = list(TELEPATHY)
+
+/obj/item/dnainjector/voidmut
+ name = "\improper DNA injector (Void Magnet)"
+ add_mutations = list(VOID)
+
+/obj/item/dnainjector/antivoid
+ name = "\improper DNA injector (Anti-Void Magnet)"
+ remove_mutations = list(VOID)
+
+/obj/item/dnainjector/firebreathmut
+ name = "\improper DNA injector (Firebreath)"
+ add_mutations = list(FIREBREATH)
+
+/obj/item/dnainjector/antifirebreath
+ name = "\improper DNA injector (Anti-Firebreath)"
+ remove_mutations = list(FIREBREATH)
+
+/obj/item/dnainjector/insulatedmut
+ name = "\improper DNA injector (Insulated)"
+ add_mutations = list(INSULATED)
+
+/obj/item/dnainjector/antiinsulated
+ name = "\improper DNA injector (Anti-Insulated)"
+ remove_mutations = list(INSULATED)
+
+/obj/item/dnainjector/shocktouchmut
+ name = "\improper DNA injector (Shock Touch)"
+ add_mutations = list(SHOCKTOUCH)
+
+/obj/item/dnainjector/antishocktouch
+ name = "\improper DNA injector (Anti-Shock Touch)"
+ remove_mutations = list(SHOCKTOUCH)
+
+/obj/item/dnainjector/antenna
+ name = "\improper DNA injector (Antenna)"
+ add_mutations = list(ANTENNA)
+
+/obj/item/dnainjector/antiantenna
+ name = "\improper DNA injector (Anti-Antenna)"
+ remove_mutations = list(ANTENNA)
+
+/obj/item/dnainjector/paranoia
+ name = "\improper DNA injector (Paranoia)"
+ add_mutations = list(PARANOIA)
+
+/obj/item/dnainjector/antiparanoia
+ name = "\improper DNA injector (Anti-Paranoia)"
+ remove_mutations = list(PARANOIA)
+
+/obj/item/dnainjector/mindread
+ name = "\improper DNA injector (Mindread)"
+ add_mutations = list(MINDREAD)
+
+/obj/item/dnainjector/antimindread
+ name = "\improper DNA injector (Anti-Mindread)"
+ remove_mutations = list(MINDREAD)
/obj/item/dnainjector/timed
var/duration = 600
/obj/item/dnainjector/timed/inject(mob/living/carbon/M, mob/user)
- prepare()
if(M.stat == DEAD) //prevents dead people from having their DNA changed
to_chat(user, "You can't modify [M]'s DNA while [M.p_theyre()] dead.")
return FALSE
@@ -317,23 +380,22 @@
M.radiation += rand(20/(damage_coeff ** 2),50/(damage_coeff ** 2))
var/log_msg = "[key_name(user)] injected [key_name(M)] with the [name]"
var/endtime = world.time+duration
- for(var/datum/mutation/human/HM in remove_mutations)
- if(HM.name == RACEMUT)
+ for(var/mutation in remove_mutations)
+ if(mutation == RACEMUT)
if(ishuman(M))
continue
- M = HM.force_lose(M)
+ M = M.dna.remove_mutation(mutation)
else
- HM.force_lose(M)
- for(var/datum/mutation/human/HM in add_mutations)
- if((HM in M.dna.mutations) && !(M.dna.temporary_mutations[HM.name]))
+ M.dna.remove_mutation(mutation)
+ for(var/mutation in add_mutations)
+ if(M.dna.get_mutation(mutation))
continue //Skip permanent mutations we already have.
- if(HM.name == RACEMUT && ishuman(M))
+ if(mutation == RACEMUT && ishuman(M))
message_admins("[ADMIN_LOOKUPFLW(user)] injected [key_name_admin(M)] with the [name] (MONKEY)")
log_msg += " (MONKEY)"
- M = HM.force_give(M)
+ M = M.dna.add_mutation(mutation, MUT_OTHER, endtime)
else
- HM.force_give(M)
- M.dna.temporary_mutations[HM.name] = endtime
+ M.dna.add_mutation(mutation, MUT_OTHER, endtime)
if(fields)
if(fields["name"] && fields["UE"] && fields["blood_type"])
if(!M.dna.previous["name"])
@@ -361,9 +423,41 @@
/obj/item/dnainjector/timed/hulk
name = "\improper DNA injector (Hulk)"
desc = "This will make you big and strong, but give you a bad skin condition."
- add_mutations_static = list(HULK)
+ add_mutations = list(HULK)
/obj/item/dnainjector/timed/h2m
name = "\improper DNA injector (Human > Monkey)"
desc = "Will make you a flea bag."
- add_mutations_static = list(RACEMUT)
+ add_mutations = list(RACEMUT)
+
+/obj/item/dnainjector/activator
+ name = "\improper DNA activator"
+ desc = "Activates the current mutation on injection, if the subject has it."
+ var/doitanyway = FALSE
+ var/research = FALSE //Set to true to get expended and filled injectors for chromosomes
+ var/filled = FALSE
+
+/obj/item/dnainjector/activator/inject(mob/living/carbon/M, mob/user)
+ if(M.has_dna() && !HAS_TRAIT(M, TRAIT_RADIMMUNE) && !HAS_TRAIT(M,TRAIT_NOCLONE))
+ M.radiation += rand(20/(damage_coeff ** 2),50/(damage_coeff ** 2))
+ var/log_msg = "[key_name(user)] injected [key_name(M)] with the [name]"
+ for(var/mutation in add_mutations)
+ var/datum/mutation/human/HM = mutation
+ if(istype(HM, /datum/mutation/human))
+ mutation = HM.type
+ if(!M.dna.activate_mutation(HM))
+ if(!doitanyway)
+ log_msg += "(FAILED)"
+ else
+ M.dna.add_mutation(HM, MUT_EXTRA)
+ name = "expended [name]"
+ else if(research && M.client)
+ filled = TRUE
+ name = "filled [name]"
+ else
+ name = "expended [name]"
+ log_msg += "([mutation])"
+ log_attack("[log_msg] [loc_name(user)]")
+ return TRUE
+ return FALSE
+
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index c0853068..e1f82e4a 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -208,6 +208,7 @@ GLOBAL_LIST_INIT(wood_recipes, list ( \
new/datum/stack_recipe("apiary", /obj/structure/beebox, 40, time = 50),\
new/datum/stack_recipe("tiki mask", /obj/item/clothing/mask/gas/tiki_mask, 2), \
new/datum/stack_recipe("honey frame", /obj/item/honey_frame, 5, time = 10),\
+ new/datum/stack_recipe("cross", /obj/structure/kitchenspike/cross, 10, time = 10),\
new/datum/stack_recipe("ore box", /obj/structure/ore_box, 4, time = 50, one_per_turf = TRUE, on_floor = TRUE),\
new/datum/stack_recipe("wooden crate", /obj/structure/closet/crate/wooden, 6, time = 50, one_per_turf = TRUE, on_floor = TRUE),\
new/datum/stack_recipe("baseball bat", /obj/item/melee/baseball_bat, 5, time = 15),\
diff --git a/code/game/objects/items/storage/bags.dm b/code/game/objects/items/storage/bags.dm
index 7ab85084..c95e93ec 100644
--- a/code/game/objects/items/storage/bags.dm
+++ b/code/game/objects/items/storage/bags.dm
@@ -397,3 +397,24 @@
GET_COMPONENT(STR, /datum/component/storage)
STR.max_combined_w_class = INFINITY
STR.max_items = 100
+
+/*
+ * Construction bag (for engineering, holds stock parts and electronics)
+ */
+
+/obj/item/storage/bag/construction
+ name = "construction bag"
+ icon = 'icons/obj/tools.dmi'
+ icon_state = "construction_bag"
+ desc = "A bag for storing small construction components."
+ w_class = WEIGHT_CLASS_TINY
+ resistance_flags = FLAMMABLE
+
+/obj/item/storage/bag/construction/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_combined_w_class = 100
+ STR.max_items = 50
+ STR.max_w_class = WEIGHT_CLASS_SMALL
+ STR.insert_preposition = "in"
+ STR.can_hold = typecacheof(list(/obj/item/stack/ore/bluespace_crystal, /obj/item/assembly, /obj/item/stock_parts, /obj/item/reagent_containers/glass/beaker, /obj/item/stack/cable_coil, /obj/item/circuitboard, /obj/item/electronics))
\ No newline at end of file
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm b/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm
index e10e3b20..96afe90b 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm
@@ -80,6 +80,7 @@
new /obj/item/clothing/mask/gas(src)
new /obj/item/clothing/glasses/meson/engine(src)
new /obj/item/storage/box/emptysandbags(src)
+ new /obj/item/storage/bag/construction(src)
/obj/structure/closet/secure_closet/atmospherics
diff --git a/code/game/objects/structures/kitchen_spike.dm b/code/game/objects/structures/kitchen_spike.dm
index 21622519..a717f7f1 100644
--- a/code/game/objects/structures/kitchen_spike.dm
+++ b/code/game/objects/structures/kitchen_spike.dm
@@ -153,4 +153,91 @@
new /obj/item/stack/rods(loc, 4)
qdel(src)
-#undef VIABLE_MOB_CHECK
+/obj/structure/kitchenspike/cross
+ name = "cross"
+ icon = 'icons/obj/cross.dmi'
+ icon_state = "cross"
+ desc = "Degenerates like you belong on one of these."
+ anchored = TRUE
+ bound_height = 64
+
+/obj/structure/kitchenspike/cross/crowbar_act(mob/living/user, obj/item/I)
+ if(has_buckled_mobs())
+ to_chat(user, "You can't do that while something's on the cross!")
+ return FALSE
+ if(I.use_tool(src, user, 20, volume=100))
+ deconstruct()
+ return TRUE
+
+/obj/structure/kitchenspike/cross/deconstruct()
+ new /obj/item/stack/sheet/mineral/wood(src.loc, 10)
+ qdel(src)
+
+/obj/structure/kitchenspike/cross/attack_hand(mob/user)
+ if(VIABLE_MOB_CHECK(user.pulling) && user.a_intent == INTENT_GRAB && !has_buckled_mobs())
+ var/mob/living/L = user.pulling
+ if(do_mob(user, src, 120))
+ if(has_buckled_mobs()) //to prevent spam/queing up attacks
+ return
+ if(L.buckled)
+ return
+ if(user.pulling != L)
+ return
+ playsound(src.loc, "sound/effects/crossed.ogg", 20, 1) // thanks hippie
+ L.visible_message("[user] ties [L] to the cross!", "[user] ties you to the cross!")
+ L.forceMove(drop_location())
+ L.setDir(2)
+ buckle_mob(L, force=1)
+ L.pixel_y = 26
+ L.overlays += image('icons/obj/cross.dmi', "lashing")
+ to_chat(user, "You can't use that on the cross!")
+ else if (has_buckled_mobs())
+ for(var/mob/living/L in buckled_mobs)
+ user_unbuckle_mob(L, user)
+ else
+ ..()
+
+/obj/structure/kitchenspike/cross/user_unbuckle_mob(mob/living/buckled_mob, mob/living/carbon/human/user)
+ if(buckled_mob && buckled_mob.buckled == src)
+ var/mob/living/M = buckled_mob
+ if(M != user)
+ M.visible_message(\
+ "[user] tries to pull [M] free of the [src]!",\
+ "[user.name] is trying to pull you off the [src], opening up fresh wounds!",\
+ "You hear rope being unraveled.")
+ if(!do_after(user, 300, target = src))
+ if(M && M.buckled)
+ M.visible_message(\
+ "[user] fails to free [M]!",\
+ "[user] fails to pull you off of the [src].")
+ return
+
+ else
+ M.visible_message(\
+ "[M] struggles to break free from the [src]!",\
+ "You struggle to break free from the [src], exacerbating your wounds! (Stay still for two minutes.)",\
+ "You hear violent scraping and struggling.")
+ M.adjustBruteLoss(20)
+ if(!do_after(M, 1200, target = src))
+ if(M && M.buckled)
+ to_chat(M, "You fail to free yourself!")
+ return
+ if(!M.buckled)
+ return
+ untie_mob(M)
+
+/obj/structure/kitchenspike/cross/proc/untie_mob(mob/living/M)
+ M.pixel_y = M.get_standard_pixel_y_offset()
+ M.adjustBruteLoss(15)
+ src.visible_message(text("[M] falls free of [src]!"))
+ unbuckle_mob(M,force=1)
+ M.emote("collapse")
+ M.overlays -= image('icons/obj/cross.dmi', "lashing")
+
+/obj/structure/kitchenspike/cross/Destroy()
+ if(has_buckled_mobs())
+ for(var/mob/living/L in buckled_mobs)
+ untie_mob(L)
+ return ..()
+
+#undef VIABLE_MOB_CHECK
\ No newline at end of file
diff --git a/code/modules/antagonists/changeling/powers/chameleon_skin.dm b/code/modules/antagonists/changeling/powers/chameleon_skin.dm
index b7545353..03f3aab4 100644
--- a/code/modules/antagonists/changeling/powers/chameleon_skin.dm
+++ b/code/modules/antagonists/changeling/powers/chameleon_skin.dm
@@ -13,17 +13,14 @@
var/mob/living/carbon/human/H = user //SHOULD always be human, because req_human = 1
if(!istype(H)) // req_human could be done in can_sting stuff.
return
- var/datum/mutation/human/HM = GLOB.mutations_list[CHAMELEON]
- if(HM in H.dna.mutations)
- HM.force_lose(H)
+ if(H.dna.get_mutation(CHAMELEON))
+ H.dna.remove_mutation(CHAMELEON)
else
- HM.force_give(H)
+ H.dna.add_mutation(CHAMELEON)
return TRUE
/obj/effect/proc_holder/changeling/chameleon_skin/on_refund(mob/user)
action.Remove(user)
if(user.has_dna())
var/mob/living/carbon/C = user
- var/datum/mutation/human/HM = GLOB.mutations_list[CHAMELEON]
- if(HM in C.dna.mutations)
- HM.force_lose(C)
\ No newline at end of file
+ C.dna.remove_mutation(CHAMELEON)
\ No newline at end of file
diff --git a/code/modules/antagonists/revenant/revenant.dm b/code/modules/antagonists/revenant/revenant.dm
index 87794993..8da0546b 100644
--- a/code/modules/antagonists/revenant/revenant.dm
+++ b/code/modules/antagonists/revenant/revenant.dm
@@ -69,7 +69,7 @@
/mob/living/simple_animal/revenant/Initialize(mapload)
. = ..()
AddSpell(new /obj/effect/proc_holder/spell/targeted/night_vision/revenant(null))
- AddSpell(new /obj/effect/proc_holder/spell/targeted/revenant_transmit(null))
+ AddSpell(new /obj/effect/proc_holder/spell/targeted/telepathy/revenant(null))
AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/revenant/defile(null))
AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/revenant/overload(null))
AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/revenant/blight(null))
diff --git a/code/modules/antagonists/revenant/revenant_abilities.dm b/code/modules/antagonists/revenant/revenant_abilities.dm
index 112a31f4..0cbe3102 100644
--- a/code/modules/antagonists/revenant/revenant_abilities.dm
+++ b/code/modules/antagonists/revenant/revenant_abilities.dm
@@ -104,35 +104,14 @@
action_background_icon_state = "bg_revenant"
//Transmit: the revemant's only direct way to communicate. Sends a single message silently to a single mob
-/obj/effect/proc_holder/spell/targeted/revenant_transmit
- name = "Transmit"
- desc = "Telepathically transmits a message to the target."
+/obj/effect/proc_holder/spell/targeted/telepathy/revenant
+ name = "Revenant Transmit"
panel = "Revenant Abilities"
- charge_max = 0
- clothes_req = 0
- range = 7
- include_user = 0
action_icon = 'icons/mob/actions/actions_revenant.dmi'
action_icon_state = "r_transmit"
action_background_icon_state = "bg_revenant"
-
-/obj/effect/proc_holder/spell/targeted/revenant_transmit/cast(list/targets, mob/living/simple_animal/revenant/user = usr)
- for(var/mob/living/M in targets)
- var/msg = stripped_input(usr, "What do you wish to tell [M]?", null, "")
- if(!msg)
- charge_counter = charge_max
- return
- log_directed_talk(user, M, msg, LOG_SAY, "revenant whisper")
- to_chat(user, "You transmit to [M]: [msg]")
- if(!M.anti_magic_check(FALSE, TRUE)) //hear no evil
- to_chat(M, "You hear something behind you talking... [msg]")
- for(var/ded in GLOB.dead_mob_list)
- if(!isobserver(ded))
- continue
- var/follow_rev = FOLLOW_LINK(ded, user)
- var/follow_whispee = FOLLOW_LINK(ded, M)
- to_chat(ded, "[follow_rev] [user] Revenant Transmit: \"[msg]\" to [follow_whispee] [M]")
-
+ notice = "revennotice"
+ boldnotice = "revenboldnotice"
/obj/effect/proc_holder/spell/aoe_turf/revenant
diff --git a/code/modules/antagonists/wishgranter/wishgranter.dm b/code/modules/antagonists/wishgranter/wishgranter.dm
index 318de51e..196ce955 100644
--- a/code/modules/antagonists/wishgranter/wishgranter.dm
+++ b/code/modules/antagonists/wishgranter/wishgranter.dm
@@ -26,5 +26,5 @@
return
H.dna.add_mutation(HULK)
H.dna.add_mutation(XRAY)
- H.dna.add_mutation(COLDRES)
+ H.dna.add_mutation(SPACEMUT)
H.dna.add_mutation(TK)
\ No newline at end of file
diff --git a/code/modules/awaymissions/mission_code/wildwest.dm b/code/modules/awaymissions/mission_code/wildwest.dm
index ac1a4077..505b9a1e 100644
--- a/code/modules/awaymissions/mission_code/wildwest.dm
+++ b/code/modules/awaymissions/mission_code/wildwest.dm
@@ -94,7 +94,7 @@
to_chat(user, "Your wish is granted, but at a terrible cost...")
to_chat(user, "The Wish Granter punishes you for your selfishness, claiming your soul and warping your body to match the darkness in your heart.")
user.dna.add_mutation(LASEREYES)
- user.dna.add_mutation(COLDRES)
+ user.dna.add_mutation(SPACEMUT)
user.dna.add_mutation(XRAY)
user.set_species(/datum/species/shadow)
if("Wealth")
diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm
index 0ff87c03..0b83304f 100644
--- a/code/modules/client/asset_cache.dm
+++ b/code/modules/client/asset_cache.dm
@@ -677,3 +677,11 @@ GLOBAL_LIST_EMPTY(asset_datums)
Insert(initial(D.id), I)
return ..()
+
+
+/datum/asset/simple/genetics
+ assets = list(
+ "dna_discovered.gif" = 'html/dna_discovered.gif',
+ "dna_undiscovered.gif" = 'html/dna_undiscovered.gif',
+ "dna_extra.gif" = 'html/dna_extra.gif'
+)
\ No newline at end of file
diff --git a/code/modules/events/disease_outbreak.dm b/code/modules/events/disease_outbreak.dm
index 96b5c8e2..4800b61f 100644
--- a/code/modules/events/disease_outbreak.dm
+++ b/code/modules/events/disease_outbreak.dm
@@ -57,7 +57,7 @@
var/datum/disease/dnaspread/DS = D
DS.strain_data["name"] = H.real_name
DS.strain_data["UI"] = H.dna.uni_identity
- DS.strain_data["SE"] = H.dna.struc_enzymes
+ DS.strain_data["SE"] = H.dna.mutation_index
else
D = new virus_type()
else
diff --git a/code/modules/jobs/job_types/medical.dm b/code/modules/jobs/job_types/medical.dm
index 9373b5ad..d57615d2 100644
--- a/code/modules/jobs/job_types/medical.dm
+++ b/code/modules/jobs/job_types/medical.dm
@@ -164,6 +164,7 @@ Geneticist
shoes = /obj/item/clothing/shoes/sneakers/white
suit = /obj/item/clothing/suit/toggle/labcoat/genetics
suit_store = /obj/item/flashlight/pen
+ l_pocket = /obj/item/sequence_scanner
backpack = /obj/item/storage/backpack/genetics
satchel = /obj/item/storage/backpack/satchel/gen
diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm
index f12b6b7e..a25ce336 100644
--- a/code/modules/mining/lavaland/necropolis_chests.dm
+++ b/code/modules/mining/lavaland/necropolis_chests.dm
@@ -453,7 +453,7 @@
/obj/item/immortality_talisman/Initialize()
. = ..()
- AddComponent(/datum/component/anti_magic, TRUE, TRUE)
+ AddComponent(/datum/component/anti_magic, TRUE, TRUE, TRUE)
/datum/action/item_action/immortality
name = "Immortality"
@@ -462,30 +462,45 @@
if(cooldown < world.time)
SSblackbox.record_feedback("amount", "immortality_talisman_uses", 1)
cooldown = world.time + 600
- user.visible_message("[user] vanishes from reality, leaving a hole in [user.p_their()] place!")
- var/obj/effect/immortality_talisman/Z = new(get_turf(src.loc))
- Z.name = "hole in reality"
- Z.desc = "It's shaped an awful lot like [user.name]."
- Z.setDir(user.dir)
- user.forceMove(Z)
- user.notransform = 1
- user.status_flags |= GODMODE
- addtimer(CALLBACK(src, .proc/return_to_reality, user, Z), 100)
+ new /obj/effect/immortality_talisman(get_turf(user), user)
else
to_chat(user, "[src] is not ready yet!")
-/obj/item/immortality_talisman/proc/return_to_reality(mob/user, obj/effect/immortality_talisman/Z)
- user.status_flags &= ~GODMODE
- user.notransform = 0
- user.forceMove(get_turf(Z))
- user.visible_message("[user] pops back into reality!")
- Z.can_destroy = TRUE
- qdel(Z)
-
/obj/effect/immortality_talisman
+ name = "hole in reality"
+ desc = "It's shaped an awful lot like a person."
icon_state = "blank"
icon = 'icons/effects/effects.dmi'
- var/can_destroy = FALSE
+ var/vanish_description = "vanishes from reality"
+ var/can_destroy = TRUE
+
+/obj/effect/immortality_talisman/Initialize(mapload, mob/new_user)
+ . = ..()
+ if(new_user)
+ vanish(new_user)
+
+/obj/effect/immortality_talisman/proc/vanish(mob/user)
+ user.visible_message("[user] [vanish_description], leaving a hole in [user.p_their()] place!")
+
+ desc = "It's shaped an awful lot like [user.name]."
+ setDir(user.dir)
+
+ user.forceMove(src)
+ user.notransform = TRUE
+ user.status_flags |= GODMODE
+
+ can_destroy = FALSE
+
+ addtimer(CALLBACK(src, .proc/unvanish, user), 10 SECONDS)
+
+/obj/effect/immortality_talisman/proc/unvanish(mob/user)
+ user.status_flags &= ~GODMODE
+ user.notransform = FALSE
+ user.forceMove(get_turf(src))
+
+ user.visible_message("[user] pops back into reality!")
+ can_destroy = TRUE
+ qdel(src)
/obj/effect/immortality_talisman/attackby()
return
@@ -502,6 +517,10 @@
else
. = ..()
+/obj/effect/immortality_talisman/void
+ vanish_description = "is dragged into the void"
+
+
//Shared Bag
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index a118dc78..89a7ec25 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -603,6 +603,14 @@
sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS)
see_in_dark = max(see_in_dark, 8)
+ if(HAS_TRAIT(src, TRAIT_THERMAL_VISION))
+ sight |= (SEE_MOBS)
+ lighting_alpha = min(lighting_alpha, LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE)
+
+ if(HAS_TRAIT(src, TRAIT_XRAY_VISION))
+ sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS)
+ see_in_dark = max(see_in_dark, 8)
+
if(see_override)
see_invisible = see_override
. = ..()
diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm
index 911c1111..a4a2aba2 100644
--- a/code/modules/mob/living/carbon/human/human_defines.dm
+++ b/code/modules/mob/living/carbon/human/human_defines.dm
@@ -25,11 +25,11 @@
var/age = 30 //Player's age
var/underwear = "Nude" //Which underwear the player wants
- var/undie_color = "#FFFFFF"
+ var/undie_color = "FFFFFF"
var/undershirt = "Nude" //Which undershirt the player wants
- var/shirt_color = "#FFFFFF"
+ var/shirt_color = "FFFFFF"
var/socks = "Nude" //Which socks the player wants
- var/socks_color = "#FFFFFF"
+ var/socks_color = "FFFFFF"
var/backbag = DBACKPACK //Which backpack type the player has chosen.
var/jumpsuit_style = PREF_SUIT //suit/skirt
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index 33b419de..b79979e2 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -44,6 +44,7 @@ GLOBAL_LIST_EMPTY(roundstart_races)
var/siemens_coeff = 1 //base electrocution coefficient
var/damage_overlay_type = "human" //what kind of damage overlays (if any) appear on our species when wounded?
var/fixed_mut_color = "" //to use MUTCOLOR with a fixed color that's independent of dna.feature["mcolor"]
+ var/inert_mutation = DWARFISM //special mutation that can be found in the genepool. Dont leave empty or changing species will be a headache
var/list/special_step_sounds //Sounds to override barefeet walkng
var/grab_sound //Special sound for grabbing
@@ -317,6 +318,14 @@ GLOBAL_LIST_EMPTY(roundstart_races)
for(var/X in inherent_traits)
REMOVE_TRAIT(C, X, SPECIES_TRAIT)
+ //If their inert mutation is not the same, swap it out
+ if((inert_mutation != new_species.inert_mutation) && LAZYLEN(C.dna.mutation_index) && (inert_mutation in C.dna.mutation_index))
+ C.dna.remove_mutation(inert_mutation)
+ //keep it at the right spot, so we can't have people taking shortcuts
+ var/location = C.dna.mutation_index.Find(inert_mutation)
+ C.dna.mutation_index[location] = new_species.inert_mutation
+ C.dna.mutation_index[new_species.inert_mutation] = create_sequence(new_species.inert_mutation)
+
SEND_SIGNAL(C, COMSIG_SPECIES_LOSS, src)
/datum/species/proc/handle_hair(mob/living/carbon/human/H, forced_colour)
@@ -529,7 +538,7 @@ GLOBAL_LIST_EMPTY(roundstart_races)
var/digilegs = (DIGITIGRADE in species_traits) ? "_d" : ""
var/mutable_appearance/MA = mutable_appearance(S.icon, "[S.icon_state][digilegs]", -BODY_LAYER)
if(UNDIE_COLORABLE(S))
- MA.color = "[H.socks_color]"
+ MA.color = "#[H.socks_color]"
standing += MA
if(standing.len)
@@ -1270,7 +1279,7 @@ GLOBAL_LIST_EMPTY(roundstart_races)
if(radiation > RAD_MOB_MUTATE)
if(prob(1))
to_chat(H, "You mutate!")
- H.randmutb()
+ H.easy_randmut(NEGATIVE+MINOR_NEGATIVE)
H.emote("gasp")
H.domutcheck()
diff --git a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
index 9b2e2ad5..8d448592 100644
--- a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
@@ -20,6 +20,7 @@
exotic_bloodtype = "L"
disliked_food = GRAIN | DAIRY
liked_food = GROSS | MEAT
+ inert_mutation = FIREBREATH
/datum/species/lizard/after_equip_job(datum/job/J, mob/living/carbon/human/H)
H.grant_language(/datum/language/draconic)
diff --git a/code/modules/mob/living/carbon/human/species_types/podpeople.dm b/code/modules/mob/living/carbon/human/species_types/podpeople.dm
index 0da4073f..fbe18a6c 100644
--- a/code/modules/mob/living/carbon/human/species_types/podpeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/podpeople.dm
@@ -60,9 +60,9 @@
H.Knockdown(100)
H.visible_message("[H] writhes in pain as [H.p_their()] vacuoles boil.", "You writhe in pain as your vacuoles boil!", "You hear the crunching of leaves.")
if(prob(80))
- H.randmutb()
+ H.easy_randmut(NEGATIVE+MINOR_NEGATIVE)
else
- H.randmutg()
+ H.easy_randmut(POSITIVE)
H.domutcheck()
else
H.adjustFireLoss(rand(5,15))
diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm
index de542754..1c2234bb 100644
--- a/code/modules/mob/living/carbon/life.dm
+++ b/code/modules/mob/living/carbon/life.dm
@@ -419,7 +419,6 @@
/mob/living/carbon/handle_mutations_and_radiation()
if(dna && dna.temporary_mutations.len)
- var/datum/mutation/human/HM
for(var/mut in dna.temporary_mutations)
if(dna.temporary_mutations[mut] < world.time)
if(mut == UI_CHANGED)
@@ -442,9 +441,9 @@
dna.previous.Remove("blood_type")
dna.temporary_mutations.Remove(mut)
continue
- HM = GLOB.mutations_list[mut]
- HM.force_lose(src)
- dna.temporary_mutations.Remove(mut)
+ for(var/datum/mutation/human/HM in dna.mutations)
+ if(HM && HM.timed)
+ dna.remove_mutation(HM.type)
radiation -= min(radiation, RAD_LOSS_PER_TICK)
if(radiation > RAD_MOB_SAFE)
diff --git a/code/modules/mob/living/carbon/monkey/life.dm b/code/modules/mob/living/carbon/monkey/life.dm
index e0386bcd..be7d90c1 100644
--- a/code/modules/mob/living/carbon/monkey/life.dm
+++ b/code/modules/mob/living/carbon/monkey/life.dm
@@ -38,7 +38,7 @@
if(radiation > RAD_MOB_MUTATE)
if(prob(1))
to_chat(src, "You mutate!")
- randmutb()
+ easy_randmut(NEGATIVE+MINOR_NEGATIVE)
emote("gasp")
domutcheck()
diff --git a/code/modules/mob/living/simple_animal/slime/life.dm b/code/modules/mob/living/simple_animal/slime/life.dm
index 07985215..29b46893 100644
--- a/code/modules/mob/living/simple_animal/slime/life.dm
+++ b/code/modules/mob/living/simple_animal/slime/life.dm
@@ -69,7 +69,7 @@
if(Target.Adjacent(src))
Target.attack_slime(src)
- return
+ break
if(!Target.lying && prob(80))
if(Target.client && Target.health >= 20)
diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm
index e6f79198..65947711 100644
--- a/code/modules/mob/transform_procs.dm
+++ b/code/modules/mob/transform_procs.dm
@@ -50,9 +50,8 @@
O.updateappearance(icon_update=0)
if(tr_flags & TR_KEEPSE)
- O.dna.struc_enzymes = dna.struc_enzymes
- var/datum/mutation/human/race/R = GLOB.mutations_list[RACEMUT]
- O.dna.struc_enzymes = R.set_se(O.dna.struc_enzymes, on=1)//we don't want to keep the race block inactive
+ O.dna.mutation_index = dna.mutation_index
+ O.dna.set_se(1, GET_INITIALIZED_MUTATION(RACEMUT))
if(suiciding)
O.suiciding = suiciding
@@ -210,9 +209,8 @@
O.name = O.real_name
if(tr_flags & TR_KEEPSE)
- O.dna.struc_enzymes = dna.struc_enzymes
- var/datum/mutation/human/race/R = GLOB.mutations_list[RACEMUT]
- O.dna.struc_enzymes = R.set_se(O.dna.struc_enzymes, on=0)//we don't want to keep the race block active
+ O.dna.mutation_index = dna.mutation_index
+ O.dna.set_se(0, GET_INITIALIZED_MUTATION(RACEMUT))
O.domutcheck()
if(suiciding)
diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
index 0d7ca6e3..d6bf0c13 100644
--- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
@@ -913,7 +913,7 @@
/datum/reagent/medicine/mutadone/on_mob_life(mob/living/carbon/M)
M.jitteriness = 0
if(M.has_dna())
- M.dna.remove_all_mutations()
+ M.dna.remove_all_mutations(mutadone = TRUE)
if(!QDELETED(M)) //We were a monkey, now a human
..()
diff --git a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
index 2b50ecc0..c38bbfed 100644
--- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
@@ -43,9 +43,9 @@
if((method==VAPOR && prob(min(33, reac_volume))) || method==INGEST || method==PATCH || method==INJECT)
M.randmuti()
if(prob(98))
- M.randmutb()
+ M.easy_randmut(NEGATIVE+MINOR_NEGATIVE)
else
- M.randmutg()
+ M.easy_randmut(POSITIVE)
M.updateappearance()
M.domutcheck()
..()
diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm
index 580f9c87..5a8fc03d 100644
--- a/code/modules/research/designs/medical_designs.dm
+++ b/code/modules/research/designs/medical_designs.dm
@@ -162,6 +162,16 @@
category = list("Medical Designs")
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL
+/datum/design/genescanner
+ name = "Genetic Sequence Analyzer"
+ desc = "A handy hand-held analyzers for quickly determining mutations and collecting the full sequence."
+ id = "genescanner"
+ build_path = /obj/item/sequence_scanner
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL = 1000, MAT_GLASS = 500)
+ category = list("Medical Designs")
+ departmental_flags = DEPARTMENTAL_FLAG_MEDICAL
+
/datum/design/healthanalyzer_advanced
name = "Advanced Health Analyzer"
desc = "A hand-held body scanner able to distinguish vital signs of the subject with high accuracy."
diff --git a/code/modules/research/techweb/_techweb.dm b/code/modules/research/techweb/_techweb.dm
index da3fec61..42cd5ff1 100644
--- a/code/modules/research/techweb/_techweb.dm
+++ b/code/modules/research/techweb/_techweb.dm
@@ -19,6 +19,7 @@
var/largest_bomb_value = 0
var/organization = "Third-Party" //Organization name, used for display.
var/list/last_bitcoins = list() //Current per-second production, used for display only.
+ var/list/discovered_mutations = list() //Mutations discovered by genetics, this way they are shared and cant be destroyed by destroying a single console
var/list/tiers = list() //Assoc list, datum = number, 1 is available, 2 is all reqs are 1, so on
/datum/techweb/New()
diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm
index 5f848ce8..1349fd23 100644
--- a/code/modules/research/techweb/all_nodes.dm
+++ b/code/modules/research/techweb/all_nodes.dm
@@ -60,7 +60,7 @@
display_name = "Biological Technology"
description = "What makes us tick." //the MC, silly!
prereq_ids = list("base")
- design_ids = list("medicalkit", "chem_heater", "chem_master", "chem_dispenser", "sleeper", "vr_sleeper", "pandemic", "defibmount", "operating", "soda_dispenser", "beer_dispenser", "healthanalyzer", "blood_bag")
+ design_ids = list("medicalkit", "chem_heater", "chem_master", "chem_dispenser", "sleeper", "vr_sleeper", "pandemic", "defibmount", "operating", "soda_dispenser", "beer_dispenser", "healthanalyzer", "blood_bag","genescanner")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
export_price = 5000
diff --git a/code/modules/ruins/spaceruin_code/hilbertshotel.dm b/code/modules/ruins/spaceruin_code/hilbertshotel.dm
new file mode 100644
index 00000000..0d4d2b6a
--- /dev/null
+++ b/code/modules/ruins/spaceruin_code/hilbertshotel.dm
@@ -0,0 +1,529 @@
+GLOBAL_VAR_INIT(hhStorageTurf, null)
+GLOBAL_VAR_INIT(hhmysteryRoomNumber, 1337)
+
+/obj/item/hilbertshotel
+ name = "Hilbert's Hotel"
+ desc = "A sphere of what appears to be an intricate network of bluespace. Observing it in detail seems to give you a headache as you try to comprehend the infinite amount of infinitesimally distinct points on its surface."
+ icon_state = "hilbertshotel"
+ w_class = WEIGHT_CLASS_SMALL
+ resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
+ var/datum/map_template/hilbertshotel/hotelRoomTemp
+ var/datum/map_template/hilbertshotel/empty/hotelRoomTempEmpty
+ var/datum/map_template/hilbertshotel/lore/hotelRoomTempLore
+ var/list/activeRooms = list()
+ var/list/storedRooms = list()
+ var/storageTurf
+ //Lore Stuff
+ var/ruinSpawned = FALSE
+ var/mysteryRoom
+
+/obj/item/hilbertshotel/Initialize()
+ . = ..()
+ //Load templates
+ hotelRoomTemp = new()
+ hotelRoomTempEmpty = new()
+ hotelRoomTempLore = new()
+ var/area/currentArea = get_area(src)
+ if(currentArea.type == /area/ruin/space/has_grav/hilbertresearchfacility)
+ ruinSpawned = TRUE
+
+/obj/item/hilbertshotel/Destroy()
+ ejectRooms()
+ return ..()
+
+/obj/item/hilbertshotel/attack(mob/living/M, mob/living/user)
+ if(M.mind)
+ to_chat(user, "You invite [M] to the hotel.")
+ promptAndCheckIn(M)
+ else
+ to_chat(user, "[M] is not intelligent enough to understand how to use this device!")
+
+/obj/item/hilbertshotel/attack_self(mob/user)
+ . = ..()
+ promptAndCheckIn(user)
+
+/obj/item/hilbertshotel/proc/promptAndCheckIn(mob/user)
+ var/chosenRoomNumber = input(user, "What number room will you be checking into?", "Room Number") as null|num
+ if(!chosenRoomNumber)
+ return
+ if(chosenRoomNumber > SHORT_REAL_LIMIT)
+ to_chat(user, "You have to check out the first [SHORT_REAL_LIMIT] rooms before you can go to a higher numbered one!")
+ return
+ if((chosenRoomNumber < 1) || (chosenRoomNumber != round(chosenRoomNumber)))
+ to_chat(user, "That is not a valid room number!")
+ return
+ if(ismob(loc))
+ if(user == loc) //Not always the same as user
+ forceMove(get_turf(user))
+ if(!storageTurf) //Blame subsystems for not allowing this to be in Initialize
+ if(!GLOB.hhStorageTurf)
+ var/datum/map_template/hilbertshotelstorage/storageTemp = new()
+ var/datum/turf_reservation/storageReservation = SSmapping.RequestBlockReservation(3, 3)
+ storageTemp.load(locate(storageReservation.bottom_left_coords[1], storageReservation.bottom_left_coords[2], storageReservation.bottom_left_coords[3]))
+ GLOB.hhStorageTurf = locate(storageReservation.bottom_left_coords[1]+1, storageReservation.bottom_left_coords[2]+1, storageReservation.bottom_left_coords[3])
+ else
+ storageTurf = GLOB.hhStorageTurf
+ if(tryActiveRoom(chosenRoomNumber, user))
+ return
+ if(tryStoredRoom(chosenRoomNumber, user))
+ return
+ sendToNewRoom(chosenRoomNumber, user)
+
+
+/obj/item/hilbertshotel/proc/tryActiveRoom(var/roomNumber, var/mob/user)
+ if(activeRooms["[roomNumber]"])
+ var/datum/turf_reservation/roomReservation = activeRooms["[roomNumber]"]
+ do_sparks(3, FALSE, get_turf(user))
+ user.forceMove(locate(roomReservation.bottom_left_coords[1] + hotelRoomTemp.landingZoneRelativeX, roomReservation.bottom_left_coords[2] + hotelRoomTemp.landingZoneRelativeY, roomReservation.bottom_left_coords[3]))
+ return TRUE
+ else
+ return FALSE
+
+/obj/item/hilbertshotel/proc/tryStoredRoom(var/roomNumber, var/mob/user)
+ if(storedRooms["[roomNumber]"])
+ var/datum/turf_reservation/roomReservation = SSmapping.RequestBlockReservation(hotelRoomTemp.width, hotelRoomTemp.height)
+ hotelRoomTempEmpty.load(locate(roomReservation.bottom_left_coords[1], roomReservation.bottom_left_coords[2], roomReservation.bottom_left_coords[3]))
+ var/turfNumber = 1
+ for(var/i=0, iAs the sphere breaks apart, you're suddenly ejected into the depths of space!")
+ var/max = world.maxx-TRANSITIONEDGE
+ var/min = 1+TRANSITIONEDGE
+ var/list/possible_transtitons = list()
+ for(var/AZ in SSmapping.z_list)
+ var/datum/space_level/D = AZ
+ if (D.linkage == CROSSLINKED)
+ possible_transtitons += D.z_value
+ var/_z = pick(possible_transtitons)
+ var/_x = rand(min,max)
+ var/_y = rand(min,max)
+ var/turf/T = locate(_x, _y, _z)
+ A.forceMove(T)
+ qdel(room)
+
+ if(storedRooms.len)
+ for(var/x in storedRooms)
+ var/list/atomList = storedRooms[x]
+ for(var/atom/movable/A in atomList)
+ var/max = world.maxx-TRANSITIONEDGE
+ var/min = 1+TRANSITIONEDGE
+ var/list/possible_transtitons = list()
+ for(var/AZ in SSmapping.z_list)
+ var/datum/space_level/D = AZ
+ if (D.linkage == CROSSLINKED)
+ possible_transtitons += D.z_value
+ var/_z = pick(possible_transtitons)
+ var/_x = rand(min,max)
+ var/_y = rand(min,max)
+ var/turf/T = locate(_x, _y, _z)
+ A.forceMove(T)
+
+//Template Stuff
+/datum/map_template/hilbertshotel
+ name = "Hilbert's Hotel Room"
+ mappath = '_maps/templates/hilbertshotel.dmm'
+ var/landingZoneRelativeX = 2
+ var/landingZoneRelativeY = 8
+
+/datum/map_template/hilbertshotel/empty
+ name = "Empty Hilbert's Hotel Room"
+ mappath = '_maps/templates/hilbertshotelempty.dmm'
+
+/datum/map_template/hilbertshotel/lore
+ name = "Doctor Hilbert's Deathbed"
+ mappath = '_maps/templates/hilbertshotellore.dmm'
+
+/datum/map_template/hilbertshotelstorage
+ name = "Hilbert's Hotel Storage"
+ mappath = '_maps/templates/hilbertshotelstorage.dmm'
+
+
+//Turfs and Areas
+/turf/closed/indestructible/hotelwall
+ name = "hotel wall"
+ desc = "A wall designed to protect the security of the hotel's guests."
+ icon_state = "hotelwall"
+ canSmoothWith = list(/turf/closed/indestructible/hotelwall)
+ explosion_block = INFINITY
+
+/turf/open/indestructible/hotelwood
+ desc = "Stylish dark wood with extra reinforcement. Secured firmly to the floor to prevent tampering."
+ icon_state = "wood"
+ footstep = FOOTSTEP_WOOD
+ tiled_dirt = FALSE
+
+/turf/open/indestructible/hoteltile
+ desc = "Smooth tile with extra reinforcement. Secured firmly to the floor to prevent tampering."
+ icon_state = "showroomfloor"
+ footstep = FOOTSTEP_FLOOR
+ tiled_dirt = FALSE
+
+/turf/open/space/bluespace
+ name = "\proper bluespace hyperzone"
+ icon_state = "bluespace"
+ baseturfs = /turf/open/space/bluespace
+ flags_1 = NOJAUNT_1
+ explosion_block = INFINITY
+ var/obj/item/hilbertshotel/parentSphere
+
+/turf/open/space/bluespace/Entered(atom/movable/A)
+ . = ..()
+ A.forceMove(get_turf(parentSphere))
+
+/turf/closed/indestructible/hoteldoor
+ name = "Hotel Door"
+ icon_state = "hoteldoor"
+ explosion_block = INFINITY
+ var/obj/item/hilbertshotel/parentSphere
+
+/turf/closed/indestructible/hoteldoor/proc/promptExit(mob/living/user)
+ if(!isliving(user))
+ return
+ if(!user.mind)
+ return
+ if(!parentSphere)
+ to_chat(user, "The door seems to be malfunctioning and refuses to operate!")
+ return
+ if(alert(user, "Hilbert's Hotel would like to remind you that while we will do everything we can to protect the belongings you leave behind, we make no guarantees of their safety while you're gone, especially that of the health of any living creatures. With that in mind, are you ready to leave?", "Exit", "Leave", "Stay") == "Leave")
+ if(!user.canmove || (get_dist(get_turf(src), get_turf(user)) > 1)) //no teleporting around if they're dead or moved away during the prompt.
+ return
+ user.forceMove(get_turf(parentSphere))
+ do_sparks(3, FALSE, get_turf(user))
+
+/turf/closed/indestructible/hoteldoor/attack_ghost(mob/dead/observer/user)
+ if(!isobserver(user) || !parentSphere)
+ return ..()
+ user.forceMove(get_turf(parentSphere))
+
+//If only this could be simplified...
+/turf/closed/indestructible/hoteldoor/attack_tk(mob/user)
+ return //need to be close.
+
+/turf/closed/indestructible/hoteldoor/attack_hand(mob/user)
+ promptExit(user)
+
+/turf/closed/indestructible/hoteldoor/attack_animal(mob/user)
+ promptExit(user)
+
+/turf/closed/indestructible/hoteldoor/attack_paw(mob/user)
+ promptExit(user)
+
+/turf/closed/indestructible/hoteldoor/attack_hulk(mob/living/carbon/human/user, does_attack_animation)
+ promptExit(user)
+
+/turf/closed/indestructible/hoteldoor/attack_larva(mob/user)
+ promptExit(user)
+
+/turf/closed/indestructible/hoteldoor/attack_slime(mob/user)
+ promptExit(user)
+
+/turf/closed/indestructible/hoteldoor/attack_robot(mob/user)
+ if(get_dist(get_turf(src), get_turf(user)) <= 1)
+ promptExit(user)
+
+/turf/closed/indestructible/hoteldoor/AltClick(mob/user)
+ . = ..()
+ if(get_dist(get_turf(src), get_turf(user)) <= 1)
+ to_chat(user, "You peak through the door's bluespace peephole...")
+ user.reset_perspective(parentSphere)
+ user.set_machine(src)
+ var/datum/action/peepholeCancel/PHC = new
+ user.overlay_fullscreen("remote_view", /obj/screen/fullscreen/impaired, 1)
+ PHC.Grant(user)
+
+/turf/closed/indestructible/hoteldoor/check_eye(mob/user)
+ if(get_dist(get_turf(src), get_turf(user)) >= 2)
+ user.unset_machine()
+ for(var/datum/action/peepholeCancel/PHC in user.actions)
+ PHC.Trigger()
+
+/datum/action/peepholeCancel
+ name = "Cancel View"
+ desc = "Stop looking through the bluespace peephole."
+ button_icon_state = "cancel_peephole"
+
+/datum/action/peepholeCancel/Trigger()
+ . = ..()
+ to_chat(owner, "You move away from the peephole.")
+ owner.reset_perspective()
+ owner.clear_fullscreen("remote_view", 0)
+ qdel(src)
+
+/area/hilbertshotel
+ name = "Hilbert's Hotel Room"
+ icon_state = "hilbertshotel"
+ requires_power = FALSE
+ has_gravity = TRUE
+ noteleport = TRUE
+ hidden = TRUE
+ unique = FALSE
+ dynamic_lighting = DYNAMIC_LIGHTING_FORCED
+ ambientsounds = list('sound/ambience/servicebell.ogg')
+ var/roomnumber = 0
+ var/obj/item/hilbertshotel/parentSphere
+ var/datum/turf_reservation/reservation
+ var/turf/storageTurf
+
+/area/hilbertshotel/Entered(atom/movable/AM)
+ . = ..()
+ if(istype(AM, /obj/item/hilbertshotel))
+ relocate(AM)
+ var/list/obj/item/hilbertshotel/hotels = AM.GetAllContents(/obj/item/hilbertshotel)
+ for(var/obj/item/hilbertshotel/H in hotels)
+ if(parentSphere == H)
+ relocate(H)
+
+/area/hilbertshotel/proc/relocate(obj/item/hilbertshotel/H)
+ if(prob(0.135685)) //Because screw you
+ qdel(H)
+ return
+ var/turf/targetturf = find_safe_turf()
+ if(!targetturf)
+ if(GLOB.blobstart.len > 0)
+ targetturf = get_turf(pick(GLOB.blobstart))
+ else
+ CRASH("Unable to find a blobstart landmark")
+ var/turf/T = get_turf(H)
+ var/area/A = T.loc
+ log_game("[H] entered itself. Moving it to [loc_name(targetturf)].")
+ message_admins("[H] entered itself. Moving it to [ADMIN_VERBOSEJMP(targetturf)].")
+ for(var/mob/M in A)
+ to_chat(M, "[H] almost implodes in upon itself, but quickly rebounds, shooting off into a random point in space!")
+ H.forceMove(targetturf)
+
+/area/hilbertshotel/Exited(atom/movable/AM)
+ . = ..()
+ if(ismob(AM))
+ var/mob/M = AM
+ if(M.mind)
+ var/stillPopulated = FALSE
+ var/list/currentLivingMobs = GetAllContents(/mob/living) //Got to catch anyone hiding in anything
+ for(var/mob/living/L in currentLivingMobs) //Check to see if theres any sentient mobs left.
+ if(L.mind)
+ stillPopulated = TRUE
+ break
+ if(!stillPopulated)
+ storeRoom()
+
+/area/hilbertshotel/proc/storeRoom()
+ var/roomSize = (reservation.top_right_coords[1]-reservation.bottom_left_coords[1]+1)*(reservation.top_right_coords[2]-reservation.bottom_left_coords[2]+1)
+ var/storage[roomSize]
+ var/turfNumber = 1
+ var/obj/item/abstracthotelstorage/storageObj = new(storageTurf)
+ storageObj.roomNumber = roomnumber
+ storageObj.parentSphere = parentSphere
+ storageObj.name = "Room [roomnumber] Storage"
+ for(var/i=0, iIt's to far away to scan!")
+ return
+ var/obj/item/hilbertshotel/sphere = target
+ if(sphere.activeRooms.len)
+ to_chat(user, "Currently Occupied Rooms:")
+ for(var/roomnumber in sphere.activeRooms)
+ to_chat(user, roomnumber)
+ else
+ to_chat(user, "No currenty occupied rooms.")
+ if(sphere.storedRooms.len)
+ to_chat(user, "Vacated Rooms:")
+ for(var/roomnumber in sphere.storedRooms)
+ to_chat(user, roomnumber)
+ else
+ to_chat(user, "No vacated rooms.")
+
+/obj/effect/mob_spawn/human/doctorhilbert
+ name = "Doctor Hilbert"
+ mob_name = "Doctor Hilbert"
+ mob_gender = "male"
+ assignedrole = null
+ ghost_usable = FALSE
+ oxy_damage = 500
+ mob_species = /datum/species/skeleton
+ id_job = "Head Researcher"
+ id_access = ACCESS_RESEARCH
+ id_access_list = list(ACCESS_AWAY_GENERIC3, ACCESS_RESEARCH)
+ instant = TRUE
+ id = /obj/item/card/id/silver
+ uniform = /obj/item/clothing/under/rank/research_director
+ shoes = /obj/item/clothing/shoes/sneakers/brown
+ back = /obj/item/storage/backpack/satchel/leather
+ suit = /obj/item/clothing/suit/toggle/labcoat
+
+/obj/item/paper/crumpled/docslogs
+ name = "Research Logs"
+
+/obj/item/paper/crumpled/docslogs/Initialize()
+ . = ..()
+ GLOB.hhmysteryRoomNumber = rand(1, SHORT_REAL_LIMIT)
+ info = {"
Research Logs
+ I might just be onto something here!
+ The strange space-warping properties of bluespace have been known about for awhile now, but I might be on the verge of discovering a new way of harnessing it.
+ It's too soon to say for sure, but this might be the start of something quite important!
+ I'll be sure to log any major future breakthroughs. This might be a lot more than I can manage on my own, perhaps I should hire that secretary after all...
+
Breakthrough!
+ I can't believe it, but I did it! Just when I was certain it couldn't be done, I made the final necessary breakthrough.
+ Exploiting the effects of space dilation caused by specific bluespace structures combined with a precise use of geometric calculus, I've discovered a way to correlate an infinite amount of space within a finite area!
+ While the potential applications are endless, I utilized it in quite a nifty way so far by designing a system that recursively constructs subspace rooms and spatially links them to any of the infinite infinitesimally distinct points on the spheres surface.
+ I call it: Hilbert's Hotel!
+
Goodbye
+ I can't take this anymore. I know what happens next, and the fear of what is coming leaves me unable to continue working.
+ Any fool in my field has heard the stories. It's not that I didn't believe them, it's just... I guess I underestimated the importance of my own research...
+ Robert has reported a further increase in frequency of the strange, prying visitors who ask questions they have no business asking. I've requested him to keep everything on strict lockdown and have permanently dismissed all other assistants.
+ I've also instructed him to use the encryption method we discussed for any important quantitative data. The poor lad... I don't think he truly understands what he's gotten himself into...
+ It's clear what happens now. One day they'll show up uninvited, and claim my research as their own, leaving me as nothing more than a bullet ridden corpse floating in space.
+ I can't stick around to the let that happen.
+ I'm escaping into the very thing that brought all this trouble to my doorstep in the first place - my hotel.
+ I'll be in
[uppertext(num2hex(GLOB.hhmysteryRoomNumber, 0))] (That will make sense to anyone who should know)
+ I'm sorry that I must go like this. Maybe one day things will be different and it will be safe to return... maybe...
+ Goodbye
+
+
Doctor Hilbert"}
+
+/obj/item/paper/crumpled/robertsworkjournal
+ name = "Work Journal"
+ info = {"
First Week!
+ First week on the new job. It's a secretarial position, but hey, whatever pays the bills. Plus it seems like some interesting stuff goes on here.
+ Doc says its best that I don't openly talk about his research with others, I guess he doesn't want it getting out or something. I've caught myself slipping a few times when talking to others, it's hard not to brag about something this cool!
+ I'm not really sure why I'm choosing to journal this. Doc seems to log everything. He says it's incase he discovers anything important.
+ I guess that's why I'm doing it too, I've always wanted to be a part of something important.
+ Here's to a new job and to becoming a part of something important!
+
Weird times...
+ Things are starting to get a little strange around here. Just weeks after Doc's amazing breakthrough, weird visitors have began showing up unannounced, asking strange things about Doc's work.
+ I knew Doc wasn't a big fan of company, but even he seemed strangely unnerved when I told him about the visitors.
+ He said it's important that from here on out we keep tight security on everything, even other staff members.
+ He also said something about securing data, something about hexes. What's that mean? Some sort of curse? Doc never struck me as the magic type...
+ He often uses a lot of big sciencey words that I don't really understand, but I kinda dig it, it makes me feel like I'm witnessing something big.
+ I hope things go back to normal soon, but I guess that's the price you pay for being a part of something important.
+
Last day I guess?
+ Things are officially starting to get too strange for me.
+ The visitors have been coming a lot more often, and they all seem increasingly aggressive and nosey. I'm starting to see why they made Doc so nervous, they're certainly starting to creep me out too.
+ Awhile ago Doc started having me keep the place on strict lockdown and requested I refuse entry to anyone else, including previous staff.
+ But the weirdest part?
+ I haven't seen Doc in days. It's not unusual for him to work continuously for long periods of time in the lab, but when I took a peak in their yesterday - he was nowhere to be seen! I didn't risk prying much further, Doc had a habit of leaving the defense systems on these last few weeks.
+ I'm thinking it might be time to call it quits. Can't work much without a boss, plus things are starting to get kind of shady. I wanted to be a part of something important, but you gotta know when to play it safe.
+ As my dad always said, "The smart get famous, but the wise survive..."
+
+
Robert P."}
+
+/obj/item/paper/crumpled/bloody/docsdeathnote
+ name = "note"
+ info = {"This is it isn't it?
+ No one's coming to help, that much has become clear.
+ Sure, it's lonely, but do I have much choice? At least I brought the analyzer with me, they shouldn't be able to find me without it.
+ Who knows who's waiting for me out there. Its either die out there in their hands, or die a slower, slightly more comfortable death in here.
+ Everyday I can feel myself slipping away more and more, both physically and mentally. Who knows what happens now...
+ Heh, so it's true then, this must be the inescapable path of all great minds... so be it then.
+
+
+
+
Choose a room, and enter the sphere
+ Lay your head to rest, it soon becomes clear
+ There's always more room around every bend
+ Not all that's countable has an end..."}
diff --git a/code/modules/shuttle/supply.dm b/code/modules/shuttle/supply.dm
index 2249f5f1..3bd031cc 100644
--- a/code/modules/shuttle/supply.dm
+++ b/code/modules/shuttle/supply.dm
@@ -24,6 +24,7 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list(
/obj/effect/portal,
/obj/item/shared_storage,
/obj/structure/extraction_point,
+ /obj/item/hilbertshotel,
/obj/machinery/syndicatebomb
)))
diff --git a/code/modules/spells/spell_types/genetic.dm b/code/modules/spells/spell_types/genetic.dm
index b670d93f..c3bdc74e 100644
--- a/code/modules/spells/spell_types/genetic.dm
+++ b/code/modules/spells/spell_types/genetic.dm
@@ -4,7 +4,7 @@
var/list/active_on = list()
var/list/traits = list() //disabilities
- var/list/mutations = list() //mutation strings
+ var/list/mutations = list() //mutation defines
var/duration = 100 //deciseconds
/*
Disabilities
diff --git a/code/modules/spells/spell_types/summonitem.dm b/code/modules/spells/spell_types/summonitem.dm
index 6d46c53a..7d9823e2 100644
--- a/code/modules/spells/spell_types/summonitem.dm
+++ b/code/modules/spells/spell_types/summonitem.dm
@@ -61,6 +61,10 @@
organ.Remove(organ.owner)
else
while(!isturf(item_to_retrieve.loc) && infinite_recursion < 10) //if it's in something you get the whole thing.
+ if(isitem(item_to_retrieve.loc))
+ var/obj/item/I = item_to_retrieve.loc
+ if(I.item_flags & ABSTRACT) //Being able to summon abstract things because your item happened to get placed there is a no-no
+ break
if(ismob(item_to_retrieve.loc)) //If its on someone, properly drop it
var/mob/M = item_to_retrieve.loc
diff --git a/code/modules/spells/spell_types/telepathy.dm b/code/modules/spells/spell_types/telepathy.dm
new file mode 100644
index 00000000..db7e284e
--- /dev/null
+++ b/code/modules/spells/spell_types/telepathy.dm
@@ -0,0 +1,30 @@
+/obj/effect/proc_holder/spell/targeted/telepathy
+ name = "Telepathy"
+ desc = "Telepathically transmits a message to the target."
+ charge_max = 0
+ clothes_req = 0
+ range = 7
+ include_user = 0
+ action_icon = 'icons/mob/actions/actions_revenant.dmi'
+ action_icon_state = "r_transmit"
+ action_background_icon_state = "bg_spell"
+ var/notice = "notice"
+ var/boldnotice = "boldnotice"
+ var/magic_check = TRUE
+
+/obj/effect/proc_holder/spell/targeted/telepathy/cast(list/targets, mob/living/simple_animal/revenant/user = usr)
+ for(var/mob/living/M in targets)
+ var/msg = stripped_input(usr, "What do you wish to tell [M]?", null, "")
+ if(!msg)
+ charge_counter = charge_max
+ return
+ log_directed_talk(user, M, msg, LOG_SAY, "[name]")
+ to_chat(user, "You transmit to [M]: [msg]")
+ if(!magic_check || !M.anti_magic_check(FALSE, TRUE)) //hear no evil
+ to_chat(M, "You hear something behind you talking... [msg]")
+ for(var/ded in GLOB.dead_mob_list)
+ if(!isobserver(ded))
+ continue
+ var/follow_rev = FOLLOW_LINK(ded, user)
+ var/follow_whispee = FOLLOW_LINK(ded, M)
+ to_chat(ded, "[follow_rev] [user] [name]: \"[msg]\" to [follow_whispee] [M]")
\ No newline at end of file
diff --git a/code/modules/spells/spell_types/touch_attacks.dm b/code/modules/spells/spell_types/touch_attacks.dm
index 0ffe02ce..f0a9735c 100644
--- a/code/modules/spells/spell_types/touch_attacks.dm
+++ b/code/modules/spells/spell_types/touch_attacks.dm
@@ -1,6 +1,8 @@
/obj/effect/proc_holder/spell/targeted/touch
var/hand_path = /obj/item/melee/touch_attack
var/obj/item/melee/touch_attack/attached_hand = null
+ var/drawmessage = "You channel the power of the spell to your hand."
+ var/dropmessage = "You draw the power out of your hand."
invocation_type = "none" //you scream on connecting, not summoning
include_user = 1
range = -1
@@ -21,7 +23,7 @@
/obj/effect/proc_holder/spell/targeted/touch/cast(list/targets,mob/user = usr)
if(!QDELETED(attached_hand))
remove_hand(TRUE)
- to_chat(user, "You draw the power out of your hand.")
+ to_chat(user, "[dropmessage]")
return
for(var/mob/living/carbon/C in targets)
@@ -43,7 +45,7 @@
remove_hand(TRUE)
to_chat(user, "Your hands are full!")
return FALSE
- to_chat(user, "You channel the power of the spell to your hand.")
+ to_chat(user, "[drawmessage]")
return TRUE
diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm
index 9341fb6c..4e2b81dc 100644
--- a/code/modules/surgery/bodyparts/dismemberment.dm
+++ b/code/modules/surgery/bodyparts/dismemberment.dm
@@ -108,7 +108,7 @@
for(var/X in C.dna.mutations) //some mutations require having specific limbs to be kept.
var/datum/mutation/human/MT = X
if(MT.limb_req && MT.limb_req == body_zone)
- MT.force_lose(C)
+ C.dna.force_lose(MT)
for(var/X in C.internal_organs) //internal organs inside the dismembered limb are dropped.
var/obj/item/organ/O = X
diff --git a/code/modules/vending/engivend.dm b/code/modules/vending/engivend.dm
index 9358d165..10ffdad9 100644
--- a/code/modules/vending/engivend.dm
+++ b/code/modules/vending/engivend.dm
@@ -5,6 +5,7 @@
icon_deny = "engivend-deny"
req_access = list(ACCESS_ENGINE_EQUIP)
products = list(/obj/item/clothing/glasses/meson/engine = 5,
+ /obj/item/storage/bag/construction = 5,
/obj/item/clothing/glasses/welding = 5,
/obj/item/multitool = 5,
/obj/item/construction/rcd/loaded = 3,
diff --git a/config/spaceRuinBlacklist.txt b/config/spaceRuinBlacklist.txt
index deafa479..edf16cc1 100644
--- a/config/spaceRuinBlacklist.txt
+++ b/config/spaceRuinBlacklist.txt
@@ -50,3 +50,4 @@
#_maps/RandomRuins/SpaceRuins/arcade.dmm
#_maps/RandomRuins/SpaceRuins/spacehermit.dmm
#_maps/RandomRuins/SpaceRuins/advancedlab.dmm
+#_maps/RandomRuins/SpaceRuins/hilbertshoteltestingsite.dmm
diff --git a/config/title_music/klaytheme.ogg b/config/title_music/klaytheme.ogg
deleted file mode 100644
index 562e28b2..00000000
Binary files a/config/title_music/klaytheme.ogg and /dev/null differ
diff --git a/html/browser/scannernew.css b/html/browser/scannernew.css
index 0e809a67..6746a61a 100644
--- a/html/browser/scannernew.css
+++ b/html/browser/scannernew.css
@@ -16,3 +16,24 @@
float: left;
}
+img.selected
+{
+ border: 1px solid blue;
+}
+img.unselected
+{
+ border: 2px solid black;
+}
+div>table {
+ float: left;
+}
+td
+{
+ text-align: center;
+}
+a.clean
+{
+ background: none;
+ border: none;
+ marging: none;
+}
\ No newline at end of file
diff --git a/html/dna_discovered.gif b/html/dna_discovered.gif
new file mode 100644
index 00000000..bc6b75f2
Binary files /dev/null and b/html/dna_discovered.gif differ
diff --git a/html/dna_extra.gif b/html/dna_extra.gif
new file mode 100644
index 00000000..c92218a6
Binary files /dev/null and b/html/dna_extra.gif differ
diff --git a/html/dna_undiscovered.gif b/html/dna_undiscovered.gif
new file mode 100644
index 00000000..a3c182a4
Binary files /dev/null and b/html/dna_undiscovered.gif differ
diff --git a/icons/effects/genetics.dmi b/icons/effects/genetics.dmi
index a9f5f433..373a9de6 100644
Binary files a/icons/effects/genetics.dmi and b/icons/effects/genetics.dmi differ
diff --git a/icons/mob/actions.dmi b/icons/mob/actions.dmi
index 4e23c102..f0f283cd 100644
Binary files a/icons/mob/actions.dmi and b/icons/mob/actions.dmi differ
diff --git a/icons/mob/actions/actions_genetic.dmi b/icons/mob/actions/actions_genetic.dmi
new file mode 100644
index 00000000..72bd87f9
Binary files /dev/null and b/icons/mob/actions/actions_genetic.dmi differ
diff --git a/icons/mob/actions/actions_spells.dmi b/icons/mob/actions/actions_spells.dmi
index 30927d36..5617e228 100644
Binary files a/icons/mob/actions/actions_spells.dmi and b/icons/mob/actions/actions_spells.dmi differ
diff --git a/icons/obj/chromosomes.dmi b/icons/obj/chromosomes.dmi
new file mode 100644
index 00000000..a8ff6186
Binary files /dev/null and b/icons/obj/chromosomes.dmi differ
diff --git a/icons/obj/cross.dmi b/icons/obj/cross.dmi
new file mode 100644
index 00000000..de147177
Binary files /dev/null and b/icons/obj/cross.dmi differ
diff --git a/icons/obj/items_and_weapons.dmi b/icons/obj/items_and_weapons.dmi
index 8788567b..3a6d8023 100644
Binary files a/icons/obj/items_and_weapons.dmi and b/icons/obj/items_and_weapons.dmi differ
diff --git a/icons/obj/tools.dmi b/icons/obj/tools.dmi
index c983201d..23a138d5 100644
Binary files a/icons/obj/tools.dmi and b/icons/obj/tools.dmi differ
diff --git a/icons/turf/areas.dmi b/icons/turf/areas.dmi
index 01cb909d..992b37cc 100644
Binary files a/icons/turf/areas.dmi and b/icons/turf/areas.dmi differ
diff --git a/icons/turf/space.dmi b/icons/turf/space.dmi
index d32279e7..33e3fab9 100644
Binary files a/icons/turf/space.dmi and b/icons/turf/space.dmi differ
diff --git a/icons/turf/walls.dmi b/icons/turf/walls.dmi
index 3290e3f9..0df70e9e 100644
Binary files a/icons/turf/walls.dmi and b/icons/turf/walls.dmi differ
diff --git a/interface/menu.dm b/interface/menu.dm
index 57e151bd..224d933f 100644
--- a/interface/menu.dm
+++ b/interface/menu.dm
@@ -169,4 +169,3 @@ GLOBAL_LIST_EMPTY(menulist)
/datum/verbs/menu/Icon/Scaling/verb/BL()
set name = "@.winset \"mapwindow.map.zoom-mode=blur\""
set desc = "Bilinear"
-
diff --git a/modular_citadel/code/game/machinery/wishgranter.dm b/modular_citadel/code/game/machinery/wishgranter.dm
index f24062a1..6121893c 100644
--- a/modular_citadel/code/game/machinery/wishgranter.dm
+++ b/modular_citadel/code/game/machinery/wishgranter.dm
@@ -17,7 +17,7 @@
to_chat(user, "Your head pounds for a moment, before your vision clears. The Wish Granter, sensing the darkness in your heart, has given you limitless power, and it's all yours!")
user.dna.add_mutation(HULK)
user.dna.add_mutation(XRAY)
- user.dna.add_mutation(COLDRES)
+ user.dna.add_mutation(SPACEMUT)
user.dna.add_mutation(TK)
user.next_move_modifier *= 0.5 //half the delay between attacks!
to_chat(user, "Things around you feel slower!")
diff --git a/sound/ambience/servicebell.ogg b/sound/ambience/servicebell.ogg
new file mode 100644
index 00000000..f55c7d5a
Binary files /dev/null and b/sound/ambience/servicebell.ogg differ
diff --git a/sound/effects/crossed.ogg b/sound/effects/crossed.ogg
new file mode 100644
index 00000000..276d7f25
Binary files /dev/null and b/sound/effects/crossed.ogg differ
diff --git a/tgstation.dme b/tgstation.dme
index da983cfb..abff4adf 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -117,6 +117,7 @@
#include "code\__HELPERS\AStar.dm"
#include "code\__HELPERS\cmp.dm"
#include "code\__HELPERS\dates.dm"
+#include "code\__HELPERS\dna.dm"
#include "code\__HELPERS\files.dm"
#include "code\__HELPERS\game.dm"
#include "code\__HELPERS\global_lists.dm"
@@ -470,10 +471,14 @@
#include "code\datums\mood_events\generic_positive_events.dm"
#include "code\datums\mood_events\mood_event.dm"
#include "code\datums\mood_events\needs_events.dm"
+#include "code\datums\mutations\actions.dm"
+#include "code\datums\mutations\antenna.dm"
#include "code\datums\mutations\body.dm"
#include "code\datums\mutations\chameleon.dm"
#include "code\datums\mutations\cold_resistance.dm"
+#include "code\datums\mutations\combined.dm"
#include "code\datums\mutations\hulk.dm"
+#include "code\datums\mutations\radioactive.dm"
#include "code\datums\mutations\sight.dm"
#include "code\datums\mutations\speech.dm"
#include "code\datums\mutations\telekinesis.dm"
@@ -786,6 +791,7 @@
#include "code\game\objects\items\cardboard_cutouts.dm"
#include "code\game\objects\items\cards_ids.dm"
#include "code\game\objects\items\charter.dm"
+#include "code\game\objects\items\chromosome.dm"
#include "code\game\objects\items\chrono_eraser.dm"
#include "code\game\objects\items\cigs_lighters.dm"
#include "code\game\objects\items\clown_items.dm"
@@ -2637,6 +2643,7 @@
#include "code\modules\ruins\spaceruin_code\crashedship.dm"
#include "code\modules\ruins\spaceruin_code\deepstorage.dm"
#include "code\modules\ruins\spaceruin_code\DJstation.dm"
+#include "code\modules\ruins\spaceruin_code\hilbertshotel.dm"
#include "code\modules\ruins\spaceruin_code\listeningstation.dm"
#include "code\modules\ruins\spaceruin_code\miracle.dm"
#include "code\modules\ruins\spaceruin_code\oldstation.dm"
@@ -2697,6 +2704,7 @@
#include "code\modules\spells\spell_types\spacetime_distortion.dm"
#include "code\modules\spells\spell_types\summonitem.dm"
#include "code\modules\spells\spell_types\taeclowndo.dm"
+#include "code\modules\spells\spell_types\telepathy.dm"
#include "code\modules\spells\spell_types\the_traps.dm"
#include "code\modules\spells\spell_types\touch_attacks.dm"
#include "code\modules\spells\spell_types\trigger.dm"