From fe1e07149938118db1c2e232d06165257eec835a Mon Sep 17 00:00:00 2001 From: _0Steven <42909981+00-Steven@users.noreply.github.com> Date: Thu, 30 Jan 2025 13:17:08 +0100 Subject: [PATCH] Bitrunning Gimmick Loadout Disks (feat. minor disk load refactor) (#88716) ## About The Pull Request Where I forget about a pr for 5 months. This started because there was a pr that made it so skills transfer between the real and the digital, and so I thought it'd be really funny if I could train boxing in virtual reality... but there's no easy way to get boxing gloves or anything like it in the virtual world... So! I decided to make a disk for it, but then thought about fishing and gaming and- Anyhow it all went downhill from there and here we are now: Gimmick Disks. (And a refactor of disk loading) This implements a new type of bitrunning disk that instead of a single item or ability, grants a full set of thematic items/abilities! Unhelpfully cosplay as a wizard! Game inside your game! Down a digital protein shake and box some simplemobs! As the name "Gimmick" implies, these are primarily intended to help shake up the sometimes stale bitrunner gameplay. By letting you invoke, if you so desire, what to me is the most enjoyable gaming experience: doing stupid shit with your buddies. To facilitate the new type of disk I had to refactor disk loading, as it was hardcoded to the item types. Instead, we make disk loading send a signal to the bitrunner, and register for this when held in their inventory. This allows us to do things like making the lead acid battery give you shock touch when held, without needing to make an explicit typecheck or iterate over every item in the bitrunner's nested contents to see if they have a loadable item. ## Why It's Good For The Game I think it'd be really funny if you could train your boxing in the digital realm. As said above, I feel the bitrunner gameplay can get stale sometimes, and this is how I hope to help people shake it up for themselves sometimes. By giving them more stupid shit to do. Doing stupid extended bits with other people is one of the things I enjoy most out of ss13, and this is there to let the bitrunners do exactly that with each other. And sometimes you just have to roleplay as Gamers:tm: entering virtual reality to fight the virtual syndicate in bad cosplay while roleplaying as a wizard smoking his magic weed, an overly edgy rogue, and the healer desperately trying to keep them from exploding into a million pieces. ## Changelog :cl: refactor: Bitrunning item/ability loading has been refactored. Please report any issues. add: Added Bitrunning gimmick loadout disks. These disks contain full sets of equipment for all your digital cosplay needs, each including questionably helpful equipment. Currently includes Sports (Boxer, Skater, Archer, Fisher, Gamer) and Dungeon Crawling (Alchemist, Rogue, Healer, Wizard). add: Taking a lead acid battery into the netpod with you now gives your bit avatar shock touch. /:cl: --- code/__DEFINES/bitrunning.dm | 19 ++ .../dcs/signals/signals_bitrunning.dm | 3 + code/game/objects/items/maintenance_loot.dm | 16 ++ .../bitrunning/components/avatar_gear.dm | 67 ++++++ code/modules/bitrunning/objects/disks.dm | 34 +++ .../objects/gimmick_disks/_gimmick_disk.dm | 115 +++++++++ .../objects/gimmick_disks/dungeon_disk.dm | 226 ++++++++++++++++++ .../objects/gimmick_disks/sports_disk.dm | 152 ++++++++++++ code/modules/bitrunning/orders/tech.dm | 10 + .../bitrunning/server/obj_generation.dm | 47 +--- .../virtual_domain/virtual_domain.dm | 6 +- tgstation.dme | 4 + 12 files changed, 658 insertions(+), 41 deletions(-) create mode 100644 code/modules/bitrunning/components/avatar_gear.dm create mode 100644 code/modules/bitrunning/objects/gimmick_disks/_gimmick_disk.dm create mode 100644 code/modules/bitrunning/objects/gimmick_disks/dungeon_disk.dm create mode 100644 code/modules/bitrunning/objects/gimmick_disks/sports_disk.dm diff --git a/code/__DEFINES/bitrunning.dm b/code/__DEFINES/bitrunning.dm index ba6c3b9b61c..639ae26c3fc 100644 --- a/code/__DEFINES/bitrunning.dm +++ b/code/__DEFINES/bitrunning.dm @@ -26,3 +26,22 @@ /// Camera network bitrunner bodycams are on #define BITRUNNER_CAMERA_NET "bitrunner" + +/** + * Bitrunner Domain External Load Restriction Bitflags + */ +/// Domain forbids external sources from loading items onto avatars +#define DOMAIN_FORBIDS_ITEMS (1<<0) +/// Domain forbids external sources from loading abilities onto avatars +#define DOMAIN_FORBIDS_ABILITIES (1<<1) + +/// Combination flag for blocking anything from being loaded onto avatars by external sources +#define DOMAIN_FORBIDS_ALL ALL + +/** + * COMSIG_BITRUNNER_STOCKING_GEAR Return Bitflags + */ +/// Something failed to load +#define BITRUNNER_GEAR_LOAD_FAILED (1<<0) +/// The domain restrictions blocked something from loading +#define BITRUNNER_GEAR_LOAD_BLOCKED (1<<1) diff --git a/code/__DEFINES/dcs/signals/signals_bitrunning.dm b/code/__DEFINES/dcs/signals/signals_bitrunning.dm index ac3095d6f5a..fcce7ea68d0 100644 --- a/code/__DEFINES/dcs/signals/signals_bitrunning.dm +++ b/code/__DEFINES/dcs/signals/signals_bitrunning.dm @@ -41,6 +41,9 @@ /// from /obj/machienry/quantum_server/station_spawn() #define COMSIG_BITRUNNER_STATION_SPAWN "bitrunner_station_spawn" +/// from /obj/machinery/quantum_server/stock_gear(): (mob/living/carbon/human/avatar, external_load_flags) +#define COMSIG_BITRUNNER_STOCKING_GEAR "bitrunner_stocking_gear" + // Ladder /// from /obj/structure/hololadder/disconnect() diff --git a/code/game/objects/items/maintenance_loot.dm b/code/game/objects/items/maintenance_loot.dm index 5389f7723b9..a8d088a0098 100644 --- a/code/game/objects/items/maintenance_loot.dm +++ b/code/game/objects/items/maintenance_loot.dm @@ -49,3 +49,19 @@ var/initial_percent = rand(40, 60) / 100 // 250kJ to 350kJ charge = initial_percent * maxcharge ADD_TRAIT(src, TRAIT_FISHING_BAIT, INNATE_TRAIT) + AddComponent(/datum/component/loads_avatar_gear, \ + load_callback = CALLBACK(src, PROC_REF(shockingly_improve_avatar)), \ + ) + +// Give our owner shock touch when entering the digital realm +/obj/item/stock_parts/power_store/cell/lead/proc/shockingly_improve_avatar(mob/living/carbon/human/neo, mob/living/carbon/human/avatar, external_load_flags) + if(external_load_flags & DOMAIN_FORBIDS_ABILITIES) + return BITRUNNER_GEAR_LOAD_BLOCKED + + if(!avatar.can_mutate()) + return BITRUNNER_GEAR_LOAD_FAILED + + if(avatar.dna.mutation_in_sequence(/datum/mutation/human/shock)) + avatar.dna.activate_mutation(/datum/mutation/human/shock) + else + avatar.dna.add_mutation(/datum/mutation/human/shock, MUT_EXTRA) diff --git a/code/modules/bitrunning/components/avatar_gear.dm b/code/modules/bitrunning/components/avatar_gear.dm new file mode 100644 index 00000000000..be7ede816fe --- /dev/null +++ b/code/modules/bitrunning/components/avatar_gear.dm @@ -0,0 +1,67 @@ + +/// Tracks the highest up human carrying this item, and loads bitrunning gear onto their avatar when they enter a virtual domain +/datum/component/loads_avatar_gear + /// The callback called when COMSIG_BITRUNNER_STOCKING_GEAR is sent to our host human + var/datum/callback/load_callback + /// Weakref to the human we are currently carried by + var/datum/weakref/tracked_human_ref + +/datum/component/loads_avatar_gear/Initialize(datum/callback/load_callback) + if(!isitem(parent)) + return COMPONENT_INCOMPATIBLE + + src.load_callback = load_callback + +/datum/component/loads_avatar_gear/RegisterWithParent() + RegisterSignal(parent, COMSIG_ATOM_ENTERING, PROC_REF(on_entered_loc)) + + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERING = PROC_REF(on_entered_loc), + ) + AddComponent(/datum/component/connect_containers, parent, loc_connections) + +/datum/component/loads_avatar_gear/UnregisterFromParent() + UnregisterSignal(parent, list( + COMSIG_ATOM_ENTERING, + )) + + qdel(GetComponent(/datum/component/connect_containers)) + +/datum/component/loads_avatar_gear/Destroy(force) + load_callback = null + tracked_human_ref = null + return ..() + + +/datum/component/loads_avatar_gear/proc/on_entered_loc(datum/source, atom/destination, atom/old_loc, list/atom/old_locs) + SIGNAL_HANDLER + + // No need to do checks if we're just moving turfs + if(isturf(destination) && isturf(old_loc)) + return + + // Iterate over list in reverse so we get the topmost human first + // Because it's funnier if you get to use the avatar gear of the people you're carrying + var/list/nested_locs = get_nested_locs(parent) + for(var/i in length(nested_locs) to 1 step -1) + var/atom/container = nested_locs[i] + if(ishuman(container)) + switch_tracking(container) + return + + // No humans found, stop tracking + switch_tracking(null) + +/datum/component/loads_avatar_gear/proc/switch_tracking(mob/living/carbon/human/to_track) + var/mob/living/carbon/human/tracked_human = tracked_human_ref?.resolve() + if(tracked_human == to_track) + return + + if(tracked_human) + UnregisterSignal(tracked_human, COMSIG_BITRUNNER_STOCKING_GEAR) + tracked_human_ref = WEAKREF(to_track) + RegisterSignal(to_track, COMSIG_BITRUNNER_STOCKING_GEAR, PROC_REF(load_onto_avatar)) + +/datum/component/loads_avatar_gear/proc/load_onto_avatar(mob/living/carbon/human/neo, mob/living/carbon/human/avatar, external_load_flags) + SIGNAL_HANDLER + return load_callback?.Invoke(neo, avatar, external_load_flags) diff --git a/code/modules/bitrunning/objects/disks.dm b/code/modules/bitrunning/objects/disks.dm index dd81a0f463e..b51a1dd076c 100644 --- a/code/modules/bitrunning/objects/disks.dm +++ b/code/modules/bitrunning/objects/disks.dm @@ -19,6 +19,10 @@ icon_state = "[base_icon_state][rand(0, 7)]" update_icon() + AddComponent(/datum/component/loads_avatar_gear, \ + load_callback = CALLBACK(src, PROC_REF(load_onto_avatar)), \ + ) + /obj/item/bitrunning_disk/examine(mob/user) . = ..() . += span_infoplain("This disk must be carried on your person into a netpod to be used.") @@ -30,6 +34,11 @@ . += span_info("It has been used to select: [choice_made].") . += span_notice("It cannot make another selection.") +/// Handles loading our stuff onto avatars +/obj/item/bitrunning_disk/proc/load_onto_avatar(mob/living/carbon/human/neo, mob/living/carbon/human/avatar, external_load_flags) + return NONE + + /obj/item/bitrunning_disk/ability desc = "A disk containing source code. It can be used to preload abilities into the virtual domain. Duplicate abilities will be ignored." /// The selected ability that this grants @@ -37,6 +46,20 @@ /// The list of actions that this can grant var/list/datum/action/selectable_actions = list() +/obj/item/bitrunning_disk/ability/load_onto_avatar(mob/living/carbon/human/neo, mob/living/carbon/human/avatar, external_load_flags) + if(external_load_flags & DOMAIN_FORBIDS_ABILITIES) + return BITRUNNER_GEAR_LOAD_BLOCKED + + if(isnull(granted_action)) + return BITRUNNER_GEAR_LOAD_FAILED + + if(locate(granted_action) in avatar.actions) + return BITRUNNER_GEAR_LOAD_FAILED + + var/datum/action/our_action = new granted_action() + our_action.Grant(avatar) + return NONE + /obj/item/bitrunning_disk/ability/attack_self(mob/user, modifiers) . = ..() @@ -87,6 +110,7 @@ /datum/action/cooldown/spell/shapeshift/polar_bear, ) + /obj/item/bitrunning_disk/item desc = "A disk containing source code. It can be used to preload items into the virtual domain." /// The selected item that this grants @@ -94,6 +118,16 @@ /// The list of actions that this can grant var/list/obj/selectable_items = list() +/obj/item/bitrunning_disk/item/load_onto_avatar(mob/living/carbon/human/neo, mob/living/carbon/human/avatar, external_load_flags) + if(external_load_flags & DOMAIN_FORBIDS_ITEMS) + return BITRUNNER_GEAR_LOAD_BLOCKED + + if(isnull(granted_item)) + return BITRUNNER_GEAR_LOAD_FAILED + + avatar.put_in_hands(new granted_item()) + return NONE + /obj/item/bitrunning_disk/item/attack_self(mob/user, modifiers) . = ..() diff --git a/code/modules/bitrunning/objects/gimmick_disks/_gimmick_disk.dm b/code/modules/bitrunning/objects/gimmick_disks/_gimmick_disk.dm new file mode 100644 index 00000000000..2580d8620c7 --- /dev/null +++ b/code/modules/bitrunning/objects/gimmick_disks/_gimmick_disk.dm @@ -0,0 +1,115 @@ + +/** + * Bitrunning tech disks which let you load full loadouts into the vdom on first avatar generation. + */ +/obj/item/bitrunning_disk/gimmick + desc = "A disk containing source code. It can be used to preload gimmick loadouts into the virtual domain." + /// The selected loadout that this grants + var/datum/bitrunning_gimmick/granted_loadout + /// The list of loadouts that this can grant + var/list/datum/bitrunning_gimmick/selectable_loadouts + +/obj/item/bitrunning_disk/gimmick/Destroy() + QDEL_NULL(granted_loadout) + return ..() + +/obj/item/bitrunning_disk/gimmick/load_onto_avatar(mob/living/carbon/human/neo, mob/living/carbon/human/avatar, external_load_flags) + if(isnull(granted_loadout)) + return BITRUNNER_GEAR_LOAD_FAILED + return granted_loadout.grant_loadout(neo, avatar, external_load_flags) + +/obj/item/bitrunning_disk/gimmick/attack_self(mob/user, modifiers) + . = ..() + + if(granted_loadout) + return + + var/names = list() + for(var/datum/bitrunning_gimmick/loadout as anything in selectable_loadouts) + names += initial(loadout.name) + + var/choice = tgui_input_list(user, message = "Select a gimmick loadout", title = "Bitrunning Program", items = names) + if(isnull(choice) || !user.is_holding(src)) + return + + for(var/datum/bitrunning_gimmick/loadout as anything in selectable_loadouts) + if(initial(loadout.name) == choice) + granted_loadout = new loadout() + + balloon_alert(user, "selected") + playsound(user, 'sound/items/click.ogg', 50, TRUE) + choice_made = choice + + +/** + * The datum used by the gimmick loadout disk to determine what a loadout actually spawns. + */ +/datum/bitrunning_gimmick + /// Player readable name of the gimmick loadout + var/name = "Gimmick Loadout" + /// The list of actions that this will grant + var/list/datum/action/granted_actions + /// The list of items that this will grant + var/list/obj/item/granted_items + /// The item type we will use as a container for our granted items, given to the avatar + var/obj/item/container_item_type = /obj/item/storage/briefcase/secure/digital_storage + /// Prefix our name onto the + var/prefix_container_name = TRUE + +/// Grants out loadout. +/datum/bitrunning_gimmick/proc/grant_loadout(mob/living/carbon/human/neo, mob/living/carbon/human/avatar, external_load_flags) + var/return_flags = NONE + return_flags |= grant_items(neo, avatar, external_load_flags) + return_flags |= grant_abilities(neo, avatar, external_load_flags) + return return_flags + +/datum/bitrunning_gimmick/proc/grant_items(mob/living/carbon/human/neo, mob/living/carbon/human/avatar, external_load_flags) + if(!length(granted_items)) + return NONE + + if(external_load_flags & DOMAIN_FORBIDS_ITEMS) + return BITRUNNER_GEAR_LOAD_BLOCKED + + var/obj/item/container_item = new container_item_type() + if(prefix_container_name) + container_item.name = "[LOWER_TEXT(name)]'s [initial(container_item.name)]" + + for(var/obj/item/granted_item as anything in granted_items) + new granted_item(container_item) + + avatar.put_in_hands(container_item) + + return NONE + +/datum/bitrunning_gimmick/proc/grant_abilities(mob/living/carbon/human/neo, mob/living/carbon/human/avatar, external_load_flags) + if(!length(granted_actions)) + return NONE + + if(external_load_flags & DOMAIN_FORBIDS_ABILITIES) + return BITRUNNER_GEAR_LOAD_BLOCKED + + var/return_flags = NONE + + for(var/datum/action/granted_action as anything in granted_actions) + if(locate(granted_action) in avatar.actions) + return_flags |= BITRUNNER_GEAR_LOAD_FAILED + continue + + var/datum/action/our_action = new granted_action() + our_action.Grant(avatar) + + return return_flags + + +/** + * Base container used for gimmick disks. + */ +/obj/item/storage/briefcase/secure/digital_storage + name = "digi-case" + desc = "It's made of AUTHENTIC digital leather and has a price-tag still attached. Its owner must be a real professional." + icon_state = "secure" + base_icon_state = "secure" + inhand_icon_state = "sec-case" + +/obj/item/storage/briefcase/secure/digital_storage/PopulateContents() + return diff --git a/code/modules/bitrunning/objects/gimmick_disks/dungeon_disk.dm b/code/modules/bitrunning/objects/gimmick_disks/dungeon_disk.dm new file mode 100644 index 00000000000..559c7f022df --- /dev/null +++ b/code/modules/bitrunning/objects/gimmick_disks/dungeon_disk.dm @@ -0,0 +1,226 @@ + +/** + * Bitrunning gimmick loadouts themed around dungeon crawling. + * Mostly for fun, have niche but not insignificant advantages. + */ +/obj/item/bitrunning_disk/gimmick/dungeon + name = "bitrunning gimmick: dungeon crawling" + selectable_loadouts = list( + /datum/bitrunning_gimmick/alchemist, + /datum/bitrunning_gimmick/rogue, + /datum/bitrunning_gimmick/healer, + /datum/bitrunning_gimmick/wizard, + ) + + +/datum/bitrunning_gimmick/alchemist + name = "Alchemist" + + granted_items = list( + /obj/item/clothing/suit/bio_suit/plaguedoctorsuit, + /obj/item/clothing/mask/gas/plaguedoctor, + /obj/item/clothing/head/bio_hood/plague, + /obj/item/storage/box/alchemist_basic_chems, + /obj/item/storage/box/alchemist_basic_chems, + /obj/item/storage/box/alchemist_random_chems, + /obj/item/storage/box/alchemist_chemistry_kit, + ) + +/obj/item/reagent_containers/cup/bottle/alchemist_basic + name = "unlabeled bottle" + desc = "A small bottle. You don't remember what you put in it." + + /// List of possible reagents we may pick from + var/static/list/possible_reagents = list( + /datum/reagent/aluminium, // Basic chems + /datum/reagent/bromine, + /datum/reagent/carbon, + /datum/reagent/chlorine, + /datum/reagent/copper, + /datum/reagent/consumable/ethanol, + /datum/reagent/fluorine, + /datum/reagent/hydrogen, + /datum/reagent/iodine, + /datum/reagent/iron, + /datum/reagent/lithium, + /datum/reagent/mercury, + /datum/reagent/nitrogen, + /datum/reagent/oxygen, + /datum/reagent/phosphorus, + /datum/reagent/potassium, + /datum/reagent/uranium/radium, + /datum/reagent/silicon, + /datum/reagent/sodium, + /datum/reagent/stable_plasma, + /datum/reagent/consumable/sugar, + /datum/reagent/sulfur, + /datum/reagent/toxin/acid, + /datum/reagent/water, + /datum/reagent/fuel, + /datum/reagent/toxin/leadacetate, // Funny chems + /datum/reagent/consumable/liquidgibs, + /datum/reagent/consumable/nutriment, + /datum/reagent/consumable/coffee, + ) + +/obj/item/reagent_containers/cup/bottle/alchemist_basic/add_initial_reagents() + var/our_reagent = pick(possible_reagents) + reagents.add_reagent(our_reagent, 50) + +/obj/item/reagent_containers/cup/bottle/alchemist_random + name = "skull-labeled bottle" + desc = "A small bottle. You don't remember what you put in it." + + /// List of random adjectives this bottle may have + var/static/list/possible_adjectives = list( + "unlabeled", + "skull-labeled", // Labels + "heart-labeled", + "explosion-labeled", + "fish-labeled", + "smiley-labeled", + "frown-labeled", + "interrobang-labeled", + "plus-labeled", + "d20-labeled", + "unreadably-labeled", + "black-labeled", + "empty-labeled", + "age-yellowed", // Qualities + "blood-tinged", + "ash-marred", + "claw-scratched", + "marker-marked", + "cracked", + "ominous", // Abstract + ) + +/obj/item/reagent_containers/cup/bottle/alchemist_random/Initialize(mapload) + . = ..() + name = "[pick(possible_adjectives)] bottle" + +/obj/item/reagent_containers/cup/bottle/alchemist_random/add_initial_reagents() + var/our_reagent = get_random_reagent_id() + var/our_amount = rand(20, 50) + reagents.add_reagent(our_reagent, our_amount) + +/obj/item/storage/box/alchemist_basic_chems + name = "box of alchemical bases" + desc = "Contains a set of basic reagents, for all your potion-making needs! If only you labeled them." + illustration = "beaker" + +/obj/item/storage/box/alchemist_basic_chems/PopulateContents() + for(var/i in 1 to 7) + if(prob(1)) + new /obj/item/reagent_containers/cup/glass/coffee(src) + continue + new /obj/item/reagent_containers/cup/bottle/alchemist_basic(src) + +/obj/item/storage/box/alchemist_random_chems + name = "box of potions" + desc = "An especially fancy box to keep your finished potions safe." + icon_state = "syndiebox" + illustration = "beaker" + +/obj/item/storage/box/alchemist_random_chems/PopulateContents() + for(var/i in 1 to 7) + if(prob(1)) + new /obj/item/reagent_containers/cup/glass/coffee(src) + continue + new /obj/item/reagent_containers/cup/bottle/alchemist_random(src) + +/obj/item/storage/box/alchemist_chemistry_kit + name = "box of alchemy tools" + desc = "Contains everything needed for the up and coming chemistry student to enact hazardous chemical mishaps in the comfort of their own home." + +/obj/item/storage/box/alchemist_chemistry_kit/PopulateContents() + new /obj/item/reagent_containers/cup/mortar(src) + new /obj/item/pestle(src) + new /obj/item/lighter/skull(src) + new /obj/item/ph_booklet(src) + new /obj/item/thermometer(src) + new /obj/item/storage/test_tube_rack/full(src) + new /obj/item/reagent_containers/cup/glass/coffee(src) + + +/datum/bitrunning_gimmick/rogue + name = "Rogue" + + granted_items = list( + /obj/item/clothing/under/color/black, + /obj/item/clothing/shoes/sneakers/black, + /obj/item/clothing/mask/facescarf/rogue, + /obj/item/clothing/glasses/eyepatch, + /obj/item/bedsheet/black/rogue_cape, + /obj/item/storage/belt/fannypack/black/rogue, + /obj/item/knife/combat/survival, + ) + +/obj/item/clothing/shoes/sneakers/black + name = "sneaker of SNEAKING" + +/obj/item/clothing/mask/facescarf/rogue + name = "cloth of DOOM" + greyscale_colors = "#292929" + +/obj/item/clothing/glasses/eyepatch + name = "eyepatch of SEALING" + +/obj/item/bedsheet/black/rogue_cape + name = "cape of DARKNESS" + +/obj/item/storage/belt/fannypack/black/rogue + name = "fannypack of ULTIMATE DESPAIR" + +/obj/item/storage/belt/fannypack/black/rogue/PopulateContents() + new /obj/item/food/drug/saturnx(src) + new /obj/item/reagent_containers/cup/blastoff_ampoule(src) + new /obj/item/reagent_containers/hypospray/medipen/methamphetamine(src) + + +/datum/bitrunning_gimmick/healer + name = "Healer" + + granted_items = list( + /obj/item/clothing/under/costume/singer/yellow, + /obj/item/clothing/shoes/singery, + /obj/item/rod_of_asclepius, + /obj/item/storage/medkit/surgery, + /obj/item/emergency_bed, + /obj/item/food/canned/larvae, + /obj/item/reagent_containers/dropper, + ) + + +/datum/bitrunning_gimmick/wizard + name = "Wizard" + + granted_items = list( + /obj/item/clothing/head/wizard/fake, + /obj/item/clothing/suit/wizrobe/fake, + /obj/item/clothing/glasses/eyepatch/medical/chuuni, + /obj/item/staff, + /obj/item/toy/eightball, + /obj/item/storage/fancy/cigarettes/cigpack_cannabis, + /obj/item/storage/box/matches, + ) + + granted_actions = list( + /datum/action/cooldown/spell/pointed/untie_shoes/digital, + /datum/action/cooldown/spell/smoke/digital, + ) + +/datum/action/cooldown/spell/pointed/untie_shoes/digital + name = "Untie Digi-Shoes" + spell_requirements = SPELL_REQUIRES_WIZARD_GARB + +/datum/action/cooldown/spell/smoke/digital + name = "Digi-Smoke" + desc = "This spell spawns a small cloud of smoke at your location." + + school = SCHOOL_CONJURATION + cooldown_time = 36 SECONDS + spell_requirements = SPELL_REQUIRES_WIZARD_GARB + + smoke_type = /datum/effect_system/fluid_spread/smoke + smoke_amt = 2 diff --git a/code/modules/bitrunning/objects/gimmick_disks/sports_disk.dm b/code/modules/bitrunning/objects/gimmick_disks/sports_disk.dm new file mode 100644 index 00000000000..6be07b53d58 --- /dev/null +++ b/code/modules/bitrunning/objects/gimmick_disks/sports_disk.dm @@ -0,0 +1,152 @@ + +/** + * Bitrunning gimmick loadouts themed around sports. + * Mostly for fun, have niche or little advantages. + */ +/obj/item/bitrunning_disk/gimmick/sports + name = "bitrunning gimmick: sports" + selectable_loadouts = list( + /datum/bitrunning_gimmick/boxer, + /datum/bitrunning_gimmick/skater, + /datum/bitrunning_gimmick/archer, + /datum/bitrunning_gimmick/fisher, + /datum/bitrunning_gimmick/gamer, + ) + + +/datum/bitrunning_gimmick/boxer + name = "Boxer" + + granted_items = list( + /obj/item/clothing/gloves/boxing/evil, + /obj/item/clothing/under/shorts/red, + /obj/item/reagent_containers/condiment/protein, + /obj/item/reagent_containers/cup/glass/drinkingglass/filled/protein_blend, + ) + +/obj/item/reagent_containers/cup/glass/drinkingglass/filled/protein_blend + name = "Protein Blend" + list_reagents = list(/datum/reagent/consumable/ethanol/protein_blend = 50) + + +/datum/bitrunning_gimmick/skater + name = "Skater" + + granted_items = list( + /obj/item/clothing/shoes/wheelys, + /obj/item/melee/skateboard, + /obj/item/clothing/suit/costume/wellworn_shirt/graphic, + /obj/item/clothing/head/soft/black, + /obj/item/clothing/shoes/sneakers/black, + /obj/item/storage/cans/sixenergydrink, + ) + +/obj/item/storage/cans/sixenergydrink + name = "energy drink bottle ring" + desc = "Holds six energy drink cans. Remember to recycle when you're done!" + + /// Pool of energy drinks tm we may add from + var/list/energy_drink_options = list( + /obj/item/reagent_containers/cup/soda_cans/space_mountain_wind = 50, + /obj/item/reagent_containers/cup/soda_cans/monkey_energy = 30, + /obj/item/reagent_containers/cup/soda_cans/volt_energy = 15, + /obj/item/reagent_containers/cup/soda_cans/thirteenloko = 5, + ) + +/obj/item/storage/cans/sixenergydrink/PopulateContents() + for(var/i in 1 to 6) + var/obj/item/chosen_energy_drink = pick_weight(energy_drink_options) + new chosen_energy_drink(src) + + +/datum/bitrunning_gimmick/archer + name = "Archer" + + granted_items = list( + /obj/item/clothing/under/costume/kimono, + /obj/item/clothing/shoes/sandal/alt, + /obj/item/storage/bag/quiver/endless, + /obj/item/gun/ballistic/bow/longbow, + /obj/item/ammo_casing/arrow/holy/blazing, + ) + +/obj/item/storage/bag/quiver/endless + name = "endless quiver" + desc = "Holds arrows for your bow. A deep digital void is contained within." + max_slots = 1 + +/obj/item/storage/bag/quiver/endless/Initialize(mapload) + . = ..() + RegisterSignal(src, COMSIG_ATOM_EXITED, PROC_REF(handle_removed)) + +/obj/item/storage/bag/quiver/endless/PopulateContents() + . = ..() + new arrow_path(src) + +/obj/item/storage/bag/quiver/endless/proc/handle_removed(datum/source, obj/item/gone) + new arrow_path(src) + + +/datum/bitrunning_gimmick/fisher + name = "Fisher" + + granted_items = list( + /obj/item/clothing/under/misc/overalls, + /obj/item/clothing/suit/jacket/miljacket, + /obj/item/clothing/head/soft/black, + /obj/item/clothing/shoes/jackboots, + /obj/item/storage/toolbox/fishing/small, + /obj/item/bait_can/worm/premium, + /obj/item/grenade/iedcasing/spawned, + /obj/item/stock_parts/power_store/cell/lead, + /obj/item/reagent_containers/cup/glass/bottle/beer/light, + ) + + +/datum/bitrunning_gimmick/gamer + name = "Gamer" + + granted_items = list( + /obj/item/clothing/under/suit/black_really, + /obj/item/clothing/shoes/laceup, + /obj/item/clothing/gloves/color/black, + /obj/item/clothing/glasses/sunglasses, + /obj/item/modular_computer/laptop/gamer, + /obj/item/storage/cans/sixgamerdrink, + ) + +/obj/item/storage/cans/sixgamerdrink + name = "gamer drink bottle ring" + desc = "Holds six gamer drink cans. Remember to recycle when you're done!" + + /// Pool of gamer drinks tm we may add from + var/list/gamer_drink_options = list( + /obj/item/reagent_containers/cup/soda_cans/pwr_game = 55, + /obj/item/reagent_containers/cup/soda_cans/space_mountain_wind = 15, + /obj/item/reagent_containers/cup/soda_cans/monkey_energy = 15, + /obj/item/reagent_containers/cup/soda_cans/volt_energy = 10, + /obj/item/reagent_containers/cup/soda_cans/thirteenloko = 5, + ) + +/obj/item/storage/cans/sixgamerdrink/PopulateContents() + for(var/i in 1 to 6) + var/obj/item/chosen_gamer_drink = pick_weight(gamer_drink_options) + new chosen_gamer_drink(src) + +/obj/item/modular_computer/laptop/gamer + desc = "A high-end laptop often used for metagaming." + device_theme = PDA_THEME_TERMINAL + starting_programs = list( + /datum/computer_file/program/themeify, + /datum/computer_file/program/filemanager, + /datum/computer_file/program/notepad, + /datum/computer_file/program/arcade/eazy, + /datum/computer_file/program/mafia, + ) + start_open = FALSE + +/obj/item/modular_computer/laptop/gamer/install_default_programs() + // Only install starting programs, we don't want the software downloading program from default programs + for(var/programs as anything in starting_programs) + var/datum/computer_file/program/program_type = new programs + store_file(program_type) diff --git a/code/modules/bitrunning/orders/tech.dm b/code/modules/bitrunning/orders/tech.dm index cb509fb6252..d14cf7bf1a1 100644 --- a/code/modules/bitrunning/orders/tech.dm +++ b/code/modules/bitrunning/orders/tech.dm @@ -31,6 +31,16 @@ purchase_path = /obj/item/bitrunning_disk/ability/tier3 desc = "This disk contains a program that lets you shapeshift into a lesser ashdrake, or a polar bear." +/datum/orderable_item/bitrunning_tech/gimmick_sports + cost_per_order = 750 + purchase_path = /obj/item/bitrunning_disk/gimmick/sports + desc = "This disk contains a program that lets you equip a sports-themed gimmick loadout." + +/datum/orderable_item/bitrunning_tech/gimmick_dungeon_crawling + cost_per_order = 1250 + purchase_path = /obj/item/bitrunning_disk/gimmick/dungeon + desc = "This disk contains a program that lets you equip a dungeon crawling-themed gimmick loadout." + /datum/orderable_item/bitrunning_tech/flip_skillchip purchase_path = /obj/item/skillchip/matrix_taunt cost_per_order = 1500 diff --git a/code/modules/bitrunning/server/obj_generation.dm b/code/modules/bitrunning/server/obj_generation.dm index 9f473980bbf..cd2f39738d6 100644 --- a/code/modules/bitrunning/server/obj_generation.dm +++ b/code/modules/bitrunning/server/obj_generation.dm @@ -129,54 +129,27 @@ /// Scans over neo's contents for bitrunning tech disks. Loads the items or abilities onto the avatar. /obj/machinery/quantum_server/proc/stock_gear(mob/living/carbon/human/avatar, mob/living/carbon/human/neo, datum/lazy_template/virtual_domain/generated_domain) - var/domain_forbids_items = generated_domain.forbids_disk_items - var/domain_forbids_spells = generated_domain.forbids_disk_spells + var/domain_forbids_flags = generated_domain.external_load_flags var/import_ban = list() var/disk_ban = list() - if(domain_forbids_items) + if(domain_forbids_flags & DOMAIN_FORBIDS_ITEMS) import_ban += "smuggled digital equipment" disk_ban += "items" - if(domain_forbids_spells) + if(domain_forbids_flags & DOMAIN_FORBIDS_ABILITIES) import_ban += "imported_abilities" disk_ban += "powers" if(length(import_ban)) - to_chat(neo, span_warning("This domain forbids the use of [english_list(import_ban)], your disk [english_list(disk_ban)] will not be granted!")) + to_chat(neo, span_warning("This domain forbids the use of [english_list(import_ban)], your externally loaded [english_list(disk_ban)] will not be granted!")) - var/failed = FALSE + var/return_flags = NONE + return_flags = SEND_SIGNAL(neo, COMSIG_BITRUNNER_STOCKING_GEAR, avatar, domain_forbids_flags) - // We don't need to bother going over the disks if neither of the types can be used. - if(domain_forbids_spells && domain_forbids_items) - return - for(var/obj/item/bitrunning_disk/disk in neo.get_contents()) - if(istype(disk, /obj/item/bitrunning_disk/ability) && !domain_forbids_spells) - var/obj/item/bitrunning_disk/ability/ability_disk = disk - - if(isnull(ability_disk.granted_action)) - failed = TRUE - continue - - var/datum/action/our_action = new ability_disk.granted_action() - - if(locate(our_action.type) in avatar.actions) - failed = TRUE - continue - - our_action.Grant(avatar) - continue - - if(istype(disk, /obj/item/bitrunning_disk/item) && !domain_forbids_items) - var/obj/item/bitrunning_disk/item/item_disk = disk - - if(isnull(item_disk.granted_item)) - failed = TRUE - continue - - avatar.put_in_hands(new item_disk.granted_item()) - - if(failed) - to_chat(neo, span_warning("One of your disks failed to load. Check for duplicate or inactive disks.")) + if(return_flags & BITRUNNER_GEAR_LOAD_FAILED) + to_chat(neo, span_warning("At least one of your external data sources has encountered a failure in its loading process. Check for overlapping or inactive disks.")) + if(return_flags & BITRUNNER_GEAR_LOAD_BLOCKED) + to_chat(neo, span_warning("At least one of your external data sources has been blocked from fully loading. Check domain restrictions.")) var/obj/item/organ/brain/neo_brain = neo.get_organ_slot(ORGAN_SLOT_BRAIN) for(var/obj/item/skillchip/skill_chip as anything in neo_brain?.skillchips) diff --git a/code/modules/bitrunning/virtual_domain/virtual_domain.dm b/code/modules/bitrunning/virtual_domain/virtual_domain.dm index f81f6186cbe..7467b8e4c5e 100644 --- a/code/modules/bitrunning/virtual_domain/virtual_domain.dm +++ b/code/modules/bitrunning/virtual_domain/virtual_domain.dm @@ -37,10 +37,8 @@ * Player customization */ - /// If this domain blocks the use of items from disks, for whatever reason - var/forbids_disk_items = FALSE - /// If this domain blocks the use of spells from disks, for whatever reason - var/forbids_disk_spells = FALSE + /// Any restrictions this domain has on what external sources can load in + var/external_load_flags = NONE /// Any outfit that you wish to force on avatars. Overrides preferences var/datum/outfit/forced_outfit diff --git a/tgstation.dme b/tgstation.dme index 25ecd8cb435..465dfbaa341 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -3628,6 +3628,7 @@ #include "code\modules\bitrunning\antagonists\ghost_role.dm" #include "code\modules\bitrunning\antagonists\netguardian.dm" #include "code\modules\bitrunning\components\avatar_connection.dm" +#include "code\modules\bitrunning\components\avatar_gear.dm" #include "code\modules\bitrunning\components\bitrunning_points.dm" #include "code\modules\bitrunning\components\glitch.dm" #include "code\modules\bitrunning\components\netpod_healing.dm" @@ -3651,6 +3652,9 @@ #include "code\modules\bitrunning\objects\loot_crate.dm" #include "code\modules\bitrunning\objects\quantum_console.dm" #include "code\modules\bitrunning\objects\vendor.dm" +#include "code\modules\bitrunning\objects\gimmick_disks\_gimmick_disk.dm" +#include "code\modules\bitrunning\objects\gimmick_disks\dungeon_disk.dm" +#include "code\modules\bitrunning\objects\gimmick_disks\sports_disk.dm" #include "code\modules\bitrunning\orders\bepis.dm" #include "code\modules\bitrunning\orders\flair.dm" #include "code\modules\bitrunning\orders\tech.dm"