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

📄 tcsgamedlg.cpp

📁 贪吃蛇游戏的原代码
💻 CPP
📖 第 1 页 / 共 2 页
字号:
// TcsGameDlg.cpp : implementation file
//
//BY 吴志刚 ewuzg@163.com,cn.fox@tom.com  2003//
#include "stdafx.h"
#include "TcsGame.h"
#include "TcsGameDlg.h"
#include "DlgVictory.h"

//Load Sound Lib//
#include <mmsystem.h>
#pragma comment(lib,"WINMM.LIB")
//==============================//
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
	CAboutDlg();

// Dialog Data
	//{{AFX_DATA(CAboutDlg)
	enum { IDD = IDD_ABOUTBOX };
	//}}AFX_DATA

	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CAboutDlg)
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
	//}}AFX_VIRTUAL

// Implementation
protected:
	//{{AFX_MSG(CAboutDlg)
	afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
	afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
	afx_msg void OnPaint();
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
	//{{AFX_DATA_INIT(CAboutDlg)
	//}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CAboutDlg)
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
	//{{AFX_MSG_MAP(CAboutDlg)
	ON_WM_CTLCOLOR()
	ON_WM_LBUTTONDOWN()
	ON_WM_PAINT()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CTcsGameDlg dialog

CTcsGameDlg::CTcsGameDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CTcsGameDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CTcsGameDlg)
		// NOTE: the ClassWizard will add member initialization here
	//}}AFX_DATA_INIT
	// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
	m_hIcon = AfxGetApp()->LoadIcon(IDI_ICOSNAKE);
}

void CTcsGameDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CTcsGameDlg)
		// NOTE: the ClassWizard will add DDX and DDV calls here
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CTcsGameDlg, CDialog)
	//{{AFX_MSG_MAP(CTcsGameDlg)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_WM_KEYDOWN()
	ON_WM_TIMER()
	ON_COMMAND(IDM_START, OnStart)
	ON_COMMAND(IDM_EXIT, OnExit)
	ON_COMMAND(IDM_ABOUT, OnAbout)
	ON_COMMAND(IDM_PAUSE, OnPause)
	ON_WM_CTLCOLOR()
	ON_COMMAND(IDM_SOUND, OnSound)
	ON_COMMAND(IDM_THROUGH, OnThrough)
	ON_COMMAND(IDM_STOP, OnStop)
	ON_WM_MOUSEMOVE()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CTcsGameDlg message handlers

BOOL CTcsGameDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// Add "About..." menu item to system menu.

	// IDM_ABOUTBOX must be in the system command range.
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		CString strAboutMenu;
		strAboutMenu.LoadString(IDS_ABOUTBOX);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon
	
	// TODO: Add extra initialization here
	srand(GetTickCount());
	////////////////////////init status bar//////////////////////////////
	m_wndStatusBar.Create(WS_CHILD|WS_VISIBLE|CCS_BOTTOM,CRect(0,0,0,0),this,102);
	int iPartDim[3]={80,370,-1};
	m_wndStatusBar.SetParts(2,iPartDim);
	m_wndStatusBar.SetText(_T("停止游戏"),0,0);
	m_wndStatusBar.SetText(_T("方向键控制方向,P暂停,F2新游戏"),1,0);
	///////////////////////////////////////////////////
	pNumBmp[0].LoadBitmap(IDB_BMPN0);
	pNumBmp[1].LoadBitmap(IDB_BMPN1);
	pNumBmp[2].LoadBitmap(IDB_BMPN2);
	pNumBmp[3].LoadBitmap(IDB_BMPN3);
	pNumBmp[4].LoadBitmap(IDB_BMPN4);
	pNumBmp[5].LoadBitmap(IDB_BMPN5);
	pNumBmp[6].LoadBitmap(IDB_BMPN6);
	pNumBmp[7].LoadBitmap(IDB_BMPN7);
	pNumBmp[8].LoadBitmap(IDB_BMPN8);
	pNumBmp[9].LoadBitmap(IDB_BMPN9);
	pNumBmp[10].LoadBitmap(IDB_BMPNBLACK);
	///////////////////////////////////////////////////
	ClearArray();
	m_nSnakeLen=0;
	srand(GetTickCount());
	m_nLenX=15;
	m_nLenY=20;
	m_nBlockLen=16;
	m_nLevel=0;
	m_nSnakeLife=5;
	m_nTotalScore=0;
	m_nTimeInterval=180;
	m_bEnableSound=FALSE;
	m_bCanThrough=FALSE;
	m_bSnakeDied=TRUE;
	m_nMaxLevel=10;
	m_nWaitType=WAIT_NONE;
	////////////////////////
	m_nInitX=5;
	m_nInitY=45;
	m_rcGame.SetRect(m_nInitX,m_nInitY,m_nInitX+m_nLenX*m_nBlockLen,m_nInitY+m_nLenY*m_nBlockLen);
	m_ptDisplay.x=m_rcGame.right+25;
	m_ptDisplay.y=m_rcGame.top;
	int nWx,nWy;
	nWx=m_rcGame.left+((m_rcGame.right-m_rcGame.left)-180)/2;
	nWy=m_rcGame.top+((m_rcGame.bottom-m_rcGame.top)-80)/2;
	m_rcWait.SetRect(nWx,nWy,nWx+180,nWy+80);
	///////////////////INIT TOOLBAR 256COLORS//////////////////////////////////
	if (!m_wndToolBar.CreateEx(this, TBSTYLE_BUTTON, WS_CHILD | WS_VISIBLE | CBRS_TOP
		| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
		!m_wndToolBar.LoadToolBar(IDR_TOOLBAR1))
	{
		TRACE0("Failed to create toolbar\n");
		return -1;      // fail to create
	}
	//设置ToolBar的图标列表
	m_ilTB.Create(32, 32, TRUE | ILC_COLOR8, 5, 0);
	HICON hIcon = NULL;

	hIcon = (HICON)::LoadImage(::AfxGetInstanceHandle(), 
		MAKEINTRESOURCE(IDI_ICONNEW), IMAGE_ICON, 32, 32, 0);
	m_ilTB.Add(hIcon);

	hIcon = (HICON)::LoadImage(::AfxGetInstanceHandle(), 
		MAKEINTRESOURCE(IDI_ICOPAUSE), IMAGE_ICON, 32, 32, 0);
	m_ilTB.Add(hIcon);

	hIcon = (HICON)::LoadImage(::AfxGetInstanceHandle(), 
		MAKEINTRESOURCE(IDI_ICONSTOP), IMAGE_ICON, 32, 32, 0);
	m_ilTB.Add(hIcon);

	hIcon = (HICON)::LoadImage(::AfxGetInstanceHandle(), 
		MAKEINTRESOURCE(IDI_ICOHELP), IMAGE_ICON, 32, 32, 0);
	m_ilTB.Add(hIcon);

	hIcon = (HICON)::LoadImage(::AfxGetInstanceHandle(), 
		MAKEINTRESOURCE(IDI_ICONQUIT), IMAGE_ICON, 32, 32, 0);
	m_ilTB.Add(hIcon);

	m_wndToolBar.GetToolBarCtrl().SetImageList(&m_ilTB);
	RepositionBars(AFX_IDW_CONTROLBAR_FIRST,AFX_IDW_CONTROLBAR_LAST,0);
	///////////////////////////////////////////////////////////////////////////
	return TRUE;  // return TRUE  unless you set the focus to a control
}

void CTcsGameDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialog::OnSysCommand(nID, lParam);
	}
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CTcsGameDlg::OnPaint() 
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		DrawGame();
		DrawARNumber(m_nLevel,   m_ptDisplay.x+6*13,m_ptDisplay.y+25);
		DrawARNumber(m_nSnakeLen,m_ptDisplay.x+6*13,m_ptDisplay.y+23+50);
		DrawARNumber(m_nTotalScore,m_ptDisplay.x+6*13,m_ptDisplay.y+46+75);
		DrawLifeCount(m_nSnakeLife,m_ptDisplay.x,m_ptDisplay.y+69+60+30);
		DrawInfoText();
		DrawWait(m_nWaitType,m_nWaitCount);
		CDialog::OnPaint();
	}
}

// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CTcsGameDlg::OnQueryDragIcon()
{
	return (HCURSOR) m_hIcon;
}

void CTcsGameDlg::DrawGame()
{
	CBrush brush;
	int i,j;
	CRect rc;
	CDC* pDC=GetDC();
	brush.CreateSolidBrush(RGB(0,0,0));
	pDC->FillRect(&m_rcGame,&brush);
	rc.left=m_rcGame.left-3;
	rc.right=m_rcGame.right+3;
	rc.top=m_rcGame.top-3;
	rc.bottom=m_rcGame.bottom+3;
	//3D背景绘制
	pDC->Draw3dRect(&rc,RGB(255,255,255),RGB(0,0,0));
	pDC->DeleteDC();
	brush.DeleteObject();
	if(m_bSnakeDied)
	{
		for(i=0;i<m_nSnakeLen;i++)
			DrawBlock(m_ptSnake[i].x,m_ptSnake[i].y,FALSE,TRUE);
	}
	else
	{
		for(i=0;i<m_nSnakeLen;i++)
			DrawBlock(m_ptSnake[i].x,m_ptSnake[i].y,FALSE);
	}
	for(i=0;i<20;i++)
	{
		for(j=0;j<15;j++)
		{		
			switch(m_nArray[i][j])
			{
			case TYPE_STONE:
				DrawStone(j,i);
				break;
			case TYPE_FOOD:
				DrawFood(j,i,TYPE_FOOD);
				break;
			case TYPE_AWARD:
				DrawFood(j,i,TYPE_AWARD);
				break;
			}
		}
	}
}
//画“蛇”的一节
void CTcsGameDlg::DrawBlock(int ix, int iy,BOOL bErase,BOOL bDie)
{
	CDC* pDC=GetDC();
	CDC  memDC;
	CBrush brush;
	CRect  rcBlock;
	if(bErase)//擦除
	{
		brush.CreateSolidBrush(RGB(0,0,0));
		rcBlock.SetRect(ix*m_nBlockLen+m_nInitX,
						iy*m_nBlockLen+m_nInitY,
						ix*m_nBlockLen+m_nInitX+m_nBlockLen,
						iy*m_nBlockLen+m_nInitY+m_nBlockLen);
		pDC->FillRect(&rcBlock,&brush);
		brush.DeleteObject();
	}
	else
	{
		CBitmap bitmap;
		if(bDie)
			bitmap.LoadBitmap(IDB_BITMAP5);
		else
			bitmap.LoadBitmap(IDB_BITMAP1);
		memDC.CreateCompatibleDC(pDC);
		memDC.SelectObject(bitmap);
		pDC->BitBlt(ix*m_nBlockLen+m_nInitX,iy*m_nBlockLen+m_nInitY,
					m_nBlockLen,m_nBlockLen,&memDC,0,0,SRCCOPY);
		memDC.DeleteDC();
		bitmap.DeleteObject();
	}
	pDC->DeleteDC();
}
//键盘接口
void CTcsGameDlg::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	switch(nChar)//1:down 2:Up 3:left 4:right
	{
	case VK_LEFT:
		if((m_nCurDir!=4)&&(!m_bPause)&&(m_nCurDirTemp!=4))
		{
			m_nCurDir=3;
		}
		break;
	case VK_RIGHT:
		if((m_nCurDir!=3)&&(!m_bPause)&&(m_nCurDirTemp!=3))
		{
			m_nCurDir=4;
		}
		break;
	case VK_DOWN:
		if((m_nCurDir!=2)&&(!m_bPause)&&(m_nCurDirTemp!=2))
		{
			m_nCurDir=1;
		}
		break;
	case VK_UP:
		if((m_nCurDir!=1)&&(!m_bPause)&&(m_nCurDirTemp!=1))
		{
			m_nCurDir=2;
		}
		break;
	case 'p':
	case 'P':
		OnPause();
		break;
	case VK_F2://新游戏
		OnStart();
		break;
	}
	//CDialog::OnKeyDown(nChar, nRepCnt, nFlags);
}
//映射键盘消息
BOOL CTcsGameDlg::PreTranslateMessage(MSG* pMsg) 
{
	if(pMsg->message==WM_KEYDOWN)
	{
		OnKeyDown((UINT)pMsg->wParam,(UINT)pMsg->lParam,(UINT)pMsg->lParam);
		return TRUE;
	}		
	return CDialog::PreTranslateMessage(pMsg);
}

void CTcsGameDlg::AddSnakeLen(int indexX, int indexY)
{
	int i;
	for(i=m_nSnakeLen;i>0;i--)
		m_ptSnake[i]=m_ptSnake[i-1];
	m_ptSnake[0].x=indexX;
	m_ptSnake[0].y=indexY;
	m_nSnakeLen++;
}
//“移动”蛇
void CTcsGameDlg::MoveSnake(int nDir)
{
	int i;
	CPoint pt[25];
	for(i=0;i<25;i++)
		pt[i]=m_ptSnake[i];
	switch(nDir)
	{
	case 1://down
		m_ptSnake[0].y++;
		m_ptSnake[0].y=m_ptSnake[0].y%20;
		break;
	case 2://up
		m_ptSnake[0].y--;
		if(m_ptSnake[0].y==-1)
			m_ptSnake[0].y=19;
		break;
	case 3://left
		m_ptSnake[0].x--;
		if(m_ptSnake[0].x==-1)
			m_ptSnake[0].x=14;
		break;
	case 4://right
		m_ptSnake[0].x++;
		m_ptSnake[0].x=m_ptSnake[0].x%15;
		break;
	}
	
	if(m_nSnakeLen==1)
		DrawBlock(pt[0].x,pt[0].y,TRUE);
	if(m_nSnakeLen>=2)
	{
		DrawBlock(m_ptSnake[m_nSnakeLen-1].x,m_ptSnake[m_nSnakeLen-1].y,TRUE);
		for(int i=1;i<m_nSnakeLen;i++)
		{
			m_ptSnake[i]=pt[i-1];
		}
	}
	DrawBlock(m_ptSnake[0].x,m_ptSnake[0].y,FALSE);
}
//时间事件
void CTcsGameDlg::OnTimer(UINT nIDEvent) 
{
	if(nIDEvent==TIMER_NORMAL)
	{//时钟用于蛇的运动
		if(CheckDie())
		{
			KillTimer(1);
			m_bSnakeDied=TRUE;
			int i;
			if(m_bEnableSound)
				::PlaySound(MAKEINTRESOURCE(IDR_WAVDIE),AfxGetApp()->m_hInstance,SND_RESOURCE|SND_ASYNC); 
			for(i=0;i<m_nSnakeLen;i++)
				DrawBlock(m_ptSnake[i].x,m_ptSnake[i].y,FALSE,TRUE);
			m_nSnakeLife--;
			if(m_nSnakeLife>=0)
			{
				DrawLifeCount(m_nSnakeLife,m_ptDisplay.x,m_ptDisplay.y+69+60+30);
				SetTimer(TIMER_WAIT,200,NULL);
				m_nWaitCount=1;
				m_nWaitType=WAIT_DIED;
			}
			return;
		}
		if(CheckCanEat())
		{
			m_nTotalScore+=10;
			DrawBlock(m_ptSnake[0].x,m_ptSnake[0].y,FALSE);
			DrawARNumber(m_nSnakeLen,m_ptDisplay.x+6*13,m_ptDisplay.y+23+50);
			DrawARNumber(m_nTotalScore,m_ptDisplay.x+6*13,m_ptDisplay.y+46+75);
			if(m_bEnableSound)
				::PlaySound(MAKEINTRESOURCE(IDR_WAVEAT),AfxGetApp()->m_hInstance,SND_RESOURCE|SND_ASYNC); 
			if(m_nSnakeLen==25)
			{
				KillTimer(1);
				if(m_nLevel==m_nMaxLevel)
				{
					CDlgVictory dlg;
					dlg.DoModal();
				}
				else
				{
					SetTimer(TIMER_WAIT,200,NULL);
					m_nWaitCount=1;
					m_nWaitType=WAIT_LEVUP;

				}
			}
			else
			{
				CreateNewFood();
				if((rand()%5)==0)
					CreateNewFood(TYPE_AWARD);
				if((rand()%25)==0)
					CreateNewFood(TYPE_LIFE);
			}
		}
		else
		{

			m_nCurDirTemp=m_nCurDir;
			MoveSnake(m_nCurDir);
		}
	}//TIMER_NORMAL Events
	else if(nIDEvent==TIMER_WAIT)
	{//时钟:用于等待
		DrawWait(m_nWaitType,m_nWaitCount%18);
		m_nWaitCount++;
		if(m_nWaitCount>18)
		{
			KillTimer(TIMER_WAIT);
			
			if(m_nWaitType==WAIT_DIED)
			{
				ReDoLevel();
			}
			else if(m_nWaitType==WAIT_LEVUP)
			{
				m_nLevel++;
				DoNextLevel();
			}
			m_nWaitType=WAIT_NONE;
		}
	
	}
	CDialog::OnTimer(nIDEvent);
}
//开始新游戏
void CTcsGameDlg::OnStart() 
{
	m_nTotalScore=0;
	m_nSnakeLife=5;
	m_nSnakeLen=1;
	m_nLevel=1;
	m_bSnakeDied=FALSE;
	if(m_bEnableSound)
		::PlaySound(MAKEINTRESOURCE(IDR_WAVSTART),AfxGetApp()->m_hInstance,SND_RESOURCE|SND_ASYNC); 
	///////////////////////////////////////
	m_ptSnake[0].x=7;
	m_ptSnake[0].y=0;
	m_nCurDir=1;//1:down 2:Up 3:left 4:right
	m_nCurDirTemp=1;
	////////////////////////////////////////
	m_bPause=FALSE;
	ClearArray();
	SetLevel_00();
	InvalidateRect(&m_rcGame,FALSE);
	CreateNewFood();
	////////////////////////////////////////
	//关值
	DrawARNumber(m_nLevel,   m_ptDisplay.x+6*13,m_ptDisplay.y+25);
	//长度
	DrawARNumber(m_nSnakeLen,m_ptDisplay.x+6*13,m_ptDisplay.y+23+50);
	//分数
	DrawARNumber(m_nTotalScore,m_ptDisplay.x+6*13,m_ptDisplay.y+46+75);
	//生命值
	DrawLifeCount(m_nSnakeLife,m_ptDisplay.x,m_ptDisplay.y+69+60+30);
	////////////////////////////////////////
	m_nWaitType=WAIT_NONE;
	m_wndStatusBar.SetText(_T("正在游戏"),0,0);
	SetTimer(TIMER_NORMAL,m_nTimeInterval,NULL);
}
//清空矩阵
void CTcsGameDlg::ClearArray()
{
	int i,j;
	for(i=0;i<20;i++)
		for(j=0;j<15;j++)
			m_nArray[i][j]=TYPE_NULL;
}
//检查下一位置是否可“吃”
BOOL CTcsGameDlg::CheckCanEat()
{
	int ix,iy;
	switch(m_nCurDir)
	{
	case 1://down
		ix=m_ptSnake[0].x;
		iy=(m_ptSnake[0].y+1)%20;
		break;
	case 2://up
		ix=m_ptSnake[0].x;
		iy=m_ptSnake[0].y-1;
		if(iy==-1)
			iy=19;
		break;
	case 3://left
		ix=m_ptSnake[0].x-1;
		iy=m_ptSnake[0].y;
		if(ix==-1)
			ix=14;
		break;
	case 4://right
		ix=(m_ptSnake[0].x+1)%15;
		iy=m_ptSnake[0].y;
		break;
	}
	if(m_nArray[iy][ix]==TYPE_FOOD)
	{
		m_nArray[iy][ix]=TYPE_NULL;
		AddSnakeLen(ix,iy);
		return TRUE;
	}
	else if(m_nArray[iy][ix]==TYPE_AWARD)
	{
		m_nArray[iy][ix]=TYPE_NULL;
		m_nTotalScore+=10;
		DrawARNumber(m_nTotalScore,m_ptDisplay.x+6*13,m_ptDisplay.y+46+75);
		return FALSE;
	}
	else if(m_nArray[iy][ix]==TYPE_LIFE)
	{
		m_nArray[iy][ix]=TYPE_NULL;
		m_nSnakeLife++;
		DrawLifeCount(m_nSnakeLife,m_ptDisplay.x,m_ptDisplay.y+69+60+30);
		return FALSE;
	}
	return FALSE;
}
//产生新的"食物",要排除"蛇"和障碍物已占有的位置
void CTcsGameDlg::CreateNewFood(int nFoodType)
{

⌨️ 快捷键说明

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