diff --git a/xwords4/android/XWords4/jni/LocalizedStrIncludes.h b/xwords4/android/XWords4/jni/LocalizedStrIncludes.h
index 63edb5f02..7dd9819fd 100644
--- a/xwords4/android/XWords4/jni/LocalizedStrIncludes.h
+++ b/xwords4/android/XWords4/jni/LocalizedStrIncludes.h
@@ -1,4 +1,4 @@
-/* Keep these in sync with the constants in XW_UtilCtxt.java */
+/* Keep these in sync with the constants in UtilCtxt.java */
#ifndef _LOCALIZEDSTRINCLUDES_H_
@@ -24,10 +24,8 @@
# define STRD_TRADED 18
# define STR_LOSTTURN 19
# define STR_COMMIT_CONFIRM 20
-# define STR_LOCAL_NAME 21
-# define STR_NONLOCAL_NAME 22
-# define STR_BONUS_ALL 23
-# define STRD_TURN_SCORE 24
+# define STR_BONUS_ALL 21
+# define STRD_TURN_SCORE 22
-# define N_AND_USER_STRINGS 24
+# define N_AND_USER_STRINGS 22
#endif
diff --git a/xwords4/android/XWords4/jni/anddict.c b/xwords4/android/XWords4/jni/anddict.c
index 84ce56000..4b93fc10c 100644
--- a/xwords4/android/XWords4/jni/anddict.c
+++ b/xwords4/android/XWords4/jni/anddict.c
@@ -513,6 +513,7 @@ makeDict( MPFORMAL JNIEnv *env, JNIUtilCtxt* jniutil, jstring jname,
/* copy the name */
anddict->super.name = getStringCopy( MPPARM(mpool) env, jname );
+ XP_LOGF( "%s: setting dict name: %s", __func__, anddict->super.name );
anddict->super.langName = getStringCopy( MPPARM(mpool) env, jlangname );
return (DictionaryCtxt*)anddict;
diff --git a/xwords4/android/XWords4/jni/utilwrapper.c b/xwords4/android/XWords4/jni/utilwrapper.c
index 8c1cd0979..396e78122 100644
--- a/xwords4/android/XWords4/jni/utilwrapper.c
+++ b/xwords4/android/XWords4/jni/utilwrapper.c
@@ -399,12 +399,10 @@ and_util_bonusSquareHeld( XW_UtilCtxt* uc, XWBonusType bonus )
}
static void
-and_util_playerScoreHeld( XW_UtilCtxt* uc, const XP_UCHAR* txt )
+and_util_playerScoreHeld( XW_UtilCtxt* uc, XP_U16 player )
{
- UTIL_CBK_HEADER( "playerScoreHeld", "(Ljava/lang/String;)V" );
- jstring jmsg = (*env)->NewStringUTF( env, txt );
- (*env)->CallVoidMethod( env, util->jutil, mid, jmsg );
- (*env)->DeleteLocalRef( env, jmsg );
+ UTIL_CBK_HEADER( "playerScoreHeld", "(I)V" );
+ (*env)->CallVoidMethod( env, util->jutil, mid, player );
UTIL_CBK_TAIL();
}
#endif
diff --git a/xwords4/android/XWords4/jni/xwjni.c b/xwords4/android/XWords4/jni/xwjni.c
index 572a86fb8..8e660940a 100644
--- a/xwords4/android/XWords4/jni/xwjni.c
+++ b/xwords4/android/XWords4/jni/xwjni.c
@@ -907,6 +907,25 @@ Java_org_eehouse_android_xw4_jni_XwJNI_model_1getNMoves
return result;
}
+JNIEXPORT jstring JNICALL
+Java_org_eehouse_android_xw4_jni_XwJNI_model_1getPlayersLastScore
+(JNIEnv* env, jclass C, jint gamePtr, jint player )
+{
+ jstring result = NULL;
+ XWJNI_START();
+ XP_ASSERT( !!state->game.model );
+ XP_UCHAR buf[64];
+ XP_U16 buflen = sizeof(buf);
+ if ( !model_getPlayersLastScore( state->game.model, player, buf,
+ &buflen ) ) {
+ buf[0] = '\0';
+ }
+ result = (*env)->NewStringUTF( env, buf );
+ (*env)->DeleteLocalRef( env, result );
+ XWJNI_END();
+ return result;
+}
+
JNIEXPORT jstring JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_server_1writeFinalScores
( JNIEnv* env, jclass C, jint gamePtr )
diff --git a/xwords4/android/XWords4/res/values-ca/strings.xml b/xwords4/android/XWords4/res/values-ca/strings.xml
index eb266c4a8..9e7ed2fec 100644
--- a/xwords4/android/XWords4/res/values-ca/strings.xml
+++ b/xwords4/android/XWords4/res/values-ca/strings.xml
@@ -98,8 +98,6 @@
Torn perdut
Voleu fer la jugada?\n
- %s
- %s (remot)
Bonificació per usar totes les fitxes: 50\n
Puntuació del torn: %d\n
@@ -110,7 +108,6 @@
Les fitxes noves han de tocar alguna de les que ja estan jugades al tauler (o la casella central en la primera jugada).
No podeu fer això, no és el vostre torn!
No podeu mirar les fitxes del robot!
- Recolliu les fitxes posades al tauler abans de canviar fitxer.
Hi ha poques fitxes al saquet i ja no podeu canviar-les.
L\'assignació de fitxers no es pot desfer.
Les pistes es troben inhabilitades per a aquesta partida. Habiliteu-les per a una partida nova al menú Preferències.
diff --git a/xwords4/android/XWords4/res/values-cs/strings.xml b/xwords4/android/XWords4/res/values-cs/strings.xml
index 464b49b32..e6298f05e 100644
--- a/xwords4/android/XWords4/res/values-cs/strings.xml
+++ b/xwords4/android/XWords4/res/values-cs/strings.xml
@@ -97,8 +97,6 @@
%1$s:%2$d
Tah byl ztracen
Potvrdit tento tah?\n
- %s
- %s (vzdálený)
Bonus za využití všech kamenů: 50\n
Skóre za tah: %d\n
@@ -109,7 +107,6 @@
Nové kameny musí navazovat na již položené kameny (nebo se s nimi křížit).
To nemůžete udělat; není to váš tah!
Nenakukujte na kameny robota!
- Vzdálený hráč hrál s kameny před výměnou.
Zbývá příliš málo kamenů pro výměnu.
Přiřazení kamene nemůže být vráceno zpět.
Pro tuto hru je zakázána nápověda. Pro novou hru ji můžete povolit v Předvolbách.
diff --git a/xwords4/android/XWords4/res/values-sk/strings.xml b/xwords4/android/XWords4/res/values-sk/strings.xml
index 8e22fb456..15c7d90dd 100644
--- a/xwords4/android/XWords4/res/values-sk/strings.xml
+++ b/xwords4/android/XWords4/res/values-sk/strings.xml
@@ -96,8 +96,6 @@
%1$s:%2$d
Ťah bol stratený
Potvrdiť tento ťah?\n
- %s
- %s (vzdialený)
Bonus za využitie všetkých kameňov: 50\n
Skóre za ťah: %d\n
@@ -108,7 +106,6 @@
Nové kamene musia naväzovať na už položené kamene (alebo sa s nimi krížiť).
To nemôžete spraviť; nie je to váš ťah!
Nenakúkajte na kamene robota!
- Vzdialený hráč hral s kameňmi pred výmenou.
Zostáva príliž málo kameňov na výmenu.
Priradenie kameňa nemôže byť vrátené spät.
Pre túto hru je zakázaná nápoveda. Pre novú hru ju môžete povoliť v Predvolbách.
diff --git a/xwords4/android/XWords4/res/values/common_rsrc.xml b/xwords4/android/XWords4/res/values/common_rsrc.xml
index 178645034..1de3efa04 100644
--- a/xwords4/android/XWords4/res/values/common_rsrc.xml
+++ b/xwords4/android/XWords4/res/values/common_rsrc.xml
@@ -91,6 +91,7 @@
Source version id
Relay game port
Relay device port
+ %1$s/%2$s
diff --git a/xwords4/android/XWords4/res/values/strings.xml b/xwords4/android/XWords4/res/values/strings.xml
index 76447c1f0..5e5aee9c5 100644
--- a/xwords4/android/XWords4/res/values/strings.xml
+++ b/xwords4/android/XWords4/res/values/strings.xml
@@ -479,6 +479,10 @@
bonus square. Triple-word -->
3W
+
+ (No moves yet)
+
- Traded %d
+ Exchanged %d tiles
diff --git a/xwords4/android/XWords4/res/xml/xwprefs.xml b/xwords4/android/XWords4/res/xml/xwprefs.xml
index e5e700cc3..c961870b2 100644
--- a/xwords4/android/XWords4/res/xml/xwprefs.xml
+++ b/xwords4/android/XWords4/res/xml/xwprefs.xml
@@ -14,11 +14,13 @@
adapter =
+ (ArrayAdapter)spinner.getAdapter();
+
+ if ( position < adapter.getCount() ) {
+ lp.dictName = adapter.getItem(position);
}
lp.setIsRobot( Utils.getChecked( dialog, R.id.robot_check ) );
@@ -453,7 +457,9 @@ public class GameConfig extends XWActivity
handleLockedChange();
}
- m_gi = new CurGameInfo( this, m_giOrig );
+ if ( null == m_gi ) {
+ m_gi = new CurGameInfo( this, m_giOrig );
+ }
m_carOrig = new CommsAddrRec( this );
if ( XwJNI.game_hasComms( gamePtr ) ) {
@@ -493,7 +499,7 @@ public class GameConfig extends XWActivity
adjustConnectStuff();
}
- loadPlayers();
+ loadPlayersList();
configLangSpinner();
m_phoniesSpinner.setSelection( m_gi.phoniesAction.ordinal() );
@@ -530,7 +536,7 @@ public class GameConfig extends XWActivity
public void deleteCalled( int myPosition, final String name )
{
if ( m_gi.delete( myPosition ) ) {
- loadPlayers();
+ loadPlayersList();
}
}
@@ -560,11 +566,11 @@ public class GameConfig extends XWActivity
int curIndex = m_gi.nPlayers;
if ( curIndex < CurGameInfo.MAX_NUM_PLAYERS ) {
m_gi.addPlayer(); // ups nPlayers
- loadPlayers();
+ loadPlayersList();
}
} else if ( m_jugglePlayersButton == view ) {
m_gi.juggle();
- loadPlayers();
+ loadPlayersList();
} else if ( m_joinPublicCheck == view ) {
adjustConnectStuff();
} else if ( m_gameLockedCheck == view ) {
@@ -617,11 +623,11 @@ public class GameConfig extends XWActivity
return consumed || super.onKeyDown( keyCode, event );
}
- private void loadPlayers()
+ private void loadPlayersList()
{
m_playerLayout.removeAllViews();
- String[] names = m_gi.visibleNames();
+ String[] names = m_gi.visibleNames( false );
// only enable delete if one will remain (or two if networked)
boolean canDelete = names.length > 2
|| (m_notNetworkedGame && names.length > 1);
@@ -667,7 +673,7 @@ public class GameConfig extends XWActivity
showDialog( FORCE_REMOTE );
}
adjustPlayersLabel();
- } // loadPlayers
+ } // loadPlayersList
private String[] buildListWithBrowse( String[] input )
{
@@ -683,7 +689,7 @@ public class GameConfig extends XWActivity
return result;
}
- private void configDictSpinner( final Dialog dialog, final LocalPlayer lp )
+ private void configDictSpinner( final Dialog dialog, LocalPlayer lp )
{
Spinner dictsSpinner =
(Spinner)dialog.findViewById( R.id.dict_spinner );
@@ -703,8 +709,6 @@ public class GameConfig extends XWActivity
if ( chosen.equals( m_browseText ) ) {
DictsActivity.launchAndDownload( GameConfig.this,
m_gi.dictLang );
- } else {
- lp.dictName = chosen;
}
}
diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java
index d202df276..b3b79dd1f 100644
--- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java
+++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java
@@ -460,6 +460,23 @@ public class GameUtils {
}
}
+ public static String[] dictNames( Context context, long rowid,
+ int[] missingLang )
+ {
+ byte[] stream = savedGame( context, rowid );
+ CurGameInfo gi = new CurGameInfo( context );
+ XwJNI.gi_from_stream( gi, stream );
+ if ( null != missingLang ) {
+ missingLang[0] = gi.dictLang;
+ }
+ return gi.dictNames();
+ }
+
+ public static String[] dictNames( Context context, long rowid )
+ {
+ return dictNames( context, rowid, null );
+ }
+
public static boolean gameDictsHere( Context context, long rowid )
{
return gameDictsHere( context, rowid, null, null );
@@ -471,17 +488,10 @@ public class GameUtils {
String[][] missingNames,
int[] missingLang )
{
- byte[] stream = savedGame( context, rowid );
- CurGameInfo gi = new CurGameInfo( context );
- XwJNI.gi_from_stream( gi, stream );
- final String[] dictNames = gi.dictNames();
+ String[] dictNames = dictNames( context, rowid, missingLang );
HashSet missingSet;
String[] installed = dictList( context );
- if ( null != missingLang ) {
- missingLang[0] = gi.dictLang;
- }
-
missingSet = new HashSet( Arrays.asList( dictNames ) );
missingSet.remove( null );
missingSet.removeAll( Arrays.asList(installed) );
diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/CurGameInfo.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/CurGameInfo.java
index bac12f374..19f5f1e9b 100644
--- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/CurGameInfo.java
+++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/CurGameInfo.java
@@ -235,18 +235,22 @@ public class CurGameInfo {
return !consistent;
}
- public String[] visibleNames()
+ public String[] visibleNames( boolean withDicts )
{
+ String nameFmt = withDicts? m_context.getString( R.string.name_dict_fmt )
+ : "%s";
String[] names = new String[nPlayers];
for ( int ii = 0; ii < nPlayers; ++ii ) {
LocalPlayer lp = players[ii];
if ( lp.isLocal || serverRole == DeviceRole.SERVER_STANDALONE ) {
+ String name;
if ( lp.isRobot() ) {
String format = m_context.getString( R.string.robot_namef );
- names[ii] = String.format( format, lp.name );
+ name = String.format( format, lp.name );
} else {
- names[ii] = lp.name;
+ name = lp.name;
}
+ names[ii] = String.format( nameFmt, name, dictName(lp) );
} else {
names[ii] = m_context.getString( R.string.guest_name );
}
diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/GameSummary.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/GameSummary.java
index d6953d405..0244bc355 100644
--- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/GameSummary.java
+++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/GameSummary.java
@@ -97,6 +97,33 @@ public class GameSummary {
return result;
}
+ public void readPlayers( String playersStr )
+ {
+ if ( null != playersStr ) {
+ players = new String[nPlayers];
+ String sep;
+ if ( playersStr.contains("\n") ) {
+ sep = "\n";
+ } else {
+ sep = m_context.getString( R.string.vs_join );
+ }
+
+ int ii, nxt;
+ for ( ii = 0, nxt = 0; ; ++ii ) {
+ int prev = nxt;
+ nxt = playersStr.indexOf( sep, nxt );
+ String name = -1 == nxt ?
+ playersStr.substring( prev ) :
+ playersStr.substring( prev, nxt );
+ players[ii] = name;
+ if ( -1 == nxt ) {
+ break;
+ }
+ nxt += sep.length();
+ }
+ }
+ }
+
public void setPlayerSummary( String summary )
{
m_playersSummary = summary;
@@ -213,7 +240,7 @@ public class GameSummary {
{
String[] names = null;
if ( null != m_gi ) {
- names = m_gi.visibleNames();
+ names = m_gi.visibleNames( false );
} else if ( null != m_playersSummary ) {
names = TextUtils.split( m_playersSummary, "\n" );
}
diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/UtilCtxt.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/UtilCtxt.java
index 514c5d3b5..b890bb5a7 100644
--- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/UtilCtxt.java
+++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/UtilCtxt.java
@@ -48,7 +48,7 @@ public interface UtilCtxt {
void setIsServer( boolean isServer );
void bonusSquareHeld( int bonus );
- void playerScoreHeld( String text );
+ void playerScoreHeld( int player );
static final int STRD_ROBOT_TRADED = 1;
static final int STR_ROBOT_MOVED = 2;
@@ -70,10 +70,8 @@ public interface UtilCtxt {
static final int STRD_TRADED = 18;
static final int STR_LOSTTURN = 19;
static final int STR_COMMIT_CONFIRM = 20;
- static final int STR_LOCAL_NAME = 21;
- static final int STR_NONLOCAL_NAME = 22;
- static final int STR_BONUS_ALL = 23;
- static final int STRD_TURN_SCORE = 24;
+ static final int STR_BONUS_ALL = 21;
+ static final int STRD_TURN_SCORE = 22;
String getUserString( int stringCode );
static final int QUERY_COMMIT_TURN = 0;
@@ -97,11 +95,10 @@ public interface UtilCtxt {
static final int ERR_REG_UNEXPECTED_USER = 10;
static final int ERR_REG_SERVER_SANS_REMOTE = 11;
static final int STR_NEED_BT_HOST_ADDR = 12;
- static final int ERR_CANT_TRADE_MID_MOVE = 13;
- static final int ERR_NO_EMPTY_TRADE = 14;
- static final int ERR_CANT_UNDO_TILEASSIGN = 15;
- static final int ERR_CANT_HINT_WHILE_DISABLED = 16;
- static final int ERR_RELAY_BASE = 17;
+ static final int ERR_NO_EMPTY_TRADE = 13;
+ static final int ERR_CANT_UNDO_TILEASSIGN = 14;
+ static final int ERR_CANT_HINT_WHILE_DISABLED = 15;
+ static final int ERR_RELAY_BASE = 16;
void userError( int id );
void notifyGameOver();
diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/UtilCtxtImpl.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/UtilCtxtImpl.java
index 969384d4d..3654e082f 100644
--- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/UtilCtxtImpl.java
+++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/UtilCtxtImpl.java
@@ -87,7 +87,7 @@ public class UtilCtxtImpl implements UtilCtxt {
{
}
- public void playerScoreHeld( String text )
+ public void playerScoreHeld( int player )
{
}
@@ -155,12 +155,6 @@ public class UtilCtxtImpl implements UtilCtxt {
case UtilCtxt.STR_COMMIT_CONFIRM:
id = R.string.str_commit_confirm;
break;
- case UtilCtxt.STR_LOCAL_NAME:
- id = R.string.str_local_name;
- break;
- case UtilCtxt.STR_NONLOCAL_NAME:
- id = R.string.str_nonlocal_name;
- break;
case UtilCtxt.STR_BONUS_ALL:
id = R.string.str_bonus_all;
break;
diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/XwJNI.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/XwJNI.java
index 090ea7aa4..0034dfbd2 100644
--- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/XwJNI.java
+++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/XwJNI.java
@@ -202,6 +202,7 @@ public class XwJNI {
public static native String model_writeGameHistory( int gamePtr,
boolean gameOver );
public static native int model_getNMoves( int gamePtr );
+ public static native String model_getPlayersLastScore( int gamePtr, int player );
// Server
public static native void server_reset( int gamePtr );
diff --git a/xwords4/common/board.c b/xwords4/common/board.c
index 6f5faec48..b7ca0df41 100644
--- a/xwords4/common/board.c
+++ b/xwords4/common/board.c
@@ -720,20 +720,21 @@ hideMiniWindow( BoardCtxt* board, XP_Bool destroy, MiniWindowType winType )
#endif
static XP_Bool
-warnBadWords( XP_UCHAR* word, void* closure )
+warnBadWords( const XP_UCHAR* word, XP_Bool isLegal, void* closure )
{
- BadWordInfo bwi;
- XP_Bool ok;
- BoardCtxt* board = (BoardCtxt*)closure;
- XP_S16 turn = server_getCurrentTurn( board->server );
+ XP_Bool ok = XP_TRUE;
+ if ( !isLegal ) {
+ BadWordInfo bwi;
+ BoardCtxt* board = (BoardCtxt*)closure;
+ XP_S16 turn = server_getCurrentTurn( board->server );
- bwi.nWords = 1;
- bwi.words[0] = word;
-
- ok = !board->badWordRejected
- && util_warnIllegalWord( board->util, &bwi, turn, XP_FALSE );
- board->badWordRejected = !ok || board->badWordRejected;
+ bwi.nWords = 1;
+ bwi.words[0] = word;
+ ok = !board->badWordRejected
+ && util_warnIllegalWord( board->util, &bwi, turn, XP_FALSE );
+ board->badWordRejected = !ok || board->badWordRejected;
+ }
return ok;
} /* warnBadWords */
@@ -757,18 +758,18 @@ board_commitTurn( BoardCtxt* board )
nothing */
} else if ( checkRevealTray( board ) ) {
if ( pti->tradeInProgress ) {
+ TileBit traySelBits = pti->traySelBits;
result = XP_TRUE; /* there's at least the window to clean up
after */
+ /* server_commitTrade() changes selPlayer, so board_endTrade
+ must be called first() */
+ (void)board_endTrade( board );
- if ( NO_TILES == pti->traySelBits ) {
+ if ( NO_TILES == traySelBits ) {
util_userError( board->util, ERR_NO_EMPTY_TRADE );
} else if ( util_userQuery( board->util, QUERY_COMMIT_TRADE,
(XWStreamCtxt*)NULL ) ) {
- TileBit traySelBits = pti->traySelBits;
- /* server_commitTrade() changes selPlayer, so board_endTrade
- must be called first() */
- (void)board_endTrade( board );
- result = server_commitTrade( board->server, traySelBits );
+ (void)server_commitTrade( board->server, traySelBits );
}
} else {
XP_Bool warn, legal;
@@ -945,7 +946,9 @@ timerFiredForPen( BoardCtxt* board )
{
XP_Bool draw = XP_FALSE;
const XP_UCHAR* text = (XP_UCHAR*)NULL;
+#ifdef XWFEATURE_MINIWIN
XP_UCHAR buf[80];
+#endif
if ( board->penDownObject == OBJ_BOARD ) {
if ( !dragDropInProgress( board ) || !dragDropHasMoved( board ) ) {
@@ -974,7 +977,6 @@ timerFiredForPen( BoardCtxt* board )
board->penTimerFired = XP_TRUE;
}
} else if ( board->penDownObject == OBJ_SCORE ) {
- LocalPlayer* lp;
XP_S16 scoreIndex = figureScoreRectTapped( board, board->penDownX,
board->penDownY );
/* I've seen this assert fire on simulator. No log is kept so I can't
@@ -982,11 +984,11 @@ timerFiredForPen( BoardCtxt* board )
/* XP_ASSERT( player >= 0 ); */
if ( scoreIndex > CURSOR_LOC_REM ) {
XP_U16 player = scoreIndex - 1;
+#ifdef XWFEATURE_MINIWIN
const XP_UCHAR* format;
XP_UCHAR scoreExpl[48];
XP_U16 explLen;
-
- lp = &board->gi->players[player];
+ LocalPlayer* lp = &board->gi->players[player];
format = util_getUserString( board->util, lp->isLocal?
STR_LOCAL_NAME: STR_NONLOCAL_NAME );
XP_SNPRINTF( buf, sizeof(buf), format, emptyStringIfNull(lp->name) );
@@ -998,10 +1000,9 @@ timerFiredForPen( BoardCtxt* board )
XP_ASSERT( XP_STRLEN(buf) + explLen < sizeof(buf) );
XP_STRCAT( buf, scoreExpl );
}
-#ifdef XWFEATURE_MINIWIN
text = buf;
#else
- util_playerScoreHeld( board->util, buf );
+ util_playerScoreHeld( board->util, player );
#endif
}
@@ -2077,13 +2078,12 @@ board_beginTrade( BoardCtxt* board )
result = preflight( board );
if ( result ) {
- /* check turn before tradeInProgress so I can't tell my opponent's in a
- trade */
- if ( 0 != model_getCurrentMoveCount( board->model, board->selPlayer )){
- util_userError( board->util, ERR_CANT_TRADE_MID_MOVE );
- } else if ( server_countTilesInPool(board->server) < MIN_TRADE_TILES){
+ if ( server_countTilesInPool(board->server) < MIN_TRADE_TILES){
util_userError( board->util, ERR_TOO_FEW_TILES_LEFT_TO_TRADE );
} else {
+ model_resetCurrentTurn( board->model, board->selPlayer );
+ XP_ASSERT( 0 == model_getCurrentMoveCount( board->model,
+ board->selPlayer ) );
#ifdef XWFEATURE_MINIWIN
board->tradingMiniWindowInvalid = XP_TRUE;
#endif
diff --git a/xwords4/common/engine.c b/xwords4/common/engine.c
index b039dad9e..a0dcab4fe 100644
--- a/xwords4/common/engine.c
+++ b/xwords4/common/engine.c
@@ -1118,7 +1118,7 @@ considerScoreWordHasBlanks( EngineCtxt* engine, XP_U16 blanksLeft,
score = figureMoveScore( engine->model, engine->turn,
&posmove->moveInfo,
engine, (XWStreamCtxt*)NULL,
- (WordNotifierInfo*)NULL, NULL, 0 );
+ (WordNotifierInfo*)NULL );
/* First, check that the score is even what we're interested in. If
it is, then go to the expense of filling in a PossibleMove to be
diff --git a/xwords4/common/model.c b/xwords4/common/model.c
index b7f77e9f9..b0be06f3c 100644
--- a/xwords4/common/model.c
+++ b/xwords4/common/model.c
@@ -1933,6 +1933,23 @@ model_writeGameHistory( ModelCtxt* model, XWStreamCtxt* stream,
}
} /* model_writeGameHistory */
+typedef struct _FirstWordData {
+ XP_UCHAR word[32];
+} FirstWordData;
+
+static XP_Bool
+getFirstWord( const XP_UCHAR* word, XP_Bool isLegal, void* closure )
+{
+ LOG_FUNC();
+ if ( isLegal ) {
+ FirstWordData* data = (FirstWordData*)closure;
+ if ( '\0' == data->word[0] && '\0' != word[0] ) {
+ XP_STRCAT( data->word, word );
+ }
+ }
+ return XP_TRUE;
+}
+
static void
scoreLastMove( ModelCtxt* model, MoveInfo* moveInfo, XP_U16 howMany,
XP_UCHAR* buf, XP_U16* bufLen )
@@ -1945,8 +1962,9 @@ scoreLastMove( ModelCtxt* model, MoveInfo* moveInfo, XP_U16 howMany,
XP_STRNCPY( buf, str, len + 1 );
} else {
XP_U16 score;
- XP_UCHAR wordBuf[MAX_ROWS+1];
const XP_UCHAR* format;
+ WordNotifierInfo notifyInfo;
+ FirstWordData data;
ModelCtxt* tmpModel = makeTmpModel( model, NULL, NULL, NULL, NULL );
XP_U16 turn;
@@ -1959,14 +1977,16 @@ scoreLastMove( ModelCtxt* model, MoveInfo* moveInfo, XP_U16 howMany,
XP_ASSERT( 0 );
}
+ data.word[0] = '\0';
+ notifyInfo.proc = getFirstWord;
+ notifyInfo.closure = &data;
score = figureMoveScore( tmpModel, turn, moveInfo, (EngineCtxt*)NULL,
- (XWStreamCtxt*)NULL, (WordNotifierInfo*)NULL,
- wordBuf, VSIZE(wordBuf) );
+ (XWStreamCtxt*)NULL, ¬ifyInfo );
model_destroy( tmpModel );
format = util_getUserString( model->vol.util, STRSD_SUMMARYSCORED );
- *bufLen = XP_SNPRINTF( buf, *bufLen, format, wordBuf, score );
+ *bufLen = XP_SNPRINTF( buf, *bufLen, format, data.word, score );
}
} /* scoreLastMove */
diff --git a/xwords4/common/model.h b/xwords4/common/model.h
index e0ec0d495..2e15c0bc6 100644
--- a/xwords4/common/model.h
+++ b/xwords4/common/model.h
@@ -238,7 +238,8 @@ void model_countAllTrayTiles( ModelCtxt* model, XP_U16* counts,
/********************* scoring ********************/
-typedef XP_Bool (*WordNotifierProc)( XP_UCHAR* word, void* closure );
+typedef XP_Bool (*WordNotifierProc)( const XP_UCHAR* word, XP_Bool isLegal,
+ void* closure );
typedef struct WordNotifierInfo {
WordNotifierProc proc;
void* closure;
@@ -265,8 +266,7 @@ void model_figureFinalScores( ModelCtxt* model, ScoresArray* scores,
/* figureMoveScore is meant only for the engine's use */
XP_U16 figureMoveScore( const ModelCtxt* model, XP_U16 turn, MoveInfo* mvInfo,
EngineCtxt* engine, XWStreamCtxt* stream,
- WordNotifierInfo* notifyInfo, XP_UCHAR* mainWord,
- XP_U16 mainWordLen );
+ WordNotifierInfo* notifyInfo );
/********************* persistence ********************/
#ifdef INCLUDE_IO_SUPPORT
diff --git a/xwords4/common/mscore.c b/xwords4/common/mscore.c
index f3dc718b5..2e17ca684 100644
--- a/xwords4/common/mscore.c
+++ b/xwords4/common/mscore.c
@@ -42,8 +42,7 @@ static XP_S16 checkScoreMove( ModelCtxt* model, XP_S16 turn,
XP_Bool silent, WordNotifierInfo* notifyInfo );
static XP_U16 scoreWord( const ModelCtxt* model, XP_U16 turn, MoveInfo* movei,
EngineCtxt* engine, XWStreamCtxt* stream,
- WordNotifierInfo* notifyInfo, XP_UCHAR* mainWord,
- XP_U16 mainWordLen );
+ WordNotifierInfo* notifyInfo );
/* for formatting when caller wants an explanation of the score. These live
in separate function called only when stream != NULL so that they'll have
@@ -64,8 +63,7 @@ static void wordScoreFormatterAddTile( WordScoreFormatter* fmtr, Tile tile,
XP_U16 tileMultiplier,
XP_Bool isBlank );
static void wordScoreFormatterFinish( WordScoreFormatter* fmtr, Tile* word,
- XWStreamCtxt* stream, XP_UCHAR* mainWord,
- XP_U16 mainWordLen );
+ XWStreamCtxt* stream );
static void formatWordScore( XWStreamCtxt* stream, XP_U16 wordScore,
XP_U16 moveMultiplier );
static void formatSummary( XWStreamCtxt* stream, const ModelCtxt* model,
@@ -104,7 +102,7 @@ adjustScoreForUndone( ModelCtxt* model, MoveInfo* mi, XP_U16 turn )
} else {
moveScore = figureMoveScore( model, turn, mi, (EngineCtxt*)NULL,
(XWStreamCtxt*)NULL,
- (WordNotifierInfo*)NULL, NULL, 0 );
+ (WordNotifierInfo*)NULL );
}
player->score -= moveScore;
player->curMoveScore = 0;
@@ -241,7 +239,7 @@ checkScoreMove( ModelCtxt* model, XP_S16 turn, EngineCtxt* engine,
if ( isLegalMove( model, &moveInfo, silent ) ) {
score = figureMoveScore( model, turn, &moveInfo, engine, stream,
- notifyInfo, NULL, 0 );
+ notifyInfo );
}
} else if ( !silent ) { /* tiles out of line */
util_userError( model->vol.util, ERR_TILES_NOT_IN_LINE );
@@ -450,8 +448,7 @@ isLegalMove( ModelCtxt* model, MoveInfo* mInfo, XP_Bool silent )
XP_U16
figureMoveScore( const ModelCtxt* model, XP_U16 turn, MoveInfo* moveInfo,
EngineCtxt* engine, XWStreamCtxt* stream,
- WordNotifierInfo* notifyInfo, XP_UCHAR* mainWord,
- XP_U16 mainWordLen )
+ WordNotifierInfo* notifyInfo )
{
XP_U16 col, row;
XP_U16* incr;
@@ -480,7 +477,7 @@ figureMoveScore( const ModelCtxt* model, XP_U16 turn, MoveInfo* moveInfo,
}
oneScore = scoreWord( model, turn, moveInfo, (EngineCtxt*)NULL, stream,
- notifyInfo, mainWord, mainWordLen );
+ notifyInfo );
if ( !!stream ) {
formatWordScore( stream, oneScore, moveMultiplier );
}
@@ -494,19 +491,10 @@ figureMoveScore( const ModelCtxt* model, XP_U16 turn, MoveInfo* moveInfo,
for ( i = 0, tiles = moveInfo->tiles; i < nTiles; ++i, ++tiles ) {
- /* Moves using only one tile will sometimes score only in the
- crosscheck direction. Score may still be 0 after the call to
- scoreWord above. Keep trying to get some text in mainWord until
- something's been scored. */
- if ( score > 0 ) {
- mainWord = NULL;
- }
-
tmpMI.commonCoord = tiles->varCoord;
tmpMI.tiles[0].tile = tiles->tile;
- oneScore = scoreWord( model, turn, &tmpMI, engine, stream,
- notifyInfo, mainWord, mainWordLen );
+ oneScore = scoreWord( model, turn, &tmpMI, engine, stream, notifyInfo );
if ( !!stream ) {
formatWordScore( stream, oneScore, multipliers[i] );
}
@@ -566,8 +554,7 @@ scoreWord( const ModelCtxt* model, XP_U16 turn,
MoveInfo* movei, /* new tiles */
EngineCtxt* engine,/* for crosswise caching */
XWStreamCtxt* stream,
- WordNotifierInfo* notifyInfo,
- XP_UCHAR* mainWord, XP_U16 mainWordLen )
+ WordNotifierInfo* notifyInfo )
{
XP_U16 tileMultiplier;
XP_U16 restScore = 0;
@@ -599,7 +586,7 @@ scoreWord( const ModelCtxt* model, XP_U16 turn,
if ( (end - start) >= 1 ) { /* one-letter word: score 0 */
WordScoreFormatter fmtr;
- if ( !!stream || !!mainWord ) {
+ if ( !!stream ) {
wordScoreFormatterInit( &fmtr, dict );
}
@@ -658,7 +645,7 @@ scoreWord( const ModelCtxt* model, XP_U16 turn,
*curTile++ = tile; /* save in case we're checking phonies */
- if ( !!stream || !!mainWord ) {
+ if ( !!stream ) {
wordScoreFormatterAddTile( &fmtr, tile, tileMultiplier,
isBlank );
}
@@ -679,17 +666,14 @@ scoreWord( const ModelCtxt* model, XP_U16 turn,
XP_U16 len = curTile - checkWordBuf;
XP_Bool legal = engine_check( dict, checkWordBuf, len );
- if ( !legal ) {
- XP_UCHAR buf[(MAX_ROWS*2)+1];
- dict_tilesToString( dict, checkWordBuf, len, buf,
- sizeof(buf) );
- (*notifyInfo->proc)( buf, notifyInfo->closure );
- }
+ XP_UCHAR buf[(MAX_ROWS*2)+1];
+ dict_tilesToString( dict, checkWordBuf, len, buf,
+ sizeof(buf) );
+ (void)(*notifyInfo->proc)( buf, legal, notifyInfo->closure );
}
- if ( !!stream || !!mainWord ) {
- wordScoreFormatterFinish( &fmtr, checkWordBuf, stream,
- mainWord, mainWordLen );
+ if ( !!stream ) {
+ wordScoreFormatterFinish( &fmtr, checkWordBuf, stream );
}
#ifdef DEBUG
@@ -804,8 +788,7 @@ wordScoreFormatterAddTile( WordScoreFormatter* fmtr, Tile tile,
static void
wordScoreFormatterFinish( WordScoreFormatter* fmtr, Tile* word,
- XWStreamCtxt* stream, XP_UCHAR* mainWord,
- XP_U16 mainWordLen )
+ XWStreamCtxt* stream )
{
XP_UCHAR buf[(MAX_ROWS*2)+1];
XP_U16 len = dict_tilesToString( fmtr->dict, word, fmtr->nTiles,
@@ -817,11 +800,6 @@ wordScoreFormatterFinish( WordScoreFormatter* fmtr, Tile* word,
stream_putBytes( stream, fmtr->fullBuf, fmtr->bufLen );
stream_putU8( stream, ']' );
}
-
- if ( !!mainWord ) {
- XP_STRNCPY( mainWord, fmtr->wordBuf, mainWordLen );
- }
-
} /* wordScoreFormatterFinish */
static void
diff --git a/xwords4/common/server.c b/xwords4/common/server.c
index 3b5cc5e51..65cd69d08 100644
--- a/xwords4/common/server.c
+++ b/xwords4/common/server.c
@@ -1713,15 +1713,16 @@ server_setGameOverListener( ServerCtxt* server, GameOverListener gol,
} /* server_setGameOverListener */
static XP_Bool
-storeBadWords( XP_UCHAR* word, void* closure )
+storeBadWords( const XP_UCHAR* word, XP_Bool isLegal, void* closure )
{
- ServerCtxt* server = (ServerCtxt*)closure;
+ if ( !isLegal ) {
+ ServerCtxt* server = (ServerCtxt*)closure;
- XP_STATUSF( "storeBadWords called with \"%s\"", word );
-
- server->illegalWordInfo.words[server->illegalWordInfo.nWords++]
- = copyString( server->mpool, word );
+ XP_STATUSF( "storeBadWords called with \"%s\"", word );
+ server->illegalWordInfo.words[server->illegalWordInfo.nWords++]
+ = copyString( server->mpool, word );
+ }
return XP_TRUE;
} /* storeBadWords */
diff --git a/xwords4/common/util.h b/xwords4/common/util.h
index 9c5064a0f..00b6cf0c6 100644
--- a/xwords4/common/util.h
+++ b/xwords4/common/util.h
@@ -52,7 +52,6 @@ typedef enum {
ERR_REG_SERVER_SANS_REMOTE,
STR_NEED_BT_HOST_ADDR,
#endif
- ERR_CANT_TRADE_MID_MOVE,
ERR_NO_EMPTY_TRADE,
/* ERR_CANT_ENGINE_MID_MOVE, */
/* ERR_NOT_YOUR_TURN_TO_TRADE, */
@@ -160,7 +159,7 @@ typedef struct UtilVtable {
#ifndef XWFEATURE_MINIWIN
void (*m_util_bonusSquareHeld)( XW_UtilCtxt* uc, XWBonusType bonus );
- void (*m_util_playerScoreHeld)( XW_UtilCtxt* uc, const XP_UCHAR* txt );
+ void (*m_util_playerScoreHeld)( XW_UtilCtxt* uc, XP_U16 player );
#endif
#ifndef XWFEATURE_STANDALONE_ONLY
@@ -266,8 +265,8 @@ struct XW_UtilCtxt {
#ifndef XWFEATURE_MINIWIN
# define util_bonusSquareHeld( uc, b ) \
(uc)->vtable->m_util_bonusSquareHeld( (uc), (b) )
-# define util_playerScoreHeld( uc, txt ) \
- (uc)->vtable->m_util_playerScoreHeld( (uc), (txt) )
+# define util_playerScoreHeld( uc, player ) \
+ (uc)->vtable->m_util_playerScoreHeld( (uc), (player) )
#endif
#ifndef XWFEATURE_STANDALONE_ONLY
diff --git a/xwords4/linux/gtkmain.c b/xwords4/linux/gtkmain.c
index fb3100e99..d3abe3258 100644
--- a/xwords4/linux/gtkmain.c
+++ b/xwords4/linux/gtkmain.c
@@ -897,6 +897,15 @@ handle_trayEditToggle_off( GtkWidget* widget, GtkAppGlobals* globals )
}
#endif
+static void
+handle_trade_cancel( GtkWidget* XP_UNUSED(widget), GtkAppGlobals* globals )
+{
+ BoardCtxt* board = globals->cGlobals.game.board;
+ if ( board_endTrade( board ) ) {
+ board_draw( board );
+ }
+}
+
#ifndef XWFEATURE_STANDALONE_ONLY
static void
handle_resend( GtkWidget* XP_UNUSED(widget), GtkAppGlobals* globals )
@@ -992,6 +1001,9 @@ makeMenus( GtkAppGlobals* globals, int XP_UNUSED(argc),
(void)createAddItem( fileMenu, "Load dictionary",
GTK_SIGNAL_FUNC(load_dictionary), globals );
+ (void)createAddItem( fileMenu, "Cancel trade",
+ GTK_SIGNAL_FUNC(handle_trade_cancel), globals );
+
fileMenu = makeAddSubmenu( menubar, "Edit" );
(void)createAddItem( fileMenu, "Undo",
@@ -1005,7 +1017,6 @@ makeMenus( GtkAppGlobals* globals, int XP_UNUSED(argc),
(void)createAddItem( fileMenu, "Dis-allow tray edit",
GTK_SIGNAL_FUNC(handle_trayEditToggle_off), globals );
#endif
-
fileMenu = makeAddSubmenu( menubar, "Network" );
#ifndef XWFEATURE_STANDALONE_ONLY
@@ -1683,15 +1694,6 @@ gtk_util_getTraySearchLimits( XW_UtilCtxt* XP_UNUSED(uc),
#endif
#ifndef XWFEATURE_MINIWIN
-static void
-gtk_util_setInTrade( XW_UtilCtxt* uc, XP_U16 turn, XP_Bool entering )
-{
- XP_LOGF( "%s(turn=%d; entering=%d)", __func__, turn, entering );
- XP_USE( uc );
- XP_USE( turn );
- XP_USE( entering );
-}
-
static void
gtk_util_bonusSquareHeld( XW_UtilCtxt* uc, XWBonusType bonus )
{
@@ -1701,11 +1703,19 @@ gtk_util_bonusSquareHeld( XW_UtilCtxt* uc, XWBonusType bonus )
}
static void
-gtk_util_playerScoreHeld( XW_UtilCtxt* uc, const XP_UCHAR* txt )
+gtk_util_playerScoreHeld( XW_UtilCtxt* uc, XP_U16 player )
{
LOG_FUNC();
- XP_USE( uc );
- XP_USE( txt );
+
+ GtkAppGlobals* globals = (GtkAppGlobals*)uc->closure;
+
+ XP_UCHAR scoreExpl[48];
+ XP_U16 explLen = sizeof(scoreExpl);
+
+ if ( model_getPlayersLastScore( globals->cGlobals.game.model,
+ player, scoreExpl, &explLen ) ) {
+ XP_LOGF( "got: %s", scoreExpl );
+ }
}
#endif
@@ -1928,7 +1938,6 @@ setupGtkUtilCallbacks( GtkAppGlobals* globals, XW_UtilCtxt* util )
#endif
#ifndef XWFEATURE_MINIWIN
- util->vtable->m_util_setInTrade = gtk_util_setInTrade;
util->vtable->m_util_bonusSquareHeld = gtk_util_bonusSquareHeld;
util->vtable->m_util_playerScoreHeld = gtk_util_playerScoreHeld;
#endif
diff --git a/xwords4/linux/linuxutl.c b/xwords4/linux/linuxutl.c
index 196cd0d04..15513d306 100644
--- a/xwords4/linux/linuxutl.c
+++ b/xwords4/linux/linuxutl.c
@@ -332,10 +332,6 @@ linux_getErrString( UtilErrID id, XP_Bool* silent )
break;
#endif
- case ERR_CANT_TRADE_MID_MOVE:
- message = "Remove played tiles before trading.";
- break;
-
case ERR_NO_EMPTY_TRADE:
message = "No tiles selected; trade cancelled.";
break;