Skip to content

Commit

Permalink
feat: Weapons track what is killed with them (#5394)
Browse files Browse the repository at this point in the history
* make kill_tracker usable without the event bus

* pass the weapon through the attack function chain and record if it kills something

* record murder weapon

* override to disable stats for kills xp in kill_tracker

* Fixed pointers, it actually works now. Hacked in a crude count display in the item description.

* de/serialize item kill tracker

* First pass for a proper UI

* track NPC kills

* astyle, draw borders

* record kills for thrown items

* item::kill_count()

* style(autofix.ci): automated formatting

* prevent kills from being tracked if ENABLE_EVENTS is toggled off

* remove kills_set() since it isn't actually needed for anything

* explicit overloads for character::deal damage(), remove some commented out code that never worked

* renamed every instance of "s_weapon" and "s_proj"

* rename one last instance of s_weapon to source_weapon

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
  • Loading branch information
Soadreqm and autofix-ci[bot] authored Sep 28, 2024
1 parent db61eb6 commit 2a8077a
Show file tree
Hide file tree
Showing 21 changed files with 301 additions and 24 deletions.
7 changes: 7 additions & 0 deletions data/raw/keybindings/keybindings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3268,6 +3268,13 @@
"name": "Disassemble",
"bindings": [ { "input_method": "keyboard", "key": "D" } ]
},
{
"id": "SHOW_KILL_LIST",
"type": "keybinding",
"category": "INVENTORY_ITEM",
"name": "Kills",
"bindings": [ { "input_method": "keyboard", "key": "K" } ]
},
{
"id": "FAVORITE_ADD",
"type": "keybinding",
Expand Down
6 changes: 3 additions & 3 deletions src/ballistics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ projectile_attack_aim projectile_attack_roll( const dispersion_sources &dispersi

dealt_projectile_attack projectile_attack( const projectile &proj_arg, const tripoint &source,
const tripoint &target_arg, const dispersion_sources &dispersion,
Creature *origin, const vehicle *in_veh )
Creature *origin, item *source_weapon, const vehicle *in_veh )
{
const bool do_animation = get_option<bool>( "ANIMATION_PROJECTILES" );

Expand Down Expand Up @@ -432,7 +432,7 @@ dealt_projectile_attack projectile_attack( const projectile &proj_arg, const tri
continue;
}
attack.missed_by = cur_missed_by;
critter->deal_projectile_attack( null_source ? nullptr : origin, attack );
critter->deal_projectile_attack( null_source ? nullptr : origin, source_weapon, attack );
// Critter can still dodge the projectile
// In this case hit_critter won't be set
if( attack.hit_critter != nullptr ) {
Expand Down Expand Up @@ -501,7 +501,7 @@ dealt_projectile_attack projectile_attack( const projectile &proj_arg, const tri
Creature &z = *mon_ptr;
add_msg( _( "The attack bounced to %s!" ), z.get_name() );
z.add_effect( effect_bounced, 1_turns );
projectile_attack( proj, tp, z.pos(), dispersion, origin, in_veh );
projectile_attack( proj, tp, z.pos(), dispersion, origin, source_weapon, in_veh );
sfx::play_variant_sound( "fire_gun", "bio_lightning_tail",
sfx::get_heard_volume( z.pos() ), sfx::get_heard_angle( z.pos() ) );
}
Expand Down
3 changes: 2 additions & 1 deletion src/ballistics.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#define CATA_SRC_BALLISTICS_H

class Creature;
class item;
class dispersion_sources;
class vehicle;
struct dealt_projectile_attack;
Expand Down Expand Up @@ -32,7 +33,7 @@ projectile_attack_aim projectile_attack_roll( const dispersion_sources &dispersi
*/
dealt_projectile_attack projectile_attack( const projectile &proj_arg, const tripoint &source,
const tripoint &target_arg, const dispersion_sources &dispersion,
Creature *origin = nullptr, const vehicle *in_veh = nullptr );
Creature *origin = nullptr, item *source_weapon = nullptr, const vehicle *in_veh = nullptr );

namespace ranged
{
Expand Down
37 changes: 34 additions & 3 deletions src/character.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8917,7 +8917,9 @@ void Character::on_hit( Creature *source, bodypart_id bp_hit,
Where damage to character is actually applied to hit body parts
Might be where to put bleed stuff rather than in player::deal_damage()
*/
void Character::apply_damage( Creature *source, bodypart_id hurt, int dam,
void Character::apply_damage( Creature *source, item *source_weapon, item *source_projectile,
bodypart_id hurt,
int dam,
const bool bypass_med )
{
if( is_dead_state() || has_trait( trait_DEBUG_NODMG ) ) {
Expand Down Expand Up @@ -8959,6 +8961,12 @@ void Character::apply_damage( Creature *source, bodypart_id hurt, int dam,
get_name() );
}
set_killer( source );
if( source_weapon ) {
source_weapon->add_npc_kill( get_name() );
}
if( source_projectile ) {
source_projectile->add_npc_kill( get_name() );
}
}

if( !bypass_med ) {
Expand All @@ -8972,9 +8980,21 @@ void Character::apply_damage( Creature *source, bodypart_id hurt, int dam,
}
}
}
void Character::apply_damage( Creature *source, item *source_weapon, bodypart_id hurt,
int dam,
const bool bypass_med )
{
apply_damage( source, source_weapon, nullptr, hurt, dam, bypass_med );
}
void Character::apply_damage( Creature *source, bodypart_id hurt,
int dam,
const bool bypass_med )
{
apply_damage( source, nullptr, nullptr, hurt, dam, bypass_med );
}

dealt_damage_instance Character::deal_damage( Creature *source, bodypart_id bp,
const damage_instance &d )
const damage_instance &d, item *source_weapon, item *source_projectile )
{
if( has_trait( trait_DEBUG_NODMG ) ) {
return dealt_damage_instance();
Expand All @@ -8987,7 +9007,8 @@ dealt_damage_instance Character::deal_damage( Creature *source, bodypart_id bp,
}

//damage applied here
dealt_damage_instance dealt_dams = Creature::deal_damage( source, bp, d );
dealt_damage_instance dealt_dams = Creature::deal_damage( source, bp, d, source_weapon,
source_projectile );
//block reduction should be by applied this point
int dam = dealt_dams.total_damage();

Expand Down Expand Up @@ -9128,6 +9149,16 @@ dealt_damage_instance Character::deal_damage( Creature *source, bodypart_id bp,
on_hurt( source );
return dealt_dams;
}
dealt_damage_instance Character::deal_damage( Creature *source, bodypart_id bp,
const damage_instance &d, item *source_weapon )
{
return deal_damage( source, bp, d, source_weapon, nullptr );
}
dealt_damage_instance Character::deal_damage( Creature *source, bodypart_id bp,
const damage_instance &d )
{
return deal_damage( source, bp, d, nullptr, nullptr );
}

int Character::reduce_healing_effect( const efftype_id &eff_id, int remove_med,
const bodypart_id &hurt )
Expand Down
9 changes: 9 additions & 0 deletions src/character.h
Original file line number Diff line number Diff line change
Expand Up @@ -671,9 +671,18 @@ class Character : public Creature, public location_visitable<Character>
void did_hit( Creature &target );

/** Actually hurt the player, hurts a body_part directly, no armor reduction */
void apply_damage( Creature *source, item *source_weapon, item *source_projectile, bodypart_id hurt,
int dam,
bool bypass_med = false ) override;
void apply_damage( Creature *source, item *source_weapon, bodypart_id hurt, int dam,
bool bypass_med = false ) override;
void apply_damage( Creature *source, bodypart_id hurt, int dam,
bool bypass_med = false ) override;
/** Calls Creature::deal_damage and handles damaged effects (waking up, etc.) */
dealt_damage_instance deal_damage( Creature *source, bodypart_id bp,
const damage_instance &d, item *source_weapon, item *source_projectile ) override;
dealt_damage_instance deal_damage( Creature *source, bodypart_id bp,
const damage_instance &d, item *source_weapon ) override;
dealt_damage_instance deal_damage( Creature *source, bodypart_id bp,
const damage_instance &d ) override;
/** Reduce healing effect intensity, return initial intensity of the effect */
Expand Down
41 changes: 33 additions & 8 deletions src/creature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,8 @@ int Creature::deal_melee_attack( Creature *source, int hitroll )
return hit_spread;
}

void Creature::deal_melee_hit( Creature *source, int hit_spread, bool critical_hit,
void Creature::deal_melee_hit( Creature *source, item *source_weapon, int hit_spread,
bool critical_hit,
const damage_instance &dam, dealt_damage_instance &dealt_dam )
{
if( source == nullptr || source->is_hallucination() ) {
Expand All @@ -592,7 +593,8 @@ void Creature::deal_melee_hit( Creature *source, int hit_spread, bool critical_h
if( mons && mons->mounted_player ) {
if( !mons->has_flag( MF_MECH_DEFENSIVE ) &&
one_in( std::max( 2, mons->get_size() - mons->mounted_player->get_size() ) ) ) {
mons->mounted_player->deal_melee_hit( source, hit_spread, critical_hit, dam, dealt_dam );
mons->mounted_player->deal_melee_hit( source, source_weapon, hit_spread, critical_hit, dam,
dealt_dam );
return;
}
}
Expand All @@ -603,9 +605,14 @@ void Creature::deal_melee_hit( Creature *source, int hit_spread, bool critical_h
block_hit( source, bp_hit, d );

on_hit( source, bp_hit ); // trigger on-gethit events
dealt_dam = deal_damage( source, bp_hit, d );
dealt_dam = deal_damage( source, bp_hit, d, source_weapon );
dealt_dam.bp_hit = bp_token;
}
void Creature::deal_melee_hit( Creature *source, int hit_spread, bool critical_hit,
const damage_instance &dam, dealt_damage_instance &dealt_dam )
{
deal_melee_hit( source, nullptr, hit_spread, critical_hit, dam, dealt_dam );
}

namespace ranged
{
Expand Down Expand Up @@ -708,10 +715,12 @@ auto get_stun_srength( const projectile &proj, creature_size size ) -> int
* Attempts to harm a creature with a projectile.
*
* @param source Pointer to the creature who shot the projectile.
* @param source_weapon Pointer to the weapon used to fire the projectile.
* @param attack A structure describing the attack and its results.
* @param print_messages enables message printing by default.
*/
void Creature::deal_projectile_attack( Creature *source, dealt_projectile_attack &attack )
void Creature::deal_projectile_attack( Creature *source, item *source_weapon,
dealt_projectile_attack &attack )
{
const bool magic = attack.proj.has_effect( ammo_effect_magic );
const bool targetted_crit_allowed = !attack.proj.has_effect( ammo_effect_NO_CRIT );
Expand All @@ -726,7 +735,7 @@ void Creature::deal_projectile_attack( Creature *source, dealt_projectile_attack
if( mons && mons->mounted_player ) {
if( !mons->has_flag( MF_MECH_DEFENSIVE ) &&
one_in( std::max( 2, mons->get_size() - mons->mounted_player->get_size() ) ) ) {
mons->mounted_player->deal_projectile_attack( source, attack );
mons->mounted_player->deal_projectile_attack( source, source_weapon, attack );
return;
}
}
Expand Down Expand Up @@ -844,7 +853,8 @@ void Creature::deal_projectile_attack( Creature *source, dealt_projectile_attack

// If we have a shield, it might passively block ranged impacts
block_ranged_hit( source, bp_hit, impact );
dealt_dam = deal_damage( source, bp_hit, impact );
// If the projectile survives, both it and the launcher get credit for the kill.
dealt_dam = deal_damage( source, bp_hit, impact, source_weapon, attack.proj.get_drop() );
dealt_dam.bp_hit = bp_hit->token;

// Apply ammo effects to target.
Expand Down Expand Up @@ -926,8 +936,13 @@ void Creature::deal_projectile_attack( Creature *source, dealt_projectile_attack
attack.missed_by = goodhit;
}

void Creature::deal_projectile_attack( Creature *source, dealt_projectile_attack &attack )
{
deal_projectile_attack( source, nullptr, attack );
}

dealt_damage_instance Creature::deal_damage( Creature *source, bodypart_id bp,
const damage_instance &dam )
const damage_instance &dam, item *source_weapon, item *source_projectile )
{
if( is_dead_state() ) {
return dealt_damage_instance();
Expand All @@ -951,9 +966,19 @@ dealt_damage_instance Creature::deal_damage( Creature *source, bodypart_id bp,

mod_pain( total_pain );

apply_damage( source, bp, total_damage );
apply_damage( source, source_weapon, source_projectile, bp, total_damage );
return dealt_dams;
}
dealt_damage_instance Creature::deal_damage( Creature *source, bodypart_id bp,
const damage_instance &dam, item *source_weapon )
{
return deal_damage( source, bp, dam, source_weapon, nullptr );
}
dealt_damage_instance Creature::deal_damage( Creature *source, bodypart_id bp,
const damage_instance &dam )
{
return deal_damage( source, bp, dam, nullptr, nullptr );
}
void Creature::deal_damage_handle_type( const damage_unit &du, bodypart_id bp, int &damage,
int &pain )
{
Expand Down
18 changes: 18 additions & 0 deletions src/creature.h
Original file line number Diff line number Diff line change
Expand Up @@ -356,11 +356,16 @@ class Creature

// completes a melee attack against the creature
// dealt_dam is overwritten with the values of the damage dealt
virtual void deal_melee_hit( Creature *source, item *source_weapon, int hit_spread,
bool critical_hit,
const damage_instance &dam, dealt_damage_instance &dealt_dam );
virtual void deal_melee_hit( Creature *source, int hit_spread, bool critical_hit,
const damage_instance &dam, dealt_damage_instance &dealt_dam );

// Makes a ranged projectile attack against the creature
// Sets relevant values in `attack`.
virtual void deal_projectile_attack( Creature *source, item *source_weapon,
dealt_projectile_attack &attack );
virtual void deal_projectile_attack( Creature *source, dealt_projectile_attack &attack );

/**
Expand All @@ -374,15 +379,28 @@ class Creature
* @param source The attacking creature, can be null.
* @param bp The attacked body part
* @param dam The damage dealt
* @param source_weapon The weapon used in the attack, optional
* @param source_projectile The projectile fired in the attack, optional
*/
virtual dealt_damage_instance deal_damage( Creature *source, bodypart_id bp,
const damage_instance &dam, item *source_weapon, item *source_projectile );
virtual dealt_damage_instance deal_damage( Creature *source, bodypart_id bp,
const damage_instance &dam, item *source_weapon );
virtual dealt_damage_instance deal_damage( Creature *source, bodypart_id bp,
const damage_instance &dam );

// for each damage type, how much gets through and how much pain do we
// accrue? mutates damage and pain
virtual void deal_damage_handle_type( const damage_unit &du,
bodypart_id bp, int &damage, int &pain );
// directly decrements the damage. ONLY handles damage, doesn't
// increase pain, apply effects, etc
virtual void apply_damage( Creature *source, item *source_weapon, item *source_projectile,
bodypart_id bp,
int amount,
bool bypass_med = false ) = 0;
virtual void apply_damage( Creature *source, item *source_weapon, bodypart_id bp, int amount,
bool bypass_med = false ) = 0;
virtual void apply_damage( Creature *source, bodypart_id bp, int amount,
bool bypass_med = false ) = 0;

Expand Down
7 changes: 7 additions & 0 deletions src/examine_item_menu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,13 @@ bool run(
return true;
} );

if( itm.kill_count() > 0 ) {
add_entry( "SHOW_KILL_LIST", hint_rating::good, [&]() {
itm.show_kill_list();
return true;
} );
}

if( !itm.is_favorite ) {
add_entry( "FAVORITE_ADD",
hint_rating::good, [&]() {
Expand Down
41 changes: 41 additions & 0 deletions src/item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
#include "ret_val.h"
#include "rot.h"
#include "rng.h"
#include "scores_ui.h"
#include "skill.h"
#include "stomach.h"
#include "string_formatter.h"
Expand Down Expand Up @@ -10715,3 +10716,43 @@ location_vector<item> &item::get_components()
{
return components;
}

bool item::init_kill_tracker()
{
if( kills ) {
return true;
} else if( get_option<bool>( "ENABLE_EVENTS" ) ) {
kills = std::make_unique<kill_tracker>( false );
return true;
} else {
return false;
}
}
void item::add_monster_kill( mtype_id mon )
{
if( init_kill_tracker() ) {
kills->add_monster( mon );
}
}
void item::add_npc_kill( std::string npc )
{
if( init_kill_tracker() ) {
kills->add_npc( npc );
}
}
void item::show_kill_list()
{
if( !kills ) {
debugmsg( "Tried to display empty kill list" );
return;
}
show_kills( *kills );
}
int item::kill_count()
{
if( !kills ) {
return 0;
} else {
return kills->monster_kill_count() + kills->npc_kill_count();
}
}
18 changes: 18 additions & 0 deletions src/item.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "gun_mode.h"
#include "io_tags.h"
#include "item_contents.h"
#include "kill_tracker.h"
#include "location_vector.h"
#include "pimpl.h"
#include "safe_reference.h"
Expand Down Expand Up @@ -2425,6 +2426,23 @@ class item : public location_visitable<item>, public game_object<item>
* Ideally, this would be stored outside item class.
*/
pimpl<item_drop_token> drop_token;

private:
/** Kill tracker */
std::unique_ptr<kill_tracker> kills;
/**
* Check if there's a kill_tracker
* Make one if there isn't and if ENABLE_EVENTS option is toggled on
* @returns true if a kill_tracker exists, or if one was created
* false if there is no kill_tracker, and one wasn't created
*/
bool init_kill_tracker();

public:
void add_monster_kill( mtype_id );
void add_npc_kill( std::string );
void show_kill_list();
int kill_count();
};

bool item_compare_by_charges( const item &left, const item &right );
Expand Down
Loading

0 comments on commit 2a8077a

Please sign in to comment.