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