// ==UserScript== // @name onlinetichu.com card counter // @version 1.0.2 // @description automatically count cards at onlinetichu.com // @author kesslermaximilian // @match *://*.onlinetichu.com/Site/Game/Table/* // ==/UserScript== // disable auto fold functionality completely function injectCounter() { if (ot.AutoFold) { ot.$autoFold.click() ot.$autoFold.hide() } var myStyles = ` #playedHigh { position: absolute; top: 620px; width: 850px; height: 120px; } #playedLow { position: absolute; top: 750px; width: 850px; height: 120px; } .playedCardsLayout { position: absolute; height: 125px; } .playedCardsLayout .card { width: 80px; height: 120px; border-radius: 5px; float: left; margin-left: -50px; text-align: left; box-shadow: 2px 2px 5px #555; border: 1px solid #000000; } .playedCardsLayoutSmall { position: absolute; height: 62px; } .playedCardsLayoutSmall .card { width: 40px; height: 60px; border-radius: 3px; float: left; margin-left: -25px; text-align: left; box-shadow: 1px 1px 3px #555; border: 1px solid #000000; background-size: contain; } .singleCard { width: 40px; height: 60px; border-radius: 3px; text-align: left; box-shadow: 1px 1px 3px #555; border: 1px solid #000000; background-size: contain; float: right; } .playedCardsLayout .card:hover { z-index: 1400; } ` var card = document.createElement('div'); card.className = "card c3-5 singleCard"; document.getElementById('westPlayerName').appendChild(card); var styleSheet = document.createElement("style") styleSheet.innerText = myStyles; console.log(styleSheet); document.head.appendChild(styleSheet); // add an extra field where we will put down the played cards var cardsField = document.createElement('div'); cardsField.id = 'cardsField'; cardsField.style.marginLeft = '25px'; var playedHigh = document.createElement('div'); playedHigh.id = 'playedHigh'; playedHigh.style.marginLeft = '25px'; playedHigh.className = 'row'; playedHigh.hidden = true; var playedLow = document.createElement('div'); playedLow.id = 'playedLow'; playedLow.style.marginLeft = '25px'; playedLow.className = 'row'; playedLow.hidden = true; tableIG = document.getElementById('gameField').parentNode; tableIG.appendChild(playedHigh); tableIG.appendChild(playedLow); var createCardPlace = function(value, shift, node) { var playedCards = document.createElement('ul'); playedCards.id = 'playedCards' + value; // playedCards.className = 'layoutPlayedCards list-unstyled col-lg-2'; // playedCards.className = 'playedCardsLayout list-unstyled'; playedCards.className = 'playedCardsLayoutSmall list-unstyled'; playedCards.style.width = '160px'; playedCards.style.marginLeft = '25px'; playedCards.style.left = shift*129 + 'px'; node.appendChild(playedCards); } createCardPlace(1, 0, document.getElementById('playedHigh')); for(let i = 14; i > 8; i--) { createCardPlace(i, 14 - i + 1, document.getElementById('playedHigh')); } for(let i = 8; i > 1; i--) { createCardPlace(i, 8 - i, document.getElementById('playedLow')); } var smartFoldButton = document.createElement('button'); smartFoldButton.className = "btn btn-danger btn-xs"; smartFoldButton.id = "smartFold"; smartFoldButton.type = "button"; smartFoldButton.innerHTML = 'Smart fold: Yes'; var showPlayedButton = document.createElement('button'); showPlayedButton.className = "btn btn-primary btn-xs"; showPlayedButton.id = "showPlayed"; showPlayedButton.type = "button"; showPlayedButton.innerHTML = 'Show counted cards: No'; var showRemainingButton = document.createElement('button'); showRemainingButton.className = "btn btn-info btn-xs"; showRemainingButton.id = "showRemaining"; showRemainingButton.type = "button"; showRemainingButton.innerHTML = 'Show: Remaining'; buttons = document.getElementById('statusButtons'); buttons.insertBefore(showRemainingButton, document.getElementById('autoFold')); buttons.insertBefore(showPlayedButton, document.getElementById('autoFold')); buttons.insertBefore(smartFoldButton, document.getElementById('autoFold')); // playedCards. // ot.$gameField.insertAfter(playedCards, ot.$gameField.lastChild); // set up counting of played cards var OnlineTichuCounter = { $playedCards: {}, $smartFold: $('#smartFold'), $smartFoldValue: $('#smartFoldValue'), $showPlayed: $('#showPlayed'), $showPlayedValue: $('#showPlayedValue'), $showRemaining: $('#showRemaining'), $showRemainingValue: $('#showRemainingValue'), $playedHigh: $('#playedHigh'), $playedLow: $('#playedLow'), allCards: {}, playedCards: {}, totalPlayed: [], exchangedTo: {}, exchangedFrom: {}, smartFold: true, showPlayed: false, showRemaining: true, bombAfterFold: null, util: { displayCardsHtmlString: function(cards) { htmlString = ""; $(cards).each(function () { var value = this.Value; if (this.Shape == 7) { value = 0; } htmlString += '
  • '; }); return htmlString; }, populateAllCards: function() { for(let val = 2; val < 15; val++) { otc.allCards[val] = []; for(let shape = 0; shape < 4; shape++) { otc.allCards[val].push({ Shape: shape, Value: val }); } } otc.allCards[1] = [ { Shape: 4, Value: 1 }, // mahjong { Shape: 5, Value: 0 }, // dog { Shape: 6, Value: 15}, // dragon { Shape: 7, Value: 14 } // phoenix ]; }, cardInArray: function(card, array) { contained = false; for(let i = 0; i < array.length; i++) { if (array[i].Shape == card.Shape && array[i].Value == card.Value) { contained = true; } } return contained; } }, action: { handlePlayedCard: function(card) { index = 0; // special cards have Shapes 4,5,6,7. We group them at value 1 if(card.Shape >= 4) { index = 1; if (card.Shape == 7) { // Treat phoenix as if it had value 14 // This is important for comparisons later card.Value = 14; } } else { index = card.Value; } otc.playedCards[index].push(card); otc.totalPlayed.push(card); otc.playedCards[index].sort(function(c1, c2){ return c2.Shape - c1.Shape; }); }, handlePlayedCards: function(cards) { if (cards.length === 0) { return; } if (!otc.util.cardInArray(cards[0], otc.totalPlayed)) { $(message.Table.TableCards).each( (index, card) => otc.action.handlePlayedCard(card) ); } }, resetPlayedCards: function() { for(let i = 1; i < 15; i++) { otc.playedCards[i] = []; } otc.totalPlayed = []; }, drawPlayedCards: function() { for(let i = 1; i < 15; i++) { if (otc.showRemaining) { cards = []; for(let j=0; j < otc.allCards[i].length; j ++) { if(!otc.util.cardInArray(otc.allCards[i][j], otc.playedCards[i])) { cards.push(otc.allCards[i][j]); } otc.$playedCards[i].html(otc.util.displayCardsHtmlString(cards)); } } else { otc.$playedCards[i].html(otc.util.displayCardsHtmlString(otc.playedCards[i])); } } } } } if (!window.otc) { window.otc = OnlineTichuCounter; } otc.$showRemaining.hide(); // add functionality to smartFold button otc.$smartFold.click(function () { otc.smartFold = !otc.smartFold; if (otc.smartFold) { otc.$smartFoldValue.text(otc.$smartFoldValue.data('on')); } else { otc.$smartFoldValue.text(otc.$smartFoldValue.data('off')); } }); // add functionality to show button otc.$showPlayed.click(function () { otc.showPlayed = !otc.showPlayed; if (otc.showPlayed) { otc.$showPlayedValue.text(otc.$showPlayedValue.data('on')); otc.$playedHigh.show(); otc.$playedLow.show(); otc.$showRemaining.show(); } else { otc.$showPlayedValue.text(otc.$showPlayedValue.data('off')); otc.$playedHigh.hide(); otc.$playedLow.hide(); otc.$showRemaining.hide(); } }); otc.$showRemaining.click(function () { otc.showRemaining = !otc.showRemaining; if (otc.showRemaining) { otc.$showRemainingValue.text(otc.$showRemainingValue.data('on')); } else { otc.$showRemainingValue.text(otc.$showRemainingValue.data('off')); } otc.action.drawPlayedCards(); }); for(let i = 1; i < 15; i++) { otc.$playedCards[i] = $('#playedCards' + i); } otc.action.resetPlayedCards(); otc.util.populateAllCards(); MyTableState = (function() { var cachedFunction = ot.reaction.TableState; return function() { var result = cachedFunction.apply(this, arguments); message = arguments[0]; // console.log('detected table state change event'); // console.log(message.Table.Status); // reset played cards at start of round if(message.Table.Status == 'WaitForNextCards') { otc.action.resetPlayedCards(); } if(message.Table.Status == 'Playing') { otc.action.handlePlayedCards(message.Table.TableCards); } otc.action.drawPlayedCards(); console.log('reached smartFold point'); $(message.Table.Players).each(function () { if (this.Username == ot.$Username.val()) { console.log('found player'); if (message.Table.Turn == this.Position) { console.log('my turn!'); if (this.Cards.length < 4 && this.Cards.length < message.Table.TableCards.length) { console.log('obvious that have to fold now'); if (otc.smartFold) { ot.action.Fold(); } } } } }); return result; } })(); MyChat = (function() { var cachedFunction = ot.reaction.Chat; return function() { var result = cachedFunction.apply(this, arguments); message = arguments[0]; if (message.User.Username === "System") { var messageArray = message.Text.replace(//g, '>').split(' '); message.Text = messageArray.join(' '); messageArray = message.Text.replace(//g, '>').split(' '); for(var i in messageArray) { if(messageArray[i] == "dogs") { otc.action.handlePlayedCard({ "Shape": 5, "Value": 0 }); } } } return result; } })(); MyExchangeCards = (function() { var cachedFunction = ot.action.ExchangeCards; return function() { otc.exchangedTo['west'] = { Shape: ot.$westExchange.children('li:nth-child(1)').data('shape'), Value: ot.$westExchange.children('li:nth-child(1)').data('value') }; otc.exchangedTo['north'] = { Shape: ot.$northExchange.children('li:nth-child(1)').data('shape'), Value: ot.$northExchange.children('li:nth-child(1)').data('value') }; otc.exchangedTo['east'] = { Shape: ot.$eastExchange.children('li:nth-child(1)').data('shape'), Value: ot.$eastExchange.children('li:nth-child(1)').data('value') }; console.log(otc.exchangedTo); var result = cachedFunction.apply(this, arguments); return result; } })(); MyReviewExchange = (function() { var cachedFunction = ot.reaction.ReviewExchange; return function() { var result = cachedFunction.apply(this, arguments); return result; } })(); ot.reaction.TableState = MyTableState; ot.reaction.Chat = MyChat; ot.action.ExchangeCards = MyExchangeCards; }; // Special care is to be taken to safely inject our javascript into the websiet // without the website being able to talk back to greasemonkey // (which would be a security problem) // see // https://stackoverflow.com/questions/5006460/userscripts-greasemonkey-calling-a-websites-javascript-functions // and the excellent answer by Wayne for details on this function exec(fn) { var script = document.createElement('script'); script.setAttribute("type", "application/javascript"); script.textContent = '(' + fn + ')();'; document.body.appendChild(script); // run the script document.body.removeChild(script); // clean up } exec(injectCounter);