#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.
*/
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);
}