if (!String.prototype.startsWith) {
String.prototype.startsWith = function(search, pos) {
return this.substr(!pos || pos < 0 ? 0 : +pos, search.length) === search;
};
}
define([
"dojo",
"dojo/_base/declare",
"ebg/core/gamegui",
"ebg/counter",
"ebg/stock"
],
function (dojo, declare) {
return declare("bgagame.pi", ebg.core.gamegui, {
constructor: function(){
this.CARD_WIDTH = 90;
this.CARD_HEIGHT = 138;
this.TILE_HEIGHT = 64;
this.TILE_WIDTH = 64;
this.tileDivsByName = {};
},
setup: function(gamedatas)
{
var CARD_ITEM_MARGIN = 3;
var CARD_ITEMS_PER_ROW = 9;
var TILE_ITEMS_PER_ROW = 7;
for (var player_id in gamedatas.players) {
var player = gamedatas.players[player_id];
if (gamedatas.players[player_id].solved_in_round != null) {
this.disablePlayerPanel(player_id);
}
dojo.place(
this.format_block('jstpl_player_board', player),
$('player_board_' + player_id));
if (player.is_startplayer == 1) {
dojo.addClass('sp_marker_' + player_id, 'visible');
}
}
this.addTooltipToClass('sp_marker', _("Start player of the current mini-game"), '');
this.playerHand = new ebg.stock();
this.playerHand.create(this, $('myhand'), this.CARD_WIDTH, this.CARD_HEIGHT);
this.playerHand.setSelectionMode(0); this.playerHand.image_items_per_row = CARD_ITEMS_PER_ROW;
this.playerHand.item_margin = CARD_ITEM_MARGIN;
this.playerHand.onItemCreate = dojo.hitch(this, 'setupCaseCard');
this.evidenceDisplay = new ebg.stock();
this.evidenceDisplay.setSelectionMode(1); this.evidenceDisplay.create(this, $('evidence'), this.CARD_WIDTH, this.CARD_HEIGHT);
this.evidenceDisplay.image_items_per_row = CARD_ITEMS_PER_ROW;
this.evidenceDisplay.item_margin = CARD_ITEM_MARGIN;
this.evidenceDisplay.onItemCreate = dojo.hitch(this, 'setupEvidenceDisplayCard');
this.evidenceDiscard = new ebg.stock();
this.evidenceDiscard.setSelectionMode(0); this.evidenceDiscard.create(this, $('evidence_discard'), this.CARD_WIDTH, this.CARD_HEIGHT);
this.evidenceDiscard.image_items_per_row = CARD_ITEMS_PER_ROW;
this.evidenceDiscard.setOverlap(.01, 0); this.evidenceDiscard.item_margin = 0;
dojo.connect(this.evidenceDiscard, 'onItemCreate', this, 'setupEvidenceDiscardCard');
dojo.connect(this.evidenceDiscard, 'onItemCreate', this, 'updateDiscardTooltip');
this.playerDisplays = {};
for (var player_id in gamedatas.players) {
var pdstock = new ebg.stock();
this.playerDisplays[player_id] = pdstock;
var node_id = (player_id == this.player_id) ? 'myevidence' : 'playerdisplay_' + player_id;
pdstock.setSelectionMode(0); pdstock.create(this, $(node_id), this.CARD_WIDTH, this.CARD_HEIGHT);
pdstock.setOverlap(50, 0);
pdstock.image_items_per_row = CARD_ITEMS_PER_ROW;
pdstock.item_margin = CARD_ITEM_MARGIN;
pdstock.onItemCreate = dojo.hitch(this, 'setupEvidenceCard');
}
this.playerDisplay = this.playerDisplays[this.player_id];
for (var i = 1; i <= 36; i++) {
var pos_in_img = i - 1;
var cardinfo = this.gamedatas.cardinfos[i];
var weight = {suspect: 100, location: 200, crime:300}[cardinfo.casetype] + cardinfo.name.charCodeAt();
this.playerHand.addItemType(i + 36, weight, g_gamethemeurl + 'img/casecards.jpg', pos_in_img);
this.evidenceDisplay.addItemType(i, weight, g_gamethemeurl + 'img/evidencecards.jpg', pos_in_img);
this.evidenceDiscard.addItemType(i, 0, g_gamethemeurl + 'img/evidencecards.jpg', pos_in_img);
for (var player_id in gamedatas.players) {
this.playerDisplays[player_id].addItemType(i, weight, g_gamethemeurl + 'img/evidencecards.jpg', pos_in_img);
}
}
this.tiles = new ebg.stock();
this.tiles.setSelectionMode(0); this.tiles.setSelectionAppearance('class');
this.tiles.create(this, $('tilestock'), this.TILE_WIDTH, this.TILE_HEIGHT);
this.tiles.setOverlap(0.01, 0); this.tiles.image_items_per_row = TILE_ITEMS_PER_ROW;
this.tiles.item_margin = 0;
this.tiles.onItemCreate = dojo.hitch(this, 'setupTile');
for (var i = 1; i <= 42; i++) {
var pos_in_img = i - 1; this.tiles.addItemType(i, 0, g_gamethemeurl + 'img/tiles_64_2x.jpg', pos_in_img);
}
this.placePlayerHand(this.gamedatas.hand);
this.placeEvidenceCards(
this.gamedatas.evidence_display,
this.gamedatas.evidence_discard,
this.gamedatas.player_display_cards
);
this.placeTiles(this.gamedatas.tiles);
var tokens = this.gamedatas.tokens;
tokens.sort(function (a, b) { return b.key.startsWith('pi_') - a.key.startsWith('pi_') });
this.placeTokens(tokens);
this.updateCounters(this.gamedatas.counters);
dojo.connect(this.evidenceDisplay, 'onChangeSelection', this, 'onEvidenceDisplaySelectionChanged');
this.addTooltip('casecards_help', _("The case cards of your left neighbour. TOP SECRET! Only you can see these."), '');
this.addTooltip('myevidence_help', _("These cards remind you which evidence is unrelated to your case. They are visible to everybody."), '');
this.setupNotifications();
},
setupCardTileHighlightConnection: function(card_div, name) {
dojo.connect(card_div, 'mouseover', this, function (e) {
dojo.query('.locslot div.stockitem').removeClass('highlight');
dojo.addClass(this.tileDivsByName[name], 'highlight');
});
dojo.connect(card_div, 'mouseout', this, function (e) {
dojo.removeClass(this.tileDivsByName[name], 'highlight');
});
},
setupCaseCard: function(card_div, card_type_id, card_id) {
var name = this.gamedatas.cardinfos[card_type_id].name;
this.setupCardTileHighlightConnection(card_div, name);
dojo.place('<div class="cardname casecard">' + _(name) + '</div>', card_div.id);
this.addTooltipHtml(
card_div.id,
this.format_block('jstpl_casecard_tooltip', {name: _(name).toUpperCase()})
);
},
setupEvidenceCard: function(card_div, card_type_id, card_id) {
var name = this.gamedatas.cardinfos[card_type_id].name;
this.setupCardTileHighlightConnection(card_div, name);
dojo.place('<div class="cardname">' + _(name) + '</div>', card_div.id);
this.addTooltipHtml(
card_div.id,
this.format_block('jstpl_card_tooltip', {name: _(name).toUpperCase()})
);
},
setupEvidenceDiscardCard: function(card_div, card_type_id, card_id) {
dojo.place('<div class="cardname">' + _(this.gamedatas.cardinfos[card_type_id].name) + '</div>', card_div.id);
},
setupEvidenceDisplayCard: function(card_div, card_type_id, card_id) {
var name = this.gamedatas.cardinfos[card_type_id].name;
this.setupCardTileHighlightConnection(card_div, name);
dojo.place('<div class="cardname">' + _(name) + '</div>', card_div.id);
this.addTooltipHtml(
card_div.id,
this.format_block('jstpl_displaycard_tooltip', {
name: _(name).toUpperCase(),
action: _('Follow this evidence…')
})
);
},
setupTile: function (card_div, card_type_id, card_id) {
if (card_type_id <= 6) {
dojo.addClass(card_id, "no_x_tile");
}
if (card_type_id >= 29) {
dojo.addClass(card_id, "fake_tile");
dojo.setStyle(card_id, "background", "none");
}
var name = this.gamedatas.tileinfos[card_type_id].name;
if (!name.startsWith('NO ')) {
dojo.place('<div class="tilename">' + _(name) + '</div>', card_div.id);
}
this.addTooltipHtml(
card_div.id,
this.format_block('jstpl_card_tooltip', {name: _(name).toUpperCase()})
);
this.tileDivsByName[name] = card_div;
},
updateDiscardTooltip: function () {
var html = '<b>' + _("Discard pile") + '</b><hr>';
html += this.evidenceDiscard.getAllItems().map(dojo.hitch(this, function(card) {
return _(this.gamedatas.cardinfos[card.type].name);
}))
.sort()
.join('<br>');
this.removeTooltip('evidence_discard');
this.addTooltipHtml('evidence_discard', html);
},
onEnteringState: function(stateName, args)
{
this.stateEventHandles = this.stateEventHandles || {};
var ehdls = this.stateEventHandles[stateName] = [];
switch (stateName)
{
case 'startMinigame':
args = args.args;
this.enableAllPlayerPanels();
dojo.query(".sp_marker").removeClass('visible');
for (var player_id in args.players) {
var player = args.players[player_id];
dojo.toggleClass('sp_marker_' + player_id, 'visible', player.is_startplayer == 1);
}
this.updateCounters(args.counters);
this.placeEvidenceCards(args.evidence_display, [], []);
this.placeTiles(args.tiles);
this.placeTokens(args.tokens, null, 100);
this.placePlayerHand(args._private.hand);
break;
case 'client_playerPicksSolution':
this.tiles.setSelectionMode(2);
ehdls.push(dojo.connect(this.tiles, 'onChangeSelection', this, 'onCaseSelectionChanged'));
dojo.query('.locslot .stockitem').addClass('active_slot');
this.hideCardDisplay();
break;
case 'client_playerPlacesInvestigator':
for (var i=1; i<=14; i++) {
var node_id = 'agentarea_' + i;
var color = this.gamedatas.players[this.player_id].colorname;
if (dojo.query('.investigator_' + color, node_id).length) {
continue;
}
dojo.addClass(node_id, 'active_slot');
ehdls.push(dojo.connect($(node_id), 'onclick', this, 'onAgentAreaClicked'));
this.addTooltip(
node_id,
'',
dojo.string.substitute(_("Send investigator to ${location_name}."), {
location_name: this.gamedatas.locationinfos[i].name
})
);
}
this.hideCardDisplay();
break;
}
},
onLeavingState: function(stateName)
{
switch (stateName)
{
case 'client_playerPicksSolution':
this.tiles.unselectAll();
this.tiles.setSelectionMode(0);
this.showCardDisplay();
break;
case 'client_playerPlacesInvestigator':
this.showCardDisplay();
for (var i=1; i<=14; i++) { this.removeTooltip('agentarea_' + i) }
break;
}
dojo.query('.active_slot').removeClass('active_slot');
(this.stateEventHandles[stateName] || []).forEach(function (handle, idx) {
dojo.disconnect(handle);
});;
},
onUpdateActionButtons: function(stateName, args)
{
if (this.isCurrentPlayerActive()) {
switch (stateName) {
case "playerTurn":
if (args.remainingInvestigators != 0) {
this.addActionButton(
'btn_place_investigator',
dojo.string.substitute(
_('Place an investigator (${nbr} left)'),
{nbr: args.remainingInvestigators}),
'onPlaceInvestigatorClicked');
}
this.addActionButton(
'btn_solve_case',
_('Solve case'),
'onSolveCaseClicked');
break;
case "client_playerPicksSolution":
if (this.on_client_state && !$('button_cancel')) {
this.addActionButton(
'button_confirm',
_('Confirm'),
'onConfirmCaseSelection');
this.addActionButton(
'button_cancel',
_('Cancel'),
dojo.hitch(this, function() {this.restoreServerGameState();}),
null, null, 'gray');
}
break;
case "client_playerPlacesInvestigator":
if (this.on_client_state && !$('button_cancel')) {
this.addActionButton(
'button_cancel',
_('Cancel'),
dojo.hitch(this, function() {this.restoreServerGameState();}),
null, null, 'gray');
}
break;
}
}
},
checkActivePlayer: function() {
if (!this.isCurrentPlayerActive()) {
this.showMessage(__("lang_mainsite", "This is not your turn"), "error");
return false;
}
return true;
},
isActiveSlot: function(id) {
if (!dojo.hasClass(id, 'active_slot')) { return false; }
return true;
},
checkActiveSlot: function(id) {
if (!this.isActiveSlot(id)) {
this.showMoveUnauthorized();
return false;
}
return true;
},
checkActivePlayerAndSlot: function(id) {
if (!this.checkActivePlayer()) { return false; }
if (!this.checkActiveSlot(id)) { return false; }
return true;
},
hideCardDisplay: function () {
dojo.fx.wipeOut({node: $('carddisplay')}).play();
},
showCardDisplay: function () {
dojo.fx.wipeIn({node: $('carddisplay')}).play();
this.evidenceDisplay.resetItemsPosition();
this.evidenceDiscard.resetItemsPosition();
},
placeEvidenceCards: function(display, discard, player_display_cards) {
this.evidenceDisplay.removeAll(); for (i in display) {
var card = display[i];
this.evidenceDisplay.addToStockWithId(card.type_arg, card.id);
}
this.evidenceDiscard.removeAll();
for (i in discard) {
var card = discard[i];
this.evidenceDiscard.addToStockWithId(card.type_arg, card.id);
}
for (var player_id in this.gamedatas.players) {
this.playerDisplays[player_id].removeAll();
}
for (i in player_display_cards) {
var card = player_display_cards[i];
this.playerDisplays[card.location_arg].addToStockWithId(card.type_arg, card.id);
}
},
placePlayerHand: function (hand) {
this.playerHand.removeAll(); for (i in hand) {
var card = hand[i];
this.playerHand.addToStockWithId(card.type_arg, card.id);
}
},
placeTiles: function (tiles) {
this.tiles.removeAll(); for (i in tiles) {
var tile = tiles[i];
this.tiles.addToStockWithId(tile.type_arg, tile.id);
dojo.place($(this.tiles.getItemDivId(tile.id)), $('locslot_' + tile.location_arg));
}
},
placeToken: function (token, target_id, delay) {
var key = token.key;
var keyparts = key.split('_');
var ttype = keyparts[0]; var color = keyparts[1];
var duration = 500;
delay = (delay === undefined) ? 0 : delay;
target_id = target_id || token.location;
var placeOfftable = false;
if (target_id == 'box' || target_id == 'offtable') {
placeOfftable = true;
} else if (!$(target_id)) {
console.warn('Node \'' + target_id + '\' not found.' )
placeOfftable = true;
}
if (placeOfftable) {
if ($(key)) this.fadeOutAndDestroy(key, duration, delay);
return;
}
if ((ttype == 'cube' || ttype == 'disc') && target_id.startsWith('agentarea')) {
target_id = dojo.query('#' + target_id + ' .investigator_' + color)[0].id;
}
if (ttype == 'cube' && target_id.startsWith('locslot_')) {
target_id += '_cubes';
}
var $el = $(key);
if (!$el) { var html = this.format_block('jstpl_token_' + ttype, {key: key, color: color});
dojo.place(html, target_id);
} else { if ($el.parentNode.id == target_id) return;
var html = $el.outerHTML;
this.slideToObjectAndDestroy(key, target_id, duration, delay);
window.setTimeout(dojo.hitch(this, function () {
if ($(key) && $(key).parentNode.id == target_id) return;
dojo.place(html, target_id);
}), delay + duration);
}
},
placeTokens: function (tokens, target_id, delay, duration) {
duration = (duration === undefined) ? 1000 : duration; delay = (delay === undefined) ? 100 : delay;
delay = Math.min(delay, duration / tokens.length);
tokens.forEach(function (token, i) {
this.placeToken(token, target_id, i * delay);
}.bind(this));
},
validateCaseSelection: function(opts) {
var opts = opts || {};
var selectedTiles = this.tiles.getSelectedItems() || [];
tileinfos = this.gamedatas.tileinfos;
var hasSelected = {'crime': false, 'suspect': false, 'location': false};
for (var i in selectedTiles) {
var tile = selectedTiles[i];
var tile_type_arg = parseInt(tile.type, 10);
if (parseInt(tile_type_arg, 10) <= 6) {
this.showMessage(_("NO CRIME and NO SUSPECT tiles cannot be selected."), "error");
if (opts.clicked_id) this.tiles.unselectItem(opts.clicked_id);
return false;
}
if (hasSelected[tileinfos[tile_type_arg].tiletype]) {
this.showMessage(_("You must not select more than one of each type (crime/location/suspect)."), "error");
if (opts.clicked_id) this.tiles.unselectItem(opts.clicked_id);
return false;
}
hasSelected[tileinfos[tile_type_arg].tiletype] = true;
}
if (opts.strict && selectedTiles.length != 3) {
this.showMessage(_("You must select exactly 3 tiles."), "error");
return false;
}
return true;
},
onAgentAreaClicked: function(e) {
var id = e.currentTarget.id;
if (!this.checkActivePlayerAndSlot(id)) return;
var location_id = e.currentTarget.id.split('_')[1];
var action = 'placeInvestigator';
if (this.checkAction(action, true)) {
dojo.query('.agentarea.active_slot').removeClass('active_slot');
this.ajaxcall("/" + this.game_name + "/" + this.game_name + "/" + action + ".html", {
location_id: location_id,
lock : true
}, this, function(result) {
}, function(is_error) {
});
}
},
onEvidenceDisplaySelectionChanged: function() {
var items = this.evidenceDisplay.getSelectedItems();
if (items.length > 0) {
var action = 'selectEvidence';
if (this.checkAction(action, true)) {
var card_id = items[0].id;
this.ajaxcall("/" + this.game_name + "/" + this.game_name + "/" + action + ".html", {
id: card_id,
lock : true
}, this, function(result) {
}, function(is_error) {
});
this.evidenceDisplay.unselectAll();
} else {
this.evidenceDisplay.unselectAll();
}
}
},
onCaseSelectionChanged: function (control_name, id) {
this.validateCaseSelection({clicked_id: id, strict: false});
},
onConfirmCaseSelection: function () {
if (!this.validateCaseSelection({strict: true})) return;
var action = 'solveCase';
if (this.checkAction(action, true)) {
this.ajaxcall("/" + this.game_name + "/" + this.game_name + "/" + action + ".html", {
tile_ids : this.tiles.getSelectedItems().map(function (tile) { return tile.id }).join(';'),
lock : true
}, this, function(result) {
}, function(is_error) {
});
} else {
}
},
onPlaceInvestigatorClicked: function () {
this.setClientState(
"client_playerPlacesInvestigator", {
descriptionmyturn: _("Place investigator: ${you} must select a location…"),
});
},
onSolveCaseClicked: function () {
this.setClientState(
"client_playerPicksSolution", {
descriptionmyturn: _("Solve Case: ${you} must select the correct suspect, location, and crime…"),
});
},
setupNotifications: function()
{
this.notifqueue.setSynchronous('minigameEnds', 2000);
dojo.subscribe('cleanBoard', this, "notif_cleanBoard");
this.notifqueue.setSynchronous('cleanBoard', 1000);
dojo.subscribe('evidenceClose', this, "notif_evidenceClose");
this.notifqueue.setSynchronous('evidenceClose', 800);
dojo.subscribe('evidenceCorrect', this, "notif_evidenceCorrect");
this.notifqueue.setSynchronous('evidenceCorrect', 800);
dojo.subscribe('evidenceWrong', this, "notif_evidenceWrong");
this.notifqueue.setSynchronous('evidenceWrong', 500);
dojo.subscribe('evidenceExhausted', this, "notif_evidenceExhausted");
dojo.subscribe('evidenceReplenished', this, "notif_evidenceReplenished");
dojo.subscribe('newScores', this, "notif_newScores");
this.notifqueue.setSynchronous('playerFailed', 1000);
dojo.subscribe('placeToken', this, "notif_placeToken");
this.notifqueue.setSynchronous('placeToken', 500);
dojo.subscribe('placeTokens', this, "notif_placeTokens");
this.notifqueue.setSynchronous('placeTokens', 500);
dojo.subscribe('playerSolved', this, "notif_playerSolved");
this.notifqueue.setSynchronous('playerSolved', 1000);
this.notifqueue.setSynchronous('animate', 1000);
},
notif_cleanBoard: function(notif) {
this.playerHand.removeAll();
this.evidenceDisplay.removeAll();
this.evidenceDiscard.removeAll();
for (player_id in this.playerDisplays) {
this.playerDisplays[player_id].removeAll();
}
this.tiles.removeAll(); dojo.query('.locslot > div.stockitem').forEach(function (node) { dojo.destroy(node) }); window.setTimeout(dojo.hitch(this, function () {
this.placeTokens(notif.args.tokens, null, 10);
}), 300);
},
notif_evidenceExhausted: function(notif) {
this.evidenceDiscard.removeAll();
},
notif_evidenceReplenished: function(notif) {
this.evidenceDisplay.addToStockWithId(notif.args.card_type, notif.args.card_id);
if (notif.args.discard_is_empty) {
this.evidenceDiscard.removeAll();
}
},
notif_evidenceClose: function(notif) {
this.evidenceDiscard.addToStockWithId(notif.args.card_type, notif.args.card_id, 'evidence');
this.evidenceDisplay.removeFromStockById(notif.args.card_id);
},
notif_evidenceCorrect: function(notif) {
this.evidenceDiscard.addToStockWithId(notif.args.card_type, notif.args.card_id, 'evidence');
this.evidenceDisplay.removeFromStockById(notif.args.card_id);
},
notif_evidenceWrong: function(notif) {
this.playerDisplays[notif.args.player_id].addToStockWithId(notif.args.card_type, notif.args.card_id, 'evidence');
this.evidenceDisplay.removeFromStockById(notif.args.card_id);
},
notif_newScores: function(notif) {
for (var player_id in notif.args.scores) {
this.scoreCtrl[player_id].toValue(notif.args.scores[player_id]);
}
},
notif_placeToken: function(notif) {
this.placeToken(notif.args.token, notif.args.target_id);
if (notif.args.counters) this.updateCounters(notif.args.counters);
},
notif_placeTokens: function(notif) {
this.placeTokens(notif.args.tokens, notif.args.target_id);
if (notif.args.counters) this.updateCounters(notif.args.counters);
},
notif_playerSolved: function(notif) {
this.disablePlayerPanel(notif.args.player_id);
},
});
});