1 #include "material.h"
2 
3 #include <algorithm>
4 #include <iterator>
5 #include <map>
6 #include <set>
7 
8 #include "assign.h"
9 #include "calendar.h"
10 #include "debug.h"
11 #include "generic_factory.h"
12 #include "item.h"
13 #include "json.h"
14 
15 namespace
16 {
17 
18 generic_factory<material_type> material_data( "material" );
19 
20 } // namespace
21 
22 /** @relates string_id */
23 template<>
is_valid() const24 bool string_id<material_type>::is_valid() const
25 {
26     return material_data.is_valid( *this );
27 }
28 
29 /** @relates string_id */
30 template<>
obj() const31 const material_type &string_id<material_type>::obj() const
32 {
33     return material_data.obj( *this );
34 }
35 
material_type()36 material_type::material_type() :
37     id( material_id::NULL_ID() ),
38     _bash_dmg_verb( to_translation( "damages" ) ),
39     _cut_dmg_verb( to_translation( "damages" ) )
40 {
41     _dmg_adj = { to_translation( "lightly damaged" ), to_translation( "damaged" ), to_translation( "very damaged" ), to_translation( "thoroughly damaged" ) };
42 }
43 
load_mat_burn_data(const JsonObject & jsobj)44 static mat_burn_data load_mat_burn_data( const JsonObject &jsobj )
45 {
46     mat_burn_data bd;
47     assign( jsobj, "immune", bd.immune );
48     assign( jsobj, "volume_per_turn", bd.volume_per_turn );
49     jsobj.read( "fuel", bd.fuel );
50     jsobj.read( "smoke", bd.smoke );
51     jsobj.read( "burn", bd.burn );
52     return bd;
53 }
54 
load(const JsonObject & jsobj,const std::string &)55 void material_type::load( const JsonObject &jsobj, const std::string & )
56 {
57     mandatory( jsobj, was_loaded, "name", _name );
58 
59     mandatory( jsobj, was_loaded, "bash_resist", _bash_resist );
60     mandatory( jsobj, was_loaded, "cut_resist", _cut_resist );
61     mandatory( jsobj, was_loaded, "acid_resist", _acid_resist );
62     mandatory( jsobj, was_loaded, "elec_resist", _elec_resist );
63     mandatory( jsobj, was_loaded, "fire_resist", _fire_resist );
64     mandatory( jsobj, was_loaded, "bullet_resist", _bullet_resist );
65     mandatory( jsobj, was_loaded, "chip_resist", _chip_resist );
66     mandatory( jsobj, was_loaded, "density", _density );
67 
68     optional( jsobj, was_loaded, "specific_heat_liquid", _specific_heat_liquid );
69     optional( jsobj, was_loaded, "specific_heat_solid", _specific_heat_solid );
70     optional( jsobj, was_loaded, "latent_heat", _latent_heat );
71     optional( jsobj, was_loaded, "freeze_point", _freeze_point );
72 
73     assign( jsobj, "salvaged_into", _salvaged_into );
74     optional( jsobj, was_loaded, "repaired_with", _repaired_with, itype_id::NULL_ID() );
75     optional( jsobj, was_loaded, "edible", _edible, false );
76     optional( jsobj, was_loaded, "rotting", _rotting, false );
77     optional( jsobj, was_loaded, "soft", _soft, false );
78     optional( jsobj, was_loaded, "reinforces", _reinforces, false );
79 
80     for( JsonArray pair : jsobj.get_array( "vitamins" ) ) {
81         _vitamins.emplace( vitamin_id( pair.get_string( 0 ) ), pair.get_float( 1 ) );
82     }
83 
84     mandatory( jsobj, was_loaded, "bash_dmg_verb", _bash_dmg_verb );
85     mandatory( jsobj, was_loaded, "cut_dmg_verb", _cut_dmg_verb );
86 
87     mandatory( jsobj, was_loaded, "dmg_adj", _dmg_adj );
88 
89     if( jsobj.has_array( "burn_data" ) ) {
90         for( JsonObject brn : jsobj.get_array( "burn_data" ) ) {
91             _burn_data.emplace_back( load_mat_burn_data( brn ) );
92         }
93     }
94     if( _burn_data.empty() ) {
95         // If not specified, supply default
96         mat_burn_data mbd;
97         if( _fire_resist <= 0 ) {
98             mbd.burn = 1;
99         }
100         _burn_data.emplace_back( mbd );
101     }
102 
103     optional( jsobj, was_loaded, "fuel_data", fuel );
104 
105     jsobj.read( "burn_products", _burn_products, true );
106 
107     optional( jsobj, was_loaded, "compact_accepts", _compact_accepts,
108               auto_flags_reader<material_id>() );
109     optional( jsobj, was_loaded, "compacts_into", _compacts_into, auto_flags_reader<itype_id>() );
110 }
111 
check() const112 void material_type::check() const
113 {
114     if( name().empty() ) {
115         debugmsg( "material %s has no name.", id.c_str() );
116     }
117     if( _dmg_adj.size() < 4 ) {
118         debugmsg( "material %s specifies insufficient damaged adjectives.", id.c_str() );
119     }
120     if( _salvaged_into && ( !item::type_is_defined( *_salvaged_into ) || _salvaged_into->is_null() ) ) {
121         debugmsg( "invalid \"salvaged_into\" %s for %s.", _salvaged_into->c_str(), id.c_str() );
122     }
123     if( !item::type_is_defined( _repaired_with ) ) {
124         debugmsg( "invalid \"repaired_with\" %s for %s.", _repaired_with.c_str(), id.c_str() );
125     }
126     for( const material_id &ca : _compact_accepts ) {
127         if( !ca.is_valid() ) {
128             debugmsg( "invalid \"compact_accepts\" %s for %s.", ca.c_str(), id.c_str() );
129         }
130     }
131     for( const itype_id &ci : _compacts_into ) {
132         if( !item::type_is_defined( ci ) ||
133             !item( ci, calendar::turn_zero ).only_made_of( std::set<material_id> { id } ) ) {
134             debugmsg( "invalid \"compacts_into\" %s for %s.", ci.c_str(), id.c_str() );
135         }
136     }
137 }
138 
ident() const139 material_id material_type::ident() const
140 {
141     return id;
142 }
143 
name() const144 std::string material_type::name() const
145 {
146     return _name.translated();
147 }
148 
salvaged_into() const149 cata::optional<itype_id> material_type::salvaged_into() const
150 {
151     return _salvaged_into;
152 }
153 
repaired_with() const154 itype_id material_type::repaired_with() const
155 {
156     return _repaired_with;
157 }
158 
bash_resist() const159 int material_type::bash_resist() const
160 {
161     return _bash_resist;
162 }
163 
cut_resist() const164 int material_type::cut_resist() const
165 {
166     return _cut_resist;
167 }
168 
bullet_resist() const169 int material_type::bullet_resist() const
170 {
171     return _bullet_resist;
172 }
173 
bash_dmg_verb() const174 std::string material_type::bash_dmg_verb() const
175 {
176     return _bash_dmg_verb.translated();
177 }
178 
cut_dmg_verb() const179 std::string material_type::cut_dmg_verb() const
180 {
181     return _cut_dmg_verb.translated();
182 }
183 
dmg_adj(int damage) const184 std::string material_type::dmg_adj( int damage ) const
185 {
186     if( damage <= 0 ) {
187         // not damaged (+/- reinforced)
188         return std::string();
189     }
190 
191     // apply bounds checking
192     return _dmg_adj[std::min( static_cast<size_t>( damage ), _dmg_adj.size() ) - 1].translated();
193 }
194 
acid_resist() const195 int material_type::acid_resist() const
196 {
197     return _acid_resist;
198 }
199 
elec_resist() const200 int material_type::elec_resist() const
201 {
202     return _elec_resist;
203 }
204 
fire_resist() const205 int material_type::fire_resist() const
206 {
207     return _fire_resist;
208 }
209 
chip_resist() const210 int material_type::chip_resist() const
211 {
212     return _chip_resist;
213 }
214 
specific_heat_liquid() const215 float material_type::specific_heat_liquid() const
216 {
217     return _specific_heat_liquid;
218 }
219 
specific_heat_solid() const220 float material_type::specific_heat_solid() const
221 {
222     return _specific_heat_solid;
223 }
224 
latent_heat() const225 float material_type::latent_heat() const
226 {
227     return _latent_heat;
228 }
229 
freeze_point() const230 int material_type::freeze_point() const
231 {
232     return _freeze_point;
233 }
234 
density() const235 int material_type::density() const
236 {
237     return _density;
238 }
239 
edible() const240 bool material_type::edible() const
241 {
242     return _edible;
243 }
244 
rotting() const245 bool material_type::rotting() const
246 {
247     return _rotting;
248 }
249 
soft() const250 bool material_type::soft() const
251 {
252     return _soft;
253 }
254 
reinforces() const255 bool material_type::reinforces() const
256 {
257     return _reinforces;
258 }
259 
get_fuel_data() const260 fuel_data material_type::get_fuel_data() const
261 {
262     return fuel;
263 }
264 
burn_data(size_t intensity) const265 const mat_burn_data &material_type::burn_data( size_t intensity ) const
266 {
267     return _burn_data[ std::min<size_t>( intensity, _burn_data.size() ) - 1 ];
268 }
269 
burn_products() const270 const mat_burn_products &material_type::burn_products() const
271 {
272     return _burn_products;
273 }
274 
compact_accepts() const275 const material_id_list &material_type::compact_accepts() const
276 {
277     return _compact_accepts;
278 }
279 
compacts_into() const280 const mat_compacts_into &material_type::compacts_into() const
281 {
282     return _compacts_into;
283 }
284 
load(const JsonObject & jo,const std::string & src)285 void materials::load( const JsonObject &jo, const std::string &src )
286 {
287     material_data.load( jo, src );
288 }
289 
check()290 void materials::check()
291 {
292     material_data.check();
293 }
294 
reset()295 void materials::reset()
296 {
297     material_data.reset();
298 }
299 
get_all()300 material_list materials::get_all()
301 {
302     return material_data.get_all();
303 }
304 
get_compactable()305 material_list materials::get_compactable()
306 {
307     material_list all = get_all();
308     material_list compactable;
309     std::copy_if( all.begin(), all.end(),
310     std::back_inserter( compactable ), []( const material_type & mt ) {
311         return !mt.compacts_into().empty();
312     } );
313     return compactable;
314 }
315 
get_rotting()316 std::set<material_id> materials::get_rotting()
317 {
318     static generic_factory<material_type>::Version version;
319     static std::set<material_id> rotting;
320 
321     // freshly created version is guaranteed to be invalid
322     if( !material_data.is_valid( version ) ) {
323         material_list all = get_all();
324         rotting.clear();
325         for( const material_type &m : all ) {
326             if( m.rotting() ) {
327                 rotting.emplace( m.ident() );
328             }
329         }
330         version = material_data.get_version();
331     }
332 
333     return rotting;
334 }
335 
load(const JsonObject & jsobj)336 void fuel_data::load( const JsonObject &jsobj )
337 {
338     mandatory( jsobj, was_loaded, "energy", energy );
339     optional( jsobj, was_loaded, "pump_terrain", pump_terrain );
340     optional( jsobj, was_loaded, "explosion_data", explosion_data );
341     optional( jsobj, was_loaded, "perpetual", is_perpetual_fuel );
342 }
343 
deserialize(JsonIn & jsin)344 void fuel_data::deserialize( JsonIn &jsin )
345 {
346     const JsonObject &jo = jsin.get_object();
347     load( jo );
348 }
349 
is_empty()350 bool fuel_explosion_data::is_empty()
351 {
352     return explosion_chance_cold == 0 && explosion_chance_hot == 0 && explosion_factor == 0.0f &&
353            !fiery_explosion && fuel_size_factor == 0.0f;
354 }
355 
load(const JsonObject & jsobj)356 void fuel_explosion_data::load( const JsonObject &jsobj )
357 {
358     optional( jsobj, was_loaded, "chance_hot", explosion_chance_hot );
359     optional( jsobj, was_loaded, "chance_cold", explosion_chance_cold );
360     optional( jsobj, was_loaded, "factor", explosion_factor );
361     optional( jsobj, was_loaded, "size_factor", fuel_size_factor );
362     optional( jsobj, was_loaded, "fiery", fiery_explosion );
363 }
364 
deserialize(JsonIn & jsin)365 void fuel_explosion_data::deserialize( JsonIn &jsin )
366 {
367     const JsonObject &jo = jsin.get_object();
368     load( jo );
369 }
370