mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-12 18:51:53 +00:00
## About The Pull Request ### Special versions are no longer vulnerable to recoil There are two versions of hulk that are not available under normal circumstances. The version given by wizard's Transformation, and the version that Warlord pirates spawn with. These versions no longer break their arms when destroying walls. Normal versions, like the genetic hulks and the orc variant, still suffer this effect. ### oof ow my bones Hulk recoil damage now utilizes RNG wound determination. This allows for the wound to escalate normally if the wound meets the threshold. The damage passed means that there is a roughly 50/50 chance to cause a dislocation, a very slim chance to roll a fracture, and this probability will grow worse once the arm is wounded and threshold penalties start to take effect. This also means that the mutation respects wound resistance and wound vulnerability, where as the previous behavior did not. ### Cleans up some mutation code a smidge There is clearly some leftovers from prior refactors still littered through mutation code, so I did some maintenance while I was here. ## Why It's Good For The Game https://github.com/tgstation/tgstation/pull/51389 introduced this weakness to dissuade hulks from flattening walls all the way to the armory (for the fuckbillionth time). However, it applies to all versions of hulk, including instances where it is an antagonist's ability or power. Rather than have antagonists suffer from balance considerations largely aimed at crew/tiders, we make them exempt so that they can SMASH to their heart's content. Hulk wound determination was kind of weird. For one, it relied heavily on the arm health consistently being 50. When it wasn't, you started getting into less reliable behaviour. In addition, it does not at all respect any kind of vulnerability or resistance to wounding. It is possible that utilizing this system was so that the wounding effects would be staggered out rather than sporadic, and so slightly fairer on the user by being more reliable. However, I think letting it operate similarly to how our natural wound determination effects work provides some more interesting outcomes for those who might want to use the mutation in an earnest fashion, and still otherwise limits those people who are just looking to low effort grief. I think in retrospect this might have been a pretty heavy-handed nerf, but I'm not wholly reversing it, I'm just making it...different. Outcomes should be largely the same.
249 lines
9.2 KiB
Plaintext
249 lines
9.2 KiB
Plaintext
|
|
/// Negatives that are virtually harmless and mostly just funny (language)
|
|
// Set to 0 because munchkinning via miscommunication = bad
|
|
#define NEGATIVE_STABILITY_MINI 0
|
|
/// Negatives that are slightly annoying (unused)
|
|
#define NEGATIVE_STABILITY_MINOR -20
|
|
/// Negatives that present an uncommon or weak, consistent hindrance to gameplay (cough, paranoia)
|
|
#define NEGATIVE_STABILITY_MODERATE -30
|
|
/// Negatives that present a major consistent hindrance to gameplay (deaf, mute, acid flesh)
|
|
#define NEGATIVE_STABILITY_MAJOR -40
|
|
|
|
/// Positives that provide basically no benefit (glowy)
|
|
#define POSITIVE_INSTABILITY_MINI 5
|
|
/// Positives that are niche in application or useful in rare circumstances (parlor tricks, geladikinesis, autotomy)
|
|
#define POSITIVE_INSTABILITY_MINOR 10
|
|
/// Positives that provide a new ability that's roughly par with station equipment (insulated, cryokinesis)
|
|
#define POSITIVE_INSTABILITY_MODERATE 25
|
|
/// Positives that are unique, very powerful, and noticeably change combat/gameplay (hulk, tk)
|
|
#define POSITIVE_INSTABILITY_MAJOR 35
|
|
|
|
/datum/mutation
|
|
var/name = "mutation"
|
|
/// Description of the mutation
|
|
var/desc = "A mutation."
|
|
/// Is this mutation currently locked?
|
|
var/locked
|
|
/// Quality of the mutation
|
|
var/quality
|
|
/// Message given to the user upon gaining this mutation
|
|
var/text_gain_indication = ""
|
|
/// Message given to the user upon losing this mutation
|
|
var/text_lose_indication = ""
|
|
/// Visual indicators upon the character of the owner of this mutation
|
|
var/static/list/visual_indicators = list()
|
|
/// The path of action we grant to our user on mutation gain
|
|
var/datum/action/cooldown/power_path
|
|
/// Which mutation layer to use
|
|
var/layer_used = MUTATIONS_LAYER
|
|
/// To restrict mutation to only certain species
|
|
var/list/species_allowed
|
|
/// Minimum health required to acquire the mutation
|
|
var/health_req
|
|
/// Required limbs to acquire this mutation
|
|
var/limb_req
|
|
/// The owner of this mutation's DNA
|
|
var/datum/dna/dna
|
|
/// Owner of this mutation
|
|
var/mob/living/carbon/human/owner
|
|
/// Instability the holder gets when the mutation is not native
|
|
var/instability = 0
|
|
/// Amount of those big blocks with gene sequences
|
|
var/blocks = 4
|
|
/// Amount of missing sequences. Sometimes it removes an entire pair for 2 points
|
|
var/difficulty = 8
|
|
/// 'Mutation #49', decided every round to get some form of distinction between undiscovered mutations
|
|
var/alias
|
|
/// Whether we can read it if it's active. To avoid cheesing with mutagen
|
|
var/scrambled = FALSE
|
|
/// The sources of the mutation (found in defines/dna.dm)
|
|
var/list/sources = list()
|
|
/**
|
|
* any mutations that might conflict.
|
|
* put mutation typepath defines in here.
|
|
* make sure to enter it both ways (so that A conflicts with B, and B with A)
|
|
*/
|
|
var/list/conflicts
|
|
var/remove_on_aheal = TRUE
|
|
|
|
/**
|
|
* can we take chromosomes?
|
|
* 0: CHROMOSOME_NEVER never
|
|
* 1: CHROMOSOME_NONE yeah
|
|
* 2: CHROMOSOME_USED no, already have one
|
|
*/
|
|
var/can_chromosome = CHROMOSOME_NONE
|
|
/// Name of the chromosome
|
|
var/chromosome_name
|
|
|
|
//Chromosome stuff - set to -1 to prevent people from changing it. Example: It'd be a waste to decrease cooldown on mutism
|
|
/// genetic stability coeff
|
|
var/stabilizer_coeff = 1
|
|
/// Makes the mutation hurt the user less
|
|
var/synchronizer_coeff = MUTATION_COEFFICIENT_UNMODIFIABLE
|
|
/// Boosts mutation strength
|
|
var/power_coeff = MUTATION_COEFFICIENT_UNMODIFIABLE
|
|
/// Lowers mutation cooldown
|
|
var/energy_coeff = MUTATION_COEFFICIENT_UNMODIFIABLE
|
|
/// List of strings of valid chromosomes this mutation can accept.
|
|
var/list/valid_chrom_list = list()
|
|
/// List of traits that are added or removed by the mutation with GENETIC_TRAIT source.
|
|
var/list/mutation_traits
|
|
|
|
/datum/mutation/New()
|
|
. = ..()
|
|
|
|
/datum/mutation/Destroy()
|
|
power_path = null
|
|
dna = null
|
|
owner = null
|
|
return ..()
|
|
|
|
/datum/mutation/proc/make_copy()
|
|
var/datum/mutation/copy = new type
|
|
|
|
copy.chromosome_name = chromosome_name
|
|
copy.stabilizer_coeff = stabilizer_coeff
|
|
copy.synchronizer_coeff = synchronizer_coeff
|
|
copy.power_coeff = power_coeff
|
|
copy.energy_coeff = energy_coeff
|
|
copy.can_chromosome = can_chromosome
|
|
copy.valid_chrom_list = valid_chrom_list
|
|
update_valid_chromosome_list()
|
|
|
|
return copy
|
|
|
|
/datum/mutation/proc/on_acquiring(mob/living/carbon/human/acquirer)
|
|
if(!acquirer || !istype(acquirer) || acquirer.stat == DEAD || (src in acquirer.dna.mutations))
|
|
return FALSE
|
|
if(species_allowed && !species_allowed.Find(acquirer.dna.species.id))
|
|
return FALSE
|
|
if(health_req && acquirer.health < health_req)
|
|
return FALSE
|
|
if(limb_req && !acquirer.get_bodypart(limb_req))
|
|
return FALSE
|
|
for(var/datum/mutation/mewtayshun as anything in acquirer.dna.mutations) //check for conflicting powers
|
|
if(!(mewtayshun.type in conflicts) && !(type in mewtayshun.conflicts))
|
|
continue
|
|
to_chat(acquirer, span_warning("You feel your genes resisting something."))
|
|
return FALSE
|
|
owner = acquirer
|
|
dna = acquirer.dna
|
|
dna.mutations += src
|
|
SEND_SIGNAL(src, COMSIG_MUTATION_GAINED, acquirer)
|
|
if(text_gain_indication)
|
|
to_chat(owner, text_gain_indication)
|
|
if(visual_indicators.len)
|
|
var/list/mut_overlay = list(get_visual_indicator())
|
|
if(owner.overlays_standing[layer_used])
|
|
mut_overlay = owner.overlays_standing[layer_used]
|
|
mut_overlay |= get_visual_indicator()
|
|
owner.remove_overlay(layer_used)
|
|
owner.overlays_standing[layer_used] = mut_overlay
|
|
owner.apply_overlay(layer_used)
|
|
grant_power() //we do checks here so nothing about hulk getting magic
|
|
if(mutation_traits)
|
|
owner.add_traits(mutation_traits, GENETIC_MUTATION)
|
|
return TRUE
|
|
|
|
/datum/mutation/proc/get_visual_indicator()
|
|
return
|
|
|
|
/datum/mutation/proc/on_life(seconds_per_tick, times_fired)
|
|
return
|
|
|
|
/datum/mutation/proc/on_losing(mob/living/carbon/human/owner)
|
|
if(!istype(owner) || !(owner.dna.mutations.Remove(src)))
|
|
return TRUE
|
|
. = FALSE
|
|
SEND_SIGNAL(src, COMSIG_MUTATION_LOST, owner)
|
|
if(text_lose_indication && owner.stat != DEAD)
|
|
to_chat(owner, text_lose_indication)
|
|
if(visual_indicators.len)
|
|
var/list/mut_overlay = list()
|
|
if(owner.overlays_standing[layer_used])
|
|
mut_overlay = owner.overlays_standing[layer_used]
|
|
owner.remove_overlay(layer_used)
|
|
mut_overlay.Remove(get_visual_indicator())
|
|
owner.overlays_standing[layer_used] = mut_overlay
|
|
owner.apply_overlay(layer_used)
|
|
|
|
if(mutation_traits)
|
|
owner.remove_traits(mutation_traits, GENETIC_MUTATION)
|
|
|
|
/mob/living/carbon/proc/update_mutations_overlay()
|
|
return
|
|
|
|
/mob/living/carbon/human/update_mutations_overlay()
|
|
for(var/datum/mutation/mutation as anything in dna.mutations)
|
|
if(mutation.species_allowed && !mutation.species_allowed.Find(dna.species.id))
|
|
dna.remove_mutation(mutation, mutation.sources) //shouldn't have that mutation at all
|
|
continue
|
|
if(mutation.visual_indicators.len == 0)
|
|
continue
|
|
var/list/mut_overlay = list()
|
|
if(overlays_standing[mutation.layer_used])
|
|
mut_overlay = overlays_standing[mutation.layer_used]
|
|
var/mutable_appearance/indicator_to_add = mutation.get_visual_indicator()
|
|
if(!mut_overlay.Find(indicator_to_add)) //either we lack the visual indicator or we have the wrong one
|
|
remove_overlay(mutation.layer_used)
|
|
for(var/mutable_appearance/indicator_to_remove in mutation.visual_indicators[mutation.type])
|
|
mut_overlay.Remove(indicator_to_remove)
|
|
mut_overlay |= indicator_to_add
|
|
overlays_standing[mutation.layer_used] = mut_overlay
|
|
apply_overlay(mutation.layer_used)
|
|
|
|
/**
|
|
* Called after on_aquiring, or when a chromosome is applied.
|
|
* returns the instance of 'power_path' for children calls to use without calling locate() again.
|
|
*/
|
|
/datum/mutation/proc/setup()
|
|
if(!power_path || QDELETED(owner))
|
|
return
|
|
var/datum/action/cooldown/modified_power = locate(power_path) in owner.actions
|
|
if(!modified_power)
|
|
CRASH("Genetic mutation [type] called setup(), but could not find a action to modify!")
|
|
modified_power.cooldown_time = initial(modified_power.cooldown_time) * GET_MUTATION_ENERGY(src)
|
|
return modified_power
|
|
|
|
/datum/mutation/proc/remove_chromosome()
|
|
stabilizer_coeff = initial(stabilizer_coeff)
|
|
synchronizer_coeff = initial(synchronizer_coeff)
|
|
power_coeff = initial(power_coeff)
|
|
energy_coeff = initial(energy_coeff)
|
|
can_chromosome = initial(can_chromosome)
|
|
chromosome_name = null
|
|
|
|
/datum/mutation/proc/grant_power()
|
|
if(!ispath(power_path) || !owner)
|
|
return FALSE
|
|
|
|
var/datum/action/cooldown/new_power = new power_path(src)
|
|
new_power.background_icon_state = "bg_tech_blue"
|
|
new_power.base_background_icon_state = new_power.background_icon_state
|
|
new_power.active_background_icon_state = "[new_power.base_background_icon_state]_active"
|
|
new_power.overlay_icon_state = "bg_tech_blue_border"
|
|
new_power.active_overlay_icon_state = "bg_spell_border_active_blue"
|
|
new_power.panel = "Genetic"
|
|
new_power.Grant(owner)
|
|
|
|
return new_power
|
|
|
|
// Runs through all the coefficients and uses this to determine which chromosomes the
|
|
// mutation can take. Stores these as text strings in a list.
|
|
/datum/mutation/proc/update_valid_chromosome_list()
|
|
valid_chrom_list.Cut()
|
|
|
|
if(can_chromosome == CHROMOSOME_NEVER)
|
|
valid_chrom_list += "none"
|
|
return
|
|
|
|
if(stabilizer_coeff != MUTATION_COEFFICIENT_UNMODIFIABLE)
|
|
valid_chrom_list += "Stabilizer"
|
|
if(synchronizer_coeff != MUTATION_COEFFICIENT_UNMODIFIABLE)
|
|
valid_chrom_list += "Synchronizer"
|
|
if(power_coeff != MUTATION_COEFFICIENT_UNMODIFIABLE)
|
|
valid_chrom_list += "Power"
|
|
if(energy_coeff != MUTATION_COEFFICIENT_UNMODIFIABLE)
|
|
valid_chrom_list += "Energetic"
|