From 2c12e8725692e86b9f5a9770fe4e9ddfb8ea9da4 Mon Sep 17 00:00:00 2001 From: NotRanged Date: Tue, 2 Feb 2021 18:20:28 +0100 Subject: [PATCH] Cleans up blob code, removes a lot of hardcoded stuff, many new defines (#56306) Cleaned up, commented on, and (hopefully) improves a lot of blob code. Put pretty much everything balance-related (except for the strains themselves) into a define file, for easier viewing, changing, and balancing. Added a bunch of new functionality that new strains (some of which I plan to add in the future) could use: from increased expansion range to more spores per factory. --- code/__DEFINES/antagonists.dm | 12 -- code/__DEFINES/blob_defines.dm | 107 ++++++++++++ code/_onclick/hud/blob_overmind.dm | 63 +++++-- code/modules/antagonists/blob/blob.dm | 5 +- code/modules/antagonists/blob/blob_mobs.dm | 38 ++--- .../blob/blobstrains/_blobstrain.dm | 128 ++++++++++++-- .../antagonists/blob/blobstrains/_reagent.dm | 6 +- .../blob/blobstrains/blazing_oil.dm | 1 + .../blob/blobstrains/debris_devourer.dm | 8 +- .../blob/blobstrains/electromagnetic_web.dm | 10 +- .../blob/blobstrains/networked_fibers.dm | 4 +- .../blob/blobstrains/regenerative_materia.dm | 4 +- .../blob/blobstrains/synchronous_mesh.dm | 4 +- code/modules/antagonists/blob/overmind.dm | 33 ++-- code/modules/antagonists/blob/powers.dm | 141 +++------------- .../antagonists/blob/structures/_blob.dm | 159 ++++++++++++------ .../antagonists/blob/structures/core.dm | 37 ++-- .../antagonists/blob/structures/factory.dm | 41 ++--- .../antagonists/blob/structures/node.dm | 33 ++-- .../antagonists/blob/structures/resource.dm | 16 +- .../antagonists/blob/structures/shield.dm | 14 +- code/modules/mob/transform_procs.dm | 2 +- tgstation.dme | 1 + 23 files changed, 542 insertions(+), 325 deletions(-) create mode 100644 code/__DEFINES/blob_defines.dm diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm index 0906738b673..6041844b66f 100644 --- a/code/__DEFINES/antagonists.dm +++ b/code/__DEFINES/antagonists.dm @@ -24,16 +24,6 @@ #define APPRENTICE_ROBELESS "robeless" #define APPRENTICE_HEALING "healing" - -//Blob -/// blob gets a free reroll every X time -#define BLOB_REROLL_TIME 2400 -#define BLOB_SPREAD_COST 4 -/// blob refunds this much if it attacks and doesn't spread -#define BLOB_ATTACK_REFUND 2 -#define BLOB_REFLECTOR_COST 15 - - //ERT Types #define ERT_BLUE "Blue" #define ERT_RED "Red" @@ -85,8 +75,6 @@ GLOBAL_LIST_INIT(heretic_start_knowledge,list(/datum/eldritch_knowledge/spell/ba #define PATH_FLESH "Flesh" #define PATH_VOID "Void" -/// How much does it cost to reroll strains? -#define BLOB_REROLL_COST 40 /// How many telecrystals a normal traitor starts with #define TELECRYSTALS_DEFAULT 20 diff --git a/code/__DEFINES/blob_defines.dm b/code/__DEFINES/blob_defines.dm new file mode 100644 index 00000000000..7cfec0e7d05 --- /dev/null +++ b/code/__DEFINES/blob_defines.dm @@ -0,0 +1,107 @@ +// Overmind defines + +#define OVERMIND_MAX_POINTS_DEFAULT 100 // Max point storage +#define OVERMIND_STARTING_POINTS 60 // Points granted upon start +#define OVERMIND_STARTING_REROLLS 1 // Free strain rerolls at the start +#define OVERMIND_STARTING_MIN_PLACE_TIME 1 MINUTES // Minimum time before the core can be placed +#define OVERMIND_STARTING_AUTO_PLACE_TIME 6 MINUTES // After this time, randomly place the core somewhere viable +#define OVERMIND_WIN_CONDITION_AMOUNT 400 // Blob structures required to win +#define OVERMIND_ANNOUNCEMENT_MIN_SIZE 75 // Once the blob has this many structures, announce their presence +#define OVERMIND_ANNOUNCEMENT_MAX_TIME 10 MINUTES // If the blob hasn't reached the minimum size before this time, announce their presence +#define OVERMIND_MAX_CAMERA_STRAY "3x3" // How far the overmind camera is allowed to stray from blob tiles. 3x3 is 1 tile away, 5x5 2 tiles etc + + +// Generic blob defines + +#define BLOB_BASE_POINT_RATE 2 // Base amount of points per process() +#define BLOB_EXPAND_COST 4 // Price to expand onto a new tile +#define BLOB_ATTACK_REFUND 2 // Points 'refunded' when the expand attempt actually attacks something instead +#define BLOB_BRUTE_RESIST 0.5 // Brute damage taken gets multiplied by this value +#define BLOB_FIRE_RESIST 1 // Burn damage taken gets multiplied by this value +#define BLOB_EXPAND_CHANCE_MULTIPLIER 1 // Increase this value to make blobs naturally expand faster +#define BLOB_REINFORCE_CHANCE 2.5 // The delta_time chance for cores/nodes to reinforce their surroundings +#define BLOB_REAGENTATK_VOL 25 // Amount of strain-reagents that get injected when the blob attacks: main source of blob damage + + +// Structure properties + +#define BLOB_CORE_MAX_HP 400 +#define BLOB_CORE_HP_REGEN 2 // Bases health regeneration rate every process(), can be added on by strains +#define BLOB_CORE_CLAIM_RANGE 12 // Range in which blob tiles are 'claimed' (converted from dead to alive, rarely useful) +#define BLOB_CORE_PULSE_RANGE 4 // The radius up to which the core activates structures, and up to which structures can be built +#define BLOB_CORE_EXPAND_RANGE 3 // Radius of automatic expansion +#define BLOB_CORE_STRONG_REINFORCE_RANGE 1 // The radius of tiles surrounding the core that get upgraded +#define BLOB_CORE_REFLECTOR_REINFORCE_RANGE 0 +#define BLOB_CORE_MAX_SPORES 0 // Spores that the core can produce for free + +#define BLOB_NODE_MAX_HP 200 +#define BLOB_NODE_HP_REGEN 3 +#define BLOB_NODE_MIN_DISTANCE 5 // Minimum distance between nodes +#define BLOB_NODE_CLAIM_RANGE 10 +#define BLOB_NODE_PULSE_RANGE 3 // The radius up to which the core activates structures, and up to which structures can be built +#define BLOB_NODE_EXPAND_RANGE 2 // Radius of automatic expansion +#define BLOB_NODE_STRONG_REINFORCE_RANGE 0 // The radius of tiles surrounding the node that get upgraded +#define BLOB_NODE_REFLECTOR_REINFORCE_RANGE 0 +#define BLOB_NODE_MAX_SPORES 0 // Spores that nodes can maintain + +#define BLOB_FACTORY_MAX_HP 200 +#define BLOB_FACTORY_HP_REGEN 1 +#define BLOB_FACTORY_MIN_DISTANCE 7 // Minimum distance between factories +#define BLOB_FACTORY_MAX_SPORES 3 + +#define BLOB_RESOURCE_MAX_HP 60 +#define BLOB_RESOURCE_HP_REGEN 15 +#define BLOB_RESOURCE_MIN_DISTANCE 4 // Minimum distance between resource blobs +#define BLOB_RESOURCE_GATHER_DELAY 4 SECONDS // Gather points when pulsed outside this interval +#define BLOB_RESOURCE_GATHER_ADDED_DELAY 0.25 SECONDS// Every additional resource blob adds this amount to the gather delay +#define BLOB_RESOURCE_GATHER_AMOUNT 1 // The amount of points added to the overmind + +#define BLOB_REGULAR_MAX_HP 30 +#define BLOB_REGULAR_HP_REGEN 1 // Health regenerated when pulsed by a node/core + +#define BLOB_STRONG_MAX_HP 150 +#define BLOB_STRONG_HP_REGEN 2 + +#define BLOB_REFLECTOR_MAX_HP 150 +#define BLOB_REFLECTOR_HP_REGEN 2 + + +// Structure purchasing + +#define BLOB_UPGRADE_STRONG_COST 15 // Upgrade and build costs here +#define BLOB_UPGRADE_REFLECTOR_COST 15 +#define BLOB_STRUCTURE_RESOURCE_COST 40 +#define BLOB_STRUCTURE_FACTORY_COST 60 +#define BLOB_STRUCTURE_NODE_COST 50 + +#define BLOB_REFUND_STRONG_COST 4 // Points refunded when destroying the structure +#define BLOB_REFUND_REFLECTOR_COST 8 +#define BLOB_REFUND_FACTORY_COST 25 +#define BLOB_REFUND_NODE_COST 25 + +// Blob power properties + +#define BLOB_POWER_RELOCATE_COST 80 // Resource cost to move your core to a different node +#define BLOB_POWER_REROLL_COST 40 // Strain reroll +#define BLOB_POWER_REROLL_FREE_TIME 4 MINUTES // Gain a free strain reroll every x minutes +#define BLOB_POWER_REROLL_CHOICES 6 // Possibilities to choose from; keep in mind increasing this might fuck with the radial menu + + +// Mob defines + +#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_SPAWN_COOLDOWN 8 SECONDS +#define BLOBMOB_SPORE_DMG_LOWER 2 +#define BLOBMOB_SPORE_DMG_UPPER 4 +#define BLOBMOB_BLOBBERNAUT_RESOURCE_COST 40 // Purchase price for making a blobbernaut +#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_UPPER 20 +#define BLOBMOB_BLOBBERNAUT_DMG_LOWER 4 // Damage dealt with active overmind (most damage comes from strain chems) +#define BLOBMOB_BLOBBERNAUT_DMG_UPPER 4 +#define BLOBMOB_BLOBBERNAUT_REAGENTATK_VOL 20 // Amounts of strain reagents applied on attack -- basically the main damage stat +#define BLOBMOB_BLOBBERNAUT_DMG_OBJ 60 // Damage dealth to objects/machines +#define BLOBMOB_BLOBBERNAUT_HEALING_CORE 0.1 // Percentage multiplier HP restored on Life() when within 2 tiles of the blob core +#define BLOBMOB_BLOBBERNAUT_HEALING_NODE 0.05 // Same, but for a nearby node +#define BLOBMOB_BLOBBERNAUT_HEALTH_DECAY 0.025 // Percentage multiplier HP lost when not near blob tiles or without factory diff --git a/code/_onclick/hud/blob_overmind.dm b/code/_onclick/hud/blob_overmind.dm index bb4936fc549..110cabcc746 100644 --- a/code/_onclick/hud/blob_overmind.dm +++ b/code/_onclick/hud/blob_overmind.dm @@ -53,8 +53,14 @@ /atom/movable/screen/blob/blobbernaut icon_state = "ui_blobbernaut" - name = "Produce Blobbernaut (40)" - desc = "Produces a strong, smart blobbernaut from a factory blob for 40 resources.
The factory blob used will become fragile and unable to produce spores." + // Name and description get given their proper values on Initialize() + name = "Produce Blobbernaut (ERROR)" + desc = "Produces a strong, smart blobbernaut from a factory blob for (ERROR) resources.
The factory blob used will become fragile and unable to produce spores." + +/atom/movable/screen/blob/blobbernaut/Initialize() + . = ..() + name = "Produce Blobbernaut ([BLOBMOB_BLOBBERNAUT_RESOURCE_COST])" + desc = "Produces a strong, smart blobbernaut from a factory blob for [BLOBMOB_BLOBBERNAUT_RESOURCE_COST] resources.
The factory blob used will become fragile and unable to produce spores." /atom/movable/screen/blob/blobbernaut/Click() if(isovermind(usr)) @@ -63,38 +69,57 @@ /atom/movable/screen/blob/resource_blob icon_state = "ui_resource" - name = "Produce Resource Blob (40)" - desc = "Produces a resource blob for 40 resources.
Resource blobs will give you resources every few seconds." + // Name and description get given their proper values on Initialize() + name = "Produce Resource Blob (ERROR)" + desc = "Produces a resource blob for ERROR resources.
Resource blobs will give you resources every few seconds." + +/atom/movable/screen/blob/resource_blob/Initialize() + . = ..() + name = "Produce Resource Blob ([BLOB_STRUCTURE_RESOURCE_COST])" + desc = "Produces a resource blob for [BLOB_STRUCTURE_RESOURCE_COST] resources.
Resource blobs will give you resources every few seconds." /atom/movable/screen/blob/resource_blob/Click() if(isovermind(usr)) var/mob/camera/blob/B = usr - B.create_resource() + B.createSpecial(BLOB_STRUCTURE_RESOURCE_COST, /obj/structure/blob/special/resource, BLOB_RESOURCE_MIN_DISTANCE, TRUE) /atom/movable/screen/blob/node_blob icon_state = "ui_node" - name = "Produce Node Blob (50)" - desc = "Produces a node blob for 50 resources.
Node blobs will expand and activate nearby resource and factory blobs." + // Name and description get given their proper values on Initialize() + name = "Produce Node Blob (ERROR)" + desc = "Produces a node blob for ERROR resources.
Node blobs will expand and activate nearby resource and factory blobs." + +/atom/movable/screen/blob/node_blob/Initialize() + . = ..() + name = "Produce Node Blob ([BLOB_STRUCTURE_NODE_COST])" + desc = "Produces a node blob for [BLOB_STRUCTURE_NODE_COST] resources.
Node blobs will expand and activate nearby resource and factory blobs." /atom/movable/screen/blob/node_blob/Click() if(isovermind(usr)) var/mob/camera/blob/B = usr - B.create_node() + B.createSpecial(BLOB_STRUCTURE_NODE_COST, /obj/structure/blob/special/node, BLOB_NODE_MIN_DISTANCE, FALSE) /atom/movable/screen/blob/factory_blob icon_state = "ui_factory" - name = "Produce Factory Blob (60)" - desc = "Produces a factory blob for 60 resources.
Factory blobs will produce spores every few seconds." + // Name and description get given their proper values on Initialize() + name = "Produce Factory Blob (ERROR)" + desc = "Produces a factory blob for ERROR resources.
Factory blobs will produce spores every few seconds." + +/atom/movable/screen/blob/factory_blob/Initialize() + . = ..() + name = "Produce Factory Blob ([BLOB_STRUCTURE_FACTORY_COST])" + desc = "Produces a factory blob for [BLOB_STRUCTURE_FACTORY_COST] resources.
Factory blobs will produce spores every few seconds." /atom/movable/screen/blob/factory_blob/Click() if(isovermind(usr)) var/mob/camera/blob/B = usr - B.create_factory() + B.createSpecial(BLOB_STRUCTURE_FACTORY_COST, /obj/structure/blob/special/factory, BLOB_FACTORY_MIN_DISTANCE, TRUE) /atom/movable/screen/blob/readapt_strain icon_state = "ui_chemswap" + // Description gets given its proper values on Initialize() name = "Readapt Strain" - desc = "Allows you to choose a new strain from 6 random choices for 40 resources." + desc = "Allows you to choose a new strain from ERROR random choices for ERROR resources." /atom/movable/screen/blob/readapt_strain/MouseEntered(location,control,params) if(hud?.mymob && isovermind(hud.mymob)) @@ -103,8 +128,8 @@ name = "[initial(name)] (FREE)" desc = "Randomly rerolls your strain for free." else - name = "[initial(name)] ([BLOB_REROLL_COST])" - desc = initial(desc) + name = "[initial(name)] ([BLOB_POWER_REROLL_COST])" + desc = "Allows you to choose a new strain from [BLOB_POWER_REROLL_CHOICES] random choices for [BLOB_POWER_REROLL_COST] resources." ..() /atom/movable/screen/blob/readapt_strain/Click() @@ -114,8 +139,14 @@ /atom/movable/screen/blob/relocate_core icon_state = "ui_swap" - name = "Relocate Core (80)" - desc = "Swaps a node and your core for 80 resources." + // Name and description get given their proper values on Initialize() + name = "Relocate Core (ERROR)" + desc = "Swaps a node and your core for ERROR resources." + +/atom/movable/screen/blob/relocate_core/Initialize() + . = ..() + name = "Relocate Core ([BLOB_POWER_RELOCATE_COST])" + desc = "Swaps a node and your core for [BLOB_POWER_RELOCATE_COST] resources." /atom/movable/screen/blob/relocate_core/Click() if(isovermind(usr)) diff --git a/code/modules/antagonists/blob/blob.dm b/code/modules/antagonists/blob/blob.dm index 0709ff412db..36d520a3238 100644 --- a/code/modules/antagonists/blob/blob.dm +++ b/code/modules/antagonists/blob/blob.dm @@ -6,8 +6,7 @@ job_rank = ROLE_BLOB var/datum/action/innate/blobpop/pop_action - var/starting_points_human_blob = 60 - var/point_rate_human_blob = 2 + var/starting_points_human_blob = OVERMIND_STARTING_POINTS /datum/antagonist/blob/roundend_report() var/basic_report = ..() @@ -57,7 +56,7 @@ var/mob/camera/blob/B = new /mob/camera/blob(get_turf(old_body), blobtag.starting_points_human_blob) owner.mind.transfer_to(B) old_body.gib() - B.place_blob_core(blobtag.point_rate_human_blob, pop_override = TRUE) + B.place_blob_core(placement_override = TRUE, pop_override = TRUE) /datum/antagonist/blob/antag_listing_status() . = ..() diff --git a/code/modules/antagonists/blob/blob_mobs.dm b/code/modules/antagonists/blob/blob_mobs.dm index 7f5f29de816..9cb33f38952 100644 --- a/code/modules/antagonists/blob/blob_mobs.dm +++ b/code/modules/antagonists/blob/blob_mobs.dm @@ -19,7 +19,7 @@ lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE initial_language_holder = /datum/language_holder/empty var/mob/camera/blob/overmind = null - var/obj/structure/blob/factory/factory = null + var/obj/structure/blob/special/factory = null var/independent = FALSE /mob/living/simple_animal/hostile/blob/update_icons() @@ -53,7 +53,7 @@ H.color = overmind.blobstrain.complementary_color else H.color = "#000000" - adjustHealth(-maxHealth*0.0125) + adjustHealth(-maxHealth*BLOBMOB_HEALING_MULTIPLIER) /mob/living/simple_animal/hostile/blob/fire_act(exposed_temperature, exposed_volume) ..() @@ -92,14 +92,14 @@ icon_state = "blobpod" icon_living = "blobpod" health_doll_icon = "blobpod" - health = 30 - maxHealth = 30 + health = BLOBMOB_SPORE_HEALTH + maxHealth = BLOBMOB_SPORE_HEALTH verb_say = "psychically pulses" verb_ask = "psychically probes" verb_exclaim = "psychically yells" verb_yell = "psychically screams" - melee_damage_lower = 2 - melee_damage_upper = 4 + melee_damage_lower = BLOBMOB_SPORE_DMG_LOWER + melee_damage_upper = BLOBMOB_SPORE_DMG_UPPER environment_smash = ENVIRONMENT_SMASH_NONE obj_damage = 0 attack_verb_continuous = "hits" @@ -115,7 +115,7 @@ ///Whether or not this is a fragile spore from Distributed Neurons var/is_weak = FALSE -/mob/living/simple_animal/hostile/blob/blobspore/Initialize(mapload, obj/structure/blob/factory/linked_node) +/mob/living/simple_animal/hostile/blob/blobspore/Initialize(mapload, obj/structure/blob/special/linked_node) . = ..() if(istype(linked_node)) factory = linked_node @@ -253,12 +253,12 @@ icon_state = "blobbernaut" icon_living = "blobbernaut" icon_dead = "blobbernaut_dead" - health = 200 - maxHealth = 200 + health = BLOBMOB_BLOBBERNAUT_HEALTH + maxHealth = BLOBMOB_BLOBBERNAUT_HEALTH damage_coeff = list(BRUTE = 0.5, BURN = 1, TOX = 1, CLONE = 1, STAMINA = 0, OXY = 1) - melee_damage_lower = 20 - melee_damage_upper = 20 - obj_damage = 60 + melee_damage_lower = BLOBMOB_BLOBBERNAUT_DMG_SOLO_LOWER + melee_damage_upper = BLOBMOB_BLOBBERNAUT_DMG_SOLO_UPPER + obj_damage = BLOBMOB_BLOBBERNAUT_DMG_OBJ attack_verb_continuous = "slams" attack_verb_simple = "slam" attack_sound = 'sound/effects/blobattack.ogg' @@ -289,15 +289,15 @@ if(!factory) damagesources++ else - if(locate(/obj/structure/blob/core) in blobs_in_area) - adjustHealth(-maxHealth*0.1) + if(locate(/obj/structure/blob/special/core) in blobs_in_area) + adjustHealth(-maxHealth*BLOBMOB_BLOBBERNAUT_HEALING_CORE) var/obj/effect/temp_visual/heal/H = new /obj/effect/temp_visual/heal(get_turf(src)) //hello yes you are being healed if(overmind) H.color = overmind.blobstrain.complementary_color else H.color = "#000000" - if(locate(/obj/structure/blob/node) in blobs_in_area) - adjustHealth(-maxHealth*0.05) + if(locate(/obj/structure/blob/special/node) in blobs_in_area) + adjustHealth(-maxHealth*BLOBMOB_BLOBBERNAUT_HEALING_NODE) var/obj/effect/temp_visual/heal/H = new /obj/effect/temp_visual/heal(get_turf(src)) if(overmind) H.color = overmind.blobstrain.complementary_color @@ -305,7 +305,7 @@ H.color = "#000000" if(damagesources) for(var/i in 1 to damagesources) - adjustHealth(maxHealth*0.025) //take 2.5% of max health as damage when not near the blob or if the naut has no factory, 5% if both + adjustHealth(maxHealth*BLOBMOB_BLOBBERNAUT_HEALTH_DECAY) //take 2.5% of max health as damage when not near the blob or if the naut has no factory, 5% if both var/image/I = new('icons/mob/blob.dmi', src, "nautdamage", MOB_LAYER+0.01) I.appearance_flags = RESET_COLOR if(overmind) @@ -329,8 +329,8 @@ /mob/living/simple_animal/hostile/blob/blobbernaut/update_icons() ..() if(overmind) //if we have an overmind, we're doing chemical reactions instead of pure damage - melee_damage_lower = 4 - melee_damage_upper = 4 + melee_damage_lower = BLOBMOB_BLOBBERNAUT_DMG_LOWER + melee_damage_upper = BLOBMOB_BLOBBERNAUT_DMG_UPPER attack_verb_continuous = overmind.blobstrain.blobbernaut_message else melee_damage_lower = initial(melee_damage_lower) diff --git a/code/modules/antagonists/blob/blobstrains/_blobstrain.dm b/code/modules/antagonists/blob/blobstrains/_blobstrain.dm index 4fa454a85e2..8e081706e70 100644 --- a/code/modules/antagonists/blob/blobstrains/_blobstrain.dm +++ b/code/modules/antagonists/blob/blobstrains/_blobstrain.dm @@ -3,19 +3,67 @@ GLOBAL_LIST_INIT(valid_blobstrains, subtypesof(/datum/blobstrain) - list(/datum/ /datum/blobstrain var/name var/description - var/color = "#000000" - var/complementary_color = "#000000" //a color that's complementary to the normal blob color - var/shortdesc = null //just damage and on_mob effects, doesn't include special, blob-tile only effects - var/effectdesc = null //any long, blob-tile specific effects + var/color = COLOR_BLACK + /// The color that stuff like healing effects and the overmind camera gets + var/complementary_color = COLOR_BLACK + /// A short description of the power and its effects + var/shortdesc = null + /// Any long, blob-tile specific effects + var/effectdesc = null + /// Short descriptor of what the strain does damage-wise, generally seen in the reroll menu var/analyzerdescdamage = "Unknown. Report this bug to a coder, or just adminhelp." + /// Short descriptor of what the strain does in general, generally seen in the reroll menu var/analyzerdesceffect - var/blobbernaut_message = "slams" //blobbernaut attack verb - var/message = "The blob strikes you" //message sent to any mob hit by the blob - var/message_living = null //extension to first mob sent to only living mobs i.e. silicons have no skin to be burnt - var/core_regen = 2 + /// Blobbernaut attack verb + var/blobbernaut_message = "slams" + /// Message sent to any mob hit by the blob + var/message = "The blob strikes you" + /// Gets added onto 'message' if the mob stuck is of type living + var/message_living = null + /// Stores world.time to figure out when to next give resources var/resource_delay = 0 - var/point_rate = 2 + /// For blob-mobs and extinguishing-based effects + var/fire_based = FALSE var/mob/camera/blob/overmind + /// The amount of health regenned on core_process + var/base_core_regen = BLOB_CORE_HP_REGEN + /// The amount of points gained on core_process + var/point_rate = BLOB_BASE_POINT_RATE + + // Various vars that strains can buff the blob with + /// HP regen bonus added by strain + var/core_regen_bonus = 0 + /// resource point bonus added by strain + var/point_rate_bonus = 0 + + /// Adds to claim, pulse, and expand range + var/core_range_bonus = 0 + /// The core can sustain this many extra spores with this strain + var/core_spore_bonus = 0 + /// Extra range up to which the core reinforces blobs + var/core_strong_reinforcement_range_bonus = 0 + /// Extra range up to which the core reinforces blobs into reflectors + var/core_reflector_reinforcement_range_bonus = 0 + + /// Adds to claim, pulse, and expand range + var/node_range_bonus = 0 + /// Nodes can sustain this any extra spores with this strain + var/node_spore_bonus = 0 + /// Extra range up to which the node reinforces blobs + var/node_strong_reinforcement_range_bonus = 0 + /// Extra range up to which the node reinforces blobs into reflectors + var/node_reflector_reinforcement_range_bonus = 0 + + /// Extra spores produced by factories with this strain + var/factory_spore_bonus = 0 + + /// Multiplies the max and current health of every blob with this value upon selecting this strain. + var/max_structure_health_multiplier = 1 + /// Multiplies the max and current health of every mob with this value upon selecting this strain. + var/max_mob_health_multiplier = 1 + + /// Makes blobbernauts inject a bonus amount of reagents, making their attacks more powerful + var/blobbernaut_reagentatk_bonus = 0 /datum/blobstrain/New(mob/camera/blob/new_overmind) if (!istype(new_overmind)) @@ -24,16 +72,66 @@ GLOBAL_LIST_INIT(valid_blobstrains, subtypesof(/datum/blobstrain) - list(/datum/ /datum/blobstrain/proc/on_gain() overmind.color = complementary_color - for(var/BL in GLOB.blobs) - var/obj/structure/blob/B = BL + + if(overmind.blob_core) + overmind.blob_core.max_spores += core_spore_bonus + overmind.blob_core.claim_range += core_range_bonus + overmind.blob_core.pulse_range += core_range_bonus + overmind.blob_core.expand_range += core_range_bonus + overmind.blob_core.strong_reinforce_range += core_strong_reinforcement_range_bonus + overmind.blob_core.reflector_reinforce_range += core_reflector_reinforcement_range_bonus + + for(var/obj/structure/blob/special/node/N as anything in overmind.node_blobs) + N.max_spores += node_spore_bonus + N.claim_range += node_range_bonus + N.pulse_range += node_range_bonus + N.expand_range += node_range_bonus + N.strong_reinforce_range += node_strong_reinforcement_range_bonus + N.reflector_reinforce_range += node_reflector_reinforcement_range_bonus + + for(var/obj/structure/blob/special/factory/F as anything in overmind.factory_blobs) + F.max_spores += factory_spore_bonus + + for(var/obj/structure/blob/B as anything in overmind.all_blobs) + B.max_integrity *= max_structure_health_multiplier + B.obj_integrity *= max_structure_health_multiplier B.update_icon() - for(var/BLO in overmind.blob_mobs) - var/mob/living/simple_animal/hostile/blob/BM = BLO + + for(var/mob/living/simple_animal/hostile/blob/BM as anything in overmind.blob_mobs) + BM.maxHealth *= max_mob_health_multiplier + BM.health *= max_mob_health_multiplier BM.update_icons() //If it's getting a new strain, tell it what it does! to_chat(BM, "Your overmind's blob strain is now: [name]!") to_chat(BM, "The [name] strain [shortdesc ? "[shortdesc]" : "[description]"]") /datum/blobstrain/proc/on_lose() + if(overmind.blob_core) + overmind.blob_core.max_spores -= core_spore_bonus + overmind.blob_core.claim_range -= core_range_bonus + overmind.blob_core.pulse_range -= core_range_bonus + overmind.blob_core.expand_range -= core_range_bonus + overmind.blob_core.strong_reinforce_range -= core_strong_reinforcement_range_bonus + overmind.blob_core.reflector_reinforce_range -= core_reflector_reinforcement_range_bonus + + for(var/obj/structure/blob/special/node/N as anything in overmind.node_blobs) + N.max_spores -= node_spore_bonus + N.claim_range -= node_range_bonus + N.pulse_range -= node_range_bonus + N.expand_range -= node_range_bonus + N.strong_reinforce_range -= node_strong_reinforcement_range_bonus + N.reflector_reinforce_range -= node_reflector_reinforcement_range_bonus + + for(var/obj/structure/blob/special/factory/F as anything in overmind.factory_blobs) + F.max_spores -= factory_spore_bonus + + for(var/obj/structure/blob/B as anything in overmind.all_blobs) + B.max_integrity /= max_structure_health_multiplier + B.obj_integrity /= max_structure_health_multiplier + + for(var/mob/living/simple_animal/hostile/blob/BM as anything in overmind.blob_mobs) + BM.maxHealth /= max_mob_health_multiplier + BM.health /= max_mob_health_multiplier + /datum/blobstrain/proc/on_sporedeath(mob/living/spore) @@ -47,8 +145,8 @@ GLOBAL_LIST_INIT(valid_blobstrains, subtypesof(/datum/blobstrain) - list(/datum/ /datum/blobstrain/proc/core_process() if(resource_delay <= world.time) resource_delay = world.time + 10 // 1 second - overmind.add_points(point_rate) - overmind.blob_core.obj_integrity = min(overmind.blob_core.max_integrity, overmind.blob_core.obj_integrity+core_regen) + overmind.add_points(point_rate+point_rate_bonus) + overmind.blob_core.obj_integrity = min(overmind.blob_core.max_integrity, overmind.blob_core.obj_integrity+base_core_regen+core_regen_bonus) /datum/blobstrain/proc/attack_living(mob/living/L, list/nearby_blobs) // When the blob attacks people send_message(L) diff --git a/code/modules/antagonists/blob/blobstrains/_reagent.dm b/code/modules/antagonists/blob/blobstrains/_reagent.dm index 8e253932890..ac8b6fe2b82 100644 --- a/code/modules/antagonists/blob/blobstrains/_reagent.dm +++ b/code/modules/antagonists/blob/blobstrains/_reagent.dm @@ -1,4 +1,4 @@ -/datum/blobstrain/reagent // Blobs that mess with reagents, all "legacy" ones +/datum/blobstrain/reagent // Blobs that mess with reagents, all "legacy" ones // what do you mean "legacy" you never added an alternative var/datum/reagent/reagent /datum/blobstrain/reagent/New(mob/camera/blob/new_overmind) @@ -8,12 +8,12 @@ /datum/blobstrain/reagent/attack_living(mob/living/L) var/mob_protection = L.get_permeability_protection() - reagent.expose_mob(L, VAPOR, 25, 1, mob_protection, overmind) + reagent.expose_mob(L, VAPOR, BLOB_REAGENTATK_VOL, 1, mob_protection, overmind) send_message(L) /datum/blobstrain/reagent/blobbernaut_attack(mob/living/L) var/mob_protection = L.get_permeability_protection() - reagent.expose_mob(L, VAPOR, 20, 0, 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(L, VAPOR, BLOBMOB_BLOBBERNAUT_REAGENTATK_VOL+blobbernaut_reagentatk_bonus, 0, 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/spore) spore.reagents.add_reagent(reagent.type, 10) diff --git a/code/modules/antagonists/blob/blobstrains/blazing_oil.dm b/code/modules/antagonists/blob/blobstrains/blazing_oil.dm index e9d8b6aef2a..c24200adeb0 100644 --- a/code/modules/antagonists/blob/blobstrains/blazing_oil.dm +++ b/code/modules/antagonists/blob/blobstrains/blazing_oil.dm @@ -12,6 +12,7 @@ message = "The blob splashes you with burning oil" message_living = ", and you feel your skin char and melt" reagent = /datum/reagent/blob/blazing_oil + fire_based = TRUE /datum/blobstrain/reagent/blazing_oil/extinguish_reaction(obj/structure/blob/B) B.take_damage(1.5, BURN, ENERGY) diff --git a/code/modules/antagonists/blob/blobstrains/debris_devourer.dm b/code/modules/antagonists/blob/blobstrains/debris_devourer.dm index 9fefe3bd0b3..dacb75c1923 100644 --- a/code/modules/antagonists/blob/blobstrains/debris_devourer.dm +++ b/code/modules/antagonists/blob/blobstrains/debris_devourer.dm @@ -18,7 +18,7 @@ debris_attack(L, blob) /datum/blobstrain/debris_devourer/on_sporedeath(mob/living/spore) - var/obj/structure/blob/core/core = overmind.blob_core + var/obj/structure/blob/special/core/core = overmind.blob_core for(var/i in 1 to 3) var/obj/item/I = pick(core.contents) if (I && !QDELETED(I)) @@ -30,7 +30,7 @@ I.forceMove(overmind.blob_core) /datum/blobstrain/debris_devourer/proc/debris_attack(mob/living/L, source) - var/obj/structure/blob/core/core = overmind.blob_core + var/obj/structure/blob/special/core/core = overmind.blob_core if (prob(40 * DEBRIS_DENSITY)) // Pretend the items are spread through the blob and its mobs and not in the core. var/obj/item/I = pick(core.contents) if (I && !QDELETED(I)) @@ -41,12 +41,12 @@ debris_attack(L,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 - var/obj/structure/blob/core/core = overmind.blob_core + 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 /datum/blobstrain/debris_devourer/examine(mob/user) . = ..() - var/obj/structure/blob/core/core = overmind.blob_core + var/obj/structure/blob/special/core/core = overmind.blob_core if (isobserver(user)) . += "Absorbed debris is currently reducing incoming damage by [round(max(min(DEBRIS_DENSITY, 10),0))]" else diff --git a/code/modules/antagonists/blob/blobstrains/electromagnetic_web.dm b/code/modules/antagonists/blob/blobstrains/electromagnetic_web.dm index 427e0f839a3..4a5c49d851a 100644 --- a/code/modules/antagonists/blob/blobstrains/electromagnetic_web.dm +++ b/code/modules/antagonists/blob/blobstrains/electromagnetic_web.dm @@ -10,14 +10,8 @@ reagent = /datum/reagent/blob/electromagnetic_web /datum/blobstrain/reagent/electromagnetic_web/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag) - if(damage_type == BRUTE) //take full brute - switch(B.brute_resist) - if(0.5) - return damage * 2 - if(0.25) - return damage * 4 - if(0.1) - return damage * 10 + if(damage_type == BRUTE) // take full brute, divide by the multiplier to get full value + return damage / B.brute_resist return damage * 1.25 //a laser will do 25 damage, which will kill any normal blob /datum/blobstrain/reagent/electromagnetic_web/death_reaction(obj/structure/blob/B, damage_flag) diff --git a/code/modules/antagonists/blob/blobstrains/networked_fibers.dm b/code/modules/antagonists/blob/blobstrains/networked_fibers.dm index 0518d0bac05..5ceec1a4cad 100644 --- a/code/modules/antagonists/blob/blobstrains/networked_fibers.dm +++ b/code/modules/antagonists/blob/blobstrains/networked_fibers.dm @@ -9,7 +9,7 @@ color = "#4F4441" complementary_color = "#414C4F" reagent = /datum/reagent/blob/networked_fibers - core_regen = 5 + core_regen_bonus = 5 /datum/blobstrain/reagent/networked_fibers/expand_reaction(obj/structure/blob/spawning_blob, obj/structure/blob/new_blob, turf/chosen_turf, mob/camera/blob/overmind) if(!overmind && new_blob.overmind) @@ -19,7 +19,7 @@ if(isspaceturf(chosen_turf)) return for(var/obj/structure/blob/possible_expander in range(1, new_blob)) - if(possible_expander.overmind == overmind && (istype(possible_expander, /obj/structure/blob/core) || istype(possible_expander, /obj/structure/blob/node))) + if(possible_expander.overmind == overmind && (istype(possible_expander, /obj/structure/blob/special/core) || istype(possible_expander, /obj/structure/blob/special/node))) new_blob.forceMove(get_turf(possible_expander)) possible_expander.forceMove(chosen_turf) possible_expander.setDir(get_dir(new_blob, possible_expander)) diff --git a/code/modules/antagonists/blob/blobstrains/regenerative_materia.dm b/code/modules/antagonists/blob/blobstrains/regenerative_materia.dm index 6b7aa557ef3..d5d2e363659 100644 --- a/code/modules/antagonists/blob/blobstrains/regenerative_materia.dm +++ b/code/modules/antagonists/blob/blobstrains/regenerative_materia.dm @@ -7,8 +7,8 @@ complementary_color = "#AF7B8D" message_living = ", and you feel alive" reagent = /datum/reagent/blob/regenerative_materia - core_regen = 20 - point_rate = 3 + core_regen_bonus = 20 + point_rate_bonus = 1 /datum/reagent/blob/regenerative_materia name = "Regenerative Materia" diff --git a/code/modules/antagonists/blob/blobstrains/synchronous_mesh.dm b/code/modules/antagonists/blob/blobstrains/synchronous_mesh.dm index 1c57edea1c3..081b1f3dd31 100644 --- a/code/modules/antagonists/blob/blobstrains/synchronous_mesh.dm +++ b/code/modules/antagonists/blob/blobstrains/synchronous_mesh.dm @@ -15,10 +15,10 @@ if(damage_flag == MELEE || damage_flag == BULLET || damage_flag == LASER) //the cause isn't fire or bombs, so split the damage var/damagesplit = 1 //maximum split is 9, reducing the damage each blob takes to 11% but doing that damage to 9 blobs for(var/obj/structure/blob/C in orange(1, B)) - if(!istype(C, /obj/structure/blob/core) && !istype(C, /obj/structure/blob/node) && C.overmind && C.overmind.blobstrain.type == B.overmind.blobstrain.type) //if it doesn't have the same chemical or is a core or node, don't split damage to it + if(!C.ignore_syncmesh_share && C.overmind && C.overmind.blobstrain.type == B.overmind.blobstrain.type) //if it doesn't have the same chemical or is a core or node, don't split damage to it damagesplit += 1 for(var/obj/structure/blob/C in orange(1, B)) - if(!istype(C, /obj/structure/blob/core) && !istype(C, /obj/structure/blob/node) && C.overmind && C.overmind.blobstrain.type == B.overmind.blobstrain.type) //only hurt blobs that have the same overmind chemical and aren't cores or nodes + if(!C.ignore_syncmesh_share && C.overmind && C.overmind.blobstrain.type == B.overmind.blobstrain.type) //only hurt blobs that have the same overmind chemical and aren't cores or nodes C.take_damage(damage/damagesplit, CLONE, 0, 0) return damage / damagesplit else diff --git a/code/modules/antagonists/blob/overmind.dm b/code/modules/antagonists/blob/overmind.dm index 85abedf7e3a..629bc3d2a2b 100644 --- a/code/modules/antagonists/blob/overmind.dm +++ b/code/modules/antagonists/blob/overmind.dm @@ -21,32 +21,36 @@ GLOBAL_LIST_EMPTY(blob_nodes) faction = list(ROLE_BLOB) lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE hud_type = /datum/hud/blob_overmind - var/obj/structure/blob/core/blob_core = null // The blob overmind's core + var/obj/structure/blob/special/core/blob_core = null // The blob overmind's core var/blob_points = 0 - var/max_blob_points = 100 + var/max_blob_points = OVERMIND_MAX_POINTS_DEFAULT var/last_attack = 0 - var/datum/blobstrain/blobstrain + var/datum/blobstrain/reagent/blobstrain var/list/blob_mobs = list() + /// A list of all blob structures + var/list/all_blobs = list() var/list/resource_blobs = list() - var/free_strain_rerolls = 1 //one free strain reroll + var/list/factory_blobs = list() + var/list/node_blobs = list() + var/free_strain_rerolls = OVERMIND_STARTING_REROLLS var/last_reroll_time = 0 //time since we last rerolled, used to give free rerolls var/nodes_required = TRUE //if the blob needs nodes to place resource and factory blobs var/placed = FALSE - var/manualplace_min_time = 600 //in deciseconds //a minute, to get bearings - var/autoplace_max_time = 3600 //six minutes, as long as should be needed + var/manualplace_min_time = OVERMIND_STARTING_MIN_PLACE_TIME // Some time to get your bearings + var/autoplace_max_time = OVERMIND_STARTING_AUTO_PLACE_TIME // Automatically place the core in a random spot var/list/blobs_legit = list() var/max_count = 0 //The biggest it got before death - var/blobwincount = 400 + var/blobwincount = OVERMIND_WIN_CONDITION_AMOUNT var/victory_in_progress = FALSE var/rerolling = FALSE - var/announcement_size = 75 + var/announcement_size = OVERMIND_ANNOUNCEMENT_MIN_SIZE // Announce the biohazard when this size is reached var/announcement_time var/has_announced = FALSE /// The list of strains the blob can reroll for. var/list/strain_choices -/mob/camera/blob/Initialize(mapload, starting_points = 60) +/mob/camera/blob/Initialize(mapload, starting_points = OVERMIND_STARTING_POINTS) validate_location() blob_points = starting_points manualplace_min_time += world.time @@ -117,7 +121,7 @@ GLOBAL_LIST_EMPTY(blob_nodes) max_blob_points = INFINITY blob_points = INFINITY addtimer(CALLBACK(src, .proc/victory), 450) - else if(!free_strain_rerolls && (last_reroll_time + BLOB_REROLL_TIMEYou have gained another free strain re-roll.") free_strain_rerolls = 1 @@ -184,6 +188,13 @@ GLOBAL_LIST_EMPTY(blob_nodes) if(BM) BM.overmind = null BM.update_icons() + for(var/obj/structure/blob/blob_structure as anything in all_blobs) + blob_structure.overmind = null + all_blobs = null + resource_blobs = null + factory_blobs = null + node_blobs = null + blob_mobs = null GLOB.overminds -= src QDEL_LIST_ASSOC_VAL(strain_choices) @@ -270,7 +281,7 @@ GLOBAL_LIST_EMPTY(blob_nodes) /mob/camera/blob/Move(NewLoc, Dir = 0) if(placed) - var/obj/structure/blob/B = locate() in range("3x3", NewLoc) + var/obj/structure/blob/B = locate() in range(OVERMIND_MAX_CAMERA_STRAY, NewLoc) if(B) forceMove(NewLoc) else diff --git a/code/modules/antagonists/blob/powers.dm b/code/modules/antagonists/blob/powers.dm index 21c61e340b0..c1eebaf0fc6 100644 --- a/code/modules/antagonists/blob/powers.dm +++ b/code/modules/antagonists/blob/powers.dm @@ -1,4 +1,3 @@ -#define BLOB_REROLL_CHOICES 6 #define BLOB_REROLL_RADIUS 60 /mob/camera/blob/proc/can_buy(cost = 15) @@ -8,8 +7,6 @@ add_points(-cost) return TRUE -// Power verbs - /mob/camera/blob/proc/place_blob_core(placement_override, pop_override = FALSE) if(placed && placement_override != -1) return TRUE @@ -54,34 +51,28 @@ if(placed && blob_core) blob_core.forceMove(loc) else - var/obj/structure/blob/core/core = new(get_turf(src), src, 1) + var/obj/structure/blob/special/core/core = new(get_turf(src), src, 1) core.overmind = src blobs_legit += src blob_core = core core.update_icon() update_health_hud() - placed = 1 - announcement_time = world.time + 6000 + placed = TRUE + announcement_time = world.time + OVERMIND_ANNOUNCEMENT_MAX_TIME return TRUE -/mob/camera/blob/verb/transport_core() - set category = "Blob" - set name = "Jump to Core" - set desc = "Move your camera to your core." +/mob/camera/blob/proc/transport_core() if(blob_core) forceMove(blob_core.drop_location()) -/mob/camera/blob/verb/jump_to_node() - set category = "Blob" - set name = "Jump to Node" - set desc = "Move your camera to a selected node." +/mob/camera/blob/proc/jump_to_node() if(GLOB.blob_nodes.len) var/list/nodes = list() for(var/i in 1 to GLOB.blob_nodes.len) - var/obj/structure/blob/node/B = GLOB.blob_nodes[i] + var/obj/structure/blob/special/node/B = GLOB.blob_nodes[i] nodes["Blob Node #[i] ([get_area_name(B)])"] = B var/node_name = input(src, "Choose a node to jump to.", "Node Jump") in nodes - var/obj/structure/blob/node/chosen_node = nodes[node_name] + var/obj/structure/blob/special/node/chosen_node = nodes[node_name] if(chosen_node) forceMove(chosen_node.loc) @@ -100,7 +91,7 @@ if(!(A.area_flags & BLOBS_ALLOWED)) //factory and resource blobs must be legit to_chat(src, "This type of blob must be placed on the station!") return - if(nodes_required && !(locate(/obj/structure/blob/node) in orange(3, T)) && !(locate(/obj/structure/blob/core) in orange(4, T))) + if(nodes_required && !(locate(/obj/structure/blob/special/node) in orange(BLOB_NODE_PULSE_RANGE, T)) && !(locate(/obj/structure/blob/special/core) in orange(BLOB_CORE_PULSE_RANGE, T))) to_chat(src, "You need to place this blob closer to a node or core!") return //handholdotron 2000 if(minSeparation) @@ -113,60 +104,30 @@ var/obj/structure/blob/N = B.change_to(blobstrain, src) return N -/mob/camera/blob/verb/toggle_node_req() - set category = "Blob" - set name = "Toggle Node Requirement" - set desc = "Toggle requiring nodes to place resource and factory blobs." +/mob/camera/blob/proc/toggle_node_req() nodes_required = !nodes_required if(nodes_required) to_chat(src, "You now require a nearby node or core to place factory and resource blobs.") else to_chat(src, "You no longer require a nearby node or core to place factory and resource blobs.") -/mob/camera/blob/verb/create_shield_power() - set category = "Blob" - set name = "Create/Upgrade Shield Blob (15)" - set desc = "Create a shield blob, which will block fire and is hard to kill. Using this on an existing shield blob turns it into a reflective blob, capable of reflecting most projectiles but making it twice as weak to brute attacks." - create_shield() - /mob/camera/blob/proc/create_shield(turf/T) var/obj/structure/blob/shield/S = locate(/obj/structure/blob/shield) in T if(S) - if(!can_buy(BLOB_REFLECTOR_COST)) + if(!can_buy(BLOB_UPGRADE_REFLECTOR_COST)) return if(S.obj_integrity < S.max_integrity * 0.5) - add_points(BLOB_REFLECTOR_COST) + add_points(BLOB_UPGRADE_REFLECTOR_COST) to_chat(src, "This shield blob is too damaged to be modified properly!") return to_chat(src, "You secrete a reflective ooze over the shield blob, allowing it to reflect projectiles at the cost of reduced integrity.") S.change_to(/obj/structure/blob/shield/reflective, src) else - createSpecial(15, /obj/structure/blob/shield, 0, FALSE, T) + createSpecial(BLOB_UPGRADE_STRONG_COST, /obj/structure/blob/shield, 0, FALSE, T) -/mob/camera/blob/verb/create_resource() - set category = "Blob" - set name = "Create Resource Blob (40)" - set desc = "Create a resource tower which will generate resources for you." - createSpecial(40, /obj/structure/blob/resource, 4, TRUE) - -/mob/camera/blob/verb/create_node() - set category = "Blob" - set name = "Create Node Blob (50)" - set desc = "Create a node, which will power nearby factory and resource blobs." - createSpecial(50, /obj/structure/blob/node, 5, FALSE) - -/mob/camera/blob/verb/create_factory() - set category = "Blob" - set name = "Create Factory Blob (60)" - set desc = "Create a spore tower that will spawn spores to harass your enemies." - createSpecial(60, /obj/structure/blob/factory, 7, TRUE) - -/mob/camera/blob/verb/create_blobbernaut() - set category = "Blob" - set name = "Create Blobbernaut (40)" - set desc = "Create a powerful blobbernaut which is mildly smart and will attack enemies." +/mob/camera/blob/proc/create_blobbernaut() var/turf/T = get_turf(src) - var/obj/structure/blob/factory/B = locate(/obj/structure/blob/factory) in T + var/obj/structure/blob/special/factory/B = locate(/obj/structure/blob/special/factory) in T if(!B) to_chat(src, "You must be on a factory blob!") return @@ -176,7 +137,7 @@ if(B.obj_integrity < B.max_integrity * 0.5) to_chat(src, "This factory blob is too damaged to sustain a blobbernaut.") return - if(!can_buy(40)) + if(!can_buy(BLOBMOB_BLOBBERNAUT_RESOURCE_COST)) return B.naut = TRUE //temporary placeholder to prevent creation of more than one per factory. @@ -207,15 +168,12 @@ to_chat(blobber, "The [blobstrain.name] reagent [blobstrain.shortdesc ? "[blobstrain.shortdesc]" : "[blobstrain.description]"]") else to_chat(src, "You could not conjure a sentience for your blobbernaut. Your points have been refunded. Try again later.") - add_points(40) + add_points(BLOBMOB_BLOBBERNAUT_RESOURCE_COST) B.naut = null -/mob/camera/blob/verb/relocate_core() - set category = "Blob" - set name = "Relocate Core (80)" - set desc = "Swaps the locations of your core and the selected node." +/mob/camera/blob/proc/relocate_core() var/turf/T = get_turf(src) - var/obj/structure/blob/node/B = locate(/obj/structure/blob/node) in T + var/obj/structure/blob/special/node/B = locate(/obj/structure/blob/special/node) in T if(!B) to_chat(src, "You must be on a blob node!") return @@ -226,7 +184,7 @@ if(isspaceturf(T) || A && !(A.area_flags & BLOBS_ALLOWED)) to_chat(src, "You cannot relocate your core here!") return - if(!can_buy(80)) + if(!can_buy(BLOB_POWER_RELOCATE_COST)) return var/turf/old_turf = get_turf(blob_core) var/olddir = blob_core.dir @@ -235,13 +193,6 @@ B.forceMove(old_turf) B.setDir(olddir) -/mob/camera/blob/verb/revert() - set category = "Blob" - set name = "Remove Blob" - set desc = "Removes a blob, giving you back some resources." - var/turf/T = get_turf(src) - remove_blob(T) - /mob/camera/blob/proc/remove_blob(turf/T) var/obj/structure/blob/B = locate() in T if(!B) @@ -258,13 +209,6 @@ to_chat(src, "Gained [B.point_return] resources from removing \the [B].") qdel(B) -/mob/camera/blob/verb/expand_blob_power() - set category = "Blob" - set name = "Expand/Attack Blob ([BLOB_SPREAD_COST])" - set desc = "Attempts to create a new blob in this tile. If the tile isn't clear, instead attacks it, damaging mobs and objects and refunding [BLOB_ATTACK_REFUND] points." - var/turf/T = get_turf(src) - expand_blob(T) - /mob/camera/blob/proc/expand_blob(turf/T) if(world.time < last_attack) return @@ -274,7 +218,7 @@ if(!possibleblobs.len) to_chat(src, "There is no blob adjacent to the target tile!") return - if(can_buy(BLOB_SPREAD_COST)) + if(can_buy(BLOB_EXPAND_COST)) var/attacksuccess = FALSE for(var/mob/living/L in T) if(ROLE_BLOB in L.faction) //no friendly/dead fire @@ -289,7 +233,7 @@ add_points(BLOB_ATTACK_REFUND) else to_chat(src, "There is a blob there!") - add_points(BLOB_SPREAD_COST) //otherwise, refund all of the cost + add_points(BLOB_EXPAND_COST) //otherwise, refund all of the cost else var/list/cardinalblobs = list() var/list/diagonalblobs = list() @@ -311,19 +255,12 @@ playsound(OB, 'sound/effects/splat.ogg', 50, TRUE) add_points(BLOB_ATTACK_REFUND) else - add_points(BLOB_SPREAD_COST) //if we're attacking diagonally and didn't hit anything, refund + add_points(BLOB_EXPAND_COST) //if we're attacking diagonally and didn't hit anything, refund if(attacksuccess) last_attack = world.time + CLICK_CD_MELEE else last_attack = world.time + CLICK_CD_RAPID -/mob/camera/blob/verb/rally_spores_power() - set category = "Blob" - set name = "Rally Spores" - set desc = "Rally your spores to move to a target location." - var/turf/T = get_turf(src) - rally_spores(T) - /mob/camera/blob/proc/rally_spores(turf/T) to_chat(src, "You rally your spores.") var/list/surrounding_turfs = block(locate(T.x - 1, T.y - 1, T.z), locate(T.x + 1, T.y + 1, T.z)) @@ -334,27 +271,9 @@ BS.LoseTarget() BS.Goto(pick(surrounding_turfs), BS.move_to_delay) -/mob/camera/blob/verb/blob_broadcast() - set category = "Blob" - set name = "Blob Broadcast" - set desc = "Speak with your blob spores and blobbernauts as your mouthpieces." - var/speak_text = stripped_input(src, "What would you like to say with your minions?", "Blob Broadcast", null) - if(!speak_text) - return - else - to_chat(src, "You broadcast with your minions, [speak_text]") - for(var/BLO in blob_mobs) - var/mob/living/simple_animal/hostile/blob/BM = BLO - if(BM.stat == CONSCIOUS) - BM.say(speak_text) - -/mob/camera/blob/verb/strain_reroll() - set category = "Blob" - set name = "Reactive Strain Adaptation (40)" - set desc = "Replaces your strain with a random, different one." - - if (!free_strain_rerolls && blob_points < BLOB_REROLL_COST) - to_chat(src, "You need at least [BLOB_REROLL_COST] resources to reroll your strain again!") +/mob/camera/blob/proc/strain_reroll() + if (!free_strain_rerolls && blob_points < BLOB_POWER_REROLL_COST) + to_chat(src, "You need at least [BLOB_POWER_REROLL_COST] resources to reroll your strain again!") return open_reroll_menu() @@ -365,7 +284,7 @@ strain_choices = list() var/list/new_strains = GLOB.valid_blobstrains.Copy() - blobstrain.type - for (var/_ in 1 to BLOB_REROLL_CHOICES) + for (var/_ in 1 to BLOB_POWER_REROLL_CHOICES) var/datum/blobstrain/strain = pick_n_take(new_strains) var/image/strain_icon = image('icons/mob/blob.dmi', "blob_core") @@ -386,7 +305,7 @@ if (isnull(strain_result)) return - if (!free_strain_rerolls && !can_buy(BLOB_REROLL_COST)) + if (!free_strain_rerolls && !can_buy(BLOB_POWER_REROLL_COST)) return for (var/_other_strain in GLOB.valid_blobstrains) @@ -402,10 +321,7 @@ return -/mob/camera/blob/verb/blob_help() - set category = "Blob" - set name = "*Blob Help*" - set desc = "Help on how to blob." +/mob/camera/blob/proc/blob_help() to_chat(src, "As the overmind, you can control the blob!") to_chat(src, "Your blob reagent is: [blobstrain.name]!") to_chat(src, "The [blobstrain.name] reagent [blobstrain.description]") @@ -426,5 +342,4 @@ to_chat(src, "You will automatically place your blob core in [DisplayTimeText(autoplace_max_time - world.time)].") to_chat(src, "You [manualplace_min_time ? "will be able to":"can"] manually place your blob core by pressing the Place Blob Core button in the bottom right corner of the screen.") -#undef BLOB_REROLL_CHOICES #undef BLOB_REROLL_RADIUS diff --git a/code/modules/antagonists/blob/structures/_blob.dm b/code/modules/antagonists/blob/structures/_blob.dm index dd19d9c67ed..3319c1a8fd5 100644 --- a/code/modules/antagonists/blob/structures/_blob.dm +++ b/code/modules/antagonists/blob/structures/_blob.dm @@ -10,21 +10,32 @@ layer = BELOW_MOB_LAYER pass_flags_self = PASSBLOB CanAtmosPass = ATMOS_PASS_PROC - var/point_return = 0 //How many points the blob gets back when it removes a blob of that type. If less than 0, blob cannot be removed. + /// How many points the blob gets back when it removes a blob of that type. If less than 0, blob cannot be removed. + var/point_return = 0 max_integrity = 30 armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 0, RAD = 0, FIRE = 80, ACID = 70) - var/health_regen = 2 //how much health this blob regens when pulsed - var/pulse_timestamp = 0 //we got pulsed when? - var/heal_timestamp = 0 //we got healed when? - var/brute_resist = 0.5 //multiplies brute damage by this - var/fire_resist = 1 //multiplies burn damage by this - var/atmosblock = FALSE //if the blob blocks atmos and heat spread + /// how much health this blob regens when pulsed + var/health_regen = BLOB_REGULAR_HP_REGEN + /// We got pulsed when? + COOLDOWN_DECLARE(pulse_timestamp) + /// we got healed when? + COOLDOWN_DECLARE(heal_timestamp) + /// Multiplies brute damage by this + var/brute_resist = BLOB_BRUTE_RESIST + /// Multiplies burn damage by this + var/fire_resist = BLOB_FIRE_RESIST + /// Only used by the synchronous mesh strain. If set to true, these blobs won't share or receive damage taken with others. + var/ignore_syncmesh_share = 0 + /// If the blob blocks atmos and heat spread + var/atmosblock = FALSE var/mob/camera/blob/overmind + /obj/structure/blob/Initialize(mapload, owner_overmind) . = ..() if(owner_overmind) overmind = owner_overmind + overmind.all_blobs += src var/area/Ablob = get_area(src) if(Ablob.area_flags & BLOBS_ALLOWED) //Is this area allowed for winning as blob? overmind.blobs_legit += src @@ -44,7 +55,9 @@ atmosblock = FALSE air_update_turf(TRUE, FALSE) if(overmind) + overmind.all_blobs -= src overmind.blobs_legit -= src //if it was in the legit blobs list, it isn't now + overmind = null GLOB.blobs -= src //it's no longer in the all blobs list either playsound(src.loc, 'sound/effects/splat.ogg', 50, TRUE) //Expand() is no longer broken, no check necessary. return ..() @@ -78,47 +91,14 @@ else remove_atom_colour(FIXED_COLOUR_PRIORITY) -/obj/structure/blob/proc/Pulse_Area(mob/camera/blob/pulsing_overmind, claim_range = 10, pulse_range = 3, expand_range = 2) - if(QDELETED(pulsing_overmind)) - pulsing_overmind = overmind - Be_Pulsed() - var/expanded = FALSE - if(prob(70) && expand()) - expanded = TRUE - var/list/blobs_to_affect = list() - for(var/obj/structure/blob/B in urange(claim_range, src, 1)) - blobs_to_affect += B - shuffle_inplace(blobs_to_affect) - for(var/L in blobs_to_affect) - var/obj/structure/blob/B = L - if(!B.overmind && !istype(B, /obj/structure/blob/core) && prob(30)) - B.overmind = pulsing_overmind //reclaim unclaimed, non-core blobs. - B.update_icon() - var/distance = get_dist(get_turf(src), get_turf(B)) - var/expand_probablity = max(20 - distance * 8, 1) - if(B.Adjacent(src)) - expand_probablity = 20 - if(distance <= expand_range) - var/can_expand = TRUE - if(blobs_to_affect.len >= 120 && B.heal_timestamp > world.time) - can_expand = FALSE - if(can_expand && B.pulse_timestamp <= world.time && prob(expand_probablity)) - var/obj/structure/blob/newB = B.expand(null, null, !expanded) //expansion falls off with range but is faster near the blob causing the expansion - if(newB) - if(expanded) - qdel(newB) - expanded = TRUE - if(distance <= pulse_range) - B.Be_Pulsed() - /obj/structure/blob/proc/Be_Pulsed() - if(pulse_timestamp <= world.time) + if(COOLDOWN_FINISHED(src, pulse_timestamp)) ConsumeTile() - if(heal_timestamp <= world.time) + if(COOLDOWN_FINISHED(src, heal_timestamp)) obj_integrity = min(max_integrity, obj_integrity+health_regen) - heal_timestamp = world.time + 20 + COOLDOWN_START(src, heal_timestamp, 20) update_icon() - pulse_timestamp = world.time + 10 + COOLDOWN_START(src, pulse_timestamp, 10) return TRUE//we did it, we were pulsed! return FALSE //oh no we failed @@ -326,10 +306,9 @@ name = "normal blob" icon_state = "blob" light_range = 0 - obj_integrity = 21 //doesn't start at full health - max_integrity = 25 - health_regen = 1 - brute_resist = 0.25 + max_integrity = BLOB_REGULAR_MAX_HP + health_regen = BLOB_REGULAR_HP_REGEN + brute_resist = BLOB_BRUTE_RESIST * 0.5 /obj/structure/blob/normal/scannerreport() if(obj_integrity <= 15) @@ -342,14 +321,92 @@ icon_state = "blob_damaged" name = "fragile blob" desc = "A thin lattice of slightly twitching tendrils." - brute_resist = 0.5 + brute_resist = BLOB_BRUTE_RESIST else if (overmind) icon_state = "blob" name = "blob" desc = "A thick wall of writhing tendrils." - brute_resist = 0.25 + brute_resist = BLOB_BRUTE_RESIST * 0.5 else icon_state = "blob" name = "dead blob" desc = "A thick wall of lifeless tendrils." - brute_resist = 0.25 + brute_resist = BLOB_BRUTE_RESIST * 0.5 + +/obj/structure/blob/special // Generic type for nodes/factories/cores/resource + // Core and node vars: claiming, pulsing and expanding + /// The radius inside which (previously dead) blob tiles are 'claimed' again by the pulsing overmind. Very rarely used. + var/claim_range = 0 + /// The radius inside which blobs are pulsed by this overmind. Does stuff like expanding, making blob spores from factories, make resources from nodes etc. + var/pulse_range = 0 + /// The radius up to which this special structure naturally grows normal blobs. + var/expand_range = 0 + + // Spore production vars: for core, factories, and nodes (with strains) + var/mob/living/simple_animal/hostile/blob/blobbernaut/naut = null + var/max_spores = 0 + var/list/spores = list() + COOLDOWN_DECLARE(spore_delay) + var/spore_cooldown = BLOBMOB_SPORE_SPAWN_COOLDOWN + + // Area reinforcement vars: used by cores and nodes, for strains to modify + /// Range this blob free upgrades to strong blobs at: for the core, and for strains + var/strong_reinforce_range = 0 + /// Range this blob free upgrades to reflector blobs at: for the core, and for strains + var/reflector_reinforce_range = 0 + +/obj/structure/blob/special/proc/reinforce_area(delta_time) // Used by cores and nodes to upgrade their surroundings + if(strong_reinforce_range) + for(var/obj/structure/blob/normal/B in range(strong_reinforce_range, src)) + if(DT_PROB(BLOB_REINFORCE_CHANCE, delta_time)) + B.change_to(/obj/structure/blob/shield/core, overmind) + if(reflector_reinforce_range) + for(var/obj/structure/blob/shield/B in range(reflector_reinforce_range, src)) + if(DT_PROB(BLOB_REINFORCE_CHANCE, delta_time)) + B.change_to(/obj/structure/blob/shield/reflective/core, overmind) + +/obj/structure/blob/special/proc/pulse_area(mob/camera/blob/pulsing_overmind, claim_range = 10, pulse_range = 3, expand_range = 2) + if(QDELETED(pulsing_overmind)) + pulsing_overmind = overmind + Be_Pulsed() + var/expanded = FALSE + if(prob(70*(1/BLOB_EXPAND_CHANCE_MULTIPLIER)) && expand()) + expanded = TRUE + var/list/blobs_to_affect = list() + for(var/obj/structure/blob/B in urange(claim_range, src, 1)) + blobs_to_affect += B + shuffle_inplace(blobs_to_affect) + for(var/L in blobs_to_affect) + var/obj/structure/blob/B = L + if(!B.overmind && prob(30)) + B.overmind = pulsing_overmind //reclaim unclaimed, non-core blobs. + B.update_icon() + var/distance = get_dist(get_turf(src), get_turf(B)) + var/expand_probablity = max(20 - distance * 8, 1) + if(B.Adjacent(src)) + expand_probablity = 20 + if(distance <= expand_range) + var/can_expand = TRUE + if(blobs_to_affect.len >= 120 && !(COOLDOWN_FINISHED(B, heal_timestamp))) + can_expand = FALSE + if(can_expand && COOLDOWN_FINISHED(B, pulse_timestamp) && prob(expand_probablity*BLOB_EXPAND_CHANCE_MULTIPLIER)) + if(!expanded) + var/obj/structure/blob/newB = B.expand(null, null, !expanded) //expansion falls off with range but is faster near the blob causing the expansion + if(newB) + expanded = TRUE + if(distance <= pulse_range) + B.Be_Pulsed() + +/obj/structure/blob/special/proc/produce_spores() + if(naut) + return + if(spores.len >= max_spores) + return + if(!COOLDOWN_FINISHED(src, spore_delay)) + return + COOLDOWN_START(src, spore_delay, spore_cooldown) + var/mob/living/simple_animal/hostile/blob/blobspore/BS = new (loc, src) + if(overmind) //if we don't have an overmind, we don't need to do anything but make a spore + BS.overmind = overmind + BS.update_icons() + overmind.blob_mobs.Add(BS) diff --git a/code/modules/antagonists/blob/structures/core.dm b/code/modules/antagonists/blob/structures/core.dm index af2e2340105..e2f8ff17636 100644 --- a/code/modules/antagonists/blob/structures/core.dm +++ b/code/modules/antagonists/blob/structures/core.dm @@ -1,16 +1,23 @@ -/obj/structure/blob/core +/obj/structure/blob/special/core name = "blob core" icon = 'icons/mob/blob.dmi' icon_state = "blank_blob" desc = "A huge, pulsating yellow mass." - max_integrity = 400 + max_integrity = BLOB_CORE_MAX_HP armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 0, RAD = 0, FIRE = 75, ACID = 90) explosion_block = 6 point_return = -1 health_regen = 0 //we regen in Life() instead of when pulsed resistance_flags = LAVA_PROOF + strong_reinforce_range = BLOB_CORE_STRONG_REINFORCE_RANGE + reflector_reinforce_range = BLOB_CORE_REFLECTOR_REINFORCE_RANGE + claim_range = BLOB_CORE_CLAIM_RANGE + pulse_range = BLOB_CORE_PULSE_RANGE + expand_range = BLOB_CORE_EXPAND_RANGE + max_spores = BLOB_CORE_MAX_SPORES + ignore_syncmesh_share = TRUE -/obj/structure/blob/core/Initialize(mapload, client/new_overmind = null, placed = 0) +/obj/structure/blob/special/core/Initialize(mapload, client/new_overmind = null, placed = 0) GLOB.blob_cores += src START_PROCESSING(SSobj, src) AddElement(/datum/element/point_of_interest) @@ -18,13 +25,14 @@ if(!placed && !overmind) return INITIALIZE_HINT_QDEL if(overmind) + overmind.blobstrain.on_gain() update_icon() . = ..() -/obj/structure/blob/core/scannerreport() +/obj/structure/blob/special/core/scannerreport() return "Directs the blob's expansion, gradually expands, and sustains nearby blob spores and blobbernauts." -/obj/structure/blob/core/update_icon() +/obj/structure/blob/special/core/update_icon() cut_overlays() color = null var/mutable_appearance/blob_overlay = mutable_appearance('icons/mob/blob.dmi', "blob") @@ -33,7 +41,7 @@ add_overlay(blob_overlay) add_overlay(mutable_appearance('icons/mob/blob.dmi', "blob_core_overlay")) -/obj/structure/blob/core/Destroy() +/obj/structure/blob/special/core/Destroy() GLOB.blob_cores -= src if(overmind) overmind.blob_core = null @@ -41,17 +49,17 @@ STOP_PROCESSING(SSobj, src) return ..() -/obj/structure/blob/core/ex_act(severity, target) +/obj/structure/blob/special/core/ex_act(severity, target) var/damage = 50 - 10 * severity //remember, the core takes half brute damage, so this is 20/15/10 damage based on severity take_damage(damage, BRUTE, BOMB, 0) -/obj/structure/blob/core/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir, overmind_reagent_trigger = 1) +/obj/structure/blob/special/core/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir, overmind_reagent_trigger = 1) . = ..() if(obj_integrity > 0) if(overmind) //we should have an overmind, but... overmind.update_health_hud() -/obj/structure/blob/core/process(delta_time) +/obj/structure/blob/special/core/process(delta_time) if(QDELETED(src)) return if(!overmind) @@ -59,17 +67,16 @@ if(overmind) overmind.blobstrain.core_process() overmind.update_health_hud() - Pulse_Area(overmind, 12, 4, 3) - for(var/obj/structure/blob/normal/B in range(1, src)) - if(DT_PROB(2.5, delta_time)) - B.change_to(/obj/structure/blob/shield/core, overmind) + pulse_area(overmind, claim_range, pulse_range, expand_range) + reinforce_area(delta_time) + produce_spores() ..() -/obj/structure/blob/core/ComponentInitialize() +/obj/structure/blob/special/core/ComponentInitialize() . = ..() AddComponent(/datum/component/stationloving, FALSE, TRUE) -/obj/structure/blob/core/onTransitZ(old_z, new_z) +/obj/structure/blob/special/core/onTransitZ(old_z, new_z) if(overmind && is_station_level(new_z)) overmind.forceMove(get_turf(src)) return ..() diff --git a/code/modules/antagonists/blob/structures/factory.dm b/code/modules/antagonists/blob/structures/factory.dm index 138a359e72e..a65f16be155 100644 --- a/code/modules/antagonists/blob/structures/factory.dm +++ b/code/modules/antagonists/blob/structures/factory.dm @@ -1,25 +1,24 @@ -/obj/structure/blob/factory +/obj/structure/blob/special/factory name = "factory blob" icon = 'icons/mob/blob.dmi' icon_state = "blob_factory" desc = "A thick spire of tendrils." - max_integrity = 200 - health_regen = 1 - point_return = 25 + max_integrity = BLOB_FACTORY_MAX_HP + health_regen = BLOB_FACTORY_HP_REGEN + point_return = BLOB_REFUND_FACTORY_COST resistance_flags = LAVA_PROOF - var/list/spores = list() - var/mob/living/simple_animal/hostile/blob/blobbernaut/naut = null - var/max_spores = 3 - var/spore_delay = 0 - var/spore_cooldown = 80 //8 seconds between spores and after spore death + max_spores = BLOB_FACTORY_MAX_SPORES - -/obj/structure/blob/factory/scannerreport() +/obj/structure/blob/special/factory/scannerreport() if(naut) return "It is currently sustaining a blobbernaut, making it fragile and unable to produce blob spores." return "Will produce a blob spore every few seconds." -/obj/structure/blob/factory/Destroy() +/obj/structure/blob/special/factory/creation_action() + if(overmind) + overmind.factory_blobs += src + +/obj/structure/blob/special/factory/Destroy() for(var/mob/living/simple_animal/hostile/blob/blobspore/spore in spores) if(spore.factory == src) spore.factory = null @@ -28,20 +27,10 @@ to_chat(naut, "Your factory was destroyed! You feel yourself dying!") naut.throw_alert("nofactory", /atom/movable/screen/alert/nofactory) spores = null + if(overmind) + overmind.factory_blobs -= src return ..() -/obj/structure/blob/factory/Be_Pulsed() +/obj/structure/blob/special/factory/Be_Pulsed() . = ..() - if(naut) - return - if(spores.len >= max_spores) - return - if(spore_delay > world.time) - return - flick("blob_factory_glow", src) - spore_delay = world.time + spore_cooldown - var/mob/living/simple_animal/hostile/blob/blobspore/BS = new/mob/living/simple_animal/hostile/blob/blobspore(src.loc, src) - if(overmind) //if we don't have an overmind, we don't need to do anything but make a spore - BS.overmind = overmind - BS.update_icons() - overmind.blob_mobs.Add(BS) + produce_spores() diff --git a/code/modules/antagonists/blob/structures/node.dm b/code/modules/antagonists/blob/structures/node.dm index 456b2d8bcf0..73d2250d6d4 100644 --- a/code/modules/antagonists/blob/structures/node.dm +++ b/code/modules/antagonists/blob/structures/node.dm @@ -1,24 +1,29 @@ -/obj/structure/blob/node +/obj/structure/blob/special/node name = "blob node" icon = 'icons/mob/blob.dmi' icon_state = "blank_blob" desc = "A large, pulsating yellow mass." - max_integrity = 200 + max_integrity = BLOB_NODE_MAX_HP + health_regen = BLOB_NODE_HP_REGEN armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 0, RAD = 0, FIRE = 65, ACID = 90) - health_regen = 3 - point_return = 25 + point_return = BLOB_REFUND_NODE_COST + claim_range = BLOB_NODE_CLAIM_RANGE + pulse_range = BLOB_NODE_PULSE_RANGE + expand_range = BLOB_NODE_EXPAND_RANGE resistance_flags = LAVA_PROOF + max_spores = BLOB_NODE_MAX_SPORES + ignore_syncmesh_share = TRUE -/obj/structure/blob/node/Initialize() +/obj/structure/blob/special/node/Initialize() GLOB.blob_nodes += src START_PROCESSING(SSobj, src) . = ..() -/obj/structure/blob/node/scannerreport() +/obj/structure/blob/special/node/scannerreport() return "Gradually expands and sustains nearby blob spores and blobbernauts." -/obj/structure/blob/node/update_icon() +/obj/structure/blob/special/node/update_icon() cut_overlays() color = null var/mutable_appearance/blob_overlay = mutable_appearance('icons/mob/blob.dmi', "blob") @@ -27,11 +32,19 @@ add_overlay(blob_overlay) add_overlay(mutable_appearance('icons/mob/blob.dmi', "blob_node_overlay")) -/obj/structure/blob/node/Destroy() +/obj/structure/blob/special/node/creation_action() + if(overmind) + overmind.node_blobs += src + +/obj/structure/blob/special/node/Destroy() GLOB.blob_nodes -= src STOP_PROCESSING(SSobj, src) + if(overmind) + overmind.node_blobs -= src return ..() -/obj/structure/blob/node/process() +/obj/structure/blob/special/node/process(delta_time) if(overmind) - Pulse_Area(overmind, 10, 3, 2) + pulse_area(overmind, claim_range, pulse_range, expand_range) + reinforce_area(delta_time) + produce_spores() diff --git a/code/modules/antagonists/blob/structures/resource.dm b/code/modules/antagonists/blob/structures/resource.dm index 81eda7f4edd..ec931f08dcc 100644 --- a/code/modules/antagonists/blob/structures/resource.dm +++ b/code/modules/antagonists/blob/structures/resource.dm @@ -1,4 +1,4 @@ -/obj/structure/blob/resource +/obj/structure/blob/special/resource name = "resource blob" icon = 'icons/mob/blob.dmi' icon_state = "blob_resource" @@ -8,25 +8,25 @@ resistance_flags = LAVA_PROOF var/resource_delay = 0 -/obj/structure/blob/resource/scannerreport() +/obj/structure/blob/special/resource/scannerreport() return "Gradually supplies the blob with resources, increasing the rate of expansion." -/obj/structure/blob/resource/creation_action() +/obj/structure/blob/special/resource/creation_action() if(overmind) overmind.resource_blobs += src -/obj/structure/blob/resource/Destroy() +/obj/structure/blob/special/resource/Destroy() if(overmind) overmind.resource_blobs -= src return ..() -/obj/structure/blob/resource/Be_Pulsed() +/obj/structure/blob/special/resource/Be_Pulsed() . = ..() if(resource_delay > world.time) return flick("blob_resource_glow", src) if(overmind) - overmind.add_points(1) - resource_delay = world.time + 40 + overmind.resource_blobs.len * 2.5 //4 seconds plus a quarter second for each resource blob the overmind has + overmind.add_points(BLOB_RESOURCE_GATHER_AMOUNT) + resource_delay = world.time + BLOB_RESOURCE_GATHER_DELAY + overmind.resource_blobs.len * BLOB_RESOURCE_GATHER_ADDED_DELAY //4 seconds plus a quarter second for each resource blob the overmind has else - resource_delay = world.time + 40 + resource_delay = world.time + BLOB_RESOURCE_GATHER_DELAY diff --git a/code/modules/antagonists/blob/structures/shield.dm b/code/modules/antagonists/blob/structures/shield.dm index acd7419ef34..5da6ce75f51 100644 --- a/code/modules/antagonists/blob/structures/shield.dm +++ b/code/modules/antagonists/blob/structures/shield.dm @@ -5,9 +5,10 @@ desc = "A solid wall of slightly twitching tendrils." var/damaged_desc = "A wall of twitching tendrils." max_integrity = 150 - brute_resist = 0.25 + health_regen = BLOB_STRONG_HP_REGEN + brute_resist = BLOB_BRUTE_RESIST * 0.5 explosion_block = 3 - point_return = 4 + point_return = BLOB_REFUND_STRONG_COST atmosblock = TRUE armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 0, RAD = 0, FIRE = 90, ACID = 90) @@ -16,7 +17,7 @@ return "Will prevent the spread of atmospheric changes." return "N/A" -/obj/structure/blob/shield/core +/obj/structure/blob/shield/core // Automatically generated by the core point_return = 0 /obj/structure/blob/shield/update_icon() @@ -40,5 +41,10 @@ damaged_desc = "A wall of twitching tendrils with a reflective glow." icon_state = "blob_glow" flags_ricochet = RICOCHET_SHINY - point_return = 8 + point_return = BLOB_REFUND_REFLECTOR_COST explosion_block = 2 + max_integrity = BLOB_REFLECTOR_MAX_HP + health_regen = BLOB_REFLECTOR_HP_REGEN + +/obj/structure/blob/shield/reflective/core // Automatically generated by the core + point_return = 0 diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm index 93700e92bc8..076e1391b4a 100644 --- a/code/modules/mob/transform_procs.dm +++ b/code/modules/mob/transform_procs.dm @@ -225,7 +225,7 @@ . = new_slime qdel(src) -/mob/proc/become_overmind(starting_points = 60) +/mob/proc/become_overmind(starting_points = OVERMIND_STARTING_POINTS) var/mob/camera/blob/B = new /mob/camera/blob(get_turf(src), starting_points) B.key = key . = B diff --git a/tgstation.dme b/tgstation.dme index 093d6d8992d..3674974e9be 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -33,6 +33,7 @@ #include "code\__DEFINES\atom_hud.dm" #include "code\__DEFINES\bitfields.dm" #include "code\__DEFINES\blackmarket.dm" +#include "code\__DEFINES\blob_defines.dm" #include "code\__DEFINES\botany.dm" #include "code\__DEFINES\callbacks.dm" #include "code\__DEFINES\cargo.dm"