# Include file for template character generation.

array orChooseArray[0];

func dispItemNameCost(listName, name) {
	local cost, class, itemName, ind, value, origName;
	ind = strindex(name, '/[={]/');
	if (ind > 0)
		itemName = substr(name, 1, ind - 1);
		value = substr(name, ind, strlen(name) - ind + 1);
	else
		itemName = name;
		value = "";
	endif
	ind = strindex(itemName, '[');
	if (ind > 0)
		origName = substr(itemName, 1, ind - 1);
		itemName = substr(itemName, ind + 1, strindex(itemName, ']') - ind - 1);
	else
		origName = itemName;
	endif
	if (listName = "Skills")
		if (inCategory("Martial Arts Style"))
			return format("%s [%s]", itemName, optValue('Style Cost'));
		endif
		class = listItemInfo(concat("*", listName), origName, "@class@");
		return format("%s%s [%s]", itemName, value, class);
	endif

	cost = listItemInfo(concat("*", listName), origName, "@c@");
	if (cost = 0)
		return format("%s%s [Var.]", itemName, value);
	endif
	return format("%s%s [%d]", itemName, value, cost);
}

func dispNameCost(listName, nm) {
	local i, n, result, name;
	name = replaceString(nm, "/{[^}]+}/", "");
	n = split(name, "/ +OR +/", orChooseArray);
	i = 1;
	result = "";
	while (i <= sizeof(orChooseArray))
		if (strlen(result) > 0)
			result = concat(result, " OR ");
		endif 
		result = concat(result, dispItemNameCost(listName, orChooseArray[i]));
		i = i + 1;
	endwhile

	return result;
}

array items[0];

sub DebugMsg(msg) {
	if (debugging)
		warn "$$msg";
	endif
}

array ignoreKW[] = "?choose", "?endchoose", "?suppdups", "?title", "?multiple";

func ignoreName(name)
{
	local i;
	i = 1;
	while (i <= sizeof(ignoreKW))
		if (strindex(name, ignoreKW[i]) > 0)
			return true;
		endif
		i = i + 1;
	endwhile
	return false;
}


# Check to see that items for the current item are available for selection. This will
# be removed in production, but it ensures the presence of all items referenced.

array honk[0];
array woof[0];

sub checkItems(nm) {
	var i, j;
	name = nm;
	options
	Items ~
		n = addElement(items, @optTextValue);
		end
	endoptions
	while (sizeof(items) > 0)
		string = items[1];
		if (strindex(string, "|") > 0)
			string = substr(string, strindex(string, "|") + 1, strlen(string));
		endif
		if (strindex(string, "&") = 1)
			# Append and use previous listname.
			if (strindex(string, "?") = 2)
				ind = strindex(string, ":");
				itemList = substr(string, ind+1, strlen(string) - ind);
			else
				itemList = substr(string, 2, strlen(string) - 1);
			endif
		else
			n = split(string, "~", "listName", "itemlist");
			listName = replaceString(listName, "/^\?{.+}/", "");
		endif
		if (strindex(listName, ":") > 0)
			listName = substr(listName, strindex(listName, ":") + 1, strlen(listName));
		endif
		if (listName = "")
			appendProgress "list missing in $$name";
		endif
		if (strindex(itemlist, "/^\#?[-0-9¼½¾.]+:/") > 0)
			itemlist = substr(itemlist, strindex(itemlist, ":") + 1, strlen(itemList));
		endif
		n = split(itemlist, "/ *, */", honk);
		for (i = 1; i <= sizeof(honk); i = i + 1)
			n = split(honk[i], "/ +OR +/", woof);
			for (j = 1; j <= sizeof(woof); j = j + 1)
				n = split(woof[i], "/[[{=]/", "name", "junk");
				if (name = "")
					appendProgress "Empty item in $$itemlist";
				endif
				if (strindex(name, "/^([0-9¼½¾.]*:)?\*/") <= 0 and strindex(name, "/^(ST|DX|HT|IQ|Fatigue)[-+]/") <= 0)
					if (!ignoreName(name))
						if (strindex(name, "/[!?]/") = 1)
							name = substr(name, 2, strlen(name));
						endif
						ind = strindex(name, "::");
						if (ind > 0)
							specList = substr(name, 1, ind - 1);
							specName = substr(name, ind + 2, strlen(name) - ind - 1);
							if (!$+available($$specList, $$specName))
								appendProgress "$$name not available";
							endif
						else
							if (!$+available($$listName, $$name))
								appendProgress "$$name not available";
							endif
						endif
					endif
				endif
			endfor
		endfor
		n = removeElement(items, 1);
	endwhile
}


sub select(itemList) {
	array optValues[0];
	array varNames[0];
	array selectedValues[0];
	var n, ind, val, ok, i, pickRand, defined, prompt;

	if (strindex(itemList, "/^\?{/") > 0)
		ind = strindex(itemList, "}");
		cond = substr(itemList, 3, ind-3);
		if (!eval(cond))
			return;
		endif
		itemList = substr(itemList, ind+1, strlen(itemList)-ind);
	endif
	prompt = "";
	if (strindex(itemList, "{") = 1)
		ind = strindex(itemList, "}");
		if (ind > 0)
			prompt = substr(itemList, 2, ind - 2);
			itemList = substr(itemList, ind+1, strlen(itemList) - ind);
		endif
	endif
	n = split(itemList, ":", "optName", "values");
	n = split(values, "/ *, */", optValues);
	if (strindex(optName, ",") > 1)
		n = split(optName, "/ *, */", varNames);
		if (randGen)
			pickRand = true;
		else
			if (prompt = "")
				prompt = format("Select %d of the following.", sizeof(varNames));
			endif
			defined = false;

			# Query user till right number of specialties picked.

			while (!defined)
				n = sizeof(varNames);
				dialog ok, Select $$n Specialties;
					keyword Templates: Select Specialty;
					text $$prompt;
					listbox selectedValues, Specialties:, Random, $$optValues;
					checkbox randGen, &Generate Randomly;
					end
				if (!ok) exit; endif
				if (val = 'Random' or randGen)
					pickRand = true;
					defined = true;
				else
					pickRand = false;
					if (sizeof(varNames) = sizeof(selectedValues))
						# Assign the selected values to the variables.
						defined = true;
						for (i = 1; i <= sizeof(varNames); i = i + 1)
							n = split(selectedValues[i], "!kludge!", varNames[i]);
						endfor
					else
						dialog ok, Incorrect Number Selected;
							text "You must select exactly $$n specialties.";
							text "Click OK to try again or Cancel to exist.";
							end
						if (!ok) exit; endif
					endif
				endif
			endwhile
		endif

		if (pickRand)
			for (i = 1; i <= sizeof(varNames); i = i + 1)
				ind = rand(sizeof(optValues));
				val = optValues[ind];
				n = removeElement(optValues, ind);
				# Assign the 
				n = split(val, "!kludge!", varNames[i]);
			endfor
		endif
	else
		if (randGen)
			val = optValues[rand(sizeof(optvalues))];
		else
			val = 0;
			if (prompt = "")
				prompt = "Select one of the following.";
			endif
			dialog ok, Select $$optName;
				keyword Templates: Select Specialty;
				text $$prompt;
				listbox val, $$optName:, Random, $$optValues;
				checkbox randGen, &Generate Randomly;
				end
			if (!ok) exit; endif
			if (val = 'Random' or randGen)
				val = optValues[rand(sizeof(optValues))];
			endif
		endif
		# Trick to assign value into variable named in optName.
		n = split(val, "!kludge!", optName);
	endif
}


array templates[0];
array cats[0];

# The opts array must be set already.

sub setOptions(listName, itemName) {
	var j;
	if (sizeof(opts) > 0)
		for (j = 1; j <= sizeof(opts); j = j + 1)
			if (opts[j] = "open" or opts[j] = "close")
				ind = strindex(itemName, "[");
				if (ind > 0)
					itemName = substr(itemName, ind + 1, strlen(itemName) - ind - 1);
				endif
				if (opts[j] = "close")
					closesublist $$listName.$$itemName;
				else
					opensublist $$listName.$$itemName;
				endif
			else
				n = split(opts[j], "=", "name", "value");
				option $$name = value;
			endif
		endfor
	endif
}


# Choose one item from a category. The category is presumed to
# start with an asterisk.


sub chooseCat(listName, starcat) {
	array itemNames[0];
	var mainCat, addCat, inCat;

	# Handle a selection from a category.

	n = removeAllElements(itemNames);
	cat = substr(starcat, 2, strlen(starcat) - 1);

	nn = split(cat, "=", "cat", "level");

	n = split(cat, "/ *; */", "mainCat", "addCat");
	foreach (*$$listName : $$mainCat)
		if (addCat)
			inCat = inCategory(addCat);
		else
			inCat = true;
		endif
		if (!@see and inCat)
			n = addElement(itemNames, @name);
		endif
	endforeach

	if (sizeof(itemNames) > 0)
		n = sortElements(itemNames, 1);
		if (randGen)
			nm = ChooseRandomItem(listName, itemNames);
		else
			nm = 0;
			dialog ok, Choose $$cat from $$listName;
				keyword Templates: Choose Category;
				listbox nm, $$listName:, Random, $$itemNames;
				checkbox randGen, &Generate Randomly;
				end
			if (nm = 'Random' or randGen)
				nm = ChooseRandomItem(listName, itemNames);
			endif
		endif
		if (!ok) exit; endif

		if (level <> "")
			nm = format('%s=%d', nm, level);
		endif
	else
		warning "No items were found in category $$cat.";
		return 0;
	endif

	return nm;
}

sub IncreaseCost(desiredCost) {
	_lastCost = 0;
	while (abs(@cost) < abs(desiredCost) and _lastCost != @cost)
		_lastCost = @cost;
		set $@level = @level + 1;
	endwhile
}


sub getUniqueName(listName, actualName) {
	var count = 0;
	var str;
	var paren;
	var done;
	var orgName = actualName;

	if (!$+inList($$listName, $$actualName))
		return actualName;
	endif

	while ($+inList($$listName, $$actualName))
		paren = strindex(actualName, "/\([0-9]+\)$/");
		if (paren > 0)
			str = substr(actualName, paren+1, strlen(actualName) - paren - 1);
			count = eval(str) + 1;
			actualName = replaceString(actualName, "/\([0-9]+\)$/", "($$count)");
		else
			actualName = format('%s (2)', actualName);
		endif
	endwhile

	done = false;

	while (!done)
		dialog ok, Item Already Present;
			text "There is already an item in the list with the name '$$orgName'.";
			text "Enter a unique name that should be used instead:";
			edittext actualName, "Name:";
			end
		if (!ok) return false; endif
		done = !$+inList($$listName, $$actualName);
	endwhile

	return actualName;
}


array itemArr[0];
array choose[0];
array opts[0];

# Add the specified item to the list. Options may be
# present specified by {Option 1=value/Option 2=value}, and a level
# may be specified with =.
# Return true if something editable was added.
# Set addedNewItem, addItemListName and addItemName.

sub addItem(listName, nm, nodup) {
	var actualName;
	var useOldItem;
	var addAsName = "";
	var ok, ind;
	var editable, n;

	editable = true;

	if (strindex(nm, "/^(ST|DX|HT|IQ|Fatigue)[-+]/") > 0)
		sign = strindex(nm, '-') > 0 ? -1 : +1;
		n = split(nm, "/[-+]/", "stat", "inc");
		inc = sign * inc;
		if (stat = "ST")
			Main.ST = $@st+inc;
		elseif (stat = "DX")
			Main.DX = $@dx+inc;
		elseif (stat = "HT")
			Main.HT = $@ht+inc;
		elseif (stat = "IQ")
			Main.IQ = $@iq+inc;
		elseif (stat = "Fatigue")
			Main.x_st = $@x_st+inc;
		else
			warning "Bad attribute increase: $$nm";
		endif
		return false;
	endif

	if (strindex(nm, "{"))
		# To include a '/' in the option name, precede by \.
		n = split(nm, "{", "nm", "optionList");
		optionList = replaceString(optionList, "/}$/", "");
		optionList = replaceString(optionList, "/([^\\])//", "\1!!!");
		optionList = replaceString(optionList, "\/", "/");
		n = split(optionList, "!!!", opts);
	else
		n = removeAllElements(opts);
	endif

	ind = strindex(nm, "::");
	if (ind > 0)
		listName = substr(nm, 1, ind - 1);
		nm = substr(nm, ind+2, strlen(nm) - ind - 1);
	endif

	openwindow $$listName;

	defBased = false;
	if (strindex(nm, "="))
		n = split(nm, "=", "name", "level");
		if (strindex(level, "def"))
			defBased = true;
			level = replaceString(level, "def", "");
		endif
	else
		name = nm;
		level = 0;
	endif

	if (strindex(level, "#") > 0)
		desiredCost = 0+replaceString(level, "#", "");
		costSpecified=true;
	else
		costSpecified=false;
	endif

	ind = strindex(name, "[");

	if (ind > 0)
		# Item is renamed as it's added.
		actualName = substr(name, ind + 1, strlen(name) - ind - 1);
		actualName = replaceString(actualName, "/ * ] *$/", "");
		actualName = replaceString(actualName, "/^ */", "");
		addAsName = "[$$actualName]";
		name = substr(name, 1, ind - 1);
	else
		actualName = name;
	endif

	if (nodup)
		actualName = getUniqueName(listName, actualName);
		if (!actualName)
			return false;
		endif
		addAsName = "[$$actualName]";
		useOldItem = false;
	else
		useOldItem = $+inList($$listName, $$actualName);
	endif

	addItemListName = listName;
	addItemName = actualName;

	if (useOldItem)
		# Item already in list: change existing item.
		addedNewItem = false;

		item ($$listName, actualName)
			if (costSpecified)
				IncreaseCost(desiredCost);
			elseif (level != 0)
				if (defBased)
					set $@default = 1;
				endif
				if (@level < level)
					set $@level = level;
				endif
			endif
			setOptions(listName, actualName);
			set $@autoParent = parent;
		enditem
	else
		addedNewItem = true;
		if (costSpecified)
			add $$listName.$$name$$addAsName;
			item ($$listName, actualName)
				IncreaseCost(desiredCost);
			enditem
		elseif (level != 0)
			add $$listName.$$name$$addAsName = level;
			if (defBased)
				set $@default = 1;
				set $@level = level;
			endif
		else
			add $$listName.$$name$$addAsName;
		endif
		set $@autoParent = parent;
		setOptions(listName, actualName);
	endif

	return editable;
}

# If list is skill, select a random skill level if there are character
# points left. Since most skills are at 1/2 to 4 points, we just pick
# a random number in that range.

sub randSkill(listName) {
	if (listName != "Skills")
		return;
	endif

	# No points to play with: just leave skill at value added.

	availPoints = $@beginningPoints - $@total;
	if (availPoints <= 0)
		return;
	endif

	cost = qindex(rand(4), .5, 1, 2, 4);
	cost = min(availPoints, cost);

	attempts = 0;
	while ($@@cost < cost and attempts < 10)
		set $@level = $@@level + 1;
		attempts = attempts+1;
	endwhile
}

# Choose an item at random. Prefer items that satisfy the
# requirements, but if all fail, the last one tried will be
# returned.

array randomIndexes[0];

sub ChooseRandomItem(listName, chooseArray) {
	n = removeAllElements(randomIndexes);
	for (cri = 1; cri <= sizeof(chooseArray); cri = cri + 1)
		n = addElement(randomIndexes, cri);
	endfor

	nm = 0;
	while (sizeof(randomIndexes) > 0)
		randIndex = rand(sizeof(randomIndexes));
		nm = chooseArray[randomIndexes[randIndex]];

		# Handle the occurrence of OR clauses or categories.

		if (strindex(nm, "/ OR /") > 0 or strindex(nm, "*") = 1)
			return nm;
		endif
		if (strindex(nm, "/^(ST|DX|HT|IQ|Fatigue)[-+]/") > 0)
			# Allow these to always be chosen.
			return nm;
		endif
		reqSatisfied = CheckItemReq(listName, nm);
		if (reqSatisfied)
			return nm;
		endif
		# This item doesn't satisfy requirements. Try another.
		n = removeElement(randomIndexes, randIndex);
	endwhile

	return nm;
}


# Check that the named item satisifies requirements and isn't already present.
# The name may include a level or options, which will be stripped off. Return
# true if the item satisfies requirements and isn't present.

sub CheckItemReq(listName, nm) {
	n = split(nm, "/[{=]/", "baseItemName", "junk");
	ciri = strindex(baseItemName, "[");
	if (ciri > 0)
		originalName = substr(baseItemName, 1, ciri - 1);
		baseItemName = substr(baseItemName, ciri+1, strlen(baseItemName) - ciri - 1);
	else
		originalName = baseItemName;
	endif
	reqSatisfied = false;
	if (!$+inList($$listName, $$baseItemName) and $+available($$listName, $$originalName))
		checkreq $$listName, $$originalName, reqSatisfied;
	endif
	return reqSatisfied;
}


sub ItemSuppressed(suppDups, listName, name, suppDupLevel) {
	var level, n;
	if (!suppDups)
		return false;
	endif
	n = replaceString(name, "/ *[={].+$/", "");
	if (!$+InList($$listName, $$n))
		return false;
	endif
	if (suppDupLevel = 0)
		# No level -- suppress everything found.
		return true;
	endif
	return listItemInfo(listName, n, '@v@') >= suppDupLevel;
}



b_skip = 2;
b_ok = 1;
b_help = "Help";
b_cancel = 0;


# Choose items from a list. The items are separated by
# commas. If the value list starts with a number and a colon,
# you can choose that many points of items, or if it starts
# with "#number:" you can pick that many items.

sub chooseItems(listName, values) {
	array itemData[0];
	array requiredItems[0];
	array itemCostArray[0];
	array chooseArray[0];
	array itemNames[0];
	array itemsToChoose[0];
	var multiple;
	var tlend, tlrange, tlstart;
	var addIt;
	var i, j, ind;
	var mainCat, addCat, inCat;
	var dialogTitle;
	var suppDups, suppDupLevel, suppressed;
	var itemName;
	multiple = false;

	if (strindex(values, "/\?title{[^}]+\}/") > 0)
		ind = strindex(values, "?title{");
		dialogTitle = substr(values, ind + 7, strlen(values) - ind - 7);
		dialogTitle = substr(dialogTitle, 1, strindex(dialogTitle, "}") - 1);
		values = replaceString(values, "/\?title{[^}]+\},?/", "");
	else
		dialogTitle = "Choose from $$listName";
	endif

	if (strindex(values, '?multiple') > 0)
		multiple = true;
		values = replaceString(values, '/ *\?multiple *[,;]? */', '');
	endif

	ind = strindex(values, '/\?suppdups[^,]*,/');
	if (ind > 0)
		suppDups = true;
		suppDupLevel = replaceString(values, "/^.*\?suppdups=?/", "");
		suppDupLevel = integer(replaceString(suppDupLevel, "/,.+$/", ""));
		values = replaceString(values, "/\?suppdups[^,]*,/", "");
	endif

	ind = strindex(values, ":");
	num = substr(values, 1, ind - 1);
	if (strindex(num, '#') = 1)
		useCount = true;
		num = substr(num, 2, strlen(num) - 1);
	else	
		useCount = false;
	endif
	num = eval(num);
		
	values = substr(values, ind + 1, strlen(values) - ind);

	commaSplit(listName, values, itemArr);
	n = removeAllElements(itemData);
	n = removeAllElements(itemNames);
	n = removeAllElements(requiredItems);

	for (i = 1; i <= sizeof(itemArr); i = i + 1);
		name = itemArr[i];
		if (strIndex(name, "/^\*/") > 0)
			ind = strindex(itemArr[i], "/[={]/");
			if (ind > 0)
				name = substr(itemArr[i], 1, ind - 1);
				extraData = substr(itemArr[i], ind, strlen(itemArr[i]) - ind + 1);
			else
				name = itemArr[i];
				extraData = "";
			endif

			cat = substr(name, 2, strlen(name) - 1);
			n = split(cat, "/ *; */", "mainCat", "addCat");
			foreach (*$$listName : $$mainCat)
				if (addCat)
					inCat = inCategory(addCat);
				else
					inCat = true;
				endif
				if (@rand != -1 and !@see and inCat)
					addIt = false;
					if (tlhigh and optPresent('TL Range'))
						tlrange = @`TL Range`;
						if (strindex(tlrange, '/^[0-9]+\+$/') > 0)
							tlend = integer(tlrange);
							if (tllow)
								addIt = tllow <= tlend and tlhigh >= tlend;
							else
								addIt = tlend <= tlhigh;
							endif
						else
							n = split(tlrange, "-", "tlstart", "tlend");
							# Single TL Specified
							if (!tlend or tlend = '')
								tlend = tlstart;
							endif
							tlend = replaceString(tlend, "/[^0-9]/", "");
							if (tlend >= tllow and tlend <= tlhigh)
								addIt = true;
							endif
						endif
					else
						addit = true;
					endif
					nm = @name;
					if (!$+inList($$listName, $$nm) and addIt)
						n = addElement(itemNames, dispNameCost(listName, concat(@name, extraData)));
						n = addElement(itemData, concat(@name, extraData));
					endif
				endif
			endforeach

		elseif (strIndex(name, "!") = 1)
			itemName = substr(itemArr[i], 2, strlen(itemArr[i])-1);
			suppressed = ItemSuppressed(suppDups, listName, itemName, suppDupLevel);
			if (!suppressed)
				n = addElement(requiredItems, itemName);
			endif
		else
			suppressed = ItemSuppressed(suppDups, listName, name, suppDupLevel);
			if (!suppressed)
				n = addElement(itemNames, dispNameCost(listName, name));
				n = addElement(itemData, itemArr[i]);
			endif
		endif
	endfor

	added = 0;

	# Add any required items, letting the user edit them. If the user
	# cancels editing an item or cancels when requirements are shown,
	# the item is displayed again or another choice is allowed.

	for (i = 1; i <= sizeof(requiredItems); )
		doIncrement = true;
		nm = requiredItems[i];
		reselectPossible = false;

		if (strindex(nm, "/ OR /"))
			# An entry in an or that's required assumes that any of the items in the list can be required.
			# Get rid of the extra ! in the items and let the user choose from a "prettified" version of the
			# the items.
			n = split(nm, "/ +OR +/", chooseArray);
			for (j = 1; j <= sizeof(chooseArray); j = j + 1)
				n = setElement(chooseArray, j, replaceString(chooseArray[j], "/^!/", ""));
			endfor
			if (randGen)
				nm = ChooseRandomItem(listName, chooseArray);
			else
				n = removeAllElements(itemsToChoose);
				for (j = 1; j <= sizeof(chooseArray); j = j + 1)
					n = addElement(itemsToChoose, dispNameCost(listName, chooseArray[j]));
				endfor
				nm = 0;
				reselectPossible = true;
				dialog ok, "$$dialogTitle";
					keyword Templates: Choose Item;
					text Choose one item from $$listName.;
					listbox nm, $$listName:, Random, $$itemsToChoose;
					checkbox randGen, &Generate Randomly;
					end
				if (nm = 'Random' or randGen)
					nm = ChooseRandomItem(listName, chooseArray);
				else
					nm = chooseArray[index(itemsToChoose, nm)];
				endif
				if (!ok) exit; endif
				if (strindex(nm, "*") = 1)
					nm = chooseCat(listName, nm);
					if (nm = 0) exit; endif
				endif
			endif
		endif

		curCost = $@total;
		editable = addItem(listName, nm, multiple);

		if (editable)
			if (!randGen)
				item ($$addItemListName, addItemName)
					checkreq ok;
					if (!ok)
						showreq ok;
						if (!ok)
							deleteItem;
							if (!reselectPossible)
								exit;
							endif
							doIncrement = false;
						endif
					endif
					if (ok) 
						editItem ok;
						if (!ok)
							doIncrement = false;
							deleteItem;
							if (!reselectPossible)
								exit;
							endif
						endif
					endif
				enditem
			endif
		endif
		added = added + ($@total - curCost);
		if (doIncrement)
			i = i + 1;
		endif
	endfor

	# Let the user choose from the itemNames/itemData arrays.

	while (abs(added) < abs(num))
		if (sizeof(itemNames) = 0)
			#warn "No more items to select from.";
			return;
		endif

		level = 0;
		cost = 0;
		numleft = num - added;
		nm = 0;
		if (useCount)
			dispAdd = added + 1;
			if (randGen)
				nm = ChooseRandomItem(listName, itemData);
				i = index(itemData, nm);
			else
				nm = 0;
				dialog ok, "$$dialogTitle";
					keyword Templates: Choose Item;
					text Choose item $$dispAdd of $$num.;
					listbox nm, $$listName:, Random, $$itemNames;
					checkbox randGen, &Generate Randomly;
					defbutton b_ok, OK;
					button b_skip, &Skip;
					button b_cancel, Cancel;
					button b_help, &Help;
					end
				if (nm = 'Random' or randGen)
					nm = ChooseRandomItem(listName, itemData);
					i = index(itemData, nm);
				else
					i = index(itemNames, nm);
					nm = itemData[i];
				endif
			endif
		else
			if (randGen)
				nm = ChooseRandomItem(listName, itemData);
				i = index(itemData, nm);
			else
				nm = 0;
				dialog ok, "$$dialogTitle";
					keyword Templates: Choose Item;
					text Choose an item. $$added of $$num point(s) added. $$numLeft point(s) left.;
					listbox nm, $$listName:, Random, $$itemNames;
					checkbox randGen, &Generate Randomly;
					defbutton b_ok, OK;
					button b_skip, &Skip;
					button b_cancel, Cancel;
					button b_help, &Help;
					end
				if (nm = 'Random' or randGen)
					nm = ChooseRandomItem(listName, itemData);
					i = index(itemData, nm);
				else
					i = index(itemNames, nm);
					nm = itemData[i];
				endif
			endif
		endif
		if (ok = b_skip) return; endif
		if (!ok) exit; endif

		if (i < 1)
			warning "Could not find $$nm in list.";
		else
			addIt = true;
			if (strindex(nm, "/ OR /"))
				n = split(nm, "/ +OR +/", choose);
				if (randGen)
					nm = ChooseRandomItem(listName, choose);
				else
					# Add these items with their costs to another array
					# for selection.
					n = removeAllElements(itemCostArray);
					for (ci = 1; ci <= sizeof(choose); ci = ci + 1)
						n = addElement(itemCostArray, dispNameCost(listName, choose[ci]));
					endfor
					nm = 0;
					dialog ok, "$$dialogTitle";
						keyword Templates: Choose Item;
						text Choose one item from $$listName.;
						listbox nm, $$listName:, Random, $$itemCostArray;
						checkbox randGen, &Generate Randomly;
						end
					if (!ok)
						addIt = false;
					else
						if (nm = 'Random' or randGen)
							nm = ChooseRandomItem(listName, choose);
						else
							ci = index(itemCostArray, nm);
							nm = choose[ci];
						endif
					endif
				endif
				if (strindex(nm, "*") = 1)
					nm = chooseCat(listName, nm);
					if (nm = 0)
						addIt = false;
					endif
				endif
			endif

			if (addIt)
				curCost = $@total;
				editable = addItem(listName, nm, multiple);

				if (useCount)
					ok = true;
				elseif (randGen)
					ok = true;
					randSkill(listName);
				elseif (editable)
					item ($$addItemListName, addItemName)
						checkreq ok;
						if (!ok)
							showreq ok;
						endif
						if (ok)
							edititem ok;
						endif
					enditem
				else
					ok = true;
				endif

				if (ok) 
					if (!multiple)
						n = removeElement(itemData, i);
						n = removeElement(itemNames, i);
					endif
					if (useCount)
						added = added + 1;
					else
						added = added + $@total - curCost;
					endif
				elseif (addedNewItem)
					item ($$addItemListName, addItemName)
						deleteitem;
					enditem
				endif
			endif
		endif
	endwhile
}


sub getItemListMembers(listName, itemListName, itemList) {
	var n, values;
	foreach (*$$listName : Item List)
		if (itemListName = @name)
			values = @Items;
		endif
	endforeach
	if (values)
		splitArray(values, itemList);
	endif
}


sub splitArray(values, itemArr) {
	var itemList, n;
	itemList = replaceString(values, "/([^\\]),/", "\1!!!");
	itemList = replaceString(itemList, "\,", ",");
	n = split(itemList, "/ *!!! */", itemArr);
}

sub commaSplit(listName, values, itemArr) {
	var i, entry, n, j;
	array items[0];
	array itemList[0];

	splitArray(values, items);
	n = removeAllElements(itemArr);

	for (i = 1; i <= sizeof(items); i = i + 1)
		entry = items[i];
		if (strindex(entry, "@") = 1)
			getItemListMembers(listName, substr(entry 2, strlen(entry) - 1), itemList);
			for (j = 1; j <= sizeof(itemList); j = j + 1)
				n = addElement(itemArr, itemList[j]);
			endfor
		else
			n = addElement(itemArr, entry);
		endif
	endfor
}


sub addItemSplit(listName, values, itemArr) {
	var i, n, choose, done, entry, ind, num;
	array items[0];

	commaSplit(listName, values, items);
	n = removeAllElements(itemArr);

	# Convert any ?choose,#?[0-9]:item1,item2,?endchoose sequences back into proper selection
	# syntax.

	for (i = 1; i <= sizeof(items); )
		if (strindex(items[i], "?choose:") > 0)
			ind = strindex(items[i], ":");
			num = concat(substr(items[i], ind+1, strlen(items[i]) - ind), ":");
			i = i + 1;
			choose = "";
			done = false;
			while (i <= sizeof(items) and !done)
				entry = replaceString(items[i], "\\", "\\\\");
				entry = replaceString(items[i], ",", "\\,");
				entry = replaceString(items[i], '"', '\\"');
				i = i + 1;
				if (entry = '?endchoose')
					done = true;
				else
					if (num)
						entry = concat(num, entry);
						num = "";
					endif
					if (choose) choose = concat(choose, ","); endif
					choose = concat(choose, entry);
				endif
			endwhile
			if (choose)
				n = addElement(itemArr, choose);
			endif
		else
			n = addElement(itemArr, items[i]);
			i = i + 1;
		endif
	endfor
}



# Add a list of items to the specified list. If items are separated
# by OR then the user is asked to pick from the list of items.

sub addItemList(listName, values) {
	array itemArr[0];
	array chooseList[0];
	var i, j, n, nm, entry, editable;

	addItemSplit(listName, values, itemArr);

	for (i = 1; i <= sizeof(itemArr); )
		doIncrement = true;
		nm = 0;
		editIt = false;
		entry = itemArr[i];
		if (strindex(entry, "?") = 1)
			editIt = true;
			entry = substr(entry, 2, strlen(entry)-1);
		endif

		if (strindex(entry, "/^#?[-0-9¼½¾.]+:/") > 0)
			chooseItems(listName, entry);
			nm = 0;

		elseif (strindex(entry, "/ OR /"))
			n = split(entry, "/ +OR +/", choose);
			for (j = 1; j <= sizeof(choose); j = j + 1)
				if (strindex(choose[j], "/ *\?/") = 1)
					n = setElement(choose, j, replaceString(choose[j], '/^\? */', ''));
					editIt = true;
				endif
				n = addElement(chooseList, dispNameCost(listName, choose[j]));
			endfor
			if (randGen)
				nm = ChooseRandomItem(listName, choose);
			else
				nm = 0;
				dialog ok, Choose from $$listName;
					keyword Templates: Choose Item;
					text Choose one item from $$listName.;
					listbox nm, $$listName:, Random, $$chooseList;
					checkbox randGen, &Generate Randomly;
					end
				if (nm = 'Random' or randGen)
					nm = ChooseRandomItem(listName, choose);
				else
					nm = choose[index(chooseList, nm)];
				endif
				if (strindex(nm, "*") = 1)
					nm = chooseCat(listName, nm);
				endif
			endif

		elseif (strindex(entry, "*") = 1)
			nm = chooseCat(listName, entry);

		else
			nm = entry;
		endif

		if (nm)
			if (strindex(nm, "*") = 1)
				nm = chooseCat(listName, nm);
				if (nm = 0) exit; endif
			endif
			editable = addItem(listName, nm, false);
			if (editIt and editable)
				item ($$addItemListName, addItemName)
					checkreq ok;
					if (!ok)
						showreq ok;
						if (!ok)
							doIncrement = false;
						endif
					endif
					if (ok)
						editItem ok;
						if (!ok)
							doIncrement = false;
						endif
					endif
				enditem
			endif
		endif
		if (doIncrement)
			i = i + 1;
		endif
	endfor
}


# Add an entry, in the format "List Name~[##:]item1,item2,..."

sub addEntry(entry) {
	n = split(entry, "~", "lst", "values");
	if (strindex(values, "/^\#?[-0-9¼½¾.]+:/"))
		# A number of points is specified, so 
		# the user can choose from the list until that many
		# points worth have been added.
		chooseItems(lst, values);
	else
		addItemList(lst, values);
	endif
}

# If the condition on the entry is true retun the entr to add,
# otherwise return the empty string.

sub getConditional(entry) {
	if (strindex(entry, "?{") = 1)
		ind = strindex(entry, "}");
		if (ind <= 0)
			warning "Conditional missing '}': $$entry";
			exit;
		endif
		condition = substr(entry, 3, ind - 3);
		if (!eval(condition))
			return "";
		endif
		return substr(entry, ind+1, strlen(entry)-ind);
	endif

	return entry;
}


sub DoAssignments(asn) {
	array assignments[0];
	var n, i, var, value;
	n = split(asn, "/ *; */", assignments);
	for (i = 1; i <= sizeof(assignments); i = i + 1)
		n = split(assignments[i], "=", "var", "value");
		$$var = eval(value);
	endfor
}


# Go through the items required for the template and add them.
# Returns the name of the next script to execute if one was specified 
# in the template, or 0 if there is no additional script.

array select[0];
array names[0];

sub GenerateCharacter() {
	var n, ntoAdd;
	var i;
	var datasheets[0];
	var sheetName;
	var chainScript;
	var assignments;

	if (countItems("Advantages:Template", 0, 0, 'cost') > 0)
		dialog btn, "Delete Existing Templates?";
			keyword Templates: Delete Existing;
			text "This character sheet already has at least one template.";
			text "Do you wish to delete the existing templates?";
			text "Click Yes to delete the templates, or No to leave them.";
			defbutton 1, &No;
			button 2, &Yes;
			button 0, Cancel;
			button "Help", &Help;
			end

		if (btn = 0)
			exit;
		elseif (btn = 2)
			# Delete the existing template.

			foreach (Advantages)
				if (inCategory("Template"))
					name = @name;
					deleteitem;
				endif
			endforeach
		endif
	endif

	foreach(*Advantages : Template)
		n = split(@category, "/ *; */", cats);
		cat = replaceString(cats[2], "*", "");
		if (index(templates, cat) <= 0)
			n = addElement(templates, cat);
		endif
	endforeach

	n = sortElements(templates, true);

	# Choose the type of character.

	tlhigh = $@tl;

	template = 0;
	dialog ok, Select Character Type;
		keyword Templates: Select Type;
		text Select the type of character to create.;
		listbox template, &Type:, Random, $$templates;
		number tlhigh, "&TL:             ";
		number tllow, &Lowest TL:;
		checkbox randGen, &Automatic Generation;
		checkbox genApp, &Generate Random Appearance;
		end

	if (!ok) exit; endif

	TL = tlhigh;
	if ($@TL < TL)
		Configuration.TL = TL;
	endif

	if (template = "Random")
		template = templates[rand(sizeof(templates))];
	endif


	# Build a list of the available templates.

	n = removeAllElements(templates);

	if (tllow < 0)
		showProgress "Checking Template Integrity...";
	endif

	foreach(*Advantages : Template)
		if (inCategory(template))
			name = format('%s [%d]', @name, @points);
			n = addElement(templates, name);
			# Comment back in to validate presence of all items in templates in the data sheets.
			if (tllow < 0)
				n = @name;
				appendProgress "$$n...";
				checkItems(name);
				if ($+canceled)
					exit;
				endif
			endif
		endif
	endforeach

	if (tllow < 0)
		closeProgress;
		tllow = -tllow;
	endif

	if (sizeof(templates) = 0)
		warning There are no templates of type $$template.;
		exit;
	endif

	n = sortElements(templates, 1);

	# Choose the destructor...

	temp = 0;
	dialog ok, Select Template;
		keyword Templates: Select Template;
		text Select a $$template template.;
		listbox temp, Template:, Random, $$templates;
		end

	if (!ok) exit; endif

	if (temp = "Random")
		temp = templates[rand(sizeof(templates))];
	endif


	# Add the template to the Advantages. Record the auto ID
	# so that all items are marked as children of the template item,
	# which means they all get deleted when the template item is deleted.

	n = split(temp, " [", "temp", "junk");
	add Advantages.$$temp;
	parent = $@@autoID;

	# Get the data from the template item.

	item (Advantages, "$$temp")
		points = float(@Points);
		begPts = integer(@`Beginning Points`);
		maxDisadv = integer(@`Maximum Disadvantages`);
		chartype = @`Character Type`;
		iq = @IQ;
		dx = @DX;
		ht = @ht;
		st = @st;
		chainScript = @`Chain Script`;
		assign = @assignments;
		options
		Assignments ~
			assignments = @optValue;
			end
		Items ~
			itemList = @optTextValue;
			if (strindex(itemList, "&") = 1)
				# Append this to the end of the last item in the list.
				if (strindex(itemList, "?") = 2)
					ind = strindex(itemList, ":");
					if (ind <= 0)
						warning "Missing ':' in append item list";
						exit;
					endif
					exp = substr(itemList, 3, ind - 3);
					val = eval(exp);
					if (val)
						appendItems = substr(itemList, ind + 1, strlen(itemList) - ind);
					else
						appendItems = "";
					endif
				else
					append = true;
					appendItems = substr(itemList, 2, strlen(itemList)-1);
				endif
				if (appendItems <> "")
					ind = sizeof(items);
					itemList = concat(items[ind], concat(",", appendItems));
					n = removeElement(items, ind);
					n = addElement(items, itemList);
				endif
				
			else
				n = addElement(items, itemList);
			endif
			end
		Select ~
			select(@optTextValue);
			end
		Minimum TL ~
			newTL = integer(@optTextValue);
			if ($@TL < newTL)
				TL = newTL;
				dialog ok, Minimum TL Required;
					keyword Templates: Minimum TL;
					text The minimum TL for this template is $$TL.;
					text Enter desired TL.;
					number TL, TL:;
					end
				if (!ok) exit; endif
				Configuration.TL = TL;
				tlhigh = TL;
			endif
			end
		Data Sheets ~
			n = split(@optvalue, "/ *, */", datasheets);
			end
		endoptions
		parent = @autoID;
	enditem

	for (i = 1; i <= sizeof(datasheets); i = i + 1)
		sheetName = datasheets[i];
		datasheet $$sheetName;
	endfor

	# Set the stat values.

	if (chartype)
		Configuration.charType = chartype;
	endif

	if (begPts and begPts != $@beginningPoints)
		Configuration.beginningPoints = begPts;
	endif

	if ($@beginningPoints < points)
		Configuration.beginningPoints = points;
	endif

	if (maxDisadv and maxDisadv != $@disadvlim)
		Configuration.disadvlim = maxDisadv;
	endif

	if (assignments)
		DoAssignments(assignments);
	endif

	# Set any attributes to the specified value if they aren't already
	# at least that high.

	if ($*iq < iq)
		Main.iq = iq;
	endif

	if ($*dx < dx)
		Main.dx = dx;
	endif

	if ($*ht < ht)
		Main.ht = ht;
	endif

	if ($*st < st)
		Main.st = st;
	endif

	if (genApp)
		GenerateAppearance();
	endif

	openwindow Advantages;

	for (it = 1; it <= sizeof(items); it = it + 1)
		entry = getConditional(items[it]);
		if (entry != "")
			if (strindex(entry, "|"))
				# Found a specification for a specialization.
				n = removeAllElements(select);
				n = removeAllElements(names);

				n = split(entry, "|", "spec", "values");
				n = split(spec, ":", "indic", "name");
				n = addElement(names, name);
				n = addElement(select, values);

				i = it + 1;
				found = true;
				ntoAdd = 1;

				while (i <= sizeof(items) and found)
					entry = getConditional(items[i]);
					if (strindex(entry, "/^$$indic:/"))
						n = split(entry, "|", "spec", "values");
						n = split(spec, ":", "junk", "name");

						if (strIndex(values, "/^\#?[0-9*]+:/") > 0)
							ind = strIndex(values, ":");
							ntoAdd = substr(values, 1, ind - 1);
							ntoAdd = replaceString(ntoAdd, '#', '');
							values = substr(values, ind+1, strlen(values) - ind);
						endif
						n = addElement(names, name);
						n = addElement(select, values);
						it = i;
					else
						found = strindex(items[i], "/$$indic:/");
					endif
					i = i + 1;
				endwhile

				if (ntoAdd = "*")
					chooseOptionalSpecialties(select, names, indic);
				else
					chooseSpecialties(select, names, ntoAdd, indic);
				endif

			else
				addEntry(entry);

			endif
		endif
	endfor

	return chainScript;
}


# Split the list up and format it so that it looks okay when presented
# to the user in a listbox.

sub prettyListSplit(listName, values, itemArr) {
	var i, entry, n, ind, num;
	addItemSplit(listName, values, itemArr);
	for (i = 1; i <= sizeof(itemArr); i = i + 1)
		entry = replaceString(itemArr[i], "/{[^}]+}/", "");
		if (strindex(entry, "?") = 1)
			entry = substr(entry, 2, strlen(entry) - 1);
		endif
		if (strindex(entry, "/^\#?[0-9¼½¾.]+:/") > 0)
			if (strlen(entry) > 50)
				entry = replaceString(substr(entry, 1, 50), "/,[^,]+$/", "");
				entry = concat(entry, " . . .");
			endif
			entry = replaceString(entry, ",", ", ");
			ind = strindex(entry, ":");
			num = substr(entry, 1, ind - 1);
			entry = substr(entry, ind+1, strlen(entry) - ind);
			if (strindex(num, "#") <= 0)
				entry = format('Choose %d Points: %s', num, entry);
			else
				num = replaceString(num, "#", "");
				entry = format('Choose %d: %s', num, entry);
			endif
		elseif (strlen(entry) > 50)
			entry = replaceString(substr(entry, 1, 50), "/ [^ ]+$/", "");
			entry = concat(entry, " . . .");
		endif
		n = setElement(itemArr, i, entry);
	endfor
}


sub chooseSpecialties(select, names, ntoAdd, indic) {
	var nadded, choice, ok, entry, lst, values, ind;
	var addIt, nm, selItem;
	array itemArr[0];

	nadded = 1;
	while (nadded <= ntoAdd) 
		choice = 0;
		if (randGen)
			choice = names[rand(sizeof(names))];
		else
			choice = 0;
			if (ntoAdd > 1)
				dialog ok, Choose $$indic;
					keyword Templates: Choose Group;
					text Choose group $$nadded of $$ntoAdd for $$indic.;
					listbox choice, "$$indic:", Random, $$names;
					checkbox randGen, &Generate Randomly;
					end
			else
				dialog ok, Choose $$indic;
					keyword Templates: Choose Group;
					text Choose a group of items for $$indic.;
					listbox choice, "$$indic:", Random, $$names;
					checkbox randGen, &Generate Randomly;
					end
			endif
			if (!ok) exit; endif
			if (choice = 'Random' or randGen)
				choice = names[rand(sizeof(names))];
			endif
		endif
		if (choice)
			selItem = index(names, choice);

			entry = select[selItem];

			n = split(entry, "~", "lst", "values");
			lst = replaceString(lst, '/^\#[0-9]+:/', '');
			if (strindex(values, "/^\#?[0-9¼½¾.]+:/") > 0)
				ind = strindex(values, ":");
				values = substr(values, ind+1, strlen(values)-ind);
			endif

			prettyListSplit(lst, values, itemArr);

			if (randGen)
				addIt = true;
			else
				nm = 0;
				dialog addIt, Items to Add to $$lst;
					keyword Templates: Items to Add;
					text "The following items will be selected for $$choice.";
					listbox nm, Items:, $$itemArr;
					text "Click OK to add these items, or Cancel to make a different selection.";
					end
			endif
			if (addIt)
				entry = replaceString(select[selItem], '/^\#?[0-9]+:/', '');
				addEntry(entry);
				n = removeElement(select, selItem);
				n = removeElement(names, selItem);
				nadded = nadded + 1;
			endif
		else
			warning A choice must be made.;
		endif
	endwhile
}


sub chooseOptionalSpecialties(select, names, indic) {
	var ok, lst, values, ind;
	var addIt, nm, i, j, n;
	array choices[0];
	array addValues[0];
	var nchoices;
	array itemArr[0];
	array items[0];
	var done;

	if (randGen)
		return;
	endif
	while (!done)
		n = removeAllElements(choices);
		n = removeAllElements(items);
		dialog ok, Choose $$indic;
			keyword Templates: Choose Group;
			text "Choose one or more groups of items for $$indic.\nClick an entry to select, and click it again to deselect it.";
			text "To choose none of these, just click OK.";
			listbox choices, " ", $$names;
			end

		if (!ok) exit; endif
		for (i = 1; i <= sizeof(choices); i = i + 1)
			selItem = index(names, choices[i]);

			entry = select[selItem];
			n = addElement(addValues, entry);

			n = split(entry, "~", "lst", "values");
			lst = replaceString(lst, '/^\#[0-9]+:/', '');
			if (strindex(values, "/^\#?[0-9¼½¾.]+:/") > 0)
				ind = strindex(values, ":");
				values = substr(values, ind+1, strlen(values)-ind);
			endif

			prettyListSplit(lst, values, itemArr);
			for (j = 1; j <= sizeof(itemArr); j = j + 1)
				n = addElement(items, itemArr[j]);
			endfor
		endfor

		if (sizeof(items) > 0)
			nm = 0;
			dialog addIt, Items to Add;
				keyword Templates: Items to Add;
				text "The following items will be selected.";
				listbox nm, Items:, $$items;
				text "Click OK to add these items, or Cancel to make a different selection.";
				end
			if (addIt)
				for (i = 1; i <= sizeof(addValues); i = i + 1)
					addEntry(replaceString(addValues[i], '/^\#?[0-9*]+:/', ''));
				endfor
				done = true;
			endif
		else
			done = true;
		endif
	endwhile
}
