Merge branch 'master' of https://github.com/Citadel-Station-13/Citadel-Station-13 into Ghommie-cit616
This commit is contained in:
+65
-31
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
. = ..()
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,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
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
@@ -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."
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
@@ -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()
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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(..())
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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?
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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]
|
||||
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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>"
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 ..()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
..()
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
..()
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user