Merge remote-tracking branch 'origin/master' into leg-movement-2

This commit is contained in:
rsninja722 2020-04-18 15:57:41 -04:00
commit ae9a1d545a
15 changed files with 816 additions and 58 deletions

5
.gitignore vendored
View File

@ -2,4 +2,7 @@
docs/_site/*
docs/.sass-cache/*
docs/.jekyll-cache/*
docs/node_modules
docs/node_modules
# idea
.idea

View File

@ -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);

View File

@ -26,11 +26,20 @@ 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
}
},
lifeFuncs:{
breath:{
fullBreath: 200
},
cardio:{
optimalPressure: 50
}
},
player:{

1
docs/assets/js/crypto/core.min.js vendored Normal file
View 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});

View 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;
}));

View File

@ -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();
}

View File

@ -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:
@ -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);
}
})
});

View File

@ -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;
};

View File

@ -3,4 +3,6 @@ function handlePlaying() {
if(keyPress[k.BACKSLASH]) {
globalState = globalStates.building;
}
updateLife();
}

View 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;
}

View 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();

View 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);
}

View 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;
}
}

Binary file not shown.

View File

@ -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>
@ -32,19 +36,25 @@
<!-- <script src="assets/js/world/build.js"></script> -->
<script src="assets/js/world/level.js"></script>
<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/player.js"></script>
<script src="assets/js/player/leg.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>