If you appreciate the work done within the wiki, please consider supporting The Cutting Room Floor on Patreon. Thanks for all your support!

Notes:Bookworm Adventures Volume 2

From The Cutting Room Floor
Jump to navigation Jump to search

This page contains notes for the game Bookworm Adventures Volume 2.

Hmmm...
To do:
Compare with compiled variants.

The scripts/common directory has the following uncompiled Lua scripts.

App.lua:

class App;

def gApp;
def gCApp;

function App:Init(theCObj)
{
	def mCObj = theCObj;
	gApp = self;
	gCApp = theCObj;
}

function App:WrapWidget(theClass, theDefaultClass, theCObj)
{
	if (theClass == nil)
		theClass = theDefaultClass;
	local aNewObj = theClass:new(theCObj);
	return aNewObj;}

function App:CreateLuaWidget(theClass)
{
	return WrapWidget(theClass, LuaWidget,
		mCObj:CreateLuaWidget());
}

function App:CreateHyperlinkWidget(theId, theListener, theClass)
{
	return WrapWidget(theClass, LuaHyperlinkWidget, 
		mCObj:CreateLuaHyperlinkWidget(theId, theListener.mCObj));
}

function App:CreateLabelWidget(theListener, theClass)
{
	return WrapWidget(theClass, LuaLabelWidget, 
		mCObj:CreateLuaLabelWidget());
}

function App:CreateButtonWidget(theId, theListener, theClass)
{
	return WrapWidget(theClass, LuaButtonWidget, 
		mCObj:CreateLuaButtonWidget(theId, theListener.mCObj));
}

function App:CreateCheckboxWidget(theUncheckedImage, theCheckedImage, theId, theListener, theClass)
{
	return WrapWidget(theClass, LuaCheckboxWidget, 
		mCObj:CreateLuaCheckboxWidget(theUncheckedImage, theCheckedImage, theId, theListener.mCObj));
}

function App:CreateScrollbarWidget(theId, theListener, theClass)
{
	return WrapWidget(theClass, LuaScrollbarWidget, 
		mCObj:CreateLuaScrollbarWidget(theId, theListener.mCObj));
}

function App:CreateEditWidget(theId, theListener, theClass)
{
	return WrapWidget(theClass, LuaEditWidget, 
		mCObj:CreateLuaEditWidget(theId, theListener.mCObj));
}

function App:CreateListWidget(theId, theFont, theListener, theClass)
{
	return WrapWidget(theClass, LuaListWidget, 
		mCObj:CreateLuaListWidget(theId, theFont, theListener.mCObj));
}

function App:CreateDialog(theComponentImage, theButtonComponentImage,
	theId, isModal, theDialogHeader, theDialogLines, theDialogFooter, theButtonMode, theClass)
{
	local aWidget = WrapWidget(theClass, LuaWidget, 
		mCObj:CreateLuaDialog(theComponentImage, theButtonComponentImage,
		theId, isModal, theDialogHeader, theDialogLines, theDialogFooter, theButtonMode));
	gCApp:AddDialog(aWidget.mCObj);
	return aWidget;
}

function App:DialogButtonPress(theDialogId, theButtonId)
{
}
	
function App:DialogButtonDepress(theDialogId, theButtonId)
{
	mCObj:KillDialog(theDialogId);
}

RegisterLinkedClass("App", App);

ClassLink.lua:

def gLinkedClassConstructors = {};
def gLinkedClasses = {};
def gLinkedObjectTable = {};
def gLinkedDynCallTable = {};

def gLinkedClassNames = {};

function RegisterLinkedClass(theClassName, theClass)
{
	//print("RegisterLinkedClass: "..theClassName);
	gLinkedClasses[theClassName] = theClass;
}

function RegisterLinkedConstructor(theClassName, theConstructor)
{
	gLinkedClassConstructors[theClassName] = theConstructor;
}

function CreateLinkedObject(theClassName, theCObj)	
{
	if (gLinkedObjectTable[theCObj] != nil)
		return;
	
	gLinkedClassNames[theCObj] = theClassName;
	
	local aClass = gLinkedClasses[theClassName];
	if (aClass)
	{
		gLinkedDynCallTable.a = aClass["new"];
		local aLuaObject = gLinkedDynCallTable@aClass:a(theCObj);
		gLinkedObjectTable[theCObj] = aLuaObject;
	}
	else
	{
		local aConstructor = gLinkedClassConstructors[theClassName];
		local aLuaObject = aConstructor(theCObj);
		gLinkedObjectTable[theCObj] = aLuaObject;
	}
}

function SetLinkedObject(theCObj, theLuaObject)
{
	:gLinkedObjectTable[theCObj] = theLuaObject;
}

function DeleteLinkedObject(theCObj)
{
	local anObj = :gLinkedObjectTable[theCObj];
	if (anObj == nil)
		return;
	
	//print("DeleteLinkedObject: class = "..gLinkedClassNames[theCObj]..", address = "..tostring(theCObj));
	gLinkedClassNames[theCObj] = nil;
	
	if (table.contains(anObj, "CObjDeleted"))
		anObj:CObjDeleted();

	:gLinkedObjectTable[theCObj] = nil;
}

function CallLinkedMethod(theCObject, theMethodName, ...)
{
	local aLuaObject = gLinkedObjectTable[theCObject];	
	:gLinkedDynCallTable.a = aLuaObject[theMethodName];	
	return gLinkedDynCallTable@aLuaObject:a(unpack(arg));
}

function GetLinkedLuaObject(theCObject)
{
	return gLinkedObjectTable[theCObject];
}

Common.lua:

require("ClassLink.lua");
require("LuaApp.lua");
require("LuaWidget.lua");
require("DProps.lua");
require("LuaCommonWidgets.lua");
require("Consts.lua");
require("QRand.lua");

function Color(r, g, b, a)
{
	if (a == nil)
		return {r=r, g=g, b=b, a=255};
	else
		return {r=r, g=g, b=b, a=a};
}

function IColor(r, g, b, a)
{
	if (a ~= nil)
		return (a*0x1000000)+(r*0x10000)+(g*0x100)+b;
	else
		return (0xFF000000)+(r*0x10000)+(g*0x100)+b;
}

function Rect(x, y, width, height)
{
	return {mX=x, mY=y, mWidth=width, mHeight=height};
}

function RectContains(theRect, x, y)
{
	return ((x >= theRect.mX) && (y >= theRect.mY) &&
		(x < theRect.mX + theRect.mWidth) && (y < theRect.mY + theRect.mHeight));
}

function RectUnpack(theRect)
{
	return theRect.mX, theRect.mY, theRect.mWidth, theRect.mHeight;
}

function RectIntersects(theRect1, theRect2)
{
	return !((theRect2.mX + theRect2.mWidth <= theRect1.mX) ||
			(theRect2.mY + theRect2.mHeight <= theRect1.mY) ||
			(theRect2.mX >= theRect1.mX + theRect1.mWidth) ||
			(theRect2.mY >= theRect1.mY + theRect1.mHeight));
}

function RectIntersection(theRect1, theRect2)
{
	local x1 = Math.max(theRect1.mX, theRect2.mX);
	local x2 = Math.min(theRect1.mX + theRect1.mWidth, theRect2.mX + theRect2.mWidth);
	local y1 = Math.max(theRect1.mY, theRect2.mY);
	local y2 = Math.min(theRect1.mY + theRect1.mHeight, theRect2.mY + theRect2.mHeight);
	if (x2 - x1 < 0 || y2 - y1 < 0)
		return Rect(0,0,0,0);
	else
		return Rect(x1, y1, x2 - x1, y2 - y1);
}

function Point(x, y) { return {mX = x, mY = y}; }
function Vector(x, y) { return {mX = x, mY = y}; }
function VecDotProd(vec1, vec2) { return vec1.mX*vec2.mX + vec1.mY*vec2.mY; }
function VecMagnitude(theVec) { return Math.sqrt(VecDotProd(theVec, theVec)); }
function VecNormalize(theVec) { local aMag = VecMagnitude(theVec); return VecScale(theVec, 1/aMag); }
function VecScale(theVec, theScale) { return { mX = theVec.mX*theScale, mY = theVec.mY*theScale }; }


function RandFloat()
{
	return (Rand()%100000000)/100000000.0;
}

function DupTable(theTable)
{
	local aNewTab = {};
	for aKey, aVal in theTable
	{
		aNewTab[aKey] = aVal;
	}
	return aNewTab;
}

function GetDirectoryFromPos(thePos)
{
	local aFileName = string.sub(thePos[0], 1);

	local aSlashPos = 0;
	for (i < string.len(aFileName))
		if (string.sub(aFileName, i, 1) == "\\")
			aSlashPos = i;
	
	return string.sub(aFileName, 0, aSlashPos);
}

function URLEncode(theString)
{
	local aHexChars = "0123456789ABCDEF";
	local aString = "";
	
	if (theString == nil) return aString;

	for (i < string.len(theString))
	{
		local aPattern = string.sub(theString,i,1);
		if (string.find(aPattern, "[%w]") == nil) aPattern = "%"..aPattern; // escape non-alphanumeric characters
		if (string.sub(theString,i,1) == " ")
			aString = aString.."+";
		else if (string.find("?&%+\r\n\t", aPattern) != nil)
		{
			aString = aString.."%";
			aString = aString..string.sub(aHexChars,bit.band(int(string.byte(theString,i)/16),0xF),1);
			aString = aString..string.sub(aHexChars,bit.band(int(string.byte(theString,i)),0xF),1);
		}			
		else
		{
			aString = aString..string.sub(theString,i,1);
		}
	}
	return aString;
}

Consts.lua:

def KEYCODE = {};
setprot(KEYCODE);
const KEYCODE.TAB				= 0x09;
const KEYCODE.RETURN			= 0x0D;
const KEYCODE.SHIFT				= 0x10;
const KEYCODE.CONTROL			= 0x11;
const KEYCODE.MENU				= 0x12;
const KEYCODE.ALT				= 0x12;
const KEYCODE.ESCAPE			= 0x1B;
const KEYCODE.PGUP				= 0x21;
const KEYCODE.PGDN				= 0x22;
const KEYCODE.END				= 0x23;
const KEYCODE.HOME				= 0x24;
const KEYCODE.LEFT				= 0x25;
const KEYCODE.UP				= 0x26;
const KEYCODE.RIGHT				= 0x27;
const KEYCODE.DOWN				= 0x28;
const KEYCODE.INSERT			= 0x2D;
const KEYCODE.DELETE			= 0x2E;

def D3DIMAGEFLAGS = {};
setprot(D3DIMAGEFLAGS);
const D3DIMAGEFLAGS.MinimizeNumSubdivisions	= 0x0001;
const D3DIMAGEFLAGS.Use64By64Subdivisions	= 0x0002;
const D3DIMAGEFLAGS.UseA4R4G4B4				= 0x0004;
const D3DIMAGEFLAGS.UseA8R8G8B8				= 0x0008;

def CURSOR = {};
setprot(CURSOR);
const CURSOR.POINTER			= 0;
const CURSOR.HAND				= 1;
const CURSOR.DRAGGING			= 2;
const CURSOR.TEXT				= 3;
const CURSOR.CIRCLE_SLASH		= 4;
const CURSOR.SIZEALL			= 5;
const CURSOR.SIZENESW			= 6;
const CURSOR.SIZENS				= 7;
const CURSOR.SIZENWSE			= 8;
const CURSOR.SIZEWE				= 9;
const CURSOR.WAIT				= 10;
const CURSOR.NONE				= 11;
const CURSOR.CUSTOM				= 12;

def DIALOG = {};
setprot(DIALOG);
def DIALOG.BUTTONS = {};
setprot(DIALOG.BUTTONS);
const DIALOG.BUTTONS.NONE		= 0;
const DIALOG.BUTTONS.YES_NO		= 1;
const DIALOG.BUTTONS.OK_CANCEL	= 2;
const DIALOG.BUTTONS.FOOTER		= 3;
def DIALOG.ID = {};
setprot(DIALOG.ID);
const DIALOG.ID.YES				= 1000;
const DIALOG.ID.NO				= 1001;
const DIALOG.ID.OK				= 1000;
const DIALOG.ID.CANCEL			= 1001;
const DIALOG.ID.FOOTER			= 1000;

def DRAWMODE = {};
setprot(DRAWMODE);
const DRAWMODE.NORMAL			= 0;
const DRAWMODE.ADDITIVE			= 1;

DProps.lua:

class DProps : LuaWidget;

class DPropsShared
{
	def mWidgetTypes = {};
	def mWidgetTypeOrder = {};
	def mObjects = {};
	def mWidgetToLuaWidget = {};
	def mCurAddWidgetType;
	def mLastAddWidgetX;
	def mLastAddWidgetY;
	def mGridOn;
	def mGridSizeX;
	def mGridSizeY;
	def mGridOfsX;
	def mGridOfsY;
}

function DPropsShared:Load()
{
	mLastAddWidgetX = gCApp:RegistryRead("LastAddWidgetX");
	mLastAddWidgetY = gCApp:RegistryRead("LastAddWidgetY");
	mGridOn = gCApp:RegistryRead("GridOn", false);
	mGridSizeX = gCApp:RegistryRead("GridSizeX", 8);
	mGridSizeY = gCApp:RegistryRead("GridSizeY", 8);
	mGridOfsX = gCApp:RegistryRead("GridOfsX", 0);
	mGridOfsY = gCApp:RegistryRead("GridOfsY", 0);
}

function DPropsShared:Save()
{
	gCApp:RegistryWrite("LastAddWidgetX", mLastAddWidgetX);
	gCApp:RegistryWrite("LastAddWidgetY", mLastAddWidgetY);
	gCApp:RegistryWrite("GridOn", mGridOn);
	gCApp:RegistryWrite("GridSizeX", mGridSizeX);
	gCApp:RegistryWrite("GridSizeY", mGridSizeY);
	gCApp:RegistryWrite("GridOfsX", mGridOfsX);
	gCApp:RegistryWrite("GridOfsY", mGridOfsY);
}

DProps.mShared = DPropsShared:new();
DProps.mEditing = false;

function dptrack(thePos, theTrackInfo)
{
	local anObj = theTrackInfo[0];
	
	// Get object tab
	local anObjTab = DProps.mShared.mObjects[anObj];
	
	// Get file table
	local aFileTab = anObjTab.mFiles[thePos[0]];
	if (aFileTab == nil)
	{
		aFileTab = {};
		anObjTab.mFiles[thePos[0]] = aFileTab;
	}
	
	// Store setting information
	aFileTab[thePos[1]] = {theTrackInfo[1], theTrackInfo[2], theTrackInfo[3], theTrackInfo[4]};
	
	// Get props table
	for (aPropNum < *theTrackInfo[3])
	{
		anObjTab.mProps[theTrackInfo[3][aPropNum]] = theTrackInfo[4][aPropNum];
		local aPropsPos = anObjTab.mPropsPos[theTrackInfo[3][aPropNum]];
		if (aPropsPos != nil)
			anObjTab.mFiles[aPropsPos[0]][aPropsPos[1]] = nil; // Remove the old entry
		anObjTab.mPropsPos[theTrackInfo[3][aPropNum]] = thePos;
	}
}

function DProps.AddWidgetType(theName, theClass, theImage, theImageNum)
{
	if (*DProps.mShared.mWidgetTypeOrder == 0)
		DProps.mShared.mCurAddWidgetType = theName;
	
	if (DProps.mShared.mWidgetTypes[theName] == nil)
		DProps.mShared.mWidgetTypeOrder[*DProps.mShared.mWidgetTypeOrder] = theName;
	
	DProps.mShared.mWidgetTypes[theName] = 
		{mName=theName, mClass=theClass, mImage=theImage, mImageNum=theImageNum};
}

function DProps.WidgetCreated(theWidget)
{
	local aCWidget = theWidget.mCObj:GetWidget();
	DProps.mShared.mWidgetToLuaWidget[aCWidget] = theWidget;
	
	local anObjTab = {mProps={}, mPropsPos={}, mFiles={}, mDrawOrder=0};
	DProps.mShared.mObjects[theWidget] = anObjTab;
}

function DProps.WidgetDeleted(theWidget)
{
	DProps.mShared.mObjects[theWidget] = nil;
	local aCWidget = theWidget.mCObj:GetWidget();
	DProps.mShared.mWidgetToLuaWidget[aCWidget] = nil;
}

//////////////////////////////////////////////////////////////////////////

function DProps.DProps_Loader(theCObj)
{
	DProps.mShared:Load();
	require("DProps_Editor.lua");
	return DProps:new(theCObj);
}

RegisterLinkedConstructor("DPropsWidget", DProps.DProps_Loader);

LuaApp.lua:

class LuaApp;

def gApp;
def gCApp;

function LuaApp:Init(theCObj)
{
	def mCObj = theCObj;
	gApp = self;
	gCApp = theCObj;
}

function LuaApp:WrapWidget(theClass, theDefaultClass, theCObj)
{
	if (theClass == nil)
		theClass = theDefaultClass;
	local aNewObj = theClass:new(theCObj);
	return aNewObj;}

function LuaApp:CreateLuaWidget(theClass)
{
	return WrapWidget(theClass, LuaWidget,
		mCObj:CreateLuaWidget());
}

function LuaApp:CreateHyperlinkWidget(theId, theListener, theClass)
{
	return WrapWidget(theClass, LuaHyperlinkWidget, 
		mCObj:CreateLuaHyperlinkWidget(theId, theListener.mCObj));
}

function LuaApp:CreateTextWidget(theClass)
{
	return WrapWidget(theClass, LuaTextWidget, 
		mCObj:CreateLuaTextWidget());
}

function LuaApp:CreateButtonWidget(theId, theListener, theClass)
{
	return WrapWidget(theClass, LuaButtonWidget, 
		mCObj:CreateLuaButtonWidget(theId, theListener.mCObj));
}

function LuaApp:CreateCheckboxWidget(theUncheckedImage, theCheckedImage, theId, theListener, theClass)
{
	return WrapWidget(theClass, LuaCheckboxWidget, 
		mCObj:CreateLuaCheckboxWidget(theUncheckedImage, theCheckedImage, theId, theListener.mCObj));
}

function LuaApp:CreateScrollbarWidget(theId, theListener, theClass)
{
	return WrapWidget(theClass, LuaScrollbarWidget, 
		mCObj:CreateLuaScrollbarWidget(theId, theListener.mCObj));
}

function LuaApp:CreateEditWidget(theId, theListener, theClass)
{
	return WrapWidget(theClass, LuaEditWidget, 
		mCObj:CreateLuaEditWidget(theId, theListener.mCObj));
}

function LuaApp:CreateListWidget(theId, theFont, theListener, theClass)
{
	return WrapWidget(theClass, LuaListWidget, 
		mCObj:CreateLuaListWidget(theId, theFont, theListener.mCObj));
}

function LuaApp:CreateDialog(theComponentImage, theButtonComponentImage,
	theId, isModal, theDialogHeader, theDialogLines, theDialogFooter, theButtonMode, theClass)
{
	local aWidget = WrapWidget(theClass, LuaWidget, 
		mCObj:CreateLuaDialog(theComponentImage, theButtonComponentImage,
		theId, isModal, theDialogHeader, theDialogLines, theDialogFooter, theButtonMode));
	gCApp:AddDialog(aWidget.mCObj);
	return aWidget;
}

function LuaApp:DialogButtonPress(theDialogId, theButtonId)
{
}
	
function LuaApp:DialogButtonDepress(theDialogId, theButtonId)
{
	mCObj:KillDialog(theDialogId);
}

function LuaApp:DebugKeyDown(theKey)
{
	return mCObj:DebugKeyDown(theKey);
}

RegisterLinkedClass("LuaApp", LuaApp);

LuaCommonWidgets.lua:

//////////////////////////////////////////////////////////////////////////

class LuaDialog : LuaWidget(theCObj)
{
	super:Init(theCObj);
}

function LuaDialog:Create(theComponentImage, theButtonComponentImage,
	theId, isModal, theDialogHeader, theDialogLines, theDialogFooter, theButtonMode)
{
	local aWidget = gApp:CreateDialog(theComponentImage, theButtonComponentImage,
		theId, isModal, theDialogHeader, theDialogLines, theDialogFooter, theButtonMode, self);
	gCApp:GetWidgetManager():AddWidget(aWidget.mCObj);
	return aWidget;
}

//////////////////////////////////////////////////////////////////////////

class LuaButtonWidget : LuaWidget(theCObj)
{
	super:Init(theCObj);
}

function LuaButtonWidget:Create(theId, theParent)
{
	local aWidget = gApp:CreateButtonWidget(theId, theParent, self);
	theParent:AddWidget(aWidget);
	return aWidget;
}

function LuaButtonWidget:SetLabel(theStr)
{
	mCObj:SetLabel(theStr);
}

function LuaButtonWidget:SetLabel_DYN(thePos, theStr)
{
	SetLabel(theStr);
	dptrack(thePos, {self, SetLabel, 0, {"Label"}, {theStr}});
}

function LuaButtonWidget.DPGetDefaultSize()
{
	return 80, 24;
}

function LuaButtonWidget.DPGetCreateDefault(thePos, theParent, theX, theY, theWidth, theHeight)
{
	local aWidget = LuaButtonWidget:Create(-1, theParent);
	thePos[1]++; 
	aWidget:SetLabel_DYN(thePos, "Button");
	thePos[1]++;
	aWidget:Resize_DYN(thePos, theX, theY, theWidth, theHeight);
	
	return aWidget, {"local aButton = LuaButtonWidget:Create(-1, self);",
		"aButton:SetLabel_DYN(__POS, \"Button\");",
		"aButton:Resize_DYN(__POS, " .. theX .. ", " .. theY .. ", " .. theWidth .. ", " .. theHeight .. ");"};
}

//////////////////////////////////////////////////////////////////////////

class LuaCheckboxWidget : LuaWidget(theCObj)
{
	super:Init(theCObj);
}

function LuaCheckboxWidget:Create(theId, theParent)
{
	local aWidget = gApp:CreateCheckboxWidget(nil, nil, theId, theParent, self);
	theParent:AddWidget(aWidget);
	return aWidget;
}

function LuaCheckboxWidget.DPGetDefaultSize()
{
	return 16, 16;
}

function LuaCheckboxWidget:SetChecked(checked)
{
	mCObj:SetChecked(checked);
}

function LuaCheckboxWidget:SetChecked_DYN(thePos, checked)
{
	SetChecked(checked);
	dptrack(thePos, {self, SetChecked, 0, {"Checked"}, {checked}});
}

function LuaCheckboxWidget.DPGetCreateDefault(thePos, theParent, theX, theY, theWidth, theHeight)
{
	local aWidget = LuaCheckboxWidget:Create(-1, theParent);
	thePos[1]++; 
	aWidget:Resize_DYN(thePos, theX, theY, theWidth, theHeight);
	thePos[1]++; 
	aWidget:SetChecked_DYN(thePos, false);
	
	return aWidget, {"local aCheckbox = LuaCheckboxWidget:Create(-1, self);",
		"aCheckbox:SetChecked_DYN(__POS, false);",
		"aCheckbox:Resize_DYN(__POS, " .. theX .. ", " .. theY .. ", " .. theWidth .. ", " .. theHeight .. ");"};
}

//////////////////////////////////////////////////////////////////////////

class LuaEditWidget : LuaWidget(theCObj)
{
	super:Init(theCObj);
}

function LuaEditWidget:Create(theId, theParent)
{
	local aWidget = gApp:CreateEditWidget(theId, theParent, self);
	theParent:AddWidget(aWidget);
	return aWidget;
}

function LuaEditWidget.DPGetDefaultSize()
{
	return 80, 24;
}

function LuaEditWidget.DPGetCreateDefault(thePos, theParent, theX, theY, theWidth, theHeight)
{
	local aWidget = LuaEditWidget:Create(-1, theParent);
	thePos[1]++; 
	aWidget:Resize_DYN(thePos, theX, theY, theWidth, theHeight);
	
	return aWidget, {"local aEdit = LuaEditWidget:Create(-1, self);",
		"aEdit:Resize_DYN(__POS, " .. theX .. ", " .. theY .. ", " .. theWidth .. ", " .. theHeight .. ");"};
}

//////////////////////////////////////////////////////////////////////////

class LuaListWidget : LuaWidget(theCObj)
{
	super:Init(theCObj);
}

function LuaListWidget:Create(theId, theParent)
{
	local aWidget = gApp:CreateListWidget(theId, nil, theParent, self);
	theParent:AddWidget(aWidget);
	return aWidget;
}

function LuaListWidget.DPGetDefaultSize()
{
	return 80, 64;
}

function LuaListWidget.DPGetCreateDefault(thePos, theParent, theX, theY, theWidth, theHeight)
{
	local aWidget = LuaListWidget:Create(-1, theParent);
	thePos[1]++; 
	aWidget:Resize_DYN(thePos, theX, theY, theWidth, theHeight);
	
	return aWidget, {"local aList = LuaListWidget:Create(-1, self);",
		"aList:Resize_DYN(__POS, " .. theX .. ", " .. theY .. ", " .. theWidth .. ", " .. theHeight .. ");"};
}

//////////////////////////////////////////////////////////////////////////

class LuaTextWidget : LuaWidget(theCObj)
{
	super:Init(theCObj);
}

function LuaTextWidget:Create(theParent)
{
	local aWidget = gApp:CreateTextWidget(self);
	theParent:AddWidget(aWidget);
	return aWidget;
}

function LuaTextWidget:IsPointVisible(theX, theY)
{
	// Allow us to select it if we're editing
	return DProps.mEditing;
}

function LuaTextWidget:SetLabel(theStr)
{
	mCObj:SetLabel(theStr);
}

function LuaTextWidget:SetLabel_DYN(thePos, theStr)
{
	SetLabel(theStr);
	dptrack(thePos, {self, SetLabel, 0, {"Label"}, {theStr}});
}

function LuaTextWidget:SetLineSpacing(theLineSpacing)
{
	mCObj:SetLineSpacing(theLineSpacing);
}

function LuaTextWidget:SetLineSpacing_DYN(thePos, theLineSpacing)
{
	SetLineSpacing(theLineSpacing);
	dptrack(thePos, {self, SetLineSpacing, 0, {"Line Spacing"}, {theLineSpacing}});
}

function LuaTextWidget:SetJustification(theJustification)
{
	mCObj:SetJustification(theJustification);
}

function LuaTextWidget:SetJustification_DYN(thePos, theJustification)
{
	SetJustification(theJustification);
	dptrack(thePos, {self, SetJustification, 0, {"Justification"}, {theJustification}});
}

function LuaTextWidget.DPGetDefaultSize()
{
	return 128, 16;
}

function LuaTextWidget.DPGetCreateDefault(thePos, theParent, theX, theY, theWidth, theHeight)
{
	local aWidget = LuaTextWidget:Create(theParent);
	thePos[1]++; 
	aWidget:SetLabel_DYN(thePos, "Text");
	thePos[1]++;
	aWidget:SetLineSpacing_DYN(thePos, -1);
	thePos[1]++;
	aWidget:SetJustification_DYN(thePos, -1);
	thePos[1]++;
	aWidget:Resize_DYN(thePos, theX, theY, theWidth, theHeight);
	thePos[1]++;
	aWidget:SetColors_DYN(thePos, {0xFFFFFFFF});
	
	return aWidget, {"local aText = LuaTextWidget:Create(self);",
		"aText:SetLabel_DYN(__POS, \"Text\");",
		"aText:SetLineSpacing_DYN(__POS, -1);",
		"aText:SetJustification_DYN(__POS, -1);",
		"aText:Resize_DYN(__POS, " .. theX .. ", " .. theY .. ", " .. theWidth .. ", " .. theHeight .. ");",
		"aText:SetColors_DYN(__POS, {0xFFFFFFFF});"};
}

//////////////////////////////////////////////////////////////////////////

class LuaScrollbarWidget : LuaWidget(theCObj)
{
	super:Init(theCObj);
}

function LuaScrollbarWidget:Create(theId, theParent)
{
	local aWidget = gApp:CreateScrollbarWidget(theId, theParent, self);
	theParent:AddWidget(aWidget);
	return aWidget;
}

function LuaScrollbarWidget:ResizeScrollbar(theX, theY, theWidth, theHeight)
{
	mCObj:ResizeScrollbar(theX, theY, theWidth, theHeight);
}

function LuaScrollbarWidget:ResizeScrollbar_DYN(thePos, theX, theY, theWidth, theHeight)
{
	ResizeScrollbar(theX, theY, theWidth, theHeight);
	dptrack(thePos, {self, LuaScrollbarWidget.ResizeScrollbar, 0, {"X", "Y", "Width", "Height"}, {theX, theY, theWidth, theHeight}});
}

function LuaScrollbarWidget.DPGetDefaultSize()
{
	return 16, 64;
}

function LuaScrollbarWidget.DPGetCreateDefault(thePos, theParent, theX, theY, theWidth, theHeight)
{
	local aWidget = LuaScrollbarWidget:Create(-1, theParent);
	thePos[1]++; 
	aWidget:ResizeScrollbar_DYN(thePos, theX, theY, theWidth, theHeight);
	
	return aWidget, {"local aScrollbar = LuaScrollbarWidget:Create(-1, self);",
		"aScrollbar:ResizeScrollbar_DYN(__POS, " .. theX .. ", " .. theY .. ", " .. theWidth .. ", " .. theHeight .. ");"};
}

//////////////////////////////////////////////////////////////////////////

class LuaHorzScrollbarWidget : LuaScrollbarWidget(theCObj)
{
	super:Init(theCObj);
}

function LuaHorzScrollbarWidget:Create(theId, theParent)
{
	local aWidget = gApp:CreateScrollbarWidget(theId, theParent, self);
	theParent:AddWidget(aWidget);
	aWidget.mCObj:SetHorizontal(true);
	return aWidget;
}

function LuaHorzScrollbarWidget.DPGetDefaultSize()
{
	return 64, 16;
}

function LuaHorzScrollbarWidget.DPGetCreateDefault(thePos, theParent, theX, theY, theWidth, theHeight)
{
	local aWidget = LuaHorzScrollbarWidget:Create(-1, theParent);
	thePos[1]++; 
	aWidget:ResizeScrollbar_DYN(thePos, theX, theY, theWidth, theHeight);
	
	return aWidget, {"local aScrollbar = LuaHorzScrollbarWidget:Create(-1, self);",
		"aScrollbar:ResizeScrollbar_DYN(__POS, " .. theX .. ", " .. theY .. ", " .. theWidth .. ", " .. theHeight .. ");"};
}

//////////////////////////////////////////////////////////////////////////

class LuaHyperlinkWidget : LuaWidget(theCObj)
{
	super:Init(theCObj);
}

function LuaHyperlinkWidget:Create(theId, theParent)
{
	local aWidget = gApp:CreateHyperlinkWidget(theId, theParent, self);
	theParent:AddWidget(aWidget);
	return aWidget;
}

function LuaHyperlinkWidget:SetLabel(theStr)
{
	mCObj:SetLabel(theStr);
}

function LuaHyperlinkWidget:SetLabel_DYN(thePos, theStr)
{
	SetLabel(theStr);
	dptrack(thePos, {self, SetLabel, 0, {"Label"}, {theStr}});
}

function LuaHyperlinkWidget.DPGetDefaultSize()
{
	return 128, 20;
}

function LuaHyperlinkWidget.DPGetCreateDefault(thePos, theParent, theX, theY, theWidth, theHeight)
{
	local aWidget = LuaHyperlinkWidget:Create(-1, theParent);
	thePos[1]++; 
	aWidget:SetLabel_DYN(thePos, "Hyperlink");
	thePos[1]++;
	aWidget:Resize_DYN(thePos, theX, theY, theWidth, theHeight);
	thePos[1]++;
	aWidget:SetColors_DYN(thePos, {0xFFFFFF00, 0xFFFFFFFF});
	
	return aWidget, {"local aHyperlink = LuaHyperlinkWidget:Create(-1, self);",
		"aHyperlink:SetLabel_DYN(__POS, \"Hyperlink\");",
		"aHyperlink:Resize_DYN(__POS, " .. theX .. ", " .. theY .. ", " .. theWidth .. ", " .. theHeight .. ");",
		"aHyperlink:SetColors_DYN(__POS, {0xFFFFFF00, 0xFFFFFFFF});"};
}

//////////////////////////////////////////////////////////////////////////

DProps.AddWidgetType("Button", LuaButtonWidget, nil, 0);
DProps.AddWidgetType("Checkbox", LuaCheckboxWidget, nil, 1);
DProps.AddWidgetType("Edit", LuaEditWidget, nil, 2);
DProps.AddWidgetType("List", LuaListWidget, nil, 3);
DProps.AddWidgetType("Text", LuaTextWidget, nil, 4);
DProps.AddWidgetType("Horz Scrollbar", LuaHorzScrollbarWidget, nil, 5);
DProps.AddWidgetType("Vert Scrollbar", LuaScrollbarWidget, nil, 6);
DProps.AddWidgetType("Hyperlink", LuaHyperlinkWidget, nil, 7);

//////////////////////////////////////////////////////////////////////////

RegisterLinkedClass("LuaButtonWidget", LuaButtonWidget);

LuaWidget.lua:

class LuaWidget(theCObj)
{
	def mCObj = theCObj;
	def mUpdateCnt = 0;
	def mChildren = {};
	setprot(mChildren, "l");
	
	DProps.WidgetCreated(self);
	
	:SetLinkedObject(theCObj, self);
	theCObj:RegisterObject();
}

function LuaWidget:Create(theParent)
{
	local aWidget = gApp:CreateLuaWidget(self);
	theParent:AddWidget(aWidget);
	return aWidget;
}

function LuaWidget:CObjDeleted()
{
	DProps.WidgetDeleted(self);
}

//////////////////////////////////////////////////////////////////////////

function LuaWidget:Update()
{
	mUpdateCnt++;
	mCObj:Update();
}

function LuaWidget:Draw(g)
{
	mCObj:Draw();
}

function LuaWidget:DrawOverlay(g)
{
	mCObj:DrawOverlay();
}

function LuaWidget:IsPointVisible(theX, theY)
{
	return true;
}

function LuaWidget:MouseEnter()
{
	mCObj:MouseEnter();
}

function LuaWidget:MouseLeave()
{
	mCObj:MouseLeave();
}

function LuaWidget:MouseMove(theX, theY)
{
	mCObj:MouseMove(theX, theY);
}

function LuaWidget:MouseDrag(theX, theY)
{
	mCObj:MouseDrag(theX, theY);
}

function LuaWidget:MouseDown(theX, theY, theBtnNum, theClickCount)	
{
	mCObj:MouseDown(theX, theY, theBtnNum, theClickCount);
}

function LuaWidget:MouseUp(theX, theY, theBtnNum, theClickCount)
{
	mCObj:MouseUp(theX, theY, theBtnNum, theClickCount);
}

function LuaWidget:MouseWheel(theDelta)
{
	mCObj:MouseWheel(theDelta);
}

function LuaWidget:KeyChar(theChar)
{
	mCObj:KeyChar(theChar);
}

function LuaWidget:KeyDown(theKey)
{
	mCObj:KeyDown(theKey);
}

function LuaWidget:KeyUp(theKey)
{
	mCObj:KeyUp(theKey);
}

function LuaWidget:GotFocus()
{
	mCObj:GotFocus();
}

function LuaWidget:LostFocus()
{
	mCObj:LostFocus();
}

function LuaWidget:WantsFocus()
{
	return 	mCObj:WantsFocus();
}

///
function LuaWidget:CheckboxChecked(theId, checked)
{
	mCObj:CheckboxChecked(theId, checked);
}

function LuaWidget:ButtonPress(theBtnId, theClickCount)
{
	mCObj:ButtonPress(theBtnId, theClickCount);
}

function LuaWidget:ButtonDepress(theBtnId)
{
	mCObj:ButtonDepress(theBtnId);
}

function LuaWidget:ButtonMouseEnter(theId)
{
	mCObj:ButtonMouseEnter(theId);
}

function LuaWidget:ButtonMouseLeave(theId)
{
	mCObj:ButtonMouseLeave(theId);
}

function LuaWidget:ButtonMouseMove(theId, theX, theY)
{
	mCObj:ButtonMouseMove(theId, theX, theY);
}

function LuaWidget:ButtonDownTick(theId, theClickCount)
{
	mCObj:ButtonDownTick(theId, theClickCount);
}

function LuaWidget:ScrollPosition(theId, thePosition)
{
	mCObj:ScrollPosition(theId, thePosition);
}

function LuaWidget:EditWidgetText(theId, theString)
{
	mCObj:EditWidgetText(theId, theString);
}

function LuaWidget:AllowKey(theId, theKey)
{
	return mCObj:AllowKey(theId, theKey);
}

function LuaWidget:AllowChar(theId, theChar)
{
	return mCObj:AllowChar(theId, theChar);
}

function LuaWidget:AllowText(theId, theText)
{
	return mCObj:AllowText(theId, theText);
}

function LuaWidget:ListClicked(theId, theIdx, theClickCount)
{
	mCObj:ListClicked(theId, theIdx, theClickCount);
}

function LuaWidget:ListClosed(theId)
{
	mCObj:ListClosed(theId);
}

function LuaWidget:ListHiliteChanged(theId, theOldIdx, theNewIdx)
{
	mCObj:ListHiliteChanged(theId, theOldIdx, theNewIdx);
}

//////////////////////////////////////////////////////////////////////////

function LuaWidget:Resize(theX, theY, theWidth, theHeight)
{
	mCObj:Resize(theX, theY, theWidth, theHeight);
}

function LuaWidget:Resize_DYN(thePos, theX, theY, theWidth, theHeight)
{
	Resize(theX, theY, theWidth, theHeight);
	dptrack(thePos, {self, LuaWidget.Resize, 0, {"X", "Y", "Width", "Height"}, {theX, theY, theWidth, theHeight}});
}

function LuaWidget:Move(theX, theY)
{
	mCObj:Resize(theX, theY, mCObj:GetWidth(), mCObj:GetHeight());
}

function LuaWidget:SetColors(theColors)
{
	mCObj:SetColors(theColors);
}

function LuaWidget:SetColors_DYN(thePos, theColors)
{
	mCObj:SetColors(theColors);
	dptrack(thePos, {self, LuaWidget.SetColors, 0, {"Colors"}, {theColors}});
}

function LuaWidget:AddWidget(theWidget)
{
	if (table.contains(theWidget.mCObj, "GetID"))
		mChildren[theWidget.mCObj:GetID()] = theWidget;
	mCObj:AddWidget(theWidget.mCObj);
}

function LuaWidget:RemoveWidget(theWidget)
{
	if (table.contains(theWidget.mCObj, "GetID"))
		mChildren[theWidget.mCObj:GetID()] = nil;
	mCObj:RemoveWidget(theWidget.mCObj);
}


//////////////////////////////////////////////////////////////////////////

RegisterLinkedClass("LuaWidget", LuaWidget);

QRand.lua:

//def mQRand = QRand:new(0.25, 0.5, 0.75, 0.1);

class QRand(...)
{
	def mUpdateCnt = 0;
	
	def mWeights = {};
	def mLastHitUpdate = {};
	def mPrevLastHitUpdate = {};
	def mCurSway = {};
	def mLastIndex = -1;
	
	SetWeights(unpack(arg));
}

function QRand:SetWeights(...)
{
	if (arg.n == 1)
	{
		mWeights[0] = 1.0 - arg[0];
		mWeights[1] = arg[0];
	}
	else
	{
		local aTotal = 0.0;
		for (anArgNum < arg.n)
		{
			mWeights[anArgNum] = arg[anArgNum];
			aTotal += mWeights[anArgNum];
		}
		
		for (aWeightNum < *mWeights)
			mWeights[aWeightNum] /= aTotal;
	}
	
	for (i = *mLastHitUpdate, *mWeights-1, 1)
	{
		mLastHitUpdate[i] = 0;
		mPrevLastHitUpdate[i] = 0;
	}
}

function QRand:Next()
{
	mUpdateCnt++;

	//local aStr = "";
	local aTotalWeight = 0.0;
	for (aResultNum,aWeight in mWeights)
	{
		if (aWeight != 0)
		{
			local anAvgLength = 1.0 / aWeight;
			local aPctOff = 1.0 + (((mUpdateCnt - mLastHitUpdate[aResultNum]) - anAvgLength) / anAvgLength);
			local aPrevPctOff = 1.0 + (((mUpdateCnt - mPrevLastHitUpdate[aResultNum]) - anAvgLength*2) / (anAvgLength*2));
			
			local aSway = aWeight * math.max(math.min(aPctOff*0.75 + aPrevPctOff*0.25, 3.0), 0.333);
			//aSway = aWeight; // Comment this in to get a normal random number generator
			mCurSway[aResultNum] = aSway;
			aTotalWeight += aSway;
			//aStr = aStr .. aSway .. " ";
		}
		else
			mCurSway[aResultNum] = 0;
	}
		
	
	
	local aRand = math.random() * aTotalWeight;
	local anIdx = 0;
	while (aRand > mCurSway[anIdx])
	{
		aRand -= mCurSway[anIdx];
		anIdx++;
	}
	
	mPrevLastHitUpdate[anIdx] = mLastHitUpdate[anIdx];
	mLastHitUpdate[anIdx] = mUpdateCnt;
	
	mLastIndex = anIdx;
	return anIdx;
}