之前已经完成了对行棋路线的判定,接下来需要实现对军棋中吃子规则的判定
1.大小比较
只有在行棋路线合法的情况下才比较大小,比较棋子大小的函数为CompareChess
输入的参数是选中的棋子和目标棋子(也可能目标位置上没棋子),返回值是比较的结果,比较的结果有4种,分别如下:
- 移动
- 吃掉
- 打兑
碰死
如果目标位置上没棋,那么说明是没有比较,只是棋子的移动。如果一方有炸弹,则判定为打兑,这个优先级最高。在没炸弹时,目标棋子为军旗,则判定为吃掉。接着如果目标棋子是地雷,除工兵外,都判定为撞死,工兵可以挖雷,判定为吃掉。除去以上棋子,接着就是司令~工兵这9个种类型的棋子之间的比较了,将会产生吃掉、打兑或撞死3种结果,代码如下:
int CompareChess(BoardChess *pSrc, BoardChess *pDst)
{
enum CompareType result;
assert( pSrc->type!=NONE );
if( pDst->type==NONE )
{
result = MOVE;
}
else if( pDst->type==ZHADAN||pSrc->type==ZHADAN )
{
result = BOMB;
}
else if( pDst->type==JUNQI )
{
result = EAT;
}
else if( pDst->type==DILEI )
{
if( pSrc->type==GONGB )
{
result = EAT;
}
else
{
result = KILLED;
}
}
else
{
assert( pDst->type>=SILING && pDst->type<=GONGB &&
pSrc->type>=SILING && pSrc->type<=GONGB );
if( pDst->type == pSrc->type )
{
result = BOMB;
}
else if( pSrc->type<pDst->type )
{
result = EAT;
}
else
{
result = KILLED;
}
}
return result;
}
2.行棋结果
有了比较的结果之后,需要根据结果来显示棋子,另外除了以上4种判定结果外,有时候还有一些附加效果要处理,如司令死掉后要亮军旗,军旗被挖后,这一家的所有棋子都要消失。最后的处理看下面的注释说明:
//输入参数是原位置和目标位置,type为上一次比较的结果
void PlayResult(Junqi *pJunqi, BoardChess *pSrc, BoardChess *pDst, int type)
{
//不管是移动还是吃子,都要把选中的棋子移到目标位置上
if( type==EAT || type==MOVE )
{
gtk_fixed_move(GTK_FIXED(pJunqi->fixed),
pSrc->pLineup->pImage[pDst->iDir],
pDst->xPos,pDst->yPos);
SendSoundEvent(pJunqi,MOVE);
}
//如果是吃子或打兑,需要把目标位置上的棋子隐藏掉
if( type==EAT || type==BOMB )
{
assert(pDst->type!=NONE);
gtk_widget_hide(pDst->pLineup->pImage[pDst->iDir]);
if( pDst->pLineup->pFlag )
{
gtk_widget_destroy(pDst->pLineup->pFlag);
pDst->pLineup->pFlag = NULL;
}
if( type==BOMB )
{
pDst->type = NONE;
SendSoundEvent(pJunqi,BOMB);
}
else
{
SendSoundEvent(pJunqi,EAT);
}
//吃掉或炸掉的可能是军旗,那么移除军旗所在这一家的所有棋子
if( pDst->pLineup->type==JUNQI )
{
SendSoundEvent(pJunqi, DEAD);
DestroyAllChess(pJunqi, pDst->iDir);
}
}
//pLineup保存该棋子的图片,将目标位置上的棋子替换为原选中棋子
if( type==EAT || type==MOVE )
{
//之所以现在更新pDst,是因为之前还要判断是否是军旗
pDst->type = pSrc->pLineup->type;
pDst->pLineup = pSrc->pLineup;
//如果有标棋,那么标棋也要跟着移动
if(pSrc->pLineup->pFlag)
{
MoveFlag(pJunqi,pDst,1);
}
}
//司令死后,需要调用ShowBanner显示军旗位置
if( type==KILLED || type==BOMB )
{
if( type==KILLED )
{
SendSoundEvent(pJunqi,KILLED);
//此处是司令撞地雷
if( pSrc->pLineup->type==SILING )
{
ShowBanner(pJunqi, pSrc->pLineup->iDir);
}
}
//撞死或打兑,原棋子已经不存在,相应的标记也要清除
if(pSrc->pLineup->pFlag)
{
gtk_widget_hide(pSrc->pLineup->pFlag);
}
if( type==BOMB )
{
//打兑时还要判断是否有司令,从而显示军旗位
if( pSrc->pLineup->type==SILING )
ShowBanner(pJunqi, pSrc->pLineup->iDir);
if( pDst->pLineup->type==SILING )
ShowBanner(pJunqi, pDst->pLineup->iDir);
}
}
assert( pSrc->pLineup->type!=NONE );
//无论哪一种情况都要隐藏原来位置上的棋子
gtk_widget_hide(pSrc->pLineup->pImage[pSrc->iDir]);
pSrc->type = NONE;
if( type==EAT || type==MOVE )
{
//把棋子显示到目标位置上
gtk_widget_show(pDst->pLineup->pImage[pDst->iDir]);
}
//在目标位置上画红色方框表示移动目的地
ShowRectangle(pJunqi, pDst, RECTANGLE_RED);
//最后画上行棋路径的箭头
ShowPathArrow(pJunqi, 0);
}
3.声音效果
播放声音需要调用windows下的 PlaySound函数,另外要添加mmsystem.h头文件和winmm.dll库。
首先定义各种类型的声音文件路径
#define MOVE_SOUND "./sound/move.wav"
#define BOMB_SOUND "./sound/bomb.wav"
#define EAT_SOUND "./sound/eat.wav"
#define KILLED_SOUND "./sound/killed.wav"
#define DEAD_SOUND "./sound/dead.wav"
#define SHOW_FLAG_SOUND "./sound/showflag.wav"
#define SELECT_SOUND "./sound/select.wav"
#define BEGIN_SOUND "./sound/begin.wav"
如果需要播放打兑的声音,则调用如下:
PlaySound (BOMB_SOUND, NULL, SND_FILENAME | SND_NODEFAULT);
要注意的是在上面PlayResult ()的行棋过程处理中不要直接播放声音,否则会等声音播放完毕后再显示棋子的移动会产生很强烈的延迟感觉。所有为了先让棋子移动后再播放声音,把声音播放放在一个0.5秒的定时器回调函数里
g_timeout_add(500, (GSourceFunc)sound_event, pJunqi);
根据pJunqi->sound_type的类型播放相应的声音
void sound_event(gpointer data)
{
switch(pJunqi->sound_type)
{
... ...
}
}
最后只要在声音事件产生后对pJunqi->sound_type赋值即可
void SendSoundEvent(Junqi *pJunqi, enum CompareType type)
{
pJunqi->sound_type = type;
}