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

Birdshot demolishes brick walls #55541

Closed
oosyrag opened this issue Feb 20, 2022 · 18 comments · Fixed by #74529
Closed

Birdshot demolishes brick walls #55541

oosyrag opened this issue Feb 20, 2022 · 18 comments · Fixed by #74529
Labels
Fields / Furniture / Terrain / Traps Objects that are part of the map or its features. Good First Issue This is a good first issue for a new contributor (S2 - Confirmed) Bug that's been confirmed to exist

Comments

@oosyrag
Copy link
Contributor

oosyrag commented Feb 20, 2022

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

Screenshot_20220220-110511_6fe8938556442e3d4f710ab4f9a16601__01

A single round of birdshot from point blank range destroying 3 tiles of adobe brick walls is absurd.

Solution you would like.

A single round of birdshot should not demolish 3 tiles worth of adobe brick walls from one tile away.

Birdshot would have trouble penetrating wood, much less brick.

https://images.guns.com/wordpress/2017/06/BirdshotBuckshotarticle3.jpg

The spread of birdshot at 10 yards (tiles) is less than a 1 foot.

Describe alternatives you have considered.

Birdshot can remain the most economical remodeling tool in the game.

Additional context

No response

@PlutusPleion
Copy link
Contributor

Does the game model each single pellet or is it conglomerated into one damage instance? If it is counted as one, that could be why. There's just a min damage and a max damage where you have a chance to break the wall.

Another point is the penetration and hardness which is also not simulated for wall breaking.

@oosyrag
Copy link
Contributor Author

oosyrag commented Feb 20, 2022

If I'm reading the Json right, birdshot is made of 100 projectiles that do .2 damage each. https://nornagon.github.io/cdda-guide/#/item/shot_bird

00 shot has 9 projectiles that do 15 damage each.

I'm not sure why their respective listed damages are 50 and 60, or how that number is applied.

@LeahLuong
Copy link

Are you sure the issue is birdshot = OP & not adobe brick = papier mâché??

@Jarewill
Copy link
Contributor

Adobe brick is made from just soil, which makes it not that as strong as normal bricks.
In my testing, birdshot was only able to destroy an adobe brick wall, but not a normal brick wall, log wall, metal wall or stone wall.

Now, while it's not as strong as normal bricks, it still does seem weird how birdshot was able to destroy it so easily.
Also in my testing I wasn't able to replicate it breaking multiple walls at once, so I don't know what's up with that.

@catdach
Copy link
Contributor

catdach commented Feb 22, 2022

Does the game model each single pellet or is it conglomerated into one damage instance? If it is counted as one, that could be why. There's just a min damage and a max damage where you have a chance to break the wall.

It depends on the range. At point blank it's one instance (this is an intentional design choice), otherwise it's multiple instances.

@a-chancey
Copy link
Contributor

If I'm reading the Json right, birdshot is made of 100 projectiles that do .2 damage each. https://nornagon.github.io/cdda-guide/#/item/shot_bird

00 shot has 9 projectiles that do 15 damage each.

I'm not sure why their respective listed damages are 50 and 60, or how that number is applied.

Iirc the big damage number is for point blank, or close enough that spread won't be an issue.

Shot damage is for spread calculations

@Maleclypse Maleclypse added (S1 - Need confirmation) Report waiting on confirmation of reproducibility Fields / Furniture / Terrain / Traps Objects that are part of the map or its features. Good First Issue This is a good first issue for a new contributor labels Feb 28, 2022
@stale
Copy link

stale bot commented Mar 30, 2022

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. Please do not 'bump' or comment on this issue unless you are actively working on it. Stale issues, and stale issues that are closed are still considered.

@stale stale bot added the stale Closed for lack of activity, but still valid. label Mar 30, 2022
@github-actions github-actions bot removed the stale Closed for lack of activity, but still valid. label Dec 6, 2022
@TealcOneill
Copy link
Contributor

/Confirm
This is a little absurd. For reference it took me 8-10 hits with a regular sledge hammer, with 8 strength to smash down a wall.

  • OS: Windows
    • OS Version: 10.0.19045.4291 (22H2)
  • Game Version: cdda-experimental-2024-05-12-1705 39e2afa [64-bit]
  • Graphics Version: Tiles
  • Game Language: System language []
  • Mods loaded: [
    Dark Days Ahead [dda],
    Disable NPC Needs [no_npc_food],
    Portal Storms Ignore NPCs [personal_portal_storms],
    Slowdown Fungal Growth [no_fungal_growth]
    ]

@github-actions github-actions bot added (S2 - Confirmed) Bug that's been confirmed to exist and removed (S1 - Need confirmation) Report waiting on confirmation of reproducibility labels May 13, 2024
@Owlblocks
Copy link
Contributor

Alright, I've looked into fixing this, and through use of the Visual Studio debugger, I've come to some interesting conclusions.
Firstly, while 00 shot appears to properly use 9 projectiles, birdshot only uses 20, despite having 100 in its json.
And, more importantly, it appears that when shooting at a wall with a shotgun, all projectiles (until the wall is destroyed) deal the full damage. So, for birdshot, shooting with a shotgun at point blank range appears to run the bashing logic 20 times with 51 (due to the shotgun used?) damage. This means that, due to semi-persistent map damage, by the time the 20th projectile is fired, an adobe wall will be decimated (total map damage must exceed, I believe, max_bash - min_bash). This appears to also allow for using several rounds of birdshot to destroy a Dry Stone wall, with 50 min bash strength, as long as the cumulative map damage reaches the breaking point (it might have required 00 shot, but I believe bird shot was sufficient).

As, to my understanding, the full damage is not supposed to be applied to individual instances of shot (unless bashing is different), that means that this is not merely game logic working correctly in a weird way, but an error in the game logic.

I don't know if this only applies to bashing logic; it's possible shotguns work fine on monsters. However, in Character::fire_gun(), in the for loop going through each projectile, it doesn't seem to be properly breaking out of the loop (and setting multishot to false). However, a damage of 51 for birdshot and 61 for 00 shot is being applied on each shot to the bash code. So while I'm pretty sure a tile away is supposed to be enough for point blank range (it doesn't seem to recognize it as such), even if it weren't it still should only apply the individual projectile damage with each projectile). If each individual shot were counted, it should be unable to even scratch adobe. But if one, point blank shot were counted, it should take many, many shots with a shotgun for the map damage to accumulate enough to destroy it. Neither of those are the case.

My guess is that 1) no point blank damage adjustment is being done, and 2) the calculation for point-blank distance is wrong. I wasn't able to figure out more tonight, but I can see if I can find time in the next few days to look at it more. The code for point-blank distance (which doesn't seem to affect damage, only a message and preventing shooting more than one projectile) is this:

if( proj.count > 1 && rl_dist( pos(), shot.end_point ) == 1 ) {
    // Point-blank shots don't act like shot, everything hits the same target.
    multishot = false;
    break;
}

My guess is that damage is decreased based on similar code elsewhere, maybe in the code that only damages creatures. I'll look back at it later.

@Owlblocks
Copy link
Contributor

Okay, I think I've narrowed down (one of) the problem(s).

if( proj.count > 1 && rl_dist( pos(), shot.end_point ) == 1 ) {
    // Point-blank shots don't act like shot, everything hits the same target.
    multishot = false;
    break;
}

Here, shot.end_point is being used for the calculation. shot.end_point, however, is not necessarily where the damage was applied, but rather where drops are supposed to land.

if( here.impassable( tp ) ) {
        tp = prev_point;
}

This changes shot.end_point to the tile before the wall if the hit tile is impassable, which allows projectiles to drop one tile before the wall they hit. However, this means that rl_dist is falsely calculating whether the tile is adjacent based on where the drop lands, not where the gunfire is hitting.

The issue is, I don't know if there are situations where you'd hit a tile that isn't impassable, meaning you can't just modify the calculation to determine point-blank if the rl_dist turns up 0. We also can't use the targeted tile (I don't think), as the calculations should determine point blank based on where the shot hit, not where you aimed it. My first thought is to add another field to struct dealt_projectile_attack, returning an additional tile of where the shot hit, but this would potentially require many changes to other code that uses dealt_projectile_attack. We should probably also rename end_point, to something like "drop_location" to avoid this type of confusion in the future.

If we don't want to do that, I can try to come up with some other way of fixing the issue. Advice from someone with more knowledge of the codebase would be appreciated before I do something like this.

@kevingranade
Copy link
Member

I think you're on to something, but I don't think it's with multishot, that's only used for adjusting the hit mesage, not where terrain gets bashed.

@kevingranade
Copy link
Member

It looks like a part of the problem is that the transition from "act like one projectile" to "act like a bunch of shot" happens here

// If the attack is shot, once we're past point-blank,
on the "we hit a creature" branch and that simply never occurs when the thing we hit is furniture or terrain.

@Owlblocks
Copy link
Contributor

Yes, I believe there are actually two problems. Firstly, there's the fact that it doesn't properly recognize (in ranged.cpp) when it's hitting point blank. The problem isn't the multishot changing the message, it's the break statement after it, which is supposed to exit out of the for loop after one shot, rather than looping through each one. The suggestion for modifying the dealt_projectile_attack struct is because, at least at first inspection, it appears that the calculated data needed for determining if it's point blank is provided by ballistics.cpp and not being returned to ranged.cpp for use there.

The second one is (I think) what you just mentioned, which is that at long range (or at what's supposed to be point blank, as we're currently not telling the difference) each individual shot is dealing the full damage, rather than shot damage.

A shot at an adjacent wall is currently both shooting every pellet, which it shouldn't, and calculating each as having the full damage, which it shouldn't even if it were supposed to shoot them all. The full damage issue is also present at long range.

@oosyrag
Copy link
Contributor Author

oosyrag commented Jun 13, 2024

What is persistent map damage? I think it's simply just rolling for breaking the wall 20 (why is birdshot only 20 and not 100?) times at 50 (51?) strength. For example, it doesn't seem to be able to punch through brick wall (60 str_min) or concrete (80 str_min) no matter how many times you shoot.

@Owlblocks
Copy link
Contributor

The birdshot-only-having-20-shots problem is also something I've noticed, but I don't think it's directly relevant. You're right, though, we should address it somewhere.

As for persistent map damage, I don't fully understand it, but it seems there's a feature where it keeps track of damage on a per-tile basis. I'll check again, but I think I remember getting dry stone walls to demolish after a few hits. I had thought it was just rolling with the same odds each time, but looking at the code showed it was factoring in damage that increases after each pellet that hit. You can see the damage increasing in the debugger after each hit.

@kevingranade
Copy link
Member

20 distinct pellets of birdshot is intentional, because otherwise they each have fractional damage.

@kevingranade
Copy link
Member

Coming soon...

TEST_CASE( "shooting at terrain", "[map][bash][ranged]" )
{
    // Make a shooter                                                                                                                                                                                      
    clear_map();
    map &here = get_map();

    standard_npc shooter( "Shooter", { 10, 10, 0 } );
    shooter.set_body();
    shooter.worn.wear_item( shooter, item( "backpack" ), false, false );
    SECTION( "birdshot vs adobe wall point blank" ) {
        arm_shooter( shooter, "remington_870", {}, "shot_bird" );
        shoot_at_terrain( shooter, ter_t_adobe_brick_wall, 1 );
    }
    SECTION( "birdshot vs screen door point blank" ) {
        arm_shooter( shooter, "remington_870", {}, "shot_bird" );
        shoot_at_terrain( shooter, ter_t_screen_door_c, 1 );
    }
    SECTION( "birdshot vs adobe wall near" ) {
        arm_shooter( shooter, "remington_870", {}, "shot_bird" );
        shoot_at_terrain( shooter, ter_t_adobe_brick_wall, 2 );
    }
    SECTION( "birdshot vs screen door near" ) {
        arm_shooter( shooter, "remington_870", {}, "shot_bird" );
        shoot_at_terrain( shooter, ter_t_screen_door_c, 2 );
    }
}

@Owlblocks
Copy link
Contributor

Nice, using shot.proj.count was a very neat way of doing it that I wouldn't have thought of.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Fields / Furniture / Terrain / Traps Objects that are part of the map or its features. Good First Issue This is a good first issue for a new contributor (S2 - Confirmed) Bug that's been confirmed to exist
Projects
None yet
Development

Successfully merging a pull request may close this issue.

10 participants