Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Weakpoints #51305

Closed
I-am-Erk opened this issue Sep 2, 2021 · 15 comments
Closed

Weakpoints #51305

I-am-Erk opened this issue Sep 2, 2021 · 15 comments
Labels
<Enhancement / Feature> New features, or enhancements on existing Game: Balance Balancing of (existing) in-game features. Game: Mechanics Change Code that changes how major features work Monsters Monsters both friendly and unfriendly. (P3 - Medium) Medium (normal) priority

Comments

@I-am-Erk
Copy link
Member

I-am-Erk commented Sep 2, 2021

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

Monsters with high armour, which are quite common, have universal armour coverage. There are no holes for faces or arms, no soft spots over joints. We need gaps in armour. #51304 has begun implementing this critical feature, I am writing this issue to cover some of the ways we need to structure this for expandability.

Describe the solution you'd like

Gaps in armour, which are independent of your attack/critical roll but still based on skill. We'll call these weakpoints, because eventually this won't be just about armour but also about special hits that cause certain effects.

These gaps should be individually listed in an array. Initially it's probably easiest to do it inline in the monster definition. Some day it might be nice to have json objects for these for easy moddability and for easily repeating some common basic patterns.

In the end, our JSON should look something like this. Don't be daunted, because I am going to layout a stepwise approach to implementation and the first step should be easy.

"weakpoints": [
  {
    "name": "narrow armour gap",
    "armour": { "all": 0.2 },
    "damage": 1.2,
    "critical": 2,
    "size": "small",
    "difficulty": 8
  },
  {
    "name": "soft joints",
    "armour": { "all": 0.8, "piercing": 0.7 },
    "critical": 0.5,
    "effects": [ "knockdown" ],
    "size": "large",
    "difficulty": 3
  }
]

First pass

I'll write this part up and then take a break but I want this out so the PR can proceed.

A first pass only needs to support a name, an armour value, and a difficulty score. So, a first pass weakpoints array should look like this:

"weakpoints": [
  {
    "name": "narrow armour gap",
    "armour": { "all": 0.2 },
    "difficulty": 8
  },
  {
    "name": "soft joints",
    "armour": { "all": 0.8, "piercing": 0.7 },
    "difficulty": 3
  }
]

name: This is the string we use both to describe what this is for the content creators, and to generate an attack string, along the lines of "You hit the in its soft joints, doing 30 damage!".

armour: This is a multiplier for existing armour scores. By default it is 1.0, ie. the same armour as the base monster. An entry of all overrides that for all armour types, and individual values can be specified separately to override (not combine with) the value in all. So:

  • "armour": { "all": 0.2 } - all the monster's armour is reduced to 20% of base value.
  • "armour": { "piercing": 0.2 } - the monster's piercing armour is reduced to 20% of base value, the other armours are still full strength
  • "armour": { "all": 0.2, "piercing": 0.5 } - all the monster's armour is reduced to 20% of base value, except piercing armour which is at 50% of the base value.

"difficulty" is how likely you are to hit this weak spot. On a first pass, think of this as a percentage weight. Here is our starting algorithm:

  1. Determine if the attack hits, and if it is a critical. If not, we will not evaluate weakpoints at all. If it hits, move to step 2. Whether or not the attack is a critical is irrelevant.
  2. Generate a ranked probability list of weakpoints based on their difficulty values, with "no weak point" filling the rest. We assume a max difficulty of 10, so the final weight is 10 - difficulty. So in the above example, with a difficulty 8 and 3 weakpoint, the list would be: 93%: no weak point; 2%: narrow armour gap; 7%: soft joints.
  3. Randomly roll on the weighted list of weakpoints to choose which one you hit. Apply its armour multiplier to the incoming attack, then apply critical effects and things as usual.

Second pass: adding a role for skill to your chance to hit a weakpoint

See #51482 for a more up to date conversation on this issue.

~~Once we have a basic ability to hit weakpoints at random, we should add a role for skill to making you better at hitting them. There are a number of skill factors to consider:

  • Your inherent ability - dexterity and perception
  • Your skill with your weapon, and with weapons in-class (melee/marksmanship)
  • How many monsters of this class (eg. skeleton type zombies, electric type zombies, blob type enemies) you have killed or dissected, represented by a proficiency. This doesn't actually need to be added in the second pass but it's something we should have eventually.~~

These factors will combine with the difficulty of the weakpoint, to create a final weight value, taking the place of the difficulty score in the weighted list we built in part 1. I propose the following maths:

weight = ( weapon_skill + weapon_class_skill + dex/8 + per/8 + proficiency_factors ) - difficulty, min ((10 - difficulty) / 2)`, min 0 on that second equation

~~This makes it so that easy weakpoints can still be hit randomly even if you have no skill (that min 10-difficulty/2 bit) but harder-to-hit weakpoints require skill, and your skill will always have a significant role. ~~

once we calculate weight in this way, we can go back and do the same weighted ranking as before.

addendum: I realized a day after writing this that I also really want the ability to define a weakpoint called default, which allows us to specify special data if an attack hits no weakpoints at all. Most of the time we don't need this, but after Part 3, it can allow us to do things like make monsters take reduced damage or not be susceptible to criticals unless they're hit in a weak spot. by Part 4 it would become an alternate implementation of #45142, in that we can make monsters immune to attacks on their main body and only get slowed down or knocked back.

Third pass: Special damage and critical hit effects

Once we have a role for skill we can start getting into colourful stuff, specific data for each weakpoint. I really want this feature because it allows us to represent various types of critical organ and joint injury, especially in combination with part four.

  1. "damage": This very self-explanatory field is a multiplier for incoming damage, applied after the armour is taken into effect. So first you hit the weak spot, reduce damage by the modified armour value, and then multiply your final damage by the damage modifier. This can represent specific points that are weak in armour but not particularly valuable spots (a weak point over the fleshy part of your arm, eg), or it can represent weak points where even if you didn't specifically roll a critical hit, it's going to do some extra damage off the bat (the eye, for example).
  2. "critical": This works exactly like damage, but is applied to critical hits. If this field is blank, the damage field is used for both regular and critical hits. If both this field and damage exist, use critical instead of - not as well as - damage. This can mainly be used to represent weak points over top of vulnerable areas that still might be challenging to hit, like stabbing someone in the liver by reaching up from their abdomen. It could also represent areas which are less suscpetible to critical hits because they have no majorly important underlying structures. This multiplier should be applied to the final critical hit damage, it doesn't replace existing critical hit modifiers. However, if set to any value less than 1, this parameter should just downgrade all critical hits to regular hits.

Fourth pass: Adding special effects

This is actually one of my favourites, but I have put less thought into how to implement it. As I currently see it, we should have an array of effects that may be applied if the weak point is hit. For example, hitting someone in the hamstring might not do much more damage but could knock them down or cripple their speed.

The array would look something like: "effects": [ "knockdown", "hamstring" ]. Presently I am assuming that the severity, duration, and likelihood of those effects would be determined by the effects themselves. However we might also need some data on how to determine if the effect happens, so it might need to be more complex than that. Another option would be like:

"effects": [
  { "id": "knockdown", "damage_percent": [ 0, 100 ], "chance": 0, "damage_affect_chance": true },
  { "id": "hamstring", "damage_percent": [ 10, 50 ], "intensity": [ 1, 2 ], "chance": 10, "damage_affect_chance": false },
  { "id": "hamstring", "damage_percent": [ 51, 100 ], "intensity": [ 3, 4 ], "chance": 15, "damage_affect_chance": false }
]

If we go with this more complex model then:

  • damage_percent represents what percent of the monster's max HP the attack inflicted in damage
  • intensity determines how intense the effect is applied. Optionally, we might make it so higher damage is likely to inflict higher intensity, but personally I don't think that's needed... as you can see above, if we want that behaviour we can represent it by putting the same effect in twice.
  • chance is the percent chance this effect will occur if the conditions are met
  • damage_affect_chance: if true, add the damage of the incoming attack (as a percent of the monster's base HP) to the chance score before determining if it happens.

In the above example, if an attack does, say 30% of the target's base HP, then it has a 30% chance of inflicting knockdown and a 10% chance of hamstringing the target at intensity 1 or 2.

This more complex implementation would probably be the ideal, but it's a lot more work and I'm not convinced we need it. That said, it would allow me to add headshots to Dark Days of the Dead, so PLEASE DO IT. Gah.

Fifth pass: determining if your weapon can hit the weakpoint

At this point we add that "size" attribute, which accepts "small", "medium", and "large", as well as no value. If there is no value, we assume any weapon can hit this weakpoint. Otherwise, this is for melee attacks only at this point, although later we may want to add the infrastructure to get something similar for ranged attacks.

In very short this pass would look at the to-hit data of a weapon, and use that to determine if it can hit a small weakpoint. note that the size of the weakpoint here is completely independent of its difficulty: a weakpoint could be small but easy to hit, if there are a lot of them and they are near the center of mass, but a long broad weapon might just be too large to hit them.

Other features to consider

It would be nice if, while aiming, you had the option to toggle "aim for weakpoints" at a penalty to your to-hit chance but an increase to your "chance to hit weakpoints". I don't know the best way to do the same thing in melee though.

Describe alternatives you've considered

Something simpler?

Additional context

I think it's important these stats be in an attribute block just because of how many inline stats there already are in a monster entry.

@I-am-Erk I-am-Erk added <Enhancement / Feature> New features, or enhancements on existing Game: Mechanics Change Code that changes how major features work Game: Balance Balancing of (existing) in-game features. Monsters Monsters both friendly and unfriendly. (P3 - Medium) Medium (normal) priority labels Sep 2, 2021
@Joshua-Chin
Copy link
Contributor

Thanks for writing this up! There's a lot more depth to armor gaps than I first thought. I agree with much of what you wrote, and I have a few thoughts about the first pass:

  • We might want both an armor multiplier and an offset (ax + b).
    • Some monsters, like giant ants have weak spots where their armor is comparatively thinner. Other monsters, like police zombies, have weak spots with defensive values unrelated to their armor (i.e. riot armor/tough skin). Having both an multiplier and offset would make it more natural to represent both types of defenses.
  • Contributors might prefer specifying a coverage value instead of a difficulty.
    • In most cases, it appears that difficulty represents a cutoff (recipes / construction), or a 50% breakpoint (hacking, bionics). Weakpoint difficulty doesn't seem to correspond to either. On the other hand, base coverage values are relatively easy to interpret. Under the hood, we can still derive the difficulty and proceed with the skill based equation.

Please let me know what you think, or if I misinterpreted anything.

@Venera3
Copy link
Member

Venera3 commented Sep 8, 2021

When they get hooked up to skills, I think it would be useful to add a melee/ranged_penalty to weakpoint coverage definitions, to show that some of these have a different hit chances depending on engagement range and allow some targeted balancing for guns. Example: shelled monsters hiding their arm/neck weakpoints at range but exposing them in melee, or the weak spots of flying critters being harder to get a bead on outside of grappling range.

@I-am-Erk
Copy link
Member Author

I-am-Erk commented Sep 9, 2021

Yeah I'm actually trying to write a new skill and weapon effect algorithm and would like that to be part of it. Even if coverage just accepted melee and range as separate arguments in fact.

@Joshua-Chin
Copy link
Contributor

Adding separate melee / ranged coverage values shouldn't be too hard. Most of the wiring needed will be handled as a part of the "skill based hit chance" feature.

I opened a separate issue (#51482) to discuss the hit chance formula. I have some rough ideas on what the formula should look like, but I'm not completely decided.

@Malorn-Deslor
Copy link
Contributor

I very much agree with the idea that weakpoints should exist, should make combat less deterministic, which is always a great thing.

I am curious, however, why the existing coverage system was not applied to the armor of creatures, the same way it would be applied to players.

@Venera3
Copy link
Member

Venera3 commented Sep 20, 2021

Monsters don't have limbs (beyond some effect weirdness) or actual armor, and the consensus is that that also won't be a thing.

@Malorn-Deslor
Copy link
Contributor

Malorn-Deslor commented Sep 20, 2021

Sorry, allow me to clarify. What I meant is not that items should exist on monsters, but rather that the concept of 'coverage' could be used to produce a unified conceptual framework that would apply to players or monsters.

I guess I see a lot of value in a unified system, where attacking or being attacked occurs using the same basic rules. It also would seem to be much easier to create. Of course, I'm not the one doing the creating, so I suppose any shovel which moves dirt is a good shovel.

@I-am-Erk
Copy link
Member Author

Monsters are a lot simpler than characters. The coverage system assumes there is complex stuff being modelled under the armour to hit, but the attacker has no fancy targeting system. The weakpoints system assumes the target is modelled simply and the attacker has a lot of granular information about their skill level. Making coverage work for monsters would require adding a ton of infrastructure to it that isn't needed for players.

@Malorn-Deslor
Copy link
Contributor

Well, if it's actually less complex to do it this way, then that works well enough. Thank you for taking the time to explain.

@Joshua-Chin
Copy link
Contributor

It looks like weakpoints are getting some attention on Reddit. I decided to run some numbers on the scenario described:

Zombie Cops have a combined weak point coverage of 30% and a base stab armor of 6. Among the weak points, the maximum stab armor is 1.5 (a multiplier of 0.25).

Makeshift wooden arrows fired from a short bow have 5 base damage. It can't damage a Zombie Cop when it misses a weak point. When hitting a weak point, it deal an average of 10 damage on a Good Hit!.

Assuming 0 skill and 8/8/8/8 stats, it takes about 27 good hits to kill a Zombie Cop. With 3 skill, the weak point hit rate goes up to ~54% and it takes about 15 good hits. Note: these values are slightly conservative, because several weak points have no stab armor (head, eyes, etc.).

@I-am-Erk
Copy link
Member Author

That's sounding pretty correct. I think the main thing with cops and soldiers both is that they can probably get a bit of a buff to base armour now that they have so many weak points.

@LyleSY
Copy link
Contributor

LyleSY commented Sep 24, 2021

I'm starting to add this to DinoMod. One thing I've wanted a long time is to set "the soft underbelly" as a weak point, but only available when the monster is knocked to the ground. This is how every apex dino predator appears to have hunted, especially against ankylosaurids and ceratopsians. Something similar could be interesting with sauropods, where only the knee is available, but once it's hit the monster is slowed and "the soft underbelly" appears
https://books.google.com.ph/books?id=IJ9nBUq_hKkC&printsec=frontcover&source=gbs_atb&hl=en#v=onepage&q=tipping&f=false

@I-am-Erk
Copy link
Member Author

I'm starting to add this to DinoMod. One thing I've wanted a long time is to set "the soft underbelly" as a weak point, but only available when the monster is knocked to the ground. This is how every apex dino predator appears to have hunted, especially against ankylosaurids and ceratopsians. Something similar could be interesting with sauropods, where only the knee is available, but once it's hit the monster is slowed and "the soft underbelly" appears
https://books.google.com.ph/books?id=IJ9nBUq_hKkC&printsec=frontcover&source=gbs_atb&hl=en#v=onepage&q=tipping&f=false

So there are two things this makes me think of, they're not really the same.

  1. The ability to add a weakpoint via an effect. While cool, I am not sure this is something with enough utility to be considered a baseline thing.
  2. The ability to give a monster a "variable" weakpoint that is only active when a conditional is met. So for example, if the monster has an effect active.
    We could also consider a more hard-coded variant of 2, a weakpoint that's only active when the monster is knocked down, since that's the most common situation to expose a weakpoint.

Anyway for your dinosaurs I'd point out that a human-sized attacker can probably get the weak underbelly even when it's not knocked down...

@Venera3
Copy link
Member

Venera3 commented Sep 24, 2021

For what it's worth, I have plans to include "add an effect to yourself if you do this/hit with this" to monster special attacks, so keying weakpoint exposure from that would be nice.

@Joshua-Chin
Copy link
Contributor

The C++ side of the weak points project is wrapping up! We've implemented the core features, and updated the documentation. All that's left is a few minor work items and possible bug fixes.

@I-am-Erk Thanks for your guidance on this project! I've had a surprising amount of fun, and I'm happy to see people use a feature that I helped build.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
<Enhancement / Feature> New features, or enhancements on existing Game: Balance Balancing of (existing) in-game features. Game: Mechanics Change Code that changes how major features work Monsters Monsters both friendly and unfriendly. (P3 - Medium) Medium (normal) priority
Projects
None yet
Development

No branches or pull requests

5 participants