Files
Leland Kemble d49c825f8e Bitrunning glitches take much longer to escape their simulation if there are remaining active bitrunners (#95934)
## About The Pull Request

For every living, connected bit avatar remaining in the simulation, the
`do_after()` for leaving the simulation as a bitrunning glitch ghost
role is multiplied by 5. The base timer remains at it was, two seconds.

Additionally, there is an exception in place for the "Island Brawl"
domain, which is the FFA with infinite respawns. It still multiplies by
5 for each active bitrunner, but that multiplier is reduced by 1 for
each death, so you likely only have to kill one wave of bitrunners for
normal escape time.

Additionally, because of necessity for the exception and also basic good
sense, bitrunning points are no longer component-based on specific turfs
in the domain, and are held in the server. It never should have been a
component, it had no reason to be one. This, as a consenquence, also
fixes that you could completely softlock any domain using points by
simply crowbarring the turf that the crate was yet to spawn on.

## Why It's Good For The Game

Yesterday, I saw one of the lamest things I've ever seen in the game. A
bitrunning glitch spawns in, walks literally right by two bitrunners as
they ask who he is, and leaves the simulation to go on a killing spree
unburdened. There was extenuating circumstances in that situation, but
it still was a miserable breakdown of what I think the intended gameplay
of a bitrunning glitch is, and shows how ridiculously easy it is to
simply not engage with the intended hurdle in your path. The
primary(and, really, only) job of a glitch is to kill bit avatars, and
this PR makes it significantly more difficult to just... walk by them to
go on your killing spree on station. Work, then play.

## Changelog
🆑

balance: For every living, connected bitrunner remaining in a domain,
bitrunning glitches will take five times longer to escape.
fix: You can no longer softlock any virtual domain that uses points by
crowbarring the reward turf before the crate spawns.

/🆑
2026-05-29 19:35:45 +02:00

137 lines
4.9 KiB
Plaintext

/**
* # Virtual Domains
* Create your own: Read the readme file in the '_maps/virtual_domains' folder.
*/
/datum/lazy_template/virtual_domain
map_dir = "_maps/virtual_domains"
map_name = "None"
key = "Virtual Domain"
place_on_top = TRUE
/// Whether to tell observers this map is being used
var/announce_to_ghosts = FALSE
/// The map file to load
var/filename = "virtual_domain.dmm"
/// The start time of the map. Used to calculate time taken
var/start_time
/**
* Generic settings / UI
*/
/// Cost of this map to load
var/cost = BITRUNNER_COST_NONE
/// The description of the map for the console UI
var/desc = "A map."
/// Affects the ui and ability to scan info.
var/difficulty = BITRUNNER_DIFFICULTY_NONE
/// Write these to help complete puzzles and other objectives. Viewed in the domain info ability.
var/help_text
// Name to show in the UI
var/name = "Virtual Domain"
/// Points to reward for completion. Used to purchase new domains and calculate ore rewards.
var/reward_points = BITRUNNER_REWARD_MIN
/// Any additional flags for this domain
var/domain_flags = NONE
/**
* Player customization
*/
/// Any outfit that you wish to force on avatars. Overrides preferences
var/datum/outfit/forced_outfit
/**
* Loot
*/
/// An assoc list of typepath/amount to spawn on completion. Not weighted - the value is the amount
var/list/completion_loot
/// An assoc list of typepath/amount to spawn from secondary objectives. Not weighted - the value is the total number of items that can be obtained.
var/list/secondary_loot = list()
/// Number of secondary loot boxes generated. Resets when the domain is reloaded.
var/secondary_loot_generated
/// Has this domain been beaten with high enough score to spawn a tech disk?
var/disk_reward_spawned = FALSE
/// The amount of points towards the spawning of the main crate, on maps using points.
var/main_crate_points = 0
/// The amount of points required to spawn the main crate, on maps using points.
var/main_crate_point_goal = 10
/// The location the crate will spawn when enough points are accumulated, on maps using points.
var/main_crate_loc
/**
* Modularity
*/
/// Whether to display this as a modular map
var/is_modular = FALSE
/// Byond will look for modular mob segment landmarks then choose from here at random. You can make them unique also.
var/list/datum/modular_mob_segment/mob_modules = list()
/// Forces all mob modules to only load once
var/modular_unique_mobs = FALSE
/**
* Spawning
*/
/// Looks for random landmarks to spawn on.
var/list/custom_spawns = list()
/// Set TRUE if you want reusable custom spawners
var/keep_custom_spawns = FALSE
/// The domain must have this many ghost candidates willing to join as entities, or else it will not load.
var/mission_min_candidates = 0
/// Maximum amount possible of above.
var/mission_max_candidates = 0
/// Ghosts that will be spawned as, presumably, an antagonist in the map.
var/list/chosen_ghosts
/// List of spawners used for candidates.
var/list/obj/effect/mob_spawn/ghost_role/ghost_spawners
/// Current domain mobs being held by ghosts
var/list/mob/living/ghost_mobs
/// The role that ghosts will get. Only used for poll text.
var/spawner_role = "Antagonist"
/datum/lazy_template/virtual_domain/proc/can_view_name(scanner_tier, server_points)
return difficulty < scanner_tier && cost <= server_points + 5
/datum/lazy_template/virtual_domain/proc/can_view_reward(scanner_tier, server_points)
return difficulty < (scanner_tier + 1) && cost <= server_points + 3
/datum/lazy_template/virtual_domain/Destroy(force)
QDEL_NULL(ghost_spawners)
QDEL_NULL(ghost_mobs)
. = ..()
/// Sends a point to any loot signals on the map
/datum/lazy_template/virtual_domain/proc/add_points(points_to_add = 1)
main_crate_points += points_to_add
if(main_crate_points >= main_crate_point_goal)
reveal()
/datum/lazy_template/virtual_domain/proc/reveal()
if(!main_crate_loc)
return
var/turf/spawn_loc = get_turf(main_crate_loc)
playsound(spawn_loc, 'sound/effects/magic/blink.ogg', 50, TRUE)
var/obj/structure/closet/crate/secure/bitrunning/encrypted/crate = new()
crate.forceMove(spawn_loc) // Triggers any on-move effects on that turf
do_sparks(5, FALSE, spawn_loc, spark_type = /datum/effect_system/basic/spark_spread/quantum)
main_crate_loc = null
/// Loads the ghost candidates.
/datum/lazy_template/virtual_domain/proc/load_advanced_npcs(list/mob/lucky_ghosts)
for(var/mob/lucky_ghost as anything in lucky_ghosts)
var/obj/effect/mob_spawn/ghost_role/ghost_spawner = pick(ghost_spawners)
LAZYREMOVE(ghost_spawners, ghost_spawner)
var/mob/new_mob = ghost_spawner.create(lucky_ghost, lucky_ghost.real_name)
LAZYADD(ghost_mobs, new_mob)
var/ghostname = lucky_ghost.name
notify_ghosts("[ghostname] has been selected to be a [ghost_spawner.prompt_name]!", source = new_mob, header = "001010110")
/// Overridable proc to be called after the map is loaded.
/datum/lazy_template/virtual_domain/proc/setup_domain(list/created_atoms)
return