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*/