From 5905182d2968307da1fc82d6c27ad9d806d2a0e9 Mon Sep 17 00:00:00 2001 From: shmakota Date: Thu, 26 Dec 2024 23:55:17 -0600 Subject: [PATCH 1/9] aggro_character variable added to monster Co-Authored-By: Venera3 <72006894+Venera3@users.noreply.github.com> --- src/monmove.cpp | 13 +++++++++ src/monster.cpp | 60 ++++++++++++++++++++++++++++++++++++++++ src/monster.h | 2 ++ src/monstergenerator.cpp | 3 +- src/mtype.cpp | 2 ++ src/mtype.h | 3 ++ 6 files changed, 82 insertions(+), 1 deletion(-) diff --git a/src/monmove.cpp b/src/monmove.cpp index b526d5c79d98..02f7e7e40a4b 100644 --- a/src/monmove.cpp +++ b/src/monmove.cpp @@ -354,6 +354,10 @@ void monster::plan() target = &g->u; if( dist <= 5 ) { anger += angers_hostile_near; + if( angers_hostile_near && x_in_y( anger, 100 ) ) { + //add_msg_debug( debugmode::DF_MONSTER, "%s's character aggro triggered by proximity", name() ); + aggro_character = true; + } morale -= fears_hostile_near; if( angers_mating_season > 0 ) { bool mating_angry = false; @@ -369,6 +373,10 @@ void monster::plan() } if( mating_angry ) { anger += angers_mating_season; + if( x_in_y( anger, 100 ) ) { + //add_msg_debug( debugmode::DF_MONSTER, "%s's character aggro triggered by season", name() ); + aggro_character = true; + } } } } @@ -381,6 +389,7 @@ void monster::plan() //proximity to baby; monster gets furious and less likely to flee anger += angers_cub_threatened; morale += angers_cub_threatened / 2; + aggro_character = true; } } } @@ -444,6 +453,10 @@ void monster::plan() } if( mating_angry ) { anger += angers_mating_season; + if( x_in_y( anger, 100 ) ) { + //add_msg_debug( debugmode::DF_MONSTER, "%s's character aggro triggered by season", name() ); + aggro_character = true; + } } } } diff --git a/src/monster.cpp b/src/monster.cpp index 85e368dab082..b8d557a46534 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -222,6 +222,7 @@ monster::monster() : corpse_components( new monster_component_item_location( thi last_updated = calendar::start_of_cataclysm; udder_timer = calendar::turn; horde_attraction = MHA_NULL; + aggro_character = true; set_anatomy( anatomy_id( "default_anatomy" ) ); set_body(); } @@ -242,6 +243,7 @@ monster::monster( const mtype_id &id ) : monster() ammo = type->starting_ammo; upgrades = type->upgrades && ( type->half_life || type->age_grow ); reproduces = type->reproduces && type->baby_timer && !monster::has_flag( MF_NO_BREED ); + aggro_character = type->aggro_character; if( monster::has_flag( MF_AQUATIC ) ) { fish_population = dice( 1, 20 ); } @@ -1385,6 +1387,10 @@ monster_attitude monster::attitude( const Character *u ) const return MATT_ATTACK; } + if( u != nullptr && !aggro_character && !u->is_monster() ) { + return MATT_IGNORE; + } + if( type->in_species( FUNGUS ) && ( u->has_trait( trait_THRESH_MYCUS ) || u->has_trait( trait_MYCUS_FRIEND ) ) ) { return MATT_FRIEND; @@ -1513,6 +1519,12 @@ void monster::process_triggers() } } + // If we got angry at characters have a chance at calming down + if( anger == type->agro && aggro_character && !type->aggro_character && !x_in_y( anger, 100 ) ) { + //add_msg_debug( debugmode::DF_MONSTER, "%s's character aggro reset", name() ); + aggro_character = false; + } + // Cap values at [-100, 100] to prevent perma-angry moose etc. morale = std::min( 100, std::max( -100, morale ) ); anger = std::min( 100, std::max( -100, anger ) ); @@ -1947,6 +1959,10 @@ void monster::apply_damage( Creature *source, item *source_weapon, item *source_ } } else if( dam > 0 ) { process_trigger( mon_trigger::HURT, 1 + ( dam / 3 ) ); + // Get angry at characters if hurt by one + if( source != nullptr && !aggro_character && !source->is_monster() ) { + aggro_character = true; + } } } void monster::apply_damage( Creature *source, item *source_weapon, bodypart_id bp, int dam, @@ -2679,6 +2695,13 @@ void monster::die( Creature *nkiller ) int morale_adjust = 0; if( type->has_anger_trigger( mon_trigger::FRIEND_DIED ) ) { anger_adjust += 15; + if( nkiller != nullptr && !nkiller->is_monster() ) { + // A character killed our friend + //add_msg_debug( debugmode::DF_MONSTER, "%s's character aggro triggered by killing a friendly %s", + // critter.name(), name() ); + // we do not have add_msg_debug in BN + aggro_character = true; + } } if( type->has_fear_trigger( mon_trigger::FRIEND_DIED ) ) { morale_adjust -= 15; @@ -3178,6 +3201,13 @@ void monster::on_hit( Creature *source, bodypart_id, dealt_projectile_attack con int morale_adjust = 0; if( type->has_anger_trigger( mon_trigger::FRIEND_ATTACKED ) ) { anger_adjust += 15; + if( source != nullptr && !source->is_monster() ) { + // A character attacked our friend + //add_msg_debug( debugmode::DF_MONSTER, "%s's character aggro triggered by attacking a friendly %s", + // critter.name(), name() ); + // not in BN + aggro_character = true; + } } if( type->has_fear_trigger( mon_trigger::FRIEND_ATTACKED ) ) { morale_adjust -= 15; @@ -3344,6 +3374,36 @@ void monster::on_load() if( dt <= 0_turns ) { return; } + + if( anger != type->agro ) { + int dt_left_a = to_turns( dt ); + + if( std::abs( anger - type->agro ) > 15 ) { + const int adjust_by_a = std::min( ( dt_left_a / 4 ), + ( std::abs( anger - type->agro ) - 15 ) ); + dt_left_a -= adjust_by_a * 4; + if( anger < type->agro ) { + anger += adjust_by_a; + } else { + anger -= adjust_by_a; + } + } + + if( anger > type->agro ) { + anger -= std::min( static_cast( std::ceil( dt_left_a / 8.0 ) ), + std::abs( anger - type->agro ) ); + } else { + anger += std::min( ( dt_left_a / 8 ), + std::abs( anger - type->agro ) ); + } + // If we got angry at characters have a chance at calming down + if( aggro_character && !type->aggro_character && !x_in_y( anger, 100 ) ) { + //add_msg_debug( debugmode::DF_MONSTER, "%s's character aggro reset", name() ); + aggro_character = false; + } + } + + float regen = type->regenerates; if( regen <= 0 ) { if( has_flag( MF_REVIVES ) ) { diff --git a/src/monster.h b/src/monster.h index 18d6cb72e161..59a6e5c9ffc9 100644 --- a/src/monster.h +++ b/src/monster.h @@ -624,6 +624,8 @@ class monster : public Creature, public location_visitable std::bitset effect_cache; std::optional summon_time_limit = std::nullopt; + bool aggro_character = true; + player *find_dragged_foe(); void nursebot_operate( player *dragged_foe ); diff --git a/src/monstergenerator.cpp b/src/monstergenerator.cpp index 01f1c5300cb1..e1ebf2eb0c14 100644 --- a/src/monstergenerator.cpp +++ b/src/monstergenerator.cpp @@ -813,7 +813,7 @@ void mtype::load( const JsonObject &jo, const std::string &src ) remove_regeneration_modifiers( tmp, "regeneration_modifiers", src ); } } - + optional( jo, was_loaded, "starting_ammo", starting_ammo ); optional( jo, was_loaded, "luminance", luminance, 0 ); optional( jo, was_loaded, "revert_to_itype", revert_to_itype, itype_id() ); @@ -821,6 +821,7 @@ void mtype::load( const JsonObject &jo, const std::string &src ) optional( jo, was_loaded, "mech_weapon", mech_weapon, itype_id() ); optional( jo, was_loaded, "mech_str_bonus", mech_str_bonus, 0 ); optional( jo, was_loaded, "mech_battery", mech_battery, itype_id() ); + optional( jo, was_loaded, "aggro_character", aggro_character, true ); // TODO: make this work with `was_loaded` if( jo.has_array( "melee_damage" ) ) { diff --git a/src/mtype.cpp b/src/mtype.cpp index ac37d6aa9556..7d06b99e3a3f 100644 --- a/src/mtype.cpp +++ b/src/mtype.cpp @@ -55,6 +55,8 @@ mtype::mtype() harvest = harvest_id( "human" ); luminance = 0; bash_skill = 0; + + aggro_character = true; flags .set( MF_HUMAN ) diff --git a/src/mtype.h b/src/mtype.h index 4127cd5ba011..f80ecee72498 100644 --- a/src/mtype.h +++ b/src/mtype.h @@ -369,6 +369,9 @@ struct mtype { bool was_loaded = false; bool upgrades; bool reproduces; + + // Do we indiscriminately attack characters, or should we wait until one annoys us? + bool aggro_character = true; mtype(); /** From d408285ca5879c26d6fe166f2c69021526a3b87f Mon Sep 17 00:00:00 2001 From: shmakota Date: Fri, 27 Dec 2024 01:55:13 -0600 Subject: [PATCH 2/9] add weakness trigger, fix matt_ignore and m_debug messages --- src/monmove.cpp | 13 ++++++++++--- src/monster.cpp | 21 +++++++++------------ 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/monmove.cpp b/src/monmove.cpp index 02f7e7e40a4b..d271b4817032 100644 --- a/src/monmove.cpp +++ b/src/monmove.cpp @@ -355,7 +355,7 @@ void monster::plan() if( dist <= 5 ) { anger += angers_hostile_near; if( angers_hostile_near && x_in_y( anger, 100 ) ) { - //add_msg_debug( debugmode::DF_MONSTER, "%s's character aggro triggered by proximity", name() ); + add_msg( m_debug, "%s's character aggro triggered by proximity", get_name() ); aggro_character = true; } morale -= fears_hostile_near; @@ -374,7 +374,7 @@ void monster::plan() if( mating_angry ) { anger += angers_mating_season; if( x_in_y( anger, 100 ) ) { - //add_msg_debug( debugmode::DF_MONSTER, "%s's character aggro triggered by season", name() ); + add_msg( m_debug, "%s's character aggro triggered by mating season", get_name() ); aggro_character = true; } } @@ -389,6 +389,7 @@ void monster::plan() //proximity to baby; monster gets furious and less likely to flee anger += angers_cub_threatened; morale += angers_cub_threatened / 2; + add_msg( m_debug, "%s's character aggro triggered by threatening cub", get_name() ); aggro_character = true; } } @@ -454,7 +455,7 @@ void monster::plan() if( mating_angry ) { anger += angers_mating_season; if( x_in_y( anger, 100 ) ) { - //add_msg_debug( debugmode::DF_MONSTER, "%s's character aggro triggered by season", name() ); + add_msg( m_debug, "%s's character aggro triggered by mating season", get_name() ); aggro_character = true; } } @@ -599,6 +600,12 @@ void monster::plan() int hp_per = target->hp_percentage(); if( hp_per <= 70 ) { anger += 10 - ( hp_per / 10 ); + if (anger <= 40 ) { + if( x_in_y( anger, 100 ) ) { + add_msg( m_debug, "%s's character aggro triggered by weakness", get_name() ); + aggro_character = true; + } + } } } } else if( friendly > 0 && one_in( 3 ) ) { diff --git a/src/monster.cpp b/src/monster.cpp index b8d557a46534..90bcdc6ce7b1 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -1387,10 +1387,6 @@ monster_attitude monster::attitude( const Character *u ) const return MATT_ATTACK; } - if( u != nullptr && !aggro_character && !u->is_monster() ) { - return MATT_IGNORE; - } - if( type->in_species( FUNGUS ) && ( u->has_trait( trait_THRESH_MYCUS ) || u->has_trait( trait_MYCUS_FRIEND ) ) ) { return MATT_FRIEND; @@ -1465,6 +1461,10 @@ monster_attitude monster::attitude( const Character *u ) const return MATT_FLEE; } + if( u != nullptr && !aggro_character && !u->is_monster() ) { + return MATT_IGNORE; + } + if( effective_anger <= 0 ) { if( get_hp() <= 0.6 * get_hp_max() ) { return MATT_FLEE; @@ -1521,7 +1521,7 @@ void monster::process_triggers() // If we got angry at characters have a chance at calming down if( anger == type->agro && aggro_character && !type->aggro_character && !x_in_y( anger, 100 ) ) { - //add_msg_debug( debugmode::DF_MONSTER, "%s's character aggro reset", name() ); + add_msg( m_debug, "%s's character aggro reset", get_name() ); aggro_character = false; } @@ -2697,9 +2697,7 @@ void monster::die( Creature *nkiller ) anger_adjust += 15; if( nkiller != nullptr && !nkiller->is_monster() ) { // A character killed our friend - //add_msg_debug( debugmode::DF_MONSTER, "%s's character aggro triggered by killing a friendly %s", - // critter.name(), name() ); - // we do not have add_msg_debug in BN + add_msg( m_debug, "%s's character aggro triggered by killing a friendly creature", name() ); aggro_character = true; } } @@ -3201,10 +3199,9 @@ void monster::on_hit( Creature *source, bodypart_id, dealt_projectile_attack con int morale_adjust = 0; if( type->has_anger_trigger( mon_trigger::FRIEND_ATTACKED ) ) { anger_adjust += 15; - if( source != nullptr && !source->is_monster() ) { + if( source != nullptr && !source->is_monster() ) { // A character attacked our friend - //add_msg_debug( debugmode::DF_MONSTER, "%s's character aggro triggered by attacking a friendly %s", - // critter.name(), name() ); + add_msg( m_debug, "%s's character aggro triggered by attacking a friendly creature", name() ); // not in BN aggro_character = true; } @@ -3398,7 +3395,7 @@ void monster::on_load() } // If we got angry at characters have a chance at calming down if( aggro_character && !type->aggro_character && !x_in_y( anger, 100 ) ) { - //add_msg_debug( debugmode::DF_MONSTER, "%s's character aggro reset", name() ); + add_msg( m_debug, "%s's character aggro reset", name() ); aggro_character = false; } } From dd23281f060f0412c083a665e0cd23e8f1dbeeb1 Mon Sep 17 00:00:00 2001 From: shmakota Date: Fri, 27 Dec 2024 14:40:56 -0600 Subject: [PATCH 3/9] used formatter. change location of aggro ignore check --- src/monmove.cpp | 4 ++-- src/monster.cpp | 18 ++++++++++-------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/monmove.cpp b/src/monmove.cpp index d271b4817032..afb81257722c 100644 --- a/src/monmove.cpp +++ b/src/monmove.cpp @@ -454,7 +454,7 @@ void monster::plan() } if( mating_angry ) { anger += angers_mating_season; - if( x_in_y( anger, 100 ) ) { + if( x_in_y( anger, 100 ) ) { add_msg( m_debug, "%s's character aggro triggered by mating season", get_name() ); aggro_character = true; } @@ -600,7 +600,7 @@ void monster::plan() int hp_per = target->hp_percentage(); if( hp_per <= 70 ) { anger += 10 - ( hp_per / 10 ); - if (anger <= 40 ) { + if( anger <= 40 ) { if( x_in_y( anger, 100 ) ) { add_msg( m_debug, "%s's character aggro triggered by weakness", get_name() ); aggro_character = true; diff --git a/src/monster.cpp b/src/monster.cpp index 90bcdc6ce7b1..f254e61e5586 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -1454,17 +1454,13 @@ monster_attitude monster::attitude( const Character *u ) const } } + if( effective_morale < 0 ) { if( effective_morale + effective_anger > 0 && get_hp() > get_hp_max() / 3 ) { return MATT_FOLLOW; } return MATT_FLEE; } - - if( u != nullptr && !aggro_character && !u->is_monster() ) { - return MATT_IGNORE; - } - if( effective_anger <= 0 ) { if( get_hp() <= 0.6 * get_hp_max() ) { return MATT_FLEE; @@ -1477,6 +1473,10 @@ monster_attitude monster::attitude( const Character *u ) const return MATT_FOLLOW; } + if( u != nullptr && !aggro_character && !u->is_monster() ) { + return MATT_IGNORE; + } + return MATT_ATTACK; } @@ -1959,8 +1959,10 @@ void monster::apply_damage( Creature *source, item *source_weapon, item *source_ } } else if( dam > 0 ) { process_trigger( mon_trigger::HURT, 1 + ( dam / 3 ) ); - // Get angry at characters if hurt by one + // Get angry at characters if hurt by one if( source != nullptr && !aggro_character && !source->is_monster() ) { + add_msg( m_debug, "%s's aggro triggered by hurt", get_name() ); + add_msg( m_bad, "%s's aggro triggered by hurt", get_name() ); aggro_character = true; } } @@ -2695,7 +2697,7 @@ void monster::die( Creature *nkiller ) int morale_adjust = 0; if( type->has_anger_trigger( mon_trigger::FRIEND_DIED ) ) { anger_adjust += 15; - if( nkiller != nullptr && !nkiller->is_monster() ) { + if( nkiller != nullptr && !nkiller->is_monster() ) { // A character killed our friend add_msg( m_debug, "%s's character aggro triggered by killing a friendly creature", name() ); aggro_character = true; @@ -3400,7 +3402,7 @@ void monster::on_load() } } - + float regen = type->regenerates; if( regen <= 0 ) { if( has_flag( MF_REVIVES ) ) { From e448c4c8124b6aa55fc66bfcfe866ef175276433 Mon Sep 17 00:00:00 2001 From: shmakota Date: Fri, 27 Dec 2024 15:22:40 -0600 Subject: [PATCH 4/9] first pass of aggro_character variables on mons --- data/json/monsters/bird.json | 5 +++++ data/json/monsters/fish.json | 2 ++ data/json/monsters/insect_spider.json | 18 ++++++++++++++++++ data/json/monsters/mammal.json | 16 ++++++++++++++++ data/json/monsters/mutant_animal.json | 5 +++++ data/json/monsters/mutant_human.json | 1 + data/json/monsters/reptile_amphibian.json | 3 +++ 7 files changed, 50 insertions(+) diff --git a/data/json/monsters/bird.json b/data/json/monsters/bird.json index 6b3e2e45d8ba..dd178c578865 100644 --- a/data/json/monsters/bird.json +++ b/data/json/monsters/bird.json @@ -108,11 +108,16 @@ "type": "MONSTER", "copy-from": "mon_duck", "name": { "str": "goose", "str_pl": "geese" }, + "aggression": 0, + "morale": 15, + "aggro_character": false, "description": "A Canadian goose, a common waterfowl that regrets leaving Canada.", "volume": "5 L", "weight": "3750 g", "hp": 15, "dodge": 3, + "anger_triggers": [ "PLAYER_CLOSE", "FRIEND_ATTACKED" ], + "fear_triggers": [ "FRIEND_DIED", "FIRE" ], "reproduction": { "baby_egg": "egg_goose_canadian", "baby_count": 3, "baby_timer": 10 }, "baby_flags": [ "SPRING" ] }, diff --git a/data/json/monsters/fish.json b/data/json/monsters/fish.json index 7fe0066845d2..5b6544579c95 100644 --- a/data/json/monsters/fish.json +++ b/data/json/monsters/fish.json @@ -15,6 +15,7 @@ "material": [ "flesh" ], "symbol": "Y", "color": "red", + "aggro_character": false, "aggression": 15, "morale": 100, "melee_skill": 6, @@ -645,6 +646,7 @@ "material": [ "flesh" ], "symbol": "Y", "color": "magenta", + "aggro_character": false, "aggression": 4, "morale": 100, "melee_skill": 6, diff --git a/data/json/monsters/insect_spider.json b/data/json/monsters/insect_spider.json index 796c4b305840..23a8c156c99c 100644 --- a/data/json/monsters/insect_spider.json +++ b/data/json/monsters/insect_spider.json @@ -326,6 +326,7 @@ "material": [ "iflesh" ], "symbol": "a", "color": "yellow", + "aggro_character": false, "aggression": -10, "morale": 20, "melee_skill": 4, @@ -367,6 +368,7 @@ "hp": 15, "speed": 115, "attack_cost": 115, + "aggro_character": false, "aggression": 0, "morale": 0, "melee_skill": 5, @@ -406,6 +408,7 @@ "hp": 35, "speed": 110, "attack_cost": 110, + "aggro_character": false, "aggression": 9, "morale": 20, "melee_skill": 6, @@ -451,6 +454,7 @@ "volume": "300 L", "weight": "400 kg", "speed": 60, + "aggro_character": false, "aggression": 0, "morale": 10, "melee_skill": 3, @@ -499,6 +503,7 @@ "hp": 125, "speed": 110, "attack_cost": 110, + "aggro_character": false, "aggression": 100, "regen_morale": true, "melee_skill": 6, @@ -746,6 +751,7 @@ "material": [ "iflesh" ], "symbol": "s", "color": "brown", + "aggro_character": false, "aggression": -10, "morale": 100, "melee_skill": 6, @@ -815,6 +821,7 @@ "material": [ "iflesh" ], "symbol": "s", "color": "white", + "aggro_character": false, "aggression": -20, "morale": 80, "melee_skill": 5, @@ -882,6 +889,7 @@ "material": [ "iflesh" ], "symbol": "s", "color": "yellow", + "aggro_character": false, "aggression": 20, "morale": 100, "melee_skill": 4, @@ -914,6 +922,7 @@ "material": [ "iflesh" ], "symbol": "c", "color": "yellow", + "aggro_character": false, "aggression": 20, "morale": 100, "melee_skill": 1, @@ -946,6 +955,7 @@ "material": [ "iflesh" ], "symbol": "s", "color": "dark_gray", + "aggro_character": false, "aggression": -10, "morale": 100, "melee_skill": 6, @@ -980,6 +990,7 @@ "material": [ "iflesh" ], "symbol": "c", "color": "dark_gray", + "aggro_character": false, "aggression": -10, "morale": 100, "melee_skill": 4, @@ -1013,6 +1024,7 @@ "material": [ "iflesh" ], "symbol": "s", "color": "brown", + "aggro_character": false, "aggression": 20, "morale": 100, "melee_skill": 5, @@ -1086,6 +1098,7 @@ "material": [ "iflesh" ], "symbol": "a", "color": "dark_gray", + "aggro_character": false, "aggression": -10, "melee_skill": 5, "melee_dice": 2, @@ -1171,6 +1184,7 @@ "material": [ "iflesh" ], "symbol": "a", "color": "dark_gray", + "aggro_character": false, "aggression": -5, "morale": 15, "melee_skill": 7, @@ -1421,6 +1435,7 @@ "material": [ "iflesh" ], "symbol": "a", "color": "brown", + "aggro_character": false, "morale": 60, "melee_skill": 5, "melee_dice": 1, @@ -1456,6 +1471,7 @@ "material": [ "iflesh" ], "symbol": "a", "color": "green", + "aggro_character": false, "morale": 60, "melee_skill": 5, "melee_dice": 1, @@ -1548,6 +1564,7 @@ "material": [ "iflesh" ], "symbol": "a", "color": "brown", + "aggro_character": false, "aggression": 40, "morale": 100, "melee_skill": 7, @@ -1638,6 +1655,7 @@ "material": [ "iflesh" ], "symbol": "a", "color": "red", + "aggro_character": false, "aggression": 30, "morale": 100, "melee_skill": 7, diff --git a/data/json/monsters/mammal.json b/data/json/monsters/mammal.json index b59aee9c4edc..3f5e84c60709 100644 --- a/data/json/monsters/mammal.json +++ b/data/json/monsters/mammal.json @@ -67,6 +67,7 @@ "material": [ "flesh" ], "symbol": "B", "color": "dark_gray", + "aggro_character": false, "aggression": -10, "morale": 60, "melee_skill": 6, @@ -107,6 +108,7 @@ "material": [ "flesh" ], "symbol": "r", "color": "brown", + "aggro_character": false, "aggression": -35, "morale": 15, "melee_dice": 1, @@ -135,6 +137,7 @@ "material": [ "flesh" ], "symbol": "r", "color": "dark_gray", + "aggro_character": false, "aggression": 5, "morale": 5, "melee_skill": 5, @@ -169,6 +172,7 @@ "symbol": "b", "color": "brown", "looks_like": "mon_pig_piglet", + "aggro_character": false, "aggression": -10, "morale": 10, "melee_skill": 1, @@ -202,6 +206,7 @@ "material": [ "flesh" ], "symbol": "b", "color": "brown", + "aggro_character": false, "aggression": 20, "morale": 40, "melee_skill": 5, @@ -239,6 +244,7 @@ "material": [ "flesh" ], "symbol": "c", "color": "brown", + "aggro_character": false, "aggression": -25, "morale": 1, "melee_skill": 6, @@ -661,6 +667,7 @@ "material": [ "flesh" ], "symbol": "C", "color": "light_gray", + "aggro_character": false, "aggression": -50, "morale": 60, "melee_skill": 6, @@ -835,6 +842,7 @@ "material": [ "flesh" ], "symbol": "d", "color": "light_gray", + "aggro_character": false, "aggression": -10, "morale": 15, "melee_skill": 6, @@ -881,6 +889,7 @@ "weight": "1 kg", "hp": 8, "speed": 98, + "aggro_character": false, "aggression": -13, "morale": 8, "melee_skill": 2, @@ -1699,6 +1708,7 @@ "material": [ "flesh" ], "symbol": "d", "color": "light_gray", + "aggro_character": false, "aggression": -5, "morale": 30, "melee_skill": 3, @@ -1730,6 +1740,7 @@ "material": [ "flesh" ], "symbol": "d", "color": "red", + "aggro_character": false, "aggression": -5, "morale": 60, "melee_skill": 4, @@ -1848,6 +1859,7 @@ "material": [ "flesh" ], "symbol": "H", "color": "brown", + "aggro_character": false, "melee_skill": 6, "melee_dice": 2, "melee_dice_sides": 12, @@ -1937,6 +1949,7 @@ "material": [ "flesh" ], "symbol": "M", "color": "brown", + "aggro_character": false, "aggression": 5, "morale": 80, "melee_skill": 6, @@ -2131,6 +2144,7 @@ "material": [ "flesh" ], "symbol": "p", "color": "pink", + "aggro_character": false, "aggression": 10, "morale": 30, "melee_skill": 4, @@ -2248,6 +2262,7 @@ "material": [ "flesh" ], "symbol": "r", "color": "light_gray", + "aggro_character": false, "aggression": 20, "morale": 40, "melee_skill": 5, @@ -2425,6 +2440,7 @@ "material": [ "flesh" ], "symbol": "w", "color": "light_gray", + "aggro_character": false, "morale": 20, "melee_skill": 7, "melee_dice": 2, diff --git a/data/json/monsters/mutant_animal.json b/data/json/monsters/mutant_animal.json index e65b82c32a4a..663c06a37c90 100644 --- a/data/json/monsters/mutant_animal.json +++ b/data/json/monsters/mutant_animal.json @@ -6,6 +6,7 @@ "description": "As it moves, this cat's colors shift unnaturally in the light. You can't make out the breed, but its fur pattern seems to change with every step.", "copy-from": "mon_cat", "luminance": 5, + "aggro_character": false, "//": "Higher dodge due to the constantly changing fur pattern", "dodge": 9, "reproduction": { "baby_monster": "mon_cat_mutant_kitten_prism", "baby_count": 2, "baby_timer": 80 }, @@ -109,6 +110,7 @@ "symbol": "B", "color": "dark_gray", "looks_like": "mon_bear", + "aggro_character": false, "morale": 60, "melee_skill": 6, "melee_dice": 4, @@ -151,6 +153,7 @@ "symbol": "r", "color": "brown", "looks_like": "mon_beaver", + "aggro_character": false, "aggression": -10, "morale": 20, "melee_dice": 2, @@ -181,6 +184,7 @@ "symbol": "r", "color": "brown", "looks_like": "mon_beaver", + "aggro_character": false, "aggression": -50, "morale": 5, "melee_dice": 1, @@ -407,6 +411,7 @@ "symbol": "d", "color": "light_gray", "looks_like": "mon_dog", + "aggro_character": false, "aggression": -15, "morale": 15, "melee_skill": 6, diff --git a/data/json/monsters/mutant_human.json b/data/json/monsters/mutant_human.json index a0442be37f23..9a358652ccc0 100644 --- a/data/json/monsters/mutant_human.json +++ b/data/json/monsters/mutant_human.json @@ -47,6 +47,7 @@ "symbol": "M", "color": "brown", "morale": 50, + "aggro_character": false, "melee_skill": 6, "melee_dice": 3, "melee_dice_sides": 6, diff --git a/data/json/monsters/reptile_amphibian.json b/data/json/monsters/reptile_amphibian.json index 0d46cb9dd04f..41782581b36b 100644 --- a/data/json/monsters/reptile_amphibian.json +++ b/data/json/monsters/reptile_amphibian.json @@ -14,6 +14,7 @@ "material": [ "flesh" ], "symbol": "M", "color": "light_green", + "aggro_character": false, "aggression": 10, "morale": 100, "melee_skill": 5, @@ -95,6 +96,7 @@ "material": [ "flesh" ], "symbol": "s", "color": "brown", + "aggro_character": false, "aggression": -50, "morale": 60, "melee_skill": 5, @@ -129,6 +131,7 @@ "material": [ "flesh" ], "symbol": "s", "color": "brown", + "aggro_character": false, "aggression": -10, "morale": 60, "melee_skill": 6, From a690641ff07e152a8da9325c1f660bb40e6cd027 Mon Sep 17 00:00:00 2001 From: shmakota Date: Fri, 27 Dec 2024 20:33:26 -0600 Subject: [PATCH 5/9] add documentation --- .../docs/en/mod/json/reference/creatures/monsters.md | 5 +++++ doc/src/content/docs/en/mod/json/reference/json_flags.md | 8 ++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/doc/src/content/docs/en/mod/json/reference/creatures/monsters.md b/doc/src/content/docs/en/mod/json/reference/creatures/monsters.md index fa32a2afc808..9be824587231 100644 --- a/doc/src/content/docs/en/mod/json/reference/creatures/monsters.md +++ b/doc/src/content/docs/en/mod/json/reference/creatures/monsters.md @@ -218,6 +218,11 @@ hostility on detection) Monster morale. Defines how low monster HP can get before it retreats. This number is treated as % of their max HP. +## "aggro_character" +(bool, optional, default true) + +If the monster will differentiate between monsters and characters (NPC, Player) when deciding on targets - if false the monster will ignore characters regardless of current anger/morale until a character trips and anger trigger. Resets randomly when the monster is at its base anger level. + ## "speed" (integer) diff --git a/doc/src/content/docs/en/mod/json/reference/json_flags.md b/doc/src/content/docs/en/mod/json/reference/json_flags.md index c423df3c07da..b89176243bb1 100644 --- a/doc/src/content/docs/en/mod/json/reference/json_flags.md +++ b/doc/src/content/docs/en/mod/json/reference/json_flags.md @@ -937,12 +937,12 @@ Flags used to describe monsters and define their properties and abilities. ### Anger, Fear and Placation Triggers - `FIRE` There's a fire nearby. -- `FRIEND_ATTACKED` A monster of the same type was attacked. -- `FRIEND_DIED` A monster of the same type died. -- `HURT` The monster is hurt. +- `FRIEND_ATTACKED` A monster of the same type was attacked. Always triggers character aggro. +- `FRIEND_DIED` A monster of the same type died. Always triggers character aggro. +- `HURT` The monster is hurt. Always triggers character aggro. Triggers character aggro `%` of the time. - `MEAT` Meat or a corpse is nearby. - `NULL` Source use only? -- `PLAYER_CLOSE` The player gets within a few tiles distance. +- `PLAYER_CLOSE` The player gets within a few tiles distance. Triggers character aggro `%` of the time. - `PLAYER_WEAK` The player is hurt. - `SOUND` Heard a sound. - `STALK` Increases when following the player. From 8bdbe2480b4aaae7c66bfcfd81f53465211f5b76 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sat, 28 Dec 2024 05:15:42 +0000 Subject: [PATCH 6/9] style(autofix.ci): automated formatting --- .../docs/en/mod/json/reference/creatures/monsters.md | 5 ++++- doc/src/content/docs/en/mod/json/reference/json_flags.md | 6 ++++-- src/monstergenerator.cpp | 2 +- src/mtype.cpp | 2 +- src/mtype.h | 2 +- 5 files changed, 11 insertions(+), 6 deletions(-) diff --git a/doc/src/content/docs/en/mod/json/reference/creatures/monsters.md b/doc/src/content/docs/en/mod/json/reference/creatures/monsters.md index 9be824587231..184acc03f23f 100644 --- a/doc/src/content/docs/en/mod/json/reference/creatures/monsters.md +++ b/doc/src/content/docs/en/mod/json/reference/creatures/monsters.md @@ -219,9 +219,12 @@ Monster morale. Defines how low monster HP can get before it retreats. This numb of their max HP. ## "aggro_character" + (bool, optional, default true) -If the monster will differentiate between monsters and characters (NPC, Player) when deciding on targets - if false the monster will ignore characters regardless of current anger/morale until a character trips and anger trigger. Resets randomly when the monster is at its base anger level. +If the monster will differentiate between monsters and characters (NPC, Player) when deciding on +targets - if false the monster will ignore characters regardless of current anger/morale until a +character trips and anger trigger. Resets randomly when the monster is at its base anger level. ## "speed" diff --git a/doc/src/content/docs/en/mod/json/reference/json_flags.md b/doc/src/content/docs/en/mod/json/reference/json_flags.md index b89176243bb1..666e07009032 100644 --- a/doc/src/content/docs/en/mod/json/reference/json_flags.md +++ b/doc/src/content/docs/en/mod/json/reference/json_flags.md @@ -939,10 +939,12 @@ Flags used to describe monsters and define their properties and abilities. - `FIRE` There's a fire nearby. - `FRIEND_ATTACKED` A monster of the same type was attacked. Always triggers character aggro. - `FRIEND_DIED` A monster of the same type died. Always triggers character aggro. -- `HURT` The monster is hurt. Always triggers character aggro. Triggers character aggro `%` of the time. +- `HURT` The monster is hurt. Always triggers character aggro. Triggers character aggro `%` + of the time. - `MEAT` Meat or a corpse is nearby. - `NULL` Source use only? -- `PLAYER_CLOSE` The player gets within a few tiles distance. Triggers character aggro `%` of the time. +- `PLAYER_CLOSE` The player gets within a few tiles distance. Triggers character aggro `%` of + the time. - `PLAYER_WEAK` The player is hurt. - `SOUND` Heard a sound. - `STALK` Increases when following the player. diff --git a/src/monstergenerator.cpp b/src/monstergenerator.cpp index e1ebf2eb0c14..cead2bf257f9 100644 --- a/src/monstergenerator.cpp +++ b/src/monstergenerator.cpp @@ -813,7 +813,7 @@ void mtype::load( const JsonObject &jo, const std::string &src ) remove_regeneration_modifiers( tmp, "regeneration_modifiers", src ); } } - + optional( jo, was_loaded, "starting_ammo", starting_ammo ); optional( jo, was_loaded, "luminance", luminance, 0 ); optional( jo, was_loaded, "revert_to_itype", revert_to_itype, itype_id() ); diff --git a/src/mtype.cpp b/src/mtype.cpp index 7d06b99e3a3f..fc340a804403 100644 --- a/src/mtype.cpp +++ b/src/mtype.cpp @@ -55,7 +55,7 @@ mtype::mtype() harvest = harvest_id( "human" ); luminance = 0; bash_skill = 0; - + aggro_character = true; flags diff --git a/src/mtype.h b/src/mtype.h index f80ecee72498..2345ffc27776 100644 --- a/src/mtype.h +++ b/src/mtype.h @@ -369,7 +369,7 @@ struct mtype { bool was_loaded = false; bool upgrades; bool reproduces; - + // Do we indiscriminately attack characters, or should we wait until one annoys us? bool aggro_character = true; From ecf7f09845943a7b08a3046be2d176168393217d Mon Sep 17 00:00:00 2001 From: shmakota Date: Fri, 3 Jan 2025 18:24:45 -0600 Subject: [PATCH 7/9] npcs should no longer aggro from a non-player fight & save/load aggro_character tracking Co-Authored-By: Eric <52087122+Ramza13@users.noreply.github.com> Co-Authored-By: Venera3 <72006894+Venera3@users.noreply.github.com> --- src/mattack_actors.cpp | 2 +- src/monster.cpp | 6 +++--- src/monster.h | 4 +++- src/savegame_json.cpp | 2 ++ 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/mattack_actors.cpp b/src/mattack_actors.cpp index 9234b133e15e..7ceaf2589868 100644 --- a/src/mattack_actors.cpp +++ b/src/mattack_actors.cpp @@ -579,7 +579,7 @@ bool gun_actor::call( monster &z ) const aim_at = target->pos(); } else { target = z.attack_target(); - if( !target || !z.sees( *target ) ) { + if( !target || !z.sees( *target ) || ( !target->is_monster() && !z.aggro_character ) ) { if( !target_moving_vehicles ) { return false; } diff --git a/src/monster.cpp b/src/monster.cpp index f254e61e5586..37739bfac694 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -1960,7 +1960,7 @@ void monster::apply_damage( Creature *source, item *source_weapon, item *source_ } else if( dam > 0 ) { process_trigger( mon_trigger::HURT, 1 + ( dam / 3 ) ); // Get angry at characters if hurt by one - if( source != nullptr && !aggro_character && !source->is_monster() ) { + if( source != nullptr && !aggro_character && !source->is_monster() && !source->is_fake() ) { add_msg( m_debug, "%s's aggro triggered by hurt", get_name() ); add_msg( m_bad, "%s's aggro triggered by hurt", get_name() ); aggro_character = true; @@ -2697,7 +2697,7 @@ void monster::die( Creature *nkiller ) int morale_adjust = 0; if( type->has_anger_trigger( mon_trigger::FRIEND_DIED ) ) { anger_adjust += 15; - if( nkiller != nullptr && !nkiller->is_monster() ) { + if( nkiller != nullptr && !nkiller->is_monster() && !nkiller->is_fake() ) { // A character killed our friend add_msg( m_debug, "%s's character aggro triggered by killing a friendly creature", name() ); aggro_character = true; @@ -3201,7 +3201,7 @@ void monster::on_hit( Creature *source, bodypart_id, dealt_projectile_attack con int morale_adjust = 0; if( type->has_anger_trigger( mon_trigger::FRIEND_ATTACKED ) ) { anger_adjust += 15; - if( source != nullptr && !source->is_monster() ) { + if( source != nullptr && !aggro_character && !source->is_monster() && !source->is_fake() ) { // A character attacked our friend add_msg( m_debug, "%s's character aggro triggered by attacking a friendly creature", name() ); // not in BN diff --git a/src/monster.h b/src/monster.h index 59a6e5c9ffc9..d58e88578714 100644 --- a/src/monster.h +++ b/src/monster.h @@ -532,6 +532,9 @@ class monster : public Creature, public location_visitable } short ignoring; + + bool aggro_character = true; + std::optional lastseen_turn; // Stair data. @@ -624,7 +627,6 @@ class monster : public Creature, public location_visitable std::bitset effect_cache; std::optional summon_time_limit = std::nullopt; - bool aggro_character = true; player *find_dragged_foe(); void nursebot_operate( player *dragged_foe ); diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index a1be2df9c56d..c32f349db388 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -1843,6 +1843,7 @@ void monster::load( const JsonObject &data ) data.read( "anger", anger ); data.read( "morale", morale ); data.read( "hallucination", hallucination ); + data.read( "aggro_character", aggro_character ); data.read( "stairscount", staircount ); // really? data.read( "fish_population", fish_population ); // Load legacy plans. @@ -1935,6 +1936,7 @@ void monster::store( JsonOut &json ) const json.member( "anger", anger ); json.member( "morale", morale ); json.member( "hallucination", hallucination ); + json.member( "aggro_character", aggro_character ); json.member( "stairscount", staircount ); if( tied_item ) { json.member( "tied_item", *tied_item ); From 6250779697e6a8ded7c7e78673d45a93d685f8d7 Mon Sep 17 00:00:00 2001 From: shmakota Date: Fri, 3 Jan 2025 23:32:38 -0600 Subject: [PATCH 8/9] oops debug message --- src/monster.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/monster.cpp b/src/monster.cpp index 37739bfac694..6f4c4e00170f 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -1962,7 +1962,6 @@ void monster::apply_damage( Creature *source, item *source_weapon, item *source_ // Get angry at characters if hurt by one if( source != nullptr && !aggro_character && !source->is_monster() && !source->is_fake() ) { add_msg( m_debug, "%s's aggro triggered by hurt", get_name() ); - add_msg( m_bad, "%s's aggro triggered by hurt", get_name() ); aggro_character = true; } } From a961bea43f882d9b96191d9b387fd54f4a7859d9 Mon Sep 17 00:00:00 2001 From: shmakota Date: Sun, 5 Jan 2025 14:54:20 -0600 Subject: [PATCH 9/9] cleanup cleanup --- .../docs/en/mod/json/reference/json_flags.md | 3 +-- src/mattack_actors.cpp | 2 +- src/monmove.cpp | 23 +++++----------- src/monster.cpp | 26 ++++++++++++++----- src/monster.h | 3 +++ 5 files changed, 30 insertions(+), 27 deletions(-) diff --git a/doc/src/content/docs/en/mod/json/reference/json_flags.md b/doc/src/content/docs/en/mod/json/reference/json_flags.md index 666e07009032..8d0d65b9293a 100644 --- a/doc/src/content/docs/en/mod/json/reference/json_flags.md +++ b/doc/src/content/docs/en/mod/json/reference/json_flags.md @@ -939,8 +939,7 @@ Flags used to describe monsters and define their properties and abilities. - `FIRE` There's a fire nearby. - `FRIEND_ATTACKED` A monster of the same type was attacked. Always triggers character aggro. - `FRIEND_DIED` A monster of the same type died. Always triggers character aggro. -- `HURT` The monster is hurt. Always triggers character aggro. Triggers character aggro `%` - of the time. +- `HURT` The monster is hurt. Always triggers character aggro. - `MEAT` Meat or a corpse is nearby. - `NULL` Source use only? - `PLAYER_CLOSE` The player gets within a few tiles distance. Triggers character aggro `%` of diff --git a/src/mattack_actors.cpp b/src/mattack_actors.cpp index 7ceaf2589868..fe240468d8c0 100644 --- a/src/mattack_actors.cpp +++ b/src/mattack_actors.cpp @@ -579,7 +579,7 @@ bool gun_actor::call( monster &z ) const aim_at = target->pos(); } else { target = z.attack_target(); - if( !target || !z.sees( *target ) || ( !target->is_monster() && !z.aggro_character ) ) { + if( !target || ( !target->is_monster() && !z.aggro_character ) || !z.sees( *target ) ) { if( !target_moving_vehicles ) { return false; } diff --git a/src/monmove.cpp b/src/monmove.cpp index afb81257722c..674496a6080d 100644 --- a/src/monmove.cpp +++ b/src/monmove.cpp @@ -354,9 +354,8 @@ void monster::plan() target = &g->u; if( dist <= 5 ) { anger += angers_hostile_near; - if( angers_hostile_near && x_in_y( anger, 100 ) ) { - add_msg( m_debug, "%s's character aggro triggered by proximity", get_name() ); - aggro_character = true; + if( angers_hostile_near ) { + trigger_character_aggro_chance( anger, "proximity" ); } morale -= fears_hostile_near; if( angers_mating_season > 0 ) { @@ -373,10 +372,7 @@ void monster::plan() } if( mating_angry ) { anger += angers_mating_season; - if( x_in_y( anger, 100 ) ) { - add_msg( m_debug, "%s's character aggro triggered by mating season", get_name() ); - aggro_character = true; - } + trigger_character_aggro_chance( anger, "mating season" ); } } } @@ -389,8 +385,7 @@ void monster::plan() //proximity to baby; monster gets furious and less likely to flee anger += angers_cub_threatened; morale += angers_cub_threatened / 2; - add_msg( m_debug, "%s's character aggro triggered by threatening cub", get_name() ); - aggro_character = true; + trigger_character_aggro( "threatening cub" ); } } } @@ -454,10 +449,7 @@ void monster::plan() } if( mating_angry ) { anger += angers_mating_season; - if( x_in_y( anger, 100 ) ) { - add_msg( m_debug, "%s's character aggro triggered by mating season", get_name() ); - aggro_character = true; - } + trigger_character_aggro_chance( anger, "mating season" ); } } } @@ -601,10 +593,7 @@ void monster::plan() if( hp_per <= 70 ) { anger += 10 - ( hp_per / 10 ); if( anger <= 40 ) { - if( x_in_y( anger, 100 ) ) { - add_msg( m_debug, "%s's character aggro triggered by weakness", get_name() ); - aggro_character = true; - } + trigger_character_aggro_chance( anger, "weakness" ); } } } diff --git a/src/monster.cpp b/src/monster.cpp index 6f4c4e00170f..b8bf6545e6b8 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -1557,6 +1557,22 @@ void monster::process_trigger( mon_trigger trig, const std::function &amo } } + +// hopefully a good spot for these functions +// eg. reason = "mating season" +void monster::trigger_character_aggro( const char *reason ) +{ + add_msg( m_debug, "%s's character aggro is triggered by %s", get_name(), reason ); + aggro_character = true; +} + +void monster::trigger_character_aggro_chance( int chance, const char *reason ) +{ + if( x_in_y( chance, 100 ) ) { + trigger_character_aggro( reason ); + } +} + bool monster::is_underwater() const { return Creature::is_underwater() && can_submerge(); @@ -1961,8 +1977,7 @@ void monster::apply_damage( Creature *source, item *source_weapon, item *source_ process_trigger( mon_trigger::HURT, 1 + ( dam / 3 ) ); // Get angry at characters if hurt by one if( source != nullptr && !aggro_character && !source->is_monster() && !source->is_fake() ) { - add_msg( m_debug, "%s's aggro triggered by hurt", get_name() ); - aggro_character = true; + trigger_character_aggro( "hurt" ); } } } @@ -2698,8 +2713,7 @@ void monster::die( Creature *nkiller ) anger_adjust += 15; if( nkiller != nullptr && !nkiller->is_monster() && !nkiller->is_fake() ) { // A character killed our friend - add_msg( m_debug, "%s's character aggro triggered by killing a friendly creature", name() ); - aggro_character = true; + trigger_character_aggro( "killing a friendly creature" ); } } if( type->has_fear_trigger( mon_trigger::FRIEND_DIED ) ) { @@ -3202,9 +3216,7 @@ void monster::on_hit( Creature *source, bodypart_id, dealt_projectile_attack con anger_adjust += 15; if( source != nullptr && !aggro_character && !source->is_monster() && !source->is_fake() ) { // A character attacked our friend - add_msg( m_debug, "%s's character aggro triggered by attacking a friendly creature", name() ); - // not in BN - aggro_character = true; + trigger_character_aggro( "killing a friendly creature" ); } } if( type->has_fear_trigger( mon_trigger::FRIEND_ATTACKED ) ) { diff --git a/src/monster.h b/src/monster.h index d58e88578714..8f339a558ad6 100644 --- a/src/monster.h +++ b/src/monster.h @@ -604,6 +604,9 @@ class monster : public Creature, public location_visitable void process_trigger( mon_trigger trig, int amount ); void process_trigger( mon_trigger trig, const std::function &amount_func ); + void trigger_character_aggro( const char *reason ); + void trigger_character_aggro_chance( int chance, const char *reason ); + location_vector corpse_components; // Hack to make bionic corpses generate CBMs on death private: