Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions code/controllers/subsystem/ticker.dm
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,11 @@ SUBSYSTEM_DEF(ticker)
else // clear any personalities the prefs added since our job clearly does not want them
new_player_living.clear_personalities()

// DARKPACK EDIT ADD START
for(var/datum/preference_middleware/our_middleware in new_player_mob.client?.prefs?.middleware)
our_middleware.on_character_spawn(new_player_mob)
// DARKPACK EDIT ADD END

if(ishuman(new_player_living))
SEND_SIGNAL(new_player_living, COMSIG_HUMAN_CHARACTER_SETUP_FINISHED)
CHECK_TICK
Expand Down
2 changes: 1 addition & 1 deletion code/modules/client/preferences_savefile.dm
Original file line number Diff line number Diff line change
Expand Up @@ -529,7 +529,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
for (var/datum/preference_middleware/preference_middleware as anything in middleware)
preference_middleware.on_new_character(usr)

character_preview_view.update_body()
character_preview_view?.update_body() // DARKPACK EDIT CHANGE - (We technicly can swap slots while they dont have it open)

/datum/preferences/proc/remove_current_slot()
PRIVATE_PROC(TRUE)
Expand Down
5 changes: 5 additions & 0 deletions code/modules/mob/dead/new_player/new_player.dm
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,11 @@
else // clear any personalities the prefs added since our job clearly does not want them
humanc.clear_personalities()

// DARKPACK EDIT ADD START
for(var/datum/preference_middleware/our_middleware in character.client?.prefs?.middleware)
our_middleware.on_character_spawn(character)
// DARKPACK EDIT ADD END

if(humanc) // Quirks may change manifest datapoints, so inject only after assigning quirks
GLOB.manifest.inject(humanc)
SEND_SIGNAL(humanc, COMSIG_HUMAN_CHARACTER_SETUP_FINISHED)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,7 @@
if(!slot)
return FALSE
slot = clamp(slot, 1, target_prefs.max_save_slots)
target_prefs.load_character(slot)
target_prefs.default_slot = slot
target_prefs.switch_to_slot(slot)
selected_slot = slot
return TRUE

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,10 @@
SHOULD_NOT_OVERRIDE(TRUE)
return points

/datum/st_stat/proc/get_initial_points()
SHOULD_NOT_OVERRIDE(TRUE)
return src::points

/datum/st_stat/proc/set_points(amount)
SHOULD_NOT_OVERRIDE(TRUE)
points = max(amount, 0)
Expand Down
136 changes: 136 additions & 0 deletions modular_darkpack/modules/storyteller_stats/code/stat_validation.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/datum/preference_middleware/stats/proc/get_stat_validation_error()
var/list/stats = preferences.preference_storyteller_stats
if(!stats || !length(stats))
return null

var/list/score_violations = list()
for(var/stat_typepath in stats)
var/datum/st_stat/stat = stats[stat_typepath]
var/score = stat.get_score(include_bonus = FALSE)
if(score < stat.min_score || score > stat.max_score)
score_violations += "[stat.name]: [score] (allowed [stat.min_score] to [stat.max_score])"

var/list/levels_by_abstract = list()
for(var/stat_typepath in stats)
var/datum/st_stat/stat = stats[stat_typepath]
if(!stat.editable || stat.type == stat.abstract_type)
continue
var/levels_above = max(0, stat.get_score(include_bonus = FALSE) - stat.starting_score)
if(!levels_above)
continue
if(!(stat.abstract_type in levels_by_abstract))
levels_by_abstract[stat.abstract_type] = 0
levels_by_abstract[stat.abstract_type] += levels_above

var/list/budget_violations = list()
var/total_freebie_needed = 0
for(var/abstract_typepath in levels_by_abstract)
var/datum/st_stat/abstract_stat = stats[abstract_typepath]
if(!abstract_stat)
continue
var/pool_size = abstract_stat.get_initial_points()
var/freebie_cost = initial(abstract_stat.freebie_point_cost)
var/levels_above = levels_by_abstract[abstract_typepath]
var/overflow = max(0, levels_above - pool_size)
if(!overflow || !freebie_cost || !abstract_stat.name)
continue
budget_violations += "[abstract_stat.name]: [levels_above] spent, budget is [pool_size] ([overflow] over)"
total_freebie_needed += overflow * freebie_cost

var/datum/st_stat/freebie/freebie_stat = stats[STAT_FREEBIE_POINTS]
var/base_freebie = freebie_stat ? freebie_stat.get_initial_points() : /datum/st_stat/freebie::points
var/quirk_balance = 0
for(var/quirk_name in preferences.all_quirks)
var/datum/quirk/quirk_type = SSquirks.quirks[quirk_name]
if(quirk_type)
quirk_balance -= quirk_type.value
var/available_freebie = max(0, base_freebie + quirk_balance)
var/freebie_short = total_freebie_needed - available_freebie

if(!length(score_violations) && freebie_short <= 0)
return null

var/msg = "STAT VALIDATION"
if(length(score_violations))
msg += "\nStat scores out of range:\n"
for(var/v in score_violations)
msg += " [v]\n"
if(length(budget_violations))
msg += "\nCategories over budget:\n"
for(var/v in budget_violations)
msg += " [v]\n"
if(freebie_short > 0)
msg += "\nFreebie points: need [total_freebie_needed], have [available_freebie] ([freebie_short] short of what is needed to be valid)\n"
return msg

/datum/preference_middleware/proc/on_character_spawn(mob/living/spawning_mob)

/datum/preference_middleware/stats/on_character_spawn(mob/living/spawning_mob)
validate_stats()

/datum/preference_middleware/stats/proc/validate_stats()
var/stat_error = get_stat_validation_error()
if(!stat_error)
// message_admins("[key_name(preferences.parent?.ckey)]'s character '[char_name]' (slot [preferences.default_slot]): VALID")
return
var/char_name = preferences.read_preference(/datum/preference/name/real_name)
// to_chat(checked_mob, span_warning(stat_error))
var/log_msg = "[key_name(preferences.parent?.ckey)]'s character '[char_name]' (slot [preferences.default_slot]) has invalid stats:\n[stat_error]"
// SSoverwatch.record_action(null, log_msg)
message_admins(log_msg)


ADMIN_VERB(validate_sheets, R_ADMIN, "Validate Sheets", "Validate Sheets.", ADMIN_CATEGORY_SECOND_CITY)
var/choice = tgui_alert(user, "Validate unloaded characters as well?", "Validate Sheets", list("Yes", "Including Offline", "No"))
var/list/connected = list()
// var/list/invalid_ckeys = list()
for(var/ckey in GLOB.directory)
var/client/C = GLOB.directory[ckey]
if(!C || !C.mob || !C.prefs)
continue
connected += ckey
/*
if(ishuman(C.mob))
var/list/validation = validate_mob_sheet(C.mob)
if(validation && !validation["valid"])
invalid_ckeys += ckey
*/
if(choice != "No")
var/original_slot = C.prefs.default_slot
var/list/profiles = C.prefs.create_character_profiles() // Helps filter for unfilled out slots
for(var/i in 1 to C.prefs.max_save_slots)
if(profiles[i])
var/slot = clamp(i, 1, C.prefs.max_save_slots)
C.prefs.switch_to_slot(slot)
for(var/datum/preference_middleware/stats/stat in C.prefs.middleware)
stat.validate_stats()
C.prefs.switch_to_slot(original_slot)
else
for(var/datum/preference_middleware/stats/stat in C.prefs.middleware)
stat.validate_stats()
if(choice != "Including Offline")
return
for(var/initial in flist("data/player_saves/"))
initial = replacetext(initial, "/", "")
for(var/search_ckey in flist("data/player_saves/[initial]/"))
search_ckey = replacetext(search_ckey, "/", "")
var/prefs_path = "data/player_saves/[initial]/[search_ckey]/preferences.json"
if(!fexists(prefs_path))
continue

if(search_ckey in connected)
continue

var/datum/client_interface/mock = new
mock.ckey = search_ckey
mock.key = search_ckey
var/datum/preferences/offline_prefs = new(mock)
var/original_slot = offline_prefs.default_slot
var/list/profiles = offline_prefs.create_character_profiles() // Helps filter for unfilled out slots
for(var/i in 1 to offline_prefs.max_save_slots)
if(profiles[i])
var/slot = clamp(i, 1, offline_prefs.max_save_slots)
offline_prefs.switch_to_slot(slot)
for(var/datum/preference_middleware/stats/stat in offline_prefs.middleware)
stat.validate_stats()
offline_prefs.switch_to_slot(original_slot)
1 change: 1 addition & 0 deletions tgstation.dme
Original file line number Diff line number Diff line change
Expand Up @@ -8124,6 +8124,7 @@
#include "modular_darkpack\modules\storyteller_stats\code\global_procs.dm"
#include "modular_darkpack\modules\storyteller_stats\code\logging.dm"
#include "modular_darkpack\modules\storyteller_stats\code\stat_pref_middleware.dm"
#include "modular_darkpack\modules\storyteller_stats\code\stat_validation.dm"
#include "modular_darkpack\modules\storyteller_stats\code\mob_affecting_adjustments\medicine_skill_surgery.dm"
#include "modular_darkpack\modules\storyteller_stats\code\mob_affecting_adjustments\mob_procs.dm"
#include "modular_darkpack\modules\storyteller_stats\code\mob_affecting_adjustments\movespeed_modifier.dm"
Expand Down
Loading