From 54b5684c94a5a75120bd6b25c4df5a1de701f284 Mon Sep 17 00:00:00 2001 From: Anton Simakov <67688115+GuardianDll@users.noreply.github.com> Date: Fri, 18 Oct 2024 02:03:00 +0200 Subject: [PATCH] Add EoC effect that deal damage (#77009) * deal damage eoc effect * add proper talker_charactere override * it was a test EoC, it should not be here * add handling for monsters, document thing * Update src/talker.h Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update src/npctalk.cpp * Update doc/EFFECT_ON_CONDITION.md Co-authored-by: Anton Burmistrov * remove unc, they can't be used with this EoC effect --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Anton Burmistrov --- doc/EFFECT_ON_CONDITION.md | 34 +++++++++++++++++++++++ src/npctalk.cpp | 57 ++++++++++++++++++++++++++++++++++++++ src/talker.h | 4 +++ src/talker_character.cpp | 6 ++++ src/talker_character.h | 2 ++ src/talker_monster.cpp | 6 ++++ src/talker_monster.h | 2 ++ 7 files changed, 111 insertions(+) diff --git a/doc/EFFECT_ON_CONDITION.md b/doc/EFFECT_ON_CONDITION.md index 4bdbc75948049..e27cb2dfc8536 100644 --- a/doc/EFFECT_ON_CONDITION.md +++ b/doc/EFFECT_ON_CONDITION.md @@ -2607,6 +2607,40 @@ A loop of 10 iterations. ## Character effects +#### `u_deal_damage`, `npc_deal_damage` + +Deal damage, the same way melee attack deals damage; it can't be dodged, but it can be mitigated by armor + +| Syntax | Optionality | Value | Info | +| --- | --- | --- | --- | +| "u_deal_damage" / "npc_deal_damage" | **mandatory** | string or [variable object](#variable-object) | Damage type that would be dealt | +| "amount" | optional | int or [variable object](#variable-object) | Amount of damage that would be dealt; Default 0 | +| "bodypart" | optional | string or [variable object](#variable-object) | Bodypart that take the damage. Reminder that only characters can have limbs. Default is RANDOM | +| "arpen" | optional | int or [variable object](#variable-object) | Armor penetration of attack; Default 0 | +| "arpen_mult" | optional | int or [variable object](#variable-object) | Multiplier for armor penetration; Default 1 | +| "dmg_mult" | optional | int or [variable object](#variable-object) | Multiplier for damage amount. Default 1 | +| "min_hit" | optional | int or [variable object](#variable-object) | If bodypart is RANDOM, limit body part only to bodyparts that has `hit_size` bigger than this; default -1 | +| "max_hit" | optional | int or [variable object](#variable-object) | If bodypart is RANDOM, limit body part only to bodyparts that has `hit_size` smaller than this; default the size of your biggest body part | +| "can_attack_high" | optional | bool | If true, can attack limbs with flag LIMB_UPPER, if false, such limbs are discarded; Default true | +| "hit_roll" | optional | int or [variable object](#variable-object) | hit_roll | + +##### Valid talkers: + +| Avatar | Character | NPC | Monster | Furniture | Item | +| ------ | --------- | --------- | ---- | ------- | --- | +| ✔️ | ✔️ | ✔️ | ✔️ | ❌ | ❌ | + +##### Examples + +Deal 20 biological damage to your torso +```json + { + "type": "effect_on_condition", + "id": "TEST", + "effect": [ { "u_deal_damage": "biological", "amount": 20, "bodypart": "torso" } ] + }, +``` + #### `u_mutate`, `npc_mutate` Your character or the NPC will attempt to mutate; used in mutation system, for other purposes it's better to use [`u_add_trait`](#`u_add_trait`, `npc_add_trait`) diff --git a/src/npctalk.cpp b/src/npctalk.cpp index bed1b949cf7e1..7ca501a26282e 100644 --- a/src/npctalk.cpp +++ b/src/npctalk.cpp @@ -6436,6 +6436,62 @@ talk_effect_fun_t::func f_give_equipment( const JsonObject &jo, std::string_view }; } + +talk_effect_fun_t::func f_deal_damage( const JsonObject &jo, std::string_view member, + const std::string_view, bool is_npc ) +{ + str_or_var dmg_type = get_str_or_var( jo.get_member( member ), member ); + dbl_or_var dmg_amount = get_dbl_or_var( jo, "amount", false, 0 ); + dbl_or_var arpen = get_dbl_or_var( jo, "arpen", false, 0 ); + dbl_or_var arpen_mult = get_dbl_or_var( jo, "arpen_mult", false, 1 ); + dbl_or_var dmg_mult = get_dbl_or_var( jo, "dmg_mult", false, 1 ); + + dbl_or_var dbl_min_hit = get_dbl_or_var( jo, "min_hit", false, -1 ); + dbl_or_var dbl_max_hit = get_dbl_or_var( jo, "max_hit", false, -1 ); + dbl_or_var dbl_hit_roll = get_dbl_or_var( jo, "hit_roll", false, 0 ); + bool can_attack_high = jo.get_bool( "can_attack_high", true ); + + + str_or_var bodypart; + + if( jo.has_member( "bodypart" ) ) { + bodypart = get_str_or_var( jo.get_member( "bodypart" ), "bodypart", false, "RANDOM" ); + } else { + bodypart.str_val = "RANDOM"; + } + + return [is_npc, dmg_type, dmg_amount, bodypart, arpen, arpen_mult, dmg_mult, dbl_min_hit, + dbl_max_hit, dbl_hit_roll, can_attack_high]( dialogue & d ) { + + damage_instance dmg_inst; + damage_type_id damage_type = damage_type_id( dmg_type.evaluate( d ) ); + std::string const bp_str = bodypart.evaluate( d ); + bodypart_id bp; + + if( d.actor( is_npc )->get_character() ) { + if( bp_str == "RANDOM" ) { + Character &guy = *d.actor( is_npc )->get_character(); + int min_hit = dbl_min_hit.evaluate( d ); + int max_hit = dbl_max_hit.evaluate( d ); + int hit_roll = dbl_hit_roll.evaluate( d ); + + if( max_hit == -1 ) { + max_hit = guy.get_max_hitsize_bodypart()->hit_size; + } + bp = guy.select_body_part( min_hit, max_hit, can_attack_high, hit_roll ); + } + } + + if( d.actor( is_npc )->get_monster() ) { + bp = bodypart_id( "bp_null" ); + } + + dmg_inst.add_damage( damage_type, dmg_amount.evaluate( d ), arpen.evaluate( d ), + arpen_mult.evaluate( d ), dmg_mult.evaluate( d ), 1, 1 ); + d.actor( is_npc )->deal_damage( d.actor( is_npc )->get_creature(), bp, dmg_inst ); + }; +} + talk_effect_fun_t::func f_spawn_monster( const JsonObject &jo, std::string_view member, const std::string_view src, bool is_npc ) { @@ -7078,6 +7134,7 @@ parsers = { { "u_consume_item_sum", "npc_consume_item_sum", jarg::array, &talk_effect_fun::f_consume_item_sum }, { "u_remove_item_with", "npc_remove_item_with", jarg::member, &talk_effect_fun::f_remove_item_with }, { "u_bulk_trade_accept", "npc_bulk_trade_accept", jarg::member, &talk_effect_fun::f_bulk_trade_accept }, + { "u_deal_damage", "npc_deal_damage", jarg::member, &talk_effect_fun::f_deal_damage }, { "u_bulk_donate", "npc_bulk_donate", jarg::member, &talk_effect_fun::f_bulk_trade_accept }, { "u_cast_spell", "npc_cast_spell", jarg::member, &talk_effect_fun::f_cast_spell }, { "u_map_run_item_eocs", "npc_map_run_item_eocs", jarg::member, &talk_effect_fun::f_map_run_item_eocs }, diff --git a/src/talker.h b/src/talker.h index 5fc22f79fe4fe..0f12b8b6fdcf0 100644 --- a/src/talker.h +++ b/src/talker.h @@ -590,6 +590,10 @@ class talker virtual int attack_speed() const { return 0; } + virtual dealt_damage_instance deal_damage( Creature *, bodypart_id, + const damage_instance & ) const { + return dealt_damage_instance(); + }; virtual double armor_at( damage_type_id &, bodypart_id & ) const { return 0; } diff --git a/src/talker_character.cpp b/src/talker_character.cpp index 7e7fdee1681a3..476243ae8e904 100644 --- a/src/talker_character.cpp +++ b/src/talker_character.cpp @@ -130,6 +130,12 @@ int talker_character_const::attack_speed() const return me_chr_const->attack_speed( cur_weap ); } +dealt_damage_instance talker_character_const::deal_damage( Creature *source, bodypart_id bp, + const damage_instance &dam ) const +{ + return source->deal_damage( source, bp, dam ); +} + void talker_character::set_str_max( int value ) { me_chr->str_max = value; diff --git a/src/talker_character.h b/src/talker_character.h index 4315f9863fae0..8e5b4fba66c51 100644 --- a/src/talker_character.h +++ b/src/talker_character.h @@ -70,6 +70,8 @@ class talker_character_const: public talker_cloner int int_cur() const override; int per_cur() const override; int attack_speed() const override; + dealt_damage_instance deal_damage( Creature *source, bodypart_id bp, + const damage_instance &dam ) const override; int pain_cur() const override; int perceived_pain_cur() const override; double armor_at( damage_type_id &dt, bodypart_id &bp ) const override; diff --git a/src/talker_monster.cpp b/src/talker_monster.cpp index 7a4c2e3745d77..2f22aab4de421 100644 --- a/src/talker_monster.cpp +++ b/src/talker_monster.cpp @@ -222,6 +222,12 @@ void talker_monster::set_all_parts_hp_cur( int set ) const me_mon->set_hp( set ); } +dealt_damage_instance talker_monster::deal_damage( Creature *source, bodypart_id bp, + const damage_instance &dam ) const +{ + return source->deal_damage( source, bp, dam ); +} + std::vector talker_monster_const::get_topics( bool ) { return me_mon_const->type->chat_topics; diff --git a/src/talker_monster.h b/src/talker_monster.h index e0594c4436926..baba58cd51e24 100644 --- a/src/talker_monster.h +++ b/src/talker_monster.h @@ -113,6 +113,8 @@ class talker_monster: public talker_cloner void die() override; void set_all_parts_hp_cur( int ) const override; + dealt_damage_instance deal_damage( Creature *source, bodypart_id bp, + const damage_instance &dam ) const override; protected: talker_monster() = default; monster *me_mon;