//************************************************************************************
// 簡易文字RPG第二版
// 2007年8月6日 下午 12:25
// by yag
//************************************************************************************
#include <iostream>
#include <ctime>	// 跟#include <time.h>不同的地方在於，有標出.h會在系統預設資料夾中找尋同樣的檔名
					// 而沒有標出.h的，代表這是ANSI C++標準的標頭檔，編譯器會自動找尋「相對應」的檔案(也就是檔名依編譯器不同而有所不同)
#include <string>	// 為了使用getline()函式

using namespace std;

const int XMAX = 2;		// 遊戲世界的寬度(場景數目)
const int YMAX = 2;		// 遊戲世界的長度(場景數目)

void Init( string MapDesc[][YMAX + 1] );	// 初始化遊戲世界的各場景說明以及給予隨機函數一個種子
void InputCmd( int &X, int &Y );	// 讀取輸入並處理
void Desc( const string MapDesc[XMAX + 1][YMAX + 1], int X, int Y );	// 顯示目前所在場景的說明、此場景之出口以及主角身上金幣數量
void CoinOrDingDing();		// 決定進入新場景時是遇到金幣還是丁丁
void fight();	// 跟丁丁戰鬥的處理
void flee();	// 遇到丁丁時選擇溜走的處理
void drop();	// 遇到丁丁時將身上的金幣全部交出的處理
void get( string things = "coins" );	// 撿起東西，預設為撿金幣

long long MyMoney = 0;		// 主角身上的金幣數量，一開始是 0 枚
short status = 0;	// 主角在場景中的狀況。 0：無事 1：發現金幣 2：遇到丁丁 99：遊戲結束
int moneyOnGround = 0;	// 隨機出現在地上的金幣數量

int main()
{
	string MapDesc[XMAX + 1][YMAX + 1];		// 儲存遊戲世界各場景的說明，預設最左下角為(0, 0)
	int X = 1, Y = 1;	// 主角所在位置，預設為(1, 1)
	char death = NULL;	// 判斷是否結束遊戲的旗標

	Init( MapDesc );
	
	while( 1 )
	{
		fflush( stdin );	// 消除下面cin >> death;時，使用者輸入的過多資訊

		Desc( MapDesc, X, Y );
		InputCmd( X, Y );		// 因為主角剛出現時要預設為無事狀態，所以先在迴圈外呼叫一次

		while( status != 99 )	// 遊戲未結束前
		{
			Desc( MapDesc, X, Y );		// 顯示場景說明
			CoinOrDingDing();	// 顯示出特殊情況說明
			InputCmd( X, Y );	// 等待接收輸入
		}

ASK:
		cout << "你已經死了！三途之川的船在前方等著你，要上船嗎？(y/n)" << endl;
		cin >> death;

		if( death == 'y' || death == 'Y' )
		{
			cout << "擺渡人拉拉：你好！你好！" << endl;
			cout << "擺渡人拉拉：抱抱！" << endl;
			cout << "擺渡人拉拉：說．再．見！" << endl;
			cout << "擺渡人拉拉：再見！" << endl;
			system( "pause" );
			break;	// 跳離while(1)，遊戲結束
		}
		else if( death == 'n' || death == 'N' )
		{
			cout << "Yag大神以巫師的神力使你原地復活！" << endl;	// 原地復活，所以X跟Y不變
			cout << "你心懷感激地將身上的金幣全部交出當成治療費。" << endl;
			MyMoney = 0;	// 將錢交出
			system( "pause" );
			system( "cls" );
			status = 0;	// 因為復活，將狀態改回正常狀態
		}
		else
			goto ASK;	// 隨便輸入就重新發問
	}

	return 0;
}

void Init( string MapDesc[][YMAX + 1] )		// 各場景說明請在此修改，預設最左下角為世界座標的起點，往右則增加X座標，往上則增加Y座標
{
	MapDesc[0][2] = "這裡是左上角的房間。";
	MapDesc[1][2] = "這裡是正上方的房間。";
	MapDesc[2][2] = "這裡是右上角的房間。";
	MapDesc[0][1] = "這裡是左邊的房間。";
	MapDesc[1][1] = "這裡是中間的房間。";
	MapDesc[2][1] = "這裡是右邊的房間。";
	MapDesc[0][0] = "這裡是左下角的房間。";
	MapDesc[1][0] = "這裡是正下方的房間。";
	MapDesc[2][0] = "這裡是右下角的房間。";

	srand( (unsigned) time( NULL ) );
}

void Desc( const string MapDesc[XMAX + 1][YMAX + 1], int X, int Y )		// 場景說明
{
	cout << MapDesc[X][Y].c_str() << endl;	// c_str()會將string類別裡存的字串變成傳統字串

	// 以下依地圖邊界判斷有何出口*************************
	cout << "此地的出口有( ";
	if( X < XMAX )
		cout << "e：東方 ";
	if( X > 0 )
		cout << "w：西方 ";
	if( Y > 0 )
		cout << "s：南方 ";
	if( Y < YMAX )
		cout << "n：北方 ";
	cout << ")" << endl;
	// ***************************************************

	cout << "你目前身上有 " << MyMoney << " 枚金幣。" << endl << endl;
}

void InputCmd( int &X, int &Y )
{
	string Move;
	bool Flag = true;	// 輸入迴圈判斷，只有當移動了場景，才會跳出輸入迴圈
						// 在同一個場景內的fight、flee、drop、get coins都會導致迴圈持續
	unsigned int i = 0;

	while( Flag )
	{
		cout << "接下來該做什麼好呢？" << endl;
		getline( cin, Move );	// 要用string類別從cin中讀取字串就要使用getline()函式
								// 要使用getline()函式就必須#include <string>

		Flag = false;	// 預設輸入過一次後就會離開迴圈

		// 以下的if跟一堆else if中，雖然都是以Move在判斷，但因Move的值無法轉型為單一整數型態，
		// 所以無法使用switch來做判斷
		if( Move == "e" )	// 往東走
		{
			if( X == XMAX && status != 2 )	// 判斷邊界以及是否戰鬥中
			{
				cout << endl;
				cout << "已到了盡頭！" << endl;
				Flag = true;	// 若是輸入不符合離開場景的條件，則將繼續迴圈
			}
			else if( status == 2 )	// 戰鬥中不可離開場景
			{
				cout << endl;
				cout << "丁丁擋住了你的路！" << endl;
				Flag = true;
			}
			else
				X++;	// 向右移動一格
		}
		else if( Move == "w" )	// 往西走
		{
			if( X == 0 && status != 2 )
			{
				cout << endl;
				cout << "已到了盡頭！" << endl;
				Flag = true;
			}
			else if( status == 2 )
			{
				cout << endl;
				cout << "丁丁擋住了你的路！" << endl;
				Flag = true;
			}
			else
				X--;
		}
		else if( Move == "s" )	// 往南走
		{
			if( Y == 0 && status != 2 )
			{
				cout << endl;
				cout << "已到了盡頭！" << endl;
				Flag = true;
			}
			else if( status == 2 )
			{
				cout << endl;
				cout << "丁丁擋住了你的路！" << endl;
				Flag = true;
			}
			else
				Y--;
		}
		else if( Move == "n" )	// 往北走
		{
			if( Y == YMAX && status != 2 )
			{
				cout << endl;
				cout << "已到了盡頭！" << endl;
				Flag = true;
			}
			else if( status == 2 )
			{
				cout << endl;
				cout << "丁丁擋住了你的路！" << endl;
				Flag = true;
			}
			else
				Y++;
		}
		else if( Move.substr( 0, 4 ) == "get " )	// 撿東西
		{
			for( i = 4; i < Move.length(); i++ )	// 去掉前面的多餘空白
			{
				if( Move.substr( i, 1 ).compare( " " ) != 0 )
					break;
			}
			if( i == Move.length() )	// 除了一堆空白外沒別的東西
				get();
			else
				get( Move.substr( i, Move.find_last_not_of( " " ) - i + 1 ) );	// 去掉後面的多餘空白，傳入get()

			Flag = true;	// 因為撿東西並沒有離開場景，所以繼續迴圈
		}
		else if( Move.substr( 0, 2 ) == "g " )		// 撿東西
		{
			for( i = 2; i < Move.length(); i++ )	// 去掉前面的多餘空白
			{
				if( Move.substr( i, 1 ).compare( " " ) != 0 )
					break;
			}
			if( i == Move.length() )	// 除了一堆空白外沒別的東西
				get();
			else
				get( Move.substr( i, Move.find_last_not_of( " " ) - i + 1 ) );	// 去掉後面的多餘空白，傳入get()

			Flag = true;	// 因為撿東西並沒有離開場景，所以繼續迴圈
		}
		else if( Move == "get" || Move == "g" )		// 撿東西
		{
			get();
			Flag = true;	// 因為撿錢並沒有離開場景，所以繼續迴圈
		}
		else if( Move == "fight" )	// 戰鬥
		{
			if( status == 2 )	// 丁丁出現才能戰鬥
				fight();
			else
				cout << "你想要跟誰戰鬥？" << endl;

			if( status != 99 )	// 如果戰鬥後沒有死亡，迴圈繼續，否則遊戲結束，離開迴圈
				Flag = true;
		}
		else if( Move == "flee" )	// 逃跑
		{
			if( status == 2 )	// 丁丁出現才能逃跑
				flee();
			else
				cout << "房間裡有小強嗎？跑那麼快做什麼？" << endl;

			Flag = true;	// 無論逃跑成功與否，迴圈都繼續
		}
		else if( Move == "drop" )	// 將金幣交出
		{
			if( status == 2 )	// 丁丁出現才能輸入此指令
				drop();
			else
				cout << "你傻了嗎？沒事把金幣丟到地上做什麼？" << endl;

			if( status != 99 )	// 如果將金幣交出後沒有死亡，迴圈繼續，否則遊戲結束，離開迴圈
				Flag = true;
		}
		else if( Move == "quit" )	// 離開遊戲
			status = 99;	// 將狀態改成遊戲結束
		else	// 只有以上是合法指令，其餘都顯示錯誤訊息並繼續迴圈
		{
			cout << "錯誤的輸入！" << endl;
			Flag = true;
		}
	}

	system( "cls" );	// 唯有離開了場景才會將螢幕清空
}

void CoinOrDingDing()	// 隨機產生突發事件
{
	status = rand() % 3;	// 無事、發現金幣、丁丁出現的機率各3分之1

	switch( status )
	{
	case 0:		// 無事
		break;
	case 1:		// 發現金幣
		moneyOnGround = rand() % 500 + 1;	// 金幣的數量從1到500
		cout << "在地上發現了金幣 " << moneyOnGround << " 枚，要撿(get coins)嗎？" << endl << endl;		// 提示撿錢指令
		break;
	case 2:		// 丁丁出現
		cout << "丁丁！人才丁丁出現了！丁丁拿起雷射光刀向你發起攻擊！！！" << endl;
		cout << "丁丁：丁丁是丁丁！丁丁是棒棒棒！丁丁是丁丁！雷此渡了虧機霍！！" << endl;
		cout << "你可以選擇戰鬥(fight)、逃走(flee)或將金幣全部交出(drop)。" << endl << endl;	// 提示戰鬥中可選擇之指令
		break;
	}
}

void fight()
{
	short WinOrLose = rand() % 6;	// 有6種戰鬥的可能結果，其中3種是勝利，3種是失敗
	short money = rand() % 500 + 1;		// 殺死丁丁會隨機得到1~500枚金幣
	
	cout << "你向丁丁發起了攻擊！！" << endl;

	switch( WinOrLose )
	{
	case 0:		// 此為勝利情況1
		cout << "丁丁將雷射光刀向你扔過來！！" << endl;
		system( "pause" );
		cout << "你閃過了雷射光刀！！" << endl;
		system( "pause" );
		cout << "丁丁手無寸鐵！！你幹掉了丁丁！！" << endl;
		system( "pause" );
		cout << "你掠奪了丁丁的屍體，得到了 " << money << " 枚金幣。" << endl;
		MyMoney += money;
		cout << endl;
		cout << "身上金幣共有 " << MyMoney << " 枚" << endl;
		status = 0;		// 打贏了就將狀態改回無事
		break;
	case 1:		// 此為勝利情況2
		cout << "丁丁突發性腦殘！將雷射光刀送給了你！！" << endl;
		system( "pause" );
		cout << "你使用丁丁的雷射光刀幹掉了丁丁！！" << endl;
		system( "pause" );
		cout << "你掠奪了丁丁的屍體，得到了 " << money << " 枚金幣。" << endl;
		MyMoney += money;
		cout << endl;
		cout << "身上金幣共有 " << MyMoney << " 枚" << endl;
		status = 0;
		break;
	case 2:		// 此為勝利情況3
		cout << "丁丁舉起雷射光刀向你衝了過來！" << endl;
		system( "pause" );
		cout << "你輕巧地閃開了丁丁的攻擊！" << endl;
		system( "pause" );
		cout << "丁丁不小心踢到石頭跌倒了！！丁丁的雷射光刀刺中了自己！！" << endl;
		system( "pause" );
		cout << "丁丁死了！" << endl;
		system( "pause" );
		cout << "你掠奪了丁丁的屍體，得到了 " << money << " 枚金幣。" << endl;
		MyMoney += money;
		cout << endl;
		cout << "身上金幣共有 " << MyMoney << " 枚" << endl;
		status = 0;
		break;
	case 3:		// 此為失敗情況1
		cout << "丁丁將雷射光刀向你扔過來！！" << endl;
		system( "pause" );
		cout << "你被丁丁的雷射光刀射中了！！" << endl;
		system( "pause" );
		cout << "你掛了！Game Over！" << endl;
		system( "pause" );
		status = 99;	// 死亡則將遊戲狀態改為遊戲結束
		break;
	case 4:		// 此為失敗情況2
		cout << "丁丁突發性腦殘！將雷射光刀送給了你！！" << endl;
		system( "pause" );
		cout << "你舉起丁丁的雷射光刀往丁丁頭上砍去！！" << endl;
		system( "pause" );
		cout << "丁丁空手入白刃！！將雷射光刀搶了回去！！" << endl;
		system( "pause" );
		cout << "你手無寸鐵，被丁丁用雷射光刀砍死了！" << endl;
		system( "pause" );
		cout << "你掛了！Game Over！" << endl;
		system( "pause" );
		status = 99;
		break;
	case 5:		// 此為失敗情況3
		cout << "丁丁舉起雷射光刀向你衝了過來！" << endl;
		system( "pause" );
		cout << "丁丁不小心踢到石頭跌倒了！！手中雷射光刀飛射而出！" << endl;
		system( "pause" );
		cout << "你被丁丁甩出來的雷射光刀刺中了！！" << endl;
		system( "pause" );
		cout << "你掛了！Game Over！" << endl;
		system( "pause" );
		status = 99;
		break;
	}
}

void flee()
{
	int SuccessOrFail = rand() % 2;		// 逃跑成功與失敗機率各2分之1

	switch( SuccessOrFail )
	{
	case 0:
		cout << "你成功地逃跑了！" << endl;
		status = 0;		// 成功跑掉則將狀態改回無事
		break;
	case 1:		// 逃跑失敗則狀態不變，依然在戰鬥中，並且會失去身上10分之1的金幣
		cout << "你逃跑失敗了！忙亂中掉了 " << MyMoney / 10 << " 枚金幣！" << endl;
		MyMoney -= MyMoney / 10;
		cout << "身上剩下 " << MyMoney << " 枚金幣" << endl;
		break;
	}
}

void drop()
{
	int LiveOrDie = rand() % 10;	// 將金幣全部交出後依然有10分之1的機率被殺死

	cout << "你將身上的金幣全部交出！" << endl;
	MyMoney = 0;

	if( LiveOrDie == 0 )	// 不一定要是0，0~9中任選一個數字都行
	{
		cout << "丁丁對你的總財產感到不滿意，隨手用雷射光刀砍死了你！！" << endl;
		system( "pause" );
		cout << "你掛了！Game Over！" << endl;
		system( "pause" );
		status = 99;	// 死了就記得改成遊戲結束狀態
	}
	else
	{
		cout << "丁丁心情不錯放了你一馬。" << endl;
		system( "pause" );
		status = 0;		// 沒事就記得把狀態改回0
	}
}

void get( string things )	// 參數不可寫為string things = "coins"，因為函數原型中已有定義過了，會造成重複定義
{
	if( things == "coins" )
	{
		if( status == 1 )	// 如果地上有金幣
		{
			MyMoney += moneyOnGround;
			cout << endl;
			cout << "你撿起了 " << moneyOnGround << " 枚金幣，";
			cout << "身上金幣共有 " << MyMoney << " 枚" << endl;
			moneyOnGround = 0;
			status = 0;		// 撿起來了記得把狀態改回來
		}
		else
		{
			cout << "地上並沒有任何東西。" << endl;
		}
	}
	else
	{
		cout << "你想要撿起什麼？" << endl;
	}
}