<file_sep>//////////////////////
//rfw menu specifics//
//////////////////////
$BCM::DefaultMenu = "RFW";

//initial menu
$BCM::Menu["RFW", 1] = "static" SPC "Inventory\nEquipment\nCrafting\nOptions\nHelp";
$BCM::Transitions["RFW", 1] = "2 3 4 5 6";
$BCM::PrevState["RFW", 1] = "0";

//inventory
$BCM::Menu["RFW", 2] = "function" SPC "%client.account.getItemList();";
$BCM::EnterCallback["RFW", 2] = "serverCmdEquip(%client, %selection); %client.BCM_MenuChangeState(%state);";
$BCM::ScrollCallback["RFW", 2] = "Menu_SetQuantity(%client, %selection);";
$BCM::EnableQuantity["RFW", 2] = true;
$BCM::QuantityCallback["RFW", 2] = "Menu_VerifyQuantity(%client, %quantity);";
$BCM::PrevState["RFW", 2] = "1";

//equipment
$BCM::Menu["RFW", 3] = "function" SPC "%client.account.getEquipmentList();";
$BCM::EnterCallback["RFW", 3] = "serverCmdUnequip(%client, %selection); %client.BCM_MenuChangeState(%state);";
$BCM::PrevState["RFW", 3] = "1";

//crafting
$BCM::Menu["RFW", 4] = "static" SPC "Materials\nWeapons and tools\nArmor\nClothes\nMedicines and potions";
$BCM::Transitions["RFW", 4] = "7 8 18 17 19";
$BCM::PrevState["RFW", 4] = "1";

//crafting > materials
$BCM::Menu["RFW", 7] = "static" SPC "Cloth\nPaper\nLeather\nGlass\nStone\nCalcite\nSanguite";
$BCM::DisplayFunction["RFW", 7] = "Menu_DisplayCrafting(%client, %menu, %position);";
$BCM::EnableQuantity["RFW", 7] = true;
$BCM::DefaultQuantity["RFW", 7] = 1;
$BCM::EnterCallback["RFW", 7] = "serverCmdCraft(%client, %quantity, %selection); %client.BCM_MenuDisplay();";
$BCM::PrevState["RFW", 7] = "4";

//crafting > weapons and tools
$BCM::Menu["RFW", 8] = "static" SPC "Pickaxes\nCrafting tools\nSwords\nDaggers\nBows\nQuivers\nMagic tools\nOther";
$BCM::Transitions["RFW", 8] = "9 10 11 12 13 14 15 16";
$BCM::PrevState["RFW", 8] = "4";

//crafting > weapons and tools > pickaxes
$BCM::Menu["RFW", 9] = "static" SPC "Stone pickaxe\nSurtra pickaxe\nCalcite pickaxe\nNithlite pickaxe\nSanguite pickaxe\nSinnite pickaxe\nCrystal pickaxe";
$BCM::DisplayFunction["RFW", 9] = "Menu_DisplayCrafting(%client, %menu, %position);";
$BCM::EnableQuantity["RFW", 9] = true;
$BCM::DefaultQuantity["RFW", 9] = 1;
$BCM::EnterCallback["RFW", 9] = "serverCmdCraft(%client, %quantity, %selection); %client.BCM_MenuDisplay();";
$BCM::PrevState["RFW", 9] = "8";

//crafting > weapons and tools > crafting tools
$BCM::Menu["RFW", 10] = "static" SPC "Wooden crafting tools\nStone crafting tools\nObsidian crafting tools\nSurtra crafting tools\nCalcite crafting tools\nNithlite crafting tools\nSanguite crafting tools\nSinnite crafting tools";
$BCM::DisplayFunction["RFW", 10] = "Menu_DisplayCrafting(%client, %menu, %position);";
$BCM::EnableQuantity["RFW", 10] = true;
$BCM::DefaultQuantity["RFW", 10] = 1;
$BCM::EnterCallback["RFW", 10] = "serverCmdCraft(%client, %quantity, %selection); %client.BCM_MenuDisplay();";
$BCM::PrevState["RFW", 10] = "8";

//crafting > weapons and tools > swords
$BCM::Menu["RFW", 11] = "static" SPC "Wooden sword\nStone sword\nObsidian sword\nSurtra sword\nCalcite sword\nNithlite sword\nSanguite sword\nSinnite sword\nCrystal sword";
$BCM::DisplayFunction["RFW", 11] = "Menu_DisplayCrafting(%client, %menu, %position);";
$BCM::EnableQuantity["RFW", 11] = true;
$BCM::DefaultQuantity["RFW", 11] = 1;
$BCM::EnterCallback["RFW", 11] = "serverCmdCraft(%client, %quantity, %selection); %client.BCM_MenuDisplay();";
$BCM::PrevState["RFW", 11] = "8";

//crafting > weapons and tools > daggers
$BCM::Menu["RFW", 12] = "static" SPC "Obsidian dagger\nSurtra dagger\nCalcite dagger\nNithlite dagger\nSanguite dagger\nSinnite dagger\nCrystal dagger";
$BCM::DisplayFunction["RFW", 12] = "Menu_DisplayCrafting(%client, %menu, %position);";
$BCM::EnableQuantity["RFW", 12] = true;
$BCM::DefaultQuantity["RFW", 12] = 1;
$BCM::EnterCallback["RFW", 12] = "serverCmdCraft(%client, %quantity, %selection); %client.BCM_MenuDisplay();";
$BCM::PrevState["RFW", 12] = "8";

//crafting > weapons and tools > bows
$BCM::Menu["RFW", 13] = "static" SPC "Wooden bow\nBone bow\nFaerie bow\nCrystal bow";
$BCM::DisplayFunction["RFW", 13] = "Menu_DisplayCrafting(%client, %menu, %position);";
$BCM::EnableQuantity["RFW", 13] = true;
$BCM::DefaultQuantity["RFW", 13] = 1;
$BCM::EnterCallback["RFW", 13] = "serverCmdCraft(%client, %quantity, %selection); %client.BCM_MenuDisplay();";
$BCM::PrevState["RFW", 13] = "8";

//crafting > weapons and tools > quivers
$BCM::Menu["RFW", 14] = "static" SPC "Wooden quiver\nBone quiver\nStone quiver\nObsidian quiver\nSurtra quiver\nCalcite quiver\nNithlite quiver\nSanguite quiver\nSinnite quiver\nCrystal quiver";
$BCM::DisplayFunction["RFW", 14] = "Menu_DisplayCrafting(%client, %menu, %position);";
$BCM::EnableQuantity["RFW", 14] = true;
$BCM::DefaultQuantity["RFW", 14] = 1;
$BCM::EnterCallback["RFW", 14] = "serverCmdCraft(%client, %quantity, %selection); %client.BCM_MenuDisplay();";
$BCM::PrevState["RFW", 14] = "8";

//crafting > weapons and tools > magic tools
$BCM::Menu["RFW", 15] = "static" SPC "Faerie crafting tools\nFaerie wand\nPyromancy wand\nAlchemy vials\nPhotometallisynthesis lens\nOssimetallisynthesis staff\nSanguimetallisynthesis wand";
$BCM::DisplayFunction["RFW", 15] = "Menu_DisplayCrafting(%client, %menu, %position);";
$BCM::EnableQuantity["RFW", 15] = true;
$BCM::DefaultQuantity["RFW", 15] = 1;
$BCM::EnterCallback["RFW", 15] = "serverCmdCraft(%client, %quantity, %selection); %client.BCM_MenuDisplay();";
$BCM::PrevState["RFW", 15] = "8";

//crafting > weapons and tools > other
$BCM::Menu["RFW", 16] = "static" SPC "Snowball";
$BCM::DisplayFunction["RFW", 16] = "Menu_DisplayCrafting(%client, %menu, %position);";
$BCM::EnableQuantity["RFW", 16] = true;
$BCM::DefaultQuantity["RFW", 16] = 1;
$BCM::EnterCallback["RFW", 16] = "serverCmdCraft(%client, %quantity, %selection); %client.BCM_MenuDisplay();";
$BCM::PrevState["RFW", 16] = "8";

//crafting > clothes
$BCM::Menu["RFW", 17] = "static" SPC "Cloth hat\nCloth shirt\nCloth pants\nCloth cape\nCloth robe\nLeather boots\nLeather gloves";
$BCM::DisplayFunction["RFW", 17] = "Menu_DisplayCrafting(%client, %menu, %position);";
$BCM::EnableQuantity["RFW", 17] = true;
$BCM::DefaultQuantity["RFW", 17] = 1;
$BCM::EnterCallback["RFW", 17] = "serverCmdCraft(%client, %quantity, %selection); %client.BCM_MenuDisplay();";
$BCM::PrevState["RFW", 17] = "4";

//crafting > armor
$BCM::Menu["RFW", 18] = "static" SPC "Wooden armor\nFaerie armor\nBone armor\nStone armor\nSurtra armor\nCalcite armor\nNithlite armor\nSanguite armor\nSinnite armor\nCrystal armor";
$BCM::DisplayFunction["RFW", 18] = "Menu_DisplayCrafting(%client, %menu, %position);";
$BCM::EnableQuantity["RFW", 18] = true;
$BCM::DefaultQuantity["RFW", 18] = 1;
$BCM::EnterCallback["RFW", 18] = "serverCmdCraft(%client, %quantity, %selection); %client.BCM_MenuDisplay();";
$BCM::PrevState["RFW", 18] = "4";

//crafting > medicines and potions
$BCM::Menu["RFW", 19] = "static" SPC "Cloth bandage\nPishelva\nHoborchakin\nFleshweaver's potion\nVorpal potion\nCrimson potion";
$BCM::DisplayFunction["RFW", 19] = "Menu_DisplayCrafting(%client, %menu, %position);";
$BCM::EnableQuantity["RFW", 19] = true;
$BCM::DefaultQuantity["RFW", 19] = 1;
$BCM::EnterCallback["RFW", 19] = "serverCmdCraft(%client, %quantity, %selection); %client.BCM_MenuDisplay();";
$BCM::PrevState["RFW", 19] = "4";

//options
$BCM::Menu["RFW", 5] = "function" SPC "%client.account.getOptions();";
//$BCM::EnterCallback["RFW", 5] = "";
//$BCM::QuantityCallback["RFW", 5] = "";
//$BCM::EnableQuantity["RFW", 5] = true;
$BCM::PrevState["RFW", 5] = "1";

//help
$BCM::Menu["RFW", 6] = "static" SPC "Getting started\nItems\nCrafting\nResources\nBiomes\nBuilding\nTotems\nTribes\nForum thread"; //help
$BCM::EnterCallback["RFW", 6] = "serverCmdHelp(%client, %selection);";
$BCM::PrevState["RFW", 6] = "1";

//helper functions
function Menu_SetQuantity(%client, %selection)
{
	%client.BCM_MenuQuantity = %client.account.getItemQuantity(%selection);
}

function Menu_VerifyQuantity(%client, %quantity)
{
	%acc = %client.account;
	%selection = getRecord(%client.BCM_MenuCache, %client.BCM_MenuPosition);
	%amt = %acc.getItemQuantity(%selection);
	if(%amt < %quantity)
	{
		messageClient(%client, 'MsgError');
		%client.BCM_MenuQuantity = %amt;
	}
}

function Menu_DisplayCrafting(%client, %menu, %position)
{
	%acc = %client.account;
	%quantity = %client.BCM_MenuQuantity;
	%init = mClamp(%position - 2, 0, getRecordCount(%menu) - 5);
	%position-= %init;
	%rec = getRecord(%menu, %init);
	%color = %acc.hasRequiredItems($RFW::RecipeMats[%rec], $RFW::RecipeTools[%rec], %quantity) ? "\c2" : "\c0";
	%print = %color @ %rec;
	for(%i = %init + 1; %i < %init + 5; %i++)
	{
		%rec = getRecord(%menu, %i);
		%color = %acc.hasRequiredItems($RFW::RecipeMats[%rec], $RFW::RecipeTools[%rec], %quantity) ? "\c2" : "\c0";
		if(%client.canCraftAnything)
			%color = "\c2";
		
		%print = %print NL %color @ %rec;
	}
	%print = setRecord(%print, %position, "\c3>>" @ getRecord(%print, %position) @ "\c3x" @ %quantity);
	return %print;
}

////////////////////////
//BrickControlsMenu.cs//
////////////////////////
//by Amadé (ID 10716)

$BCM::Version = 1;

$BCM::Up1 = "0 0 1"; //shift up 1 plate
$BCM::Up3 = "0 0 3"; //shift up 1 block
$BCM::Down1 = "0 0 -1"; //shift down 1 plate
$BCM::Down3 = "0 0 -3"; //shift down 1 block
$BCM::Right = "1 0 0";
$BCM::Left = "-1 0 0";
$BCM::Forward = "0 1 0";
$BCM::Back = "0 -1 0";

//helper functions
function hasColor(%str)
{
	if(%str $= "")
	{
		return false;
	}
	%firstChar = getSubStr(%str, 0, 1);
	if(%firstChar $= "<")
	{
		return true;
	}
	return %firstChar $= "\c0" || %firstChar $= "\c1" || %firstChar $= "\c2" || %firstChar $= "\c3";
}

function BCM_GetMenuText(%str, %client)
{
	switch$(firstWord(%str))
	{
		case "static":
			return restWords(%str);
		//case "variable":
			//eval("%str = " @ restWords(%str));
			//return %str;
		case "function":
			eval("%str = " @ restWords(%str));
			return %str;
		default:
			return %str;
	}
}

//the main attraction
package BrickControlsMenu
{
	function serverCmdPlantBrick(%client)
	{
		if(!isObject(%client.player.tempBrick))
		{
			%client.BCM_MenuEnter();
		}
		else return parent::serverCmdPlantBrick(%client);
	}

	function serverCmdCancelBrick(%client)
	{
		if(!isObject(%client.player.tempBrick))
		{
			%client.BCM_MenuCancel();
		}
		else return parent::serverCmdCancelBrick(%client);
	}

	function serverCmdShiftBrick(%client, %y, %x, %z)
	{
		//y+: forward
		//x+: left
		//z+: up
		if(!isObject(%client.player.tempBrick))
		{
			%client.BCM_MenuShift(-1 * %x, %y, %z);
		}
		else return parent::serverCmdShiftBrick(%client, %y, %x, %z);
	}

	function serverCmdSuperShiftBrick(%client, %y, %x, %z)
	{
		if(!isObject(%client.player.tempBrick))
		{
			%client.BCM_MenuShift(-1 * %x, %y, %z);
		}
		else return parent::serverCmdSuperShiftBrick(%client, %y, %x, %z);
	}
};
activatePackage(BrickControlsMenu);

function GameConnection::BCM_MenuEnter(%client)
{
	if(!strLen(%profile = %client.BCM_MenuProfile))
	{
		%client.BCM_MenuProfile = %profile = $BCM::DefaultMenu;
	}
	%state = %client.BCM_MenuState;
	if(%state == 0)
	{
		return %client.BCM_MenuChangeState(1);
	}
	%position = %client.BCM_MenuPosition;
	if((%fn = $BCM::EnterCallback[%profile, %state]) !$= "")
	{
		if($BCM::EnableQuantity[%profile, %state])
		{
			%quantity = %client.BCM_MenuQuantity;
		}
		%menu = %client.BCM_MenuCache;
		%selection = stripMLControlChars(getRecord(%menu, %position));
		eval(%fn);
	}
	if((%transitions = $BCM::Transitions[%profile, %state]) !$= "")
	{
		%client.BCM_MenuChangeState(getWord(%transitions, %position));
	}
}

function GameConnection::BCM_MenuCancel(%client)
{
	if(isEventPending(%event = %client.BCM_MenuCancelSched))
	{
		cancel(%event);
		%client.centerPrint();
	}
	%client.BCM_MenuState = 0;
}

function GameConnection::BCM_MenuShift(%client, %x, %y, %z)
{
	if(!strLen(%profile = %client.BCM_MenuProfile))
	{
		%client.BCM_MenuProfile = %profile = $BCM::DefaultMenu;
	}
	%state = %client.BCM_MenuState;
	if(!%state)
	{
		return %client.BCM_MenuEnter;
	}
	switch$(%x SPC %y SPC %z)
	{
		case $BCM::Forward:
			return %client.BCM_MenuScroll(-1);
		case $BCM::Back:
			return %client.BCM_MenuScroll(1);
		case $BCM::Right:
			return %client.BCM_MenuEnter();
		case $BCM::Left:
			return %client.BCM_MenuBack();
		case $BCM::Up1:
			if($BCM::EnableQuantity[%profile, %state])
			{
				return %client.BCM_MenuAddQuantity(1);
			}
			else
			{
				return %client.BCM_MenuScroll(-1);
			}
		case $BCM::Up3:
			if($BCM::EnableQuantity[%profile, %state])
			{
				return %client.BCM_MenuAddQuantity(10);
			}
			else
			{
				return %client.BCM_MenuScroll(-1);
			}
		case $BCM::Down1:
			if($BCM::EnableQuantity[%profile, %state])
			{
				return %client.BCM_MenuAddQuantity(-1);
			}
			else
			{
				return %client.BCM_MenuScroll(1);
			}
		case $BCM::Down3:
			if($BCM::EnableQuantity[%profile, %state])
			{
				return %client.BCM_MenuAddQuantity(-10);
			}
			else
			{
				return %client.BCM_MenuScroll(1);
			}
	}
}

function GameConnection::BCM_MenuBack(%client)
{
	%profile = %client.BCM_MenuProfile;
	%state = %client.BCM_MenuState;
	%client.BCM_MenuChangeState($BCM::PrevState[%profile, %state]);
}

function GameConnection::BCM_MenuScroll(%client, %dir)
{
	%profile = %client.BCM_MenuProfile;
	%state = %client.BCM_MenuState;
	%menu = %client.BCM_MenuCache;
	%client.BCM_MenuPosition+= %dir;
	%position = %client.BCM_MenuPosition;
	%recordCount = getRecordCount(%menu);
	if(%position > %recordCount - 1 || %recordCount == 0)
	{
		%client.BCM_MenuPosition = %position = 0;
	}
	else if(%position < 0)
	{
		%client.BCM_MenuPosition = %position = %recordCount - 1;
	}
	if((%fn = $BCM::ScrollCallback[%profile, %state]) !$= "")
	{
		%selection = getRecord(%menu, %position);
		eval(%fn);
	}
	%client.BCM_MenuDisplay();
}

function GameConnection::BCM_MenuAddQuantity(%client, %amt)
{
	%profile = %client.BCM_MenuProfile;
	%state = %client.BCM_MenuState;
	%quantity = %client.BCM_MenuQuantity + %amt;
	if(%quantity < 0 && !$BCM::NegativeQuantity[%profile, %state])
	{
		%quantity = 0;
		messageClient(%client, 'MsgError');
	}
	%client.BCM_MenuQuantity = %quantity;
	if((%fn = $BCM::QuantityCallback[%profile, %state]) !$= "")
	{
		eval(%fn);
	}
	%client.BCM_MenuDisplay();
}

function GameConnection::BCM_MenuChangeState(%client, %newstate)
{
	%profile = %client.BCM_MenuProfile;
	%state = %client.BCM_MenuState;
	if(%state != %newstate)
	{
		%client.BCM_MenuPosition = 0;
	}
	%position = %client.BCM_MenuPosition;
	%client.BCM_MenuState = %state = %newstate;
	if((%quantity = $BCM::DefaultQuantity[%profile, %state]) !$= "")
	{
		%client.BCM_MenuQuantity = %quantity;
	}
	else
	{
		%client.BCM_MenuQuantity = 0;
	}
	%client.BCM_MenuCache = %menu = BCM_GetMenuText($BCM::Menu[%profile, %state], %client);
	if((%fn = $BCM::ScrollCallback[%profile, %state]) !$= "")
	{
		%selection = getRecord(%menu, %position);
		eval(%fn);
	}
	%client.BCM_MenuDisplay();
}

function GameConnection::BCM_MenuDisplay(%client)
{
	if(%client.BCM_MenuState == 0)
	{
		return %client.centerPrint();
	}
	%profile = %client.BCM_MenuProfile;
	%state = %client.BCM_MenuState;
	%menu = %client.BCM_MenuCache;
	%position = %client.BCM_MenuPosition;
	%recordCount = getRecordCount(%menu);
	%selection = getRecord(%menu, %position);
	if((%fn = $BCM::MenuFunction[%profile, %state]) !$= "")
	{
		return eval(%fn);
	}
	else if((%fn = $BCM::DisplayFunction[%profile, %state]) !$= "")
	{
		%print = eval(%fn);
	}
	else
	{
		%selection = hasColor(%selection) ? %selection : "\c2" @ %selection;
		if($BCM::EnableQuantity[%profile, %state])
		{
			%menu = setRecord(%menu, %position, "<div:1>\c3>>" @ %selection @ "\c3x" @ %client.BCM_MenuQuantity);
		}
		else
		{
			%menu = setRecord(%menu, %position, "<color:FFFF00><div:1>>>" @ %selection @ "<<");
		}
		%init = mClamp(%position - 2, 0, %recordCount - 5);
		%print = "\c2" @ getRecord(%menu, %init);
		for(%i = %init + 1; %i < %init + 5; %i++)
		{
			%rec = getRecord(%menu, %i);
			%rec = hasColor(%rec) ? %rec : "\c2" @ %rec;
			%print = %print NL %rec;
		}
	}
	%client.centerPrint(%print, 5);
	%client.scheduleBCM_MenuCancel();
}

function GameConnection::scheduleBCM_MenuCancel(%client)
{
	cancel(%client.BCM_MenuCancelSched);
	%client.BCM_MenuCancelSched = %client.schedule(5000, BCM_MenuCancel);
}<file_sep>//Support_UpdaterMigration
//By Greek2me.

//The purpose of this module is to migrate users to the Support_Updater system.
//This includes downloading and installing Support_Updater.zip.

if(isFile("Add-Ons/Support_Updater.zip") || $supportUpdaterMigration)
	return;
$supportUpdaterMigration = true;

function doSupportUpdaterInstallNotify()
{
	%message = "<linkcolor:ff0000><sPush><font:arial bold:20>Additional Download<sPop>\n\nAn add-on that you recently installed requires you to download <a:mods.greek2me.us/storage/Support_Updater/Support_Updater.zip>Support_Updater</a>."
		SPC "Support_Updater is designed to give you the latest features and fixes from your favorite add-ons."
		NL "\nClick YES to automatically download the new add-on now. If you would like to be prompted later, click NO."
		NL "\n<sPush><font:arial bold:14>Downloading this file gives you continued access to important updates, <sPush><color:ff0000>including patches and security updates<sPop>."
		NL "\nClicking YES is HIGHLY recommended!\n\nDON'T KNOW WHAT TO DO? CLICK YES.<sPop>";
	messageBoxYesNo("", %message, "doSupportUpdaterInstallDownload();");
}

function doSupportUpdaterInstallDownload()
{
	%server = "mods.greek2me.us:80";
	%directory = "/storage/Support_Updater/Support_Updater.zip";
	%downloadPath = "Add-Ons/Support_Updater.zip";
	%className = "supportUpdaterInstallTCP";

	TCPClient(%server, %directory, %downloadPath, %className);

	messageBoxOK("Downloading...", "Downloading Support_Updater.");
}

function supportUpdaterInstallTCP::onDone(%this, %error)
{
	if(%error)
	{
		messageBoxOK("", "Error occurred:" SPC %error
			NL "Support_Updater installation will be attempted again at a later time."
			SPC "If the problem persists, contact <EMAIL>me.us for assistance.");
	}
	else
	{
		messageBoxOK("", "Installation Success\n\nThank you for your patience.\n\n   ~Greek2me");
	}
}

schedule(1000, 0, "doSupportUpdaterInstallNotify");



/////////////////////////////////////////////////////////////////////////////////////////
// THE BELOW IS A COPY OF SUPPORT_TCPCLIENT, BY GREEK2ME.
// DO NOT REUSE THIS VERSION. USE A STAND-ALONE COPY.
/////////////////////////////////////////////////////////////////////////////////////////


if($TCPClient::version > 2.1)
	return;
$TCPClient::version = 2.1;

$TCPClient::redirectWait = 5000;

$TCPClient::Error::invalidDownloadLocation = 4;
$TCPClient::Error::invalidRedirect = 3;
$TCPClient::Error::invalidResponse = 2;
$TCPClient::Error::connectionFailed = 1;
$TCPClient::Error::none = 0;

//Creates a TCP connection to the specified server.
//@param	string server	The URL and port of the server to connect to.
//@param	string directory	The location of the file on the server.
//@param	string downloadLocation	The local filepath to save binary (non-text) files to.
//@param	string class	The name of a class which can be used to change/extend functionality.
//@return	TCPObject	The TCP object that is performing the connection.
function TCPClient(%server, %directory, %downloadLocation, %class)
{
	if(!strLen(%class))
		%class = TCPClient;
	if(!strLen(%directory))
		%directory = "/";

	%tcp = new TCPObject(TCPClient)
	{
		className = %class;

		server = %server;
		directory = %directory;
		downloadLocation = %downloadLocation;
	};

	if(strLen(%downloadLocation) && !isWriteableFileName(%downloadLocation))
	{
		%tcp.onDone($TCPClient::Error::invalidDownloadLocation);
		return 0;
	}

	%tcp.schedule(0, "connect", %server);

	return %tcp;
}

//Called when the connection has been established.
function TCPClient::onConnected(%this)
{
	%this.isConnected = true;

	%this.httpStatus = "";
	%this.receiveData = false;
	%this.redirect = false;
	cancel(%this.retrySchedule);

	//Damn inheritance is broken in the engine.
	if(isFunction(%this.className, "onConnected"))
		%customReq = eval(%this.className @ "::onConnected(" @ %this @ ");");

	if(strLen(%customReq))
		%request = %customReq;
	else
	{
		%request = "GET" SPC %this.directory SPC "HTTP/1.0\r\n" @
			"Host:" SPC %this.server @ "\r\n" @
			"User-Agent: Torque/1.3\r\n" @
			"\r\n";
	}
	%this.send(%request);
}

//Called when the connection has failed.
function TCPClient::onConnectFailed(%this)
{
	%this.onDone($TCPClient::Error::connectionFailed);
}

//Called when the connection is closed.
function TCPClient::onDisconnect(%this)
{
	%this.isConnected = false;
	if(!%this.redirect)
		%this.onDone($TCPClient::Error::none);
}

//Called when the connection has completed.
//@param	int error The error message. 0 if no error.
//@return	int	The error message.
function TCPClient::onDone(%this, %error)
{
	if(%error)
	{
		error("ERROR (TCPClient): error" SPC %error);
	}

	//Damn inheritance is broken in the engine.
	if(isFunction(%this.className, "onDone"))
		eval(%this.className @ "::onDone(" @ %this @ ",\"" @ %error @ "\");");

	if(%this.isConnected)
	{
		%this.disconnect();
		%this.isConnected = false;
	}

	%this.delete();

	return %error;
}

//Called when a line is received from the server.
//@param	string line	The line received.
//@see	TCPClient::handleText
//@see	TCPClient::onBinChunk
function TCPClient::onLine(%this, %line)
{
	if(%this.receiveData)
	{
		if(%this.contentIsText || strPos(%this.headerField["Content-Type"], "text/") == 0)
		{
			if(!%this.contentIsText)
				%this.contentIsText = true;
			%this.handleText(%line);
		}
		else
		{
			%this.setBinarySize(%this.headerField["Content-Length"]);
		}
	}
	else
	{
		if(strLen(%line))
		{
			if(!%this.httpStatus && strPos(%line, "HTTP/") == 0)
			{
				%this.httpStatus = getWord(%line, 1);
				%this.httpStatusDesc = getWord(%line, 2);
				if(%this.httpStatus >= 400)
				{
					%this.onDone($TCPClient::Error::invalidResponse);
					return;
				}
				else if(%this.httpStatus >= 300)
				{
					if(%this.redirected)
					{
						%this.onDone($TCPClient::Error::invalidRedirect);
						return;
					}
					else
					{
						%this.redirect = true;
					}
				}
			}
			else
			{
				%field = getWord(%line, 0);
				%fieldLength = strLen(%field);
				if(%fieldLength > 0)
				{
					%field = getSubStr(%field, 0, %fieldLength - 1);
					%value = getWords(%line, 1);
					%this.headerField[%field] = %value;
				}

				switch$(%field)
				{
					case "Location":
						if(%this.redirect)
						{
							%this.disconnect();

							if(strPos(%value, "/") == 0)
							{
								%this.directory = %value;
							}
							else
							{
								%pos = strPos(%value, "://");
								%url = getSubStr(%value, %pos + 3, strLen(%value));
								%pos = strPos(%url, "/");
								%this.server = getSubStr(%url, 0, %pos) @ ":80";
								%this.directory = getSubStr(%url, %pos, strLen(%url));
							}

							%this.redirected = true;
							%this.retrySchedule = %this.scheduleNoQuota($TCPClient::redirectWait, "connect", %this.server);

							return;
						}
				}
			}
		}
		else
		{
			%this.receiveData = true;

			%type = %this.headerField["Content-Type"];
			if(strLen(%type) && strPos(%type, "text/") != 0)
			{
				%this.setBinarySize(%this.headerField["Content-Length"]);
			}
		}
	}
}

//Called when a binary chunk is received.
//@see	TCPClient::setProgressBar
function TCPClient::onBinChunk(%this, %chunk)
{
	%this.setProgressBar(%chunk / %this.headerField["Content-Length"]);

	if(%chunk >= %this.headerField["Content-Length"])
	{
		%this.saveBufferToFile(%this.downloadLocation);
		%this.onDone();
	}
}

//Used when downloading text files.
//@param	string text The text received.
function TCPClient::handleText(%this, %text)
{
	%text = expandEscape(%text);
	//Damn inheritance is broken in the engine.
	if(isFunction(%this.className, "handleText"))
		eval(%this.className @ "::handleText(" @ %this @ ",\"" @ %text @ "\");");
}

//Used to update a progress bar when downloading a binary file.
//@param	float completed The amount completed, represented as a floating point value from 0.0 to 1.0.	
function TCPClient::setProgressBar(%this, %completed)
{
	//Damn inheritance is broken in the engine.
	if(isFunction(%this.className, "setProgressBar"))
		eval(%this.className @ "::setProgressBar(" @ %this @ ",\"" @ %completed @ "\");");
}<file_sep>$RFW::MaxLevel = 70;

function RFW_GenerateLevels()
{
	deleteVariables("$RFW::LevelStat*");
	$RFW::LevelStatHealth = 90;
	$RFW::LevelStatExp = 180;
	$RFW::LevelStatMana = 100;
	for(%i = 1; %i <= $RFW::MaxLevel; %i++)
	{
		$RFW::LevelStat[%i, "Health"] = $RFW::LevelStatHealth + (%i-1)*2.8;
		$RFW::LevelStat[%i, "Exp"] = $RFW::LevelStatExp + (%i-1)*13.3;
		$RFW::LevelStat[%i, "Mana"] = $RFW::LevelStatMana + (%i-1)*1.6;
	}
}
RFW_GenerateLevels();

package RFW_Leveling
{
	function GameConnection::createPlayer(%this, %transform)
	{
		cancel(%this.levelingSch);
		%this.levelingSch = %this.schedule(50, "RFW_Init");
		return Parent::createPlayer(%this, %transform);
	}
};
activatePackage("RFW_Leveling");

function GameConnection::RFW_Init(%this)
{
	if(!isObject(%player = %this.player))
		return;

	if(!isObject(%acc = %this.account))
		return;

	if(%acc.vHealth $= "")
		%acc.vHealth = getLevelStat(1, "hp");

	if(%acc.vMaxExp $= "")
		%acc.vMaxExp = getLevelStat(1, "exp");

	if(%acc.vExp $= "")
		%acc.vExp = 0;

	if(%acc.vLevel $= "")
		%acc.vLevel = 1;

	%player.setMaxHealth(%acc.vHealth);
}

function getLevelStat(%lv, %type)
{
	if(%lv > $RFW::MaxLevel)
		%lv = $RFW::MaxLevel;

	%stat = 0;

	%hpStart = 100;
	%hp = 2;

	%expStart = 50;
	%exp = 5;

	%lvLoop = %lv-1;
	if(%type $= "health" || %type $= "hp")
	{
		if(%lvLoop > 1)
		{
			for(%i = 0; %i < %lvLoop; %i++)
				%hpStart += %hp;
		}

		%stat = %hpStart;
	}
	else if(%type $= "exp" || %type $= "xp")
		return $RFW::LevelStat[%lv, ""];

	return 0;
}

function GameConnection::checkLevel(%this, %debug)
{
	if(!isObject(%acc = %this.account))
		return;

	if(%acc.vLevel <= 0)
		%acc.vLevel = 1;

	if(%acc.vExp < 0)
	{
		while(%acc.vExp < 0)
		{
			if(%acc.vLevel <= 1)
			{
				%acc.vExp = 0;
				break;
			}

			%acc.vLevel--;
			%exp = %acc.vExp += $RFW::LevelStat[%acc.vLevel, "exp"];
			%levels++;
		}

		%hp = %acc.vHealth = $RFW::LevelStat[%acc.vLevel, "health"];
		%maxExp = %acc.vMaxExp = $RFW::LevelStat[%acc.vLevel, "exp"];
		%mana = %acc.vMana = $RFW::LevelStat[%acc.vLevel, "mana"];

		if(%debug)
			%debugStr = "\c6(Debug -> Health " @ %hp @ " | Mana " @ %mana @ " | EXP " @ %exp @ "/" @ %maxExp @ "\c6)";

		messageAll('', '\c3%1 \c6has leveled down to \c3%2\c6. %3', %this.getPlayerName(), %acc.vLevel, %debugStr);
	}
	else if(%acc.vExp > %acc.vMaxExp)
	{
		while(%acc.vExp > %acc.vMaxExp)
		{
			if(%acc.vLevel >= $RFW::MaxLevel)
				break;

			%exp = %acc.vExp -= $RFW::LevelStat[%acc.vLevel, "exp"];
			%acc.vLevel++;

			%levels++;
		}

		%hp = %acc.vHealth = $RFW::LevelStat[%acc.vLevel, "health"];
		%mana = %acc.vMana = $RFW::LevelStat[%acc.vLevel, "mana"];
		%maxExp = %acc.vMaxExp = $RFW::LevelStat[%acc.vLevel, "exp"];

		if(%debug)
			%debugStr = "\c6(Debug -> Health " @ %hp @ " | Mana " @ %mana @ " | EXP " @ %exp @ "/" @ %maxExp @ "\c6)";

		messageAll('', '\c3%1 \c6has leveled up to \c3%2\c6. %3', %this.getPlayerName(), %acc.vLevel, %debugStr);

		if(isObject(%player = %this.player))
			%player.setMaxHealth(%hp);
	}
}

function GameConnection::addEXP(%this, %exp, %debug)
{
	if(!isObject(%acc = %this.account))
		return;

	%acc.vExp += %exp;
	%this.checkLevel(%debug);
}

function GameConnection::setLevel(%this, %level, %debug)
{
	if(!isObject(%acc = %this.account))
		return;

	if(%level > $RFW::MaxLevel)
		%level = $RFW::MaxLevel;

	%acc.vExp = 0;
	%hp = %acc.vHealth = $RFW::LevelStat[%level, "health"];
	%maxExp = %acc.vMaxExp = $RFW::LevelStat[%level, "exp"];
	%mana = %acc.vMana = $RFW::LevelStat[%level, "mana"];
	%acc.vLevel = %level;

	if(%debug)
		%debugStr = "\c6(Debug -> Health " @ %hp @ " | Mana " @ %mana @ " | EXP 0/" @ %maxExp @ "\c6)";

	messageAll('', '\c3%1 \c6has been set to level \c3%2\c6. %3', %this.getPlayerName(), %level, %debugStr);
	if(isObject(%player = %this.player))
		%player.setMaxHealth(%hp);
}<file_sep>//ReverieFortWars/resources.cs

//TABLE OF CONTENTS
// #1. documentation
// #2. resource enumeration
// #3. resource functionality

// #1.
//$RFW::Resources             | Total number of resources.
//$RFW::Resource[%int]        | The name of resource %int (%int is in the range 0 .. $RFW::Resources-1)
//$RFW::Resource[%str]        | The resource number of the resource named %str
//$RFW::ResourceQuality[%int] | The quality of resource %int, an int in the range 0 .. 100. Determines the durability per unit volume of placed bricks of this resource type.
//$RFW::ResourceTier[%int]    | The lowest tier of weapons that can destroy/harvest this material (1=wood, 2=stone, 3=surtra/obsidian, 4=calcite/nithlite, 5=sanguite/sinnite, 6=crystal)
//$RFW::ResourceColor[%int]   | The default paint color to use to color bricks of this resource.
//$RFW::ResourceColorFX[%int] | The default color effect to use for bricks of this resource. (0=none, 1=pearl, 2=chrome, 3=glow, 4=blink, 5=swirl, 6=rainbow)

// #2.
$RFW::Resources = 0;

function RFW_RegisterResource(%name, %quality, %tier, %colors, %colorFx)
{
	%x = $RFW::Resources++;
	$RFW::Resource[%x] = %name;
	$RFW::Resource[%name] = %x;
	$RFW::ResourceQuality[%x] = %quality;
	$RFW::ResourceQuality[%name] = %quality;
	$RFW::ResourceTier[%x] = %tier;
	$RFW::ResourceTier[%name] = %tier;
	%color = getWord(%colors, 0);
	$RFW::ResourceColor[%x] = %color;
	$RFW::ResourceColor[%name] = %color;
	$RFW::ResourceByColor[%color] = %name;
	for(%i = 1; %i < getWordCount(%colors); %i++)
	{
		%color = getWord(%colors, %i);
		$RFW::ResourceByColor[%color] = %name;
	}
	$RFW::ResourceColorFx[%x] = %colorFx;
	$RFW::ResourceColorFx[%name] = %colorFx;
}


RFW_RegisterResource("Dirt", 5, 0, "0", 0);
RFW_RegisterResource("Plant matter", 5, 0, "3", 0);
RFW_RegisterResource("Sand", 5, 0, "1", 0);
RFW_RegisterResource("Glass", 5, 0, "28 29 30 31 32 33 34 35 36", 1);
RFW_RegisterResource("Cloth", 10, 0, "19 20 21 22 23 24 25 26 27", 0);
RFW_RegisterResource("Snow", 10, 0, "2", 0);
RFW_RegisterResource("Flesh", 10, 0, "17", 1);
RFW_RegisterResource("Bone", 20, 0, "18", 0);
RFW_RegisterResource("Wood", 25, 0, "4", 0);
RFW_RegisterResource("Stone", 35, 1, "6 7 8 9 10", 0);
RFW_RegisterResource("Obsidian", 50, 2, "5", 1);
RFW_RegisterResource("Surtra", 50, 2, "11", 1);
RFW_RegisterResource("Calcite", 75, 3, "12", 1);
RFW_RegisterResource("Nithlite", 75, 3, "13", 2);
RFW_RegisterResource("Sanguite", 100, 4, "14", 1);
RFW_RegisterResource("Sinnite", 100, 4, "15", 2);
RFW_RegisterResource("Crystal", 125, 5, "16", 1);

// #3.
function getBiome(%pos)
{
	%pos = vectorAdd(%pos, "0 0 10");
	%end = vectorAdd(%pos, "0 0 -100");
	%ray = containerRaycast(%pos, %end, $Typemasks::FxBrickObjectType);
	while(isObject(%brick = firstWord(%ray)))
	{
		%data = %brick.getDatablock();
		if(%brick.getGroup().bl_id == 888888 && (%data.isCube || %data == nameToID(brick16x16fData)))
		{
			%res = $RFW::ResourceByColor[%brick.colorID];
			if(%res $= "Sand" || %res $= "Snow" || %res $= "Stone" || %res $= "Flesh")
			{
				return %res;
			}
			else
			{
				return "Grass";
			}
			break;
		}
		else
		{
			if(%crashCheck++ > 25)
			{
				error("getBiome crashcheck failed for pos " @ %pos);
				return -1;
			}
			%ray = containerRaycast(posFromRaycast(%ray), %end, $Typemasks::FxBrickObjectType, %brick);
		}
	}
	return 0;
}<file_sep>//ReverieFortWars/accounts.cs

//TABLE OF CONTENTS
// #1. functionality
//   #1.1 RFW_Account functions
//   #1.2 inventory functions
//   #1.3 interfacing
//   #1.4 hasRequiredItems
//   #1.5 getBestArrow
// #2. package

// #1.
// #1.1

if(!isObject("RFW_Accounts"))
	new SimGroup("RFW_Accounts");

function RFW_NewAccount()
{
	%acc = new ScriptObject()
	{
		class = "RFW_Account";
		vMaxExp = getLevelStat(1, "exp");
		vExp = 0;
		vHealth = getLevelStat(1, "hp");
		vLevel = 1;
		vSkillPoints = 0;
		vKarma = 0;
		vMaxItems = 50;
		vMaxEquips = 5;
		vEquip0 = "Fists\t0\t";
		vUseCPQ = true;
		vVer = 0;
	};
	%tribe = getWord($RFW::ActiveTribes, getRandom(0, getWordCount($RFW::ActiveTribes) - 1));
	%acc.set("Tribe", %tribe);
	RFW_Accounts.add(%acc);
	return %acc;
}

function RFW_Account::Read(%acc, %var)
{
	return %acc.v[%var];
}

function RFW_Account::Set(%acc, %var, %value)
{
	if(%acc.readOnly)
	{
		return false;
	}
	else
	{
		%acc.v[%var] = %value;
		return true;
	}
}

function RFW_Account::Inc(%acc, %var, %amt)
{
	if(%acc.readOnly)
	{
		return false;
	}
	else
	{
		%acc.v[%var]+= %value;
		return true;
	}
}

// #1.2

//returns a bool: whether this %acc has an instance of %item
//%item should be specified as the item's name
function RFW_Account::hasItem(%acc, %item)
{
	%max = %acc.read("MaxItems");
	for(%i = 0; %i < %max; %i++)
	{
		if(getItemType(%acc.read("Item" @ %i)) $= %item)
		{
			return true;
		}
	}
	return false;
}

//returns a bool: whether this %acc has at least some amount of an item
//%item must be an item string (use the quantity field)
function RFW_Account::hasItemQuantity(%acc, %item)
{
	%type = getItemType(%item);
	%amt = getItemQuantity(%item);
	%max = %acc.read("MaxItems");
	for(%i = 0; %i < %max; %i++)
	{
		%it = %acc.read("Item" @ %i);
		if(getItemType(%it) $= %type)
		{
			if(!$RFW::ItemStackable[%type])
			{
				%count++;
			}
			else
			{
				return getItemQuantity(%it) >= %amt;
			}
		}
	}
	return %count >= %amt;
}

//returns an int: the number of the specified item possessed by the account
//%item must be an item type
function RFW_Account::getItemQuantity(%acc, %item)
{
	%type = getItemType(%item);
	%max = %acc.read("MaxItems");
	%count = 0;
	for(%i = 0; %i < %max; %i++)
	{
		%it = %acc.read("Item" @ %i);
		if(getItemType(%it) $= %type)
		{
			if(!$RFW::ItemStackable[%type])
			{
				%count++;
			}
			else
			{
				return getItemQuantity(%it);
			}
		}
	}
	return %count;
}

//returns a string corresponding to the item, or -1 if the item was not found
//%item should be specified by name
function RFW_Account::getItem(%acc, %item)
{
	%max = %acc.read("MaxItems");
	for(%i = 0; %i < %max; %i++)
	{
		%it = %acc.read("Item" @ %i);
		if(getItemType(%it) $= %item)
		{
			return %it;
		}
	}
	return -1;
}

//returns a NL-delimited string, where each line is an item possessed by the player
//%item should be specified by name
function RFW_Account::getItemList(%acc)
{
	%max = %acc.read("MaxItems");
	for(%i = 0; %i < %max; %i++)
	{
		%it = getItemType(%acc.read("Item" @ %i));
		if(%it !$= "")
		{
			%out = %out NL %it;
		}
	}
	return removeRecord(%out, 0);
}

//returns an int: the slot the item is in, or -1 if the item was not found
//%item should be specified as the item's name
function RFW_Account::getItemSlot(%acc, %item)
{
	%max = %acc.read("MaxItems");
	for(%i = 0; %i < %max; %i++)
	{
		if(getItemType(%acc.read("Item" @ %i)) $= %item)
		{
			return %i;
		}
	}
	return -1;
}

//adds %item to %acc's inventory
//%item must be an item string (see items.cs documentation)
//returns a bool: whether adding the item to inventory was successful or not
function RFW_Account::addItem(%acc, %item, %display)
{
	%type = getItemType(%item);
	%stackable = $RFW::ItemStackable[%type];
	%max = %acc.read("MaxItems");
	%freeSpace = -1;
	for(%i = 0; %i < %max; %i++)
	{
		%it = %acc.read("Item" @ %i);
		if(%stackable && getItemType(%it) $= %type)
		{
			%amt = getItemQuantity(%item);
			%item = incItemQuantity(%it, %amt);
			if(%display)
			{
				%acc.client.centerPrintQueue("\c2Got " @ %amt @ "x " @ %type @ " (" @ getItemQuantity(%item) @ "x total)", 5);
			}
			return %acc.set("Item" @ %i, %item);
		}
		else if(%freeSpace == -1 && !strLen(%it))
		{
			if(!%stackable)
			{
				if(%display)
				{
					%acc.client.centerPrintQueue("\c2Got " @ %type, 5);
					%maxEquips = %acc.read("MaxEquips");
					if($RFW::ItemEquipable[%type])
					{
						for(%i = 0; %i < %maxEquips; %i++)
						{
							if(%acc.read("Equip" @ %i) $= "")
							{
								schedule(0, 0, serverCmdFromPack, %acc.client, %type);
							}
						}
					}
					else if($RFW::ItemWearable[%type])
					{
						if(%acc.read($RFW::ItemSlot[%type]) $= "")
						{
							schedule(0, 0, serverCmdEquip, %acc.client, %type);
						}
					}
				}
				return %acc.set("Item" @ %i, %item);
			}
			%freeSpace = %i;
		}
	}
	if(%freeSpace != -1)
	{
		if(%stackable)
		{
			%amt = getItemQuantity(%item);
			if(%display)
			{
				%acc.client.centerPrintQueue("\c2Got " @ %amt @ "x " @ %type @ " (" @ %amt @ "x total)", 5);
			}
		}
		else if(%display)
		{
			%acc.client.centerPrintQueue("\c2Got " @ %type, 5);
			if($RFW::ItemEquipable[%type])
			{
				//!!! TODO: make it so this only happens if there's room
				schedule(0, 0, serverCmdFromPack, %acc.client, %type);
			}
			else if($RFW::ItemWearable[%type])
			{
				//!!! TODO: make it so this only happens if there's room
				schedule(0, 0, serverCmdEquip, %acc.client, %type);
			}
		}
		return %acc.set("Item" @ %freeSpace, %item);
	}
	return false;
}

//adds %item to %acc's inventory
//%item must be an item string (see items.cs documentation)
//%display is a bool which sets whether any information is sent to the user
//returns a bool: whether removing the item from inventory was successful or not
function RFW_Account::removeItem(%acc, %item, %display)
{
	%stackable = $RFW::ItemStackable[getItemType(%item)];
	%max = %acc.read("MaxItems");
	for(%i = 0; %i < %max; %i++)
	{
		%it = %acc.read("Item" @ %i);
		if(getItemType(%it) $= getItemType(%item))
		{
			if(%stackable)
			{
				%amt = -1 * getItemQuantity(%item);
				%item = incItemQuantity(%it, %amt);
				if(getItemQuantity(%item) < 0)
				{
					return false;
				}
				if(%display)
				{
					%acc.client.centerPrintQueue("<color:FF0000>Lost " @ %amt @ "x " @ getItemType(%item), 5);
				}
				if(getItemQuantity(%item) == 0)
				{
					%result = %acc.set("Item" @ %i, "");
					%acc.resortInventory();
					return %result;
				}
				else
				{
					return %acc.set("Item" @ %i, %item);
				}
			}
			else
			{
				if(%display)
				{
					%acc.client.centerPrintQueue("<color:FF0000>Lost " @ getItemType(%item), 5);
				}
				%result = %acc.set("Item" @ %i, "");
				%acc.resortInventory();
				return %result;
			}
		}
	}
	return false;
}

function RFW_Account::resortInventory(%acc)
{
	%maxItems = %acc.read("MaxItems");
	%drop = 0;
	for(%peek = 0; %peek < %maxItems; %peek++)
	{
		%item = %acc.read("Item" @ %peek);
		if(getItemType(%item) !$= "")
		{
			if(%peek != %drop)
			{
				%acc.set("Item" @ %peek, "");
				%acc.set("Item" @ %drop, %item);
			}
			%drop++;
		}
	}
}

// #1.4
function RFW_Account::hasRequiredItems(%acc, %mats, %tools, %mult)
{
	if(%tools !$= "")
	{
		%hasTools = false;
		%fieldCount = getFieldCount(%tools);
		for(%i = 0; %i < %fieldCount; %i++)
		{
			%tool = getField(%tools, %i);
			if(%acc.hasItem(%tool))
			{
				%hasTools = true;
				break;
			}
		}
		if(!%hasTools)
		{
			return false;
		}
	}
	if(%mult !$= "")
	{
		%fieldCount = getFieldCount(%mats);
		for(%i = 0; %i < %fieldCount; %i++)
		{
			%field = getField(%mats, %i);
			%mats = setField(%mats, %i, setWord(%field, 0, firstWord(%field) * %mult));
		}
	}
	%fieldCount = getFieldCount(%mats);
	for(%i = 0; %i < %fieldCount; %i++)
	{
		%item = getField(%mats, %i);
		%item = restWords(%item) TAB firstWord(%item);
		if(!%acc.hasItemQuantity(%item))
		{
			return false;
		}
	}
	return true;
}

function RFW_Account::GetEquipmentList(%acc)
{
	%list = "";
	for(%i = 0; %i < %acc.read("MaxEquips"); %i++)
	{
		%eq = %acc.read("Equip" @ %i);
		if(%eq !$= "")
		{
			%list = %list NL getItemType(%eq);
		}
	}
	if((%eq = %acc.read("Armor")) !$= "")
	{
		%list = %list NL getItemType(%eq);
	}
	if((%eq = %acc.read("Back")) !$= "")
	{
		%list = %list NL getItemType(%eq);
	}
	if((%eq = %acc.read("Hat")) !$= "")
	{
		%list = %list NL getItemType(%eq);
	}
	if((%eq = %acc.read("Shirt")) !$= "")
	{
		%list = %list NL getItemType(%eq);
	}
	if((%eq = %acc.read("Pants")) !$= "")
	{
		%list = %list NL getItemType(%eq);
	}
	if(getRecordCount(%list) > 0)
	{
		return removeRecord(%list, 0);
	}
	else
	{
		return "";
	}
}

function RFW_Account::GetOptions(%acc)
{
	return "nothing\nyet";
}

function RFW_Account::AddFavor(%acc, %amt, %tribe)
{
	%accTribe = %acc.read("Tribe");
	if(%accTribe !$= %tribe && %accTribe !$= "")
	{
		return false;
	}
	%acc.inc("Favor" @ %tribe, %amt);
}

function RFW_FixAccounts()
{
	//RFW_Accounts
}

if(isPackage("RFW_Accounts"))
	deactivatePackage("RFW_Accounts");

// #2.
package RFW_Accounts
{
	function GameConnection::autoAdminCheck(%client)
	{
		%p = parent::autoAdminCheck(%client);
		if(!$RFW::IgnoreID[%client.getBLID()])
		{
			if($RFW::IgnoreID[%client.getBLID()])
			{
				%client.loadBetaAccount();
				return %p;
			}

			%file = "config/server/RFW/" @ %client.bl_id @ ".cs";
			if(isFile(%file))
			{
				exec(%file);
				%acc = NewlyLoaded.getID();
				if(isObject(%acc))
				{
					%acc.setName("");
					schedule(0, messageClient, %client, '', "\c2RFW account loaded successfully.");
				}
				else
				{
					error("Account loading failed (" @ %file @ ")");
					%acc = RFW_NewAccount();
				}
			}
			else
			{
				%acc = RFW_NewAccount();
				schedule(0, messageClient, %client, '', "\c2RFW account created successfully.");
			}

			%acc.set("Name", %client.getPlayerName());
			RFW_Accounts.add(%acc);
			%client.account = %acc;
			%acc.client = %client;
			%client.brickgroup.tribe = %acc.read("Tribe");
			if(%acc.vMaxExp $= "")
				%acc.vMaxExp = getLevelStat(1, "exp");
			if(%acc.vExp $= "")
				%acc.vExp = 0;
			if(%acc.vHealth $= "")
				%acc.vHealth = getLevelStat(1, "hp");
			if(%acc.vSkillPoints $= "")
				%acc.vSkillPoints = 0;
			if(%acc.vLevel $= "")
				%acc.vLevel = 1;
		}

		return %p;
	}

	function GameConnection::onDrop(%client)
	{
		%acc = %client.account;
		if(isObject(%acc))
		{
			%acc.setName("NewlyLoaded");
			%acc.save("config/server/RFW/" @ %client.bl_id @ ".cs");
			%acc.delete();
		}
		return parent::onDrop(%client);
	}
};
activatePackage(RFW_Accounts);<file_sep>//RFW Apocalypse
function RFW_Apocalypse()
{
	$RFW::Apocalypse = true;
	for(%i = 0; %i < clientGroup.getCount(); %i++)
	{
		%client = clientGroup.getObject(%i);
		%client.centerPrintQueue("<color:FFD800>BEHOLD THE APOCALYPSE!", 60000);
		%cam = %client.camera;
		%client.setControlObject(%cam);
		if(isObject(%obj = %client.player))
		{
			%obj = %client.player;
			%cam.setTransform(%obj.getTransform());
			%obj.delete();
		}
	}
	//clear all bricks
	for(%i = 0; %i < mainBrickGroup.getCount(); %i++)
	{
		mainBrickGroup.getObject(%i).chainDeleteAll();
	}
	schedule(1000, 0, exec, "Add-Ons/Gamemode_ReverieFortWars/scripts/buildWorld.cs");
	//schedule(1000, 0, ServerDirectSaveFileLoad, "saves/Beta City.bls", 3, "", 2);
}

package RFW_Apocalypse
{
	function GameConnection::SpawnPlayer(%client)
	{
		if(!$RFW::Apocalypse)
		{
			return parent::SpawnPlayer(%client);
		}
	}

	function ServerLoadSaveFile_End()
	{
		$RFW::Apocalypse = false;
		parent::ServerLoadSaveFile_End();
		for(%i = 0; %i < clientGroup.getCount(); %i++)
		{
			clientGroup.getObject(%i).spawnPlayer();
		}
	}
};
activatePackage(RFW_Apocalypse);<file_sep>//ReverieFortWars/crafting.cs

//TABLE OF CONTENTS

$RFW::T5Tools = "Sanguite crafting tools\tSinnite crafting tools";
$RFW::T4Tools = "Calcite crafting tools\tNithlite crafting tools" TAB $RFW::T5Tools;
$RFW::T3Tools = "Surtra crafting tools\tObsidian crafting tools" TAB $RFW::T4Tools;
$RFW::T2Tools = "Stone crafting tools" TAB $RFW::T3Tools;
$RFW::T1Tools = "Wooden crafting tools\tFaerie crafting tools" TAB $RFW::T2Tools;

function RFW_AddRecipe(%product, %materials, %requirements)
{
	$RFW::RecipeMats[%product] = %materials;
	$RFW::RecipeReqs[%product] = %requirements;
}

//resource conversions
RFW_AddRecipe("Cloth", "2 Plant matter", $RFW::T1Tools);
RFW_AddRecipe("Paper", "1 Plant matter\t1 Wood", $RFW::T1Tools);
RFW_AddRecipe("Leather", "3 Flesh", $RFW::T1Tools);
RFW_AddRecipe("Glass", "2 Sand", "Pyromancy wand");
RFW_AddRecipe("Stone", "2 Sand", "Pyromancy wand");
RFW_AddRecipe("Calcite", "5 Bone", "Ossimetallisynthesis staff");
RFW_AddRecipe("Sanguite", "10 Blood", "Sanguimetallisynthesis wand");

//tools
RFW_AddRecipe("Wooden crafting tools", "32 Wood");
RFW_AddRecipe("Faerie crafting tools", "32 Plant matter\t8 Faerie dust\t1 Wooden crafting tools");
RFW_AddRecipe("Stone crafting tools", "16 Stone\t1 Wooden crafting tools");
RFW_AddRecipe("Obsidian crafting tools", "32 Obsidian\t1 Stone crafting tools");
RFW_AddRecipe("Surtra crafting tools", "32 Surtra\t1 Stone crafting tools");
RFW_AddRecipe("Calcite crafting tools", "32 Calcite\t1 Obsidian crafting tools\n32 Calcite\t1 Surtra crafting tools");
RFW_AddRecipe("Nithlite crafting tools", "32 Nithlite\t1 Obsidian crafting tools\n32 Nithlite\t1 Surtra crafting tools");
RFW_AddRecipe("Sanguite crafting tools", "32 Sanguite\t1 Calcite crafting tools");
RFW_AddRecipe("Sinnite crafting tools", "32 Sinnite\t1 Nithlite crafting tools");

RFW_AddRecipe("Faerie wand", "8 Wood\t8 Plant matter\t8 Faerie dust", "Faerie crafting tools");
RFW_AddRecipe("Pyromancy wand", "16 Wood\t8 Plant matter\t8 Faerie dust", "Faerie crafting tools");
RFW_AddRecipe("Alchemy vials", "16 Glass", "Pyromancy wand");
RFW_AddRecipe("Photometallisynthesis lens", "32 Glass\t32 Plant matter\t8 Faerie dust", "Pyromancy wand");
RFW_AddRecipe("Ossimetallisynthesis staff", "32 Bone\t32 Calcite\t8 Faerie dust", "Faerie crafting tools");
RFW_AddRecipe("Sanguimetallisynthesis wand", "32 Blood\t32 Sanguite\t8 Faerie dust", "Faerie crafting tools");

//weapons
RFW_AddRecipe("Snowball", "2 Snow");

RFW_AddRecipe("Wooden sword", "32 Wood", $RFW::T1Tools);
RFW_AddRecipe("Wooden bow", "64 Wood\t8 Plant matter", $RFW::T1Tools);
RFW_AddRecipe("Wooden quiver", "24 Wood", $RFW::T1Tools);
RFW_AddRecipe("Wooden armor", "128 Wood", $RFW::T1Tools);

RFW_AddRecipe("Bone sword", "32 Bone", $RFW::T1Tools);
RFW_AddRecipe("Bone bow", "64 Bone\t8 Plant matter", $RFW::T1Tools);
RFW_AddRecipe("Bone quiver", "24 Bone", $RFW::T1Tools);
RFW_AddRecipe("Bone armor", "128 Bone", $RFW::T1Tools);

RFW_AddRecipe("Faerie bow", "16 Plant matter\t8 Faerie dust\t1 Wooden bow", "Faerie crafting tools");
RFW_AddRecipe("Faerie armor", "128 Plant matter\t8 Faerie dust", "Faerie crafting tools");

RFW_AddRecipe("Stone sword", "8 Wood\t32 Stone", $RFW::T2Tools);
RFW_AddRecipe("Stone quiver", "16 Wood\t16 Stone", $RFW::T2Tools);
RFW_AddRecipe("Stone pickaxe", "8 Wood\t32 Stone", $RFW::T2Tools);
RFW_AddRecipe("Stone armor", "128 Stone", $RFW::T2Tools);

RFW_AddRecipe("Obsidian sword", "24 Wood\t8 Obsidian", $RFW::T3Tools);
RFW_AddRecipe("Obsidian dagger", "24 Obsidian", $RFW::T3Tools);
RFW_AddRecipe("Obsidian quiver", "16 Wood\t16 Obsidian", $RFW::T3Tools);

RFW_AddRecipe("Surtra sword", "8 Wood\t32 Surtra", $RFW::T3Tools);
RFW_AddRecipe("Surtra dagger", "4 Wood\t24 Surtra", $RFW::T3Tools);
RFW_AddRecipe("Surtra pickaxe", "8 Wood\t48 Surtra", $RFW::T3Tools);
RFW_AddRecipe("Surtra quiver", "16 Wood\t16 Surtra", $RFW::T3Tools);
RFW_AddRecipe("Surtra armor", "128 Surtra", $RFW::T3Tools);

RFW_AddRecipe("Calcite sword", "8 Wood\t32 Calcite", $RFW::T4Tools);
RFW_AddRecipe("Calcite dagger", "4 Wood\t24 Calcite", $RFW::T4Tools);
RFW_AddRecipe("Calcite pickaxe", "8 Wood\t48 Calcite", $RFW::T4Tools);
RFW_AddRecipe("Calcite quiver", "16 Wood\t16 Calcite", $RFW::T4Tools);
RFW_AddRecipe("Calcite armor", "128 Calcite", $RFW::T4Tools);

RFW_AddRecipe("Nithlite sword", "8 Wood\t32 Nithlite", $RFW::T4Tools);
RFW_AddRecipe("Nithlite dagger", "4 Wood\t24 Nithlite", $RFW::T4Tools);
RFW_AddRecipe("Nithlite pickaxe", "8 Wood\t48 Nithlite", $RFW::T4Tools);
RFW_AddRecipe("Nithlite quiver", "16 Wood\t16 Nithlite", $RFW::T4Tools);
RFW_AddRecipe("Nithlite armor", "128 Nithlite", $RFW::T4Tools);

RFW_AddRecipe("Sanguite sword", "8 Wood\t32 Sanguite", $RFW::T5Tools);
RFW_AddRecipe("Sanguite dagger", "4 Wood\t24 Sanguite", $RFW::T5Tools);
RFW_AddRecipe("Sanguite pickaxe", "8 Wood\t48 Sanguite", $RFW::T5Tools);
RFW_AddRecipe("Sanguite quiver", "16 Wood\t16 Sanguite", $RFW::T5Tools);
RFW_AddRecipe("Sanguite armor", "128 Sanguite", $RFW::T5Tools);

RFW_AddRecipe("Sinnite sword", "8 Wood\t32 Sinnite", $RFW::T5Tools);
RFW_AddRecipe("Sinnite dagger", "4 Wood\t24 Sinnite", $RFW::T5Tools);
RFW_AddRecipe("Sinnite pickaxe", "8 Wood\t48 Sinnite", $RFW::T5Tools);
RFW_AddRecipe("Sinnite quiver", "16 Wood\t16 Sinnite", $RFW::T5Tools);
RFW_AddRecipe("Sinnite armor", "128 Sinnite", $RFW::T5Tools);

RFW_AddRecipe("Crystal sword", "8 Wood\t32 Crystal", $RFW::T5Tools);
RFW_AddRecipe("Crystal dagger", "4 Wood\t24 Crystal", $RFW::T5Tools);
RFW_AddRecipe("Crystal pickaxe", "8 Wood\t48 Crystal", $RFW::T5Tools);
RFW_AddRecipe("Crystal quiver", "16 Wood\t16 Crystal", $RFW::T5Tools);
RFW_AddRecipe("Crystal armor", "128 Crystal", $RFW::T5Tools);

//clothes
RFW_AddRecipe("Cloth shirt", "24 cloth", $RFW::T1Tools);
RFW_AddRecipe("Cloth pants", "24 cloth", $RFW::T1Tools);
RFW_AddRecipe("Cloth hat", "24 cloth", $RFW::T1Tools);
RFW_AddRecipe("Cloth cape", "16 cloth", $RFW::T1Tools);
RFW_AddRecipe("Cloth robe", "64 cloth", $RFW::T1Tools);
RFW_AddRecipe("Leather boots", "16 Leather", $RFW::T1Tools);
RFW_AddRecipe("Leather gloves", "16 Leather", $RFW::T1Tools);

//medicines and potions
RFW_AddRecipe("Cloth bandage", "2 cloth\t1 plant matter");
RFW_AddRecipe("Pishelva", "5 plant matter\t1 dirt\t1 faerie dust", "Alchemy vials");
RFW_AddRecipe("Hoborchakin", "5 plant matter\t1 wood\t1 faerie dust", "Alchemy vials");
RFW_AddRecipe("Vorpal potion", "1 sanguite\t1 calcite\t10 plant matter", "Alchemy vials");
RFW_AddRecipe("Fleshweaver's potion", "3 plant matter\t3 flesh\t3 blood\t1 bone\t1 faerie dust", "Alchemy vials");
RFW_AddRecipe("Crimson potion", "10 flesh\t2 blood\t1 faerie dust", "Alchemy vials");<file_sep>//ReverieFortWars/religion.cs

//TABLE OF CONTENTS

//first up: mediir and sarti
//next up: kazezi and eristia

$RFW::KillsFavorBonus["Mediir", "Bone"] = 0.5;
$RFW::KillsFavorBonus["Mediir", "Calcite"] = 1;

datablock fxDTSBrickData(brickAltarData)
{
	brickFile = "./altar.blb";
	category = "Special";
	subCategory = "Reverie Fort Wars";
	uiName = "Altar";
	//iconName = "Add-Ons/GameMode_ReverieFortWars/models/brick16x16";
};

package RFW_Religion
{
	function gameConnection::onDeath(%this, %sObj, %sClient, %dmgType, %dmgArea)
	{
		%p = parent::onDeath(%this, %sObj, %sClient, %dmgType, %dmgArea);
		if(isObject(%sClient))
		{
			RFW_ParseKill(%this, %sClient);
		}
		return %p;
	}
};
activatePackage(RFW_Religion);

function RFW_ParseKill(%vClient, %kClient)
{
	%vAcc = %vClient.account;
	%kAcc = %kClient.account;
	//check if this was a dishonorable kill
	//%honorable = %kAcc.read("EquipmentTier") <= %vAcc.read("EquipmentTier");
	//for now..
	%honorable = true;
	%tribe = %kAcc.read("Tribe");
	%player = %kClient.player;
	%slot = %player.currTool;
	if(isObject(%player) && %slot != -1)
	{
		//record kill on weapon
		%wpn = incItemKills(%kAcc.read("Equip" @ %slot), 1);
		%kAcc.set("Equip" @ %slot, %wpn);
		//check for favor bonus for this weapon
		if(%honorable)
		{
			%mat = firstWord(getItemType(%wpn));
			%multipler = 1 + $RFW::KillsFavorBonus[%tribe, %mat];
			%favor = 10 * %multiplier;
			%kAcc.addFavor(%favor);
		}
	}
}<file_sep>//ReverieFortWars/reverieAI.cs

exec("./amdeAI.cs");

//TABLE OF CONTENTS
// #0. system
// #1. faerie
// #2. spooky skeleton
// #3. horse
// #4. exile
// #5. blood golem

$RFW::EnemyTypeExp[amdeAI_Faerie] = 40;
$RFW::EnemyTypeExp[amdeAI_Horse] = 15;
$RFW::EnemyTypeExp[amdeAI_ExileNewbie] = 4;
$RFW::EnemyTypeExp[amdeAI_ExileStone] = 6;
$RFW::EnemyTypeExp[amdeAI_ExileCalcite] = 8;
$RFW::EnemyTypeExp[amdeAI_ExileSangite] = 12;
$RFW::EnemyTypeExp[amdeAI_SkeletonWarrior] = 14;
$RFW::EnemyTypeExp[amdeAI_SkeletonRanger] = 18;
$RFW::EnemyTypeExp[amdeAI_ExileSanguite] = 20;
$RFW::EnemyTypeExp[amdeAI_Wendigo] = 32;
$RFW::EnemyTypeExp[amdeAI_BloodGolem] = 28;

$RFW::EnemyTypes["Grass"] = 3;
$RFW::EnemyType["Grass", 0] = 0.05 SPC amdeAI_Faerie;
$RFW::EnemyType["Grass", 1] = 0.2 SPC amdeAI_Horse;
$RFW::EnemyType["Grass", 2] = 0.4 SPC amdeAI_ExileNewbie;

$RFW::EnemyTypes["Sand"] = 3;
$RFW::EnemyType["Sand", 0] = 0.1 SPC amdeAI_SkeletonWarrior;
$RFW::EnemyType["Sand", 1] = 0.2 SPC amdeAI_SkeletonRanger;
$RFW::EnemyType["Sand", 2] = 0.25 SPC amdeAI_ExileNewbie;
$RFW::EnemyType["Sand", 3] = 0.35 SPC amdeAI_ExileStone;
$RFW::EnemyType["Sand", 4] = 0.4 SPC amdeAI_ExileCalcite;
$RFW::EnemyType["Sand", 5] = 0.45 SPC amdeAI_ExileSangite;

$RFW::EnemyTypes["Snow"] = 3;
$RFW::EnemyType["Snow", 0] = 0.1 SPC amdeAI_SkeletonWarrior;
$RFW::EnemyType["Snow", 1] = 0.2 SPC amdeAI_SkeletonRanger;
$RFW::EnemyType["Snow", 2] = 0.25 SPC amdeAI_ExileStone;
$RFW::EnemyType["Snow", 3] = 0.35 SPC amdeAI_ExileCalcite;
$RFW::EnemyType["Snow", 4] = 0.4 SPC amdeAI_ExileSangite;

$RFW::EnemyTypes["Stone"] = 3;
$RFW::EnemyType["Stone", 0] = 0.1 SPC amdeAI_SkeletonWarrior;
$RFW::EnemyType["Stone", 1] = 0.2 SPC amdeAI_SkeletonRanger;
$RFW::EnemyType["Stone", 2] = 0.25 SPC amdeAI_ExileNewbie;
$RFW::EnemyType["Stone", 3] = 0.35 SPC amdeAI_ExileStone;
$RFW::EnemyType["Stone", 4] = 0.45 SPC amdeAI_ExileCalcite;
$RFW::EnemyType["Stone", 5] = 0.5 SPC amdeAI_ExileSangite;

$RFW::EnemyTypes["Flesh"] = 4;
$RFW::EnemyType["Flesh", 0] = 0.1 SPC amdeAI_SkeletonWarrior;
$RFW::EnemyType["Flesh", 1] = 0.2 SPC amdeAI_SkeletonRanger;
$RFW::EnemyType["Flesh", 2] = 0.3 SPC amdeAI_ExileSanguite;
$RFW::EnemyType["Flesh", 3] = 0.9 SPC amdeAI_Wendigo;
$RFW::EnemyType["Flesh", 4] = 1.0 SPC amdeAI_BloodGolem;

// #0.

//this set keeps track of all active AIs
new simSet(RFW_AiGroup)
{
};

function AnyPlayerCanSee(%obj)
{
	%clientCount = clientGroup.getCount();
	for(%i = 0; %i < %clientCount; %i++)
	{
		if(isObject(%player = clientGroup.getObject(%i).player))
		{
			%hpos = %obj.getHackPosition();
			%eyevec = %player.getEyeVector();
			%eyept = %player.getEyePoint();
			%dvec = vectorNormalize(vectorSub(%hpos, %eyept));
			%dist = vectorDist(%hpos, %eyept);
			if(%dist < 40)
			{
				return true;
			}
			else if(%dist < 150 && getVectorAngle(%eyevec, %dvec) < 120)
			{
				%masks = $Typemasks::FxBrickObjectType; //don't really need other typemasks
				%ray = containerRaycast(%eyept, %hpos, %masks);
				if(!isObject(firstWord(%ray)))
				{
					return true;
				}
			}
		}
	}
	return false;
}

function AnyPlayerCanSeePos(%pos)
{
	%clientCount = clientGroup.getCount();
	for(%i = 0; %i < %clientCount; %i++)
	{
		%client = clientGroup.getObject(%i);
		if(isObject(%player = %client.player))
		{
			%eyevec = %player.getEyeVector();
			%eyept = %player.getEyePoint();
			%dvec = vectorNormalize(vectorSub(%pos, %eyept));
			%dist = vectorDist(%pos, %eyept);
			if(%dist < 150 && getVectorAngle(%eyevec, %dvec) < 120)
			{
				%masks = $Typemasks::FxBrickObjectType; //don't really need other typemasks
				%ray = containerRaycast(%eyept, %pos, %masks);
				if(!isObject(firstWord(%ray)))
				{
					return true;
				}
			}
		}
	}
	return false;
}

function PickAiSpawnPoint_AboveGround(%center)
{
	while(%crashCheck++ < 25)
	{
		%ang = $pi * (getRandom() - 0.5);
		%radius = getRandom(10, 35);
		%rpos = vectorAdd(%center, (%radius * mCos(%ang)) SPC (%radius * mSin(%ang)) SPC -50);
		%ray = containerRaycast(setWord(%rpos, 2, 500), %rpos, $Typemasks::FxBrickObjectType);
		if(isObject(%brick = firstWord(%ray)) && %brick.getGroup().bl_id == 888888)
		{
			%rpos = posFromRaycast(%ray);
			if(!AnyPlayerCanSeePos(%rpos))
			{
				return %brick TAB %rpos;
			}
		}
	}
	echo("\c2PickAiSpawnPoint_AboveGround crashcheck failed for center " @ %center);
	return 0 TAB %center;
}

function serverCmdSpawnMob(%this, %val)
{
	if(!%this.isSuperAdmin)
		return;

	if(!isObject(%player = %this.player))
		return;

	%ppos = %player.getPosition();
	%masks = $Typemasks::FxBrickObjectType;
	%ray = containerRaycast(%ppos, vectorAdd(%ppos, "0 0 20"), %masks);
	%underground = isObject(firstWord(%ray));
	%biome = getBiome(%ppos);
	if(%underground)
	{
	}
	else //above ground
	{
		%pos = vectorAdd(%ppos, getRandom(-6, 6) SPC getRandom(-6, 6) SPC 10);
		%r = getRandom();
		if(%val > 0)
			%r = %val;

		for(%j = 0; %j < $RFW::EnemyTypes[%biome]; %j++)
		{
			if(%r < firstWord($RFW::EnemyType[%biome, %j]))
			{
				%type = restWords($RFW::EnemyType[%biome, %j]);
				break;
			}
		}
		if(isObject(%type))
		{
			RFW_AiGroup.add(amdeAI_Create(%pos, %type));
		}
	}
}

//this function runs periodically to spawn AIs near players
function RFW_AiSpawn()
{
	cancel($RFW_AiSpawnSched);
	%clientCount = clientGroup.getCount();
	for(%i = 0; %i < %clientCount; %i++)
	{
		if(!isObject(%player = clientGroup.getObject(%i).player))
		{
			continue;
		}
		%ppos = %player.getPosition();
		%masks = $Typemasks::FxBrickObjectType;
		%ray = containerRaycast(%ppos, vectorAdd(%ppos, "0 0 20"), %masks);
		%underground = isObject(firstWord(%ray));
		%biome = getBiome(%ppos);
		if(%underground)
		{
		}
		else //above ground
		{
			%spawn = PickAiSpawnPoint_AboveGround(%ppos);
			%brick = getField(%spawn, 0);
			%spawn = getField(%spawn, 1);
			if(!isObject(%brick) || findClosestTotemDist(%spawn) != -1)
			{
				continue;
			}
			%r = getRandom();
			for(%j = 0; %j < $RFW::EnemyTypes[%biome]; %j++)
			{
				if(%r < firstWord($RFW::EnemyType[%biome, %j]))
				{
					%type = restWords($RFW::EnemyType[%biome, %j]);
					break;
				}
			}
			if(isObject(%type))
			{
				RFW_AiGroup.add(amdeAI_Create(%spawn, %type));
			}
		}
	}
	%interval = getRandom(70, 120) * 500;
	$RFW_AiSpawnSched = schedule(%interval, 0, RFW_AiSpawn);
}

$RFW_AiSpawnSched = schedule(90000, 0, RFW_AiSpawn);

//this function runs periodically to delete old AIs that are out of sight of and far away from any players
function RFW_AiCollectGarbage()
{
	cancel($RFW_AiCollectGarbageSched);
	%time = getSimTime();
	%count = RFW_AiGroup.getCount();
	for(%i = 0; %i < %count; %i++)
	{
		%obj = RFW_AiGroup.getObject(%i);
		if((%obj.lastSeenTime !$= "" ? %obj.lastSeenTime : %obj.creationTime) + %obj.type.garbageCollectionTimeout < %time && !isObject(%obj.target))
		{
			if(!AnyPlayerCanSee(%obj))
			{
				%obj.delete();
				%i--;
				%count--;
			}
			else
			{
				%obj.lastSeenTime = %time;
			}
		}
	}
	$RFW_AiCollectGarbageSched = schedule(10000, 0, RFW_AiCollectGarbage);
}

$RFW_AiCollectGarbageSched = schedule(180000, 0, RFW_AiCollectGarbage);

// #1.

new ScriptObject(amdeAI_Faerie)
{
	name = "Faerie"; //for function calls
	faerie = true;

	canMove = true;
	sightRange = 75;
	fov = 360;

	rangedWeapon = FaeFireImage;
	effectiveRange = 50;
	safeRange = 0;
	prefersRanged = true;
	rangedOnly = false;

	garbageCollectionTimeout = 60000;

	meleeWeapon = FaeFireImage;
};

function amdeAI_Faerie_OnAdd(%bot)
{
	%bot.setDatablock(PlayerFaerie);
	%bot.mountImage(FaerieImage, 1);
	%bot.damageReduction = 0.5;
	%bot.creationTime = getSimTime();
	//the faerie image will hide all nodes for us
}

function amdeAI_Faerie_ShouldAttack(%bot, %target)
{
	if(%target.getClassName() $= "AiPlayer" && %target.type.name !$= "Faerie" && %target.type.name !$= "Horse" && %target.type.name !$= "Exile")
	{
		return true;
	}
	return %bot.hates[%target];
}

function amdeAI_Faerie_OnDetectAlly(%bot, %ally)
{
	if(%bot.lastHelloTime + 30000 < getSimTime() && %ally.getClassName() $= "Player" && vectorDist(%bot.getPosition(), %ally.getPosition()) < 15)
	{
		ServerPlay3d(FaerieHello, %bot.getEyePoint());
		%bot.lastHelloTime = getSimTime();
	}
}

function amdeAI_Faerie_Attack(%bot, %target)
{
	%bot.setImageTrigger(0, 1);
	%bot.setImageTrigger(0, 0);
}

function amdeAI_Faerie_OnDamage(%bot, %sourceObject, %position, %damage, %damageType)
{
	if(!isObject(%bot.target) && isObject(%sourceObject))
	{
		switch$(%sourceObject.getClassName())
		{
			case "Player":
				%bot.target = %sourceObject;
			case "Projectile":
				%bot.target = %sourceObject.sourceObject;
			default:
				%bot.target = "";
		}
		%bot.hates[%bot.target] = true;
		%bot.amdeAI_Sched();
	}
	//aggro all nearby faeries
	%time = getSimTime();
	%eyePos = %bot.getEyePoint();
	if(%bot.lastScreamTime + 30000 < %time)
	{
		%bot.lastScreamTime = %time;
		ServerPlay3d(FaerieWatchout, %eyePos);
	}
	InitContainerRadiusSearch(%eyePos, %bot.type.sightrange, $Typemasks::PlayerObjectType);
	while(%hit = ContainerSearchNext())
	{
		if(%hit.getClassName() $= "AiPlayer" && %hit.type.faerie && !isObject(%hit.target))
		{
			%hit.target = %bot.target;
			%hit.amdeAI_Sched();
			%hit.hates[%bot.target] = true;
		}
	}
	//play loud scream?
}

function amdeAI_Faerie_OnDeath(%bot)
{
	%bot.unbutcherable = true;
	%lootcount = -1;
	%bot.loot[%lootcount++] = "Faerie dust" TAB getRandom(6, 8);
	if(getRandom() > 0.5)
	{
		%bot.loot[%lootcount++] = "Plant matter" TAB getRandom(2, 18);
	}
	if(getRandom() > 0.7)
	{
		%bot.loot[%lootcount++] = "Wood\t1";
	}
}

// #2.
new ScriptObject(amdeAI_SkeletonRanger)
{
	name = "Skeleton";

	canMove = true;
	sightRange = 50;
	fov = 135;

	rangedWeapon = BoneBowImage;
	effectiveRange = 30;
	safeRange = 10;
	prefersRanged = true;
	rangedOnly = false;

	garbageCollectionTimeout = 30000;

	meleeWeapon = BoneSwordImage;
};

new ScriptObject(amdeAI_SkeletonWarrior)
{
	name = "Skeleton";

	canMove = true;
	sightRange = 50;
	fov = 135;

	rangedWeapon = "";
	effectiveRange = 0;
	safeRange = 0;
	prefersRanged = false;
	rangedOnly = false;

	garbageCollectionTimeout = 30000;

	meleeWeapon = BoneSwordImage;
};

function amdeAI_Skeleton_OnAdd(%bot)
{
	%bot.setDatablock(ReveriePlayer);
	%bot.damageReduction = 0.75;
	%bot.damageHandicap = 0.5;
	%bot.creationTime = getSimTime();
	%bot.setNodeColor("ALL", "1 1 1 1");
	%bot.hideNode("chest");
	%bot.hideNode("lArm");
	%bot.hideNode("rArm");
	%bot.unhideNode("lArmSlim");
	%bot.unhideNode("rArmSlim");
	%bot.mountImage(RibcageImage, 2);
	if(getRandom() > 0.8)
	{
		%bot.hideNode("headSkin");
	}
	if(getRandom() > 0.8)
	{
		%bot.damageReduction = 0.6;
		%bot.unhideNode("armor");
		%bot.unhideNode("shoulderPads");
		%bot.setNodeColor("armor", "0.3 0.3 0.25 1");
		%bot.setNodeColor("shoulderPads", "0.3 0.3 0.25 1");
	}
	%bot.setFaceName("Orc");
}

function amdeAI_Skeleton_ShouldAttack(%bot, %target)
{
	if(%target.getClassName() $= "Player" || %target.type.name $= "Faerie")
	{
		return true;
	}
}

function amdeAI_Skeleton_Attack(%bot, %target)
{
	%bot.setImageTrigger(0, 1);
	%bot.setImageTrigger(0, 0);
}

function amdeAI_Skeleton_OnDamage(%bot, %sourceObject, %position, %damage, %damageType)
{
	if(!isObject(%bot.target) && isObject(%sourceObject))
	{
		switch$(%sourceObject.getClassName())
		{
			case "Player":
				%bot.target = %sourceObject;
			case "Projectile":
				%bot.target = %sourceObject.sourceObject;
			default:
				%bot.target = "";
		}
		%bot.amdeAI_Sched();
	}
}

function amdeAI_Skeleton_OnDeath(%bot, %pos)
{
	%bot.setButcherLoot(0, 0, 10);
	%lootcount = -1;
	if(getRandom() > 0.9)
	{
		%bot.loot[%lootcount++] = "Bone bow\t\t";
		%bot.loot[%lootcount++] = "Bone arrow" TAB getRandom(5, 15);
	}
	if(getRandom() > 0.9)
	{
		%bot.loot[%lootcount++] = "Bone sword\t\t";
	}
}

function amdeAI_Skeleton_OnDetectEnemy(%bot, %target)
{
	%time = getSimTime();
	if(%bot.lastGrowlTime + 45000 < %time)
	{
		%bot.lastGrowlTime = %time;
		ServerPlay3d(MonsterGrowl, %bot.getEyePoint());
	}
}

// #3.
new ScriptObject(amdeAI_Horse)
{
	name = "Horse";

	canMove = true;
	sightRange = 30;
	fov = 90;

	rangedWeapon = "";
	effectiveRange = 30;
	safeRange = 99;
	prefersRanged = true;
	rangedOnly = true;

	garbageCollectionTimeout = 60000;

	meleeWeapon = "";
};

function amdeAI_Horse_onAdd(%bot)
{
	%bot.setDatablock(ReverieHorse);
	%bot.mountVehicle = true;
	switch(getRandom(1, 6))
	{
		case 1: %bot.setNodeColor("ALL", "0.8 0.8 0.8 1");
		case 2: %bot.setNodeColor("ALL", "0.4 0.4 0.4 1");
		case 3: %bot.setNodeColor("ALL", "0.4 0.3 0.2 1");
		case 4: %bot.setNodeColor("ALL", "0.9 0.8 0.3 1");
		case 5: %bot.setNodeColor("ALL", "0.1 0.1 0.1 1");
		case 6: %bot.setNodeColor("ALL", "0.8 0.3 0.2 1");
	}
}

function amdeAI_Horse_ShouldAttack(%bot, %target)
{
	//horse will always flee, so this function is more like ShouldFlee than ShouldAttack
	if(%target.getClassName() $= "AiPlayer" && %target.type.name $= "Skeleton")
	{
		return true;
	}
	return %bot.hates[%target];
}

function amdeAI_Horse_OnDamage(%bot, %sourceObject, %position, %damage, %damageType)
{
	if(isObject(%sourceObject))
	{
		switch$(%sourceObject.getClassName())
		{
			case "Player":
				%bot.target = %sourceObject;
			case "Projectile":
				%bot.target = %sourceObject.sourceObject;
			default:
				%bot.target = "";
		}
		%bot.amdeAI_Sched();
	}
}

function amdeAI_Horse_Retreat(%bot, %target)
{
	%time = getSimTime();
	if(%bot.lastNeighTime + 45000 < %time)
	{
		%bot.lastNeighTime = %time;
		ServerPlay3d(HorseNeigh, %bot.getEyePoint());
	}
	//default retreat functionality
	%bot.setMoveObject(0);
	%bot.clearAim();
	%run = vectorNormalize(vectorSub(setWord(%tlpos, 2, 0), setWord(%target.getPosition(), 2, 0)));
	%run = vectorAdd(%tlpos, vectorScale(%run, 50));
	%bot.setMoveDestination(%run);
	%bot.setJumping(true);
	%bot.schedule(100, setJumping, false);
	return true;
}

function amdeAI_Horse_OnDeath(%bot, %pos)
{
	%bot.setButcherLoot(90, 40, 20);
}

// #4.

mutalidZombie.jumpSound = mutalidJumpSound;
mutalidZombie.isInvincible = false;

function ZombieDefault::onCollision() { }
function ZombieDefault::onDisabled() { }

function MclawProjectile::onCollision(%this, %obj, %col, %fade, %pos, %normal)
{
	if(%col.getType() & $Typemasks::PlayerObjectType && minigameCanDamage(%obj.sourceObject, %col))
	{
		%col.damage(%obj.sourceObject, %pos, 35, $DamageType::Mclaw);
	}
}

new ScriptObject(amdeAI_Wendigo)
{
	name = "Wendigo";

	canMove = true;
	sightRange = 50;
	fov = 135;

	rangedWeapon = "";
	effectiveRange = 0;
	safeRange = 0;
	prefersRanged = false;
	rangedOnly = false;

	garbageCollectionTimeout = 50000;

	meleeWeapon = mclawimage;
};

function amdeAI_Wendigo_OnAdd(%bot)
{
	%bot.setDatablock(mutalidZombie);
	%bot.damageReduction = 1;
	%bot.creationTime = getSimTime();
	%bot.setNodeColor("ALL", 0.5 + getRandom() * 0.4 SPC getRandom() * 0.2 SPC getRandom() * 0.2 SPC 1);
}

function amdeAI_Wendigo_ShouldAttack(%bot, %target)
{
	if(%target.getClassName() $= "Player" || %target.type.name $= "Faerie" || %target.type.name $= "Horse" || %target.type.name $= "Exile")
	{
		return true;
	}
}

function amdeAI_Wendigo_Attack(%bot, %target)
{
	%bot.setImageTrigger(0, 1);
	%bot.setImageTrigger(0, 0);
}

function amdeAI_Wendigo_OnDamage(%bot, %sourceObject, %position, %damage, %damageType)
{
	if(!isObject(%bot.target) && isObject(%sourceObject))
	{
		switch$(%sourceObject.getClassName())
		{
			case "Player":
				%bot.target = %sourceObject;
			case "Projectile":
				%bot.target = %sourceObject.sourceObject;
			default:
				%bot.target = "";
		}
		%bot.amdeAI_Sched();
	}
}

function amdeAI_Wendigo_OnDeath(%bot, %pos)
{
	%bot.butcherLoot = "15 Bone\t20 Blood\t70 Flesh\t15 Calcite\t10 Sanguite";
}

function amdeAI_Wendigo_OnDetectEnemy(%bot, %target)
{
	%time = getSimTime();
	if(%bot.lastGrowlTime + 45000 < %time)
	{
		%bot.lastGrowlTime = %time;
		ServerPlay3d(MonsterGrowl, %bot.getEyePoint());
	}
}

// #4.
new ScriptObject(amdeAI_ExileNewbie)
{
	name = "Exile";
	tier = 0;

	canMove = true;
	sightRange = 50;
	fov = 135;

	rangedWeapon = "";
	effectiveRange = 0;
	safeRange = 0;
	prefersRanged = false;
	rangedOnly = false;

	garbageCollectionTimeout = 30000;

	meleeWeapon = fistsImage;
};

new ScriptObject(amdeAI_ExileStone)
{
	name = "Exile";
	tier = 1;

	canMove = true;
	sightRange = 50;
	fov = 135;

	rangedWeapon = "";
	effectiveRange = 0;
	safeRange = 0;
	prefersRanged = false;
	rangedOnly = false;

	garbageCollectionTimeout = 30000;

	meleeWeapon = stoneSwordImage;
};

new ScriptObject(amdeAI_ExileCalcite)
{
	name = "Exile";
	tier = 4;

	canMove = true;
	sightRange = 50;
	fov = 135;

	rangedWeapon = "";
	effectiveRange = 0;
	safeRange = 0;
	prefersRanged = false;
	rangedOnly = false;

	garbageCollectionTimeout = 30000;

	meleeWeapon = calciteSwordImage;
};

new ScriptObject(amdeAI_ExileSanguite)
{
	name = "Exile";
	tier = 6;

	canMove = true;
	sightRange = 50;
	fov = 135;

	rangedWeapon = "";
	effectiveRange = 0;
	safeRange = 0;
	prefersRanged = false;
	rangedOnly = false;

	garbageCollectionTimeout = 30000;

	meleeWeapon = sanguiteSwordImage;
};

function amdeAI_Exile_OnAdd(%bot)
{
	%bot.setDatablock(ReveriePlayer);
	%bot.creationTime = getSimTime();
	%skin = $RFW::Skin[getRandom(0, $RFW::SkinColors - 1)];
	%tier = %bot.type.tier;
	%bot.aggressive = getRandom() < %tier / 10;
	if(%tier == 0)
	{
		%bot.setNodeColor("ALL", %skin);
		%cloth = %skin $= "0.4 0.2 0 1" ? $RFW::Pants[1] : $RFW::Pants[0];
		%bot.setNodeColor("pants", %cloth);
	}
	else
	{
		%cloth = vectorScale(getRandom() SPC getRandom() SPC getRandom(), 0.5) SPC 1;
		switch(%tier)
		{
			case 1: %metal = getColorIdTable($RFW::ResourceColor["Stone"]);
			case 2: %metal = getColorIdTable($RFW::ResourceColor["Obsidian"]);
			case 3: %metal = getColorIdTable($RFW::ResourceColor["Nithlite"]);
			case 4: %metal = getColorIdTable($RFW::ResourceColor["Calcite"]);
			case 5: %metal = getColorIdTable($RFW::ResourceColor["Sinnite"]);
			case 6: %metal = getColorIdTable($RFW::ResourceColor["Sanguite"]);
			case 7: %metal = getColorIdTable($RFW::ResourceColor["Crystal"]);
		}
		%bot.setNodeColor("chest", %cloth);
		%bot.setNodeColor("pants", setWord(%cloth, 2, 0));
		%bot.unhideNode("armor");
		%bot.setNodeColor("armor", %metal);
		%bot.unhideNode("shoulderPads");
		%bot.setNodeColor("shoulderPads", %metal);
		%bot.setNodeColor("Lhand", setWord(%metal, 3, 1));
		%bot.setNodeColor("Rhand", setWord(%metal, 3, 1));
		%bot.setNodeColor("Lshoe", setWord(%metal, 3, 1));
		%bot.setNodeColor("Rshoe", setWord(%metal, 3, 1));
		if(getRandom() < 0.5)
		{
			%hat = $Hat[getRandom(1, 3)];
			%bot.unhideNode(%hat);
			%bot.setNodeColor(%hat, %metal);
		}
		else if(getRandom() < 0.3)
		{
			%hat = $Hat[getRandom(4, 7)];
			%bot.unhideNode(%hat);
			%bot.setNodeColor(%hat, %cloth);
		}
	}
	switch(getRandom(0, 10))
	{
		case 0:
			%bot.setFacename("smileyBlonde");
		case 1:
			%bot.setFacename("smileyCreepy");
		case 2:
			%bot.setFacename("smileyEvil1");
		case 3:
			%bot.setFacename("smileyEvil2");
		case 4:
			%bot.setFacename("smileyFemale1");
			%bot.setNodeColor("chest", %cloth);
			%bot.setNodeColor("femChest", %cloth);
		case 5:
			%bot.setFacename("BrownSmiley");
		case 6:
			%bot.setFacename("Male07Smiley");
		default:
			%bot.setFacename("smiley");
	}
}

function amdeAI_Exile_ShouldAttack(%bot, %target)
{
	return %bot.aggressive || %bot.hates[%target];
}

function amdeAI_Exile_OnDamage(%bot, %sourceObject, %position, %damage, %damageType)
{
	if(!isObject(%bot.target) && isObject(%sourceObject))
	{
		switch$(%sourceObject.getClassName())
		{
			case "Player":
				%bot.target = %sourceObject;
			case "Projectile":
				%bot.target = %sourceObject.sourceObject;
			default:
				%bot.target = "";
		}
		%bot.hates[%bot.target] = true;
		%bot.amdeAI_Sched();
	}
}

function amdeAI_Exile_OnDeath(%bot, %pos)
{
	%bot.setButcherLoot(45, 20, 10);
	%lootcount = -1;
	%tier = %this.type.tier;
	if(%tier == 0) //newbie
	{
		%bot.loot[%lootcount++] = "Wood" TAB getRandom(5, 20);
		%bot.loot[%lootcount++] = "Stone" TAB getRandom(5, 20);
		if(getRandom() < 0.5)
		{
			%bot.loot[%lootcount++] = "Plant matter" TAB getRandom(5, 20);
		}
		else
		{
			%bot.loot[%lootcount++] = "Dirt" TAB getRandom(5, 20);
		}
	}
	else if(%tier == 1) //stone
	{
		%bot.loot[%lootcount++] = "Stone" TAB getRandom(15, 30);
		%bot.loot[%lootcount++] = "Cloth" TAB getRandom(5, 15);
		if(getRandom() < 0.5)
		{
			%bot.loot[%lootcount++] = "Surtra" TAB getRandom(5, 15);
		}
	}
	else if(%tier == 4)  //calcite
	{
		%bot.loot[%lootcount++] = "Cloth" TAB getRandom(5, 15);
		%bot.loot[%lootcount++] = "Calcite" TAB getRandom(4, 12);
		if(getRandom() < 0.04)
		{
			switch(getRandom(1, 5))
			{
				case 1: %bot.loot[%lootcount++] = "Calcite sword\t0\t";
				case 2: %bot.loot[%lootcount++] = "Calcite dagger\t0\t";
				case 3: %bot.loot[%lootcount++] = "Calcite armor\t\t";
				case 4: %bot.loot[%lootcount++] = "Calcite quiver\t\t";
				case 5: %bot.loot[%lootcount++] = "Sanguite" TAB getRandom(3, 9);
			}
		}
	}
	else if(%tier == 6) //sanguite
	{
		%bot.loot[%lootcount++] = "Cloth" TAB getRandom(5, 15);
		%bot.loot[%lootcount++] = "Sanguite" TAB getRandom(4, 12);
		if(getRandom() < 0.03)
		{
			switch(getRandom(1, 4))
			{
				case 1: %bot.loot[%lootcount++] = "Sanguite sword\t0\t";
				case 2: %bot.loot[%lootcount++] = "Sanguite dagger\t0\t";
				case 3: %bot.loot[%lootcount++] = "Sanguite armor\t\t";
				case 4: %bot.loot[%lootcount++] = "Sanguite quiver\t\t";
			}
		}
	}
}

// #5.
new ScriptObject(amdeAI_BloodGolem)
{
	name = "Blood_golem";

	canMove = true;
	sightRange = 30;
	fov = 110;

	rangedWeapon = "";
	effectiveRange = 0;
	safeRange = 0;
	prefersRanged = false;
	rangedOnly = false;

	garbageCollectionTimeout = 120000;

	meleeWeapon = mClawImage;
};

function amdeAI_Blood_golem_OnAdd(%bot)
{
	%scale = getRandom(25, 35) / 10;
	%bot.setDatablock(ReveriePlayer);
	%bot.damageReduction = 0.5;
	%bot.damageHandicap = 0.5;
	%bot.creationTime = getSimTime();
	%bot.setPlayerScale(%scale);
	%bot.setMaxForwardSpeed(8 / %scale);
	%bot.setMaxSideSpeed(6 / %scale);
	%bot.setMaxBackwardSpeed(4 / %scale);
	%bot.setNodeColor("ALL", (getRandom(3, 8) / 10) SPC "0 0 1");
}

function amdeAI_Blood_golem_ShouldAttack(%bot, %target)
{
	if(%target.getClassName() $= "Player" || %target.type.name $= "Faerie" || %target.type.name $= "Horse" || %target.type.name $= "Exile")
	{
		return true;
	}
}

function amdeAI_Blood_golem_Pathfind(%bot, %target)
{
	return;
}

function amdeAI_Blood_golem_OnDeath(%bot, %pos)
{
	%scale = getWord(%bot.getScale(), 2);
	%bot.sanguification = mClampF(%bot.sanguification, 0.5, 1);
	%bot.setButcherLoot(20 * %scale, 50 * %scale, 0);
}<file_sep>//blind
function Player::Blind(%this, %dur, %dr)
{
	if(%this.getClassName() $= "AIplayer")
	{
	}
	else if(%this.BlindDR < 3)
	{
		if(%dr)
		{
			%dur/= mClamp(%this.BlindDR + 1, 1, 4);
			%this.BlindDR++;
			%this.schedule(15000 + %dur, BlindDRdec);
		}
		if(isObject(%data = %this.getDatablock().firstPersonOnlyAnalogue))
		{
			%this.setDatablock(%data);
		}
		%this.blinded = true;
		%this.mountImage(DarkBlindPlayerImage, 1);
		if(isEventPending(%this.unblindSched))
		{
			cancel(%this.unblindSched);
		}
		%this.unblindSched = %this.schedule(%dur, unblind);
	}
}

function Player::Unblind(%this)
{
	if(isEventPending(%this.unblindSched))
	{
		cancel(%this.unblindSched);
	}
	if(isObject(%data = %this.getDatablock().nonFirstPersonOnlyAnalogue))
	{
		%this.setDatablock(%data);
	}
	%this.blinded = false;
	if(%this.getMountedImage(1) == nameToID(DarkBlindPlayerImage))
	{
		%this.unmountImage(1);
		return true;
	}
	return false;
}

function Player::BlindDRDec(%this)
{
	%this.BlindDR--;
}

//stun
function Player::Stun(%this, %dur, %dr)
{
	if(%this.getClassName() $= "AIplayer")
	{
	}
	else if(%this.StunDR <= 3)
	{
		if(%dr)
		{
			%dur/= mClamp(%this.StunDR + 1, 1, 4);
			%this.StunDR++;
			%this.schedule(15000 + %dur, StunDRDec);
		}
		%this.stunned = true;
		if(isEventPending(%this.unstunSched))
		{
			cancel(%this.unstunSched);
		}
		%this.unstunSched = %this.schedule(%dur, Unstun);
		if(isEventPending(%client.stunSched))
		{
			cancel(%client.stunSched);
		}
		StunSched(%this.client, %this.unstunSched);
	}
}

function StunSched(%client, %sched)
{
	%dur = getTimeRemaining(%sched) / 1000;
	if(!isObject(%client))
	{
		return;
	}
	if(!isObject(%obj = %client.player) || !%obj.stunned)
	{
		cancel(%sched);
		return;
	}
	if(%client.getControlObject() != (%cam = %client.camera))
	{
		%client.setControlObject(%cam);
		%cam.setMode("Corpse", %obj);
	}
	commandToClient(%client, 'centerPrint', "<color:FF0000>Stunned! " @ mFloatLength(%dur, 1), 0.2);
	if(%dur > 0.1)
	{
		%client.stunSched = schedule(100, 0, StunSched, %client, %sched);
	}
}

function Player::StunDRDec(%this)
{
	%this.StunDR--;
}

function Player::Unstun(%this)
{
	cancel(%this.unstunSched);
	if(isObject(%client = %this.client) && %client.getControlObject() == %client.camera)
	{
		%client.setControlObject(%this);
	}
	%this.stunned = false;
}

//slows / haste
function Player::RecalculateSpeed(%this)
{
	%armor = %this.getdatablock();
	%fspeed = %armor.maxForwardSpeed;
	%sspeed = %armor.maxSideSpeed;
	%bspeed = %armor.maxBackwardSpeed;
	%rec = %this.speedFactors;
	for(%i = 0; %i < getWordCount(%rec); %i++)
	{
		%fspeed*= getWord(%rec, %i);
		%sspeed*= getWord(%rec, %i);
		%bspeed*= getWord(%rec, %i);
	}
	%this.setMaxForwardSpeed(%fspeed);
	%this.setMaxSideSpeed(%sspeed);
	%this.setMaxBackwardSpeed(%bspeed);
}

function Player::AddSpeedFactor(%this, %amt, %dur)
{
	%this.speedFactors = trim(%this.speedFactors SPC %amt);
	if(%dur != 0)
	{
		%this.schedule(%dur, EndSpeedFactor, %amt);
	}
	%this.RecalculateSpeed();
}

function Player::EndSpeedFactor(%this, %amt)
{
	%speedFactors = %this.speedFactors;
	for(%i = 0; %i < getWordCount(%speedFactors); %i++)
	{
		if(getWord(%speedFactors, %i) $= %amt)
		{
			%this.speedFactors = removeWord(%speedFactors, %i);
			break;
		}
	}
	%this.recalculateSpeed();
}

function Player::Slow(%this, %amt, %dur)
{
	%this.addSpeedFactor(1 - %amt, %dur);
}<file_sep>//ReverieFortWars/accounts.cs

//TABLE OF CONTENTS
// #1. documentation
// #2. item system functionality
//   #2.1 readability/helper functions
// #3. item enumeration

// #1.
//////////////////
// ITEM STRINGS //
//////////////////
// Items are stored as strings. Data stored in these strings are seperated by tabs - each datum occupies a field.
// Field 0: Item type (an int, the item's type index, see following item data documentation)
// Field 1: Item kills (for weapons) / quantity (for stackable items) / 0 (for all other items)
// Field 2: Item name (for named weapons) / 0 (for all other items)
///////////////
// ITEM DATA //
///////////////
// $RFW::ItemDataCount      | int count of the number of defined item data
// $RFW::Item[i]            | string name of the ith item data
// $RFW::ItemWearable[str]  | bool whether the item named str can be worn (eg as armor)
// $RFW::ItemSlot[str]      | string the slot the item goes into, if wearable (eg armor, back...)
// $RFW::ItemEffect[str]    | string eval'd when the item is used from inventory (unwearable, unequipable items only)
// $RFW::ItemEquipable[str] | bool whether the item named str can be equipped (eg as a weapon)
// $RFW::ItemDatablock[str] | datablock (itemData) the item datablock corresponding to the item named str
// $RFW::ItemDrops[str]     | bool whether the item can drop on death
// $RFW::Item[str]          | int the index of the item datum with the provided name


// #2.

// #2.1
function getItemType(%str)
{
	return getField(%str, 0);
}

function getItemQuantity(%str)
{
	return getField(%str, 1);
}
function setItemQuantity(%str, %amt)
{
	return setField(%str, 1, %amt);
}
function incItemQuantity(%str, %amt)
{
	return setField(%str, 1, mFloatLength(getField(%str, 1) + %amt, 2));
}

function getItemKills(%str)
{
	return getField(%str, 1);
}
function setItemKills(%str, %amt)
{
	return setField(%str, 1, %amt);
}
function incItemKills(%str, %amt)
{
	return setField(%str, 1, mFloatLength(getField(%str, 1) + %amt, 0));
}

function getItemName(%str)
{
	return getField(%str, 2);
}
function setItemName(%str, %newName)
{
	return setField(%str, 2, %newName);
}

//
$RFW::ItemDataCount = 0;

//registers an item
function RFW_RegisterItem(%name, %stackable, %wearable, %equipable, %datablock, %slot, %canDrop)
{
	%x = $RFW::ItemDataCount++;
	$RFW::Item[%x] = %name;
	$RFW::ItemStackable[%name] = %stackable;
	$RFW::ItemWearable[%name] = %wearable;
	$RFW::ItemEquipable[%name] = %equipable;
	if(!%wearable && !%equipable && strLen(%slot))
	{
		$RFW::ItemEffect[%name] = %slot;
	}
	else
	{
		$RFW::ItemSlot[%name] = %slot;
	}
	$RFW::ItemDrops[%name] = %canDrop;
	if(%datablock !$= "0")
	{
		$RFW::ItemDatablock[%name] = %datablock.getID();
	}
	$RFW::Item[%name] = %x;
}

// #3.

RFW_RegisterItem("Fists", false, false, true, FistsItem, "", false);
//materials
RFW_RegisterItem("Dirt", true, false, false, 0, "", true);
RFW_RegisterItem("Wood", true, false, false, 0, "", true);
RFW_RegisterItem("Sand", true, false, false, 0, "", true);
RFW_RegisterItem("Stone", true, false, false, 0, "", true);
RFW_RegisterItem("Plant matter", true, false, false, 0, "", true);
RFW_RegisterItem("Snow", true, false, false, 0, "", true);
RFW_RegisterItem("Cloth", true, false, false, 0, "", true);
RFW_RegisterItem("Paper", true, false, false, 0, "", true);
RFW_RegisterItem("Leather", true, false, false, 0, "", true);
RFW_RegisterItem("Obsidian", true, false, false, 0, "", true);
RFW_RegisterItem("Glass", true, false, false, 0, "", true);
RFW_RegisterItem("Bone", true, false, false, 0, "", true);
RFW_RegisterItem("Blood", true, false, false, 0, "", true);
RFW_RegisterItem("Flesh", true, false, false, 0, "", true);
RFW_RegisterItem("Faerie dust", true, false, false, 0, "", true);
RFW_RegisterItem("Surtra", true, false, false, 0, "", true);
RFW_RegisterItem("Calcite", true, false, false, 0, "", true);
RFW_RegisterItem("Nithlite", true, false, false, 0, "", true);
RFW_RegisterItem("Dasnia", true, false, false, 0, "", true);
RFW_RegisterItem("Sanguite", true, false, false, 0, "", true);
RFW_RegisterItem("Sinnite", true, false, false, 0, "", true);
RFW_RegisterItem("Crystal", true, false, false, 0, "", true);
//tools
RFW_RegisterItem("Wooden crafting tools", false, false, false, 0, "", false);
RFW_RegisterItem("Faerie crafting tools", false, false, false, 0, "", false);
RFW_RegisterItem("Faerie wand", false, false, true, FaerieWandItem, "", true);
RFW_RegisterItem("Stone crafting tools", false, false, false, 0, "", false);
RFW_RegisterItem("Obsidian crafting tools", false, false, false, 0, "", false);
RFW_RegisterItem("Surtra crafting tools", false, false, false, 0, "", false);
RFW_RegisterItem("Calcite crafting tools", false, false, false, 0, "", false);
RFW_RegisterItem("Nithlite crafting tools", false, false, false, 0, "", false);
RFW_RegisterItem("Sanguite crafting tools", false, false, false, 0, "", false);
RFW_RegisterItem("Sinnite crafting tools", false, false, false, 0, "", false);
RFW_RegisterItem("Pyromancy wand", false, false, true, PyroWandItem, "", true);
RFW_RegisterItem("Alchemy vials", false, false, false, 0, "", true);
RFW_RegisterItem("Photometallisynthesis lens", false, false, true, PhotoLensItem, "", true);
RFW_RegisterItem("Ossimetallisynthesis staff", false, false, true, OssiStaffItem, "", true);
RFW_RegisterItem("Sanguimetallisynthesis wand", false, false, true, SanguiWandItem, "", true);
//equipment
RFW_RegisterItem("Snowball", true, false, true, SnowballItem, "", true);

RFW_RegisterItem("Wooden sword", false, false, true, WoodenSwordItem, "", true);
RFW_RegisterItem("Wooden bow", false, false, true, WoodenBowItem, "", true);
RFW_RegisterItem("Wooden quiver", false, true, false, 0, "Back", true);
RFW_RegisterItem("Wooden armor", false, true, false, 0, "Armor", true);

RFW_RegisterItem("Bone sword", false, false, true, BoneSwordItem, "", true);
RFW_RegisterItem("Bone bow", false, false, true, BoneBowItem, "", true);
RFW_RegisterItem("Bone quiver", false, true, false, 0, "Back", true);
RFW_RegisterItem("Bone armor", false, true, false, 0, "Armor", true);

RFW_RegisterItem("Faerie bow", false, false, true, FaerieBowItem, "", true);
RFW_RegisterItem("Faerie armor", false, true, false, 0, "Armor", true);

RFW_RegisterItem("Stone sword", false, false, true, StoneSwordItem, "", true);
RFW_RegisterItem("Stone quiver", false, true, false, 0, "Back", true);
RFW_RegisterItem("Stone pickaxe", false, false, true, StonePickaxeItem, "", true);
RFW_RegisterItem("Stone armor", false, true, false, 0, "Armor", true);

RFW_RegisterItem("Obsidian sword", false, false, true, macahuitlItem, "", true);
RFW_RegisterItem("Obsidian dagger", false, false, true, ObsidianDaggerItem, "", true);
RFW_RegisterItem("Obsidian quiver", false, true, false, 0, "Back", true);

RFW_RegisterItem("Surtra sword", false, false, true, SurtraSwordItem, "", true);
RFW_RegisterItem("Surtra dagger", false, false, true, SurtraDaggerItem, "", true);
RFW_RegisterItem("Surtra pickaxe", false, false, true, SurtraPickaxeItem, "", true);
RFW_RegisterItem("Surtra quiver", false, true, false, 0, "Back", true);
RFW_RegisterItem("Surtra armor", false, true, false, 0, "Armor", true);

RFW_RegisterItem("Calcite sword", false, false, true, CalciteSwordItem, "", true);
RFW_RegisterItem("Calcite dagger", false, false, true, CalciteDaggerItem, "", true);
RFW_RegisterItem("Calcite pickaxe", false, false, true, CalcitePickaxeItem, "", true);
RFW_RegisterItem("Calcite quiver", false, true, false, 0, "Back", true);
RFW_RegisterItem("Calcite armor", false, true, false, 0, "Armor", true);

RFW_RegisterItem("Nithlite sword", false, false, true, NithliteSwordItem, "", true);
RFW_RegisterItem("Nithlite dagger", false, false, true, NithliteDaggerItem, "", true);
RFW_RegisterItem("Nithlite pickaxe", false, false, true, NithlitePickaxeItem, "", true);
RFW_RegisterItem("Nithlite quiver", false, true, false, 0, "Back", true);
RFW_RegisterItem("Nithlite armor", false, true, false, 0, "Armor", true);

RFW_RegisterItem("Sanguite sword", false, false, true, SanguiteSwordItem, "", true);
RFW_RegisterItem("Sanguite dagger", false, false, true, SanguiteDaggerItem, "", true);
RFW_RegisterItem("Sanguite pickaxe", false, false, true, SanguitePickaxeItem, "", true);
RFW_RegisterItem("Sanguite quiver", false, true, false, 0, "Back", true);
RFW_RegisterItem("Sanguite armor", false, true, false, 0, "Armor", true);

RFW_RegisterItem("Sinnite sword", false, false, true, SinniteSwordItem, "", true);
RFW_RegisterItem("Sinnite dagger", false, false, true, SinniteDaggerItem, "", true);
RFW_RegisterItem("Sinnite pickaxe", false, false, true, SinnitePickaxeItem, "", true);
RFW_RegisterItem("Sinnite quiver", false, true, false, 0, "Back", true);
RFW_RegisterItem("Sinnite armor", false, true, false, 0, "Armor", true);

RFW_RegisterItem("Crystal sword", false, false, true, CrystalSwordItem, "", true);
RFW_RegisterItem("Crystal dagger", false, false, true, CrystalDaggerItem, "", true);
RFW_RegisterItem("Crystal pickaxe", false, false, true, CrystalPickaxeItem, "", true);
RFW_RegisterItem("Crystal quiver", false, true, false, 0, "Back", true);
RFW_RegisterItem("Crystal armor", false, true, false, 0, "Armor", true);
RFW_RegisterItem("Crystal bow", false, false, true, CrystalBowItem, "", true);
//clothes
RFW_RegisterItem("Cloth shirt", false, true, false, 0, "Shirt", true);
RFW_RegisterItem("Cloth pants", false, true, false, 0, "Pants", true);
RFW_RegisterItem("Cloth hat", false, true, false, 0, "Hat", true);
RFW_RegisterItem("Cloth cape", false, true, false, 0, "Back", true);
RFW_RegisterItem("Cloth robe", false, true, false, 0, "Shirt", true);
RFW_RegisterItem("Leather boots", false, true, false, 0, "Feet", true);
RFW_RegisterItem("Leather gloves", false, true, false, 0, "Hands", true);
//medicines and potions
RFW_RegisterItem("Cloth bandage", true, false, false, 0, "%player.bandage();", true);
RFW_RegisterItem("Pishelva", true, false, false, 0, "%player.pishelva();", true);
RFW_RegisterItem("Hoborchakin", true, false, false, 0, "%player.hoborchakin();", true);
RFW_RegisterItem("Vorpal potion", true, false, false, 0, "%player.vorpalPotion();", true);
RFW_RegisterItem("Fleshweaver's potion", true, false, false, 0, "%player.fleshweaverPotion();", true);
RFW_RegisterItem("Crimson potion", true, false, true, 0, "", true);

//potion functionality
function Player::Bandage(%this)
{
	%time = getSimTime();
	if(%this.bandageTime + 60000 < %time)
	{
		%client = %this.client;
		messageClient(%client, 'MsgError');
		%client.centerPrintQueue("<color:FF0000>Can't use that yet.", 3);
		return;
	}
	%this.client.account.removeItem("Cloth bandage\t1\t", true);
	%this.bandageTime = %time;
	for(%i = 0; %i < 5; %i++)
	{
		%this.schedule(%i * 3000, addHealth, 10);
	}
}

function Player::Hoborchakin(%this)
{
	%this.client.account.removeItem("Hoborchakin\t1\t", true);
	cancel(%this.scaleSched);
	%time = getSimTime();
	%scale = %this.getScale();
	%this.startScale = %scale;
	%this.growVec = vectorSub("1.5 1.5 1.5", %scale);
	%this.hoborchakinTime = %time;
	%this.hoborchakin1Time = %time + 5000;
	%this.hoborchakin2Time = %time + 305000;
	%this.hoborchakin1();
}

function Player::Hoborchakin1(%this)
{
	cancel(%this.scaleSched);
	if(!isObject(%this) || %this.getState() $= "Dead")
	{
		return;
	}
	%time = getSimTime();
	%endTime = %this.hoborchakin1Time;
	if(%time > %endTime)
	{
		%this.setScale("1.5 1.5 1.5");
		%this.growVec = "-0.5 -0.5 -0.5";
		%this.scaleSched = %this.schedule(50, Hoborchakin2);
		return;
	}
	%timePct = 1 - (%endTime - %time) / 5000;
	%scale = vectorAdd(%this.startScale, vectorScale(%this.growVec, %timePct));
	%this.setScale(%scale);
	%this.scaleSched = %this.schedule(50, Hoborchakin1);
}

function Player::Hoborchakin2(%this)
{
	cancel(%this.scaleSched);
	if(!isObject(%this) || %this.getState() $= "Dead")
	{
		return;
	}
	%time = getSimTime();
	%endTime = %this.hoborchakin2Time;
	if(%time > %endTime)
	{
		%this.setScale("1 1 1");
		return;
	}
	%timePct = 1 - (%endTime - %time) / 300000;
	%scale = vectorAdd("1.5 1.5 1.5", vectorScale("-0.5 -0.5 -0.5", %timePct));
	%this.setScale(%scale);
	%this.scaleSched = %this.schedule(50, Hoborchakin2);
}

function Player::Pishelva(%this)
{
	%this.client.account.removeItem("Pishelva\t1\t", true);
	cancel(%this.scaleSched);
	%time = getSimTime();
	%scale = %this.getScale();
	%this.startScale = %scale;
	%this.growVec = vectorSub("0.5 0.5 0.5", %scale);
	%this.PishelvaTime = %time;
	%this.Pishelva1Time = %time + 5000;
	%this.Pishelva2Time = %time + 305000;
	%this.Pishelva1();
}

function Player::Pishelva1(%this)
{
	cancel(%this.scaleSched);
	if(!isObject(%this) || %this.getState() $= "Dead")
	{
		return;
	}
	%time = getSimTime();
	%endTime = %this.Pishelva1Time;
	if(%time > %endTime)
	{
		%this.setScale("0.5 0.5 0.5");
		%this.growVec = "0.5 0.5 0.5";
		%this.scaleSched = %this.schedule(50, Pishelva2);
		return;
	}
	%timePct = 1 - (%endTime - %time) / 5000;
	%scale = vectorAdd(%this.startScale, vectorScale(%this.growVec, %timePct));
	%this.setScale(%scale);
	%this.scaleSched = %this.schedule(50, Pishelva1);
}

function Player::Pishelva2(%this)
{
	cancel(%this.scaleSched);
	if(!isObject(%this) || %this.getState() $= "Dead")
	{
		return;
	}
	%time = getSimTime();
	%endTime = %this.Pishelva2Time;
	if(%time > %endTime)
	{
		%this.setScale("1 1 1");
		return;
	}
	%timePct = 1 - (%endTime - %time) / 305000;
	%scale = vectorAdd("0.5 0.5 0.5", vectorScale("0.5 0.5 0.5", %timePct));
	%this.setScale(%scale);
	%this.scaleSched = %this.schedule(50, Pishelva2);
}<file_sep>//ReverieFortWars/appearance.cs

//TABLE OF CONTENTS

$RFW::Skin[0] = "1 0.9 0.6 1"; //peach
$RFW::Skin[1] = "0.4 0.2 0 1"; //brown
$RFW::Skin[2] = "0.9 0.9 0 1"; //yellow
$RFW::Skin[3] = "1 0.9 0.8 1"; //light peach
$RFW::SkinColors = 4;
$RFW::SkinMaxDist = 0.15;

$RFW::Pants[0] = "0.4 0.2 0 1"; //brown
$RFW::Pants[1] = "0 0.5 0.25 1"; //green
$RFW::PantsColors = 2;
$RFW::PantsMaxDist = 0.15;

package RFW_Appearance
{
	function GameConnection::ApplyBodyColors(%client)
	{
		parent::ApplyBodyColors(%client);
		if(!isObject(%obj = %client.player))
		{
			return;
		}
		%acc = %client.account;
		%skin = %client.headColor;
		for(%i = 0; %i < $RFW::SkinColors; %i++)
		{
			if(vectorDist(%skin, $RFW::Skin[%i]) <= $RFW::SkinMaxDist)
			{
				%v = true;
				break;
			}
		}
		if(!%v)
		{
			%skin = $RFW::Skin[0];
		}
		%obj.setNodeColor("headSkin", %skin);
		if(%acc.read("Shirt") $= "")
		{
			%obj.setNodeColor($Chest[%client.chest], %skin);
			%obj.setNodeColor($LArm[%client.lArm], %skin);
			%obj.setNodeColor($RArm[%client.rArm], %skin);
			%obj.setDecalName("AAA-None");
		}
		else
		{
			%shirt = %client.chestColor !$= %skin ? %client.chestColor : "0 0.5 0.25 1";
			%obj.setNodeColor($Chest[%client.chest], %shirt);
			if(%client.lArmColor $= %client.chestColor)
			{
				%obj.setNodeColor($LArm[%client.lArm], %shirt);
			}
			else
			{
				%obj.setNodeColor($LArm[%client.lArm], %skin);
			}
			if(%client.rArmColor $= %client.chestColor)
			{
				%obj.setNodeColor($RArm[%client.rArm], %shirt);
			}
			else
			{
				%obj.setNodeColor($RArm[%client.rArm], %skin);
			}
		}
		%back = %acc.read("Back");
		if(getWord(getItemType(%back), 1) $= "quiver")
		{
			%obj.setNodeColor("Quiver", getColorIdTable($RFW::ResourceColor[firstWord(%back)]));
		}
		else if(getWord(getItemType(%back), 1) $= "cape" && %client.pack != 3)
		{
			%obj.setNodeColor("Cape", $RFW::TribeColor[%acc.read("Tribe")] SPC 1);
		}
		if(%acc.read("Hands") $= "")
		{
			if(%client.rHand != 1)
			{
				%obj.setNodeColor($RHand[%client.rHand], %skin);
			}
			else
			{
				%obj.setNodeColor($RHand[%client.rHand], getColorIdTable($RFW::ResourceColor["Surtra"]));
			}
			if(%client.lHand != 1)
			{
				%obj.setNodeColor($LHand[%client.lHand], %skin);
			}
			else
			{
				%obj.setNodeColor($LHand[%client.lHand], getColorIdTable($RFW::ResourceColor["Surtra"]));
			}
		}
		if(%acc.read("Feet") $= "")
		{
			if(%client.rLeg != 1)
			{
				%obj.setNodeColor($RLeg[%client.rLeg], %skin);
			}
			else
			{
				%obj.setNodeColor($RLeg[%client.rLeg], getColorIdTable($RFW::ResourceColor["Wood"]));
			}
			if(%client.lLeg != 1)
			{
				%obj.setNodeColor($LLeg[%client.lLeg], %skin);
			}
			else
			{
				%obj.setNodeColor($LLeg[%client.lLeg], getColorIdTable($RFW::ResourceColor["Wood"]));
			}
		}
		%obj.applyPants(%skin);
		%obj.schedule(0, applyArmor, %client.account.read("Armor"));
	}

	function GameConnection::ApplyBodyParts(%client)
	{
		parent::ApplyBodyParts(%client);
		if(!isObject(%obj = %client.player))
		{
			return;
		}
		%acc = %client.account;
		if(%acc.read("Hat") $= "" || %client.hat < 4)
		{
			%obj.hideNode($Hat[%client.hat]);
			%obj.hideNode(getWord($AccentsAllowed[$Hat[%client.hat]], %client.accent));
		}
		%obj.hideNode($Pack[%client.pack]);
		%obj.hideNode($SecondPack[%client.secondPack]);
		%back = %acc.read("Back");
		if(getWord(getItemType(%back), 1) $= "quiver")
		{
			%obj.unhideNode("Quiver");
		}
		else if(getWord(getItemType(%back), 1) $= "cape")
		{
			%obj.unhideNode("Cape");
		}
		if(getWord(getItemType(%acc.read("Shirt")), 1) $= "Robe" && %client.hip == 0)
		{
			%obj.hideNode("pants");
			%obj.hideNode("lShoe");
			%obj.hideNode("rShoe");
			%obj.unhideNode("skirtHip");
			%obj.unhideNode("skirtTrimRight");
			%obj.unhideNode("skirtTrimLeft");
		}
		else if(%client.hip == 1)
		{
			%obj.hideNode("skirtHip");
			%obj.hideNode("skirtTrimRight");
			%obj.hideNode("skirtTrimLeft");
			%obj.unhideNode("pants");
			%obj.unhideNode("lShoe");
			%obj.unhideNode("rShoe");
		}
	}
};
activatePackage(RFW_Appearance);

function Player::ApplyArmor(%obj, %armor)
{
	%obj.damageReduction = 1;
	%obj.speedFactors = "";
	if(%armor $= "")
	{
		%obj.AddSpeedFactor(1, 0);
		return;
	}
	switch$(%armor)
	{
		case "Wooden armor":
			%color = getColorIDtable($RFW::ResourceColor["Wood"]);
			%speed = 0.9;
			%dmg = 100 / 120;
			%mana = 100 / 100;
		case "Faerie armor":
			%color = "0 0.7 0.3 1";
			%speed = 1.1;
			%dmg = 100 / 170;
			%mana = 100 / 125;
		case "Bone armor":
			%color = getColorIDtable($RFW::ResourceColor["Bone"]);
			%speed = 0.9;
			%dmg = 100 / 140;
			%mana = 100 / 110;
		case "Stone armor":
			%color = getColorIDtable($RFW::ResourceColor["Stone"]);
			%speed = 0.8;
			%dmg = 100 / 170;
			%mana = 100 / 100;
		case "Surtra armor":
			%color = getColorIDtable($RFW::ResourceColor["Surtra"]);
			%speed = 0.9;
			%dmg = 100 / 160;
			%mana = 100 / 100;
		case "Calcite armor":
			%color = getColorIDtable($RFW::ResourceColor["Calcite"]);
			%speed = 1;
			%dmg = 100 / 180;
			%mana = 100 / 100;
		case "Nithlite armor":
			%color = getColorIDtable($RFW::ResourceColor["Nithlite"]);
			%speed = 1;
			%dmg = 100 / 180;
			%mana = 100 / 110;
		case "Sanguite armor":
			%color = getColorIDtable($RFW::ResourceColor["Sanguite"]);
			%speed = 1.1;
			%dmg = 100 / 200;
			%mana = 100 / 100;
		case "Sinnite armor":
			%color = getColorIDtable($RFW::ResourceColor["Sinnite"]);
			%speed = 1.1;
			%dmg = 100 / 200;
			%mana = 100 / 125;
		case "Crystal armor":
			%color = getColorIDtable($RFW::ResourceColor["Crystal"]);
			%speed = 1.2;
			%dmg = 100 / 250;
			%mana = 100 / 150;
	}
	%obj.AddSpeedFactor(%speed, 0);
	%obj.damageReduction = %dmg;
	%obj.manaReduction = %mana;
	%obj.setHeadUp(true);
	%obj.unhideNode("armor");
	%obj.setNodeColor("armor", %color);
	%obj.unhideNode("shoulderPads");
	%obj.setNodeColor("shoulderPads", %color);
	%obj.setNodeColor("pants", setWord(%color, 3, 1));
	%obj.setNodeColor("LShoe", setWord(%color, 3, 1));
	%obj.setNodeColor("RShoe", setWord(%color, 3, 1));
	if(isObject(%client = %obj.client))
	{
		if(%client.hat > 0 && %client.hat < 4)
		{
			%obj.unhideNode($Hat[%client.hat]);
			%obj.setNodeColor($Hat[%client.hat], %color);
		}
		if(%client.LHandColor !$= %client.headColor)
		{
			%obj.setNodeColor("LHand", setWord(%color, 3, 1));
		}
		if(%client.RHandColor !$= %client.headColor)
		{
			%obj.setNodeColor("RHand", setWord(%color, 3, 1));
		}
	}
}

function Player::ApplyShirt(%obj, %shirt)
{

}

function Player::ApplyPants(%obj, %skin)
{
	%client = %obj.client;
	%pants = %client.hipColor;
	%acc = %client.account;
	if(getWord(getItemType(%acc.read("Shirt")), 1) $= "Robe" && %client.hip == 0)
	{
		%obj.setNodeColor("skirtHip", %client.chestColor);
		%obj.setNodeColor("skirtTrimRight", %client.chestColor);
		%obj.setNodeColor("skirtTrimLeft", $RFW::TribeColor[%acc.read("Tribe")] SPC 1);
	}
	else if(%acc.read("Pants") $= "")
	{
		%v = false;
		for(%j = 0; %j < $RFW::PantsColors; %j++)
		{
			if(vectorDist(%pants, $RFW::Pants[%j]) <= $RFW::PantsMaxDist)
			{
				%v = true;
				break;
			}
		}
		if(!%v)
		{
			%pants = $RFW::Pants[0];
		}
		if(%pants $= %skin)
		{
			%pants = $RFW::Pants[1];
		}
		%obj.setNodeColor($Hip[0], %pants);
	}
	else
	{
		if(%pants $= %skin)
		{
			%pants = $RFW::Pants[2];
		}
		%obj.setNodeColor($Hip[0], %pants);
	}
}

function Player::ApplyHat(%obj, %hat)
{

}

function Player::ApplyBack(%obj, %back)
{

}<file_sep>for(%file = findFirstFile("Add-Ons/GameMode_ReverieFortWars/sounds/footsteps/*.wav"); isFile(%file); %file = findNextFile("Add-Ons/GameMode_ReverieFortWars/sounds/footsteps/*.wav"))
{
	%name = fileBase(%file);
	eval("datablock AudioProfile(" @ %name @ "){ filename = \"" @ %file @ "\"; description = AudioClose3d; preload = true; };");
}

$StepSounds["Carpet"] = 5;
$StepSounds["Dirt"] = 6;
$StepSounds["Grass"] = 7;
$StepSounds["JabuJabu"] = 7;
$StepSounds["Sand"] = 5;
$StepSounds["Stone"] = 6;
$StepSounds["Wood"] = 5;

function Player::GetFootstepType(%obj, %col)
{
	if(%col.getClassName() $= "fxDtsBrick")
	{
		%type = $RFW::ResourceByColor[%col.colorID];
		switch$(%type)
		{
			case "Plant matter": %type = "Grass";
			case "Snow": %type = "Sand";
			case "Flesh": %type = "JabuJabu";
			case "Surtra": %type = "Stone";
			case "Calcite": %type = "Stone";
			case "Nithlite": %type = "Stone";
			case "Sanguite": %type = "Stone";
			case "Sinnite": %type = "Stone";
			case "Crystal": %type = "Stone";
			case "Glass": %type = "Stone";
			case "Cloth": %type = "Carpet";
		}
		if(!$StepSounds[%type])
		{
			%type = "Dirt";
		}
	}
	else
	{
		%type = "Dirt";
	}
	return %type;
}

//! The package to handle when the player walks, and then play a noise.

package peggsteps
{
	//## When any creature spawns, start peggstepping.
	function Armor::onNewDatablock(%this, %obj)
	{
		if(%this.shapeFile !$= "base/data/shapes/player/m.dts" || %this.uiName $= "Faerie")
		{
			return parent::onNewDatablock(%this, %obj);
		}
		%obj.isSlow = 0;
		%obj.peggStep = schedule(50, 0, PeggFootsteps, %obj);
		%obj.peggJumpSounds = schedule(50, 0, PeggJumpSounds, %obj);
		return parent::onNewDatablock(%this, %obj);
	}

	function Armor::onTrigger(%data,%obj,%slot,%val)
	{
		if(%slot == 3)
		{
			%obj.isProning = %val;
		}
		return Parent::onTrigger(%data,%obj,%slot,%val);
	}

	function Armor::onEnterLiquid(%data, %obj)
	{
		%obj.isSwimming = true;
		return Parent::onEnterLiquid(%data, %obj);
	}
	function Armor::onLeaveLiquid(%data, %obj)
	{
		%obj.isSwimming = false;
		return Parent::onLeaveLiquid(%data,%obj);
	}
};
activatepackage(peggsteps);

//## Drop some rad peggstep noise in here!
function PeggFootsteps(%obj)
{
	if(isObject(%obj))
	{
		%schedlen = 320;
		%start = %obj.getPosition();
		%end = vectorAdd(%start, "0 0 -0.1");
		%typemasks = $Typemasks::FxBrickAlwaysObjectType | $Typemasks::TerrainObjectType | $Typemasks::PlayerObjectType;
		%ray = containerRaycast(%start, %end, %typemasks, %obj);
		%col = firstWord(%ray);
		if(isObject(%col))
		{	
			if(%col.getClassName() $= "fxDTSbrick")
			{
				%obj.touchcolor = %col.getColorId();
			}
			%vel = %obj.getVelocity();
			if(vectorLen(setWord(%vel, 2, 0)) > 0)
			{
				%type = %obj.getFootstepType(%col);
				if(!%obj.isProning)
				{
					%sound = "OOT_Steps_" @ %type @ getRandom(1, $StepSounds[%type]);
					serverplay3d(%sound, %obj.getHackPosition());
				}
				else
				{
					%time = getSimTime();
					if(%obj.crawltime + 850 < %time)
					{
						%sound = "OOT_Steps_PushCrawl_" @ %type;
						serverplay3d(%sound, %obj.getPosition());
						%obj.crawlTime = %time;
					}
				}
			}
		}
		%obj.peggstep = schedule(320, 0, PeggFootsteps, %obj);
	}
}

function PeggJumpSounds(%obj)
{
	if(!isObject(%obj))
	{
		return;
	}
	//%obj.tbp();
	cancel(%obj.peggJumpSounds);
	%pvel = %obj.prevVelocity;
	%cvel = %obj.getVelocity();
	%obj.prevVelocity = %cvel;
	%pz = getWord(%pvel, 2);
	%cz = getWord(%cvel, 2);
	if(%cz > 8 && %pz < 1)
	{
		//jumping
		%start = %obj.getPosition();
		%end = vectorAdd(%start, "0 0 -1.5");
		%typemasks = $Typemasks::FxBrickAlwaysObjectType | $Typemasks::TerrainObjectType | $Typemasks::PlayerObjectType;
		%ray = containerRaycast(%start, %end, %typemasks, %obj);
		%col = firstWord(%ray);
		if(isObject(%col))
		{
			%type = %obj.getFootstepType(%col);
			%sound = "OOT_Steps_" @ %type @ "_Jump" @ getRandom(1, 2);
			serverplay3d(%sound, %obj.getHackPosition());
		}
	}
	else if(%cz > -1 && %pz < -4)
	{
		//landing
		%start = %obj.getPosition();
		%end = vectorAdd(%start, "0 0 -0.1");
		%typemasks = $Typemasks::FxBrickAlwaysObjectType | $Typemasks::TerrainObjectType | $Typemasks::PlayerObjectType;
		%ray = containerRaycast(%start, %end, %typemasks, %obj);
		%col = firstWord(%ray);
		if(isObject(%col))
		{
			%type = %obj.getFootstepType(%col);
			%sound = "OOT_Steps_" @ %type @ "_Land" @ getRandom(1, 2);
			serverplay3d(%sound, %obj.getHackPosition());
		}
	}
	%obj.peggJumpSounds = schedule(50, 0, PeggJumpSounds, %obj);
}<file_sep>Reverie Fort Wars combines elements from Fort Wars and Fantasy RPG servers to create a team-oriented player-versus-player survival game.

Credits:
Amade (scripting, sword models), Tingalz (pickaxe models), Phydeoux (ribcage model)

Requirements:
Execute: Brick_Large_Cubes, Vehicle_AdvancedHorse, AI_Mutalid<file_sep>//ReverieFortWars/notice.cs

//TABLE OF CONTENTS
//#1. functions
//	#1.1 noticeTick
//#2. registrations
//	#2.1 notices

//#1.
//	#1.1
function noticeTick(%delay)
{
	cancel($noticeTick);
	while((%r = getRandom(1, $noticeCount)) == $lastNotice)
	{
	}
	messageAll('', "\c7bot\c4TipsBot\c6: " @ $Notice[%r]);
	$lastNotice = %r;
	$noticetick = schedule(%delay, 0, noticeTick, %delay);
}
schedule(90000, 0, noticeTick, 90000);

//#2.
//	#2.1
$NoticeCount = 0;
$Notice[$NoticeCount++] = "You can use your building controls to manipulate the menu. (\c5/help menu\c6)";
$Notice[$NoticeCount++] = "Faeries are dangerous, but killing them can allow you to collect valuable faerie dust.";
$Notice[$NoticeCount++] = "Sanguite is most often found underground in a flesh biome.";
$Notice[$NoticeCount++] = "Calcite is most often found underground in a flesh biome.";
$Notice[$NoticeCount++] = "Surtra is most often found underground in a mountain biome.";
$Notice[$NoticeCount++] = "Mountains (especially underground) are the only place you can find Obsidian.";
$Notice[$NoticeCount++] = "Sanguite can be transmuted from blood if you have a sanguimetallisynthesis wand.";
$Notice[$NoticeCount++] = "Calcite can be transmuted from bone if you have an ossimetallisynthesis totem.";
$Notice[$NoticeCount++] = "Check out the <a:forum.blockland.us/index.php?topic=281292.0>forum thread</a>\c6! Be sure to vote in the poll.";
$Notice[$NoticeCount++] = "The Malka tribe is a blood cult that worships Shessim, the Lord of Slaughter.";
$Notice[$NoticeCount++] = "The Prelkan tribe is a flesh cult that worships the Fabric, an entity binding all things together.";
$Notice[$NoticeCount++] = "The Kazezi tribe of lightweavers worship Naramata, the god of light and life.";
$Notice[$NoticeCount++] = "The forest-dwelling Faeborn tribe worships faeries and other woodland spirits.";
$Notice[$NoticeCount++] = "The desert-dwelling Sarti tribe serves the Darkins, a malicious, shadowy race.";
$Notice[$NoticeCount++] = "The deep-dwelling Eristia tribe worships kobolds and other earth spirits.";
$Notice[$NoticeCount++] = "The Munaa tribe is a fire cult that worships the mountain-dwelling Giants.";
$Notice[$NoticeCount++] = "The Mediir tribe is a death cult that worships Rimuto, god of death and bones.";
$Notice[$NoticeCount++] = "You can loot a corpse by activating it.";
$Notice[$NoticeCount++] = "You can butcher a corpse for bone, flesh, and blood by attacking it with a melee weapon.";
$Notice[$NoticeCount++] = "When you die, you lose up to 3 random items (or 20% of a stack of an item) from your inventory.";
$Notice[$NoticeCount++] = "You can place a spawn point brick to set the spot at which you will return to life.";
$Notice[$NoticeCount++] = "You can place a totem brick to claim an area, protecting your build. (\c5/help totems\c6)";
$Notice[$NoticeCount++] = "You can unequip an item with the \c5/unequip item name\c6 command.";
$Notice[$NoticeCount++] = "You can switch tribes with \c5/switchTribe tribe\c6 command, but will lose all of your items and bricks.";
$Notice[$NoticeCount++] = "When mining, remember that ores spawn in clusters.";
$Notice[$NoticeCount++] = "The Faerie Wand allows you to add lights and emitters to bricks.";
$Notice[$NoticeCount++] = "Have you tried Lawry's Seasoned Salt on the flesh of your enemies?";<file_sep>//data" x y z angleid ?(1) color print(leave blank for non print bricks) colorfx? shapefx? raycasting? colliding? rendering?

function getFriendlyDoodad(%brick, %pos)
{
	%doodad = getDoodad(%brick, %pos);
	%doodad = strReplace(%doodad, "\n", "\\n");
	%doodad = strReplace(%doodad, "\"", "\\\"");
}

function multiDoodad(%brick, %biome, %num)
{
	for(%i = 0; %i < $Doodad[%biome, %num]; %i++)
	{
		setDoodad(%brick, $Doodad[%biome, %num, %i]);
	}
}

function setDoodad(%brick, %doodad)
{
	%basePos = %brick.getPosition();
	%count = getRecordCount(%doodad);
	for(%i = 0; %i < %count; %i++)
	{
		%record = getRecord(%doodad, %i);
		%dataEnd = strPos(%record, "\"");
		%data = $uiNameTable[getSubStr(%record, 0, %dataEnd)];
		%record = getSubStr(%record, %dataEnd + 2, strLen(%record));
		%pos = vectorAdd(%basePos, getWords(%record, 0, 2));
		%angleID = getWord(%record, 3);
		%colorID = getWord(%record, 5);
		if(%data.hasPrint)
		{
			%print = getWord(%record, 6);
			%shapeFX = getWord(%record, 7);
			%colorFX = getWord(%record, 8);
			%raycasting = getWord(%record, 9);
			%colliding = getWord(%record, 10);
			%rendering = getWord(%record, 11);
		}
		else
		{
			%shapeFX = getWord(%record, 6);
			%colorFX = getWord(%record, 7);
			%raycasting = getWord(%record, 8);
			%colliding = getWord(%record, 9);
			%rendering = getWord(%record, 10);
		}
		%newBrick = new fxDtsBrick()
		{
			datablock = %data;
			position = %pos;
			rotation = rotationFromAngleID(%angleID);
			angleID = %angleID;
			colorID = %colorID;
			colorFxID = %colorFX;
			shapeFxID = %shapeFX;
			raycasting = %raycasting;
			colliding = %colliding;
			rendering = %rendering;
			isPlanted = true;
			client = %brick.client;
		};
		%brick.getGroup().add(%newBrick);
		if((%err = %newBrick.plant()) != 0)
		{
			%newBrick.delete();
		}
		else
		{
			%newBrick.plantedTrustCheck();
		}
	}
}

function getDoodad(%brick, %pos)
{
	setDoodadTimeout(%brick);
	if(%pos $= "")
	{
		%pos = %brick.getPosition();
	}
	%count = %brick.getNumUpBricks();
	for(%i = 0; %i < %count; %i++)
	{
		%obrick = %brick.getUpBrick(%i);
		if(!%obrick.doodaded)
		{
			setDoodadTimeout(%obrick);
			%str = %str NL %obrick.getDatablock().uiName @ "\" " @ vectorSub(%obrick.getPosition(), %pos) SPC %obrick.getAngleID() SPC "1" SPC %obrick.getColorID() SPC %obrick.getPrintID() SPC %obrick.getColorFxID() SPC "0 1 1 1";
			if(%obrick.getNumUpBricks() || %obrick.getNumDownBricks())
			{
				%str = %str NL getDoodad(%obrick, %pos);
			}
		}
	}
	%count = %brick.getNumDownBricks();
	for(%i = 0; %i < %count; %i++)
	{
		%obrick = %brick.getDownBrick(%i);
		if(!%obrick.doodaded)
		{
			setDoodadTimeout(%obrick);
			%str = %str NL %obrick.getDatablock().uiName @ "\" " @ vectorSub(%obrick.getPosition(), %pos) SPC %obrick.getAngleID() SPC "1" SPC %obrick.getColorID() SPC %obrick.getPrintID() SPC %obrick.getColorFxID() SPC "0 1 1 1";
			if(%obrick.getNumUpBricks() || %obrick.getNumDownBricks())
			{
				%str = %str NL getDoodad(%obrick, %pos);
			}
		}
	}
	return trim(%str);
}

function setDoodadTimeout(%brick)
{
	%brick.doodaded = true;
	schedule(1, 0, eval, %brick @ ".doodaded = false;");
	%brick.schedule(1, setColor, 2);
	%brick.schedule(1, setColorFx, 6);
	%brick.schedule(2500, setColor, %brick.getColorID());
	%brick.schedule(2500, setColorFx, %brick.getColorFxID());
}<file_sep>//ReverieFortWars/accounts.cs

//TABLE OF CONTENTS
// #1. functionality
//   #1.1 RFW_Account functions
//   #1.2 inventory functions
//   #1.3 interfacing
//   #1.4 hasRequiredItems
//   #1.5 getBestArrow
// #2. package

// #1.
// #1.1

$RFW::NewAccountHash = sha1("VisoAccV1");
function GameConnection::loadBetaAccount(%this)
{

}

function GameConnection::saveBetaAccount(%this)
{
	
}

<file_sep>//ReverieFortWars/tribes.cs

//TABLE OF CONTENTS
// #0. party stuff
// #1. tribe stuff
// #2. package

// #0.

function RFW_AddTribe(%name, %color)
{
	$RFW::TribeColor[%name] = hex2dec(%color);
	$RFW::TribeColorHex[%name] = %color;
}

RFW_AddTribe("Malka", "BE0119");
RFW_AddTribe("Prelkan", "FE2C54");
RFW_AddTribe("Kazezi", "82CAFC");
RFW_AddTribe("Faeborn", "15B01A");
RFW_AddTribe("Sarti", "FFFF00");//D1B26F
RFW_AddTribe("Eristia", "9B7A01");
RFW_AddTribe("Mediir", "FFF39A");
RFW_AddTribe("Munaa", "F97306");
RFW_AddTribe("", "FFFFFF");
//RFW_AddParty("White", hex2dec("#ffffff"));
//RFW_AddParty("Red", hex2dec("#e50000"));
//RFW_AddParty("Blue", hex2dec("#0165fc"));
//RFW_AddParty("Green", hex2dec("#15b01a"));
//RFW_AddParty("Yellow", hex2dec("#ffff00"));
//RFW_AddParty("Purple", hex2dec("#9a0eea"));
//RFW_AddParty("Magenta", hex2dec("ff00ff"));
//RFW_AddParty("Cyan", hex2dec("#00ffff"));
//RFW_AddParty("Blood", hex2dec("#be0119"));
//RFW_AddParty("Bone", hex2dec("#fff39a"));
//RFW_AddParty("Bile", hex2dec("#9dff00"));
//RFW_AddParty("Brown", hex2dec("#9b7a01"));
//RFW_AddParty("Tan", hex2dec("#d1b26f"));
//RFW_AddParty("Lilac", hex2dec("#cea2fd"));
//RFW_AddParty("Turquoise", hex2dec("#06b1c4"));
//RFW_AddParty("Jade", hex2dec("#1fa774"));
//RFW_AddParty("Sky", hex2dec("#82cafc"));
//RFW_AddParty("Pink", hex2dec("#ffbacd"));
//RFW_AddParty("Gum", hex2dec("#ff6cb5"));
//RFW_AddParty("Mint", hex2dec("#8fff9f"));
//RFW_AddParty("Orange", hex2dec("#f97306"));
//RFW_AddParty("Silver", hex2dec("#c5c9c7"));
//RFW_AddParty("Steel", hex2dec("#607c8e"));
//RFW_AddParty("Stone", hex2dec("#ada587"));
//RFW_AddParty("Umber", hex2dec("#b26400"));
//RFW_AddParty("Flesh", hex2dec("#fe2c54"));
//RFW_AddParty("Rose", hex2dec("#cf6275"));
//RFW_AddParty("Ochre", hex2dec("#bf9005"));
//RFW_AddParty("Charcoal", hex2dec("#343837"));
//RFW_AddParty("Cobalt", hex2dec("#2c6fbb"));
//RFW_AddParty("Coral", hex2dec("#ff0789"));
//RFW_AddParty("Peach", hex2dec("#ffb07c"));
//RFW_AddParty("Pear", hex2dec("#cbf85f"));

$RFW::ActiveTribes = "<NAME>";

// #1.

package RFW_Chat
{
	function chatMessageAll(%client, %msgString, %clanPrefix, %name, %clanSuffix, %msg)
	{
		%tribe = %client.account.read("Tribe");
		%color = "<color:" @ $RFW::TribeColorHex[%tribe] @ ">";
		%name = %color @ %name;
		%clanSuffix = "<spush><font:Symbol:24>\c7" @ %clanPrefix @ %clanSuffix @ "<spop>";
		%clanPrefix = %tribe;
		%msgString = '\c7%1%2%3\c6: %4';
		return parent::chatMessageAll(%client, %msgString, %clanPrefix, %name, %clanSuffix, %msg);
	}

	function chatMessageTeam(%client, %a, %msgString, %clanPrefix, %name, %clanSuffix, %msg)
	{
		%tribe = %client.account.read("Tribe");
		if(%tribe $= "")
			return parent::chatMessageTeam(%client, %a, %msgString, %clanPrefix, %name, %clanSuffix, %msg);

		%color = "<color:" @ $RFW::TribeColorHex[%tribe] @ ">";
		%name = %color @ %name;
		%clanSuffix = "<spush><font:Symbol:24>\c7" @ %clanPrefix @ %clanSuffix @ "<spop>";
		%clanPrefix = %color @ %tribe;
		%msgString = '\c7[%1\c7]%2%3\c4: %4';
		%clientCount = clientGroup.getCount();
		//echo("clanPrefix:" @ %clanPrefix);
		//echo("clanSuffix:" @ %clanSuffix);
		//echo("name:" @ %name);
		for(%i = 0; %i < %clientCount; %i++)
		{
			%cl = clientGroup.getObject(%i);
			if(%cl.account.read("Tribe") $= %tribe)
				commandToClient(%cl, 'chatMessage', %client, "", "", %msgString, %clanPrefix, %name, %clanSuffix, %msg);
		}
	}
};
activatePackage(RFW_Chat);

// #2.

package RFW_Tribes
{
	function GameConnection::SpawnPlayer(%client)
	{
		%p = parent::SpawnPlayer(%client);
		if(isObject(%obj = %client.player))
		{
			%obj.setDatablock(ReveriePlayer);
			%obj.setEnergyLevel(0);
			commandToClient(%client, 'showEnergyBar', true);
			%obj.setShapeNameDistance(32);
			%obj.setShapeNameColor($RFW::TribeColor[%client.account.read("Tribe")]);
		}
		return %p;
	}
};
activatePackage(RFW_Tribes);

function serverCmdSwitchTribe(%client, %newtribe)
{
	if(!strLen($RFW::TribeColor[%newtribe]))
	{
		messageClient(%client, 'MsgError');
		%client.centerPrintQueue("<color:FF0000>Could not find tribe '" @ %newtribe @ "'", 3000);
		return;
	}
	%oldtribe = %client.account.read("Tribe");
	%newtribe = strUpr(getSubStr(%newtribe, 0, 1)) @ strLwr(getSubStr(%newtribe, 1, strLen(%newtribe)));
	%client.account.delete();
	%acc = RFW_NewAccount();
	%client.account = %acc;
	%acc.client = %client;
	%acc.set("Tribe", %newtribe);
	serverCmdClearBricks(%client);
	if(isObject(%client.player))
	{
		%client.instantRespawn();
	}
	messageAll('', "\c3" @ %client.name @ "\c5 has defected from the <color:" @ $RFW::TribeColorHex[%oldtribe] @ ">" @ %oldtribe @ "\c5 to the <color:" @ $RFW::TribeColorHex[%newtribe] @ ">" @ %newtribe @ "\c5 tribe!");
}<file_sep>//table of contents
//#1.
//  #1.1 getVectorAngle
//  #1.2 player::getRelativePosition
//  #1.3 player::getRelativeVector
//  #1.4 conal raycasts

// #1.1
function getVectorAngle(%vec1, %vec2)
{
	%vec1n = VectorNormalize(%vec1);
	%vec2n = VectorNormalize(%vec2);

	%vdot = VectorDot(%vec1n, %vec2n);
	%angle = mACos(%vdot);

	// convert to degrees and return
	%degangle = mRadToDeg(%angle);
	return %degangle;
}

// #1.2
function Player::getRelativePosition(%player,%vec,%slot)
{
	if(getWordCount(%vec) != 3 || !isObject(%player))
	{
		return;
	}
	// X is left/right
	// Y is forward/back
	// Z is up/down
	%x = getWord(%vec,0);
	%y = getWord(%vec,1);
	%z = getWord(%vec,2);

	%scale = %player.getScale();
	%sx = getWord(%scale,0);
	%sy = getWord(%scale,1);
	%sz = getWord(%scale,2);

	%x *= %sx;
	%y *= %sy;
	%z *= %sz;

	if(%slot $= "eye")
	{
		%yv = vectorNormalize(%player.getEyeVector());
	} else {
		%yv = vectorNormalize(%player.getForwardVector());
	}
	%zv = vectorNormalize(%player.getUpVector());
	%xv = vectorNormalize(vectorCross(%yv,%zv));

	if(%slot $= "eye")
	{
		%pos = %player.getEyePoint();
	} else if(%slot !$= "") {
		%pos = getWords(%player.getSlotTransform(%slot),0,2);
	} else {
		%pos = %player.getHackPosition();
	}

	%pos = vectorAdd(%pos,vectorScale(%xv,%x));
	%pos = vectorAdd(%pos,vectorScale(%yv,%y));
	%pos = vectorAdd(%pos,vectorScale(%zv,%z));

	return %pos;
}

// #1.3
function Player::getRelativeVector(%player,%vec,%slot)
{
	%pos2 = %player.getRelativePosition(%vec,%slot);
	if(%slot $= "eye")
	{
		%pos = %player.getEyePoint();
	} else if(%slot !$= "") {
		%pos = getWords(%player.getSlotTransform(%slot),0,2);
	} else {
		%pos = %player.getHackPosition();
	}
	return vectorNormalize(vectorSub(%pos2,%pos));
}

// #1.4
function conalRaycast(%center, %forwardVector, %radius, %angle, %typemasks, %exclude)
{
	InitContainerRadiusSearch(%center, %radius, %typemasks);
	while(%hit = containerSearchNext())
	{
		if(%hit == %exclude)
		{
			continue;
		}
		%vec1 = vectorNormalize(VectorSub(strPos(%hit.getClassName(), "Player") != -1 ? %hit.getHackPosition() : %hit.getPosition(), %center));
		%vec2 = %forwardVector;
		%ang1 = getVectorAngle(%vec2, %vec1);
		if(%ang1 <= %angle)
		{
			break;
		}
	}
	if(isObject(%hit))
	{
		%raycast = containerRaycast(%center, strPos(%hit.getClassName(), "Player") != -1 ? %hit.getHackPosition() : %hit.getPosition(), %typemasks, %exclude);
	}
	return setWord(%raycast, 0, %hit);
}
function conalRaycastM(%center, %forwardVector, %radius, %angle, %typemasks, %exclude)
{
	InitContainerRadiusSearch(%center, %radius, %typemasks);
	while(%hit = containerSearchNext())
	{
		if(%hit == %exclude)
		{
			continue;
		}
		%vec1 = vectorNormalize(VectorSub(strPos(%hit.getClassName(), "Player") != -1 ? %hit.getHackPosition() : %hit.getPosition(), %center));
		%vec2 = %forwardVector;
		%ang1 = getVectorAngle(%vec2, %vec1);
		if(%ang1 <= %angle)
		{
			%list = %list SPC %hit;
		}
	}
	return trim(%list);
}<file_sep>//ReverieFortWars/buildWorld.cs

//exec("Add-Ons/Gamemode_ReverieFortWars/scripts/buildWorld.cs");

if($RFW::Resources["Dirt"] $= "")
{
	exec("Add-Ons/Gamemode_ReverieFortWars/scripts/resources.cs");
}
exec("Add-Ons/Gamemode_ReverieFortWars/scripts/doodads.cs");

//TABLE OF CONTENTS
// #1. Parameters
// #2. High-level functions
//   #2.1 ReverieFortWars_BuildWorld
//   #2.2 ReverieFortWars_BeginPlanWorld
//   #2.3 ReverieFortWars_DiamondSquare
//   #2.4 ReverieFortWars_AddBiomes
//   #2.5 ReverieFortWars_ConstructWorld
//   #2.6 ReverieFortWars_HydraulicErosion
// #3. Low-level functions

// #1.

$RFW::WorldBasis = nameToID(brick16xCubeData);
$RFW::WorldBasisName = $RFW::WorldBasis.uiName;
//$RFW::WorldBasisGrass = nameToID(brick16);

$RFW::WorldSize = 256; //1, 2, 4, 8, 16, 32, 64, 128, 256, or 512
$RFW::WorldPlanSpeed = 250; //plannings per millisecond
$RFW::WorldBuildSpeed = 250; //bricks written to file per millisecond
$RFW::WorldSnapZ = false; //snap bricks to a Z-grid (a la normal cubescaping)

$RFW::WorldBaseHeight = 256;
$RFW::WorldBumpiness = 40 * $RFW::WorldSize / 512;

$RFW::WorldMountainHeight = $RFW::WorldBaseHeight + 25;

//$RFW::WorldForests = 0;
//$RFW::WorldForestMinRadius = 100;
//$RFW::WorldForestMaxRadius = 150;

$RFW::WorldDeserts = 2;
$RFW::WorldDesertMinRadius = 100 * $RFW::WorldSize / 512;
$RFW::WorldDesertMaxRadius = 150 * $RFW::WorldSize / 512;

$RFW::WorldTundras = 1;
$RFW::WorldTundraMinRadius = 100 * $RFW::WorldSize / 512;
$RFW::WorldTundraMaxRadius = 150 * $RFW::WorldSize / 512;

$RFW::WorldFleshBiomes = 3;
$RFW::WorldFleshMinRadius = 10;
$RFW::WorldFleshMaxRadius = 14;

$RFW::WorldDoodadFrequency = 0.0625; // 1/16

$RFW::WorldBasisSizeX = $RFW::WorldBasis.brickSizeX * 0.5;
$RFW::WorldBasisSizeY = $RFW::WorldBasis.brickSizeY * 0.5;
$RFW::WorldBasisSizeZActual = $RFW::WorldBasis.brickSizeZ * 0.2;
$RFW::WorldBasisSizeZ = $RFW::WorldSnapZ ? $RFW::WorldBasisSizeZActual : 0.6;

$RFW::Erosion::Cycles = 0;
$RFW::Erosion::RainAmt = 20; //base additional water per tile per iteration
$RFW::Erosion::Solubility = 10; //soil lost per tile per iteration
$RFW::Erosion::Evaporation = 25; //maximum water evaporation per tile per iteration
$RFW::Erosion::Capacity = 0.5; //max sediment per unit water


//$RFW::World::Z

function min(%a, %b)
{
	return %a < %b ? %a : %b;
}

// #2.
// #2.1
function ReverieFortWars_BuildWorld()
{
	echo("RFW: Building world...");
	
	//setup
	if(isObject(groundPlane))
	{
		//groundPlane.delete();
	}
	cancel($WorldBuildSched);
	cancel($WorldPlanSched);
	deleteVariables("RFW::Tunneled*");
	deleteVariables("RFW::World*");

	//ready the file
	if($AddOn__Support_AutoSaver != 1)
	{
		$RFW::FileObject = new FileObject(RFW_FileObject){};
		MissionCleanup.add($RFW::FileObject);
		$RFW::FileObject.openForWrite("saves/RFW.bls");
		$RFW::FileObject.writeLine("This is a Blockland save file.  You probably shouldn't modify it cause you'll screw it up.");
		$RFW::FileObject.writeLine("1");
		$RFW::FileObject.writeLine("");
		for(%i = 0; %i < 64; %i++)
		{
			$RFW::FileObject.writeLine(getColorIdTable(%i));
		}
		$RFW::FileObject.writeLine("Linecount 151"); //pretty sure this doesn't do anything
	}

	//initialize diamond-square
	$RFW::World::Z[0, 0] = $RFW::WorldBaseHeight - 25;
	$RFW::World::Z[$RFW::WorldSize, 0] = $RFW::WorldBaseHeight + 25;
	$RFW::World::Z[0, $RFW::WorldSize] = $RFW::WorldBaseHeight - 25;
	$RFW::World::Z[$RFW::WorldSize, $RFW::WorldSize] = $RFW::WorldBaseHeight + 25;
	ReverieFortWars_DiamondSquare(0, 0, $RFW::WorldSize, $RFW::WorldSize);

	schedule(10, 0, ReverieFortWars_AddBiomes);
}

// #2.2
function ReverieFortWars_BeginPlanWorld(%x, %y)
{
	for(%i = 0; %i < $RFW::WorldPlanSpeed; %i++)
	{
		%xContrib = 25 * mCos(%x / 23 + 7) + 20 * mCos(%x / 47 + 12) + 15 * mCos(%x / 27 + 58) + 10 * mCos(%x / 61 + 30) + 5 * mCos(%x / 23 + 2);
		%yContrib = 25 * mCos(%x / 59 + 89) + 20 * mCos(%y / 21 + 7) + 15 * mCos(%y / 91 + 4) + 10 * mCos(%y / 19 + 8) + 5 * mCos(%y / 37 + 17);
		$RFW::World::Z[%x, %y] = 100 + %xContrib + %yContrib;
		//iterate
		if(%y >= $RFW::WorldSize)
		{
			%x++;
			if(%x >= $RFW::WorldSize)
			{
				echo("RFW: Shape initialization complete.");
				//ReverieFortWars_PlanWorld(0);
				ReverieFortWars_AddBiomes();
				return;
			}
			%y = 0;
		}
		else
		{
			%y++;
		}
	}
	$WorldPlanSched = schedule(1, 0, ReverieFortWars_BeginPlanWorld, %x, %y);
}

// #2.3

function ReverieFortWars_DiamondSquare(%minX, %minY, %maxX, %maxY)
{
	%midX = (%minX + %maxX) / 2;
	%midY = (%minY + %maxY) / 2;
	%difference = %midX - %minX;
	%magnitude = $RFW::WorldBumpiness * 2 * %difference / $RFW::WorldSize;

	//diamond step
	%cornerAvg = ($RFW::World::Z[%minX, %minY] + $RFW::World::Z[%maxX, %minY] + $RFW::World::Z[%minX, %maxY] + $RFW::World::Z[%maxX, %maxY]) / 4;
	%mid = %cornerAvg + %magnitude * $RFW::WorldBumpiness * (getRandom() - 0.5);
	$RFW::World::Z[%midX, %midY] = %mid;
	//square step
	%mid = %cornerAvg + %magnitude * $RFW::WorldBumpiness * (getRandom() - 0.5);
	$RFW::World::Z[%minX, %midY] = %mid;
	%mid = %cornerAvg + %magnitude * $RFW::WorldBumpiness * (getRandom() - 0.5);
	$RFW::World::Z[%midX, %minY] = %mid;
	%mid = %cornerAvg + %magnitude * $RFW::WorldBumpiness * (getRandom() - 0.5);
	$RFW::World::Z[%midX, %maxY] = %mid;
	%mid = %cornerAvg + %magnitude * $RFW::WorldBumpiness * (getRandom() - 0.5);
	$RFW::World::Z[%maxX, %midY] = %mid;
	//check if we're done
	if(%difference <= 1)
	{
		return;
	}
	//recurse
	schedule(1, 0, ReverieFortWars_DiamondSquare, %minX, %minY, %midX, %midY);
	schedule(2, 0, ReverieFortWars_DiamondSquare, %midX, %minY, %maxX, %midY);
	schedule(3, 0, ReverieFortWars_DiamondSquare, %minX, %midY, %midX, %maxY);
	$WorldPlanSched = schedule(4, 0, ReverieFortWars_DiamondSquare, %midX, %midY, %maxX, %maxY);
}


// #2.4
function ReverieFortWars_AddBiomes()
{
	for(%tundras = 0; %tundras < $RFW::WorldTundras; %tundras++)
	{
		RFW_AddBiome(getRandom(0, $RFW::WorldSize), getRandom($RFW::WorldSize / 2, $RFW::WorldSize), getRandom($RFW::WorldTundraMinRadius, $RFW::WorldTundraMaxRadius), $RFW::Resource["Snow"]);
	}
	for(%deserts = 0; %deserts < $RFW::WorldDeserts; %deserts++)
	{
		RFW_AddBiome(getRandom(0, $RFW::WorldSize), getRandom(0, $RFW::WorldSize / 2), getRandom($RFW::WorldDesertMinRadius, $RFW::WorldDesertMaxRadius), $RFW::Resource["Sand"]);
	}
	for(%fleshbiomes = 0; %fleshbiomes < $RFW::WorldTundras; %fleshbiomes++)
	{
		RFW_AddBiome(getRandom(0, $RFW::WorldSize), getRandom(0, $RFW::WorldSize), getRandom($RFW::WorldFleshMinRadius, $RFW::WorldFleshMaxRadius), $RFW::Resource["Flesh"]);
	}
	echo("RFW: Biome initialization complete.");
	//ReverieFortWars_ConstructWorld(0, 0);
	ReverieFortWars_HydraulicErosion(0);
}

// #2.5
function ReverieFortWars_ConstructWorld(%x, %y)
{
	if(isEventPending($WorldPlanSched))
	{
		$WorldBuildSched = schedule(10, 0, ReverieFortWars_ConstructWorld, %x, %y);
		return;
	}
	if(%x == 0 && %y == 0)
	{
		echo("RFW: Saving world to file ...");
	}
	for(%i = 0; %i < $RFW::WorldBuildSpeed; %i++)
	{
		%z = mFloatLength($RFW::World::Z[%x, %y] / $RFW::WorldBasisSizeZ, 0) * $RFW::WorldBasisSizeZ;
		%pos = (%x * $RFW::WorldBasisSizeX) SPC (%y * $RFW::WorldBasisSizeY) SPC %z;
		//plant the brick
		%m = $RFW::World::M[%x, %y];
		if(%m $= "")
		{
			if(%z > $RFW::WorldMountainHeight)
			{
				%m = $RFW::Resource["Stone"];
			}
			else
			{
				%m = $RFW::Resource["Dirt"];
			}
		}
		//data" x y z angleid ?(1) color print(leave blank for non print bricks) colorfx? shapefx? raycasting? colliding? rendering?
		if($AddOn__Support_AutoSaver != 1)
			$RFW::FileObject.writeLine($RFW::WorldBasisName @ "\" " @ %pos SPC 0 SPC 1 SPC $RFW::ResourceColor[%m] @ "  " @ $RFW::ResourceColorFx[%m] SPC 0 SPC 1 SPC 1 SPC 1);
		if(%m == $RFW::Resource["Dirt"])
		{
			//add grass on top
			%plantMatter = $RFW::Resource["Plant matter"];
			//$RFW::FileObject.writeLine("4x4F\" " @ setWord(%pos, 2, %z + 1.1) SPC 0 SPC 1 SPC $RFW::ResourceColor[%plantMatter] @ "  " @ $RFW::ResourceColorFx[%plantMatter] SPC 0 SPC 1 SPC 1 SPC 1);
			%doodadOffset = vectorAdd(%pos, "0 0 4.1");
			if($AddOn__Support_AutoSaver != 1)
				$RFW::FileObject.writeLine("16x16 Base\" " @ %doodadOffset SPC 0 SPC 1 SPC $RFW::ResourceColor[%plantMatter] @ "  " @ $RFW::ResourceColorFx[%plantMatter] SPC 0 SPC 1 SPC 1 SPC 1);
		}
		else
		{
			%doodadOffset = %pos;
		}
		if(getRandom() < $RFW::WorldDoodadFrequency)
		{
			%matName = $RFW::Resource[%m];
			if($Doodads[%matName] > 0)
			{
				%r = getRandom(0, $Doodads[%matName]);
				%doodad = $Doodad[%matName, %r];
				if(getWordCount(%doodad) == 1)
				{
					for(%l = 0; %l < %doodad; %l++)
					{
						%subdoodad = $Doodad[%matname, %r, %l];
						%lines = getRecordCount(%subdoodad);
						for(%m = 0; %m < %lines; %m++)
						{
							%line = getRecord(%subdoodad, %m);
							%quotPos = strPos(%line, "\"") + 2;
							%brickName = getSubStr(%line, 0, %quotPos);
							%restLine = getSubStr(%line, %quotPos, strLen(%line));
							%restLine = vectorAdd(%doodadOffset, getWords(%restLine, 0, 2)) SPC getWords(%restLine, 3, 11);
							if($AddOn__Support_AutoSaver != 1)
								$RFW::FileObject.writeLine(%brickName @ %restLine);
						}
					}
				}
				else
				{
					%lines = getRecordCount(%doodad);
					for(%l = 0; %l < %lines; %l++)
					{
						%line = getRecord(%doodad, %l);
						%quotPos = strPos(%line, "\"") + 2;
						%brickName = getSubStr(%line, 0, %quotPos);
						%restLine = getSubStr(%line, %quotPos, strLen(%line));
						%restLine = vectorAdd(%doodadOffset, getWords(%restLine, 0, 2)) SPC getWords(%restLine, 3, 11);
						if($AddOn__Support_AutoSaver != 1)
							$RFW::FileObject.writeLine(%brickName @ %restLine);
					}
				}
			}
		}
		//gap-finding algorithm
		%otherZx = mFloatLength($RFW::World::Z[%x + 1, %y] / $RFW::WorldBasisSizeZ, 0) * $RFW::WorldBasisSizeZ;
		%otherZy = mFloatLength($RFW::World::Z[%x, %y + 1] / $RFW::WorldBasisSizeZ, 0) * $RFW::WorldBasisSizeZ;
		while(((%z - %otherZx) > $RFW::WorldBasisSizeZActuale) || ((%z - %otherZy) > $RFW::WorldBasisSizeZActual))
		{
			%z-= $RFW::WorldBasisSizeZActual;
			if($AddOn__Support_AutoSaver != 1)
				$RFW::FileObject.writeLine($RFW::WorldBasisName @ "\" " @ setWord(%pos, 2, %z) SPC 0 SPC 1 SPC $RFW::ResourceColor[%m] @ "  " @ $RFW::ResourceColorFx[%m] SPC 0 SPC 1 SPC 1 SPC 1);
		}
		if((%otherZx - %z) > $RFW::WorldBasisSizeZActual && %x < $RFW::WorldSize - 1)
		{
			%otherPos = ((%x + 1) * $RFW::WorldBasisSizeX) SPC (%y * $RFW::WorldBasisSizeY) SPC %otherZx;
			%otherRes = $RFW::World::M[%x + 1, %y];
			if(%otherRes $= "")
			{
				if(%otherZx > $RFW::WorldMountainHeight)
				{
					%otherRes = $RFW::Resource["Stone"];
				}
				else
				{
					%otherRes = $RFW::Resource["Dirt"];
				}
			}
			while((%otherZx - %z) > $RFW::WorldBasisSizeZActual)
			{
				%otherZx-= $RFW::WorldBasisSizeZActual;
				if($AddOn__Support_AutoSaver != 1)
					$RFW::FileObject.writeLine($RFW::WorldBasisName @ "\" " @ setWord(%otherPos, 2, %otherZx) SPC 0 SPC 1 SPC $RFW::ResourceColor[%otherRes] @ "  " @ $RFW::ResourceColorFx[%otherRes] SPC 0 SPC 1 SPC 1 SPC 1);
			}
		}
		if((%otherZy - %z) > $RFW::WorldBasisSizeZActual && %y < $RFW::WorldSize - 1)
		{
			%otherPos = (%x * $RFW::WorldBasisSizeX) SPC ((%y + 1) * $RFW::WorldBasisSizeY) SPC %otherZy;
			%otherRes = $RFW::World::M[%x, %y + 1];
			if(%otherRes $= "")
			{
				if(%otherZy > $RFW::WorldMountainHeight)
				{
					%otherRes = $RFW::Resource["Stone"];
				}
				else
				{
					%otherRes = $RFW::Resource["Dirt"];
				}
			}
			while((%otherZy - %z) > $RFW::WorldBasisSizeZActual)
			{
				%otherZy-= $RFW::WorldBasisSizeZActual;
				if($AddOn__Support_AutoSaver != 1)
					$RFW::FileObject.writeLine($RFW::WorldBasisName @ "\" " @ setWord(%otherPos, 2, %otherZy) SPC 0 SPC 1 SPC $RFW::ResourceColor[%otherRes] @ "  " @ $RFW::ResourceColorFx[%otherRes] SPC 0 SPC 1 SPC 1 SPC 1);
			}
		}
		//iterate
		if(%y == $RFW::WorldSize - 1)
		{
			%x++;
			if(%x == $RFW::WorldSize - 1)
			{
				echo("RFW: Saving complete.");
				if($AddOn__Support_AutoSaver != 1)
				{
					$RFW::FileObject.close();
					$RFW::FileObject.delete();
				}
				else
					Autosaver_Begin("ReverieFortWars");

				deleteVariables("RFW::World::*");
				schedule(1, 0, ServerDirectSaveFileLoad, "saves/RFW.bls", 3, 0, 2);
				return;
			}
			%y = 0;
		}
		else
		{
			%y++;
		}
	}
	$WorldBuildSched = schedule(1, 0, ReverieFortWars_ConstructWorld, %x, %y);
}

// #2.6
function ReverieFortWars_HydraulicErosion(%i)
{
	if($RFW::Erosion::Cycles == 0)
	{
		return ReverieFortWars_ConstructWorld(0, 0);
	}
	if(isEventPending($WorldPlanSched))
	{
		return schedule(100, 0, ReverieFortWars_HydraulicErosion, %i);
	}
	echo("Hydraulic erosion cycle " @ %i);
	schedule(1, 0, RFW_HE_Rain);
	RFW_HE_Erode();
	RFW_HE_Settle();
	RFW_HE_Evaporate();
	if(%i++ < $RFW::Erosion::Cycles)
	{
		return schedule(10, 0, ReverieFortWars_HydraulicErosion, %i);
	}
	else
	{
		return ReverieFortWars_ConstructWorld(0, 0);
	}
}

// #3.

function RFW_AddBiome(%centerX, %centerY, %radius, %type)
{
	%xMin = mClamp(%centerX - %radius, 0, $RFW::WorldSize);
	%xMax = mClamp(%centerX + %radius, 0, $RFW::WorldSize);
	%yMin = mClamp(%centerY - %radius, 0, $RFW::WorldSize);
	%yMax = mClamp(%centerY + %radius, 0, $RFW::WorldSize);
	for(%x = %xMin; %x <= %xMax; %x++)
	{
		for(%y = %yMin; %y <= %yMax; %y++)
		{
			//%relDist = mSqrt(mPow(%centerX - %x, 2) + mPow(%centerY - %y, 2)) / %radius;
			%relDist = vectorDist(%x SPC %y, %centerX SPC %centerY) / %radius;
			if(%relDist < 1)
			{
				if(%relDist > 0.7)
				{
					%relDist = (1 - %relDist) / 0.3;
					if(getRandom() < %relDist)
					{
						$RFW::World::M[%x, %y] = %type;
					}
				}
				else
				{
					$RFW::World::M[%x, %y] = %type;
				}
			}
		}
	}
}

function addDoodad(%brick, %doodad)
{
	%pos = %brick.getPosition();
	for(%i = 0 ; %i < getLineCount(%doodad); %i++)
	{
		%data = getLine(%doodad, %i);
		%nbrick = new fxDtsBrick()
		{
			datablock = nameToID(getField(%data, 0));
			position = vectorAdd(%pos, getField(%data, 1));
			rotation = rotationFromAngleID(getField(%data, 2)); //stupid voodoo magic
			colorID = getField(%data, 3);
			colorFxID = getField(%data, 4);
			//material = getField(%data, 5);
			planted = true;
		};
		%err = %nbrick.plant();
		if(!%err)
		{
			%brick.getGroup().add(%nbrick);
			%nbrick.plantedTrustCheck();
		}
		else
		{
			%nbrick.delete();
		}
	}
}

function RFW_HE_Rain()
{
	for(%x = 0; %x < $RFW::WorldSize; %x++)
	{
		for(%y = 0; %y < $RFW::WorldSize; %y++)
		{
			//add rain
			$RFW::World::Water[%x, %y]+= $RFW::Erosion::RainAmt;
		}
	}
	RFW_HE_Erode();
}

function RFW_HE_Erode()
{
	for(%x = 0; %x < $RFW::WorldSize; %x++)
	{
		for(%y = 0; %y < $RFW::WorldSize; %y++)
		{
			//erode the land
			$RFW::World::Z[%x, %y]-= $RFW::Erosion::Solubility;
			$RFW::World::Sediment[%x, %y]+= $RFW::Erosion::Solubility;
		}
	}
	RFW_HE_Settle();
}

function RFW_HE_Settle()
{
	for(%x = 0; %x < $RFW::WorldSize; %x++)
	{
		for(%y = 0; %y < $RFW::WorldSize; %y++)
		{
			//find local land heights
			%zC = $RFW::World::Z[%x, %y];
			%zU = $RFW::World::Z[%x, %y + 1];
			%zD = $RFW::World::Z[%x, %y - 1];
			%zL = $RFW::World::Z[%x - 1, %y];
			%zR = $RFW::World::Z[%x + 1, %y];
			//find local water amounts
			%wC = $RFW::World::Water[%x, %y];
			%wU = $RFW::World::Water[%x, %y + 1];
			%wD = $RFW::World::Water[%x, %y - 1];
			%wL = $RFW::World::Water[%x - 1, %y];
			%wR = $RFW::World::Water[%x + 1, %y];
			//find how many of these aren't blank
			%denom = 1 + (%zU != 0) + (%zD != 0) + (%zL != 0) + (%zR != 0);
			%wAvg = (%wC + %wU + %wD + %wL + %wR) / %denom;
			%zAvg = (%zC + %zU + %zD + %zL + %zR) / %denom;
			%hAvg = %wAvg + %zAvg;
			//we need to exclude tiles whose land height is greater than the average water+land height
			if(%zC > %hAvg)
			{
				%wAvg = %wAvg * %denom / (%denom + 1);
				%zAvg = %zAvg * %denom / (%denom + 1);
				$RFW::World::Water[%x, %y] = 0;
				%denom--;
			}
			if(%zU > %hAvg)
			{
				%wAvg = %wAvg * %denom / (%denom + 1);
				%zAvg = %zAvg * %denom / (%denom + 1);
				$RFW::World::Water[%x, %y + 1] = 0;
				%denom--;
			}
			if(%zD > %hAvg)
			{
				%wAvg = %wAvg * %denom / (%denom + 1);
				%zAvg = %zAvg * %denom / (%denom + 1);
				$RFW::World::Water[%x, %y - 1] = 0;
				%denom--;
			}
			if(%zL > %hAvg)
			{
				%wAvg = %wAvg * %denom / (%denom + 1);
				%zAvg = %zAvg * %denom / (%denom + 1);
				$RFW::World::Water[%x - 1, %y] = 0;
				%denom--;
			}
			if(%zR > %hAvg)
			{
				%wAvg = %wAvg * %denom / (%denom + 1);
				%zAvg = %zAvg * %denom / (%denom + 1);
				$RFW::World::Water[%x + 1, %y] = 0;
				%denom--;
			}
			//redistribute water
			%waterLevel = %zAvg + %wAvg;
			if(%zC <= %hAvg)
			{
				$RFW::World::Water[%x, %y] = %waterLevel - %zC;
			}
			if(%zU && %zU <= %hAvg)
			{
				$RFW::World::Water[%x, %y + 1] = %waterLevel - %zU;
			}
			if(%zD && %zD <= %hAvg)
			{
				$RFW::World::Water[%x, %y - 1] = %waterLevel - %zD;
			}
			if(%zL && %zL <= %hAvg)
			{
				$RFW::World::Water[%x - 1, %y] = %waterLevel - %zL;
			}
			if(%zR && %zR <= %hAvg)
			{
				$RFW::World::Water[%x + 1, %y] = %waterLevel - %zR;
			}
			//redistribute sediment
			%sAvg = ($RFW::World::Sediment[%x, %y] + $RFW::World::Sediment[%x, %y + 1] + $RFW::World::Sediment[%x, %y - 1] + $RFW::World::Sediment[%x - 1, %y] + $RFW::World::Sediment[%x + 1, %y]) / %denom;
			%sPerW = %sAvg / %wAvg;
			$RFW::World::Sediment[%x, %y] = %sPerW * $RFW::World::Water[%x, %y];
			$RFW::World::Sediment[%x, %y + 1] = %sPerW * $RFW::World::Water[%x, %y + 1];
			$RFW::World::Sediment[%x, %y - 1] = %sPerW * $RFW::World::Water[%x, %y - 1];
			$RFW::World::Sediment[%x - 1, %y] = %sPerW * $RFW::World::Water[%x - 1, %y];
			$RFW::World::Sediment[%x + 1, %y] = %sPerW * $RFW::World::Water[%x + 1, %y];
		}
	}
	RFW_HE_Evaporate();
}

function RFW_HE_Evaporate()
{
	for(%x = 0; %x < $RFW::WorldSize; %x++)
	{
		for(%y = 0; %y < $RFW::WorldSize; %y++)
		{
			//evaporate water
			$RFW::World::Water[%x, %y] = mClampF($RFW::Water[%x, %y] - $RFW::Erosion::Evaporation, 0, 1000);
			//deposit excess sediment, if any
			%maxSed = $RFW::World::Water[%x, %y] * $RFW::Erosion::Capacity;
			if($RFW::World::Sediment[%x, %y] > %maxSed)
			{
				%diff = %maxSed - $RFW::World::Sediment[%x, %y];
				$RFW::World::Z[%x, %y]+= %diff;
				$RFW::World::Sediment[%x, %y]-= %diff;
			}
		}
	}
}<file_sep>//ReverieFortWars/digging.cs

//TABLE OF CONTENTS
// #1. parameters
// #2. sounds
// #3. functionality

// #1.

function RFW_Digging_Initialize()
{
	$RFW::WorldBasis = nameToID(brick16xCubeData);
	$RFW::WorldBasisName = $RFW::WorldBasis.uiName;

	$RFW::WorldBasisSizeX = $RFW::WorldBasis.brickSizeX * 0.5;
	$RFW::WorldBasisSizeY = $RFW::WorldBasis.brickSizeY * 0.5;
	$RFW::WorldBasisSizeZActual = $RFW::WorldBasis.brickSizeZ * 0.2;

	Brick4xCubeData.isCube = true;
	Brick8xCubeData.isCube = true;
	Brick16xCubeData.isCube = true;
}
schedule(10, 0, RFW_Digging_Initialize);

$RFW::Ores["Stone"] = 4;
$RFW::Ore["Stone", 0] = 0.6 SPC "Obsidian";
$RFW::Ore["Stone", 1] = 0.7 SPC "Surtra";
$RFW::Ore["Stone", 2] = 0.9 SPC "Calcite";
$RFW::Ore["Stone", 3] = 1.0 SPC "Sanguite";

$RFW::Ores["Sand"] = 4;
$RFW::Ore["Sand", 0] = 0.1 SPC "Stone";
$RFW::Ore["Sand", 1] = 0.5 SPC "Surtra";
$RFW::Ore["Sand", 2] = 0.9 SPC "Calcite";
$RFW::Ore["Sand", 3] = 1.0 SPC "Sanguite";

$RFW::Ores["Snow"] = 4;
$RFW::Ore["Snow", 0] = 0.1 SPC "Stone";
$RFW::Ore["Snow", 1] = 0.6 SPC "Surtra";
$RFW::Ore["Snow", 2] = 0.85 SPC "Calcite";
$RFW::Ore["Snow", 3] = 1.0 SPC "Sanguite";

$RFW::Ores["Dirt"] = 3;
$RFW::Ore["Dirt", 0] = 0.4 SPC "Stone";
$RFW::Ore["Dirt", 1] = 0.85 SPC "Surtra";
$RFW::Ore["Dirt", 2] = 1.0 SPC "Calcite";

$RFW::Ores["Flesh"] = 3;
$RFW::Ore["Flesh", 0] = 0.2 SPC "Bone";
$RFW::Ore["Flesh", 0] = 0.7 SPC "Sanguite";
$RFW::Ore["Flesh", 1] = 1.0 SPC "Calcite";

$PublicClient = new ScriptObject(){ brickGroup = Brickgroup_888888; BL_ID = 888888; };

// #2.

datablock AudioProfile(dirtHitSound1)
{
	filename = "Add-Ons/Gamemode_ReverieFortWars/sounds/dirt_hit_1.wav";
	description = AudioClose3d;
	preload = true;
};

datablock AudioProfile(dirtHitSound2)
{
	filename = "Add-Ons/Gamemode_ReverieFortWars/sounds/dirt_hit_2.wav";
	description = AudioClose3d;
	preload = true;
};

datablock AudioProfile(dirtHitSound3)
{
	filename = "Add-Ons/Gamemode_ReverieFortWars/sounds/dirt_hit_3.wav";
	description = AudioClose3d;
	preload = true;
};

datablock AudioProfile(dirtHitSound4)
{
	filename = "Add-Ons/Gamemode_ReverieFortWars/sounds/dirt_hit_4.wav";
	description = AudioClose3d;
	preload = true;
};

datablock AudioProfile(dirtHitSound5)
{
	filename = "Add-Ons/Gamemode_ReverieFortWars/sounds/dirt_hit_5.wav";
	description = AudioClose3d;
	preload = true;
};

datablock AudioProfile(dirtHitSound6)
{
	filename = "Add-Ons/Gamemode_ReverieFortWars/sounds/dirt_hit_6.wav";
	description = AudioClose3d;
	preload = true;
};

datablock AudioProfile(dirtHitSound7)
{
	filename = "Add-Ons/Gamemode_ReverieFortWars/sounds/dirt_hit_7.wav";
	description = AudioClose3d;
	preload = true;
};

datablock AudioProfile(plantHitSound)
{
	filename = "Add-Ons/Gamemode_ReverieFortWars/sounds/plant_hit.wav";
	description = AudioClose3d;
	preload = true;
};

datablock AudioProfile(sandHitSound1)
{
	filename = "Add-Ons/Gamemode_ReverieFortWars/sounds/sand_hit_1.wav";
	description = AudioClose3d;
	preload = true;
};

datablock AudioProfile(sandHitSound2)
{
	filename = "Add-Ons/Gamemode_ReverieFortWars/sounds/sand_hit_2.wav";
	description = AudioClose3d;
	preload = true;
};

datablock AudioProfile(sandHitSound3)
{
	filename = "Add-Ons/Gamemode_ReverieFortWars/sounds/sand_hit_3.wav";
	description = AudioClose3d;
	preload = true;
};

datablock AudioProfile(stoneHitSound1)
{
	filename = "Add-Ons/Gamemode_ReverieFortWars/sounds/stone_hit_1.wav";
	description = AudioClose3d;
	preload = true;
};

datablock AudioProfile(stoneHitSound2)
{
	filename = "Add-Ons/Gamemode_ReverieFortWars/sounds/stone_hit_2.wav";
	description = AudioClose3d;
	preload = true;
};

datablock AudioProfile(stoneHitSound3)
{
	filename = "Add-Ons/Gamemode_ReverieFortWars/sounds/stone_hit_3.wav";
	description = AudioClose3d;
	preload = true;
};

datablock AudioProfile(stoneHitSound4)
{
	filename = "Add-Ons/Gamemode_ReverieFortWars/sounds/stone_hit_4.wav";
	description = AudioClose3d;
	preload = true;
};

datablock AudioProfile(stoneHitSound5)
{
	filename = "Add-Ons/Gamemode_ReverieFortWars/sounds/stone_hit_5.wav";
	description = AudioClose3d;
	preload = true;
};

datablock AudioProfile(metalHitSound)
{
	filename = "Add-Ons/Gamemode_ReverieFortWars/sounds/metal_hit.wav";
	description = AudioClose3d;
	preload = true;
};

datablock AudioProfile(fleshHitSound1)
{
	filename = "Add-Ons/Gamemode_ReverieFortWars/sounds/flesh_hit_1.wav";
	description = AudioClose3d;
	preload = true;
};

datablock AudioProfile(fleshHitSound2)
{
	filename = "Add-Ons/Gamemode_ReverieFortWars/sounds/flesh_hit_2.wav";
	description = AudioClose3d;
	preload = true;
};

datablock AudioProfile(fleshHitSound3)
{
	filename = "Add-Ons/Gamemode_ReverieFortWars/sounds/flesh_hit_3.wav";
	description = AudioClose3d;
	preload = true;
};

datablock AudioProfile(glassHitSound)
{
	filename = "Add-Ons/Gamemode_ReverieFortWars/sounds/glass_hit.wav";
	description = AudioClose3d;
	preload = true;
};

// #3.
function fxDtsBrick::getVolume(%this)
{
	%data = %this.getDatablock();
	if(%vol = %data.volume)
	{
		return %vol;
	}
	else
	{
		%vol = %data.brickSizeX * %data.brickSizeY * %data.brickSizeZ * 0.05;
		%data.volume = %vol;
		return %vol;
	}
}

function RFW_GetOffset(%z)
{
	//modulus is a better way to do this but torque's modulus function isn't cooperating
	%quotient = %z / $RFW::WorldBasisSizeZActual;
	%quotient-= mFloor(%quotient);
	return mFloatLength(%quotient * $RFW::WorldBasisSizeZActual, 1);
}

function fxDtsBrick::RFW_KillBrick(%this, %sourceObject)
{
	if(isEventPending(%this.delSched))
	{
		return;
	}
	%this.hp = 0;
	%this.schedule(0, fakeKillBrick, "0 0 10", 1);
	%this.setRaycasting(false);
	%this.setColliding(false);
	%this.delSched = %this.schedule(1000, delete);
	if(%this.getGroup().bl_id == 888888)
	{
		%data = %this.getDatablock();
		for(%i = 0; %i < %this.getNumUpBricks(); %i++)
		{
			%o = %this.getUpBrick(%i);
			if(%o.getDatablock().isCube || %o.HPmult || isEventPending(%o.delSched))
			{
				continue;
			}
			%o.delSched = %o.schedule(250, damage, %sourceObject, "INF");
			for(%j = 0; %j < %o.getNumDownBricks(); %j++)
			{
				%o2 = %o.getDownBrick(%j);
				if(isObject(%o2) && !isEventPending(%o2.delSched))
				{
					cancel(%o.delSched);
					break;
				}
			}
		}
	}
}

function fxDtsBrick::Damage(%this, %sourceObject, %amt, %dmgTier, %pos, %normal)
{
	if(isEventPending(%this.delSched))
	{
		return;
	}
	if(isObject(%sourceObject) && getTrustLevel(%sourceObject, %this) >= 2)
	{
		//instantly break
		%amt = "INF";
	}
	%isPublic = %this.getGroup().bl_id == 888888;
	//find nearest totem
	%closestTotem = 0;
	%closestTotemDist = $RFW::TotemRadius;
	for(%i = 0; %i < mainBrickGroup.getCount(); %i++)
	{
		%totem = mainBrickGroup.getObject(%i).totem;
		if(isObject(%totem))
		{
			%dist = vectorDist(%pos, %totem.getPosition());
			if(%dist < %closestTotemDist)
			{
				%closestTotemDist = %dist;
				%closestTotem = %totem;
			}
		}
	}
	if(!isObject(%closestTotem) || %this.getDatablock().uiName $= "Totem" || (isObject(%sourceObject) && getTrustLevel(%sourceObject, %closestTotem) >= 2))
	{
		%totem = 0;
	}
	else
	{
		%totem = %closestTotem;
	}
	%material = $RFW::ResourceByColor[%this.colorID];
	if(%this.hp $= "")
	{
		%hp = %this.getVolume() * $RFW::ResourceQuality[%material];
		if(isObject(%totem))
		{
			%hp*= %totem.HPmult;
		}
		%this.hp = %hp;
		%this.maxHp = %hp;
	}
	if(isObject(%totem))
	{
		%dmgTier-= %totem.tierBonus;
	}
	if(%amt !$= "INF" && $RFW::ResourceTier[%material] == (%dmgTier + 1))
	{
		%dmgTier++;
		%amt/= 2;
	}
	if($RFW::ResourceTier[%material] <= %dmgTier || %amt $= "INF")
	{
		if(!%isPublic || %this.getNumUpBricks() == 0 || %this.getDatablock() != nameToID(brick16x16fdata))
		{
			%this.hp-= %amt;
		}
		if(%amt > 0)
		{
			cancel(%this.resetSched);
			%this.resetSched = %this.schedule(120000, resetHP);
			if(isObject(%totem))
			{
				if(isObject(%sourceObject) && %sourceObject.getClassName() $= "Player")
				{
					%sourceObject.damage(%sourceObject, %pos, %totem.damageReflection * %amt, $DamageType::Totem);
				}
				if(!isEventPending(%this.HPregenSched) && %totem.HPregen)
				{
					%this.HPregenSched = %this.HPregenSched(%totem.HPregen);
				}
			}
		}
		switch$(%material)
		{
			case "Plant matter":
				%sound = "plantHitSound";
			case "Snow":
				%sound = "sandHitSound" @ getRandom(1, 3);
			case "Sand":
				%sound = "sandHitSound" @ getRandom(1, 3);
			case "Stone":
				%sound = "stoneHitSound" @ getRandom(1, 5);
			case "Flesh":
				%sound = "fleshHitSound" @ getRandom(1, 3);
			case "Surtra":
				%sound = "metalHitSound";
			case "Calcite":
				%sound = "metalHitSound";
			case "Sanguite":
				%sound = "metalHitSound";
			case "Nithlite":
				%sound = "metalHitSound";
			case "Sanguite":
				%sound = "metalHitSound";
			case "Glass":
				%sound = "glassHitSound";
			default:
				%sound = "dirtHitSound" @ getRandom(1, 7);
		}
		serverPlay3D(%sound, %pos);
	}
	else
	{
		serverPlay3D(metalHitSound, %pos);
		%sourceObject.addVelocity(vectorAdd(vectorScale(%normal, 8), "0 0 8"));
	}
	if(%this.hp <= 0 || %amt $= "INF")
	{
		%this.RFW_KillBrick(%sourceObject);
		//give killer resources
		if(isObject(%sourceObject))
		{
			%client = %sourceObject.getClassName() $= "GameConnection" ? %sourceObject : %sourceObject.client;
			if(isObject(%client))
			{
				%material = $RFW::ResourceByColor[%this.colorID];
				%quantity = %this.getVolume();
				%itemDatum = %material TAB %quantity TAB 0;
				%client.account.addItem(%itemDatum, true);
				if(isEventPending(%this.unburnSched))
				{
					if(%this.emitter.emitter $= "FaeFireExplosionEmitter")
					{
						%client.account.addItem("Faerie dust\t3\t0", true);
					}
					else if(%this.emitter.emitter $= "FaeFireTrailEmitter")
					{
						%client.account.addItem("Faerie dust\t1\t0", true);
					}
				}
			}
			if(%sourceObject.getClassName() $= "Player")
			{
				%sourceObject.updateBottomPrintHUD("Digging", %material TAB 0 TAB %this.maxHp);
			}
		}
	}
	else
	{
		%data = %this.getDatablock();
		if(%isPublic && %data.isCube && %data.brickSizeX > 4)
		{
			%mult = %data.brickSizeX == 16 ? 0.984375 : 0.875;
			if(%this.hp <= %mult * %this.maxHP)
			{
				%material = $RFW::ResourceByColor[%this.colorID];
				%itemDatum = %material TAB 8 TAB 0;
				if(%data == $RFW::WorldBasis)
				{
					AddAdjacentBricks(%this.getPosition(), %this.underground);
				}
				if(%sourceObject.getClassName() $= "Player")
				{
					%sourceObject.client.account.addItem(%itemDatum, true);
					%sourceObject.updateBottomPrintHUD("Digging", %material TAB 0 TAB %this.maxHp);
					%sourceObject.schedule(0, addVelocity, "0 0 2");
				}
				else
				{
					%sourceObject.account.addItem(%itemDatum, true);
				}
				%this.chisel(%pos, 4, $PublicClient, %sourceObject);
				return;
			}
		}
		if(%sourceObject.getClassName() $= "Player")
		{
			%sourceObject.updateBottomPrintHUD("Digging", %material TAB %this.hp TAB %this.maxHp);
		}
	}
}

function fxDtsBrick::resetHP(%this)
{
	cancel(%this.resetSched);
	%this.hp = "";
	%this.maxhp = "";
}

function fxDtsBrick::HPregenSched(%this, %amt)
{
	cancel(%this.HPregenSched);
	%this.hp = mClampF(%this.hp + %amt, 0, %this.maxhp);
	if(%this.hp == %this.maxHP)
	{
		return;
	}
	%this.HPregenSched = %this.schedule(1000, HPregenSched, %amt);
}

function AddAdjacentBricks(%pos0, %underground)
{
	%offsetMatrix = "1 0 0\n-1 0 0\n0 1 0\n0 -1 0\n0 0 -1";
	if(%underground)
	{
		%offsetMatrix = %offsetMatrix NL "0 0 1";
	}
	%rc = getRecordCount(%offsetMatrix);
	$RFW::Tunneled[%pos0] = true;
	%z0 = getWord(%pos0, 2);
	%offset0 = RFW_GetOffset(%z0);
	%x = $RFW::WorldBasisSizeX;
	%masks = $Typemasks::PlayerObjectType | $Typemasks::FxBrickAlwaysObjectType;
	for(%i = 0; %i < %rc; %i++)
	{
		%dir = getRecord(%offsetMatrix, %i);
		if(%dir $= %normal)
		{
			continue;
		}
		%pos1 = vectorAdd(%pos0, vectorScale(%dir, %x));
		%hit = containerFindFirst(%masks, %pos1, 0.1, 0.1, 500);
		%highestZ = 0; //bricks cannot be placed below z = 0
		while(isObject(%hit))
		{
			if(%hit.getGroup().bl_id == 888888 && %hit.getDatablock() == $RFW::WorldBasis && (%z = getWord(%hit.getPosition(), 2)) > %highestZ)
			{
				%highestZ = %z;
				%highest = %hit;
			}
			%hit = containerFindNext();
		}
		//if the highest Z is still 0 we're either at the edge of the map or someone has mined all the way to the bottom...
		if(%highestZ != 0)
		{
			if(%highestZ < %z0)
			{
				continue;
			}
			%offset1 = RFW_GetOffset(%highestZ);
			if(%offset0 == %offset1)
			{
				//same offset, just need one brick here
				//check that this area is empty and this area hasn't already been tunneled through
				if(!ContainerBoxEmpty(%masks, %pos1, %x / 2 - 0.1, %x / 2 - 0.1, $RFW::WorldBasisSizeZActual / 2 - 0.1) || $RFW::Tunneled[%pos1])
				{
					continue;
				}
				$RFW::Tunneled[%pos1] = true;
				AddBasisBlock(%pos1, $RFW::ResourceByColor[%highest.colorID]);
			}
			else
			{
				//different offsets, need two bricks
				%z1 = %z0 - %offset0 + %offset1;
				%nPos0 = setWord(%pos1, 2, %z1);
				if(%z1 < %z0)
				{
					%nPos1 = vectorAdd(%nPos0, "0 0 " @ $RFW::WorldBasisSizeZActual);
				}
				else
				{
					%nPos1 = vectorSub(%nPos0, "0 0 " @ $RFW::WorldBasisSizeZActual);
				}
				//first brick
				if(!ContainerBoxEmpty(%masks, %nPos0, %x / 2 - 0.1, %x / 2 - 0.1, $RFW::WorldBasisSizeZActual / 2 - 0.1) || $RFW::Tunneled[%nPos0])
				{
					//continue;
				}
				else
				{
					$RFW::Tunneled[%nPos0] = true;
					AddBasisBlock(%nPos0, $RFW::ResourceByColor[%highest.colorID]);
				}
				//second brick
				if(!ContainerBoxEmpty(%masks, %nPos1, %x / 2 - 0.1, %x / 2 - 0.1, $RFW::WorldBasisSizeZActual / 2 - 0.1) || $RFW::Tunneled[%nPos1])
				{
					continue;
				}
				$RFW::Tunneled[%nPos1] = true;
				AddBasisBlock(%nPos1, $RFW::ResourceByColor[%highest.colorID]);
			}
		}
	}
}

function AddBasisBlock(%pos, %mat)
{
	%brick = new fxDtsBrick()
	{
		datablock = $RFW::WorldBasis;
		position = %pos;
		colorID = $RFW::ResourceColor[%mat];
		colorFxID = $RFW::ResourceColorFX[%mat];
		isPlanted = true;
		underground = true;
	};
	%err = %brick.plant();
	if(!%err || %err == 2)
	{
		Brickgroup_888888.add(%brick);
		%brick.plantedTrustCheck();
	}
	else
	{
		%brick.delete();
		return;
	}
	%oreCt = $RFW::Ores[%mat];
	%roll = getRandom();
	if(%oreCt > 0 && !$OresDepleted)
	{
		$OresDepleted = true;
		schedule(100, 0, eval, "$OresDepleted = false;");
		%roll = getRandom();
		for(%i = 0; %i < %oreCt; %i++)
		{
			%ore = $RFW::Ore[%mat, %i];
			if(getRandom() < getWord(%ore, 0))
			{
				AddAdjacentBricks(%pos, %brick.underground);
				%x = getWord(%pos, 0) + 2 * (getRandom() - 0.5) * $RFW::WorldBasisSizeX;
				%y = getWord(%pos, 1) + 2 * (getRandom() - 0.5) * $RFW::WorldBasisSizeX;
				%z = getWord(%pos, 2) + 2 * (getRandom() - 0.5) * $RFW::WorldBasisSizeZActual;
				%brick.addOre(%x SPC %y SPC %z, 4, $PublicClient, getWord(%ore, 1));
				break;
			}
		}
	}
}

function fxDtsBrick::addOre(%this, %pos, %size, %client, %ore)
{
	%data = %this.getDatablock();
	%name = %data.getName();
	if(%name !$= "brick64xCubeData" && %name !$= "brick32xCubeData" && %name !$= "brick16xCubeData" && %name !$= "brick8xCubeData" && %name !$= "brick4xCubeData" && %name !$= "brick2xCubeData")
	{
		return;
	}
	else if((%size != 2 || !isObject(brick2xCubeData)) && %size != 4 && %size != 8 && %size != 16 && %size != 32 && %size != 64)
	{
		return;
	}
	else
	{
		%srcSize = %data.brickSizeX;
		if(%srcSize <= %size)
		{
			%this.setColor($RFW::ResourceColor[%ore]);
			%this.setColorFx($RFW::ResourceColorFx[%ore]);
			return;
		}
		%bricks = %this.fracture(%client);
		%closest = -1;
		%minDist = 9999;
		for(%i = 0; %i < getWordCount(%bricks); %i++)
		{
			if(!isObject(%brick = getWord(%bricks, %i)))
			{
				continue;
			}
			%dist = vectorDist(%pos, %brick.getPosition());
			// if(%dist < %minDist)
			// {
				// %minDist = %dist;
				// %closest = %brick;
			// }
			if(getRandom() < 0.33)
			{
				%brick.addOre(%pos, %size, %client, %ore);
			}
		}
		// if(isObject(%closest))
		// {
			// %closest.addOre(%pos, %size, %client, %ore);
		// }
	}
}

function fxDtsBrick::chisel(%this, %pos, %size, %client, %sourceObject)
{
	%data = %this.getDatablock();
	%name = %data.getName();
	if(%name !$= "brick64xCubeData" && %name !$= "brick32xCubeData" && %name !$= "brick16xCubeData" && %name !$= "brick8xCubeData" && %name !$= "brick4xCubeData" && %name !$= "brick2xCubeData")
	{
		return;
	}
	else if((%size != 2 || !isObject(brick2xCubeData)) && %size != 4 && %size != 8 && %size != 16 && %size != 32 && %size != 64)
	{
		return;
	}
	else
	{
		%srcSize = %data.brickSizeX;
		if(%srcSize <= %size)
		{
			%this.schedule(1, RFW_KillBrick, %sourceObject);
			//%this.delete();
			return;
		}
		%bricks = %this.fracture(%client);
		%closest = -1;
		%minDist = 9999;
		for(%i = 0; %i < getWordCount(%bricks); %i++)
		{
			if(!isObject(%brick = getWord(%bricks, %i)))
			{
				continue;
			}
			%dist = vectorDist(%pos, %brick.getPosition());
			if(%dist < %minDist)
			{
				%minDist = %dist;
				%closest = %brick;
			}
		}
		if(isObject(%closest))
		{
			%closest.chisel(%pos, %size, %client, %sourceObject);
		}
	}
}

function SAG_BrickPlantStuff(%brick, %client)
{
	if(!isObject(%brick))
	{
		//already deleted, perhaps by the chisel
		return -1;
	}
	%err = %brick.plant();
	%client.brickGroup.add(%brick);
	%brick.plantedTrustCheck();
	return %err;
}

function fxDtsBrick::fracture(%this, %client)
{
	%data = %this.getDatablock();
	%name = %data.getName();
	if(%name !$= "brick64xCubeData" && %name !$= "brick32xCubeData" && %name !$= "brick16xCubeData" && %name !$= "brick8xCubeData" && (%name !$= "brick4xCubeData" || !isObject(brick2xCubeData)))
	{
		return;
	}
	else
	{
		%basepos = %this.getPosition();
		%color = %this.colorID;
		%colorFx = %this.colorFxID;
		%cl = isObject(%client) ? %client : %this.client;
		
		%this.delete();
		%newsize = %data.brickSizeX / 2;
		%newdata = "Brick" @ %newsize @ "xCubeData";
		%offset = %newsize / 4;
		%offsetMatrix = "-1 -1 -1\t1 -1 -1\t-1 1 -1\t1 1 -1\t-1 -1 1\t1 -1 1\t-1 1 1\t1 1 1";
		%bricklist = "";
		for(%i = 0; %i < getFieldCount(%offsetMatrix); %i++)
		{
			%pos = vectorAdd(%basepos, vectorScale(getField(%offsetMatrix, %i), %offset));
			%brick = new fxDtsBrick()
			{
				datablock = %newdata;
				position = %pos;
				colorID = %color;
				colorFxID = %colorFx;
				shapeFxID = 0;
				angleID = 0;
				client = %cl;
				stackBL_ID = %cl.BL_ID; 
				isPlanted = true;
			};
			//deleted bricks no longer instantly stop causing overlaps wth new bricks, so schedule this brick's actual planting
			schedule(1, 0, SAG_BrickPlantStuff, %brick, %cl);
			%bricklist = %bricklist SPC %brick;
		}
	}
	return %bricklist;
}<file_sep>if($GameModeArg !$= "Add-Ons/Gamemode_ReverieFortWars/gamemode.txt" && !brickVehicleSpawnData.initBrickRev)
{
	brickVehicleSpawnData.initBrickRev = 1;
	forceRequiredAddOn("Ai_Mutalid");
	forceRequiredAddOn("Brick_2x_Cube");
	//CRC'd -- why?
	exec("add-ons/Brick_Corners/server.cs");
	forceRequiredAddOn("Brick_Large_Cubes");
	forceRequiredAddOn("Brick_Treasure_Chest");
	forceRequiredAddOn("Vehicle_AdvancedHorse");
}

if(!brickVehicleSpawnData.initRev)
{
	brickVehicleSpawnData.initRev = 1;

	//$Pref::Server::GhostLimit = 1280000;
	//$Pref::Server::BrickLimit = 1048576;
	$GameModeDisplayName = "Tribal RPG - v0.9";
	$Pref::Server::Name = "RFW";

	$CorpseTimeoutValue = 60000;
	$Game::MinRespawnTime = 15000;

	$Pref::Server::AS_["BootLoad"] = 0;

	exec("./scripts/leveling.cs");
	exec("./scripts/geometry.cs");
	exec("./scripts/resources.cs");
	exec("./scripts/datablocks.cs");
	exec("./scripts/combat.cs");
	exec("./scripts/accounts.cs");
	exec("./scripts/digging.cs");
	exec("./scripts/items.cs");
	exec("./scripts/appearance.cs");
	exec("./scripts/crafting.cs");
	exec("./scripts/interfacing.cs");
	exec("./scripts/BrickControlsMenu.cs");
	exec("./scripts/tribes.cs");
	exec("./scripts/building.cs");
	exec("./scripts/totems.cs");
	exec("./scripts/help.cs");
	exec("./scripts/notice.cs");
	exec("./scripts/statusEffects.cs");
	exec("./scripts/Support_InventorySlotAdjustment.cs");
	exec("./scripts/hitboxes.cs");
	exec("./scripts/reverieAI.cs");
	exec("./scripts/sunrise.cs");
	exec("./scripts/footsteps.cs");
	exec("./scripts/chests.cs");
	exec("./scripts/buildWorld.cs");
}

//todo:
//random level downs
//random items disappearing
//fix RAM usage

//return; //testing

// PlayerStandardArmor.isInvincible = true; //for admin (ab)use

function RFW_Init(%bypass)
{
	echo("--- BRICK LIMIT: " @ getBrickLimit());
	echo("--- GHOST LIMIT: " @ getGhostLimit());
	brickVehicleSpawnData.uiName = "";
	brickVehicleSpawnData.category = "";
	brickVehicleSpawnData.subcategory = "";

	brick8xWaterRiverData.uiName = "";
	brick8xWaterRiverData.category = "";
	brick8xWaterRiverData.subcategory = "";

	brick8xWaterRapidsData.uiName = "";
	brick8xWaterRapidsData.category = "";
	brick8xWaterRapidsData.subcategory = "";

	brick8xWaterData.uiName = "";
	brick8xWaterData.category = "";
	brick8xWaterData.subcategory = "";

	brick32xWaterData.uiName = "";
	brick32xWaterData.category = "";
	brick32xWaterData.subcategory = "";

	createUINameTable();

	if(!$RFW::Init || %bypass)
	{
		if($Server::ServerType !$= "SinglePlayer")
		{
			if(isFile("saves/RFW.bls"))
			{
				//load as public

				if(isFile("saves/AutoSaver/ReverieFortWars.bls") && isFunction("Autosaver_Begin"))
					schedule(100, 0, ServerDirectSaveFileLoad, "saves/AutoSaver/ReverieFortWars.bls", 3, "", 2);
				else
					schedule(100, 0, ServerDirectSaveFileLoad, "saves/RFW.bls", 3, "", 2);
			}
			else if(isfile("saves/RFW_Backup.bls"))
			{
				//load with saved owner
				schedule(100, 0, ServerDirectSaveFileLoad, "saves/RFW_Backup.bls", 3, "", 1);
			}
			else
				schedule(100, 0, ReverieFortWars_BuildWorld);
		}
	}

	$RFW::Init = 1;
}
schedule(0, 0, "RFW_Init");

function RFW_Autosave()
{
	cancel($RFW_Autosave);
	if($Server::ServerType !$= "SinglePlayer")
	{
		export("$RFW::Tribe*", "config/saves/RFW/Tribes.cs");
		export("$RFW::Chest*", "config/saves/RFW/Chests.cs");
		if(isFunction("Autosaver_Begin"))
			Autosaver_Begin("ReverieFortWars");
		else
			RFW_SaveBricks(0, 0);
	}

	if(clientGroup.getCount() > 0)
	{
		%clientCount = clientGroup.getCount();
		for(%i = 0; %i < %clientCount; %i++)
		{
			%client = clientGroup.getObject(%i);
			%acc = %client.account;
			%acc.setName("NewlyLoaded");
			%acc.save("config/server/RFW/" @ %client.bl_id @ ".cs");
			%acc.setName("");
		}
		echo("RFW: Autosave complete.");
	}
	$RFW_Autosave = schedule(120000, 0, RFW_Autosave);
}
if(!isEventPending($RFW_Autosave))
	$RFW_Autosave = schedule(120000, 0, RFW_Autosave);

function RFW_SaveBricks(%i, %j)
{
	if(!isObject($RFW::FileObject))
	{
		$RFW::FileObject = new FileObject();
		$RFW::FileObject.openForWrite("saves/RFW_Backup.bls");
		$RFW::FileObject.writeLine("This is a Blockland save file.  You probably shouldn't modify it cause you'll screw it up.");
		$RFW::FileObject.writeLine("1");
		$RFW::FileObject.writeLine("");
		for(%c = 0; %c < 64; %c++)
		{
			$RFW::FileObject.writeLine(getColorIdTable(%c));
		}
		$RFW::FileObject.writeLine("Linecount " @ getBrickCount()); //pretty sure this doesn't do anything
	}
	if(mainBrickGroup.getCount() <= %i)
	{
		$RFW::FileObject.close();
		$RFW::FileObject.delete();
		echo("RFW: Bricks autosave complete.");
		return;
	}
	%group = mainBrickGroup.getObject(%i);
	%groupCount = %group.getCount();
	while(%iter++ < 100)
	{
		if(%j >= %groupCount)
		{
			return schedule(0, 0, RFW_SaveBricks, %i + 1, 0);
		}
		%brick = %group.getObject(%j);
		$RFW::FileObject.writeLine(%brick.getDatablock().uiName @ "\" " @ %brick.getPosition() SPC %brick.angleID SPC 1 SPC %brick.colorID SPC (%brick.printID == 0 ? "" : %brick.printID) SPC %brick.colorFx SPC %brick.shapeFX SPC %brick.isRaycasting() SPC %brick.isColliding() SPC %brick.isRendering());
		$RFW::FileObject.writeLine("+-OWNER " @ %group.bl_id);
		if(isObject(%light = %brick.light))
		{
			$RFW::FileObject.writeLine("+-LIGHT " @ %light.getDatablock().uiName @ "\" 1"); //what's this 1 for?
		}
		if(isObject(%emitter = %brick.emitter))
		{
			$RFW::FileObject.writeLine("+-EMITTER " @ %emitter.emitter.uiName @ "\" " @ %brick.emitterDirection);
		}
		if(%eventCount = %brick.numEvents)
		{
			for(%e = 0; %e < %eventCount; %e++)
			{
				%ln = "+-EVENT" TAB %e TAB %brick.eventEnabled[%e] TAB %brick.eventInput[%e] TAB %brick.eventDelay[%e] TAB %brick.eventTarget[%e] TAB %brick.eventNT[%e] TAB %brick.eventOutput[%e];
				for(%f = 1; %f < 5; %f++)
				{
					%ln = %ln TAB %brick.eventOutputParameter[%e, %f];
				}
				$RFW::FileObject.writeLine(%ln);
			}
		}
		%j++;
	}
	return schedule(1, 0, RFW_SaveBricks, %i, %j);
}

function shutdown(%time)
{
	if(%time <= 0)
	{
		%time = 60;
	}
	messageAll('MsgError', "\c4SERVER SHUTDOWN IN " @ %time @ " SECONDS");
	echo("SERVER SHUTDOWN IN " @ %time @ " SECONDS");
	RFW_Autosave();
	cancel($RFW_Autosave);
	schedule(%time * 1000, 0, shutdown_exec);
}

function shutdown_exec()
{
	if(isObject($RFW::FileObject))
	{
		return schedule(1000, 0, shutdown_exec);
	}
	quit();
}

//function MingiameSO::ForceEquip()
//{
//
//}

//no-good rotten tricksters
function serverCmdCreateMinigame()
{
}

function serverCmdWand()
{
}

if(isPackage("ReverieFortWars"))
	deactivatePackage("ReverieFortWars");

package ReverieFortWars
{
	function Armor::onEnterLiquid(%data, %obj, %coverage, %type)
	{
		Parent::onEnterLiquid(%data, %obj, %coverage, %type);
		if(%type != 0)
		{
			%obj.invulnerable = false;
			%obj.damage(%obj, %obj.getPosition(), 10000, $DamageType::Lava);
		}
	}

	function GameConnection::createPlayer(%client, %transform)
	{
		%client.schedule(0, "ApplyHealthBar");
		return Parent::createPlayer(%client, %transform);
	}
	
	function GameConnection::spawnPlayer(%client)
	{
		%result = parent::spawnPlayer(%client);
		%player = %client.player;	
		if(isObject(%player))
			%player.RFW_DisplayHealth();
		
		return %result;
	}

	function Armor::onDisabled(%this, %obj)
	{
		%obj.RFW_DisplayHealth();
		return Parent::onDisabled(%this, %obj);
	}
	
	function Armor::onNewDatablock(%this, %obj, %b)
	{
		Parent::onNewDatablock(%this, %obj, %b);
		%obj.RFW_DisplayHealth();
	}

	// onDamage also handles negative integers.
	function Armor::onDamage(%this, %obj, %damage)
	{
		parent::onDamage(%this, %obj, %damage);
		%obj.RFW_DisplayHealth();
	}
};
activatePackage("ReverieFortWars");

function Player::RFW_DisplayHealth(%this)
{
	if(%this.getClassName() $= "AIPlayer")
		%name = %this.type.name;
	else
	{
		if(isObject(%cl = %this.client))
		{
			if(isObject(%acc = %cl.account))
			{
				if(%acc.vLevel !$= "")
					%lvPr = "[" @ %acc.vLevel @ "] ";
			}

			%name = %lvPr @ %cl.getPlayerName();
		}
		else
			%name = "";
	}

	if(%this.getState() $= "dead")
		%this.SetPlayerShapeName("");	
	else
		%this.SetPlayerShapeName(%name @ "(" @ mFloatLength(%this.getHealth() / %this.getMaxHealth() * 100, 1) @ "%)");
}

function Player::SetPlayerShapeName(%this,%name){%this.setShapeName(%name,"8564862");}
function AIPlayer::SetPlayerShapeName(%this,%name,%tog){if(%tog && trim(%name) !$= "") %name = "(AI) " @ %name; %this.setShapeName(%name,"8564862");}<file_sep>//ReverieFortWars/interfacing.cs

//TABLE OF CONTENTS
// #0. helper functions
// #2. BottomPrint HUD
// #3. Inventory interfacing commands
//   #3.1 /checkInventory
//   #3.2 /toPack
//   #3.3 /fromPack
//   #3.4
//   #3.5 /make
//   #3.6 /equip
//   #3.7 /unequip
// #4. Inventory BrickMenu
// #5. misc stuff
//   #5.1 centerPrint queue
//   #5.2 dropTool package

// #0.
$Hex = "0123456789ABCDEF";

function getChar(%str, %char)
{
	return getSubStr(%str, %char, 1);
}

function Dec2Hex(%v, %max)
{
	%wc = getWordCount(%v);
	if(%max < %wc)
	{
		%max = %wc;
	}
	for(%i = 0; %i < %wc; %i++)
	{
		%n = getWord(%v, %i) * 255;
		%output = %output @ getChar($Hex, mFloor(%n / 16)) @ getChar($Hex, %n % 16);
	}
	return %output;
}

function Hex2Dec(%str)
{
	if(getChar(%str, 0) $= "#")
	{
		%str = getSubStr(%str, 1, strLen(%str));
	}
	%r = (striPos($Hex, getChar(%str, 0)) * 16 + striPos($Hex, getChar(%str, 1))) / 255;
	%g = (striPos($Hex, getChar(%str, 2)) * 16 + striPos($Hex, getChar(%str, 3))) / 255;
	%b = (striPos($Hex, getChar(%str, 4)) * 16 + striPos($Hex, getChar(%str, 5))) / 255;
	return %r SPC %g SPC %b;
}

function max(%a, %b)
{
	return %a > %b ? %a : %b;
}

function colorNormalize(%col)
{
	%max = 0;
	for(%i = 0; %i < 3; %i++)
	{
		%max = max(%max, getWord(%col, %i));
	}
	return vectorScale(%col, %max == 0 ? 1 : 1 / %max);
}


// #2.
$BrickImage = BrickImage.getID();

function Player::UpdateBottomPrintHUD(%obj, %mode, %data)
{
	if(!isObject(%client = %obj.client))
	{
		return;
	}
	%time = getSimTime();
	switch$(%mode)
	{	
		// case "Combat": //%data is the name of the opponent TAB its percent HP e.g. "Ghost\t90"
			// %op = getField(%data, 0);
			// %hp = getField(%data, 1);
			// %dataStr = "<color:FF0000>" @ %op @ " \c6(" @ mFloatLength(%hp, 0) @ "%)" @ "     ";
			// %obj.lastMode = %mode;
			// %obj.lastDataTime = %time;
		case "Digging": //%data is the type of brick TAB its current HP TAB its max HP e.g. "Sand\t50\t100"
			%mat = getField(%data, 0);
			%hp = getField(%data, 1);
			%maxHp = getField(%data, 2);
			%dataStr = "\c6" @ %mat @ " block (" @ %hp @ "/" @ %maxHp @ ")" @ "     ";
			%obj.lastMode = %mode;
			%obj.lastDataTime = %time;
		case "Building": //%data is the current relevant resource (by paint color) TAB the amount of that resource the player has e.g. "0\t512"
			%n = getField(%data, 0);
			%quantity = getField(%data, 1);
			%dataStr = "\c6" @ $RFW::ResourceByColor[%n] @ ": <color:" @ Dec2Hex(getColorIdTable(%n), 3) @ ">" @ %quantity;
			if(isObject(%brick = %obj.tempBrick))
			{
				%dataStr = %dataStr @ "/" @ %brick.getVolume();
			}
			else if(%obj.getMountedImage(0) == $BrickImage)
			{
				%dataStr = %dataStr @ "/" @ %client.instantUseData.getVolume();
			}
			%dataStr = %dataStr @ "     ";
			%obj.lastMode = %mode;
			%obj.lastDataTime = %time;
		// case "Examine":
			// %obj.lastMode = %mode;
			// %obj.lastDataTime = %time;
		default:
			%dataStr = %obj.lastData;
	}
	if(%obj.lastDataTime + 15000 < %time)
	{
		%obj.lastDataTime = %time;
		%dataStr = "";
	}
	%obj.lastData = %dataStr;
	//territory
	%pos = %obj.getPosition();
	%closestTotem = 0;
	%closestTotemDist = $RFW::TotemRadius + 1;
	%groupCount = mainBrickGroup.getCount();
	for(%i = 1; %i < %groupCount; %i++)
	{
		%totem = mainBrickGroup.getObject(%i).totem;
		if(isObject(%totem))
		{
			%dist = vectorDist(%totem.getPosition(), %pos);
			if(%dist < %closestTotemDist)
			{
				%closestTotem = %totem;
				%closestTotemDist = %dist;
			}
		}
	}
	if(isObject(%closestTotem))
	{
		%group = %closestTotem.getGroup();
		%color =  %group.tribe $= %client.account.read("Tribe") ? "\c2" : "<color:FF0000>";
		%dataStr = %dataStr @ %color @ %group.tribe @ " territory";
	}
	//position
	%x = mFloatLength(getWord(%pos, 0) / 8, 0);
	%y = mFloatLength(getWord(%pos, 1) / 8, 0);
	%z = mFloatLength(getWord(%pos, 2) / 8, 0);
	%pos = "<just:right>\c6Position: \c3" @ %x SPC %y SPC %z @ " ";
	//hp
	%dmgPercent = %obj.getDamagePercent();
	%hpColor = colorNormalize(vectorAdd("0 1 0", vectorScale("1 -1 0", %dmgPercent)));
	%hpColor = "<color:" @ Dec2Hex(%hpColor, 3) @ ">";
	%line2 = "\n<just:left>\c6Health: " @ %hpColor @ mFloatLength((1 - %dmgPercent) * 100, 0) @ "%";
	//direction
	%vec = %obj.getForwardVector();
	%x = getWord(%vec, 0);
	%y = getWord(%vec, 1);
	%vec = "<just:right>\c6Compass: \c3";
	if(mAbs(%x) > mAbs(%y))
	{
		%vec = %vec @ (%x > 0 ? "East " : "West ");
	}
	else
	{
		%vec = %vec @ (%y > 0 ? "North " : "South ");
	}
	%client.bottomPrint(%dataStr @ %pos @ %line2 @ %vec, 6, true);
	cancel(%obj.hudUpdateSched);
	%obj.hudUpdateSched = %obj.schedule(200, UpdateBottomPrintHUD);
}

package RFW_Interfacing
{
	function GameConnection::spawnPlayer(%client)
	{
		%p = parent::spawnPlayer(%client);
		if(isObject(%obj = %client.player)) //probably not necessary
		{
			%obj.clearTools();
			%acc = %client.account;
			%maxEquips = %acc.read("MaxEquips");
			for(%i = 0; %i < %maxEquips; %i++)
			{
				%eq = %acc.read("Equip" @ %i);
				%eq = $RFW::ItemDatablock[getItemType(%eq)];
				%obj.tool[%i] = %eq;
				messageClient(%client, 'MsgItemPickup', "", %i, %eq, 1);
			}
			%obj.UpdateBottomPrintHUD();
		}
		return %p;
	}

	function GameConnection::getSpawnPoint(%client)
	{
		%p = parent::getSpawnPoint(%client);
		if(getWord(%p, 2) > 1)
		{
			return %p;
		}
		while(%i++ < 20)
		{
			%x = getRandom(683, 1365);
			%y = getRandom(683, 1365);
			%ray = containerRaycast(%x SPC %y SPC 700, %x SPC %y SPC 0, $Typemasks::FxBrickObjectType);
			if(isObject(%hit = firstWord(%ray)))
			{
				%pos = %hit.getPosition();
				%pos = vectorAdd(%pos, "0 0 " @ (0.1 * %hit.getDatablock().brickSizeZ));
				return %pos;
			}
		}
		error("crashcheck for pickSpawnPoint failure");
		return %p;
	}

	function serverCmdLight(%client)
	{
		%p = parent::serverCmdLight(%client);
		if(isObject(%light = %client.player.light))
		{
			%light.setDatablock(OrangeDimAmbientLight);
		}
		return %p;
	}

	function serverCmdDropTool(%client, %tool)
	{
		%obj = %client.player;
		if(!isObject(%obj))
		{
			return;
		}
		%acc = %client.account;
		if(%client.BCM_MenuState == 2)
		{
			%itemData = stripMLcontrolChars(getRecord(%client.BCM_MenuCache, %client.BCM_MenuPosition));
			if(%itemData $= "Fists")
			{
				messageClient('MsgError');
				%client.centerPrintQueue("<color:FF0000>You'll have to cut them off first.", 3);
				return;
			}
			if($RFW::ItemStackable[%itemData])
			{
				%q = %client.BCM_MenuQuantity;
				if(%q == 0)
				{
					return;
				}
				%name = %itemData @ " x" @ %q;
				%itemData = %itemData TAB %q;
			}
			else
			{
				%name = %itemData;
			}
			if(!%client.account.removeItem(%itemData, false))
			{
				return;
			}
			%temp = %obj.tool[%tool];
			%datablock = $RFW::ItemDatablock[getField(%itemData, 0)];
			%obj.tool[%tool] = %datablock ? %datablock : nameToID(genericItem);
			%p = parent::serverCmdDropTool(%client, %tool);
			%obj.tool[%tool] = %temp;
			messageClient(%client, 'MsgItemPickup', "", %tool, %temp, 1);
			InitContainerRadiusSearch(%obj.getPosition(), 5, $TypeMasks::ItemObjectType);
			while(%hit = containerSearchNext())
			{
				if(!%hit.RFW_Searched)
				{
					%hit.RFW_Searched = true;
					%hit.setShapeNameDistance(32);
					%hit.setShapeName(%name);
					%hit.data = %itemData;
					%found = true;
					break;
				}
			}
			%client.BCM_MenuChangeState(2);
		}
		else
		{
			if(%obj.tool[%tool] == nameToID(FistsItem))
			{
				messageClient('MsgError');
				%client.centerPrintQueue("<color:FF0000>You'll have to cut them off first.", 3);
				return;
			}
			%p = parent::serverCmdDropTool(%client, %tool);
			%itemData = %acc.read("Equip" @ %tool);
			%name = getItemType(%itemData);
			if($RFW::ItemStackable[%name])
			{
				%name = %name SPC "x" @ getItemQuantity(%itemData);
			}
			InitContainerRadiusSearch(%obj.getPosition(), 5, $TypeMasks::ItemObjectType);
			while(%hit = containerSearchNext())
			{
				if(!%hit.RFW_Searched)
				{
					%hit.RFW_Searched = true;
					%hit.setShapeNameDistance(32);
					%hit.setShapeName(%name);
					%hit.data = %itemData;
					%found = true;
					break;
				}
			}
			if(%found)
			{
				%acc.set("Equip" @ %tool, "");
			}
		}
		return %p;
	}

	function armor::onCollision(%this, %obj, %col, %norm, %speed)
	{
		if(%col.getClassName() $= "Item")
		{
			if(%obj.getClassName() $= "AIPlayer")
			{
				//bots shouldn't pick up items
				return;
			}
			else if(isObject(%acc = (%client = %obj.client).account) && strLen(%data = %col.data))
			{
				if(!%acc.addItem(%data, true))
				{
					messageClient("<color:FF0000>No pack space!", 3);
					return;
				}
				else
				{
					%col.delete();
					return;
				}
			}
		}
		return parent::onCollision(%this, %obj, %col, %norm, %speed);
	}
};
activatePackage(RFW_Interfacing);

// #3.

// #3.1
function serverCmdCheckInventory(%client, %w1, %w2, %w3, %w4, %w5)
{
	%w = trim(%w1 SPC %w2 SPC %w3 SPC %w4 SPC %w5);
	%w = $RFW::Item[$RFW::Item[%w]]; //this will force its capitalization to be correct
	%acc = %client.account;
	%maxItems = %acc.read("MaxItems");
	if($RFW::Item[%w] $= "")
	{
		//all the items
		for(%i = 0; %i < %maxItems; %i++)
		{
			%item = %acc.read("Item" @ %i);
			if(%item !$= "")
			{
				%type = getItemType(%item);
				%str = "\c3(#" @ %i @ ") \c2";
				if($RFW::ItemStackable[%type])
				{
					%str = %str @ getItemQuantity(%item) @ "x " @ %type;
				}
				else
				{
					%str = %str @ %type @ " (" @ (getItemKills(%item) * 1) @ " kills)";
				}
				messageClient('', %str);
			}
		}
		return;
	}
	for(%i = 0; %i < %maxItems; %i++)
	{
		%item = %acc.read("Item" @ %i);
		if(getItemType(%item) $= %w)
		{
			if($RFW::ItemStackable[%w])
			{
				%quantity = getItemQuantity(%item);
				break;
			}
			else
			{
				%quantity++;
			}
		}
	}
	if(%quantity > 0)
	{
		%client.centerPrintQueue("\c2You have " @ %quantity @ "x " @ %w @ ".", 3);
	}
	else
	{
		messageClient('MsgError');
		%client.centerPrintQueue("<color:FF0000>You don't have any " @ %w @ ".", 3);
	}
}

// #3.2
function serverCmdToPack(%client, %w1, %w2, %w3, %w4, %w5)
{
	%w = trim(%w1 SPC %w2 SPC %w3 SPC %w4 SPC %w5);
	if(!strLen(%w))
	{
		//try to move the currently equipped item
		if(isObject(%obj = %client.player))
		{
			%w = %obj.getMountedImage(0).item.uiName;
		}
	}
	else
	{
		%w = $RFW::Item[$RFW::Item[%w]]; //this will force its capitalization to be correct
	}
	if($RFW::Item[%w] $= "")
	{
		messageClient('MsgError');
		%client.centerPrintQueue("<color:FF0000>There is not an item named \"" @ %w @ "\".", 3);
		return;
	}
	%acc = %client.account;
	%maxEquips = %acc.read("MaxEquips");
	for(%i = 0; %i < %maxEquips; %i++)
	{
		%item = %acc.read("Equip" @ %i);
		if(getItemType(%item) $= %w)
		{
			%found = true;
			break;
		}
	}
	if(!%found)
	{
		messageClient('MsgError');
		%client.centerPrintQueue("<color:FF0000>You don't have a " @ %w @ " equipped.", 3);
	}
	else
	{
		if(%acc.addItem(%item))
		{
			if(isObject(%obj = %client.player))
			{
				if(%obj.currTool == %i)
				{
					serverCmdUnuseTool(%client);
				}
				%obj.tool[%i] = "";
				messageClient(%client, 'MsgItemPickup', '', %i, 0);
			}
			%acc.set("Equip" @ %i, "");
			%client.centerPrintQueue("\c2Moved " @ %w @ " to pack successfully.", 3);
		}
		else
		{
			messageClient('MsgError');
			%client.centerPrintQueue("<color:FF0000>You don't have room in your pack for a " @ %w @ ".", 3);
		}
	}
}

// #3.3
function serverCmdFromPack(%client, %w1, %w2, %w3, %w4, %w5)
{
	%w = trim(%w1 SPC %w2 SPC %w3 SPC %w4 SPC %w5);
	if(!strLen(%w))
	{
		messageClient('MsgError');
		%client.centerPrintQueue("<color:FF0000>You must enter an item name.", 3);
		return;
	}
	else
	{
		%w = $RFW::Item[$RFW::Item[%w]]; //this will force its capitalization to be correct
	}
	if($RFW::Item[%w] $= "")
	{
		messageClient('MsgError');
		%client.centerPrintQueue("<color:FF0000>There is not an item named \"" @ %w @ "\".", 3);
		return;
	}
	else if(!(%itemDB = $RFW::ItemDatablock[%w]))
	{
		messageClient('MsgError');
		%client.centerPrintQueue("<color:FF0000>" @ %w @ " cannot be equipped.", 3);
		return;
	}
	%acc = %client.account;
	%i = %acc.getItemSlot(%w);
	if(%i == -1)
	{
		messageClient('MsgError');
		%client.centerPrintQueue("<color:FF0000>You don't have a " @ %w @ ".", 3);
		return;
	}
	%item = %acc.read("Item" @ %i);
	%maxEquips = %acc.read("MaxEquips");
	for(%j = 0; %j < %maxEquips; %j++)
	{
		if(%acc.read("Equip" @ %j) $= "")
		{
			%acc.set("Item" @ %i, "");
			%acc.set("Equip" @ %j, %item);
			%found = true;
			break;
		}
	}
	if(!%found)
	{
		messageClient('MsgError');
		%client.centerPrintQueue("<color:FF0000>No room in your equipment for a " @ %w @ ".", 3);
		return;
	}
	if(isObject(%obj = %client.player))
	{
		%obj.tool[%j] = %itemDB;
		messageClient(%client, 'MsgItemPickup', '', %j, %itemDB);
	}
	%client.centerPrintQueue("\c2" @ %w @ " added to equipment.", 3);
}

//3.4
function serverCmdNameWeapon(%client, %w1, %w2, %w3, %w4, %w5)
{
	%w = trim(%w1 SPC %w2 SPC %w3 SPC %w4 SPC %w5);
	messageClient(%client, 'MsgError', "not yet implemented");
}

// #3.5
function serverCmdCraft(%client, %w1, %w2, %w3, %w4, %w5)
{
	%w = trim(%w1 SPC %w2 SPC %w3 SPC %w4 SPC %w5);
	%w1 = firstWord(%w);
	if(%w1 $= %w1 * 1)
	{
		%q = mFloatLength(%w1, 0);
		if(%q < 1)
		{
			messageClient(%client, 'MsgError');
			%client.centerPrintQueue("<color:FF0000>Cannot craft " @ %q @ " of an item.", 3);
			return;
		}
		%w = restWords(%w);
	}
	else
	{
		%q = 1;
	}
	%it = $RFW::Item[$RFW::Item[%w]];
	if(%it $= "")
	{
		messageClient(%client, 'MsgError');
		%client.centerPrintQueue("<color:FF0000>There is not an item named \"" @ %w @ "\".", 3);
		return;
	}
	%matsLists = $RFW::RecipeMats[%it];
	%reqs = $RFW::RecipeReqs[%it];
	if(%matsLists $= "")
	{
		messageClient(%client, 'MsgError');
		%client.centerPrintQueue("<color:FF0000>There is not a recipe to craft \"" @ %it @ "\".", 3);
		return;
	}
	%acc = %client.account;
	%rcount = getRecordCount(%matsLists);
	//check materials first
	for(%i = 0; %i < %rcount; %i++)
	{
		%tfailed = false;
		%matsList = getRecord(%matsLists, %i);
		%tcount = getFieldCount(%matsList);
		for(%j = 0; %j < %tcount; %j++)
		{
			%mat = getField(%matsList, %j);
			%mat = restWords(%mat) TAB (%q * firstWord(%mat));
			if(!%acc.hasItemQuantity(%mat) && !%client.canCraftAnything)
			{
				%tfailed = true;
				messageClient(%client, 'MsgError');
				%q = getField(%mat, 1);
				%client.centerPrintQueue("\c0Requires " @ getField(%mat, 1) @ "x " @ getField(%mat, 0) @ " (You have: " @ %acc.getItemQuantity(%mat) @ "x)", 3);
				break;
			}

			if(%requireStr $= "")
				%requireStr = getField(%mat, 0) @ "x" @ getField(%mat, 1);
			else
				%requireStr = %requireStr NL getField(%mat, 0) @ "x" @ getField(%mat, 1);
		}
		if(!%tfailed)
		{
			%success = true;
			break;
		}
	}
	if(!%success)
	{
		return;
	}
	%success = false;

	//check requirements
	%tcount = getFieldCount(%reqs);
	for(%i = 0; %i < %tcount; %i++)
	{
		if(%acc.hasItem(getField(%reqs, %i)))
		{
			%success = true;
			break;
		}
	}

	if(!%success && %tcount > 0 && !%client.canCraftAnything)
	{
		messageClient(%client, 'MsgError');
		%client.centerPrintQueue("<color:FF0000>Requires " @ getField(%reqs, 0) @ " or better.", 3);
		return;
	}

	%client.matQ = %q;
	%client.matL = %matsList;
	%client.matIT = %it;

	%client.craftConfirm = 1;
	%client.lastCraftAttempt = getSimTime();
	commandToClient(%client, 'MessageBoxOKCancel', "Craft " @ %w @ "?", "Requirements:\n" @ %requireStr, 'ConfirmCraft');
}

function serverCmdConfirmCraft(%client)
{
	if(getSimTime() - %client.lastCraftAttempt < 10000 && %client.craftConfirm)
	{
		if(!isObject(%acc = %client.account))
		{
			%client.chatMessage("You have an invalid account. Please talk to an administrator.");
			return;
		}

		%client.craftConfirm = 0;
		%q = %client.matQ;
		%matsList = %client.matL;
		%it = %client.matIT;

		if(%q $= "" || %matsList $= "" || %it $= "")
		{
			%client.chatMessage("You did not request an actual crafting item.");
			return;
		}

		//remove materials and add item
		if(!%client.canCraftAnything)
		{
			%tcount = getFieldCount(%matsList);
			for(%i = 0; %i < %tcount; %i++)
			{
				%mat = getField(%matsList, %i);
				%mat = restWords(%mat) TAB (%q * firstWord(%mat));
				if(!%acc.removeItem(%mat))
				{
					error("Material removal failure");
					%acc.dump();
					%client.centerPrintQueue("\c4Material removal failure", 3);
				}
			}
		}
		
		if($RFW::ItemStackable[%it])
		{
			%it = %it TAB %q TAB "";
			%acc.addItem(%it, true);
		}
		else
		{
			%it = %it TAB 0 TAB "";
			for(%i = 0; %i < %q; %i++)
			{
				if(!%acc.addItem(%it TAB 0 TAB "", true))
				{
					%client.centerPrintQueue("\c4Out of inventory space", 3);
					break;
					//TODO: handle this better
				}
			}
		}
	}
}

function serverCmdMake(%client, %w1, %w2, %w3, %w4, %w5)
{
	serverCmdCraft(%client, %w1, %w2, %w3, %w4, %w5);
}

// #3.6
function serverCmdEquip(%client, %w1, %w2, %w3, %w4, %w5)
{
	%w = trim(%w1 SPC %w2 SPC %w3 SPC %w4 SPC %w5);
	%it = $RFW::Item[$RFW::Item[%w]];
	%acc = %client.account;
	if(%it $= "")
	{
		messageClient(%client, 'MsgError');
		%client.centerPrintQueue("<color:FF0000>There is not an item named \"" @ %w @ "\".", 3);
		return;
	}
	%armor = %acc.getItem(%it);
	if(!%armor $= "0")
	{
		messageClient(%client, 'MsgError');
		%client.centerPrintQueue("<color:FF0000>You don't have " @ %it @ ".", 3);
		return;
	}
	if(!$RFW::ItemWearable[%it])
	{
		if($RFW::ItemEquipable[%it])
		{
			serverCmdFromPack(%client, %w);
			return;
		}
		if(%fn = $RFW::ItemEffect[%it])
		{
			if(isObject(%player = %client.player))
			{
				return eval(%fn);
			}
			else
			{
				messageClient(%client, 'MsgError');
				%client.centerPrintQueue("<color:FF0000>Cannot use \"" @ %it @ "\" right now.", 3);
			}
		}
		messageClient(%client, 'MsgError');
		%client.centerPrintQueue("<color:FF0000>Cannot equip \"" @ %it @ "\".", 3);
		return;
	}
	%acc.removeItem(%armor, false);
	%type = $RFW::ItemSlot[%it];
	if((%equipped = %acc.read(%type)) !$= "")
	{
		serverCmdUnequip(%client, %equipped);
	}
	%acc.set(%type, %it);
	%client.centerPrintQueue("\c2Equipped " @ %it @ ".", 3);
	if(isObject(%obj = %client.player))
	{
		%client.applyBodyParts();
		%client.applyBodyColors();
	}
}

// #3.7
function serverCmdUnequip(%client, %w1, %w2, %w3, %w4, %w5)
{
	%w = trim(%w1 SPC %w2 SPC %w3 SPC %w4 SPC %w5);
	%it = $RFW::Item[$RFW::Item[%w]];
	if(%it $= "")
	{
		messageClient(%client, 'MsgError');
		%client.centerPrintQueue("<color:FF0000>There is not an item named \"" @ %w @ "\".", 3);
		return;
	}
	if(!$RFW::ItemWearable[%it])
	{
		if($RFW::ItemEquipable[%it])
		{
			serverCmdToPack(%client, %w);
			return;
		}
		messageClient(%client, 'MsgError');
		%client.centerPrintQueue("<color:FF0000>Cannot equip \"" @ %it @ "\".", 3);
		return;
	}
	%acc = %client.account;
	%type = $RFW::ItemSlot[%it];
	%item = %acc.read(%type);
	if(%item $= "")
	{
		messageClient(%client, 'MsgError');
		%client.centerPrintQueue("<color:FF0000>Nothing equipped in that slot.", 3);
		return;
	}
	if(%acc.addItem(%item, false))
	{
		%client.centerPrintQueue("\c2Unequipped " @ getItemType(%item) @ ".", 3);
		switch$(getWord(%it, 1))
		{
			case "Armor": %acc.set("Armor", "");
			case "Shirt": %acc.set("Shirt", "");
			case "Pants": %acc.set("Pants", "");
			case "Hat": %acc.set("Hat", "");
		}
		if(isObject(%obj = %client.player))
		{
			%client.applyBodyParts();
			%client.applyBodyColors();
		}
	}
	else
	{
		messageClient(%client, 'MsgError');
		%client.centerPrintQueue("<color:FF0000>No room in your inventory for the " @ %it @ ".", 3);
	}
	return;
}

// #5.
// #5.1
function GameConnection::centerPrintQueue(%client, %text, %time)
{
	if(%client.BCM_MenuState != 0 || %client.getControlObject() != %client.player || !%client.account.read("UseCPQ"))
	{
		messageClient(%client, '', %text);
		return;
	}
	if(%time < 1000)
	{
		%time*= 1000;
	}
	RFW_CPQ_Enqueue(%client, %text, %time);
}

function RFW_CPQ_Update(%client)
{
	if(!isObject(%client) || %client.BCM_MenuState != 0 || %client.getControlObject() != %client.player)
	{
		return;
	}
	%text = getFields(%client.RFW_CPQ[0], 1, getFieldCount(%client.RFW_CPQ[0]));
	for(%i = 1; (%str = %client.RFW_CPQ[%i]) !$= ""; %i++)
	{
		%text = %text NL getFields(%str, 1, getFieldCount(%str));
	}
	%client.centerPrint(%text, 5);
}

function RFW_CPQ_Enqueue(%client, %text, %time)
{
	%text = getRandom() TAB %text;
	for(%i = 4; %i > 0; %i--)
	{
		%client.RFW_CPQ[%i] = %client.RFW_CPQ[%i - 1];
	}
	%client.RFW_CPQ[0] = %text;
	schedule(%time, 0, RFW_CPQ_Dequeue, %client, %text);
	RFW_CPQ_Update(%client);
}

function RFW_CPQ_Dequeue(%client, %text)
{
	for(%i = 0; %i < 5; %i++)
	{
		if(!%found)
		{
			if(%client.RFW_CPQ[%i] $= %text)
			{
				%found = true;
				%i--;
			}
		}
		else
		{
			%client.RFW_CPQ[%i] = %client.RFW_CPQ[%i + 1];
		}
	}
	RFW_CPQ_Update(%client);
}<file_sep>$RFW::WorldNames = "Astralis Oneirofortress Ciralen Luni Floren Tarsia Ivoni Ebori Shivan Seercrest Dreamridge Sagemont Vannil Choci Steerber Cherish Stardream Solhome";

datablock AudioProfile(RoosterSound)
{
	filename = "Add-Ons/GameMode_ReverieFortWars/sounds/OOT_6amRooster.wav";
	description = Audio2d;
	preload = true;
};

datablock AudioProfile(WolfSound)
{
	filename = "Add-Ons/GameMode_ReverieFortWars/sounds/OOT_6pmWolf.wav";
	description = Audio2d;
	preload = true;
};

function RFW_SetWorldName(%name)
{
	if(!strLen(%name))
	{
		$RFW::WorldName = getWord($RFW::WorldNames, getRandom(0, getWordcount($RFW::WorldNames) - 1));
	}
	else
	{
		$RFW::WorldName = %name;
	}
}

function RFW_StartDayTracking()
{
	cancel($RFW_Dawn);
	cancel($RFW_Dusk);
	if(!strLen($RFW::WorldName))
	{
		RFW_SetWorldName();
	}
	%time = $Sim::Time;
	%len = DayCycle.dayLength;
	%offset = %len * DayCycle.dayOffset;
	$RFW::DaysPassed = mFloor((%time - %offset) / %len);
	%rem = ($RFW::DaysPassed + 1) * %len + %offset - %time;
	$RFW_Dawn = schedule(%rem * 1000, 0, RFW_Dawn);
}

function RFW_Dawn()
{
	cancel($RFW_Dawn);
	cancel($RFW_Dusk);
	%clientCount = clientGroup.getCount();
	for(%i = 0; %i < %clientCount; %i++)
	{
		clientGroup.getObject(%i).play2d(RoosterSound);
	}
	$RFW::DaysPassed++;
	messageAll('', "\c6Day " @ $RFW::DaysPassed @ " dawns on " @ $RFW::WorldName @ ".");
	$RFW_Dawn = schedule(DayCycle.dayLength * 1000, 0, RFW_Dawn);
	$RFW_Dusk = schedule(DayCycle.dayLength * 500, 0, RFW_Dusk);
}

function RFW_Dusk()
{
	%clientCount = clientGroup.getCount();
	for(%i = 0; %i < %clientCount; %i++)
	{
		clientGroup.getObject(%i).play2d(WolfSound);
	}
}<file_sep>//ReverieFortWars/combat.cs

//TABLE OF CONTENTS
// #0. deaath message declarations
// #1.
// #1.1 assists functionality
// #2. package

// #0.
AddDamageType("Sword", '<bitmap:Add-Ons/Weapon_Sword/CI_sword> %1', '%2 <bitmap:Add-Ons/Weapon_Sword/CI_sword> %1', 0.75, 1);
AddDamageType("Pickaxe", '<bitmap:Add-Ons/Weapon_Sword/CI_sword> %1', '%2 <bitmap:Add-Ons/Weapon_Sword/CI_sword> %1', 0.75, 1);
AddDamageType("Dagger", '<bitmap:Add-Ons/Weapon_Sword/CI_sword> %1', '%2 <bitmap:Add-Ons/Weapon_Sword/CI_sword> %1', 0.75, 1);
AddDamageType("Bow", '<bitmap:add-ons/Weapon_Bow/CI_arrow> %1', '%2 <bitmap:add-ons/Weapon_Bow/CI_arrow> %1', 0.5, 1);
AddDamageType("FaeFire", '<bitmap:Add-Ons/Weapon_Sword/CI_sword> %1', '%2 <bitmap:Add-Ons/Weapon_Sword/CI_sword> %1', 1, 1);
AddDamageType("Totem", '<bitmap:Add-Ons/Weapon_Sword/CI_sword> %1', '%2 <bitmap:Add-Ons/Weapon_Sword/CI_sword> %1', 1, 1);
AddDamageType("Lens", '<bitmap:Add-Ons/Weapon_Sword/CI_sword> %1', '%2 <bitmap:Add-Ons/Weapon_Sword/CI_sword> %1', 1, 1);
AddDamageType("Mclaw", '<bitmap:Add-Ons/Weapon_Sword/CI_sword> %1', '%2 <bitmap:Add-Ons/Weapon_Sword/CI_sword> %1', 1, 1);
AddDamageType("OssiStaff", '<bitmap:Add-Ons/Weapon_Sword/CI_sword> %1', '%2 <bitmap:Add-Ons/Weapon_Sword/CI_sword> %1', 1, 1);
AddDamageType("Pyromancy", '<bitmap:Add-Ons/Weapon_Sword/CI_sword> %1', '%2 <bitmap:Add-Ons/Weapon_Sword/CI_sword> %1', 1, 1);
AddDamageType("SanguiWand", '<bitmap:Add-Ons/Weapon_Sword/CI_sword> %1', '%2 <bitmap:Add-Ons/Weapon_Sword/CI_sword> %1', 1, 1);
AddDamageType("Bleed", '<bitmap:Add-Ons/Weapon_Sword/CI_sword> %1', '%2 <bitmap:Add-Ons/Weapon_Sword/CI_sword> %1', 1, 1);

$RFW::DeathMessageCount[$DamageType::Default] = 4;
$RFW::DeathMessage[$DamageType::Default, 0] = "%1 put %2 out of their misery";
$RFW::DeathMessage[$DamageType::Default, 1] = "%1 put an end to %2";
$RFW::DeathMessage[$DamageType::Default, 2] = "%1 was the last thing %2 ever saw";
$RFW::DeathMessage[$DamageType::Default, 3] = "%1 was the end of %2";
$RFW::DeathMessage[$DamageType::Default, 4] = "%1 murdered %2";
$RFW::DeathMessageCount[$DamageType::Sword] = 4;
$RFW::DeathMessage[$DamageType::Sword, 0] = "%1 ran %2 through";
$RFW::DeathMessage[$DamageType::Sword, 1] = "%1 sliced %2 to ribbons";
$RFW::DeathMessage[$DamageType::Sword, 2] = "%1 chopped %2 into pieces";
$RFW::DeathMessage[$DamageType::Sword, 3] = "%1 removed %2's head";
$RFW::DeathMessage[$DamageType::Sword, 4] = "%1 found out what %2 was really made of";
$RFW::DeathMessageCount[$DamageType::Dagger] = 4;
$RFW::DeathMessage[$DamageType::Dagger, 0] = "%1 poked %2 full of holes";
$RFW::DeathMessage[$DamageType::Dagger, 1] = "%1 stuck %2 with the pointy end";
$RFW::DeathMessage[$DamageType::Dagger, 2] = "%1 stabbed %2 in the back";
$RFW::DeathMessage[$DamageType::Dagger, 3] = "%1 cut the life from %2";
$RFW::DeathMessage[$DamageType::Dagger, 4] = "%1 examined %2's insides";
$RFW::DeathMessageCount[$DamageType::Pickaxe] = 4;
$RFW::DeathMessage[$DamageType::Pickaxe, 0] = "%1 impaled %2";
$RFW::DeathMessage[$DamageType::Pickaxe, 1] = "%1 mistook %2 for a rock";
$RFW::DeathMessage[$DamageType::Pickaxe, 2] = "%1 mined out %2's skull";
$RFW::DeathMessage[$DamageType::Pickaxe, 3] = "%1 axed %2 a question";
$RFW::DeathMessage[$DamageType::Pickaxe, 4] = "%1 really digs %2";
$RFW::DeathMessageCount[$DamageType::Bow] = 3;
$RFW::DeathMessage[$DamageType::Bow, 0] = "%1 filled %2 with arrows";
$RFW::DeathMessage[$DamageType::Bow, 1] = "%1 used %2 for target practice";
$RFW::DeathMessage[$DamageType::Bow, 2] = "%1 couldn't teach %2 how to dodge arrows";
$RFW::DeathMessage[$DamageType::Bow, 3] = "%1 turned %2 into a pincushion";
$RFW::DeathMessageCount[$DamageType::Snowball] = 2;
$RFW::DeathMessage[$DamageType::Snowball, 0] = "%1 built a snowman out of %2";
$RFW::DeathMessage[$DamageType::Snowball, 1] = "%1 gave %2 a brainfreeze";
$RFW::DeathMessage[$DamageType::Snowball, 2] = "%1 snowballed %2";
$RFW::DeathMessageCount[$DamageType::FaeFire] = 4;
$RFW::DeathMessage[$DamageType::FaeFire, 0] = "A faerie burned %2 to an eldritch crisp";
$RFW::DeathMessage[$DamageType::FaeFire, 1] = "A faerie incinerated %2";
$RFW::DeathMessage[$DamageType::FaeFire, 2] = "A faerie cremated %2";
$RFW::DeathMessage[$DamageType::FaeFire, 3] = "A faerie blasted %2 to cinders";
$RFW::DeathMessage[$DamageType::FaeFire, 4] = "A faerie transmuted %2 into eldritch ash";
$RFW::DeathMessageCount[$DamageType::Lava] = 3;
$RFW::DeathMessage[$DamageType::Lava, 0] = "%2 took a lava bath";
$RFW::DeathMessage[$DamageType::Lava, 1] = "%2 can't swim in lava";
$RFW::DeathMessage[$DamageType::Lava, 2] = "%2 couldn't take the heat";
$RFW::DeathMessage[$DamageType::Lava, 3] = "%2 is sleeping with the lava fishies";
$RFW::DeathMessageCount[$DamageType::Suicide] = 1;
$RFW::DeathMessage[$DamageType::Suicide, 0] = "%2 couldn't handle the pressure";
$RFW::DeathMessage[$DamageType::Suicide, 1] = "%2 took the easy way out";
$RFW::DeathMessageCount[$DamageType::Fall] = 2;
$RFW::DeathMessage[$DamageType::Fall, 0] = "%2 fell down a well";
$RFW::DeathMessage[$DamageType::Fall, 1] = "%2 broke every bone in their body";
$RFW::DeathMessage[$DamageType::Fall, 2] = "%2 didn't stick the landing";
$RFW::DeathMessageCount[$DamageType::Totem] = 2;
$RFW::DeathMessage[$DamageType::Totem, 0] = "%2 picked the wrong fort to raid";
$RFW::DeathMessage[$DamageType::Totem, 1] = "%2 succumbed to a curse";
$RFW::DeathMessage[$DamageType::Totem, 2] = "In Soviet Russia, fort destroys %2";
$RFW::DeathMessageCount[$DamageType::Mclaw] = 3;
$RFW::DeathMessage[$DamageType::Mclaw, 0] = "%1 devoured %2";
$RFW::DeathMessage[$DamageType::Mclaw, 1] = "%1 ripped %2 limb from limb";
$RFW::DeathMessage[$DamageType::Mclaw, 2] = "%1 tore %2 in half";
$RFW::DeathMessage[$DamageType::Mclaw, 3] = "%1 feasted upon %2";
$RFW::DeathMessageCount[$DamageType::Lens] = 2;
$RFW::DeathMessage[$DamageType::Lens, 0] = "%1 blinded %2";
$RFW::DeathMessage[$DamageType::Lens, 1] = "%1 enlightened %2";
$RFW::DeathMessage[$DamageType::Lens, 2] = "%1 showed %2 the light";
$RFW::DeathMessageCount[$DamageType::Bleed] = 1;
$RFW::DeathMessage[$DamageType::Bleed, 0] = "%2 succumbed to red wasting";
$RFW::DeathMessage[$DamageType::Bleed, 1] = "%2 ran out of blood";
$RFW::DeathMessageCount[$DamageType::Pyromancy] = 4;
$RFW::DeathMessage[$DamageType::Pyromancy, 0] = "%1 burned %2 to a crisp";
$RFW::DeathMessage[$DamageType::Pyromancy, 1] = "%1 incinerated %2";
$RFW::DeathMessage[$DamageType::Pyromancy, 2] = "%1 cremated %2";
$RFW::DeathMessage[$DamageType::Pyromancy, 3] = "%1 blasted %2 to cinders";
$RFW::DeathMessage[$DamageType::Pyromancy, 4] = "%1 transmuted %2 into ash";
$RFW::DeathMessageCount[$DamageType::OssiStaff] = 2;
$RFW::DeathMessage[$DamageType::OssiStaff, 0] = "%1 shattered %2's bones";
$RFW::DeathMessage[$DamageType::OssiStaff, 1] = "%1 removed %2's spine";
$RFW::DeathMessage[$DamageType::OssiStaff, 2] = "%1 transmuted %2 into calcite";
$RFW::DeathMessageCount[$DamageType::SanguiWand] = 2;
$RFW::DeathMessage[$DamageType::SanguiWand, 0] = "%1 solidified %2";
$RFW::DeathMessage[$DamageType::SanguiWand, 1] = "%1 made %2's blood into something more useful";
$RFW::DeathMessage[$DamageType::SanguiWand, 2] = "%1 transmuted %2 into sanguite";

// #1.1
function Player::AddDamager(%this, %obj)
{
	if(!isObject(%this) || !isObject(%obj))
	{
		return;
	}
	switch$(%obj.getClassName())
	{
		case "Projectile": return %this.addDamager(%this, %obj.sourceObject);
		case "Player": return %this.addDamager(%this, %obj.client);
		case "AiPlayer": return %this.addDamager(%this, %obj.type);
	}
	for(%i = 0; %i < getWordCount(%this.damagers); %i++)
	{
		if(getWord(%this.damagers, %i) $= %obj)
		{
			%this.removeDamager[%obj] = %this.schedule(10000, removeDamager, %obj);
			return;
		}
	}
	%this.damagers = %this.damagers SPC %obj;
	%this.removeDamager[%obj] = %this.schedule(10000, removeDamager, %obj);
}

function Player::RemoveDamager(%this, %obj)
{
	for(%i = 0; %i < getWordCount(%this.damagers); %i++)
	{
		if(getWord(%this.damagers, %i) $= %obj)
		{
			%this.damagers = removeWord(%this.damagers, %i);
			return;
		}
	}
}

function Player::AddAssist(%this, %obj)
{
	if(!isObject(%this) || !isObject(%obj))
	{
		return;
	}
	switch$(%obj.getClassName())
	{
		case "Projectile": return %this.addAssist(%this, %obj.sourceObject);
		case "Player": return %this.addAssist(%this, %obj.client);
		case "AiPlayer": return %this.addAssist(%this, %obj.type);
	}
	for(%i = 0; %i < getWordCount(%this.assists); %i++)
	{
		if(getWord(%this.assists, %i) $= %obj)
		{
			%this.removeAssist[%obj] = %this.schedule(10000, removeAssist, %obj);
			return;
		}
	}
	%this.assists = %this.assists SPC %obj;
	%this.removeAssist[%obj] = %this.schedule(10000, removeAssist, %obj);
}

function Player::RemoveAssist(%this, %obj)
{
	for(%i = 0; %i < getWordCount(%this.assists); %i++)
	{
		if(getWord(%this.assists, %i) $= %obj)
		{
			%this.assists = removeWord(%this.assists, %i);
			return;
		}
	}
}

function player::HpRegenSched(%this)
{
	cancel(%this.HpRegenSched);
	if(%this.isDead)
		return;

	%this.addHealth(mClampF(%this.getMaxHealth() * 0.01, 1, 100));
	//%health = mClampF(%this.getHealth() + mClampF(%this.getMaxHealth() * 0.01, 1, 100), 0, %this.getMaxHealth());
	//%this.health = %health;
	//%this.setDamageLevel(%this.getHealthLevel());

	%this.HpRegenSched = %this.schedule(6000, HpRegenSched);
}

// #1.2
function getDeathMessage(%dmgType)
{
	if(%n = $RFW::DeathMessageCount[%dmgType])
	{
		return $RFW::DeathMessage[%dmgType, getRandom(0, %n)];
	}
	else
	{
		return getTaggedString($DeathMessage_Murder[%dmgType]);
	}
}

// #1.3
$RFW::nodeList = "copHat knitHat scoutHat plume triPlume septPlume flareHelmet plumeHelmet helmet visor headSkin chest femChest shoulderPads epaulets epauletsRankA epauletsRankB epauletsRankC epauletsRankD pack armor cape quiver tank bucket lArm lArmSlim rArm rArmSlim lHand lHook rHand rHook pants skirtHip lShoe lPeg rShoe rPeg skirtTrimLeft skirtTrimRight";

function Player::Butcher(%this, %acc)
{
	if(%this.unbutcherable)
	{
		return;
	}
	serverPlay3d("fleshHitSound" @ getRandom(1, 3), %this.getPosition());
	if(%this.butcherHits < 3)
	{
		%this.butcherHits++;
		return;
	}
	if(%this.butcherHits == 3)
	{
		%this.butcherHits = 4;
		if(%this.getDatablock().shapeFile $= "base/data/shapes/player/m.dts")
		{
			%nodeCount = getWordCount($RFW::nodeList);
		}
		else
		{
			%this.setNodeColor("ALL", "0.8 0 0 1");
		}
		for(%i = 0; %i < %nodeCount; %i++)
		{
			%node = getWord($RFW::nodeList, %i);
			if(%this.isNodeVisible(%node))
			{
				%this.nodeList = %this.nodeList SPC %node;
			}
		}
		%this.nodeList = ltrim(%this.nodeList);
	}
	%r = getRandom(0, getWordCount(%this.nodeList) - 1);
	%node = getWord(%this.nodeList, %r);
	%this.nodeList = removeWord(%this.nodeList, %r);
	if(%node $= "chest" || %node $= "femChest")
	{
		%this.setDecalName("AAA-None");
		%this.setNodeColor(%node, "0.8 0 0 1");
	}
	else if(%node $= "headSkin")
	{
		%this.setFaceName("asciiTerror");
		%this.setNodeColor(%node, "0.8 0 0 1");
	}
	else if(%node $= "pants" || %node $= "skirtHip")
	{
		%this.setNodeColor(%node, "0.8 0 0 1");
	}
	else if(%node $= "copHat" || %node $= "knitHat" || %node $= "scoutHat" || %node $= "plume" || %node $= "triPlume" || %node $= "septPlume" || %node $= "flareHelmet" || %node $= "plumeHelmet" || %node $= "helmet" || %node $= "visor" || %node $= "pack" || %node $= "cape" || %node $= "armor" || %node $= "quiver" || %node $= "tank" || %node $= "bucket")
	{
		%this.hideNode(%node);
	}
	else if(%node $= "lHook" || %node $= "rHook" || $node $= "lPeg" || %node $= "rPeg")
	{
		%this.hideNode(%node);
		%acc.addItem("Wood\t1\t0", true);
		return;
	}
	else
	{
		%this.hideNode(%node);
	}
	%loot = %this.butcherLoot;
	if(getFieldCount(%loot) == 0)
	{
		if(!strLen(%this.loot[0]) && getWordCount(%this.nodeList) == 0)
		{
			if(%this.finalButchery++ == 3)
			{
				%this.spawnExplosion(deathProjectile, getWord(%this.getScale(), 2));
				%this.delete();
			}
		}
		return;
	}
	%r = getRandom(0, getFieldCount(%loot) - 1);
	%lootField = getField(%loot, %r);
	%rem = firstWord(%lootField);
	%amt = getRandom(1, %rem);
	%rem = %rem - %amt;
	%lootField = setWord(%lootField, 0, %rem);
	if(%rem > 0)
	{
		%this.butcherLoot = setField(%loot, %r, %lootField);
	}
	else
	{
		%this.butcherLoot = removeField(%loot, %r);
	}
	%acc.addItem(restWords(%lootField) TAB %amt TAB "", true);
}

function Player::SetButcherLoot(%obj, %flesh, %blood, %bone)
{
	if(%flesh > 0)
	{
		%flesh = %flesh SPC "Flesh";
	}
	else
	{
		%flesh = "";
	}
	if(%blood > 0)
	{
		if(%obj.sanguification > 0)
		{
			%amt = %blood * mClampF(%obj.sanguification, 0, 1);
			if(%amt < %blood)
			{
				%blood = (%blood - %amt) @ " Blood\t" @ %amt @ " Sanguite";
			}
			else
			{
				%blood = %blood SPC "Sanguite";
			}
		}
		else
		{
			%blood = %blood SPC "Blood";
		}
	}
	else
	{
		%blood = "";
	}
	if(%bone > 0)
	{
		if(%obj.ossification > 0)
		{
			%amt = %bone * mClampF(%obj.ossification, 0, 1);
			if(%amt < %bone)
			{
				%bone = (%bone - %amt) @ " Bone\t" @ %amt @ " Calcite";
			}
			else
			{
				%bone = %bone SPC "Calcite";
			}
		}
		else
		{
			%bone = %bone SPC "Bone";
		}
	}
	else
	{
		%bone = "";
	}
	%obj.butcherLoot = trim(%flesh TAB %blood TAB %bone);
}


// #2.
package RFW_MinigameDamage
{
	function minigameCanDamage(%a, %b)
	{
		%p = parent::minigameCanDamage(%a, %b);
		switch$(%a.getClassName())
		{
			case "Projectile": %a = %a.sourceObject;
			case "GameConnection": %a = %a.player;
			case "AIplayer": return true;
		}
		switch$(%b.getClassName())
		{
			case "Projectile": %b = %b.sourceObject;
			case "GameConnection": %b = %b.player;
			case "AIplayer": return true;
		}
		if(isObject(%a) && isObject(%b) && %a != %b)
		{
			if(%a.getClassName() $= "Player" && %b.getClassName() $= "Player")
			{
				%atribe = %a.client.account.read("Tribe");
				%btribe = %b.client.account.read("Tribe");
				return %atribe !$= %btribe;
			}
		}
		return %p;
	}
};
activatePackage("RFW_MinigameDamage");

if(isPackage("RFW_Combat"))
	deactivatePackage("RFW_Combat");

package RFW_Combat
{
	function ShapeBase::Damage(%this, %sourceObject, %position, %damage, %damageType, %wat)
	{
		if(%damage > 0)
		{
			//%this.addDamager(%sourceObject);
			if(%sourceObject.damageHandicap > 0)
				%damage *= %sourceObject.damageHandicap;

			if(%this.damageReduction > 0)
				%damage *= %this.damageReduction;

			if(!isEventPending(%this.HpRegenSched))
				%this.HpRegenSched = %this.schedule(12000, HpRegenSched);
		}
		
		%this.lastDamager = %sourceObject;
		%curExp = %this.exp;
		parent::Damage(%this, %sourceObject, %position, %damage, %damageType, %wat);
		if(%this.getState() $= "dead")
		{
			if(!%this.isDead && (isObject(%sourceClient = %sourceObject.sourceClient) || isObject(%sourceClient = %sourceObject.client)))
			{
				if(%curExp > 0 && isObject(%type = %this.type) && isObject(%acc = %sourceClient.account))
				{
					%sourceClient.addEXP(%curExp);
					%sourceClient.centerPrintQueue("\c3Exp\c2x" @ %curExp @ "/" @ %acc.vMaxExp, 3);
				}
			}

			%this.isDead = true;
		}
	}

	function gameConnection::onDeath(%this, %sObj, %sClient, %dmgType, %dmgArea)
	{
		%obj = %this.player;
		%obj.isDead = true;
		%obj.setButcherLoot(45, 20, 10);
		%msg = getDeathMessage(%dmgType);
		if(isObject(%sClient))
		{
			%msg = strReplace(%msg, "%1", %sClient.name);
		}
		else
		{
			%msg = strReplace(%msg, "%1", strReplace(%obj.lastDamager.type.name, "_", " "));
		}
		%msg = strReplace(%msg, "%2", %this.name);
		messageAll('', %msg);
		//prevent normal death message from showing up
		%dmgType = 0;
		%this.player.lastDirectDamageType = 0;
		parent::onDeath(%this, %sObj, %sClient, %dmgType, %dmgArea);
		%acc = %this.account;
		%i = 0;
		%check = 0;
		while(%i < 3 && %check++ < 100)
		{
			%r = getRandom(0, %acc.read("MaxItems") - 1);
			%itm = %acc.read("Item" @ %r);
			if(%itm !$= "")
			{
				%type = getItemType(%itm);
				if(!$RFW::ItemDrops[%type])
				{
					continue;
				}
				if($RFW::ItemStackable[%type])
				{
					%q = mFloor(getItemQuantity(%itm) * 0.2);
					if(%q == 0)
					{
						continue;
					}
					%itm = %type TAB %q TAB "";
				}
				%acc.removeItem(%itm, true);
				%obj.loot[%i] = %itm;
				%i++;
			}
		}
		//drop all crystal equipment
		%maxItems = %acc.read("MaxItems");
		for(%j = 0; %j < %maxItems; %j++)
		{
			%itm = %acc.read("Item" @ %j);
			if(firstWord(getItemType(%itm)) $= "Crystal")
			{
				%acc.removeItem(%itm, true);
				%obj.loot[%i] = %itm;
				%i++;
			}
		}
		%maxEquips = %acc.read("MaxEquips");
		for(%j = 0; %j < %maxEquips; %j++)
		{
			%itm = %acc.read("Equip" @ %j);
			if(firstWord(getItemType(%itm)) $= "Crystal")
			{
				%acc.set("Equip" @ %j, "");
				%obj.loot[%i] = %itm;
				%i++;
			}
		}
	}

	function Player::ActivateStuff(%this)
	{
		parent::ActivateStuff(%this);
		%scale = getWord(%this.getScale(), 2);
		%start = %this.getEyePoint();
		%targets = conalRaycastM(%start, %this.getEyeVector(), 5 * %scale, 30, $Typemasks::CorpseObjectType);
		%acc = %this.client.account;
		while(isObject(%hit = firstWord(%targets)))
		{
			%targets = restWords(%targets);

			%itm = %hit.loot[0];
			if(%itm !$= "")
			{
				%acc.addItem(%itm, true);
				for(%i = 1; %hit.loot[%i] !$= ""; %i++)
				{
					%hit.loot[%i - 1] = %hit.loot[%i];
				}
				%hit.loot[%i - 1] = "";
			}
		}
	}
};
activatePackage(RFW_Combat);<file_sep>//ReverieFortWars/chests.cs

function RFW_SerializePosition(%pos)
{
	%x = mFloatLength(getWord(%pos, 0) * 2, 0);
	%y = mFloatLength(getWord(%pos, 1) * 2, 0);
	%z = mFloatLength(getWord(%pos, 2) * 3, 0);
	return "x" @ %x @ "y" @ %y @ "z" @ %z;
}

//undo some things
deactivatePackage(TreasureChestPersistencePackage);
deactivatePackage(TreasureChestPackage);

function serverCmdTreasureStatus() { }

//set volumes
brickTreasureChestData.volume = 256;
brickTreasureChestOpenData.volume = 256;

//these functions are already called when the brick is placed
function registerTreasureChest(%obj)
{
	%pos = RFW_SerializePosition(%obj.getPosition());
	if(!strLen($RFW::ChestSlots[%pos]))
	{
		%mat = $RFW::ResourceByColor[%obj.getColorID()];
		$RFW::ChestSlots[%pos] = %slots = mCeil($RFW::ResourceQuality[%mat] / 5);
	}
}

//TODO: ensure this is called on deletion
function unregisterTreasureChest(%obj)
{
	$RFW::ChestSlots[%pos] = "";
	return;

	%realpos = %obj.getPosition();
	%pos = RFW_SerializePosition(%realpos);
	%slots = $RFW::ChestSlots[%pos];
	for(%i = 0; %i < %slots; %i++)
	{
		%itm = $RFW::ChestItem[%pos, %i];
		$RFW::ChestItem[%pos, %i] = "";
		%type = getItemType(%itm);
		if(!%type $= "")
		{
			continue;
		}
		%data = $RFW::ItemDatablock[%type];
		%data = isObject(%data) ? %data : genericItem;
		%item = new Item()
		{
			datablock = %data;
			position = %realpos;
			data = %itm;
		};
		%name = getItemType(%itm);
		if($RFW::ItemStackable[%name])
		{
			%name = %name SPC "x" @ getItemQuantity(%itm);
		}
		%item.setShapeNameDistance(32);
		%item.setShapeName(%name);
		%item.setVelocity(getRandom(-10, 10) SPC getRandom(-10, 10) SPC getRandom(5, 10));
		//TODO: ensure fade and pop are working properly
		%item.schedulePop();
		%item.startFade(1000, 4000, true);
	}
	$RFW::ChestSlots[%pos] = "";
}<file_sep>///////////////
// amdeAI.cs //
///////////////
// reverent version

// table of contents

// #1. basic AI functionality
//   #1.1 amdeAI_call
//   #1.2 amdeAI_Sched (main functionality)
//   #1.3 amdeAI_create
//   #1.4 package
// #2. default AI type

// #1.

// #1.1
function amdeAI_call(%fn, %bot, %a1, %a2, %a3, %a4, %a5)
{
	return call(isFunction("amdeAI_" @ %bot.type.name @ "_" @ %fn) ? "amdeAI_" @ %bot.type.name @ "_" @ %fn : "amdeAI_Default_" @ %fn, %bot, %a1, %a2, %a3, %a4, %a5);
}

// #1.2
function AIPlayer::amdeAI_Sched(%this)
{
	if(!isObject(%this))
	{
		return;
	}
	if(%this.isDead)
	{
		%this.stop();
		return;
	}
	cancel(%this.jumpsched);
	cancel(%this.amdeAI_Sched);
	%this.dismount();
	%schedlen = getRandom(1000, 1500);
	%this.amdeAI_Sched = %this.schedule(%schedlen, amdeAI_Sched);
	%tepos = %this.getEyePoint();
	%tlpos = %this.getPosition();
	%type = %this.type;
	%hasTarget = (isObject(%target = %this.target) && %target.getState() !$= "Dead" && vectorDist(%tepos, %targpos = %target.getPosition()) <= %type.sightrange * 2);
	if(%hasTarget)
	{
		if(amdeAI_call("CanSee", %this, %target))
		{
			//weapon selection
			%dist = vectorDist(%tlpos, %target.getPosition());
			if(%dist > 5)
			{
				//ranged
				if(isObject(%image = nameToID(%type.rangedWeapon)) && %this.getMountedImage(0) != %image)
				{
					%this.updateArm(%image);
					%this.mountImage(%image, 0);
				}
			}
			else if(!%type.rangedOnly)
			{
				//melee
				if(isObject(%image = nameToID(%type.meleeWeapon)) && %this.getMountedImage(0) != %image)
				{
					%this.updateArm(%image);
					%this.mountImage(%image, 0);
				}
				//strafing
				%this.setMoveX(getRandom(-1, 1));
				%this.schedule(%schedlen / 2, clearMoveX);
			}
			if(%type.canMove)
			{
				//chase behavior
				if(%type.rangedOnly || (%dist > 5 && %type.prefersRanged))
				{
					//ranged chase behavior
					if(%dist > %type.effectiveRange + 5)
					{
						//too far away: advance
						%this.setMoveObject(%target);
						switch$(amdeAI_call("Pathfind", %this, %target))
						{
							case "Jump": %this.setJumping(true); %this.schedule(%schedlen / 2, setJumping, false);
							case "Crouch": %this.setCrouching(true); %this.schedule(%schedlen / 2, setCrouching, false);
						}
						//attack
						%this.setAimObject(%target);
					}
					else if(%dist < %type.safeRange)
					{
						//too close: retreat
						if(amdeAI_call("Retreat", %this, %target))
						{
							return;
						}
					}
					else
					{
						//sweet spot: backpedal
						%this.setMoveObject(0);
						%this.stop();
						if(%dist + 5 < %this.effectiveRange)
						{
							%this.setMoveY(-1);
							%this.schedule(%schedlen - 1, clearMoveY);
						}
						//attack
						%this.setAimObject(%target);
					}
				}
				else
				{
					//melee chase behavior
					%this.setMoveObject(%target);
					%this.setAimObject(%target);
					switch$(amdeAI_call("Pathfind", %this, %target))
					{
						case "Jump": %this.setJumping(true); %this.schedule(%schedlen / 2, setJumping, false);
						case "Crouch": %this.setCrouching(true); %this.schedule(%schedlen / 2, setCrouching, false);
					}
				}
			}
			//attack
			amdeAI_call("Attack", %this, %target);
			//store target info
			%this.lastSeenTarget = getSimTime();
			%this.lastSeenTargetPos = %targpos;
			%noWander = true;
		}
		else
		{
			//can't see target
			%this.setImageTrigger(0, 0);
			%this.setImageTrigger(1, 0);
			%this.setMoveObject(0);
			%this.clearAim();
			if(%this.lastSeenTarget + 5000 < getSimTime())
			{
				//lost target
				%hasTarget = false;
				if(isObject(%this.target))
				{
					amdeAI_call("OnLoseTarget", %this, %target);
				}
			}
			else if(%this.canMove)
			{
				%this.setMoveDestination(%this.lastSeenTargetPos);
				%noWander = true;
			}
		}
	}
	if(!%hasTarget)
	{
		//clear attacks
		%this.setImageTrigger(0, 0);
		%this.setImageTrigger(1, 0);
		//clear target info
		%this.lastSeenTarget = 0;
		%this.lastSeenTargetPos = 0;
		%this.target = 0;
		//clear movement
		%this.setMoveObject(0);
		%this.clearMoveX();
		%this.clearAim();
	}
	//this search runs even if it has a target, just in case there's a better target around
	InitContainerRadiusSearch(%tepos, %type.sightrange, $Typemasks::PlayerObjectType);
	while(%hit = ContainerSearchNext())
	{
		if(amdeAI_call("CanSee", %this, %hit) && %hit.getState() !$= "Dead")
		{
			%hpos = %hit.getHackPosition();
			%eyevec = %this.getEyeVector();
			%dvec = vectorNormalize(vectorSub(%hpos, %tepos));
			%nFOV = getVectorAngle(%eyevec, %dvec);
			if(%nFOV <= %type.fov)
			{
				if(amdeAI_call("ShouldAttack", %this, %hit))
				{
					amdeAI_call("OnDetectEnemy", %this, %hit);
					if(!%haveenemy)
					{
						%this.target = %hit;
						if(%this.canMove && (vectorDist(%hpos, %tepos) > 5 && isObject(%this.item[0])))
						{
							%this.setMoveObject(%hit);
						}
						%this.setAimObject(%hit);
						amdeAI_call("OnChase", %this, %hit);
						%haveenemy = true;
					}
				}
				else
				{
					amdeAI_call("OnDetectAlly", %this, %hit);
				}
			}
		}
	}
	if(!%noWander)
	{
		%dest = vectorAdd(%tepos, (getRandom() - 0.5) * 50 SPC (getRandom() - 0.5) * 50 SPC 0);
		while(isObject(firstWord(containerRaycast(%tlpos, %dest, $TypeMasks::FxBrickAlwaysObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::TerrainObjectType | $TypeMasks::VehicleBlockerObjectType, %this))))
		{
			%dest = vectorAdd(%tepos, (getRandom() - 0.5) * 20 SPC (getRandom() - 0.5) * 20 SPC 0);
			if(%i++ > 10) //This is here so if the object is encased in bricks or something the server doesn't crash
			{
				break;
			}
		}
		if(%type.canMove)
		{
			%this.setMoveDestination(%dest);
		}
		else
		{
			%this.setAimLocation(%dest);
		}
	}
}

// #1.3
function amdeAI_Create(%pos, %type)
{
	%bot = new AIplayer()
	{
		position = %pos;
		datablock = playerStandardArmor;
		type = %type;
		exp = $RFW::EnemyTypeExp[%type.getName()];
	};
	%bot.setMoveSlowdown(false);
	%bot.setMoveTolerance(3);
	amdeAI_call("onAdd", %bot);
	%bot.schedule(1, amdeAI_sched);
	%bot.spawnTime = getSimTime();
	%bot.setShapeNameDistance(60);
	return %bot;
}

// #1.4
package amde_AI
{
	// #1.4.1
	function AIplayer::Damage(%this, %sourceObject, %position, %damage, %damageType)
	{
		//%this.addDamager(%sourceObject);
		parent::Damage(%this, %sourceObject, %position, %damage, %damageType);
		if(%this.getState() $= "dead")
		{
			//it ded
			amdeAI_call("OnDeath", %this, %this.getHackPosition());
		}
		else
		{
			amdeAI_call("OnDamage", %this, %sourceObject, %position, %damage, %damageType);
		}
	}
};
activatePackage(amde_AI);

// #2.
new ScriptObject(amdeAI_Default)
{
	name = "Default"; //for function calls

	canMove = true;
	sightRange = 80;
	fov = 180;

	rangedWeapon = woodenBowImage;
	effectiveRange = 35;
	safeRange = 10;
	prefersRanged = false; //advance even if already in effective range?
	rangedOnly = false; //retreat instead of engage in melee combat?

	meleeWeapon = woodenSwordImage;
};

function amdeAI_Default_OnAdd(%bot)
{
	//appearance stuff
	%bot.hideNode("rshoe");
	%bot.hideNode("lshoe");
	%bot.hideNode("chest");
	%bot.hideNode("larm");
	%bot.hideNode("rarm");
	%bot.unhideNode("rpeg");
	%bot.unhideNode("lpeg");
	%bot.unhideNode("femChest");
	%bot.unhideNode("larmslim");
	%bot.unhideNode("rarmslim");
	%bot.setNodeColor("ALL", "1 1 1 1");
}

function amdeAI_Default_Pathfind(%bot, %target)
{
	%oh = %bot.getEyePoint();
	%th = %target.getEyePoint();
	%tl = vectorAdd(%target.getPosition(), "0 0 0.1");
	%typemasks = $Typemasks::InteriorObjectType | $Typemasks::TerrainObjectType;
	%types = $Typemasks::PlayerObjectType | $Typemasks::FXbrickAlwaysObjectType;
	%headRaycast = containerRaycast(%oh, %th, %typemasks | %types, %bot);
	%hit = firstWord(%headRaycast);
	%headCanSee = %hit == %target;
	%legRaycast = containerRaycast(%oh, %tl, %typemasks | %types, %bot);
	%hit = firstWord(%legRaycast);
	%legsCanSee = %hit == %target;
	if(%legsCanSee && !%headCanSee)
	{
		return "Crouch";
	}
	if(%headCanSee && !%legsCanSee)
	{
		return "Jump";
	}
	%oz = getWord(%bot.getPosition(), 2) + 0.1;
	%tz = getWord(%tl, 2);
	%ow = %bot.getWaterCoverage() > 0.5;
	%tw = %target.getWaterCoverage() > 0.5;
	if(%ow && %tw)
	{
		if(%oz < %tz)
		{
			return "Crouch";
		}
		if(%tz < %oz)
		{
			return "Jump";
		}
		return "None";
	}
	if(%tz > %oz + 0.6)
	{
		return "Jump";
	}
	return "None";
}

function amdeAI_Default_ShouldAttack(%bot, %target)
{
	return minigameCanDamage(%bot, %target);
}

function amdeAI_Default_Attack(%bot, %target)
{
	%bot.setImageTrigger(0, 1);
	%bot.schedule(%schedlen * 0.3, setImageTrigger, 1, 1);
	%bot.schedule(%schedlen * 0.7, setImageTrigger, 0, 0);
	%bot.schedule(%schedlen - 1, setImageTrigger, 1, 0);
}

function amdeAI_Default_CanSee(%bot, %target)
{
	%opos = %bot.getEyePoint();
	%th = %target.getEyePoint();
	%tl = %target.getPosition();
	%typemasks = $Typemasks::InteriorObjectType | $Typemasks::TerrainObjectType;
	%types = $Typemasks::PlayerObjectType | $Typemasks::FXbrickAlwaysObjectType;
	%headRaycast = containerRaycast(%opos, %th, %typemasks | %types, %bot);
	%hit = firstWord(%headRaycast);
	if(isObject(%hit) && (%type = %hit.getType()) & %types)
	{
		if(%hit == %target)
		{
			return true;
		}
		else if(%type & $Typemasks::PlayerObjectType)
		{
			return false;
		}
		else //Has to be a brick
		{
			if(!%hit.isRendering() || getWord(getColorIDtable(%hit.colorID), 3) < 1)
			{
				return true;
			}
		}
	}
	%legRaycast = containerRaycast(%opos, %tl, %typemasks, %bot);
	%hit = firstWord(%legRaycast);
	if(isObject(%hit) && (%type = %hit.getType()) & %types)
	{
		if(%hit == %target)
		{
			return true;
		}
		else if(%type & $Typemasks::PlayerObjectType)
		{
			return false;
		}
		else
		{
			if(!%hit.isRendering() || getWord(getColorIDtable(%hit.colorID), 3) < 1)
			{
				return true;
			}
		}
	}
	return false;
}

function amdeAI_Default_OnDetectEnemy(%bot, %target)
{
}

function amdeAI_Default_onChase(%bot, %target)
{
}

function amdeAI_Default_Retreat(%bot, %target)
{
	%bot.setMoveObject(0);
	%bot.clearAim();
	%run = vectorNormalize(vectorSub(setWord(%tlpos, 2, 0), setWord(%target.getPosition(), 2, 0)));
	%run = vectorAdd(%tlpos, vectorScale(%run, 50));
	%bot.setMoveDestination(%run);
	return true;
}

function amdeAI_Default_OnDamage(%bot, %sourceObject, %position, %damage, %damageType)
{
}

function amdeAI_Default_OnDeath(%bot, %pos)
{
}

function amdeAI_Default_OnLoseTarget(%bot, %target)
{
}

function amdeAI_Default_OnDetectAlly(%bot, %ally)
{
}<file_sep>//ReverieFortWars/totems.cs

//TABLE OF CONTENTS

$RFW::TotemRadius = 100;

datablock fxDTSBrickData(totemBrick)
{
	brickFile = "./totem.blb";
	category = "Special";
	subCategory = "Reverie Fort Wars";
	uiName = "Totem";
	iconName = "base/client/ui/brickIcons/2x2x3";
	volume = 512;
};

function totemBrick::onPlant(%this, %obj)
{
	parent::onPlant(%this, %obj);
	if(!RegisterTotem(%obj))
	{
		return;
	}
	%obj.setEmitter(playerTeleportEmitterB);
	%obj.setLight(OrangeDimmerAmbientLight);
}

function totemBrick::onLoadPlant(%this, %obj)
{
	parent::onLoadPlant(%this, %obj);
	RegisterTotem(%obj);
}

function RegisterTotem(%obj)
{
	switch$($RFW::ResourceByColor[%obj.colorID])
	{
		case "Wood":
			%obj.damageReflection = 0.01;
			%obj.tierBonus = 0;
			%obj.HPmult = 2;
			%obj.HPregen = 0;
		case "Stone":
			%obj.damageReflection = 0.01;
			%obj.tierBonus = 1;
			%obj.HPmult = 2;
			%obj.HPregen = 0;
		case "Flesh":
			%obj.damageReflection = 0.02;
			%obj.tierBonus = 0;
			%obj.HPmult = 2;
			%obj.HPregen = 2;
		case "Surtra":
			%obj.damageReflection = 0.015;
			%obj.tierBonus = 0;
			%obj.HPmult = 4;
			%obj.HPregen = 0;
		case "Obsidian":
			%obj.damageReflection = 0.04;
			%obj.tierBonus = 0;
			%obj.HPmult = 2;
			%obj.HPregen = 0;
		case "Calcite":
			%obj.damageReflection = 0.03;
			%obj.tierBonus = 0;
			%obj.HPmult = 4;
			%obj.HPregen = 0;
		case "Nithlite":
			%obj.damageReflection = 0.03;
			%obj.tierBonus = 1;
			%obj.HPmult = 2;
			%obj.HPregen = 2;
		case "Sanguite":
			%obj.damageReflection = 0.035;
			%obj.tierBonus = 1;
			%obj.HPmult = 2;
			%obj.HPregen = 4;
		case "Sinnite":
			%obj.damageReflection = 0.03;
			%obj.tierBonus = 1;
			%obj.HPmult = 4;
			%obj.HPregen = 0;
		case "Crystal":
			%obj.damageReflection = 0.045;
			%obj.tierBonus = 1;
			%obj.HPmult = 4.5;
			%obj.HPregen = 2;
		default: 
			if(isObject(%client = %obj.getGroup().client))
			{
				%obj.damage(%client, "INF");
			}
			else
			{
				%obj.delete();
			}
			return false;
	}
	%brickGroup = %obj.getGroup();
	if(!strLen(%brickGroup.tribe) && isObject(%client = %brickGroup.client))
	{
		%brickGroup.tribe = %client.account.read("Tribe");
	}
	if(isObject(%oldTotem = %brickGroup.totem) && %oldTotem.isPlanted)
	{
		if(isObject(%client = %brickGroup.client))
			%oldTotem.damage(%client, "INF");
		else
			%oldTotem.delete();

		%brickGroup.totem = %obj;
	}
	return true;
}

// wood: 0.5% damage reflection
// stone: increases tier of tools needed to mine, 0.5% damage reflection
// flesh: brick HP regenerates over time, 1% damage reflection
// surtra: doubles brick HP, 0.8% damage reflection
// obsidian: 2% damage reflection
// calcite: doubles brick HP, 1.5% damage reflection
// sanguite: increases tier of tools needed to mine, brick HP regenerates over time, 2.5% damage reflection

function findClosestTotem(%pos)
{
	%closestTotem = 0;
	%closestTotemDist = $RFW::TotemRadius;
	for(%i = 0; %i < mainBrickGroup.getCount(); %i++)
	{
		%totem = mainBrickGroup.getObject(%i).totem;
		if(isObject(%totem))
		{
			%dist = vectorDist(%pos, %totem.getPosition());
			if(%dist < %closestTotemDist)
			{
				%closestTotemDist = %dist;
				%closestTotem = %totem;
			}
		}
	}
	return %closestTotem;
}


function findClosestTotemDist(%pos)
{
	%closestTotemDist = $RFW::TotemRadius;
	for(%i = 0; %i < mainBrickGroup.getCount(); %i++)
	{
		%totem = mainBrickGroup.getObject(%i).totem;
		if(isObject(%totem))
		{
			%dist = vectorDist(%pos, %totem.getPosition());
			if(%dist < %closestTotemDist)
			{
				%closestTotemDist = %dist;
			}
		}
	}
	return %closestTotemDist != $RFW::TotemRadius ? %closestTotemDist : -1;
}<file_sep>//ReverieFortWars/building.cs

//TABLE OF CONTENTS


function serverCmdUseSprayCan(%client, %n)
{
	%n = mClamp(%n, 0, 63);
	%client.currentFxCan = 0;
	%client.currentColor = %n;
	if(isObject(%obj = %client.player))
	{
		if(isObject(%brick = %obj.tempBrick))
		{
			%brick.setColor(%n);
		}
		%acc = %client.account;
		%res = $RFW::ResourceByColor[%n];
		%item = %acc.getItem(%res);
		%obj.updateBottomPrintHUD("Building", %n TAB mFloatLength(getItemQuantity(%item), 1));
	}
}

function serverCmdUseFxCan(%client, %n)
{
	%client.currentFxCan = %n;
}

function serverCmdUndoBrick(%client)
{
	%op = %client.undoStack.pop();
	if(getField(%op, 1) $= "PLANT")
	{
		if(isObject(%brick = getField(%op, 0)))
		{
			%brick.damage(%client, "INF");
		}
	}
	else if(!isObject(getField(%op, 0)) && %op !$= "")
	{
		schedule(0, 0, serverCmdUndoBrick, %client);
	}
}

if(isPackage("RFW_Building"))
	deactivatePackage("RFW_Building");

package RFW_Building
{
	function serverCmdUseInventory(%client, %inv)
	{
		parent::serverCmdUseInventory(%client, %inv);
		if(isObject(%obj = %client.player))
		{
			%q = getItemQuantity(%client.account.getItem($RFW::ResourceByColor[%client.currentColor]));
			%obj.updateBottomPrintHUD("Building", %client.currentColor TAB mFloatLength(%q, 1));
		}
	}

	function serverCmdShiftBrick(%client, %x, %y, %z)
	{
		parent::serverCmdShiftBrick(%client, %x, %y, %z);
		if(!isObject(%client.player.tempBrick))
		{
			return %brick;
		}
		if(isObject(%obj = %client.player))
		{
			%q = getItemQuantity(%client.account.getItem($RFW::ResourceByColor[%client.currentColor]));
			%obj.updateBottomPrintHUD("Building", %client.currentColor TAB mFloatLength(%q, 1));
		}
	}

	function fxDtsBrick::onPlant(%brick)
	{
		Parent::onPlant(%brick);
		%brick.schedule(0, "RFW_Init");
	}

	function serverCmdPlantBrick(%client)
	{
		if(!isObject(%client.player.tempBrick))
		{
			parent::serverCmdPlantBrick(%client);
			return;
		}

		if(isObject(%temp = %client.player.tempBrick) && %temp.getClassName() $= "fxDtsBrick")
		{
			%acc = %client.account;
			%mat = $RFW::ResourceByColor[%client.currentColor];
			%vol = %temp.getVolume();
			%it = %mat TAB %vol;
			if(%acc.hasItemQuantity(%it))
			{
				//find nearest totem
				%pos = %temp.getPosition();
				%closestTotem = 0;
				%closestTotemDist = $RFW::TotemRadius;
				for(%i = 0; %i < mainBrickGroup.getCount(); %i++)
				{
					%totem = mainBrickGroup.getObject(%i).totem;
					if(isObject(%totem))
					{
						%dist = vectorDist(%pos, %totem.getPosition());
						if(%dist < %closestTotemDist)
						{
							%closestTotemDist = %dist;
							%closestTotem = %totem;
						}
					}
				}

				if(isObject(%closestTotem) && getTrustLevel(%client, %closestTotem) < 1)
				{
					messageClient(%client, 'MsgError');
					%client.centerPrintQueue("<color:FF0000>Cannot build in other players' territory.", 3);
					if(isObject(%obj = %client.player))
						%obj.updateBottomPrintHUD("Building", %client.currentColor TAB mFloatLength(getItemQuantity(%acc.getItem(%mat)), 1));

					return;
				}
				else
				{
					%brick = parent::serverCmdPlantBrick(%client);
					if(!isObject(%brick))
						return %brick;

					%brick.setColorFX($RFW::ResourceColorFx[%mat]);
					%brick.mat = %mat;
				}

				if(isObject(%obj = %client.player))
					%obj.updateBottomPrintHUD("Building", %client.currentColor TAB mFloatLength(getItemQuantity(%acc.getItem(%mat)), 1));
			}
			else
			{
				messageClient(%client, 'MsgError');
				%client.centerPrintQueue("<color:FF0000>Not enough " @ %mat @ ".", 3);
				return;
			}
		}

		return %brick;
	}
};
activatePackage(RFW_Building);

function fxDtsBrick::RFW_Init(%brick)
{
	if(!isObject(%client = %brick.client))
		return;

	if(!isObject(%acc = %client.account))
		return;

	%acc.removeItem(%brick.mat TAB %brick.getVolume());
}<file_sep>//ReverieFortWars/datablocks.cs

//TABLE OF CONTENTS
// #1. misc stuff
//   #1.1 player datablock
//   #1.2 generic item
//   #1.3 16x16 brick
// #2. Melee weapons
//   #2.1 general functionality
//   #2.2 Fists
//   #2.3 Swords
//   #2.4 pickaxes
//   #2.5 daggers
// #3. ranged weapons
//   #3.1 bows
//   #3.2 snowball
// #4. magic weapons
//   #4.1 faerie fire
//   #4.2 faerie wand
//   #4.3 photometallisynthesis lens
//   #4.4 pyromancy wand
//   #4.5 ossimetallisynthesis staff
//   #4.6 sanguimetallisynthesis wand
// #5. enemies
//   #5.1 faerie
//   #5.2 skeleton
//   #5.3 horse

// #1.
// #1.1
datablock PlayerData(ReveriePlayer : PlayerStandardArmor)
{
	canJet = false;
	isInvincible = false;

	showEnergyBar = true;
	rechargeRate = 0.01;
	runEnergyDrain = 0.01;

	maxForwardSpeed = 7 * 1.5;
	maxSideSpeed = 6 * 1.5;
	maxBackwardSpeed = 4 * 1.5;

	runForce = 4320;
	jumpForce = 1080;

	maxStepHeight = 1.25;

	jumpSound = ""; //see footsteps.cs

	uiName = "Reverie Player";
};

// #1.2

datablock ItemData(genericItem)
{
	category = "Weapon";
	className = "Weapon";

	shapeFile = "Add-Ons/GameMode_Blockheads_Ruin_Xmas/present.dts";
	mass = 1;
	density = 0.2;
	elasticity = 0.2;
	friction = 0.6;
	emap = true;

	uiName = "generic item";
	iconName = "";
	doColorShift = true;
	colorShiftColor = "1 0 0 1";
	image = hammerImage;

	canDrop = true;
};

// #1.3
datablock fxDTSBrickData(brick16x16Data)
{
	brickFile = "./16x16.blb";
	category = "Bricks";
	subCategory = "16x";
	uiName = "16x16";
	iconName = "Add-Ons/GameMode_ReverieFortWars/models/brick16x16";
};

// #2.
// #2.1
datablock AudioProfile(swordDrawSound)
{
	filename = "Add-Ons/Weapon_Sword/swordDraw.wav";
	description = AudioClosest3d;
	preload = true;
};

datablock AudioProfile(swordHitSound)
{
	filename = "Add-Ons/Weapon_Sword/swordHit.wav";
	description = AudioClosest3d;
	preload = true;
};

datablock ParticleData(swordExplosionParticle)
{
	dragCoefficient = 4.5;
	gravityCoefficient = 3.5;
	inheritedVelFactor = 0.4;
	constantAcceleration = 0;
	spinRandomMin = -90;
	spinRandomMax = 90;
	lifetimeMS = 350;
	lifetimeVarianceMS = 300;
	textureName = "base/data/particles/chunk";
	colors[0] = "0.88 0.76 0.0 1.0";
	colors[1] = "0.88 0.76 0.0 1.0";
	sizes[0] = 0.1;
	sizes[1] = 0.0;
};

datablock ParticleEmitterData(swordExplosionEmitter)
{
	ejectionPeriodMS = 8;
	periodVarianceMS = 0;
	ejectionVelocity = 15;
	velocityVariance = 7.5;
	ejectionOffset = 0.0;
	thetaMin = 0;
	thetaMax = 50;
	phiReferenceVel= 0;
	phiVariance= 360;
	overrideAdvance = false;
	particles = "swordExplosionParticle";

	uiName = "Sword Hit";
};

datablock ExplosionData(swordExplosion)
{
	lifeTimeMS = 200;

	//soundProfile = swordHitSound;

	particleEmitter = swordExplosionEmitter;
	particleDensity = 25;
	particleRadius = 0.2;

	faceViewer = true;
	explosionScale = "1 1 1";

	shakeCamera = true;
	camShakeFreq = "20.0 22.0 20.0";
	camShakeAmp = "0.2 0.2 0.2";
	camShakeDuration = 0.5;
	camShakeRadius = 5.0;

	lightStartRadius = 3;
	lightEndRadius = 0;
	lightStartColor = "0.88 0.76 0.21";
	lightEndColor = "0 0 0";
};

datablock ProjectileData(swordProjectile)
{
	directDamage = 35;
	directDamageType = $DamageType::Sword;
	radiusDamageType = $DamageType::Sword;
	explosion = swordExplosion;

	muzzleVelocity = 50;
	velInheritFactor = 1;

	armingDelay = 0;
	lifetime = 100;
	fadeDelay = 70;
	bounceElasticity = 0;
	bounceFriction = 0;
	isBallistic = false;
	gravityMod = 0;

	hasLight = false;
	lightRadius = 3;
	lightColor= "0.88 0.76 0.21";

	uiName = "Sword Hit";
};

function ShapeBaseImageData::DoMeleeStrike(%this, %obj, %slot, %angle, %range, %damage, %proj, %sound, %backstab)
{
	%scale = getWord(%obj.getScale(), 2);
	%start = %obj.getEyePoint();
	%targets = conalRaycastM(%start, %obj.getEyeVector(), %range * %scale, %angle, $Typemasks::PlayerObjectType | $Typemasks::CorpseObjectType, %obj);
	for(%i = 0; (%hit = getWord(%targets, %i)) !$= ""; %i++)
	{
		if(%hit.isDead)
		{
			if(isObject(%proj))
			{
				%hit.spawnExplosion(%proj, %scale);
			}
			%hit.butcher(%obj.client.account);
			%hitcount++;
		}
		else if(minigameCanDamage(%obj, %hit))
		{
			if(isObject(%proj))
			{
				%hit.spawnExplosion(%proj, %scale);
			}
			if(isObject(%sound))
			{
				serverPlay3D(%sound, %hit.getPosition());
			}
			if(minigameCanDamage(%obj, %hit))
			{
				if(%backstab && vectorDot(%obj.getForwardVector(), %hit.getForwardVector()) > 0.25)
				{
					%hit.damage(%obj, %hit.getPosition(), 2 * %damage * %scale, %this.damageType);
				}
				else
				{
					%hit.damage(%obj, %hit.getPosition(), %damage * %scale, %this.damageType);
				}
			}
			%hitcount++;
		}
	}
	if(%hitcount == 0)
	{
		%typemasks = $Typemasks::VehicleObjectType | $Typemasks::TerrainObjectType | $Typemasks::FXbrickAlwaysObjectType | $TypeMasks::StaticShapeObjectType;
		%end = vectorAdd(%start, vectorScale(%obj.getMuzzleVector(%slot), %range * %scale));
		%raycast = containerRaycast(%start, %end, %typemasks, %obj);
		while(isObject(%hit = getWord(%raycast, 0)))
		{
			%crashCheck++;
			if(%crashCheck > 20)
			{
				error("DoMeleeStrike crash check failed");
				break;
			}
			%pos = posFromRaycast(%raycast);
			%normal = normalFromRaycast(%raycast);
			if(%hit.getClassName() $= "fxDtsBrick" && %this.isPickaxe)
			{
				if(%hit.isColliding())
				{
					%hit.damage(%obj, %damage * 5 * %scale, %this.tier, %pos, %normal);
				}
				else
				{
					%start = posFromRaycast(%raycast);
					if(%start $= "")
					{
						break;
					}
					else
					{
						%raycast = containerRaycast(posFromRaycast(%raycast), %end, %typemasks, %hit);
						%hit = firstWord(%raycast);
						continue;
					}
				}
			}
			if(isObject(%proj))
			{
				%p = new Projectile()
				{
					datablock = %proj;
					initialPosition = %pos;
					initialVelocity = %normal;
					scale = %scale SPC %scale SPC %scale;
					sourceSlot = %slot;
					sourceObject = %obj;
				};
				%p.explode();
			}
			if(isObject(%sound))
			{
				serverPlay3D(%sound, posFromRaycast(%raycast));
			}
			break;
		}
	}
}

// #2.2
datablock ItemData(fistsItem)
{
	category = "Weapon";
	className = "Weapon";

	shapeFile = "Add-Ons/Gamemode_Rampage/rHand.dts";
	mass = 1;
	density = 0.2;
	elasticity = 0.2;
	friction = 0.6;
	emap = true;

	uiName = "Fists";
	iconName = "Add-Ons/Gamemode_ReverieFortWars/models/icon_Fists";
	doColorShift = true;
	colorShiftColor = "1 1 1 0.7";

	image = fistsImage;
	canDrop = false;
};

datablock ShapeBaseImageData(fistsImage)
{
	shapeFile = fistsItem.shapeFile;
	emap = true;

	mountPoint = 0;
	offset = "0 0 0";

	correctMuzzleVector = false;

	eyeOffset = "0 0 0";

	className = "WeaponImage";

	item = fistsItem;
	ammo = " ";
	projectile = "";
	projectileType = Projectile;

	melee = true;
	doRetraction = false;
	armReady = true;
	isPickaxe = true;

	doColorShift = true;
	colorShiftColor = fistsItem.colorShiftColor;

	stateName[0] = "Activate";
	stateTimeoutValue[0] = 0.5;
	stateTransitionOnTimeout[0] = "Ready";

	stateName[1] = "Ready";
	stateTransitionOnTriggerDown[1] = "PreFire";
	stateAllowImageChange[1] = true;

	stateName[2] = "PreFire";
	stateScript[2] = "onPreFire";
	stateAllowImageChange[2] = false;
	stateTimeoutValue[2] = 0.1;
	stateTransitionOnTimeout[2] = "Fire";

	stateName[3] = "Fire";
	stateTransitionOnTimeout[3] = "StopFire";
	stateTimeoutValue[3] = 0.2;
	stateFire[3] = true;
	stateAllowImageChange[3] = false;
	stateSequence[3] = "Fire";
	stateScript[3] = "onFire";
	stateWaitForTimeout[3] = true;

	stateName[5] = "StopFire";
	stateTransitionOnTimeout[5] = "Ready";
	stateTimeoutValue[5] = 0.1;
	stateAllowImageChange[5] = false;
	stateWaitForTimeout[5] = true;
	stateSequence[5] = "StopFire";
	stateScript[5] = "onStopFire";
};

function fistsImage::onPreFire(%this, %obj, %slot)
{
	%obj.playthread(2, armattack);
}

function fistsImage::onFire(%this, %obj, %slot)
{
	%this.DoMeleeStrike(%obj, %slot, 15, 3, 6, %this.projectile);
}

function fistsImage::onStopFire(%this, %obj, %slot)
{
	%obj.playthread(2, root);
}

// #2.3
// Wooden Sword declaration
datablock ItemData(WoodenSwordItem)
{
	category = "Weapon";
	className = "Weapon";

	shapeFile = "Add-Ons/Gamemode_ReverieFortWars/models/sword-2.dts";
	mass = 1;
	density = 0.2;
	elasticity = 0.2;
	friction = 0.6;
	emap = true;

	uiName = "Wooden Sword";
	iconName = "Add-Ons/Gamemode_ReverieFortWars/models/icon_sword-2";
	doColorShift = true;
	colorShiftColor = "0.81 0.69 0.56 1";

	image = woodenSwordImage;
	canDrop = true;
};

datablock ShapeBaseImageData(WoodenSwordImage)
{
	shapeFile = woodenSwordItem.shapeFile;
	emap = true;

	mountPoint = 0;
	offset = "0 0 0";

	correctMuzzleVector = false;

	eyeOffset = "0 0 0";

	className = "WeaponImage";

	item = WoodenSwordItem;
	tier = 0;
	ammo = " ";
	projectile = swordProjectile;
	damageType = $DamageType::Sword;
	soundProfile = swordHitSound;
	projectileType = Projectile;

	melee = true;
	doRetraction = false;
	armReady = true;

	doColorShift = true;
	colorShiftColor = WoodenSwordItem.colorShiftColor;

	stateName[0] = "Activate";
	stateTimeoutValue[0] = 0.5;
	stateTransitionOnTimeout[0] = "Ready";
	stateSound[0] = weaponSwitchSound;

	stateName[1] = "Ready";
	stateTransitionOnTriggerDown[1] = "PreFire";
	stateAllowImageChange[1] = true;

	stateName[2] = "PreFire";
	stateScript[2] = "onPreFire";
	stateAllowImageChange[2] = false;
	stateTimeoutValue[2] = 0.1;
	stateTransitionOnTimeout[2] = "Fire";

	stateName[3] = "Fire";
	stateTransitionOnTimeout[3] = "CheckFire";
	stateTimeoutValue[3]= 0.2;
	stateFire[3] = true;
	stateAllowImageChange[3] = false;
	stateSequence[3] = "Fire";
	stateScript[3] = "onFire";
	stateWaitForTimeout[3] = true;

	stateName[4] = "CheckFire";
	stateTransitionOnTriggerUp[4]	= "StopFire";
	stateTransitionOnTriggerDown[4] = "Fire";

	stateName[5] = "StopFire";
	stateTransitionOnTimeout[5] = "Ready";
	stateTimeoutValue[5] = 0.2;
	stateAllowImageChange[5] = false;
	stateWaitForTimeout[5] = true;
	stateSequence[5] = "StopFire";
	stateScript[5] = "onStopFire";
};

function woodenSwordImage::onPreFire(%this, %obj, %slot)
{
	%obj.playthread(2, armattack);
}

function woodenSwordImage::onStopFire(%this, %obj, %slot)
{	
	%obj.playthread(2, root);
}

function woodenSwordImage::onFire(%this, %obj, %slot)
{
	%this.DoMeleeStrike(%obj, %slot, 10, 3, 10, %this.projectile, swordHitSound);
}

// Stone Sword declaration
datablock ItemData(StoneSwordItem : WoodenSwordItem)
{
	shapeFile = "Add-Ons/Gamemode_ReverieFortWars/models/sword-1.dts";
	iconName = "Add-Ons/Gamemode_ReverieFortWars/models/icon_sword-1";
	colorShiftColor = "0.5 0.5 0.5 1";
	uiName = "Stone Sword";
	image = StoneSwordImage;
};

datablock ShapeBaseImageData(StoneSwordImage : WoodenSwordImage)
{
	shapeFile = stoneSwordItem.shapeFile;
	colorShiftcolor = stoneSwordItem.colorShiftColor;
	tier = 1;
	item = stoneSwordItem;
	isPickaxe = false;
};

function StoneSwordImage::onPreFire(%this, %obj, %slot)
{
	%obj.playthread(2, armattack);
}

function StoneSwordImage::onStopFire(%this, %obj, %slot)
{	
	%obj.playthread(2, root);
}

function StoneSwordImage::onFire(%this, %obj, %slot)
{
	%this.DoMeleeStrike(%obj, %slot, 10, 3, 12, %this.projectile, swordHitSound);
}

// Bone Sword declaration
datablock ItemData(BoneSwordItem : WoodenSwordItem)
{
	shapeFile = "Add-Ons/Gamemode_ReverieFortWars/models/sword-1.dts";
	iconName = "Add-Ons/Gamemode_ReverieFortWars/models/icon_sword-1";
	colorShiftColor = "0.8 0.8 0.8 1 1";
	uiName = "Bone Sword";
	image = BoneSwordImage;
};

datablock ShapeBaseImageData(BoneSwordImage : WoodenSwordImage)
{
	shapeFile = BoneSwordItem.shapeFile;
	colorShiftcolor = BoneSwordItem.colorShiftColor;
	tier = 1;
	item = BoneSwordItem;
	isPickaxe = false;
};

function BoneSwordImage::onPreFire(%this, %obj, %slot)
{
	%obj.playthread(2, armattack);
}

function BoneSwordImage::onStopFire(%this, %obj, %slot)
{	
	%obj.playthread(2, root);
}

function BoneSwordImage::onFire(%this, %obj, %slot)
{
	%this.DoMeleeStrike(%obj, %slot, 10, 3, 12, %this.projectile, swordHitSound);
}

// Obsidian Sword declaration / Macahuitl decalaration
datablock ItemData(MacahuitlItem : WoodenSwordItem)
{
	shapeFile = "Add-Ons/Gamemode_ReverieFortWars/models/Macahuitl.dts";
	iconName = "Add-Ons/Gamemode_ReverieFortWars/models/icon_Macahuitl";
	colorShiftColor = woodenSwordItem.colorShiftColor;
	uiName = "Obsidian Sword";
	image = MacahuitlImage;
};

datablock ShapeBaseImageData(MacahuitlImage : WoodenSwordImage)
{
	shapeFile = MacahuitlItem.shapeFile;
	colorShiftcolor = MacahuitlItem.colorShiftColor;
	tier = 2;
	item = MacahuitlItem;
	isPickaxe = false;
};

function MacahuitlImage::onPreFire(%this, %obj, %slot)
{
	%obj.playthread(2, armattack);
}

function MacahuitlImage::onStopFire(%this, %obj, %slot)
{	
	%obj.playthread(2, root);
}

function MacahuitlImage::onFire(%this, %obj, %slot)
{
	%this.DoMeleeStrike(%obj, %slot, 10, 3, getRandom() < 0.2 ? 30 : 15, %this.projectile);
}

// Surtra Sword declaration
datablock ItemData(SurtraSwordItem : WoodenSwordItem)
{
	shapeFile = "Add-Ons/Gamemode_ReverieFortWars/models/sword-1.dts";
	iconName = "Add-Ons/Gamemode_ReverieFortWars/models/icon_sword-1";
	colorShiftColor = "0.31 0.29 0.25 1";
	uiName = "Surtra Sword";
	image = SurtraSwordImage;
};

datablock ShapeBaseImageData(SurtraSwordImage : WoodenSwordImage)
{
	shapeFile = SurtraSwordItem.shapeFile;
	colorShiftcolor = SurtraSwordItem.colorShiftColor;
	tier = 2;
	item = SurtraSwordItem;
	isPickaxe = false;
	stateSound[0] = swordDrawSound;
};

function SurtraSwordImage::onPreFire(%this, %obj, %slot)
{
	%obj.playthread(2, armattack);
}

function SurtraSwordImage::onStopFire(%this, %obj, %slot)
{	
	%obj.playthread(2, root);
}

function SurtraSwordImage::onFire(%this, %obj, %slot)
{
	%this.DoMeleeStrike(%obj, %slot, 10, 3, 15, %this.projectile, swordHitSound);
}

// Calcite Sword declaration
datablock ItemData(CalciteSwordItem : WoodenSwordItem)
{
	shapeFile = "Add-Ons/Weapon_Sword/sword.dts";
	iconName = "Add-Ons/Gamemode_ReverieFortWars/models/icon_sword";
	colorShiftColor = "0.87 0.8 0.57 1";
	uiName = "Calcite Sword";
	image = CalciteSwordImage;
};

datablock ShapeBaseImageData(CalciteSwordImage : WoodenSwordImage)
{
	shapeFile = CalciteSwordItem.shapeFile;
	colorShiftcolor = CalciteSwordItem.colorShiftColor;
	tier = 3;
	item = CalciteSwordItem;
	isPickaxe = false;
	stateSound[0] = swordDrawSound;
};

function CalciteSwordImage::onPreFire(%this, %obj, %slot)
{
	%obj.playthread(2, armattack);
}

function CalciteSwordImage::onStopFire(%this, %obj, %slot)
{	
	%obj.playthread(2, root);
}

function CalciteSwordImage::onFire(%this, %obj, %slot)
{
	%this.DoMeleeStrike(%obj, %slot, 10, 3, 20, %this.projectile, swordHitSound);
}

// Nithlite Sword declaration
datablock ItemData(NithliteSwordItem : WoodenSwordItem)
{
	shapeFile = "Add-Ons/Gamemode_ReverieFortWars/models/swordalt.dts";
	iconName = "Add-Ons/Gamemode_ReverieFortWars/models/icon_swordalt";
	colorShiftColor = "0.96 0.75 0.89 1";
	uiName = "Nithlite Sword";
	image = NithliteSwordImage;
};

datablock ShapeBaseImageData(NithliteSwordImage : WoodenSwordImage)
{
	shapeFile = NithliteSwordItem.shapeFile;
	colorShiftcolor = NithliteSwordItem.colorShiftColor;
	tier = 3;
	item = NithliteSwordItem;
	isPickaxe = false;
	stateSound[0] = swordDrawSound;
};

function NithliteSwordImage::onPreFire(%this, %obj, %slot)
{
	%obj.playthread(2, armattack);
}

function NithliteSwordImage::onStopFire(%this, %obj, %slot)
{	
	%obj.playthread(2, root);
}

function NithliteSwordImage::onFire(%this, %obj, %slot)
{
	%this.DoMeleeStrike(%obj, %slot, 12, 3, 20, %this.projectile, swordHitSound);
}

// Sanguite Sword declaration
datablock ItemData(SanguiteSwordItem : WoodenSwordItem)
{
	shapeFile = "Add-Ons/Gamemode_ReverieFortWars/models/sword+1.dts";
	iconName = "Add-Ons/Gamemode_ReverieFortWars/models/icon_sword+1";
	colorShiftColor = "0.56 0.03 0.03 1";
	uiName = "Sanguite Sword";
	image = SanguiteSwordImage;
};

datablock ShapeBaseImageData(SanguiteSwordImage : WoodenSwordImage)
{
	shapeFile = SanguiteSwordItem.shapeFile;
	colorShiftcolor = SanguiteSwordItem.colorShiftColor;
	tier = 4;
	item = SanguiteSwordItem;
	isPickaxe = false;
	stateSound[0] = swordDrawSound;
};

function SanguiteSwordImage::onPreFire(%this, %obj, %slot)
{
	%obj.playthread(2, armattack);
}

function SanguiteSwordImage::onStopFire(%this, %obj, %slot)
{	
	%obj.playthread(2, root);
}

function SanguiteSwordImage::onFire(%this, %obj, %slot)
{
	%this.DoMeleeStrike(%obj, %slot, 15, 3, 25, %this.projectile, swordHitSound);
}

// Sinnite Sword declaration
datablock ItemData(SinniteSwordItem : WoodenSwordItem)
{
	shapeFile = "Add-Ons/Gamemode_ReverieFortWars/models/sword+1.dts";
	iconName = "Add-Ons/Gamemode_ReverieFortWars/models/icon_sword+1";
	colorShiftColor = "0.54 0.50 0.43 1";
	uiName = "Sinnite Sword";
	image = SinniteSwordImage;
};

datablock ShapeBaseImageData(SinniteSwordImage : WoodenSwordImage)
{
	shapeFile = SinniteSwordItem.shapeFile;
	colorShiftcolor = SinniteSwordItem.colorShiftColor;
	tier = 4;
	item = SinniteSwordItem;
	isPickaxe = false;
	stateSound[0] = swordDrawSound;
};

function SinniteSwordImage::onPreFire(%this, %obj, %slot)
{
	%obj.playthread(2, armattack);
}

function SinniteSwordImage::onStopFire(%this, %obj, %slot)
{	
	%obj.playthread(2, root);
}

function SinniteSwordImage::onFire(%this, %obj, %slot)
{
	%this.DoMeleeStrike(%obj, %slot, 18, 3, 25, %this.projectile, swordHitSound);
}

// Sinnite Sword declaration
datablock ItemData(CrystalSwordItem : WoodenSwordItem)
{
	shapeFile = "Add-Ons/Gamemode_ReverieFortWars/models/sword+1.dts";
	iconName = "Add-Ons/Gamemode_ReverieFortWars/models/icon_sword+1";
	colorShiftColor = getColorIdTable($RFW::ResourceColor["Crystal"]);
	uiName = "Crystal Sword";
	image = CrystalSwordImage;
};

datablock ShapeBaseImageData(CrystalSwordImage : WoodenSwordImage)
{
	shapeFile = CrystalSwordItem.shapeFile;
	colorShiftcolor = CrystalSwordItem.colorShiftColor;
	tier = 5;
	item = CrystalSwordItem;
	isPickaxe = false;
	stateSound[0] = swordDrawSound;
};

function CrystalSwordImage::onPreFire(%this, %obj, %slot)
{
	%obj.playthread(2, armattack);
}

function CrystalSwordImage::onStopFire(%this, %obj, %slot)
{	
	%obj.playthread(2, root);
}

function CrystalSwordImage::onFire(%this, %obj, %slot)
{
	%this.DoMeleeStrike(%obj, %slot, 20, 3.5, 25, %this.projectile, swordHitSound);
}

// #2.4

// zzz why did I ever do this
function RFW_RegisterWeapon(%dbName, %uiName, %shapeFile, %iconName, %colorshiftColor, %tier, %hitAngle, %range, %damage, %isPickaxe, %damageType, %sound, %projectile)
{
	eval("datablock ItemData(" @ %dbName @ "Item : woodenSwordItem) { colorShiftColor = \"" @ %colorshiftColor @ "\"; shapeFile = \"" @ %shapeFile @ "\"; iconName = \"" @ %iconName @ "\"; uiName = \"" @ %uiName @ "\"; image = " @ %dbName @ "Image; };");
	eval("datablock ShapeBaseImageData(" @ %dbName @ "Image : woodenSwordImage) { colorShiftColor = \"" @ %colorShiftColor @ "\"; shapeFile = \"" @ %shapeFile @ "\"; item = " @ %dbName @ "Item; tier = " @ %tier @ "; isPickaxe = " @ %isPickaxe @ "; stateSound[0] = " @ (%isPickaxe ? weaponSwitchSound : swordDrawSound) @ "; };");
	if(%damageType !$= "")
	{
		eval(%dbName @ "image.damageType = " @ %damageType @ ";");
	}
	if(%sound !$= "")
	{
		eval(%dbName @ "image.soundProfile = " @ %sound @ ";");
	}
	if(%projectile !$= "")
	{
		eval(%dbName @ "image.projectile = " @ %projectile @ ";");
	}
	eval("function " @ %dbName @ "Image::onPreFire(%this, %obj, %slot) { %obj.playThread(2, armAttack); }");
	eval("function " @ %dbName @ "Image::onStopFire(%this, %obj, %slot) { %obj.playThread(2, root); }");
	eval("function " @ %dbName @ "Image::onFire(%this, %obj, %slot) { %this.DoMeleeStrike(%obj, %slot, " @ %hitAngle @ ", " @ %range @ ", " @ %damage @ ", %this.projectile); }");
}

RFW_RegisterWeapon("stonePickaxe", "Stone Pickaxe", "Add-Ons/Gamemode_ReverieFortWars/models/stonepickaxe.dts", "Add-Ons/Gamemode_ReverieFortWars/models/StonePickaxe", "0.5 0.5 0.5 1", 1, 15, 3.5, 10, true, $DamageType::Pickaxe, hammerHitSound);
RFW_RegisterWeapon("surtraPickaxe", "Surtra Pickaxe", "Add-Ons/Gamemode_ReverieFortWars/models/surtrapickaxe.dts", "Add-Ons/Gamemode_ReverieFortWars/models/SurtraPickaxe", "0.31 0.29 0.25 1", 2, 15, 3.5, 13, true, $DamageType::Pickaxe, hammerHitSound);
RFW_RegisterWeapon("calcitePickaxe", "Calcite Pickaxe", "Add-Ons/Gamemode_ReverieFortWars/models/calcitepickaxe.dts", "Add-Ons/Gamemode_ReverieFortWars/models/CalcitePickaxe", "0.87 0.8 0.57 1", 3, 15, 3.5, 16, true, $DamageType::Pickaxe, hammerHitSound);
RFW_RegisterWeapon("nithlitePickaxe", "Nithlite Pickaxe", "Add-Ons/Gamemode_ReverieFortWars/models/nithlitepickaxe.dts", "Add-Ons/Gamemode_ReverieFortWars/models/NithlitePickaxe", "0.96 0.75 0.89 1", 3, 15, 3.5, 16, true, $DamageType::Pickaxe, hammerHitSound);
RFW_RegisterWeapon("sanguitePickaxe", "Sanguite Pickaxe", "Add-Ons/Gamemode_ReverieFortWars/models/sanguitepickaxe.dts", "Add-Ons/Gamemode_ReverieFortWars/models/SanguitePickaxe", "0.56 0.03 0.03 1", 4, 20, 3.5, 20, true, $DamageType::Pickaxe, hammerHitSound);
RFW_RegisterWeapon("sinnitePickaxe", "Sinnite Pickaxe", "Add-Ons/Gamemode_ReverieFortWars/models/sinnitepickaxe.dts", "Add-Ons/Gamemode_ReverieFortWars/models/SinnitePickaxe", "0.54 0.50 0.43 1", 4, 20, 3.5, 20, true, $DamageType::Pickaxe, hammerHitSound);
RFW_RegisterWeapon("crystalPickaxe", "Crystal Pickaxe", "Add-Ons/Gamemode_ReverieFortWars/models/sinnitepickaxe.dts", "Add-Ons/Gamemode_ReverieFortWars/models/SinnitePickaxe", setWord(crystalSwordItem.colorShiftColor, 3, 1), 4, 20, 3.5, 25, true, $DamageType::Pickaxe, hammerHitSound);

// #2.5

datablock ItemData(SurtraDaggerItem)
{
	category = "Weapon";
	className = "Weapon";

	shapeFile = "Add-Ons/Gamemode_ReverieFortWars/models/dagger-1.dts";
	mass = 1;
	density = 0.2;
	elasticity = 0.2;
	friction = 0.6;
	emap = true;

	uiName = "Surtra dagger";
	iconName = "Add-Ons/Gamemode_ReverieFortWars/models/icon_dagger-1";
	doColorShift = true;
	colorShiftColor = SurtraSwordItem.colorShiftColor;

	image = surtraDaggerImage;
	canDrop = true;
};

datablock ShapeBaseImageData(surtraDaggerImage)
{
	shapeFile = surtraDaggerItem.shapeFile;
	emap = true;

	mountPoint = 0;
	offset = "0 0 0";

	correctMuzzleVector = false;

	eyeOffset = "0 0 0";

	className = "WeaponImage";

	item = surtraDaggerItem;
	tier = 0;
	ammo = " ";
	projectile = swordProjectile;
	damageType = $DamageType::Dagger;
	soundProfile = swordHitSound;
	projectileType = Projectile;

	melee = true;
	doRetraction = false;
	armReady = true;

	doColorShift = true;
	colorShiftColor = surtraDaggerItem.colorShiftColor;

	stateName[0] = "Activate";
	stateTimeoutValue[0] = 0.1;
	stateTransitionOnTimeout[0]	= "Ready";
	stateSequence[0] = "ready";

	stateName[1] = "Ready";
	stateTransitionOnTriggerDown[1] = "Charge";
	stateAllowImageChange[1] = true;
	
	stateName[2] = "Charge";
	stateTransitionOnTimeout[2] = "Armed";
	stateTimeoutValue[2] = 0.7;
	stateWaitForTimeout[2] = false;
	stateTransitionOnTriggerUp[2] = "AbortCharge";
	stateScript[2] = "onCharge";
	stateAllowImageChange[2] = false;
	
	stateName[3] = "AbortCharge";
	stateTransitionOnTimeout[3] = "Ready";
	stateTimeoutValue[3] = 0.3;
	stateWaitForTimeout[3] = true;
	stateScript[3] = "onAbortCharge";
	stateAllowImageChange[3] = false;

	stateName[4] = "Armed";
	stateTransitionOnTriggerUp[4] = "Fire";
	stateAllowImageChange[4] = false;

	stateName[5] = "Fire";
	stateTransitionOnTimeout[5] = "Ready";
	stateTimeoutValue[5] = 0.5;
	stateFire[5] = true;
	stateSequence[5] = "fire";
	stateScript[5] = "onFire";
	stateWaitForTimeout[5] = true;
	stateAllowImageChange[5] = false;
	stateSound[5] = spearFireSound;
};

function surtraDaggerImage::onCharge(%this, %obj, %slot)
{
	%obj.playthread(2, spearReady);
}

function surtraDaggerImage::onAbortCharge(%this, %obj, %slot)
{
	%obj.playthread(2, spearThrow);
	%this.DoMeleeStrike(%obj, %slot, 15, 3, 10, %this.projectile, swordHitSound, true);
}

function surtraDaggerImage::onFire(%this, %obj, %slot)
{
	%obj.playthread(2, spearThrow);
	%this.DoMeleeStrike(%obj, %slot, 15, 3, 30, %this.projectile, swordHitSound, true);
}

datablock ItemData(ObsidianDaggerItem : SurtraDaggerItem)
{
	shapeFile = "Add-Ons/Gamemode_ReverieFortWars/models/dagger-1.dts";
	iconName = "Add-Ons/Gamemode_ReverieFortWars/models/icon_dagger-1";
	uiName = "Obsidian dagger";
	colorShiftColor = "0.1 0.1 0.1 1";
	image = ObsidianDaggerImage;
};

datablock ShapeBaseImageData(ObsidianDaggerImage : SurtraDaggerImage)
{
	shapeFile = ObsidianDaggerItem.shapeFile;
	item = ObsidianDaggerItem;
	colorShiftColor = ObsidianDaggerItem.colorShiftColor;
};

function ObsidianDaggerImage::onCharge(%this, %obj, %slot)
{
	%obj.playthread(2, spearReady);
}

function ObsidianDaggerImage::onAbortCharge(%this, %obj, %slot)
{
	%obj.playthread(2, spearThrow);
	%this.DoMeleeStrike(%obj, %slot, 15, 3, 10, %this.projectile, swordHitSound, true);
}

function ObsidianDaggerImage::onFire(%this, %obj, %slot)
{
	%obj.playthread(2, spearThrow);
	%this.DoMeleeStrike(%obj, %slot, 15, 3, 30, %this.projectile, swordHitSound, true);
}

datablock ItemData(CalciteDaggerItem : SurtraDaggerItem)
{
	shapeFile = "Add-Ons/Gamemode_ReverieFortWars/models/dagger-1.dts";
	iconName = "Add-Ons/Gamemode_ReverieFortWars/models/icon_dagger-1";
	uiName = "Calcite dagger";
	colorShiftColor = CalciteSwordItem.colorShiftColor;
	image = CalciteDaggerImage;
};

datablock ShapeBaseImageData(CalciteDaggerImage : SurtraDaggerImage)
{
	shapeFile = CalciteDaggerItem.shapeFile;
	item = CalciteDaggerItem;
	colorShiftColor = CalciteDaggerItem.colorShiftColor;
};

function CalciteDaggerImage::onCharge(%this, %obj, %slot)
{
	%obj.playthread(2, spearReady);
}

function CalciteDaggerImage::onAbortCharge(%this, %obj, %slot)
{
	%obj.playthread(2, spearThrow);
	%this.DoMeleeStrike(%obj, %slot, 20, 3, 15, %this.projectile, swordHitSound, true);
}

function CalciteDaggerImage::onFire(%this, %obj, %slot)
{
	%obj.playthread(2, spearThrow);
	%this.DoMeleeStrike(%obj, %slot, 20, 3, 45, %this.projectile, swordHitSound, true);
}

datablock ItemData(NithliteDaggerItem : SurtraDaggerItem)
{
	shapeFile = "Add-Ons/Gamemode_ReverieFortWars/models/dagger-1.dts";
	iconName = "Add-Ons/Gamemode_ReverieFortWars/models/icon_dagger-1";
	uiName = "Nithlite dagger";
	colorShiftColor = NithliteSwordItem.colorShiftColor;
	image = NithliteDaggerImage;
};

datablock ShapeBaseImageData(NithliteDaggerImage : SurtraDaggerImage)
{
	shapeFile = NithliteDaggerItem.shapeFile;
	item = NithliteDaggerItem;
	colorShiftColor = NithliteDaggerItem.colorShiftColor;
};

function NithliteDaggerImage::onCharge(%this, %obj, %slot)
{
	%obj.playthread(2, spearReady);
}

function NithliteDaggerImage::onAbortCharge(%this, %obj, %slot)
{
	%obj.playthread(2, spearThrow);
	%this.DoMeleeStrike(%obj, %slot, 20, 3, 15, %this.projectile, swordHitSound, true);
}

function NithliteDaggerImage::onFire(%this, %obj, %slot)
{
	%obj.playthread(2, spearThrow);
	%this.DoMeleeStrike(%obj, %slot, 20, 3, 45, %this.projectile, swordHitSound, true);
}

datablock ItemData(SanguiteDaggerItem : SurtraDaggerItem)
{
	shapeFile = "Add-Ons/Gamemode_ReverieFortWars/models/dagger-1.dts";
	iconName = "Add-Ons/Gamemode_ReverieFortWars/models/icon_dagger-1";
	uiName = "Sanguite dagger";
	colorShiftColor = SanguiteSwordItem.colorShiftColor;
	image = SanguiteDaggerImage;
};

datablock ShapeBaseImageData(SanguiteDaggerImage : SurtraDaggerImage)
{
	shapeFile = SanguiteDaggerItem.shapeFile;
	item = SanguiteDaggerItem;
	colorShiftColor = SanguiteDaggerItem.colorShiftColor;
};

function SanguiteDaggerImage::onCharge(%this, %obj, %slot)
{
	%obj.playthread(2, spearReady);
}

function SanguiteDaggerImage::onAbortCharge(%this, %obj, %slot)
{
	%obj.playthread(2, spearThrow);
	%this.DoMeleeStrike(%obj, %slot, 25, 3.5, 20, %this.projectile, swordHitSound, true);
}

function SanguiteDaggerImage::onFire(%this, %obj, %slot)
{
	%obj.playthread(2, spearThrow);
	%this.DoMeleeStrike(%obj, %slot, 25, 3.5, 60, %this.projectile, swordHitSound, true);
}

datablock ItemData(SinniteDaggerItem : SurtraDaggerItem)
{
	shapeFile = "Add-Ons/Gamemode_ReverieFortWars/models/dagger-1.dts";
	iconName = "Add-Ons/Gamemode_ReverieFortWars/models/icon_dagger-1";
	uiName = "Sinnite dagger";
	colorShiftColor = SinniteSwordItem.colorShiftColor;
	image = SinniteDaggerImage;
};

datablock ShapeBaseImageData(SinniteDaggerImage : SurtraDaggerImage)
{
	shapeFile = SinniteDaggerItem.shapeFile;
	item = SinniteDaggerItem;
	colorShiftColor = SinniteDaggerItem.colorShiftColor;
};

function SinniteDaggerImage::onCharge(%this, %obj, %slot)
{
	%obj.playthread(2, spearReady);
}

function SinniteDaggerImage::onAbortCharge(%this, %obj, %slot)
{
	%obj.playthread(2, spearThrow);
	%this.DoMeleeStrike(%obj, %slot, 25, 3.5, 20, %this.projectile, swordHitSound, true);
}

function SinniteDaggerImage::onFire(%this, %obj, %slot)
{
	%obj.playthread(2, spearThrow);
	%this.DoMeleeStrike(%obj, %slot, 25, 3.5, 60, %this.projectile, swordHitSound, true);
}

datablock ItemData(CrystalDaggerItem : SurtraDaggerItem)
{
	shapeFile = "Add-Ons/Gamemode_ReverieFortWars/models/dagger-1.dts";
	iconName = "Add-Ons/Gamemode_ReverieFortWars/models/icon_dagger-1";
	uiName = "Crystal dagger";
	colorShiftColor = CrystalSwordItem.colorShiftColor;
	image = CrystalDaggerImage;
};

datablock ShapeBaseImageData(CrystalDaggerImage : SurtraDaggerImage)
{
	shapeFile = CrystalDaggerItem.shapeFile;
	item = CrystalDaggerItem;
	colorShiftColor = CrystalDaggerItem.colorShiftColor;
};

function CrystalDaggerImage::onCharge(%this, %obj, %slot)
{
	%obj.playthread(2, spearReady);
}

function CrystalDaggerImage::onAbortCharge(%this, %obj, %slot)
{
	%obj.playthread(2, spearThrow);
	%this.DoMeleeStrike(%obj, %slot, 30, 3.5, 25, %this.projectile, swordHitSound, true);
}

function CrystalDaggerImage::onFire(%this, %obj, %slot)
{
	%obj.playthread(2, spearThrow);
	%this.DoMeleeStrike(%obj, %slot, 30, 3.5, 75, %this.projectile, swordHitSound, true);
}

// #3.
// #3.1
function DoBowShot(%obj, %slot, %headshot)
{
	if(isObject(%acc = %obj.client.account))
	{
		%quiver = %acc.read("Back");
		%type = getItemType(%quiver);
		switch$(%type)
		{
			case "Crystal quiver":
				%speed = 150;
				%damage = 25;
			case "Sanguite quiver":
				%speed = 120;
				%damage = 21;
			case "Sinnite quiver":
				%speed = 120;
				%damage = 21;
			case "Calcite quiver":
				%speed = 110;
				%damage = 19;
			case "Nithlite quiver":
				%speed = 110;
				%damage = 19;
			case "Obsidian quiver":
				%speed = 100;
				%damage = 17;
				%headshot = true;
			case "Surtra quiver":
				%speed = 100;
				%damage = 17;
			case "Stone quiver":
				%speed = 90;
				%damage = 15;
			case "Bone quiver":
				%speed = 90;
				%damage = 15;
			case "Wooden quiver":
				%speed = 80;
				%damage = 12;
			default:
				%obj.client.centerPrintQueue("<color:FF0000>Need to equip a quiver!", 3);
				return;
		}
		%vec = %obj.getMuzzleVector(%slot);
		%pos = vectorAdd(%obj.getMuzzlePoint(%slot), vectorScale(%vec, 0.1));
		%vec = vectorAdd(vectorScale(%vec, %speed), %obj.getVelocity());
		%proj = new Projectile()
		{
			datablock = arrowProjectile;
			initialPosition = %pos;
			initialVelocity = %vec;
			sourceObject = %obj;
			damage = %damage;
			headshot = %headshot;
		};
	}
	else
	{
		%vec = %obj.getMuzzleVector(%slot);
		%pos = vectorAdd(%obj.getMuzzlePoint(%slot), vectorScale(%vec, 0.1));
		%vec = vectorAdd(vectorScale(%vec, 90), %obj.getVelocity());
		%proj = new Projectile()
		{
			datablock = arrowProjectile;
			initialPosition = %pos;
			initialVelocity = %vec;
			sourceObject = %obj;
			damage = 15;
		};
	}
	return %proj;
}

datablock AudioProfile(arrowHitSound)
{
	filename = "Add-Ons/Weapon_Bow/arrowHit.wav";
	description = AudioClose3d;
	preload = true;
};

datablock AudioProfile(bowFireSound)
{
	filename = "Add-Ons/Weapon_Bow/bowFire.wav";
	description = AudioClosest3d;
	preload = true;
};


datablock ParticleData(arrowTrailParticle)
{
	dragCoefficient = 3.0;
	windCoefficient = 0.0;
	gravityCoefficient = 0.0;
	inheritedVelFactor = 0.0;
	constantAcceleration = 0.0;
	lifetimeMS = 200;
	lifetimeVarianceMS = 0;
	spinSpeed = 10.0;
	spinRandomMin = -50.0;
	spinRandomMax = 50.0;
	useInvAlpha = false;
	animateTexture = false;

	textureName = "base/data/particles/dot";

	colors[0] = "1 1 1 0.2";
	colors[1]= "1 1 1 0.0";
	sizes[0] = 0.2;
	sizes[1] = 0.01;
	times[0] = 0.0;
	times[1] = 1.0;
};

datablock ParticleEmitterData(arrowTrailEmitter)
{
	ejectionPeriodMS = 2;
	periodVarianceMS = 0;

	ejectionVelocity = 0;
	velocityVariance = 0;

	ejectionOffset = 0;

	thetaMin = 0.0;
	thetaMax = 90.0;  

	particles = arrowTrailParticle;

	useEmitterColors = true;
	uiName = "Arrow Trail";
};

//effects
datablock ParticleData(arrowStickExplosionParticle)
{
	dragCoefficient = 5;
	gravityCoefficient = 0.1;
	inheritedVelFactor = 0.2;
	constantAcceleration = 0.0;
	lifetimeMS = 500;
	lifetimeVarianceMS = 300;
	textureName = "base/data/particles/chunk";
	spinSpeed = 10.0;
	spinRandomMin = -50.0;
	spinRandomMax = 50.0;
	colors[0] = "0.9 0.9 0.6 0.9";
	colors[1] = "0.9 0.5 0.6 0.0";
	sizes[0] = 0.25;
	sizes[1] = 0.0;
};
datablock ParticleEmitterData(arrowStickExplosionEmitter)
{
	ejectionPeriodMS = 1;
	periodVarianceMS = 0;
	ejectionVelocity = 5;
	velocityVariance = 0.0;
	ejectionOffset = 0.0;
	thetaMin = 80;
	thetaMax = 80;
	phiReferenceVel = 0;
	phiVariance = 360;
	overrideAdvance = false;
	particles = "arrowStickExplosionParticle";

	useEmitterColors = true;
	uiName = "Arrow Stick";
};

datablock ExplosionData(arrowStickExplosion)
{
	soundProfile = arrowHitSound;

	lifeTimeMS = 150;

	particleEmitter = arrowStickExplosionEmitter;
	particleDensity = 10;
	particleRadius = 0.2;

	emitter[0] = "";

	faceViewer     = true;
	explosionScale = "1 1 1";

	shakeCamera = false;
	camShakeFreq = "10.0 11.0 10.0";
	camShakeAmp = "1.0 1.0 1.0";
	camShakeDuration = 0.5;
	camShakeRadius = 10.0;

	lightStartRadius = 0;
	lightEndRadius = 0;
	lightStartColor = "0 0 0";
	lightEndColor = "0 0 0";
};

datablock ParticleData(arrowExplosionParticle)
{
	dragCoefficient = 8;
	gravityCoefficient = -0.3;
	inheritedVelFactor = 0.2;
	constantAcceleration = 0.0;
	lifetimeMS = 500;
	lifetimeVarianceMS = 300;
	textureName = "base/data/particles/cloud";
	spinSpeed = 10.0;
	spinRandomMin = -50.0;
	spinRandomMax = 50.0;
	colors[0] = "0.5 0.5 0.5 0.9";
	colors[1] = "0.5 0.5 0.5 0.0";
	sizes[0] = 0.45;
	sizes[1] = 0.0;

   useInvAlpha = true;
};
datablock ParticleEmitterData(arrowExplosionEmitter)
{
	ejectionPeriodMS = 1;
	periodVarianceMS = 0;
	ejectionVelocity = 3;
	velocityVariance = 0.0;
	ejectionOffset = 0.0;
	thetaMin = 0;
	thetaMax = 180;
	phiReferenceVel = 0;
	phiVariance = 360;
	overrideAdvance = false;
	particles = "arrowExplosionParticle";

	useEmitterColors = true;
	uiName = "Arrow Vanish";
};

datablock ExplosionData(arrowExplosion)
{
	soundProfile = "";

	lifeTimeMS = 50;

	emitter[0] = arrowExplosionEmitter;

	faceViewer     = true;
	explosionScale = "1 1 1";

	shakeCamera = false;
	camShakeFreq = "10.0 11.0 10.0";
	camShakeAmp = "1.0 1.0 1.0";
	camShakeDuration = 0.5;
	camShakeRadius = 10.0;

	lightStartRadius = 0;
	lightEndRadius = 0;
	lightStartColor = "0 0 0";
	lightEndColor = "0 0 0";
};

datablock ProjectileData(arrowProjectile)
{
	projectileShapeName = "Add-Ons/Weapon_Bow/arrow.dts";

	directDamage = 30;
	directDamageType = $DamageType::ArrowDirect;

	radiusDamage = 0;
	damageRadius = 0;
	radiusDamageType = $DamageType::ArrowDirect;

	explosion = arrowExplosion;
	stickExplosion = arrowStickExplosion;
	bloodExplosion = arrowStickExplosion;
	particleEmitter = arrowTrailEmitter;
	explodeOnPlayerImpact = true;
	explodeOnDeath = true;  

	armingDelay = 4000;
	lifetime = 4000;
	fadeDelay = 4000;

	isBallistic = true;
	bounceAngle = 170;
	minStickVelocity = 10;
	bounceElasticity = 0.2;
	bounceFriction = 0.01;   
	gravityMod = 0.25;

	hasLight = false;
	lightRadius = 3.0;
	lightColor = "0 0 0.5";

	muzzleVelocity = 65;
	velInheritFactor = 1;

	uiName = "Arrow";
};

function ArrowProjectile::onCollision(%this, %obj, %col, %fade, %pos, %normal)
{
	if(%col.getType() & $Typemasks::PlayerObjectType && minigameCanDamage(%obj.sourceObject, %col))
	{
		if(%obj.headShot)
		{
			%hitbox = getHitbox(%obj, %col, %pos);
			if(strPos(%hitbox, "headSkin") != -1)
			{
				%obj.damage*= 2;
			}
		}
		%col.damage(%obj.sourceObject, %pos, %obj.damage, $DamageType::Bow);
	}
}

datablock ItemData(WoodenBowItem)
{
	category = "Weapon";
	className = "Weapon";

	shapeFile = "Add-Ons/Weapon_Bow/bow.dts";
	rotate = false;
	mass = 1;
	density = 0.2;
	elasticity = 0.2;
	friction = 0.6;
	emap = true;

	uiName = "Wooden Bow";
	iconName = "Add-Ons/Weapon_Bow/icon_bow";
	doColorShift = true;
	colorShiftColor = "0.81 0.69 0.56 1";

	image = WoodenBowImage;
	canDrop = true;
};

datablock ShapeBaseImageData(WoodenBowImage)
{
	shapeFile = "Add-Ons/Weapon_Bow/bow.dts";
	emap = true;

	mountPoint = 0;
	offset = "0 0 0";
	eyeOffset = 0;
	rotation = eulerToMatrix( "0 0 10" );

	correctMuzzleVector = true;

	className = "WeaponImage";

	item = BowItem;
	ammo = " ";
	projectile = "";
	projectileType = Projectile;

	melee = false;
	armReady = true;

	doColorShift = true;
	colorShiftColor = WoodenBowItem.colorShiftColor;

	stateName[0] = "Activate";
	stateTimeoutValue[0] = 0.5;
	stateTransitionOnTimeout[0] = "Ready";
	stateSound[0] = weaponSwitchSound;

	stateName[1] = "Ready";
	stateTransitionOnTriggerDown[1] = "Fire";
	stateAllowImageChange[1] = true;

	stateName[2] = "Fire";
	stateTransitionOnTimeout[2] = "Reload";
	stateTimeoutValue[2] = 0.05;
	stateFire[2] = true;
	stateAllowImageChange[2] = false;
	stateSequence[2] = "Fire";
	stateScript[2] = "onFire";
	stateWaitForTimeout[2] = true;
	stateSound[2] = bowFireSound;

	stateName[3] = "Reload";
	stateSequence[3] = "Reload";
	stateAllowImageChange[3] = false;
	stateTimeoutValue[3] = 0.5;
	stateWaitForTimeout[3] = true;
	stateTransitionOnTimeout[3] = "Check";

	stateName[4] = "Check";
	stateTransitionOnTriggerUp[4] = "StopFire";
	stateTransitionOnTriggerDown[4] = "Fire";

	stateName[5] = "StopFire";
	stateTransitionOnTimeout[5] = "Ready";
	stateTimeoutValue[5] = 0.2;
	stateAllowImageChange[5]= false;
	stateWaitForTimeout[5] = true;
	stateScript[5] = "onStopFire";
};

function WoodenBowImage::onFire(%this, %obj, %slot)
{
	DoBowShot(%obj, %slot, false);
}

datablock ItemData(BoneBowItem : WoodenBowItem)
{
	image = BoneBowImage;
	colorShiftColor = "0.8 0.8 0.8 1";
	uiName = "Bone Bow";
};

datablock ShapeBaseImageData(BoneBowImage : WoodenBowImage)
{
	item = BoneBowItem;
	colorShiftColor = "0.8 0.8 0.8 1";

	stateTimeoutValue[3] = 0.42;
};

function BoneBowImage::onFire(%this, %obj, %slot)
{
	DoBowShot(%obj, %slot, true);
}

datablock ItemData(FaerieBowItem : WoodenBowItem)
{
	image = FaerieBowImage;
	colorShiftColor = "0 0.7 0.3 1";
	uiName = "Faerie Bow";
};

datablock ShapeBaseImageData(FaerieBowImage : WoodenBowImage)
{
	item = FaerieBowItem;
	colorShiftColor = "0 0.7 0.3 1";

	stateTimeoutValue[3] = 0.42;
};

function FaerieBowImage::onFire(%this, %obj, %slot)
{
	%proj = DoBowShot(%obj, %slot, true);
	if(!isObject(%proj))
	{
		return;
	}
	%projData = %proj.getDatablock();
	%vel = vectorLen(%proj.getVelocity());
	//target info
	%start = %obj.getMuzzlePoint(%slot);
	%masks = $Typemasks::PlayerObjectType;
	%tgts = conalRaycastM(%start, %obj.getEyeVector(), 300, 7.5, %masks, %obj);
	while((%tgt = firstWord(%tgts)) !$= "")
	{
		if(minigameCanDamage(%obj, %tgt))
		{
			break;
		}
		else
		{
			%tgts = restWords(%tgts);
		}
	}
	if(isObject(%tgt))
	{
		//warning: math
		%tgtPos = %tgt.getHackPosition();
		%dist = vectorDist(%start, %tgtPos);
		%time = %dist / %vel;
		%tgtPos = vectorAdd(%tgtPos, vectorScale(%tgt.getVelocity(), %time));
		%dropCorrection = 9.81 * %projData.gravityMod * mPow(%time, 2);
		%vec = vectorScale(vectorNormalize(vectorSub(%tgtPos, %start)), %vel);
		%vec = vectorAdd(%vec, "0 0 " @ %dropCorrection);
	}
	else
	{
		%spread = vectorNormalize((getRandom() - 0.5) SPC (getRandom() - 0.5) SPC (getRandom() - 0.5));
		%vec = vectorScale(%obj.getEyeVector(), 10);
		%vec = vectorNormalize(vectorAdd(%vec, %spread));
		%vec = vectorScale(%vec, %vel);
	}
	%proj = new Projectile()
	{
		datablock = %projData;
		initialPosition = %start;
		initialVelocity = %vec;
		sourceObject = %obj;
		sourceSlot = %slot;
		damage = %proj.damage / 2;
		scale = "0.7 0.7 0.7";
		headshot = true;
	};
}

datablock ItemData(CrystalBowItem : WoodenBowItem)
{
	image = CrystalBowImage;
	colorShiftColor = "0.92 0.55 1 0.78";
	uiName = "<NAME>";
};

datablock ShapeBaseImageData(CrystalBowImage : WoodenBowImage)
{
	item = CrystalBowItem;
	colorShiftColor = CrystalBowItem.colorShiftColor;

	stateTimeoutValue[3] = 0.35;
};

function CrystalBowImage::onFire(%this, %obj, %slot)
{
	//projectile info
	%proj = DoBowShot(%obj, %slot, true);
	if(!isObject(%proj))
	{
		return;
	}
	%projData = %proj.getDatablock();
	%vel = vectorLen(%proj.getVelocity());
	//target info
	%start = %obj.getMuzzlePoint(%slot);
	%masks = $Typemasks::PlayerObjectType;
	%tgts = conalRaycastM(%start, %obj.getEyeVector(), 300, 7.5, %masks, %obj);
	while((%tgt = firstWord(%tgts)) !$= "")
	{
		if(minigameCanDamage(%obj, %tgt))
		{
			break;
		}
		else
		{
			%tgts = restWords(%tgts);
		}
	}
	if(isObject(%tgt))
	{
		//warning: math
		%tgtPos = %tgt.getHackPosition();
		%dist = vectorDist(%start, %tgtPos);
		%time = %dist / %vel;
		%tgtPos = vectorAdd(%tgtPos, vectorScale(%tgt.getVelocity(), %time));
		%dropCorrection = 9.81 * %projData.gravityMod * mPow(%time, 2);
		%vec = vectorScale(vectorNormalize(vectorSub(%tgtPos, %start)), %vel);
		%vec = vectorAdd(%vec, "0 0 " @ %dropCorrection);
	}
	else
	{
		%spread = vectorNormalize((getRandom() - 0.5) SPC (getRandom() - 0.5) SPC (getRandom() - 0.5));
		%vec = vectorScale(%obj.getEyeVector(), 10);
		%vec = vectorNormalize(vectorAdd(%vec, %spread));
		%vec = vectorScale(%vec, %vel);
	}
	%proj = new Projectile()
	{
		datablock = %projData;
		initialPosition = %start;
		initialVelocity = %vec;
		sourceObject = %obj;
		sourceSlot = %slot;
		damage = %proj.damage;
		headshot = true;
	};
}

// #3.2

datablock ParticleData(snowballExplosionParticle)
{
	dragCoefficient = 4.5;
	gravityCoefficient = 2.5;
	inheritedVelFactor = 0.4;
	constantAcceleration = 0;
	lifetimeMS = 400;
	lifetimeVarianceMS = 300;
	textureName = "base/data/particles/dot";
	spinSpeed = 0;
	spinRandomMin = 0;
	spinRandomMax = 0;
	colors[0] = "1 1 1 0.9";
	colors[1] = "1 1 1 0.0";
	sizes[0] = 0.45;
	sizes[1] = 0.0;

	useInvAlpha = true;
};
datablock ParticleEmitterData(snowballExplosionEmitter)
{
	ejectionPeriodMS = 3;
	periodVarianceMS = 0;
	ejectionVelocity = 15;
	velocityVariance = 7.5;
	ejectionOffset = 0;
	thetaMin = 0;
	thetaMax = 180;
	phiReferenceVel = 0;
	phiVariance = 360;
	overrideAdvance = false;
	particles = "snowballExplosionParticle";

	useEmitterColors = true;
	uiName = "Snowball Explosion";
};

datablock ExplosionData(snowballExplosion)
{
	soundProfile = "";

	lifeTimeMS = 50;

	emitter[0] = snowballExplosionEmitter;

	faceViewer     = true;
	explosionScale = "1 1 1";

	shakeCamera = false;
	camShakeFreq = "10.0 11.0 10.0";
	camShakeAmp = "1.0 1.0 1.0";
	camShakeDuration = 0.5;
	camShakeRadius = 10.0;

	lightStartRadius = 0;
	lightEndRadius = 0;
	lightStartColor = "0 0 0";
	lightEndColor = "0 0 0";
};

datablock ProjectileData(snowballProjectile)
{
	projectileShapeName = "base/data/shapes/snowball.dts";

	explosion = snowballExplosion;

	armingDelay = 0;
	lifetime = 30000;
	fadeDelay = 30000;

	isBallistic = true;
	bounceAngle = 170;
	minStickVelocity = 10;
	bounceElasticity = 0.5;
	bounceFriction = 0;
	gravityMod = 1;

	hasLight = false;

	muzzleVelocity = 30;
	velInheritFactor = 1;

	uiName = "Snowball";
};

function snowballProjectile::onCollision(%this, %obj, %col, %fade, %pos, %normal)
{
	serverPlay3d("sandHitSound" @ getRandom(1, 3), %pos);
}

datablock ItemData(snowballItem)
{
	category = "Weapon";
	className = "Weapon";

	shapeFile = "base/data/shapes/snowball.dts";
	rotate = false;
	mass = 1;
	density = 0.2;
	elasticity = 0.2;
	friction = 0.6;
	emap = true;

	uiName = "Snowball";
	iconName = "Add-Ons/Gamemode_ReverieFortWars/models/icon_Snowball";
	doColorShift = true;
	colorShiftColor = "1 1 1 1";

	image = snowballImage;
	canDrop = true;
};

datablock ShapeBaseImageData(snowballImage)
{
	shapeFile = "base/data/shapes/snowball.dts";
	emap = true;

	mountPoint = 0;
	offset = "0 0 0";

	correctMuzzleVector = true;

	className = "WeaponImage";

	item = snowballItem;
	ammo = " ";
	projectile = snowballProjectile;
	projectileType = Projectile;

	melee = false;
	armReady = true;

	doColorShift = true;
	colorShiftColor = "1 1 1 1";

	stateName[0] = "Activate";
	stateTimeoutValue[0] = 0.1;
	stateTransitionOnTimeout[0] = "Ready";
	stateSequence[0] = "ready";

	stateName[1] = "Ready";
	stateTransitionOnTriggerDown[1] = "Charge";
	stateAllowImageChange[1] = true;

	stateName[2] = "Charge";
	stateTransitionOnTimeout[2] = "Armed";
	stateTimeoutValue[2] = 0.7;
	stateWaitForTimeout[2] = false;
	stateTransitionOnTriggerUp[2] = "AbortCharge";
	stateScript[2] = "onCharge";
	stateAllowImageChange[2] = false;

	stateName[3] = "AbortCharge";
	stateTransitionOnTimeout[3] = "Ready";
	stateTimeoutValue[3] = 0.3;
	stateWaitForTimeout[3] = true;
	stateScript[3] = "onAbortCharge";
	stateAllowImageChange[3] = false;

	stateName[4] = "Armed";
	stateTransitionOnTriggerUp[4] = "Fire";
	stateAllowImageChange[4] = false;

	stateName[5] = "Fire";
	stateTransitionOnTimeout[5] = "Ready";
	stateTimeoutValue[5] = 0.5;
	stateFire[5] = true;
	stateScript[5] = "onFire";
	stateWaitForTimeout[5] = true;
	stateAllowImageChange[5]  = false;
};

function snowballImage::onMount(%this, %obj, %slot)
{
	%obj.playAudio(%slot, "sandHitSound" @ getRandom(1, 3));
	parent::onMount(%this, %obj, %slot);
}

function snowballImage::onCharge(%this, %obj, %slot)
{
	%obj.playthread(2, spearReady);
}

function snowballImage::onAbortCharge(%this, %obj, %slot)
{
	%obj.playthread(2, root);
}

function snowballImage::onFire(%this, %obj, %slot)
{
	%obj.playthread(2, spearThrow);
	Parent::onFire(%this, %obj, %slot);
	%acc = %obj.client.account;
	if(!isObject(%acc))
	{
		return;
	}
	%item = %acc.read("Equip" @ %obj.currtool);
	%q = getItemQuantity(%item) - 1;
	%obj.updateBottomPrintHUD("Bow", "Snowball" TAB %q);
	if(%q < 1)
	{
		%acc.set("Equip" @ %obj.currtool, "");
		%obj.unmountImage(%slot);
		%obj.playThread(1, "root");
		%obj.tool[%obj.currTool] = "";
		messageClient(%obj.client, 'MsgItemPickup', '', %obj.currTool, "");
	}
	else
	{
		%acc.set("Equip" @ %obj.currtool, setItemQuantity(%item, %q));
	}
}

// #4.

function Player::SubtractMana(%obj, %amt)
{
	%reduction = %obj.manaReduction != 0 ? %obj.manaReduction : 1;
	%mana = %obj.getEnergyLevel() - %amt * %reduction;
	if(%mana > 0)
	{
		%obj.setEnergyLevel(%mana);
		return true;
	}
	else
	{
		return false;
	}
}

// #4.1

datablock AudioProfile(FireLoopSound)
{
	filename = "Add-Ons/Gamemode_ReverieFortWars/sounds/fire_loop.wav";
	description = AudioCloseLooping3d;
	preload = true;
};

datablock AudioProfile(FaeFireImpactSound)
{
	filename = "Add-Ons/Gamemode_ReverieFortWars/sounds/faefire_impact.wav";
	description = AudioDefault3d;
	preload = true;
};

datablock ParticleData(FaeFireTrailParticle)
{
   dragCoefficient = 1;
   windCoefficient = 0;
   gravityCoefficient = -0.5;
   inheritedVelFactor = 0.2;
   constantAcceleration = 0;
   lifetimeMS = 300;
   lifetimeVarianceMS = 100;
   spinSpeed = 0;
   spinRandomMin = -900;
   spinRandomMax = 900;
   useInvAlpha = false;
   animateTexture = false;
   textureName = "base/data/particles/cloud";
 
   colors[0] = "0.2 1   0.4 0.1";
   colors[1] = "0.2 0.8 0.4 0.5";
   colors[2] = "1   0   0.5 0.1";
   sizes[0] = 0.2;
   sizes[1] = 0.6;
   sizes[2] = 0.2;
   times[0] = 0;
   times[1] = 0.2;
   times[2] = 1;
};

datablock ParticleEmitterData(FaeFireTrailEmitter)
{
	ejectionPeriodMS = 3;
	periodVarianceMS = 0;
	ejectionVelocity = 1.5;
	velocityVariance = 0.5;
	ejectionOffset = 0;
	thetaMin = 0;
	thetaMax = 160;
	phiReferenceVel = 0;
	phiVariance = 360;
	overrideAdvance = 0;
	orientParticles = 0;
	orientOnVelocity = 1;
	particles = "FaeFireTrailParticle";
	useEmitterSizes = false;
	useEmitterColors = false;
	uiName = "<NAME>";
};

datablock ParticleData(FaeFireExplosionParticle)
{
	dragCoefficient = 1;
	windCoefficient = 0;
	gravityCoefficient = -0.5;
	inheritedVelFactor = 0.2;
	constantAcceleration = 0;
	lifetimeMS = 750;
	lifetimeVarianceMS = 250;
	spinSpeed = 0;
	spinRandomMin = -180;
	spinRandomMax = 180;
	useInvAlpha = false;
	textureName = "base/data/particles/cloud";
	colors[0] = "0.2 1   0.4 0.1";
	colors[1] = "0.2 0.8 0.4 0.5";
	colors[2] = "1   0   0.5 0.1";
	sizes[0] = 0.5;
	sizes[1] = 1.2;
	sizes[2] = 0.2;
	times[0] = 0;
	times[1] = 0.2;
	times[2] = 1;
};

datablock ParticleEmitterData(FaeFireExplosionEmitter)
{
	ejectionPeriodMS = 5;
	periodVarianceMS = 0;
	ejectionVelocity = 2.5;
	velocityVariance = 1;
	ejectionOffset = 0.2;
	thetaMin = 0;
	thetaMax = 160;
	phiReferenceVel = 0;
	phiVariance = 360;
	overrideAdvance = 0;
	orientParticles = 0;
	orientOnVelocity = 1;
	particles = "FaeFireExplosionParticle";
	lifetimeMS = 0;
	lifetimeVarianceMS = 0;
	useEmitterSizes = 0;
	useEmitterColors = 0;
	uiName = "Faerie Fire Explosion";
};

datablock ExplosionData(FaeFireExplosion)
{
	soundProfile = FaeFireImpactSound;

	lifeTimeMS = 800;

	emitter[0] = FaeFireExplosionEmitter;

	faceViewer     = true;
	explosionScale = "1 1 1";

	shakeCamera = false;
	camShakeFreq = "10.0 11.0 10.0";
	camShakeAmp = "1.0 1.0 1.0";
	camShakeDuration = 0.5;
	camShakeRadius = 10;

	lightStartRadius = 8;
	lightEndRadius = 0;
	lightStartColor = "0.2 1 0.4";
	lightEndColor = "1 0 0.5";

	damageRadius = 2;
	radiusDamage = 30;
	playerBurnTime = 5000;
};

datablock ProjectileData(FaeFireProjectile)
{
	projectileShapeName = "base/data/shapes/empty.dts";

	explosion = FaeFireExplosion;

	directDamage = 10;
	directDamageType = $DamageType::FaeFire;
	radiusDamageType = $DamageType::FaeFire;

	armingDelay = 0;
	lifetime = 4000;
	fadeDelay = 4000;

	isBallistic = true;
	ballRadius = 1;
	bounceAngle = 170;
	minStickVelocity = 10;
	bounceElasticity = 0.5;
	bounceFriction = 0;
	gravityMod = 1;

	hasLight = true;
	lightRadius = 10;
	lightColor = "0.2 1 0.4";

	muzzleVelocity = 80;
	velInheritFactor = 0;

	sound = FaeFireImpactSound;
	particleEmitter = FaeFireTrailEmitter;

	uiName = "Faerie Fire";
};

function faeFireProjectile::onCollision(%this, %obj, %col, %fade, %pos, %normal)
{
	if(%col.getClassName() $= "fxDtsBrick" && (!isObject(%col.emitter) || isEventPending(%col.unburnSched)))
	{
		if(%col.getVolume() < 10)
		{
			cancel(%col.emberSched);
			cancel(%col.unburnSched);
			%col.setEmitter(FaeFireExplosionEmitter);
			%col.emberSched = %col.schedule(5000, setEmitter, FaeFireTrailemitter);
			%col.unburnSched = %col.schedule(15000, setEmitter);
		}
	}
	else
	{
		return parent::onCollision(%this, %obj, %col, %fade, %pos, %normal);
	}
}

datablock ShapeBaseImageData(faeFireImage)
{
	shapeFile = "base/data/shapes/empty.dts";
	emap = true;

	mountPoint = $HeadSlot;
	offset = "0 1 0";

	correctMuzzleVector = false;

	className = "WeaponImage";

	item = " ";
	ammo = " ";
	projectile = FaeFireProjectile;
	projectileType = Projectile;

	melee = false;
	armReady = true;

	doColorShift = false;
	colorShiftColor = "1 1 1 1";

	stateName[0] = "Ready";
	stateTransitionOnTriggerDown[0] = "Fire";

	stateName[1] = "Fire";
	stateTransitionOnTriggerUp[1] = "Ready";
	stateFire[1] = true;
	stateScript[1] = "onFire";
};

// #4.2

datablock ItemData(FaerieWandItem)
{
	category = "Weapon";
	className = "Weapon";

	shapeFile = "base/data/shapes/wand.dts";
	mass = 1;
	density = 0.2;
	elasticity = 0.2;
	friction = 0.6;
	emap = true;

	uiName = "<NAME>";
	iconName = "base/client/ui/itemIcons/wand";
	doColorShift = true;
	colorShiftColor = "0 0.7 0.3 1";

	image = FaerieWandImage;
	canDrop = true;
};

datablock ShapeBaseImageData(FaerieWandImage)
{
	shapeFile = FaerieWandItem.shapeFile;
	emap = true;

	mountPoint = 0;
	offset = "0 0 0";

	correctMuzzleVector = false;

	eyeOffset = "0 0 0";

	className = "WeaponImage";

	item = FaerieWandItem;
	ammo = " ";

	melee = true;
	doRetraction = false;
	armReady = true;

	doColorShift = true;
	colorShiftColor = FaerieWandItem.colorShiftColor;

	stateName[0] = "Activate";
	stateTimeoutValue[0] = 0.5;
	stateTransitionOnTimeout[0] = "Ready";
	stateSound[0] = weaponSwitchSound;

	stateName[1] = "Ready";
	stateTransitionOnTriggerDown[1] = "PreFire";
	stateAllowImageChange[1] = true;

	stateName[2] = "PreFire";
	stateScript[2] = "onPreFire";
	stateAllowImageChange[2] = false;
	stateTimeoutValue[2] = 0.1;
	stateTransitionOnTimeout[2] = "Fire";

	stateName[3] = "Fire";
	stateTransitionOnTimeout[3] = "CheckFire";
	stateTimeoutValue[3]= 0.2;
	stateAllowImageChange[3] = false;
	stateEmitter[3] = FaeFireTrailEmitter;
	stateEmitterTime[3] = 0.2;
	stateEmitterNode[3] = muzzlePoint;
	stateScript[3] = "onFire";
	stateWaitForTimeout[3] = true;

	stateName[4] = "CheckFire";
	stateTransitionOnTriggerUp[4]	= "StopFire";
	stateTransitionOnTriggerDown[4] = "Fire";

	stateName[5] = "StopFire";
	stateTransitionOnTimeout[5] = "Ready";
	stateTimeoutValue[5] = 0.2;
	stateAllowImageChange[5] = false;
	stateWaitForTimeout[5] = true;
	stateSequence[5] = "StopFire";
	stateScript[5] = "onStopFire";
};

function FaerieWandImage::onPreFire(%this, %obj, %slot)
{
	%obj.playthread(2, armattack);
}

function FaerieWandImage::onStopFire(%this, %obj, %slot)
{	
	%obj.playthread(2, root);
}

function FaerieWandImage::onFire(%this, %obj, %slot)
{
	%start = %obj.getEyePoint();
	%end = vectorAdd(%start, vectorScale(%obj.getEyeVector(), 10));
	%ray = containerRaycast(%start, %end, $Typemasks::FxBrickAlwaysObjectType);
	if(isObject(%col = firstWord(%ray)))
	{
		if(getTrustLevel(%obj, %col) >= 2)
		{
			%pos = posFromRaycast(%ray);
			%norm = normalFromRaycast(%ray);
			%p = new projectile()
			{
				datablock = faeFireProjectile;
				initialPosition = %pos;
				initialVelocity = %norm;
				scale = "0.5 0.5 0.5";
			};
			%p.explode();
			if((%fx = %obj.client.currentFxCan) != 0)
			{
				if(%fx == 7 || %fx == 8)
				{
					%col.setShapeFx(%fx - 7);
				}
				else
				{
					%col.setColorFx(%fx);
				}
			}
			else
			{
				WrenchImage::onHitObject(%this, %obj, %slot, %col, %pos, %norm);
			}
		}
		else if(%ray.getGroup().bl_id == 888888 && %col.getDatablock() == nameToID(brick16x16fData))
		{
			if(!%obj.subtractMana(5))
			{
				return;
			}
			%pos = posFromRaycast(%ray);
			%norm = normalFromRaycast(%ray);
			%p = new projectile()
			{
				datablock = faeFireProjectile;
				initialPosition = %pos;
				initialVelocity = %norm;
				scale = "0.5 0.5 0.5";
			};
			%p.explode();
			%brick = new fxDtsBrick()
			{
				position = vectorAdd(%pos, "0 0 2.3");
				datablock = brickPineTreeData;
				colorID = $RFW::ResourceColor["Wood"];
				client = $PublicClient;
				stackBL_ID = 888888;
				isPlanted = true;
			};
			%err = SAG_BrickPlantStuff(%brick, $PublicClient);
			if(%err == 0)
			{
				%obj.setEnergyLevel(%obj.getEnergyLevel() - 5);
				%brick.setEmitter(FaeFireExplosionEmitter);
				%brick.schedule(200, setEmitter);
			}
			else
			{
				%brick.delete();
			}
		}
	}
}

package RFW_FaerieWand
{
	function fxDtsBrick::setItem(%this, %item, %client)
	{
		if(isObject(%item) && isObject(%client) && !%client.isAdmin)
		{
			messageClient(%client, 'MsgError');
			%client.centerPrintQueue("<color:FF0000>Cannot spawn items.", 3000);
			return;
		}
		return parent::setItem(%this, %item, %client);
	}
	function serverCmdAddEvent(%client, %a, %b, %c, %d, %e, %f, %g, %h, %i, %j)
	{
		if(isObject(%client) && !%client.isAdmin)
		{
			messageClient(%client, 'MsgError');
			%client.centerPrintQueue("<color:FF0000>Cannot add events.", 3000);
			return;
		}
		parent::serverCmdAddEvent(%client, %a, %b, %c, %d, %e, %f, %g, %h, %i, %j);
	}
};
activatePackage(RFW_FaerieWand);

// #4.3

datablock AudioProfile(photoLensSound1)
{
	filename = "Add-Ons/Gamemode_ReverieFortWars/sounds/photoLens1.wav";
	description = AudioClose3d;
	preload = true;
};

datablock AudioProfile(photoLensSound2)
{
	filename = "Add-Ons/Gamemode_ReverieFortWars/sounds/photoLens2.wav";
	description = AudioClose3d;
	preload = true;
};

datablock AudioProfile(photoLensSound3)
{
	filename = "Add-Ons/Gamemode_ReverieFortWars/sounds/photoLens3.wav";
	description = AudioClose3d;
	preload = true;
};

datablock AudioProfile(photoLensProjectileSound)
{
	filename = "Add-Ons/Gamemode_ReverieFortWars/sounds/sparkler_loop.wav";
	description = AudioCloseLooping3d;
	preload = true;
};

datablock ParticleData(PhotoLensTrailParticle)
{
	dragCoefficient = 0;
	windCoefficient = 0;
	gravityCoefficient = 0;
	inheritedVelFactor = 0;
	constantAcceleration = 5;
	lifetimeMS = 100;
	lifetimeVarianceMS = 5;
	spinSpeed = 0;
	spinRandomMin = -900;
	spinRandomMax = 900;
	useInvAlpha = false;
	textureName = "base/data/particles/cloud";
	colors[0] = "1 0 0 1";
	colors[1] = "0 1 0 1";
	colors[2] = "0 0 1 1";
	colors[3] = "1 0 1 0";
	sizes[0] = "0.3";
	sizes[1] = "0.3";
	sizes[2] = "0.3";
	sizes[3] = "0.3";
	times[0] = 0;
	times[1] = 0.5;
	times[2] = 0.75;
	times[3] = 1;
};

datablock ParticleEmitterData(PhotoLensTrailEmitter)
{
	className = "ParticleEmitterData";
	ejectionPeriodMS = 5;
	periodVarianceMS = 0;
	ejectionVelocity = 0;
	velocityVariance = 0;
	ejectionOffset = 0.5;
	thetaMin = 90;
	thetaMax = 90;
	phiReferenceVel = 2400;
	phiVariance = 0;
	overrideAdvance = 0;
	orientParticles = 0;
	orientOnVelocity = 1;
	particles = "PhotoLensTrailParticle";
	lifetimeMS = 0;
	lifetimeVarianceMS = 0;
	useEmitterSizes = 0;
	useEmitterColors = 0;
	uiName = "Faerie Fire Trail";
};

datablock ParticleData(PhotoLensExplosionParticle)
{
	dragCoefficient = 0;
	windCoefficient = 0;
	gravityCoefficient = 0;
	inheritedVelFactor = 0;
	constantAcceleration = 5;
	lifetimeMS = 420;
	lifetimeVarianceMS = 0;
	spinSpeed = 0;
	spinRandomMin = -900;
	spinRandomMax = 900;
	useInvAlpha = false;
	textureName = "base/data/particles/cloud";
	colors[0] = "1 0 1 0";
	colors[1] = "0.1 0 1 1";
	colors[2] = "0.2 1 0 1";
	colors[3] = "1 0 0 0.5";
	sizes[0] = "0.3";
	sizes[1] = "0.3";
	sizes[2] = "0.4";
	sizes[3] = 1;
	times[0] = 0;
	times[1] = 0.25;
	times[2] = 0.5;
	times[3] = 1;
};

datablock ParticleEmitterData(PhotoLensExplosionEmitter)
{
	className = "ParticleEmitterData";
	ejectionPeriodMS = 2;
	periodVarianceMS = 0;
	ejectionVelocity = -5;
	velocityVariance = 0;
	ejectionOffset = 5;
	thetaMin = 0;
	thetaMax = 45;
	phiReferenceVel = 0;
	phiVariance = 360;
	overrideAdvance = 0;
	orientParticles = 0;
	orientOnVelocity = 1;
	particles = "PhotoLensExplosionParticle";
	lifetimeMS = 0;
	lifetimeVarianceMS = 0;
	useEmitterSizes = 0;
	useEmitterColors = 0;
	uiName = "PhotoMS Lens Explosion";
};

datablock ExplosionData(PhotoLensExplosion)
{
	soundProfile = "PhotoLensImpactSound";
	particleEmitter = "PhotoLensTrailEmitter";
	particleDensity = 25;
	particleRadius = 0.25;
	emitter[0] = "PhotoLensExplosionEmitter";
	emitter[1] = "PhotoLensTrailEmitter";
	lifetimeMS = 350;
	shakeCamera = false;
	lightStartRadius = 10;
	lightEndRadius = 0;
	lightStartColor = "1 1 0.5 1";
	lightEndColor = "0.5 0.5 1 1";
	damageRadius = 2;
	radiusDamage = 30;
};

datablock ProjectileData(PhotoLensProjectile)
{
	projectileShapeName = "base/data/shapes/empty.dts";
	particleEmitter = PhotoLensTrailEmitter;
	sound = photoLensProjectileSound;
	Explosion = PhotoLensExplosion;
	hasLight = true;
	lightRadius = 10;
	lightColor = "1 1 1";
	isBallistic = 1;
	velInheritFactor = 0;
	muzzleVelocity = 80;
	lifetime = 5000;
	armingDelay = 0;
	fadeDelay = 125;
	bounceAngle = 170;
	minStickVelocity = 10;
	ballRadius = 0.7;
	uiName = "PhotoMS Lens";
	bounceElasticity = 0.5;
	bounceFriction = 0;
	gravityMod = 0;
};

function PhotoLensProjectile::onCollision(%this, %obj, %col, %fade, %pos, %normal)
{
	if(%col.getType() & $Typemasks::PlayerObjectType && minigameCanDamage(%obj.sourceObject, %col))
	{
		%col.damage(%obj.sourceObject, %pos, 20, $DamageType::Lens);
	}
	else if(%col.getType() & $Typemasks::FxBrickObjectType && (getTrustLevel(%obj.sourceObject, %col) >= 2 || %col.getGroup().bl_id == 888888))
	{
		if(%col.getVolume() <= 4)
		{
			if(getWord(%col.getPosition(), 2) >= 312)
			{
				%col.setColor($RFW::ResourceColor["Sinnite"]);
				%col.setColorFx($RFW::ResourceColorFx["Sinnite"]);
			}
			else
			{
				%col.setColor($RFW::ResourceColor["Nithlite"]);
				%col.setColorFx($RFW::ResourceColorFx["Nithlite"]);
			}
		}
	}
}

datablock ItemData(PhotoLensItem)
{
	category = "Weapon";
	className = "Weapon";

	shapeFile = "Add-Ons/GameMode_ReverieFortWars/models/Photometallisynthesis_Lens.dts";
	mass = 1;
	density = 0.2;
	elasticity = 0.2;
	friction = 0.6;
	emap = true;

	uiName = "PhotoMS lens";
	iconName = "Add-Ons/GameMode_ReverieFortWars/models/PhotoLens";
	doColorShift = false;
	colorShiftColor = "1 1 1 1";

	image = PhotoLensImage;
	canDrop = true;
};

datablock ShapeBaseImageData(PhotoLensImage)
{
	shapeFile = PhotoLensItem.shapeFile;
	emap = true;

	mountPoint = 0;
	offset = "-0.5 0 0";

	correctMuzzleVector = false;

	eyeOffset = "0 0 0";

	className = "WeaponImage";

	item = PhotoLensItem;
	ammo = " ";
	projectile = PhotoLensProjectile;

	melee = true;
	doRetraction = false;
	armReady = true;

	doColorShift = PhotoLensItem.doColorShift;
	colorShiftColor = PhotoLensItem.colorShiftColor;

	stateName[0] = "Activate";
	stateTimeoutValue[0] = 0.5;
	stateTransitionOnTimeout[0] = "Ready";
	stateSound[0] = weaponSwitchSound;

	stateName[1] = "Ready";
	stateTimeoutValue[1] = 5;
	stateWaitForTimeout[1] = false;
	stateTransitionOnTimeout[1] = "Presynthesis";
	stateTransitionOnTriggerDown[1] = "Fire";
	stateAllowImageChange[1] = true;

	stateName[2] = "Presynthesis";
	stateScript[2] = "Presynthesis";
	stateAllowImageChange[2] = false;
	stateTimeoutValue[2] = 1.5;
	stateWaitForTimeout[2] = true;
	stateTransitionOnTimeout[2] = "Synthesis";
	stateEmitter[2] = PhotoLensTrailEmitter;
	stateEmitterNode[2] = muzzlePoint;
	stateEmitterTime[2] = 1.5;

	stateName[3] = "Synthesis";
	stateScript[3] = "Synthesis";
	stateAllowImageChange[3] = false;
	stateTimeoutValue[3] = 0.5;
	stateWaitForTimeout[3] = false;
	stateTransitionOnTimeout[3] = "Ready";
	stateTransitionOnTriggerDown[3] = "Fire";
	stateEmitter[3] = PhotoLensExplosionEmitter;
	stateEmitterNode[3] = muzzlePoint;
	stateEmitterTime[3] = 0.1;

	stateName[4] = "Fire";
	stateFire[4] = true;
	stateScript[4] = "onFire";
	stateAllowImageChange[4] = false;
	stateTimeoutValue[4] = 1;
	stateWaitForTimeout[4] = true;
	stateTransitionOnTimeout[4] = "AltReady";

	stateName[5] = "AltReady";
	stateTransitionOnTriggerDown[5] = "Fire";
	stateAllowImageChange[5] = true;
};

function PhotoLensImage::onMount(%this, %obj, %slot)
{
	parent::onMount(%this, %obj, %slot);
	%obj.playThread(1, armreadyboth);
}

function PhotoLensImage::Presynthesis(%this, %obj, %slot)
{
	serverPlay3d("PhotoLensSound" @ getRandom(1, 3), %obj.getMuzzlePoint(%slot));
}

function PhotoLensImage::Synthesis(%this, %obj, %slot)
{
	%mat = getWord(%obj.getPosition(), 2) >= 308 ? "Sinnite" : "Nithlite";
	if(%obj.subtractMana(16))
	{
		%obj.client.account.addItem(%mat TAB 8, true);
	}
	else
	{
		%energy = %obj.getEnergyLevel();
		%amt = mFloatLength(0.5 * %energy - 0.05, 1);
		%obj.setEnergyLevel(0);
		%obj.client.account.addItem(%mat TAB %amt, true);
	}
}

function PhotoLensImage::onFire(%this, %obj, %slot)
{
	if(%obj.subtractMana(4))
	{
		%proj = new Projectile()
		{
			datablock = PhotoLensProjectile;
			initialPosition = %obj.getMuzzlePoint(%slot);
			initialVelocity = vectorScale(%obj.getMuzzleVector(%slot), 100);
			sourceObject = %obj;
			damage = 30;
		};
	}
}

// #4.4
datablock ParticleData(PyroWandTrailParticle)
{
	dragCoefficient = 0;
	windCoefficient = 0;
	gravityCoefficient = 0;
	inheritedVelFactor = 0;
	constantAcceleration = 5;
	lifetimeMS = 100;
	lifetimeVarianceMS = 5;
	spinSpeed = 0;
	spinRandomMin = -900;
	spinRandomMax = 900;
	useInvAlpha = false;
	textureName = "base/data/particles/cloud";
	colors[0] = "1 0 0 1";
	colors[1] = "0 1 0 1";
	colors[2] = "0 0 1 1";
	colors[3] = "1 0 1 0";
	sizes[0] = "0.3";
	sizes[1] = "0.3";
	sizes[2] = "0.3";
	sizes[3] = "0.3";
	times[0] = 0;
	times[1] = 0.5;
	times[2] = 0.75;
	times[3] = 1;
};

datablock ParticleEmitterData(PyroWandTrailEmitter)
{
	className = "ParticleEmitterData";
	ejectionPeriodMS = 5;
	periodVarianceMS = 0;
	ejectionVelocity = 0;
	velocityVariance = 0;
	ejectionOffset = 0.5;
	thetaMin = 90;
	thetaMax = 90;
	phiReferenceVel = 2400;
	phiVariance = 0;
	overrideAdvance = 0;
	orientParticles = 0;
	orientOnVelocity = 1;
	particles = "PyroWandTrailParticle";
	lifetimeMS = 0;
	lifetimeVarianceMS = 0;
	useEmitterSizes = 0;
	useEmitterColors = 0;
	uiName = "Pyro Wand Trail";
};

datablock ParticleData(PyroWandExplosionParticle)
{
	dragCoefficient = 0;
	windCoefficient = 0;
	gravityCoefficient = 0;
	inheritedVelFactor = 0;
	constantAcceleration = 5;
	lifetimeMS = 420;
	lifetimeVarianceMS = 0;
	spinSpeed = 0;
	spinRandomMin = -900;
	spinRandomMax = 900;
	useInvAlpha = false;
	textureName = "base/data/particles/cloud";
	colors[0] = "1 0 1 0";
	colors[1] = "0.1 0 1 1";
	colors[2] = "0.2 1 0 1";
	colors[3] = "1 0 0 0.5";
	sizes[0] = "0.3";
	sizes[1] = "0.3";
	sizes[2] = "0.4";
	sizes[3] = 1;
	times[0] = 0;
	times[1] = 0.25;
	times[2] = 0.5;
	times[3] = 1;
};

datablock ParticleEmitterData(PyroWandExplosionEmitter)
{
	className = "ParticleEmitterData";
	ejectionPeriodMS = 2;
	periodVarianceMS = 0;
	ejectionVelocity = -5;
	velocityVariance = 0;
	ejectionOffset = 5;
	thetaMin = 0;
	thetaMax = 45;
	phiReferenceVel = 0;
	phiVariance = 360;
	overrideAdvance = 0;
	orientParticles = 0;
	orientOnVelocity = 1;
	particles = "PyroWandExplosionParticle";
	lifetimeMS = 0;
	lifetimeVarianceMS = 0;
	useEmitterSizes = 0;
	useEmitterColors = 0;
	uiName = "Pyro Wand Explosion";
};

datablock ExplosionData(PyroWandExplosion)
{
	soundProfile = "PyroWandImpactSound";
	particleEmitter = "PyroWandTrailEmitter";
	particleDensity = 25;
	particleRadius = 0.25;
	emitter[0] = "PyroWandExplosionEmitter";
	emitter[1] = "PyroWandTrailEmitter";
	lifetimeMS = 350;
	shakeCamera = false;
	lightStartRadius = 10;
	lightEndRadius = 0;
	lightStartColor = "1 1 0.5 1";
	lightEndColor = "0.5 0.5 1 1";
	damageRadius = 2;
	radiusDamage = 30;
};

datablock ProjectileData(PyroWandProjectile)
{
	projectileShapeName = "base/data/shapes/empty.dts";
	particleEmitter = PyroWandTrailEmitter;
	sound = FireLoopSound;
	Explosion = PyroWandExplosion;
	hasLight = true;
	lightRadius = 10;
	lightColor = "1 1 1";
	isBallistic = 1;
	velInheritFactor = 0;
	muzzleVelocity = 80;
	lifetime = 5000;
	armingDelay = 0;
	fadeDelay = 125;
	bounceAngle = 170;
	minStickVelocity = 10;
	ballRadius = 0.7;
	uiName = "Pyro wand";
	bounceElasticity = 0.5;
	bounceFriction = 0;
	gravityMod = 0;
};

datablock ItemData(PyroWandItem)
{
	category = "Weapon";
	className = "Weapon";

	shapeFile = "Add-Ons/GameMode_ReverieFortWars/models/Pyromancy_Wand.dts";
	mass = 1;
	density = 0.2;
	elasticity = 0.2;
	friction = 0.6;
	emap = true;

	uiName = "<NAME>and";
	iconName = "Add-Ons/GameMode_ReverieFortWars/models/PyroWand";
	doColorShift = false;
	colorShiftColor = "1 1 1 1";

	image = PyroWandImage;
	canDrop = true;
};

datablock ShapeBaseImageData(PyroWandImage)
{
	shapeFile = PyroWandItem.shapeFile;
	emap = true;

	mountPoint = 0;
	offset = "0 0 0";

	correctMuzzleVector = false;

	eyeOffset = "0 0 0";

	className = "WeaponImage";

	item = PyroWandItem;
	ammo = " ";
	projectile = PyroWandProjectile;
	projectileType = projectile;

	melee = true;
	doRetraction = false;
	armReady = true;

	doColorShift = PyroWandItem.doColorShift;
	colorShiftColor = PyroWandItem.colorShiftColor;

	stateName[0] = "Activate";
	stateTimeoutValue[0] = 0.5;
	stateTransitionOnTimeout[0] = "Ready";
	stateSound[0] = weaponSwitchSound;

	stateName[1] = "Ready";
	stateTransitionOnTriggerDown[1] = "Fire";
	stateAllowImageChange[1] = true;

	stateName[2] = "Fire";
	stateFire[2] = true;
	stateScript[2] = "onFire";
	stateAllowImageChange[2] = false;
	stateTimeoutValue[2] = 1;
	stateWaitForTimeout[2] = true;
	stateTransitionOnTimeout[2] = "Ready";
};

// #4.5
datablock AudioProfile(EvilLoopSound)
{
	filename = "Add-Ons/Gamemode_ReverieFortWars/sounds/evil_loop.wav";
	description = AudioCloseLooping3d;
	preload = true;
};

datablock ParticleData(OssiStaffTrailParticle)
{
	dragCoefficient = 0;
	windCoefficient = 0;
	gravityCoefficient = 0;
	inheritedVelFactor = 0;
	constantAcceleration = 5;
	lifetimeMS = 100;
	lifetimeVarianceMS = 5;
	spinSpeed = 0;
	spinRandomMin = -900;
	spinRandomMax = 900;
	useInvAlpha = false;
	textureName = "base/data/particles/cloud";
	colors[0] = "1 0 0 1";
	colors[1] = "0 1 0 1";
	colors[2] = "0 0 1 1";
	colors[3] = "1 0 1 0";
	sizes[0] = "0.3";
	sizes[1] = "0.3";
	sizes[2] = "0.3";
	sizes[3] = "0.3";
	times[0] = 0;
	times[1] = 0.5;
	times[2] = 0.75;
	times[3] = 1;
};

datablock ParticleEmitterData(OssiStaffTrailEmitter)
{
	className = "ParticleEmitterData";
	ejectionPeriodMS = 5;
	periodVarianceMS = 0;
	ejectionVelocity = 0;
	velocityVariance = 0;
	ejectionOffset = 0.5;
	thetaMin = 90;
	thetaMax = 90;
	phiReferenceVel = 2400;
	phiVariance = 0;
	overrideAdvance = 0;
	orientParticles = 0;
	orientOnVelocity = 1;
	particles = "OssiStaffTrailParticle";
	lifetimeMS = 0;
	lifetimeVarianceMS = 0;
	useEmitterSizes = 0;
	useEmitterColors = 0;
	uiName = "OssiMS Staff Trail";
};

datablock ParticleData(OssiStaffExplosionParticle)
{
	dragCoefficient = 0;
	windCoefficient = 0;
	gravityCoefficient = 0;
	inheritedVelFactor = 0;
	constantAcceleration = 5;
	lifetimeMS = 420;
	lifetimeVarianceMS = 0;
	spinSpeed = 0;
	spinRandomMin = -900;
	spinRandomMax = 900;
	useInvAlpha = false;
	textureName = "base/data/particles/cloud";
	colors[0] = "1 0 1 0";
	colors[1] = "0.1 0 1 1";
	colors[2] = "0.2 1 0 1";
	colors[3] = "1 0 0 0.5";
	sizes[0] = "0.3";
	sizes[1] = "0.3";
	sizes[2] = "0.4";
	sizes[3] = 1;
	times[0] = 0;
	times[1] = 0.25;
	times[2] = 0.5;
	times[3] = 1;
};

datablock ParticleEmitterData(OssiStaffExplosionEmitter)
{
	className = "ParticleEmitterData";
	ejectionPeriodMS = 2;
	periodVarianceMS = 0;
	ejectionVelocity = -5;
	velocityVariance = 0;
	ejectionOffset = 5;
	thetaMin = 0;
	thetaMax = 45;
	phiReferenceVel = 0;
	phiVariance = 360;
	overrideAdvance = 0;
	orientParticles = 0;
	orientOnVelocity = 1;
	particles = "OssiStaffExplosionParticle";
	lifetimeMS = 0;
	lifetimeVarianceMS = 0;
	useEmitterSizes = 0;
	useEmitterColors = 0;
	uiName = "OssiMS Staff Explosion";
};

datablock ExplosionData(OssiStaffExplosion)
{
	soundProfile = "OssiStaffImpactSound";
	particleEmitter = "OssiStaffTrailEmitter";
	particleDensity = 25;
	particleRadius = 0.25;
	emitter[0] = "OssiStaffExplosionEmitter";
	emitter[1] = "OssiStaffTrailEmitter";
	lifetimeMS = 350;
	shakeCamera = false;
	lightStartRadius = 10;
	lightEndRadius = 0;
	lightStartColor = "1 1 0.5 1";
	lightEndColor = "0.5 0.5 1 1";
	damageRadius = 2;
	radiusDamage = 30;
};

datablock ProjectileData(OssiStaffProjectile)
{
	projectileShapeName = "base/data/shapes/empty.dts";
	particleEmitter = OssiStaffTrailEmitter;
	sound = EvilLoopSound;
	Explosion = OssiStaffExplosion;
	hasLight = true;
	lightRadius = 10;
	lightColor = "1 1 1";
	isBallistic = 1;
	velInheritFactor = 0;
	muzzleVelocity = 80;
	lifetime = 5000;
	armingDelay = 0;
	fadeDelay = 125;
	bounceAngle = 170;
	minStickVelocity = 10;
	ballRadius = 0.7;
	uiName = "OssiMS staff";
	bounceElasticity = 0.5;
	bounceFriction = 0;
	gravityMod = 0;
};

datablock ItemData(OssiStaffItem)
{
	category = "Weapon";
	className = "Weapon";

	shapeFile = "Add-Ons/GameMode_ReverieFortWars/models/Ossimetallisynthesis_Staff.dts";
	mass = 1;
	density = 0.2;
	elasticity = 0.2;
	friction = 0.6;
	emap = true;

	uiName = "OssiMS staff";
	iconName = "Add-Ons/GameMode_ReverieFortWars/models/OssiStaff";
	doColorShift = false;
	colorShiftColor = "1 1 1 1";

	image = OssiStaffImage;
	canDrop = true;
};

datablock ShapeBaseImageData(OssiStaffImage)
{
	shapeFile = OssiStaffItem.shapeFile;
	emap = true;

	mountPoint = 0;
	offset = "0 0 0";

	correctMuzzleVector = false;

	eyeOffset = "0 0 0";

	className = "WeaponImage";

	item = OssiStaffItem;
	ammo = " ";
	projectile = OssiStaffProjectile;
	projectileType = projectile;

	melee = true;
	doRetraction = false;
	armReady = true;

	doColorShift = OssiStaffItem.doColorShift;
	colorShiftColor = OssiStaffItem.colorShiftColor;

	stateName[0] = "Activate";
	stateTimeoutValue[0] = 0.5;
	stateTransitionOnTimeout[0] = "Ready";
	stateSound[0] = weaponSwitchSound;

	stateName[1] = "Ready";
	stateTransitionOnTriggerDown[1] = "Fire";
	stateAllowImageChange[1] = true;

	stateName[2] = "Fire";
	stateFire[2] = true;
	stateScript[2] = "onFire";
	stateAllowImageChange[2] = false;
	stateTimeoutValue[2] = 1;
	stateWaitForTimeout[2] = true;
	stateTransitionOnTimeout[2] = "Ready";
};

// #4.6
datablock AudioProfile(BloodSplatSound)
{
	filename = "Add-Ons/Gamemode_ReverieFortWars/sounds/blood_splat.wav";
	description = AudioDefault3d;
	preload = true;
};

datablock ParticleData(SanguiWandTrailParticle)
{
	dragCoefficient = 0;
	windCoefficient = 0;
	gravityCoefficient = 0.3;
	inheritedVelFactor = 0;
	constantAcceleration = 0;
	lifetimeMS = 800;
	lifetimeVarianceMS = 100;
	spinSpeed = 0;
	spinRandomMin = 900;
	spinRandomMax = 900;
	useInvAlpha = true;
	textureName = "base/data/particles/dot";
	colors[0] = "0.8 0 0 1";
	colors[1] = "0.75 0 0 1";
	colors[2] = "0.7 0 0 1";
	colors[3] = "0.6 0 0 0";
	sizes[0] = 0.25;
	sizes[1] = 0.2;
	sizes[2] = 0.15;
	sizes[3] = 0.05;
	times[0] = 0;
	times[1] = 0.3;
	times[2] = 0.7;
	times[3] = 1;
};

datablock ParticleEmitterData(SanguiWandTrailEmitter)
{
	ejectionPeriodMS = 10;
	periodVarianceMS = 0;
	ejectionVelocity = 0.5;
	velocityVariance = 0.2;
	ejectionOffset = 0;
	thetaMin = 0;
	thetaMax = 180;
	phiReferenceVel = 0;
	phiVariance = 360;
	overrideAdvance = false;
	particles = "SanguiWandTrailParticle";
	uiName = "SanguiMS Wand Trail";
};

datablock ParticleData(SanguiWandExplosionParticle)
{
	dragCoefficient = 6;
	windCoefficient = 0;
	gravityCoefficient = 1.5;
	inheritedVelFactor = 0;
	constantAcceleration = 0;
	lifetimeMS = 500;
	lifetimeVarianceMS = 100;
	spinSpeed = 0;
	spinRandomMin = -900;
	spinRandomMax = 900;
	useInvAlpha = true;
	textureName = "base/data/particles/dot";
	colors[0] = "0.8 0 0 1";
	colors[1] = "0.75 0 0 1";
	colors[2] = "0.7 0 0 1";
	colors[3] = "0.6 0 0 0";
	sizes[0] = 0.25;
	sizes[1] = 0.1;
	sizes[2] = 0.05;
	sizes[3] = 0.05;
	times[0] = 0;
	times[1] = 0.33;
	times[2] = 0.67;
	times[3] = 1;
};

datablock ParticleEmitterData(SanguiWandExplosionEmitter)
{
	ejectionPeriodMS = 7;
	periodVarianceMS = 0;
	ejectionVelocity = 10;
	velocityVariance = 5;
	ejectionOffset = 0.1;
	thetaMin = 0;
	thetaMax = 60;
	phiReferenceVel = 0;
	phiVariance = 360;
	overrideAdvance = false;
	particles = "SanguiWandExplosionParticle";
	uiName = "SanguiMS Wand Explosion";
};

datablock ExplosionData(SanguiWandExplosion)
{
	soundProfile = BloodSplatSound;
	particleEmitter = SanguiWandExplosionEmitter;
	particleDensity = 25;
	particleRadius = 0.25;
	explosionScale = "1 1 1";
	playSpeed = 1;
	emitter[0] = SanguiWandExplosionEmitter;
	emitter[1] = SanguiWandTrailEmitter;
	debrisNum = 0;
	debrisNumVariance = 0;
	lifetimeMS = 250;
	lifetimeVariance = 50;
	shakeCamera = false;
	damageRadius = 0;
	radiusDamage = 0;
};

datablock ProjectileData(SanguiWandProjectile)
{
	projectileShapeName = "base/data/shapes/empty.dts";
	particleEmitter = SanguiWandTrailEmitter;
	sound = EvilLoopSound;
	Explosion = SanguiWandExplosion;
	hasLight = false;
	isBallistic = 1;
	velInheritFactor = 0;
	muzzleVelocity = 80;
	lifetime = 5000;
	armingDelay = 0;
	fadeDelay = 125;
	bounceAngle = 170;
	minStickVelocity = 10;
	uiName = "<NAME>";
	bounceElasticity = 0.5;
	bounceFriction = 0;
	gravityMod = 0.2;
};

function SanguiWandProjectile::onCollision(%this, %obj, %col, %fade, %pos, %normal)
{
	if(%col.getType() & $Typemasks::PlayerObjectType && minigameCanDamage(%obj.sourceObject, %col))
	{
		%col.damage(%obj.sourceObject, %pos, 40, $DamageType::SanguiWand);
		%col.sanguification+= 0.3;
		for(%i = 1; %i <= 5; %i++)
		{
			%col.schedule(%i * 1000, damage, %obj.sourceObject, %pos, 5, $DamageType::Bleed);
		}
	}
}

datablock ItemData(SanguiWandItem)
{
	category = "Weapon";
	className = "Weapon";

	shapeFile = "Add-Ons/GameMode_ReverieFortWars/models/Sanguimetallisynthesis_Wand.dts";
	mass = 1;
	density = 0.2;
	elasticity = 0.2;
	friction = 0.6;
	emap = true;

	uiName = "<NAME>";
	iconName = "Add-Ons/GameMode_ReverieFortWars/models/SanguiWand";
	doColorShift = false;
	colorShiftColor = "1 1 1 1";

	image = SanguiWandImage;
	canDrop = true;
};

datablock ShapeBaseImageData(SanguiWandImage)
{
	shapeFile = SanguiWandItem.shapeFile;
	emap = true;

	mountPoint = 0;
	offset = "0 0 0";

	correctMuzzleVector = false;

	eyeOffset = "0 0 0";

	className = "WeaponImage";

	item = SanguiWandItem;
	ammo = " ";
	projectile = SanguiWandProjectile;
	projectileType = projectile;

	melee = true;
	doRetraction = false;
	armReady = true;

	doColorShift = SanguiWandItem.doColorShift;
	colorShiftColor = SanguiWandItem.colorShiftColor;

	stateName[0] = "Activate";
	stateTimeoutValue[0] = 0.5;
	stateTransitionOnTimeout[0] = "Ready";
	stateSound[0] = weaponSwitchSound;

	stateName[1] = "Ready";
	stateTransitionOnTriggerDown[1] = "Prefire";
	stateAllowImageChange[1] = true;

	stateName[2] = "Prefire";
	stateTimeoutValue[2] = 0.13;
	stateTransitionOnTimeout[2] = "Fire";
	stateScript[2] = "onPrefire";

	stateName[3] = "Fire";
	stateFire[3] = true;
	stateScript[3] = "onFire";
	stateAllowImageChange[3] = false;
	stateTimeoutValue[3] = 0.6;
	stateTransitionOnTimeout[3] = "Ready";
};

function SanguiWandImage::onPrefire(%this, %obj, %slot)
{
	%obj.playThread(2, shiftAway);
}

function SanguiWandImage::onFire(%this, %obj, %slot)
{
	%obj.playThread(2, shiftTo);
	if(%obj.subtractMana(1))
	{
		%data = %this.projectile;
		%z = getWord(%obj.getScale(), 2);
		%proj = new Projectile()
		{
			datablock = %data;
			initialPosition = %obj.getMuzzlePoint(%slot);
			initialVelocity = vectorScale(%obj.getMuzzleVector(%slot), %data.muzzleVelocity);
			sourceObject = %obj;
			scale = %z SPC %z SPC %z;
		};
	}
}

// #5.

// #5.1

datablock AudioProfile(FaerieAmbientSound)
{
	filename = "Add-Ons/Gamemode_ReverieFortWars/sounds/faerie_loop.wav";
	description = AudioDefaultLooping3d;
	preload = true;
};

datablock AudioProfile(FaerieJumpSound)
{
	filename = "Add-Ons/Gamemode_ReverieFortWars/sounds/faerie_jump.wav";
	description = AudioClose3d;
	preload = true;
};

datablock AudioProfile(FaerieHello)
{
	filename = "Add-Ons/Gamemode_ReverieFortWars/sounds/faerie_hello.wav";
	description = AudioDefault3d;
	preload = true;
};

datablock AudioProfile(FaerieWatchout)
{
	filename = "Add-Ons/Gamemode_ReverieFortWars/sounds/faerie_watchout.wav";
	description = AudioDefault3d;
	preload = true;
};

datablock PlayerData(PlayerFaerie : ReveriePlayer)
{
	uiName = "Faerie";
	mass = 30;
	jumpForce = 500;
	minImpactSpeed = 500;
	jumpSound = FaerieJumpSound;
};

datablock ParticleData(FaerieParticle)
{
	dragCoefficient = 2;
	windCoefficient = 0.25;
	gravityCoefficient = 0;
	inheritedVelFactor = 0.4;
	constantAcceleration = 0;
	lifetimeMS = 500;
	lifetimeVarianceMS = 300;
	spinSpeed = 0;
	spinRandomMin = -360;
	spinRandomMax = 360;
	useInvAlpha = false;
	textureName = "base/data/particles/cloud";
	colors[0] = "0.1 0.6 0.3 0.4";
	colors[1] = "0.1 1   0.3 0.2";
	colors[2] = "0   0.6 0.3 0";
	sizes[0] = 1;
	sizes[1] = 1.2;
	sizes[2] = 0.5;
	times[0] = 0;
	times[1] = 0.2;
	times[2] = 1;
};

datablock ParticleEmitterData(FaerieEmitter)
{
	ejectionPeriodMS = 8;
	periodVarianceMS = 0;
	ejectionVelocity = 0.7;
	velocityVariance = 0.3;
	ejectionOffset = 0;
	thetaMin = 0;
	thetaMax = 180;
	phiReferenceVel = 0;
	phiVariance = 360;
	overrideAdvance = 0;
	orientParticles = 0;
	orientOnVelocity = 1;
	particles = "FaerieParticle";
	useEmitterSizes = false;
	useEmitterColors = false;
	uiName = "Faerie";
};

datablock ShapeBaseImageData(FaerieImage)
{
	emap = false;
	shapeFile = "base/data/shapes/empty.dts";
	cloakable = true;
	mountPoint = $HeadSlot;
	firstPersonParticles = false;
	offset = "0 0 0";
	rotation = "1 0 0 0";
	eyeOffset = "0 0 0";
	eyeRotation = "1 0 0 0";
	firstPerson = true;
	usesEnergy = false;
	accuFire = false;
	lightType = "ConstantLight";
	lightColor = "0 0.6 0.3";
	lightTime = 1000;
	lightRadius = 20;

	stateName[0] = "Ready";
	stateTransitionOnTimeout[0] = "Fire";
	stateTimeoutValue[0] = 0.01;
	stateWaitForTimeout[0] = true;

	stateName[1] = "Fire";
	stateTransitionOnTimeout[1] = "Fire";
	stateTimeoutValue[1] = 10000;
	stateWaitForTimeout[1] = true;
	stateEmitter[1] = "FaerieEmitter";
	stateEmitterTime[1] = 10000;
	stateSound[1] = FaerieAmbientSound;
};

function FaerieImage::onMount(%this, %obj, %slot)
{
	%obj.hideNode("ALL");
}

// #5.2
datablock AudioProfile(MonsterGrowl)
{
	filename = "Add-Ons/Gamemode_ReverieFortWars/sounds/monster_growl.wav";
	description = AudioDefault3d;
	preload = true;
};

datablock ShapeBaseImageData(RibcageImage)
{
	shapeFile = "Add-Ons/Gamemode_ReverieFortWars/models/ribcage.dts";
	emap = true;
	mountPoint = 2;
	offset = "0 0 -0.6";
	eyeOffset = "0 0 0.4";
	rotation = eulerToMatrix("0 0 0");
	scale = "1 1 1";
	doColorShift = false;
	colorShiftColor = "0 0.5 0.25 1";
};

// #5.3
datablock PlayerData(ReverieHorse : AdvancedHorseArmor)
{
	maxForwardSpeed = ReveriePlayer.maxForwardSpeed * 2; //vroom vroom
	maxSideSpeed = ReveriePlayer.maxSideSpeed * 0.5;
};

datablock AudioProfile(HorseNeigh)
{
	filename = "Add-Ons/Gamemode_ReverieFortWars/sounds/horse_neigh.wav";
	description = AudioDefault3d;
	preload = true;
};<file_sep>//--------------------
// Name: Hitboxes
// Type: Hack
// Version: 1.0
// Author: Jookia (ID 119)
//--------------------

if(isPackage(hitboxes) == true)
	return; // Don't need duplicates.

package hitboxes
{
	function getHitbox(%obj, %col, %pos)
	{
		$hitbox = true;
		$hitboxList = "";
		
		paintProjectile::onCollision("", %obj, %col, "", %pos, "");
		cancel(%col.tempColorSchedule);
		$hitbox = "";
		
		%hitboxList = $hitboxList;
		$hitboxList = "";
		
		return %hitboxList;
	}
	function shapeBase::setNodeColor(%obj, %node, %color)
	{
		if($hitbox == true)
		{
			if($hitboxList $= "")
				$hitboxList = %node;
			else
				$hitboxList = $hitboxList SPC %node;
			
			return;
		}
		parent::setNodeColor(%obj, %node, %color);
	}
};
activatePackage(hitboxes);