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/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/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 5e76d506ba..a7d96fe6eb 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/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/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/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/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/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/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..e8c764345e 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" @@ -2465,6 +2467,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 +3607,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"