Semi working game... ready international release
This commit is contained in:
commit
08e5e1b997
5
.gitignore
vendored
5
.gitignore
vendored
@ -2,4 +2,7 @@
|
||||
docs/_site/*
|
||||
docs/.sass-cache/*
|
||||
docs/.jekyll-cache/*
|
||||
docs/node_modules
|
||||
docs/node_modules
|
||||
|
||||
# idea
|
||||
.idea
|
@ -1,57 +1,130 @@
|
||||
// UI for title screen
|
||||
function drawTitleScreenUI() {};
|
||||
function drawTitleScreenUI() {
|
||||
}
|
||||
|
||||
// UI for level transition
|
||||
function drawLevelTransitionUI() {};
|
||||
function drawLevelTransitionUI() {
|
||||
}
|
||||
|
||||
// UI for playing
|
||||
function drawPlayingUI() {};
|
||||
function drawPlayingUI() {
|
||||
|
||||
// cartesianRect(0,ch/3*2, cw, ch/3, "#333333")
|
||||
|
||||
//Heart Rate Monitor
|
||||
heartBeatUI(cw/4*3-8,ch/8*7-8,cw/4,ch/8);
|
||||
|
||||
//Respiration Monitor
|
||||
respiratoryUI(cw/8*5,ch/8*7-8, cw/16, ch/8);
|
||||
}
|
||||
|
||||
//UI for pause screen
|
||||
function drawPausedUI() {};
|
||||
function drawPausedUI() {
|
||||
}
|
||||
|
||||
//UI for game end
|
||||
function drawEndUI() {};
|
||||
function drawEndUI() {
|
||||
}
|
||||
|
||||
// Construct a rectangular UI
|
||||
function rectUI() {};
|
||||
|
||||
/***
|
||||
*
|
||||
* RESPIRATORY UI
|
||||
*
|
||||
*/
|
||||
|
||||
function respiratoryUI(x, y, width, height){
|
||||
cartesianRect(x,y,width,height, "rgb("+noBreathTimer/180*255+","+0+","+0+")");
|
||||
cartesianRect(x,y+(height-breath/constants.lifeFuncs.breath.fullBreath*height), width, breath/constants.lifeFuncs.breath.fullBreath*height, "rgb("+255+","+(255-fullBreathTimer/180*255)+","+(255-fullBreathTimer/180*255)+")");
|
||||
}
|
||||
|
||||
/***
|
||||
*
|
||||
* HEART RATE MONITOR UI
|
||||
*
|
||||
*/
|
||||
|
||||
//Heart rate monitor history
|
||||
var heartBeatHistory = [].fill(0,0, constants.ui.heartRate.history_length);
|
||||
let heartBeatHistory = [];
|
||||
heartBeatHistory.length = constants.ui.heartRate.history_length;
|
||||
heartBeatHistory.fill(0);
|
||||
|
||||
//Shift accumulation
|
||||
var shiftAccum = 0;
|
||||
let shiftAccum = 0;
|
||||
|
||||
//Beat progression
|
||||
var beatTimeElapsed = 0;
|
||||
let beatTimeElapsed = Infinity;
|
||||
|
||||
// Draw heartbeat UI
|
||||
function heartBeatUI(x, y, width, height){
|
||||
|
||||
//Shift monitor over once a full scrolling unit is accumulated
|
||||
shiftAccum += constants.ui.heartRate.scroll_speed;
|
||||
if(shiftAccum>=1){
|
||||
shiftAccum%=1;
|
||||
beatTimeElapsed += 0.04;
|
||||
|
||||
//Remove oldest value
|
||||
heartBeatHistory.shift();
|
||||
|
||||
//Append new value
|
||||
pushNextBeatValue();
|
||||
}
|
||||
|
||||
if(timeSinceLastBeat===0){
|
||||
//If heart is beaten, reset beat timer.
|
||||
if(heartBeat){
|
||||
beatTimeElapsed = 0;
|
||||
heartBeat = false;
|
||||
}
|
||||
|
||||
//Backdrop
|
||||
rect(x+width/2,y+height/2,width,height,"black");
|
||||
|
||||
//Pressure Meter
|
||||
rect(x+width-8,y+height/2,16,height,"red");
|
||||
rect(x+width-8,y+height/2,16,height/2,"yellow");
|
||||
rect(x+width-8,y+height/2,16,height/6,"green");
|
||||
let pressureHeight = Math.max(Math.min(y+height-(pressure/constants.lifeFuncs.cardio.optimalPressure*height/2),y+height),y);
|
||||
line(x+width-16, pressureHeight,x+width,pressureHeight, 2,"black")
|
||||
|
||||
//Graph
|
||||
for (let index = 0; index < heartBeatHistory.length; index++) {
|
||||
const qrsValueAtPosition = heartBeatHistory[index];
|
||||
line(x+index, y+(2*height/3), x+index, y+(2*height/3)+qrsValueAtPosition);
|
||||
const qrsValueAtNextPosition = heartBeatHistory[index+1];
|
||||
line(x+(index*(width-16)/heartBeatHistory.length), y+(2*height/3)+(qrsValueAtPosition*(width-16)/heartBeatHistory.length), x+((index+1)*(width-16)/heartBeatHistory.length), y+(2*height/3)+(qrsValueAtNextPosition*(width-16)/heartBeatHistory.length),Math.min(3,Math.max(3/beatTimeElapsed,1)), "red");
|
||||
}
|
||||
}
|
||||
|
||||
//Determine next value to be added to the graph
|
||||
function pushNextBeatValue(){
|
||||
var nextBeatValue;
|
||||
let nextBeatValue = 0;
|
||||
|
||||
beatTimeElapsed %= constants.ui.heartRate.complex_width;
|
||||
if(beatTimeElapsed<=constants.ui.heartRate.pr_width){
|
||||
nextBeatValue = -0.25((x - 1.5)**2) + 0.5625;
|
||||
} else if (beatTimeElapsed >= constants.ui.heartRate.pr_width + 1 && beatTimeElapsed <= constants.ui.heartRate.pr_width + 1 + constants.ui.heartRate.qrs_width/4) {
|
||||
//Timespan of one "square" on the EKG
|
||||
const squareSize = constants.ui.heartRate.square_size;
|
||||
//Length of full complex
|
||||
const complexTime = constants.ui.heartRate.complex_width*squareSize;
|
||||
//Length of PR segment of complex
|
||||
const prTime = constants.ui.heartRate.pr_width*squareSize;
|
||||
//Length of QRS component of complex
|
||||
const qrsTime = constants.ui.heartRate.qrs_width*squareSize;
|
||||
//Length of QT component of complex
|
||||
const qtTime = constants.ui.heartRate.qt_width*squareSize;
|
||||
|
||||
if(beatTimeElapsed<=complexTime) {
|
||||
//PR Segment
|
||||
if (beatTimeElapsed <= prTime) {
|
||||
nextBeatValue = 0.5*(Math.pow((beatTimeElapsed/squareSize - (prTime/2/squareSize)), 2)) - 2;
|
||||
} else if (beatTimeElapsed > prTime + squareSize && beatTimeElapsed <= prTime + squareSize + (qrsTime / 4)) { //QRS Segment pt. 1
|
||||
nextBeatValue = -4 + beatTimeElapsed/squareSize;
|
||||
} else if (beatTimeElapsed > prTime + squareSize + qrsTime / 4 && beatTimeElapsed <= prTime + squareSize + qrsTime / 2) { //QRS Segment pt. 2
|
||||
nextBeatValue = -14 * (beatTimeElapsed/squareSize - 4.5) - 0.5;
|
||||
} else if (beatTimeElapsed > prTime + squareSize + qrsTime / 2 && beatTimeElapsed <= prTime + squareSize + (3*qrsTime / 4)) { //QRS Segment pt. 3
|
||||
nextBeatValue = 7 * (beatTimeElapsed/squareSize - 5) - 6.5;
|
||||
} else if (beatTimeElapsed > prTime + squareSize + (3*qrsTime / 4) && beatTimeElapsed <= prTime + squareSize + qrsTime) { //QRS Segment pt. 4
|
||||
nextBeatValue = 2 * (beatTimeElapsed/squareSize - 6);
|
||||
} else if (beatTimeElapsed > prTime + squareSize*2 + qrsTime && beatTimeElapsed <= prTime + squareSize*2 + qrsTime + qtTime) { //PT Segment
|
||||
nextBeatValue = 0.5 * Math.pow((beatTimeElapsed/squareSize - (prTime + squareSize*2 + qrsTime + qtTime/2)/squareSize),2) - 3;
|
||||
}
|
||||
}
|
||||
|
||||
heartBeatHistory.push(nextBeatValue);
|
||||
|
@ -26,16 +26,32 @@ var constants = {
|
||||
history_length: 100,
|
||||
|
||||
//300 squares/min
|
||||
scroll_speed: 0.13333,
|
||||
pr_width: 0.16,
|
||||
qrs_width: 0.1,
|
||||
qt_width: 0.39,
|
||||
complex_width: 0.65
|
||||
scroll_speed: 0.8,
|
||||
square_size: 0.08,
|
||||
pr_width: 4,
|
||||
qrs_width: 2,
|
||||
qt_width: 5,
|
||||
complex_width: 18
|
||||
}
|
||||
},
|
||||
legs:{
|
||||
size:{
|
||||
maximumMovement: 30
|
||||
lifeFuncs:{
|
||||
breath:{
|
||||
fullBreath: 200
|
||||
},
|
||||
cardio:{
|
||||
optimalPressure: 50
|
||||
}
|
||||
},
|
||||
player:{
|
||||
leg_speed: 0.1,
|
||||
movement_divider: 50,
|
||||
max_movement_speed: 3,
|
||||
width: 30,
|
||||
height: 50,
|
||||
select_range: 10,
|
||||
hip: {
|
||||
offset_x: 15,
|
||||
offset_y: 25
|
||||
}
|
||||
}
|
||||
|
||||
|
1
docs/assets/js/crypto/core.min.js
vendored
Normal file
1
docs/assets/js/crypto/core.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
!function(t,n){"object"==typeof exports?module.exports=exports=n():"function"==typeof define&&define.amd?define([],n):t.CryptoJS=n()}(this,function(){var t=t||function(f){var t;if("undefined"!=typeof window&&window.crypto&&(t=window.crypto),!t&&"undefined"!=typeof window&&window.msCrypto&&(t=window.msCrypto),!t&&"undefined"!=typeof global&&global.crypto&&(t=global.crypto),!t&&"function"==typeof require)try{t=require("crypto")}catch(t){}function i(){if(t){if("function"==typeof t.getRandomValues)try{return t.getRandomValues(new Uint32Array(1))[0]}catch(t){}if("function"==typeof t.randomBytes)try{return t.randomBytes(4).readInt32LE()}catch(t){}}throw new Error("Native crypto module could not be used to get secure random number.")}var e=Object.create||function(t){var n;return r.prototype=t,n=new r,r.prototype=null,n};function r(){}var n={},o=n.lib={},s=o.Base={extend:function(t){var n=e(this);return t&&n.mixIn(t),n.hasOwnProperty("init")&&this.init!==n.init||(n.init=function(){n.$super.init.apply(this,arguments)}),(n.init.prototype=n).$super=this,n},create:function(){var t=this.extend();return t.init.apply(t,arguments),t},init:function(){},mixIn:function(t){for(var n in t)t.hasOwnProperty(n)&&(this[n]=t[n]);t.hasOwnProperty("toString")&&(this.toString=t.toString)},clone:function(){return this.init.prototype.extend(this)}},p=o.WordArray=s.extend({init:function(t,n){t=this.words=t||[],this.sigBytes=null!=n?n:4*t.length},toString:function(t){return(t||c).stringify(this)},concat:function(t){var n=this.words,e=t.words,i=this.sigBytes,r=t.sigBytes;if(this.clamp(),i%4)for(var o=0;o<r;o++){var s=e[o>>>2]>>>24-o%4*8&255;n[i+o>>>2]|=s<<24-(i+o)%4*8}else for(o=0;o<r;o+=4)n[i+o>>>2]=e[o>>>2];return this.sigBytes+=r,this},clamp:function(){var t=this.words,n=this.sigBytes;t[n>>>2]&=4294967295<<32-n%4*8,t.length=f.ceil(n/4)},clone:function(){var t=s.clone.call(this);return t.words=this.words.slice(0),t},random:function(t){for(var n=[],e=0;e<t;e+=4)n.push(i());return new p.init(n,t)}}),a=n.enc={},c=a.Hex={stringify:function(t){for(var n=t.words,e=t.sigBytes,i=[],r=0;r<e;r++){var o=n[r>>>2]>>>24-r%4*8&255;i.push((o>>>4).toString(16)),i.push((15&o).toString(16))}return i.join("")},parse:function(t){for(var n=t.length,e=[],i=0;i<n;i+=2)e[i>>>3]|=parseInt(t.substr(i,2),16)<<24-i%8*4;return new p.init(e,n/2)}},u=a.Latin1={stringify:function(t){for(var n=t.words,e=t.sigBytes,i=[],r=0;r<e;r++){var o=n[r>>>2]>>>24-r%4*8&255;i.push(String.fromCharCode(o))}return i.join("")},parse:function(t){for(var n=t.length,e=[],i=0;i<n;i++)e[i>>>2]|=(255&t.charCodeAt(i))<<24-i%4*8;return new p.init(e,n)}},d=a.Utf8={stringify:function(t){try{return decodeURIComponent(escape(u.stringify(t)))}catch(t){throw new Error("Malformed UTF-8 data")}},parse:function(t){return u.parse(unescape(encodeURIComponent(t)))}},h=o.BufferedBlockAlgorithm=s.extend({reset:function(){this._data=new p.init,this._nDataBytes=0},_append:function(t){"string"==typeof t&&(t=d.parse(t)),this._data.concat(t),this._nDataBytes+=t.sigBytes},_process:function(t){var n,e=this._data,i=e.words,r=e.sigBytes,o=this.blockSize,s=r/(4*o),a=(s=t?f.ceil(s):f.max((0|s)-this._minBufferSize,0))*o,c=f.min(4*a,r);if(a){for(var u=0;u<a;u+=o)this._doProcessBlock(i,u);n=i.splice(0,a),e.sigBytes-=c}return new p.init(n,c)},clone:function(){var t=s.clone.call(this);return t._data=this._data.clone(),t},_minBufferSize:0}),l=(o.Hasher=h.extend({cfg:s.extend(),init:function(t){this.cfg=this.cfg.extend(t),this.reset()},reset:function(){h.reset.call(this),this._doReset()},update:function(t){return this._append(t),this._process(),this},finalize:function(t){return t&&this._append(t),this._doFinalize()},blockSize:16,_createHelper:function(e){return function(t,n){return new e.init(n).finalize(t)}},_createHmacHelper:function(e){return function(t,n){return new l.HMAC.init(e,n).finalize(t)}}}),n.algo={});return n}(Math);return t});
|
268
docs/assets/js/crypto/md5.js
Normal file
268
docs/assets/js/crypto/md5.js
Normal file
@ -0,0 +1,268 @@
|
||||
;(function (root, factory) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
(function (Math) {
|
||||
// Shortcuts
|
||||
var C = CryptoJS;
|
||||
var C_lib = C.lib;
|
||||
var WordArray = C_lib.WordArray;
|
||||
var Hasher = C_lib.Hasher;
|
||||
var C_algo = C.algo;
|
||||
|
||||
// Constants table
|
||||
var T = [];
|
||||
|
||||
// Compute constants
|
||||
(function () {
|
||||
for (var i = 0; i < 64; i++) {
|
||||
T[i] = (Math.abs(Math.sin(i + 1)) * 0x100000000) | 0;
|
||||
}
|
||||
}());
|
||||
|
||||
/**
|
||||
* MD5 hash algorithm.
|
||||
*/
|
||||
var MD5 = C_algo.MD5 = Hasher.extend({
|
||||
_doReset: function () {
|
||||
this._hash = new WordArray.init([
|
||||
0x67452301, 0xefcdab89,
|
||||
0x98badcfe, 0x10325476
|
||||
]);
|
||||
},
|
||||
|
||||
_doProcessBlock: function (M, offset) {
|
||||
// Swap endian
|
||||
for (var i = 0; i < 16; i++) {
|
||||
// Shortcuts
|
||||
var offset_i = offset + i;
|
||||
var M_offset_i = M[offset_i];
|
||||
|
||||
M[offset_i] = (
|
||||
(((M_offset_i << 8) | (M_offset_i >>> 24)) & 0x00ff00ff) |
|
||||
(((M_offset_i << 24) | (M_offset_i >>> 8)) & 0xff00ff00)
|
||||
);
|
||||
}
|
||||
|
||||
// Shortcuts
|
||||
var H = this._hash.words;
|
||||
|
||||
var M_offset_0 = M[offset + 0];
|
||||
var M_offset_1 = M[offset + 1];
|
||||
var M_offset_2 = M[offset + 2];
|
||||
var M_offset_3 = M[offset + 3];
|
||||
var M_offset_4 = M[offset + 4];
|
||||
var M_offset_5 = M[offset + 5];
|
||||
var M_offset_6 = M[offset + 6];
|
||||
var M_offset_7 = M[offset + 7];
|
||||
var M_offset_8 = M[offset + 8];
|
||||
var M_offset_9 = M[offset + 9];
|
||||
var M_offset_10 = M[offset + 10];
|
||||
var M_offset_11 = M[offset + 11];
|
||||
var M_offset_12 = M[offset + 12];
|
||||
var M_offset_13 = M[offset + 13];
|
||||
var M_offset_14 = M[offset + 14];
|
||||
var M_offset_15 = M[offset + 15];
|
||||
|
||||
// Working varialbes
|
||||
var a = H[0];
|
||||
var b = H[1];
|
||||
var c = H[2];
|
||||
var d = H[3];
|
||||
|
||||
// Computation
|
||||
a = FF(a, b, c, d, M_offset_0, 7, T[0]);
|
||||
d = FF(d, a, b, c, M_offset_1, 12, T[1]);
|
||||
c = FF(c, d, a, b, M_offset_2, 17, T[2]);
|
||||
b = FF(b, c, d, a, M_offset_3, 22, T[3]);
|
||||
a = FF(a, b, c, d, M_offset_4, 7, T[4]);
|
||||
d = FF(d, a, b, c, M_offset_5, 12, T[5]);
|
||||
c = FF(c, d, a, b, M_offset_6, 17, T[6]);
|
||||
b = FF(b, c, d, a, M_offset_7, 22, T[7]);
|
||||
a = FF(a, b, c, d, M_offset_8, 7, T[8]);
|
||||
d = FF(d, a, b, c, M_offset_9, 12, T[9]);
|
||||
c = FF(c, d, a, b, M_offset_10, 17, T[10]);
|
||||
b = FF(b, c, d, a, M_offset_11, 22, T[11]);
|
||||
a = FF(a, b, c, d, M_offset_12, 7, T[12]);
|
||||
d = FF(d, a, b, c, M_offset_13, 12, T[13]);
|
||||
c = FF(c, d, a, b, M_offset_14, 17, T[14]);
|
||||
b = FF(b, c, d, a, M_offset_15, 22, T[15]);
|
||||
|
||||
a = GG(a, b, c, d, M_offset_1, 5, T[16]);
|
||||
d = GG(d, a, b, c, M_offset_6, 9, T[17]);
|
||||
c = GG(c, d, a, b, M_offset_11, 14, T[18]);
|
||||
b = GG(b, c, d, a, M_offset_0, 20, T[19]);
|
||||
a = GG(a, b, c, d, M_offset_5, 5, T[20]);
|
||||
d = GG(d, a, b, c, M_offset_10, 9, T[21]);
|
||||
c = GG(c, d, a, b, M_offset_15, 14, T[22]);
|
||||
b = GG(b, c, d, a, M_offset_4, 20, T[23]);
|
||||
a = GG(a, b, c, d, M_offset_9, 5, T[24]);
|
||||
d = GG(d, a, b, c, M_offset_14, 9, T[25]);
|
||||
c = GG(c, d, a, b, M_offset_3, 14, T[26]);
|
||||
b = GG(b, c, d, a, M_offset_8, 20, T[27]);
|
||||
a = GG(a, b, c, d, M_offset_13, 5, T[28]);
|
||||
d = GG(d, a, b, c, M_offset_2, 9, T[29]);
|
||||
c = GG(c, d, a, b, M_offset_7, 14, T[30]);
|
||||
b = GG(b, c, d, a, M_offset_12, 20, T[31]);
|
||||
|
||||
a = HH(a, b, c, d, M_offset_5, 4, T[32]);
|
||||
d = HH(d, a, b, c, M_offset_8, 11, T[33]);
|
||||
c = HH(c, d, a, b, M_offset_11, 16, T[34]);
|
||||
b = HH(b, c, d, a, M_offset_14, 23, T[35]);
|
||||
a = HH(a, b, c, d, M_offset_1, 4, T[36]);
|
||||
d = HH(d, a, b, c, M_offset_4, 11, T[37]);
|
||||
c = HH(c, d, a, b, M_offset_7, 16, T[38]);
|
||||
b = HH(b, c, d, a, M_offset_10, 23, T[39]);
|
||||
a = HH(a, b, c, d, M_offset_13, 4, T[40]);
|
||||
d = HH(d, a, b, c, M_offset_0, 11, T[41]);
|
||||
c = HH(c, d, a, b, M_offset_3, 16, T[42]);
|
||||
b = HH(b, c, d, a, M_offset_6, 23, T[43]);
|
||||
a = HH(a, b, c, d, M_offset_9, 4, T[44]);
|
||||
d = HH(d, a, b, c, M_offset_12, 11, T[45]);
|
||||
c = HH(c, d, a, b, M_offset_15, 16, T[46]);
|
||||
b = HH(b, c, d, a, M_offset_2, 23, T[47]);
|
||||
|
||||
a = II(a, b, c, d, M_offset_0, 6, T[48]);
|
||||
d = II(d, a, b, c, M_offset_7, 10, T[49]);
|
||||
c = II(c, d, a, b, M_offset_14, 15, T[50]);
|
||||
b = II(b, c, d, a, M_offset_5, 21, T[51]);
|
||||
a = II(a, b, c, d, M_offset_12, 6, T[52]);
|
||||
d = II(d, a, b, c, M_offset_3, 10, T[53]);
|
||||
c = II(c, d, a, b, M_offset_10, 15, T[54]);
|
||||
b = II(b, c, d, a, M_offset_1, 21, T[55]);
|
||||
a = II(a, b, c, d, M_offset_8, 6, T[56]);
|
||||
d = II(d, a, b, c, M_offset_15, 10, T[57]);
|
||||
c = II(c, d, a, b, M_offset_6, 15, T[58]);
|
||||
b = II(b, c, d, a, M_offset_13, 21, T[59]);
|
||||
a = II(a, b, c, d, M_offset_4, 6, T[60]);
|
||||
d = II(d, a, b, c, M_offset_11, 10, T[61]);
|
||||
c = II(c, d, a, b, M_offset_2, 15, T[62]);
|
||||
b = II(b, c, d, a, M_offset_9, 21, T[63]);
|
||||
|
||||
// Intermediate hash value
|
||||
H[0] = (H[0] + a) | 0;
|
||||
H[1] = (H[1] + b) | 0;
|
||||
H[2] = (H[2] + c) | 0;
|
||||
H[3] = (H[3] + d) | 0;
|
||||
},
|
||||
|
||||
_doFinalize: function () {
|
||||
// Shortcuts
|
||||
var data = this._data;
|
||||
var dataWords = data.words;
|
||||
|
||||
var nBitsTotal = this._nDataBytes * 8;
|
||||
var nBitsLeft = data.sigBytes * 8;
|
||||
|
||||
// Add padding
|
||||
dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32);
|
||||
|
||||
var nBitsTotalH = Math.floor(nBitsTotal / 0x100000000);
|
||||
var nBitsTotalL = nBitsTotal;
|
||||
dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = (
|
||||
(((nBitsTotalH << 8) | (nBitsTotalH >>> 24)) & 0x00ff00ff) |
|
||||
(((nBitsTotalH << 24) | (nBitsTotalH >>> 8)) & 0xff00ff00)
|
||||
);
|
||||
dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = (
|
||||
(((nBitsTotalL << 8) | (nBitsTotalL >>> 24)) & 0x00ff00ff) |
|
||||
(((nBitsTotalL << 24) | (nBitsTotalL >>> 8)) & 0xff00ff00)
|
||||
);
|
||||
|
||||
data.sigBytes = (dataWords.length + 1) * 4;
|
||||
|
||||
// Hash final blocks
|
||||
this._process();
|
||||
|
||||
// Shortcuts
|
||||
var hash = this._hash;
|
||||
var H = hash.words;
|
||||
|
||||
// Swap endian
|
||||
for (var i = 0; i < 4; i++) {
|
||||
// Shortcut
|
||||
var H_i = H[i];
|
||||
|
||||
H[i] = (((H_i << 8) | (H_i >>> 24)) & 0x00ff00ff) |
|
||||
(((H_i << 24) | (H_i >>> 8)) & 0xff00ff00);
|
||||
}
|
||||
|
||||
// Return final computed hash
|
||||
return hash;
|
||||
},
|
||||
|
||||
clone: function () {
|
||||
var clone = Hasher.clone.call(this);
|
||||
clone._hash = this._hash.clone();
|
||||
|
||||
return clone;
|
||||
}
|
||||
});
|
||||
|
||||
function FF(a, b, c, d, x, s, t) {
|
||||
var n = a + ((b & c) | (~b & d)) + x + t;
|
||||
return ((n << s) | (n >>> (32 - s))) + b;
|
||||
}
|
||||
|
||||
function GG(a, b, c, d, x, s, t) {
|
||||
var n = a + ((b & d) | (c & ~d)) + x + t;
|
||||
return ((n << s) | (n >>> (32 - s))) + b;
|
||||
}
|
||||
|
||||
function HH(a, b, c, d, x, s, t) {
|
||||
var n = a + (b ^ c ^ d) + x + t;
|
||||
return ((n << s) | (n >>> (32 - s))) + b;
|
||||
}
|
||||
|
||||
function II(a, b, c, d, x, s, t) {
|
||||
var n = a + (c ^ (b | ~d)) + x + t;
|
||||
return ((n << s) | (n >>> (32 - s))) + b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut function to the hasher's object interface.
|
||||
*
|
||||
* @param {WordArray|string} message The message to hash.
|
||||
*
|
||||
* @return {WordArray} The hash.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var hash = CryptoJS.MD5('message');
|
||||
* var hash = CryptoJS.MD5(wordArray);
|
||||
*/
|
||||
C.MD5 = Hasher._createHelper(MD5);
|
||||
|
||||
/**
|
||||
* Shortcut function to the HMAC's object interface.
|
||||
*
|
||||
* @param {WordArray|string} message The message to hash.
|
||||
* @param {WordArray|string} key The secret key.
|
||||
*
|
||||
* @return {WordArray} The HMAC.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var hmac = CryptoJS.HmacMD5(message, key);
|
||||
*/
|
||||
C.HmacMD5 = Hasher._createHmacHelper(MD5);
|
||||
}(Math));
|
||||
|
||||
|
||||
return CryptoJS.MD5;
|
||||
|
||||
}));
|
@ -341,6 +341,11 @@ function rect(x,y,w,h,color) {
|
||||
curCtx.fillRect(x-(w/2)+camera.x+difx,y-(h/2)+camera.y+dify,w,h);
|
||||
}
|
||||
|
||||
function cartesianRect(x,y,w,h,color) {
|
||||
curCtx.fillStyle = color;
|
||||
curCtx.fillRect(x+camera.x+difx,y+camera.y+dify,w,h);
|
||||
}
|
||||
|
||||
function circle(x,y,r,color) {
|
||||
curCtx.beginPath();
|
||||
curCtx.arc(x+camera.x+difx, y+camera.y+dify, r, 0, 2 * Math.PI, false);
|
||||
@ -348,11 +353,12 @@ function circle(x,y,r,color) {
|
||||
curCtx.fill();
|
||||
}
|
||||
|
||||
function line(x1, y1, x2, y2, color) {
|
||||
function line(x1, y1, x2, y2, weight, color) {
|
||||
curCtx.beginPath();
|
||||
curCtx.style = color;
|
||||
curCtx.strokeStyle = color;
|
||||
curCtx.lineWidth = weight;
|
||||
curCtx.moveTo(x1 + camera.x + difx, y1 + camera.y + dify);
|
||||
curCtx.lineTo(x2 + camera.x + difx, y2 + camera.y + dify);
|
||||
curCtx.lineTo(x2 + camera.x + difx , y2 + camera.y + dify);
|
||||
curCtx.stroke();
|
||||
}
|
||||
|
||||
|
@ -51,6 +51,12 @@ function update() {
|
||||
}
|
||||
|
||||
function draw() {
|
||||
|
||||
// If draw is being called, the user has interacted with the page at least once.
|
||||
// This signal can be used to notify the audio permission handler
|
||||
unlockAudioPermission();
|
||||
|
||||
// Handle game state
|
||||
switch (globalState) {
|
||||
// title screen
|
||||
case globalStates.titleScreen:
|
||||
@ -62,6 +68,7 @@ function draw() {
|
||||
break;
|
||||
// playing
|
||||
case globalStates.playing:
|
||||
camera.zoom = 2;
|
||||
drawWorldBlocks();
|
||||
player.draw();
|
||||
break;
|
||||
@ -93,7 +100,6 @@ function absoluteDraw() {
|
||||
// playing
|
||||
case globalStates.playing:
|
||||
drawPlayingUI();
|
||||
|
||||
break;
|
||||
// paused
|
||||
case globalStates.paused:
|
||||
@ -113,6 +119,25 @@ function onAssetsLoaded() {
|
||||
|
||||
setup(60);
|
||||
|
||||
// Hide the preloader
|
||||
// This should actually run after all assets have been downloaded
|
||||
page_preloader.hide(false);
|
||||
/* Preload actions */
|
||||
|
||||
// To make something happen before the preloader is hidden, add it to this list
|
||||
// The function must take a callback that will be run when the function finishes
|
||||
let actions = [preCacheSounds];
|
||||
let actionsCompleted = 0;
|
||||
|
||||
// Loop through every action, and load it
|
||||
actions.forEach((action) => {
|
||||
|
||||
// Run the action & handle loading
|
||||
action(() => {
|
||||
|
||||
// Incr the number of successfully loaded actions
|
||||
actionsCompleted += 1;
|
||||
|
||||
// If this is the last aciton, hide the loader
|
||||
if (actionsCompleted == actions.length) {
|
||||
page_preloader.hide(false);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
@ -1,44 +1,75 @@
|
||||
|
||||
var breath = 180;
|
||||
var fullBreathTimer = 0;
|
||||
var heartRate = 60;
|
||||
let breath = 180;
|
||||
let fullBreathTimer = 0;
|
||||
let noBreathTimer = 0;
|
||||
let pressure = 50;
|
||||
|
||||
let heartBeat = false;
|
||||
|
||||
var breathMode = {
|
||||
inhale: 0,
|
||||
exhale: 1
|
||||
};
|
||||
|
||||
let currentBreathMode = breathMode.exhale;
|
||||
|
||||
var timeSinceLastBeat = 0;
|
||||
|
||||
|
||||
function updateLife() {
|
||||
|
||||
if(keyDown[k.Z]) {
|
||||
breathe();
|
||||
} else {
|
||||
breath--;
|
||||
if(keyDown[k.w]) {
|
||||
if(breath === 0) currentBreathMode = breathMode.inhale;
|
||||
}
|
||||
|
||||
if(keyPress[k.X]) {
|
||||
if(keyDown[k.s]) {
|
||||
if(breath === constants.lifeFuncs.breath.fullBreath) currentBreathMode = breathMode.exhale;
|
||||
}
|
||||
|
||||
breathe();
|
||||
|
||||
if(keyPress[k.x]) {
|
||||
heartbeat();
|
||||
} else {
|
||||
timeSinceLastBeat++;
|
||||
}
|
||||
|
||||
pressure-=0.25;
|
||||
if(pressure<=0){
|
||||
pressure = 0;
|
||||
}
|
||||
};
|
||||
|
||||
function breathe() {
|
||||
|
||||
breath += 5;
|
||||
if(breath >= 200) {
|
||||
breath = 200;
|
||||
fullBreathTimer++;
|
||||
if(fullBreathTimer >= 60) {
|
||||
//cough and lose breath or something
|
||||
}
|
||||
} else {
|
||||
fullBreathTimer = 0;
|
||||
switch (currentBreathMode) {
|
||||
case breathMode.inhale:
|
||||
breath += 1;
|
||||
if(breath >= constants.lifeFuncs.breath.fullBreath) {
|
||||
breath = constants.lifeFuncs.breath.fullBreath;
|
||||
fullBreathTimer++;
|
||||
if(fullBreathTimer >= 600) {
|
||||
//cough and lose breath or something
|
||||
}
|
||||
} else {
|
||||
fullBreathTimer = 0;
|
||||
}
|
||||
break;
|
||||
case breathMode.exhale:
|
||||
breath -= 2;
|
||||
if(breath <= 0) {
|
||||
breath = 0;
|
||||
noBreathTimer++;
|
||||
if(noBreathTimer >= 300) {
|
||||
//cough and lose breath or something
|
||||
}
|
||||
} else {
|
||||
noBreathTimer = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
function heartbeat() {
|
||||
|
||||
timeSinceLastBeat = 0;
|
||||
|
||||
pressure+=10;
|
||||
if(pressure>=100){
|
||||
pressure = 100;
|
||||
}
|
||||
heartBeat = true;
|
||||
};
|
@ -1,49 +1,88 @@
|
||||
class Player {
|
||||
|
||||
constructor(x, y){
|
||||
constructor(x, y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.w = 10;
|
||||
this.h = 20;
|
||||
this.hipLeft = {x:this.x-5,y:this.y+10};
|
||||
this.hipRight = {x:this.x+5,y:this.y+10};
|
||||
this.leftLeg = new Leg(this.hipLeft.x,this.hipLeft.y,50,-Math.PI/4);
|
||||
this.rightLeg = new Leg(this.hipRight.x, this.hipRight.y, 50, Math.PI/2);
|
||||
this.w = constants.player.width;
|
||||
this.h = constants.player.height;
|
||||
this.hipLeft = { x: this.x - constants.player.hip.offset_x, y: this.y + constants.player.hip.offset_y };
|
||||
this.hipRight = { x: this.x + constants.player.hip.offset_x, y: this.y + constants.player.hip.offset_y };
|
||||
this.leftLeg = new Leg(this.hipLeft.x, this.hipLeft.y, 50, -Math.PI / 4);
|
||||
this.rightLeg = new Leg(this.hipRight.x, this.hipRight.y, 50, Math.PI / 2);
|
||||
this.legSelected = "R";
|
||||
this.shouldMoveLeg = true;
|
||||
this.shouldMoveLeg = false;
|
||||
this.collided = false;
|
||||
this.lastBodyX = x;
|
||||
this.lastBodyY = y;
|
||||
this.hover = { active: false, leg: "R" };
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
Player.prototype.getActiveLeg = function(){
|
||||
if(this.legSelected === "L"){
|
||||
if (this.legSelected === "L") {
|
||||
return this.leftLeg;
|
||||
}
|
||||
return this.rightLeg;
|
||||
}
|
||||
|
||||
Player.prototype.getLockedLeg = function(){
|
||||
if(this.legSelected === "R"){
|
||||
if (this.legSelected === "R") {
|
||||
return this.leftLeg;
|
||||
}
|
||||
return this.rightLeg;
|
||||
}
|
||||
|
||||
// leg has been selected, move leg towards mouse
|
||||
Player.prototype.moveLeg = function(){
|
||||
// Stops if we shouldn't move leg
|
||||
if(!this.shouldMoveLeg){
|
||||
return 0;
|
||||
Player.prototype.update = function() {
|
||||
var curLeg = this.getActiveLeg();
|
||||
|
||||
// select
|
||||
if (this.shouldMoveLeg) {
|
||||
this.moveLeg();
|
||||
if(mousePress[0]) {// if (collidingWithWorld({ x: curLeg.x2, y: curLeg.y2, w: 4, h: 4 })) {
|
||||
if (this.legSelected === "R") {
|
||||
this.leftLeg.angle += pi;
|
||||
} else {
|
||||
this.rightLeg.angle += pi;
|
||||
}
|
||||
this.shouldMoveLeg = false;
|
||||
}
|
||||
// deselect
|
||||
} else {
|
||||
|
||||
var targetPos = mousePosition();
|
||||
var curLeg = this.getActiveLeg();
|
||||
this.hover.active = false;
|
||||
//left
|
||||
if (distanceToLineSegment(this.leftLeg.x, this.leftLeg.y, this.leftLeg.x2, this.leftLeg.y2, targetPos.x, targetPos.y) < constants.player.select_range) {
|
||||
this.hover.active = true;
|
||||
this.hover.leg = "L";
|
||||
if(mousePress[0]) {
|
||||
this.shouldMoveLeg = true;
|
||||
this.legSelected = "L";
|
||||
this.hover.active = false;
|
||||
}
|
||||
// right
|
||||
} else if (distanceToLineSegment(this.rightLeg.x, this.rightLeg.y, this.rightLeg.x2, this.rightLeg.y2, targetPos.x, targetPos.y) < constants.player.select_range) {
|
||||
this.hover.active = true;
|
||||
this.hover.leg = "R";
|
||||
if(mousePress[0]) {
|
||||
this.shouldMoveLeg = true;
|
||||
this.legSelected = "R";
|
||||
this.hover.active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
centerCameraOn(this.x,this.y);
|
||||
}
|
||||
|
||||
|
||||
// leg has been selected, move leg towards mouse
|
||||
Player.prototype.moveLeg = function(){
|
||||
var targetPos = mousePosition();
|
||||
|
||||
// gets active leg & target
|
||||
var curLeg = this.getActiveLeg();
|
||||
var target = mousePos;
|
||||
var target = targetPos;
|
||||
|
||||
// Last leg position
|
||||
var lastX = curLeg.x2;
|
||||
@ -51,8 +90,9 @@ Player.prototype.moveLeg = function(){
|
||||
|
||||
|
||||
// move selected leg towards mouse
|
||||
|
||||
// console.log(curLeg.angle.toPrecision(5),pointTo(curLeg,target).toPrecision(5));
|
||||
curLeg.angle = turn( curLeg.angle,pointTo(curLeg,target),0.1);
|
||||
curLeg.angle = turn(curLeg.angle, pointTo(curLeg, target), constants.player.leg_speed);
|
||||
// var angle = pointTo(curLeg,target);
|
||||
curLeg.x2 = curLeg.x + curLeg.len * Math.cos(curLeg.angle);
|
||||
curLeg.y2 = curLeg.y + curLeg.len * Math.sin(curLeg.angle);
|
||||
@ -74,44 +114,43 @@ Player.prototype.moveLeg = function(){
|
||||
}
|
||||
|
||||
|
||||
if(dist(curLeg,target) > curLeg.len) {
|
||||
if (dist(curLeg, target) > curLeg.len) {
|
||||
// move towards mouse
|
||||
this.x += Math.cos(pointTo(curLeg,target)) * clamp(dist(curLeg,target)/50,1,3);
|
||||
this.x += Math.cos(pointTo(curLeg, target)) * clamp(dist(curLeg, target) / constants.player.movement_divider, 1, constants.player.max_movement_speed);
|
||||
|
||||
this.y += Math.sin(pointTo(curLeg,target)) * clamp(dist(curLeg,target)/50,1,3);
|
||||
this.y += Math.sin(pointTo(curLeg, target)) * clamp(dist(curLeg, target) / constants.player.movement_divider, 1, constants.player.max_movement_speed);
|
||||
this.updateHips();
|
||||
}
|
||||
|
||||
// if leg is right update it accordingly
|
||||
if(this.legSelected === "R") {
|
||||
if (this.legSelected === "R") {
|
||||
// set angle to the locked foot to the locked hip
|
||||
oppLeg = this.getLockedLeg();
|
||||
oppLeg.angle = pointTo({x:oppLeg.x2,y:oppLeg.y2},this.hipRight);
|
||||
oppLeg.angle = pointTo({ x: oppLeg.x2, y: oppLeg.y2 }, this.hipRight);
|
||||
|
||||
|
||||
// snap body to a position where the hip is attached to the leg
|
||||
this.x = (oppLeg.x2 + oppLeg.len * Math.cos(oppLeg.angle)) - 5;
|
||||
this.y = (oppLeg.y2 + oppLeg.len * Math.sin(oppLeg.angle)) - 10;
|
||||
this.x = (oppLeg.x2 + oppLeg.len * Math.cos(oppLeg.angle)) - constants.player.hip.offset_x;
|
||||
this.y = (oppLeg.y2 + oppLeg.len * Math.sin(oppLeg.angle)) - constants.player.hip.offset_y;
|
||||
|
||||
this.updateHips();
|
||||
|
||||
|
||||
// make sure each leg ends at the hips
|
||||
oppLeg.x = this.hipRight.x;
|
||||
oppLeg.y = this.hipRight.y;
|
||||
|
||||
curLeg.x = this.hipLeft.x;
|
||||
curLeg.y = this.hipLeft.y;
|
||||
//console.log(oppLeg.angle)
|
||||
// if leg is left update it accordingly
|
||||
// if leg is left update it accordingly
|
||||
} else {
|
||||
//console.log(curLeg.angle)
|
||||
// set angle to the locked foot to the locked hip
|
||||
// set angle to the locked foot to the locked hip
|
||||
oppLeg = this.getLockedLeg();
|
||||
oppLeg.angle = pointTo({x:oppLeg.x2,y:oppLeg.y2},this.hipLeft);
|
||||
oppLeg.angle = pointTo({ x: oppLeg.x2, y: oppLeg.y2 }, this.hipLeft);
|
||||
|
||||
|
||||
// snap body to a position where the hip is attached to the leg
|
||||
this.x = (oppLeg.x2 + oppLeg.len * Math.cos(oppLeg.angle)) + 5;
|
||||
this.y = (oppLeg.y2 + oppLeg.len * Math.sin(oppLeg.angle)) - 10;
|
||||
this.x = (oppLeg.x2 + oppLeg.len * Math.cos(oppLeg.angle)) + constants.player.hip.offset_x;
|
||||
this.y = (oppLeg.y2 + oppLeg.len * Math.sin(oppLeg.angle)) - constants.player.hip.offset_y;
|
||||
|
||||
this.updateHips();
|
||||
|
||||
@ -132,34 +171,70 @@ Player.prototype.moveLeg = function(){
|
||||
}
|
||||
|
||||
Player.prototype.updateHips = function() {
|
||||
this.hipLeft = {x:this.x-5,y:this.y+10};
|
||||
this.hipRight = {x:this.x+5,y:this.y+10};
|
||||
this.hipLeft = { x: this.x - constants.player.hip.offset_x, y: this.y + constants.player.hip.offset_y };
|
||||
this.hipRight = { x: this.x + constants.player.hip.offset_x, y: this.y + constants.player.hip.offset_y };
|
||||
}
|
||||
|
||||
|
||||
Player.prototype.draw = function() {
|
||||
rect(this.x, this.y, this.w, this.h,"green");
|
||||
|
||||
this.leftLeg.draw();
|
||||
this.rightLeg.draw();
|
||||
rect(this.x, this.y, this.w, this.h, "green");
|
||||
if(this.hover.active) {
|
||||
if(this.hover.leg === "R") {
|
||||
curCtx.shadowBlur = 10;
|
||||
curCtx.shadowColor = "yellow";
|
||||
curCtx.lineWidth = 3;
|
||||
this.rightLeg.draw();
|
||||
curCtx.lineWidth = 1;
|
||||
curCtx.shadowBlur = 0;
|
||||
curCtx.shadowColor = "black";
|
||||
this.leftLeg.draw();
|
||||
} else {
|
||||
curCtx.shadowBlur = 10;
|
||||
curCtx.shadowColor = "yellow";
|
||||
curCtx.lineWidth = 3;
|
||||
this.leftLeg.draw();
|
||||
curCtx.lineWidth = 1;
|
||||
curCtx.shadowBlur = 0;
|
||||
curCtx.shadowColor = "black";
|
||||
this.rightLeg.draw();
|
||||
}
|
||||
} else {
|
||||
this.leftLeg.draw();
|
||||
this.rightLeg.draw();
|
||||
}
|
||||
}
|
||||
|
||||
Player.prototype.update = function() {
|
||||
this.moveLeg();
|
||||
var curLeg = this.getActiveLeg();
|
||||
if(mousePress[0] || this.collided){
|
||||
if(this.legSelected === "R"){
|
||||
this.legSelected = "L";
|
||||
this.leftLeg.angle += pi;
|
||||
} else {
|
||||
this.legSelected = "R";
|
||||
this.rightLeg.angle += pi;
|
||||
}
|
||||
|
||||
this.collided = false;
|
||||
|
||||
// https://github.com/scottglz/distance-to-line-segment/blob/master/index.js
|
||||
function distanceSquaredToLineSegment2(lx1, ly1, ldx, ldy, lineLengthSquared, px, py) {
|
||||
var t; // t===0 at line pt 1 and t ===1 at line pt 2
|
||||
if (!lineLengthSquared) {
|
||||
// 0-length line segment. Any t will return same result
|
||||
t = 0;
|
||||
}
|
||||
centerCameraOn(this.x,this.y);
|
||||
else {
|
||||
t = ((px - lx1) * ldx + (py - ly1) * ldy) / lineLengthSquared;
|
||||
|
||||
if (t < 0)
|
||||
t = 0;
|
||||
else if (t > 1)
|
||||
t = 1;
|
||||
}
|
||||
|
||||
var lx = lx1 + t * ldx,
|
||||
ly = ly1 + t * ldy,
|
||||
dx = px - lx,
|
||||
dy = py - ly;
|
||||
return dx * dx + dy * dy;
|
||||
}
|
||||
function distanceSquaredToLineSegment(lx1, ly1, lx2, ly2, px, py) {
|
||||
var ldx = lx2 - lx1,
|
||||
ldy = ly2 - ly1,
|
||||
lineLengthSquared = ldx * ldx + ldy * ldy;
|
||||
return distanceSquaredToLineSegment2(lx1, ly1, ldx, ldy, lineLengthSquared, px, py);
|
||||
}
|
||||
|
||||
function distanceToLineSegment(lx1, ly1, lx2, ly2, px, py) {
|
||||
return Math.sqrt(distanceSquaredToLineSegment(lx1, ly1, lx2, ly2, px, py));
|
||||
}
|
||||
|
||||
|
||||
|
@ -3,4 +3,6 @@ function handlePlaying() {
|
||||
if(keyPress[k.BACKSLASH]) {
|
||||
globalState = globalStates.building;
|
||||
}
|
||||
|
||||
updateLife();
|
||||
}
|
22
docs/assets/js/sounds/permissionhandler.js
Normal file
22
docs/assets/js/sounds/permissionhandler.js
Normal file
@ -0,0 +1,22 @@
|
||||
/**
|
||||
* This file just exists to keep track of weather the user has interacted with the
|
||||
* webpage. Most browsers will block autoplay if no interaction has been made.
|
||||
*/
|
||||
|
||||
|
||||
// Tracker for permission unlock
|
||||
let _audioPermUnlock = false;
|
||||
|
||||
/**
|
||||
* Call this when the user interacts with the page
|
||||
*/
|
||||
function unlockAudioPermission() {
|
||||
_audioPermUnlock = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if autoplay is enabled
|
||||
*/
|
||||
function canPlayAudio() {
|
||||
return _audioPermUnlock;
|
||||
}
|
164
docs/assets/js/sounds/soundcontext.js
Normal file
164
docs/assets/js/sounds/soundcontext.js
Normal file
@ -0,0 +1,164 @@
|
||||
/**
|
||||
* This file contains classes for playing sounds.
|
||||
* The SoundContext works by providing multiple audio channels,
|
||||
* just like an old ATARI, or even GameBoy sound system. Each
|
||||
* channel can be controlled individually.
|
||||
*
|
||||
* ---- Usage ----
|
||||
*
|
||||
* // Load all sounds
|
||||
* preCacheSounds(()=>{
|
||||
* // Code can be run here as soon as all sounds are loaded
|
||||
* // ...
|
||||
* });
|
||||
*
|
||||
* // Play a sound using channels
|
||||
* globalSoundContext.playSound(globalSoundContext.channels.<channel>, sounds.<soundname>);
|
||||
*
|
||||
* // Just play a sound now
|
||||
* globalSoundContext.playSoundNow(sounds.<soundname>);
|
||||
*
|
||||
* // Stop a channel
|
||||
* globalSoundContext.mute(globalSoundContext.channels.<channel>);
|
||||
*/
|
||||
|
||||
/**
|
||||
* A sound channel can play 1 sound at a time, and supports sound queueing
|
||||
*/
|
||||
class _SoundChannel {
|
||||
|
||||
/**
|
||||
* Create a sound channel
|
||||
* @param {number} max_queue_size Maximum number of sounds that can be queued before sounds are skipped
|
||||
*/
|
||||
constructor(max_queue_size) {
|
||||
this.max_size = max_queue_size
|
||||
this.sound_queue = []
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a snippet to the queue
|
||||
* @param {SoundSnippet} snippet
|
||||
*/
|
||||
enqueue(snippet) {
|
||||
|
||||
console.log(this.sound_queue)
|
||||
|
||||
// If the queue is full, cut out the next sound in the queue to make room
|
||||
// if (this.sound_queue.length > this.max_size) {
|
||||
// this.sound_queue.splice(1, 1);
|
||||
// }
|
||||
|
||||
// Append the sound to the queue
|
||||
this.sound_queue.push(snippet);
|
||||
|
||||
// If this is the first sound in the queue, spawn a watcher job, and play it
|
||||
if (this.sound_queue.length == 1) {
|
||||
this.sound_queue[0].play();
|
||||
this._spawnWatcher(this.sound_queue[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a job to run when the sound finishes to remove the sound from the queue
|
||||
* @param {SoundSnippet} snippet
|
||||
*/
|
||||
_spawnWatcher(snippet) {
|
||||
|
||||
// Read the snippet length
|
||||
let length = snippet.getLengthSeconds();
|
||||
|
||||
// Spawn a clean action for that time in the future
|
||||
setTimeout(() => {
|
||||
this._cleanQueue(snippet);
|
||||
}, length * 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be run when every sound finishes. This will remove that
|
||||
* sound from from the queue, start the next sound, and spawn a
|
||||
* new watcher for that sound.
|
||||
* @param {SoundSnippet} snippet
|
||||
*/
|
||||
_cleanQueue(snippet) {
|
||||
|
||||
// Get the snippet hash
|
||||
let hash = snippet.getHash();
|
||||
|
||||
// Make sure there are actually sounds playing
|
||||
if (this.sound_queue.length > 0) {
|
||||
|
||||
// If the first snippet in the queue matches this hash, remove it.
|
||||
// If not, something must have happened. Just ignore it, and move on
|
||||
if (this.sound_queue[0].getHash() == hash) {
|
||||
|
||||
// Popoff the snippet
|
||||
this.sound_queue.shift();
|
||||
|
||||
}
|
||||
|
||||
// Spawn a watcher for the next sound & play it
|
||||
if (this.sound_queue.length > 0) {
|
||||
this.sound_queue[0].play();
|
||||
this._spawnWatcher(this.sound_queue[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the entire queue, and stop all sounds
|
||||
*/
|
||||
clear() {
|
||||
|
||||
// Stop every sound
|
||||
this.sound_queue.forEach((sound) => {
|
||||
sound.stop();
|
||||
})
|
||||
|
||||
// Clear the queue
|
||||
this.sound_queue = [];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class _SoundContext {
|
||||
|
||||
constructor() {
|
||||
|
||||
// Define all sound channels
|
||||
this.channels = {
|
||||
bgm: new _SoundChannel(3)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Play a sound in a channel
|
||||
* @param {*} channel
|
||||
* @param {SoundSnippet} snippet
|
||||
*/
|
||||
playSound(channel, snippet) {
|
||||
console.log(`[SoundContext] Playing snippet: ${snippet.getName()}`);
|
||||
channel.enqueue(snippet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Play a sound right now
|
||||
* @param {SoundSnippet} snippet
|
||||
*/
|
||||
playSoundNow(snippet) {
|
||||
snippet.play();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop all sounds in a channel
|
||||
* @param {*} channel
|
||||
*/
|
||||
mute(channel) {
|
||||
channel.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// The global context for sounds
|
||||
let globalSoundContext = new _SoundContext();
|
63
docs/assets/js/sounds/sounds.js
Normal file
63
docs/assets/js/sounds/sounds.js
Normal file
@ -0,0 +1,63 @@
|
||||
/**
|
||||
* This file handles all sound assets, and loading them
|
||||
* To add a new sound asset:
|
||||
* 1) Make a mapping from the asset name to it's filename in soundAssetMap
|
||||
* 2) Define the SoundSnippet in soundAssets
|
||||
*
|
||||
* The preloader will handle asset loading for you.
|
||||
* Make sure to check the console for any errors with loading your file
|
||||
*/
|
||||
|
||||
|
||||
// A mapping of asset names to their files
|
||||
// This exists to give nicer names to files
|
||||
let soundAssetMap = {
|
||||
"debug-ding":"/assets/sounds/debug-ding.mp3"
|
||||
}
|
||||
|
||||
// All available sounds
|
||||
let soundAssets = {
|
||||
debug_ding: new SoundSnippet("debug-ding")
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache all sounds in browser, then notify a callback of success
|
||||
* @param {function} callback Callback for completion
|
||||
*/
|
||||
function preCacheSounds(callback) {
|
||||
|
||||
// Counter for number of sounds cached
|
||||
let cachedCount = 0;
|
||||
|
||||
Object.keys(soundAssets).forEach((key) => {
|
||||
|
||||
// Get the SoundSnippet
|
||||
let sound = soundAssets[key];
|
||||
|
||||
// Cache the sound
|
||||
sound.cache(() => {
|
||||
|
||||
// Incr the cache count
|
||||
cachedCount += 1;
|
||||
|
||||
// If this is the last sound, fire off the callback
|
||||
if (cachedCount == Object.keys(soundAssets).length) {
|
||||
callback();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
// Spawn a notifier for loading issues
|
||||
setTimeout(() => {
|
||||
|
||||
// If not all sounds have been cached by the time this is called, send a warning
|
||||
if (cachedCount < Object.keys(soundAssets).length) {
|
||||
console.warn(`[preCacheSounds] Only ${cachedCount} of ${Object.keys(soundAssets).length} sounds have been cached after 2 seconds. Is there a missing asset? or is the user on a slow connection?`);
|
||||
}
|
||||
|
||||
}, 2000);
|
||||
|
||||
}
|
81
docs/assets/js/sounds/soundsnippet.js
Normal file
81
docs/assets/js/sounds/soundsnippet.js
Normal file
@ -0,0 +1,81 @@
|
||||
|
||||
// Counter for hash generation
|
||||
let _hashCounter = 0;
|
||||
|
||||
class SoundSnippet {
|
||||
|
||||
/**
|
||||
* Load a sound asset to a snipper
|
||||
* @param {string} asset_name Asset name as defined in soundassetmap.js
|
||||
*/
|
||||
constructor(asset_name) {
|
||||
|
||||
// Store asset name
|
||||
this.asset_name = asset_name;
|
||||
|
||||
// Compute an asset hash
|
||||
this.asset_hash = CryptoJS.MD5(`ASSET:${asset_name}::${_hashCounter}`);
|
||||
_hashCounter += 1;
|
||||
|
||||
// Read actual asset path
|
||||
this.assetPath = soundAssetMap[asset_name];
|
||||
|
||||
// Set up the audio object
|
||||
this.audio = new Audio();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache this sound, then notify a callback of completion
|
||||
* @param {function} callback callback to notify
|
||||
*/
|
||||
cache(callback) {
|
||||
|
||||
// Set the audio SRC
|
||||
this.audio.src = this.assetPath;
|
||||
|
||||
// Create a callback for loading finished
|
||||
this.audio.addEventListener("loadeddata", callback, true);
|
||||
}
|
||||
|
||||
|
||||
play() {
|
||||
|
||||
// If autoplay is disabled, we notify the console
|
||||
if (canPlayAudio()) {
|
||||
// Play the snippet
|
||||
this.audio.play();
|
||||
} else {
|
||||
console.warn("[SoundSnippet] Tried to play audio with autoplay disabled. The user must press the play button before you can play audio");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
stop() {
|
||||
|
||||
// Stop the snippet
|
||||
this.audio.stop();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sound length in seconds
|
||||
*/
|
||||
getLengthSeconds() {
|
||||
return this.audio.duration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the asset name
|
||||
*/
|
||||
getName() {
|
||||
return this.asset_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this asset's hash. This can be used for comparing objects efficiently.
|
||||
*/
|
||||
getHash() {
|
||||
return this.asset_hash;
|
||||
}
|
||||
}
|
BIN
docs/assets/sounds/debug-ding.mp3
Normal file
BIN
docs/assets/sounds/debug-ding.mp3
Normal file
Binary file not shown.
@ -24,6 +24,10 @@
|
||||
<!-- Graphics Library -->
|
||||
<script src="assets/js/game.js"></script>
|
||||
|
||||
<!-- Crypto -->
|
||||
<script src="assets/js/crypto/core.min.js"></script>
|
||||
<script src="assets/js/crypto/md5.js"></script>
|
||||
|
||||
<!-- Constants & Globals -->
|
||||
<script src="assets/js/constants.js"></script>
|
||||
<script src="assets/js/utils.js"></script>
|
||||
@ -34,17 +38,23 @@
|
||||
|
||||
<script src="assets/js/player/leg.js"></script>
|
||||
<script src="assets/js/player/player.js"></script>
|
||||
<!-- <script src="assets/js/player/lifeFunctions.js"></script> -->
|
||||
<script src="assets/js/player/lifeFunctions.js"></script>
|
||||
|
||||
<script src="assets/js/playing/playing.js"></script>
|
||||
|
||||
<script src="assets/js/titleScreen/titleScreen.js"></script>
|
||||
<!-- <script src="assets/js/titleScreen/titleScreen.js"></script> -->
|
||||
<script src="assets/js/UI/ui.js"></script>
|
||||
|
||||
<!-- Webpage -->
|
||||
<script src="assets/js/injection/cssinjector.js"></script>
|
||||
<script src="assets/js/preloader/preloader.js"></script>
|
||||
|
||||
<!-- Sounds -->
|
||||
<script src="assets/js/sounds/soundsnippet.js"></script>
|
||||
<script src="assets/js/sounds/sounds.js"></script>
|
||||
<script src="assets/js/sounds/soundcontext.js"></script>
|
||||
<script src="assets/js/sounds/permissionhandler.js"></script>
|
||||
|
||||
<!-- Game -->
|
||||
<script src="assets/js/index.js"></script>
|
||||
</body>
|
||||
|
Reference in New Issue
Block a user