diff --git a/_maps/map_files/PubbyStation/job_changes.dm b/_maps/map_files/PubbyStation/job_changes.dm
deleted file mode 100644
index 726601725b..0000000000
--- a/_maps/map_files/PubbyStation/job_changes.dm
+++ /dev/null
@@ -1,20 +0,0 @@
-#define JOB_MODIFICATION_MAP_NAME "PubbyStation"
-
-/datum/job/hos/New()
- ..()
- MAP_JOB_CHECK
- access += ACCESS_CREMATORIUM
- minimal_access += ACCESS_CREMATORIUM
-
-/datum/job/warden/New()
- ..()
- MAP_JOB_CHECK
- access += ACCESS_CREMATORIUM
- minimal_access += ACCESS_CREMATORIUM
-
-/datum/job/officer/New()
- ..()
- MAP_JOB_CHECK
- access += ACCESS_CREMATORIUM
- minimal_access += ACCESS_CREMATORIUM
-
diff --git a/_maps/pubbystation.json b/_maps/pubbystation.json
index f99cca57c5..acf49b773b 100644
--- a/_maps/pubbystation.json
+++ b/_maps/pubbystation.json
@@ -7,5 +7,10 @@
"whiteship": "whiteship_pubby",
"ferry": "ferry_fancy",
"cargo": "cargo_box"
- }
+ },
+ "job_access_add": {
+ "/datum/job/hos": [27],
+ "/datum/job/officer": [27],
+ "/datum/job/detective": [27]
+ }
}
diff --git a/code/__DEFINES/_flags/item_flags.dm b/code/__DEFINES/_flags/item_flags.dm
index 26e764c45b..7da71e22cb 100644
--- a/code/__DEFINES/_flags/item_flags.dm
+++ b/code/__DEFINES/_flags/item_flags.dm
@@ -22,12 +22,10 @@
#define SURGICAL_TOOL (1<<10)
///Can be worn on certain slots (currently belt and id) that would otherwise require an uniform.
#define NO_UNIFORM_REQUIRED (1<<11)
-///Damage when attacking people is not affected by combat mode.
-#define NO_COMBAT_MODE_FORCE_MODIFIER (1<<12)
/// This item can be used to parry. Only a basic check used to determine if we should proceed with parry chain at all.
-#define ITEM_CAN_PARRY (1<<13)
+#define ITEM_CAN_PARRY (1<<12)
/// This item can be used in the directional blocking system. Only a basic check used to determine if we should proceed with directional block handling at all.
-#define ITEM_CAN_BLOCK (1<<14)
+#define ITEM_CAN_BLOCK (1<<13)
// Flags for the clothing_flags var on /obj/item/clothing
diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm
index eff5d9eca9..5f5a76b58d 100644
--- a/code/__DEFINES/combat.dm
+++ b/code/__DEFINES/combat.dm
@@ -34,6 +34,8 @@
#define COMBAT_FLAGS_DEFAULT (COMBAT_FLAG_PARRY_CAPABLE | COMBAT_FLAG_BLOCK_CAPABLE)
/// Default combat flags for everyone else (so literally everyone but humans).
#define COMBAT_FLAGS_SPRINT_EXEMPT (COMBAT_FLAG_SPRINT_ACTIVE | COMBAT_FLAG_SPRINT_TOGGLED | COMBAT_FLAG_SPRINT_FORCED | COMBAT_FLAG_PARRY_CAPABLE | COMBAT_FLAG_BLOCK_CAPABLE)
+/// Default combat flags for those in stamina combat system
+#define COMBAT_FLAGS_STAMINA_COMBAT (COMBAT_FLAG_PARRY_CAPABLE | COMBAT_FLAG_BLOCK_CAPABLE | COMBAT_FLAG_STAMINA_BUFFER)
/// The user wants sprint mode on
#define COMBAT_FLAG_SPRINT_TOGGLED (1<<0)
@@ -47,8 +49,8 @@
#define COMBAT_FLAG_RESISTING_REST (1<<4)
/// Intentionally resting
#define COMBAT_FLAG_INTENTIONALLY_RESTING (1<<5)
-/// Currently stamcritted but not as violently
-#define COMBAT_FLAG_SOFT_STAMCRIT (1<<6)
+/// This mob requires stamina buffer to do things that require stamina buffer. Not having this exempts the mob from stamina combat.
+#define COMBAT_FLAG_STAMINA_BUFFER (1<<6)
/// Force sprint mode on at all times, overrides everything including sprint disable traits.
#define COMBAT_FLAG_SPRINT_FORCED (1<<7)
/// This mob is capable of using the active parrying system.
@@ -64,26 +66,17 @@
// Helpers for getting someone's stamcrit state. Cast to living.
#define NOT_STAMCRIT 0
-#define SOFT_STAMCRIT 1
-#define HARD_STAMCRIT 2
+#define HARD_STAMCRIT 1
// Stamcrit check helpers
#define IS_STAMCRIT(mob) (CHECK_STAMCRIT(mob) != NOT_STAMCRIT)
-#define CHECK_STAMCRIT(mob) ((mob.combat_flags & COMBAT_FLAG_HARD_STAMCRIT)? HARD_STAMCRIT : ((mob.combat_flags & COMBAT_FLAG_SOFT_STAMCRIT)? SOFT_STAMCRIT : NOT_STAMCRIT))
+#define CHECK_STAMCRIT(mob) (mob.combat_flags & COMBAT_FLAG_HARD_STAMCRIT)
//stamina stuff
-///Threshold over which attacks start being hindered.
-#define STAMINA_NEAR_SOFTCRIT 90
-///softcrit for stamina damage. prevents standing up, some actions that cost stamina, etc, but doesn't force a rest or stop movement
-#define STAMINA_SOFTCRIT 100
-///sanity cap to prevent stamina actions (that are still performable) from sending you into crit.
-#define STAMINA_NEAR_CRIT 130
-///crit for stamina damage. forces a rest, and stops movement until stamina goes back to stamina softcrit
+/// crit for stamina damage. forces a rest, and stops movement until stamina goes back to stamina softcrit
#define STAMINA_CRIT 140
-///same as STAMINA_SOFTCRIT except for the more traditional health calculations
-#define STAMINA_SOFTCRIT_TRADITIONAL 0
-///ditto, but for STAMINA_CRIT
-#define STAMINA_CRIT_TRADITIONAL -40
+/// Threshold under for which you are unable to draw from stamina health to replace stamina buffer
+#define STAMINA_NO_OVERDRAW_THRESHOLD 100
#define CRAWLUNDER_DELAY 30 //Delay for crawling under a standing mob
@@ -252,36 +245,6 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list(
//We will round to this value in damage calculations.
#define DAMAGE_PRECISION 0.01
-//items total mass, used to calculate their attacks' stamina costs. If not defined, the cost will be (w_class * 1.25)
-#define TOTAL_MASS_TINY_ITEM 1.25
-#define TOTAL_MASS_SMALL_ITEM 2.5
-#define TOTAL_MASS_NORMAL_ITEM 3.75
-#define TOTAL_MASS_BULKY_ITEM 5
-#define TOTAL_MASS_HUGE_ITEM 6.25
-#define TOTAL_MASS_GIGANTIC_ITEM 7.5
-
-#define TOTAL_MASS_HAND_REPLACEMENT 5 //standard punching stamina cost. most hand replacements are huge items anyway.
-#define TOTAL_MASS_MEDIEVAL_WEAPON 3.6 //very, very generic average sword/warpick/etc. weight in pounds.
-#define TOTAL_MASS_TOY_SWORD 1.5
-
-//stamina cost defines.
-#define STAM_COST_ATTACK_OBJ_MULT 1.2
-#define STAM_COST_ATTACK_MOB_MULT 1
-#define STAM_COST_BATON_MOB_MULT 1
-#define STAM_COST_NO_COMBAT_MULT 1.25
-#define STAM_COST_W_CLASS_MULT 1.25
-#define STAM_COST_THROW_MULT 2
-#define STAM_COST_THROW_MOB 2.5 //multiplied by (mob size + 1)^2.
-
-///Multiplier of the (STAMINA_NEAR_CRIT - user current stamina loss) : (STAMINA_NEAR_CRIT - STAMINA_SOFTCRIT) ratio used in damage penalties when stam soft-critted.
-#define STAM_CRIT_ITEM_ATTACK_PENALTY 0.66
-/// changeNext_move penalty multiplier of the above.
-#define STAM_CRIT_ITEM_ATTACK_DELAY 1.75
-/// Damage penalty when fighting prone.
-#define LYING_DAMAGE_PENALTY 0.7
-/// Added delay when firing guns stam-softcritted. Summed with a hardset CLICK_CD_RANGE delay, similar to STAM_CRIT_DAMAGE_DELAY otherwise.
-#define STAM_CRIT_GUN_DELAY 2.75
-
//stamina recovery defines. Blocked if combat mode is on.
#define STAM_RECOVERY_STAM_CRIT -7.5
#define STAM_RECOVERY_RESTING -6
diff --git a/code/__DEFINES/combat/stamina_combat.dm b/code/__DEFINES/combat/stamina_combat.dm
new file mode 100644
index 0000000000..1f781ca3e0
--- /dev/null
+++ b/code/__DEFINES/combat/stamina_combat.dm
@@ -0,0 +1,35 @@
+// Stamina Buffer
+
+// Standard amounts for stamina usage
+
+// Multipliers
+/// Base stamina cost for an item of a certain w_class without total_mass set.
+#define STAM_COST_W_CLASS_MULT 1.25
+
+// Flat amounts
+/// Usage for eyestabbing with a screwdriver
+#define STAMINA_COST_ITEM_EYESTAB 7.5
+/// Usage for shoving yourself off the ground instantly
+#define STAMINA_COST_SHOVE_UP 10
+
+//items total mass, used to calculate their attacks' stamina costs. If not defined, the cost will be (w_class * 1.25)
+#define TOTAL_MASS_TINY_ITEM 1.25
+#define TOTAL_MASS_SMALL_ITEM 2.5
+#define TOTAL_MASS_NORMAL_ITEM 3.75
+#define TOTAL_MASS_BULKY_ITEM 5
+#define TOTAL_MASS_HUGE_ITEM 6.25
+#define TOTAL_MASS_GIGANTIC_ITEM 7.5
+
+#define TOTAL_MASS_HAND_REPLACEMENT 5 //standard punching stamina cost. most hand replacements are huge items anyway.
+#define TOTAL_MASS_MEDIEVAL_WEAPON 3.6 //very, very generic average sword/warpick/etc. weight in pounds.
+#define TOTAL_MASS_TOY_SWORD 1.5
+
+//stamina cost defines.
+#define STAM_COST_ATTACK_OBJ_MULT 0.75
+#define STAM_COST_ATTACK_MOB_MULT 1
+#define STAM_COST_BATON_MOB_MULT 0.85
+#define STAM_COST_THROW_MULT 0.75
+#define STAM_COST_THROW_MOB 1.25 //multiplied by (mob size + 1)^2.
+
+/// Damage penalty when fighting prone.
+#define LYING_DAMAGE_PENALTY 0.7
diff --git a/code/__DEFINES/configuration.dm b/code/__DEFINES/configuration.dm
index 20c1df2ec7..0428a16828 100644
--- a/code/__DEFINES/configuration.dm
+++ b/code/__DEFINES/configuration.dm
@@ -3,6 +3,8 @@
#define CONFIG_SET(X, Y) global.config.Set(/datum/config_entry/##X, ##Y)
/// Gets the datum of the object, for when editing a const define.
#define CONFIG_GET_ENTRY(X) global.config.GetEntryDatum(/datum/config_entry/##X)
+/// Caches an entry in the proc
+#define CONFIG_CACHE_ENTRY_AND_FETCH_VALUE(X, varname) var/static/datum/config_entry/##X/entrycache_##varname;if(!entrycache_##varname){entrycache_##varname=CONFIG_GET_ENTRY(##X);};var/##varname=entrycache_##varname.config_entry_value
#define CONFIG_MAPS_FILE "maps.txt"
diff --git a/code/__DEFINES/maps.dm b/code/__DEFINES/maps.dm
index c5c2dd58c2..854b6c9dcf 100644
--- a/code/__DEFINES/maps.dm
+++ b/code/__DEFINES/maps.dm
@@ -20,11 +20,6 @@ Multi-Z stations are supported and multi-Z mining and away missions would
require only minor tweaks.
*/
-// helpers for modifying jobs, used in various job_changes.dm files
-#define MAP_JOB_CHECK if(SSmapping.config.map_name != JOB_MODIFICATION_MAP_NAME) { return; }
-#define MAP_JOB_CHECK_BASE if(SSmapping.config.map_name != JOB_MODIFICATION_MAP_NAME) { return ..(); }
-#define MAP_REMOVE_JOB(jobpath) /datum/job/##jobpath/map_check() { return (SSmapping.config.map_name != JOB_MODIFICATION_MAP_NAME) && ..() }
-
#define SPACERUIN_MAP_EDGE_PAD 15
// traits
diff --git a/code/__DEFINES/skills/defines.dm b/code/__DEFINES/skills/defines.dm
index 47aaeeb1dc..a0b34895df 100644
--- a/code/__DEFINES/skills/defines.dm
+++ b/code/__DEFINES/skills/defines.dm
@@ -40,7 +40,6 @@
#define SKILL_TRAIN_ATTACK_OBJ "train_attack_obj"
#define SKILL_STAMINA_COST "stamina_cost" //Influences the stamina cost from weapon usage.
#define SKILL_THROW_STAM_COST "throw_stam_cost"
-#define SKILL_COMBAT_MODE "combat_mode" //The user must have combat mode on.
#define SKILL_SANITY "sanity" //Is the skill affected by (in)sanity.
#define SKILL_INTELLIGENCE "intelligence" //Is the skill affected by brain damage?
diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index a5190af23a..520f205381 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -208,6 +208,10 @@
#define TRAIT_FAST_PUMP "fast_pump"
#define TRAIT_NO_PROCESS_FOOD "no-process-food" // You don't get benefits from nutriment, nor nutrition from reagent consumables
#define TRAIT_NICE_SHOT "nice_shot" //hnnnnnnnggggg..... you're pretty good...
+/// Prevents stamina buffer regeneration
+#define TRAIT_NO_STAMINA_BUFFER_REGENERATION "block_stamina_buffer_regen"
+/// Prevents stamina regeneration
+#define TRAIT_NO_STAMINA_REGENERATION "block_stamina_regen"
// mobility flag traits
// IN THE FUTURE, IT WOULD BE NICE TO DO SOMETHING SIMILAR TO https://github.com/tgstation/tgstation/pull/48923/files (ofcourse not nearly the same because I have my.. thoughts on it)
diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm
index 85f5e4fd9b..b558b28cf2 100644
--- a/code/_globalvars/bitfields.dm
+++ b/code/_globalvars/bitfields.dm
@@ -252,7 +252,6 @@ GLOBAL_LIST_INIT(bitfields, list(
"COMBAT_FLAG_SPRINT_ACTIVE" = COMBAT_FLAG_SPRINT_ACTIVE,
"COMBAT_FLAG_ATTEMPTING_CRAWL" = COMBAT_FLAG_ATTEMPTING_CRAWL,
"COMBAT_FLAG_HARD_STAMCRIT" = COMBAT_FLAG_HARD_STAMCRIT,
- "COMBAT_FLAG_SOFT_STAMCRIT" = COMBAT_FLAG_SOFT_STAMCRIT,
"COMBAT_FLAG_INTENTIONALLY_RESTING" = COMBAT_FLAG_INTENTIONALLY_RESTING,
"COMBAT_FLAG_RESISTING_REST" = COMBAT_FLAG_RESISTING_REST,
"COMBAT_FLAG_SPRINT_FORCED" = COMBAT_FLAG_SPRINT_FORCED
diff --git a/code/_globalvars/lists/mobs.dm b/code/_globalvars/lists/mobs.dm
index 134f9d9cbe..64bac1f9af 100644
--- a/code/_globalvars/lists/mobs.dm
+++ b/code/_globalvars/lists/mobs.dm
@@ -40,6 +40,7 @@ GLOBAL_LIST_EMPTY(sentient_disease_instances)
GLOBAL_LIST_EMPTY(latejoin_ai_cores)
GLOBAL_LIST_EMPTY(mob_config_movespeed_type_lookup)
+GLOBAL_LIST_EMPTY(mob_config_movespeed_type_lookup_floating)
GLOBAL_LIST_EMPTY(latejoiners) //CIT CHANGE - All latejoining people, for traitor-target purposes.
@@ -47,14 +48,22 @@ GLOBAL_LIST_EMPTY(latejoiners) //CIT CHANGE - All latejoining people, for traito
// NOTE: This is entirely based on the fact that byond typesof/subtypesof gets longer/deeper paths before shallower ones.
// If that ever breaks this entire proc breaks.
var/list/mob_types = typesof(/mob)
- var/list/entry_value = CONFIG_GET(keyed_list/multiplicative_movespeed)
+ var/list/mob_types_floating = typesof(/mob)
+ var/list/entry_value = CONFIG_GET(keyed_list/multiplicative_movespeed/normal)
+ var/list/entry_value_floating = CONFIG_GET(keyed_list/multiplicative_movespeed/floating)
var/list/configured_types = list()
+ var/list/configured_types_floating = list()
for(var/path in entry_value)
var/value = entry_value[path]
if(isnull(value))
continue
// associative list sets for elements that already exist preserve order
mob_types[path] = value
+ for(var/path in entry_value_floating)
+ var/value = entry_value_floating[path]
+ if(isnull(value))
+ continue
+ mob_types_floating[path] = value
// now go back up through it to set everything, making absolute sure that base paths are overridden by child paths all the way down the path tree.
for(var/i in length(mob_types) to 1 step -1)
var/path = mob_types[i]
@@ -63,7 +72,14 @@ GLOBAL_LIST_EMPTY(latejoiners) //CIT CHANGE - All latejoining people, for traito
// we're going from bottom to top so it should be safe to do this without further checks..
for(var/subpath in typesof(path))
configured_types[subpath] = mob_types[path]
+ for(var/i in length(mob_types_floating) to 1 step -1)
+ var/path = mob_types_floating[i]
+ if(isnull(mob_types_floating[path]))
+ continue
+ for(var/subpath in typesof(path))
+ configured_types_floating[subpath] = mob_types_floating[path]
GLOB.mob_config_movespeed_type_lookup = configured_types
+ GLOB.mob_config_movespeed_type_lookup_floating = configured_types_floating
if(update_mobs)
update_mob_config_movespeeds()
diff --git a/code/_onclick/hud/screen_objects/stamina.dm b/code/_onclick/hud/screen_objects/stamina.dm
index 5484014f8f..98a387d32d 100644
--- a/code/_onclick/hud/screen_objects/stamina.dm
+++ b/code/_onclick/hud/screen_objects/stamina.dm
@@ -11,7 +11,9 @@
/obj/screen/staminas/Click(location,control,params)
if(isliving(usr))
var/mob/living/L = usr
- to_chat(L, "You have [L.getStaminaLoss()] stamina loss.
Your stamina buffer can take [L.stambuffer] stamina loss, and recharges at no cost.
Your stamina buffer is [(L.stambuffer*(100/L.stambuffer))-(L.bufferedstam*(100/L.stambuffer))]% full.")
+ CONFIG_CACHE_ENTRY_AND_FETCH_VALUE(number/stamina_combat/buffer_max, buffer_max)
+ to_chat(L, "You have [L.getStaminaLoss()] stamina loss.
\
+
Your stamina buffer is [round((L.stamina_buffer / buffer_max) * 100, 0.1)]% full.")
/obj/screen/staminas/update_icon_state()
var/mob/living/carbon/user = hud?.mymob
@@ -24,6 +26,19 @@
else
icon_state = "stamina[clamp(FLOOR(user.getStaminaLoss() /20, 1), 0, 6)]"
+/obj/screen/staminas/update_overlays()
+ . = ..()
+ var/mob/living/carbon/user = hud?.mymob
+ if(!user)
+ return
+ var/percent = user.getStaminaLoss() / STAMINA_CRIT
+ if((user.stat == DEAD) || (user.combat_flags & COMBAT_FLAG_HARD_STAMCRIT) || (user.hal_screwyhud in 1 to 2))
+ . += list("stamina_alert3")
+ else if(percent >= 0.85)
+ . += list("stamina_alert2")
+ else if(percent >= 0.7)
+ . += list("stamina_alert1")
+
//stam buffer
/obj/screen/staminabuffer
icon = 'modular_citadel/icons/ui/screen_gen.dmi'
@@ -33,29 +48,33 @@
layer = ABOVE_HUD_LAYER + 0.1
mouse_opacity = 0
-/obj/screen/staminabuffer/update_icon_state()
+/obj/screen/staminabuffer/proc/mark_dirty()
+ if(update_to_mob())
+ START_PROCESSING(SShuds, src)
+
+/obj/screen/staminabuffer/process()
+ if(!update_to_mob())
+ return PROCESS_KILL
+
+/obj/screen/staminabuffer/Destroy()
+ STOP_PROCESSING(SShuds, src)
+ return ..()
+
+/obj/screen/staminabuffer/proc/update_to_mob()
var/mob/living/carbon/user = hud?.mymob
- if(!user)
- return
+ user.UpdateStaminaBuffer(FALSE)
+ CONFIG_CACHE_ENTRY_AND_FETCH_VALUE(number/stamina_combat/buffer_max, buffer_max)
+ if(!user?.client)
+ return FALSE
if(user.stat == DEAD || (user.combat_flags & COMBAT_FLAG_HARD_STAMCRIT) || (user.hal_screwyhud in 1 to 2))
- icon_state = "stambuffer7"
- else if(user.hal_screwyhud == 5)
icon_state = "stambuffer0"
+ return FALSE
+ else if(user.hal_screwyhud == 5)
+ icon_state = "stambuffer29"
+ return FALSE
+ else if(user.stamina_buffer >= buffer_max)
+ icon_state = "stambuffer29"
+ return FALSE
else
- switch(user.bufferedstam / user.stambuffer)
- if(0.95 to INFINITY)
- icon_state = "stambuffer7"
- if(0.9 to 0.95)
- icon_state = "stambuffer6"
- if(0.8 to 0.9)
- icon_state = "stambuffer5"
- if(0.6 to 0.8)
- icon_state = "stambuffer4"
- if(0.4 to 0.6)
- icon_state = "stambuffer3"
- if(0.2 to 0.4)
- icon_state = "stambuffer2"
- if(0.05 to 0.2)
- icon_state = "stambuffer1"
- else
- icon_state = "stambuffer0"
+ icon_state = "stambuffer[FLOOR((user.stamina_buffer / buffer_max) * 29, 1)]"
+ return TRUE
diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm
index 9e88476513..cd1ee56848 100644
--- a/code/_onclick/item_attack.dm
+++ b/code/_onclick/item_attack.dm
@@ -85,6 +85,9 @@
if(force && damtype != STAMINA && HAS_TRAIT(user, TRAIT_PACIFISM))
to_chat(user, "You don't want to harm other living beings!")
return
+
+ if(!UseStaminaBufferStandard(user, STAM_COST_ATTACK_MOB_MULT, null, TRUE))
+ return DISCARD_LAST_ACTION
if(!force)
playsound(loc, 'sound/weapons/tap.ogg', get_clamped_volume(), 1, -1)
@@ -100,10 +103,6 @@
log_combat(user, M, "attacked", src.name, "(INTENT: [uppertext(user.a_intent)]) (DAMTYPE: [uppertext(damtype)])")
add_fingerprint(user)
- var/weight = getweight(user, STAM_COST_ATTACK_MOB_MULT) //CIT CHANGE - makes attacking things cause stamina loss
- if(weight)
- user.adjustStaminaLossBuffered(weight)
-
// CIT SCREENSHAKE
if(force >= 15)
shake_camera(user, ((force - 10) * 0.01 + 1), ((force - 10) * 0.01))
@@ -120,31 +119,21 @@
if(SEND_SIGNAL(src, COMSIG_ITEM_ATTACK_OBJ, O, user) & COMPONENT_NO_ATTACK_OBJ)
return
if(item_flags & NOBLUDGEON)
- return
+ return DISCARD_LAST_ACTION
+ if(!UseStaminaBufferStandard(user, STAM_COST_ATTACK_OBJ_MULT, warn = TRUE))
+ return DISCARD_LAST_ACTION
user.do_attack_animation(O)
O.attacked_by(src, user)
- var/weight = getweight(user, STAM_COST_ATTACK_OBJ_MULT)
- if(weight)
- user.adjustStaminaLossBuffered(weight)//CIT CHANGE - makes attacking things cause stamina loss
/atom/movable/proc/attacked_by()
return
/obj/attacked_by(obj/item/I, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1)
var/totitemdamage = I.force * damage_multiplier
- var/bad_trait
-
- var/stamloss = user.getStaminaLoss()
- if(stamloss > STAMINA_NEAR_SOFTCRIT) //The more tired you are, the less damage you do.
- var/penalty = (stamloss - STAMINA_NEAR_SOFTCRIT)/(STAMINA_NEAR_CRIT - STAMINA_NEAR_SOFTCRIT)*STAM_CRIT_ITEM_ATTACK_PENALTY
- totitemdamage *= 1 - penalty
-
- if(SEND_SIGNAL(user, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE))
- bad_trait = SKILL_COMBAT_MODE //blacklist combat skills.
if(I.used_skills && user.mind)
if(totitemdamage)
- totitemdamage = user.mind.item_action_skills_mod(I, totitemdamage, I.skill_difficulty, SKILL_ATTACK_OBJ, bad_trait)
+ totitemdamage = user.mind.item_action_skills_mod(I, totitemdamage, I.skill_difficulty, SKILL_ATTACK_OBJ)
for(var/skill in I.used_skills)
if(!(SKILL_TRAIN_ATTACK_OBJ in I.used_skills[skill]))
continue
@@ -187,28 +176,15 @@
if(!.)
return
- var/stamloss = user.getStaminaLoss()
var/stam_mobility_mult = 1
- if(stamloss > STAMINA_NEAR_SOFTCRIT) //The more tired you are, the less damage you do.
- var/penalty = (stamloss - STAMINA_NEAR_SOFTCRIT)/(STAMINA_NEAR_CRIT - STAMINA_NEAR_SOFTCRIT)*STAM_CRIT_ITEM_ATTACK_PENALTY
- stam_mobility_mult -= penalty
if(stam_mobility_mult > LYING_DAMAGE_PENALTY && !CHECK_MOBILITY(user, MOBILITY_STAND)) //damage penalty for fighting prone, doesn't stack with the above.
stam_mobility_mult = LYING_DAMAGE_PENALTY
. *= stam_mobility_mult
- var/bad_trait
- if(!(I.item_flags & NO_COMBAT_MODE_FORCE_MODIFIER))
- if(SEND_SIGNAL(user, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE))
- bad_trait = SKILL_COMBAT_MODE //blacklist combat skills.
- if(SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE))
- . *= 0.8
- else if(SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE))
- . *= 1.2
-
if(!user.mind || !I.used_skills)
return
if(.)
- . = user.mind.item_action_skills_mod(I, ., I.skill_difficulty, SKILL_ATTACK_MOB, bad_trait)
+ . = user.mind.item_action_skills_mod(I, ., I.skill_difficulty, SKILL_ATTACK_MOB)
for(var/skill in I.used_skills)
if(!(SKILL_TRAIN_ATTACK_MOB in I.used_skills[skill]))
continue
@@ -263,21 +239,23 @@
. = (total_mass || w_class * STAM_COST_W_CLASS_MULT) * multiplier
if(!user)
return
- var/bad_trait
- if(SEND_SIGNAL(user, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE))
- . *= STAM_COST_NO_COMBAT_MULT
- bad_trait = SKILL_COMBAT_MODE
if(used_skills && user.mind)
- . = user.mind.item_action_skills_mod(src, ., skill_difficulty, trait, bad_trait, FALSE)
- var/total_health = user.getStaminaLoss()
- . = clamp(., 0, STAMINA_NEAR_CRIT - total_health)
+ . = user.mind.item_action_skills_mod(src, ., skill_difficulty, trait, null, FALSE)
+
+/**
+ * Uses the amount of stamina required for a standard hit
+ */
+/obj/item/proc/UseStaminaBufferStandard(mob/living/user, multiplier = 1, trait = SKILL_STAMINA_COST, warn = TRUE)
+ ASSERT(user)
+ var/cost = getweight(user, multiplier, trait)
+ return user.UseStaminaBuffer(cost, warn)
/// How long this staggers for. 0 and negatives supported.
/obj/item/proc/melee_stagger_duration(force_override)
if(!isnull(stagger_force))
return stagger_force
/// totally not an untested, arbitrary equation.
- return clamp((1.5 + (w_class/7.5)) * ((force_override || force) / 2), 0, 10 SECONDS)
+ return clamp((1.5 + (w_class/5)) * ((force_override || force) / 1.5), 0, 10 SECONDS)
/obj/item/proc/do_stagger_action(mob/living/target, mob/living/user, force_override)
if(!CHECK_BITFIELD(target.status_flags, CANSTAGGER))
diff --git a/code/controllers/configuration/entries/game_options.dm b/code/controllers/configuration/entries/game_options.dm
index 8f55293820..c91ac2e0d4 100644
--- a/code/controllers/configuration/entries/game_options.dm
+++ b/code/controllers/configuration/entries/game_options.dm
@@ -230,14 +230,6 @@
/datum/config_entry/keyed_list/multiplicative_movespeed
key_mode = KEY_MODE_TYPE
value_mode = VALUE_MODE_NUM
- config_entry_value = list( //DEFAULTS
- /mob/living/simple_animal = 1,
- /mob/living/silicon/pai = 1,
- /mob/living/carbon/alien/humanoid/sentinel = 0.25,
- /mob/living/carbon/alien/humanoid/drone = 0.5,
- /mob/living/carbon/alien/humanoid/royal/praetorian = 1,
- /mob/living/carbon/alien/humanoid/royal/queen = 3
- )
/datum/config_entry/keyed_list/multiplicative_movespeed/ValidateAndSet()
. = ..()
@@ -249,6 +241,24 @@
if(. && (var_name == NAMEOF(src, config_entry_value)))
update_config_movespeed_type_lookup(TRUE)
+/datum/config_entry/keyed_list/multiplicative_movespeed/normal
+ config_entry_value = list( //DEFAULTS
+ /mob/living/simple_animal = 1,
+ /mob/living/silicon/pai = 1,
+ /mob/living/carbon/alien/humanoid/sentinel = 0.25,
+ /mob/living/carbon/alien/humanoid/drone = 0.5,
+ /mob/living/carbon/alien/humanoid/royal/praetorian = 1,
+ /mob/living/carbon/alien/humanoid/royal/queen = 3
+ )
+
+/datum/config_entry/keyed_list/multiplicative_movespeed/floating
+ config_entry_value = list(
+ /mob/living = 0,
+ /mob/living/carbon/alien/humanoid = 0,
+ /mob/living/carbon/alien/humanoid/royal/praetorian = 0,
+ /mob/living/carbon/alien/humanoid/royal/queen = 2
+ )
+
/datum/config_entry/number/movedelay //Used for modifying movement speed for mobs.
abstract_type = /datum/config_entry/number/movedelay
integer = FALSE
@@ -280,6 +290,12 @@
/datum/config_entry/number/movedelay/sprint_speed_increase
config_entry_value = 1
+/datum/config_entry/number/movedelay/sprint_max_tiles_increase
+ config_entry_value = 5
+
+/datum/config_entry/number/movedelay/sprint_absolute_max_tiles
+ config_entry_value = 13
+
/datum/config_entry/number/movedelay/sprint_buffer_max
config_entry_value = 24
@@ -291,7 +307,7 @@
/////////////////////////////////////////////////Outdated move delay
/datum/config_entry/number/outdated_movedelay
- deprecated_by = /datum/config_entry/keyed_list/multiplicative_movespeed
+ deprecated_by = /datum/config_entry/keyed_list/multiplicative_movespeed/normal
abstract_type = /datum/config_entry/number/outdated_movedelay
var/movedelay_type
diff --git a/code/controllers/configuration/entries/stamina_combat.dm b/code/controllers/configuration/entries/stamina_combat.dm
new file mode 100644
index 0000000000..65580899f1
--- /dev/null
+++ b/code/controllers/configuration/entries/stamina_combat.dm
@@ -0,0 +1,35 @@
+/datum/config_entry/number/stamina_combat
+ integer = FALSE
+ abstract_type = /datum/config_entry/number/stamina_combat
+
+/// Maximum stamina buffer
+/datum/config_entry/number/stamina_combat/buffer_max
+ config_entry_value = 25
+
+/// Seconds until percent_regeneration_out_of_combat kicks in
+/datum/config_entry/number/stamina_combat/out_of_combat_timer
+ config_entry_value = 15
+
+/// Base regeneration per second
+/datum/config_entry/number/stamina_combat/base_regeneration
+ config_entry_value = 0.5
+
+/// Combat mode regeneration per second
+/datum/config_entry/number/stamina_combat/combat_regeneration
+ config_entry_value = 5
+
+/// After out_of_combat_timer elapses, additionally regenerate this percent of total stamina per second. Unaffected by combat mode.
+/datum/config_entry/number/stamina_combat/percent_regeneration_out_of_combat
+ config_entry_value = 30
+
+/// Seconds after an action for which your regeneration is penalized
+/datum/config_entry/number/stamina_combat/post_action_penalty_delay
+ config_entry_value = 5
+
+/// Factor to multiply by for penalizing post-action-stamina-regen
+/datum/config_entry/number/stamina_combat/post_action_penalty_factor
+ config_entry_value = 0.25
+
+/// Factor to multiply by for stamina usage past buffer into health
+/datum/config_entry/number/stamina_combat/overdraw_penalty_factor
+ config_entry_value = 1.5
diff --git a/code/controllers/subsystem/input.dm b/code/controllers/subsystem/input.dm
index 32936af9e9..bc3f6cf51b 100644
--- a/code/controllers/subsystem/input.dm
+++ b/code/controllers/subsystem/input.dm
@@ -64,10 +64,10 @@ SUBSYSTEM_DEF(input)
// Misc
macroset_classic_input["Tab"] = "\".winset \\\"mainwindow.macro=[SKIN_MACROSET_CLASSIC_HOTKEYS] map.focus=true input.background-color=[COLOR_INPUT_DISABLED]\\\"\""
macroset_classic_input["Escape"] = "\".winset \\\"input.text=\\\"\\\"\\\"\""
-
+
// FINALLY, WE CAN DO SOMETHING MORE NORMAL FOR THE SNOWFLAKE-BUT-LESS KEYSET.
-
- // HAHA - SIKE. Because of BYOND weirdness (tl;dr not specifically binding this way results in potentially duplicate chatboxes when
+
+ // HAHA - SIKE. Because of BYOND weirdness (tl;dr not specifically binding this way results in potentially duplicate chatboxes when
// conflicts occur with something like say indicator vs say), we're going to snowflake this anyways
var/list/hard_binds = list(
"O" = "ooc",
@@ -80,7 +80,7 @@ SUBSYSTEM_DEF(input)
for(var/key in hard_binds)
for(var/modifier in anti_collision_modifiers)
hard_bind_anti_collision["[modifier]+[key]"] = ".NONSENSICAL_VERB_THAT_DOES_NOTHING"
-
+
macroset_classic_hotkey = list(
"Any" = "\"KeyDown \[\[*\]\]\"",
"Any+UP" = "\"KeyUp \[\[*\]\]\"",
@@ -88,7 +88,7 @@ SUBSYSTEM_DEF(input)
"Escape" = "\".winset \\\"input.text=\\\"\\\"\\\"\"",
"Back" = "\".winset \\\"input.text=\\\"\\\"\\\"\"",
)
-
+
macroset_classic_hotkey |= hard_binds
macroset_classic_hotkey |= hard_bind_anti_collision
@@ -100,7 +100,7 @@ SUBSYSTEM_DEF(input)
"Escape" = "\".winset \\\"input.text=\\\"\\\"\\\"\"",
"Back" = "\".winset \\\"input.text=\\\"\\\"\\\"\"",
)
-
+
macroset_hotkey |= hard_binds
macroset_hotkey |= hard_bind_anti_collision
diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm
index e61a3b3854..0f936bdeff 100644
--- a/code/controllers/subsystem/job.dm
+++ b/code/controllers/subsystem/job.dm
@@ -55,9 +55,10 @@ SUBSYSTEM_DEF(job)
continue
if(!job.config_check())
continue
- if(!job.map_check()) //Even though we initialize before mapping, this is fine because the config is loaded at new
+ if(!job.map_check(SSmapping.config)) //Even though we initialize before mapping, this is fine because the config is loaded at new
testing("Removed [job.type] due to map config");
continue
+ job.process_map_overrides(SSmapping.config)
occupations += job
name_occupations[job.title] = job
type_occupations[J] = job
diff --git a/code/controllers/subsystem/npcpool.dm b/code/controllers/subsystem/npcpool.dm
index c67deaede1..c20820c092 100644
--- a/code/controllers/subsystem/npcpool.dm
+++ b/code/controllers/subsystem/npcpool.dm
@@ -5,6 +5,10 @@ SUBSYSTEM_DEF(npcpool)
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
var/list/currentrun = list()
+ /// catches sleeping
+ var/invoking = FALSE
+ /// Invoke start time
+ var/invoke_start = 0
/datum/controller/subsystem/npcpool/stat_entry(msg)
var/list/activelist = GLOB.simple_animals[AI_ON]
@@ -12,7 +16,6 @@ SUBSYSTEM_DEF(npcpool)
return ..()
/datum/controller/subsystem/npcpool/fire(resumed = FALSE)
-
if (!resumed)
var/list/activelist = GLOB.simple_animals[AI_ON]
src.currentrun = activelist.Copy()
@@ -24,12 +27,22 @@ SUBSYSTEM_DEF(npcpool)
var/mob/living/simple_animal/SA = currentrun[currentrun.len]
--currentrun.len
- if(!SA.ckey && !SA.mob_transforming)
- if(SA.stat != DEAD)
- SA.handle_automated_movement()
- if(SA.stat != DEAD)
- SA.handle_automated_action()
- if(SA.stat != DEAD)
- SA.handle_automated_speech()
+ invoking = TRUE
+ invoke_start = world.time
+ INVOKE_ASYNC(src, .proc/invoke_process, SA)
+ if(invoking)
+ stack_trace("WARNING: [SA] ([SA.type]) slept during NPCPool processing.")
+ invoking = FALSE
+
if (MC_TICK_CHECK)
return
+
+/datum/controller/subsystem/npcpool/proc/invoke_process(mob/living/simple_animal/SA)
+ if(!SA.ckey && !SA.mob_transforming)
+ if(SA.stat != DEAD)
+ SA.handle_automated_movement()
+ if(SA.stat != DEAD)
+ SA.handle_automated_action()
+ if(SA.stat != DEAD)
+ SA.handle_automated_speech()
+ invoking = FALSE
diff --git a/code/datums/martial/sleeping_carp.dm b/code/datums/martial/sleeping_carp.dm
index 7d884344f1..81b7ea0628 100644
--- a/code/datums/martial/sleeping_carp.dm
+++ b/code/datums/martial/sleeping_carp.dm
@@ -130,7 +130,7 @@
playsound(get_turf(A), pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 75, TRUE)
P.firer = A
P.setAngle(rand(0, 360))//SHING
- A.adjustStaminaLossBuffered (3) //Citadel change to stop infinite bullet sponging as you run away, but it is buffered!
+ A.adjustStaminaLoss(3)
return BULLET_ACT_FORCE_PIERCE
return BULLET_ACT_HIT
diff --git a/code/datums/weather/weather_types/ash_storm.dm b/code/datums/weather/weather_types/ash_storm.dm
index 43190ef50c..6248be0de5 100644
--- a/code/datums/weather/weather_types/ash_storm.dm
+++ b/code/datums/weather/weather_types/ash_storm.dm
@@ -95,8 +95,8 @@
if(is_ash_immune(L))
return
if(is_species(L, /datum/species/lizard/ashwalker))
- if(!IS_STAMCRIT(L))
- L.adjustStaminaLossBuffered(4)
+ if(L.getStaminaLoss() < (STAMINA_CRIT - 40))
+ L.adjustStaminaLoss(4)
return
L.adjustFireLoss(4)
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 65fdbb6f1c..1c75f1e533 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -565,7 +565,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
to_chat(user, "You cannot locate any organic eyes on this brain!")
return
- if(IS_STAMCRIT(user))//CIT CHANGE - makes eyestabbing impossible if you're in stamina softcrit
+ if(IS_STAMCRIT(user) || !user.UseStaminaBuffer(STAMINA_COST_ITEM_EYESTAB, warn = TRUE))//CIT CHANGE - makes eyestabbing impossible if you're in stamina softcrit
to_chat(user, "You're too exhausted for that.")//CIT CHANGE - ditto
return //CIT CHANGE - ditto
@@ -575,8 +575,6 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
user.do_attack_animation(M)
- user.adjustStaminaLossBuffered(10)//CIT CHANGE - makes eyestabbing cost stamina
-
if(M != user)
M.visible_message("[user] has stabbed [M] in the eye with [src]!", \
"[user] stabs you in the eye with [src]!")
diff --git a/code/game/objects/items/electrostaff.dm b/code/game/objects/items/electrostaff.dm
index 65d2fdd699..31aaff12b5 100644
--- a/code/game/objects/items/electrostaff.dm
+++ b/code/game/objects/items/electrostaff.dm
@@ -21,12 +21,11 @@
var/can_block_projectiles = FALSE //can't block guns
var/lethal_cost = 400 //10000/400*20 = 500. decent enough?
var/lethal_damage = 20
- var/lethal_stam_cost = 4
var/stun_cost = 333 //10000/333*25 = 750. stunbatons are at time of writing 10000/1000*49 = 490.
var/stun_status_effect = STATUS_EFFECT_ELECTROSTAFF //a small slowdown effect
var/stun_stamdmg = 40
var/stun_status_duration = 25
- var/stun_stam_cost = 3.5
+ var/stam_cost = 3.5
var/wielded = FALSE // track wielded status on item
// haha security desword time /s
@@ -171,7 +170,7 @@
turn_off()
/obj/item/electrostaff/attack(mob/living/target, mob/living/user)
- if(IS_STAMCRIT(user))//CIT CHANGE - makes it impossible to baton in stamina softcrit
+ if(IS_STAMCRIT(user) || !user.UseStaminaBuffer(stam_cost))//CIT CHANGE - makes it impossible to baton in stamina softcrit
to_chat(user, "You're too exhausted to use [src] properly.")//CIT CHANGE - ditto
return //CIT CHANGE - ditto
if(on && HAS_TRAIT(user, TRAIT_CLUMSY) && prob(50))
@@ -186,13 +185,11 @@
if(user.a_intent != INTENT_HARM)
if(stun_act(target, user, null, return_list))
user.do_attack_animation(target)
- user.adjustStaminaLossBuffered(stun_stam_cost)
return
else if(!harm_act(target, user, null, return_list))
return ..() //if you can't fry them just beat them with it
else //we did harm act them
user.do_attack_animation(target)
- user.adjustStaminaLossBuffered(lethal_stam_cost)
/obj/item/electrostaff/proc/stun_act(mob/living/target, mob/living/user, no_charge_and_force = FALSE, list/block_return = list())
var/stunforce = block_calculate_resultant_damage(stun_stamdmg, block_return)
diff --git a/code/game/objects/items/melee/misc.dm b/code/game/objects/items/melee/misc.dm
index 52ff0f740e..15d10c4d11 100644
--- a/code/game/objects/items/melee/misc.dm
+++ b/code/game/objects/items/melee/misc.dm
@@ -352,6 +352,8 @@
return
else
if(cooldown_check < world.time)
+ if(!UseStaminaBufferStandard(user, STAM_COST_BATON_MOB_MULT, warn = TRUE))
+ return DISCARD_LAST_ACTION
if(target.mob_run_block(src, 0, "[user]'s [name]", ATTACK_TYPE_MELEE, 0, user, null, null) & BLOCK_SUCCESS)
playsound(target, 'sound/weapons/genhit.ogg', 50, 1)
return
@@ -373,7 +375,6 @@
else
target.LAssailant = WEAKREF(user)
cooldown_check = world.time + cooldown
- user.adjustStaminaLossBuffered(getweight(user, STAM_COST_BATON_MOB_MULT))
else
var/wait_desc = get_wait_description()
if(wait_desc)
diff --git a/code/game/objects/items/mop.dm b/code/game/objects/items/mop.dm
index b420bfc002..01ef96b7e8 100644
--- a/code/game/objects/items/mop.dm
+++ b/code/game/objects/items/mop.dm
@@ -56,22 +56,20 @@
return
if(T)
+ if(!L.UseStaminaBuffer(stamusage, warn = TRUE))
+ return
user.visible_message("[user] cleans \the [T] with [src].", "You clean \the [T] with [src].")
clean(T)
user.DelayNextAction(CLICK_CD_MELEE)
user.do_attack_animation(T, used_item = src)
- if(istype(L))
- L.adjustStaminaLossBuffered(stamusage)
playsound(T, "slosh", 50, 1)
-
/obj/effect/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/mop) || istype(I, /obj/item/soap))
return
else
return ..()
-
/obj/item/mop/proc/janicart_insert(mob/user, obj/structure/janitorialcart/J)
if(insertable)
J.put_in_cart(src, user)
diff --git a/code/game/objects/items/powerfist.dm b/code/game/objects/items/powerfist.dm
index 2834b3b758..bd83404356 100644
--- a/code/game/objects/items/powerfist.dm
+++ b/code/game/objects/items/powerfist.dm
@@ -6,7 +6,7 @@
lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
flags_1 = CONDUCT_1
- item_flags = NEEDS_PERMIT | NO_COMBAT_MODE_FORCE_MODIFIER //To avoid ambushing and oneshotting healthy crewmembers on force setting 3.
+ item_flags = NEEDS_PERMIT
attack_verb = list("whacked", "fisted", "power-punched")
force = 20
throwforce = 10
@@ -76,6 +76,9 @@
if(!tank)
to_chat(user, "\The [src] can't operate without a source of gas!")
return FALSE
+ var/weight = getweight(user, STAM_COST_ATTACK_MOB_MULT)
+ if(!user.UseStaminaBuffer(weight, warn = TRUE))
+ return FALSE
var/datum/gas_mixture/gasused = tank.air_contents.remove(gasperfist * fisto_setting)
var/turf/T = get_turf(src)
if(!T)
@@ -108,8 +111,4 @@
target.throw_at(throw_target, 5 * fisto_setting, 0.5 + (fisto_setting / 2))
log_combat(user, target, "power fisted", src)
-
- var/weight = getweight(user, STAM_COST_ATTACK_MOB_MULT)
- if(weight)
- user.adjustStaminaLossBuffered(weight)
return TRUE
diff --git a/code/game/objects/items/shields.dm b/code/game/objects/items/shields.dm
index 4952c93928..d1d57fe375 100644
--- a/code/game/objects/items/shields.dm
+++ b/code/game/objects/items/shields.dm
@@ -136,15 +136,17 @@
if(!(shield_flags & SHIELD_BASH_GROUND_SLAM))
to_chat(user, "You can't ground slam with [src]!")
return FALSE
+ if(!user.UseStaminaBuffer(shieldbash_stamcost, warn = TRUE))
+ return FALSE
bash_target(user, target, NONE, harmful)
user.do_attack_animation(target, used_item = src)
playsound(src, harmful? "swing_hit" : 'sound/weapons/thudswoosh.ogg', 75, 1)
last_shieldbash = world.time
- user.adjustStaminaLossBuffered(shieldbash_stamcost)
return TRUE
// Directional sweep!
last_shieldbash = world.time
- user.adjustStaminaLossBuffered(shieldbash_stamcost)
+ if(!user.UseStaminaBuffer(shieldbash_stamcost, warn = TRUE))
+ return FALSE
// Since we are in combat mode, we can probably safely use the user's dir instead of getting their mouse pointing cardinal dir.
var/bashdir = user.dir
do_shieldbash_effect(user, bashdir, harmful)
diff --git a/code/game/objects/items/stunbaton.dm b/code/game/objects/items/stunbaton.dm
index caad287e6c..16efecec7c 100644
--- a/code/game/objects/items/stunbaton.dm
+++ b/code/game/objects/items/stunbaton.dm
@@ -166,13 +166,12 @@
if(turned_on)
if(baton_stun(M, user, disarming))
user.do_attack_animation(M)
- user.adjustStaminaLossBuffered(getweight(user, STAM_COST_BATON_MOB_MULT))
else if(user.a_intent != INTENT_HARM) //they'll try to bash in the last proc.
M.visible_message("[user] has prodded [M] with [src]. Luckily it was off.", \
"[user] has prodded you with [src]. Luckily it was off")
return disarming || (user.a_intent != INTENT_HARM)
-/obj/item/melee/baton/proc/baton_stun(mob/living/L, mob/user, disarming = FALSE)
+/obj/item/melee/baton/proc/baton_stun(mob/living/L, mob/living/user, disarming = FALSE)
var/list/return_list = list()
if(L.mob_run_block(src, 0, "[user]'s [name]", ATTACK_TYPE_MELEE, 0, user, null, return_list) & BLOCK_SUCCESS) //No message; check_shields() handles that
playsound(L, 'sound/weapons/genhit.ogg', 50, 1)
@@ -194,6 +193,9 @@
return FALSE
stunpwr *= round(stuncharge/hitcost, 0.1)
+ if(!user.UseStaminaBuffer(getweight(user, STAM_COST_BATON_MOB_MULT), warn = TRUE))
+ return FALSE
+
if(!disarming)
if(knockdown)
L.DefaultCombatKnockdown(50, override_stamdmg = 0) //knockdown
diff --git a/code/game/objects/items/tanks/jetpack.dm b/code/game/objects/items/tanks/jetpack.dm
index 7cf25098e1..29961d12b4 100644
--- a/code/game/objects/items/tanks/jetpack.dm
+++ b/code/game/objects/items/tanks/jetpack.dm
@@ -55,6 +55,8 @@
RegisterSignal(user, COMSIG_MOVABLE_MOVED, .proc/move_react)
if(full_speed)
user.add_movespeed_modifier(/datum/movespeed_modifier/jetpack/fullspeed)
+ else
+ user.add_movespeed_modifier(/datum/movespeed_modifier/jetpack)
/obj/item/tank/jetpack/proc/turn_off(mob/user)
on = FALSE
@@ -63,6 +65,7 @@
ion_trail.stop()
UnregisterSignal(user, COMSIG_MOVABLE_MOVED)
user.remove_movespeed_modifier(/datum/movespeed_modifier/jetpack/fullspeed)
+ user.remove_movespeed_modifier(/datum/movespeed_modifier/jetpack)
/obj/item/tank/jetpack/proc/move_react(mob/user)
allow_thrust(0.01, user)
diff --git a/code/game/objects/items/tools/screwdriver.dm b/code/game/objects/items/tools/screwdriver.dm
index bb507e5725..bf767af2ed 100644
--- a/code/game/objects/items/tools/screwdriver.dm
+++ b/code/game/objects/items/tools/screwdriver.dm
@@ -161,4 +161,4 @@
item_state = "screwdriver_nuke"
usesound = 'sound/items/pshoom.ogg'
toolspeed = 0.2
- random_color = FALSE
\ No newline at end of file
+ random_color = FALSE
diff --git a/code/modules/holiday/holidays.dm b/code/modules/holiday/holidays.dm
index 83bb67b879..f6e4684e21 100644
--- a/code/modules/holiday/holidays.dm
+++ b/code/modules/holiday/holidays.dm
@@ -351,7 +351,7 @@
/datum/holiday/halloween
name = HALLOWEEN
- begin_day = 28
+ begin_day = 1
begin_month = OCTOBER
end_day = 2
end_month = NOVEMBER
diff --git a/code/modules/integrated_electronics/subtypes/output.dm b/code/modules/integrated_electronics/subtypes/output.dm
index efd98c4d50..833b2963ad 100644
--- a/code/modules/integrated_electronics/subtypes/output.dm
+++ b/code/modules/integrated_electronics/subtypes/output.dm
@@ -44,18 +44,18 @@
/obj/item/integrated_circuit/output/screen/large/do_work()
..()
- if(isliving(assembly.loc))//this whole block just returns if the assembly is neither in a mobs hands or on the ground
- var/mob/living/H = assembly.loc
- if(H.get_active_held_item() != assembly && H.get_inactive_held_item() != assembly)
- return
- else
- if(!isturf(assembly.loc))
- return
-
- var/atom/host = assembly || src
var/list/mobs = list()
- for(var/mob/M in range(0, get_turf(src)))
- mobs += M
+ if(isliving(assembly.loc))
+ mobs += assembly.loc
+ var/mob/living/L = assembly.loc
+ if(L.is_holding(src))
+ for(var/mob/M in range(1, get_turf(src)))
+ mobs += M
+ else
+ for(var/mob/M in range(2, get_turf(src)))
+ mobs += M
+
+ var/atom/host = assembly || src
to_chat(mobs, "[icon2html(host.icon, world, host.icon_state)] flashes a message: [stuff_to_display]")
host.investigate_log("displayed \"[html_encode(stuff_to_display)]\" as [type].", INVESTIGATE_CIRCUIT)
diff --git a/code/modules/jobs/job_types/_job.dm b/code/modules/jobs/job_types/_job.dm
index 0feceaf1a8..b0ac5248c1 100644
--- a/code/modules/jobs/job_types/_job.dm
+++ b/code/modules/jobs/job_types/_job.dm
@@ -73,6 +73,31 @@
/// Starting skill modifiers.
var/list/starting_modifiers
+/**
+ * Checks if we should be created on a certain map
+ */
+/datum/job/proc/map_check(datum/map_config/C)
+ return (length(C.job_whitelist)? (type in C.job_whitelist) : !(type in C.job_blacklist))
+
+/**
+ * Processes map specific overrides
+ */
+/datum/job/proc/process_map_overrides(datum/map_config/C)
+ if(type in C.job_override_spawn_positions)
+ spawn_positions = C.job_override_spawn_positions[type]
+ if(type in C.job_override_total_positions)
+ total_positions = C.job_override_total_positions[type]
+ if(type in C.job_access_override)
+ access = C.job_access_override[type]
+ minimal_access = access
+ else
+ if(type in C.job_access_add)
+ access += C.job_access_add[type]
+ minimal_access += C.job_access_add[type]
+ if(type in C.job_access_remove)
+ access -= C.job_access_add[type]
+ minimal_access -= C.job_access_remove[type]
+
//Only override this proc
//H is usually a human unless an /equip override transformed it
/datum/job/proc/after_spawn(mob/living/H, mob/M, latejoin = FALSE)
@@ -175,9 +200,6 @@
/datum/job/proc/config_check()
return TRUE
-/datum/job/proc/map_check()
- return TRUE
-
/datum/job/proc/radio_help_message(mob/M)
to_chat(M, "Prefix your message with :h to speak on your department's radio. To see other prefixes, look closely at your headset.")
diff --git a/code/modules/jobs/map_changes/map_changes.dm b/code/modules/jobs/map_changes/map_changes.dm
deleted file mode 100644
index 575075037b..0000000000
--- a/code/modules/jobs/map_changes/map_changes.dm
+++ /dev/null
@@ -1,4 +0,0 @@
-//this needs to come after the job_types subfolder to keep the correct ordering
-
-#include "..\..\..\..\_maps\map_files\PubbyStation\job_changes.dm"
-#undef JOB_MODIFICATION_MAP_NAME
\ No newline at end of file
diff --git a/code/modules/keybindings/bindings_client.dm b/code/modules/keybindings/bindings_client.dm
index 3a47cd2315..3d12f89cfe 100644
--- a/code/modules/keybindings/bindings_client.dm
+++ b/code/modules/keybindings/bindings_client.dm
@@ -1,6 +1,7 @@
// Clients aren't datums so we have to define these procs indpendently.
// These verbs are called for all key press and release events
/client/verb/keyDown(_key as text)
+ SHOULD_NOT_SLEEP(TRUE)
set instant = TRUE
set hidden = TRUE
@@ -83,6 +84,7 @@
keyUp("[key]")
/client/verb/keyUp(_key as text)
+ SHOULD_NOT_SLEEP(TRUE)
set instant = TRUE
set hidden = TRUE
diff --git a/code/modules/keybindings/setup.dm b/code/modules/keybindings/setup.dm
index a53820b5d2..47dbfd855f 100644
--- a/code/modules/keybindings/setup.dm
+++ b/code/modules/keybindings/setup.dm
@@ -1,12 +1,11 @@
/datum/proc/key_down(key, client/user) // Called when a key is pressed down initially
- return
+ SHOULD_NOT_SLEEP(TRUE)
/datum/proc/key_up(key, client/user) // Called when a key is released
- return
+ SHOULD_NOT_SLEEP(TRUE)
/datum/proc/keyLoop(client/user) // Called once every frame
- set waitfor = FALSE
- return
+ SHOULD_NOT_SLEEP(TRUE)
// removes all the existing macros
/client/proc/erase_all_macros()
diff --git a/code/modules/mapping/map_config.dm b/code/modules/mapping/map_config.dm
index c03ef65f43..d18ca1e1e3 100644
--- a/code/modules/mapping/map_config.dm
+++ b/code/modules/mapping/map_config.dm
@@ -43,6 +43,21 @@
/// Orientation to load in by default.
var/orientation = SOUTH //byond defaults to placing everyting SOUTH.
+ /// Jobs whitelist - if this is not empty, ONLY these jobs are allowed. Overrides blacklist.
+ var/list/job_whitelist
+ /// Jobs blacklist - if this is not empty, jobs in this aren't allowed.
+ var/list/job_blacklist
+ /// Job spawn position mod - type = number
+ var/list/job_override_spawn_positions
+ /// Job total position mod - type = number
+ var/list/job_override_total_positions
+ /// Add these accesses to jobs - type = list()
+ var/list/job_access_add
+ /// Remove these accesses from jobs - type = list()
+ var/list/job_access_remove
+ /// Override job accesses - type = list() - overrides everything else
+ var/list/job_access_override
+
/proc/load_map_config(filename = "data/next_map.json", default_to_box, delete_after, error_if_missing = TRUE)
var/datum/map_config/config = new
if (default_to_box)
@@ -161,6 +176,69 @@
allow_custom_shuttles = json["allow_custom_shuttles"] != FALSE
+ if("job_whitelist" in json)
+ job_whitelist = list()
+ for(var/path in json["job_whitelist"])
+ var/type = text2path(path)
+ if(!path)
+ log_config("map datum [config_filename] failed to validate path [path] in job overrides.")
+ continue
+ job_whitelist += type
+
+ if("job_blacklist" in json)
+ job_blacklist = list()
+ for(var/path in json["job_blacklist"])
+ var/type = text2path(path)
+ if(!path)
+ log_config("map datum [config_filename] failed to validate path [path] in job overrides.")
+ continue
+ job_blacklist += type
+
+ if("job_override_spawn_positions" in json)
+ job_override_spawn_positions = list()
+ for(var/path in json["job_override_spawn_positions"])
+ var/type = text2path(path)
+ if(!path)
+ log_config("map datum [config_filename] failed to validate path [path] in job overrides.")
+ continue
+ job_override_spawn_positions += type
+
+ if("job_override_total_positions" in json)
+ job_override_total_positions = list()
+ for(var/path in json["job_override_total_positions"])
+ var/type = text2path(path)
+ if(!path)
+ log_config("map datum [config_filename] failed to validate path [path] in job overrides.")
+ continue
+ job_override_total_positions += type
+
+ if("job_access_add" in json)
+ job_access_add = list()
+ for(var/path in json["job_acces_add"])
+ var/type = text2path(path)
+ if(!path)
+ log_config("map datum [config_filename] failed to validate path [path] in job overrides.")
+ continue
+ job_access_add[type] = json["job_access_add"]
+
+ if("job_access_remove" in json)
+ job_access_remove = list()
+ for(var/path in json["job_acces_add"])
+ var/type = text2path(path)
+ if(!path)
+ log_config("map datum [config_filename] failed to validate path [path] in job overrides.")
+ continue
+ job_access_remove[type] = json["job_access_remove"]
+
+ if("job_access_override" in json)
+ job_access_override = list()
+ for(var/path in json["job_acces_add"])
+ var/type = text2path(path)
+ if(!path)
+ log_config("map datum [config_filename] failed to validate path [path] in job overrides.")
+ continue
+ job_access_override[type] = json["job_access_override"]
+
defaulted = FALSE
return TRUE
#undef CHECK_EXISTS
@@ -190,6 +268,13 @@
jsonlist["announcertype"] = announcertype
jsonlist["orientation"] = orientation
jsonlist["allow_custom_shuttles"] = allow_custom_shuttles
+ jsonlist["job_whitelist"] = job_whitelist
+ jsonlist["job_blacklist"] = job_blacklist
+ jsonlist["job_override_spawn_positions"] = job_override_spawn_positions
+ jsonlist["job_override_total_positions"] = job_override_total_positions
+ jsonlist["job_access_add"] = job_access_add
+ jsonlist["job_access_remove"] = job_access_remove
+ jsonlist["job_access_override"] = job_access_override
if(fexists("data/next_map.json"))
fdel("data/next_map.json")
var/F = file("data/next_map.json")
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 11b133f5e7..26486f255a 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -191,8 +191,8 @@
if(HAS_TRAIT(src, TRAIT_PACIFISM))
to_chat(src, "You gently let go of [throwable_mob].")
return
-
- adjustStaminaLossBuffered(STAM_COST_THROW_MOB * ((throwable_mob.mob_size+1)**2))// throwing an entire person shall be very tiring
+ if(!UseStaminaBuffer(STAM_COST_THROW_MOB * ((throwable_mob.mob_size+1)**2), TRUE))
+ return
var/turf/start_T = get_turf(loc) //Get the start and target tile for the descriptors
var/turf/end_T = get_turf(target)
if(start_T && end_T)
@@ -206,7 +206,8 @@
to_chat(src, "You set [I] down gently on the ground.")
return
- adjustStaminaLossBuffered(I.getweight(src, STAM_COST_THROW_MULT, SKILL_THROW_STAM_COST))
+ if(!UseStaminaBuffer(I.getweight(src, STAM_COST_THROW_MULT, SKILL_THROW_STAM_COST), warn = TRUE))
+ return
if(thrown_thing)
var/power_throw = 0
@@ -594,15 +595,23 @@
remove_movespeed_modifier(/datum/movespeed_modifier/carbon_softcrit)
/mob/living/carbon/update_stamina()
- var/stam = getStaminaLoss()
- if(stam > DAMAGE_PRECISION)
- var/total_health = (maxHealth - stam)
- if(total_health <= crit_threshold && !stat)
- if(CHECK_MOBILITY(src, MOBILITY_STAND))
- to_chat(src, "You're too exhausted to keep going...")
- KnockToFloor(TRUE)
- update_health_hud()
-
+ var/total_health = getStaminaLoss()
+ if(total_health)
+ if(!(combat_flags & COMBAT_FLAG_HARD_STAMCRIT) && total_health >= STAMINA_CRIT && !stat)
+ to_chat(src, "You're too exhausted to keep going...")
+ set_resting(TRUE, FALSE, FALSE)
+ SEND_SIGNAL(src, COMSIG_DISABLE_COMBAT_MODE)
+ ENABLE_BITFIELD(combat_flags, COMBAT_FLAG_HARD_STAMCRIT)
+ filters += CIT_FILTER_STAMINACRIT
+ update_mobility()
+ if((combat_flags & COMBAT_FLAG_HARD_STAMCRIT) && total_health <= STAMINA_CRIT)
+ to_chat(src, "You don't feel nearly as exhausted anymore.")
+ DISABLE_BITFIELD(combat_flags, COMBAT_FLAG_HARD_STAMCRIT)
+ filters -= CIT_FILTER_STAMINACRIT
+ update_mobility()
+ UpdateStaminaBuffer()
+ update_health_hud()
+
/mob/living/carbon/update_sight()
if(!client)
return
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index a4543389b4..1b7ab81d59 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -805,7 +805,7 @@
hud_used.healthdoll.icon_state = "healthdoll_DEAD"
hud_used.staminas?.update_icon_state()
- hud_used.staminabuffer?.update_icon_state()
+ hud_used.staminabuffer?.mark_dirty()
/mob/living/carbon/human/fully_heal(admin_revive = FALSE)
if(admin_revive)
@@ -1044,10 +1044,9 @@
remove_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown)
remove_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown_flying)
return
- var/stambufferinfluence = (bufferedstam*(100/stambuffer))*0.2 //CIT CHANGE - makes stamina buffer influence movedelay
if(!HAS_TRAIT(src, TRAIT_IGNOREDAMAGESLOWDOWN)) //if we want to ignore slowdown from damage, but not from equipment
var/scaling = maxHealth / 100
- var/health_deficiency = (((maxHealth / scaling) + stambufferinfluence) - (health / scaling) + (getStaminaLoss()*0.75))//CIT CHANGE - reduces the impact of staminaloss and makes stamina buffer influence it
+ var/health_deficiency = ((maxHealth / scaling) - (health / scaling) + (getStaminaLoss()*0.75))//CIT CHANGE - reduces the impact of staminaloss and makes stamina buffer influence it
if(health_deficiency >= 40)
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown, TRUE, (health_deficiency-39) / 75)
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown_flying, TRUE, (health_deficiency-39) / 25)
diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm
index a6a3f408dc..89a4570e8d 100644
--- a/code/modules/mob/living/carbon/human/human_defines.dm
+++ b/code/modules/mob/living/carbon/human/human_defines.dm
@@ -7,7 +7,7 @@
buckle_lying = FALSE
mob_biotypes = MOB_ORGANIC|MOB_HUMANOID
/// Enable stamina combat
- combat_flags = COMBAT_FLAGS_DEFAULT | COMBAT_FLAG_UNARMED_PARRY
+ combat_flags = COMBAT_FLAGS_STAMINA_COMBAT | COMBAT_FLAG_UNARMED_PARRY
status_flags = CANSTUN|CANKNOCKDOWN|CANUNCONSCIOUS|CANPUSH|CANSTAGGER
has_field_of_vision = FALSE //Handled by species.
diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm
index 5ced11e35a..97c40c96d7 100644
--- a/code/modules/mob/living/carbon/human/human_movement.dm
+++ b/code/modules/mob/living/carbon/human/human_movement.dm
@@ -11,11 +11,6 @@
/mob/living/carbon/human/movement_delay()
. = ..()
- if(CHECK_MOBILITY(src, MOBILITY_STAND) && m_intent == MOVE_INTENT_RUN && (combat_flags & COMBAT_FLAG_SPRINT_ACTIVE))
- var/static/datum/config_entry/number/movedelay/sprint_speed_increase/SSI
- if(!SSI)
- SSI = CONFIG_GET_ENTRY(number/movedelay/sprint_speed_increase)
- . -= SSI.config_entry_value
if (m_intent == MOVE_INTENT_WALK && HAS_TRAIT(src, TRAIT_SPEEDY_STEP))
. -= 1.5
@@ -61,7 +56,11 @@
HM.on_move(NewLoc)
if(. && (combat_flags & COMBAT_FLAG_SPRINT_ACTIVE) && !(movement_type & FLYING) && CHECK_ALL_MOBILITY(src, MOBILITY_MOVE|MOBILITY_STAND) && m_intent == MOVE_INTENT_RUN && has_gravity(loc) && (!pulledby || (pulledby.pulledby == src)))
if(!HAS_TRAIT(src, TRAIT_FREESPRINT))
- doSprintLossTiles(1)
+ var/datum/movespeed_modifier/equipment_speedmod/MM = get_movespeed_modifier_datum(/datum/movespeed_modifier/equipment_speedmod)
+ var/amount = 1
+ if(MM?.multiplicative_slowdown >= 1)
+ amount *= (1 + (6 - (3 / MM.multiplicative_slowdown)))
+ doSprintLossTiles(amount)
if((oldpseudoheight - pseudo_z_axis) >= 8)
to_chat(src, "You trip off of the elevated surface!")
for(var/obj/item/I in held_items)
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index bfeba22a04..50a725f8cf 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -1364,9 +1364,10 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
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)
- else
- user.adjustStaminaLossBuffered(3.5)
+ if(!user.UseStaminaBuffer(1.5, warn = TRUE))
+ return
+ else if(!user.UseStaminaBuffer(3.5, warn = TRUE))
+ return
if(attacker_style && attacker_style.harm_act(user,target))
return TRUE
@@ -1505,6 +1506,8 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
return FALSE
else if(aim_for_mouth && ( target_on_help || target_restrained || target_aiming_for_mouth))
+ if(!user.UseStaminaBuffer(3, warn = TRUE))
+ return
playsound(target.loc, 'sound/weapons/slap.ogg', 50, 1, -1)
target.visible_message(\
@@ -1512,7 +1515,6 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
"[user] slaps you in the face! ",\
"You hear a slap.", target = user, target_message = "You slap [user == target ? "yourself" : "\the [target]"] in the face! ")
user.do_attack_animation(target, ATTACK_EFFECT_FACE_SLAP)
- user.adjustStaminaLossBuffered(3)
if (!HAS_TRAIT(target, TRAIT_PERMABONER))
stop_wagging_tail(target)
return FALSE
@@ -1520,8 +1522,9 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
if(target.client?.prefs.cit_toggles & NO_ASS_SLAP)
to_chat(user,"A force stays your hand, preventing you from slapping \the [target]'s ass!")
return FALSE
+ if(!user.UseStaminaBuffer(3, warn = TRUE))
+ return FALSE
user.do_attack_animation(target, ATTACK_EFFECT_ASS_SLAP)
- user.adjustStaminaLossBuffered(3)
target.adjust_arousal(20,maso = TRUE)
if (ishuman(target) && HAS_TRAIT(target, TRAIT_MASO) && target.has_dna() && prob(10))
target.mob_climax(forced_climax=TRUE)
@@ -1539,9 +1542,11 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
user.do_attack_animation(target, ATTACK_EFFECT_DISARM)
if(HAS_TRAIT(user, TRAIT_PUGILIST))//CITADEL CHANGE - makes disarmspam cause staminaloss, pugilists can do it almost effortlessly
- user.adjustStaminaLossBuffered(1)
+ if(!user.UseStaminaBuffer(1, warn = TRUE))
+ return
else
- user.adjustStaminaLossBuffered(3)
+ if(!user.UseStaminaBuffer(1, warn = TRUE))
+ return
if(attacker_style && attacker_style.disarm_act(user,target))
return TRUE
@@ -1777,9 +1782,10 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
if(CHECK_MOBILITY(user, MOBILITY_STAND))
to_chat(user, "You can only force yourself up if you're on the ground.")
return
+ if(!user.UseStaminaBuffer(STAMINA_COST_SHOVE_UP, TRUE))
+ return
user.visible_message("[user] forces [p_them()]self up to [p_their()] feet!", "You force yourself up to your feet!")
user.set_resting(FALSE, TRUE)
- user.adjustStaminaLossBuffered(user.stambuffer) //Rewards good stamina management by making it easier to instantly get up from resting
playsound(user, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
/datum/species/proc/altdisarm(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style)
@@ -1798,8 +1804,9 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
else
if(user == target)
return
+ if(!user.UseStaminaBuffer(4, warn = TRUE))
+ return
user.do_attack_animation(target, ATTACK_EFFECT_DISARM)
- user.adjustStaminaLossBuffered(4)
playsound(target, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
if(target.w_uniform)
diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm
index 8f181d47fd..30785a0103 100644
--- a/code/modules/mob/living/carbon/life.dm
+++ b/code/modules/mob/living/carbon/life.dm
@@ -502,19 +502,14 @@ GLOBAL_LIST_INIT(ballmer_windows_me_msg, list("Yo man, what if, we like, uh, put
//this updates all special effects: stun, sleeping, knockdown, druggy, stuttering, etc..
/mob/living/carbon/handle_status_effects()
..()
- if(getStaminaLoss() && !SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE)) //CIT CHANGE - prevents stamina regen while combat mode is active
- adjustStaminaLoss(!CHECK_MOBILITY(src, MOBILITY_STAND) ? ((combat_flags & COMBAT_FLAG_HARD_STAMCRIT) ? STAM_RECOVERY_STAM_CRIT : STAM_RECOVERY_RESTING) : STAM_RECOVERY_NORMAL)
+ var/combat_mode = SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE)
+ if(getStaminaLoss() && !HAS_TRAIT(src, TRAIT_NO_STAMINA_REGENERATION))
+ adjustStaminaLoss((!CHECK_MOBILITY(src, MOBILITY_STAND) ? ((combat_flags & COMBAT_FLAG_HARD_STAMCRIT) ? STAM_RECOVERY_STAM_CRIT : STAM_RECOVERY_RESTING) : STAM_RECOVERY_NORMAL) * (combat_mode? 0.25 : 1))
if(!(combat_flags & COMBAT_FLAG_HARD_STAMCRIT) && incomingstammult != 1)
incomingstammult = max(0.01, incomingstammult)
incomingstammult = min(1, incomingstammult*2)
- //CIT CHANGES START HERE. STAMINA BUFFER STUFF
- if(bufferedstam && world.time > stambufferregentime)
- var/drainrate = max((bufferedstam*(bufferedstam/(5)))*0.1,1)
- bufferedstam = max(bufferedstam - drainrate, 0)
- //END OF CIT CHANGES
-
var/restingpwr = 1 + 4 * !CHECK_MOBILITY(src, MOBILITY_STAND)
//Dizziness
diff --git a/code/modules/mob/living/emote.dm b/code/modules/mob/living/emote.dm
index 1d78979cbd..4ee52a08a6 100644
--- a/code/modules/mob/living/emote.dm
+++ b/code/modules/mob/living/emote.dm
@@ -562,7 +562,7 @@
if(. && iscarbon(user))
var/mob/living/carbon/C = user
if(isjellyperson(C))
- pick(playsound(C, 'sound/effects/attackblob.ogg', 50, 1),playsound(C, 'sound/effects/blobattack.ogg', 50, 1))
+ playsound(C, 'sound/effects/attackblob.ogg', 50, 1)
/datum/emote/living/audio_emote/blurp
key = "blurp"
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 7e872992dc..57d688bde3 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -8,6 +8,8 @@
for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds)
diag_hud.add_to_hud(src)
faction += "[REF(src)]"
+ stamina_buffer = INFINITY
+ UpdateStaminaBuffer()
GLOB.mob_living_list += src
/mob/living/prepare_huds()
@@ -1257,7 +1259,7 @@
SetUnconscious(clamp_unconscious_to)
HealAllImmobilityUpTo(clamp_immobility_to)
adjustStaminaLoss(min(0, -stamina_boost))
- adjustStaminaLossBuffered(min(0, -stamina_buffer_boost))
+ RechargeStaminaBuffer(stamina_buffer_boost) // this MUST GO AFTER ADJUSTSTAMINALOSS.
if(scale_stamina_loss_recovery)
adjustStaminaLoss(min(-((getStaminaLoss() - stamina_loss_recovery_bypass) * scale_stamina_loss_recovery), 0))
if(put_on_feet)
diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm
index 8e8b94463b..db472df388 100644
--- a/code/modules/mob/living/living_active_block.dm
+++ b/code/modules/mob/living/living_active_block.dm
@@ -8,6 +8,8 @@
active_block_item = null
REMOVE_TRAIT(src, TRAIT_MOBILITY_NOUSE, ACTIVE_BLOCK_TRAIT)
REMOVE_TRAIT(src, TRAIT_SPRINT_LOCKED, ACTIVE_BLOCK_TRAIT)
+ REMOVE_TRAIT(src, TRAIT_NO_STAMINA_BUFFER_REGENERATION, ACTIVE_BLOCK_TRAIT)
+ REMOVE_TRAIT(src, TRAIT_NO_STAMINA_REGENERATION, ACTIVE_BLOCK_TRAIT)
remove_movespeed_modifier(/datum/movespeed_modifier/active_block)
var/datum/block_parry_data/data = I.get_block_parry_data()
DelayNextAction(data.block_end_click_cd_add)
@@ -27,6 +29,10 @@
ADD_TRAIT(src, TRAIT_MOBILITY_NOUSE, ACTIVE_BLOCK_TRAIT) //probably should be something else at some point
if(data.block_lock_sprinting)
ADD_TRAIT(src, TRAIT_SPRINT_LOCKED, ACTIVE_BLOCK_TRAIT)
+ if(data.block_no_stamina_regeneration)
+ ADD_TRAIT(src, TRAIT_NO_STAMINA_REGENERATION, ACTIVE_BLOCK_TRAIT)
+ if(data.block_no_stambuffer_regeneration)
+ ADD_TRAIT(src, TRAIT_NO_STAMINA_BUFFER_REGENERATION, ACTIVE_BLOCK_TRAIT)
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/active_block, multiplicative_slowdown = data.block_slowdown)
active_block_effect_start()
return TRUE
@@ -192,7 +198,7 @@
var/held_index = C.get_held_index_of_item(src)
var/obj/item/bodypart/BP = C.hand_bodyparts[held_index]
if(!BP?.body_zone)
- return C.adjustStaminaLossBuffered(stamina_amount) //nah
+ return C.adjustStaminaLoss(stamina_amount) //nah
var/zone = BP.body_zone
var/stamina_to_zone = data.block_stamina_limb_ratio * stamina_amount
var/stamina_to_chest = stamina_amount - stamina_to_zone
@@ -200,9 +206,9 @@
stamina_to_chest -= stamina_buffered
C.apply_damage(stamina_to_zone, STAMINA, zone)
C.apply_damage(stamina_to_chest, STAMINA, BODY_ZONE_CHEST)
- C.adjustStaminaLossBuffered(stamina_buffered)
+ C.adjustStaminaLoss(stamina_buffered)
else
- owner.adjustStaminaLossBuffered(stamina_amount)
+ owner.adjustStaminaLoss(stamina_amount)
/obj/item/proc/on_active_block(mob/living/owner, atom/object, damage, damage_blocked, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return, override_direction)
return
diff --git a/code/modules/mob/living/living_active_parry.dm b/code/modules/mob/living/living_active_parry.dm
index 10dece1ffc..7353add745 100644
--- a/code/modules/mob/living/living_active_parry.dm
+++ b/code/modules/mob/living/living_active_parry.dm
@@ -68,12 +68,13 @@
// can always implement it later, whatever.
if((data.parry_respect_clickdelay && !CheckActionCooldown()) || ((parry_end_time_last + data.parry_cooldown) > world.time))
to_chat(src, "You are not ready to parry (again)!")
- return
+ return FALSE
// Point of no return, make sure everything is set.
parrying = method
if(method == ITEM_PARRY)
active_parry_item = using_item
- adjustStaminaLossBuffered(data.parry_stamina_cost)
+ if(!UseStaminaBuffer(data.parry_stamina_cost, TRUE))
+ return FALSE
parry_start_time = world.time
successful_parries = list()
addtimer(CALLBACK(src, .proc/end_parry_sequence), full_parry_duration)
diff --git a/code/modules/mob/living/living_blocking_parrying.dm b/code/modules/mob/living/living_blocking_parrying.dm
index 47dae8849d..08ef674b33 100644
--- a/code/modules/mob/living/living_blocking_parrying.dm
+++ b/code/modules/mob/living/living_blocking_parrying.dm
@@ -74,8 +74,12 @@ GLOBAL_LIST_EMPTY(block_parry_data)
/// Ratio of stamina incurred by chest (so after [block_stamina_limb_ratio] runs) that is buffered.
var/block_stamina_buffer_ratio = 1
- /// Stamina dealt directly via adjustStaminaLossBuffered() per SECOND of block.
+ /// Stamina dealt directly via UseStaminaBuffer() per SECOND of block.
var/block_stamina_cost_per_second = 1.5
+ /// Prevent stamina buffer regeneration while block?
+ var/block_no_stambuffer_regeneration = TRUE
+ /// Prevent stamina regeneration while block?
+ var/block_no_stamina_regeneration = FALSE
/// Bitfield for attack types that we can block while down. This will work in any direction.
var/block_resting_attack_types_anydir = ATTACK_TYPE_MELEE | ATTACK_TYPE_UNARMED | ATTACK_TYPE_TACKLE
@@ -307,7 +311,7 @@ GLOBAL_LIST_EMPTY(block_parry_data)
/mob/living/proc/handle_block_parry(seconds = 1)
if(combat_flags & COMBAT_FLAG_ACTIVE_BLOCKING)
var/datum/block_parry_data/data = return_block_parry_datum(active_block_item.block_parry_data)
- adjustStaminaLossBuffered(data.block_stamina_cost_per_second * seconds)
+ UseStaminaBuffer(data.block_stamina_cost_per_second * seconds)
/mob/living/on_item_dropped(obj/item/I)
if(I == active_block_item)
diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm
index 5495d37297..5621fab9c1 100644
--- a/code/modules/mob/living/living_defines.dm
+++ b/code/modules/mob/living/living_defines.dm
@@ -53,6 +53,7 @@
var/hallucination = 0 //Directly affects how long a mob will hallucinate for
+ var/last_special = 0 //Used by the resist verb, likely used to prevent players from bypassing next_move by logging in/out.
var/timeofdeath = 0
//Allows mobs to move through dense areas without restriction. For instance, in space or out of holder objects.
@@ -148,9 +149,6 @@
var/combatmessagecooldown = 0
var/incomingstammult = 1
- var/bufferedstam = 0
- var/stambuffer = 20
- var/stambufferregentime
//Sprint buffer---
var/sprint_buffer = 42 //Tiles
@@ -159,3 +157,13 @@
var/sprint_buffer_regen_last = 0 //last world.time this was regen'd for math.
var/sprint_stamina_cost = 0.70 //stamina loss per tile while insufficient sprint buffer.
//---End
+
+ // Stamina Buffer---
+ /// Our stamina buffer
+ var/stamina_buffer
+ /// Stamina buffer regen modifier
+ var/stamina_buffer_regen_mod = 1
+ /// Last time stamina buffer regen was done
+ var/stamina_buffer_regen_last = 0
+ /// Last time we used stamina buffer
+ var/stamina_buffer_last_use = 0
diff --git a/code/modules/mob/living/living_mobility.dm b/code/modules/mob/living/living_mobility.dm
index 92de711c27..8c6cf9d207 100644
--- a/code/modules/mob/living/living_mobility.dm
+++ b/code/modules/mob/living/living_mobility.dm
@@ -176,4 +176,6 @@
else
remove_movespeed_modifier(/datum/movespeed_modifier/limbless)
+ update_movespeed()
+
return mobility_flags
diff --git a/code/modules/mob/living/living_sprint.dm b/code/modules/mob/living/living_sprint.dm
index dc0f6d2767..3ef67c9edd 100644
--- a/code/modules/mob/living/living_sprint.dm
+++ b/code/modules/mob/living/living_sprint.dm
@@ -29,6 +29,7 @@
if(combat_flags & COMBAT_FLAG_SPRINT_ACTIVE)
return
ENABLE_BITFIELD(combat_flags, COMBAT_FLAG_SPRINT_ACTIVE)
+ add_movespeed_modifier(/datum/movespeed_modifier/sprinting)
if(update_icon)
update_sprint_icon()
@@ -36,6 +37,7 @@
if(!(combat_flags & COMBAT_FLAG_SPRINT_ACTIVE) || (combat_flags & COMBAT_FLAG_SPRINT_FORCED))
return
DISABLE_BITFIELD(combat_flags, COMBAT_FLAG_SPRINT_ACTIVE)
+ remove_movespeed_modifier(/datum/movespeed_modifier/sprinting)
if(update_icon)
update_sprint_icon()
diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm
index 66c2cd96c7..4c03e74d70 100644
--- a/code/modules/mob/living/say.dm
+++ b/code/modules/mob/living/say.dm
@@ -82,6 +82,7 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
return new_msg
/mob/living/say(message, bubble_type,var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
+ set waitfor = FALSE
var/static/list/crit_allowed_modes = list(MODE_WHISPER = TRUE, MODE_CHANGELING = TRUE, MODE_ALIEN = TRUE)
var/static/list/unconscious_allowed_modes = list(MODE_CHANGELING = TRUE, MODE_ALIEN = TRUE)
var/talk_key = get_key(message)
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index 1eb308af00..83df3ffba5 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -1132,10 +1132,6 @@
bellyup = 1
update_icons()
-/mob/living/silicon/robot/adjustStaminaLossBuffered(amount, updating_health = 1)
- if(istype(cell))
- cell.charge -= amount * 5
-
/mob/living/silicon/robot/verb/viewmanifest()
set category = "Robot Commands"
set name = "View Crew Manifest"
diff --git a/code/modules/mob/living/simple_animal/bot/ed209bot.dm b/code/modules/mob/living/simple_animal/bot/ed209bot.dm
index 3f28fd6e01..8375d621d4 100644
--- a/code/modules/mob/living/simple_animal/bot/ed209bot.dm
+++ b/code/modules/mob/living/simple_animal/bot/ed209bot.dm
@@ -236,7 +236,7 @@ Auto Patrol[]"},
if(targets.len>0)
var/mob/living/carbon/t = pick(targets)
if((t.stat!=2) && (t.lying != 1) && (!t.handcuffed)) //we don't shoot people who are dead, cuffed or lying down.
- shootAt(t)
+ INVOKE_ASYNC(src, .proc/shootAt, t)
switch(mode)
if(BOT_IDLE) // idle
@@ -254,7 +254,7 @@ Auto Patrol[]"},
if(target) // make sure target exists
if(Adjacent(target) && isturf(target.loc)) // if right next to perp
- stun_attack(target)
+ INVOKE_ASYNC(src, .proc/stun_attack, target)
mode = BOT_PREP_ARREST
anchored = TRUE
diff --git a/code/modules/mob/living/simple_animal/hostile/wizard.dm b/code/modules/mob/living/simple_animal/hostile/wizard.dm
index b3523fc42c..59bd67e42a 100644
--- a/code/modules/mob/living/simple_animal/hostile/wizard.dm
+++ b/code/modules/mob/living/simple_animal/hostile/wizard.dm
@@ -62,6 +62,9 @@
/mob/living/simple_animal/hostile/wizard/handle_automated_action()
. = ..()
+ INVOKE_ASYNC(src, .proc/AutomatedCast)
+
+/mob/living/simple_animal/hostile/wizard/proc/AutomatedCast()
if(target && next_cast < world.time)
if((get_dir(src,target) in list(SOUTH,EAST,WEST,NORTH)) && fireball.cast_check(0,src)) //Lined up for fireball
src.setDir(get_dir(src,target))
diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm
index 3ca29b6746..1ff7e2f799 100644
--- a/code/modules/mob/living/simple_animal/parrot.dm
+++ b/code/modules/mob/living/simple_animal/parrot.dm
@@ -436,12 +436,7 @@
newspeak.Add(possible_phrase)
speak = newspeak
- //Search for item to steal
- parrot_interest = search_for_item()
- if(parrot_interest)
- emote("me", EMOTE_VISIBLE, "looks in [parrot_interest]'s direction and takes flight.")
- parrot_state = PARROT_SWOOP | PARROT_STEAL
- icon_state = icon_living
+ INVOKE_ASYNC(src, .proc/attempt_item_theft)
return
//-----WANDERING - This is basically a 'I dont know what to do yet' state
@@ -620,6 +615,14 @@
parrot_lastmove = src.loc
return 0
+/mob/living/simple_animal/parrot/proc/attempt_item_theft()
+ //Search for item to steal
+ search_for_item()
+ if(parrot_interest)
+ emote("me", EMOTE_VISIBLE, "looks in [parrot_interest]'s direction and takes flight.")
+ parrot_state = PARROT_SWOOP | PARROT_STEAL
+ icon_state = icon_living
+
/mob/living/simple_animal/parrot/proc/search_for_item()
var/item
for(var/atom/movable/AM in view(src))
diff --git a/code/modules/mob/living/stamina_buffer.dm b/code/modules/mob/living/stamina_buffer.dm
new file mode 100644
index 0000000000..a410df582b
--- /dev/null
+++ b/code/modules/mob/living/stamina_buffer.dm
@@ -0,0 +1,56 @@
+/**
+ * Attempts to use an amount of stamina from our stamina buffer.
+ * Does not use anything if we don't have enough.
+ *
+ * Returns TRUE or FALSE based on if we have it.
+ */
+/mob/living/proc/UseStaminaBuffer(amount, warn = FALSE, considered_action = TRUE)
+ if(!(combat_flags & COMBAT_FLAG_STAMINA_BUFFER))
+ return TRUE
+ if(stamina_buffer < amount)
+ var/stamina_health = getStaminaLoss()
+ if(stamina_health > STAMINA_NO_OVERDRAW_THRESHOLD)
+ if(warn)
+ to_chat(src, "You do not have enough action stamina to do that!")
+ return FALSE
+ adjustStaminaLoss(amount * CONFIG_GET(number/stamina_combat/overdraw_penalty_factor))
+ else
+ stamina_buffer -= amount
+ if(considered_action)
+ stamina_buffer_last_use = world.time
+ UpdateStaminaBuffer()
+ return TRUE
+
+/**
+ * Updates our stamina buffer amount.
+ */
+/mob/living/proc/UpdateStaminaBuffer(updating_hud = TRUE)
+ var/time = world.time - stamina_buffer_regen_last
+ CONFIG_CACHE_ENTRY_AND_FETCH_VALUE(number/stamina_combat/buffer_max, buffer_max)
+ stamina_buffer_regen_last = world.time
+ if((stamina_buffer >= buffer_max) || !(combat_flags & COMBAT_FLAG_STAMINA_BUFFER))
+ stamina_buffer = buffer_max
+ return
+ else if(!time || HAS_TRAIT(src, TRAIT_NO_STAMINA_BUFFER_REGENERATION))
+ return
+ CONFIG_CACHE_ENTRY_AND_FETCH_VALUE(number/stamina_combat/out_of_combat_timer, out_of_combat_timer)
+ CONFIG_CACHE_ENTRY_AND_FETCH_VALUE(number/stamina_combat/base_regeneration, base_regeneration)
+ CONFIG_CACHE_ENTRY_AND_FETCH_VALUE(number/stamina_combat/combat_regeneration, combat_regeneration)
+ CONFIG_CACHE_ENTRY_AND_FETCH_VALUE(number/stamina_combat/percent_regeneration_out_of_combat, percent_regeneration_out_of_combat)
+ CONFIG_CACHE_ENTRY_AND_FETCH_VALUE(number/stamina_combat/post_action_penalty_delay, post_action_penalty_delay)
+ CONFIG_CACHE_ENTRY_AND_FETCH_VALUE(number/stamina_combat/post_action_penalty_factor, post_action_penalty_factor)
+ var/base_regen = (SEND_SIGNAL(src, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE))? base_regeneration : combat_regeneration
+ var/time_since_last_action = world.time - stamina_buffer_last_use
+ var/action_penalty = ((time_since_last_action) < (post_action_penalty_delay * 10))? post_action_penalty_factor : 1
+ var/out_of_combat_bonus = (time_since_last_action < (out_of_combat_timer * 10))? 0 : ((buffer_max * percent_regeneration_out_of_combat * 0.01))
+ var/regen = ((base_regen * action_penalty) + out_of_combat_bonus) * time * 0.1 * stamina_buffer_regen_mod
+ stamina_buffer += min((buffer_max - stamina_buffer), regen)
+ if(updating_hud)
+ hud_used?.staminabuffer?.mark_dirty()
+
+/**
+ * Boosts our stamina buffer by this much.
+ */
+/mob/living/proc/RechargeStaminaBuffer(amount)
+ stamina_buffer += abs(amount)
+ UpdateStaminaBuffer()
diff --git a/code/modules/movespeed/_movespeed_modifier.dm b/code/modules/movespeed/_movespeed_modifier.dm
index 3bc4463531..9c8036bd55 100644
--- a/code/modules/movespeed/_movespeed_modifier.dm
+++ b/code/modules/movespeed/_movespeed_modifier.dm
@@ -38,6 +38,12 @@ Key procs
/// Multiplicative slowdown
var/multiplicative_slowdown = 0
+ /// Next two variables depend on this: Should we do advanced calculations?
+ var/complex_calculation = FALSE
+ /// Absolute max tiles we can boost to
+ var/absolute_max_tiles_per_second
+ /// Max tiles per second we can boost
+ var/max_tiles_per_second_boost
/// Movetypes this applies to
var/movetypes = ALL
@@ -53,6 +59,16 @@ Key procs
if(!id)
id = "[type]" //We turn the path into a string.
+/**
+ * Returns new multiplicative movespeed after modification.
+ */
+/datum/movespeed_modifier/proc/apply_multiplicative(existing, mob/target)
+ if(!complex_calculation || (multiplicative_slowdown > 0)) // we aren't limiting how much things can slowdown.. yet.
+ return existing + multiplicative_slowdown
+ var/current_tiles = 10 / max(existing, world.tick_lag)
+ var/minimum_speed = 10 / min(current_tiles + max_tiles_per_second_boost, max(current_tiles, absolute_max_tiles_per_second))
+ return max(minimum_speed, existing + multiplicative_slowdown)
+
GLOBAL_LIST_EMPTY(movespeed_modification_cache)
/// Grabs a STATIC MODIFIER datum from cache. YOU MUST NEVER EDIT THESE DATUMS, OR IT WILL AFFECT ANYTHING ELSE USING IT TOO!
@@ -171,13 +187,15 @@ GLOBAL_LIST_EMPTY(movespeed_modification_cache)
/// Set or update the global movespeed config on a mob
/mob/proc/update_config_movespeed()
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/mob_config_speedmod, multiplicative_slowdown = get_config_multiplicative_speed())
+ add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/mob_config_speedmod_floating, multiplicative_slowdown = get_config_multiplicative_speed(TRUE))
/// Get the global config movespeed of a mob by type
-/mob/proc/get_config_multiplicative_speed()
- if(!islist(GLOB.mob_config_movespeed_type_lookup) || !GLOB.mob_config_movespeed_type_lookup[type])
+/mob/proc/get_config_multiplicative_speed(floating = FALSE)
+ var/list/read = floating? GLOB.mob_config_movespeed_type_lookup_floating : GLOB.mob_config_movespeed_type_lookup
+ if(!islist(read) || !read[type])
return 0
else
- return GLOB.mob_config_movespeed_type_lookup[type]
+ return read[type]
/// Go through the list of movespeed modifiers and calculate a final movespeed. ANY ADD/REMOVE DONE IN UPDATE_MOVESPEED MUST HAVE THE UPDATE ARGUMENT SET AS FALSE!
/mob/proc/update_movespeed()
@@ -198,7 +216,7 @@ GLOBAL_LIST_EMPTY(movespeed_modification_cache)
conflict_tracker[conflict] = amt
else
continue
- . += amt
+ . = M.apply_multiplicative(., src)
var/old = cached_multiplicative_slowdown // CITAEDL EDIT - To make things a bit less jarring, when in situations where
// your delay decreases, "give" the delay back to the client
cached_multiplicative_slowdown = .
@@ -220,6 +238,13 @@ GLOBAL_LIST_EMPTY(movespeed_modification_cache)
var/datum/movespeed_modifier/M = movespeed_modification[id]
. += M.multiplicative_slowdown
+/**
+ * Gets the movespeed modifier datum of a modifier on a mob. Returns null if not found.
+ * DANGER: IT IS UP TO THE PERSON USING THIS TO MAKE SURE THE MODIFIER IS NOT MODIFIED IF IT HAPPENS TO BE GLOBAL/CACHED.
+ */
+/mob/proc/get_movespeed_modifier_datum(id)
+ return movespeed_modification[id]
+
/// Checks if a move speed modifier is valid and not missing any data
/proc/movespeed_data_null_check(datum/movespeed_modifier/M) //Determines if a data list is not meaningful and should be discarded.
. = TRUE
diff --git a/code/modules/movespeed/modifiers/items.dm b/code/modules/movespeed/modifiers/items.dm
index 94dc2a1553..a8510ecd86 100644
--- a/code/modules/movespeed/modifiers/items.dm
+++ b/code/modules/movespeed/modifiers/items.dm
@@ -1,12 +1,13 @@
/datum/movespeed_modifier/jetpack
conflicts_with = MOVE_CONFLICT_JETPACK
movetypes = FLOATING
+ multiplicative_slowdown = -1
/datum/movespeed_modifier/jetpack/cybernetic
- multiplicative_slowdown = -0.5
+ multiplicative_slowdown = -1.25
/datum/movespeed_modifier/jetpack/fullspeed
- multiplicative_slowdown = -2
+ multiplicative_slowdown = -1.5
/datum/movespeed_modifier/die_of_fate
multiplicative_slowdown = 1
diff --git a/code/modules/movespeed/modifiers/mobs.dm b/code/modules/movespeed/modifiers/mobs.dm
index d17767bb1f..f6cf84eb83 100644
--- a/code/modules/movespeed/modifiers/mobs.dm
+++ b/code/modules/movespeed/modifiers/mobs.dm
@@ -111,6 +111,12 @@
/datum/movespeed_modifier/mob_config_speedmod
variable = TRUE
+ blacklisted_movetypes = FLOATING
+ flags = IGNORE_NOSLOW
+
+/datum/movespeed_modifier/mob_config_speedmod_floating
+ variable = TRUE
+ movetypes = FLOATING
flags = IGNORE_NOSLOW
/datum/movespeed_modifier/liver_cirrhosis
@@ -120,3 +126,30 @@
/datum/movespeed_modifier/active_block
variable = TRUE
flags = IGNORE_NOSLOW
+
+/datum/movespeed_modifier/sprinting
+ flags = IGNORE_NOSLOW
+ blacklisted_movetypes = FLOATING
+ priority = 100
+
+/// for speed reasons this is sorta copypasty.
+/datum/movespeed_modifier/sprinting/apply_multiplicative(existing, mob/target)
+ . = existing
+ if(target.m_intent != MOVE_INTENT_RUN)
+ return
+ if(isliving(target))
+ var/mob/living/L = target
+ if(!(L.mobility_flags & MOBILITY_STAND))
+ return
+ var/static/datum/config_entry/number/movedelay/sprint_max_tiles_increase/SMTI
+ if(!SMTI)
+ SMTI = CONFIG_GET_ENTRY(number/movedelay/sprint_max_tiles_increase)
+ var/static/datum/config_entry/number/movedelay/sprint_speed_increase/SSI
+ if(!SSI)
+ SSI = CONFIG_GET_ENTRY(number/movedelay/sprint_speed_increase)
+ var/static/datum/config_entry/number/movedelay/sprint_absolute_max_tiles/SAMT
+ if(!SAMT)
+ SAMT = CONFIG_GET_ENTRY(number/movedelay/sprint_absolute_max_tiles)
+ var/current_tiles = 10 / max(existing, world.tick_lag)
+ var/minimum_speed = 10 / min(max(SAMT.config_entry_value, current_tiles), current_tiles + SMTI.config_entry_value)
+ . = min(., max(minimum_speed, existing - SSI.config_entry_value))
diff --git a/code/modules/power/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm
index e3fdbd3654..d61220a1a2 100644
--- a/code/modules/power/supermatter/supermatter.dm
+++ b/code/modules/power/supermatter/supermatter.dm
@@ -928,8 +928,15 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
var/mob/living/user = AM
if(user.status_flags & GODMODE)
return
- message_admins("[src] has consumed [key_name_admin(user)] [ADMIN_JMP(src)].")
- investigate_log("has consumed [key_name(user)].", INVESTIGATE_SUPERMATTER)
+ var/add
+ if(user.mind?.assigned_role == "Clown")
+ var/denergy = rand(-1000, 1000)
+ var/ddamage = rand(-150, clamp(150, 0, (explosion_point - damage) + 150))
+ energy += denergy
+ damage += ddamage
+ add = ", adding [denergy] energy and [ddamage] damage to the crystal"
+ message_admins("[src] has consumed [key_name_admin(user)] [ADMIN_JMP(src)][add].")
+ investigate_log("has consumed [key_name(user)][add].", INVESTIGATE_SUPERMATTER)
user.dust(force = TRUE)
if(power_changes)
matter_power += 200
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index 1369115b0d..1c3a0d230f 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -154,8 +154,8 @@
shake_camera(user, recoil + 1, recoil)
if(stam_cost) //CIT CHANGE - makes gun recoil cause staminaloss
- var/safe_cost = clamp(stam_cost, 0, STAMINA_NEAR_CRIT - user.getStaminaLoss())*(firing && burst_size >= 2 ? 1/burst_size : 1)
- user.adjustStaminaLossBuffered(safe_cost) //CIT CHANGE - ditto
+ var/safe_cost = clamp(stam_cost, 0, user.stamina_buffer)*(firing && burst_size >= 2 ? 1/burst_size : 1)
+ user.UseStaminaBuffer(safe_cost)
if(suppressed)
playsound(user, fire_sound, 10, 1)
@@ -590,6 +590,9 @@
update_icon()
/obj/item/gun/proc/getinaccuracy(mob/living/user, bonus_spread, stamloss)
+ return 0 // Replacement TBD: Exponential curved aim instability system.
+
+/*
if(inaccuracy_modifier == 0)
return bonus_spread
var/base_inaccuracy = weapon_weight * 25 * inaccuracy_modifier
@@ -612,6 +615,7 @@
if(mult < 0) //accurate weapons should provide a proper bonus with negative inaccuracy. the opposite is true too.
mult *= 1/inaccuracy_modifier
return max(bonus_spread + (base_inaccuracy * mult), 0) //no negative spread.
+*/
/obj/item/gun/proc/getstamcost(mob/living/carbon/user)
. = recoil
diff --git a/code/modules/projectiles/guns/ballistic/shotgun.dm b/code/modules/projectiles/guns/ballistic/shotgun.dm
index ecf6e538b8..7b3ac36ef6 100644
--- a/code/modules/projectiles/guns/ballistic/shotgun.dm
+++ b/code/modules/projectiles/guns/ballistic/shotgun.dm
@@ -44,10 +44,9 @@
if(HAS_TRAIT(user, TRAIT_FAST_PUMP))
recentpump = world.time + 2
else
+ if(!user.UseStaminaBuffer(2, warn = TRUE))
+ return
recentpump = world.time + 10
- if(istype(user))//CIT CHANGE - makes pumping shotguns cost a lil bit of stamina.
- user.adjustStaminaLossBuffered(2) //CIT CHANGE - DITTO. make this scale inversely to the strength stat when stats/skills are added
- return
/obj/item/gun/ballistic/shotgun/blow_up(mob/user)
. = 0
diff --git a/html/changelogs/AutoChangeLog-pr-13193.yml b/html/changelogs/AutoChangeLog-pr-13193.yml
new file mode 100644
index 0000000000..980a9366d8
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-13193.yml
@@ -0,0 +1,4 @@
+author: "silicons"
+delete-after: True
+changes:
+ - tweak: "medium screens are better now"
diff --git a/html/changelogs/AutoChangeLog-pr-13530.yml b/html/changelogs/AutoChangeLog-pr-13530.yml
new file mode 100644
index 0000000000..b99ed1c545
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-13530.yml
@@ -0,0 +1,4 @@
+author: "silicons"
+delete-after: True
+changes:
+ - rscadd: "Clowns now have unpredictable effects on supermatter crystals when dusting from contact."
diff --git a/modular_citadel/code/modules/mob/living/carbon/damage_procs.dm b/modular_citadel/code/modules/mob/living/carbon/damage_procs.dm
index 17c3f17ad8..0457b853b7 100644
--- a/modular_citadel/code/modules/mob/living/carbon/damage_procs.dm
+++ b/modular_citadel/code/modules/mob/living/carbon/damage_procs.dm
@@ -1,16 +1,3 @@
-/mob/living/carbon/adjustStaminaLossBuffered(amount, updating_health = 1)
- if(status_flags & GODMODE)
- return 0
- if(CONFIG_GET(flag/disable_stambuffer))
- return
- var/directstamloss = (bufferedstam + amount) - stambuffer
- if(directstamloss > 0)
- adjustStaminaLoss(directstamloss)
- bufferedstam = clamp(bufferedstam + amount, 0, stambuffer)
- stambufferregentime = world.time + 10
- if(updating_health)
- update_health_hud()
-
/mob/living/carbon/adjustStaminaLoss(amount, updating_health = TRUE, forced = FALSE, affected_zone = BODY_ZONE_CHEST)
if(!forced && (status_flags & GODMODE))
return FALSE
diff --git a/modular_citadel/code/modules/mob/living/damage_procs.dm b/modular_citadel/code/modules/mob/living/damage_procs.dm
deleted file mode 100644
index a399c17c71..0000000000
--- a/modular_citadel/code/modules/mob/living/damage_procs.dm
+++ /dev/null
@@ -1,2 +0,0 @@
-/mob/living/proc/adjustStaminaLossBuffered(amount, updating_health = TRUE, forced = FALSE)
- return
diff --git a/modular_citadel/code/modules/mob/living/living.dm b/modular_citadel/code/modules/mob/living/living.dm
index 01867e9dcc..b5be028607 100644
--- a/modular_citadel/code/modules/mob/living/living.dm
+++ b/modular_citadel/code/modules/mob/living/living.dm
@@ -1,5 +1,3 @@
-/mob/living
-
/atom
var/pseudo_z_axis
@@ -24,26 +22,3 @@
if(.)
pseudo_z_axis = newloc.get_fake_z()
pixel_z = pseudo_z_axis
-
-/mob/living/carbon/update_stamina()
- var/total_health = getStaminaLoss()
- if(total_health >= STAMINA_SOFTCRIT)
- if(!(combat_flags & COMBAT_FLAG_SOFT_STAMCRIT))
- ENABLE_BITFIELD(combat_flags, COMBAT_FLAG_SOFT_STAMCRIT)
- else
- if(combat_flags & COMBAT_FLAG_SOFT_STAMCRIT)
- DISABLE_BITFIELD(combat_flags, COMBAT_FLAG_SOFT_STAMCRIT)
- if(total_health)
- if(!(combat_flags & COMBAT_FLAG_HARD_STAMCRIT) && total_health >= STAMINA_CRIT && !stat)
- to_chat(src, "You're too exhausted to keep going...")
- set_resting(TRUE, FALSE, FALSE)
- SEND_SIGNAL(src, COMSIG_DISABLE_COMBAT_MODE)
- ENABLE_BITFIELD(combat_flags, COMBAT_FLAG_HARD_STAMCRIT)
- filters += CIT_FILTER_STAMINACRIT
- update_mobility()
- if((combat_flags & COMBAT_FLAG_HARD_STAMCRIT) && total_health <= STAMINA_SOFTCRIT)
- to_chat(src, "You don't feel nearly as exhausted anymore.")
- DISABLE_BITFIELD(combat_flags, COMBAT_FLAG_HARD_STAMCRIT | COMBAT_FLAG_SOFT_STAMCRIT)
- filters -= CIT_FILTER_STAMINACRIT
- update_mobility()
- update_health_hud()
diff --git a/modular_citadel/icons/ui/screen_gen.dmi b/modular_citadel/icons/ui/screen_gen.dmi
index d006185a3c..510d312f1c 100644
Binary files a/modular_citadel/icons/ui/screen_gen.dmi and b/modular_citadel/icons/ui/screen_gen.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index 9587a792de..750486e032 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -139,6 +139,7 @@
#include "code\__DEFINES\combat\attack_types.dm"
#include "code\__DEFINES\combat\block.dm"
#include "code\__DEFINES\combat\block_parry.dm"
+#include "code\__DEFINES\combat\stamina_combat.dm"
#include "code\__DEFINES\dcs\flags.dm"
#include "code\__DEFINES\dcs\helpers.dm"
#include "code\__DEFINES\dcs\signals.dm"
@@ -293,6 +294,7 @@
#include "code\controllers\configuration\entries\plushies.dm"
#include "code\controllers\configuration\entries\policy.dm"
#include "code\controllers\configuration\entries\resources.dm"
+#include "code\controllers\configuration\entries\stamina_combat.dm"
#include "code\controllers\subsystem\acid.dm"
#include "code\controllers\subsystem\adjacent_air.dm"
#include "code\controllers\subsystem\air.dm"
@@ -2283,7 +2285,6 @@
#include "code\modules\jobs\job_types\station_engineer.dm"
#include "code\modules\jobs\job_types\virologist.dm"
#include "code\modules\jobs\job_types\warden.dm"
-#include "code\modules\jobs\map_changes\map_changes.dm"
#include "code\modules\keybindings\bindings_atom.dm"
#include "code\modules\keybindings\bindings_client.dm"
#include "code\modules\keybindings\focus.dm"
@@ -2465,6 +2466,7 @@
#include "code\modules\mob\living\login.dm"
#include "code\modules\mob\living\logout.dm"
#include "code\modules\mob\living\say.dm"
+#include "code\modules\mob\living\stamina_buffer.dm"
#include "code\modules\mob\living\status_procs.dm"
#include "code\modules\mob\living\taste.dm"
#include "code\modules\mob\living\update_icons.dm"
@@ -3604,7 +3606,6 @@
#include "modular_citadel\code\modules\mentor\mentorpm.dm"
#include "modular_citadel\code\modules\mentor\mentorsay.dm"
#include "modular_citadel\code\modules\mob\cit_emotes.dm"
-#include "modular_citadel\code\modules\mob\living\damage_procs.dm"
#include "modular_citadel\code\modules\mob\living\living.dm"
#include "modular_citadel\code\modules\mob\living\carbon\carbon.dm"
#include "modular_citadel\code\modules\mob\living\carbon\damage_procs.dm"