From d0d1c964842acaf93aaae39c6adae583c0864d92 Mon Sep 17 00:00:00 2001 From: RobbieNeko <30732426+RobbieNeko@users.noreply.github.com> Date: Sun, 13 Oct 2024 23:52:33 -0400 Subject: [PATCH] feat(content, port): Port Directed push from DDA (#5566) * Port Directed push from DDA When the push is directed Co-Authored-By: Curtis Merrill <30208401+KorGgenT@users.noreply.github.com> * style(autofix.ci): automated formatting * Documentation, fix debug dash real quick Oops! --------- Co-authored-by: Curtis Merrill <30208401+KorGgenT@users.noreply.github.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- data/mods/Magiclysm/Spells/debug.json | 36 ++++ .../en/mod/json/reference/creatures/magic.md | 4 + src/magic.cpp | 1 + src/magic.h | 1 + src/magic_spell_effect.cpp | 158 ++++++++++++++++-- 5 files changed, 184 insertions(+), 16 deletions(-) diff --git a/data/mods/Magiclysm/Spells/debug.json b/data/mods/Magiclysm/Spells/debug.json index 80b4a1815093..a65310004564 100644 --- a/data/mods/Magiclysm/Spells/debug.json +++ b/data/mods/Magiclysm/Spells/debug.json @@ -247,5 +247,41 @@ "base_energy_cost": 350, "max_level": 1, "energy_source": "MANA" + }, + { + "type": "SPELL", + "id": "debug_push_basic", + "name": "debug push no aoe", + "description": "pushes all types of objects with an aoe of 0", + "valid_targets": [ "ally", "self", "hostile", "ground", "item" ], + "effect": "directed_push", + "min_damage": 0, + "max_damage": 10, + "damage_increment": 1, + "min_range": 20, + "max_range": 20, + "max_level": 10, + "base_casting_time": 100, + "energy_source": "MANA", + "base_energy_cost": 100 + }, + { + "type": "SPELL", + "id": "debug_push_blast", + "name": "debug push blast", + "description": "pushes all types of objects with an aoe of 3 in a blast pattern", + "valid_targets": [ "ally", "self", "hostile", "ground", "item" ], + "effect": "directed_push", + "min_aoe": 3, + "max_aoe": 3, + "min_damage": 0, + "max_damage": 10, + "damage_increment": 1, + "min_range": 20, + "max_range": 20, + "max_level": 10, + "base_casting_time": 100, + "energy_source": "MANA", + "base_energy_cost": 100 } ] diff --git a/doc/src/content/docs/en/mod/json/reference/creatures/magic.md b/doc/src/content/docs/en/mod/json/reference/creatures/magic.md index 0c1a4b62c036..051aea5406c8 100644 --- a/doc/src/content/docs/en/mod/json/reference/creatures/magic.md +++ b/doc/src/content/docs/en/mod/json/reference/creatures/magic.md @@ -210,6 +210,10 @@ experience you need to get to a level is below: - `dash` - moves the player to the target tile, can leave behind fields. +- `area_push` - pushes things outwards from a single point + +- `directed_push` pushes things in a single direction away from you. + - `WONDER` - Unlike the above, this is not an "effect" but a "flag". This alters the behavior of the parent spell drastically: The spell itself doesn't cast, but its damage and range information is used in order to cast the extra_effects. N of the extra_effects will be chosen at random to be diff --git a/src/magic.cpp b/src/magic.cpp index 8de0b0da22b6..c922892e42db 100644 --- a/src/magic.cpp +++ b/src/magic.cpp @@ -209,6 +209,7 @@ void spell_type::load( const JsonObject &jo, const std::string & ) { "translocate", spell_effect::translocate }, { "area_pull", spell_effect::area_pull }, { "area_push", spell_effect::area_push }, + { "directed_push", spell_effect::directed_push }, { "timed_event", spell_effect::timed_event }, { "ter_transform", spell_effect::transform_blast }, { "noise", spell_effect::noise }, diff --git a/src/magic.h b/src/magic.h index f22bea8e7d39..7d122c3750af 100644 --- a/src/magic.h +++ b/src/magic.h @@ -534,6 +534,7 @@ void line_attack( const spell &sp, Creature &caster, void area_pull( const spell &sp, Creature &caster, const tripoint ¢er ); void area_push( const spell &sp, Creature &caster, const tripoint ¢er ); +void directed_push( const spell &sp, Creature &caster, const tripoint &target ); std::set spell_effect_blast( const spell &, const tripoint &, const tripoint &target, int aoe_radius, bool ignore_walls ); diff --git a/src/magic_spell_effect.cpp b/src/magic_spell_effect.cpp index 922a1dafd9f6..260d437766d2 100644 --- a/src/magic_spell_effect.cpp +++ b/src/magic_spell_effect.cpp @@ -658,6 +658,33 @@ void area_expander::sort_descending() } ); } +static void move_items( map &here, const tripoint &from, const tripoint &to ) +{ + auto src_items = here.i_at( from ); + auto dst_items = here.i_at( to ); + + for( detached_ptr &it : src_items.clear() ) { + dst_items.insert( std::move( it ) ); + } + src_items.clear(); +} + +static void move_field( map &here, const tripoint &from, const tripoint &to ) +{ + field &src_field = here.field_at( from ); + std::map moving_fields; + for( const std::pair &fd : src_field ) { + if( fd.first.is_valid() && !fd.first.id().is_null() ) { + const int intensity = fd.second.get_field_intensity(); + moving_fields.emplace( fd.first, intensity ); + } + } + for( const std::pair &fd : moving_fields ) { + here.remove_field( from, fd.first ); + here.set_field_intensity( to, fd.first, fd.second ); + } +} + // Moving all objects from one point to another by the power of magic. static void spell_move( const spell &sp, const Creature &caster, const tripoint &from, const tripoint &to ) @@ -695,22 +722,8 @@ static void spell_move( const spell &sp, const Creature &caster, src_items.clear(); } - // Helper function to move particular field type if corresponding target flag is enabled. - auto move_field = [&sp, &here, from, to]( valid_target target, field_type_id fid ) { - if( !sp.is_valid_effect_target( target ) ) { - return; - } - auto &src_field = here.field_at( from ); - if( field_entry *entry = src_field.find_field( fid ) ) { - int intensity = entry->get_field_intensity(); - here.remove_field( from, fid ); - here.set_field_intensity( to, fid, intensity ); - } - }; - // Moving fields. - move_field( target_fd_fire, fd_fire ); - move_field( target_fd_blood, fd_blood ); - move_field( target_fd_blood, fd_gibs_flesh ); + // Helper function to move fields + move_field( get_map(), from, to ); } void spell_effect::area_pull( const spell &sp, Creature &caster, const tripoint ¢er ) @@ -749,6 +762,119 @@ void spell_effect::area_push( const spell &sp, Creature &caster, const tripoint sp.make_sound( caster.pos() ); } +static void character_push_effects( Creature *caster, Character &guy, tripoint &push_dest, + const int push_distance, const std::vector &push_vec ) +{ + int dist_left = std::abs( push_distance ); + for( const tripoint &pushed_point : push_vec ) { + if( get_map().impassable( pushed_point ) ) { + guy.hurtall( dist_left * 4, caster ); + push_dest = pushed_point; + break; + } else { + dist_left--; + } + } + guy.setpos( push_dest ); +} + +void spell_effect::directed_push( const spell &sp, Creature &caster, const tripoint &target ) +{ + std::set area = spell_effect_area( sp, target, spell_effect_blast, caster ); + // this group of variables is for deferring movement of the avatar + int pushed_distance; + tripoint push_to; + std::vector pushed_vec; + bool player_pushed = false; + + ::map &here = get_map(); + + // whether it's push or pull, so how the multimap is sorted + // -1 is push and 1 is pull + const int sign = sp.damage() > 0 ? -1 : 1; + + std::multimap targets_ordered_by_range; + for( const tripoint &pt : area ) { + targets_ordered_by_range.emplace( sign * rl_dist( pt, caster.pos() ), pt ); + } + + for( const std::pair &pair : targets_ordered_by_range ) { + const tripoint &push_point = pair.second; + const units::angle start_angle = coord_to_angle( caster.pos(), target ); + // positive is push, negative is pull + int push_distance = sp.damage(); + const int prev_distance = rl_dist( caster.pos(), target ); + if( push_distance < 0 ) { + push_distance = std::max( -std::abs( push_distance ), -std::abs( prev_distance ) ); + } + if( push_distance == 0 ) { + continue; + } + + tripoint push_dest; + calc_ray_end( start_angle, push_distance, push_point, push_dest ); + const std::vector push_vec = line_to( push_point, push_dest ); + + const Creature *critter = g->critter_at( push_point ); + if( critter != nullptr ) { + const Attitude attitude_to_target = + caster.attitude_to( *g->critter_at( push_point ) ); + + monster *mon = g->critter_at( push_point ); + Character *guy = g->critter_at( push_point ); + + if( ( sp.is_valid_target( target_self ) && push_point == caster.pos() ) || + ( attitude_to_target == Attitude::A_FRIENDLY && + sp.is_valid_target( target_ally ) ) || + ( ( attitude_to_target == Attitude::A_HOSTILE || + attitude_to_target == Attitude::A_NEUTRAL ) && + sp.is_valid_target( target_hostile ) ) ) { + if( g->critter_at( push_point ) ) { + // defer this because this absolutely must be done last in order not to mess up our calculations + player_pushed = true; + pushed_distance = push_distance; + push_to = push_dest; + pushed_vec = push_vec; + } else if( mon ) { + int dist_left = std::abs( push_distance ); + for( const tripoint &pushed_push_point : push_vec ) { + if( get_map().impassable( pushed_push_point ) ) { + mon->apply_damage( &caster, bodypart_id(), dist_left * 10 ); + push_dest = pushed_push_point; + break; + } else { + dist_left--; + } + } + mon->setpos( push_dest ); + } else if( guy ) { + character_push_effects( &caster, *guy, push_dest, push_distance, push_vec ); + } + } + } + + if( sp.is_valid_target( target_item ) && here.has_items( push_point ) ) { + move_items( here, push_point, push_dest ); + } + + + if( sp.is_valid_target( target_fd_blood ) ) { + move_field( here, push_point, push_dest ); + } + + if( sp.is_valid_target( target_fd_fire ) ) { + move_field( here, push_point, push_dest ); + } + } + + // deferred avatar pushing + if( player_pushed ) { + character_push_effects( &caster, get_avatar(), push_to, pushed_distance, pushed_vec ); + } +} + + + void spell_effect::spawn_ethereal_item( const spell &sp, Creature &caster, const tripoint & ) { detached_ptr granted = item::spawn( sp.effect_data(), calendar::turn );