//
//	clipvsl.cs
//
//	ビデオスロットゲーム用サブルーチン集
//
//	CLiP - Common Library for P/ECE
//	Copyright (C) 2017 Naoyuki Sawa
//
//	* Wed Mar 01 22:47:42 JST 2017 Naoyuki Sawa
//	- 1st リリース。
//	* Fri Mar 03 22:34:24 JST 2017 Naoyuki Sawa
//	- clipptr_net.csモジュールを追加した事に伴い、ポインタの扱いを大幅に変更しました。
//	  これまでは、配列とインデクスを保持する必要が有りましたが、今後はC言語版と同様に、配列ポインタ一つを保持すれば良くなりました。
//	- 今回は変更量がかなり多かったので、変更前のコードはコメントアウトして残していません。
//	  変更前のコードを参照する場合は、前回のアーカイブを参照して下さい。
//	* Thu Mar 16 22:31:07 JST 2017 Naoyuki Sawa
//	- clipvsl.cの「dVslDefC.exe対応ユーティリティ」を変更した事に伴い、当モジュールも追従しました。
//	  詳細は、clipvsl.cの同日のコメントを参照して下さい。
//	  当モジュールも、やや大幅な変更を行ったので、変更前のコードはコメントアウトして残していません。
//	  変更前のコードを参照する必要が有る場合は、前回のアーカイブを参照して下さい。
//	* Fri Mar 17 23:53:28 JST 2017 Naoyuki Sawa
//	- clipvsl.cにVslDef_GetRstSym_w()を追加した事に伴い、clipvsl.csにもVslDef_GetRstSym_w()を追加しました。
//	  詳細は、clipvsl.cの同日のコメントを参照して下さい。
//	* Wed Apr 12 22:29:29 JST 2017 Naoyuki Sawa
//	- VslDef_GetSymName(),VslDef_GetSymDesc(),VslDef_GetGmdName()を追加しました。
//
using System;
namespace org.piece_me {
	public static partial class libclip {
		//*****************************************************************************
		//	定数
		//*****************************************************************************
		public const int VslAtt_Wild		= (1<<0);	//ワイルドシンボル
		public const int VslAtt_Scatter		= (1<<1);	//スキャッターシンボル
		//*****************************************************************************
		//	共通処理
		//*****************************************************************************
		//入賞判定の共通処理を行う。
		public static int VslCmn_GetWin(int[/*iRel*/,/*iRow*/] TBL_Sta,
		    int nRow,
		    Func<int/*iSym*/,int/*nOak*/,object/*arg*/,int> fnScaPay,
		    Func<int/*iSym*/,object/*arg*/,int> fnGetAtt,
		    Func<int/*iRel*/,int/*iRow*/,object/*arg*/,int> fnGetSta,
		    Action<int/*iSym*/,int/*nOak*/,int/*nPay*/,object/*arg*/> fnScaWin,
		    object arg) {
			int nPaySum = 0;							//スキャッターシンボルの賞金総額。
			int[,] TBL_Sca = new int[(5*nRow)/*nSca*/,2/*[0]=iSym,[1]=nOak*/];	//スキャッターシンボルの出現数。スキャッターワイルドシンボルは含めない。	理論上最大で(5*nRow)種類のスキャッターシンボルが同時に出現しても対応可能である。(まず有り得ないが)
			int nSca = 0;								//スキャッターシンボルの種類数。スキャッターワイルドシンボルは含めない。
			int nScwCnt =  0;							//スキャッターワイルドシンボル出現数。複数種類のスキャッターワイルドシンボルが出現した場合は合算する。
			int iScwSym = -1;							//スキャッターワイルドシンボルの種類。(nScwCnt>=1)の場合のみ有効。複数種類のスキャッターワイルドシンボルが出現した場合はどれを格納するかは不定です。	スキャッターワイルドシンボルだけが出現した場合は、どのスキャッターワイルドシンボルで払い出しても同じ配当となるようにペイテーブルを設計せよ。	←←←←要注意!!
			if(nRow < 1) { throw new ApplicationException(); }
			//--- 停止形を取得する ---
			{
				int iRel;
				for(iRel = 0; iRel < 5; iRel++) {
					int iRow;
					for(iRow = 0; iRow < nRow; iRow++) {
						int iSym = fnGetSta.Invoke(iRel, iRow, arg);	//シンボルを取得する。
						int iAtt = fnGetAtt.Invoke(iSym, arg);		//シンボル属性を取得する。
						TBL_Sta[iRel,iRow] = iSym;			//停止形を格納する。
					    //{{停止形を取得しながら、スキャッターウィンの処理も行う。
						//このシンボルがスキャッター属性を持つシンボルならば…
						if((iAtt & VslAtt_Scatter) != 0) {
							//このシンボルがワイルド属性を持つシンボルならば…
							if((iAtt & VslAtt_Wild) != 0) {
								//スキャッターワイルドシンボル出現数を増やす。
								nScwCnt++;
								//スキャッターワイルドシンボルの種類を格納する。
								iScwSym = iSym;	//既に格納されていた場合も上書きする。(必須ではない。既に格納されている方を優先しても構わない。)
							//このシンボルがワイルド属性を持たないシンボルならば…
							} else {
								//この種類のスキャッターシンボルが既に出現しているか?
								int iSca;
								for(iSca = 0; iSca < nSca; iSca++) {
									if(TBL_Sca[iSca,0/*iSym*/] == iSym) { break; }
								}
								//この種類のスキャッターシンボルが初出ならば、種類を登録する。
								if(iSca == nSca) {
									if(++nSca > TBL_Sca.GetLength(0)) { throw new ApplicationException(); }	//当関数のバグ
									TBL_Sca[iSca,0/*iSym*/] = iSym;
									TBL_Sca[iSca,1/*nOak*/] = 0;	//すぐこの下で1になる。
								}
								//この種類のスキャッターシンボルの出現数を増やす。
								TBL_Sca[iSca,1/*nOak*/]++;
							}
						}
					    //}}停止形を取得しながら、スキャッターウィンの処理も行う。
					}
				}
			}
			//--- スキャッターウィン ---
			{
				//ワイルド属性を持たないスキャッターシンボルが一種類以上出現していたら、スキャッターワイルドシンボルはそれに含めて通知する。
				//スキャッターワイルドシンボル単体での通知は行わない。
				if(nSca != 0) {
					int iSca;
					for(iSca = 0; iSca < nSca; iSca++) {
						int iSym = TBL_Sca[iSca,0/*iSym*/];
						int nOak = TBL_Sca[iSca,1/*nOak*/] + nScwCnt/*0以上*/;
						int nPay = fnScaPay.Invoke(iSym, nOak, arg);
						fnScaWin.Invoke(iSym, nOak, nPay, arg);	//スキャッターウィン通知
						nPaySum += nPay;	//スキャッターシンボルの賞金総額を加算する。
					}
				//ワイルド属性を持たないスキャッターシンボルが一種類も出現していなければ、スキャッターワイルドシンボル単体で通知する。
				//スキャッターワイルドシンボルが二種類以上出現していた場合は、任意の一種類のスキャッターワイルドシンボルに合算して通知する。
				} else if(nScwCnt != 0) {
					int nPay = fnScaPay.Invoke(iScwSym, nScwCnt, arg);
					fnScaWin.Invoke(iScwSym, nScwCnt, nPay, arg);	//スキャッターウィン通知
					nPaySum += nPay;		//スキャッターシンボルの賞金総額を加算する。
				}
			}
			return nPaySum;	//スキャッターシンボルの賞金総額を返す。
		}
		//*****************************************************************************
		//	通常ラインタイプ
		//*****************************************************************************
		public struct ST_Vsl5nl_GetWin_Inf {
			public int nRow;									//行数				1～
			public int nLin;									//ライン数			1～
			public Func<int/*iLin*/,int/*iRel*/,object/*arg*/,int> fnGetRow;			//ライン行取得			(ライン番号=iLin,リール番号=iRel)に対する、行番号(0～(nRow-1))を返して下さい。
			public Func<int/*iSym*/,int/*nOak*/,int/*iLin*/,object/*arg*/,int> fnLinPay;		//ライン配当額取得		(シンボル番号=iSym,出現数=nOak,ライン番号=iLin)に対する、配当額(0～)を返して下さい。
			public Func<int/*iSym*/,int/*nOak*/,object/*arg*/,int> fnScaPay;			//スキャッター配当額取得	(シンボル番号=iSym,出現数=nOak)に対する、配当額(0～)を返して下さい。
			public Func<int/*iSym*/,object/*arg*/,int> fnGetAtt;					//シンボル属性取得		0,VslAtt_Wild,VslAtt_Scatter,(VslAtt_Wild|VslAtt_Scatter)のいずれかの値を返して下さい。
			public Func<int/*iRel*/,int/*iRow*/,object/*arg*/,int> fnGetSta;			//停止形取得(Stationary)	0以上の値で、アプリケーション定義のシンボル番号を返して下さい。
			public Action<int/*iSym*/,int/*nOak*/,int/*iLin*/,int/*nPay*/,object/*arg*/> fnLinWin;	//ラインウィン通知		このラインで揃った(nOak≧1)のコンビネーションの内、最大の配当額(0も含む)について通知する。
			public Action<int/*iSym*/,int/*nOak*/,int/*nPay*/,object/*arg*/> fnScaWin;		//スキャッターウィン通知	当関数は配当額を考慮しない。(nOak≧1)のコンビネーションを全て通知する。
		}
		//-----------------------------------------------------------------------------
		//通常ラインタイプの、入賞判定処理を行う。
		public static int Vsl5nl_GetWin(ST_Vsl5nl_GetWin_Inf pInf, object arg) {
			int nPaySum;						//賞金総額
			int[,] TBL_Sta = new int[5/*iRel*/,pInf.nRow/*iRow*/];	//停止形
			//--- 共通処理 ---
			{
				nPaySum = VslCmn_GetWin(TBL_Sta,
					pInf.nRow,
					pInf.fnScaPay,
					pInf.fnGetAtt,
					pInf.fnGetSta,
					pInf.fnScaWin,
					arg);
			}
			//--- ラインウィン ---
			{
				int iLin, iRel, iRow, iAtt;
				int[] iSym = new int[5];
				int iPurSym, nPurOak, nPurPay;	//純粋コンビネーションの、シンボル,出現数,賞金額
				int iCpxSym, nCpxOak, nCpxPay;	//複合コンビネーションの、シンボル,出現数,賞金額
				for(iLin = 0; iLin < pInf.nLin; iLin++) {
					//このライン上のシンボルを取得する。
					for(iRel = 0; iRel < 5; iRel++) {
						iRow = pInf.fnGetRow.Invoke(iLin, iRel, arg);
						if((uint)iRow >= (uint)pInf.nRow) { throw new ApplicationException(); }
						iSym[iRel] = TBL_Sta[iRel,iRow];
					}
					//第1リールに、ワイルド属性を持たないスキャッターシンボルが有れば、純粋コンビネーションも複合コンビネーションも成立しない。
					if(pInf.fnGetAtt.Invoke(iSym[0], arg) == VslAtt_Scatter) { continue; }	//次のラインの処理へ
					//純粋コンビネーションの、シンボル,及び,出現数(1～5),及び,賞金額を求める。
					// - '純粋コンビネーション'とは、自分で勝手につけた呼び方です。
					//   'スキャッターシンボル以外の特定のシンボル'が、第1リールから連続して並んでいる事を指します。
					iPurSym = iSym[0];
					for(nPurOak = 1; nPurOak < 5; nPurOak++) {
						iAtt = pInf.fnGetAtt.Invoke(iSym[nPurOak], arg);		//このシンボルの属性を取得する。
						if(iAtt == VslAtt_Scatter) { break; }				//このシンボルが、ワイルド属性を持たないスキャッターシンボルならば、ここまで。
						if(iSym[nPurOak] != iPurSym) { break; }				//異なるシンボルが出現したら、ここまで。
					}
					nPurPay = pInf.fnLinPay.Invoke(iPurSym, nPurOak, iLin, arg);
					//複合コンビネーションの、シンボル,及び,出現数(1～5),及び,賞金額を求める。
					// - '複合コンビネーション'とは、自分で勝手につけた呼び方です。
					//   'スキャッターシンボル以外の特定のシンボル'が、0個以上のワイルドシンボルを含んで、第1リールから連続して並んでいる事を指します。
					//   'スキャッターシンボル以外の特定のシンボル'自体が、ワイルドシンボルである事も有り得ます。
					iCpxSym = iSym[0];
					for(nCpxOak = 1; nCpxOak < 5; nCpxOak++) {
						iAtt = pInf.fnGetAtt.Invoke(iSym[nCpxOak], arg);		//このシンボルの属性を取得する。
						if(iAtt == VslAtt_Scatter) { break; }				//このシンボルが、ワイルド属性を持たないスキャッターシンボルならば、ここまで。
						if(iSym[nCpxOak] != iCpxSym) {					//異なるシンボルが出現したら…
							if((iAtt & VslAtt_Wild) == 0) {				//このシンボルがワイルド属性を持っていなければ…
								if((pInf.fnGetAtt.Invoke(iCpxSym, arg) & VslAtt_Wild) == 0) { break; }	//ワイルド属性を持っていない初出のシンボルでなければ、ここまで。
								iCpxSym = iSym[nCpxOak];			//ワイルド属性を持っていない初出のシンボルを格納する。
							}
						}
					}
					nCpxPay = pInf.fnLinPay.Invoke(iCpxSym, nCpxOak, iLin, arg);
					//純粋コンビネーションの賞金額の方が大きければ…
					if(nPurPay > nCpxPay) {
						//純粋コンビネーションを通知する。
						pInf.fnLinWin.Invoke(iPurSym, nPurOak, iLin, nPurPay, arg);	//ラインウィン通知
						nPaySum += nPurPay;	//賞金総額を加算する。
					//複合コンビネーションの賞金額の方が大きい,又は,同じならば、
					} else {
						//複合コンビネーションを通知する。
						// - 複合コンビネーションの方が、出現数が大きい値になっているはずである。
						//   賞金額が同じ場合、出現数が大きい方で入賞を表示する方が、遊技者に不信感を与えないだろうと思い、こうする事にした。
						pInf.fnLinWin.Invoke(iCpxSym, nCpxOak, iLin, nCpxPay, arg);	//ラインウィン通知
						nPaySum += nCpxPay;	//賞金総額を加算する。
					}
				}
			}
			return nPaySum;	//賞金総額を返す。
		}
		//*****************************************************************************
		//	243ウェイ・ルート払い戻しタイプ
		//*****************************************************************************
		public struct ST_Vsl243_GetWin_Inf {
			public int nRow;											//行数				1～
		//無し	public int nLin;											//ライン数			1～
		//無し	public Func<int/*iLin*/,int/*iRel*/,object/*arg*/,int> fnGetRow;					//ライン行取得			(ライン番号=iLin,リール番号=iRel)に対する、行番号(0～(nRow-1))を返して下さい。
			public Func<int/*iSym*/,int/*nOak*/,int[/*nOak*/]/*iRow*/,object/*arg*/,int> fnWayPay;			//ウェイ配当額取得		(シンボル番号=iSym,出現数=nOak,ウェイ=iRow[0～(nOak-1)])に対する、配当額(0～)を返して下さい。
			public Func<int/*iSym*/,int/*nOak*/,object/*arg*/,int> fnScaPay;					//スキャッター配当額取得	(シンボル番号=iSym,出現数=nOak)に対する、配当額(0～)を返して下さい。
			public Func<int/*iSym*/,object/*arg*/,int> fnGetAtt;							//シンボル属性取得		0,VslAtt_Wild,VslAtt_Scatter,(VslAtt_Wild|VslAtt_Scatter)のいずれかの値を返して下さい。
			public Func<int/*iRel*/,int/*iRow*/,object/*arg*/,int> fnGetSta;					//停止形取得(Stationary)	0以上の値で、アプリケーション定義のシンボル番号を返して下さい。
			public Action<int/*iSym*/,int/*nOak*/,int[/*nOak*/]/*iRow*/,int/*nPay*/,object/*arg*/> fnWayWin;	//ウェイウィン通知		当関数は配当額を考慮しない。(nOak≧1)のコンビネーションを全て通知する。
			public Action<int/*iSym*/,int/*nOak*/,int/*nPay*/,object/*arg*/> fnScaWin;				//スキャッターウィン通知	当関数は配当額を考慮しない。(nOak≧1)のコンビネーションを全て通知する。
		}
		//-----------------------------------------------------------------------------
		public static int Vsl243_GetWin(ST_Vsl243_GetWin_Inf pInf, object arg) {
			int nPaySum;						//賞金総額
			int[,] TBL_Sta = new int[5/*iRel*/,pInf.nRow/*iRow*/];	//停止形
			//--- 共通処理 ---
			{
				nPaySum = VslCmn_GetWin(TBL_Sta,
					pInf.nRow,
					pInf.fnScaPay,
					pInf.fnGetAtt,
					pInf.fnGetSta,
					pInf.fnScaWin,
					arg);
			}
			//--- ウェイウィン ---
			{
				int iAtt, nPay;
				int[] bWay = new int[5], iRow = new int[5], iSym = new int[5];
				bWay[0] = 0;	//1リールウェイ入賞フラグ
				for(iRow[0] = 0; iRow[0] < pInf.nRow; iRow[0]++) {
					iSym[0] = TBL_Sta[0,iRow[0]];			//このリールのシンボルを取得する。
					iAtt = pInf.fnGetAtt.Invoke(iSym[0], arg);	//このリールのシンボルの属性を取得する。
					if(iAtt == VslAtt_Scatter) { continue; }	//このシンボルが、ワイルド属性を持たないスキャッターシンボルならば、ここまで。
					if((iAtt & VslAtt_Wild) != 0) { iSym[0] = -1; }	//このシンボルがワイルド属性を持っていたら、ワイルドシンボルを示す値として(-1)を格納しておく。
					{
						bWay[1] = 0;	//2リールウェイ入賞フラグ
						for(iRow[1] = 0; iRow[1] < pInf.nRow; iRow[1]++) {
							iSym[1] = TBL_Sta[1,iRow[1]];			//このリールのシンボルを取得する。
							iAtt = pInf.fnGetAtt.Invoke(iSym[1], arg);	//このリールのシンボルの属性を取得する。
							if(iAtt == VslAtt_Scatter) { continue; }	//このシンボルが、ワイルド属性を持たないスキャッターシンボルならば、ここまで。
							if((iAtt & VslAtt_Wild) != 0) { iSym[1] = -1; }	//このシンボルがワイルド属性を持っていたら、ワイルドシンボルを示す値として(-1)を格納しておく。
							if(iSym[0] != iSym[1]) {			//前のリールのシンボルと、このリールのシンボルが違ったら…
								if(iSym[0] != -1) {			//前のリールのシンボルが、ワイルドシンボルでなければ…
									if(iSym[1] != -1) {		//このリールのシンボルがワイルドシンボルでなければ…
										continue;		//このリールのこのウェイから派生する入賞判定を行わない。
									} else {			//このリールのシンボルがワイルドシンボルならば…
										iSym[1] = iSym[0];	//このリールのシンボルを、前のリールのシンボルに合わせる。
									}
								}
							}
							{
								bWay[2] = 0;	//3リールウェイ入賞フラグ
								for(iRow[2] = 0; iRow[2] < pInf.nRow; iRow[2]++) {
									iSym[2] = TBL_Sta[2,iRow[2]];			//このリールのシンボルを取得する。
									iAtt = pInf.fnGetAtt.Invoke(iSym[2], arg);	//このリールのシンボルの属性を取得する。
									if(iAtt == VslAtt_Scatter) { continue; }	//このシンボルが、ワイルド属性を持たないスキャッターシンボルならば、ここまで。
									if((iAtt & VslAtt_Wild) != 0) { iSym[2] = -1; }	//このシンボルがワイルド属性を持っていたら、ワイルドシンボルを示す値として(-1)を格納しておく。
									if(iSym[1] != iSym[2]) {			//前のリールのシンボルと、このリールのシンボルが違ったら…
										if(iSym[1] != -1) {			//前のリールのシンボルが、ワイルドシンボルでなければ…
											if(iSym[2] != -1) {		//このリールのシンボルがワイルドシンボルでなければ…
												continue;		//このリールのこのウェイから派生する入賞判定を行わない。
											} else {			//このリールのシンボルがワイルドシンボルならば…
												iSym[2] = iSym[1];	//このリールのシンボルを、前のリールのシンボルに合わせる。
											}
										}
									}
									{
										bWay[3] = 0;	//4リールウェイ入賞フラグ
										for(iRow[3] = 0; iRow[3] < pInf.nRow; iRow[3]++) {
											iSym[3] = TBL_Sta[3,iRow[3]];			//このリールのシンボルを取得する。
											iAtt = pInf.fnGetAtt.Invoke(iSym[3], arg);	//このリールのシンボルの属性を取得する。
											if(iAtt == VslAtt_Scatter) { continue; }	//このシンボルが、ワイルド属性を持たないスキャッターシンボルならば、ここまで。
											if((iAtt & VslAtt_Wild) != 0) { iSym[3] = -1; }	//このシンボルがワイルド属性を持っていたら、ワイルドシンボルを示す値として(-1)を格納しておく。
											if(iSym[2] != iSym[3]) {			//前のリールのシンボルと、このリールのシンボルが違ったら…
												if(iSym[2] != -1) {			//前のリールのシンボルが、ワイルドシンボルでなければ…
													if(iSym[3] != -1) {		//このリールのシンボルがワイルドシンボルでなければ…
														continue;		//このリールのこのウェイから派生する入賞判定を行わない。
													} else {			//このリールのシンボルがワイルドシンボルならば…
														iSym[3] = iSym[2];	//このリールのシンボルを、前のリールのシンボルに合わせる。
													}
												}
											}
											{
												bWay[4] = 0;	//5リールウェイ入賞フラグ
												for(iRow[4] = 0; iRow[4] < pInf.nRow; iRow[4]++) {
													iSym[4] = TBL_Sta[4,iRow[4]];			//このリールのシンボルを取得する。
													iAtt = pInf.fnGetAtt.Invoke(iSym[4], arg);	//このリールのシンボルの属性を取得する。
													if(iAtt == VslAtt_Scatter) { continue; }	//このシンボルが、ワイルド属性を持たないスキャッターシンボルならば、ここまで。
													if((iAtt & VslAtt_Wild) != 0) { iSym[4] = -1; }	//このシンボルがワイルド属性を持っていたら、ワイルドシンボルを示す値として(-1)を格納しておく。
													if(iSym[3] != iSym[4]) {			//前のリールのシンボルと、このリールのシンボルが違ったら…
														if(iSym[3] != -1) {			//前のリールのシンボルが、ワイルドシンボルでなければ…
															if(iSym[4] != -1) {		//このリールのシンボルがワイルドシンボルでなければ…
																continue;		//このリールのこのウェイから派生する入賞判定を行わない。
															} else {			//このリールのシンボルがワイルドシンボルならば…
																iSym[4] = iSym[3];	//このリールのシンボルを、前のリールのシンボルに合わせる。
															}
														}
													}
													//5リールウェイ入賞判定
													if(iSym[4] != -1) {	//このリールまでに一つでもワイルドシンボル以外が有れば…	「243ウェイ・ルート払い戻しタイプ」においては、ワイルドシンボルだけの並びに対して配当を設定しない。後述のコメントを参照せよ。
														nPay = pInf.fnWayPay.Invoke(iSym[4], 5/*nOak*/, iRow, arg);
														pInf.fnWayWin.Invoke(iSym[4], 5/*nOak*/, iRow, nPay, arg);	//ウェイウィン通知
														nPaySum += nPay;	//賞金総額を加算する。
														bWay[4] = 1;	//5リールウェイ入賞有り
													}
												}
												if(bWay[4] != 0) {	//5リールウェイ入賞有りならば、この4リールウェイ以下の入賞判定を行わない。
													bWay[3] = 1;	//4リールウェイ入賞有りと見なす
												} else {
													//4リールウェイ入賞判定
													if(iSym[3] != -1) {	//このリールまでに一つでもワイルドシンボル以外が有れば…	「243ウェイ・ルート払い戻しタイプ」においては、ワイルドシンボルだけの並びに対して配当を設定しない。後述のコメントを参照せよ。
														nPay = pInf.fnWayPay.Invoke(iSym[3], 4/*nOak*/, iRow, arg);
														pInf.fnWayWin.Invoke(iSym[3], 4/*nOak*/, iRow, nPay, arg);	//ウェイウィン通知
														nPaySum += nPay;	//賞金総額を加算する。
														bWay[3] = 1;	//4リールウェイ入賞有り
													}
												}
											}
										}
										if(bWay[3] != 0) {	//4リールウェイ入賞有りならば、この3リールウェイ以下の入賞判定を行わない。
											bWay[2] = 1;	//3リールウェイ入賞有りと見なす
										} else {
											//3リールウェイ入賞判定
											if(iSym[2] != -1) {	//このリールまでに一つでもワイルドシンボル以外が有れば…	「243ウェイ・ルート払い戻しタイプ」においては、ワイルドシンボルだけの並びに対して配当を設定しない。後述のコメントを参照せよ。
												nPay = pInf.fnWayPay.Invoke(iSym[2], 3/*nOak*/, iRow, arg);
												pInf.fnWayWin.Invoke(iSym[2], 3/*nOak*/, iRow, nPay, arg);	//ウェイウィン通知
												nPaySum += nPay;	//賞金総額を加算する。
												bWay[2] = 1;	//3リールウェイ入賞有り
											}
										}
									}
								}
								if(bWay[2] != 0) {	//3リールウェイ入賞有りならば、この2リールウェイ以下の入賞判定を行わない。
									bWay[1] = 1;	//2リールウェイ入賞有りと見なす
								} else {
									//2リールウェイ入賞判定
									if(iSym[1] != -1) {	//このリールまでに一つでもワイルドシンボル以外が有れば…	「243ウェイ・ルート払い戻しタイプ」においては、ワイルドシンボルだけの並びに対して配当を設定しない。後述のコメントを参照せよ。
										nPay = pInf.fnWayPay.Invoke(iSym[1], 2/*nOak*/, iRow, arg);
										pInf.fnWayWin.Invoke(iSym[1], 2/*nOak*/, iRow, nPay, arg);	//ウェイウィン通知
										nPaySum += nPay;	//賞金総額を加算する。
										bWay[1] = 1;	//2リールウェイ入賞有り
									}
								}
							}
						}
						if(bWay[1] != 0) {	//2リールウェイ入賞有りならば、この1リールウェイ以下の入賞判定を行わない。
							bWay[0] = 1;	//1リールウェイ入賞有りと見なす
						} else {
							//1リールウェイ入賞判定
							if(iSym[0] != -1) {	//このリールまでに一つでもワイルドシンボル以外が有れば…	「243ウェイ・ルート払い戻しタイプ」においては、ワイルドシンボルだけの並びに対して配当を設定しない。後述のコメントを参照せよ。
								nPay = pInf.fnWayPay.Invoke(iSym[0], 1/*nOak*/, iRow, arg);
								pInf.fnWayWin.Invoke(iSym[0], 1/*nOak*/, iRow, nPay, arg);	//ウェイウィン通知
								nPaySum += nPay;	//賞金総額を加算する。
								bWay[0] = 1;	//1リールウェイ入賞有り
							}
						}
					}
				}
			}
			return nPaySum;	//賞金総額を返す。
		}
		//*****************************************************************************
		//	リールアニメーション処理の補助関数
		//*****************************************************************************
		//直線上の移動において、センサを跨いだ回数を求める。
		public static int VslRel_GetSensorCrossingCount_Linear(double oldpos, double vec, double snspos) {
			double pnewpos;
			return VslRel_GetSensorCrossingCount_Linear(oldpos, vec, snspos, out pnewpos);
		}
		public static int VslRel_GetSensorCrossingCount_Linear(double oldpos, double vec, double snspos, out double pnewpos) {
			int count = 0;	//この変数に、正方向にセンサを跨いだ回数(1),又は,逆方向にセンサを跨いだ回数(-1),又は,センサを跨いでいない(0)を格納する。
			//移動後の位置を求める。
			double newpos = oldpos + vec;
			//移動方向が正方向ならば…
			if(vec > 0.0) {
				//移動前の位置がセンサの位置未満で、移動後の位置がセンサの位置以上ならば、正方向にセンサを跨いだと見なして1加算する。
				if((oldpos < snspos) && (newpos >= snspos)) { count++; }
			//移動方向が逆方向ならば…
			} else if(vec < 0.0) {
				//移動前の位置がセンサの位置以上で、移動後の位置がセンサの位置未満ならば、逆方向にセンサを跨いだと見なして1減算する。
				if((oldpos >= snspos) && (newpos < snspos)) { count--; }
			//移動方向が無ければ…
			} else {
				/** no job **/
			}
			//移動後の位置を格納する変数のポインタが指定されていたら、移動後の位置を格納する。
			pnewpos = newpos;
			//正方向にセンサを跨いだ回数(1),又は,逆方向にセンサを跨いだ回数(-1),又は,センサを跨いでいない(0)を返す。
			return count;
		}
		//-----------------------------------------------------------------------------
		//円周上の移動において、センサを跨いだ回数を求める。
		public static int VslRel_GetSensorCrossingCount_Circular(double oldpos, double vec, double snspos, double lbound, double ubound) {
			double pnewpos;
			return VslRel_GetSensorCrossingCount_Circular(oldpos, vec, snspos, lbound, ubound, out pnewpos);
		}
		public static int VslRel_GetSensorCrossingCount_Circular(double oldpos, double vec, double snspos, double lbound, double ubound, out double pnewpos) {
			int count = 0;	//この変数に、正方向にセンサを跨いだ回数(N),又は,逆方向にセンサを跨いだ回数(-N),又は,センサを跨いでいない(0)を格納する。
			//領域の幅を求める。
			double width = ubound - lbound;
			if(width <= 0.0) { throw new ApplicationException(); }	//(lbound≧ubound)が指定された。呼び出し側のバグです。
			//移動前の位置を、[lbound,ubound)の範囲に補正する。
			oldpos -= lbound;
			oldpos = oldpos % width;
			if(oldpos < 0.0) { oldpos += width; }
			oldpos += lbound;
			//センサの位置を、[lbound,ubound)の範囲に補正する。
			snspos -= lbound;
			snspos = snspos % width;
			if(snspos < 0.0) { snspos += width; }
			snspos += lbound;
			{
				//初回のために、移動前の位置を、仮想的な前回の移動後の位置とする。
				double newpos = oldpos;
				//移動方向が正方向ならば…
				if(vec > 0.0) {
					do {
						//前回の移動後の位置を、今回の移動前の位置とする。
						oldpos = newpos;
						//移動後の位置を求める。
						newpos = oldpos + vec;	//(newpos≧ubound)になっても構わない。
						//移動前の位置がセンサの位置未満で、移動後の位置がセンサの位置以上ならば、正方向にセンサを跨いだと見なして1加算する。
						if((oldpos < snspos) && (newpos >= snspos)) { count++; }
						//移動後の位置が上界を超過した分を、次回の移動距離とする。
						vec = newpos - ubound;	//(vec＜0.0)になっても構わない。								//┐
						//移動後の位置が上界以上ならば…											//│
						if(vec >= 0.0) {	//「if(newpos>=ubound){～」で判定しても同じです。	①←┐					//├この処理順序を逆にしないように注意!!
							//移動後の位置を、下界に折り返す。					//　│					//│
							newpos = lbound;							//　│					//┘
							//移動後の位置がセンサの位置以上(イコール判定でも構わない)ならば、正方向にセンサを跨いだと見なして1加算する。	//┬(snspos==lbound)が指定された場合のための判定処理です。
							if(newpos >= snspos) { count++; }	//(newpos==snspos)で判定しても構わない。				//┘(newpos==snspos)でなく(newpos>=snspos)で判定した理由は、上の処理と見た目を合わせるためだけで、実際にはどちらでも構いません。
						}										//　│
					} while(vec > 0.0);	//次回の移動距離が残っている間、繰り返す。			②←┴①は≧,②は＞である事に注意しろ。
				//移動方向が逆方向ならば…
				} else if(vec < 0.0) {
					do {
						//前回の移動後の位置を、今回の移動前の位置とする。
						oldpos = newpos;
						//移動後の位置を求める。
						newpos = oldpos + vec;	//(newpos＜lbound)になっても構わない。
						//移動前の位置がセンサの位置以上で、移動後の位置がセンサの位置未満ならば、逆方向にセンサを跨いだと見なして1減算する。	//┬(snspos==lbound)が指定された場合もここで判定出来ます。
						if((oldpos >= snspos) && (newpos < snspos)) { count--; }								//┘移動方向が正方向の場合の処理と違って、移動後の位置を折り返す処理の中で、センサを跨いだかどうかの判定を行う必要は有りません。
						//移動後の位置が下界を下回った分を、次回の移動距離とする。
						vec = newpos - lbound;	//(vec＞0.0)になっても構わない。								//┐
						//移動後の位置が下界未満ならば…											//│
						if(vec < 0.0) {		//「if(newpos<lbound){～」で判定しても同じです。						//├この処理順序を逆にしないように注意!!
							//移動後の位置を、上界に折り返す。										//│
							newpos = ubound;	//一時的に[lbound,ubound)の範囲外となりますが、必ず次回のループが有るので大丈夫です。	//┘
						}
					} while(vec < 0.0);	//次回の移動距離が残っている間、繰り返す。
				}
				//移動後の位置を格納する変数のポインタが指定されていたら、移動後の位置を格納する。
				pnewpos = newpos;
			}
			//正方向にセンサを跨いだ回数(N),又は,逆方向にセンサを跨いだ回数(-N),又は,センサを跨いでいない(0)を返す。
			return count;
		}
		//-----------------------------------------------------------------------------
		//直線上の移動において、センサを超えた回数を求める。
		public static int VslRel_GetSensorExceedingCount_Linear(double oldpos, double vec, double snspos) {
			double pnewpos;
			return VslRel_GetSensorExceedingCount_Linear(oldpos, vec, snspos, out pnewpos);
		}
		public static int VslRel_GetSensorExceedingCount_Linear(double oldpos, double vec, double snspos, out double pnewpos) {
			int count = 0;	//この変数に、正方向にセンサを超えた回数(1),又は,逆方向にセンサを超えた回数(-1),又は,センサを超えていない(0)を格納する。
			//移動後の位置を求める。
			double newpos = oldpos + vec;
			//移動方向が正方向ならば…
			if(vec > 0.0) {
				//移動前の位置に関係無く、移動後の位置がセンサの位置以上ならば、正方向にセンサを超えたと見なして1加算する。
				if(newpos >= snspos) { count++; }
			//移動方向が逆方向ならば…
			} else if(vec < 0.0) {
				//移動前の位置に関係無く、移動後の位置がセンサの位置未満ならば、逆方向にセンサを超えたと見なして1減算する。
				if(newpos <  snspos) { count--; }
			//移動方向が無ければ…
			} else {
				/** no job **/
			}
			//移動後の位置を格納する変数のポインタが指定されていたら、移動後の位置を格納する。
			pnewpos = newpos;
			//正方向にセンサを超えた回数(1),又は,逆方向にセンサを超えた回数(-1),又は,センサを超えていない(0)を返す。
			return count;
		}
		//*****************************************************************************
		//	dVslDefC.exe対応ユーティリティ
		//*****************************************************************************
		//ゲームモードのレジストリキーに属する、キーの名前
		public const int VslDef_ReelStrip	= 1;		//リールストリップ
		public const int VslDef_LineDef		= 2;		//ライン定義
		public const int VslDef_PayTable	= 3;		//ペイテーブル
		//-----------------------------------------------------------------------------
		//シンボルの数を返す。
		public static int VslDef_GetSymNum(VoidPtr pEnmKey, int _RegKey_VslSym) {
			return EnmDef_GetNum(pEnmKey, _RegKey_VslSym);
		}
		//-----------------------------------------------------------------------------
//{{2017/04/12追加:VslDef_GetSymName(),VslDef_GetSymDesc(),VslDef_GetGmdName()を追加しました。
		//シンボルの名前を返す。
		public static string VslDef_GetSymName(VoidPtr pEnmKey, int _RegKey_VslSym, int iVslSym) {
			return EnmDef_GetName(pEnmKey, _RegKey_VslSym, iVslSym);
		}
		/*--------------------------------------------------------------------------*/
		//シンボルの説明を返す。
		public static string VslDef_GetSymDesc(VoidPtr pEnmKey, int _RegKey_VslSym, int iVslSym) {
			return EnmDef_GetDesc(pEnmKey, _RegKey_VslSym, iVslSym);
		}
//}}2017/04/12追加:VslDef_GetSymName(),VslDef_GetSymDesc(),VslDef_GetGmdName()を追加しました。
		//-----------------------------------------------------------------------------
		//ゲームモードの数を返す。
		public static int VslDef_GetGmdNum(VoidPtr pEnmKey, int _RegKey_VslGmd) {
			return EnmDef_GetNum(pEnmKey, _RegKey_VslGmd);
		}
		//-----------------------------------------------------------------------------
//{{2017/04/12追加:VslDef_GetSymName(),VslDef_GetSymDesc(),VslDef_GetGmdName()を追加しました。
		//ゲームモードの名前を返す。
		public static string VslDef_GetGmdName(VoidPtr pEnmKey, int _RegKey_VslGmd, int iVslGmd) {
			return EnmDef_GetName(pEnmKey, _RegKey_VslGmd, iVslGmd);
		}
//}}2017/04/12追加:VslDef_GetSymName(),VslDef_GetSymDesc(),VslDef_GetGmdName()を追加しました。
		//-----------------------------------------------------------------------------
		//このゲームモードの、リールストリップの数を返す。
		public static int VslDef_GetRstNum(VoidPtr pVslKey, int iVslGmd) {
			int nReel = REG_num_blob_l(pVslKey, iVslGmd, VslDef_ReelStrip);
			if(nReel == 0) { throw new ApplicationException(); }	//リールストリップが一本も無ければエラー停止する。多分呼び出し側のバグです。
			return nReel;
		}
		//-----------------------------------------------------------------------------
		//このゲームモードの、このリールストリップの長さを返す。
		public static int VslDef_GetRstLen(VoidPtr pVslKey, int iVslGmd, int iReel) {
			BytePtr pReelStrip = REG_get_blob_l(pVslKey, iVslGmd, VslDef_ReelStrip, iReel);
			if(!pReelStrip) { throw new ApplicationException(); }	//リールストリップのBLOBが取得出来なければエラー停止する。多分呼び出し側のバグです。
			return REG_get_blob_size(pReelStrip);
		}
		//-----------------------------------------------------------------------------
		//このゲームモードの、このリールストリップの、指定された位置のシンボルを返す。
		public static int VslDef_GetRstSym(VoidPtr pVslKey, int iVslGmd, int iReel, int iPos) {
			BytePtr pReelStrip = REG_get_blob_l(pVslKey, iVslGmd, VslDef_ReelStrip, iReel);
			if(!pReelStrip) { throw new ApplicationException(); }	//リールストリップのBLOBが取得出来なければエラー停止する。多分呼び出し側のバグです。
			if((uint)iPos >= (uint)REG_get_blob_size(pReelStrip)) { throw new ApplicationException(); }	//シンボル位置が範囲外ならばエラー停止する。多分呼び出し側のバグです。
			return pReelStrip[iPos];
		}
		//-----------------------------------------------------------------------------
		//このゲームモードの、このリールストリップの、指定された位置を自動的に折り返した位置のシンボルを返す。
		public static int VslDef_GetRstSym_w(VoidPtr pVslKey, int iVslGmd, int iReel, int iPos) {
			int nLen = VslDef_GetRstLen(pVslKey, iVslGmd, iReel);
			if((iPos %= nLen) < 0) { iPos += nLen; }	//INT_MIN～INT_MAXを、0～VslDef_GetRstNum(pVslKey,iVslGmd)-1の範囲に折り返す。
			return VslDef_GetRstSym(pVslKey, iVslGmd, iReel, iPos);
		}
		//-----------------------------------------------------------------------------
		//このゲームモードの、ライン数を返す。
		public static int VslDef_GetLinNum(VoidPtr pVslKey, int iVslGmd) {
			int nLine = REG_num_blob_l(pVslKey, iVslGmd, VslDef_LineDef);
			if(nLine == 0) { throw new ApplicationException(); }	//ライン定義が一つも無ければエラー停止する。多分呼び出し側のバグです。
			return nLine;
		}
		//-----------------------------------------------------------------------------
		//このゲームモードの、このラインの、指定されたリール番号の、行番号を返す。
		public static int VslDef_GetLinRow(VoidPtr pVslKey, int iVslGmd, int iLine, int iReel) {
			BytePtr pLineDef = REG_get_blob_l(pVslKey, iVslGmd, VslDef_LineDef, iLine);
			if(!pLineDef) { throw new ApplicationException(); }	//ライン定義のBLOBが取得出来なければエラー停止する。多分呼び出し側のバグです。
			if((uint)iReel >= (uint)REG_get_blob_size(pLineDef)) { throw new ApplicationException(); }	//リール番号が範囲外ならばエラー停止する。多分呼び出し側のバグです。
			return pLineDef[iReel];
		}
		//-----------------------------------------------------------------------------
		//このゲームモードの、このシンボルに、何個揃いまでの配当額が設定されているかを返す。
		public static int VslDef_GetMaxOak(VoidPtr pVslKey, int iVslGmd, int iVslSym) {
			UInt16Ptr pPayTable = REG_get_blob_l(pVslKey, iVslGmd, VslDef_PayTable, iVslSym);
			if(!pPayTable) { return 0; }	//このゲームモードのこのシンボルに、配当額が一つも設定されていなければ、0を返します。
			return REG_get_blob_size(pPayTable) / sizeof(ushort);
		}
		//-----------------------------------------------------------------------------
		//このゲームモードの、このシンボルの、指定されたシンボルと揃った数に対応する、配当額を返す。
		public static int VslDef_GetPay(VoidPtr pVslKey, int iVslGmd, int iVslSym, int nOfAKind) {
			UInt16Ptr pPayTable = REG_get_blob_l(pVslKey, iVslGmd, VslDef_PayTable, iVslSym);
			if(!pPayTable) { throw new ApplicationException(); }	//ペイテーブルのBLOBが取得出来なければエラー停止する。多分呼び出し側のバグです。
			if((uint)(nOfAKind - 1) >= ((uint)REG_get_blob_size(pPayTable) / sizeof(ushort))) { throw new ApplicationException(); }	//配当額が設定されている揃い数の最大値を超えていたらエラー停止する。多分呼び出し側のバグです。
			return Exp10_Decode(pPayTable[nOfAKind - 1]);	//配当額は、exp10形式で格納されているので、元の値にデコードして返す。
		}
		//-----------------------------------------------------------------------------
		//□使用例
#if false
		using System;
		using System.IO;
		using static org.piece_me.libclip;
		using static Const;
		class Program {
			static void Main() {
				byte[] TBL_RegTbl = File.ReadAllBytes("data/RegTbl.bin");
				BytePtr pEnmKey = REG_open_key(TBL_RegTbl, RegKey_EnmDef);
				BytePtr pVslKey = REG_open_key(TBL_RegTbl, RegKey_VslDef);
				int nSym, iSym, nGmd, iGmd, nRst, iRst, nLen, iPos, nLin, iLin, iRow, nOak, iOak, nPay;
				//シンボル
				nSym = VslDef_GetSymNum(pEnmKey, RegKey_VslSym);
				Console.Write("({0} == {1}) {2}\n", nSym, VslSym_Max, (nSym == VslSym_Max) ? "ok" : "ERROR");
				for(iSym = 1; iSym <= nSym; iSym++) {
					Console.Write("{0}: {1} {2}\n",
						iSym,
//{{2017/04/12変更:VslDef_GetSymName(),VslDef_GetSymDesc(),VslDef_GetGmdName()を追加しました。
//						EnmDef_GetName(pEnmKey, RegKey_VslSym, iSym),
//						EnmDef_GetDesc(pEnmKey, RegKey_VslSym, iSym));
//↓2017/04/12変更:VslDef_GetSymName(),VslDef_GetSymDesc(),VslDef_GetGmdName()を追加しました。
						VslDef_GetSymName(pEnmKey, RegKey_VslSym, iSym),
						VslDef_GetSymDesc(pEnmKey, RegKey_VslSym, iSym));
//}}2017/04/12変更:VslDef_GetSymName(),VslDef_GetSymDesc(),VslDef_GetGmdName()を追加しました。
				}
				//ゲームモード
				nGmd = VslDef_GetGmdNum(pEnmKey, RegKey_VslGmd);
				Console.Write("({0} == {1}) {2}\n", nGmd, VslGmd_Max, (nGmd == VslGmd_Max) ? "ok" : "ERROR");
				for(iGmd = 1; iGmd <= nGmd; iGmd++) {
					Console.Write("{0}: {1}\n",
						iGmd,
//{{2017/04/12変更:VslDef_GetSymName(),VslDef_GetSymDesc(),VslDef_GetGmdName()を追加しました。
//						EnmDef_GetName(pEnmKey, RegKey_VslGmd, iGmd));
//↓2017/04/12変更:VslDef_GetSymName(),VslDef_GetSymDesc(),VslDef_GetGmdName()を追加しました。
						VslDef_GetGmdName(pEnmKey, RegKey_VslGmd, iGmd));
//}}2017/04/12変更:VslDef_GetSymName(),VslDef_GetSymDesc(),VslDef_GetGmdName()を追加しました。
					//リールストリップ
					nRst = VslDef_GetRstNum(pVslKey, iGmd);
					Console.Write("{0}\n", nRst);
					for(iRst = 0; iRst < nRst; iRst++) {
						nLen = VslDef_GetRstLen(pVslKey, iGmd, iRst);
						Console.Write("{0}: {1}:", iRst, nLen);
						for(iPos = 0; iPos < nLen; iPos++) {
							iSym = VslDef_GetRstSym(pVslKey, iGmd, iRst, iPos);
//{{2017/04/12変更:VslDef_GetSymName(),VslDef_GetSymDesc(),VslDef_GetGmdName()を追加しました。
//							Console.Write(" {0}", EnmDef_GetName(pEnmKey, RegKey_VslSym, iSym));
//↓2017/04/12変更:VslDef_GetSymName(),VslDef_GetSymDesc(),VslDef_GetGmdName()を追加しました。
							Console.Write(" {0}", VslDef_GetSymName(pEnmKey, RegKey_VslSym, iSym));
//}}2017/04/12変更:VslDef_GetSymName(),VslDef_GetSymDesc(),VslDef_GetGmdName()を追加しました。
						}
						Console.Write("\n");
					}
					//ライン定義
					nLin = VslDef_GetLinNum(pVslKey, iGmd);
					Console.Write("{0}\n", nLin);
					for(iLin = 0; iLin < nLin; iLin++) {
						for(iRst = 0; iRst < nRst; iRst++) {	//当サンプルでは(リール数==リールストリップ数)と仮定した。一般的にはそうだが常にそうだとは限らない。/clip/clipvsl.hの'Thu Mar 16 22:31:07 JST 2017 Naoyuki Sawa'のコメントを参照せよ。
							iRow = VslDef_GetLinRow(pVslKey, iGmd, iLin, iRst);
							Console.Write(" {0}", iRow);
						}
						Console.Write("\n");
					}
					//ペイテーブル
					for(iSym = 1; iSym <= nSym; iSym++) {
						nOak = VslDef_GetMaxOak(pVslKey, iGmd, iSym);
						if(nOak != 0) {
//{{2017/04/12変更:VslDef_GetSymName(),VslDef_GetSymDesc(),VslDef_GetGmdName()を追加しました。
//							Console.Write("{0}:", EnmDef_GetName(pEnmKey, RegKey_VslSym, iSym));
//↓2017/04/12変更:VslDef_GetSymName(),VslDef_GetSymDesc(),VslDef_GetGmdName()を追加しました。
							Console.Write("{0}:", VslDef_GetSymName(pEnmKey, RegKey_VslSym, iSym));
//}}2017/04/12変更:VslDef_GetSymName(),VslDef_GetSymDesc(),VslDef_GetGmdName()を追加しました。
							for(iOak = 1; iOak <= nOak; iOak++) {
								nPay = VslDef_GetPay(pVslKey, iGmd, iSym, iOak);
								Console.Write(" {0}", nPay);
							}
							Console.Write("\n");
						}
					}
				}
			}
		}
#endif
	}
}
