mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-14 19:51:59 +00:00
## About The Pull Request Maked `fieldset_block` and `examine_block` more stylish and neat, also `fieldset_block` no longer has a centred title. Renamed `examine_block` to `boxed_message` and adds `custom_boxed_message` which can be colored. - AdminPMs, admin tickets and vote results has been wrapped into `fieldset_block` for comfort and visibility - Health Analyzer results painted to blue - Vote notice and tips of the round wrapped to purple `custom_boxed_message` - Tooltip text border color, now uses text color, not just white ## Why It's Good For The Game Demonstration in both themes <details><summary>Dark</summary>    </details> <details><summary>Light</summary>    </details> ## Changelog 🆑 qol: AdminPMs, admin tickets, vote results and started vote notification are now much more visible in the chat. qol: Boxed messages in chat (like examine), has been restyled. /🆑
458 lines
19 KiB
Plaintext
458 lines
19 KiB
Plaintext
#define TRADER_RADIAL_BUY "TRADER_RADIAL_BUY"
|
|
#define TRADER_RADIAL_SELL "TRADER_RADIAL_SELL"
|
|
#define TRADER_RADIAL_TALK "TRADER_RADIAL_TALK"
|
|
#define TRADER_RADIAL_LORE "TRADER_RADIAL_LORE"
|
|
#define TRADER_RADIAL_NO "TRADER_RADIAL_NO"
|
|
#define TRADER_RADIAL_YES "TRADER_RADIAL_YES"
|
|
#define TRADER_RADIAL_OUT_OF_STOCK "TRADER_RADIAL_OUT_OF_STOCK"
|
|
#define TRADER_RADIAL_DISCUSS_BUY "TRADER_RADIAL_DISCUSS_BUY"
|
|
#define TRADER_RADIAL_DISCUSS_SELL "TRADER_RADIAL_DISCUSS_SELL"
|
|
|
|
#define TRADER_OPTION_BUY "Buy"
|
|
#define TRADER_OPTION_SELL "Sell"
|
|
#define TRADER_OPTION_TALK "Talk"
|
|
#define TRADER_OPTION_LORE "Lore"
|
|
#define TRADER_OPTION_NO "No"
|
|
#define TRADER_OPTION_YES "Yes"
|
|
#define TRADER_OPTION_BUYING "Buying?"
|
|
#define TRADER_OPTION_SELLING "Selling?"
|
|
|
|
//The defines below show the index the info is located in the product_info entry list
|
|
|
|
#define TRADER_PRODUCT_INFO_PRICE 1
|
|
#define TRADER_PRODUCT_INFO_QUANTITY 2
|
|
//Only valid for wanted_items
|
|
#define TRADER_PRODUCT_INFO_PRICE_MOD_DESCRIPTION 3
|
|
|
|
/**
|
|
* # Trader NPC Component
|
|
* Manages the barks and the stocks of the traders
|
|
* Also manages the interactive radial menu
|
|
*/
|
|
/datum/component/trader
|
|
|
|
/**
|
|
* Format; list(TYPEPATH = list(PRICE, QUANTITY))
|
|
* Associated list of items the NPC sells with how much they cost and the quantity available before a restock
|
|
* This list is filled by Initialize(), if you want to change the starting products, modify initial_products()
|
|
* *
|
|
*/
|
|
var/list/obj/item/products = list()
|
|
/**
|
|
* A list of wanted items that the trader would wish to buy, each typepath has a assigned value, quantity and additional flavor text
|
|
*
|
|
* CHILDREN OF TYPEPATHS INCLUDED IN WANTED_ITEMS WILL BE TREATED AS THE PARENT IF NO ENTRY EXISTS FOR THE CHILDREN
|
|
*
|
|
* As an additional note; if you include multiple children of a typepath; the typepath with the most children should be placed after all other typepaths
|
|
* Bad; list(/obj/item/milk = list(100, 1, ""), /obj/item/milk/small = list(50, 2, ""))
|
|
* Good; list(/obj/item/milk/small = list(50, 2, ""), /obj/item/milk = list(100, 1, ""))
|
|
* This is mainly because sell_item() uses a istype(item_being_sold, item_in_entry) to determine what parent should the child be automatically considered as
|
|
* If /obj/item/milk/small/spooky was being sold; /obj/item/milk/small would be the first to check against rather than /obj/item/milk
|
|
*
|
|
* Format; list(TYPEPATH = list(PRICE, QUANTITY, ADDITIONAL_DESCRIPTION))
|
|
* Associated list of items able to be sold to the NPC with the money given for them.
|
|
* The price given should be the "base" price; any price manipulation based on variables should be done with apply_sell_price_mods()
|
|
* ADDITIONAL_DESCRIPTION is any additional text added to explain how the variables of the item effect the price; if it's stack based, its final price depends how much is in the stack
|
|
* EX; /obj/item/stack/sheet/mineral/diamond = list(500, INFINITY, ", per 100 cm3 sheet of diamond")
|
|
* This list is filled by Initialize(), if you want to change the starting wanted items, modify initial_wanteds()
|
|
*/
|
|
var/list/wanted_items = list()
|
|
|
|
///Contains images of all radial icons
|
|
var/static/list/radial_icons_cache = list()
|
|
|
|
///Contains information of a specific trader
|
|
var/datum/trader_data/trader_data
|
|
|
|
/*
|
|
Can accept both a type path, and an instance of a datum. Type path has priority.
|
|
*/
|
|
/datum/component/trader/Initialize(trader_data_path = null, trader_data = null)
|
|
. = ..()
|
|
if(!isliving(parent))
|
|
return COMPONENT_INCOMPATIBLE
|
|
|
|
if(ispath(trader_data_path, /datum/trader_data))
|
|
trader_data = new trader_data_path
|
|
if(isnull(trader_data))
|
|
CRASH("Initialised trader component with no trader data.")
|
|
|
|
src.trader_data = trader_data
|
|
|
|
radial_icons_cache = list(
|
|
TRADER_RADIAL_BUY = image(icon = 'icons/hud/radial.dmi', icon_state = "radial_buy"),
|
|
TRADER_RADIAL_SELL = image(icon = 'icons/hud/radial.dmi', icon_state = "radial_sell"),
|
|
TRADER_RADIAL_TALK = image(icon = 'icons/hud/radial.dmi', icon_state = "radial_talk"),
|
|
TRADER_RADIAL_LORE = image(icon = 'icons/hud/radial.dmi', icon_state = "radial_lore"),
|
|
TRADER_RADIAL_DISCUSS_BUY = image(icon = 'icons/hud/radial.dmi', icon_state = "radial_buying"),
|
|
TRADER_RADIAL_DISCUSS_SELL = image(icon = 'icons/hud/radial.dmi', icon_state = "radial_selling"),
|
|
TRADER_RADIAL_YES = image(icon = 'icons/hud/radial.dmi', icon_state = "radial_yes"),
|
|
TRADER_RADIAL_NO = image(icon = 'icons/hud/radial.dmi', icon_state = "radial_no"),
|
|
TRADER_RADIAL_OUT_OF_STOCK = image(icon = 'icons/hud/radial.dmi', icon_state = "radial_center"),
|
|
)
|
|
|
|
restock_products()
|
|
renew_item_demands()
|
|
|
|
/datum/component/trader/RegisterWithParent()
|
|
RegisterSignal(parent, COMSIG_ATOM_ATTACK_HAND, PROC_REF(on_attack_hand))
|
|
|
|
/datum/component/trader/UnregisterFromParent()
|
|
UnregisterSignal(parent, COMSIG_ATOM_ATTACK_HAND)
|
|
|
|
///If our trader is alive, and the customer left clicks them with an empty hand without combat mode
|
|
/datum/component/trader/proc/on_attack_hand(atom/source, mob/living/carbon/customer)
|
|
SIGNAL_HANDLER
|
|
if(!can_trade(customer) || customer.combat_mode)
|
|
return
|
|
var/list/npc_options = list()
|
|
if(length(products))
|
|
npc_options[TRADER_OPTION_BUY] = radial_icons_cache[TRADER_RADIAL_BUY]
|
|
if(length(wanted_items))
|
|
npc_options[TRADER_OPTION_SELL] = radial_icons_cache[TRADER_RADIAL_SELL]
|
|
if(length(trader_data.say_phrases))
|
|
npc_options[TRADER_OPTION_TALK] = radial_icons_cache[TRADER_RADIAL_TALK]
|
|
if(!length(npc_options))
|
|
return
|
|
|
|
var/mob/living/trader = parent
|
|
trader.face_atom(customer)
|
|
|
|
INVOKE_ASYNC(src, PROC_REF(open_npc_options), customer, npc_options)
|
|
|
|
return COMPONENT_CANCEL_ATTACK_CHAIN
|
|
|
|
/**
|
|
* Generates a radial of the initial radials of the NPC
|
|
* Called via asynch, due to the sleep caused by show_radial_menu
|
|
* Arguments:
|
|
* * customer - (Mob REF) The mob trying to buy something
|
|
*/
|
|
/datum/component/trader/proc/open_npc_options(mob/living/carbon/customer, list/npc_options)
|
|
if(!can_trade(customer))
|
|
return
|
|
var/npc_result = show_radial_menu(customer, parent, npc_options, custom_check = CALLBACK(src, PROC_REF(check_menu), customer), require_near = TRUE, tooltips = TRUE)
|
|
switch(npc_result)
|
|
if(TRADER_OPTION_BUY)
|
|
buy_item(customer)
|
|
if(TRADER_OPTION_SELL)
|
|
try_sell(customer)
|
|
if(TRADER_OPTION_TALK)
|
|
discuss(customer)
|
|
|
|
/**
|
|
* Checks if the customer is ok to use the radial
|
|
*
|
|
* Checks if the customer is not a mob or is incapacitated or not adjacent to the source of the radial, in those cases returns FALSE, otherwise returns TRUE
|
|
* Arguments:
|
|
* * customer - (Mob REF) The mob checking the menu
|
|
*/
|
|
/datum/component/trader/proc/check_menu(mob/customer)
|
|
if(!istype(customer))
|
|
return FALSE
|
|
if(IS_DEAD_OR_INCAP(customer) || !customer.Adjacent(parent))
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/**
|
|
* Generates a radial of the items the NPC sells and lets the user try to buy one
|
|
* Arguments:
|
|
* * customer - (Mob REF) The mob trying to buy something
|
|
*/
|
|
/datum/component/trader/proc/buy_item(mob/customer)
|
|
if(!can_trade(customer))
|
|
return
|
|
|
|
if(!LAZYLEN(products))
|
|
return
|
|
|
|
var/list/display_names = list()
|
|
var/list/items = list()
|
|
var/list/product_info
|
|
|
|
for(var/obj/item/thing as anything in products)
|
|
display_names["[initial(thing.name)]"] = thing
|
|
|
|
if(!radial_icons_cache[thing])
|
|
radial_icons_cache[thing] = image(icon = initial(thing.icon), icon_state = initial(thing.icon_state_preview) ? initial(thing.icon_state_preview) : initial(thing.icon_state))
|
|
|
|
var/image/item_image = radial_icons_cache[thing]
|
|
product_info = products[thing]
|
|
|
|
if(product_info[TRADER_PRODUCT_INFO_QUANTITY] <= 0) //out of stock
|
|
item_image.overlays += radial_icons_cache[TRADER_RADIAL_OUT_OF_STOCK]
|
|
|
|
items += list("[initial(thing.name)]" = item_image)
|
|
|
|
var/pick = show_radial_menu(customer, parent, items, custom_check = CALLBACK(src, PROC_REF(check_menu), customer), require_near = TRUE, tooltips = TRUE)
|
|
if(!pick || !can_trade(customer))
|
|
return
|
|
|
|
var/obj/item/item_to_buy = display_names[pick]
|
|
var/mob/living/trader = parent
|
|
trader.face_atom(customer)
|
|
product_info = products[item_to_buy]
|
|
|
|
if(!product_info[TRADER_PRODUCT_INFO_QUANTITY])
|
|
trader.say("[initial(item_to_buy.name)] appears to be out of stock.")
|
|
return
|
|
|
|
trader.say("It will cost you [product_info[TRADER_PRODUCT_INFO_PRICE]] [trader_data.currency_name] to buy \the [initial(item_to_buy.name)]. Are you sure you want to buy it?")
|
|
var/list/npc_options = list(
|
|
TRADER_OPTION_YES = radial_icons_cache[TRADER_RADIAL_YES],
|
|
TRADER_OPTION_NO = radial_icons_cache[TRADER_RADIAL_NO],
|
|
)
|
|
|
|
var/buyer_will_buy = show_radial_menu(customer, trader, npc_options, custom_check = CALLBACK(src, PROC_REF(check_menu), customer), require_near = TRUE, tooltips = TRUE)
|
|
if(buyer_will_buy != TRADER_OPTION_YES || !can_trade(customer))
|
|
return
|
|
|
|
trader.face_atom(customer)
|
|
|
|
if(!spend_buyer_offhand_money(customer, product_info[TRADER_PRODUCT_INFO_PRICE]))
|
|
trader.say(trader_data.return_trader_phrase(NO_CASH_PHRASE))
|
|
return
|
|
|
|
item_to_buy = new item_to_buy(get_turf(customer))
|
|
customer.put_in_hands(item_to_buy)
|
|
playsound(trader, trader_data.sell_sound, 50, TRUE)
|
|
log_econ("[item_to_buy] has been sold to [customer] (typepath used for product info; [item_to_buy.type]) by [trader] for [product_info[TRADER_PRODUCT_INFO_PRICE]] cash.")
|
|
product_info[TRADER_PRODUCT_INFO_QUANTITY] -= 1
|
|
trader.say(trader_data.return_trader_phrase(BUY_PHRASE))
|
|
|
|
///Calculates the value of money in the hand of the buyer and spends it if it's sufficient
|
|
/datum/component/trader/proc/spend_buyer_offhand_money(mob/customer, the_cost)
|
|
var/value = 0
|
|
var/obj/item/holochip/cash = customer.is_holding_item_of_type(/obj/item/holochip)
|
|
if(cash)
|
|
value += cash.credits
|
|
if((value >= the_cost) && cash)
|
|
return cash.spend(the_cost)
|
|
return FALSE //Purchase unsuccessful
|
|
|
|
/**
|
|
* Tries to call sell_item on one of the customer's held items, if fail gives a chat message
|
|
*
|
|
* Gets both items in the customer's hands, and then tries to call sell_item on them, if both fail, he gives a chat message
|
|
* Arguments:
|
|
* * customer - (Mob REF) The mob trying to sell something
|
|
*/
|
|
/datum/component/trader/proc/try_sell(mob/customer)
|
|
if(!can_trade(customer))
|
|
return
|
|
var/sold_item = FALSE
|
|
for(var/obj/item/an_item in customer.held_items)
|
|
if(sell_item(customer, an_item))
|
|
sold_item = TRUE
|
|
break
|
|
if(!sold_item && can_trade(customer)) //only talk if you are not dead or in combat
|
|
var/mob/living/trader = parent
|
|
trader.say(trader_data.return_trader_phrase(ITEM_REJECTED_PHRASE))
|
|
|
|
|
|
/**
|
|
* Checks if an item is in the list of wanted items and if it is after a Yes/No radial returns generate_cash with the value of the item for the NPC
|
|
* Arguments:
|
|
* * customer - (Mob REF) The mob trying to sell something
|
|
* * selling - (Item REF) The item being sold
|
|
*/
|
|
/datum/component/trader/proc/sell_item(mob/customer, obj/item/selling)
|
|
if(isnull(selling))
|
|
return FALSE
|
|
var/list/product_info
|
|
//Keep track of the typepath; rather mundane but it's required for correctly modifying the wanted_items
|
|
//should a product be sellable because even if it doesn't have a entry because it's a child of a parent that is present on the list
|
|
var/typepath_for_product_info
|
|
|
|
if(selling.type in wanted_items)
|
|
product_info = wanted_items[selling.type]
|
|
typepath_for_product_info = selling.type
|
|
else //Assume wanted_items is setup in the correct way; read wanted_items documentation for more info
|
|
for(var/typepath in wanted_items)
|
|
if(!istype(selling, typepath))
|
|
continue
|
|
|
|
product_info = wanted_items[typepath]
|
|
typepath_for_product_info = typepath
|
|
break
|
|
|
|
if(!product_info) //Nothing interesting to sell
|
|
return FALSE
|
|
|
|
var/mob/living/trader = parent
|
|
|
|
if(product_info[TRADER_PRODUCT_INFO_QUANTITY] <= 0)
|
|
trader.say(trader_data.return_trader_phrase(TRADER_HAS_ENOUGH_ITEM_PHRASE))
|
|
return FALSE
|
|
|
|
var/cost = apply_sell_price_mods(selling, product_info[TRADER_PRODUCT_INFO_PRICE])
|
|
if(cost <= 0)
|
|
trader.say(trader_data.return_trader_phrase(ITEM_IS_WORTHLESS_PHRASE))
|
|
return FALSE
|
|
|
|
trader.say(trader_data.return_trader_phrase(INTERESTED_PHRASE))
|
|
trader.say("You will receive [cost] [trader_data.currency_name] for the [selling].")
|
|
var/list/npc_options = list(
|
|
TRADER_OPTION_YES = radial_icons_cache[TRADER_RADIAL_YES],
|
|
TRADER_OPTION_NO = radial_icons_cache[TRADER_RADIAL_NO],
|
|
)
|
|
|
|
trader.face_atom(customer)
|
|
|
|
var/npc_result = show_radial_menu(customer, trader, npc_options, custom_check = CALLBACK(src, PROC_REF(check_menu), customer), require_near = TRUE, tooltips = TRUE)
|
|
if(!can_trade(customer))
|
|
return
|
|
if(npc_result != TRADER_OPTION_YES)
|
|
trader.say(trader_data.return_trader_phrase(ITEM_SELLING_CANCELED_PHRASE))
|
|
return TRUE
|
|
|
|
trader.say(trader_data.return_trader_phrase(ITEM_SELLING_ACCEPTED_PHRASE))
|
|
playsound(trader, trader_data.sell_sound, 50, TRUE)
|
|
log_econ("[selling] has been sold to [trader] (typepath used for product info; [typepath_for_product_info]) by [customer] for [cost] cash.")
|
|
exchange_sold_items(selling, cost, typepath_for_product_info)
|
|
generate_cash(cost, customer)
|
|
return TRUE
|
|
|
|
/**
|
|
* Modifies the 'base' price of a item based on certain variables
|
|
*
|
|
* Arguments:
|
|
* * Reference to the item; this is the item being sold
|
|
* * Original cost; the original cost of the item, to be manipulated depending on the variables of the item, one example is using item.amount if it's a stack
|
|
*/
|
|
/datum/component/trader/proc/apply_sell_price_mods(obj/item/selling, original_cost)
|
|
if(isstack(selling))
|
|
var/obj/item/stack/stackoverflow = selling
|
|
original_cost *= stackoverflow.amount
|
|
return original_cost
|
|
|
|
/**
|
|
* Handles modifying/deleting the items to ensure that a proper amount is converted into cash; put into its own proc to make the children of this not override a 30+ line sell_item()
|
|
*
|
|
* Arguments:
|
|
* * selling - (Item REF) this is the item being sold
|
|
* * value_exchanged_for - (Number) the "value", useful for a scenario where you want to remove enough items equal to the value
|
|
* * original_typepath - (Typepath) For scenarios where a children of a parent is being sold but we want to modify the parent's product information
|
|
*/
|
|
/datum/component/trader/proc/exchange_sold_items(obj/item/selling, value_exchanged_for, original_typepath)
|
|
var/list/product_info = wanted_items[original_typepath]
|
|
if(isstack(selling))
|
|
var/obj/item/stack/the_stack = selling
|
|
var/actually_sold = min(the_stack.amount, product_info[TRADER_PRODUCT_INFO_QUANTITY])
|
|
the_stack.use(actually_sold)
|
|
product_info[TRADER_PRODUCT_INFO_QUANTITY] -= (actually_sold)
|
|
else
|
|
qdel(selling)
|
|
product_info[TRADER_PRODUCT_INFO_QUANTITY] -= 1
|
|
|
|
/**
|
|
* Creates an item equal to the value set by the proc and puts it in the user's hands if possible
|
|
* Arguments:
|
|
* * value - A number; The amount of cash that will be on the holochip
|
|
* * customer - Reference to a mob; The mob we put the holochip in hands of
|
|
*/
|
|
/datum/component/trader/proc/generate_cash(value, mob/customer)
|
|
var/obj/item/holochip/chip = new /obj/item/holochip(get_turf(customer), value)
|
|
customer.put_in_hands(chip)
|
|
|
|
///Talk about what items are being sold/wanted by the trader and in what quantity or lore
|
|
/datum/component/trader/proc/discuss(mob/customer)
|
|
var/list/npc_options = list(
|
|
TRADER_OPTION_LORE = radial_icons_cache[TRADER_RADIAL_LORE],
|
|
TRADER_OPTION_SELLING = radial_icons_cache[TRADER_RADIAL_DISCUSS_SELL],
|
|
TRADER_OPTION_BUYING = radial_icons_cache[TRADER_RADIAL_DISCUSS_BUY],
|
|
)
|
|
var/pick = show_radial_menu(customer, parent, npc_options, custom_check = CALLBACK(src, PROC_REF(check_menu), customer), require_near = TRUE, tooltips = TRUE)
|
|
if(!can_trade(customer))
|
|
return
|
|
switch(pick)
|
|
if(TRADER_OPTION_LORE)
|
|
var/mob/living/trader = parent
|
|
trader.say(trader_data.return_trader_phrase(TRADER_LORE_PHRASE))
|
|
if(TRADER_OPTION_BUYING)
|
|
trader_buys_what(customer)
|
|
if(TRADER_OPTION_SELLING)
|
|
trader_sells_what(customer)
|
|
|
|
///Displays to the customer what the trader is willing to buy and how much until a restock happens
|
|
/datum/component/trader/proc/trader_buys_what(mob/customer)
|
|
if(!can_trade(customer))
|
|
return
|
|
if(!length(wanted_items))
|
|
var/mob/living/trader = parent
|
|
trader.say(trader_data.return_trader_phrase(TRADER_NOT_BUYING_ANYTHING))
|
|
return
|
|
|
|
var/list/buy_info = list(span_green("I'm willing to buy the following:"))
|
|
|
|
var/list/product_info
|
|
for(var/obj/item/thing as anything in wanted_items)
|
|
product_info = wanted_items[thing]
|
|
var/tern_op_result = (product_info[TRADER_PRODUCT_INFO_QUANTITY] == INFINITY ? "as many as I can." : "[product_info[TRADER_PRODUCT_INFO_QUANTITY]]") //Coder friendly string concat
|
|
if(product_info[TRADER_PRODUCT_INFO_QUANTITY] <= 0) //Zero demand
|
|
buy_info += span_notice("• [span_red("(DOESN'T WANT MORE)")] [initial(thing.name)] for [product_info[TRADER_PRODUCT_INFO_PRICE]] [trader_data.currency_name][product_info[TRADER_PRODUCT_INFO_PRICE_MOD_DESCRIPTION]]; willing to buy [span_red("[tern_op_result]")] more.")
|
|
else
|
|
buy_info += span_notice("• [initial(thing.name)] for [product_info[TRADER_PRODUCT_INFO_PRICE]] [trader_data.currency_name][product_info[TRADER_PRODUCT_INFO_PRICE_MOD_DESCRIPTION]]; willing to buy [span_green("[tern_op_result]")]")
|
|
|
|
to_chat(customer, boxed_message(buy_info.Join("\n")))
|
|
|
|
///Displays to the customer what the trader is selling and how much is in stock
|
|
/datum/component/trader/proc/trader_sells_what(mob/customer)
|
|
if(!can_trade(customer))
|
|
return
|
|
var/mob/living/trader = parent
|
|
if(!length(products))
|
|
trader.say(trader_data.return_trader_phrase(TRADER_NOT_SELLING_ANYTHING))
|
|
return
|
|
var/list/sell_info = list(span_green("I'm currently selling the following:"))
|
|
var/list/product_info
|
|
for(var/obj/item/thing as anything in products)
|
|
product_info = products[thing]
|
|
var/tern_op_result = (product_info[TRADER_PRODUCT_INFO_QUANTITY] == INFINITY ? "an infinite amount" : "[product_info[TRADER_PRODUCT_INFO_QUANTITY]]") //Coder friendly string concat
|
|
if(product_info[TRADER_PRODUCT_INFO_QUANTITY] <= 0) //Out of stock
|
|
sell_info += span_notice("• [span_red("(OUT OF STOCK)")] [initial(thing.name)] for [product_info[TRADER_PRODUCT_INFO_PRICE]] [trader_data.currency_name]; [span_red("[tern_op_result]")] left in stock")
|
|
else
|
|
sell_info += span_notice("• [initial(thing.name)] for [product_info[TRADER_PRODUCT_INFO_PRICE]] [trader_data.currency_name]; [span_green("[tern_op_result]")] left in stock")
|
|
to_chat(customer, boxed_message(sell_info.Join("\n")))
|
|
|
|
///Sets quantity of all products to initial(quanity); this proc is currently called during initialize
|
|
/datum/component/trader/proc/restock_products()
|
|
products = trader_data.initial_products.Copy()
|
|
|
|
///Sets quantity of all wanted_items to initial(quanity); this proc is currently called during initialize
|
|
/datum/component/trader/proc/renew_item_demands()
|
|
wanted_items = trader_data.initial_wanteds.Copy()
|
|
|
|
///Returns if the trader is conscious and its combat mode is disabled.
|
|
/datum/component/trader/proc/can_trade(mob/customer)
|
|
var/mob/living/trader = parent
|
|
if(trader.combat_mode)
|
|
trader.balloon_alert(customer, "in combat!")
|
|
return FALSE
|
|
if(IS_DEAD_OR_INCAP(trader))
|
|
trader.balloon_alert(customer, "indisposed!")
|
|
return FALSE
|
|
return TRUE
|
|
|
|
#undef TRADER_RADIAL_BUY
|
|
#undef TRADER_RADIAL_SELL
|
|
#undef TRADER_RADIAL_TALK
|
|
#undef TRADER_RADIAL_LORE
|
|
#undef TRADER_RADIAL_DISCUSS_BUY
|
|
#undef TRADER_RADIAL_DISCUSS_SELL
|
|
#undef TRADER_RADIAL_NO
|
|
#undef TRADER_RADIAL_YES
|
|
#undef TRADER_RADIAL_OUT_OF_STOCK
|
|
#undef TRADER_PRODUCT_INFO_PRICE
|
|
#undef TRADER_PRODUCT_INFO_QUANTITY
|
|
#undef TRADER_PRODUCT_INFO_PRICE_MOD_DESCRIPTION
|
|
|
|
#undef TRADER_OPTION_BUY
|
|
#undef TRADER_OPTION_SELL
|
|
#undef TRADER_OPTION_TALK
|
|
#undef TRADER_OPTION_LORE
|
|
#undef TRADER_OPTION_NO
|
|
#undef TRADER_OPTION_YES
|
|
#undef TRADER_OPTION_BUYING
|
|
#undef TRADER_OPTION_SELLING
|