void curses_impossible(const char *msg) { curses_msgwin("Impossible state detected in the client.", krc_notification); curses_msgwin(msg, krc_notification); curses_msgwin("You may wish to save and restart the client.", krc_notification); }
static void validate_character_presets(const struct nh_roles_info *ri, int *initrole, int *initrace, int *initgend, int *initalign) { struct nh_listitem list[1]; char listbuffer[256]; list[0].caption = listbuffer; /* throw out obviously invalid values */ if (*initrole < ROLE_RANDOM || *initrole >= ri->num_roles) *initrole = ROLE_NONE; if (*initrace < ROLE_RANDOM || *initrace >= ri->num_races) *initrace = ROLE_NONE; if (*initgend < ROLE_RANDOM || *initgend >= ri->num_genders) *initgend = ROLE_NONE; if (*initalign < ROLE_RANDOM || *initalign >= ri->num_aligns) *initalign = ROLE_NONE; /* catch mutually incompatible character options (male valkyrie) */ if (!is_valid_character(ri, *initrole, *initrace, *initgend, *initalign)) { /* always keep the role */ if (is_valid_character(ri, *initrole, *initrace, *initgend, ROLE_NONE)) { curses_msgwin("Incompatible alignment!"); *initalign = ROLE_NONE; } else if (is_valid_character(ri, *initrole, *initrace, ROLE_NONE, *initalign)) { curses_msgwin("Incompatible gender!"); *initgend = ROLE_NONE; } else if (is_valid_character(ri, *initrole, ROLE_NONE, *initgend, *initalign)) { curses_msgwin("Incompatible race!"); *initrace = ROLE_NONE; } else { curses_msgwin("Incompatible character presets!"); *initrace = ROLE_NONE; *initgend = ROLE_NONE; *initalign = ROLE_NONE; } } /* if there is only one possible option for any attribute, choose it here */ if ((*initrole == ROLE_NONE || *initrole == ROLE_RANDOM) && get_valid_roles(ri, *initrace, *initgend, *initalign, list, 1) == 1) *initrole = list[0].id; if ((*initrace == ROLE_NONE || *initrace == ROLE_RANDOM) && get_valid_races(ri, *initrole, *initgend, *initalign, list, 1) == 1) *initrace = list[0].id; if ((*initgend == ROLE_NONE || *initgend == ROLE_RANDOM) && get_valid_genders(ri, *initrole, *initrace, *initalign, list, 1) == 1) *initgend = list[0].id; if ((*initalign == ROLE_NONE || *initalign == ROLE_RANDOM) && get_valid_aligns(ri, *initrole, *initrace, *initgend, list, 1) == 1) *initalign = list[0].id; }
enum nh_direction curses_getdir(const char *query, nh_bool restricted) { int key; enum nh_direction dir; key = curses_msgwin(query ? query : "In what direction?"); if (key == '.' || key == 's') return DIR_SELF; dir = key_to_dir(key); if (dir == DIR_NONE) curses_msgwin("What a strange direction!"); return dir; }
void net_loadgame(void) { char buf[BUFSZ]; struct nhnet_game *gamelist; struct nh_menulist menu; int id, size, i, ret, pick[1]; gamelist = nhnet_list_games(FALSE, FALSE, &size); if (!gamelist) { curses_msgwin("Failed to retrieve the list of saved games.", krc_notification); return; } if (!size) { curses_msgwin("No saved games found.", krc_notification); return; } init_menulist(&menu); for (i = 0; i < size; i++) { if (gamelist[i].status == LS_DONE || gamelist[i].status == LS_INVALID) continue; describe_game(buf, gamelist[i].status, &gamelist[i].i); id = (gamelist[i].status == LS_IN_PROGRESS) ? 0 : gamelist[i].gameid; add_menu_item(&menu, id, buf, 0, FALSE); } curses_display_menu(&menu, "saved games", PICK_ONE, PLHINT_ANYWHERE, pick, curses_menu_callback); if (*pick == CURSES_MENU_CANCELLED) return; id = pick[0]; create_game_windows(); ret = playgame(id, FM_PLAY); destroy_game_windows(); cleanup_messages(); game_ended(ret, NULL, TRUE); }
static void rule_pattern_callback(const char *str, void *pat_void) { char *pat = pat_void; if (*str == '\033') return; if (strlen(str) >= AUTOPICKUP_PATTERNSZ) { curses_msgwin("That pattern is too long.", krc_notification); return; } memset(pat, 0, AUTOPICKUP_PATTERNSZ); strcpy(pat, str); }
/* pos holds maximum position on call, chosen position or -1 on return */ static void rule_position_callback(const char *str, void *pos_void) { int *pos = pos_void; long l; if (!*str || *str == '\033') { *pos = -1; return; } l = strtol(str, NULL, 0); if (l >= 1 && l <= *pos) *pos = (int)l; else { curses_msgwin("Invalid rule position.", krc_notification); *pos = -1; } }
/* open a config file and separate it into lines for read_config_line() */ static void read_config_file(const fnchar * filename) { FILE *fp; int fsize; char *line; fp = fopen(filename, "rb"); if (!fp) return; /* obtain file size. */ fseek(fp, 0, SEEK_END); fsize = ftell(fp); rewind(fp); if (!fsize) { /* truncated config file */ fclose(fp); return; } char buf[fsize + 1]; if (fread(buf, 1, fsize, fp) < fsize) { /* This can only happen if the file shrinks while it's open; let's just not read the file at all, because it's probably corrupted */ curses_msgwin("warning: corrupted configuration file", krc_notification); fclose(fp); return; } fclose(fp); buf[fsize] = '\0'; /* each option is expected to have the following format: * name=value\n */ line = strtok(buf, "\n"); do { read_config_line(line); line = strtok(NULL, "\n"); } while (line); }
int main(int argc, char *argv[]) { srand(time(NULL)); char **gamepaths; int i; nh_bool init_ok; umask(0777 & ~FCMASK); /* this can change argc and *argv, so must come first */ initialize_uncursed(&argc, argv); process_args(argc, argv); /* grab -U, -H, -k, --help early */ init_options(); gamepaths = init_game_paths(argv[0]); nh_lib_init(&curses_windowprocs, (const char * const*)gamepaths); init_curses_ui(gamepaths[DATAPREFIX]); init_ok = read_nh_config(); for (i = 0; i < PREFIX_COUNT; i++) free(gamepaths[i]); free(gamepaths); process_args(argc, argv); /* other command line options */ init_displaychars(); if (init_ok) mainmenu(); else curses_msgwin("Could not initialize game options!", krc_notification); exit_curses_ui(); nh_lib_exit(); free_displaychars(); exit(EXIT_SUCCESS); /*NOTREACHED*/ return 0; }
void replay(void) { char buf[BUFSZ]; fnchar logdir[BUFSZ], savedir[BUFSZ], filename[1024], *dir, **files; struct nh_menuitem *items; int i, n, fd, icount, size, filecount, pick[1]; enum nh_log_status status; struct nh_game_info gi; if (!get_gamedir(LOG_DIR, logdir)) logdir[0] = '\0'; if (!get_gamedir(SAVE_DIR, savedir)) savedir[0] = '\0'; if (*logdir) dir = logdir; else if (*savedir) dir = savedir; else { curses_msgwin("There are no games to replay."); return; } while (1) { filename[0] = '\0'; files = list_gamefiles(dir, &filecount); /* make sure there are some files to show */ if (!filecount) { if (dir == savedir) { curses_msgwin("There are no saved games to replay."); savedir[0] = '\0'; } else { curses_msgwin("There are no completed games to replay."); logdir[0] = '\0'; } dir = (dir == savedir) ? logdir : savedir; if (!*dir) return; continue; } icount = 0; size = filecount + 2; items = malloc(size * sizeof (struct nh_menuitem)); /* add all the files to the menu */ for (i = 0; i < filecount; i++) { fd = sys_open(files[i], O_RDWR, 0660); status = nh_get_savegame_status(fd, &gi); close(fd); describe_game(buf, status, &gi); add_menu_item(items, size, icount, (status == LS_IN_PROGRESS) ? 0 : icount + 1, buf, 0, FALSE); } if (dir == logdir && *savedir) { add_menu_txt(items, size, icount, "", MI_NORMAL); add_menu_item(items, size, icount, -1, "View saved games instead", '!', FALSE); } else if (dir == savedir && *logdir) { add_menu_txt(items, size, icount, "", MI_NORMAL); add_menu_item(items, size, icount, -1, "View saved games instead", '!', FALSE); } n = curses_display_menu(items, icount, "Pick a game to view", PICK_ONE, PLHINT_ANYWHERE, pick); free(items); filename[0] = '\0'; if (n > 0 && pick[0] != -1) fnncat(filename, files[pick[0] - 1], sizeof (filename) / sizeof (fnchar) - 1); for (i = 0; i < filecount; i++) free(files[i]); free(files); if (n <= 0) return; if (pick[0] == -1) { dir = (dir == savedir) ? logdir : savedir; continue; } /* we have a valid filename */ break; } fd = sys_open(filename, O_RDWR, 0660); replay_commandloop(fd); close(fd); }
void replay_commandloop(int fd) { int key, move, count; char buf[BUFSZ], qbuf[BUFSZ]; nh_bool ret, firsttime = TRUE; struct nh_replay_info rinfo; struct nh_cmd_arg noarg; struct nh_cmd_desc *cmd; create_game_windows(); if (!nh_view_replay_start(fd, &curses_replay_windowprocs, &rinfo)) return; load_keymap(); while (1) { draw_msgwin(); curses_update_status(NULL); draw_sidebar(); draw_replay_info(&rinfo); if (firsttime) show_replay_help(); firsttime = FALSE; key = get_map_key(TRUE); switch (key) { /* step forward */ case KEY_RIGHT: case ' ': ret = nh_view_replay_step(&rinfo, REPLAY_FORWARD, 1); draw_replay_info(&rinfo); if (ret == FALSE) { key = curses_msgwin("You have reached the end of this game. " "Go back or press ESC to exit."); if (key == KEY_ESC) goto out; } break; /* step backward */ case KEY_LEFT: nh_view_replay_step(&rinfo, REPLAY_BACKWARD, 1); draw_replay_info(&rinfo); break; case KEY_ESC: goto out; case 'g': strncpy(qbuf, "What move do you want to jump to?", BUFSZ); if (rinfo.max_moves > 0) sprintf(qbuf + strlen(qbuf), " (Max: %d)", rinfo.max_moves); curses_getline(qbuf, buf); if (buf[0] == '\033' || !(move = atoi(buf))) break; nh_view_replay_step(&rinfo, REPLAY_GOTO, move); break; case KEY_F(12): /* timetest! */ if (allow_timetest()) timetest(fd, &rinfo); break; default: count = 0; noarg.argtype = CMD_ARG_NONE; cmd = keymap[key]; if (!cmd) break; if (cmd->flags & CMD_UI) handle_internal_cmd(&cmd, &noarg, &count); if (cmd) nh_command(cmd->name, count, &noarg); break; } } out: nh_view_replay_finish(); free_keymap(); destroy_game_windows(); cleanup_messages(); }
static void timetest(int fd, struct nh_replay_info *rinfo) { char buf[BUFSZ]; hp_time t_start, t_end; long ms; int mmax, initial, revpos; initial = rinfo->moves; nh_view_replay_step(rinfo, REPLAY_GOTO, 0); /* run forward */ gettime(&t_start); while (rinfo->actions < rinfo->max_actions) { nh_view_replay_step(rinfo, REPLAY_FORWARD, 1); curses_update_status(NULL); draw_replay_info(rinfo); doupdate(); } draw_msgwin(); gettime(&t_end); ms = clock_delta_ms(&t_start, &t_end); snprintf(buf, BUFSZ, "%d actions replayed with display in %ld ms. (%ld actions/sec)", rinfo->max_actions, ms, rinfo->max_actions * 1000 / ms); curses_msgwin(buf); /* reset the entire replay state to delete checkpoints */ mmax = rinfo->moves; /* max_moves may not be available if this is a replay of a crashed game */ nh_view_replay_finish(); nh_view_replay_start(fd, &curses_replay_windowprocs, rinfo); /* run forward without showing the map etc. */ gettime(&t_start); nh_view_replay_step(rinfo, REPLAY_GOTO, mmax); gettime(&t_end); ms = clock_delta_ms(&t_start, &t_end); snprintf(buf, BUFSZ, "%d actions replayed without display in %ld ms. (%ld actions/sec)", rinfo->actions, ms, rinfo->actions * 1000 / ms); curses_msgwin(buf); /* run backward */ revpos = rinfo->actions; gettime(&t_start); while (rinfo->actions > 0 && revpos < rinfo->actions + 1000) { nh_view_replay_step(rinfo, REPLAY_BACKWARD, 1); curses_update_status(NULL); draw_msgwin(); draw_replay_info(rinfo); doupdate(); } gettime(&t_end); ms = clock_delta_ms(&t_start, &t_end); snprintf(buf, BUFSZ, "%d actions replayed backward with display in %ld ms. (%ld actions/sec)", revpos - rinfo->actions, ms, (revpos - rinfo->actions) * 1000 / ms); curses_msgwin(buf); nh_view_replay_step(rinfo, REPLAY_GOTO, initial); }
static void msgtype_edit_rule(struct nh_msgtype_rules *mt_rules, int ruleno) { struct nh_msgtype_rule *rule; /* Menu variables. */ struct nh_menuitem *items; int icount, menusize, selected[1], n; rule = &mt_rules->rules[ruleno]; menusize = 5; items = malloc(sizeof(struct nh_menuitem) * menusize); do { char buf[BUFSZ], query[BUFSZ]; int i; icount = 0; sprintf(buf, "Position:\t[%d]", ruleno + 1); add_menu_item(items, menusize, icount, 1, buf, 0, 0); sprintf(buf, "Action:\t[%s]", msgtype_action_string(rule->action)); add_menu_item(items, menusize, icount, 2, buf, 0, 0); sprintf(buf, "Pattern:\t[%s]", rule->pattern); add_menu_item(items, menusize, icount, 3, buf, 0, 0); add_menu_txt(items, menusize, icount, "", MI_TEXT); add_menu_item(items, menusize, icount, 4, "Delete this match", '!', 0); n = curses_display_menu(items, icount, "Edit match:", PICK_ONE, selected); if (n > 0) { switch (selected[0]) { case 1: /* change position */ { int newpos; struct nh_msgtype_rule tmprule; sprintf(query, "New match position: (1 - %d, currently %d)", mt_rules->num_rules, ruleno + 1); buf[0] = '\0'; curses_getline(query, buf); if (!*buf || *buf == '\033') break; newpos = atoi(buf); if (newpos <= 0 || newpos > mt_rules->num_rules) { curses_msgwin("Invalid match position."); break; } newpos--; if (newpos == ruleno) break; tmprule = mt_rules->rules[ruleno]; /* Shuffle rules in-between. */ if (newpos > ruleno) { for (i = ruleno; i < newpos; i++) mt_rules->rules[i] = mt_rules->rules[i + 1]; } else { for (i = ruleno; i > newpos; i--) mt_rules->rules[i] = mt_rules->rules[i - 1]; } mt_rules->rules[newpos] = tmprule; /* Exit menu do-while (n > 0) */ n = 0; } break; case 2: /* change action */ rule->action = get_msgtype_action(rule->action); break; case 3: /* change pattern */ snprintf(query, BUFSZ, "New match pattern: (currently \"%s\")", rule->pattern); buf[0] = '\0'; curses_getline(query, buf); if (*buf != '\033') { /* Replace the same chars as msgtype_to_string to reduce * player surprise when loading a game and viewing msgtypes. */ for (i = 0; i < sizeof(buf); i++) { if (buf[i] == '"' || buf[i] == '|' || buf[i] == ';') buf[i] = '?'; } strncpy(rule->pattern, buf, sizeof(rule->pattern)); } rule->pattern[sizeof(rule->pattern) - 1] = '\0'; break; case 4: /* delete match */ /* Shuffle rules down. */ for (i = ruleno; i < mt_rules->num_rules - 1; i++) mt_rules->rules[i] = mt_rules->rules[i + 1]; mt_rules->num_rules--; mt_rules->rules = realloc(mt_rules->rules, mt_rules->num_rules * sizeof(struct nh_msgtype_rule)); /* Exit menu do-while (n > 0) */ n = 0; break; default: curses_msgwin("无效的消息类型匹配目录选择。"); } } } while (n > 0); free(items); }
static void edit_ap_rule(struct nh_autopick_option *desc, struct nh_autopickup_rules *ar, int ruleno) { struct nh_autopickup_rule *r = &ar->rules[ruleno]; struct nh_autopickup_rule tmprule; struct nh_menuitem *items; int i, icount, size = 7, n, selected[1], newpos; char query[BUFSZ], buf[BUFSZ], *classname; items = malloc(sizeof(struct nh_menuitem) * size); do { icount = 0; sprintf(buf, "rule position:\t[%d]", ruleno + 1); add_menu_item(items, size, icount, 1, buf, 0, 0); sprintf(buf, "name pattern:\t[%s]", r->pattern); add_menu_item(items, size, icount, 2, buf, 0, 0); classname = NULL; for (i = 0; i < desc->numclasses && !classname; i++) if (desc->classes[i].id == r->oclass) classname = desc->classes[i].caption; sprintf(buf, "object type:\t[%s]", classname); add_menu_item(items, size, icount, 3, buf, 0, 0); sprintf(buf, "beatitude:\t[%s]", bucnames[r->buc]); add_menu_item(items, size, icount, 4, buf, 0, 0); sprintf(buf, "action:\t[%s]", r->action == AP_GRAB ? "GRAB" : "LEAVE"); add_menu_item(items, size, icount, 5, buf, 0, 0); add_menu_txt(items, size, icount, "", MI_TEXT); add_menu_item(items, size, icount, 6, "delete this rule", 'x', 0); n = curses_display_menu(items, icount, "Edit rule:", PICK_ONE, selected); if (n <= 0) break; switch (selected[0]) { /* move this rule */ case 1: sprintf(query, "New rule position: (1 - %d), currently: %d", ar->num_rules, ruleno + 1); buf[0] = '\0'; curses_getline(query, buf); if (!*buf || *buf == '\033') break; newpos = atoi(buf); if (newpos <= 0 || newpos > ar->num_rules) { curses_msgwin("Invalid rule position."); break; } newpos--; if (newpos == ruleno) break; tmprule = ar->rules[ruleno]; /* shift the rules around */ if (newpos > ruleno) { for (i = ruleno; i < newpos; i++) ar->rules[i] = ar->rules[i+1]; } else { for (i = ruleno; i > newpos; i--) ar->rules[i] = ar->rules[i-1]; } ar->rules[newpos] = tmprule; goto out; /* edit the pattern */ case 2: sprintf(query, "New name pattern (empty matches everything):"); buf[0] = '\0'; curses_getline(query, buf); if (*buf != '\033') strncpy(r->pattern, buf, sizeof(r->pattern)); r->pattern[sizeof(r->pattern)-1] = '\0'; break; /* edit object class match */ case 3: r->oclass = get_autopickup_oclass(desc, r->oclass); break; /* edit beatitude match */ case 4: r->buc = get_autopickup_buc(r->buc); break; /* toggle action */ case 5: if (r->action == AP_GRAB) r->action = AP_LEAVE; else r->action = AP_GRAB; break; /* delete */ case 6: for (i = ruleno; i < ar->num_rules - 1; i++) ar->rules[i] = ar->rules[i+1]; ar->num_rules--; ar->rules = realloc(ar->rules, ar->num_rules * sizeof(struct nh_autopickup_rule)); goto out; /* break just beaks the switch .. doh */ } } while (n > 0); out: free(items); }
/* get a new value of the appropriate type for the given option */ static nh_bool get_option_value(struct win_menu *mdat, int idx) { char buf[BUFSZ], query[BUFSZ]; union nh_optvalue value; struct nh_option_desc *option, *optlist; int listid = mdat->items[idx].id >> 10; int id = mdat->items[idx].id & 0x1ff; char strbuf[BUFSZ]; int prev_optstyle = settings.optstyle; switch (listid) { case ACT_BIRTH_OPTS: optlist = nh_get_options(ACTIVE_BIRTH_OPTIONS); break; case CUR_BIRTH_OPTS: optlist = nh_get_options(CURRENT_BIRTH_OPTIONS); break; case GAME_OPTS: optlist = nh_get_options(GAME_OPTIONS); break; case UI_OPTS: optlist = curses_options; break; default: return FALSE; } option = &optlist[id]; value.s = strbuf; switch ((int)option->type) { case OPTTYPE_BOOL: select_boolean_value(&value, option); break; case OPTTYPE_INT: sprintf(query, "New value for %s (number from %d to %d)", option->name, option->i.min, option->i.max); sprintf(buf, "%d", value.i); curses_getline(query, buf); if (buf[0] == '\033') return FALSE; sscanf(buf, "%d", &value.i); break; case OPTTYPE_ENUM: select_enum_value(&value, option); break; case OPTTYPE_STRING: sprintf(query, "New value for %s (text)", option->name); curses_getline(query, value.s); if (value.s[0] == '\033') return FALSE; break; case OPTTYPE_AUTOPICKUP_RULES: show_autopickup_menu(option); return FALSE; case OPTTYPE_MSGTYPE: show_msgtype_menu(option); return FALSE; case OPTTYPE_KEYMAP: show_keymap_menu(FALSE); return FALSE; default: return FALSE; } if (!nh_set_option(option->name, value, FALSE)) { sprintf(strbuf, "new value for %s rejected", option->name); curses_msgwin(strbuf); } else print_option_string(option, mdat->items[idx].caption); /* special case: directly redo option menu appearance */ if (settings.optstyle != prev_optstyle) return TRUE; return FALSE; }
static void show_msgtype_menu(struct nh_option_desc *opt) { /* msgtype option variables. */ union nh_optvalue value; unsigned int size; /* Menu variables. */ struct nh_menuitem *items; int icount, menusize, selected[1], n; /* Clone msgtype rules. */ value.mt = malloc(sizeof(struct nh_msgtype_rules)); if (opt->value.mt) { value.mt->num_rules = opt->value.mt->num_rules; size = value.mt->num_rules * sizeof(struct nh_msgtype_rule); value.mt->rules = malloc(size); memcpy(value.mt->rules, opt->value.mt->rules, size); } else { value.mt->num_rules = 0; value.mt->rules = NULL; } menusize = value.mt->num_rules + 4; items = malloc(sizeof(struct nh_menuitem) * menusize); selected[0] = 0; do { int i, id; icount = 0; add_menu_txt(items, menusize, icount, "Pos\tAction\tPattern", MI_HEADING); for (i = 0; i < value.mt->num_rules; i++) { /* position (3) + '.' (1) + '\t' (1) + pattern (119) + '\t' (1) + * "NO REPEAT" (9) + null (1) */ char buf[134]; struct nh_msgtype_rule *r = &value.mt->rules[i]; sprintf(buf, "%2d.\t%s\t%s", i + 1, msgtype_action_string(r->action), r->pattern); add_menu_item(items, menusize, icount, i + 1, buf, 0, 0); } add_menu_txt(items, menusize, icount, "", MI_TEXT); add_menu_item(items, menusize, icount, -1, "add new match", '+', 0); add_menu_item(items, menusize, icount, -2, "help", '?', 0); /* If the previous selection was to add a rule, scroll to the bottom now * so that the player can see it. */ if (selected[0] == -1) { n = curses_display_menu_bottom(items, icount, "Message types:", PICK_ONE, selected); } else { n = curses_display_menu(items, icount, "Message types:", PICK_ONE, selected); } if (n > 0) { id = selected[0]; if (id == -2) { msgtype_help(); } else if (id == -1) { /* add new match */ if (value.mt->num_rules >= MSGTYPE_MAX_RULES) { curses_msgwin("Maximum number of rules reached."); } else { struct nh_msgtype_rule *r; id = value.mt->num_rules; value.mt->num_rules++; size = value.mt->num_rules * sizeof(struct nh_msgtype_rule); value.mt->rules = realloc(value.mt->rules, size); r = &value.mt->rules[id]; r->pattern[0] = '\0'; r->action = MSGTYPE_DEFAULT; } } else { /* edit existing match */ msgtype_edit_rule(value.mt, id - 1); } } } while (n > 0); nh_set_option(opt->name, value, FALSE); if (value.mt->rules) free(value.mt->rules); free(value.mt); free(items); }
/* parse a single line from the config file and set the option */ static void read_config_line(char *line) { char *comment, *delim, *name, *value; struct nh_option_desc *option; struct nh_option_desc *optlist = NULL; union nh_optvalue optval; comment = strchr(line, '#'); if (comment) *comment = '\0'; delim = strchr(line, '='); if (!delim) return; /* could whine about junk chars in the config, but why bother */ name = line; value = delim + 1; *delim-- = '\0'; /* remove space around name */ while (isspace(*name)) name++; while (isspace(*delim)) *delim-- = '\0'; /* remove spaces around value */ delim = value; while (*delim) delim++; delim--; while (isspace(*value)) value++; while (isspace(*delim)) *delim-- = '\0'; /* value may be enclosed with double quotes (") */ if (*value == '"' && *delim == '"') { value++; *delim = '\0'; } /* Find an options list with which to parse this option name. */ option = nhlib_find_option(curses_options, name); if (!option) { optlist = curses_get_nh_opts(); if (optlist) option = nhlib_find_option(optlist, name); } if (!option) { #if 0 curses_msgwin("Unknown option in config file:", krc_notification); curses_msgwin(name, krc_notification); #endif return; } optval = nhlib_string_to_optvalue(option, value); curses_set_option(name, optval); if (option->type == OPTTYPE_AUTOPICKUP_RULES) { free(optval.ar->rules); free(optval.ar); } if (optlist) curses_free_nh_opts(optlist); }
nh_bool loadgame(void) { char buf[BUFSZ]; fnchar savedir[BUFSZ], filename[1024], **files; struct nh_menuitem *items; int size, icount, fd, i, n, ret, pick[1]; enum nh_log_status status; struct nh_game_info gi; if (!get_gamedir(SAVE_DIR, savedir)) { curses_raw_print("Could not find or create the save directory."); return FALSE; } files = list_gamefiles(savedir, &size); if (!size) { curses_msgwin("No saved games found."); return FALSE; } icount = 0; items = malloc(size * sizeof(struct nh_menuitem)); for (i = 0; i < size; i++) { fd = sys_open(files[i], O_RDWR, FILE_OPEN_MASK); status = nh_get_savegame_status(fd, &gi); close(fd); describe_game(buf, status, &gi); add_menu_item(items, size, icount, (status == LS_IN_PROGRESS) ? 0 : icount + 1, buf, 0, FALSE); } n = curses_display_menu(items, icount, "saved games", PICK_ONE, pick); free(items); filename[0] = '\0'; if (n > 0) fnncat(filename, files[pick[0]-1], sizeof(filename)/sizeof(fnchar)-1); for (i = 0; i < icount; i++) free(files[i]); free(files); if (n <= 0) return FALSE; fd = sys_open(filename, O_RDWR, FILE_OPEN_MASK); create_game_windows(); if (nh_restore_game(fd, NULL, FALSE) != GAME_RESTORED) { destroy_game_windows(); close(fd); if (curses_yn_function("Failed to load the save. Do you wish to delete the file?", "yn", 'n') == 'y') unlink(filename); return FALSE; } load_keymap(); /* need to load the keymap after the game has been started */ ret = commandloop(); free_keymap(); close(fd); destroy_game_windows(); cleanup_messages(); game_ended(ret, filename); return TRUE; }
int curses_getpos(int *x, int *y, nh_bool force, const char *goal) { int result = 0; int cx, cy; int key, dx, dy; int sidx; static const char pick_chars[] = " \r\n.,;:"; static const int pick_vals[] = {1, 1, 1, 1, 2, 3, 4}; const char *cp; char printbuf[BUFSZ]; char *matching = NULL; enum nh_direction dir; struct coord *monpos = NULL; int moncount, monidx; int firstmove = 1; werase(statuswin); mvwaddstr(statuswin, 0, 0, "Move the cursor with the direction keys. Press " "the letter of a dungeon symbol"); mvwaddstr(statuswin, 1, 0, "to select it or use m to move to a nearby " "monster. Finish with one of .,;:"); wrefresh(statuswin); cx = *x >= 1 ? *x : player.x; cy = *y >= 0 ? *y : player.y; wmove(mapwin, cy, cx - 1); while (1) { if (!firstmove) { struct nh_desc_buf descbuf; int mx = 0, my = 0; nh_describe_pos(cx, cy, &descbuf, NULL); werase(statuswin); place_desc_message(statuswin, &mx, &my, descbuf.effectdesc); place_desc_message(statuswin, &mx, &my, descbuf.invisdesc); place_desc_message(statuswin, &mx, &my, descbuf.mondesc); place_desc_message(statuswin, &mx, &my, descbuf.objdesc); place_desc_message(statuswin, &mx, &my, descbuf.trapdesc); place_desc_message(statuswin, &mx, &my, descbuf.bgdesc); wrefresh(statuswin); wmove(mapwin, cy, cx - 1); } firstmove = 0; dx = dy = 0; key = get_map_key(FALSE); if (key == KEY_ESC) { cx = cy = -10; result = -1; break; } if ((cp = strchr(pick_chars, (char)key)) != 0) { /* '.' => 0, ',' => 1, ';' => 2, ':' => 3 */ result = pick_vals[cp - pick_chars]; break; } dir = key_to_dir(key); if (dir != DIR_NONE) { dx = xdir[dir]; dy = ydir[dir]; } else if ((dir = key_to_dir(tolower((char)key))) != DIR_NONE) { /* a shifted movement letter */ dx = xdir[dir] * 8; dy = ydir[dir] * 8; } if (dx || dy) { /* truncate at map edge */ if (cx + dx < 1) dx = 1 - cx; if (cx + dx > COLNO - 1) dx = COLNO - 1 - cx; if (cy + dy < 0) dy = -cy; if (cy + dy > ROWNO - 1) dy = ROWNO - 1 - cy; cx += dx; cy += dy; goto nxtc; } if (key == 'm') { if (!monpos) { int i, j; moncount = 0; for (i = 0; i < ROWNO; i++) for (j = 0; j < COLNO; j++) if (display_buffer[i][j].mon && (j != player.x || i != player.y)) moncount++; monpos = malloc(moncount * sizeof (struct coord)); monidx = 0; for (i = 0; i < ROWNO; i++) for (j = 0; j < COLNO; j++) if (display_buffer[i][j].mon && (j != player.x || i != player.y)) { monpos[monidx].x = j; monpos[monidx].y = i; monidx++; } monidx = 0; qsort(monpos, moncount, sizeof (struct coord), compare_coord_dist); } if (moncount) { /* there is at least one monster to move to */ cx = monpos[monidx].x; cy = monpos[monidx].y; monidx = (monidx + 1) % moncount; } } else { int k = 0, tx, ty; int pass, lo_x, lo_y, hi_x, hi_y; matching = malloc(default_drawing->num_bgelements); memset(matching, 0, default_drawing->num_bgelements); for (sidx = default_drawing->bg_feature_offset; sidx < default_drawing->num_bgelements; sidx++) if (key == default_drawing->bgelements[sidx].ch) matching[sidx] = (char)++k; if (k) { for (pass = 0; pass <= 1; pass++) { /* pass 0: just past current pos to lower right; pass 1: upper left corner to current pos */ lo_y = (pass == 0) ? cy : 0; hi_y = (pass == 0) ? ROWNO - 1 : cy; for (ty = lo_y; ty <= hi_y; ty++) { lo_x = (pass == 0 && ty == lo_y) ? cx + 1 : 1; hi_x = (pass == 1 && ty == hi_y) ? cx : COLNO - 1; for (tx = lo_x; tx <= hi_x; tx++) { k = display_buffer[ty][tx].bg; if (k && matching[k]) { cx = tx; cy = ty; goto nxtc; } } /* column */ } /* row */ } /* pass */ sprintf(printbuf, "Can't find dungeon feature '%c'.", (char)key); curses_msgwin(printbuf); } else { sprintf(printbuf, "Unknown direction%s.", !force ? " (ESC to abort)" : ""); curses_msgwin(printbuf); } } if (force) goto nxtc; cx = -1; cy = 0; result = 0; /* not -1 */ break; nxtc: wmove(mapwin, cy, cx - 1); wrefresh(mapwin); } *x = cx; *y = cy; if (monpos) free(monpos); if (matching) free(matching); curses_update_status(NULL); /* clear the help message */ return result; }
/* get a new value of the appropriate type for the given option */ static nh_bool query_new_value(struct win_menu *mdat, int idx) { struct nh_option_desc *option, *optlist; struct nh_option_desc optioncopy; struct nh_option_desc *optioncopy_p = &optioncopy; int listid = mdat->items[idx].id >> 10; int id = mdat->items[idx].id & 0x1ff; int prev_optstyle = settings.optstyle; enum nh_menupaging prev_menupaging = settings.menupaging; nh_bool prev_menuborder = settings.whichframes != FRAME_NONE; nh_bool ret = FALSE; switch (listid) { case BIRTH_OPTS: case CREATION_OPTS: case GAME_OPTS: optlist = curses_get_nh_opts(); break; case UI_OPTS: optlist = curses_options; break; default: return FALSE; } option = &optlist[id]; /* optioncopy holds the new option we're planning to set */ optioncopy = *option; switch ((int)optioncopy.type) { case OPTTYPE_BOOL: select_boolean_value(&optioncopy.value, &optioncopy); break; case OPTTYPE_INT: if (optioncopy.i.min >= -2147483647-1 && optioncopy.i.max <= 2147483647) { /* Maximum length of a number as text is 11 chars */ char query[11 + 1 + strlen(optioncopy.name) + sizeof "New value for (number from to )"]; sprintf(query, "New value for %s (number from %d to %d)", optioncopy.name, optioncopy.i.min, optioncopy.i.max); curses_getline(query, &optioncopy_p, getlin_option_callback); } break; case OPTTYPE_ENUM: select_enum_value(&optioncopy.value, option); break; case OPTTYPE_STRING: if (!strcmp(optioncopy.name, "tileset")) { select_tileset_value(&optioncopy.value, option); } else { char query[strlen(optioncopy.name) + 1 + sizeof "New value for (text)"]; sprintf(query, "New value for %s (text)", optioncopy.name); optioncopy.value.s = NULL; curses_getline(query, &optioncopy_p, getlin_option_callback); } break; case OPTTYPE_AUTOPICKUP_RULES: show_autopickup_menu(option); goto free; case OPTTYPE_KEYMAP: show_keymap_menu(FALSE); goto free; default: goto free; } /* getlin_option_callback NULLs out optioncopy_p to indicate that setting was cancelled */ if (optioncopy_p && !curses_set_option(optioncopy.name, optioncopy.value)) { char query[strlen(optioncopy.name) + 1 + sizeof "new value for rejected"]; sprintf(query, "new value for %s rejected", optioncopy.name); curses_msgwin(query, krc_notification); } else if (optioncopy_p) { if (listid != UI_OPTS) { curses_free_nh_opts(optlist); optlist = curses_get_nh_opts(); option = &optlist[id]; } print_option_string(option, mdat->items[idx].caption); } /* We need to deallocate any string that might have been allocated by the getlin callback. */ if (optioncopy.type == OPTTYPE_STRING && optioncopy.value.s) free(optioncopy.value.s); /* If we're changing the option menu appearance, or if we changed game options, we need to reload and redraw the menu. */ if (settings.optstyle != prev_optstyle || settings.menupaging != prev_menupaging || (settings.whichframes != FRAME_NONE) != prev_menuborder || (game_is_running && listid != UI_OPTS)) ret = TRUE; free: if (listid != UI_OPTS) curses_free_nh_opts(optlist); return ret; }