|
Build as a Windows application. Link with XFT.lib. !======================================================================= ! ___________ ! \\//----------- ! )( //== // ! //\\ // // !======================================================================= ! EXTENDED FORTRAN TYPES LIBRARY SAMPLES !======================================================================= ! _Yamb.f90 - Main application module for XYamb sample. ! ! XYamb is a simple game written in XFT. The Yamb game is slightly ! modified "Yacht" or "Yachtzee". The sample demonstrates: ! * usage of SDI architecture with menu, view window, toolbar and status bar ! * drawing on application windows using XFTGDI routines ! * usage of registry routines to store last user name and high score list ! ! 2005. Jugoslav Dujic (jdujic@uns.ns.ac.yu) ! ! You are free to use the code for both commercial and non-commercial ! use. The code is provided as-is without warranties. You are free to ! distribute and modify this source code provided that the list of ! original author(s) remains untouched and your revisions are indicated ! as such. Contributions are welcome. !======================================================================= !XChild_OnPaint !XFrame_OnGetMMInfo !XChild_OnLButtonUp !OnNewRoll !OnRoll !OnTopTen !OnAnnounce !OnUndo !OnNewGame !OnExit !DrawTable !CalcResult !GameOver !ResetState !XInit !======================================================= MODULE XYamb USE XFT USE DFWIN IMPLICIT NONE TYPE(X_WINDOW):: xChild !View window TYPE(X_MENU):: xMenu !Main menu TYPE(X_TOOLBAR):: xTb !Toolbar INTEGER, PARAMETER:: COMPLETE = 0, & !Field is completed LOCKED = 1, & !Field is free, but disabled FREE = 2 !Field is free, but enabled INTEGER, PARAMETER:: ACT_WRITE = 1, & !Last action is "enter the result" ACT_ANNOUNCE = 2, & !Last action is "announce" ACT_ANNOUNCE_SEL = 3 !Last action is "select announced field" INTEGER, PARAMETER:: nColumns = 4 INTEGER:: iResult(15, nColumns), & !Result of each field (including the intermediates) iState(15, nColumns), & !State of each field in the table nLastAction = 0, & !Last action to be undid nLastCol = 0, & !Last filled column nLastRow = 0, & !Last filled row iDice(6), & !Numbers on each dice nRolls = 1, & !Current roll iTotal !Total game result CHARACTER(100) sPlayerName !Name of actual player CHARACTER(*), PARAMETER:: APP_REGKEY = "Software\XFT\Yamb\2.0" LOGICAL:: bKeep(6) = .FALSE., & !Selected dice bAnnounce = .FALSE. !TRUE if "Announce" is selected but the field is not chosen yet CHARACTER(20):: sTopTenName(10) !Names of top 10 players INTEGER:: iTopTenResult(10) !Results of top 10 players INTEGER:: IDC_STATUS !======================================================= CONTAINS !======================================================= !PURPOSE: Initialization function called by XFT library on app start. INTEGER FUNCTION XInit(szCmdLine, nCmdShow) !DEC$ATTRIBUTES DEFAULT, DECORATE, ALIAS: 'XINIT':: XInit USE XFT USE XFTMENU USE XFTCTRL USE DFWIN !USE COMCTL32 IMPLICIT NONE CHARACTER*(*) szCmdLine INTEGER, INTENT(IN):: nCmdShow TYPE(X_WINDOW):: xWnd INTEGER:: iSt, iParts(2) = (/80, -1/), iX1, iY1, iX2, iY2, iWidth, iHeight, & hKey, iSize, iType, tbHeight, sbHeight LOGICAL:: bSt TYPE(T_RECT):: Rect TYPE(X_WINDOW):: Dlg TYPE(X_TBBUTTON):: xButtons(5) TYPE(X_BITMAP):: xBmp INCLUDE 'Resource.fd' !Loading menu resource & attaching it to the frame xMenu = XLoadMenu(IDR_MENU_YAMB) !Creation of App & frame window iSt = XCreateSDIApp(xMenu, "XYamb", IDI_ICON_YAMB) iSt = XSetWindowMenu(XW_FRAME, xMenu) !Moving frame to screen center iSt = SystemParametersInfo(SPI_GETWORKAREA, 0, LOC(Rect), 0) iX1 = Rect%Right/2 iY1 = Rect%Bottom/2 iSt = XSetWindowPos(XW_FRAME, iX1-200, iY1-300, 400, 600) iSt = XGetClientRect(XW_FRAME, iWidth, iHeight) !Creation of toolbar iSt = XCreateToolbar(XW_FRAME, xTb, IDR_TOOLBAR2, WS_BORDER.OR.TBSTYLE_FLAT.OR.TBSTYLE_TOOLTIPS, 16, 16, 16, 15) bSt = XCreateBitmap(xBmp, IDR_TOOLBAR2, LR_LOADMAP3DCOLORS) iSt = XToolbarSet(xTb, IDR_TOOLBAR2, xBmp%hBmp, CTL_BITMAP) xButtons(1) = X_TBBUTTON(ID_ACTION_ROLL, TBSTATE_ENABLED, TBSTYLE_BUTTON, 1, 0) xButtons(2) = X_TBBUTTON(ID_ACTION_NEWROLL, TBSTATE_ENABLED, TBSTYLE_BUTTON, 2, 0) xButtons(3) = X_TBBUTTON(0, 0, TBSTYLE_SEP, 0, 0) xButtons(4) = X_TBBUTTON(ID_ACTION_ANNOUNCE, TBSTATE_ENABLED, TBSTYLE_BUTTON, 3, 0) xButtons(5) = X_TBBUTTON(ID_ACTION_UNDO, TBSTATE_ENABLED, TBSTYLE_BUTTON, 4, 0) iSt = XToolbarAddButtons(xTb, xButtons(1:5), 0) IDC_STATUS = XCtlCreate(XW_FRAME, 2, XCTL_STATUS, 0) iSt = XCtlSet(XW_FRAME, IDC_STATUS, iParts, CTL_RECTS) iSt = XCtlSet(XW_FRAME, IDC_STATUS, .TRUE., CTL_VISIBLE) iSt = XCtlGetPos(XW_FRAME, IDC_STATUS, iHeight=sbHeight) iSt = XToolbarGetPos(xTb, iHeight=tbHeight) iSt = XCreateWindow(xChild, XW_FRAME, WS_CHILD, ""C, XS_EX_DBLBUFFERED, 0, tbHeight, iWidth, iHeight-tbHeight-sbHeight) iSt = XShowWindow(xChild, SW_SHOW) bSt = XSetHandler(xChild, WM_PAINT, XChild_OnPaint) bSt = XSetHandler(xChild, WM_LBUTTONUP, XChild_OnLButtonUp) bSt = XSetHandler(XW_FRAME, WM_GETMINMAXINFO, XFrame_OnGetMMInfo) bSt = XSetCommand(XW_FRAME, ID_GAME_NEW, OnNewGame) bSt = XSetCommand(XW_FRAME, ID_GAME_TOPTEN, OnTopTen) bSt = XSetCommand(XW_FRAME, ID_GAME_EXIT, OnExit) bSt = XSetCommand(XW_FRAME, ID_ACTION_ROLL, OnRoll) bSt = XSetCommand(XW_FRAME, ID_ACTION_NEWROLL, OnNewRoll) bSt = XSetCommand(XW_FRAME, ID_ACTION_ANNOUNCE, OnAnnounce) bSt = XSetCommand(XW_FRAME, ID_ACTION_UNDO, OnUndo) hKey = XRegOpen(HKEY_LOCAL_MACHINE, APP_REGKEY) iSt = XRegRead(hKey, "Player", sPlayerName) iSt = XRegRead(hKey, "TopTenResult", LOC(iTopTenResult), SIZEOF(iTopTenResult)) iSt = XRegRead(hKey, "TopTenName", LOC(sTopTenName), SIZEOF(sTopTenName)) iSt = XLoadDialog(IDD_NAME, Dlg, XW_FRAME) iSt = XCtlSet(Dlg, IDC_EDIT_NAME, sPlayerName, CTL_STATE) iSt = XModalDialog(Dlg) iSt = XCtlGet(Dlg, IDC_EDIT_NAME, sPlayerName, CTL_STATE) CALL XDestroyDialog(Dlg) iSt = XCtlSet(XW_FRAME, IDC_STATUS, sPlayerName, 1) iSt = XRegWrite(hKey, "Player", sPlayerName) CALL XRegClose(hKey) iDice = 0 XInit = 1 CALL OnNewGame(XW_FRAME, 0, 0) END FUNCTION XInit !======================================================= !PURPOSE: handler for WM_GETMINMAXINFO message. Prevents the !frame from resizing by setting nMinSize and nMaxSize to (400, 600) INTEGER FUNCTION XFrame_OnGetMMInfo(xWnd, xSizeMax, xMaxPos, xMinSize, xMaxSize) TYPE(X_WINDOW), INTENT(IN):: xWnd TYPE(X_POINT), INTENT(INOUT):: xSizeMax TYPE(X_POINT), INTENT(INOUT):: xMaxPos TYPE(X_POINT), INTENT(INOUT):: xMinSize !Minimal allowed size TYPE(X_POINT), INTENT(INOUT):: xMaxSize !Maximal allowed size xMinSize = X_POINT(400, 600) xMaxSize = X_POINT(400, 600) XFrame_OnGetMMInfo = 0 END FUNCTION XFrame_OnGetMMInfo !======================================================= !PURPOSE: handler for WM_PAINT message. Draws the dice and !the table. INTEGER FUNCTION XChild_OnPaint(xWnd, xDC, iX1, iY1, iX2, iY2) TYPE(X_WINDOW):: xWnd !Window ( = xChild) TYPE(X_DC):: xDC !Window's DC INTEGER, INTENT(IN):: iX1, iY1, iX2, iY2 !Dimensions of update rectangle INTEGER:: iSt, i, nColor, iX, iY, n INTEGER, PARAMETER:: iSpot(9,6) = (/0, 0, 0, 0, 1, 0, 0, 0, 0, & !Spots on dice 1, 0, 0, 0, 0, 0, 0, 0, 1, & 0, 0, 1, 0, 1, 0, 1, 0, 0, & 1, 0, 1, 0, 0, 0, 1, 0, 1, & 1, 0, 1, 0, 1, 0, 1, 0, 1, & 1, 0, 1, 1, 0, 1, 1, 0, 1/) !ClearScreen CALL XSetBrush(xDC, XCOLOR_GREEN) CALL XSetPen(xDC, XCOLOR_GREEN) iSt = XRectangle(xDC, 0, 0, xDC%xView%iXExt, xDC%xView%iYExt) XChild_OnPaint = 0 !Don't draw if the game is not started yet IF (iDice(1).EQ.0) RETURN !Drawing dice DO i = 1, 6 IF (bKeep(i)) THEN nColor = XCOLOR_LTCYAN ELSE nColor = XCOLOR_YELLOW END IF iSt = XRectangle(xDC, 20, 80*i-60, 80, 80*i, & X_PEN(PS_SOLID, 0, nColor), & X_BRUSH(BS_SOLID, nColor, 0)) CALL XSetBrush(xDC, XCOLOR_GREEN) CALL XSetPen(xDC, XCOLOR_GREEN) iSt = XRectangle(xDC, 20, 80*I-60, 30, 80*I-50) iSt = XRectangle(xDC, 70, 80*I-60, 80, 80*I-50) iSt = XRectangle(xDC, 20, 80*I-10, 30, 80*I) iSt = XRectangle(xDC, 70, 80*I-10, 80, 80*I) CALL XSetBrush(xDC, nColor) CALL XSetPen(xDC, nColor) iSt = XEllipse(xDC, 20, 80*I-60, 40, 80*I-40) iSt = XEllipse(xDC, 60, 80*I-60, 80, 80*I-40) iSt = XEllipse(xDC, 20, 80*I-20, 40, 80*I) iSt = XEllipse(xDC, 60, 80*I-20, 80, 80*I) CALL XSetBrush(xDC, XCOLOR_BLACK) !Draw spots DO ix = 1, 3 DO iy = 1, 3 n = ix+3*(iy-1) IF(iSpot(n, iDice(i)).eq.1) THEN iSt = XEllipse(xDC, 16+15*ix, 80*i-64+15*iy, 24+15*ix, 80*i-60+15*iy+4) END IF END DO END DO END DO CALL DrawTable(xDC) END FUNCTION XChild_OnPaint !======================================================= !PURPOSE: handler for WM_LBUTTONUP message INTEGER FUNCTION XChild_OnLButtonUp(xWnd, iX, iY, Msg, iKeyState) TYPE(X_WINDOW):: xWnd !Window ( = xChild) INTEGER:: iX, iY, Msg, iKeyState !Click coordinates and state of keys INTEGER:: iSt, i, j, k, m, iX1, iY1, iX2, iY2, iXStep, iYStep INCLUDE "Resource.fd" !If one of dice is hit, toggle its keep state DO i = 1, 6 IF(iY.GE.80*I-60 .AND. iY.LE.80*I .AND. iX.GE.20 .AND. (iX.LE.80)) & bKeep(i) = .NOT.bKeep(i) END DO iXStep = 50 iYStep = 30 XChild_OnLButtonUp = 0 !Checking the table for click DO j = 1, nColumns DO k = 1, 14 ix1 = 100+j*(iXStep) ix2 = ix1+45 iy1 = k*(iYStep) iy2 = iy1+25 IF(iY.GE.iy1 .AND. iY.LE.iy2 .AND. iX.GE.ix1 .AND.iX.LE.ix2) THEN IF(iState(k, j).eq.FREE) THEN !The clicked field (k, j) is found IF (bAnnounce) THEN bAnnounce = .FALSE. !Enable roll & disable re-announce iSt = XMenuSet(xMenu, ID_ACTION_ROLL, .TRUE., XC_ENABLE) iSt = XMenuSet(xMenu, ID_ACTION_ANNOUNCE, .FALSE., XC_ENABLE) iSt = XToolbarSet(xTb, ID_ACTION_ROLL, .TRUE., XC_ENABLE) iSt = XToolbarSet(xTb, ID_ACTION_ANNOUNCE, .FALSE., XC_ENABLE) !Save Undo information nLastCol = j nLastRow = k nLastAction = ACT_ANNOUNCE_SEL iSt = XMenuSet(xMenu, ID_ACTION_UNDO, .TRUE., XC_ENABLE) iSt = XToolbarSet(xTb, ID_ACTION_UNDO, .TRUE., XC_ENABLE) !Lock all other fields for writing DO m = 1, 14 IF (iState(m, 4).NE.COMPLETE .AND. m.NE.k) iState(m, 4) = LOCKED END DO iSt = XCtlSet(XW_FRAME, IDC_STATUS, "Roll the dice.", 2) ELSE !Save Undo information nLastCol = j nLastRow = k nLastAction = ACT_WRITE !Enable undo except if last roll with announcement IF (.NOT.(j.EQ.4 .AND. nRolls.EQ.3)) THEN iSt = XMenuSet(xMenu, ID_ACTION_UNDO, .TRUE., XC_ENABLE) iSt = XToolbarSet(xTb, ID_ACTION_UNDO, .TRUE., XC_ENABLE) END IF CALL CalcResult(k, j) !Lock entire table - no entry can be made until undo or new roll WHERE (iState.EQ.FREE) iState = LOCKED END WHERE END IF CALL XUpdateWindow(xWnd) RETURN ELSE RETURN END IF END IF END DO END DO CALL XUpdateWindow(xWnd) END FUNCTION XChild_OnLButtonUp !======================================================= !PURPOSE: Handler for Action/New Roll menu. SUBROUTINE OnNewRoll(xWnd, ID, nCode) TYPE(X_WINDOW):: xWnd INTEGER, INTENT(IN):: ID, nCode INTEGER:: iSt INCLUDE "Resource.fd" IF (nCode.EQ.0) THEN iSt = XMenuSet(xMenu, ID_ACTION_NEWROLL, .FALSE., XC_ENABLE) iSt = XToolbarSet(xTb, ID_ACTION_NEWROLL, .FALSE., XC_ENABLE) !Reset keep status and number of rolls bKeep = .FALSE. nRolls = 0 !Reset the table to normal state CALL ResetState CALL OnRoll(xWnd, ID, nCode) END IF END SUBROUTINE OnNewRoll !======================================================= !PURPOSE: Handler for Action/Roll menu. SUBROUTINE OnRoll(xWnd, ID, nCode) USE MSFLIB TYPE(X_WINDOW):: xWnd INTEGER, INTENT(IN):: ID, nCode INTEGER:: iSt, i REAL:: fRnd CHARACTER(30):: sMessage INCLUDE "Resource.fd" IF (nCode.EQ.0) THEN nRolls = nRolls+1 IF (nRolls.EQ.3) THEN sMessage = "Enter the result." ELSE WRITE(sMessage, "('Roll ', i1, a1)") nRolls END IF !iSt = XSendMessage(xStatus, SB_SETTEXT, 1, LOC(sMessage)) iSt = XCtlSet(XW_FRAME, IDC_STATUS, sMessage, 2) !Enable announce if 1st roll; disable roll if 3rd roll iSt = XMenuSet(xMenu, ID_ACTION_ANNOUNCE, nRolls.EQ.1, XC_ENABLE) iSt = XMenuSet(xMenu, ID_ACTION_ROLL, nRolls.LT.3, XC_ENABLE) iSt = XToolbarSet(xTb, ID_ACTION_ANNOUNCE, nRolls.EQ.1, XC_ENABLE) iSt = XToolbarSet(xTb, ID_ACTION_ROLL, nRolls.LT.3, XC_ENABLE) IF (nRolls.EQ.1) THEN !If only "announced" column is left, disable "Roll" item IF (ALL(iState(1:14, 1:3).EQ.COMPLETE)) iSt = XMenuSet(xMenu, ID_ACTION_ROLL, .FALSE., XC_ENABLE) END IF iSt = XMenuSet(xMenu, ID_ACTION_UNDO, .FALSE., XC_ENABLE) iSt = XToolbarSet(xTb, ID_ACTION_UNDO, .FALSE., XC_ENABLE) !Assign random numbers to dice CALL SEED(RND$TIMESEED) DO i = 1, 6 IF (.NOT.bKeep(i)) THEN CALL RANDOM(fRnd) iDice(i) = INT(6*fRnd)+1 END IF END DO CALL XUpdateWindow(xChild) END IF END SUBROUTINE OnRoll !======================================================= !PURPOSE: Callback for Game/Top ten menu item. Displays the "Top ten" !Dialog SUBROUTINE OnTopTen(xWnd, ID, nCode) USE XSTRINGS USE XFTCTRL !USE XFLOGM2 TYPE(X_WINDOW):: xWnd INTEGER, INTENT(IN):: ID, nCode TYPE(X_WINDOW):: Dlg !TYPE(DIALOG):: Dlg CHARACTER*5:: sTemp INTEGER:: iSt, i INCLUDE "Resource.fd" iSt = XLoadDialog(IDD_TOPTEN, Dlg, XW_FRAME) DO i = 1, 10 iSt = XCtlSet(Dlg, IDC_IME1+i-1, sTopTenName(i), CTL_TITLE) WRITE(sTemp, '(i4)') iTopTenResult(i) iSt = XCtlSet(Dlg, IDC_REZ1+i-1, sTemp, CTL_TITLE) IF (iTopTenResult(i).EQ.iTotal .AND. STRCMP(sTopTenName(i), sPlayerName)) THEN iSt = XCtlSet(Dlg, IDC_IME1+i-1, #FF, CTL_COLOR) END IF END DO iSt = XModalDialog(Dlg) CALL XDestroyDialog(Dlg) !xDlg = XLoadDialog(IDD_TOPTEN) !DO i = 1, 10 ! iSt = XCtlSet(xDlg, IDC_IME1+i-1, sTopTenName(i), CTL_TITLE) ! WRITE(sTemp, '(i4)') iTopTenResult(i) ! iSt = XCtlSet(xDlg, IDC_REZ1+i-1, sTemp, CTL_TITLE) ! IF (iTopTenResult(i).EQ.iTotal .AND. sTopTenName(i).EQ.sPlayerName) THEN !! iSt = XCtlSet(xDlg, IDC_IME1+i-1, #FF, CTL_COLOR) ! END IF !END DO !iSt = XModalDialog(xDlg, xChild%hWnd) !CALL xDlgUnInit(xDlg) END SUBROUTINE OnTopTen !======================================================= !PURPOSE: Callback for Announce/Top Ten menu item. SUBROUTINE OnAnnounce(xWnd, ID, nCode) TYPE(X_WINDOW):: xWnd INTEGER, INTENT(IN):: ID, nCode INTEGER:: iSt, j, k INCLUDE "Resource.fd" IF (nCode.EQ.0) THEN !Lock the entire table except "announce" column DO j = 1, 14 IF (iState(j, 4).eq.LOCKED) iState(j, 4) = FREE DO k = 1, 3 IF(iState(j, k).eq.FREE) iState(j, k) = LOCKED END DO END DO !Save undo information nLastAction = ACT_ANNOUNCE bAnnounce = .TRUE. !Disable "Announce" & "Roll" (until a field is selected in OnLButtonUp). !Enable "Undo". iSt = XMenuSet(xMenu, ID_ACTION_ANNOUNCE, .FALSE., XC_ENABLE) iSt = XMenuSet(xMenu, ID_ACTION_ROLL, .FALSE., XC_ENABLE) iSt = XMenuSet(xMenu, ID_ACTION_UNDO, .TRUE., XC_ENABLE) iSt = XToolbarSet(xTb, ID_ACTION_ANNOUNCE, .FALSE., XC_ENABLE) iSt = XToolbarSet(xTb, ID_ACTION_ROLL, .FALSE., XC_ENABLE) iSt = XToolbarSet(xTb, ID_ACTION_UNDO, .TRUE., XC_ENABLE) iSt = XCtlSet(XW_FRAME, IDC_STATUS, "Select the field to announce.", 2) CALL XUpdateWindow(xChild) END IF END SUBROUTINE OnAnnounce !======================================================= !PURPOSE: Handler for Action/Undo menu item. Restores the previous !state (applicable in case of misclicking a field or changing mind !when making an announcement) SUBROUTINE OnUndo(xWnd, ID, nCode) TYPE(X_WINDOW):: xWnd INTEGER, INTENT(IN):: ID, nCode CHARACTER(40):: sMessage INTEGER:: iSt, j, k INCLUDE "Resource.fd" IF (nCode.EQ.0) THEN SELECT CASE (nLastAction) CASE(ACT_ANNOUNCE) !Last action is Action/Announce. Reset the table into !normal state, re-enable "Roll" and "Announce" menu items. bAnnounce = .FALSE. iSt = XCtlGet(XW_FRAME, IDC_STATUS, sMessage, 2) CALL ResetState() iSt = XCtlSet(XW_FRAME, IDC_STATUS, sMessage, 2) iSt = XMenuSet(xMenu, ID_ACTION_ANNOUNCE, .TRUE., XC_ENABLE) iSt = XMenuSet(xMenu, ID_ACTION_ROLL, .TRUE., XC_ENABLE) iSt = XToolbarSet(xTb, ID_ACTION_ANNOUNCE, .TRUE., XC_ENABLE) iSt = XToolbarSet(xTb, ID_ACTION_ROLL, .TRUE., XC_ENABLE) CASE(ACT_ANNOUNCE_SEL) !Last action is selection of announce field. Allow only !to change selection, not to get back to another column. !Free selected field & unlock "Announced" column iState(nLastRow, nLastCol) = FREE DO j = 1, 14 IF (iState(j, 4).EQ.LOCKED) iState(j, 4) = FREE END DO !Disable roll & go back to announcement-selection mode iSt = XMenuSet(xMenu, ID_ACTION_ROLL, .FALSE., XC_ENABLE) iSt = XToolbarSet(xTb, ID_ACTION_ROLL, .FALSE., XC_ENABLE) bAnnounce = .TRUE. iSt = XCtlSet(XW_FRAME, IDC_STATUS, "Select the field to announce.", 2) CASE(ACT_WRITE) !Last action is writing down the result. iSt = XCtlGet(XW_FRAME, IDC_STATUS, sMessage, 2) IF (nLastCol.NE.4) THEN !If in first three columns, free the clicked field !and reset the table. iState(nLastRow, nLastCol) = FREE iResult(nLastRow, nLastCol) = 0 CALL CalcResult(0, 0) CALL ResetState() ELSE IF (nLastCol.EQ.4 .AND. nRolls.LT.3) THEN !If in "Announced" column, free only that field, and recalculate !the result iState(nLastRow, nLastCol) = FREE iResult(nLastRow, nLastCol) = 0 CALL CalcResult(0, 0) END IF IF (nRolls.LT.3) THEN iSt = XMenuSet(xMenu, ID_ACTION_ROLL, .TRUE., XC_ENABLE) iSt = XToolbarSet(xTb, ID_ACTION_ROLL, .TRUE., XC_ENABLE) END IF !Disable new roll until the result is written down (OnLButtonUp) iSt = XCtlSet(XW_FRAME, IDC_STATUS, sMessage, 2) iSt = XMenuSet(xMenu, ID_ACTION_NEWROLL, .FALSE., XC_ENABLE) iSt = XToolbarSet(xTb, ID_ACTION_NEWROLL, .FALSE., XC_ENABLE) END SELECT iSt = XMenuSet(xMenu, ID_ACTION_UNDO, .FALSE., XC_ENABLE) iSt = XToolbarSet(xTb, ID_ACTION_UNDO, .FALSE., XC_ENABLE) CALL XUpdateWindow(xChild) END IF END SUBROUTINE OnUndo !======================================================= !PURPOSE: Handler for Game/New menu item SUBROUTINE OnNewGame(xWnd, ID, nCode) TYPE(X_WINDOW):: xWnd INTEGER, INTENT(IN):: ID, nCode INTEGER:: iSt, nFiles IF (nCode.EQ.0) THEN !Free entire table iState = LOCKED iState(1, 1) = FREE iState(14, 3) = FREE iState(:, 2) = FREE iState(7, :) = COMPLETE iState(10, :) = COMPLETE iState(15, :) = COMPLETE iResult = 0 iResult(7, :) = 0 iResult(10, :) = 0 iResult(15, :) = 0 iTotal = 0 CALL OnNewRoll(xWnd, ID, nCode) END IF END SUBROUTINE OnNewGame !======================================================= !PURPOSE: Handler for exit menu item SUBROUTINE OnExit(xWnd, ID, nCode) TYPE(X_WINDOW):: xWnd INTEGER:: ID, nCode INTEGER:: iSt iSt = XSendMessage(XW_FRAME, WM_CLOSE, 0, 0) END SUBROUTINE OnExit !======================================================= !PURPOSE: Draws the table on xDC. Called from OnPaint callback. SUBROUTINE DrawTable(xDC) TYPE(X_DC):: xDC !Column titles (arrows in "Symbol" font) INTEGER, PARAMETER:: sColTitle(8) = (/175, 171, 173, 65, 65, 65, 65, 65/) CHARACTER(15), PARAMETER:: sGames = '123456S+-SSFPYS' CHARACTER(4):: sNum INTEGER:: iSt, iXStep, iYStep, j, k, iLength, iX1, iY1, iX2, iY2, nColor1, nColor2, nColorText iXStep = 50 iYStep = 30 DO k = 1, 15 IF (k.NE.7 .AND. k.NE.10 .AND. k.NE.15) THEN iSt = XSetFont(xDC, "Arial"C, 12) ELSE iSt = XSetFont(xDC, "Symbol"C, 12) END IF iSt = XTextOut(xDC, 140, k*30+5, sGames(k:k), XCOLOR_LTGRAY) END DO DO j = 1, nColumns iSt = XSetFont(xDC, "Symbol"C, 12) iSt = XTextOut(xDC, 100+j*iXStep+iXStep/2-3, 5, CHAR(sColTitle(j)), XCOLOR_LTGRAY) iSt = XSetFont(xDC, "Arial"C, 12) DO k = 1, 15 ix1 = 100+j*(iXStep) ix2 = ix1+45 iy1 = k*(iYStep) iy2 = iy1+25 SELECT CASE (iState(k, j)) CASE(COMPLETE) nColor1 = XCOLOR_GREEN nColor2 = XCOLOR_BLACK nColorText = XCOLOR_LTGRAY CASE(LOCKED) nColor1 = XCOLOR_LTGRAY nColor2 = XCOLOR_GRAY nColorText = XCOLOR_GRAY CASE(FREE) nColor1 = XCOLOR_WHITE nColor2 = XCOLOR_BLACK nColorText = XCOLOR_BLACK END SELECT IF (k.NE.7 .AND. k.NE.10 .AND. k.NE.15) THEN CALL XSetBrush(xDC, nColor1) CALL XSetPen(xDC, nColor2) iSt = XRectangle(xDC, ix1, iy1, ix2, iy2) iSt = XRectangle(xDC, ix1+1, iy1+1, ix2-1, iy2-1) CALL XSetPen(xDC, XCOLOR_LTGRAY) CALL XMoveTo(xDC, ix1, iy2) iSt = XLineTo(xDC, ix2, iy2) iSt = XLineTo(xDC, ix2, iy1) CALL XMoveTo(xDC, ix1+1, iy2-1) iSt = XLineTo(xDC, ix2-1, iy2-1) iSt = XLineTo(xDC, ix2-1, iy1+1) END IF IF((iState(k, j).eq.COMPLETE)) THEN WRITE(sNum, "(i4)") iResult(k, j) CALL XGetTextExtent(xDC, sNum, iLength, iSt) iSt = XTextOut(xDC, ix2-3-iLength, iy1+4, sNum, nColorText) END IF END DO END DO ix1 = 100+4*iXStep ix2 = ix1+45 iy1 = 16*iYStep iy2 = iy1+25 CALL XSetBrush(xDC, XCOLOR_GREEN) CALL XSetPen(xDC, XCOLOR_BLACK) iSt = XRectangle(xDC, ix1, iy1, ix2, iy2) CALL XSetPen(xDC, XCOLOR_LTGRAY) CALL XMoveTo(xDC, ix1, iy2) iSt = XLineTo(xDC, ix2, iy2) iSt = XLineTo(xDC, ix2, iy1) CALL XMoveTo(xDC, ix1+1, iy2-1) iSt = XLineTo(xDC, ix2-1, iy2-1) iSt = XLineTo(xDC, ix2-1, iy1+1) !Total result WRITE(sNum, "(i4)") iTotal CALL XGetTextExtent(xDC, sNum, iLength, iSt) iSt = XTextOut(xDC, ix2-3-iLength, iy1+4, sNum, nColor2) END SUBROUTINE DrawTable !======================================================= !PURPOSE: Calculates the score in field (k, j), partial scores and !total scores on the basis of state of the dice. Called from OnLButtonUp !(and Undo, when undoing state after LButtonUp) SUBROUTINE CalcResult(k, j) USE MSFWIN INTEGER, INTENT(IN):: k, j LOGICAL:: b2345 INTEGER:: iSt, hKey, nMin, nMax, n, nZeros, iSuma1, iSuma2, iTemp, iRez INTEGER:: iAux(6) INCLUDE "Resource.fd" iRez = 0 SELECT CASE(k) CASE(1:6) !First six column iRez = COUNT(iDice.eq.k)*k CASE(8) !Maximum nmin = MINVAL(iDice) !Die with min. value iRez = SUM(iDice)-nmin CASE(9) !Min nMax = MAXVAL(iDice) !Die with max. value iRez = SUM(iDice)-nMax CASE(11) !Straight b2345 = .TRUE. !Check if there's at least one 2, 3, 4 & 5 in dice set DO n = 2, 5 b2345 = b2345.AND.(ANY((iDice).eq.n)) END DO !If it is, and there's at least one 1 or 6, set the score according to roll IF(b2345.AND.(ANY(iDice.eq.1).OR.ANY(iDice.eq.6))) THEN IF(nRolls.eq.1)THEN iRez = 66 ELSE IF(nRolls.eq.2) THEN iRez = 56 ELSE iRez = 46 END IF END IF CASE (12:14) !Full, Poker, Yamb DO n = 1, 6 iAux(n) = COUNT(iDice.eq.n) END DO IF(k.eq.12) THEN ! Full nZeros = COUNT(iAux.eq.0) nMax = MAXVAL(iAux) !Condition: there are 3 or 4 dice with the same (nonzero) value !and at least two more with another value IF((nZeros.GE.3 .AND. nMax.GT.2 .AND. nMax.LT.5 .AND. & COUNT(iAux.GE.2).gt.1)) THEN iSuma1 = 0 iSuma2 = 0 !Take care that combination 111666 equals to (66611), not (66111) DO n = 1, 6 IF(iAux(n).ge.3) THEN iTemp = iSuma1 iSuma1 = 3*n+iSuma2 iSuma2 = 2*n+iTemp ELSE IF(iAux(n).eq.2) THEN iSuma1 = 2*n+iSuma1 iSuma2 = 2*n+iSuma2 END IF END DO iRez = MAX(iSuma1, iSuma2)+30 END IF END IF IF(k.eq.13) THEN ! Poker nMax = MAXVAL(iAux) IF(nMax.ge.4) THEN !Actually, this is equal to MAXLOC DO n = 1, 6 IF(iAux(n).gt.3) THEN iRez = 4*n+40 EXIT END IF END DO END IF END IF IF(k.eq.14) THEN !Yamb nMax = MAXVAL(iAux) IF(nMax.ge.5) THEN DO n = 1, 6 IF(iAux(n).gt.4) THEN iRez = 5*n+50 EXIT END IF END DO END IF END IF END SELECT IF (k.NE.0) THEN !Mark the field as complete, and enter the result. iResult(k, j) = iRez iState(k, j) = COMPLETE IF (iState(k, j).eq.0) THEN iSt = XCtlSet(XW_FRAME, IDC_STATUS, "New roll", 2) END IF !If the first column, free the next field, but skip the ones with !intermediate results IF(j.eq.1 .AND. K.LT.14) THEN IF (K+1.EQ.7 .OR. K+1.EQ.10) THEN iState(k+2, j) = FREE ELSE iState(k+1, j) = FREE END IF END IF !If the third column, free the next field, but skip the ones with !intermediate results IF(j.eq.3 .AND. K.GT.1) THEN IF (K-1.EQ.7 .OR. K-1.EQ.10) THEN iState(k-2, j) = FREE ELSE iState(k-1, j) = FREE END IF END IF END IF !Sum the intermediates DO n = 1, nColumns !Rows 1-6 iResult(7, n) = sum(iResult(1:6, n)) IF (iResult(7, n).GE.60) iResult(7, n) = iResult(7, n)+30 !Maximal column IF (iState(1, n).eq.COMPLETE .AND. iState(8, n).eq.COMPLETE .AND. & iState(9, n).eq.COMPLETE) THEN iResult(10, n) = (iResult(8, n)-iResult(9, n))*iResult(1, n) END IF !S+F+P+Y iResult(15, n) = sum(iResult(11:14, n)) END DO iSt = XMenuSet(xMenu, ID_ACTION_NEWROLL, .TRUE., XC_ENABLE) iSt = XMenuSet(xMenu, ID_ACTION_ROLL, .FALSE., XC_ENABLE) iSt = XMenuSet(xMenu, ID_ACTION_ANNOUNCE, .FALSE., XC_ENABLE) iSt = XToolbarSet(xTb, ID_ACTION_NEWROLL, .TRUE., XC_ENABLE) iSt = XToolbarSet(xTb, ID_ACTION_ROLL, .FALSE., XC_ENABLE) iSt = XToolbarSet(xTb, ID_ACTION_ANNOUNCE, .FALSE., XC_ENABLE) iTotal = SUM(iResult(7, :))+SUM(iResult(10, :))+SUM(iResult(15, :)) IF (ALL(iState(1:15, 1:nColumns).EQ.COMPLETE)) CALL GameOver END SUBROUTINE CalcResult !======================================================= !PURPOSE: called when the game is over. SUBROUTINE GameOver() !USE XFLOGM2 INTEGER:: iSt, i, hKey, iType TYPE(X_WINDOW):: Dlg INCLUDE "Resource.fd" IF(ALL(iTopTenResult.GE.iTotal)) THEN !"No luck" dialog iSt = XLoadDialog(IDD_FAIL, Dlg, XW_FRAME) iSt = XModalDialog(Dlg) CALL XDestroyDialog(Dlg) ELSE !Updating top ten DO i = 10, 1, -1 !Move current positions downwards IF (i.NE.10) THEN sTopTenName(i+1) = sTopTenName(i) iTopTenResult(i+1) = iTopTenResult(i) END IF !Insert the top ten & exit IF(i.EQ.1 .OR. iTotal.LT.iTopTenResult(i-1) ) THEN sTopTenName(i) = sPlayerName iTopTenResult(i) = iTotal EXIT END IF END DO !Display the dialog & write the registry CALL OnTopTen(XW_FRAME, 0, 0) hKey = XRegOpen(HKEY_LOCAL_MACHINE, APP_REGKEY) iSt = XRegWrite(hKey, "TopTenResult", LOC(iTopTenResult), SIZEOF(iTopTenResult)) iSt = XRegWrite(hKey, "TopTenName", LOC(sTopTenName), SIZEOF(sTopTenName)) CALL XRegClose(hKey) END IF !Ask for continuation iSt = XLoadDialog(IDD_CONTINUE, Dlg, XW_FRAME) iSt = XModalDialog(Dlg) CALL XDestroyDialog(Dlg) IF (iSt.EQ.IDOK) THEN CALL OnNewGame(XW_FRAME, 0, 0) ELSE CALL OnExit(XW_FRAME, 0, 0) END IF END SUBROUTINE GameOver !======================================================= !PURPOSE: unlocks all fields which are not written SUBROUTINE ResetState INTEGER:: k DO k = 1, 14 IF(iState(k, 4).ne.COMPLETE) iState(k, 4) = LOCKED !Disable announced column IF(iState(k, 2).ne.COMPLETE) iState(k, 2) = FREE !Enable free column END DO DO k = 1, 14 IF(iState(k, 1).ne.COMPLETE) THEN iState(k, 1) = FREE !Enable remainder of first column EXIT END IF END DO DO k = 14, 1, -1 IF(iState(k, 3).ne.COMPLETE) THEN iState(k, 3) = FREE !Enable remainder of third column EXIT END IF END DO END SUBROUTINE ResetState !======================================================= END MODULE XYamb |