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