Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions code/__DEFINES/~darkpack/admin_categories.dm
Comment thread
buffyuwu marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Second City admin verb category
#define ADMIN_CATEGORY_SECOND_CITY "Admin.Second City"
12 changes: 7 additions & 5 deletions code/modules/client/preferences.dm
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,16 @@ GLOBAL_LIST_EMPTY(preferences_datums)

//Job preferences 2.0 - indexed by job title , no key or value implies never
var/list/job_preferences = list()
// DARKPACK EDIT ADD START - STORYTELLER_STATS
// DARKPACK EDIT ADD START
var/list/preference_storyteller_stats = list()
// DARKPACK EDIT ADD END
// DARKPACK EDIT ADD START - ALTERNATIVE_JOB_TITLES
/// Alternative job titles stored in preferences. Assoc list, ie. alt_job_titles["Scientist"] = "Cytologist"
// Associative list of disciplines and their current level. like: list("/datum/discipline/animalism" = 2)
var/list/discipline_levels = list()
// Alternative job titles stored in preferences. Assoc list, ie. alt_job_titles["Scientist"] = "Cytologist"
var/list/alt_job_titles = list()
/// Whether this player is whitelisted to bypass discipline sheet validation limits
var/discipline_trusted = FALSE
// DARKPACK EDIT ADD END
/// The current window, PREFERENCE_TAB_* in [`code/__DEFINES/preferences.dm`]
// The current window, PREFERENCE_TAB_* in [`code/__DEFINES/preferences.dm`]
var/current_window = PREFERENCE_TAB_CHARACTER_PREFERENCES

var/unlock_content = 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,25 @@
// List of known guestbook names we have saved
var/list/guestbook_names = list()

/datum/preferences/load_preferences()
. = ..()
discipline_trusted = savefile.get_entry("discipline_trusted", FALSE) // this could probably be done in load_character but it would be less performant

/datum/preferences/save_preferences()
. = ..()
savefile.set_entry("discipline_trusted", discipline_trusted) // since existing load/save is per character, save and load it from a level above that

/datum/preferences/load_character(slot)
. = ..()
var/tree_key = "character[slot]"
var/tree_key = "character[default_slot]"
var/list/save_data = savefile.get_entry(tree_key)

// We don't really need to do any validation since that's all done in the parent proc.

guestbook_names = save_data?["guestbook_names"]
guestbook_names = SANITIZE_LIST(guestbook_names)
alt_job_titles = save_data?["alt_job_titles"] // ALTERNATIVE_JOB_TITLES
discipline_levels = SANITIZE_LIST(save_data?["discipline_levels"]) || list()

/datum/preferences/save_character()
. = ..()
Expand All @@ -22,3 +31,5 @@

save_data["guestbook_names"] = guestbook_names
save_data["alt_job_titles"] = alt_job_titles // ALTERNATIVE_JOB_TITLES
save_data["discipline_levels"] = discipline_levels
savefile.save()
212 changes: 212 additions & 0 deletions modular_darkpack/modules/powers/code/admin_discipline_editor.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
/datum/admin_discipline_editor
var/target_ckey = ""
var/selected_slot = 0
var/datum/preferences/target_prefs = null
var/loaded_offline = FALSE
var/not_found = FALSE
var/list/discipline_cache = null

/datum/admin_discipline_editor/Destroy()
if(loaded_offline)
QDEL_NULL(target_prefs)
target_prefs = null
return ..()

/datum/admin_discipline_editor/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "AdminDisciplineEditor")
ui.open()

/datum/admin_discipline_editor/ui_state(mob/user)
return ADMIN_STATE(R_ADMIN)

/datum/admin_discipline_editor/ui_data(mob/user)
var/list/data = list()
data["target_ckey"] = target_ckey
data["selected_slot"] = selected_slot
data["not_found"] = not_found
data["is_trusted"] = target_prefs?.discipline_trusted || FALSE
data["character_slots"] = list()
data["discipline_levels"] = list()
data["clan_disciplines"] = list()
data["disciplines"] = build_discipline_cache()
data["discipline_validation"] = null
data["character_name"] = null
data["character_age"] = null
data["immortal_age"] = null
data["clan_name"] = null
data["splat_name"] = null
data["flavor_text"] = null
data["headshot"] = null

var/list/connected = list()
var/list/invalid_ckeys = list()
var/list/trusted_ckeys = list()
for(var/ckey in GLOB.directory)
var/client/C = GLOB.directory[ckey]
if(!C || !C.mob)
continue
connected += ckey
if(C.prefs?.discipline_trusted)
trusted_ckeys += ckey
if(ishuman(C.mob))
var/list/validation = validate_mob_sheet(C.mob)
if(validation && !validation["valid"])
invalid_ckeys += ckey
data["connected_ckeys"] = connected
data["invalid_ckeys"] = invalid_ckeys
data["trusted_ckeys"] = trusted_ckeys

if(target_prefs)
var/list/profiles = target_prefs.create_character_profiles()
for(var/i in 1 to target_prefs.max_save_slots)
if(profiles[i])
data["character_slots"] += list(list("slot" = i, "name" = profiles[i]))

if(selected_slot > 0)
data["character_name"] = target_prefs.read_preference(/datum/preference/name/real_name)
data["character_age"] = target_prefs.read_preference(/datum/preference/numeric/age)
data["immortal_age"] = target_prefs.read_preference(/datum/preference/numeric/immortal_age)
data["flavor_text"] = target_prefs.read_preference(/datum/preference/text/flavor_text)
data["headshot"] = target_prefs.read_preference(/datum/preference/text/headshot)

var/splat_path = target_prefs.read_preference(/datum/preference/choiced/splats)
if(ispath(splat_path, /datum/splat))
var/datum/splat/splat_proto = GLOB.splat_prototypes[splat_path]
data["splat_name"] = splat_proto?.name
else
data["splat_name"] = "Human"

var/list/clan_discs = list()
for(var/disc_path in target_prefs.discipline_levels)
data["discipline_levels"]["[disc_path]"] = target_prefs.discipline_levels[disc_path]

var/clan_value = target_prefs.read_preference(/datum/preference/choiced/subsplat/vampire_clan)
if(clan_value)
var/datum/subsplat/vampire_clan/clan_datum = get_vampire_clan(clan_value)
if(clan_datum)
data["clan_name"] = clan_datum.name
for(var/disc_type in clan_datum.clan_disciplines)
if(ispath(disc_type, /datum/discipline))
var/disc_str = "[disc_type]"
data["clan_disciplines"] += disc_str
clan_discs += disc_str

data["discipline_validation"] = validate_discipline_sheet(target_prefs.discipline_levels, clan_discs, target_prefs.discipline_trusted)

return data

/datum/admin_discipline_editor/proc/build_discipline_cache()
if(discipline_cache)
return discipline_cache

discipline_cache = list()
for(var/discipline_type in subtypesof(/datum/discipline))
var/datum/discipline/discipline = new discipline_type
if(!discipline.selectable)
qdel(discipline)
continue
if(ispath(discipline_type, /datum/discipline/path))
qdel(discipline)
continue
var/list/disc_data = list()
disc_data["name"] = discipline.name
disc_data["desc"] = discipline.desc
disc_data["max_level"] = discipline.max_selectable_level || length(discipline.all_powers)
disc_data["rarity"] = (discipline_type in RARE_DISCIPLINE_TYPES) ? "rare" : "common"
disc_data["icon"] = initial(discipline.icon)
disc_data["icon_state"] = discipline.icon_state
discipline_cache["[discipline_type]"] = disc_data
qdel(discipline)

return discipline_cache

/datum/admin_discipline_editor/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return

switch(action)
if("search_ckey")
var/search = params["ckey"]
if(!search)
return FALSE
search = ckey(search)
not_found = !load_target(search)
log_admin_private("[key_name_admin(ui.user)] searched for [search] in the discipline UI.")
return TRUE

if("select_slot")
if(!target_prefs)
return FALSE
var/slot = round(text2num(params["slot"]))
if(!slot)
return FALSE
slot = clamp(slot, 1, target_prefs.max_save_slots)
target_prefs.load_character(slot)
target_prefs.default_slot = slot
selected_slot = slot
return TRUE

if("set_discipline_level")
if(!target_prefs || !selected_slot)
return FALSE
var/disc_path = params["discipline"]
var/new_level = text2num(params["level"])
if(!disc_path || isnull(new_level))
return FALSE
new_level = round(new_level)
if(new_level < 0 || new_level > 5)
return FALSE
if(new_level == 0)
target_prefs.discipline_levels -= disc_path
else
target_prefs.discipline_levels[disc_path] = new_level
target_prefs.save_character()
var/character_name = target_prefs.read_preference(/datum/preference/name/real_name)
message_admins("[key_name_admin(ui.user)] set [disc_path] to level [new_level] for [ADMIN_LOOKUPFLW(target_ckey)]'s character [character_name]).")
log_admin("[key_name_admin(ui.user)] set [disc_path] to level [new_level] for [ADMIN_LOOKUPFLW(target_ckey)]'s character [character_name]).")
return TRUE

if("toggle_trusted")
if(!target_prefs)
return FALSE
target_prefs.discipline_trusted = !target_prefs.discipline_trusted
target_prefs.save_preferences()
message_admins("[ui.user] [target_prefs.discipline_trusted ? "granted" : "revoked"] trusted discipline whitelist for [ADMIN_LOOKUPFLW(target_ckey)].")
return TRUE

/datum/admin_discipline_editor/proc/load_target(search_ckey)
if(loaded_offline && target_prefs)
qdel(target_prefs)
target_prefs = null
loaded_offline = FALSE

target_ckey = search_ckey
selected_slot = 0

var/client/found_client = GLOB.directory[search_ckey]
if(found_client?.prefs)
target_prefs = found_client.prefs
loaded_offline = FALSE
selected_slot = target_prefs.default_slot
return TRUE
var/prefs_path = "data/player_saves/[search_ckey[1]]/[search_ckey]/preferences.json"
if(!fexists(prefs_path))
return FALSE
var/datum/client_interface/mock = new
mock.ckey = search_ckey
mock.key = search_ckey
var/datum/preferences/offline_prefs = new(mock)
offline_prefs.load_character(1)
offline_prefs.default_slot = 1
target_prefs = offline_prefs
loaded_offline = TRUE
selected_slot = 1
return TRUE

ADMIN_VERB(discipline_menu, R_ADMIN, "Discipline Menu", "Edit a player's disciplines.", ADMIN_CATEGORY_SECOND_CITY)
var/datum/admin_discipline_editor/editor = new
editor.ui_interact(user.mob)
BLACKBOX_LOG_ADMIN_VERB("Discipline Menu")
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
var/name = "Discipline name"
///Text description of this Discipline.
var/desc = "Discipline description"
///Icon for this Discipline as in disciplines.dmi
///Icon file for this Discipline
var/icon = 'modular_darkpack/modules/deprecated/icons/UI/actions.dmi'
///Icon state for this Discipline
var/icon_state
///If this Discipline is unique to a certain Clan.
var/clan_restricted = FALSE
Expand All @@ -14,6 +16,8 @@
var/action_type = /datum/action/discipline
///If this Discipline can be selected at all, or has special handling.
var/selectable = TRUE
///Override for the number of selectable levels shown in UI. 0 = derive from all_powers length. this exists because of Thaumaturgy.
var/max_selectable_level = 0

/* BACKEND */
///What rank, or how many dots the caster has in this Discipline.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
icon_state = "thaumaturgy"
clan_restricted = TRUE
power_type = /datum/discipline_power/thaumaturgy
max_selectable_level = 5

/datum/discipline/thaumaturgy/post_gain()
. = ..()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
desc = "We fall into a false death state, allowing us to feign death."
icon_state = "quietus"
power_type = /datum/discipline_power/torpor
selectable = FALSE // so it doesn't show up in the admin UI as selectable

/datum/discipline_power/torpor
name = "Torpor"
Expand Down
Loading
Loading