-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgame.js
122 lines (108 loc) · 3.45 KB
/
game.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
// vim: set sw=4:ts=4:
class Game {
constructor() {
this.levels = [];
this.idxCurrentLevel = 0;
this.currentLevel = {};
this.audioLibrary = new AudioLibrary([
{ name: "Snare", file: "snare.wav" },
{ name: "Kick", file: "kick.wav" },
{ name: "Open Hi-Hat", file: "hihat_open.wav" },
{ name: "Hi-Hat", file: "hihat_closed.wav" },
{ name: "Crash", file: "crash.wav" },
{ name: "Cowbell", file: "cowbell.wav" },
{ name: "Stick", file: "stick.wav" },
]);
}
isReady() {
return this.audioLibrary.ready;
}
nextLevel() {
if (this.hasNextLevel()) {
this.loadLevel(++this.idxCurrentLevel);
} else {
console.error("There is no next level, sorry!");
}
}
hasNextLevel() {
const lastLevel = this.levels.length - 1;
return this.idxCurrentLevel < lastLevel;
}
loadLevel(idxLevel) {
this.idxCurrentLevel = idxLevel || 0;
this.currentLevel = { ...this.levels[this.idxCurrentLevel] };
let amountOfSteps = 0;
if (!this.currentLevel.pattern) {
throw Error("Level has no pattern configured");
}
for (let i = 0; i < this.currentLevel.pattern.length; i++) {
const pattern = this.currentLevel.pattern[i];
if (i == 0) {
amountOfSteps = pattern.steps.length;
}
if (amountOfSteps != pattern.steps.length) {
console.error(
"Unexpected difference of number of steps: " +
amountOfSteps +
" != " +
pattern.steps.length
);
}
}
this.currentLevel.amountOfSteps = amountOfSteps;
console.log("Initialized level with amountOfSteps =", amountOfSteps);
}
load(callback) {
const request = new XMLHttpRequest();
request.open("GET", "./levels.json");
request.onload = () => {
this.levels = JSON.parse(request.response);
this.loadLevel(0);
callback();
};
request.onerror = function () {
throw Error("BufferLoader: XHR error");
};
request.send();
}
playTrackSampleOnce(track) {
this.audioLibrary.playSampleAfter(track, 0);
}
isCorrectPattern(enteredPattern) {
const matches = this.currentLevel.pattern.map((patt) => {
const enteredSteps = enteredPattern[patt.name];
return JSON.stringify(enteredSteps) == JSON.stringify(patt.steps);
});
return matches.reduce((o, v) => o && v, true);
}
playCurrentLevelLoop({ tickCallback, finishCallback }) {
/*
* Play drump loop for the current level, calling tickCallback for every
* pattern step along the way, and finishCallback when it's done playing.
*/
if (!this.isReady()) {
console.log("Not ready yet!");
return;
}
const level = this.currentLevel;
const startTime = this.audioLibrary.getCurrentTime();
const repeat = 2;
const beatDuration = 60 / level.bpm;
const barDuration = beatDuration * level.amountOfSteps;
for (let currentBar = 0; currentBar < repeat; currentBar++) {
for (let step = 0; step < level.amountOfSteps; step++) {
const durationSecs = currentBar * barDuration + step * beatDuration;
setTimeout(tickCallback, durationSecs * 1000, step);
level.pattern.forEach((pattern) => {
if (pattern.steps[step] == 1) {
this.audioLibrary.playSampleAfter(
pattern.name,
0.05 + startTime + durationSecs
);
}
});
}
}
setTimeout(finishCallback, barDuration * repeat * 1000);
}
}