toward making tile picking work through rotations

Make face-up tile picker util method return void and add mechanism for
passing in selected tiles asynchronously, as has been done recently with
the rest of the once-blocking util callbacks. Works perfectly in the gtk
case. Likely crashes in curses (if picking face-up option is on.) In
java all the callbacks are there but rather than put up a UI we pretend
the user says "pick 'em for me" each time. Putting up a UI is next.
This commit is contained in:
Eric House 2017-03-09 20:36:14 -08:00
parent 70b3aaa263
commit 6e5973c55b
19 changed files with 385 additions and 131 deletions

View file

@ -1869,16 +1869,32 @@ public class BoardDelegate extends DelegateBase
} }
@Override @Override
public int userPickTileTray( int playerNum, String[] texts, public void informNeedPickTiles( final boolean isInitial,
String[] curTiles, int nPicked ) final int playerNum, int nToPick,
String[] texts, int[] counts )
{ {
String curTilesStr = TextUtils.join( ", ", curTiles ); post( new Runnable() {
boolean canUndoTiles = 0 < nPicked; @Override
waitBlockingDialog( DlgID.PICK_TILE_REQUESTTRAY_BLK, public void run() {
UtilCtxt.PICKER_PICKALL, texts, curTilesStr, int[] noNewTiles = new int[0];
canUndoTiles ); if ( isInitial ) {
return m_resultCode; handleViaThread( JNICmd.CMD_TILES_PICKED, playerNum, noNewTiles );
} else {
handleViaThread( JNICmd.CMD_COMMIT, true, true, noNewTiles );
}
}
} );
} }
// public int userPickTileTray( int playerNum, String[] texts,
// String[] curTiles, int nPicked )
// {
// String curTilesStr = TextUtils.join( ", ", curTiles );
// boolean canUndoTiles = 0 < nPicked;
// waitBlockingDialog( DlgID.PICK_TILE_REQUESTTRAY_BLK,
// UtilCtxt.PICKER_PICKALL, texts, curTilesStr,
// canUndoTiles );
// return m_resultCode;
// }
@Override @Override
public void informNeedPassword( int player, String name ) public void informNeedPassword( int player, String name )

View file

@ -72,6 +72,7 @@ public class JNIThread extends Thread {
CMD_KEYUP, CMD_KEYUP,
CMD_TIMER_FIRED, CMD_TIMER_FIRED,
CMD_COMMIT, CMD_COMMIT,
CMD_TILES_PICKED,
CMD_JUGGLE, CMD_JUGGLE,
CMD_FLIP, CMD_FLIP,
CMD_TOGGLE_TRAY, CMD_TOGGLE_TRAY,
@ -552,9 +553,17 @@ public class JNIThread extends Thread {
? false : (Boolean)args[0]; ? false : (Boolean)args[0];
boolean turnConfirmed = args.length < 2 boolean turnConfirmed = args.length < 2
? false : (Boolean)args[1]; ? false : (Boolean)args[1];
int[] newTiles = args.length < 3 ? null : (int[])args[2];
draw = XwJNI.board_commitTurn( m_jniGamePtr, phoniesConfirmed, draw = XwJNI.board_commitTurn( m_jniGamePtr, phoniesConfirmed,
turnConfirmed ); turnConfirmed, newTiles );
break; break;
case CMD_TILES_PICKED:
int playerNum = (Integer)args[0];
int[] tiles = (int[])args[1];
XwJNI.server_tilesPicked( m_jniGamePtr, playerNum, tiles );
break;
case CMD_JUGGLE: case CMD_JUGGLE:
draw = XwJNI.board_juggleTray( m_jniGamePtr ); draw = XwJNI.board_juggleTray( m_jniGamePtr );
break; break;

View file

@ -38,8 +38,9 @@ public interface UtilCtxt {
public static final int PICKER_BACKUP = -2; public static final int PICKER_BACKUP = -2;
void notifyPickTileBlank( int playerNum, int col, int row, String[] texts ); void notifyPickTileBlank( int playerNum, int col, int row, String[] texts );
int userPickTileTray( int playerNum, String[] tiles,
String[] curTiles, int nPicked ); void informNeedPickTiles( boolean isInitial, int playerNum, int nToPick,
String[] texts, int[] counts );
void informNeedPassword( int player, String name ); void informNeedPassword( int player, String name );

View file

@ -54,11 +54,10 @@ public class UtilCtxtImpl implements UtilCtxt {
subclassOverride( "userPickTileBlank" ); subclassOverride( "userPickTileBlank" );
} }
public int userPickTileTray( int playerNum, String[] texts, public void informNeedPickTiles( boolean isInitial, int playerNum, int nToPick,
String[] curTiles, int nPicked ) String[] texts, int[] counts )
{ {
subclassOverride( "userPickTileTray" ); subclassOverride( "informNeedPickTiles" );
return 0;
} }
public void informNeedPassword( int player, String name ) public void informNeedPassword( int player, String name )

View file

@ -300,7 +300,8 @@ public class XwJNI {
public static native boolean board_toggle_showValues( GamePtr gamePtr ); public static native boolean board_toggle_showValues( GamePtr gamePtr );
public static native boolean board_commitTurn( GamePtr gamePtr, public static native boolean board_commitTurn( GamePtr gamePtr,
boolean phoniesConfirmed, boolean phoniesConfirmed,
boolean turnConfirmed ); boolean turnConfirmed,
int[] newTiles );
public static native boolean board_flip( GamePtr gamePtr ); public static native boolean board_flip( GamePtr gamePtr );
public static native boolean board_replaceTiles( GamePtr gamePtr ); public static native boolean board_replaceTiles( GamePtr gamePtr );
@ -358,6 +359,8 @@ public class XwJNI {
public static native void server_reset( GamePtr gamePtr ); public static native void server_reset( GamePtr gamePtr );
public static native void server_handleUndo( GamePtr gamePtr ); public static native void server_handleUndo( GamePtr gamePtr );
public static native boolean server_do( GamePtr gamePtr ); public static native boolean server_do( GamePtr gamePtr );
public static native void server_tilesPicked( GamePtr gamePtr, int player, int[] tiles );
public static native String server_formatDictCounts( GamePtr gamePtr, int nCols ); public static native String server_formatDictCounts( GamePtr gamePtr, int nCols );
public static native boolean server_getGameIsOver( GamePtr gamePtr ); public static native boolean server_getGameIsOver( GamePtr gamePtr );
public static native String server_writeFinalScores( GamePtr gamePtr ); public static native String server_writeFinalScores( GamePtr gamePtr );

View file

@ -1,4 +1,4 @@
/* -*-mode: C; compile-command: "cd ..; ../scripts/ndkbuild.sh -j3"; -*- */ /* -*-mode: C; compile-command: "find-and-gradle.sh insXwdDeb"; -*- */
/* /*
* Copyright 2001-2010 by Eric House (xwords@eehouse.org). All rights * Copyright 2001-2010 by Eric House (xwords@eehouse.org). All rights
* reserved. * reserved.

View file

@ -1,6 +1,6 @@
/* -*- compile-command: "find-and-ant.sh debug install"; -*- */ /* -*- compile-command: "find-and-gradle.sh insXw4Deb"; -*- */
/* /*
* Copyright 2001-2014 by Eric House (xwords@eehouse.org). All rights * Copyright 2001 - 2017 by Eric House (xwords@eehouse.org). All rights
* reserved. * reserved.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
@ -162,24 +162,23 @@ and_util_notifyPickTileBlank( XW_UtilCtxt* uc, XP_U16 playerNum,
UTIL_CBK_TAIL(); UTIL_CBK_TAIL();
} }
static XP_S16 static void
and_util_userPickTileTray( XW_UtilCtxt* uc, const PickInfo* pi, and_util_informNeedPickTiles( XW_UtilCtxt* uc, XP_Bool isInitial,
XP_U16 playerNum, const XP_UCHAR** tileFaces, XP_U16 player, XP_U16 nToPick,
XP_U16 nTiles ) XP_U16 nFaces, const XP_UCHAR** faces,
const XP_U16* counts )
{ {
XP_S16 result = -1; UTIL_CBK_HEADER("informNeedPickTiles",
UTIL_CBK_HEADER("userPickTileTray", "(ZII[Ljava/lang/String;[I)V" );
"(I[Ljava/lang/String;[Ljava/lang/String;I)I" ); jobject jtexts = makeStringArray( env, nFaces, faces );
jobject jtexts = makeStringArray( env, nTiles, tileFaces ); jobject jcounts = makeIntArray( env, nFaces, counts );
jobject jcurtiles = makeStringArray( env, pi->nCurTiles, pi->curTiles );
result = (*env)->CallIntMethod( env, util->jutil, mid, (*env)->CallVoidMethod( env, util->jutil, mid, isInitial, player,
playerNum, jtexts, jcurtiles, nToPick, jtexts, jcounts );
pi->thisPick );
deleteLocalRefs( env, jtexts, jcurtiles, DELETE_NO_REF ); deleteLocalRefs( env, jtexts, jcounts, DELETE_NO_REF );
UTIL_CBK_TAIL(); UTIL_CBK_TAIL();
return result; } /* and_util_informNeedPickTiles */
} /* and_util_userPickTile */
static void static void
and_util_informNeedPassword( XW_UtilCtxt* uc, XP_U16 player, and_util_informNeedPassword( XW_UtilCtxt* uc, XP_U16 player,
@ -707,7 +706,7 @@ makeUtil( MPFORMAL EnvThreadInfo* ti, jobject jutil, CurGameInfo* gi,
SET_PROC(notifyMove); SET_PROC(notifyMove);
SET_PROC(notifyTrade); SET_PROC(notifyTrade);
SET_PROC(notifyPickTileBlank); SET_PROC(notifyPickTileBlank);
SET_PROC(userPickTileTray); SET_PROC(informNeedPickTiles);
SET_PROC(informNeedPassword); SET_PROC(informNeedPassword);
SET_PROC(trayHiddenChange); SET_PROC(trayHiddenChange);
SET_PROC(yOffsetChange); SET_PROC(yOffsetChange);

View file

@ -220,6 +220,21 @@ envForMe( EnvThreadInfo* ti, const char* caller )
return result; return result;
} }
static void
tilesArrayToTileSet( JNIEnv* env, jintArray jtiles, TrayTileSet* tset )
{
if ( jtiles != NULL ) {
jsize nTiles = (*env)->GetArrayLength( env, jtiles );
int tmp[MAX_TRAY_TILES];
getIntsFromArray( env, tmp, jtiles, nTiles, XP_FALSE );
tset->nTiles = nTiles;
for ( int ii = 0; ii < nTiles; ++ii ) {
tset->tiles[ii] = tmp[ii];
}
}
}
#ifdef GAMEPTR_IS_OBJECT #ifdef GAMEPTR_IS_OBJECT
static JNIState* static JNIState*
getState( JNIEnv* env, GamePtrType gamePtr ) getState( JNIEnv* env, GamePtrType gamePtr )
@ -1282,13 +1297,21 @@ Java_org_eehouse_android_xw4_jni_XwJNI_board_1toggle_1showValues
JNIEXPORT jboolean JNICALL JNIEXPORT jboolean JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_board_1commitTurn Java_org_eehouse_android_xw4_jni_XwJNI_board_1commitTurn
(JNIEnv* env, jclass C, GamePtrType gamePtr, jboolean phoniesConfirmed, ( JNIEnv* env, jclass C, GamePtrType gamePtr, jboolean phoniesConfirmed,
jboolean turnConfirmed) jboolean turnConfirmed, jintArray jNewTiles )
{ {
jboolean result; jboolean result;
XWJNI_START(); XWJNI_START();
TrayTileSet* newTilesP = NULL;
TrayTileSet newTiles;
if ( jNewTiles != NULL ) {
tilesArrayToTileSet( env, jNewTiles, &newTiles );
newTilesP = &newTiles;
}
result = board_commitTurn( state->game.board, phoniesConfirmed, result = board_commitTurn( state->game.board, phoniesConfirmed,
turnConfirmed ); turnConfirmed, newTilesP );
XWJNI_END(); XWJNI_END();
return result; return result;
} }
@ -1356,6 +1379,17 @@ Java_org_eehouse_android_xw4_jni_XwJNI_server_1do
return result; return result;
} }
JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_server_1tilesPicked
( JNIEnv* env, jclass C, GamePtrType gamePtr, jint player, jintArray jNewTiles )
{
XWJNI_START();
TrayTileSet newTiles;
tilesArrayToTileSet( env, jNewTiles, &newTiles );
server_tilesPicked( state->game.server, player, &newTiles );
XWJNI_END();
}
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_board_1resetEngine Java_org_eehouse_android_xw4_jni_XwJNI_board_1resetEngine
(JNIEnv* env, jclass C, GamePtrType gamePtr ) (JNIEnv* env, jclass C, GamePtrType gamePtr )

View file

@ -1026,17 +1026,19 @@ boardNotifyTrade( BoardCtxt* board, const TrayTileSet* tiles )
XP_Bool XP_Bool
board_commitTurn( BoardCtxt* board, XP_Bool phoniesConfirmed, board_commitTurn( BoardCtxt* board, XP_Bool phoniesConfirmed,
XP_Bool turnConfirmed /* includes trade */ ) XP_Bool turnConfirmed /* includes trade */,
TrayTileSet* newTiles )
{ {
XP_Bool result = XP_FALSE; XP_Bool result = XP_FALSE;
const XP_S16 turn = server_getCurrentTurn( board->server, NULL ); const XP_S16 turn = server_getCurrentTurn( board->server, NULL );
PerTurnInfo* pti = board->pti + turn; PerTurnInfo* pti = board->pti + turn;
ModelCtxt* model = board->model;
if ( board->gameOver || turn < 0 ) { if ( board->gameOver || turn < 0 ) {
/* do nothing */ /* do nothing */
} else if ( turn != board->selPlayer ) { } else if ( turn != board->selPlayer ) {
util_userError( board->util, ERR_NOT_YOUR_TURN ); util_userError( board->util, ERR_NOT_YOUR_TURN );
} else if ( 0 == model_getNumTilesTotal( board->model, turn ) ) { } else if ( 0 == model_getNumTilesTotal( model, turn ) ) {
/* game's over but still undoable so turn hasn't changed; do /* game's over but still undoable so turn hasn't changed; do
nothing */ nothing */
} else if ( phoniesConfirmed || turnConfirmed || checkRevealTray( board ) ) { } else if ( phoniesConfirmed || turnConfirmed || checkRevealTray( board ) ) {
@ -1051,11 +1053,15 @@ board_commitTurn( BoardCtxt* board, XP_Bool phoniesConfirmed,
TrayTileSet selTiles; TrayTileSet selTiles;
getSelTiles( board, traySelBits, &selTiles ); getSelTiles( board, traySelBits, &selTiles );
if ( turnConfirmed ) { if ( turnConfirmed ) {
/* server_commitTrade() changes selPlayer, so board_endTrade if ( !server_askPickTiles( board->server, turn, newTiles,
must be called first() */ selTiles.nTiles ) ) {
(void)board_endTrade( board ); /* server_commitTrade() changes selPlayer, so board_endTrade
must be called first() */
(void)board_endTrade( board );
(void)server_commitTrade( board->server, &selTiles ); (void)server_commitTrade( board->server, &selTiles,
newTiles );
}
} else { } else {
boardNotifyTrade( board, &selTiles ); boardNotifyTrade( board, &selTiles );
} }
@ -1081,13 +1087,13 @@ board_commitTurn( BoardCtxt* board, XP_Bool phoniesConfirmed,
info.proc = saveBadWords; info.proc = saveBadWords;
info.closure = &bwl; info.closure = &bwl;
} }
legal = model_checkMoveLegal( board->model, turn, stream, legal = model_checkMoveLegal( model, turn, stream,
warn? &info:(WordNotifierInfo*)NULL); warn? &info:(WordNotifierInfo*)NULL);
} }
if ( 0 < bwl.bwi.nWords && !phoniesConfirmed ) { if ( 0 < bwl.bwi.nWords && !phoniesConfirmed ) {
bwl.bwi.dictName = bwl.bwi.dictName =
dict_getShortName( model_getPlayerDict( board->model, turn ) ); dict_getShortName( model_getPlayerDict( model, turn ) );
util_notifyIllegalWords( board->util, &bwl.bwi, turn, XP_FALSE ); util_notifyIllegalWords( board->util, &bwl.bwi, turn, XP_FALSE );
} else { } else {
/* Hide the tray so no peeking. Leave it hidden even if user /* Hide the tray so no peeking. Leave it hidden even if user
@ -1099,14 +1105,20 @@ board_commitTurn( BoardCtxt* board, XP_Bool phoniesConfirmed,
} }
if ( board->skipCommitConfirm || turnConfirmed ) { if ( board->skipCommitConfirm || turnConfirmed ) {
result = server_commitMove( board->server ) || result; XP_U16 nToPick = MAX_TRAY_TILES -
/* invalidate all tiles in case we'll be drawing this tray model_getNumTilesInTray( model, turn );
again rather than some other -- as is the case in a if ( !server_askPickTiles( board->server, turn, newTiles,
two-player game where one's a robot. We really only nToPick ) ) {
need the selected tiles and the rightmost (in case it's result = server_commitMove( board->server, newTiles )
showing points-this-turn), but this is easier. */ || result;
board_invalTrayTiles( board, ALLTILES ); /* invalidate all tiles in case we'll be drawing this tray
pti->traySelBits = 0x00; again rather than some other -- as is the case in a
two-player game where one's a robot. We really only
need the selected tiles and the rightmost (in case it's
showing points-this-turn), but this is easier. */
board_invalTrayTiles( board, ALLTILES );
pti->traySelBits = 0x00;
}
} else { } else {
util_notifyMove( board->util, stream ); util_notifyMove( board->util, stream );
} }

View file

@ -177,7 +177,7 @@ XP_Bool board_setBlankValue( BoardCtxt* board, XP_U16 XP_UNUSED(player),
void board_resetEngine( BoardCtxt* board ); void board_resetEngine( BoardCtxt* board );
XP_Bool board_commitTurn( BoardCtxt* board, XP_Bool phoniesConfirmed, XP_Bool board_commitTurn( BoardCtxt* board, XP_Bool phoniesConfirmed,
XP_Bool turnConfirmed ); XP_Bool turnConfirmed, TrayTileSet* newTiles );
void board_pushTimerSave( BoardCtxt* board ); void board_pushTimerSave( BoardCtxt* board );
void board_popTimerSave( BoardCtxt* board ); void board_popTimerSave( BoardCtxt* board );

View file

@ -34,6 +34,7 @@
#include "pool.h" #include "pool.h"
#include "engine.h" #include "engine.h"
#include "strutils.h" #include "strutils.h"
#include "dbgutil.h"
#include "LocalizedStrIncludes.h" #include "LocalizedStrIncludes.h"
@ -132,6 +133,8 @@ struct ServerCtxt {
/******************************* prototypes *******************************/ /******************************* prototypes *******************************/
static void assignTilesToAll( ServerCtxt* server ); static void assignTilesToAll( ServerCtxt* server );
static void makePoolOnce( ServerCtxt* server );
static void resetEngines( ServerCtxt* server ); static void resetEngines( ServerCtxt* server );
static void nextTurn( ServerCtxt* server, XP_S16 nxtTurn ); static void nextTurn( ServerCtxt* server, XP_S16 nxtTurn );
@ -142,6 +145,9 @@ static void badWordMoveUndoAndTellUser( ServerCtxt* server,
static XP_Bool tileCountsOk( const ServerCtxt* server ); static XP_Bool tileCountsOk( const ServerCtxt* server );
static void setTurn( ServerCtxt* server, XP_S16 turn ); static void setTurn( ServerCtxt* server, XP_S16 turn );
static XWStreamCtxt* mkServerStream( ServerCtxt* server ); static XWStreamCtxt* mkServerStream( ServerCtxt* server );
static void fetchTiles( ServerCtxt* server, XP_U16 playerNum, XP_U16 nToFetch,
const TrayTileSet* tradedTiles,
TrayTileSet* resultTiles );
#ifndef XWFEATURE_STANDALONE_ONLY #ifndef XWFEATURE_STANDALONE_ONLY
static XWStreamCtxt* messageStreamWithHeader( ServerCtxt* server, static XWStreamCtxt* messageStreamWithHeader( ServerCtxt* server,
@ -900,7 +906,7 @@ makeRobotMove( ServerCtxt* server )
if ( trade ) { if ( trade ) {
TrayTileSet oldTiles = *model_getPlayerTiles( model, turn ); TrayTileSet oldTiles = *model_getPlayerTiles( model, turn );
XP_LOGF( "%s: robot trading %d tiles", __func__, oldTiles.nTiles ); XP_LOGF( "%s: robot trading %d tiles", __func__, oldTiles.nTiles );
result = server_commitTrade( server, &oldTiles ); result = server_commitTrade( server, &oldTiles, NULL );
/* Quick hack to fix gremlin bug where all-robot game seen none /* Quick hack to fix gremlin bug where all-robot game seen none
able to trade for tiles to move and blowing the undo stack. able to trade for tiles to move and blowing the undo stack.
@ -933,7 +939,7 @@ makeRobotMove( ServerCtxt* server )
server->nv.prevMoveStream = stream; server->nv.prevMoveStream = stream;
server->nv.prevWordsStream = wordsStream; server->nv.prevWordsStream = wordsStream;
} }
result = server_commitMove( server ); result = server_commitMove( server, NULL );
} else { } else {
result = XP_FALSE; result = XP_FALSE;
} }
@ -1047,6 +1053,64 @@ showPrevScore( ServerCtxt* server )
SETSTATE( server, server->nv.stateAfterShow ); SETSTATE( server, server->nv.stateAfterShow );
} /* showPrevScore */ } /* showPrevScore */
void
server_tilesPicked( ServerCtxt* server, XP_U16 player,
const TrayTileSet* newTilesP )
{
TrayTileSet newTiles = *newTilesP;
pool_removeTiles( server->pool, &newTiles );
fetchTiles( server, player, MAX_TRAY_TILES, NULL, &newTiles );
model_assignPlayerTiles( server->vol.model, player, &newTiles );
util_requestTime( server->vol.util );
}
static void
informNeedPickTiles( ServerCtxt* server, XP_Bool initial, XP_U16 turn,
XP_U16 nToPick )
{
ModelCtxt* model = server->vol.model;
DictionaryCtxt* dict = model_getDictionary(model);
XP_U16 nFaces = dict_numTileFaces( dict );
XP_U16 counts[MAX_UNIQUE_TILES];
const XP_UCHAR* faces[MAX_UNIQUE_TILES];
XP_U16 nLeft = pool_getNTilesLeft( server->pool );
if ( nLeft < nToPick ) {
nToPick = nLeft;
}
for ( Tile tile = 0; tile < nFaces; ++tile ) {
faces[tile] = dict_getTileString( dict, tile );
counts[tile] = pool_getNTilesLeftFor( server->pool, tile );
}
util_informNeedPickTiles( server->vol.util, initial, turn,
nToPick, nFaces, faces, counts );
}
static XP_Bool
askedForTiles( ServerCtxt* server )
{
XP_Bool asked = XP_FALSE;
CurGameInfo* gi = server->vol.gi;
if ( gi->serverRole == SERVER_STANDALONE && gi->allowPickTiles ) {
XP_U16 nPlayers = gi->nPlayers;
ModelCtxt* model = server->vol.model;
makePoolOnce( server );
for ( XP_U16 turn = 0; !asked && turn < nPlayers; ++turn ) {
LocalPlayer* player = &gi->players[turn];
XP_U16 nTiles = model_getNumTilesInTray( model, turn );
if ( nTiles == 0 && !LP_IS_ROBOT(player) ) {
informNeedPickTiles( server, XP_TRUE, turn, MAX_TRAY_TILES );
asked = XP_TRUE;
}
}
}
LOG_RETURNF( "%s", boolToStr(asked));
return asked;
}
XP_Bool XP_Bool
server_do( ServerCtxt* server ) server_do( ServerCtxt* server )
{ {
@ -1062,10 +1126,12 @@ server_do( ServerCtxt* server )
case XWSTATE_BEGIN: case XWSTATE_BEGIN:
if ( server->nv.pendingRegistrations == 0 ) { /* all players on if ( server->nv.pendingRegistrations == 0 ) { /* all players on
device */ device */
assignTilesToAll( server ); if ( !askedForTiles( server ) ) {
SETSTATE( server, XWSTATE_INTURN ); assignTilesToAll( server );
setTurn( server, 0 ); SETSTATE( server, XWSTATE_INTURN );
moreToDo = XP_TRUE; setTurn( server, 0 );
moreToDo = XP_TRUE;
}
} }
break; break;
@ -1359,8 +1425,8 @@ client_readInitialMessage( ServerCtxt* server, XWStreamCtxt* stream )
dict_unref( newDict ); /* new owner will have ref'd */ dict_unref( newDict ); /* new owner will have ref'd */
XP_ASSERT( !server->pool ); XP_ASSERT( !server->pool );
pool = server->pool = pool_make( MPPARM_NOCOMMA(server->mpool) ); makePoolOnce( server );
pool_initFromDict( server->pool, model_getDictionary(model)); pool = server->pool;
/* now read the assigned tiles for each player from the stream, and /* now read the assigned tiles for each player from the stream, and
remove them from the newly-created local pool. */ remove them from the newly-created local pool. */
@ -1755,6 +1821,23 @@ curTrayAsTexts( ServerCtxt* server, XP_U16 turn, const TrayTileSet* notInTray,
*nUsedP = nUsed; *nUsedP = nUsed;
} /* curTrayAsTexts */ } /* curTrayAsTexts */
/**
* Return true (after calling util_informPickTiles()) IFF allowPickTiles is
* TRUE and the tile set passed in is NULL. If it doesn't contain as many
* tiles as are needed that's cool: server code will later interpret that as
* meaning the remainder should be assigned randomly as usual.
*/
XP_Bool
server_askPickTiles( ServerCtxt* server, XP_U16 turn, TrayTileSet* newTiles,
XP_U16 nToPick )
{
XP_Bool asked = newTiles == NULL && server->vol.gi->allowPickTiles;
if ( asked ) {
informNeedPickTiles( server, XP_FALSE, turn, nToPick );
}
return asked;
}
/* Get tiles for one user. If picking is available, let user pick until /* Get tiles for one user. If picking is available, let user pick until
* cancels. Otherwise, and after cancel, pick for 'im. * cancels. Otherwise, and after cancel, pick for 'im.
*/ */
@ -1763,7 +1846,7 @@ fetchTiles( ServerCtxt* server, XP_U16 playerNum, XP_U16 nToFetch,
const TrayTileSet* tradedTiles, TrayTileSet* resultTiles ) const TrayTileSet* tradedTiles, TrayTileSet* resultTiles )
{ {
XP_Bool ask; XP_Bool ask;
XP_U16 nSoFar = 0; XP_U16 nSoFar = resultTiles->nTiles;
XP_U16 nLeft; XP_U16 nLeft;
PoolContext* pool = server->pool; PoolContext* pool = server->pool;
TrayTileSet oneTile; TrayTileSet oneTile;
@ -1796,17 +1879,17 @@ fetchTiles( ServerCtxt* server, XP_U16 playerNum, XP_U16 nToFetch,
#ifdef FEATURE_TRAY_EDIT /* good compiler would note ask==0, but... */ #ifdef FEATURE_TRAY_EDIT /* good compiler would note ask==0, but... */
/* First ask until cancelled */ /* First ask until cancelled */
for ( nSoFar = 0; ask && nSoFar < nToFetch; ) { for ( ; ask && nSoFar < nToFetch; ) {
const XP_UCHAR* texts[MAX_UNIQUE_TILES]; const XP_UCHAR* texts[MAX_UNIQUE_TILES];
Tile tiles[MAX_UNIQUE_TILES]; Tile tiles[MAX_UNIQUE_TILES];
XP_S16 chosen; XP_S16 chosen;
XP_U16 nUsed = MAX_UNIQUE_TILES; XP_U16 nUsed = MAX_UNIQUE_TILES;
// XP_ASSERT(0); /* should no longer happen!!! */
model_packTilesUtil( server->vol.model, pool, model_packTilesUtil( server->vol.model, pool,
XP_TRUE, &nUsed, texts, tiles ); XP_TRUE, &nUsed, texts, tiles );
chosen = util_userPickTileTray( server->vol.util, &pi, playerNum, chosen = PICKER_PICKALL; /*util_userPickTileTray( server->vol.util,
texts, nUsed ); &pi, playerNum, texts, nUsed );*/
if ( chosen == PICKER_PICKALL ) { if ( chosen == PICKER_PICKALL ) {
ask = XP_FALSE; ask = XP_FALSE;
@ -1846,6 +1929,18 @@ fetchTiles( ServerCtxt* server, XP_U16 playerNum, XP_U16 nToFetch,
resultTiles->nTiles = (XP_U8)nSoFar; resultTiles->nTiles = (XP_U8)nSoFar;
} /* fetchTiles */ } /* fetchTiles */
static void
makePoolOnce( ServerCtxt* server )
{
ModelCtxt* model = server->vol.model;
XP_ASSERT( model_getDictionary(model) != NULL );
if ( server->pool == NULL ) {
server->pool = pool_make( MPPARM_NOCOMMA(server->mpool) );
XP_STATUSF( "initing pool" );
pool_initFromDict( server->pool, model_getDictionary(model));
}
}
static void static void
assignTilesToAll( ServerCtxt* server ) assignTilesToAll( ServerCtxt* server )
{ {
@ -1855,12 +1950,7 @@ assignTilesToAll( ServerCtxt* server )
XP_U16 nPlayers = server->vol.gi->nPlayers; XP_U16 nPlayers = server->vol.gi->nPlayers;
XP_ASSERT( server->vol.gi->serverRole != SERVER_ISCLIENT ); XP_ASSERT( server->vol.gi->serverRole != SERVER_ISCLIENT );
XP_ASSERT( model_getDictionary(model) != NULL ); makePoolOnce( server );
if ( server->pool == NULL ) {
server->pool = pool_make( MPPARM_NOCOMMA(server->mpool) );
XP_STATUSF( "initing pool" );
pool_initFromDict( server->pool, model_getDictionary(model));
}
XP_STATUSF( "assignTilesToAll" ); XP_STATUSF( "assignTilesToAll" );
@ -1871,9 +1961,11 @@ assignTilesToAll( ServerCtxt* server )
numAssigned = MAX_TRAY_TILES; numAssigned = MAX_TRAY_TILES;
} }
for ( ii = 0; ii < nPlayers; ++ii ) { for ( ii = 0; ii < nPlayers; ++ii ) {
TrayTileSet newTiles; if ( 0 == model_getNumTilesInTray( model, ii ) ) {
fetchTiles( server, ii, numAssigned, NULL, &newTiles ); TrayTileSet newTiles = {0};
model_assignPlayerTiles( model, ii, &newTiles ); fetchTiles( server, ii, numAssigned, NULL, &newTiles );
model_assignPlayerTiles( model, ii, &newTiles );
}
sortTilesIf( server, ii ); sortTilesIf( server, ii );
} }
@ -2345,16 +2437,20 @@ reflectMove( ServerCtxt* server, XWStreamCtxt* stream )
* if it was legal -- but only if DISALLOW is set. * if it was legal -- but only if DISALLOW is set.
*/ */
XP_Bool XP_Bool
server_commitMove( ServerCtxt* server ) server_commitMove( ServerCtxt* server, TrayTileSet* newTilesP )
{ {
XP_S16 turn = server->nv.currentTurn; XP_S16 turn = server->nv.currentTurn;
ModelCtxt* model = server->vol.model; ModelCtxt* model = server->vol.model;
CurGameInfo* gi = server->vol.gi; CurGameInfo* gi = server->vol.gi;
TrayTileSet newTiles; TrayTileSet newTiles = {0};
XP_U16 nTilesMoved; XP_U16 nTilesMoved;
XP_Bool isLegalMove = XP_TRUE; XP_Bool isLegalMove = XP_TRUE;
XP_Bool isClient = gi->serverRole == SERVER_ISCLIENT; XP_Bool isClient = gi->serverRole == SERVER_ISCLIENT;
if ( !!newTilesP ) {
newTiles = *newTilesP;
}
#ifdef DEBUG #ifdef DEBUG
if ( LP_IS_ROBOT( &gi->players[turn] ) ) { if ( LP_IS_ROBOT( &gi->players[turn] ) ) {
XP_ASSERT( model_checkMoveLegal( model, turn, (XWStreamCtxt*)NULL, XP_ASSERT( model_checkMoveLegal( model, turn, (XWStreamCtxt*)NULL,
@ -2408,9 +2504,13 @@ server_commitMove( ServerCtxt* server )
} /* server_commitMove */ } /* server_commitMove */
XP_Bool XP_Bool
server_commitTrade( ServerCtxt* server, const TrayTileSet* oldTiles ) server_commitTrade( ServerCtxt* server, const TrayTileSet* oldTiles,
TrayTileSet* newTilesP )
{ {
TrayTileSet newTiles; TrayTileSet newTiles = {0};
if ( !!newTilesP ) {
newTiles = *newTilesP;
}
XP_U16 turn = server->nv.currentTurn; XP_U16 turn = server->nv.currentTurn;
fetchTiles( server, turn, oldTiles->nTiles, oldTiles, &newTiles ); fetchTiles( server, turn, oldTiles->nTiles, oldTiles, &newTiles );

View file

@ -102,12 +102,18 @@ XP_U32 server_getLastMoveTime( const ServerCtxt* server );
/* Signed in case no dictionary available */ /* Signed in case no dictionary available */
XP_S16 server_countTilesInPool( ServerCtxt* server ); XP_S16 server_countTilesInPool( ServerCtxt* server );
XP_Bool server_askPickTiles( ServerCtxt* server, XP_U16 player,
TrayTileSet* newTiles, XP_U16 nToPick );
void server_tilesPicked( ServerCtxt* server, XP_U16 player,
const TrayTileSet* newTiles );
XP_U16 server_getPendingRegs( const ServerCtxt* server ); XP_U16 server_getPendingRegs( const ServerCtxt* server );
XP_Bool server_do( ServerCtxt* server ); XP_Bool server_do( ServerCtxt* server );
XP_Bool server_commitMove( ServerCtxt* server ); XP_Bool server_commitMove( ServerCtxt* server, TrayTileSet* newTiles );
XP_Bool server_commitTrade( ServerCtxt* server, const TrayTileSet* oldTiles ); XP_Bool server_commitTrade( ServerCtxt* server, const TrayTileSet* oldTiles,
TrayTileSet* newTiles );
/* call this when user wants to end the game */ /* call this when user wants to end the game */
void server_endGame( ServerCtxt* server ); void server_endGame( ServerCtxt* server );

View file

@ -460,7 +460,7 @@ handleActionInTray( BoardCtxt* board, XP_S16 index, XP_Bool onDivider )
} }
#endif #endif
} else if ( index == -(MAX_TRAY_TILES) ) { /* pending score tile */ } else if ( index == -(MAX_TRAY_TILES) ) { /* pending score tile */
result = board_commitTurn( board, XP_FALSE, XP_FALSE ); result = board_commitTurn( board, XP_FALSE, XP_FALSE, NULL );
#if defined XWFEATURE_TRAYUNDO_ALL #if defined XWFEATURE_TRAYUNDO_ALL
} else if ( index < 0 ) { /* other empty area */ } else if ( index < 0 ) { /* other empty area */
/* it better be true */ /* it better be true */

View file

@ -101,14 +101,14 @@ typedef struct UtilVtable {
void (*m_util_notifyMove)( XW_UtilCtxt* uc, XWStreamCtxt* stream ); void (*m_util_notifyMove)( XW_UtilCtxt* uc, XWStreamCtxt* stream );
void (*m_util_notifyTrade)( XW_UtilCtxt* uc, const XP_UCHAR** tiles, void (*m_util_notifyTrade)( XW_UtilCtxt* uc, const XP_UCHAR** tiles,
XP_U16 nTiles ); XP_U16 nTiles );
/* return of < 0 means computer should pick */
void (*m_util_notifyPickTileBlank)( XW_UtilCtxt* uc, XP_U16 playerNum, void (*m_util_notifyPickTileBlank)( XW_UtilCtxt* uc, XP_U16 playerNum,
XP_U16 col, XP_U16 row, XP_U16 col, XP_U16 row,
const XP_UCHAR** tileFaces, const XP_UCHAR** tileFaces,
XP_U16 nTiles ); XP_U16 nTiles );
XP_S16 (*m_util_userPickTileTray)( XW_UtilCtxt* uc, const PickInfo* pi, void (*m_util_informNeedPickTiles)( XW_UtilCtxt* uc, XP_Bool isInitial,
XP_U16 playerNum, XP_U16 player, XP_U16 nToPick,
const XP_UCHAR** texts, XP_U16 nTiles ); XP_U16 nFaces, const XP_UCHAR** faces,
const XP_U16* counts );
void (*m_util_informNeedPassword)( XW_UtilCtxt* uc, XP_U16 playerNum, void (*m_util_informNeedPassword)( XW_UtilCtxt* uc, XP_U16 playerNum,
const XP_UCHAR* name ); const XP_UCHAR* name );
@ -236,9 +236,11 @@ struct XW_UtilCtxt {
(uc)->vtable->m_util_notifyTrade((uc), (tx), (nt)) (uc)->vtable->m_util_notifyTrade((uc), (tx), (nt))
#define util_notifyPickTileBlank( uc, c, r, n, tx, nt ) \ #define util_notifyPickTileBlank( uc, c, r, n, tx, nt ) \
(uc)->vtable->m_util_notifyPickTileBlank( (uc), (c), (r), (n), (tx), (nt) ) (uc)->vtable->m_util_notifyPickTileBlank( (uc), (c), (r), (n), (tx), (nt) )
#define util_userPickTileTray( uc, w, n, tx, nt ) \
(uc)->vtable->m_util_userPickTileTray( (uc), (w), (n), (tx), (nt) ) #define util_informNeedPickTiles( uc, ii, pl, np, nt, fc, cn ) \
(uc)->vtable->m_util_informNeedPickTiles( (uc), (ii), (pl), (np), (nt), (fc), (cn) )
#define util_informNeedPassword( uc, pn, n ) \ #define util_informNeedPassword( uc, pn, n ) \
(uc)->vtable->m_util_informNeedPassword( (uc), (pn), (n) ) (uc)->vtable->m_util_informNeedPassword( (uc), (pn), (n) )

View file

@ -243,22 +243,26 @@ curses_util_notifyPickTileBlank( XW_UtilCtxt* uc, XP_U16 playerNum,
// return index; // return index;
} /* util_userPickTile */ } /* util_userPickTile */
static XP_S16 static void
curses_util_userPickTileTray( XW_UtilCtxt* uc, const PickInfo* XP_UNUSED(pi), curses_util_informNeedPickTiles( XW_UtilCtxt* XP_UNUSED(uc),
XP_U16 playerNum, const XP_UCHAR** texts, XP_Bool XP_UNUSED(isInitial),
XP_U16 nTiles ) XP_U16 XP_UNUSED(player),
XP_U16 XP_UNUSED(nToPick),
XP_U16 XP_UNUSED(nFaces),
const XP_UCHAR** XP_UNUSED(faces),
const XP_U16* XP_UNUSED(counts) )
{ {
CursesAppGlobals* globals = (CursesAppGlobals*)uc->closure; /* CursesAppGlobals* globals = (CursesAppGlobals*)uc->closure; */
char query[128]; /* char query[128]; */
XP_S16 index; /* XP_S16 index; */
char* playerName = globals->cGlobals.gi->players[playerNum].name; /* char* playerName = globals->cGlobals.gi->players[playerNum].name; */
snprintf( query, sizeof(query), /* snprintf( query, sizeof(query), */
"Pick tile for %s! (Tab or type letter to select " /* "Pick tile for %s! (Tab or type letter to select " */
"then hit <cr>.)", playerName ); /* "then hit <cr>.)", playerName ); */
index = curses_askLetter( globals, query, texts, nTiles ); /* index = curses_askLetter( globals, query, texts, nTiles ); */
return index; /* return index; */
} /* util_userPickTile */ } /* util_userPickTile */
static void static void
@ -620,7 +624,7 @@ static XP_Bool
handleCommit( CursesAppGlobals* globals ) handleCommit( CursesAppGlobals* globals )
{ {
globals->doDraw = board_commitTurn( globals->cGlobals.game.board, globals->doDraw = board_commitTurn( globals->cGlobals.game.board,
XP_FALSE, XP_FALSE ); XP_FALSE, XP_FALSE, NULL );
return XP_TRUE; return XP_TRUE;
} /* handleCommit */ } /* handleCommit */
@ -1456,7 +1460,7 @@ setupCursesUtilCallbacks( CursesAppGlobals* globals, XW_UtilCtxt* util )
util->vtable->m_util_notifyMove = curses_util_notifyMove; util->vtable->m_util_notifyMove = curses_util_notifyMove;
util->vtable->m_util_notifyTrade = curses_util_notifyTrade; util->vtable->m_util_notifyTrade = curses_util_notifyTrade;
util->vtable->m_util_notifyPickTileBlank = curses_util_notifyPickTileBlank; util->vtable->m_util_notifyPickTileBlank = curses_util_notifyPickTileBlank;
util->vtable->m_util_userPickTileTray = curses_util_userPickTileTray; util->vtable->m_util_informNeedPickTiles = curses_util_informNeedPickTiles;
util->vtable->m_util_trayHiddenChange = curses_util_trayHiddenChange; util->vtable->m_util_trayHiddenChange = curses_util_trayHiddenChange;
util->vtable->m_util_informMove = curses_util_informMove; util->vtable->m_util_informMove = curses_util_informMove;
util->vtable->m_util_informUndo = curses_util_informUndo; util->vtable->m_util_informUndo = curses_util_informUndo;

View file

@ -1445,7 +1445,8 @@ handle_trade_button( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
static void static void
handle_done_button( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals ) handle_done_button( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
{ {
if ( board_commitTurn( globals->cGlobals.game.board, XP_FALSE, XP_FALSE ) ) { if ( board_commitTurn( globals->cGlobals.game.board, XP_FALSE,
XP_FALSE, NULL ) ) {
board_draw( globals->cGlobals.game.board ); board_draw( globals->cGlobals.game.board );
disenable_buttons( globals ); disenable_buttons( globals );
} }
@ -1543,7 +1544,8 @@ handle_hide_button( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
static void static void
handle_commit_button( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals ) handle_commit_button( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
{ {
if ( board_commitTurn( globals->cGlobals.game.board, XP_FALSE, XP_FALSE ) ) { if ( board_commitTurn( globals->cGlobals.game.board, XP_FALSE,
XP_FALSE, NULL ) ) {
board_draw( globals->cGlobals.game.board ); board_draw( globals->cGlobals.game.board );
} }
} /* handle_commit_button */ } /* handle_commit_button */
@ -1693,7 +1695,7 @@ ask_blank( gpointer data )
XP_UCHAR* name = globals->cGlobals.gi->players[cGlobals->selPlayer].name; XP_UCHAR* name = globals->cGlobals.gi->players[cGlobals->selPlayer].name;
XP_S16 result = gtkletterask( NULL, XP_FALSE, name, XP_S16 result = gtkletterask( NULL, XP_FALSE, name,
cGlobals->nTiles, cGlobals->tiles ); cGlobals->nTiles, cGlobals->tiles, NULL );
for ( int ii = 0; ii < cGlobals->nTiles; ++ii ) { for ( int ii = 0; ii < cGlobals->nTiles; ++ii ) {
g_free( (gpointer)cGlobals->tiles[ii] ); g_free( (gpointer)cGlobals->tiles[ii] );
@ -1725,18 +1727,78 @@ gtk_util_notifyPickTileBlank( XW_UtilCtxt* uc, XP_U16 playerNum, XP_U16 col,
(void)g_idle_add( ask_blank, globals ); (void)g_idle_add( ask_blank, globals );
} }
static XP_S16 static gint
gtk_util_userPickTileTray( XW_UtilCtxt* uc, const PickInfo* pi, ask_tiles( gpointer data )
XP_U16 playerNum, const XP_UCHAR** texts,
XP_U16 nTiles )
{ {
XP_S16 chosen; GtkGameGlobals* globals = (GtkGameGlobals*)data;
GtkGameGlobals* globals = (GtkGameGlobals*)uc->closure; CommonGlobals* cGlobals = &globals->cGlobals;
XP_UCHAR* name = globals->cGlobals.gi->players[playerNum].name;
chosen = gtkletterask( pi, XP_TRUE, name, nTiles, texts ); TrayTileSet newTiles = {0};
return chosen; XP_UCHAR* name = cGlobals->gi->players[cGlobals->selPlayer].name;
} /* gtk_util_userPickTile */ for ( XP_Bool done = XP_FALSE; !done; ) {
XP_S16 picked = gtkletterask( &newTiles, XP_TRUE, name,
cGlobals->nTiles, cGlobals->tiles,
cGlobals->tileCounts );
switch ( picked ) {
case PICKER_PICKALL:
done = XP_TRUE;
break;
case PICKER_BACKUP:
if ( newTiles.nTiles > 0 ) {
Tile backed = newTiles.tiles[--newTiles.nTiles];
++cGlobals->tileCounts[backed];
}
break;
default:
XP_ASSERT( picked >= 0 && picked < cGlobals->nTiles );
--cGlobals->tileCounts[picked];
newTiles.tiles[newTiles.nTiles++] = picked;
done = newTiles.nTiles == cGlobals->nToPick;
break;
}
}
for ( int ii = 0; ii < cGlobals->nTiles; ++ii ) {
g_free( (gpointer)cGlobals->tiles[ii] );
}
BoardCtxt* board = cGlobals->game.board;
XP_Bool draw = XP_TRUE;
if ( cGlobals->pickIsInitial ) {
server_tilesPicked( cGlobals->game.server, cGlobals->selPlayer,
&newTiles );
} else {
draw = board_commitTurn( cGlobals->game.board, XP_TRUE, XP_TRUE,
&newTiles );
}
if ( draw ) {
board_draw( board );
}
return 0;
}
static void
gtk_util_informNeedPickTiles( XW_UtilCtxt* uc, XP_Bool isInitial,
XP_U16 player, XP_U16 nToPick,
XP_U16 nFaces, const XP_UCHAR** faces,
const XP_U16* counts )
{
GtkGameGlobals* globals = (GtkGameGlobals*)uc->closure;
CommonGlobals* cGlobals = &globals->cGlobals;
cGlobals->selPlayer = player;
cGlobals->pickIsInitial = isInitial;
cGlobals->nToPick = nToPick;
cGlobals->nTiles = nFaces;
for ( int ii = 0; ii < nFaces; ++ii ) {
cGlobals->tiles[ii] = g_strdup( faces[ii] );
cGlobals->tileCounts[ii] = counts[ii];
}
(void)g_idle_add( ask_tiles, globals );
} /* gtk_util_informNeedPickTiles */
static gint static gint
ask_password( gpointer data ) ask_password( gpointer data )
@ -2125,7 +2187,7 @@ ask_bad_words( gpointer data )
if ( GTK_RESPONSE_YES == gtkask( globals->window, cGlobals->question, if ( GTK_RESPONSE_YES == gtkask( globals->window, cGlobals->question,
GTK_BUTTONS_YES_NO, NULL ) ) { GTK_BUTTONS_YES_NO, NULL ) ) {
board_commitTurn( cGlobals->game.board, XP_TRUE, XP_FALSE ); board_commitTurn( cGlobals->game.board, XP_TRUE, XP_FALSE, NULL );
} }
return 0; return 0;
} }
@ -2275,7 +2337,7 @@ ask_move( gpointer data )
gint chosen = gtkask( globals->window, cGlobals->question, buttons, NULL ); gint chosen = gtkask( globals->window, cGlobals->question, buttons, NULL );
if ( GTK_RESPONSE_OK == chosen || chosen == GTK_RESPONSE_YES ) { if ( GTK_RESPONSE_OK == chosen || chosen == GTK_RESPONSE_YES ) {
BoardCtxt* board = cGlobals->game.board; BoardCtxt* board = cGlobals->game.board;
if ( board_commitTurn( board, XP_TRUE, XP_TRUE ) ) { if ( board_commitTurn( board, XP_TRUE, XP_TRUE, NULL ) ) {
board_draw( board ); board_draw( board );
} }
} }
@ -2314,7 +2376,7 @@ ask_trade( gpointer data )
cGlobals->question, cGlobals->question,
GTK_BUTTONS_YES_NO, NULL ) ) { GTK_BUTTONS_YES_NO, NULL ) ) {
BoardCtxt* board = cGlobals->game.board; BoardCtxt* board = cGlobals->game.board;
if ( board_commitTurn( board, XP_TRUE, XP_TRUE ) ) { if ( board_commitTurn( board, XP_TRUE, XP_TRUE, NULL ) ) {
board_draw( board ); board_draw( board );
} }
} }
@ -2466,7 +2528,7 @@ setupGtkUtilCallbacks( GtkGameGlobals* globals, XW_UtilCtxt* util )
util->vtable->m_util_notifyTrade = gtk_util_notifyTrade; util->vtable->m_util_notifyTrade = gtk_util_notifyTrade;
util->vtable->m_util_getVTManager = gtk_util_getVTManager; util->vtable->m_util_getVTManager = gtk_util_getVTManager;
util->vtable->m_util_notifyPickTileBlank = gtk_util_notifyPickTileBlank; util->vtable->m_util_notifyPickTileBlank = gtk_util_notifyPickTileBlank;
util->vtable->m_util_userPickTileTray = gtk_util_userPickTileTray; util->vtable->m_util_informNeedPickTiles = gtk_util_informNeedPickTiles;
util->vtable->m_util_informNeedPassword = gtk_util_informNeedPassword; util->vtable->m_util_informNeedPassword = gtk_util_informNeedPassword;
util->vtable->m_util_trayHiddenChange = gtk_util_trayHiddenChange; util->vtable->m_util_trayHiddenChange = gtk_util_trayHiddenChange;
util->vtable->m_util_yOffsetChange = gtk_util_yOffsetChange; util->vtable->m_util_yOffsetChange = gtk_util_yOffsetChange;

View file

@ -42,8 +42,8 @@ abort_button_event( GtkWidget* XP_UNUSED(widget), gpointer XP_UNUSED(closure) )
#define BUTTONS_PER_ROW 13 #define BUTTONS_PER_ROW 13
XP_S16 XP_S16
gtkletterask( const PickInfo* pi, XP_Bool forTray, const XP_UCHAR* name, gtkletterask( const TrayTileSet* curPick, XP_Bool forTray, const XP_UCHAR* name,
XP_U16 nTiles, const XP_UCHAR** texts ) XP_U16 nTiles, const XP_UCHAR** texts, const XP_U16* counts )
{ {
GtkWidget* dialog; GtkWidget* dialog;
GtkWidget* label; GtkWidget* label;
@ -70,7 +70,9 @@ gtkletterask( const PickInfo* pi, XP_Bool forTray, const XP_UCHAR* name,
gtk_box_pack_start( GTK_BOX(hbox), button, FALSE, TRUE, 0 ); gtk_box_pack_start( GTK_BOX(hbox), button, FALSE, TRUE, 0 );
g_signal_connect( button, "clicked", g_signal_connect( button, "clicked",
G_CALLBACK(set_bool_and_quit), &results[ii] ); G_CALLBACK(set_bool_and_quit), &results[ii] );
gtk_widget_show( button );
/* disable if we don't have any tiles! */
gtk_widget_set_sensitive( button, !counts || counts[ii] > 0 );
if ( ii+1 == nTiles || (ii % BUTTONS_PER_ROW == 0) ) { if ( ii+1 == nTiles || (ii % BUTTONS_PER_ROW == 0) ) {
gtk_widget_show( hbox ); gtk_widget_show( hbox );
@ -118,9 +120,10 @@ gtkletterask( const PickInfo* pi, XP_Bool forTray, const XP_UCHAR* name,
char curTilesBuf[64]; char curTilesBuf[64];
int len = snprintf( curTilesBuf, sizeof(curTilesBuf), "%s", int len = snprintf( curTilesBuf, sizeof(curTilesBuf), "%s",
"Tiles so far: " ); "Tiles so far: " );
for ( ii = 0; ii < pi->nCurTiles; ++ii ) { for ( ii = 0; ii < curPick->nTiles; ++ii ) {
Tile tile = curPick->tiles[ii];
len += snprintf( &curTilesBuf[len], sizeof(curTilesBuf) - len, "%s ", len += snprintf( &curTilesBuf[len], sizeof(curTilesBuf) - len, "%s ",
pi->curTiles[ii] ); texts[tile] );
} }
GtkWidget* curTilesLabel = gtk_label_new( curTilesBuf ); GtkWidget* curTilesLabel = gtk_label_new( curTilesBuf );

View file

@ -26,9 +26,10 @@
#include "gtkboard.h" #include "gtkboard.h"
XP_S16 gtkletterask( const PickInfo* pi, XP_Bool forTray, XP_S16 gtkletterask( const TrayTileSet* curPick, XP_Bool forTray,
const XP_UCHAR* name, const XP_UCHAR* name,
XP_U16 nTiles, const XP_UCHAR** texts ); XP_U16 nTiles, const XP_UCHAR** texts,
const XP_U16* counts );
#endif /* _GTKLETTERASK_H_ */ #endif /* _GTKLETTERASK_H_ */

View file

@ -220,10 +220,13 @@ struct CommonGlobals {
char question[256*4]; char question[256*4];
const XP_UCHAR* askPassName; const XP_UCHAR* askPassName;
XP_U16 nTiles; XP_U16 nTiles;
XP_U16 nToPick;
XP_U16 blankCol; XP_U16 blankCol;
XP_U16 blankRow; XP_U16 blankRow;
XP_Bool pickIsInitial;
const XP_UCHAR* tiles[MAX_UNIQUE_TILES]; const XP_UCHAR* tiles[MAX_UNIQUE_TILES];
XP_U16 tileCounts[MAX_UNIQUE_TILES];
#ifdef XWFEATURE_RELAY #ifdef XWFEATURE_RELAY
int relaySocket; /* tcp connection to relay */ int relaySocket; /* tcp connection to relay */