/*
 * OpenClonk, http://www.openclonk.org
 *
 * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
 * Copyright (c) 2009-2016, The OpenClonk Team and contributors
 *
 * Distributed under the terms of the ISC license; see accompanying file
 * "COPYING" for details.
 *
 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
 * See accompanying file "TRADEMARK" for details.
 *
 * To redistribute this file separately, substitute the full license texts
 * for the above references.
 */
// complex dynamic landscape creator

#include "C4Include.h"
#include "landscape/C4MapCreatorS2.h"

#include "control/C4Record.h"
#include "graphics/CSurface8.h"
#include "landscape/C4Material.h"
#include "landscape/C4Texture.h"
#include "lib/C4Random.h"
#include "script/C4ScriptHost.h"

namespace {
	// node attribute entry for SetField search
	enum C4MCValueType
	{
		C4MCV_None,
		C4MCV_Integer,
		C4MCV_Percent,
		C4MCV_Pixels,
		C4MCV_Material,
		C4MCV_Texture,
		C4MCV_Algorithm,
		C4MCV_Boolean,
		C4MCV_Zoom,
		C4MCV_ScriptFunc
	};

	template<typename T>
	class MemberAdapter {
	public:
		typedef char (T::*OffsetType);

		MemberAdapter(T& object, OffsetType offset)
				: Object(object), Offset(offset)
		{
		}

		template<typename U>
		U& As()
		{
			typedef U (T::*TargetPtrType);
			return Object.*reinterpret_cast<TargetPtrType>(Offset);
		}

	private:
		T& Object;
		OffsetType Offset;
	};

	typedef MemberAdapter<C4MCOverlay>::OffsetType C4MCOverlayOffsetType;

	struct C4MCNodeAttr
	{
		const char* Name; // name of field
		C4MCValueType Type; // type of field
		C4MCOverlayOffsetType Offset; // offset of field in overlay MCOverlay-class
	};

	extern C4MCNodeAttr C4MCOvrlMap[];
}

/* --- C4MCCallbackArray --- */

C4MCCallbackArray::C4MCCallbackArray(C4AulFunc *pSFunc, C4MapCreatorS2 *pMapCreator)
{
	// store fn
	pSF = pSFunc;
	// zero fields
	pMap=nullptr; pNext=nullptr;
	// store and add in map creator
	if ((this->pMapCreator=pMapCreator))
		pMapCreator->CallbackArrays.Add(this);
	// done
}

C4MCCallbackArray::~C4MCCallbackArray()
{
	// clear map, if present
	if (pMap) delete [] pMap;
}

void C4MCCallbackArray::EnablePixel(int32_t iX, int32_t iY)
{
	// array not yet created? then do that now!
	if (!pMap)
	{
		// safety
		if (!pMapCreator) return;
		// get current map size
		C4MCMap *pCurrMap = pMapCreator->pCurrentMap;
		if (!pCurrMap) return;
		iWdt = pCurrMap->Wdt; iHgt = pCurrMap->Hgt;
		// create bitmap
		int32_t iSize=(iWdt*iHgt+7)/8;
		pMap = new BYTE[iSize];
		memset(pMap, 0, iSize);
		// done
	}
	// safety: do not set outside map!
	if (iX<0 || iY<0 || iX>=iWdt || iY>=iHgt) return;
	// set in map
	int32_t iIndex = iX + iY*iWdt;
	pMap[iIndex/8] |= 1<<(iIndex%8);
	// done
}

void C4MCCallbackArray::Execute(int32_t iMapZoom)
{
	// safety
	if (!pSF || !pMap) return;
	// pre-create parset
	C4AulParSet Pars(0, 0, iMapZoom);
	// call all funcs
	int32_t iIndex=iWdt*iHgt;
	while (iIndex--)
		if (pMap[iIndex/8]&(1<<(iIndex%8)))
		{
			// set pars
			Pars[0] = C4VInt((iIndex%iWdt) * iMapZoom - (iMapZoom/2));
			Pars[1] = C4VInt((iIndex/iWdt) * iMapZoom - (iMapZoom/2));
			// call
			pSF->Exec(nullptr, &Pars);
		}
	// done
}



/* --- C4MCCallbackArrayList --- */

void C4MCCallbackArrayList::Add(C4MCCallbackArray *pNewArray)
{
	// add to end
	if (pFirst)
	{
		C4MCCallbackArray *pLast = pFirst;
		while (pLast->pNext) pLast=pLast->pNext;
		pLast->pNext=pNewArray;
	}
	else pFirst=pNewArray;
}

void C4MCCallbackArrayList::Clear()
{
	// remove all arrays
	C4MCCallbackArray *pArray, *pNext=pFirst;
	while ((pArray=pNext))
	{
		pNext=pArray->pNext;
		delete pArray;
	}
	// zero first-field
	pFirst=nullptr;
}

void C4MCCallbackArrayList::Execute(int32_t iMapZoom)
{
	// execute all arrays
	for (C4MCCallbackArray *pArray = pFirst; pArray; pArray=pArray->pNext)
		pArray->Execute(iMapZoom);
}




/* --- C4MCNode --- */

C4MCNode::C4MCNode(C4MCNode *pOwner)
{
	// reg to owner
	Reg2Owner(pOwner);
	// no name
	*Name=0;
}

C4MCNode::C4MCNode(C4MCParser* pParser, C4MCNode *pOwner, C4MCNode &rTemplate, bool fClone)
{
	// Make sure the template is not used recursively within itself
	for(C4MCNode* pParent = pOwner; pParent != nullptr; pParent = pParent->Owner)
		if(pParent == &rTemplate)
			throw C4MCParserErr(pParser, C4MCErr_NoRecTemplate, rTemplate.Name);
	// set owner and stuff
	Reg2Owner(pOwner);
	// copy children from template
	for (C4MCNode *pChild=rTemplate.Child0; pChild; pChild=pChild->Next)
		pChild->clone(pParser, this);
	// no name
	*Name=0;
}

C4MCNode::~C4MCNode()
{
	// clear
	Clear();
	// remove from list
	if (Prev) Prev->Next = Next; else if (Owner) Owner->Child0 = Next;
	if (Next) Next->Prev = Prev; else if (Owner) Owner->ChildL = Prev;
}

void C4MCNode::Reg2Owner(C4MCNode *pOwner)
{
	// init list
	Child0=ChildL=nullptr;
	// owner?
	if ((Owner=pOwner))
	{
		// link into it
		if ((Prev = Owner->ChildL))
			Prev->Next = this;
		else
			Owner->Child0 = this;
		Owner->ChildL = this;
		MapCreator=pOwner->MapCreator;
	}
	else
	{
		Prev=nullptr;
		MapCreator=nullptr;
	}
	// we're always last entry
	Next=nullptr;
}

void C4MCNode::Clear()
{
	// delete all children; they'll unreg themselves
	while (Child0) delete Child0;
}

C4MCOverlay *C4MCNode::OwnerOverlay()
{
	for (C4MCNode *pOwnr=Owner; pOwnr; pOwnr=pOwnr->Owner)
		if (C4MCOverlay *pOwnrOvrl=pOwnr->Overlay())
			return pOwnrOvrl;
	// no overlay-owner
	return nullptr;
}

C4MCNode *C4MCNode::GetNodeByName(const char *szName)
{
	// search local list (backwards: last node has highest priority)
	for (C4MCNode *pChild=ChildL; pChild; pChild=pChild->Prev)
		// name match?
		if (SEqual(pChild->Name, szName))
			// yeah, success!
			return pChild;
	// search owner, if present
	if (Owner) return Owner->GetNodeByName(szName);
	// nothing found
	return nullptr;
}

bool C4MCNode::SetField(C4MCParser *pParser, const char *szField, const char *szSVal, int32_t iVal, C4MCTokenType ValType)
{
	// no fields in base class
	return false;
}

int32_t C4MCNode::IntPar(C4MCParser *pParser, const char *szSVal, int32_t iVal, C4MCTokenType ValType)
{
	// check if int32_t
	if (ValType == MCT_INT || ValType == MCT_PERCENT || ValType == MCT_PX)
		return iVal;
	throw C4MCParserErr(pParser, C4MCErr_FieldValInvalid, szSVal);
}

const char *C4MCNode::StrPar(C4MCParser *pParser, const char *szSVal, int32_t iVal, C4MCTokenType ValType)
{
	// check if identifier
	if (ValType != MCT_IDTF)
		throw C4MCParserErr(pParser, C4MCErr_FieldValInvalid, szSVal);
	return szSVal;
}

#define IntPar IntPar(pParser, szSVal, iVal, ValType) // shortcut for checked int32_t param
#define StrPar StrPar(pParser, szSVal, iVal, ValType) // shortcut for checked str param

void C4MCNode::ReEvaluate()
{
	// evaluate ourselves
	Evaluate();
	// evaluate children
	for (C4MCNode *pChild=Child0; pChild; pChild=pChild->Next)
		pChild->ReEvaluate();
}


// overlay

C4MCOverlay::C4MCOverlay(C4MCNode *pOwner) : C4MCNode(pOwner)
{
	// zero members
	X=Y=Wdt=Hgt=OffX=OffY=0;
	Material=MNone;
	*Texture=0;
	Op=MCT_NONE;
	MatClr=0;
	MatClrBkg=0;
	Algorithm=nullptr;
	Sub=false;
	ZoomX=ZoomY=0;
	FixedSeed=Seed=0;
//  Alpha=Beta=0;
	Turbulence=Lambda=Rotate=0;
	Invert=LooseBounds=Group=Mask=false;
	pEvaluateFunc=pDrawFunc=nullptr;
}

C4MCOverlay::C4MCOverlay(C4MCParser* pParser, C4MCNode *pOwner, C4MCOverlay &rTemplate, bool fClone) : C4MCNode(pParser, pOwner, rTemplate, fClone)
{
	// copy fields
	X=rTemplate.X; Y=rTemplate.Y; Wdt=rTemplate.Wdt; Hgt=rTemplate.Hgt;
	RX=rTemplate.RX; RY=rTemplate.RY; RWdt=rTemplate.RWdt; RHgt=rTemplate.RHgt;
	OffX=rTemplate.OffX; OffY=rTemplate.OffY; ROffX=rTemplate.ROffX; ROffY=rTemplate.ROffY;
	Material=rTemplate.Material;
	SCopy(rTemplate.Texture, Texture, C4MaxName);
	Algorithm=rTemplate.Algorithm;
	Sub=rTemplate.Sub;
	ZoomX=rTemplate.ZoomX; ZoomY=rTemplate.ZoomY;
	MatClr=rTemplate.MatClr;
	MatClrBkg=rTemplate.MatClrBkg;
	Seed=rTemplate.Seed;
	Alpha=rTemplate.Alpha; Beta=rTemplate.Beta; Turbulence=rTemplate.Turbulence; Lambda=rTemplate.Lambda;
	Rotate=rTemplate.Rotate;
	Invert=rTemplate.Invert; LooseBounds=rTemplate.LooseBounds; Group=rTemplate.Group; Mask=rTemplate.Mask;
	FixedSeed=rTemplate.FixedSeed;
	pEvaluateFunc=rTemplate.pEvaluateFunc;
	pDrawFunc=rTemplate.pDrawFunc;
	// zero non-template-fields
	if (fClone) Op=rTemplate.Op; else Op=MCT_NONE;
}

void C4MCOverlay::Default()
{
	// default algo
	Algorithm=GetAlgo(C4MC_DefAlgo);
	// no mat (sky) default
	Material=MNone;
	*Texture=0;
	// but if mat is set, assume it sub
	Sub=true;
	// full size
	OffX=OffY=X=Y=0;
	ROffX.Set(0,true); ROffY.Set(0,true); RX.Set(0,true); RY.Set(0,true);
	Wdt=Hgt=C4MC_SizeRes;
	RWdt.Set(C4MC_SizeRes,true); RHgt.Set(C4MC_SizeRes,true);
	// def zoom
	ZoomX=ZoomY=C4MC_ZoomRes;
	// def values
	Alpha.Set(0,false); Beta.Set(0,false); Turbulence=Lambda=Rotate=0; Invert=LooseBounds=Group=Mask=false;
	FixedSeed=0;
	// script funcs
	pEvaluateFunc=pDrawFunc=nullptr;
}

bool C4MCOverlay::SetField(C4MCParser *pParser, const char *szField, const char *szSVal, int32_t iVal, C4MCTokenType ValType)
{
	int32_t iMat; C4MCAlgorithm *pAlgo;
	// inherited fields
	if (C4MCNode::SetField(pParser, szField, szSVal, iVal, ValType)) return true;
	//local fields
	for (C4MCNodeAttr *pAttr=&C4MCOvrlMap[0]; *pAttr->Name; pAttr++)
		if (SEqual(szField, pAttr->Name))
		{
			// field was found, get offset to store in
			MemberAdapter<C4MCOverlay> Target(*this, pAttr->Offset);
			// store according to field type
			switch (pAttr->Type)
			{
			case C4MCV_Integer:
				// simply store
				Target.As<int32_t>() = IntPar;
				break;
			case C4MCV_Percent:
				Target.As<int_bool>().Set(IntPar, ValType == MCT_PERCENT || ValType == MCT_INT);
				break;
			case C4MCV_Pixels:
				Target.As<int_bool>().Set(IntPar, ValType == MCT_PERCENT);
				break;
			case C4MCV_Material:
				// get material by string
				iMat = MapCreator->MatMap->Get(StrPar);
				// check validity
				if (iMat == MNone) throw C4MCParserErr(pParser, C4MCErr_MatNotFound, StrPar);
				// store
				Target.As<int32_t>() = iMat;
				break;
			case C4MCV_Texture:
				// check validity
				if (!MapCreator->TexMap->CheckTexture(StrPar))
					throw C4MCParserErr(pParser, C4MCErr_TexNotFound, StrPar);
				// store
				SCopy(StrPar, Target.As<char [C4M_MaxName + 1]>(), C4M_MaxName);
				break;
			case C4MCV_Algorithm:
				// get algo
				pAlgo=GetAlgo(StrPar);
				// check validity
				if (!pAlgo) throw C4MCParserErr(pParser, C4MCErr_AlgoNotFound, StrPar);
				// store
				Target.As<C4MCAlgorithm *>()=pAlgo;
				break;
			case C4MCV_Boolean:
				// store whether value is not zero
				Target.As<bool>()=IntPar!=0;
				break;
			case C4MCV_Zoom:
				// store calculated zoom
				Target.As<int32_t>()=Clamp<int32_t>(C4MC_ZoomRes-IntPar,1,C4MC_ZoomRes*2);
				break;
			case C4MCV_ScriptFunc:
			{
				// get script func of main script
				C4AulFunc *pSFunc = ::GameScript.ScenPropList._getPropList()->GetFunc(StrPar);
				if (!pSFunc) throw C4MCParserErr(pParser, C4MCErr_SFuncNotFound, StrPar);
				// add to main
				Target.As<C4MCCallbackArray*>() = new C4MCCallbackArray(pSFunc, MapCreator);
			}
			default:
				// TODO
				break;
			}
			// done
			return true;
		}
	// nothing found :(
	return false;
}

C4MCAlgorithm *C4MCOverlay::GetAlgo(const char *szName)
{
	// search map
	for (C4MCAlgorithm *pAlgo = &C4MCAlgoMap[0]; pAlgo->Function; pAlgo++)
		// check name
		if (SEqual(pAlgo->Identifier, szName))
			// success!
			return pAlgo;
	// nothing found
	return nullptr;
}

void C4MCOverlay::Evaluate()
{
	// inherited
	C4MCNode::Evaluate();
	// get mat color
	if (Inside<int32_t>(Material,0,MapCreator->MatMap->Num-1))
	{
		MatClr=MapCreator->TexMap->GetIndexMatTex(MapCreator->MatMap->Map[Material].Name, *Texture ? Texture : nullptr);
		if(MatClr == 0 || !Sub)
			MatClrBkg = 0;
		else
			MatClrBkg = Mat2PixColDefault(MTunnel);
	}
	else
	{
		MatClr=0;
		MatClrBkg=0;
	}

	// calc size
	if (Owner)
	{
		C4MCOverlay *pOwnrOvrl;
		if ((pOwnrOvrl=OwnerOverlay()))
		{
			int32_t iOwnerWdt=pOwnrOvrl->Wdt; int32_t iOwnerHgt=pOwnrOvrl->Hgt;
			X = RX.Evaluate(iOwnerWdt) + pOwnrOvrl->X;
			Y = RY.Evaluate(iOwnerHgt) + pOwnrOvrl->Y;
			Wdt = RWdt.Evaluate(iOwnerWdt);
			Hgt = RHgt.Evaluate(iOwnerHgt);
			OffX = ROffX.Evaluate(iOwnerWdt);
			OffY = ROffY.Evaluate(iOwnerHgt);
		}
	}
	// calc seed
	if (!(Seed=FixedSeed))
	{
		int32_t r1=Random(32768);
		int32_t r2=Random(65536);
		Seed=(r1<<16) | r2;
	}
}

C4MCOverlay *C4MCOverlay::FirstOfChain()
{
	// run backwards until nullptr, non-overlay or overlay without operator is found
	C4MCOverlay *pOvrl=this;
	C4MCOverlay *pPrevO;
	while (pOvrl->Prev)
	{
		if (!(pPrevO=pOvrl->Prev->Overlay())) break;
		if (pPrevO->Op == MCT_NONE) break;
		pOvrl=pPrevO;
	}
	// done
	return pOvrl;
}

bool C4MCOverlay::CheckMask(int32_t iX, int32_t iY)
{
	// bounds match?
	if (!LooseBounds) if (iX<X || iY<Y || iX>=X+Wdt || iY>=Y+Hgt) return false;
	if (Config.General.DebugRec)
	{
		C4RCTrf rc;
		rc.x=iX; rc.y=iY; rc.Rotate=Rotate; rc.Turbulence=Turbulence;
		AddDbgRec(RCT_MCT1, &rc, sizeof(rc));
	}
	C4Real dX=itofix(iX); C4Real dY=itofix(iY);
	// apply turbulence
	if (Turbulence)
	{
		const C4Real Rad2Grad = itofix(3754936, 65536);
		int32_t j=3;
		for (int32_t i=10; i<=Turbulence; i*=10)
		{
			int32_t Seed2; Seed2=Seed;
			for (int32_t l=0; l<Lambda+1; ++l)
			{
				for (C4Real d=itofix(2); d<6; d+=C4REAL10(15))
				{
					dX += Sin(((dX / 7 + itofix(Seed2) / ZoomX + dY) / j + d) * Rad2Grad) * j / 2;
					dY += Cos(((dY / 7 + itofix(Seed2) / ZoomY + dX) / j - d) * Rad2Grad) * j / 2;
				}
				Seed2 = (Seed * (Seed2<<3) + 0x4465) & 0xffff;
			}
			j+=3;
		}
	}
	// apply rotation
	if (Rotate)
	{
		C4Real dXo(dX), dYo(dY);
		dX = dXo*Cos(itofix(Rotate)) - dYo*Sin(itofix(Rotate));
		dY = dYo*Cos(itofix(Rotate)) + dXo*Sin(itofix(Rotate));
	}
	if (Rotate || Turbulence)
		{ iX=fixtoi(dX, ZoomX); iY=fixtoi(dY, ZoomY); }
	else
		{ iX*=ZoomX; iY*=ZoomY; }
	if (Config.General.DebugRec)
	{
		C4RCPos rc2;
		rc2.x=iX; rc2.y=iY;
		AddDbgRec(RCT_MCT2, &rc2, sizeof(rc2));
	}
	// apply offset
	iX-=OffX*ZoomX; iY-=OffY*ZoomY;
	// check bounds, if loose
	if (LooseBounds) if (iX<X*ZoomX || iY<Y*ZoomY || iX>=(X+Wdt)*ZoomX || iY>=(Y+Hgt)*ZoomY) return Invert;
	// query algorithm
	return (Algorithm->Function) (this, iX, iY)^Invert;
}

bool C4MCOverlay::RenderPix(int32_t iX, int32_t iY, BYTE &rPix, BYTE &rPixBkg, C4MCTokenType eLastOp, bool fLastSet, bool fDraw, C4MCOverlay **ppPixelSetOverlay)
{
	// algo match?
	bool SetThis=CheckMask(iX, iY);
	bool DoSet;
	// exec last op
	switch (eLastOp)
	{
	case MCT_AND: // and
		DoSet=SetThis&&fLastSet;
		break;
	case MCT_OR: // or
		DoSet=SetThis||fLastSet;
		break;
	case MCT_XOR: // xor
		DoSet=SetThis^fLastSet;
		break;
	default: // no op
		DoSet=SetThis;
		break;
	}

	// set pix to local value and exec children, if no operator is following
	if ((DoSet && fDraw && Op == MCT_NONE) || Group)
	{
		// groups don't set a pixel value, if they're associated with an operator
		fDraw &= !Group || (Op == MCT_NONE);
		if (fDraw && DoSet && !Mask)
		{
			rPix=MatClr;
			rPixBkg=MatClrBkg;
			if (ppPixelSetOverlay) *ppPixelSetOverlay = this;
		}
		bool fLastSetC=false; eLastOp=MCT_NONE;
		// evaluate children overlays, if this was painted, too
		for (C4MCNode *pChild=Child0; pChild; pChild=pChild->Next)
			if (C4MCOverlay *pOvrl=pChild->Overlay())
			{
				fLastSetC=pOvrl->RenderPix(iX, iY, rPix, rPixBkg, eLastOp, fLastSetC, fDraw, ppPixelSetOverlay);
				if (Group && (pOvrl->Op == MCT_NONE))
					DoSet |= fLastSetC;
				eLastOp=pOvrl->Op;
			}
		// add evaluation-callback
		if (pEvaluateFunc && DoSet && fDraw) pEvaluateFunc->EnablePixel(iX, iY);
	}
	// done
	return DoSet;
}

bool C4MCOverlay::PeekPix(int32_t iX, int32_t iY)
{
	// start with this one
	C4MCOverlay *pOvrl=this; bool fLastSetC=false; C4MCTokenType eLastOp=MCT_NONE; BYTE Crap;
	// loop through op chain
	while (true)
	{
		fLastSetC=pOvrl->RenderPix(iX, iY, Crap, Crap, eLastOp, fLastSetC, false);
		eLastOp=pOvrl->Op;
		if (!pOvrl->Op) break;
		// must be another overlay, since there's an operator
		// hopefully, the preparser will catch all the other crap
		pOvrl=pOvrl->Next->Overlay();
	}
	// return result
	return fLastSetC;
}

// point

C4MCPoint::C4MCPoint(C4MCNode *pOwner) : C4MCNode(pOwner)
{
	// zero members
	X=Y=0;
}

C4MCPoint::C4MCPoint(C4MCParser* pParser, C4MCNode *pOwner, C4MCPoint &rTemplate, bool fClone) : C4MCNode(pParser, pOwner, rTemplate, fClone)
{
	// copy fields
	X=rTemplate.X; Y=rTemplate.Y;
	RX=rTemplate.RX; RY=rTemplate.RY;
}

void C4MCPoint::Default()
{
	X=Y=0;
}

bool C4MCPoint::SetField(C4MCParser *pParser, const char *szField, const char *szSVal, int32_t iVal, C4MCTokenType ValType)
{
	// only explicit %/px
	if (ValType == MCT_INT) return false;
	if (SEqual (szField, "x"))
	{
		RX.Set(IntPar, ValType == MCT_PERCENT);
		return true;
	}
	else if (SEqual (szField, "y"))
	{
		RY.Set(IntPar, ValType == MCT_PERCENT);
		return true;
	}
	return false;
}

void C4MCPoint::Evaluate()
{
	// inherited
	C4MCNode::Evaluate();
	// get mat color
	// calc size
	if (Owner)
	{
		C4MCOverlay *pOwnrOvrl;
		if ((pOwnrOvrl=OwnerOverlay()))
		{
			X = RX.Evaluate(pOwnrOvrl->Wdt) + pOwnrOvrl->X;
			Y = RY.Evaluate(pOwnrOvrl->Hgt) + pOwnrOvrl->Y;
		}
	}
}

// map

C4MCMap::C4MCMap(C4MCNode *pOwner) : C4MCOverlay(pOwner)
{

}

C4MCMap::C4MCMap(C4MCParser* pParser, C4MCNode *pOwner, C4MCMap &rTemplate, bool fClone) : C4MCOverlay(pParser, pOwner, rTemplate, fClone)
{

}

void C4MCMap::Default()
{
	// inherited
	C4MCOverlay::Default();
	// size by landscape def
	MapCreator->Landscape->GetMapSize(Wdt, Hgt, MapCreator->PlayerCount);
}

bool C4MCMap::RenderTo(BYTE *pToBuf, BYTE *pToBufBkg, int32_t iPitch)
{
	// set current render target
	if (MapCreator) MapCreator->pCurrentMap=this;
	// draw pixel by pixel
	for (int32_t iY=0; iY<Hgt; iY++)
	{
		for (int32_t iX=0; iX<Wdt; iX++)
		{
			// default to sky
			BYTE dummyPix;
			*pToBuf=0;
			if (pToBufBkg) *pToBufBkg=0;
			// render pixel value
			C4MCOverlay *pRenderedOverlay = nullptr;
			RenderPix(iX, iY, *pToBuf, pToBufBkg ? *pToBufBkg : dummyPix, MCT_NONE, false, true, &pRenderedOverlay);
			// add draw-callback for rendered overlay
			if (pRenderedOverlay)
				if (pRenderedOverlay->pDrawFunc)
					pRenderedOverlay->pDrawFunc->EnablePixel(iX, iY);
			// next pixel
			pToBuf++;
			if (pToBufBkg) pToBufBkg++;
		}
		// next line
		pToBuf+=iPitch-Wdt;
		if (pToBufBkg) pToBufBkg+=iPitch-Wdt;
	}
	// reset render target
	if (MapCreator) MapCreator->pCurrentMap=nullptr;
	// success
	return true;
}

void C4MCMap::SetSize(int32_t iWdt, int32_t iHgt)
{
	// store new size
	Wdt=iWdt; Hgt=iHgt;
	// update relative values
	MapCreator->ReEvaluate();
}


// map creator

C4MapCreatorS2::C4MapCreatorS2(C4SLandscape *pLandscape, C4TextureMap *pTexMap, C4MaterialMap *pMatMap, int iPlayerCount) : C4MCNode(nullptr)
{
	// me r b creator
	MapCreator=this;
	// store members
	Landscape=pLandscape; TexMap=pTexMap; MatMap=pMatMap;
	PlayerCount=iPlayerCount;
	// set engine field for default stuff
	DefaultMap.MapCreator=this;
	DefaultOverlay.MapCreator=this;
	DefaultPoint.MapCreator=this;
	// default to landscape settings
	Default();
}

C4MapCreatorS2::~C4MapCreatorS2()
{
	// clear fields
	Clear();
}

void C4MapCreatorS2::Default()
{
	// default templates
	DefaultMap.Default();
	DefaultOverlay.Default();
	DefaultPoint.Default();
	pCurrentMap=nullptr;
}

void C4MapCreatorS2::Clear()
{
	// clear nodes
	C4MCNode::Clear();
	// clear callbacks
	CallbackArrays.Clear();
	// defaults templates
	Default();
}

bool C4MapCreatorS2::ReadFile(const char *szFilename, C4Group *pGrp)
{
	// create parser and read file
	try
	{
		C4MCParser(this).ParseFile(szFilename, pGrp);
	}
	catch (C4MCParserErr err)
	{
		err.show();
		return false;
	}
	// success
	return true;
}

bool C4MapCreatorS2::ReadScript(const char *szScript)
{
	// create parser and read
	try
	{
		C4MCParser(this).Parse(szScript);
	}
	catch (C4MCParserErr err)
	{
		err.show();
		return false;
	}
	// success
	return true;
}

C4MCMap *C4MapCreatorS2::GetMap(const char *szMapName)
{
	C4MCMap *pMap=nullptr; C4MCNode *pNode;
	// get map
	if (szMapName && *szMapName)
	{
		// by name...
		if ((pNode = GetNodeByName(szMapName)))
			if (pNode->Type() == MCN_Map)
				pMap = (C4MCMap *) pNode;
	}
	else
	{
		// or simply last map entry
		for (pNode = ChildL; pNode; pNode=pNode->Prev)
			if (pNode->Type() == MCN_Map)
			{
				pMap = (C4MCMap *) pNode;
				break;
			}
	}
	return pMap;
}

bool C4MapCreatorS2::Render(const char *szMapName, CSurface8*& sfcMap, CSurface8*& sfcMapBkg)
{
	assert(sfcMap == nullptr);
	assert(sfcMapBkg == nullptr);

	// get map
	C4MCMap *pMap=GetMap(szMapName);
	if (!pMap) return false;

	// get size
	int32_t sfcWdt, sfcHgt;
	sfcWdt=pMap->Wdt; sfcHgt=pMap->Hgt;
	if (!sfcWdt || !sfcHgt) return false;

	// create surfaces
	sfcMap = new CSurface8(sfcWdt, sfcHgt);
	sfcMapBkg = new CSurface8(sfcWdt, sfcHgt);
	assert(sfcMap->Pitch == sfcMapBkg->Pitch);

	// render map to surface
	pMap->RenderTo(sfcMap->Bits, sfcMapBkg->Bits, sfcMap->Pitch);

	// success
	return true;
}

static inline void DWordAlign(int &val)
{
	if (val%4) { val>>=2; val<<=2; val+=4; }
}

BYTE *C4MapCreatorS2::RenderBuf(const char *szMapName, int32_t &sfcWdt, int32_t &sfcHgt)
{
	// get map
	C4MCMap *pMap=GetMap(szMapName);
	if (!pMap) return nullptr;

	// get size
	sfcWdt=pMap->Wdt; sfcHgt=pMap->Hgt;
	if (!sfcWdt || !sfcHgt) return nullptr;
	int dwSfcWdt = sfcWdt;
	DWordAlign(dwSfcWdt);
	sfcWdt = dwSfcWdt;

	// create buffer
	BYTE *buf=new BYTE[sfcWdt*sfcHgt];

	// render and return it
	pMap->RenderTo(buf, nullptr, sfcWdt);
	return buf;
}

C4MCParserErr::C4MCParserErr(C4MCParser *pParser, const char *szMsg)
{
	// create error message
	sprintf(Msg, "%s: %s (%d)", pParser->Filename, szMsg, pParser->BPos ? SGetLine(pParser->BPos, pParser->CPos) : 0);
}

C4MCParserErr::C4MCParserErr(C4MCParser *pParser, const char *szMsg, const char *szPar)
{
	char Buf[C4MaxMessage];
	// create error message
	sprintf(Buf, szMsg, szPar);
	sprintf(Msg, "%s: %s (%d)", pParser->Filename, Buf, pParser->BPos ? SGetLine(pParser->BPos, pParser->CPos) : 0);
}

void C4MCParserErr::show()
{
	// log error
	Log(Msg);
}


// parser

C4MCParser::C4MCParser(C4MapCreatorS2 *pMapCreator)
{
	// store map creator
	MapCreator=pMapCreator;
	// reset some fields
	Code=nullptr; BPos = nullptr; CPos=nullptr; *Filename=0;
}

C4MCParser::~C4MCParser()
{
	// clean up
	Clear();
}

void C4MCParser::Clear()
{
	// clear code if present
	if (Code) delete [] Code; Code=nullptr; BPos = nullptr; CPos=nullptr;
	// reset filename
	*Filename=0;
}

bool C4MCParser::AdvanceSpaces()
{
	char C, C2 = (char) 0;
	// defaultly, not in comment
	int32_t InComment = 0; // 0/1/2 = no comment/line comment/multi line comment
	// don't go past end
	while ((C = *CPos))
	{
		// loop until out of comment and non-whitespace is found
		switch (InComment)
		{
		case 0:
			if (C == '/')
			{
				CPos++;
				switch (*CPos)
				{
				case '/': InComment = 1; break;
				case '*': InComment = 2; break;
				default: CPos--; return true;
				}
			}
			else if ((BYTE) C > 32) return true;
			break;
		case 1:
			if (((BYTE) C == 13) || ((BYTE) C == 10)) InComment = 0;
			break;
		case 2:
			if ((C == '/') && (C2 == '*')) InComment = 0;
			break;
		}
		// next char; store prev
		CPos++; C2 = C;
	}
	// end of code reached; return false
	return false;
}

bool C4MCParser::GetNextToken()
{
	// move to start of token
	if (!AdvanceSpaces()) { CurrToken=MCT_EOF; return false; }
	// store offset
	const char *CPos0 = CPos;
	int32_t Len = 0;
	// token get state
	enum TokenGetState
	{
		TGS_None,       // just started
		TGS_Ident,      // getting identifier
		TGS_Int,        // getting integer
		TGS_Dir         // getting directive
	};
	TokenGetState State = TGS_None;

	// loop until finished
	while (true)
	{
		// get char
		char C = *CPos;

		switch (State)
		{
		case TGS_None:
			// get token type by first char
			// +/- are operators
			if ((((C >= '0') && (C <= '9')) || (C == '+') || (C == '-')))
				State = TGS_Int;                              // integer by +, -, 0-9
			else if (C == '#')  State = TGS_Dir;                              // directive by "#"
			else if (C == ';') {CPos++; CurrToken=MCT_SCOLON;   return true; }  // ";"
			else if (C == '=') {CPos++; CurrToken=MCT_EQ;       return true; }  // "="
			else if (C == '{') {CPos++; CurrToken=MCT_BLOPEN;   return true; }  // "{"
			else if (C == '}') {CPos++; CurrToken=MCT_BLCLOSE;  return true; }  // "}"
			else if (C == '&') {CPos++; CurrToken=MCT_AND;      return true; }  // "&"
			else if (C == '|') {CPos++; CurrToken=MCT_OR;       return true; }  // "|"
			else if (C == '^') {CPos++; CurrToken=MCT_XOR;      return true; }  // "^"
			else if (C >= '@')  State = TGS_Ident;                            // identifier by all non-special chars
			else
			{
				// unrecognized char
				CPos++;
				throw C4MCParserErr(this, "unexpected character found");
			}
			break;

		case TGS_Ident: // ident and directive: parse until non ident-char is found
		case TGS_Dir:
			if (((C < '0') || (C > '9')) && ((C < 'a') || (C > 'z')) && ((C < 'A') || (C > 'Z')) && (C != '_'))
			{
				// return ident/directive
				Len = std::min<int32_t>(Len, C4MaxName);
				SCopy(CPos0, CurrTokenIdtf, Len);
				if (State==TGS_Ident) CurrToken=MCT_IDTF; else CurrToken=MCT_DIR;
				return true;
			}
			break;

		case TGS_Int: // integer: parse until non-number is found
			if ((C < '0') || (C > '9'))
			{
				// return integer
				Len = std::min<int32_t>(Len, C4MaxName);
				CurrToken=MCT_INT;
				// check for "-"
				if (Len == 1 && *CPos0 == '-')
				{
					CurrToken = MCT_RANGE;
					return true;
				}
				else if ('%' == C) { CPos++; CurrToken=MCT_PERCENT; } // "%"
				else if ('p' == C)
				{
					// p or px
					++CPos;
					if ('x' == *CPos) ++CPos;
					CurrToken=MCT_PX;
				}
				SCopy(CPos0, CurrTokenIdtf, Len);
				// it's not, so return the int32_t
				sscanf(CurrTokenIdtf, "%d", &CurrTokenVal);
				return true;
			}
			break;

		}
		// next char
		CPos++; Len++;
	}

}

static void PrintNodeTree(C4MCNode *pNode, int depth)
{
	for (int i = 0; i < depth; ++i)
		printf("  ");
	switch (pNode->Type())
	{
	case MCN_Node: printf("Node %s\n", pNode->Name); break;
	case MCN_Overlay: printf("Overlay %s\n", pNode->Name); break;
	case MCN_Point: printf("Point %s\n", pNode->Name); break;
	case MCN_Map: printf("Map %s\n", pNode->Name); break;
	}
	for (C4MCNode * pChild = pNode->Child0; pChild; pChild = pChild->Next)
		PrintNodeTree(pChild, depth + 1);
}

void C4MCParser::ParseTo(C4MCNode *pToNode)
{
	C4MCNode *pNewNode=nullptr;  // new node
	bool Done=false;          // finished?
	C4MCNodeType LastOperand = C4MCNodeType(-1); // last first operand of operator
	char FieldName[C4MaxName];// buffer for current field to access
	C4MCNode *pCpyNode;       // node to copy from
	// current state
	enum ParseState
	{
		PS_NONE,      // just started
		PS_KEYWD1,    // got block-opening keyword (map, overlay etc.)
		PS_KEYWD1N,   // got name for block
		PS_AFTERNODE, // node has been parsed; expect ; or operator
		PS_GOTOP,     // got operator
		PS_GOTIDTF,   // got identifier, expect '=', ';' or '{'; identifier remains in CurrTokenIdtf
		PS_GOTOPIDTF, // got identifier after operator; accept ';' or '{' only
		PS_SETFIELD   // accept field value; field is stored in FieldName
	};
	ParseState State = PS_NONE;
	// parse until end of file (or block)
	while (GetNextToken())
	{
		switch (State)
		{
		case PS_NONE:
		case PS_GOTOP:
			switch (CurrToken)
			{
			case MCT_DIR:
				// top level needed
				if (!pToNode->GlobalScope())
					throw C4MCParserErr(this, C4MCErr_NoDirGlobal);
				// no directives so far
				throw C4MCParserErr(this, C4MCErr_UnknownDir, CurrTokenIdtf);
				break;
			case MCT_IDTF:
				// identifier: check keywords
				if (SEqual(CurrTokenIdtf, C4MC_Overlay))
				{
					// overlay: create overlay node, using default template
					pNewNode = new C4MCOverlay(this, pToNode, MapCreator->DefaultOverlay, false);
					State=PS_KEYWD1;
				}
				else if (SEqual(CurrTokenIdtf, C4MC_Point) && !pToNode->GetNodeByName(CurrTokenIdtf))
				{
					// only in overlays
					if (!pToNode->Overlay())
						throw C4MCParserErr(this, C4MCErr_PointOnlyOvl);
					// create point node, using default template
					pNewNode = new C4MCPoint(this, pToNode, MapCreator->DefaultPoint, false);
					State=PS_KEYWD1;
				}
				else if (SEqual(CurrTokenIdtf, C4MC_Map))
				{
					// map: check top level
					if (!pToNode->GlobalScope())
						throw C4MCParserErr(this, C4MCErr_MapNoGlobal);
					// create map node, using default template
					pNewNode = new C4MCMap(this, pToNode, MapCreator->DefaultMap, false);
					State=PS_KEYWD1;
				}
				else
				{
					// so this is either a field-set or a defined node
					// '=', ';' or '{' may follow, none of these will clear the CurrTokenIdtf
					// so safely assume it preserved and just update the state
					if (State==PS_GOTOP) State=PS_GOTOPIDTF; else State=PS_GOTIDTF;
				}
				// operator: check type
				if (State == PS_GOTOP && pNewNode)
					if (LastOperand != pNewNode->Type())
						throw C4MCParserErr(this, C4MCErr_OpTypeErr);
				break;
			case MCT_BLCLOSE:
			case MCT_EOF:
				// block done
				Done=true;
				break;
			default:
				// we don't like that
				throw C4MCParserErr(this, C4MCErr_IdtfExp);
				break;
			}
			break;
		case PS_KEYWD1:
			if (CurrToken==MCT_IDTF)
			{
				// name the current node
				SCopy(CurrTokenIdtf, pNewNode->Name, C4MaxName);
				State=PS_KEYWD1N;
				break;
			}
			else if (pToNode->GlobalScope())
			{
				// disallow unnamed nodes in global scope
				throw C4MCParserErr(this, C4MCErr_UnnamedNoGlbl);
			}
			// in local scope, allow unnamed; so continue
		case PS_KEYWD1N:
			// do expect a block opening
			if (CurrToken!=MCT_BLOPEN)
				throw C4MCParserErr(this, C4MCErr_BlOpenExp);
			// parse new node
			ParseTo(pNewNode);
			// check file end
			if (CurrToken==MCT_EOF)
				throw C4MCParserErr(this, C4MCErr_EOF);
			// reset state
			State=PS_AFTERNODE;
			break;
		case PS_GOTIDTF:
		case PS_GOTOPIDTF:
			switch (CurrToken)
			{
			case MCT_EQ:
				// so it's a field set
				// not after operators
				if (State==PS_GOTOPIDTF)
					throw C4MCParserErr(this, C4MCErr_Obj2Exp);
				// store field name
				SCopy(CurrTokenIdtf, FieldName, C4MaxName);
				// update state to accept value
				State=PS_SETFIELD;
				break;
			case MCT_BLOPEN:
			case MCT_SCOLON:
			case MCT_AND: case MCT_OR: case MCT_XOR:
				// so it's a node copy
				// local scope only
				if (pToNode->GlobalScope())
					throw C4MCParserErr(this, C4MCErr_ReinstNoGlobal, CurrTokenIdtf);
				// get the node
				pCpyNode=pToNode->GetNodeByName(CurrTokenIdtf);
				if (!pCpyNode)
					throw C4MCParserErr(this, C4MCErr_UnknownObj, CurrTokenIdtf);
				// create the copy
				switch (pCpyNode->Type())
				{
				case MCN_Overlay:
					// create overlay
					pNewNode=new C4MCOverlay(this, pToNode, *((C4MCOverlay *) pCpyNode), false);
					break;
				case MCN_Map:
					// maps not allowed
					if (pCpyNode->Type() == MCN_Map)
						throw C4MCParserErr(this, C4MCErr_MapNoGlobal, CurrTokenIdtf);
					break;
				default:
					// huh?
					throw C4MCParserErr(this, C4MCErr_ReinstUnknown, CurrTokenIdtf);
					break;
				}
				// check type for operators
				if (State==PS_GOTOPIDTF)
					if (LastOperand != pNewNode->Type())
						throw C4MCParserErr(this, C4MCErr_OpTypeErr);
				// further overloads?
				if (CurrToken==MCT_BLOPEN)
				{
					// parse new node
					ParseTo(pNewNode);
					// get next token, as we'll simply fall through to PS_AFTERNODE
					GetNextToken();
					// check file end
					if (CurrToken==MCT_EOF)
						throw C4MCParserErr(this, C4MCErr_EOF);
				}
				// reset state
				State=PS_AFTERNODE;
				break;

			default:
				throw C4MCParserErr(this, C4MCErr_EqSColonBlOpenExp);
				break;
			}
			// fall through to next case, if it was a named node reinstanciation
			if (State != PS_AFTERNODE) break;
		case PS_AFTERNODE:
			// expect operator or semicolon
			switch (CurrToken)
			{
			case MCT_SCOLON:
				// reset state
				State=PS_NONE;
				break;
			case MCT_AND:
			case MCT_OR:
			case MCT_XOR:
				// operator: not in global scope
				if (pToNode->GlobalScope())
					throw C4MCParserErr(this, C4MCErr_OpsNoGlobal);
				// set operator
				if (!pNewNode->SetOp(CurrToken))
					throw C4MCParserErr(this, "';' expected");
				LastOperand=pNewNode->Type();
				// update state
				State=PS_GOTOP;
				break;
			default:
				throw C4MCParserErr(this, C4MCErr_SColonOrOpExp);
				break;
			}
			// node done
			// evaluate node and children, if this is top-level
			// we mustn't evaluate everything immediately, because parents must be evaluated first!
			if (pToNode->GlobalScope()) pNewNode->ReEvaluate();
			pNewNode=nullptr;
			break;
		case PS_SETFIELD:
			ParseValue (pToNode, FieldName);
			/*// set field: accept integer constants and identifiers
			switch (CurrToken)
			  {
			  case MCT_IDTF:
			    // reset value field
			    CurrTokenVal=0;
			  case MCT_INT:
			    break;
			  default:
			    throw C4MCParserErr(this, C4MCErr_FieldConstExp, CurrTokenIdtf);
			    break;
			  }
			// set field
			if (!pToNode->SetField(this, FieldName, CurrTokenIdtf, CurrTokenVal, CurrToken))
			  // field not found
			  throw C4MCParserErr(this, C4MCErr_Field404, FieldName);
			// now, the one and only thing to get is a semicolon
			if (!GetNextToken())
			  throw C4MCParserErr(this, C4MCErr_EOF);
			if (CurrToken != MCT_SCOLON)
			  throw C4MCParserErr(this, C4MCErr_SColonExp);*/
			// reset state
			State=PS_NONE;
			break;
		}
		// don't get another token!
		if (Done) break;
	}
	// end of file expected?
	if (State != PS_NONE)
	{
		if (State == PS_GOTOP)
			throw C4MCParserErr(this, C4MCErr_Obj2Exp);
		else
			throw C4MCParserErr(this, C4MCErr_EOF);
	}
}

void C4MCParser::ParseValue(C4MCNode *pToNode, const char *szFieldName)
{
	int32_t Value;
	C4MCTokenType Type;
	switch (CurrToken)
	{
	case MCT_IDTF:
	{
		// set field
		if (!pToNode->SetField(this, szFieldName, CurrTokenIdtf, 0, CurrToken))
			// field not found
			throw C4MCParserErr(this, C4MCErr_Field404, szFieldName);
		if (!GetNextToken())
			throw C4MCParserErr(this, C4MCErr_EOF);
		break;
	}
	case MCT_INT:
	case MCT_PX:
	case MCT_PERCENT:
	{
		Value = CurrTokenVal;
		Type = CurrToken;
		if (!GetNextToken())
			throw C4MCParserErr(this, C4MCErr_EOF);
		// range
		if (MCT_RANGE == CurrToken)
		{
			// Get the second value
			if (!GetNextToken())
				throw C4MCParserErr(this, C4MCErr_EOF);
			if (MCT_INT == CurrToken || MCT_PX == CurrToken || MCT_PERCENT == CurrToken)
			{
				Value += Random (CurrTokenVal - Value);
			}
			else
				throw C4MCParserErr(this, C4MCErr_FieldConstExp, CurrTokenIdtf);
			Type = CurrToken;
			if (!GetNextToken())
				throw C4MCParserErr(this, C4MCErr_EOF);
		}
		if (!pToNode->SetField(this, szFieldName, CurrTokenIdtf, Value, Type))
			// field not found
			throw C4MCParserErr(this, C4MCErr_Field404, szFieldName);
		break;
	}
	default:
	{
		throw C4MCParserErr(this, C4MCErr_FieldConstExp, CurrTokenIdtf);
	}
	}

	// now, the one and only thing to get is a semicolon
	if (CurrToken != MCT_SCOLON)
		throw C4MCParserErr(this, C4MCErr_SColonExp);


	/*
	// set field: accept integer constants and identifiers
	switch (CurrToken)
	  {
	  case MCT_IDTF:
	    // reset value field
	    CurrTokenVal=0;
	    // set field
	    if (!pToNode->SetField(this, szFieldName, CurrTokenIdtf, CurrTokenVal, CurrToken))
	      // field not found
	      throw C4MCParserErr(this, C4MCErr_Field404, szFieldName);
	    break;
	  case MCT_INT:
	    Value1 = CurrTokenVal;
	    while (GetNextToken ())
	      {
	      switch (CurrToken)
	        {
	        case MCT_SCOLON:
	          // set field
	          if (!pToNode->SetField(this, szFieldName, CurrTokenIdtf, Value1, MCT_INT))
	            // field not found
	            throw C4MCParserErr(this, C4MCErr_Field404, szFieldName);
	          return;
	          break;
	        case MCT_RANGE:
	          break;
	        case MCT_INT:
	          Value2 = CurrTokenVal;
	          Value1 += Random (Value2 - Value1);
	          break;
	        default:
	          throw C4MCParserErr(this, C4MCErr_SColonExp);
	          break;
	        }
	      }
	    break;
	  default:
	    throw C4MCParserErr(this, C4MCErr_FieldConstExp, CurrTokenIdtf);
	    break;
	  }
	// now, the one and only thing to get is a semicolon
	if (!GetNextToken())
	  throw C4MCParserErr(this, C4MCErr_EOF);
	if (CurrToken != MCT_SCOLON)
	  throw C4MCParserErr(this, C4MCErr_SColonExp);*/
}

void C4MCParser::ParseFile(const char *szFilename, C4Group *pGrp)
{
	size_t iSize; // file size

	// clear any old data
	Clear();
	// store filename
	SCopy(szFilename, Filename, C4MaxName);
	// check group
	if (!pGrp) throw C4MCParserErr(this, C4MCErr_NoGroup);
	// get file
	if (!pGrp->AccessEntry(szFilename, &iSize))
		// 404
		throw C4MCParserErr(this, C4MCErr_404);
	// file is empty?
	if (!iSize) return;
	// alloc mem
	Code = new char[iSize+1];
	// read file
	pGrp->Read((void *) Code, iSize);
	Code[iSize]=0;
	// parse it
	BPos=Code;
	CPos=Code;
	ParseTo(MapCreator);
	if (false) PrintNodeTree(MapCreator, 0);
	// free code
	// on errors, this will be done be destructor
	Clear();
}

void C4MCParser::Parse(const char *szScript)
{
	// clear any old data
	Clear();
	// parse it
	BPos=szScript;
	CPos=szScript;
	ParseTo(MapCreator);
	if (false) PrintNodeTree(MapCreator, 0);
	// free code
	// on errors, this will be done be destructor
	Clear();

}

void C4MCParser::ParseMemFile(const char *szScript, const char *szFilename)
{
	// clear any old data
	Clear();
	// store filename
	SCopy(szFilename, Filename, C4MaxName);
	// parse it
	BPos=szScript;
	CPos=szScript;
	ParseTo(MapCreator);
	// on errors, this will be done be destructor
	Clear();
}


// algorithms ---------------------

// helper func
bool PreparePeek(C4MCOverlay **ppOvrl, int32_t &iX, int32_t &iY, C4MCOverlay **ppTopOvrl)
{
	// zoom out
	iX/=(*ppOvrl)->ZoomX; iY/=(*ppOvrl)->ZoomY;
	// get owning overlay
	C4MCOverlay *pOvrl2=(*ppOvrl)->OwnerOverlay();
	if (!pOvrl2) return false;
	// get uppermost overlay
	C4MCOverlay *pNextOvrl;
	for (*ppTopOvrl=pOvrl2; (pNextOvrl=(*ppTopOvrl)->OwnerOverlay()); *ppTopOvrl=pNextOvrl) {}
	// get first of operator-chain
	pOvrl2=pOvrl2->FirstOfChain();
	// set new overlay
	*ppOvrl=pOvrl2;
	// success
	return true;
}

#define a  pOvrl->Alpha
#define b  pOvrl->Beta
#define s  pOvrl->Seed
#define z  C4MC_ZoomRes
#define z2 (C4MC_ZoomRes*C4MC_ZoomRes)

bool AlgoSolid(C4MCOverlay *pOvrl, int32_t iX, int32_t iY)
{
	// solid always solid :)
	return true;
}

bool AlgoRandom(C4MCOverlay *pOvrl, int32_t iX, int32_t iY)
{
	// totally random
	return !((((s ^ (iX<<2) ^ (iY<<5))^((s>>16)+1+iX+(iY<<2)))/17)%(a.Evaluate(C4MC_SizeRes)+2));
}

bool AlgoChecker(C4MCOverlay *pOvrl, int32_t iX, int32_t iY)
{
	// checkers with size of 10
	return !(((iX/(z*10))%2)^((iY/(z*10))%2));
}

bool AlgoBozo(C4MCOverlay *pOvrl, int32_t iX, int32_t iY)
{
	// do some bozo stuff - keep it regular here, since it may be modified by turbulence
	int32_t iXC=(iX/10+s+(iY/80))%(z*2)-z;
	int32_t iYC=(iY/10+s+(iX/80))%(z*2)-z;
	int32_t id=Abs(iXC*iYC); // ((iSeed^iX^iY)%z)
	return id > z2*(a.Evaluate(C4MC_SizeRes)+10)/50;
}

bool AlgoSin(C4MCOverlay *pOvrl, int32_t iX, int32_t iY)
{
	// a sine curve where bottom is filled
	return iY > fixtoi((Sin(itofix(iX/z*10))+1)*z*10);
}

bool AlgoBoxes(C4MCOverlay *pOvrl, int32_t iX, int32_t iY)
{
	// For percents instead of Pixels
	int32_t pxb = b.Evaluate(pOvrl->Wdt);
	int32_t pxa = a.Evaluate(pOvrl->Wdt);
	// return whether inside box
	return Abs(iX+(s%4738))%(pxb*z+1)<pxa*z+1 && Abs(iY+(s/4738))%(pxb*z+1)<pxa*z+1;
}

bool AlgoRndChecker(C4MCOverlay *pOvrl, int32_t iX, int32_t iY)
{
	// randomly set squares with size of 10
	return AlgoRandom(pOvrl, iX/(z*10), iY/(z*10));
}

bool AlgoLines(C4MCOverlay *pOvrl, int32_t iX, int32_t iY)
{
	// For percents instead of Pixels
	int32_t pxb = b.Evaluate(pOvrl->Wdt);
	int32_t pxa = a.Evaluate(pOvrl->Wdt);
	// return whether inside line
	return Abs(iX+(s%4738))%(pxb*z+1)<pxa*z+1;
}

bool AlgoBorder(C4MCOverlay *pOvrl, int32_t iX, int32_t iY)
{
	C4MCOverlay *pTopOvrl;
	// get params before, since pOvrl will be changed by PreparePeek
	int32_t la=a.Evaluate(pOvrl->Wdt); int32_t lb=b.Evaluate(pOvrl->Hgt);
	// prepare a pixel peek from owner
	if (!PreparePeek(&pOvrl, iX, iY, &pTopOvrl)) return false;
	// query a/b pixels in x/y-directions
	for (int32_t x=iX-la; x<=iX+la; x++) if (pTopOvrl->InBounds(x, iY)) if (!pOvrl->PeekPix(x, iY)) return true;
	for (int32_t y=iY-lb; y<=iY+lb; y++) if (pTopOvrl->InBounds(iX, y)) if (!pOvrl->PeekPix(iX, y)) return true;
	// nothing found
	return false;
}

bool AlgoMandel(C4MCOverlay *pOvrl, int32_t iX, int32_t iY)
{
	// how many iterations?
	uint32_t iMandelIter = a.Evaluate(C4MC_SizeRes) != 0 ? a.Evaluate(C4MC_SizeRes) : 1000;
	if (iMandelIter < 10) iMandelIter = 10;
	// calc c & ci values
	double c =  ((double) iX / z / pOvrl->Wdt - .5 * ((double) pOvrl->ZoomX / z)) * 4;
	double ci = ((double) iY / z / pOvrl->Hgt - .5 * ((double) pOvrl->ZoomY / z)) * 4;
	// create _z & _zi
	double _z = c, _zi = ci;
	double xz;
	uint32_t i;
	for (i=0; i<iMandelIter; i++)
	{
		xz = _z * _z - _zi * _zi;
		_zi = 2 * _z * _zi + ci;
		_z = xz + c;
		if (_z * _z + _zi * _zi > 4) break;
	}
	return !(i<iMandelIter);
}

bool AlgoGradient(C4MCOverlay *pOvrl, int32_t iX, int32_t iY)
{
	return (std::abs((iX^(iY*3)) * 2531011L) % 214013L) % z > iX / pOvrl->Wdt;
}

bool AlgoScript(C4MCOverlay *pOvrl, int32_t iX, int32_t iY)
{
	C4AulParSet Pars(iX, iY, pOvrl->Alpha.Evaluate(C4MC_SizeRes), pOvrl->Beta.Evaluate(C4MC_SizeRes));
	return ::GameScript.Call(FormatString("ScriptAlgo%s", pOvrl->Name).getData(), &Pars).getBool();
}

bool AlgoRndAll(C4MCOverlay *pOvrl, int32_t iX, int32_t iY)
{
	// return by seed and params; ignore pos
	return s%100<a.Evaluate(C4MC_SizeRes);
}

bool AlgoPolygon(C4MCOverlay *pOvrl, int32_t iX, int32_t iY)
{
	// Wo do not support empty polygons.
	if (!pOvrl -> ChildL) return false;
	int32_t uX = 0; // last point before current point
	int32_t uY = 0; // with uY != iY
	int32_t cX, cY; // current point
	int32_t lX = 0; // x of really last point before current point
	int32_t count = 0;
	bool ignore = false;
	int32_t zX; //Where edge intersects with line
	C4MCNode *pChild, *pStartChild;
	//get a point with uY!=iY, or anyone
	for (pChild = pOvrl -> ChildL; pChild->Prev; pChild = pChild->Prev)
		if (pChild->Type() == MCN_Point)
		{
			uX = ((C4MCPoint*) pChild) -> X * 100;
			lX = uX;
			uY = ((C4MCPoint*) pChild) -> Y * 100;
			if (iY != uY) break;
		}
	pStartChild = pChild -> Next;
	if (!pStartChild) pStartChild = pOvrl->Child0;
	if (!pStartChild) return false;
	for (pChild = pStartChild; ; pChild=pChild -> Next)
	{
		if (!pChild) pChild = pOvrl->Child0;
		if (pChild->Type() == MCN_Point)
		{
			cX = ((C4MCPoint*) pChild) -> X * 100;
			cY = ((C4MCPoint*) pChild) -> Y * 100;
			//If looking at line
			if (ignore)
			{
				//if C is on line
				if (cY == iY)
				{
					//if I is on edge
					if (((lX < iX) == (iX < cX)) || (cX == iX)) return true;
				}
				else
				{
					//if edge intersects line
					if ((uY < iY) == (iY < cY) && (lX >= iX)) count++;
					ignore = false;
					uX = cX;
					uY = cY;
				}
			}
			//if looking at ray
			else
			{
				//If point C lays on ray
				if (cY == iY)
				{
					//are I and C the same points?
					if (cX == iX) return true;
					//skip this point for now
					ignore = true;
				}
				else
				{
					//if edge intersects line
					if ((uY < iY) == (iY <= cY))
					{
						//and edge intersects ray, because both points are right of iX
						if (iX < std::min (uX, cX))
						{
							count++;
						}
						//or one is right of I
						else if (iX <= std::max (uX, cX))
						{
							//and edge intersects with ray
							if (iX < (zX = ((cX - uX) * (iY - uY) / (cY - uY)) + uX)) count++;
							//if I lays on CU
							if (zX == iX) return true;
						}
					}
					uX = cX;
					uY = cY;
				}
			}
			lX = cX;
		}
		if (pChild -> Next == pStartChild) break;
		if (!pChild -> Next) if (pStartChild == pOvrl->Child0) break;
	}
	//if edge has changed side of ray uneven times
	if ((count & 1) > 0) return true; else return false;
}

#undef a
#undef b
#undef s
#undef z
#undef z2

C4MCAlgorithm C4MCAlgoMap[] =
{
	{ "solid",      &AlgoSolid },
	{ "random",     &AlgoRandom },
	{ "checker",    &AlgoChecker },
	{ "bozo",       &AlgoBozo },
	{ "sin",        &AlgoSin },
	{ "boxes",      &AlgoBoxes },
	{ "rndchecker", &AlgoRndChecker },
	{ "lines",      &AlgoLines },
	{ "border",     &AlgoBorder },
	{ "mandel",     &AlgoMandel },
	{ "gradient",   &AlgoGradient },
	{ "script",     &AlgoScript },
	{ "rndall",     &AlgoRndAll },
	{ "poly",       &AlgoPolygon },
	{ "",     nullptr }
};

#define offsC4MCOvrl(x) reinterpret_cast<C4MCOverlayOffsetType>(&C4MCOverlay::x)

namespace {
	C4MCNodeAttr C4MCOvrlMap[] =
	{
		{ "x",           C4MCV_Percent,     offsC4MCOvrl(RX)            },
		{ "y",           C4MCV_Percent,     offsC4MCOvrl(RY)            },
		{ "wdt",         C4MCV_Percent,     offsC4MCOvrl(RWdt)          },
		{ "hgt",         C4MCV_Percent,     offsC4MCOvrl(RHgt)          },
		{ "ox",          C4MCV_Percent,     offsC4MCOvrl(ROffX)         },
		{ "oy",          C4MCV_Percent,     offsC4MCOvrl(ROffY)         },
		{ "mat",         C4MCV_Material,    offsC4MCOvrl(Material)      },
		{ "tex",         C4MCV_Texture,     offsC4MCOvrl(Texture)       },
		{ "algo",        C4MCV_Algorithm,   offsC4MCOvrl(Algorithm)     },
		{ "sub",         C4MCV_Boolean,     offsC4MCOvrl(Sub)           },
		{ "zoomX",       C4MCV_Zoom,        offsC4MCOvrl(ZoomX)         },
		{ "zoomY",       C4MCV_Zoom,        offsC4MCOvrl(ZoomY)         },
		{ "a",           C4MCV_Pixels,      offsC4MCOvrl(Alpha)         },
		{ "b",           C4MCV_Pixels,      offsC4MCOvrl(Beta)          },
		{ "turbulence",  C4MCV_Integer,     offsC4MCOvrl(Turbulence)    },
		{ "lambda",      C4MCV_Integer,     offsC4MCOvrl(Lambda)        },
		{ "rotate",      C4MCV_Integer,     offsC4MCOvrl(Rotate)        },
		{ "seed",        C4MCV_Integer,     offsC4MCOvrl(FixedSeed)     },
		{ "invert",      C4MCV_Boolean,     offsC4MCOvrl(Invert)        },
		{ "loosebounds", C4MCV_Boolean,     offsC4MCOvrl(LooseBounds)   },
		{ "grp",         C4MCV_Boolean,     offsC4MCOvrl(Group)         },
		{ "mask",        C4MCV_Boolean,     offsC4MCOvrl(Mask)          },
		{ "evalFn",      C4MCV_ScriptFunc,  offsC4MCOvrl(pEvaluateFunc) },
		{ "drawFn",      C4MCV_ScriptFunc,  offsC4MCOvrl(pDrawFunc)     },
		{ "", C4MCV_None, nullptr }
	};
}
