Blob Chunks 2 (#9143)

* Restore blob chunks to the world.

Blobs have chunks they spawn on death, carrying an artifact effect related to the blob type. The chunk carries a copy of the parent blob's type datum, to prevent modification of a blob causing issues with the chunk, and vice versa, if both somehow exist at the same time.

* Organization, origin tech.

* Convert to List Based system, drop transferring type datums directly as discussed in DMs

* Added Volatile Alluvium chunk effect.

Nanite swarm chunks exist, but don't do anything special for now.

Blob Chunks can sniff a mob to take their faction.

* Copy requires static

* Chunk and effect code cleanliness
lifemerger now utilizes a timer&callback instead of spawn()
This commit is contained in:
Mechoid
2023-12-27 21:40:25 -08:00
committed by GitHub
parent b54652baf6
commit 14c681720d
30 changed files with 616 additions and 19 deletions

View File

@@ -8,6 +8,3 @@
#define BLOB_DIFFICULTY_MEDIUM 1 #define BLOB_DIFFICULTY_MEDIUM 1
#define BLOB_DIFFICULTY_HARD 2 #define BLOB_DIFFICULTY_HARD 2
#define BLOB_DIFFICULTY_SUPERHARD 3 #define BLOB_DIFFICULTY_SUPERHARD 3
#define BLOB_CHUNK_CONSTANT 0
#define BLOB_CHUNK_TOGGLE 1

View File

@@ -113,6 +113,7 @@ var/global/list/blob_cores = list()
blob_cores -= src blob_cores -= src
if(overmind) if(overmind)
overmind.blob_core = null overmind.blob_core = null
overmind.blob_type.make_chunk(get_turf(src))
qdel(overmind) qdel(overmind)
overmind = null overmind = null
STOP_PROCESSING(SSobj, src) STOP_PROCESSING(SSobj, src)

View File

@@ -0,0 +1,117 @@
// Blob Type Modification to allow for chunks. //
/datum/blob_type
var/list/core_tech = list(TECH_BIO = 4, TECH_MATERIAL = 3) // Tech for the item created when a core is destroyed.
var/chunk_type = /obj/item/blob_chunk
var/chunk_effect_cooldown = 10
var/chunk_effect_range = 1 // Tile range of the effect.
var/generation = 0 // How many times has this blob been chunked
/datum/blob_type/proc/make_chunk(var/turf/T)
if(!T)
return
if(ispath(chunk_type, /obj/item/blob_chunk))
return new chunk_type(T, listify_vars())
return new chunk_type(T)
// Base chunk. //
/obj/item/blob_chunk
name = "chunk"
desc = "A piece of a blob's core."
description_info = "Blob core chunks can be 'revived' with the help of Xenobio. \
40 units of Blood, 40 units of Phoron, and 10 units of Slime Jelly will cause the chunk \
to produce a new core on the turf it is on. The chunk remains, but unable to produce another. \
You can change the faction of the blob created pre-emptively by using the chunk on a mob, imprinting \
its faction upon the chunk."
icon = 'icons/mob/blob.dmi'
icon_state = "blobcore"
unacidable = TRUE
var/can_revive = TRUE
var/list/blob_type_vars
var/datum/blob_type/default_blob = /datum/blob_type/classic
var/blob_effect_master_type = /datum/component/artifact_master/blob
/obj/item/blob_chunk/Initialize(var/newloc, var/list/transfer_vars)
. = ..()
create_reagents(120)
if(!transfer_vars)
default_blob = new default_blob()
transfer_vars = default_blob.listify_vars()
blob_type_vars = transfer_vars
color = transfer_vars["color"]
name = "[blob_type_vars["name"]] [initial(name)]"
AddComponent(blob_effect_master_type)
/obj/item/blob_chunk/is_open_container()
return TRUE
/obj/item/blob_chunk/afterattack(var/atom/target, var/mob/user, var/proximity)
if(proximity && Adjacent(target))
user.visible_message(SPAN_WARNING("[user] holds \the [src] toward [target]."))
if(isliving(target) && do_after(user, 3 SECONDS, target))
var/mob/living/L = target
user.visible_message(SPAN_WARNING("[bicon(src)] \the [src] inflates slighty, before it releases a puff of gas toward [L]."))
to_chat(user, SPAN_NOTICE("[bicon(src)] [src] has registered \the [L.faction] pheromone as its own, forgetting \the [blob_type_vars["faction"]] pheromone."))
blob_type_vars["faction"] = L.faction
return
/obj/item/blob_chunk/proc/reviveBlob(var/ask_player = FALSE)
var/turf/T = get_turf(src)
var/obj/structure/blob/core/C = new(T, null, 2, 0)
C.desired_blob_type = blob_type_vars["blob_type"]
C.create_overmind()
var/datum/blob_type/to_modify = C.overmind.blob_type
to_modify.apply_vars(blob_type_vars)
C.update_icon()
if(!can_revive)
name = "weakened [name]"
desc += " It can no longer reproduce."
if(C?.overmind)
return TRUE
return
// Artifact master solely for blobs. //
/datum/component/artifact_master/blob
make_effects = list(
/datum/artifact_effect/uncommon/fortify
)
/datum/component/artifact_master/blob/do_setup()
..()
if(istype(holder, /obj/item/blob_chunk))
var/obj/item/blob_chunk/type_source = holder
for(var/datum/artifact_effect/AE in my_effects)
AE.trigger = TRIGGER_TOUCH
AE.effect = EFFECT_PULSE
AE.effectrange = type_source.blob_type_vars["chunk_effect_range"]
AE.chargelevelmax = type_source.blob_type_vars["chunk_effect_cooldown"]

View File

@@ -47,10 +47,6 @@
var/node_type = /obj/structure/blob/node var/node_type = /obj/structure/blob/node
var/shield_type = /obj/structure/blob/shield var/shield_type = /obj/structure/blob/shield
var/list/core_tech = list(TECH_BIO = 4, TECH_MATERIAL = 3) // Tech for the item created when a core is destroyed.
var/chunk_active_type = BLOB_CHUNK_TOGGLE
var/chunk_active_ability_cooldown = 20 SECONDS
var/chunk_passive_ability_cooldown = 5 SECONDS
// Called when a blob receives damage. This needs to return the final damage or blobs will be immortal. // Called when a blob receives damage. This needs to return the final damage or blobs will be immortal.
/datum/blob_type/proc/on_received_damage(var/obj/structure/blob/B, damage, damage_type) /datum/blob_type/proc/on_received_damage(var/obj/structure/blob/B, damage, damage_type)
@@ -95,3 +91,112 @@
// Spore handle_special call. // Spore handle_special call.
/datum/blob_type/proc/on_spore_lifetick(mob/living/simple_mob/blob/spore/S) /datum/blob_type/proc/on_spore_lifetick(mob/living/simple_mob/blob/spore/S)
return return
/datum/blob_type/proc/listify_vars()
var/list/transfer_vars = list()
transfer_vars["blob_type"] = type
transfer_vars["name"] = name
transfer_vars["desc"] = desc
transfer_vars["effect_desc"] = effect_desc
transfer_vars["ai_desc"] = ai_desc
transfer_vars["color"] = color
transfer_vars["complementary_color"] = complementary_color
transfer_vars["faction"] = faction
transfer_vars["attack_message"] = attack_message
transfer_vars["attack_message_living"] = attack_message_living
transfer_vars["attack_message_synth"] = attack_message_synth
transfer_vars["attack_verb"] = attack_verb
transfer_vars["damage_type"] = damage_type
transfer_vars["armor_check"] = armor_check
transfer_vars["armor_pen"] = armor_pen
transfer_vars["damage_lower"] = damage_lower
transfer_vars["damage_upper"] = damage_upper
transfer_vars["brute_multiplier"] = brute_multiplier
transfer_vars["burn_multiplier"] = burn_multiplier
transfer_vars["spread_modifier"] = spread_modifier
transfer_vars["slow_spread_with_size"] = slow_spread_with_size
transfer_vars["ai_aggressiveness"] = ai_aggressiveness
transfer_vars["can_build_factories"] = can_build_factories
transfer_vars["can_build_resources"] = can_build_resources
transfer_vars["can_build_nodes"] = can_build_nodes
transfer_vars["spore_type"] = spore_type
transfer_vars["ranged_spores"] = ranged_spores
transfer_vars["spore_firesound"] = spore_firesound
transfer_vars["spore_range"] = spore_range
transfer_vars["spore_projectile"] = spore_projectile
transfer_vars["spore_accuracy"] = spore_accuracy
transfer_vars["spore_dispersion"] = spore_dispersion
transfer_vars["factory_type"] = factory_type
transfer_vars["resource_type"] = resource_type
transfer_vars["node_type"] = node_type
transfer_vars["shield_type"] = shield_type
transfer_vars["core_tech"] = core_tech.Copy()
transfer_vars["chunk_type"] = chunk_type
transfer_vars["chunk_effect_cooldown"] = chunk_effect_cooldown
transfer_vars["chunk_effect_range"] = chunk_effect_range
transfer_vars["generation"] = generation
return transfer_vars
/datum/blob_type/proc/apply_vars(var/list/incoming_vars)
if(LAZYLEN(incoming_vars))
name = incoming_vars["name"]
desc = incoming_vars["desc"]
effect_desc = incoming_vars["effect_desc"]
ai_desc = incoming_vars["ai_desc"]
color = incoming_vars["color"]
complementary_color = incoming_vars["complementary_color"]
faction = incoming_vars["faction"]
attack_message = incoming_vars["attack_message"]
attack_message_living = incoming_vars["attack_message_living"]
attack_message_synth = incoming_vars["attack_message_synth"]
attack_verb = incoming_vars["attack_verb"]
damage_type = incoming_vars["damage_type"]
armor_check = incoming_vars["armor_check"]
armor_pen = incoming_vars["armor_pen"]
damage_lower = incoming_vars["damage_lower"]
damage_upper = incoming_vars["damage_upper"]
brute_multiplier = incoming_vars["brute_multiplier"]
burn_multiplier = incoming_vars["burn_multiplier"]
spread_modifier = incoming_vars["spread_modifier"]
slow_spread_with_size = incoming_vars["slow_spread_with_size"]
ai_aggressiveness = incoming_vars["ai_aggressiveness"]
can_build_factories = incoming_vars["can_build_factories"]
can_build_resources = incoming_vars["can_build_resources"]
can_build_nodes = incoming_vars["can_build_nodes"]
spore_type = incoming_vars["spore_type"]
ranged_spores = incoming_vars["ranged_spores"]
spore_firesound = incoming_vars["spore_firesound"]
spore_range = incoming_vars["spore_range"]
spore_projectile = incoming_vars["spore_projectile"]
spore_accuracy = incoming_vars["spore_accuracy"]
spore_dispersion = incoming_vars["spore_dispersion"]
factory_type = incoming_vars["factory_type"]
resource_type = incoming_vars["resource_type"]
node_type = incoming_vars["node_type"]
shield_type = incoming_vars["shield_type"]
var/list/new_core_tech = incoming_vars["core_tech"]
core_tech = islist(new_core_tech) ? new_core_tech : list()
chunk_type = incoming_vars["chunk_type"]
chunk_effect_cooldown = incoming_vars["chunk_effect_cooldown"]
chunk_effect_range = incoming_vars["chunk_effect_range"]
generation = incoming_vars["generation"] + 1

View File

@@ -13,3 +13,9 @@
brute_multiplier = 0.35 brute_multiplier = 0.35
burn_multiplier = 0.8 burn_multiplier = 0.8
chunk_type = /obj/item/blob_chunk/barnacle
/obj/item/blob_chunk/barnacle
default_blob = /datum/blob_type/barnacle
blob_effect_master_type = /datum/component/artifact_master/blob

View File

@@ -11,15 +11,26 @@
ai_aggressiveness = 50 ai_aggressiveness = 50
damage_type = BURN damage_type = BURN
burn_multiplier = 0 // Fire immunity burn_multiplier = 0 // Fire immunity
chunk_active_ability_cooldown = 4 MINUTES chunk_effect_cooldown = 4 MINUTES
attack_message = "The blazing oil splashes you with its burning oil" attack_message = "The blazing oil splashes you with its burning oil"
attack_message_living = ", and you feel your skin char and melt" attack_message_living = ", and you feel your skin char and melt"
attack_message_synth = ", and your external plating melts" attack_message_synth = ", and your external plating melts"
attack_verb = "splashes" attack_verb = "splashes"
chunk_type = /obj/item/blob_chunk/blazing_oil
/obj/item/blob_chunk/blazing_oil
default_blob = /datum/blob_type/blazing_oil
blob_effect_master_type = /datum/component/artifact_master/blob/blazing_oil
/datum/blob_type/blazing_oil/on_attack(obj/structure/blob/B, mob/living/victim) /datum/blob_type/blazing_oil/on_attack(obj/structure/blob/B, mob/living/victim)
victim.fire_act() // Burn them. victim.fire_act() // Burn them.
/datum/blob_type/blazing_oil/on_water(obj/structure/blob/B, amount) /datum/blob_type/blazing_oil/on_water(obj/structure/blob/B, amount)
spawn(1) spawn(1)
B.adjust_integrity(-(amount * 5)) B.adjust_integrity(-(amount * 5))
/datum/component/artifact_master/blob/blazing_oil
make_effects = list(
/datum/artifact_effect/common/heat
)

View File

@@ -10,3 +10,14 @@
can_build_nodes = FALSE can_build_nodes = FALSE
spread_modifier = 1.0 spread_modifier = 1.0
ai_aggressiveness = 0 ai_aggressiveness = 0
chunk_type = /obj/item/blob_chunk/classic
/obj/item/blob_chunk/classic
default_blob = /datum/blob_type/classic
blob_effect_master_type = /datum/component/artifact_master/blob/classic
/datum/component/artifact_master/blob/classic
make_effects = list(
/datum/artifact_effect/common/sweating
)

View File

@@ -14,12 +14,18 @@
burn_multiplier = 1.2 burn_multiplier = 1.2
spread_modifier = 0.5 spread_modifier = 0.5
ai_aggressiveness = 50 ai_aggressiveness = 50
chunk_active_ability_cooldown = 4 MINUTES chunk_effect_cooldown = 4 MINUTES
attack_message = "The goo stabs you" attack_message = "The goo stabs you"
attack_message_living = ", and you feel an intense chill from within" attack_message_living = ", and you feel an intense chill from within"
attack_message_synth = ", and your system reports lower internal temperatures" attack_message_synth = ", and your system reports lower internal temperatures"
attack_verb = "stabs" attack_verb = "stabs"
chunk_type = /obj/item/blob_chunk/cryogenic_goo
/obj/item/blob_chunk/cryogenic_goo
default_blob = /datum/blob_type/cryogenic_goo
blob_effect_master_type = /datum/component/artifact_master/blob/cryogenic_goo
/datum/blob_type/cryogenic_goo/on_attack(obj/structure/blob/B, mob/living/victim) /datum/blob_type/cryogenic_goo/on_attack(obj/structure/blob/B, mob/living/victim)
if(ishuman(victim)) if(ishuman(victim))
var/mob/living/carbon/human/H = victim var/mob/living/carbon/human/H = victim
@@ -35,3 +41,8 @@
H.bodytemperature = max(H.bodytemperature - temp_change, temp_cap) H.bodytemperature = max(H.bodytemperature - temp_change, temp_cap)
else // Just do some extra burn for mobs who don't process bodytemp else // Just do some extra burn for mobs who don't process bodytemp
victim.adjustFireLoss(20) victim.adjustFireLoss(20)
/datum/component/artifact_master/blob/cryogenic_goo
make_effects = list(
/datum/artifact_effect/common/cold
)

View File

@@ -25,6 +25,13 @@
var/list/active_beams = list() var/list/active_beams = list()
chunk_effect_range = 6
chunk_type = /obj/item/blob_chunk/ectoplasmic_horror
/obj/item/blob_chunk/ectoplasmic_horror
default_blob = /datum/blob_type/ectoplasmic_horror
blob_effect_master_type = /datum/component/artifact_master/blob/ectoplasmic_horror
/datum/blob_type/ectoplasmic_horror/on_pulse(var/obj/structure/blob/B) /datum/blob_type/ectoplasmic_horror/on_pulse(var/obj/structure/blob/B)
if(B.type == /obj/structure/blob && (locate(/obj/structure/blob/node) in oview(2, get_turf(B)))) if(B.type == /obj/structure/blob && (locate(/obj/structure/blob/node) in oview(2, get_turf(B))))
B.visible_message("<span class='alien'>The [name] quakes, before hardening.</span>") B.visible_message("<span class='alien'>The [name] quakes, before hardening.</span>")
@@ -67,3 +74,8 @@
animate(B,alpha = 10, alpha = initial_alpha, time = 10) animate(B,alpha = 10, alpha = initial_alpha, time = 10)
return 0 return 0
return ..() return ..()
/datum/component/artifact_master/blob/ectoplasmic_horror
make_effects = list(
/datum/artifact_effect/rare/lifedrain
)

View File

@@ -13,14 +13,25 @@
brute_multiplier = 3 brute_multiplier = 3
burn_multiplier = 2 burn_multiplier = 2
ai_aggressiveness = 60 ai_aggressiveness = 60
chunk_active_type = BLOB_CHUNK_CONSTANT
attack_message = "The web lashes you" attack_message = "The web lashes you"
attack_message_living = ", and you hear a faint buzzing" attack_message_living = ", and you hear a faint buzzing"
attack_message_synth = ", and your electronics get badly damaged" attack_message_synth = ", and your electronics get badly damaged"
attack_verb = "lashes" attack_verb = "lashes"
chunk_effect_range = 3
chunk_type = /obj/item/blob_chunk/electromagnetic_web
/obj/item/blob_chunk/electromagnetic_web
default_blob = /datum/blob_type/electromagnetic_web
blob_effect_master_type = /datum/component/artifact_master/blob/electromagnetic_web
/datum/blob_type/electromagnetic_web/on_death(obj/structure/blob/B) /datum/blob_type/electromagnetic_web/on_death(obj/structure/blob/B)
empulse(B.loc, 0, 1, 2) empulse(B.loc, 0, 1, 2)
/datum/blob_type/electromagnetic_web/on_attack(obj/structure/blob/B, mob/living/victim) /datum/blob_type/electromagnetic_web/on_attack(obj/structure/blob/B, mob/living/victim)
victim.emp_act(2) victim.emp_act(2)
/datum/component/artifact_master/blob/electromagnetic_web
make_effects = list(
/datum/artifact_effect/rare/emp
)

View File

@@ -20,6 +20,17 @@
attack_verb = "prods" attack_verb = "prods"
spore_projectile = /obj/item/projectile/beam/shock spore_projectile = /obj/item/projectile/beam/shock
chunk_type = /obj/item/blob_chunk/energized_jelly
/obj/item/blob_chunk/energized_jelly
default_blob = /datum/blob_type/energized_jelly
blob_effect_master_type = /datum/component/artifact_master/blob/energized_jelly
/datum/blob_type/energized_jelly/on_attack(obj/structure/blob/B, mob/living/victim, def_zone) /datum/blob_type/energized_jelly/on_attack(obj/structure/blob/B, mob/living/victim, def_zone)
victim.electrocute_act(10, src, 1, def_zone) victim.electrocute_act(10, src, 1, def_zone)
victim.stun_effect_act(0, 40, BP_TORSO, src) victim.stun_effect_act(0, 40, BP_TORSO, src)
/datum/component/artifact_master/blob/energized_jelly
make_effects = list(
/datum/artifact_effect/uncommon/faraday
)

View File

@@ -22,6 +22,12 @@
attack_verb = "blasts" attack_verb = "blasts"
var/exploding = FALSE var/exploding = FALSE
chunk_type = /obj/item/blob_chunk/explosive_lattice
/obj/item/blob_chunk/explosive_lattice
default_blob = /datum/blob_type/explosive_lattice
blob_effect_master_type = /datum/component/artifact_master/blob/explosive_lattice
/datum/blob_type/explosive_lattice/on_attack(obj/structure/blob/B, mob/living/victim, def_zone) // This doesn't use actual bombs since they're too strong and it would hurt the blob. /datum/blob_type/explosive_lattice/on_attack(obj/structure/blob/B, mob/living/victim, def_zone) // This doesn't use actual bombs since they're too strong and it would hurt the blob.
if(exploding) // We're busy, don't infinite loop us. if(exploding) // We're busy, don't infinite loop us.
return return
@@ -48,3 +54,8 @@
M << 'sound/effects/explosionfar.ogg' M << 'sound/effects/explosionfar.ogg'
exploding = FALSE exploding = FALSE
/datum/component/artifact_master/blob/explosive_lattice
make_effects = list(
/datum/artifact_effect/uncommon/blast_shielding
)

View File

@@ -22,6 +22,12 @@
ai_aggressiveness = 50 //Really doesn't like you near it. ai_aggressiveness = 50 //Really doesn't like you near it.
spore_type = /mob/living/simple_mob/mechanical/hivebot/swarm spore_type = /mob/living/simple_mob/mechanical/hivebot/swarm
chunk_type = /obj/item/blob_chunk/fabrication_swarm
/obj/item/blob_chunk/fabrication_swarm
default_blob = /datum/blob_type/fabrication_swarm
blob_effect_master_type = /datum/component/artifact_master/blob
/datum/blob_type/fabrication_swarm/on_received_damage(var/obj/structure/blob/B, damage, damage_type, mob/living/attacker) /datum/blob_type/fabrication_swarm/on_received_damage(var/obj/structure/blob/B, damage, damage_type, mob/living/attacker)
if(istype(B, /obj/structure/blob/normal)) if(istype(B, /obj/structure/blob/normal))
if(damage > 0) if(damage > 0)

View File

@@ -17,7 +17,13 @@
ai_aggressiveness = 30 // The spores do most of the fighting. ai_aggressiveness = 30 // The spores do most of the fighting.
can_build_factories = TRUE can_build_factories = TRUE
spore_type = /mob/living/simple_mob/blob/spore/weak spore_type = /mob/living/simple_mob/blob/spore/weak
chunk_active_ability_cooldown = 60 SECONDS chunk_effect_cooldown = 60
chunk_type = /obj/item/blob_chunk/fulminant_organism
/obj/item/blob_chunk/fulminant_organism
default_blob = /datum/blob_type/fulminant_organism
blob_effect_master_type = /datum/component/artifact_master/blob/fulminant_organism
/datum/blob_type/fulminant_organism/on_expand(var/obj/structure/blob/B, var/obj/structure/blob/new_B, var/turf/T, var/mob/observer/blob/O) /datum/blob_type/fulminant_organism/on_expand(var/obj/structure/blob/B, var/obj/structure/blob/new_B, var/turf/T, var/mob/observer/blob/O)
if(prob(10)) // 10% chance to make a weak spore when expanding. if(prob(10)) // 10% chance to make a weak spore when expanding.
@@ -41,3 +47,8 @@
else else
S.faction = faction S.faction = faction
S.update_icons() S.update_icons()
/datum/component/artifact_master/blob/fulminant_organism
make_effects = list(
/datum/artifact_effect/uncommon/berserk
)

View File

@@ -16,7 +16,12 @@
ai_aggressiveness = 40 ai_aggressiveness = 40
can_build_factories = TRUE can_build_factories = TRUE
spore_type = /mob/living/simple_mob/blob/spore/infesting spore_type = /mob/living/simple_mob/blob/spore/infesting
chunk_active_ability_cooldown = 2 MINUTES
chunk_type = /obj/item/blob_chunk/fungal_bloom
/obj/item/blob_chunk/fungal_bloom
default_blob = /datum/blob_type/fungal_bloom
blob_effect_master_type = /datum/component/artifact_master/blob/fungal_bloom
/datum/blob_type/fungal_bloom/on_spore_death(mob/living/simple_mob/blob/spore/S) /datum/blob_type/fungal_bloom/on_spore_death(mob/living/simple_mob/blob/spore/S)
if(S.is_infesting) if(S.is_infesting)
@@ -28,3 +33,8 @@
else else
B = new /obj/structure/blob/normal(T, S.overmind) // Otherwise spread it. B = new /obj/structure/blob/normal(T, S.overmind) // Otherwise spread it.
B.visible_message("<span class='danger'>\A [B] forms on \the [T] as \the [S] bursts!</span>") B.visible_message("<span class='danger'>\A [B] forms on \the [T] as \the [S] bursts!</span>")
/datum/component/artifact_master/blob/fungal_bloom
make_effects = list(
/datum/artifact_effect/extreme/resurrect
)

View File

@@ -16,5 +16,13 @@
attack_message_synth = ", and your external plating dissolves" attack_message_synth = ", and your external plating dissolves"
faction = "nanomachines" faction = "nanomachines"
core_tech = list(TECH_ENGINEERING = 10, TECH_MATERIAL = 10, TECH_ILLEGAL = 10)
chunk_type = /obj/item/blob_chunk/grey_goo
/obj/item/blob_chunk/grey_goo
default_blob = /datum/blob_type/grey_goo
blob_effect_master_type = /datum/component/artifact_master/blob
/datum/blob_type/grey_goo/on_emp(obj/structure/blob/B, severity) /datum/blob_type/grey_goo/on_emp(obj/structure/blob/B, severity)
B.adjust_integrity(-(20 / severity)) B.adjust_integrity(-(20 / severity))

View File

@@ -20,6 +20,12 @@
attack_message_synth = ", and the fluid wears down on your components" attack_message_synth = ", and the fluid wears down on your components"
attack_verb = "splashes" attack_verb = "splashes"
chunk_type = /obj/item/blob_chunk/pressurized_slime
/obj/item/blob_chunk/pressurized_slime
default_blob = /datum/blob_type/pressurized_slime
blob_effect_master_type = /datum/component/artifact_master/blob/pressurized_slime
/datum/blob_type/pressurized_slime/on_attack(obj/structure/blob/B, mob/living/victim, def_zone) /datum/blob_type/pressurized_slime/on_attack(obj/structure/blob/B, mob/living/victim, def_zone)
victim.water_act(5) victim.water_act(5)
var/turf/simulated/T = get_turf(victim) var/turf/simulated/T = get_turf(victim)
@@ -46,3 +52,8 @@
T.wet_floor() T.wet_floor()
for(var/atom/movable/AM in T) for(var/atom/movable/AM in T)
AM.water_act(2) AM.water_act(2)
/datum/component/artifact_master/blob/pressurized_slime
make_effects = list(
/datum/artifact_effect/common/extinguisher
)

View File

@@ -20,5 +20,16 @@
attack_message_synth = ", and your internal systems are bombarded by ionizing radiation" attack_message_synth = ", and your internal systems are bombarded by ionizing radiation"
attack_verb = "splashes" attack_verb = "splashes"
chunk_type = /obj/item/blob_chunk/radioactive_ooze
/obj/item/blob_chunk/radioactive_ooze
default_blob = /datum/blob_type/radioactive_ooze
blob_effect_master_type = /datum/component/artifact_master/blob/radioactive_ooze
/datum/blob_type/radioactive_ooze/on_pulse(var/obj/structure/blob/B) /datum/blob_type/radioactive_ooze/on_pulse(var/obj/structure/blob/B)
SSradiation.radiate(B, 200) SSradiation.radiate(B, 200)
/datum/component/artifact_master/blob/radioactive_ooze
make_effects = list(
/datum/artifact_effect/rare/radiate
)

View File

@@ -21,6 +21,12 @@
attack_message_synth = ", and your body begins to corrode" attack_message_synth = ", and your body begins to corrode"
attack_verb = "splashes" attack_verb = "splashes"
chunk_type = /obj/item/blob_chunk/ravenous_macrophage
/obj/item/blob_chunk/ravenous_macrophage
default_blob = /datum/blob_type/ravenous_macrophage
blob_effect_master_type = /datum/component/artifact_master/blob/ravenous_macrophage
/datum/blob_type/ravenous_macrophage/on_pulse(var/obj/structure/blob/B) /datum/blob_type/ravenous_macrophage/on_pulse(var/obj/structure/blob/B)
var/mob/living/L = locate() in range(world.view, B) var/mob/living/L = locate() in range(world.view, B)
if(L && prob(1) && L.mind && !L.stat) // There's some active living thing nearby, produce offgas. if(L && prob(1) && L.mind && !L.stat) // There's some active living thing nearby, produce offgas.
@@ -37,3 +43,8 @@
B.visible_message("<span class='danger'>The dying mass is rapidly consumed by the nearby [other]!</span>") B.visible_message("<span class='danger'>The dying mass is rapidly consumed by the nearby [other]!</span>")
if(other.overmind) if(other.overmind)
other.overmind.add_points(rand(1,4)) other.overmind.add_points(rand(1,4))
/datum/component/artifact_master/blob/ravenous_macrophage
make_effects = list(
/datum/artifact_effect/common/noxious
)

View File

@@ -16,13 +16,20 @@
brute_multiplier = 2.0 brute_multiplier = 2.0
spread_modifier = 0.35 // Ranged projectiles tend to have a higher material cost, so ease up on the spreading. spread_modifier = 0.35 // Ranged projectiles tend to have a higher material cost, so ease up on the spreading.
ai_aggressiveness = 40 ai_aggressiveness = 40
chunk_passive_ability_cooldown = 0.5 SECONDS chunk_effect_cooldown = 0.5 SECONDS
attack_message = "The blob stabs you" attack_message = "The blob stabs you"
attack_message_living = ", and you feel sharp spines pierce your flesh" attack_message_living = ", and you feel sharp spines pierce your flesh"
attack_message_synth = ", and your external plating is pierced by sharp spines" attack_message_synth = ", and your external plating is pierced by sharp spines"
attack_verb = "stabs" attack_verb = "stabs"
spore_projectile = /obj/item/projectile/bullet/thorn spore_projectile = /obj/item/projectile/bullet/thorn
chunk_effect_range = 3
chunk_type = /obj/item/blob_chunk/reactive_spines
/obj/item/blob_chunk/reactive_spines
default_blob = /datum/blob_type/reactive_spines
blob_effect_master_type = /datum/component/artifact_master/blob/reactive_spines
// Even if the melee attack is enough to one-shot this blob, it gets to retaliate at least once. // Even if the melee attack is enough to one-shot this blob, it gets to retaliate at least once.
/datum/blob_type/reactive_spines/on_received_damage(var/obj/structure/blob/B, damage, damage_type, mob/living/attacker) /datum/blob_type/reactive_spines/on_received_damage(var/obj/structure/blob/B, damage, damage_type, mob/living/attacker)
if(damage > 0 && attacker && get_dist(B, attacker) <= 1) if(damage > 0 && attacker && get_dist(B, attacker) <= 1)
@@ -30,3 +37,8 @@
B.blob_attack_animation(attacker, B.overmind) B.blob_attack_animation(attacker, B.overmind)
attacker.blob_act(B) attacker.blob_act(B)
return ..() return ..()
/datum/component/artifact_master/blob/reactive_spines
make_effects = list(
/datum/artifact_effect/extreme/spines
)

View File

@@ -22,3 +22,15 @@
attack_verb = "lashes" attack_verb = "lashes"
spore_projectile = /obj/item/projectile/arc/spore spore_projectile = /obj/item/projectile/arc/spore
factory_type = /obj/structure/blob/factory/turret factory_type = /obj/structure/blob/factory/turret
chunk_effect_range = 2
chunk_type = /obj/item/blob_chunk/roiling_mold
/obj/item/blob_chunk/roiling_mold
default_blob = /datum/blob_type/roiling_mold
blob_effect_master_type = /datum/component/artifact_master/blob/roiling_mold
/datum/component/artifact_master/blob/roiling_mold
make_effects = list(
/datum/artifact_effect/extreme/spines/spore
)

View File

@@ -13,10 +13,16 @@
burn_multiplier = 0.5 burn_multiplier = 0.5
spread_modifier = 0.5 spread_modifier = 0.5
ai_aggressiveness = 30 ai_aggressiveness = 30
chunk_active_ability_cooldown = 3 MINUTES chunk_effect_cooldown = 3 MINUTES
attack_message = "A fragment strikes you" attack_message = "A fragment strikes you"
attack_verb = "strikes" attack_verb = "strikes"
chunk_type = /obj/item/blob_chunk/shifting_fragments
/obj/item/blob_chunk/shifting_fragments
default_blob = /datum/blob_type/shifting_fragments
blob_effect_master_type = /datum/component/artifact_master/blob/shifting_fragments
/datum/blob_type/shifting_fragments/on_received_damage(var/obj/structure/blob/B, damage, damage_type) /datum/blob_type/shifting_fragments/on_received_damage(var/obj/structure/blob/B, damage, damage_type)
if(damage > 0 && prob(60)) if(damage > 0 && prob(60))
var/list/available_blobs = list() var/list/available_blobs = list()
@@ -34,3 +40,8 @@
if(istype(B, /obj/structure/blob/normal) || (istype(B, /obj/structure/blob/shield) && prob(25))) if(istype(B, /obj/structure/blob/normal) || (istype(B, /obj/structure/blob/shield) && prob(25)))
new_B.forceMove(get_turf(B)) new_B.forceMove(get_turf(B))
B.forceMove(T) B.forceMove(T)
/datum/component/artifact_master/blob/shifting_fragments
make_effects = list(
/datum/artifact_effect/common/sprinting
)

View File

@@ -18,6 +18,13 @@
attack_verb = "synchronously strikes" attack_verb = "synchronously strikes"
var/synchronously_attacking = FALSE var/synchronously_attacking = FALSE
chunk_effect_range = 3
chunk_type = /obj/item/blob_chunk/synchronous_mesh
/obj/item/blob_chunk/synchronous_mesh
default_blob = /datum/blob_type/synchronous_mesh
blob_effect_master_type = /datum/component/artifact_master/blob/synchronous_mesh
/datum/blob_type/synchronous_mesh/on_attack(obj/structure/blob/B, mob/living/victim) /datum/blob_type/synchronous_mesh/on_attack(obj/structure/blob/B, mob/living/victim)
if(synchronously_attacking) if(synchronously_attacking)
return return
@@ -42,3 +49,8 @@
C.adjust_integrity(-(damage / blobs_to_hurt.len)) C.adjust_integrity(-(damage / blobs_to_hurt.len))
return damage / max(blobs_to_hurt.len, 1) // To hurt the blob that got hit. return damage / max(blobs_to_hurt.len, 1) // To hurt the blob that got hit.
/datum/component/artifact_master/blob/synchronous_mesh
make_effects = list(
/datum/artifact_effect/extreme/lifemerger
)

View File

@@ -26,7 +26,13 @@
spore_dispersion = 45 spore_dispersion = 45
factory_type = /obj/structure/blob/factory/sluggish factory_type = /obj/structure/blob/factory/sluggish
resource_type = /obj/structure/blob/resource/sluggish resource_type = /obj/structure/blob/resource/sluggish
chunk_active_ability_cooldown = 2 MINUTES
chunk_effect_range = 3
chunk_type = /obj/item/blob_chunk/volatile_alluvium
/obj/item/blob_chunk/volatile_alluvium
default_blob = /datum/blob_type/volatile_alluvium
blob_effect_master_type = /datum/component/artifact_master/blob/volatile_alluvium
/datum/blob_type/volatile_alluvium/on_received_damage(var/obj/structure/blob/B, damage, damage_type, mob/living/attacker) /datum/blob_type/volatile_alluvium/on_received_damage(var/obj/structure/blob/B, damage, damage_type, mob/living/attacker)
if(damage > 0 && attacker && get_dist(B, attacker) <= 2 && prob(min(damage, 70)) && istype(attacker, /mob/living/carbon/human)) // Melee weapons of any type carried by a human will have a high chance of being stolen. if(damage > 0 && attacker && get_dist(B, attacker) <= 2 && prob(min(damage, 70)) && istype(attacker, /mob/living/carbon/human)) // Melee weapons of any type carried by a human will have a high chance of being stolen.
@@ -49,3 +55,8 @@
B.adjust_integrity(-(damage)) B.adjust_integrity(-(damage))
if(B && prob(damage)) if(B && prob(damage))
B.visible_message("<span class='danger'>The [name] begins to crumble!</span>") B.visible_message("<span class='danger'>The [name] begins to crumble!</span>")
/datum/component/artifact_master/blob/volatile_alluvium
make_effects = list(
/datum/artifact_effect/uncommon/disarmament
)

View File

@@ -0,0 +1,20 @@
// Reactions for Blob Chunks. //
/decl/chemical_reaction/instant/blob_chunk_revival
name = "Blob Chunk Revival"
id = "blob_revival"
result = null
required_reagents = list("blood" = 40, "phoron" = 40, "slimejelly" = 10)
result_amount = 1
/decl/chemical_reaction/instant/blob_chunk_revival/can_happen(var/datum/reagents/holder)
if(holder.my_atom && istype(holder.my_atom, /obj/item/blob_chunk))
return ..()
return FALSE
/decl/chemical_reaction/instant/blob_chunk_revival/on_reaction(var/datum/reagents/holder)
var/obj/item/blob_chunk/chunk = holder.my_atom
if(chunk.can_revive)
chunk.can_revive = FALSE
chunk.reviveBlob()

View File

@@ -0,0 +1,32 @@
/datum/artifact_effect/uncommon/disarmament
name = "disarmament"
effect_type = EFFECT_PSIONIC
effect_state = "impact_laser_monochrome"
effect_color = "#e17ceb"
/datum/artifact_effect/uncommon/disarmament/proc/disarm(var/mob/living/L)
if(istype(L) && L.is_sentient())
var/obj/item/Item = L.get_active_hand()
if(istype(Item))
to_chat(L, SPAN_WARNING("Something forces you to drop \the [Item]."))
L.drop_item()
/datum/artifact_effect/uncommon/disarmament/DoEffectTouch(mob/living/user)
var/weakness = GetAnomalySusceptibility(user)
if(prob(weakness * 100))
disarm(user)
/datum/artifact_effect/uncommon/disarmament/DoEffectPulse()
var/turf/T = get_turf(get_master_holder())
for(var/mob/living/L in oview(src.effectrange, T))
var/weakness = GetAnomalySusceptibility(L)
if(prob(weakness * 100))
disarm(L)
/datum/artifact_effect/uncommon/disarmament/DoEffectAura()
var/turf/T = get_turf(get_master_holder())
for(var/mob/living/L in oview(src.effectrange, T))
var/weakness = GetAnomalySusceptibility(L)
if(prob(10) && prob(weakness * 100))
disarm(L)

View File

@@ -2,7 +2,7 @@
name = "N2O creation" name = "N2O creation"
/datum/artifact_effect/gassleeping/New() /datum/artifact_effect/rare/gassleeping/New()
..() ..()
effect = pick(EFFECT_TOUCH, EFFECT_AURA) effect = pick(EFFECT_TOUCH, EFFECT_AURA)
effect_type = pick(EFFECT_BLUESPACE, EFFECT_SYNTH) effect_type = pick(EFFECT_BLUESPACE, EFFECT_SYNTH)

View File

@@ -1,8 +1,10 @@
/datum/artifact_effect/rare/spines /datum/artifact_effect/extreme/spines
name = "spines" name = "spines"
effect_color = "#8db6b2" effect_color = "#8db6b2"
effect_type = EFFECT_ORGANIC effect_type = EFFECT_ORGANIC
var/obj/item/projectile_type = /obj/item/projectile/bullet/thorn
/datum/artifact_effect/extreme/spines/New() /datum/artifact_effect/extreme/spines/New()
. = ..() . = ..()
@@ -12,13 +14,13 @@
/datum/artifact_effect/extreme/spines/proc/shoot(var/list/exempt = list()) /datum/artifact_effect/extreme/spines/proc/shoot(var/list/exempt = list())
var/atom/A = get_master_holder() var/atom/A = get_master_holder()
A.visible_message("<span class='danger'>\The [get_master_holder()] fires spines wildly in all directions!</span>") A.visible_message("<span class='danger'>\The [get_master_holder()] fires spines wildly in all directions!</span>")
for(var/i in range(1, round(chargelevel / 3))) for(var/i in rand(1, round(chargelevel / 3)))
var/atom/target = pick(oview(world.view, get_turf(get_master_holder()))) var/atom/target = pick(oview(world.view, get_turf(get_master_holder())))
if(target in exempt) if(target in exempt)
continue continue
var/obj/item/projectile/P = new /obj/item/projectile/bullet/thorn(get_turf(get_master_holder())) var/obj/item/projectile/P = new projectile_type(get_turf(get_master_holder()))
P.launch_projectile(target, BP_TORSO, get_master_holder()) P.launch_projectile(target, BP_TORSO, get_master_holder())
chargelevel -= 3 chargelevel -= 3
@@ -33,3 +35,21 @@
if(chargelevel < 3) if(chargelevel < 3)
return return
shoot() shoot()
/datum/artifact_effect/extreme/spines/spore
name = "bombarding"
projectile_type = /obj/item/projectile/arc/spore
/datum/artifact_effect/extreme/spines/spore/shoot(var/list/exempt = list())
var/atom/A = get_master_holder()
A.visible_message("<span class='danger'>\The [get_master_holder()] launches spores wildly in all directions!</span>")
for(var/i in rand(1, round(chargelevel / 3)))
var/atom/target = check_trajectory(pick(oview(world.view, get_turf(get_master_holder()))), get_master_holder(), PASSTABLE)
if(target in exempt)
continue
var/obj/item/projectile/P = new projectile_type(get_turf(get_master_holder()))
P.launch_projectile(target, BP_TORSO, get_master_holder())
chargelevel -= 3

View File

@@ -0,0 +1,91 @@
/datum/artifact_effect/extreme/lifemerger
name = "lifemerger"
effect_color = "#3b1f3b"
effect_type = EFFECT_ENERGY
var/list/active_beams
var/merging = FALSE
/datum/artifact_effect/extreme/lifemerger/DoEffectTouch()
mergelife()
/datum/artifact_effect/extreme/lifemerger/DoEffectPulse()
mergelife()
/datum/artifact_effect/extreme/lifemerger/DoEffectAura()
mergelife()
/datum/artifact_effect/extreme/lifemerger/proc/mergelife()
var/atom/holder = get_master_holder()
if(!holder)
return
var/turf/T = get_turf(holder)
var/list/nearby_mobs = list()
for(var/mob/living/L in view(world.view, T) - holder)
if(L.stat != DEAD)
nearby_mobs |= L
if(nearby_mobs.len > 2)
listclearnulls(active_beams)
for(var/mob/living/L in nearby_mobs)
if(L.stat == DEAD)
continue
if(!prob(5))
continue
var/beamtarget_exists = FALSE
if(active_beams.len)
for(var/datum/beam/Beam in active_beams)
if(Beam.target == L)
beamtarget_exists = TRUE
break
if(beamtarget_exists || GetAnomalySusceptibility(L) < 0.5)
continue
holder.visible_message(
"<span class='danger'>A bright beam beam lashes out from [bicon(get_master_holder())] \the [get_master_holder()] at \the [L]!</span>")
var/datum/beam/drain_beam = holder.Beam(L, icon_state = "medbeam", time = 3 SECONDS * nearby_mobs.len)
active_beams |= drain_beam
if(!merging) // Don't add a ton of timers if this procs faster than 2 seconds, just average every 2.
merging = TRUE
addtimer(CALLBACK(src, .proc/averagelife), 2 SECONDS)
/datum/artifact_effect/extreme/lifemerger/proc/averagelife()
listclearnulls(active_beams) // Clear nulls from mobs leaving the range.
if(active_beams.len >= 2) // Work with remaining beams to find still-valid targets, if we still have 2 or more.
var/list/remaining_mobs = list()
var/brute_avg = 0
var/burn_avg = 0
var/oxy_avg = 0
var/tox_avg = 0
for(var/datum/beam/Beam in active_beams) // Find remaining beams' targets and their damage values.
if(isliving(Beam.target))
remaining_mobs |= Beam.target
var/mob/living/L = Beam.target
brute_avg += L.getBruteLoss()
burn_avg += L.getFireLoss()
oxy_avg += L.getOxyLoss()
tox_avg += L.getToxLoss()
if(remaining_mobs.len) // Average by the number of remaining mobs.
brute_avg /= remaining_mobs.len
burn_avg /= remaining_mobs.len
oxy_avg /= remaining_mobs.len
tox_avg /= remaining_mobs.len
for(var/mob/living/L in remaining_mobs) // Adjust brute, burn, oxy, and tox of remaining mobs to the average
L.adjustBruteLoss(brute_avg - L.getBruteLoss())
L.adjustFireLoss(burn_avg - L.getFireLoss())
L.adjustOxyLoss(oxy_avg - L.getOxyLoss())
L.adjustToxLoss(tox_avg - L.getToxLoss())
L.add_modifier(/datum/modifier/berserk_exhaustion, remaining_mobs.len * 2 SECONDS)
for(var/datum/beam/Beam in active_beams) // Cull the beams.
Beam.End()
listclearnulls(active_beams) // Cull the nulls.
merging = FALSE // We're done!

View File

@@ -1551,6 +1551,7 @@
#include "code\modules\blob2\blobs\normal.dm" #include "code\modules\blob2\blobs\normal.dm"
#include "code\modules\blob2\blobs\resource.dm" #include "code\modules\blob2\blobs\resource.dm"
#include "code\modules\blob2\blobs\shield.dm" #include "code\modules\blob2\blobs\shield.dm"
#include "code\modules\blob2\overmind\chunks.dm"
#include "code\modules\blob2\overmind\overmind.dm" #include "code\modules\blob2\overmind\overmind.dm"
#include "code\modules\blob2\overmind\powers.dm" #include "code\modules\blob2\overmind\powers.dm"
#include "code\modules\blob2\overmind\types.dm" #include "code\modules\blob2\overmind\types.dm"
@@ -2983,6 +2984,7 @@
#include "code\modules\reagents\reactions\_reactions.dm" #include "code\modules\reagents\reactions\_reactions.dm"
#include "code\modules\reagents\reactions\distilling\distilling.dm" #include "code\modules\reagents\reactions\distilling\distilling.dm"
#include "code\modules\reagents\reactions\fusion\fusion.dm" #include "code\modules\reagents\reactions\fusion\fusion.dm"
#include "code\modules\reagents\reactions\instant\blob.dm"
#include "code\modules\reagents\reactions\instant\drinks.dm" #include "code\modules\reagents\reactions\instant\drinks.dm"
#include "code\modules\reagents\reactions\instant\food.dm" #include "code\modules\reagents\reactions\instant\food.dm"
#include "code\modules\reagents\reactions\instant\instant.dm" #include "code\modules\reagents\reactions\instant\instant.dm"
@@ -3259,6 +3261,7 @@
#include "code\modules\xenoarcheaology\effects\cellcharge.dm" #include "code\modules\xenoarcheaology\effects\cellcharge.dm"
#include "code\modules\xenoarcheaology\effects\celldrain.dm" #include "code\modules\xenoarcheaology\effects\celldrain.dm"
#include "code\modules\xenoarcheaology\effects\cold.dm" #include "code\modules\xenoarcheaology\effects\cold.dm"
#include "code\modules\xenoarcheaology\effects\disarm.dm"
#include "code\modules\xenoarcheaology\effects\electric_field.dm" #include "code\modules\xenoarcheaology\effects\electric_field.dm"
#include "code\modules\xenoarcheaology\effects\emp.dm" #include "code\modules\xenoarcheaology\effects\emp.dm"
#include "code\modules\xenoarcheaology\effects\extinguisher.dm" #include "code\modules\xenoarcheaology\effects\extinguisher.dm"
@@ -3279,6 +3282,7 @@
#include "code\modules\xenoarcheaology\effects\hurt.dm" #include "code\modules\xenoarcheaology\effects\hurt.dm"
#include "code\modules\xenoarcheaology\effects\indiscriminate_spines.dm" #include "code\modules\xenoarcheaology\effects\indiscriminate_spines.dm"
#include "code\modules\xenoarcheaology\effects\lifedrain.dm" #include "code\modules\xenoarcheaology\effects\lifedrain.dm"
#include "code\modules\xenoarcheaology\effects\lifemerger.dm"
#include "code\modules\xenoarcheaology\effects\lifesteal.dm" #include "code\modules\xenoarcheaology\effects\lifesteal.dm"
#include "code\modules\xenoarcheaology\effects\noxious.dm" #include "code\modules\xenoarcheaology\effects\noxious.dm"
#include "code\modules\xenoarcheaology\effects\poltergeist.dm" #include "code\modules\xenoarcheaology\effects\poltergeist.dm"