// DefaultMover.tjs - レイヤ自動移動のデフォルトのハンドラ
// Copyright (C)2001-2003, W.Dee  改変・配布は自由です

class LinearMover
{
	// 直線補間

	var layer; // レイヤ
	var path; // 点配列
	var time; // 点を通過するのに必要な時間
	var accel; // 加速度的な動きをするかどうか
	var pointCount; // path.count -1
	var finalFunction; // 移動終了時に呼ぶ関数
	var startTick; // 移動開始をしたティック
	var totalTime; // 全体の時間
	var delayTimer; // 動作開始時まで待つためのタイマ
	var moving = false; // 移動中か
	var first = true; // 初回のハンドラ呼び出しか
	var nextStop = false; // 次のハンドラ呼び出しで停止するか

	function LinearMover(layer, path, time, accel, finalfunction)
	{
		this.layer = layer;
		this.path = path;
		this.time = time;
		this.accel = accel;
		this.finalFunction = finalfunction;
		totalTime = (pointCount = (path.count \ 3 - 1)) * time;
	}

	function finalize()
	{
		stopMove();
		if(delayTimer !== void) invalidate delayTimer;
	}

	function startMove(delay)
	{
		// 移動を開始する
		if(delay != 0)
		{
			delayTimer = new Timer(onDelayTimer, '');
			delayTimer.interval = delay;
			delayTimer.enabled = true;
			moving = true;
		}
		else
		{
			System.addContinuousHandler(handler); // ContinuousHandlers に追加
			moving = true;
		}
	}

	function onDelayTimer()
	{
		// delayTimer が発動した
		delayTimer.enabled = false;
		System.addContinuousHandler(handler);
	}

	function stopMove()
	{
		// 移動を終わる
		if(moving)
		{
			// 最終位置に移動
			if(layer isvalid)
			{
				var idx = int(pointCount*3);
				var p = path;
				layer.setPos(p[idx], p[idx+1]);
				layer.opacity = p[idx+2];
			}
			System.removeContinuousHandler(handler);
			moving = false;
			finalFunction();
		}
	}

	function handler(tick)
	{
		// 移動中に呼ばれる
		if(nextStop || !(layer isvalid)) { stopMove();  return; }
		if(first) startTick = tick, first = false;
		tick -= startTick;
		if(tick >= totalTime)
		{
			nextStop = true;
			var idx = int(pointCount*3);
			var p = path;
			layer.setPos(p[idx], p[idx+1]);
			layer.opacity = p[idx+2];
			return;
		}

		if(accel < 0)
		{
			// 上弦 ( 最初が動きが早く、徐々に遅くなる )
			tick = 1.0 - tick / totalTime;
			tick = Math.pow(tick, -accel);
			tick = int ( (1.0 - tick) * totalTime );
		}
		else if(accel > 0)
		{
			// 下弦 ( 最初は動きが遅く、徐々に早くなる )
			tick = tick / totalTime;
			tick = Math.pow(tick, accel);
			tick = int ( tick * totalTime );
		}

		move(tick);
	}

	function move(tick)
	{
		var index = tick \ time * 3;
		var ratio = tick % time / time;
		var p = path;
		var sx = p[index];
		var sy = p[index+1];
		var so = p[index+2];
		var ex = p[index+3];
		var ey = p[index+4];
		var eo = p[index+5];

		var l = int((ex-sx)*ratio + sx);
		var t = int((ey-sy)*ratio + sy);
		var o = eo >= 256 ? so : int((eo-so)*ratio + so);

		layer.setPos(l, t);
		layer.opacity = o;
	}
}


class SplineMover extends LinearMover
{
	// スプライン補間
	var zx = []; // スプラインワーク
	var zy = []; // スプラインワーク

	function SplineMover(layer, path, time, accel, finalfunction)
	{
		super.LinearMover(...);

		if(path.count < 9)
		{
			// 3 点以下 ( 指定するのはそのうちの2点 ) は補完できない
			throw new Exception("2 点以上を指定してください");
		}

		// スプライン補間に必要なワークを計算
		var points = path.count \ 3;
		var tmpx = [], tmpy = [];
		var tx = zx, ty = zy;
		tx[0] = 0;
		ty[0] = 0;
		tx[points-1] = 0;
		ty[points-1] = 0;

		for(var i = points-2, pi = path.count-6; i >= 0; i--, pi -= 3)
		{
			tmpx[i+1] = (path[pi + 3] - path[pi  ]);
			tmpy[i+1] = (path[pi + 4] - path[pi+1]);
		}

		tx[1] = tmpx[2] - tmpx[1] - tx[0];
		ty[1] = tmpy[2] - tmpy[1] - ty[0];

		tmpx[1] = 4;
		tmpy[1] = 4;

		var lim = points - 2;
		for(var i = 1, pi = 3; i < lim; i++, pi += 3)
		{
			var x = 1 / tmpx[i];
			var y = 1 / tmpy[i];
			tx[i+1] = tmpx[i+2] - tmpx[i+1] - path[pi  ] * x;
			ty[i+1] = tmpy[i+2] - tmpy[i+1] - path[pi+1] * y;
			tmpx[i+1] = 4 - x;
			tmpy[i+1] = 4 - y;
		}

		tx[points-2] -= tx[points-1];
		ty[points-2] -= ty[points-1];

		for(var i = points-2, pi = path.count-6; i>0; i--, pi -= 3)
		{
			tx[i] = (tx[i] - tx[i+1]) / tmpx[i];
			ty[i] = (ty[i] - ty[i+1]) / tmpx[i];
		}
	}

	function move(tick)
	{
		var index;
		var pindex = (index = tick \ time) * 3;
		var d = tick % time / time;

		var p = path;

		var l = (((zx[index+1] - zx[index])*d +
				zx[index]*3)*d +
				((p[pindex + 3] - p[pindex]) -
				(zx[index]*2 + zx[index+1]))) * d +
				p[pindex];

		var t = (((zy[index+1] - zy[index])*d +
				zy[index]*3)*d +
				((p[pindex + 4] - p[pindex+1]) -
				(zy[index]*2 + zy[index+1]))) * d +
				p[pindex+1];

		var so = p[pindex+2];
		var eo = p[pindex+5];

		var o = eo >= 256 ? so : int((eo-so)*d + so);

		layer.setPos(l, t);
		layer.opacity = o;
	}

}


