CleverRaven / CleverRaven/Cataclysm-DDA

Modeling muscle use through "strain"

Open
#59,862 6 comments 5 reactions 0 assignees View on GitHub
(P3 - Medium) <Suggestion / Discussion> Game: Mechanics Change Mechanics: Character / Player Mechanics: Effects / Skills / Stats
Dominant language
C++
Stars
13.1k
Forks
4.6k
Avg merge
1d 23h
Merged PRs (30d)
186

Description

### Is your feature request related to a problem? Please describe.

The model we've developed for improving cardiovascular fitness to increase stamina seems to be working quite well as a general representation of exercise, but we still can't exercise other stats. Notably, strength is fully static and should not be.

We need a cohesive model for physical strength. However, a rational model for increasing physical strength requires better representation to distinguish muscle contraction from aerobic exercise. This means breaking up stamina *again* to distinguish "I ran so I am tired" from "I have been lifting and my arms don't work anymore". This follows a [preceding recommendation](https://github.com/CleverRaven/Cataclysm-DDA/issues/59858) to change the term `stamina` into `breath` and `fatigue` into `sleepiness` to avoid confusion.

### Solution you would like.

Once we have clarified our terminology we need to add a new short term stamina resource, `muscle strain` or `strain` for short. (Exertion has been suggested as a good alternative). Strain represents using your muscles until failure, and will cross paths with current stamina a lot at first. However part of the reason to change stamina to `breath` is to help clarify where we use the two.

- `strain` is represented as a resource pool that increases as you use your muscle, and drops over time.
- `strain` is predominately upper body, although it's abstracted so some things that use legs also use `strain`.
- `strain`, like `breath`, recovers rapidly after it's used up.
- `strain` recovers slower as `weariness` rises, and high levels of `weariness` will reduce your max strain so that your muscles fatigue faster
- Rising `strain` doesn't cause problems until it hits a critical level at which your effective strength rapidly drops off.
- The UI for `strain` should show it as a meter that is empty when strain is the lowest it can currently go, and is maxed when strain is at your tolerance cap (it can go higher than this but the meter won't change at that point). This keeps the UI easy to read even as the amount of strain you can tolerate moves around dynamically.

Later we'll add a second hidden resource to track how many times you've maxed out your strain recently, but that will be skipped in this Issue to keep it simple.

#### Activities that affect strain

Note that your current strength isn't used to calculate strain. Instead your current strength determines how much strain you can tolerate, but the amounts of strain a given activity causes should be pretty constant.

1. Swinging a melee weapon: Inflicts `strain` at a rate of `std::min( 5 * weapon_weight_kg, 5 )` (at some point we may wish to figure weapon type and length in here as well, swinging a sledgehammer is harder than swinging a balanced sword - but for now this should allow us to get a reasonable system going.)
2. Drawing a bow or crossbow, holding a drawn bow: Inflicts `strain` at a rate of `weapon_strength_requirement` to draw, and every turn you hold it drawn.
3. Throwing an object: Inflicts `strain` equal to swinging it in melee.
4. Carrying: Carrying more than 100% of your max carry weight should inflict 1 `strain` per minute for every kg over carry limit even if holding still.
5. Moving while overburdened: If you are over your carry weight, every time you move, inflict the amount of `strain` calculated in 4 above.
6. Climbing: Climbing up or down a z-level (not just transitioning but specifically climbing) inflicts strain equal to `current_carry_weight_kg`
7. Dragging objects/vehicles: Adds `strain` equal to the strength required to move the object.
8. Inventory actions (picking up or dropping objects, moving between containers eg): Add an amount of strain equal to `std::min( (weight_in_kg - 5), 0 ) - this means that items below 5kg (about 10 lbs) never add strain.
9. Other activities: Eventually certain high-energy activities may add strain, so that if you suddenly drop your shovel from digging trenches and start wailing on a zombie, you aren't fresh as if you've been resting. Eventually we should add the ability to intentionally strain yourself with an exercise activity. These can wait.

#### Strain tolerance

`base_strain_cap = base_strength * ( 10 - current_weariness_level )`

- We can cache base_strain_cap and only recalculate it when weariness level changes, or every 24h.
- Reaching your strain cap causes your effective strength to drop for most, but not all, activities:

`if(current_strain>base_strain_cap): effective_strength = base_strength * (base_strain_cap/current_strain) ^ 2`

- If an activity would add more strain than your current `effective_strength` you get an error, eg. "your arms are too tired to swing the %n". Do not make this check if `effective_strength=base_strength`, though: this should only kick in when you start wearing out.
- We may wish to nerf pain's effects on your strength here. Pain should still have an impact on your effective strength, but not nearly as profound as we are now moving some of that model over to strain.
- Maxing out strain can also cause short term mild pain, increasing if you push past your cap. Later, pushing past your cap should have a risk of giving you a muscle pull.

In melee combat, discounting weariness/recovery, this model lets you swing a light weapon a number of times equal to double your strength, without breaks, before hitting your cap. Then your damage starts to rapidly fall off, assuming your weapon doesn't have a minimum strength to wield. At strength 8, you'd be unable to keep swinging when your current_strain reaches about 100, or about 20 swings of a light weapon - remember this is assuming you're taking no breaks and recovering no strain. That's about 20 seconds of protracted swinging, and it would then take about 12 seconds to recover all your strength (until we add lactate buildup). Those last four swings are going to get weaker each time. Weapons that weigh more than 5kg will allow fewer swings, assuming an equal attack speed, but bear in mind that if something takes longer to swing, it won't necessarily accrue strain as quickly because of the time gap. This will need some playtesting to sort out fully.

Notable exceptions:

- ideally, reducing `effective_strength` by running out of strain should not affect carry weight. If you are at your strain cap and carrying higher than your carry weight, you should suffer a different penalty. For the short term, inflicting pain due to back sprain is sufficient. When we have a wounds system we can add specifically shoulder and back sprains for overburdening yourself. In this model we'd remove the current existing effects for traveling overburdened, replacing them with this.

#### Strain recovery

Strain_recovery runs every second in this simple model. Later we will add things that can slow this down over time.

Each time strain_recovery runs, reduce strain by `std::max( base_strength - current_weariness_level ), 1 )` if strain has not increased since last recovery. If strain has increased since last recovery, do not decrease strain this time.

We use base_strength here because your strain increases proportional to your strength and it shouldn't take longer to recover when you are stronger. At higher strengths your weariness won't impact your recovery quite as much.

#### Impact on weariness

We should make sure that for any action where strain is calculated, weariness does not have any impact on speed. Most notably, your weapon attacks should not be slowed by weariness, because we're instead representing that by encouraging you to take short breaks and recover muscle function.

Every point of strain you gain should also count as 1 kcal burned for the purposes of increasing weariness - this should not increase dietary needs though. Later, the lactate system will also give ways for strain to increase weariness if you push yourself to your limits.

#### Changes to current stamina (renamed Breath)

With strain implemented, stamina should have less of a role in the systems that now implement strain. I suggest we just flat reduce stamina costs in any affected combat area by 1/2, to start. The target balance should be that melee combat is at roughly the same setpoint as before strain was added, for a starting average character, but the limiting resource is strain rather than breath. When you run out of strain you should still have a little energy to run with.

Ultimately this will probably be seen as a nerf to melee but I'd like it to be a small buff mechanically, even before we get to improving your strength, due to that extra ability to still run away.

### Describe alternatives you have considered.

It would be possible to use our current stamina as a catchall for both aerobic and resistance exercise, but it leads to a lot less reasonable physical management, where doing anything that trains cardio also gains strength, and it makes for a more boring game where you have only a single improvable physical stat that improves with all the same actions.

It would be worth considering having current breath impact strain recovery, if you're out of breath it takes longer to clear your muscles.

### Additional context

Next up is adding lactate, without which this mechanic is mostly vestigial. Without lactate it is almost trivially easy to continually recover all your strain in a few seconds. Lactate will make it so that you can initially recover your strain very fast, but the more you do that, the longer you have to wait the next time, until your arms become noodles.

I would like to review and balance the numbers here based on boxing match information at various levels of skill. If someone can get more numbers and hlep me out that'd be awesome

Contributor guide

Open the contributing guide

Research direction

The issue names no files, tests, or entry points. Start by mapping the existing stamina, weariness, strength, melee, carrying, climbing, inventory, and UI systems before evaluating the proposed strain resource. Done would require implementing the specified strain effects, recovery, tolerance, activity impacts, display, and reduced stamina costs, followed by balancing and playtesting.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
game-dev
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.