四国军棋界面开发(3) 标棋和调入布局

通过上一章的讲解,棋子已经能动起来了,接下来我们来添加标棋和调入布局的功能

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);
        }
    }
}

3.源代码

https://github.com/pfysw/JunQi

猜你喜欢

转载自blog.csdn.net/pfysw/article/details/81407826