⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dgview.cpp

📁 很经典的用C++编的空当接龙的程序
💻 CPP
字号:
// DGView.cpp : implementation file
//

#include "stdafx.h"
#include "JL.h"
#include "JLDoc.h"
#include "JLView.h"
#include "DGView.h"
#include "DlgDefGame.h"
//用于支持洗牌的函数
#include <algorithm>
#include <functional>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CDGWnd
extern CJLDoc * AfxGetDocument();
extern CJLView * AfxGetView();

IMPLEMENT_DYNCREATE(CDGWnd, CWnd)

CDGWnd::CDGWnd()
{
	Init();
}

CDGWnd::~CDGWnd()
{
}


BEGIN_MESSAGE_MAP(CDGWnd, CWnd)
	//{{AFX_MSG_MAP(CDGWnd)
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONDBLCLK()
	ON_WM_ERASEBKGND()
	ON_WM_PAINT()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CDGWnd diagnostics

#ifdef _DEBUG
void CDGWnd::AssertValid() const
{
	CWnd::AssertValid();
}

void CDGWnd::Dump(CDumpContext& dc) const
{
	CWnd::Dump(dc);
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CDGWnd drawing

void CDGWnd::OnPaint()
{
	CPaintDC dc(this);
	CJLView *pView = AfxGetView();
	//创建内存DC及位图
	CDC dcMem;
	dcMem.CreateCompatibleDC(&dc);
	CBitmap bmp;
	bmp.CreateCompatibleBitmap(&dc,CARD_WID,CARD_HEI);
	//绘制牌盒子
	CBitmap *pOldBmp = dcMem.SelectObject(&bmp);
	//如果此局已编辑完整就在牌盒子的区域中显示提示
	if(GameIsValid()) {
		CFont font;
		font.CreatePointFont(20*10,"Arial",&dc);
		CFont *prevFont = dc.SelectObject(&font);
		int prevMode = dc.SetBkMode(TRANSPARENT);
		
		dc.DrawText("此局编辑完毕,请存档!",RectOfBox(),DT_CENTER);
		dc.SetBkMode(prevMode);
		dc.SelectObject(prevFont);
	}
	for ( UINT i = 52; i >= 1; --i ) {
		CRect r = RectOf(i); 
		UINT card = Get(i);
		if(!card) continue;
		//在内存DC中绘制此牌并贴到窗口中此牌的矩形位置上
		pView->DrawCard(CPoint(0,0),card,&dcMem); 
		dc.BitBlt(r.left,r.top,r.Width(),r.Height(),&dcMem,0,0,SRCCOPY);
		if(m_iSrcCol == i) { 
			dc.InvertRect(r);        //如果是选中状态则反色
		}
	}
	dcMem.SelectObject(pOldBmp);
	//绘制桌面
	CBrush *pOldBrush = dc.SelectObject(&pView->m_brushBkgnd);
	//共八列牌,前四列每列七张,后四列每列六张,共计52张
	for(i = 1; i<= 8; i++) {
		UINT m = (i <= 4 ? 7 : 6);
		for(UINT j = 1; j <= m; j++) {
			int idx = (i<<8)+j , card = Get(idx);
			CRect r = RectOf(idx);
			//如果无牌就画空框
			if(!card) {
				if(j < m) r.bottom += 5;
				dc.RoundRect(r,CPoint(5,5));

			}
			//否则绘制此牌 
			else {
				pView->DrawCard(r.TopLeft(),card,&dc);
				if(m_iSrcCol == idx) dc.InvertRect(r);
			}
		}
	}
	dc.SelectObject(pOldBrush);
}

/////////////////////////////////////////////////////////////////////////////
// CDGWnd message handlers
//双击桌面上的牌将会把该牌回收到牌盒子中
void CDGWnd::OnLButtonDblClk(UINT nFlags, CPoint point) 
{
	//测试双击了哪张牌
	UINT hit = HitTest(point);
	//如果双击了桌面上的牌
	if(hit && !IS_CARD(hit) && Get(hit)) { 
		if(m_iSrcCol) { //选中某牌
			CRect rBack = RectOf(m_iSrcCol);//当前选中的列的矩形的备份
			m_iSrcCol = 0;//取消选中
			InvalidateRect(rBack);
		}
		UINT card = Get(hit);
		Set(hit,0);
		InvalidateRect(RectOf(hit));
		Set(card,card);//回收此列
		InvalidateRect(RectOf(card));
		m_iSrcCol = 0;
		m_bModified = true;
		SetTitle();
	}

	CheckGame();

	CWnd::OnLButtonDblClk(nFlags, point);
}

void CDGWnd::OnLButtonDown(UINT nFlags, CPoint point) 
{
	// TODO: Add your message handler code here and/or call default
	UINT hit = HitTest(point);
	if(!hit) {
		;
	}
	//无源牌
	else if(!m_iSrcCol) { 
		if(Get(hit)) { //击中牌
			m_iSrcCol = hit;//选中此牌
			InvalidateRect(RectOf(hit));
		}
	} 
	//有源牌
	else if(!Get(hit)) {
		//击中空桌 
		if(!IS_CARD(hit)) {
			//移入空桌
			UINT card = Get(m_iSrcCol);
			Set(m_iSrcCol,0);
			InvalidateRect(RectOf(m_iSrcCol));
			Set(hit,card);
			InvalidateRect(RectOf(hit));
			
			m_iSrcCol = 0;
			m_bModified = true;

			SetTitle();
		}
		//击中空盒子,且源在桌面上
		else if(!IS_CARD(m_iSrcCol)) {
			UINT card = Get(m_iSrcCol);
			Set(m_iSrcCol,0);
			InvalidateRect(RectOf(m_iSrcCol));
			Set(card,card);//回收此列
			InvalidateRect(RectOf(card));
			
			m_iSrcCol = 0;
			m_bModified = true;

			SetTitle();
		}
	}
	//击中牌
	else { 
		CRect rBack = RectOf(m_iSrcCol);//当前选中的列的矩形的备份
		//击中源牌
		if(hit == m_iSrcCol) {
			m_iSrcCol = 0;//取消选中
		} else {
			m_iSrcCol = hit;//选中击中的牌
			InvalidateRect(RectOf(hit));
		}
		InvalidateRect(rBack);
	}

	CheckGame();

	CWnd::OnLButtonDown(nFlags, point);
}
//击中测试,看看点击了哪张牌
UINT CDGWnd::HitTest(const CPoint &pt)
{
	//测试是否在牌盒子的某牌上点击
	for(UINT i = 1; i<= 52; i++) {
		CRect r = RectOf(i);
		if(r.PtInRect(pt)) return i;
	}
	//测试是否在桌面上放牌区点击
	for(i = 1; i<= 8; i++) {
		UINT x = i <= 4 ? 7 : 6;//索引上限
		for(UINT j = 1; j<= x; j++) {
			UINT idx = (i<<8)+j;
			CRect r = RectOf(idx);
			if(r.PtInRect(pt)) return idx;
		}
	}
	return 0;
}
//计算指定的牌占据的矩形位置
CRect CDGWnd::RectOf(UINT pos)
{
	CRect cr;
	GetClientRect(cr);
	int wid = cr.Width()/13;
	int hei = (cr.Height()-CARD_HEI)/(6+4+1);
	//牌盒子中的牌每张所在的矩形
	CRect r(0,0,0,0);
	if(IS_CARD(pos)) { 
		r.left   = (13-NUM(pos))*wid;
		r.right  = r.left + wid;
		r.top    = TYPE(pos)*hei;
		r.bottom = r.top + hei;
	}
	//桌面上每张牌所占据的矩形
	else {
		UINT col = pos>>8;
		UINT idx = pos&0x000000FF;
		ASSERT(col<=8&&col>=1 && idx>=1);
		ASSERT(col<=4&&idx<=7 || col>=4&&idx<=6);

		CPoint p(0,hei*5);
		int inter = (cr.Width()-8*CARD_WID)/9;
		int x = (col - 1) * (inter+CARD_WID) + inter;
		int y = (4 + idx) * hei;
		r.SetRect(
			x,
			y,
			x + CARD_WID,
			y + ((col<=4&&idx==7 || col>4&&idx==6) ? CARD_HEI : hei)
			);
	}
	return r;
}
//在指定的位置存放给定的牌
void CDGWnd::Set(UINT pos,UINT card)
{
	//如果位置是在牌盒子中
	if(IS_CARD(pos)) {
		m_iBox[pos-1] = card;
	}
	else {
		UINT col = pos>>8;
		UINT idx = pos&0x000000FF;
		ASSERT(col<=8&&col>=1 && idx>=1);
		ASSERT(col<=4&&idx<=7 || col>=4&&idx<=6);

		if(col<=4) {
			m_iTbl[(col-1)*7+idx-1] = card;
		} else {
			m_iTbl[28 + (col-5)*6 + idx-1] = card ;
		}
	}
}

//根据给定的索引位置取出此处的牌值
UINT CDGWnd::Get(UINT pos)
{
	//如果是牌盒子中的牌
	if(IS_CARD(pos)) {
		return m_iBox[pos-1];
	}
	//否则就是桌面上的牌
	else {
		UINT col = pos>>8;
		UINT idx = pos&0x000000FF;
		ASSERT(col<=8&&col>=1 && idx>=1);
		ASSERT(col<=4&&idx<=7 || col>=4&&idx<=6);

		if(col<=4) {
			return m_iTbl[(col-1)*7+idx-1];
		} else {
			return m_iTbl[28 + (col-5)*6 + idx-1];
		}
	}
}

BOOL CDGWnd::OnEraseBkgnd(CDC* pDC) 
{
	CRect cr;
	GetClientRect(cr);
	pDC->FillRect(cr,&AfxGetView()->m_brushBkgnd);
	return 1;
}


void CDGWnd::NewDefGame()
{
	if(!GiveUp()) return;

	Init();
	InvalidateRect(NULL);
	SetTitle();
}

void CDGWnd::SaveDefGameAs()
{
	CString name = m_strFile;
	bool bModi = m_bModified;

	m_strFile.Empty();
	m_bModified = true;

	SaveDefGame();

	if(m_strFile.IsEmpty()) {
		m_strFile = name;
		m_bModified = bModi;
	}
}

void CDGWnd::SaveDefGame()
{
	struct SIZE_INF { UINT size, *pAddr; } cols[3] = {
		{ sizeof(m_iCards  ) / sizeof(UINT) , &m_iCards[0][0]   },
		{ sizeof(m_iBuffer ) / sizeof(UINT) , &m_iBuffer[0]     },
		{ sizeof(m_iRecycle) / sizeof(UINT) , &m_iRecycle[0][0] }
	};
	if(!m_bModified) return;

	if(m_strFile.IsEmpty()) {
		CFileDialog dlg(FALSE,"rep","自定义牌局",dwFlags,filter);
		if(dlg.DoModal() == IDCANCEL) return;
		m_strFile = dlg.GetPathName();
	
		SetTitle();
	}
	CFile file(m_strFile,modeCrWr);
	CArchive ar(&file,CArchive::store);
	if(GameIsValid()) {
		////////////////////////////////////////////////////////////
		//完整的自定义牌局,其牌局代号为0,
		//保存时存为一个完整的牌局存档文件
		////////////////////////////////////////////////////////////
		Shuffle();//洗牌
		m_nCurGameNumber = 0;
		ar << m_nCurGameNumber;//保存本局代号
		CObList ops;
		ops.Serialize(ar);//保存空的步骤记录
		for(UINT k = 0; k < 3 ; ++k)//保存牌局
			for(UINT i = 0; i < cols[k].size; i++)
				ar << cols[k].pAddr[i];
	} else { 
		////////////////////////////////////////////////////////////
		//不完整的自定义牌局,其牌局代号为-1,
		//保存时只保存当前编辑时有用的信息,便于下次再编辑
		////////////////////////////////////////////////////////////
		m_nCurGameNumber = -1;
		ar << m_nCurGameNumber;//保存本局代号
		for(UINT i = 0; i < 52; i++) { ar << m_iTbl[i]; }//保存桌面
		for(     i = 0; i < 52; i++) { ar << m_iBox[i]; }//保存牌盒子
	}
	ar.Close();
	file.Close();

	m_bModified = false;

	SetTitle();
}
//读档,根据牌局代号决定如何读档:
//如果牌局代号为0则说明是完整的自定义牌局,
//	此时根据步骤记录判断此局是否走过若干步,如果是则提示玩家先撤消到开局状态并存档后编辑,
//	否则就说明牌局是完整的且没有走过,那么就读出牌盒子和桌面;
//如果牌局代号大于0则说明是标准牌局的存档文件,
//	此时就重新生成一个原始牌局;
//如果牌局代号为-1则说明是未编辑完整的自定义牌局,
//	此时就读出牌盒子和桌面。
void CDGWnd::LoadDefGame()
{
	struct SIZE_INF { UINT size, *pAddr; } cols[3] = {
		{ sizeof(m_iCards  ) / sizeof(UINT) , &m_iCards[0][0]   },
		{ sizeof(m_iBuffer ) / sizeof(UINT) , &m_iBuffer[0]     },
		{ sizeof(m_iRecycle) / sizeof(UINT) , &m_iRecycle[0][0] }
	};

	if(!GiveUp()) return;

	CFileDialog dlg(TRUE,"rep",NULL,dwFlags,filter);
	if(dlg.DoModal() == IDCANCEL) return;

	Init();

	CFile file(dlg.GetPathName(),modeRead);
	CArchive ar(&file,CArchive::load);

	//读取本局代号
	ar>>m_nCurGameNumber;
	//如果是未编辑完成的牌局
	if(m_nCurGameNumber == -1) {
		for(UINT i = 0; i < 52; i++) { ar >> m_iTbl[i]; }//读取桌面
		for(     i = 0; i < 52; i++) { ar >> m_iBox[i]; }//读取牌盒子
	} 
	//如果是标准牌局的存档
	else if(m_nCurGameNumber > 0) {
		//准备一副新牌,并洗牌
		int cards[52];
		for(int i = 1; i <= 52; ++i) cards[i-1] = i;

		using namespace std;

		srand(m_nCurGameNumber >> 16);
		random_shuffle(cards, cards + 52);
		srand(m_nCurGameNumber & 0xFFFF);
		random_shuffle(cards, cards + 52);
	
		for(i = 0; i < 52; i++) { m_iTbl[i] = cards[i]; }//发牌到桌面
		for(i = 0; i < 52; i++) { m_iBox[i] = 0; }//准备空的牌盒子
	}
	//已经完成的自定义牌局
	else {
		CObList ops;
		ops.Serialize(ar); //读取步骤记录
		UINT nSteps = ops.GetCount();
		ClrOpsRecords(&ops);//清除步骤记录
		if(nSteps == 0) {
			for(UINT k = 0; k < 3 ; ++k) //读取牌局
				for(UINT i = 0; i < cols[k].size; i++)
					ar >> cols[k].pAddr[i];

			for(int col = 0; col < 4; col++)//准备牌局前4列
				for(int idx = 0; idx < 7; idx++)
					m_iTbl[col*7+idx] = m_iCards[col][idx];
			for(    col = 4; col < 8; col++)//准备牌局后4列
				for(int idx = 0; idx < 6; idx++) 
					m_iTbl[28+(col-4)*6+idx] = m_iCards[col][idx];

			for(UINT i = 0; i < 52; i++) { m_iBox[i] = 0; }//准备空的牌盒子
		}
		else {
			ar.Close();//关闭文件
			file.Close();
			CString errStr;
			errStr.LoadString(IDS_RESON_UNEDITABLE);
			AfxMessageBox(errStr);
			return;
		}
	}
	ar.Close();//关闭文件
	file.Close();

	m_strFile = dlg.GetPathName();
	m_bGameIsValid = GameIsValid();

	InvalidateRect(NULL);
	SetTitle();
}
//洗牌并发牌到桌面
void CDGWnd::Shuffle()
{
	//清空缓存列、回收列和牌列
	struct SIZE_INF { UINT size, *pAddr; };
	const SIZE_INF cols[3] = {
		{ sizeof(m_iCards  ) / sizeof(UINT) , &m_iCards[0][0]   },
		{ sizeof(m_iBuffer ) / sizeof(UINT) , &m_iBuffer[0]     },
		{ sizeof(m_iRecycle) / sizeof(UINT) , &m_iRecycle[0][0] },
	};
	for(UINT k = 0; k < 3 ; ++k)
		for(UINT i = 0; i < cols[k].size; i++)
			cols[k].pAddr[i] = 0;
	//发牌
	for(int col = 0; col <= 3; col++) {
		UINT *pTop;
		
		pTop = &m_iCards[col][0];
		for(UINT i=0;i<7;i++) {
			UINT *pDes = &pTop[pTop[19]];//指向底牌之下
			pDes[i] = m_iTbl[col*7 + i];
		}
		pTop[19] = 7;
		
		pTop = &m_iCards[col+4][0];
		for(i=0;i<6;i++) {
			UINT *pDes = &pTop[pTop[19]];//指向底牌之下
			pDes[i] = m_iTbl[28 + col*6 + i];
		}
		pTop[19] = 6;
	}
}

//////////////////////////////////////////////////////////////////////
//看看牌局是否编辑完全
//牌盒子中的牌全部拿到桌面上了,则说明编辑完成,牌局是完整的了
//////////////////////////////////////////////////////////////////////
bool CDGWnd::GameIsValid()
{
	for(UINT i = 0; i < 52; i++) {
		if(m_iBox[i]) return false;
	}
	return true;
}

//根据牌局是否修改过决定是否提示存档
BOOL CDGWnd::GiveUp()
{
	return !m_bModified || IDYES == AfxMessageBox(
		"牌局已经更改,尚未存档,要放弃它吗?", MB_YESNO);
}
//清除步骤记录
void CDGWnd::ClrOpsRecords(CObList *pList)
{
	POSITION p = pList->GetHeadPosition();
	while(p) {
		COperations *pOps = (COperations *)pList->GetNext(p);
		pOps->ClrOps();
		delete pOps;
	}
	pList->RemoveAll();
}

void CDGWnd::Init()
{
	m_bGameIsValid = false;
	m_strFile.Empty();
	m_iSrcCol   = 0;
	for(UINT i = 0; i < 52; i++) {
		m_iTbl[i] = 0;//桌面上无牌
		m_iBox[i] = i+1;//准备一副新牌放入牌盒子中
	}
	m_bModified = false;
}
//根据程序当前状态决定在标题栏中显示什么
//如果没有存档且已改动则在文件名后加上*号
void CDGWnd::SetTitle()
{
	CString title = "自定义牌局";
	const CString strModi = "*";

	if(!m_strFile.IsEmpty()) {
		int idx = m_strFile.ReverseFind('\\');
		title = m_strFile.Right(m_strFile.GetLength()-idx-1);
	}
	if(m_bModified) { 
		title += strModi;
	}
	CDlgDefGame *pDlg = (CDlgDefGame *)GetParent();
	pDlg->SetWindowText(title);
}

void CDGWnd::CheckGame()
{
	//根据当前牌面判断自定义牌局是否编辑完整
	bool isValidNow = GameIsValid();
	//如果当前状态和点击前的状态不同则刷新牌盒子
	//也就是说决定是否显示牌局是否编辑完整的提示信息
	if(m_bGameIsValid ^ isValidNow) {
		m_bGameIsValid = isValidNow;
		InvalidateRect(RectOfBox());
	}
}
//计算整个牌盒子的矩形
CRect CDGWnd::RectOfBox()
{
	CRect r;
	r.UnionRect(RectOf(1),RectOf(52));
	return r;
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -