uint32_t pc::wear_in(uint32_t slot) { object *tmp; uint32_t i; if (!in[slot] || !in[slot]->is_equipable()) { return 1; } /* Rings are tricky since there are two slots. We will alwas favor * * an empty slot, and if there is no empty slot, we'll use the * * first slot. */ i = in[slot]->get_eq_slot_index(); if (eq[i] && ((eq[i]->get_type() == objtype_RING) && !eq[i + 1])) { i++; } tmp = in[slot]; in[slot] = eq[i]; eq[i] = tmp; io_queue_message("You wear %s.", eq[i]->get_name()); recalculate_speed(); return 0; }
uint32_t pc::pick_up(dungeon_t *d) { object *o; while (has_open_inventory_slot() && d->objmap[position[dim_y]][position[dim_x]]) { io_queue_message("You pick up %s.", d->objmap[position[dim_y]][position[dim_x]]->get_name()); in[get_first_open_inventory_slot()] = from_pile(d, position); } for (o = d->objmap[position[dim_y]][position[dim_x]]; o; o = o->get_next()) { io_queue_message("You have no room for %s.", o->get_name()); } return 0; }
uint32_t pc::remove_eq(uint32_t slot) { if (!eq[slot] || !in[slot]->is_removable() || !has_open_inventory_slot()) { io_queue_message("You can't remove %s, because you have nowhere to put it.", eq[slot]->get_name()); return 1; } io_queue_message("You remove %s.", eq[slot]->get_name()); in[get_first_open_inventory_slot()] = eq[slot]; eq[slot] = NULL; recalculate_speed(); return 0; }
void pc::DoBombCombat(dungeon_t *d, character* c) { int i, j; int damage = 100; int damageSplash = (damage / 2); int dmgUsed = 0; for(i = (character_get_y(c) - 1); i <= (character_get_y(c) + 1); i++) { for(j = (character_get_x(c) - 1); j <= (character_get_x(c) + 1); j++) { if(d->charmap[i][j] && d->charmap[i][j] != d->the_pc) { if(i == character_get_y(c) && j == character_get_x(c)) { dmgUsed = damage; } else { dmgUsed = damageSplash; } io_queue_message("You hit the %s for %d.", d->charmap[i][j]->name, dmgUsed); if (dmgUsed >= d->charmap[i][j]->hp) { io_queue_message("The %s dies.", d->charmap[i][j]->name); d->charmap[i][j]->hp = 0; d->charmap[i][j]->alive = 0; d->num_monsters--; charpair(d->charmap[i][j]->position) = NULL; } else { d->charmap[i][j]->hp -= dmgUsed; } } } } }
uint32_t pc::destroy_in(uint32_t slot) { if (!in[slot] || !in[slot]->is_destructable()) { return 1; } io_queue_message("You destroy %s.", in[slot]->get_name()); delete in[slot]; in[slot] = NULL; return 0; }
uint32_t pc::drop_in(dungeon_t *d, uint32_t slot) { if (!in[slot] || !in[slot]->is_dropable()) { return 1; } io_queue_message("You drop %s.", in[slot]->get_name()); in[slot]->to_pile(d, position); in[slot] = NULL; return 0; }
void pc::DoRangedCombat(dungeon_t *d, character* c) { int i; int damage = 0; int defenderDodge = c->dodge; int defenderDef = c->defence; int blocked = 0; float defMultipler; if(rand()%100 + 1 > defenderDodge) { for (i = 2; i < num_eq_slots; i++) { if (d->the_pc->eq[i]) { damage += d->the_pc->eq[i]->roll_dice(); } } blocked = damage; defMultipler = ((float)100 / (float)(100 + defenderDef)); damage = (damage * defMultipler); blocked = blocked - damage; io_queue_message("you hit the %s for %d. It blocked %d damage.", c->name, damage, blocked); if (damage >= c->hp) { c->hp = 0; c->alive = 0; d->num_monsters--; charpair(c->position) = NULL; io_queue_message("The %s dies.", c->name); } else { c->hp -= damage; } } else { io_queue_message("The %s dodged your ranged attack", c->name); } }
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; 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; }