From 296861121234a33e993016a70de8c9ca353d53a5 Mon Sep 17 00:00:00 2001 From: Spock Date: Sat, 25 May 2024 13:01:01 -0700 Subject: [PATCH] Rebalances Hivemind Special Options (#5287) * Rebalances Hivemind Special Options * Fixes a wrong proc I used * Makes the gibbing toggle into a float for how long a warning is given * Backend code improvements, make Hive turrets shoot again * Gives hive turrets IFF and makes them not shoot corpses * Fixes a proc --- .../gamemodes/events/hivemind_controller.dm | 17 ++-- code/game/objects/structures/burrows.dm | 2 +- code/modules/hivemind/machines.dm | 83 +++++++++++++++++-- code/modules/hivemind/wires.dm | 36 +++++--- 4 files changed, 113 insertions(+), 25 deletions(-) diff --git a/code/game/gamemodes/events/hivemind_controller.dm b/code/game/gamemodes/events/hivemind_controller.dm index bb53ffdf0e5..e69eda15a8a 100644 --- a/code/game/gamemodes/events/hivemind_controller.dm +++ b/code/game/gamemodes/events/hivemind_controller.dm @@ -6,14 +6,14 @@ GLOBAL_LIST_INIT(hive_data_bool, list( "allow_tyrant_spawn" = TRUE, "tyrant_death_kills_hive" = FALSE, "all_church_to_battle" = FALSE, - "prevent_gibbing_dead" = TRUE, "pop_lock" = TRUE, "slime_pop_lock" = TRUE)) GLOBAL_LIST_INIT(hive_data_float, list( "maximum_controlled_areas" = 0, // Stop expansion when controlling certain number of areas, 0 to disable "maximum_existing_mobs" = 50, // Should be true in "hive_data_bool" to take effect, 0 means no hive mob spawn (except champions) - "core_oddity_drop_chance" = 50)) // prob() of hive node leaving hive-themed oddity on death + "gibbing_warning_timer" = 0, //How many seconds of warning should be given before a humanoid body is gibbed by hivemind wires. 0 means it doesn't at all + "core_oddity_drop_chance" = 50,)) // prob() of hive node leaving hive-themed oddity on death GLOBAL_LIST_INIT(hive_names, list("Von Neumann", "Lazarus", "Abattoir", "Auto-Surgeon", "NanoTrasen", "NanoNurse", "Vivisector", "Ex Costa", "Apostasy", "Gnosis", "Balaam", "Ophite", @@ -92,6 +92,9 @@ GLOBAL_VAR_INIT(hivemind_panel, new /datum/hivemind_panel) data += "
Core oddity drop chance: [GLOB.hive_data_float["core_oddity_drop_chance"]] \ \[SET\]" + data += "
How Long before Gibbing Humans? (0 is disabled): [GLOB.hive_data_float["gibbing_warning_timer"]] \ + \[SET\]" + data += "
Spread trough burrows: [GLOB.hive_data_bool["spread_trough_burrows"] ? "Enabled" : "Disabled"] \ \[TOGGLE\]" @@ -110,9 +113,6 @@ GLOBAL_VAR_INIT(hivemind_panel, new /datum/hivemind_panel) data += "
All Church To Inquisitors: [GLOB.hive_data_bool["all_church_to_battle"] ? "Enabled" : "Disabled"] \ \[TOGGLE\]" - data += "
Prevent Hivemind Gibbing Dead Victims: [GLOB.hive_data_bool["prevent_gibbing_dead"] ? "Enabled" : "Disabled"] \ - \[TOGGLE\]" - data += "
Prevent Hivemind Events Below 7 Pop: [GLOB.hive_data_bool["pop_lock"] ? "Enabled" : "Disabled"] \ \[TOGGLE\]" @@ -148,6 +148,10 @@ GLOBAL_VAR_INIT(hivemind_panel, new /datum/hivemind_panel) var/percent = input(usr, "Percentage probability of hive node dropping oddity on destruction", "Rigging gacha") as null|num GLOB.hive_data_float["core_oddity_drop_chance"] = CLAMP(percent ? percent : 0, 0, 100) + if(href_list["set_gibbing_warning_timer"]) + var/timer = input(usr, "Time in seconds before human bodies are destroyed on wires, 0 to disable", "Time in Seconds") as null|num + GLOB.hive_data_float["gibbing_warning_timer"] = CLAMP(timer ? timer : 0, 0, 300) + if(href_list["set_name"]) var/name = input(usr, "Choose wisely", "Hivemind name") as null|anything in GLOB.hive_names if(name) @@ -204,9 +208,6 @@ GLOBAL_VAR_INIT(hivemind_panel, new /datum/hivemind_panel) if(href_list["toggle_tyrant_gameover"]) GLOB.hive_data_bool["tyrant_death_kills_hive"] = !GLOB.hive_data_bool["tyrant_death_kills_hive"] - if(href_list["toggle_prevent_gibbing_dead"]) - GLOB.hive_data_bool["prevent_gibbing_dead"] = !GLOB.hive_data_bool["prevent_gibbing_dead"] - if(href_list["toggle_pop_lock"]) GLOB.hive_data_bool["pop_lock"] = !GLOB.hive_data_bool["pop_lock"] diff --git a/code/game/objects/structures/burrows.dm b/code/game/objects/structures/burrows.dm index 21e2f3114d8..ced29da3332 100755 --- a/code/game/objects/structures/burrows.dm +++ b/code/game/objects/structures/burrows.dm @@ -708,7 +708,7 @@ percentage is a value in the range 0..1 that determines what portion of this mob if(locate(/obj/effect/plant) in loc) return - if(!hive_mind_ai || !hive_mind_ai.hives.len || maintenance || !GLOB.hive_data_bool["spread_trough_burrows"]) + if(!hive_mind_ai || !hive_mind_ai.hives.len || !GLOB.hive_data_bool["spread_trough_burrows"]) return var/area/A = get_area(src) diff --git a/code/modules/hivemind/machines.dm b/code/modules/hivemind/machines.dm index 7e545137c3e..ca74d2f3431 100644 --- a/code/modules/hivemind/machines.dm +++ b/code/modules/hivemind/machines.dm @@ -1,6 +1,9 @@ //Hivemind various machines #define REGENERATION_SPEED 4 +#define TURRET_PRIORITY_TARGET 2 +#define TURRET_SECONDARY_TARGET 1 +#define TURRET_NOT_TARGET 0 @@ -446,19 +449,86 @@ icon_state = "turret" cooldown_time = 1 SECONDS spawn_weight = 60 + var/firing_range = 7 + var/last_target //last target fired at, prevents turrets from erratically firing at all valid targets in range + var/shot_delay = 30 //3 seconds between each shot by default, gets faster with evo level + var/last_fired = 0 //1: if the turret is cooling down from a shot, 0: turret is ready to fire var/proj_type = /obj/item/projectile/goo -/obj/machinery/hivemind_machine/turret/Process() +/obj/machinery/hivemind_machine/turret/Process() //Copied mostly from porta_turret code now if(!..()) return - var/mob/living/target = locate() in all_mobs_in_view(world.view, src) - if(target && is_attackable(target) && target.faction != HIVE_FACTION) - use_ability(target) - set_cooldown() + var/list/targets = list() //Primary targets + var/list/secondarytargets = list() //targets that are less important + + shot_delay = max(30 - hive_mind_ai.evo_level * 5, 5) //Scales at half a second faster for each evo level, minimum half a second + + for(var/mob/living/M in view(firing_range, src)) + assess_and_assign(M, targets, secondarytargets) + + for(var/obj/mecha/mech in GLOB.mechas_list) + if (mech.z == z && (get_dist(mech, src) < firing_range) && can_see(src, mech, firing_range)) + var/mob/living/occupant = mech.get_mob() + if (occupant) + assess_and_assign(occupant, targets, secondarytargets) + + if(!tryToShootAt(targets)) + tryToShootAt(secondarytargets) + +/obj/machinery/hivemind_machine/turret/proc/assess_and_assign(var/mob/living/L, var/list/targets, var/list/secondarytargets) + switch(assess_living(L)) + if(TURRET_PRIORITY_TARGET) + targets += L + if(TURRET_SECONDARY_TARGET) + secondarytargets += L + +/obj/machinery/hivemind_machine/turret/proc/assess_living(var/mob/living/L) + if(L.faction == "hive") //Don't shoot hive mobs + return TURRET_NOT_TARGET + if(L.stat == DEAD) + return TURRET_NOT_TARGET //Don't shoot the dead either + if(L.lying) //Lying down people are lower priority to shoot + return TURRET_SECONDARY_TARGET + return TURRET_PRIORITY_TARGET //If you ain't hive or lying, you're a priority target + +/obj/machinery/hivemind_machine/turret/proc/tryToShootAt(var/list/mob/living/targets) + if(targets.len && last_target && (last_target in targets) && target(last_target)) + return TRUE + + while(targets.len > 0) + var/mob/living/M = pick(targets) + targets -= M + if(target(M)) + return TRUE + +/obj/machinery/hivemind_machine/turret/proc/target(var/mob/living/target) + if(target) + last_target = target + set_dir(get_dir(src, target)) //even if you can't shoot, follow the target + spawn() + shootAt(target) + return TRUE + return + +/obj/machinery/hivemind_machine/turret/proc/shootAt(var/mob/living/target) + if(last_fired) //prevents rapid-fire shooting + return + last_fired = 1 + spawn() + sleep(shot_delay) + last_fired = 0 + + var/turf/T = get_turf(src) + var/turf/U = get_turf(target) + if(!istype(T) || !istype(U)) + return + + use_ability(target) /obj/machinery/hivemind_machine/turret/use_ability(atom/target) var/obj/item/projectile/proj = new proj_type(loc) + proj.faction_iff = "hive" //We get some iff so we're not used as a weapon against the hive... we should pretty much always have mobs attacking! proj.launch(target) playsound(src, 'sound/effects/blobattack.ogg', 70, 1) @@ -724,3 +794,6 @@ #undef REGENERATION_SPEED +#undef TURRET_PRIORITY_TARGET +#undef TURRET_SECONDARY_TARGET +#undef TURRET_NOT_TARGET diff --git a/code/modules/hivemind/wires.dm b/code/modules/hivemind/wires.dm index ba3d7177090..d83df020af7 100644 --- a/code/modules/hivemind/wires.dm +++ b/code/modules/hivemind/wires.dm @@ -12,6 +12,7 @@ var/obj/machinery/hivemind_machine/node/master_node var/list/wires_connections = list("0", "0", "0", "0") var/my_area + var/assimilation_timer /obj/effect/plant/hivemind/New() ..() @@ -35,6 +36,10 @@ GLOB.hivemind_areas.Remove(my_area) return ..() +/obj/effect/plant/hivemind/die_off() + if(assimilation_timer) + deltimer(assimilation_timer) + return ..() /obj/effect/plant/hivemind/after_spread(obj/effect/plant/child, turf/target_turf) if(master_node) @@ -378,23 +383,23 @@ //Corpse reanimation if(isliving(subject) && !ishivemindmob(subject)) //human bodies - if(ishuman(subject)) + if(ishuman(subject) && !assimilation_timer) var/mob/living/L = subject - if(GLOB.hive_data_bool["prevent_gibbing_dead"]) - //We we dont touch the dead via are controler we dont want to pk people form the round + if(!GLOB.hive_data_float["gibbing_warning_timer"]) //If the value is set to 0 (the default) we don't touch player humans return - //if our target has cruciform, let's just leave it - if(is_neotheology_disciple(L)) + var/timer = GLOB.hive_data_float["gibbing_warning_timer"] SECONDS //If we've continued, then there's a value there and we want it in seconds + + if(is_neotheology_disciple(L)) //If our target has a cruciform, we don't touch it return - for(var/obj/item/W in L) - L.drop_from_inventory(W) - var/M = pick(/mob/living/simple_animal/hostile/hivemind/himan, /mob/living/simple_animal/hostile/hivemind/phaser) - new M(loc) + visible_message("Wires begin to wreathe around [L], starting the process of converting them into part of the hivemind.") //We tell people to get them off the wires + assimilation_timer = addtimer(CALLBACK(src, .proc/assimilate_human, L), timer, TIMER_STOPPABLE) + return + //robot corpses - else if(issilicon(subject)) + else if(issilicon(subject)) //If you're a borg... sucks to suck? I don't feel like reworking this, you're too mechanical to prevent hivemind taking over you new /mob/living/simple_animal/hostile/hivemind/hiborg(loc) //other dead bodies else @@ -403,6 +408,15 @@ qdel(subject) +/obj/effect/plant/hivemind/proc/assimilate_human(var/mob/living/L) + if(!locate(/obj/effect/plant/hivemind) in L.loc || !(L.stat == DEAD)) //If we don't see any wires after the alotted time or we're alive again, we don't get got + return + for(var/obj/item/W in L) + L.drop_from_inventory(W) + var/M = pick(/mob/living/simple_animal/hostile/hivemind/himan, /mob/living/simple_animal/hostile/hivemind/phaser) + new M(loc) + + L.dust() ////////////////////////////////////////////////////////////////// /////////////////////////>RESPONSE CODE