#charset "us-ascii" /* * Copyright (c) 2006 by Kevin Forchione. All rights reserved. * * This file is part of the TADS 3 Diff Library Extension * * diff.t * * version 1.0 * * Diff provides a mechanism for silently testing an * action, rolling back any state changes that may * have resulted, and then restoring game state to * that prior to the action's execution. */ #include #include #include #include diff(func) { local aLu, bLu, cLu, propList, dispVec; /* * Save the current game state */ savepoint(); /* * Turn on our diff display capturing */ DiffDisplayCapture.on(); /* * Create a lookup table prior to our test and * load it with all object states. */ aLu = new LookupTable(); for (local o = firstObj(Object, ObjAll); o != nil; o = nextObj(o, Object, ObjAll)) { propList = o.getPropList(); foreach (local prop in propList) { if (o.propType(prop) is in (TypeNil, TypeTrue, TypeObject, TypeInt, TypeSString, TypeList, TypeFuncPtr, TypeEnum)) { aLu[[o, prop]] = o.(prop); } } } /* * Perform our test */ (func)(); /* * Create a lookup table after our test and * load it with all object states. */ bLu = new LookupTable(); for (local o = firstObj(Object, ObjAll); o != nil; o = nextObj(o, Object, ObjAll)) { propList = o.getPropList(); foreach (local prop in propList) { if (o.propType(prop) is in (TypeNil, TypeTrue, TypeObject, TypeInt, TypeSString, TypeList, TypeFuncPtr, TypeEnum)) { bLu[[o, prop]] = o.(prop); } } } /* * Turn off our diff display capturing * and retrieve any captured display. */ dispVec = DiffDisplayCapture.off(); /* * Restore the previous game state */ undo(); /* * Create a lookup table and load it with * all object state differences. */ cLu = new LookupTable(); foreach (local key in aLu.keysToList()) { if (bLu.isKeyPresent(key)) { if (aLu[key] != bLu[key]) cLu[key] = [aLu[key], bLu[key]]; } else cLu[key] = [aLu[key], []]; } foreach (local key in bLu.keysToList()) { if (!aLu.isKeyPresent(key)) { cLu[key] = [[], bLu[key]]; } } /* * Return the captured display vector and changes * lookup table. */ return [dispVec, cLu]; } /* * This object is used to capture any display * produced during our test. */ DiffDisplayCapture: object { dispVec = nil oldDispFunc = nil oldDispMeth = nil /* * This method is called for both object display * method and function display method calls and * loads all display into a vector. */ captureDisplay(obj, val) { dispVec.append([obj, val]); } /* * Turn on diff display capturing. We create * a new display vector and set display methods * and functions for our diff capture. */ on() { dispVec = new Vector(100); oldDispFunc = t3SetSay(diffDisplayFunction); oldDispMeth = t3SetSay(&diffDisplayMethod); } /* * Turn off diff display capture. We reset the * old display method and function values and * return a copy of the diff display vector. */ off() { local vec; if (oldDispFunc) t3SetSay(oldDispFunc); else t3SetSay(T3SetSayNoFunc); if (oldDispMeth) t3SetSay(oldDispMeth); else t3SetSay(T3SetSayNoMethod); vec = new Vector(dispVec.length()); vec.copyFrom(dispVec, 1, 1, dispVec.length()); return vec; } } /* * Define a diff display method to all game objects * in order to identify where text is being produced * in game objects. */ modify Object { diffDisplayMethod(val) { DiffDisplayCapture.captureDisplay(self, val); } } /* * Define a diff display function to capture any display * from functions or double-quoted strings and embedded * evaluations. */ diffDisplayFunction(val) { DiffDisplayCapture.captureDisplay(nil, val); }