// Endless Night's Dream
//  Copyright (C) 2003-2006 Jaret "Kero Kato" Cantu
//  http://www.realitysend.com/dreamon
//    If you are looking at this code, CLOSE THIS FILE IMMEDIATELY. This code is NOT meant to be viewed
//   but used on pages containing END puzzles. This code is not to be copied, dissected, or hacked in any way.







































// objects
function Dreamer(name, clas, algn, catg, type, imag, powr)
{
	this.name = name;
	this.clas = clas;
	this.type = type;
	this.algn = algn;
	this.catg = catg;
	this.avtr = imag;
	this.powr = powr;
	this.curpowr = powr;

	this.spiritMax = dreamerSpiritMax;
}
//  Dreamer methods
function dreamerSpiritMax()
{
	return (10 + this.powr);
}

function BoardPiece(dy, dx, fy, fx, stype, special, method, priority)
{	// dy=dx=power=0=explosion
	this.dy=dy; this.dx=dx; this.fy=fy; this.fx=fx; this.stype=stype; this.special=special;
	this.method = method;
	this.priority = priority; // 0=ready, every progression counts it down
}

function TrickShot(name, type, trick)
{
	this.name = name;
	this.shot = typeRevert(type) | BLOCK;
	this.trick = trick;
}
function BigShot(name, desc, cost, shot, limit, trick, type)
{
	this.name = name;
	this.desc = desc;
	this.shot = shot;
	this.cost = cost;
	this.trick = (trick&(TS_STRAIGHT|TS_SPLIT)?0:TS_STRAIGHT)|trick;
	this.type = type;
	this.limit = limit;

	if(gametype < 4 && cost.length >= 6)
	{
		this.cost[1]+= cost[5];
		this.cost[5] = 0;
	}
	if(gametype < 4 && cost.length >= 5)
	{
		this.cost[0]+= cost[4];
		this.cost[4] = 0;
	}
	if(gametype < 3 && cost.length >= 4)
	{
		this.cost[0]+= cost[3];
		this.cost[3] = 0;
	}
	if(gametype < 2 && cost.length >= 3)
	{
		this.cost[1]+= cost[2];
		this.cost[2] = 0;
	}

	this.canUse = function() {
		var i, total = gameplay + (gameplay >= 4 ? 2 : 1);
		for(i=0; i<total && i<this.cost.length; i++)
			if(powerbar[i] < this.cost[i]) return false;
		if(this.limit != 0)
		{
			this.limit--;
			// remove from use
		}
		for(i=0; i<total && i<this.cost.length; i++)
			modPowerBar(i, -this.cost[i])
		return true;
	}
}

function SpecialTile(name, img, parts, desc, expl)
{
	this.name = name;
	this.desc = desc;
	this.img  = img;
	this.parts = parts;
	this.onExplode = expl;
}

// stock objects
function getBigShot(bgst)
{
	switch(bgst)
	{
	case "BakuJar":
		var o = new BigShot("Baku Jar", "Permenantly expend one Power Level to draw a block in towards you; drains TOTEMS.",
					[2,2,2,2,2,2], bakuJarBlast);
		o.normalCost = o.canUse;
		// This is the only time this function will ever be needed; no other ability reduced TOTAL power, just
		//  the per-game power (cur/max)
		o.canUse = function() {
			if(player.curpowr == 0)
			{
				describe("You need at least one POWER LEVEL to use BAKU JAR.");
				return false;
			}
			return this.normalCost();
		}
		return o;
	case "SpiritWall":
		return new BigShot("Spirit Wall", "Summon spirits into the bottom row to protect you.",
					[0,0], summonSpirits);
	// Actual BigShots
	//  Site Dreamers
	case "DawnsRay":
		return new BigShot("Dawn\'s Ray", "Three-wide beam that completely destroys all NIGHTMARE spiris.",
					[50, 0,0,0,0,0], bigShotDawnsRay);
	}
}
function getStockBlock(stbl)
{
	switch(stbl)
	{
	case "Protection":
		return new SpecialTile("Protection", GRAPHIC_BASE + "crystal.gif", [IMMUN], "Protect yourself at all costs; when these run out, you lose!",
						function() { addQueue(0,0,0,0, NULLT, 0, protectDestroy, 2); });
	}
}

// methods
//  timing
function progress()
{
	if(messages > 0)
	{
		var i,j, nothing=true;
		for(i=messages-1; i>=0; i--)
		{
			if(queue[i].priority == 0)
			{
				nothing = false;

				queue[i].method(queue[i].dy, queue[i].dx, queue[i].fy, queue[i].fx, queue[i].stype, queue[i].special); // edit

				for(j=i; j<messages-1; j++)
					queue[j] = queue[j+1];
				messages--;
			}
		}
		if(nothing)
		{
			for(i=messages-1; i>=0; i--)
			{
				if(queue[i].priority > 0) queue[i].priority--;
			}
			clearPow();
			progress(); // fast; no timeout
		}
		else
			setTimeout('progress()', 250);
	}
	else if(shooting || !p_acting) // prevent from executing on a stopped shot
	{
		clearPow();
		wrapup();
		shooting = false;
		rotation = 0;
	}
}
function addQueue(dy, dx, fy, fx, stype, special, method, priority)
{
	queue[messages] = new BoardPiece(dy,dx,fy,fx,stype,special,method,priority);
	messages++;
}
//  field control
function wrapup()
{
	var y,x,i;
	if(p_acting)
	{
		if(gameplay != COUNTDOWN)
			turns++;
		else
			turns--;
	}
	document.dreamon.turncounter.value = turns;
	if(gameplay == SURVIVAL)
	{
		if(deadEdge(tall-1))
		{
			gameOver(false);
			return;
		}
		dropField(1, tall-2, 0);
	}
	else if(gameplay == BATTLE)
	{
		p_acting = !p_acting;
		if(!p_acting && divide >= tall-2 && lineCount(tall-3,tall-2) > 0 || lineCount(tall-1, tall-1) == 0) // second part can always happen
		{
			cascadeLimited(1, divide, 0);
			altDivide(1);
			deadEdge(tall-1);
			gameOver(false);
		}
		if(!p_acting)
		{
			var dir = Number(p_acting ? 1 : -1);
			var lnmodiv = divide - dir;
			var stop = (p_acting ? 0 : tall-2);
			if(inBounds(divide+dir, 0) && lineCount(divide+dir, divide+dir) == 0 && lineCount(divide-dir,divide-dir) != 0)
			{
				if(divide < stop)
					cascadeLimited(dir, divide, stop);
				altDivide(dir);
			}
			else if(inBounds(lnmodiv, 0) && lnmodiv < stop)
				cascadeLimited(dir, lnmodiv, stop);
		}
		if(divide <= 1)
			gameOver(true);
	}
	else
	{
		if(isCleared())
			gameOver(true);
		else if(gameplay == CASCADE)
		{
			for(y=tall-2; y>=0; y--)
			{
				for(x=0; x<wide; x++)
				{
					if(!isEmpty(grid[y][x]) && isEmpty(grid[y+1][x]))
					{
						for(i=y+1; i<tall&&isEmpty(grid[i][x]); i++)
						{					
							setCoord(i,x,grid[i-1][x]);
							setCoord(i-1,x,EMPTY);
						}					
					}
				}
			}
		}
		else if(gameplay == COUNTDOWN && turns == 0)
		{
			gameOver(false);
			return;
		}
	}
	if(endProc) endProc();
}
function gameOver(victory)
{
	gameplay = GAMEOVER;
	if(victory)
	{
		describe("You win!");
	}
	else
	{
		describe("Game Over....");
		if(endProc) endProc();
	}
}
function dropField(dy, start, stop)
{
	var y,x, ny;
	if(!dy && !start && !stop)
	{
		dy = 1;
		start = tall-2;
		stop = 0;
	}
	for(y=start, ny=Number(start)+Number(dy); y!=stop-dy; y-= dy, ny-= dy)
	{
		for(x=0; x<wide; x++)
		{
			if(grid[y][x] != grid[ny][x])
				setCoord(ny, x, grid[y][x]);
		}
	}
	clearRange(stop,stop);
	flopLine(stop);
}
function flopLine(y)
{
	var x, cnt=0;
	for(x=0; x<wide; x++)
		if(isEmpty(grid[y][x]) && setRand(y, x))
			cnt++;
	return cnt;
}
function cascadeLimited(dy, start, stop)
{
	var y, x;
	if((start < stop) != (dy < 0))
	{
		alert("Bad CL: " + dy + ", " + start + ", " + stop);
		return;
	}
	for(y=start-dy; y!=stop-dy; y-= dy)
	{
		for(x=0; x<wide; x++)
		{
			if(inBounds(y,x) && !isEmpty(grid[y][x]) && inBounds(y+dy,x) && isEmpty(grid[y+dy][x]))
			{
				setCoord(y+dy,x,grid[y][x]);
				setCoord(y,x,EMPTY);
			}
		}
	}
}
function deadEdge(y)
{
	var dead = false;
	for(var x=0; x<wide; x++)
	{
		if(!isEmpty(grid[y][x]))
		{
			setCoord(y,x, (grid[y][x]&TYPE)|KBOOM);
			dead = true;
		}
	}
	return dead;
}
function lineCount(top, bot)
{
	var x, y, count = 0;
	var start = (top<bot ? top : bot);
	var stop  = (top>bot ? top : bot);
	for(y=start; y<=stop && y<tall && y>=0; y++)
	{
		for(x=0; x<wide; x++)
			if(!isEmpty(grid[y][x])) count++;
	}
	return count;
}
function altDivide(dd)
{
	var above = divide-1;
	var below = divide+1;
	if(inBounds(above,0))
	{
		document["borderl"+above].src = BLANK_GRAPHIC;
		document["borderr"+above].src = BLANK_GRAPHIC;
	}
	if(inBounds(divide,0))
	{
		document["borderl"+divide].src = BLANK_GRAPHIC;
		document["borderr"+divide].src = BLANK_GRAPHIC;
	}
	if(inBounds(below,0))
	{
		document["borderl"+below].src = BLANK_GRAPHIC;
		document["borderr"+below].src = BLANK_GRAPHIC;
	}
	divide+= dd;
	above+=  dd;
	below+=  dd;
	if(gameplay == BATTLE)
	{
		if(inBounds(above,0))
		{
			document["borderl"+above].src = GRAPHIC_BASE + "divmarkup.gif";
			document["borderr"+above].src = GRAPHIC_BASE + "divmarkup.gif";
		}
		if(inBounds(divide,0))
		{
			document["borderl"+divide].src = GRAPHIC_BASE + "divmarkmid.gif";
			document["borderr"+divide].src = GRAPHIC_BASE + "divmarkmid.gif";
		}
		if(inBounds(below,0))
		{
			document["borderl"+below].src = GRAPHIC_BASE + "divmarkdown.gif";
			document["borderr"+below].src = GRAPHIC_BASE + "divmarkdown.gif";
		}
	}
}
function push(dy, dx, fy, fx, stype)
{
	if(!inBounds(fy,fx)) return false;
	if(isEmpty(grid[fy][fx]))
	{
		setCoord(fy, fx, stype);
		return true;
	}
	else if(isOpposing(stype, grid[fy][fx]))
		addQueue(0,0,fy,fx,grid[fy][fx]&TYPE,0,explode,1)
	else if(!isOpposing(grid[fy][fx], stype)) // is _not_ lesser than -- still returns false
	{
		if(push(dy, dx, fy+dy, fx+dx, grid[fy][fx]))
		{
			setCoord(fy, fx, stype);
			return true;
		}
		addQueue(0,0,fy,fx,grid[fy][fx]&TYPE,0,explode,1)
	}
	return false;
}
function explode(dy, dx, fy, fx, stype, special)
{
	stype&= TYPE;
	var hits = chain(fy,fx,stype, 1);
	if(hits > 1 && p_acting)
	{
		var x, dhits = hits-1;
		if(player.algn == PURGE)
		{
			switch(hits)
			{
			case 0: case 1: x = 0; break;
			case 2: x = dhits*4; break;
			case 3: x = dhits*5.5; break;
			case 4: x = dhits*6.35; break;
			case 5: x = dhits*7.1; break;
			default:x = dhits*8;
			}
		}
		else if(player.algn == HRVST)
			x = dhits * 6;
		x = Math.ceil(x * gametype/2);
		//if(!(gameplay == BATTLE && fy >= divide))
		regainEnergy(x, stype);
	}
}
function chain(fy, fx, stype, init)
{
	if(!inBounds(fy,fx)) return 0;
	var gridpart = grid[fy][fx] & PART;
	var gridtype = grid[fy][fx] & TYPE;
	if(gridpart == KBOOM || gridpart == SHELL || gridpart == EMPTY) return 0;
	if(gridtype == stype || stype == OMNIT && gridtype != NULLT || gridtype == OMNIT && stype != NULLT)
	{
		if(isPart(IMMUN, gridpart) && !init)
			return 0;
		else if(isPart(THICK, gridpart))
			setCoord(fy, fx, gridtype|SHELL);
		else
			setCoord(fy, fx, gridtype|KBOOM);
		if(isSpecial(gridpart) && spectile[getSpecial(gridpart)].onExplode)
			spectile[getSpecial(gridpart)].onExplode();
		if(gridtype == OMNIT)
		{
			omniChain(fy-1, fx, OMNIT); // non-queued explosions
			omniChain(fy+1, fx, OMNIT);
			omniChain(fy, fx-1, OMNIT);
			omniChain(fy, fx+1, OMNIT);
		}
		return (1 + chain(fy-1, fx, gridtype) + chain(fy+1, fx, gridtype) + chain(fy, fx-1, gridtype) + chain(fy, fx+1, gridtype));
	}
	return 0;
}
function omniChain(fy, fx, stype)
{
	if(inBounds(fy,fx) && !isPart(IMMUN, grid[fy][fx]))
		explode(0,0, fy,fx, stype,0);
}
function clearPow()
{
	var x, y, gridpart, gridtype;
	for(y=0; y<tall; y++)
	{
		for(x=0; x<wide; x++)
		{
			gridpart = grid[y][x] & PART;
			gridtype = grid[y][x] & TYPE;
			if(gridpart == KBOOM || gridpart == SHELL)
			{
				/*if(gameplay == BATTLE) // do before grid type changes to null
				{
					if(y == tall-1 && !p_acting)
					{
						player.damage(1, gridtype);
						regainEnergy(6, gridtype);
					}
					else if(y == 0 && p_acting)
					{
						opponent.damage(1, gridtype); // if foe has energy, do that, too
						enemyPower+= 6;
					}
				}*/
				if(gridpart == SHELL)
					setCoord(y, x, gridtype|ENEMY);
				else
					setCoord(y, x, EMPTY);
			}
		}
	}
}
//  shot functions
function erasePrev(fy,fx,stype,special)
{
	if(inBounds(fy,fx) && !(special&TS_SKIPPING))
		setCoord(fy, fx, EMPTY);
}
function chameleon(dy,dx,fy,fx,stype,special)
{
	if(inBounds(fy-dx, fx+dy) && inBounds(fy+dx, fx-dy))
	{
		if(isEmpty(grid[fy-dx][fx+dy]) != isEmpty(grid[fy+dx][fx-dy])) // not both empty/both not empty
		{
			if(!isEmpty(grid[fy-dx][fx+dy]))
				stype = (stype&PART)|(TYPE&grid[fy-dx][fx+dy]);
			else
				stype = (stype&PART)|(TYPE&grid[fy+dx][fx-dy]);
		}
		else if(!isEmpty(grid[fy-dx][fx+dy]) && (TYPE&grid[fy-dx][fx+dy]) == (TYPE&grid[fy+dx][fx-dy]))
			stype = (stype&PART)|(TYPE&grid[fy+dx][fx-dy]);
	}
	else if(inBounds(fy-dx, fx+dy) && !isEmpty(grid[fy-dx][fx+dy]))
		stype = (stype&PART)|(TYPE&grid[fy-dx][fx+dy]);
	else if(inBounds(fy+dx, fx-dy) && !isEmpty(grid[fy+dx][fx-dy]))
		stype = (stype&PART)|(TYPE&grid[fy+dx][fx-dy]);
	if(!(special&(TS_SKIP|TS_SKIPPING)))
		setCoord(fy,fx,stype);
	return stype;
}
function disappear(dy,dx,fy,fx,stype,special)
{	// beginning of a skip
	if(!isEmpty(grid[fy][fx]))
	{	// begin hiding when unempty area is found
		special&= ~TS_SKIP;
		special|= TS_SKIPPING;
	}
	else
		setCoord(fy,fx,stype);
	return special;
}
function reappear(dy,dx,fy,fx,stype,special)
{	// end of a skip
	if(inBounds(fy-dy,fx-dx))
	{
		if(isEmpty(grid[fy][fx]))
		{	// bring out of hiding once returning to open air
			setCoord(fy,fx,stype);
			special&= ~TS_SKIPPING;
		}
	}
	return special;
}
function directionBreak(dy,dx,fy,fx,stype,special,techf)
{	// shot exploded, now may continue
	var tspecial = (special & ~(TS_DIRECTIONAL));
	if(special&TS_POWERFUL)
		addQueue(dy, dx, fy, fx, stype, tspecial, techf, 2);
	if(special&TS_BACKLASH)
		addQueue(-dy, -dx, fy, fx, stype, tspecial, techf, 2);
	if(special&TS_BREAKR)
	{
		if(dx==0)
			addQueue(dy, -dy, fy-dy, fx-dx, stype, tspecial, techf, 2);
		else if(dy==0)
			addQueue(dx,  dx, fy-dy, fx-dx, stype, tspecial, techf, 2);
		else if((dy>0)==(dx>0)) // same sign
			addQueue(dy, 0, fy-dy, fx-dx, stype, tspecial, techf, 2);
		else
			addQueue(0, dx, fy-dy, fx-dx, stype, tspecial, techf, 2);
	}
	if(special&TS_BREAKL)
	{
		if(dx==0)
			addQueue( dy, dy, fy-dy, fx-dx, stype, tspecial, techf, 2);
		else if(dy==0)
			addQueue(-dx, dx, fy-dy, fx-dx, stype, tspecial, techf, 2);
		else if((dy>0)==(dx>0)) // same sign
			addQueue(0, dx, fy-dy, fx-dx, stype, tspecial, techf, 2);
		else
			addQueue(dy, 0, fy-dy, fx-dx, stype, tspecial, techf, 2);
	}
}
function moveOn(dy,dx,fy,fx,stype,special, techf)
{	// didn't 'splode or reach end of board
	if(special&TS_CHAMELEON)
		stype = chameleon(dy,dx,fy,fx,stype,special);
	if(special&TS_SKIP)
		special = disappear(dy,dx,fy,fx,stype,special);
	else if(special&TS_SKIPPING)
		special = reappear(dy,dx,fy,fx,stype,special);
	addQueue(dy, dx, fy, fx, stype, special, techf, 0);
}
function hit(dy, dx, fy, fx, stype, special, techf)
{	// does not relate hit-or-miss, but rather continuation of movement; "hit" refers to hitting a space with a block
	var tspecial;
	if(!inBounds(fy,fx)) return false;
	if((special&TS_SKIP) || (special&TS_SKIPPING))
		return true;
	else if(isEmpty(grid[fy][fx]))
	{
		setCoord(fy, fx, stype);
		return true;
	}
	else if(isOpposing(stype, grid[fy][fx]))
	{
		addQueue(0,0,fy,fx,grid[fy][fx],0,explode,1);
		if(inBounds(fy-dy, fx-dx))
		{
			setCoord(fy-dy, fx-dx, stype);
			addQueue(0,0,fy-dy,fx-dx,stype,0,explode,1)
		}
		directionBreak(dy,dx,fy,fx,stype,special,techf);
	}
	else if(isOpposing(grid[fy][fx], stype)) // backwards-opposing to check lesser than
	{	// just like above, but without the target exploding
		if(inBounds(fy-dy, fx-dx))
		{
			setCoord(fy-dy, fx-dx, stype);
			addQueue(0,0,fy-dy,fx-dx,stype,0,explode,1)
		}
		directionBreak(dy,dx,fy,fx,stype,special,techf);
	}
	else // push
	{
		if(inBounds(fy-dy, fx-dx))
			grid[fy-dy][fx-dx] = stype; // reset for backwards-explosion
		if(push(dy, dx, fy, fx, stype))
		{
			if(inBounds(fy-dy, fx-dx))
				grid[fy-dy][fx-dx] = EMPTY;
			setCoord(fy, fx, stype);
			return true;
		}
		else
		{
			if(inBounds(fy-dy, fx-dx))
			{
				setCoord(fy-dy, fx-dx, stype);
				addQueue(0,0,fy-dy,fx-dx,stype,0,explode,1);
			}
			directionBreak(dy,dx,fy,fx,stype,special,techf);
		}
	}
	return false; 
}
function stopShot()
{
	queue = [queue[messages-1]]; // reduce to only the current item
	messages = 1; // one to represent the current item
	shooting = false;
	clearTimeout();
}
//  stock shot functions
function shoot(dy, dx, fy, fx, stype, special)
{
	erasePrev(fy,fx,stype,special);
	fy+= dy; fx+= dx;
	if(!inBounds(fy,fx))
	{
		if(inBounds(fy-dy,fx-dx))
		{
			setCoord(fy-dy, fx-dx, stype);
			addQueue(0,0,fy-dy,fx-dx,stype,0,explode,1);
		}
		directionBreak(dy,dx,fy,fx,stype,special,shoot);
	}
	else if(hit(dy,dx,fy,fx,stype,special,shoot)) // keep moving
		moveOn(dy,dx,fy,fx,stype,special, shoot);
}
function purge(dy, dx, fy, fx, stype, special)
{
	erasePrev(fy,fx,stype,special);
	fy+= dy; fx+= dx;
	if(inBounds(fy,fx))
	{
		var mt = isEmpty(grid[fy][fx]);
		if(mt || (special&(TS_SKIP|TS_SKIPPING)))
		{
			moveOn(dy,dx,fy,fx,stype,special,purge);
			if(mt) setImage(fy, fx, GRAPHIC_BASE + "purgeblock.gif");
		}
		else
		{
			addQueue(0,0,fy,fx,grid[fy][fx]&TYPE,0,explode,1);
			if(inBounds(fy+dy,fx+dx)) // explode next tile, too
				addQueue(0,0,fy+dy,fx+dx,grid[fy+dy][fx+dx]&TYPE,0,explode,1)
			directionBreak(dy,dx,fy,fx,stype,special,purge);
		}
	}
	else
		directionBreak(dy,dx,fy,fx,stype,special,purge);
}
function harvest(dy, dx, fy, fx, stype, special)
{
	erasePrev(fy,fx,stype,special);
	fy+= dy; fx+= dx;
	if(inBounds(fy,fx))
	{
		var mt = isEmpty(grid[fy][fx]);
		if(mt || (special&(TS_SKIP|TS_SKIPPING)))
		{
			moveOn(dy,dx,fy,fx,stype,special,harvest);
			if(mt) setImage(fy, fx, GRAPHIC_BASE + "harvestblock.gif");
		}
		else
		{
			addQueue(dy, dx, fy, fx, grid[fy][fx], special, shoot, 0); // push the block
			directionBreak(dy,dx,fy,fx,stype,special,purge);
		}
	}
	else
		directionBreak(dy,dx,fy,fx,stype,special,purge);
}
function summonSpirits(dy, dx, fy, fx, stype, special)
{
	var lnmodiv = divide - dy; //(p_acting ? 0 : 1); // line mod for divide
	var stop = (dy==-1 ? tall-2 : 0); // tall-1 w/o crystals
	var rndwal = (p_acting ? spiritwall : foewall); // wall is not actually random
	var maxs = (p_acting ? player.spiritMax() : opponent.spiritMax() );
	var cur = lineCount(lnmodiv, stop);
	var i, neg, add, typ;

	if(cur >= maxs)
	{
		describe("You are at your limit of spirits!");
		stopShot();
	}
	else if(!rndwal)
	{
		if(p_acting)
			describe("You have no Spirit Wall.");
		//else ignore, dream builder messed up
		stopShot();
	}
	else
	{
		fy+= dy; // get row

		var plc, p;
		var cat = (p_acting ? player.catg : opponent.catg);
		var enough;
		var needed = calcPowerArray(DREAM, 0);
		for(plc=0,p=0,i=-8; i<=8; i++) // if it is over, who cares?
		{
			if((1<<plc) & CAT_WALL_PAT[cat])
			{
				if(inBounds(fy, fx-i) && isEmpty(grid[fy][fx-i]))
				{
					add = rndwal[p];
					typ = (add&TYPE);
					if(!isEmpty(add))
						calcPowerArray(rndwal[plc], (typ==player.type?-3:-4), needed);
				}
				p++;
			}
			plc++;
		}
		if(p_acting)
		{
			if(divide >= tall-2)
			{
				enough = false;
				describe("No room left to summon spirits!");
				stopShot();
			}
			else if(checkPowerArray(needed))
			{
				enough = true;
				for(i=0; i<needed.length; i++)
					modPowerBar(DREAM+i, needed[i]);
			}
			else
			{
				enough = false;
				describe("Not enough to summon spirits!");
				stopShot();
			}
		}

		if(!p_acting || enough)
		{
			for(plc=0,p=0,i=-8; i<=8; i++)
			{
				if((1<<plc) & CAT_WALL_PAT[cat])
				{
					if(inBounds(fy, fx-i) && isEmpty(grid[fy][fx-i]))
					{
						add = rndwal[p];
						if(!isEmpty(add))
						{
							setCoord(fy,fx-i,add);
						}
					}
					p++;
				}
				plc++;
			}
		}

	}
}
function bakuJarBlast(dy,dx,fy,fx,stype,special)
{
	var dreamer = (p_acting ? player : opponent);
	var y, blk, drawn = -1;
	for(y=fy+dy; drawn==-1 && y<tall && y>=0; y+=dy)
		if(!isEmpty(grid[y][fx])) drawn = y;
	if(drawn != -1)
	{
		blk = grid[drawn][fx];
		if(drawn == fy+dy)
		{
			if(isSpecial(blk) && typeof(drainTotem) != undefined)
				drainTotem(getSpecial(blk));
			decMaxPowerLevel();
			var rdx = new Image();
			rdx.src = "http://www.realitysend.com/cgi-bin/endres/rdxpowlev.pl?" + randomize(100000);
			regainEnergy(10, blk&TYPE);
			addQueue(dy,dx,drawn,fx,blk,special, explode, 1);
		}
		else
		{
			setImage(fy+dy, fx, GRAPHIC_BASE + "crystal.gif"); // BakuJar Image
			erasePrev(drawn, fx);
			if(hit(-dy,-dx, drawn-dy,fx-dx, blk,special))
			{
				addQueue(dy,dx,fy,fx,stype,special, bakuJarBlast, 1);
			}
		}
	}
}
function bigShotDawnsRay(dy,dx,fy,fx,stype,special)
{
	// do somethin'
}
//  field action
function regainEnergy(amt, stype)
{
	if(stype != OMNIT && stype != NULLT)
		modPowerBar(stype, amt);
	else
	{
		var t = Math.ceil(amt / (gametype + ((gametype==4)?2:1)));
		modPowerBar(DREAM, t);
		modPowerBar(NMARE, t);
		if(gametype >= 2)
			modPowerBar(RESTL, t);
		if(gametype >= 3)
			modPowerBar(TRANC, t);
		if(gametype >= 4)
		{
			modPowerBar(SLUMB, t);
			modPowerBar(TEROR, t);
		}
	}
}
function canUseShot(stype, cost, techf)
{
	if(!document.dreamon.bigshot || document.dreamon.bigshot.selectedIndex==0) // standard
		return modPowerBar(stype, cost);
	else
		return bigshots[document.dreamon.bigshot.selectedIndex-1].canUse();
}
function dreamShot(loc)
{
	if(!shooting && gameplay != GAMEOVER && p_acting)
	{
		var ystart;
		if(gameplay != BATTLE)
			ystart = tall;
		else if(technique != summonSpirits)
			ystart = divide;
		else
			ystart = tall-1;
		if(canUseShot(shot&TYPE, shotCost(trick), technique))
		{
			shooting = true;
			if(trick&TS_STRAIGHT)
				addQueue(-1, 0, ystart, loc, shot, trick, technique, 0);
			if(trick&TS_SPLITR)
				addQueue(-1,  1, ystart, loc, shot, trick, technique, 0);
			if(trick&TS_SPLITL)
				addQueue(-1, -1, ystart, loc, shot, trick, technique, 0);
			progress();
		}
		else
		{
			var basiccost = -shotCost(deftrick);
			var total = gametype + ((gametype==4)?2:1);
			var somethingtouse = false;
			for(var bars=0; bars<total; bars++)
				if(powerbar[bars] >= basiccost)
					somethingtouse = true;

			if(somethingtouse)
				describe("Not enough power to use a " + (technique==shoot?typeString(shot).toUpperCase():"BIG") + " SHOT.");
			else
			{
				if(player.algn == PURGE)
				{
					if(confirm("Not enough power to use any Dream Shots. Use PURGE instead?"))
					{
						shooting = true;
						addQueue(-1, 0, ystart, loc, player.type, 0, purge, 0);
						progress();
					}
				}
				else
				{
					if(confirm("Not enough power to use any Dream Shots. Use HARVEST instead?"))
					{
						shooting = true;
						addQueue(-1, 0, ystart, loc, player.type, 0, harvest, 0);
						progress();
					}
				}
			}
		}
	}
}
function rotate(dir) // -1 = left-clockwise, 1 = right-counterclock
{
	if(gameplay == SURVIVAL && rotation != 0)
		describe("CANNOT TURN FIELD TWICE IN SURVIVAL");
	else if(!shooting && gameplay != GAMEOVER && gameplay != BATTLE && tall == wide && !(dir == -1 && (bar&NOLEFTTURN) || dir == 1 && (bar&NORIGHTTURN)))
	{
		var gridcopy, x, y, modx, mody;
		gridcopy = new Array(tall);
		modx=0; mody=0;
		if(dir ==  1) mody=tall-1; // _should_ be equal in size
		if(dir == -1) modx=wide-1;
		for(y=0; y<tall; y++)
		{
			gridcopy[y] = new Array(wide);
			for(x=0; x<wide; x++)
				gridcopy[y][x] = 0 | grid[y][x];
		}
		for(y=0; y<tall; y++)
		{
			for(x=0; x<wide; x++)
			{
				if(dir==-1) setCoord(tall-1-x,y,gridcopy[y][x]);
				if(dir== 1) setCoord(x,wide-1-y,gridcopy[y][x]);
			}
		}
		if(rotation != -1) rotation++;
		if(rotation > 1)
		{
			rotation = 0;
			wrapup();
		}
	}
	else
	{
		if(dir == -1) describe("LEFT TURNS DISABLED");
		else if(dir == 1) describe("RIGHT TURNS DISABLED");
	}
}
function unChange(to)
{
	var i, total = gametype + ((gametype==4)?2:1);
	for(i=0; i<total; i++)
		document["select" + i].src = BLANK_GRAPHIC;
	if(to != 2 && document.dreamon.trickshot)
	{
		document.dreamon.trickshot.selectedIndex = 0;
		document.selecttrick.src = BLANK_GRAPHIC;
	}
	if(to != 3 && document.dreamon.bigshot)
	{
		document.dreamon.bigshot.selectedIndex = 0;
		document.selectbig.src = BLANK_GRAPHIC;
	}
}
function shotChange(stype)
{
	unChange(1);
	shot = (defshot&PART) | stype;
	trick = deftrick;
	technique = deftech;
	document["select" + (stype-DREAM)].src = GRAPHIC_BASE + "shotselected.gif";
	describe("Change to " + typeString(stype).toUpperCase() + " SHOT");
}
function bigChange(dex)
{
	if(bar&NOBIGSHOTS)
		describe("BIG SHOTS DISABLED");
	else if(dex >= 0 && dex < bigshots.length)
	{
		var msg;
		unChange(3);
		document["selectbig"].src = GRAPHIC_BASE + "shotselected.gif";
		shot = (isEmpty(bigshots[dex].type)?PART&defshot:0)|bigshots[dex].type;
		trick = bigshots[dex].trick;
		technique = bigshots[dex].shot;
		describe("Change to " + bigshots[dex].name.toUpperCase() + "\n  " + bigshots[dex].desc);
	}
}
function trickChange(dex)
{
	if(bar&NOTRICKSHOTS)
		describe("TRICK SHOTS DISABLED");
	else if(dex >= 0 && dex < trickshots.length)
	{
		var msg;
		unChange(2);
		document["selecttrick"].src = GRAPHIC_BASE + "shotselected.gif";
		shot = trickshots[dex].shot;
		trick = trickshots[dex].trick;
		technique = shoot; // not deftech; always shoot
		msg = "Change to " + trickshots[dex].name.toUpperCase() + " TRICK SHOT\n";
		msg+= " " + typeString(shot).toUpperCase() + " Type\n";
		if(trick&TS_STRAIGHT)
			msg+= " Launches one shot straight forward\n";
		if((trick&TS_SPLITL) && (trick&TS_SPLITR))
			msg+= " Launches two shots to the left and right\n";
		else if(trick&TS_SPLITL)
			msg+= " Launches one shot to the left diagonally\n";
		else if(trick&TS_SPLITR)
			msg+= " Launches one shot to the right diagonally\n";
		if(trick&TS_POWERFUL)
			msg+= " Does not disappear after exploding once\n";
		if(trick&TS_BACKLASH)
			msg+= " Travels backwards after explosion\n";
		if(trick&TS_SKIP)
			msg+= " Skips the first clump of blocks encountered\n";
		if(trick&TS_CHAMELEON)
			msg+= " Type changes to that of passing spirits\n";
		if((trick&TS_BREAKR) && (trick&TS_BREAKL))
			msg+= " Breaks into two shots after first explosion\n";
		else if(trick&TS_BREAKL)
			msg+= " After first explosion, shot curves to the left\n";
		else if(trick&TS_BREAKR)
			msg+= " After first explosion, shot curves to the right\n";
		describe(msg);
	}
}
function decMaxPowerLevel()
{
	player.powr--;
	document.dreamon.maxpowerlevel.value = player.powr;
	decPowerLevel();
}
function decPowerLevel()
{
	player.curpowr--;
	document.dreamon.curpowerlevel.value = player.curpowr;
}
function modPowerBar(stype, amt)
{
	if(amt == 0) return true;
	var dex, type;
	type = stype&TYPE;

	if(((shot&PART)==(defshot&PART) && trick==deftrick && amt < 0 && !(type == OMNIT && type == NULLT) && (bar&NOCOSTSHOTS) || (bar & NOCOSTTRICKS)) && technique == shoot) // not for big shots
		return 1;
	if(type != OMNIT && type != NULLT)
	{
		dex = type - DREAM;
		if(powerbar[dex] + amt < 0)
			return false;
		powerbar[dex]+= amt;
		if(powerbar[dex] > 200) powerbar[dex] = 200;
		document['bar' + dex].width = powerbar[dex];
	}
	else
	{
		var t = Math.ceil(2 * amt / (gametype + ((gametype==4)?2:1)));
		if(powerbar[0] + amt < 0 || powerbar[1] + amt < 0) return false;
		if(gametype >= 2)
		{
			if(powerbar[2] + amt < 0) return false;
			if(gametype >= 3)
			{
				if(powerbar[3] + amt < 0) return false;
				if(gametype >= 4)
					if(powerbar[4] + amt < 0 || powerbar[5] + amt < 0) return false;
			}
		}

		modPowerBar(DREAM, t);
		modPowerBar(NMARE, t);
		if(gametype >= 2)
			modPowerBar(RESTL, t);
		if(gametype >= 3)
			modPowerBar(TRANC, t);
		if(gametype >= 4)
		{
			modPowerBar(SLUMB, t);
			modPowerBar(TEROR, t);
		}
	}
	return true;
}
function calcPowerArray(stype, amt, rolling)
{
	var t = (gametype + ((gametype==4)?2:1));
	var type = stype&TYPE;
	if(!rolling)
	{
		var rolling = new Array(t);
		for(var i=0; i<t; i++)
			rolling[i] = 0;
	}
	if(type != OMNIT && type != NULLT)
		rolling[type-DREAM]+= amt;
	else
	{
		var subamt = Math.ceil(2 * amt / t);
		for(var i=0; i<t; i++)
			rolling[i]+= subamt;
	}
	return rolling;
}
function checkPowerArray(rolling)
{
	var t = (gametype + ((gametype==4)?2:1));
	for(var i=0; i<t; i++)
		if(rolling[i] + powerbar[i] < 0)
			return false;
	return true;
}
function shotCost(sp)
{
	var cost = -10, mult = 0;
	if(sp&TS_POWERFUL)	cost-= 15;
	if(sp&TS_BREAKL)		cost-= 15;
	if(sp&TS_BREAKR)		cost-= 15;
	if(sp&TS_CHAMELEON)	cost-= 5;
	if(sp&TS_SKIP)		cost-= 5;
	if(sp&TS_BACKLASH)	cost-= 5;
	if(sp&TS_STRAIGHT)	{ mult++; }
	if(sp&TS_SPLITL)		{ mult++; cost--; }
	if(sp&TS_SPLITR)		{ mult++; cost--; }
	return cost*mult;
}
function typeRevert(stype)
{
	var type = TYPE&stype;

	if     (type == RESTL && gametype < 2) return (stype&PART) | NMARE;
	else if(type == TRANC && gametype < 3) return (stype&PART) | DREAM;
	else if(type == SLUMB && gametype < 4) return (stype&PART) | DREAM;
	else if(type == TEROR && gametype < 4) return (stype&PART) | NMARE;
	return stype;
}
function setImage(y, x, img)
{
	debuggin = 'map' + y + '' + x;
	document[debuggin].src = img;
}
function setCoord(y, x, val)
{
	grid[y][x] = val;
	setImage(y, x, getImage(val));
}
function setRand(y, x, frc)
{
	if(rndset == null)
	{
		var total = gametype + ((gametype==4)?2:1);
		var i = 0;
		switch(randomize(total))
		{
		case 0: i|= DREAM; break;
		case 1: i|= NMARE; break;
		case 2: i|= RESTL; break;
		case 3: i|= TRANC; break;
		case 4: i|= SLUMB; break;
		case 5: i|= TEROR; break;
		default:i|= OMNIT;
		}
		if(gameplay == CASCADE)
			i|= ENEMY;
		else
		{
			switch(randomize(2))
			{
			case 0: i|= ENEMY; break;
			default: i|= EMPTY;
			}
		}
		if(frc && isEmpty(i))
			i|= ENEMY;
		setCoord(y, x, i);
	}
	else
		setCoord(y,x,randomFrom(rndset));
	return !isEmpty(grid[y][x]);
}
function randomFrom(rnd)
{
	var i,j,r,dex,total;
	var maxrand = 0;
	for(i=1; i < rnd.length; i+=2)
		maxrand+= rnd[i];
	r = randomize(maxrand);
	for(dex=1,j=rnd[dex]; j<=r; dex+=2,j+=rnd[dex]);
	return typeRevert(rnd[dex-1]);
}
function inBounds(y, x)
{
	return (y >= 0 && y < tall && x >= 0 && x < wide);
}
function getImage(val)
{
	if(!isSpecial(val))
	{
		var ret = partString(val);
		if(ret != "blank") ret = typeString(val) + ret;
		return GRAPHIC_BASE + ret + ".gif";
	}
	else
		return spectile[getSpecial(val)].img;
}
function typeString(val)
{
	switch(val & TYPE)
	{
	case DREAM: return "dream";
	case NMARE: return "nightmare";
	case RESTL: return "restless";
	case TRANC: return "trance";
	case SLUMB: return "slumber";
	case TEROR: return "terror";
	case OMNIT: return "omni";
	case NULLT: return "null";
	}
	return "";
}
function partString(val)
{
	switch(val & PART)
	{
	case EMPTY: return "blank";
	case ENEMY: return "spirit";
	case BLOCK: return "block";
	case KBOOM:
	case SHELL: return "explode";
	case THICK: return "thick";
	case THINP: return "thin";
	case GHOST: return "ghost";
	case IMMUN: return "immune";
	case SPEC1: case SPEC2:
	case SPEC3: case SPEC4:
		return spectile[getSpecial(val)].name;
	default: return "";
	}
}
function alignString(val)
{
	switch(val)
	{
	case PURGE: return "purger";
	case HRVST: return "reaper";
	}
}
function categoryString(val)
{
	switch(val)
	{
	case CAT_NONE:  return "";
	case CAT_MAGIC: return "magical";
	case CAT_POWER: return "powerful";
	case CAT_ELEMT: return "elemental";
	case CAT_ANIML: return "animal";
	case CAT_TACTC: return "tactical";
	}
}
function isCleared(sy, sx, ly, lx)
{	// relates whether or not the field has been cleared; faster than (lineCount(0,tall-1)!=0)
	var x, y;
	if(!sx && !sy && !lx && !ly)
	{
		sx = 0;
		sy = 0;
		lx = wide-1;
		ly = tall-1;
	}
	for(y=sy; y<=ly; y++)
	{
		for(x=sx; x<=lx; x++)
		{
			if(!isEmpty(grid[y][x]))
				return false;
		}
	}
	return true;
}
function clearField()
{
	return clearRange(0, tall-1);
}
function clearRange(start, stop)
{
	for(y=start; y<=stop; y++)
	{
		for(x=0; x<wide; x++)
		{
			if(!isEmpty(grid[y][x]))
				setCoord(y,x,EMPTY);
		}
	}
	return true;
}
function isEmpty(val)
{
	return ((val&PART) == EMPTY); // DON'T UPDATE THIS; it will recurse, YFFI.
}
function isPart(part, tile)
{
	var val = (PART&tile);
	if(val == part) return true;
	if(isSpecial(val)) // special
	{
		var i, sd = getSpecial(val);
		for(i=0; i<spectile[sd].parts.length; i++)
			if(spectile[sd].parts[i] == part) return true;
	}
	return false;
}
function isSpecial(val)
{
	return ((val&SPEC1)==SPEC1);
}
function isSpecialName(name, val)
{
	return (isSpecial(val) && spectile[getSpecial(val)].name == name);
}
function getSpecial(val)
{
	switch(val&PART)
	{
	case SPEC1: return 0;
	case SPEC2: return 1;
	case SPEC3: return 2;
	case SPEC4: return 3;
	}
}
function reverseSpecial(val)
{
	switch(val)
	{
	case 0: return SPEC1;
	case 1: return SPEC2;
	case 2: return SPEC3;
	case 3: return SPEC4;
	}
}
function isOpposing(hit, tar)
{
	hit&= TYPE; tar&= TYPE;
	if(hit == NULLT || tar == NULLT) return false; // null before omni
	if(hit == OMNIT || tar == OMNIT) return true;
	switch(gametype)
	{
	case 1:
		if     (hit == DREAM && tar == NMARE) return true;
		else if(hit == NMARE && tar == DREAM) return true;
		break;
	case 2:
		if     (hit == DREAM && tar == RESTL) return true;
		else if(hit == RESTL && tar == NMARE) return true;
		else if(hit == NMARE && tar == DREAM) return true;
		break;
	case 3:
		if     (hit == DREAM && tar == TRANC) return true;
		else if(hit == TRANC && tar == RESTL) return true;
		else if(hit == RESTL && tar == NMARE) return true;
		else if(hit == NMARE && tar == DREAM) return true;
		break;
	}
	return false;
}
// utility functions
function fieldHelp(yval, xval)
{
	var p = (grid[yval][xval]&PART);
	if(p == EMPTY)
		describe("EMPTY SPACE");
	else
	{
		var t = (grid[yval][xval]&TYPE);
		var msg = '';
		if(!isSpecial(grid[yval][xval]))
			msg+= typeString(t).toUpperCase() + ' ';
		msg+= partString(p).toUpperCase() + ":\n";
		if(t == DREAM)		msg+=" -Pushed Against Dream\nDestroyed Against Nightmare\n";
		else if(t == NMARE)	msg+=" -Destroyed Against Dream\nPushed Against Nightmare\n";
		else if(t == OMNIT)	msg+=" -Destroyed Against All Types\nPushed Against Null Only\n";
		else if(t == NULLT)	msg+=" -Destroyed Against No Types\nPushed Against All Types\n";
		switch(p)
		{
		case ENEMY: case BLOCK: case SHELL:
			msg+=" -Removed from play after being in an explosion\n"; break;
		case SPEC1: case SPEC2: case SPEC3: case SPEC4:
			msg+=" -" + spectile[getSpecial(p)].desc + "\n"; break;
		}
		describe(msg);
	}
}
function shotHelp(fx)
{
	if(!p_acting || shooting)
		return;
	var ystart;
	if(gameplay == BATTLE && technique == summonSpirits)
	{
		ystart = tall-2;
		for(var plc=0,p=0,i=-8; i<=8; i++)
		{
			if((1<<plc) & CAT_WALL_PAT[player.catg])
			{
				if(inBounds(ystart, fx-i) && isEmpty(grid[ystart][fx-i]))
				{
					add = spiritwall[p];
					if(!isEmpty(add))
						setImage(ystart,fx-i,getImage(add));
				}
				p++;
			}
			plc++;
		}
	}
	else
	{
		if(gameplay == BATTLE)
			ystart = divide;
		else
			ystart = tall;
		if(inBounds(ystart, fx) && isEmpty(grid[ystart][fx]))
			setImage(ystart, fx, getImage(shot));
	}
}
function unshotHelp(fx)
{
	var ystart;
	if(gameplay == BATTLE && technique == summonSpirits)
	{
		ystart = tall-2;
		for(var i=0; i<wide; i++)
			setImage(ystart, i, getImage(grid[ystart][i]));
	}
	else
	{
		if(gameplay == BATTLE)
			ystart = divide;
		else
			ystart = tall;
		if(inBounds(ystart, fx))
			setImage(ystart, fx, getImage(grid[ystart][fx]));
	}
}
function challenge()
{
	if(gameplay == GAMEOVER)
		hallOfFame();
}
function hallOfFame()
{
	if(p_acct == "Unregistered User")
	{
		document.rankit.action = "http://www.realitysend.com/cgi-bin/dreamhighscore.pl";
		p_acct = prompt("Enter a name for a High Score Entry:", "");
		if(p_acct.length > 20)
			p_acct = p_acct.substring(0, 20);
		document.rankit.entry.value = p_acct;
	}
	document.rankit.score.value = turns;
	document.rankit.submit();
}
function gainTotem(ttm)
{
	document.gainit.totems.value+= ttm + " ";
}
function endTotemHunt(ttm)
{
	if(gameplay == GAMEOVER && p_acct != "Unregistered User")
	{
		if(ttm) gainTotem(ttm);
		document.gainit.submit();
	}
}
function printForm(pzzl)
{
	document.write('<form name="rankit" action="http://www.realitysend.com/cgi-bin/endres/highscore.pl" method="post">\n');
	document.write('<input name="entry" type="hidden" value="">\n');
	document.write('<input name="score" type="hidden" value="">\n');
	document.write('<input name="puzzle" type="hidden" value="' + pzzl + '">\n');
	document.write('</form>\n');
}
function printGain(pzzl)
{
	document.write('<form name="gainit" action="http://www.realitysend.com/cgi-bin/endres/gain.pl" method="post">\n');
	document.write('<input name="totems" type="hidden" value="">\n');
	document.write('<input name="power" type="hidden" value="">\n');
	document.write('<input name="puzzle" type="hidden" value="' + pzzl + '">\n');
	document.write('</form>\n');
}
function puzzleBegin(pzzl, dscrpt)
{
	printForm(pzzl);
	endProc = challenge;
	gameplay = PUZZLE;
	turns = 0;
	printField();
	startGame(dscrpt);
}
function surviveBegin(pzzl, dscrpt)
{
	printForm(pzzl);
	endProc = challenge;
	gameplay = SURVIVAL;
	turns = 0;
	printField();
	startGame(dscrpt);
}
function totemHuntBegin(pzzl, dscrpt)
{
	printGain(pzzl);
	endProc = endTotemHunt;
	gameplay = SURVIVAL;
	turns = 0;
	printField();
	document.gainit.power.value = player.powr;
	document.gainit.totems.value = '';
	startGame(dscrpt);
}
function beginBattle(dscrpt)
{
	gameplay = BATTLE;
	turns = 0;
	endProc = stdBattleAI;

	if(spiritwall)
		bigshots = [getBigShot("SpiritWall")];

	printField();

	startGame(dscrpt);
}
function beginBigBattle(pzzl, dscrpt)
{
	printGain(pzzl);
	gameplay = BATTLE;
	turns = 0;
	endProc = stdBigBattleAI;

	if(spiritwall)
		bigshots = [getBigShot("SpiritWall")];
	else
		bigshots = [];
	bigshots.length++;
	bigshots[bigshots.length-1] = getBigShot("BakuJar");
	printField();

	document.gainit.totems.value = '';
	startGame(dscrpt);
}
function protectDestroy(dy,dx, fy,fx, stype,special)
{
	cascadeLimited(1, tall-2, 0);
	altDivide(1);
	clearRange(divide, divide);
}

function stdBattleAI()
{
	if(gameplay != GAMEOVER && !p_acting) // foe-go
	{
		var flin = lineCount(divide-1, divide-1);
		var allcnt=lineCount(0,divide-1);
		if(flin != 0 && lineCount(divide+1,divide+1) == 0)
		{
			altDivide(1);
			cascadeLimited(1, divide-1, 0);
			flopLine(0);
		}
		else if((flin < wide/2 && randomize(2) || flin < wide/4) && !(flin != 0 && (allcnt > divide*wide*0.7 /*|| allcnt > opponent.spiritMax()*/)))
		{
			cascadeLimited(1, divide-1, 0);
			flopLine(0);
		}
		else
		{
			var i, pr, strt, fin;
			strt = randomize(wide);
			for(pr=0; pr<3; pr++)
			{
				for(fin=strt; fin!=strt+wide; fin++)
				{
					if(fin >= wide)	i = fin-wide;
					else			i = fin;
					// simplistic check-for-wall-destroy now; advance later
					if(inBounds(divide-1, i) && !isEmpty(grid[divide-1][i]) && (pr==0 && !isEmpty(grid[tall-1][i]) || pr==1 && (!isEmpty(grid[divide+1][i]) || !isEmpty(grid[tall-2][i])) || pr==2))
					{
						shooting = true;
						addQueue(1,0, divide-1, i, BLOCK|(TYPE&grid[divide-1][i]), TS_STRAIGHT, shoot, 0);
						pr = 3; // kill outer loop
						break;
					}
				}
			}
			if(!shooting)
				cascadeLimited(1, divide-1, 0);
			/*
			
			var sx = randomize(wide);

			var tallest = new Array(wide);
			var heighth = new Array(wide);
			var unblocked = new Array(wide);
			var x, y, tmpt, tmph, picked = false;
			for(x=0; x<wide; x++)
			{
				unblocked[x] = true;
				heighth[x] = 0;
				tallest[x] = x;
				for(y=0; y<divide && unblocked[x]; y++)
					if(!isEmpty(grid[y][x])) unblocked[x] = false;
				for(y=divide; y<tall && heighth[x] == 0; y++)
					if(!isEmpty(grid[y][x])) heighth[x] = tall - y;
			}
			for(y=0; y<wide-2; y++)
			{
				for(x=0; x<wide-y-1; x++)
				{
					if(heighth[x] > heighth[x+1] || heighth[x] == heighth[x+1] && (x&1))
					{
						tmpt = tallest[x+1];
						tmph = heighth[x+1];
						tallest[x+1] = tallest[x];
						heighth[x+1] = heighth[x];
						tallest[x] = tmpt;
						heighth[x] = tmph;
					}
				}
			}
			for(x=wide-1; x>=0 && !picked && heighth[x] > 0 && heighth[x] > heighth[wide-1]-tall/2; x--)
			{
				if(unblocked[tallest[x]])
				{
					picked = true;
					sx = tallest[x];
				}
			}
			if(!picked)
				sx = tallest[wide-1];
			addQueue(1, 0, -1, sx, BLOCK|(randomize(2) == 0 ? NMARE:DREAM), TS_STRAIGHT, shoot, 0);
			*/
		}
		progress();		
	}
	else if(gameplay == GAMEOVER)
		altDivide(0); // erase
}
function stdBigBattleAI()
{
	if(gameplay != GAMEOVER)
		stdBattleAI();
	else if(divide == 1) // player won
	{
		var df, mid = Math.floor(wide/2);
		describe("Use your Baku Jar on the Big Shot Totem when it appears!");
		clearRange(1,tall-1); // almost entire field, but not first line
		for(df=0; df<tall/2; df++)
			dropField(1, tall-2, 0);
		gameplay = SURVIVAL;
		altDivide(0); // erase

		endProc = endTotemHunt;
		drainTotem = function(spc) { gainTotem(spectile[spc].name); }
		p_acting = true; // make sure player can act
		shooting = false;
		spectile[spectile.length-1] = new SpecialTile("BigShot", GRAPHIC_BASE + "crystal.gif", [IMMUN], "Capture with Baku Jar in order to gain a new Big Shot!", function() { gameOver(true); } );
		setCoord(0, mid, OMNIT|reverseSpecial(spectile.length-1));
		for(df=1; df<tall/2; df++)
			if(isEmpty(grid[df][mid])){
				setRand(df, mid, true); alert("bla"); }
	}
	else
		endTotemHunt();
}

function randomize(s)
{
	return Math.floor(Math.random() * s);
}
function describe(txt)
{
	document.dreamon.messagebox.value = txt;
}
// cookie functions
function setCookieEND(name, value)
{
	var expiration = new Date();
	expiration.setYear(expiration.getYear()+1);
	document.cookie+= name + "=" + value + "; expires=" + expiration.toString() + "; domain=realitysend.com; path=/end;"; 
}
// creation functions
function startGame(msg)
{
	var x,y,i,total;
	total = gametype + ((gametype==4)?2:1);
	p_acting = (gameplay != BATTLE);
	if(preset != null)
	{
		i=0;
		for(y=0; y<tall; y++)
		{
			for(x=0; x<wide; x++)
			{
				setCoord(y, x, typeRevert(preset[i]));
				i++;
			}
		}
	}
	else
	{
		for(y=0; y<tall; y++)
		{
			for(x=0; x<wide; x++)
			{
				if(!((gameplay == SURVIVAL) && y > 0 || gameplay == BATTLE))
					setRand(y,x);
				else	setCoord(y,x, EMPTY);
			}
		}
	}

	if(gameplay == BATTLE)
	{
		divide = Math.floor(tall-3);
		if(spectile == null)
			spectile = [getStockBlock("Protection")];
		else
		{
			spectile.length++;
			spectile[spectile.length-1] = getStockBlock("Protection");
		}
		var specpart = reverseSpecial(spectile.length-1); // always last element
		for(var x=0; x<wide; x++)
			setCoord(tall-1, x, player.type|specpart);
		altDivide(0); // sets image
	}
		
	shot = defshot; trick = deftrick; technique = deftech;
	shotChange(DREAM);
	for(i=0; i<total; i++)
	{
		powerbar[i] = Math.floor(100/total); // default, unloaded values
		document['bar' + i].width = powerbar[i];
		document["select" + i].src = BLANK_GRAPHIC;
	}
	i = (shot&TYPE) - DREAM;
	document["select" + i].src = GRAPHIC_BASE + "shotselected.gif";
	preload[0] = new Image();
	preload[0].src = GRAPHIC_BASE + "dreamblock.gif";
	preload[1] = new Image();
	preload[1].src = GRAPHIC_BASE + "nightmareblock.gif";
	preload[2] = new Image();
	preload[2].src = GRAPHIC_BASE + "nullblock.gif";
	preload[3] = new Image();
	preload[3].src = GRAPHIC_BASE + "omniblock.gif";
	preload[4] = new Image();
	preload[4].src = GRAPHIC_BASE + "dreamexplode.gif";
	preload[5] = new Image();
	preload[5].src = GRAPHIC_BASE + "nightmareexplode.gif";
	preload[6] = new Image();
	preload[6].src = GRAPHIC_BASE + "nullexplode.gif";
	preload[7] = new Image();
	preload[7].src = GRAPHIC_BASE + "omniexplode.gif";
	preload[8] = new Image();
	if(player.algn == PURGE) preload[8].src = GRAPHIC_BASE + "purgeblock.gif";
	else if(player.algn == HRVST) preload[8].src = GRAPHIC_BASE + "harvestblock.gif";
	document.dreamon.turncounter.value = turns;
	document.dreamon.curpowerlevel.value = player.curpowr;
	document.dreamon.maxpowerlevel.value = player.powr;
	//if(gameplay == BATTLE)
	//{
	//	document.dreamon.phitcounter.value = player.hits;
	//	document.dreamon.ehitcounter.value = opponent.hits;
	//}
	if(msg) describe(msg);

	if(gameplay == BATTLE) endProc(); // use spirit wall
}
function printField()
{
	var x,y;
	if(tall>20||tall<1) tall = 10;
	if(wide>20||wide<1) wide = 10;
	while(tall*wide > 100)
	{
		if(tall>wide) tall--;
		else wide--;
	}
	document.write('<form name="dreamon"><div align="center" style="background-image: URL(' + player.avtr + '); background-position: bottom right; background-repeat: no-repeat; background-color: transparent;"><table border=1  style="background: none;"><tr style="background: none;"><td align="center" style="text-align: left; padding: 20px 0px 0px 0px; margin: 0px; background-image: URL(' + GRAPHIC_BASE + 'tile.gif); background-color: transparent; background-position: 25px 20px; width: ' + (50*(wide+1)) + 'px;" colspan=2><nobr>\n');
	//document.write('<img src="' + BLANK_GRAPHIC + '" vspace=0 hspace=0 style="width: ' + ((wide+1)*50) + 'px; height: 20px;"><br>\n');
	for(y=0; y<tall; y++)
	{
		grid[y] = new Array(wide);
		document.write('<img src="' + BLANK_GRAPHIC + '" name="borderl' + y + '" vspace=0 hspace=0 width=25 height=30>');
		for(x=0; x<wide; x++)
		{
			document.write('<img name="map' + y + '' + x + '" src="' + getImage(EMPTY) + '" style="padding: 0px; border: 0px; width: 50px; height: 50px; margin: -20px 0px 0px 0px;" onClick="fieldHelp(' + y + ',' + x + ');">');
		}
		document.write('<img src="' + BLANK_GRAPHIC + '" name="borderr' + y + '" vspace=0 hspace=0 width=25 height=30><br>\n');
	}
	document.write('<img src="' + GRAPHIC_BASE + 'larrow.gif" onclick="rotate(-1);" vspace=0 hspace=0 width=25 height=25>');
	for(x=0; x<wide; x++)
		document.write('<img src="' + GRAPHIC_BASE + 'shootbottom.gif" onclick="dreamShot(' + x + ');" onmouseover="shotHelp(' + x + ');" onmouseout="unshotHelp(' + x + ');" vspace=0 hspace=0 width=50 height=25>');
document.write('<img src="' + GRAPHIC_BASE + 'rarrow.gif" onclick="rotate(1)" vspace=0 hspace=0 width=25 height=25><br>\n');
	document.write('</nobr></table><table align="center" style="background: none;"><tr style="background: none;"><td align="left" width=250 valign="center" style="background: none;">');
	document.write('<img src="' + BLANK_GRAPHIC + '" name="select0" style="height: 25px; width: 25px; margin: 0px; padding: 0px;"><img src="' + GRAPHIC_BASE + 'dreamchange.gif" name="dreamchange" width=25 height=25 vspace=0 hspace=0 onclick="shotChange(DREAM)"><img src="' + BLANK_GRAPHIC + '" width=0 name="bar0" style="height: 25px; margin: 0px; padding: 0px; background-image: URL(' + GRAPHIC_BASE + 'bar.gif);"><br>\n');
	document.write('<img src="' + BLANK_GRAPHIC + '" name="select1" style="height: 25px; width: 25px; margin: 0px; padding: 0px;"><img src="' + GRAPHIC_BASE + 'nightmarechange.gif" name="nightmarechange" width=25 height=25 vspace=0 hspace=0 onclick="shotChange(NMARE)"><img src="' + BLANK_GRAPHIC + '" name="bar1" width=0 style="height: 25px; margin: 0px; padding: 0px; background-image: URL(' + GRAPHIC_BASE + 'bar.gif);"><br>\n');
	if(gametype >= 2)
	{
		document.write('<img src="' + BLANK_GRAPHIC + '" name="select2" style="height: 25px; width: 25px; margin: 0px; padding: 0px;"><img src="' + GRAPHIC_BASE + 'restlesschange.gif" name="restlesschange" width=25 height=25 vspace=0 hspace=0 onclick="shotChange(RESTL)"><img src="' + BLANK_GRAPHIC + '" name="bar2" width=0 style="height: 25px; margin: 0px; padding: 0px; background-image: URL(' + GRAPHIC_BASE + 'bar.gif);"><br>\n');
	}
	if(gametype >= 3)
	{
		document.write('<img src="' + BLANK_GRAPHIC + '" name="select3" style="height: 25px; width: 25px; margin: 0px; padding: 0px;"><img src="' + GRAPHIC_BASE + 'trancechange.gif" name="trancechange" width=25 height=25 vspace=0 hspace=0 onclick="shotChange(TRANC)"><img src="' + BLANK_GRAPHIC + '" name="bar3" width=0 style="height: 25px; margin: 0px; padding: 0px; background-image: URL(' + GRAPHIC_BASE + 'bar.gif);"><br>\n');
	}
	if(trickshots != null && !(bar&NOTRICKSHOTS))
	{
		document.write('<img src="' + BLANK_GRAPHIC + '" name="selecttrick" style="height: 25px; width: 25px; margin: 0px; padding: 0px;"><select name="trickshot" onChange="trickChange(this.value)"><option value="-1" selected>- Trick Shots</option>\n');
		y = 0;
		for(x=0; x<trickshots.length; x++)
		{
			document.write('<option value="' + y + '">' + trickshots[x].name + '</option>\n');
			y++;
		}
		document.write('</select><br><br>\n');
	}
	if(bigshots != null && !(bar&NOBIGSHOTS))
	{
		document.write('<img src="' + BLANK_GRAPHIC + '" name="selectbig" style="height: 25px; width: 25px; margin: 0px; padding: 0px;"><select name="bigshot" onChange="bigChange(this.value)"><option value="-1" selected>- Big Shots & Specials</option>\n');
		y = 0;
		for(x=0; x<bigshots.length; x++)
		{
			document.write('<option value="' + y + '">' + bigshots[x].name + '</option>\n');
			y++;
		}
		document.write('</select><br><br>\n');
	}
	if(player.name != "")
		document.write('<b>' + player.name+ ' Dreamer</b>');
	else
		document.write(p_acct);
	document.write('<br>\n- Type: ' + typeString(player.type).toUpperCase() + '<br>\n- Align: ' + alignString(player.algn).toUpperCase());
	if(player.catg != CAT_NONE)
		document.write('<br>\n- Catgry: ' + categoryString(player.catg).toUpperCase());
	document.write('<br>\n- Power Level: <input type="text" name="curpowerlevel" readonly size=2 style="text-align: center; background: none; border: none; color: #0000FF; height: 1.25em;">/<input type="text" name="maxpowerlevel" readonly size=2 style="text-align: center; background: none; border: none; color: #0000FF; height: 1.25em;">');
	document.write('\n<td align="left" valign="top" style="background: transparent;">Turns: <input type="text" size=3 name="turncounter" value="' + turns + '" readonly>');
	//if(gameplay == BATTLE)
	//{
	//	document.write(' - Hits: <input type="text" size=2 name="phitcounter" value="' + player.hits + '" readonly> - Enemy: <input type="text" size=2 name="ehitcounter" value="' + opponent.hits + '" readonly>');
	//}
	document.write('<br>\n<textarea style="width: 250px; font-family: serif; padding: 0px; margin: 0px;" rows=7 name="messagebox" readonly onFocus="blur()";></textarea>');
	document.write('</table></div></form>\n');
}
// constants
//  types
var TYPE  = 0x07;
var NULLT = 0x00;
var DREAM = 0x01;
var NMARE = 0x02;
var RESTL = 0x03;
var TRANC = 0x04;
var SLUMB = 0x05; // largely unused
var TEROR = 0x06; // skips five; second (1) bit has nightmare association
var OMNIT = 0x07;
//  parts
var PART  = 0xFF8;
var EMPTY = 0x000;
var ENEMY = 0x008;
var BLOCK = 0x010;
var KBOOM = 0x018;
var THICK = 0x020;
var THINP = 0x028;
var GHOST = 0x030;
var SHELL = 0x038; // fake part applied to hit thicks
var IMMUN = 0x040;
var SPEC1 = 0xF00;
var SPEC2 = 0xF08;
var SPEC3 = 0xF10;
var SPEC4 = 0xF18;
//  trick shots
var TS_STRAIGHT	= 0x001;
var TS_SPLITL	= 0x002;
var TS_SPLITR	= 0x004;
var TS_POWERFUL	= 0x008;
var TS_BREAKL	= 0x010;
var TS_BREAKR	= 0x020;
var TS_CHAMELEON	= 0x040;
var TS_SKIP		= 0x080;
var TS_SKIPPING	= 0x100; // fake trick applied for skipping
var TS_BACKLASH	= 0x200;
var TS_SPLIT = TS_SPLITL|TS_SPLITR;
var TS_BREAK = TS_BREAKL|TS_BREAKR;
var TS_DIRECTIONAL = TS_BREAK | TS_BACKLASH | TS_POWERFUL;
//  alignment
var ALGN = 7;
var PURGE = 1;
var HRVST = 2;
var FMOON = 4;
//  class
var CLS_DREAMER	= 0;
var CLS_BIGDREAMER= 1;
//  category
var CAT_NONE  = 0;
var CAT_ELEMT = 1;
var CAT_TACTC = 2;
var CAT_POWER = 3;
var CAT_MAGIC = 4;
var CAT_ANIML = 5;
var CAT_WALL_PAT = [0x15555, 0x0739C, 0x15555, 0x01FF0, 0x1E10F, 0x09BB4];
//  game types
var GAMEOVER = 0;
var PUZZLE = 1;
var SURVIVAL = 2;
var COUNTDOWN = 3;
var CASCADE = 4;
var BATTLE = 5;
// restrictions
var NOLEFTTURN	= 0x0001;
var NORIGHTTURN	= 0x0002;
var NOCOSTSHOTS	= 0x0004;
var NOTRICKSHOTS	= 0x0008;
var NOBIGSHOTS	= 0x0010;
var NOCLASSCHANGE	= 0x0020;
var NOCOSTTRICKS	= 0x0040;
var TRICKSHOTS_ONLY = NOBIGSHOTS | NOCLASSCHANGE;
//  const
var GRAPHIC_BASE	= "http://www.realitysend.com/dreamon/";
var BLANK_GRAPHIC	= GRAPHIC_BASE + "blank.gif";
// globals
var shot, trick, technique;
var divide; // battle mode
var preload = new Array(16); // types x 2
var powerbar = new Array(6);
var shooting = false;
var queue = new Array();
var messages = 0;
var grid = new Array(tall);
// modifiable
var preset = null; // array of one-hundred size for pre-determined play
var rndset = null; // array of randomization combinations in type/number combos
var spiritwall = null; // player custom randset
//var foewall = null;
//var enemyPower = 0;
var trickshots = null; // list of all special, trick shots
var bigshots = null; // list of all heavily coded shots
var totems = null; // list of all totems
var spectile = null;
var defshot = DREAM|BLOCK;
var deftrick = TS_STRAIGHT;
var deftech = shoot;
var gameplay = PUZZLE;
var gametype = 1; // 1 = two spirits, 2 = three (RESTL), 3 = four (TRANC), 4 = six (TEROR&SLUMB)
var rotation = 0;
var turns = 0;
var tall = 10;
var wide = 10;
var bar = 0; // restrictions
var endProc = null; // function that can be customly processed at the end of a round
// default player
var p_acct = "Unregistered User";
var p_acting = true;
var player = new Dreamer("", CLS_DREAMER, HRVST, NULLT, "", 0, 0);
var opponent = null;
