/* * human.c * cc65 Chess * * Created by Stefan Wessels, February 2014. * */ #include "types.h" #include "globals.h" #include "undo.h" #include "board.h" #include "human.h" #include "frontend.h" #include "plat.h" /*-----------------------------------------------------------------------*/ // Internal function Prototype void human_ProcessInput(int keyMask); void human_ProcessToggle(int keyMask, char side, char tile); /*-----------------------------------------------------------------------*/ // Track the user controlled curson on the board static char sc_cursorX, sc_cursorY; /*-----------------------------------------------------------------------*/ // Handle the cursor movement void human_ProcessInput(int keyMask) { switch(keyMask) { case INPUT_UP: if(!sc_cursorY) sc_cursorY = 7; else --sc_cursorY; break; case INPUT_RIGHT: if(7==sc_cursorX) sc_cursorX = 0; else ++sc_cursorX; break; case INPUT_DOWN: if(7==sc_cursorY) sc_cursorY = 0; else ++sc_cursorY; break; case INPUT_LEFT: if(!sc_cursorX) sc_cursorX = 7; else --sc_cursorX; break; } } /*-----------------------------------------------------------------------*/ // Deal with the toggling on/off of the attack and defend states // as well as the 'b'oard state which shows all attcks/defences void human_ProcessToggle(int keyMask, char side, char tile) { char attack = 0; switch(keyMask) { case INPUT_TOGGLE_B: gShowAttackBoard = 1 - gShowAttackBoard; plat_DrawBoard(0); break; case INPUT_TOGGLE_A: attack = 1; // Intentional fall-through since the code // below is the same for showing attack or defence case INPUT_TOGGLE_D: { int offset; char attackers, attacker, i; gShowAttacks[side] ^= SET_BIT(attack); offset = giAttackBoardOffset[tile][0]; if((side & !attack) || (!side && attack)) offset += ATTACK_WHITE_OFFSET; attackers = gpAttackBoard[offset]; ++offset; for(i=0;i> 7; gMove[selector] = gPiece[selector] & PIECE_MOVED; gPiece[selector] &= PIECE_DATA; if(keyMask & INPUT_MOTION) { // If no piece selected and the cursor moved, update the possible moves for this tile if(!selector) board_GeneratePossibleMoves(gTile[0], 0); else { // If a piece is selected, see if the second tile, under the cursor, is // a valid move-to tile validMove = board_findInList(gPossibleMoves, gNumMoves, gTile[1]); plat_Highlight(gTile[1], validMove ? HCOLOR_ATTACK : HCOLOR_INVALID,1); } // Show the cursor plat_Highlight(gTile[0], selector ? HCOLOR_SELECTED : NONE == gPiece[0] || gColor[0] != side ? HCOLOR_EMPTY : gNumMoves ? HCOLOR_VALID : HCOLOR_INVALID,1); } // If the cursor moved and the toggle-show-attackers/defenders states were on for this side, // Handle showing them for the now selected tile. Bit 2/3 says it was on, so toggle it on. // 2/3 is set whem the selection changes, lower in this same routine if(gShowAttacks[side] & SET_BIT(2)) { gShowAttacks[side] &= ~SET_BIT(2); human_ProcessToggle(INPUT_TOGGLE_D, side, gTile[selector]); } if(gShowAttacks[side] & SET_BIT(3)) { gShowAttacks[side] &= ~SET_BIT(3); human_ProcessToggle(INPUT_TOGGLE_A, side, gTile[selector]); } // Get input keyMask = plat_ReadKeys(1); // Always clear the message area once a key is pressed plat_ClearMessage(); // If the selected tile changes, make sure the toggle-show updates will happen (Set 2/3 bit) if(keyMask & (INPUT_MOTION | INPUT_UNDOREDO) || ((keyMask & INPUT_SELECT) && selector && validMove)) { if(gShowAttacks[side] & SET_BIT(0)) { gShowAttacks[side] |= SET_BIT(2); human_ProcessToggle(INPUT_TOGGLE_D, side, gTile[selector]); } if(gShowAttacks[side] & SET_BIT(1)) { gShowAttacks[side] |= SET_BIT(3); human_ProcessToggle(INPUT_TOGGLE_A, side, gTile[selector]); } } if(keyMask & INPUT_MOTION) { // Erase the cursor and move it plat_DrawSquare(gTile[selector]); human_ProcessInput(keyMask & INPUT_MOTION); } else if(keyMask & INPUT_MENU) { // Drop out so the menu can be displayed return OUTCOME_MENU; } else if(keyMask & INPUT_TOGGLE) { // Handle the toggle-show-attackers-defenders-board state chages human_ProcessToggle(keyMask & INPUT_TOGGLE, side, gTile[selector]); } else if(keyMask & INPUT_BACKUP) { // If a piece was selected, deselct the piece if(selector) { plat_DrawSquare(gTile[selector]); selector = 0; sc_cursorY = gTile[0] / 8; sc_cursorX = gTile[0] & 7; keyMask = INPUT_MOTION; } // Otherwise bring up the menu else return OUTCOME_MENU; } else if(keyMask & INPUT_UNDOREDO) { // if there's data in the undo/redo buffers to undo or redo, then do the undo/redo if(((keyMask & INPUT_UNDO) && undo_CanUndo()) || ((keyMask & INPUT_REDO) && undo_CanRedo())) { char numUndo = 1; // If there's AI, undo 2 moves, to get back to the humans' previous move if(gUserMode != (USER_BLACK | USER_WHITE)) numUndo = 2; do { plat_DrawSquare(gTile[0]); if(selector) plat_DrawSquare(gTile[1]); if(keyMask & INPUT_UNDO) undo_Undo(); else undo_Redo(); plat_DrawSquare(gTile[0]); plat_DrawSquare(gTile[1]); if(NULL_TILE != gTile[2]) { plat_DrawSquare(gTile[2]); gTile[2] = NULL_TILE; } if(NULL_TILE != gTile[3]) { plat_DrawSquare(gTile[3]); gTile[3] = NULL_TILE; } gCursorPos[1-side][0] = gTile[0] / 8; gCursorPos[1-side][1] = gTile[0] & 7; // Undo scrolls down so updates at the top of the log, redo scrolls up if(keyMask & INPUT_UNDO) frontend_LogMove(1); else frontend_LogMove(0); } while(--numUndo); // Fix the Attack DB board_PlacePieceAttacks(); // If 2 humans are playing, return so sides can switch if(gUserMode == (USER_BLACK | USER_WHITE)) return OUTCOME_OK; keyMask = INPUT_MOTION; } else { // if there's nothing in the undo/redo buffer, show a message to say so // This could be if all moves have been undone or redone also plat_ShowMessage((keyMask & INPUT_UNDO) ? gszNoUndo : gszNoRedo, HCOLOR_INVALID); } } else if(keyMask & INPUT_SELECT) { if(!selector && gColor[0] == side && gNumMoves) { // If the cursor is on a piece of this turn, that has moved and no other piece // has yet been selected, then select this piece ++selector; keyMask = INPUT_MOTION; } else if(gTile[0] == gTile[1] && gColor[0] == side) { // If the selected tile is re-selected, deselct it --selector; keyMask = INPUT_MOTION; } else if(selector && validMove) { // If a dest is selected for the selected tile, try to do the move // gOutcome of 0 is OUTCOME_INVALID, example moving into check if((gOutcome = board_ProcessAction())) { // If no piece was taken, update the move without taking counter // else reset it to zero if(NONE != (gPiece[1] & PIECE_DATA)) gMoveCounter = 0; else if(NUM_MOVES_TO_DRAW == ++gMoveCounter) gOutcome = OUTCOME_DRAW; // Add the move to the undo stack and update the display undo_AddMove(); plat_DrawSquare(gTile[0]); plat_DrawSquare(gTile[1]); if(NULL_TILE != gTile[2]) { plat_DrawSquare(gTile[2]); gTile[2] = NULL_TILE; } if(NULL_TILE != gTile[3]) { plat_DrawSquare(gTile[3]); gTile[3] = NULL_TILE; } // Log the move frontend_LogMove(0); // This will exit the while loop ++selector; } else { // Show a message to indicate that the move was invalid plat_ShowMessage(gszInvalid,HCOLOR_INVALID); keyMask = INPUT_MOTION; } } } // This does nothing on the C64 but some other platforms may need a // screen refresh - since this function doesn't always fall back to main plat_UpdateScreen(); } while(selector < 2); // Save the cursor positions for next time gCursorPos[side][0] = sc_cursorY; gCursorPos[side][1] = sc_cursorX; return OUTCOME_OK; }