1 /*
2  * game.c
3  * Copyright (C) 2009-2018 Joachim de Groot <jdegroot@web.de>
4  *
5  * NLarn is free software: you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * NLarn is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13  * See the GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #ifdef __linux__
20 # ifndef _GNU_SOURCE
21 #  define _GNU_SOURCE
22 # endif
23 #endif
24 
25 #include <errno.h>
26 #include <glib.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <zlib.h>
30 #include <glib/gstdio.h>
31 #include <unistd.h>
32 
33 #if (defined __unix) || (defined __unix__) || (defined __APPLE__)
34 # include <sys/file.h>
35 #endif
36 
37 #ifdef WIN32
38 # include <io.h>
39 # include <sys/locking.h>
40 #endif
41 
42 #include "cJSON.h"
43 #include "config.h"
44 #include "display.h"
45 #include "game.h"
46 #include "extdefs.h"
47 #include "player.h"
48 #include "spheres.h"
49 #include "random.h"
50 
51 static void game_new();
52 static gboolean game_load();
53 static void game_items_shuffle(game *g);
54 
55 /* file descriptor for locking the savegame file */
56 static int sgfd = 0;
57 
print_welcome_message(gboolean newgame)58 static void print_welcome_message(gboolean newgame)
59 {
60     log_add_entry(nlarn->log, "Welcome %sto NLarn %s!",
61                   newgame ? "" : "back ", nlarn_version);
62     log_add_entry(nlarn->log, "For a list of commands, press '?'.");
63 }
64 
try_locking_savegame_file(FILE * sg)65 static int try_locking_savegame_file(FILE *sg)
66 {
67     /*
68      * get a copy of the file descriptor for locking - gzclose would close
69      * it and thus unlock the file.
70      */
71     int fd = dup(fileno(sg));
72 
73     /* Try to obtain the lock on the save file to avoid reading it twice */
74 #if (defined __unix) || (defined __unix__) || (defined __APPLE__)
75     if (flock(fd, LOCK_EX | LOCK_NB) == -1)
76 #elif (defined(WIN32))
77     if (_locking(fd, LK_NBLCK, 0xffffffff) == -1)
78 #endif
79     {
80         /* could not obtain the lock */
81         GString *desc = g_string_new("NLarn cannot be started.\n\n"
82                                      "Could not lock the savegame file:\n");
83         g_string_append(desc, strerror(errno));
84         display_show_message("Error", desc->str, 0);
85         g_string_free(desc, TRUE);
86         display_shutdown();
87         exit(EXIT_FAILURE);
88     }
89 
90     return fd;
91 }
92 
game_init(struct game_config * config)93 void game_init(struct game_config *config)
94 {
95     /* allocate space for game structure */
96     nlarn = g_malloc0(sizeof(game));
97 
98     /* set autosave setting (default: TRUE) */
99     game_autosave(nlarn) = !config->no_autosave;
100 
101     if (!game_load())
102     {
103         /* set game parameters */
104         game_difficulty(nlarn) = config->difficulty;
105         game_wizardmode(nlarn) = config->wizard;
106 
107         /* restoring a save game failed - start a new game. */
108         game_new();
109 
110         /* put the player into the town */
111         player_map_enter(nlarn->p, game_map(nlarn, 0), FALSE);
112 
113         /* give player knowledge of the town */
114         scroll_mapping(nlarn->p, NULL);
115 
116         if (config->name && strlen(config->name) > 0)
117         {
118             nlarn->p->name = g_strdup(config->name);
119         }
120 
121         if (config->gender)
122         {
123             nlarn->p->sex = parse_gender(config->gender[0]);
124         }
125 
126         if (config->stats && strlen(config->stats) > 0)
127         {
128             config->stats[0] = g_ascii_tolower(config->stats[0]);
129             nlarn->player_stats_set = player_assign_bonus_stats(
130                     nlarn->p, config->stats[0]);
131         }
132 
133 
134         if (config->wizard)
135         {
136             log_add_entry(nlarn->log, "Wizard mode has been activated.");
137         }
138 
139     } /* end new game only settings */
140 
141     /* parse auto pick-up settings */
142     if (config->auto_pickup)
143     {
144         parse_autopickup_settings(config->auto_pickup,
145                 nlarn->p->settings.auto_pickup);
146     }
147 }
148 
game_destroy(game * g)149 game *game_destroy(game *g)
150 {
151     g_assert(g != NULL);
152 
153     /* everything must go */
154     for (int i = 0; i < MAP_MAX; i++)
155     {
156         if (g->maps[i] == NULL)
157         {
158             /* killed early during game initialisation */
159             g_free(g);
160             return NULL;
161         }
162         map_destroy(g->maps[i]);
163     }
164 
165     player_destroy(g->p);
166     log_destroy(g->log);
167 
168     if (g->store_stock)
169         inv_destroy(g->store_stock, FALSE);
170 
171     if (g->monastery_stock)
172         inv_destroy(g->monastery_stock, FALSE);
173 
174     g_hash_table_destroy(g->items);
175     g_hash_table_destroy(g->effects);
176     g_hash_table_destroy(g->monsters);
177     g_ptr_array_free(g->dead_monsters, TRUE);
178 
179     g_ptr_array_foreach(g->spheres, (GFunc)sphere_destroy, g);
180     g_ptr_array_free(g->spheres, TRUE);
181     g_free(g);
182 
183     return NULL;
184 }
185 
game_save(game * g)186 int game_save(game *g)
187 {
188     int err;
189     struct cJSON *save, *obj;
190     display_window *win = NULL;
191 
192     g_assert(g != NULL);
193 
194     /* if the display has been initialised, show a pop-up message */
195     if (display_available())
196         win = display_popup(2, 2, 0, NULL, "Saving....", 0);
197 
198     save = cJSON_CreateObject();
199 
200     cJSON_AddNumberToObject(save, "nlarn_version", g->version);
201     cJSON_AddNumberToObject(save, "time_start", g->time_start);
202     cJSON_AddNumberToObject(save, "gtime", g->gtime);
203     cJSON_AddNumberToObject(save, "difficulty", g->difficulty);
204     cJSON_AddItemToObject(save, "rng_state", rand_serialize());
205 
206     /* maps */
207     cJSON_AddItemToObject(save, "maps", obj = cJSON_CreateArray());
208     for (int idx = 0; idx < MAP_MAX; idx++)
209     {
210         cJSON_AddItemToArray(obj, map_serialize(g->maps[idx]));
211     }
212 
213     cJSON_AddItemToObject(save, "amulet_created",
214                           cJSON_CreateIntArray(g->amulet_created, AM_MAX));
215 
216     cJSON_AddItemToObject(save, "armour_created",
217                           cJSON_CreateIntArray(g->armour_created, AT_MAX));
218 
219     cJSON_AddItemToObject(save, "weapon_created",
220                           cJSON_CreateIntArray(g->weapon_created, WT_MAX));
221 
222     if (g->cure_dianthr_created) cJSON_AddTrueToObject(save, "cure_dianthr_created");
223 
224     cJSON_AddItemToObject(save, "amulet_material_mapping",
225                           cJSON_CreateIntArray(g->amulet_material_mapping, AM_MAX));
226 
227     cJSON_AddItemToObject(save, "potion_desc_mapping",
228                           cJSON_CreateIntArray(g->potion_desc_mapping, PO_MAX));
229 
230     cJSON_AddItemToObject(save, "ring_material_mapping",
231                           cJSON_CreateIntArray(g->ring_material_mapping, RT_MAX));
232 
233     cJSON_AddItemToObject(save, "scroll_desc_mapping",
234                           cJSON_CreateIntArray(g->scroll_desc_mapping, ST_MAX));
235 
236     cJSON_AddItemToObject(save, "book_desc_mapping",
237                           cJSON_CreateIntArray(g->book_desc_mapping, SP_MAX));
238 
239     cJSON_AddItemToObject(save, "monster_genocided",
240                           cJSON_CreateIntArray(g->monster_genocided, MT_MAX));
241 
242     if (g->wizard) cJSON_AddTrueToObject(save, "wizard");
243     if (g->fullvis) cJSON_AddTrueToObject(save, "fullvis");
244 
245     /* store stock */
246     if (inv_length(g->store_stock) > 0)
247     {
248         cJSON_AddItemToObject(save, "store_stock", inv_serialize(g->store_stock));
249     }
250 
251     /* monastery stock */
252     if (inv_length(g->monastery_stock) > 0)
253     {
254         cJSON_AddItemToObject(save, "monastery_stock", inv_serialize(g->monastery_stock));
255     }
256 
257     /* storage at player's home */
258     if (inv_length(g->player_home) > 0)
259     {
260         cJSON_AddItemToObject(save, "player_home", inv_serialize(g->player_home));
261     }
262 
263     /* log */
264     cJSON_AddItemToObject(save, "log", log_serialize(g->log));
265 
266     /* add player */
267     cJSON_AddItemToObject(save, "player",  player_serialize(g->p));
268 
269     /* add items */
270     cJSON_AddItemToObject(save, "items", obj = cJSON_CreateArray());
271     g_hash_table_foreach(g->items, item_serialize, obj);
272 
273     /* add effects */
274     cJSON_AddItemToObject(save, "effects", obj = cJSON_CreateArray());
275     g_hash_table_foreach(g->effects, (GHFunc)effect_serialize, obj);
276 
277     /* add monsters */
278     cJSON_AddItemToObject(save, "monsters", obj = cJSON_CreateArray());
279     g_hash_table_foreach(g->monsters, (GHFunc)monster_serialize, obj);
280 
281     /* add spheres */
282     if (g->spheres->len > 0)
283     {
284         cJSON_AddItemToObject(save, "spheres", obj = cJSON_CreateArray());
285         g_ptr_array_foreach(g->spheres, (GFunc)sphere_serialize, obj);
286     }
287 
288     /* print save game into a string */
289     char *sg = cJSON_Print(save);
290 
291     /* free memory claimed by JSON structures */
292     cJSON_Delete(save);
293 
294     /* open save file for writing */
295     FILE* fhandle;
296     if (sgfd)
297     {
298         /*
299          * File is already opened.
300          * We need to open a duplicate of the file descriptor as gzclose
301          * would close the file descriptor we keep to ensure the lock
302          * on the file is kept.
303          */
304         fhandle = fdopen(dup(sgfd), "w");
305         /* Position at beginning of file, otherwise zlib would append */
306         rewind(fhandle);
307     }
308     else
309     {
310         /* File need to be opened for the first time */
311         fhandle = fopen(nlarn_savefile, "wb");
312     }
313 
314     if (fhandle == NULL)
315     {
316         log_add_entry(g->log, "Error opening save file \"%s\".", nlarn_savefile);
317         free(sg);
318         return FALSE;
319     }
320 
321     if (!sgfd)
322     {
323         /* first time save, try locking the file */
324         sgfd = try_locking_savegame_file(fhandle);
325     }
326 
327     gzFile file = gzdopen(fileno(fhandle), "wb");
328     if (gzputs(file, sg) != (int)strlen(sg))
329     {
330         log_add_entry(g->log, "Error writing save file \"%s\": %s",
331                 nlarn_savefile, gzerror(file, &err));
332 
333         free(sg);
334         return FALSE;
335     }
336 
337     free(sg);
338     gzclose(file);
339 
340     /* if a pop-up message has been opened, destroy it here */
341     if (win != NULL)
342         display_window_destroy(win);
343 
344     return TRUE;
345 }
346 
game_map(game * g,guint nmap)347 map *game_map(game *g, guint nmap)
348 {
349     g_assert (g != NULL && nmap < MAP_MAX);
350 
351     return g->maps[nmap];
352 }
353 
game_spin_the_wheel(game * g)354 void game_spin_the_wheel(game *g)
355 {
356     map *amap;
357 
358     g_assert(g != NULL);
359 
360     /* add the player's speed to the player's movement points */
361     nlarn->p->movement += player_get_speed(nlarn->p);
362 
363     /* per-map actions */
364     for (int nmap = 0; nmap < MAP_MAX; nmap++)
365     {
366         amap = game_map(g, nmap);
367 
368         /* call map timers */
369         map_timer(amap);
370 
371         /* spawn some monsters every now and then */
372         if (g->gtime % (100 + nmap) == 0)
373         {
374             map_fill_with_life(amap);
375         }
376     }
377 
378     amap = game_map(nlarn, Z(g->p->pos));
379 
380     /* check if player is stuck inside a wall without walk through wall */
381     if ((map_tiletype_at(amap, g->p->pos) == LT_WALL)
382             && !player_effect(g->p, ET_WALL_WALK))
383     {
384         player_die(g->p, PD_STUCK, 0);
385     }
386 
387     /* check if the player is on a deep water tile without levitation */
388     if ((map_tiletype_at(amap, g->p->pos) == LT_DEEPWATER)
389             && !player_effect(g->p, ET_LEVITATION))
390     {
391         player_die(g->p, PD_DROWNED, 0);
392     }
393 
394     /* check if the player is on a lava tile without levitation */
395     if ((map_tiletype_at(amap, g->p->pos) == LT_LAVA)
396             && !player_effect(g->p, ET_LEVITATION))
397     {
398         player_die(g->p, PD_MELTED, 0);
399     }
400 
401     /* deal damage cause by map tiles to player */
402     damage *dam = map_tile_damage(amap, g->p->pos,
403                                   player_effect(g->p, ET_LEVITATION));
404 
405     if (dam != NULL)
406         player_damage_take(g->p, dam, PD_MAP, map_tiletype_at(amap, g->p->pos));
407 
408     /* move all monsters */
409     g_hash_table_foreach(g->monsters, (GHFunc)monster_move, g);
410 
411     /* destroy all monsters that have been killed during this turn */
412     game_remove_dead_monsters(g);
413 
414     /* move all spheres */
415     g_ptr_array_foreach(g->spheres, (GFunc)sphere_move, g);
416 
417     /* calculate bank interest */
418     building_bank_calc_interest(g);
419 
420     g->gtime++; /* count up the time  */
421     log_set_time(g->log, g->gtime); /* adjust time for log entries */
422 }
423 
game_remove_dead_monsters(game * g)424 void game_remove_dead_monsters(game *g)
425 {
426     g_assert (g != NULL);
427 
428     while (g->dead_monsters->len > 0)
429     {
430         g_ptr_array_remove_index(g->dead_monsters, g->dead_monsters->len - 1);
431     }
432 }
433 
game_item_register(game * g,item * it)434 gpointer game_item_register(game *g, item *it)
435 {
436     g_assert (g != NULL && it != NULL);
437 
438     gpointer nkey = GUINT_TO_POINTER(++g->item_max_id);
439     g_hash_table_insert(g->items, nkey, it);
440 
441     return nkey;
442 }
443 
game_item_unregister(game * g,gpointer it)444 void game_item_unregister(game *g, gpointer it)
445 {
446     g_assert (g != NULL && it != NULL);
447 
448     g_hash_table_remove(g->items, it);
449 }
450 
game_item_get(game * g,gpointer id)451 item *game_item_get(game *g, gpointer id)
452 {
453     g_assert(g != NULL && id != NULL);
454 
455     return (item *)g_hash_table_lookup(g->items, id);
456 }
457 
game_effect_register(game * g,effect * e)458 gpointer game_effect_register(game *g, effect *e)
459 {
460     g_assert (g != NULL && e != NULL);
461 
462     gpointer nkey = GUINT_TO_POINTER(++g->effect_max_id);
463     g_hash_table_insert(g->effects, nkey, e);
464 
465     return nkey;
466 }
467 
game_effect_unregister(game * g,gpointer e)468 void game_effect_unregister(game *g, gpointer e)
469 {
470     g_assert (g != NULL && e != NULL);
471 
472     g_hash_table_remove(g->effects, e);
473 }
474 
game_effect_get(game * g,gpointer id)475 effect *game_effect_get(game *g, gpointer id)
476 {
477     g_assert(g != NULL && id != NULL);
478     return (effect *)g_hash_table_lookup(g->effects, id);
479 }
480 
game_monster_register(game * g,monster * m)481 gpointer game_monster_register(game *g, monster *m)
482 {
483     g_assert (g != NULL && m != NULL);
484 
485     gpointer nkey = GUINT_TO_POINTER(++g->monster_max_id);
486     g_hash_table_insert(g->monsters, nkey, m);
487 
488     return nkey;
489 }
490 
game_monster_unregister(game * g,gpointer m)491 void game_monster_unregister(game *g, gpointer m)
492 {
493     g_assert (g != NULL && m != NULL);
494 
495     g_hash_table_remove(g->monsters, m);
496 }
497 
game_monster_get(game * g,gpointer id)498 monster *game_monster_get(game *g, gpointer id)
499 {
500     g_assert(g != NULL && id != NULL);
501     return (monster *)g_hash_table_lookup(g->monsters, id);
502 }
503 
game_new()504 static void game_new()
505 {
506     /* initialize object hashes (here as they will be needed by player_new) */
507     nlarn->items = g_hash_table_new(&g_direct_hash, &g_direct_equal);
508     nlarn->effects = g_hash_table_new(&g_direct_hash, &g_direct_equal);
509     nlarn->monsters = g_hash_table_new(&g_direct_hash, &g_direct_equal);
510 
511     /* initialize the array to store monsters that died during the turn */
512     nlarn->dead_monsters = g_ptr_array_new_with_free_func(
513             (GDestroyNotify)monster_destroy);
514 
515     nlarn->spheres = g_ptr_array_new();
516 
517     /* generate player */
518     nlarn->p = player_new();
519 
520     /* randomize unidentified item descriptions */
521     game_items_shuffle(nlarn);
522 
523     /* fill the store */
524     building_dndstore_init();
525 
526     /* initialize the monastery */
527     building_monastery_init();
528 
529     /* generate levels */
530     for (size_t idx = 0; idx < MAP_MAX; idx++)
531     {
532         /* if map_new fails, it returns NULL.
533            loop while no map has been generated */
534         do
535         {
536             nlarn->maps[idx] = map_new(idx, nlarn_mazefile);
537         }
538         while (nlarn->maps[idx] == NULL);
539     }
540 
541     /* game time handling */
542     nlarn->gtime = 1;
543     nlarn->time_start = time(NULL);
544     nlarn->version = SAVEFILE_VERSION;
545 
546     /* start a new diary */
547     nlarn->log = log_new();
548 
549     /* welcome message */
550     print_welcome_message(TRUE);
551 
552     log_set_time(nlarn->log, nlarn->gtime);
553 }
554 
game_load()555 static gboolean game_load()
556 {
557     int size;
558     cJSON *save, *obj;
559     display_window *win = NULL;
560 
561     /* size of the buffer we allocate to store the uncompressed file content */
562     const int bufsize = 1024 * 1024 * 3;
563 
564     /* try to open save file */
565     FILE* file = fopen(nlarn_savefile, "rb+");
566 
567     if (file == NULL)
568     {
569         /* failed to open save game file */
570         return FALSE;
571     }
572 
573     /*
574      * When not on Windows, lock the save file as long the process is
575      * alive. This ensures no two instances of the game can be started
576      * from the user's saved game.
577      */
578     sgfd = try_locking_savegame_file(file);
579 
580     /* open the file with zlib */
581     gzFile sg = gzdopen(fileno(file), "rb");
582 
583     /* if the display has been initialised, show a pop-up message */
584     if (display_available())
585         win = display_popup(2, 2, 0, NULL, "Loading....", 0);
586 
587     /* temporary buffer to store uncompressed save file content */
588     char *sgbuf = g_malloc0(bufsize);
589 
590     if (!gzread(sg, sgbuf, bufsize))
591     {
592         /* Reading the file failed. Terminate the game with an error message */
593         display_shutdown();
594         g_printerr("Failed to restore save file \"%s\".\n", nlarn_savefile);
595 
596         exit(EXIT_FAILURE);
597     }
598 
599     /* close save file */
600     gzclose(sg);
601 
602     /* parse save file */
603     save = cJSON_Parse(sgbuf);
604 
605     /* throw away the buffer */
606     g_free(sgbuf);
607 
608     /* check for save file incompatibility */
609     gboolean compatible_version = FALSE;
610     if (cJSON_GetObjectItem(save, "nlarn_version"))
611     {
612         nlarn->version = cJSON_GetObjectItem(save, "nlarn_version")->valueint;
613 
614         if (nlarn->version == SAVEFILE_VERSION)
615             compatible_version = TRUE;
616     }
617 
618     /* handle incompatible save file */
619     if (!compatible_version)
620     {
621         /* free the memory allocated by loading the save file */
622         cJSON_Delete(save);
623 
624         /* if a pop-up message has been opened, destroy it here */
625         if (win != NULL)
626             display_window_destroy(win);
627 
628         /* offer to delete the incompatible save game */
629         if (display_get_yesno("Saved game could not be loaded. "
630                     "Delete and start new game?", NULL, NULL, NULL))
631         {
632             /* delete save file */
633             g_unlink(nlarn_savefile);
634         }
635         else
636         {
637             display_shutdown();
638             g_printerr("Save file \"%s\" is not compatible to current version.\n",
639                     nlarn_savefile);
640 
641             exit(EXIT_FAILURE);
642         }
643 
644         return FALSE;
645     }
646 
647     /* restore saved game */
648     nlarn->time_start = cJSON_GetObjectItem(save, "time_start")->valueint;
649     nlarn->gtime = cJSON_GetObjectItem(save, "gtime")->valueint;
650     nlarn->difficulty = cJSON_GetObjectItem(save, "difficulty")->valueint;
651     rand_deserialize(cJSON_GetObjectItem(save, "rng_state"));
652 
653     if (cJSON_GetObjectItem(save, "wizard"))
654         nlarn->wizard = TRUE;
655 
656     if (cJSON_GetObjectItem(save, "fullvis"))
657         nlarn->fullvis = TRUE;
658 
659     obj = cJSON_GetObjectItem(save, "amulet_created");
660     size = cJSON_GetArraySize(obj);
661     g_assert(size == AM_MAX);
662     for (int idx = 0; idx < size; idx++)
663         nlarn->amulet_created[idx] = cJSON_GetArrayItem(obj, idx)->valueint;
664 
665     obj = cJSON_GetObjectItem(save, "armour_created");
666     size = cJSON_GetArraySize(obj);
667     g_assert(size == AT_MAX);
668     for (int idx = 0; idx < size; idx++)
669         nlarn->armour_created[idx] = cJSON_GetArrayItem(obj, idx)->valueint;
670 
671     obj = cJSON_GetObjectItem(save, "weapon_created");
672     size = cJSON_GetArraySize(obj);
673     g_assert(size == WT_MAX);
674     for (int idx = 0; idx < size; idx++)
675         nlarn->weapon_created[idx] = cJSON_GetArrayItem(obj, idx)->valueint;
676 
677     if (cJSON_GetObjectItem(save, "cure_dianthr_created"))
678         nlarn->cure_dianthr_created = TRUE;
679 
680 
681     obj = cJSON_GetObjectItem(save, "amulet_material_mapping");
682     size = cJSON_GetArraySize(obj);
683     g_assert(size == AM_MAX);
684     for (int idx = 0; idx < size; idx++)
685         nlarn->amulet_material_mapping[idx] = cJSON_GetArrayItem(obj, idx)->valueint;
686 
687     obj = cJSON_GetObjectItem(save, "potion_desc_mapping");
688     size = cJSON_GetArraySize(obj);
689     g_assert(size == PO_MAX);
690     for (int idx = 0; idx < size; idx++)
691         nlarn->potion_desc_mapping[idx] = cJSON_GetArrayItem(obj, idx)->valueint;
692 
693     obj = cJSON_GetObjectItem(save, "ring_material_mapping");
694     size = cJSON_GetArraySize(obj);
695     g_assert(size == RT_MAX);
696     for (int idx = 0; idx < size; idx++)
697         nlarn->ring_material_mapping[idx] = cJSON_GetArrayItem(obj, idx)->valueint;
698 
699     obj = cJSON_GetObjectItem(save, "scroll_desc_mapping");
700     size = cJSON_GetArraySize(obj);
701     g_assert(size == ST_MAX);
702     for (int idx = 0; idx < size; idx++)
703         nlarn->scroll_desc_mapping[idx] = cJSON_GetArrayItem(obj, idx)->valueint;
704 
705     obj = cJSON_GetObjectItem(save, "book_desc_mapping");
706     size = cJSON_GetArraySize(obj);
707     g_assert(size == SP_MAX);
708     for (int idx = 0; idx < size; idx++)
709         nlarn->book_desc_mapping[idx] = cJSON_GetArrayItem(obj, idx)->valueint;
710 
711     obj = cJSON_GetObjectItem(save, "monster_genocided");
712     size = cJSON_GetArraySize(obj);
713     g_assert(size == MT_MAX);
714     for (int idx = 0; idx < size; idx++)
715         nlarn->monster_genocided[idx] = cJSON_GetArrayItem(obj, idx)->valueint;
716 
717 
718     /* restore effects (have to come first) */
719     nlarn->effects = g_hash_table_new(&g_direct_hash, &g_direct_equal);
720     obj = cJSON_GetObjectItem(save, "effects");
721 
722     for (int idx = 0; idx < cJSON_GetArraySize(obj); idx++)
723         effect_deserialize(cJSON_GetArrayItem(obj, idx), nlarn);
724 
725 
726     /* restore items */
727     nlarn->items = g_hash_table_new(&g_direct_hash, &g_direct_equal);
728     obj = cJSON_GetObjectItem(save, "items");
729     for (int idx = 0; idx < cJSON_GetArraySize(obj); idx++)
730         item_deserialize(cJSON_GetArrayItem(obj, idx), nlarn);
731 
732 
733     /* restore maps */
734     obj = cJSON_GetObjectItem(save, "maps");
735     size = cJSON_GetArraySize(obj);
736     g_assert(size == MAP_MAX);
737     for (int idx = 0; idx < size; idx++)
738         nlarn->maps[idx] = map_deserialize(cJSON_GetArrayItem(obj, idx));
739 
740 
741     /* restore dnd store stock */
742     obj = cJSON_GetObjectItem(save, "store_stock");
743     if (obj != NULL) nlarn->store_stock = inv_deserialize(obj);
744 
745     /* restore monastery stock */
746     obj = cJSON_GetObjectItem(save, "monastery_stock");
747     if (obj != NULL) nlarn->monastery_stock = inv_deserialize(obj);
748 
749     /* restore storage of player's home */
750     obj = cJSON_GetObjectItem(save, "player_home");
751     if (obj != NULL) nlarn->player_home = inv_deserialize(obj);
752 
753     /* restore log */
754     nlarn->log = log_deserialize(cJSON_GetObjectItem(save, "log"));
755 
756 
757     /* restore player */
758     nlarn->p = player_deserialize(cJSON_GetObjectItem(save, "player"));
759 
760 
761     /* restore monsters */
762     nlarn->monsters = g_hash_table_new(&g_direct_hash, &g_direct_equal);
763     obj = cJSON_GetObjectItem(save, "monsters");
764 
765     for (int idx = 0; idx < cJSON_GetArraySize(obj); idx++)
766         monster_deserialize(cJSON_GetArrayItem(obj, idx), nlarn);
767 
768     /* initialize the array to store monsters that died during the turn */
769     nlarn->dead_monsters = g_ptr_array_new_with_free_func(
770             (GDestroyNotify)monster_destroy);
771 
772 
773     /* restore spheres */
774     nlarn->spheres = g_ptr_array_new();
775 
776     if ((obj = cJSON_GetObjectItem(save, "spheres")))
777     {
778         for (int idx = 0; idx < cJSON_GetArraySize(obj); idx++)
779             sphere_deserialize(cJSON_GetArrayItem(obj, idx), nlarn);
780     }
781 
782     /* free parsed save game */
783     cJSON_Delete(save);
784 
785     /* set log turn number to current game turn number */
786     log_set_time(nlarn->log, nlarn->gtime);
787 
788     /* welcome message */
789     print_welcome_message(FALSE);
790 
791     /* refresh FOV */
792     player_update_fov(nlarn->p);
793 
794     /* no need to define the player's stats */
795     nlarn->player_stats_set = TRUE;
796 
797     /* if a pop-up message has been opened, destroy it here */
798     if (win != NULL)
799         display_window_destroy(win);
800 
801     return TRUE;
802 }
803 
game_items_shuffle(game * g)804 static void game_items_shuffle(game *g)
805 {
806     shuffle(g->amulet_material_mapping, AM_MAX, 0);
807     shuffle(g->potion_desc_mapping, PO_MAX, 1);
808     shuffle(g->ring_material_mapping, RT_MAX, 0);
809     shuffle(g->scroll_desc_mapping, ST_MAX, 1);
810     shuffle(g->book_desc_mapping, SP_MAX, 0);
811 }
812 
game_delete_savefile()813 void game_delete_savefile()
814 {
815     if (sgfd == 0)
816     {
817         /* no savegame present */
818         return;
819     }
820 
821     /* close and thus unlock the savegame file descriptor;
822      * Windows won't let us delete the file otherwise */
823     close(sgfd);
824     sgfd = 0;
825 
826     /* actually delete the file */
827     g_unlink(nlarn_savefile);
828 }
829