Skip to content

Commit

Permalink
[TGUI HELL] Adds a Shopping Cart System to the syndicate uplink (#22039)
Browse files Browse the repository at this point in the history
* hoooooly shit

* slight suggestion rework

* lewc review

* sanity + locking uplink removes 0 amt cart items

* review

* shit forgot to apply this

* update bundle

* m

* bundle

* j

* l

* fix this shit

* pretty

* yea

* they lied

* teegooeye

* tgui

* farie review

* farie review
  • Loading branch information
Contrabang authored Oct 7, 2023
1 parent c4e96e4 commit 71cc3c5
Show file tree
Hide file tree
Showing 10 changed files with 427 additions and 69 deletions.
60 changes: 30 additions & 30 deletions code/datums/uplink_items/uplink_general.dm
Original file line number Diff line number Diff line change
Expand Up @@ -111,47 +111,47 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
desc = replacetext(initial(temp.desc), "\n", "<br>")
return desc

/datum/uplink_item/proc/buy(obj/item/uplink/hidden/U, mob/user)
/datum/uplink_item/proc/buy_uplink_item(obj/item/uplink/hidden/U, mob/user, put_in_hands = TRUE)
if(!istype(U))
return FALSE
return

if(user.stat || user.restrained())
return FALSE
return

if(!(ishuman(user)))
return FALSE
if(!ishuman(user))
return

// If the uplink's holder is in the user's contents
if((U.loc in user.contents || (in_range(U.loc, user) && isturf(U.loc.loc))))
if(cost > U.uses)
return FALSE

var/obj/I = spawn_item(get_turf(user), U)

if(I)
if(ishuman(user))
var/mob/living/carbon/human/A = user
if(limited_stock > 0)
log_game("[key_name(user)] purchased [name]. [name] was discounted to [cost].")
if(!user.mind.special_role)
message_admins("[key_name_admin(user)] purchased [name] (discounted to [cost]), as a non antagonist.")

else
log_game("[key_name(user)] purchased [name].")
if(!user.mind.special_role)
message_admins("[key_name_admin(user)] purchased [name], as a non antagonist.")

A.put_in_any_hand_if_possible(I)
return

if(istype(I,/obj/item/storage/box/) && I.contents.len>0)
for(var/atom/o in I)
U.purchase_log += "<BIG>[bicon(o)]</BIG>"

else
U.purchase_log += "<BIG>[bicon(I)]</BIG>"
var/obj/I = spawn_item(get_turf(user), U)

return TRUE
return FALSE
if(!I || I == UPLINK_SPECIAL_SPAWNING)
return // Failed to spawn, or we handled it with special spawning
if(limited_stock > 0)
limited_stock--
log_game("[key_name(user)] purchased [name]. [name] was discounted to [cost].")
if(!user.mind.special_role)
message_admins("[key_name_admin(user)] purchased [name] (discounted to [cost]), as a non antagonist.")

else
log_game("[key_name(user)] purchased [name].")
if(!user.mind.special_role)
message_admins("[key_name_admin(user)] purchased [name], as a non antagonist.")

if(istype(I, /obj/item/storage/box) && length(I.contents))
for(var/atom/o in I)
U.purchase_log += "<big>[bicon(o)]</big>"

else
U.purchase_log += "<big>[bicon(I)]</big>"

if(put_in_hands)
user.put_in_any_hand_if_possible(I)
return I

/*
//
Expand Down
150 changes: 145 additions & 5 deletions code/game/objects/items/devices/uplinks.dm
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,29 @@ GLOBAL_LIST_EMPTY(world_uplinks)
if(UI.limited_stock == 0)
to_chat(usr, "<span class='warning'>You have redeemed this discount already.</span>")
return
UI.buy(src,usr)
if(UI.limited_stock > 0) // only decrement it if it's actually limited
UI.limited_stock--
UI.buy_uplink_item(src,usr)
SStgui.update_uis(src)

return TRUE

/obj/item/uplink/proc/mass_purchase(datum/uplink_item/UI, reference, quantity = 1)
// jamming check happens in ui_act
if(!UI)
return
if(quantity <= 0)
return
if(UI.limited_stock == 0)
return
if(UI.limited_stock > 0 && UI.limited_stock < quantity)
quantity = UI.limited_stock
var/list/bought_things = list()
for(var/i in 1 to quantity)
var/item = UI.buy_uplink_item(src, usr, put_in_hands = FALSE)
if(isnull(item))
break
bought_things += item
return bought_things

/obj/item/uplink/proc/refund(mob/user as mob)
var/obj/item/I = user.get_active_hand()
if(I) // Make sure there's actually something in the hand before even bothering to check
Expand Down Expand Up @@ -144,6 +160,12 @@ GLOBAL_LIST_EMPTY(world_uplinks)
name = "hidden uplink"
desc = "There is something wrong if you're examining this."
var/active = FALSE
/// An assoc list of references (the variable called reference on an uplink item) and its value being how many of the item
var/list/shopping_cart
/// A cached version of shopping_cart containing all the data for the tgui side
var/list/cached_cart
/// A list of 3 categories and item indexes in uplink_cats, to show as recommendedations
var/list/lucky_numbers

// The hidden uplink MUST be inside an obj/item's contents.
/obj/item/uplink/hidden/New(loc)
Expand Down Expand Up @@ -184,6 +206,10 @@ GLOBAL_LIST_EMPTY(world_uplinks)

data["crystals"] = uses

data["cart"] = generate_tgui_cart()
data["cart_price"] = calculate_cart_tc()
data["lucky_numbers"] = lucky_numbers

return data

/obj/item/uplink/hidden/ui_static_data(mob/user)
Expand All @@ -192,6 +218,8 @@ GLOBAL_LIST_EMPTY(world_uplinks)
// Actual items
if(!uplink_cats || !uplink_items)
generate_item_lists(user)
if(!lucky_numbers) // Make sure these are generated AFTER the categories, otherwise shit will get messed up
shuffle_lucky_numbers()
data["cats"] = uplink_cats

// Exploitable info
Expand All @@ -210,26 +238,57 @@ GLOBAL_LIST_EMPTY(world_uplinks)

return data

/obj/item/uplink/hidden/proc/calculate_cart_tc()
. = 0
for(var/reference in shopping_cart)
var/datum/uplink_item/item = uplink_items[reference]
var/purchase_amt = shopping_cart[reference]
. += item.cost * purchase_amt

/obj/item/uplink/hidden/proc/generate_tgui_cart(update = FALSE)
if(!update)
return cached_cart

if(!length(shopping_cart))
shopping_cart = null
cached_cart = null
return cached_cart

cached_cart = list()
for(var/reference in shopping_cart)
var/datum/uplink_item/I = uplink_items[reference]
cached_cart += list(list(
"name" = sanitize(I.name),
"desc" = sanitize(I.description()),
"cost" = I.cost,
"hijack_only" = I.hijack_only,
"obj_path" = I.reference,
"amount" = shopping_cart[reference],
"limit" = I.limited_stock))

// Interaction code. Gathers a list of items purchasable from the paren't uplink and displays it. It also adds a lock button.
/obj/item/uplink/hidden/interact(mob/user)
ui_interact(user)

// The purchasing code.
/obj/item/uplink/hidden/ui_act(action, list/params)
/obj/item/uplink/hidden/ui_act(action, list/params, datum/tgui/ui)
if(..())
return

. = TRUE

switch(action)
if("lock")
toggle()
uses += hidden_crystals
hidden_crystals = 0
SStgui.close_uis(src)
for(var/reference in shopping_cart)
if(shopping_cart[reference] == 0) // I know this isn't lazy, but this should runtime on purpose if we can't access this for some reason
remove_from_cart(reference)

if("refund")
refund(usr)
refund(ui.user)

if("buyRandom")
var/datum/uplink_item/UI = chooseRandomItem()
Expand All @@ -239,6 +298,87 @@ GLOBAL_LIST_EMPTY(world_uplinks)
var/datum/uplink_item/UI = uplink_items[params["item"]]
return buy(UI, UI ? UI.reference : "")

if("add_to_cart")
var/datum/uplink_item/UI = uplink_items[params["item"]]
if(LAZYIN(shopping_cart, params["item"]))
to_chat(ui.user, "<span class='warning'>[UI.name] is already in your cart!</span>")
return
var/startamount = 1
if(UI.limited_stock == 0)
startamount = 0
LAZYSET(shopping_cart, params["item"], startamount)
generate_tgui_cart(TRUE)

if("remove_from_cart")
remove_from_cart(params["item"])

if("set_cart_item_quantity")
var/amount = text2num(params["quantity"])
LAZYSET(shopping_cart, params["item"], max(amount, 0))
generate_tgui_cart(TRUE)

if("purchase_cart")
if(!LAZYLEN(shopping_cart)) // sanity check
return
if(calculate_cart_tc() > uses)
to_chat(ui.user, "<span class='warning'>[src] buzzes, it doesn't contain enough telecrystals!</span>")
return
if(is_jammed)
to_chat(ui.user, "<span class='warning'>[src] seems to be jammed - it cannot be used here!</span>")
return

// Buying of the uplink stuff
var/list/bought_things = list()
for(var/reference in shopping_cart)
var/datum/uplink_item/item = uplink_items[reference]
var/purchase_amt = shopping_cart[reference]
if(purchase_amt <= 0)
continue
bought_things += mass_purchase(item, item ? item.reference : "", purchase_amt)

// Check how many of them are items
var/list/obj/item/items_for_crate = list()
for(var/obj/item/thing in bought_things)
// because sometimes you can buy items like crates from surpluses and stuff
// the crates will already be on the ground, so we dont need to worry about them
if(isitem(thing))
items_for_crate += thing

// If we have more than 2 of them, put them in a crate
if(length(items_for_crate) > 2)
var/obj/structure/closet/crate/C = new(get_turf(src))
for(var/obj/item/item as anything in items_for_crate)
item.forceMove(C)
// Otherwise, just put the items in their hands
else if(length(items_for_crate))
for(var/obj/item/item as anything in items_for_crate)
ui.user.put_in_any_hand_if_possible(item)

empty_cart()
SStgui.update_uis(src)

if("empty_cart")
empty_cart()

if("shuffle_lucky_numbers")
// lets see paul allen's random uplink item
shuffle_lucky_numbers()

/obj/item/uplink/hidden/proc/shuffle_lucky_numbers()
lucky_numbers = list()
for(var/i in 1 to 4)
var/cate_number = rand(1, length(uplink_cats))
var/item_number = rand(1, length(uplink_cats[cate_number]["items"]))
lucky_numbers += list(list("cat" = cate_number - 1, "item" = item_number - 1)) // dm lists are 1 based, js lists are 0 based, gotta -1

/obj/item/uplink/hidden/proc/remove_from_cart(item_reference) // i want to make it eventually remove all instances
LAZYREMOVE(shopping_cart, item_reference)
generate_tgui_cart(TRUE)

/obj/item/uplink/hidden/proc/empty_cart()
shopping_cart = null
generate_tgui_cart(TRUE)

// I placed this here because of how relevant it is.
// You place this in your uplinkable item to check if an uplink is active or not.
// If it is, it will display the uplink menu and return 1, else it'll return false.
Expand Down
1 change: 1 addition & 0 deletions tgui/packages/tgui/components/Box.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ const styleMapperByPropName = {
textTransform: mapRawPropTo('text-transform'),
wordWrap: mapRawPropTo('word-wrap'),
textOverflow: mapRawPropTo('text-overflow'),
borderRadius: mapRawPropTo('border-radius'),
// Boolean props
inline: mapBooleanPropTo('display', 'inline-block'),
bold: mapBooleanPropTo('font-weight', 'bold'),
Expand Down
6 changes: 6 additions & 0 deletions tgui/packages/tgui/components/Button.js
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,10 @@ export class ButtonInput extends Component {
}

setInInput(inInput) {
const { disabled } = this.props;
if (disabled) {
return;
}
this.setState({
inInput,
});
Expand Down Expand Up @@ -230,6 +234,7 @@ export class ButtonInput extends Component {
tooltip,
tooltipPosition,
color = 'default',
disabled,
placeholder,
maxLength,
multiLine,
Expand All @@ -241,6 +246,7 @@ export class ButtonInput extends Component {
className={classes([
'Button',
fluid && 'Button--fluid',
disabled && 'Button--disabled',
'Button--color--' + color,
multiLine + 'Button--multiLine',
])}
Expand Down
8 changes: 7 additions & 1 deletion tgui/packages/tgui/components/Section.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const Section = (props) => {
content,
stretchContents,
noTopPadding,
showBottom = true,
children,
...rest
} = props;
Expand All @@ -26,7 +27,12 @@ export const Section = (props) => {
{...rest}
>
{hasTitle && (
<div className="Section__title">
<div
className={classes([
'Section__title',
showBottom && 'Section__title--showBottom',
])}
>
<span className="Section__titleText">{title}</span>
<div className="Section__buttons">{buttons}</div>
</div>
Expand Down
Loading

0 comments on commit 71cc3c5

Please sign in to comment.