mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-06-22 06:35:12 +01:00
dec8c784f6
## About The Pull Request When bitrunners attempt to leave their domain via the ladder, they will fail if there is an active, living bitrunning glitch antagonist within the domain. Other methods of leaving such as death or taking the red pill are unaffected. they can also leave if the crate's delivered sorta a sister PR to #95934 ## Why It's Good For The Game enter domain -> get alert about bitrunning glitch -> leave -> turn off domain What a fun gameplay loop, for everyone involved. You'll fight the glitch and you'll like it. (liking it [pending](https://github.com/tgstation/tgstation/pull/96138)) You can still leave if you REALLY, REALLY need to at the cost of a little brain damage, but otherwise the literal only obstacle that will ever be in your path should be something you have to engage with. ## Changelog 🆑 balance: Bitrunners can't back out of domains via the ladder while bitrunning glitches remain in them /🆑
248 lines
8.1 KiB
Plaintext
248 lines
8.1 KiB
Plaintext
#define POLLING_COOLDOWN_TIME 2 MINUTES
|
|
|
|
/// Gives all current occupants a notification that the server is going down
|
|
/obj/machinery/quantum_server/proc/begin_shutdown(mob/user)
|
|
if(isnull(generated_domain))
|
|
return
|
|
|
|
if(!length(avatar_connection_refs))
|
|
balloon_alert_to_viewers("powering down domain...")
|
|
playsound(src, 'sound/machines/terminal/terminal_off.ogg', 40, vary = TRUE)
|
|
reset()
|
|
return
|
|
|
|
balloon_alert_to_viewers("notifying clients...")
|
|
playsound(src, 'sound/machines/terminal/terminal_alert.ogg', 100, vary = TRUE)
|
|
user.visible_message(
|
|
span_danger("[user] begins depowering the server!"),
|
|
span_notice("You start disconnecting clients..."),
|
|
span_danger("You hear frantic keying on a keyboard."),
|
|
)
|
|
|
|
SEND_SIGNAL(src, COMSIG_BITRUNNER_SHUTDOWN_ALERT, user)
|
|
|
|
if(!do_after(user, 20 SECONDS, src))
|
|
return
|
|
|
|
reset()
|
|
|
|
|
|
/// Links all the loading processes together - does validation for booting a map
|
|
/obj/machinery/quantum_server/proc/cold_boot_map(map_key, was_random_selection)
|
|
if(!is_ready)
|
|
return FALSE
|
|
|
|
if(isnull(map_key))
|
|
balloon_alert_to_viewers("no domain specified!")
|
|
return FALSE
|
|
|
|
if(generated_domain)
|
|
balloon_alert_to_viewers("stop the current domain first!")
|
|
return FALSE
|
|
|
|
if(length(avatar_connection_refs))
|
|
balloon_alert_to_viewers("all clients must disconnect!")
|
|
return FALSE
|
|
|
|
is_ready = FALSE
|
|
playsound(src, 'sound/machines/terminal/terminal_processing.ogg', 30, 2)
|
|
|
|
/// If any one of these fail, it reverts the entire process
|
|
if(!load_domain(map_key) || !load_map_items() || !load_mob_segments())
|
|
balloon_alert_to_viewers("initialization failed!")
|
|
scrub_vdom()
|
|
is_ready = TRUE
|
|
return FALSE
|
|
|
|
|
|
//We will want to record how the domain was selected. Either entirely randomly, with the name redacted, or with full information.
|
|
//Without this, it is difficult to determine what domains are selected more often intentionallly, vs unintentionally.
|
|
var/selection_type
|
|
|
|
if(was_random_selection)
|
|
selection_type = "random selection"
|
|
else
|
|
if(generated_domain.can_view_name(scanner_tier, points))
|
|
selection_type = "full information"
|
|
else
|
|
selection_type = "redacted information"
|
|
SSblackbox.record_feedback("nested tally", "bitrunning_domain_loaded", 1, list(selection_type, map_key))
|
|
|
|
is_ready = TRUE
|
|
|
|
var/spawn_chance = clamp((threat * glitch_chance), 5, threat_prob_max)
|
|
if(prob(spawn_chance))
|
|
setup_glitch()
|
|
|
|
playsound(src, 'sound/machines/terminal/terminal_insert_disc.ogg', 30, vary = TRUE)
|
|
balloon_alert_to_viewers("domain loaded.")
|
|
generated_domain.start_time = world.time
|
|
points -= generated_domain.cost
|
|
update_use_power(ACTIVE_POWER_USE)
|
|
update_appearance()
|
|
|
|
if(broadcasting)
|
|
start_broadcasting_network(BITRUNNER_CAMERA_NET)
|
|
|
|
if(generated_domain.announce_to_ghosts)
|
|
notify_ghosts("Bitrunners have loaded a domain that offers ghost interactions. Check the spawners menu for more information.",
|
|
src,
|
|
"Matrix Glitch",
|
|
)
|
|
|
|
return TRUE
|
|
|
|
|
|
/// Initializes a new domain if the given key is valid and the user has enough points
|
|
/obj/machinery/quantum_server/proc/load_domain(map_key)
|
|
for(var/datum/lazy_template/virtual_domain/available in SSbitrunning.all_domains)
|
|
if(map_key == available.key && points >= available.cost)
|
|
generated_domain = available
|
|
break
|
|
|
|
if(!generated_domain)
|
|
return FALSE
|
|
|
|
if(generated_domain.mission_min_candidates && (!COOLDOWN_FINISHED(src, polling_cooldown)))
|
|
say("Advanced NPC algorithms resetting, please wait [DisplayTimeText(polling_cooldown)] or load a different domain.")
|
|
playsound(src, "sound/machines/buzz-[pick("sigh", "two")].ogg", 50, TRUE)
|
|
return FALSE
|
|
|
|
var/list/mob/lucky_ghosts
|
|
if(generated_domain.mission_min_candidates)
|
|
playsound(src, 'sound/machines/chime.ogg', 50, TRUE)
|
|
say("Loading advanced NPCs...")
|
|
var/list/mob/candidates = SSpolling.poll_ghost_candidates("Do you want to play as a virtual [generated_domain.spawner_role] in a bitrunner domain?", ROLE_GHOST_ROLE, ROLE_GHOST_ROLE, 15 SECONDS, POLL_IGNORE_SHUTTLE_DENIZENS, TRUE)
|
|
for(var/amount in 1 to generated_domain.mission_max_candidates)
|
|
if(length(candidates)) // If no candidates, fails in code below anyways
|
|
LAZYADD(lucky_ghosts, pick_n_take(candidates))
|
|
|
|
if(length(lucky_ghosts) < generated_domain.mission_min_candidates)
|
|
notify_ghosts("Not enough candidates for [generated_domain.spawner_role]! Aborting mission!")
|
|
playsound(src, "sound/machines/buzz-[pick("sigh", "two")].ogg", 50, TRUE)
|
|
say("Error! Unable to load advanced NPCs. Please try again or select different domain.")
|
|
COOLDOWN_START(src, polling_cooldown, POLLING_COOLDOWN_TIME)
|
|
return FALSE
|
|
|
|
playsound(src, 'sound/machines/ping.ogg', 50, TRUE)
|
|
say("Success!")
|
|
|
|
generated_domain.load_advanced_npcs(lucky_ghosts)
|
|
RegisterSignal(generated_domain, COMSIG_LAZY_TEMPLATE_LOADED, PROC_REF(on_template_loaded))
|
|
generated_domain.lazy_load()
|
|
|
|
return TRUE
|
|
|
|
/// Loads in necessary map items like hololadder spawns, caches, etc
|
|
/obj/machinery/quantum_server/proc/load_map_items()
|
|
var/turf/goal_turfs = list()
|
|
var/turf/cache_turfs = list()
|
|
var/turf/curiosity_turfs = list()
|
|
|
|
for(var/obj/effect/landmark/bitrunning/thing in GLOB.landmarks_list)
|
|
if(istype(thing, /obj/effect/landmark/bitrunning/hololadder_spawn))
|
|
exit_turfs += get_turf(thing)
|
|
qdel(thing) // i'm worried about multiple servers getting confused so lets clean em up
|
|
continue
|
|
|
|
if(istype(thing, /obj/effect/landmark/bitrunning/cache_goal_turf))
|
|
var/turf/tile = get_turf(thing)
|
|
goal_turfs += tile
|
|
RegisterSignal(tile, COMSIG_ATOM_ENTERED, PROC_REF(on_goal_turf_entered))
|
|
RegisterSignal(tile, COMSIG_ATOM_EXAMINE, PROC_REF(on_goal_turf_examined))
|
|
qdel(thing)
|
|
continue
|
|
|
|
if(istype(thing, /obj/effect/landmark/bitrunning/cache_spawn))
|
|
cache_turfs += get_turf(thing)
|
|
qdel(thing)
|
|
continue
|
|
|
|
if(istype(thing, /obj/effect/mob_spawn))
|
|
generated_domain.ghost_spawners += thing
|
|
continue
|
|
|
|
if(istype(thing, /obj/effect/landmark/bitrunning/curiosity_spawn))
|
|
curiosity_turfs += get_turf(thing)
|
|
qdel(thing)
|
|
continue
|
|
|
|
if(istype(thing, /obj/effect/landmark/bitrunning/loot_signal))
|
|
generated_domain.main_crate_loc = thing.loc
|
|
qdel(thing)
|
|
continue
|
|
|
|
if(istype(thing, /obj/effect/landmark/bitrunning/permanent_exit))
|
|
var/turf/tile = get_turf(thing)
|
|
exit_turfs += tile
|
|
qdel(thing)
|
|
|
|
new /obj/structure/hololadder(tile)
|
|
|
|
if(!length(exit_turfs))
|
|
CRASH("Failed to find exit turfs on generated domain.")
|
|
if(!length(goal_turfs))
|
|
CRASH("Failed to find send turfs on generated domain.")
|
|
if(!attempt_spawn_cache(cache_turfs))
|
|
return FALSE
|
|
|
|
while(length(curiosity_turfs))
|
|
var/turf/picked_turf = attempt_spawn_curiosity(curiosity_turfs)
|
|
if(!picked_turf)
|
|
break
|
|
generated_domain.secondary_loot_generated += 1
|
|
curiosity_turfs -= picked_turf
|
|
|
|
return TRUE
|
|
|
|
|
|
/// Stops the current virtual domain and disconnects all users
|
|
/obj/machinery/quantum_server/proc/reset(fast = FALSE)
|
|
is_ready = FALSE
|
|
domain_complete = FALSE
|
|
|
|
sever_connections()
|
|
|
|
if(!fast)
|
|
notify_spawned_threats()
|
|
addtimer(CALLBACK(src, PROC_REF(scrub_vdom)), 15 SECONDS, TIMER_UNIQUE|TIMER_STOPPABLE)
|
|
else
|
|
scrub_vdom() // used in unit testing, no need to wait for callbacks
|
|
|
|
addtimer(CALLBACK(src, PROC_REF(cool_off)), ROUND_UP(server_cooldown_time * capacitor_coefficient), TIMER_UNIQUE|TIMER_STOPPABLE|TIMER_DELETE_ME)
|
|
update_appearance()
|
|
|
|
update_use_power(IDLE_POWER_USE)
|
|
domain_randomized = FALSE
|
|
retries_spent = 0
|
|
|
|
stop_broadcasting_network(BITRUNNER_CAMERA_NET)
|
|
|
|
|
|
/// Tries to clean up everything in the domain
|
|
/obj/machinery/quantum_server/proc/scrub_vdom()
|
|
sever_connections() /// just in case someone's connected
|
|
SEND_SIGNAL(src, COMSIG_BITRUNNER_DOMAIN_SCRUBBED) // avatar cleanup just in case
|
|
|
|
if(length(generated_domain.reservations))
|
|
var/datum/turf_reservation/res = generated_domain.reservations[1]
|
|
res.Release()
|
|
|
|
var/list/creatures = spawned_threat_refs + mutation_candidate_refs
|
|
for(var/datum/weakref/creature_ref as anything in creatures)
|
|
var/mob/living/creature = creature_ref?.resolve()
|
|
if(isnull(creature))
|
|
continue
|
|
|
|
qdel(creature)
|
|
|
|
generated_domain.secondary_loot_generated = 0
|
|
|
|
avatar_connection_refs.Cut()
|
|
exit_turfs = list()
|
|
generated_domain = null
|
|
mutation_candidate_refs.Cut()
|
|
spawned_threat_refs.Cut()
|
|
|
|
#undef POLLING_COOLDOWN_TIME
|