Skip to content

Commit

Permalink
S.P.E.C.I.A.L. pt. 2 (#97)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tsurupeta committed Apr 24, 2024
1 parent df8a9bf commit 6b84c4b
Show file tree
Hide file tree
Showing 21 changed files with 362 additions and 139 deletions.
1 change: 1 addition & 0 deletions code/__DEFINES/jobs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@
#define JOB_UNAVAILABLE_SLOTFULL 5
#define JOB_UNAVAILABLE_SPECIESLOCK 6
#define JOB_UNAVAILABLE_WHITELIST 7
#define JOB_UNAVAILABLE_SPECIAL 8

#define DEFAULT_RELIGION "Christianity"
#define DEFAULT_DEITY "Space Jesus"
Expand Down
6 changes: 6 additions & 0 deletions code/__DEFINES/special.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#define SPECIAL_MAX_POINT_SUM_CAP 38
#define SPECIAL_MIN_ATTR_VALUE 1
#define SPECIAL_DEFAULT_ATTR_VALUE 5
#define SPECIAL_MAX_ATTR_VALUE 10

#define SPECIAL_MIN_INT_CRAFTING_REQUIREMENT 3
2 changes: 2 additions & 0 deletions code/__DEFINES/traits.dm
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@
#define TRAIT_PARALYSIS_R_LEG "para-r-leg"
#define TRAIT_DISK_VERIFIER "disk-verifier"
#define TRAIT_UNINTELLIGIBLE_SPEECH "unintelligible-speech"
#define TRAIT_SAY_STUTTERING "say-stuttering"
#define TRAIT_SAY_LISPING "say-lisping"
#define TRAIT_SOOTHED_THROAT "soothed-throat"
#define TRAIT_LAW_ENFORCEMENT_METABOLISM "law-enforcement-metabolism"
#define TRAIT_QUICK_CARRY "quick-carry"
Expand Down
2 changes: 2 additions & 0 deletions code/_globalvars/traits.dm
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_SOOTHED_THROAT" = TRAIT_SOOTHED_THROAT,
"TRAIT_LAW_ENFORCEMENT_METABOLISM" = TRAIT_LAW_ENFORCEMENT_METABOLISM,
"TRAIT_UNINTELLIGIBLE_SPEECH" = TRAIT_UNINTELLIGIBLE_SPEECH,
"TRAIT_SAY_STUTTERING" = TRAIT_SAY_STUTTERING,
"TRAIT_SAY_LISPING " = TRAIT_SAY_LISPING,
"TRAIT_UNSTABLE" = TRAIT_UNSTABLE,
"TRAIT_COLDBLOODED" = TRAIT_COLDBLOODED,
"TRAIT_NONATURALHEAL" = TRAIT_NONATURALHEAL,
Expand Down
20 changes: 13 additions & 7 deletions code/_onclick/item_attack.dm
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,10 @@
* * damage_multiplier - what to multiply the damage by
*/
/obj/item/proc/attack(mob/living/M, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1)
if(SEND_SIGNAL(src, COMSIG_ITEM_ATTACK, M, user) & COMPONENT_ITEM_NO_ATTACK)
var/mob/living/target = M
if(SEND_SIGNAL(src, COMSIG_ITEM_ATTACK, target, user) & COMPONENT_ITEM_NO_ATTACK)
return
SEND_SIGNAL(user, COMSIG_MOB_ITEM_ATTACK, M, user)
SEND_SIGNAL(user, COMSIG_MOB_ITEM_ATTACK, target, user)
if(item_flags & NOBLUDGEON)
return
if(force && damtype != STAMINA && HAS_TRAIT(user, TRAIT_PACIFISM))
Expand Down Expand Up @@ -124,13 +125,18 @@
else if(hitsound)
playsound(loc, hitsound, get_clamped_volume(), 1, -1)

M.lastattacker = user.real_name
M.lastattackerckey = user.ckey
target.lastattacker = user.real_name
target.lastattackerckey = user.ckey

user.do_attack_animation(M)
M.attacked_by(src, user, attackchain_flags, damage_multiplier, damage_addition = force_modifier)
user.do_attack_animation(target)

log_combat(user, M, "attacked", src.name, "(INTENT: [uppertext(user.a_intent)]) (DAMTYPE: [uppertext(damtype)])")
if(prob(user.get_luck_critfail_chance())) //S.P.E.C.I.A.L.
target = user
user.visible_message(span_warning("Critical fail! [user] tries to attack [M], but hits [user.p_them()]self instead!"))

log_combat(user, M, "attacked", src.name, "(INTENT: [uppertext(user.a_intent)]) (DAMTYPE: [uppertext(damtype)])[M != target ? "(Critfail: hit [target] instead)" : ""]")

target.attacked_by(src, user, attackchain_flags, damage_multiplier, damage_addition = force_modifier)
add_fingerprint(user)

//the equivalent of the standard version of attack() but for object targets.
Expand Down
13 changes: 13 additions & 0 deletions code/controllers/subsystem/job.dm
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ SUBSYSTEM_DEF(job)
var/datum/job/job = GetJob(rank)
if(!job)
return FALSE
if(job.special_stat_check(player.client?.prefs))
return FALSE
if(jobban_isbanned(player, rank) || QDELETED(player))
return FALSE
if(!job.player_old_enough(player.client))
Expand All @@ -126,6 +128,9 @@ SUBSYSTEM_DEF(job)
JobDebug("Running FOC, Job: [job], Level: [level], Flag: [flag]")
var/list/candidates = list()
for(var/mob/dead/new_player/player in unassigned)
if(job.special_stat_check(player.client?.prefs))
JobDebug("FOC special stat failed, player: [player]")
continue
if(jobban_isbanned(player, job.title) || QDELETED(player))
JobDebug("FOC isbanned failed, Player: [player]")
continue
Expand Down Expand Up @@ -165,6 +170,10 @@ SUBSYSTEM_DEF(job)
if((job.title in GLOB.faction_whitelist_positions) && (CONFIG_GET(flag/use_role_whitelist))) //If you want a whitelist position, get a whitelist and choose it.
continue

if(job.special_stat_check(player.client?.prefs))
JobDebug("GRJ special stat failed, player: [player]")
continue

if(jobban_isbanned(player, job.title) || QDELETED(player))
if(QDELETED(player))
JobDebug("GRJ isbanned failed, Player deleted")
Expand Down Expand Up @@ -346,6 +355,10 @@ SUBSYSTEM_DEF(job)
if(!job)
continue

if(job.special_stat_check(player.client?.prefs))
JobDebug("DO special stat failed, player: [player]")
continue

if(jobban_isbanned(player, job.title))
JobDebug("DO isbanned failed, Player: [player], Job:[job.title]")
continue
Expand Down
71 changes: 53 additions & 18 deletions code/modules/client/preferences.dm
Original file line number Diff line number Diff line change
Expand Up @@ -205,13 +205,13 @@ GLOBAL_LIST_EMPTY(preferences_datums)

var/hide_ckey = FALSE //pref for hiding if your ckey shows round-end or not

var/special_s = 5
var/special_p = 5
var/special_e = 5
var/special_c = 5
var/special_i = 5
var/special_a = 5
var/special_l = 5
var/special_s = SPECIAL_DEFAULT_ATTR_VALUE
var/special_p = SPECIAL_DEFAULT_ATTR_VALUE
var/special_e = SPECIAL_DEFAULT_ATTR_VALUE
var/special_c = SPECIAL_DEFAULT_ATTR_VALUE
var/special_i = SPECIAL_DEFAULT_ATTR_VALUE
var/special_a = SPECIAL_DEFAULT_ATTR_VALUE
var/special_l = SPECIAL_DEFAULT_ATTR_VALUE

/// Associative list: matchmaking_prefs[/datum/matchmaking_pref subtype] -> number of desired matches
var/list/matchmaking_prefs = list()
Expand Down Expand Up @@ -929,6 +929,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(jobban_isbanned(user, rank))
HTML += "<font color=red>[rank]</font></td><td><a href='?_src_=prefs;bancheck=[rank]'> BANNED</a></td></tr>"
continue
if(job.special_stat_check(src))
HTML += "<font color=red>[rank]</font></td><td><font color=red>\[SPECIAL [job.special_stat_check(src)]\]</font></td></tr>"
continue
var/required_playtime_remaining = job.required_playtime_remaining(user.client)
if(required_playtime_remaining)
HTML += "<font color=red>[rank]</font></td><td><font color=red> \[ [get_exp_format(required_playtime_remaining)] as [job.get_exp_req_type()] \] </font></td></tr>"
Expand Down Expand Up @@ -1150,21 +1153,28 @@ GLOBAL_LIST_EMPTY(preferences_datums)

dat += "<center><b>Allocate points</b></center>"
dat += "<center>Note: SPECIAL is functional here. These points have an effect on gameplay.</center><br>"
dat += "<center>[total] out of 35 possible</center><br>"
dat += "<center>[total] out of [SPECIAL_MAX_POINT_SUM_CAP] possible</center><br>"
dat += "<b>Strength :</b> <a href='?_src_=prefs;preference=special_s;task=input'>[special_s]</a><BR>"
dat += "<font size='1'><i>Strength affects the amount of melee damage you dish out.</i></font><br>"
dat += "<b>Perception :</b> <a href='?_src_=prefs;preference=special_p;task=input'>[special_p]</a><BR>"
dat += "<font size='1'><i>Perception affects how well you can fire ranged weapons.</i></font><br>"
dat += "<b>Endurance :</b> <a href='?_src_=prefs;preference=special_e;task=input'>[special_e]</a><BR>"
dat += "<font size='1'>Endurance affects your maximum health & resistance to fire and poisons.<i></i></font><br>"
dat += "<b>Charisma :</b> <a href='?_src_=prefs;preference=special_c;task=input'>[special_c]</a><BR>"
dat += "<font size='1'><i>Charisma determines your character's examine text, leadership & ability to manipulate others.</i></font><br>"
dat += "<b>Intelligence:</b> <a href='?_src_=prefs;preference=special_i;task=input'>[special_i]</a><BR>"
dat += "<font size='1'><i>Intelligence is necessary for being able to craft various recipes, as well as the operation of energy weapons.</i></font><br>"
dat += "<b>Agility :</b> <a href='?_src_=prefs;preference=special_a;task=input'>[special_a]</a><BR>"
dat += "<font size='1'><i>Agility determines your move speed and how much stamina you use while sprinting.</i></font><br>"
dat += "<b>Luck :</b> <a href='?_src_=prefs;preference=special_l;task=input'>[special_l]</a><BR>"
if (total>35)
dat += "<font size='1'><i>Luck does stuff!</i></font><br>"
if (total > SPECIAL_MAX_POINT_SUM_CAP)
dat += "<center>Maximum exceeded, please change until your total is at or below 35<center>"
else
dat += "<center><a href='?_src_=prefs;preference=special;task=close'>Done</a></center>"

user << browse(null, "window=preferences")
var/datum/browser/popup = new(user, "mob_occupation", "<div align='center'>S.P.E.C.I.A.L</div>", 300, 400) //no reason not to reuse the occupation window, as it's cleaner that way
var/datum/browser/popup = new(user, "mob_occupation", "<div align='center'>S.P.E.C.I.A.L</div>", 400, 550) //no reason not to reuse the occupation window, as it's cleaner that way
popup.set_window_options("can_close=0")
popup.set_content(dat.Join())
popup.open(0)
Expand Down Expand Up @@ -1347,43 +1357,43 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if("special_s")
var/new_point = input(user, "Choose Amount(1-10)", "Strength") as num|null
if(new_point)
special_s = max(min(round(text2num(new_point)), 10),1)
special_s = clamp(round(new_point), SPECIAL_MIN_ATTR_VALUE, SPECIAL_MAX_ATTR_VALUE)
SetSpecial(user)
return 1
if("special_p")
var/new_point = input(user, "Choose Amount(1-10)", "Perception") as num|null
if(new_point)
special_p = max(min(round(text2num(new_point)), 10),1)
special_p = clamp(round(new_point), SPECIAL_MIN_ATTR_VALUE, SPECIAL_MAX_ATTR_VALUE)
SetSpecial(user)
return 1
if("special_e")
var/new_point = input(user, "Choose Amount(1-10)", "Endurance") as num|null
if(new_point)
special_e = max(min(round(text2num(new_point)), 10),1)
special_e = clamp(round(new_point), SPECIAL_MIN_ATTR_VALUE, SPECIAL_MAX_ATTR_VALUE)
SetSpecial(user)
return 1
if("special_c")
var/new_point = input(user, "Choose Amount(1-10)", "Charisma") as num|null
if(new_point)
special_c = max(min(round(text2num(new_point)), 10),1)
special_c = clamp(round(new_point), SPECIAL_MIN_ATTR_VALUE, SPECIAL_MAX_ATTR_VALUE)
SetSpecial(user)
return 1
if("special_i")
var/new_point = input(user, "Choose Amount(1-10)", "Intelligence") as num|null
if(new_point)
special_i = max(min(round(text2num(new_point)), 10),1)
special_i = clamp(round(new_point), SPECIAL_MIN_ATTR_VALUE, SPECIAL_MAX_ATTR_VALUE)
SetSpecial(user)
return 1
if("special_a")
var/new_point = input(user, "Choose Amount(1-10)", "Agility") as num|null
if(new_point)
special_a = max(min(round(text2num(new_point)), 10),1)
special_a = clamp(round(new_point), SPECIAL_MIN_ATTR_VALUE, SPECIAL_MAX_ATTR_VALUE)
SetSpecial(user)
return 1
if("special_l")
var/new_point = input(user, "Choose Amount(1-10)", "Luck") as num|null
if(new_point)
special_l = max(min(round(text2num(new_point)), 10),1)
special_l = clamp(round(new_point), SPECIAL_MIN_ATTR_VALUE, SPECIAL_MAX_ATTR_VALUE)
SetSpecial(user)
return 1
if("ghostform")
Expand Down Expand Up @@ -2127,7 +2137,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)

character.gender = gender
character.age = age
//special stuff
//S.P.E.C.I.A.L.
fix_special_values()
character.special_s = special_s
character.special_p = special_p
character.special_e = special_e
Expand Down Expand Up @@ -2348,6 +2359,30 @@ GLOBAL_LIST_EMPTY(preferences_datums)
else
to_chat(parent, span_userdanger("Something went wrong! Your quirks have been reset, and you'll need to set up your quirks again."))

/datum/preferences/proc/clamp_special_values() // S.P.E.C.I.A.L.
special_s = clamp(special_s, SPECIAL_MIN_ATTR_VALUE, SPECIAL_MAX_ATTR_VALUE)
special_p = clamp(special_p, SPECIAL_MIN_ATTR_VALUE, SPECIAL_MAX_ATTR_VALUE)
special_e = clamp(special_e, SPECIAL_MIN_ATTR_VALUE, SPECIAL_MAX_ATTR_VALUE)
special_c = clamp(special_c, SPECIAL_MIN_ATTR_VALUE, SPECIAL_MAX_ATTR_VALUE)
special_i = clamp(special_i, SPECIAL_MIN_ATTR_VALUE, SPECIAL_MAX_ATTR_VALUE)
special_a = clamp(special_a, SPECIAL_MIN_ATTR_VALUE, SPECIAL_MAX_ATTR_VALUE)
special_l = clamp(special_l, SPECIAL_MIN_ATTR_VALUE, SPECIAL_MAX_ATTR_VALUE)

/datum/preferences/proc/reset_special_values() // S.P.E.C.I.A.L.
special_s = SPECIAL_DEFAULT_ATTR_VALUE
special_p = SPECIAL_DEFAULT_ATTR_VALUE
special_e = SPECIAL_DEFAULT_ATTR_VALUE
special_c = SPECIAL_DEFAULT_ATTR_VALUE
special_i = SPECIAL_DEFAULT_ATTR_VALUE
special_a = SPECIAL_DEFAULT_ATTR_VALUE
special_l = SPECIAL_DEFAULT_ATTR_VALUE

/datum/preferences/proc/fix_special_values() // S.P.E.C.I.A.L.
clamp_special_values()
var/sum = special_s + special_p + special_e + special_c + special_i + special_a + special_l
if(sum > SPECIAL_MAX_POINT_SUM_CAP)
reset_special_values()


#undef DEFAULT_SLOT_AMT
#undef HANDS_SLOT_AMT
Expand Down
21 changes: 12 additions & 9 deletions code/modules/client/preferences_savefile.dm
Original file line number Diff line number Diff line change
Expand Up @@ -519,7 +519,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
loadout_data = safe_json_decode(S["loadout"])
else
loadout_data = list()
//special

//S.P.E.C.I.A.L.
S["special_s"] >> special_s
S["special_p"] >> special_p
S["special_e"] >> special_e
Expand Down Expand Up @@ -567,13 +568,14 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
socks = sanitize_inlist(socks, GLOB.socks_list)
socks_color = sanitize_hexcolor(socks_color, 6, FALSE, initial(socks_color))
age = sanitize_integer(age, AGE_MIN, AGE_MAX, initial(age))
special_s = sanitize_integer(special_s, 1, 10, initial(special_s))
special_p = sanitize_integer(special_p, 1, 10, initial(special_p))
special_e = sanitize_integer(special_e, 1, 10, initial(special_e))
special_c = sanitize_integer(special_c, 1, 10, initial(special_c))
special_i = sanitize_integer(special_i, 1, 10, initial(special_i))
special_a = sanitize_integer(special_a, 1, 10, initial(special_a))
special_l = sanitize_integer(special_l, 1, 10, initial(special_l))
special_s = sanitize_integer(special_s, SPECIAL_MIN_ATTR_VALUE, SPECIAL_MAX_ATTR_VALUE, initial(special_s))
special_p = sanitize_integer(special_p, SPECIAL_MIN_ATTR_VALUE, SPECIAL_MAX_ATTR_VALUE, initial(special_p))
special_e = sanitize_integer(special_e, SPECIAL_MIN_ATTR_VALUE, SPECIAL_MAX_ATTR_VALUE, initial(special_e))
special_c = sanitize_integer(special_c, SPECIAL_MIN_ATTR_VALUE, SPECIAL_MAX_ATTR_VALUE, initial(special_c))
special_i = sanitize_integer(special_i, SPECIAL_MIN_ATTR_VALUE, SPECIAL_MAX_ATTR_VALUE, initial(special_i))
special_a = sanitize_integer(special_a, SPECIAL_MIN_ATTR_VALUE, SPECIAL_MAX_ATTR_VALUE, initial(special_a))
special_l = sanitize_integer(special_l, SPECIAL_MIN_ATTR_VALUE, SPECIAL_MAX_ATTR_VALUE, initial(special_l))
fix_special_values()

hair_color = sanitize_hexcolor(hair_color, 6, FALSE)
facial_hair_color = sanitize_hexcolor(facial_hair_color, 6, FALSE)
Expand Down Expand Up @@ -705,7 +707,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car

WRITE_FILE(S["feature_taste"], features["taste"])

//special
//S.P.E.C.I.A.L.
fix_special_values()
WRITE_FILE(S["special_s"] ,special_s)
WRITE_FILE(S["special_p"] ,special_p)
WRITE_FILE(S["special_e"] ,special_e)
Expand Down
60 changes: 60 additions & 0 deletions code/modules/jobs/job_types/_job.dm
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,33 @@
/// the value should be something like "strings/names/cow.txt"
var/whitelist_path

var/list/min_required_special

/* //!Template for pasting into job defines, leave only needed fields:
min_required_special = list(
"special_s" = 0,
"special_p" = 0,
"special_e" = 0,
"special_c" = 0,
"special_i" = 0,
"special_a" = 0,
"special_l" = 0,
)
*/

var/list/modify_special

/* //!Template for pasting into job defines, leave only needed fields:
modify_special = list(
"special_s" = 0,
"special_p" = 0,
"special_e" = 0,
"special_c" = 0,
"special_i" = 0,
"special_a" = 0,
"special_l" = 0,
)
*/

/datum/job/proc/after_spawn(mob/living/spawner, mob/client_holder, latejoin = FALSE)
SHOULD_CALL_PARENT(TRUE)
Expand Down Expand Up @@ -170,6 +197,15 @@
//Equip the rest of the gear
H.dna.species.before_equip_job(src, H, visualsOnly)

if(islist(modify_special))
H.special_s = clamp(H.special_s + modify_special["special_s"], SPECIAL_MIN_ATTR_VALUE, SPECIAL_MAX_ATTR_VALUE)
H.special_p = clamp(H.special_p + modify_special["special_p"], SPECIAL_MIN_ATTR_VALUE, SPECIAL_MAX_ATTR_VALUE)
H.special_e = clamp(H.special_e + modify_special["special_e"], SPECIAL_MIN_ATTR_VALUE, SPECIAL_MAX_ATTR_VALUE)
H.special_c = clamp(H.special_c + modify_special["special_c"], SPECIAL_MIN_ATTR_VALUE, SPECIAL_MAX_ATTR_VALUE)
H.special_i = clamp(H.special_i + modify_special["special_i"], SPECIAL_MIN_ATTR_VALUE, SPECIAL_MAX_ATTR_VALUE)
H.special_a = clamp(H.special_a + modify_special["special_a"], SPECIAL_MIN_ATTR_VALUE, SPECIAL_MAX_ATTR_VALUE)
H.special_l = clamp(H.special_l + modify_special["special_l"], SPECIAL_MIN_ATTR_VALUE, SPECIAL_MAX_ATTR_VALUE)

var/datum/outfit/job/O = outfit_override || outfit
if(O)
H.equipOutfit(O, visualsOnly, preference_source) //mob doesn't have a client yet.
Expand Down Expand Up @@ -383,3 +419,27 @@
if(CONFIG_GET(flag/security_has_maint_access))
return list(ACCESS_MAINT_TUNNELS)
return list()

/datum/job/proc/special_stat_check(datum/preferences/prefs)
var/output = ""
var/sum = prefs.special_s + prefs.special_p + prefs.special_e + prefs.special_c + prefs.special_i + prefs.special_a + prefs.special_l
if(sum > SPECIAL_MAX_POINT_SUM_CAP)
return "\[SPECIAL CAP\]"
if(islist(min_required_special))
if(min_required_special["special_s"] && min_required_special["special_s"] > prefs.special_s)
output += "S: [min_required_special["special_s"]], "
if(min_required_special["special_p"] && min_required_special["special_p"] > prefs.special_p)
output += "P: [min_required_special["special_p"]], "
if(min_required_special["special_e"] && min_required_special["special_e"] > prefs.special_e)
output += "E: [min_required_special["special_e"]], "
if(min_required_special["special_c"] && min_required_special["special_c"] > prefs.special_c)
output += "C: [min_required_special["special_c"]], "
if(min_required_special["special_i"] && min_required_special["special_i"] > prefs.special_i)
output += "I: [min_required_special["special_i"]], "
if(min_required_special["special_a"] && min_required_special["special_a"] > prefs.special_a)
output += "A: [min_required_special["special_a"]], "
if(min_required_special["special_l"] && min_required_special["special_l"] > prefs.special_l)
output += "L: [min_required_special["special_l"]], "
if(length(output))
output = copytext_char(output, 1, length(output) - 1)
return output
Loading

0 comments on commit 6b84c4b

Please sign in to comment.