MessageLog			= { "Welcome to the world of Chaos Hack. Please enjoy your stay." }

WaitForDescription	= false
WaitForTrash		= false

DescriptionItem		= nil

LightingInitialized	= false

MonsterCount		= 0

Monsterpool = { "Bat", "Zombie", "VenusFlytrap", "Bloodhound", "Cyclops", "Emu", "Ghoul", "Aquator", "Centaur", "Griffin", "Hydra", "Lesser_Demon", "Lindworm", "Greater_Demon", "Dragon" }

ItemPool = {
	{ "Dagger", "Flail", "Club", "Falchion", "Sabre", "Warhammer", "Broadsword", "Bidenhander" }, -- Weapons
	{ "BoarFur", "LightShield", "Buckler", "Chainmail", "SpikedShield", "Platemail", "ImperialChainmail", "MithrilArmor" }, -- Armor
	{ "HealingPotionL", "HealingPotion", "HealingPotionB", "HealingPotionG" }, -- Potions
	{ "AttackCharmL", "DefenseCharmL", "AttackCharm", "DefenseCharm", "AttackCharmB", "DefenseCharmB", "AttackCharmG", "DefenseCharmG" }, -- Charms
	{ "Matchbox", "Candle", "Torch", "Lantern", "Flashlight" }  -- Illuminants
}

function SelectMonster( Depth )
	return Monsterpool[ Max( Min( Depth - 2 + math.random( 3 ), #Monsterpool ), 1 ) ]
end

function UpdateMonster( Monster )
	if TileVisible( Monster.TileX, Monster.TileY ) then
		if ObjectDistanceSq( Monster, Hero ) == 1 then
			CalculateDamage( Monster, Hero )
		else
			if math.abs( Hero.TileX - Monster.TileX ) > math.abs( Hero.TileY - Monster.TileY ) then
				Monster.MoveX = Sgn( Hero.TileX - Monster.TileX )
				Monster.MoveY = 0
			else
				Monster.MoveX = 0
				Monster.MoveY = Sgn( Hero.TileY - Monster.TileY )
			end
		end
	else
		Monster.MoveX = math.random( 3 ) - 2
		Monster.MoveY = 0

		if Monster.MoveX == 0 then Monster.MoveY = math.random( 3 ) - 2 end
	end
end

function DropItems( Monster )
	local Diff = Monster.Level - Hero.Level

	if Diff >= 2 then
		local Stash = CreateObject( "ItemStash", Monster.TileX, Monster.TileY )

		local A, B, C, D, E, F, G, Depth = GetMapData( 0, 0 )

		local Item1 = Min( math.floor( Depth/3 ), #ItemPool[ Index1 ] )
		local Item2 = Min( math.floor( Depth/3 ), #ItemPool[ Index1 ] )

		Stash:AddItems( "Gold", math.random( 100, 200 ), ItemPool[ 1 ][ Item2 ], ItemPool[ 2 ][ Item1 ] )
	elseif Diff == 1 then
		local Stash = CreateObject( "ItemStash", Monster.TileX, Monster.TileY )

		local Index1 = math.random( 3 ) + 2
		local Index2 = math.random( 2 )

		local A, B, C, D, E, F, G, Depth = GetMapData( 0, 0 )

		local Item1 = Min( math.floor( Depth/3 ), #ItemPool[ Index1 ] )
		local Item2 = Min( math.floor( Depth/3), #ItemPool[ Index1 ] )

		Stash:AddItems( "Gold", math.random( 50, 100 ), ItemPool[ Index2 ][ Item2 ], ItemPool[ Index1 ][ Item1 ] )
	elseif Diff == 0 then
		if math.random( 6 ) == 1 then
			local Stash = CreateObject( "ItemStash", Monster.TileX, Monster.TileY )

			local Index = math.random( 3 ) + 2

			local A, B, C, D, E, F, G, Depth = GetMapData( 0, 0 )

			local Item = Min( math.floor( Depth/4 ), #ItemPool[ Index ] )

			Stash:AddItems( "Gold", math.random( 10, 20 ), ItemPool[ Index ][ Item ] )
		end
	end
end

function CalculateDamage( Attacker, Defender )
	PrintLog( Attacker.Description .. " attacks " .. Defender.Description .. ". " )

	if math.random( 100 ) < Attacker.AttackRating + ( Attacker.AttackPlus or 0 ) then
		if math.random( 100 ) < Defender.DefenseRating + ( Defender.DefensePlus or 0 ) then
			AttachLog( Defender.Description .. " blocks the attack." )
		else
			local Weapon = Attacker.Inventory.Weapon or { MinDamage = 0, DamageRange = 0 }
			local Armor  = Defender.Inventory.Armor  or { MinArmor  = 0,  ArmorRange = 0 }

			local Damage = Max( Weapon.MinDamage - Armor.MinArmor + math.random( Weapon.DamageRange + 1 ) + math.random( Armor.ArmorRange + 1 ) - 2, 0 )

			AttachLog( "The attack hits. " .. Defender.Description .. " takes " .. Damage .. " damage." )

			if Defender.Inventory.Armor .HP then Defender.Inventory.Armor .HP = Defender.Inventory.Armor .HP - 0.1 end
			if Attacker.Inventory.Weapon.HP then Attacker.Inventory.Weapon.HP = Attacker.Inventory.Weapon.HP - 0.1 end

			Defender.HP = Defender.HP - Damage

			SyncObject( Defender )
		end
	else
		AttachLog( "The attack fails." )
	end
end

function CalculateExp( Monster )
	MonsterCount = MonsterCount + 1

	local Exp = ( Monster.Level + 1 )/Hero.Level*( Monster.Level + 1 )

	PrintLog( "You earn " .. Exp .. " experience!" )

	Hero.Experience = Hero.Experience + Exp

	if Hero.Experience >= Hero.NextExp then
		Hero.NextExp		= math.ceil( Hero.NextExp^1.2 )
		Hero.Level  		= Hero.Level + 1
		Hero.MaxHP  		= math.ceil( Hero.MaxHP*1.5 )
		Hero.AttackRating	= Hero.AttackRating  + 5
		Hero.DefenseRating	= Hero.DefenseRating + 5
		Hero.HP     		= Hero.MaxHP

		SyncObject( Hero )

		PrintLog( "LEVEL UP! New stats: MaxHP " .. Hero.MaxHP .. ", Attack rating " .. Hero.AttackRating .. ", Defense rating " .. Hero.DefenseRating )
	end
end

function NumberToString( Number )
	if Number < 0 then return tostring( Number ) else return "+" .. tostring( Number ) end
end

function RandomizeItem( Item )
	if Item.Randomize then
		for Attribute, Range in pairs( Item.Randomize ) do
			Item[ Attribute ] = math.random( Range[ 2 ] - Range[ 1 ] + 1 ) - 1 + Range[ 1 ]
		end
	end
end

function DrawLineText( Text, X, Y )
	for Line in string.gmatch( Text, "%C+" ) do
		DrawText( Line, X, Y )

		Y = Y + 18
	end
end

function BuildDescription( Item )
	if Item.Kind == "Weapon" then
		return Item.LongDescription .. "\nAttack rating: " .. NumberToString( Item.AttackRating ) .. "%\nDamage: " .. Item.MinDamage .. "-" .. Item.MinDamage + Item.DamageRange .. "\nDurability: " .. math.ceil( Item.HP )
	elseif Item.Kind == "Armor" then
		return Item.LongDescription .. "\nDefense Rating: " .. NumberToString( Item.DefenseRating ) .. "%\nArmor: " .. Item.MinArmor .. "-" .. Item.MinArmor + Item.ArmorRange .. "\nDurability: " .. math.ceil( Item.HP )
	else
		return Item.LongDescription
	end
end

function PrintLog( Text )
	if #MessageLog == 4 then table.remove( MessageLog, 1 ) end

	MessageLog[ #MessageLog + 1 ] = Text
end

function AttachLog( Text )
	MessageLog[ #MessageLog ] = MessageLog[ #MessageLog ] .. Text
end

function Max( A, B )
	return ( A > B and A or B )
end

function Min( A, B )
	return ( A < B and A or B )
end

function Sgn( A )
	return ( A < 0 and -1 or ( A == 0 and 0 or 1 ) )
end

function CopyObject( Table )
	local Copy = { }

	for Key, Value in pairs( Table ) do
		Copy[ Key ] = Value
	end

	return Copy
end

function ObjectDistanceSq( A, B )
	return ( A.TileX - B.TileX )*( A.TileX - B.TileX ) + ( A.TileY - B.TileY )*( A.TileY - B.TileY )
end

function PointDistanceSq( A1, A2, B1, B2 )
	return ( A1 - B1 )*( A1 - B1 ) + ( A2 - B2 )*( A2 - B2 )
end

math.randomseed( os.time() )
