Add a first pass at a soundtrack, with 5 tracks wow!
This commit is contained in:
parent
0535cbc0bf
commit
b7ceafc5a1
14
index.html
14
index.html
@ -72,12 +72,22 @@
|
||||
<output></output>
|
||||
</div>
|
||||
<div class="inventory"></div>
|
||||
<div id="player-music">
|
||||
<div id="player-music-left">
|
||||
🎵 <a id="player-music-title">title</a> by <a id="player-music-author">author</a>
|
||||
</div>
|
||||
<div id="player-music-right">
|
||||
<input id="player-music-volume" type="range" min="0" max="1" step="0.05" value="1">
|
||||
<input id="player-music-unmute" type="checkbox" checked>
|
||||
</div>
|
||||
<audio loop preload="auto">
|
||||
</div>
|
||||
<div class="controls">
|
||||
<div class="play-controls">
|
||||
<button class="control-pause" type="button">Pause</button>
|
||||
<button class="control-pause" type="button">Pause (p)</button>
|
||||
<button class="control-restart" type="button">Restart</button>
|
||||
<button class="control-undo" type="button">Undo</button>
|
||||
<button class="control-rewind" type="button">Rewind</button>
|
||||
<button class="control-rewind" type="button">Rewind (z)</button>
|
||||
</div>
|
||||
<div class="demo-controls">
|
||||
<button class="demo-play" type="button">View replay</button>
|
||||
|
||||
102
js/main.js
102
js/main.js
@ -6,6 +6,7 @@ import * as dat from './format-dat.js';
|
||||
import * as format_util from './format-util.js';
|
||||
import { Level } from './game.js';
|
||||
import CanvasRenderer from './renderer-canvas.js';
|
||||
import SOUNDTRACK from './soundtrack.js';
|
||||
import { Tileset, CC2_TILESET_LAYOUT, LL_TILESET_LAYOUT, TILE_WORLD_TILESET_LAYOUT } from './tileset.js';
|
||||
import TILE_TYPES from './tiletypes.js';
|
||||
import { random_choice, mk, mk_svg, promise_event, fetch, walk_grid } from './util.js';
|
||||
@ -254,6 +255,36 @@ class Player extends PrimaryView {
|
||||
this.input_el = this.root.querySelector('.input');
|
||||
this.demo_el = this.root.querySelector('.demo');
|
||||
|
||||
this.music_el = this.root.querySelector('#player-music');
|
||||
this.music_audio_el = this.music_el.querySelector('audio');
|
||||
this.music_index = null;
|
||||
let volume_el = this.music_el.querySelector('#player-music-volume');
|
||||
this.music_audio_el.volume = this.conductor.options.music_volume ?? 1.0;
|
||||
volume_el.value = this.music_audio_el.volume;
|
||||
volume_el.addEventListener('input', ev => {
|
||||
let volume = ev.target.value;
|
||||
this.conductor.options.music_volume = volume;
|
||||
this.conductor.save_stash();
|
||||
|
||||
this.music_audio_el.volume = ev.target.value;
|
||||
});
|
||||
let enabled_el = this.music_el.querySelector('#player-music-unmute');
|
||||
this.music_enabled = this.conductor.options.music_enabled ?? true;
|
||||
enabled_el.checked = this.music_enabled;
|
||||
enabled_el.addEventListener('change', ev => {
|
||||
this.music_enabled = ev.target.checked;
|
||||
this.conductor.options.music_enabled = this.music_enabled;
|
||||
this.conductor.save_stash();
|
||||
|
||||
// TODO also hide most of the music stuff
|
||||
if (this.music_enabled) {
|
||||
this.update_music_playback_state();
|
||||
}
|
||||
else {
|
||||
this.music_audio_el.pause();
|
||||
}
|
||||
});
|
||||
|
||||
// Bind buttons
|
||||
this.pause_button = this.root.querySelector('.controls .control-pause');
|
||||
this.pause_button.addEventListener('click', ev => {
|
||||
@ -467,6 +498,8 @@ class Player extends PrimaryView {
|
||||
this.level = new Level(stored_level, this.compat);
|
||||
this.renderer.set_level(this.level);
|
||||
this.root.classList.toggle('--has-demo', !!this.level.stored_level.demo);
|
||||
// TODO base this on a hash of the UA + some identifier for the pack + the level index. StoredLevel doesn't know its own index atm...
|
||||
this.change_music(Math.floor(Math.random() * SOUNDTRACK.length));
|
||||
this._clear_state();
|
||||
}
|
||||
|
||||
@ -845,6 +878,8 @@ class Player extends PrimaryView {
|
||||
this.renderer.use_rewind_effect = false;
|
||||
}
|
||||
|
||||
this.update_music_playback_state();
|
||||
|
||||
// The advance and redraw methods run in a loop, but they cancel
|
||||
// themselves if the game isn't running, so restart them here
|
||||
if (this.state === 'playing' || this.state === 'rewinding') {
|
||||
@ -857,6 +892,56 @@ class Player extends PrimaryView {
|
||||
}
|
||||
}
|
||||
|
||||
// Music stuff
|
||||
|
||||
change_music(index) {
|
||||
if (index === this.music_index)
|
||||
return;
|
||||
this.music_index = index;
|
||||
|
||||
let track = SOUNDTRACK[index];
|
||||
this.music_audio_el.src = track.path;
|
||||
|
||||
let title_el = this.music_el.querySelector('#player-music-title');
|
||||
title_el.textContent = track.title;
|
||||
if (track.beepbox) {
|
||||
title_el.setAttribute('href', track.beepbox);
|
||||
}
|
||||
else {
|
||||
title_el.removeAttribute('href');
|
||||
}
|
||||
|
||||
let author_el = this.music_el.querySelector('#player-music-author');
|
||||
author_el.textContent = track.author;
|
||||
if (track.twitter) {
|
||||
author_el.setAttribute('href', 'https://twitter.com/' + track.twitter);
|
||||
}
|
||||
else {
|
||||
author_el.removeAttribute('href');
|
||||
}
|
||||
}
|
||||
|
||||
update_music_playback_state() {
|
||||
if (! this.music_enabled)
|
||||
return;
|
||||
|
||||
// Audio tends to match the game state
|
||||
// TODO rewind audio when rewinding the game? would need to use the audio api, so high effort low reward
|
||||
if (this.state === 'waiting') {
|
||||
this.music_audio_el.pause();
|
||||
this.music_audio_el.currentTime = 0;
|
||||
}
|
||||
if (this.state === 'playing' || this.state === 'rewinding') {
|
||||
this.music_audio_el.play();
|
||||
}
|
||||
else if (this.state === 'paused') {
|
||||
this.music_audio_el.pause();
|
||||
}
|
||||
else if (this.state === 'stopped') {
|
||||
this.music_audio_el.pause();
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-size the game canvas to fit the screen, if possible
|
||||
adjust_scale() {
|
||||
// TODO make this optional
|
||||
@ -1645,11 +1730,22 @@ class LevelBrowserOverlay extends DialogOverlay {
|
||||
}
|
||||
|
||||
// Central dispatcher of what we're doing and what we've got loaded
|
||||
const STORAGE_KEY = "Lexy's Labyrinth";
|
||||
class Conductor {
|
||||
constructor(tileset) {
|
||||
this.stored_game = null;
|
||||
this.tileset = tileset;
|
||||
// TODO options and whatnot should go here too
|
||||
|
||||
this.stash = JSON.parse(window.localStorage.getItem(STORAGE_KEY));
|
||||
// TODO more robust way to ensure this is shaped how i expect?
|
||||
if (! this.stash) {
|
||||
this.stash = {};
|
||||
}
|
||||
if (! this.stash.options) {
|
||||
this.stash.options = {};
|
||||
}
|
||||
// Handy aliases
|
||||
this.options = this.stash.options;
|
||||
|
||||
this.splash = new Splash(this);
|
||||
this.editor = new Editor(this);
|
||||
@ -1767,6 +1863,10 @@ class Conductor {
|
||||
this.nav_prev_button.disabled = !this.stored_game || this.level_index <= 0;
|
||||
this.nav_next_button.disabled = !this.stored_game || this.level_index >= this.stored_game.levels.length;
|
||||
}
|
||||
|
||||
save_stash() {
|
||||
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(this.stash));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
31
js/soundtrack.js
Normal file
31
js/soundtrack.js
Normal file
@ -0,0 +1,31 @@
|
||||
export default [{
|
||||
title: "chiptune challenged",
|
||||
author: "Eevee",
|
||||
twitter: "eevee",
|
||||
beepbox: 'https://www.beepbox.co/#8n31s0k0l00e0it3um1afg0ij0fi0r1o1330T1v2L4u01q1d5f7y0z6C1c0A1F2B4V6Q0090Pf519E0181T0v2L4u00q1d5f6y1z7C2w8c1h0T0v0L4u00q1d5f4y4z8C0w6c2h0T4v3L4uf0q1z6666ji8k8k3jSBKSJJAArriiiiii07JCABrzrrrrrrr00YrkqHrsrrrrjr005zrAqzrjzrrqr1jRjrqGGrrzsrsA099ijrABJJJIAzrrtirqrqjqixzsrAjrqjiqaqqysttAJqjikikrizrHtBJJAzArzrIsRCITKSS099ijrAJS____Qg99habbCAYrDzh00b24gh144xiz608wz608gy44h15a8oh3a007M289j628p0y4gUgy48gh14g10328o0p2b1FE-omKmHAWGqGEeFdjUbibAmRmlSRkRlmRpwJ8L5HbpiKjGFGGwWIMmAn8JGIHJGFGGJGP1qhushTAt97uhQAs2hVpyVh7ihQAtp7ihRAumbkbpH-xoeWZGCGHqjk-2QyV6RpnJkRlrmobibOdGOLqFGGBn51pcnb-_G_GCGGvPNrNg0idAzBy5CFMO0nbLbInbLbInbJCO-XpEZkwbSieCmqDUsnbLbInbLbInbLwG_z_8V7GBYnoKnunoKnunoKnunoKnuz46rF7aA6XEKnunoKnunoKnu5nyX_O91GmOf9CTmpvt5PtJTdQndSTsThsTrtPt5Ptxi2_Oek8btPt5PtJTdQndSTsThsTsUxwy2MwJSi8btAO2TpcwJShWCgsnoKnunoKnunoKrLf_P_B-EA76WbBTBSbBTBSbBTMlrMOA17jn_MBtBtttpWe02PsvXcbjdHWZITjUtlPSrFPrLsRPnZ1v2RJ_bP1usR8UZd7XnNRMsIR_lAtIAtTndQ-Jf7HWq_CPqDUllesXupPd7RsbHCU_z0-sPARtlTPe9KfXUOL3VWVKDAFIWtOqVioNUK0FKDceDIeDQCKCRSaznGduCzecKwUewzEcKwOW3wW2ewOZ1BQ71QmkAtN7ipt1Mt5l9714uAOW3MKwOW3wW2ewOZ17ghM97shR4t17khR4tx7ghQCnihQ5Z5d000',
|
||||
path: 'music/chiptune-challenged.ogg',
|
||||
}, {
|
||||
title: "random access melody",
|
||||
author: "Ringo",
|
||||
twitter: "SheepyRingo",
|
||||
beepbox: 'https://beepbox.co/#8n31sbk0l00e0ft2Km1a7g0fj07i0r1o3210T0v0L4u00q0d0f8y0z1C2w2c0h0T0v1L4u11q0d0f8y0z1C2w1c0h0T1v0L4u19q1d2fay0z1C3c2A5F4B0V1Q0202PeebbE0011T3v1L4u03q0d2f3y6z1C0S-JIAArrrAIjqirpb4zgid18Q4zgid18Q4zgid18Q4zhmu5pU4h4h4x4h4h8p24rIQue4nQOnQOnQOnQOhRx4uCi-Ci-Ci-Ci-Ciek38ZcBZcBZcBZcBZcAs22hWpbWpbWpbWpbWp8X_8MFEZ8zE8W2ewzE8W2ewzK8Z17ghQ4t17ghQ4t17a4uwzE8W2ewzE8W2ewzwifghQ4t17ghQ4t17ghRAoldN_LOkezqCVnQYlAtEGyZ5FKDUFE-aGGGGGGkR_dsJlmcLnHInP9kWXhUGyW9FE-qqGGGGGkR_tllvy9Dy9EOzaqYVja50OddupkCza8YCGHlAAukPnBlBkr56Pg0',
|
||||
path: 'music/random-access-melody.ogg',
|
||||
}, {
|
||||
title: "shanty jig",
|
||||
author: "ubuntor",
|
||||
twitter: "ubuntor",
|
||||
beepbox: 'https://beepbox.co/#8n41s7k7l00e0jt22m1a7g0jj07i0r2o32120T0v1L4u12q1d1f7y1z1C0w2c0h2T5v1L4ua6q1d2f5y4z1C0c4h0HYr901i8ah00000T5v3L7u05q1d1f7y1z7C1c0h0HT_QT_ItRAJJAAzT1v1L1u40q1d5f9y1z6C1c0A4F2B6V6Q0068Pf624E0111T4v1L4u04q1z6666ji8k8k3jSBKSJJAArriiiiii07JCABrzrrrrrrr00YrkqHrsrrrrjr005zrAqzrjzrrqr1jRjrqGGrrzsrsA099ijrABJJJIAzrrtirqrqjqixzsrAjrqjiqaqqysttAJqjikikrizrHtBJJAzArzrIsRCITKSS099ijrAJS____Qg99habbCAYrDzh00b000i4QlD4xc0008j4x95pN8j4xc00x8jhmsi4N80018i4zgS4x8i5h8i4x8j4x8i4x8p2efFDNFeGGhIGwrGqGX9i2waGGWqrardJczlnplv0Mc51i5kzjrjlrtvr9qFeiGAraHHHUa1wapaKSGGGCwDPeAux4X6xX6NECNIq58lkX6NIr6NGmNIrds3wqGrHWrbqpqhFLJIFGFJFGFGFTiddldc9jjrtjtjljjllnpn9iuPoSdPoSdDoSdLoPqGcyzoEEEEEEEkPWZY6f1ll3RQRgBBkbtllnljplrKGG7IGG7HFGIIGGHIGIIFGGFRRm3Sll3RQRmmlllSlmmlml3XaqxXaGxWWqHbaHbaHbaGqGHd5dwZBlEZRdltBlRtBlltdlleWGQuOGEuKCGOOGOOGOOGCGGPhjofplqftjlnpljlljlljlliFDNaaaNW1W1W860FGBEguwuwuynETkkm3Q3Q3QmTgddltQB0llQRlXWab1W1W1WbrHWqGXXHWGWqGZZ55wZ0Z0Z5JRdkFufgfgfhbQo6yyMuwuwuyKhGBbWomC6FGFHFQkkm3Q3Q3QllQRiJUZ0Z0Z4MkrGa88uwuyKSW-1EifhgytyyyyyyyxjfFQ9DEifhgF3xA9D-EjfgAuyzye4qe8Uyxi73oUhEUzyaeoYnhN74kshNt74shhN75QshN52Ad6MEzxQr52h-uEERkthN57ss0QshN574slhN74ku4sphN74ksNUiEUzyae8UKzye8EUzzae8UyzyecEUzyae8YjhN74kshNd74shhN76QshN57x29Saaaaaap-a4PQ97EEYoYtEYoYoEUzyqe8Uyzye8EUzyae8bcVRzFD8Fj6jhIPj8-NjIQkkpdd5ejhIPjeg7APjdcQPFDoEOpSacCqpOadzpDoEEEOpOaaacCsyz9CysCz9DoO9EDj8-hjIQrcQOfIkXd556P8Z4zQifj8ZczQPh80',
|
||||
path: 'music/shanty-jig.ogg',
|
||||
}, {
|
||||
title: "Contemplating the Groove",
|
||||
author: "triplebatman",
|
||||
twitter: "doublebatman",
|
||||
beepbox: 'https://beepbox.co/#8n31sbk0l00e0lt2km1a7g0lj0ai0r1o3210T0v0L4u00q1d2f7y1z1C0w2c0h7T1v2L4u01q1d2f7y0z1C0c2A1F0B2VaQ0950Pc454E0112T1v4L4u01q3d3f7y2z1C0c2AbF6B6V9Q0490Pb976E0001T4v3L4u04q1z6666ji8k8732SCKSJIsArriiiiii07JCABrzrrrrrrr02BrkqHrsrrrrjr005ziiqzrjzrrqr1jRjrqGGrrzsrsA099ijrABJJJIAzrrtirqrqjqixzsrAjrqjiqaqqysttAJqjikikrizrHtBJJAzArzrIsRCITKSS099ijrAJS____Qg99habbCAYrDzh00b4xd5ix8jhklEC7Ii6kl64xB5hDtUwN8id3gi4zgQlFBo5hol5xkm5hoycPgp2cjFEY4zkWWf3Ild7EM5aDnkWn9E-p5dvOCO-GqfYzX_Nu-LFX00X1FOg5dvU-uFEZ4zkWWf1EnHU2BjHGs03LWsA1joLn1GCzW8XWb82BjNW_i1jhYYwlRJduUzHEJKFU_e0K00eaAbaKzyP58ib9Mc5nnnnnn8QSkRilcDllnjpttGGGwqWWWWWWWQyJRRRRRRRF5bHHHHHHHhannnnUGKSCGOGGGGGGCCOGGF9v0GloJ02ieCF9vnVWWWKKKKKKKKKGGGOSCGKGGGOSCGKCGCGCzU8WG86KBjNW_0Nd7YFRRe00CzWkQvzQ4W9g21LJHHHHHHf2WqfAzBa8ayC18X2eiE8V6zshSQnkkSAswhQQ4tluGgFFjnYQVc2IpuLnkZwiCzWLwI2hRklkkS4tB2q_XfFET4tJ5R5dN7thAVltCwzGEGEFH8XacDaFGSAt17thhihQ4t17jjlF8W2eGBcLtBJelnpjCRSlllpmAzAqaWar2eg8Wq2eGMFLh_qrEu4TmWfqFKDUAqqfCCBdvxpdldddirw-uhkCO-sCyCDyldB-_GuNljJpdldddldaCnHM2DLU4-5FJvOhYRljRKjljjjljjR6FBUFGHZjiIQp_AqqcAzEEAICyf98W2exPhA0mCzbGpgF5l16QOxigOdja5aEGGGEOGO93aDARQ4ujp7Bklllkplp5uleDsKAzOFOGifaDdOWOYCkQp3SzaGXUyPhBihFEOif8T9HOr8Wp8Wq8YBPhBPuhSp8YAzOaOqYCOeCieCyeCtKGSM0',
|
||||
path: 'music/contemplating-the-groove.ogg',
|
||||
}, {
|
||||
title: "gently haunting",
|
||||
author: "Trotim",
|
||||
twitter: "trotimwolf",
|
||||
beepbox: 'https://www.beepbox.co/#8n31s7k4l00e0jt2mm0a7g0jj07i0r1o3210T1v2L4u3bq1d5f7y1z7C0c2AcF8B4V6Q047cPa744E0000T1v1L4u61q1d5f7y0z6C1c0A5F2B6V7Q0530Pf636E0011T5v1L4ue8q3d6f7y1z8C0c0h6H-SstrsrBzjAqihT4v1L4uf0q1z6666ji8k8k3jSBKSJJAArriiiiii07JCABrzrrrrrrr00YrkqHrsrrrrjr005zrAqzrjzrrqr1jRjrqGGrrzsrsA099ijrABJJJIAzrrtirqrqjqixzsrAjrqjiqaqqysttAJqjikikrizrHtBJJAzArzrIsRCITKSS099ijrAJS____Qg99habbCAYrDzh00b010y8x4h00000000000id10Mk60id18Q4zgid18Q4h8h4w00cPcPcMp23MFEZkzjn_V97ihQAujAAth7F8RdjRW1jhZ4th7ihVdph7mhwkQvp7ohQAs0hQAth6FBZezqqfwzOp7khQAs2hQAth7CTA4uh8XyeAzJ8WieEzOXO2f8Atp7ihS4t97khVtV17pE_lOePieKOe0m8WX8Xt8SCnQWa_IQv7Aukn8X_8WX8U1szHIzJQzNlvYzOcITE-Heh4O0-hQarneGFFQQQFHOkOCSO5Bd6h14t76Vll97B0',
|
||||
path: 'music/gently-haunting.ogg',
|
||||
}];
|
||||
BIN
music/chiptune-challenged.ogg
Normal file
BIN
music/chiptune-challenged.ogg
Normal file
Binary file not shown.
BIN
music/contemplating-the-groove.ogg
Normal file
BIN
music/contemplating-the-groove.ogg
Normal file
Binary file not shown.
BIN
music/gently-haunting.ogg
Normal file
BIN
music/gently-haunting.ogg
Normal file
Binary file not shown.
BIN
music/random-access-melody.ogg
Normal file
BIN
music/random-access-melody.ogg
Normal file
Binary file not shown.
BIN
music/shanty-jig.ogg
Normal file
BIN
music/shanty-jig.ogg
Normal file
Binary file not shown.
45
style.css
45
style.css
@ -20,7 +20,8 @@ main[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
input[type=radio],
|
||||
input[type=checkbox] {
|
||||
input[type=checkbox],
|
||||
input[type=range] {
|
||||
margin: 0.125em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
@ -292,16 +293,17 @@ body[data-mode=player] #editor-play {
|
||||
display: grid;
|
||||
align-items: center;
|
||||
grid:
|
||||
"controls controls" min-content
|
||||
"level chips" min-content
|
||||
"level time" min-content
|
||||
"level bonus" min-content
|
||||
"level message" 1fr
|
||||
"level inventory" min-content
|
||||
"controls controls"
|
||||
"music ." min-content
|
||||
/* Need explicit min-content to force the hint to wrap */
|
||||
/ min-content min-content
|
||||
;
|
||||
column-gap: 1em;
|
||||
column-gap: 2em;
|
||||
row-gap: 0.5em;
|
||||
|
||||
image-rendering: optimizeSpeed;
|
||||
@ -504,6 +506,43 @@ dl.score-chart .-sum {
|
||||
background: #0009;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#player-music {
|
||||
grid-area: music;
|
||||
display: flex;
|
||||
text-transform: lowercase;
|
||||
color: #909090;
|
||||
}
|
||||
#player-music #player-music-left {
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
#player-music #player-music-right {
|
||||
text-align: right;
|
||||
}
|
||||
#player-music a {
|
||||
color: #c0c0c0;
|
||||
}
|
||||
#player-music a:link,
|
||||
#player-music a:visited {
|
||||
text-decoration: underline dotted;
|
||||
}
|
||||
#player-music a:link {
|
||||
color: hsl(225, 50%, 75%);
|
||||
}
|
||||
#player-music a:visited {
|
||||
color: hsl(300, 50%, 75%);
|
||||
}
|
||||
#player-music a:link:hover,
|
||||
#player-music a:visited:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
#player-music a:active {
|
||||
color: hsl(0, 50%, 75%);
|
||||
}
|
||||
#player-music #player-music-volume {
|
||||
width: 8em;
|
||||
}
|
||||
|
||||
#player .controls {
|
||||
grid-area: controls;
|
||||
display: flex;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user