通过上一章的讲解,棋子已经能动起来了,接下来我们来添加标棋和调入布局的功能
1.标棋
1.1获取素材
首先调用InitFlagPixbuf(pJunqi);来初始化棋子,先导入标棋图片,如下图
把这张图片先放在消息盒里,然后在放在pJunqi->flagObj.image变量里,当鼠标右键按下棋子时就会弹出该图片。
这里有20个标记小图标,需要把这些小图标取出来当素材,素材以GdkPixbuf的类型存放在paPixbuf[20]数组里面,截取后的标记图标太大,还需要缩小一些,相关代码如下:
pixbuf = gdk_pixbuf_new_from_file(imageName, NULL);
for(i=0; i<20; i++)
{
//获取标记图标的x、y的坐标
... ...
temp = gdk_pixbuf_new_subpixbuf(pixbuf,x,y,34,34);
temp = gdk_pixbuf_scale_simple(temp,22,22,GDK_INTERP_BILINEAR);
pJunqi->flagObj.paPixbuf[i] = temp;
}
1.2右键显示标记图片
在棋盘的鼠标事件中判断如果是右键按下,则显示标记图片:
//点击右键
else if( event->button==3 )
{
if(pChess->type!=NONE)
{
ShowFlagChess(pJunqi, pChess);
}
}
考虑为了不让标记图片被边界遮挡,根据棋子所在的不同方位,显示在不同的位置上
switch(pChess->iDir)
{
case HOME:
case LEFT:
x = pChess->xPos+40;
y = pChess->yPos-250;
break;
case OPPS:
x = pChess->xPos+40;
y = pChess->yPos+40;
break;
case RIGHT:
x = pChess->xPos-195;
y = pChess->yPos-250;
break;
default:
break;
}
标记图片和标记棋子都是放在fixed里面,但是标记棋子在标记图片之后创建的,所以每次标记棋子都会显示在标记图片的上面,如下图这个样子,被标记为司令和排长的2个棋子显示在标记图片上面:
但是这不是我们想要的效果,我们想要标记图片一直显示在最上面,但是由于没找到GTK把控件显示在fixed容器最前面的函数,所以只能先把标记图片销毁,再重新创建一张新的,这时候显示在棋盘上就会覆盖掉标记棋子。
gtk_widget_destroy(pJunqi->flagObj.image);
InitFlagImage(pJunqi);
1.3显示标记棋子
标记图片已经放在了消息盒子里,所以可以对图片绑定鼠标点击的回调函数
g_signal_connect(FlagImage, "button-press-event",
G_CALLBACK(select_flag_event), pJunqi);
和上文确定选中棋子的方法类似,在select_flag_event()里先获取鼠标的位置,再由鼠标的坐标算出具体选中了哪一个标记棋子,之前右键已经获得了选中的棋子pJunqi->pSelect,然后由选中的图标生成一个图片控件,粘贴到棋子上面就可以了
iPos = iPosX+iPosY*4;
pixbuf = pJunqi->flagObj.paPixbuf[iPos];
if(pJunqi->pSelect->pLineup->pFlag)
{
gtk_widget_destroy(pJunqi->pSelect->pLineup->pFlag);
}
pJunqi->pSelect->pLineup->pFlag = gtk_image_new_from_pixbuf(pixbuf);
MoveFlag(pJunqi,pJunqi->pSelect,0);
在移动棋子的时候,标记棋子也跟着一起移动
pDst->pLineup = pSrc->pLineup;
//移动棋子
gtk_fixed_move(GTK_FIXED(pJunqi->fixed),
pSrc->pLineup->pImage[pDst->iDir],
pDst->xPos,pDst->yPos);
//如果标棋了,则标记棋子也跟着移动
if(pDst->pLineup->pFlag)
{
MoveFlag(pJunqi,pDst,1);
}
2.调入布局
棋盘上每家都有一个调入布局的按钮,现在要为这个按钮实现调入布局的功能,每个按钮都绑定button_cb的回调函数,传入参数为所在方位,如自家的按钮,代码如下:
g_signal_connect (button[i], "clicked", G_CALLBACK (button_cb), "home");
由于回调传入的参数只有一个,所以不得不使用全局变量来获取pJunqi句柄,来记录选中的方位
char *zDir = (char*)data;
Junqi *pJunqi = gJunqi;
if( strcmp(zDir,"home" )==0 )
{
pJunqi->selectDir = HOME;
}
else if( strcmp(zDir,"right" )==0 )
{
pJunqi->selectDir = RIGHT;
}
else if( strcmp(zDir,"opps" )==0 )
{
pJunqi->selectDir = OPPS;
}
else if( strcmp(zDir,"left" )==0 )
{
pJunqi->selectDir = LEFT;
}
接下来就是打开一个文件选择对话框,用来选择布阵,选择完毕后触发get_lineup_cb回调函数
GtkFileChooserNative *native;
native = gtk_file_chooser_native_new ("Open File",
NULL,
GTK_FILE_CHOOSER_ACTION_OPEN,
"_Open",
"_Cancel");
gtk_native_dialog_show (GTK_NATIVE_DIALOG (native));
pJunqi->native = native;
g_signal_connect (native,
"response",
G_CALLBACK (get_lineup_cb),
pJunqi);
在回调函数中获取选中的文件名,该文件名是window下的文件名,如”D:\军旗\布局2\1.jql”,因为’\’是转义字符,所以该文件名不能用来直接打开文件,需要把文件名转为”D:/军旗/布局2/1.jql”或”D:\军旗\布局2\1.jql”,这里为了方便使用第一种,此后读取布阵文件,将布阵保存在pLineup
name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER (native));
ConvertFilename(name);
fd = open(name, O_RDWR|O_CREAT, 0600);
OsRead(fd, aBuf, 4096, 0);
pLineup = (Jql*)(&aBuf[0]);
接下来比较pJunqi->ChessPos和pLineup的棋子,如果第i个棋子的类型不一样,那么遍历pJunqi->ChessPos[iDir][i]之后的棋子,找到与pLineup->chess[i]相同的棋子,并与当前棋子进行交换,代码如下
iDir = pJunqi->selectDir;
for(i=0;i<30;i++)
{
if(pJunqi->ChessPos[iDir][i].type == NONE)
{
continue;
}
if( pJunqi->ChessPos[iDir][i].type != pLineup->chess[i] )
{
for(j=i+1;j<30;j++)
{
if(pLineup->chess[i]==pJunqi->ChessPos[iDir][j].type)
{
SwapChess(pJunqi,i,j);
break;
}
}
}
}
如果没有开始,还可以对布局进行调整,当然调整需要满足一定的规则,如炸弹不能放在第一排等等,目前还没有做,只是简单实现2个棋子的交换
if(!pJunqi->bStart)
{
ChangeChess(pJunqi, pChess);
}
void ChangeChess(Junqi *pJunqi, BoardChess *pChess)
{
if(pChess->type!=NONE)
{
if(pChess->iDir==pJunqi->pSelect->iDir)
{
SwapChess(pJunqi,pChess->index,pJunqi->pSelect->index);
}
}
}