// ==UserScript==
// @name Prettier-81Dojo
// @namespace http://tampermonkey.net/
// @version 0.1.7
// @description A CSS and shortcuts on 81 Dojo.
// @author Quisette
// @match http://81dojo.com/client/
// @match https://system.81dojo.com/ja/kifus/
// @match https://system.81dojo.com/en/kifus/
// @icon http://81dojo.com/client/favicon.ico
// @grant GM_log
// @grant GM_addStyle
// @run-at document-start
// @license private
// ==/UserScript==
!function() {
'use strict';
addCustomScripts()
deleteOldScripts()
removeClock()
//addArrowKeyMoveKifu()
}();
function addArrowKeyMoveKifu(){
document.addEventListener("keydown", e=>{
if(e.key === "ArrowLeft" ){
_replayButtonClick(-1)
}else if(e.key === "ArrowRight"){
_replayButtonClick(1)
}else if(e.key === "ArrowUp"){
_replayButtonClick(-2)
}else if(e.key === "ArrowDown"){
_replayButtonClick(2)
}else if(e.key === "Escape"){
_closeBoard()
}
})
}
function removeClock(){
new MutationObserver(function(mutations) {
if (document.getElementsByTagName('body')) {
const clock = document.getElementById("worldClocks")
if(clock){
clock.remove(clock)
this.disconnect(); // disconnect the observer
}
}
}).observe(document, {childList: true, subtree: true});
}
function addCustomScripts(){
var scriptElem = document.createElement('script')
scriptElem.innerHTML = (`
class Board{
constructor(div){
////////////////////////////////////////////////////////
///// EDIT THE PATH HERE TO CHANGE YOUR KOMA STYLE /////
////////////////////////////////////////////////////////
// default koma style is here //
// this.komaOrigin = 'http://81dojo.com/client/img/themes/ryoko/'
////////////////////////////////////////////////////////
this.komaOrigin = 'https://raw.githubusercontent.com/Quisette/komakasou/main/Portella_2moji/'
////////////////////////////////////////////////////////
this.komaUrl = this.komaOrigin
// Board Condition parameters
this.myRoleType = null // 0: sente as player, 1: gote as player, 2: watcher
this.isPostGame = false
this.studyHostType = null // 0: not host, 1: Sub-host, 2: Host
// Properties
this._div = div
this._originalWidth = div.width()
this._originalHeight = div.height()
this._komadais = new Array(2)
this.playerInfos = new Array(2)
this._timers = new Array(2)
this.accumulatedTimes = new Array(2)
this.endTime = null
this._flags = new Array(2)
this._publicPosition
this._position
this._direction = Position.CONST.SENTE
this.pieceDesignType = 0
this._selectedSquare = null
this._lastSquare = null
this._mouseDownSquare = null
this._pickedPiece = null
this._canMoveMyPiece = true
this._canMoveHisPiece = true
this.onListen = true
this._initialPositionStr = null
this.moves = new Array()
this.game = null
this.result = null // 0: sente win, 1: gote win, -1: draw
this._kid = null
this._rematchReady = [false, false]
this._arrowsSelf = []
this._arrowsPublic = []
this._scale = 1
this._promotionDialog = null
this.hostMark = null
this._generateParts()
this._setTheme()
this.loadPieceDesignOption()
}
detectKomaUrl(theme){ //wip
if (this.komaOrigin !== ''){
this.komaUrl = this.KomaOrigin
}else{
this.komaUrl = 'http://81dojo.com/client/img/themes/' + theme + '/'
}
}
_generateParts(){
this._div.empty()
this._ban = $('<div></div>', {id: 'banField'}).appendTo(this._div)
this._coord = $('<div></div>', {id: 'coord'}).appendTo(this._div)
this._komadais[0] = $('<div></div>', {id: 'senteKomadai', class: 'komadai'}).appendTo(this._div)
this._komadais[1] = $('<div></div>', {id: 'goteKomadai', class: 'komadai'}).appendTo(this._div)
this.playerInfos[0] = $('<div></div>', {id: 'senteInfo', class: 'player-info'}).appendTo(this._div)
this.playerInfos[1] = $('<div></div>', {id: 'goteInfo', class: 'player-info'}).appendTo(this._div)
this._timers[0] = new GameTimer($('<div></div>', {id: 'senteTimer', class: 'game-timer'}).appendTo(this._div))
this._timers[1] = new GameTimer($('<div></div>', {id: 'goteTimer', class: 'game-timer'}).appendTo(this._div))
this._flags[0] = $('<div></div>', {id: 'senteFlag', class: 'board-flag'}).appendTo(this._div)
this._flags[1] = $('<div></div>', {id: 'goteFlag', class: 'board-flag'}).appendTo(this._div)
for (let i = 0; i < 2; i++){
this.playerInfos[i].append('<div class="avatar-wrapper" style="margin:5px 15px;"><img class="avatar"/></div><span id="player-info-mark" style="font-size:15px">' + (i == 0 ? '☗' : '☖') + '</span><span id="player-info-name"></span><br><span id="player-info-rate"></span>')
}
$('[id=player-info-name]').dblclick(function(){_playerNameDblClick($(this).text())})
$('div.player-info').on('touchstart', function(e){
mouseX = e.originalEvent.touches[0].clientX
mouseY = e.originalEvent.touches[0].clientY
_playerNameDblClick($(this).find('#player-info-name').text())
})
this._arrowCanvas = $('<canvas></canvas>', {id: 'boardCanvas'}).attr({width: this._originalWidth, height: this._originalHeight}).appendTo(this._div)
this._pickedPiece = $('<div></div>', {class: 'picked-piece'})
this.hostMark = $('<i></i>', {class: 'fa fa-graduation-cap fa-lg'}).css('color', '#008')
let thisInstance = this
$('#senteKomadai, #goteKomadai').click(function(e){
thisInstance._handleKomadaiClick()
})
}
_setTheme(){
this._theme = "ichiji"
this._banW = 410
this._banH = 454
this._banX = 185
this._banY = 10
this._komadaiW = 170
this._komadaiH = 200
this._hisKomadaiX = 7
this._hisKomadaiY = 10
this._myKomadaiX = 605
this._myKomadaiY = 264
this._myInfoX = 615
this._myInfoY = 16
this._hisInfoX = 7
this._hisInfoY = 267
this._myFlagX = 718
this._myFlagY = 215
this._hisFlagX = 10
this._hisFlagY = 215
this._myTimerX = 615
this._myTimerY = 220
this._hisTimerX = 80
this._hisTimerY = 220
this._komaW = 43
this._komaH = 48
this._banEdgeX = 11
this._banEdgeY = 11
this._partSize()
this._partLayout()
//this.detectKomaUrl(this._theme)
this._preloadPieceImages(this._theme)
}
_partSize(){
this._ban.css({width: this._banW + 'px', height: this._banH + 'px'})
this._coord.css({width: this._banW + 'px', height: this._banH + 'px'})
$(this._komadais.map(e => e[0])).css({width: this._komadaiW + 'px', height: this._komadaiH + 'px'})
this._pickedPiece.css({width: this._komaW + 'px', height: this._komaH + 'px'})
}
_imagePath(){
let dir = ['dobutsu', 'blind_extreme'].includes(this._theme) ? this._theme : 'default'
this._ban.css('background-image', 'url(img/themes/' + dir + '/ban.jpg)')
this._coord.css('background-image', 'url(img/themes/' + dir + (this._direction ? '/Scoord.png)' : '/Gcoord.png)'))
this._komadais[this._direction ? 0 : 1].css('background-image', 'url(img/themes/' + dir + '/Shand.jpg)')
this._komadais[this._direction ? 1 : 0].css('background-image', 'url(img/themes/' + dir + '/Ghand.jpg)')
}
_partLayout(){
let myTurnIndex = this._direction ? 0 : 1
let hisTurnIndex = this._direction ? 1 : 0
this._ban.css({left: this._banX + 'px', top: this._banY + 'px'})
this._coord.css({left: this._banX + 'px', top: this._banY + 'px'})
this._komadais[myTurnIndex].css({left: this._myKomadaiX + 'px', top: this._myKomadaiY + 'px'})
this._komadais[hisTurnIndex].css({left: this._hisKomadaiX + 'px', top: this._hisKomadaiY + 'px'})
this.playerInfos[myTurnIndex].css({left: this._myInfoX + 'px', top: this._myInfoY + 'px'})
this.playerInfos[hisTurnIndex].css({left: this._hisInfoX + 'px', top: this._hisInfoY + 'px'})
this._flags[myTurnIndex].css({left: this._myFlagX + 'px', top: this._myFlagY + 'px'})
this._flags[hisTurnIndex].css({left: this._hisFlagX + 'px', top: this._hisFlagY + 'px'})
this._timers[myTurnIndex].setPosition(this._myTimerX, this._myTimerY)
this._timers[hisTurnIndex].setPosition(this._hisTimerX, this._hisTimerY)
}
_generateSquares(){
//Call after direction is set!
this._ban.empty()
for(let y = 1; y <= 9; y++){
for(let x = 1; x <= 9; x++){
let left
let top
if (this._direction) {
left = this._banEdgeX + (9-x) * this._komaW
top = this._banEdgeY + (y-1) * this._komaH
} else {
left = this._banEdgeX + (x-1) * this._komaW
top = this._banEdgeY + (9-y) * this._komaH
}
let sq = $('<div></div>', {id: 'sq' + x + '_' + y, class: 'square'}).data({x: x, y: y})
sq.css({width: this._komaW + 'px', height: this._komaH + 'px', left: left + 'px', top: top + 'px'})
if (x < this.position.xmin || x > this.position.xmax || y < this.position.ymin || y > this.position.ymax) sq.addClass('square-wall')
sq.appendTo(this._ban)
}
}
let thisInstance = this
$('.square').not('.square-wall').on("click", function(e){
if (e.ctrlKey) return
thisInstance._handleSquareClick($(this))
})
$('.square').not('.square-wall').on("mousedown", function(){
thisInstance._handleSquareMouseDown($(this))
})
$('.square').not('.square-wall').on("mouseup", function(e){
thisInstance._handleSquareMouseUp($(this), e.ctrlKey)
})
}
_refreshSquare(sq){
sq.removeClass("square-last")
if (sq.hasClass("square-wall")) return
let koma = this._position.getPieceFromSquare(sq)
if (koma) {
sq.css('background-image', 'url(' + this.komaUrl + koma.toImagePath(!this._direction) + ')')
} else {
sq.css('background-image', 'none')
}
}
_refreshPosition(){
if (this._position == null) return
let thisInstance = this
for (let i = 0; i < 2; i++){
this._komadais[i].empty()
}
$('.square').each(function(){
thisInstance._refreshSquare($(this))
})
$(".square-selected").removeClass("square-selected")
// $(".square-last").removeClass("square-last")
for (let i = 0; i < 2; i++){
let _layoutHandPiece = this._layoutHandPieceClosureFunc(i)
this._position.komadais[i].sort(function(a,b){return - a.type + b.type})
this._position.komadais[i].forEach(function(piece){
if (piece.CSA == 'OU' && this.game.gameType != 'vazoo') return
let sq = $('<div></div>', {id: 'sq' + (piece.owner ? 0 : -1) + '_' + piece.getType(), class: 'square'}).data({x: piece.owner ? 0 : -1, y: piece.getType()})
sq.css({width: this._komaW + 'px', height: this._komaH + 'px'})
sq.css('background-image', 'url(' + this.komaUrl + piece.toImagePath(!this._direction) + ')')
_layoutHandPiece(sq, piece)
let thisInstance = this
sq.on("click", function(e){
thisInstance._handleSquareClick($(this))
e.stopPropagation()
})
sq.on("mousedown", function(){
thisInstance._handleSquareMouseDown($(this))
})
sq.appendTo(this._komadais[i])
}, this)
_layoutHandPiece = null
}
if (this._position.lastMove) {
$('#sq' + this._position.lastMove.toX + '_' + this._position.lastMove.toY).addClass('square-last')
$('#sq' + this._position.lastMove.fromX + '_' + this._position.lastMove.fromY).addClass('square-last')
}
if ($("#modalImpasse").dialog('isOpen')) this.calcImpasse()
$(".name-popup").remove()
this.highlightIfIllegal()
}
highlightIfIllegal(){
let arrows = this.onListen ? this._arrowsPublic : this._arrowsSelf
if (arrows.length > 0) return
let obj = this.position.checkIllegal()
if (obj){
let name = EJ('ILLEGAL', '反則')
if (obj.cause == 'NIFU'){
this.addArrow(-1, obj.x1, obj.y1, obj.x1, obj.y1, 0xff0000, this.onListen, name)
this.addArrow(-1, obj.x2, obj.y2, obj.x2, obj.y2, 0xff0000, this.onListen, name)
} else if(obj.cause == 'SUICIDE'){
this.addArrow(-1, obj.x1, obj.y1, obj.x2, obj.y2, 0xff0000, this.onListen, name)
}
}
}
_layoutHandPieceClosureFunc(i){
let hash = this._position.handCoordinateHash(i)
let direction = this._direction == (i == 0)
let komaW = this._komaW
return function(sq, piece){
//square, piece
let dirSign = direction ? 1 : -1
let h = hash[piece.CSA]
if (h.i == 0) h.dx = h.n > 1 ? ((h.xmax - h.xmin) / (h.n - 1)) : 0
if (h.n > h.fanmax) {
let sqX = h.xmin + h.i * h.dx
let sqY = h.y
sq.css({left: sqX + 'px', top: sqY + 'px'})
} else {
let theta = direction ? (Math.floor((h.n - 1) / 2) * 11.2 - h.i * 11.2) : (Math.floor(h.n / 2) * (-11.2) + h.i * 11.2)
let sqX = 0.5 * (h.xmin + h.xmax) - 3 + (h.n % 2 == 0 ? (direction ? -0.35 : 0.45) * komaW : 0)
let sqY = h.y + (h.n > 3 && direction ? 8 : 0) + Math.abs(theta)*0.1*dirSign
let oY = 24 - dirSign * h.originH
sq.css({left: sqX + 'px', top: sqY + 'px', 'transform-origin': '50% ' + oY + 'px', transform: 'rotate(' + theta + 'deg)'})
}
h.i += 1
}
}
loadPieceDesignOption(){
let newTheme
let v = options.piece_type || 0
if (v <= 8) newTheme = ['ichiji', 'ninju', 'hidetchi', 'ichiji_ryoko', 'dobutsu', 'kinki', 'ryoko', 'kiyoyasu', 'shogicz'][v]
else if (v >= 100) newTheme = ['blind_middle', 'blind_hard', 'blind_extreme'][v - 100]
if (this._theme != newTheme) this._preloadPieceImages(newTheme)
this._theme = newTheme
this._imagePath()
//this.detectKomaUrl(this._theme)
this._refreshPosition()
}
_preloadPieceImages(theme){
let komaUrl = this.komaUrl;
['ou', 'gyoku', 'ryu', 'hi', 'uma', 'kaku', 'kin', 'ngin', 'gin', 'nkei', 'kei', 'nkyo', 'kyo', 'to', 'fu'].forEach(function(p){
$('<img>').attr('src', komaUrl + '/S' + p + '.png')
$('<img>').attr('src', komaUrl + '/G' + p + '.png')
})
}
setScale(scale){
if (scale == this._scale) return false // Not changed
this._div.css('transform', 'scale(' + scale + ')')
let hMargin = (scale - 1) * this._originalHeight / 2
let wMargin = (scale - 1) * this._originalWidth / 2
this._div.css('margin', hMargin + 'px ' + wMargin + 'px')
this._scale = scale
return true // Changed
}
actualWidth(){
return this._div.width() * this._scale
}
actualHeight(){
return this._div.height() * this._scale
}
setGame(game){
this.game = game
this.displayPlayerInfos()
if (!this.game.isStudy()) {
if (!this.game.isBlackIn && this.game.black.name != me.name) this.playerNameClassChange(0, "name-left", true)
if (!this.game.isWhiteIn && this.game.white.name != me.name) this.playerNameClassChange(1, "name-left", true)
}
for (let i = 0; i < 2; i++){
this._flags[i].css('opacity', this.game.isStudy() ? 0 : 1)
this._timers[i].setOpacity(this.game.isStudy() ? 0 : 1)
}
}
setDirection(direction){
this._direction = direction
this._partLayout()
this._imagePath()
}
displayPlayerInfos(){
for (let i = 0; i < 2; i++){
let user = i == 0 ? this.game.black : this.game.white
this.playerInfos[i].find("img.avatar").attr("src", user.avatarURL())
this.playerInfos[i].find("#player-info-name").html(user.name)
this.playerInfos[i].find("#player-info-rate").html('R: ' + user.rate + ' (' + makeRankFromRating(user.rate) + ')')
if (user.titleName() != "") this.playerInfos[i].find("#player-info-rate").append('<span style="color:white;font-weight:bold;background:crimson;padding:0 0.2em;margin-left:0.3em;cursor:default">' + user.titleTag() + '</span>')
this._flags[i].html(user.country.flagImgTagMovie())
}
}
flipBoard(){
this._cancelSelect()
this.setDirection(!this._direction)
this._generateSquares()
this._refreshPosition()
this.redrawAllArrows(this.onListen)
}
loadNewPosition(str = Position.CONST.INITIAL_POSITION){
this._publicPosition = new Position(this.game ? this.game.gameType : null)
this._publicPosition.superior = this.game ? (!this.game.isHandicap() && this.game.black.rate > this.game.white.rate) : false
this._publicPosition.loadFromString(str)
this._position = new Position(this.game ? this.game.gameType : null)
this._position.superior = this.game ? (!this.game.isHandicap() && this.game.black.rate > this.game.white.rate) : false
this._position.loadFromString(str)
this._initialPositionStr = str
this._generateSquares()
this._refreshPosition()
this._scratchKifu()
}
_scratchKifu(){
this.moves = new Array()
let firstMove = new Movement()
this.moves.push(firstMove)
kifuGrid.clear()
this.addMoveToKifuGrid(firstMove)
}
static get MOVEMENT_CONST(){
return Movement.CONST
}
addMoveToKifuGrid(move, highlight = true){
if (move.num < kifuGrid.rows().count()) {
while (kifuGrid.row(move.num).length == 1) kifuGrid.row(move.num).remove()
}
kifuGrid.row.add(move)
kifuGrid.draw()
if (highlight) {
kifuGrid.rows().deselect()
kifuGrid.row(move.num).select()
scrollGridToSelected(kifuGrid)
}
}
startGame(positionStr, myRoleType, move_strings = [], since_last_move = 0) {
this.myRoleType = myRoleType
if (myRoleType == 2) this.setDirection(true)
else this.setDirection(myRoleType == 0)
this.loadNewPosition(positionStr)
this.clearArrows(true)
this.clearArrows(false)
this.isPostGame = false
if (this.studyHostType == null) this.studyHostType = 0 // Keep the same host level as before even if startGame() is newly called
this.onListen = true
this._timers[0].initialize(this.game.total, this.game.byoyomi)
this._timers[1].initialize(this.game.total, this.game.byoyomi)
this.accumulatedTimes[0] = 0
this.accumulatedTimes[1] = 0
if (this.isPlayer()) this._timers[this.myRoleType].myPlayingTimer = true
this.clearRematch()
/*
_board_coord_image.visible = false;
studyOrigin = 0;
*/
if (move_strings.length > 0) {
move_strings.forEach(function(move_str){
if (move_str.match(/^%TORYO/)) {
resignTime = parseInt(move_str.split(",")[1])
return
}
let move = this.getFinalMove().constructNextMove()
move.setFromCSA(move_str.split(",")[0])
move.setTime(parseInt(move_str.split(",")[1]), this)
this.runningTimer.useTime(move.time)
move = this._publicPosition.makeMove(move, false)
this.moves.push(move)
kifuGrid.row.add(move)
}, this)
this._position.deepCopy(this._publicPosition)
this._refreshPosition()
kifuGrid.draw()
kifuGrid.rows().deselect()
kifuGrid.row(":last").select()
scrollGridToSelected(kifuGrid)
}
if (since_last_move > 0) {
this.runningTimer.useTime(since_last_move, true)
}
this.runningTimer.run()
this.updateTurnHighlight()
}
handleMonitorMove(move){
this.runningTimer.useTime(move.time)
move = this._publicPosition.makeMove(move)
this.moves.push(move)
if (!kifuGrid.row(':last').data().branch) {
kifuGrid.row.add(move)
if (this.onListen) {
kifuGrid.draw()
kifuGrid.rows().deselect()
kifuGrid.row(":last").select()
scrollGridToSelected(kifuGrid)
} else {
drawGridMaintainScroll(kifuGrid)
}
}
if (this.onListen) {
this._position.deepCopy(this._publicPosition)
this._refreshPosition()
}
this.updateTurnHighlight()
this.runningTimer.run()
}
refreshKifuGrid(){
}
_handleSquareMouseDown(sq){
this._mouseDownSquare = sq
}
_handleSquareMouseUp(sq, circleEnabled){
if (this._mouseDownSquare == null) return
if (circleEnabled && this._mouseDownSquare.is(sq) || !this._mouseDownSquare.is(sq)) {
if (this.onListen){
if (!board.isPlaying()) {
if (_studyBase != null || !this.isPostGame) this._addMyArrow(sq, true)
}
} else {
this._addMyArrow(sq, false)
}
}
this._mouseDownSquare = null
}
_addMyArrow(sqTo, isPublic){
if (me && me.isGuest) return
let fromType = -1
let fromX = this._mouseDownSquare.data().x
let fromY = this._mouseDownSquare.data().y
let toX = sqTo.data().x
let toY = sqTo.data().y
if (fromX <= 0) {
fromType = fromX == 0 ? 0 : 1
fromX = fromY
fromY = 0
let thisInstance = this
this._komadais[fromType].find('.square').each(function(i, node){
if ($(node).is(thisInstance._mouseDownSquare)) {
return false
} else if ($(node).data().y == thisInstance._mouseDownSquare.data().y) {
fromY++
}
})
}
this.addArrow(fromType, fromX, fromY, toX, toY, options.arrow_color, isPublic, me ? me.name : null)
if (isPublic) client.gameChat("[##ARROW]" + fromType + "," + fromX + "," + fromY + "," + toX + "," + toY + ",0x" + options.arrow_color.toString(16))
}
_handleSquareClick(sq){
if (false) {
let koma_test = this._position.getPieceFromSquare(sq)
if (koma_test) this._position.komadais[koma_test.owner ? 0 : 1].push(koma_test)
this._refreshPosition()
return
}
if (!this._selectedSquare) {
let koma = this._position.getPieceFromSquare(sq)
if (this._canMovePieceNow() && koma && koma.owner == this._position.turn) {
this._selectedSquare = sq
sq.addClass("square-selected")
if (!isTouchDevice && options.hold_piece && options.piece_type != 102) this._pickUpPiece(sq)
if (this.isPlaying()) { //Send ##GRAB message
let x = sq.data().x
let y = sq.data().y
if (x <= 0) {
x = 100 + koma.type
y = 0
}
sendGrab(x, y)
}
this._position.getMovableGridsFromSquare(sq).forEach(function(e){
$("#sq" + e[0] + "_" + e[1]).addClass("square-movable")
if (options.highlight_movable == 1 && options.piece_type != 102) $("#sq" + e[0] + "_" + e[1]).addClass("square-movable-highlight")
})
}
} else {
if (sq.hasClass("square-movable")) {
$(this._div).unbind("mouseleave")
let res = this._position.canPromote(this._selectedSquare, sq)
if (this.game.isKyoto() && this._selectedSquare.data().x <= 0) this._openPromotionDialog(sq, true) // Kyoto-shogi drop dialog
else if (res == 2) this._manualMoveCommandComplete(sq, true)
else if (res == 1) this._openPromotionDialog(sq)
else this._manualMoveCommandComplete(sq, false)
} else {
this._cancelSelect()
}
}
}
_handleKomadaiClick(){
if (this._selectedSquare) this._cancelSelect()
}
_pickUpPiece(sq){
this._pickedPiece.css('background-image', sq.css('background-image'))
this._positionPickedPiece(mouseX, mouseY)
this._pickedPiece.appendTo(this._div)
sq.addClass("square-picked-up")
let thisInstance = this
$(this._div).mousemove(function(e){
thisInstance._positionPickedPiece(e.pageX, e.pageY)
})
$(this._div).mouseleave(function(e){
if (e.pageX <= thisInstance._div.offset().left ||
e.pageX >= thisInstance._div.offset().left + thisInstance.actualWidth() ||
e.pageY <= thisInstance._div.offset().top ||
e.pageY >= thisInstance._div.offset().top + thisInstance.actualHeight()) thisInstance._cancelSelect()
})
}
_positionPickedPiece(mouse_X, mouse_Y){
this._pickedPiece.css({
top: (mouse_Y - this._div.offset().top) / this._scale - this._komaW/2 - 4 + 'px',
left: (mouse_X - this._div.offset().left) / this._scale - this._komaH/2 + 2 + 'px'
})
}
_openPromotionDialog(sq, kyotoFlipDrop = false){
//square
let thisInstance = this
this._promotionDialog = $("<div></div>",{
title: kyotoFlipDrop ? i18next.t("board.ask_flip") : i18next.t("board.ask_promote"),
html: '<div id="promotion-window">\
<button type=button id="promote-yes" class="promotion-button"><img class="promotion-image" id="promote-yes-image"></button>\
<button type=button id="promote-no" class="promotion-button"><img class="promotion-image" id="promote-no-image"></button>\
</div>'
}).dialog({
dialogClass: 'no-close',
modal: true,
autoOpen: false,
width: 100,
minHeight: 0,
position: {at:'left+'+mouseX+' top+'+mouseY},
close: function(e){
thisInstance._promotionDialog.dialog('destroy').remove()
thisInstance._promotionDialog = null
}
})
let koma = this._position.getPieceFromSquare(this._selectedSquare)
let komaUrl = this.komaUrl
this._promotionDialog.find('#promote-yes-image').attr('src', this.komaUrl + (kyotoFlipDrop ? koma.convertKyoto().toImagePath(!this._direction) : koma.toImagePath(!this._direction, true)))
this._promotionDialog.find('#promote-no-image').attr('src', this.komaUrl + koma.toImagePath(!this._direction))
$('#promote-yes').click(function(){
thisInstance._manualMoveCommandComplete(sq, true)
})
$('#promote-no').click(function(){
thisInstance._manualMoveCommandComplete(sq, false)
})
this._promotionDialog.dialog('open')
}
_manualMoveCommandComplete(destinationSquare, promote){
//sqaure, boolean
let move = kifuGrid.row({selected: true}).data().constructNextMove()
move.setFromManualMove(this._position.turn, this._selectedSquare, destinationSquare, promote)
this._cancelSelect()
this._executeManualMove(move)
}
_executeManualMove(move){
this.clearArrows(false)
if (this.isPlaying()) {
this.runningTimer.stop()
}
move = this._position.makeMove(move)
if (this.isPlaying()) {
sendMoveAsPlayer(move)
this._publicPosition.deepCopy(this._position)
this.updateTurnHighlight()
} else {
move.branch = true
this.addMoveToKifuGrid(move)
if (this.onListen && this.studyHostType >= 1) {
sendStudy()
this.clearArrows(true)
} else forceKifuMode(0)
}
this._refreshPosition()
}
handleReceivedMove(move){
move = this._publicPosition.makeMove(move)
this.moves.push(move)
this.addMoveToKifuGrid(move)
if (this.onListen) {
this._position.deepCopy(this._publicPosition)
this._refreshPosition()
}
}
replayMoves(moves){
this._cancelSelect()
//_last_to_square = null;
this._position.loadFromString(this._initialPositionStr)
for (let i = 0; i < moves.length; i++) {
if (moves[i].replayable()) this._position.makeMove(moves[i], false)
}
/*
if (mv.replayable()) {
_last_to_square = _cells[mv.to.y][mv.to.x];
_last_to_square.setStyle('backgroundColor', '0xCC3333');
if (mv.from.x < Kyokumen.HAND) {
_last_from_square = _cells[mv.from.y][mv.from.x];
_last_from_square.setStyle('backgroundColor', '0xFF5555');
}
}
*/
this._refreshPosition()
}
handleBranchMove(move, withSound = false){
return this._position.makeMove(move, withSound)
}
handleGrab(x, y){
$(".square-selected").removeClass("square-selected")
if (x == 0) {
return
} else if (x >= 100) {
y = x - 100
x = this._position.turn ? 0 : -1
}
$("#sq" + x + "_" + y).addClass("square-selected")
}
setResult(v){
this.result = v // 0: sente win, 1: gote win, -1: draw
if (v >= 0) {
board.playerNameClassChange(v, "name-winner", true)
}
}
setBoardConditions(){
if (this.isPlaying()) {
this._canMoveMyPiece = true
this._canMoveHisPiece = false
this._coord.css('opacity', 0)
} else {
this._canMoveMyPiece = !this.onListen || this.studyHostType >= 1
this._canMoveHisPiece = !this.onListen || this.studyHostType >= 1
this._coord.css('opacity', 1)
}
}
_canMovePieceNow(){
return (this._canMoveMyPiece && this._position.turn == this._direction) || (this._canMoveHisPiece && this._position.turn != this._direction)
}
_cancelSelect(){
if (this._promotionDialog) this._promotionDialog.dialog('close')
if (this._selectedSquare != null){
$(this._div).unbind("mousemove mouseleave")
$(".square").removeClass("square-movable square-movable-highlight")
this._pickedPiece.remove()
this._selectedSquare.removeClass("square-selected square-picked-up")
this._selectedSquare = null
}
if (this.isPlaying()) sendGrab(0,0)
//_endGrab();
//_pieceGrab = false;
}
updateTurnHighlight(){
if (this.isPostGame) {
this.playerInfos[0].removeClass('player-info-has-turn')
this.playerInfos[1].removeClass('player-info-has-turn')
} else {
this.playerInfos[this._publicPosition.turn ? 1 : 0].removeClass('player-info-has-turn')
this.playerInfos[this._publicPosition.turn ? 0 : 1].addClass('player-info-has-turn')
}
}
playerNameClassChange(turn, className, add_or_remove){
//int, string, boolean
if (add_or_remove) this.playerInfos[turn].find("#player-info-name").addClass(className)
else this.playerInfos[turn].find("#player-info-name").removeClass(className)
}
pauseAllTimers(){
this._timers[0].stop(true)
this._timers[1].stop(true)
}
close(){
this.myRoleType = null
this.studyHostType = null
this.game = null
this.result = null
this.playerInfos[0].find("#player-info-name").removeClass("name-winner name-left name-mouse-out")
this.playerInfos[1].find("#player-info-name").removeClass("name-winner name-left name-mouse-out")
this._timers[0].stop()
this._timers[1].stop()
this.endTime = null
}
rematch(turn){
//integer
this._rematchReady[turn] = true
}
rematchAgreed(){
return this._rematchReady[0] == true && this._rematchReady[1] == true
}
clearRematch(){
this._rematchReady = [false, false]
}
disconnectTimer(turn){
//integer
this._timers[turn].disconnect()
}
reconnectTimer(turn){
//integer
this._timers[turn].reconnect()
if (this.isPlaying) this.runningTimer.run()
}
calcImpasse(){
if (this.isPlaying() && this.myRoleType == (this._position.turn ? 0 : 1)) {
_updateImpasseWindow(this._position.calcImpasse(), this.myRoleType)
} else {
_updateImpasseWindow(this._position.calcImpasse())
}
}
refreshPosition(){
this._refreshPosition()
}
addArrow(fromType, fromX, fromY, toX, toY, color, isPublic, sender){
//integer, integer, integer, integer, integer, uint, boolean, string
let arrows = isPublic ? this._arrowsPublic : this._arrowsSelf
let isFull = arrows.length >= BoardArrow.CONST.MAX_ARROWS
if (isFull) {
arrows.shift()
if (this.onListen == isPublic) this.redrawAllArrows(isPublic)
}
let arrow = new BoardArrow(fromType, fromX, fromY, toX, toY, color, sender, this._arrowCanvas)
arrows.push(arrow)
if (this.onListen == isPublic) {
arrow.draw(this._scale)
if (sender && me && sender != me.name) this.popupName(toX, toY, sender, color)
}
}
clearArrows(isPublic, sender = "*"){
let arrows = isPublic ? this._arrowsPublic : this._arrowsSelf
let found = false
if (sender != "*") {
for (let i = arrows.length - 1; i >= 0; i--) {
if (arrows[i].name == sender) {
found = true
arrows.splice(i,1)
}
}
}
if (!found) arrows.length = 0
if (this.onListen && isPublic || !this.onListen && !isPublic) this.redrawAllArrows(isPublic)
return found
}
redrawAllArrows(isPublic, withLabel = false){
let arrows = isPublic ? this._arrowsPublic : this._arrowsSelf
this.clearCanvas()
arrows.forEach(function(arrow){
arrow.draw(this._scale)
if (withLabel) this.popupName(arrow.toX, arrow.toY, arrow.name, arrow.color)
}, this)
}
popupName(x, y, name, color){
let sq = $('#sq' + x + '_' + y)
let posX = (sq.offset().left - this._div.offset().left) / this._scale + 0.56 * sq.width()
let posY = (sq.offset().top - this._div.offset().top) / this._scale + 0.66 * sq.height()
let element = $("<div></div>",{
class: 'name-popup',
html: '<span>' + name + '</span>'
}).css({left: posX, top: posY, color: intToColorStyle(color)})
this._div.append(element)
element.delay(1500).animate({top: (posY - 15) + 'px', opacity: 0}, 1500, function(){$(this).remove()})
}
clearCanvas(){
this._arrowCanvas.get(0).getContext('2d').clearRect(0, 0, this._arrowCanvas.width(), this._arrowCanvas.height())
}
isPlayer(){
return (this.myRoleType == 0 || this.myRoleType == 1)
}
isPlaying(){
return this.isPlayer() && !this.isPostGame
}
isWatcher(){
return this.myRoleType == 2
}
isHost(){
return this.studyHostType == 2
}
isSubHost(){
return this.studyHostType == 1
}
isPlayerPresent(turn){
//integer: 0 or 1
return !this.playerInfos[turn].find("#player-info-name").hasClass("name-left")
}
getPlayerRoleFromName(name){
if (!this.game) return null
if (name == this.game.black.name) return 0
else if (name == this.game.white.name) return 1
else return null
}
getFinalMove(){
return this.moves[this.moves.length - 1]
}
getPlayersTimer(sente){
return this._timers[sente ? 0 : 1]
}
getOpponent(){
if (this.myRoleType == 0) return this.game.white
else if (this.myRoleType == 1) return this.game.black
else return null
}
setKifuId(kid){
this._kid = kid
}
toKifuURL(withOption = false){
if (this._kid == null || this._kid == 0) {
return ""
} else {
let url = "http://system.81dojo.com/" + EJ('en', 'ja') + "/kifus/" + this._kid
if (withOption) {
let vars = []
if (!this._direction) vars.push("turn=1")
if (kifuGrid.row({selected: true}).index() != 0) vars.push("moves=" + kifuGrid.row({selected: true}).index())
if (vars.length > 0) url += "?" + vars.join("&")
}
return url
}
}
getMaterialBalance(joban){
return this._position.materialBalance(this._direction, joban)
}
get runningTimer(){
return this._timers[this._publicPosition.turn ? 0 : 1]
}
get position(){
return this._position
}
get div(){
return this._div
}
}
class Piece{
constructor(owner, promoted = false){
//boolean(sente:true), int, int, boolean
this.owner = owner
this._promoted = promoted
this.type
this.CSA = ""
this.promotedCSA = null
this._img = ""
this._promotedImg = null
this._sfen = ""
this.normalNotation = 'S'
this.reversedNotation ='G'
}
normalMoves(){
return []
}
promotedMoves(){
return []
}
adjacentMoves(){
return this._promoted ? this.promotedMoves() : this.normalMoves()
}
farMoves(){
return []
}
promote(){
if (!this._promoted) this._promoted = true
}
depromote(){
if (this._promoted) this._promoted = false
}
toString(){
let str = this.owner ? "+" : "-"
return str + (this._promoted ? this.promotedCSA : this.CSA)
}
toImagePath(reverse = false, showPromotedSide = false){
return (this.owner == !reverse ? this.normalNotation : this.reversedNotation ) + ((this._promoted || showPromotedSide) ? this._promotedImg : this._img) + ".png"
}
getType(){
return this.type + (this._promoted ? 8 : 0)
}
isPromoted(){
return this._promoted
}
isKing(){
return false
}
isPawn(){
return false
}
isPromotable(){
return true
}
toSFEN(){
let str = (this.isPromotable() && this.isPromoted() ? "+" : "") + this._sfen
return this.owner ? str : str.toLowerCase()
}
soundVolume(){
return 1
}
// kyotoConvert(){
// kyoto_conversion:Array = new Array(0, 7, 4, 5, 2, 3, 15, 1, 8, 9, 10, 11, 12, 13, 14, 6);
// }
mustPromote(rowsAhead){
// integer (how many rows are ahead of the piece until the end of the opponent side)
return false
}
checkSibling(){ //whether the piece needs to check siblings when moved
return true
}
convertKyoto(){
return this
}
}
class PieceOU extends Piece{
constructor(owner, promoted){
super(owner, promoted)
this.point = 0
this.type = 0
this.CSA = "OU"
this.promotedCSA = "OU"
this._img = "gyoku"
this._promotedImg = "ou"
this._sfen = "K"
}
normalMoves(){
return [[0, +1], [+1, +1], [-1, +1], [+1, +0], [-1, +0], [0, -1], [+1, -1], [-1, -1]]
}
promotedMoves(){
return this.normalMoves()
}
isKing(){
return true
}
isPromotable(){
return false
}
checkSibling(){
return false
}
}
class PieceHI extends Piece{
constructor(owner, promoted){
super(owner, promoted)
this.point = 5
this.type = 1
this.CSA = "HI"
this.promotedCSA = "RY"
this._img = "hi"
this._promotedImg = "ryu"
this._sfen = "R"
}
promotedMoves(){
return [[+1, +1], [-1, +1], [+1, -1], [-1, -1]]
}
farMoves(){
return [[0, +1], [+1, 0], [0, -1], [-1, 0]]
}
soundVolume(){
return this._promoted ? 1 : 0.9
}
convertKyoto(){
return new PieceFU(this.owner)
}
}
class PieceKA extends Piece{
constructor(owner, promoted){
super(owner, promoted)
this.point = 5
this.type = 2
this.CSA = "KA"
this.promotedCSA = "UM"
this._img = "kaku"
this._promotedImg = "uma"
this._sfen = "B"
}
promotedMoves(){
return [[0, +1], [+1, 0], [-1, 0], [0, -1]]
}
farMoves(){
return [[+1, +1], [+1, -1], [-1, -1], [-1, +1]]
}
soundVolume(){
return this._promoted ? 1 : 0.9
}
convertKyoto(){
return new PieceGI(this.owner)
}
}
class PieceKI extends Piece{
constructor(owner, promoted){
super(owner, false)
this.point = 1
this.type = 3
this.CSA = "KI"
this._img = "kin"
this._sfen = "G"
}
normalMoves(){
return [[0, +1], [+1, +1], [-1, +1], [+1, +0], [-1, +0], [0, -1]]
}
isPromotable(){
return false
}
soundVolume(){
return 0.65
}
convertKyoto(){
return new PieceKE(this.owner)
}
}
class PieceGI extends Piece{
constructor(owner, promoted){
super(owner, promoted)
this.point = 1
this.type = 4
this.CSA = "GI"
this.promotedCSA = "NG"
this._img = "gin"
this._promotedImg = "ngin"
this._sfen = "S"
}
normalMoves(){
return [[0, +1], [+1, +1], [-1, +1], [+1, -1], [-1, -1]]
}
promotedMoves(){
return [[0, +1], [+1, +1], [-1, +1], [+1, +0], [-1, +0], [0, -1]]
}
soundVolume(){
return 0.65
}
convertKyoto(){
return new PieceKA(this.owner)
}
}
class PieceKE extends Piece{
constructor(owner, promoted){
super(owner, promoted)
this.point = 1
this.type = 5
this.CSA = "KE"
this.promotedCSA = "NK"
this._img = "kei"
this._promotedImg = "nkei"
this._sfen = "N"
}
normalMoves(){
return [[+1, +2], [-1, +2]]
}
promotedMoves(){
return [[0, +1], [+1, +1], [-1, +1], [+1, +0], [-1, +0], [0, -1]]
}
soundVolume(){
return 0.5
}
mustPromote(rowsAhead){
return !this._promoted && rowsAhead < 2
}
convertKyoto(){
return new PieceKI(this.owner)
}
}
class PieceKY extends Piece{
constructor(owner, promoted){
super(owner, promoted)
this.point = 1
this.type = 6
this.CSA = "KY"
this.promotedCSA = "NY"
this._img = "kyo"
this._promotedImg = "nkyo"
this._sfen = "L"
}
promotedMoves(){
return [[0, +1], [+1, +1], [-1, +1], [+1, +0], [-1, +0], [0, -1]]
}
farMoves(){
return this._promoted ? [] : [[0, +1]]
}
soundVolume(){
return 0.5
}
mustPromote(rowsAhead){
return !this._promoted && rowsAhead < 1
}
checkSibling(){
return this._promoted
}
convertKyoto(){
return new PieceFU(this.owner, true)
}
}
class PieceFU extends Piece{
constructor(owner, promoted){
super(owner, promoted)
this.point = 1
this.type = 7
this.CSA = "FU"
this.promotedCSA = "TO"
this._img = "fu"
this._promotedImg = "to"
this._sfen = "P"
}
normalMoves(){
return [[0, +1]]
}
promotedMoves(){
return [[0, +1], [+1, +1], [-1, +1], [+1, +0], [-1, +0], [0, -1]]
}
isPawn(){
return !this._promoted
}
soundVolume(){
return this._promoted ? 0.4 : 0.35
}
mustPromote(rowsAhead){
return !this._promoted && rowsAhead < 1
}
checkSibling(){
return this._promoted
}
convertKyoto(){
return this._promoted ? new PieceKY(this.owner) : new PieceHI(this.owner)
}
}
class PieceZL extends PieceOU{
constructor(owner, promoted){
super(owner, promoted)
this._img = "ra"
this._promotedImg = "ra"
}
}
class PieceZG extends Piece{
constructor(owner, promoted){
super(owner, false)
this.type = 1
this.CSA = "ZG"
this._img = "ki"
}
normalMoves(){
return [[0, +1], [+1, +0], [-1, +0], [0, -1]]
}
isPromotable(){
return false
}
}
class PieceZE extends Piece{
constructor(owner, promoted){
super(owner, false)
this.type = 2
this.CSA = "ZE"
this._img = "zo"
}
normalMoves(){
return [[+1, +1], [-1, +1], [+1, -1], [-1, -1]]
}
isPromotable(){
return false
}
}
class PieceZC extends Piece{
constructor(owner, promoted){
super(owner, promoted)
this.type = 7
this.CSA = "ZC"
this.promotedCSA = "TO"
this._img = "hi"
this._promotedImg = "ni"
}
normalMoves(){
return [[0, +1]]
}
promotedMoves(){
return [[0, +1], [+1, +1], [-1, +1], [+1, +0], [-1, +0], [0, -1]]
}
checkSibling(){
return this._promoted
}
}
`)
document.head.appendChild(scriptElem)
}
function deleteOldScripts(){
new MutationObserver(function(mutations) {
if (document.getElementsByTagName('body')) {
var scripts = document.querySelectorAll('script');
if (scripts) {
for (var i = (scripts.length-1); i >= 0; i--) {
if(scripts[i].getAttribute('src') === 'js/Board.js?2.2.7\n' || scripts[i].getAttribute('src') === 'js/Piece.js?2.2.7\n'|| scripts[i].getAttribute('src') === 'js/WorldClock.js?2.2.7\n' ){
scripts[i].parentNode.removeChild(scripts[i]);
}
}
this.disconnect(); // disconnect the observer
}
}
}).observe(document, {childList: true, subtree: true});
}
GM_addStyle(`
@import url('https://fonts.googleapis.com/css2?family=Noto+Serif+JP&family=Roboto+Mono:wght@500&display=swap'); body{
font-family: Noto Serif JP, sans-serif !important;
font-size: 14px !important;
transition: 0.2s !important;
}
#playerListBox{
width: 30% !important;
}
#replayButtonFirst, #replayButtonBack, #replayButtonForward, #replayButtonLast {
border-radius: 8px !important;
}
#replayButtonFirst:hover, #replayButtonBack:hover, #replayButtonForward:hover, #replayButtonLast:hover {
transform: scale(0.9) translateY(3px)!important;
transition: 0.2s !important;
}
a{
transition: 0.2s!important;
}
.picked-piece{
background-size: cover !important;
}
.square{
transition: 0.2s !important;
background-position:center !important;
background-size: cover !important;
}
@keyframes testAnimation{
0%{
opacity: 100%
}
50%{
opacity: 0%
}
100%{
opacity: 100%
}
}
tr{
transition: 0.15s !important;
}
.ui-dialog{
transition: 0.2s !important;
border-radius: 10px !important;
}
.game-timer{
border-radius: 8px !important;
border: 0px !important;
box-shadow: 3px 3px 3px 0px #303030 !important;
}
#goteInfo, #senteInfo{
border-radius: 8px !important;
box-shadow: 3px 3px 3px 0px #303030 !important;
text-align: center !important;
}
.avatar{
box-shadow: 2px 2px 3px 0px #aaaaaa !important;
border: solid 2px #ddd !important;
border-radius: 8px !important;
}
.game-timer-label{
font-family: Roboto Mono!important;
}
.game-timer-sub-label{
font-family: Roboto Mono !important;
color: #0080ff !important;
width: 95% !important;
}
#player-info-name #player-info-rate{
text-align: center !important;
}
#boardBox{
background: #DFCCA1 !important;
border-radius: 10px !important;
}
.ui-widget{
font-family: Noto Serif JP !important;
border-radius: 10px !important;
}
.boardChatInput,.lobbyChatInput{
border-radius: 2em !important;
font-family: Noto Serif JP !important;
}
div#playerGridWrapper, #waiterGridWrapper, #lobbyChatBox, #gameGridWrapper,#lobbyMessageArea{
border-radius: 10px;
margin-bottom: 10px;
}
#loginButton, #reloginButton{
transition: 0.2s; border-radius: 9999em !important; height: 30px
}
#banField{
border-radius: 5px !important;
box-shadow: 3px 3px 3px 0px #303030 !important;
}
.komadai{
border-radius: 10px !important;
background-image: url("https://www.shogi-extend.com/system/material/board/12.png") !important;
box-shadow: 3px 3px 3px 0px #303030 !important;
}
.layer{
transition: 0.2s;
}
::-webkit-scrollbar {
width: 10px;
}
/* Track */
::-webkit-scrollbar-track {
// box-shadow: inset 0 0 5px grey;
border-radius: 10px;
}
/* Handle */
::-webkit-scrollbar-thumb {
background: grey;
border-radius: 5px;
}
#lobbyMessageArea{
overflow-y: auto !important;
}
`)