1 #ifndef _Effect_h_
2 #define _Effect_h_
3 
4 
5 #include "EnumsFwd.h"
6 
7 #include <boost/container/flat_map.hpp>
8 #include <boost/serialization/access.hpp>
9 
10 #include <memory>
11 #include <map>
12 #include <string>
13 #include <vector>
14 #include <unordered_map>
15 
16 #include "../util/Export.h"
17 
18 FO_COMMON_API extern const int INVALID_OBJECT_ID;
19 
20 class UniverseObject;
21 struct ScriptingContext;
22 
23 namespace Condition {
24     struct Condition;
25 }
26 
27 namespace Effect {
28     struct AccountingInfo;
29     class EffectsGroup;
30 
31     typedef std::vector<std::shared_ptr<UniverseObject>> TargetSet;
32     /** Effect accounting information for all meters of all objects that are
33       * acted on by effects. */
34     typedef std::unordered_map<int, boost::container::flat_map<MeterType, std::vector<AccountingInfo>>> AccountingMap;
35 
36     /** Description of cause of an effect: the general cause type, and the
37       * specific cause.  eg. Building and a particular BuildingType. */
38     struct FO_COMMON_API EffectCause {
39         explicit EffectCause();
40         EffectCause(EffectsCauseType cause_type_, const std::string& specific_cause_,
41                     const std::string& custom_label_ = "");
42         EffectsCauseType    cause_type;     ///< general type of effect cause, eg. tech, building, special...
43         std::string         specific_cause; ///< name of specific cause, eg. "Wonder Farm", "Antenna Mk. VI"
44         std::string         custom_label;   ///< script-specified accounting label for this effect cause
45     };
46 
47     /** Combination of targets and cause for an effects group. */
48     struct TargetsAndCause {
49         explicit TargetsAndCause();
50         TargetsAndCause(const TargetSet& target_set_, const EffectCause& effect_cause_);
51         TargetSet target_set;
52         EffectCause effect_cause;
53     };
54 
55     /** Combination of an EffectsGroup and the id of a source object. */
56     struct SourcedEffectsGroup {
57         explicit SourcedEffectsGroup();
58         SourcedEffectsGroup(int source_object_id_, const EffectsGroup* effects_group_);
59         bool operator<(const SourcedEffectsGroup& right) const;
60         int source_object_id = INVALID_OBJECT_ID;
61         const EffectsGroup* effects_group = nullptr;
62     };
63 
64     /** Map from (effects group and source object) to target set of for
65       * that effects group with that source object.  A multimap is used
66       * so that a single source object can have multiple instances of the
67       * same effectsgroup.  This is useful when a Ship has multiple copies
68       * of the same effects group due to having multiple copies of the same
69       * ship part in its design. */
70     typedef std::vector<std::pair<SourcedEffectsGroup, TargetsAndCause>> SourcesEffectsTargetsAndCausesVec;
71 
72     /** The base class for all Effects.  When an Effect is executed, the source
73     * object (the object to which the Effect or its containing EffectGroup is
74     * attached) and the target object are both required.  Note that this means
75     * that ValueRefs contained within Effects can refer to values in either the
76     * source or target objects. */
77     class FO_COMMON_API Effect {
78     public:
79         virtual ~Effect();
80 
81         virtual void Execute(ScriptingContext& context) const = 0;
82 
83         virtual void Execute(ScriptingContext& context, const TargetSet& targets) const;
84 
85         virtual void Execute(ScriptingContext& context,
86                              const TargetSet& targets,
87                              AccountingMap* accounting_map,
88                              const EffectCause& effect_cause,
89                              bool only_meter_effects = false,
90                              bool only_appearance_effects = false,
91                              bool include_empire_meter_effects = false,
92                              bool only_generate_sitrep_effects = false) const;
93 
94         virtual std::string     Dump(unsigned short ntabs = 0) const = 0;
95 
IsMeterEffect()96         virtual bool            IsMeterEffect() const { return false; }
IsEmpireMeterEffect()97         virtual bool            IsEmpireMeterEffect() const { return false; }
IsAppearanceEffect()98         virtual bool            IsAppearanceEffect() const { return false; }
IsSitrepEffect()99         virtual bool            IsSitrepEffect() const { return false; }
IsConditionalEffect()100         virtual bool            IsConditionalEffect() const { return false; }
101 
102         // TODO: source-invariant?
103 
104         virtual void            SetTopLevelContent(const std::string& content_name) = 0;
105         virtual unsigned int    GetCheckSum() const;
106 
107     private:
108         friend class boost::serialization::access;
109         template <typename Archive>
110         void serialize(Archive& ar, const unsigned int version);
111     };
112 
113     /** Accounting information about what the causes are and changes produced
114       * by effects groups acting on meters of objects. */
115     struct FO_COMMON_API AccountingInfo : public EffectCause {
116         explicit AccountingInfo();
117         AccountingInfo(int source_id_, EffectsCauseType cause_type_, float meter_change_,
118                        float running_meter_total_, const std::string& specific_cause_ = "",
119                        const std::string& custom_label_ = "");
120 
121         bool operator==(const AccountingInfo& rhs) const;
122 
123         int     source_id = INVALID_OBJECT_ID;  ///< source object of effect
124         float   meter_change = 0.0f;            ///< net change on meter due to this effect, as best known by client's empire
125         float   running_meter_total = 0.0f;     ///< meter total as of this effect.
126     };
127 
128     /** Contains one or more Effects, a Condition which indicates the objects in
129     * the scope of the Effect(s), and a Condition which indicates whether or not
130     * the Effect(s) will be executed on the objects in scope during the current
131     * turn.  Since Conditions operate on sets of objects (usually all objects in
132     * the universe), the activation condition bears some explanation.  It exists
133     * to allow an EffectsGroup to be activated or suppressed based on the source
134     * object only (the object to which the EffectsGroup is attached).  It does
135     * this by considering the "universe" containing only the source object. If
136     * the source object meets the activation condition, the EffectsGroup will be
137     * active in the current turn. */
138     class FO_COMMON_API EffectsGroup {
139     public:
140         EffectsGroup(std::unique_ptr<Condition::Condition>&& scope,
141                      std::unique_ptr<Condition::Condition>&& activation,
142                      std::vector<std::unique_ptr<Effect>>&& effects,
143                      const std::string& accounting_label = "",
144                      const std::string& stacking_group = "",
145                      int priority = 0,
146                      const std::string& description = "",
147                      const std::string& content_name = "");
148         virtual ~EffectsGroup();
149 
150         /** execute all effects in group */
151         void Execute(ScriptingContext& source_context,
152                      const TargetsAndCause& targets_cause,
153                      AccountingMap* accounting_map = nullptr,
154                      bool only_meter_effects = false,
155                      bool only_appearance_effects = false,
156                      bool include_empire_meter_effects = false,
157                      bool only_generate_sitrep_effects = false) const;
158 
StackingGroup()159         const std::string&              StackingGroup() const       { return m_stacking_group; }
Scope()160         Condition::Condition*           Scope() const               { return m_scope.get(); }
Activation()161         Condition::Condition*           Activation() const          { return m_activation.get(); }
162         const std::vector<Effect*>      EffectsList() const;
163         const std::string&              GetDescription() const;
AccountingLabel()164         const std::string&              AccountingLabel() const     { return m_accounting_label; }
Priority()165         int                             Priority() const            { return m_priority; }
166         std::string                     Dump(unsigned short ntabs = 0) const;
167         bool                            HasMeterEffects() const;
168         bool                            HasAppearanceEffects() const;
169         bool                            HasSitrepEffects() const;
170 
171         void                            SetTopLevelContent(const std::string& content_name);
TopLevelContent()172         const std::string&              TopLevelContent() const { return m_content_name; }
173 
174         virtual unsigned int            GetCheckSum() const;
175 
176     protected:
177         std::unique_ptr<Condition::Condition>   m_scope;
178         std::unique_ptr<Condition::Condition>   m_activation;
179         std::string                             m_stacking_group;
180         std::vector<std::unique_ptr<Effect>>    m_effects;
181         std::string                             m_accounting_label;
182         int                                     m_priority; // constructor sets this, so don't need a default value here
183         std::string                             m_description;
184         std::string                             m_content_name;
185 
186     private:
187         friend class boost::serialization::access;
188         template <typename Archive>
189         void serialize(Archive& ar, const unsigned int version);
190     };
191 
192     /** Returns a single string which `Dump`s a vector of EffectsGroups. */
193     FO_COMMON_API std::string Dump(const std::vector<std::shared_ptr<EffectsGroup>>& effects_groups);
194 }
195 
196 #endif
197