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