Repository: CleverRaven/Cataclysm-DDA
Pull request #80168: Add a Bombastic Perk “Damage Dice” and add ammo context var to event character_ranged_attacks_character and character_ranged_attacks_monster
Author: login: typeryougishiki
Labels: name: <Enhancement / Feature>
description: New features, or enhancements on existing
name: <Documentation>
description: Design documents, internal info, guides and help.
name: [JSON]
description: Changes (can be) made in JSON
name: [C++]
description: Changes (can be) made in C++. Previously named `Code`
name: [Markdown]
description: Markdown issues and PRs
name: astyled
description: astyled PR, label is assigned by github actions
name: json-styled
description: JSON lint passed, label assigned by github actions
name: BasicBuildPassed
description: This PR builds correctly, label assigned by github actions
name: Mods: Bombastic Perks
Created: 2025-03-15T04:40:42Z | Merged: 2025-03-24T03:37:30Z

<!-- HOW TO USE: Under each "#### Heading" below, enter information relevant to your pull request.
Leave the headings unless they don't apply to your PR.

Please read carefully.
Once a pull request is submitted, automatic stylistic and consistency checks will be performed on the PR's changes.
The results can be either seen under the "Files changed" section of a PR or in the check's details.

Rules for suggested pull requests:
- If possible, limit yourself to small changes, 500 strings at max. Exceptions are adding or changing maps, and changes, that won't work unless they are done in a single run (even then there can be ways) - violating it puts a lot of unnecessary work on our merge team.
- Do not scope creep. If you make a pull request "Add new gun", please do not make anything more than adding the gun and following changes, like changing the stats of the gun, removing other guns from itemgroups or tweaking zombie horse stats - violating it makes future search and debugging stuff much harder, since PR name is not related to what is changed in the game. "Who the hell removed the quest item from drop in location X in PR, that adds a new plushie" - this may be a quote from a person who was affected by scope creep
- Do not make omnibus PRs. Meaning do not make a single PR, that fixes ten different, not related issues, at once, even if they are all one string - same as previously mentioned scope creep, it doesn't help to search the changes when debugging, despite all power of git blame tool

NOTE: Please grant permission for repository maintainers to edit your PR.  It is EXTREMELY common for PRs to be held up due to trivial changes being requested and the author being unavailable to make them. -->

#### Summary
Content "Add a Bombastic Perk “Damage Dice” and add ammo context var to event character_ranged_attacks_character and character_ranged_attacks_monster"
<!-- This section should consist of exactly one line, edit the one above.
1. Replace the word "Category" with one of these words: Features, Content, Interface, Mods, Balance, Bugfixes, Performance, Infrastructure, Build, I18N.
2. Replace the text inside the quotes with a brief description of your changes.
Or if you don't want a changelog entry, replace the whole line with just the word "None" (with no quotes).
Examples:
1. None
2. Features "In-game Armor sprite change"
3. Interface "Show crafting failure chances in the crafting interface"
4. Infrastructure "JSON-ize slot machines"
5. Bugfixes "Crafting GUI: show how much recipe makes for non-charge items"
For more on the meaning of each category, see:
https://github.com/CleverRaven/Cataclysm-DDA/blob/master/doc/CHANGELOG_GUIDELINES.md
If approved and merged, your summary will be added to the project changelog:
https://github.com/CleverRaven/Cataclysm-DDA/blob/master/data/changelog.txt -->

#### Purpose of change
Add a perk "Damage Dice" to mod "Bombastic Perks", which provides a little extra damage to ranged attacks that use dice as ammunition.
Add ammo context variable to event character_ranged_attacks_character and character_ranged_attacks_monster, which is the item type id of the ammo in the gun when creating projectiles.It is use to perk implement "Damage Dice".
<!-- With a few sentences, describe your reasons for making this change.
If it relates to an existing issue, you can link it with a # followed by the GitHub issue number, like #1234.
When you submit a pull request that completely resolves an issue, use [Github's closing keywords](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/using-keywords-in-issues-and-pull-requests#linking-a-pull-request-to-an-issue)
to automatically close the issue once your pull request is merged.
If there is no related issue, explain here what issue, feature, or other concern you are addressing.  If this is a bugfix, include steps to reproduce the original bug, so your fix can be verified. -->

#### Describe the solution
1. Add itype_id field "ammo" to event character_ranged_attacks_monster and character_ranged_attacks_monster.
2. In Character::fire_gun in  ranged.cpp, before call make_gun_projectile, get ammo's itype id in gun as make event arguement. If no ammo in gun,use itype_id::NULL_ID().
3. Add Perk “Damage Dice”.
<!-- How does the feature work, or how does this fix a bug?  The easier you make your solution to understand, the faster it can get merged. -->

#### Describe alternatives you've considered
None
<!-- Explain any alternative solutions, different approaches, or possibilities you've considered using to solve the same problem. -->

#### Testing
1.Fire bow, gun using bullet, gun using battery, and gun not need ammo. No errors or crashes.
2.Use the following eoc to test. Fire bow, gun using bullet, gun using battery, and gun not need ammo. It prints the ammo id correctly and prints null when gun not need ammo.
`[
  {
    "id": "temp_test_character_ranged_attacks_monster",
    "type": "effect_on_condition",
    "eoc_type": "EVENT",
    "required_event": "character_ranged_attacks_monster",
    "effect": [ {
      "message": "weapon:<context_val:weapon> ammo:<context_val:ammo>"
    }]
  },
  {
    "id": "temp_test_character_ranged_attacks_character",
    "type": "effect_on_condition",
    "eoc_type": "EVENT",
    "required_event": "character_ranged_attacks_character",
    "effect": [ {
      "message": "weapon:<context_val:weapon> ammo:<context_val:ammo>"
    }]
  }
]`
3. Leveling up a character and getting the "Damage Dice“ perk from the perk menu works fine. The bonus damage and tooltip provided by "Damage Dice“ work fine.
<!-- Describe what steps you took to test that this PR resolved the bug or added the feature, and what tests you performed to make sure it didn't cause any regressions.  Also, include testing suggestions for reviewers and maintainers. See TESTING_YOUR_CHANGES.md -->

#### Additional context
1. The RPG dice will determine the number of sides when it is used for the first time and store it in a variable of the item, but I haven't figured out a proper way to get the variables stored in the ammo item in the character_ranged_attacks_* events. So a random number of dice sides is used.
<!-- Add any other context (such as mock-ups, proof of concepts or screenshots) about the feature or bugfix here. -->


<!--README: Cataclysm: Dark Days Ahead is released under the Creative Commons Attribution ShareAlike 3.0 license.
The code and content of the game are free to use, modify, and redistribute for any purpose whatsoever.
By contributing to the project you agree to the terms of the license and that any contribution you make will also be covered by the same license.
See http://creativecommons.org/licenses/by-sa/3.0/ for details. -->



Diff:
diff --git a/data/mods/BombasticPerks/perkdata/dice_addition_damage.json b/data/mods/BombasticPerks/perkdata/dice_addition_damage.json
new file mode 100644
index 0000000000000..c14369318bc00
--- /dev/null
+++ b/data/mods/BombasticPerks/perkdata/dice_addition_damage.json
@@ -0,0 +1,36 @@
+[
+  {
+    "type": "effect_on_condition",
+    "id": "eoc_perk_dice_addition_damage",
+    "effect": [
+      { "math": [ "_dice_side_nums = 0" ] },
+      {
+        "if": { "compare_string": [ { "context_val": "ammo" }, "plastic_six_dice" ] },
+        "then": { "math": [ "_dice_side_nums = 6" ] }
+      },
+      {
+        "if": { "compare_string": [ { "context_val": "ammo" }, "RPG_die", "metal_RPG_die" ] },
+        "then": { "math": [ "_dice_side_nums = ( rand( 4 ) + 2 )*2" ] }
+      },
+      { "math": [ "_dice_result = rand(_dice_side_nums-1)+1" ] },
+      { "u_message": "Damage dice result: 1D<context_val:dice_side_nums> = <context_val:dice_result>" },
+      { "npc_deal_damage": "pure", "amount": { "context_val": "dice_result" } }
+    ]
+  },
+  {
+    "type": "effect_on_condition",
+    "id": "event_perk_dice_addition_damage_ranged_attacks_character",
+    "eoc_type": "EVENT",
+    "required_event": "character_ranged_attacks_character",
+    "condition": { "and": [ { "u_has_trait": "perk_dice_addition_damage" } ] },
+    "effect": [ { "run_eocs": [ "eoc_perk_dice_addition_damage" ] } ]
+  },
+  {
+    "type": "effect_on_condition",
+    "id": "event_perk_dice_addition_damage_ranged_attacks_monster",
+    "eoc_type": "EVENT",
+    "required_event": "character_ranged_attacks_monster",
+    "condition": { "and": [ { "u_has_trait": "perk_dice_addition_damage" } ] },
+    "effect": [ { "run_eocs": [ "eoc_perk_dice_addition_damage" ] } ]
+  }
+]
diff --git a/data/mods/BombasticPerks/perkmenu.json b/data/mods/BombasticPerks/perkmenu.json
index fd9cf9a333f79..2d724a2b03cc5 100644
--- a/data/mods/BombasticPerks/perkmenu.json
+++ b/data/mods/BombasticPerks/perkmenu.json
@@ -1313,6 +1313,25 @@
         ],
         "topic": "TALK_PERK_MENU_SELECT"
       },
+      {
+        "condition": { "not": { "u_has_trait": "perk_dice_addition_damage" } },
+        "text": "Gain [<trait_name:perk_dice_addition_damage>]",
+        "effect": [
+          { "set_string_var": "<trait_name:perk_dice_addition_damage>", "target_var": { "context_val": "trait_name" } },
+          {
+            "set_string_var": "<trait_description:perk_dice_addition_damage>",
+            "target_var": { "context_val": "trait_description" }
+          },
+          { "set_string_var": "perk_dice_addition_damage", "target_var": { "context_val": "trait_id" } },
+          {
+            "set_string_var": "Must have Dungeon Master trait.",
+            "target_var": { "context_val": "trait_requirement_description" },
+            "i18n": true
+          },
+          { "set_condition": "perk_condition", "condition": { "u_has_trait": "PROF_DICEMASTER" } }
+        ],
+        "topic": "TALK_PERK_MENU_SELECT"
+      },
       {
         "condition": { "math": [ "u_no_playstyle == 0" ] },
         "text": "Playstyle Perks",
diff --git a/data/mods/BombasticPerks/perks.json b/data/mods/BombasticPerks/perks.json
index 059050ba8d6b5..232fc6dba3dd5 100644
--- a/data/mods/BombasticPerks/perks.json
+++ b/data/mods/BombasticPerks/perks.json
@@ -905,6 +905,16 @@
       { "condition": { "u_has_wielded_with_flag": "OLD_GUN" }, "values": [ { "value": "RANGED_DAMAGE", "multiply": 0.25 } ] }
     ]
   },
+  {
+    "type": "mutation",
+    "id": "perk_dice_addition_damage",
+    "name": { "str": "Damage dice" },
+    "points": 0,
+    "purifiable": false,
+    "valid": false,
+    "description": "Before Cataclysm, you always used dice to determine the damage in the game.  Now, the power of fate allows you to use dice to deal additional mysterious damage.  Throwing dice using a sling or similar weapon deals additional damage, which depends on the number of sides of the dice.",
+    "category": [ "perk" ]
+  },
   {
     "type": "mutation",
     "id": "perk_moonstruck",
diff --git a/doc/JSON/EFFECT_ON_CONDITION.md b/doc/JSON/EFFECT_ON_CONDITION.md
index 95a02bf4b4ac3..b187b2592b8cb 100644
--- a/doc/JSON/EFFECT_ON_CONDITION.md
+++ b/doc/JSON/EFFECT_ON_CONDITION.md
@@ -1654,8 +1654,8 @@ Every event EOC passes context vars with each of their key value pairs that the
 | character_melee_attacks_character |  | { "attacker", `character_id` },<br/> { "weapon", `itype_id` },<br/> { "hits", `bool` },<br/> { "victim", `character_id` },<br/> { "victim_name", `string` }, | character (attacker) / character (victim) |
 | character_melee_attacks_monster | | { "attacker", `character_id` },<br/> { "weapon", `itype_id` },<br/> { "hits", `bool` },<br/> { "victim_type", `mtype_id` },| character / monster |
 | character_radioactively_mutates | triggered when a character mutates due to being irradiated | { "character", `character_id` }, | character / NONE |
-| character_ranged_attacks_character | |  { "attacker", `character_id` },<br/> { "weapon", `itype_id` },<br/> { "victim", `character_id` },<br/> { "victim_name", `string` }, | character (attacker) / character (victim) |
-| character_ranged_attacks_monster | | { "attacker", `character_id` },<br/> { "weapon", `itype_id` },<br/> { "victim_type", `mtype_id` }, | character / monster |
+| character_ranged_attacks_character | |  { "attacker", `character_id` },<br/> { "weapon", `itype_id` },<br/> { "ammo", `itype_id` },<br/> { "victim", `character_id` },<br/> { "victim_name", `string` }, | character (attacker) / character (victim) |
+| character_ranged_attacks_monster | | { "attacker", `character_id` },<br/> { "weapon", `itype_id` },<br/> { "ammo", `itype_id` },<br/>  { "victim_type", `mtype_id` }, | character / monster |
 | character_smashes_tile | | { "character", `character_id` },<br/> { "terrain", `ter_str_id` },  { "furniture", `furn_str_id` }, | character / NONE |
 | character_starts_activity | Triggered when character starts or resumes activity | { "character", `character_id` },<br/> { "activity", `activity_id` },<br/> { "resume", `bool` } | character / NONE |
 | character_takeoff_item | triggers when character removes a worn item. If using `run_inv_eocs`, remember that the event fires before the items are actually removed | { "character", `character_id` },<br/> { "itype", `itype_id` } |
diff --git a/src/event.h b/src/event.h
index 0c3feb24d3a10..e5cf746c0ba52 100644
--- a/src/event.h
+++ b/src/event.h
@@ -428,9 +428,10 @@ struct event_spec<event_type::character_radioactively_mutates> : event_spec_char
 
 template<>
 struct event_spec<event_type::character_ranged_attacks_character> {
-    static constexpr std::array<std::pair<const char *, cata_variant_type>, 4> fields = {{
+    static constexpr std::array<std::pair<const char *, cata_variant_type>, 5> fields = {{
             { "attacker", cata_variant_type::character_id },
             { "weapon", cata_variant_type::itype_id },
+            { "ammo", cata_variant_type::itype_id },
             { "victim", cata_variant_type::character_id },
             { "victim_name", cata_variant_type::string },
         }
@@ -439,9 +440,10 @@ struct event_spec<event_type::character_ranged_attacks_character> {
 
 template<>
 struct event_spec<event_type::character_ranged_attacks_monster> {
-    static constexpr std::array<std::pair<const char *, cata_variant_type>, 3> fields = {{
+    static constexpr std::array<std::pair<const char *, cata_variant_type>, 4> fields = {{
             { "attacker", cata_variant_type::character_id },
             { "weapon", cata_variant_type::itype_id },
+            { "ammo", cata_variant_type::itype_id },
             { "victim_type", cata_variant_type::mtype_id },
         }
     };
diff --git a/src/ranged.cpp b/src/ranged.cpp
index ec283eae14b83..88a72290e9d35 100644
--- a/src/ranged.cpp
+++ b/src/ranged.cpp
@@ -1092,6 +1092,9 @@ int Character::fire_gun( map &here, const tripoint_bub_ms &target, int shots, it
 
         weakpoint_attack wp_attack;
         wp_attack.weapon = &gun;
+        // get ammo_id in gun for event character_ranged_attacks_monster. If no ammo, use itype_id::NULL_ID()
+        itype_id projectile_use_ammo_id = gun.has_ammo_data() ? gun.ammo_data()->get_id() :
+                                          itype_id::NULL_ID();
         projectile proj = make_gun_projectile( gun );
 
         for( damage_unit &elem : proj.impact.damage_units ) {
@@ -1107,16 +1110,19 @@ int Character::fire_gun( map &here, const tripoint_bub_ms &target, int shots, it
         if( !shot.targets_hit.empty() ) {
             hits++;
         }
+
         for( std::pair<Creature *const, std::pair<int, int>> &hit_entry : shot.targets_hit ) {
             if( hit_entry.second.first == 0 ) {
                 continue;
             }
             if( monster *const m = hit_entry.first->as_monster() ) {
                 cata::event e = cata::event::make<event_type::character_ranged_attacks_monster>( getID(), gun_id,
+                                projectile_use_ammo_id,
                                 m->type->id );
                 get_event_bus().send_with_talker( this, m, e );
             } else if( Character *const c = hit_entry.first->as_character() ) {
                 cata::event e = cata::event::make<event_type::character_ranged_attacks_character>( getID(), gun_id,
+                                projectile_use_ammo_id,
                                 c->getID(), c->get_name() );
                 get_event_bus().send_with_talker( this, c, e );
             }


Reviews:
user: login: github-actions[bot]
body: Auto-requesting reviews from non-collaborators: @bombasticSlacks
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T07:43:58Z
user: login: GuardianDll
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-20T06:30:38Z
user: login: typeryougishiki
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-20T06:37:28Z
user: login: GuardianDll
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-20T06:45:07Z

Review comments:
diff_hunk: @@ -905,6 +905,16 @@
       { "condition": { "u_has_wielded_with_flag": "OLD_GUN" }, "values": [ { "value": "RANGED_DAMAGE", "multiply": 0.25 } ] }
     ]
   },
+  {
+    "type": "mutation",
+    "id": "perk_dice_addition_damage",
+    "name": { "str": "Damage dice" },
+    "points": 0,
+    "purifiable": false,
+    "valid": false,
+    "description": "Before Cataclysm, you always use dice to determine the damage in game.  Now, the power of fate allows you to use dice to deal addition mysterious damage.  Ranged attack using dice deals addition damage, which depends on the number of sides of the dice.",
path: data/mods/BombasticPerks/perks.json
user: login: GuardianDll
body: ```suggestion
    "description": "Before Cataclysm, you always used dice to determine the damage in the game.  Now, the power of fate allows you to use dice to deal additional mysterious damage. Throwing dice using a sling or similar weapon deals additional damage, which depends on the number of sides of the dice.",
```
created_at: 2025-03-20T06:30:38Z
author_association: MEMBER
diff_hunk: @@ -905,6 +905,16 @@
       { "condition": { "u_has_wielded_with_flag": "OLD_GUN" }, "values": [ { "value": "RANGED_DAMAGE", "multiply": 0.25 } ] }
     ]
   },
+  {
+    "type": "mutation",
+    "id": "perk_dice_addition_damage",
+    "name": { "str": "Damage dice" },
+    "points": 0,
+    "purifiable": false,
+    "valid": false,
+    "description": "Before Cataclysm, you always use dice to determine the damage in game.  Now, the power of fate allows you to use dice to deal addition mysterious damage.  Ranged attack using dice deals addition damage, which depends on the number of sides of the dice.",
path: data/mods/BombasticPerks/perks.json
user: login: typeryougishiki
body: Thanks for the correction, my English is not very good. However, since character_ranged_attacks_* events currently do not work with throw, this perk currently works when using dice as ammunition for slingshot or other weapons, so Ranged attack is used in the description.
created_at: 2025-03-20T06:37:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -905,6 +905,16 @@
       { "condition": { "u_has_wielded_with_flag": "OLD_GUN" }, "values": [ { "value": "RANGED_DAMAGE", "multiply": 0.25 } ] }
     ]
   },
+  {
+    "type": "mutation",
+    "id": "perk_dice_addition_damage",
+    "name": { "str": "Damage dice" },
+    "points": 0,
+    "purifiable": false,
+    "valid": false,
+    "description": "Before Cataclysm, you always use dice to determine the damage in game.  Now, the power of fate allows you to use dice to deal addition mysterious damage.  Ranged attack using dice deals addition damage, which depends on the number of sides of the dice.",
path: data/mods/BombasticPerks/perks.json
user: login: GuardianDll
body: updated
created_at: 2025-03-20T06:45:07Z
author_association: MEMBER

PR comments:
user: login: Maleclypse
created_at: 2025-03-17T04:11:40Z
body: Hi there, this looks interesting but it needs an in repo use case either in vanilla or in a mod that ships with the base game such as Xedra Evolved, MoM, Aftershock, Magiclysm or Isolation Protocol to be merged.
author_association: MEMBER
user: login: typeryougishiki
created_at: 2025-03-17T06:01:29Z
body: > Hi there, this looks interesting but it needs an in repo use case either in vanilla or in a mod that ships with the base game such as Xedra Evolved, MoM, Aftershock, Magiclysm or Isolation Protocol to be merged.

I will try it.
author_association: CONTRIBUTOR
user: login: typeryougishiki
created_at: 2025-03-18T23:42:22Z
body: > Hi there, this looks interesting but it needs an in repo use case either in vanilla or in a mod that ships with the base game such as Xedra Evolved, MoM, Aftershock, Magiclysm or Isolation Protocol to be merged.

After adding a use case to a in repo mod, how do I modify the PR format? Should I simply add the use case to "Purpose of change" or should I completely modify the PR to make it themed around what I added to the mod?
author_association: CONTRIBUTOR