Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce enum bitset and use it for monster flags and triggers #29368

Merged
merged 14 commits into from
Apr 10, 2019
Merged
8 changes: 4 additions & 4 deletions src/defense.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,10 +191,10 @@ void defense_game::init_mtypes()
mtype *const t = const_cast<mtype *>( &type );
t->difficulty *= 1.5;
t->difficulty += static_cast<int>( t->difficulty / 5 );
t->flags.insert( MF_BASHES );
t->flags.insert( MF_SMELLS );
t->flags.insert( MF_HEARS );
t->flags.insert( MF_SEES );
t->set_flag( MF_BASHES );
t->set_flag( MF_SMELLS );
t->set_flag( MF_HEARS );
t->set_flag( MF_SEES );
}
}

Expand Down
100 changes: 100 additions & 0 deletions src/enum_bitset.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#pragma once
#ifndef ENUM_BITSET_H
#define ENUM_BITSET_H

#include <bitset>
#include <type_traits>


template<typename E>
struct enum_traits;


namespace detail
{

template<typename E>
using last_type = typename std::decay<decltype( enum_traits<E>::last )>::type;

template<typename E, typename U = E>
struct has_proper_traits : std::false_type {};

template<typename E>
struct has_proper_traits<E, last_type<E>> : std::true_type {};

} // namespace detail


template<typename E>
class enum_bitset
{
static_assert( std::is_enum<E>::value, "the template argument is not an enum." );
static_assert( detail::has_proper_traits<E>::value,
"a specialization of 'enum_traits<E>' template containing 'last' element of the enum must be defined somewhere. "
"The `last` constant must be of the same type as the enum iteslf."
);

public:
enum_bitset() = default;
enum_bitset( const enum_bitset & ) = default;
enum_bitset &operator=( const enum_bitset & ) = default;

bool operator==( const enum_bitset &rhs ) const noexcept {
return bits == rhs.bits;
}

bool operator!=( const enum_bitset &rhs ) const noexcept {
return !( *this == rhs );
}

enum_bitset &operator&=( const enum_bitset &rhs ) noexcept {
bits &= rhs.bits;
return *this;
}

enum_bitset &operator|=( const enum_bitset &rhs ) noexcept {
bits |= rhs.bits;
return *this;
}

bool operator[]( E e ) const {
return bits[ get_pos( e ) ];
}

enum_bitset &operator[]( E e ) {
return bits[ get_pos( e ) ];
}

enum_bitset &set( E e, bool val = true ) {
bits.set( get_pos( e ), val );
return *this;
}

enum_bitset &reset( E e ) {
bits.reset( get_pos( e ) );
return *this;
}

enum_bitset &reset() {
bits.reset();
return *this;
}

bool test( E e ) const {
return bits.test( get_pos( e ) );
}

static constexpr size_t size() noexcept {
return get_pos( enum_traits<E>::last );
}

private:
static constexpr size_t get_pos( E e ) noexcept {
return static_cast<size_t>( static_cast<typename std::underlying_type<E>::type>( e ) );
}

private:
std::bitset<enum_bitset<E>::size()> bits;
};

#endif // ENUM_BITSET_H
2 changes: 1 addition & 1 deletion src/field.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2556,7 +2556,7 @@ void map::monster_in_field( monster &z )

case fd_fungal_haze:
if( !z.type->in_species( FUNGUS ) &&
!z.type->has_flag( "NO_BREATHE" ) &&
!z.type->has_flag( MF_NO_BREATHE ) &&
!z.make_fungus() ) {
// Don't insta-kill jabberwocks, that's silly
const int density = cur.getFieldDensity();
Expand Down
70 changes: 52 additions & 18 deletions src/generic_factory.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "assign.h"
#include "catacharset.h"
#include "debug.h"
#include "enum_bitset.h"
#include "init.h"
#include "int_id.h"
#include "json.h"
Expand Down Expand Up @@ -636,6 +637,22 @@ struct handler<std::bitset<N>> {
static constexpr bool is_container = true;
};

template<typename E>
struct handler<enum_bitset<E>> {
void clear( enum_bitset<E> &container ) const {
container.reset();
}
template<typename T>
void insert( enum_bitset<E> &container, const T &data ) const {
container.set( data );
}
template<typename T>
void erase( enum_bitset<E> &container, const T &data ) const {
container.reset( data );
}
static constexpr bool is_container = true;
};

template<typename T>
struct handler<std::vector<T>> {
void clear( std::vector<T> &container ) const {
Expand Down Expand Up @@ -824,52 +841,69 @@ class auto_flags_reader : public generic_typed_reader<auto_flags_reader<FlagType
* One can use this if the member is `std::set<some_enum>` or `some_enum` and a
* map `std::map<std::string, some_enum>` with all the value enumeration values exists.
*
* The class can be instantiated for a given map `mapping` like this:
* `typed_flag_reader<decltype(mapping)> reader{ mapping, "error" };`
* The error string (@ref error_msg) is used when the input contains invalid flags
* The class can be conveniently instantiated for a given map `mapping` using
* the helper function @ref make_flag_reader (see below).
* The flag type (@ref flag_type) is used when the input contains invalid flags
* (a string that is not contained in the map). It should sound something like
* "invalid my-enum-type".
* "my-enum-type".
*/
template<typename C>
class typed_flag_reader : public generic_typed_reader<typed_flag_reader<C>>
template<typename T>
class typed_flag_reader : public generic_typed_reader<typed_flag_reader<T>>
{
protected:
const C &flag_map;
const std::string error_msg;
private:
using map_t = std::map<std::string, T>;

private:
const map_t &flag_map;
const std::string flag_type;

public:
typed_flag_reader( const C &m, const std::string &e )
: flag_map( m )
, error_msg( e ) {
typed_flag_reader( const map_t &flag_map, const std::string &flag_type )
: flag_map( flag_map )
, flag_type( flag_type ) {
}

typename C::mapped_type get_next( JsonIn &jin ) const {
const auto position = jin.tell();
T get_next( JsonIn &jin ) const {
const std::string flag = jin.get_string();
const auto iter = flag_map.find( flag );
if( iter == flag_map.end() ) {
jin.seek( position );
jin.error( error_msg + ": \"" + flag + "\"" );

if( iter == flag_map.cend() ) {
jin.seek( jin.tell() );
jin.error( string_format( "invalid %s: \"%s\"", flag_type, flag ) );
}

return iter->second;
}
};


template<typename T>
typed_flag_reader<T> make_flag_reader( const std::map<std::string, T> &m, const std::string &e )
{
return typed_flag_reader<T> { m, e };
}

/**
* Uses @ref io::string_to_enum to convert the string from JSON to a C++ enum.
*/
template<typename E>
class enum_flags_reader : public generic_typed_reader<enum_flags_reader<E>>
{
private:
const std::string flag_type;

public:
enum_flags_reader( const std::string &flag_type ) : flag_type( flag_type ) {
}

E get_next( JsonIn &jin ) const {
const auto position = jin.tell();
const std::string flag = jin.get_string();
try {
return io::string_to_enum<E>( flag );
} catch( const io::InvalidEnumString & ) {
jin.seek( position );
jin.error( "invalid enumeration value: \"" + flag + "\"" );
jin.error( string_format( "invalid %s: \"%s\"", flag_type, flag ) );
throw; // ^^ throws already
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/monattack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -823,7 +823,7 @@ bool mattack::resurrect( monster *z )
for( auto &i : g->m.i_at( p ) ) {
const mtype *mt = i.get_mtype();
if( !( i.is_corpse() && i.active && mt->has_flag( MF_REVIVES ) &&
mt->in_species( ZOMBIE ) && !mt->has_flag( "NO_NECRO" ) ) ) {
mt->in_species( ZOMBIE ) && !mt->has_flag( MF_NO_NECRO ) ) ) {
continue;
}

Expand Down
14 changes: 7 additions & 7 deletions src/monmove.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -207,13 +207,13 @@ void monster::plan( const mfactions &factions )
float dist = !smart_planning ? 1000 : 8.6f;
bool fleeing = false;
bool docile = friendly != 0 && has_effect( effect_docile );
bool angers_hostile_weak = type->anger.find( MTRIG_HOSTILE_WEAK ) != type->anger.end();
int angers_hostile_near =
( type->anger.find( MTRIG_HOSTILE_CLOSE ) != type->anger.end() ) ? 5 : 0;
int angers_mating_season = ( type->anger.find( MTRIG_MATING_SEASON ) != type->anger.end() ) ? 3 : 0;
int angers_cub_threatened = ( type->anger.find( MTRIG_PLAYER_NEAR_BABY ) != type->anger.end() ) ?
8 : 0;
int fears_hostile_near = ( type->fear.find( MTRIG_HOSTILE_CLOSE ) != type->fear.end() ) ? 5 : 0;

const bool angers_hostile_weak = type->has_anger_trigger( mon_trigger::HOSTILE_WEAK );
const int angers_hostile_near = type->has_anger_trigger( mon_trigger::HOSTILE_CLOSE ) ? 5 : 0;
const int angers_mating_season = type->has_anger_trigger( mon_trigger::MATING_SEASON ) ? 3 : 0;
const int angers_cub_threatened = type->has_anger_trigger( mon_trigger::PLAYER_NEAR_BABY ) ? 8 : 0;
const int fears_hostile_near = type->has_fear_trigger( mon_trigger::HOSTILE_CLOSE ) ? 5 : 0;

auto all_monsters = g->all_monsters();
bool group_morale = has_flag( MF_GROUP_MORALE ) && morale < type->morale;
bool swarms = has_flag( MF_SWARMS );
Expand Down
Loading