最終更新日 2024-09-25

戦争 - ヘックス位置

ヘックス位置とは、
戦争中、画面に表示されているヘックス画面(野戦もしくは籠城戦)において、
武将が居るヘックス(升目)の「位置」もしくは、その位置の役割(山・橋・門等)を得るための
APIとなります。

野戦のヘックス

攻城戦のヘックス

ヘックスマップエディタを触ってみると、ヘックスがすんなり理解できる

ヘックスマップエディタ を触ってみると、
ScenarioModのヘックスの概念の理解が促進されることでしょう。

現在の画面に出陣している武将のヘックス位置を得る

ヘックス位置型 Get_武将の野戦ヘックス位置(int 武将番号【配列用】)
ヘックス位置型 Get_武将の攻城戦ヘックス位置(int 武将番号【配列用】)
int Get_攻城戦ヘックス高さ(ヘックス位置型 P)
int Get_攻城戦ヘックス高さ(double X位置, double Y位置)
void カスタム::On_残りターン変更《戦争画面》(int 残りターン数) {
	if (Is_野戦中()) {
		番号リスト型 list = Get_出陣中の武将番号リスト【配列用】《表示中マップ》();

		for (int iBushouID : list) {
			ヘックス位置型 pos = Get_武将の野戦ヘックス位置(iBushouID);
			デバッグ出力 << Get_名字(iBushouID) + Get_名前(iBushouID) << "のヘックス位置:(" << pos.X << ", " << pos.Y << ")" << endl;
		}
	}
}

void カスタム::On_ターン変更《攻城中画面》(int ターン数) {
	if (Is_攻城戦中()) {
		番号リスト型 list = Get_出陣中の武将番号リスト【配列用】《表示中マップ》();

		for (int iBushouID : list) {
			ヘックス位置型 pos = Get_武将の攻城戦ヘックス位置(iBushouID);
			int 高さ = Get_攻城戦ヘックス高さ(pos); // Get_攻城戦ヘックス高さ(pos.X, pos.Y); でも同じ
			デバッグ出力 << Get_名字(iBushouID) + Get_名前(iBushouID) << "のヘックス位置:(" << pos.X << ", " << pos.Y << ")" << endl;
			デバッグ出力 << "その位置の高さ" << 高さ << endl;
		}
	}
}

ヘックス位置Aとヘックス位置Bの間隔

武将Aと武将Bとの間隔や、武将と特定のポイント(城や川)などの近さも計算できます。

int Get_ヘックス間隔(ヘックス位置型 P1, ヘックス位置型 P2)
void カスタム::On_残りターン変更《戦争画面》(int 残りターン数) {

	/*
	* もしも上杉謙信と武田信玄が、二人とも戦場に居るならば
	* 二人のヘックス上の位置の間隔を取得する。
	*/

	番号リスト型 list = Get_出陣中の武将番号リスト【配列用】《表示中マップ》(); // 出陣中の

	番号リスト型 信玄と謙信;
	ヘックス位置型 謙信pos, 信玄pos; // 謙信、信玄の位置

	for each (int iBushouID in list) {
		if (p武将戸籍情報[iBushouID].顔番号 == 顔番号::武田晴信) { // 信玄が出陣している。
			信玄と謙信.push_back(iBushouID);
			// どのヘックスに居るのか覚えておく
			if (Is_野戦中()) {
				信玄pos = Get_武将の野戦ヘックス位置(iBushouID);
			}
			else if (Is_攻城戦中()) {
				信玄pos = Get_武将の攻城戦ヘックス位置(iBushouID);
			}

		}

		else if (p武将戸籍情報[iBushouID].顔番号 == 顔番号::長尾景虎) { // 謙信が出陣している。
			信玄と謙信.push_back(iBushouID);
			// どのヘックスに居るのか覚えておく
			if (Is_野戦中()) {
				謙信pos = Get_武将の野戦ヘックス位置(iBushouID);
			}
			else if (Is_攻城戦中()) {
				謙信pos = Get_武将の攻城戦ヘックス位置(iBushouID);
			}

		}
	}

	// 2人とも存在している
	if (信玄と謙信.size() == 2) {
		int 二人の間隔 = Get_ヘックス間隔(謙信pos, 信玄pos);
		デバッグ出力 << "信玄から謙信へは直線で" << 二人の間隔 << "マス移動すれば同じ位置に到達出来る。" << endl;
	}
}

この考え方を流用するだけで...

void カスタム::On_基本コマンド表示直前《戦争画面》(int 武将番号) {

	/*
	* もしも上杉謙信と武田信玄が、接しているならば、
	* 互いに一喝の命令が変化する。
	*/

	int iBushouID = 武将番号 - 1; // 武将番号→武将番号【配列用】に

	if (0 <= iBushouID && iBushouID < 最大数::武将情報::配列数) {

		番号リスト型 list = Get_出陣中の武将番号リスト【配列用】《表示中マップ》(); // 出陣中の

		番号リスト型 信玄と謙信;

		ヘックス位置型 謙信pos, 信玄pos; // 謙信、信玄の位置
		for each (int iBushouID in list) {
			if (p武将戸籍情報[iBushouID].顔番号 == 顔番号::武田晴信) { // 信玄が出陣している。
				信玄と謙信.push_back(iBushouID);
				// どのヘックスに居るのか覚えておく
				if (Is_野戦中()) {
					信玄pos = Get_武将の野戦ヘックス位置(iBushouID);
				}
				else if (Is_攻城戦中()) {
					信玄pos = Get_武将の攻城戦ヘックス位置(iBushouID);
				}

			}

			else if (p武将戸籍情報[iBushouID].顔番号 == 顔番号::長尾景虎) { // 謙信が出陣している。
				信玄と謙信.push_back(iBushouID);
				// どのヘックスに居るのか覚えておく
				if (Is_野戦中()) {
					謙信pos = Get_武将の野戦ヘックス位置(iBushouID);
				}
				else if (Is_攻城戦中()) {
					謙信pos = Get_武将の攻城戦ヘックス位置(iBushouID);
				}

			}
		}

		// 2人とも存在し、その距離はヘックス升で1つ。即ち隣接している。
		if (信玄と謙信.size() == 2 && Get_ヘックス間隔(謙信pos, 信玄pos) == 1) {
			// 信玄なら
			if (p武将戸籍情報[iBushouID].顔番号 == 顔番号::武田晴信) {
				Set_コマンド名(戦争画面::基本コマンド名::一喝, "龍撃虎");
				Set_コマンド名(戦争画面::基本コマンド名::一喝, "龍撃虎");
				// 謙信なら
			}
			else if (p武将戸籍情報[iBushouID].顔番号 == 顔番号::長尾景虎) {
				Set_コマンド名(戦争画面::基本コマンド名::一喝, "虎破龍");
				Set_コマンド名(戦争画面::基本コマンド名::一喝, "虎破龍");
			}
		}
	}
}
		

野戦のヘックスの役割

int Get_野戦ヘックス役割(double X位置, double Y位置)
int Get_野戦ヘックス役割(ヘックス位置型 P)

役割の概念は、ヘックスエディタを利用してみるとわかりやすい

下図では中央部だけフォーカスしているが、実際は当然野戦マップ全体に役割が割り当てられている

void カスタム::On_残りターン変更《戦争画面》(int 残りターン数) {
	if (Is_野戦中())
	{
		// 今表示中の武将達
		auto blist = Get_出陣中の武将番号リスト【配列用】《表示中マップ》();

		int iBushouID = 0xFFFF;
		if (blist.size() >= 1) {
			iBushouID = blist[0]; // 先頭の人だけ表示

			ヘックス位置型 位置 = Get_武将の野戦ヘックス位置(iBushouID);
			int 役割 = Get_野戦ヘックス役割(位置);
			デバッグ出力 << Get_名字(iBushouID) + Get_名前(iBushouID) << "は、";
			switch (役割) {
			case 野戦ヘックス役割::高山:
				デバッグ出力 << "高山";
				break;
			case 野戦ヘックス役割::中山:
				デバッグ出力 << "中山";
				break;
			case 野戦ヘックス役割::低山:
				デバッグ出力 << "低山";
				break;
			case 野戦ヘックス役割::城:
				デバッグ出力 << "城";
				break;
			case 野戦ヘックス役割::川:
				デバッグ出力 << "川";
				break;
			case 野戦ヘックス役割::海: // 野戦ヘックス役割::湖 と同じ番号
				デバッグ出力 << "海か湖";
				break;
			case 野戦ヘックス役割::森:
				デバッグ出力 << "森";
				break;
			case 野戦ヘックス役割::橋:
				デバッグ出力 << "橋";
				break;
			case 野戦ヘックス役割::湿地:
				デバッグ出力 << "湿地";
				break;
			case 野戦ヘックス役割::荒れ地:
				デバッグ出力 << "荒れ地";
				break;
			case 野戦ヘックス役割::道:
				デバッグ出力 << "道";
				break;
			case 野戦ヘックス役割::平地:
				デバッグ出力 << "平地";
				break;
			case 野戦ヘックス役割::枠外: // 左右の境界線など、ヘックスとしては存在するが、ユニットなど決して到達できない枠外というチップもある
				デバッグ出力 << "枠外";
				break;
			case -1: // 指定した「位置」が不適切で、枠外をさらに飛び越えて、そもそも対応するヘックス自体ないような場所を指し示している
				デバッグ出力 << "マップ外";
				break;
			}

			デバッグ出力 << "に居ます" << endl;
		}
	}
}

特定のヘックスの位置を指定し、その役割を調査することもあるだろう。

void カスタム::On_残りターン変更《戦争画面》(int 残りターン数) {
	if (Is_野戦中())
	{
		// 今表示中の武将達
		auto blist = Get_出陣中の武将番号リスト【配列用】《表示中マップ》();

		int iBushouID = 0xFFFF;
		if (blist.size() >= 1) {
			iBushouID = blist[0]; // 先頭の人だけ表示

			ヘックス位置型 位置{野戦ヘックス::中心X, 野戦ヘックス::中心Y};
			int 役割 = Get_野戦ヘックス役割(位置);
			デバッグ出力 << "野戦の中心は、(" << 位置.X << "," << 位置.Y << ")" << endl;
			デバッグ出力 << "その役割は)" << endl;
			switch (役割) {
			case 野戦ヘックス役割::高山:
				デバッグ出力 << "高山";
				break;
			case 野戦ヘックス役割::中山:
				デバッグ出力 << "中山";
				break;
			case 野戦ヘックス役割::低山:
				デバッグ出力 << "低山";
				break;
			case 野戦ヘックス役割::城: // 通常 城ヘックスの上に居ることは出来ない(城に入場してしまう)ため、こうなることはない
				デバッグ出力 << "城";
				break;
			case 野戦ヘックス役割::川:
				デバッグ出力 << "川";
				break;
			case 野戦ヘックス役割::海: // 野戦ヘックス役割::湖 と同じ番号
				デバッグ出力 << "海か湖";
				break;
			case 野戦ヘックス役割::森:
				デバッグ出力 << "森";
				break;
			case 野戦ヘックス役割::橋:
				デバッグ出力 << "橋";
				break;
			case 野戦ヘックス役割::湿地:
				デバッグ出力 << "湿地";
				break;
			case 野戦ヘックス役割::荒れ地:
				デバッグ出力 << "荒れ地";
				break;
			case 野戦ヘックス役割::道:
				デバッグ出力 << "道";
				break;
			case 野戦ヘックス役割::平地:
				デバッグ出力 << "平地";
				break;
			case 野戦ヘックス役割::枠外: // 左右の境界線など、ヘックスとしては存在するが、ユニットなど決して到達できない枠外というチップもある
				デバッグ出力 << "枠外";
				break;
			case -1: // 指定した「位置」が不適切で、枠外をさらに飛び越えて、そもそも対応するヘックス自体ないような場所を指し示している
				デバッグ出力 << "マップ外";
				break;
			}

			デバッグ出力 << "です" << endl;
		}
	}
}

攻城戦のヘックスの役割

int Get_攻城戦ヘックス役割(double X位置, double Y位置)
int Get_攻城戦ヘックス役割(ヘックス位置型 P)

役割の概念は、ヘックスエディタを利用してみるとわかりやすい

void カスタム::On_ターン変更《攻城中画面》(int ターン数) {
	if (Is_攻城戦中())
	{
		// 今表示中の武将達
		auto blist = Get_出陣中の武将番号リスト【配列用】《表示中マップ》();

		int iBushouID = 0xFFFF;
		if (blist.size() >= 1) {
			iBushouID = blist[0]; // 先頭の人だけ表示

			ヘックス位置型 位置 = Get_武将の攻城戦ヘックス位置(iBushouID);
			int 役割 = Get_攻城戦ヘックス役割(位置);
			デバッグ出力 << Get_名字(iBushouID) + Get_名前(iBushouID) << "は、";
			switch (役割) {
			case 攻城戦ヘックス役割::城外平地:
				デバッグ出力 << "城外平地";
				break;
			case 攻城戦ヘックス役割::城内平地:
				デバッグ出力 << "城内平地";
				break;
			case 攻城戦ヘックス役割::城内整地:
				デバッグ出力 << "城内整地";
				break;
			case 攻城戦ヘックス役割::城壁:
				デバッグ出力 << "城壁";
				break;
			case 攻城戦ヘックス役割::堀: // 攻城戦ヘックス役割::海 と 攻城戦ヘックス役割::湖 も同じ値
				デバッグ出力 << "堀 か 海 か 湖";
				break;
			case 攻城戦ヘックス役割::堀橋:
				デバッグ出力 << "堀橋";
				break;
			case 攻城戦ヘックス役割::本丸:
				デバッグ出力 << "本丸";
				break;
			case 攻城戦ヘックス役割::柵:
				デバッグ出力 << "柵";
				break;
			case 攻城戦ヘックス役割::森:
				デバッグ出力 << "森";
				break;
			case 攻城戦ヘックス役割::櫓:
				デバッグ出力 << "櫓";
				break;
			case 攻城戦ヘックス役割::閉門:
				デバッグ出力 << "閉まっている門";
				break;
			case 攻城戦ヘックス役割::開門:
				デバッグ出力 << "開いている門";
				break;
			case 攻城戦ヘックス役割::枠外: // 左右の境界線など、ヘックスとしては存在するが、ユニットなど決して到達できない枠外というチップもある
				デバッグ出力 << "枠外";
				break;
			case -1: // 指定した「位置」が不適切で、枠外をさらに飛び越えて、そもそも対応するヘックス自体ないような場所を指し示している
				デバッグ出力 << "マップ外";
				break;
			}

			デバッグ出力 << "に居ます" << endl;
		}
	}
}

特定のヘックスの位置を指定し、その役割を調査することもあるだろう。

void カスタム::On_ターン変更《攻城中画面》(int ターン数) {
	if (Is_攻城戦中())
	{
		// 今表示中の武将達
		auto blist = Get_出陣中の武将番号リスト【配列用】《表示中マップ》();

		int iBushouID = 0xFFFF;
		if (blist.size() >= 1) {
			iBushouID = blist[0]; // 先頭の人だけ表示

			ヘックス位置型 位置{ 攻城戦ヘックス::中心X, 攻城戦ヘックス::中心Y };;
			int 役割 = Get_攻城戦ヘックス役割(位置);
			デバッグ出力 << "攻城戦の中心は、(" << 位置.X << "," << 位置.Y << ")" << endl;
			デバッグ出力 << "その役割は)" << endl;
			switch (役割) {
			case 攻城戦ヘックス役割::城外平地:
				デバッグ出力 << "城外平地";
				break;
			case 攻城戦ヘックス役割::城内平地:
				デバッグ出力 << "城内平地";
				break;
			case 攻城戦ヘックス役割::城内整地:
				デバッグ出力 << "城内整地";
				break;
			case 攻城戦ヘックス役割::城壁:
				デバッグ出力 << "城壁";
				break;
			case 攻城戦ヘックス役割::堀: // 攻城戦ヘックス役割::海 と 攻城戦ヘックス役割::湖 も同じ値
				デバッグ出力 << "堀 か 海 か 湖";
				break;
			case 攻城戦ヘックス役割::堀橋:
				デバッグ出力 << "堀橋";
				break;
			case 攻城戦ヘックス役割::本丸:
				デバッグ出力 << "本丸";
				break;
			case 攻城戦ヘックス役割::柵:
				デバッグ出力 << "柵";
				break;
			case 攻城戦ヘックス役割::森:
				デバッグ出力 << "森";
				break;
			case 攻城戦ヘックス役割::櫓:
				デバッグ出力 << "櫓";
				break;
			case 攻城戦ヘックス役割::閉門:
				デバッグ出力 << "閉まっている門";
				break;
			case 攻城戦ヘックス役割::開門:
				デバッグ出力 << "開いている門";
				break;
			case 攻城戦ヘックス役割::枠外: // 左右の境界線など、ヘックスとしては存在するが、ユニットなど決して到達できない枠外というチップもある
				デバッグ出力 << "枠外";
				break;
			case -1: // 指定した「位置」が不適切で、枠外をさらに飛び越えて、そもそも対応するヘックス自体ないような場所を指し示している
				デバッグ出力 << "マップ外";
				break;
			}

			デバッグ出力 << "です" << endl;
		}
	}
}

攻城戦のヘックスが炎上しているかどうか

int Get_攻城戦ヘックス炎上(double X位置, double Y位置);
int Get_攻城戦ヘックス炎上(ヘックス位置型 P)

ほとんどの場合は、武将が居る位置が炎上しているかどうかを調べるような目的で利用することになるでしょう。

void カスタム::On_ターン変更《攻城中画面》(int ターン数) {
	if (Is_攻城戦中())
	{
		// 今表示中の武将達
		auto blist = Get_出陣中の武将番号リスト【配列用】《表示中マップ》();

		for (int iBushouID : blist) {
			ヘックス位置型 pos = Get_武将の攻城戦ヘックス位置(iBushouID);
			int 炎上状態 = Get_攻城戦ヘックス炎上(pos);

			if (炎上状態 == 攻城戦ヘックス炎上::炎上) {
				デバッグ出力 << Get_名字(iBushouID) + Get_名前(iBushouID) << "は炎上しています" << endl;
			}
			else if (炎上状態 == 攻城戦ヘックス炎上::無し) { // 通常は普通に else とだけ書けばよい。今回は定義があるということを示すためにわざと記述
				デバッグ出力 << Get_名字(iBushouID) + Get_名前(iBushouID) << "は炎上してません" << endl;
			}
		}
	}
}

ヘックスベクトル型

単純に「ヘックス位置A」から「ヘックス位置B」の方向を求めることがあると考えられるため、定義されています。

void カスタム::On_残りターン変更《戦争画面》(int 残りターン数) {
	if (Is_野戦中())
	{
		// 今表示中の武将達
		auto blist = Get_出陣中の武将番号リスト【配列用】《表示中マップ》();

		// 2人以上いるかチェック
		if (blist.size() >= 2) {
			// 最初の2人の位置
			int iBushouID_A = blist[0];
			int iBushouID_B = blist[1];
			ヘックス位置型 posA = Get_武将の野戦ヘックス位置(iBushouID_A);
			ヘックス位置型 posB = Get_武将の野戦ヘックス位置(iBushouID_B);

			ヘックスベクトル型 vec_hex = posB - posA;
			2Dベクトル型 vec_2d = { vec_hex.X, vec_hex.Y };

			string 姓名A = Get_名字(iBushouID_A) + Get_名前(iBushouID_A);
			string 姓名B = Get_名字(iBushouID_B) + Get_名前(iBushouID_B);


			デバッグ出力 << 姓名A << "→" << 姓名B << "への方向ベクトルは..." << endl;
			デバッグ出力 << "(" << vec_hex.X << "," << vec_hex.Y << ")" << endl;
			デバッグ出力 << "即ち、" << 姓名B << "は、" << 姓名A << "の" << Get_8方位文字列(vec_2d) << "に居ます" << endl;
		}
	}
}
		

野戦のヘックス位置を3D座標へ

ゲームは2Dですが、関連させる座標が3Dといったことは十分ありえます。
例えば、ScenarioModには、効果音を「3D」空間上で再生する機能が提供されています。

このように野戦のヘックスマップと3Dとを変換しやすいように関数が提供されています。

3D位置型 野戦ヘックス位置→3D位置(ヘックス位置型 P)
ヘックス位置型 3D位置→野戦ヘックス位置(3D位置型 P)

こちらの「3D位置→野戦ヘックス位置」については原則あまり使うことはないでしょうが、
「3D位置型 野戦ヘックス位置→3D位置(ヘックス位置型 P)」で座標を変換したものを元へと戻せる、という特性があります。

戦場に上杉謙信が居れば、謙信ユニットの野戦での「位置」に応じて、スピーカーから音が聞こえてくる。
彼が左奥に居れば左奥から、彼が右手前にいれば右手前から音が聞こえる。
void カスタム::On_残りターン変更《戦争画面》(int 残りターン数) {
	if (Is_野戦中())
	{
		// 今表示中の武将達のヘックス位置を表示する。
		auto blist = Get_出陣中の武将番号リスト【配列用】《表示中マップ》();

		for (int iBushouID : blist) {
			if (p武将戸籍情報[iBushouID].顔番号 == 顔番号::長尾景虎) {
				ヘックス位置型 pos = Get_武将の野戦ヘックス位置(iBushouID);

				3D位置型 pos_3D = 野戦ヘックス位置→3D位置(pos);
				3D効果音再生(効果音音源::壁越え, "", false, pos_3D);
				デバッグ出力 << "効果音を鳴らしました(" << pos_3D.X << "," << pos_3D.Y << "," << pos_3D.Z << endl;
			}
		}
	}
}		

攻城戦のヘックス位置を3D座標へ

野戦と同様です。

3D位置型 攻城戦ヘックス位置→3D位置(ヘックス位置型 P)
ヘックス位置型 3D位置→攻城戦ヘックス位置(3D位置型 P)

こちらの「3D位置→攻城戦ヘックス位置」については原則あまり使うことはないでしょうが、
「3D位置型 攻城戦ヘックス位置→3D位置(ヘックス位置型 P)」で座標を変換したものを元へと戻せる、という特性があります。

戦場に上杉謙信が居れば、謙信ユニットの攻城戦での「位置」に応じて、スピーカーから音が聞こえてくる。
彼が左奥に居れば左奥から、彼が右手前にいれば右手前から音が聞こえる。
void カスタム::On_ターン変更《攻城中画面》(int ターン数) {
	if (Is_攻城戦中())
	{
		// 今表示中の武将達のヘックス位置を表示する。
		auto blist = Get_出陣中の武将番号リスト【配列用】《表示中マップ》();

		for (int iBushouID : blist) {
			if (p武将戸籍情報[iBushouID].顔番号 == 顔番号::長尾景虎) {
				ヘックス位置型 pos = Get_武将の攻城戦ヘックス位置(iBushouID);

				3D位置型 pos_3D = 攻城戦ヘックス位置→3D位置(pos);
				3D効果音再生(効果音音源::壁越え, "", false, pos_3D);
				デバッグ出力 << "効果音を鳴らしました(" << pos_3D.X << "," << pos_3D.Y << "," << pos_3D.Z << endl;
			}
		}
	}
}
	

より詳細を知るには...

「戦争 - ヘックス位置」に関する主な所は以上となります。 詳しくは「戦場情報型.h」「戦場情報列挙.h」などを参照してください。