#charset "us-ascii" /* * Krister's debugging suite. This extension contains the following * debugging aids: * * - a set of debugging actions: * - 'purloin', for getting hold of objects that are out of scope * - 'gonear', for teleporting to remote locations * - 'frotz', for making objects emit light (or stop emitting light, if * they already are) * * - a framework for adding game-specific debugging actions (see comments * below for how to do this) * * - an in-game expression evaluator (again, see below for details) * * Note that this extension automatically includes reflect.t for debug * builds, so there is no need to do this manually. * * kfDebug.t Version 1 * Copyright 2010, Krister Fundin (fundin@yahoo.com) */ #ifdef __DEBUG #include #include #include /* ---------------------------------------------------------------------- */ /* * Purloin. This action allows the player to get hold of objects that are * either out of scope or for other reasons unreachable. It makes no * checks at all for logicalness, so it's quite possible to purloin * buildings or people or anything at all. */ DefineTAction(Purloin) objInScope(obj) { return true; } verifyAction() { } execAction() { dobjCur_.moveIntoForTravel(gActor); "Purloined. "; } ; VerbRule(Purloin) 'purloin' dobjList : PurloinAction verbPhrase = 'purloin/purloining (what)' ; /* ---------------------------------------------------------------------- */ /* * Gonear. This action teleports the player to the location of a specified * object. It works with NestedRooms as well as Rooms, so a bit of caution * might be needed: if you type 'gonear bob' and Bob is sitting in an * armchair, then the destination will be the armchair. You can of course * name the rooms directly if you have assigned vocabulary words to them. */ DefineTAction(Gonear) objInScope(obj) { return true; } verifyAction() { } execAction() { local dest = dobjCur_.roomLocation; if (dest != nil) { gActor.moveIntoForTravel(dest); gActor.lookAround(true); } else { "You can't go there. "; return; } } ; VerbRule(Gonear) 'gonear' singleDobj : GonearAction verbPhrase = 'go/going near (what)' ; /* ---------------------------------------------------------------------- */ /* * Frotz. This action makes arbitrary objects start emitting light, or * stop emitting light if they already are. */ DefineTAction(Frotz) verifyAction() { } execAction() { if (dobjCur_.brightness == 0) { dobjCur_.brightness = 3; "{The dobj/he} start{s} to glow. "; } else { dobjCur_.brightness = 0; "{The dobj/he} stop{s} glowing. "; } } ; VerbRule(Frotz) 'frotz' singleDobj : FrotzAction verbPhrase = 'frotz/frotzing (what)' ; /* ---------------------------------------------------------------------- */ /* * Game-specific debugging commands. These can be used E.G. to jump to a * later chapter in the game. To add one, use this syntax: * * grammar debugCommand: * 'chapter5' * : BasicProd * * execute() * { * // carry out the necessary steps here * } * ; * * A definition such as this won't have any effect in a release build, so * it's not necessary to put an #ifdef around it. One might want to set up * a macro for creating these debug commands in order to save some typing, * however. Here is one way to do so: * * #ifdef __DEBUG * #define dbgCommand(name, code) \ * grammar debugCommand : name : BasicProd execute() code * #else * #define dbgCommand(name, code) * #endif * * With this macro in place, the definition can be shortened to: * * dbgCommand('chapter5', * { * // carry out the necessary steps here * }); * * In any case, the command is then executed by typing 'debug chapter5'. */ DefineSystemAction(CustomDebug) execSystemAction() { cmd_.execute(); } ; VerbRule(CustomDebug) 'debug' debugCommand->cmd_ : CustomDebugAction verbPhrase = 'debug/debugging' ; grammar debugCommand(unknown): [badness 500] miscWordList : BasicProd execute() { "Unknown debugging command. "; } ; /* ---------------------------------------------------------------------- */ /* * In-game expression evaluation. This part of the extension provides a * quick way to look up variables from within the game. Any command line * which starts with an equals sign is passed through a much simplified * version of the 'evaluate' command from the TADS 3 debugger, and the * result is then displayed if there was one. * * The parser used here can evaluate properties and call methods on * objects, possibly with arguments, and it recognizes simple strings and * numbers, but that's as far as it goes. In particular, it should be * noted that macros such as gDobj can't be evaluated this way. */ evalPreParser: StringPreParser doParsing(str, which) { if (which == rmcAskLiteral) return str; if (!str.startsWith('=')) return str; try { local toks = evalTokenizer.tokenize(str.substr(2)); local match = evalExpr.parseTokens(toks, nil); if (match != []) { say (reflectionServices.valToSymbol(match[1].evaluate())); return nil; } } catch (Exception exc) { } return str; } ; evalException: Exception; evalTokenizer: Tokenizer rules_ = static [ ['whitespace', new RexPattern('+'), nil, &tokCvtSkip, nil], ['operators', new RexPattern('[.,()]'), tokPunct, nil, nil], ['strings', new RexPattern('.*?'), tokString, nil, nil], ['numbers', new RexPattern('+'), tokInt, nil, nil], ['symbols', new RexPattern('+'), tokWord, nil, nil] ] ; grammar evalExpr(compound): evalExpr->lhs_ '.' evalObj->rhs_ evalArgList->args_ : BasicProd evaluate() { local lhs = lhs_.evaluate(); local rhs = rhs_.evaluate(); local args = args_.evaluate(); if (dataType(lhs) not in (TypeObject, TypeSString, TypeList) || dataType(rhs) != TypeProp) { throw evalException; } return lhs.(rhs)(args...); } ; grammar evalExpr(simple): evalObj->obj_ : BasicProd evaluate() { return obj_.evaluate(); } ; grammar evalArgList(nonempty): '(' evalArgs->args_ ')' : BasicProd evaluate() { return args_.evaluate(); } ; grammar evalArgList(empty): ('(' ')' | ) : BasicProd evaluate() { return []; } ; grammar evalArgs(compound): evalExpr->arg_ ',' evalArgs->args_ : BasicProd evaluate() { return args_.evaluate().prepend(arg_.evaluate()); } ; grammar evalArgs(single): evalExpr->arg_ : BasicProd evaluate() { return [arg_.evaluate()]; } ; grammar evalObj(symbol): tokWord->tok_ : BasicProd evaluate() { switch (tok_) { case 'true': return true; case 'nil': return nil; default: local val = reflectionServices.symtab_[tok_]; if (val != nil) return val; else throw evalException; } } ; grammar evalObj(string): tokString->tok_ : BasicProd evaluate() { return tok_.substr(2, tok_.length - 2); } ; grammar evalObj(number): tokInt->tok_ : BasicProd evaluate() { return toInteger(tok_); } ; #endif // __DEBUG