From 4b2752263838267f579c0d967e8bd8ad0a22f815 Mon Sep 17 00:00:00 2001 From: miniature-tiger <35037641+miniature-tiger@users.noreply.github.com> Date: Sun, 29 Apr 2018 17:59:28 +0100 Subject: [PATCH] Adding trade routes --- board.css | 6 ++ board.js | 70 +++++++++++-- compass.css | 1 - contracts.js | 284 +++++++++++++++++++++++++++++++++++++++++++++++---- main.js | 9 +- movement.js | 5 + pieces.css | 16 +++ 7 files changed, 359 insertions(+), 32 deletions(-) diff --git a/board.css b/board.css index 297be24..c843449 100644 --- a/board.css +++ b/board.css @@ -24,6 +24,12 @@ z-index: 2; } +.tradeRoute { + position: absolute; + background-color: 'transparent'; + z-index: 10; +} + /* style for row holders within game board */ .board_row { margin: 0px 0px 0px 0px; diff --git a/board.js b/board.js index 7e74c72..d1cf39e 100644 --- a/board.js +++ b/board.js @@ -137,12 +137,12 @@ let gameBoard = { // Creation of plantation this.boardArray[boardCenter][boardCenter+1].pieces = {populatedSquare: true, category: 'Resources', type: 'plantation', direction: '0', used: 'unused', team: 'Kingdom', goods: 'coffee', stock: 0}; - /* // TEST AREA - this.boardArray[(row-3)][boardCenter].terrain = 'land'; - this.boardArray[(row-3)][boardCenter].pieces = {populatedSquare: true, category: 'Settlements', type: 'fort', direction: '0', used: 'unused', team: 'Kingdom', goods: 'none', stock: 0}; - tradeContracts.contractsArray[2].row = row-3; - tradeContracts.contractsArray[2].col = boardCenter; - */ + // TEST AREA + /* this.boardArray[(row-4)][boardCenter].terrain = 'land'; + this.boardArray[(row-4)][boardCenter].pieces = {populatedSquare: true, category: 'Settlements', type: 'fort', direction: '0', used: 'unused', team: 'Kingdom', goods: 'none', stock: 0}; + tradeContracts.contractsArray[2].row = row-4; + tradeContracts.contractsArray[2].col = boardCenter; */ + }, // Method to allocate start tiles to teams @@ -698,7 +698,6 @@ let gameBoard = { for (var j = 0; j < col; j++) { Xcenter = (gridSize + tileBorder * 2) * j + (gridSize/2 + boardSurround + tileBorder); - // Currently just cargo ships - other tiles to be update to svg if (this.boardArray[i][j].pieces.populatedSquare == true) { // Create action tile svg and add to the board boardMarkNode.appendChild(this.createActionTile(i, j, this.boardArray[i][j].pieces.type, this.boardArray[i][j].pieces.team,'tile' + Number(i*1000 + j), boardSurround + tileBorder/2 + (gridSize + tileBorder * 2) * i, boardSurround + tileBorder/2 + (gridSize + tileBorder * 2) * j, 1, this.boardArray[i][j].pieces.direction)); @@ -938,5 +937,62 @@ let gameBoard = { }, + // Method to add trade route layer to board + // ----------------------------------------- + createTradeRouteLayer: function() { + let layer = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + layer.setAttribute('width', col * (gridSize + tileBorder * 2) + boardSurround * 2); + layer.setAttribute('height', row * (gridSize + tileBorder * 2) + boardSurround * 2); + layer.setAttribute('viewBox', '0, 0, ' + (col * (gridSize + tileBorder * 2) + boardSurround * 2) + ', ' + (row * (gridSize + tileBorder * 2) + boardSurround * 2)); + layer.setAttribute('class', 'tradeRoute'); + return(layer); + }, + + // Method to draw a trade route on the board + // ----------------------------------------- + // Local path is an array of objects of the form {fromRow: 15, fromCol: 4} + tradeRoute: function(localPath, localTeam) { + + let route = document.createElementNS('http://www.w3.org/2000/svg', 'path'); + route.setAttribute('class', localTeam + ' team_route'); + + let buildRoute = 'M ' + (boardSurround + tileBorder + gridSize/2 + (gridSize + tileBorder * 2) * localPath[0].fromCol) + ' ' + (boardSurround + tileBorder + gridSize/2 + (gridSize + tileBorder * 2) * localPath[0].fromRow); + //let buildRoute = 'M ' + (boardSurround + tileBorder + gridSize/2 + (gridSize + tileBorder * 2) * localPath[0].fromCol + gameManagement.teamArray.indexOf(gameManagement.turn)*2-4) + ' ' + (boardSurround + tileBorder + gridSize/2 + (gridSize + tileBorder * 2) * localPath[0].fromRow + gameManagement.teamArray.indexOf(gameManagement.turn)*2-4); + + for (var i = 0; i < localPath.length; i++) { + buildRoute += ' L ' + (boardSurround + tileBorder + gridSize/2 + (gridSize + tileBorder * 2) * localPath[i].fromCol) + ' ' + (boardSurround + tileBorder + gridSize/2 + (gridSize + tileBorder * 2) * localPath[i].fromRow); + //buildRoute += ' L ' + (boardSurround + tileBorder + gridSize/2 + (gridSize + tileBorder * 2) * localPath[i].fromCol + gameManagement.teamArray.indexOf(gameManagement.turn)*2-4) + ' ' + (boardSurround + tileBorder + gridSize/2 + (gridSize + tileBorder * 2) * localPath[i].fromRow + gameManagement.teamArray.indexOf(gameManagement.turn)*2-4); + } + + route.setAttribute('d', buildRoute); + route.setAttribute('fill', 'none'); + route.setAttribute('stroke-linecap', 'round'); + route.setAttribute('stroke-linejoin', 'round'); + route.setAttribute('stroke-opacity', '0.5'); + route.style.strokeWidth = '3px'; + tradeRouteLayer.appendChild(route); + + let startCircle = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); + startCircle.setAttribute('class', localTeam + ' team_fill team_route'); + startCircle.setAttribute('cx', (boardSurround + tileBorder + gridSize/2 + (gridSize + tileBorder * 2) * localPath[0].fromCol)); + startCircle.setAttribute('cy', (boardSurround + tileBorder + gridSize/2 + (gridSize + tileBorder * 2) * localPath[0].fromRow)); + startCircle.setAttribute('r', '3'); + startCircle.style.strokeWidth = '1px'; + startCircle.style.strokeLinecap = 'round'; + startCircle.setAttribute('fill', 'none'); + tradeRouteLayer.appendChild(startCircle); + + let endCircle = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); + endCircle.setAttribute('class', localTeam + ' team_fill team_route'); + endCircle.setAttribute('cx', (boardSurround + tileBorder + gridSize/2 + (gridSize + tileBorder * 2) * localPath[localPath.length-1].fromCol)); + endCircle.setAttribute('cy', (boardSurround + tileBorder + gridSize/2 + (gridSize + tileBorder * 2) * localPath[localPath.length-1].fromRow)); + endCircle.setAttribute('r', '3'); + endCircle.style.strokeWidth = '1px'; + endCircle.style.strokeLinecap = 'round'; + //endCircle.setAttribute('fill', 'none'); + tradeRouteLayer.appendChild(endCircle); + + }, + // LAST BRACKET OF OBJECT } diff --git a/compass.css b/compass.css index bf49f39..705d207 100644 --- a/compass.css +++ b/compass.css @@ -7,7 +7,6 @@ position: absolute; background-color: rgb(246, 232, 206); z-index: 1; - } #needle2 { diff --git a/contracts.js b/contracts.js index 88f011c..4dc6354 100644 --- a/contracts.js +++ b/contracts.js @@ -9,7 +9,6 @@ let tradeContracts = { {name: 'Swordfish'}, ], - // Method to populate contracts array // ---------------------------------- populateContracts: function() { @@ -70,8 +69,8 @@ let tradeContracts = { }, -// Method to check possible delivery to fulfil open contract -// --------------------------------------------------------- + // Method to check possible delivery to fulfil open contract + // --------------------------------------------------------- checkDelivery : function(locali, localj, searchType, localStock) { // Determines which fort is being delivered to let chosenFort = -1; @@ -92,36 +91,279 @@ let tradeContracts = { // Method to complete contract delivery // ------------------------------------ - fulfilDelivery : function() { - // Determines which fort is being delivered to - let chosenFort = -1; - for (var k = 0; k < this.contractsArray.length; k++) { - if(this.contractsArray[k].row == pieceMovement.movementArray.end.row && this.contractsArray[k].col == pieceMovement.movementArray.end.col) { - chosenFort = k; + fulfilDelivery : function() { + // Determines which fort is being delivered to + let chosenFort = -1; + for (var k = 0; k < this.contractsArray.length; k++) { + if(this.contractsArray[k].row == pieceMovement.movementArray.end.row && this.contractsArray[k].col == pieceMovement.movementArray.end.col) { + chosenFort = k; + } + } + + // Updates game board based on delivery + deliveryGoods = gameBoard.boardArray[pieceMovement.movementArray.start.row][pieceMovement.movementArray.start.col].pieces.goods; + deliveryStock = this.contractsArray[chosenFort].contracts[deliveryGoods].initial; + gameBoard.boardArray[pieceMovement.movementArray.start.row][pieceMovement.movementArray.start.col].pieces.stock -= deliveryStock; + if (gameBoard.boardArray[pieceMovement.movementArray.start.row][pieceMovement.movementArray.start.col].pieces.stock == 0) { + gameBoard.boardArray[pieceMovement.movementArray.start.row][pieceMovement.movementArray.start.col].pieces.goods = 'none'; + } + + // Updates contracts array based on delivery + this.contractsArray[chosenFort].contracts[deliveryGoods].struck = gameManagement.turn; + this.contractsArray[chosenFort].totalOpen -=1; + this.contractsArray[chosenFort].totalClosed +=1; + }, + + // Array of paths for finding trade route + // -------------------------------------- + tradePath: [], + + // Method to initialise tradePath array which holds board size (i rows x j columns) array of paths + // ----------------------------------------------------------------------------------------------- + initialiseTradePath: function(localStartRow, localStartCol) { + for (var i = 0; i < col; i++) { + let localMoveRow = []; + for (var j = 0; j < row; j++) { + localMoveRow[j] = {activeStatus: 'inactive', moveCost: 0, distance: 0, team: '', path: [{fromRow: +localStartRow , fromCol: +localStartCol}]}; + } + this.tradePath[i] = localMoveRow; + } + }, + + // Method to find path for ship to move + // ------------------------------------ + discoverPath: function(localEndRow, localEndCol, localGoods) { + // Finds current team resource in boardArray for start of path + let localStartRow = 0; + let localStartCol = 0; + for (var y = 0; y < gameBoard.boardArray.length; y++) { + for (var x = 0; x < gameBoard.boardArray[y].length; x++) { + if (gameBoard.boardArray[y][x].pieces.team == gameManagement.turn && gameBoard.boardArray[y][x].pieces.goods == localGoods && gameBoard.boardArray[y][x].pieces.category == 'Resources') { + localStartRow = y; + localStartCol = x; + } + } + } + + // Converts start and end land positions into the nearest non-diagonal sea positions for start and end of trade route + let harbour = this.nearestHarbour(localStartRow, localStartCol, localEndRow, localEndCol); + + // Finds straight-line direction from start to end of trade path to aid in choice of trade path route + let directionAngle = Math.floor(Math.atan(Math.abs(harbour.harbourEndCol - harbour.harbourStartCol) / Math.abs(harbour.harbourEndRow - harbour.harbourStartRow)) * (180 / Math.PI)); + if(harbour.harbourEndRow - harbour.harbourStartRow > 0 && harbour.harbourEndCol - harbour.harbourStartCol > 0) { + directionAngle += 90; + } else if(harbour.harbourEndRow - harbour.harbourStartRow > 0 && harbour.harbourEndCol - harbour.harbourStartCol <= 0) { + directionAngle -= 180; + } else if(harbour.harbourEndRow - harbour.harbourStartRow <= 0 && harbour.harbourEndCol - harbour.harbourStartCol <= 0) { + directionAngle -= 90; + } + + // Safety net on number of iterations - should not be required as while loop applied + let localMaxMove = Math.abs(localEndRow - localStartRow) + Math.abs(localEndCol - localStartCol); + + // Initialises tradePath array which holds board size array of paths + this.initialiseTradePath(harbour.harbourStartRow, harbour.harbourStartCol); + + // Sets clicked piece status to active as starting point of chain reaction of setting active status + this.tradePath[harbour.harbourStartRow][harbour.harbourStartCol].activeStatus = 'active'; + + // Loops until fort destination harbour is found + // Each loop searches for potentially reachable tiles to activated within one tile reach of a previously activated tile + // First search (k=0) takes each active tile within 1x1 grid around piece (i.e. just the piece itself) then uses + // pathTiles to search in 3x3 grid around this tile + // Second search (k=1) takes each active tile within a 3x3 grid of the piece (i.e. within one tile move reach of the piece) then uses + // pathTiles to search in 3x3 grid around each of these active tiles + // Third search (k=2) takes each active tile within a 5x5 grid of the piece (i.e. within two tile move reach of the piece) then uses + // pathTiles to search in 3x3 grid around each of these active tiles (making a maximum potential 3 tile distance from the piece for active tiles) + // Continues until destination is found + + let k = 0; + let found = false; + while (found == false && k < localMaxMove) { + + // Loops through i rows and j columns to form the 3x3 etc grids + for (var i = -k; i < k+1; i++) { + // Restrict by map size for rows so not searching off edge of board + if(harbour.harbourStartRow+i>=0 && harbour.harbourStartRow+i =0 && harbour.harbourStartCol+j =0 && localStartRowI+i =0 && localStartColJ+j Math.abs(horizontal)) { + if(vertical >= 0) { + if(horizontal >= 0) { + harbourRank = [[1,0], [0,1], [0,-1], [-1,0]] + } else { + harbourRank = [[1,0], [0,-1], [0,1], [-1,0]] + } + } else { + if(horizontal >= 0) { + harbourRank = [[-1,0], [0,1], [0,-1], [1,0]] + } else { + harbourRank = [[-1,0], [0,-1], [0,1], [1,0]] + } + } + } else { + if(vertical >= 0) { + if(horizontal >= 0) { + harbourRank = [[0,1], [1,0], [-1,0], [0,-1]] + } else { + harbourRank = [[0,-1], [1,0], [-1,0], [0,1]] + } + } else { + if(horizontal >= 0) { + harbourRank = [[0,1], [-1,0], [1,0], [0,-1]] + } else { + harbourRank = [[0,-1], [-1,0], [1,0], [0,1]] + } } + } - // Updates contracts array based on delivery - this.contractsArray[chosenFort].contracts[deliveryGoods].struck = gameManagement.turn; - this.contractsArray[chosenFort].totalOpen -=1; - this.contractsArray[chosenFort].totalClosed +=1; + let resourceHarbour = false; + let fortHarbour = false; + let harbourStartRow = 0; + let harbourStartCol = 0; + let harbourEndRow = 0; + let harbourEndCol = 0; - }, + // Loops through each of four possible tiles to find preferred harbour + for (var i = 0; i < harbourRank.length; i++) { + if(resourceHarbour == false && (localStartRow+harbourRank[i][0]) >=0 && (localStartRow+harbourRank[i][0]) < 31 && (localStartCol+harbourRank[i][1]) >=0 && (localStartCol+harbourRank[i][1]) < 31) { + if(gameBoard.boardArray[localStartRow+harbourRank[i][0]][localStartCol+harbourRank[i][1]].terrain == 'sea') { + harbourStartRow = localStartRow+harbourRank[i][0]; + harbourStartCol = localStartCol+harbourRank[i][1]; + resourceHarbour = true; + } + } + // Reverses the order of preference at the other end of the trade route + if(fortHarbour == false && (localEndRow+harbourRank[harbourRank.length - 1 - i][0]) >=0 && (localEndRow+harbourRank[harbourRank.length - 1 - i][0]) < 31 && (localEndCol+harbourRank[harbourRank.length - 1 - i][1]) >=0 && (localEndCol+harbourRank[harbourRank.length - 1 - i][1]) < 31) { + + if(gameBoard.boardArray[localEndRow+harbourRank[harbourRank.length - 1 - i][0]][localEndCol+harbourRank[harbourRank.length - 1 - i][1]].terrain == 'sea') { + harbourEndRow = localEndRow+harbourRank[harbourRank.length - 1 - i][0]; + harbourEndCol = localEndCol+harbourRank[harbourRank.length - 1 - i][1]; + fortHarbour = true; + } + } + } + return {harbourStartRow, harbourStartCol, harbourEndRow, harbourEndCol}; + }, + + // Method to find the most direct trade route + // ------------------------------------------- + // Sets the cost of each move in relation to the overall direction of the trade route + pathCost: function(localStartRow, localStartCol, localEndRow, localEndCol, localDirectionAngle) { + let moveCostResult = 0; + // Calculates direction of move + let localMoveTop = (localEndRow - localStartRow); + let localMoveLeft = (localEndCol - localStartCol); + let localMoveDirection = pieceMovement.movementDirection[localMoveLeft+1][localMoveTop+1]; + // Calculates difference in angle of direction between trade route direction and piece movement + let angleDiff = (localMoveDirection - localDirectionAngle + 360) % 360; + // Returns cost based on the difference in angle + if (angleDiff > 89 && angleDiff < 271) { + moveCostResult = 1.1; + } else if (angleDiff > 44 && angleDiff < 316) { + moveCostResult = 0.9; + } else { + moveCostResult = 0.75; + } + return moveCostResult; + }, // Method to populate contract dashboard on right-hand panel // --------------------------------------------------------- - drawContracts : function() { + drawContracts: function() { // Finds the stockDashboard holder in the left hand panel let contractDashboardNode = document.querySelector('div.contractDashboard'); diff --git a/main.js b/main.js index c14d8f2..942765d 100644 --- a/main.js +++ b/main.js @@ -39,7 +39,6 @@ for (var c = 0; c < headFootCollection.length; c++) { // boardMarkNode is board holder in document let boardMarkNode = document.querySelector('div.boardmark'); - // Canvas element createed for board let board = document.createElement('canvas'); board.setAttribute('id', 'board'); @@ -49,7 +48,6 @@ let canvasBoard = board.getContext('2d'); canvasBoard.canvas.width = col * (gridSize + tileBorder * 2) + boardSurround * 2; canvasBoard.canvas.height = row * (gridSize + tileBorder * 2) + boardSurround * 2; - // Canavs 'activeBoard' is created and size is set dynamically let activeBoard = document.createElement('canvas'); boardMarkNode.appendChild(activeBoard); @@ -59,6 +57,10 @@ canvasActive.canvas.height = col * (gridSize + tileBorder * 2) + boardSurround * // ID is set with CSS styles of higher z-index and transparent background to function as overlay activeBoard.setAttribute('id', 'activeBoard'); +// SVG layer for trade routes set up +let tradeRouteLayer = gameBoard.createTradeRouteLayer(); +boardMarkNode.appendChild(tradeRouteLayer); + // Function to set up board and resource deck and allocate resources function boardSetUp(row, col, gridSize, boardShape) { gameBoard.populateBoardArray(row, col, boardShape); @@ -238,7 +240,7 @@ window.addEventListener('click', function(element) { // Maximum number of iterations for movement and thus maximum number of tiles that can be moved // --- movement is also influenced by the movement cost // --- once more transport is created this will all need to be built into an array if it desired that different ships move at different speeds -let maxMove = 3; +let maxMove = 5; // Variables for clicked tiles with startEnd indicating the start or end of the move let startEnd = 'start'; @@ -388,6 +390,7 @@ boardMarkNode.addEventListener('click', function(event) { gameBoard.drawActiveTiles(); tradeContracts.fulfilDelivery(); tradeContracts.drawContracts(); + tradeContracts.discoverPath(pieceMovement.movementArray.end.row, pieceMovement.movementArray.end.col, pieceMovement.movementArray.start.pieces.goods); // Unloading to own team fort or resource tile } else if (pieceMovement.movementArray.start.pieces.type == 'cargo ship' && pieceMovement.movementArray.end.pieces.team == gameManagement.turn && (pieceMovement.movementArray.end.pieces.type == 'fort' || pieceMovement.movementArray.end.pieces.category == 'Resources')) { diff --git a/movement.js b/movement.js index 6ce3e03..871bf85 100644 --- a/movement.js +++ b/movement.js @@ -252,6 +252,10 @@ let pieceMovement = { // Obtaining path of piece that leads to end tile of move from findPath array let localPath = this.findPath[this.movementArray.end.row][this.movementArray.end.col].path; + console.log(localPath); + // tem p + //gameBoard.tradeRoute(localPath, gameManagement.turn); + // Length gives number of steps in path let numberOfTiles = localPath.length - 1; @@ -302,6 +306,7 @@ let pieceMovement = { chosenPiece.style.top = parseFloat(chosenPiece.style.top) + (topDirection * (gridSize + tileBorder*2)) + 'px'; chosenPiece.style.transform = 'rotate(' + rotateDirection + 'deg)'; }, n * 1000); + }, // Method to allow discovery of new land tiles diff --git a/pieces.css b/pieces.css index 23068eb..778e387 100644 --- a/pieces.css +++ b/pieces.css @@ -370,6 +370,10 @@ stroke: #00394d; } +.Blue.team_route { + stroke: #0099cc; +} + .Orange.team_fill { background-color: #bd5d0f; @@ -380,6 +384,10 @@ stroke: #472306; } +.Orange.team_route { + stroke: #bd5d0f; +} + .Red.team_fill { background-color: #a52746; fill: #a52746; @@ -389,6 +397,10 @@ stroke: #3e0f1a; } +.Red.team_route { + stroke: #a52746; +} + .Green.team_fill { background-color: #34987d; fill: #34987d; @@ -398,6 +410,10 @@ stroke: #14392f; } +.Green.team_route { + stroke: #34987d; +} + .Kingdom.team_stroke { stroke: #251934; }