1 #include "field.h"
2 
3 #include <algorithm>
4 #include <cmath>
5 #include <random>
6 #include <utility>
7 
8 #include "calendar.h"
9 #include "make_static.h"
10 #include "rng.h"
11 
symbol() const12 std::string field_entry::symbol() const
13 {
14     return get_field_type()->get_symbol( get_field_intensity() - 1 );
15 }
16 
color() const17 nc_color field_entry::color() const
18 {
19     return get_intensity_level().color;
20 }
21 
get_intensity_level() const22 const field_intensity_level &field_entry::get_intensity_level() const
23 {
24     return get_field_type()->get_intensity_level( intensity - 1 );
25 }
26 
is_dangerous() const27 bool field_entry::is_dangerous() const
28 {
29     return get_intensity_level().dangerous;
30 }
31 
get_field_type() const32 field_type_id field_entry::get_field_type() const
33 {
34     return type;
35 }
36 
set_field_type(const field_type_id & new_type)37 field_type_id field_entry::set_field_type( const field_type_id &new_type )
38 {
39     type = new_type;
40     return type;
41 }
42 
get_max_field_intensity() const43 int field_entry::get_max_field_intensity() const
44 {
45     return type.obj().get_max_intensity();
46 }
47 
get_field_intensity() const48 int field_entry::get_field_intensity() const
49 {
50     return intensity;
51 }
52 
set_field_intensity(int new_intensity)53 int field_entry::set_field_intensity( int new_intensity )
54 {
55     is_alive = new_intensity > 0;
56     return intensity = std::max( std::min( new_intensity, get_max_field_intensity() ), 1 );
57 
58 }
59 
mod_field_intensity(int mod)60 void field_entry::mod_field_intensity( int mod )
61 {
62     set_field_intensity( get_field_intensity() + mod );
63 }
64 
get_field_age() const65 time_duration field_entry::get_field_age() const
66 {
67     return age;
68 }
69 
set_field_age(const time_duration & new_age)70 time_duration field_entry::set_field_age( const time_duration &new_age )
71 {
72     decay_time = time_point();
73     return age = new_age;
74 }
75 
do_decay()76 void field_entry::do_decay()
77 {
78     // Bypass set_field_age() so we don't reset decay_time;
79     age += 1_turns;
80     if( type.obj().half_life > 0_turns && get_field_age() > 0_turns ) {
81         // Legacy handling for fire because it's weird and complicated.
82         if( type == STATIC( field_type_str_id( "fd_fire" ) ) ) {
83             if( to_turns<int>( type->half_life ) < dice( 2, to_turns<int>( age ) ) ) {
84                 set_field_age( 0_turns );
85                 set_field_intensity( get_field_intensity() - 1 );
86             }
87             return;
88         }
89         if( decay_time == calendar::turn_zero ) {
90             std::exponential_distribution<> d( 1.0f / ( M_LOG2E * to_turns<float>
91                                                ( type.obj().half_life ) ) );
92             const time_duration decay_delay = time_duration::from_turns( d( rng_get_engine() ) );
93             decay_time = calendar::turn - age + decay_delay;
94         }
95         if( decay_time <= calendar::turn ) {
96             set_field_age( 0_turns );
97             set_field_intensity( get_field_intensity() - 1 );
98         }
99     }
100 }
101 
field()102 field::field()
103     : _displayed_field_type( fd_null.id_or( INVALID_FIELD_TYPE_ID ) )
104 {
105 }
106 
107 /*
108 Function: find_field
109 Returns a field entry corresponding to the field_type_id parameter passed in. If no fields are found then returns NULL.
110 Good for checking for existence of a field: if(myfield.find_field(fd_fire)) would tell you if the field is on fire.
111 */
find_field(const field_type_id & field_type_to_find,const bool alive_only)112 field_entry *field::find_field( const field_type_id &field_type_to_find, const bool alive_only )
113 {
114     if( !_displayed_field_type ) {
115         return nullptr;
116     }
117     const auto it = _field_type_list.find( field_type_to_find );
118     if( it != _field_type_list.end() && ( !alive_only || it->second.is_field_alive() ) ) {
119         return &it->second;
120     }
121     return nullptr;
122 }
123 
find_field(const field_type_id & field_type_to_find,const bool alive_only) const124 const field_entry *field::find_field( const field_type_id &field_type_to_find,
125                                       const bool alive_only ) const
126 {
127     if( !_displayed_field_type ) {
128         return nullptr;
129     }
130     const auto it = _field_type_list.find( field_type_to_find );
131     if( it != _field_type_list.end() && ( !alive_only || it->second.is_field_alive() ) ) {
132         return &it->second;
133     }
134     return nullptr;
135 }
136 
137 /*
138 Function: add_field
139 Inserts the given field_type_id into the field list for a given tile if it does not already exist.
140 Returns false if the field_type_id already exists, true otherwise.
141 If the field already exists, it will return false BUT it will add the intensity/age to the current values for upkeep.
142 If you wish to modify an already existing field use find_field and modify the result.
143 Intensity defaults to 1, and age to 0 (permanent) if not specified.
144 */
add_field(const field_type_id & field_type_to_add,const int new_intensity,const time_duration & new_age)145 bool field::add_field( const field_type_id &field_type_to_add, const int new_intensity,
146                        const time_duration &new_age )
147 {
148     // sanity check, we don't want to store fd_null
149     if( !field_type_to_add ) {
150         return false;
151     }
152     auto it = _field_type_list.find( field_type_to_add );
153     if( it != _field_type_list.end() ) {
154         //Already exists, but lets update it. This is tentative.
155         int prev_intensity = it->second.get_field_intensity();
156         if( !it->second.is_field_alive() ) {
157             it->second.set_field_age( new_age );
158             prev_intensity = 0;
159         }
160         it->second.set_field_intensity( prev_intensity + new_intensity );
161         return false;
162     }
163     if( !_displayed_field_type ||
164         field_type_to_add.obj().priority >= _displayed_field_type.obj().priority ) {
165         _displayed_field_type = field_type_to_add;
166     }
167     _field_type_list[field_type_to_add] = field_entry( field_type_to_add, new_intensity, new_age );
168     return true;
169 }
170 
remove_field(const field_type_id & field_to_remove)171 bool field::remove_field( const field_type_id &field_to_remove )
172 {
173     const auto it = _field_type_list.find( field_to_remove );
174     if( it == _field_type_list.end() ) {
175         return false;
176     }
177     remove_field( it );
178     return true;
179 }
180 
remove_field(std::map<field_type_id,field_entry>::iterator const it)181 void field::remove_field( std::map<field_type_id, field_entry>::iterator const it )
182 {
183     _field_type_list.erase( it );
184     _displayed_field_type = fd_null;
185     if( !_field_type_list.empty() ) {
186         for( auto &fld : _field_type_list ) {
187             if( !_displayed_field_type || fld.first.obj().priority >= _displayed_field_type.obj().priority ) {
188                 _displayed_field_type = fld.first;
189             }
190         }
191     }
192 }
193 
clear()194 void field::clear()
195 {
196     _field_type_list.clear();
197     _displayed_field_type = fd_null;
198 }
199 
200 /*
201 Function: field_count
202 Returns the number of fields existing on the current tile.
203 */
field_count() const204 unsigned int field::field_count() const
205 {
206     return _field_type_list.size();
207 }
208 
begin()209 std::map<field_type_id, field_entry>::iterator field::begin()
210 {
211     return _field_type_list.begin();
212 }
213 
begin() const214 std::map<field_type_id, field_entry>::const_iterator field::begin() const
215 {
216     return _field_type_list.begin();
217 }
218 
end()219 std::map<field_type_id, field_entry>::iterator field::end()
220 {
221     return _field_type_list.end();
222 }
223 
end() const224 std::map<field_type_id, field_entry>::const_iterator field::end() const
225 {
226     return _field_type_list.end();
227 }
228 
229 /*
230 Function: displayed_field_type
231 Returns the last added field type from the tile for drawing purposes.
232 */
displayed_field_type() const233 field_type_id field::displayed_field_type() const
234 {
235     return _displayed_field_type;
236 }
237 
displayed_description_affix() const238 description_affix field::displayed_description_affix() const
239 {
240     return _displayed_field_type.obj().desc_affix;
241 }
242 
total_move_cost() const243 int field::total_move_cost() const
244 {
245     int current_cost = 0;
246     for( const auto &fld : _field_type_list ) {
247         current_cost += fld.second.get_intensity_level().move_cost;
248     }
249     return current_cost;
250 }
251 
field_effects() const252 std::vector<field_effect> field_entry::field_effects() const
253 {
254     return type->get_intensity_level( intensity - 1 ).field_effects;
255 }
256