Code Samples
Back to Top Javascript (With jQuery and Data Templates)
/**
* offlineCards
*
* This object provides a namespace for all "offline game" functionality.
* The code below duplicates the game rules originally implemented in PHP
* in the playActions controller
*
* @author Simon Lang
*/
var offlineCards = {
deckSize: 52,
handSize: 26, // This number is updated dynamically anyway...
numberOfPlayers: 4,
currentPlayerIndex: 0,
currentRound: 0,
selectedStatisticIndex: null,
teamNames: ['Player 1', 'Player 2', 'Player 3', 'Player 4'],
hands: [],
scores: [],
restart: function() {
// Update round
this.currentRound = 0;
$('.round').html(1);
// Set number of teams
this.numberOfPlayers = parseInt($('#numberOfOpponents').val()) + 1; // From common settings form.
// Name Teams
var fields = $('#offlineSettingsForm form').find('input[name*=team]').serializeArray()
for (i in fields) {
var teamName = fields[i].value;
if (!teamName) {
var playerNumber = parseInt(i) + 1;
teamName = 'Player '+playerNumber;
}
this.teamNames[i] = teamName;
}
// Hide a bunch of panes
$('.showHands,.highScorePane,.currentCardPane').html('');
$('.gameMessage,#blackOverlay,#offlineSettingsForm,.gameOverPane').fadeOut();
// Shuffle, Deal, Update Scoreboard, Display first card
if ($('.previousOfflineCard').length == 0) {
// This is here so the "view cards" screen is in order. Messy code. Remove when done testing.
this.shuffle();
}
this.deal().updateScoreboard().popCard()
$('.gameplayArea').fadeIn();
},
getCurrentHand: function() {
return this.hands[this.currentPlayerIndex];
},
shuffle: function() {
cards = array_shuffle(cards);
return this;
},
deal: function() {
for (i = 0; i < this.numberOfPlayers; i++) {
this.hands[i] = [];
this.scores[i] = 0;
}
this.handSize = Math.floor(this.deckSize / this.numberOfPlayers);
var cardIndex = 0;
for (i = 0; i < this.handSize; i++) {
for (j = 0; j < this.numberOfPlayers; j++) {
var card = cards[cardIndex];
card.cardIndex = cardIndex;
this.hands[j].push(card);
cardIndex++;
}
}
$('.totalRounds').html(this.handSize);
return this;
},
popCard: function() {
var hand = this.getCurrentHand();
var card = hand[this.currentRound];
$('.currentCard').html($('#cardTemplate').render(card)).show();
$('#tooltip').remove(); // bugfix. Sometimes gets stuck because of no mouseout event presumably
},
loadCard: function(card_name, allowStatSelect) {
var card = cards[card_name];
card.allowStatSelect = allowStatSelect;
$(".currentCard").html($("#cardTemplate").render(card)).show();
if (this.selectedStatisticIndex && !allowStatSelect) {
$('.playStats tr[rel='+this.selectedStatisticIndex+']').addClass('selectedStatistic');
}
//$('#tooltip').remove(); // needed here?
},
nextRound: function(selectedStatisticIndex) {
this.selectedStatisticIndex = selectedStatisticIndex;
var playedCards = [];
var winningPlayerIndex = 0;
var winningCard = this.hands[winningPlayerIndex][this.currentRound];
// The first card is usually the winningCard. Except when it's an anti_trump
while (winningCard.is_anti_trump && winningPlayerIndex < this.numberOfPlayers) {
// They lose money
//var billionsLost = (winningCard.name == 'Resources') ? 100 : 50; // messes up syntax highlighter
this.scores[winningPlayerIndex] -= ONE_BILLION * billionsLost;
// Try the next card
winningPlayerIndex++;
winningCard = this.hands[winningPlayerIndex][this.currentRound];
}
// Check for Equal Draw
var statValues = [];
var trumpInHand = false;
var isDraw = false;
var highWins;
for (i = 0; i < this.numberOfPlayers; i++) {
var card = this.hands[i][this.currentRound];
if (card.is_trump) {
trumpInHand = true;
}
if (typeof(card.PlayStats[this.selectedStatisticIndex]) != "undefined") {
highWins = card.PlayStats[this.selectedStatisticIndex].high_wins;
statValues.push(card.PlayStats[this.selectedStatisticIndex].value);
}
}
statValues.sort(function(a,b){return a - b});
if (!highWins) {
statValues.reverse();
}
if (!trumpInHand) {
if (statValues[statValues.length - 1] == statValues[statValues.length - 2]) {
isDraw = true;
}
}
// Check for best card
for (i = 0; i < this.numberOfPlayers; i++) {
var card = this.hands[i][this.currentRound];
// For the .showHands area
var playedCard = {
teamName: this.teamNames[i],
cardName: card.name,
image: card.image,
statValue: "",
cardIndex: card.cardIndex
};
// Trumps win by default
if (card.is_trump) {
winningCard = card;
winningPlayerIndex = i;
playedCards.push(playedCard);
continue;
}
// Anti_Trumps lose by default
if (card.is_anti_trump) {
// They lose money
//var billionsLost = (card.name == 'Resources') ? 100 : 50; // messes up syntax highlighter
this.scores[i] -= ONE_BILLION * billionsLost;
playedCards.push(playedCard);
continue;
}
// Ensure we are looking at a valid Statistic
var cardStat = card.PlayStats[this.selectedStatisticIndex];
var winningCardStat = winningCard.PlayStats[this.selectedStatisticIndex];
if (typeof(cardStat) !== "undefined") {
playedCard.statValue = cardStat.short_display_value;
// Ensure we are looking at a valid Statistic
if (typeof(winningCardStat) !== "undefined") {
// High Wins
if (cardStat.high_wins && parseFloat(cardStat.value) > parseFloat(winningCardStat.value)) {
winningCard = card;
winningPlayerIndex = i;
}
// Low Wins
if (!cardStat.high_wins && parseFloat(cardStat.value) < parseFloat(winningCardStat.value)) {
winningCard = card;
winningPlayerIndex = i;
}
}
}
playedCards.push(playedCard);
}
if (isDraw) {
// Announce draw. No points awarded.
$(".gameMessage").html("Draw!").show();
}
else {
// Award points and announce winner
var billionsWon = this.numberOfPlayers;
if (winningCard.is_trump) {
//billionsWon = (winningCard.name == 'World Peace') ? 300 : 200; // messes up syntax highlighter
}
this.scores[winningPlayerIndex] += ONE_BILLION * billionsWon;
this.updateScoreboard();
$(".gameMessage").html(this.teamNames[winningPlayerIndex]+" wins with card: "+winningCard.name).show();
}
this.currentPlayerIndex = winningPlayerIndex;
// Display played cards
$('.showHands').html($('#showHandsTemplate').render(playedCards));
// Advance round
this.currentRound++;
$(".round").html(this.currentRound + 1);
// Check for Game Over
if (this.currentRound >= this.handSize) {
this.gameOver();
return;
}
// "It's your turn" prompt area
var winningTeamName = this.teamNames[winningPlayerIndex];
var nextCard = this.hands[winningPlayerIndex][this.currentRound];
$('.currentCardPane').html($('#currentCardPaneTemplate').render({
teamName: winningTeamName,
cardIndex: nextCard.cardIndex,
cardName: nextCard.name,
cardImage: nextCard.image
}));
// Display next card for the winning player
this.popCard();
$('.gameplayArea').show();
},
gameOver: function() {
var topPlayerIndex = 0;
for (i = 0; i < this.numberOfPlayers; i++) {
if (this.scores[i] > this.scores[topPlayerIndex]) {
topPlayerIndex = i;
}
}
$('.playerThatWon').html(this.teamNames[topPlayerIndex]);
$('.gameplayArea,.currentCard').fadeOut(function() {
$('.gameOverPane').fadeIn();
});
// Submit HighScores
var request = {};
for (i = 0; i < this.numberOfPlayers; i++) {
request[this.teamNames[i]] = this.scores[i];
}
$.post('/offline/enterHighScore', request, function(d) {
$('.highScorePane').load('/play/highScores');
});
},
updateScoreboard: function() {
var scores = [];
for (i = 0; i < this.numberOfPlayers; i++) {
var teamName = this.teamNames[i];
var score = '$'+number_format(this.scores[i]);
scores.push({
teamName: teamName,
score: score
});
}
$('#offlineScoreboard').html($('#scoreboardTemplate').render(scores));
return this;
},
// Debugging
logHands: function() {
console.log(this.hands);
},
// Debugging: Produces a clickable list of all cards
createDebugCardList: function() {
for (i in cards) {
$('.output').append('<a href="" class="offlineCardLink" rel="'+i+'">'+cards[i].name+'</a>');
}
}
};
$(function() {
// Start game
offlineCards.restart();
$('.offlineSelectStatistic').live('click', function(e) {
e.preventDefault();
offlineCards.nextRound($(this).attr('rel'));
});
$('.offlineCardLink').live('click', function(e) {
e.preventDefault();
offlineCards.loadCard($(this).attr('rel'), $(this).hasClass('allowStatSelect'));
});
$('.backToCurrentCardLink').live('click', function(e) {
e.preventDefault();
$('.currentOfflineCardLink').click();
});
$('.showOfflineSettings').live('click', function(e) {
e.preventDefault();
$('#blackOverlay,#offlineSettingsForm').fadeIn();
});
// Restart Game
$('#offlineSettingsForm form').submit(function() {
offlineCards.restart();
return false;
});
});
Back to Top PHP (Symfony)
<?php
/**
* multiplayer actions.
*
* @package wickets
* @subpackage multiplayer
* @author Simon Lang
*/
class multiplayerActions extends wicketsActions
{
public function preExecute()
{
$this->multiplayerSession = new MultiplayerSession();
$this->user = $this->getUser()->getDatabaseRow();
}
public function executeIndex(sfWebRequest $request)
{
$this->cleanup();
$this->modes = Doctrine::getTable('Mode')->getPlayable();
}
public function executeCreateGame(sfWebRequest $request)
{
$game = Doctrine_Core::getTable('Game')->create();
$game->name = $request->getParameter('name');
if (!$request->getParameter('name')) {
$game->name = $this->getUser()->getDatabaseRow()->getFirstName()."'s Game";
}
$game->mode_id = $request->getParameter('mode_id');
$game->collection_id = $request->getParameter('collection_id');
$game->share_deck = $request->getParameter('share_deck');
$game->last_request = time();
$game->save();
$this->redirect('multiplayer/joinGame?id='.$game->getId());
}
public function executeJoinGame(sfWebRequest $request)
{
$this->getGame($request->getParameter('id'), $request);
// Get player if they already exist
$this->player = Doctrine_Core::getTable('Player')->getByUserIdAndGameId($this->user->getId(), $this->game->getId());
// Render "Choose Team" form
if (!$this->game->getShareDeck() && !$request->getParameter('actual_collection_id') && !is_object($this->player)) {
$userCollection = Doctrine_Core::getTable('Collection')->getByUserId($this->user->getId())->getFirst();
$this->userCollectionId = null;
if (is_object($userCollection)) {
$this->userCollectionId = $userCollection->getId();
}
$this->canUseOwnDeck = $this->getUser()->getDatabaseRow()->canUseOwnDeck($this->game->getModeId());
return;
}
// If the game has already started, delete this player and kick them out
if ($this->game->getStartedAt()) {
if (is_object($this->player)) {
$this->redirect($this->generateUrl('multiplayer', $this->game));
}
else {
$this->getUser()->setFlash('flashMessageIcon', 'error');
$this->getUser()->setFlash('flashMessage', 'Sorry, that game is already in progress.');
$this->redirect('multiplayer/index');
}
}
// If the player does not exist yet, create them.
if (!is_object($this->player)) {
$this->player = Doctrine_Core::getTable('Player')->create();
$this->player->game_id = $this->game->getId();
$this->player->name = 'Player';
$this->player->session_id = session_id();
$this->player->last_request = time();
$this->player->user_id = $this->getUser()->getDatabaseRow()->getId();
$this->player->name = $this->getUser()->getDatabaseRow()->getFirstName();
$this->player->save();
}
// Player selects their deck
if (!$this->game->getShareDeck() && $request->getParameter('actual_collection_id')) {
$this->player->collection_id = $request->getParameter('actual_collection_id');
}
$this->player->is_ready = true; // TODO: remove this field. it is now redundant
$this->player->save();
$this->redirect($this->generateUrl('multiplayer', $this->game));
}
public function executeStartGame(sfWebRequest $request)
{
$this->getGame($request->getParameter('id'), $request);
$this->players = $this->game->getPlayers();
$randomPlayerIndex = rand(0, count($this->players) - 1);
$this->game->round = 1;
$this->game->started_at = time();
$this->game->current_player_id = $this->players->get($randomPlayerIndex)->getId();
$this->game->message = 'Game Started!';
$this->game->save();
$this->game->getCurrentPlayer()->updateLastRequest();
$this->shuffleAndDeal();
$this->redirect($this->generateUrl('multiplayer', $this->game));
}
public function executeViewGame(sfWebRequest $request)
{
$this->getGame($request->getParameter('id'), $request);
$_SESSION['game_id'] = $this->game->getId();
$this->players = $this->game->getPlayers();
$this->currentPlayer = $this->game->getCurrentPlayer();
$this->hostPlayer = $this->players->get(0);
$this->player = $this->game->getPlayerByUserId($this->user->getId());
// Check this user actually has a player in this game.
if (!is_object($this->player)) {
$this->getUser()->setFlash('flashMessageIcon', 'error');
$this->getUser()->setFlash('flashMessage', 'Sorry, that game is already in progress.');
$this->redirect('multiplayer/index');
}
}
/*
... SNIP ...
This is just a sample. It does not include the entire file.
*/
}