diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index 9de0dbe7c5..fa86b909ea 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -474,6 +474,14 @@ #define COMPONENT_BLOCK_SHARPEN_ALREADY 4 #define COMPONENT_BLOCK_SHARPEN_MAXED 8 #define COMSIG_ITEM_MICROWAVE_ACT "microwave_act" //called on item when microwaved (): (obj/machinery/microwave/M) + +// /obj/item signals for economy +#define COMSIG_ITEM_SOLD "item_sold" //called when an item is sold by the exports subsystem +#define COMSIG_STRUCTURE_UNWRAPPED "structure_unwrapped" //called when a wrapped up structure is opened by hand +#define COMSIG_ITEM_UNWRAPPED "item_unwrapped" //called when a wrapped up item is opened by hand + #define COMSIG_ITEM_SPLIT_VALUE 1 +#define COMSIG_ITEM_SPLIT_PROFIT "item_split_profits" //Called when getting the item's exact ratio for cargo's profit. + #define COMSIG_ITEM_WORN_OVERLAYS "item_worn_overlays" //from base of obj/item/worn_overlays(): (isinhands, icon_file, used_state, style_flags, list/overlays) #define COMSIG_ARMOR_PLATED "armor_plated" //called when an armor plate is successfully applied to an object // THE FOLLOWING TWO BLOCKS SHOULD RETURN BLOCK FLAGS AS DEFINED IN __DEFINES/combat.dm! diff --git a/code/datums/components/pricetag.dm b/code/datums/components/pricetag.dm new file mode 100644 index 0000000000..3220dc425a --- /dev/null +++ b/code/datums/components/pricetag.dm @@ -0,0 +1,28 @@ +/datum/component/pricetag + var/datum/bank_account/owner + var/profit_ratio = 1 + +/datum/component/pricetag/Initialize(_owner,_profit_ratio) + if(!isobj(parent)) //Has to account for both objects and sellable structures like crates. + return COMPONENT_INCOMPATIBLE + owner = _owner + if(_profit_ratio) + profit_ratio = _profit_ratio + RegisterSignal(parent, COMSIG_ITEM_SOLD, .proc/split_profit) + RegisterSignal(parent, COMSIG_STRUCTURE_UNWRAPPED, .proc/Unwrapped) + RegisterSignal(parent, COMSIG_ITEM_UNWRAPPED, .proc/Unwrapped) + RegisterSignal(parent, COMSIG_ITEM_SPLIT_PROFIT, .proc/return_ratio) + +/datum/component/pricetag/proc/Unwrapped() + qdel(src) //Once it leaves it's wrapped container, the object in question should lose it's pricetag component. + +/datum/component/pricetag/proc/split_profit(var/item_value) + var/price = item_value + if(price) + var/adjusted_value = price*(profit_ratio/100) + owner.adjust_money(adjusted_value) + owner.bank_card_talk("Sale recorded. [adjusted_value] credits added to account.") + return TRUE + +/datum/component/pricetag/proc/return_ratio() + return profit_ratio diff --git a/code/game/objects/effects/decals/cleanable/misc.dm b/code/game/objects/effects/decals/cleanable/misc.dm index cc9fc8c2b3..7033284a86 100644 --- a/code/game/objects/effects/decals/cleanable/misc.dm +++ b/code/game/objects/effects/decals/cleanable/misc.dm @@ -311,3 +311,9 @@ random_icon_states = list("xfloor1", "xfloor2", "xfloor3", "xfloor4", "xfloor5", "xfloor6", "xfloor7") mergeable_decal = TRUE persistent = TRUE + +/obj/effect/decal/cleanable/wrapping + name = "wrapping shreds" + desc = "Torn pieces of cardboard and paper, left over from a package." + icon = 'icons/obj/objects.dmi' + icon_state = "paper_shreds" diff --git a/code/game/objects/items/stacks/wrap.dm b/code/game/objects/items/stacks/wrap.dm index 6ae63e640f..c969e2d1d4 100644 --- a/code/game/objects/items/stacks/wrap.dm +++ b/code/game/objects/items/stacks/wrap.dm @@ -20,6 +20,9 @@ if(QDELETED(src) && !transfer) new /obj/item/c_tube(T) +/obj/item/stack/wrapping_paper/small + desc = "Wrap packages with this festive paper to make gifts. This roll looks a bit skimpy." + amount = 10 /* * Package Wrap @@ -121,6 +124,11 @@ if(QDELETED(src) && !transfer) new /obj/item/c_tube(T) +/obj/item/stack/packageWrap/small + desc = "You can use this to wrap items in. This roll looks a bit skimpy." + w_class = WEIGHT_CLASS_SMALL + amount = 5 + /obj/item/c_tube name = "cardboard tube" desc = "A tube... of cardboard." diff --git a/code/game/objects/items/storage/boxes.dm b/code/game/objects/items/storage/boxes.dm index 79280faca6..a2ab0c2ff4 100644 --- a/code/game/objects/items/storage/boxes.dm +++ b/code/game/objects/items/storage/boxes.dm @@ -1456,3 +1456,18 @@ /obj/item/storage/box/strange_seeds_5pack/PopulateContents() for(var/i in 1 to 5) new /obj/item/seeds/random(src) + +/obj/item/storage/box/shipping + name = "box of shipping supplies" + desc = "Contains several scanners and labelers for shipping things. Wrapping Paper not included." + illustration = "shipping" + +/obj/item/storage/box/shipping/PopulateContents() + var/static/items_inside = list( + /obj/item/destTagger=1,\ + /obj/item/sales_tagger=1,\ + /obj/item/export_scanner=1,\ + /obj/item/stack/packageWrap/small=2,\ + /obj/item/stack/wrapping_paper/small=1 + ) + generate_items_inside(items_inside,src) diff --git a/code/modules/cargo/bounties/assistant.dm b/code/modules/cargo/bounties/assistant.dm index 35c7c787b6..a009ef20af 100644 --- a/code/modules/cargo/bounties/assistant.dm +++ b/code/modules/cargo/bounties/assistant.dm @@ -101,22 +101,22 @@ required_count = 15 wanted_types = list(/obj/item/shard) -/* /datum/bounty/item/assistant/comfy_chair name = "Comfortable Chairs" description = "Commander Pat is unhappy with his chair. He claims it hurts his back. Ship some alternatives out to humor him." reward = 900 required_count = 5 wanted_types = list(/obj/structure/chair/comfy) -*/ - /datum/bounty/item/assistant/geranium +/* +/datum/bounty/item/assistant/geranium name = "Geraniums" description = "Commander Zot has the hots for Commander Zena. Send a shipment of geraniums - her favorite flower - and he'll happily reward you." reward = 1000 required_count = 3 wanted_types = list(/obj/item/reagent_containers/food/snacks/grown/poppy/geranium) include_subtypes = FALSE +*/ /datum/bounty/item/assistant/poppy name = "Poppies" diff --git a/code/modules/cargo/exports.dm b/code/modules/cargo/exports.dm index e6ed245294..66713a3053 100644 --- a/code/modules/cargo/exports.dm +++ b/code/modules/cargo/exports.dm @@ -33,6 +33,8 @@ Credit dupes that require a lot of manual work shouldn't be removed, unless they if(!GLOB.exports_list.len) setupExports() + var/profit_ratio = 1 //Percentage that gets sent to the seller, rest goes to cargo. + var/list/contents = AM.GetAllContents() var/datum/export_report/report = external_report @@ -47,7 +49,7 @@ Credit dupes that require a lot of manual work shouldn't be removed, unless they if(!E) continue if(E.applies_to(thing, allowed_categories, apply_elastic)) - sold = E.sell_object(thing, report, dry_run, allowed_categories , apply_elastic) + sold = E.sell_object(thing, report, dry_run, allowed_categories , apply_elastic, profit_ratio) report.exported_atoms += " [thing.name]" break if(thing.reagents?.value_multiplier) @@ -138,9 +140,19 @@ Credit dupes that require a lot of manual work shouldn't be removed, unless they var/the_cost = get_cost(O, allowed_categories , apply_elastic) var/amount = get_amount(O) + var/profit_ratio = 0 + if(amount <=0 || the_cost <=0) return FALSE + if(dry_run == FALSE) + if(SEND_SIGNAL(O, COMSIG_ITEM_SOLD, item_value = get_cost(O, allowed_categories , apply_elastic)) & COMSIG_ITEM_SPLIT_VALUE) + profit_ratio = SEND_SIGNAL(O, COMSIG_ITEM_SPLIT_PROFIT) + the_cost = the_cost*((100-profit_ratio)/100) + else + profit_ratio = SEND_SIGNAL(O, COMSIG_ITEM_SPLIT_PROFIT) + the_cost = the_cost*((100-profit_ratio)/100) + report.total_value[src] += the_cost if(istype(O, /datum/export/material)) diff --git a/code/modules/recycling/sortingmachinery.dm b/code/modules/recycling/sortingmachinery.dm index 008e093b74..28176e5ed4 100644 --- a/code/modules/recycling/sortingmachinery.dm +++ b/code/modules/recycling/sortingmachinery.dm @@ -7,9 +7,15 @@ mouse_drag_pointer = MOUSE_ACTIVE_POINTER var/giftwrapped = FALSE var/sortTag = 0 + var/obj/item/barcode/sticker /obj/structure/bigDelivery/interact(mob/user) + to_chat(user, "You start to unwrap the package...") + if(!do_after(user, 15, target = user)) + return playsound(src.loc, 'sound/items/poster_ripped.ogg', 50, 1) + new /obj/effect/decal/cleanable/wrapping(get_turf(user)) + unwrap_contents() qdel(src) /obj/structure/bigDelivery/Destroy() @@ -18,6 +24,11 @@ AM.forceMove(T) return ..() +/obj/structure/bigDelivery/examine(mob/user) + . = ..() + if(sticker) + . += "There's a barcode attached to the side." + /obj/structure/bigDelivery/contents_explosion(severity, target, origin) for(var/atom/movable/AM in contents) AM.ex_act(severity, target, origin) @@ -53,6 +64,47 @@ icon_state = "gift[icon_state]" else to_chat(user, "You need more paper!") + else if(istype(W, /obj/item/sales_tagger)) + var/obj/item/sales_tagger/tagger = W + if(sticker) + to_chat(user, "This package already has a barcode attached!") + return + if(!(tagger.payments_acc)) + to_chat(user, "Swipe an ID on [tagger] first!") + return + if(tagger.paper_count <= 0) + to_chat(user, "[tagger] is out of paper!") + return + user.visible_message("[user] attaches a barcode to [src].", "You attach a barcode to [src].") + tagger.paper_count -= 1 + sticker = new /obj/item/barcode(src) + sticker.payments_acc = tagger.payments_acc //new tag gets the tagger's current account. + sticker.percent_cut = tagger.percent_cut //same, but for the percentage taken. + + var/list/wrap_contents = src.GetAllContents() + for(var/obj/I in wrap_contents) + I.AddComponent(/datum/component/pricetag, sticker.payments_acc, tagger.percent_cut) + var/overlaystring = "[icon_state]_tag" + if(giftwrapped) + overlaystring = copytext(overlaystring, 5) + add_overlay(overlaystring) + else if(istype(W, /obj/item/barcode)) + var/obj/item/barcode/stickerA = W + if(sticker) + to_chat(user, "This package already has a barcode attached!") + return + if(!(stickerA.payments_acc)) + to_chat(user, "This barcode seems to be invalid. Guess it's trash now.") + return + if(!user.transferItemToLoc(W, src)) + to_chat(user, "For some reason, you can't attach [W]!") + return + sticker = stickerA + var/overlaystring = "[icon_state]_tag" + if(giftwrapped) + overlaystring = copytext_char(overlaystring, 5) //5 == length("gift") + 1 + add_overlay(overlaystring) + else return ..() @@ -68,11 +120,18 @@ to_chat(user, "You successfully removed [O]'s wrapping !") O.forceMove(loc) playsound(src.loc, 'sound/items/poster_ripped.ogg', 50, 1) + new /obj/effect/decal/cleanable/wrapping(get_turf(user)) + unwrap_contents() qdel(src) else if(user.loc == src) //so we don't get the message if we resisted multiple times and succeeded. to_chat(user, "You fail to remove [O]'s wrapping!") +/obj/structure/bigDelivery/proc/unwrap_contents() + if(!sticker) + return + for(var/obj/I in src.GetAllContents()) + SEND_SIGNAL(I, COMSIG_STRUCTURE_UNWRAPPED) /obj/item/smallDelivery name = "parcel" @@ -81,17 +140,23 @@ icon_state = "deliverypackage3" var/giftwrapped = 0 var/sortTag = 0 + var/obj/item/barcode/sticker /obj/item/smallDelivery/contents_explosion(severity, target, origin) for(var/atom/movable/AM in contents) AM.ex_act(severity, target, origin) /obj/item/smallDelivery/attack_self(mob/user) + to_chat(user, "You start to unwrap the package...") + if(!do_after(user, 15, target = user)) + return user.temporarilyRemoveItemFromInventory(src, TRUE) + unwrap_contents() for(var/X in contents) var/atom/movable/AM = X user.put_in_hands(AM) playsound(src.loc, 'sound/items/poster_ripped.ogg', 50, 1) + new /obj/effect/decal/cleanable/wrapping(get_turf(user)) qdel(src) /obj/item/smallDelivery/attack_self_tk(mob/user) @@ -106,6 +171,8 @@ var/atom/movable/AM = X AM.forceMove(src.loc) playsound(src.loc, 'sound/items/poster_ripped.ogg', 50, 1) + new /obj/effect/decal/cleanable/wrapping(get_turf(user)) + unwrap_contents() qdel(src) /obj/item/smallDelivery/attackby(obj/item/W, mob/user, params) @@ -140,6 +207,59 @@ else to_chat(user, "You need more paper!") + else if(istype(W, /obj/item/sales_tagger)) + var/obj/item/sales_tagger/tagger = W + if(sticker) + to_chat(user, "This package already has a barcode attached!") + return + if(!(tagger.payments_acc)) + to_chat(user, "Swipe an ID on [tagger] first!") + return + if(tagger.paper_count <= 0) + to_chat(user, "[tagger] is out of paper!") + return + user.visible_message("[user] attaches a barcode to [src].", "You attach a barcode to [src].") + tagger.paper_count -= 1 + sticker = new /obj/item/barcode(src) + sticker.payments_acc = tagger.payments_acc //new tag gets the tagger's current account. + sticker.percent_cut = tagger.percent_cut //as above, as before. + + var/list/wrap_contents = src.GetAllContents() + for(var/obj/I in wrap_contents) + I.AddComponent(/datum/component/pricetag, sticker.payments_acc, tagger.percent_cut) + var/overlaystring = "[icon_state]_tag" + if(giftwrapped) + overlaystring = copytext(overlaystring, 5) + add_overlay(overlaystring) + + else if(istype(W, /obj/item/barcode)) + var/obj/item/barcode/stickerA = W + if(sticker) + to_chat(user, "This package already has a barcode attached!") + return + if(!(stickerA.payments_acc)) + to_chat(user, "This barcode seems to be invalid. Guess it's trash now.") + return + if(!user.transferItemToLoc(W, src)) + to_chat(user, "For some reason, you can't attach [W]!") + return + sticker = stickerA + var/overlaystring = "[icon_state]_tag" + if(giftwrapped) + overlaystring = copytext_char(overlaystring, 5) //5 == length("gift") + 1 + add_overlay(overlaystring) + +/obj/item/smallDelivery/proc/unwrap_contents() + if(!sticker) + return + for(var/obj/I in src.GetAllContents()) + SEND_SIGNAL(I, COMSIG_ITEM_UNWRAPPED) + +/obj/item/smallDelivery/examine(mob/user) + . = ..() + if(sticker) + . += "There's a barcode attached to the side." + /obj/item/destTagger name = "destination tagger" @@ -194,3 +314,90 @@ var/n = text2num(href_list["nextTag"]) currTag = n openwindow(usr) + +/obj/item/sales_tagger + name = "sales tagger" + desc = "A scanner that lets you tag wrapped items for sale, splitting the profit between you and cargo. Ctrl-Click to clear the registered account." + icon = 'icons/obj/device.dmi' + icon_state = "salestagger" + item_state = "electronic" + lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' + righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' + w_class = WEIGHT_CLASS_TINY + slot_flags = ITEM_SLOT_BELT + ///The account which is recieving the split profits. + var/datum/bank_account/payments_acc = null + var/paper_count = 10 + var/max_paper_count = 20 + ///Details the percentage the scanned account recieves off the final sale. + var/percent_cut = 20 + +/obj/item/sales_tagger/examine(mob/user) + . = ..() + . += "[src] has [paper_count]/[max_paper_count] available barcodes. Refill with paper." + . += "Profit split on sale is currently set to [percent_cut]%." + +/obj/item/sales_tagger/attackby(obj/item/I, mob/living/user, params) + . = ..() + if(istype(I, /obj/item/card/id)) + var/obj/item/card/id/potential_acc = I + if(potential_acc.registered_account) + payments_acc = potential_acc.registered_account + playsound(src, 'sound/machines/ping.ogg', 40, TRUE) + to_chat(user, "[src] registers the ID card. Tag a wrapped item to create a barcode.") + else if(!potential_acc.registered_account) + to_chat(user, "This ID card has no account registered!") + return + else if(payments_acc != potential_acc.registered_account) + to_chat(user, "ID card already registered.") + if(istype(I, /obj/item/paper)) + if (!(paper_count >= max_paper_count)) + paper_count += 10 + qdel(I) + if (paper_count >= max_paper_count) + paper_count = max_paper_count + to_chat(user, "[src]'s paper supply is now full.") + return + to_chat(user, "You refill [src]'s paper supply, you have [paper_count] left.") + return + else + to_chat(user, "[src]'s paper supply is full.") + return + +/obj/item/sales_tagger/attack_self(mob/user) + . = ..() + if(paper_count <= 0) + to_chat(user, "You're out of paper!'.") + return + if(!payments_acc) + to_chat(user, "You need to swipe [src] with an ID card first.") + return + paper_count -= 1 + playsound(src, 'sound/machines/click.ogg', 40, TRUE) + to_chat(user, "You print a new barcode.") + var/obj/item/barcode/new_barcode = new /obj/item/barcode(src) + new_barcode.payments_acc = payments_acc //The sticker gets the scanner's registered account. + user.put_in_hands(new_barcode) + +/obj/item/sales_tagger/CtrlClick(mob/user) + . = ..() + payments_acc = null + to_chat(user, "You clear the registered account.") + +/obj/item/sales_tagger/AltClick(mob/user) + . = ..() + var/potential_cut = input("How much would you like to payout to the registered card?","Percentage Profit") as num|null + if(!potential_cut) + percent_cut = 50 + percent_cut = clamp(round(potential_cut, 1), 1, 50) + to_chat(user, "[percent_cut]% profit will be recieved if a package with a barcode is sold.") + +/obj/item/barcode + name = "Barcode tag" + desc = "A tiny tag, associated with a crewmember's account. Attach to a wrapped item to give that account a portion of the wrapped item's profit." + icon = 'icons/obj/bureaucracy.dmi' + icon_state = "barcode" + w_class = WEIGHT_CLASS_TINY + ///All values inheirited from the sales tagger it came from. + var/datum/bank_account/payments_acc = null + var/percent_cut = 5 diff --git a/code/modules/research/designs/autolathe_desings/autolathe_designs_electronics.dm b/code/modules/research/designs/autolathe_desings/autolathe_designs_electronics.dm index 78b0a365c5..fea67a921f 100644 --- a/code/modules/research/designs/autolathe_desings/autolathe_designs_electronics.dm +++ b/code/modules/research/designs/autolathe_desings/autolathe_designs_electronics.dm @@ -90,3 +90,12 @@ materials = list(/datum/material/iron = 2010, /datum/material/glass = 5) build_path = /obj/item/electronics/electrochromatic_kit category = list("initial", "Electronics") + +/datum/design/salestagger + name = "Sales Tagger" + id = "salestagger" + build_type = AUTOLATHE | PROTOLATHE + materials = list(/datum/material/iron = 700, /datum/material/glass = 200) + build_path = /obj/item/sales_tagger + category = list("initial", "Electronics") + departmental_flags = DEPARTMENTAL_FLAG_CARGO | DEPARTMENTAL_FLAG_SERVICE diff --git a/code/modules/research/techweb/_techweb_node.dm b/code/modules/research/techweb/_techweb_node.dm index 8c91c09abb..92eaa2bd41 100644 --- a/code/modules/research/techweb/_techweb_node.dm +++ b/code/modules/research/techweb/_techweb_node.dm @@ -106,4 +106,4 @@ "destructive_analyzer", "circuit_imprinter", "experimentor", "rdconsole", "bepis", "design_disk", "tech_disk", "rdserver", "rdservercontrol", "mechfab", "paystand", "space_heater", "beaker", "large_beaker", "xlarge_beaker", "bucket", "hypovial", "large_hypovial", "syringe", "pillbottle", "sec_shellclip", "sec_beanbag", "sec_rshot", "sec_bshot", "sec_slug", "sec_islug", "sec_dart", "sec_38", "sec_38lethal", - "rglass","plasteel","plastitanium","plasmaglass","plasmareinforcedglass","titaniumglass","plastitaniumglass") + "rglass","plasteel","plastitanium","plasmaglass","plasmareinforcedglass","titaniumglass","plastitaniumglass", "salestagger") diff --git a/icons/obj/computer.dmi b/icons/obj/computer.dmi index 33cd46d15f..36402ad2d5 100644 Binary files a/icons/obj/computer.dmi and b/icons/obj/computer.dmi differ diff --git a/icons/obj/device.dmi b/icons/obj/device.dmi index 5254b53d2b..62fe5bbf92 100644 Binary files a/icons/obj/device.dmi and b/icons/obj/device.dmi differ diff --git a/icons/obj/storage.dmi b/icons/obj/storage.dmi index 2c926ef639..27ad84b1a4 100644 Binary files a/icons/obj/storage.dmi and b/icons/obj/storage.dmi differ diff --git a/tgstation.dme b/tgstation.dme index 0cbed7f72e..90f21bf649 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -562,6 +562,7 @@ #include "code\datums\components\paintable.dm" #include "code\datums\components\pellet_cloud.dm" #include "code\datums\components\phantomthief.dm" +#include "code\datums\components\pricetag.dm" #include "code\datums\components\rad_insulation.dm" #include "code\datums\components\radioactive.dm" #include "code\datums\components\remote_materials.dm"