1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "glk/agt/agility.h"
24
25 namespace Glk {
26 namespace AGT {
27
28 /*
29 This is a mishmash of utilities and preinitialized arrays,
30 including the verblist, the metacommand token list,
31 and the dictionary routines.
32 */
33
34 /* ------------------------------------------------------------------- */
35 /* Preinitialized data structures */
36 /* Most of the preinitialized data structures used by all of the */
37 /* AGT-related programs go here . */
38 /* ------------------------------------------------------------------- */
39
40
41 /* ------------------------------------------------------------ */
42 /* The PC --> ASCII conversion table. This converts the 8th-bit */
43 /* PC characters to their nearest ASCII equivalent. */
44 /* ------------------------------------------------------------ */
45
46 const char trans_ibm[] =
47 "CueaaaaceeeiiiAA" /* 80 */
48 "E@@ooouuyOUc$$pf" /* 90 */
49 "aiounNao?....!<>" /* A0 */
50 "###|++|+++|\\/++\\" /* B0 */
51 "\\+++-+||\\/+++=+=" /* C0 */
52 "+=+++++++//@@@@@" /* D0 */
53 "abGpSsmtFTOd.fe^" /* E0 */
54 "=+><fj/=***/n2# "; /* F0 */
55
56
57 /* ------------------------------------------------------------- */
58 /* Tables of built in properties and attributes */
59 /* ------------------------------------------------------------- */
60
61 #define rnc(p) {#p,offsetof(room_rec,p),offsetof(noun_rec,p), \
62 offsetof(creat_rec,p)}
63 #define rn(p) {#p,offsetof(room_rec,p),offsetof(noun_rec,p),-1}
64 #define nc(p) {#p,-1,offsetof(noun_rec,p),offsetof(creat_rec,p)}
65 #define rc(p) {#p,offsetof(room_rec,p),-1,offsetof(creat_rec,p)}
66 #define r(p) {#p, offsetof(room_rec,p), -1, -1}
67 #define n(p) {#p, -1, offsetof(noun_rec,p),-1}
68 #define c(p) {#p, -1, -1, offsetof(creat_rec,p)}
69
70 const prop_struct proplist[NUM_PROP] = {
71 /* The following are writable */
72 rnc(oclass), rnc(points), r(light),
73 n(num_shots), n(weight), n(size),
74 c(counter), c(timecounter),
75 /* The remaining properties are read-only */
76 nc(location), rn(key), c(weapon), c(threshold), c(timethresh),
77 nc(flagnum)
78 };
79
80 const prop_struct attrlist[NUM_ATTR] = {
81 /* The following are writable */
82 n(on), n(open), n(locked), n(movable),
83 c(groupmemb), c(hostile),
84 /* The remaining attributes are read-only */
85 r(end), rn(win), r(killplayer), n(plural),
86 n(pushable), n(pullable), n(turnable), n(playable), n(readable), n(closable),
87 n(lockable), n(edible), n(wearable), n(drinkable), n(poisonous), n(light),
88 n(shootable), nc(isglobal),
89 /* This is writable again */
90 rnc(seen),
91 nc(proper) /* This is not writable */
92 };
93
94 #undef rnc
95 #undef rn
96 #undef rc
97 #undef cn
98 #undef r
99 #undef c
100 #undef n
101
102
103
104 /* ------------------------------------------------------------- */
105 /* Tables of Opcodes */
106 /* These gives the names and argument types of all of the AGT */
107 /* opcodes. */
108 /* ------------------------------------------------------------- */
109
110
111 /* All of the following are undefined again just after the table */
112
113 #define n AGT_NUM
114 #define v AGT_VAR
115 #define r AGT_ROOM
116 #define i AGT_ITEM
117
118 #define o (AGT_ITEM|AGT_CREAT) /* "object" */
119 #define l (r|o|AGT_NONE|AGT_SELF|AGT_WORN) /* "location" */
120
121
122
123 /* opcode, argnum, arg1, arg2 */
124 #ifdef LOWMEM
125 #define a(s) {"",0,0,0}
126 #define b(s,a1) {"",1,(a1),0}
127 #define c(s,a1,a2) {"",2,(a1),(a2)}
128 #else
129 #define a(s) {#s,0,0,0}
130 #define b(s,a1) {#s,1,(a1),0}
131 #define c(s,a1,a2) {#s,2,(a1),(a2)}
132 #endif
133
134 const opdef cond_def[] = {
135 b(AtLocation, r), b(AtLocationGT, n), b(AtLocationLT, n),
136 a(SongPlaying), a(SoundIsOn), a(DirectionOK), b(DirectionIs, AGT_DIR),
137 c(BetweenRooms, n, n), b(HasVisitedRoom, r),
138 a(EnteredObject), b(TimeGT, n), b(TimeLT, n),
139 a(FirstVisitToRoom),
140 a(NewLife),
141 a(IsCarryingSomething), a(IsCarryingNothing),
142 a(IsWearingSomething),
143 b(IsCarryingTreasure, n),
144 a(IsWearingNothing),
145 b(LoadWeightEquals, n), b(LoadWeightGT, n), b(LoadWeightLT, n),
146 b(Present, o), b(IsWearing, o), b(IsCarrying, o),
147 b(IsNowhere, o), b(IsSomewhere, o),
148 b(InRoom, o), c(IsLocated, o, l), c(Together, o, o),
149 b(IsON, o), b(IsOFF, o),
150 b(IsGroupMember, AGT_CREAT),
151 b(IsOpen, o), b(IsClosed, o), b(IsLocked, o), b(IsUnLocked, o),
152 b(IsEdible, o), b(IsDrinkable, o), b(IsPoisonous, o),
153 b(IsMovable, o),
154 a(NOUNPresent), a(NOUNIsWearing), a(NOUNIsCarrying),
155 a(NOUNIsNowhere), a(NOUNIsSomewhere),
156 a(NOUNInRoom), b(NOUNIsLocated, l),
157 a(NOUNIsOn), a(NOUNIsOff),
158 a(NOUNIsOpen), a(NOUNIsClosed), a(NOUNIsLocked), a(NOUNIsUnLocked),
159 a(NOUNIsEdible), a(NOUNIsDrinkable), a(NOUNIsPoisonous),
160 a(NOUNIsMovable),
161 b(NOUNpointsEquals, n), b(NOUNpointsGT, n), b(NOUNpointsLT, n),
162 b(NOUNweightEquals, n), b(NOUNweightGT, n), b(NOUNweightLT, n),
163 a(LightPresent), a(RoomNeedsLight),
164 b(FlagON, AGT_FLAG), b(FlagOFF, AGT_FLAG),
165 b(RoomFlagOn, AGT_ROOMFLAG), b(Room_PixHere, AGT_PIX),
166 b(RoomFlagOff, AGT_ROOMFLAG),
167 b(ScoreEquals, n), b(ScoreGT, n), b(ScoreLT, n),
168 b(NumberEquals, n), b(NumberGT, n), b(NumberLT, n),
169 a(AnswerIsCorrect), a(AnswerIsWrong),
170 b(TurnsEquals, n), b(TurnsGT, n), b(TurnsLT, n),
171 c(CounterEquals, AGT_CNT, n), c(CounterGT, AGT_CNT, n), c(CounterLT, AGT_CNT, n),
172 c(VariableEquals, v | n, n), c(VariableGT, v | n, n), c(VariableLT, v | n, n),
173 c(CompareVariables, v, v), c(VariableChance, v | n, n),
174 a(NamePresent), b(NameIsNumber, o | AGT_NONE), /* QQ:Not sure about these */
175 b(NOUNIsNumber, o | AGT_NONE), b(ObjectIsNumber, o | AGT_NONE),
176 b(SomethingInside, r | o | AGT_SELF),
177 b(Chance, n),
178 a(PromptForYES), a(PromptForNO),
179 a(VerbIsDirection),
180 a(NOUNIsCreature),
181 a(NOUNIsMan), a(NOUNIsWoman), a(NOUNIsThing),
182 a(OBJECTIsMan), a(OBJECTIsWoman), a(OBJECTIsThing),
183 a(ObjectIsCreature),
184 a(ObjectPresent),
185 a(NOT), a(OR),
186 a(BeforeCommand), a(AfterCommand), /* 110,111 */
187 b(HourEquals, n), b(HourGT, n), b(HourLT, n),
188 b(MinuteEq, n), b(MinuteGT, n), b(MinuteLT, n),
189 a(IsAM),
190
191 a(OnDisambig),
192 b(IsHostile, o), a(HostilePresent),
193 a(NameWasPresent), a(OncePerTurn),
194 c(IsClass, r | o, AGT_NONE | r | o),
195 c(AttrOn, r | o, AGT_ATTR),
196 a(NumericNOUN), a(NumericOBJECT),
197 c(Equal, n, n), c(GT, n, n), c(LT, n, n), c(GE, n, n), c(LE, n, n),
198 c(CaseCompareStrings, AGT_STR, AGT_STR), c(CaseStringBefore, AGT_STR, AGT_STR),
199 c(CaseStringAfter, AGT_STR, AGT_STR),
200 c(CompareStrings, AGT_STR, AGT_STR), c(StringBefore, AGT_STR, AGT_STR),
201 c(StringAfter, AGT_STR, AGT_STR),
202 c(StringIsAnswer, AGT_STR, AGT_QUEST),
203 b(HasSeen, r | o),
204 c(ObjFlagON, r | o, AGT_OBJFLAG),
205 c(ObjFlagOFF, r | o, AGT_OBJFLAG),
206 c(CanGo, r | o | AGT_SELF, AGT_DIR)
207 };
208
209
210 const opdef act_def[] = {
211 b(GoToRoom, r), c(GoToRandomRoom, r, r),
212 b(MakeVarRoomNum, v), b(MakeVarNounNum, v), b(MakeVarObjectNum, v),
213 b(GoToVariableRoom, v | r), c(SendToVariableRoom, o, v | l),
214 b(GetVariableIt, v | o), b(PrintVariableMessage, v | AGT_MSG),
215 b(GetIt, o), b(WearIt, o), b(DropIt, o), b(RemoveIt, o),
216 b(LoadFont, AGT_FONT), b(ShowPicture, AGT_PIC), c(ChangePicture, AGT_PIC, AGT_PIC),
217 b(IfYShowPicture, AGT_PIC),
218 b(ShowRoom_Pix, AGT_PIX), b(IfYShowRoom_Pix, AGT_PIX),
219 b(PlaySong, AGT_SONG), c(PlayRandom, n, n), b(RepeatSong, AGT_SONG),
220 a(EndRepeatSong), a(StopSong), a(SuspendSong), a(ResumeSong),
221 b(ToggleMovable, i), c(ChangeDescr, r | o, AGT_MSG), c(ChangePoints, r | o, n),
222 a(DestroyOBJECT), b(GetString, AGT_STR),
223 b(GetVariable, v), b(SetVariableToTime, v), b(SetTimeToVariable, v | n),
224 b(SetTime, n), b(AddToTime, n), b(SetDeltaTime, n),
225 b(DoSubroutine, AGT_SUB), a(Return),
226 a(GetNOUN), a(WearNOUN), a(DropNOUN), a(RemoveNOUN),
227 a(DropEverything), a(RemoveEverything), a(KillPlayer),
228 b(PutInCurrentRoom, o), c(SendToRoom, o, l),
229 c(RePosition, o, l),
230 a(PutNOUNInCurrentRoom), b(SendNOUNToRoom, l),
231 b(SendAllToRoom, l), c(SendTreasuresToRoom, l, n),
232 c(RelocateAll, l, l),
233 b(Destroy, o), a(DestroyNOUN),
234 c(SwapLocations, o, o), c(SendToItem, o, o), b(SendNOUNtoItem, o),
235 b(AddToGroup, AGT_CREAT), b(RemoveFromGroup, AGT_CREAT), b(MoveTheGroup, l),
236 a(RedirectTo),
237 c(RandomMessage, AGT_MSG, AGT_MSG), b(ShowContents, r | o | AGT_SELF | AGT_WORN),
238 b(OpenIt, i), b(CloseIt, i), b(LockIt, i), b(UnlockIt, i),
239 a(OpenNOUN), a(CloseNOUN), a(LockNOUN), a(UnlockNOUN),
240 a(ShowScore), b(PlusScore, n), b(MinusScore, n),
241 a(ShowInventory), a(WaitForReturn), a(TimePasses),
242 b(Delay, n),
243 a(ClearScreen),
244 b(DescribeThing, r | o), a(LookAtRoom),
245 b(PrintMessage, AGT_MSG), a(BlankLine), c(Tone, n, n),
246 c(GetNumberInput, n, n), b(AskQuestion, AGT_QUEST),
247 c(ChangePassageway, AGT_DIR, AGT_EXIT),
248 b(TurnFlagOn, AGT_FLAG), b(TurnFlagOff, AGT_FLAG), b(ToggleFlag, AGT_FLAG),
249 b(TurnRoomFlagOn, AGT_ROOMFLAG), b(TurnRoomFlagOff, AGT_ROOMFLAG),
250 b(ToggleRoomFlag, AGT_ROOMFLAG),
251 b(TurnCounterOn, AGT_CNT), b(TurnCounterOff, AGT_CNT),
252 c(SetVariableTo, v, n), c(AddToVariable, v | n, n), c(SubtractFromVariable, v | n, n),
253 c(AddVariables, v, v), c(SubtractVariables, v, v),
254 c(RandomVariable, v, n),
255 b(NounToVariable, v), b(ObjectToVariable, v),
256 b(Quote, AGT_MSG),
257 b(TimePlus, n), b(TimeMinus, n), b(SetHour, n), b(SetMinute, n),
258 b(TimePlusVariable, v | n), b(TimeMinusVariable, v | n),
259 b(SetHourToVariable, v | n), b(SetMinutesToVariable, v | n),
260
261 b(SubtractFromTime, n), b(SetDisambigPriority, n),
262 b(SetVariableToDeltaTime, v), b(ChangeStatus, n),
263 c(MultiplyVariable, v | n, n), c(DivideVariable, v | n, n),
264 c(ComputeRemainder, v | n, n),
265 a(WaitForKey),
266 b(SetHE, o), b(SetSHE, o), b(SetIT, o), b(SetTHEY, o),
267 b(PrintMessageNoNL, AGT_MSG),
268 b(StandardMessage, AGT_ERR),
269 b(FailMessage, AGT_MSG), b(FailStdMessage, AGT_ERR),
270 c(ErrMessage, n, AGT_MSG), c(ErrStdMessage, n, AGT_ERR),
271 a(AND),
272 c(SetClass, r | o, AGT_NONE | r | o),
273 c(SetVariableToClass, v, r | o),
274 b(PushStack, n), b(PopStack, v),
275 a(AddStack), a(SubStack), a(MultStack), a(DivStack), a(ModStack),
276 a(DupStack), a(DiscardStack),
277 b(SetVariableToInput, v),
278 c(TurnAttrOn, r | o, AGT_ATTR), c(TurnAttrOff, r | o, AGT_ATTR),
279 c(PushProp, r | o, AGT_PROP), c(PopProp, r | o, AGT_PROP),
280 b(Goto, n), b(OnFailGoto, n),
281 b(EndDisambig, n),
282 b(XRedirect, n),
283 c(CopyString, AGT_STR, AGT_STR),
284 b(UpcaseString, AGT_STR), b(DowncaseString, AGT_STR),
285 c(TurnObjFlagON, r | o, AGT_OBJFLAG), c(TurnObjFlagOFF, r | o, AGT_OBJFLAG),
286 c(ToggleObjFlag, r | o, AGT_OBJFLAG),
287 c(PushObjProp, r | o, AGT_OBJPROP),
288 c(PopObjProp, r | o, AGT_OBJPROP),
289 c(MoveInDirection, o | AGT_SELF, AGT_DIR)
290 };
291
292 const opdef end_def[] = {
293 a(WinGame), a(EndGame),
294 a(QuitThisCMD), a(QuitAllCMDs), a(DoneWithTurn)
295 };
296
297 const opdef illegal_def = a(ILLEGAL);
298
299 #undef a
300 #undef b
301 #undef c
302
303 #undef n
304 #undef v
305 #undef r
306 #undef i
307 #undef o
308 #undef l
309
310
311
312
313 /* ------------------------------------------------------------- */
314 /* Opcode Translation Tables */
315 /* These convert opcode numbers from the various AGT versions */
316 /* to a uniform coding. */
317 /* ------------------------------------------------------------- */
318
319 /*NOTE this is being changed so that rather than the second term
320 is an absolute offset of the first term. Still applies to ranges
321 up until next one. Also incorporates the +1000 correction
322 into the correction set itself. (to avoid further problems
323 when including more opcodes, e.g. AGT 1.83).
324 The last table entry is now marked by a new value of -1.*/
325
326 /* Versions of the command set:
327 v1.21 apparantly has a compatible command set w/ 1.7 (!)
328 [except that their maxcmd is apparantly 22, not 30]
329 1.0 doesn't; it seems to have an EOC code of 154, as opposed to
330 165 or so.
331 1.18 seems to be slightly different from 1.7, but seemingly only
332 by one opcode.
333 [And of course both ME and 1.8 have their own extended command sets]
334 */
335
336 static const cmd_fix_rec FIX_ME[] = /* No longer using this as baseline */
337 { {0, 0},
338 {110, 1000}, /* i.e. commands moved to start at opcode 1000 */
339 {215, WIN_ACT},
340 {220, -1}
341 };
342
343 static const cmd_fix_rec FIX_ME0[] =
344 /* 169 */
345 { {0, 0},
346 {110, 1000},
347 {136, 1028}, /* Skip ToggleMoveable and ChangeDescr */
348 {156, 1049}, /* Skip RePosition */
349 {212, WIN_ACT},
350 {217, -1}
351 };
352
353 static const cmd_fix_rec FIX_ME0A[] = /* Pre-ME/1.0: */
354 /* 169 */
355 { {0, 0},
356 {110, 1000},
357 {130, 1021}, /* Skip PlayRandom */
358 {135, 1028}, /* Skip ToggleMoveable and ChangeDescr */
359 {155, 1049}, /* Skip RePosition */
360 {211, WIN_ACT},
361 {216, -1}
362 };
363
364 static const cmd_fix_rec FIX_ME15[] = {
365 {0, 0},
366 {110, 1000}, /* i.e. commands moved to start at opcode 1000 */
367 {158, 1049}, /* Skip the one opcode added in 1.56: RePosition */
368 {214, WIN_ACT},
369 {219, -1}
370 };
371
372 static const cmd_fix_rec FIX_135[] = {
373 {0, 0},
374 {3, 12},
375 {59, 71},
376 {88, 106},
377 {92, 1000},
378 {105, 1039}, /* 149 */
379 {114, 1049}, /* 159 */
380 {157, 1095}, /* 205 */
381 {167, WIN_ACT},
382 {172, -1}
383 };
384
385 static const cmd_fix_rec FIX_118[] = {
386 {0, 0},
387 {3, 12},
388 {59, 71},
389 {88, 106},
390 {92, 1000},
391 {105, 1039}, /* 149 */
392 {114, 1049}, /* 159 */
393 {118, 1054}, /* Skip SendTreasuresToRoom */
394 {156, 1095}, /* 205 */
395 {166, WIN_ACT},
396 {171, -1}
397 };
398
399
400 static const cmd_fix_rec FIX_182[] = {
401 {0, 0},
402 {3, 12},
403 {53, 110}, /* Shift BeforeCmd and AfterCmd */
404 {55, 62},
405 {61, 71},
406 {90, 106},
407 {94, 1000},
408 {107, 1039}, /* 149 */
409 {116, 1049}, /* 159 */
410 {143, 1105}, /* QUOTE-- need to move somewhere else */
411 {144, 1076},
412 {160, 1095}, /* 205 */
413 {170, WIN_ACT},
414 {175, -1}
415 };
416
417
418 static const cmd_fix_rec FIX_183[] = {
419 {0, 0},
420 {3, 12},
421 {55, 110}, /* Shift BeforeCmd and AfterCmd */
422 {57, 64},
423 {61, 71},
424 {90, 106},
425 {94, 112}, /* Time condition tokens */
426 {101, 1000},
427 {114, 1039},
428 {123, 1049},
429 {158, 1105}, /* QUOTE-- need to move somewhere else */
430 {159, 1084},
431 {167, 1095},
432 {169, 1106}, /* Time Action Tokens */
433 {177, 1097},
434 {185, WIN_ACT},
435 {190, -1}
436 };
437
438 static const cmd_fix_rec FIX_10[] = /* This *seems* to work */
439 { {0, 0},
440 {3, 12},
441 {59, 71},
442 {80, 95},
443 {84, 108},
444 {86, 1000},
445 {88, 1009},
446 {92, 1039},
447 {101, 1049},
448 {105, 1054},
449 {115, 1065},
450 {142, 1095},
451 {152, WIN_ACT},
452 {157, -1}
453 };
454
455 static const cmd_fix_rec FIX_15[] = /* This works */
456 { {0, 0},
457 {3, 12}, /* Skip 3-11 */
458 {60, 70}, /* Skip 69 */
459 /* {61,72}, */ /* Skip 71 -- WRONG! */
460 {90, 106}, /* Skip 101-105 */
461 {94, 1000},
462 {107, 1039}, /* skip 1013-1038 */
463 {116, 1049}, /* Skip 1048 */
464 {172, WIN_ACT},
465 {177, -1}
466 };
467
468 const fix_array FIX_LIST[] = /* An array of arrays, indexed by aver */
469 {
470 FIX_135, /* Aver=0: unknown format, might as well assume Classic */
471 FIX_10, FIX_118, FIX_135, FIX_135, FIX_135, FIX_182, FIX_183,
472 FIX_15, FIX_15, FIX_15, FIX_ME0, FIX_ME0A, FIX_ME15, FIX_ME15, FIX_ME
473 };
474
475
476 /* ------------------------------------------------------------- */
477 /* Miscellaneous collections of strings */
478 /* ------------------------------------------------------------- */
479
480 const char *verstr[] = {"????", "SMALL", "BIG", "MASTER", "SOGGY"};
481 const char *averstr[] = {"????", "1.0", "1.18",
482 "1.2", "1.32/COS", "Classic",
483 "1.82", "1.83",
484 "1.5/H", "1.5/F", "1.6",
485 "ME/1.0b", "ME/1.0a",
486 "ME/1.5", "ME/1.55", "ME/1.6",
487 "Magx"
488 };
489
490 const char *portstr = PORTSTR;
491 const char *version_str = "version 1.1.1";
492
493 const char nonestr[5] = {4, 'n', 'o', 'n', 'e'};
494 static const char NONEstr[5] = {4, 'N', 'O', 'N', 'E'};
495
496
497 /* Names of exits */
498 const char *exitname[13] =
499 {"N", "S", "E", "W", "NE", "NW", "SE", "SW", "U", "D", "IN", "OUT", "SPC"};
500
501
502
503
504 /* ------------------------------------------------------------- */
505 /* Verblist is the array of canonical forms of all the verbs */
506 /* ------------------------------------------------------------- */
507 /* The following long string defines all the built in AGT verbs, in the
508 following format:
509 verb syn syn syn , prep prep ; next_verb ....
510 except that if a verb takes no objects at all, it should be period
511 terminated and if it is a metaverb it should be terminated by '!'. */
512 static const char verbdef[] =
513 "north n. south s. east e. west w."
514 "northeast ne. northwest nw. southeast se. southwest sw."
515 "up u. down d."
516 "enter in inside go&in go&into go&in&to get&in get&into get&in&to."
517 "exit leave out go&out get&out get&out&of. special."
518 "throw cast dump, at to in into across inside;"
519 "open , with; close shut; lock, with; unlock, with;"
520 "look l. examine x ex check inspect look&at look∈"
521 "change_locations change_location;"
522 "read; eat; drink; score! attack kill fight hit, with;"
523 "wait z. yell shout scream."
524 "put place, in with inside into near behind over under on;"
525 "quit q! tell talk talk&to talk&with, to about;"
526 "inventory inv i. get take pick pick&up; ask, about for;"
527 "turn, on off; push touch press, with; pull; play;"
528 "list. show, to; drop;"
529 "listexit listexits list_exits list&exits show&exits."
530 "brief! verbose! save! restore!"
531 "light; extinguish ext put&out; fire shoot, at with;"
532 "help h. wear put&on; remove take&off;"
533 "script script&on! unscript script&off! magic_word. view; after."
534 "instructions ins!" /* INSTRUCTIONS is "1.83 only" */
535 /* The following are not defined in the original AGT */
536 "again g. restart! oops; undo. notify!"
537 "listexit_on listexit&on listexits&on!"
538 "listexit_off listexit&off listexits&off!"
539 "agildebug agtdebug! log! logoff log&off log&close! replay!"
540 "replay_step replay&step! menu! replay_fast replay&fast."
541 "sound sound_on sound&on! sound_off sound&off! introduction intro!"
542 "dir_addr.";
543
544 /* 1.83: Removes listexit; adds instructions after remove. */
545
546 /* Then come the dummy verbs */
547 /* Dummy verb n ==> n-55 105,122
548 Dummy_verb1...Dummy_Verb50 */
549
550 /* Possible extension to verb definitons (not implemented):
551 If it _requires_ a prep, use : ?
552 If it takes a prep and no dobj, use | ?
553 */
554
555 /* These are alternative (that is, non-canonical) forms of verbs that
556 were present in the oringal AGT interpreters. They have the property
557 that they have no effect if used in a dummy_verb declaration. */
558 /* Their dictionary indices are stored in old_agt_verb, which is
559 initialized by reinit_dict. */
560 /* PICK, GO */
561 const char *const old_agt_verb_str[] = {
562 "n", "s", "e", "w", "ne", "nw", "se", "sw", "u", "d", "in", "inside", "leave",
563 "cast", "dump", "shut", "l", "ex", "inspect", "check", "kill", "fight", "hit",
564 "shout", "scream", "place", "q", "talk", "i", "take", "touch", "ext",
565 "shoot", "h", "ins", nullptr
566 };
567
568
569
570 /* ------------------------------------------------------------------- */
571 /* Dictionary primitives: the basic functions for manipulating the */
572 /* dictionary data structures. */
573 /* ------------------------------------------------------------------- */
574 #define HASHSIZE (1<<HASHBITS)
575 #define HASHMASK (HASHSIZE-1)
576
577 #ifdef DOHASH
578 static word DOSFARDATA hash[HASHSIZE];
579 #endif
580
hashfunc(const char * s)581 static int hashfunc(const char *s) {
582 unsigned long n, i;
583
584 n = 0;
585 for (; *s != 0; s++) {
586 n += (n << 2) + (uchar) * s;
587 i = n & ~HASHMASK;
588 if (i)
589 n = (n ^ (i >> HASHBITS))&HASHMASK;
590 }
591 return (n & HASHMASK);
592 }
593
search0_dict(const char * s)594 static word search0_dict(const char *s) {
595 int i;
596
597 #ifdef DOHASH
598 for (i = hashfunc(s);
599 hash[i] != -1 && strcmp(s, dict[hash[i]]) != 0;
600 i = (i + 1)&HASHMASK);
601 return hash[i];
602 #else
603 for (i = 0; strcmp(s, dict[i]) != 0 && i < dp; i++);
604 if (i < dp) return i;
605 return -1;
606 #endif
607 }
608
search_dict(const char * s)609 word search_dict(const char *s)
610 /* This does a case-insensitive search */
611 {
612 word w;
613 char *t, *p;
614
615 t = rstrdup(s);
616 for (p = t; *p; p++) *p = tolower(*p);
617 w = search0_dict(t);
618 rfree(t);
619 return w;
620 }
621
622 /* The basic routine to add s to the dictionary; this does no preprocessing
623 of s; use add_dict for that */
add0_dict(const char * s)624 static word add0_dict(const char *s) {
625 int i;
626 long newptr;
627 char *newstr;
628
629 i = search0_dict(s);
630 if (i != -1) return i;
631 /* Okay, it's not in the dictionary; need to add it. */
632 /* rprintf("Adding %s\n",s);*/
633
634 dict = (char **)rrealloc(dict, sizeof(char *) * (dp + 1));
635 newptr = dictstrptr + strlen(s) + 1;
636 if (newptr > dictstrsize) { /* Enlarge dictstr */
637 if (dictstrsize == 0) dictstrsize = DICT_INIT;
638 while (newptr > dictstrsize)
639 dictstrsize += DICT_GRAN;
640 newstr = (char *)rrealloc(dictstr, dictstrsize);
641 /* Now need to update all of our pointers */
642 for (i = 0; i < dp; i++)
643 dict[i] = (dict[i] - dictstr) + newstr;
644 dictstr = newstr;
645 }
646 strcpy(dictstr + dictstrptr, s); /* Copy word into memory */
647 dict[dp] = dictstr + dictstrptr;
648 dictstrptr = newptr;
649
650 #ifdef DOHASH /* Need to update the hash table */
651 if (dp > HASHSIZE) fatal("Hash table overflow");
652 for (i = hashfunc(s); hash[i] != -1; i = (i + 1)&HASHMASK);
653 hash[i] = dp;
654 #endif
655 return dp++;
656 }
657
658 #ifdef DOHASH
659
init_hash(void)660 static void init_hash(void) {
661 int i;
662
663 for (i = 0; i < HASHSIZE; i++) hash[i] = -1;
664 }
665
666
667 /* This routine rebuilds the hash table from the dictionary. */
668 /* It's used by the AGX reading routines, since they save */
669 /* the dictionary but not the hash table */
rebuild_hash(void)670 static void rebuild_hash(void) {
671 int i, j;
672
673 if (dp > HASHSIZE) fatal("Hash table overflow");
674 init_hash();
675
676 for (i = 0; i < dp; i++) {
677 for (j = hashfunc(dict[i]); hash[j] != -1; j = (j + 1)&HASHMASK);
678 hash[j] = i;
679 }
680 }
681 #endif
682
683
init0_dict(void)684 static void init0_dict(void)
685 /* This sets up the basic data structures associated with the dictionary */
686 /* (It's called by init_dict, which also adds the basic verbs) */
687 {
688 #ifdef DOHASH
689 init_hash();
690 hash[hashfunc("any")] = 0;
691 #endif
692
693 dict = (char **)rmalloc(sizeof(char *));
694 dictstr = (char *)rmalloc(DICT_GRAN);
695 strcpy(dictstr, "any");
696 dict[0] = dictstr;
697
698 dictstrptr = 4; /* Point just after 'any' */
699 dictstrsize = DICT_GRAN;
700 dp = 1;
701 syntbl = NULL;
702 synptr = 0;
703 syntbl_size = 0; /* Clear synonym table */
704 }
705
706
707
708
709 /* ------------------------------------------------------------------- */
710 /* Higher level dictionary routines: Things that load initial vocab, */
711 /* and massage strings into the correct form for the dictionary */
712 /* ------------------------------------------------------------------- */
713
714 static rbool no_syn;
715
716 /* This splits dict[w] into space-separated pieces and adds them to
717 the dictionary and to a growing synonym list, which it marks the end of.
718 It returns a pointer to the beginning of this list.
719 If there are no spaces, it doesn't do anything and returns 0. */
add_multi_word(word w)720 slist add_multi_word(word w) {
721 slist start_list;
722 rbool end_found;
723 char *curr;
724 char *s, *t;
725
726 for (s = dict[w]; *s != 0 && *s != ' '; s++);
727 if (*s != ' ') return 0;
728
729 start_list = synptr;
730 curr = t = rstrdup(dict[w]);
731 s = t + (s - dict[w]);
732
733 addsyn(w); /* First entry is the 'word' to condense to */
734 while (1) {
735 end_found = (*s == 0);
736 *s = 0;
737 addsyn(add0_dict(curr)); /* Add to comb list */
738 if (end_found) break;
739 curr = ++s;
740 while (*s != 0 && *s != ' ') s++;
741 }
742 addsyn(-1); /* Mark the end of the list */
743 rfree(t);
744 return start_list;
745 }
746
747
748 /* Check verb vp for multiwords and enter any found in the auxilary
749 combination list */
verb_multiword(int vp)750 static void verb_multiword(int vp) {
751 int i;
752 slist ptr;
753
754 if (no_syn) return;
755 for (i = auxsyn[vp]; syntbl[i] != 0; i++) {
756 ptr = add_multi_word(syntbl[i]);
757 if (ptr != 0) {
758 num_auxcomb += 1;
759 auxcomb = (slist *)rrealloc(auxcomb, num_auxcomb * sizeof(slist));
760 auxcomb[num_auxcomb - 1] = ptr;
761 }
762 }
763 }
764
765
enter_verbs(int vp,const char * s)766 static void enter_verbs(int vp, const char *s)
767 /* Read definition string s, starting to make entries at verb # vp */
768 /* WARNING: This doesn't do any sort of checking; it assumes the input
769 string is correctly formed. */
770 {
771 const char *p; /* Points along string. */
772 words curr; /* word currently being read. */
773 int n; /* length of curr */
774 rbool have_multiword;
775
776 n = 0;
777 have_multiword = 0;
778 auxsyn[vp] = synptr;
779 for (p = s; *p != 0; p++)
780 if (*p == ';' || *p == ',' || *p == '.' || *p == '!' || isspace(*p)) {
781 if (n > 0) { /* word just ended: need to add it to dictionary etc */
782 curr[n] = 0;
783 n = 0;
784 addsyn(add0_dict(curr)); /* Add to syn list or prep list, depending */
785 }
786 if (!isspace(*p))
787 addsyn(-1); /* Mark the end of the list */
788 if (*p == ';' || *p == '.' || *p == '!') {
789 if (*p == ';') verbflag[vp] |= VERB_TAKEOBJ;
790 if (*p == '!') verbflag[vp] |= VERB_META;
791 if (have_multiword)
792 verb_multiword(vp);
793 have_multiword = 0;
794 vp++;
795 if (vp >= TOTAL_VERB) break;
796 auxsyn[vp] = synptr; /* The following words will be the syn list */
797 } else if (*p == ',')
798 preplist[vp] = synptr; /* The following words will be the prep list */
799 } else if (*p == '&') {
800 curr[n++] = ' ';
801 have_multiword = 1;
802 } else curr[n++] = *p;
803 }
804
805
806
807
init_dict(void)808 void init_dict(void) {
809 dict = NULL;
810 verblist = NULL;
811 syntbl = NULL;
812 no_syn = 0;
813 auxsyn = NULL;
814 preplist = NULL;
815 verbflag = NULL;
816 auxcomb = NULL;
817 old_agt_verb = NULL;
818 num_auxcomb = 0;
819 }
820
821 /* This is called by agttest.c */
build_verblist(void)822 void build_verblist(void) {
823 int i;
824
825 verblist = (words *)rmalloc(sizeof(words) * TOTAL_VERB);
826 for (i = 0; i < TOTAL_VERB; i++)
827 strncpy(verblist[i], dict[syntbl[auxsyn[i]]], sizeof(words));
828 #ifdef DUMP_VLIST
829 {
830 int j;
831 rprintf("VERB LIST:\n");
832 for (i = 0; i < TOTAL_VERB; i++) {
833 rprintf("%2d %s:", i, verblist[i]);
834 for (j = auxsyn[i]; syntbl[j] != 0; j++)
835 rprintf(" %s", dict[syntbl[auxsyn[i]]]);
836 rprintf(" ==> ");
837 for (j = preplist[i]; syntbl[j] != 0; j++)
838 rprintf(" %s", dict[ syntbl[preplist[i]]]);
839 writeln("");
840 }
841 }
842 #endif
843 }
844
845
846
set_verbflag(void)847 void set_verbflag(void) {
848 verbflag[14] |= VERB_MULTI; /* throw */
849 verbflag[29] |= VERB_MULTI; /* put */
850 verbflag[33] |= VERB_MULTI; /* get */
851 verbflag[41] |= VERB_MULTI; /* drop */
852 verbflag[51] |= VERB_MULTI; /* wear */
853 verbflag[52] |= VERB_MULTI; /* remove */
854 }
855
856
reinit_dict(void)857 void reinit_dict(void)
858 /* reinit_dict initializes verblist and sets up aux_syn as well
859 as loading the initial vocabulary into the dictionary. */
860 {
861 char buff[16]; /* Needs to be big enough to hold dummy_verbNNN\0
862 or subroutineNNN\0 */
863 int i;
864
865 no_syn = no_auxsyn;
866
867 auxsyn = (slist *)rmalloc(sizeof(slist) * TOTAL_VERB);
868 auxcomb = NULL;
869 num_auxcomb = 0;
870 preplist = (slist *)rmalloc(sizeof(slist) * TOTAL_VERB);
871 verbflag = (uchar *)rmalloc(sizeof(uchar) * TOTAL_VERB);
872
873 if (!agx_file)
874 init0_dict();
875 #ifdef DOHASH
876 else
877 rebuild_hash();
878 #endif
879
880 for (i = 0; i < TOTAL_VERB; i++)
881 verbflag[i] = 0;
882
883 auxsyn[0] = synptr;
884 addsyn(-1);
885
886 enter_verbs(1, verbdef);
887 set_verbflag(); /* Do additional verbflag initialization */
888
889 for (i = 0; i < DVERB; i++) {
890 sprintf(buff, "dummy_verb%d", i + 1);
891 auxsyn[i + BASE_VERB] = synptr;
892 addsyn(add0_dict(buff));
893 addsyn(-1);
894 }
895 for (i = 0; i < MAX_SUB; i++) {
896 sprintf(buff, "subroutine%d", i + 1);
897 auxsyn[i + BASE_VERB + DVERB] = synptr;
898 addsyn(sub_name[i] = add0_dict(buff));
899 addsyn(-1);
900 }
901 no_syn = 0; /* Return to usual state */
902 verblist = NULL;
903
904 /* Now initialize old_agt_verb array */
905 for (i = 0; old_agt_verb_str[i] != NULL; i++);
906 rfree(old_agt_verb);
907 old_agt_verb = (word *)rmalloc(sizeof(word) * (i + 1));
908 for (i = 0; old_agt_verb_str[i] != NULL; i++) {
909 old_agt_verb[i] = search_dict(old_agt_verb_str[i]);
910 assert(old_agt_verb[i] != -1);
911 }
912 old_agt_verb[i] = -1; /* Mark end of list */
913 }
914
915
916
917
free_dict(void)918 void free_dict(void) {
919 rfree(dict);
920 rfree(verblist);
921 rfree(syntbl);
922 rfree(auxsyn);
923 rfree(preplist);
924 rfree(verbflag);
925 }
926
add_dict(const char * str)927 word add_dict(const char *str) {
928 int i, j;
929 char s[50];
930
931 strncpy(s, str, 48);
932 for (i = 0; s[i] != 0 && rspace(s[i]); i++);
933 if (s[i] == 0) return 0; /* If it's all whitespace, ignore. */
934 /* i now points at first non-whitespace character */
935 /* Eliminate leading whitespace and lowercase the string. */
936 for (j = 0; s[j + i] != 0; j++) s[j] = tolower(s[j + i]);
937 s[j] = 0;
938 /* Now eliminate trailing whitespace (j points to end of string) */
939 for (j--; rspace(s[j]) && j > 0; j--);
940 s[j + 1] = 0;
941 /* Okay, now make sure it isn't 'none' */
942 if (strcmp(s, "none") == 0) return 0;
943 /* Finally, add it to the dictionary if it isn't already there */
944 return add0_dict(s);
945 }
946
947 /* Adds w to dynamically grown synonym list */
948 /* If no_syn is set, then *don't* add a synonym: return immediatly */
949 /* (This is done by agt2agx to avoid creating the auxsyn lists, */
950 /* since those should be created when the interpreter loads the */
951 /* game file and not before) */
addsyn(word w)952 void addsyn(word w) {
953 if (no_syn) return;
954 if (w == 0) return;
955 if (w == -1) w = 0;
956 if (synptr >= syntbl_size) {
957 syntbl_size += SYN_GRAIN;
958 if (syntbl_size > 0x7FFF)
959 fatal("Too many synonyms.");
960 syntbl = (word *)rrealloc(syntbl, ((long)syntbl_size) * sizeof(word));
961 }
962 syntbl[synptr++] = w;
963 }
964
965
966 /* Returns the given dictionary word with some checking for -1 */
gdict(word w)967 const char *gdict(word w) {
968 assert(w >= -1 && w < dp);
969 if (w == -1) return "___"; /* NONE */
970 return dict[w];
971 }
972
973
974
975
976
977 /* ------------------------------------------------------------------- */
978 /* General utilities linking objects to their names */
979 /* ------------------------------------------------------------------- */
980
981 /* Search auxsyn for verb: that is, check built in synonyms */
verb_builtin(word w)982 int verb_builtin(word w) {
983 int i, j;
984
985 for (i = 1; i < TOTAL_VERB; i++)
986 for (j = auxsyn[i]; syntbl[j] != 0; j++)
987 if (syntbl[j] == w) return i;
988
989 /* Failed to find a match */
990 return 0;
991 }
992
verb_authorsyn(word w)993 int verb_authorsyn(word w) {
994 int i, j;
995
996 /* Check game-specific synonyms first */
997 /* Scan in reverse so later synonyms will override earlier ones */
998 for (i = TOTAL_VERB - 1; i > 0; i--)
999 for (j = synlist[i]; syntbl[j] != 0; j++)
1000 if (w == syntbl[j]) return i;
1001 return 0;
1002 }
1003
1004
verb_code(word w)1005 int verb_code(word w)
1006 /* Given a word w, searches auxsyn and returns the verb id */
1007 {
1008 int canon, tmp;
1009
1010 /* Expand author-defined synonyms */
1011 tmp = verb_authorsyn(w);
1012 if (tmp != 0) return tmp;
1013
1014 /* Expand built-in synonyms */
1015 canon = verb_builtin(w);
1016 if (canon != 0) {
1017 /* Allow built-in verbs to be overridden */
1018 tmp = verb_authorsyn(syntbl[auxsyn[canon]]);
1019 if (tmp != 0) return tmp;
1020 }
1021
1022 return canon; /* No new synonyms; return canonical match if it exists */
1023 }
1024
1025
1026 /* This is a faster version of the above for use in the special case of
1027 command headers where the verb word is much more restricted; it should
1028 be the first auxsyn entry and it should never by a synlist entry. */
cmdverb_code(word w)1029 static int cmdverb_code(word w) {
1030 int i, j;
1031
1032 for (i = 0; i < TOTAL_VERB; i++)
1033 if (syntbl[auxsyn[i]] == w) return i;
1034 /* Hmm... that failed. Search the rest of the auxsyns in case the
1035 order of auxsyns has changed or something */
1036 agtwarn("Header verb not in canonical form.", 1);
1037 for (i = 1; i < TOTAL_VERB; i++)
1038 for (j = auxsyn[i]; syntbl[j] != 0; j++)
1039 if (syntbl[j] == w) return i;
1040 agtwarn("Header verb not in internal list.", 1);
1041 return verb_code(w);
1042 }
1043
objname(int i)1044 char *objname(int i) { /* returns malloc'd name string of object i */
1045 char *s;
1046
1047 if (i < 0)
1048 return rstrdup(dict[-i]);
1049 if (i == 0)
1050 return rstrdup("....");
1051 if (i == 1) return rstrdup("*Self*");
1052 if (i == 1000) return rstrdup("*Worn*");
1053 if (i >= first_room && i <= maxroom)
1054 return rstrdup(room[i - first_room].name);
1055 if ((i >= first_noun && i <= maxnoun) || (i >= first_creat && i <= maxcreat)) {
1056 word adjw, nounw;
1057 if (i >= first_noun && i <= maxnoun) {
1058 adjw = noun[i - first_noun].adj;
1059 nounw = noun[i - first_noun].name;
1060 } else {
1061 adjw = creature[i - first_creat].adj;
1062 nounw = creature[i - first_creat].name;
1063 }
1064 if (adjw == 0 || !strcmp(dict[adjw], "no_adjective"))
1065 return rstrdup(dict[nounw]);
1066 return concdup(dict[adjw], dict[nounw]);
1067 }
1068 /* At this point we can't get a name: return ILLn. */
1069 s = (char *)rmalloc(3 + 1 + (5 * sizeof(int)) / 2 + 1);
1070 /* Make sure we have enough space in case i is big */
1071 sprintf(s, "ILL%d", i);
1072 return s;
1073 }
1074
1075
1076
1077 /* ------------------------------------------------------------------- */
1078 /* Routines to sort the command array and construct verbptr */
1079 /* ------------------------------------------------------------------- */
1080
1081 #define SORT_META
1082
1083 #ifdef SORT_META
1084
1085 #define ch1 ((const cmd_rec*)cmd1)
1086 #define ch2 ((const cmd_rec*)cmd2)
1087
1088 /* See notes below before trying to decipher this routine;
1089 during the sort, many of the fields are being used for nonstandard
1090 purposes */
1091
1092
1093
1094 #define s_verb(cmd) ( (cmd)->actor<0 ? (cmd)->data[0] : (cmd)->verbcmd)
1095
cmp_cmd(const void * cmd1,const void * cmd2)1096 static int cmp_cmd(const void *cmd1, const void *cmd2) {
1097 word v1, v2;
1098
1099 /* We are sorting on command[].verbcmd, but if one of the headers
1100 is really the object of a redirect command then we need to use
1101 its parent's verbcmd */
1102 /* For commands with actors, we need to avoid sorting them at all. */
1103 v1 = s_verb(ch1);
1104 v2 = s_verb(ch2);
1105
1106 if (v1 < v2) return -1;
1107 if (v1 > v2) return +1;
1108
1109 /* v1==v2, so leave them in the same order as before */
1110 /* We have to take absolute values here because we are using negatives
1111 to indicate redirection objects */
1112 if (ABS(ch1->actor) < ABS(ch2->actor))
1113 return -1;
1114 else if (ABS(ch1->actor) == ABS(ch2->actor))
1115 return 0;
1116 else return 1;
1117 /* Equality should be impossible */
1118 }
1119
1120 #undef ch1
1121 #undef ch2
1122
1123 /* This sets things up for qsort */
1124 /* We need a sort that is
1125 i) Stable and
1126 ii) Keeps "redirection headers" attached to the correct command */
1127 /* We steal the field actor for this purpose */
1128 /* actor will equal the index of the header in the original list. */
1129 /* (or negative the header if the command is a redirection) */
1130 /* For redirected commands, we steal the data pointer since it shouldn't
1131 be being used anyhow. */
1132 /* In a field pointed to by data we store the verb word */
1133 /* NOTE: this routine requires that the data type of *data (namely
1134 integer) is big enough to hold a value of type word. */
1135
rsort(void)1136 static void rsort(void) {
1137 long i;
1138 integer *save_actor;
1139 word *save_verb;
1140
1141 save_actor = (integer *)rmalloc(last_cmd * sizeof(integer));
1142 save_verb = (word *)rmalloc(last_cmd * sizeof(word));
1143
1144 /* The following loop does three things:
1145 i) Copies command[].actor to save_actor[]
1146 ii) Sets command[].actor to the commands index in the array
1147 iii) For actor commands, sets the verb to .... after saving it
1148 in save_verb.
1149 iv) For redirection commands, stores the verb of the owning
1150 header in a block pointed to by data */
1151
1152 for (i = 0; i < last_cmd; i++) { /* Copy actor to save_actor */
1153 save_verb[i] = command[i].verbcmd;
1154 if (command[i].actor > 1) /* i.e. there _is_ an actor */
1155 command[i].verbcmd = syntbl[auxsyn[DIR_ADDR_CODE]];
1156 save_actor[i] = command[i].actor;
1157 command[i].actor = i;
1158 if (save_actor[i] < 0) { /* Redirected command */
1159 int j;
1160
1161 command[i].actor = -i;
1162 rfree(command[i].data); /* data should be NULL, anyhow */
1163 command[i].data = (integer *)rmalloc(sizeof(integer));
1164 for (j = i; j > 0 && save_actor[j] < 0; j--);
1165 if (save_actor[j] > 0)
1166 command[i].data[0] = command[j].verbcmd;
1167 else {
1168 command[i].data[0] = 0;
1169 agtwarn("First command header is REDIRECT object!", 0);
1170 }
1171 }
1172 }
1173
1174 /* Now do the sort... */
1175 qsort(command, last_cmd, sizeof(cmd_rec), cmp_cmd);
1176
1177 #if 0 /* This is code to test the integrity of the sort */
1178 for (i = 0; i < last_command; i++)
1179 if (command[i].actor < 0)
1180 assert(i == 0 || command[i].data[0] == command[i - 1].verbcmd);
1181 #endif
1182
1183 /* Finally, restore everything to normal */
1184 for (i = 0; i < last_cmd; i++) { /* Restore actor */
1185 command[i].verbcmd = save_verb[ABS(command[i].actor)];
1186 command[i].actor = save_actor[ABS(command[i].actor)];
1187 if (command[i].actor < 0) {
1188 rfree(command[i].data); /* Sets it to NULL automatically */
1189 command[i].cmdsize = 0;
1190 }
1191 }
1192 rfree(save_actor);
1193 rfree(save_verb);
1194 }
1195
1196 #endif
1197
sort_cmd(void)1198 void sort_cmd(void) {
1199 int i;
1200 word curr_vb;
1201 word all_word, global_word;
1202
1203 verbptr = (short *)rmalloc(sizeof(short) * TOTAL_VERB);
1204 verbend = (short *)rmalloc(sizeof(short) * TOTAL_VERB);
1205
1206 if (mars_fix) { /* Don't bother if mars scanning is active */
1207 for (i = 0; i < TOTAL_VERB; i++) {
1208 verbptr[i] = 0; /* That is, scan the whole space for all verbs */
1209 verbend[i] = last_cmd;
1210 }
1211 return;
1212 }
1213
1214 #ifdef SORT_META
1215 if (!agx_file && aver >= AGX00) rsort();
1216 #endif
1217
1218
1219 if (no_auxsyn) return; /* Used by agt2agx */
1220
1221 for (i = 0; i < TOTAL_VERB; i++) {
1222 verbptr[i] = last_cmd;
1223 verbend[i] = 0;
1224 }
1225
1226 all_word = search_dict("all");
1227 if (all_word == 0) all_word = -1; /* This means none of the metacommands
1228 used ALL, so prevent ANY matches */
1229 global_word = search_dict("global_scope");
1230 if (global_word == 0) global_word = -1; /* Ditto */
1231
1232
1233 for (i = 0; i < last_cmd; i++) {
1234 if (command[i].actor < 0) continue; /* Redirection */
1235 if (command[i].nouncmd == all_word)
1236 /* Detect multinoun accepting verbs by ALL */
1237 verbflag[cmdverb_code(command[i].verbcmd)] |= VERB_MULTI;
1238 if (command[i].actor > 1)
1239 curr_vb = DIR_ADDR_CODE;
1240 else
1241 curr_vb = cmdverb_code(command[i].verbcmd);
1242 if (i < verbptr[curr_vb]) verbptr[curr_vb] = i;
1243 if (i > verbend[curr_vb]) verbend[curr_vb] = i;
1244 }
1245
1246 for (i = 0; i < TOTAL_VERB; i++)
1247 if (verbptr[i] == last_cmd) /* No occurences of this verb */
1248 verbend[i] = last_cmd;
1249 else verbend[i]++; /* Point *after* last occurance */
1250
1251 for (i = 0; i < TOTAL_VERB; i++) {
1252 int j;
1253
1254 j = synlist[i];
1255 if (syntbl[j] == 0) continue;
1256 while (syntbl[j] != 0) j++;
1257 j--;
1258 if (syntbl[j] == global_word) { /* Ends with global_scope */
1259 verbflag[i] |= VERB_GLOBAL;
1260 syntbl[j] = 0;
1261 }
1262 }
1263 }
1264
1265
1266
1267
1268 /* ------------------------------------------------------------------- */
1269 /* Functions for getting opcode information */
1270 /* ------------------------------------------------------------------- */
1271
1272
1273 /* Returns the opdef structure associated with an opcode */
get_opdef(integer op)1274 const opdef *get_opdef(integer op) {
1275 op = op % 2048; /* Strip operand information */
1276 if (op < 0 || (op > MAX_COND && op < START_ACT) || (op > PREWIN_ACT && op < WIN_ACT)
1277 || (op > MAX_ACT)) {
1278 return &illegal_def;
1279 }
1280 if (op >= 2000)
1281 return &end_def[op - 2000];
1282 if (op >= 1000)
1283 return &act_def[op - 1000];
1284 return &cond_def[op];
1285 }
1286
1287
1288
1289 /* ------------------------------------------------------------------- */
1290 /* Functions for processing strings */
1291 /* ------------------------------------------------------------------- */
1292
new_str(char * buff,int max_leng,rbool pasc)1293 long new_str(char *buff, int max_leng, rbool pasc)
1294 /* Stores the (up to leng) characters of a string
1295 into our master string space (enlarging it if neccessary)
1296 and returns the offset into the array.
1297 pasc=1 ==> pascal-style string
1298 pasc=0 ==> C-style string; ignore max_leng and NONE strings
1299 */
1300 {
1301 int leng, i;
1302 long p;
1303
1304 if (pasc) {
1305 leng = buff[0];
1306 if (leng > max_leng) leng = max_leng;
1307 } else
1308 leng = strlen(buff);
1309
1310 if (ss_size < ss_end + leng + 1) {
1311 while (ss_size < ss_end + leng + 1) ss_size += SS_GRAIN;
1312 static_str = (char *)rrealloc(static_str, sizeof(char) * ss_size);
1313 }
1314
1315 if (pasc)
1316 if (memcmp(buff, nonestr, 5) == 0 || memcmp(buff, NONEstr, 5) == 0) {
1317 /* "none" --> empty string */
1318 if (ss_end != 0) return (ss_end - 1); /* Points to last \0 */
1319 else { /* Very first string */
1320 static_str[0] = 0;
1321 ss_end = 1;
1322 return 0;
1323 }
1324 }
1325
1326 p = ss_end; /* Remember begining of string */
1327 for (i = 0; i < leng;)
1328 static_str[ss_end++] = fixchar[(uchar)buff[pasc + (i++)]];
1329 static_str[ss_end++] = 0;
1330
1331 return p;
1332 }
1333
1334
1335
1336 /* ------------------------------------------------------------------- */
1337 /* Functions for reading in descriptions */
1338 /* ------------------------------------------------------------------- */
1339
1340
read_descr(long start,long size)1341 descr_line *read_descr(long start, long size) {
1342 if (agx_file)
1343 return agx_read_descr(start, size);
1344 else
1345 return agt_read_descr(start, size);
1346 }
1347
free_descr(descr_line * txt)1348 void free_descr(descr_line *txt) {
1349 if (txt == NULL) return;
1350 if (mem_descr == NULL)
1351 rfree(txt[0]); /* First free the string block containing the text...*/
1352 rfree(txt); /* ... then the array of pointers to it */
1353 }
1354
1355
1356
1357 /* ------------------------------------------------------------------- */
1358 /* ObjFlag and ObjProp routines */
1359 /* ------------------------------------------------------------------- */
1360
objextsize(char op)1361 long objextsize(char op) {
1362
1363 /* op=0 for flags, =1 for props */
1364 if (op == 0)
1365 return num_rflags * rangefix(maxroom - first_room + 1)
1366 + num_nflags * rangefix(maxnoun - first_noun + 1)
1367 + num_cflags * rangefix(maxcreat - first_creat + 1);
1368 else
1369 return num_rprops * rangefix(maxroom - first_room + 1)
1370 + num_nprops * rangefix(maxnoun - first_noun + 1)
1371 + num_cprops * rangefix(maxcreat - first_creat + 1);
1372 }
1373
lookup_objflag(int id,int t,char * ofs)1374 long lookup_objflag(int id, int t, char *ofs) {
1375 if (id < 0 || id >= oflag_cnt) return -1;
1376 switch (t) {
1377 case 0:
1378 *ofs = attrtable[id].rbit;
1379 return attrtable[id].r;
1380 case 1:
1381 *ofs = attrtable[id].nbit;
1382 return attrtable[id].n;
1383 case 2:
1384 *ofs = attrtable[id].cbit;
1385 return attrtable[id].c;
1386 default:
1387 rprintf("INT ERROR: Invalid object type.\n");
1388 return -1;
1389 }
1390 }
1391
lookup_objprop(int id,int t)1392 long lookup_objprop(int id, int t) {
1393 if (id < 0 || id >= oprop_cnt) return -1;
1394 switch (t) {
1395 case 0:
1396 return proptable[id].r;
1397 case 1:
1398 return proptable[id].n;
1399 case 2:
1400 return proptable[id].c;
1401 default:
1402 rprintf("INT ERROR: Invalid object type.\n");
1403 return -1;
1404 }
1405 }
1406
num_oattrs(int t,rbool isflag)1407 int num_oattrs(int t, rbool isflag) {
1408 switch (t) {
1409 case 0:
1410 return isflag ? num_rflags : num_rprops;
1411 case 1:
1412 return isflag ? num_nflags : num_nprops;
1413 case 2:
1414 return isflag ? num_cflags : num_cprops;
1415 default:
1416 rprintf("INT ERROR: Invalid object type.\n");
1417 return 0;
1418 }
1419 }
1420
op_simpflag(uchar * pf,char ofs,int op)1421 rbool op_simpflag(uchar *pf, char ofs, int op)
1422 /* op: 0=clear, 1=set, 2=nop, 3=toggle two bits: <ab> */
1423 {
1424 unsigned char mask, amask, bmask;
1425
1426 mask = 1 << ofs;
1427 amask = ~mask | ((op >> 1) << ofs);
1428 bmask = (op & 1) << ofs;
1429
1430 *pf = (*pf & amask)^bmask;
1431
1432 return (*pf & mask) != 0;
1433 }
1434
calcindex(integer obj,integer objbase,int ocnt,int base)1435 static long calcindex(integer obj, integer objbase, int ocnt, int base) {
1436 int rval;
1437
1438 if (base == -1) rval = -1;
1439 else rval = (obj - objbase) * ocnt + base;
1440 /* rprintf("INDEX %d + %d::%d ==> %d\n",base,obj,ocnt,rval); */
1441 return rval;
1442 }
1443
1444
have_objattr(rbool prop,integer obj,int id)1445 rbool have_objattr(rbool prop, integer obj, int id) {
1446 int t;
1447 char ofs;
1448
1449 if (troom(obj)) t = 0;
1450 else if (tnoun(obj)) t = 1;
1451 else if (tcreat(obj)) t = 2;
1452 else return 0;
1453 if (prop)
1454 return (lookup_objprop(id, t) >= 0);
1455 else
1456 return (lookup_objflag(id, t, &ofs) >= 0);
1457 }
1458
1459
1460
op_objflag(int op,integer obj,int id)1461 rbool op_objflag(int op, integer obj, int id) {
1462 /* op: 0=clear, 1=set, 2=nop, 3=toggle two bits: <ab> */
1463 /* <flagbit>= (<flagbit>&<a>)^<b> ) */
1464 int index;
1465 int t, firstobj;
1466 char ofs;
1467
1468 if (troom(obj)) {
1469 t = 0;
1470 firstobj = first_room;
1471 } else if (tnoun(obj)) {
1472 t = 1;
1473 firstobj = first_noun;
1474 } else if (tcreat(obj)) {
1475 t = 2;
1476 firstobj = first_creat;
1477 } else return 0;
1478
1479 index = calcindex(obj, firstobj, num_oattrs(t, 1), lookup_objflag(id, t, &ofs));
1480 if (index == -1) return 0;
1481
1482 return op_simpflag(&objflag[index], ofs, op);
1483 }
1484
op_objprop(int op,int obj,int id,long val)1485 long op_objprop(int op, int obj, int id, long val) {
1486 /* op: 2=get, 1=set */
1487 int index, t, firstobj;
1488
1489 if (troom(obj)) {
1490 t = 0;
1491 firstobj = first_room;
1492 } else if (tnoun(obj)) {
1493 t = 1;
1494 firstobj = first_noun;
1495 } else if (tcreat(obj)) {
1496 t = 2;
1497 firstobj = first_creat;
1498 } else return 0;
1499
1500 index = calcindex(obj, firstobj, num_oattrs(t, 0), lookup_objprop(id, t));
1501 if (index == -1) return 0;
1502
1503 if (op == 2) return objprop[index];
1504 else objprop[index] = val;
1505 return val;
1506 }
1507
get_objattr_str(int dtype,int id,long val)1508 const char *get_objattr_str(int dtype, int id, long val) {
1509 int max_val;
1510
1511 if (dtype == AGT_OBJPROP) {
1512 if (!proptable || !propstr || id < 0 || id >= oprop_cnt) return "";
1513 max_val = proptable[id].str_cnt;
1514 if (val < 0) val = 0;
1515 if (val >= max_val) val = max_val - 1;
1516 if (max_val > 0)
1517 return propstr[ proptable[id].str_list + val ];
1518 return "";
1519 } else if (dtype == AGT_VAR) {
1520 if (!vartable || !propstr || id < 0 || id > VAR_NUM) return "";
1521 max_val = vartable[id].str_cnt;
1522 if (val < 0) val = 0;
1523 if (val >= max_val) val = max_val - 1;
1524 if (max_val > 0)
1525 return propstr[ vartable[id].str_list + val ];
1526 return "";
1527 } else if (dtype == AGT_OBJFLAG) {
1528 if (attrtable && id >= 0 && id < oflag_cnt)
1529 return (val ? attrtable[id].ystr : attrtable[id].nstr);
1530 else
1531 return (val ? "yes" : "no");
1532 } else if (dtype == AGT_FLAG) {
1533 /* This uses yes/no as defaults, not on/off */
1534 if (flagtable && id >= 0 && id <= FLAG_NUM)
1535 return val ? flagtable[id].ystr : flagtable[id].nstr;
1536 else
1537 return val ? "on" : "off";
1538 } else
1539 rprintf("INTERNAL ERROR: Invalid data type for get_objattr_str().");
1540 return "";
1541 }
1542
1543 /* ------------------------------------------------------------------- */
1544 /* Warning and error functions */
1545 /* ------------------------------------------------------------------- */
1546
agtwarn(const char * s,int elev)1547 void agtwarn(const char *s, int elev) {
1548 if (ERR_LEVEL >= elev)
1549 rprintf("Warning: %s\n", s);
1550 }
1551
agtnwarn(const char * s,int n,int elev)1552 void agtnwarn(const char *s, int n, int elev) {
1553 if (ERR_LEVEL >= elev)
1554 rprintf("Warning: %s%d.\n", s, n);
1555 }
1556
fatal(const char * s)1557 void fatal(const char *s) {
1558 error("Fatal error: %s", s);
1559 }
1560
init_flags(void)1561 void init_flags(void) {
1562 rm_trap = 1;
1563 DIAG = def_DIAG;
1564 interp_arg = def_interp_arg;
1565 debug_da1 = def_debug_da1;
1566 RAW_CMD_OUT = def_RAW_CMD_OUT;
1567 ERR_LEVEL = def_ERR_LEVEL;
1568 irun_mode = 0;
1569 fix_ascii_flag = fix_ascii;
1570 descr_maxmem = DESCR_BUFFSIZE;
1571 bold_mode = 0;
1572 dbg_nomsg = 0; /* Print out MSG arguments to metacommands */
1573 debug_mode = 0;
1574 dbgflagptr = NULL;
1575 dbgvarptr = NULL;
1576 dbgcntptr = NULL;
1577 no_auxsyn = 0;
1578 text_file = 0;
1579 #ifdef PATH_SEP
1580 gamepath = NULL;
1581 #endif
1582 BATCH_MODE = make_test = 0;
1583 font_status = 0;
1584 #ifdef OPEN_AS_TEXT
1585 open_as_binary = 0;
1586 #endif
1587 }
1588
1589 } // End of namespace AGT
1590 } // End of namespace Glk
1591