| 12 | | #include "z-file.h" |
|---|
| | 25 | #include "ui.h" |
|---|
| | 26 | |
|---|
| | 27 | |
|---|
| | 28 | |
|---|
| | 29 | typedef struct { |
|---|
| | 30 | int maxnum; /* Maximum possible item count for this class */ |
|---|
| | 31 | bool easy_know; /* Items don't need to be IDed to recognize membership */ |
|---|
| | 32 | |
|---|
| | 33 | const char *(*name)(int gid); /* name of this group */ |
|---|
| | 34 | |
|---|
| | 35 | /* Compare, in group and display order */ |
|---|
| | 36 | /* Optional, if already sorted */ |
|---|
| | 37 | int (*gcomp)(const void *, const void *); /* Compare GroupIDs of two oids */ |
|---|
| | 38 | int (*group)(int oid); /* Group ID for of an oid */ |
|---|
| | 39 | bool (*aware)(object_type *obj); /* Object is known sufficiently for group */ |
|---|
| | 40 | |
|---|
| | 41 | } group_funcs; |
|---|
| | 42 | |
|---|
| | 43 | typedef struct { |
|---|
| | 44 | |
|---|
| | 45 | /* Display object label (possibly with cursor) at given screen location. */ |
|---|
| | 46 | void (*display_label)(int col, int row, bool cursor, int oid); |
|---|
| | 47 | |
|---|
| | 48 | void (*lore)(int oid); /* Dump known lore to screen*/ |
|---|
| | 49 | |
|---|
| | 50 | /* Required only for objects with modifiable display attributes */ |
|---|
| | 51 | /* Unknown 'flavors' return flavor attributes */ |
|---|
| | 52 | char *(*xchar)(int oid); /* get character attr for OID (by address) */ |
|---|
| | 53 | byte *(*xattr)(int oid); /* get color attr for OID (by address) */ |
|---|
| | 54 | |
|---|
| | 55 | /* Required only for manipulable (ordinary) objects */ |
|---|
| | 56 | /* Address of inscription. Unknown 'flavors' return null */ |
|---|
| | 57 | u16b *(*note)(int oid); |
|---|
| | 58 | |
|---|
| | 59 | } member_funcs; |
|---|
| | 60 | |
|---|
| | 61 | |
|---|
| | 62 | /* Helper class for generating joins */ |
|---|
| | 63 | typedef struct join { |
|---|
| | 64 | int oid; |
|---|
| | 65 | int gid; |
|---|
| | 66 | } join_t; |
|---|
| | 67 | |
|---|
| | 68 | /* A default group-by */ |
|---|
| | 69 | static join_t *default_join; |
|---|
| | 70 | static int default_join_cmp(const void *a, const void *b) |
|---|
| | 71 | { |
|---|
| | 72 | join_t *ja = &default_join[*(int*)a]; |
|---|
| | 73 | join_t *jb = &default_join[*(int*)b]; |
|---|
| | 74 | int c = ja->gid - jb->gid; |
|---|
| | 75 | if (c) return c; |
|---|
| | 76 | return ja->oid - jb->oid; |
|---|
| | 77 | } |
|---|
| | 78 | static int default_group(int oid) { return default_join[oid].gid; } |
|---|
| | 79 | |
|---|
| | 80 | |
|---|
| | 81 | static int *obj_group_order; |
|---|
| | 82 | /* |
|---|
| | 83 | * Description of each monster group. |
|---|
| | 84 | */ |
|---|
| | 85 | static struct { |
|---|
| | 86 | cptr chars; |
|---|
| | 87 | cptr name; |
|---|
| | 88 | } monster_group[] = |
|---|
| | 89 | { |
|---|
| | 90 | { (cptr)-1, "Uniques" }, |
|---|
| | 91 | { "A", "Angels" }, |
|---|
| | 92 | { "a", "Ants" }, |
|---|
| | 93 | { "b", "Bats" }, |
|---|
| | 94 | { "B", "Birds" }, |
|---|
| | 95 | { "C", "Canines" }, |
|---|
| | 96 | { "c", "Centipedes" }, |
|---|
| | 97 | { "uU", "Demons" }, |
|---|
| | 98 | { "dD", "Dragons" }, |
|---|
| | 99 | { "vE", "Elementals/Vortices" }, |
|---|
| | 100 | { "e", "Eyes/Beholders" }, |
|---|
| | 101 | { "f", "Felines" }, |
|---|
| | 102 | { "G", "Ghosts" }, |
|---|
| | 103 | { "OP", "Giants/Ogres" }, |
|---|
| | 104 | { "g", "Golems" }, |
|---|
| | 105 | { "H", "Harpies/Hybrids" }, |
|---|
| | 106 | { "h", "Hominids (Elves, Dwarves)" }, |
|---|
| | 107 | { "H", "Hydras" }, |
|---|
| | 108 | { "i", "Icky Things" }, |
|---|
| | 109 | { "FI", "Insects" }, |
|---|
| | 110 | { "j", "Jellies" }, |
|---|
| | 111 | { "K", "Killer Beetles" }, |
|---|
| | 112 | { "k", "Kobolds" }, |
|---|
| | 113 | { "L", "Lichs" }, |
|---|
| | 114 | { "$?!_", "Mimics" }, |
|---|
| | 115 | { "m", "Molds" }, |
|---|
| | 116 | { ",", "Mushroom Patches" }, |
|---|
| | 117 | { "n", "Nagas" }, |
|---|
| | 118 | { "o", "Orcs" }, |
|---|
| | 119 | { "tp", "Humans" }, |
|---|
| | 120 | { "q", "Quadrupeds" }, |
|---|
| | 121 | { "Q", "Quylthulgs" }, |
|---|
| | 122 | { "R", "Reptiles/Amphibians" }, |
|---|
| | 123 | { "r", "Rodents" }, |
|---|
| | 124 | { "S", "Scorpions/Spiders" }, |
|---|
| | 125 | { "s", "Skeletons/Drujs" }, |
|---|
| | 126 | { "J", "Snakes" }, |
|---|
| | 127 | { "T", "Trolls" }, |
|---|
| | 128 | { "V", "Vampires" }, |
|---|
| | 129 | { "W", "Wights/Wraiths" }, |
|---|
| | 130 | { "w", "Worms/Worm Masses" }, |
|---|
| | 131 | { "X", "Xorns/Xarens" }, |
|---|
| | 132 | { "Y", "Yeti" }, |
|---|
| | 133 | { "Z", "Zephyr Hounds" }, |
|---|
| | 134 | { "z", "Zombies" }, |
|---|
| | 135 | { NULL, NULL } |
|---|
| | 136 | }; |
|---|
| | 137 | |
|---|
| | 138 | /* |
|---|
| | 139 | * Description of each feature group. |
|---|
| | 140 | */ |
|---|
| | 141 | const char *feature_group_text[] = |
|---|
| | 142 | { |
|---|
| | 143 | "Floors", |
|---|
| | 144 | "Traps", |
|---|
| | 145 | "Doors", |
|---|
| | 146 | "Stairs", |
|---|
| | 147 | "Walls", |
|---|
| | 148 | "Streamers", |
|---|
| | 149 | "Obstructions", |
|---|
| | 150 | "Stores", |
|---|
| | 151 | "Other", |
|---|
| | 152 | NULL |
|---|
| | 153 | }; |
|---|
| | 154 | |
|---|
| | 155 | |
|---|
| | 156 | |
|---|
| | 157 | /* Useful method declarations */ |
|---|
| | 158 | static void display_visual_list(int col, int row, int height, int width, |
|---|
| | 159 | byte attr_top, char char_left); |
|---|
| | 160 | |
|---|
| | 161 | static void browser_mouse(key_event ke, int *column, int *grp_cur, int grp_cnt, |
|---|
| | 162 | int *list_cur, int list_cnt, int col0, int row0, |
|---|
| | 163 | int grp0, int list0, int *delay); |
|---|
| | 164 | |
|---|
| | 165 | static void browser_cursor(char ch, int *column, int *grp_cur, int grp_cnt, |
|---|
| | 166 | int *list_cur, int list_cnt); |
|---|
| | 167 | |
|---|
| | 168 | static bool visual_mode_command(key_event ke, bool *visual_list_ptr, |
|---|
| | 169 | int height, int width, |
|---|
| | 170 | byte *attr_top_ptr, char *char_left_ptr, |
|---|
| | 171 | byte *cur_attr_ptr, char *cur_char_ptr, |
|---|
| | 172 | int col, int row, int *delay); |
|---|
| | 173 | |
|---|
| | 174 | static void place_visual_list_cursor(int col, int row, byte a, |
|---|
| | 175 | byte c, byte attr_top, byte char_left); |
|---|
| | 176 | |
|---|
| | 177 | static void dump_pref_file(void (*dump)(FILE*), const char *title); |
|---|
| | 178 | |
|---|
| | 179 | /* |
|---|
| | 180 | * Clipboard variables for copy&paste in visual mode |
|---|
| | 181 | */ |
|---|
| | 182 | static byte attr_idx = 0; |
|---|
| | 183 | static byte char_idx = 0; |
|---|
| | 184 | |
|---|
| | 185 | /* |
|---|
| | 186 | * Return a specific ordering for the features |
|---|
| | 187 | */ |
|---|
| | 188 | int feat_order(int feat) |
|---|
| | 189 | { |
|---|
| | 190 | feature_type *f_ptr = &f_info[feat]; |
|---|
| | 191 | switch(f_ptr->d_char) { |
|---|
| | 192 | case '.': return 0; |
|---|
| | 193 | case '^': return 1; |
|---|
| | 194 | case '\'': case '+': return 2; |
|---|
| | 195 | case '<': case '>': return 3; |
|---|
| | 196 | case '#': return 4; |
|---|
| | 197 | case '*': case '%' : return 5; |
|---|
| | 198 | case ';': case ':' : return 6; |
|---|
| | 199 | default: |
|---|
| | 200 | if(isdigit(f_ptr->d_char)) return 7; |
|---|
| | 201 | return 8; |
|---|
| | 202 | } |
|---|
| | 203 | }; |
|---|
| | 204 | |
|---|
| | 205 | |
|---|
| | 206 | /* HACK */ |
|---|
| | 207 | static const int use_dbltile = 0; |
|---|
| | 208 | static const int use_trptile = 0; |
|---|
| | 209 | |
|---|
| | 210 | /* Emit a 'graphical' symbol and a padding character if appropriate */ |
|---|
| | 211 | static void big_pad(int col, int row, byte a, byte c) |
|---|
| | 212 | { |
|---|
| | 213 | Term_putch(col, row, a, c); |
|---|
| | 214 | if(!use_bigtile) return; |
|---|
| | 215 | if (a &0x80) Term_putch(col+1, row, 255, -1); |
|---|
| | 216 | else Term_putch(col+1, row, 1, ' '); |
|---|
| | 217 | } |
|---|
| | 218 | |
|---|
| | 219 | static int actual_width(int width) { |
|---|
| | 220 | if (use_trptile) width = width * 3; |
|---|
| | 221 | else if(use_dbltile) width *= 2; |
|---|
| | 222 | if(use_bigtile) width *= 2; |
|---|
| | 223 | return width; |
|---|
| | 224 | } |
|---|
| | 225 | static int actual_height(int height) { |
|---|
| | 226 | if(use_bigtile) height *= 2; |
|---|
| | 227 | if (use_trptile) height = height * 3 / 2; |
|---|
| | 228 | else if(use_dbltile) height *= 2; |
|---|
| | 229 | return height; |
|---|
| | 230 | } |
|---|
| | 231 | |
|---|
| | 232 | static int logical_width(int width) |
|---|
| | 233 | { |
|---|
| | 234 | int div = 1; |
|---|
| | 235 | if(use_trptile) div = 3; |
|---|
| | 236 | else if(use_dbltile) div *= 2; |
|---|
| | 237 | if(use_bigtile) div *= 2; |
|---|
| | 238 | return width / div; |
|---|
| | 239 | } |
|---|
| | 240 | |
|---|
| | 241 | static int logical_height(int height) |
|---|
| | 242 | { |
|---|
| | 243 | int div = 1; |
|---|
| | 244 | if(use_trptile) { |
|---|
| | 245 | height *= 2; |
|---|
| | 246 | div = 3; |
|---|
| | 247 | } |
|---|
| | 248 | else if(use_dbltile) div = 2; |
|---|
| | 249 | if(use_bigtile) div *= 2; |
|---|
| | 250 | return height / div; |
|---|
| | 251 | } |
|---|
| | 252 | |
|---|
| | 253 | /* |
|---|
| | 254 | * Interact with inscriptions. |
|---|
| | 255 | * Create a copy of an existing quark, except if the quark has '=x' in it, |
|---|
| | 256 | * If an quark has '=x' in it remove it from the copied string, otherwise append it where 'x' is ch. |
|---|
| | 257 | * Return the new quark location. |
|---|
| | 258 | */ |
|---|
| | 259 | static int auto_note_modify(int note, char ch) |
|---|
| | 260 | { |
|---|
| | 261 | char tmp[80]; |
|---|
| | 262 | |
|---|
| | 263 | cptr s; |
|---|
| | 264 | |
|---|
| | 265 | /* Paranoia */ |
|---|
| | 266 | if (!ch) return(note); |
|---|
| | 267 | |
|---|
| | 268 | /* Null length string to start */ |
|---|
| | 269 | tmp[0] = '\0'; |
|---|
| | 270 | |
|---|
| | 271 | /* Inscription */ |
|---|
| | 272 | if (note) |
|---|
| | 273 | { |
|---|
| | 274 | |
|---|
| | 275 | /* Get the inscription */ |
|---|
| | 276 | s = quark_str(note); |
|---|
| | 277 | |
|---|
| | 278 | /* Temporary copy */ |
|---|
| | 279 | my_strcpy(tmp,s,80); |
|---|
| | 280 | |
|---|
| | 281 | /* Process inscription */ |
|---|
| | 282 | while (s) |
|---|
| | 283 | { |
|---|
| | 284 | |
|---|
| | 285 | /* Auto-pickup on "=g" */ |
|---|
| | 286 | if (s[1] == ch) |
|---|
| | 287 | { |
|---|
| | 288 | |
|---|
| | 289 | /* Truncate string */ |
|---|
| | 290 | tmp[strlen(tmp)-strlen(s)] = '\0'; |
|---|
| | 291 | |
|---|
| | 292 | /* Overwrite shorter string */ |
|---|
| | 293 | my_strcat(tmp,s+2,80); |
|---|
| | 294 | |
|---|
| | 295 | /* Create quark */ |
|---|
| | 296 | return(quark_add(tmp)); |
|---|
| | 297 | } |
|---|
| | 298 | |
|---|
| | 299 | /* Find another '=' */ |
|---|
| | 300 | s = strchr(s + 1, '='); |
|---|
| | 301 | } |
|---|
| | 302 | } |
|---|
| | 303 | |
|---|
| | 304 | /* Append note */ |
|---|
| | 305 | my_strcat(tmp,format("=%c",ch),80); |
|---|
| | 306 | |
|---|
| | 307 | /* Create quark */ |
|---|
| | 308 | return(quark_add(tmp)); |
|---|
| | 309 | } |
|---|
| | 310 | |
|---|
| | 311 | /* |
|---|
| | 312 | * Display a list with a cursor |
|---|
| | 313 | */ |
|---|
| | 314 | static void display_group_list(int col, int row, int wid, int per_page, |
|---|
| | 315 | int start, int max, int cursor, const cptr group_text[]) |
|---|
| | 316 | { |
|---|
| | 317 | int i, pos; |
|---|
| | 318 | |
|---|
| | 319 | /* Display lines until done */ |
|---|
| | 320 | for (i = 0, pos = start; i < per_page && pos < max; i++, pos++) |
|---|
| | 321 | { |
|---|
| | 322 | char buffer[21]; |
|---|
| | 323 | byte attr = curs_attrs[CURS_KNOWN][cursor == pos]; |
|---|
| | 324 | |
|---|
| | 325 | /* Erase the line */ |
|---|
| | 326 | Term_erase(col, row + i, wid); |
|---|
| | 327 | |
|---|
| | 328 | /* Display it (width should not exceed 20) */ |
|---|
| | 329 | strncpy(buffer, group_text[pos], 20); |
|---|
| | 330 | buffer[20] = 0; |
|---|
| | 331 | c_put_str(attr, buffer, row + i, col); |
|---|
| | 332 | } |
|---|
| | 333 | /* Wipe the rest? */ |
|---|
| | 334 | } |
|---|
| | 335 | |
|---|
| | 336 | /* |
|---|
| | 337 | * Display the members of a list. |
|---|
| | 338 | * Aware of inscriptions, wizard information, and string-formatted visual data. |
|---|
| | 339 | * label function must display actual visuals, to handle illumination, etc |
|---|
| | 340 | */ |
|---|
| | 341 | static void display_member_list(int col, int row, int wid, int per_page, |
|---|
| | 342 | int start, int o_count, int cursor, int object_idx [], |
|---|
| | 343 | member_funcs o_funcs) |
|---|
| | 344 | { |
|---|
| | 345 | int i, pos; |
|---|
| | 346 | |
|---|
| | 347 | for(i = 0, pos = start; i < per_page && pos < o_count; i++, pos++) { |
|---|
| | 348 | int oid = object_idx[pos]; |
|---|
| | 349 | byte attr = curs_attrs[CURS_KNOWN][cursor == oid]; |
|---|
| | 350 | |
|---|
| | 351 | /* Print basic label */ |
|---|
| | 352 | o_funcs.display_label(col, row + i, pos == cursor, oid); |
|---|
| | 353 | |
|---|
| | 354 | /* Show inscription, if applicable, aware and existing */ |
|---|
| | 355 | if(o_funcs.note && o_funcs.note(oid) && *o_funcs.note(oid)) { |
|---|
| | 356 | c_put_str(TERM_YELLOW,quark_str(*o_funcs.note(oid)), row+i, 65); |
|---|
| | 357 | } |
|---|
| | 358 | |
|---|
| | 359 | if (p_ptr->wizard) |
|---|
| | 360 | c_put_str(attr, format("%d", oid), row, 60); |
|---|
| | 361 | |
|---|
| | 362 | /* Do visual mode */ |
|---|
| | 363 | if(per_page == 1 && o_funcs.xattr) { |
|---|
| | 364 | char c = *o_funcs.xchar(oid); |
|---|
| | 365 | byte a = *o_funcs.xattr(oid); |
|---|
| | 366 | c_put_str(attr, format((c & 0x80) ? "%02x/%02x" : "%02x/%d", a, c), row + i, 60); |
|---|
| | 367 | } |
|---|
| | 368 | } |
|---|
| | 369 | |
|---|
| | 370 | /* Clear remaining lines */ |
|---|
| | 371 | for (; i < per_page; i++) |
|---|
| | 372 | { |
|---|
| | 373 | Term_erase(col, row + i, 255); |
|---|
| | 374 | } |
|---|
| | 375 | } |
|---|
| | 376 | |
|---|
| | 377 | |
|---|
| | 378 | /* |
|---|
| | 379 | * Interactive group by. |
|---|
| | 380 | * Recognises inscriptions, graphical symbols, lore |
|---|
| | 381 | */ |
|---|
| | 382 | static void display_knowledge(const char *title, int *obj_list, int o_count, |
|---|
| | 383 | group_funcs g_funcs, member_funcs o_funcs, |
|---|
| | 384 | const char *otherfields) |
|---|
| | 385 | { |
|---|
| | 386 | /* maximum number of groups to display */ |
|---|
| | 387 | int max_group = g_funcs.maxnum < o_count ? g_funcs.maxnum : o_count ; |
|---|
| | 388 | |
|---|
| | 389 | /* This could (should?) be (void **) */ |
|---|
| | 390 | int *g_list, *g_offset; |
|---|
| | 391 | |
|---|
| | 392 | const char **g_names; |
|---|
| | 393 | |
|---|
| | 394 | int g_name_len = 8; /* group name length, minumum is 8 */ |
|---|
| | 395 | |
|---|
| | 396 | int grp_cnt = 0; /* total number groups */ |
|---|
| | 397 | |
|---|
| | 398 | int g_cur = 0, grp_old = -1, grp_top = 0; /* group list positions */ |
|---|
| | 399 | int o_cur = 0, object_top = 0; /* object list positions */ |
|---|
| | 400 | int g_o_count = 0; /* object count for group */ |
|---|
| | 401 | int o_first = 0, g_o_max = 0; /* group limits in object list */ |
|---|
| | 402 | int oid = -1, old_oid = -1; /* object identifiers */ |
|---|
| | 403 | |
|---|
| | 404 | /* display state variables */ |
|---|
| | 405 | bool visual_list = FALSE; |
|---|
| | 406 | byte attr_top = 0; |
|---|
| | 407 | char char_left = 0; |
|---|
| | 408 | int note_idx = 0; |
|---|
| | 409 | |
|---|
| | 410 | int delay = 0; |
|---|
| | 411 | int column = 0; |
|---|
| | 412 | |
|---|
| | 413 | bool flag = FALSE; |
|---|
| | 414 | bool redraw = TRUE; |
|---|
| | 415 | |
|---|
| | 416 | int browser_rows; |
|---|
| | 417 | int wid, hgt; |
|---|
| | 418 | int i; |
|---|
| | 419 | int prev_g = -1; |
|---|
| | 420 | |
|---|
| | 421 | /* Get size */ |
|---|
| | 422 | Term_get_size(&wid, &hgt); |
|---|
| | 423 | browser_rows = hgt - 8; |
|---|
| | 424 | |
|---|
| | 425 | /* Do the group by. ang_sort only works on (void **) */ |
|---|
| | 426 | /* Maybe should make this a precondition? */ |
|---|
| | 427 | if(g_funcs.gcomp) |
|---|
| | 428 | qsort(obj_list, o_count, sizeof(*obj_list), g_funcs.gcomp); |
|---|
| | 429 | |
|---|
| | 430 | C_MAKE(g_list, max_group+1, int); |
|---|
| | 431 | C_MAKE(g_offset, max_group+1, int); |
|---|
| | 432 | |
|---|
| | 433 | for(i = 0; i < o_count; i++) { |
|---|
| | 434 | if(prev_g != g_funcs.group(obj_list[i])) { |
|---|
| | 435 | prev_g = g_funcs.group(obj_list[i]); |
|---|
| | 436 | g_offset[grp_cnt] = i; |
|---|
| | 437 | g_list[grp_cnt++] = prev_g; |
|---|
| | 438 | } |
|---|
| | 439 | } |
|---|
| | 440 | g_offset[grp_cnt] = o_count; |
|---|
| | 441 | g_list[grp_cnt] = -1; |
|---|
| | 442 | |
|---|
| | 443 | |
|---|
| | 444 | /* The compact set of group names, in display order */ |
|---|
| | 445 | C_MAKE(g_names, grp_cnt, const char **); |
|---|
| | 446 | for (i = 0; i < grp_cnt; i++) { |
|---|
| | 447 | int len; |
|---|
| | 448 | g_names[i] = g_funcs.name(g_list[i]); |
|---|
| | 449 | len = strlen(g_names[i]); |
|---|
| | 450 | if(len > g_name_len) g_name_len = len; |
|---|
| | 451 | } |
|---|
| | 452 | if(g_name_len >= 20) g_name_len = 20; |
|---|
| | 453 | |
|---|
| | 454 | &n |
|---|