static void io_list_monsters(dungeon_t *d) { character **c; uint32_t x, y, count; c = (character **) malloc(d->num_monsters * sizeof (*c)); /* Get a linear list of monsters */ for (count = 0, y = 1; y < DUNGEON_Y - 1; y++) { for (x = 1; x < DUNGEON_X - 1; x++) { if (d->charmap[y][x] && d->charmap[y][x] != d->pc) { c[count++] = d->charmap[y][x]; } } } /* Sort it by distance from PC */ dungeon = d; qsort(c, count, sizeof (*c), compare_monster_distance); /* Display it */ io_list_monsters_display(d, c, count); free(c); /* And redraw the dungeon */ io_display(d); }
void io_handle_input(dungeon_t *d) { uint32_t fail_code; int key; do { switch (key = getch()) { case '7': case 'y': case KEY_HOME: fail_code = move_pc(d, 7); break; case '8': case 'k': case KEY_UP: fail_code = move_pc(d, 8); break; case '9': case 'u': case KEY_PPAGE: fail_code = move_pc(d, 9); break; case '6': case 'l': case KEY_RIGHT: fail_code = move_pc(d, 6); break; case '3': case 'n': case KEY_NPAGE: fail_code = move_pc(d, 3); break; case '2': case 'j': case KEY_DOWN: fail_code = move_pc(d, 2); break; case '1': case 'b': case KEY_END: fail_code = move_pc(d, 1); break; case '4': case 'h': case KEY_LEFT: fail_code = move_pc(d, 4); break; case '5': case ' ': case KEY_B2: fail_code = 0; break; case '>': fail_code = move_pc(d, '>'); break; case '<': fail_code = move_pc(d, '<'); break; case 'S': d->save_and_exit = 1; character_reset_turn(d->pc); fail_code = 0; break; case 'Q': /* Extra command, not in the spec. Quit without saving. */ d->quit_no_save = 1; fail_code = 0; break; case 'T': /* New command. Display the distances for tunnelers. */ io_display_tunnel(d); fail_code = 1; break; case 'D': /* New command. Display the distances for non-tunnelers. */ io_display_distance(d); fail_code = 1; break; case 'H': /* New command. Display the hardnesses. */ io_display_hardness(d); fail_code = 1; break; case 's': /* New command. Return to normal display after displaying some * * special screen. */ io_display(d); fail_code = 1; break; case 'g': /* Teleport the PC to a random place in the dungeon. */ io_teleport_pc(d); fail_code = 0; break; case 'm': io_list_monsters(d); fail_code = 1; break; case 'a': io_display_all(d); fail_code = 1; break; case 'q': /* Demonstrate use of the message queue. You can use this for * * printf()-style debugging (though gdb is probably a better * * option. Not that it matterrs, but using this command will * * waste a turn. Set fail_code to 1 and you should be able to * * figure out why I did it that way. */ io_queue_message("This is the first message."); io_queue_message("Since there are multiple messages, " "you will see \"more\" prompts."); io_queue_message("You can use any key to advance through messages."); io_queue_message("Normal gameplay will not resume until the queue " "is empty."); io_queue_message("Long lines will be truncated, not wrapped."); io_queue_message("io_queue_message() is variadic and handles " "all printf() conversion specifiers."); io_queue_message("Did you see %s?", "what I did there"); io_queue_message("When the last message is displayed, there will " "be no \"more\" prompt."); io_queue_message("Have fun! And happy printing!"); fail_code = 0; break; default: /* Also not in the spec. It's not always easy to figure out what * * key code corresponds with a given keystroke. Print out any * * unhandled key here. Not only does it give a visual error * * indicator, but it also gives an integer value that can be used * * for that key in this (or other) switch statements. Printed in * * octal, with the leading zero, because ncurses.h lists codes in * * octal, thus allowing us to do reverse lookups. If a key has a * * name defined in the header, you can use the name here, else * * you can directly use the octal value. */ mvprintw(0, 0, "Unbound key: %#o ", key); fail_code = 1; } } while (fail_code); }
int main(int argc, char *argv[]) { dungeon_t d; time_t seed; struct timeval tv; uint32_t i; uint32_t do_load, do_save, do_seed, do_image; uint32_t long_arg; char *save_file; char *pgm_file; memset(&d, 0, sizeof (d)); /* Default behavior: Seed with the time, generate a new dungeon, * * and don't write to disk. */ do_load = do_save = do_image = 0; do_seed = 1; save_file = NULL; d.max_monsters = 10; /* The project spec requires '--load' and '--save'. It's common * * to have short and long forms of most switches (assuming you * * don't run out of letters). For now, we've got plenty. Long * * forms use whole words and take two dashes. Short forms use an * ` * abbreviation after a single dash. We'll add '--rand' (to * * specify a random seed), which will take an argument of it's * * own, and we'll add short forms for all three commands, '-l', * * '-s', and '-r', respectively. We're also going to allow an * * optional argument to load to allow us to load non-default save * * files. No means to save to non-default locations, however. * * And the final switch, '--image', allows me to create a dungeon * * from a PGM image, so that I was able to create those more * * interesting test dungeons for you. */ if (argc > 1) { for (i = 1, long_arg = 0; i < argc; i++, long_arg = 0) { if (argv[i][0] == '-') { /* All switches start with a dash */ if (argv[i][1] == '-') { argv[i]++; /* Make the argument have a single dash so we can */ long_arg = 1; /* handle long and short args at the same place. */ } switch (argv[i][1]) { case 'r': if ((!long_arg && argv[i][2]) || (long_arg && strcmp(argv[i], "-rand")) || argc < ++i + 1 /* No more arguments */ || !sscanf(argv[i], "%lu", &seed) /* Argument is not an integer */) { usage(argv[0]); } do_seed = 0; break; case 'l': if ((!long_arg && argv[i][2]) || (long_arg && strcmp(argv[i], "-load"))) { usage(argv[0]); } do_load = 1; if ((argc > i + 1) && argv[i + 1][0] != '-') { /* There is another argument, and it's not a switch, so * * we'll treat it as a save file and try to load it. */ save_file = argv[++i]; } break; case 's': if ((!long_arg && argv[i][2]) || (long_arg && strcmp(argv[i], "-save"))) { usage(argv[0]); } do_save = 1; break; case 'i': if ((!long_arg && argv[i][2]) || (long_arg && strcmp(argv[i], "-image"))) { usage(argv[0]); } do_image = 1; if ((argc > i + 1) && argv[i + 1][0] != '-') { /* There is another argument, and it's not a switch, so * * we'll treat it as a save file and try to load it. */ pgm_file = argv[++i]; } break; case 'n': if ((!long_arg && argv[i][2]) || (long_arg && strcmp(argv[i], "-nummon")) || argc < ++i + 1 /* No more arguments */ || !sscanf(argv[i], "%hu", &d.max_monsters)) { usage(argv[0]); } break; default: usage(argv[0]); } } else { /* No dash */ usage(argv[0]); } } } if (do_seed) { /* Allows me to generate more than one dungeon * * per second, as opposed to time(). */ gettimeofday(&tv, NULL); seed = (tv.tv_usec ^ (tv.tv_sec << 20)) & 0xffffffff; } printf("Seed is %ld.\n", seed); srand(seed); io_init_terminal(); init_dungeon(&d); if (do_load) { read_dungeon(&d, save_file); } else if (do_image) { read_pgm(&d, pgm_file); } else { gen_dungeon(&d); } config_pc(&d); gen_monsters(&d, d.max_monsters, 0); io_display(&d); while (pc_is_alive(&d) && dungeon_has_npcs(&d) && !d.save_and_exit) { do_moves(&d); if (!pc_is_alive(&d)) { break; } } io_display(&d); if (!d.save_and_exit) { sleep(2); } io_reset_terminal(); if (do_save) { write_dungeon(&d); } printf(pc_is_alive(&d) ? victory : tombstone); /* PC can't be deleted with the dungeon, else * * it disappears when we use the stairs. */ if (pc_is_alive(&d)) { /* If the PC is dead, it's in the move heap and will get automatically * * deleted when the heap destructs. In that case, we can't call * * delete_pc(), because it will lead to a double delete. */ delete_pc(d.pc); } delete_dungeon(&d); return 0; }
int main(int argc, char *argv[]) { dungeon_t d; time_t seed; uint32_t nummon; struct timeval tv; uint32_t i; uint32_t do_seed; uint32_t long_arg; uint32_t do_huge; memset(&d, 0, sizeof (d)); /* Default behavior: Seed with the time, generate a new dungeon, * * and don't write to disk. */ do_seed = 1; do_huge = 0; nummon = 10; /* The project spec requires '--load' and '--store'. It's common * * to have short and long forms of most switches (assuming you * * don't run out of letters). For now, we've got plenty. Long * * forms use whole words and take two dashes. Short forms use an * * abbreviation after a single dash. We'll add '--rand' (to * * specify a random seed), which will take an argument of its * * own, and we'll add short forms for all three commands, '-l', * * '-s', and '-r', respectively. We're also going to allow an * * optional argument to load to allow us to load non-default save * * files. No means to store to non-default locations, however. */ if (argc > 1) { for (i = 1, long_arg = 0; i < argc; i++, long_arg = 0) { if (argv[i][0] == '-') { /* All switches start with a dash */ if (argv[i][1] == '-') { argv[i]++; /* Make the argument have a single dash so we can */ long_arg = 1; /* handle long and short args at the same place. */ } switch (argv[i][1]) { case 'r': if ((!long_arg && argv[i][2]) || (long_arg && strcmp(argv[i], "-rand")) || argc < ++i + 1 /* No more arguments */ || !sscanf(argv[i], "%lu", &seed) /* Argument is not an integer */) { usage(argv[0]); } do_seed = 0; break; case 'n': if ((!long_arg && argv[i][2]) || (long_arg && strcmp(argv[i], "-nummon")) || argc < ++i + 1 /* No more arguments */ || !sscanf(argv[i], "%u", &nummon) /* Argument is not an int */) { usage(argv[0]); } break; case 'h': if ((!long_arg && argv[i][2]) || (long_arg && strcmp(argv[i], "-huge"))) { usage(argv[0]); } do_huge = 1; break; default: usage(argv[0]); } } else { /* No dash */ usage(argv[0]); } } } if (do_seed) { /* Allows me to generate more than one dungeon * * per second, as opposed to time(). */ gettimeofday(&tv, NULL); seed = (tv.tv_usec ^ (tv.tv_sec << 20)) & 0xffffffff; } if (do_huge) { d.render_whole_dungeon = 1; } else { d.render_whole_dungeon = 0; } printf("Seed is %ld.\n", seed); srand(seed); parse_descriptions(&d); io_init_terminal(); init_dungeon(&d); if (read_dungeon(&d)) { gen_dungeon(&d); config_pc(&d); gen_monsters(&d, nummon, 0); gen_objects(&d, 10); } io_display(&d); while (pc_is_alive(&d) && dungeon_has_npcs(&d) && !(d.save_and_exit || d.quit_no_save)) { do_moves(&d); io_display(&d); if (!pc_is_alive(&d)) { break; } io_handle_input(&d); } io_display(&d); io_reset_terminal(); if (!(d.save_and_exit || d.quit_no_save)) { sleep(2); if (pc_is_alive(&d)) { printf("%s says, \"%s\"\n", d.pc.pc->name, d.pc.pc->catch_phrase); } else { printf("The monster hordes growl menacingly.\n"); } unlink_dungeon(); } else if (d.save_and_exit) { write_dungeon(&d); } else /* d.quit_no_save */ { unlink_dungeon(); } pc_delete(d.pc.pc); destroy_descriptions(&d); delete_dungeon(&d); return 0; }
int main(int argc, char *argv[]) { dungeon_t d; time_t seed; struct timeval tv; int32_t i; uint32_t do_load, do_save, do_seed, do_image, do_save_seed, do_save_image, do_place_pc; uint32_t long_arg; char *save_file; char *load_file; char *pgm_file; memset(&d, 0, sizeof (d)); /* Default behavior: Seed with the time, generate a new dungeon, * * and don't write to disk. */ do_load = do_save = do_image = do_save_seed = do_save_image = do_place_pc = 0; do_seed = 1; save_file = load_file = NULL; d.max_monsters = MAX_MONSTERS; d.max_objects = MAX_OBJECTS; /* The project spec requires '--load' and '--save'. It's common * * to have short and long forms of most switches (assuming you * * don't run out of letters). For now, we've got plenty. Long * * forms use whole words and take two dashes. Short forms use an * * abbreviation after a single dash. We'll add '--rand' (to * * specify a random seed), which will take an argument of it's * * own, and we'll add short forms for all three commands, '-l', * * '-s', and '-r', respectively. We're also going to allow an * * optional argument to load to allow us to load non-default save * * files. No means to save to non-default locations, however. * * And the final switch, '--image', allows me to create a dungeon * * from a PGM image, so that I was able to create those more * * interesting test dungeons for you. */ if (argc > 1) { for (i = 1, long_arg = 0; i < argc; i++, long_arg = 0) { if (argv[i][0] == '-') { /* All switches start with a dash */ if (argv[i][1] == '-') { argv[i]++; /* Make the argument have a single dash so we can */ long_arg = 1; /* handle long and short args at the same place. */ } switch (argv[i][1]) { case 'r': if ((!long_arg && argv[i][2]) || (long_arg && strcmp(argv[i], "-rand")) || argc < ++i + 1 /* No more arguments */ || !sscanf(argv[i], "%lu", &seed) /* Argument is not an integer */) { usage(argv[0]); } do_seed = 0; break; case 'l': if ((!long_arg && argv[i][2]) || (long_arg && strcmp(argv[i], "-load"))) { usage(argv[0]); } do_load = 1; if ((argc > i + 1) && argv[i + 1][0] != '-') { /* There is another argument, and it's not a switch, so * * we'll treat it as a save file and try to load it. */ load_file = argv[++i]; } break; case 's': if ((!long_arg && argv[i][2]) || (long_arg && strcmp(argv[i], "-save"))) { usage(argv[0]); } do_save = 1; if ((argc > i + 1) && argv[i + 1][0] != '-') { /* There is another argument, and it's not a switch, so * * we'll save to it. If it is "seed", we'll save to * * <the current seed>.rlg327. If it is "image", we'll * * save to <the current image>.rlg327. */ if (!strcmp(argv[++i], "seed")) { do_save_seed = 1; do_save_image = 0; } else if (!strcmp(argv[i], "image")) { do_save_image = 1; do_save_seed = 0; } else { save_file = argv[i]; } } break; case 'i': if ((!long_arg && argv[i][2]) || (long_arg && strcmp(argv[i], "-image"))) { usage(argv[0]); } do_image = 1; if ((argc > i + 1) && argv[i + 1][0] != '-') { /* There is another argument, and it's not a switch, so * * we'll treat it as a save file and try to load it. */ pgm_file = argv[++i]; } break; case 'n': if ((!long_arg && argv[i][2]) || (long_arg && strcmp(argv[i], "-nummon")) || argc < ++i + 1 /* No more arguments */ || !sscanf(argv[i], "%hu", &d.max_monsters)) { usage(argv[0]); } break; case 'p': /* PC placement makes no effort to avoid placing * * the PC inside solid rock. */ if ((!long_arg && argv[i][2]) || (long_arg && strcmp(argv[i], "-pc"))) { usage(argv[0]); } if ((d.PC->position[dim_y] = atoi(argv[++i])) < 1 || d.PC->position[dim_y] > DUNGEON_Y - 2 || (d.PC->position[dim_x] = atoi(argv[++i])) < 1 || d.PC->position[dim_x] > DUNGEON_X - 2) { fprintf(stderr, "Invalid PC position.\n"); usage(argv[0]); } do_place_pc = 1; break; case 'o': if ((!long_arg && argv[i][2]) || (long_arg && strcmp(argv[i], "-objcount")) || argc < ++i + 1 /* No more arguments */ || !sscanf(argv[i], "%hu", &d.max_objects)) { usage(argv[0]); } break; default: usage(argv[0]); } } else { /* No dash */ usage(argv[0]); } } } if (do_seed) { /* Allows me to generate more than one dungeon * * per second, as opposed to time(). */ gettimeofday(&tv, NULL); seed = (tv.tv_usec ^ (tv.tv_sec << 20)) & 0xffffffff; } srand(seed); parse_descriptions(&d); io_init_terminal(); init_dungeon(&d); if (do_load) { read_dungeon(&d, load_file); } else if (do_image) { read_pgm(&d, pgm_file); } else { gen_dungeon(&d); } config_pc(&d); gen_monsters(&d); gen_objects(&d); pc_observe_terrain(d.PC, &d); io_display(&d); io_queue_message("Seed is %u.", seed); while (pc_is_alive(&d) && dungeon_has_npcs(&d) && !d.quit) { do_moves(&d); } io_display(&d); io_reset_terminal(); if (do_save) { if (do_save_seed) { /* 10 bytes for number, dot, extention and null terminator. */ save_file = (char *) malloc(18); sprintf(save_file, "%ld.rlg327", seed); } if (do_save_image) { if (!pgm_file) { fprintf(stderr, "No image file was loaded. Using default.\n"); do_save_image = 0; } else { /* Extension of 3 characters longer than image extension + null. */ save_file = (char *) malloc(strlen(pgm_file) + 4); strcpy(save_file, pgm_file); strcpy(strchr(save_file, '.') + 1, "rlg327"); } } write_dungeon(&d, save_file); if (do_save_seed || do_save_image) { free(save_file); } } printf("%s", pc_is_alive(&d) ? victory : tombstone); printf("You defended your life in the face of %u deadly beasts.\n" "You avenged the cruel and untimely murders of %u " "peaceful dungeon residents.\n", d.PC->kills[kill_direct], d.PC->kills[kill_avenged]); if (pc_is_alive(&d)) { /* If the PC is dead, it's in the move heap and will get automatically * * deleted when the heap destructs. In that case, we can't call * * delete_pc(), because it will lead to a double delete. */ character_delete(d.PC); } delete_dungeon(&d); destroy_descriptions(&d); return 0; }