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