Da blobmob update! Indepedent strains, new sprites, egglike spores, fixes, sounds and buffs! #Cytology2025 (#91368)

This PR makes a number of changes focusing on improving the blob
minions, spores, nauts, zombies and their associated component.

The blob spore, blob zombie and blobbernaut has been resprited.

The spore and zombie have been lightly touched to preserve the most of
the original characteristics while given a cleaner look.

The spore and zombie have a partially desaturated version used to let
more of the strain colour through instead of them all ending up dark
brown.

The blobbernaut has been reshaded and the side sprite has been made
coherent with the front state, I made decided how the front state was
shaded should be the "canon" one (this might be a bit controversial but
the wild inconsistency was bugging me.)

The blobbernaut is a bit less veiny, but the veins look more natural and
use the strain complementary colour. Many combinations are cool, some
are a bit lacking due to the weird choices of complementary colour.

![image](https://github.com/user-attachments/assets/aee01fb1-5d3a-47ad-abd6-85096f8b8751)

![image](https://github.com/user-attachments/assets/732a4157-c7f6-4a43-ad46-62a505a97088)

Blob mobs can now have strains independently of an overmind.

The 15% mutation chance of vat grown creatures causes a spore or
blobbernaut to get a random strain.

When I first added the blob spore cell line, ghosts could click on cyto
blob spores to posses them, they would then presumably(?) but not
explicitly be free antags. This ability was lost when the blob spore
code was modernised.

Very few people knew about this, and no one grew blob spores anyway.

This feature is coming back in a big way, vat grown blob spores present
a new unique job hazard, they are automatically offered to ghost as an
extremely shitty, but free antag.

I have tested spawning like 15 antag pre-buff blob spores in a live
round and they failed completely to antagonise the crew effectively,
hopefully these buffed spores won't present too much of an issue to our
great administration team, if that ends up being the case, there are
many levers to pull to tone them down.

Blob spores prior to this PR were almost completely useless.

The main cause of this was the extremely dilute reagent smoke reaction;
10u divided over 20 seconds.

This resulted the smoke clouds dealing 0.15 - 0.6 DPS, a completely
negligible and useless amount.

The smoke reagent concentration has been massively increased(10u -> 40u)
and the smoke duration has been reduced(20s -> 8s).
The result of this is that blob spore clouds are something you want to
avoid standing in, but they provide less smoke cover for the blob and
nauts.

Blob spores have also gained the ability to vent crawl. Simple mobs that
can't either open doors or vent crawl feel super bad to play.

They also deal a little more melee damage, but this is still
pathetically low on account of their low attack speed.

I have adjusted their supplementary reagents and reduced the amounts of
spores produced per cycle(2 -> 1) to make them a bit harder to mass
produce.

I have not made this PR with the goal of buffing any particular strain,
but some changes have affected blob strain balance:

This was the only strain that was strongly mechanically tied to the
core.
In order to allow for independent debris devourer mobs, they can now eat
trash(or any item really), they are independent, they store these items
inside their mob, and use these for the debris devourer reactions.

If they have an overmind, the item gets sent to the core.

This should result in a nice buff to the strain, which I've been told is
one of the bad ones.

5 years back another contributor removed the ability of blobs and
blobbernauts to transfer reagents with their attacks(as their expose
method is vapour).

This was a completely undocumented change and possibly unintentional, so
I am reverting it by giving blob reagents penetrates_skin = VAPOR again.

This only really affects these two strains. It makes regenerative
materia much stronger, while barely having any effect on cryogenic
poison, because temperature normalisation changes has made it completely
ineffective even with much more reagent applied.

The spore reagent cloud buff might also give a boost to some strains
with good expose effects, like electromagnetic web.

Blob spores now drop spore sacks, they can be ground for spore toxin, or
cracked on a griddle to create an egg-like treat!

I also added a detoxification reaction to reduce the amount of toxin
when cooked, might not work yet because I think griddles may not
actually heat the food?

Blob spores bursting and blobbernauts dying have sound effects.

level 5 biohazard

🆑
image: blob mobs have been respectfully resprited.
add: vat grown blob mobs can sometimes get born with a blob strain.
add: blob spores drop spore sacks, crack them on the griddle.
add: debris devourer mobs can now eat trash, sending it to the core, if
there is one.
add: vat grown blob spores are now sentient and evil.
balance: blob spores now have much more concentrated smoke.
balance: blob spores can ventcrawl.
fix: regenerative materia and cryogenic poison strain blob tiles & nauts
now inject chems again.
sound: blob spores & blobbernaut now have death sound effects.
/🆑
This commit is contained in:
Krysonism
2025-07-02 18:03:47 +02:00
committed by Roxy
parent 2f79341e78
commit 26527f85e7
32 changed files with 483 additions and 170 deletions

View File

@@ -93,8 +93,8 @@
#define BLOBMOB_HEALING_MULTIPLIER 0.0125 // Multiplies by -maxHealth and heals the blob by this amount every blob_act #define BLOBMOB_HEALING_MULTIPLIER 0.0125 // Multiplies by -maxHealth and heals the blob by this amount every blob_act
#define BLOBMOB_SPORE_HEALTH 30 // Base spore health #define BLOBMOB_SPORE_HEALTH 30 // Base spore health
#define BLOBMOB_SPORE_SPAWN_COOLDOWN (8 SECONDS) #define BLOBMOB_SPORE_SPAWN_COOLDOWN (8 SECONDS)
#define BLOBMOB_SPORE_DMG_LOWER 2 #define BLOBMOB_SPORE_DMG_LOWER 4
#define BLOBMOB_SPORE_DMG_UPPER 4 #define BLOBMOB_SPORE_DMG_UPPER 8
#define BLOBMOB_BLOBBERNAUT_RESOURCE_COST 40 // Purchase price for making a blobbernaut #define BLOBMOB_BLOBBERNAUT_RESOURCE_COST 40 // Purchase price for making a blobbernaut
#define BLOBMOB_BLOBBERNAUT_HEALTH 200 // Base blobbernaut health #define BLOBMOB_BLOBBERNAUT_HEALTH 200 // Base blobbernaut health
#define BLOBMOB_BLOBBERNAUT_DMG_SOLO_LOWER 20 // Damage without active overmind (core dead or xenobio mob) #define BLOBMOB_BLOBBERNAUT_DMG_SOLO_LOWER 20 // Damage without active overmind (core dead or xenobio mob)
@@ -106,3 +106,13 @@
#define BLOBMOB_BLOBBERNAUT_HEALING_CORE 0.05 // Percentage multiplier HP restored on Life() when within 2 tiles of the blob core #define BLOBMOB_BLOBBERNAUT_HEALING_CORE 0.05 // Percentage multiplier HP restored on Life() when within 2 tiles of the blob core
#define BLOBMOB_BLOBBERNAUT_HEALING_NODE 0.025 // Same, but for a nearby node #define BLOBMOB_BLOBBERNAUT_HEALING_NODE 0.025 // Same, but for a nearby node
#define BLOBMOB_BLOBBERNAUT_HEALTH_DECAY 0.0125 // Percentage multiplier HP lost when not near blob tiles or without factory #define BLOBMOB_BLOBBERNAUT_HEALTH_DECAY 0.0125 // Percentage multiplier HP lost when not near blob tiles or without factory
/// For blobmobs that you don't want to have a deathburst effect. (radius)
#define BLOBMOB_CLOUD_NONE -1
/// For blobmobs with small single tile clouds
#define BLOBMOB_CLOUD_SMALL 0
/// For normal 3x3 sized clouds
#define BLOBMOB_CLOUD_NORMAL 1
/// How much reagents we put into the spore death clouds in units.
#define BLOBMOB_CLOUD_REAGENT_VOLUME 40

View File

@@ -77,6 +77,7 @@
#define COLOR_LIGHT_YELLOW "#FFFEE0" #define COLOR_LIGHT_YELLOW "#FFFEE0"
#define COLOR_OLIVE "#808000" #define COLOR_OLIVE "#808000"
#define COLOR_OLIVE_GREEN "#677714"
#define COLOR_ASSISTANT_OLIVE "#828163" #define COLOR_ASSISTANT_OLIVE "#828163"
#define COLOR_VIBRANT_LIME "#00FF00" #define COLOR_VIBRANT_LIME "#00FF00"
#define COLOR_SERVICE_LIME "#58C800" #define COLOR_SERVICE_LIME "#58C800"
@@ -112,7 +113,7 @@
#define COLOR_MODERATE_BLUE "#555CC2" #define COLOR_MODERATE_BLUE "#555CC2"
#define COLOR_TRAM_BLUE "#6160A8" #define COLOR_TRAM_BLUE "#6160A8"
#define COLOR_TRAM_LIGHT_BLUE "#A8A7DA" #define COLOR_TRAM_LIGHT_BLUE "#A8A7DA"
#define COLOR_AMETHYST "#822BFF" #define COLOR_AMETHYST "#822bff"
#define COLOR_BLUE_LIGHT "#33CCFF" #define COLOR_BLUE_LIGHT "#33CCFF"
#define COLOR_BLUE_VERY_LIGHT "#ccecff" #define COLOR_BLUE_VERY_LIGHT "#ccecff"
#define COLOR_NAVY "#000080" #define COLOR_NAVY "#000080"
@@ -131,7 +132,7 @@
#define COLOR_VIOLET "#B900F7" #define COLOR_VIOLET "#B900F7"
#define COLOR_VOID_PURPLE "#53277E" #define COLOR_VOID_PURPLE "#53277E"
#define COLOR_STRONG_VIOLET "#6927C5" #define COLOR_STRONG_VIOLET "#6927C5"
#define COLOR_DARK_PURPLE "#551A8B" #define COLOR_DARK_PURPLE "#551a8b"
#define COLOR_ORANGE "#FF9900" #define COLOR_ORANGE "#FF9900"
#define COLOR_IRISH_ORANGE "#FF883E" #define COLOR_IRISH_ORANGE "#FF883E"

View File

@@ -24,3 +24,7 @@
///from /datum/status_effect/slime_leech: (mob/living/basic/slime/draining_slime) ///from /datum/status_effect/slime_leech: (mob/living/basic/slime/draining_slime)
#define COMSIG_SLIME_DRAINED "slime_drained" #define COMSIG_SLIME_DRAINED "slime_drained"
/// from /mob/living/basic/mutate(): (mob/living/basic/mutated_mob)
#define COMSIG_BASICMOB_MUTATED "basicmob_mutated"
///cancel further mutation modifications to the mob such as shiny mutation.
#define MUTATED_NO_FURTHER_MUTATIONS (1 << 0)

View File

@@ -253,7 +253,7 @@
#define COMSIG_MOB_DROPPING_ITEM "mob_dropping_item" #define COMSIG_MOB_DROPPING_ITEM "mob_dropping_item"
/// from /mob/proc/change_mob_type() : () /// from /mob/proc/change_mob_type() : ()
#define COMSIG_PRE_MOB_CHANGED_TYPE "mob_changed_type" #define COMSIG_PRE_MOB_CHANGED_TYPE "pre_mob_changed_type"
#define COMPONENT_BLOCK_MOB_CHANGE (1<<0) #define COMPONENT_BLOCK_MOB_CHANGE (1<<0)
/// from /mob/proc/change_mob_type_unchecked() : () /// from /mob/proc/change_mob_type_unchecked() : ()
#define COMSIG_MOB_CHANGED_TYPE "mob_changed_type" #define COMSIG_MOB_CHANGED_TYPE "mob_changed_type"

View File

@@ -72,6 +72,7 @@
/// This flag specifically is used as a generic catch-all antag ban /// This flag specifically is used as a generic catch-all antag ban
#define ROLE_SYNDICATE "Syndicate" #define ROLE_SYNDICATE "Syndicate"
#define ROLE_EXPERIMENTAL_CLONER "Experimental Cloner" #define ROLE_EXPERIMENTAL_CLONER "Experimental Cloner"
#define ROLE_FREE_BLOB "Free Blob"
#define ROLE_CLOWN_OPERATIVE "Clown Operative" #define ROLE_CLOWN_OPERATIVE "Clown Operative"
#define ROLE_FREE_GOLEM "Free Golem" #define ROLE_FREE_GOLEM "Free Golem"

View File

@@ -334,26 +334,14 @@ world
/proc/RotateHue(rgb, angle) /proc/RotateHue(rgb, angle)
var/list/HSV = rgb2hsv(rgb) var/list/HSV = rgb2hsv(rgb)
// normalize hsv in case anything is screwy angle %= 360
if(HSV[1] >= 1536)
HSV[1] %= 1536 HSV[1] = round(HSV[1] + angle)
HSV[1] %= 360
if(HSV[1] < 0) if(HSV[1] < 0)
HSV[1] += 1536 HSV[1] += 360
// Compress hue into easier-to-manage range
HSV[1] -= HSV[1] >> 8
if(angle < 0 || angle >= 360)
angle -= 360 * round(angle / 360)
HSV[1] = round(HSV[1] + angle * (1530/360), 1)
// normalize hue
if(HSV[1] < 0 || HSV[1] >= 1530)
HSV[1] %= 1530
if(HSV[1] < 0)
HSV[1] += 1530
// decompress hue
HSV[1] += round(HSV[1] / 255)
return hsv2rgb(HSV) return hsv2rgb(HSV)

View File

@@ -39,6 +39,7 @@
#define POLL_IGNORE_VENUSHUMANTRAP "venus_human_trap" #define POLL_IGNORE_VENUSHUMANTRAP "venus_human_trap"
#define POLL_IGNORE_RECOVERED_CREW "recovered_crew" #define POLL_IGNORE_RECOVERED_CREW "recovered_crew"
#define POLL_IGNORE_EXPERIMENTAL_CLONER "experimental_cloner" #define POLL_IGNORE_EXPERIMENTAL_CLONER "experimental_cloner"
#define POLL_IGNORE_FREE_SPORE "free_spore"
GLOBAL_LIST_INIT(poll_ignore_desc, list( GLOBAL_LIST_INIT(poll_ignore_desc, list(
POLL_IGNORE_ACADEMY_WIZARD = "Academy Wizard Defender", POLL_IGNORE_ACADEMY_WIZARD = "Academy Wizard Defender",
@@ -79,6 +80,7 @@ GLOBAL_LIST_INIT(poll_ignore_desc, list(
POLL_IGNORE_SYNDICATE = "Syndicate", POLL_IGNORE_SYNDICATE = "Syndicate",
POLL_IGNORE_VENUSHUMANTRAP = "Venus Human Traps", POLL_IGNORE_VENUSHUMANTRAP = "Venus Human Traps",
POLL_IGNORE_RECOVERED_CREW = "Recovered Crew", POLL_IGNORE_RECOVERED_CREW = "Recovered Crew",
POLL_IGNORE_FREE_SPORE = "Free spore",
)) ))
GLOBAL_LIST_INIT(poll_ignore, init_poll_ignore()) GLOBAL_LIST_INIT(poll_ignore, init_poll_ignore())

View File

@@ -364,6 +364,7 @@
old_component.InheritComponent(arglist(arguments)) old_component.InheritComponent(arglist(arguments))
else else
old_component.InheritComponent(new_component, TRUE) old_component.InheritComponent(new_component, TRUE)
QDEL_NULL(new_component)
if(COMPONENT_DUPE_SOURCES) if(COMPONENT_DUPE_SOURCES)
if((source in old_component.sources) && !old_component.allow_source_update(source)) if((source in old_component.sources) && !old_component.allow_source_update(source))

View File

@@ -7,40 +7,66 @@
var/mob/eye/blob/overmind var/mob/eye/blob/overmind
/// Callback to run if overmind strain changes /// Callback to run if overmind strain changes
var/datum/callback/on_strain_changed var/datum/callback/on_strain_changed
/// Our strain we should not acess the overminds strain directly as we may not have one.
var/datum/blobstrain/our_strain
/// Used to determine the size of blob mob death clouds or equivlent strain dependant spore death effects
var/death_cloud_size = BLOBMOB_CLOUD_NONE
/datum/component/blob_minion/Initialize(mob/eye/blob/overmind, datum/callback/on_strain_changed) /datum/component/blob_minion/Initialize(mob/eye/blob/new_overmind, datum/callback/on_strain_changed, new_death_cloud_size, datum/blobstrain/new_strain)
. = ..() . = ..()
if (!isliving(parent)) if (!isliving(parent))
return COMPONENT_INCOMPATIBLE return COMPONENT_INCOMPATIBLE
src.on_strain_changed = on_strain_changed
register_overlord(overmind)
/datum/component/blob_minion/InheritComponent(datum/component/new_comp, i_am_original, mob/eye/blob/overmind, datum/callback/on_strain_changed) if(isnum(new_death_cloud_size))
death_cloud_size = new_death_cloud_size
src.on_strain_changed = on_strain_changed
//checking for a lack of overmind to avoid calling strain_properties changed twice.
if(new_strain && !new_overmind)
strain_properties_changed(null, new_strain)
if(new_overmind)
register_overlord(new_overmind)
/datum/component/blob_minion/InheritComponent(datum/component/new_comp, i_am_original, mob/eye/blob/new_overmind, datum/callback/on_strain_changed, new_death_cloud_size, datum/blobstrain/new_strain)
if(isnum(new_death_cloud_size))
death_cloud_size = new_death_cloud_size
if (!isnull(on_strain_changed)) if (!isnull(on_strain_changed))
src.on_strain_changed = on_strain_changed src.on_strain_changed = on_strain_changed
register_overlord(overmind)
/datum/component/blob_minion/proc/register_overlord(mob/eye/blob/overmind) if(new_strain && !new_overmind)
if (isnull(overmind)) strain_properties_changed(null, new_strain)
return
src.overmind = overmind if(new_overmind)
register_overlord(new_overmind)
/datum/component/blob_minion/proc/register_overlord(mob/eye/blob/new_overmind)
overmind = new_overmind
overmind.register_new_minion(parent) overmind.register_new_minion(parent)
RegisterSignal(overmind, COMSIG_QDELETING, PROC_REF(overmind_deleted)) RegisterSignal(overmind, COMSIG_QDELETING, PROC_REF(overmind_deleted))
RegisterSignal(overmind, COMSIG_BLOB_SELECTED_STRAIN, PROC_REF(overmind_properties_changed)) RegisterSignal(overmind, COMSIG_BLOB_SELECTED_STRAIN, PROC_REF(strain_properties_changed))
overmind_properties_changed(overmind, overmind.blobstrain) strain_properties_changed(overmind, overmind.blobstrain)
/// Our overmind is gone, uh oh! /// Our overmind is gone, uh oh!
/datum/component/blob_minion/proc/overmind_deleted() /datum/component/blob_minion/proc/overmind_deleted()
SIGNAL_HANDLER SIGNAL_HANDLER
overmind = null overmind = null
overmind_properties_changed() strain_properties_changed()
/// Our overmind has changed colour and properties /// Our strain has changed, perhaps because our blob overmind has changed strain, died, or because of a mutation.
/datum/component/blob_minion/proc/overmind_properties_changed(mob/eye/blob/overmind, datum/blobstrain/new_strain) /datum/component/blob_minion/proc/strain_properties_changed(mob/eye/blob/changed_overmind, datum/blobstrain/new_strain)
SIGNAL_HANDLER SIGNAL_HANDLER
var/mob/living/living_parent = parent var/mob/living/living_parent = parent
if(new_strain)
our_strain = new_strain
else
our_strain = null
living_parent.update_appearance(UPDATE_ICON) living_parent.update_appearance(UPDATE_ICON)
on_strain_changed?.Invoke(overmind, new_strain) on_strain_changed?.Invoke(changed_overmind, new_strain)
/datum/component/blob_minion/RegisterWithParent() /datum/component/blob_minion/RegisterWithParent()
var/mob/living/living_parent = parent var/mob/living/living_parent = parent
@@ -57,7 +83,11 @@
RegisterSignal(parent, COMSIG_MOVABLE_SPACEMOVE, PROC_REF(on_space_move)) RegisterSignal(parent, COMSIG_MOVABLE_SPACEMOVE, PROC_REF(on_space_move))
RegisterSignal(parent, COMSIG_MOB_TRY_SPEECH, PROC_REF(on_try_speech)) RegisterSignal(parent, COMSIG_MOB_TRY_SPEECH, PROC_REF(on_try_speech))
RegisterSignal(parent, COMSIG_MOB_CHANGED_TYPE, PROC_REF(on_transformed)) RegisterSignal(parent, COMSIG_MOB_CHANGED_TYPE, PROC_REF(on_transformed))
living_parent.update_appearance(UPDATE_ICON) RegisterSignal(parent, COMSIG_LIVING_DEATH, PROC_REF(on_death))
RegisterSignal(parent, COMSIG_BASICMOB_MUTATED, PROC_REF(on_mutated))
RegisterSignal(parent, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(on_minion_atom_interacted))
if(overmind || our_strain)
strain_properties_changed(overmind, our_strain)
GLOB.blob_telepathy_mobs |= parent GLOB.blob_telepathy_mobs |= parent
/datum/component/blob_minion/UnregisterFromParent() /datum/component/blob_minion/UnregisterFromParent()
@@ -78,6 +108,9 @@
COMSIG_MOB_GET_STATUS_TAB_ITEMS, COMSIG_MOB_GET_STATUS_TAB_ITEMS,
COMSIG_MOB_MIND_INITIALIZED, COMSIG_MOB_MIND_INITIALIZED,
COMSIG_MOVABLE_SPACEMOVE, COMSIG_MOVABLE_SPACEMOVE,
COMSIG_LIVING_DEATH,
COMSIG_BASICMOB_MUTATED,
COMSIG_HOSTILE_PRE_ATTACKINGTARGET,
)) ))
GLOB.blob_telepathy_mobs -= parent GLOB.blob_telepathy_mobs -= parent
@@ -92,10 +125,10 @@
/// When our icon is updated, update our colour too /// When our icon is updated, update our colour too
/datum/component/blob_minion/proc/on_update_appearance(mob/living/minion) /datum/component/blob_minion/proc/on_update_appearance(mob/living/minion)
SIGNAL_HANDLER SIGNAL_HANDLER
if(isnull(overmind)) if(our_strain?.color)
minion.add_atom_colour(our_strain.color, FIXED_COLOUR_PRIORITY)
else
minion.remove_atom_colour(FIXED_COLOUR_PRIORITY) minion.remove_atom_colour(FIXED_COLOUR_PRIORITY)
return
minion.add_atom_colour(overmind.blobstrain.color, FIXED_COLOUR_PRIORITY)
/// When our icon is updated, update our colour too /// When our icon is updated, update our colour too
/datum/component/blob_minion/proc/on_update_status_tab(mob/living/minion, list/status_items) /datum/component/blob_minion/proc/on_update_status_tab(mob/living/minion, list/status_items)
@@ -148,8 +181,37 @@
/// Called when a blob minion is transformed into something else, hopefully a spore into a zombie /// Called when a blob minion is transformed into something else, hopefully a spore into a zombie
/datum/component/blob_minion/proc/on_transformed(mob/living/minion, mob/living/replacement) /datum/component/blob_minion/proc/on_transformed(mob/living/minion, mob/living/replacement)
SIGNAL_HANDLER SIGNAL_HANDLER
overmind?.assume_direct_control(replacement) replacement.AddComponent(/datum/component/blob_minion, new_overmind = overmind, new_death_cloud_size = death_cloud_size, new_strain = our_strain)
/datum/component/blob_minion/PostTransfer(datum/new_parent) /datum/component/blob_minion/proc/on_death(mob/living/minion)
if(!isliving(new_parent)) SIGNAL_HANDLER
return COMPONENT_INCOMPATIBLE
if(death_cloud_size <= BLOBMOB_CLOUD_NONE)
return
if(our_strain)
our_strain.on_sporedeath(minion, death_cloud_size)
else
do_chem_smoke(range = death_cloud_size, holder = minion, location = get_turf(minion), reagent_type = /datum/reagent/toxin/spore, reagent_volume = BLOBMOB_CLOUD_REAGENT_VOLUME, smoke_type = /datum/effect_system/fluid_spread/smoke/chem/medium)
playsound(minion, 'sound/mobs/non-humanoids/blobmob/blob_spore_burst.ogg', vol = 100)
///When am independent mob with this component mutates, like from a random cytology mutation, give them a strain and modify their name to let the players know they have something special.
/datum/component/blob_minion/proc/on_mutated(mob/living/minion)
SIGNAL_HANDLER
if(overmind || our_strain)
return
var/datum/blobstrain/mutant_strain = pick(GLOB.valid_blobstrains)
strain_properties_changed(changed_overmind = null, new_strain = new mutant_strain)
minion.name = "[LOWER_TEXT(our_strain.name)] [minion.name]"
//normally the overmind would handle this, but we have none.
minion.maxHealth *= our_strain.max_mob_health_multiplier
minion.health *= our_strain.max_mob_health_multiplier
return MUTATED_NO_FURTHER_MUTATIONS
///For when we want to trigger effects when a blobmob clicks something, such as clicking on items.
/datum/component/blob_minion/proc/on_minion_atom_interacted(mob/living/minion, atom/interacted_atom, adjacent, modifiers)
SIGNAL_HANDLER
return our_strain?.on_blobmob_atom_interacted(minion, interacted_atom, adjacent, modifiers)

View File

@@ -397,11 +397,11 @@
return TRUE return TRUE
/// Helper to quickly create a cloud of reagent smoke /// Helper to quickly create a cloud of reagent smoke
/proc/do_chem_smoke(range = 0, amount = DIAMOND_AREA(range), atom/holder = null, location = null, reagent_type = /datum/reagent/water, reagent_volume = 10, log = FALSE) /proc/do_chem_smoke(range = 0, amount = DIAMOND_AREA(range), atom/holder = null, location = null, reagent_type = /datum/reagent/water, reagent_volume = 10, log = FALSE, datum/effect_system/fluid_spread/smoke/chem/smoke_type = /datum/effect_system/fluid_spread/smoke/chem)
var/datum/reagents/smoke_reagents = new/datum/reagents(reagent_volume) var/datum/reagents/smoke_reagents = new/datum/reagents(reagent_volume)
smoke_reagents.add_reagent(reagent_type, reagent_volume) smoke_reagents.add_reagent(reagent_type, reagent_volume)
var/datum/effect_system/fluid_spread/smoke/chem/smoke = new var/datum/effect_system/fluid_spread/smoke/chem/smoke = new smoke_type
smoke.attach(location) smoke.attach(location)
smoke.set_up(amount = amount, holder = holder, location = location, carry = smoke_reagents, silent = TRUE) smoke.set_up(amount = amount, holder = holder, location = location, carry = smoke_reagents, silent = TRUE)
smoke.start(log = log) smoke.start(log = log)
@@ -470,3 +470,12 @@
/datum/effect_system/fluid_spread/smoke/chem/quick /datum/effect_system/fluid_spread/smoke/chem/quick
effect_type = /obj/effect/particle_effect/fluid/smoke/chem/quick effect_type = /obj/effect/particle_effect/fluid/smoke/chem/quick
/**
* A version of chemical smoke with a intermediate lifespan.
*/
/obj/effect/particle_effect/fluid/smoke/chem/medium
lifetime = 8 SECONDS
/datum/effect_system/fluid_spread/smoke/chem/medium
effect_type = /obj/effect/particle_effect/fluid/smoke/chem/medium

View File

@@ -347,3 +347,74 @@ GLOBAL_VAR_INIT(chicks_from_eggs, 0)
foodtypes = MEAT | VEGETABLES foodtypes = MEAT | VEGETABLES
venue_value = FOOD_PRICE_NORMAL venue_value = FOOD_PRICE_NORMAL
crafting_complexity = FOOD_COMPLEXITY_3 crafting_complexity = FOOD_COMPLEXITY_3
/obj/item/food/spore_sack
name = "spore sack"
desc = "A spore sack. blobby and gooey!"
icon = 'icons/obj/food/egg.dmi'
icon_state = "spore_sack"
base_icon_state = "spore_sack"
inhand_icon_state = "egg"
food_reagents = list(/datum/reagent/consumable/eggyolk = 4, /datum/reagent/toxin/spore = 4, /datum/reagent/consumable/eggwhite = 1, /datum/reagent/consumable/nutriment/vitamin = 1)
tastes = list("sliminess" = 4, "blob" = 2)
foodtypes = MEAT | RAW | TOXIC
w_class = WEIGHT_CLASS_TINY
ant_attracting = FALSE
preserved_food = TRUE
/obj/item/food/spore_sack/Initialize(mapload)
. = ..()
if(prob(50))
icon_state = "[base_icon_state]2"
AddElement(/datum/element/swabable, CELL_LINE_TABLE_BLOBSPORE, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 5)
/obj/item/food/spore_sack/interact_with_atom_secondary(atom/interacting_with, mob/living/user, list/modifiers)
if(!istype(interacting_with, /obj/machinery/griddle))
return NONE
var/obj/machinery/griddle/hit_griddle = interacting_with
if(length(hit_griddle.griddled_objects) >= hit_griddle.max_items)
interacting_with.balloon_alert(user, "no room!")
return ITEM_INTERACT_BLOCKING
var/atom/broken_egg = new /obj/item/food/rawegg/spore(interacting_with.loc)
if(LAZYACCESS(modifiers, ICON_X))
broken_egg.pixel_x = clamp(text2num(LAZYACCESS(modifiers, ICON_X)) - 16, -(ICON_SIZE_X/2), ICON_SIZE_X/2)
if(LAZYACCESS(modifiers, ICON_Y))
broken_egg.pixel_y = clamp(text2num(LAZYACCESS(modifiers, ICON_Y)) - 16, -(ICON_SIZE_Y/2), ICON_SIZE_Y/2)
playsound(user, 'sound/items/sheath.ogg', 40, TRUE)
reagents.copy_to(broken_egg, reagents.total_volume)
hit_griddle.AddToGrill(broken_egg, user)
interacting_with.balloon_alert(user, "cracks [src] open")
qdel(src)
return ITEM_INTERACT_BLOCKING
/obj/item/food/spore_sack/independent
icon_state = "spore_sack_independent"
base_icon_state = "spore_sack_independent"
/obj/item/food/friedegg/spore
name = "fried spore"
desc = "A fried blob spore. Would go well with a dab of cold sauce."
icon_state = "friedspore"
//superior healing and cyto reagents to compensate for rarity and mild poison effect.
food_reagents = list(
/datum/reagent/consumable/nutriment/peptides = 2,
/datum/reagent/consumable/nutriment/vitamin = 1,
)
tastes = list("blob" = 4, "level 5 biohazard" = 2)
/obj/item/food/rawegg/spore
name = "burst spore"
desc = "Is this the ant egg everyone is always talking about? Better fried."
icon_state = "burstspore"
tastes = list("sliminess" = 4, "blob" = 2)
/obj/item/food/rawegg/spore/Initialize(mapload)
. = ..()
AddElement(/datum/element/swabable, CELL_LINE_TABLE_BLOBSPORE, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 5)
/obj/item/food/rawegg/spore/make_grillable()
AddComponent(/datum/component/grillable, /obj/item/food/friedegg/spore, rand(15 SECONDS, 25 SECONDS), TRUE, FALSE)

View File

@@ -63,9 +63,8 @@ GLOBAL_LIST_INIT(valid_blobstrains, subtypesof(/datum/blobstrain) - list(/datum/
var/blobbernaut_reagentatk_bonus = 0 var/blobbernaut_reagentatk_bonus = 0
/datum/blobstrain/New(mob/eye/blob/new_overmind) /datum/blobstrain/New(mob/eye/blob/new_overmind)
if (!istype(new_overmind)) if(new_overmind)
stack_trace("blobstrain created without overmind") overmind = new_overmind
overmind = new_overmind
/datum/blobstrain/Destroy() /datum/blobstrain/Destroy()
overmind = null overmind = null
@@ -126,7 +125,8 @@ GLOBAL_LIST_INIT(valid_blobstrains, subtypesof(/datum/blobstrain) - list(/datum/
blob_mob.health /= max_mob_health_multiplier blob_mob.health /= max_mob_health_multiplier
/datum/blobstrain/proc/on_sporedeath(mob/living/spore) /datum/blobstrain/proc/on_sporedeath(mob/living/dead_minion, death_cloud_size)
return
/datum/blobstrain/proc/send_message(mob/living/M) /datum/blobstrain/proc/send_message(mob/living/M)
var/totalmessage = message var/totalmessage = message
@@ -145,7 +145,8 @@ GLOBAL_LIST_INIT(valid_blobstrains, subtypesof(/datum/blobstrain) - list(/datum/
send_message(L) send_message(L)
/// When this blob's blobbernaut attacks any atom /// When this blob's blobbernaut attacks any atom
/datum/blobstrain/proc/blobbernaut_attack(atom/attacking, mob/living/basic/blobbernaut) /datum/blobstrain/proc/blobbernaut_attack(mob/living/blobbernaut, atom/victim)
SIGNAL_HANDLER
return return
/datum/blobstrain/proc/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag, coefficient = 1) //when the blob takes damage, do this /datum/blobstrain/proc/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag, coefficient = 1) //when the blob takes damage, do this
@@ -168,3 +169,6 @@ GLOBAL_LIST_INIT(valid_blobstrains, subtypesof(/datum/blobstrain) - list(/datum/
/datum/blobstrain/proc/examine(mob/user) /datum/blobstrain/proc/examine(mob/user)
return list("<b>Progress to Critical Mass:</b> [span_notice("[overmind.blobs_legit.len]/[overmind.blobwincount].")]") return list("<b>Progress to Critical Mass:</b> [span_notice("[overmind.blobs_legit.len]/[overmind.blobwincount].")]")
/datum/blobstrain/proc/on_blobmob_atom_interacted(mob/living/minion, atom/interacted_atom, adjacent, modifiers)
return

View File

@@ -11,17 +11,18 @@
reagent.expose_mob(L, VAPOR, BLOB_REAGENTATK_VOL, TRUE, mob_protection, overmind) reagent.expose_mob(L, VAPOR, BLOB_REAGENTATK_VOL, TRUE, mob_protection, overmind)
send_message(L) send_message(L)
/datum/blobstrain/reagent/blobbernaut_attack(atom/attacking, mob/living/basic/blobbernaut) /datum/blobstrain/reagent/blobbernaut_attack(mob/living/blobbernaut, atom/victim)
if(!isliving(attacking)) ..()
if(!isliving(victim))
return return
var/mob/living/living_attacking = attacking var/mob/living/living_victim = victim
var/mob_protection = living_attacking.getarmor(null, BIO) * 0.01 var/mob_protection = living_victim.getarmor(null, BIO) * 0.01
reagent.expose_mob(living_attacking, VAPOR, BLOBMOB_BLOBBERNAUT_REAGENTATK_VOL+blobbernaut_reagentatk_bonus, FALSE, mob_protection, overmind)//this will do between 10 and 20 damage(reduced by mob protection), depending on chemical, plus 4 from base brute damage. reagent.expose_mob(living_victim, VAPOR, BLOBMOB_BLOBBERNAUT_REAGENTATK_VOL+blobbernaut_reagentatk_bonus, FALSE, mob_protection, overmind)//this will do between 10 and 20 damage(reduced by mob protection), depending on chemical, plus 4 from base brute damage.
/datum/blobstrain/reagent/on_sporedeath(mob/living/basic/spore) /datum/blobstrain/reagent/on_sporedeath(mob/living/dead_minion, death_cloud_size)
var/burst_range = (spore.type == /mob/living/basic/blob_minion/spore) ? 1 : 0 do_chem_smoke(range = death_cloud_size, holder = dead_minion, location = get_turf(dead_minion), reagent_type = reagent.type, reagent_volume = BLOBMOB_CLOUD_REAGENT_VOLUME, smoke_type = /datum/effect_system/fluid_spread/smoke/chem/medium)
do_chem_smoke(range = burst_range, holder = spore, location = get_turf(spore), reagent_type = reagent.type) playsound(dead_minion, 'sound/mobs/non-humanoids/blobmob/blob_spore_burst.ogg', vol = 100, vary = TRUE)
// These can only be applied by blobs. They are what (reagent) blobs are made out of. // These can only be applied by blobs. They are what (reagent) blobs are made out of.
/datum/reagent/blob /datum/reagent/blob
@@ -30,7 +31,6 @@
color = COLOR_WHITE color = COLOR_WHITE
taste_description = "bad code and slime" taste_description = "bad code and slime"
chemical_flags = NONE chemical_flags = NONE
penetrates_skin = NONE
/datum/reagent/blob/New() /datum/reagent/blob/New()

View File

@@ -1,4 +1,6 @@
#define DEBRIS_DENSITY (length(core.contents) / (length(overmind.blobs_legit) * 0.25)) // items per blob #define DEBRIS_DENSITY (length(overmind.blob_core.contents) / (length(overmind.blobs_legit) * 0.25)) // items per blob
#define SPORE_TRASH_COUNT 3
#define FREE_MINION_DEBRIS_CHANCE 80
// Accumulates junk liberally // Accumulates junk liberally
/datum/blobstrain/debris_devourer /datum/blobstrain/debris_devourer
@@ -11,42 +13,53 @@
blobbernaut_message = "blasts" blobbernaut_message = "blasts"
message = "The blob blasts you" message = "The blob blasts you"
/datum/blobstrain/debris_devourer/attack_living(mob/living/L, list/nearby_blobs) /datum/blobstrain/debris_devourer/attack_living(mob/living/L, list/nearby_blobs)
send_message(L) send_message(L)
for (var/obj/structure/blob/blob in nearby_blobs) for (var/obj/structure/blob/blob in nearby_blobs)
debris_attack(L, blob) debris_attack(L, blob)
/datum/blobstrain/debris_devourer/on_sporedeath(mob/living/spore) /datum/blobstrain/debris_devourer/on_sporedeath(mob/living/spore, death_cloud_size)
var/obj/structure/blob/special/core/core = overmind.blob_core var/list/trash_source = overmind ? overmind.blob_core.contents : spore.contents
for(var/i in 1 to 3)
var/obj/item/I = pick(core.contents) var/trashsplosion_count = overmind ? SPORE_TRASH_COUNT : spore.contents.len
if (I && !QDELETED(I))
I.forceMove(get_turf(spore)) for(var/i in 1 to trashsplosion_count)
I.throw_at(get_edge_target_turf(spore,pick(GLOB.alldirs)), 6, 5, spore, TRUE, FALSE, null, 3) var/obj/item/trash_shrapnel = pick(trash_source)
if (trash_shrapnel && !QDELETED(trash_shrapnel))
trash_shrapnel.forceMove(get_turf(spore))
trash_shrapnel.throw_at(get_edge_target_turf(spore,pick(GLOB.alldirs)), 6, 5, spore, TRUE, FALSE, null, 3)
playsound(spore, 'sound/effects/pop_expl.ogg', vol = 100, vary = TRUE)
/datum/blobstrain/debris_devourer/expand_reaction(obj/structure/blob/B, obj/structure/blob/newB, turf/T, mob/eye/blob/O, coefficient = 1) //when the blob expands, do this /datum/blobstrain/debris_devourer/expand_reaction(obj/structure/blob/B, obj/structure/blob/newB, turf/T, mob/eye/blob/O, coefficient = 1) //when the blob expands, do this
for (var/obj/item/I in T) for (var/obj/item/I in T)
I.forceMove(overmind.blob_core) I.forceMove(overmind.blob_core)
/datum/blobstrain/debris_devourer/proc/debris_attack(atom/attacking, atom/source) /datum/blobstrain/debris_devourer/proc/debris_attack(atom/attacking, atom/source)
var/obj/structure/blob/special/core/core = overmind.blob_core if (!prob(overmind ? 40 * DEBRIS_DENSITY : FREE_MINION_DEBRIS_CHANCE)) // Pretend the items are spread through the blob and its mobs and not in the core.
if (prob(40 * DEBRIS_DENSITY)) // Pretend the items are spread through the blob and its mobs and not in the core. return
var/obj/item/I = length(core.contents) ? pick(core.contents) : null
if (!QDELETED(I))
I.forceMove(get_turf(source))
I.throw_at(attacking, 6, 5, overmind, TRUE, FALSE, null, 3)
/datum/blobstrain/debris_devourer/blobbernaut_attack(atom/attacking, mob/living/basic/blobbernaut) var/list/trash_collection = overmind ? overmind.blob_core.contents : source.contents
debris_attack(attacking, blobbernaut)
if(!length(trash_collection))
return
var/obj/item/trash_weapon = pick(trash_collection)
if (QDELETED(trash_weapon))
return
trash_weapon.forceMove(get_turf(source))
trash_weapon.throw_at(attacking, 6, 5, overmind ? overmind : source, TRUE, FALSE, null, 3)
/datum/blobstrain/debris_devourer/blobbernaut_attack(mob/living/blobbernaut, atom/victim)
..()
debris_attack(victim, blobbernaut)
/datum/blobstrain/debris_devourer/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag, coefficient = 1) //when the blob takes damage, do this /datum/blobstrain/debris_devourer/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag, coefficient = 1) //when the blob takes damage, do this
var/obj/structure/blob/special/core/core = overmind.blob_core
return round(max((coefficient*damage)-min(coefficient*DEBRIS_DENSITY, 10), 0)) // reduce damage taken by items per blob, up to 10 return round(max((coefficient*damage)-min(coefficient*DEBRIS_DENSITY, 10), 0)) // reduce damage taken by items per blob, up to 10
/datum/blobstrain/debris_devourer/examine(mob/user) /datum/blobstrain/debris_devourer/examine(mob/user)
. = ..() . = ..()
var/obj/structure/blob/special/core/core = overmind.blob_core
if (isobserver(user)) if (isobserver(user))
. += span_notice("Absorbed debris is currently reducing incoming damage by [round(max(min(DEBRIS_DENSITY, 10),0))]") . += span_notice("Absorbed debris is currently reducing incoming damage by [round(max(min(DEBRIS_DENSITY, 10),0))]")
else else
@@ -60,4 +73,21 @@
if (8 to 10) if (8 to 10)
. += span_notice("Absorbed debris is currently reducing incoming damage by a medium amount.") . += span_notice("Absorbed debris is currently reducing incoming damage by a medium amount.")
/datum/blobstrain/debris_devourer/on_blobmob_atom_interacted(mob/living/minion, atom/interacted_atom, adjacent, modifiers)
. = ..()
if(!isitem(interacted_atom) || !adjacent)
return
if(minion.contents.len >= minion.mob_size * 5)
to_chat(minion, span_warning("You feel too full to eat more trash."))
return
playsound(minion, 'sound/items/eatfood.ogg', 60, TRUE)
var/obj/item/tasty_trash = interacted_atom
minion.do_attack_animation(tasty_trash)
tasty_trash.forceMove(overmind ? overmind.blob_core : minion)
return COMPONENT_HOSTILE_NO_ATTACK
#undef DEBRIS_DENSITY #undef DEBRIS_DENSITY
#undef SPORE_TRASH_COUNT
#undef FREE_MINION_DEBRIS_CHANCE

View File

@@ -26,7 +26,7 @@
. = ..() . = ..()
reac_volume = return_mob_expose_reac_volume(exposed_mob, methods, reac_volume, show_message, touch_protection, overmind) reac_volume = return_mob_expose_reac_volume(exposed_mob, methods, reac_volume, show_message, touch_protection, overmind)
exposed_mob.apply_damage(0.6*reac_volume, TOX) exposed_mob.apply_damage(0.6*reac_volume, TOX)
if(overmind && ishuman(exposed_mob)) if(ishuman(exposed_mob))
if(exposed_mob.stat == UNCONSCIOUS || exposed_mob.stat == HARD_CRIT) if(exposed_mob.stat == UNCONSCIOUS || exposed_mob.stat == HARD_CRIT)
exposed_mob.investigate_log("has been killed by distributed neurons (blob).", INVESTIGATE_DEATHS) exposed_mob.investigate_log("has been killed by distributed neurons (blob).", INVESTIGATE_DEATHS)
exposed_mob.death() //sleeping in a fight? bad plan. exposed_mob.death() //sleeping in a fight? bad plan.

View File

@@ -18,13 +18,17 @@
return damage * 1.5 return damage * 1.5
return ..() return ..()
/datum/blobstrain/reagent/explosive_lattice/on_sporedeath(mob/living/spore) /datum/blobstrain/reagent/explosive_lattice/on_sporedeath(mob/living/dead_spore, death_cloud_size)
var/obj/effect/temp_visual/explosion/fast/effect = new /obj/effect/temp_visual/explosion/fast(get_turf(spore)) var/obj/effect/temp_visual/explosion/fast/effect = new /obj/effect/temp_visual/explosion/fast(get_turf(dead_spore))
effect.alpha = 150 effect.alpha = 150
for(var/mob/living/actor in orange(get_turf(spore), 1)) for(var/mob/living/actor in orange(get_turf(dead_spore), death_cloud_size))
if(ROLE_BLOB in actor.faction) // No friendly fire if(ROLE_BLOB in actor.faction) // No friendly fire
continue continue
actor.take_overall_damage(10, 10) //increases damage to mobs if death_cloud_size is increased, but the damage falls off with distance.
var/damage_total = (10 + 10 * death_cloud_size) / max(1, get_dist(get_turf(dead_spore), get_turf(actor)))
//split damage between brute and burn
actor.take_overall_damage(damage_total / 2, damage_total / 2)
playsound(dead_spore , 'sound/effects/explosion/explosion2.ogg', 20 + 20 * death_cloud_size, TRUE)
/datum/reagent/blob/explosive_lattice /datum/reagent/blob/explosive_lattice
name = "Explosive Lattice" name = "Explosive Lattice"
@@ -38,7 +42,7 @@
var/bomb_armor = 0 var/bomb_armor = 0
reac_volume = return_mob_expose_reac_volume(exposed_mob, methods, reac_volume, show_message, touch_protection, overmind) reac_volume = return_mob_expose_reac_volume(exposed_mob, methods, reac_volume, show_message, touch_protection, overmind)
if(reac_volume >= 10) // If it's not coming from a sporecloud, AOE 'explosion' damage if(reac_volume > 10) // If it's not coming from a sporecloud, AOE 'explosion' damage
var/epicenter_turf = get_turf(exposed_mob) var/epicenter_turf = get_turf(exposed_mob)
var/obj/effect/temp_visual/explosion/fast/ex_effect = new /obj/effect/temp_visual/explosion/fast(get_turf(exposed_mob)) var/obj/effect/temp_visual/explosion/fast/ex_effect = new /obj/effect/temp_visual/explosion/fast(get_turf(exposed_mob))
ex_effect.alpha = 150 ex_effect.alpha = 150

View File

@@ -4,6 +4,8 @@
/datum/blobstrain/multiplex/New(mob/eye/blob/new_overmind, list/blobstrains) /datum/blobstrain/multiplex/New(mob/eye/blob/new_overmind, list/blobstrains)
. = ..() . = ..()
if(!overmind)
return
for (var/bt in blobstrains) for (var/bt in blobstrains)
if (ispath(bt, /datum/blobstrain)) if (ispath(bt, /datum/blobstrain))
src.blobstrains += new bt(overmind) src.blobstrains += new bt(overmind)

View File

@@ -179,23 +179,12 @@ GLOBAL_LIST_EMPTY(blob_nodes)
/// Create a blob spore and link it to us /// Create a blob spore and link it to us
/mob/eye/blob/proc/create_spore(turf/spore_turf, spore_type = /mob/living/basic/blob_minion/spore/minion) /mob/eye/blob/proc/create_spore(turf/spore_turf, spore_type = /mob/living/basic/blob_minion/spore/minion)
var/mob/living/basic/blob_minion/spore/spore = new spore_type(spore_turf) var/mob/living/basic/blob_minion/spore/spore = new spore_type(spore_turf)
assume_direct_control(spore) spore.AddComponent(/datum/component/blob_minion, src)
return spore return spore
/// Give our new minion the properties of a minion
/mob/eye/blob/proc/assume_direct_control(mob/living/minion)
minion.AddComponent(/datum/component/blob_minion, src)
/// Add something to our list of mobs and wait for it to die /// Add something to our list of mobs and wait for it to die
/mob/eye/blob/proc/register_new_minion(mob/living/minion) /mob/eye/blob/proc/register_new_minion(mob/living/minion)
blob_mobs |= minion blob_mobs |= minion
if (!istype(minion, /mob/living/basic/blob_minion/blobbernaut))
RegisterSignal(minion, COMSIG_LIVING_DEATH, PROC_REF(on_minion_death))
/// When a spore (or zombie) dies then we do this
/mob/eye/blob/proc/on_minion_death(mob/living/spore)
SIGNAL_HANDLER
blobstrain.on_sporedeath(spore)
/mob/eye/blob/proc/victory() /mob/eye/blob/proc/victory()
sound_to_playing_players('sound/announcer/alarm/nuke_alarm.ogg', 70) sound_to_playing_players('sound/announcer/alarm/nuke_alarm.ogg', 70)

View File

@@ -217,17 +217,9 @@
return FALSE return FALSE
var/mob/living/basic/blob_minion/blobbernaut/minion/blobber = new(get_turf(factory)) var/mob/living/basic/blob_minion/blobbernaut/minion/blobber = new(get_turf(factory))
assume_direct_control(blobber) blobber.AddComponent(/datum/component/blob_minion, new_overmind = src, new_death_cloud_size = blobber.death_cloud_size)
factory.assign_blobbernaut(blobber) factory.assign_blobbernaut(blobber)
blobber.assign_key(ghost.key, blobstrain) blobber.assign_key(ghost.key, blobstrain)
RegisterSignal(blobber, COMSIG_HOSTILE_POST_ATTACKINGTARGET, PROC_REF(on_blobbernaut_attacked))
/// When one of our boys attacked something, we sometimes want to perform extra effects
/mob/eye/blob/proc/on_blobbernaut_attacked(mob/living/basic/blobbynaut, atom/target, success)
SIGNAL_HANDLER
if (!success)
return
blobstrain.blobbernaut_attack(target, blobbynaut)
/** Moves the core */ /** Moves the core */
/mob/eye/blob/proc/relocate_core() /mob/eye/blob/proc/relocate_core()

View File

@@ -327,3 +327,9 @@
required_reagents = list(/datum/reagent/consumable/grapejuice = 5) required_reagents = list(/datum/reagent/consumable/grapejuice = 5)
required_catalysts = list(/datum/reagent/consumable/enzyme = 5) required_catalysts = list(/datum/reagent/consumable/enzyme = 5)
mix_message = "The smell of the mixture reminds you of how you lost access to the country club..." mix_message = "The smell of the mixture reminds you of how you lost access to the country club..."
/datum/chemical_reaction/food/spore_detoxification
results = list(/datum/reagent/consumable/nutriment/vitamin = 1)
required_reagents = list(/datum/reagent/toxin/spore = 1, /datum/reagent/consumable/eggwhite = 0.5)
required_temp = 350
optimal_temp = 420

View File

@@ -341,3 +341,12 @@
Move(get_step(src, dir), dir) Move(get_step(src, dir), dir)
animate(src, pixel_y = 18, time = 0.4 SECONDS, flags = ANIMATION_RELATIVE, easing = CUBIC_EASING|EASE_OUT) animate(src, pixel_y = 18, time = 0.4 SECONDS, flags = ANIMATION_RELATIVE, easing = CUBIC_EASING|EASE_OUT)
animate(pixel_y = -18, time = 0.4 SECONDS, flags = ANIMATION_RELATIVE, easing = CUBIC_EASING|EASE_IN) animate(pixel_y = -18, time = 0.4 SECONDS, flags = ANIMATION_RELATIVE, easing = CUBIC_EASING|EASE_IN)
///ovverride to add mob specific cytology mutation effects, returns TRUE if we added a mob specific mutation
/mob/living/basic/proc/mutate()
if(SEND_SIGNAL(src, COMSIG_BASICMOB_MUTATED) & MUTATED_NO_FURTHER_MUTATIONS)
//Tells the vat our mob has been mutated by another source and we don't want to add potentially incompatible mutations such as shiny mutation.
return TRUE
else
return FALSE

View File

@@ -4,6 +4,7 @@
desc = "A nonfunctional fungal creature created by bad code or celestial mistake. Point and laugh." desc = "A nonfunctional fungal creature created by bad code or celestial mistake. Point and laugh."
icon = 'icons/mob/nonhuman-player/blob.dmi' icon = 'icons/mob/nonhuman-player/blob.dmi'
icon_state = "blob_head" icon_state = "blob_head"
base_icon_state = "blob_head"
unique_name = TRUE unique_name = TRUE
status_flags = CANPUSH status_flags = CANPUSH
pass_flags = PASSBLOB pass_flags = PASSBLOB
@@ -20,15 +21,24 @@
initial_language_holder = /datum/language_holder/empty initial_language_holder = /datum/language_holder/empty
can_buckle_to = FALSE can_buckle_to = FALSE
damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 1, STAMINA = 0, OXY = 1) damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 1, STAMINA = 0, OXY = 1)
/// Size of cloud produced from a dying spore
var/death_cloud_size = BLOBMOB_CLOUD_NONE
var/list/loot = list(/obj/item/food/spore_sack)
/mob/living/basic/blob_minion/Initialize(mapload) /mob/living/basic/blob_minion/Initialize(mapload)
. = ..() . = ..()
add_traits(list(TRAIT_BLOB_ALLY, TRAIT_MUTE), INNATE_TRAIT) add_traits(list(TRAIT_BLOB_ALLY, TRAIT_MUTE), INNATE_TRAIT)
AddComponent(/datum/component/blob_minion, on_strain_changed = CALLBACK(src, PROC_REF(on_strain_updated))) AddComponent(/datum/component/blob_minion, on_strain_changed = CALLBACK(src, PROC_REF(on_strain_updated)), new_death_cloud_size = death_cloud_size)
if(length(loot))
loot = string_list(loot)
AddElement(/datum/element/death_drops, loot)
/// Called when our blob overmind changes their variant, update some of our mob properties /// Called when our blob overmind changes their variant, update some of our mob properties
/mob/living/basic/blob_minion/proc/on_strain_updated(mob/eye/blob/overmind, datum/blobstrain/new_strain) /mob/living/basic/blob_minion/proc/on_strain_updated(mob/eye/blob/overmind, datum/blobstrain/new_strain)
return //revert independent blob mobs to the pale sprite so they can be recoloured
if(new_strain)
icon_state = base_icon_state
/// Associates this mob with a specific blob factory node /// Associates this mob with a specific blob factory node
/mob/living/basic/blob_minion/proc/link_to_factory(obj/structure/blob/special/factory/factory) /mob/living/basic/blob_minion/proc/link_to_factory(obj/structure/blob/special/factory/factory)

View File

@@ -6,6 +6,7 @@
desc = "A floating, fragile spore." desc = "A floating, fragile spore."
icon = 'icons/mob/nonhuman-player/blob.dmi' icon = 'icons/mob/nonhuman-player/blob.dmi'
icon_state = "blobpod" icon_state = "blobpod"
base_icon_state = "blobpod"
icon_living = "blobpod" icon_living = "blobpod"
health_doll_icon = "blobpod" health_doll_icon = "blobpod"
health = BLOBMOB_SPORE_HEALTH health = BLOBMOB_SPORE_HEALTH
@@ -16,7 +17,7 @@
verb_yell = "psychically screams" verb_yell = "psychically screams"
melee_damage_lower = BLOBMOB_SPORE_DMG_LOWER melee_damage_lower = BLOBMOB_SPORE_DMG_LOWER
melee_damage_upper = BLOBMOB_SPORE_DMG_UPPER melee_damage_upper = BLOBMOB_SPORE_DMG_UPPER
obj_damage = 0 obj_damage = 10
attack_verb_continuous = "batters" attack_verb_continuous = "batters"
attack_verb_simple = "batter" attack_verb_simple = "batter"
attack_sound = 'sound/items/weapons/genhit1.ogg' attack_sound = 'sound/items/weapons/genhit1.ogg'
@@ -24,28 +25,23 @@
gold_core_spawnable = HOSTILE_SPAWN gold_core_spawnable = HOSTILE_SPAWN
basic_mob_flags = DEL_ON_DEATH basic_mob_flags = DEL_ON_DEATH
ai_controller = /datum/ai_controller/basic_controller/blob_spore ai_controller = /datum/ai_controller/basic_controller/blob_spore
/// Size of cloud produced from a dying spore death_cloud_size = BLOBMOB_CLOUD_NORMAL
var/death_cloud_size = 1
/// Type of mob to create /// Type of mob to create
var/mob/living/zombie_type = /mob/living/basic/blob_minion/zombie var/mob/living/zombie_type = /mob/living/basic/blob_minion/zombie
/mob/living/basic/blob_minion/spore/Initialize(mapload) /mob/living/basic/blob_minion/spore/Initialize(mapload)
. = ..() . = ..()
AddElement(/datum/element/simple_flying) AddElement(/datum/element/simple_flying)
AddElement(/datum/element/swabable, CELL_LINE_TABLE_BLOBSPORE, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 5) AddElement(/datum/element/swabable, CELL_LINE_TABLE_BLOBSPORE, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 5)
ADD_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS, INNATE_TRAIT)
/mob/living/basic/blob_minion/spore/death(gibbed) /mob/living/basic/blob_minion/spore/death(gibbed)
. = ..() . = ..()
death_burst()
/mob/living/basic/blob_minion/spore/on_factory_destroyed() /mob/living/basic/blob_minion/spore/on_factory_destroyed()
death() death()
/// Create an explosion of spores on death
/mob/living/basic/blob_minion/spore/proc/death_burst()
do_chem_smoke(range = death_cloud_size, holder = src, location = get_turf(src), reagent_type = /datum/reagent/toxin/spore)
/mob/living/basic/blob_minion/spore/melee_attack(mob/living/carbon/human/target, list/modifiers, ignore_cooldown) /mob/living/basic/blob_minion/spore/melee_attack(mob/living/carbon/human/target, list/modifiers, ignore_cooldown)
. = ..() . = ..()
if (!ishuman(target) || target.stat != DEAD) if (!ishuman(target) || target.stat != DEAD)
@@ -104,21 +100,43 @@
else else
qdel(GetComponent(/datum/component/ghost_direct_control)) qdel(GetComponent(/datum/component/ghost_direct_control))
/mob/living/basic/blob_minion/spore/minion/death_burst()
return // This behaviour is superseded by the overmind's intervention
/// Weakened spore spawned by distributed neurons, can't zombify people and makes a teeny explosion /// Weakened spore spawned by distributed neurons, can't zombify people and makes a teeny explosion
/mob/living/basic/blob_minion/spore/minion/weak /mob/living/basic/blob_minion/spore/minion/weak
name = "fragile blob spore" name = "fragile blob spore"
health = 15 health = BLOBMOB_SPORE_HEALTH / 2
maxHealth = 15 maxHealth = BLOBMOB_SPORE_HEALTH / 2
melee_damage_lower = 1 melee_damage_lower = BLOBMOB_SPORE_DMG_LOWER / 2
melee_damage_upper = 2 melee_damage_upper = BLOBMOB_SPORE_DMG_UPPER / 2
death_cloud_size = 0 death_cloud_size = BLOBMOB_CLOUD_SMALL
obj_damage = 0
/mob/living/basic/blob_minion/spore/minion/weak/zombify() /mob/living/basic/blob_minion/spore/minion/weak/zombify()
return return
/mob/living/basic/blob_minion/spore/minion/weak/on_strain_updated() /mob/living/basic/blob_minion/spore/minion/weak/on_strain_updated()
return return
/// independent spore spawned by cytology, extremely weak and shitty like all spores but exhibits a high degree of sentience in addition to the predatory nature of inherent to blob creatures.
/mob/living/basic/blob_minion/spore/independent
//We are on our own and get to enjoy the classic orange look, which frankly, many people are saying is the best!
//If I had removed it they'd all be messaging me, people like you wouldn't believe, tough, real tough people, they'd be messaging me with tears in their eyes; "Sir, sir please bring it back!"
icon_state = "blobpod_independent"
//we hate gold cores
gold_core_spawnable = NO_SPAWN
loot = list(/obj/item/food/spore_sack/independent)
/mob/living/basic/blob_minion/spore/independent/Initialize(mapload)
. = ..()
//free but incredibly shitty antag. Good job hazard to add some friction to gathering spore toxin.
AddComponent(\
/datum/component/ghost_direct_control,\
ban_type = ROLE_FREE_BLOB,\
poll_candidates = TRUE,\
poll_ignore_key = POLL_IGNORE_FREE_SPORE,\
after_assumed_control = CALLBACK(src, PROC_REF(on_assumed_control)),\
)
/mob/living/basic/blob_minion/spore/independent/proc/on_assumed_control()
to_chat(src, span_blobannounce("You are a spore born free from the shackles of an overmind.\n\nHowever this strange predicament has not muted the hostility you feel towards creatures that are not your kin, this base instinct appears to be a part of your true self."))
SEND_SOUND(src, sound('sound/music/antag/blobalert.ogg', volume = 50))

View File

@@ -23,8 +23,11 @@
gold_core_spawnable = NO_SPAWN gold_core_spawnable = NO_SPAWN
basic_mob_flags = DEL_ON_DEATH basic_mob_flags = DEL_ON_DEATH
ai_controller = /datum/ai_controller/basic_controller/blob_zombie ai_controller = /datum/ai_controller/basic_controller/blob_zombie
death_cloud_size = BLOBMOB_CLOUD_NORMAL
/// The dead body we have inside /// The dead body we have inside
var/mob/living/carbon/human/corpse var/mob/living/carbon/human/corpse
///Our head overlay
var/mutable_appearance/blob_head_overlay
/mob/living/basic/blob_minion/zombie/Initialize(mapload) /mob/living/basic/blob_minion/zombie/Initialize(mapload)
. = ..() . = ..()
@@ -33,7 +36,6 @@
/mob/living/basic/blob_minion/zombie/death(gibbed) /mob/living/basic/blob_minion/zombie/death(gibbed)
corpse?.forceMove(loc) corpse?.forceMove(loc)
death_burst()
return ..() return ..()
/mob/living/basic/blob_minion/zombie/Exited(atom/movable/gone, direction) /mob/living/basic/blob_minion/zombie/Exited(atom/movable/gone, direction)
@@ -51,17 +53,18 @@
. = ..() . = ..()
death() death()
//Sets up our appearance /mob/living/basic/blob_minion/zombie/on_strain_updated(mob/eye/blob/overmind, datum/blobstrain/new_strain)
/mob/living/basic/blob_minion/zombie/proc/set_up_zombie_appearance() . = ..()
copy_overlays(corpse, TRUE) update_appearance()
var/mutable_appearance/blob_head_overlay = mutable_appearance('icons/mob/nonhuman-player/blob.dmi', "blob_head") color = initial(color)
blob_head_overlay.color = LAZYACCESS(atom_colours, FIXED_COLOUR_PRIORITY) || COLOR_WHITE blob_head_overlay?.icon_state = "blob_head"
color = initial(color) // reversing what our component did lol, but we needed the value for the overlay blob_head_overlay?.color = new_strain ? new_strain.color : COLOR_WHITE
overlays += blob_head_overlay
/// Create an explosion of spores on death /mob/living/basic/blob_minion/zombie/update_overlays()
/mob/living/basic/blob_minion/zombie/proc/death_burst() . = ..()
do_chem_smoke(range = 0, holder = src, location = get_turf(src), reagent_type = /datum/reagent/toxin/spore) if(!blob_head_overlay)
blob_head_overlay = mutable_appearance('icons/mob/nonhuman-player/blob.dmi', "blob_head_independent")
. += blob_head_overlay
/// Store a body so that we can drop it on death /// Store a body so that we can drop it on death
/mob/living/basic/blob_minion/zombie/proc/consume_corpse(mob/living/carbon/human/new_corpse) /mob/living/basic/blob_minion/zombie/proc/consume_corpse(mob/living/carbon/human/new_corpse)
@@ -72,8 +75,8 @@
new_corpse.set_hairstyle("Bald", update = TRUE) new_corpse.set_hairstyle("Bald", update = TRUE)
new_corpse.forceMove(src) new_corpse.forceMove(src)
corpse = new_corpse corpse = new_corpse
copy_overlays(corpse, TRUE)
update_appearance(UPDATE_ICON) update_appearance(UPDATE_ICON)
set_up_zombie_appearance()
RegisterSignal(corpse, COMSIG_LIVING_REVIVE, PROC_REF(on_corpse_revived)) RegisterSignal(corpse, COMSIG_LIVING_REVIVE, PROC_REF(on_corpse_revived))
/// Dynamic changeling reentry /// Dynamic changeling reentry
@@ -95,6 +98,3 @@
poll_candidates = TRUE,\ poll_candidates = TRUE,\
poll_ignore_key = POLL_IGNORE_BLOB,\ poll_ignore_key = POLL_IGNORE_BLOB,\
) )
/mob/living/basic/blob_minion/zombie/controlled/death_burst()
return

View File

@@ -6,6 +6,7 @@
name = "blobbernaut" name = "blobbernaut"
desc = "A hulking, mobile chunk of blobmass." desc = "A hulking, mobile chunk of blobmass."
icon_state = "blobbernaut" icon_state = "blobbernaut"
base_icon_state = "blobbernaut"
icon_living = "blobbernaut" icon_living = "blobbernaut"
icon_dead = "blobbernaut_dead" icon_dead = "blobbernaut_dead"
health = BLOBMOB_BLOBBERNAUT_HEALTH health = BLOBMOB_BLOBBERNAUT_HEALTH
@@ -24,23 +25,43 @@
verb_yell = "bellows" verb_yell = "bellows"
pressure_resistance = 50 pressure_resistance = 50
mob_size = MOB_SIZE_LARGE mob_size = MOB_SIZE_LARGE
gold_core_spawnable = HOSTILE_SPAWN
ai_controller = /datum/ai_controller/basic_controller/blobbernaut ai_controller = /datum/ai_controller/basic_controller/blobbernaut
loot = list()
///The HUD given to blobbernauts, updated by the Blob itself ///The HUD given to blobbernauts, updated by the Blob itself
var/atom/movable/screen/healths/blob/overmind/overmind_hud var/atom/movable/screen/healths/blob/overmind/overmind_hud
///The overlay for veins.
var/mutable_appearance/vein_overlay
///The overlay for our eyes
var/mutable_appearance/eyes_overlay
///emissive eyes
var/static/mutable_appearance/eyes_emissive
/mob/living/basic/blob_minion/blobbernaut/Initialize(mapload) /mob/living/basic/blob_minion/blobbernaut/Initialize(mapload)
. = ..() . = ..()
AddElement(/datum/element/swabable, CELL_LINE_TABLE_BLOBBERNAUT, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 5) AddElement(/datum/element/swabable, CELL_LINE_TABLE_BLOBBERNAUT, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 5)
AddElement(/datum/element/damage_threshold, 10) AddElement(/datum/element/damage_threshold, 10)
var/static/list/food_types = list(
/obj/item/food/egg,
/obj/item/food/rawegg,
/obj/item/food/friedegg,
/obj/item/food/boiledegg,
/obj/item/flashlight/flare,
/obj/item/reagent_containers/cup/soda_cans/shamblers,
)
AddComponent(/datum/component/tameable, food_types = food_types, tame_chance = 25, bonus_tame_chance = 15)
update_appearance()
/mob/living/basic/blob_minion/blobbernaut/Destroy() /mob/living/basic/blob_minion/blobbernaut/Destroy()
QDEL_NULL(overmind_hud) QDEL_NULL(overmind_hud)
return ..() return ..()
/mob/living/basic/blob_minion/blobbernaut/death(gibbed) /mob/living/basic/blob_minion/blobbernaut/death(gibbed)
flick("blobbernaut_death", src) flick("[icon_state]_death", src)
playsound(src, 'sound/mobs/non-humanoids/blobmob/blobbernaut_death.ogg', 100, TRUE)
update_overlays()
return ..() return ..()
/mob/living/basic/blob_minion/blobbernaut/create_mob_hud() /mob/living/basic/blob_minion/blobbernaut/create_mob_hud()
@@ -51,9 +72,48 @@
hud_used.infodisplay += overmind_hud hud_used.infodisplay += overmind_hud
hud_used.show_hud(hud_used.hud_version) hud_used.show_hud(hud_used.hud_version)
/mob/living/basic/blob_minion/blobbernaut/on_strain_updated(mob/eye/blob/overmind, datum/blobstrain/new_strain)
. = ..()
if(new_strain)
attack_verb_continuous = new_strain.blobbernaut_message
melee_damage_upper = BLOBMOB_BLOBBERNAUT_DMG_UPPER
melee_damage_lower = BLOBMOB_BLOBBERNAUT_DMG_LOWER
vein_overlay?.color = new_strain.complementary_color
eyes_overlay?.color = new_strain.complementary_color
//revert independent blobbernauts to the pale sprite so they can be recoloured
icon_dead = "[base_icon_state]_dead"
icon_living = base_icon_state
new_strain.RegisterSignal(src, COMSIG_HOSTILE_POST_ATTACKINGTARGET, TYPE_PROC_REF(/datum/blobstrain/, blobbernaut_attack))
else
attack_verb_continuous = initial(attack_verb_continuous)
melee_damage_upper = BLOBMOB_BLOBBERNAUT_DMG_SOLO_UPPER
melee_damage_lower = BLOBMOB_BLOBBERNAUT_DMG_SOLO_LOWER
//Our overmind has died and our veins turns a mournful amethyst to complement our pale strainless body.
vein_overlay?.color = "#7d6eb4"
eyes_overlay?.color = COLOR_WHITE
update_appearance()
/mob/living/basic/blob_minion/blobbernaut/update_overlays()
. = ..()
if(!vein_overlay)
vein_overlay = mutable_appearance(icon, "[base_icon_state]_veins", appearance_flags = RESET_COLOR | KEEP_APART)
///Give it independent olive green veins until strain modifies it
vein_overlay.color = COLOR_OLIVE_GREEN
if(!eyes_overlay)
eyes_overlay = mutable_appearance(icon, "[base_icon_state]_eyes", appearance_flags = RESET_COLOR | KEEP_APART)
eyes_overlay.color = "#ffc90e" //blobber eye yellow
if(!eyes_emissive)
eyes_emissive = emissive_appearance(icon, "[base_icon_state]_eyes", src)
if(stat != DEAD)
. += vein_overlay
. += eyes_overlay
. += eyes_emissive
/// This variant is the one actually spawned by blob factories, takes damage when away from blob tiles /// This variant is the one actually spawned by blob factories, takes damage when away from blob tiles
/mob/living/basic/blob_minion/blobbernaut/minion /mob/living/basic/blob_minion/blobbernaut/minion
gold_core_spawnable = NO_SPAWN
/// Is our factory dead? /// Is our factory dead?
var/orphaned = FALSE var/orphaned = FALSE
@@ -64,13 +124,13 @@
var/damage_sources = 0 var/damage_sources = 0
var/list/blobs_in_area = range(2, src) var/list/blobs_in_area = range(2, src)
if (!(locate(/obj/structure/blob) in blobs_in_area)) if(!(locate(/obj/structure/blob) in blobs_in_area))
damage_sources++ damage_sources++
if (orphaned) if (orphaned)
damage_sources++ damage_sources++
else else
var/particle_colour = atom_colours[FIXED_COLOUR_PRIORITY] || COLOR_BLACK var/particle_colour = atom_colours?[FIXED_COLOUR_PRIORITY] || COLOR_BLACK
if (locate(/obj/structure/blob/special/core) in blobs_in_area) if (locate(/obj/structure/blob/special/core) in blobs_in_area)
heal_overall_damage(maxHealth * BLOBMOB_BLOBBERNAUT_HEALING_CORE * seconds_per_tick) heal_overall_damage(maxHealth * BLOBMOB_BLOBBERNAUT_HEALING_CORE * seconds_per_tick)
var/obj/effect/temp_visual/heal/heal_effect = new /obj/effect/temp_visual/heal(get_turf(src)) var/obj/effect/temp_visual/heal/heal_effect = new /obj/effect/temp_visual/heal(get_turf(src))
@@ -86,10 +146,27 @@
// take 2.5% of max health as damage when not near the blob or if the naut has no factory, 5% if both // take 2.5% of max health as damage when not near the blob or if the naut has no factory, 5% if both
apply_damage(maxHealth * BLOBMOB_BLOBBERNAUT_HEALTH_DECAY * damage_sources * seconds_per_tick, damagetype = TOX) // We reduce brute damage apply_damage(maxHealth * BLOBMOB_BLOBBERNAUT_HEALTH_DECAY * damage_sources * seconds_per_tick, damagetype = TOX) // We reduce brute damage
var/mutable_appearance/harming = mutable_appearance('icons/mob/nonhuman-player/blob.dmi', "nautdamage", MOB_LAYER + 0.01, appearance_flags = RESET_COLOR|KEEP_APART)
harming = color_atom_overlay(harming) //hopefully this sound won't get too annoying.
harming.dir = dir if(prob(20))
flick_overlay_view(harming, 0.8 SECONDS) playsound(src, 'sound/items/weapons/sear.ogg', 5, vary = TRUE)
//create aooearances for the naut damage effect
var/mutable_appearance/naut_damage_overlay = mutable_appearance(icon, "[base_icon_state]_veins", appearance_flags = RESET_COLOR | KEEP_APART)
//modify appearances to intitially have no effect
naut_damage_overlay.color = vein_overlay.color
//flick naut damage overlays
var/atom/movable/flick_visual/naut_damage_animation = flick_overlay_view(naut_damage_overlay, seconds_per_tick)
//make flick objects obey dirs
naut_damage_animation.vis_flags |= VIS_INHERIT_DIR
//animate the naut damage overlays to fade in the 180 vein hue shift and emsissive.
animate(naut_damage_animation, time = seconds_per_tick / 2, easing = SINE_EASING, color = RotateHue(vein_overlay.color, 180))
animate(time = seconds_per_tick / 2, easing = SINE_EASING, color = vein_overlay.color)
return TRUE return TRUE
/// Called by the blob creation power to give us a mind and a basic task orientation /// Called by the blob creation power to give us a mind and a basic task orientation
@@ -104,19 +181,14 @@
to_chat(src, span_infoplain("Your overmind's blob reagent is: <b><font color=\"[blobstrain.color]\">[blobstrain.name]</b></font>!")) to_chat(src, span_infoplain("Your overmind's blob reagent is: <b><font color=\"[blobstrain.color]\">[blobstrain.name]</b></font>!"))
to_chat(src, span_infoplain("The <b><font color=\"[blobstrain.color]\">[blobstrain.name]</b></font> reagent [blobstrain.shortdesc ? "[blobstrain.shortdesc]" : "[blobstrain.description]"]")) to_chat(src, span_infoplain("The <b><font color=\"[blobstrain.color]\">[blobstrain.name]</b></font> reagent [blobstrain.shortdesc ? "[blobstrain.shortdesc]" : "[blobstrain.description]"]"))
/// Set our attack damage based on blob's properties
/mob/living/basic/blob_minion/blobbernaut/minion/on_strain_updated(mob/eye/blob/overmind, datum/blobstrain/new_strain)
if (isnull(overmind))
melee_damage_lower = initial(melee_damage_lower)
melee_damage_upper = initial(melee_damage_upper)
attack_verb_continuous = initial(attack_verb_continuous)
return
melee_damage_lower = BLOBMOB_BLOBBERNAUT_DMG_LOWER
melee_damage_upper = BLOBMOB_BLOBBERNAUT_DMG_UPPER
attack_verb_continuous = new_strain.blobbernaut_message
/// Called by our factory to inform us that it's not going to support us financially any more /// Called by our factory to inform us that it's not going to support us financially any more
/mob/living/basic/blob_minion/blobbernaut/minion/on_factory_destroyed() /mob/living/basic/blob_minion/blobbernaut/minion/on_factory_destroyed()
. = ..() . = ..()
orphaned = TRUE orphaned = TRUE
throw_alert("nofactory", /atom/movable/screen/alert/nofactory) throw_alert("nofactory", /atom/movable/screen/alert/nofactory)
///brand new orange look to match the classic spore.
/mob/living/basic/blob_minion/blobbernaut/independent
icon_state = "blobbernaut_independent"
icon_living = "blobbernaut_independent"
icon_dead = "blobbernaut_independent_dead"

View File

@@ -446,8 +446,29 @@
color = "#9ACD32" color = "#9ACD32"
toxpwr = 1 toxpwr = 1
ph = 11 ph = 11
liver_damage_multiplier = 0.7
taste_description = "spores"
chemical_flags = REAGENT_CAN_BE_SYNTHESIZED|REAGENT_NO_RANDOM_RECIPE chemical_flags = REAGENT_CAN_BE_SYNTHESIZED|REAGENT_NO_RANDOM_RECIPE
/datum/reagent/toxin/spore/on_transfer(atom/A, methods, trans_volume)
. = ..()
if(!isliving(A))
return
if(!(methods & INHALE))
return
var/mob/living/spore_lung_victim = A
if(!(spore_lung_victim.mob_biotypes & (MOB_HUMANOID | MOB_BEAST)))
return
if(prob(min(trans_volume * 10, 80)))
to_chat(spore_lung_victim, span_danger("[pick("You have a coughing fit!", "You hack and cough!", "Your lungs burn!")]"))
spore_lung_victim.Stun(1 SECONDS)
spore_lung_victim.emote("cough")
/datum/reagent/toxin/spore/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired) /datum/reagent/toxin/spore/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired)
. = ..() . = ..()
affected_mob.damageoverlaytemp = 60 affected_mob.damageoverlaytemp = 60

View File

@@ -106,6 +106,11 @@
vat.visible_message(span_nicegreen("[thing] pops out of [vat]!")) vat.visible_message(span_nicegreen("[thing] pops out of [vat]!"))
//We maybe add some color. the chance is static for now, but idewally we would be able to manipulate it in the future. //We maybe add some color. the chance is static for now, but idewally we would be able to manipulate it in the future.
if(prob(CYTO_SHINY_CHANCE)) if(prob(CYTO_SHINY_CHANCE))
if(isbasicmob(thing))
var/mob/living/basic/vat_creature = thing
//if the mob has a special mutation interaction don't do any other mutations. Once we add more mutations that are stackable with shiny we should probably roll for each type independently.
if(vat_creature.mutate())
return
mutate_color(thing) mutate_color(thing)
///Overriden to show more info like needs, supplementary and supressive reagents and also growth. ///Overriden to show more info like needs, supplementary and supressive reagents and also growth.

View File

@@ -254,18 +254,20 @@
required_reagents = list(/datum/reagent/consumable/nutriment/protein) required_reagents = list(/datum/reagent/consumable/nutriment/protein)
supplementary_reagents = list( supplementary_reagents = list(
/datum/reagent/consumable/nutriment/vitamin = 3, /datum/reagent/consumable/nutriment/vitamin = 2,
/datum/reagent/consumable/liquidgibs = 2, /datum/reagent/consumable/eggrot = 2,
/datum/reagent/sulfur = 2) /datum/reagent/medicine/c2/synthflesh = 1,
/datum/reagent/consumable/liquidgibs = 1,
/datum/reagent/sulfur = 1)
suppressive_reagents = list( suppressive_reagents = list(
/datum/reagent/consumable/tinlux = -6, /datum/reagent/consumable/tinlux = -6,
/datum/reagent/lead = -4, //neurotoxin, works because spores are smort
/datum/reagent/napalm = -4, /datum/reagent/napalm = -4,
/datum/reagent/medicine/psicodine = -2) //Blob zombies likely wouldn't appreciate psicodine so why this is here /datum/reagent/medicine/psicodine = -2) //Blob zombies likely wouldn't appreciate psicodine so why this is here
virus_suspectibility = 0 virus_suspectibility = 0
resulting_atom = /mob/living/basic/blob_minion/spore resulting_atom = /mob/living/basic/blob_minion/spore/independent
resulting_atom_count = 2
/datum/micro_organism/cell_line/blobbernaut /datum/micro_organism/cell_line/blobbernaut
desc = "Blobular myocytes" desc = "Blobular myocytes"
@@ -284,7 +286,7 @@
suppressive_reagents = list(/datum/reagent/consumable/tinlux = -6) suppressive_reagents = list(/datum/reagent/consumable/tinlux = -6)
virus_suspectibility = 0 virus_suspectibility = 0
resulting_atom = /mob/living/basic/blob_minion/blobbernaut resulting_atom = /mob/living/basic/blob_minion/blobbernaut/independent
/datum/micro_organism/cell_line/gelatinous_cube /datum/micro_organism/cell_line/gelatinous_cube
desc = "Cubic ooze particles" desc = "Cubic ooze particles"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Binary file not shown.