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