Skip to content

Commit

Permalink
Implements Bitrunner Prefs Disks (Feat. Quirks & Evil Hacks) (without…
Browse files Browse the repository at this point in the history
… the blowing up part) (#243)

* redraw that horse as penance for rolling worst blunt imaginable

* THERE we go
  • Loading branch information
00-Steven authored Dec 15, 2024
1 parent 4bec5f8 commit b026856
Show file tree
Hide file tree
Showing 5 changed files with 193 additions and 0 deletions.
21 changes: 21 additions & 0 deletions code/modules/bitrunning/server/obj_generation.dm
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,9 @@
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!"))

var/failed = FALSE
//DOPPLER EDIT ADDITION BEGIN - BITRUNNING_PREFS_DISKS - Track if we've used multiple avatar preference disks, for avoiding overrides and displaying the failure message.
var/duplicate_prefs = FALSE
//DOPPLER EDIT ADDITION END

// 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)
Expand Down Expand Up @@ -175,6 +178,24 @@

avatar.put_in_hands(new item_disk.granted_item())

//DOPPLER EDIT ADDITION BEGIN - BITRUNNING_PREFS_DISKS - Handles our avatar preference disks, if present.
if(istype(disk, /obj/item/bitrunning_disk/preferences))
var/obj/item/bitrunning_disk/preferences/prefs_disk = disk
var/datum/preferences/avatar_preference = prefs_disk.chosen_preference

if(isnull(avatar_preference) || duplicate_prefs)
failed = TRUE
continue

if(!domain_forbids_spells)
avatar_preference.safe_transfer_prefs_to(avatar)
SSquirks.AssignQuirks(avatar, prefs_disk.mock_client)
if(!domain_forbids_items && prefs_disk.include_loadout)
avatar.equip_outfit_and_loadout(/datum/outfit, avatar_preference)

duplicate_prefs = TRUE
//DOPPLER EDIT ADDITION END

if(failed)
to_chat(neo, span_warning("One of your disks failed to load. Check for duplicate or inactive disks."))

Expand Down
119 changes: 119 additions & 0 deletions modular_doppler/bitrunning_prefs_disks/code/disks/prefs_disk.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/**
* Bitrunning tech disks which let you load a custom character preference for your bit avatar.
* This uses a preference selected from your character list.
* Optionally, this may include the loadout as well.
*
* For the sake of domain restrictions:
* - ability blocks block the application of character prefs.
* - item blocks block the application of character loadout.
*/
/obj/item/bitrunning_disk/preferences
name = "bitrunning program: personalized avatar"
desc = "A disk containing source code. It can be used to override your bit avatar's standard appearance. Further avatar disks will be ignored."

// Allows it to be held in the pocket
w_class = WEIGHT_CLASS_SMALL

/// Our chosen preference.
var/datum/preferences/chosen_preference
/// Whether we include the loadout as well.
var/include_loadout = FALSE
/// Mock client we use for forwarding to quirk assignment (beware, evil hacks).
var/datum/prefs_disk_client_interface/mock_client

/obj/item/bitrunning_disk/preferences/Initialize(mapload)
. = ..()
register_context()

/obj/item/bitrunning_disk/preferences/examine(mob/user)
. = ..()
if(isnull(chosen_preference))
return

. += span_info("Loadout application is currently [include_loadout ? "enabled" : "disabled"].")
. += span_notice("Ctrl-click to toggle loadout application.")

/obj/item/bitrunning_disk/preferences/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
var/result = NONE
if(isnull(chosen_preference) && (held_item == src))
context[SCREENTIP_CONTEXT_LMB] = "Select avatar"
result = CONTEXTUAL_SCREENTIP_SET
if(!isturf(src.loc))
context[SCREENTIP_CONTEXT_CTRL_LMB] = "Toggle loadout"
result = CONTEXTUAL_SCREENTIP_SET

return result

/obj/item/bitrunning_disk/preferences/Destroy()
QDEL_NULL(chosen_preference)
QDEL_NULL(mock_client)
return ..()

/obj/item/bitrunning_disk/preferences/attack_self(mob/user, modifiers)
. = ..()

if(isnull(user.client) || chosen_preference)
return

var/list/character_profiles = user.client.prefs?.create_character_profiles()
if(isnull(character_profiles) || !length(character_profiles))
return

var/choice = tgui_input_list(user, message = "Select a character", title = "Bitrunning Avatar", items = character_profiles)
if(isnull(choice) || !user.is_holding(src))
return

choice_made = choice
chosen_preference = new(user.client)
chosen_preference.load_character(character_profiles.Find(choice))

// Perform our evil hacks
if(isnull(mock_client))
mock_client = new
mock_client.prefs = chosen_preference
// Done loading from the client, so replace reference to the real client
chosen_preference.parent = mock_client

balloon_alert(user, "avatar set!")
playsound(user, 'sound/items/click.ogg', 50, TRUE)

/obj/item/bitrunning_disk/preferences/item_ctrl_click(mob/user)
if(isturf(src.loc)) // If on a turf, we skip to dragging
return NONE
if(isnull(chosen_preference))
balloon_alert(user, "set preference first!")
return CLICK_ACTION_BLOCKING
include_loadout = !include_loadout
balloon_alert(user, include_loadout ? "loadout enabled!" : "loadout disabled!")

// High frequency range when enabled, low when disabled. More tactile.
var/toggle_frequency = include_loadout ? rand(45000, 55000) : rand(32000, 42000)
playsound(user, 'sound/items/click.ogg', 50, TRUE, frequency = toggle_frequency)

return CLICK_ACTION_SUCCESS

/**
* Allows for ordering of the prefs disk.
*/
/datum/orderable_item/bitrunning_tech/prefs_disk
cost_per_order = 1000
purchase_path = /obj/item/bitrunning_disk/preferences
desc = "This disk contains a program that lets you load in custom bit avatars."

/**
* Evil hack that allows us to assign quirks without needing to forward a real client.
* Using this instead of the normal mock client allows us to include only what we need without editing the base,
* or interfering with things like `mock_client_uid`.
*
* Much the same, this should match the interface of /client wherever necessary.
*/
/datum/prefs_disk_client_interface
/// Player preferences datum for the client
var/datum/preferences/prefs

/// The mob the client controls
var/mob/mob

/// We don't actually care about award status, but we don't want it to runtime due to not existing.
/datum/prefs_disk_client_interface/proc/get_award_status(achievement_type, mob/user, value = 1)
return 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

// Spawns a single preferences disk to start with for all bitrunners.
/datum/outfit/job/bitrunner
r_pocket = /obj/item/bitrunning_disk/preferences
47 changes: 47 additions & 0 deletions modular_doppler/bitrunning_prefs_disks/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<!-- This should be copy-pasted into the root of your module folder as readme.md -->

SOON<!--PR Number-->

## Bitrunning Avatar Preference Disks <!--Title of your addition.-->

Module ID: BITRUNNING_PREFS_DISKS <!-- Uppercase, UNDERSCORE_CONNECTED name of your module, that you use to mark files. This is so people can case-sensitive search for your edits, if any. -->

### Description:

Allows bitrunners to buy a personalized avatar disk, which lets them load in a given character preference, with all that entails.
This includes even quirks through evil hacks, and optionally loadouts.
Preference application and quirks are blocked if a domain blocks spells/abilities, loadouts are blocked if a domain blocks items.
The evil hacks this performs are using a barebones mock client to allow for quirk assignment without forwarding or affecting the real client.

<!-- Here, try to describe what your PR does, what features it provides and any other directly useful information. -->

### TG Proc/File Changes:

- `code/modules/bitrunning/server/obj_generation.dm`: `proc/stock_gear`
<!-- If you edited any core procs, you should list them here. You should specify the files and procs you changed.
E.g:
- `code/modules/mob/living.dm`: `proc/overriden_proc`, `var/overriden_var`
-->

### Modular Overrides:

- N/A
<!-- If you added a new modular override (file or code-wise) for your module, you should list it here. Code files should specify what procs they changed, in case of multiple modules using the same file.
E.g:
- `modular_doppler/master_files/sound/my_cool_sound.ogg`
- `modular_doppler/master_files/code/my_modular_override.dm`: `proc/overriden_proc`, `var/overriden_var`
-->

### Defines:

- N/A
<!-- If you needed to add any defines, mention the files you added those defines in, along with the name of the defines. -->

### Included files that are not contained in this module:

- N/A
<!-- Likewise, be it a non-modular file or a modular one that's not contained within the folder belonging to this specific module, it should be mentioned here. Good examples are icons or sounds that are used between multiple modules, or other such edge-cases. -->

### Credits: 00-Steven

<!-- Here go the credits to you, dear coder, and in case of collaborative work or ports, credits to the original source of the code. -->
2 changes: 2 additions & 0 deletions tgstation.dme
Original file line number Diff line number Diff line change
Expand Up @@ -6571,6 +6571,8 @@
#include "modular_doppler\autotransfer\autotransfer_config.dm"
#include "modular_doppler\autotransfer\shuttle.dm"
#include "modular_doppler\autotransfer\transfer_vote.dm"
#include "modular_doppler\bitrunning_prefs_disks\code\disks\prefs_disk.dm"
#include "modular_doppler\bitrunning_prefs_disks\code\outfit_overrides\bitrunner_outfit_override.dm"
#include "modular_doppler\cell_component\code\cell_component.dm"
#include "modular_doppler\colony_fabricator\code\cargo_packs.dm"
#include "modular_doppler\colony_fabricator\code\colony_fabricator.dm"
Expand Down

0 comments on commit b026856

Please sign in to comment.