diff --git a/_maps/map_files/BoxStation/BoxStation.dmm b/_maps/map_files/BoxStation/BoxStation.dmm
index f675671b3f0..6e0598df41a 100644
--- a/_maps/map_files/BoxStation/BoxStation.dmm
+++ b/_maps/map_files/BoxStation/BoxStation.dmm
@@ -52558,6 +52558,16 @@
},
/turf/closed/wall/r_wall,
/area/maintenance/disposal/incinerator)
+"tXW" = (
+/obj/structure/table/reinforced,
+/obj/machinery/door/firedoor,
+/obj/machinery/door/poddoor/shutters/preopen{
+ id = "kitchen";
+ name = "kitchen shutters"
+ },
+/obj/structure/displaycase/forsale,
+/turf/open/floor/plasteel/cafeteria,
+/area/crew_quarters/kitchen)
"tYi" = (
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
/obj/structure/disposalpipe/segment{
@@ -53073,6 +53083,15 @@
},
/turf/open/floor/plating,
/area/science/nanite)
+"wlq" = (
+/obj/structure/table/reinforced,
+/obj/effect/turf_decal/tile/bar,
+/obj/effect/turf_decal/tile/bar{
+ dir = 1
+ },
+/obj/structure/displaycase/forsale,
+/turf/open/floor/plasteel,
+/area/crew_quarters/bar)
"wph" = (
/obj/docking_port/stationary{
dheight = 4;
@@ -89895,7 +89914,7 @@ aXj
aVy
aSY
aVy
-aVy
+wlq
aYI
bah
aJC
@@ -92467,7 +92486,7 @@ aTO
cCq
aVz
aVz
-bak
+tXW
bbz
aYV
bdp
diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm
index 8208bbfab7d..40bf60febca 100644
--- a/_maps/map_files/Deltastation/DeltaStation2.dmm
+++ b/_maps/map_files/Deltastation/DeltaStation2.dmm
@@ -113598,6 +113598,21 @@
},
/turf/open/floor/plasteel,
/area/maintenance/port)
+"kDh" = (
+/obj/structure/table/reinforced,
+/obj/effect/turf_decal/tile/neutral{
+ dir = 1
+ },
+/obj/effect/turf_decal/tile/neutral,
+/obj/effect/turf_decal/tile/neutral{
+ dir = 4
+ },
+/obj/effect/turf_decal/tile/neutral{
+ dir = 8
+ },
+/obj/structure/displaycase/forsale,
+/turf/open/floor/plasteel/dark,
+/area/crew_quarters/bar)
"kzm" = (
/obj/effect/turf_decal/tile/blue{
dir = 4
@@ -114877,6 +114892,17 @@
/obj/structure/cable,
/turf/open/floor/plating,
/area/maintenance/port)
+"qkz" = (
+/obj/structure/table/reinforced,
+/obj/machinery/door/firedoor,
+/obj/machinery/door/poddoor/shutters/preopen{
+ id = "kitchencounter";
+ name = "Kitchen Counter Shutters"
+ },
+/obj/effect/turf_decal/delivery,
+/obj/structure/displaycase/forsale,
+/turf/open/floor/plasteel,
+/area/crew_quarters/kitchen)
"qkG" = (
/obj/effect/mapping_helpers/airlock/cyclelink_helper{
dir = 8
@@ -153594,7 +153620,7 @@ aVr
aNh
aLL
aKz
-bcj
+qkz
bdJ
bfe
bgs
@@ -154610,7 +154636,7 @@ aDL
aFa
aGl
aHE
-aJa
+kDh
aKB
aLM
aLL
diff --git a/_maps/map_files/Donutstation/Donutstation.dmm b/_maps/map_files/Donutstation/Donutstation.dmm
index a53be8f3526..19d45a9f889 100644
--- a/_maps/map_files/Donutstation/Donutstation.dmm
+++ b/_maps/map_files/Donutstation/Donutstation.dmm
@@ -32452,6 +32452,7 @@
/obj/structure/window/reinforced{
dir = 1
},
+/obj/structure/displaycase/forsale,
/turf/open/floor/plasteel,
/area/crew_quarters/bar)
"bGZ" = (
@@ -38400,6 +38401,16 @@
/obj/structure/cable,
/turf/open/floor/plating,
/area/maintenance/port)
+"bYB" = (
+/obj/structure/table/reinforced,
+/obj/machinery/door/firedoor,
+/obj/machinery/door/poddoor/shutters/preopen{
+ id = "kitchenshutter";
+ name = "privacy shutters"
+ },
+/obj/structure/displaycase/forsale,
+/turf/open/floor/plasteel/cafeteria,
+/area/crew_quarters/kitchen)
"bYJ" = (
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden/layer3{
dir = 4
@@ -83468,7 +83479,7 @@ akG
bZC
alu
alF
-anJ
+bYB
anJ
aoj
anJ
diff --git a/_maps/map_files/KiloStation/KiloStation.dmm b/_maps/map_files/KiloStation/KiloStation.dmm
index 7e74fd5b366..30b5c21e553 100644
--- a/_maps/map_files/KiloStation/KiloStation.dmm
+++ b/_maps/map_files/KiloStation/KiloStation.dmm
@@ -31887,14 +31887,10 @@
dir = 1
},
/obj/effect/turf_decal/tile/neutral,
-/obj/item/radio{
- desc = "An old handheld radio. You could use it, if you really wanted to.";
- icon_state = "radio";
- name = "old radio"
- },
/obj/structure/disposalpipe/segment{
dir = 4
},
+/obj/structure/displaycase/forsale,
/turf/open/floor/plasteel/dark,
/area/crew_quarters/bar)
"aXn" = (
@@ -42996,6 +42992,7 @@
name = "Hallway Hatch"
},
/obj/structure/window/reinforced,
+/obj/item/reagent_containers/food/snacks/pie/cream,
/turf/open/floor/plating,
/area/crew_quarters/kitchen)
"bmT" = (
@@ -44087,11 +44084,11 @@
/obj/structure/table/reinforced,
/obj/effect/turf_decal/delivery,
/obj/machinery/door/firedoor,
-/obj/item/reagent_containers/food/snacks/pie/cream,
/obj/machinery/door/poddoor/shutters/preopen{
id = "kitchen";
name = "Serving Hatch"
},
+/obj/structure/displaycase/forsale,
/turf/open/floor/plating,
/area/crew_quarters/kitchen)
"box" = (
@@ -93733,6 +93730,19 @@
/obj/machinery/holopad/secure,
/turf/open/floor/wood,
/area/crew_quarters/heads/captain)
+"gXk" = (
+/obj/structure/table/wood,
+/obj/effect/turf_decal/tile/neutral{
+ dir = 1
+ },
+/obj/effect/turf_decal/tile/neutral,
+/obj/item/radio{
+ desc = "An old handheld radio. You could use it, if you really wanted to.";
+ icon_state = "radio";
+ name = "old radio"
+ },
+/turf/open/floor/plasteel/dark,
+/area/crew_quarters/bar)
"llk" = (
/obj/effect/turf_decal/stripes/line{
dir = 8
@@ -119894,7 +119904,7 @@ aKK
cfa
btE
aXm
-btX
+gXk
bva
aYv
btX
diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm
index 1bd685297b6..e8d7bbf67bf 100644
--- a/_maps/map_files/MetaStation/MetaStation.dmm
+++ b/_maps/map_files/MetaStation/MetaStation.dmm
@@ -39299,6 +39299,7 @@
id = "kitchen";
name = "Serving Hatch"
},
+/obj/structure/displaycase/forsale,
/turf/open/floor/plasteel/cafeteria{
dir = 5
},
@@ -73871,6 +73872,15 @@
/obj/effect/turf_decal/tile/purple,
/turf/open/floor/plasteel/white,
/area/science/misc_lab/range)
+"pzW" = (
+/obj/structure/table/reinforced,
+/obj/effect/turf_decal/tile/bar,
+/obj/effect/turf_decal/tile/bar{
+ dir = 1
+ },
+/obj/structure/displaycase/forsale,
+/turf/open/floor/plasteel,
+/area/crew_quarters/bar)
"pCp" = (
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
/obj/structure/cable,
@@ -109471,7 +109481,7 @@ bmO
aat
aaL
byy
-bAj
+pzW
bBT
bDD
bFl
diff --git a/_maps/map_files/PubbyStation/PubbyStation.dmm b/_maps/map_files/PubbyStation/PubbyStation.dmm
index d1a94c845bf..c51316269e9 100644
--- a/_maps/map_files/PubbyStation/PubbyStation.dmm
+++ b/_maps/map_files/PubbyStation/PubbyStation.dmm
@@ -55151,6 +55151,17 @@
},
/turf/open/floor/plasteel/white,
/area/medical/chemistry)
+"pVr" = (
+/obj/structure/table/reinforced,
+/obj/effect/turf_decal/tile/red{
+ dir = 1
+ },
+/obj/effect/turf_decal/tile/red{
+ dir = 4
+ },
+/obj/structure/displaycase/forsale,
+/turf/open/floor/plasteel/dark,
+/area/crew_quarters/bar)
"pVD" = (
/obj/machinery/light/small{
dir = 1
@@ -56092,6 +56103,22 @@
/obj/structure/girder,
/turf/open/floor/plating,
/area/maintenance/department/crew_quarters/dorms)
+"sbI" = (
+/obj/structure/table/reinforced,
+/obj/machinery/door/poddoor/shutters/preopen{
+ id = "kitchenshutters";
+ name = "kitchen shutters"
+ },
+/obj/machinery/door/firedoor,
+/obj/effect/turf_decal/tile/red{
+ dir = 1
+ },
+/obj/effect/turf_decal/tile/red{
+ dir = 8
+ },
+/obj/structure/displaycase/forsale,
+/turf/open/floor/plasteel/dark,
+/area/crew_quarters/kitchen)
"sbY" = (
/obj/machinery/vending/coffee,
/turf/open/floor/wood,
@@ -89463,7 +89490,7 @@ bad
bbl
bbl
bbl
-bbl
+sbI
aRN
bgo
bhd
@@ -91770,7 +91797,7 @@ aPE
cpb
bah
bah
-aYg
+pVr
cpi
bag
bbr
diff --git a/code/game/objects/structures/displaycase.dm b/code/game/objects/structures/displaycase.dm
index 81e31dc49b1..9d77a9718d7 100644
--- a/code/game/objects/structures/displaycase.dm
+++ b/code/game/objects/structures/displaycase.dm
@@ -10,6 +10,7 @@
max_integrity = 200
integrity_failure = 0.25
var/obj/item/showpiece = null
+ var/obj/item/showpiece_type = null //This allows for showpieces that can only hold items if they're the same istype as this.
var/alert = TRUE
var/open = FALSE
var/openable = TRUE
@@ -17,6 +18,7 @@
var/start_showpiece_type = null //add type for items on display
var/list/start_showpieces = list() //Takes sublists in the form of list("type" = /obj/item/bikehorn, "trophy_message" = "henk")
var/trophy_message = ""
+ var/glass_fix = TRUE
/obj/structure/displaycase/Initialize()
. = ..()
@@ -131,11 +133,14 @@
to_chat(user, "You [open ? "close":"open"] [src].")
toggle_lock(user)
else if(open && !showpiece)
+ if(showpiece_type && !istype(W, showpiece_type))
+ to_chat(user, "This doesn't belong in this kind of display.")
+ return TRUE
if(user.transferItemToLoc(W, src))
showpiece = W
to_chat(user, "You put [W] on display.")
update_icon()
- else if(istype(W, /obj/item/stack/sheet/glass) && broken)
+ else if(glass_fix && broken && istype(W, /obj/item/stack/sheet/glass))
var/obj/item/stack/sheet/glass/G = W
if(G.get_amount() < 2)
to_chat(user, "You need two glass sheets to fix the case!")
@@ -206,6 +211,21 @@
electronics = I
to_chat(user, "You install the airlock electronics.")
+ else if(istype(I, /obj/item/stock_parts/card_reader))
+ var/obj/item/stock_parts/card_reader/C = I
+ to_chat(user, "You start adding [C] to [src]...")
+ if(do_after(user, 20, target = src))
+ var/obj/structure/displaycase/forsale/sale = new(src.loc)
+ if(electronics)
+ electronics.forceMove(sale)
+ sale.electronics = electronics
+ if(electronics.one_access)
+ sale.req_one_access = electronics.accesses
+ else
+ sale.req_access = electronics.accesses
+ qdel(src)
+ qdel(C)
+
else if(istype(I, /obj/item/stack/sheet/glass))
var/obj/item/stack/sheet/glass/G = I
if(G.get_amount() < 10)
@@ -342,3 +362,137 @@
name = initial(I.name)
icon = initial(I.icon)
icon_state = initial(I.icon_state)
+
+/obj/structure/displaycase/forsale
+ name = "vend-a-tray"
+ icon = 'icons/obj/stationobjs.dmi'
+ icon_state = "laserbox0"
+ desc = "A display case with an ID-card swiper. Use your ID to purchase the contents."
+ density = FALSE
+ max_integrity = 100
+ req_access = list(ACCESS_KITCHEN)
+ showpiece_type = /obj/item/reagent_containers/food
+ alert = FALSE //No, we're not calling the fire department because someone stole your cookie.
+ glass_fix = FALSE //Fixable with tools instead.
+ var/sale_price = 20
+ var/datum/bank_account/payments_acc = null
+
+/obj/structure/displaycase/forsale/update_icon() //remind me to fix my shitcode later
+ var/icon/I
+ if(open)
+ I = icon('icons/obj/stationobjs.dmi',"laserboxb0")
+ else
+ I = icon('icons/obj/stationobjs.dmi',"laserbox0")
+ if(!showpiece && !open)
+ I = icon('icons/obj/stationobjs.dmi',"laserbox_open")
+ if(broken)
+ I = icon('icons/obj/stationobjs.dmi',"laserbox_broken")
+ if(showpiece)
+ var/icon/S = getFlatIcon(showpiece)
+ S.Scale(17,17)
+ I.Blend(S,ICON_UNDERLAY,8,12)
+ src.icon = I
+ return
+
+/obj/structure/displaycase/forsale/attackby(obj/item/I, mob/living/user, params)
+ if(isidcard(I))
+ //Card Registration
+ var/obj/item/card/id/potential_acc = I
+ if(!potential_acc.registered_account)
+ to_chat(user, "This ID card has no account registered!")
+ return
+ if(payments_acc && payments_acc != potential_acc.registered_account)
+ to_chat(user, "This Vend-a-tray is already registered!")
+ return
+ if(!payments_acc && potential_acc.registered_account)
+ payments_acc = potential_acc.registered_account
+ playsound(src, 'sound/machines/click.ogg', 20, TRUE)
+ to_chat(user, "Vend-a-tray registered. Use your ID on grab intent to change the sale price, or disarm intent to open the tray.")
+ return
+ //Buying the contained item with the ID.
+ switch(user.a_intent)
+ if(INTENT_HELP)
+ if(!showpiece)
+ to_chat(user, "There's nothing for sale.")
+ return TRUE
+ if(broken)
+ to_chat(user, "[src] appears to be broken.")
+ return TRUE
+ var/confirm = alert(user, "Purchase [showpiece] for [sale_price]?", "Purchase?", "Confirm", "Cancel")
+ if(confirm == "Cancel" || !user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
+ return TRUE
+ var/datum/bank_account/account = potential_acc.registered_account
+ if(!account.has_money(sale_price))
+ to_chat(user, "You do not possess the funds to purchase this.")
+ return TRUE
+ else
+ account.adjust_money(-sale_price)
+ if(payments_acc)
+ payments_acc.adjust_money(sale_price)
+ user.put_in_hands(showpiece)
+ to_chat(user, "You purchase [showpiece] for [sale_price] credits.")
+ playsound(src, 'sound/effects/cashregister.ogg', 40, TRUE)
+ icon = 'icons/obj/stationobjs.dmi'
+ flick("laserbox_vend", src)
+ showpiece = null
+ update_icon()
+ return TRUE
+ //Setting the object's price.
+ if(INTENT_GRAB)
+ var/new_price_input = input(user,"Set the sale price for this vend-a-tray.","new price",0) as num|null
+ if(isnull(new_price_input) || !user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
+ return
+ new_price_input = clamp(round(new_price_input, 1), 10, 1000)
+ sale_price = new_price_input
+ to_chat(user, "The cost is now set to [sale_price].")
+ return TRUE
+ if(I.tool_behaviour == TOOL_WRENCH && open && user.a_intent == INTENT_HELP )
+ if(anchored)
+ to_chat(user, "You start unsecuring [src]...")
+ else
+ to_chat(user, "You start securing [src]...")
+ if(I.use_tool(src, user, 16, volume=50))
+ if(QDELETED(I))
+ return
+ if(anchored)
+ to_chat(user, "You unsecure [src].")
+ else
+ to_chat(user, "You secure [src].")
+ anchored = !anchored
+ return
+ else if(I.tool_behaviour == TOOL_WRENCH && !open && user.a_intent == INTENT_HELP)
+ to_chat(user, "[src] must be open to move it.")
+ return
+ if(istype(I, /obj/item/pda))
+ return TRUE
+ . = ..()
+
+/obj/structure/displaycase/forsale/multitool_act(mob/living/user, obj/item/I)
+ . = ..()
+ if(obj_integrity <= (integrity_failure * max_integrity))
+ to_chat(user, "You start recalibrating [src]'s hover field...")
+ if(do_after(user, 20, target = src))
+ broken = 0
+ obj_integrity = max_integrity
+ update_icon()
+ return TRUE
+
+/obj/structure/displaycase/forsale/emag_act(mob/user)
+ . = ..()
+ payments_acc = null
+ req_access = list()
+ to_chat(user, "[src]'s card reader fizzles and smokes, and the account owner is reset.")
+
+/obj/structure/displaycase/forsale/examine(mob/user)
+ . = ..()
+ if(showpiece && !open)
+ . += "[showpiece] is for sale for [sale_price] credits."
+ if(broken)
+ . += "[src] is sparking and the hover field generator seems to be overloaded. Use a multitool to fix it."
+
+/obj/structure/displaycase/forsale/obj_break(damage_flag)
+ if(!broken && !(flags_1 & NODECONSTRUCT_1))
+ broken = TRUE
+ playsound(src, "shatter", 70, TRUE)
+ update_icon()
+ trigger_alarm() //In case it's given an alarm anyway.
diff --git a/code/modules/research/designs/stock_parts_designs.dm b/code/modules/research/designs/stock_parts_designs.dm
index ce8a46ea879..fa718344e94 100644
--- a/code/modules/research/designs/stock_parts_designs.dm
+++ b/code/modules/research/designs/stock_parts_designs.dm
@@ -316,3 +316,13 @@
build_path = /obj/item/stock_parts/subspace/transmitter
category = list("Stock Parts")
departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_SCIENCE
+
+/datum/design/card_reader
+ name = "Card Reader"
+ desc = "A small magnetic card reader, used for devices that take and transmit holocredits."
+ id = "c-reader"
+ build_type = PROTOLATHE
+ materials = list(/datum/material/iron=50, /datum/material/glass=10)
+ build_path = /obj/item/stock_parts/card_reader
+ category = list("Stock Parts")
+ departmental_flags = DEPARTMENTAL_FLAG_ALL
diff --git a/code/modules/research/stock_parts.dm b/code/modules/research/stock_parts.dm
index 352d3010120..ac082f11d6e 100644
--- a/code/modules/research/stock_parts.dm
+++ b/code/modules/research/stock_parts.dm
@@ -317,6 +317,12 @@ If you create T5+ please take a pass at gene_modder.dm [L40]. Max_values MUST fi
desc = "A large piece of equipment used to open a window into the subspace dimension."
custom_materials = list(/datum/material/iron=50)
+/obj/item/stock_parts/card_reader
+ name = "card reader"
+ icon_state = "card_reader"
+ desc = "A small magnetic card reader, used for devices that take and transmit holocredits."
+ custom_materials = list(/datum/material/iron=50, /datum/material/glass=10)
+
/obj/item/research//Makes testing much less of a pain -Sieve
name = "research"
icon = 'icons/obj/stock_parts.dmi'
diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm
index 54095e6b354..e2e6f165fc8 100644
--- a/code/modules/research/techweb/all_nodes.dm
+++ b/code/modules/research/techweb/all_nodes.dm
@@ -8,7 +8,7 @@
display_name = "Basic Research Technology"
description = "NT default research technologies."
// Default research tech, prevents bricking
- design_ids = list("basic_matter_bin", "basic_cell", "basic_scanning", "basic_capacitor", "basic_micro_laser", "micro_mani", "desttagger", "salestagger", "handlabel", "packagewrap",
+ design_ids = list("basic_matter_bin", "basic_cell", "basic_scanning", "basic_capacitor", "basic_micro_laser", "micro_mani", "c-reader", "desttagger", "salestagger", "handlabel", "packagewrap",
"destructive_analyzer", "circuit_imprinter", "experimentor", "rdconsole", "bepis", "design_disk", "tech_disk", "rdserver", "rdservercontrol", "mechfab",
"paystand", "space_heater", "bucket", "sec_rshot", "sec_beanbag_slug", "sec_bshot", "sec_slug", "sec_Islug", "sec_dart", "sec_38", "rglass", "plasteel",
"plastitanium", "plasmaglass", "plasmareinforcedglass", "titaniumglass", "plastitaniumglass", "plastic_knife", "plastic_fork", "plastic_spoon")
diff --git a/icons/obj/stationobjs.dmi b/icons/obj/stationobjs.dmi
index c8ee39ede6d..0d487ca493a 100644
Binary files a/icons/obj/stationobjs.dmi and b/icons/obj/stationobjs.dmi differ
diff --git a/icons/obj/stock_parts.dmi b/icons/obj/stock_parts.dmi
index fbc8be5474b..d71a7625213 100644
Binary files a/icons/obj/stock_parts.dmi and b/icons/obj/stock_parts.dmi differ
diff --git a/sound/effects/cashregister.ogg b/sound/effects/cashregister.ogg
new file mode 100644
index 00000000000..fa1f29f1de2
Binary files /dev/null and b/sound/effects/cashregister.ogg differ