//! Video Poker in ZScript //! Version 0.8 //! 14th September, 2020 namespace videopoker { //User Interface const int VPUI_FONT = 0; //General UI font and colour. const int VPUI_HOLD_COLOUR = 0x01; //Whitetru const int VPUI_HOLD_BG_COLOUR = 0; //Red, box behind 'HOLD' text. //Felt const int VPUI_BACKGROUND_COLOUR = 0x0F; //Should be green. const int VPUI_FELT_WIDTH = 250; const int VPUI_FELT_HEIGHT = 170; const int VPUI_FELT_X = 4; const int VPUI_FELT_Y = 4; const int VPUI_FELT_SCALE = 1; //Backdrop behind felt. const int VPUI_COLOUR_BLACK = 0x0F; const int VPUI_BLACK_BG_SCALE = 1; const int VPUI_BLACK_BG_X = 0; const int VPUI_BLACK_BG_Y = 0; const int VPUI_BLACK_BG_WIDTH = 255; const int VPUI_BLACK_BG_HEIGHT = 175; //Card Dimensions and Properties const int VP_CARD_HOLD_SPACING = 3; //Space between card bottom and 'HOLD' indicator. const int VP_CARD_LAYER = 3; //Layer for drawing card objects to the screen. const int VP_CARD_WIDTH = 40; const int VP_CARD_HEIGHT = 60; const int VP_CARD_SPACING = 8; //Space between cards. const int VP_CARD_COLOUR = 0x01; //White const int VP_CARD_SHADOW = 0x0F; //Black const int VP_CARD_OPACITY = 128; //Opaque const int VP_CARD_SHADOW_OPACITY = 64; //Trans const int VP_CARD_BASE_X = 10; const int VP_CARD_BASE_Y = 20; const int VP_CARD_SHADOW_Y_OFFSET = 5; const int VP_CARD_SHADOW_X_OFFSET = 3; const int VP_CARD_SCALE = 1; //Card Display (Text) //Display of characters over the cards, for 2-10 and A-K-Q-J. const int VP_CARD_CHAR_X_OFFSET = 18; //offset from card left/top edges const int VP_CARD_CHAR_Y_OFFSET = 24; const int VP_CARD_CHAR_FONT = 1; //font for glyphs const int VP_CARD_CHAR_BG_COLOUR = 0; //background colour of glyphs. Normally 0. const int VP_CARD_CHAR_WIDTH = -1; //Glyph scale: This only works in 2.54+ const int VP_CARD_CHAR_HEIGHT = -1; const int VP_CARD_CHAR_OPACITY = 128; //Hand Types const int HT_ROYAL = 9; const int HT_STRAIGHTFLUSH = 8; const int HT_FLUSH = 7; const int HT_STRAIGHT = 6; const int HT_QUADS = 5; const int HT_FULLHOUSE = 4; const int HT_TRIPS = 3; const int HT_TWOPAIR = 2; const int HT_ONEPAIR = 1; //Settings const int MAX_SHUFFLE_FRAMES = 600; //String Display const int BETS_LAYER = 4; const int BETS_X = 5; //Where to display the number of bets on the UI. const int BETS_Y = -16; const int BETS_FONT = 1; const int BETS_COLOUR = 1; const int BETS_BG_COLOUR = 0; const int BETS_FORMAT = 0; //Suit Colours const int CARD_SUIT_HEARTS_COLOUR = 1; //Red const int CARD_SUIT_DIAMONDS_COLOUR = 1; //Blue const int CARD_SUIT_SPADES_COLOUR = 1; //Black const int CARD_SUIT_CLUBS_COLOUR = 1; //Green const int VP_MIN_PAIR = 11; //Jacks. const int VP_SFX_WIN = 31; const int VP_SFX_LOSE = 0; const int VP_SFX_HOLD = 0; const int VP_SFX_DRAWCARDS = 0; const int VP_SFX_BET = 0; ffc script VideoPoker { void run(int min_pair, int win_sfx, int lose_sfx, int holdcard_sfx, int draw_sfx, int counter, int bet_sfx){ //c[5] used for fault timer if ( counter < 1 ) counter = CR_RUPEES; //Default. //Defaults if ( min_pair < 1 ) min_pair = VP_MIN_PAIR; if ( lose_sfx < 1 ) lose_sfx = VP_SFX_LOSE; if ( win_sfx < 1 ) win_sfx = VP_SFX_WIN; if ( holdcard_sfx < 1 ) holdcard_sfx = VP_SFX_HOLD; if ( bet_sfx < 1 ) bet_sfx = VP_SFX_BET; min_pair = min_pair << 0; //Truncate for safety. //Numeric Values int cards[52]={1,2,3,4,5,6,7,8,9,10,11,12,13, 1,2,3,4,5,6,7,8,9,10,11,12,13, 1,2,3,4,5,6,7,8,9,10,11,12,13, 1,2,3,4,5,6,7,8,9,10,11,12,13,}; //Suits int suits[52]={ 1,1,1,1,1,1,1,1,1,1,1,1,1, 2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,3,3,3,3,3, 4,4,4,4,4,4,4,4,4,4,4,4,4 }; //cards dealt. bool dealt[52]; //five first cards, and five under them. int deal[10] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; int counts[13]; //Pairs, pryles, quads. int c[255]; bool held[5]; //The cards that will be held. int hand[5]; //The final five chards after dealing. bool cardanims[5]; //holds what cards to 'flip'. int handbuf[5]; //Buffer to use for initial hand checking, to do preliminary display //if hand prior to discarding. //X and Y of each of the five cards. int cardpositions[5] = { VP_CARD_BASE_X, VP_CARD_BASE_X+((VP_CARD+WIDTH+VP_CARD_SPACING)*1), VP_CARD_BASE_X+((VP_CARD+WIDTH+VP_CARD_SPACING)*2), VP_CARD_BASE_X+((VP_CARD+WIDTH+VP_CARD_SPACING)*3), VP_CARD_BASE_X+((VP_CARD+WIDTH+VP_CARD_SPACING)*4)}; int s_hand_rank[40]; //String buffer for hand rank. int coins; //present number of coins. int betline = -1; //number of bets int s_coind[7]; //string to hold ItoA for coins int s_bets[2]; //String to hold ItoA for bets. bool draw; bool running; int handtype = doHandType(handbuf,cards,counts,min_pair); //Establish ther hand type. //This needs a buffer so that we do not use 'hand'until we do a discard phase. //We will check handtype again after doDraw(); c[3] = getHandRankString(s_hand_rank, handtype); //Put the hand rank string into the buffer. //c[3] holds the buffer pointer. bool firstdeal = true; while ( firstdeal ) { //Initial bet entry. drawVPUI(VPUI_FELT_X, VPUI_FELT_Y, VPUI_FELT_WIDTH, VPUI_FELT_HEIGHT, VPUI_BACKGROUND_COLOUR, VPUI_FELT_SCALE); displayBetline(); //display the present number of bets. if ( KeyPress[KEY_1] ) { betline = 1; setBets(betline, s_bets); } if ( KeyPress[KEY_2] ) { betline = 2; setBets(betline, s_bets); } if ( KeyPress[KEY_3] ) { betline = 3; setBets(betline, s_bets); } if ( KeyPress[KEY_4] ) { betline = 4; setBets(betline, s_bets); } if ( KeyPress[KEY_5] ) { betline = 5; setBets(betline, s_bets); } //Press b for next hand. if ( KeyPress[KEY_B] ) { firstdeal = false; break; } Waitframe(); } running = ( doBet(betline, counter, bet_sfx) && getCards(deck,hand) ); //Shuffle deck and load cards into hand[]. while(running) { bool waiting = true; //bool betting = true; //DIsplay the cards. //Display the current hand rank. //! Keys /// 1-5 hold or unhold card 1 to 5 /// b = set bet amount /// d = draw //!Display the UI drawVPUI(VPUI_FELT_X, VPUI_FELT_Y, VPUI_FELT_WIDTH, VPUI_FELT_HEIGHT, VPUI_BACKGROUND_COLOUR, VPUI_FELT_SCALE); //DISPLAY THE CARDS drawCards(cardpositions, hand, cards, suits); //Display the cards //DISPLAY WHICH ARE HELD drawHeld(cardpositions, held); //! This might be better as an if statement. while(waiting) //Waiting on input { drawVPUI(VPUI_FELT_X, VPUI_FELT_Y, VPUI_FELT_WIDTH, VPUI_FELT_HEIGHT, VPUI_BACKGROUND_COLOUR, VPUI_FELT_SCALE); displayBetline(); //display the present number of bets. drawCards(cardpositions, hand, cards, suits); //Display the cards. //Allow player to hold cards. if ( KeyPress[KEY_1] ) { if ( holdcard_sfx ) Game->PlaySound(holdcard_sfx); held[0] = !held[0]; cardanims[0] = held[0]; } if ( KeyPress[KEY_2] ) { if ( holdcard_sfx ) Game->PlaySound(holdcard_sfx); held[1] = !held[1]; cardanims[1] = held[1]; } if ( KeyPress[KEY_3] ) { if ( holdcard_sfx ) Game->PlaySound(holdcard_sfx); held[2] = !held[2]; cardanims[2] = held[2]; } if ( KeyPress[KEY_4] ) { if ( holdcard_sfx ) Game->PlaySound(holdcard_sfx); held[3] = !held[3]; cardanims[3] = held[3]; } if ( KeyPress[KEY_5] ) { if ( holdcard_sfx ) Game->PlaySound(holdcard_sfx); held[4] = !held[4]; cardanims[4] = held[4]; } if ( KeyPress[KEY_D] ) { if ( draw_sfx) Game->PlaySound(draw_sfx); draw = true; waiting = false; break; } drawHeld(cardpositions, held); //Draw 'HOLD'string at card locations. Waitframe(); } //Do the draw //!Update cards //future location of fancy animation. //Check what cards are held, and replace un-held cards. if ( draw ) { draw = false; int display_timer = 300; bool wait_nextgame; doDraw(deal, hand, held); //Update the hand. //Update hand evaluations. handtype = doHandType(hand,cards,counts,min_pair); c[3] = getHandRankString(s_hand_rank, handtype); //!do animation //bool cardanims[5]; //holds what cards to 'flip'. //flip animated cards to reveal new cards //clear cardanims[] //Show payout. int pay = doOdds(betline); //Calculates the payout. //Make sound. if ( pay > 0 && win_sfx ) Game->PlaySound(win_sfx); if ( pay <= 0 && lose_sfx ) Game->PlaySound(lose_sfx); //Award the money. Game->DCounter[counter] += pay; //Might not want a counter for this. while( display_timer-- ) { drawVPUI(VPUI_FELT_X, VPUI_FELT_Y, VPUI_FELT_WIDTH, VPUI_FELT_HEIGHT, VPUI_BACKGROUND_COLOUR, VPUI_FELT_SCALE); displayBetline(); //display the present number of bets. //Show strings. Waitframe(); } //Pay out wait_nextgame = true; //Show Next Game Options while ( wait_nextgame ) { //Allow changing the bet amount, or exiting. if ( KeyPress[KEY_1] ) { betline = 1; setBets(betline, s_bets); } if ( KeyPress[KEY_2] ) { betline = 2; setBets(betline, s_bets); } if ( KeyPress[KEY_3] ) { betline = 3; setBets(betline, s_bets); } if ( KeyPress[KEY_4] ) { betline = 4; setBets(betline, s_bets); } if ( KeyPress[KEY_5] ) { betline = 5; setBets(betline, s_bets); } drawVPUI(VPUI_FELT_X, VPUI_FELT_Y, VPUI_FELT_WIDTH, VPUI_FELT_HEIGHT, VPUI_BACKGROUND_COLOUR, VPUI_FELT_SCALE); displayBetline(); //Show the present bet value. //Press b for next hand. if ( KeyPress[KEY_B] ) { wait_nextgame = false; break; } //Press key to begin new game. Waitframe(); } running = ( doBet(betline, counter, bet_sfx) && getCards(deck,hand) ); //Do next hand arrangement. //!! Lack of short circuiting may cause a problem doing it this way. -Z //!! MAY NEED: running = doBet(); then running = getCards() as separate instructions. //fault = nextHand(hand, handbuf, count, held, deal, delt, cardanims, c); //Do all hand arrangements. //if ( fault >= MAX_SHUFFLE_FRAMES ) { // //do machine error //} } Waitframe(); } } void drawVPUI(int felt_x, int felt_y, int feltwidth, int feltheight, int feltcolour, int feltscale) { //Draw a black field Screen->Rectangle(1, VPUI_BLACK_BG_X, VPUI_BLACK_BG_Y, VPUI_BLACK_BG_X+VPUI_BLACK_BG_WIDTH, VPUI_BLACK_BG_Y+VPUI_BLACK_BG_HEIGHT, VPUI_COLOUR_BLACK, VPUI_BLACK_BG_SCALE, 0,0,0,true, OP_OPAQUE); //Draw the felt Screen->Rectangle(1, felt_x, felt_y, felt_x+feltwidth, felt_y+feltheight, feltcolour, feltscale, 0,0,0,true, OP_OPAQUE); //Text draws go here, later. } //Processes a bet and starts the game. bool doBet(int bet, int counter, int sfx) { if ( Game->Counter[counter] >= bet ) { Game->DCounter -= bet; if ( sfx ) Game->PlaySound(sfx); return true; } return false; } //Shuffles deck and populates ten-card hand. //The hand is selected from a frame of ten cards from the shuffled pack, //with the first entity on that frame randomised, and wrapping around if //it reaches the array boundary. //! This 10-card frame is an extra randomisation factor to minimise digital RNG favourability. bool getCards(int deck, int hand) { int q = Rand(0,51); //Find the entry point of the frame. int w = q+10; int h; Shuffle(deck); for ( ; q < w; ++q ) { //Grab a frame of ten cards at random from the shuffled deck. if ( q <= 51 ) { hand[h] = deck[q]; h++; } //Populate the hand. if ( q >= 52 ) { q -= 52; h-= 52; //Wrap around hand[h] = deck[q]; h++; //Finish populating the hand. } } return true; //boolean so that we can do: // bool running = getCards(deck,hand); } void drawCards(int cardpositions, int hand, int cards, int suits){ int q; int markings[13]={'A', '2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K'}; int suitcolours[4] = { CARD_SUIT_SPADES_COLOUR, CARD_SUIT_HEARTS_COLOUR, CARD_SUIT_DIAMONDS_COLOUR, CARD_SUIT_CLUBS_COLOUR }; for ( q = 0; q < 5; ++q ) { //Shadows Screen->Rectangle(VP_CARD_LAYER, cardpositions[q]+VP_CARD_SHADOW_X_OFFSET, VP_CARD_BASE_Y+VP_CARD_SHADOW_Y_OFFSET, cardpositions[q]+VP_CARD_WIDTH+VP_CARD_SHADOW_X_OFFSET, VP_CARD_BASE_Y+VP_CARD_HEIGHT+VP_CARD_SHADOW_Y_OFFSET, VP_CARD_SHADOW, VP_CARD_SCALE, 0, 0, 0, true, VP_CARD_SHADOW_OPACITY); //Card background Screen->Rectangle(VP_CARD_LAYER, cardpositions[q], VP_CARD_BASE_Y, VP_CARD_BASE_Y+VP_CARD_WIDTH, cardpositions[q+1]+VP_CARD_HEIGHT, VP_CARD_COLOUR, VP_CARD_SCALE, 0, 0, 0, true, VP_CARD_OPACITY); //Card pips and markers. //Pips //...to add. //Markers Screen->DrawCharacter(VP_CARD_LAYER, cardpositions[q]+VP_CARD_CHAR_X_OFFSET, VP_CARD_BASE_Y+VP_CARD_CHAR_Y_OFFSET, VP_CARD_CHAR_FONT, suitcolours[ suits[ hand[q] ] ], VP_CARD_CHAR_BG_COLOUR, VP_CARD_CHAR_WIDTH, VP_CARD_CHAR_HEIGHT, markings[ cards[ hand[q] ] ], VP_CARD_CHAR_OPACITY); } } void drawHeld(cardpositions, held) { for ( int q = 0; q < 10; q+= 2 ) { //carspositions q = x, q+1 = y Screen->DrawString(VP_CARD_LAYER+1, cardpositions[q], cardpositions[q+1]+VP_CARD_HEIGHT+VP_CARD_HOLD_SPACING, VPUI_FONT, VPUI_HOLD_COLOUR, VPUI_HOLD_BG_COLOUR, 0, "HOLD", OP_OPAQUE); } } int nextHand(int hand, int handbuf, int count, bool held, int deal, bool delt, bool cardanims, int c) { //!Needs to use Shuffle() c[5] = 0; //fault timer. //wipe dealt for ( c[1] = 0; c[1] < 52; c[1]++ ) { delt[ c[1] = false; } //wipe the pack for ( c[1] = 0; c[1] < 10; c[1]++ ) { deal[ c[1] ] = -1; } //clear the count for ( c[1] = 0; c[1] < 13; c[1]++ ) { count[ c[1] ] = 0; } for ( c[1] = 0; c[1] < 5; c[1]++ ) { held[ c[1] ] = false; //clear held cards cardanims[ c[1] ] = false; //clear card anims } //! New hand //Populate the cards. for ( c[1] = 0; c[1] < 10; c[1]++ ) { do { c[0] = Rand(0,51); ++c[5]; //fault timer //If we take too long to shuffe, raise a fault. if ( c[5] > MAX_SHUFFLE_FRAMES ) { return c[5]; } } while( dealt[ c[0] ] ); //If the card was dealt, choose another. deal[ c[q] ] = c[0]; //Put the card in the deal pack. dealt[ c[0] ] = true; //Mark the card dealt. } //Populate the hand wih the first five cards. for ( c[1] = 0; c[1] < 5; C[1] ++ ) { hand[ c[1] ] = deal[ [c[1] ]; } for ( c[1] = 0; c[1] < 5; ++c[1] ) { handbuf[ c[1] ] = hand[ c[1] ]; //Copy to buffer. } //refreshes the game for the next hand, retaining the betting values. return 0; } int leaveGame() { int s_leavegame[]="Äre you sure that you wish to quit?"; int surety; //Are you sure? //!INCOMPLETE MENU if ( KeyPress[KEY_Y] ) surety = 1; if ( KeyPress[KEY_N] ) surety = -1; return surety; } void setBets(int bets, int s_bets) { ItoA(bets,s_bets); } void displayBetline(int bets, int s_bets) { Screen->DrawString(BETS_LAYER, BETS_X, BETS_Y, BETS_FONT, BETS_COLOUR, BETS_BG_COLOUR, BETS_FORMAT, s_bets, OP_OPAQUE); } //Fetch a string with the hand rank text into a buffer. int getHandRankString(int buf, int handrank) { int strings[10]={ "High Card", "One Pair", "Two Pair", "Three of a Kind", "Straight", "Flush", "Full House", "Four of a Kind", "Straight Flush", "Royal Flush"}; int ptr = strings[handrank]; //int sz = SizeOfArray(ptr); //int q; //for ( q = 0; q < sz; ++q ) { buf[q] = ptr[q]; } //buf[q+1] = NULL; //Terminate the string. strcpy(buf, ptr); return buf; //Return the pointer to the buffer on success. } int doHandType(int hand, int cards, int counts, int min_pair) { bool pryle; int pairs; bool onepair; bool twopairs; bool quads; bool flush; bool straight; bool broadway; bool fullhouse; bool straightflush; bool royal; int hand_type; flush = isFlush(hand,cards); straight = isStraight(hand,cards); broadway = isBroadway(hand,cards); if ( straight & flush ) straightflush = true; if ( broadway && flush ) royal = true; if ( !flush && !straight && !broadway ) { hand_type = handRand(hand, cards, counts); if ( hand_type == HT_ONEPAIR && min_pair > 0 ) { if ( !minPair(counts, min_pair) ) return 0; } } if ( royal ) return HT_ROYAL; if ( straightflush ) return HT_STRAIGHTFLUSH; if ( flush ) return HT_FLUSH; if ( straight ) return HT_STRAIGHT; else return hand_type; //Quads, full house, trips, two pair, //and one pair if ( the pair is > min_pair ) or //there is no min pair. //if ( quads ) return HT_QUADS; //if ( fullhouse ) return HT_FULLHOUSE; //if ( pryle ) return HT_TRIPS; //if ( twopairs ) return HT_TWOPAIR; //if ( onepair ) return HT_ONEPAIR; return 0; //Dead hand. } bool isStraight(int hand, int cards) { int sorted[5]; //store numeric values from cards[] for ( int q = 0; q < 5; ++q ) { sorted[q] = cards[ hand[q] ]; //The numeric values. } //Now, sort the array in a descending manner. sortArray(cards, 5); //! bool match; for ( int q = 0; q < 4; ++q ) //Not all five. { if ( sorted[q+1] == sorted[q] -1 ) { match = true }; else match = false; } return match; } bool isBroadway(int hand, int cards) //A-10 Straight { int sorted[5]; //store numeric values from cards[] for ( int q = 0; q < 5; ++q ) { sorted[q] = cards[ hand[q] ]; //The numeric values. } //Now, sort the array in a descending manner. sortArray(cards, 5); //! bool match; for ( int q = 0; q < 3; ++q ) { //Check the first four, and read ito the fourth only. if ( sorted[q+1] == sorted[q] -1 ) { match = true }; else match = false; } if ( sorted[5] != 1 ) { match = false; } //it must be an Ace. return match; } bool isFlush(int hand, int suits) { int suit; bool match; suit = suits[ hand[0] ]; for ( int q = 1; q < 5; ++q ) { if ( suits[ hand[q] ] == suit ) { match = true; } else match = false; } return match; } //Hand ranks using matched cards. int handRank(int counts) { int pairs; bool pryle; bool quads; bool fullhouse; for ( int q = 0; q < 13; ++q ) { if ( counts[q] == 4 ) quads = true; if ( counts[q] == 3 ) pryle = true; if ( counts[q] == 2 ) ++pairs; } if(pryle) { switch(pairs) { case 1: return HT_FULLHOUSE; default: return HT_TRIPS; } } else { switch(pairs) { case 2: return HT_TWOPAIR case 1: return HT_ONEPAIR; case 0: return 0; } } } //If only one pair, checks that the pair is at at least the minimum type. bool minPair(int counts, int highcard) { int card; for ( card = 0; card < 13; card++ ) { if ( counts[q] == 2 ) { break; } } if ( card >= (highcard -1) ) return true; return false; } void doOdds(int handtype) { //int odds[] = { ODDS_NOPAIR, ODDS_PAIR, ODDS_TWOPAIR, ODDS_TRIPS, ODDS_STRAIGHT, ODDS_FLUSH, // ODDS_FULLHOUSE, ODDS_QUADS, ODDS_STRAIGHTFLUSH, ODDS_ROYAL } ; int payout[] = { 0, 1, 2, 3, 4, 6, 9, 25, 50, 800 }; return payout[handtype]; } //Draw the cards. Populates the un-held slots in the present hand with the preselected cards. void doDraw(int deal, int held, int hand ) { for ( int q = 0; q < 5; ++q ) { unless ( held[q] ) { hand[q] = deal[q+5]; } } } void sortArray(int ptr, int size) { unless(IsValidArray(ptr)) { printf("Invalid array passed to sortArray\n"); return; } if (size < 2) {return;} for (int i = 1; i < size; ++i) { int j = i; int j_val = ptr[j]; while (j > 0) { if (ptr[j - 1] <= j_val) {break;} int temp = ptr[j - 1]; ptr[j - 1] = ptr[j]; ptr[j] = temp; --j; } } } void doCounts(int cards, int hand, int counts) { int pairs = 0; for (int i = 0; i < 5; ++i) { int rank = cards[hand[i]]; ++counts[rank - 1]; //if (counts[rank - 1] % 2 == 0) pairs++; } //return pairs; } } } /* int countPairs(int cards, int hand) { int counts[13]; int pairs = 0; for (int i = 0; i < 5; ++i) { int rank = cards[hand[i]]; counts[rank - 1]++; if (counts[rank - 1] % 2 == 0) pairs++; } return pairs; } */