This commit is contained in:
Ghommie
2020-03-15 22:35:59 +01:00
248 changed files with 4790 additions and 1911 deletions
+65 -31
View File
@@ -2,32 +2,57 @@
#define CHECK_DNA_AND_SPECIES(C) if((!(C.dna)) || (!(C.dna.species))) return
//Defines copying names of mutations in all cases, make sure to change this if you change mutation's name
#define HULK "Hulk"
#define XRAY "X Ray Vision"
#define COLDRES "Cold Resistance"
#define TK "Telekinesis"
#define NERVOUS "Nervousness"
#define EPILEPSY "Epilepsy"
#define MUTATE "Unstable DNA"
#define COUGH "Cough"
#define DWARFISM "Dwarfism"
#define CLOWNMUT "Clumsiness"
#define TOURETTES "Tourettes Syndrome"
#define DEAFMUT "Deafness"
#define BLINDMUT "Blindness"
#define RACEMUT "Monkified"
#define BADSIGHT "Near Sightness"
#define LASEREYES "Laser Eyes"
#define CHAMELEON "Chameleon"
#define WACKY "Wacky"
#define MUT_MUTE "Mute"
#define SMILE "Smile"
#define STONER "Stoner"
#define UNINTELLIGIBLE "Unintelligible"
#define SWEDISH "Swedish"
#define CHAV "Chav"
#define ELVIS "Elvis"
//Defines copying names of mutations in all cases, make sure to change this if you change mutation's type
#define HULK /datum/mutation/human/hulk
#define XRAY /datum/mutation/human/thermal/x_ray
#define SPACEMUT /datum/mutation/human/space_adaptation
#define TK /datum/mutation/human/telekinesis
#define NERVOUS /datum/mutation/human/nervousness
#define EPILEPSY /datum/mutation/human/epilepsy
#define MUTATE /datum/mutation/human/bad_dna
#define COUGH /datum/mutation/human/cough
#define DWARFISM /datum/mutation/human/dwarfism
#define GIGANTISM /datum/mutation/human/gigantism
#define CLOWNMUT /datum/mutation/human/clumsy
#define TOURETTES /datum/mutation/human/tourettes
#define DEAFMUT /datum/mutation/human/deaf
#define BLINDMUT /datum/mutation/human/blind
#define RACEMUT /datum/mutation/human/race
#define BADSIGHT /datum/mutation/human/nearsight
#define LASEREYES /datum/mutation/human/laser_eyes
#define CHAMELEON /datum/mutation/human/chameleon
#define WACKY /datum/mutation/human/wacky
#define MUT_MUTE /datum/mutation/human/mute
#define SMILE /datum/mutation/human/smile
#define STONER /datum/mutation/human/stoner
#define UNINTELLIGIBLE /datum/mutation/human/unintelligible
#define SWEDISH /datum/mutation/human/swedish
#define CHAV /datum/mutation/human/chav
#define ELVIS /datum/mutation/human/elvis
#define RADIOACTIVE /datum/mutation/human/radioactive
#define GLOWY /datum/mutation/human/glow
#define ANTIGLOWY /datum/mutation/human/glow/anti
#define TELEPATHY /datum/mutation/human/telepathy
#define FIREBREATH /datum/mutation/human/firebreath
#define VOID /datum/mutation/human/void
#define TONGUESPIKE /datum/mutation/human/tongue_spike
#define TONGUESPIKECHEM /datum/mutation/human/tongue_spike/chem
#define STRONG /datum/mutation/human/strong
#define STIMMED /datum/mutation/human/stimmed
#define FIRESWEAT /datum/mutation/human/fire
#define THERMAL /datum/mutation/human/thermal
#define ANTENNA /datum/mutation/human/antenna
#define PARANOIA /datum/mutation/human/paranoia
#define MINDREAD /datum/mutation/human/mindreader
#define INSULATED /datum/mutation/human/insulated
#define SHOCKTOUCH /datum/mutation/human/shock
#define OLFACTION /datum/mutation/human/olfaction
#define ACIDFLESH /datum/mutation/human/acidflesh
#define BADBLINK /datum/mutation/human/badblink
#define SPASTIC /datum/mutation/human/spastic
#define GELADIKINESIS /datum/mutation/human/geladikinesis
#define CRYOKINESIS /datum/mutation/human/cryokinesis
#define SPIDER_WEB /datum/mutation/human/webbing
#define UI_CHANGED "ui changed"
#define UE_CHANGED "ue changed"
@@ -39,10 +64,12 @@
//Types of usual mutations
#define POSITIVE 1
#define NEGATIVE 2
#define MINOR_NEGATIVE 3
#define MINOR_NEGATIVE 4
//Mutations that cant be taken from genetics and are not in SE
#define NON_SCANNABLE -1
//Mutation classes. Normal being on them, extra being additional mutations with instability and other being stuff you dont want people to fuck with like wizard mutate
#define MUT_NORMAL 1
#define MUT_EXTRA 2
#define MUT_OTHER 3
//DNA - Because fuck you and your magic numbers being all over the codebase.
#define DNA_BLOCK_SIZE 3
@@ -63,7 +90,8 @@
#define DNA_MUTANTMARKING_BLOCK 13
#define DNA_TAUR_BLOCK 14
#define DNA_STRUC_ENZYMES_BLOCKS 18
#define DNA_SEQUENCE_LENGTH 4
#define DNA_MUTATION_BLOCKS 8
#define DNA_UNIQUE_ENZYMES_LEN 32
//Transformation proc stuff
@@ -141,7 +169,13 @@
#define STANDARD_ORGAN_HEALING (1/(15 MINUTES / (2 SECONDS)))
#define STANDARD_ORGAN_DECAY (1/(15 MINUTES / (2 SECONDS))) //designed to fail organs when left to decay for ~15 minutes. 2 SECOND is SSmobs tickrate.
//used for the can_chromosome var on mutations
#define CHROMOSOME_NEVER 0
#define CHROMOSOME_NONE 1
#define CHROMOSOME_USED 2
#define G_MALE 1
#define G_FEMALE 2
#define G_PLURAL 3
#define G_NEUTER 4
#define G_NEUTER 4
+6 -2
View File
@@ -1,3 +1,6 @@
#define TRAITOR_HUMAN /datum/traitor_class/human/freeform
#define TRAITOR_AI /datum/traitor_class/ai
#define NUKE_RESULT_FLUKE 0
#define NUKE_RESULT_NUKE_WIN 1
#define NUKE_RESULT_CREW_WIN 2
@@ -74,6 +77,7 @@
#define MARTIALART_HUNTER "hunter-fu"
//Blob
/// blob gets a free reroll every X time
#define BLOB_REROLL_TIME 2400
#define BLOB_REROLL_TIME 2400 // blob gets a free reroll every X time
#define BLOB_SPREAD_COST 4
#define BLOB_ATTACK_REFUND 2 //blob refunds this much if it attacks and doesn't spread
#define BLOB_REFLECTOR_COST 15
+41
View File
@@ -0,0 +1,41 @@
/// Return this from `/datum/component/Initialize` or `datum/component/OnTransfer` to have the component be deleted if it's applied to an incorrect type.
/// `parent` must not be modified if this is to be returned.
/// This will be noted in the runtime logs
#define COMPONENT_INCOMPATIBLE 1
/// Returned in PostTransfer to prevent transfer, similar to `COMPONENT_INCOMPATIBLE`
#define COMPONENT_NOTRANSFER 2
/// Return value to cancel attaching
#define ELEMENT_INCOMPATIBLE 1
// /datum/element flags
/// Causes the detach proc to be called when the host object is being deleted
#define ELEMENT_DETACH (1 << 0)
/**
* Only elements created with the same arguments given after `id_arg_index` share an element instance
* The arguments are the same when the text and number values are the same and all other values have the same ref
*/
#define ELEMENT_BESPOKE (1 << 1)
// How multiple components of the exact same type are handled in the same datum
/// old component is deleted (default)
#define COMPONENT_DUPE_HIGHLANDER 0
/// duplicates allowed
#define COMPONENT_DUPE_ALLOWED 1
/// new component is deleted
#define COMPONENT_DUPE_UNIQUE 2
/// old component is given the initialization args of the new
#define COMPONENT_DUPE_UNIQUE_PASSARGS 4
/// each component of the same type is consulted as to whether the duplicate should be allowed
#define COMPONENT_DUPE_SELECTIVE 5
//Redirection component init flags
#define REDIRECT_TRANSFER_WITH_TURF 1
//Arch
#define ARCH_PROB "probability" //Probability for each item
#define ARCH_MAXDROP "max_drop_amount" //each item's max drop amount
//Ouch my toes!
#define CALTROP_BYPASS_SHOES 1
#define CALTROP_IGNORE_WALKERS 2
+16
View File
@@ -0,0 +1,16 @@
/// Used to trigger signals and call procs registered for that signal
/// The datum hosting the signal is automaticaly added as the first argument
/// Returns a bitfield gathered from all registered procs
/// Arguments given here are packaged in a list and given to _SendSignal
#define SEND_SIGNAL(target, sigtype, arguments...) ( !target.comp_lookup || !target.comp_lookup[sigtype] ? NONE : target._SendSignal(sigtype, list(target, ##arguments)) )
#define SEND_GLOBAL_SIGNAL(sigtype, arguments...) ( SEND_SIGNAL(SSdcs, sigtype, ##arguments) )
/// A wrapper for _AddElement that allows us to pretend we're using normal named arguments
#define AddElement(arguments...) _AddElement(list(##arguments))
/// A wrapper for _RemoveElement that allows us to pretend we're using normal named arguments
#define RemoveElement(arguments...) _RemoveElement(list(##arguments))
/// A wrapper for _AddComponent that allows us to pretend we're using normal named arguments
#define AddComponent(arguments...) _AddComponent(list(##arguments))
@@ -1,28 +1,3 @@
#define SEND_SIGNAL(target, sigtype, arguments...) ( !target.comp_lookup || !target.comp_lookup[sigtype] ? NONE : target._SendSignal(sigtype, list(target, ##arguments)) )
#define SEND_GLOBAL_SIGNAL(sigtype, arguments...) ( SEND_SIGNAL(SSdcs, sigtype, ##arguments) )
#define COMPONENT_INCOMPATIBLE 1
#define COMPONENT_NOTRANSFER 2
#define ELEMENT_INCOMPATIBLE 1 // Return value to cancel attaching
// /datum/element flags
/// Causes the detach proc to be called when the host object is being deleted
#define ELEMENT_DETACH (1 << 0)
/**
* Only elements created with the same arguments given after `id_arg_index` share an element instance
* The arguments are the same when the text and number values are the same and all other values have the same ref
*/
#define ELEMENT_BESPOKE (1 << 1)
// How multiple components of the exact same type are handled in the same datum
#define COMPONENT_DUPE_HIGHLANDER 0 //old component is deleted (default)
#define COMPONENT_DUPE_ALLOWED 1 //duplicates allowed
#define COMPONENT_DUPE_UNIQUE 2 //new component is deleted
#define COMPONENT_DUPE_UNIQUE_PASSARGS 4 //old component is given the initialization args of the new
// All signals. Format:
// When the signal is called: (signal arguments)
// All signals send the source datum of the signal as the first argument
@@ -306,7 +281,7 @@
#define COMSIG_HUMAN_MELEE_UNARMED_ATTACKBY "human_melee_unarmed_attackby" //from mob/living/carbon/human/UnarmedAttack(): (mob/living/carbon/human/attacker)
#define COMSIG_HUMAN_DISARM_HIT "human_disarm_hit" //Hit by successful disarm attack (mob/living/carbon/human/attacker,zone_targeted)
#define COMSIG_HUMAN_PREFS_COPIED_TO "human_prefs_copied_to" //from datum/preferences/copy_to(): (datum/preferences, icon_updates, roundstart_checks)
#define COMSIG_HUMAN_HARDSET_DNA "human_hardset_dna" //from mob/living/carbon/human/hardset_dna(): (ui, se, newreal_name, newblood_type, datum/species, newfeatures)
#define COMSIG_HUMAN_HARDSET_DNA "human_hardset_dna" //from mob/living/carbon/human/hardset_dna(): (ui, list/mutation_index, newreal_name, newblood_type, datum/species, newfeatures)
#define COMSIG_HUMAN_ON_RANDOMIZE "humman_on_randomize" //from base of proc/randomize_human()
// /datum/species signals
@@ -374,19 +349,6 @@
#define COMSIG_ACTION_TRIGGER "action_trigger" //from base of datum/action/proc/Trigger(): (datum/action)
#define COMPONENT_ACTION_BLOCK_TRIGGER 1
/*******Non-Signal Component Related Defines*******/
//Redirection component init flags
#define REDIRECT_TRANSFER_WITH_TURF 1
//Arch
#define ARCH_PROB "probability" //Probability for each item
#define ARCH_MAXDROP "max_drop_amount" //each item's max drop amount
//Ouch my toes!
#define CALTROP_BYPASS_SHOES 1
#define CALTROP_IGNORE_WALKERS 2
//Xenobio hotkeys
#define COMSIG_XENO_SLIME_CLICK_CTRL "xeno_slime_click_ctrl" //from slime CtrlClickOn(): (/mob)
#define COMSIG_XENO_SLIME_CLICK_ALT "xeno_slime_click_alt" //from slime AltClickOn(): (/mob)
+1
View File
@@ -118,3 +118,4 @@
#define STATUS_EFFECT_RAINBOWPROTECTION /datum/status_effect/rainbow_protection //Invulnerable and pacifistic
#define STATUS_EFFECT_SLIMESKIN /datum/status_effect/slimeskin //Increased armor
#define STATUS_EFFECT_DNA_MELT /datum/status_effect/dna_melt //usually does something horrible to you when you hit 100 genetic instability
+3
View File
@@ -83,6 +83,7 @@
#define TRAIT_DISFIGURED "disfigured"
#define TRAIT_XENO_HOST "xeno_host" //Tracks whether we're gonna be a baby alien's mummy.
#define TRAIT_STUNIMMUNE "stun_immunity"
#define TRAIT_TASED_RESISTANCE "tased_resistance" //prevents you from suffering most of the effects of being tased
#define TRAIT_SLEEPIMMUNE "sleep_immunity"
#define TRAIT_PUSHIMMUNE "push_immunity"
#define TRAIT_SHOCKIMMUNE "shock_immunity"
@@ -194,6 +195,8 @@
#define TRAIT_AUTO_CATCH_ITEM "auto_catch_item"
#define TRAIT_CLOWN_MENTALITY "clown_mentality" // The future is now, clownman.
#define TRAIT_FREESPRINT "free_sprinting"
#define TRAIT_XRAY_VISION "xray_vision"
#define TRAIT_THERMAL_VISION "thermal_vision"
#define TRAIT_NO_TELEPORT "no-teleport" //you just can't
#define TRAIT_NO_INTERNALS "no-internals"
#define TRAIT_NO_ALCOHOL "alcohol_intolerance"
+12
View File
@@ -0,0 +1,12 @@
//////////////////////////////////////////////////////////
//A bunch of helpers to make genetics less of a headache//
//////////////////////////////////////////////////////////
#define GET_INITIALIZED_MUTATION(A) GLOB.all_mutations[A]
#define GET_GENE_STRING(A, B) (B.mutation_index[A])
#define GET_SEQUENCE(A) (GLOB.full_sequences[A])
#define GET_MUTATION_STABILIZER(A) ((A.stabilizer_coeff < 0) ? 1 : A.stabilizer_coeff)
#define GET_MUTATION_SYNCHRONIZER(A) ((A.synchronizer_coeff < 0) ? 1 : A.synchronizer_coeff)
#define GET_MUTATION_POWER(A) ((A.power_coeff < 0) ? 1 : A.power_coeff)
#define GET_MUTATION_ENERGY(A) ((A.energy_coeff < 0) ? 1 : A.energy_coeff)
+1 -1
View File
@@ -425,7 +425,7 @@
candidates -= M
/proc/pollGhostCandidates(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, ignore_category = null, flashwindow = TRUE)
var/datum/element/ghost_role_eligibility/eligibility = SSdcs.GetElement(/datum/element/ghost_role_eligibility)
var/datum/element/ghost_role_eligibility/eligibility = SSdcs.GetElement(list(/datum/element/ghost_role_eligibility))
var/list/candidates = eligibility.get_all_ghost_role_eligible()
return pollCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category, flashwindow, candidates)
+5 -2
View File
@@ -21,8 +21,11 @@ GLOBAL_LIST(op_se_blocks)
GLOBAL_VAR(NULLED_SE)
GLOBAL_VAR(NULLED_UI)
GLOBAL_LIST_EMPTY(global_mutations) // list of hidden mutation things
GLOBAL_LIST_EMPTY(all_mutations)
GLOBAL_LIST_EMPTY(full_sequences)
GLOBAL_LIST_EMPTY(bad_mutations)
GLOBAL_LIST_EMPTY(good_mutations)
GLOBAL_LIST_EMPTY(not_good_mutations)
GLOBAL_LIST_EMPTY(not_good_mutations)
GLOBAL_LIST_EMPTY(mutation_recipes)
+1 -5
View File
@@ -125,11 +125,7 @@
if(prob(33))
I.add_mob_blood(src)
var/turf/location = get_turf(src)
if(iscarbon(src))
var/mob/living/carbon/C = src
C.bleed(totitemdamage)
else
add_splatter_floor(location)
add_splatter_floor(location)
if(totitemdamage >= 10 && get_dist(user, src) <= 1) //people with TK won't get smeared with blood
user.add_mob_blood(src)
return TRUE //successful attack
@@ -371,19 +371,18 @@
for(var/T in storyteller_cache)
var/datum/dynamic_storyteller/S = T
var/config_tag = initial(S.config_tag)
var/final_weight = initial(S.weight)
if(probabilities[config_tag]<=0)
var/probability = (config_tag in probabilities) ? probabilities[config_tag] : initial(S.weight)
if(probability <= 0)
continue
final_weight = probabilities[config_tag]
if(SSpersistence.saved_storytellers.len == 3 && repeated_mode_adjust.len == 3)
if(SSpersistence.saved_storytellers.len == repeated_mode_adjust.len)
var/name = initial(S.name)
var/recent_round = min(SSpersistence.saved_storytellers.Find(name),3)
var/adjustment = 0
while(recent_round)
adjustment += repeated_mode_adjust[recent_round]
recent_round = SSpersistence.saved_modes.Find(name,recent_round+1,0)
final_weight *= ((100-adjustment)/100)
runnable_storytellers[S] = final_weight
probability *= ((100-adjustment)/100)
runnable_storytellers[S] = probability
return runnable_storytellers
@@ -82,17 +82,23 @@
config_entry_value = 600
min_val = 0
/datum/config_entry/number/vote_autotransfer_initial //length of time before the first autotransfer vote is called (deciseconds, default 2 hours)
/// Length of time before the first autotransfer vote is called (deciseconds, default 2 hours)
/// Set to 0 to disable the subsystem altogether.
/datum/config_entry/number/vote_autotransfer_initial
config_entry_value = 72000
min_val = 0
/datum/config_entry/number/vote_autotransfer_interval //length of time to wait before subsequent autotransfer votes (deciseconds, default 30 minutes)
///length of time to wait before subsequent autotransfer votes (deciseconds, default 30 minutes)
/datum/config_entry/number/vote_autotransfer_interval
config_entry_value = 18000
min_val = 0
/datum/config_entry/number/vote_autotransfer_maximum // maximum extensions until the round autoends
/// maximum extensions until the round autoends.
/// Set to 0 to force automatic crew transfer after the 'vote_autotransfer_initial' elapsed.
/// Set to -1 to disable the maximum extensions cap.
/datum/config_entry/number/vote_autotransfer_maximum
config_entry_value = 4
min_val = 0
min_val = -1
/datum/config_entry/flag/default_no_vote // vote does not default to nochange/norestart
@@ -459,3 +465,6 @@
/datum/config_entry/number/max_bunker_days
config_entry_value = 7
min_val = 1
/datum/config_entry/flag/minimaps_enabled
config_entry_value = TRUE
+13 -10
View File
@@ -16,7 +16,7 @@ SUBSYSTEM_DEF(atoms)
/datum/controller/subsystem/atoms/Initialize(timeofday)
GLOB.fire_overlay.appearance_flags = RESET_COLOR
setupGenetics() //to set the mutations' place in structural enzymes, so monkey.initialize() knows where to put the monkey mutation.
setupGenetics() //to set the mutations' sequence.
initialized = INITIALIZATION_INNEW_MAPLOAD
InitializeAtoms()
return ..()
@@ -108,16 +108,19 @@ SUBSYSTEM_DEF(atoms)
BadInitializeCalls = SSatoms.BadInitializeCalls
/datum/controller/subsystem/atoms/proc/setupGenetics()
var/list/avnums = new /list(DNA_STRUC_ENZYMES_BLOCKS)
for(var/i=1, i<=DNA_STRUC_ENZYMES_BLOCKS, i++)
avnums[i] = i
CHECK_TICK
for(var/A in subtypesof(/datum/mutation/human))
var/datum/mutation/human/B = new A()
if(B.dna_block == NON_SCANNABLE)
var/list/mutations = subtypesof(/datum/mutation/human)
shuffle_inplace(mutations)
for(var/A in subtypesof(/datum/generecipe))
var/datum/generecipe/GR = A
GLOB.mutation_recipes[initial(GR.required)] = initial(GR.result)
for(var/i in 1 to LAZYLEN(mutations))
var/path = mutations[i] //byond gets pissy when we do it in one line
var/datum/mutation/human/B = new path ()
B.alias = "Mutation #[i]"
GLOB.all_mutations[B.type] = B
GLOB.full_sequences[B.type] = generate_gene_sequence(B.blocks)
if(B.locked)
continue
B.dna_block = pick_n_take(avnums)
if(B.quality == POSITIVE)
GLOB.good_mutations |= B
else if(B.quality == NEGATIVE)
+21 -8
View File
@@ -1,3 +1,5 @@
#define NO_MAXVOTES_CAP -1
SUBSYSTEM_DEF(autotransfer)
name = "Autotransfer Vote"
flags = SS_KEEP_TIMING | SS_BACKGROUND
@@ -7,21 +9,32 @@ SUBSYSTEM_DEF(autotransfer)
var/targettime
var/voteinterval
var/maxvotes
var/curvotes
var/curvotes = 0
/datum/controller/subsystem/autotransfer/Initialize(timeofday)
var/init_vote = CONFIG_GET(number/vote_autotransfer_initial)
if(!init_vote) //Autotransfer voting disabled.
can_fire = FALSE
return ..()
starttime = world.time
targettime = starttime + CONFIG_GET(number/vote_autotransfer_initial)
targettime = starttime + init_vote
voteinterval = CONFIG_GET(number/vote_autotransfer_interval)
maxvotes = CONFIG_GET(number/vote_autotransfer_maximum)
curvotes = 0
return ..()
/datum/controller/subsystem/autotransfer/Recover()
starttime = SSautotransfer.starttime
voteinterval = SSautotransfer.voteinterval
curvotes = SSautotransfer.curvotes
/datum/controller/subsystem/autotransfer/fire()
if(maxvotes > curvotes)
if(world.time > targettime)
SSvote.initiate_vote("transfer",null) //TODO figure out how to not use null as the user
targettime = targettime + voteinterval
curvotes += 1
if(world.time < targettime)
return
if(maxvotes == NO_MAXVOTES_CAP || maxvotes > curvotes)
SSvote.initiate_vote("transfer","server")
targettime = targettime + voteinterval
curvotes++
else
SSshuttle.autoEnd()
#undef NO_MAXVOTES_CAP
+38 -12
View File
@@ -1,27 +1,53 @@
PROCESSING_SUBSYSTEM_DEF(dcs)
name = "Datum Component System"
flags = SS_NO_INIT
var/list/elements_by_type = list()
/datum/controller/subsystem/processing/dcs/Recover()
comp_lookup = SSdcs.comp_lookup
/datum/controller/subsystem/processing/dcs/proc/GetElement(datum/element/eletype, ...)
/datum/controller/subsystem/processing/dcs/proc/GetElement(list/arguments)
var/datum/element/eletype = arguments[1]
var/element_id = eletype
if(!ispath(eletype, /datum/element))
CRASH("Attempted to instantiate [eletype] as a /datum/element")
if(initial(eletype.element_flags) & ELEMENT_BESPOKE)
var/list/fullid = list("[eletype]")
for(var/i in initial(eletype.id_arg_index) to length(args))
var/argument = args[i]
if(istext(argument) || isnum(argument))
fullid += "[argument]"
else
fullid += "[REF(argument)]"
element_id = fullid.Join("&")
element_id = GetIdFromArguments(arguments)
. = elements_by_type[element_id]
if(.)
return
if(!ispath(eletype, /datum/element))
CRASH("Attempted to instantiate [eletype] as a /datum/element")
. = elements_by_type[element_id] = new eletype
. = elements_by_type[element_id] = new eletype
/****
* Generates an id for bespoke elements when given the argument list
* Generating the id here is a bit complex because we need to support named arguments
* Named arguments can appear in any order and we need them to appear after ordered arguments
* We assume that no one will pass in a named argument with a value of null
**/
/datum/controller/subsystem/processing/dcs/proc/GetIdFromArguments(list/arguments)
var/datum/element/eletype = arguments[1]
var/list/fullid = list("[eletype]")
var/list/named_arguments = list()
for(var/i in initial(eletype.id_arg_index) to length(arguments))
var/key = arguments[i]
var/value
if(istext(key))
value = arguments[key]
if(!(istext(key) || isnum(key)))
key = REF(key)
key = "[key]" // Key is stringified so numbers dont break things
if(!isnull(value))
if(!(istext(value) || isnum(value)))
value = REF(value)
named_arguments["[key]"] = value
else
fullid += "[key]"
if(length(named_arguments))
named_arguments = sortList(named_arguments)
fullid += named_arguments
return list2params(fullid)
+20
View File
@@ -0,0 +1,20 @@
SUBSYSTEM_DEF(minimaps)
name = "Minimaps"
flags = SS_NO_FIRE
var/list/station_minimaps
var/datum/minimap_group/station_minimap
/datum/controller/subsystem/minimaps/Initialize()
if(!CONFIG_GET(flag/minimaps_enabled))
to_chat(world, "<span class='boldwarning'>Minimaps disabled! Skipping init.</span>")
return ..()
build_minimaps()
return ..()
/datum/controller/subsystem/minimaps/proc/build_minimaps()
station_minimaps = list()
for(var/z in SSmapping.levels_by_trait(ZTRAIT_STATION))
var/datum/space_level/SL = SSmapping.get_level(z)
var/name = (SL.name == initial(SL.name))? "[z] - Station" : "[z] - [SL.name]"
station_minimaps += new /datum/minimap(z, name = name)
station_minimap = new(station_minimaps, "Station")
@@ -85,7 +85,7 @@ PROCESSING_SUBSYSTEM_DEF(quirks)
our_quirks -= i
cut += i
pointscut += quirk_points_by_name(i)
if (pointscut >= 0) //with how it works, it needs to be above zero, not below, as points for positive is positive, and negative is negative, we only want it to break if it's above zero, ie. we cut more positive than negative
if (pointscut >= 0)
break
/* //Code to automatically reduce positive quirks until balance is even.
var/points_used = total_points(our_quirks)
@@ -102,7 +102,7 @@ PROCESSING_SUBSYSTEM_DEF(quirks)
*/
//Nah, let's null all non-neutrals out.
if (pointscut != 0)// only if the pointscutting didn't work.
if (pointscut < 0)// only if the pointscutting didn't work.
if(cut.len)
for(var/i in our_quirks)
if(quirk_points_by_name(i) != 0)
+2 -16
View File
@@ -89,20 +89,6 @@ SUBSYSTEM_DEF(vote)
choices[GLOB.master_mode] += non_voters.len
if(choices[GLOB.master_mode] >= greatest_votes)
greatest_votes = choices[GLOB.master_mode]
else if(mode == "transfer") // austation begin -- Crew autotransfer vote
var/factor = 1
switch(world.time / (1 MINUTES))
if(0 to 60)
factor = 0.5
if(61 to 120)
factor = 0.8
if(121 to 240)
factor = 1
if(241 to 300)
factor = 1.2
else
factor = 1.4
choices["Initiate Crew Transfer"] += round(non_voters.len * factor) // austation end
//get all options with that many votes and return them in a list
. = list()
if(greatest_votes)
@@ -370,7 +356,7 @@ SUBSYSTEM_DEF(vote)
var/list/runnable_storytellers = config.get_runnable_storytellers()
for(var/T in runnable_storytellers)
var/datum/dynamic_storyteller/S = T
runnable_storytellers[S] *= stored_gamemode_votes[initial(S.name)]
runnable_storytellers[S] *= round(stored_gamemode_votes[initial(S.name)]*100000,1)
var/datum/dynamic_storyteller/S = pickweightAllowZero(runnable_storytellers)
GLOB.dynamic_storyteller_type = S
if("map")
@@ -457,7 +443,7 @@ SUBSYSTEM_DEF(vote)
var/admin = FALSE
var/ckey = ckey(initiator_key)
if(GLOB.admin_datums[ckey])
if(GLOB.admin_datums[ckey] || initiator_key == "server")
admin = TRUE
if(next_allowed_time > world.time && !admin)
+2 -128
View File
@@ -4,132 +4,6 @@
Loosely adapted from /vg/. This is an entity component system for adding behaviours to datums when inheritance doesn't quite cut it. By using signals and events instead of direct inheritance, you can inject behaviours without hacky overloads. It requires a different method of thinking, but is not hard to use correctly. If a behaviour can have application across more than one thing. Make it generic, make it a component. Atom/mob/obj event? Give it a signal, and forward it's arguments with a `SendSignal()` call. Now every component that want's to can also know about this happening.
### In the code
See [this thread](https://tgstation13.org/phpBB/viewtopic.php?f=5&t=22674) for an introduction to the system as a whole.
#### Slippery things
At the time of this writing, every object that is slippery overrides atom/Crossed does some checks, then slips the mob. Instead of all those Crossed overrides they could add a slippery component to all these objects. And have the checks in one proc that is run by the Crossed event
#### Powercells
A lot of objects have powercells. The `get_cell()` proc was added to give generic access to the cell var if it had one. This is just a specific use case of `GetComponent()`
#### Radios
The radio object as it is should not exist, given that more things use the _concept_ of radios rather than the object itself. The actual function of the radio can exist in a component which all the things that use it (Request consoles, actual radios, the SM shard) can add to themselves.
#### Standos
Stands have a lot of procs which mimic mob procs. Rather than inserting hooks for all these procs in overrides, the same can be accomplished with signals
## API
### Defines
1. `COMPONENT_INCOMPATIBLE` Return this from `/datum/component/Initialize` or `datum/component/OnTransfer` to have the component be deleted if it's applied to an incorrect type. `parent` must not be modified if this is to be returned. This will be noted in the runtime logs
### Vars
1. `/datum/var/list/datum_components` (private)
* Lazy associated list of type -> component/list of components.
1. `/datum/var/list/comp_lookup` (private)
* Lazy associated list of signal -> registree/list of registrees
1. `/datum/var/list/signal_procs` (private)
* Associated lazy list of signals -> `/datum/callback`s that will be run when the parent datum receives that signal
1. `/datum/var/signal_enabled` (protected, boolean)
* If the datum is signal enabled. If not, it will not react to signals
* `FALSE` by default, set to `TRUE` when a signal is registered
1. `/datum/component/var/dupe_mode` (protected, enum)
* How duplicate component types are handled when added to the datum.
* `COMPONENT_DUPE_HIGHLANDER` (default): Old component will be deleted, new component will first have `/datum/component/proc/InheritComponent(datum/component/old, FALSE)` on it
* `COMPONENT_DUPE_ALLOWED`: The components will be treated as separate, `GetComponent()` will return the first added
* `COMPONENT_DUPE_UNIQUE`: New component will be deleted, old component will first have `/datum/component/proc/InheritComponent(datum/component/new, TRUE)` on it
* `COMPONENT_DUPE_UNIQUE_PASSARGS`: New component will never exist and instead its initialization arguments will be passed on to the old component.
1. `/datum/component/var/dupe_type` (protected, type)
* Definition of a duplicate component type
* `null` means exact match on `type` (default)
* Any other type means that and all subtypes
1. `/datum/component/var/datum/parent` (protected, read-only)
* The datum this component belongs to
* Never `null` in child procs
1. `report_signal_origin` (protected, boolean)
* If `TRUE`, will invoke the callback when signalled with the signal type as the first argument.
* `FALSE` by default.
### Procs
1. `/datum/proc/GetComponent(component_type(type)) -> datum/component?` (public, final)
* Returns a reference to a component of component_type if it exists in the datum, null otherwise
1. `/datum/proc/GetComponents(component_type(type)) -> list` (public, final)
* Returns a list of references to all components of component_type that exist in the datum
1. `/datum/proc/GetExactComponent(component_type(type)) -> datum/component?` (public, final)
* Returns a reference to a component whose type MATCHES component_type if that component exists in the datum, null otherwise
1. `GET_COMPONENT(varname, component_type)` OR `GET_COMPONENT_FROM(varname, component_type, src)`
* Shorthand for `var/component_type/varname = src.GetComponent(component_type)`
1. `SEND_SIGNAL(target, sigtype, ...)` (public, final)
* Use to send signals to target datum
* Extra arguments are to be specified in the signal definition
* Returns a bitflag with signal specific information assembled from all activated components
* Arguments are packaged in a list and handed off to _SendSignal()
1. `/datum/proc/AddComponent(component_type(type), ...) -> datum/component` (public, final)
* Creates an instance of `component_type` in the datum and passes `...` to its `Initialize()` call
* Sends the `COMSIG_COMPONENT_ADDED` signal to the datum
* All components a datum owns are deleted with the datum
* Returns the component that was created. Or the old component in a dupe situation where `COMPONENT_DUPE_UNIQUE` was set
* If this tries to add an component to an incompatible type, the component will be deleted and the result will be `null`. This is very unperformant, try not to do it
* Properly handles duplicate situations based on the `dupe_mode` var
1. `/datum/proc/LoadComponent(component_type(type), ...) -> datum/component` (public, final)
* Equivalent to calling `GetComponent(component_type)` where, if the result would be `null`, returns `AddComponent(component_type, ...)` instead
1. `/datum/proc/ComponentActivated(datum/component/C)` (abstract, async)
* Called on a component's `parent` after a signal received causes it to activate. `src` is the parameter
* Will only be called if a component's callback returns `TRUE`
1. `/datum/proc/TakeComponent(datum/component/C)` (public, final)
* Properly transfers ownership of a component from one datum to another
* Signals `COMSIG_COMPONENT_REMOVING` on the parent
* Called on the datum you want to own the component with another datum's component
1. `/datum/proc/_SendSignal(signal, list/arguments)` (private, final)
* Handles most of the actual signaling procedure
* Will runtime if used on datums with an empty component list
1. `/datum/proc/RegisterSignal(datum/target, signal(string/list of strings), proc_ref(type), override(boolean))` (protected, final)
* If signal is a list it will be as if RegisterSignal was called for each of the entries with the same following arguments
* Makes the datum listen for the specified `signal` on it's `parent` datum.
* When that signal is received `proc_ref` will be called on the component, along with associated arguments
* Example proc ref: `.proc/OnEvent`
* If a previous registration is overwritten by the call, a runtime occurs. Setting `override` to TRUE prevents this
* These callbacks run asyncronously
* Returning `TRUE` from these callbacks will trigger a `TRUE` return from the `SendSignal()` that initiated it
1. `/datum/component/New(datum/parent, ...)` (private, final)
* Runs internal setup for the component
* Extra arguments are passed to `Initialize()`
1. `/datum/component/Initialize(...)` (abstract, no-sleep)
* Called by `New()` with the same argments excluding `parent`
* Component does not exist in `parent`'s `datum_components` list yet, although `parent` is set and may be used
* Signals will not be received while this function is running
* Component may be deleted after this function completes without being attached
* Do not call `qdel(src)` from this function
1. `/datum/component/Destroy(force(bool), silent(bool))` (virtual, no-sleep)
* Sends the `COMSIG_COMPONENT_REMOVING` signal to the parent datum if the `parent` isn't being qdeleted
* Properly removes the component from `parent` and cleans up references
* Setting `force` makes it not check for and remove the component from the parent
* Setting `silent` deletes the component without sending a `COMSIG_COMPONENT_REMOVING` signal
1. `/datum/component/proc/InheritComponent(datum/component/C, i_am_original(boolean))` (abstract, no-sleep)
* Called on a component when a component of the same type was added to the same parent
* See `/datum/component/var/dupe_mode`
* `C`'s type will always be the same of the called component
1. `/datum/component/proc/AfterComponentActivated()` (abstract, async)
* Called on a component that was activated after it's `parent`'s `ComponentActivated()` is called
1. `/datum/component/proc/OnTransfer(datum/new_parent)` (abstract, no-sleep)
* Called before `new_parent` is assigned to `parent` in `TakeComponent()`
* Allows the component to react to ownership transfers
1. `/datum/component/proc/_RemoveFromParent()` (private, final)
* Clears `parent` and removes the component from it's component list
1. `/datum/component/proc/_JoinParent` (private, final)
* Tries to add the component to it's `parent`s `datum_components` list
1. `/datum/component/proc/RegisterWithParent` (abstract, no-sleep)
* Used to register the signals that should be on the `parent` object
* Use this if you plan on the component transfering between parents
1. `/datum/component/proc/UnregisterFromParent` (abstract, no-sleep)
* Counterpart to `RegisterWithParent()`
* Used to unregister the signals that should only be on the `parent` object
### See/Define signals and their arguments in __DEFINES\components.dm
### See/Define signals and their arguments in [__DEFINES\components.dm](..\..\__DEFINES\components.dm)
+228 -36
View File
@@ -1,21 +1,71 @@
/**
* # Component
*
* The component datum
*
* A component should be a single standalone unit
* of functionality, that works by receiving signals from it's parent
* object to provide some single functionality (i.e a slippery component)
* that makes the object it's attached to cause people to slip over.
* Useful when you want shared behaviour independent of type inheritance
*/
/datum/component
/// Defines how duplicate existing components are handled when added to a datum
/// See `COMPONENT_DUPE_*` definitions for available options
var/dupe_mode = COMPONENT_DUPE_HIGHLANDER
/// The type to check for duplication
/// `null` means exact match on `type` (default)
/// Any other type means that and all subtypes
var/dupe_type
/// The datum this components belongs to
var/datum/parent
//only set to true if you are able to properly transfer this component
//At a minimum RegisterWithParent and UnregisterFromParent should be used
//Make sure you also implement PostTransfer for any post transfer handling
/// Only set to true if you are able to properly transfer this component
/// At a minimum RegisterWithParent and UnregisterFromParent should be used
/// Make sure you also implement PostTransfer for any post transfer handling
var/can_transfer = FALSE
/datum/component/New(datum/P, ...)
parent = P
var/list/arguments = args.Copy(2)
/**
* Create a new component.
* Additional arguments are passed to `Initialize()`
*
* Arguments:
* * datum/P the parent datum this component reacts to signals from
*/
/datum/component/New(list/raw_args)
parent = raw_args[1]
var/list/arguments = raw_args.Copy(2)
if(Initialize(arglist(arguments)) == COMPONENT_INCOMPATIBLE)
qdel(src, TRUE, TRUE)
CRASH("Incompatible [type] assigned to a [P.type]! args: [json_encode(arguments)]")
CRASH("Incompatible [type] assigned to a [parent.type]! args: [json_encode(arguments)]")
_JoinParent(P)
_JoinParent(parent)
/**
* Called during component creation with the same arguments as in new excluding parent.
* Do not call `qdel(src)` from this function, `return COMPONENT_INCOMPATIBLE` instead
*/
/datum/component/proc/Initialize(...)
return
/**
* Properly removes the component from `parent` and cleans up references
* Setting `force` makes it not check for and remove the component from the parent
* Setting `silent` deletes the component without sending a `COMSIG_COMPONENT_REMOVING` signal
*/
/datum/component/Destroy(force=FALSE, silent=FALSE)
if(!force && parent)
_RemoveFromParent()
if(!silent)
SEND_SIGNAL(parent, COMSIG_COMPONENT_REMOVING, src)
parent = null
return ..()
/**
* Internal proc to handle behaviour of components when joining a parent
*/
/datum/component/proc/_JoinParent()
var/datum/P = parent
//lazy init the parent's dc list
@@ -51,21 +101,9 @@
RegisterWithParent()
// If you want/expect to be moving the component around between parents, use this to register on the parent for signals
/datum/component/proc/RegisterWithParent()
SEND_SIGNAL(src, COMSIG_COMPONENT_REGISTER_PARENT) //CITADEL EDIT
/datum/component/proc/Initialize(...)
return
/datum/component/Destroy(force=FALSE, silent=FALSE)
if(!force && parent)
_RemoveFromParent()
if(!silent)
SEND_SIGNAL(parent, COMSIG_COMPONENT_REMOVING, src)
parent = null
return ..()
/**
* Internal proc to handle behaviour when being removed from a parent
*/
/datum/component/proc/_RemoveFromParent()
var/datum/P = parent
var/list/dc = P.datum_components
@@ -84,9 +122,38 @@
UnregisterFromParent()
/datum/component/proc/UnregisterFromParent()
SEND_SIGNAL(src, COMSIG_COMPONENT_UNREGISTER_PARENT) //CITADEL EDIT
/**
* Register the component with the parent object
*
* Use this proc to register with your parent object
* Overridable proc that's called when added to a new parent
*/
/datum/component/proc/RegisterWithParent()
return
/**
* Unregister from our parent object
*
* Use this proc to unregister from your parent object
* Overridable proc that's called when removed from a parent
* *
*/
/datum/component/proc/UnregisterFromParent()
return
/**
* Register to listen for a signal from the passed in target
*
* This sets up a listening relationship such that when the target object emits a signal
* the source datum this proc is called upon, will recieve a callback to the given proctype
* Return values from procs registered must be a bitfield
*
* Arguments:
* * datum/target The target to listen for signals from
* * sig_type_or_types Either a string signal name, or a list of signal names (strings)
* * proctype The proc to call back when the signal is emitted
* * override If a previous registration exists you must explicitly set this
*/
/datum/proc/RegisterSignal(datum/target, sig_type_or_types, proctype, override = FALSE)
if(QDELETED(src) || QDELETED(target))
return
@@ -119,6 +186,16 @@
signal_enabled = TRUE
/**
* Stop listening to a given signal from target
*
* Breaks the relationship between target and source datum, removing the callback when the signal fires
* Doesn't care if a registration exists or not
*
* Arguments:
* * datum/target Datum to stop listening to signals from
* * sig_typeor_types Signal string key or list of signal keys to stop listening to specifically
*/
/datum/proc/UnregisterSignal(datum/target, sig_type_or_types)
var/list/lookup = target.comp_lookup
if(!signal_procs || !signal_procs[target] || !lookup)
@@ -126,6 +203,8 @@
if(!islist(sig_type_or_types))
sig_type_or_types = list(sig_type_or_types)
for(var/sig in sig_type_or_types)
if(!signal_procs[target][sig])
continue
switch(length(lookup[sig]))
if(2)
lookup[sig] = (lookup[sig]-src)[1]
@@ -148,15 +227,47 @@
if(!signal_procs[target].len)
signal_procs -= target
/**
* Called on a component when a component of the same type was added to the same parent
* See `/datum/component/var/dupe_mode`
* `C`'s type will always be the same of the called component
*/
/datum/component/proc/InheritComponent(datum/component/C, i_am_original)
return
/**
* Called on a component when a component of the same type was added to the same parent with COMPONENT_DUPE_SELECTIVE
* See `/datum/component/var/dupe_mode`
* `C`'s type will always be the same of the called component
* return TRUE if you are absorbing the component, otherwise FALSE if you are fine having it exist as a duplicate component
*/
/datum/component/proc/CheckDupeComponent(datum/component/C, ...)
return
/**
* Callback Just before this component is transferred
*
* Use this to do any special cleanup you might need to do before being deregged from an object
*
*/
/datum/component/proc/PreTransfer()
return
/**
* Callback Just after a component is transferred
*
* Use this to do any special setup you need to do after being moved to a new object
* Do not call `qdel(src)` from this function, `return COMPONENT_INCOMPATIBLE` instead
*
*/
/datum/component/proc/PostTransfer()
return COMPONENT_INCOMPATIBLE //Do not support transfer by default as you must properly support it
/**
* Internal proc to create a list of our type and all parent types
*/
/datum/component/proc/_GetInverseTypeList(our_type = type)
//we can do this one simple trick
var/current_type = parent_type
@@ -166,6 +277,11 @@
current_type = type2parent(current_type)
. += current_type
/**
* Internal proc to handle most all of the signaling procedure
* Will runtime if used on datums with an empty component list
* Use the `SEND_SIGNAL` define instead
*/
/datum/proc/_SendSignal(sigtype, list/arguments)
var/target = comp_lookup[sigtype]
if(!length(target))
@@ -183,9 +299,16 @@
. |= CallAsync(C, proctype, arguments)
// The type arg is casted so initial works, you shouldn't be passing a real instance into this
/**
* Return any component assigned to this datum of the given type
* This will throw an error if it's possible to have more than one component of that type on the parent
*
* Arguments:
* * datum/component/c_type The typepath of the component you want to get a reference to
*/
/datum/proc/GetComponent(datum/component/c_type)
RETURN_TYPE(c_type)
if(initial(c_type.dupe_mode) == COMPONENT_DUPE_ALLOWED)
if(initial(c_type.dupe_mode) == COMPONENT_DUPE_ALLOWED || initial(c_type.dupe_mode) == COMPONENT_DUPE_SELECTIVE)
stack_trace("GetComponent was called to get a component of which multiple copies could be on an object. This can easily break and should be changed. Type: \[[c_type]\]")
var/list/dc = datum_components
if(!dc)
@@ -194,7 +317,18 @@
if(length(.))
return .[1]
/datum/proc/GetExactComponent(c_type)
// The type arg is casted so initial works, you shouldn't be passing a real instance into this
/**
* Return any component assigned to this datum of the exact given type
* This will throw an error if it's possible to have more than one component of that type on the parent
*
* Arguments:
* * datum/component/c_type The typepath of the component you want to get a reference to
*/
/datum/proc/GetExactComponent(datum/component/c_type)
RETURN_TYPE(c_type)
if(initial(c_type.dupe_mode) == COMPONENT_DUPE_ALLOWED || initial(c_type.dupe_mode) == COMPONENT_DUPE_SELECTIVE)
stack_trace("GetComponent was called to get a component of which multiple copies could be on an object. This can easily break and should be changed. Type: \[[c_type]\]")
var/list/dc = datum_components
if(!dc)
return null
@@ -206,6 +340,12 @@
return C
return null
/**
* Get all components of a given type that are attached to this datum
*
* Arguments:
* * c_type The component type path
*/
/datum/proc/GetComponents(c_type)
var/list/dc = datum_components
if(!dc)
@@ -214,7 +354,15 @@
if(!length(.))
return list(.)
/datum/proc/AddComponent(new_type, ...)
/**
* Creates an instance of `new_type` in the datum and attaches to it as parent
* Sends the `COMSIG_COMPONENT_ADDED` signal to the datum
* Returns the component that was created. Or the old component in a dupe situation where `COMPONENT_DUPE_UNIQUE` was set
* If this tries to add an component to an incompatible type, the component will be deleted and the result will be `null`. This is very unperformant, try not to do it
* Properly handles duplicate situations based on the `dupe_mode` var
*/
/datum/proc/_AddComponent(list/raw_args)
var/new_type = raw_args[1]
var/datum/component/nt = new_type
var/dm = initial(nt.dupe_mode)
var/dt = initial(nt.dupe_type)
@@ -229,7 +377,7 @@
new_comp = nt
nt = new_comp.type
args[1] = src
raw_args[1] = src
if(dm != COMPONENT_DUPE_ALLOWED)
if(!dt)
@@ -240,37 +388,62 @@
switch(dm)
if(COMPONENT_DUPE_UNIQUE)
if(!new_comp)
new_comp = new nt(arglist(args))
new_comp = new nt(raw_args)
if(!QDELETED(new_comp))
old_comp.InheritComponent(new_comp, TRUE)
QDEL_NULL(new_comp)
if(COMPONENT_DUPE_HIGHLANDER)
if(!new_comp)
new_comp = new nt(arglist(args))
new_comp = new nt(raw_args)
if(!QDELETED(new_comp))
new_comp.InheritComponent(old_comp, FALSE)
QDEL_NULL(old_comp)
if(COMPONENT_DUPE_UNIQUE_PASSARGS)
if(!new_comp)
var/list/arguments = args.Copy(2)
old_comp.InheritComponent(null, TRUE, arguments)
var/list/arguments = raw_args.Copy(2)
arguments.Insert(1, null, TRUE)
old_comp.InheritComponent(arglist(arguments))
else
old_comp.InheritComponent(new_comp, TRUE)
if(COMPONENT_DUPE_SELECTIVE)
var/list/arguments = raw_args.Copy()
arguments[1] = new_comp
var/make_new_component = TRUE
for(var/i in GetComponents(new_type))
var/datum/component/C = i
if(C.CheckDupeComponent(arglist(arguments)))
make_new_component = FALSE
QDEL_NULL(new_comp)
break
if(!new_comp && make_new_component)
new_comp = new nt(raw_args)
else if(!new_comp)
new_comp = new nt(arglist(args)) // There's a valid dupe mode but there's no old component, act like normal
new_comp = new nt(raw_args) // There's a valid dupe mode but there's no old component, act like normal
else if(!new_comp)
new_comp = new nt(arglist(args)) // Dupes are allowed, act like normal
new_comp = new nt(raw_args) // Dupes are allowed, act like normal
if(!old_comp && !QDELETED(new_comp)) // Nothing related to duplicate components happened and the new component is healthy
SEND_SIGNAL(src, COMSIG_COMPONENT_ADDED, new_comp)
return new_comp
return old_comp
/**
* Get existing component of type, or create it and return a reference to it
*
* Use this if the item needs to exist at the time of this call, but may not have been created before now
*
* Arguments:
* * component_type The typepath of the component to create or return
* * ... additional arguments to be passed when creating the component if it does not exist
*/
/datum/proc/LoadComponent(component_type, ...)
. = GetComponent(component_type)
if(!.)
return AddComponent(arglist(args))
return _AddComponent(args)
/**
* Removes the component from parent, ends up with a null parent
*/
/datum/component/proc/RemoveComponent()
if(!parent)
return
@@ -280,6 +453,14 @@
parent = null
SEND_SIGNAL(old_parent, COMSIG_COMPONENT_REMOVING, src)
/**
* Transfer this component to another parent
*
* Component is taken from source datum
*
* Arguments:
* * datum/component/target Target datum to transfer to
*/
/datum/proc/TakeComponent(datum/component/target)
if(!target || target.parent == src)
return
@@ -296,6 +477,14 @@
if(target == AddComponent(target))
target._JoinParent()
/**
* Transfer all components to target
*
* All components from source datum are taken
*
* Arguments:
* * /datum/target the target to move the components to
*/
/datum/proc/TransferComponents(datum/target)
var/list/dc = datum_components
if(!dc)
@@ -310,5 +499,8 @@
if(C.can_transfer)
target.TakeComponent(comps)
/**
* Return the object that is the host of any UI's that this component has
*/
/datum/component/ui_host()
return parent
+7 -10
View File
@@ -30,26 +30,23 @@
return ..()
/datum/component/fantasy/RegisterWithParent()
. = ..()
var/obj/item/master = parent
originalName = master.name
modify()
/datum/component/fantasy/UnregisterFromParent()
. = ..()
unmodify()
/datum/component/fantasy/InheritComponent(datum/component/fantasy/newComp, original, list/arguments)
/datum/component/fantasy/InheritComponent(datum/component/fantasy/newComp, original, quality, list/affixes, canFail, announce)
unmodify()
if(newComp)
quality += newComp.quality
canFail = newComp.canFail
announce = newComp.announce
src.quality += newComp.quality
src.canFail = newComp.canFail
src.announce = newComp.announce
else
arguments.len = 5 // This is done to replicate what happens when an arglist smaller than the necessary arguments is given
quality += arguments[1]
canFail = arguments[4] || canFail
announce = arguments[5] || announce
src.quality += quality
src.canFail = canFail || src.canFail
src.announce = announce || src.announce
modify()
/datum/component/fantasy/proc/randomQuality()
+2 -2
View File
@@ -101,11 +101,11 @@
host_mob = null
return ..()
/datum/component/nanites/InheritComponent(datum/component/nanites/new_nanites, i_am_original, list/arguments)
/datum/component/nanites/InheritComponent(datum/component/nanites/new_nanites, i_am_original, amount, cloud)
if(new_nanites)
adjust_nanites(null, new_nanites.nanite_volume)
else
adjust_nanites(null, arguments[1]) //just add to the nanite volume
adjust_nanites(null, amount) //just add to the nanite volume
/datum/component/nanites/process()
adjust_nanites(null, regen_rate)
+3 -3
View File
@@ -41,9 +41,9 @@
orbiters = null
return ..()
/datum/component/orbiter/InheritComponent(datum/component/orbiter/newcomp, original, list/arguments)
if(arguments)
begin_orbit(arglist(arguments))
/datum/component/orbiter/InheritComponent(datum/component/orbiter/newcomp, original, atom/movable/orbiter, radius, clockwise, rotation_speed, rotation_segments, pre_rotation)
if(!newcomp)
begin_orbit(arglist(args.Copy(3)))
return
// The following only happens on component transfers
orbiters += newcomp.orbiters
+2 -2
View File
@@ -47,7 +47,7 @@
if(strength <= RAD_BACKGROUND_RADIATION)
return PROCESS_KILL
/datum/component/radioactive/InheritComponent(datum/component/C, i_am_original, list/arguments)
/datum/component/radioactive/InheritComponent(datum/component/C, i_am_original, _strength, _source, _half_life, _can_contaminate)
if(!i_am_original)
return
if(!hl3_release_date) // Permanently radioactive things don't get to grow stronger
@@ -56,7 +56,7 @@
var/datum/component/radioactive/other = C
strength = max(strength, other.strength)
else
strength = max(strength, arguments[1])
strength = max(strength, _strength)
/datum/component/radioactive/proc/rad_examine(datum/source, mob/user, list/examine_list)
var/atom/master = parent
+15 -5
View File
@@ -53,11 +53,21 @@ handles linking back and forth.
/datum/component/remote_materials/proc/_MakeLocal()
silo = null
mat_container = parent.AddComponent(/datum/component/material_container,
list(/datum/material/iron, /datum/material/glass, /datum/material/silver, /datum/material/gold, /datum/material/diamond, /datum/material/plasma, /datum/material/uranium, /datum/material/bananium, /datum/material/titanium, /datum/material/bluespace, /datum/material/plastic),
local_size,
FALSE,
/obj/item/stack)
var/static/list/allowed_mats = list(
/datum/material/iron,
/datum/material/glass,
/datum/material/silver,
/datum/material/gold,
/datum/material/diamond,
/datum/material/plasma,
/datum/material/uranium,
/datum/material/bananium,
/datum/material/titanium,
/datum/material/bluespace,
/datum/material/plastic,
)
mat_container = parent.AddComponent(/datum/component/material_container, allowed_mats, local_size, allowed_types=/obj/item/stack)
/datum/component/remote_materials/proc/set_local_size(size)
local_size = size
+1 -1
View File
@@ -341,7 +341,7 @@
var/mob/living/parent
var/selfdeleting = FALSE
/obj/item/riding_offhand/dropped()
/obj/item/riding_offhand/dropped(mob/user)
selfdeleting = TRUE
. = ..()
+4 -4
View File
@@ -15,13 +15,13 @@
src.allow_death = allow_death
check_in_bounds() // Just in case something is being created outside of station/centcom
/datum/component/stationloving/InheritComponent(datum/component/stationloving/newc, original, list/arguments)
/datum/component/stationloving/InheritComponent(datum/component/stationloving/newc, original, inform_admins, allow_death)
if (original)
if (istype(newc))
if (newc)
inform_admins = newc.inform_admins
allow_death = newc.allow_death
else if (LAZYLEN(arguments))
inform_admins = arguments[1]
else
inform_admins = inform_admins
/datum/component/stationloving/proc/relocate()
var/targetturf = find_safe_turf()
+2 -2
View File
@@ -43,13 +43,13 @@
master.cut_overlay(overlay)
return ..()
/datum/component/thermite/InheritComponent(datum/component/thermite/newC, i_am_original, list/arguments)
/datum/component/thermite/InheritComponent(datum/component/thermite/newC, i_am_original, _amount)
if(!i_am_original)
return
if(newC)
amount += newC.amount
else
amount += arguments[1]
amount += _amount
/datum/component/thermite/proc/thermite_melt(mob/user)
var/turf/master = parent
+6 -2
View File
@@ -28,7 +28,7 @@ GLOBAL_LIST_EMPTY(uplinks)
var/compact_mode = FALSE
var/debug = FALSE
/datum/component/uplink/Initialize(_owner, _lockable = TRUE, _enabled = FALSE, datum/game_mode/_gamemode, starting_tc = 20, datum/ui_state/_checkstate)
/datum/component/uplink/Initialize(_owner, _lockable = TRUE, _enabled = FALSE, datum/game_mode/_gamemode, starting_tc = 20, datum/ui_state/_checkstate, datum/traitor_class/traitor_class)
if(!isitem(parent))
return COMPONENT_INCOMPATIBLE
@@ -47,7 +47,11 @@ GLOBAL_LIST_EMPTY(uplinks)
RegisterSignal(parent, COMSIG_PEN_ROTATED, .proc/pen_rotation)
GLOB.uplinks += src
uplink_items = get_uplink_items(gamemode, TRUE, allow_restricted)
var/list/filters = list()
if(istype(traitor_class))
filters = traitor_class.uplink_filters
starting_tc = traitor_class.TC
uplink_items = get_uplink_items(gamemode, TRUE, allow_restricted, filters)
if(_owner)
owner = _owner
+2 -2
View File
@@ -12,9 +12,9 @@
var/permanent = FALSE
var/last_process = 0
/datum/component/wet_floor/InheritComponent(datum/newcomp, orig, argslist)
/datum/component/wet_floor/InheritComponent(datum/newcomp, orig, strength, duration_minimum, duration_add, duration_maximum, _permanent)
if(!newcomp) //We are getting passed the arguments of a would-be new component, but not a new component
add_wet(arglist(argslist))
add_wet(arglist(args.Copy(3)))
else //We are being passed in a full blown component
var/datum/component/wet_floor/WF = newcomp //Lets make an assumption
if(WF.gc()) //See if it's even valid, still. Also does LAZYLEN and stuff for us.
-1
View File
@@ -287,7 +287,6 @@
G.fields["gender"] = "Other"
L.fields["blood_type"] = H.dna.blood_type
L.fields["b_dna"] = H.dna.unique_enzymes
L.fields["enzymes"] = H.dna.struc_enzymes
L.fields["identity"] = H.dna.uni_identity
L.fields["species"] = H.dna.species.type
L.fields["features"] = H.dna.features
+48 -5
View File
@@ -1,12 +1,42 @@
/**
* The absolute base class for everything
*
* A datum instantiated has no physical world prescence, use an atom if you want something
* that actually lives in the world
*
* Be very mindful about adding variables to this class, they are inherited by every single
* thing in the entire game, and so you can easily cause memory usage to rise a lot with careless
* use of variables at this level
*/
/datum
/**
* Tick count time when this object was destroyed.
*
* If this is non zero then the object has been garbage collected and is awaiting either
* a hard del by the GC subsystme, or to be autocollected (if it has no references)
*/
var/gc_destroyed //Time when this object was destroyed.
var/list/active_timers //for SStimer
var/list/datum_components //for /datum/components
/// Active timers with this datum as the target
var/list/active_timers
/// Status traits attached to this datum
var/list/status_traits
/// Components attached to this datum
/// Lazy associated list in the structure of `type:component/list of components`
var/list/datum_components
/// Any datum registered to receive signals from this datum is in this list
/// Lazy associated list in the structure of `signal:registree/list of registrees`
var/list/comp_lookup //it used to be for looking up components which had registered a signal but now anything can register
/// Lazy associated list in the structure of `signals:proctype` that are run when the datum receives that signal
var/list/list/datum/callback/signal_procs
/// Is this datum capable of sending signals?
/// Set to true when a signal has been registered
var/signal_enabled = FALSE
/// Datum level flags
var/datum_flags = NONE
/// A weak reference to another datum
var/datum/weakref/weak_reference
#ifdef TESTING
@@ -18,9 +48,22 @@
var/list/cached_vars
#endif
// Default implementation of clean-up code.
// This should be overridden to remove all references pointing to the object being destroyed.
// Return the appropriate QDEL_HINT; in most cases this is QDEL_HINT_QUEUE.
/**
* Default implementation of clean-up code.
*
* This should be overridden to remove all references pointing to the object being destroyed, if
* you do override it, make sure to call the parent and return it's return value by default
*
* Return an appropriate QDEL_HINT to modify handling of your deletion;
* in most cases this is QDEL_HINT_QUEUE.
*
* The base case is responsible for doing the following
* * Erasing timers pointing to this datum
* * Erasing compenents on this datum
* * Notifying datums listening to signals from this datum that we are going away
*
* Returns QDEL_HINT_QUEUE
*/
/datum/proc/Destroy(force=FALSE, ...)
tag = null
datum_flags &= ~DF_USE_TAG //In case something tries to REF us
@@ -61,12 +61,12 @@ Bonus
symptom_delay_max = 60
if(A.properties["resistance"] >= 8) //mutate twice
power = 2
possible_mutations = (GLOB.bad_mutations | GLOB.not_good_mutations) - GLOB.mutations_list[RACEMUT]
possible_mutations = (GLOB.bad_mutations | GLOB.not_good_mutations) - GLOB.all_mutations[RACEMUT]
var/mob/living/carbon/M = A.affected_mob
if(M)
if(!M.has_dna())
return
archived_dna = M.dna.struc_enzymes
archived_dna = M.dna.mutation_index
// Give them back their old DNA when cured.
/datum/symptom/genetic_mutation/End(datum/disease/advance/A)
@@ -77,5 +77,5 @@ Bonus
if(M && archived_dna)
if(!M.has_dna())
return
M.dna.struc_enzymes = archived_dna
M.dna.mutation_index = archived_dna
M.domutcheck()
@@ -257,6 +257,8 @@
/datum/symptom/heal/coma/End(datum/disease/advance/A)
if(!..())
return
if(active_coma)
uncoma()
REMOVE_TRAIT(A.affected_mob, TRAIT_NOCRITDAMAGE, DISEASE_TRAIT)
/datum/symptom/heal/coma/CanHeal(datum/disease/advance/A)
@@ -277,7 +279,7 @@
/datum/symptom/heal/coma/proc/coma(mob/living/M)
if(deathgasp)
M.emote("deathgasp")
M.fakedeath("regenerative_coma")
M.fakedeath("regenerative_coma", TRUE)
M.update_stat()
M.update_mobility()
addtimer(CALLBACK(src, .proc/uncoma, M), 300)
+243 -59
View File
@@ -2,7 +2,6 @@
/////////////////////////// DNA DATUM
/datum/dna
var/unique_enzymes
var/struc_enzymes
var/uni_identity
var/blood_type
var/datum/species/species = new /datum/species/human //The type of mutant race the player is if applicable (i.e. potato-man)
@@ -11,10 +10,13 @@
var/nameless = FALSE
var/custom_species //siiiiigh I guess this is important
var/list/mutations = list() //All mutations are from now on here
var/list/temporary_mutations = list() //Timers for temporary mutations
var/list/temporary_mutations = list() //Temporary changes to the UE
var/list/previous = list() //For temporary name/ui/ue/blood_type modifications
var/mob/living/holder
var/delete_species = TRUE //Set to FALSE when a body is scanned by a cloner to fix #38875
var/mutation_index[DNA_MUTATION_BLOCKS] //List of which mutations this carbon has and its assigned block
var/stability = 100
var/scrambled = FALSE //Did we take something like mutagen? In that case we cant get our genes scanned to instantly cheese all the powers.
/datum/dna/New(mob/living/new_holder)
if(istype(new_holder))
@@ -42,8 +44,8 @@
destination.dna.unique_enzymes = unique_enzymes
destination.dna.uni_identity = uni_identity
destination.dna.blood_type = blood_type
destination.dna.features = features.Copy()
destination.set_species(species.type, icon_update=0)
destination.dna.features = features.Copy()
destination.dna.real_name = real_name
destination.dna.nameless = nameless
destination.dna.custom_species = custom_species
@@ -52,13 +54,13 @@
var/mob/living/carbon/human/H = destination
H.give_genitals(TRUE)//This gives the body the genitals of this DNA. Used for any transformations based on DNA
if(transfer_SE)
destination.dna.struc_enzymes = struc_enzymes
destination.dna.mutation_index = mutation_index
SEND_SIGNAL(destination, COMSIG_CARBON_IDENTITY_TRANSFERRED_TO, src, transfer_SE)
/datum/dna/proc/copy_dna(datum/dna/new_dna)
new_dna.unique_enzymes = unique_enzymes
new_dna.struc_enzymes = struc_enzymes
new_dna.mutation_index = mutation_index
new_dna.uni_identity = uni_identity
new_dna.blood_type = blood_type
new_dna.features = features.Copy()
@@ -68,26 +70,32 @@
new_dna.custom_species = custom_species
new_dna.mutations = mutations.Copy()
/datum/dna/proc/add_mutation(mutation_name)
var/datum/mutation/human/HM = GLOB.mutations_list[mutation_name]
HM.on_acquiring(holder)
//See mutation.dm for what 'class' does. 'time' is time till it removes itself in decimals. 0 for no timer
/datum/dna/proc/add_mutation(mutation, class = MUT_OTHER, time)
var/mutation_type = mutation
if(istype(mutation, /datum/mutation/human))
var/datum/mutation/human/HM = mutation
mutation_type = HM.type
if(get_mutation(mutation_type))
return
return force_give(new mutation_type (class, time, copymut = mutation))
/datum/dna/proc/remove_mutation(mutation_name)
var/datum/mutation/human/HM = GLOB.mutations_list[mutation_name]
HM.on_losing(holder)
/datum/dna/proc/remove_mutation(mutation_type)
return force_lose(get_mutation(mutation_type))
/datum/dna/proc/check_mutation(mutation_name)
var/datum/mutation/human/HM = GLOB.mutations_list[mutation_name]
return mutations.Find(HM)
/datum/dna/proc/check_mutation(mutation_type)
return get_mutation(mutation_type)
/datum/dna/proc/remove_all_mutations()
remove_mutation_group(mutations)
/datum/dna/proc/remove_all_mutations(list/classes = list(MUT_NORMAL, MUT_EXTRA, MUT_OTHER), mutadone = FALSE)
remove_mutation_group(mutations, classes, mutadone)
scrambled = FALSE
/datum/dna/proc/remove_mutation_group(list/group)
/datum/dna/proc/remove_mutation_group(list/group, list/classes = list(MUT_NORMAL, MUT_EXTRA, MUT_OTHER), mutadone = FALSE)
if(!group)
return
for(var/datum/mutation/human/HM in group)
HM.force_lose(holder)
if((HM.class in classes) && !(HM.mutadone_proof && mutadone))
force_lose(HM)
/datum/dna/proc/generate_uni_identity()
. = ""
@@ -137,19 +145,49 @@
. += random_string(DNA_BLOCK_SIZE,GLOB.hex_characters)
return .
/datum/dna/proc/generate_struc_enzymes()
var/list/sorting = new /list(DNA_STRUC_ENZYMES_BLOCKS)
var/result = ""
for(var/datum/mutation/human/A in GLOB.good_mutations + GLOB.bad_mutations + GLOB.not_good_mutations)
if(A.name == RACEMUT && ismonkey(holder))
sorting[A.dna_block] = num2hex(A.lowest_value + rand(0, 256 * 6), DNA_BLOCK_SIZE)
mutations |= A
else
sorting[A.dna_block] = random_string(DNA_BLOCK_SIZE, list("0","1","2","3","4","5","6"))
/datum/dna/proc/generate_dna_blocks()
var/bonus
if(species && species.inert_mutation)
bonus = GET_INITIALIZED_MUTATION(species.inert_mutation)
var/list/mutations_temp = GLOB.good_mutations + GLOB.bad_mutations + GLOB.not_good_mutations + bonus
if(!LAZYLEN(mutations_temp))
return
mutation_index.Cut()
shuffle_inplace(mutations_temp)
if(ismonkey(holder))
mutations |= new RACEMUT(MUT_NORMAL)
mutation_index[RACEMUT] = GET_SEQUENCE(RACEMUT)
else
mutation_index[RACEMUT] = create_sequence(RACEMUT, FALSE)
for(var/i in 2 to DNA_MUTATION_BLOCKS)
var/datum/mutation/human/M = mutations_temp[i]
mutation_index[M.type] = create_sequence(M.type, FALSE,M.difficulty)
shuffle_inplace(mutation_index)
for(var/B in sorting)
result += B
return result
//Used to generate original gene sequences for every mutation
/proc/generate_gene_sequence(length=4)
var/static/list/active_sequences = list("AT","TA","GC","CG")
var/sequence
for(var/i in 1 to length*DNA_SEQUENCE_LENGTH)
sequence += pick(active_sequences)
return sequence
//Used to create a chipped gene sequence
/proc/create_sequence(mutation, active, difficulty)
if(!difficulty)
var/datum/mutation/human/A = GET_INITIALIZED_MUTATION(mutation) //leaves the possibility to change difficulty mid-round
if(!A)
return
difficulty = A.difficulty
difficulty += rand(-2,4)
var/sequence = GET_SEQUENCE(mutation)
if(active)
return sequence
while(difficulty)
var/randnum = rand(1, length(sequence))
sequence = copytext(sequence, 1, randnum) + "X" + copytext(sequence, randnum+1, length(sequence)+1)
difficulty--
return sequence
/datum/dna/proc/generate_unique_enzymes()
. = ""
@@ -202,12 +240,57 @@
if(DNA_TAUR_BLOCK)
construct_block(GLOB.taur_list.Find(features["taur"]), GLOB.taur_list.len)
//Please use add_mutation or activate_mutation instead
/datum/dna/proc/force_give(datum/mutation/human/HM)
if(holder && HM)
if(HM.class == MUT_NORMAL)
set_se(1, HM)
. = HM.on_acquiring(holder)
if(.)
qdel(HM)
update_instability()
//Use remove_mutation instead
/datum/dna/proc/force_lose(datum/mutation/human/HM)
if(holder && (HM in mutations))
set_se(0, HM)
. = HM.on_losing(holder)
update_instability(FALSE)
return
/datum/dna/proc/is_same_as(datum/dna/D)
if(uni_identity == D.uni_identity && struc_enzymes == D.struc_enzymes && real_name == D.real_name && nameless == D.nameless && custom_species == D.custom_species)
if(uni_identity == D.uni_identity && mutation_index == D.mutation_index && real_name == D.real_name && nameless == D.nameless && custom_species == D.custom_species)
if(species.type == D.species.type && features == D.features && blood_type == D.blood_type)
return 1
return 0
/datum/dna/proc/update_instability(alert=TRUE)
stability = 100
for(var/datum/mutation/human/M in mutations)
if(M.class == MUT_EXTRA)
stability -= M.instability * GET_MUTATION_STABILIZER(M)
if(holder)
var/message
if(alert)
switch(stability)
if(70 to 90)
message = "<span class='warning'>You shiver.</span>"
if(60 to 69)
message = "<span class='warning'>You feel cold.</span>"
if(40 to 59)
message = "<span class='warning'>You feel sick.</span>"
if(20 to 39)
message = "<span class='warning'>It feels like your skin is moving.</span>"
if(1 to 19)
message = "<span class='warning'>You can feel your cells burning.</span>"
if(-INFINITY to 0)
message = "<span class='boldwarning'>You can feel your DNA exploding, we need to do something fast!</span>"
if(stability <= 0)
holder.apply_status_effect(STATUS_EFFECT_DNA_MELT)
if(message)
to_chat(holder, message)
//used to update dna UI, UE, and dna.real_name.
/datum/dna/proc/update_dna_identity()
uni_identity = generate_uni_identity()
@@ -218,7 +301,7 @@
blood_type = newblood_type
unique_enzymes = generate_unique_enzymes()
uni_identity = generate_uni_identity()
struc_enzymes = generate_struc_enzymes()
generate_dna_blocks()
features = random_features(species?.id)
@@ -233,10 +316,10 @@
/datum/dna/stored/check_mutation(mutation_name)
return
/datum/dna/stored/remove_all_mutations()
/datum/dna/stored/remove_all_mutations(list/classes = list(MUT_NORMAL, MUT_EXTRA, MUT_OTHER), mutadone = FALSE)
return
/datum/dna/stored/remove_mutation_group(list/group)
/datum/dna/stored/remove_mutation_group(list/group, list/classes = list(MUT_NORMAL, MUT_EXTRA, MUT_OTHER), mutadone = FALSE)
return
/////////////////////////// DNA MOB-PROCS //////////////////////
@@ -271,6 +354,7 @@
if(icon_update)
update_body()
update_hair()
update_body_parts()
update_mutations_overlay()// no lizard with human hulk overlay please.
@@ -281,7 +365,7 @@
return dna
/mob/living/carbon/human/proc/hardset_dna(ui, se, newreal_name, newblood_type, datum/species/mrace, newfeatures)
/mob/living/carbon/human/proc/hardset_dna(ui, list/mutation_index, newreal_name, newblood_type, datum/species/mrace, newfeatures)
if(newfeatures)
dna.features = newfeatures
@@ -302,11 +386,11 @@
dna.uni_identity = ui
updateappearance(icon_update=0)
if(se)
dna.struc_enzymes = se
if(LAZYLEN(mutation_index))
dna.mutation_index = mutation_index.Copy()
domutcheck()
SEND_SIGNAL(src, COMSIG_HUMAN_HARDSET_DNA, ui, se, newreal_name, newblood_type, mrace, newfeatures)
SEND_SIGNAL(src, COMSIG_HUMAN_HARDSET_DNA, ui, mutation_index, newreal_name, newblood_type, mrace, newfeatures)
if(mrace || newfeatures || ui)
update_body()
@@ -357,19 +441,63 @@
/mob/proc/domutcheck()
return
/mob/living/carbon/domutcheck(force_powers=0) //Set force_powers to 1 to bypass the power chance
/mob/living/carbon/domutcheck()
if(!has_dna())
return
for(var/datum/mutation/human/A in GLOB.good_mutations | GLOB.bad_mutations | GLOB.not_good_mutations)
if(ismob(A.check_block(src, force_powers)))
for(var/mutation in dna.mutation_index)
if(ismob(dna.check_block(mutation)))
return //we got monkeyized/humanized, this mob will be deleted, no need to continue.
update_mutations_overlay()
/datum/dna/proc/check_block(mutation)
var/datum/mutation/human/HM = get_mutation(mutation)
if(check_block_string(mutation))
if(!HM)
. = add_mutation(mutation, MUT_NORMAL)
return
return force_lose(HM)
//Return the active mutation of a type if there is one
/datum/dna/proc/get_mutation(A)
for(var/datum/mutation/human/HM in mutations)
if(HM.type == A)
return HM
/datum/dna/proc/check_block_string(mutation)
if((LAZYLEN(mutation_index) > DNA_MUTATION_BLOCKS) || !(mutation in mutation_index))
return 0
return is_gene_active(mutation)
/datum/dna/proc/is_gene_active(mutation)
return (mutation_index[mutation] == GET_SEQUENCE(mutation))
/datum/dna/proc/set_se(on=TRUE, datum/mutation/human/HM)
if(!HM || !(HM.type in mutation_index) || (LAZYLEN(mutation_index) < DNA_MUTATION_BLOCKS))
return
. = TRUE
if(on)
mutation_index[HM.type] = GET_SEQUENCE(HM.type)
else if(GET_SEQUENCE(HM.type) == mutation_index[HM.type])
mutation_index[HM.type] = create_sequence(HM.type, FALSE, HM.difficulty)
/datum/dna/proc/activate_mutation(mutation) //note that this returns a boolean and not a new mob
if(!mutation)
return FALSE
var/mutation_type = mutation
if(istype(mutation, /datum/mutation/human))
var/datum/mutation/human/M = mutation
mutation_type = M.type
if(!mutation_in_sequence(mutation_type)) //cant activate what we dont have, use add_mutation
return FALSE
add_mutation(mutation, MUT_NORMAL)
return TRUE
/////////////////////////// DNA HELPER-PROCS //////////////////////////////
/proc/getleftblocks(input,blocknumber,blocksize)
if(blocknumber > 1)
return copytext_char(input,1,((blocksize*blocknumber)-(blocksize-1)))
@@ -386,29 +514,47 @@
return 0
return getleftblocks(istring, blocknumber, blocksize) + replacement + getrightblocks(istring, blocknumber, blocksize)
/datum/dna/proc/mutation_in_sequence(mutation)
if(!mutation)
return
if(istype(mutation, /datum/mutation/human))
var/datum/mutation/human/HM = mutation
if(HM.type in mutation_index)
return TRUE
else if(mutation in mutation_index)
return TRUE
/mob/living/carbon/proc/randmut(list/candidates, difficulty = 2)
if(!has_dna())
return
var/datum/mutation/human/num = pick(candidates)
. = num.force_give(src)
var/mutation = pick(candidates)
. = dna.add_mutation(mutation)
/mob/living/carbon/proc/randmutb()
/mob/living/carbon/proc/easy_randmut(quality = POSITIVE + NEGATIVE + MINOR_NEGATIVE, scrambled = TRUE, sequence = TRUE, exclude_monkey = TRUE)
if(!has_dna())
return
var/datum/mutation/human/HM = pick((GLOB.bad_mutations | GLOB.not_good_mutations) - GLOB.mutations_list[RACEMUT])
. = HM.force_give(src)
var/list/mutations = list()
if(quality & POSITIVE)
mutations += GLOB.good_mutations
if(quality & NEGATIVE)
mutations += GLOB.bad_mutations
if(quality & MINOR_NEGATIVE)
mutations += GLOB.not_good_mutations
var/list/possible = list()
for(var/datum/mutation/human/A in mutations)
if((!sequence || dna.mutation_in_sequence(A.type)) && !dna.get_mutation(A.type))
possible += A.type
if(exclude_monkey)
possible.Remove(RACEMUT)
if(LAZYLEN(possible))
var/mutation = pick(possible)
. = dna.activate_mutation(mutation)
if(scrambled)
var/datum/mutation/human/HM = dna.get_mutation(mutation)
if(HM)
HM.scrambled = TRUE
return TRUE
/mob/living/carbon/proc/randmutg()
if(!has_dna())
return
var/datum/mutation/human/HM = pick(GLOB.good_mutations)
. = HM.force_give(src)
/mob/living/carbon/proc/randmutvg()
if(!has_dna())
return
var/datum/mutation/human/HM = pick((GLOB.good_mutations) - GLOB.mutations_list[HULK] - GLOB.mutations_list[DWARFISM])
. = HM.force_give(src)
/mob/living/carbon/proc/randmuti()
if(!has_dna())
@@ -431,9 +577,9 @@
if(!M.has_dna())
return 0
if(se)
for(var/i=1, i<=DNA_STRUC_ENZYMES_BLOCKS, i++)
for(var/i=1, i<=DNA_MUTATION_BLOCKS, i++)
if(prob(probability))
M.dna.struc_enzymes = setblock(M.dna.struc_enzymes, i, random_string(DNA_BLOCK_SIZE, GLOB.hex_characters))
M.dna.generate_dna_blocks()
M.domutcheck()
if(ui)
for(var/i=1, i<=DNA_UNI_IDENTITY_BLOCKS, i++)
@@ -460,3 +606,41 @@
return value
/////////////////////////// DNA HELPER-PROCS
/mob/living/carbon/human/proc/something_horrible()
if(!has_dna()) //shouldn't ever happen anyway so it's just in really weird cases
return
if(dna.stability > 0)
return
var/instability = -dna.stability
dna.remove_all_mutations()
dna.stability = 100
if(prob(max(70-instability,0)))
switch(rand(0,3)) //not complete and utter death
if(0)
monkeyize()
if(1)
gain_trauma(/datum/brain_trauma/severe/paralysis)
if(2)
corgize()
if(3)
to_chat(src, "<span class='notice'>Oh, we actually feel quite alright!</span>")
else
switch(rand(0,3))
if(0)
gib()
if(1)
dust()
if(2)
death()
petrify(INFINITY)
if(3)
if(prob(90))
var/obj/item/bodypart/BP = get_bodypart(pick(BODY_ZONE_CHEST,BODY_ZONE_HEAD))
if(BP)
BP.dismember()
else
gib()
else
set_species(/datum/species/dullahan)
+19 -7
View File
@@ -1,4 +1,11 @@
/**
* A holder for simple behaviour that can be attached to many different types
*
* Only one element of each type is instanced during game init.
* Otherwise acts basically like a lightweight component.
*/
/datum/element
/// Option flags for element behaviour
var/element_flags = NONE
/**
* The index of the first attach argument to consider for duplicate elements
@@ -7,13 +14,17 @@
*/
var/id_arg_index = INFINITY
/// Activates the functionality defined by the element on the given target datum
/datum/element/proc/Attach(datum/target)
SHOULD_CALL_PARENT(1)
if(type == /datum/element)
return ELEMENT_INCOMPATIBLE
if(element_flags & ELEMENT_DETACH)
RegisterSignal(target, COMSIG_PARENT_QDELETING, .proc/Detach, override = TRUE)
/// Deactivates the functionality defines by the element on the given datum
/datum/element/proc/Detach(datum/source, force)
SHOULD_CALL_PARENT(1)
UnregisterSignal(source, COMSIG_PARENT_QDELETING)
/datum/element/Destroy(force)
@@ -24,16 +35,17 @@
//DATUM PROCS
/datum/proc/AddElement(eletype, ...)
var/datum/element/ele = SSdcs.GetElement(arglist(args))
args[1] = src
if(ele.Attach(arglist(args)) == ELEMENT_INCOMPATIBLE)
CRASH("Incompatible [eletype] assigned to a [type]! args: [json_encode(args)]")
/// Finds the singleton for the element type given and attaches it to src
/datum/proc/_AddElement(list/arguments)
var/datum/element/ele = SSdcs.GetElement(arguments)
arguments[1] = src
if(ele.Attach(arglist(arguments)) == ELEMENT_INCOMPATIBLE)
CRASH("Incompatible [arguments[1]] assigned to a [type]! args: [json_encode(args)]")
/**
* Finds the singleton for the element type given and detaches it from src
* You only need additional arguments beyond the type if you're using ELEMENT_BESPOKE
*/
/datum/proc/RemoveElement(eletype, ...)
var/datum/element/ele = SSdcs.GetElement(arglist(args))
/datum/proc/_RemoveElement(list/arguments)
var/datum/element/ele = SSdcs.GetElement(arguments)
ele.Detach(src)
+2 -2
View File
@@ -60,9 +60,9 @@ GLOBAL_LIST_EMPTY(mobs_with_editable_flavor_text) //et tu, hacky code
return
var/msg = replacetext(text, "\n", " ")
if(length_char(msg) <= 40)
examine_list += "<span class='notice'>[html_encode(msg)]</span>"
examine_list += "<span class='notice'>[msg]</span>"
else
examine_list += "<span class='notice'>[html_encode(copytext_char(msg, 1, 37))]... <a href='?src=[REF(src)];show_flavor=[REF(target)]'>More...</span></a>"
examine_list += "<span class='notice'>[copytext_char(msg, 1, 37)]... <a href='?src=[REF(src)];show_flavor=[REF(target)]'>More...</span></a>"
/datum/element/flavor_text/Topic(href, href_list)
. = ..()
@@ -17,9 +17,10 @@
penalty += roundstart_quit_limit - world.time
if(penalty)
penalty += world.realtime
var/maximumRoundEnd = SSautotransfer.starttime + SSautotransfer.voteinterval * SSautotransfer.maxvotes
if(penalty - SSshuttle.realtimeofstart > maximumRoundEnd + SSshuttle.emergencyCallTime + SSshuttle.emergencyDockTime + SSshuttle.emergencyEscapeTime)
penalty = CANT_REENTER_ROUND
if(SSautotransfer.can_fire && SSautotransfer.maxvotes)
var/maximumRoundEnd = SSautotransfer.starttime + SSautotransfer.voteinterval * SSautotransfer.maxvotes
if(penalty - SSshuttle.realtimeofstart > maximumRoundEnd + SSshuttle.emergencyCallTime + SSshuttle.emergencyDockTime + SSshuttle.emergencyEscapeTime)
penalty = CANT_REENTER_ROUND
if(!(M.ckey in timeouts))
timeouts += M.ckey
timeouts[M.ckey] = 0
@@ -41,7 +42,7 @@
return candidates
/mob/proc/can_reenter_round(silent = FALSE)
var/datum/element/ghost_role_eligibility/eli = SSdcs.GetElement(/datum/element/ghost_role_eligibility)
var/datum/element/ghost_role_eligibility/eli = SSdcs.GetElement(list(/datum/element/ghost_role_eligibility))
return eli.can_reenter_round(src,silent)
/datum/element/ghost_role_eligibility/proc/can_reenter_round(var/mob/M,silent = FALSE)
+8 -8
View File
@@ -8,18 +8,18 @@
var/inv_slots
var/proctype //if present, will be invoked on headwear generation.
/datum/element/mob_holder/Attach(datum/target, _worn_state, _alt_worn, _right_hand, _left_hand, _inv_slots = NONE, _proctype)
/datum/element/mob_holder/Attach(datum/target, worn_state, alt_worn, right_hand, left_hand, inv_slots = NONE, proctype)
. = ..()
if(!isliving(target))
return ELEMENT_INCOMPATIBLE
worn_state = _worn_state
alt_worn = _alt_worn
right_hand = _right_hand
left_hand = _left_hand
inv_slots = _inv_slots
proctype = _proctype
src.worn_state = worn_state
src.alt_worn = alt_worn
src.right_hand = right_hand
src.left_hand = left_hand
src.inv_slots = inv_slots
src.proctype = proctype
RegisterSignal(target, COMSIG_CLICK_ALT, .proc/mob_try_pickup)
RegisterSignal(target, COMSIG_PARENT_EXAMINE, .proc/on_examine)
@@ -138,7 +138,7 @@
destination = get_turf(loc)
AM.forceMove(destination)
/obj/item/clothing/head/mob_holder/dropped()
/obj/item/clothing/head/mob_holder/dropped(mob/user)
. = ..()
if(held_mob && isturf(loc))//don't release on soft-drops
release()
@@ -5,23 +5,20 @@
var/id = "" //ID, used by mind/has_martialartcode\game\objects\items\granters.dm:345:error: user.mind.has_martialart: undefined proccode\game\objects\items\granters.dm:345:error: user.mind.has_martialart: undefined proccode\game\objects\items\granters.dm:345:error: user.mind.has_martialart: undefined proccode\game\objects\items\granters.dm:345:error: user.mind.has_martialart: undefined proccode\game\objects\items\granters.dm:345:error: user.mind.has_martialart: undefined proc
var/current_target
var/datum/martial_art/base // The permanent style. This will be null unless the martial art is temporary
var/deflection_chance = 0 //Chance to deflect projectiles
var/reroute_deflection = FALSE //Delete the bullet, or actually deflect it in some direction?
var/block_chance = 0 //Chance to block melee attacks using items while on throw mode.
var/dodge_chance = 0
var/restraining = 0 //used in cqc's disarm_act to check if the disarmed is being restrained and so whether they should be put in a chokehold or not
var/help_verb
var/pacifism_check = TRUE //are the martial arts combos/attacks unable to be used by pacifist.
var/allow_temp_override = TRUE //if this martial art can be overridden by temporary martial arts
/datum/martial_art/proc/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
return 0
return FALSE
/datum/martial_art/proc/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
return 0
return FALSE
/datum/martial_art/proc/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
return 0
return FALSE
/datum/martial_art/proc/can_use(mob/living/carbon/human/H)
return TRUE
@@ -38,51 +35,6 @@
current_target = new_target
streak = ""
/datum/martial_art/proc/basic_hit(mob/living/carbon/human/A,mob/living/carbon/human/D)
var/damage = rand(A.dna.species.punchdamagelow, A.dna.species.punchdamagehigh)
var/atk_verb = A.dna.species.attack_verb
if(D.lying)
atk_verb = "kick"
switch(atk_verb)
if("kick")
A.do_attack_animation(D, ATTACK_EFFECT_KICK)
if("slash")
A.do_attack_animation(D, ATTACK_EFFECT_CLAW)
if("smash")
A.do_attack_animation(D, ATTACK_EFFECT_SMASH)
else
A.do_attack_animation(D, ATTACK_EFFECT_PUNCH)
if(!damage)
playsound(D.loc, A.dna.species.miss_sound, 25, 1, -1)
D.visible_message("<span class='warning'>[A] has attempted to [atk_verb] [D]!</span>", \
"<span class='userdanger'>[A] has attempted to [atk_verb] [D]!</span>", null, COMBAT_MESSAGE_RANGE)
log_combat(A, D, "attempted to [atk_verb]")
return 0
var/obj/item/bodypart/affecting = D.get_bodypart(ran_zone(A.zone_selected))
var/armor_block = D.run_armor_check(affecting, "melee")
playsound(D.loc, A.dna.species.attack_sound, 25, 1, -1)
D.visible_message("<span class='danger'>[A] has [atk_verb]ed [D]!</span>", \
"<span class='userdanger'>[A] has [atk_verb]ed [D]!</span>", null, COMBAT_MESSAGE_RANGE)
D.apply_damage(damage, BRUTE, affecting, armor_block)
log_combat(A, D, "punched")
if((D.stat != DEAD) && damage >= A.dna.species.punchstunthreshold)
D.visible_message("<span class='danger'>[A] has knocked [D] down!!</span>", \
"<span class='userdanger'>[A] has knocked [D] down!</span>")
D.apply_effect(40, EFFECT_KNOCKDOWN, armor_block)
D.forcesay(GLOB.hit_appends)
else if(D.lying)
D.forcesay(GLOB.hit_appends)
return 1
/datum/martial_art/proc/teach(mob/living/carbon/human/H, make_temporary = FALSE)
if(!istype(H) || !H.mind)
return FALSE
@@ -121,3 +73,7 @@
if(help_verb)
H.verbs -= help_verb
return
///Gets called when a projectile hits the owner. Returning anything other than BULLET_ACT_HIT will stop the projectile from hitting the mob.
/datum/martial_art/proc/on_projectile_hit(mob/living/carbon/human/A, obj/item/projectile/P, def_zone)
return BULLET_ACT_HIT
+10 -13
View File
@@ -12,16 +12,16 @@
if(findtext(streak,TORNADO_COMBO))
streak = ""
Tornado(A,D)
return 1
return TRUE
if(findtext(streak,THROWBACK_COMBO))
streak = ""
Throwback(A,D)
return 1
return TRUE
if(findtext(streak,PLASMA_COMBO))
streak = ""
Plasma(A,D)
return 1
return 0
return TRUE
return FALSE
/datum/martial_art/plasma_fist/proc/TornadoAnimate(mob/living/carbon/human/A)
set waitfor = FALSE
@@ -66,23 +66,20 @@
/datum/martial_art/plasma_fist/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
add_to_streak("H",D)
if(check_streak(A,D))
return 1
basic_hit(A,D)
return 1
return TRUE
return FALSE
/datum/martial_art/plasma_fist/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
add_to_streak("D",D)
if(check_streak(A,D))
return 1
basic_hit(A,D)
return 1
return TRUE
return FALSE
/datum/martial_art/plasma_fist/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
add_to_streak("G",D)
if(check_streak(A,D))
return 1
basic_hit(A,D)
return 1
return TRUE
return FALSE
/mob/living/carbon/human/proc/plasma_fist_help()
set name = "Recall Teachings"
+2 -2
View File
@@ -61,8 +61,8 @@
D.throw_at(throwtarget, 4, 2, A)//So stuff gets tossed around at the same time.
D.DefaultCombatKnockdown(60)
if(7,8)
basic_hit(A,D)
return FALSE
if(atk_verb)
log_combat(A, D, "[atk_verb] (Psychotic Brawling)")
return 1
return TRUE
+27 -14
View File
@@ -7,7 +7,6 @@
/datum/martial_art/the_rising_bass
name = "The Rising Bass"
id = MARTIALART_RISINGBASS
dodge_chance = 100
allow_temp_override = FALSE
help_verb = /mob/living/carbon/human/proc/rising_bass_help
var/datum/action/risingbassmove/sidekick = new/datum/action/risingbassmove/sidekick()
@@ -18,24 +17,24 @@
if(findtext(streak,SIDE_KICK_COMBO))
streak = ""
sideKick(A,D)
return 1
return TRUE
if(findtext(streak,SHOULDER_FLIP_COMBO))
streak = ""
shoulderFlip(A,D)
return 1
return TRUE
if(findtext(streak,REPULSE_PUNCH_COMBO))
streak = ""
repulsePunch(A,D)
return 1
return TRUE
if(findtext(streak,FOOT_SMASH_COMBO))
streak = ""
footSmash(A,D)
return 1
return TRUE
if(findtext(streak,DEFT_SWITCH_COMBO))
streak = ""
deftSwitch(A,D)
return 1
return 0
return TRUE
return FALSE
//Repulse Punch - Slams the opponent far away from you.
@@ -94,7 +93,7 @@
D.forceMove(L)
log_combat(A, D, "side kicked (Rising Bass)")
return TRUE
return basic_hit(A,D)
return TRUE
/datum/martial_art/the_rising_bass/proc/shoulderFlip(mob/living/carbon/human/A, mob/living/carbon/human/D)
if(CHECK_MOBILITY(D, MOBILITY_STAND))
@@ -112,7 +111,7 @@
D.forceMove(L)
log_combat(A, D, "shoulder flipped (Rising Bass)")
return TRUE
return basic_hit(A,D)
return FALSE
/datum/martial_art/the_rising_bass/proc/repulsePunch(mob/living/carbon/human/A, mob/living/carbon/human/D)
if(CHECK_MOBILITY(D, MOBILITY_STAND) && repulsecool < world.time)
@@ -127,7 +126,7 @@
log_combat(A, D, "repulse punched (Rising Bass)")
repulsecool = world.time + 3 SECONDS
return TRUE
return basic_hit(A,D)
return FALSE
/datum/martial_art/the_rising_bass/proc/footSmash(mob/living/carbon/human/A, mob/living/carbon/human/D)
if(CHECK_MOBILITY(D, MOBILITY_STAND))
@@ -139,7 +138,7 @@
D.dropItemToGround(D.get_active_held_item())
log_combat(A, D, "foot smashed (Rising Bass)")
return TRUE
return basic_hit(A,D)
return FALSE
/datum/martial_art/the_rising_bass/proc/deftSwitch(mob/living/carbon/human/A, mob/living/carbon/human/D)
if(CHECK_MOBILITY(D, MOBILITY_STAND))
@@ -158,19 +157,19 @@
/datum/martial_art/the_rising_bass/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
add_to_streak("D",D)
if(check_streak(A,D))
return 1
return TRUE
return ..()
/datum/martial_art/the_rising_bass/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
add_to_streak("H",D)
if(check_streak(A,D))
return 1
return TRUE
return ..()
/datum/martial_art/the_rising_bass/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
add_to_streak("G",D)
if(check_streak(A,D))
return 1
return TRUE
return ..()
/datum/martial_art/the_rising_bass/add_to_streak(element,mob/living/carbon/human/D)
@@ -178,6 +177,20 @@
return
. = ..()
/datum/martial_art/the_rising_bass/on_projectile_hit(mob/living/carbon/human/A, obj/item/projectile/P, def_zone)
. = ..()
if(A.incapacitated(FALSE, TRUE)) //NO STUN
return BULLET_ACT_HIT
if(!(A.mobility_flags & MOBILITY_USE)) //NO UNABLE TO USE
return BULLET_ACT_HIT
if(A.dna && A.dna.check_mutation(HULK)) //NO HULK
return BULLET_ACT_HIT
if(!isturf(A.loc)) //NO MOTHERFLIPPIN MECHS!
return BULLET_ACT_HIT
A.visible_message("<span class='danger'>[A] dodges the projectile cleanly, they're immune to ranged weapons!</span>", "<span class='userdanger'>You dodge out of the way of the projectile!</span>")
playsound(get_turf(A), pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 75, TRUE)
return BULLET_ACT_FORCE_PIERCE
/mob/living/carbon/human/proc/rising_bass_help()
set name = "Recall Teachings"
set desc = "Remember the martial techniques of the Rising Bass clan."
+115 -115
View File
@@ -1,141 +1,101 @@
#define WRIST_WRENCH_COMBO "DD"
#define BACK_KICK_COMBO "HG"
#define STOMACH_KNEE_COMBO "GH"
#define HEAD_KICK_COMBO "DHH"
#define ELBOW_DROP_COMBO "HDHDH"
#define STRONG_PUNCH_COMBO "HH"
#define LAUNCH_KICK_COMBO "HD"
#define DROP_KICK_COMBO "HG"
/datum/martial_art/the_sleeping_carp
name = "The Sleeping Carp"
id = MARTIALART_SLEEPINGCARP
deflection_chance = 100
reroute_deflection = TRUE
allow_temp_override = FALSE
help_verb = /mob/living/carbon/human/proc/sleeping_carp_help
var/old_grab_state = null
/datum/martial_art/the_sleeping_carp/proc/check_streak(mob/living/carbon/human/A, mob/living/carbon/human/D)
if(findtext(streak,WRIST_WRENCH_COMBO))
if(findtext(streak,STRONG_PUNCH_COMBO))
streak = ""
wristWrench(A,D)
strongPunch(A,D)
return TRUE
if(findtext(streak,BACK_KICK_COMBO))
if(findtext(streak,LAUNCH_KICK_COMBO))
streak = ""
backKick(A,D)
launchKick(A,D)
return TRUE
if(findtext(streak,STOMACH_KNEE_COMBO))
if(findtext(streak,DROP_KICK_COMBO))
streak = ""
kneeStomach(A,D)
return TRUE
if(findtext(streak,HEAD_KICK_COMBO))
streak = ""
headKick(A,D)
return TRUE
if(findtext(streak,ELBOW_DROP_COMBO))
streak = ""
elbowDrop(A,D)
dropKick(A,D)
return TRUE
return FALSE
/datum/martial_art/the_sleeping_carp/proc/wristWrench(mob/living/carbon/human/A, mob/living/carbon/human/D)
if(CHECK_MOBILITY(D, MOBILITY_USE))
log_combat(A, D, "wrist wrenched (Sleeping Carp)")
A.do_attack_animation(D, ATTACK_EFFECT_PUNCH)
D.visible_message("<span class='warning'>[A] grabs [D]'s wrist and wrenches it sideways!</span>", \
"<span class='userdanger'>[A] grabs your wrist and violently wrenches it to the side!</span>")
playsound(get_turf(A), 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
D.emote("scream")
D.dropItemToGround(D.get_active_held_item())
D.apply_damage(5, BRUTE, pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
D.DefaultCombatKnockdown(60)//CIT CHANGE - makes sleepingcarp use knockdown() for its stuns instead of stun()
return TRUE
return basic_hit(A,D)
///Gnashing Teeth: Harm Harm, consistent 20 force punch on every second harm punch, has a chance to crit
/datum/martial_art/the_sleeping_carp/proc/strongPunch(mob/living/carbon/human/A, mob/living/carbon/human/D)
///this var is so that the strong punch is always aiming for the body part the user is targeting and not trying to apply to the chest before deviating
var/obj/item/bodypart/affecting = D.get_bodypart(ran_zone(A.zone_selected))
A.do_attack_animation(D, ATTACK_EFFECT_PUNCH)
var/atk_verb = pick("precisely kick", "brutally chop", "cleanly hit", "viciously slam")
///this is the critical hit damage added to the attack if it rolls, it starts at 0 because it'll be changed when rolled
var/crit_damage = 0
D.visible_message("<span class='danger'>[A] [atk_verb]s [D]!</span>", \
"<span class='userdanger'>[A] [atk_verb]s you!</span>", null, null, A)
to_chat(A, "<span class='danger'>You [atk_verb] [D]!</span>")
if(prob(10))
crit_damage += 20
playsound(get_turf(D), 'sound/weapons/bite.ogg', 50, TRUE, -1)
D.visible_message("<span class='warning'>[D] sputters blood as the blow strikes them with inhuman force!</span>", "<span class='userdanger'>You are struck with incredible precision by [A]!</span>")
log_combat(A, D, "critcal strong punched (Sleeping Carp)")//log it here because a critical can swing for 40 force and it's important for the sake of how hard they hit
else
playsound(get_turf(D), 'sound/weapons/punch1.ogg', 25, TRUE, -1)
log_combat(A, D, "strong punched (Sleeping Carp)")//so as to not double up on logging
D.apply_damage(20 + crit_damage, BRUTE, affecting)
return
/datum/martial_art/the_sleeping_carp/proc/backKick(mob/living/carbon/human/A, mob/living/carbon/human/D)
if(CHECK_MOBILITY(D, MOBILITY_STAND))
if(A.dir == D.dir)
log_combat(A, D, "back-kicked (Sleeping Carp)")
A.do_attack_animation(D, ATTACK_EFFECT_PUNCH)
D.visible_message("<span class='warning'>[A] kicks [D] in the back!</span>", \
"<span class='userdanger'>[A] kicks you in the back, making you stumble and fall!</span>")
step_to(D,get_step(D,D.dir),1)
D.DefaultCombatKnockdown(80)
playsound(get_turf(D), 'sound/weapons/punch1.ogg', 50, 1, -1)
return TRUE
else
log_combat(A, D, "missed a back-kick (Sleeping Carp) on")
D.visible_message("<span class='warning'>[A] tries to kick [D] in the back, but misses!</span>", \
"<span class='userdanger'>[A] tries to kick you in the back, but misses!</span>")
return basic_hit(A,D)
///Crashing Wave Kick: Harm Disarm combo, throws people seven tiles backwards
/datum/martial_art/the_sleeping_carp/proc/launchKick(mob/living/carbon/human/A, mob/living/carbon/human/D)
A.do_attack_animation(D, ATTACK_EFFECT_KICK)
D.visible_message("<span class='warning'>[A] kicks [D] square in the chest, sending them flying!</span>", \
"<span class='userdanger'>You are kicked square in the chest by [A], sending you flying!</span>", "<span class='hear'>You hear a sickening sound of flesh hitting flesh!</span>", COMBAT_MESSAGE_RANGE, A)
playsound(get_turf(A), 'sound/effects/hit_kick.ogg', 50, TRUE, -1)
var/atom/throw_target = get_edge_target_turf(D, A.dir)
D.throw_at(throw_target, 7, 14, A)
D.apply_damage(15, BRUTE, BODY_ZONE_CHEST)
log_combat(A, D, "launchkicked (Sleeping Carp)")
return
/datum/martial_art/the_sleeping_carp/proc/kneeStomach(mob/living/carbon/human/A, mob/living/carbon/human/D)
if(CHECK_MOBILITY(D, MOBILITY_STAND))
log_combat(A, D, "stomach kneed (Sleeping Carp)")
A.do_attack_animation(D, ATTACK_EFFECT_KICK)
D.visible_message("<span class='warning'>[A] knees [D] in the stomach!</span>", \
"<span class='userdanger'>[A] winds you with a knee in the stomach!</span>")
D.audible_message("<b>[D]</b> gags!")
D.losebreath += 3
D.DefaultCombatKnockdown(40)//CIT CHANGE - makes sleepingcarp use knockdown() for its stuns instead of stun()
playsound(get_turf(D), 'sound/weapons/punch1.ogg', 50, 1, -1)
return TRUE
return basic_hit(A,D)
/datum/martial_art/the_sleeping_carp/proc/headKick(mob/living/carbon/human/A, mob/living/carbon/human/D)
if(CHECK_MOBILITY(D, MOBILITY_STAND))
log_combat(A, D, "head kicked (Sleeping Carp)")
A.do_attack_animation(D, ATTACK_EFFECT_KICK)
D.visible_message("<span class='warning'>[A] kicks [D] in the head!</span>", \
"<span class='userdanger'>[A] kicks you in the jaw!</span>")
D.apply_damage(20, BRUTE, BODY_ZONE_HEAD)
///Keelhaul: Harm Grab combo, knocks people down, deals stamina damage while they're on the floor
/datum/martial_art/the_sleeping_carp/proc/dropKick(mob/living/carbon/human/A, mob/living/carbon/human/D)
A.do_attack_animation(D, ATTACK_EFFECT_KICK)
playsound(get_turf(A), 'sound/effects/hit_kick.ogg', 50, TRUE, -1)
if((D.mobility_flags & MOBILITY_STAND))
D.apply_damage(10, BRUTE, BODY_ZONE_HEAD)
D.DefaultCombatKnockdown(50)
D.adjustStaminaLoss(40) //A cit specific change form the tg port to really punish anyone who tries to stand up
D.visible_message("<span class='warning'>[A] kicks [D] in the head, sending them face first into the floor!</span>", \
"<span class='userdanger'>You are kicked in the head by [A], sending you crashing to the floor!</span>", "<span class='hear'>You hear a sickening sound of flesh hitting flesh!</span>", COMBAT_MESSAGE_RANGE, A)
if(!(D.mobility_flags & MOBILITY_STAND))
D.apply_damage(5, BRUTE, BODY_ZONE_HEAD)
D.adjustStaminaLoss(40)
D.drop_all_held_items()
playsound(get_turf(D), 'sound/weapons/punch1.ogg', 50, 1, -1)
D.DefaultCombatKnockdown(80)//CIT CHANGE - makes sleepingcarp use knockdown() for its stuns instead of stun()
return TRUE
return basic_hit(A,D)
/datum/martial_art/the_sleeping_carp/proc/elbowDrop(mob/living/carbon/human/A, mob/living/carbon/human/D)
if(!CHECK_MOBILITY(D, MOBILITY_STAND))
log_combat(A, D, "elbow dropped (Sleeping Carp)")
A.do_attack_animation(D, ATTACK_EFFECT_PUNCH)
D.visible_message("<span class='warning'>[A] elbow drops [D]!</span>", \
"<span class='userdanger'>[A] piledrives you with their elbow!</span>")
if(D.stat)
D.death() //FINISH HIM!
D.apply_damage(50, BRUTE, BODY_ZONE_CHEST)
playsound(get_turf(D), 'sound/weapons/punch1.ogg', 75, 1, -1)
return TRUE
return basic_hit(A,D)
D.visible_message("<span class='warning'>[A] kicks [D] in the head!</span>", \
"<span class='userdanger'>You are kicked in the head by [A]!</span>", "<span class='hear'>You hear a sickening sound of flesh hitting flesh!</span>", COMBAT_MESSAGE_RANGE, A)
log_combat(A, D, "dropkicked (Sleeping Carp)")
return
/datum/martial_art/the_sleeping_carp/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
if(A.a_intent == INTENT_GRAB && A!=D) // A!=D prevents grabbing yourself
add_to_streak("G",D)
if(check_streak(A,D)) //if a combo is made no grab upgrade is done
return TRUE
old_grab_state = A.grab_state
D.grabbedby(A, 1)
if(old_grab_state == GRAB_PASSIVE)
D.drop_all_held_items()
A.setGrabState(GRAB_AGGRESSIVE) //Instant agressive grab if on grab intent
log_combat(A, D, "grabbed", addition="aggressively")
D.visible_message("<span class='warning'>[A] violently grabs [D]!</span>", \
"<span class='userdanger'>[A] violently grabs you!</span>")
add_to_streak("G",D)
if(check_streak(A,D))
return TRUE
return FALSE
log_combat(A, D, "grabbed (Sleeping Carp)")
return ..()
/datum/martial_art/the_sleeping_carp/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
add_to_streak("H",D)
if(check_streak(A,D))
return TRUE
var/obj/item/bodypart/affecting = D.get_bodypart(ran_zone(A.zone_selected))
A.do_attack_animation(D, ATTACK_EFFECT_PUNCH)
var/atk_verb = pick("punches", "kicks", "chops", "hits", "slams")
D.visible_message("<span class='danger'>[A] [atk_verb] [D]!</span>", \
"<span class='userdanger'>[A] [atk_verb] you!</span>")
D.apply_damage(rand(10,15), BRUTE)
playsound(get_turf(D), 'sound/weapons/punch1.ogg', 25, 1, -1)
if(prob(D.getBruteLoss()) && !D.lying)
D.visible_message("<span class='warning'>[D] stumbles and falls!</span>", "<span class='userdanger'>The blow sends you to the ground!</span>")
D.DefaultCombatKnockdown(80)
log_combat(A, D, "[atk_verb] (Sleeping Carp)")
var/atk_verb = pick("kick", "chop", "hit", "slam")
D.visible_message("<span class='danger'>[A] [atk_verb]s [D]!</span>", \
"<span class='userdanger'>[A] [atk_verb]s you!</span>", null, null, A)
to_chat(A, "<span class='danger'>You [atk_verb] [D]!</span>")
D.apply_damage(rand(10,15), BRUTE, affecting)
playsound(get_turf(D), 'sound/weapons/punch1.ogg', 25, TRUE, -1)
log_combat(A, D, "punched (Sleeping Carp)")
return TRUE
@@ -143,17 +103,58 @@
add_to_streak("D",D)
if(check_streak(A,D))
return TRUE
log_combat(A, D, "disarmed (Sleeping Carp)")
return ..()
/datum/martial_art/the_sleeping_carp/on_projectile_hit(mob/living/carbon/human/A, obj/item/projectile/P, def_zone)
. = ..()
if(A.incapacitated(FALSE, TRUE)) //NO STUN
return BULLET_ACT_HIT
if(!(A.mobility_flags & MOBILITY_USE)) //NO UNABLE TO USE
return BULLET_ACT_HIT
if(A.dna && A.dna.check_mutation(HULK)) //NO HULK
return BULLET_ACT_HIT
if(!isturf(A.loc)) //NO MOTHERFLIPPIN MECHS!
return BULLET_ACT_HIT
if(A.in_throw_mode)
A.visible_message("<span class='danger'>[A] effortlessly swats the projectile aside! They can deflect projectile with their bare hands!</span>", "<span class='userdanger'>You deflect the projectile!</span>")
playsound(get_turf(A), pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 75, TRUE)
P.firer = A
P.setAngle(rand(0, 360))//SHING
return BULLET_ACT_FORCE_PIERCE
return BULLET_ACT_HIT
/datum/martial_art/the_sleeping_carp/teach(mob/living/carbon/human/H, make_temporary = FALSE)
. = ..()
if(!.)
return
ADD_TRAIT(H, TRAIT_NOGUNS, SLEEPING_CARP_TRAIT)
ADD_TRAIT(H, TRAIT_PIERCEIMMUNE, SLEEPING_CARP_TRAIT)
ADD_TRAIT(H, TRAIT_NODISMEMBER, SLEEPING_CARP_TRAIT)
H.physiology.brute_mod *= 0.4 //brute is really not gonna cut it
H.physiology.burn_mod *= 0.7 //burn is distinctly more useful against them than brute but they're still resistant
H.physiology.stamina_mod *= 0.5 //stun batons prove to be one of the few ways to fight them. They have stun resistance already, so I think doubling down too hard on this resistance is a bit much.
H.physiology.stun_mod *= 0.3 //for those rare stuns
H.physiology.pressure_mod *= 0.3 //go hang out with carp
H.physiology.cold_mod *= 0.3 //cold mods are different to burn mods, they do stack however
H.physiology.heat_mod *= 2 //this is mostly so sleeping carp has a viable weakness. Cooking them alive. Setting them on fire and heating them will be their biggest weakness. The reason for this is....filet jokes.
H.faction |= "carp" //:D
/datum/martial_art/the_sleeping_carp/on_remove(mob/living/carbon/human/H)
. = ..()
REMOVE_TRAIT(H, TRAIT_NOGUNS, SLEEPING_CARP_TRAIT)
REMOVE_TRAIT(H, TRAIT_PIERCEIMMUNE, SLEEPING_CARP_TRAIT)
REMOVE_TRAIT(H, TRAIT_NODISMEMBER, SLEEPING_CARP_TRAIT)
H.physiology.brute_mod = initial(H.physiology.brute_mod)
H.physiology.burn_mod = initial(H.physiology.burn_mod)
H.physiology.stamina_mod = initial(H.physiology.stamina_mod)
H.physiology.stun_mod = initial(H.physiology.stun_mod)
H.physiology.pressure_mod = initial(H.physiology.pressure_mod) //no more carpies
H.physiology.cold_mod = initial(H.physiology.cold_mod)
H.physiology.heat_mod = initial(H.physiology.heat_mod)
H.faction -= "carp" //:(
/mob/living/carbon/human/proc/sleeping_carp_help()
set name = "Recall Teachings"
@@ -162,11 +163,10 @@
to_chat(usr, "<b><i>You retreat inward and recall the teachings of the Sleeping Carp...</i></b>")
to_chat(usr, "<span class='notice'>Wrist Wrench</span>: Disarm Disarm. Forces opponent to drop item in hand.")
to_chat(usr, "<span class='notice'>Back Kick</span>: Harm Grab. Opponent must be facing away. Knocks down.")
to_chat(usr, "<span class='notice'>Stomach Knee</span>: Grab Harm. Knocks the wind out of opponent and stuns.")
to_chat(usr, "<span class='notice'>Head Kick</span>: Disarm Harm Harm. Decent damage, forces opponent to drop item in hand.")
to_chat(usr, "<span class='notice'>Elbow Drop</span>: Harm Disarm Harm Disarm Harm. Opponent must be on the ground. Deals huge damage, instantly kills anyone in critical condition.")
to_chat(usr, "<span class='notice'>Gnashing Teeth</span>: Harm Harm. Deal additional damage every second punch, with a chance for even more damage!")
to_chat(usr, "<span class='notice'>Crashing Wave Kick</span>: Harm Disarm. Launch people brutally across rooms, and away from you.")
to_chat(usr, "<span class='notice'>Keelhaul</span>: Harm Grab. Kick opponents to the floor. Against prone targets, deal additional stamina damage and disarm them.")
to_chat(usr, "<span class='notice'>In addition, your body has become incredibly resilient to most forms of attack. Weapons cannot readily pierce your hardened skin, and you are highly resistant to stuns and knockdowns, and can block all projectiles in Throw Mode. However, you are not invincible, and sustained damage will take it's toll. Avoid heat at all costs!</span>")
/obj/item/twohanded/bostaff
name = "bo staff"
+2 -2
View File
@@ -93,12 +93,12 @@ Unless you know what you're doing, only use the first three numbers. They're in
/datum/material/plasma/on_applied(atom/source, amount, material_flags)
. = ..()
if(ismovableatom(source))
source.AddElement(/datum/element/firestacker)
source.AddElement(/datum/element/firestacker, amount=1)
source.AddComponent(/datum/component/explodable, 0, 0, amount / 2500, amount / 1250)
/datum/material/plasma/on_removed(atom/source, material_flags)
. = ..()
source.RemoveElement(/datum/element/firestacker)
source.RemoveElement(/datum/element/firestacker, amount=1)
qdel(source.GetComponent(/datum/component/explodable))
///Can cause bluespace effects on use. (Teleportation) (Not yet implemented)
+8 -6
View File
@@ -256,9 +256,11 @@
remove_rev()
SSticker.mode.update_cult_icons_removed(src)
/datum/mind/proc/equip_traitor(employer = "The Syndicate", silent = FALSE, datum/antagonist/uplink_owner)
/datum/mind/proc/equip_traitor(datum/traitor_class/traitor_class, silent = FALSE, datum/antagonist/uplink_owner)
if(!current)
return
if(!traitor_class)
traitor_class = GLOB.traitor_classes[TRAITOR_HUMAN]
var/mob/living/carbon/human/traitor_mob = current
if (!istype(traitor_mob))
return
@@ -306,21 +308,21 @@
if (!uplink_loc)
if(!silent)
to_chat(traitor_mob, "Unfortunately, [employer] wasn't able to get you an Uplink.")
to_chat(traitor_mob, "Unfortunately, [traitor_class.employer] wasn't able to get you an Uplink.")
. = 0
else
. = uplink_loc
var/datum/component/uplink/U = uplink_loc.AddComponent(/datum/component/uplink, traitor_mob.key)
var/datum/component/uplink/U = uplink_loc.AddComponent(/datum/component/uplink, traitor_mob.key,traitor_class)
if(!U)
CRASH("Uplink creation failed.")
U.setup_unlock_code()
if(!silent)
if(uplink_loc == R)
to_chat(traitor_mob, "[employer] has cunningly disguised a Syndicate Uplink as your [R.name]. Simply dial the frequency [format_frequency(U.unlock_code)] to unlock its hidden features.")
to_chat(traitor_mob, "[traitor_class.employer] has cunningly disguised a Syndicate Uplink as your [R.name]. Simply dial the frequency [format_frequency(U.unlock_code)] to unlock its hidden features.")
else if(uplink_loc == PDA)
to_chat(traitor_mob, "[employer] has cunningly disguised a Syndicate Uplink as your [PDA.name]. Simply enter the code \"[U.unlock_code]\" into the ringtone select to unlock its hidden features.")
to_chat(traitor_mob, "[traitor_class.employer] has cunningly disguised a Syndicate Uplink as your [PDA.name]. Simply enter the code \"[U.unlock_code]\" into the ringtone select to unlock its hidden features.")
else if(uplink_loc == P)
to_chat(traitor_mob, "[employer] has cunningly disguised a Syndicate Uplink as your [P.name]. Simply twist the top of the pen [U.unlock_code] from its starting position to unlock its hidden features.")
to_chat(traitor_mob, "[traitor_class.employer] has cunningly disguised a Syndicate Uplink as your [P.name]. Simply twist the top of the pen [U.unlock_code] from its starting position to unlock its hidden features.")
if(uplink_owner)
uplink_owner.antag_memory += U.unlock_note + "<br>"
+96 -40
View File
@@ -1,58 +1,55 @@
GLOBAL_LIST_EMPTY(mutations_list)
/datum/mutation
var/name
/datum/mutation/New()
GLOB.mutations_list[name] = src
/datum/mutation/human
var/dna_block
name = "mutation"
var/desc = "A mutation."
var/locked
var/quality
var/get_chance = 100
var/lowest_value = 256 * 8
var/text_gain_indication = ""
var/text_lose_indication = ""
var/list/mutable_appearance/visual_indicators = list()
var/obj/effect/proc_holder/spell/power
var/layer_used = MUTATIONS_LAYER //which mutation layer to use
var/list/species_allowed = list() //to restrict mutation to only certain species
var/health_req //minimum health required to acquire the mutation
var/limb_req //required limbs to acquire this mutation
var/time_coeff = 1 //coefficient for timed mutations
var/datum/dna/dna
var/mob/living/carbon/human/owner
var/instability = 0 //instability the holder gets when the mutation is not native
var/blocks = 4 //Amount of those big blocks with gene sequences
var/difficulty = 8 //Amount of missing sequences. Sometimes it removes an entire pair for 2 points
var/timed = FALSE //Boolean to easily check if we're going to self destruct
var/alias //'Mutation #49', decided every round to get some form of distinction between undiscovered mutations
var/scrambled = FALSE //Wheter we can read it if it's active. To avoid cheesing with mutagen
var/class //Decides player accesibility, sorta
//MUT_NORMAL - A mutation that can be activated and deactived by completing a sequence
//MUT_EXTRA - A mutation that is in the mutations tab, and can be given and taken away through though the DNA console. Has a 0 before it's name in the mutation section of the dna console
//MUT_OTHER Cannot be interacted with by players through normal means. I.E. wizards mutate
/datum/mutation/human/proc/force_give(mob/living/carbon/human/owner)
set_block(owner)
. = on_acquiring(owner)
var/can_chromosome = CHROMOSOME_NONE //can we take chromosomes? 0: CHROMOSOME_NEVER never, 1:CHROMOSOME_NONE yeah, 2: CHROMOSOME_USED no, already have one
var/chromosome_name //purely cosmetic
var/modified = FALSE //ugly but we really don't want chromosomes and on_acquiring to overlap and apply double the powers
var/mutadone_proof = FALSE
/datum/mutation/human/proc/force_lose(mob/living/carbon/human/owner)
set_block(owner, 0)
. = on_losing(owner)
//Chromosome stuff - set to -1 to prevent people from changing it. Example: It'd be a waste to decrease cooldown on mutism
var/stabilizer_coeff = 1 //genetic stability coeff
var/synchronizer_coeff = -1 //makes the mutation hurt the user less
var/power_coeff = -1 //boosts mutation strength
var/energy_coeff = -1 //lowers mutation cooldown
/datum/mutation/human/proc/set_se(se_string, on = 1)
if(!se_string || length(se_string) < DNA_STRUC_ENZYMES_BLOCKS * DNA_BLOCK_SIZE)
return
var/before = copytext_char(se_string, 1, ((dna_block - 1) * DNA_BLOCK_SIZE) + 1)
var/injection = num2hex(on ? rand(lowest_value, (256 * 16) - 1) : rand(0, lowest_value - 1), DNA_BLOCK_SIZE)
var/after = copytext_char(se_string, (dna_block * DNA_BLOCK_SIZE) + 1, 0)
return before + injection + after
/datum/mutation/human/proc/set_block(mob/living/carbon/owner, on = 1)
if(owner && owner.has_dna())
owner.dna.struc_enzymes = set_se(owner.dna.struc_enzymes, on)
/datum/mutation/human/proc/check_block_string(se_string)
if(!se_string || length(se_string) < DNA_STRUC_ENZYMES_BLOCKS * DNA_BLOCK_SIZE)
return 0
if(hex2num(getblock(se_string, dna_block)) >= lowest_value)
return 1
/datum/mutation/human/proc/check_block(mob/living/carbon/human/owner, force_powers=0)
if(check_block_string(owner.dna.struc_enzymes))
if(prob(get_chance)||force_powers)
. = on_acquiring(owner)
else
. = on_losing(owner)
/datum/mutation/human/New(class_ = MUT_OTHER, timer, datum/mutation/human/copymut)
. = ..()
class = class_
if(timer)
addtimer(CALLBACK(src, .proc/remove), timer)
timed = TRUE
if(copymut && istype(copymut, /datum/mutation/human))
copy_mutation(copymut)
/datum/mutation/human/proc/on_acquiring(mob/living/carbon/human/owner)
if(!owner || !istype(owner) || owner.stat == DEAD || (src in owner.dna.mutations))
@@ -63,7 +60,8 @@ GLOBAL_LIST_EMPTY(mutations_list)
return TRUE
if(limb_req && !owner.get_bodypart(limb_req))
return TRUE
owner.dna.mutations.Add(src)
dna = owner.dna
dna.mutations += src
if(text_gain_indication)
to_chat(owner, text_gain_indication)
if(visual_indicators.len)
@@ -75,6 +73,10 @@ GLOBAL_LIST_EMPTY(mutations_list)
owner.overlays_standing[layer_used] = mut_overlay
owner.apply_overlay(layer_used)
grant_spell(owner)
if(!modified)
addtimer(CALLBACK(src, .proc/modify, 5)) //gonna want children calling ..() to run first
/datum/mutation/human/proc/get_visual_indicator(mob/living/carbon/human/owner)
return
@@ -102,26 +104,80 @@ GLOBAL_LIST_EMPTY(mutations_list)
mut_overlay.Remove(get_visual_indicator(owner))
owner.overlays_standing[layer_used] = mut_overlay
owner.apply_overlay(layer_used)
if(power)
owner.RemoveSpell(power)
qdel(src)
return 0
return 1
/datum/mutation/human/proc/say_mod(message)
if(message)
return message
/datum/mutation/human/proc/get_spans()
return list()
/mob/living/carbon/proc/update_mutations_overlay()
return
/mob/living/carbon/human/update_mutations_overlay()
for(var/datum/mutation/human/CM in dna.mutations)
if(CM.species_allowed.len && !CM.species_allowed.Find(dna.species.id))
CM.force_lose(src) //shouldn't have that mutation at all
dna.force_lose(CM) //shouldn't have that mutation at all
continue
if(CM.visual_indicators.len)
var/list/mut_overlay = list()
if(overlays_standing[CM.layer_used])
mut_overlay = overlays_standing[CM.layer_used]
var/mutable_appearance/V = CM.get_visual_indicator(src)
var/mutable_appearance/V = CM.get_visual_indicator()
if(!mut_overlay.Find(V)) //either we lack the visual indicator or we have the wrong one
remove_overlay(CM.layer_used)
for(var/mutable_appearance/MA in CM.visual_indicators)
for(var/mutable_appearance/MA in CM.visual_indicators[CM.type])
mut_overlay.Remove(MA)
mut_overlay |= V
overlays_standing[CM.layer_used] = mut_overlay
apply_overlay(CM.layer_used)
/datum/mutation/human/proc/modify() //called when a genome is applied so we can properly update some stats without having to remove and reapply the mutation from someone
if(modified || !power || !owner)
return
power.charge_max *= GET_MUTATION_ENERGY(src)
power.charge_counter *= GET_MUTATION_ENERGY(src)
modified = TRUE
/datum/mutation/human/proc/copy_mutation(datum/mutation/human/HM)
if(!HM)
return
chromosome_name = HM.chromosome_name
stabilizer_coeff = HM.stabilizer_coeff
synchronizer_coeff = HM.synchronizer_coeff
power_coeff = HM.power_coeff
energy_coeff = HM.energy_coeff
mutadone_proof = HM.mutadone_proof
can_chromosome = HM.can_chromosome
/datum/mutation/human/proc/remove_chromosome()
stabilizer_coeff = initial(stabilizer_coeff)
synchronizer_coeff = initial(synchronizer_coeff)
power_coeff = initial(power_coeff)
energy_coeff = initial(energy_coeff)
mutadone_proof = initial(mutadone_proof)
can_chromosome = initial(can_chromosome)
chromosome_name = null
/datum/mutation/human/proc/remove()
if(dna)
dna.force_lose(src)
else
qdel(src)
/datum/mutation/human/proc/grant_spell(mob/living/carbon/human/owner)
if(!ispath(power) || !owner)
return FALSE
power = new power()
power.action_background_icon_state = "bg_tech_blue_on"
power.panel = "Genetic"
owner.AddSpell(power)
return TRUE
+503
View File
@@ -0,0 +1,503 @@
/datum/mutation/human/telepathy
name = "Telepathy"
desc = "A rare mutation that allows the user to telepathically communicate to others."
quality = POSITIVE
text_gain_indication = "<span class='notice'>You can hear your own voice echoing in your mind!</span>"
text_lose_indication = "<span class='notice'>You don't hear your mind echo anymore.</span>"
difficulty = 12
power = /obj/effect/proc_holder/spell/targeted/telepathy/genetic
instability = 10
energy_coeff = 1
/datum/mutation/human/telepathy/on_acquiring(mob/living/carbon/human/owner)
. = ..()
/datum/mutation/human/telepathy/on_losing(mob/living/carbon/human/owner)
. = ..()
/obj/effect/proc_holder/spell/targeted/telepathy/genetic
magic_check = FALSE
/datum/mutation/human/firebreath
name = "Fire Breath"
desc = "An ancient mutation that gives lizards breath of fire."
quality = POSITIVE
difficulty = 12
locked = TRUE
text_gain_indication = "<span class='notice'>Your throat is burning!</span>"
text_lose_indication = "<span class='notice'>Your throat is cooling down.</span>"
power = /obj/effect/proc_holder/spell/aimed/firebreath
instability = 30
energy_coeff = 1
power_coeff = 1
/datum/mutation/human/firebreath/modify()
if(power)
var/obj/effect/proc_holder/spell/aimed/firebreath/S = power
S.strength = 4 + GET_MUTATION_POWER(src)
/obj/effect/proc_holder/spell/aimed/firebreath
name = "Fire Breath"
desc = "You can breathe fire at a target."
school = "evocation"
charge_max = 1200
clothes_req = FALSE
range = 20
base_icon_state = "fireball"
action_icon_state = "fireball0"
sound = 'sound/magic/demon_dies.ogg' //horrifying lizard noises
active_msg = "You built up heat in your mouth."
deactive_msg = "You swallow the flame."
var/strength = 4
/obj/effect/proc_holder/spell/aimed/firebreath/before_cast(list/targets)
. = ..()
if(iscarbon(usr))
var/mob/living/carbon/C = usr
if(C.is_mouth_covered())
C.adjust_fire_stacks(2)
C.IgniteMob()
to_chat(C,"<span class='warning'>Something in front of your mouth caught fire!</span>")
return FALSE
/obj/effect/proc_holder/spell/aimed/firebreath/cast(list/targets, mob/living/user)
var/turf/T = user.loc
if(!isturf(T))
return FALSE
firecone(T,user.dir,strength)
remove_ranged_ability()
charge_counter = 0
start_recharge()
on_deactivation(user)
/proc/firecone(loc,dir,length)
var/addsides = FALSE
var/list/turf/recentturf = list(loc)
for (var/i = 0;i < length;i++)
var/list/turf/h = list()
for (var/turf/g in recentturf)
var/frontturf = get_step(g,dir)
if (addsides)
var/rightturf = get_step(frontturf,turn(dir,90))
var/leftturf = get_step(frontturf,turn(dir,270))
if (!(rightturf in h))
h += rightturf
if (!(leftturf in h))
h += leftturf
if (!(frontturf in h))
h += frontturf
for (var/turf/j in h)
if (j.blocks_air)
h -= j
continue
for (var/obj/o in j)
if (o.CanAtmosPass == ATMOS_PASS_PROC ? !o.CanAtmosPass(loc) : !o.CanAtmosPass)
h -= j
continue
for (var/turf/l in h)
new /obj/effect/hotspot(l)
l.hotspot_expose(700,50,1)
sleep(1)
recentturf = h
addsides = !addsides
/datum/mutation/human/void
name = "Void Magnet"
desc = "A rare genome that attracts odd forces not usually observed."
quality = MINOR_NEGATIVE //upsides and downsides
text_gain_indication = "<span class='notice'>You feel a heavy, dull force just beyond the walls watching you.</span>"
instability = 30
power = /obj/effect/proc_holder/spell/self/void
energy_coeff = 1
synchronizer_coeff = 1
/datum/mutation/human/void/on_life(mob/living/carbon/human/owner)
if(!isturf(owner.loc))
return
if(prob((0.5+((100-dna.stability)/20))) * GET_MUTATION_SYNCHRONIZER(src)) //very rare, but enough to annoy you hopefully. +0.5 probability for every 10 points lost in stability
new /obj/effect/immortality_talisman/void(get_turf(owner), owner)
/obj/effect/proc_holder/spell/self/void
name = "Convoke Void" //magic the gathering joke here
desc = "A rare genome that attracts odd forces not usually observed. May sometimes pull you in randomly."
school = "evocation"
clothes_req = FALSE
charge_max = 600
invocation = "DOOOOOOOOOOOOOOOOOOOOM!!!"
invocation_type = "shout"
action_icon_state = "void_magnet"
var/in_use = FALSE //so it doesnt cast while you are already deep innit
/obj/effect/proc_holder/spell/self/void/can_cast(mob/user = usr)
. = ..()
if(!isturf(user.loc))
return FALSE
/obj/effect/proc_holder/spell/self/void/cast(mob/user = usr)
. = ..()
new /obj/effect/immortality_talisman/void(get_turf(user), user)
/datum/mutation/human/shock
name = "Shock Touch"
desc = "The affected can channel excess electricity through their hands without shocking themselves, allowing them to shock others."
quality = POSITIVE
locked = TRUE
difficulty = 16
text_gain_indication = "<span class='notice'>You feel power flow through your hands.</span>"
text_lose_indication = "<span class='notice'>The energy in your hands subsides.</span>"
power = /obj/effect/proc_holder/spell/targeted/touch/shock
instability = 30
/obj/effect/proc_holder/spell/targeted/touch/shock
name = "Shock Touch"
desc = "Channel electricity to your hand to shock people with."
drawmessage = "You channel electricity into your hand."
dropmessage = "You let the electricity from your hand dissipate."
hand_path = /obj/item/melee/touch_attack/shock
charge_max = 400
clothes_req = FALSE
action_icon_state = "zap"
/obj/item/melee/touch_attack/shock
name = "\improper shock touch"
desc = "This is kind of like when you rub your feet on a shag rug so you can zap your friends, only a lot less safe."
catchphrase = null
on_use_sound = 'sound/weapons/zapbang.ogg'
icon_state = "zapper"
item_state = "zapper"
/obj/item/melee/touch_attack/shock/afterattack(atom/target, mob/living/carbon/user, proximity)
if(!proximity || !isliving(target))
return
if(iscarbon(target))
var/mob/living/carbon/C = target
if(C.electrocute_act(15, user, 1, SHOCK_NOSTUN))//doesnt stun. never let this stun
C.dropItemToGround(C.get_active_held_item())
C.dropItemToGround(C.get_inactive_held_item())
C.confused += 10
C.visible_message("<span class='danger'>[user] electrocutes [target]!</span>","<span class='userdanger'>[user] electrocutes you!</span>")
return ..()
else
user.visible_message("<span class='warning'>[user] fails to electrocute [target]!</span>")
return ..()
else if(isliving(target))
var/mob/living/L = target
L.electrocute_act(15, user, 1, SHOCK_NOSTUN)
L.visible_message("<span class='danger'>[user] electrocutes [target]!</span>","<span class='userdanger'>[user] electrocutes you!</span>")
return ..()
else
to_chat(user,"<span class='warning'>The electricity doesn't seem to affect [target]...</span>")
return ..()
/datum/mutation/human/olfaction
name = "Transcendent Olfaction"
desc = "Your sense of smell is comparable to that of a canine."
quality = POSITIVE
difficulty = 12
text_gain_indication = "<span class='notice'>Smells begin to make more sense...</span>"
text_lose_indication = "<span class='notice'>Your sense of smell goes back to normal.</span>"
power = /obj/effect/proc_holder/spell/targeted/olfaction
instability = 30
synchronizer_coeff = 1
var/reek = 200
/datum/mutation/human/olfaction/modify()
if(power)
var/obj/effect/proc_holder/spell/targeted/olfaction/S = power
S.sensitivity = GET_MUTATION_SYNCHRONIZER(src)
/obj/effect/proc_holder/spell/targeted/olfaction
name = "Remember the Scent"
desc = "Get a scent off of the item you're currently holding to track it. With an empty hand, you'll track the scent you've remembered."
charge_max = 100
clothes_req = FALSE
range = -1
include_user = TRUE
action_icon_state = "nose"
var/mob/living/carbon/tracking_target
var/list/mob/living/carbon/possible = list()
var/sensitivity = 1
/obj/effect/proc_holder/spell/targeted/olfaction/cast(list/targets, mob/living/user = usr)
//can we sniff? is there miasma in the air?
var/datum/gas_mixture/air = user.loc.return_air()
var/list/cached_gases = air.gases
if(cached_gases[/datum/gas/miasma])
user.adjust_disgust(sensitivity * 45)
to_chat(user, "<span class='warning'>With your overly sensitive nose, you get a whiff of stench and feel sick! Try moving to a cleaner area!</span>")
return
var/atom/sniffed = user.get_active_held_item()
if(sniffed)
var/old_target = tracking_target
possible = list()
for(var/mob/living/carbon/human/H in GLOB.alive_mob_list)
if(md5(H.dna.uni_identity) in sniffed.fingerprints)
possible |= H
if(!length(possible))
to_chat(user,"<span class='warning'>Despite your best efforts, there are no scents to be found on [sniffed]...</span>")
return
tracking_target = input(user, "Choose a scent to remember.", "Scent Tracking") as null|anything in sortNames(possible)
if(!tracking_target)
if(!old_target)
to_chat(user,"<span class='warning'>You decide against remembering any scents. Instead, you notice your own nose in your peripheral vision. This goes on to remind you of that one time you started breathing manually and couldn't stop. What an awful day that was.</span>")
return
tracking_target = old_target
on_the_trail(user)
return
to_chat(user,"<span class='notice'>You pick up the scent of [tracking_target]. The hunt begins.</span>")
on_the_trail(user)
return
if(!tracking_target)
to_chat(user,"<span class='warning'>You're not holding anything to smell, and you haven't smelled anything you can track. You smell your skin instead; it's kinda salty.</span>")
return
on_the_trail(user)
/obj/effect/proc_holder/spell/targeted/olfaction/proc/on_the_trail(mob/living/user)
if(!tracking_target)
to_chat(user,"<span class='warning'>You're not tracking a scent, but the game thought you were. Something's gone wrong! Report this as a bug.</span>")
return
if(tracking_target == user)
to_chat(user,"<span class='warning'>You smell out the trail to yourself. Yep, it's you.</span>")
return
if(usr.z < tracking_target.z)
to_chat(user,"<span class='warning'>The trail leads... way up above you? Huh. They must be really, really far away.</span>")
return
else if(usr.z > tracking_target.z)
to_chat(user,"<span class='warning'>The trail leads... way down below you? Huh. They must be really, really far away.</span>")
return
var/direction_text = "[dir2text(get_dir(usr, tracking_target))]"
if(direction_text)
to_chat(user,"<span class='notice'>You consider [tracking_target]'s scent. The trail leads <b>[direction_text].</b></span>")
/datum/mutation/human/self_amputation
name = "Autotomy"
desc = "Allows a creature to voluntary discard a random appendage."
quality = POSITIVE
text_gain_indication = "<span class='notice'>Your joints feel loose.</span>"
instability = 30
power = /obj/effect/proc_holder/spell/self/self_amputation
energy_coeff = 1
synchronizer_coeff = 1
/obj/effect/proc_holder/spell/self/self_amputation
name = "Drop a limb"
desc = "Concentrate to make a random limb pop right off your body."
clothes_req = FALSE
human_req = FALSE
charge_max = 100
action_icon_state = "autotomy"
/obj/effect/proc_holder/spell/self/self_amputation/cast(list/targets, mob/user = usr)
if(!iscarbon(user))
return
var/mob/living/carbon/C = user
if(HAS_TRAIT(C, TRAIT_NODISMEMBER))
return
var/list/parts = list()
for(var/X in C.bodyparts)
var/obj/item/bodypart/BP = X
if(BP.body_part != HEAD && BP.body_part != CHEST)
if(BP.dismemberable)
parts += BP
if(!parts.len)
to_chat(usr, "<span class='notice'>You can't shed any more limbs!</span>")
return
var/obj/item/bodypart/BP = pick(parts)
BP.dismember()
//spider webs
/datum/mutation/human/webbing
name = "Webbing Production"
desc = "Allows the user to lay webbing, and travel through it."
quality = POSITIVE
text_gain_indication = "<span class='notice'>Your skin feels webby.</span>"
instability = 15
power = /obj/effect/proc_holder/spell/self/lay_genetic_web
/obj/effect/proc_holder/spell/self/lay_genetic_web
name = "Lay Web"
desc = "Drops a web. Only you will be able to traverse your web easily, making it pretty good for keeping you safe."
clothes_req = FALSE
human_req = FALSE
charge_max = 4 SECONDS //the same time to lay a web
action_icon = 'icons/mob/actions/actions_genetic.dmi'
action_icon_state = "lay_web"
/obj/effect/proc_holder/spell/self/lay_genetic_web/cast(list/targets, mob/user = usr)
var/failed = FALSE
if(!isturf(user.loc))
to_chat(user, "<span class='warning'>You can't lay webs here!</span>")
failed = TRUE
var/turf/T = get_turf(user)
var/obj/structure/spider/stickyweb/genetic/W = locate() in T
if(W)
to_chat(user, "<span class='warning'>There's already a web here!</span>")
failed = TRUE
if(failed)
revert_cast(user)
return FALSE
user.visible_message("<span class='notice'>[user] begins to secrete a sticky substance.</span>","<span class='notice'>You begin to lay a web.</span>")
if(!do_after(user, 4 SECONDS, target = T))
to_chat(user, "<span class='warning'>Your web spinning was interrupted!</span>")
return
else
new /obj/structure/spider/stickyweb/genetic(T, user)
/datum/mutation/human/tongue_spike
name = "Tongue Spike"
desc = "Allows a creature to voluntary shoot their tongue out as a deadly weapon."
quality = POSITIVE
text_gain_indication = "<span class='notice'>Your feel like you can throw your voice.</span>"
instability = 15
power = /obj/effect/proc_holder/spell/self/tongue_spike
energy_coeff = 1
synchronizer_coeff = 1
/obj/effect/proc_holder/spell/self/tongue_spike
name = "Launch spike"
desc = "Shoot your tongue out in the direction you're facing, embedding it and dealing damage until they remove it."
clothes_req = FALSE
human_req = TRUE
charge_max = 100
action_icon = 'icons/mob/actions/actions_genetic.dmi'
action_icon_state = "spike"
var/spike_path = /obj/item/hardened_spike
/obj/effect/proc_holder/spell/self/tongue_spike/cast(list/targets, mob/user = usr)
if(!iscarbon(user))
return
var/mob/living/carbon/C = user
if(HAS_TRAIT(C, TRAIT_NODISMEMBER))
return
var/obj/item/organ/tongue/tongue
for(var/org in C.internal_organs)
if(istype(org, /obj/item/organ/tongue))
tongue = org
break
if(!tongue)
to_chat(C, "<span class='notice'>You don't have a tongue to shoot!</span>")
return
tongue.Remove(C, special = TRUE)
var/obj/item/hardened_spike/spike = new spike_path(get_turf(C), C)
tongue.forceMove(spike)
spike.throw_at(get_edge_target_turf(C,C.dir), 14, 4, C)
/obj/item/hardened_spike
name = "biomass spike"
desc = "Hardened biomass, shaped into a spike. Very pointy!"
icon_state = "tonguespike"
force = 2
throwforce = 15 //15 + 2 (WEIGHT_CLASS_SMALL) * 4 (EMBEDDED_IMPACT_PAIN_MULTIPLIER) = i didnt do the math
throw_speed = 4
embedding = list("embedded_pain_multiplier" = 4, "embed_chance" = 100, "embedded_fall_chance" = 0)
w_class = WEIGHT_CLASS_SMALL
sharpness = IS_SHARP
var/mob/living/carbon/human/fired_by
/obj/item/hardened_spike/Initialize(mapload, firedby)
. = ..()
fired_by = firedby
addtimer(CALLBACK(src, .proc/checkembedded), 5 SECONDS)
/obj/item/hardened_spike/proc/checkembedded()
if(ishuman(loc))
var/mob/living/carbon/human/embedtest = loc
for(var/l in embedtest.bodyparts)
var/obj/item/bodypart/limb = l
if(src in limb.embedded_objects)
return limb
unembedded()
/obj/item/hardened_spike/unembedded()
var/turf/T = get_turf(src)
visible_message("<span class='warning'>[src] cracks and twists, changing shape!</span>")
for(var/i in contents)
var/obj/o = i
o.forceMove(T)
qdel(src)
/datum/mutation/human/tongue_spike/chem
name = "Chem Spike"
desc = "Allows a creature to voluntary shoot their tongue out as biomass, allowing a long range transfer of chemicals."
quality = POSITIVE
text_gain_indication = "<span class='notice'>Your feel like you can really connect with people by throwing your voice.</span>"
instability = 15
locked = TRUE
power = /obj/effect/proc_holder/spell/self/tongue_spike/chem
energy_coeff = 1
synchronizer_coeff = 1
/obj/effect/proc_holder/spell/self/tongue_spike/chem
name = "Launch chem spike"
desc = "Shoot your tongue out in the direction you're facing, embedding it for a very small amount of damage. While the other person has the spike embedded, you can transfer your chemicals to them."
action_icon_state = "spikechem"
spike_path = /obj/item/hardened_spike/chem
/obj/item/hardened_spike/chem
name = "chem spike"
desc = "Hardened biomass, shaped into... something."
icon_state = "tonguespikechem"
throwforce = 2 //2 + 2 (WEIGHT_CLASS_SMALL) * 0 (EMBEDDED_IMPACT_PAIN_MULTIPLIER) = i didnt do the math again but very low or smthin
embedding = list("embedded_pain_multiplier" = 0, "embed_chance" = 100, "embedded_fall_chance" = 0, "embedded_pain_chance" = 0, "embedded_ignore_throwspeed_threshold" = TRUE) //never hurts once it's in you
var/been_places = FALSE
var/datum/action/innate/send_chems/chems
/obj/item/hardened_spike/chem/embedded(mob/living/carbon/human/embedded_mob)
if(been_places)
return
been_places = TRUE
chems = new
chems.transfered = embedded_mob
chems.spikey = src
to_chat(fired_by, "<span class='notice'>Link established! Use the \"Transfer Chemicals\" ability to send your chemicals to the linked target!</span>")
chems.Grant(fired_by)
/obj/item/hardened_spike/chem/unembedded()
to_chat(fired_by, "<span class='warning'>Link lost!</span>")
QDEL_NULL(chems)
..()
/datum/action/innate/send_chems
icon_icon = 'icons/mob/actions/actions_genetic.dmi'
background_icon_state = "bg_spell"
check_flags = AB_CHECK_CONSCIOUS
button_icon_state = "spikechemswap"
name = "Transfer Chemicals"
desc = "Send all of your reagents into whomever the chem spike is embedded in. One use."
var/obj/item/hardened_spike/chem/spikey
var/mob/living/carbon/human/transfered
/datum/action/innate/send_chems/Activate()
if(!ishuman(transfered) || !ishuman(owner))
return
var/mob/living/carbon/human/transferer = owner
to_chat(transfered, "<span class='warning'>You feel a tiny prick!</span>")
transferer.reagents.trans_to(transfered, transferer.reagents.total_volume, 1, 1, 0)
var/obj/item/bodypart/L = spikey.checkembedded()
L.embedded_objects -= spikey
//this is where it would deal damage, if it transfers chems it removes itself so no damage
spikey.forceMove(get_turf(L))
transfered.visible_message("<span class='notice'>[spikey] falls out of [transfered]!</span>")
if(!transfered.has_embedded_objects())
transfered.clear_alert("embeddedobject")
SEND_SIGNAL(transfered, COMSIG_CLEAR_MOOD_EVENT, "embedded")
spikey.unembedded()
+108
View File
@@ -0,0 +1,108 @@
/datum/mutation/human/antenna
name = "Antenna"
desc = "The affected person sprouts an antenna. This is known to allow them to access common radio channels passively."
quality = POSITIVE
text_gain_indication = "<span class='notice'>You feel an antenna sprout from your forehead.</span>"
text_lose_indication = "<span class='notice'>Your antenna shrinks back down.</span>"
instability = 5
difficulty = 8
var/obj/item/implant/radio/antenna/linked_radio
/obj/item/implant/radio/antenna
name = "internal antenna organ"
desc = "The internal organ part of the antenna. Science has not yet given it a good name."
icon = 'icons/obj/radio.dmi'//maybe make a unique sprite later. not important
icon_state = "walkietalkie"
/obj/item/implant/radio/antenna/Initialize(mapload)
..()
if (radio)
radio.name = "internal antenna"
/datum/mutation/human/antenna/on_acquiring(mob/living/carbon/human/owner)
if(..())
return
linked_radio = new(owner)
linked_radio.implant(owner, null, TRUE, TRUE)
/datum/mutation/human/antenna/on_losing(mob/living/carbon/human/owner)
if(..())
return
if(linked_radio)
linked_radio.Destroy()
/datum/mutation/human/antenna/New(class_ = MUT_OTHER, timer, datum/mutation/human/copymut)
..()
if(!(type in visual_indicators))
visual_indicators[type] = list(mutable_appearance('icons/effects/genetics.dmi', "antenna", -FRONT_MUTATIONS_LAYER+1))//-MUTATIONS_LAYER+1
/datum/mutation/human/antenna/get_visual_indicator(mob/living/carbon/human/owner)
return visual_indicators[type][1]
/datum/mutation/human/mindreader
name = "Mind Reader"
desc = "The affected person can look into the recent memories of others."
quality = POSITIVE
text_gain_indication = "<span class='notice'>You hear distant voices at the corners of your mind.</span>"
text_lose_indication = "<span class='notice'>The distant voices fade.</span>"
power = /obj/effect/proc_holder/spell/targeted/mindread
instability = 40
difficulty = 8
locked = TRUE
/obj/effect/proc_holder/spell/targeted/mindread
name = "Mindread"
desc = "Read the target's mind."
charge_max = 300
range = 7
clothes_req = FALSE
action_icon_state = "mindread"
/obj/effect/proc_holder/spell/targeted/mindread/cast(list/targets, mob/living/carbon/human/user = usr)
for(var/mob/living/M in targets)
if(usr.anti_magic_check(FALSE, FALSE, TRUE, 0) || M.anti_magic_check(FALSE, FALSE, TRUE, 0))
to_chat(usr, "<span class='warning'>As you reach out with your mind, you're suddenly stopped by a vision of a massive tinfoil wall that streches beyond visible range. It seems you've been foiled.</span>")
return
if(M.stat == DEAD)
to_chat(user, "<span class='boldnotice'>[M] is dead!</span>")
return
if(M.mind)
to_chat(user, "<span class='boldnotice'>You plunge into [M]'s mind...</span>")
if(prob(20))
to_chat(M, "<span class='danger'>You feel something foreign enter your mind.</span>")//chance to alert the read-ee
var/list/recent_speech = list()
var/list/say_log = list()
var/log_source = M.logging
for(var/log_type in log_source)//this whole loop puts the read-ee's say logs into say_log in an easy to access way
var/nlog_type = text2num(log_type)
if(nlog_type & LOG_SAY)
var/list/reversed = log_source[log_type]
if(islist(reversed))
say_log = reverseRange(reversed.Copy())
break
if(LAZYLEN(say_log))
for(var/spoken_memory in say_log)
if(recent_speech.len >= 3)//up to 3 random lines of speech, favoring more recent speech
break
if(prob(50))
recent_speech[spoken_memory] = say_log[spoken_memory]
if(recent_speech.len)
to_chat(user, "<span class='boldnotice'>You catch some drifting memories of their past conversations...</span>")
for(var/spoken_memory in recent_speech)
to_chat(user, "<span class='notice'>[recent_speech[spoken_memory]]</span>")
if(iscarbon(M))
var/mob/living/carbon/human/H = M
to_chat(user, "<span class='boldnotice'>You find that their intent is to [H.a_intent]...</span>")
var/datum/dna/the_dna = H.has_dna()
if(the_dna)
to_chat(user, "<span class='boldnotice'>You uncover that [H.p_their()] true identity is [the_dna.real_name].</span>")
else
to_chat(user, "<span class='warning'>You can't find a mind to read inside of [M]!</span>")
/datum/mutation/human/mindreader/New(class_ = MUT_OTHER, timer, datum/mutation/human/copymut)
..()
if(!(type in visual_indicators))
visual_indicators[type] = list(mutable_appearance('icons/effects/genetics.dmi', "antenna", -FRONT_MUTATIONS_LAYER+1))
/datum/mutation/human/mindreader/get_visual_indicator(mob/living/carbon/human/owner)
return visual_indicators[type][1]
+232 -10
View File
@@ -3,16 +3,19 @@
//Epilepsy gives a very small chance to have a seizure every life tick, knocking you unconscious.
/datum/mutation/human/epilepsy
name = "Epilepsy"
desc = "A genetic defect that sporadically causes seizures."
quality = NEGATIVE
text_gain_indication = "<span class='danger'>You get a headache.</span>"
synchronizer_coeff = 1
power_coeff = 1
/datum/mutation/human/epilepsy/on_life(mob/living/carbon/human/owner)
if(prob(1) && owner.stat == CONSCIOUS)
if(prob(1 * GET_MUTATION_SYNCHRONIZER(src)) && owner.stat == CONSCIOUS)
owner.visible_message("<span class='danger'>[owner] starts having a seizure!</span>", "<span class='userdanger'>You have a seizure!</span>")
owner.Unconscious(200)
owner.Jitter(1000)
owner.Unconscious(200 * GET_MUTATION_POWER(src))
owner.Jitter(1000 * GET_MUTATION_POWER(src))
SEND_SIGNAL(owner, COMSIG_ADD_MOOD_EVENT, "epilepsy", /datum/mood_event/epilepsy)
addtimer(CALLBACK(src, .proc/jitter_less, owner), 90)
addtimer(CALLBACK(src, .proc/jitter_less), 90)
/datum/mutation/human/epilepsy/proc/jitter_less(mob/living/carbon/human/owner)
if(owner)
@@ -22,19 +25,23 @@
//Unstable DNA induces random mutations!
/datum/mutation/human/bad_dna
name = "Unstable DNA"
desc = "Strange mutation that causes the holder to randomly mutate."
quality = NEGATIVE
text_gain_indication = "<span class='danger'>You feel strange.</span>"
locked = TRUE
/datum/mutation/human/bad_dna/on_acquiring(mob/living/carbon/human/owner)
if(..())
return
to_chat(owner, text_gain_indication)
var/mob/new_mob
if(prob(95))
if(prob(50))
new_mob = owner.randmutb()
new_mob = owner.easy_randmut(NEGATIVE + MINOR_NEGATIVE)
else
new_mob = owner.randmuti()
else
new_mob = owner.randmutg()
new_mob = owner.easy_randmut(POSITIVE)
if(new_mob && ismob(new_mob))
owner = new_mob
. = owner
@@ -44,21 +51,31 @@
//Cough gives you a chronic cough that causes you to drop items.
/datum/mutation/human/cough
name = "Cough"
desc = "A chronic cough."
quality = MINOR_NEGATIVE
text_gain_indication = "<span class='danger'>You start coughing.</span>"
synchronizer_coeff = 1
power_coeff = 1
/datum/mutation/human/cough/on_life(mob/living/carbon/human/owner)
if(prob(5) && owner.stat == CONSCIOUS)
if(prob(5 * GET_MUTATION_SYNCHRONIZER(src)) && owner.stat == CONSCIOUS)
owner.drop_all_held_items()
owner.emote("cough")
if(GET_MUTATION_POWER(src) > 1)
var/cough_range = GET_MUTATION_POWER(src) * 4
var/turf/target = get_ranged_target_turf(owner, turn(owner.dir, 180), cough_range)
owner.throw_at(target, cough_range, GET_MUTATION_POWER(src))
//Dwarfism shrinks your body and lets you pass tables.
/datum/mutation/human/dwarfism
name = "Dwarfism"
desc = "A mutation believed to be the cause of dwarfism."
quality = POSITIVE
get_chance = 15
lowest_value = 256 * 12
difficulty = 16
instability = 5
locked = TRUE // Default intert species for now, so locked from regular pool.
/datum/mutation/human/dwarfism/on_acquiring(mob/living/carbon/human/owner)
if(..())
@@ -78,6 +95,7 @@
//Clumsiness has a very large amount of small drawbacks depending on item.
/datum/mutation/human/clumsy
name = "Clumsiness"
desc = "A genome that inhibits certain brain functions, causing the holder to appear clumsy. Honk"
quality = MINOR_NEGATIVE
text_gain_indication = "<span class='danger'>You feel lightheaded.</span>"
@@ -95,11 +113,13 @@
//Tourettes causes you to randomly stand in place and shout.
/datum/mutation/human/tourettes
name = "Tourette's Syndrome"
desc = "A chronic twitch that forces the user to use colorful language."
quality = NEGATIVE
text_gain_indication = "<span class='danger'>You twitch.</span>"
synchronizer_coeff = 1
/datum/mutation/human/tourettes/on_life(mob/living/carbon/human/owner)
if(prob(10) && owner.stat == CONSCIOUS && !owner.IsStun())
if(prob(10 * GET_MUTATION_SYNCHRONIZER(src)) && owner.stat == CONSCIOUS && !owner.IsStun())
owner.Stun(200)
switch(rand(1, 3))
if(1)
@@ -117,6 +137,7 @@
//Deafness makes you deaf.
/datum/mutation/human/deaf
name = "Deafness"
desc = "The holder of this genome is completely deaf."
quality = NEGATIVE
text_gain_indication = "<span class='danger'>You can't seem to hear anything.</span>"
@@ -134,8 +155,10 @@
//Monified turns you into a monkey.
/datum/mutation/human/race
name = "Monkified"
desc = "A strange genome, believing to be what differentiates monkeys from humans."
quality = NEGATIVE
time_coeff = 2
locked = TRUE //Species specific, keep out of actual gene pool
/datum/mutation/human/race/on_acquiring(mob/living/carbon/human/owner)
if(..())
@@ -145,3 +168,202 @@
/datum/mutation/human/race/on_losing(mob/living/carbon/monkey/owner)
if(owner && istype(owner) && owner.stat != DEAD && (owner.dna.mutations.Remove(src)))
. = owner.humanize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPORGANS | TR_KEEPDAMAGE | TR_KEEPVIRUS | TR_KEEPSE)
/datum/mutation/human/glow
name = "Glowy"
desc = "You permanently emit a light with a random color and intensity."
quality = POSITIVE
text_gain_indication = "<span class='notice'>Your skin begins to glow softly.</span>"
instability = 5
var/obj/effect/dummy/luminescent_glow/glowth //shamelessly copied from luminescents
var/glow = 1.5
power_coeff = 1
/datum/mutation/human/glow/on_acquiring(mob/living/carbon/human/owner)
if(..())
return
glowth = new(owner)
glowth.set_light(glow, glow, dna.features["mcolor"])
/datum/mutation/human/glow/modify(mob/living/carbon/human/owner)
if(glowth)
glowth.set_light(glow + GET_MUTATION_POWER(src) , glow + GET_MUTATION_POWER(src), dna.features["mcolor"])
/datum/mutation/human/glow/on_losing(mob/living/carbon/human/owner)
if(..())
return
qdel(glowth)
/datum/mutation/human/strong
name = "Strength"
desc = "The user's muscles slightly expand."
quality = POSITIVE
text_gain_indication = "<span class='notice'>You feel strong.</span>"
difficulty = 16
/datum/mutation/human/fire
name = "Fiery Sweat"
desc = "The user's skin will randomly combust, but is generally alot more resilient to burning."
quality = NEGATIVE
text_gain_indication = "<span class='warning'>You feel hot.</span>"
text_lose_indication = "<span class'notice'>You feel a lot cooler.</span>"
difficulty = 14
synchronizer_coeff = 1
power_coeff = 1
/datum/mutation/human/fire/on_life(mob/living/carbon/human/owner)
if(prob((1+(100-dna.stability)/10)) * GET_MUTATION_SYNCHRONIZER(src))
owner.adjust_fire_stacks(2 * GET_MUTATION_POWER(src))
owner.IgniteMob()
/datum/mutation/human/fire/on_acquiring(mob/living/carbon/human/owner)
if(..())
return
owner.physiology.burn_mod *= 0.5
/datum/mutation/human/fire/on_losing(mob/living/carbon/human/owner)
if(..())
return
owner.physiology.burn_mod *= 2
/datum/mutation/human/insulated
name = "Insulated"
desc = "The affected person does not conduct electricity."
quality = POSITIVE
text_gain_indication = "<span class='notice'>Your fingertips go numb.</span>"
text_lose_indication = "<span class='notice'>Your fingertips regain feeling.</span>"
difficulty = 16
instability = 25
/datum/mutation/human/insulated/on_acquiring(mob/living/carbon/human/owner)
if(..())
return
ADD_TRAIT(owner, TRAIT_SHOCKIMMUNE, "genetics")
/datum/mutation/human/insulated/on_losing(mob/living/carbon/human/owner)
if(..())
return
REMOVE_TRAIT(owner, TRAIT_SHOCKIMMUNE, "genetics")
/datum/mutation/human/glow/anti
name = "Anti-Glow"
desc = "Your skin seems to attract and absorb nearby light creating 'darkness' around you."
text_gain_indication = "<span class='notice'>Your light around you seems to disappear.</span>"
glow = -3.5 //Slightly stronger, since negating light tends to be harder than making it.
locked = TRUE
/datum/mutation/human/stimmed
name = "Stimmed"
desc = "The user's chemical balance is more robust."
quality = POSITIVE
text_gain_indication = "<span class='notice'>You feel stimmed.</span>"
difficulty = 16
/datum/mutation/human/paranoia
name = "Paranoia"
desc = "Subject is easily terrified, and may suffer from hallucinations."
quality = NEGATIVE
text_gain_indication = "<span class='danger'>You feel screams echo through your mind...</span>"
text_lose_indication = "<span class'notice'>The screaming in your mind fades.</span>"
/datum/mutation/human/paranoia/on_life(mob/living/carbon/human/owner)
if(prob(5) && owner.stat == CONSCIOUS)
owner.emote("scream")
owner.jitteriness = min(max(0, owner.jitteriness + 5), 30)
if(prob(25))
to_chat(owner,"<span class='warning'>You feel someone creeping in on you...</span>")
owner.hallucination += 20
/datum/mutation/human/badblink
name = "Spatial Instability"
desc = "The victim of the mutation has a very weak link to spatial reality, and may be displaced. Often causes extreme nausea."
quality = NEGATIVE
text_gain_indication = "<span class='warning'>The space around you twists sickeningly.</span>"
text_lose_indication = "<span class'notice'>The space around you settles back to normal.</span>"
difficulty = 18//high so it's hard to unlock and abuse
instability = 10
synchronizer_coeff = 1
energy_coeff = 1
power_coeff = 1
var/warpchance = 0
/datum/mutation/human/badblink/on_life()
if (!owner)
return
if(prob(warpchance))
var/warpmessage = pick(
"<span class='warning'>With a sickening 720-degree twist of [owner.p_their()] back, [owner] vanishes into thin air.</span>",
"<span class='warning'>[owner] does some sort of strange backflip into another dimension. It looks pretty painful.</span>",
"<span class='warning'>[owner] does a jump to the left, a step to the right, and warps out of reality.</span>",
"<span class='warning'>[owner]'s torso starts folding inside out until it vanishes from reality, taking [owner] with it.</span>",
"<span class='warning'>One moment, you see [owner]. The next, [owner] is gone.</span>")
owner.visible_message(warpmessage, "<span class='userdanger'>You feel a wave of nausea as you fall through reality!</span>")
var/warpdistance = rand(10,15) * GET_MUTATION_POWER(src)
do_teleport(owner, get_turf(owner), warpdistance, channel = TELEPORT_CHANNEL_FREE)
owner.adjust_disgust(GET_MUTATION_SYNCHRONIZER(src) * (warpchance * warpdistance))
warpchance = 0
owner.visible_message("<span class='danger'>[owner] appears out of nowhere!</span>")
else
warpchance += 0.25 * GET_MUTATION_ENERGY(src)
/datum/mutation/human/acidflesh
name = "Acidic Flesh"
desc = "Subject has acidic chemicals building up underneath the skin. This is often lethal."
quality = NEGATIVE
text_gain_indication = "<span class='userdanger'>A horrible burning sensation envelops you as your flesh turns to acid!</span>"
text_lose_indication = "<span class'notice'>A feeling of relief fills you as your flesh goes back to normal.</span>"
difficulty = 18//high so it's hard to unlock and use on others
var/msgcooldown = 0
/datum/mutation/human/acidflesh/on_life()
if(prob(25))
if(world.time > msgcooldown)
to_chat(owner, "<span class='danger'>Your acid flesh bubbles...</span>")
msgcooldown = world.time + 200
if(prob(15))
owner.acid_act(rand(30,50), 10)
owner.visible_message("<span class='warning'>[owner]'s skin bubbles and pops.</span>", "<span class='userdanger'>Your bubbling flesh pops! It burns!</span>")
playsound(owner,'sound/weapons/sear.ogg', 50, TRUE)
/datum/mutation/human/gigantism
name = "Gigantism"//negative version of dwarfism
desc = "The cells within the subject spread out to cover more area, making the subject appear larger."
quality = MINOR_NEGATIVE
difficulty = 12
/datum/mutation/human/gigantism/on_acquiring(mob/living/carbon/human/owner)
if(..())
return
owner.resize = 1.25
owner.update_transform()
owner.visible_message("<span class='danger'>[owner] suddenly grows!</span>", "<span class='notice'>Everything around you seems to shrink..</span>")
/datum/mutation/human/gigantism/on_losing(mob/living/carbon/human/owner)
if(..())
return
owner.resize = 0.8
owner.update_transform()
owner.visible_message("<span class='danger'>[owner] suddenly shrinks!</span>", "<span class='notice'>Everything around you seems to grow..</span>")
/datum/mutation/human/spastic
name = "Spastic"
desc = "Subject suffers from muscle spasms."
quality = NEGATIVE
text_gain_indication = "<span class='warning'>You flinch.</span>"
text_lose_indication = "<span class'notice'>Your flinching subsides.</span>"
difficulty = 16
/datum/mutation/human/spastic/on_acquiring()
if(..())
return
if (owner)
owner.apply_status_effect(STATUS_EFFECT_SPASMS)
/datum/mutation/human/spastic/on_losing()
if(..())
return
if (owner)
owner.remove_status_effect(STATUS_EFFECT_SPASMS)
+3 -2
View File
@@ -1,12 +1,13 @@
//Chameleon causes the owner to slowly become transparent when not moving.
/datum/mutation/human/chameleon
name = "Chameleon"
desc = "A genome that causes the holder's skin to become transparent over time."
quality = POSITIVE
get_chance = 20
lowest_value = 256 * 12
difficulty = 16
text_gain_indication = "<span class='notice'>You feel one with your surroundings.</span>"
text_lose_indication = "<span class='notice'>You feel oddly exposed.</span>"
time_coeff = 5
instability = 25
/datum/mutation/human/chameleon/on_acquiring(mob/living/carbon/human/owner)
if(..())
+43
View File
@@ -0,0 +1,43 @@
/datum/mutation/human/geladikinesis
name = "Geladikinesis"
desc = "Allows the user to concentrate moisture and sub-zero forces into snow."
quality = POSITIVE
text_gain_indication = "<span class='notice'>Your hand feels cold.</span>"
instability = 10
difficulty = 10
synchronizer_coeff = 1
power = /obj/effect/proc_holder/spell/targeted/conjure_item/snow
/obj/effect/proc_holder/spell/targeted/conjure_item/snow
name = "Create Snow"
desc = "Concentrates cryokinetic forces to create snow, useful for snow-like construction."
item_type = /obj/item/stack/sheet/mineral/snow
charge_max = 50
action_icon_state = "snow"
delete_old = FALSE
/datum/mutation/human/cryokinesis
name = "Cryokinesis"
desc = "Draws negative energy from the sub-zero void to freeze surrounding temperatures at subject's will."
quality = POSITIVE //upsides and downsides
text_gain_indication = "<span class='notice'>Your hand feels cold.</span>"
instability = 20
difficulty = 12
synchronizer_coeff = 1
power = /obj/effect/proc_holder/spell/aimed/cryo
/obj/effect/proc_holder/spell/aimed/cryo
name = "Cryobeam"
desc = "This power fires a frozen bolt at a target."
charge_max = 150
cooldown_min = 150
clothes_req = FALSE
range = 3
projectile_type = /obj/item/projectile/temp/cryo
base_icon_state = "icebeam"
action_icon_state = "icebeam"
active_msg = "You focus your cryokinesis!"
deactive_msg = "You relax."
active = FALSE
-32
View File
@@ -1,32 +0,0 @@
//Cold Resistance gives your entire body an orange halo, and makes you immune to the effects of vacuum and cold.
/datum/mutation/human/cold_resistance
name = "Cold Resistance"
quality = POSITIVE
get_chance = 25
lowest_value = 256 * 12
text_gain_indication = "<span class='notice'>Your body feels warm!</span>"
time_coeff = 5
/datum/mutation/human/cold_resistance/New()
..()
visual_indicators |= mutable_appearance('icons/effects/genetics.dmi', "fire", -MUTATIONS_LAYER)
/datum/mutation/human/cold_resistance/get_visual_indicator(mob/living/carbon/human/owner)
return visual_indicators[1]
/datum/mutation/human/cold_resistance/on_acquiring(mob/living/carbon/human/owner)
if(..())
return
ADD_TRAIT(owner, TRAIT_RESISTCOLD, "cold_resistance")
// ADD_TRAIT(owner, TRAIT_RESISTLOWPRESSURE, "cold_resistance") CITADEL CHANGE
/datum/mutation/human/cold_resistance/on_losing(mob/living/carbon/human/owner)
if(..())
return
REMOVE_TRAIT(owner, TRAIT_RESISTCOLD, "cold_resistance")
// REMOVE_TRAIT(owner, TRAIT_RESISTLOWPRESSURE, "cold_resistance") CITADEL CHANGE
/datum/mutation/human/cold_resistance/on_life(mob/living/carbon/human/owner)
if(owner.getFireLoss())
if(prob(1))
owner.heal_bodypart_damage(0,1) //Is this really needed?
+32
View File
@@ -0,0 +1,32 @@
/datum/generecipe
var/required = "" //it hurts so bad but initial is not compatible with lists
var/result = null
/proc/get_mixed_mutation(mutation1, mutation2)
if(!mutation1 || !mutation2)
return FALSE
if(mutation1 == mutation2) //this could otherwise be bad
return FALSE
for(var/A in GLOB.mutation_recipes)
if(findtext(A, "[mutation1]") && findtext(A, "[mutation2]"))
return GLOB.mutation_recipes[A]
/datum/generecipe/x_ray
required = "/datum/mutation/human/thermal; /datum/mutation/human/radioactive"
result = /datum/mutation/human/thermal/x_ray
/datum/generecipe/shock
required = "/datum/mutation/human/insulated; /datum/mutation/human/radioactive"
result = SHOCKTOUCH
/datum/generecipe/mindread
required = "/datum/mutation/human/antenna; /datum/mutation/human/paranoia"
result = MINDREAD
/datum/generecipe/antiglow
required = "/datum/mutation/human/glow; /datum/mutation/human/void"
result = ANTIGLOWY
/datum/generecipe/tonguechem
required = "/datum/mutation/human/tongue_spike; /datum/mutation/human/stimmed"
result = TONGUESPIKECHEM
+6 -5
View File
@@ -1,12 +1,13 @@
//Hulk turns your skin green, and allows you to punch through walls.
/datum/mutation/human/hulk
name = "Hulk"
desc = "A poorly understood genome that causes the holder's muscles to expand, inhibit speech and gives the person a bad skin condition."
quality = POSITIVE
get_chance = 15
lowest_value = 256 * 12
locked = TRUE
difficulty = 16
text_gain_indication = "<span class='notice'>Your muscles hurt!</span>"
species_allowed = list("fly") //no skeleton/lizard hulk
health_req = 25
instability = 40
/datum/mutation/human/hulk/on_acquiring(mob/living/carbon/human/owner)
if(..())
@@ -18,7 +19,7 @@
SEND_SIGNAL(owner, COMSIG_ADD_MOOD_EVENT, "hulk", /datum/mood_event/hulk)
RegisterSignal(owner, COMSIG_MOB_SAY, .proc/handle_speech)
/datum/mutation/human/hulk/on_attack_hand(mob/living/carbon/human/owner, atom/target, proximity)
/datum/mutation/human/hulk/on_attack_hand(mob/living/carbon/human/owner,atom/target, proximity)
if(proximity) //no telekinetic hulk attack
return target.attack_hulk(owner)
@@ -32,7 +33,7 @@
return
REMOVE_TRAIT(owner, TRAIT_STUNIMMUNE, TRAIT_HULK)
REMOVE_TRAIT(owner, TRAIT_PUSHIMMUNE, TRAIT_HULK)
ADD_TRAIT(owner, TRAIT_CHUNKYFINGERS, TRAIT_HULK)
REMOVE_TRAIT(owner, TRAIT_CHUNKYFINGERS, TRAIT_HULK)
owner.update_body_parts()
SEND_SIGNAL(owner, COMSIG_CLEAR_MOOD_EVENT, "hulk")
UnregisterSignal(owner, COMSIG_MOB_SAY)
+19
View File
@@ -0,0 +1,19 @@
/datum/mutation/human/radioactive
name = "Radioactivity"
desc = "A volatile mutation that causes the host to sent out deadly beta radiation. This affects both the hosts and their surroundings."
quality = NEGATIVE
text_gain_indication = "<span class='warning'>You can feel it in your bones!</span>"
time_coeff = 5
instability = 5
difficulty = 8
/datum/mutation/human/radioactive/on_life(mob/living/carbon/human/owner)
radiation_pulse(owner, 20)
/datum/mutation/human/radioactive/New(class_ = MUT_OTHER, timer, datum/mutation/human/copymut)
..()
if(!(type in visual_indicators))
visual_indicators[type] = list(mutable_appearance('icons/effects/genetics.dmi', "radiation", -MUTATIONS_LAYER))
/datum/mutation/human/radioactive/get_visual_indicator()
return visual_indicators[type][1]
+29 -12
View File
@@ -1,6 +1,7 @@
//Nearsightedness restricts your vision by several tiles.
/datum/mutation/human/nearsight
name = "Near Sightness"
desc = "The holder of this mutation has poor eyesight."
quality = MINOR_NEGATIVE
text_gain_indication = "<span class='danger'>You can't see very well.</span>"
@@ -18,6 +19,7 @@
//Blind makes you blind. Who knew?
/datum/mutation/human/blind
name = "Blindness"
desc = "Renders the subject completely blind."
quality = NEGATIVE
text_gain_indication = "<span class='danger'>You can't seem to see anything.</span>"
@@ -32,43 +34,58 @@
owner.cure_blind(GENETIC_MUTATION)
//X-ray Vision lets you see through walls.
/datum/mutation/human/x_ray
name = "X Ray Vision"
///Thermal Vision lets you see mobs through walls
/datum/mutation/human/thermal
name = "Thermal Vision"
desc = "The user of this genome can visually percieve the unique human thermal signature."
quality = POSITIVE
get_chance = 25
lowest_value = 256 * 12
text_gain_indication = "<span class='notice'>The walls suddenly disappear!</span>"
difficulty = 18
text_gain_indication = "<span class='notice'>You can see the heat rising off of your skin...</span>"
time_coeff = 2
instability = 25
var/visionflag = TRAIT_THERMAL_VISION
/datum/mutation/human/x_ray/on_acquiring(mob/living/carbon/human/owner)
/datum/mutation/human/thermal/on_acquiring(mob/living/carbon/human/owner)
if(..())
return
ADD_TRAIT(owner, visionflag, GENETIC_MUTATION)
owner.update_sight()
/datum/mutation/human/x_ray/on_losing(mob/living/carbon/human/owner)
/datum/mutation/human/thermal/on_losing(mob/living/carbon/human/owner)
if(..())
return
REMOVE_TRAIT(owner, visionflag, GENETIC_MUTATION)
owner.update_sight()
///X-ray Vision lets you see through walls.
/datum/mutation/human/thermal/x_ray
name = "X Ray Vision"
desc = "A strange genome that allows the user to see between the spaces of walls." //actual x-ray would mean you'd constantly be blasting rads, wich might be fun for later //hmb
text_gain_indication = "<span class='notice'>The walls suddenly disappear!</span>"
instability = 35
locked = TRUE
visionflag = TRAIT_XRAY_VISION
//Laser Eyes lets you shoot lasers from your eyes!
/datum/mutation/human/laser_eyes
name = "Laser Eyes"
desc = "Reflects concentrated light back from the eyes."
quality = POSITIVE
dna_block = NON_SCANNABLE
locked = TRUE
difficulty = 16
text_gain_indication = "<span class='notice'>You feel pressure building up behind your eyes.</span>"
layer_used = FRONT_MUTATIONS_LAYER
limb_req = BODY_ZONE_HEAD
/datum/mutation/human/laser_eyes/New()
/datum/mutation/human/laser_eyes/New(class_ = MUT_OTHER, timer, datum/mutation/human/copymut)
..()
visual_indicators |= mutable_appearance('icons/effects/genetics.dmi', "lasereyes", -FRONT_MUTATIONS_LAYER)
/datum/mutation/human/laser_eyes/get_visual_indicator(mob/living/carbon/human/owner)
/datum/mutation/human/laser_eyes/get_visual_indicator()
return visual_indicators[1]
/datum/mutation/human/laser_eyes/on_ranged_attack(mob/living/carbon/human/owner, atom/target, mouseparams)
/datum/mutation/human/laser_eyes/on_ranged_attack(atom/target, mouseparams)
if(owner.a_intent == INTENT_HARM)
owner.LaserEyes(target, mouseparams)
+30
View File
@@ -0,0 +1,30 @@
//Cold Resistance gives your entire body an orange halo, and makes you immune to the effects of vacuum and cold.
/datum/mutation/human/space_adaptation
name = "Space Adaptation"
desc = "A strange mutation that renders the host immune to the vacuum if space. Will still need an oxygen supply."
quality = POSITIVE
difficulty = 16
text_gain_indication = "<span class='notice'>Your body feels warm!</span>"
time_coeff = 5
instability = 30
/datum/mutation/human/space_adaptation/New(class_ = MUT_OTHER, timer, datum/mutation/human/copymut)
..()
if(!(type in visual_indicators))
visual_indicators[type] = list(mutable_appearance('icons/effects/genetics.dmi', "fire", -MUTATIONS_LAYER))
/datum/mutation/human/space_adaptation/get_visual_indicator(mob/living/carbon/human/owner)
return visual_indicators[type][1]
/datum/mutation/human/space_adaptation/on_acquiring(mob/living/carbon/human/owner)
if(..())
return
ADD_TRAIT(owner, TRAIT_RESISTCOLD, "cold_resistance")
ADD_TRAIT(owner, TRAIT_RESISTLOWPRESSURE, "cold_resistance")
/datum/mutation/human/space_adaptation/on_losing(mob/living/carbon/human/owner)
if(..())
return
REMOVE_TRAIT(owner, TRAIT_RESISTCOLD, "cold_resistance")
REMOVE_TRAIT(owner, TRAIT_RESISTLOWPRESSURE, "cold_resistance")
+13 -8
View File
@@ -3,6 +3,7 @@
/datum/mutation/human/nervousness
name = "Nervousness"
desc = "Causes the holder to stutter."
quality = MINOR_NEGATIVE
text_gain_indication = "<span class='danger'>You feel nervous.</span>"
@@ -13,6 +14,7 @@
/datum/mutation/human/wacky
name = "Wacky"
desc = "<span class='sans'>Unknown.</span>"
quality = MINOR_NEGATIVE
text_gain_indication = "<span class='sans'>You feel an off sensation in your voicebox.</span>"
text_lose_indication = "<span class='notice'>The off sensation passes.</span>"
@@ -34,6 +36,7 @@
/datum/mutation/human/mute
name = "Mute"
desc = "Completely inhibits the vocal section of the brain."
quality = NEGATIVE
text_gain_indication = "<span class='danger'>You feel unable to express yourself at all.</span>"
text_lose_indication = "<span class='danger'>You feel able to speak freely again.</span>"
@@ -53,8 +56,8 @@
/datum/mutation/human/smile
name = "Smile"
desc = "Causes the user to be in constant mania."
quality = MINOR_NEGATIVE
dna_block = NON_SCANNABLE
text_gain_indication = "<span class='notice'>You feel so happy. Nothing can be wrong with anything. :)</span>"
text_lose_indication = "<span class='notice'>Everything is terrible again. :(</span>"
@@ -95,7 +98,6 @@
message = replacetext(message," ugly "," beautiful ")
message = replacetext(message," douchbag "," nice guy ")
message = replacetext(message," whore "," lady ")
message = replacetext(message," gamer "," intellectual ")
message = replacetext(message," nerd "," smarty pants ")
message = replacetext(message," moron "," fun person ")
message = replacetext(message," IT'S LOOSE "," EVERYTHING IS FINE ")
@@ -121,8 +123,8 @@
/datum/mutation/human/unintelligible
name = "Unintelligible"
desc = "Partially inhibits the vocal center of the brain, severely distorting speech."
quality = NEGATIVE
dna_block = NON_SCANNABLE
text_gain_indication = "<span class='danger'>You can't seem to form any coherent thoughts!</span>"
text_lose_indication = "<span class='danger'>Your mind feels more clear.</span>"
@@ -141,8 +143,9 @@
/datum/mutation/human/swedish
name = "Swedish"
desc = "A horrible mutation originating from the distant past. Thought to be eradicated after the incident in 2037."
quality = MINOR_NEGATIVE
dna_block = NON_SCANNABLE
locked = TRUE
text_gain_indication = "<span class='notice'>You feel Swedish, however that works.</span>"
text_lose_indication = "<span class='notice'>The feeling of Swedishness passes.</span>"
@@ -173,8 +176,8 @@
/datum/mutation/human/chav
name = "Chav"
desc = "Unknown"
quality = MINOR_NEGATIVE
dna_block = NON_SCANNABLE
text_gain_indication = "<span class='notice'>Ye feel like a reet prat like, innit?</span>"
text_lose_indication = "<span class='notice'>You no longer feel like being rude and sassy.</span>"
@@ -220,12 +223,13 @@
/datum/mutation/human/elvis
name = "Elvis"
desc = "A terrifying mutation named after its 'patient-zero'."
quality = MINOR_NEGATIVE
dna_block = NON_SCANNABLE
locked = TRUE
text_gain_indication = "<span class='notice'>You feel pretty good, honeydoll.</span>"
text_lose_indication = "<span class='notice'>You feel a little less conversation would be great.</span>"
/datum/mutation/human/elvis/on_life(mob/living/carbon/human/owner)
/datum/mutation/human/elvis/on_life()
switch(pick(1,2))
if(1)
if(prob(15))
@@ -266,8 +270,9 @@
/datum/mutation/human/stoner
name = "Stoner"
desc = "A common mutation that severely decreases intelligence."
quality = NEGATIVE
dna_block = NON_SCANNABLE
locked = TRUE
text_gain_indication = "<span class='notice'>You feel...totally chill, man!</span>"
text_lose_indication = "<span class='notice'>You feel like you have a better sense of time.</span>"
+8 -6
View File
@@ -1,18 +1,20 @@
//Telekinesis lets you interact with objects from range, and gives you a light blue halo around your head.
/datum/mutation/human/telekinesis
name = "Telekinesis"
desc = "A strange mutation that allows the holder to interact with objects through thought."
quality = POSITIVE
get_chance = 20
lowest_value = 256 * 12
difficulty = 18
text_gain_indication = "<span class='notice'>You feel smarter!</span>"
limb_req = BODY_ZONE_HEAD
instability = 30
/datum/mutation/human/telekinesis/New()
/datum/mutation/human/telekinesis/New(class_ = MUT_OTHER, timer, datum/mutation/human/copymut)
..()
visual_indicators |= mutable_appearance('icons/effects/genetics.dmi', "telekinesishead", -MUTATIONS_LAYER)
if(!(type in visual_indicators))
visual_indicators[type] = list(mutable_appearance('icons/effects/genetics.dmi', "telekinesishead", -MUTATIONS_LAYER))
/datum/mutation/human/telekinesis/get_visual_indicator(mob/living/carbon/human/owner)
return visual_indicators[1]
return visual_indicators[type][1]
/datum/mutation/human/telekinesis/on_ranged_attack(mob/living/carbon/human/owner, atom/target)
target.attack_tk(owner)
target.attack_tk(owner)
+6 -3
View File
@@ -168,7 +168,8 @@
id = "alien-nest"
description = "Not even Necropolis is safe from alien infestation. The competition for hosts has locked the legion and aliens in an endless conflict that can only be resolved by a PKA."
suffix = "lavaland_surface_alien_nest.dmm"
cost = 20
cost = 10
allow_duplicates = FALSE
/datum/map_template/ruin/lavaland/fountain
name = "Fountain Hall"
@@ -176,6 +177,7 @@
description = "The fountain has a warning on the side. DANGER: May have undeclared side effects that only become obvious when implemented."
suffix = "lavaland_surface_fountain_hall.dmm"
cost = 5
allow_duplicates = FALSE
/datum/map_template/ruin/lavaland/survivalcapsule
name = "Survival Capsule Ruins"
@@ -198,7 +200,7 @@
description = "A place of vile worship, the scrawling of blood in the middle glowing eerily. A demonic laugh echoes throughout the caverns"
suffix = "lavaland_surface_cultaltar.dmm"
allow_duplicates = FALSE
cost = 10
cost = 5
/datum/map_template/ruin/lavaland/hermit
name = "Makeshift Shelter"
@@ -244,6 +246,7 @@
description = "A strange tumor which houses a powerful beast..."
suffix = "lavaland_surface_elite_tumor.dmm"
cost = 5
placement_weight = 3
always_place = TRUE
allow_duplicates = TRUE
@@ -255,4 +258,4 @@
suffix = "miningbase.dmm"
cost = 0
always_place = TRUE
unpickable = TRUE
unpickable = TRUE
+31 -1
View File
@@ -128,6 +128,7 @@
/datum/status_effect/electrode
id = "tased"
alert_type = null
var/slowdown = 1.5
var/slowdown_priority = 50 //to make sure the stronger effect overrides
var/affect_crawl = FALSE
@@ -155,7 +156,11 @@
/datum/status_effect/electrode/tick()
var/diff = world.time - last_tick
if(owner)
owner.adjustStaminaLoss(max(0, stamdmg_per_ds * diff)) //if you really want to try to stamcrit someone with a taser alone, you can, but it'll take time and good timing.
var/mob/living/carbon/C = owner
if(HAS_TRAIT(C, TRAIT_TASED_RESISTANCE))
qdel(src)
else
C.adjustStaminaLoss(max(0, stamdmg_per_ds * diff)) //if you really want to try to stamcrit someone with a taser alone, you can, but it'll take time and good timing.
last_tick = world.time
/datum/status_effect/electrode/nextmove_modifier() //why is this a proc. its no big deal since this doesnt get called often at all but literally w h y
@@ -172,6 +177,8 @@
. = ..()
if(iscarbon(owner))
var/mob/living/carbon/C = owner
if(HAS_TRAIT(C, TRAIT_TASED_RESISTANCE))
return
if(C.combatmode)
C.toggle_combat_mode(TRUE)
@@ -794,3 +801,26 @@ datum/status_effect/pacify
to_chat(owner, "<span class='warning'>Your arm spasms!</span>")
owner.log_message("threw [I] due to a Muscle Spasm", LOG_ATTACK)
owner.throw_item(pick(targets))
/datum/status_effect/dna_melt
id = "dna_melt"
duration = 600
status_type = STATUS_EFFECT_REPLACE
alert_type = /obj/screen/alert/status_effect/dna_melt
var/kill_either_way = FALSE //no amount of removing mutations is gonna save you now
/datum/status_effect/dna_melt/on_creation(mob/living/new_owner, set_duration, updating_canmove)
. = ..()
to_chat(new_owner, "<span class='boldwarning'>My body can't handle the mutations! I need to get my mutations removed fast!</span>")
/datum/status_effect/dna_melt/on_remove()
if(!ishuman(owner))
owner.gib() //fuck you in particular
return
var/mob/living/carbon/human/H = owner
H.something_horrible(kill_either_way)
/obj/screen/alert/status_effect/dna_melt
name = "Genetic Breakdown"
desc = "I don't feel so good. Your body can't handle the mutations! You have one minute to remove your mutations, or you will be met with a horrible fate."
icon_state = "dna_melt"
+11 -1
View File
@@ -63,6 +63,9 @@
var/xenobiology_compatible = FALSE //Can the Xenobio management console transverse this area by default?
var/list/canSmoothWithAreas //typecache to limit the areas that atoms in this area can smooth with
/// Color on minimaps, if it's null (which is default) it makes one at random.
var/minimap_color
/**
* These two vars allow for multiple unique areas to be linked to a master area
* and share some functionalities such as APC powernet nodes, fire alarms etc, without sacrificing
@@ -96,7 +99,14 @@ GLOBAL_LIST_EMPTY(teleportlocs)
// ===
/area/New()
// This interacts with the map loader, so it needs to be set immediately
if(!minimap_color) // goes in New() because otherwise it doesn't fucking work
// generate one using the icon_state
if(icon_state && icon_state != "unknown")
var/icon/I = new(icon, icon_state, dir)
I.Scale(1,1)
minimap_color = I.GetPixel(1,1)
else // no icon state? use random.
minimap_color = rgb(rand(50,70),rand(50,70),rand(50,70)) // This interacts with the map loader, so it needs to be set immediately
// rather than waiting for atoms to initialize.
if (unique)
GLOB.areas_by_type[type] = src
@@ -2,6 +2,7 @@
ruletype = "Event"
var/typepath // typepath of the event
var/triggering
var/earliest_start = 20 MINUTES
/datum/dynamic_ruleset/event/get_blackbox_info()
var/list/ruleset_data = list()
@@ -25,8 +26,10 @@
return E
/datum/dynamic_ruleset/event/ready(forced = FALSE) // same as midround cause we're still using enemy system
/datum/dynamic_ruleset/event/ready(forced = FALSE)
if (!forced)
if(earliest_start >= world.time-SSticker.round_start_time)
return FALSE
var/job_check = 0
if (enemy_roles.len > 0)
for (var/mob/M in mode.current_players[CURRENT_LIVING_PLAYERS])
@@ -56,6 +59,7 @@
required_enemies = list(2,2,1,1,0,0,0,0,0,0)
weight = 5
cost = 10
earliest_start = 30 MINUTES
blocking_rules = list(/datum/dynamic_ruleset/roundstart/nuclear,/datum/dynamic_ruleset/midround/from_ghosts/nuclear)
requirements = list(70,60,50,50,40,40,40,30,20,15)
property_weights = list("story_potential" = 1, "trust" = 1, "chaos" = 1)
@@ -157,17 +161,18 @@
required_enemies = list(3,3,3,3,3,3,3,3,3,3)
cost = 15
weight = 3
earliest_start = 25 MINUTES
repeatable_weight_decrease = 2
requirements = list(60,50,40,30,30,30,30,30,30,30)
high_population_requirement = 30
property_weights = list("extended" = -2)
/datum/dynamic_ruleset/event/meteor_wave/ready()
if(mode.threat_level > 40 && mode.threat >= 25 && prob(20))
if(world.time-SSticker.round_start_time > 35 MINUTES && mode.threat_level > 40 && mode.threat >= 25 && prob(30))
name = "Meteor Wave: Threatening"
cost = 25
typepath = /datum/round_event/meteor_wave/threatening
else if(mode.threat_level > 50 && mode.threat >= 40 && prob(30))
else if(world.time-SSticker.round_start_time > 45 MINUTES && mode.threat_level > 50 && mode.threat >= 40 && prob(30))
name = "Meteor Wave: Catastrophic"
cost = 40
typepath = /datum/round_event/meteor_wave/catastrophic
@@ -280,6 +285,7 @@
cost = 4
requirements = list(10,10,10,10,10,10,10,10,10,10)
high_population_requirement = 10
earliest_start = 10 MINUTES
repeatable = TRUE
property_weights = list("extended" = 1)
@@ -324,6 +330,7 @@
requirements = list(5,5,5,5,5,5,5,5,5,5)
high_population_requirement = 5
repeatable = TRUE
earliest_start = 0 MINUTES
property_weights = list("extended" = 1)
always_max_weight = TRUE
@@ -381,3 +388,67 @@
requirements = list(5,5,5,5,5,5,5,5,5,5)
high_population_requirement = 5
property_weights = list("extended" = 1,"chaos" = 1)
/datum/dynamic_ruleset/event/portal_storm_syndicate
name = "Portal Storm"
config_tag = "portal_storm"
typepath = /datum/round_event/portal_storm/syndicate_shocktroop
cost = 10
weight = 1
enemy_roles = list("Head of Security","Security Officer","AI","Captain","Shaft Miner")
required_enemies = list(2,2,2,2,2,2,2,2,2,2)
requirements = list(101,101,101,30,30,30,30,30,30,30)
high_population_requirement = 30
earliest_start = 30 MINUTES
property_weights = list("teamwork" = 1,"chaos" = 1, "extended" = -1)
/datum/dynamic_ruleset/event/wormholes
name = "Wormholes"
config_tag = "wormhole"
typepath = /datum/round_event/wormholes
cost = 3
weight = 4
enemy_roles = list("AI","Medical Doctor","Station Engineer","Head of Personnel","Captain")
required_enemies = list(2,2,2,2,2,2,2,2,2,2)
requirements = list(5,5,5,5,5,5,5,5,5,5)
high_population_requirement = 5
property_weights = list("extended" = 1)
/datum/dynamic_ruleset/event/swarmers
name = "Swarmers"
config_tag = "swarmer"
typepath = /datum/round_event/spawn_swarmer
cost = 10
weight = 1
earliest_start = 30 MINUTES
enemy_roles = list("AI","Security Officer","Head of Security","Captain","Station Engineer","Atmos Technician","Chief Engineer")
required_enemies = list(4,4,4,4,3,3,2,2,1,1)
requirements = list(101,101,101,101,101,101,101,101,101,101)
high_population_requirement = 5
property_weights = list("extended" = -2)
/datum/dynamic_ruleset/event/sentient_disease
name = "Sentient Disease"
config_tag = "sentient_disease"
typepath = /datum/round_event/ghost_role/sentient_disease
enemy_roles = list("Virologist","Chief Medical Officer","Captain","Chemist")
required_enemies = list(2,1,1,1,0,0,0,0,0,0)
required_candidates = 1
weight = 4
cost = 5
requirements = list(30,30,20,20,15,10,10,10,10,5) // yes, it can even happen in "extended"!
property_weights = list("story_potential" = 1, "extended" = 1, "valid" = -2)
high_population_requirement = 5
/datum/dynamic_ruleset/event/revenant
name = "Revenant"
config_tag = "revenant"
typepath = /datum/round_event/ghost_role/revenant
enemy_roles = list("Chief Engineer","Station Engineer","Captain","Chaplain","AI")
required_enemies = list(2,1,1,1,0,0,0,0,0,0)
required_candidates = 1
weight = 4
cost = 5
requirements = list(30,30,30,30,20,15,15,15,15,15)
high_population_requirement = 15
property_weights = list("story_potential" = -2, "extended" = -1)
@@ -34,7 +34,7 @@
living_players = trim_list(mode.current_players[CURRENT_LIVING_PLAYERS])
living_antags = trim_list(mode.current_players[CURRENT_LIVING_ANTAGS])
list_observers = trim_list(mode.current_players[CURRENT_OBSERVERS])
var/datum/element/ghost_role_eligibility/eligibility = SSdcs.GetElement(/datum/element/ghost_role_eligibility)
var/datum/element/ghost_role_eligibility/eligibility = SSdcs.GetElement(list(/datum/element/ghost_role_eligibility))
ghost_eligible = trim_list(eligibility.get_all_ghost_role_eligible())
/datum/dynamic_ruleset/midround/proc/trim_list(list/L = list())
@@ -273,10 +273,14 @@
var/ion_announce = 33
var/removeDontImproveChance = 10
/datum/dynamic_ruleset/midround/malf/ready()
if(!candidates || !candidates.len)
return FALSE
return ..()
/datum/dynamic_ruleset/midround/malf/trim_candidates()
..()
living_players = candidates[CURRENT_LIVING_PLAYERS]
for(var/mob/living/player in candidates)
for(var/mob/living/player in living_players)
if(!isAI(player))
candidates -= player
continue
@@ -287,8 +291,6 @@
candidates -= player
/datum/dynamic_ruleset/midround/malf/execute()
if(!candidates || !candidates.len)
return FALSE
var/mob/living/silicon/ai/M = pick_n_take(candidates)
assigned += M.mind
var/datum/antagonist/traitor/AI = new
@@ -337,6 +339,7 @@
/datum/dynamic_ruleset/midround/from_ghosts/wizard/finish_setup(mob/new_character, index)
..()
new_character.forceMove(pick(GLOB.wizardstart))
wizard = new_character.mind
/datum/dynamic_ruleset/midround/from_ghosts/wizard/rule_process() // i can literally copy this from are_special_antags_dead it's great
if(isliving(wizard.current) && wizard.current.stat!=DEAD)
@@ -447,9 +450,7 @@
property_weights = list("story_potential" = -1, "trust" = 1, "chaos" = 2, "extended" = -2, "valid" = 2)
var/list/vents = list()
/datum/dynamic_ruleset/midround/from_ghosts/xenomorph/execute()
// 50% chance of being incremented by one
required_candidates += prob(50)
/datum/dynamic_ruleset/midround/from_ghosts/xenomorph/ready()
for(var/obj/machinery/atmospherics/components/unary/vent_pump/temp_vent in GLOB.machines)
if(QDELETED(temp_vent))
continue
@@ -463,6 +464,12 @@
vents += temp_vent
if(!vents.len)
return FALSE
return ..()
/datum/dynamic_ruleset/midround/from_ghosts/xenomorph/execute()
// 50% chance of being incremented by one
required_candidates += prob(50)
. = ..()
/datum/dynamic_ruleset/midround/from_ghosts/xenomorph/generate_ruleset_body(mob/applicant)
@@ -497,7 +504,7 @@
property_weights = list("story_potential" = 1, "trust" = 1, "extended" = 1, "valid" = 2, "integrity" = 1)
var/list/spawn_locs = list()
/datum/dynamic_ruleset/midround/from_ghosts/nightmare/execute()
/datum/dynamic_ruleset/midround/from_ghosts/nightmare/ready()
for(var/X in GLOB.xeno_spawn)
var/turf/T = X
var/light_amount = T.get_lumcount()
@@ -505,7 +512,7 @@
spawn_locs += T
if(!spawn_locs.len)
return FALSE
. = ..()
return ..()
/datum/dynamic_ruleset/midround/from_ghosts/nightmare/generate_ruleset_body(mob/applicant)
var/datum/mind/player_mind = new /datum/mind(applicant.key)
@@ -523,88 +530,6 @@
log_game("DYNAMIC: [key_name(S)] was spawned as a Nightmare by the midround ruleset.")
return S
//////////////////////////////////////////////
// //
// SENTIENT DISEASE //
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/midround/from_ghosts/sentient_disease
name = "Sentient Disease"
config_tag = "sentient_disease"
antag_flag = ROLE_ALIEN
enemy_roles = list("Virologist","Chief Medical Officer","Captain","Chemist")
required_enemies = list(2,1,1,1,0,0,0,0,0,0)
required_candidates = 1
weight = 4
cost = 5
requirements = list(30,30,20,20,15,10,10,10,10,5) // yes, it can even happen in "extended"!
property_weights = list("story_potential" = 1, "extended" = 1, "valid" = -2)
high_population_requirement = 5
/datum/dynamic_ruleset/midround/from_ghosts/sentient_disease/generate_ruleset_body(mob/applicant)
var/mob/camera/disease/virus = new /mob/camera/disease(SSmapping.get_station_center())
applicant.transfer_ckey(virus, FALSE)
INVOKE_ASYNC(virus, /mob/camera/disease/proc/pick_name)
message_admins("[ADMIN_LOOKUPFLW(virus)] has been made into a sentient disease by the midround ruleset.")
log_game("[key_name(virus)] was spawned as a sentient disease by the midround ruleset.")
return virus
//////////////////////////////////////////////
// //
// REVENANT //
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/midround/from_ghosts/revenant
name = "Revenant"
config_tag = "revenant"
antag_flag = ROLE_REVENANT
enemy_roles = list("Chief Engineer","Station Engineer","Captain","Chaplain","AI")
required_enemies = list(2,1,1,1,0,0,0,0,0,0)
required_candidates = 1
weight = 4
cost = 5
requirements = list(30,30,30,30,20,15,15,15,15,15)
high_population_requirement = 15
property_weights = list("story_potential" = -2, "extended" = -1)
var/list/spawn_locs = list()
/datum/dynamic_ruleset/midround/from_ghosts/revenant/acceptable(population = 0,threat = 0)
var/deadMobs = 0
for(var/mob/M in GLOB.dead_mob_list)
deadMobs++
if(deadMobs < REVENANT_SPAWN_THRESHOLD)
return FALSE
return ..()
/datum/dynamic_ruleset/midround/from_ghosts/revenant/ready(forced = FALSE)
for(var/mob/living/L in GLOB.dead_mob_list) //look for any dead bodies
var/turf/T = get_turf(L)
if(T && is_station_level(T.z))
spawn_locs += T
if(!spawn_locs.len || spawn_locs.len < 15) //look for any morgue trays, crematoriums, ect if there weren't alot of dead bodies on the station to pick from
for(var/obj/structure/bodycontainer/bc in GLOB.bodycontainers)
var/turf/T = get_turf(bc)
if(T && is_station_level(T.z))
spawn_locs += T
if(!spawn_locs.len) //If we can't find any valid spawnpoints, try the carp spawns
for(var/obj/effect/landmark/carpspawn/L in GLOB.landmarks_list)
if(isturf(L.loc))
spawn_locs += L.loc
if(!spawn_locs.len) //If we can't find THAT, then just give up and cry
return FALSE
return ..()
/datum/dynamic_ruleset/midround/from_ghosts/revenant/generate_ruleset_body(mob/applicant)
var/mob/living/simple_animal/revenant/revvie = new(pick(spawn_locs))
applicant.transfer_ckey(revvie, FALSE)
message_admins("[ADMIN_LOOKUPFLW(revvie)] has been made into a revenant by the midround ruleset.")
log_game("[key_name(revvie)] was spawned as a revenant by the midround ruleset.")
return revvie
//////////////////////////////////////////////
// //
// SLAUGHTER DEMON //
@@ -668,11 +593,11 @@
required_candidates = 2
weight = 8
cost = 10
requirements = list(101,101,70,50,40,30,30,20,15,15)
requirements = list(101,101,70,50,40,30,30,30,30,30)
blocking_rules = list(/datum/dynamic_ruleset/roundstart/nuclear,/datum/dynamic_ruleset/midround/from_ghosts/nuclear)
high_population_requirement = 15
var/datum/team/abductor_team/team
property_weights = list("story_potential" = 1, "extended" = -2, "valid" = 1, "trust" = -1, "chaos" = 2)
property_weights = list("extended" = -2, "valid" = 1, "trust" = -1, "chaos" = 2)
repeatable_weight_decrease = 4
repeatable = TRUE
@@ -742,7 +667,8 @@
var/datum/antagonist/ninja/ninjadatum = new
ninjadatum.helping_station = pick(TRUE,FALSE)
if(ninjadatum.helping_station)
mode.refund_threat(5)
mode.refund_threat(cost+5)
mode.log_threat("Ninja was helping station; [cost+5] cost refunded.")
Mind.add_antag_datum(ninjadatum)
if(Ninja.mind != Mind) //something has gone wrong!
@@ -623,8 +623,7 @@
config_tag = "clownops"
antag_datum = /datum/antagonist/nukeop/clownop
antag_leader_datum = /datum/antagonist/nukeop/leader/clownop
requirements = list(101,101,101,101,101,101,101,101,101,101)
high_population_requirement = 101
weight = 1
property_weights = list("trust" = 2, "chaos" = 2, "extended" = -2, "story_potential" = 2, "valid" = 2)
@@ -1,16 +1,22 @@
/datum/dynamic_storyteller
var/name = "none"
var/config_tag = null
var/desc = "A coder's idiocy."
var/list/property_weights = list()
var/curve_centre = 0
var/curve_width = 1.8
var/name = "none" // Name for voting.
var/config_tag = null // Config tag for config weights.
var/desc = "A coder's idiocy." // Description for voting.
var/list/property_weights = list() // See below.
var/curve_centre = 0 // As GLOB.dynamic_curve_centre.
var/curve_width = 1.8 // As GLOB.dynamic_curve_width.
var/forced_threat_level = -1
/*
NO_ASSASSIN: Will not have permanent assassination targets.
WAROPS_ALWAYS_ALLOWED: Can always do warops, regardless of threat level.
USE_PREF_WEIGHTS: Will use peoples' preferences to change the threat centre.
*/
var/flags = 0
var/weight = 3 // how many rounds need to have been recently played for this storyteller to be left out of the vote
var/event_frequency_lower = 6 MINUTES
var/event_frequency_upper = 20 MINUTES
var/datum/game_mode/dynamic/mode = null
var/weight = 3 // Weights for randomly picking storyteller. Multiplied by score after voting.
var/event_frequency_lower = 6 MINUTES // How rare events will be, at least.
var/event_frequency_upper = 20 MINUTES // How rare events will be, at most.
var/pop_antag_ratio = 5 // How many non-antags there should be vs antags.
var/datum/game_mode/dynamic/mode = null // Cached as soon as it's made, by dynamic.
/**
Property weights are:
@@ -80,7 +86,7 @@ Property weights are:
var/chance = 0
// If the high pop override is in effect, we reduce the impact of population on the antag injection chance
var/high_pop_factor = (mode.current_players[CURRENT_LIVING_PLAYERS].len >= GLOB.dynamic_high_pop_limit)
var/max_pop_per_antag = max(5,15 - round(mode.threat_level/10) - round(mode.current_players[CURRENT_LIVING_PLAYERS].len/(high_pop_factor ? 10 : 5)))
var/max_pop_per_antag = max(pop_antag_ratio,15 - round(mode.threat_level/10) - round(mode.current_players[CURRENT_LIVING_PLAYERS].len/(high_pop_factor ? 10 : 5)))
if (!mode.current_players[CURRENT_LIVING_ANTAGS].len)
chance += 80 // No antags at all? let's boost those odds!
else
@@ -173,12 +179,13 @@ Property weights are:
name = "Chaotic"
config_tag = "chaotic"
curve_centre = 10
desc = "Chaos: high. Variation: high. Likely antags: clock cult, revs, wizard."
desc = "High chaos modes. Revs, wizard, clock cult. Multiple antags at once. Chaos is kept up all round."
property_weights = list("extended" = -1, "chaos" = 10)
weight = 1
event_frequency_lower = 2 MINUTES
event_frequency_upper = 10 MINUTES
flags = WAROPS_ALWAYS_ALLOWED
pop_antag_ratio = 4
var/refund_cooldown = 0
/datum/dynamic_storyteller/cowabunga/get_midround_cooldown()
@@ -189,14 +196,14 @@ Property weights are:
/datum/dynamic_storyteller/cowabunga/do_process()
if(refund_cooldown < world.time)
mode.refund_threat(20)
mode.log_threat("Cowabunga it is. Refunded 20 threat. Threat is now [mode.threat].")
refund_cooldown = world.time + 600 SECONDS
mode.refund_threat(40)
mode.log_threat("Chaotic storyteller refunded 40 threat. Threat is now [mode.threat].")
refund_cooldown = world.time + 1200 SECONDS
/datum/dynamic_storyteller/team
name = "Teamwork"
config_tag = "teamwork"
desc = "Chaos: high. Variation: low. Likely antags: nukies, clockwork cult, wizard, blob, xenomorph."
desc = "Modes where the crew must band together. Nukies, xenos, blob. Only one antag threat at once."
curve_centre = 2
curve_width = 1.5
weight = 2
@@ -209,56 +216,71 @@ Property weights are:
/datum/dynamic_storyteller/conversion
name = "Conversion"
config_tag = "conversion"
desc = "Chaos: high. Variation: medium. Likely antags: cults, bloodsuckers, revs."
desc = "Conversion antags. Cults, revs."
curve_centre = 3
curve_width = 1
weight = 2
weight = 0
flags = WAROPS_ALWAYS_ALLOWED
property_weights = list("valid" = 1, "conversion" = 20)
/datum/dynamic_storyteller/classic
name = "Random"
config_tag = "random"
desc = "Chaos: varies. Variation: highest. No special weights attached."
weight = 6
flags = USE_PREF_WEIGHTS
desc = "No special weights attached. Anything goes."
weight = 4
curve_width = 4
pop_antag_ratio = 7
flags = USE_PREF_WEIGHTS
/datum/dynamic_storyteller/memes
/datum/dynamic_storyteller/story
name = "Story"
config_tag = "story"
desc = "Chaos: varies. Variation: high. Likely antags: abductors, nukies, wizard, traitor."
weight = 4
flags = USE_PREF_WEIGHTS
curve_width = 4
desc = "Antags with options for loadouts and gimmicks. Traitor, wizard, nukies."
weight = 2
curve_width = 2
pop_antag_ratio = 7
property_weights = list("story_potential" = 10)
/datum/dynamic_storyteller/suspicion
name = "Intrigue"
config_tag = "intrigue"
desc = "Chaos: low. Variation: high. Likely antags: traitor, bloodsucker. Rare: revs, blood cult."
weight = 4
flags = USE_PREF_WEIGHTS
curve_width = 4
desc = "Antags that instill distrust in the crew. Traitors, bloodsuckers."
weight = 2
curve_width = 2
pop_antag_ratio = 7
property_weights = list("trust" = -5)
/datum/dynamic_storyteller/liteextended
name = "Calm"
config_tag = "calm"
desc = "Chaos: low. Variation: medium. Likely antags: bloodsuckers, traitors, sentient disease, revenant."
desc = "Low-chaos round. Few antags. No conversion."
curve_centre = -3
curve_width = 0.5
flags = NO_ASSASSIN
weight = 1
pop_antag_ratio = 10
property_weights = list("extended" = 1, "chaos" = -1, "valid" = -1, "story_potential" = 1, "conversion" = -10)
/datum/dynamic_storyteller/no_antag
name = "Extended"
config_tag = "semiextended"
desc = "No standard antags. Threatening events may still spawn."
curve_centre = -5
curve_width = 0.5
flags = NO_ASSASSIN
weight = 2
property_weights = list("extended" = 1, "chaos" = -1, "valid" = -1, "story_potential" = 1, "conversion" = -10)
weight = 1
property_weights = list("extended" = 2)
/datum/dynamic_storyteller/liteextended/get_injection_chance(dry_run = FALSE)
return ..()/2
/datum/dynamic_storyteller/no_antag/roundstart_draft()
return list()
/datum/dynamic_storyteller/no_antag/get_injection_chance(dry_run)
return 0
/datum/dynamic_storyteller/extended
name = "Extended"
name = "Super Extended"
config_tag = "extended"
desc = "Chaos: none. Variation: none. Likely antags: none."
desc = "No antags. No dangerous events."
curve_centre = -20
weight = 0
curve_width = 0.5
+6 -1
View File
@@ -1163,12 +1163,17 @@ GLOBAL_LIST_EMPTY(possible_sabotages)
var/payout = 0
var/payout_bonus = 0
var/area/dropoff = null
var/static/list/blacklisted_areas = typecacheof(list(/area/ai_monitored/turret_protected,
/area/solar/,
/area/ruin/, //thank you station space ruins
/area/science/test_area/,
/area/shuttle/))
/datum/objective/contract/proc/generate_dropoff() // Generate a random valid area on the station that the dropoff will happen.
var/found = FALSE
while(!found)
var/area/dropoff_area = pick(GLOB.sortedAreas)
if(dropoff_area && is_station_level(dropoff_area.z) && !dropoff_area.outdoors && !istype(dropoff_area, /area/shuttle/))
if(dropoff_area && is_station_level(dropoff_area.z) && !dropoff_area.outdoors && !is_type_in_typecache(dropoff_area, blacklisted_areas))
dropoff = dropoff_area
found = TRUE
+4 -5
View File
@@ -47,8 +47,8 @@
)
/obj/machinery/autolathe/Initialize()
AddComponent(/datum/component/material_container,
list(/datum/material/iron,
var/static/list/allowed_types = list(
/datum/material/iron,
/datum/material/glass,
/datum/material/gold,
/datum/material/silver,
@@ -62,10 +62,9 @@
/datum/material/plastic,
/datum/material/adamantine,
/datum/material/mythril
),
0, TRUE, null, null, CALLBACK(src, .proc/AfterMaterialInsert))
)
AddComponent(/datum/component/material_container, allowed_types, _show_on_examine=TRUE, _after_insert=CALLBACK(src, .proc/AfterMaterialInsert))
. = ..()
wires = new /datum/wires/autolathe(src)
stored_research = new /datum/techweb/specialized/autounlocking/autolathe
matching_designs = list()
+6 -3
View File
@@ -77,6 +77,8 @@
name = "cloning data disk"
icon_state = "datadisk0" //Gosh I hope syndies don't mistake them for the nuke disk.
var/list/fields = list()
var/list/mutations = list()
var/max_mutations = 6
var/read_only = 0 //Well,it's still a floppy disk
//Disk stuff.
@@ -129,7 +131,7 @@
return examine(user)
//Start growing a human clone in the pod!
/obj/machinery/clonepod/proc/growclone(ckey, clonename, ui, se, mindref, datum/species/mrace, list/features, factions, list/quirks)
/obj/machinery/clonepod/proc/growclone(ckey, clonename, ui, mutation_index, mindref, datum/species/mrace, list/features, factions, list/quirks)
if(panel_open)
return FALSE
if(mess || attempting)
@@ -165,8 +167,9 @@
var/mob/living/carbon/human/H = new /mob/living/carbon/human(src)
H.hardset_dna(ui, se, H.real_name, null, mrace, features)
H.randmutb() //100% bad mutation. Can be cured with mutadone.
H.hardset_dna(ui, mutation_index, H.real_name, null, mrace, features)
H.easy_randmut(NEGATIVE+MINOR_NEGATIVE) //100% bad mutation. Can be cured with mutadone.
H.silent = 20 //Prevents an extreme edge case where clones could speak if they said something at exactly the right moment.
occupant = H
+1 -1
View File
@@ -485,7 +485,7 @@
R.fields["id"] = copytext_char(md5(mob_occupant.real_name), 2, 6)
R.fields["UE"] = dna.unique_enzymes
R.fields["UI"] = dna.uni_identity
R.fields["SE"] = dna.struc_enzymes
R.fields["SE"] = dna.mutation_index
R.fields["blood_type"] = dna.blood_type
R.fields["features"] = dna.features
R.fields["factions"] = mob_occupant.faction
+592 -142
View File
@@ -1,14 +1,16 @@
#define INJECTOR_TIMEOUT 100
#define REJUVENATORS_INJECT 15
#define REJUVENATORS_MAX 90
#define NUMBER_OF_BUFFERS 3
#define SCRAMBLE_TIMEOUT 600
#define JOKER_TIMEOUT 12000 //20 minutes
#define JOKER_UPGRADE 1800
#define RADIATION_STRENGTH_MAX 15
#define RADIATION_STRENGTH_MULTIPLIER 1 //larger has a more range
#define RADIATION_STRENGTH_MULTIPLIER 1 //larger has more range
#define RADIATION_DURATION_MAX 30
#define RADIATION_ACCURACY_MULTIPLIER 3 //larger is less accurate
#define RADIATION_IRRADIATION_MULTIPLIER 1 //multiplier for how much radiation a test subject receives
#define SCANNER_ACTION_SE 1
@@ -23,22 +25,44 @@
icon_keyboard = "med_key"
density = TRUE
circuit = /obj/item/circuitboard/computer/scan_consolenew
var/radduration = 2
var/radstrength = 1
var/list/buffer[NUMBER_OF_BUFFERS]
var/injectorready = 0 //world timer cooldown var
var/current_screen = "mainmenu"
var/obj/machinery/dna_scannernew/connected = null
var/obj/item/disk/data/diskette = null
var/list/delayed_action = null
use_power = IDLE_POWER_USE
idle_power_usage = 10
active_power_usage = 400
light_color = LIGHT_COLOR_BLUE
var/datum/techweb/stored_research
var/max_storage = 6
var/combine
var/radduration = 2
var/radstrength = 1
var/max_chromosomes = 6
///Amount of mutations we can store
var/list/buffer[NUMBER_OF_BUFFERS]
///mutations we have stored
var/list/stored_mutations = list()
///chromosomes we have stored
var/list/stored_chromosomes = list()
///combinations of injectors for the 'injector selection'. format is list("Elsa" = list(Cryokinesis, Geladikinesis), "The Hulk" = list(Hulk, Gigantism), etc) Glowy and the gang being an initialized datum
var/list/injector_selection = list()
///max amount of selections you can make
var/max_injector_selections = 2
///hard-cap on the advanced dna injector
var/max_injector_mutations = 10
///the max instability of the advanced injector.
var/max_injector_instability = 50
var/injectorready = 0 //world timer cooldown var
var/jokerready = 0
var/scrambleready = 0
var/current_screen = "mainmenu"
var/current_mutation //what block are we inspecting? only used when screen = "info"
var/current_storage //what storage block are we looking at?
var/obj/machinery/dna_scannernew/connected = null
var/obj/item/disk/data/diskette = null
var/list/delayed_action = null
/obj/machinery/computer/scan_consolenew/attackby(obj/item/I, mob/user, params)
if (istype(I, /obj/item/disk/data)) //INSERT SOME DISKETTES
if (!src.diskette)
@@ -48,6 +72,28 @@
to_chat(user, "<span class='notice'>You insert [I].</span>")
src.updateUsrDialog()
return
if (istype(I, /obj/item/chromosome))
if(LAZYLEN(stored_chromosomes) < max_chromosomes)
I.forceMove(src)
stored_chromosomes += I
to_chat(user, "<span class='notice'>You insert [I].</span>")
else
to_chat(user, "<span class='warning'>You cannot store any more chromosomes!</span>")
return
if(istype(I, /obj/item/dnainjector/activator))
var/obj/item/dnainjector/activator/A = I
if(A.used)
to_chat(user,"<span class='notice'>Recycled [I].</span>")
if(A.research)
var/c_typepath = generate_chromosome()
var/obj/item/chromosome/CM = new c_typepath (drop_location())
to_chat(user,"<span class='notice'>Recycled [I].</span>")
if((LAZYLEN(stored_chromosomes) < max_chromosomes) && prob(60))
CM.forceMove(src)
stored_chromosomes += CM
to_chat(user,"<span class='notice'>[capitalize(CM.name)] added to storage.</span>")
qdel(I)
return
else
return ..()
@@ -58,12 +104,26 @@
if(!isnull(connected))
break
injectorready = world.time + INJECTOR_TIMEOUT
scrambleready = world.time + SCRAMBLE_TIMEOUT
jokerready = world.time + JOKER_TIMEOUT
stored_research = SSresearch.science_tech
/obj/machinery/computer/scan_consolenew/examine(mob/user)
. = ..()
if(jokerready < world.time)
. += "<span class='notice'>JOKER algorithm available.</span>"
else
. += "<span class='notice'>JOKER algorithm available in about [round(0.00166666667 * (jokerready - world.time))] minutes."
/obj/machinery/computer/scan_consolenew/ui_interact(mob/user, last_change)
. = ..()
if(!user)
return
var/datum/browser/popup = new(user, "scannernew", "DNA Modifier Console", 800, 630) // Set up the popup browser window
if(user.client)
var/datum/asset/simple/assets = get_asset_datum(/datum/asset/simple/genetics)
assets.send(user.client)
if(!(in_range(src, user) || hasSiliconAccessInArea(user)))
popup.close()
return
@@ -88,8 +148,6 @@
occupant_status += "</div></div>"
occupant_status += "<div class='line'><div class='statusLabel'>Health:</div><div class='progressBar'><div style='width: [viable_occupant.health]%;' class='progressFill good'></div></div><div class='statusValue'>[viable_occupant.health] %</div></div>"
occupant_status += "<div class='line'><div class='statusLabel'>Radiation Level:</div><div class='progressBar'><div style='width: [viable_occupant.radiation/(RAD_MOB_SAFE/100)]%;' class='progressFill bad'></div></div><div class='statusValue'>[viable_occupant.radiation/(RAD_MOB_SAFE/100)] %</div></div>"
var/rejuvenators = viable_occupant.reagents.get_reagent_amount(/datum/reagent/medicine/potass_iodide)
occupant_status += "<div class='line'><div class='statusLabel'>Rejuvenators:</div><div class='progressBar'><div style='width: [round((rejuvenators / REJUVENATORS_MAX) * 100)]%;' class='progressFill highlight'></div></div><div class='statusValue'>[rejuvenators] units</div></div>"
occupant_status += "<div class='line'><div class='statusLabel'>Unique Enzymes :</div><div class='statusValue'><span class='highlight'>[viable_occupant.dna.unique_enzymes]</span></div></div>"
occupant_status += "<div class='line'><div class='statusLabel'>Last Operation:</div><div class='statusValue'>[last_change ? last_change : "----"]</div></div>"
else
@@ -147,28 +205,61 @@
buttons += "<a href='?src=[REF(src)];task=togglelock;'>[connected.locked ? "Unlock" : "Lock"] Scanner</a>"
else
buttons += "<span class='linkOff'>Open Scanner</span> <span class='linkOff'>Lock Scanner</span>"
if(viable_occupant)
buttons += "<a href='?src=[REF(src)];task=rejuv'>Inject Rejuvenators</a>"
if(viable_occupant && (scrambleready < world.time))
buttons += "<a href='?src=[REF(src)];task=scramble'>Scramble DNA</a>"
else
buttons += "<span class='linkOff'>Inject Rejuvenators</span>"
buttons += "<span class='linkOff'>Scramble DNA</span>"
if(diskette)
buttons += "<a href='?src=[REF(src)];task=ejectdisk'>Eject Disk</a>"
buttons += "<a href='?src=[REF(src)];task=screen;text=disk;'>Disk</a>"
else
buttons += "<span class='linkOff'>Eject Disk</span>"
if(current_screen == "buffer")
buttons += "<a href='?src=[REF(src)];task=screen;text=mainmenu;'>Radiation Emitter Menu</a>"
buttons += "<span class='linkOff'>Disk</span>"
if(current_screen == "mutations")
buttons += "<span class='linkOff'>Mutations</span>"
else
buttons += "<a href='?src=[REF(src)];task=screen;text=buffer;'>Buffer Menu</a>"
buttons += "<a href='?src=[REF(src)];task=screen;text=mutations;'>Mutations</a>"
if((current_screen == "mainmenu") || !current_screen)
buttons += "<span class='linkOff'>Genetic Sequencer</span>"
else
buttons += "<a href='?src=[REF(src)];task=screen;text=mainmenu;'>Genetic Sequencer</a>"
if(current_screen == "ui")
buttons += "<span class='linkOff'>Unique Identifiers</span>"
else
buttons += "<a href='?src=[REF(src)];task=screen;text=ui;'>Unique Identifiers</a>"
if(current_screen == "advinjector")
buttons += "<span class='linkOff'>Adv. Injectors</span>"
else
buttons += "<a href='?src=[REF(src)];task=screen;text=advinjector;'>Adv. Injectors</a>"
switch(current_screen)
if("working")
temp_html += status
temp_html += "<h1>System Busy</h1>"
temp_html += "Working ... Please wait ([DisplayTimeText(radduration*10)])"
if("buffer")
if("ui")
temp_html += status
temp_html += buttons
temp_html += "<h1>Buffer Menu</h1>"
temp_html += "<h1>Unique Identifiers</h1>"
temp_html += "<a href='?src=[REF(src)];task=setstrength;num=[radstrength-1];'>--</a> <a href='?src=[REF(src)];task=setstrength;'>Output Level</a> <a href='?src=[REF(src)];task=setstrength;num=[radstrength+1];'>++</a>"
temp_html += "<br><a href='?src=[REF(src)];task=setduration;num=[radduration-1];'>--</a> <a href='?src=[REF(src)];task=setduration;'>Pulse Duration</a> <a href='?src=[REF(src)];task=setduration;num=[radduration+1];'>++</a>"
temp_html += "<h3>Irradiate Subject</h3>"
temp_html += "<div class='line'><div class='statusLabel'>Unique Identifier:</div><div class='statusValue'><div class='clearBoth'>"
var/max_line_len = 7*DNA_BLOCK_SIZE
if(viable_occupant)
temp_html += "<div class='dnaBlockNumber'>1</div>"
var/char = ""
var/ui_text = viable_occupant.dna.uni_identity
var/len_byte = length(ui_text)
var/char_it = 0
for(var/byte_it = 1, byte_it <= len_byte, byte_it += length(char))
char_it++
char = ui_text[byte_it]
temp_html += "<a class='dnaBlock' href='?src=[REF(src)];task=pulseui;num=[char_it];'>[char]</a>"
if((char_it % max_line_len) == 0)
temp_html += "</div><div class='clearBoth'>"
if((char_it % DNA_BLOCK_SIZE) == 0 && byte_it < len_byte)
temp_html += "<div class='dnaBlockNumber'>[(char_it / DNA_BLOCK_SIZE) + 1]</div>"
else
temp_html += "---------"
temp_html += "</div></div><br><h1>Buffer Menu</h1>"
if(istype(buffer))
for(var/i=1, i<=buffer.len, i++)
@@ -188,7 +279,6 @@
temp_html += "<span class='linkOff'>Save to Disk</span>"
else
var/ui = buffer_slot["UI"]
var/se = buffer_slot["SE"]
var/ue = buffer_slot["UE"]
var/name = buffer_slot["name"]
var/label = buffer_slot["label"]
@@ -234,19 +324,6 @@
temp_html += "<a href='?src=[REF(src)];task=injector;num=[i];text=mixed'>UI+UE Injector</a>"
else
temp_html += "<span class='linkOff'>UI+UE Injector</span>"
if(se)
temp_html += "<br>\tSE: [se] "
if(viable_occupant)
temp_html += "<a href='?src=[REF(src)];task=transferbuffer;num=[i];text=se'>Occupant</a>"
else
temp_html += "<span class='linkOff'>Occupant</span>"
temp_html += "<a href='?src=[REF(src)];task=setdelayed;num=[i];delayaction=[SCANNER_ACTION_SE]'>Occupant:Delayed</a>"
if(injectorready < world.time )
temp_html += "<a href='?src=[REF(src)];task=injector;num=[i];text=se'>Injector</a>"
else
temp_html += "<span class='linkOff'>Injector</span>"
else
temp_html += "<br>\tSE: No Data"
if(viable_occupant)
temp_html += "<br><a href='?src=[REF(src)];task=setbuffer;num=[i];'>Save to Buffer</a>"
else
@@ -260,58 +337,225 @@
temp_html += "<a href='?src=[REF(src)];task=savedisk;num=[i];'>Save to Disk</a>"
else
temp_html += "<span class='linkOff'>Save to Disk</span>"
if("disk")
temp_html += status
temp_html += buttons
if(diskette)
temp_html += "<h3>[diskette.name]</h3><br>"
temp_html += "<a href='?src=[REF(src)];task=ejectdisk'>Eject Disk</a><br>"
if(LAZYLEN(diskette.mutations))
temp_html += "<table>"
for(var/datum/mutation/human/A in diskette.mutations)
temp_html += "<tr><td><span class='linkOff'>[A.name]</span></td>"
temp_html += "<td><a href='?src=[REF(src)];task=deletediskmut;num=[diskette.mutations.Find(A)];'>Delete</a></td>"
if(LAZYLEN(stored_mutations) < max_storage)
temp_html += "<td><a href='?src=[REF(src)];task=importdiskmut;num=[diskette.mutations.Find(A)];'>Import</a></td>"
else
temp_html += "<td><td><span class='linkOff'>Import</span></td>"
temp_html += "</tr>"
temp_html += "</table>"
else
temp_html += "<br>Load diskette to start ----------"
if("info")
if(LAZYLEN(stored_mutations))
if(LAZYLEN(stored_mutations) >= current_storage)
var/datum/mutation/human/HM = stored_mutations[current_storage]
if(HM)
temp_html += display_sequence(HM.type, current_storage)
else
current_screen = "mainmenu"
if("mutations")
temp_html += status
temp_html += buttons
temp_html += "<h3>Mutation Storage:<br></h3>"
temp_html += "<table>"
for(var/datum/mutation/human/HM in stored_mutations)
var/i = stored_mutations.Find(HM)
temp_html += "<tr><td><a href='?src=[REF(src)];task=inspectstorage;num=[i]'>[HM.name]</a></td>"
temp_html += "<td><a href='?src=[REF(src)];task=exportdiskmut;path=[HM.type]'>Export</a></td>"
temp_html += "<td><a href='?src=[REF(src)];task=deletemut;num=[i]'>Delete</a></td>"
if(combine == HM.type)
temp_html += "<td><span class='linkOff'>Combine</span></td></tr>"
else
temp_html += "<td><a href='?src=[REF(src)];task=combine;num=[i]'>Combine</a></td></tr>"
temp_html += "</table><br>"
temp_html += "<h3>Chromosome Storage:<br></h3>"
temp_html += "<table>"
for(var/i in 1 to stored_chromosomes.len)
var/obj/item/chromosome/CM = stored_chromosomes[i]
temp_html += "<td><a href='?src=[REF(src)];task=ejectchromosome;num=[i]'>[CM.name]</a></td><br>"
temp_html += "</table>"
if("advinjector")
temp_html += status
temp_html += buttons
temp_html += "<div class='line'><div class='statusLabel'><b>Advanced Injectors:</b></div></div><br>"
temp_html += "<div class='statusLine'><a href='?src=[REF(src)];task=add_advinjector;'>New Selection</a></div>"
for(var/A in injector_selection)
temp_html += "<div class='statusDisplay'><b>[A]</b>"
var/list/true_selection = injector_selection[A]
temp_html += "<br>"
for(var/B in true_selection)
var/datum/mutation/human/HM = B
var/mutcolor
switch(HM.quality)
if(POSITIVE)
mutcolor = "good"
if(MINOR_NEGATIVE)
mutcolor = "average"
if(NEGATIVE)
mutcolor = "bad"
temp_html += "<div class='statusLine'><span class='[mutcolor]'>[HM.name] </span>"
temp_html += "<a href='?src=[REF(src)];task=remove_from_advinjector;injector=[A];path=[HM.type];'>Remove</a></div>"
if (injectorready < world.time)
temp_html += "<div class='statusLine'> <a href='?src=[REF(src)];task=advinjector;injector=[A];'>Print Advanced Injector</a>"
else
temp_html += "<div class='statusLine'> <span class='linkOff'>Print Advanced Injector</span>"
temp_html += "<a href='?src=[REF(src)];task=remove_advinjector;injector=[A];'>Remove Injector</a></div>"
temp_html += "<br></div>"
else
temp_html += status
temp_html += buttons
temp_html += "<h1>Radiation Emitter Menu</h1>"
temp_html += "<a href='?src=[REF(src)];task=setstrength;num=[radstrength-1];'>--</a> <a href='?src=[REF(src)];task=setstrength;'>Output Level</a> <a href='?src=[REF(src)];task=setstrength;num=[radstrength+1];'>++</a>"
temp_html += "<br><a href='?src=[REF(src)];task=setduration;num=[radduration-1];'>--</a> <a href='?src=[REF(src)];task=setduration;'>Pulse Duration</a> <a href='?src=[REF(src)];task=setduration;num=[radduration+1];'>++</a>"
temp_html += "<h3>Irradiate Subject</h3>"
temp_html += "<div class='line'><div class='statusLabel'>Unique Identifier:</div><div class='statusValue'><div class='clearBoth'>"
var/max_line_len = 7*DNA_BLOCK_SIZE
temp_html += "<div class='line'><div class='statusLabel'>Genetic Sequence:</div><br>"
if(viable_occupant)
temp_html += "<div class='dnaBlockNumber'>1</div>"
var/char = ""
var/ui_text = viable_occupant.dna.uni_identity
var/len_byte = length(ui_text)
var/char_it = 0
for(var/byte_it = 1, byte_it <= len_byte, byte_it += length(char))
char_it++
char = ui_text[byte_it]
temp_html += "<a class='dnaBlock' href='?src=[REF(src)];task=pulseui;num=[char_it];'>[char]</a>"
if((char_it % max_line_len) == 0)
temp_html += "</div><div class='clearBoth'>"
if((char_it % DNA_BLOCK_SIZE) == 0 && byte_it < len_byte)
temp_html += "<div class='dnaBlockNumber'>[(char_it / DNA_BLOCK_SIZE) + 1]</div>"
if(viable_occupant)
for(var/A in get_mutation_list())
temp_html += display_inactive_sequence(A)
temp_html += "<br>"
else
temp_html += "----"
if(viable_occupant && (current_mutation in get_mutation_list(viable_occupant)))
temp_html += display_sequence(current_mutation)
temp_html += "</div><br>"
else
temp_html += "----"
temp_html += "</div></div></div><br>"
temp_html += "<br><div class='line'><div class='statusLabel'>Structural Enzymes:</div><div class='statusValue'><div class='clearBoth'>"
if(viable_occupant)
temp_html += "<div class='dnaBlockNumber'>1</div>"
var/char = ""
var/se_text = viable_occupant.dna.struc_enzymes
var/len_byte = length(se_text)
var/char_it = 0
for(var/byte_it = 1, byte_it <= len_byte, byte_it += length(char))
char_it++
char = se_text[byte_it]
temp_html += "<a class='dnaBlock' href='?src=[REF(src)];task=pulsese;num=[char_it];'>[char]</a>"
if((char_it % max_line_len) == 0)
temp_html += "</div><div class='clearBoth'>"
if((char_it % DNA_BLOCK_SIZE) == 0 && byte_it < len_byte)
temp_html += "<div class='dnaBlockNumber'>[(char_it / DNA_BLOCK_SIZE) + 1]</div>"
else
temp_html += "----"
temp_html += "</div></div></div>"
temp_html += "----------"
popup.set_content(temp_html.Join())
popup.open()
/obj/machinery/computer/scan_consolenew/proc/display_inactive_sequence(mutation)
var/temp_html = ""
var/class = "unselected"
var/mob/living/carbon/viable_occupant = get_viable_occupant()
if(!viable_occupant)
return
var/location = viable_occupant.dna.mutation_index.Find(mutation) //We do this because we dont want people using sysexp or similair tools to just read the mutations.
if(!location) //Do this only when needed, dont make a list with mutations for every iteration if you dont need to
var/list/mutations = get_mutation_list(TRUE)
if(mutation in mutations)
location = mutations.Find(mutation)
if(mutation == current_mutation)
class = "selected"
if(location > DNA_MUTATION_BLOCKS)
temp_html += "<a class='clean' href='?src=[REF(src)];task=inspect;num=[location];'><img class='[class]' src='dna_extra.gif' width = '65' alt='Extra Mutation'></a>"
else if(mutation in stored_research.discovered_mutations)
temp_html += "<a class='clean' href='?src=[REF(src)];task=inspect;num=[location];'><img class='[class]' src='dna_discovered.gif' width = '65' alt='Discovered Mutation'></a>"
else
temp_html += "<a class='clean' clean href='?src=[REF(src)];task=inspect;num=[location];'><img class='[class]' src='dna_undiscovered.gif' width = '65' alt=Undiscovered Mutation'></a>"
return temp_html
/obj/machinery/computer/scan_consolenew/proc/display_sequence(mutation, storage_slot) //Storage slot is for when viewing from the stored mutations
var/temp_html = ""
if(!mutation)
temp_html += "ERR-"
return
var/mut_name = "Unknown gene"
var/mut_desc = "No information available."
var/alias
var/discovered = FALSE
var/active = FALSE
var/scrambled = FALSE
var/instability
var/mob/living/carbon/viable_occupant = get_viable_occupant()
var/datum/mutation/human/HM = get_valid_mutation(mutation)
if(viable_occupant)
var/datum/mutation/human/M = viable_occupant.dna.get_mutation(mutation)
if(M)
scrambled = M.scrambled
active = TRUE
var/datum/mutation/human/A = GET_INITIALIZED_MUTATION(mutation)
alias = A.alias
if(active && !scrambled)
discover(mutation)
if(stored_research && (mutation in stored_research.discovered_mutations))
mut_name = A.name
mut_desc = A.desc
discovered = TRUE
instability = A.instability
var/extra
if(viable_occupant && !(storage_slot || viable_occupant.dna.mutation_in_sequence(mutation)))
extra = TRUE
if(discovered && !scrambled)
var/mutcolor
switch(A.quality)
if(POSITIVE)
mutcolor = "good"
if(MINOR_NEGATIVE)
mutcolor = "average"
if(NEGATIVE)
mutcolor = "bad"
if(HM)
instability *= GET_MUTATION_STABILIZER(HM)
temp_html += "<div class='statusDisplay'><div class='statusLine'><span class='[mutcolor]'><b>[mut_name]</b></span><small> ([alias])</small><br>"
temp_html += "<div class='statusLine'>Instability : [round(instability)]</span><br>"
else
temp_html += "<div class='statusDisplay'><div class='statusLine'><b>[alias]</b><br>"
temp_html += "<div class='statusLine'>[mut_desc]<br></div>"
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 = "<a href='?src=[REF(src)];task=applychromosome;path=[mutation];num=[i];'>----</a>"
if(HM.chromosome_name)
chromosome_name = HM.chromosome_name
temp_html += "<div class='statusLine'>Chromosome status: [chromosome_name]<br></div>"
temp_html += "<div class='statusLine'>Sequence:<br><br></div>"
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 += "<div class='statusLine'><table class='statusDisplay'><tr>"
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 += "<td><div class='statusLine'><span class='dnaBlockNumber'><a href='?src=[REF(src)];task=pulsegene;num=[genenum];path=[mutation];'>[sequence[num]]</span></a></div></td>"
temp_html += "</tr><tr>"
for(var/i in 1 to DNA_SEQUENCE_LENGTH)
temp_html += "<td><div class='statusLine'>|</div></td>"
temp_html += "</tr><tr>"
for(var/i in 1 to DNA_SEQUENCE_LENGTH)
var/num = i*2
var/genenum = num+(DNA_SEQUENCE_LENGTH*2*(block-1))
temp_html += "<td><div class='statusLine'><span class='dnaBlockNumber'><a href='?src=[REF(src)];task=pulsegene;num=[genenum];path=[mutation];'>[sequence[num]]</span></a></div></td>"
temp_html += "</tr></table></div>"
temp_html += "<br><br><br><br><br>"
else
temp_html = "<div class='statusLine'>Sequence unreadable due to unpredictable mutation.</div>"
if((active || storage_slot) && (injectorready < world.time) && !scrambled)
temp_html += "<a href='?src=[REF(src)];task=activator;path=[mutation];slot=[storage_slot];'>Print Activator</a>"
temp_html += "<a href='?src=[REF(src)];task=mutator;path=[mutation];slot;=[storage_slot];'>Print Mutator</a>"
else
temp_html += "<span class='linkOff'>Print Activator</span>"
temp_html += "<span class='linkOff'>Print Mutator</span>"
temp_html += "<br><div class='statusLine'>"
if(storage_slot)
temp_html += "<a href='?src=[REF(src)];task=deletemut;num=[storage_slot];'>Delete</a>"
if((LAZYLEN(stored_mutations) < max_storage) && diskette && !diskette.read_only)
temp_html += "<a href='?src=[REF(src)];task=exportdiskmut;path=[mutation];'>Export</a>"
else
temp_html += "<span class='linkOff'>Export</span>"
temp_html += "<a href='?src=[REF(src)];task=screen;text=mutations;'>Back</a>"
else if(active && !scrambled)
temp_html += "<a href='?src=[REF(src)];task=savemut;path=[mutation];'>Store</a>"
temp_html += "<a href='?src=[REF(src)];task=expand_advinjector;path=[mutation];'>Adv. Injector</a>"
if(extra || scrambled)
temp_html += "<a href='?src=[REF(src)];task=nullify;'>Nullify</a>"
else
temp_html += "<span class='linkOff'>Nullify</span>"
temp_html += "</div></div>"
return temp_html
/obj/machinery/computer/scan_consolenew/Topic(href, href_list)
if(..())
@@ -350,13 +594,15 @@
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(/datum/reagent/medicine/potass_iodide)
var/can_add = max(min(REJUVENATORS_MAX - potassiodide_amount, REJUVENATORS_INJECT), 0)
viable_occupant.reagents.add_reagent(/datum/reagent/medicine/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,"<span class'notice'>DNA scrambled.</span>")
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]
@@ -368,7 +614,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
@@ -382,8 +627,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")
@@ -397,28 +640,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)
@@ -457,7 +678,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)
@@ -469,7 +690,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
@@ -478,7 +699,7 @@
var/len = length_char(viable_occupant.dna.uni_identity)
num = WRAP(num, 1, len+1)
num = randomize_radiation_accuracy(num, radduration + (connected.precision_coeff ** 2), len) //Each manipulator level above 1 makes randomization as accurate as selected time + manipulator lvl^2
//Value is this high for the same reason as with laser - not worth the hassle of upgrading if the bonus is low
//Value is this high for the same reason as with laser - not worth the hassle of upgrading if the bonus is low
var/block = round((num-1)/DNA_BLOCK_SIZE)+1
var/subblock = num - block*DNA_BLOCK_SIZE
last_change = "UI #[block]-[subblock]; "
@@ -490,35 +711,229 @@
viable_occupant.dna.uni_identity = copytext_char(viable_occupant.dna.uni_identity, 1, num) + hex + copytext_char(viable_occupant.dna.uni_identity, num + 1)
viable_occupant.updateappearance(mutations_overlay_update=1)
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_char(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_char(viable_occupant.dna.struc_enzymes, 1, num) + hex + copytext_char(viable_occupant.dna.struc_enzymes, num + 1)
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,"<span class='notice'>Mutation succesfully stored.</span>")
if(!succes) //we can exactly return here
to_chat(usr,"<span class='warning'>Mutation storage is full.</span>")
if("deletemut")
var/datum/mutation/human/HM = stored_mutations[num]
if(HM)
stored_mutations.Remove(HM)
qdel(HM)
current_screen = "mutations"
if("activator")
if(injectorready < world.time)
var/mutation = text2path(href_list["path"])
if(ispath(mutation, /datum/mutation/human))
var/datum/mutation/human/HM = get_valid_mutation(mutation)
if(HM)
var/obj/item/dnainjector/activator/I = new /obj/item/dnainjector/activator(loc)
I.add_mutations += new HM.type (copymut = HM)
I.name = "[HM.name] activator"
I.research = TRUE
if(connected)
I.damage_coeff = connected.damage_coeff*4
injectorready = world.time + INJECTOR_TIMEOUT * (1 - 0.1 * connected.precision_coeff) //precision_coeff being the matter bin rating
else
injectorready = world.time + INJECTOR_TIMEOUT
if("mutator")
if(injectorready < world.time)
var/mutation = text2path(href_list["path"])
if(ispath(mutation, /datum/mutation/human))
var/datum/mutation/human/HM = get_valid_mutation(mutation)
if(HM)
var/obj/item/dnainjector/activator/I = new /obj/item/dnainjector/activator(loc)
I.add_mutations += new HM.type (copymut = HM)
I.doitanyway = TRUE
I.name = "[HM.name] injector"
if(connected)
I.damage_coeff = connected.damage_coeff
injectorready = world.time + INJECTOR_TIMEOUT * 5 * (1 - 0.1 * connected.precision_coeff)
else
injectorready = world.time + INJECTOR_TIMEOUT * 5
if("advinjector")
var/selection = href_list["injector"]
if(injectorready < world.time)
if(injector_selection.Find(selection))
var/list/true_selection = injector_selection[selection]
if(LAZYLEN(injector_selection))
var/obj/item/dnainjector/activator/I = new /obj/item/dnainjector/activator(loc)
for(var/A in true_selection)
var/datum/mutation/human/HM = A
I.add_mutations += new HM.type (copymut = HM)
I.doitanyway = TRUE
I.name = "Advanced [selection] injector"
if(connected)
I.damage_coeff = connected.damage_coeff
injectorready = world.time + INJECTOR_TIMEOUT * 8 * (1 - 0.1 * connected.precision_coeff)
else
injectorready = world.time + INJECTOR_TIMEOUT * 8
if("nullify")
if(viable_occupant)
var/datum/mutation/human/A = viable_occupant.dna.get_mutation(current_mutation)
if(A && (!viable_occupant.dna.mutation_in_sequence(current_mutation) || A.scrambled))
viable_occupant.dna.remove_mutation(current_mutation)
current_screen = "mainmenu"
current_mutation = null
if("pulsegene")
if(current_screen != "info")
var/path = text2path(href_list["path"])
if(viable_occupant && num && (path in viable_occupant.dna.mutation_index))
var/list/genes = list("A","T","G","C","X")
if(jokerready < world.time)
genes += "JOKER"
var/sequence = GET_GENE_STRING(path, viable_occupant.dna)
var/original = sequence[num]
var/new_gene = input("From [original] to-", "New block", original) as null|anything in genes
if(!new_gene)
new_gene = original
if(viable_occupant == get_viable_occupant()) //No cheesing
if((new_gene == "JOKER") && (jokerready < world.time))
var/true_genes = GET_SEQUENCE(current_mutation)
new_gene = true_genes[num]
jokerready = world.time + JOKER_TIMEOUT - (JOKER_UPGRADE * (connected.precision_coeff-1))
sequence = copytext(sequence, 1, num) + new_gene + copytext(sequence, num+1, length(sequence)+1)
viable_occupant.dna.mutation_index[path] = sequence
viable_occupant.radiation += RADIATION_STRENGTH_MULTIPLIER/connected.damage_coeff
viable_occupant.domutcheck()
if("exportdiskmut")
if(diskette && !diskette.read_only)
var/path = text2path(href_list["path"])
if(ispath(path, /datum/mutation/human))
var/datum/mutation/human/A = get_valid_mutation(path)
if(A && diskette && (LAZYLEN(diskette.mutations) < diskette.max_mutations))
var/datum/mutation/human/HM = new A.type()
diskette.mutations += HM
HM.copy_mutation(A)
to_chat(usr, "<span class='notice'>Succesfully written [A.name] to [diskette.name].</span>")
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,"<span class='notice'>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, "<span class='boldnotice'>Succes! New mutation has been added to storage</span>")
discover(result_path)
combine = null
else
to_chat(usr, "<span class='warning'>Failed. No mutation could be created.</span>")
combine = null
else
combine = path
to_chat(usr,"<span class='notice'>Selected [A.name] for combining</span>")
else
to_chat(usr, "<span class='warning'>Not enough space to store potential mutation.</span>")
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, "<span class='notice'>You apply [CM] to [HM.name].</span>")
stored_chromosomes -= CM
CM.apply(HM)
if("expand_advinjector")
var/mutation = text2path(href_list["path"])
var/datum/mutation/human/HM = get_valid_mutation(mutation)
if(HM && LAZYLEN(injector_selection))
var/which_injector = input(usr, "Select Adv. Injector", "Advanced Injectors") as null|anything in injector_selection
if(injector_selection.Find(which_injector))
var/list/true_selection = injector_selection[which_injector]
var/total_instability
for(var/B in true_selection)
var/datum/mutation/human/mootacion = B
total_instability += mootacion.instability
total_instability += HM.instability
if((total_instability > max_injector_instability) || (true_selection.len + 1) > max_injector_mutations)
to_chat(usr, "<span class='warning'>Adding more mutations would make the advanced injector too unstable!</span>")
else
true_selection += HM //reminder that this works. because I keep forgetting this works
if("remove_from_advinjector")
var/mutation = text2path(href_list["path"])
var/selection = href_list["injector"]
if(injector_selection.Find(selection))
var/list/true_selection = injector_selection[selection]
for(var/B in true_selection)
var/datum/mutation/human/HM = B
if(HM.type == mutation)
true_selection -= HM
break
if("remove_advinjector")
var/selection = href_list["injector"]
for(selection in injector_selection)
if(selection == selection)
injector_selection.Remove(selection)
if("add_advinjector")
if(LAZYLEN(injector_selection) < max_injector_selections)
var/new_selection = input(usr, "Enter Adv. Injector name", "Advanced Injectors") as text|null
if(new_selection && !(new_selection in injector_selection))
injector_selection[new_selection] = list()
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
@@ -547,10 +962,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"]
@@ -572,15 +983,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, "<span class='notice'>[src] activates!</span>")
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
+1 -1
View File
@@ -22,7 +22,7 @@
precision_coeff = 0
for(var/obj/item/stock_parts/scanning_module/P in component_parts)
scan_level += P.rating
for(var/obj/item/stock_parts/manipulator/P in component_parts)
for(var/obj/item/stock_parts/matter_bin/P in component_parts)
precision_coeff = P.rating
for(var/obj/item/stock_parts/micro_laser/P in component_parts)
damage_coeff = P.rating
+13 -13
View File
@@ -9,7 +9,7 @@
internal_radio = FALSE
//Start growing a human clone in the pod!
/obj/machinery/clonepod/experimental/growclone(ckey, clonename, ui, se, datum/species/mrace, list/features, factions)
/obj/machinery/clonepod/experimental/growclone(clonename, ui, mutation_index, mindref, last_death, blood_type, datum/species/mrace, list/features, factions, list/quirks)
if(panel_open)
return FALSE
if(mess || attempting)
@@ -20,15 +20,15 @@
var/mob/living/carbon/human/H = new /mob/living/carbon/human(src)
H.hardset_dna(ui, se, H.real_name, null, mrace, features)
H.hardset_dna(ui, mutation_index, H.real_name, blood_type, mrace, features)
if(efficiency > 2)
var/list/unclean_mutations = (GLOB.not_good_mutations|GLOB.bad_mutations)
H.dna.remove_mutation_group(unclean_mutations)
if(efficiency > 5 && prob(20))
H.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
@@ -42,18 +42,17 @@
icon_state = "pod_1"
//Get the clone body ready
maim_clone(H)
ADD_TRAIT(H, TRAIT_STABLEHEART, CLONING_POD_TRAIT)
ADD_TRAIT(H, TRAIT_STABLELIVER, CLONING_POD_TRAIT)
ADD_TRAIT(H, TRAIT_EMOTEMUTE, CLONING_POD_TRAIT)
ADD_TRAIT(H, TRAIT_MUTE, CLONING_POD_TRAIT)
ADD_TRAIT(H, TRAIT_NOBREATH, CLONING_POD_TRAIT)
ADD_TRAIT(H, TRAIT_NOCRITDAMAGE, CLONING_POD_TRAIT)
ADD_TRAIT(H, TRAIT_STABLEHEART, "cloning")
ADD_TRAIT(H, TRAIT_EMOTEMUTE, "cloning")
ADD_TRAIT(H, TRAIT_MUTE, "cloning")
ADD_TRAIT(H, TRAIT_NOBREATH, "cloning")
ADD_TRAIT(H, TRAIT_NOCRITDAMAGE, "cloning")
H.Unconscious(80)
var/list/candidates = pollCandidatesForMob("Do you want and agree to play as a [clonename]'s defective clone, respect their character and not engage in ERP without permission from the original?", null, null, null, 100, H, POLL_IGNORE_CLONE)
var/list/candidates = pollCandidatesForMob("Do you want to play as [clonename]'s defective clone?", null, null, null, 100, H)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
C.transfer_ckey(H)
H.key = C.key
if(grab_ghost_when == CLONER_FRESH_CLONE)
H.grab_ghost()
@@ -293,6 +292,7 @@
temp = "<font class='bad'>Cloning cycle already in progress.</font>"
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(mob_occupant.real_name, dna.uni_identity, dna.mutation_index, null, null, dna.blood_type, clone_species, dna.features, mob_occupant.faction)
temp = "[mob_occupant.real_name] => <font class='good'>Cloning data sent to pod.</font>"
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
+2 -2
View File
@@ -63,7 +63,7 @@ Buildable meters
if(make_from.flipped)
do_a_flip()
/obj/item/pipe/dropped()
/obj/item/pipe/dropped(mob/user)
if(loc)
setPipingLayer(piping_layer)
return ..()
@@ -225,7 +225,7 @@ Buildable meters
to_chat(user, "<span class='notice'>You fasten the meter to the [loc.name].</span>")
qdel(src)
/obj/item/pipe_meter/dropped()
/obj/item/pipe_meter/dropped(mob/user)
. = ..()
if(loc)
setAttachLayer(piping_layer)
+6 -1
View File
@@ -46,7 +46,12 @@
)
/obj/machinery/autoylathe/Initialize()
AddComponent(/datum/component/material_container, list(/datum/material/iron, /datum/material/glass, /datum/material/plastic), 0, TRUE, null, null, CALLBACK(src, .proc/AfterMaterialInsert))
var/static/list/allowed_materials = list(
/datum/material/iron,
/datum/material/glass,
/datum/material/plastic
)
AddComponent(/datum/component/material_container, allowed_materials, 0, TRUE, null, null, CALLBACK(src, .proc/AfterMaterialInsert))
. = ..()
wires = new /datum/wires/autoylathe(src)
@@ -283,13 +283,13 @@
/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/proc/get_charge()
if(equip_ready) //disabled
return
var/area/A = get_base_area(chassis)
var/pow_chan = get_power_channel(A)
var/area/A = get_area(chassis)
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)
+13 -3
View File
@@ -35,9 +35,19 @@
)
/obj/machinery/mecha_part_fabricator/Initialize()
var/datum/component/material_container/materials = AddComponent(/datum/component/material_container,
list(/datum/material/iron, /datum/material/glass, /datum/material/silver, /datum/material/gold, /datum/material/diamond, /datum/material/plasma, /datum/material/uranium, /datum/material/bananium, /datum/material/titanium, /datum/material/bluespace), 0,
TRUE, /obj/item/stack, CALLBACK(src, .proc/is_insertion_ready), CALLBACK(src, .proc/AfterMaterialInsert))
var/static/list/allowed_types = list(
/datum/material/iron,
/datum/material/glass,
/datum/material/silver,
/datum/material/gold,
/datum/material/diamond,
/datum/material/plasma,
/datum/material/uranium,
/datum/material/bananium,
/datum/material/titanium,
/datum/material/bluespace
)
var/datum/component/material_container/materials = AddComponent(/datum/component/material_container, allowed_types, 0, TRUE, /obj/item/stack, CALLBACK(src, .proc/is_insertion_ready), CALLBACK(src, .proc/AfterMaterialInsert))
materials.precise_insertion = TRUE
stored_research = new
return ..()
+1 -5
View File
@@ -320,11 +320,7 @@
clearInternalDamage(MECHA_INT_CONTROL_LOST)
/obj/mecha/narsie_act()
if(occupant)
var/mob/living/L = occupant
go_out(TRUE)
if(L)
L.narsie_act()
emp_act(EMP_HEAVY)
/obj/mecha/ratvar_act()
if((GLOB.ratvar_awakens || GLOB.clockwork_gateway_activated) && occupant)
+24
View File
@@ -28,6 +28,7 @@
take_damage(5, BURN, 0, 0)
/obj/structure/spider/stickyweb
var/genetic = FALSE
icon_state = "stickyweb1"
/obj/structure/spider/stickyweb/Initialize()
@@ -36,6 +37,8 @@
. = ..()
/obj/structure/spider/stickyweb/CanPass(atom/movable/mover, turf/target)
if (genetic)
return
if(istype(mover, /mob/living/simple_animal/hostile/poison/giant_spider))
return TRUE
else if(isliving(mover))
@@ -48,6 +51,27 @@
return prob(30)
return TRUE
/obj/structure/spider/stickyweb/genetic //for the spider genes in genetics
genetic = TRUE
var/mob/living/allowed_mob
/obj/structure/spider/stickyweb/genetic/Initialize(mapload, allowedmob)
allowed_mob = allowedmob
. = ..()
/obj/structure/spider/stickyweb/genetic/CanPass(atom/movable/mover, turf/target)
. = ..() //this is the normal spider web return aka a spider would make this TRUE
if(mover == allowed_mob)
return TRUE
else if(isliving(mover)) //we change the spider to not be able to go through here
if(mover.pulledby == allowed_mob)
return TRUE
if(prob(50))
to_chat(mover, "<span class='danger'>You get stuck in \the [src] for a moment.</span>")
return FALSE
else if(istype(mover, /obj/item/projectile))
return prob(30)
/obj/structure/spider/eggcluster
name = "egg cluster"
desc = "They seem to pulse slightly with an inner life."
+6
View File
@@ -850,3 +850,9 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
if (HAS_TRAIT(src, TRAIT_NODROP))
return
return ..()
/obj/item/proc/embedded(mob/living/carbon/human/embedded_mob)
return
/obj/item/proc/unembedded()
return
+1 -1
View File
@@ -117,7 +117,7 @@
. = ..()
if(!isinhands || !(loaded?.amount))
return
var/mutable_appearance/cable_overlay = mutable_appearance(icon, "rcl-[CEILING(loaded.amount/(max_amount/3), 1)]")
var/mutable_appearance/cable_overlay = mutable_appearance(icon_file, "rcl-[CEILING(loaded.amount/(max_amount/3), 1)]")
cable_overlay.color = GLOB.cable_colors[colors[current_color_index]]
. += cable_overlay
+92
View File
@@ -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
..()
+1 -5
View File
@@ -18,15 +18,11 @@
/obj/item/chrono_eraser/proc/pass_mind(datum/mind/M)
erased_minds += M
/obj/item/chrono_eraser/dropped()
/obj/item/chrono_eraser/dropped(mob/user)
..()
if(PA)
qdel(PA)
/obj/item/chrono_eraser/Destroy()
dropped()
return ..()
/obj/item/chrono_eraser/ui_action_click(mob/user)
if(iscarbon(user))
var/mob/living/carbon/C = user
@@ -74,7 +74,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)
@@ -21,7 +21,7 @@
var/obj/item/cigbutt/butt = /obj/item/cigbutt
saved_appearance = initial(butt.appearance)
/obj/item/chameleon/dropped()
/obj/item/chameleon/dropped(mob/user)
..()
disrupt()
@@ -218,7 +218,7 @@
/obj/item/geiger_counter/cyborg/proc/redirect_rad_act(datum/source, amount)
rad_act(amount)
/obj/item/geiger_counter/cyborg/dropped()
/obj/item/geiger_counter/cyborg/dropped(mob/user)
. = ..()
if(listeningTo)
UnregisterSignal(listeningTo, COMSIG_ATOM_RAD_ACT)
+107 -3
View File
@@ -150,7 +150,10 @@ SLIME SCANNER
msg += "\n\t<span class='alert'>Subject appears to have [M.getCloneLoss() > 30 ? "Severe" : "Minor"] cellular damage.</span>"
if(advanced)
msg += "\n\t<span class='info'>Cellular Damage Level: [M.getCloneLoss()].</span>"
if(ishuman(M))
var/mob/living/carbon/human/H = M
if(advanced && H.has_dna())
msg += "\n\t<span class='info'>Genetic Stability: [H.dna.stability]%.</span>"
to_chat(user, msg)
msg = ""
@@ -326,8 +329,8 @@ SLIME SCANNER
breathes = FALSE
if(NOBLOOD in C.dna.species.species_traits)
blooded = FALSE
var/has_liver = (!(NOLIVER in C.dna.species.species_traits))
var/has_stomach = (!(NOSTOMACH in C.dna.species.species_traits))
var/has_liver = C.dna && !(NOLIVER in C.dna.species.species_traits)
var/has_stomach = C.dna && !(NOSTOMACH in C.dna.species.species_traits)
if(!M.getorganslot(ORGAN_SLOT_EYES))
msg += "\t<span class='alert'><b>Subject does not have eyes.</b></span>\n"
if(!M.getorganslot(ORGAN_SLOT_EARS))
@@ -776,3 +779,104 @@ SLIME SCANNER
var/response = SEND_SIGNAL(M, COMSIG_NANITE_SCAN, user, TRUE)
if(!response)
to_chat(user, "<span class='info'>No nanites detected in the subject.</span>")
/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 for analyzing someones gene sequence on the fly. Hold near a DNA console to update the internal 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
custom_materials = list(/datum/material/iron=200)
var/list/discovered = list() //hit a dna console to update the scanners database
var/list/buffer
var/ready = TRUE
var/cooldown = 200
/obj/item/sequence_scanner/attack(mob/living/M, mob/living/carbon/human/user)
add_fingerprint(user)
if (!HAS_TRAIT(M, TRAIT_RADIMMUNE)) //no scanning if its a husk or DNA-less Species
user.visible_message("<span class='notice'>[user] analyzes [M]'s genetic sequence.</span>", \
"<span class='notice'>You analyze [M]'s genetic sequence.</span>")
gene_scan(M, user)
else
user.visible_message("<span class='notice'>[user] failed to analyse [M]'s genetic sequence.</span>", "<span class='warning'>[M] has no readable genetic sequence!</span>")
/obj/item/sequence_scanner/attack_self(mob/user)
display_sequence(user)
/obj/item/sequence_scanner/attack_self_tk(mob/user)
return
/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, "<span class='notice'>[name] linked to central research database.</span>")
discovered = C.stored_research.discovered_mutations
else
to_chat(user,"<span class='warning'>No database to update from.</span>")
/obj/item/sequence_scanner/proc/gene_scan(mob/living/carbon/C, mob/living/user)
if(!iscarbon(C) || !C.has_dna())
return
buffer = C.dna.mutation_index
to_chat(user, "<span class='notice'>Subject [C.name]'s DNA sequence has been saved to buffer.</span>")
if(LAZYLEN(buffer))
for(var/A in buffer)
to_chat(user, "<span class='notice'>[get_display_name(A)]</span>")
/obj/item/sequence_scanner/proc/display_sequence(mob/living/user)
if(!LAZYLEN(buffer) || !ready)
return
var/list/options = list()
for(var/A in buffer)
options += get_display_name(A)
var/answer = input(user, "Analyze Potential", "Sequence Analyzer") as null|anything in sortList(options)
if(answer && ready && user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
var/sequence
for(var/A in buffer) //this physically hurts but i dont know what anything else short of an assoc list
if(get_display_name(A) == answer)
sequence = buffer[A]
break
if(sequence)
var/display
for(var/i in 0 to length_char(sequence) / DNA_MUTATION_BLOCKS-1)
if(i)
display += "-"
display += copytext_char(sequence, 1 + i*DNA_MUTATION_BLOCKS, DNA_MUTATION_BLOCKS*(1+i) + 1)
to_chat(user, "<span class='boldnotice'>[display]</span><br>")
ready = FALSE
icon_state = "[icon_state]_recharging"
addtimer(CALLBACK(src, .proc/recharge), cooldown, TIMER_UNIQUE)
/obj/item/sequence_scanner/proc/recharge()
icon_state = initial(icon_state)
ready = TRUE
/obj/item/sequence_scanner/proc/get_display_name(mutation)
var/datum/mutation/human/HM = GET_INITIALIZED_MUTATION(mutation)
if(!HM)
return "ERROR"
if(mutation in discovered)
return "[HM.name] ([HM.alias])"
else
return HM.alias
@@ -63,10 +63,10 @@
if(attached_device)
attached_device.holder_movement()
/obj/item/transfer_valve/dropped()
/obj/item/transfer_valve/dropped(mob/user)
. = ..()
if(attached_device)
attached_device.dropped()
attached_device.dropped(user)
/obj/item/transfer_valve/on_found(mob/finder)
if(attached_device)
+256 -75
View File
@@ -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] <span class='danger'>(MONKEY)</span>")
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,258 @@
/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/void
name = "\improper DNA injector (Void)"
add_mutations = list(VOID)
/obj/item/dnainjector/antivoid
name = "\improper DNA injector (Anti-Void)"
remove_mutations = list(VOID)
/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/radioactive
name = "\improper DNA injector (Radioactive)"
add_mutations = list(RADIOACTIVE)
/obj/item/dnainjector/antiradioactive
name = "\improper DNA injector (Anti-Radioactive)"
remove_mutations = list(RADIOACTIVE)
/obj/item/dnainjector/olfaction
name = "\improper DNA injector (Olfaction)"
add_mutations = list(OLFACTION)
/obj/item/dnainjector/antiolfaction
name = "\improper DNA injector (Anti-Olfaction)"
remove_mutations = list(OLFACTION)
/obj/item/dnainjector/insulated
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/shock
name = "\improper DNA injector (Shock Touch)"
add_mutations = list(SHOCKTOUCH)
/obj/item/dnainjector/antishock
name = "\improper DNA injector (Anti-Shock Touch)"
remove_mutations = list(SHOCKTOUCH)
/obj/item/dnainjector/spacialinstability
name = "\improper DNA injector (Spacial Instability)"
add_mutations = list(BADBLINK)
/obj/item/dnainjector/antispacialinstability
name = "\improper DNA injector (Anti-Spacial Instability)"
remove_mutations = list(BADBLINK)
/obj/item/dnainjector/acidflesh
name = "\improper DNA injector (Acid Flesh)"
add_mutations = list(ACIDFLESH)
/obj/item/dnainjector/antiacidflesh
name = "\improper DNA injector (Acid Flesh)"
remove_mutations = list(ACIDFLESH)
/obj/item/dnainjector/gigantism
name = "\improper DNA injector (Gigantism)"
add_mutations = list(GIGANTISM)
/obj/item/dnainjector/antigigantism
name = "\improper DNA injector (Anti-Gigantism)"
remove_mutations = list(GIGANTISM)
/obj/item/dnainjector/spastic
name = "\improper DNA injector (Spastic)"
add_mutations = list(SPASTIC)
/obj/item/dnainjector/antispastic
name = "\improper DNA injector (Anti-Spastic)"
remove_mutations = list(SPASTIC)
/obj/item/dnainjector/geladikinesis
name = "\improper DNA injector (Geladikinesis)"
add_mutations = list(GELADIKINESIS)
/obj/item/dnainjector/antigeladikinesis
name = "\improper DNA injector (Anti-Geladikinesis)"
remove_mutations = list(GELADIKINESIS)
/obj/item/dnainjector/cryokinesis
name = "\improper DNA injector (Cryokinesis)"
add_mutations = list(CRYOKINESIS)
/obj/item/dnainjector/anticryokinesis
name = "\improper DNA injector (Anti-Cryokinesis)"
remove_mutations = list(CRYOKINESIS)
/obj/item/dnainjector/thermal
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/glow
name = "\improper DNA injector (Glowy)"
add_mutations = list(GLOWY)
/obj/item/dnainjector/removeglow
name = "\improper DNA injector (Anti-Glowy)"
remove_mutations = list(GLOWY)
/obj/item/dnainjector/antiglow
name = "\improper DNA injector (Antiglowy)"
add_mutations = list(ANTIGLOWY)
/obj/item/dnainjector/removeantiglow
name = "\improper DNA injector (Anti-Antiglowy)"
remove_mutations = list(ANTIGLOWY)
/obj/item/dnainjector/firebreath
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/tonguespike
name = "\improper DNA injector (Tongue Spike)"
add_mutations = list(TONGUESPIKE)
/obj/item/dnainjector/antitonguespike
name = "\improper DNA injector (Anti-Tongue Spike)"
remove_mutations = list(TONGUESPIKE)
/obj/item/dnainjector/spiderweb
name = "\improper DNA injector (Spider Web)"
add_mutations = list(SPIDER_WEB)
/obj/item/dnainjector/antispiderweb
name = "\improper DNA injector (Anti-Spider Web)"
remove_mutations = list(SPIDER_WEB)
/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, "<span class='notice'>You can't modify [M]'s DNA while [M.p_theyre()] dead.</span>")
return FALSE
@@ -317,23 +467,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] <span class='danger'>(MONKEY)</span>")
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 +510,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
+5 -4
View File
@@ -11,7 +11,7 @@
/obj/item/book/granter/proc/turn_page(mob/user)
playsound(user, pick('sound/effects/pageturn1.ogg','sound/effects/pageturn2.ogg','sound/effects/pageturn3.ogg'), 30, 1)
if(do_after(user,50, user))
if(do_after(user,50, TRUE, user))
if(remarks.len)
to_chat(user, "<span class='notice'>[pick(remarks)]</span>")
else
@@ -53,7 +53,7 @@
on_reading_stopped()
reading = FALSE
return
if(do_after(user,50, user))
if(do_after(user,50, TRUE, user))
on_reading_finished(user)
reading = FALSE
return TRUE
@@ -402,10 +402,11 @@
martialname = "sleeping carp"
desc = "A scroll filled with strange markings. It seems to be drawings of some sort of martial art."
greet = "<span class='sciradio'>You have learned the ancient martial art of the Sleeping Carp! Your hand-to-hand combat has become much more effective, and you are now able to deflect any projectiles \
directed toward you. However, you are also unable to use any ranged weaponry. You can learn more about your newfound art by using the Recall Teachings verb in the Sleeping Carp tab.</span>"
directed toward you while in Throw Mode. Your body is also honed to protect you from damage and punctures, and even briefly survive space. \
However, you are also unable to use any ranged weaponry, and some medical supplies will prove useless to you. You can learn more about your newfound art by using the Recall Teachings verb in the Sleeping Carp tab.</span>"
icon = 'icons/obj/wizard.dmi'
icon_state = "scroll2"
remarks = list("I must prove myself worthy to the masters of the sleeping carp...", "Stance means everything...", "Focus... And you'll be able to incapacitate any foe in seconds...", "I must pierce armor for maximum damage...", "I don't think this would combine with other martial arts...", "Grab them first so they don't retaliate...", "I must prove myself worthy of this power...")
remarks = list("Wait, a high protein diet is really all it takes to become bulletproof...?", "Overwhelming force, immovable object...", "Focus... And you'll be able to incapacitate any foe in seconds...", "I must pierce armor for maximum damage...", "I don't think this would combine with other martial arts...", "Become one with the carp...", "Glub...")
/obj/item/book/granter/martial/carp/onlearned(mob/living/carbon/user)
..()
+2 -2
View File
@@ -357,7 +357,7 @@
/obj/item/borg/lollipop/equipped()
check_amount()
/obj/item/borg/lollipop/dropped()
/obj/item/borg/lollipop/dropped(mob/user)
check_amount()
/obj/item/borg/lollipop/proc/check_amount() //Doesn't even use processing ticks.
@@ -620,7 +620,7 @@
return host.loc
return null
/obj/item/borg/projectile_dampen/dropped()
/obj/item/borg/projectile_dampen/dropped(mob/user)
. = ..()
host = loc
@@ -462,7 +462,7 @@
desc = "An upgrade to the Medical module's hypospray, containing \
stronger versions of existing chemicals."
additional_reagents = list(/datum/reagent/medicine/oxandrolone, /datum/reagent/medicine/sal_acid,
/datum/reagent/medicine/rezadone, /datum/reagent/medicine/pen_acid)
/datum/reagent/medicine/rezadone, /datum/reagent/medicine/pen_acid, /datum/reagent/medicine/prussian_blue)
/obj/item/borg/upgrade/piercing_hypospray
name = "cyborg piercing hypospray"
+1 -1
View File
@@ -132,7 +132,7 @@
RegisterSignal(user, COMSIG_MOVABLE_MOVED, .proc/Pickup_ores)
listeningTo = user
/obj/item/storage/bag/ore/dropped()
/obj/item/storage/bag/ore/dropped(mob/user)
. = ..()
if(listeningTo)
UnregisterSignal(listeningTo, COMSIG_MOVABLE_MOVED)
@@ -525,14 +525,6 @@
desc = "A kit containing a Deluxe hypospray and Vials."
icon_state = "tactical-mini"
/obj/item/storage/hypospraykit/cmo/ComponentInitialize()
. = ..()
var/datum/component/storage/STR = GetComponent(/datum/component/storage)
STR.max_items = 6
STR.can_hold = typecacheof(list(
/obj/item/hypospray/mkii,
/obj/item/reagent_containers/glass/bottle/vial))
/obj/item/storage/hypospraykit/cmo/PopulateContents()
if(empty)
return
+1 -3
View File
@@ -25,7 +25,7 @@
/obj/item/melee/baton/examine(mob/user)
. = ..()
. += "<span class='notice'>Right click attack while in combat mode or attack while in disarm intent to disarm instead of stun.</span>"
. += "<span class='notice'>Right click attack while in combat mode to disarm instead of stun.</span>"
/obj/item/melee/baton/get_cell()
. = cell
@@ -149,8 +149,6 @@
//return TRUE to interrupt attack chain.
/obj/item/melee/baton/proc/common_baton_melee(mob/M, mob/living/user, disarming = FALSE)
if(user.a_intent == INTENT_DISARM)
disarming = TRUE //override if they're in disarm intent.
if(iscyborg(M) || !isliving(M)) //can't baton cyborgs
return FALSE
if(status && HAS_TRAIT(user, TRAIT_CLUMSY) && prob(50))
+6 -6
View File
@@ -246,11 +246,11 @@
user.visible_message("<span class='suicide'>[user] axes [user.p_them()]self from head to toe! It looks like [user.p_theyre()] trying to commit suicide!</span>")
return (BRUTELOSS)
/obj/item/twohanded/fireaxe/afterattack(atom/A, mob/user, proximity)
/obj/item/twohanded/fireaxe/afterattack(atom/A, mob/living/user, proximity)
. = ..()
if(!proximity)
if(!proximity || (user.getStaminaLoss() > STAMINA_SOFTCRIT))
return
if(wielded) //destroys windows and grilles in one hit
if(wielded) //destroys windows and grilles in one hit (or more if it has a ton of health like plasmaglass)
if(istype(A, /obj/structure/window))
var/obj/structure/window/W = A
W.take_damage(200, BRUTE, "melee", 0)
@@ -1217,7 +1217,7 @@
target.lastattackerckey = user.ckey
target.visible_message("<span class='danger'>[user] has shocked [target] with [src]!</span>", \
"<span class='userdanger'>[user] has shocked you with [src]!</span>")
log_combat(user, user, "stunned with an electrostaff")
log_combat(user, target, "stunned with an electrostaff")
playsound(src, 'sound/weapons/staff.ogg', 50, 1, -1)
target.apply_status_effect(stun_status_effect, stun_status_duration)
if(ishuman(user))
@@ -1242,9 +1242,9 @@
if(user)
target.lastattacker = user.real_name
target.lastattackerckey = user.ckey
target.visible_message("<span class='danger'>[user] has seared [user] with [src]!</span>", \
target.visible_message("<span class='danger'>[user] has seared [target] with [src]!</span>", \
"<span class='userdanger'>[user] has seared you with [src]!</span>")
log_combat(user, user, "burned with an electrostaff")
log_combat(user, target, "burned with an electrostaff")
playsound(src, 'sound/weapons/sear.ogg', 50, 1, -1)
return TRUE
+4 -5
View File
@@ -127,11 +127,10 @@
if ((M.client && M.machine == src))
is_in_use = TRUE
ui_interact(M)
if(isAI(usr) || iscyborg(usr) || IsAdminGhost(usr) || hasSiliconAccessInArea(usr))
if (!(usr in nearby))
if (usr.client && usr.machine==src) // && M.machine == src is omitted because if we triggered this by using the dialog, it doesn't matter if our machine changed in between triggering it and this - the dialog is probably still supposed to refresh.
is_in_use = TRUE
ui_interact(usr)
if(usr && hasSiliconAccessInArea(usr) && !(usr in nearby))
if (usr.client && usr.machine==src) // && M.machine == src is omitted because if we triggered this by using the dialog, it doesn't matter if our machine changed in between triggering it and this - the dialog is probably still supposed to refresh.
is_in_use = TRUE
ui_interact(usr)
// check for TK users
@@ -315,13 +315,8 @@
/obj/structure/windoor_assembly/ComponentInitialize()
. = ..()
AddComponent(
/datum/component/simple_rotation,
ROTATION_ALTCLICK | ROTATION_CLOCKWISE | ROTATION_COUNTERCLOCKWISE | ROTATION_VERBS,
null,
CALLBACK(src, .proc/can_be_rotated),
CALLBACK(src,.proc/after_rotation)
)
var/static/rotation_flags = ROTATION_ALTCLICK | ROTATION_CLOCKWISE | ROTATION_COUNTERCLOCKWISE | ROTATION_VERBS
AddComponent(/datum/component/simple_rotation, rotation_flags, can_be_rotated=CALLBACK(src, .proc/can_be_rotated), after_rotation=CALLBACK(src,.proc/after_rotation))
/obj/structure/windoor_assembly/proc/can_be_rotated(mob/user,rotation_type)
if(anchored)

Some files were not shown because too many files have changed in this diff Show More