threejs-dice-es
Version:
Create 3d dice which can be rolled.
728 lines (626 loc) • 27.1 kB
JavaScript
'use strict';
import * as CANNON from 'cannon-es';
import * as THREE from 'three';
class DiceManagerClass {
constructor() {
this.world = null;
}
setWorld(world) {
this.world = world;
this.diceBodyMaterial = new CANNON.Material();
this.floorBodyMaterial = new CANNON.Material();
this.barrierBodyMaterial = new CANNON.Material();
world.addContactMaterial(
new CANNON.ContactMaterial(this.floorBodyMaterial, this.diceBodyMaterial, { friction: 0.01, restitution: 0.5 })
);
world.addContactMaterial(
new CANNON.ContactMaterial(this.barrierBodyMaterial, this.diceBodyMaterial, { friction: 0, restitution: 1.0 })
);
world.addContactMaterial(
new CANNON.ContactMaterial(this.diceBodyMaterial, this.diceBodyMaterial, { friction: 0, restitution: 0.5 })
);
}
/**
*
* @param {array} diceValues
* @param {DiceObject} [diceValues.dice]
* @param {number} [diceValues.value]
*
*/
prepareValues(diceValues) {
if (this.throwRunning) throw new Error('Cannot start another throw. Please wait, till the current throw is finished.');
for (let i = 0; i < diceValues.length; i++) {
if (diceValues[i].value < 1 || diceValues[i].dice.values < diceValues[i].value) {
throw new Error('Cannot throw die to value ' + diceValues[i].value + ', because it has only ' + diceValues[i].dice.values + ' sides.');
}
}
this.throwRunning = true;
for (let i = 0; i < diceValues.length; i++) {
diceValues[i].dice.simulationRunning = true;
diceValues[i].vectors = diceValues[i].dice.getCurrentVectors();
diceValues[i].stableCount = 0;
}
let check = () => {
let allStable = true;
for (let i = 0; i < diceValues.length; i++) {
if (diceValues[i].dice.isFinished()) {
diceValues[i].stableCount++;
} else {
diceValues[i].stableCount = 0;
}
if (diceValues[i].stableCount < 50) {
allStable = false;
}
}
if (allStable) {
console.log("all stable");
DiceManager.world.removeEventListener('postStep', check);
for (let i = 0; i < diceValues.length; i++) {
diceValues[i].dice.shiftUpperValue(diceValues[i].value);
diceValues[i].dice.resetBody();
diceValues[i].dice.setVectors(diceValues[i].vectors);
diceValues[i].dice.simulationRunning = false;
}
this.throwRunning = false;
} else {
DiceManager.world.step(DiceManager.world.dt);
}
};
this.world.addEventListener('postStep', check);
}
}
class DiceObject {
values;
scaleFactor;
vertices;
faces;
chamfer;
tab;
af;
textMargin;
faceTexts;
customTextTextureFunction;
mass;
simulationRunning
;
/**
* @constructor
* @param {object} options
* @param {Number} [options.size = 100]
* @param {Number} [options.fontColor = '#000000']
* @param {Number} [options.backColor = '#ffffff']
*/
constructor(options) {
options = this.setDefaults(options, {
size: 100,
fontColor: '#000000',
backColor: '#ffffff'
});
this.object = null;
this.size = options.size;
this.invertUpside = false;
this.materialOptions = {
specular: 0x172022,
color: 0xf0f0f0,
shininess: 40,
flatShading: true,
//shading: THREE.FlatShading,
};
this.labelColor = options.fontColor;
this.diceColor = options.backColor;
}
setDefaults(options, defaults) {
options = options || {};
for (let key in defaults) {
if (!defaults.hasOwnProperty(key)) continue;
if (!(key in options)) {
options[key] = defaults[key];
}
}
return options;
}
emulateThrow(callback) {
let stableCount = 0;
let check = () => {
if (this.isFinished()) {
stableCount++;
if (stableCount === 50) {
DiceManager.world.removeEventListener('postStep', check);
callback(this.getUpsideValue());
}
} else {
stableCount = 0;
}
DiceManager.world.step(DiceManager.world.dt);
};
DiceManager.world.addEventListener('postStep', check);
}
isFinished() {
let threshold = 1;
let angularVelocity = this.object.body.angularVelocity;
let velocity = this.object.body.velocity;
return (Math.abs(angularVelocity.x) < threshold && Math.abs(angularVelocity.y) < threshold && Math.abs(angularVelocity.z) < threshold &&
Math.abs(velocity.x) < threshold && Math.abs(velocity.y) < threshold && Math.abs(velocity.z) < threshold);
}
getUpsideValue() {
let vector = new THREE.Vector3(0, this.invertUpside ? -1 : 1);
let closest_face;
let closest_angle = Math.PI * 2;
let normals = this.object.geometry.getAttribute('normal').array;
for (let i = 0; i < this.object.geometry.groups.length; ++i) {
let face = this.object.geometry.groups[i];
if (face.materialIndex === 0) continue;
//Each group consists in 3 vertices of 3 elements (x, y, z) so the offset between faces in the Float32BufferAttribute is 9
let startVertex = i * 9;
let normal = new THREE.Vector3(normals[startVertex], normals[startVertex + 1], normals[startVertex + 2]);
let angle = normal.clone().applyQuaternion(this.object.body.quaternion).angleTo(vector);
if (angle < closest_angle) {
closest_angle = angle;
closest_face = face;
}
}
return closest_face.materialIndex - 1;
}
getCurrentVectors() {
return {
position: this.object.body.position.clone(),
quaternion: this.object.body.quaternion.clone(),
velocity: this.object.body.velocity.clone(),
angularVelocity: this.object.body.angularVelocity.clone()
};
}
setVectors(vectors) {
this.object.body.position = vectors.position;
this.object.body.quaternion = vectors.quaternion;
this.object.body.velocity = vectors.velocity;
this.object.body.angularVelocity = vectors.angularVelocity;
}
shiftUpperValue(toValue) {
let geometry = this.object.geometry.clone();
let fromValue = this.getUpsideValue();
for (let i = 0, l = geometry.groups.length; i < l; ++i) {
let materialIndex = geometry.groups[i].materialIndex;
if (materialIndex === 0) continue;
materialIndex += toValue - fromValue - 1;
while (materialIndex > this.values) materialIndex -= this.values;
while (materialIndex < 1) materialIndex += this.values;
geometry.groups[i].materialIndex = materialIndex + 1;
}
this.updateMaterialsForValue(toValue - fromValue);
this.object.geometry = geometry;
}
getChamferGeometry(vectors, faces, chamfer) {
let chamfer_vectors = [], chamfer_faces = [], corner_faces = new Array(vectors.length);
for (let i = 0; i < vectors.length; ++i) corner_faces[i] = [];
for (let i = 0; i < faces.length; ++i) {
let ii = faces[i], fl = ii.length - 1;
let center_point = new THREE.Vector3();
let face = new Array(fl);
for (let j = 0; j < fl; ++j) {
let vv = vectors[ii[j]].clone();
center_point.add(vv);
corner_faces[ii[j]].push(face[j] = chamfer_vectors.push(vv) - 1);
}
center_point.divideScalar(fl);
for (let j = 0; j < fl; ++j) {
let vv = chamfer_vectors[face[j]];
vv.subVectors(vv, center_point).multiplyScalar(chamfer).addVectors(vv, center_point);
}
face.push(ii[fl]);
chamfer_faces.push(face);
}
for (let i = 0; i < faces.length - 1; ++i) {
for (let j = i + 1; j < faces.length; ++j) {
let pairs = [], lastm = -1;
for (let m = 0; m < faces[i].length - 1; ++m) {
let n = faces[j].indexOf(faces[i][m]);
if (n >= 0 && n < faces[j].length - 1) {
if (lastm >= 0 && m !== lastm + 1) pairs.unshift([i, m], [j, n]);
else pairs.push([i, m], [j, n]);
lastm = m;
}
}
if (pairs.length !== 4) continue;
chamfer_faces.push([chamfer_faces[pairs[0][0]][pairs[0][1]],
chamfer_faces[pairs[1][0]][pairs[1][1]],
chamfer_faces[pairs[3][0]][pairs[3][1]],
chamfer_faces[pairs[2][0]][pairs[2][1]], -1]);
}
}
for (let i = 0; i < corner_faces.length; ++i) {
let cf = corner_faces[i], face = [cf[0]], count = cf.length - 1;
while (count) {
for (let m = faces.length; m < chamfer_faces.length; ++m) {
let index = chamfer_faces[m].indexOf(face[face.length - 1]);
if (index >= 0 && index < 4) {
if (--index === -1) index = 3;
let next_vertex = chamfer_faces[m][index];
if (cf.indexOf(next_vertex) >= 0) {
face.push(next_vertex);
break;
}
}
}
--count;
}
face.push(-1);
chamfer_faces.push(face);
}
return { vectors: chamfer_vectors, faces: chamfer_faces };
}
makeGeometry(vertices, faces, radius, tab, af) {
let geom = new THREE.BufferGeometry();
for (let i = 0; i < vertices.length; ++i) {
vertices[i] = vertices[i].multiplyScalar(radius);
}
let positions = [];
const normals = [];
const uvs = [];
const cb = new THREE.Vector3();
const ab = new THREE.Vector3();
let materialIndex;
let faceFirstVertexIndex = 0;
for (let i = 0; i < faces.length; ++i) {
let ii = faces[i], fl = ii.length - 1;
let aa = Math.PI * 2 / fl;
materialIndex = ii[fl] + 1;
for (let j = 0; j < fl - 2; ++j) {
//Vertices
positions.push(...vertices[ii[0]].toArray());
positions.push(...vertices[ii[j + 1]].toArray());
positions.push(...vertices[ii[j + 2]].toArray());
// Flat face normals
cb.subVectors( vertices[ii[j + 2]], vertices[ii[j + 1]] );
ab.subVectors( vertices[ii[0]], vertices[ii[j + 1]] );
cb.cross( ab );
cb.normalize();
// Vertex Normals
normals.push(...cb.toArray());
normals.push(...cb.toArray());
normals.push(...cb.toArray());
//UVs
uvs.push((Math.cos(af) + 1 + tab) / 2 / (1 + tab), (Math.sin(af) + 1 + tab) / 2 / (1 + tab));
uvs.push((Math.cos(aa * (j + 1) + af) + 1 + tab) / 2 / (1 + tab), (Math.sin(aa * (j + 1) + af) + 1 + tab) / 2 / (1 + tab));
uvs.push((Math.cos(aa * (j + 2) + af) + 1 + tab) / 2 / (1 + tab), (Math.sin(aa * (j + 2) + af) + 1 + tab) / 2 / (1 + tab));
}
//Set Group for face materials.
let numOfVertices = (fl - 2) * 3;
for (let i = 0; i < numOfVertices/3; i++) {
geom.addGroup(faceFirstVertexIndex, 3, materialIndex);
faceFirstVertexIndex += 3;
}
}
geom.setAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
geom.setAttribute( 'normal', new THREE.Float32BufferAttribute( normals, 3 ) );
geom.setAttribute( 'uv', new THREE.Float32BufferAttribute( uvs, 2 ) );
geom.boundingSphere = new THREE.Sphere(new THREE.Vector3(), radius);
return geom;
}
createShape(vertices, faces, radius) {
let cv = new Array(vertices.length), cf = new Array(faces.length);
for (let i = 0; i < vertices.length; ++i) {
let v = vertices[i];
cv[i] = new CANNON.Vec3(v.x * radius, v.y * radius, v.z * radius);
}
for (let i = 0; i < faces.length; ++i) {
cf[i] = faces[i].slice(0, faces[i].length - 1);
}
return new CANNON.ConvexPolyhedron({
vertices: cv,
faces: cf
});
}
getGeometry() {
let radius = this.size * this.scaleFactor;
let vectors = new Array(this.vertices.length);
for (let i = 0; i < this.vertices.length; ++i) {
vectors[i] = (new THREE.Vector3).fromArray(this.vertices[i]).normalize();
}
let chamferGeometry = this.getChamferGeometry(vectors, this.faces, this.chamfer);
let geometry = this.makeGeometry(chamferGeometry.vectors, chamferGeometry.faces, radius, this.tab, this.af);
geometry.cannon_shape = this.createShape(vectors, this.faces, radius);
return geometry;
}
calculateTextureSize(approx) {
return Math.max(128, Math.pow(2, Math.floor(Math.log(approx) / Math.log(2))));
}
createTextTexture(text, color, backColor) {
let canvas = document.createElement("canvas");
let context = canvas.getContext("2d");
let ts = this.calculateTextureSize(this.size / 2 + this.size * this.textMargin) * 2;
canvas.width = canvas.height = ts;
context.font = ts / (1 + 2 * this.textMargin) + "pt Arial";
context.fillStyle = backColor;
context.fillRect(0, 0, canvas.width, canvas.height);
context.textAlign = "center";
context.textBaseline = "middle";
context.fillStyle = color;
context.fillText(text, canvas.width / 2, canvas.height / 2);
let texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
return texture;
}
getMaterials() {
let materials = [];
for (let i = 0; i < this.faceTexts.length; ++i) {
let texture = null;
if (this.customTextTextureFunction) {
texture = this.customTextTextureFunction(this.faceTexts[i], this.labelColor, this.diceColor);
} else {
texture = this.createTextTexture(this.faceTexts[i], this.labelColor, this.diceColor);
}
materials.push(new THREE.MeshPhongMaterial(Object.assign({}, this.materialOptions, { map: texture })));
}
return materials;
}
getObject() {
return this.object;
}
create() {
if (!DiceManager.world) throw new Error('You must call DiceManager.setWorld(world) first.');
this.object = new THREE.Mesh(this.getGeometry(), this.getMaterials());
this.object.reveiceShadow = true;
this.object.castShadow = true;
this.object.diceObject = this;
this.object.body = new CANNON.Body({
mass: this.mass,
shape: this.object.geometry.cannon_shape,
material: DiceManager.diceBodyMaterial
});
this.object.body.linearDamping = 0.1;
this.object.body.angularDamping = 0.1;
DiceManager.world.addBody(this.object.body);
return this.object;
}
updateMeshFromBody() {
if (!this.simulationRunning) {
this.object.position.copy(this.object.body.position);
this.object.quaternion.copy(this.object.body.quaternion);
}
}
updateBodyFromMesh() {
this.object.body.position.copy(this.object.position);
this.object.body.quaternion.copy(this.object.quaternion);
}
resetBody() {
this.object.body.vlambda = new CANNON.Vec3();
//this.object.body.collisionResponse = true;
this.object.body.position = new CANNON.Vec3();
this.object.body.previousPosition = new CANNON.Vec3();
this.object.body.initPosition = new CANNON.Vec3();
this.object.body.velocity = new CANNON.Vec3();
this.object.body.initVelocity = new CANNON.Vec3();
this.object.body.force = new CANNON.Vec3();
//this.object.body.sleepState = 0;
//this.object.body.timeLastSleepy = 0;
//this.object.body._wakeUpAfterNarrowphase = false;
this.object.body.torque = new CANNON.Vec3();
this.object.body.quaternion = new CANNON.Quaternion();
this.object.body.initQuaternion = new CANNON.Quaternion();
this.object.body.angularVelocity = new CANNON.Vec3();
this.object.body.initAngularVelocity = new CANNON.Vec3();
this.object.body.interpolatedPosition = new CANNON.Vec3();
this.object.body.interpolatedQuaternion = new CANNON.Quaternion();
this.object.body.inertia = new CANNON.Vec3();
this.object.body.invInertia = new CANNON.Vec3();
this.object.body.invInertiaWorld = new CANNON.Mat3();
//this.object.body.invMassSolve = 0;
this.object.body.invInertiaSolve = new CANNON.Vec3();
this.object.body.invInertiaWorldSolve = new CANNON.Mat3();
//this.object.body.aabb = new CANNON.AABB();
//this.object.body.aabbNeedsUpdate = true;
this.object.body.wlambda = new CANNON.Vec3();
this.object.body.updateMassProperties();
}
updateMaterialsForValue(diceValue) {}
}
export class DiceD4 extends DiceObject {
tab;
af;
chamfer;
vertices;
faces;
scaleFactor;
values;
faceTexts;
updateMaterialsForValue;
customTextTextureFunction;
mass;
constructor(options) {
super(options);
this.tab = -0.1;
this.af = Math.PI * 7 / 6;
this.chamfer = 0.96;
this.vertices = [[1, 1, 1], [-1, -1, 1], [-1, 1, -1], [1, -1, -1]];
this.faces = [[1, 0, 2, 1], [0, 1, 3, 2], [0, 3, 2, 3], [1, 2, 3, 4]];
this.scaleFactor = 1.2;
this.values = 4;
this.d4FaceTexts = [
[[], [0, 0, 0], [2, 4, 3], [1, 3, 4], [2, 1, 4], [1, 2, 3]],
[[], [0, 0, 0], [2, 3, 4], [3, 1, 4], [2, 4, 1], [3, 2, 1]],
[[], [0, 0, 0], [4, 3, 2], [3, 4, 1], [4, 2, 1], [3, 1, 2]],
[[], [0, 0, 0], [4, 2, 3], [1, 4, 3], [4, 1, 2], [1, 3, 2]]
];
this.faceTexts = this.d4FaceTexts[0];
this.updateMaterialsForValue = function(diceValue) {
if (diceValue < 0) diceValue += 4;
this.faceTexts = this.d4FaceTexts[diceValue];
this.object.material = this.getMaterials();
};
this.customTextTextureFunction = function (text, color, backColor) {
let canvas = document.createElement("canvas");
let context = canvas.getContext("2d");
let ts = this.calculateTextureSize(this.size / 2 + this.size * 2) * 2;
canvas.width = canvas.height = ts;
context.font = ts / 5 + "pt Arial";
context.fillStyle = backColor;
context.fillRect(0, 0, canvas.width, canvas.height);
context.textAlign = "center";
context.textBaseline = "middle";
context.fillStyle = color;
for (let i in text) {
context.fillText(text[i], canvas.width / 2,
canvas.height / 2 - ts * 0.3);
context.translate(canvas.width / 2, canvas.height / 2);
context.rotate(Math.PI * 2 / 3);
context.translate(-canvas.width / 2, -canvas.height / 2);
}
let texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
return texture;
};
this.mass = 300;
this.inertia = 5;
this.invertUpside = true;
this.create();
}
}
export class DiceD6 extends DiceObject {
constructor(options) {
super(options);
this.tab = 0.1;
this.af = Math.PI / 4;
this.chamfer = 0.96;
this.vertices = [[-1, -1, -1], [1, -1, -1], [1, 1, -1], [-1, 1, -1],
[-1, -1, 1], [1, -1, 1], [1, 1, 1], [-1, 1, 1]];
this.faces = [[0, 3, 2, 1, 1], [1, 2, 6, 5, 2], [0, 1, 5, 4, 3],
[3, 7, 6, 2, 4], [0, 4, 7, 3, 5], [4, 5, 6, 7, 6]];
this.scaleFactor = 0.9;
this.values = 6;
this.faceTexts = [' ', '0', '1', '2', '3', '4', '5', '6', '7', '8',
'9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20'];
this.textMargin = 1.0;
this.mass = 300;
this.inertia = 13;
this.create();
}
}
export class DiceD8 extends DiceObject {
constructor(options) {
super(options);
this.tab = 0;
this.af = -Math.PI / 4 / 2;
this.chamfer = 0.965;
this.vertices = [[1, 0, 0], [-1, 0, 0], [0, 1, 0], [0, -1, 0], [0, 0, 1], [0, 0, -1]];
this.faces = [[0, 2, 4, 1], [0, 4, 3, 2], [0, 3, 5, 3], [0, 5, 2, 4], [1, 3, 4, 5],
[1, 4, 2, 6], [1, 2, 5, 7], [1, 5, 3, 8]];
this.scaleFactor = 1;
this.values = 8;
this.faceTexts = [' ', '0', '1', '2', '3', '4', '5', '6', '7', '8',
'9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20'];
this.textMargin = 1.2;
this.mass = 340;
this.inertia = 10;
this.create();
}
}
export class DiceD10 extends DiceObject {
constructor(options) {
super(options);
this.tab = 0;
this.af = Math.PI * 6 / 5;
this.chamfer = 0.945;
this.vertices = [];
this.faces = [[5, 7, 11, 0], [4, 2, 10, 1], [1, 3, 11, 2], [0, 8, 10, 3], [7, 9, 11, 4],
[8, 6, 10, 5], [9, 1, 11, 6], [2, 0, 10, 7], [3, 5, 11, 8], [6, 4, 10, 9],
[1, 0, 2, -1], [1, 2, 3, -1], [3, 2, 4, -1], [3, 4, 5, -1], [5, 4, 6, -1],
[5, 6, 7, -1], [7, 6, 8, -1], [7, 8, 9, -1], [9, 8, 0, -1], [9, 0, 1, -1]];
for (let i = 0, b = 0; i < 10; ++i, b += Math.PI * 2 / 10) {
this.vertices.push([Math.cos(b), Math.sin(b), 0.105 * (i % 2 ? 1 : -1)]);
}
this.vertices.push([0, 0, -1]);
this.vertices.push([0, 0, 1]);
this.scaleFactor = 0.9;
this.values = 10;
this.faceTexts = [' ', '0', '1', '2', '3', '4', '5', '6', '7', '8',
'9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20'];
this.textMargin = 1.0;
this.mass = 350;
this.inertia = 9;
this.create();
}
}
export class DiceD12 extends DiceObject {
constructor(options) {
super(options);
let p = (1 + Math.sqrt(5)) / 2;
let q = 1 / p;
this.tab = 0.2;
this.af = -Math.PI / 4 / 2;
this.chamfer = 0.968;
this.vertices = [[0, q, p], [0, q, -p], [0, -q, p], [0, -q, -p], [p, 0, q],
[p, 0, -q], [-p, 0, q], [-p, 0, -q], [q, p, 0], [q, -p, 0], [-q, p, 0],
[-q, -p, 0], [1, 1, 1], [1, 1, -1], [1, -1, 1], [1, -1, -1], [-1, 1, 1],
[-1, 1, -1], [-1, -1, 1], [-1, -1, -1]];
this.faces = [[2, 14, 4, 12, 0, 1], [15, 9, 11, 19, 3, 2], [16, 10, 17, 7, 6, 3], [6, 7, 19, 11, 18, 4],
[6, 18, 2, 0, 16, 5], [18, 11, 9, 14, 2, 6], [1, 17, 10, 8, 13, 7], [1, 13, 5, 15, 3, 8],
[13, 8, 12, 4, 5, 9], [5, 4, 14, 9, 15, 10], [0, 12, 8, 10, 16, 11], [3, 19, 7, 17, 1, 12]];
this.scaleFactor = 0.9;
this.values = 12;
this.faceTexts = [' ', '0', '1', '2', '3', '4', '5', '6', '7', '8',
'9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20'];
this.textMargin = 1.0;
this.mass = 350;
this.inertia = 8;
this.create();
}
}
export class DiceD20 extends DiceObject {
constructor(options) {
super(options);
let t = (1 + Math.sqrt(5)) / 2;
this.tab = -0.2;
this.af = -Math.PI / 4 / 2;
this.chamfer = 0.955;
this.vertices = [[-1, t, 0], [1, t, 0], [-1, -t, 0], [1, -t, 0],
[0, -1, t], [0, 1, t], [0, -1, -t], [0, 1, -t],
[t, 0, -1], [t, 0, 1], [-t, 0, -1], [-t, 0, 1]];
this.faces = [[0, 11, 5, 1], [0, 5, 1, 2], [0, 1, 7, 3], [0, 7, 10, 4], [0, 10, 11, 5],
[1, 5, 9, 6], [5, 11, 4, 7], [11, 10, 2, 8], [10, 7, 6, 9], [7, 1, 8, 10],
[3, 9, 4, 11], [3, 4, 2, 12], [3, 2, 6, 13], [3, 6, 8, 14], [3, 8, 9, 15],
[4, 9, 5, 16], [2, 4, 11, 17], [6, 2, 10, 18], [8, 6, 7, 19], [9, 8, 1, 20]];
this.scaleFactor = 1;
this.values = 20;
this.faceTexts = [' ', '0', '1', '2', '3', '4', '5', '6', '7', '8',
'9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20'];
this.textMargin = 1.0;
this.mass = 400;
this.inertia = 6;
this.create();
}
}
//---------------------------------------------//
export const DiceManager = new DiceManagerClass();
if (typeof define === 'function' && define.amd) {
define(function () {
return {
DiceManager: DiceManager,
DiceD4: DiceD4,
DiceD6: DiceD6,
DiceD8: DiceD8,
DiceD10: DiceD10,
DiceD12: DiceD12,
DiceD20: DiceD20,
};
});
} else if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
module.exports = {
DiceManager: DiceManager,
DiceD4: DiceD4,
DiceD6: DiceD6,
DiceD8: DiceD8,
DiceD10: DiceD10,
DiceD12: DiceD12,
DiceD20: DiceD20,
};
} else {
window.Dice = {
DiceManager: DiceManager,
DiceD4: DiceD4,
DiceD6: DiceD6,
DiceD8: DiceD8,
DiceD10: DiceD10,
DiceD12: DiceD12,
DiceD20: DiceD20,
};
}