diff --git a/code/__DEFINES/market.dm b/code/__DEFINES/market.dm index e0bd4578352..7c8427f4691 100644 --- a/code/__DEFINES/market.dm +++ b/code/__DEFINES/market.dm @@ -12,3 +12,24 @@ /// The percentage on gains that's removed when selling an item through the blackmarket with the LTSRBT #define MARKET_WITHHOLDING_TAX 0.15 + +//Black Market Uplink categories +#define BLACKMARKET_CATEGORY_CLOTHING "Clothing" +#define BLACKMARKET_CATEGORY_CONSUMABLES "Consumables" +#define BLACKMARKET_CATEGORY_HOSTAGES "Hostages" +#define BLACKMARKET_CATEGORY_LOCAL_GOODS "Local Goods" +#define BLACKMARKET_CATEGORY_MISC "Miscellaneous" +#define BLACKMARKET_CATEGORY_FENCED_GOODS "Fenced Goods" +#define BLACKMARKET_CATEGORY_TOOLS "Tools" +#define BLACKMARKET_CATEGORY_WEAPONS "Weapons" + +#define BLACKMARKET_CATEGORIES list(\ + BLACKMARKET_CATEGORY_CLOTHING,\ + BLACKMARKET_CATEGORY_CONSUMABLES,\ + BLACKMARKET_CATEGORY_HOSTAGES,\ + BLACKMARKET_CATEGORY_LOCAL_GOODS,\ + BLACKMARKET_CATEGORY_MISC,\ + BLACKMARKET_CATEGORY_FENCED_GOODS,\ + BLACKMARKET_CATEGORY_TOOLS,\ + BLACKMARKET_CATEGORY_WEAPONS,\ +) diff --git a/code/controllers/subsystem/market.dm b/code/controllers/subsystem/market.dm index a56ad90b1fd..8d165bee25d 100644 --- a/code/controllers/subsystem/market.dm +++ b/code/controllers/subsystem/market.dm @@ -7,7 +7,7 @@ SUBSYSTEM_DEF(market) /// Descriptions for each shipping methods. var/shipping_method_descriptions = list( - SHIPPING_METHOD_LAUNCH = "Launches the item at the station from space, cheap but you might not receive your item at all.", + SHIPPING_METHOD_LAUNCH = "Launches the item at the station from space, with free shipping! However, you'll need to risk a spacewalk to receive your goods.", SHIPPING_METHOD_LTSRBT = "Long-To-Short-Range-Bluespace-Transceiver, a machine that receives items outside the station and then teleports them to the location of the uplink.", SHIPPING_METHOD_TELEPORT = "Teleports the item in a random area in the station, you get 60 seconds to get there first though.", SHIPPING_METHOD_SUPPLYPOD = "Ships the item inside a supply pod at your exact location. Showy, speedy and expensive.", @@ -19,6 +19,8 @@ SUBSYSTEM_DEF(market) var/list/obj/machinery/ltsrbt/telepads = list() /// Currently queued purchases. var/list/queued_purchases = list() + /// How many admin items have been spawned this round? Used to iterate the identifier of admin-created market items. + var/admin_items_spawned = 0 /datum/controller/subsystem/market/Initialize() for(var/market in subtypesof(/datum/market)) @@ -29,6 +31,11 @@ SUBSYSTEM_DEF(market) return SS_INIT_SUCCESS +/** + * Follows the standard process for adding an item to the market from a path. + * @param path The path of the market datum to initialize and add to the market. + * @param market_whitelist A list of markets to which the item should be added. If null, the item is added to all markets. + */ /datum/controller/subsystem/market/proc/initialize_item(datum/market_item/path, list/market_whitelist) if(!path::item || !prob(path::availability_prob)) return @@ -40,6 +47,19 @@ SUBSYSTEM_DEF(market) if(isnull(market_whitelist) || (potential_market in market_whitelist)) markets[potential_market].add_item(item_instance) +/** + * Adds an admin polled item to the market, expecting a market_item object having been created. + */ +/datum/controller/subsystem/market/proc/initialize_admin_item(datum/market_item/item_instance, list/market_whitelist) + if(!item_instance) + return + for(var/potential_market in item_instance.markets) + if(!markets[potential_market]) + stack_trace("SSmarket: Item [item_instance] available in market that does not exist.") + continue + if(isnull(market_whitelist) || (potential_market in market_whitelist)) + markets[potential_market].add_item(item_instance) + /datum/controller/subsystem/market/fire(resumed) while(length(queued_purchases)) var/datum/market_purchase/purchase = queued_purchases[1] @@ -72,7 +92,7 @@ SUBSYSTEM_DEF(market) // Get random area, throw it somewhere there. if(SHIPPING_METHOD_TELEPORT) - var/turf/targetturf = get_safe_random_station_turf_equal_weight() + var/turf/targetturf = get_safe_random_station_turf_equal_weight() //todo: split weights // This shouldn't happen. if (!targetturf) continue @@ -88,10 +108,11 @@ SUBSYSTEM_DEF(market) var/startSide = pick(GLOB.cardinals) var/turf/T = get_turf(purchase.uplink) var/pickedloc = spaceDebrisStartLoc(startSide, T.z) + var/obj/delivery_pod = new /obj/structure/closet/crate/market(pickedloc) - var/atom/movable/item = purchase.entry.spawn_item(pickedloc, purchase) + var/atom/movable/item = purchase.entry.spawn_item(delivery_pod, purchase) purchase.post_purchase_effects(item) - item.throw_at(purchase.uplink, 3, 3, spin = FALSE) + delivery_pod.throw_at(purchase.uplink, 3, 3, spin = FALSE) to_chat(buyer, span_notice("[purchase.uplink] flashes a message noting the order is being launched at the station from [dir2text(startSide)].")) qdel(purchase) diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 8b5d7911504..14b4fa73ba8 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -1307,6 +1307,9 @@ explosive_resistance += explosion_block SEND_SIGNAL(src, COMSIG_MOVABLE_EXPLOSION_BLOCK_CHANGED, old_block, explosion_block) +/** + * This proc is called when a thrown object makes contact with it's target. It then follows up by calling hitby below. + */ /atom/movable/proc/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) set waitfor = FALSE var/hitpush = TRUE diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm index f293b86e8e5..545d7412773 100644 --- a/code/game/objects/structures/crates_lockers/crates.dm +++ b/code/game/objects/structures/crates_lockers/crates.dm @@ -500,3 +500,25 @@ icon_state = "lavender" base_icon_state = "lavender" glitter_color = "#db80ff" + +/obj/structure/closet/crate/market + name = "shield bubble" + desc = "A rippling blue energy bubble, capable of sustaining itself until it hits a solid wall." + icon = 'icons/effects/effects.dmi' + icon_state = "shield2" + base_icon_state = "shield2" + +/obj/structure/closet/crate/market/after_open(mob/living/user, force) + . = ..() + visible_message(span_notice("[src] pops as [user] touches it!")) + pop_crate() + +/obj/structure/closet/crate/market/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) + . = ..() + pop_crate() + +/// Called when the bubble either arrives at the station, or is interacted with someone/something. +/obj/structure/closet/crate/market/proc/pop_crate() + do_sparks(1, TRUE, get_turf(src)) + dump_contents() + qdel(src) diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index a2ea441d823..5704a111195 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -806,3 +806,45 @@ ADMIN_VERB(give_ai_speech, R_FUN, "Give Random AI Speech", ADMIN_VERB_NO_DESCRIP if (behaviour_exists) return our_controller.planning_subtrees = list(GLOB.ai_subtrees[/datum/ai_planning_subtree/random_speech/blackboard]) + our_controller.planning_subtrees + +ADMIN_VERB(new_blackmarket_item, R_BUILD, "Create Black Market Item", "Add an item to the black market for purchase.", ADMIN_CATEGORY_EVENTS, object as text) + //first: have admins select a typepath for the item they want to offer. + var/obj/chosen = pick_closest_path(object, make_types_fancy(subtypesof(/obj))) + // second: poll admins for the name, description, price, and quantity. + + var/name = tgui_input_text(user, "Name of the item to sell?", "Item listing name", "Arcane Object", max_length = MAX_NAME_LEN) + if(isnull(name)) + return + var/description = tgui_input_text(user, "Custom description of the item to sell?", "Item listing description", "[chosen::desc]", max_length = 200) + if(isnull(description)) + return + var/price = tgui_input_number(user, "Price for the item listing?", "Item listing price", max_value = INFINITY, min_value = 1, round_value = TRUE) + if(isnull(price)) + return + var/quantity = tgui_input_number(user, "Quantity of the item to sell?", "Item listing quantity", default = 1, max_value = 100, min_value = 1, round_value = TRUE) + if(isnull(quantity)) + return + //lastly: pick a category for the item to go under + var/category = tgui_input_list(user, "Category to list the item under?", "Item listing category", BLACKMARKET_CATEGORIES) + if(isnull(category)) + return + + var/datum/market_item/admin_item = new /datum/market_item() + // Making a note here that we don't need to assign to blackmarket because we still only have one market type, but if we ever start using multiple we'll want to poll admins. + admin_item.item = chosen + SSblackbox.record_feedback("tally", "admin blackmarket items", 1, chosen) + + admin_item.name = name + admin_item.desc = description + admin_item.price = price + admin_item.stock = quantity + admin_item.category = category + admin_item.restockable = FALSE + + SSmarket.admin_items_spawned++ + admin_item.identifier = "admin_[SSmarket.admin_items_spawned]" + + SSmarket.initialize_admin_item(admin_item) + log_admin("[key_name(user)] created a new black market item: [name] ([chosen]) for [price] credits, of quantity [quantity].") + + BLACKBOX_LOG_ADMIN_VERB("Create Black Market Item") diff --git a/code/modules/cargo/markets/_market.dm b/code/modules/cargo/markets/_market.dm index 4212ac63d86..78f2d7bd1a3 100644 --- a/code/modules/cargo/markets/_market.dm +++ b/code/modules/cargo/markets/_market.dm @@ -91,7 +91,7 @@ name = "Black Market" shipping = list( SHIPPING_METHOD_LTSRBT = 40, - SHIPPING_METHOD_LAUNCH = 10, - SHIPPING_METHOD_TELEPORT= 75, + SHIPPING_METHOD_LAUNCH = 0, + SHIPPING_METHOD_TELEPORT = 75, ) legal_status = FALSE