diff --git a/code/__DEFINES/components.dm b/code/__DEFINES/components.dm index f1a4e014a176..6bd346821ef9 100644 --- a/code/__DEFINES/components.dm +++ b/code/__DEFINES/components.dm @@ -63,4 +63,4 @@ // /obj/machinery signals #define COMSIG_MACHINE_PROCESS "machine_process" //from machinery subsystem fire(): () -#define COMSIG_MACHINE_PROCESS_ATMOS "machine_process_atmos" //from air subsystem process_atmos_machinery(): () +#define COMSIG_MACHINE_PROCESS_ATMOS "machine_process_atmos" //from air subsystem process_atmos_machinery(): () diff --git a/code/datums/components/_component.dm b/code/datums/components/_component.dm index 0c85ddd871af..48e1b98fd194 100644 --- a/code/datums/components/_component.dm +++ b/code/datums/components/_component.dm @@ -16,7 +16,7 @@ if(Initialize(arglist(arguments)) == COMPONENT_INCOMPATIBLE) qdel(src, TRUE, TRUE) return - + _CheckDupesAndJoinParent(P) /datum/component/proc/_CheckDupesAndJoinParent() @@ -45,12 +45,12 @@ if(!old) //let the others know P.SendSignal(COMSIG_COMPONENT_ADDED, src) - + //lazy init the parent's dc list var/list/dc = P.datum_components if(!dc) P.datum_components = dc = list() - + //set up the typecache var/our_type = type for(var/I in _GetInverseTypeList(our_type)) @@ -114,7 +114,7 @@ if(!procs) procs = list() signal_procs = procs - + var/list/sig_types = islist(sig_type_or_types) ? sig_type_or_types : list(sig_type_or_types) for(var/sig_type in sig_types) if(!override) @@ -177,7 +177,7 @@ for(var/I in target) var/datum/component/C = I if(!C.enabled) - continue + continue var/list/sps = C.signal_procs var/datum/callback/CB = LAZYACCESS(sps, sigtype) if(!CB) @@ -255,3 +255,6 @@ target.TakeComponent(I) else target.TakeComponent(comps) + +/datum/component/ui_host() + return parent diff --git a/code/datums/components/uplink.dm b/code/datums/components/uplink.dm new file mode 100644 index 000000000000..407965aeb98f --- /dev/null +++ b/code/datums/components/uplink.dm @@ -0,0 +1,204 @@ +GLOBAL_LIST_EMPTY(uplinks) + +/** + * Uplinks + * + * All /obj/item(s) have a hidden_uplink var. By default it's null. Give the item one with 'new(src') (it must be in it's contents). Then add 'uses.' + * Use whatever conditionals you want to check that the user has an uplink, and then call interact() on their uplink. + * You might also want the uplink menu to open if active. Check if the uplink is 'active' and then interact() with it. +**/ +/datum/component/uplink + dupe_mode = COMPONENT_DUPE_UNIQUE + var/name = "syndicate uplink" + var/active = FALSE + var/lockable = TRUE + var/locked = TRUE + var/telecrystals + var/selected_cat + var/owner = null + var/datum/game_mode/gamemode + var/spent_telecrystals = 0 + var/datum/uplink_purchase_log/purchase_log + var/list/uplink_items + var/hidden_crystals = 0 + +/datum/component/uplink/Initialize(_owner, _lockable = TRUE, _enabled = FALSE, datum/game_mode/_gamemode, starting_tc = 20) + if(!isitem(parent)) + return COMPONENT_INCOMPATIBLE + GLOB.uplinks += src + uplink_items = get_uplink_items(gamemode) + RegisterSignal(COMSIG_PARENT_ATTACKBY, .proc/OnAttackBy) + RegisterSignal(COMSIG_ITEM_ATTACK_SELF, .proc/interact) + owner = _owner + if(owner) + LAZYINITLIST(GLOB.uplink_purchase_logs_by_key) + if(GLOB.uplink_purchase_logs_by_key[owner]) + purchase_log = GLOB.uplink_purchase_logs_by_key[owner] + else + purchase_log = new(owner, src) + lockable = _lockable + active = _enabled + gamemode = _gamemode + telecrystals = starting_tc + if(!lockable) + active = TRUE + locked = FALSE + +/datum/component/uplink/InheritComponent(datum/component/uplink/U) + lockable |= U.lockable + active |= U.active + if(!gamemode) + gamemode = U.gamemode + telecrystals += U.telecrystals + if(purchase_log && U.purchase_log) + purchase_log.MergeWithAndDel(U.purchase_log) + +/datum/component/uplink/Destroy() + GLOB.uplinks -= src + gamemode = null + return ..() + +/datum/component/uplink/proc/LoadTC(mob/user, obj/item/stack/telecrystal/TC, silent = FALSE) + if(!silent) + to_chat(user, "You slot [TC] into [parent] and charge its internal uplink.") + var/amt = TC.amount + telecrystals += amt + TC.use(amt) + +/datum/component/uplink/proc/set_gamemode(_gamemode) + gamemode = _gamemode + uplink_items = get_uplink_items(gamemode) + +/datum/component/uplink/proc/OnAttackBy(obj/item/I, mob/user) + if(!active) + return //no hitting everyone/everything just to try to slot tcs in! + if(istype(I, /obj/item/stack/telecrystal)) + LoadTC(user, I) + for(var/item in subtypesof(/datum/uplink_item)) + var/datum/uplink_item/UI = item + var/path = null + if(initial(UI.refund_path)) + path = initial(UI.refund_path) + else + path = initial(UI.item) + var/cost = 0 + if(initial(UI.refund_amount)) + cost = initial(UI.refund_amount) + else + cost = initial(UI.cost) + var/refundable = initial(UI.refundable) + if(I.type == path && refundable && I.check_uplink_validity()) + telecrystals += cost + spent_telecrystals -= cost + to_chat(user, "[I] refunded.") + qdel(I) + return + +/datum/component/uplink/proc/interact(mob/user) + if(locked) + return + active = TRUE + if(user) + ui_interact(user) + +/datum/component/uplink/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ + datum/tgui/master_ui = null, datum/ui_state/state = GLOB.inventory_state) + active = TRUE + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "uplink", name, 450, 750, master_ui, state) + ui.set_autoupdate(FALSE) // This UI is only ever opened by one person, and never is updated outside of user input. + ui.set_style("syndicate") + ui.open() + +/datum/component/uplink/ui_data(mob/user) + if(!user.mind) + return + var/list/data = list() + data["telecrystals"] = telecrystals + data["lockable"] = lockable + + data["categories"] = list() + for(var/category in uplink_items) + var/list/cat = list( + "name" = category, + "items" = (category == selected_cat ? list() : null)) + if(category == selected_cat) + for(var/item in uplink_items[category]) + var/datum/uplink_item/I = uplink_items[category][item] + if(I.limited_stock == 0) + continue + if(I.restricted_roles.len) + var/is_inaccessible = 1 + for(var/R in I.restricted_roles) + if(R == user.mind.assigned_role) + is_inaccessible = 0 + if(is_inaccessible) + continue + cat["items"] += list(list( + "name" = I.name, + "cost" = I.cost, + "desc" = I.desc, + )) + data["categories"] += list(cat) + return data + +/datum/component/uplink/ui_act(action, params) + if(!active) + return + + switch(action) + if("buy") + var/item = params["item"] + + var/list/buyable_items = list() + for(var/category in uplink_items) + buyable_items += uplink_items[category] + + if(item in buyable_items) + var/datum/uplink_item/I = buyable_items[item] + MakePurchase(usr, I) + . = TRUE + if("lock") + active = FALSE + locked = TRUE + telecrystals += hidden_crystals + hidden_crystals = 0 + SStgui.close_uis(src) + if("select") + selected_cat = params["category"] + return TRUE + +/datum/component/uplink/proc/MakePurchase(mob/user, datum/uplink_item/U) + if(!istype(U)) + return + if (!user || user.incapacitated()) + return + + if(telecrystals < U.cost || U.limited_stock == 0) + return + telecrystals -= U.cost + + var/atom/A = U.spawn_item(get_turf(user), src, user) + if(U.purchase_log_vis && purchase_log) + var/obj/item/storage/box/B = A + var/list/atom/logging = list() + if(istype(B) && B.contents.len > 0) + logging |= list(B) + else + logging |= A + for(var/atom/_logging in logging) + purchase_log.LogPurchase(_logging, U.cost) + + if(U.limited_stock > 0) + U.limited_stock -= 1 + + SSblackbox.record_feedback("nested tally", "traitor_uplink_items_bought", 1, list("[initial(U.name)]", "[U.cost]")) + if(ishuman(user) && istype(A, /obj/item)) + var/mob/living/carbon/human/H = user + if(H.put_in_hands(A)) + to_chat(H, "[A] materializes into your hands!") + else + to_chat(H, "\The [A] materializes onto the floor.") + return TRUE + diff --git a/code/datums/mind.dm b/code/datums/mind.dm index be4c679d68f6..9ba84074f56c 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -295,9 +295,7 @@ to_chat(traitor_mob, "Unfortunately, [employer] wasn't able to get you an Uplink.") . = 0 else - var/obj/item/device/uplink/U = new(uplink_loc) - U.owner = "[traitor_mob.key]" - uplink_loc.hidden_uplink = U + uplink_loc.LoadComponent(/datum/component/uplink, traitor_mob.key) if(uplink_loc == R) R.traitor_frequency = sanitize_frequency(rand(MIN_FREQ, MAX_FREQ)) @@ -713,7 +711,7 @@ if(((src in SSticker.mode.traitors) || (src in SSticker.mode.syndicates)) && ishuman(current)) text = "Uplink: give" - var/obj/item/device/uplink/U = find_syndicate_uplink() + var/datum/component/uplink/U = find_syndicate_uplink() if(U) text += " | take" if (check_rights(R_FUN, 0)) @@ -1296,7 +1294,7 @@ log_admin("[key_name(usr)] removed [current]'s uplink.") if("crystals") if(check_rights(R_FUN, 0)) - var/obj/item/device/uplink/U = find_syndicate_uplink() + var/datum/component/uplink/U = find_syndicate_uplink() if(U) var/crystals = input("Amount of telecrystals for [key]","Syndicate uplink", U.telecrystals) as null | num if(!isnull(crystals)) @@ -1325,15 +1323,14 @@ /datum/mind/proc/find_syndicate_uplink() var/list/L = current.GetAllContents() - for (var/obj/item/I in L) - if (I.hidden_uplink) - return I.hidden_uplink - return null + for (var/i in L) + var/atom/movable/I = i + . = I.GetComponent(/datum/component/uplink) + if(.) + break /datum/mind/proc/take_uplink() - var/obj/item/device/uplink/H = find_syndicate_uplink() - if(H) - qdel(H) + qdel(find_syndicate_uplink()) /datum/mind/proc/make_Traitor() if(!(has_antag_datum(ANTAG_DATUM_TRAITOR))) diff --git a/code/modules/uplink/uplink_item.dm b/code/datums/uplink_items.dm similarity index 97% rename from code/modules/uplink/uplink_item.dm rename to code/datums/uplink_items.dm index 0c178d91a2f6..22c52bc91a3e 100644 --- a/code/modules/uplink/uplink_item.dm +++ b/code/datums/uplink_items.dm @@ -89,43 +89,10 @@ GLOBAL_LIST_EMPTY(uplink_items) // Global list so we only initialize this once. /datum/uplink_item/proc/get_discount() return pick(4;0.75,2;0.5,1;0.25) -/datum/uplink_item/proc/spawn_item(turf/loc, obj/item/device/uplink/U) +/datum/uplink_item/proc/spawn_item(turf/loc, datum/component/uplink/U, mob/user) if(item) - SSblackbox.record_feedback("nested tally", "traitor_uplink_items_bought", 1, list("[initial(name)]", "[cost]")) return new item(loc) -/datum/uplink_item/proc/buy(mob/user, obj/item/device/uplink/U) - if(!istype(U)) - return - if (!user || user.incapacitated()) - return - - if(U.telecrystals < cost || limited_stock == 0) - return - else - U.telecrystals -= cost - U.spent_telecrystals += cost - - var/atom/A = spawn_item(get_turf(user), U) - var/obj/item/storage/box/B = A - if(istype(B) && B.contents.len > 0) - for(var/obj/item/I in B) - U.purchase_log += "[icon2base64html(I)]" - else - if(purchase_log_vis) - U.purchase_log += "[icon2base64html(A)]" - - if(limited_stock > 0) - limited_stock -= 1 - - if(ishuman(user) && istype(A, /obj/item)) - var/mob/living/carbon/human/H = user - if(H.put_in_hands(A)) - to_chat(H, "[A] materializes into your hands!") - else - to_chat(H, "\The [A] materializes onto the floor.") - return 1 - /datum/uplink_item/Destroy() if(src in GLOB.uplink_items) GLOB.uplink_items -= src //Take us out instead of leaving a null! @@ -368,7 +335,7 @@ GLOBAL_LIST_EMPTY(uplink_items) // Global list so we only initialize this once. cost = 12 surplus = 35 include_modes = list(/datum/game_mode/nuclear) - + /datum/uplink_item/dangerous/guardian name = "Holoparasites" desc = "Though capable of near sorcerous feats via use of hardlight holograms and nanomachines, they require an \ @@ -976,8 +943,8 @@ GLOBAL_LIST_EMPTY(uplink_items) // Global list so we only initialize this once. item = /obj/item/briefcase_launchpad cost = 6 -/datum/uplink_item/device_tools/briefcase_launchpad/buy(mob/user, obj/item/device/uplink/U) - var/obj/item/device/launchpad_remote/L = new(get_turf(user)) //free remote +/datum/uplink_item/device_tools/briefcase_launchpad/spawn_item(turf/loc, datum/component/uplink/U, mob/user) + var/obj/item/device/launchpad_remote/L = new(loc) //free remote if(ishuman(user)) var/mob/living/carbon/human/H = user if(H.put_in_hands(L)) @@ -1380,7 +1347,7 @@ GLOBAL_LIST_EMPTY(uplink_items) // Global list so we only initialize this once. exclude_modes = list(/datum/game_mode/nuclear) cant_discount = TRUE -/datum/uplink_item/badass/surplus/spawn_item(turf/loc, obj/item/device/uplink/U) +/datum/uplink_item/badass/surplus/spawn_item(turf/loc, datum/component/uplink/U) var/list/uplink_items = get_uplink_items(SSticker.mode) var/crate_value = 50 @@ -1396,7 +1363,7 @@ GLOBAL_LIST_EMPTY(uplink_items) // Global list so we only initialize this once. continue crate_value -= I.cost var/obj/goods = new I.item(C) - U.purchase_log += "[icon2base64html(goods)]" + U.purchase_log.LogPurchase(goods, I.cost) SSblackbox.record_feedback("nested tally", "traitor_uplink_items_bought", 1, list("[initial(name)]", "[cost]")) return C @@ -1408,7 +1375,7 @@ GLOBAL_LIST_EMPTY(uplink_items) // Global list so we only initialize this once. cost = 0 cant_discount = TRUE -/datum/uplink_item/badass/random/spawn_item(turf/loc, obj/item/device/uplink/U) +/datum/uplink_item/badass/random/spawn_item(turf/loc, datum/component/uplink/U) var/list/uplink_items = get_uplink_items(SSticker.mode) var/list/possible_items = list() for(var/category in uplink_items) diff --git a/code/datums/uplink_purchase_log.dm b/code/datums/uplink_purchase_log.dm new file mode 100644 index 000000000000..5ec462d237a1 --- /dev/null +++ b/code/datums/uplink_purchase_log.dm @@ -0,0 +1,64 @@ +GLOBAL_LIST(uplink_purchase_logs_by_key) //assoc key = /datum/uplink_purchase_log + +/datum/uplink_purchase_log + var/owner + var/list/purchase_log //assoc path-of-item = /datum/uplink_purchase_entry + var/datum/component/uplink/parent + var/total_spent = 0 + +/datum/uplink_purchase_log/New(_owner, datum/component/uplink/_parent) + owner = _owner + parent = _parent + LAZYINITLIST(GLOB.uplink_purchase_logs_by_key) + if(owner) + if(GLOB.uplink_purchase_logs_by_key[owner]) + stack_trace("WARNING: DUPLICATE PURCHASE LOGS DETECTED. [_owner] [_parent] [_parent.type]") + MergeWithAndDel(GLOB.uplink_purchase_logs_by_key[owner]) + GLOB.uplink_purchase_logs_by_key[owner] = src + purchase_log = list() + +/datum/uplink_purchase_log/Destroy() + purchase_log = null + parent = null + return ..() + +/datum/uplink_purchase_log/proc/MergeWithAndDel(datum/uplink_purchase_log/other) + if(!istype(other)) + return + . = owner == other.owner + if(!.) + return + for(var/path in other.purchase_log) + if(!purchase_log[path]) + purchase_log[path] = other.purchase_log[path] + else + var/datum/uplink_purchase_entry/UPE = purchase_log[path] + var/datum/uplink_purchase_entry/UPE_O = other.purchase_log[path] + UPE.amount_purchased += UPE_O.amount_purchased + qdel(other) + +/datum/uplink_purchase_log/proc/TotalTelecrystalsSpent() + . = total_spent + +/datum/uplink_purchase_log/proc/generate_render(show_key = TRUE) + . = "" + for(var/path in purchase_log) + var/datum/uplink_purchase_entry/UPE = purchase_log[path] + . += "\[[UPE.icon_b64][show_key?"([owner])":""]\]" + +/datum/uplink_purchase_log/proc/LogPurchase(atom/A, cost) + var/datum/uplink_purchase_entry/UPE + if(purchase_log[A.type]) + UPE = purchase_log[A.type] + else + UPE = new + purchase_log[A.type] = UPE + UPE.path = A.type + UPE.icon_b64 = "[icon2base64html(A)]" + UPE.amount_purchased++ + total_spent += cost + +/datum/uplink_purchase_entry + var/amount_purchased = 0 + var/path + var/icon_b64 diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm index 1e5d899bfae1..94c372ef2305 100644 --- a/code/game/gamemodes/nuclear/nuclear.dm +++ b/code/game/gamemodes/nuclear/nuclear.dm @@ -264,10 +264,11 @@ var/TC_uses = 0 for(var/datum/mind/syndicate in syndicates) text += printplayer(syndicate) - for(var/obj/item/device/uplink/H in GLOB.uplinks) - if(H && H.owner && H.owner == syndicate.key) - TC_uses += H.spent_telecrystals - purchases += H.purchase_log + for(var/datum/component/uplink/H in GLOB.uplinks) + if(H.purchase_log) + purchases += H.purchase_log.generate_render() + else + stack_trace("WARNING: Uplink with no purchase_log in nuclear mode! Owner: [H.owner]") text += "
" text += "(Syndicates used [TC_uses] TC) [purchases]" if(TC_uses == 0 && station_was_nuked && !are_operatives_dead()) @@ -325,9 +326,7 @@ R.freqlock = 1 if(tc) - var/obj/item/device/radio/uplink/nuclear/U = new(H) - U.hidden_uplink.owner = "[H.key]" - U.hidden_uplink.telecrystals = tc + var/obj/item/device/radio/uplink/nuclear/U = new(H, H.key, tc) H.equip_to_slot_or_del(U, slot_in_backpack) var/obj/item/implant/weapons_auth/W = new/obj/item/implant/weapons_auth(H) diff --git a/code/game/gamemodes/nuclear/nuclear_challenge.dm b/code/game/gamemodes/nuclear/nuclear_challenge.dm index c9314ece1a0c..58128b89cee5 100644 --- a/code/game/gamemodes/nuclear/nuclear_challenge.dm +++ b/code/game/gamemodes/nuclear/nuclear_challenge.dm @@ -54,10 +54,7 @@ var/obj/item/circuitboard/computer/syndicate_shuttle/board = V board.challenge = TRUE - var/obj/item/device/radio/uplink/nuclear/U = new(get_turf(user)) - U.hidden_uplink.owner = "[user.key]" - U.hidden_uplink.telecrystals = CHALLENGE_TELECRYSTALS - U.hidden_uplink.set_gamemode(/datum/game_mode/nuclear) + new /obj/item/device/radio/uplink/nuclear(get_turf(user), user.key, CHALLENGE_TELECRYSTALS) CONFIG_SET(number/shuttle_refuel_delay, max(CONFIG_GET(number/shuttle_refuel_delay), CHALLENGE_SHUTTLE_DELAY)) SSblackbox.record_feedback("amount", "nuclear_challenge_mode", 1) @@ -66,22 +63,22 @@ /obj/item/device/nuclear_challenge/proc/check_allowed(mob/living/user) if(declaring_war) to_chat(user, "You are already in the process of declaring war! Make your mind up.") - return 0 + return FALSE if(GLOB.player_list.len < CHALLENGE_MIN_PLAYERS) to_chat(user, "The enemy crew is too small to be worth declaring war on.") - return 0 + return FALSE if(user.z != ZLEVEL_CENTCOM) to_chat(user, "You have to be at your base to use this.") - return 0 + return FALSE if(world.time-SSticker.round_start_time > CHALLENGE_TIME_LIMIT) to_chat(user, "It's too late to declare hostilities. Your benefactors are already busy with other schemes. You'll have to make do with what you have on hand.") - return 0 + return FALSE for(var/V in GLOB.syndicate_shuttle_boards) var/obj/item/circuitboard/computer/syndicate_shuttle/board = V if(board.moved) to_chat(user, "The shuttle has already been moved! You have forfeit the right to declare war.") - return 0 - return 1 + return FALSE + return TRUE #undef CHALLENGE_TELECRYSTALS #undef CHALLENGE_MIN_PLAYERS diff --git a/code/game/gamemodes/sandbox/h_sandbox.dm b/code/game/gamemodes/sandbox/h_sandbox.dm index cf2b8679419d..101a33e55152 100644 --- a/code/game/gamemodes/sandbox/h_sandbox.dm +++ b/code/game/gamemodes/sandbox/h_sandbox.dm @@ -28,7 +28,7 @@ GLOBAL_VAR_INIT(hsboxspawn, TRUE) var/global/list/spawn_forbidden = list( /obj/item/tk_grab, /obj/item/implant, // not implanter, the actual thing that is inside you /obj/item/assembly, /obj/item/device/onetankbomb, /obj/item/radio, /obj/item/device/pda/ai, - /obj/item/device/uplink, /obj/item/smallDelivery, /obj/item/projectile, + /obj/item/smallDelivery, /obj/item/projectile, /obj/item/borg/sight, /obj/item/borg/stun, /obj/item/robot_module) /datum/hSB/proc/update() diff --git a/code/game/gamemodes/traitor/traitor.dm b/code/game/gamemodes/traitor/traitor.dm index a72facc59ea8..c687c0305383 100644 --- a/code/game/gamemodes/traitor/traitor.dm +++ b/code/game/gamemodes/traitor/traitor.dm @@ -103,11 +103,11 @@ var/TC_uses = 0 var/uplink_true = FALSE var/purchases = "" - for(var/obj/item/device/uplink/H in GLOB.uplinks) + for(var/datum/component/uplink/H in GLOB.uplinks) if(H && H.owner && H.owner == traitor.key) TC_uses += H.spent_telecrystals uplink_true = TRUE - purchases += H.purchase_log + purchases += H.purchase_log.generate_render(FALSE) var/objectives = "" if(traitor.objectives.len)//If the traitor had no objectives, don't need to process this. diff --git a/code/game/machinery/computer/telecrystalconsoles.dm b/code/game/machinery/computer/telecrystalconsoles.dm index 420dc435278d..b0714217f9ec 100644 --- a/code/game/machinery/computer/telecrystalconsoles.dm +++ b/code/game/machinery/computer/telecrystalconsoles.dm @@ -33,7 +33,8 @@ GLOBAL_LIST_INIT(possible_uplinker_IDs, list("Alfa","Bravo","Charlie","Delta","E if(uplinkholder) to_chat(user, "[src] already has an uplink in it.") return - if(I.hidden_uplink) + GET_COMPONENT_FROM(hidden_uplink, /datum/component/uplink, I) + if(hidden_uplink) if(!user.transferItemToLoc(I, src)) return uplinkholder = I @@ -56,26 +57,28 @@ GLOBAL_LIST_INIT(possible_uplinker_IDs, list("Alfa","Bravo","Charlie","Delta","E /obj/machinery/computer/telecrystals/uplinker/proc/donateTC(amt, addLog = 1) if(uplinkholder && linkedboss) + GET_COMPONENT_FROM(hidden_uplink, /datum/component/uplink, uplinkholder) if(amt < 0) - linkedboss.storedcrystals += uplinkholder.hidden_uplink.telecrystals + linkedboss.storedcrystals += hidden_uplink.telecrystals if(addLog) - linkedboss.logTransfer("[src] donated [uplinkholder.hidden_uplink.telecrystals] telecrystals to [linkedboss].") - uplinkholder.hidden_uplink.telecrystals = 0 - else if(amt <= uplinkholder.hidden_uplink.telecrystals) - uplinkholder.hidden_uplink.telecrystals -= amt + linkedboss.logTransfer("[src] donated [hidden_uplink.telecrystals] telecrystals to [linkedboss].") + hidden_uplink.telecrystals = 0 + else if(amt <= hidden_uplink.telecrystals) + hidden_uplink.telecrystals -= amt linkedboss.storedcrystals += amt if(addLog) linkedboss.logTransfer("[src] donated [amt] telecrystals to [linkedboss].") /obj/machinery/computer/telecrystals/uplinker/proc/giveTC(amt, addLog = 1) if(uplinkholder && linkedboss) + GET_COMPONENT_FROM(hidden_uplink, /datum/component/uplink, uplinkholder) if(amt < 0) - uplinkholder.hidden_uplink.telecrystals += linkedboss.storedcrystals + hidden_uplink.telecrystals += linkedboss.storedcrystals if(addLog) linkedboss.logTransfer("[src] received [linkedboss.storedcrystals] telecrystals from [linkedboss].") linkedboss.storedcrystals = 0 else if(amt <= linkedboss.storedcrystals) - uplinkholder.hidden_uplink.telecrystals += amt + hidden_uplink.telecrystals += amt linkedboss.storedcrystals -= amt if(addLog) linkedboss.logTransfer("[src] received [amt] telecrystals from [linkedboss].") @@ -95,7 +98,8 @@ GLOBAL_LIST_INIT(possible_uplinker_IDs, list("Alfa","Bravo","Charlie","Delta","E dat += "No linked management consoles detected. Scan for uplink stations using the management console.

" if(uplinkholder) - dat += "[uplinkholder.hidden_uplink.telecrystals] telecrystals remain in this uplink.
" + GET_COMPONENT_FROM(hidden_uplink, /datum/component/uplink, uplinkholder) + dat += "[hidden_uplink.telecrystals] telecrystals remain in this uplink.
" if(linkedboss) dat += "Donate TC: 1 | 5 | All" dat += "
Eject Uplink" @@ -175,7 +179,8 @@ GLOBAL_LIST_INIT(possible_uplinker_IDs, list("Alfa","Bravo","Charlie","Delta","E for(var/obj/machinery/computer/telecrystals/uplinker/A in TCstations) dat += "[A.name] | " if(A.uplinkholder) - dat += "[A.uplinkholder.hidden_uplink.telecrystals] telecrystals." + GET_COMPONENT_FROM(hidden_uplink, /datum/component/uplink, A.uplinkholder) + dat += "[hidden_uplink.telecrystals] telecrystals." if(storedcrystals) dat+= "
Add TC: 1 | 5 | 10 | All" dat += "
" diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 17bb5923741e..df4bd8d4c8b7 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -57,7 +57,6 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE) var/slowdown = 0 // How much clothing is slowing you down. Negative values speeds you up var/armour_penetration = 0 //percentage of armour effectiveness to remove var/list/allowed = null //suit storage stuff. - var/obj/item/device/uplink/hidden_uplink = null var/equip_delay_self = 0 //In deciseconds, how long an item takes to equip; counts only for normal clothing slots, not pockets etc. var/equip_delay_other = 20 //In deciseconds, how long an item takes to put on another person var/strip_delay = 40 //In deciseconds, how long an item takes to remove from another person @@ -228,9 +227,6 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE) /obj/item/interact(mob/user) add_fingerprint(user) - if(hidden_uplink && hidden_uplink.active) - hidden_uplink.interact(user) - return 1 ui_interact(user) /obj/item/ui_act(action, params) diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm index 771cdf7c2ee9..1f7fd77c4aca 100644 --- a/code/game/objects/items/devices/PDA/PDA.dm +++ b/code/game/objects/items/devices/PDA/PDA.dm @@ -158,15 +158,13 @@ GLOBAL_LIST_EMPTY(PDAs) to_chat(user, "You don't have the dexterity to do this!") return + . = ..() + var/datum/asset/assets = get_asset_datum(/datum/asset/simple/pda) assets.send(user) user.set_machine(src) - if(hidden_uplink && hidden_uplink.active) - hidden_uplink.interact(user) - return - var/dat = "Personal Data Assistant" @@ -499,7 +497,9 @@ GLOBAL_LIST_EMPTY(PDAs) if("Ringtone") var/t = input(U, "Please enter new ringtone", name, ttone) as text if(in_range(src, U) && loc == U && t) + GET_COMPONENT(hidden_uplink, /datum/component/uplink) if(hidden_uplink && (trim(lowertext(t)) == trim(lowertext(lock_code)))) + hidden_uplink.locked = FALSE hidden_uplink.interact(U) to_chat(U, "The PDA softly beeps.") U << browse(null, "window=pda") @@ -825,8 +825,6 @@ GLOBAL_LIST_EMPTY(PDAs) var/obj/item/photo/P = C photo = P.img to_chat(user, "You scan \the [C].") - else if(hidden_uplink && hidden_uplink.active) - hidden_uplink.attackby(C, user, params) else return ..() diff --git a/code/game/objects/items/devices/PDA/virus_cart.dm b/code/game/objects/items/devices/PDA/virus_cart.dm index ce7f85b82584..e28f6676e80c 100644 --- a/code/game/objects/items/devices/PDA/virus_cart.dm +++ b/code/game/objects/items/devices/PDA/virus_cart.dm @@ -7,7 +7,7 @@ /obj/item/cartridge/virus/message_header() return "[charges] viral files left.
" - + /obj/item/cartridge/virus/message_special(obj/item/device/pda/target) if (!istype(loc, /obj/item/device/pda)) return "" //Sanity check, this shouldn't be possible. @@ -67,11 +67,12 @@ var/difficulty = 0 if(target.cartridge) difficulty += BitCount(target.cartridge.access&(CART_MEDICAL | CART_SECURITY | CART_ENGINE | CART_CLOWN | CART_JANITOR | CART_MANIFEST)) - if(target.cartridge.access & CART_MANIFEST) + if(target.cartridge.access & CART_MANIFEST) difficulty++ //if cartridge has manifest access it has extra snowflake difficulty else difficulty += 2 - if(!target.detonatable || prob(difficulty * 15) || (target.hidden_uplink)) + GET_COMPONENT_FROM(hidden_uplink, /datum/component/uplink, target) + if(!target.detonatable || prob(difficulty * 15) || (hidden_uplink)) U.show_message("An error flashes on your [src].", 1) else U.show_message("Success!", 1) @@ -92,14 +93,14 @@ charges-- var/lock_code = "[rand(100,999)] [pick(GLOB.phonetic_alphabet)]" to_chat(U, "Virus Sent! The unlock code to the target is: [lock_code]") - if(!target.hidden_uplink) - var/obj/item/device/uplink/uplink = new(target) - target.hidden_uplink = uplink + GET_COMPONENT_FROM(hidden_uplink, /datum/component/uplink, target) + if(!hidden_uplink) + hidden_uplink = target.LoadComponent(/datum/component/uplink) target.lock_code = lock_code else - target.hidden_uplink.hidden_crystals += target.hidden_uplink.telecrystals //Temporarially hide the PDA's crystals, so you can't steal telecrystals. - target.hidden_uplink.telecrystals = telecrystals + hidden_uplink.hidden_crystals += hidden_uplink.telecrystals //Temporarially hide the PDA's crystals, so you can't steal telecrystals. + hidden_uplink.telecrystals = telecrystals telecrystals = 0 - target.hidden_uplink.active = TRUE + hidden_uplink.active = TRUE else to_chat(U, "PDA not found.") diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index 6603bc29ef06..236f3df93ecc 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -155,7 +155,9 @@ if(.) frequency = sanitize_frequency(tune, freerange) set_frequency(frequency) - if(frequency == traitor_frequency && hidden_uplink) + GET_COMPONENT(hidden_uplink, /datum/component/uplink) + if(hidden_uplink && (frequency == traitor_frequency)) + hidden_uplink.locked = FALSE hidden_uplink.interact(usr) ui.close() if("listen") diff --git a/code/game/objects/items/devices/uplink_devices.dm b/code/game/objects/items/devices/uplink_devices.dm new file mode 100644 index 000000000000..3f3970fe512f --- /dev/null +++ b/code/game/objects/items/devices/uplink_devices.dm @@ -0,0 +1,31 @@ + +// A collection of pre-set uplinks, for admin spawns. +/obj/item/device/radio/uplink/Initialize(mapload, _owner, _tc_amount = 20) + . = ..() + icon_state = "radio" + lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' + righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' + LoadComponent(/datum/component/uplink, _owner, FALSE, TRUE, null, _tc_amount) + +/obj/item/device/radio/uplink/nuclear/Initialize() + . = ..() + GET_COMPONENT(hidden_uplink, /datum/component/uplink) + hidden_uplink.set_gamemode(/datum/game_mode/nuclear) + +/obj/item/device/multitool/uplink/Initialize(mapload, _owner, _tc_amount = 20) + . = ..() + LoadComponent(/datum/component/uplink, _owner, FALSE, TRUE, null, _tc_amount) + +/obj/item/pen/uplink/Initialize(mapload, _owner, _tc_amount = 20) + . = ..() + LoadComponent(/datum/component/uplink) + traitor_unlock_degrees = 360 + +/obj/item/device/radio/uplink/old + name = "dusty radio" + desc = "A dusty looking radio." + +/obj/item/device/radio/uplink/old/Initialize(mapload, _owner, _tc_amount = 10) + . = ..() + GET_COMPONENT(hidden_uplink, /datum/component/uplink) + hidden_uplink.name = "dusty radio" diff --git a/code/game/objects/items/implants/implantuplink.dm b/code/game/objects/items/implants/implantuplink.dm index 8a3729f2e4e4..4ef25c28a942 100644 --- a/code/game/objects/items/implants/implantuplink.dm +++ b/code/game/objects/items/implants/implantuplink.dm @@ -7,25 +7,32 @@ righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' var/starting_tc = 0 -/obj/item/implant/uplink/New() - hidden_uplink = new(src) - hidden_uplink.telecrystals = starting_tc - ..() +/obj/item/implant/uplink/Initialize(mapload, _owner) + . = ..() + LoadComponent(/datum/component/uplink, _owner, TRUE, FALSE, null, starting_tc) /obj/item/implant/uplink/implant(mob/living/target, mob/user, silent = 0) - for(var/X in target.implants) - if(istype(X, type)) - var/obj/item/implant/imp_e = X - imp_e.hidden_uplink.telecrystals += hidden_uplink.telecrystals - qdel(src) - return 1 + GET_COMPONENT(hidden_uplink, /datum/component/uplink) + if(hidden_uplink) + for(var/X in target.implants) + if(istype(X, type)) + var/obj/item/implant/imp_e = X + GET_COMPONENT_FROM(their_hidden_uplink, /datum/component/uplink, imp_e) + if(their_hidden_uplink) + their_hidden_uplink.telecrystals += hidden_uplink.telecrystals + qdel(src) + return TRUE + else + qdel(imp_e) //INFERIOR AND EMPTY! if(..()) - hidden_uplink.owner = "[user.key]" - return 1 - return 0 + if(hidden_uplink) + hidden_uplink.owner = "[user.key]" + return TRUE + return FALSE /obj/item/implant/uplink/activate() + GET_COMPONENT(hidden_uplink, /datum/component/uplink) if(hidden_uplink) hidden_uplink.interact(usr) diff --git a/code/game/objects/items/stacks/telecrystal.dm b/code/game/objects/items/stacks/telecrystal.dm index ce54ed66a659..3597a3f4d663 100644 --- a/code/game/objects/items/stacks/telecrystal.dm +++ b/code/game/objects/items/stacks/telecrystal.dm @@ -12,18 +12,16 @@ if(target == user) //You can't go around smacking people with crystals to find out if they have an uplink or not. for(var/obj/item/implant/uplink/I in target) if(I && I.imp_in) - I.hidden_uplink.telecrystals += amount - use(amount) - to_chat(user, "You press [src] onto yourself and charge your hidden uplink.") + GET_COMPONENT_FROM(hidden_uplink, /datum/component/uplink, I) + if(hidden_uplink) + hidden_uplink.telecrystals += amount + use(amount) + to_chat(user, "You press [src] onto yourself and charge your hidden uplink.") + else + return ..() /obj/item/stack/telecrystal/afterattack(obj/item/I, mob/user, proximity) - if(!proximity) - return - if(isitem(I) && I.hidden_uplink && I.hidden_uplink.active) //No metagaming by using this on every PDA around just to see if it gets used up. - I.hidden_uplink.telecrystals += amount - use(amount) - to_chat(user, "You slot [src] into [I] and charge its internal uplink.") - else if(istype(I, /obj/item/cartridge/virus/frame)) + if(istype(I, /obj/item/cartridge/virus/frame)) var/obj/item/cartridge/virus/frame/cart = I if(!cart.charges) to_chat(user, "[cart] is out of charges, it's refusing to accept [src].") @@ -31,6 +29,8 @@ cart.telecrystals += amount use(amount) to_chat(user, "You slot [src] into [cart]. The next time it's used, it will also give telecrystals.") + else + return ..() /obj/item/stack/telecrystal/five amount = 5 diff --git a/code/modules/awaymissions/mission_code/snowdin.dm b/code/modules/awaymissions/mission_code/snowdin.dm index c8658c677f8d..c015d778feb7 100644 --- a/code/modules/awaymissions/mission_code/snowdin.dm +++ b/code/modules/awaymissions/mission_code/snowdin.dm @@ -218,15 +218,6 @@ desc = "This wand uses healing magics to heal and revive. The years of the cold have weakened the magic inside the wand." max_charges = 5 -/obj/item/device/radio/uplink/old - name = "dusty radio" - desc = "A dusty looking radio." - -/obj/item/device/radio/uplink/old/Initialize() - . = ..() - hidden_uplink.name = "dusty radio" - hidden_uplink.telecrystals = 10 - /obj/effect/mob_spawn/human/syndicatesoldier/coldres name = "Syndicate Snow Operative" outfit = /datum/outfit/snowsyndie/corpse diff --git a/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm b/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm index a19a6abeb351..3aa1153133bb 100644 --- a/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm +++ b/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm @@ -34,7 +34,8 @@ /mob/living/simple_animal/drone/syndrone/Initialize() . = ..() - internal_storage.hidden_uplink.telecrystals = 10 + GET_COMPONENT_FROM(hidden_uplink, /datum/component/uplink, internal_storage) + hidden_uplink.telecrystals = 10 /mob/living/simple_animal/drone/syndrone/Login() ..() @@ -47,7 +48,8 @@ /mob/living/simple_animal/drone/syndrone/badass/Initialize() . = ..() - internal_storage.hidden_uplink.telecrystals = 30 + GET_COMPONENT_FROM(hidden_uplink, /datum/component/uplink, internal_storage) + hidden_uplink.telecrystals = 30 var/obj/item/implant/weapons_auth/W = new/obj/item/implant/weapons_auth(src) W.implant(src) diff --git a/code/modules/paperwork/pen.dm b/code/modules/paperwork/pen.dm index ad2a780a712a..6abc641dffd5 100644 --- a/code/modules/paperwork/pen.dm +++ b/code/modules/paperwork/pen.dm @@ -99,19 +99,13 @@ if(deg && (deg > 0 && deg <= 360)) degrees = deg to_chat(user, "You rotate the top of the pen to [degrees] degrees.") + GET_COMPONENT(hidden_uplink, /datum/component/uplink) if(hidden_uplink && degrees == traitor_unlock_degrees) to_chat(user, "Your pen makes a clicking noise, before quickly rotating back to 0 degrees!") degrees = 0 + hidden_uplink.locked = FALSE hidden_uplink.interact(user) - -/obj/item/pen/attackby(obj/item/I, mob/user, params) - if(hidden_uplink) - return hidden_uplink.attackby(I, user, params) - else - return ..() - - /obj/item/pen/attack(mob/living/M, mob/user,stealth) if(!istype(M)) return diff --git a/code/modules/reagents/chemistry/holder.dm b/code/modules/reagents/chemistry/holder.dm index d16d2715a196..b962bf17c4ee 100644 --- a/code/modules/reagents/chemistry/holder.dm +++ b/code/modules/reagents/chemistry/holder.dm @@ -733,7 +733,7 @@ out += "[taste_desc]" return english_list(out, "something indescribable") - + /datum/reagents/proc/expose_temperature(var/temperature, var/coeff=0.02) var/temp_delta = (temperature - chem_temp) * coeff if(temp_delta > 0) diff --git a/code/modules/uplink/uplink.dm b/code/modules/uplink/uplink.dm deleted file mode 100644 index 6b53ed59206c..000000000000 --- a/code/modules/uplink/uplink.dm +++ /dev/null @@ -1,162 +0,0 @@ -GLOBAL_LIST_EMPTY(uplinks) - -/** - * Uplinks - * - * All /obj/item(s) have a hidden_uplink var. By default it's null. Give the item one with 'new(src') (it must be in it's contents). Then add 'uses.' - * Use whatever conditionals you want to check that the user has an uplink, and then call interact() on their uplink. - * You might also want the uplink menu to open if active. Check if the uplink is 'active' and then interact() with it. -**/ -/obj/item/device/uplink - name = "syndicate uplink" - desc = "There is something wrong if you're examining this." - var/active = FALSE - var/lockable = TRUE - var/telecrystals = 20 - var/selected_cat = null - var/owner = null - var/datum/game_mode/gamemode = null - var/spent_telecrystals = 0 - var/purchase_log = "" - var/list/uplink_items - var/hidden_crystals = 0 - -/obj/item/device/uplink/Initialize() - . = ..() - GLOB.uplinks += src - uplink_items = get_uplink_items(gamemode) - -/obj/item/device/uplink/proc/set_gamemode(gamemode) - src.gamemode = gamemode - uplink_items = get_uplink_items(gamemode) - -/obj/item/device/uplink/Destroy() - GLOB.uplinks -= src - return ..() - -/obj/item/device/uplink/attackby(obj/item/I, mob/user, params) - for(var/item in subtypesof(/datum/uplink_item)) - var/datum/uplink_item/UI = item - var/path = null - if(initial(UI.refund_path)) - path = initial(UI.refund_path) - else - path = initial(UI.item) - var/cost = 0 - if(initial(UI.refund_amount)) - cost = initial(UI.refund_amount) - else - cost = initial(UI.cost) - var/refundable = initial(UI.refundable) - if(I.type == path && refundable && I.check_uplink_validity()) - telecrystals += cost - spent_telecrystals -= cost - to_chat(user, "[I] refunded.") - qdel(I) - return - ..() - -/obj/item/device/uplink/interact(mob/user) - active = TRUE - if(user) - ui_interact(user) - -/obj/item/device/uplink/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ - datum/tgui/master_ui = null, datum/ui_state/state = GLOB.inventory_state) - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "uplink", name, 450, 750, master_ui, state) - ui.set_autoupdate(FALSE) // This UI is only ever opened by one person, and never is updated outside of user input. - ui.set_style("syndicate") - ui.open() - -/obj/item/device/uplink/ui_data(mob/user) - if(!user.mind) - return - var/list/data = list() - data["telecrystals"] = telecrystals - data["lockable"] = lockable - - data["categories"] = list() - for(var/category in uplink_items) - var/list/cat = list( - "name" = category, - "items" = (category == selected_cat ? list() : null)) - if(category == selected_cat) - for(var/item in uplink_items[category]) - var/datum/uplink_item/I = uplink_items[category][item] - if(I.limited_stock == 0) - continue - if(I.restricted_roles.len) - var/is_inaccessible = 1 - for(var/R in I.restricted_roles) - if(R == user.mind.assigned_role) - is_inaccessible = 0 - if(is_inaccessible) - continue - cat["items"] += list(list( - "name" = I.name, - "cost" = I.cost, - "desc" = I.desc, - )) - data["categories"] += list(cat) - return data - - -/obj/item/device/uplink/ui_act(action, params) - if(!active) - return - - switch(action) - if("buy") - var/item = params["item"] - - var/list/buyable_items = list() - for(var/category in uplink_items) - buyable_items += uplink_items[category] - - if(item in buyable_items) - var/datum/uplink_item/I = buyable_items[item] - I.buy(usr, src) - . = TRUE - if("lock") - active = FALSE - telecrystals += hidden_crystals - hidden_crystals = 0 - SStgui.close_uis(src) - if("select") - selected_cat = params["category"] - return 1 - - -/obj/item/device/uplink/ui_host() - return loc - -// Refund certain items by hitting the uplink with it. -/obj/item/device/radio/uplink/attackby(obj/item/I, mob/user, params) - return hidden_uplink.attackby(I, user, params) - -// A collection of pre-set uplinks, for admin spawns. -/obj/item/device/radio/uplink/Initialize() - . = ..() - icon_state = "radio" - lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' - righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' - hidden_uplink = new(src) - hidden_uplink.active = TRUE - hidden_uplink.lockable = FALSE - -/obj/item/device/radio/uplink/nuclear/Initialize() - . = ..() - hidden_uplink.set_gamemode(/datum/game_mode/nuclear) - -/obj/item/device/multitool/uplink/Initialize() - . = ..() - hidden_uplink = new(src) - hidden_uplink.active = TRUE - hidden_uplink.lockable = FALSE - -/obj/item/pen/uplink/Initialize() - . = ..() - hidden_uplink = new(src) - traitor_unlock_degrees = 360 diff --git a/tgstation.dme b/tgstation.dme index 349b2e44ab5b..08ce44563319 100755 --- a/tgstation.dme +++ b/tgstation.dme @@ -269,6 +269,8 @@ #include "code\datums\shuttles.dm" #include "code\datums\soullink.dm" #include "code\datums\spawners_menu.dm" +#include "code\datums\uplink_items.dm" +#include "code\datums\uplink_purchase_log.dm" #include "code\datums\verbs.dm" #include "code\datums\weakrefs.dm" #include "code\datums\world_topic.dm" @@ -301,6 +303,7 @@ #include "code\datums\components\spooky.dm" #include "code\datums\components\squeek.dm" #include "code\datums\components\thermite.dm" +#include "code\datums\components\uplink.dm" #include "code\datums\diseases\_disease.dm" #include "code\datums\diseases\_MobProcs.dm" #include "code\datums\diseases\anxiety.dm" @@ -855,6 +858,7 @@ #include "code\game\objects\items\devices\taperecorder.dm" #include "code\game\objects\items\devices\traitordevices.dm" #include "code\game\objects\items\devices\transfer_valve.dm" +#include "code\game\objects\items\devices\uplink_devices.dm" #include "code\game\objects\items\devices\PDA\cart.dm" #include "code\game\objects\items\devices\PDA\PDA.dm" #include "code\game\objects\items\devices\PDA\PDA_types.dm" @@ -2299,8 +2303,6 @@ #include "code\modules\tgui\states\self.dm" #include "code\modules\tgui\states\zlevel.dm" #include "code\modules\tooltip\tooltip.dm" -#include "code\modules\uplink\uplink.dm" -#include "code\modules\uplink\uplink_item.dm" #include "code\modules\vehicles\atv.dm" #include "code\modules\vehicles\bicycle.dm" #include "code\modules\vehicles\pimpin_ride.dm"