'use strict';
const Alexa = require('alexa-sdk');
const SKILL_NAME = "Candy Grab";
let speechOutput ="";
exports.handler = (event, context) => {
// standard Alexa Skill Kit initialization
const alexa = Alexa.handler(event, context);
alexa.appId = '';
alexa.registerHandlers(main);
alexa.execute();
};
const main = {
'LaunchRequest': function(handlerInput) {
if(Object.keys(this.attributes).length === 0) {
this.attributes['sessionProcess'] = 'LaunchRequest';
this.attributes['timeStamp'] = + new Date();
this.attributes['timeOutOnce'] = false;
}
console.log("starting " + this.attributes['sessionProcess']);
/*
For this skill, on launch we'll immediately setup the
input handler to listen to all attached buttons for 30
seconds.
We'll setup two events that each report when buttons are
pressed down and when they're released up.
After 60 seconds, we'll get the timeout event.
*/
this.response._addDirective({
"type": "GameEngine.StartInputHandler",
"timeout": 30000,
"recognizers": {
"button_down_recognizer": {
type: "match",
fuzzy: false,
anchor: "end",
"pattern": [{
"action": "down"
}]
}
},
"events": {
"button_down_event": {
"meets": ["button_down_recognizer"],
"reports": "matches",
"shouldEndInputHandler": false
},
"timeout": {
"meets": ["timed out"],
"reports": "history",
"shouldEndInputHandler": false
}
}
});
this.attributes.buttonCount = 0;
if (this.attributes['sessionProcess'] == 'LaunchRequest') {
// build 'button down' animation for when the button is pressed
this.response._addDirective(buildButtonDownAnimationDirective([]));
this.response.cardRenderer(SKILL_NAME, "Let's play candy grab! Make four groups of candy, then youngest player, press the Echo Button to start! or say help for more info.");
this.response.speak("<audio src='soundbank://soundlibrary/magic/amzn_sfx_magic_blast_1x_01'/> Let's play candy grab! Make four groups of candy, then youngest player, press the Echo Button to start!");
this.handler.response.response.shouldEndSession = false;
}
this.emit(':responseReady');
},
'GameEngine.InputHandlerEvent': function() {
console.log('Received game event', JSON.stringify(this.event, null, 2));
let gameEngineEvents = this.event.request.events || [];
for (let i = 0; i < gameEngineEvents.length; i++) {
let buttonId;
switch (gameEngineEvents[i].name) {
case 'button_down_event':
this.attributes['timeStamp'] = + new Date();
// id of the button that triggered event
buttonId = gameEngineEvents[i].inputEvents[0].gadgetId;
if (this.attributes[buttonId + '_initialized'] === undefined) {
this.attributes.buttonCount += 1;
this.attributes[buttonId + '_initialized'] = true;
}
let colorNumber = randomInteger(1,6);
delete this.handler.response.response.shouldEndSession;
let responseText = randomColor(colorNumber);
if (colorNumber > 4) {
let responseText2 = responseText.replace(/\<audio.*\>/, '');
this.response.cardRenderer(SKILL_NAME, responseText2);
} else {
this.response.cardRenderer(SKILL_NAME, responseText);
}
this.response.speak(' ' + spinnerSays() + " " + responseText) ;
console.log("color number "+ colorNumber);
switch (colorNumber) {
case 1:
this.response._addDirective(buildButtonIdleAnimationDirective([buttonId], redAnimation));
break;
case 2:
this.response._addDirective(buildButtonIdleAnimationDirective([buttonId], blueAnimation));
break;
case 3:
this.response._addDirective(buildButtonIdleAnimationDirective([buttonId], greenAnimation));
break;
case 4:
this.response._addDirective(buildButtonIdleAnimationDirective([buttonId], purpleAnimation));
break;
case 5:
this.response._addDirective(buildButtonIdleAnimationDirective([buttonId], breathAnimation));
break;
case 6:
this.response._addDirective(buildButtonIdleAnimationDirective([buttonId], breathAnimation));
break;
}
this.handler.response.response.outputSpeech.playBehavior = 'REPLACE_ALL';
delete this.handler.response.response.shouldEndSession;
this.emit(':responseReady');
break;
case 'timeout':
let lasttime = this.attributes['timeStamp'];
let thistime = + new Date();
let sincelasttime = thistime - lasttime;
console.log('timeout case sincelasttime '+ sincelasttime);
if (sincelasttime < 50000) {
this.attributes['sessionProcess'] = 'theTimeOut';
delete this.handler.response.response.shouldEndSession;
this.emit('LaunchRequest');
}
else {
this.response.speak("Thank you for using Candy Grab! " + sincelasttime);
this.handler.response.response.shouldEndSession = true;
this.emit('AMAZON.StopIntent');
}
break;
}
}
},
'AMAZON.HelpIntent': function() {
console.log('HelpIntent');
this.handler.response.response.shouldEndSession = false;
this.attributes['timeStamp'] = + new Date();
speechOutput = "It's The Candy Grab game! Start with four small piles of candy, a Red, Blue, Green and Purple Pile, (colors don\'t have to match the pile color). "+
"Youngest player goes first, press the Echo Button to spin...you will spin one of the following: "+
"1, A random color... if a piece of candy of this color is left in the pile, grab it and put it in your private stash (but don't eat it yet!) " +
"2, Animal sound...Aww, too bad! no candy when this happens. Or " +
"3, crash sound...Oh no, Candy Crash!, you have to return 2 pieces of candy! " +
"When the candy in the middle is gone, the player with the most candy in their stash wins! ... Remember winner, sharing is good!, Now let's begin!";
this.attributes['timeStamp'] = + new Date();
this.response.cardRenderer(SKILL_NAME, "Start with 4 small piles of candy, a Red, Blue, Green and Purple. (colors don\'t have to match the pile color). "+
"Youngest player goes first, press the Echo Button to spin...you will spin one of the following: " +
"1, A random color... if a piece of candy of this color is left in the pile, grab it and put it in your private stash (but don't eat it yet!) " +
"2, Animal sound...Aww, too bad! no candy when this happens. Or " +
"3, crash sound...Oh no, Candy Crash!, you have to return 2 pieces of candy! " +
"When the candy in the middle is gone, the player with the most candy in their stash wins! ... Remember winner, sharing is good!, Now let's begin!");
this.response.speak(speechOutput);
this.emit(':responseReady');
},
'AMAZON.StopIntent': function() {
console.log('StopIntent');
this.response.speak('Good Bye for now from Candy Grab');
this.response.cardRenderer(SKILL_NAME, "Good Bye for now from Candy Grab.");
this.response._addDirective(buttonFadeoutAnimationDirective);
this.emit(':responseReady');
},
'AMAZON.CancelIntent': function() {
console.log('CancelIntent');
this.response.speak('Alright, canceling');
this.response._addDirective(buttonFadeoutAnimationDirective);
this.emit(':responseReady');
},
'SessionEndedRequest': function() {
console.log('SessionEndedRequest');
},
'System.ExceptionEncountered': function() {
console.log('ExceptionEncountered');
console.log(this.event.request.error);
console.log(this.event.request.cause);
},
'Unhandled': function() {
console.log('Unhandled');
const msg = "Sorry, I didn't get that.";
this.emit(':ask', msg, msg);
}
};
const buildBreathAnimation = function(fromRgbHex, toRgbHex, steps, totalDuration) {
const halfSteps = steps / 2;
const halfTotalDuration = totalDuration / 2;
return buildSeqentialAnimation(fromRgbHex, toRgbHex, halfSteps, halfTotalDuration)
.concat(buildSeqentialAnimation(toRgbHex, fromRgbHex, halfSteps, halfTotalDuration));
};
const buildSeqentialAnimation = function(fromRgbHex, toRgbHex, steps, totalDuration) {
const fromRgb = parseInt(fromRgbHex, 16);
let fromRed = fromRgb >> 16;
let fromGreen = (fromRgb & 0xff00) >> 8;
let fromBlue = fromRgb & 0xff;
const toRgb = parseInt(toRgbHex, 16);
const toRed = toRgb >> 16;
const toGreen = (toRgb & 0xff00) >> 8;
const toBlue = toRgb & 0xff;
const deltaRed = (toRed - fromRed) / steps;
const deltaGreen = (toGreen - fromGreen) / steps;
const deltaBlue = (toBlue - fromBlue) / steps;
const oneStepDuration = Math.floor(totalDuration / steps);
const result = [];
for (let i = 0; i < steps; i++) {
result.push({
"durationMs": oneStepDuration,
"color": rgb2h(fromRed, fromGreen, fromBlue),
"intensity": 255,
"blend": true
});
fromRed += deltaRed;
fromGreen += deltaGreen;
fromBlue += deltaBlue;
}
return result;
};
const rgb2h = function(r, g, b) {
return '' + n2h(r) + n2h(g) + n2h(b);
};
// number to hex with leading zeroes
const n2h = function(n) {
return ('00' + (Math.floor(n)).toString(16)).substr(-2);
};
let breathAnimation = buildBreathAnimation('1e90ff', 'b22222', 30, 1200);
let redAnimation = buildBreathAnimation('f44242', 'f44242', 30, 1200);
let blueAnimation = buildBreathAnimation('4b42f4', '4b42f4', 30, 1200);
let greenAnimation = buildBreathAnimation('22b222', '22b222', 30, 1200);
let purpleAnimation = buildBreathAnimation('7441f4', '7441f4', 30, 1200);
// build 'button down' animation directive
// animation will overwrite default 'button down' animation
const buildButtonDownAnimationDirective = function(targetGadgets) {
return {
"type": "GadgetController.SetLight",
"version": 1,
"targetGadgets": targetGadgets,
"parameters": {
"animations": [{
"repeat": 1,
"targetLights": ["1"],
"sequence": [{
"durationMs": 300,
"color": "228b22",
"intensity": 255,
"blend": false
}]
}],
"triggerEvent": "buttonDown",
"triggerEventTimeMs": 0
}
};
};
// build idle animation directive
const buildButtonIdleAnimationDirective = function(targetGadgets, animation) {
return {
"type": "GadgetController.SetLight",
"version": 1,
"targetGadgets": targetGadgets,
"parameters": {
"animations": [{
"repeat": 100,
"targetLights": ["1"],
"sequence": animation
}],
"triggerEvent": "none",
"triggerEventTimeMs": 0
}
};
};
// fadeout animation directive
const buttonFadeoutAnimationDirective = {
"type": "GadgetController.SetLight",
"version": 1,
"targetGadgets": [],
"parameters": {
"animations": [{
"repeat": 1,
"targetLights": ["1"],
"sequence": [{
"durationMs": 1,
"color": "FFFFFF",
"intensity": 255,
"blend": true
}, {
"durationMs": 1000,
"color": "000000",
"intensity": 255,
"blend": true
}]
}],
"triggerEvent": "none",
"triggerEventTimeMs": 0
}
};
function randomColor(colorNumber) {
let outText = "";
switch (colorNumber) {
case 1:
outText = outText + " Red. Take one from the Red Pile.";
break;
case 2:
outText = outText + " Blue. Take one of the Blue.";
break;
case 3:
outText = outText + " Green. Take one from the Green Pile.";
break;
case 4:
outText = outText + " Purple. Take one from the Purple Pile.";
break;
case 5:
if (randomInteger(1, 2)==1) {
outText = outText + "<audio src='soundbank://soundlibrary/animals/amzn_sfx_bear_roar_small_01'/> Watch it, a bear! You have to put 1 piece back. ";
} else {
outText = outText + "<audio src='soundbank://soundlibrary/animals/amzn_sfx_dog_med_bark_growl_01'/> Oops, it\'s the watchdog! You don't get a piece this time...";
}
break;
case 6:
if (randomInteger(1, 3)==1) {
outText = outText + "<audio src='soundbank://soundlibrary/cartoon/amzn_sfx_boing_long_1x_01'/> Oh no! Candy Crash! You have to put 2 pieces back!";
} else {
outText = outText + "<audio src='soundbank://soundlibrary/animals/amzn_sfx_monkey_chimp_01'/> A crazy monkey! You don't get a piece of candy this turn!";
}
break;
}
return outText + " " + passTheButton();
}
function randomInteger(lowest, highest) {
return Math.floor(Math.random() * (highest - lowest + 1) + lowest);
}
function spinnerSays() {
let random = randomInteger(1,4);
let responseString ;
switch(random) {
case 1:
responseString = "Spinning...Spinner says:<audio src='soundbank://soundlibrary/ui/gameshow/amzn_ui_sfx_gameshow_positive_response_01'/>";
break;
case 2:
responseString = "Spinning...<audio src='soundbank://soundlibrary/ui/gameshow/amzn_ui_sfx_gameshow_positive_response_01'/>";
break;
case 3:
responseString = "Here We go!:<audio src='soundbank://soundlibrary/ui/gameshow/amzn_ui_sfx_gameshow_positive_response_01'/>";
break;
case 4:
responseString = "And the spinner says:<audio src='soundbank://soundlibrary/ui/gameshow/amzn_ui_sfx_gameshow_positive_response_01'/>";
break;
}
return responseString;
}
function passTheButton() {
let random = randomInteger(1,4);
let responseString ;
switch(random) {
case 1:
responseString = " Then pass the spinner button.";
break;
case 2:
responseString = " Then on to the next player.";
break;
case 3:
responseString = " Then pass the button.";
break;
case 4:
responseString = " Then the next player goes.";
break;
}
return responseString;
}
Comments