client/code/game/game.audio.js
'use strict'
/* global ss, CivicSeed, $game, Howl, Howler */
var _soundtracks = []
var _triggerFx = null
var _environmentLoopFx = null
var _environmentOnceFx = null
var _currentTrack = -1
var _prevTrack = -1
var _numTracks = 8
var _musicPath = CivicSeed.CLOUD_PATH_MEDIA + '/audio/music/'
var _midTransition = false
var _extension = null
var _environmentLoopFxPlaces = [
{ sound: 'stream',
locations: [{x: 30, y: 10}, {x: 30, y: 50}, {x: 30, y: 90}, {x: 30, y: 130}, {x: 30, y: 170}],
prox: {x: 10, y: 10}
},
{ sound: 'wind',
locations: [{x: 15, y: 8}, {x: 60, y: 50}],
prox: {x: 20, y: 15}
},
{ sound: 'chatter',
locations: [{x: 100, y: 30}],
prox: {x: 20, y: 20}
},
{ sound: 'waves',
locations: [{x: 10, y: 80}],
prox: {x: 20, y: 20}
}
]
var _environmentOnceFxPlaces = [
{location: {x: 35, y: 30}, sounds: ['forest1', 'forest2', 'forest3', 'bird1', 'bird2', 'bird3'], prox: {x: 35, y: 30}, chance: 0.2},
{location: {x: 105, y: 30}, sounds: ['churchBells', 'townBells', 'chatter', 'bird2', 'bird3'], prox: {x: 20, y: 20}, chance: 0.1},
{location: {x: 105, y: 90}, sounds: ['chickens', 'sheep1', 'sheep2', 'horse', 'bird3'], prox: {x: 35, y: 30}, chance: 0.15},
{location: {x: 35, y: 90}, sounds: ['portBells', 'foghorn', 'bird2'], prox: {x: 25, y: 20}, chance: 0.1}
]
var _currentLoop = null
var config = {
soundtrackVolume: 0.2,
environmentLoopFxVolume: 0.1,
environmentOnceFxVolume: 0.3,
triggerFxVolume: 0.4
}
function getProximity (location, prox) {
var currentPosition = $game.$player.getPosition()
var distX = Math.abs(currentPosition.x - location.x)
var distY = Math.abs(currentPosition.y - location.y)
return (distX < prox.x && distY < prox.y) ? true : false
}
// Determine which soundtrack to play
function whichTrack () {
// This is dependent on player's position in the world, so get that
var track = $game.$player.getGameRegion()
// If 'no man's land' is returned, cover it by changing to track 4
if (track === -1) track = 4
return track
}
var $audio = module.exports = {
ready: false,
isMute: false,
init: function (callback) {
if (CivicSeed.ENVIRONMENT !== 'development') {
_extension = CivicSeed.version
}
$audio.isMute = $game.$player.isMuted
if ($audio.isMute) {
$game.$input.muteAudio()
$audio.mute()
}
var track = whichTrack()
$audio.loadTrack(track)
// hack to check if all stuff is loaded so we can callback
var checkDone = function () {
if ($audio.ready) {
callback()
} else {
setTimeout(checkDone, 30)
}
}
checkDone()
},
resetInit: function () {
_soundtracks = []
_triggerFx = null
_environmentLoopFx = null
_environmentOnceFx = null
_currentTrack = -1
_prevTrack = -1
_midTransition = false
_extension = null
_currentLoop = null
$audio.ready = false
$audio.isMute = $game.$player.isMuted
},
loadTrack: function (num) {
var mp3 = _musicPath + num + '.mp3?VERSION='
var ogg = _musicPath + '/ogg/' + num + '.ogg?VERSION='
if (_extension) {
mp3 += _extension
ogg += _extension
} else {
mp3 += Math.round(Math.random(1) * 1000000000)
ogg += Math.round(Math.random(1) * 1000000000)
}
var autoplay = true
if ($game.$player.currentLevel > 3 && $game.bossModeUnlocked) {
autoplay = false
}
_soundtracks[num] = new Howl({
urls: [mp3, ogg],
autoplay: autoplay,
loop: true,
volume: config.soundtrackVolume,
buffer: true
})
// this goes thru all the tracks, and skips num since its preloaded
$audio.loadOtherTrack(0, num)
$audio.loadTriggerFx()
_currentTrack = num
},
loadOtherTrack: function (track, num) {
if (track !== num) {
var mp3 = _musicPath + track + '.mp3?VERSION='
var ogg = _musicPath + '/ogg/' + '.ogg?VERSION='
if (_extension) {
mp3 += _extension
ogg += _extension
} else {
mp3 += Math.round(Math.random(1) * 1000000000)
ogg += Math.round(Math.random(1) * 1000000000)
}
_soundtracks[track] = new Howl({
urls: [mp3, ogg],
autoplay: false,
loop: true,
volume: 0.0,
buffer: true
})
track++
if (track !== _numTracks) {
$audio.loadOtherTrack(track, num)
}
} else {
track++
if (track !== _numTracks) {
$audio.loadOtherTrack(track, num)
}
}
},
loadTriggerFx: function () {
var mp3 = _musicPath + 'triggers.mp3?VERSION='
var ogg = _musicPath + '/ogg/triggers.ogg?VERSION='
if (_extension) {
mp3 += _extension
ogg += _extension
} else {
mp3 += Math.round(Math.random(1) * 1000000000)
ogg += Math.round(Math.random(1) * 1000000000)
}
_triggerFx = new Howl({
urls: [mp3, ogg],
sprite: {
chatSend: [0, 500],
chatReceive: [1000, 1300],
npcBubble: [3000, 300],
windowShow: [4000, 400],
seedDrop: [5000, 500],
riddleDrop1: [6000, 700],
riddleDrop2: [7000, 700],
riddleDrop3: [8000, 700],
riddleDrop4: [9000, 700],
resourceRight: [10000, 500],
resourceWrong: [11000, 700],
puzzleRight: [12000, 1200],
puzzleWrong: [14000, 700],
pieceSelect: [15000, 300],
pieceDrop: [16000, 200],
robot: [17000, 2800]
},
volume: config.triggerFxVolume,
onload: function () {
$audio.loadEnvironmentLoopFx()
}
})
},
loadEnvironmentLoopFx: function () {
var mp3 = _musicPath + 'environmentloop.mp3?VERSION='
var ogg = _musicPath + '/ogg/environmentloop.ogg?VERSION='
if (_extension) {
mp3 += _extension
ogg += _extension
} else {
mp3 += Math.round(Math.random(1) * 1000000000)
ogg += Math.round(Math.random(1) * 1000000000)
}
_environmentLoopFx = new Howl({
urls: [mp3, ogg],
sprite: {
stream: [0, 5010],
chatter: [6000, 12025],
wave: [19000, 16500],
wind: [36000, 14350]
},
loop: true,
onend: function () {
$audio.checkLoopExit()
},
volume: config.environmentLoopFxVolume,
onload: function () {
$audio.loadEnvironmentOnceFx()
}
})
},
loadEnvironmentOnceFx: function () {
var mp3 = _musicPath + 'environmentonce.mp3?VERSION='
var ogg = _musicPath + '/ogg/environmentonce.ogg?VERSION='
if (_extension) {
mp3 += _extension
ogg += _extension
} else {
mp3 += Math.round(Math.random(1) * 1000000000)
ogg += Math.round(Math.random(1) * 1000000000)
}
_environmentOnceFx = new Howl({
urls: [mp3, ogg],
sprite: {
neo: [0, 1520],
bigger: [2000, 5300],
horse: [8000, 11000],
sheep1: [11000, 1600],
sheep2: [13000, 1500],
chickens: [15000, 4400],
forest1: [20000, 1000],
forest2: [21000, 1600],
portBells: [23000, 4400],
churhBells: [28000, 3150],
townBells: [32000, 2400],
bird1: [35000, 700],
bird2: [36000, 500],
bird3: [37000, 900],
foghorn: [38000, 4300],
chatter: [43000, 5500],
forest3: [49000, 2700]
},
volume: config.environmentOnceFxVolume,
onend: function () {
},
onload: function () {
$audio.ready = true
}
})
},
playTriggerFx: function (fx) {
_triggerFx.play(fx)
},
playEnvironmentLoopFx: function (fx) {
_environmentLoopFx.play(fx)
},
playEnvironmentOnceFx: function (fx) {
var ranDelay = Math.random() * 2000
setTimeout(function () {
_environmentOnceFx.play(fx)
}, ranDelay)
},
update: function () {
var trackNum = whichTrack()
var message = 'Entering '
if (_soundtracks[trackNum]._loaded && trackNum !== _currentTrack && !_midTransition) {
$audio.switchTrack(trackNum)
// As audio switches, display a notice to the player of where
// they are entering.
// TODO: Move this elsewhere (to player, map, or game.js)
// Since this is not really music-related.
switch (trackNum) {
case 0:
// Top left
message += $game.world.northwest.name
break
case 1:
// Top right
message += $game.world.northeast.name
break
case 2:
// Bottom right
message += $game.world.southeast.name
break
case 3:
// Bottom left
message += $game.world.southwest.name
break
case 5:
// Botanist's area
message += $game.world.origin.name
break
default:
// message = 'You are on the equator'
message = ''
break
}
if (message.length > 0) {
$game.alert(message)
}
}
if (!_currentLoop) {
$audio.checkEnvironmentLoopFx(trackNum)
}
$audio.checkEnvironmentOnceFx(trackNum)
},
checkEnvironmentLoopFx: function () {
var numPlaces = _environmentLoopFxPlaces.length
var p = 0
while (p < numPlaces) {
var place = _environmentLoopFxPlaces[p]
var numLocations = place.locations.length
var l = 0
while (l < numLocations) {
var inProx = getProximity(place.locations[l], place.prox)
if (inProx) {
_currentLoop = place
$audio.playEnvironmentLoopFx(place.sound)
// get out of BOTH loops
l = numLocations
p = numPlaces
}
l++
}
p++
}
},
checkEnvironmentOnceFx: function () {
var numPlaces = _environmentOnceFxPlaces.length
var p = 0
while (p < numPlaces) {
var place = _environmentOnceFxPlaces[p]
var inProx = getProximity(place.location, place.prox)
if (inProx) {
var rollDice = Math.random()
if (rollDice < place.chance) {
var soundIndex = Math.floor((rollDice / place.chance) * place.sounds.length)
$audio.playEnvironmentOnceFx(place.sounds[soundIndex])
// if we don't want overlapping of sounds triggered at a time
p = numPlaces
}
}
p++
}
},
checkLoopExit: function () {
var numLocations = _currentLoop.locations.length
var l = 0
var inRange = false
while (l < numLocations) {
var inProx = getProximity(_currentLoop.locations[l], _currentLoop.prox)
if (inProx) {
inRange = true
l = numLocations
}
l++
}
if (!inRange) {
_environmentLoopFx.pause(_currentLoop.sound)
_currentLoop = null
}
},
pauseTrack: function () {
_soundtracks[_currentTrack].pause()
},
switchTrack: function (swap) {
_prevTrack = _currentTrack
_currentTrack = swap
if (_prevTrack !== _currentTrack) {
_midTransition = true
_soundtracks[_prevTrack].fadeOut(0, 1000, function () {
_soundtracks[_prevTrack].pause()
})
_soundtracks[_currentTrack].fadeIn(config.soundtrackVolume, 3000, function (swap) {
_midTransition = false
})
}
},
// Toggle audio on or off, and save setting to player profile
toggleMute: function () {
$audio.isMute = !$audio.isMute
if ($audio.isMute) {
$audio.mute()
} else {
$audio.unmute()
}
// Save volume state to player profile
$game.$player.isMuted = $audio.isMute
ss.rpc('game.player.updateGameInfo', {
id: $game.$player.id,
isMuted: $game.$player.isMuted
})
return $audio.isMute
},
// Mute and unmute wrappers for Howler
mute: function () {
Howler.mute()
},
unmute: function () {
Howler.unmute()
// Make sure that sountrack volume is restored, because sometimes it does not do it
_soundtracks[_currentTrack].volume(config.soundtrackVolume)
},
// Sound is faded to a lower level when viewing resource contents
fadeLow: function () {
if (!$audio.isMute) {
_soundtracks[_currentTrack].volume(config.soundtrackVolume * 0.25)
_environmentLoopFx.volume(config.environmentLoopFxVolume * 0.15)
}
},
fadeHi: function () {
if (!$audio.isMute) {
_soundtracks[_currentTrack].volume(config.soundtrackVolume)
_environmentLoopFx.volume(config.environmentLoopFxVolume)
}
},
stopAll: function () {
_soundtracks[_currentTrack].stop()
_environmentLoopFx.stop()
}
}