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