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