diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm
index cff68506b42..b9f554fd78e 100644
--- a/code/__DEFINES/antagonists.dm
+++ b/code/__DEFINES/antagonists.dm
@@ -71,6 +71,13 @@ GLOBAL_LIST_INIT(heretic_start_knowledge,list(/datum/eldritch_knowledge/spell/ba
#define PATH_FLESH "Flesh"
#define PATH_VOID "Void"
+/// Forces the blob to place the core where they currently are, ignoring any checks.
+#define BLOB_FORCE_PLACEMENT -1
+/// Normal blob placement, does the regular checks to make sure the blob isn't placing itself in an invalid location
+#define BLOB_NORMAL_PLACEMENT 0
+/// Selects a random location for the blob to be placed.
+#define BLOB_RANDOM_PLACEMENT 1
+
#define CONSTRUCT_JUGGERNAUT "Juggernaut"
#define CONSTRUCT_WRAITH "Wraith"
#define CONSTRUCT_ARTIFICER "Artificer"
diff --git a/code/_onclick/hud/blob_overmind.dm b/code/_onclick/hud/blob_overmind.dm
index 5af4f278788..dd3224c5500 100644
--- a/code/_onclick/hud/blob_overmind.dm
+++ b/code/_onclick/hud/blob_overmind.dm
@@ -49,7 +49,7 @@
if(isovermind(usr))
var/mob/camera/blob/B = usr
if(!B.placed)
- B.place_blob_core(0)
+ B.place_blob_core(BLOB_NORMAL_PLACEMENT)
B.transport_core()
/atom/movable/screen/blob/blobbernaut
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
index 68012c8afa3..dd77eb35ec9 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
+++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
@@ -444,7 +444,7 @@
enemy_roles = list("Security Officer", "Detective", "Head of Security", "Captain")
required_enemies = list(2,2,1,1,1,1,1,0,0,0)
required_candidates = 1
- weight = 4
+ weight = 2
cost = 10
requirements = list(101,101,101,80,60,50,30,20,10,10)
repeatable = TRUE
@@ -453,6 +453,41 @@
var/body = applicant.become_overmind()
return body
+/// Infects a random player, making them explode into a blob.
+/datum/dynamic_ruleset/midround/blob_infection
+ name = "Blob Infection"
+ antag_datum = /datum/antagonist/blob
+ antag_flag = ROLE_BLOB
+ protected_roles = list("Prisoner", "Security Officer", "Warden", "Detective", "Head of Security", "Captain")
+ restricted_roles = list("Cyborg", "AI", "Positronic Brain")
+ enemy_roles = list("Security Officer", "Detective", "Head of Security", "Captain")
+ required_enemies = list(2,2,1,1,1,1,1,0,0,0)
+ required_candidates = 1
+ weight = 2
+ cost = 10
+ requirements = list(101,101,101,80,60,50,30,20,10,10)
+ repeatable = TRUE
+
+/datum/dynamic_ruleset/midround/blob_infection/trim_candidates()
+ ..()
+ candidates = living_players
+ for(var/mob/living/player as anything in candidates)
+ var/turf/player_turf = get_turf(player)
+ if(!player_turf || !is_station_level(player_turf.z))
+ candidates -= player
+ continue
+
+ if(player.mind && (player.mind.special_role || length(player.mind.antag_datums) > 0))
+ candidates -= player
+
+/datum/dynamic_ruleset/midround/blob_infection/execute()
+ if(!candidates || !candidates.len)
+ return FALSE
+ var/mob/living/carbon/human/blob_antag = pick_n_take(candidates)
+ assigned += blob_antag.mind
+ blob_antag.mind.special_role = antag_flag
+ return ..()
+
//////////////////////////////////////////////
// //
// XENOMORPH (GHOST) //
diff --git a/code/modules/antagonists/blob/blob.dm b/code/modules/antagonists/blob/blob.dm
index 216352809f8..a8c54453f68 100644
--- a/code/modules/antagonists/blob/blob.dm
+++ b/code/modules/antagonists/blob/blob.dm
@@ -19,13 +19,25 @@
return basic_report
/datum/antagonist/blob/greet()
+ to_chat(owner.current, span_notice("You are the Blob!"))
+ owner.announce_objectives()
if(!isovermind(owner.current))
- to_chat(owner,span_userdanger("You feel bloated."))
+ to_chat(owner.current, span_notice("Use the pop ability to place your blob core! It is recommended you do this away from anyone else, as you'll be taking on the entire crew!"))
+
+ owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/blobalert.ogg', 100, FALSE, pressure_affected = FALSE, use_reverb = FALSE)
/datum/antagonist/blob/on_gain()
create_objectives()
. = ..()
+/datum/antagonist/blob/remove_innate_effects()
+ QDEL_NULL(pop_action)
+ return ..()
+
+/datum/antagonist/blob/farewell()
+ to_chat(owner.current, "You are no longer the Blob!")
+ return ..()
+
/datum/antagonist/blob/proc/create_objectives()
var/datum/objective/blob_takeover/main = new
main.owner = owner
@@ -47,20 +59,51 @@
icon_icon = 'icons/mob/blob.dmi'
button_icon_state = "blob"
-/datum/action/innate/blobpop/Activate()
+ /// The time taken before this ability is automatically activated.
+ var/autoplace_time = OVERMIND_STARTING_AUTO_PLACE_TIME
+
+/datum/action/innate/blobpop/Grant(Target)
+ . = ..()
+ if(owner)
+ addtimer(CALLBACK(src, .proc/Activate, TRUE), autoplace_time, TIMER_UNIQUE|TIMER_OVERRIDE)
+ to_chat(owner, "You will automatically pop and place your blob core in [DisplayTimeText(autoplace_time)].")
+
+/datum/action/innate/blobpop/Activate(timer_activated = FALSE)
var/mob/living/old_body = owner
+ if(!owner)
+ return
+
var/datum/antagonist/blob/blobtag = owner.mind.has_antag_datum(/datum/antagonist/blob)
if(!blobtag)
- Remove()
+ Remove(owner)
return
- var/mob/camera/blob/B = new /mob/camera/blob(get_turf(old_body), blobtag.starting_points_human_blob)
- owner.mind.transfer_to(B)
+
+ . = TRUE
+ var/turf/target_turf = get_turf(owner)
+ if(target_turf.density)
+ to_chat(owner, "This spot is too dense to place a blob core on!")
+ . = FALSE
+ var/area/target_area = get_area(target_turf)
+ if(isspaceturf(target_turf) || !(target_area?.area_flags & BLOBS_ALLOWED) || !is_station_level(target_turf.z))
+ to_chat(owner, "You cannot place your core here!")
+ . = FALSE
+
+ var/placement_override = BLOB_FORCE_PLACEMENT
+ if(!.)
+ if(!timer_activated)
+ return
+ placement_override = BLOB_RANDOM_PLACEMENT
+ to_chat(owner, "Because your current location is an invalid starting spot and you need to pop, you've been moved to a random location!")
+
+ var/mob/camera/blob/blob_cam = new /mob/camera/blob(get_turf(old_body), blobtag.starting_points_human_blob)
+ owner.mind.transfer_to(blob_cam)
old_body.gib()
- B.place_blob_core(placement_override = TRUE, pop_override = TRUE)
+ blob_cam.place_blob_core(placement_override, pop_override = TRUE)
+ playsound(get_turf(blob_cam), 'sound/ambience/antag/blobalert.ogg', 50, FALSE)
/datum/antagonist/blob/antag_listing_status()
. = ..()
if(owner?.current)
- var/mob/camera/blob/B = owner.current
- if(istype(B))
- . += "(Progress: [B.blobs_legit.len]/[B.blobwincount])"
+ var/mob/camera/blob/blob_cam = owner.current
+ if(istype(blob_cam))
+ . += "(Progress: [length(blob_cam.blobs_legit)]/[blob_cam.blobwincount])"
diff --git a/code/modules/antagonists/blob/overmind.dm b/code/modules/antagonists/blob/overmind.dm
index 5e7c5c59ef3..b843c7d9d89 100644
--- a/code/modules/antagonists/blob/overmind.dm
+++ b/code/modules/antagonists/blob/overmind.dm
@@ -114,7 +114,7 @@ GLOBAL_LIST_EMPTY(blob_nodes)
to_chat(src, span_big("You will automatically place your blob core in [DisplayTimeText(autoplace_max_time - world.time)]."))
manualplace_min_time = 0
if(autoplace_max_time && world.time >= autoplace_max_time)
- place_blob_core(1)
+ place_blob_core(BLOB_RANDOM_PLACEMENT)
else
qdel(src)
else if(!victory_in_progress && (blobs_legit.len >= blobwincount))
diff --git a/code/modules/antagonists/blob/powers.dm b/code/modules/antagonists/blob/powers.dm
index 7412cd5f687..46349c8e6bb 100644
--- a/code/modules/antagonists/blob/powers.dm
+++ b/code/modules/antagonists/blob/powers.dm
@@ -7,10 +7,10 @@
add_points(-cost)
return TRUE
-/mob/camera/blob/proc/place_blob_core(placement_override, pop_override = FALSE)
- if(placed && placement_override != -1)
+/mob/camera/blob/proc/place_blob_core(placement_override = BLOB_NORMAL_PLACEMENT, pop_override = FALSE)
+ if(placed && placement_override != BLOB_FORCE_PLACEMENT)
return TRUE
- if(!placement_override)
+ if(placement_override == BLOB_NORMAL_PLACEMENT)
if(!pop_override)
for(var/mob/living/M in range(7, src))
if(ROLE_BLOB in M.faction)
@@ -45,7 +45,7 @@
if(!pop_override && world.time <= manualplace_min_time && world.time <= autoplace_max_time)
to_chat(src, span_warning("It is too early to place your blob core!"))
return FALSE
- else if(placement_override == 1)
+ else if(placement_override == BLOB_RANDOM_PLACEMENT)
var/turf/T = pick(GLOB.blobstart)
forceMove(T) //got overrided? you're somewhere random, motherfucker
if(placed && blob_core)
diff --git a/config/dynamic.json b/config/dynamic.json
index 519796b79f1..c503697963f 100644
--- a/config/dynamic.json
+++ b/config/dynamic.json
@@ -1,252 +1,142 @@
{
- "Dynamic": {
- "threat_curve_centre": -3,
- "threat_curve_width": 1.4,
- "roundstart_split_curve_centre": 3,
- "roundstart_split_curve_width": 1.2,
- "midround_delay_min": 18000,
- "midround_delay_max": 30000,
- "latejoin_delay_min": 6000,
- "latejoin_delay_max": 15000
- },
- "Roundstart": {
- "Traitors": {
- "antag_cap": {
- "denominator": 40
- },
- "protected_roles": [
- "Prisoner",
- "Vanguard Operative",
- "Civil Disputes Officer",
- "Security Medic",
- "Security Sergeant",
- "Security Officer",
- "Corrections Officer",
- "Warden",
- "Detective",
- "Head of Security",
- "Blueshield",
- "Head of Personnel",
- "Quartermaster",
- "Chief Engineer",
- "Chief Medical Officer",
- "Research Director",
- "Captain"
- ],
- "restricted_roles": [
- "Cyborg"
- ]
- },
- "Blood Brothers": {
- "weight": 0
- },
- "Changelings": {
- "protected_roles": [
- "Prisoner",
- "Vanguard Operative",
- "Civil Disputes Officer",
- "Security Medic",
- "Security Sergeant",
- "Security Officer",
- "Corrections Officer",
- "Warden",
- "Detective",
- "Head of Security",
- "Blueshield",
- "Head of Personnel",
- "Quartermaster",
- "Chief Engineer",
- "Chief Medical Officer",
- "Research Director",
- "Captain"
- ],
- "restricted_roles": [
- "Cyborg"
- ]
- },
- "Heretics": {
- "weight": 2,
- "scaling_cost": 12,
- "protected_roles": [
- "Prisoner",
- "Vanguard Operative",
- "Civil Disputes Officer",
- "Security Medic",
- "Security Sergeant",
- "Security Officer",
- "Corrections Officer",
- "Warden",
- "Detective",
- "Head of Security",
- "Blueshield",
- "Head of Personnel",
- "Quartermaster",
- "Chief Engineer",
- "Chief Medical Officer",
- "Research Director",
- "Captain"
- ],
- "restricted_roles": [
- "Cyborg"
- ]
- },
- "Wizard": {
- "weight": 1,
- "protected_roles": [
- "Prisoner",
- "Vanguard Operative",
- "Civil Disputes Officer",
- "Security Medic",
- "Security Sergeant",
- "Security Officer",
- "Corrections Officer",
- "Warden",
- "Detective",
- "Head of Security",
- "Blueshield",
- "Head of Personnel",
- "Quartermaster",
- "Chief Engineer",
- "Chief Medical Officer",
- "Research Director",
- "Captain"
- ],
- "restricted_roles": [
- "Cyborg"
- ]
- },
- "Blood Cult": {
- "weight": 0
- },
- "Clockwork Cult": {
- "weight": 0
- },
- "Nuclear Emergency": {
- "weight": 0
- },
- "Revolution": {
- "weight": 0
- },
- "Families": {
- "weight": 0
- }
- },
- "Midround": {
- "Syndicate Sleeper Agent": {
- "protected_roles": [
- "Prisoner",
- "Vanguard Operative",
- "Civil Disputes Officer",
- "Security Medic",
- "Security Sergeant",
- "Security Officer",
- "Corrections Officer",
- "Warden",
- "Detective",
- "Head of Security",
- "Blueshield",
- "Head of Personnel",
- "Quartermaster",
- "Chief Engineer",
- "Chief Medical Officer",
- "Research Director",
- "Captain"
- ],
- "restricted_roles": [
- "Cyborg"
- ]
- },
- "Family Head Aspirants": {
- "weight": 0
- },
- "Nuclear Assault": {
- "weight": 0
- },
- "Space Dragon": {
- "weight": 1
- },
- "Blob": {
- "weight": 1
- },
- "Spiders": {
- "weight": 1
- },
- "Alien Infestation": {
- "weight": 1
- }
- },
- "Latejoin": {
- "Syndicate Infiltrator": {
- "protected_roles": [
- "Prisoner",
- "Vanguard Operative",
- "Civil Disputes Officer",
- "Security Medic",
- "Security Sergeant",
- "Security Officer",
- "Corrections Officer",
- "Warden",
- "Detective",
- "Head of Security",
- "Blueshield",
- "Head of Personnel",
- "Quartermaster",
- "Chief Engineer",
- "Chief Medical Officer",
- "Research Director",
- "Captain"
- ],
- "restricted_roles": [
- "Cyborg"
- ]
- },
- "Provocateur": {
- "protected_roles": [
- "Prisoner",
- "Vanguard Operative",
- "Civil Disputes Officer",
- "Security Medic",
- "Security Sergeant",
- "Security Officer",
- "Corrections Officer",
- "Warden",
- "Detective",
- "Head of Security",
- "Blueshield",
- "Head of Personnel",
- "Quartermaster",
- "Chief Engineer",
- "Chief Medical Officer",
- "Research Director",
- "Captain"
- ],
- "restricted_roles": [
- "Cyborg"
- ]
- },
- "Heretic Smuggler": {
- "protected_roles": [
- "Prisoner",
- "Vanguard Operative",
- "Civil Disputes Officer",
- "Security Medic",
- "Security Sergeant",
- "Security Officer",
- "Corrections Officer",
- "Warden",
- "Detective",
- "Head of Security",
- "Blueshield",
- "Head of Personnel",
- "Quartermaster",
- "Chief Engineer",
- "Chief Medical Officer",
- "Research Director",
- "Captain"
- ],
- "restricted_roles": [
- "Cyborg"
- ]
- }
- }
+ "Dynamic": {},
+ "Roundstart": {
+ "Traitors": {
+ "cost": 8,
+ "scaling_cost": 9,
+ "weight": 0,
+ "required_candidates": 1,
+ "minimum_required_age": 0,
+ "requirements": [
+ 10,
+ 10,
+ 10,
+ 10,
+ 10,
+ 10,
+ 10,
+ 10,
+ 10,
+ 10
+ ],
+ "antag_cap": {
+ "denominator": 24
+ },
+ "protected_roles": [
+ "Prisoner",
+ "Security Officer",
+ "Warden",
+ "Detective",
+ "Head of Security",
+ "Captain"
+ ],
+ "restricted_roles": [
+ "Cyborg"
+ ]
+ },
+
+ "Blood Brothers": {
+ "weight": 0
+ },
+
+ "Changelings": {
+ "weight": 0
+ },
+
+ "Heretics": {
+ "weight": 0
+ },
+
+ "Wizard": {
+ "weight": 0
+ },
+
+ "Blood Cult": {
+ "weight": 0
+ },
+
+ "Nuclear Emergency": {
+ "weight": 0
+ },
+
+ "Revolution": {
+ "weight": 0
+ },
+
+ "Families": {
+ "weight": 0
+ }
+ },
+
+ "Midround": {
+ "Syndicate Sleeper Agent": {
+ "weight": 0
+ },
+
+ "Family Head Aspirants": {
+ "weight": 0
+ },
+
+ "Malfunctioning AI": {
+ "weight": 0
+ },
+
+ "Wizard": {
+ "weight": 0
+ },
+
+ "Nuclear Assault": {
+ "weight": 0
+ },
+
+ "Blob": {
+ "weight": 0
+ },
+
+ "Blob Infection": {
+ "weight": 0
+ },
+
+ "Alien Infestation": {
+ "weight": 0
+ },
+
+ "Nightmare": {
+ "weight": 0
+ },
+
+ "Space Dragon": {
+ "weight": 0
+ },
+
+ "Abductors": {
+ "weight": 0
+ },
+
+ "Swarmers": {
+ "weight": 0
+ },
+
+ "Space Ninja": {
+ "weight": 0
+ },
+
+ "Spiders": {
+ "weight": 0
+ }
+ },
+
+ "Latejoin": {
+ "Syndicate Infiltrator": {
+ "weight": 0
+ },
+
+ "Provocateur": {
+ "weight": 0
+ },
+
+ "Heretic Smuggler": {
+ "weight": 0
+ }
+ }
}
diff --git a/sound/ambience/antag/blobalert.ogg b/sound/ambience/antag/blobalert.ogg
new file mode 100644
index 00000000000..c9fcfbee448
Binary files /dev/null and b/sound/ambience/antag/blobalert.ogg differ