Adds a unit test for techweb design presence, puts some missing designs into protolathes (#90219)

## About The Pull Request

Adds a unit test that checks that all designs are accessible through
some source, be it techweb, disks, or innate designs. Certain designs,
like pocket extinguishers, entertainment screens, etc, that have been
present in autolathes but not in the techweb despite having PROTOLATHE
flag assigned have been put into protolathes (where it made sense,
otherwise the flag was removed).
An important change is that restaurant portals are now printable, and
thus can be constructed and deconstructed. Indestructible portals have
been a major gripe of mine for a while, and I don't see a solid reason
for keeping them indestructible if they can easily be printed from the
service protolathe.

Closes #90212

## Why It's Good For The Game

Initial argument for keeping portals unbreakable was to prevent people
from griefing the chef/bartender, but by that logic we can make most
machinery unbreakable too. I don't think that having an unbreakable
portal is good if its locking us out from allowing chefs/bartenders to
reorder or even outright reposition their lunchroom/bar.

## Changelog

🆑
balance: Restaurant portals can now be printed, constructed and
deconstructed. They're also no longer completely invulnerable.
/🆑
This commit is contained in:
SmArtKar
2025-04-03 08:05:08 +02:00
committed by GitHub
parent 8d7ed74df6
commit 44c968f75e
20 changed files with 217 additions and 81 deletions
@@ -112,7 +112,8 @@
/datum/ai_behavior/leave_venue/setup(datum/ai_controller/controller, venue_key)
. = ..()
var/datum/venue/attending_venue = controller.blackboard[venue_key]
set_movement_target(controller, attending_venue.restaurant_portal)
var/datum/weakref/portal_ref = attending_venue.current_visitors[controller.pawn]
set_movement_target(controller, portal_ref.resolve())
/datum/ai_behavior/leave_venue/perform(seconds_per_tick, datum/ai_controller/controller, venue_key)
qdel(controller.pawn) //save the world, my final message, goodbye.
@@ -1505,7 +1505,42 @@
name = "Restaurant Portal"
greyscale_colors = CIRCUIT_COLOR_SERVICE
build_path = /obj/machinery/restaurant_portal
req_components = list(
/datum/stock_part/scanning_module = 2,
/obj/item/stack/sheet/glass = 1)
needs_anchored = TRUE
/// Type of the venue that we're linked to
var/venue_type = /datum/venue/restaurant
/obj/item/circuitboard/machine/restaurant_portal/multitool_act(mob/living/user)
var/list/radial_items = list()
var/list/radial_results = list()
for(var/type_key in SSrestaurant.all_venues)
var/datum/venue/venue = SSrestaurant.all_venues[type_key]
radial_items[venue.name] = image('icons/obj/machines/restaurant_portal.dmi', venue.name)
radial_results[venue.name] = type_key
var/choice = show_radial_menu(user, src, radial_items, null, require_near = TRUE)
if(!choice)
return ITEM_INTERACT_BLOCKING
venue_type = radial_results[choice]
to_chat(user, span_notice("You change [src]'s linked venue."))
return ITEM_INTERACT_SUCCESS
/obj/item/circuitboard/machine/restaurant_portal/examine(mob/user)
. = ..()
if (venue_type)
var/datum/venue/as_venue = venue_type
. += span_notice("[src] is linked to \a [initial(as_venue.name)] venue.")
/obj/item/circuitboard/machine/restaurant_portal/configure_machine(obj/machinery/restaurant_portal/machine)
if(!istype(machine))
CRASH("Cargo board attempted to configure incorrect machine type: [machine] ([machine?.type])")
machine.linked_venue = SSrestaurant.all_venues[venue_type]
machine.linked_venue.restaurant_portals += machine
/obj/item/circuitboard/machine/abductor
name = "alien board (Report This)"
+1 -1
View File
@@ -261,7 +261,7 @@ GLOBAL_LIST_EMPTY(objects_by_id_tag)
return SUCCESSFUL_UNFASTEN
/// Try to unwrench an object in a WONDERFUL DYNAMIC WAY
/obj/proc/default_unfasten_wrench(mob/user, obj/item/wrench, time = 20)
/obj/proc/default_unfasten_wrench(mob/user, obj/item/wrench, time = 2 SECONDS)
if(wrench.tool_behaviour != TOOL_WRENCH)
return CANT_UNFASTEN
@@ -10,8 +10,8 @@
var/list/customer_types
///Is the venue open at the moment?
var/open
///Portal linked to this venue at the moment
var/obj/machinery/restaurant_portal/restaurant_portal
///List of portals linked to this venue at the moment
var/list/obj/machinery/restaurant_portal/restaurant_portals = list()
///Lists the current visitors of a venue
var/list/current_visitors = list()
///Cooldown for next guest to arrive
@@ -42,13 +42,14 @@
/datum/venue/proc/create_new_customer()
var/list/customer_types_to_choose = customer_types
var/datum/customer_data/customer_type
var/obj/machinery/restaurant_portal/chosen_portal = pick(restaurant_portals)
// In practice, the list will never run out, but this is for sanity.
while (customer_types_to_choose.len)
customer_type = pick_weight(customer_types_to_choose)
var/datum/customer_data/customer = SSrestaurant.all_customers[customer_type]
if (customer.can_use(src))
if (customer.can_use(src, chosen_portal))
break
// Only copy the list once, so that we're not mutating ourselves.
@@ -60,8 +61,8 @@
if (initial(customer_type.is_unique))
customer_types -= customer_type
var/mob/living/basic/robot_customer/new_customer = new /mob/living/basic/robot_customer(get_turf(restaurant_portal), customer_type, src)
current_visitors += new_customer
var/mob/living/basic/robot_customer/new_customer = new /mob/living/basic/robot_customer(get_turf(chosen_portal), customer_type, src)
current_visitors[new_customer] = WEAKREF(chosen_portal)
/datum/venue/proc/order_food(mob/living/basic/robot_customer/customer_pawn, datum/customer_data/customer_data)
var/order = pick_weight(customer_data.orderable_objects[venue_type])
@@ -76,7 +77,7 @@
order = initial(reagent_order.restaurant_order)
if(ispath(order, /datum/custom_order)) // generate the special order
var/datum/custom_order/custom_order = new order(arglist(order_args || list()))
var/datum/custom_order/custom_order = new order(arglist(list("customer" = customer_pawn) + (order_args || list())))
food_image = custom_order.get_order_appearance(src)
food_line = custom_order.get_order_line(src)
order = custom_order.dispense_order()
@@ -144,13 +145,15 @@
/datum/venue/proc/open()
open = TRUE
restaurant_portal.update_icon()
for (var/obj/machinery/restaurant_portal/portal as anything in restaurant_portals)
portal.update_icon()
COOLDOWN_START(src, visit_cooldown, 4 SECONDS) //First one comes faster
START_PROCESSING(SSobj, src)
/datum/venue/proc/close()
open = FALSE
restaurant_portal.update_icon()
for (var/obj/machinery/restaurant_portal/portal as anything in restaurant_portals)
portal.update_icon()
STOP_PROCESSING(SSobj, src)
for(var/mob/living/basic/robot_customer as anything in current_visitors)
robot_customer.ai_controller.set_blackboard_key(BB_CUSTOMER_LEAVING, TRUE) //LEAVEEEEEE
@@ -160,32 +163,51 @@
desc = "A robot-only gate into the wonders of Space Station cuisine!"
icon = 'icons/obj/machines/restaurant_portal.dmi'
icon_state = "portal"
base_icon_state = "portal"
anchored = TRUE
density = FALSE
circuit = /obj/item/circuitboard/machine/restaurant_portal
layer = BELOW_OBJ_LAYER
resistance_flags = INDESTRUCTIBLE | FIRE_PROOF | UNACIDABLE | ACID_PROOF
armor_type = /datum/armor/restaurant_portal
resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF
///What venue is this portal for? Uses a typepath which is turned into an instance on Initialize
var/datum/venue/linked_venue = /datum/venue
var/datum/venue/linked_venue
/// A weak reference to the mob who turned on the portal
var/datum/weakref/turned_on_portal
/datum/armor/restaurant_portal
melee = 50
bullet = 30
laser = 50
energy = 20
bomb = 20
fire = 100
acid = 100
/obj/machinery/restaurant_portal/Initialize(mapload)
. = ..()
if(linked_venue)
linked_venue = SSrestaurant.all_venues[linked_venue]
linked_venue.restaurant_portal = src
register_context()
if (!linked_venue)
return
var/obj/item/circuitboard/machine/restaurant_portal/board = circuit
board.venue_type = linked_venue
linked_venue = SSrestaurant.all_venues[linked_venue]
linked_venue.restaurant_portals += src
/obj/machinery/restaurant_portal/Destroy()
. = ..()
turned_on_portal = null
linked_venue.restaurant_portal = null
linked_venue.restaurant_portals -= src
linked_venue = null
return ..()
/obj/machinery/restaurant_portal/on_construction(mob/user)
. = ..()
circuit.configure_machine(src)
/obj/machinery/restaurant_portal/update_overlays()
. = ..()
if(!linked_venue.open) //Any open venues
if(!linked_venue?.open) //Any open venues
. += mutable_appearance(icon, "portal_door")
/obj/machinery/restaurant_portal/attack_hand(mob/living/user)
@@ -217,14 +239,17 @@
for(var/type_key in SSrestaurant.all_venues)
var/datum/venue/venue = SSrestaurant.all_venues[type_key]
radial_items[venue.name] = image('icons/obj/machines/restaurant_portal.dmi', venue.name)
radial_results[venue.name] = venue
radial_results[venue.name] = type_key
var/choice = show_radial_menu(user, src, radial_items, null, require_near = TRUE)
if(!choice)
return
var/datum/venue/chosen_venue = radial_results[choice]
var/venue_type = radial_results[choice]
var/obj/item/circuitboard/machine/restaurant_portal/board = circuit
board.venue_type = venue_type
var/datum/venue/chosen_venue = SSrestaurant.all_venues[venue_type]
turned_on_portal = WEAKREF(user)
@@ -234,15 +259,54 @@
to_chat(user, span_notice("You change the portal's linked venue."))
if(linked_venue && linked_venue.restaurant_portal) //We're already linked, unlink us.
if(linked_venue.open)
if(linked_venue && (src in linked_venue.restaurant_portals)) //We're already linked, unlink us.
linked_venue.restaurant_portals -= src
if(linked_venue.open && !length(linked_venue.restaurant_portals))
linked_venue.close()
linked_venue.restaurant_portal = null
linked_venue = null
linked_venue = chosen_venue
linked_venue.restaurant_portal = src
linked_venue.restaurant_portals += src
/obj/machinery/restaurant_portal/screwdriver_act(mob/user, obj/item/tool)
if (default_deconstruction_screwdriver(user, "[base_icon_state]-open", base_icon_state, tool))
return ITEM_INTERACT_SUCCESS
return ITEM_INTERACT_BLOCKING
/obj/machinery/restaurant_portal/crowbar_act(mob/user, obj/item/tool)
if(default_deconstruction_crowbar(tool))
return ITEM_INTERACT_SUCCESS
return ITEM_INTERACT_BLOCKING
/obj/machinery/restaurant_portal/wrench_act(mob/living/user, obj/item/tool)
if(!panel_open)
balloon_alert(user, "open the panel first!")
return ITEM_INTERACT_BLOCKING
if (default_unfasten_wrench(user, tool))
return ITEM_INTERACT_SUCCESS
return ITEM_INTERACT_BLOCKING
/obj/machinery/restaurant_portal/add_context(atom/source, list/context, obj/item/held_item, mob/user)
. = NONE
if(isnull(held_item))
return
if(held_item.tool_behaviour == TOOL_SCREWDRIVER)
context[SCREENTIP_CONTEXT_LMB] = "[panel_open ? "Close" : "Open"] Panel"
return CONTEXTUAL_SCREENTIP_SET
if(held_item.tool_behaviour == TOOL_WRENCH)
context[SCREENTIP_CONTEXT_LMB] = anchored ? "Unsecure" : "Secure"
return CONTEXTUAL_SCREENTIP_SET
if(held_item.tool_behaviour == TOOL_CROWBAR && panel_open)
context[SCREENTIP_CONTEXT_LMB] = "Deconstruct"
return CONTEXTUAL_SCREENTIP_SET
if(isidcard(held_item))
context[SCREENTIP_CONTEXT_LMB] = "Change Venue"
return CONTEXTUAL_SCREENTIP_SET
/obj/item/holosign_creator/robot_seat
name = "seating indicator placer"
@@ -41,8 +41,10 @@
/// The item type that we want to order, usually clothing
var/wanted_clothing_type
/datum/custom_order/moth_clothing/New(datum/venue/our_venue)
var/mob/living/carbon/buffet = our_venue.restaurant_portal?.turned_on_portal?.resolve()
/datum/custom_order/moth_clothing/New(mob/living/basic/robot_customer/customer, datum/venue/our_venue)
var/datum/weakref/portal_ref = our_venue.current_visitors[customer]
var/obj/machinery/restaurant_portal/portal = portal_ref.resolve()
var/mob/living/carbon/buffet = portal?.turned_on_portal?.resolve()
if (!istype(buffet)) // Always asks for the clothes that you have on, but this is a fallback.
wanted_clothing_type = pick_weight(list(
/obj/item/clothing/head/utility/chefhat = 3,
@@ -90,7 +92,7 @@
/// stores tha name of our order generated on New()
var/icecream_name
/datum/custom_order/icecream/New()
/datum/custom_order/icecream/New(mob/living/basic/robot_customer/customer)
if(prob(33))
cone_type = /obj/item/food/icecream/chocolate
var/static/list/possible_flavors = list()
@@ -136,7 +138,7 @@
/// How many reagents is needed
var/reagents_needed = VENUE_BAR_MINIMUM_REAGENTS
/datum/custom_order/reagent/New(reagent_type)
/datum/custom_order/reagent/New(mob/living/basic/robot_customer/customer, reagent_type)
. = ..()
src.reagent_type = reagent_type
@@ -198,7 +200,7 @@
/// What serving we picked for the order
var/picked_serving
/datum/custom_order/reagent/soup/New(reagent_type)
/datum/custom_order/reagent/soup/New(mob/living/basic/robot_customer/customer, reagent_type)
. = ..()
var/list/serving_sizes = list(
"small serving (15u)" = 15,
@@ -53,7 +53,7 @@
orderable_restaurant[/datum/custom_order/icecream] *= 3
/// Can this customer be chosen for this venue?
/datum/customer_data/proc/can_use(datum/venue/venue)
/datum/customer_data/proc/can_use(datum/venue/venue, obj/machinery/restaurant_portal/portal)
return TRUE
/datum/customer_data/proc/get_overlays(mob/living/basic/robot_customer/customer)
@@ -295,8 +295,8 @@
// The whole gag is taking off your hat and giving it to the customer.
// If it takes any more effort, it loses a bit of the comedy.
// Therefore, only show up if it's reasonable for that gag to happen.
/datum/customer_data/moth/can_use(datum/venue/venue)
var/mob/living/carbon/buffet = venue.restaurant_portal?.turned_on_portal?.resolve()
/datum/customer_data/moth/can_use(datum/venue/venue, obj/machinery/restaurant_portal/portal)
var/mob/living/carbon/buffet = portal.turned_on_portal?.resolve()
if (!istype(buffet))
return FALSE
if(QDELETED(buffet.head) && QDELETED(buffet.gloves) && QDELETED(buffet.shoes))
@@ -168,7 +168,7 @@
/datum/design/toolbox
name = "Toolbox"
id = "tool_box"
build_type = AUTOLATHE | PROTOLATHE | AWAY_LATHE
build_type = AUTOLATHE
materials = list(MAT_CATEGORY_ITEM_MATERIAL =SMALL_MATERIAL_AMOUNT*5)
build_path = /obj/item/storage/toolbox
category = list(
@@ -536,7 +536,7 @@
name = "Paper Biscuit"
desc = "A paper biscuit which can seal paperwork inside. After sealing it the only way to open is through cracking it, cracking is irreversible and makes it permanently open. Not actually a biscuit."
id = "biscuit"
build_type = PROTOLATHE | AWAY_LATHE | AUTOLATHE
build_type = AUTOLATHE
materials = list(/datum/material/plastic =SMALL_MATERIAL_AMOUNT*0.2)
build_path = /obj/item/folder/biscuit/unsealed
category = list(
@@ -549,7 +549,7 @@
name = "Confidential Paper Biscuit"
desc = "A paper biscuit which can seal paperwork inside, this one is used for confidential Nanotrasen documents. After sealing it the only way to open is through cracking it, cracking is irreversible and makes it permanently open. Not actually a biscuit."
id = "confidential_biscuit"
build_type = PROTOLATHE | AWAY_LATHE | AUTOLATHE
build_type = AUTOLATHE
materials = list(/datum/material/plastic = SMALL_MATERIAL_AMOUNT*0.3)
build_path = /obj/item/folder/biscuit/unsealed/confidential
category = list(
@@ -1,7 +1,7 @@
/datum/design/beanbag_slug
name = "Beanbag Slug (Less Lethal)"
id = "beanbag_slug"
build_type = AUTOLATHE | PROTOLATHE | AWAY_LATHE
build_type = AUTOLATHE
materials = list(/datum/material/iron =SHEET_MATERIAL_AMOUNT)
build_path = /obj/item/ammo_casing/shotgun/beanbag
category = list(
@@ -13,7 +13,7 @@
/datum/design/rubbershot
name = "Rubber Shot (Less Lethal)"
id = "rubber_shot"
build_type = AUTOLATHE | PROTOLATHE | AWAY_LATHE
build_type = AUTOLATHE
materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT*2)
build_path = /obj/item/ammo_casing/shotgun/rubbershot
category = list(
@@ -25,7 +25,7 @@
/datum/design/c38
name = "Speed Loader (.38) (Lethal)"
id = "c38"
build_type = AUTOLATHE | PROTOLATHE | AWAY_LATHE
build_type = AUTOLATHE
materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT*10)
build_path = /obj/item/ammo_box/c38
category = list(
@@ -422,7 +422,7 @@
/datum/design/plastic_tree
name = "Plastic Potted Plant"
id = "plastic_trees"
build_type = AUTOLATHE | PROTOLATHE | AWAY_LATHE
build_type = AUTOLATHE
materials = list(/datum/material/plastic = SHEET_MATERIAL_AMOUNT*4)
build_path = /obj/item/kirbyplants/random/fullysynthetic
category = list(
@@ -434,7 +434,7 @@
/datum/design/beads
name = "Plastic Bead Necklace"
id = "plastic_necklace"
build_type = AUTOLATHE | PROTOLATHE | AWAY_LATHE
build_type = AUTOLATHE
materials = list(/datum/material/plastic =SMALL_MATERIAL_AMOUNT*5)
build_path = /obj/item/clothing/neck/beads
category = list(
@@ -446,7 +446,7 @@
/datum/design/plastic_ring
name = "Plastic Can Rings"
id = "ring_holder"
build_type = AUTOLATHE | PROTOLATHE | AWAY_LATHE
build_type = AUTOLATHE
materials = list(/datum/material/plastic = HALF_SHEET_MATERIAL_AMOUNT*1.2)
build_path = /obj/item/storage/cans
category = list(
@@ -458,7 +458,7 @@
/datum/design/plastic_box
name = "Plastic Box"
id = "plastic_box"
build_type = AUTOLATHE | PROTOLATHE | AWAY_LATHE
build_type = AUTOLATHE
materials = list(/datum/material/plastic =HALF_SHEET_MATERIAL_AMOUNT)
build_path = /obj/item/storage/box/plastic
category = list(
@@ -200,7 +200,6 @@
// Intentionally not growable by normal means - for balance conerns.
/datum/design/ethereal_heart
name = "Crystal Core"
id = "etherealheart"
build_type = LIMBGROWER
reagents_list = list(/datum/reagent/medicine/c2/synthflesh = 10, /datum/reagent/consumable/liquidelectricity/enriched = 20)
build_path = /obj/item/organ/heart/ethereal
@@ -231,7 +230,6 @@
/datum/design/limb_disk
name = "Limb Design Disk"
desc = "Contains designs for various limbs."
id = "limbdesign_parent"
build_type = PROTOLATHE
materials = list(/datum/material/iron =SMALL_MATERIAL_AMOUNT * 3, /datum/material/glass =SMALL_MATERIAL_AMOUNT)
build_path = /obj/item/disk/design_disk/limbs
@@ -398,28 +398,6 @@
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
/datum/design/board/protolathe
name = "Protolathe Board"
desc = "The circuit board for a protolathe."
id = "protolathe"
build_type = IMPRINTER
build_path = /obj/item/circuitboard/machine/protolathe
category = list(
RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_FAB
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
/datum/design/board/protolathe/offstation
name = "Ancient Protolathe Board"
desc = "The circuit board for an ancient protolathe."
id = "protolathe_offstation"
build_type = AWAY_IMPRINTER
build_path = /obj/item/circuitboard/machine/protolathe/offstation
category = list(
RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_FAB
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
/datum/design/board/circuit_imprinter
name = "Circuit Imprinter Board"
desc = "The circuit board for a circuit imprinter."
@@ -1101,7 +1101,6 @@
/datum/design/surgery
name = "Surgery Design"
desc = "what"
id = "surgery_parent"
research_icon = 'icons/obj/medical/surgery_ui.dmi'
research_icon_state = "surgery_any"
var/surgery
@@ -475,18 +475,6 @@
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
/datum/design/stunshell
name = "Stun Shell"
desc = "A stunning shell for a shotgun."
id = "stunshell"
build_type = PROTOLATHE | AWAY_LATHE
materials = list(/datum/material/iron =SMALL_MATERIAL_AMOUNT * 2)
build_path = /obj/item/ammo_casing/shotgun/stunslug
category = list(
RND_CATEGORY_WEAPONS + RND_SUBCATEGORY_WEAPONS_AMMO
)
departmental_flags = DEPARTMENT_BITFLAG_SECURITY
/datum/design/lasershell
name = "Scatter Laser Shotgun Shell (Lethal)"
desc = "A high-tech shotgun shell which houses an internal capacitor and laser focusing crystal inside of a shell casing. \
@@ -15,6 +15,7 @@
"plasmaman_tank_belt",
"plasmarefiller",
"extinguisher",
"pocketfireextinguisher",
"gas_filter",
"plasmaman_gas_filter",
"analyzer",
@@ -40,6 +41,7 @@
"turbine_stator",
"atmos_thermal",
"pneumatic_seal",
"large_welding_tool",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
experiments_to_unlock = list(
@@ -21,6 +21,7 @@
design_ids = list(
"mauna_mug",
"rolling_table",
"plasticducky",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
hidden = TRUE
@@ -73,7 +73,6 @@
"comp_soundemitter",
"comp_species",
"comp_speech",
"comp_speech",
"comp_split",
"comp_string_contains",
"comp_tempsensor",
@@ -129,6 +129,7 @@
"firelock_board",
"trapdoor_electronics",
"blast",
"ignition",
"big_manipulator",
"tile_sprayer",
"airlock_painter",
@@ -137,8 +138,8 @@
"cable_coil",
"welding_helmet",
"welding_tool",
"mini_welding_tool",
"tscanner",
"analyzer",
"multitool",
"wrench",
"crowbar",
@@ -178,6 +179,7 @@
"inducerengi",
"welding_goggles",
"tray_goggles",
"geigercounter",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
announce_channels = list(RADIO_CHANNEL_ENGINEERING)
@@ -33,6 +33,9 @@
"normtrash",
"wirebrush",
"flashlight",
"water_balloon",
"ticket_machine",
"radio_entertainment",
)
/datum/techweb_node/sanitation
@@ -77,6 +80,13 @@
"custom_vendor_refill",
"bounty_pad_control",
"bounty_pad",
"digital_clock_frame",
"telescreen_research",
"telescreen_ordnance",
"telescreen_interrogation",
"telescreen_prison",
"telescreen_bar",
"telescreen_entertainment",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
announce_channels = list(RADIO_CHANNEL_SERVICE)
@@ -121,6 +131,8 @@
"coffeemaker",
"coffeepot",
"syrup_bottle",
"foodtray",
"restaurant_portal",
)
/datum/techweb_node/food_proc
+55
View File
@@ -32,3 +32,58 @@
if (isnull(current_design.surgery) || current_design.surgery == default_design_surgery.surgery) //Check if surgery was not set
TEST_FAIL("Surgery Design [current_design.type] has default or null surgery var")
/datum/unit_test/design_source
/datum/unit_test/design_source/Run()
var/list/all_designs = list()
var/list/exceptions = list(
/datum/design/surgery/healing, // Ignored due to the above test
)
for (var/datum/design/design as anything in subtypesof(/datum/design))
var/design_id = design::id
if (design_id == DESIGN_ID_IGNORE || (design in exceptions))
continue
if (design_id in all_designs)
TEST_FAIL("Design [design] shares an ID \"[design_id]\" with another design")
continue
all_designs[design_id] = design
for (var/datum/techweb_node/node as anything in subtypesof(/datum/techweb_node))
node = new node()
for (var/design_id in node.design_ids)
if (!all_designs[design_id])
TEST_FAIL("Techweb node [node.display_name] ([node.id]) has a design_id \"[design_id]\" which doesn't correspond to any existing design!")
continue
all_designs -= design_id
qdel(node)
// Designs can also be disk-exclusive
for (var/obj/item/disk/design_disk/design_disk as anything in subtypesof(/obj/item/disk/design_disk))
design_disk = new design_disk()
for (var/datum/design/design as anything in design_disk.blueprints)
all_designs -= design.id
qdel(design_disk)
for (var/obj/item/disk/surgery/design_disk as anything in subtypesof(/obj/item/disk/surgery))
design_disk = new design_disk()
for (var/surgery_type as anything in design_disk.surgeries)
for (var/design_id in all_designs)
var/datum/design/surgery/design = all_designs[design_id]
if (ispath(design, /datum/design/surgery) && design::surgery == surgery_type)
all_designs -= design::id
qdel(design_disk)
// Or machine-exclusive
for (var/datum/techweb/autounlocking/techweb as anything in subtypesof(/datum/techweb/autounlocking))
techweb = new techweb()
for (var/design_id in techweb.researched_designs + techweb.hacked_designs)
var/datum/design/design = SSresearch.techweb_design_by_id(design_id)
// If we have a design thats supposed to be printable from a protolathe and an autolathe, but only autolathes can print it
// then we still should error because then we either have a missing design_id or redundant build flags
if (!(design.build_type & (~techweb.allowed_buildtypes)))
all_designs -= design_id
qdel(techweb)
for (var/missing_id in all_designs)
TEST_FAIL("Design [all_designs[missing_id]] has an ID \"[missing_id]\" which is not in any of the techweb nodes or tech disks, or it is possibly misconfigured!")
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB