Thunderjack! HTML5 Game Update: Stand and Double Down

Hello! I hope your holiday season has been a safe and fun one. I spent Christmas day out with extended family, and got to spend some time with some people I don’t see that often. πŸŽ„ And of course the food was outstanding – gotta get a little of everything! πŸ–πŸ πŸ’ͺ🏿

In this article, we’ll be handling two more moves you can perform in the game: standing, and doubling down.

We’ve already covered the first move, hitting, which you can check out here.

  • Hit
  • Stand
  • Double
  • Split
  • Surrender

Stand

cIn the game of Thunderjack!, if you feel you have a competitive score, or do not wish to risk busting with a hit, you can stand. This will end your turn, and pass the turn to the next player. If there are no more players playing against the dealer, then it will be the dealer’s turn. Standing is a perfectly safe move, and can be performed at any time during your turn.

Here, after a cuppa hits, you have a pretty strong hand with four cards and 19 points. 😎 Standing sounds like a good idea… unless you’re crazy of course. You could go for another hit – you never you – you just might draw an Ace or a two! But no. Keeping your sanity, you decide to stay, and as you can see it’s now the next player’s turn. You will play against the dealer with 19 points.

(Even though the dealer has 12 points with both cards exposed, haha! You may have noticed that throughout the development of this game. It was intentional during the development of the game. Later on, we’ll hide one of the dealer’s cards until later in the round.)

First, let’s create a button that will allow the player to stand when pressed. The steps we use to create the button (using Phaser Editor are similar to what we did in the previous article with the hit button:

  1. Right-click on the main game canvas, then select Add, then Button.
  2. From the Add Sprite dialog, select the image you want to use as the main button sprite, then press OK.
  3. This will add a button to your canvas. You can drag it to position it where ever you want.
  4. In the button’s properties, set the field property to true. This will create a reference to the button in the code that the canvas compiler generates, allowing you to reference it as well.
  5. Set a varName for the button. I decided to use the game_button_stand.
  6. Set the callback to this._onStandButtonPressed, and set the callbackContext to this.
  7. Set the four “Frame” properties to the appropriate sprite images, so that the button appears responsive when the it is moused over, pressed, and released.

When the canvas is saved, it’ll add the code that creates and positions the button, as well as specify the callback function onStandButtonPressed. That function:

onStandButtonPressed
GamePlay.prototype._onStandButtonPressed = function() {
  this._beginTurnPlayerStand();
};
beginTurnPlayerStand
GamePlay.prototype._beginTurnPlayerStand = function() {
  this._setNextTurnPlayer();
};

When the stand button is pressed, it calls another function, beginTurnPlayerStand. Hmm, why not just put that here? Because, we’ll be using this functionality for both, the players, and the dealer. Standing is also used by the dealer, after doubling down, and if you want the player to automatically stand after splitting a pair of aces. So, we want the function to be easily accessible to those moves as well. Also, should we need to add other functionality that is exclusive to standing, we can add it to this function.

As you can see, beginTurnPlayerStand simply calls setNextTurnPlayer, which we’ve already seen when setting up our hit function earlier.

That’s it for implementing the standing move. We’ll move onto the next move: doubling down.

Double Down

This move can only be performed on your opening hand. At the cost of a new wager equal to the bet of the hand, you can take one more card (a hit). Then, you must stand; the game will automatically end your turn. If you beat the dealer, you will win twice your original wager. Beware: like a hit, there is a risk that you will bust, causing you to lose your new wager. There is also that, even after the hit, your score may still not be high enough to beat the dealer, resulting in lost wages.

With a score of 9, the bottom center player decided to take a chance and double their original bet of 10, making it 20.

One more card was dealt, then they must automatically stand.

The dealer busted, and the player won double their winnings, bringing their total winnings to 40.

So, we’ll break the double down function into four sections in this article:

  1. Adding the double down button to the canvas
  2. Showing the double down button when appropriate during a player’s turn
  3. Implementing the actual double down function
  4. Awarding double the winnings if the player beats the dealer
1. Adding the double down button to the canvas

First, we’ll create a button that will allow the player to double down when pressed. The steps we use to create the button very similar to what we did in the the stand button. The varName will be game_button_double, and the callback is onDoubleDownButtonPressed.

onDoubleDownButtonPressed
GamePlay.prototype.onDoubleDownButtonPressed = function() {
  this._beginTurnPlayerDoubleDown();
};
2. Showing the double down button when appropriate during a player’s turn

Next, we’ll implement thebeginTurnPlayerDoubleDown later in this article. For now, you can leave it as an empty function.

Doubling down is only available on the opening hand of a player’s turn, so the button should only be available during their first move. We have a setTurnPlayer function that we defined earlier:

setTurnPlayer
GamePlay.prototype._setTurnPlayer = function(i_turnPlayerIndex) {
  this._hideAllGameButtons();
  
  this._turnPlayerIndex = i_turnPlayerIndex;
  var s_playerId = this._getTurnPlayerId();
  if (!s_playerId) {
    //no turn player to set 
    return(false);
  }
   
  if (this._isDealerId(s_playerId)) {
    this._beginDealerTurn();
  } else {
    this._beginPlayerTurn(s_playerId);
  }
  
  return(true);
};

First, we want to hide all the game buttons, because the state of each player will be different after every move. When we are done setting the player’s turn, the updated setTurnPlayer will have only the appropriate game buttons visible for the player. We’ll add this new function:

hideAllGameButtons
GamePlay.prototype._hideAllGameButtons = function() {
  this.fGame_button_hit.visible = this.fGame_button_stand.visible = this.fGame_button_double.visible = false;
};

Also in setTurnPlayer, there is the _beginPlayerTurn function. We’ll use this to set up which move buttons. We haven’t really needed it until now, because the hit and stand buttons are always visible during a player’s turn.

beginPlayerTurn
GamePlay.prototype._beginPlayerTurn = function(s_playerId) {
  var playerHandData = this._playerData.getHandData(s_playerHandId);
  this._showGameButtonsForPlayer(playerHandData);
};

In this function, we first get the PlayerHandData of the current turn player. On the PlayerData class, we’ll add a new function, getHandData.

getHandData
PlayerData.prototype.getHandData = function (s_playerHandId) {
  return(this._hands[s_playerHandId]);
};

Next, there is a showGameButtonsForPlayer function. This will display the buttons appropriate for the current player, and hide the rest.

showGameButtonsForPlayer
GamePlay.prototype._showGameButtonsForPlayer = function(playerHandData) {
  this.fGame_button_hit.visible = true;
	this.fGame_button_stand.visible = true;
  this.fGame_button_double.visible = this._isDoubleEligible(playerHandData);
};

The hit and stand buttons are always visible for every move. However, setting visibility of the double down button is not a simple true or false. In order to keep showGameButtonsForPlayer simple, an isDoubleEligible function is created, which will determine whether or not the double down button should be shown.

isDoubleEligible
GamePlay.prototype._isDoubleEligible = function(playerHandData) {
return(
  this._isOpeningHand(playerHandData) &&
  this._canAffordDouble(playerHandData));
};

The double down button will be visible only on the player’s opening hand (when the player has two cards), and the player must have enough credits to afford placing the double wager.

isOpeningHand
GamePlay.prototype._isOpeningHand = function(playerHandData) {
  var NUM_CARDS_IN_OPENING_HAND = 2;
  return(playerHandData.getNumCards() === NUM_CARDS_IN_OPENING_HAND);
};
canAffordDouble
GamePlay.prototype._canAffordDouble = function(playerHandData) {
  return(this._playerData.credits >= playerHandData.betValue);
};

With these changes, adding and showing the double down button is complete. It will only appear on the opening hand of each player’s turn, and if the player has enough credits to afford doubling down. Next, we’ll focus on the double down functionality.

3. Implementing the actual double down function

This step is much easier than the the last, because we’ve already seen most of the code. The implementation consists of:

  • deducting the cost of the double down wager from the player’s credits
  • updating the player hand’s status to indicate double down
  • drawing one card (taking a hit)
  • automatic standing (if the player did not bust)

We’ll revisit our empty beginTurnPlayerDoubleDown method and place the code in it.

beginTurnPlayerDoubleDown
GamePlay.prototype._beginTurnPlayerDoubleDown = function() {
  this._setTurnPlayerDoubleDownStatus();
  this._payDoubleDownCostForTurnPlayer();
  this._dealDoubleDownCardToTurnPlayer();
};
setTurnPlayerDoubleDownStatus
GamePlay.prototype._setTurnPlayerDoubleDownStatus = function() {
  this._getTurnPlayer().hasDoubled = true;
};
payDoubleDownCostForTurnPlayer
GamePlay.prototype._payDoubleDownCostForTurnPlayer = function() {
  var playerHandData = this._getTurnPlayer();
  this._playerData.credits -= playerHandData.betValue;
};
dealDoubleDownCardToTurnPlayer
GamePlay.prototype._dealDoubleDownCardToTurnPlayer = function() {
  var turnPlayer = this._getTurnPlayer();
  
  this._dealCardToPlayer(turnPlayer);
  this.updateScores();
  if (this.didPlayerBust(turnPlayer)) {
    this._beginPlayerBust(turnPlayer);
    this._setNextTurnPlayer();
  } else {
    this._beginTurnPlayerStand();
  }
};

The dealDoubleDownCardToTurnPlayer is very similar to the beginTurnPlayerHit, except that if the player did not bust, they will automatically stand. Either way, the player’s turn ends after doubling down.

4. Awarding double the winnings if the player beats the dealer.

If the player beats the dealer, they will be awarded double their winnings. This final piece of code handles that. This is where setting the hasDoubled property of the player’s hand to true.

You may have seen an endRound function used in the pseudo code from this article, and we’ll begin coverage for that here.

endRound
GamePlay.prototype._endRound = function() {
  if (this._dealerData.isBust) {
    this._presentPlayersWinViaDealerBust();
  } else {
    this._compareCards();
  }
  
  this._showBetUi();
};

If the dealer busts, all players that are playing against the dealer automatically win. And players who doubled down will win double their winnings. Otherwise, those same players will have their scores compared against the dealer’s.

presentPlayersWinViaDealerBust
GamePlay.prototype._presentPlayersWinViaDealerBust = function() {
  var a_playerHands = this._playerData.getAllHands();
  
  for (var i_index = 0; i_index < a_playerHands.length; i_index++) {
    var playerHandData = a_playerHands[i_index];
    if (this._isPlayingVsDealer(playerHandData)) {
      this._presentPlayerWon(playerHandData);
    }
  }
};

The isPlayingVsDealer determines if a player is playing against the dealer. Our last implementation of the function looked like this:

function isPlayingVsDealer(handData) {
  return(
    !(playerHandData.hasBlackjack ||
    playerHandData.isPush ||
    playerHandData.hasThunderjack));
}

We need to update this to include players that have not busted are also eligible to play against the dealer:

isPlayingVsDealer
function isPlayingVsDealer(handData) {
  return(
    !(playerHandData.hasBlackjack ||
    playerHandData.isPush ||
    playerHandData.isBust ||
    playerHandData.hasThunderjack));
}
presentPlayerWon
GamePlay.prototype._presentPlayerWon = function(playerHandData) {
  var n_betValue = playerHandData.betValue;
  var WIN_RATIO_WIN = 1 / 1;
  var n_amountWon = this._calcWinAmount(n_betValue, WIN_RATIO_WIN); 
  
  if (playerHandData.hasDoubled) {
    n_amountWon *= 2;
  }
  
  this._playerData.credits += n_amountWon;
};

The presentPlayerWon function is where the winnings are added to the player’s credits. Inside the function, if the player has doubled down, the amount won is doubled.

But, what’s this calcWinAmount function, you ask? (:

That’s what determines how much the player won, based on the hand’s bet value.

calcWinAmount
GamePlay.prototype._calcWinAmount = function(n_betValue, n_winRatio) {
  return(n_betValue + n_betValue * n_winRatio);
};

The WIN_RATIO_WIN value is treated as a constant and is defined as 1 / 1. This reads as “one-to-one”, or “1:1”, meaning equal to the wager. For example, if the player placed a bet of 10 credits and won, they’d win (10 + 10 * 1) = 20 credits. There are also other ratios for other types of wins, such as blackjack (3:2), or dealer busts (1:1).

If the player is playing against the dealer, we want to compare the player’s score with the dealer’s. This is what the compareCards function does.

compareCards
GamePlay.prototype._compareCards = function() {
  var i_dealerScore = this._dealerData.score;
  
  var a_playerHands = this._playerData.getAllHands();
  for (var i_index = 0; i_index < a_playerHands.length; i_index++) {
    var playerHandData = a_playerHands[i_index];
    if (!this._isPlayingVsDealer(playerHandData)) {
      continue;
    }
    
    if (i_dealerScore < playerHandData.score) {
      this._presentPlayerWon(playerHandData);
    } else if (i_dealerScore === playerHandData.score) {
      this._presentPlayerPush(playerHandData);
    }
  }
};
presentPlayerPush
GamePlay.prototype._presentPlayerPush = function(playerHandData) {
  playerHandData.isPush = true;
  this._playerData.credits += playerHandData.betValue;
};

Finally in the endRound function, the showBetUi function will show all the controls that help us place bets, these are the:

  • hand buttons
  • bet chip buttons
  • bet button
  • clear button

That does it for this standing and doubling down. The next article will cover splitting, which allows us to create two hands from a hand of two cards with the same face value.

Whew! We covered a lot in this article, including payouts, comparisons against the dealer, and helping set the stage for future development of the remaining two moves, splitting and surrendering. If you read all of this, and you’re still with me, kudos! ⭐

And here is a video that shows standing and doubling down. On the third round, I did win a double-down bet, turning my original wager of 10 into winnings of 40.

Thanks, and take care,

– C. out.

Thunderjack! HTML5 Game Update: Completed the Logic for Thunderjack Hands

Hey there.

Welcome back!
Next we’re going to add what happens when a player gets a hand of the same name as the game itself: a Thunderjack!

PFCHSHSH-KCGHOGHOGHOUGHOGHOUGHWGHWGHW… “THUNDERJACK!!” (spelling of a thunder sound effect)

A Thunderjack is a suited (both cards have the same suit) Blackjack, and the non-ace card is a Jack or higher. The following hands are examples of Thunderjack hands:

Ace Clubs + Jack Clubs
Queen of Hearts + Ace of Hearts
King of Spades + Ace of Spades

However, an Ace of Diamonds and 10 of Diamonds is not a Thunderjack, but a regular Blackjack, even though the cards are of the same suit.

There are also some additional criteria for a Thunderjack:

  1. Only one player hand can get a Thunderjack. If more than one player gets a Thunderjack, those affected hands cancel each other out and push.
  2. The one player with a Thunderjack is payed out immediately.
  3. That player also does not play against the dealer.
  4. If the dealer gets a Blackjack, the player is still paid the Thunderjack payout. The result is not a push.
  5. The dealer can not get a Thunderjack. Even if the dealer gets a suited Blackjack with the non-ace card being a Jack or higher, the result is still a Blackjack.

Just like checking for a regular Blackjack, we will check for a Thunderjack after all the players and dealer have received been dealt their first two cards. We’ll need to change our original code that involves checking the opening hands for Blackjack. If you recall from the previous article, the pseudo code looks like this:

if hasBlackjack(dealer) {
   handleDealerBlackjack();
   endRound();
 } else {
   checkIfPlayersHaveBlackjacks();
   setFirstTurnPlayer();
 }

First, we need to store a few variables. We want to determine which player, if any, has a Thunderjack. This code checks every player’s hand for a suited Blackjack where the non-ace card is a face card. The pseudo code now looks like:

var playerWithThunderjack = checkIfPlayerHasThunderjack()
var playersWithBlackjack = checkIfPlayersHaveBlackjacks()
var doesDealerHaveBlackjack = hasBlackjack(dealer)
if playerWithThunderjack {
  handleThunderjack(playerWithThunderjack)
  endRound()
} else {
  if doesDealerHaveBlackjack {
    handleDealerBlackjack()
    endRound()
  } else {
    setFirstTurnPlayer()
  }
}

All of the Thunderjack handling happens in the checkIfPlayerHasThunderjack() function. If there is more than one player with a Thunderjack, all affected players push. If only one player qualifies, we return that player for later use.

function checkIfPlayerHasThunderjack() {
  var playersWithTj = getAllPlayersWithTj();
  if playersWithTj.length > 1 {
    handleMultipleThunderjacks(playersWithTj);
    return null;
  } else if (playersWithTj.length == 0) {
    return null;
  }
  return playersWithTj[0];
}

This function determines all players, if any, that have a Thunderjack, and returns them in an array. The array will be empty if no players qualify.

function getAllPlayersWithTj() {
  var playersWithTj = []; // set up an empty array
  //do not include the dealer here - only the players, because dealer can not get a thunderjack
  for each player in the game {
    if hasThunderjack(player) {
      playersWithTj.push(player); // add this player to the array
    }
  }
  return playersWithTj;
}

If we have more than one player with a Thunderjack, they all push:

function handleMultipleThunderjacks(playersWithTj) {
  for each player in playersWithTj {
    player.handData.isPush = true;
  }
}

The criteria for getting a Thunderjack is defined in this function:

function hasThunderjack(player) {
  return hasBlackjack(player) &&
    isFlush(player) &&
    isLowCardAtLeast(player, Thunderjack.CardValues.JACK);
}

This function handles the one player that has Thunderjack:

function handleThunderjack(player) {
  player.handData.hasThunderjack = true;
}

One requirement for a Thunderjack is that both cards must be the same suit (like a flush in Poker). Since this will only be run against the player’s opening hand of two cards, was can assume indexes 0 and indexes 1 in the player.cardsData array.

function isFlush(player) {
  var firstCardData = player.cardsData[0];
  var secondCardData = player.cardsData[1];
  return firstCardData.suit == secondCardData.suit;
}

We’re using the same two cards. Let’s use a lowestCardValue parameter instead of making a function “isLowCardAtLeastAJack()”. That way, if we want to change this requirement in the future, all we have to do is change what is being passed as lowestCardValue.

function isLowCardAtLeast(player, lowestCardValue) {
  var firstCardData = player.cardsData[0];
  var secondCardData = player.cardsData[1];
  return
    isCardValueAtLeast(firstCardData, lowestCardValue) &&
    isCardValueAtLeast(secondCardData, lowestCardValue);
}

function isCardValueAtLeast(cardData, lowestCardValue) {
  var indexOfLowestCardValue = Thunderjack.CardValues.ALL.indexOf(lowestCardValue);
  var indexOfCardDataValue = Thunderjack.CardValues.ALL.indexOf(cardData.cardValue);
  return indexOfCardDataValue >= indexOfLowestCardValue;
}

In the Thunderjack.CardValues.ALL array, all the card values are defined in order of lowest rank to highest rank (Ace being the highest here). The JavaScript Array class has a method called indexOf, which will search the array for a specified value, and return it’s index, or position, within the array. So, what we do here is get the index of the cardValue of our CardData, and compare it to the index of the specified indexOfLowestCardValue.

The CardData’s index must be at least as high lowCardValue’s in order to qualify. Since we specified Thunderjack.CardValues.JACK as the lowest card value, our CardData’s card value must be at least a Jack, just like it was defined in the Thunderjack requirements.

This completes handling of players with Thunderjacks. After all opening hands have been resolved, and the game has not ended due to a player’s Thunderjack, or a dealer’s Blackjack, we want to set the first turn player.

From the previous article, we had a setFirstTurnPlayer function that sets the turn to the first player eligible to play against the dealer.

function setFirstTurnPlayer() {
  var turnPlayerIndex = getNextTurnPlayerIndex(0); 
  if (turnPlayerIndex !== -1) {
    setTurnPlayer(turnPlayerIndex);
  } else {
    endRound();
  }
}

The function that setFirstTurnPlayer uses to determine which player goes first is getNextTurnPlayerIndex. This function gets the index of the next player that is eligible to play against the dealer. Players are stored in an array, in this order: right, middle, left, dealer. When setFirstTurnPlayer runs, it calls getNextTurnPlayerIndex with startTurnPlayerIndex value set to 0. This allows us to check the first array index.

Note: Later in the game, when we’re moving onto the next player, getNextTurnPlayerIndex will have a value of whatever the current turn player index sent to it, plus one. For now, don’t worry about it. We’ll get to this in the next article.

function getNextTurnPlayerIndex(startTurnPlayerIndex) {
  var index = startTurnPlayerIndex;
  while (index < turnPlayerIds.length) {
    var playerId = turnPlayerIds[index];
    var handData = playerHandsData[playerId];
    if (isPlayingVsDealer(handData)) {
      return(index);
    }
    index++;
  }
  return(-1);
}

The getNextTurnPlayerIndex function in turn, calls isPlayingVsDealer, and this is the function we’re interested in. As shown, a player is eligible to play against the dealer if the player does not have a Blackjack, and has not pushed.

function isPlayingVsDealer(handData) {
  return(
    !(playerHandData.hasBlackjack ||
    playerHandData.isPush));
}

We want to edit this function so that it includes checking if the player does not have a Thunderjack. If so, the player is eligible.

function isPlayingVsDealer(handData) {
  return(
    !(playerHandData.hasBlackjack ||
    playerHandData.isPush ||
    playerHandData.hasThunderjack));
}

The game now allows us to properly determine if any players have a Thunderjack, and to obtain the first turn player, skipping over any players with a Thunderjack (because they are ineligible to play against the dealer).

In the next article, we’ll get into placing bets, which I touched on in an earlier post, but will cover in more detail.

Let me know if you have any questions. Thanks for staying with me, guys. Take care.

– C. out.

Thunderjack! HTML5 Game Update: Completed the Logic for Blackjack Hands

Hello again!

We have more updates available for the HTML5 version of Thunderjack! using Phaser.

This time, we’re covering what happens when one or more players or the dealer gets a Blackjack.

As you may be aware, when a player gets a Blackjack on the opening hand, they are immediately paid, and do not play against the dealer. When the game first starts players who do have Blackjack will have their turns skipped, and the game starts with the first non-Blackjack player. If all players get a Blackjack, then the game ends.

If the dealer gets a Blackjack, then the game ends immediately. If any players also have a Blackjack, their result is a Push (the player gets their original bet back). All non-Blackjack players lose their bets.

Let’s get started on the logic for this might look like.

After all the cards are dealt, here is the basic rundown of the logic:

  1. Calculate the scores of all players and dealer
  2. Determine which hands, if any, have Blackjacks
  3. If the dealer has a blackjack:
    1. For each player that has a Blackjack, result is a push
    2. End game
  4. If the dealer does not have a Blackjack:
    1. For each player that has a Blackjack, is paid immediately, and does not play against the dealer
    2. Game starts with the first non-Blackjack player taking their turn.

We’ll tackle those one at a time.

1) Calculate the scores of all players and dealer

Every time one or more player’s or dealer’s hands change cards, we need to calculate how many points they have. As I hinted in this post, we have a Thunderjack.HandData class

Thunderjack.HandData = function(handId) {
  this.cardsData = [];
  this.handId = handId;
  this.score = 0;
  this.hasBlackjack = false;
};

We’re interested in the cardsData and score properties. The dealer will use this class to store data.

For the player, we need additional data that is exclusive on to the player. The player’s hand class looks like:

Thunderjack.PlayerHandData = function(handId) {
  Thunderjack.HandData.call(this, handId);
  this.isPush = false;
};

Because PlayerHandData inherits from HandData, it here we’re interested in the isPush property. The dealer can never push, which is why this property is nor defined in HandData.

We got into dealing the cards in this post. After the cards are dealt to a player, their cardsData property will contain the array of Thunderjack.CardData objects. The class looks like:

CardData
Thunderjack.CardData = function(cardValue, cardSuit, deckIndex) {
  this.value = cardValue;
  this.suit = cardSuit;
  this.deckIndex = deckIndex;
  this.id = this.value + "_" + this.suit + "_" + this.deckIndex;
};

We also talked Thunderjack.CardValues and the numeric values for each card:

CardPoints
Thunderjack.CardPoints = { };
Thunderjack.CardPoints[Thunderjack.CardValues.TWO] = 2;
Thunderjack.CardPoints[Thunderjack.CardValues.THREE] = 3;
Thunderjack.CardPoints[Thunderjack.CardValues.FOUR] = 4;
Thunderjack.CardPoints[Thunderjack.CardValues.FIVE] = 5;
Thunderjack.CardPoints[Thunderjack.CardValues.SIX] = 6;
Thunderjack.CardPoints[Thunderjack.CardValues.SEVEN] = 7;
Thunderjack.CardPoints[Thunderjack.CardValues.EIGHT] = 8;
Thunderjack.CardPoints[Thunderjack.CardValues.NINE] = 9;
Thunderjack.CardPoints[Thunderjack.CardValues.TEN] = 10;
Thunderjack.CardPoints[Thunderjack.CardValues.JACK] = 10;
Thunderjack.CardPoints[Thunderjack.CardValues.QUEEN] = 10;
Thunderjack.CardPoints[Thunderjack.CardValues.KING] = 10;
Thunderjack.CardPoints[Thunderjack.CardValues.ACE] = 1;

Now let’s wrote some code that figures out the players’ hand scores based on their cards. For the most part, it’s just adding up the values, but things are a little tricky with the ace, since it can be one or eleven.

CalcScore
Thunderjack.CalcScore.ACE_POINTS_REMAINING = 10;
Thunderjack.CalcScore.Eval = function (cardsData) {
  var points = 0;   var containsAnAce = false;
  for (var index = 0; index < cardsData.length; index++) {
    var cardData = cardsData[index];
    cardPoints = Thunderjack.CardPoints[cardData.value];
    if (cardData .value === Thunderjack.CardValues.ACE) {
      containsAnAce = true;
    }
    points += cardPoints;
  }

  if (containsAnAce) {
    var proposedValue = points + Thunderjack.CalcScore.ACE_POINTS_REMAINING;
    if (proposedValue <= Thunderjack.DesignData.BLACKJACK_POINTS) {
      points = proposedValue;
    }
  }
  return(points);
};


We’d call the Thunderjack.CalcScore.Eval function and send it the cardsData property of the hand we’re interested in.

We get the points of an individual card based on its value, then look it up in the Thunderjack.CardPoints object. For aces, we always add 1 point (for now), and we note that we’ve encountered at least one ace with the containsAnAce boolean.

If the hand contains an ace, we know that we can add the remaining 10 points of the high value of the ace to the points, as long as the results doesn’t exceed 21 (Thunderjack.DesignData.BLACKJACK_POINTS).

We eventually stored the returned result in the player’s hand. After all the cards have been dealt, we have these functions:

updateScores
function updateScores() {
  for each player in the game {
    player.handData.score = Thunderjack.CalcScore.Eval(player.handData.cardsData);
  }
  dealer.handData.score = Thunderjack.CalcScore.Eval(dealer.handData.cardsData);
}


Now the have the scores of all the players and dealer, we can evaluate their opening hands.

2) Determine which hands, if any, have Blackjacks. Here’s a quick pseudocode of the logic.

Here’s the break down:

if hasBlackjack(dealer) {
  handleDealerBlackjack();
  endRound();
} else {
  checkIfPlayersHaveBlackjacks();
  setFirstTurnPlayer();
}

Let’s start with determining if has a hand has a blackjack.

hasBlackjack
function hasBlackjack(player) {
  return (player.handData.score === Thunderjack.DesignData.BLACKJACK_POINTS);
}


As long as hasBlackjack is called after the scores have been update, the results will be accurate.

3) If the dealer has a blackjack:
3a) For each player that has a Blackjack, result is a push
3b) End game

We have a function specific to when the dealer has a Blackjack:

handleDealerBlackjack
function handleDealerBlackjack() {
  for each player in the game {
    if hasBlackjack(player) {
      player.handData.isPush = true;
    }
  }
  endRound();
}

Note that when the player has a Blackjack, we did not set the hasBlackjack property to true. Th is because the results is a push, even though the player got a Blackjack on their opening hand, they’re not being paid a Blackjack.

4 If the dealer does not have a Blackjack:
4a) For each player that has a Blackjack, is paid immediately, and does not play against the dealer
4b) Game starts with the first non-Blackjack player taking their turn

However, if the dealer does not have a Blackjack, we have code that handles below:

checkIfPlayersHaveBlackjacks
function checkIfPlayersHaveBlackjacks() {
  var bjHands = []
  for each player in the game {
    if hasBlackjack(player) {
      player.handData.hasBlackjack = true
      add player to bjHands array
    }
  }
  return bjHands
}
setFirstTurnPlayer
function setFirstTurnPlayer() {
  var turnPlayerIndex = getNextTurnPlayerIndex(0);
  if (turnPlayerIndex !== -1) {
    setTurnPlayer(turnPlayerIndex);
  } else {
    endRound();
  }
}

We’ve a some new function with setFirstTurnPlayer. There is a specific order in which the players take their turns. I’ve defined it as:

  1. Right player
  2. Middle player
  3. Left player
  4. Dealer

These are stored in an array called turnPlayerIds, and it will store the values we defined in Thunderjack.PlayerHandIds. These IDs were introduced in this article.

Thunderjack.PlayerHandIds.LOWER_RIGHT = "lowerRight";
Thunderjack.PlayerHandIds.LOWER_MIDDLE = "lowerMiddle";
Thunderjack.PlayerHandIds.LOWER_LEFT = "lowerLeft";
Thunderjack.PlayerHandIds.UPPER_RIGHT = "upperRight";
Thunderjack.PlayerHandIds.UPPER_MIDDLE = "upperMiddle";
Thunderjack.PlayerHandIds.UPPER_LEFT = "upperLeft";


So, assuming that all three player hands are active, turnPlayerIds would look like:

turnPlayerIds
turnPlayerIds = [
  Thunderjack.PlayerHandIds.LOWER_RIGHT,
  Thunderjack.PlayerHandIds.LOWER_MIDDLE,
  Thunderjack.PlayerHandIds.LOWER_LEFT,
  Thunderjack.PlayerHandIds.DEALER
];

(The “upper” hands are used for splitting, which we’ll go over in a later article.)

The first turn player will be the one who is eligible to play against the dealer. This is the player who did not get a Blackjack on their opening hand.

getNextTurnPlayerIndex
function getNextTurnPlayerIndex(startTurnPlayerIndex) {
  var index = startTurnPlayerIndex;
  while (index < turnPlayerIds.length) {
    var playerId = turnPlayerIds[index];
    var handData = playerHandsData[playerId];
    if (isPlayingVsDealer(handData)) {
      return(index);
    }
    index++;
  }

  return(-1);
}

The playerHandsData variable is an object that can store the player hands, indexed by their player IDs.

The isPlayingVsDealer function is what we can use to determine if a player will play vs the dealer. It looks like:

isPlayingVsDealer
function isPlayingVsDealer(handData) {
  return(!
    (playerHandData.hasBlackjack ||
    playerHandData.isPush));
};


If a player does not have a Blackjack or a push, they will play against the dealer. If there is no player to play against the dealer (getNextTurnPlayerIndex would return -1), then the game would end.

That’s it for this post. We covered quite a bit in these few steps, but most of the opening hand logic is done. In the next article, we’ll add my special feature to this game – winning a hand via Thunderjack! Stay tuned, and take care.

– C. out.

Thunderjack! HTML5 Game Update: Cheating? Yes But No

Hey guys. We’re into getting into some of the updates mentioned from my past post – mainly checking if an opening hand has a Blackjack, and eventually other specific combinations of cards in the hand. And there’s just one little problem… it can take all day to get the cards we need in order to test.

So, we’ll need a way to control the order in which cards are drawn. Otherwise, we may waiting a long time before we’re dealt the cards we want. So, we’ll have to cheat and pull a fast one here.

But before we get into that, let’s take a look at how the game sets up the cards and the deck.

Card Suits And Values

As you know, there are four suits and we have values from 2 to Ace. Let’s start with the suits first.

In Thunderjack!, we have a class called CardValues that acts like an enumeration, similar to what you might see in C# or Java. Except, we’ll be storing strings instead of integral values.

Thunderjack.CardSuits.HEARTS = "h";
Thunderjack.CardSuits.DIAMONDS = "d";
Thunderjack.CardSuits.CLUBS = "c";
Thunderjack.CardSuits.SPADES = "s";
Thunderjack.CardSuits.ALL = [
  Thunderjack.CardSuits.HEARTS,
  Thunderjack.CardSuits.DIAMONDS,
  Thunderjack.CardSuits.CLUBS,
  Thunderjack.CardSuits.SPADES
];

The ALL array is useful in iterating through all of the suits, which we’ll do later.

When storing the card values, you may think we’d use a number. However, we’ll going to use both string and number. The strings for suits and values will be used when creating unique IDs. We’ll get into these later, but for now, let’s have a look at the values.

We also have another ALL array for iterating through all of the values.

The CardPoints object stores the values, indexed by the string values defined above.

Thunderjack.CardValues.TWO = "2";
Thunderjack.CardValues.THREE = "3";
Thunderjack.CardValues.FOUR = "4";
Thunderjack.CardValues.FIVE = "5";
Thunderjack.CardValues.SIX = "6";
Thunderjack.CardValues.SEVEN = "7";
Thunderjack.CardValues.EIGHT = "8";
Thunderjack.CardValues.NINE = "9";
Thunderjack.CardValues.TEN = "10";
Thunderjack.CardValues.JACK = "j";
Thunderjack.CardValues.QUEEN = "q";
Thunderjack.CardValues.KING = "k";
Thunderjack.CardValues.ACE = "a";
Thunderjack.CardValues.ALL = [
  Thunderjack.CardValues.TWO,
  Thunderjack.CardValues.THREE,
  Thunderjack.CardValues.FOUR,
  Thunderjack.CardValues.FIVE,
  Thunderjack.CardValues.SIX,
  Thunderjack.CardValues.SEVEN,
  Thunderjack.CardValues.EIGHT,
  Thunderjack.CardValues.NINE,
  Thunderjack.CardValues.TEN,
  Thunderjack.CardValues.JACK,
  Thunderjack.CardValues.QUEEN,
  Thunderjack.CardValues.KING,
  Thunderjack.CardValues.ACE
];
Thunderjack.CardPoints = { };
Thunderjack.CardPoints[Thunderjack.CardValues.TWO] = 2;
Thunderjack.CardPoints[Thunderjack.CardValues.THREE] = 3;
Thunderjack.CardPoints[Thunderjack.CardValues.FOUR] = 4;
Thunderjack.CardPoints[Thunderjack.CardValues.FIVE] = 5;
Thunderjack.CardPoints[Thunderjack.CardValues.SIX] = 6;
Thunderjack.CardPoints[Thunderjack.CardValues.SEVEN] = 7;
Thunderjack.CardPoints[Thunderjack.CardValues.EIGHT] = 8;
Thunderjack.CardPoints[Thunderjack.CardValues.NINE] = 9;
Thunderjack.CardPoints[Thunderjack.CardValues.TEN] = 10;
Thunderjack.CardPoints[Thunderjack.CardValues.JACK] = 10;
Thunderjack.CardPoints[Thunderjack.CardValues.QUEEN] = 10;
Thunderjack.CardPoints[Thunderjack.CardValues.KING] = 10;
Thunderjack.CardPoints[Thunderjack.CardValues.ACE] = 1;

You may also notice that Ace only stores the value of 1 not 11. We’ll be using the 11 at another time, when we calculate how many points are in a player’s hand than contains an Ace. We won’t be using the CardPoints in this post, but we’ll need it soon.

Card Data Structure

Now that we have suites and values defined, we want a card data object that can store these. In Blackjack, more than one deck is commonly used, and Thunderjack! is no exception. This introduces the problem of having multiple cards with the same suit and value, and we want to be able to uniquely identify every card in each deck.

This is where the card ID that I mentioned earlier comes into play. We’ll be storing that in our data object, as well as the zero-based array index that identifies which deck the card represented by the card data object comes from.

This is what our card data object class looks like:

Thunderjack.CardData = function(s_cardValue, s_cardSuit, i_deckIndex) {
  this.value = s_cardValue;
  this.suit = s_cardSuit;
  this.deckIndex = i_deckIndex;
  this.id = this.value + "_" + this.suit + "_" + this.deckIndex;
};

As you can see, we’re forming our card ID as a string using the following format:
[card value] _ [card suit] _ [deck index]

For example, the seven of clubs from the third deck would look like this:
"7_c_2"

Deck Structure

Every card is represented by a card data object, and a deck is simply an array of 52 of these card data objects. We have a simple class for “dealing” and shuffling cards, as well as a few other utility functions.

Starting out, the deck data class looks like this:

//===========================================================================================
//"public"
//=========================================================================================== 

//===========================================================================================
//ctor
//===========================================================================================
Thunderjack.DeckData = function(i_numDecks) {
  //data: Thunderjack.CardData
  this._cardsData = [];
  this._init(i_numDecks);
};

//-------------------------------------------------------------------------------------------
//getLength
//-------------------------------------------------------------------------------------------
Thunderjack.DeckData.prototype.getLength = function() {
  return(this._cardsData.length);
};

//-------------------------------------------------------------------------------------------
//popCardData
//-------------------------------------------------------------------------------------------
Thunderjack.DeckData.prototype.popCardData = function() {
  return(this.getLength() > 0 ? this._cardsData.pop() : null);
};

//-------------------------------------------------------------------------------------------
//shuffle
//-------------------------------------------------------------------------------------------
Thunderjack.DeckData.prototype.shuffle = function() {
  Phaser.ArrayUtils.shuffle(this._cardsData);
};

//===========================================================================================
//"private"
//===========================================================================================

//-------------------------------------------------------------------------------------------
//_init
//-------------------------------------------------------------------------------------------
Thunderjack.DeckData.prototype._init = function(i_numDecks) {
  var i_decksCount;
  if (typeof i_numDecks === "number") {
    i_decksCount = i_numDecks;
  } else {
    return;
  }

  for (var i_deckIndex = 0; i_deckIndex < i_decksCount; i_deckIndex++) {
    for (var i_valueIndex = 0; i_valueIndex < Thunderjack.CardValues.ALL.length; i_valueIndex++) {
      var s_value = Thunderjack.CardValues.ALL[i_valueIndex];
      for (var i_suitIndex = 0; i_suitIndex < Thunderjack.CardSuits.ALL.length; i_suitIndex++) {
        var s_suit = Thunderjack.CardSuits.ALL[i_suitIndex];
        this._cardsData.push(new Thunderjack.CardData(s_value, s_suit, i_deckIndex));
      }
    }
  }
};

You can see in the _init method is where we create the CardData objects for each card in every deck. This is where we use the ALL arrays from CardSuits and CardValues.

Note that, instead of using those ALL properties, we could have also iterated through them using a for…in statement, but the arrays are useful in other parts of the game, which we won’t get into right now.

The method we’re interested in when dealing cards is DeckData.popCardData(). This will remove the last CardData object from the _cards array and return it to us. This is the basic structure of the DeckData class. There are other methods in this class as well, and they’ll be added as we need them.

You’ll also notice a shuffle method. Phaser already provides for us an ArrayUtils class, and so happens to have a shuffle method that randomizes the order of items in an array. We don’t have to write one ourselves. Remember, coders are lazy. (:

Get off yo ass and write some code!
Get off yo ass and write some code!
Dealing Cards to Each Player

With the deck and cards set up, we can now begin to deal cards to the players and dealer.
Some psuedocode looks like this:

function dealCardsToPlayers() {
  while (numberOfCardsDealtToEachPlayer < 2) {
    for each player in the game {
      dealCardToPlayer (player);
    }
    
    dealCardToPlayer (dealer);
    numberOfCardsDealtToEachPlayer++;
  }
}

When a card is dealt in dealCardToPlayer, it looks something like this:

function dealCardToPlayer (playerData) {
  var cardData = deckData.popCardData();
  playerData.cardsData.push(cardData);
}
Controlling the Order in Which Cards are Drawn (To Cheat!)

Now, if we’re trying to test out certain combinations of cards in a player’s hand, such as a Blackjack on the opening hand, we can be waiting a long ass time before we get what we’re looking for. This is where che~~~ ahem, controlling the order of the cards comes in.

The plan is like this:

  • Setup an array of card ID strings that will specify the order in which we want the cards to be drawn.
  • Shuffle the deck (if necessary)
  • After the cards have been shuffled, set the order of the cards in the deck according to the array of card Ids in step one.

Let’s setup each of these steps.

1). Setup an array of card ID strings that will specify the order in which we want the cards to be drawn.

We can set the array up that will hold the card IDs we want to use in the following:

debugCardIds = [
  "4_h_0",  //first card that will be dealt to the player
  "a_d_0",  //first card that will be dealt to the dealer
  "5_c_0",  //second card that will be dealt to the player
  "10_s_0"  // second card that will be dealt to the dealer
];

Say we have only one player and the dealer. From the card dealing snipped above, all players are dealt a card, then the dealer. Then this process continues until each player and the dealer has two cards.

2). Shuffle the deck (if necessary)

If we’re just starting the game, we’ll want to shuffle the deck. We can do that using the shuffle method of the DeckData class mentioned earlier.

deckData.shuffle();

3). After the cards have been shuffled, set the order of the cards in the deck according to the array of card Ids in step one.

function aceUpSleeve() {
  var s_cardId = _debugCardIds.shift();
  var i_swapIndex = _deckData.getLength();
  while (s_cardId) {
    _deckData.swap(s_cardId, --i_swapIndex);
    s_cardId = _debugCardIds.shift();
  }
}

What we’re doing here is using the shift method of JavaScript’s Array class to retrieve and remove the first card ID.

When dealing cards, remember that we retrieve and remove the last CardData object from the deck. So when setting up the deck in the order we want, we swap out the last card data object in the deck with debug card ID. Then, we work our way backwards, towards the beginning of the deck, until our debugCardsId array is empty.

The i_swapIndex variable represents the array index within the DeckData where we will place the CardData represented by our debug card ID. This is where a new method for DeckData, swap(), comes in:

Thunderjack.DeckData.prototype.swap = function(s_cardId, i_indexToSwapAt) {
  if (i_indexToSwapAt < 0 || i_indexToSwapAt >= this.getLength()) {
    return;
  }

  var i_indexOf = this.indexOfId(s_cardId);
  if (i_indexOf > -1) {
    var cardData = this._cardsData[i_indexToSwapAt];
    this._cardsData[i_indexToSwapAt] = this._cardsData[i_indexOf];
    this._cardsData[i_indexOf] = cardData;
  }
};

What this method does is first, locate the index within the _cardsData array of the CardData represented by our card debug ID, Then, it will swap the elements of the _cardsData array; the card data found at our requested swap index with the card data at the located index.

So now, our dealCardsToPlayers() method looks like this:

function dealCardsToPlayers() {
  //do this first – assume cards have been shuffled already in some other function
  aceUpSleeve();

  while (numberOfCardsDealtToEachPlayer < 2) {
    for each player in the game {
      dealCardToPlayer (player);
    }
    
    dealCardToPlayer (dealer);
    numberOfCardsDealtToEachPlayer++;
  }
}

Also, if we don’t want to control the order of dealing cards, we can set debugCardIds to an empty array.

The problem with this is, we have to keep editing the code when we want to control the order. Later on in development, I’ll build a user interface that allows us to manipulate the debug card Ids. Adding a debug/diagnostic interface like this is not something you’d put in a release version of your game, but they are invaluable when debugging, testing, and demoing your game.

Yes, they are a little bit of a detour, but it’s worth it. If you’re testing the game with someone else, they’ll be able to easily control the order of the deck without having to change the code. For my clients, they wouldn’t want to anyway – they just want something that’s easy for them to use without having to poke around in the code. That’s my job. πŸ˜‰

Finally, you can watch this video of the *cough*cheat’n’*cough* in action. You may want to view the video at full-screen so you can see the edits I’m making in code. Also, after the opening cards are drawn each time, feel free to pause the video to check that the order of the cards drawn does indeed match with the _debugCardIds array (they do):

So that’s it for now. This was a long one, and thanks for reading all the way to the end. I can’t wait until this game is finished for you all to play!

– C. out.

Thunderjack! HTML5 Game Update: Card Dealing

Hey guys.

Welcome back to Gameplay Coder. Quite a bit of progress has been made on the HTML5 version of Thunderjack! using Phaser and the Phaser Editor. I’m getting the hang of basic usage of the editor. And it’s great to have a visual editor at your disposal – so you don’t have to place sprites using hard-coded x/y coordinates via trial and error, or reply on some external graphics tool then have to copy/paste those coordinates.

Updates to this game include:

  1. Being able to place bets.
  2. Dealing the cards to the players and dealer.
  3. Updating points for the players.

Hit functionality is currently in the works, as is determining when a player or dealer has a blackjack, and when a player has a Thunderjack.

Use of Fonts

One issue I encountered is with using fonts. In the browser, they don’t appear exactly as the do in the editor. Some features such as stroke, aren’t rendered. So I’ll likely end up using Phaser bitmap fonts instead of fonts, which, on my machine, is a small set of Windows fonts that the editor has access to. Furthermore, there is no guarantee that the fonts used in the project will be installed on the machine of someone playing the game.

There is an online Flash tool (at the time of this post), called Littera. This can be used to create bitmap fonts from fonts installed on your machine. What’s also cool about it is, Phaser supports the format, as shown in this example.

Prototypal Inheritance

While I’m no stranger to OOP (object-oriented programming) and classical inheritance in languages like ActionScript 3, C# or Java, I’m fairly new as to prototypal inheritance in JavaScript, so I’m giving it a try in this project. Hell yeah! πŸ’ͺ🏾

I’m using prototypal inheritance with the data that stores the player hands and the dealer hand. There is a ‘base class’ called HandData, whose function constructor looks like this:

HandData class
HandData = function(s_handId) {
  this.cards = [];
  this.handId = s_handId;
  this.score = 0;
  this.hasBlackjack = false;
  this.isBust = false;
};

This is basic data that both types of hands, player and dealer, will contain.

However, player hands will need to store additional data, such as the bet amount, and can perform other moves such as split, double, and surrender. Players can also win via Blitz (6-card Charlie in this game). The dealer does not use any of these functions, and thus doesn’t need access to them.

The player hand data, PlayerHandData, will inherit from the HandData, and looks like this:

PlayerHandData class
PlayerHandData = function(s_handId) {
  HandData.call(this, s_handId);
  this.betValue = 0;
  this.hasThunderjack = false;
  this.isPush = false;
  this.hasBlitz = false;
  this.hasDoubled = false;
  this.hasSurrendered = false;
};

That call() function is like calling the constructor of the parent class. In this case, it sends the parameters that were sent when it was created onto its parent constructor. Also, the this keyword is supplied first, so that the context in which the parent constructor function is being called is set to the inheriting function constructor.

However, PlayerHandData still needs to have its constructor inherit the methods of the parent prototype. This creates a new object based on the parent prototype:

PlayerHandData.prototype = Object.create(HandData.prototype);

And finally, the constructor of our new PlayerHandData prototype needs to point to PlayerHandData instead of HandData. That can be fixed with this line:
PlayerHandData.prototype.constructor = PlayerHandData;

Now when we create data for each the dealer hand, we can write:
var dealerHand = new HandData(s_dealerId);

And for each of the six player hands, we can write something like:
var somePlayerHand = new PlayerHandData(s_playerId);

The player hand will have all the properties of the dealer hand, plus the extras that are required to support the additional game play available to the players.

So when inheriting one “class” from another (I put “class” in quotes because, JavaScript doesn’t have classes. They can be simulated using prototypes.), it’s basically three steps:

1). Within the inheriting function constructor, call the parent constructor function using the call(). Be sure to set this as the first parameter, succeeded by any function constructor parameters.
2). Create a copy of the parent function’s prototype, and assign it to the inheriting constructor function’s prototype.
3). Assign the constructor of the inheriting prototype to the inheriting constructor function itself.

This can be a little confusing, but typing this out can help reinforce learning (though writing it out is better, studies have shown, but I digress (:

But doing is the best way to learn, in my opinion, of course. So, I’ll definitely take more opportunities to apply this so it becomes easier.

Whew!

Well!… Lemme get a sippa water…
* Ahhhhh! *

Moving on, here is a short video of what I have so far. It’s what was mentioned in the updates mentioned near the start of this article.

It’s not much, but under the hood, a lot has been accomplished, and with most of the lower-level code now out of the way, I can start getting into the fun stuff!

Thanks for stick with me guys, and stay tuned for more.

– C. out.

Thunderjack! (HTML5 Version) and Phaser Editor

Hey there.

In addition to the Android version of Thunderjack! that I’m working on, I’ve also started a browser version, using HTML5. Flash is reaching its end-of-life, but browser games are still a thing. So, HTML5 is the next step for me, and I’ve been improving my skills to produce web-based games that do not use a plugin.

I found Phaser, an HTML5 game development library, and started learning it. This library is incredible! I’ve used it to create my first HTML5 game, Brickout.

Sure, I’m not the biggest fan of JavaScript (I took this course to get some quick JavaScript training), but that’s a relatively small hurdle to overcome – especially if you were like me – ego and attitude was keeping you from learning JavaScript.

I also wanted some sort of WYSIWYG IDE for designing the visual aspects of HTML5 games, similar to Adobe Flash/Animate. Eventually, I found the Phaser Editor.

Giving this software a try, I’m really liking most of what I’m finding with this editor. When you add visual objects the canvas scene, Phaser Editor generates the JavaScript code that creates those Phaser objects. No more having to hard-code things like the x/y positions of objects and line up your objects using trial and error.

You can even create custom, re-useable objects (known as prefabs in Phaser Editor), which can really facilitate the building process.

While the learning curve isn’t too steep, though I would have liked to see a tutorial that creates a game using the editor from scratch, rather than a pre-made game.

This editor also comes with its own JavaScript coding editor, with many of the standard features you’d expect from an editor, such as auto-completions, listing of JavaScript and Phaser API methods, code refactoring, and more.

Phaser Editor comes in two flavors: free and paid. The free version has some limitations, but not enough to stop you unless you’re building a massive game.

Overall, this is a very handy tool, and I’m excited about integrating it into my workflow as I add HTML5 game coding to my list of services.

More updates on Thunderjack! will be in the coming weeks. Stay tuned.

Talk to you soon.
– C. out.

Adding Text From a Tiled Map to Phaser

Hey.

When I’m building a game, I prefer to have a visual editor when placing as many objects as possible. This includes user interface such as HUDs and text.

So, I’m adding text to my Tiled map to have Phaser’s Tilemap object load it for display. I’m using Phaser.BitmapText, but Tiled uses fonts installed on your system, so showing the text won’t display as easily as, say, tileset tiles.

To add text to a Tiled layer, you need to first create an Object layer, then you can add the text object to it. The text properties of a text object will look something like this:

When the map is exported to a format that Phaser can use – I’m using JSON – the format of the object looks something like:

{
  "height":32,
  "id":18,
  "name":"txtScoreLabel",
  "rotation":0,
  "text": {
    "color":"#ffffff",
    "fontfamily":"Press Start 2P",
    "pixelsize":24,
    "text":"Score:",
    "wrap":true
  },
  "type":"press2p24",
  "visible":true,
  "width":160,
  "x":32,
  "y":32
}

On the Phaser side, I’ve loaded the JSON map with this:

loader.tilemap('brickout', 'media/graphics/maps/brickout.json', null, Phaser.Tilemap.TILED_JSON);

And created a bitmap font using Littera. It’s loaded into Phaser with this:

loader.bitmapFont('press2p24', 'press2p24.png', 'press2p24.fnt');

Looking back at the JSON format above, you’ll see that the type property has the value of press2p24. The type property doesn’t appear to be used by Tiled for anything. On the Tiled’s JSON Map Format page, it states, “String assigned to type field in editor”. So I decided to use the type property as the key that Phaser uses when defining BitmapText objects.

Also, in the JSON format, some other key properties include x, y, text.pixelsize, and text.text.

I first tried to get the text data by creating a Phaser.TilemapLayer from the layer (named ‘text’) like this:

map.createLayer('text');

This is a layer I used exclusively for text objects in Tiled. So, I’d then iterate through all the elements in the ‘objects’ property of the Phaser.TilemapLayer and create my BitmapText objects like this:

game.add.bitmapText(object[i].x, object[i].y, object[i].type, object[i].text.text, object[i].text.pixelSize);

However, the text was not showing up in the game. It seems all the properties were loaded into Phaser except for the text sub-object. The object[i].text sub-object wasn’t being loaded into Phaser. You can see in this screenshot below, when I inspect the Tilemap object look at the objects property, the first element (0) of the text layer does not have a text sub-object in it.

So, Phaser was not loading in that data. When I inspected the code on GitHub (I tracked it down to the src/tilemap/TilemapParser.js.), the text object was eventually being loaded as a Tiled rectangle object, and there is no code for loading the text sub-object.

But, the raw JSON data that Phaser loads does include all the information needed, including the text sub-object, as shown:

I was able to obtain the raw data with this code:

var rawMapData = game.cache.getTilemapData(tileMap.key);

Then I could get the text objects layer from with rawMapData.data.layers. And from there, the text sub-object to retrieve the text and pixelSize properties.

It seems like quite a bit of work, but not really. Going through the learning process was longer than the solution, actually. (: I just needed a way to specify the data in a visual editor and have it supported in code. I’d prefer not hard-code positions and other visual aspects that should be defined in a visual editor.

And If you’re wondering the font is Press Start 2P. (:

– C. out.

Tools I Use

Hey.

I want to fill you in on the tools and resources I use when I’m coding games. This is not an exhaustive list.

Coding Editors/IDEs

  • FlashDevelop
    My current go-to IDE for all things ActionScript 3. I also like how it can download and configure updates of the Adobe AIR and Flex SDKs.
  • Adobe Flash CS/Animate
    I don’t use this for coding, but for managing assets. While I’m not an artist, graphic designer, game designer, I find its visual editor very easy to use and mostly intuitive.
  • Brackets
    Been using this free editor for a little while. I use it for JavaScript when coding HTML5 games using Phaser.

Programming Languages/Engines/SDKs/Runtimes

  • Adobe AIR
    Despite Flash being retired soon, Adobe AIR is still at large. AIR is not the same thing as Flash – they both happen to use ActionScript 3 and many of the same APIs (with AIR having access to many more of them, partly due to it being able to target desktops). Plus, Adobe AIR can be used to target mobile (Android and IOs) from the same code with minor changes.
  • Adobe Flash
    Yes, I know Flash will no longer be updated or distributed by end of 2020, but until then, hey, it’s still being used.
  • Phaser
    I only care enough about HTML5 and JavaScript because of Phaser and it’s capabilities for game making. I don’t give a damn about web development.
  • Unity
    While I haven’t used Unity to make any games yet, I found a good course on Udemy for it.

Graphics

  • Paint.NET
    I use this for editing of bitmap files. Another simple and intuitive tool to use.

Project Management

  • Trello
    A free online tool for keeping track of my progress, especially when working on larger projects. I use it in a Kanban style, and I’ll have three major categories for tasks: Backlog; Work In Progress; and Ready for Testing. Once the client has tested the latest updates and all’s well, I will archive all the tasks in the Ready for Testing category, and start anew.

Source Control

  • GitHub
    Using Git as the version control system.
    You can find me here, hint, hint.
  • Bitbucket
    I also use Git here, but have tried Mercurial.

Audio

  • Wavosaur
    Simple tool for editing audio files.
  • Audacity
    Not as easy to use as Wavosaur, but it has more functions, particularly in terms of effects you can apple to sounds.

Music

 

So, that’s pretty much the jist of what I use. If you have any questions about anything here, let me know in the comments!

– C. out.