From 6b84c4b75436389211a10858811cf3843fa8a43a Mon Sep 17 00:00:00 2001
From: Tsurupeta <41485301+Tsurupeta@users.noreply.github.com>
Date: Wed, 24 Apr 2024 21:31:31 +0200
Subject: [PATCH] S.P.E.C.I.A.L. pt. 2 (#97)
---
code/__DEFINES/jobs.dm | 1 +
code/__DEFINES/special.dm | 6 +
code/__DEFINES/traits.dm | 2 +
code/_globalvars/traits.dm | 2 +
code/_onclick/item_attack.dm | 20 ++-
code/controllers/subsystem/job.dm | 13 ++
code/modules/client/preferences.dm | 71 +++++++---
code/modules/client/preferences_savefile.dm | 21 +--
code/modules/jobs/job_types/_job.dm | 60 +++++++++
code/modules/jobs/job_types/bos.dm | 12 ++
code/modules/jobs/job_types/legion.dm | 16 +++
code/modules/jobs/job_types/ncr.dm | 12 ++
.../modules/mob/dead/new_player/new_player.dm | 5 +-
.../mob/living/carbon/human/species.dm | 4 +
code/modules/mob/living/say.dm | 17 ++-
code/modules/mob/mob_helpers.dm | 20 +++
code/modules/mob/special_stats.dm | 85 +++++++++---
code/modules/projectiles/gun.dm | 4 +
fallout/obj/stack/f13Cash.dm | 122 +++++++-----------
fallout/obj/trash_stack.dm | 7 +-
hailmary.dme | 1 +
21 files changed, 362 insertions(+), 139 deletions(-)
create mode 100644 code/__DEFINES/special.dm
diff --git a/code/__DEFINES/jobs.dm b/code/__DEFINES/jobs.dm
index 3134f59c3ed..eac863962db 100644
--- a/code/__DEFINES/jobs.dm
+++ b/code/__DEFINES/jobs.dm
@@ -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"
diff --git a/code/__DEFINES/special.dm b/code/__DEFINES/special.dm
new file mode 100644
index 00000000000..0c5ec2e973d
--- /dev/null
+++ b/code/__DEFINES/special.dm
@@ -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
diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index 1d75692464b..5fe0346d075 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -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"
diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm
index a3f0c3093c9..fc1a0c72700 100644
--- a/code/_globalvars/traits.dm
+++ b/code/_globalvars/traits.dm
@@ -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,
diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm
index 9f6f08575e0..7b4c728f07f 100644
--- a/code/_onclick/item_attack.dm
+++ b/code/_onclick/item_attack.dm
@@ -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))
@@ -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.
diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm
index d939e993f7f..31c89b56a09 100644
--- a/code/controllers/subsystem/job.dm
+++ b/code/controllers/subsystem/job.dm
@@ -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))
@@ -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
@@ -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")
@@ -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
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index fdfb884b582..9844724508c 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -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()
@@ -929,6 +929,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(jobban_isbanned(user, rank))
HTML += "[rank]
BANNED | "
continue
+ if(job.special_stat_check(src))
+ HTML += "[rank]\[SPECIAL [job.special_stat_check(src)]\] | "
+ continue
var/required_playtime_remaining = job.required_playtime_remaining(user.client)
if(required_playtime_remaining)
HTML += "[rank] \[ [get_exp_format(required_playtime_remaining)] as [job.get_exp_req_type()] \] | "
@@ -1150,21 +1153,28 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "Allocate points"
dat += "Note: SPECIAL is functional here. These points have an effect on gameplay.
"
- dat += "[total] out of 35 possible
"
+ dat += "[total] out of [SPECIAL_MAX_POINT_SUM_CAP] possible
"
dat += "Strength : [special_s]
"
+ dat += "Strength affects the amount of melee damage you dish out.
"
dat += "Perception : [special_p]
"
+ dat += "Perception affects how well you can fire ranged weapons.
"
dat += "Endurance : [special_e]
"
+ dat += "Endurance affects your maximum health & resistance to fire and poisons.
"
dat += "Charisma : [special_c]
"
+ dat += "Charisma determines your character's examine text, leadership & ability to manipulate others.
"
dat += "Intelligence: [special_i]
"
+ dat += "Intelligence is necessary for being able to craft various recipes, as well as the operation of energy weapons.
"
dat += "Agility : [special_a]
"
+ dat += "Agility determines your move speed and how much stamina you use while sprinting.
"
dat += "Luck : [special_l]
"
- if (total>35)
+ dat += "Luck does stuff!
"
+ if (total > SPECIAL_MAX_POINT_SUM_CAP)
dat += "Maximum exceeded, please change until your total is at or below 35"
else
dat += "Done"
user << browse(null, "window=preferences")
- var/datum/browser/popup = new(user, "mob_occupation", "S.P.E.C.I.A.L
", 300, 400) //no reason not to reuse the occupation window, as it's cleaner that way
+ var/datum/browser/popup = new(user, "mob_occupation", "S.P.E.C.I.A.L
", 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)
@@ -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")
@@ -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
@@ -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
diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm
index a96b80e2471..167c6109bc2 100644
--- a/code/modules/client/preferences_savefile.dm
+++ b/code/modules/client/preferences_savefile.dm
@@ -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
@@ -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)
@@ -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)
diff --git a/code/modules/jobs/job_types/_job.dm b/code/modules/jobs/job_types/_job.dm
index d582c1dc816..90893f0dccb 100644
--- a/code/modules/jobs/job_types/_job.dm
+++ b/code/modules/jobs/job_types/_job.dm
@@ -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)
@@ -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.
@@ -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
diff --git a/code/modules/jobs/job_types/bos.dm b/code/modules/jobs/job_types/bos.dm
index 9ebd1cd295e..92323cfc067 100644
--- a/code/modules/jobs/job_types/bos.dm
+++ b/code/modules/jobs/job_types/bos.dm
@@ -173,6 +173,10 @@ Paladin Commander
/datum/outfit/loadout/sentmini //Minigun
)
+ min_required_special = list(
+ "special_c" = 4,
+ )
+
access = list(ACCESS_BROTHERHOOD_COMMAND, ACCESS_ROBOTICS, ACCESS_BOS, ACCESS_ENGINE_EQUIP, ACCESS_ENGINE, ACCESS_HYDROPONICS, ACCESS_KITCHEN, ACCESS_BAR, ACCESS_SEC_DOORS, ACCESS_CHANGE_IDS)
minimal_access = list(ACCESS_BROTHERHOOD_COMMAND, ACCESS_ROBOTICS, ACCESS_BOS, ACCESS_ENGINE_EQUIP, ACCESS_ENGINE, ACCESS_HYDROPONICS, ACCESS_KITCHEN, ACCESS_BAR, ACCESS_SEC_DOORS, ACCESS_CHANGE_IDS)
@@ -328,6 +332,10 @@ Knight-Captain
access = list(ACCESS_BROTHERHOOD_COMMAND, ACCESS_ROBOTICS, ACCESS_BOS, ACCESS_ENGINE_EQUIP, ACCESS_ENGINE, ACCESS_HYDROPONICS, ACCESS_KITCHEN, ACCESS_BAR, ACCESS_SEC_DOORS, ACCESS_ARMORY, ACCESS_BRIG, ACCESS_CHANGE_IDS)
minimal_access = list(ACCESS_BROTHERHOOD_COMMAND, ACCESS_ROBOTICS, ACCESS_BOS, ACCESS_ENGINE_EQUIP, ACCESS_ENGINE, ACCESS_HYDROPONICS, ACCESS_KITCHEN, ACCESS_BAR, ACCESS_SEC_DOORS, ACCESS_ARMORY, ACCESS_BRIG, ACCESS_CHANGE_IDS)
+ min_required_special = list(
+ "special_c" = 4,
+ )
+
/datum/outfit/job/bos/f13knightcap/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
..()
if(visualsOnly)
@@ -462,6 +470,10 @@ Paladin
),
)
+ min_required_special = list(
+ "special_c" = 4,
+ )
+
/datum/outfit/job/bos/f13paladin/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
..()
if(visualsOnly)
diff --git a/code/modules/jobs/job_types/legion.dm b/code/modules/jobs/job_types/legion.dm
index e2a00095466..bc1b670b319 100644
--- a/code/modules/jobs/job_types/legion.dm
+++ b/code/modules/jobs/job_types/legion.dm
@@ -211,6 +211,10 @@ Weapons Lever shotgun, Grease gun, Repeater carbines, Revolvers, simple guns al
/datum/outfit/loadout/centurion, // 14mm Pistol + Unique Lever-Action
)
+ min_required_special = list(
+ "special_c" = 4,
+ )
+
/datum/outfit/job/CaesarsLegion/Legionnaire/f13centurion/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
..()
if(visualsOnly)
@@ -304,6 +308,10 @@ Weapons Lever shotgun, Grease gun, Repeater carbines, Revolvers, simple guns al
/datum/outfit/loadout/decvetbrave, // Lever-action Shotgun, 44 revolver, ballistic fist
)
+ min_required_special = list(
+ "special_c" = 4,
+ )
+
/datum/outfit/job/CaesarsLegion/Legionnaire/f13decanvet/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
..()
@@ -409,6 +417,10 @@ Weapons Lever shotgun, Grease gun, Repeater carbines, Revolvers, simple guns al
),
)
+ min_required_special = list(
+ "special_c" = 4,
+ )
+
/datum/outfit/job/CaesarsLegion/Legionnaire/f13decan/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
..()
if(visualsOnly)
@@ -500,6 +512,10 @@ Weapons Lever shotgun, Grease gun, Repeater carbines, Revolvers, simple guns al
),
)
+ min_required_special = list(
+ "special_c" = 4,
+ )
+
/datum/outfit/job/CaesarsLegion/Legionnaire/f13decanrec/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
..()
if(visualsOnly)
diff --git a/code/modules/jobs/job_types/ncr.dm b/code/modules/jobs/job_types/ncr.dm
index 03baab73c79..7acc497a010 100644
--- a/code/modules/jobs/job_types/ncr.dm
+++ b/code/modules/jobs/job_types/ncr.dm
@@ -156,6 +156,10 @@ Weapons Service Rifle, Grease Gun, 9mm pistol, all good.
/datum/outfit/loadout/captainfrontline, // AR Service Rifle
)
+ min_required_special = list(
+ "special_c" = 4,
+ )
+
/datum/outfit/job/ncr/f13captain/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
..()
if(visualsOnly)
@@ -236,6 +240,10 @@ Weapons Service Rifle, Grease Gun, 9mm pistol, all good.
),
)
+ min_required_special = list(
+ "special_c" = 4,
+ )
+
/datum/outfit/job/ncr/f13lieutenant // AR Service Rifle, Binoculars, Bayonet, M1911 custom
name = "NCR Lieutenant"
jobtype = /datum/job/ncr/f13lieutenant
@@ -536,6 +544,10 @@ Weapons Service Rifle, Grease Gun, 9mm pistol, all good.
/datum/outfit/loadout/vrcqc // 2 x .45 Long colt revolvers
)
+ min_required_special = list(
+ "special_c" = 4,
+ )
+
/datum/outfit/job/ncr/f13vetranger/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
..()
if(visualsOnly)
diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm
index 7a912937859..c1c3e4b742a 100644
--- a/code/modules/mob/dead/new_player/new_player.dm
+++ b/code/modules/mob/dead/new_player/new_player.dm
@@ -405,6 +405,8 @@
return "Your species cannot play as a [jobtitle]."
if(JOB_UNAVAILABLE_WHITELIST)
return "[jobtitle] requires a whitelist."
+ if(JOB_UNAVAILABLE_SPECIAL)
+ return "[jobtitle] requires certain SPECIAL stats high enough."
return "Error: Unknown job availability."
/mob/dead/new_player/proc/IsJobUnavailable(rank, latejoin = FALSE)
@@ -422,6 +424,8 @@
return JOB_UNAVAILABLE_SLOTFULL
if(jobban_isbanned(src,rank))
return JOB_UNAVAILABLE_BANNED
+ if(job.special_stat_check(client?.prefs))
+ return JOB_UNAVAILABLE_SPECIAL
if(QDELETED(src))
return JOB_UNAVAILABLE_GENERIC
if(!job.player_old_enough(client))
@@ -717,4 +721,3 @@
// Add verb for re-opening the interview panel, and re-init the verbs for the stat panel
add_verb(src, /mob/dead/new_player/proc/open_interview)
-
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index c9deed02029..00b196460f0 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -1395,6 +1395,10 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
target_message = span_warning("[target] blocks your attack!"))
return FALSE
+ if(prob(user.get_luck_critfail_chance())) //S.P.E.C.I.A.L.
+ user.visible_message(span_warning("Critical fail! [user] tries to attack [target], but hits [user.p_them()]self instead!"))
+ target = user
+
if(!(attackchain_flags & ATTACK_IS_PARRY_COUNTERATTACK))
if(HAS_TRAIT(user, TRAIT_PUGILIST))//CITADEL CHANGE - makes punching cause staminaloss but funny martial artist types get a discount
user.adjustStaminaLossBuffered(1.5)
diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm
index 3ea764984ec..86aa15f698e 100644
--- a/code/modules/mob/living/say.dm
+++ b/code/modules/mob/living/say.dm
@@ -22,12 +22,12 @@
/mob/living/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, just_chat)
/* var/static/list/crit_allowed_modes = list(
- MODE_WHISPER = TRUE,
- MODE_CUSTOM_SAY = TRUE,
- MODE_SING = TRUE,
- MODE_HEADSET = TRUE,
- MODE_ROBOT = TRUE,
- MODE_CHANGELING = TRUE,
+ MODE_WHISPER = TRUE,
+ MODE_CUSTOM_SAY = TRUE,
+ MODE_SING = TRUE,
+ MODE_HEADSET = TRUE,
+ MODE_ROBOT = TRUE,
+ MODE_CHANGELING = TRUE,
MODE_ALIEN = TRUE
) */
var/static/list/unconscious_allowed_modes = list(MODE_CHANGELING = TRUE, MODE_ALIEN = TRUE)
@@ -316,7 +316,7 @@
if(derpspeech)
message = derpspeech(message, stuttering)
- if(stuttering)
+ if(stuttering || HAS_TRAIT(src, TRAIT_SAY_STUTTERING))
message = stutter(message)
if(slurring)
@@ -328,6 +328,9 @@
if(clockcultslurring)
message = CLOCK_CULT_SLUR(message)
+ if(HAS_TRAIT(src, TRAIT_SAY_LISPING))
+ message = lisp_replace(message)
+
var/end_char = copytext(message, length(message), length(message) + 1)
if(!(end_char in list(".", "?", "!", "-", "~", ",", "_", "+", "|", "*")))
message += "."
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index ad353c1fb20..16dc963488c 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -210,6 +210,26 @@
. += letter
return sanitize(.)
+/proc/lisp_replace(message)
+ var/static/regex/replace_s = new("s+h?h?", "g")
+ var/static/regex/replace_S = new("S+H?H?", "g")
+ var/static/regex/replace_z = new("z+h?h?", "g")
+ var/static/regex/replace_Z = new("Z+H?H?", "g")
+ var/static/regex/replace_x = new("x+h?h?", "g")
+ var/static/regex/replace_X = new("X+H?H?", "g")
+ var/static/regex/replace_ceci = new("ceh?|cih?", "g")
+ var/static/regex/replace_CECI = new("CEH?|CIH?", "g")
+ if(message[1] != "*")
+ message = replace_s.Replace(message, "th")
+ message = replace_S.Replace(message, "TH")
+ message = replace_z.Replace(message, "th")
+ message = replace_Z.Replace(message, "TH")
+ message = replace_ceci.Replace(message, "th")
+ message = replace_CECI.Replace(message, "TH")
+ message = replace_x.Replace(message, "ckth")
+ message = replace_X.Replace(message, "CKTH")
+ return message
+
/*
The difference with stutter is that this proc can stutter more than 1 letter
The issue here is that anything that does not have a space is treated as one word (in many instances). For instance, "LOOKING," is a word, including the comma.
diff --git a/code/modules/mob/special_stats.dm b/code/modules/mob/special_stats.dm
index 3eab666f135..5727860e98a 100644
--- a/code/modules/mob/special_stats.dm
+++ b/code/modules/mob/special_stats.dm
@@ -1,18 +1,12 @@
-//////////////////////////////////////////////////////
-////////////////////SUBTLE COMMAND////////////////////
-//////////////////////////////////////////////////////
-
-#define DEFAULT_SPECIAL_ATTR_VALUE 5
-#define MIN_INT_CRAFTING_REQUIREMENT 3
/mob
var/flavor_text = "" //tired of fucking double checking this
- var/special_s = DEFAULT_SPECIAL_ATTR_VALUE // +/-2 dmg in melee for each level above/below 5 ST
- var/special_p = DEFAULT_SPECIAL_ATTR_VALUE // +/- 5 degrees of innate gun spread for each level below/above 5 PR
- var/special_e = DEFAULT_SPECIAL_ATTR_VALUE // +/-5 maxHealth for each level above/below 5 END
- var/special_c = DEFAULT_SPECIAL_ATTR_VALUE // Desc message + moodlets
- var/special_i = DEFAULT_SPECIAL_ATTR_VALUE // Can't craft with INT under MIN_INT_CRAFTING_REQUIREMENT, certain recipes can be INT locked, certain guns can be INT locked
- var/special_a = DEFAULT_SPECIAL_ATTR_VALUE // +/- 10% Sprint stamina usage modifier -/+ 0.05 movespeed modifier per lvl below/above 5 AGI
- var/special_l = DEFAULT_SPECIAL_ATTR_VALUE // Currently nothing
+ var/special_s = SPECIAL_DEFAULT_ATTR_VALUE // +/-2 dmg in melee for each level above/below 5 ST
+ var/special_p = SPECIAL_DEFAULT_ATTR_VALUE // +/- 5 degrees of innate gun spread for each level below/above 5 PR
+ var/special_e = SPECIAL_DEFAULT_ATTR_VALUE // +/-5 maxHealth for each level above/below 5 END
+ var/special_c = SPECIAL_DEFAULT_ATTR_VALUE // Desc message + moodlets
+ var/special_i = SPECIAL_DEFAULT_ATTR_VALUE // Can't craft with INT under SPECIAL_MIN_INT_CRAFTING_REQUIREMENT, certain recipes can be INT locked, certain guns can be INT locked
+ var/special_a = SPECIAL_DEFAULT_ATTR_VALUE // +/- 10% Sprint stamina usage modifier -/+ 0.05 movespeed modifier per lvl below/above 5 AGI
+ var/special_l = SPECIAL_DEFAULT_ATTR_VALUE // Currently nothing
/mob/proc/get_top_level_mob()
if(istype(src.loc,/mob)&&src.loc!=src)
@@ -61,20 +55,20 @@ proc/get_top_level_mob(mob/S)
/// STRENGTH
/obj/item/proc/calc_melee_dam_mod_from_special(mob/living/user)
- return ((user.special_s - DEFAULT_SPECIAL_ATTR_VALUE) * 2)
+ return ((user.special_s - SPECIAL_DEFAULT_ATTR_VALUE) * 2)
/datum/species/proc/calc_unarmed_dam_mod_from_special(mob/living/user)
- return ((user.special_s - DEFAULT_SPECIAL_ATTR_VALUE) * 2)
+ return ((user.special_s - SPECIAL_DEFAULT_ATTR_VALUE) * 2)
/// PERCEPTION
/obj/item/ammo_casing/proc/calc_bullet_spread_mod_from_special(mob/living/user)
- return ((user.special_p - DEFAULT_SPECIAL_ATTR_VALUE) * 5) // +/- 5 degrees of innate spread per lvl
+ return ((user.special_p - SPECIAL_DEFAULT_ATTR_VALUE) * 5) // +/- 5 degrees of innate spread per lvl
/// ENDURANCE
/mob/living/carbon/human/initialize_special_endurance()
- maxHealth = initial(maxHealth) + ((special_e - DEFAULT_SPECIAL_ATTR_VALUE) * 5)
+ maxHealth = initial(maxHealth) + ((special_e - SPECIAL_DEFAULT_ATTR_VALUE) * 5)
health = maxHealth
/datum/species/proc/get_special_burn_resist_multiplier(mob/living/user)
@@ -130,12 +124,27 @@ proc/get_top_level_mob(mob/S)
/mob/living/carbon/human/initialize_special_charisma()
RegisterSignal(src, COMSIG_PARENT_EXAMINE, PROC_REF(handle_special_charisma_examine_moodlet), TRUE)
+ initialize_charisma_traits(src)
/mob/living/carbon/human/Destroy()
UnregisterSignal(src, COMSIG_PARENT_EXAMINE)
return ..()
+/mob/proc/initialize_charisma_traits(mob/living/carbon/user)
+ if(HAS_TRAIT_FROM(user, TRAIT_SAY_STUTTERING, "charisma"))
+ REMOVE_TRAIT(user, TRAIT_SAY_STUTTERING, "charisma")
+ if(HAS_TRAIT_FROM(user, TRAIT_SAY_LISPING, "charisma"))
+ REMOVE_TRAIT(user, TRAIT_SAY_LISPING, "charisma")
+ switch(special_c)
+ if(3)
+ ADD_TRAIT(user, TRAIT_SAY_STUTTERING, "charisma")
+ if(2)
+ ADD_TRAIT(user, TRAIT_SAY_LISPING, "charisma")
+ if(1)
+ ADD_TRAIT(user, TRAIT_SAY_STUTTERING, "charisma")
+ ADD_TRAIT(user, TRAIT_SAY_LISPING, "charisma")
+
/mob/proc/handle_special_charisma_examine_moodlet(mob/living/examinee, mob/living/examiner, text)
if(!istype(examiner))
return
@@ -182,7 +191,7 @@ proc/get_top_level_mob(mob/S)
/datum/component/personal_crafting/proc/special_crafting_check(mob/living/user)
if(!istype(user))
return FALSE
- if(user.special_i <= MIN_INT_CRAFTING_REQUIREMENT)
+ if(user.special_i <= SPECIAL_MIN_INT_CRAFTING_REQUIREMENT)
to_chat(user, "Your brain is too dumb to craft items.")
return FALSE
return TRUE
@@ -199,13 +208,49 @@ proc/get_top_level_mob(mob/S)
return
/mob/living/carbon/calc_sprint_stamina_mod_from_special()
- return (1 - ((special_a - DEFAULT_SPECIAL_ATTR_VALUE) * 0.1))
+ return (1 - ((special_a - SPECIAL_DEFAULT_ATTR_VALUE) * 0.1))
/mob/proc/calc_movespeed_mod_from_special()
- return -((special_a - DEFAULT_SPECIAL_ATTR_VALUE) * 0.05)
+ return -((special_a - SPECIAL_DEFAULT_ATTR_VALUE) * 0.05)
/// LUCK
+/// Currently affects only money from trashpiles
+/mob/proc/get_luck_loot_amt_multiplier()
+ switch(special_l)
+ if(1)
+ return 0.5
+ if(2)
+ return 0.625
+ if(3)
+ return 0.750
+ if(4)
+ return 0.875
+ if(5)
+ return 1
+ if(6)
+ return 1.1
+ if(7)
+ return 1.2
+ if(8)
+ return 1.3
+ if(9)
+ return 1.4
+ if(10)
+ return 1.5
+ return 1
+
+
+/// Chance to drop a gun or hit yourself in melee
+/mob/proc/get_luck_critfail_chance()
+ switch(special_l)
+ if(1)
+ return 10
+ if(2)
+ return 4
+ if(3)
+ return 1
+ return 0
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index 3973012fe2f..ed44788dedd 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -564,6 +564,10 @@ ATTACHMENTS
if(time_till_draw)
to_chat(user, "You're still drawing your [src]! It'll take another [time_till_draw*0.1] seconds until it's ready!")
return
+ if(prob(user.get_luck_critfail_chance()))
+ user.drop_all_held_items()
+ user.visible_message(span_warning("Critical fail! [user] accidentally drops [p_their()] gun!"))
+ return
if(pre_fire(user, target, params, zone_override, stam_cost))
return TRUE // pre_fire will handle what comes next~ (like firing at your mouse cursor after a delay)
firing = TRUE
diff --git a/fallout/obj/stack/f13Cash.dm b/fallout/obj/stack/f13Cash.dm
index 36d43882256..d591ea02b61 100644
--- a/fallout/obj/stack/f13Cash.dm
+++ b/fallout/obj/stack/f13Cash.dm
@@ -9,31 +9,24 @@
/* value of coins to spawn, use as-is for caps */
/* LOW_MIN / AUR = amount in AUR */
-// A low value cash spawn is on average worth 25
-#define LOW_MIN 7
-#define LOW_MAX 19
+/// Average amount of caps spawned from trash pile
+#define AVG_BASE 13 / CASH_CAP
-// A medium value cash spawn is on average worth 60ish
-#define MED_MIN 20
-#define MED_MAX 35
+// A low value cash spawn
+#define CASH_MULT_LOW 1
+// A medium value cash spawn
+#define CASH_MULT_MED 2.1
-// A high value cash spawn is on average worth 280
-#define HIGH_MIN 36
-#define HIGH_MAX 45
+// A high value cash spawn
+#define CASH_MULT_HIGH 3.1
-
-// Bad Pebbles fix to NCR money fudgery
-#define TEMP3_MIN 0
-#define TEMP3_MAX 0
-#define TEMP_MIN 0
-#define TEMP_MAX 0
-#define TEMP2_MIN 0
-#define TEMP2_MAX 0
+/// round(AVG * CASH_MULT) +- AVG_SPREAD
+#define AVG_SPEAD 6 / CASH_CAP
// The Bankers Vault-Stash, done like this make it so it only spawns on his person to stop metarushing. Average 8500.
-#define BANKER_MIN 2000
-#define BANKER_MAX 15000
+#define AVG_BANKER 8500 / CASH_CAP
+#define BANKER_SPREAD 6500 / CASH_CAP
/obj/item/stack/f13Cash //DO NOT USE THIS
name = "bottle cap"
@@ -141,20 +134,25 @@
/obj/item/stack/f13Cash/random
var/money_type = /obj/item/stack/f13Cash/caps
- var/min_qty = LOW_MIN
- var/max_qty = LOW_MAX
+ var/average_qty = AVG_BASE
+ var/average_spread = AVG_SPEAD
+ var/average_mult = CASH_MULT_LOW
var/spawn_nothing_chance = 0 //chance no money at all spawns
-/obj/item/stack/f13Cash/random/Initialize()
+/obj/item/stack/f13Cash/random/Initialize(mapload, new_amount, merge = TRUE, amt_multiplier = 0) //S.P.E.C.I.A.L.
..()
+ amt_multiplier = max(amt_multiplier, 0)
+ if(amt_multiplier)
+ average_mult *= amt_multiplier
if(!prob(spawn_nothing_chance))
spawn_money()
return INITIALIZE_HINT_QDEL
/obj/item/stack/f13Cash/random/proc/spawn_money()
+ var/spawn_amount = round(average_qty * average_mult)
var/obj/item/stack/f13Cash/stack = new money_type
stack.loc = loc
- stack.amount = round(rand(min_qty, max_qty))
+ stack.amount = round(rand(round(spawn_amount - average_spread), round(spawn_amount + average_spread)))
stack.update_icon()
/* we have 6 icons, so we will use our own, instead of stack's */
@@ -174,8 +172,7 @@
icon_state = "[initial(icon_state)]6"
/obj/item/stack/f13Cash/random/low
- min_qty = LOW_MIN / CASH_CAP
- max_qty = LOW_MAX / CASH_CAP
+ average_mult = CASH_MULT_LOW
/obj/item/stack/f13Cash/random/low/lowchance
spawn_nothing_chance = 75
@@ -184,16 +181,14 @@
spawn_nothing_chance = 50
/obj/item/stack/f13Cash/random/med
- min_qty = MED_MIN / CASH_CAP
- max_qty = MED_MAX / CASH_CAP
+ average_mult = CASH_MULT_MED
/obj/item/stack/f13Cash/random/high
- min_qty = HIGH_MIN / CASH_CAP
- max_qty = HIGH_MAX / CASH_CAP
+ average_mult = CASH_MULT_HIGH
/obj/item/stack/f13Cash/random/banker
- min_qty = BANKER_MIN / CASH_CAP
- max_qty = BANKER_MAX / CASH_CAP
+ average_qty = AVG_BANKER
+ average_spread = BANKER_SPREAD
/obj/item/stack/f13Cash/denarius
name = "Denarius"
@@ -208,31 +203,27 @@
merge_type = /obj/item/stack/f13Cash/denarius
/obj/item/stack/f13Cash/random/denarius
+ average_qty = AVG_BASE / CASH_DEN
+ average_spread = AVG_SPEAD / CASH_DEN
money_type = /obj/item/stack/f13Cash/denarius
/obj/item/stack/f13Cash/random/denarius/low
- min_qty = LOW_MIN / CASH_DEN
- max_qty = LOW_MAX / CASH_DEN
+ average_mult = CASH_MULT_LOW
/obj/item/stack/f13Cash/random/denarius/med
- min_qty = MED_MIN / CASH_DEN
- max_qty = MED_MAX / CASH_DEN
+ average_mult = CASH_MULT_MED
/obj/item/stack/f13Cash/random/denarius/high
- min_qty = HIGH_MIN / CASH_DEN
- max_qty = HIGH_MAX / CASH_DEN
+ average_mult = CASH_MULT_HIGH
/obj/item/stack/f13Cash/random/denarius/legionpay_basic
- min_qty = LOW_MIN / CASH_DEN
- max_qty = LOW_MAX / CASH_DEN
+ average_mult = CASH_MULT_LOW
/obj/item/stack/f13Cash/random/denarius/legionpay_veteran
- min_qty = MED_MIN / CASH_DEN
- max_qty = MED_MAX / CASH_DEN
+ average_mult = CASH_MULT_MED
/obj/item/stack/f13Cash/random/denarius/legionpay_officer
- min_qty = HIGH_MIN / CASH_DEN
- max_qty = HIGH_MAX / CASH_DEN
+ average_mult = CASH_MULT_HIGH
/obj/item/stack/f13Cash/aureus
name = "Aureus"
@@ -247,19 +238,15 @@
merge_type = /obj/item/stack/f13Cash/aureus
/obj/item/stack/f13Cash/random/aureus
+ average_qty = 0
+ average_spread = 0
money_type = /obj/item/stack/f13Cash/aureus
/obj/item/stack/f13Cash/random/aureus/low
- min_qty = 0
- max_qty = 0
/obj/item/stack/f13Cash/random/aureus/med
- min_qty = 0
- max_qty = 0
/obj/item/stack/f13Cash/random/aureus/high
- min_qty = 0
- max_qty = 0 //uses flat values because aurei are worth so much
/obj/item/stack/f13Cash/ncr
name = "NCR Dollar"
@@ -290,30 +277,29 @@
/obj/item/stack/f13Cash/random/ncr
money_type = /obj/item/stack/f13Cash/ncr
+ average_qty = AVG_BASE / CASH_NCR
+ average_spread = AVG_SPEAD / CASH_NCR
/obj/item/stack/f13Cash/random/ncr/low
- min_qty = TEMP3_MIN / CASH_NCR
- max_qty = TEMP3_MAX / CASH_NCR
+ average_qty = 0
+ average_spread = 0
/obj/item/stack/f13Cash/random/ncr/med
- min_qty = TEMP_MIN / CASH_NCR
- max_qty = TEMP_MAX / CASH_NCR
+ average_qty = 0
+ average_spread = 0
/obj/item/stack/f13Cash/random/ncr/high
- min_qty = TEMP2_MIN / CASH_NCR
- max_qty = TEMP2_MAX / CASH_NCR
+ average_qty = 0
+ average_spread = 0
/obj/item/stack/f13Cash/random/ncr/ncrpay_basic
- min_qty = LOW_MIN / CASH_NCR
- max_qty = LOW_MAX / CASH_NCR
+ average_mult = CASH_MULT_LOW
/obj/item/stack/f13Cash/random/ncr/ncrpay_veteran
- min_qty = MED_MIN / CASH_NCR
- max_qty = MED_MAX / CASH_NCR
+ average_mult = CASH_MULT_MED
/obj/item/stack/f13Cash/random/ncr/ncrpay_officer
- min_qty = HIGH_MIN / CASH_NCR
- max_qty = HIGH_MAX / CASH_NCR
+ average_mult = CASH_MULT_HIGH
#undef maxCoinIcon
@@ -321,17 +307,3 @@
#undef CASH_AUR
#undef CASH_DEN
#undef CASH_NCR
-#undef LOW_MIN
-#undef LOW_MAX
-#undef MED_MIN
-#undef MED_MAX
-#undef HIGH_MIN
-#undef HIGH_MAX
-#undef BANKER_MIN
-#undef BANKER_MAX
-#undef TEMP3_MIN
-#undef TEMP3_MAX
-#undef TEMP_MIN
-#undef TEMP_MAX
-#undef TEMP2_MIN
-#undef TEMP2_MAX
diff --git a/fallout/obj/trash_stack.dm b/fallout/obj/trash_stack.dm
index 502eda862ee..fa64a5956f5 100644
--- a/fallout/obj/trash_stack.dm
+++ b/fallout/obj/trash_stack.dm
@@ -38,7 +38,11 @@
//var/itemtypebonus= pickweight(lootable_trash)
if(itemtype)
to_chat(user, span_notice("You scavenge through [src]."))
- var/atom/newthing = new itemtype(ST)
+ var/atom/newthing
+ if(ispath(itemtype, /obj/item/stack/f13Cash/random)) // SHITCODE TIME (I can't imagine any clever solution right now at 2 AM) ~Tsuru
+ newthing = new itemtype(ST, null, TRUE, user.get_luck_loot_amt_multiplier())
+ else
+ newthing = new itemtype(ST)
//if (prob(10+(user.special_l*3.5)))//SPECIAL Integration
// to_chat(user, span_notice("You get lucky and find even more loot!"))
// var/obj/item/bonusitem = new itemtypebonus(ST)
@@ -113,4 +117,3 @@
for(var/i in garbage_list)
for(var/ii in i)
lootable_trash += ii
-
diff --git a/hailmary.dme b/hailmary.dme
index 70bab8a3920..9d10ff0b136 100644
--- a/hailmary.dme
+++ b/hailmary.dme
@@ -118,6 +118,7 @@
#include "code\__DEFINES\sound.dm"
#include "code\__DEFINES\spaceman_dmm.dm"
#include "code\__DEFINES\span.dm"
+#include "code\__DEFINES\special.dm"
#include "code\__DEFINES\stat.dm"
#include "code\__DEFINES\stat_tracking.dm"
#include "code\__DEFINES\statpanel.dm"