From 235ebba7e0be6b50abc89cc0766c7cc29aebd173 Mon Sep 17 00:00:00 2001
From: Jeremiah <42397676+jlsnow301@users.noreply.github.com>
Date: Mon, 9 Oct 2023 00:26:16 -0400
Subject: [PATCH] Bitrunning hotfix 3 (#78818)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## About The Pull Request
The next round of small changes to how bitrunning works - mostly from
feedback, bug reports etc.
- The loot crate delivery spot is now a buildable machine (the
byteforge), making it replaceable in the event of a disaster
- Same for netpods and quantum consoles. These boards are now
researchable and buildable.
- New icons for the byteforge and the health monitor
- Some bug fixes around despawning avatars
- Reimplements one of the bitrunning unit tests
Pictures ⬇️
Host monitor

Byteforge

Spawning a crate

## Why It's Good For The Game
Bitrunning bug fixes and personal requests
Fixes #78571
Fixes an issue reported in discord - players stuck as gondola spawn
## Changelog
:cl:
fix: Added extra checks to bitrunning domain cleanup so avatars are
deleted properly.
add: Quantum servers now look for a new machine called a byteforge to
spawn loot on- no longer on an invisible landmark. This should make the
rooms rebuildable after disasters.
add: *Most* bitrunning machinery is now researchable and buildable via
circuits in the engineering protolathe.
/:cl:
---
_maps/map_files/Birdshot/birdshot.dmm | 16 +---
.../map_files/Deltastation/DeltaStation2.dmm | 25 ++---
.../map_files/IceBoxStation/IceBoxStation.dmm | 15 +--
_maps/map_files/MetaStation/MetaStation.dmm | 14 +--
_maps/map_files/NorthStar/north_star.dmm | 27 ++----
_maps/map_files/debug/runtimestation.dmm | 6 +-
_maps/map_files/tramstation/tramstation.dmm | 14 +--
.../dcs/signals/signals_bitrunning.dm | 3 +
.../circuitboards/computer_circuitboards.dm | 5 -
.../machines/machine_circuitboards.dm | 18 ----
.../components/avatar_connection.dm | 4 +-
code/modules/bitrunning/designs.dm | 87 ++++++++++++++++++
code/modules/bitrunning/objects/byteforge.dm | 55 +++++++++++
.../bitrunning/objects/host_monitor.dm | 4 +-
code/modules/bitrunning/objects/landmarks.dm | 5 -
code/modules/bitrunning/objects/netpod.dm | 30 +++---
.../objects/{bit_vendor.dm => vendor.dm} | 0
code/modules/bitrunning/server/loot.dm | 12 ++-
.../modules/bitrunning/server/map_handling.dm | 5 +-
.../bitrunning/server/quantum_server.dm | 7 +-
code/modules/bitrunning/server/util.dm | 20 ++--
code/modules/unit_tests/_unit_tests.dm | 1 +
code/modules/unit_tests/bitrunning.dm | 15 +++
icons/obj/device.dmi | Bin 84915 -> 85414 bytes
icons/obj/machines/bitrunning.dmi | Bin 8248 -> 23871 bytes
tgstation.dme | 4 +-
26 files changed, 238 insertions(+), 154 deletions(-)
create mode 100644 code/modules/bitrunning/designs.dm
create mode 100644 code/modules/bitrunning/objects/byteforge.dm
rename code/modules/bitrunning/objects/{bit_vendor.dm => vendor.dm} (100%)
create mode 100644 code/modules/unit_tests/bitrunning.dm
diff --git a/_maps/map_files/Birdshot/birdshot.dmm b/_maps/map_files/Birdshot/birdshot.dmm
index 052aee9740c..0711e8dc96a 100644
--- a/_maps/map_files/Birdshot/birdshot.dmm
+++ b/_maps/map_files/Birdshot/birdshot.dmm
@@ -32991,15 +32991,10 @@
/turf/open/floor/iron,
/area/station/security/checkpoint/escape)
"lLq" = (
-/obj/effect/turf_decal/box/corners{
- dir = 1
- },
-/obj/effect/turf_decal/box/corners{
- dir = 4
- },
/obj/machinery/firealarm/directional/east,
/obj/effect/decal/cleanable/oil,
-/obj/effect/landmark/bitrunning/station_reward_spawn,
+/obj/machinery/byteforge,
+/obj/effect/turf_decal/box,
/turf/open/floor/iron/dark/smooth_large,
/area/station/bitrunning/den)
"lLr" = (
@@ -37938,13 +37933,10 @@
/turf/open/floor/circuit/telecomms/mainframe,
/area/station/science/xenobiology)
"nyf" = (
-/obj/effect/turf_decal/box/corners{
- dir = 8
- },
-/obj/effect/turf_decal/box/corners,
/obj/machinery/camera/autoname/directional/east,
/obj/effect/decal/cleanable/dirt,
-/obj/effect/landmark/bitrunning/station_reward_spawn,
+/obj/structure/table,
+/obj/item/food/cornchips/green,
/turf/open/floor/iron/dark/smooth_large,
/area/station/bitrunning/den)
"nyi" = (
diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm
index ff106565a42..7ab412a4f3d 100644
--- a/_maps/map_files/Deltastation/DeltaStation2.dmm
+++ b/_maps/map_files/Deltastation/DeltaStation2.dmm
@@ -10151,12 +10151,9 @@
/turf/open/floor/iron/dark,
/area/station/command/heads_quarters/hop)
"cwd" = (
-/obj/effect/turf_decal/box/corners,
-/obj/effect/turf_decal/box/corners{
- dir = 4
- },
/obj/machinery/light/directional/south,
-/obj/effect/landmark/bitrunning/station_reward_spawn,
+/obj/machinery/byteforge,
+/obj/effect/turf_decal/box,
/turf/open/floor/iron/dark/smooth_large,
/area/station/bitrunning/den)
"cwe" = (
@@ -39286,15 +39283,15 @@
/turf/open/floor/iron/white,
/area/station/command/heads_quarters/cmo)
"jKY" = (
-/obj/effect/turf_decal/box/corners{
- dir = 8
- },
-/obj/effect/turf_decal/box/corners{
- dir = 1
- },
/obj/effect/decal/cleanable/oil/streak,
/obj/machinery/camera/directional/south,
-/obj/effect/landmark/bitrunning/station_reward_spawn,
+/obj/structure/table,
+/obj/item/storage/toolbox/mechanical{
+ pixel_y = 7
+ },
+/obj/item/reagent_containers/cup/soda_cans/space_mountain_wind{
+ pixel_x = 5
+ },
/turf/open/floor/iron/dark/smooth_large,
/area/station/bitrunning/den)
"jLa" = (
@@ -86574,10 +86571,6 @@
dir = 1
},
/obj/effect/decal/cleanable/dirt/dust,
-/obj/item/storage/toolbox/mechanical{
- pixel_x = -8;
- pixel_y = 17
- },
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/obj/structure/cable,
/obj/machinery/holopad,
diff --git a/_maps/map_files/IceBoxStation/IceBoxStation.dmm b/_maps/map_files/IceBoxStation/IceBoxStation.dmm
index 6aa6f438643..172a001020e 100644
--- a/_maps/map_files/IceBoxStation/IceBoxStation.dmm
+++ b/_maps/map_files/IceBoxStation/IceBoxStation.dmm
@@ -10091,15 +10091,10 @@
/area/mine/laborcamp)
"cWa" = (
/obj/machinery/light_switch/directional/west,
-/obj/effect/turf_decal/box/corners{
- dir = 1
- },
-/obj/effect/turf_decal/box/corners{
- dir = 4
- },
/obj/effect/decal/cleanable/cobweb,
/obj/effect/decal/cleanable/dirt/dust,
-/obj/effect/landmark/bitrunning/station_reward_spawn,
+/obj/machinery/byteforge,
+/obj/effect/turf_decal/box,
/turf/open/floor/iron/dark/smooth_large,
/area/station/bitrunning/den)
"cWq" = (
@@ -62098,11 +62093,7 @@
/turf/open/floor/plating/snowed/icemoon,
/area/icemoon/underground/explored)
"sVW" = (
-/obj/effect/turf_decal/box/corners{
- dir = 8
- },
-/obj/effect/turf_decal/box/corners,
-/obj/effect/landmark/bitrunning/station_reward_spawn,
+/obj/effect/decal/cleanable/dirt/dust,
/turf/open/floor/iron/dark/smooth_large,
/area/station/bitrunning/den)
"sWl" = (
diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm
index f0550cb5fed..c4a4756f915 100644
--- a/_maps/map_files/MetaStation/MetaStation.dmm
+++ b/_maps/map_files/MetaStation/MetaStation.dmm
@@ -3343,16 +3343,9 @@
/turf/open/floor/iron,
/area/station/hallway/primary/starboard)
"bje" = (
-/obj/effect/turf_decal/box/corners{
- dir = 8
- },
-/obj/effect/turf_decal/box/corners{
- dir = 1
- },
/obj/effect/decal/cleanable/oil/streak,
/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4,
/obj/structure/extinguisher_cabinet/directional/north,
-/obj/effect/landmark/bitrunning/station_reward_spawn,
/turf/open/floor/iron/dark/smooth_large,
/area/station/bitrunning/den)
"bjl" = (
@@ -29712,14 +29705,11 @@
/turf/open/floor/engine,
/area/station/science/explab)
"kHU" = (
-/obj/effect/turf_decal/box/corners,
-/obj/effect/turf_decal/box/corners{
- dir = 4
- },
/obj/effect/decal/cleanable/dirt/dust,
/obj/machinery/light_switch/directional/north,
/obj/effect/decal/cleanable/dirt/dust,
-/obj/effect/landmark/bitrunning/station_reward_spawn,
+/obj/machinery/byteforge,
+/obj/effect/turf_decal/box,
/turf/open/floor/iron/dark/smooth_large,
/area/station/bitrunning/den)
"kIG" = (
diff --git a/_maps/map_files/NorthStar/north_star.dmm b/_maps/map_files/NorthStar/north_star.dmm
index 535f37974ba..60ccea505c0 100644
--- a/_maps/map_files/NorthStar/north_star.dmm
+++ b/_maps/map_files/NorthStar/north_star.dmm
@@ -17161,9 +17161,6 @@
/obj/item/cigbutt/cigarbutt{
pixel_x = 7
},
-/obj/effect/turf_decal/stripes{
- dir = 8
- },
/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{
dir = 1
},
@@ -34844,12 +34841,10 @@
/turf/open/floor/pod/light,
/area/station/maintenance/floor1/starboard/fore)
"jhP" = (
-/obj/effect/turf_decal/box/corners,
-/obj/effect/turf_decal/box/corners{
- dir = 4
- },
/obj/effect/decal/cleanable/dirt/dust,
-/obj/effect/landmark/bitrunning/station_reward_spawn,
+/obj/effect/turf_decal/stripes{
+ dir = 8
+ },
/turf/open/floor/iron/dark/smooth_large,
/area/station/bitrunning/den)
"jhU" = (
@@ -50980,9 +50975,6 @@
/obj/structure/cable,
/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2,
/obj/effect/turf_decal/trimline/brown/line,
-/obj/effect/turf_decal/stripes/corner{
- dir = 8
- },
/obj/structure/disposalpipe/segment{
dir = 4
},
@@ -55648,11 +55640,13 @@
},
/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4,
/obj/structure/cable,
-/obj/effect/turf_decal/stripes,
/obj/effect/turf_decal/trimline/brown/line,
/obj/structure/disposalpipe/segment{
dir = 5
},
+/obj/effect/turf_decal/stripes/corner{
+ dir = 8
+ },
/turf/open/floor/iron/dark/smooth_half,
/area/station/bitrunning/den)
"owo" = (
@@ -78519,16 +78513,11 @@
/turf/open/floor/iron,
/area/station/hallway/floor2/aft)
"uDm" = (
-/obj/effect/turf_decal/box/corners{
- dir = 8
- },
-/obj/effect/turf_decal/box/corners{
- dir = 1
- },
/obj/machinery/firealarm/directional/south,
/obj/machinery/light/directional/south,
/obj/effect/decal/cleanable/oil/streak,
-/obj/effect/landmark/bitrunning/station_reward_spawn,
+/obj/machinery/byteforge,
+/obj/effect/turf_decal/box,
/turf/open/floor/iron/dark/smooth_large,
/area/station/bitrunning/den)
"uDr" = (
diff --git a/_maps/map_files/debug/runtimestation.dmm b/_maps/map_files/debug/runtimestation.dmm
index 3b86ffcfc20..dff02b2abd7 100644
--- a/_maps/map_files/debug/runtimestation.dmm
+++ b/_maps/map_files/debug/runtimestation.dmm
@@ -2184,7 +2184,7 @@
/turf/open/floor/iron,
/area/station/command/bridge)
"FW" = (
-/obj/effect/landmark/bitrunning/station_reward_spawn,
+/obj/machinery/byteforge,
/turf/open/floor/circuit/green,
/area/station/bitrunning/den)
"Ht" = (
@@ -3196,7 +3196,7 @@ aa
aa
aa
uI
-FW
+hU
hU
jR
Oq
@@ -3288,8 +3288,8 @@ aa
aa
aa
uI
-FW
hU
+FW
jR
Oq
uI
diff --git a/_maps/map_files/tramstation/tramstation.dmm b/_maps/map_files/tramstation/tramstation.dmm
index 638f7512d67..9ce8d37d988 100644
--- a/_maps/map_files/tramstation/tramstation.dmm
+++ b/_maps/map_files/tramstation/tramstation.dmm
@@ -42017,14 +42017,9 @@
/turf/open/floor/iron/white,
/area/station/science/lobby)
"nUF" = (
-/obj/effect/turf_decal/box/corners,
-/obj/effect/turf_decal/box/corners{
- dir = 4
- },
/obj/effect/decal/cleanable/dirt/dust,
/obj/machinery/light/directional/north,
/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2,
-/obj/effect/landmark/bitrunning/station_reward_spawn,
/turf/open/floor/iron/dark/smooth_large,
/area/station/bitrunning/den)
"nUM" = (
@@ -45391,17 +45386,12 @@
/turf/open/floor/plating,
/area/station/commons/vacant_room)
"plH" = (
-/obj/effect/turf_decal/box/corners{
- dir = 8
- },
-/obj/effect/turf_decal/box/corners{
- dir = 1
- },
/obj/effect/decal/cleanable/oil/streak,
/obj/structure/sign/poster/random/directional/north,
/obj/machinery/power/apc/auto_name/directional/west,
/obj/structure/cable,
-/obj/effect/landmark/bitrunning/station_reward_spawn,
+/obj/machinery/byteforge,
+/obj/effect/turf_decal/box,
/turf/open/floor/iron/dark/smooth_large,
/area/station/bitrunning/den)
"plQ" = (
diff --git a/code/__DEFINES/dcs/signals/signals_bitrunning.dm b/code/__DEFINES/dcs/signals/signals_bitrunning.dm
index 3d008449ee7..957e61ae303 100644
--- a/code/__DEFINES/dcs/signals/signals_bitrunning.dm
+++ b/code/__DEFINES/dcs/signals/signals_bitrunning.dm
@@ -29,3 +29,6 @@
/// from /obj/machinery/quantum_server/refreshParts(): (servo rating)
#define COMSIG_BITRUNNER_SERVER_UPGRADED "bitrunner_server_upgraded"
+
+/// from /obj/machinery/quantum_server/scrub_vdom()
+#define COMSIG_BITRUNNER_DOMAIN_SCRUBBED "bitrunner_domain_scrubbed"
diff --git a/code/game/objects/items/circuitboards/computer_circuitboards.dm b/code/game/objects/items/circuitboards/computer_circuitboards.dm
index 0338da6b4fa..79efe62a225 100644
--- a/code/game/objects/items/circuitboards/computer_circuitboards.dm
+++ b/code/game/objects/items/circuitboards/computer_circuitboards.dm
@@ -605,8 +605,3 @@
name = "Medical Order"
greyscale_colors = CIRCUIT_COLOR_SUPPLY
build_path = /obj/machinery/computer/department_orders/medical
-
-/obj/item/circuitboard/computer/quantum_console
- name = "Quantum Server Console"
- greyscale_colors = CIRCUIT_COLOR_SUPPLY
- build_path = /obj/machinery/computer/quantum_console
diff --git a/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm b/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm
index 4bea5b1335c..9ca1095ee4b 100644
--- a/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm
+++ b/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm
@@ -1529,21 +1529,3 @@
/obj/item/stack/sheet/plasteel = 2,
)
-/obj/item/circuitboard/machine/quantum_server
- name = "Quantum Server"
- greyscale_colors = CIRCUIT_COLOR_SUPPLY
- build_path = /obj/machinery/quantum_server
- req_components = list(
- /datum/stock_part/servo = 2,
- /datum/stock_part/scanning_module = 1,
- /datum/stock_part/capacitor = 1,
- )
-
-/obj/item/circuitboard/machine/netpod
- name = "Netpod"
- greyscale_colors = CIRCUIT_COLOR_SUPPLY
- build_path = /obj/machinery/netpod
- req_components = list(
- /datum/stock_part/servo = 1,
- /datum/stock_part/matter_bin = 2,
- )
diff --git a/code/modules/bitrunning/components/avatar_connection.dm b/code/modules/bitrunning/components/avatar_connection.dm
index 2a151d05066..24f42d8f3e5 100644
--- a/code/modules/bitrunning/components/avatar_connection.dm
+++ b/code/modules/bitrunning/components/avatar_connection.dm
@@ -68,7 +68,7 @@
/datum/component/avatar_connection/RegisterWithParent()
ADD_TRAIT(parent, TRAIT_TEMPORARY_BODY, REF(src))
RegisterSignal(parent, COMSIG_BITRUNNER_SAFE_DISCONNECT, PROC_REF(on_safe_disconnect))
- RegisterSignal(parent, COMSIG_LIVING_DEATH, PROC_REF(on_sever_connection), override = TRUE)
+ RegisterSignal(parent, COMSIG_LIVING_DEATH, PROC_REF(on_sever_connection))
RegisterSignal(parent, COMSIG_MOB_APPLY_DAMAGE, PROC_REF(on_linked_damage))
/datum/component/avatar_connection/UnregisterFromParent()
@@ -79,7 +79,9 @@
/// Disconnects the avatar and returns the mind to the old_body.
/datum/component/avatar_connection/proc/full_avatar_disconnect(forced = FALSE, datum/source)
+#ifndef UNIT_TESTS
return_to_old_body()
+#endif
var/obj/machinery/netpod/hosting_netpod = netpod_ref?.resolve()
if(isnull(hosting_netpod) && istype(source, /obj/machinery/netpod))
diff --git a/code/modules/bitrunning/designs.dm b/code/modules/bitrunning/designs.dm
new file mode 100644
index 00000000000..4e7bca1c1a8
--- /dev/null
+++ b/code/modules/bitrunning/designs.dm
@@ -0,0 +1,87 @@
+// Quantum server
+
+/obj/item/circuitboard/machine/quantum_server
+ name = "Quantum Server"
+ greyscale_colors = CIRCUIT_COLOR_SUPPLY
+ build_path = /obj/machinery/quantum_server
+ req_components = list(
+ /datum/stock_part/servo = 2,
+ /datum/stock_part/scanning_module = 1,
+ /datum/stock_part/capacitor = 1,
+ )
+
+/**
+ * quantum server design
+ * are you absolutely sure??
+ */
+
+// Netpod
+
+/obj/item/circuitboard/machine/netpod
+ name = "Netpod"
+ greyscale_colors = CIRCUIT_COLOR_SUPPLY
+ build_path = /obj/machinery/netpod
+ req_components = list(
+ /datum/stock_part/servo = 1,
+ /datum/stock_part/matter_bin = 2,
+ )
+
+/datum/design/board/netpod
+ name = "Netpod Board"
+ desc = "The circuit board for a netpod."
+ id = "netpod"
+ build_path = /obj/item/circuitboard/machine/netpod
+ category = list(
+ RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_CARGO
+ )
+ departmental_flags = DEPARTMENT_BITFLAG_ENGINEERING
+
+// Quantum console
+
+/obj/item/circuitboard/computer/quantum_console
+ name = "Quantum Console"
+ greyscale_colors = CIRCUIT_COLOR_SUPPLY
+ build_path = /obj/machinery/computer/quantum_console
+
+/datum/design/board/quantum_console
+ name = "Quantum Console Board"
+ desc = "Allows for the construction of circuit boards used to build a Quantum Console."
+ id = "quantum_console"
+ build_path = /obj/item/circuitboard/computer/quantum_console
+ category = list(
+ RND_CATEGORY_COMPUTER + RND_SUBCATEGORY_COMPUTER_CARGO
+ )
+ departmental_flags = DEPARTMENT_BITFLAG_ENGINEERING
+
+// Byteforge
+
+/obj/item/circuitboard/machine/byteforge
+ name = "Byteforge"
+ greyscale_colors = CIRCUIT_COLOR_SUPPLY
+ build_path = /obj/machinery/byteforge
+ req_components = list(
+ /datum/stock_part/micro_laser = 1,
+ )
+
+/datum/design/board/byteforge
+ name = "Byteforge Board"
+ desc = "Allows for the construction of circuit boards used to build a Byteforge."
+ id = "byteforge"
+ build_path = /obj/item/circuitboard/machine/byteforge
+ category = list(
+ RND_CATEGORY_COMPUTER + RND_SUBCATEGORY_COMPUTER_CARGO
+ )
+ departmental_flags = DEPARTMENT_BITFLAG_ENGINEERING
+
+
+/datum/techweb_node/bitrunning
+ id = "bitrunning"
+ display_name = "Bitrunning Technology"
+ description = "Bluespace technology has led to the development of quantum-scale computing, which unlocks the means to materialize atomic structures while executing advanced programs."
+ prereq_ids = list("practical_bluespace")
+ design_ids = list(
+ "byteforge",
+ "quantum_console",
+ "netpod",
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
diff --git a/code/modules/bitrunning/objects/byteforge.dm b/code/modules/bitrunning/objects/byteforge.dm
new file mode 100644
index 00000000000..e4543601ce9
--- /dev/null
+++ b/code/modules/bitrunning/objects/byteforge.dm
@@ -0,0 +1,55 @@
+/obj/machinery/byteforge
+ name = "byteforge"
+
+ circuit = /obj/item/circuitboard/machine/byteforge
+ desc = "A machine used by the quantum server. Quantum code converges here, materializing decrypted assets from the virtual abyss."
+ icon = 'icons/obj/machines/bitrunning.dmi'
+ icon_state = "byteforge"
+ obj_flags = BLOCKS_CONSTRUCTION
+ /// Idle particles
+ var/mutable_appearance/byteforge_particles
+
+/obj/machinery/byteforge/Initialize(mapload)
+ . = ..()
+
+ return INITIALIZE_HINT_LATELOAD
+
+/obj/machinery/byteforge/LateInitialize()
+ . = ..()
+
+ byteforge_particles = mutable_appearance(initial(icon), "on_particles", ABOVE_MOB_LAYER)
+ setup_particles()
+
+/obj/machinery/byteforge/update_appearance(updates)
+ . = ..()
+
+ setup_particles()
+
+/// Adds the particle overlays to the byteforge
+/obj/machinery/byteforge/proc/setup_particles()
+ cut_overlays()
+
+ if(is_operational)
+ add_overlay(byteforge_particles)
+
+/// Begins spawning the crate - lights, overlays, etc
+/obj/machinery/byteforge/proc/start_to_spawn(obj/structure/closet/crate/secure/bitrunning/encrypted/cache)
+ addtimer(CALLBACK(src, PROC_REF(spawn_crate), cache), 1 SECONDS, TIMER_UNIQUE|TIMER_OVERRIDE|TIMER_STOPPABLE)
+
+ var/mutable_appearance/lighting = mutable_appearance(initial(icon), "on_overlay")
+ flick_overlay_view(lighting, 1 SECONDS)
+
+ set_light(l_range = 2, l_power = 1.5, l_color = LIGHT_COLOR_BABY_BLUE, l_on = TRUE)
+
+/// Sparks, moves the crate to the location
+/obj/machinery/byteforge/proc/spawn_crate(obj/structure/closet/crate/secure/bitrunning/encrypted/cache)
+ if(QDELETED(cache))
+ return
+
+ playsound(src, 'sound/magic/blink.ogg', 50, TRUE)
+ var/datum/effect_system/spark_spread/quantum/sparks = new()
+ sparks.set_up(5, 1, loc)
+ sparks.start()
+
+ cache.forceMove(loc)
+ set_light(l_on = FALSE)
diff --git a/code/modules/bitrunning/objects/host_monitor.dm b/code/modules/bitrunning/objects/host_monitor.dm
index f59ca61cbd0..c35edea6319 100644
--- a/code/modules/bitrunning/objects/host_monitor.dm
+++ b/code/modules/bitrunning/objects/host_monitor.dm
@@ -2,10 +2,10 @@
name = "host monitor"
custom_materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 2)
- desc = "A complex medical device that, when attached to an avatar's data stream, can detect the user of their host's health."
+ desc = "A complex electronic that will analyze the connection health between host and avatar."
flags_1 = CONDUCT_1
icon = 'icons/obj/device.dmi'
- icon_state = "gps-b"
+ icon_state = "host_monitor"
inhand_icon_state = "electronic"
item_flags = NOBLUDGEON
lefthand_file = 'icons/mob/inhands/items/devices_lefthand.dmi'
diff --git a/code/modules/bitrunning/objects/landmarks.dm b/code/modules/bitrunning/objects/landmarks.dm
index d78283c6a8b..a4539c7c3da 100644
--- a/code/modules/bitrunning/objects/landmarks.dm
+++ b/code/modules/bitrunning/objects/landmarks.dm
@@ -44,11 +44,6 @@
qdel(src)
-/// Where the crates get ported to station
-/obj/effect/landmark/bitrunning/station_reward_spawn
- name = "Bitrunning rewards spawn"
- icon_state = "station"
-
/// Where the exit hololadder spawns
/obj/effect/landmark/bitrunning/hololadder_spawn
name = "Bitrunning hololadder spawn"
diff --git a/code/modules/bitrunning/objects/netpod.dm b/code/modules/bitrunning/objects/netpod.dm
index 349a304f04b..d92da961b86 100644
--- a/code/modules/bitrunning/objects/netpod.dm
+++ b/code/modules/bitrunning/objects/netpod.dm
@@ -189,7 +189,7 @@
return TRUE
/obj/machinery/netpod/ui_interact(mob/user, datum/tgui/ui)
- if(!is_operational)
+ if(!is_operational || occupant)
return
ui = SStgui.try_update_ui(user, src, ui)
@@ -242,20 +242,12 @@
to_chat(player, span_notice("The machine disconnects itself and begins to drain."))
open_machine()
-/**
- * ### Disconnect occupant
- * If this goes smoothly, should reconnect a receiving mind to the occupant's body
- *
- * This is the second stage of the process - if you want to disconn avatars start at the mind first
- */
+/// Handles occupant post-disconnection effects like damage, sounds, etc
/obj/machinery/netpod/proc/disconnect_occupant(forced = FALSE)
- var/mob/living/mob_occupant = occupant
- if(isnull(occupant) || !isliving(occupant))
- return
-
connected = FALSE
- if(mob_occupant.stat == DEAD)
+ var/mob/living/mob_occupant = occupant
+ if(isnull(occupant) || !isliving(occupant) || mob_occupant.stat == DEAD)
open_machine()
return
@@ -347,8 +339,9 @@
return
server_ref = WEAKREF(server)
- RegisterSignal(server, COMSIG_BITRUNNER_SERVER_UPGRADED, PROC_REF(on_server_upgraded), override = TRUE)
- RegisterSignal(server, COMSIG_BITRUNNER_DOMAIN_COMPLETE, PROC_REF(on_domain_complete), override = TRUE)
+ RegisterSignal(server, COMSIG_BITRUNNER_SERVER_UPGRADED, PROC_REF(on_server_upgraded))
+ RegisterSignal(server, COMSIG_BITRUNNER_DOMAIN_COMPLETE, PROC_REF(on_domain_complete))
+ RegisterSignal(server, COMSIG_BITRUNNER_DOMAIN_SCRUBBED, PROC_REF(on_domain_scrubbed))
return server
@@ -395,6 +388,7 @@
account.bitrunning_points += reward_points * 100
+/// User inspects the machine
/obj/machinery/netpod/proc/on_examine(datum/source, mob/examiner, list/examine_text)
SIGNAL_HANDLER
@@ -409,7 +403,15 @@
examine_text += span_notice("It is currently occupied by [occupant].")
examine_text += span_notice("It can be pried open with a crowbar, but its safety mechanisms will alert the occupant.")
+/// The domain has been fully purged, so we should double check our avatar is deleted
+/obj/machinery/netpod/proc/on_domain_scrubbed(datum/source)
+ SIGNAL_HANDLER
+ var/mob/living/current_avatar = avatar_ref?.resolve()
+ if(isnull(current_avatar))
+ return
+
+ QDEL_NULL(current_avatar)
/// When the server is upgraded, drops brain damage a little
/obj/machinery/netpod/proc/on_server_upgraded(datum/source, servo_rating)
diff --git a/code/modules/bitrunning/objects/bit_vendor.dm b/code/modules/bitrunning/objects/vendor.dm
similarity index 100%
rename from code/modules/bitrunning/objects/bit_vendor.dm
rename to code/modules/bitrunning/objects/vendor.dm
diff --git a/code/modules/bitrunning/server/loot.dm b/code/modules/bitrunning/server/loot.dm
index 29b730aae78..8b3af95607c 100644
--- a/code/modules/bitrunning/server/loot.dm
+++ b/code/modules/bitrunning/server/loot.dm
@@ -16,14 +16,16 @@
/// Generates a reward based on the given domain
/obj/machinery/quantum_server/proc/generate_loot()
- if(!length(receive_turfs) && !locate_receive_turfs())
+ var/list/obj/machinery/byteforge/nearby_forges = get_nearby_forges()
+ if(isnull(nearby_forges))
+ say(src, "No nearby byteforges detected.")
return FALSE
points += generated_domain.reward_points
playsound(src, 'sound/machines/terminal_success.ogg', 30, 2)
- var/turf/dest_turf = pick(receive_turfs)
- if(isnull(dest_turf))
+ var/obj/machinery/byteforge/chosen_forge = pick(nearby_forges)
+ if(isnull(chosen_forge))
stack_trace("Failed to find a turf to spawn loot crate on.")
return FALSE
@@ -34,11 +36,11 @@
certificate.name = "certificate of domain completion"
certificate.update_appearance()
- var/obj/structure/closet/crate/secure/bitrunning/decrypted/reward_crate = new(dest_turf, generated_domain, bonus)
+ var/obj/structure/closet/crate/secure/bitrunning/decrypted/reward_crate = new(src, generated_domain, bonus)
reward_crate.manifest = certificate
reward_crate.update_appearance()
- spark_at_location(reward_crate)
+ chosen_forge.start_to_spawn(reward_crate)
return TRUE
/// Returns the markdown text containing domain completion information
diff --git a/code/modules/bitrunning/server/map_handling.dm b/code/modules/bitrunning/server/map_handling.dm
index 02126c290f7..741fad476f0 100644
--- a/code/modules/bitrunning/server/map_handling.dm
+++ b/code/modules/bitrunning/server/map_handling.dm
@@ -1,3 +1,4 @@
+#define ONLY_TURF 1
/// Gives all current occupants a notification that the server is going down
/obj/machinery/quantum_server/proc/begin_shutdown(mob/user)
@@ -119,7 +120,6 @@
new /obj/structure/closet/crate/secure/bitrunning/encrypted(pick(crate_turfs))
return TRUE
-#define ONLY_TURF 1 // There should only ever be one turf at the bottom left of the map.
/// Loads the safehouse
/obj/machinery/quantum_server/proc/initialize_safehouse()
@@ -160,7 +160,8 @@
/// Deletes all the tile contents
/obj/machinery/quantum_server/proc/scrub_vdom()
- SEND_SIGNAL(src, COMSIG_BITRUNNER_SEVER_AVATAR) // just in case
+ SEND_SIGNAL(src, COMSIG_BITRUNNER_SEVER_AVATAR) /// 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]
diff --git a/code/modules/bitrunning/server/quantum_server.dm b/code/modules/bitrunning/server/quantum_server.dm
index 404a31cca6a..b869fb7f02e 100644
--- a/code/modules/bitrunning/server/quantum_server.dm
+++ b/code/modules/bitrunning/server/quantum_server.dm
@@ -48,8 +48,6 @@
var/servo_bonus = 0
/// The turfs we can place a hololadder on.
var/turf/exit_turfs = list()
- /// The turfs on station where we generate loot.
- var/turf/receive_turfs = list()
/obj/machinery/quantum_server/Initialize(mapload)
. = ..()
@@ -84,18 +82,17 @@
avatar_connection_refs.Cut()
spawned_threat_refs.Cut()
QDEL_NULL(exit_turfs)
- QDEL_NULL(receive_turfs)
QDEL_NULL(generated_domain)
QDEL_NULL(generated_safehouse)
QDEL_NULL(radio)
/obj/machinery/quantum_server/update_appearance(updates)
if(isnull(generated_domain) || !is_operational)
- set_light(0)
+ set_light(l_on = FALSE)
return ..()
set_light_color(is_ready ? LIGHT_COLOR_BABY_BLUE : LIGHT_COLOR_FIRE)
- set_light(2, 1.5)
+ set_light(l_range = 2, l_power = 1.5, l_on = TRUE)
return ..()
diff --git a/code/modules/bitrunning/server/util.dm b/code/modules/bitrunning/server/util.dm
index c4f1319cd12..f4dbada9ef6 100644
--- a/code/modules/bitrunning/server/util.dm
+++ b/code/modules/bitrunning/server/util.dm
@@ -107,14 +107,14 @@
return shuffle(mutation_candidate_refs)
-/// Locates any turfs with crate out landmarks
-/obj/machinery/quantum_server/proc/locate_receive_turfs()
- for(var/obj/effect/landmark/bitrunning/station_reward_spawn/spawner in GLOB.landmarks_list)
- if(IN_GIVEN_RANGE(src, spawner, MAX_DISTANCE))
- receive_turfs += get_turf(spawner)
- qdel(spawner)
+/// Locates any turfs with forges on them
+/obj/machinery/quantum_server/proc/get_nearby_forges()
+ var/list/obj/machinery/byteforge/nearby_forges = list()
- return length(receive_turfs) > 0
+ for(var/obj/machinery/byteforge/forge in oview(MAX_DISTANCE, src))
+ nearby_forges += forge
+
+ return nearby_forges
/// Finds any mobs with minds in the zones and gives them the bad news
/obj/machinery/quantum_server/proc/notify_spawned_threats()
@@ -132,10 +132,10 @@
to_chat(baddie, span_userdanger("You have been flagged for deletion! Thank you for your service."))
/// Do some magic teleport sparks
-/obj/machinery/quantum_server/proc/spark_at_location(obj/crate)
- playsound(crate, 'sound/magic/blink.ogg', 50, TRUE)
+/obj/machinery/quantum_server/proc/spark_at_location(obj/cache)
+ playsound(cache, 'sound/magic/blink.ogg', 50, TRUE)
var/datum/effect_system/spark_spread/quantum/sparks = new()
- sparks.set_up(5, 1, get_turf(crate))
+ sparks.set_up(5, 1, get_turf(cache))
sparks.start()
#undef REDACTED
diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm
index d4839542122..c6fdecbd29b 100644
--- a/code/modules/unit_tests/_unit_tests.dm
+++ b/code/modules/unit_tests/_unit_tests.dm
@@ -100,6 +100,7 @@
#include "baseturfs.dm"
#include "bespoke_id.dm"
#include "binary_insert.dm"
+#include "bitrunning.dm"
#include "blindness.dm"
#include "bloody_footprints.dm"
#include "breath.dm"
diff --git a/code/modules/unit_tests/bitrunning.dm b/code/modules/unit_tests/bitrunning.dm
new file mode 100644
index 00000000000..568eeeed8c1
--- /dev/null
+++ b/code/modules/unit_tests/bitrunning.dm
@@ -0,0 +1,15 @@
+/// Ensures settings on vdoms are correct
+/datum/unit_test/bitrunner_vdom_settings
+
+/datum/unit_test/bitrunner_vdom_settings/Run()
+ var/obj/structure/closet/crate/secure/bitrunning/decrypted/cache = allocate(/obj/structure/closet/crate/secure/bitrunning/decrypted)
+
+ for(var/path in subtypesof(/datum/lazy_template/virtual_domain))
+ var/datum/lazy_template/virtual_domain/vdom = new path
+ TEST_ASSERT_NOTNULL(vdom.key, "[path] should have a key")
+ TEST_ASSERT_NOTNULL(vdom.map_name, "[path] should have a map name")
+
+ if(!length(vdom.extra_loot))
+ continue
+
+ TEST_ASSERT_EQUAL(cache.spawn_loot(vdom.extra_loot), TRUE, "[path] didn't spawn loot. Extra loot should be an associative list")
diff --git a/icons/obj/device.dmi b/icons/obj/device.dmi
index d89ee6e5d6408ff981d5220b03864b6341748f1f..fe74b6c11c5c7d2ed0172c41bbfcfc1f5377d357 100644
GIT binary patch
delta 4250
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
delta 3757
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
NLA9Q1R%pP({|`ZnK}-Mu
diff --git a/icons/obj/machines/bitrunning.dmi b/icons/obj/machines/bitrunning.dmi
index a910a16b35cf8c36847ce2b3481c57d2150f5e05..b3f8ad63a6c9937b287abab9b981dc4c08b0d74b 100644
GIT binary patch
literal 23871
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