1 #include <algorithm>
2 #include <list>
3 #include <map>
4 #include <memory>
5 #include <sstream>
6 #include <string>
7 #include <vector>
8
9 #include "calendar.h"
10 #include "catch/catch.hpp"
11 #include "colony.h"
12 #include "construction.h"
13 #include "field.h"
14 #include "game.h"
15 #include "game_constants.h"
16 #include "item.h"
17 #include "json.h"
18 #include "make_static.h"
19 #include "mapdata.h"
20 #include "point.h"
21 #include "string_formatter.h"
22 #include "submap.h"
23 #include "trap.h"
24 #include "type_id.h"
25 #include "vehicle.h"
26
27 static const point &corner_ne = point_zero;
28 static const point corner_nw( SEEX - 1, 0 );
29 static const point corner_se( 0, SEEY - 1 );
30 static const point corner_sw( SEEX - 1, SEEY - 1 );
31 static const point random_pt( 4, 7 );
32
33 static std::istringstream submap_empty_ss(
34 "{\n"
35 " \"version\": 32,\n"
36 " \"coordinates\": [ 0, 0, 0 ],\n"
37 " \"turn_last_touched\": 0,\n"
38 " \"temperature\": 0,\n"
39 " \"terrain\": [ [ \"t_dirt\", 144 ] ],\n"
40 " \"radiation\": [ 0, 144 ],\n"
41 " \"furniture\": [ ],\n"
42 " \"items\": [ ],\n"
43 " \"traps\": [ ],\n"
44 " \"fields\": [ ],\n"
45 " \"cosmetics\": [ ],\n"
46 " \"spawns\": [ ],\n"
47 " \"vehicles\": [ ],\n"
48 " \"partial_constructions\": [ ],\n"
49 " \"computers\": [ ]\n"
50 "}\n"
51 );
52 static std::istringstream submap_terrain_rle_ss(
53 "{\n"
54 " \"version\": 32,\n"
55 " \"coordinates\": [ 0, 0, 0 ],\n"
56 " \"turn_last_touched\": 0,\n"
57 " \"temperature\": 0,\n"
58 " \"terrain\": [\n"
59 " \"t_floor_red\",\n"
60 " [ \"t_dirt\", 10 ],\n"
61 " \"t_floor_green\",\n"
62 " [ \"t_dirt\", 60 ],\n"
63 " [ \"t_rock_floor\", 60 ],\n"
64 " \"t_floor_blue\",\n"
65 " [ \"t_rock_floor\", 10 ],\n"
66 " \"t_floor\"\n"
67 " ],\n"
68 " \"radiation\": [ 0, 144 ],\n"
69 " \"furniture\": [ ],\n"
70 " \"items\": [ ],\n"
71 " \"traps\": [ ],\n"
72 " \"fields\": [ ],\n"
73 " \"cosmetics\": [ ],\n"
74 " \"spawns\": [ ],\n"
75 " \"vehicles\": [ ],\n"
76 " \"partial_constructions\": [ ],\n"
77 " \"computers\": [ ]\n"
78 "}\n"
79 );
80 static std::istringstream submap_furniture_ss(
81 "{\n"
82 " \"version\": 32,\n"
83 " \"coordinates\": [ 0, 0, 0 ],\n"
84 " \"turn_last_touched\": 0,\n"
85 " \"temperature\": 0,\n"
86 " \"terrain\": [ [ \"t_dirt\", 144 ] ],\n"
87 " \"radiation\": [ 0, 144 ],\n"
88 " \"furniture\": [\n"
89 " [ 0, 0, \"f_bookcase\" ],\n"
90 " [ 0, 11, \"f_crate_o\" ],\n"
91 " [ 11, 0, \"f_coffin_c\" ],\n"
92 " [ 11, 11, \"f_dresser\" ],\n"
93 " [ 4, 7, \"f_gas_tank\" ]\n"
94 " ],\n"
95 " \"items\": [ ],\n"
96 " \"traps\": [ ],\n"
97 " \"fields\": [ ],\n"
98 " \"cosmetics\": [ ],\n"
99 " \"spawns\": [ ],\n"
100 " \"vehicles\": [ ],\n"
101 " \"partial_constructions\": [ ],\n"
102 " \"computers\": [ ]\n"
103 "}\n"
104 );
105 static std::istringstream submap_trap_ss(
106 "{\n"
107 " \"version\": 32,\n"
108 " \"coordinates\": [ 0, 0, 0 ],\n"
109 " \"turn_last_touched\": 0,\n"
110 " \"temperature\": 0,\n"
111 " \"terrain\": [ [ \"t_dirt\", 144 ] ],\n"
112 " \"radiation\": [ 0, 144 ],\n"
113 " \"furniture\": [ ],\n"
114 " \"items\": [ ],\n"
115 " \"traps\": [\n"
116 " [ 0, 0, \"tr_bubblewrap\" ],\n"
117 " [ 0, 11, \"tr_funnel\" ],\n"
118 " [ 11, 0, \"tr_rollmat\" ],\n"
119 " [ 11, 11, \"tr_beartrap\" ],\n"
120 " [ 4, 7, \"tr_landmine\" ]\n"
121 " ],\n"
122 " \"fields\": [ ],\n"
123 " \"cosmetics\": [ ],\n"
124 " \"spawns\": [ ],\n"
125 " \"vehicles\": [ ],\n"
126 " \"partial_constructions\": [ ],\n"
127 " \"computers\": [ ]\n"
128 "}\n"
129 );
130 static std::istringstream submap_rad_ss(
131 "{\n"
132 " \"version\": 32,\n"
133 " \"coordinates\": [ 0, 0, 0 ],\n"
134 " \"turn_last_touched\": 0,\n"
135 " \"temperature\": 0,\n"
136 " \"terrain\": [ [ \"t_dirt\", 144 ] ],\n"
137 " \"radiation\": [\n"
138 " 1, 1,\n"
139 " 0, 10,\n"
140 " 2, 1,\n"
141 " 0, 76,\n"
142 " 5, 1,\n"
143 " 0, 43,\n"
144 " 3, 1,\n"
145 " 0, 10,\n"
146 " 4, 1\n"
147 " ],\n"
148 " \"furniture\": [ ],\n"
149 " \"items\": [ ],\n"
150 " \"traps\": [ ],\n"
151 " \"fields\": [ ],\n"
152 " \"cosmetics\": [ ],\n"
153 " \"spawns\": [ ],\n"
154 " \"vehicles\": [ ],\n"
155 " \"partial_constructions\": [ ],\n"
156 " \"computers\": [ ]\n"
157 "}\n"
158 );
159 static std::istringstream submap_item_ss(
160 "{\n"
161 " \"version\": 32,\n"
162 " \"coordinates\": [ 0, 0, 0 ],\n"
163 " \"turn_last_touched\": 0,\n"
164 " \"temperature\": 0,\n"
165 " \"terrain\": [ [ \"t_dirt\", 144 ] ],\n"
166 " \"radiation\": [ 0, 144 ],\n"
167 " \"furniture\": [ ],\n"
168 " \"items\": [\n"
169 " 0, 0, [\n"
170 " {\n"
171 " \"typeid\": \"foodperson_mask\",\n"
172 " \"bday\": 39316373,\n"
173 " \"item_vars\": { \"magazine_converted\": \"1\" },\n"
174 " \"item_tags\": [ \"OUTER\", \"SUN_GLASSES\" ],\n"
175 " \"relic_data\": null,\n"
176 " \"contents\": {\n"
177 " \"contents\": [\n"
178 " { \"pocket_type\": 2, \"contents\": [ ], \"_sealed\": false },\n"
179 " { \"pocket_type\": 4, \"contents\": [ ], \"_sealed\": false },\n"
180 " { \"pocket_type\": 3, \"contents\": [ ], \"_sealed\": false },\n"
181 " { \"pocket_type\": 6, \"contents\": [ ], \"_sealed\": false }\n"
182 " ]\n"
183 " }\n"
184 " }\n"
185 " ],\n"
186 " 0, 11, [\n"
187 " {\n"
188 " \"typeid\": \"bat_nerf\",\n"
189 " \"bday\": 39316373,\n"
190 " \"item_tags\": [ \"FRAGILE_MELEE\" ],\n"
191 " \"relic_data\": null,\n"
192 " \"contents\": {\n"
193 " \"contents\": [\n"
194 " { \"pocket_type\": 4, \"contents\": [ ], \"_sealed\": false },\n"
195 " { \"pocket_type\": 3, \"contents\": [ ], \"_sealed\": false },\n"
196 " { \"pocket_type\": 6, \"contents\": [ ], \"_sealed\": false }\n"
197 " ]\n"
198 " }\n"
199 " }\n"
200 " ],\n"
201 " 11, 0, [\n"
202 " {\n"
203 " \"typeid\": \"machete\",\n"
204 " \"bday\": 39316373,\n"
205 " \"item_vars\": { \"magazine_converted\": \"1\" },\n"
206 " \"item_tags\": [ \"DURABLE_MELEE\", \"SHEATH_SWORD\" ],\n"
207 " \"relic_data\": null,\n"
208 " \"contents\": {\n"
209 " \"contents\": [\n"
210 " { \"pocket_type\": 4, \"contents\": [ ], \"_sealed\": false },\n"
211 " { \"pocket_type\": 3, \"contents\": [ ], \"_sealed\": false },\n"
212 " { \"pocket_type\": 6, \"contents\": [ ], \"_sealed\": false }\n"
213 " ]\n"
214 " }\n"
215 " },\n"
216 " {\n"
217 " \"typeid\": \"foon\",\n"
218 " \"bday\": 39316373,\n"
219 " \"owner\": \"your_followers\",\n"
220 " \"item_tags\": [ \"STAB\", \"SHEATH_KNIFE\" ],\n"
221 " \"relic_data\": null,\n"
222 " \"contents\": {\n"
223 " \"contents\": [\n"
224 " { \"pocket_type\": 4, \"contents\": [ ], \"_sealed\": false },\n"
225 " { \"pocket_type\": 3, \"contents\": [ ], \"_sealed\": false },\n"
226 " { \"pocket_type\": 6, \"contents\": [ ], \"_sealed\": false }\n"
227 " ]\n"
228 " }\n"
229 " }\n"
230 " ],\n"
231 " 11, 11, [\n"
232 " {\n"
233 " \"typeid\": \"bottle_plastic\",\n"
234 " \"bday\": 39316373,\n"
235 " \"owner\": \"your_followers\",\n"
236 " \"relic_data\": null,\n"
237 " \"contents\": {\n"
238 " \"contents\": [\n"
239 " { \"pocket_type\": 0, \"contents\": [ ], \"_sealed\": false },\n"
240 " { \"pocket_type\": 4, \"contents\": [ ], \"_sealed\": false },\n"
241 " { \"pocket_type\": 3, \"contents\": [ ], \"_sealed\": false },\n"
242 " { \"pocket_type\": 6, \"contents\": [ ], \"_sealed\": false }\n"
243 " ]\n"
244 " }\n"
245 " }\n"
246 " ],\n"
247 " 4, 7, [\n"
248 " {\n"
249 " \"typeid\": \"jackhammer\",\n"
250 " \"bday\": 39316417,\n"
251 " \"item_tags\": [ \"DIG_TOOL\", \"POWERED\", \"STAB\" ],\n"
252 " \"relic_data\": null,\n"
253 " \"contents\": {\n"
254 " \"contents\": [\n"
255 " {\n"
256 " \"pocket_type\": 1,\n"
257 " \"contents\": [\n"
258 " {\n"
259 " \"typeid\": \"gasoline\",\n"
260 " \"charges\": 400,\n"
261 " \"bday\": 39316417,\n"
262 " \"relic_data\": null,\n"
263 " \"contents\": {\n"
264 " \"contents\": [\n"
265 " { \"pocket_type\": 4, \"contents\": [ ], \"_sealed\": false },\n"
266 " { \"pocket_type\": 3, \"contents\": [ ], \"_sealed\": false },\n"
267 " { \"pocket_type\": 6, \"contents\": [ ], \"_sealed\": false }\n"
268 " ]\n"
269 " }\n"
270 " }\n"
271 " ],\n"
272 " \"_sealed\": false\n"
273 " },\n"
274 " { \"pocket_type\": 4, \"contents\": [ ], \"_sealed\": false },\n"
275 " { \"pocket_type\": 3, \"contents\": [ ], \"_sealed\": false },\n"
276 " { \"pocket_type\": 6, \"contents\": [ ], \"_sealed\": false }\n"
277 " ]\n"
278 " }\n"
279 " }\n"
280 " ]\n"
281 " ],\n"
282 " \"traps\": [ ],\n"
283 " \"fields\": [ ],\n"
284 " \"cosmetics\": [ ],\n"
285 " \"spawns\": [ ],\n"
286 " \"vehicles\": [ ],\n"
287 " \"partial_constructions\": [ ],\n"
288 " \"computers\": [ ]\n"
289 "}\n"
290 );
291 static std::istringstream submap_field_ss(
292 "{\n"
293 " \"version\": 32,\n"
294 " \"coordinates\": [ 0, 0, 0 ],\n"
295 " \"turn_last_touched\": 0,\n"
296 " \"temperature\": 0,\n"
297 " \"terrain\": [ [ \"t_dirt\", 144 ] ],\n"
298 " \"radiation\": [ 0, 144 ],\n"
299 " \"furniture\": [ ],\n"
300 " \"items\": [ ],\n"
301 " \"traps\": [ ],\n"
302 " \"fields\": [\n"
303 " 0, 0, [ \"fd_laser\", 1, 1997 ],\n"
304 " 0, 11, [ \"fd_acid\", 2, 2003 ],\n"
305 " 11, 0, [ \"fd_web\", 3, 2077 ],\n"
306 " 11, 0, [ \"fd_smoke\", 4, 3004 ],\n"
307 " 11, 11, [ \"fd_electricity\", 5, 1482 ],\n"
308 " 4, 7, [ \"fd_nuke_gas\", 6, 1615 ]\n"
309 " ],\n"
310 " \"cosmetics\": [ ],\n"
311 " \"spawns\": [ ],\n"
312 " \"vehicles\": [ ],\n"
313 " \"partial_constructions\": [ ],\n"
314 " \"computers\": [ ]\n"
315 "}\n"
316 );
317 static std::istringstream submap_graffiti_ss(
318 "{\n"
319 " \"version\": 32,\n"
320 " \"coordinates\": [ 0, 0, 0 ],\n"
321 " \"turn_last_touched\": 0,\n"
322 " \"temperature\": 0,\n"
323 " \"terrain\": [ [ \"t_dirt\", 144 ] ],\n"
324 " \"radiation\": [ 0, 144 ],\n"
325 " \"furniture\": [ ],\n"
326 " \"items\": [ ],\n"
327 " \"traps\": [ ],\n"
328 " \"fields\": [ ],\n"
329 " \"graffiti\": [\n"
330 " [ 0, 0, \"a\" ],\n"
331 " [ 0, 11, \"b\" ],\n"
332 " [ 11, 0, \"c\" ],\n"
333 " [ 11, 11, \"d\" ],\n"
334 " [ 4, 7, \"e\" ]\n"
335 " ],\n"
336 " \"cosmetics\": [ ],\n"
337 " \"spawns\": [ ],\n"
338 " \"vehicles\": [ ],\n"
339 " \"partial_constructions\": [ ],\n"
340 " \"computers\": [ ]\n"
341 "}\n"
342 );
343 static std::istringstream submap_spawns_ss(
344 "{\n"
345 " \"version\": 32,\n"
346 " \"coordinates\": [ 0, 0, 0 ],\n"
347 " \"turn_last_touched\": 0,\n"
348 " \"temperature\": 0,\n"
349 " \"terrain\": [ [ \"t_dirt\", 144 ] ],\n"
350 " \"radiation\": [ 0, 144 ],\n"
351 " \"furniture\": [ ],\n"
352 " \"items\": [ ],\n"
353 " \"traps\": [ ],\n"
354 " \"fields\": [ ],\n"
355 " \"cosmetics\": [ ],\n"
356 " \"spawns\": [\n"
357 " [ \"mon_cockatrice\", 1, 0, 0, -1, -1, false, \"NONE\" ],\n"
358 " [ \"mon_mininuke_hack\", 2, 0, 11, -1, -1, true, \"Tim\" ],\n"
359 " [ \"mon_fish_eel\", 3, 11, 0, -1, -1, false, \"Bob\" ],\n"
360 " [ \"mon_zombie_fungus\", 4, 11, 11, -1, -1, false, \"Hopper\" ],\n"
361 " [ \"mon_plague_vector\", 5, 4, 7, -1, -1, true, \"Alice\" ]\n"
362 " ],\n"
363 " \"vehicles\": [ ],\n"
364 " \"partial_constructions\": [ ],\n"
365 " \"computers\": [ ]\n"
366 "}\n"
367 );
368 static std::istringstream submap_vehicle_ss(
369 "{\n"
370 " \"version\": 32,\n"
371 " \"coordinates\": [ 0, 0, 0 ],\n"
372 " \"turn_last_touched\": 0,\n"
373 " \"temperature\": 0,\n"
374 " \"terrain\": [ [ \"t_dirt\", 144 ] ],\n"
375 " \"radiation\": [ 0, 144 ],\n"
376 " \"furniture\": [ ],\n"
377 " \"items\": [ ],\n"
378 " \"traps\": [ ],\n"
379 " \"fields\": [ ],\n"
380 " \"cosmetics\": [ ],\n"
381 " \"spawns\": [ ],\n"
382 " \"vehicles\": [\n"
383 " {\n"
384 " \"type\": \"welding_cart\",\n"
385 " \"posx\": 9,\n"
386 " \"posy\": 2,\n"
387 " \"om_id\": 0,\n"
388 " \"faceDir\": 270,\n"
389 " \"moveDir\": 270,\n"
390 " \"turn_dir\": -90,\n"
391 " \"velocity\": 0,\n"
392 " \"falling\": false,\n"
393 " \"floating\": false,\n"
394 " \"flying\": false,\n"
395 " \"cruise_velocity\": 0,\n"
396 " \"vertical_velocity\": 0,\n"
397 " \"cruise_on\": true,\n"
398 " \"engine_on\": false,\n"
399 " \"tracking_on\": false,\n"
400 " \"skidding\": false,\n"
401 " \"of_turn_carry\": 0.0,\n"
402 " \"name\": \"Welding Cart\",\n"
403 " \"owner\": \"your_followers\",\n"
404 " \"old_owner\": \"NULL\",\n"
405 " \"theft_time\": null,\n"
406 " \"parts\": [\n"
407 " {\n"
408 " \"id\": \"xlframe\",\n"
409 " \"variant\": \"vertical_2\",\n"
410 " \"base\": {\n"
411 " \"typeid\": \"xlframe\",\n"
412 " \"item_tags\": [ \"VEHICLE\" ],\n"
413 " \"relic_data\": null,\n"
414 " \"contents\": {\n"
415 " \"contents\": [\n"
416 " { \"pocket_type\": 4, \"contents\": [ ], \"_sealed\": false },\n"
417 " { \"pocket_type\": 3, \"contents\": [ ], \"_sealed\": false },\n"
418 " { \"pocket_type\": 6, \"contents\": [ ], \"_sealed\": false }\n"
419 " ]\n"
420 " }\n"
421 " },\n"
422 " \"mount_dx\": 0,\n"
423 " \"mount_dy\": 0,\n"
424 " \"open\": false,\n"
425 " \"direction\": 0,\n"
426 " \"blood\": 0,\n"
427 " \"enabled\": false,\n"
428 " \"flags\": 0,\n"
429 " \"passenger_id\": -1,\n"
430 " \"crew_id\": -1,\n"
431 " \"items\": [ ],\n"
432 " \"ammo_pref\": \"null\"\n"
433 " },\n"
434 " {\n"
435 " \"id\": \"wheel_caster\",\n"
436 " \"base\": {\n"
437 " \"typeid\": \"wheel_caster\",\n"
438 " \"damaged\": 3372,\n"
439 " \"item_tags\": [ \"VEHICLE\" ],\n"
440 " \"relic_data\": null,\n"
441 " \"contents\": {\n"
442 " \"contents\": [\n"
443 " { \"pocket_type\": 4, \"contents\": [ ], \"_sealed\": false },\n"
444 " { \"pocket_type\": 3, \"contents\": [ ], \"_sealed\": false },\n"
445 " { \"pocket_type\": 6, \"contents\": [ ], \"_sealed\": false }\n"
446 " ]\n"
447 " }\n"
448 " },\n"
449 " \"mount_dx\": 0,\n"
450 " \"mount_dy\": 0,\n"
451 " \"open\": false,\n"
452 " \"direction\": 0,\n"
453 " \"blood\": 0,\n"
454 " \"enabled\": false,\n"
455 " \"flags\": 0,\n"
456 " \"passenger_id\": -1,\n"
457 " \"crew_id\": -1,\n"
458 " \"items\": [ ],\n"
459 " \"ammo_pref\": \"null\"\n"
460 " },\n"
461 " {\n"
462 " \"id\": \"small_storage_battery\",\n"
463 " \"base\": {\n"
464 " \"typeid\": \"small_storage_battery\",\n"
465 " \"damaged\": 3000,\n"
466 " \"item_tags\": [ \"VEHICLE\" ],\n"
467 " \"relic_data\": null,\n"
468 " \"contents\": {\n"
469 " \"contents\": [\n"
470 " {\n"
471 " \"pocket_type\": 1,\n"
472 " \"contents\": [\n"
473 " {\n"
474 " \"typeid\": \"battery\",\n"
475 " \"charges\": 316,\n"
476 " \"bday\": 39317353,\n"
477 " \"item_tags\": [ \"IRREMOVABLE\", \"NO_DROP\" ],\n"
478 " \"relic_data\": null,\n"
479 " \"contents\": {\n"
480 " \"contents\": [\n"
481 " { \"pocket_type\": 4, \"contents\": [ ], \"_sealed\": false },\n"
482 " { \"pocket_type\": 3, \"contents\": [ ], \"_sealed\": false },\n"
483 " { \"pocket_type\": 6, \"contents\": [ ], \"_sealed\": false }\n"
484 " ]\n"
485 " }\n"
486 " }\n"
487 " ],\n"
488 " \"_sealed\": false\n"
489 " },\n"
490 " { \"pocket_type\": 4, \"contents\": [ ], \"_sealed\": false },\n"
491 " { \"pocket_type\": 3, \"contents\": [ ], \"_sealed\": false },\n"
492 " { \"pocket_type\": 6, \"contents\": [ ], \"_sealed\": false }\n"
493 " ]\n"
494 " }\n"
495 " },\n"
496 " \"mount_dx\": 0,\n"
497 " \"mount_dy\": 0,\n"
498 " \"open\": false,\n"
499 " \"direction\": 0,\n"
500 " \"blood\": 0,\n"
501 " \"enabled\": false,\n"
502 " \"flags\": 0,\n"
503 " \"passenger_id\": -1,\n"
504 " \"crew_id\": -1,\n"
505 " \"items\": [ ],\n"
506 " \"ammo_pref\": \"null\"\n"
507 " },\n"
508 " {\n"
509 " \"id\": \"welding_rig\",\n"
510 " \"base\": {\n"
511 " \"typeid\": \"weldrig\",\n"
512 " \"damaged\": 2000,\n"
513 " \"item_tags\": [ \"VEHICLE\" ],\n"
514 " \"relic_data\": null,\n"
515 " \"contents\": {\n"
516 " \"contents\": [\n"
517 " { \"pocket_type\": 4, \"contents\": [ ], \"_sealed\": false },\n"
518 " { \"pocket_type\": 3, \"contents\": [ ], \"_sealed\": false },\n"
519 " { \"pocket_type\": 6, \"contents\": [ ], \"_sealed\": false }\n"
520 " ]\n"
521 " }\n"
522 " },\n"
523 " \"mount_dx\": 0,\n"
524 " \"mount_dy\": 0,\n"
525 " \"open\": false,\n"
526 " \"direction\": 0,\n"
527 " \"blood\": 0,\n"
528 " \"enabled\": false,\n"
529 " \"flags\": 0,\n"
530 " \"passenger_id\": -1,\n"
531 " \"crew_id\": -1,\n"
532 " \"items\": [\n"
533 " {\n"
534 " \"typeid\": \"goggles_welding\",\n"
535 " \"bday\": 39317353,\n"
536 " \"relic_data\": null,\n"
537 " \"contents\": {\n"
538 " \"contents\": [\n"
539 " { \"pocket_type\": 4, \"contents\": [ ], \"_sealed\": false },\n"
540 " { \"pocket_type\": 3, \"contents\": [ ], \"_sealed\": false },\n"
541 " { \"pocket_type\": 6, \"contents\": [ ], \"_sealed\": false }\n"
542 " ]\n"
543 " }\n"
544 " }\n"
545 " ],\n"
546 " \"ammo_pref\": \"null\"\n"
547 " }\n"
548 " ],\n"
549 " \"tags\": [ ],\n"
550 " \"labels\": [ ],\n"
551 " \"zones\": [ ],\n"
552 " \"other_tow_point\": [ 0, 0, 0 ],\n"
553 " \"is_locked\": false,\n"
554 " \"is_alarm_on\": false,\n"
555 " \"camera_on\": false,\n"
556 " \"last_update_turn\": 39317413,\n"
557 " \"pivot\": [ 0, 0 ],\n"
558 " \"is_following\": false,\n"
559 " \"is_patrolling\": false,\n"
560 " \"autodrive_local_target\": [ 0, 0, 0 ],\n"
561 " \"airworthy\": true,\n"
562 " \"summon_time_limit\": null,\n"
563 " \"magic\": false,\n"
564 " \"smart_controller\": null\n"
565 " }\n"
566 " ],\n"
567 " \"partial_constructions\": [ ],\n"
568 " \"computers\": [ ]\n"
569 "}\n"
570 );
571 static std::istringstream submap_construction_ss(
572 "{\n"
573 " \"version\": 32,\n"
574 " \"coordinates\": [ 0, 0, 0 ],\n"
575 " \"turn_last_touched\": 0,\n"
576 " \"temperature\": 0,\n"
577 " \"terrain\": [ [ \"t_dirt\", 144 ] ],\n"
578 " \"radiation\": [ 0, 144 ],\n"
579 " \"furniture\": [ ],\n"
580 " \"items\": [ ],\n"
581 " \"traps\": [ ],\n"
582 " \"fields\": [ ],\n"
583 " \"cosmetics\": [ ],\n"
584 " \"spawns\": [ ],\n"
585 " \"vehicles\": [ ],\n"
586 " \"partial_constructions\": [\n"
587 " 3,\n"
588 " 2,\n"
589 " 0,\n"
590 " 123334,\n"
591 " \"constr_ground_cable\",\n"
592 " [\n"
593 " {\n"
594 " \"typeid\": \"cable\",\n"
595 " \"charges\": 4,\n"
596 " \"bday\": 39319475,\n"
597 " \"relic_data\": null,\n"
598 " \"contents\": {\n"
599 " \"contents\": [\n"
600 " { \"pocket_type\": 4, \"contents\": [ ], \"_sealed\": false },\n"
601 " { \"pocket_type\": 3, \"contents\": [ ], \"_sealed\": false },\n"
602 " { \"pocket_type\": 6, \"contents\": [ ], \"_sealed\": false }\n"
603 " ]\n"
604 " }\n"
605 " }\n"
606 " ],\n"
607 " 3,\n"
608 " 3,\n"
609 " 0,\n"
610 " 4934,\n"
611 " \"constr_rack_coat\",\n"
612 " [\n"
613 " {\n"
614 " \"typeid\": \"2x4\",\n"
615 " \"bday\": 39316447,\n"
616 " \"relic_data\": null,\n"
617 " \"contents\": {\n"
618 " \"contents\": [\n"
619 " { \"pocket_type\": 4, \"contents\": [ ], \"_sealed\": false },\n"
620 " { \"pocket_type\": 3, \"contents\": [ ], \"_sealed\": false },\n"
621 " { \"pocket_type\": 6, \"contents\": [ ], \"_sealed\": false }\n"
622 " ]\n"
623 " }\n"
624 " },\n"
625 " {\n"
626 " \"typeid\": \"2x4\",\n"
627 " \"bday\": 39316447,\n"
628 " \"relic_data\": null,\n"
629 " \"contents\": {\n"
630 " \"contents\": [\n"
631 " { \"pocket_type\": 4, \"contents\": [ ], \"_sealed\": false },\n"
632 " { \"pocket_type\": 3, \"contents\": [ ], \"_sealed\": false },\n"
633 " { \"pocket_type\": 6, \"contents\": [ ], \"_sealed\": false }\n"
634 " ]\n"
635 " }\n"
636 " },\n"
637 " {\n"
638 " \"typeid\": \"2x4\",\n"
639 " \"bday\": 39316972,\n"
640 " \"relic_data\": null,\n"
641 " \"contents\": {\n"
642 " \"contents\": [\n"
643 " { \"pocket_type\": 4, \"contents\": [ ], \"_sealed\": false },\n"
644 " { \"pocket_type\": 3, \"contents\": [ ], \"_sealed\": false },\n"
645 " { \"pocket_type\": 6, \"contents\": [ ], \"_sealed\": false }\n"
646 " ]\n"
647 " }\n"
648 " },\n"
649 " {\n"
650 " \"typeid\": \"nail\",\n"
651 " \"charges\": 8,\n"
652 " \"bday\": 39316972,\n"
653 " \"relic_data\": null,\n"
654 " \"contents\": {\n"
655 " \"contents\": [\n"
656 " { \"pocket_type\": 4, \"contents\": [ ], \"_sealed\": false },\n"
657 " { \"pocket_type\": 3, \"contents\": [ ], \"_sealed\": false },\n"
658 " { \"pocket_type\": 6, \"contents\": [ ], \"_sealed\": false }\n"
659 " ]\n"
660 " }\n"
661 " }\n"
662 " ]\n"
663 " ],\n"
664 " \"computers\": [ ]\n"
665 "}\n"
666 );
667 static std::istringstream submap_computer_ss(
668 "{\n"
669 " \"version\": 32,\n"
670 " \"coordinates\": [ 0, 0, 0 ],\n"
671 " \"turn_last_touched\": 0,\n"
672 " \"temperature\": 0,\n"
673 " \"terrain\": [ [ \"t_dirt\", 144 ] ],\n"
674 " \"radiation\": [ 0, 144 ],\n"
675 " \"furniture\": [ ],\n"
676 " \"items\": [ ],\n"
677 " \"traps\": [ ],\n"
678 " \"fields\": [ ],\n"
679 " \"cosmetics\": [ ],\n"
680 " \"spawns\": [ ],\n"
681 " \"vehicles\": [ ],\n"
682 " \"partial_constructions\": [ ],\n"
683 " \"computers\": [\n"
684 " [ 0, 1 ],\n"
685 " {\n"
686 " \"name\": \"Bionic Vault\",\n"
687 " \"mission\": -1,\n"
688 " \"security\": 3,\n"
689 " \"alerts\": 0,\n"
690 " \"next_attempt\": -1,\n"
691 " \"options\": [\n"
692 " { \"name\": \"MANIFEST\", \"action\": \"list_bionics\", \"security\": 0 },\n"
693 " { \"name\": \"UNLOCK ENTRANCE\", \"action\": \"unlock_disarm\", \"security\": 7 }\n"
694 " ],\n"
695 " \"failures\": [ { \"action\": \"damage\" }, { \"action\": \"secubots\" }, { \"action\": \"shutdown\" } ],\n"
696 " \"access_denied\": \"ERROR! Access denied! Unauthorized access will be met with lethal force!\"\n"
697 " },\n"
698 " [ 3, 5 ],\n"
699 " {\n"
700 " \"name\": \"Bionic Vault\",\n"
701 " \"mission\": -1,\n"
702 " \"security\": 3,\n"
703 " \"alerts\": 0,\n"
704 " \"next_attempt\": -1,\n"
705 " \"options\": [\n"
706 " { \"name\": \"MANIFEST\", \"action\": \"list_bionics\", \"security\": 0 },\n"
707 " { \"name\": \"UNLOCK ENTRANCE\", \"action\": \"unlock_disarm\", \"security\": 7 }\n"
708 " ],\n"
709 " \"failures\": [ { \"action\": \"damage\" }, { \"action\": \"secubots\" }, { \"action\": \"shutdown\" } ],\n"
710 " \"access_denied\": \"ERROR! Access denied! Unauthorized access will be met with lethal force!\"\n"
711 " }\n"
712 " ]\n"
713 "}\n"
714 );
715 static std::istringstream submap_cosmetic_ss(
716 "{\n"
717 " \"version\": 32,\n"
718 " \"coordinates\": [ 0, 0, 0 ],\n"
719 " \"turn_last_touched\": 0,\n"
720 " \"temperature\": 0,\n"
721 " \"terrain\": [ [ \"t_dirt\", 144 ] ],\n"
722 " \"radiation\": [ 0, 144 ],\n"
723 " \"furniture\": [\n"
724 " [ 0, 11, \"f_sign\" ],\n"
725 " [ 11, 11, \"f_sign\" ]\n"
726 " ],\n"
727 " \"items\": [ ],\n"
728 " \"traps\": [ ],\n"
729 " \"fields\": [ ],\n"
730 " \"cosmetics\": [\n"
731 " [ 0, 0, \"GRAFFITI\", \"This is written text.\" ],\n"
732 " [ 0, 11, \"SIGNAGE\", \"Subway Map: illegible city name stop\" ],\n"
733 " [ 11, 0, \"GRAFFITI\", \"I <3 Dr. Hylke van der Schaaf.\" ],\n"
734 " [ 11, 11, \"SIGNAGE\", \"This is a sign\" ],\n"
735 " [ 4, 7, \"GRAFFITI\", \"Santina is a heteronormative bully!\" ]\n"
736 " ],\n"
737 " \"spawns\": [ ],\n"
738 " \"vehicles\": [ ],\n"
739 " \"partial_constructions\": [ ],\n"
740 " \"computers\": [ ]\n"
741 "}\n"
742 );
743
744 static_assert( SEEX == 12, "Reminder to update submap tests when SEEX changes." );
745 static_assert( SEEY == 12, "Reminder to update submap tests when SEEY changes." );
746
747 static JsonIn submap_empty( submap_empty_ss );
748 static JsonIn submap_terrain_rle( submap_terrain_rle_ss );
749 static JsonIn submap_furniture( submap_furniture_ss );
750 static JsonIn submap_trap( submap_trap_ss );
751 static JsonIn submap_rad( submap_rad_ss );
752 static JsonIn submap_item( submap_item_ss );
753 static JsonIn submap_field( submap_field_ss );
754 static JsonIn submap_graffiti( submap_graffiti_ss );
755 static JsonIn submap_spawns( submap_spawns_ss );
756 static JsonIn submap_vehicle( submap_vehicle_ss );
757 static JsonIn submap_construction( submap_construction_ss );
758 static JsonIn submap_computer( submap_computer_ss );
759 static JsonIn submap_cosmetic( submap_cosmetic_ss );
760
load_from_jsin(submap & sm,JsonIn & jsin)761 static void load_from_jsin( submap &sm, JsonIn &jsin )
762 {
763 // Ensure that the JSON is up to date for our savegame version
764 REQUIRE( savegame_version == 33 );
765 jsin.start_object();
766 int version = 0;
767 while( !jsin.end_object() ) {
768 std::string name = jsin.get_member_name();
769 if( name == "version" ) {
770 version = jsin.get_int();
771 } else {
772 sm.load( jsin, name, version );
773 }
774 }
775 }
776
777 struct submap_checks {
778 bool terrain = true;
779 bool furniture = true;
780 bool traps = true;
781 bool radiation = true;
782 bool items = true;
783 bool fields = true;
784 bool cosmetics = true;
785 bool spawns = true;
786 bool vehicles = true;
787 bool construction = true;
788 bool computers = true;
789 };
790
791 //static const submap_checks dont_care;
792
is_normal_submap(const submap & sm,submap_checks checks={} )793 static bool is_normal_submap( const submap &sm, submap_checks checks = {} )
794 {
795 const bool terrain = checks.terrain;
796 const bool furniture = checks.furniture;
797 const bool traps = checks.traps;
798 const bool radiation = checks.radiation;
799 const bool items = checks.items;
800 const bool fields = checks.fields;
801 const bool cosmetics = checks.cosmetics;
802 const bool spawns = checks.spawns;
803 const bool vehicles = checks.vehicles;
804 const bool construction = checks.construction;
805 const bool computers = checks.computers;
806
807 // For every point on the submap
808 for( int y = 0; y < SEEY; ++y ) {
809 for( int x = 0; x < SEEX; ++x ) {
810 if( terrain && sm.get_ter( { x, y } ) != t_dirt ) {
811 return false;
812 }
813 if( furniture && sm.get_furn( { x, y } ) != f_null ) {
814 return false;
815 }
816 if( traps && sm.get_trap( {x, y} ) != tr_null ) {
817 return false;
818 }
819 if( radiation && sm.get_radiation( { x, y } ) != 0 ) {
820 return false;
821 }
822 if( items && !sm.get_items( {x, y} ).empty() ) {
823 return false;
824 }
825 if( fields && sm.get_field( { x, y } ).field_count() != 0 ) {
826 return false;
827 }
828 if( computers && sm.has_computer( {x, y} ) ) {
829 return false;
830 }
831 }
832 }
833
834 // Can be found without checking every point
835 if( cosmetics && !sm.cosmetics.empty() ) {
836 return false;
837 }
838 if( spawns && !sm.spawns.empty() ) {
839 return false;
840 }
841 if( vehicles && !sm.vehicles.empty() ) {
842 return false;
843 }
844 if( construction && !sm.partial_constructions.empty() ) {
845 return false;
846 }
847
848 return true;
849 }
850
851 TEST_CASE( "submap_empty_load", "[submap][load]" )
852 {
853 submap sm;
854 load_from_jsin( sm, submap_empty );
855 REQUIRE( is_normal_submap( sm ) );
856 }
857
858 TEST_CASE( "submap_terrain_rle_load", "[submap][load]" )
859 {
860 submap sm;
861 load_from_jsin( sm, submap_terrain_rle );
862 submap_checks checks;
863 checks.terrain = false;
864
865 REQUIRE( is_normal_submap( sm, checks ) );
866
867 const ter_id ter_nw = sm.get_ter( corner_nw );
868 const ter_id ter_ne = sm.get_ter( corner_ne );
869 const ter_id ter_sw = sm.get_ter( corner_sw );
870 const ter_id ter_se = sm.get_ter( corner_se );
871
872 // We placed a unique terrain in each of the corners. Check that those are correct
873 INFO( string_format( "nw: %s", ter_nw.id().str() ) );
874 INFO( string_format( "ne: %s", ter_ne.id().str() ) );
875 INFO( string_format( "sw: %s", ter_sw.id().str() ) );
876 INFO( string_format( "se: %s", ter_se.id().str() ) );
877 // Require to prevent the lower CHECK from being spammy
878 REQUIRE( ter_nw == t_floor_green );
879 REQUIRE( ter_ne == t_floor_red );
880 REQUIRE( ter_sw == t_floor );
881 REQUIRE( ter_se == t_floor_blue );
882
883 // And for the rest of the map, half of it is t_dirt, the other half t_rock_floor
884 for( int x = 1; x < SEEX - 2; ++x ) {
885 CHECK( sm.get_ter( { x, 0 } ) == t_dirt );
886 }
887 for( int y = 1; y < SEEY / 2; ++y ) {
888 for( int x = 0; x < SEEX; ++x ) {
889 CHECK( sm.get_ter( { x, y } ) == t_dirt );
890 }
891 }
892 for( int y = SEEY / 2; y < SEEY - 1; ++y ) {
893 for( int x = 0; x < SEEX; ++x ) {
894 CHECK( sm.get_ter( { x, y } ) == t_rock_floor );
895 }
896
897 }
898 for( int x = 1; x < SEEX - 2; ++x ) {
899 CHECK( sm.get_ter( { x, SEEY - 1 } ) == t_rock_floor );
900 }
901 }
902
903 TEST_CASE( "submap_furniture_load", "[submap][load]" )
904 {
905 submap sm;
906 load_from_jsin( sm, submap_furniture );
907 submap_checks checks;
908 checks.furniture = false;
909
910 REQUIRE( is_normal_submap( sm, checks ) );
911
912 const furn_id furn_nw = sm.get_furn( corner_nw );
913 const furn_id furn_ne = sm.get_furn( corner_ne );
914 const furn_id furn_sw = sm.get_furn( corner_sw );
915 const furn_id furn_se = sm.get_furn( corner_se );
916 const furn_id furn_ra = sm.get_furn( random_pt );
917
918 // We placed a unique furniture in a couple pf place. Check that those are correct
919 INFO( string_format( "nw: %s", furn_nw.id().str() ) );
920 INFO( string_format( "ne: %s", furn_ne.id().str() ) );
921 INFO( string_format( "sw: %s", furn_sw.id().str() ) );
922 INFO( string_format( "se: %s", furn_se.id().str() ) );
923 INFO( string_format( "ra: %s", furn_ra.id().str() ) );
924 // Require to prevent the lower CHECK from being spammy
925 REQUIRE( furn_nw == f_coffin_c );
926 REQUIRE( furn_ne == f_bookcase );
927 REQUIRE( furn_sw == f_dresser );
928 REQUIRE( furn_se == f_crate_o );
929 REQUIRE( furn_ra == STATIC( furn_id( "f_gas_tank" ) ) );
930
931 // Also, check we have no other furniture
932 for( int x = 0; x < SEEX; ++x ) {
933 for( int y = 0; y < SEEY; ++y ) {
934 point tested{ x, y };
935 if( tested == corner_nw || tested == corner_ne || tested == corner_sw || tested == corner_se ||
936 tested == random_pt ) {
937 continue;
938 }
939 CHECK( sm.get_furn( tested ) == f_null );
940 }
941 }
942 }
943
944 TEST_CASE( "submap_trap_load", "[submap][load]" )
945 {
946 submap sm;
947 load_from_jsin( sm, submap_trap );
948 submap_checks checks;
949 checks.traps = false;
950
951 REQUIRE( is_normal_submap( sm, checks ) );
952
953 const trap_id trap_nw = sm.get_trap( corner_nw );
954 const trap_id trap_ne = sm.get_trap( corner_ne );
955 const trap_id trap_sw = sm.get_trap( corner_sw );
956 const trap_id trap_se = sm.get_trap( corner_se );
957 const trap_id trap_ra = sm.get_trap( random_pt );
958
959 // We placed a unique trap in a couple of places. Check that those are correct
960 INFO( string_format( "nw: %s", trap_nw.id().str() ) );
961 INFO( string_format( "ne: %s", trap_ne.id().str() ) );
962 INFO( string_format( "sw: %s", trap_sw.id().str() ) );
963 INFO( string_format( "se: %s", trap_se.id().str() ) );
964 INFO( string_format( "ra: %s", trap_ra.id().str() ) );
965 // Require to prevent the lower CHECK from being spammy
966 REQUIRE( trap_nw == STATIC( trap_id( "tr_rollmat" ) ) );
967 REQUIRE( trap_ne == STATIC( trap_id( "tr_bubblewrap" ) ) );
968 REQUIRE( trap_sw == STATIC( trap_id( "tr_beartrap" ) ) );
969 REQUIRE( trap_se == STATIC( trap_id( "tr_funnel" ) ) );
970 REQUIRE( trap_ra == STATIC( trap_id( "tr_landmine" ) ) );
971
972 // Also, check we have no other traps
973 for( int x = 0; x < SEEX; ++x ) {
974 for( int y = 0; y < SEEY; ++y ) {
975 point tested{ x, y };
976 if( tested == corner_nw || tested == corner_ne || tested == corner_sw || tested == corner_se ||
977 tested == random_pt ) {
978 continue;
979 }
980 CHECK( sm.get_trap( tested ) == tr_null );
981 }
982 }
983 }
984
985 TEST_CASE( "submap_rad_load", "[submap][load]" )
986 {
987 submap sm;
988 load_from_jsin( sm, submap_rad );
989 submap_checks checks;
990 checks.radiation = false;
991
992 REQUIRE( is_normal_submap( sm, checks ) );
993
994 const int rad_nw = sm.get_radiation( corner_nw );
995 const int rad_ne = sm.get_radiation( corner_ne );
996 const int rad_sw = sm.get_radiation( corner_sw );
997 const int rad_se = sm.get_radiation( corner_se );
998 const int rad_ra = sm.get_radiation( random_pt );
999
1000 // We placed a unique rad level in a couple of places. Check that those are correct
1001 INFO( string_format( "nw: %d", rad_nw ) );
1002 INFO( string_format( "ne: %d", rad_ne ) );
1003 INFO( string_format( "sw: %d", rad_sw ) );
1004 INFO( string_format( "se: %d", rad_se ) );
1005 INFO( string_format( "ra: %d", rad_ra ) );
1006 // Require to prevent the lower CHECK from being spammy
1007 REQUIRE( rad_nw == 2 );
1008 REQUIRE( rad_ne == 1 );
1009 REQUIRE( rad_sw == 4 );
1010 REQUIRE( rad_se == 3 );
1011 REQUIRE( rad_ra == 5 );
1012
1013 int rads[SEEX];
1014 // Also, check we have no other radiation
1015 INFO( "Below is the radiation on the row above and the current row. Unknown values are -1" );
1016 for( int y = 0; y < SEEY; ++y ) {
1017 INFO( string_format( "%2d: %2d%2d%2d%2d%2d%2d%2d%2d%2d%2d%2d%2d", y - 1, rads[0], rads[1], rads[2],
1018 rads[3], rads[4], rads[5], rads[6], rads[7], rads[8], rads[9], rads[10], rads[11] ) );
1019 for( int &rad : rads ) {
1020 rad = -1;
1021 }
1022 for( int x = 0; x < SEEX; ++x ) {
1023 point tested{ x, y };
1024 if( tested == corner_nw || tested == corner_ne || tested == corner_sw || tested == corner_se ||
1025 tested == random_pt ) {
1026 rads[x] = sm.get_radiation( tested );
1027 continue;
1028 }
1029 int fetched = sm.get_radiation( tested );
1030 rads[x] = fetched;
1031 INFO( string_format( "%2d: %2d%2d%2d%2d%2d%2d%2d%2d%2d%2d%2d%2d", y, rads[0], rads[1], rads[2],
1032 rads[3], rads[4], rads[5], rads[6], rads[7], rads[8], rads[9], rads[10], rads[11] ) );
1033 CHECK( fetched == 0 );
1034 }
1035 }
1036 }
1037
1038 TEST_CASE( "submap_item_load", "[submap][load]" )
1039 {
1040 submap sm;
1041 load_from_jsin( sm, submap_item );
1042 submap_checks checks;
1043 checks.items = false;
1044
1045 REQUIRE( is_normal_submap( sm, checks ) );
1046
1047 const cata::colony<item> &citem_nw = sm.get_items( corner_nw );
1048 const cata::colony<item> &citem_ne = sm.get_items( corner_ne );
1049 const cata::colony<item> &citem_sw = sm.get_items( corner_sw );
1050 const cata::colony<item> &citem_se = sm.get_items( corner_se );
1051 const cata::colony<item> &citem_ra = sm.get_items( random_pt );
1052 std::vector<itype_id> item_nw;
1053 std::vector<itype_id> item_ne;
1054 std::vector<itype_id> item_sw;
1055 std::vector<itype_id> item_se;
1056 std::vector<itype_id> item_ra;
1057 // We're just checking the ids, checking more gets quite complex
1058 // Only one of these needs to be a loop, I'm just doing all of them for simplicity/extensiblity
1059 for( const item &it : citem_nw ) {
1060 item_nw.push_back( it.typeId() );
1061 }
1062 for( const item &it : citem_ne ) {
1063 item_ne.push_back( it.typeId() );
1064 }
1065 for( const item &it : citem_sw ) {
1066 item_sw.push_back( it.typeId() );
1067 }
1068 for( const item &it : citem_se ) {
1069 item_se.push_back( it.typeId() );
1070 }
1071 for( const item &it : citem_ra ) {
1072 item_ra.push_back( it.typeId() );
1073 }
1074
1075 // We placed a unique item in a couple of places. Check that those are correct
1076 INFO( string_format( "nw: %d %s %s", item_nw.size(), item_nw[0].str(), item_nw[1].str() ) );
1077 INFO( string_format( "ne: %d %s", item_ne.size(), item_ne[0].str() ) );
1078 INFO( string_format( "sw: %d %s", item_sw.size(), item_sw[0].str() ) );
1079 INFO( string_format( "se: %d %s", item_se.size(), item_se[0].str() ) );
1080 INFO( string_format( "ra: %d %s", item_ra.size(), item_ra[0].str() ) );
1081 // Require to prevent the lower CHECK from being spammy
1082 REQUIRE( item_nw[0] == STATIC( itype_id( "machete" ) ) );
1083 REQUIRE( item_nw[1] == STATIC( itype_id( "foon" ) ) );
1084 REQUIRE( item_ne[0] == STATIC( itype_id( "foodperson_mask" ) ) );
1085 REQUIRE( item_sw[0] == STATIC( itype_id( "bottle_plastic" ) ) );
1086 REQUIRE( item_se[0] == STATIC( itype_id( "bat_nerf" ) ) );
1087 REQUIRE( item_ra[0] == STATIC( itype_id( "jackhammer" ) ) );
1088
1089 // Also, check we have no other items
1090 for( int y = 0; y < SEEY; ++y ) {
1091 for( int x = 0; x < SEEX; ++x ) {
1092 point tested{ x, y };
1093 if( tested == corner_nw || tested == corner_ne || tested == corner_sw || tested == corner_se ||
1094 tested == random_pt ) {
1095 continue;
1096 }
1097 CHECK( sm.get_items( tested ).empty() );
1098 }
1099 }
1100 }
1101
1102 TEST_CASE( "submap_field_load", "[submap][load]" )
1103 {
1104 submap sm;
1105 load_from_jsin( sm, submap_field );
1106 submap_checks checks;
1107 checks.fields = false;
1108
1109 REQUIRE( is_normal_submap( sm, checks ) );
1110
1111 const field &field_nw = sm.get_field( corner_nw );
1112 const field &field_ne = sm.get_field( corner_ne );
1113 const field &field_sw = sm.get_field( corner_sw );
1114 const field &field_se = sm.get_field( corner_se );
1115 const field &field_ra = sm.get_field( random_pt );
1116 const field_entry *fd_nw = field_nw.find_field( STATIC( field_type_id( "fd_web" ) ) );
1117 const field_entry *fd_ne = field_ne.find_field( STATIC( field_type_id( "fd_laser" ) ) );
1118 const field_entry *fd_sw = field_sw.find_field( STATIC( field_type_id( "fd_electricity" ) ) );
1119 const field_entry *fd_se = field_se.find_field( STATIC( field_type_id( "fd_acid" ) ) );
1120 const field_entry *fd_ra = field_ra.find_field( STATIC( field_type_id( "fd_nuke_gas" ) ) );
1121 const field_entry *fd_ow = field_nw.find_field( STATIC( field_type_id( "fd_smoke" ) ) );
1122 // No nullptrs for me
1123 REQUIRE( fd_nw != nullptr );
1124 REQUIRE( fd_ow != nullptr );
1125 REQUIRE( fd_ne != nullptr );
1126 REQUIRE( fd_sw != nullptr );
1127 REQUIRE( fd_se != nullptr );
1128 REQUIRE( fd_ra != nullptr );
1129
1130 // We placed a unique item in a couple of places. Check that those are correct
1131 INFO( string_format( "nw: %d %s %d %d %s %d %d", field_nw.field_count(),
1132 fd_nw->get_field_type().id().str(), fd_nw->get_field_intensity(),
1133 to_turns<int>( fd_nw->get_field_age() ), fd_ow->get_field_type().id().str(),
1134 fd_ow->get_field_intensity(),
1135 to_turns<int>( fd_ow->get_field_age() ) ) );
1136 INFO( string_format( "ne: %d %s %d %d", field_ne.field_count(),
1137 fd_ne->get_field_type().id().str(), fd_ne->get_field_intensity(),
1138 to_turns<int>( fd_ne->get_field_age() ) ) );
1139 INFO( string_format( "sw: %d %s %d %d", field_sw.field_count(),
1140 fd_sw->get_field_type().id().str(), fd_sw->get_field_intensity(),
1141 to_turns<int>( fd_sw->get_field_age() ) ) );
1142 INFO( string_format( "se: %d %s %d %d", field_se.field_count(),
1143 fd_se->get_field_type().id().str(), fd_se->get_field_intensity(),
1144 to_turns<int>( fd_se->get_field_age() ) ) );
1145 INFO( string_format( "ra: %d %s %d %d", field_ra.field_count(),
1146 fd_ra->get_field_type().id().str(), fd_ra->get_field_intensity(),
1147 to_turns<int>( fd_ra->get_field_age() ) ) );
1148 // Require to prevent the lower CHECK from being spammy
1149 REQUIRE( fd_nw->get_field_intensity() == 3 );
1150 REQUIRE( fd_nw->get_field_age() == 2077_seconds );
1151 REQUIRE( fd_ow->get_field_intensity() == 4 );
1152 REQUIRE( fd_ow->get_field_age() == 3004_seconds );
1153 REQUIRE( fd_ne->get_field_intensity() == 1 );
1154 REQUIRE( fd_ne->get_field_age() == 1997_seconds );
1155 REQUIRE( fd_sw->get_field_intensity() == 5 );
1156 REQUIRE( fd_sw->get_field_age() == 1482_seconds );
1157 REQUIRE( fd_se->get_field_intensity() == 2 );
1158 REQUIRE( fd_se->get_field_age() == 2003_seconds );
1159 REQUIRE( fd_ra->get_field_intensity() == 6 );
1160 REQUIRE( fd_ra->get_field_age() == 1615_seconds );
1161
1162 // Also, check we have no other fields
1163 for( int y = 0; y < SEEY; ++y ) {
1164 for( int x = 0; x < SEEX; ++x ) {
1165 point tested{ x, y };
1166 if( tested == corner_nw || tested == corner_ne || tested == corner_sw || tested == corner_se ||
1167 tested == random_pt ) {
1168 continue;
1169 }
1170 CHECK( sm.get_field( tested ).field_count() == 0 );
1171 }
1172 }
1173 }
1174
1175 TEST_CASE( "submap_graffiti_load", "[submap][load]" )
1176 {
1177 submap sm;
1178 load_from_jsin( sm, submap_graffiti );
1179 submap_checks checks;
1180 checks.cosmetics = false;
1181
1182 REQUIRE( is_normal_submap( sm, checks ) );
1183
1184 const std::string &g_nw = sm.get_graffiti( corner_nw );
1185 const std::string &g_ne = sm.get_graffiti( corner_ne );
1186 const std::string &g_sw = sm.get_graffiti( corner_sw );
1187 const std::string &g_se = sm.get_graffiti( corner_se );
1188 const std::string &g_ra = sm.get_graffiti( random_pt );
1189
1190 // We placed a unique graffiti in a couple of places. Check that those are correct
1191 INFO( string_format( "nw: %s", g_nw ) );
1192 INFO( string_format( "ne: %s", g_ne ) );
1193 INFO( string_format( "sw: %s", g_sw ) );
1194 INFO( string_format( "se: %s", g_se ) );
1195 INFO( string_format( "ra: %s", g_ra ) );
1196 // Require to prevent the lower CHECK from being spammy
1197 REQUIRE( g_nw == "c" );
1198 REQUIRE( g_ne == "a" );
1199 REQUIRE( g_sw == "d" );
1200 REQUIRE( g_se == "b" );
1201 REQUIRE( g_ra == "e" );
1202
1203 // Also, check we have no other graffiti
1204 REQUIRE( sm.cosmetics.size() == 5 );
1205 }
1206
1207 TEST_CASE( "submap_cosmetics_load", "[submap][load]" )
1208 {
1209 submap sm;
1210 load_from_jsin( sm, submap_cosmetic );
1211 submap_checks checks;
1212 checks.cosmetics = false;
1213 // Signs require furniture
1214 checks.furniture = false;
1215
1216 REQUIRE( is_normal_submap( sm, checks ) );
1217
1218 const std::string &g_nw = sm.get_graffiti( corner_nw );
1219 const std::string &g_ne = sm.get_graffiti( corner_ne );
1220 const std::string &g_sw = sm.get_signage( corner_sw );
1221 const std::string &g_se = sm.get_signage( corner_se );
1222 const std::string &g_ra = sm.get_graffiti( random_pt );
1223
1224 // We placed a unique graffiti in a couple of places. Check that those are correct
1225 INFO( string_format( "nw: %s", g_nw ) );
1226 INFO( string_format( "ne: %s", g_ne ) );
1227 INFO( string_format( "sw: %s", g_sw ) );
1228 INFO( string_format( "se: %s", g_se ) );
1229 INFO( string_format( "ra: %s", g_ra ) );
1230 // Require to prevent the lower CHECK from being spammy
1231 REQUIRE( g_nw == "I <3 Dr. Hylke van der Schaaf." );
1232 REQUIRE( g_ne == "This is written text." );
1233 REQUIRE( g_sw == "This is a sign" );
1234 REQUIRE( g_se == "Subway Map: illegible city name stop" );
1235 REQUIRE( g_ra == "Santina is a heteronormative bully!" );
1236
1237 // Also, check we have no other cosmetics
1238 REQUIRE( sm.cosmetics.size() == 5 );
1239 }
1240
1241 TEST_CASE( "submap_spawns_load", "[submap][load]" )
1242 {
1243 submap sm;
1244 load_from_jsin( sm, submap_spawns );
1245 submap_checks checks;
1246 checks.spawns = false;
1247
1248 REQUIRE( is_normal_submap( sm, checks ) );
1249
1250 const spawn_point &nw = *std::find_if( sm.spawns.begin(),
__anonea7b565c0102( const spawn_point & spawn ) 1251 sm.spawns.end(), []( const spawn_point & spawn ) {
1252 return spawn.pos == corner_nw;
1253 } );
1254 const spawn_point &ne = *std::find_if( sm.spawns.begin(),
__anonea7b565c0202( const spawn_point & spawn ) 1255 sm.spawns.end(), []( const spawn_point & spawn ) {
1256 return spawn.pos == corner_ne;
1257 } );
1258 const spawn_point &sw = *std::find_if( sm.spawns.begin(),
__anonea7b565c0302( const spawn_point & spawn ) 1259 sm.spawns.end(), []( const spawn_point & spawn ) {
1260 return spawn.pos == corner_sw;
1261 } );
1262 const spawn_point &se = *std::find_if( sm.spawns.begin(),
__anonea7b565c0402( const spawn_point & spawn ) 1263 sm.spawns.end(), []( const spawn_point & spawn ) {
1264 return spawn.pos == corner_se;
1265 } );
1266 const spawn_point &ra = *std::find_if( sm.spawns.begin(),
__anonea7b565c0502( const spawn_point & spawn ) 1267 sm.spawns.end(), []( const spawn_point & spawn ) {
1268 return spawn.pos == random_pt;
1269 } );
1270
1271 // We placed a unique spawn in a couple of places. Check that those are correct
1272 INFO( string_format( "nw: [%d, %d] %d %s %s %s", nw.pos.x, nw.pos.y, nw.count, nw.type.str(),
1273 nw.friendly ? "friendly" : "hostile", nw.name ) );
1274 INFO( string_format( "ne: [%d, %d] %d %s %s %s", ne.pos.x, ne.pos.y, ne.count, ne.type.str(),
1275 ne.friendly ? "friendly" : "hostile", ne.name ) );
1276 INFO( string_format( "sw: [%d, %d] %d %s %s %s", sw.pos.x, sw.pos.y, sw.count, sw.type.str(),
1277 sw.friendly ? "friendly" : "hostile", sw.name ) );
1278 INFO( string_format( "se: [%d, %d] %d %s %s %s", se.pos.x, se.pos.y, se.count, se.type.str(),
1279 se.friendly ? "friendly" : "hostile", se.name ) );
1280 INFO( string_format( "ra: [%d, %d] %d %s %s %s", ra.pos.x, ra.pos.y, ra.count, ra.type.str(),
1281 ra.friendly ? "friendly" : "hostile", ra.name ) );
1282 // Require to prevent the lower CHECK from being spammy
1283 CHECK( nw.count == 3 );
1284 CHECK( nw.type.str() == "mon_fish_eel" );
1285 CHECK( !nw.friendly );
1286 CHECK( nw.name == "Bob" );
1287 CHECK( ne.count == 1 );
1288 CHECK( ne.type.str() == "mon_cockatrice" );
1289 CHECK( !ne.friendly );
1290 CHECK( ne.name == "NONE" );
1291 CHECK( sw.count == 4 );
1292 CHECK( sw.type.str() == "mon_zombie_fungus" );
1293 CHECK( !sw.friendly );
1294 CHECK( sw.name == "Hopper" );
1295 CHECK( se.count == 2 );
1296 CHECK( se.type.str() == "mon_mininuke_hack" );
1297 CHECK( se.friendly );
1298 CHECK( se.name == "Tim" );
1299 CHECK( ra.count == 5 );
1300 CHECK( ra.type.str() == "mon_plague_vector" );
1301 CHECK( ra.friendly );
1302 CHECK( ra.name == "Alice" );
1303
1304 // Also, check we have no other spawns
1305 CHECK( sm.spawns.size() == 5 );
1306 }
1307
1308 TEST_CASE( "submap_vehicle_load", "[submap][load]" )
1309 {
1310 submap sm;
1311 load_from_jsin( sm, submap_vehicle );
1312 submap_checks checks;
1313 checks.vehicles = false;
1314
1315 REQUIRE( is_normal_submap( sm, checks ) );
1316
1317 REQUIRE( sm.vehicles.size() == 1 );
1318 // There's a lot we can test here, but that's getting into vehicle testing, not submap
1319 CHECK( sm.vehicles[0]->disp_name() == "the Welding Cart" );
1320 }
1321
1322 TEST_CASE( "submap_construction_load", "[submap][load]" )
1323 {
1324 submap sm;
1325 load_from_jsin( sm, submap_construction );
1326 submap_checks checks;
1327 checks.construction = false;
1328
1329 REQUIRE( is_normal_submap( sm, checks ) );
1330
1331 REQUIRE( sm.partial_constructions.size() == 2 );
1332 REQUIRE( sm.partial_constructions.find( { 3, 2, 0 } ) != sm.partial_constructions.end() );
1333 const partial_con &con1 = sm.partial_constructions[ {3, 2, 0}];
1334 CHECK( con1.counter == 123334 );
1335 CHECK( con1.components.size() == 1 );
1336 CHECK( con1.id.id() == construction_str_id( "constr_ground_cable" ) );
1337 REQUIRE( sm.partial_constructions.find( { 3, 3, 0 } ) != sm.partial_constructions.end() );
1338 const partial_con &con2 = sm.partial_constructions[ {3, 3, 0}];
1339 CHECK( con2.counter == 4934 );
1340 CHECK( con2.components.size() == 4 );
1341 CHECK( con2.id.id() == construction_str_id( "constr_rack_coat" ) );
1342 }
1343
1344 TEST_CASE( "submap_computer_load", "[submap][load]" )
1345 {
1346 submap sm;
1347 load_from_jsin( sm, submap_computer );
1348 submap_checks checks;
1349 checks.computers = false;
1350
1351 REQUIRE( is_normal_submap( sm, checks ) );
1352 // Just check there are computers in the right place
1353 // Checking more is complicated
1354 REQUIRE( sm.has_computer( point_south ) );
1355 REQUIRE( sm.has_computer( {3, 5} ) );
1356 }
1357