1 /* SCCS Id: @(#)questpgr.c 3.4 2000/05/05 */
2 /* Copyright 1991, M. Stephenson */
3 /* NetHack may be freely redistributed. See license for details. */
4
5 #include "hack.h"
6 #include "dlb.h"
7
8 /* quest-specific pager routines. */
9
10 #define QTEXT_AREA FILE_AREA_SHARE
11 #include "qtext.h"
12
13 #define QTEXT_FILE "quest.dat"
14
15 /* #define DEBUG */ /* uncomment for debugging */
16
17 static void FDECL(Fread, (genericptr_t,int,int,dlb *));
18 STATIC_DCL struct qtmsg * FDECL(construct_qtlist, (long));
19 STATIC_DCL const char * NDECL(intermed);
20 STATIC_DCL const char * NDECL(creatorname);
21 STATIC_DCL const char * NDECL(neminame);
22 STATIC_DCL const char * NDECL(guardname);
23 STATIC_DCL const char * NDECL(homebase);
24 STATIC_DCL struct qtmsg * FDECL(msg_in, (struct qtmsg *,int));
25 STATIC_DCL void FDECL(convert_arg, (CHAR_P));
26 STATIC_DCL void NDECL(convert_line);
27 STATIC_DCL void FDECL(deliver_by_pline, (struct qtmsg *));
28 STATIC_DCL void FDECL(deliver_by_window, (struct qtmsg *,int));
29
30 static char in_line[80], cvt_buf[64], out_line[128];
31 static struct qtlists qt_list;
32 static dlb *msg_file;
33 /* used by ldrname() and neminame(), then copied into cvt_buf */
34 static char nambuf[sizeof cvt_buf];
35
36 #ifdef DEBUG
37 static void NDECL(dump_qtlist);
38
39 static void
dump_qtlist()40 dump_qtlist() /* dump the character msg list to check appearance */
41 {
42 struct qtmsg *msg;
43 long size;
44
45 for (msg = qt_list.chrole; msg->msgnum > 0; msg++) {
46 pline("msgnum %d: delivery %c",
47 msg->msgnum, msg->delivery);
48 more();
49 (void) dlb_fseek(msg_file, msg->offset, SEEK_SET);
50 deliver_by_window(msg, NHW_TEXT);
51 }
52 }
53 #endif /* DEBUG */
54
55 static void
Fread(ptr,size,nitems,stream)56 Fread(ptr, size, nitems, stream)
57 genericptr_t ptr;
58 int size, nitems;
59 dlb *stream;
60 {
61 int cnt;
62
63 if ((cnt = dlb_fread(ptr, size, nitems, stream)) != nitems) {
64
65 panic("PREMATURE EOF ON QUEST TEXT FILE! Expected %d bytes, got %d",
66 (size * nitems), (size * cnt));
67 }
68 }
69
70 STATIC_OVL struct qtmsg *
construct_qtlist(hdr_offset)71 construct_qtlist(hdr_offset)
72 long hdr_offset;
73 {
74 struct qtmsg *msg_list;
75 int n_msgs;
76
77 (void) dlb_fseek(msg_file, hdr_offset, SEEK_SET);
78 Fread(&n_msgs, sizeof(int), 1, msg_file);
79 msg_list = (struct qtmsg *)
80 alloc((unsigned)(n_msgs+1)*sizeof(struct qtmsg));
81
82 /*
83 * Load up the list.
84 */
85 Fread((genericptr_t)msg_list, n_msgs*sizeof(struct qtmsg), 1, msg_file);
86
87 msg_list[n_msgs].msgnum = -1;
88 return(msg_list);
89 }
90
91 void
load_qtlist()92 load_qtlist()
93 {
94
95 int n_classes, i;
96 char qt_classes[N_HDR][LEN_HDR];
97 long qt_offsets[N_HDR];
98
99 msg_file = dlb_fopen_area(QTEXT_AREA, QTEXT_FILE, RDBMODE);
100 if (!msg_file)
101 panic("CANNOT OPEN QUEST TEXT FILE %s.", QTEXT_FILE);
102
103 /*
104 * Read in the number of classes, then the ID's & offsets for
105 * each header.
106 */
107
108 Fread(&n_classes, sizeof(int), 1, msg_file);
109 Fread(&qt_classes[0][0], sizeof(char)*LEN_HDR, n_classes, msg_file);
110 Fread(qt_offsets, sizeof(long), n_classes, msg_file);
111
112 /*
113 * Now construct the message lists for quick reference later
114 * on when we are actually paging the messages out.
115 */
116
117 qt_list.common = qt_list.chrole = (struct qtmsg *)0;
118
119 for (i = 0; i < n_classes; i++) {
120 if (!strncmp(COMMON_ID, qt_classes[i], LEN_HDR))
121 qt_list.common = construct_qtlist(qt_offsets[i]);
122 else if (!strncmp(urole.filecode, qt_classes[i], LEN_HDR))
123 qt_list.chrole = construct_qtlist(qt_offsets[i]);
124 #if 0 /* UNUSED but available */
125 else if (!strncmp(urace.filecode, qt_classes[i], LEN_HDR))
126 qt_list.chrace = construct_qtlist(qt_offsets[i]);
127 #endif
128 }
129
130 if (!qt_list.common || !qt_list.chrole)
131 impossible("load_qtlist: cannot load quest text.");
132 #ifdef DEBUG
133 dump_qtlist();
134 #endif
135 return; /* no ***DON'T*** close the msg_file */
136 }
137
138 /* called at program exit */
139 void
unload_qtlist()140 unload_qtlist()
141 {
142 if (msg_file)
143 (void) dlb_fclose(msg_file), msg_file = 0;
144 if (qt_list.common)
145 free((genericptr_t) qt_list.common), qt_list.common = 0;
146 if (qt_list.chrole)
147 free((genericptr_t) qt_list.chrole), qt_list.chrole = 0;
148 return;
149 }
150
151 short
quest_info(typ)152 quest_info(typ)
153 int typ;
154 {
155 switch (typ) {
156 case 0: return (urole.questarti);
157 case MS_LEADER: return (urole.ldrnum);
158 case MS_NEMESIS: return (urole.neminum);
159 case MS_GUARDIAN: return (urole.guardnum);
160 default: warning("quest_info(%d)", typ);
161 }
162 return 0;
163 }
164
165 const char *
ldrname()166 ldrname() /* return your role leader's name */
167 {
168 int i = urole.ldrnum;
169
170 Sprintf(nambuf, "%s%s",
171 type_is_pname(&mons[i]) ? "" : "the ",
172 mons[i].mname);
173 return nambuf;
174 }
175
176 STATIC_OVL const char *
intermed()177 intermed() /* return your intermediate target string */
178 {
179 return (urole.intermed);
180 }
181
182 boolean
is_quest_artifact(otmp)183 is_quest_artifact(otmp)
184 struct obj *otmp;
185 {
186 return((boolean)(otmp->oartifact == urole.questarti));
187 }
188
189 STATIC_OVL const char *
neminame()190 neminame() /* return your role nemesis' name */
191 {
192 int i = urole.neminum;
193
194 Sprintf(nambuf, "%s%s",
195 type_is_pname(&mons[i]) ? "" : "the ",
196 mons[i].mname);
197 return nambuf;
198 }
199
200 STATIC_OVL const char *
guardname()201 guardname() /* return your role leader's guard monster name */
202 {
203 int i = urole.guardnum;
204
205 return(mons[i].mname);
206 }
207
208 STATIC_OVL const char *
homebase()209 homebase() /* return your role leader's location */
210 {
211 return(urole.homebase);
212 }
213
214 STATIC_OVL struct qtmsg *
msg_in(qtm_list,msgnum)215 msg_in(qtm_list, msgnum)
216 struct qtmsg *qtm_list;
217 int msgnum;
218 {
219 struct qtmsg *qt_msg;
220
221 for (qt_msg = qtm_list; qt_msg->msgnum > 0; qt_msg++)
222 if (qt_msg->msgnum == msgnum) return(qt_msg);
223
224 return((struct qtmsg *)0);
225 }
226
227 STATIC_OVL void
convert_arg(c)228 convert_arg(c)
229 char c;
230 {
231 register const char *str;
232
233 switch (c) {
234
235 case 'p': str = plname;
236 break;
237 case 'c': str = (flags.female && urole.name.f) ?
238 urole.name.f : urole.name.m;
239 break;
240 case 'r': str = rank_of(u.ulevel, Role_switch, flags.female);
241 break;
242 case 'R': str = rank_of(MIN_QUEST_LEVEL, Role_switch,
243 flags.female);
244 break;
245 case 's': str = (flags.female) ? "sister" : "brother";
246 break;
247 case 'S': str = (flags.female) ? "daughter" : "son";
248 break;
249 case 'l': str = ldrname();
250 break;
251 case 'i': str = intermed();
252 break;
253 case 'o': str = the(artiname(urole.questarti));
254 break;
255 case 'm': str = creatorname();
256 break;
257 case 'n': str = neminame();
258 break;
259 case 'g': str = guardname();
260 break;
261 case 'G': str = align_gtitle(u.ualignbase[A_ORIGINAL]);
262 break;
263 case 'H': str = homebase();
264 break;
265 case 'a': str = align_str(u.ualignbase[A_ORIGINAL]);
266 break;
267 case 'A': str = align_str(u.ualign.type);
268 break;
269 case 'd': str = align_gname(u.ualignbase[A_ORIGINAL]);
270 break;
271 case 'D': str = align_gname(A_LAWFUL);
272 break;
273 case 'C': str = "chaotic";
274 break;
275 case 'N': str = "neutral";
276 break;
277 case 'L': str = "lawful";
278 break;
279 case 'x': str = Blind ? "sense" : "see";
280 break;
281 case 'Z': str = dungeons[0].dname;
282 break;
283 case '%': str = "%";
284 break;
285 default: str = "";
286 break;
287 }
288 Strcpy(cvt_buf, str);
289 }
290
291 STATIC_OVL void
convert_line()292 convert_line()
293 {
294 char *c, *cc;
295
296 cc = out_line;
297 for (c = in_line; *c; c++) {
298
299 *cc = 0;
300 switch(*c) {
301
302 case '\r':
303 case '\n':
304 *(++cc) = 0;
305 return;
306
307 case '%':
308 if (*(c+1)) {
309 convert_arg(*(++c));
310 switch (*(++c)) {
311
312 /* insert "a"/"an" prefix */
313 case 'A': Strcat(cc, An(cvt_buf));
314 cc += strlen(cc);
315 continue; /* for */
316 case 'a': Strcat(cc, an(cvt_buf));
317 cc += strlen(cc);
318 continue; /* for */
319
320 /* capitalize */
321 case 'C': cvt_buf[0] = highc(cvt_buf[0]);
322 break;
323
324 /* pluralize */
325 case 'P': cvt_buf[0] = highc(cvt_buf[0]);
326 case 'p': Strcpy(cvt_buf, makeplural(cvt_buf));
327 break;
328
329 /* append possessive suffix */
330 case 'S': cvt_buf[0] = highc(cvt_buf[0]);
331 case 's': Strcpy(cvt_buf, s_suffix(cvt_buf));
332 break;
333
334 /* strip any "the" prefix */
335 case 't': if (!strncmpi(cvt_buf, "the ", 4)) {
336 Strcat(cc, &cvt_buf[4]);
337 cc += strlen(cc);
338 continue; /* for */
339 }
340 break;
341
342 default: --c; /* undo switch increment */
343 break;
344 }
345 Strcat(cc, cvt_buf);
346 cc += strlen(cvt_buf);
347 break;
348 } /* else fall through */
349
350 default:
351 *cc++ = *c;
352 break;
353 }
354 }
355 if (cc >= out_line + sizeof out_line)
356 panic("convert_line: overflow");
357 *cc = 0;
358 return;
359 }
360
361 char *
string_subst(str)362 string_subst(str)
363 char *str;
364 {
365 strncpy(in_line, str, 79);
366 in_line[79] = '\0';
367 convert_line();
368 return out_line;
369 }
370
371
372 STATIC_OVL void
deliver_by_pline(qt_msg)373 deliver_by_pline(qt_msg)
374 struct qtmsg *qt_msg;
375 {
376 long size;
377 char xbuf[BUFSZ];
378
379 for (size = 0; size < qt_msg->size; size += (long)strlen(in_line)) {
380 (void) dlb_fgets(xbuf, 80, msg_file);
381 (void) xcrypt(xbuf, in_line);
382 convert_line();
383 pline("%s", out_line);
384 }
385
386 }
387
388 STATIC_OVL void
deliver_by_window(qt_msg,how)389 deliver_by_window(qt_msg, how)
390 struct qtmsg *qt_msg;
391 int how;
392 {
393 long size;
394 char xbuf[BUFSZ];
395 winid datawin = create_nhwindow(how);
396
397 for (size = 0; size < qt_msg->size; size += (long)strlen(in_line)) {
398 (void) dlb_fgets(xbuf, 80, msg_file);
399 (void) xcrypt(xbuf, in_line);
400 convert_line();
401 putstr(datawin, 0, out_line);
402 }
403 display_nhwindow(datawin, TRUE);
404 destroy_nhwindow(datawin);
405 }
406
407 void
qt_com_firstline(msgnum,msgbuf)408 qt_com_firstline(msgnum, msgbuf)
409 int msgnum;
410 char *msgbuf;
411 {
412 struct qtmsg *qt_msg;
413 char xbuf[BUFSZ];
414
415 if (!(qt_msg = msg_in(qt_list.common, msgnum))) {
416 impossible("qt_com_firstline: message %d not found.", msgnum);
417 *msgbuf = 0;
418 return;
419 }
420
421 (void) dlb_fseek(msg_file, qt_msg->offset, SEEK_SET);
422 (void) dlb_fgets(xbuf, 80, msg_file);
423 (void) xcrypt(xbuf, in_line);
424 convert_line();
425 strcpy(msgbuf, out_line);
426 }
427
428 void
com_pager(msgnum)429 com_pager(msgnum)
430 int msgnum;
431 {
432 struct qtmsg *qt_msg;
433
434 if (!(qt_msg = msg_in(qt_list.common, msgnum))) {
435 warning("com_pager: message %d not found.", msgnum);
436 return;
437 }
438
439 (void) dlb_fseek(msg_file, qt_msg->offset, SEEK_SET);
440 if (qt_msg->delivery == 'p') deliver_by_pline(qt_msg);
441 else if (qt_msg->delivery == 'm') deliver_by_window(qt_msg, NHW_MENU);
442 else deliver_by_window(qt_msg, NHW_TEXT);
443 return;
444 }
445
446 void
qt_pager(msgnum)447 qt_pager(msgnum)
448 int msgnum;
449 {
450 struct qtmsg *qt_msg;
451
452 if (!(qt_msg = msg_in(qt_list.chrole, msgnum))) {
453 warning("qt_pager: message %d not found.", msgnum);
454 return;
455 }
456
457 (void) dlb_fseek(msg_file, qt_msg->offset, SEEK_SET);
458 if (qt_msg->delivery == 'p' && strcmp(windowprocs.name, "X11"))
459 deliver_by_pline(qt_msg);
460 else deliver_by_window(qt_msg, NHW_TEXT);
461 return;
462 }
463
464 /** The names of creator deities from different cultures. */
465 static const char *creator_names[] = {
466 "Marduk", /* Babylonian */
467 "Apsu", /* Babylonian */
468 "Aeon", /* Greek */
469 "Gaia", /* Greek */
470 "Khronos", /* Greek */
471 "Atum", /* Egyptian */
472 "Khepri", /* Egyptian */
473 "Kamui", /* Ainu */
474 "Mbombo", /* Bakuba */
475 "Unkulunkulu", /* Zulu */
476 "Vishvakarman", /* Vedic */
477 "Brahma", /* Hindu */
478 "Coatlique", /* Aztec */
479 "Viracocha", /* Inca */
480 "Tepeu", /* Maya */
481 "Pangu", /* Chinese */
482 "Bulaing", /* Australian */
483 "Ahura Mazda", /* Zoroastrian */
484 "Demiourgos", /* Platon */
485 };
486
487 /** Return the name of the creator deity.
488 * The name stays the same for the running game. */
489 STATIC_OVL const char *
creatorname()490 creatorname()
491 {
492 if (pirateday()) {
493 return "the FSM";
494 } else if (discordian_holiday()) {
495 return (u.ubirthday % 2) ? "Discordia" : "Eris";
496 } else {
497 int index = u.ubirthday % SIZE(creator_names);
498 return creator_names[index];
499 }
500 }
501
502 /*questpgr.c*/
503