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