export function Tile(params) { // Required parameters if(isNone(params.cell)) { throw new Error('Missing Tile configuration'); } let tile = { cell: withDefault(params.cell, null), material: withDefault(params.material, null) }; // Check if we have a hexCell; if we do, destroy the old // reference and replace with this tile if(!isNone(tile.cell) && !isNone(tile.cell.tile)) { // Destroy old tile reference tile.cell.destroy(); } // Replace tile reference in the hexCell with this tile tile.cell.tile = tile; // Default material if(isNone(tile.material)) { tile.material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } ); } // Create mesh from geometry and material tile.mesh = new THREE.Mesh(tile.cell.geometry, tile.material); // Create RigidBody for physics calculations tile.planeShape = new Goblin.PlaneShape( 1, (tile.cell.width() / 2), (tile.cell.height() / 2) ); // Reference the mesh position and rotation for ease of use tile.position = tile.mesh.position; tile.rotation = tile.mesh.rotation; // rotate the tile to face "up" (the threejs coordinate space is Y+) tile.rotation.x = -90 * DEG_TO_RAD; let scale = withDefault(params.scale, 1); tile.mesh.scale.set(scale, scale, 1); if (tile.material.emissive) { tile._emissive = tile.material.emissive.getHex(); } else { tile._emissive = null; } return extend(tile, tileUtils); };
generate: function(params) { if(isNone(params)) { params = {}; } this.size = withDefault(params.size, this.size); let x, y, z; for (x = -this.size; x < this.size + 1; x++) { for (y = -this.size; y < this.size + 1; y++) { z = -x-y; if (Math.abs(x) <= this.size && Math.abs(y) <= this.size && Math.abs(z) <= this.size) { // Create a hexCell for the position in the grid let c = HexCell({ q: x, r: y, s: z, hashDelimeter: this.hashDelimeter, size: this.cellSize }); let t = Tile({ cell: c, material: params.material }); this.add(c); } } } },
export function Player(params) { if(isNone(params)) { params = {}; } let player = { geometry: withDefault(params.geometry, new THREE.BoxGeometry(1, 1, 1)), material: withDefault(params.material, new THREE.MeshBasicMaterial({color: 0xFF0000})) }; player.mesh = new THREE.Mesh(player.geometry, player.material); var box_shape = new Goblin.BoxShape( 1, 1, 1), // dimensions are half width, half height, and half depth, or a box 1x1x1 mass = 5, dynamic_box = new Goblin.RigidBody( box_shape, mass ); player.rigidBody = dynamic_box; // Reference the mesh position and rotation for ease of use player.position = player.mesh.position; player.rotation = player.mesh.rotation; return extend(player, utils); }
export function HexGrid(params) { if(isNone(params)) { params = {}; } let hexGrid = { cellSize: withDefault(params.cellSize, 10), cells: {}, hashDelimeter: withDefault(params.hashDelimeter, '-') }; // pre-computed permutations hexGrid._directions = [ HexCell({ q: +1, r: -1, s: 0, size: hexGrid.cellSize }), HexCell({ q: +1, r: 0, s: -1, size: hexGrid.cellSize }), HexCell({ q: 0, r: +1, s: -1, size: hexGrid.cellSize }), HexCell({ q: -1, r: +1, s: 0, size: hexGrid.cellSize }), HexCell({ q: -1, r: 0, s: +1, size: hexGrid.cellSize }), HexCell({ q: 0, r: -1, s: +1, size: hexGrid.cellSize }) ]; // this._diagonals = [HexCell(+2, -1, -1), HexCell(+1, +1, -2), HexCell(-1, +2, -1), // HexCell(-2, +1, +1), HexCell(-1, -1, +2), HexCell(+1, -2, +1)]; // cached objects hexGrid._list = []; hexGrid._vec3 = new THREE.Vector3(); hexGrid._cell = HexCell({ q: 0, r: 0, s: 0, hashDelimeter: hexGrid.hashDelimeter, size: hexGrid.cellSize }); hexGrid._conversionVec = new THREE.Vector3(); hexGrid._geoCache = []; hexGrid._matCache = []; return extend(hexGrid, hexGridUtils); };
export function Tile(params) { let tileUtils = { inView(tile) { var hexsInLineToTarget = this.lineTo(tile); return hexsInLineToTarget .map(function(hex) { // Map hexagons to tiles so we can access tile data return this.map.getTile(hex.position); }.bind(this)) .reduce(function(result, current) { if(!result) { return !current.blocksVisibility; } return result; }, false); }, pathTo(targetTile) { let frontier = this.neighbors.map(function(neighbor) { var neighborTile = this.map.getTile(neighbor); return { data: neighborTile, priority: neighborTile.movementCost }; }.bind(this)); let visited = {}; let cameFrom = {}; let cost = {}; visited[this.hash()] = null; cost[this.hash()] = 0; while(frontier.length > 0) { frontier = frontier.sort(function(a, b) { return a.priority - b.priority; }); // Pop off the current tile let current = frontier.pop().data; // Exit early if we found our target if(current.position.equals(targetTile.position)) { break; } // Get the current tiles neighbors and add // them to the frontier if they haven't been visited current.neighbors .map(function(neighbor) { // Map neighbor hexagons to tiles so we can access tile data return this.map.getTile(neighbor.position); }) .forEach(function(neighbor) { if(neighbor.passable) { let newCost = cost[current.hash()] + neighbor.movementCost; let neighborCost = cost[neighbor.hash()] || 0; if(!visited[neighbor.hash()] || newCost < neighborCost) { cost[neighbor.hash()] = newCost; // Add current tile to came from so we can assemble a // path later cameFrom[neighbor.hash()] = current; frontier.push({ data: neighbor, priority: newCost + neighbor.distanceTo(targetTile) }); } } }); // Add the current tile to visited visited[current.hash()] = current; } let path = [targetTile]; let current = targetTile; let start = this.position; while(!current.position.equals(start)) { current = cameFrom[current.hash()]; path.push(current); } path.reverse(); return path; } }; // Create our hexagon that we will be extending let hexagon = Hexagon(params); // Extend the hexagon and add any new properties let tile = extend(hexagon, { movementCost: withDefault(params.movementCost, 1), passable: withDefault(params.passable, true), blocksVisibility: withDefault(params.blocksVisibility, false), map: withDefault(params.map, Map()) }); return extend(tile, tileUtils); };
export function HexCell(params) { if(isNone(params)) { params = {}; } let hexCell = { q: withDefault(params.q, 0), r: withDefault(params.r, 0), s: withDefault(params.s, 0), h: withDefault(params.h, 1), hashDelimeter: withDefault(params.hashDelimeter, '-'), tile: withDefault(params.tile, null), size: withDefault(params.size, 1), hexType: withDefault(fromEnum(params.hexType, HEX_TYPES), HEX_TYPES.FLAT), }; // Calculate coordinates for the corners of the hexagon hexCell.corners = []; let offsetAngle = (hexCell.hexType === HEX_TYPES.FLAT ? 0 : 30); for(let i = 0; i < 6; i++) { hexCell.corners.push(corner(i, hexCell.size)); } // Calculate neighbor matrix let x = hexCell.q; let y = hexCell.r; let z = hexCell.s; hexCell.neighbors = [ new THREE.Vector3(x + 1, y - 1, z + 0), new THREE.Vector3(x + 1, y + 0, z - 1), new THREE.Vector3(x + 0, y + 1, z - 1), new THREE.Vector3(x - 1, y + 1, z + 0), new THREE.Vector3(x - 1, y + 0, z + 1), new THREE.Vector3(x + 0, y - 1, z + 1) ]; // Create geometry if we dont have one cached if(isNone(hexShapeGeo)) { hexShape = new THREE.Shape(); hexShape.moveTo(hexCell.corners[0].x, hexCell.corners[0].y); for (let i = 1; i < 6; i++) { hexShape.lineTo(hexCell.corners[i].x, hexCell.corners[i].y); } hexShape.lineTo(hexCell.corners[0].x, hexCell.corners[0].y); hexGeo = new THREE.Geometry(); hexGeo.vertices = hexCell.corners; hexGeo.verticesNeedUpdate = true; hexShapeGeo = new THREE.ShapeGeometry(hexShape); } // Add reference to the geometry on the hexagon hexCell.geometry = hexShapeGeo; // Extend hexCell metaobject with utils object hexCell = extend(hexCell, hexCellUtils); if(hexCell.hexType === HEX_TYPES.FLAT) { hexCell = extend(hexCell, flatHexagonUtils); } else if(hexCell.hexType === HEX_TYPES.POINTY) { hexCell = extend(hexCell, pointyHexagonUtils); } return hexCell; };
/* global THREE */ // HexCell object factory import {extend, isNone, fromEnum, withDefault} from 'engineUtil'; import {TAU} from 'core/constants'; export let hexCellUtils = { set(q, r, s) { this.q = q; this.r = r; this.s = s; return this; }, copy(hexCell) { this.q = withDefault(hexCell.q, 0); this.r = withDefault(hexCell.r, 0); this.s = withDefault(hexCell.s, 0); this.h = withDefault(hexCell.h, 1); this.tile = withDefault(hexCell.tile, null); return this; }, add(hexCell) { this.q += hexCell.q; this.r += hexCell.r; this.s += hexCell.s; return this; }, equals(hexCell) { return this.q === hexCell.q && this.r === hexCell.r && this.s === hexCell.s;