Refactors pricetag component: no getcomponent, no ugly signals, fixes cubes a bit (#63954)

Refactors the pricetag component
Removes a getcomponent for the pricetag component in cube export handling (replaced with inherit component behavior)
Removes some nasty signals which were effectively just send signal, get 1
Deletes the internal radio within bounty cubes from before exporting
Disallows bounty cubes from being barcoded with TRAIT_NO_BARCODES
Prevents bounty cube pricetag component from being deleted by unwrapping
Closes #63921 technically
This commit is contained in:
MrMelbert
2022-01-11 21:22:52 +00:00
committed by GitHub
parent 4ef3632b84
commit 36e2c1c1c7
7 changed files with 156 additions and 69 deletions
+7 -2
View File
@@ -197,13 +197,18 @@
#define COMSIG_OBJ_ATTEMPT_CHARGE_CHANGE "obj_attempt_simple_charge_change"
// /obj/item signals for economy
///called before an item is sold by the exports system.
#define COMSIG_ITEM_PRE_EXPORT "item_pre_sold"
/// Stops the export from calling sell_object() on the item, so you can handle it manually.
#define COMPONENT_STOP_EXPORT (1<<0)
///called when an item is sold by the exports subsystem
#define COMSIG_ITEM_SOLD "item_sold"
#define COMSIG_ITEM_EXPORTED "item_sold"
/// Stops the export from adding the export information to the report, so you can handle it manually.
#define COMPONENT_STOP_EXPORT_REPORT (1<<0)
///called when a wrapped up structure is opened by hand
#define COMSIG_STRUCTURE_UNWRAPPED "structure_unwrapped"
///called when a wrapped up item is opened by hand
#define COMSIG_ITEM_UNWRAPPED "item_unwrapped"
#define COMSIG_ITEM_SPLIT_VALUE (1<<0)
///called when getting the item's exact ratio for cargo's profit.
#define COMSIG_ITEM_SPLIT_PROFIT "item_split_profits"
///called when getting the item's exact ratio for cargo's profit, without selling the item.
+2
View File
@@ -500,6 +500,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_NO_IMMOBILIZE "no_immobilize"
/// Prevents stripping this equipment
#define TRAIT_NO_STRIP "no_strip"
/// Disallows this item from being pricetagged with a barcode
#define TRAIT_NO_BARCODES "no_barcode"
//quirk traits
#define TRAIT_ALCOHOL_TOLERANCE "alcohol_tolerance"
+88 -26
View File
@@ -1,40 +1,102 @@
/*
* Pricetag component.
*
* Used when exporting items via the cargo system.
* Gives a cut of the profit to one or multiple bank accounts.
*/
/datum/component/pricetag
///Payee gets 100% of the value if no ratio has been set.
var/default_profit_ratio = 1
///List of bank accounts this pricetag pays out to. Format is payees[bank_account] = profit_ratio.
dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
/// Whether we qdel ourself when our parent is unwrapped or not.
var/delete_on_unwrap = TRUE
/// List of bank accounts this pricetag pays out to. Format is payees[bank_account] = profit_ratio.
var/list/payees = list()
/datum/component/pricetag/Initialize(_owner,_profit_ratio)
/datum/component/pricetag/Initialize(pay_to_account, profit_ratio = 1, delete_on_unwrap = TRUE)
if(!isobj(parent)) //Has to account for both objects and sellable structures like crates.
return COMPONENT_INCOMPATIBLE
if(_profit_ratio)
payees[_owner] = _profit_ratio
else
payees[_owner] = default_profit_ratio
if(isnull(pay_to_account))
stack_trace("[type] component was added to something without a pay_to_account!")
return COMPONENT_INCOMPATIBLE
RegisterSignal(parent, list(COMSIG_ITEM_SOLD), .proc/split_profit)
RegisterSignal(parent, list(COMSIG_STRUCTURE_UNWRAPPED, COMSIG_ITEM_UNWRAPPED), .proc/Unwrapped)
RegisterSignal(parent, list(COMSIG_ITEM_SPLIT_PROFIT, COMSIG_ITEM_SPLIT_PROFIT_DRY), .proc/return_ratio)
payees[pay_to_account] = profit_ratio
src.delete_on_unwrap = delete_on_unwrap
/datum/component/pricetag/proc/Unwrapped()
/datum/component/pricetag/RegisterWithParent()
RegisterSignal(parent, COMSIG_ITEM_EXPORTED, .proc/on_parent_sold)
// Register this regardless of delete_on_unwrap because it could change by inherited components.
RegisterSignal(parent, list(COMSIG_STRUCTURE_UNWRAPPED, COMSIG_ITEM_UNWRAPPED), .proc/on_parent_unwrap)
/datum/component/pricetag/UnregisterFromParent()
UnregisterSignal(parent, list(
COMSIG_ITEM_EXPORTED,
COMSIG_STRUCTURE_UNWRAPPED,
COMSIG_ITEM_UNWRAPPED,
))
/*
* Inheriting an incoming / new version of price tag:
*
* If the account passed in the incoming version is already in our list,
* only override it if the ratio is better for the payee
*
* If the account passed in the incoming version is not in our list, add it like normal.
*
* If the incoming version shouldn't delete when unwrapped,
* our version shouldn't either.
* We don't care about the other way around
* (Don't go from non-deleting to deleting)
*/
/datum/component/pricetag/InheritComponent(datum/component/pricetag/new_comp, i_am_original, pay_to_account, profit_ratio = 1, delete_on_unwrap = TRUE)
if(!isnull(payees[pay_to_account]) && payees[pay_to_account] >= profit_ratio) // They're already getting a better ratio, don't scam them
return
payees[pay_to_account] = profit_ratio
if(!delete_on_unwrap)
src.delete_on_unwrap = delete_on_unwrap
/*
* Signal proc for [COMSIG_STRUCTURE_UNWRAPPED] and [COMSIG_ITEM_UNWRAPPED].
*
* Once it leaves its wrapped container,
* the parent should loses its pricetag component
* (if delete_on_unwrap is TRUE)
*/
/datum/component/pricetag/proc/on_parent_unwrap(obj/source)
SIGNAL_HANDLER
qdel(src) //Once it leaves it's wrapped container, the object in question should lose it's pricetag component.
if(!delete_on_unwrap)
return
/datum/component/pricetag/proc/split_profit(item_value)
qdel(src)
/*
* Signal proc for [COMSIG_ITEM_EXPORTED].
*
* Pays out money to everyone in the payees list.
*/
/datum/component/pricetag/proc/on_parent_sold(obj/source, datum/export/export, datum/export_report/report, item_value)
SIGNAL_HANDLER
var/price = item_value
if(price)
for(var/datum/bank_account/payee in payees)
var/profit_ratio = payees[payee]
var/adjusted_value = price * profit_ratio
var/datum/bank_account/bank_account = payee
bank_account.adjust_money(adjusted_value)
bank_account.bank_card_talk("Sale of [parent] recorded. [adjusted_value] credits added to account.")
return TRUE
if(!isnum(item_value))
return
/datum/component/pricetag/proc/return_ratio()
SIGNAL_HANDLER
return default_profit_ratio
// Gotta see how much money we've lost by the end of things.
var/overall_item_price = item_value
for(var/datum/bank_account/payee as anything in payees)
// Every payee with a ratio gets a cut based on the item's total value
var/payee_cut = round(item_value * payees[payee])
// And of course, the cut is removed from what cargo gets. (But not below zero, just in case)
overall_item_price = max(0, overall_item_price - payee_cut)
payee.adjust_money(payee_cut)
payee.bank_card_talk("Sale of [source] recorded. [payee_cut] credits added to account.")
// Update the report with the modified final price
report.total_value[export] += overall_item_price
report.total_amount[export] += export.get_amount(source) * export.amount_report_multiplier
// And ensure we don't double-add to the report
return COMPONENT_STOP_EXPORT_REPORT
+19 -2
View File
@@ -275,14 +275,17 @@
/obj/item/bounty_cube/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_NO_BARCODES, INNATE_TRAIT) // Don't allow anyone to override our pricetag component with a barcode
radio = new(src)
radio.keyslot = new radio_key
radio.set_listening(FALSE)
radio.recalculateChannels()
RegisterSignal(radio, COMSIG_ITEM_PRE_EXPORT, .proc/on_export)
/obj/item/bounty_cube/Destroy()
UnregisterSignal(radio, COMSIG_ITEM_PRE_EXPORT)
QDEL_NULL(radio)
. = ..()
return ..()
/obj/item/bounty_cube/examine()
. = ..()
@@ -291,6 +294,20 @@
if(handler_tip && !bounty_handler_account)
. += span_notice("Scan this in the cargo shuttle with an export scanner to register your bank account for the <b>[bounty_value * handler_tip]</b> credit handling tip.")
/*
* Signal proc for [COMSIG_ITEM_EXPORTED], registered on the internal radio.
*
* Deletes the internal radio before being exported,
* to stop it from bring counted as an export.
*
* No 4 free credits for you!
*/
/obj/item/bounty_cube/proc/on_export(datum/source)
SIGNAL_HANDLER
QDEL_NULL(radio)
return COMPONENT_STOP_EXPORT // stops the radio from exporting, not the cube
/obj/item/bounty_cube/process(delta_time)
//if our nag cooldown has finished and we aren't on Centcom or in transit, then nag
if(COOLDOWN_FINISHED(src, next_nag_time) && !is_centcom_level(z) && !is_reserved_level(z))
@@ -320,7 +337,7 @@
bounty_holder_account = holder_id.registered_account
name = "\improper [bounty_value] cr [name]"
desc += " The sales tag indicates it was <i>[bounty_holder] ([bounty_holder_job])</i>'s reward for completing the <i>[bounty_name]</i> bounty."
AddComponent(/datum/component/pricetag, holder_id.registered_account, holder_cut)
AddComponent(/datum/component/pricetag, holder_id.registered_account, holder_cut, FALSE)
AddComponent(/datum/component/gps, "[src]")
START_PROCESSING(SSobj, src)
COOLDOWN_START(src, next_nag_time, nag_cooldown)
+4 -5
View File
@@ -29,6 +29,7 @@
var/mob/living/carbon/human/scan_human = user
if(istype(O, /obj/item/bounty_cube))
var/obj/item/bounty_cube/cube = O
var/datum/bank_account/scanner_account = scan_human.get_bank_account()
if(!istype(get_area(cube), /area/shuttle/supply))
to_chat(user, span_warning("Shuttle placement not detected. Handling tip not registered."))
@@ -36,12 +37,10 @@
else if(cube.bounty_handler_account)
to_chat(user, span_warning("Bank account for handling tip already registered!"))
else if(scan_human.get_bank_account() && cube.GetComponent(/datum/component/pricetag))
var/datum/component/pricetag/pricetag = cube.GetComponent(/datum/component/pricetag)
cube.bounty_handler_account = scan_human.get_bank_account()
pricetag.payees[cube.bounty_handler_account] += cube.handler_tip
else if(scanner_account)
cube.AddComponent(/datum/component/pricetag, scanner_account, cube.handler_tip, FALSE)
cube.bounty_handler_account = scanner_account
cube.bounty_handler_account.bank_card_talk("Bank account for [price ? "<b>[price * cube.handler_tip]</b> credit " : ""]handling tip successfully registered.")
if(cube.bounty_holder_account != cube.bounty_handler_account) //No need to send a tracking update to the person scanning it
+20 -22
View File
@@ -26,12 +26,10 @@ Then the player gets the profit from selling his own wasted time.
var/list/total_value = list() //export instance => total value of sold objects
// external_report works as "transaction" object, pass same one in if you're doing more than one export in single go
/proc/export_item_and_contents(atom/movable/AM, apply_elastic = TRUE, delete_unsold = TRUE, dry_run=FALSE, datum/export_report/external_report)
/proc/export_item_and_contents(atom/movable/AM, apply_elastic = TRUE, delete_unsold = TRUE, dry_run = FALSE, datum/export_report/external_report)
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.get_all_contents()
var/datum/export_report/report = external_report
@@ -45,12 +43,14 @@ Then the player gets the profit from selling his own wasted time.
var/sold = FALSE
for(var/datum/export/export as anything in GLOB.exports_list)
if(export.applies_to(thing, apply_elastic))
sold = export.sell_object(thing, report, dry_run, apply_elastic, profit_ratio)
if(!dry_run && (SEND_SIGNAL(thing, COMSIG_ITEM_PRE_EXPORT) & COMPONENT_STOP_EXPORT))
break
sold = export.sell_object(thing, report, dry_run, apply_elastic)
report.exported_atoms += " [thing.name]"
break
if(!dry_run && (sold || delete_unsold))
if(ismob(thing))
thing.investigate_log("deleted through cargo export",INVESTIGATE_CARGO)
thing.investigate_log("deleted through cargo export", INVESTIGATE_CARGO)
to_delete += thing
for(var/atom/movable/thing as anything in to_delete)
@@ -136,31 +136,29 @@ Then the player gets the profit from selling his own wasted time.
* get_cost, get_amount and applies_to do not neccesary mean a successful sale.
*
*/
/datum/export/proc/sell_object(obj/O, datum/export_report/report, dry_run = TRUE, apply_elastic = TRUE)
/datum/export/proc/sell_object(obj/sold_item, datum/export_report/report, dry_run = TRUE, apply_elastic = TRUE)
///This is the value of the object, as derived from export datums.
var/the_cost = get_cost(O, apply_elastic)
var/export_value = get_cost(sold_item, apply_elastic)
///Quantity of the object in question.
var/amount = get_amount(O)
///Utilized in the pricetag component. Splits the object's profit when it has a pricetag by the specified amount.
var/profit_ratio = 0
var/export_amount = get_amount(sold_item)
if(amount <=0 || (the_cost <=0 && !allow_negative_cost))
if(export_amount <= 0 || (export_value <= 0 && !allow_negative_cost))
return FALSE
if(dry_run == FALSE)
if(SEND_SIGNAL(O, COMSIG_ITEM_SOLD, item_value = get_cost(O, apply_elastic)) & COMSIG_ITEM_SPLIT_VALUE)
profit_ratio = SEND_SIGNAL(O, COMSIG_ITEM_SPLIT_PROFIT_DRY)
the_cost = the_cost * ((100 - profit_ratio) * 0.01)
else
profit_ratio = SEND_SIGNAL(O, COMSIG_ITEM_SPLIT_PROFIT)
the_cost = the_cost * ((100 - profit_ratio) * 0.01)
report.total_value[src] += the_cost
report.total_amount[src] += amount*amount_report_multiplier
// If we're not doing a dry run, send COMSIG_ITEM_EXPORTED to the sold item
var/export_result
if(!dry_run)
export_result = SEND_SIGNAL(sold_item, COMSIG_ITEM_EXPORTED, src, report, export_value)
// If the signal handled adding it to the report, don't do it now
if(!(export_result & COMPONENT_STOP_EXPORT_REPORT))
report.total_value[src] += export_value
report.total_amount[src] += export_amount * amount_report_multiplier
if(!dry_run)
if(apply_elastic)
cost *= NUM_E**(-1*k_elasticity*amount) //marginal cost modifier
SSblackbox.record_feedback("nested tally", "export_sold_cost", 1, list("[O.type]", "[the_cost]"))
cost *= NUM_E**(-1 * k_elasticity * export_amount) //marginal cost modifier
SSblackbox.record_feedback("nested tally", "export_sold_cost", 1, list("[sold_item.type]", "[export_value]"))
return TRUE
// Total printout for the cargo console.
+16 -12
View File
@@ -114,9 +114,10 @@
sticker.payments_acc = tagger.payments_acc //new tag gets the tagger's current account.
sticker.cut_multiplier = tagger.cut_multiplier //same, but for the percentage taken.
var/list/wrap_contents = src.get_all_contents()
for(var/obj/I in wrap_contents)
I.AddComponent(/datum/component/pricetag, sticker.payments_acc, tagger.cut_multiplier)
for(var/obj/wrapped_item in get_all_contents())
if(HAS_TRAIT(wrapped_item, TRAIT_NO_BARCODES))
continue
wrapped_item.AddComponent(/datum/component/pricetag, sticker.payments_acc, tagger.cut_multiplier)
var/overlaystring = "[icon_state]_tag"
if(giftwrapped)
overlaystring = copytext(overlaystring, 5)
@@ -133,9 +134,10 @@
to_chat(user, span_warning("For some reason, you can't attach [W]!"))
return
sticker = stickerA
var/list/wrap_contents = src.get_all_contents()
for(var/obj/I in wrap_contents)
I.AddComponent(/datum/component/pricetag, sticker.payments_acc, sticker.cut_multiplier)
for(var/obj/wrapped_item in get_all_contents())
if(HAS_TRAIT(wrapped_item, TRAIT_NO_BARCODES))
continue
wrapped_item.AddComponent(/datum/component/pricetag, sticker.payments_acc, sticker.cut_multiplier)
var/overlaystring = "[icon_state]_tag"
if(giftwrapped)
overlaystring = copytext_char(overlaystring, 5) //5 == length("gift") + 1
@@ -307,9 +309,10 @@
sticker.payments_acc = tagger.payments_acc //new tag gets the tagger's current account.
sticker.cut_multiplier = tagger.cut_multiplier //as above, as before.
var/list/wrap_contents = src.get_all_contents()
for(var/obj/I in wrap_contents)
I.AddComponent(/datum/component/pricetag, sticker.payments_acc, tagger.cut_multiplier)
for(var/obj/wrapped_item in get_all_contents())
if(HAS_TRAIT(wrapped_item, TRAIT_NO_BARCODES))
continue
wrapped_item.AddComponent(/datum/component/pricetag, sticker.payments_acc, tagger.cut_multiplier)
var/overlaystring = "[icon_state]_tag"
if(giftwrapped)
overlaystring = copytext(overlaystring, 5)
@@ -327,9 +330,10 @@
to_chat(user, span_warning("For some reason, you can't attach [W]!"))
return
sticker = stickerA
var/list/wrap_contents = src.get_all_contents()
for(var/obj/I in wrap_contents)
I.AddComponent(/datum/component/pricetag, sticker.payments_acc, sticker.cut_multiplier)
for(var/obj/wrapped_item in get_all_contents())
if(HAS_TRAIT(wrapped_item, TRAIT_NO_BARCODES))
continue
wrapped_item.AddComponent(/datum/component/pricetag, sticker.payments_acc, sticker.cut_multiplier)
var/overlaystring = "[icon_state]_tag"
if(giftwrapped)
overlaystring = copytext_char(overlaystring, 5) //5 == length("gift") + 1