Skip to content

Commit

Permalink
Refactor spell_type to use a shape enum for spell aoe
Browse files Browse the repository at this point in the history
  • Loading branch information
KorGgenT committed Oct 5, 2020
1 parent 9edf433 commit fa3bac4
Show file tree
Hide file tree
Showing 9 changed files with 196 additions and 122 deletions.
18 changes: 13 additions & 5 deletions doc/MAGIC.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ In `data/mods/Magiclysm` there is a template spell, copied here for your perusal
"valid_targets": [ "hostile", "ground", "self", "ally" ], // if a valid target is not included, you cannot cast the spell on that target.
"effect": "shallow_pit", // effects are coded in C++. A list will be provided below of possible effects that have been coded.
"effect_str": "template", // special. see below
"shape": "blast", // the "shape" of the spell's area of effect. uses the aoe stat
"extra_effects": [ { "id": "fireball", "hit_self": false, "max_level": 3 } ], // this allows you to cast multiple spells with only one spell
"affected_body_parts": [ "head", "torso", "mouth", "eyes", "arm_l", "arm_r", "hand_r", "hand_l", "leg_l", "foot_l", "foot_r" ], // body parts affected by effects
"flags": [ "SILENT", "LOUD", "SOMATIC", "VERBAL", "NO_HANDS", "NO_LEGS", "SPAWN_GROUP" ], // see "Spell Flags" below
Expand Down Expand Up @@ -76,7 +77,7 @@ The value of the `"effect"` string in the spell's JSON data says what effect the
```json
{
"id": "magic_missile",
"effect": "target_attack",
"effect": "attack",
"min_damage": 1
}
```
Expand All @@ -97,10 +98,7 @@ Below is a table of currently implemented effects, along with special rules for
|--- |---
| `pain_split` | makes all of your limbs' damage even out
| `move_earth` | "digs" at the target location. some terrain is not diggable this way.
| `target_attack` | deals damage to a target (ignores walls). If "effect_str" is included, it will add that effect (defined elsewhere in json) to the targets if able, to the body parts defined in affected_body_parts. Any aoe will manifest as a circular area centered on the target, and will only deal damage to valid_targets. (aoe does not ignore walls)
| `projectile_attack` | similar to target_attack, except the projectile you shoot will stop short at impassable terrain. If "effect_str" is included, it will add that effect (defined elsewhere in json) to the targets if able, to the body parts defined in affected_body_parts.
| `cone_attack` | fires a cone toward the target up to your range. The arc of the cone in degrees is aoe. Stops at walls. If "effect_str" is included, it will add that effect (defined elsewhere in json) to the targets if able, to the body parts defined in affected_body_parts.
| `line_attack` | fires a line with width aoe toward the target, being blocked by walls on the way. If "effect_str" is included, it will add that effect (defined elsewhere in json) to the targets if able, to the body parts defined in affected_body_parts.
| `attack` | "causes damage to targets in its aoe, and applies an effect to the targets named by `effect_str`
| `spawn_item` | spawns an item that will disappear at the end of its duration. Default duration is 0.
| `summon` | summons a monster ID or group ID from `effect_str` that will disappear at the end of its duration. Default duration is 0.
| `teleport_random` | teleports the player randomly range spaces with aoe variation
Expand All @@ -117,6 +115,15 @@ Below is a table of currently implemented effects, along with special rules for
| `mutate` | mutates the target(s). if effect_str is defined, mutates toward that category instead of picking at random. the "MUTATE_TRAIT" flag allows effect_str to be a specific trait instead of a category. damage() / 100 is the percent chance the mutation will be successful (a value of 10000 represents 100.00%)
| `bash` | bashes the terrain at the target. uses damage() as the strength of the bash.

Another mandatory member is spell "shape". This dictates how the area of effect works.

| Shape | Description
| -- | --
| `blast` | a standard circular blast that goes outward from the impact position. aoe value is the radius.
| `line` | fires a line with a width equal to the aoe
| `cone` | fires a cone with an arc equal to aoe in degrees


### Spell Flags

Flags allow you to provide additional customizations for spell effects, behavior, and limitations.
Expand Down Expand Up @@ -154,6 +161,7 @@ Spells may have any number of flags, for example:
| `WITH_CONTAINER` | items spawned with container
| `UNSAFE_TELEPORT` | teleport spell risks killing the caster or others
| `SPAWN_GROUP` | spawn or summon from an item or monster group, instead of individual item/monster ID
| `NO_PROJECTILE` | the "projectile" portion of the spell phases through walls. the epicenter of the spell effect is exactly where you target it with no regards to obstacles


### Damage Types
Expand Down
41 changes: 35 additions & 6 deletions src/magic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,24 @@ std::string enum_to_string<spell_target>( spell_target data )
abort();
}
template<>
std::string enum_to_string<spell_shape>( spell_shape data )
{
switch( data ) {
case spell_shape::blast: return "blast";
case spell_shape::cone: return "cone";
case spell_shape::line: return "line";
case spell_shape::num_shapes: break;
}
debugmsg( "Invalid spell_shape" );
abort();
}
template<>
std::string enum_to_string<spell_flag>( spell_flag data )
{
switch( data ) {
case spell_flag::PERMANENT: return "PERMANENT";
case spell_flag::IGNORE_WALLS: return "IGNORE_WALLS";
case spell_flag::NO_PROJECTILE: return "NO_PROJECTILE";
case spell_flag::HOSTILE_SUMMON: return "HOSTILE_SUMMON";
case spell_flag::HOSTILE_50: return "HOSTILE_50";
case spell_flag::FRIENDLY_POLY: return "FRIENDLY_POLY";
Expand Down Expand Up @@ -216,11 +229,8 @@ void spell_type::load( const JsonObject &jo, const std::string & )
std::map<std::string, std::function<void( const spell &, Creature &, const tripoint & )>>
effect_map{
{ "pain_split", spell_effect::pain_split },
{ "target_attack", spell_effect::target_attack },
{ "attack", spell_effect::attack },
{ "targeted_polymorph", spell_effect::targeted_polymorph },
{ "projectile_attack", spell_effect::projectile_attack },
{ "cone_attack", spell_effect::cone_attack },
{ "line_attack", spell_effect::line_attack },
{ "teleport_random", spell_effect::teleport_random },
{ "spawn_item", spell_effect::spawn_ethereal_item },
{ "recover_energy", spell_effect::recover_energy },
Expand Down Expand Up @@ -265,6 +275,8 @@ void spell_type::load( const JsonObject &jo, const std::string & )
} else {
effect = found_effect->second;
}
mandatory( jo, was_loaded, "shape", spell_area );
spell_area_function = spell_effect::shape_map.at( spell_area );

const auto targeted_monster_ids_reader = auto_flags_reader<mtype_id> {};
optional( jo, was_loaded, "targeted_monster_ids", targeted_monster_ids,
Expand Down Expand Up @@ -347,6 +359,7 @@ void spell_type::serialize( JsonOut &json ) const
json.member( "name", name.translated() );
json.member( "description", description.translated() );
json.member( "effect", effect_name );
json.member( "shape", io::enum_to_string( spell_area ) );
json.member( "valid_targets", valid_targets, enum_bitset<spell_target> {} );
json.member( "effect_str", effect_str, effect_str_default );
json.member( "skill", skill, skill_default );
Expand Down Expand Up @@ -633,6 +646,17 @@ int spell::aoe() const
}
}

std::set<tripoint> spell::effect_area( const spell_effect::override_parameters &params,
const tripoint &source, const tripoint &target ) const
{
return type->spell_area_function( params, source, target );
}

std::set<tripoint> spell::effect_area( const tripoint &source, const tripoint &target ) const
{
return effect_area( spell_effect::override_parameters( *this ), source, target );
}

bool spell::in_aoe( const tripoint &source, const tripoint &target ) const
{
if( has_flag( spell_flag::RANDOM_AOE ) ) {
Expand Down Expand Up @@ -949,6 +973,11 @@ std::string spell::colorized_fail_percent( const Character &guy ) const
return colorize( fail_str, color );
}

spell_shape spell::shape() const
{
return type->spell_area;
}

int spell::xp() const
{
return experience;
Expand Down Expand Up @@ -1378,8 +1407,8 @@ cata::optional<tripoint> spell::random_valid_target( const Creature &caster,
{
const bool ignore_ground = has_flag( spell_flag::RANDOM_CRITTER );
std::set<tripoint> valid_area;
for( const tripoint &target : spell_effect::spell_effect_blast( *this, caster_pos, caster_pos,
range(), false ) ) {
for( const tripoint &target : spell_effect::spell_effect_blast(
spell_effect::override_parameters( *this ), caster_pos, caster_pos ) ) {
if( target != caster_pos && is_valid_target( caster, target ) &&
( !ignore_ground || g->critter_at<Creature>( target ) ) ) {
valid_area.emplace( target );
Expand Down
76 changes: 59 additions & 17 deletions src/magic.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ class spell;
class time_duration;
struct requirement_data;

namespace spell_effect
{
struct override_parameters;
} // namespace spell_effect

namespace cata
{
class event;
Expand All @@ -42,6 +47,7 @@ template <typename E> struct enum_traits;
enum class spell_flag : int {
PERMANENT, // items or creatures spawned with this spell do not disappear and die as normal
IGNORE_WALLS, // spell's aoe goes through walls
NO_PROJECTILE, // spell's original targeting area can be targeted through walls
SWAP_POS, // a projectile spell swaps the positions of the caster and target
HOSTILE_SUMMON, // summon spell always spawns a hostile monster
HOSTILE_50, // summoned monster spawns friendly 50% of the time
Expand Down Expand Up @@ -91,11 +97,26 @@ enum class spell_target : int {
num_spell_targets
};

enum class spell_shape : int {
// circular blast area
blast,
// hits anything in a line from the caster to the target with a width
line,
// aoe is radius of the arc
cone,
num_shapes
};

template<>
struct enum_traits<magic_energy_type> {
static constexpr magic_energy_type last = magic_energy_type::last;
};

template<>
struct enum_traits<spell_shape> {
static constexpr spell_shape last = spell_shape::num_shapes;
};

template<>
struct enum_traits<spell_target> {
static constexpr spell_target last = spell_target::num_spell_targets;
Expand Down Expand Up @@ -187,6 +208,10 @@ class spell_type
// spell effect string. used to look up spell function
std::string effect_name;
std::function<void( const spell &, Creature &, const tripoint & )> effect;
std::function<std::set<tripoint>( const spell_effect::override_parameters &params,
const tripoint &source, const tripoint &target )> spell_area_function;
// the spell shape found in the json
spell_shape spell_area;
// extra information about spell effect. allows for combinations for effects
std::string effect_str;
// list of additional "spell effects"
Expand Down Expand Up @@ -408,6 +433,7 @@ class spell
// what is the max level of the spell
int get_max_level() const;

spell_shape shape() const;
// what is the intensity of the field the spell generates ( 0 if no field )
int field_intensity() const;
// how much damage does the spell do
Expand All @@ -418,6 +444,9 @@ class spell
damage_instance get_damage_instance() const;
// how big is the spell's radius
int aoe() const;
std::set<tripoint> effect_area( const spell_effect::override_parameters &params,
const tripoint &source, const tripoint &target ) const;
std::set<tripoint> effect_area( const tripoint &source, const tripoint &target ) const;
// distance spell can be cast
int range() const;
// how much energy does the spell cost
Expand Down Expand Up @@ -589,31 +618,37 @@ class known_magic

namespace spell_effect
{
// a struct available for override parameters.
// is a separate struct for later extensibility
// used for spell shapes to be lighter weight than passing the whole spell
// and allows for overrides for these values
struct override_parameters {
int aoe_radius;
int range;
bool ignore_walls;

override_parameters( const spell &sp ) {
aoe_radius = sp.aoe();
range = sp.range();
ignore_walls = sp.has_flag( spell_flag::IGNORE_WALLS );
}
};

void teleport_random( const spell &sp, Creature &caster, const tripoint & );
void pain_split( const spell &, Creature &, const tripoint & );
void target_attack( const spell &sp, Creature &caster,
const tripoint &epicenter );
void attack( const spell &sp, Creature &caster,
const tripoint &epicenter );
void targeted_polymorph( const spell &sp, Creature &caster, const tripoint &target );

void projectile_attack( const spell &sp, Creature &caster,
const tripoint &target );
void cone_attack( const spell &sp, Creature &caster,
const tripoint &target );
void line_attack( const spell &sp, Creature &caster,
const tripoint &target );

void area_pull( const spell &sp, Creature &caster, const tripoint &center );
void area_push( const spell &sp, Creature &caster, const tripoint &center );

std::set<tripoint> spell_effect_blast( const spell &, const tripoint &, const tripoint &target,
int aoe_radius, bool ignore_walls );
std::set<tripoint> spell_effect_cone( const spell &sp, const tripoint &source,
const tripoint &target,
int aoe_radius, bool ignore_walls );
std::set<tripoint> spell_effect_line( const spell &, const tripoint &source,
const tripoint &target,
int aoe_radius, bool ignore_walls );
std::set<tripoint> spell_effect_blast( const override_parameters &params, const tripoint &,
const tripoint &target );
std::set<tripoint> spell_effect_cone( const override_parameters &params, const tripoint &source,
const tripoint &target );
std::set<tripoint> spell_effect_line( const override_parameters &params, const tripoint &source,
const tripoint &target );

void spawn_ethereal_item( const spell &sp, Creature &, const tripoint & );
void recover_energy( const spell &sp, Creature &, const tripoint &target );
Expand All @@ -636,6 +671,13 @@ void bash( const spell &sp, Creature &caster, const tripoint &target );
void dash( const spell &sp, Creature &caster, const tripoint &target );
void banishment( const spell &sp, Creature &caster, const tripoint &target );
void none( const spell &sp, Creature &, const tripoint &target );

static const std::map<spell_shape, std::function<std::set<tripoint>
( const override_parameters &, const tripoint &, const tripoint & )>> shape_map = {
{ spell_shape::blast, spell_effect_blast },
{ spell_shape::line, spell_effect_line },
{ spell_shape::cone, spell_effect_cone }
};
} // namespace spell_effect

class spellbook_callback : public uilist_callback
Expand Down
Loading

0 comments on commit fa3bac4

Please sign in to comment.