xwords/franklin/frankplayer.cpp

466 lines
14 KiB
C++

// -*-mode: C; fill-column: 78; c-basic-offset: 4; -*-
/*
* Copyright 1999-2001 by Eric House (xwords@eehouse.org). All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <assert.h>
#include <ctype.h>
#include "sys.h"
#include "gui.h"
#include "ebm_object.h"
extern "C" {
#include "xptypes.h"
#include "strutils.h"
#include "mempool.h"
}
#include "frankids.h"
#include "frankplayer.h"
#include "frankdict.h"
#define PLAYERCOUNT_MENU_ID 2000
#define NUMPLAYERS_POPUP_ID 2001
#define NAME_LABEL_ID 2002
#define ROBOT_LABEL_ID 2003
#define PASSWORD_LABEL_ID 2004
#define OK_BUTTON_ID 2005
#define CANCEL_BUTTON_ID 2006
#define REVERT_BUTTON_ID 2007
#define COUNT_LABEL_ID 2008
#define PLAYERDICT_MENU_ID 2009
#define DICTNAMES_POPUP_ID 2010
#define DICT_LABEL_ID 2011
#define SIZE_LABEL_ID 2012
#define BOARDSIZE_MENU_ID 2013
#define BOARDSIZE_POPUP_ID 2014
#define TIMER_CHECKBOX_ID 2015
#define TIMER_FIELD_ID 2016
#define PHONIES_MENU_ID 2017
#define PHONIES_POPUP_ID 2018
#define OK_BUTTON_COL 35
#define CANCEL_BUTTON_COL 95
#define REVERT_BUTTON_COL 125
/* These must be far enough apart that derivitaves will remain unique; make it
10 for now. Also, there can't be any overlap between their ranges and
other ids!!!!*/
#define NAME_BASE 2060
#define ROBOT_BASE 2070
#define PASSWORD_BASE 2080
#define COUNTER_ROW 5
#define LABEL_ROW 20
#define FIRST_ROW (LABEL_ROW+20)
#define ROW_OFFSET 18
#define DICTMENU_ROW (FIRST_ROW + (ROW_OFFSET * MAX_NUM_PLAYERS) + 2)
#define SIZEMENU_ROW (DICTMENU_ROW + ROW_OFFSET + 1)
#define PHONIESMENU_ROW (SIZEMENU_ROW + ROW_OFFSET + 1)
#define TIMER_ROW (PHONIESMENU_ROW + ROW_OFFSET + 1)
#define BUTTON_ROW (TIMER_ROW + ROW_OFFSET + 3)
#define NAME_COL 5
#define NAME_WIDTH 105
#define NAME_HEIGHT 12
#define ROBOT_COL (NAME_COL + NAME_WIDTH + 10)
#define PASSWD_WIDTH 20
#define TIME_WIDTH 20
#define PASSWD_HEIGHT NAME_HEIGHT
#define PASSWORD_COL (ROBOT_COL + PASSWD_WIDTH + 10)
#define TIMER_FIELD_COL 120
/* Put up a window with a list for each player giving name and robotness,
* and allowing for setting/changing a password.
*/
CPlayersWindow::CPlayersWindow( MPFORMAL CurGameInfo* gi, FrankDictList* dlist,
BOOL isNew, BOOL allowCancel, BOOL* cancelledP )
: CWindow( PLAYERS_WINDOW_ID, 2, 12, 196, 226, "Game setup", !isNew,
FALSE, FALSE )
{
fLocalGI = *gi;
fGIRef = gi;
fDList = dlist;
this->resultP = cancelledP;
fIsNew = isNew;
MPASSIGN( this->mpool, mpool );
/* numplayers counter */
CLabel* label = new CLabel( COUNT_LABEL_ID, "Number of players:" );
this->AddChild( label, NAME_COL, COUNTER_ROW );
this->countMenu = new CMenu(PLAYERCOUNT_MENU_ID, 0, 0, 0, 0, 0 );
this->countMenu->SetNumRows( MAX_NUM_PLAYERS );
char* base = (char*)fNumsBuf;
for ( U16 i = 0 ; i < MAX_NUM_PLAYERS; ++i ) {
snprintf( base, 2, "%d", i+1 );
this->countMenu->SetRow( i, 2000+i, base );
base += 2;
}
CPopupTrigger *trigger = new CPopupTrigger( NUMPLAYERS_POPUP_ID, 0, 0,
this->countMenu, 0 );
trigger->SetCurrentRow( fLocalGI.nPlayers-1 );
this->AddChild( trigger, NAME_COL+130, COUNTER_ROW );
if ( !isNew ) {
DisOrEnable( NUMPLAYERS_POPUP_ID, FALSE );
}
/* Column labels */
label = new CLabel( NAME_LABEL_ID, "Name" );
this->AddChild( label, NAME_COL, LABEL_ROW );
label = new CLabel( ROBOT_LABEL_ID, "Rbt" );
this->AddChild( label, ROBOT_COL, LABEL_ROW );
label = new CLabel( PASSWORD_LABEL_ID, "Pwd" );
this->AddChild( label, PASSWORD_COL, LABEL_ROW );
/* build a row of controls for each potential player. Disable those below
the point determined by the number of players we have. */
for ( U16 i = 0; i < MAX_NUM_PLAYERS; ++i ) {
LocalPlayer* fp = &fLocalGI.players[i];
CTextEdit* name = new CTextEdit( NAME_BASE + i, NAME_WIDTH,
NAME_HEIGHT, TEXTOPTION_ONELINE );
name->SetText( (char*)fp->name );
this->AddChild( name, NAME_COL, FIRST_ROW + (ROW_OFFSET*i) );
CCheckbox *robot_check = new CCheckbox( ROBOT_BASE + i, 0, 0, "" );
robot_check->SetDownStatus( fp->isRobot );
this->AddChild( robot_check, ROBOT_COL, FIRST_ROW + (ROW_OFFSET*i) );
CTextEdit* passwd = new CTextEdit( PASSWORD_BASE + i,
PASSWD_WIDTH, PASSWD_HEIGHT,
TEXTOPTION_PASSWORD
| TEXTOPTION_ONELINE);
this->AddChild( passwd, PASSWORD_COL, FIRST_ROW + (ROW_OFFSET*i) );
const char* password = (const char*)fp->password;
if ( !!password && !!password[0] ) {
passwd->SetText( password );
}
}
this->makeDictMenu();
this->makeSizeMenu();
this->makePhoniesMenu();
/* the timer checkbox */
fTimerEnabled = new CCheckbox( TIMER_CHECKBOX_ID, 0, 0,
"Timer enabled" );
fTimerEnabled->SetDownStatus( fLocalGI.timerEnabled );
AddChild( fTimerEnabled, NAME_COL, TIMER_ROW );
if ( !isNew ) {
fTimerEnabled->Disable();
}
/* the timer field (hidden if checkbox not checked) */
fTimerField = new CTextEdit( TIMER_FIELD_ID, TIME_WIDTH,
PASSWD_HEIGHT, TEXTOPTION_ONELINE );
char buf[10];
sprintf( buf, "%d", fLocalGI.gameSeconds / 60 );
fTimerField->SetText( buf );
AddChild( fTimerField, TIMER_FIELD_COL, TIMER_ROW );
if ( !fLocalGI.timerEnabled || !isNew ) {
fTimerField->Disable();
}
/* the buttons at the bottom */
U16 okCol = OK_BUTTON_COL;
CButton* button = new CButton( OK_BUTTON_ID, 0, 0, "Ok" );
if ( !(isNew && allowCancel) ) {
U16 buttonWidth = button->GetWidth();
U16 windowWidth = this->GetWidth();
okCol = (windowWidth - buttonWidth) / 2;
}
this->AddChild( button, okCol, BUTTON_ROW );
if ( isNew && allowCancel ) {
button = new CButton( CANCEL_BUTTON_ID, 0, 0, "Cancel" );
this->AddChild( button, CANCEL_BUTTON_COL, BUTTON_ROW );
}
adjustVisibility();
XP_DEBUGF( "CPlayersWindow done" );
} // CPlayersWindow
CPlayersWindow::~CPlayersWindow()
{
delete( this->countMenu );
delete( this->dictMenu );
delete( this->sizeMenu );
} /* ~CPlayersWindow */
void
CPlayersWindow::DisOrEnable( U16 id, BOOL enable )
{
CViewable* child = this->GetChildID( id );
if ( enable ) {
XP_DEBUGF( "enabling child id=%d\n", id );
child->Enable();
} else {
XP_DEBUGF( "disabling child id=%d\n", id );
child->Disable();
}
} /* DisOrEnable */
static BOOL
checkAllDigits( CTextEdit* te )
{
char* text = te->GetText();
char ch;
while ( (ch=*text++) != '\0' ) {
if ( !isdigit(ch) ) {
return false;
}
}
return true;
} /* checkAllDigits */
S32
CPlayersWindow::MsgHandler( MSG_TYPE type, CViewable *from, S32 data )
{
S32 result = 0;
S32 id;
U16 row;
switch ( type ) {
case MSG_MENU_SELECT: /* the num-players trigger */
XP_DEBUGF( "MSG_MENU_SELECT: data=%ld\n", data );
switch ( from->GetID()) {
case NUMPLAYERS_POPUP_ID:
row = this->countMenu->GetCurrentRow();
fLocalGI.nPlayers = row + 1; /* GetCurrentRow is 0-based */
adjustVisibility();
GUI_NeedUpdate();
result = 1;
break;
/* case DICTNAMES_POPUP_ID: */
/* row = this->dictMenu->GetCurrentRow(); */
/* break; */
}
break;
case MSG_TEXT_CHANGED:
if ( (from->GetID() == TIMER_FIELD_ID)
&& !checkAllDigits( (CTextEdit*)from ) ) {
result = TEXTEDIT_PLEASE_UNDO;
}
break;
case MSG_BUTTON_SELECT:
result = 1;
id = from->GetID();
switch ( id ) {
case TIMER_CHECKBOX_ID:
DisOrEnable( TIMER_FIELD_ID, fTimerEnabled->GetDownStatus() );
break;
case OK_BUTTON_ID:
for ( U16 i = 0; i < fLocalGI.nPlayers; ++i ) {
copyIDString( NAME_BASE+i, &fLocalGI.players[i].name );
copyIDString( PASSWORD_BASE+i,
&fLocalGI.players[i].password );
}
if ( !!dictMenu ) {
fLocalGI.dictName =
copyString( MPPARM(mpool)
fDList->GetNthName(dictMenu->GetCurrentRow()));
} else {
fLocalGI.dictName = (XP_UCHAR*)NULL;
}
if ( fIsNew ) {
fLocalGI.boardSize = 15 - this->sizeMenu->GetCurrentRow();
fLocalGI.phoniesAction = fPhoniesMenu->GetCurrentRow();
}
fLocalGI.timerEnabled = fTimerEnabled->GetDownStatus();
if ( fLocalGI.timerEnabled ) {
char* text = fTimerField->GetText();
fLocalGI.gameSeconds = atoi(text) * 60;
}
*fGIRef = fLocalGI; /* copy changes to caller */
case CANCEL_BUTTON_ID:
*this->resultP = id == CANCEL_BUTTON_ID;
this->Close();
result = 1;
break;
default: /* probably one of our synthetic IDs */
if ( id >= ROBOT_BASE && id < ROBOT_BASE+MAX_NUM_PLAYERS ) {
U16 playerNum = id - ROBOT_BASE;
BOOL isRobot = ((CButton*)from)->GetDownStatus();
fLocalGI.players[playerNum].isRobot = isRobot;
adjustVisibility();
} else if (id >= NAME_BASE && id < NAME_BASE + MAX_NUM_PLAYERS ){
} else {
result = 0;
}
}
default:
break;
}
if ( result == 0 ) {
result = CWindow::MsgHandler( type, from, data );
}
return result;
} // CPlayersWindow::MsgHandler
/* This will create a dictionary of the dict listed first in the initial.mom
* file
*/
void
CPlayersWindow::makeDictMenu()
{
XP_U16 nDicts = fDList->GetDictCount();
U16 startRow;
if ( !!fLocalGI.dictName ) {
startRow = fDList->IndexForName( fLocalGI.dictName);
} else {
startRow = 0;
}
XP_ASSERT( nDicts > 0 );
CMenu* menu = new CMenu( PLAYERDICT_MENU_ID, 0, 0, 0, 0, 0 );
menu->SetNumRows( nDicts );
for ( U16 i = 0; i < nDicts; ++i ) {
menu->SetRow( i, 3000+i, (char*)fDList->GetNthName(i) );
}
CPopupTrigger *trigger = new CPopupTrigger( DICTNAMES_POPUP_ID, 0, 0,
menu, 0 );
trigger->SetCurrentRow(startRow);
menu->SetCurrentRow(startRow);
CLabel* label = new CLabel( DICT_LABEL_ID, "Dictnry:" );
this->AddChild( label, NAME_COL, DICTMENU_ROW );
U16 labelWidth = label->GetWidth();
this->AddChild( trigger, NAME_COL+labelWidth+10, DICTMENU_ROW );
if ( !fIsNew ) {
DisOrEnable( DICTNAMES_POPUP_ID, FALSE );
}
this->dictMenu = menu;
} /* CPlayersWindow::makeDictMenu */
void
CPlayersWindow::makeSizeMenu()
{
CMenu* menu = new CMenu( BOARDSIZE_MENU_ID, 0, 0, 0, 0, 0 );
menu->SetNumRows( NUM_SIZES );
for ( U16 i = 0; i < NUM_SIZES; ++i ) {
U16 siz = 15-i;
snprintf( (char*)this->sizeNames[i], sizeof(this->sizeNames[i]),
"%dx%d", siz, siz );
menu->SetRow( i, 4000+i, (char*)this->sizeNames[i] );
}
CPopupTrigger* trigger = new CPopupTrigger( BOARDSIZE_POPUP_ID, 0, 0,
menu, 0 );
U16 curSize = 15-fLocalGI.boardSize;
trigger->SetCurrentRow(curSize);
menu->SetCurrentRow(curSize);
CLabel* label = new CLabel( SIZE_LABEL_ID, "Board size:" );
this->AddChild( label, NAME_COL, SIZEMENU_ROW );
U16 labelWidth = label->GetWidth();
this->AddChild( trigger, NAME_COL+labelWidth+10, SIZEMENU_ROW );
if ( !fIsNew ) {
DisOrEnable( BOARDSIZE_POPUP_ID, FALSE );
}
this->sizeMenu = menu;
} /* CPlayersWindow::makeSizeMenu */
void
CPlayersWindow::adjustVisibility()
{
/* disable everything greater than the number of players. Before that,
disable passwords if robot */
U16 nPlayers = fLocalGI.nPlayers;
for ( U16 i = 0; i < MAX_NUM_PLAYERS; ++i ) {
XP_Bool disableAll = i >= nPlayers;
BOOL enable;
/* name */
enable = !disableAll;
DisOrEnable( NAME_BASE + i, enable );
/* robot check */
/* enable's the same as above */
DisOrEnable( ROBOT_BASE + i, enable );
/* passwd */
enable = !disableAll && !fLocalGI.players[i].isRobot;
DisOrEnable( PASSWORD_BASE + i, enable );
}
} /* adjustVisibility */
void
CPlayersWindow::makePhoniesMenu()
{
CMenu* menu = new CMenu( PHONIES_MENU_ID, 0, 0, 0, 0, 0 );
menu->SetNumRows( 3 );
menu->SetRow( 0, 5000, "Ignore" );
menu->SetRow( 1, 5001, "Warn" );
menu->SetRow( 2, 5002, "Disallow" );
CPopupTrigger* trigger = new CPopupTrigger( PHONIES_POPUP_ID, 0, 0,
menu, 0 );
XWPhoniesChoice phoniesAction = fLocalGI.phoniesAction;
trigger->SetCurrentRow(phoniesAction);
menu->SetCurrentRow(phoniesAction);
CLabel* label = new CLabel( SIZE_LABEL_ID, "Phonies:" );
this->AddChild( label, NAME_COL, PHONIESMENU_ROW );
U16 labelWidth = label->GetWidth();
this->AddChild( trigger, NAME_COL+labelWidth+10, PHONIESMENU_ROW );
fPhoniesMenu = menu;
} /* CPlayersWindow::makePhoniesMenu */
void
CPlayersWindow::copyIDString( U16 id, XP_UCHAR** where )
{
if ( *where ) {
XP_DEBUGF( "freeing string " );
XP_DEBUGF( "%s\n", *where );
XP_FREE( mpool, *where );
XP_DEBUGF( "done freeing string\n" );
}
XP_UCHAR* str = (XP_UCHAR*)NULL;
CTextEdit* te = (CTextEdit*)this->GetChildID( id );
XP_UCHAR* name = (XP_UCHAR*)te->GetText();
U16 len = te->TextLength();
if ( len > 0 ) {
str = (XP_UCHAR*)XP_MALLOC( mpool, len + 1 );
memcpy( str, name, len );
str[len] = '\0';
}
*where = str;
} /* CPlayersWindow::copyIDString */