- set up files to run locally
	- added workaround for an occasional that prevents dice from loading (detects error and refreshes the window after a few seconds)
	- changed lighting for better readability
	- optomized some redundant code
	- new, more straightforward dice scaling method
	- made result popup invisible to enable backend functionality without visual disruption
		- this is a bad solution implemented during experimentation while learning what the code does.. I will probably be too lazy to fix it later
This commit is contained in:
2025-08-11 02:29:39 -04:00
commit d819457c77
10 changed files with 3320 additions and 0 deletions

60
BeeDie/ReadMe.txt Normal file
View File

@@ -0,0 +1,60 @@
This is a locally hosted version of Andy Lawton's Bee's Dice Roller (built on Teal's dice roller).
- Optomized for use as an OBS browser source
Setup:
1. In OBS use the included index.htm file as a file URL ( file:///[path] ). Do not check 'Local file'.
- Example URL:
file:///C:/Documents/LocalBeeDie/index.htm
2. Append the desired settings to the URL using a single question mark followed by params in the following format separated by ampersands:
paramname=value
- Example to make the dice red and automatically roll 1d20 on page load:
file:///C:/Documents/LocalBeeDie/index.htm?dicehex=aa293c&roll&d=1d20
3. Set the desired dice window size in OBS using the Width and Height settings.
4. Check 'Refresh browser when scene becomes active'.
5. Use chromahex param to set the background color and use an OBS filter on the source to chroma-key that color out.
- Example:
file:///C:/Documents/LocalBeeDie/index.htm?chromahex=00ff00&roll&d=1d20
6. Toggle broswer source off and on to roll dice.
Param Options:
Title paramname value example Description
Dice Color dicehex hexcode dicehex=ffffff Color of the dice body material.
Number Color labelhex hexcode labelhex=000000 Color of the numbers written on dice faces.
Dice Opacity transparency 0-1 transparency=0.5 Transparency of the dice material.
BG Color chromahex hexcode chromahex=00ff00 Window BG color.
Dice Scale dicescale .05-4 dicescale=2 Default scale is 1/10th the shortest window dimension
Dice Shadow shadows 0 or 1 shadows=0 1 for shadows, 0 for no shadows.
Output Detail resultdetail null resultdetail Enables output per die in the backend.
Output Total resulttoal null resulttotal Enables output SUM on backend.
Depreciated noresult null noresult Disables output.
Depreciated resulthex hexcode resulthex=000000 Result popup text color.
Depreciated resultbghex hexcode resultbghex=ffffff Result popup bg color.
Depreciated resultsize int resultsize=16 Result popup font size.
Auto Roll roll null roll Enables roll on page load.
Dice d (int)d(int) 1d20 Dice selection in format [INTdINT+INTdINT]
- extended dice notation example that rolls two 20 sided die, one 6 sided die, and two 4 sided die:
2d20+1d6+2d4
changelog:
v1:
- added workaround for an occasional that prevents dice from loading (detects error and refreshes the window after a few seconds)
- changed lighting for better readability
- optomized some redundant code
- new, more straightforward dice scaling method
- made result popup invisible to enable backend functionality without visual disruption
- this is a bad solution implemented during experimentation while learning what the code does.. I will probably be too lazy to fix it later

79
BeeDie/dice.css Normal file
View File

@@ -0,0 +1,79 @@
#info_div{
background-color: #00FF00;
}
.center_field {
position: absolute;
text-align: center;
height: 100%;
width: 100%;
}
#canvas {
background-color: #00FF00;
}
.center_field * {
position: relative;
font-family: Trebuchet MS;
background-color: rgba(255, 255, 255, 0.6);
padding: 5px 15px;
}
.center_field br {
background-color: rgba(0, 0, 0, 0);
}
.bottom_field {
position: absolute;
text-align: center;
bottom: 5px;
width: inherit;
padding: 0px;
}
#label {
font-size: 32pt;
word-spacing: 0.5em;
padding: 5px 15px;
color: rgba(21, 26, 26, 0.6);
top: 45%;
}
#labelhelp {
font-size: 12pt;
padding: 5px 15px;
color: rgba(21, 26, 26, 0.5);
bottom: 50px;
}
#set {
text-align: center;
font-size: 26pt;
border: none;
color: rgba(0, 0, 0, 0.8);
/*background-color: rgba(255, 255, 255, 0);*/
background-color: rgba(255, 255, 255, 0.6);
top: 60%;
}
#sethelp {
font-size: 12pt;
color: rgba(21, 26, 26, 0.5);
background: none;
top: 25%;
}
#selector_div button {
font-size: 20pt;
color: rgb(255, 255, 255);
background-color: rgba(0, 0, 0, 0.6);
cursor: pointer;
border: none;
width: 5em;
top: 62%;
}
.dice_place {
position: absolute;
border: solid black 1px;
}

51
BeeDie/index.htm Normal file
View File

@@ -0,0 +1,51 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xml:lang="en" lang="en" xmlns="http://www.w3.org/1999/xhtml" data-lt-installed="true"><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="keywords" content="DnD, dangeon and dragons, roleplay, dice, roller, 3D, RPG, wargame">
<meta name="description" content="Online 3D dice roller">
<title>Bee's dice roller - adaptation of teal's</title>
<style type="text/css">@import "main.css";</style>
<style type="text/css">@import "dice.css";</style>
<link rel="icon" type="image/x-ico" href="https://dice.bee.ac/favicon.ico">
</head>
<body style="margin: 0">
<div id="info_div" style="display: inline-block;">
<div class="center_field" id="label_bg">
<span id="label" style="color: rgba(0, 177, 64, 0); background-color: rgba(0, 177, 64, 0);">19</span>
</div>
<div class="center_field">
<!--<div class="bottom_field">
<span id="labelhelp">click to continue or tap and drag again</span>
</div>-->
</div>
</div>
<div id="selector_div" style="display: none">
<div class="center_field">
</div>
<div class="center_field">
<input type="text" id="set" value="1d20" style="width: 7ex;"><br>
<button id="clear">clear</button>
<button style="margin-left: 0.6em" id="throw">throw</button>
</div>
</div>
<div id="canvas" style="width: 100%; height: 100%;"></div>
<script src="index_files/three.min.js"></script>
<script src="index_files/cannon.min.js"></script>
<script type="text/javascript" src="index_files/teal.js"></script>
<script type="text/javascript" src="index_files/dice.js"></script>
<script type="text/javascript" src="index_files/main.js"></script>
<script type="text/javascript" defer="defer">
dice_initialize(document.body);
</script>
</body></html>

3
BeeDie/index_files/cannon.min.js vendored Normal file

File diff suppressed because one or more lines are too long

889
BeeDie/index_files/dice.js Normal file
View File

@@ -0,0 +1,889 @@
"use strict";
(function(dice) {
var random_storage = [];
this.use_true_random = true;
this.frame_rate = 1 / 60;
function prepare_rnd(callback) {
if (!random_storage.length && $t.dice.use_true_random) {
try {
$t.rpc({ method: "random", n: 512 },
function(random_responce) {
if (!random_responce.error)
random_storage = random_responce.result.random.data;
else $t.dice.use_true_random = false;
callback();
});
return;
}
catch (e) { $t.dice.use_true_random = false; }
}
callback();
}
function rnd() {
return random_storage.length ? random_storage.pop() : Math.random();
}
function create_shape(vertices, faces, radius) {
var cv = new Array(vertices.length), cf = new Array(faces.length);
for (var i = 0; i < vertices.length; ++i) {
var v = vertices[i];
cv[i] = new CANNON.Vec3(v.x * radius, v.y * radius, v.z * radius);
}
for (var i = 0; i < faces.length; ++i) {
cf[i] = faces[i].slice(0, faces[i].length - 1);
}
return new CANNON.ConvexPolyhedron(cv, cf);
}
function make_geom(vertices, faces, radius, tab, af) {
var geom = new THREE.Geometry();
for (var i = 0; i < vertices.length; ++i) {
var vertex = vertices[i].multiplyScalar(radius);
vertex.index = geom.vertices.push(vertex) - 1;
}
for (var i = 0; i < faces.length; ++i) {
var ii = faces[i], fl = ii.length - 1;
var aa = Math.PI * 2 / fl;
for (var j = 0; j < fl - 2; ++j) {
geom.faces.push(new THREE.Face3(ii[0], ii[j + 1], ii[j + 2], [geom.vertices[ii[0]],
geom.vertices[ii[j + 1]], geom.vertices[ii[j + 2]]], 0, ii[fl] + 1));
geom.faceVertexUvs[0].push([
new THREE.Vector2((Math.cos(af) + 1 + tab) / 2 / (1 + tab),
(Math.sin(af) + 1 + tab) / 2 / (1 + tab)),
new THREE.Vector2((Math.cos(aa * (j + 1) + af) + 1 + tab) / 2 / (1 + tab),
(Math.sin(aa * (j + 1) + af) + 1 + tab) / 2 / (1 + tab)),
new THREE.Vector2((Math.cos(aa * (j + 2) + af) + 1 + tab) / 2 / (1 + tab),
(Math.sin(aa * (j + 2) + af) + 1 + tab) / 2 / (1 + tab))]);
}
}
geom.computeFaceNormals();
geom.boundingSphere = new THREE.Sphere(new THREE.Vector3(), radius);
return geom;
}
function chamfer_geom(vectors, faces, chamfer) {
var chamfer_vectors = [], chamfer_faces = [], corner_faces = new Array(vectors.length);
for (var i = 0; i < vectors.length; ++i) corner_faces[i] = [];
for (var i = 0; i < faces.length; ++i) {
var ii = faces[i], fl = ii.length - 1;
var center_point = new THREE.Vector3();
var face = new Array(fl);
for (var j = 0; j < fl; ++j) {
var 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 (var j = 0; j < fl; ++j) {
var 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 (var i = 0; i < faces.length - 1; ++i) {
for (var j = i + 1; j < faces.length; ++j) {
var pairs = [], lastm = -1;
for (var m = 0; m < faces[i].length - 1; ++m) {
var 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 (var i = 0; i < corner_faces.length; ++i) {
var cf = corner_faces[i], face = [cf[0]], count = cf.length - 1;
while (count) {
for (var m = faces.length; m < chamfer_faces.length; ++m) {
var index = chamfer_faces[m].indexOf(face[face.length - 1]);
if (index >= 0 && index < 4) {
if (--index == -1) index = 3;
var 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 };
}
function create_geom(vertices, faces, radius, tab, af, chamfer) {
var vectors = new Array(vertices.length);
for (var i = 0; i < vertices.length; ++i) {
vectors[i] = (new THREE.Vector3).fromArray(vertices[i]).normalize();
}
var cg = chamfer_geom(vectors, faces, chamfer);
var geom = make_geom(cg.vectors, cg.faces, radius, tab, af);
//var geom = make_geom(vectors, faces, radius, tab, af); // Without chamfer
geom.cannon_shape = create_shape(vectors, faces, radius);
return geom;
}
this.standart_d20_dice_face_labels = [' ', '0', '1', '2', '3', '4', '5', '6', '7', '8',
'9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20'];
this.standart_d100_dice_face_labels = [' ', '00', '10', '20', '30', '40', '50',
'60', '70', '80', '90'];
function calc_texture_size(approx) {
return Math.pow(2, Math.floor(Math.log(approx) / Math.log(2)));
}
this.create_dice_materials = function(face_labels, size, margin) {
function create_text_texture(text, color, back_color) {
if (text == undefined) return null;
var canvas = document.createElement("canvas");
var context = canvas.getContext("2d");
var ts = calc_texture_size(size + size * 2 * margin) * 2;
canvas.width = canvas.height = ts;
context.font = ts / (1 + 2 * margin) + "pt Arial";
context.fillStyle = back_color;
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);
if (text == '6' || text == '9') {
context.fillText(' .', canvas.width / 2, canvas.height / 2);
}
var texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
return texture;
}
var materials = [];
for (var i = 0; i < face_labels.length; ++i)
materials.push(new THREE.MeshPhongMaterial($t.copyto(this.material_options,
{ map: create_text_texture(face_labels[i], this.label_color, this.dice_color),opacity: 0.5 })));
return materials;
}
var d4_labels = [
[[], [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.create_d4_materials = function(size, margin, labels) {
function create_d4_text(text, color, back_color) {
var canvas = document.createElement("canvas");
var context = canvas.getContext("2d");
var ts = calc_texture_size(size + margin) * 2;
canvas.width = canvas.height = ts;
context.font = (ts - margin) / 1.5 + "pt Arial";
context.fillStyle = back_color;
context.fillRect(0, 0, canvas.width, canvas.height);
context.textAlign = "center";
context.textBaseline = "middle";
context.fillStyle = color;
for (var 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);
}
var texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
return texture;
}
var materials = [];
for (var i = 0; i < labels.length; ++i)
materials.push(new THREE.MeshPhongMaterial($t.copyto(this.material_options,
{ map: create_d4_text(labels[i], this.label_color, this.dice_color)})));
return materials;
}
this.create_d4_geometry = function(radius) {
var vertices = [[1, 1, 1], [-1, -1, 1], [-1, 1, -1], [1, -1, -1]];
var faces = [[1, 0, 2, 1], [0, 1, 3, 2], [0, 3, 2, 3], [1, 2, 3, 4]];
return create_geom(vertices, faces, radius, -0.1, Math.PI * 7 / 6, 0.96);
}
this.create_d6_geometry = function(radius) {
var 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]];
var 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]];
return create_geom(vertices, faces, radius, 0.1, Math.PI / 4, 0.96);
}
this.create_d8_geometry = function(radius) {
var vertices = [[1, 0, 0], [-1, 0, 0], [0, 1, 0], [0, -1, 0], [0, 0, 1], [0, 0, -1]];
var 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]];
return create_geom(vertices, faces, radius, 0, -Math.PI / 4 / 2, 0.965);
}
this.create_d10_geometry = function(radius) {
var a = Math.PI * 2 / 10, k = Math.cos(a), h = 0.105, v = -1;
var vertices = [];
for (var i = 0, b = 0; i < 10; ++i, b += a)
vertices.push([Math.cos(b), Math.sin(b), h * (i % 2 ? 1 : -1)]);
vertices.push([0, 0, -1]); vertices.push([0, 0, 1]);
var 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, v], [1, 2, 3, v], [3, 2, 4, v], [3, 4, 5, v], [5, 4, 6, v],
[5, 6, 7, v], [7, 6, 8, v], [7, 8, 9, v], [9, 8, 0, v], [9, 0, 1, v]];
return create_geom(vertices, faces, radius, 0, Math.PI * 6 / 5, 0.945);
}
this.create_d12_geometry = function(radius) {
var p = (1 + Math.sqrt(5)) / 2, q = 1 / p;
var 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]];
var 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]];
return create_geom(vertices, faces, radius, 0.2, -Math.PI / 4 / 2, 0.968);
}
this.create_d20_geometry = function(radius) {
var t = (1 + Math.sqrt(5)) / 2;
var 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]];
var 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]];
return create_geom(vertices, faces, radius, -0.2, -Math.PI / 4 / 2, 0.955);
}
this.material_options = {
specular: 0x172022,
color: 0xf0f0f0,
shininess: 10,
shading: THREE.FlatShading,
};
this.label_color = '#aaaaaa';
this.dice_color = '#202020';
this.ambient_light_color = 0xcfd0d1;
this.spot_light_color = 0xd4ccc7;
this.selector_back_colors = { color: 0x00ff00, shininess: 0, emissive: 0x00ff00 };
this.desk_color = 0x00ff00;
this.use_shadows = true;
this.known_types = ['d4', 'd6', 'd8', 'd10', 'd12', 'd20', 'd100'];
this.dice_face_range = { 'd4': [1, 4], 'd6': [1, 6], 'd8': [1, 8], 'd10': [0, 9],
'd12': [1, 12], 'd20': [1, 20], 'd100': [0, 9] };
this.dice_mass = { 'd4': 300, 'd6': 300, 'd8': 340, 'd10': 350, 'd12': 350, 'd20': 400, 'd100': 350 };
this.dice_inertia = { 'd4': 5, 'd6': 13, 'd8': 10, 'd10': 9, 'd12': 8, 'd20': 6, 'd100': 9 };
this.scale = 50;
this.create_d4 = function() {
if (!this.d4_geometry) this.d4_geometry = this.create_d4_geometry(this.scale * 1.2);
if (!this.d4_material) this.d4_material = new THREE.MeshFaceMaterial(
this.create_d4_materials(this.scale / 2, this.scale * 2, d4_labels[0]));
return new THREE.Mesh(this.d4_geometry, this.d4_material);
}
this.create_d6 = function() {
if (!this.d6_geometry) this.d6_geometry = this.create_d6_geometry(this.scale * 0.9);
if (!this.dice_material) this.dice_material = new THREE.MeshFaceMaterial(
this.create_dice_materials(this.standart_d20_dice_face_labels, this.scale / 2, 1.0));
return new THREE.Mesh(this.d6_geometry, this.dice_material);
}
this.create_d8 = function() {
if (!this.d8_geometry) this.d8_geometry = this.create_d8_geometry(this.scale);
if (!this.dice_material) this.dice_material = new THREE.MeshFaceMaterial(
this.create_dice_materials(this.standart_d20_dice_face_labels, this.scale / 2, 1.2));
return new THREE.Mesh(this.d8_geometry, this.dice_material);
}
this.create_d10 = function() {
if (!this.d10_geometry) this.d10_geometry = this.create_d10_geometry(this.scale * 0.9);
if (!this.dice_material) this.dice_material = new THREE.MeshFaceMaterial(
this.create_dice_materials(this.standart_d20_dice_face_labels, this.scale / 2, 1.0));
return new THREE.Mesh(this.d10_geometry, this.dice_material);
}
this.create_d12 = function() {
if (!this.d12_geometry) this.d12_geometry = this.create_d12_geometry(this.scale * 0.9);
if (!this.dice_material) this.dice_material = new THREE.MeshFaceMaterial(
this.create_dice_materials(this.standart_d20_dice_face_labels, this.scale / 2, 1.0));
return new THREE.Mesh(this.d12_geometry, this.dice_material);
}
this.create_d20 = function() {
if (!this.d20_geometry) this.d20_geometry = this.create_d20_geometry(this.scale);
if (!this.dice_material) this.dice_material = new THREE.MeshFaceMaterial(
this.create_dice_materials(this.standart_d20_dice_face_labels, this.scale / 2, 1.0));
return new THREE.Mesh(this.d20_geometry, this.dice_material);
}
this.create_d100 = function() {
if (!this.d10_geometry) this.d10_geometry = this.create_d10_geometry(this.scale * 0.9);
if (!this.d100_material) this.d100_material = new THREE.MeshFaceMaterial(
this.create_dice_materials(this.standart_d100_dice_face_labels, this.scale / 2, 1.5));
return new THREE.Mesh(this.d10_geometry, this.d100_material);
}
this.parse_notation = function(notation) {
var no = notation.split('@');
var dr0 = /\s*(\d*)([a-z]+)(\d+)(\s*(\+|\-)\s*(\d+)){0,1}\s*(\+|$)/gi;
var dr1 = /(\b)*(\d+)(\b)*/gi;
var ret = { set: [], constant: 0, result: [], error: false }, res;
while (res = dr0.exec(no[0])) {
var command = res[2];
if (command != 'd') { ret.error = true; continue; }
var count = parseInt(res[1]);
if (res[1] == '') count = 1;
var type = 'd' + res[3];
if (this.known_types.indexOf(type) == -1) { ret.error = true; continue; }
while (count--) ret.set.push(type);
if (res[5] && res[6]) {
if (res[5] == '+') ret.constant += parseInt(res[6]);
else ret.constant -= parseInt(res[6]);
}
}
while (res = dr1.exec(no[1])) {
ret.result.push(parseInt(res[2]));
}
return ret;
}
this.stringify_notation = function(nn) {
var dict = {}, notation = '';
for (var i in nn.set)
if (!dict[nn.set[i]]) dict[nn.set[i]] = 1; else ++dict[nn.set[i]];
for (var i in dict) {
if (notation.length) notation += ' + ';
notation += (dict[i] > 1 ? dict[i] : '') + i;
}
if (nn.constant) {
if (nn.constant > 0) notation += ' + ' + nn.constant;
else notation += ' - ' + Math.abs(nn.constant);
}
return notation;
}
var that = this;
this.dice_box = function(container, dimentions, scale_change) {
this.use_adapvite_timestep = true;
this.animate_selector = true;
this.dices = [];
this.scene = new THREE.Scene();
this.world = new CANNON.World();
this.renderer = window.WebGLRenderingContext
? new THREE.WebGLRenderer({ antialias: true })
: new THREE.CanvasRenderer({ antialias: true });
container.appendChild(this.renderer.domElement);
this.renderer.shadowMap.enabled = true;
this.renderer.shadowMap.type = THREE.PCFShadowMap;
this.renderer.setClearColor(0xffffff, 1);
this.reinit(container, dimentions, scale_change);
this.world.gravity.set(0, 0, -9.8 * 800);
this.world.broadphase = new CANNON.NaiveBroadphase();
this.world.solver.iterations = 16;
var ambientLight = new THREE.AmbientLight(that.ambient_light_color);
this.scene.add(ambientLight);
this.dice_body_material = new CANNON.Material();
var desk_body_material = new CANNON.Material();
var barrier_body_material = new CANNON.Material();
this.world.addContactMaterial(new CANNON.ContactMaterial(
desk_body_material, this.dice_body_material, 0.01, 0.5));
this.world.addContactMaterial(new CANNON.ContactMaterial(
barrier_body_material, this.dice_body_material, 0, 1.0));
this.world.addContactMaterial(new CANNON.ContactMaterial(
this.dice_body_material, this.dice_body_material, 0, 0.5));
this.world.add(new CANNON.RigidBody(0, new CANNON.Plane(), desk_body_material));
var barrier;
barrier = new CANNON.RigidBody(0, new CANNON.Plane(), barrier_body_material);
barrier.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), Math.PI / 2);
barrier.position.set(0, this.h * 0.93, 0);
this.world.add(barrier);
barrier = new CANNON.RigidBody(0, new CANNON.Plane(), barrier_body_material);
barrier.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI / 2);
barrier.position.set(0, -this.h * 0.93, 0);
this.world.add(barrier);
barrier = new CANNON.RigidBody(0, new CANNON.Plane(), barrier_body_material);
barrier.quaternion.setFromAxisAngle(new CANNON.Vec3(0, 1, 0), -Math.PI / 2);
barrier.position.set(this.w * 0.93, 0, 0);
this.world.add(barrier);
barrier = new CANNON.RigidBody(0, new CANNON.Plane(), barrier_body_material);
barrier.quaternion.setFromAxisAngle(new CANNON.Vec3(0, 1, 0), Math.PI / 2);
barrier.position.set(-this.w * 0.93, 0, 0);
this.world.add(barrier);
this.last_time = 0;
this.running = false;
this.renderer.render(this.scene, this.camera);
}
this.dice_box.prototype.reinit = function(container, dimentions, scale_change) {
this.cw = container.clientWidth / 2;
this.ch = container.clientHeight / 2;
if (scale_change) {
this.scale_change = scale_change;
}
else {
this.scale_change = 1;
console.log(this.scale_change);
}
/*
if (dimentions) {
this.w = dimentions.w;
this.h = dimentions.h;
}
else {
this.w = this.cw;
this.h = this.ch;
}
*/
this.w = this.cw;
this.h = this.ch;
this.aspect = 1;
let sceneMax = Math.max(this.w, this.h);
let sceneMin = Math.min(this.w, this.h);
//this.aspect = Math.min(this.cw / this.w, this.ch / this.h);
//console.log("aspect: " + this.aspect);
//that.scale = (Math.sqrt(this.w * this.w + this.h * this.h) / 13)* this.scale_change;
//that.scale = Math.floor(this.aspect * 0.1) * this.scale_change;
that.scale = (sceneMin * .1) * this.scale_change;
//console.log("scale: " + that.scale);
this.renderer.setSize(this.cw * 2, this.ch * 2);
this.wh = this.ch / this.aspect / Math.tan(10 * Math.PI / 180);
if (this.camera) this.scene.remove(this.camera);
this.camera = new THREE.PerspectiveCamera(20, this.cw / this.ch, 1, this.wh * 1.3);
this.camera.position.z = this.wh;
//custom scene lighting
var mw = Math.max(this.w, this.h);
if (this.light) this.scene.remove(this.light);
this.light = new THREE.SpotLight(that.spot_light_color, .7);
this.light.position.set(0, 0, sceneMax * 4);
this.light.target.position.set(0, 0, 0);
this.light.angle = Math.PI/2;
this.light.distance = 0;
this.light.decay = 0;
this.light.castShadow = true;
this.light.shadowCameraNear = mw / 10;
this.light.shadowCameraFar = mw * 5;
this.light.shadowCameraFov = 50;
this.light.shadowBias = 0.001;
this.light.shadowDarkness = 1.1;
this.light.shadowMapWidth = 2048;
this.light.shadowMapHeight = 2048;
//original scene lighting
/*
var mw = Math.max(this.w, this.h);
if (this.light) this.scene.remove(this.light);
this.light = new THREE.SpotLight(that.spot_light_color, 0.7);
this.light.position.set(-mw / 2, mw / 2, mw * 2);
this.light.target.position.set(0, 0, 0);
this.light.distance = mw * 5;
this.light.angle = Math.PI/2;
this.light.distance = 0;
this.light.decay = 0;
this.light.castShadow = true;
this.light.shadowCameraNear = mw / 10;
this.light.shadowCameraFar = mw * 5;
this.light.shadowCameraFov = 50;
this.light.shadowBias = 0.001;
this.light.shadowDarkness = 1.1;
this.light.shadowMapWidth = 1024;
this.light.shadowMapHeight = 1024;
*/
// if (that.use_shadows) {this.scene.add(this.light)}
this.scene.add(this.light);
//add spotlight target (testing)
//this.scene.add(this.light.target);
if (this.desk) this.scene.remove(this.desk);
this.desk = new THREE.Mesh(new THREE.PlaneGeometry(this.w * 2, this.h * 2, 1, 1),
new THREE.MeshBasicMaterial({ color: that.desk_color}));
this.desk.receiveShadow = that.use_shadows;
this.scene.add(this.desk);
this.renderer.render(this.scene, this.camera);
}
function make_random_vector(vector) {
var random_angle = rnd() * Math.PI / 5 - Math.PI / 5 / 2;
var vec = {
x: vector.x * Math.cos(random_angle) - vector.y * Math.sin(random_angle),
y: vector.x * Math.sin(random_angle) + vector.y * Math.cos(random_angle)
};
if (vec.x == 0) vec.x = 0.01;
if (vec.y == 0) vec.y = 0.01;
return vec;
}
this.dice_box.prototype.generate_vectors = function(notation, vector, boost) {
var vectors = [];
for (var i in notation.set) {
var vec = make_random_vector(vector);
var pos = {
x: this.w * (vec.x > 0 ? -1 : 1) * 0.9,
y: this.h * (vec.y > 0 ? -1 : 1) * 0.9,
z: rnd() * 200 + 200
};
var projector = Math.abs(vec.x / vec.y);
if (projector > 1.0) pos.y /= projector; else pos.x *= projector;
var velvec = make_random_vector(vector);
var velocity = { x: velvec.x * boost, y: velvec.y * boost, z: -10 };
var inertia = that.dice_inertia[notation.set[i]];
var angle = {
x: -(rnd() * vec.y * 5 + inertia * vec.y),
y: rnd() * vec.x * 5 + inertia * vec.x,
z: 0
};
var axis = { x: rnd(), y: rnd(), z: rnd(), a: rnd() };
vectors.push({ set: notation.set[i], pos: pos, velocity: velocity, angle: angle, axis: axis });
}
return vectors;
}
this.dice_box.prototype.create_dice = function(type, pos, velocity, angle, axis) {
var dice = that['create_' + type]();
dice.castShadow = true;
dice.dice_type = type;
dice.body = new CANNON.RigidBody(that.dice_mass[type],
dice.geometry.cannon_shape, this.dice_body_material);
dice.body.position.set(pos.x, pos.y, pos.z);
dice.body.quaternion.setFromAxisAngle(new CANNON.Vec3(axis.x, axis.y, axis.z), axis.a * Math.PI * 2);
dice.body.angularVelocity.set(angle.x, angle.y, angle.z);
dice.body.velocity.set(velocity.x, velocity.y, velocity.z);
dice.body.linearDamping = 0.1;
dice.body.angularDamping = 0.1;
this.scene.add(dice);
this.dices.push(dice);
this.world.add(dice.body);
}
this.dice_box.prototype.check_if_throw_finished = function() {
var res = true;
var e = 6;
if (this.iteration < 10 / that.frame_rate) {
for (var i = 0; i < this.dices.length; ++i) {
var dice = this.dices[i];
if (dice.dice_stopped === true) continue;
var a = dice.body.angularVelocity, v = dice.body.velocity;
if (Math.abs(a.x) < e && Math.abs(a.y) < e && Math.abs(a.z) < e &&
Math.abs(v.x) < e && Math.abs(v.y) < e && Math.abs(v.z) < e) {
if (dice.dice_stopped) {
if (this.iteration - dice.dice_stopped > 3) {
dice.dice_stopped = true;
continue;
}
}
else dice.dice_stopped = this.iteration;
res = false;
}
else {
dice.dice_stopped = undefined;
res = false;
}
}
}
return res;
}
function get_dice_value(dice) {
var vector = new THREE.Vector3(0, 0, dice.dice_type == 'd4' ? -1 : 1);
var closest_face, closest_angle = Math.PI * 2;
for (var i = 0, l = dice.geometry.faces.length; i < l; ++i) {
var face = dice.geometry.faces[i];
if (face.materialIndex == 0) continue;
var angle = face.normal.clone().applyQuaternion(dice.body.quaternion).angleTo(vector);
if (angle < closest_angle) {
closest_angle = angle;
closest_face = face;
}
}
if (closest_face){
var matindex = closest_face.materialIndex - 1;
}
else {
/*
console.log("undefined closest face: reloading in 3.. 2.. 1..");
setTimeout(function() {
window.location.reload()
}, 3000);
*/
window.location.reload()
}
if (dice.dice_type == 'd100') matindex *= 10;
if (dice.dice_type == 'd10' && matindex == 0) matindex = 10;
return matindex;
}
function get_dice_values(dices) {
var values = [];
for (var i = 0, l = dices.length; i < l; ++i) {
values.push(get_dice_value(dices[i]));
}
return values;
}
this.dice_box.prototype.emulate_throw = function() {
while (!this.check_if_throw_finished()) {
++this.iteration;
this.world.step(that.frame_rate);
}
return get_dice_values(this.dices);
}
this.dice_box.prototype.__animate = function(threadid) {
var time = (new Date()).getTime();
var time_diff = (time - this.last_time) / 1000;
if (time_diff > 3) time_diff = that.frame_rate;
++this.iteration;
if (this.use_adapvite_timestep) {
while (time_diff > that.frame_rate * 1.1) {
this.world.step(that.frame_rate);
time_diff -= that.frame_rate;
}
this.world.step(time_diff);
}
else {
this.world.step(that.frame_rate);
}
for (var i in this.scene.children) {
var interact = this.scene.children[i];
if (interact.body != undefined) {
interact.position.copy(interact.body.position);
interact.quaternion.copy(interact.body.quaternion);
}
}
this.renderer.render(this.scene, this.camera);
this.last_time = this.last_time ? time : (new Date()).getTime();
if (this.running == threadid && this.check_if_throw_finished()) {
this.running = false;
if (this.callback) this.callback.call(this, get_dice_values(this.dices));
}
if (this.running == threadid) {
(function(t, tid, uat) {
if (!uat && time_diff < that.frame_rate) {
setTimeout(function() { requestAnimationFrame(function() { t.__animate(tid); }); },
(that.frame_rate - time_diff) * 1000);
}
else requestAnimationFrame(function() { t.__animate(tid); });
})(this, threadid, this.use_adapvite_timestep);
}
}
this.dice_box.prototype.clear = function() {
this.running = false;
var dice;
while (dice = this.dices.pop()) {
this.scene.remove(dice);
if (dice.body) this.world.remove(dice.body);
}
if (this.pane) this.scene.remove(this.pane);
this.renderer.render(this.scene, this.camera);
var box = this;
setTimeout(function() { box.renderer.render(box.scene, box.camera); }, 100);
}
this.dice_box.prototype.prepare_dices_for_roll = function(vectors) {
this.clear();
this.iteration = 0;
for (var i in vectors) {
this.create_dice(vectors[i].set, vectors[i].pos, vectors[i].velocity,
vectors[i].angle, vectors[i].axis);
}
}
function shift_dice_faces(dice, value, res) {
var r = that.dice_face_range[dice.dice_type];
if (dice.dice_type == 'd10' && value == 10) value = 0;
if (dice.dice_type == 'd10' && res == 10) res = 0;
if (dice.dice_type == 'd100') res /= 10;
if (!(value >= r[0] && value <= r[1])) return;
var num = value - res;
var geom = dice.geometry.clone();
for (var i = 0, l = geom.faces.length; i < l; ++i) {
var matindex = geom.faces[i].materialIndex;
if (matindex == 0) continue;
matindex += num - 1;
while (matindex > r[1]) matindex -= r[1];
while (matindex < r[0]) matindex += r[1];
geom.faces[i].materialIndex = matindex + 1;
}
if (dice.dice_type == 'd4' && num != 0) {
if (num < 0) num += 4;
dice.material = new THREE.MeshFaceMaterial(
that.create_d4_materials(that.scale / 2, that.scale * 2, d4_labels[num]));
}
dice.geometry = geom;
}
this.dice_box.prototype.roll = function(vectors, values, callback) {
this.prepare_dices_for_roll(vectors);
if (values != undefined && values.length) {
this.use_adapvite_timestep = false;
var res = this.emulate_throw();
this.prepare_dices_for_roll(vectors);
for (var i in res)
shift_dice_faces(this.dices[i], values[i], res[i]);
}
this.callback = callback;
this.running = (new Date()).getTime();
this.last_time = 0;
this.__animate(this.running);
}
this.dice_box.prototype.__selector_animate = function(threadid) {
var time = (new Date()).getTime();
var time_diff = (time - this.last_time) / 1000;
if (time_diff > 3) time_diff = that.frame_rate;
var angle_change = 0.3 * time_diff * Math.PI * Math.min(24000 + threadid - time, 6000) / 6000;
if (angle_change < 0) this.running = false;
for (var i in this.dices) {
this.dices[i].rotation.y += angle_change;
this.dices[i].rotation.x += angle_change / 4;
this.dices[i].rotation.z += angle_change / 10;
}
this.last_time = time;
this.renderer.render(this.scene, this.camera);
if (this.running == threadid) {
(function(t, tid) {
requestAnimationFrame(function() { t.__selector_animate(tid); });
})(this, threadid);
}
}
this.dice_box.prototype.search_dice_by_mouse = function(ev) {
var m = $t.get_mouse_coords(ev);
var intersects = (new THREE.Raycaster(this.camera.position,
(new THREE.Vector3((m.x - this.cw) / this.aspect,
1 - (m.y - this.ch) / this.aspect, this.w / 9))
.sub(this.camera.position).normalize())).intersectObjects(this.dices);
if (intersects.length) return intersects[0].object.userData;
}
this.dice_box.prototype.draw_selector = function() {
this.clear();
var step = this.w / 4.5;
this.pane = new THREE.Mesh(new THREE.PlaneGeometry(this.w * 6, this.h * 6, 1, 1),
new THREE.MeshPhongMaterial(that.selector_back_colors));
this.pane.receiveShadow = true;
this.pane.position.set(0, 0, 1);
this.scene.add(this.pane);
var mouse_captured = false;
for (var i = 0, pos = -3; i < that.known_types.length; ++i, ++pos) {
var dice = $t.dice['create_' + that.known_types[i]]();
dice.position.set(pos * step, 0, step * 0.5);
dice.castShadow = true;
dice.userData = that.known_types[i];
this.dices.push(dice); this.scene.add(dice);
}
this.running = (new Date()).getTime();
this.last_time = 0;
if (this.animate_selector) this.__selector_animate(this.running);
else this.renderer.render(this.scene, this.camera);
}
function throw_dices(box, vector, boost, dist, notation_getter, before_roll, after_roll) {
var uat = $t.dice.use_adapvite_timestep;
function roll(request_results) {
if (after_roll) {
box.clear();
box.roll(vectors, request_results || notation.result, function(result) {
if (after_roll) after_roll.call(box, notation, result);
box.rolling = false;
$t.dice.use_adapvite_timestep = uat;
});
}
}
vector.x /= dist; vector.y /= dist;
var notation = notation_getter.call(box);
if (notation.set.length == 0) return;
var vectors = box.generate_vectors(notation, vector, boost);
box.rolling = true;
if (before_roll) before_roll.call(box, vectors, notation, roll);
else roll();
}
this.dice_box.prototype.bind_mouse = function(container, notation_getter, before_roll, after_roll) {
var box = this;
$t.bind(container, ['mousedown', 'touchstart'], function(ev) {
ev.preventDefault();
box.mouse_time = (new Date()).getTime();
box.mouse_start = $t.get_mouse_coords(ev);
});
$t.bind(container, ['mouseup', 'touchend'], function(ev) {
if (box.rolling) return;
if (box.mouse_start == undefined) return;
ev.stopPropagation();
var m = $t.get_mouse_coords(ev);
var vector = { x: m.x - box.mouse_start.x, y: -(m.y - box.mouse_start.y) };
box.mouse_start = undefined;
var dist = Math.sqrt(vector.x * vector.x + vector.y * vector.y);
if (dist < Math.sqrt(box.w * box.h * 0.01)) return;
var time_int = (new Date()).getTime() - box.mouse_time;
if (time_int > 2000) time_int = 2000;
var boost = Math.sqrt((2500 - time_int) / 2500) * dist * 2;
prepare_rnd(function() {
throw_dices(box, vector, boost, dist, notation_getter, before_roll, after_roll);
});
});
}
this.dice_box.prototype.bind_throw = function(button, notation_getter, before_roll, after_roll) {
var box = this;
$t.bind(button, ['mouseup', 'touchend'], function(ev) {
ev.stopPropagation();
box.start_throw(notation_getter, before_roll, after_roll);
});
}
this.dice_box.prototype.start_throw = function(notation_getter, before_roll, after_roll) {
var box = this;
if (box.rolling) return;
prepare_rnd(function() {
var vector = { x: (rnd() * 2 - 1) * box.w, y: -(rnd() * 2 - 1) * box.h };
var dist = Math.sqrt(vector.x * vector.x + vector.y * vector.y);
var boost = (rnd() + 3) * dist;
throw_dices(box, vector, boost, dist, notation_getter, before_roll, after_roll);
});
}
}).apply(teal.dice = teal.dice || {});

239
BeeDie/index_files/main.js Normal file
View File

@@ -0,0 +1,239 @@
"use strict";
function dice_initialize(container) {
$t.remove($t.id('loading_text'));
var canvas = $t.id('canvas');
canvas.style.width = window.innerWidth - 1 + 'px';
canvas.style.height = window.innerHeight - 1 + 'px';
var label = $t.id('label');
var set = $t.id('set');
var selector_div = $t.id('selector_div');
var info_div = $t.id('info_div');
on_set_change();
$t.dice.use_true_random = false;
function on_set_change(ev) { set.style.width = set.value.length + 3 + 'ex'; }
$t.bind(set, 'keyup', on_set_change);
$t.bind(set, 'mousedown', function(ev) { ev.stopPropagation(); });
$t.bind(set, 'mouseup', function(ev) { ev.stopPropagation(); });
$t.bind(set, 'focus', function(ev) { $t.set(container, { class: '' }); });
$t.bind(set, 'blur', function(ev) { $t.set(container, { class: 'noselect' }); });
$t.bind($t.id('clear'), ['mouseup', 'touchend'], function(ev) {
ev.stopPropagation();
set.value = '0';
on_set_change();
});
var params = $t.get_url_params();
$t.dice.desk_color = 0x00ff00;
info_div.style.display = 'none';
$t.dice.dice_color = '#65008a';
$t.dice.label_color = '#b58b00';
if (params.chromakey) {
$t.dice.desk_color = 0x00ff00;
info_div.style.display = 'none';
$t.id('control_panel').style.display = 'none';
}
if (params.shadows == 0) {
$t.dice.use_shadows = false;
}
if (params.color == 'white') {
$t.dice.dice_color = '#808080';
$t.dice.label_color = '#202020';
}
if (params.dicecolor) {
$t.dice.dice_color = params.dicecolor;
}
if (params.labelcolor) {
$t.dice.label_color = params.labelcolor;
}
if (params.dicehex) {
$t.dice.dice_color = '#' + params.dicehex;
}
if (params.labelhex) {
$t.dice.label_color = '#' + params.labelhex;
}
if (params.dicecolorhex) {
$t.dice.dice_color = '#' + params.dicecolorhex;
}
if (params.labelcolorhex) {
$t.dice.label_color = '#' + params.labelcolorhex;
}
if (params.chromahex) {
$t.dice.desk_color = '#' + params.chromahex;
$t.dice.selector_back_colors = { color: '#' + params.chromahex, shininess: 0, emissive: '#' + params.chromahex};
}
if (params.transparency){
$t.dice.material_options = {
specular: 0x172022,
color: 0xf0f0f0,
shininess: 10,
shading: THREE.FlatShading,
transparent: true,
opacity: params.transparency,
//side: THREE.DoubleSide,
};
} else {
$t.dice.material_options = {
specular: 0x172022,
color: 0xf0f0f0,
shininess: 10,
shading: THREE.FlatShading,
transparent: true,
opacity: 1,
//side: THREE.DoubleSide,
};
}
if (!params.w) {
params.w = 500;
}
if (!params.h) {
params.h = 300;
}
if (!params.dicescale) {
params.dicescale = 1;
}
else {
if (params.dicescale > 4) {
params.dicescale = 4
} else if (params.dicescale < 0.05) {
params.dicescale = 0.05
}
}
console.log(params.dicescale);
var box = new $t.dice.dice_box(canvas, { w: params.w, h: params.h}, params.dicescale);
box.animate_selector = false;
$t.bind(window, 'resize', function() {
canvas.style.width = window.innerWidth - 1 + 'px';
canvas.style.height = window.innerHeight - 1 + 'px';
// box.reinit(canvas, { w: 500, h: 300 });
});
function show_selector() {
info_div.style.display = 'none';
selector_div.style.display = 'inline-block';
box.draw_selector();
}
function before_roll(vectors, notation, callback) {
info_div.style.display = 'none';
selector_div.style.display = 'none';
// do here rpc call or whatever to get your own result of throw.
// then callback with array of your result, example:
// callback([2, 2, 2, 2]); // for 4d6 where all dice values are 2.
callback();
}
function notation_getter() {
return $t.dice.parse_notation(set.value);
}
function after_roll(notation, result) {
if (params.chromakey || params.noresult) return;
var result_total = 0;
if (params.advantage) {
result_total = Math.max(...result);
}
else if (params.disadvantage) {
result_total = Math.min(...result);
}
else {
result_total = result.reduce(function(s, a) { return s + a; });
}
// Add in constant
result_total += notation.constant;
console.log(notation, result);
let res = ' ';
if (params.resultdetail){
for (let i = 0; i < notation.set.length; i++) {
res += notation.set[i] + "=" + result[i] + ' ';
}
if (notation.constant) {
if (notation.constant > 0) res += ' +' + notation.constant + ' ';
else res += ' -' + Math.abs(notation.constant) + ' ';
}
if (params.advantage) {
res += "Total(Advantage): ";
}
else if (params.disadvantage) {
res += "Total(Disadvantage): ";
}
else {
res += "Total: ";
}
//res += "Total: ";
res += result_total;
}else if (params.resulttotal){
res += result_total;
}
else{
res = result.join(' ');
if (notation.constant) {
if (notation.constant > 0) res += ' +' + notation.constant;
else res += ' -' + Math.abs(notation.constant);
}
if (result.length > 1) res += ' = ' + result_total;
}
label.innerHTML = res;
if (params.resulthex) {
label.style.color = '#' + params.resulthex;
}
if (params.resultbghex) {
label.style.backgroundColor = '#' + params.resultbghex;
}
if (params.resultsize) {
label.style.fontSize = params.resultsize + 'px';
}
info_div.style.display = 'inline-block';
}
box.bind_mouse(container, notation_getter, before_roll, after_roll);
box.bind_throw($t.id('throw'), notation_getter, before_roll, after_roll);
$t.bind(container, ['mouseup', 'touchend'], function(ev) {
ev.stopPropagation();
if (selector_div.style.display == 'none') {
if (!box.rolling) show_selector();
box.rolling = false;
return;
}
var name = box.search_dice_by_mouse(ev);
if (name != undefined) {
var notation = $t.dice.parse_notation(set.value);
notation.set.push(name);
set.value = $t.dice.stringify_notation(notation);
on_set_change();
}
});
if (params.notation) {
set.value = params.notation;
} else if (params.d) {
set.value = params.d;
}
if (params.roll) {
$t.raise_event($t.id('throw'), 'mouseup');
}
else {
show_selector();
}
}

223
BeeDie/index_files/teal.js Normal file
View File

@@ -0,0 +1,223 @@
"use strict";
window.teal = {};
window.$t = window.teal;
teal.copyto = function(obj, res) {
if (obj == null || typeof obj !== 'object') return obj;
if (obj instanceof Array) {
for (var i = obj.length - 1; i >= 0; --i)
res[i] = $t.copy(obj[i]);
}
else {
for (var i in obj) {
if (obj.hasOwnProperty(i))
res[i] = $t.copy(obj[i]);
}
}
return res;
}
teal.copy = function(obj) {
if (!obj) return obj;
return teal.copyto(obj, new obj.constructor());
}
teal.element = function(name, props, place, content) {
var dom = document.createElement(name);
if (props) for (var i in props) dom.setAttribute(i, props[i]);
if (place) place.appendChild(dom);
if (content !== undefined) $t.inner(content, dom);
return dom;
}
teal.inner = function(obj, sel) {
sel.appendChild(obj.nodeName != undefined ? obj : document.createTextNode(obj));
return sel;
}
teal.id = function(id) {
return document.getElementById(id);
}
teal.set = function(sel, props) {
for (var i in props) sel.setAttribute(i, props[i]);
return sel;
}
teal.clas = function(sel, oldclass, newclass) {
var oc = oldclass ? oldclass.split(/\s+/) : [],
nc = newclass ? newclass.split(/\s+/) : [],
classes = (sel.getAttribute('class') || '').split(/\s+/);
if (!classes[0]) classes = [];
for (var i in oc) {
var ind = classes.indexOf(oc[i]);
if (ind >= 0) classes.splice(ind, 1);
}
for (var i in nc) {
if (nc[i] && classes.indexOf(nc[i]) < 0) classes.push(nc[i]);
}
sel.setAttribute('class', classes.join(' '));
}
teal.empty = function(sel) {
if (sel.childNodes)
while (sel.childNodes.length)
sel.removeChild(sel.firstChild);
}
teal.remove = function(sel) {
if (sel) {
if (sel.parentNode) sel.parentNode.removeChild(sel);
else for (var i = sel.length - 1; i >= 0; --i)
sel[i].parentNode.removeChild(sel[i]);
}
}
teal.bind = function(sel, eventname, func, bubble) {
if (!sel) return;
if (eventname.constructor === Array) {
for (var i in eventname)
sel.addEventListener(eventname[i], func, bubble ? bubble : false);
}
else
sel.addEventListener(eventname, func, bubble ? bubble : false);
}
teal.unbind = function(sel, eventname, func, bubble) {
if (eventname.constructor === Array) {
for (var i in eventname)
sel.removeEventListener(eventname[i], func, bubble ? bubble : false);
}
else
sel.removeEventListener(eventname, func, bubble ? bubble : false);
}
teal.one = function(sel, eventname, func, bubble) {
var one_func = function(e) {
func.call(this, e);
teal.unbind(sel, eventname, one_func, bubble);
};
teal.bind(sel, eventname, one_func, bubble);
}
teal.raise_event = function(sel, eventname, bubble, cancelable) {
var evt = document.createEvent('UIEvents');
evt.initEvent(eventname, bubble == undefined ? true : bubble,
cancelable == undefined ? true : cancelable);
sel.dispatchEvent(evt);
}
teal.raise = function(sel, eventname, params, bubble, cancelable) {
var ev = document.createEvent("CustomEvent");
ev.initCustomEvent(eventname, bubble, cancelable, params);
sel.dispatchEvent(ev);
}
if (!document.getElementsByClassName) {
teal.get_elements_by_class = function(classes, node) {
var node = node || document,
list = node.getElementsByTagName('*'),
cl = classes.split(/\s+/),
result = [];
for (var i = list.length - 1; i >= 0; --i) {
for (var j = cl.length - 1; j >= 0; --j) {
var clas = list[i].getAttribute('class');
if (clas && clas.search('\\b' + cl[j] + '\\b') != -1) {
result.push(list[i]);
break;
}
}
}
return result;
}
}
else {
teal.get_elements_by_class = function(classes, node) {
return (node || document).getElementsByClassName(classes);
}
}
teal.rpc = function(params, callback, noparse) {
var ajax = new XMLHttpRequest();
ajax.open("post", 'f', true);
ajax.onreadystatechange = function() {
if (ajax.readyState == 4)
callback.call(ajax, noparse ? ajax.responseText : JSON.parse(ajax.responseText));
};
ajax.send(JSON.stringify(params));
}
teal.uuid = function() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
teal.get_url_params = function() {
var params = window.location.search.substring(1).split("&");
var res = {};
for (var i in params) {
var keyvalue = params[i].split("=");
res[keyvalue[0]] = decodeURI(keyvalue[1]);
}
return res;
}
teal.get_mouse_coords = function(ev) {
var touches = ev.changedTouches;
if (touches) return { x: touches[0].clientX, y: touches[0].clientY };
return { x: ev.clientX, y: ev.clientY };
}
teal.deferred = function() {
var solved = false, callbacks = [], args = [];
function solve() {
while (callbacks.length) {
callbacks.shift().apply(this, args);
}
}
return {
promise: function() {
return {
then: function(callback) {
var deferred = teal.deferred(), promise = deferred.promise();
callbacks.push(function() {
var res = callback.apply(this, arguments);
if (res && 'done' in res) res.done(deferred.resolve);
else deferred.resolve.apply(this, arguments);
});
return promise;
},
done: function(callback) {
callbacks.push(callback);
if (solved) solve();
return this;
},
cancel: function() {
callbacks = [];
}
};
},
resolve: function() {
solved = true;
args = Array.prototype.slice.call(arguments, 0);
solve();
}
};
}
teal.when = function(promises) {
var deferred = teal.deferred();
var count = promises.length, ind = 0;
if (count == 0) deferred.resolve();
for (var i = 0; i < count; ++i) {
promises[i].done(function() {
if (++ind == count) deferred.resolve();
});
}
return deferred.promise();
}

870
BeeDie/index_files/three.min.js vendored Normal file

File diff suppressed because one or more lines are too long

50
BeeDie/main.css Normal file
View File

@@ -0,0 +1,50 @@
#svg *, .svg * {
-moz-user-select: none;
-webkit-user-select: none;
-khtml-user-select: none;
-o-user-select: none;
user-select: none;
}
#waitform {
position: absolute;
width: 100%;
height: 100%;
z-index: 10000;
cursor: wait;
}
a {
color: gray;
}
em {
border: 1px rgba(0, 0, 0, 0.2) solid;
font-style: normal;
padding: 0px 3px;
background-color: rgba(0, 0, 0, 0.08);
border-radius: 3px;
}
body {
font-family: Georgia;
}
h6 {
font-size: 100%;
font-weight: normal;
margin: 0px;
}
p {
font-size: 80%;
margin-top: 5px;
margin-bottom: 0px;
}
.control_panel {
padding: 15px;
padding-bottom: 0px;
position: absolute;
z-index: 10;
}

856
BeeDie/temp/dice.js Normal file
View File

@@ -0,0 +1,856 @@
"use strict";
(function(dice) {
var random_storage = [];
this.use_true_random = true;
this.frame_rate = 1 / 60;
function prepare_rnd(callback) {
if (!random_storage.length && $t.dice.use_true_random) {
try {
$t.rpc({ method: "random", n: 512 },
function(random_responce) {
if (!random_responce.error)
random_storage = random_responce.result.random.data;
else $t.dice.use_true_random = false;
callback();
});
return;
}
catch (e) { $t.dice.use_true_random = false; }
}
callback();
}
function rnd() {
return random_storage.length ? random_storage.pop() : Math.random();
}
function create_shape(vertices, faces, radius) {
var cv = new Array(vertices.length), cf = new Array(faces.length);
for (var i = 0; i < vertices.length; ++i) {
var v = vertices[i];
cv[i] = new CANNON.Vec3(v.x * radius, v.y * radius, v.z * radius);
}
for (var i = 0; i < faces.length; ++i) {
cf[i] = faces[i].slice(0, faces[i].length - 1);
}
return new CANNON.ConvexPolyhedron(cv, cf);
}
function make_geom(vertices, faces, radius, tab, af) {
var geom = new THREE.Geometry();
for (var i = 0; i < vertices.length; ++i) {
var vertex = vertices[i].multiplyScalar(radius);
vertex.index = geom.vertices.push(vertex) - 1;
}
for (var i = 0; i < faces.length; ++i) {
var ii = faces[i], fl = ii.length - 1;
var aa = Math.PI * 2 / fl;
for (var j = 0; j < fl - 2; ++j) {
geom.faces.push(new THREE.Face3(ii[0], ii[j + 1], ii[j + 2], [geom.vertices[ii[0]],
geom.vertices[ii[j + 1]], geom.vertices[ii[j + 2]]], 0, ii[fl] + 1));
geom.faceVertexUvs[0].push([
new THREE.Vector2((Math.cos(af) + 1 + tab) / 2 / (1 + tab),
(Math.sin(af) + 1 + tab) / 2 / (1 + tab)),
new THREE.Vector2((Math.cos(aa * (j + 1) + af) + 1 + tab) / 2 / (1 + tab),
(Math.sin(aa * (j + 1) + af) + 1 + tab) / 2 / (1 + tab)),
new THREE.Vector2((Math.cos(aa * (j + 2) + af) + 1 + tab) / 2 / (1 + tab),
(Math.sin(aa * (j + 2) + af) + 1 + tab) / 2 / (1 + tab))]);
}
}
geom.computeFaceNormals();
geom.boundingSphere = new THREE.Sphere(new THREE.Vector3(), radius);
return geom;
}
function chamfer_geom(vectors, faces, chamfer) {
var chamfer_vectors = [], chamfer_faces = [], corner_faces = new Array(vectors.length);
for (var i = 0; i < vectors.length; ++i) corner_faces[i] = [];
for (var i = 0; i < faces.length; ++i) {
var ii = faces[i], fl = ii.length - 1;
var center_point = new THREE.Vector3();
var face = new Array(fl);
for (var j = 0; j < fl; ++j) {
var 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 (var j = 0; j < fl; ++j) {
var 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 (var i = 0; i < faces.length - 1; ++i) {
for (var j = i + 1; j < faces.length; ++j) {
var pairs = [], lastm = -1;
for (var m = 0; m < faces[i].length - 1; ++m) {
var 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 (var i = 0; i < corner_faces.length; ++i) {
var cf = corner_faces[i], face = [cf[0]], count = cf.length - 1;
while (count) {
for (var m = faces.length; m < chamfer_faces.length; ++m) {
var index = chamfer_faces[m].indexOf(face[face.length - 1]);
if (index >= 0 && index < 4) {
if (--index == -1) index = 3;
var 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 };
}
function create_geom(vertices, faces, radius, tab, af, chamfer) {
var vectors = new Array(vertices.length);
for (var i = 0; i < vertices.length; ++i) {
vectors[i] = (new THREE.Vector3).fromArray(vertices[i]).normalize();
}
var cg = chamfer_geom(vectors, faces, chamfer);
var geom = make_geom(cg.vectors, cg.faces, radius, tab, af);
//var geom = make_geom(vectors, faces, radius, tab, af); // Without chamfer
geom.cannon_shape = create_shape(vectors, faces, radius);
return geom;
}
this.standart_d20_dice_face_labels = [' ', '0', '1', '2', '3', '4', '5', '6', '7', '8',
'9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20'];
this.standart_d100_dice_face_labels = [' ', '00', '10', '20', '30', '40', '50',
'60', '70', '80', '90'];
function calc_texture_size(approx) {
return Math.pow(2, Math.floor(Math.log(approx) / Math.log(2)));
}
this.create_dice_materials = function(face_labels, size, margin) {
function create_text_texture(text, color, back_color) {
if (text == undefined) return null;
var canvas = document.createElement("canvas");
var context = canvas.getContext("2d");
var ts = calc_texture_size(size + size * 2 * margin) * 2;
canvas.width = canvas.height = ts;
context.font = ts / (1 + 2 * margin) + "pt Arial";
context.fillStyle = back_color;
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);
if (text == '6' || text == '9') {
context.fillText(' .', canvas.width / 2, canvas.height / 2);
}
var texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
return texture;
}
var materials = [];
for (var i = 0; i < face_labels.length; ++i)
materials.push(new THREE.MeshPhongMaterial($t.copyto(this.material_options,
{ map: create_text_texture(face_labels[i], this.label_color, this.dice_color),opacity: 0.5 })));
return materials;
}
var d4_labels = [
[[], [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.create_d4_materials = function(size, margin, labels) {
function create_d4_text(text, color, back_color) {
var canvas = document.createElement("canvas");
var context = canvas.getContext("2d");
var ts = calc_texture_size(size + margin) * 2;
canvas.width = canvas.height = ts;
context.font = (ts - margin) / 1.5 + "pt Arial";
context.fillStyle = back_color;
context.fillRect(0, 0, canvas.width, canvas.height);
context.textAlign = "center";
context.textBaseline = "middle";
context.fillStyle = color;
for (var 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);
}
var texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
return texture;
}
var materials = [];
for (var i = 0; i < labels.length; ++i)
materials.push(new THREE.MeshPhongMaterial($t.copyto(this.material_options,
{ map: create_d4_text(labels[i], this.label_color, this.dice_color)})));
return materials;
}
this.create_d4_geometry = function(radius) {
var vertices = [[1, 1, 1], [-1, -1, 1], [-1, 1, -1], [1, -1, -1]];
var faces = [[1, 0, 2, 1], [0, 1, 3, 2], [0, 3, 2, 3], [1, 2, 3, 4]];
return create_geom(vertices, faces, radius, -0.1, Math.PI * 7 / 6, 0.96);
}
this.create_d6_geometry = function(radius) {
var 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]];
var 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]];
return create_geom(vertices, faces, radius, 0.1, Math.PI / 4, 0.96);
}
this.create_d8_geometry = function(radius) {
var vertices = [[1, 0, 0], [-1, 0, 0], [0, 1, 0], [0, -1, 0], [0, 0, 1], [0, 0, -1]];
var 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]];
return create_geom(vertices, faces, radius, 0, -Math.PI / 4 / 2, 0.965);
}
this.create_d10_geometry = function(radius) {
var a = Math.PI * 2 / 10, k = Math.cos(a), h = 0.105, v = -1;
var vertices = [];
for (var i = 0, b = 0; i < 10; ++i, b += a)
vertices.push([Math.cos(b), Math.sin(b), h * (i % 2 ? 1 : -1)]);
vertices.push([0, 0, -1]); vertices.push([0, 0, 1]);
var 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, v], [1, 2, 3, v], [3, 2, 4, v], [3, 4, 5, v], [5, 4, 6, v],
[5, 6, 7, v], [7, 6, 8, v], [7, 8, 9, v], [9, 8, 0, v], [9, 0, 1, v]];
return create_geom(vertices, faces, radius, 0, Math.PI * 6 / 5, 0.945);
}
this.create_d12_geometry = function(radius) {
var p = (1 + Math.sqrt(5)) / 2, q = 1 / p;
var 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]];
var 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]];
return create_geom(vertices, faces, radius, 0.2, -Math.PI / 4 / 2, 0.968);
}
this.create_d20_geometry = function(radius) {
var t = (1 + Math.sqrt(5)) / 2;
var 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]];
var 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]];
return create_geom(vertices, faces, radius, -0.2, -Math.PI / 4 / 2, 0.955);
}
this.material_options = {
specular: 0x172022,
color: 0xf0f0f0,
shininess: 10,
shading: THREE.FlatShading,
};
this.label_color = '#aaaaaa';
this.dice_color = '#202020';
this.ambient_light_color = 0xcfd0d1;
this.spot_light_color = 0xd4ccc7;
this.selector_back_colors = { color: 0x00ff00, shininess: 0, emissive: 0x00ff00 };
this.desk_color = 0x00ff00;
this.use_shadows = true;
this.known_types = ['d4', 'd6', 'd8', 'd10', 'd12', 'd20', 'd100'];
this.dice_face_range = { 'd4': [1, 4], 'd6': [1, 6], 'd8': [1, 8], 'd10': [0, 9],
'd12': [1, 12], 'd20': [1, 20], 'd100': [0, 9] };
this.dice_mass = { 'd4': 300, 'd6': 300, 'd8': 340, 'd10': 350, 'd12': 350, 'd20': 400, 'd100': 350 };
this.dice_inertia = { 'd4': 5, 'd6': 13, 'd8': 10, 'd10': 9, 'd12': 8, 'd20': 6, 'd100': 9 };
this.scale = 50;
this.create_d4 = function() {
if (!this.d4_geometry) this.d4_geometry = this.create_d4_geometry(this.scale * 1.2);
if (!this.d4_material) this.d4_material = new THREE.MeshFaceMaterial(
this.create_d4_materials(this.scale / 2, this.scale * 2, d4_labels[0]));
return new THREE.Mesh(this.d4_geometry, this.d4_material);
}
this.create_d6 = function() {
if (!this.d6_geometry) this.d6_geometry = this.create_d6_geometry(this.scale * 0.9);
if (!this.dice_material) this.dice_material = new THREE.MeshFaceMaterial(
this.create_dice_materials(this.standart_d20_dice_face_labels, this.scale / 2, 1.0));
return new THREE.Mesh(this.d6_geometry, this.dice_material);
}
this.create_d8 = function() {
if (!this.d8_geometry) this.d8_geometry = this.create_d8_geometry(this.scale);
if (!this.dice_material) this.dice_material = new THREE.MeshFaceMaterial(
this.create_dice_materials(this.standart_d20_dice_face_labels, this.scale / 2, 1.2));
return new THREE.Mesh(this.d8_geometry, this.dice_material);
}
this.create_d10 = function() {
if (!this.d10_geometry) this.d10_geometry = this.create_d10_geometry(this.scale * 0.9);
if (!this.dice_material) this.dice_material = new THREE.MeshFaceMaterial(
this.create_dice_materials(this.standart_d20_dice_face_labels, this.scale / 2, 1.0));
return new THREE.Mesh(this.d10_geometry, this.dice_material);
}
this.create_d12 = function() {
if (!this.d12_geometry) this.d12_geometry = this.create_d12_geometry(this.scale * 0.9);
if (!this.dice_material) this.dice_material = new THREE.MeshFaceMaterial(
this.create_dice_materials(this.standart_d20_dice_face_labels, this.scale / 2, 1.0));
return new THREE.Mesh(this.d12_geometry, this.dice_material);
}
this.create_d20 = function() {
if (!this.d20_geometry) this.d20_geometry = this.create_d20_geometry(this.scale);
if (!this.dice_material) this.dice_material = new THREE.MeshFaceMaterial(
this.create_dice_materials(this.standart_d20_dice_face_labels, this.scale / 2, 1.0));
return new THREE.Mesh(this.d20_geometry, this.dice_material);
}
this.create_d100 = function() {
if (!this.d10_geometry) this.d10_geometry = this.create_d10_geometry(this.scale * 0.9);
if (!this.d100_material) this.d100_material = new THREE.MeshFaceMaterial(
this.create_dice_materials(this.standart_d100_dice_face_labels, this.scale / 2, 1.5));
return new THREE.Mesh(this.d10_geometry, this.d100_material);
}
this.parse_notation = function(notation) {
var no = notation.split('@');
var dr0 = /\s*(\d*)([a-z]+)(\d+)(\s*(\+|\-)\s*(\d+)){0,1}\s*(\+|$)/gi;
var dr1 = /(\b)*(\d+)(\b)*/gi;
var ret = { set: [], constant: 0, result: [], error: false }, res;
while (res = dr0.exec(no[0])) {
var command = res[2];
if (command != 'd') { ret.error = true; continue; }
var count = parseInt(res[1]);
if (res[1] == '') count = 1;
var type = 'd' + res[3];
if (this.known_types.indexOf(type) == -1) { ret.error = true; continue; }
while (count--) ret.set.push(type);
if (res[5] && res[6]) {
if (res[5] == '+') ret.constant += parseInt(res[6]);
else ret.constant -= parseInt(res[6]);
}
}
while (res = dr1.exec(no[1])) {
ret.result.push(parseInt(res[2]));
}
return ret;
}
this.stringify_notation = function(nn) {
var dict = {}, notation = '';
for (var i in nn.set)
if (!dict[nn.set[i]]) dict[nn.set[i]] = 1; else ++dict[nn.set[i]];
for (var i in dict) {
if (notation.length) notation += ' + ';
notation += (dict[i] > 1 ? dict[i] : '') + i;
}
if (nn.constant) {
if (nn.constant > 0) notation += ' + ' + nn.constant;
else notation += ' - ' + Math.abs(nn.constant);
}
return notation;
}
var that = this;
this.dice_box = function(container, dimentions, scale_change) {
this.use_adapvite_timestep = true;
this.animate_selector = true;
this.dices = [];
this.scene = new THREE.Scene();
this.world = new CANNON.World();
this.renderer = window.WebGLRenderingContext
? new THREE.WebGLRenderer({ antialias: true })
: new THREE.CanvasRenderer({ antialias: true });
container.appendChild(this.renderer.domElement);
this.renderer.shadowMap.enabled = true;
this.renderer.shadowMap.type = THREE.PCFShadowMap;
this.renderer.setClearColor(0xffffff, 1);
this.reinit(container, dimentions, scale_change);
this.world.gravity.set(0, 0, -9.8 * 800);
this.world.broadphase = new CANNON.NaiveBroadphase();
this.world.solver.iterations = 16;
var ambientLight = new THREE.AmbientLight(that.ambient_light_color);
this.scene.add(ambientLight);
this.dice_body_material = new CANNON.Material();
var desk_body_material = new CANNON.Material();
var barrier_body_material = new CANNON.Material();
this.world.addContactMaterial(new CANNON.ContactMaterial(
desk_body_material, this.dice_body_material, 0.01, 0.5));
this.world.addContactMaterial(new CANNON.ContactMaterial(
barrier_body_material, this.dice_body_material, 0, 1.0));
this.world.addContactMaterial(new CANNON.ContactMaterial(
this.dice_body_material, this.dice_body_material, 0, 0.5));
this.world.add(new CANNON.RigidBody(0, new CANNON.Plane(), desk_body_material));
var barrier;
barrier = new CANNON.RigidBody(0, new CANNON.Plane(), barrier_body_material);
barrier.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), Math.PI / 2);
barrier.position.set(0, this.h * 0.93, 0);
this.world.add(barrier);
barrier = new CANNON.RigidBody(0, new CANNON.Plane(), barrier_body_material);
barrier.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI / 2);
barrier.position.set(0, -this.h * 0.93, 0);
this.world.add(barrier);
barrier = new CANNON.RigidBody(0, new CANNON.Plane(), barrier_body_material);
barrier.quaternion.setFromAxisAngle(new CANNON.Vec3(0, 1, 0), -Math.PI / 2);
barrier.position.set(this.w * 0.93, 0, 0);
this.world.add(barrier);
barrier = new CANNON.RigidBody(0, new CANNON.Plane(), barrier_body_material);
barrier.quaternion.setFromAxisAngle(new CANNON.Vec3(0, 1, 0), Math.PI / 2);
barrier.position.set(-this.w * 0.93, 0, 0);
this.world.add(barrier);
this.last_time = 0;
this.running = false;
this.renderer.render(this.scene, this.camera);
}
this.dice_box.prototype.reinit = function(container, dimentions, scale_change) {
this.cw = container.clientWidth / 2;
this.ch = container.clientHeight / 2;
if (scale_change) {
this.scale_change = scale_change;
}
else {
this.scale_change = 1;
console.log(this.scale_change);
}
if (dimentions) {
this.w = dimentions.w;
this.h = dimentions.h;
}
else {
this.w = this.cw;
this.h = this.ch;
}
this.w = this.cw;
this.h = this.ch;
this.aspect = Math.min(this.cw / this.w, this.ch / this.h);
that.scale = (Math.sqrt(this.w * this.w + this.h * this.h) / 13)* this.scale_change;
this.renderer.setSize(this.cw * 2, this.ch * 2);
this.wh = this.ch / this.aspect / Math.tan(10 * Math.PI / 180);
if (this.camera) this.scene.remove(this.camera);
this.camera = new THREE.PerspectiveCamera(20, this.cw / this.ch, 1, this.wh * 1.3);
this.camera.position.z = this.wh;
var mw = Math.max(this.w, this.h);
if (this.light) this.scene.remove(this.light);
this.light = new THREE.SpotLight(that.spot_light_color, 0.7);
this.light.position.set(-mw / 2, mw / 2, mw * 2);
this.light.target.position.set(0, 0, 0);
this.light.distance = mw * 5;
this.light.angle = Math.PI/2;
this.light.distance = 0;
this.light.decay = 0;
this.light.castShadow = true;
this.light.shadowCameraNear = mw / 10;
this.light.shadowCameraFar = mw * 5;
this.light.shadowCameraFov = 50;
this.light.shadowBias = 0.001;
this.light.shadowDarkness = 1.1;
this.light.shadowMapWidth = 1024;
this.light.shadowMapHeight = 1024;
// if (that.use_shadows) {this.scene.add(this.light)}
this.scene.add(this.light);
if (this.desk) this.scene.remove(this.desk);
this.desk = new THREE.Mesh(new THREE.PlaneGeometry(this.w * 2, this.h * 2, 1, 1),
new THREE.MeshPhongMaterial({ color: that.desk_color, specular: that.desk_color }));
this.desk.receiveShadow = that.use_shadows;
this.scene.add(this.desk);
this.renderer.render(this.scene, this.camera);
}
function make_random_vector(vector) {
var random_angle = rnd() * Math.PI / 5 - Math.PI / 5 / 2;
var vec = {
x: vector.x * Math.cos(random_angle) - vector.y * Math.sin(random_angle),
y: vector.x * Math.sin(random_angle) + vector.y * Math.cos(random_angle)
};
if (vec.x == 0) vec.x = 0.01;
if (vec.y == 0) vec.y = 0.01;
return vec;
}
this.dice_box.prototype.generate_vectors = function(notation, vector, boost) {
var vectors = [];
for (var i in notation.set) {
var vec = make_random_vector(vector);
var pos = {
x: this.w * (vec.x > 0 ? -1 : 1) * 0.9,
y: this.h * (vec.y > 0 ? -1 : 1) * 0.9,
z: rnd() * 200 + 200
};
var projector = Math.abs(vec.x / vec.y);
if (projector > 1.0) pos.y /= projector; else pos.x *= projector;
var velvec = make_random_vector(vector);
var velocity = { x: velvec.x * boost, y: velvec.y * boost, z: -10 };
var inertia = that.dice_inertia[notation.set[i]];
var angle = {
x: -(rnd() * vec.y * 5 + inertia * vec.y),
y: rnd() * vec.x * 5 + inertia * vec.x,
z: 0
};
var axis = { x: rnd(), y: rnd(), z: rnd(), a: rnd() };
vectors.push({ set: notation.set[i], pos: pos, velocity: velocity, angle: angle, axis: axis });
}
return vectors;
}
this.dice_box.prototype.create_dice = function(type, pos, velocity, angle, axis) {
var dice = that['create_' + type]();
dice.castShadow = true;
dice.dice_type = type;
dice.body = new CANNON.RigidBody(that.dice_mass[type],
dice.geometry.cannon_shape, this.dice_body_material);
dice.body.position.set(pos.x, pos.y, pos.z);
dice.body.quaternion.setFromAxisAngle(new CANNON.Vec3(axis.x, axis.y, axis.z), axis.a * Math.PI * 2);
dice.body.angularVelocity.set(angle.x, angle.y, angle.z);
dice.body.velocity.set(velocity.x, velocity.y, velocity.z);
dice.body.linearDamping = 0.1;
dice.body.angularDamping = 0.1;
this.scene.add(dice);
this.dices.push(dice);
this.world.add(dice.body);
}
this.dice_box.prototype.check_if_throw_finished = function() {
var res = true;
var e = 6;
if (this.iteration < 10 / that.frame_rate) {
for (var i = 0; i < this.dices.length; ++i) {
var dice = this.dices[i];
if (dice.dice_stopped === true) continue;
var a = dice.body.angularVelocity, v = dice.body.velocity;
if (Math.abs(a.x) < e && Math.abs(a.y) < e && Math.abs(a.z) < e &&
Math.abs(v.x) < e && Math.abs(v.y) < e && Math.abs(v.z) < e) {
if (dice.dice_stopped) {
if (this.iteration - dice.dice_stopped > 3) {
dice.dice_stopped = true;
continue;
}
}
else dice.dice_stopped = this.iteration;
res = false;
}
else {
dice.dice_stopped = undefined;
res = false;
}
}
}
return res;
}
function get_dice_value(dice) {
var vector = new THREE.Vector3(0, 0, dice.dice_type == 'd4' ? -1 : 1);
var closest_face, closest_angle = Math.PI * 2;
for (var i = 0, l = dice.geometry.faces.length; i < l; ++i) {
var face = dice.geometry.faces[i];
if (face.materialIndex == 0) continue;
var angle = face.normal.clone().applyQuaternion(dice.body.quaternion).angleTo(vector);
if (angle < closest_angle) {
closest_angle = angle;
closest_face = face;
}
}
if (closest_face){
var matindex = closest_face.materialIndex - 1;
}
else {
/*
console.log("undefined closest face: reloading in 3.. 2.. 1..");
setTimeout(function() {
window.location.reload()
}, 3000);
*/
window.location.reload()
}
if (dice.dice_type == 'd100') matindex *= 10;
if (dice.dice_type == 'd10' && matindex == 0) matindex = 10;
return matindex;
}
function get_dice_values(dices) {
var values = [];
for (var i = 0, l = dices.length; i < l; ++i) {
values.push(get_dice_value(dices[i]));
}
return values;
}
this.dice_box.prototype.emulate_throw = function() {
while (!this.check_if_throw_finished()) {
++this.iteration;
this.world.step(that.frame_rate);
}
return get_dice_values(this.dices);
}
this.dice_box.prototype.__animate = function(threadid) {
var time = (new Date()).getTime();
var time_diff = (time - this.last_time) / 1000;
if (time_diff > 3) time_diff = that.frame_rate;
++this.iteration;
if (this.use_adapvite_timestep) {
while (time_diff > that.frame_rate * 1.1) {
this.world.step(that.frame_rate);
time_diff -= that.frame_rate;
}
this.world.step(time_diff);
}
else {
this.world.step(that.frame_rate);
}
for (var i in this.scene.children) {
var interact = this.scene.children[i];
if (interact.body != undefined) {
interact.position.copy(interact.body.position);
interact.quaternion.copy(interact.body.quaternion);
}
}
this.renderer.render(this.scene, this.camera);
this.last_time = this.last_time ? time : (new Date()).getTime();
if (this.running == threadid && this.check_if_throw_finished()) {
this.running = false;
if (this.callback) this.callback.call(this, get_dice_values(this.dices));
}
if (this.running == threadid) {
(function(t, tid, uat) {
if (!uat && time_diff < that.frame_rate) {
setTimeout(function() { requestAnimationFrame(function() { t.__animate(tid); }); },
(that.frame_rate - time_diff) * 1000);
}
else requestAnimationFrame(function() { t.__animate(tid); });
})(this, threadid, this.use_adapvite_timestep);
}
}
this.dice_box.prototype.clear = function() {
this.running = false;
var dice;
while (dice = this.dices.pop()) {
this.scene.remove(dice);
if (dice.body) this.world.remove(dice.body);
}
if (this.pane) this.scene.remove(this.pane);
this.renderer.render(this.scene, this.camera);
var box = this;
setTimeout(function() { box.renderer.render(box.scene, box.camera); }, 100);
}
this.dice_box.prototype.prepare_dices_for_roll = function(vectors) {
this.clear();
this.iteration = 0;
for (var i in vectors) {
this.create_dice(vectors[i].set, vectors[i].pos, vectors[i].velocity,
vectors[i].angle, vectors[i].axis);
}
}
function shift_dice_faces(dice, value, res) {
var r = that.dice_face_range[dice.dice_type];
if (dice.dice_type == 'd10' && value == 10) value = 0;
if (dice.dice_type == 'd10' && res == 10) res = 0;
if (dice.dice_type == 'd100') res /= 10;
if (!(value >= r[0] && value <= r[1])) return;
var num = value - res;
var geom = dice.geometry.clone();
for (var i = 0, l = geom.faces.length; i < l; ++i) {
var matindex = geom.faces[i].materialIndex;
if (matindex == 0) continue;
matindex += num - 1;
while (matindex > r[1]) matindex -= r[1];
while (matindex < r[0]) matindex += r[1];
geom.faces[i].materialIndex = matindex + 1;
}
if (dice.dice_type == 'd4' && num != 0) {
if (num < 0) num += 4;
dice.material = new THREE.MeshFaceMaterial(
that.create_d4_materials(that.scale / 2, that.scale * 2, d4_labels[num]));
}
dice.geometry = geom;
}
this.dice_box.prototype.roll = function(vectors, values, callback) {
this.prepare_dices_for_roll(vectors);
if (values != undefined && values.length) {
this.use_adapvite_timestep = false;
var res = this.emulate_throw();
this.prepare_dices_for_roll(vectors);
for (var i in res)
shift_dice_faces(this.dices[i], values[i], res[i]);
}
this.callback = callback;
this.running = (new Date()).getTime();
this.last_time = 0;
this.__animate(this.running);
}
this.dice_box.prototype.__selector_animate = function(threadid) {
var time = (new Date()).getTime();
var time_diff = (time - this.last_time) / 1000;
if (time_diff > 3) time_diff = that.frame_rate;
var angle_change = 0.3 * time_diff * Math.PI * Math.min(24000 + threadid - time, 6000) / 6000;
if (angle_change < 0) this.running = false;
for (var i in this.dices) {
this.dices[i].rotation.y += angle_change;
this.dices[i].rotation.x += angle_change / 4;
this.dices[i].rotation.z += angle_change / 10;
}
this.last_time = time;
this.renderer.render(this.scene, this.camera);
if (this.running == threadid) {
(function(t, tid) {
requestAnimationFrame(function() { t.__selector_animate(tid); });
})(this, threadid);
}
}
this.dice_box.prototype.search_dice_by_mouse = function(ev) {
var m = $t.get_mouse_coords(ev);
var intersects = (new THREE.Raycaster(this.camera.position,
(new THREE.Vector3((m.x - this.cw) / this.aspect,
1 - (m.y - this.ch) / this.aspect, this.w / 9))
.sub(this.camera.position).normalize())).intersectObjects(this.dices);
if (intersects.length) return intersects[0].object.userData;
}
this.dice_box.prototype.draw_selector = function() {
this.clear();
var step = this.w / 4.5;
this.pane = new THREE.Mesh(new THREE.PlaneGeometry(this.w * 6, this.h * 6, 1, 1),
new THREE.MeshPhongMaterial(that.selector_back_colors));
this.pane.receiveShadow = true;
this.pane.position.set(0, 0, 1);
this.scene.add(this.pane);
var mouse_captured = false;
for (var i = 0, pos = -3; i < that.known_types.length; ++i, ++pos) {
var dice = $t.dice['create_' + that.known_types[i]]();
dice.position.set(pos * step, 0, step * 0.5);
dice.castShadow = true;
dice.userData = that.known_types[i];
this.dices.push(dice); this.scene.add(dice);
}
this.running = (new Date()).getTime();
this.last_time = 0;
if (this.animate_selector) this.__selector_animate(this.running);
else this.renderer.render(this.scene, this.camera);
}
function throw_dices(box, vector, boost, dist, notation_getter, before_roll, after_roll) {
var uat = $t.dice.use_adapvite_timestep;
function roll(request_results) {
if (after_roll) {
box.clear();
box.roll(vectors, request_results || notation.result, function(result) {
if (after_roll) after_roll.call(box, notation, result);
box.rolling = false;
$t.dice.use_adapvite_timestep = uat;
});
}
}
vector.x /= dist; vector.y /= dist;
var notation = notation_getter.call(box);
if (notation.set.length == 0) return;
var vectors = box.generate_vectors(notation, vector, boost);
box.rolling = true;
if (before_roll) before_roll.call(box, vectors, notation, roll);
else roll();
}
this.dice_box.prototype.bind_mouse = function(container, notation_getter, before_roll, after_roll) {
var box = this;
$t.bind(container, ['mousedown', 'touchstart'], function(ev) {
ev.preventDefault();
box.mouse_time = (new Date()).getTime();
box.mouse_start = $t.get_mouse_coords(ev);
});
$t.bind(container, ['mouseup', 'touchend'], function(ev) {
if (box.rolling) return;
if (box.mouse_start == undefined) return;
ev.stopPropagation();
var m = $t.get_mouse_coords(ev);
var vector = { x: m.x - box.mouse_start.x, y: -(m.y - box.mouse_start.y) };
box.mouse_start = undefined;
var dist = Math.sqrt(vector.x * vector.x + vector.y * vector.y);
if (dist < Math.sqrt(box.w * box.h * 0.01)) return;
var time_int = (new Date()).getTime() - box.mouse_time;
if (time_int > 2000) time_int = 2000;
var boost = Math.sqrt((2500 - time_int) / 2500) * dist * 2;
prepare_rnd(function() {
throw_dices(box, vector, boost, dist, notation_getter, before_roll, after_roll);
});
});
}
this.dice_box.prototype.bind_throw = function(button, notation_getter, before_roll, after_roll) {
var box = this;
$t.bind(button, ['mouseup', 'touchend'], function(ev) {
ev.stopPropagation();
box.start_throw(notation_getter, before_roll, after_roll);
});
}
this.dice_box.prototype.start_throw = function(notation_getter, before_roll, after_roll) {
var box = this;
if (box.rolling) return;
prepare_rnd(function() {
var vector = { x: (rnd() * 2 - 1) * box.w, y: -(rnd() * 2 - 1) * box.h };
var dist = Math.sqrt(vector.x * vector.x + vector.y * vector.y);
var boost = (rnd() + 3) * dist;
throw_dices(box, vector, boost, dist, notation_getter, before_roll, after_roll);
});
}
}).apply(teal.dice = teal.dice || {});