1 #ifdef RCSID
2 static char RCSid[] =
3 "$Header: d:/cvsroot/tads/TADS2/PLY.C,v 1.3 1999/05/29 15:51:02 MJRoberts Exp $";
4 #endif
5
6 /*
7 * Copyright (c) 1992, 2002 Michael J. Roberts. All Rights Reserved.
8 *
9 * Please see the accompanying license file, LICENSE.TXT, for information
10 * on using and copying this software.
11 */
12 /*
13 Name
14 ply.c - play game
15 Function
16 executes a game, starting with the 'init' function
17 Notes
18 none
19 Modified
20 04/04/92 MJRoberts - creation
21 */
22
23 #include "os.h"
24 #include "std.h"
25 #include "run.h"
26 #include "voc.h"
27 #include "err.h"
28 #include "obj.h"
29 #include "fio.h"
30 #include <ctype.h>
31
32 #include "ply.h"
33
34 /*
35 * Play the game
36 */
plygo(runcxdef * run,voccxdef * voc,tiocxdef * tio,objnum preinit,char * restore_fname)37 void plygo(runcxdef *run, voccxdef *voc, tiocxdef *tio, objnum preinit,
38 char *restore_fname)
39 {
40 int err;
41 errcxdef *ec = run->runcxerr;
42 char filbuf[128];
43 int first_time;
44 int noreg inited = FALSE;
45
46 NOREG((&inited));
47
48 first_time = TRUE;
49
50 /*
51 * Write out the special <?T2> HTML sequence, in case we're on an HTML
52 * system. This tells the HTML parser to use the parsing rules for
53 * TADS 2 callers.
54 */
55 outformat("\\H+<?T2>\\H-");
56
57 startover:
58 if (!inited)
59 {
60 /* use Me as the format-string actor for preinit and init */
61 tiosetactor(voc->voccxtio, voc->voccxme);
62
63 /*
64 * Run preinit, if it hasn't been run yet. Note that we only
65 * do this the first time through. If we come back here via the
66 * restart function, preinit will already have been run in the
67 * restart function itself, so we don't need to run it again.
68 */
69 if (first_time)
70 {
71 /* make a note that we've been through here once already */
72 first_time = FALSE;
73
74 /* remember the preinit function for later use in restarting */
75 voc->voccxpreinit = preinit;
76
77 /* run the preinit() function */
78 ERRBEGIN(ec)
79 {
80 /* reset the interpreter */
81 runrst(run);
82
83 /* reset the parser */
84 voc_stk_ini(voc, (uint)VOC_STACK_SIZE);
85
86 /* run preinit */
87 if (preinit != MCMONINV)
88 runfn(run, preinit, 0);
89 }
90 ERRCATCH(ec, err)
91 {
92 /* if they restarted, go back and start over */
93 if (err == ERR_RUNRESTART)
94 goto startover;
95
96 /* resignal the error */
97 errrse(ec);
98 }
99 ERREND(ec);
100 }
101
102 /*
103 * Run the "init" function. Do NOT run init if we're restoring
104 * a game directly from the command line AND there's an
105 * initRestore function defined.
106 */
107 if (restore_fname == 0 || voc->voccxinitrestore == MCMONINV)
108 {
109 ERRBEGIN(ec)
110 {
111 /* reset the interpreter */
112 runrst(run);
113
114 /* reset the parser */
115 voc_stk_ini(voc, (uint)VOC_STACK_SIZE);
116
117 /* run init */
118 runfn(run, (objnum)voc->voccxini, 0);
119 }
120 ERRCATCH(ec, err)
121 {
122 /* if they restarted, go back and start over */
123 if (err == ERR_RUNRESTART)
124 goto startover;
125
126 /* resignal the error */
127 errrse(ec);
128 }
129 ERREND(ec);
130 }
131 }
132
133 /* next time through, we'll need to run init again */
134 inited = FALSE;
135
136 /*
137 * check for startup parameter file to restore - if there's a
138 * system-specific parameter file specified, pretend that it was
139 * specified as the restore file
140 */
141 if (os_paramfile(filbuf))
142 restore_fname = filbuf;
143
144 /* check for a file to restore */
145 if (restore_fname != 0)
146 {
147 /*
148 * Check to see if the game file supports the initRestore
149 * function. If so, call it to restore the game. If not,
150 * restore the game directly.
151 */
152 if (voc->voccxinitrestore != MCMONINV)
153 {
154 char restore_buf[OSFNMAX*2];
155 char *src;
156 char *dst;
157
158 /* convert any backslashes to double backslashes */
159 for (src = restore_fname, dst = restore_buf ;
160 *src != '\0' && dst + 2 < restore_buf + sizeof(restore_buf) ;
161 ++src)
162 {
163 switch(*src)
164 {
165 case '\\':
166 /* it's a backslash - double it */
167 *dst++ = '\\';
168 *dst++ = '\\';
169 break;
170
171 default:
172 /* copy the character as-is */
173 *dst++ = *src;
174 }
175 }
176
177 /*
178 * all the game's initRestore function with the name of
179 * saved game file to restore as the argument
180 */
181
182 /* reset the interpreter */
183 runrst(run);
184
185 /* reset the parser */
186 voc_stk_ini(voc, (uint)VOC_STACK_SIZE);
187
188 /* push the game file name and run initRestore */
189 runpstr(run, restore_buf, dst - restore_buf, 0);
190 runfn(run, (objnum)voc->voccxinitrestore, 1);
191 }
192 else
193 {
194 /* restore the game */
195 os_printz("\n\n[Restoring saved game]\n\n");
196 err = fiorso(voc, restore_fname);
197 if (err)
198 {
199 char buf[60 + OSFNMAX];
200
201 sprintf(buf, "\n\nError: unable to restore file \"%s\"\n\n",
202 restore_fname);
203 os_printz(buf);
204 }
205 }
206
207 /* forget the saved game name, in case we restore */
208 restore_fname = 0;
209 }
210
211 /* clear out the redo command buffer */
212 voc->voccxredobuf[0] = '\0';
213
214 /* read and execute commands */
215 for (;;)
216 {
217 char buf[128];
218
219 err = 0;
220 ERRBEGIN(ec)
221
222 /* read a new command if there's nothing to redo */
223 if (!voc->voccxredo)
224 {
225 /* reset hidden output so we're showing output */
226 tioshow(tio);
227 tioflush(tio);
228
229 /* clear the interpreter stack */
230 runrst(run);
231
232 /* read a command */
233 vocread(voc, MCMONINV, MCMONINV, buf, (int)sizeof(buf), 0);
234
235 /* special qa checking */
236 if (buf[0] == '@')
237 {
238 int quiet = FALSE;
239 char *p;
240
241 p = buf + 1;
242 if (*p == '@')
243 {
244 /* turn off MORE mode */
245 setmore(0);
246
247 /* set NONSTOP mode in the OS layer */
248 os_nonstop_mode(TRUE);
249
250 /* skip the extra '@' */
251 ++p;
252 }
253 else if (*p == '!')
254 {
255 quiet = TRUE;
256 ++p;
257 }
258 while (*p != '\0' && t_isspace(*p)) ++p;
259 if (*p != '\0')
260 {
261 /* open the named file */
262 qasopn(p, quiet);
263 }
264 else
265 {
266 char fname[256];
267
268 /* no file was named - ask the user to select a file */
269 if (tio_askfile("Read script file:", fname, sizeof(fname),
270 OS_AFP_OPEN, OSFTCMD) == 0)
271 qasopn(fname, quiet);
272 }
273 goto end_loop;
274 }
275 }
276
277 /*
278 * If there's redo in the redo buffer, use it now. If the
279 * buffer is empty and the redo flag is set, we'll just
280 * re-execute whatever's in our internal buffer.
281 */
282 if (voc->voccxredo && voc->voccxredobuf[0] != '\0')
283 {
284 /* copy the redo buffer into our internal buffer */
285 strcpy(buf, voc->voccxredobuf);
286
287 /* we've consumed it now, so clear it out */
288 voc->voccxredobuf[0] = '\0';
289 }
290
291 /* we've now consumed the redo */
292 voc->voccxredo = FALSE;
293
294 /* clear any pending break that's queued up */
295 (void)os_break();
296
297 /* execute the command */
298 (void)voccmd(voc, buf, (uint)sizeof(buf));
299
300 end_loop:
301 ERRCATCH(ec, err)
302 {
303 if (err != ERR_RUNQUIT && err != ERR_RUNRESTART)
304 errclog(ec);
305 }
306 ERREND(ec);
307
308 /* on interrupt, undo last command (which was partially executed) */
309 if (err == ERR_USRINT && voc->voccxundo)
310 {
311 ERRBEGIN(ec)
312 objundo(voc->voccxmem, voc->voccxundo);
313 ERRCATCH(ec, err)
314 if (err != ERR_NOUNDO && err != ERR_ICUNDO)
315 errrse(ec);
316 ERREND(ec)
317 }
318
319 /* if they want to quit, we're done */
320 if (err == ERR_RUNQUIT)
321 break;
322 else if (err == ERR_RUNRESTART)
323 goto startover;
324 }
325
326 /*
327 * If we're quitting, give the debugger one last chance at taking
328 * control. If it just returns, we can go ahead and terminate, but
329 * if it wants it can restart the game by calling bifrst() as
330 * normal.
331 */
332 ERRBEGIN(ec)
333 {
334 /* clear anything in the debugger stack trace */
335 run->runcxdbg->dbgcxfcn = 0;
336 run->runcxdbg->dbgcxdep = 0;
337
338 /* tell the debugger the game has exited */
339 dbguquitting(run->runcxdbg);
340 }
341 ERRCATCH(ec, err)
342 {
343 switch(err)
344 {
345 case ERR_RUNRESTART:
346 /* they restarted the game - re-enter the play loop */
347 goto startover;
348
349 case ERR_RUNQUIT:
350 /* quitting - proceed to return as normal */
351 break;
352
353 default:
354 /* resignal any other error */
355 errrse(ec);
356 }
357 }
358 ERREND(ec);
359 }
360
361