text: 
  /** DIALOGUE **************************************************************************************/
  
  Dialogue = {
  
      // Music
      'help1': {
          type        : 'notification',
          text        : 'Use the arrows keys to move, spacebar to interact with someone, and enter to submit.'
      },
  
      'wetDreams': {
          type        : 'notification',
          text        : '<a href="http://ocremix.org/remix/OCR02727" target="_blank">Wet Dreams, by Phonetic Hero</a>'
      },
  
      'game_load': {
          type        : 'dialogue',
          emote       : 'happiness',
          triggeredText: function(npc) {
              npc.dialogueId = "step1_start";
          },
          goTo        : 'game_load2',
          text    : "Welcome! I'm a clerk who has been tasked with helping you fill out this form."
      },
  
      'game_load2': {
          type        : 'dialogue',
          emote       : 'happiness',
          text    : "The enter key can be used to speak to people and end dialogues. After closing this dialogue, use the arrow keys to walk over to me, and then hit enter to speak with me.",
          action: function(npc) {
              setTimeout(function(){
                  npc.npc('emote', 'question');
              }, 500);
          },
          end : true
      },
  
  
  
  
      'clerk_notready': {
          type        : 'dialogue',
          text        : "I'm not ready to speak to you yet, please go talk to my colleague.",
          emote       : 'question',
          end         : true
      },
  
      'step1_start': {
          type        : 'dialogue',
          text        : "To begin with, let's record your children's information.",
          emote       : 'question',
          goTo        : 'step1_q1'
      },
  
      'step1_q1': {
          type    : 'input',
          emote   : 'question',
          label   : 'How many children do you have? (hit enter when done)',
          goTo    : 'step1_q2',
          action: function(npc, value) {
              var total_children = parseInt(value);
              Game.children_left_to_fill_out = total_children;
              for (var i=1; i<=10; i++) {
                  $('#child'+i).hide();
              }
              for (var i=1; i<=total_children; i++) {
                  $('#child'+i).show();
                  if ($('#child'+i).data().npc.completedme) {
                      Game.children_left_to_fill_out -= 1;
                  }
              }
          }
      },
  
      'step1_q2': {
          type        : 'dialogue',
          text        : "Excellent! I created avatars to represent each child. Continue by talking to them!",
          emote       : 'happiness',
          end         : true,
          action      : function(npc) {
              if (npc && !npc.data().npc['frozen']) {
                  var move_directions = [];
  
                  move_directions.push('right');
                  move_directions.push('right');
                  move_directions.push('up');
  
                  npc.npc('move', move_directions);
                  setTimeout(function(){
                      npc.find('.npc-sprite').removeClass('up').addClass('down');
                  }, 1500);
                  npc.data().npc['frozen'] = true;
                  //npc.data().npc.dialogueId = "step1_q1";
              }
          }
      },
  
      'dchi01': {
          type        : 'dialogue',
          emote       : 'question',
          text        : "I'm an avatar representing your child! Help fill out more information about me.",
          goTo        : 'ichi01'
      },
  
  
      'ichi01': {
          type    : 'input',
          goTo    : 'ichi02',
          emote   : 'question',
          label : 'What is my name? (First name, middle initial, last name)',
          action: function(npc, value) {
              var npc_id = npc.data()['npc'].id;
              if (!(npc_id in Game.formData['children'])) {
                  Game.formData['children'][npc_id] = {};
              }
              Game.formData['children'][npc_id]['name'] = value;
          }
      },
  
      'ichi02': {
          type    : 'choice',
          emote       : 'question',
          choices : [
              {
                  label       : 'I am not a student',
                  goTo        : 'ichi03',
                  action: function(npc, value) {
                      var npc_id = npc.data()['npc'].id;
                      if (!(npc_id in Game.formData['children'])) {
                          Game.formData['children'][npc_id] = {};
                      }
                      Game.formData['children'][npc_id]['student'] = false;
                  }
              },
              {
                  label       : 'I am a student',
                  goTo        : 'ichi03',
                  action: function(npc, value) {
                      var npc_id = npc.data()['npc'].id;
                      if (!(npc_id in Game.formData['children'])) {
                          Game.formData['children'][npc_id] = {};
                      }
                      Game.formData['children'][npc_id]['student'] = true;
                  }
              }
          ]
      },
  
      'ichi03': {
          type    : 'choice',
          emote       : 'question',
          choices : [
              {
                  label       : 'I am not a foster child',
                  goTo        : 'ichi04a',
                  action: function(npc, value) {
                      var npc_id = npc.data()['npc'].id;
                      if (!(npc_id in Game.formData['children'])) {
                          Game.formData['children'][npc_id] = {};
                      }
                      Game.formData['children'][npc_id]['foster_child'] = false;
                  }
              },
              {
                  label       : 'I am a foster child',
                  goTo        : 'ichi04a',
                  action: function(npc, value) {
                      var npc_id = npc.data()['npc'].id;
                      if (!(npc_id in Game.formData['children'])) {
                          Game.formData['children'][npc_id] = {};
                      }
                      Game.formData['children'][npc_id]['foster_child'] = true;
                  }
              }
          ]
      },
  
      'ichi04a': {
          type    : 'choice',
          emote       : 'question',
          choices : [
              {
                  label       : 'I am not homeless / a migrate / a runaway',
                  goTo        : 'ichi04b',
                  action: function(npc, value) {
                      var npc_id = npc.data()['npc'].id;
                      if (!(npc_id in Game.formData['children'])) {
                          Game.formData['children'][npc_id] = {};
                      }
                      Game.formData['children'][npc_id]['homeless'] = false;
                  }
              },
              {
                  label       : 'I am homeless / a migrate / a runaway',
                  goTo        : 'ichi04b',
                  action: function(npc, value) {
                      var npc_id = npc.data()['npc'].id;
                      if (!(npc_id in Game.formData['children'])) {
                          Game.formData['children'][npc_id] = {};
                      }
                      Game.formData['children'][npc_id]['homeless'] = true;
                  }
              }
          ]
      },
  
      'ichi04b': {
          type    : 'inputincome',
          goTo    : 'ichi05',
          emote       : 'question',
          label : 'How much income do I earn?',
          action: function(npc, value) {
              if (npc && value) {
                  var npc_id = npc.data()['npc'].id;
                  Game.formData['children'][npc_id]['income'] = value;
              }
          }
      },
  
      'ichi05': {
          type        : 'dialogue',
          text        : "...",
          emote       : 'question',
          dynamicText: function(npc){
              var c = Game.formData.children[npc.data().npc.id];
              recap_statement = "";
              recap_statement += "My name is "+c.name+". ";
              if (c.student) {
                  recap_statement += "I am a student. ";
              }
              else {
                  recap_statement += "I am not a student. ";
              }
              if (c.foster_child) {
                  recap_statement += "I am a foster child. ";
              }
              else {
                  recap_statement += "I am not a foster child. ";
              }
              if (c.homeless) {
                  recap_statement += "I am homeless / a migrate / a runaway. ";
              }
              else {
                  recap_statement += "I am not homeless / a migrate / a runaway. ";
              }
              if (parseInt(c.income)) {
                  recap_statement += "I earn $"+c.income+". ";
              }
              recap_statement += "Is this correct?";
              return recap_statement;
          },
          goTo        : 'ichi06'
      },
  
      'ichi06': {
          type    : 'choice',
          emote       : 'question',
          choices : [
              {
                  label       : 'Yes',
                  goTo        : 'ichi07'
              },
  
              {
                  label       : 'No',
                  goTo        : 'ichi01'
              }
          ]
      },
  
      'ichi07': {
          type        : 'dialogue',
          text        : "Thanks! I'm good to go!",
          end         : true,
          emote       : 'question',
          triggeredText: function(npc) {
              npc.dialogueId = "step1_start";
          },
          action      : function(npc) {
              if (npc) {
  
                  if (!npc.data().npc.completedme) {
                      //If first time completing this child, mark 'em done.
                      Game.children_left_to_fill_out -= 1;
                  }
                  npc.data().npc.completedme = true;
  
                  var move_directions = [];
  
                  move_directions.push('up');
                  move_directions.push('up');
  
                  move_directions.push('up');
                  move_directions.push('up');
                  move_directions.push('up');
  
                  npc.npc('move', move_directions);
  
                  setTimeout(function(){
                      npc.find('.npc-sprite').removeClass('up').addClass('down');
                  }, 2000);
  
                  if (Game.children_left_to_fill_out <= 0) {
                      all_foster_or_homeless_children = true;
                      for(var o in Game.formData.children) {
                          if (!Game.formData.children[o].foster_child) {
                              if (!Game.formData.children[o].homeless) {
                                  all_foster_or_homeless_children = false;
                              }
                          }
                      }
  
                      if (all_foster_or_homeless_children) {
                          //skip to step 4
                          setTimeout(function(){
                              var newDialogue = Dialogue["step4_npcstart"];
                              $('#n005').npc('talk', newDialogue);
                              $('#n005').data('npc').dialogueId = "step4_start";
                          }, 1000);
                      }
                      else {
                          setTimeout(function(){
                              var newDialogue = Dialogue["step2_npcstart"];
                              $('#n003').npc('talk', newDialogue);
                              $('#n003').data('npc').dialogueId = "step2_start";
                          }, 1000);
                      }
                  }
              }
          }
      },
  
      'step2_npcstart': {
          type        : 'dialogue',
          text        : "Excellent! Come talk to me to move on to the next step.",
          emote       : 'question',
          end         : true,
          action      : function(npc) {
              if (npc && !npc.data().npc['frozen']) {
                  npc.data().npc['frozen'] = true;
  
                  var move_directions = [];
  
                  move_directions.push('down');
                  move_directions.push('down');
                  move_directions.push('down');
                  move_directions.push('down');
                  move_directions.push('right');
  
                  npc.npc('move', move_directions);
  
                  setTimeout(function(){
                      npc.find('.npc-sprite').removeClass('right').addClass('down');
                  }, 2000);
  
                  setTimeout(function(){
                      npc.npc('emote', 'question');
                  }, 500);
              }
          }
      },
  
      'step2_start': {
          type        : 'dialogue',
          emote       : 'question',
          text        : "Do any Household Members (including you) currently participate in one or more of the following assistance programs: SNAP, TANF, or FDPIR?",
          goTo        : 'step2_choice'
      },
  
      'step2_choice': {
          type    : 'choice',
          emote       : 'question',
          choices : [
              {
                  label       : 'No (we do not partipicate in SNAP, TANF, or FDPIR)',
                  goTo        : 'step2_end'
              },
              {
                  label       : 'Yes (we participate in SNAP, TANF, or FDPIR)',
                  goTo        : 'step2_followup'
              }
          ]
      },
  
      'step2_followup': {
          type    : 'input',
          goTo    : 'step2_end',
          emote       : 'question',
          label : 'What is your case number?',
          action: function(npc, value) {
              Game.formData['case_number'] = value;
          }
      },
  
      'step2_end': {
          type        : 'dialogue',
          text        : "Excellent! We're done here, move on to the next room.",
          emote       : 'question',
          end         : true,
          action: function(npc, value) {
              if (npc) {
                  if (Game.formData['case_number']) {
                      //skip to step 4
                      setTimeout(function(){
                          var newDialogue = Dialogue["step4_npcstart"];
                          $('#n005').npc('talk', newDialogue);
                          $('#n005').data('npc').dialogueId = "step4_start";
                      }, 1000);
                  }
                  else {
                      setTimeout(function(){
                          var newDialogue = Dialogue["step3_npcstart"];
                          $('#n004').npc('talk', newDialogue);
                          $('#n004').data('npc').dialogueId = "step3_start";
                      }, 1000);
                  }
              }
          }
      },
  
      'step3_npcstart': {
          type        : 'dialogue',
          text        : "Excellent! Come talk to me to move on to the next step.",
          emote       : 'happiness',
          end         : true,
          action      : function(npc) {
              if (npc && !npc.data().npc['frozen']) {
                  npc.data().npc['frozen'] = true;
                  var move_directions = [];
  
                  move_directions.push('down');
                  move_directions.push('down');
                  move_directions.push('down');
                  move_directions.push('down');
                  move_directions.push('left');
  
                  npc.npc('move', move_directions);
  
                  setTimeout(function(){
                      npc.find('.npc-sprite').removeClass('left').addClass('down');
                  }, 2000);
  
                  setTimeout(function(){
                      npc.npc('emote', 'question');
                  }, 500);
              }
          }
      },
  
      'step3_start': {
          type        : 'dialogue',
          emote       : 'question',
          text        : "We're going to finish gathering details about your household here.",
          goTo        : 'step3_i1'
      },
  
      'step3_i1': {
          type    : 'input',
          emote       : 'question',
          label   : 'How many adults are in your household?',
          goTo    : 'step3_i2',
          action: function(npc, value) {
              var total_adults = parseInt(value);
              Game.adults_left_to_fill_out = total_adults;
              for (var i=1; i<=10; i++) {
                  $('#adult'+i).hide();
              }
              for (var i=1; i<=total_adults; i++) {
                  $('#adult'+i).show();
              }
          }
      },
  
      'step3_i2': {
          type        : 'dialogue',
          text        : "...",
          emote       : 'question',
          dynamicText: function(){
              return "You said there are "+Game.adults_left_to_fill_out+" adults and "+Object.keys(Game.formData.children).length+" children in your household, is this correct?";
          },
          goTo        : 'step3_i3'
      },
  
      'step3_i3': {
          type    : 'choice',
          emote       : 'question',
          choices : [
              {
                  label       : 'Yes',
                  goTo        : 'step3_i4'
              },
  
              {
                  label       : 'No',
                  goTo        : 'step3_i1',
              }
          ]
      },
  
  
      'step3_i4': {
          type        : 'dialogue',
          text        : "Excellent! I created avatars to represent each adult. Continue by talking to them!",
          emote       : 'happiness',
          end         : true
      },
  
      'dadult01': {
          type        : 'dialogue',
          emote       : 'question',
          text        : "I'm an avatar representing an adult in your family! Help fill out more information about me.",
          goTo        : 'dadult02'
      },
  
      'dadult02': {
          type    : 'input',
          goTo    : 'dadult03',
          emote       : 'question',
          label : 'What is my name? (First name, last name)',
          action: function(npc, value) {
              var npc_id = npc.data()['npc'].id;
              if (!(npc_id in Game.formData['adults'])) {
                  Game.formData['adults'][npc_id] = {};
              }
              Game.formData['adults'][npc_id]['name'] = value;
          }
      },
  
      'dadult03': {
          type    : 'inputincome',
          goTo    : 'dadult04',
          emote       : 'question',
          label : 'What income do I get from work?',
          action: function(npc, value) {
              if (npc && value) {
                  Game.formData['adults'][npc.data()['npc'].id]['work_income'] = value;
              }
          }
      },
  
      'dadult04': {
          type    : 'inputincome',
          goTo    : 'dadult05',
          emote       : 'question',
          label : 'What income do I get from Public Assistance/Child Support/Alimony?',
          action: function(npc, value) {
              if (npc && value) {
                  Game.formData['adults'][npc.data()['npc'].id]['public_assistance_income'] = value;
              }
          }
      },
  
      'dadult05': {
          type    : 'inputincome',
          goTo    : 'dadult06',
          emote       : 'question',
          label : 'What income do I get from Pensions/Retirement/All Other Sources?',
          action: function(npc, value) {
              if (npc && value) {
                  Game.formData['adults'][npc.data()['npc'].id]['pension_income'] = value;
              }
          }
      },
  
      'dadult06': {
          type        : 'dialogue',
          text        : "Thanks! I'm good to go!",
          emote       : 'question',
          end         : true,
          triggeredText: function(npc) {
              npc.dialogueId = "step1_start";
          },
          action      : function(npc) {
              if (npc) {
                  Game.adults_left_to_fill_out -= 1;
                  var move_directions = [];
  
                  move_directions.push('down');
                  move_directions.push('down');
  
                  move_directions.push('down');
                  move_directions.push('down');
                  move_directions.push('down');
  
                  npc.npc('move', move_directions);
  
                  setTimeout(function(){
                      npc.find('.npc-sprite').removeClass('down').addClass('up');
                  }, 2000);
  
                  if (Game.adults_left_to_fill_out <= 0) {
                      setTimeout(function(){
                          var newDialogue = Dialogue["step4_npcstart"];
                          $('#n005').npc('talk', newDialogue);
                          $('#n005').data('npc').dialogueId = "step4_start";
                      }, 1000);
                  }
              }
          }
      },
  
      'step4_npcstart': {
          type        : 'dialogue',
          text        : "One last step remains! Go back to the lobby, and then join me in the final room on the left to complete it!",
          emote       : 'happiness',
          end         : true,
          action      : function(npc) {
              if (npc && !npc.data().npc['frozen']) {
                  npc.data().npc['frozen'] = true;
                  var move_directions = [];
  
                  move_directions.push('left');
                  move_directions.push('left');
                  move_directions.push('up');
  
                  npc.npc('move', move_directions);
  
                  setTimeout(function(){
                      npc.find('.npc-sprite').removeClass('up').addClass('down');
                  }, 2000);
  
                  setTimeout(function(){
                      npc.npc('emote', 'question');
                  }, 500);
              }
          }
      },
  
      'step4_start': {
          type        : 'dialogue',
          emote       : 'question',
          text        : "To finalize the form, we'll need a signature and some contact information. Let's go through that now.",
          goTo        : 'step4_form1a'
      },
  
      'step4_form1a': {
          type    : 'choice',
          emote       : 'question',
          choices : [
              {
                  label       : 'I have a SSN.',
                  goTo        : 'step4_form1b'
              },
  
              {
                  label       : 'I do not have a SSN.',
                  goTo        : 'step4_form1c'
              }
          ]
      },
  
      'step4_form1b': {
          type    : 'input',
          emote       : 'question',
          label   : 'What are the last 4 digits of your SSN?',
          goTo    : 'step4_form1c',
          action: function(npc, value) {
              Game.formData['ssn'] = value;
          }
      },
  
  
      'step4_form1c': {
          type    : 'input',
          emote       : 'question',
          label   : 'What is your street address?',
          goTo    : 'step4_form2',
          action: function(npc, value) {
              Game.formData['street_address'] = value;
          }
      },
  
      'step4_form2': {
          type    : 'input',
          emote       : 'question',
          label   : 'City?',
          goTo    : 'step4_form3',
          action: function(npc, value) {
              Game.formData['city'] = value;
          }
      },
  
      'step4_form3': {
          type    : 'input',
          emote       : 'question',
          label   : 'State?',
          goTo    : 'step4_form4',
          action: function(npc, value) {
              Game.formData['state'] = value;
          }
      },
  
      'step4_form4': {
          type    : 'input',
          emote       : 'question',
          label   : 'Zip Code?',
          goTo    : 'step4_form5',
          action: function(npc, value) {
              Game.formData['zip'] = value;
          }
      },
  
      'step4_form5': {
          type    : 'inputoptional',
          emote       : 'question',
          label   : 'Phone number? (optional, hit enter to skip)',
          goTo    : 'step4_form6',
          action: function(npc, value) {
              Game.formData['phonenumber'] = value;
          }
      },
  
      'step4_form6': {
          type    : 'inputoptional',
          emote       : 'question',
          label   : 'Email? (optional, hit enter to skip)',
          goTo    : 'step4_form7',
          action: function(npc, value) {
              Game.formData['email'] = value;
          }
      },
  
      'step4_form7': {
          type    : 'input',
          emote       : 'question',
          label   : 'What is your name?',
          goTo    : 'step4_form8a',
          action: function(npc, value) {
              Game.formData['printedname'] = value;
          }
      },
  
      'step4_form8a': {
          type    : 'dialogue',
          emote       : 'question',
          text   : 'I certify (promise) that all information on this application is true and that all income is reported. I understand that this information is given in connection with the receipt of Federal funds, and that school officials may verify (check) the information.',
          goTo    : 'step4_form8b'
      },
  
      'step4_form8b': {
          type    : 'dialogue',
          emote       : 'question',
          text   : 'I am aware that if I purposely give false information, my children may lose meal benefits, and I may be prosecuted under applicable State and Federal laws.',
          goTo    : 'step4_form9'
      },
  
      'step4_form9': {
          type    : 'input',
          emote       : 'question',
          label   : 'Type in your name here, this will operate as your legal signature:',
          goTo    : 'step4_form10a',
          action: function(npc, value) {
              Game.formData['signature'] = value;
          }
      },
  
      'step4_form10a': {
          type    : 'dialogue',
          emote       : 'question',
          text   : "We are required to ask for information about your children's race and ethnicity. This information is important and helps to make sure we are fully serving our community.",
          goTo    : 'step4_form10b'
      },
  
      'step4_form10b': {
          type    : 'dialogue',
          emote       : 'question',
          text   : "Responding to this section is optional and does not affect your children's eligibility for free or reduced price meals.",
          goTo    : 'step4_form11'
      },
  
      'step4_form11': {
          type    : 'choice',
          emote       : 'question',
          choices : [
              {
                  label       : 'I would NOT like to fill out this optional information.',
                  goTo        : 'step4_end'
              },
              {
                  label       : 'I would like to fill out this optional information.',
                  goTo        : 'step4_form12'
              }
          ]
      },
  
      'step4_form12': {
          type    : 'choice',
          emote       : 'question',
          choices : [
              {
                  label       : 'Ethnicity: Not Hispanic or Latino',
                  goTo        : 'step4_form13',
                  action: function(npc, value) {
                      Game.formData['ethnicity_hispanic'] = false;
                  }
              },
              {
                  label       : 'Ethnicity: Hispanic or Latino',
                  goTo        : 'step4_form13',
                  action: function(npc, value) {
                      Game.formData['ethnicity_hispanic'] = true;
                  }
              }
          ]
      },
  
      'step4_form13': {
          type    : 'choice',
          emote       : 'question',
          choices : [
              {
                  label       : 'Race: NOT American Indian or Alaskan Native',
                  goTo        : 'step4_form14',
                  action: function(npc, value) {
                      Game.formData['race_american_indian'] = false;
                  }
              },
              {
                  label       : 'Race: American Indian or Alaskan Native',
                  goTo        : 'step4_form14',
                  action: function(npc, value) {
                      Game.formData['race_american_indian'] = true;
                  }
              }
          ]
      },
  
      'step4_form14': {
          type    : 'choice',
          emote       : 'question',
          choices : [
              {
                  label       : 'Race: NOT Asian',
                  goTo        : 'step4_form15',
                  action: function(npc, value) {
                      Game.formData['race_asian'] = false;
                  }
              },
              {
                  label       : 'Race: Asian',
                  goTo        : 'step4_form15',
                  action: function(npc, value) {
                      Game.formData['race_asian'] = true;
                  }
              }
          ]
      },
  
      'step4_form15': {
          type    : 'choice',
          emote       : 'question',
          choices : [
              {
                  label       : 'Race: NOT Black or African American',
                  goTo        : 'step4_form16',
                  action: function(npc, value) {
                      Game.formData['race_black'] = false;
                  }
              },
              {
                  label       : 'Race: Black or African American',
                  goTo        : 'step4_form16',
                  action: function(npc, value) {
                      Game.formData['race_black'] = true;
                  }
              }
          ]
      },
  
      'step4_form16': {
          type    : 'choice',
          emote       : 'question',
          choices : [
              {
                  label       : 'Race: NOT Native Hawaiian or Other Pacific Islander',
                  goTo        : 'step4_form17',
                  action: function(npc, value) {
                      Game.formData['race_hawaiian'] = false;
                  }
              },
              {
                  label       : 'Race: Native Hawaiian or Other Pacific Islander',
                  goTo        : 'step4_form17',
                  action: function(npc, value) {
                      Game.formData['race_hawaiian'] = true;
                  }
              }
          ]
      },
  
      'step4_form17': {
          type    : 'choice',
          emote       : 'question',
          choices : [
              {
                  label       : 'Race: NOT White',
                  goTo        : 'step4_end',
                  action: function(npc, value) {
                      Game.formData['race_white'] = false;
                  }
              },
              {
                  label       : 'Race: White',
                  goTo        : 'step4_end',
                  action: function(npc, value) {
                      Game.formData['race_white'] = true;
                  }
              }
          ]
      },
  
      'step4_end': {
          type        : 'dialogue',
          text        : "Excellent! We're all done!",
          emote       : 'happiness',
          end         : true,
          action: function(npc, value) {
              if (!Game.formsub) {
                  Game.formsub = true;
                  submit_form();
              }
          }
      },
  }
  
  
  /** GAME ******************************************************************************************/
  
  Game = {
      activeNPC          : '',
      npcDialogueProgress: {},
      formData: {
          'children': {},
          'adults': {}
      },
      currentArea        : 'a000',
      currentDirection   : 'down',
      currentFocus       : '',
      domain             : location.protocol + '//' + location.host + '/static/pub/',
      directions         : {
          up      : 'up',
          down    : 'down',
          left    : 'left',
          right   : 'right'
      },
      fps                : 60,
      gridCellSize       : 32,
      loading            : 0,
      preloading         : false,
      pressedKeys        : [],
      prevArea           : 'a000',
  
      /**
       *
       */
      calculateZindex : function (object) {
          object.css({
              zIndex: object.position().top
          });
      },
  
      /**
       *
       */
      checkCollisions : function (object, direction) {
          var
              objectCoord = Game.getCoordinates(object),
              offsetL     = 0,
              offsetT     = 0;
  
          switch (direction) {
  
              // Up
              case Game.directions.up:
                  offsetT = -1;
  
                  break;
  
              // Down
              case Game.directions.down:
                  offsetT = 1;
  
                  break;
  
              // Left
              case Game.directions.left:
                  offsetL = -1;
  
                  break;
  
              // Right
              case Game.directions.right:
                  offsetL = 1;
  
                  break;
          }
  
          if (Stage.collisionsMap[objectCoord.y + offsetT]) {
              if (Stage.collisionsMap[objectCoord.y + offsetT][objectCoord.x + offsetL]) {
                  return Stage.collisionsMap[objectCoord.y + offsetT][objectCoord.x + offsetL];
              }
          }
  
          if (Stage.portalsMap[objectCoord.y + offsetT]) {
              if (Stage.portalsMap[objectCoord.y + offsetT][objectCoord.x + offsetL]) {
                  return Stage.portalsMap[objectCoord.y + offsetT][objectCoord.x + offsetL];
              }
          }
  
          if (Stage.npcsMap[objectCoord.y + offsetT]) {
              if (Stage.npcsMap[objectCoord.y + offsetT][objectCoord.x + offsetL]) {
                  return Stage.npcsMap[objectCoord.y + offsetT][objectCoord.x + offsetL];
              }
          }
  
          //if (Stage.playersMap[objectCoord.y + offsetT]) {
          //    if (Stage.playersMap[objectCoord.y + offsetT][objectCoord.x + offsetL]) {
          //        return Stage.playersMap[objectCoord.y + offsetT][objectCoord.x + offsetL];
          //    }
          //}
  
          return false;
      },
  
      /**
       *
       */
      getCoordinates : function (object) {
          var objectPos = object.position();
  
          return {
              'x': objectPos.left / Game.gridCellSize,
              'y': objectPos.top / Game.gridCellSize
          }
      },
  
      /**
       *
       */
      moveObject : function (object, direction, callback) {
          var
              animateOptions  = {},
              objectPos       = object.position(),
              left            = objectPos.left,
              top             = objectPos.top;
  
          switch (direction) {
  
              // Up
              case Game.directions.up:
                  animateOptions = {
                      top: top - Game.gridCellSize
                  }
  
                  break;
  
              // Down
              case Game.directions.down:
                  animateOptions = {
                      top: top + Game.gridCellSize
                  }
  
                  break;
  
              // Left
              case Game.directions.left:
                  animateOptions = {
                      left: left - Game.gridCellSize
                  }
  
                  break;
  
              // Right
              case Game.directions.right:
                  animateOptions = {
                      left: left + Game.gridCellSize
                  }
  
                  break;
  
          }
  
          object.stop().animate(
              animateOptions,
              180,
              'linear',
              function () {
                  Game.calculateZindex(object);
  
                  if (callback) {
                      callback();
                  }
              }
          );
      },
  
      /**
       *
       */
      preload : function () {
          var preload = [
                  // Tile Map
                  '/static/pub/img/tile-map.gif',
  
                  // Player
                  '/static/pub/img/rene-down.gif',
                  '/static/pub/img/rene-down-walking.gif',
                  '/static/pub/img/rene-left.gif',
                  '/static/pub/img/rene-left-walking.gif',
                  '/static/pub/img/rene-up.gif',
                  '/static/pub/img/rene-up-walking.gif',
  
                  // NPC
                  '/static/pub/img/dude.gif',
  
                  // Modals
                  '/static/pub/img/modal-border.gif',
  
                  // Emotes
                  '/static/pub/img/emote-happiness.gif',
                  '/static/pub/img/emote-love.gif',
                  '/static/pub/img/emote-question.gif',
                  '/static/pub/img/emote-sadness.gif',
                  '/static/pub/img/emote-talk-angry.gif',
                  '/static/pub/img/emote-talk-happy.gif',
                  '/static/pub/img/emote-think.gif',
  
                  // Icons
                  '/static/pub/img/icon-continue.gif'
              ],
              outstanding = preload.length;
  
          Game.preloading = true;
  
          if ($('#loading').length == 0) {
              $('body').append('<div id="loading">Loading...</div>');
          }
  
          $.each(preload, function (index, value) {
              Game.loading = true;
  
              $.ajax({
                  type    : 'GET',
                  url     : value
              }).done(function () {
                  // Do nothing
              }).fail(function () {
                  // Do nothing
              }).always(function () {
                  outstanding--;
  
                  if (outstanding === 0) {
                      Game.loading = false;
  
                      Game.start();
                  }
              });
          });
      },
  
      /**
       *
       */
      start : function () {
          setInterval(function () {
              Game.update();
          }, 1000 / Game.fps);
  
          Stage.init('a000');
      },
  
      /**
       *
       */
      update : function () {
          if (Game.loading) {
              if ($('#loading').length == 0) {
                  $('body').append('<div id="loading">Loading...</div>');
              }
          } else {
              $('#loading').remove();
          }
  
          if ($('.modal').length > 0) {
              $.modals.checkButtons();
          }
  
          if ($('#player').length > 0) {
              $.player.checkButtons();
          }
  
          Stage.checkButtons();
      }
  };
  
  /** BINDINGS **************************************************************************************/
  
  var game_keys = [38,37,39,40,32,13]; //Arrow keys, spacebar, enter
  $(document).on('keydown', function (event) {
      var key = event.keyCode || event.which;
      if (key in game_keys) {
          event.preventDefault();
      }
      Game.pressedKeys[event.keyCode] = true;
  });
  
  $(document).on('keyup', function (event) {
  
      var key = event.keyCode || event.which;
      if (key in game_keys) {
          event.preventDefault();
      }
      Game.pressedKeys[event.keyCode]     = false;
      $.modals.allowPress                 = true;
      $.player.allowPress                 = true;
  });
  
  function trigger_beginning() {
      var newDialogue = Dialogue[$('#n002').data('npc')['dialogueId']];
      if ('triggeredText' in newDialogue) {
          newDialogue.triggeredText($('#n002').data('npc'));
      }
      $('#n002').npc('talk', newDialogue);
  
      //$.modals.create(
      //    {
      //        height  : 16,
      //        width   : '25%'
      //    },
      //    {
      //        left    : 20 + 3, // The 3 is half the border-image-outset value
      //        top     : $(window).height() - 68 - 3 // The 3 is half the border-image-outset value
      //    },
      //    Dialogue['help1']
      //);
  }
  
  /** GAME START! ***********************************************************************************/
  
  //
  $(document).on('ready', function () {
      Game.preload();
  });
  
  
  /** MODAL WINDOWS *********************************************************************************/
  
  function Modals () {
      this.activeModal    = '';
      this.allowPress     = true;
      this.cancelTyping   = false;
      this.continueIcon   = '<div class="icon continue"></div>'
      this.id             = 0;
      this.modalCounter   = 0;
      this.npc            = '';
      this.typing         = false;
  
      /**
       *
       */
      this.checkButtons = function () {
          var modal = $.modals.activeModal;
  
          if (modal.length == 0 || modal.html() == '') {
              return;
          }
  
          var
              activeElement   = $(document.activeElement),
              dialogue        = modal.data('modal')['dialogue'];
  
          if (!$.modals.allowPress) {
              return;
          }
          
          // Spacebar, Enter
          if (Game.pressedKeys[13] || Game.pressedKeys[32]) {
              $.modals.allowPress = false;
  
              switch (true) {
                  case (activeElement.is('li')) :
                      if (Game.pressedKeys[13]) { //enter key only
                          var choice = dialogue.choices[activeElement.index()];
                          if (choice.action) {
                              choice.action(modal.data('modal')['npc'], choice);
                          }
  
                          if (choice.goTo) {
                              return modal.modal('populate', Dialogue[choice.goTo]);
                          }
  
                      }
                      break;
  
                  case (activeElement.is('input') || activeElement.hasClass('inputincome')) :
                      if (Game.pressedKeys[13]) { //enter key only
  
                          var value = activeElement.val();
  
                          if (activeElement.hasClass('inputincome')) {
                              value = activeElement.parent().find('input').val() + " " + activeElement.parent().find('select').val();
                          }
  
                          if (activeElement.hasClass('inputoptional') || value) {
                              if (dialogue.action) {
                                  dialogue.action(modal.data('modal')['npc'], value);
                              }
  
                              if (dialogue.goTo) {
                                  return modal.modal('populate', Dialogue[dialogue.goTo]);
                              }
                          }
  
                      }
                      break;
                  
                  default :
                      if ($.modals.typing) {
                          $.modals.cancelTyping = true;
  
                          return;
                      }
  
                      if (dialogue.action) {
                          dialogue.action(modal.data('modal')['npc']);
                      }
  
                      if (dialogue.goTo) {
                          modal.modal('populate', Dialogue[dialogue.goTo]);
  
                          break;
                      } else if (dialogue.end) {
                          modal.modal('destroy', $('#player'));
  
                          if (modal.data('modal')['npc']) {
                              modal.data('modal')['npc'].data('npc')['wanderPause'] = false;
                          }
  
                          break;
                      }
  
                      break;
              }
          }
  
          switch (true) {
  
              // Up Arrow
              case ((Game.pressedKeys[38])) :
                  $.modals.allowPress = false;
  
                  switch (true) {
                      case (activeElement.is('li')) :
                          activeElement.prev('li').trigger('focus');
  
                          break;
                  }
  
                  break;
  
              // Down Arrow
              case ((Game.pressedKeys[40])) :
                  $.modals.allowPress = false;
  
                  switch (true) {
                      case (activeElement.is('li')) :
                          activeElement.next('li').trigger('focus');
  
                          break;
                  }
  
                  break;
  
              // Left Arrow
              case ((Game.pressedKeys[37])) :
                  $.modals.allowPress = false;
  
                  break;
  
              // Right Arrow
              case ((Game.pressedKeys[39])) :
                  $.modals.allowPress = false;
  
                  break;
  
              default:
  
                  break;
          }
      }
  
      /**
       *
       */
      this.create = function (size, position, dialogue, npc) {
          var
              delay   = 0,
              modal   = '',
              id      = $.modals.modalCounter + '';
  
          $.modals.allowPress = false;
  
          while (id.length < (3 - (($.modals.modalCounter + '')).length + 1)) {
              id = '0' + id;
          }
  
          id = 'm' + id;
  
          if (dialogue.type == 'notification') {
              delay = 180;
  
              $('.modal.notification').not(modal).each(function (index, element) {
                  $(element).modal('destroy', null);
              });
          }
  
          setTimeout(function () {
              $('#modals').append('<div id="' + id + '" class="modal ' + dialogue.type + '" tabindex="0"></div>');
  
              $.modals.modalCounter++;
  
              modal = $('#' + id);
  
              modal.data('modal', new Modals());
              modal.data('modal')['id']   = id;
  
              if (npc) {
                  modal.data('modal')['npc']  = npc ? npc : '';
              }
  
              modal.css({
                  left    : position.left,
                  top     : position.top
              });
  
              modal.animate({
                  height  : size.height,
                  width   : size.width
              }, 180, function () {
                  modal.modal('populate', dialogue);
  
                  $.modals.activeModal = modal;
              });
          }, delay);
      },
  
      /**
       *
       */
      this.destroy = function (focus) {
          var
              modal   = $(this),
              npc     = modal.data('modal')['npc'];
  
          modal.html('');
  
          modal.animate({
              height  : 0,
              width   : 0,
              zIndex  : 0
          }, 180, function () {
              modal.remove();
  
              if (npc) {
                  npc.npc('destroyEmote');
  
                  $.npc.talking = false;
              }
  
              if (focus) {
                  focus.trigger('focus');
              }
          });
      },
  
      /**
       *
       */
      this.option = function (option, value) {
          var
              element = $(this),
              data    = element.data('modal');
  
          if (typeof value === 'undefined') {
              return data[option];
          } else {
              data[option] = value;
  
              switch (option) {
  
              }
          }
      },
  
      /**
       *
       */
      this.populate = function (dialogue) {
          var
              emote   = dialogue.emote,
              modal   = $(this),
              npc     = Game.activeNPC,
              type    = dialogue.type;
  
          if (npc && emote) {
              npc.npc('emote', emote);
          }
  
          if ('dynamicText' in dialogue) {
              dialogue.text = dialogue.dynamicText(Game.activeNPC);
          }
  
          if ('dynamicLabel' in dialogue) {
              dialogue.label = dialogue.dynamicLabel(Game.activeNPC);
          }
  
          modal.data('modal')['dialogue'] = dialogue;
  
          switch (type) {
  
              // Choice
              case 'choice':
                  var choices = '<ul class="choice">';
  
                  $.each(dialogue.choices, function (index, value) {
                      if ('dynamicLabel' in value) {
                          value.label = value.dynamicLabel(Game.activeNPC);
                      }
                      choices += '<li tabindex="0">' + value.label + '</li>'
                  });
  
                  choices += '</ul>';
  
                  modal.html(choices).find('li:first').trigger('focus');
  
                  break;
  
              // Input
              case 'input':
                  var choices = dialogue.label + '<br><input type="text" tabindex="0">';
                  modal.html(choices).find('input').trigger('focus');
                  break;
  
              // Input
              case 'inputoptional':
                  var choices = dialogue.label + '<br><input class="inputoptional" type="text" tabindex="0">';
                  modal.html(choices).find('input').trigger('focus');
                  break;
  
              // Input
              case 'inputincome':
                  var choices = dialogue.label + '<br>$<input class="inputincome" type="text" tabindex="0"> <select class="inputincome"><option>Weekly</option><option>Bi-Weekly</option><option>2x Month</option><option>Monthly</option></select>';
                  modal.html(choices).find('input').trigger('focus');
                  break;
  
              // Dialogue
              case 'dialogue':
                  var
                      counter     = 0,
                      interval    = setInterval(function () {
                          if ($.modals.cancelTyping) {
                              modal.append(dialogue.text.substr(counter, dialogue.text.length));
  
                              $.modals.cancelTyping   = false;
                              $.modals.typing         = false;
  
                              modal.append($.modals.continueIcon);
  
                              clearInterval(interval);
  
                              return;
                          };
  
                          modal.append(dialogue.text.charAt(counter));
  
                          Sounds.fx.bip.play();
  
                          counter++;
  
                          if (counter >= dialogue.text.length) {
                              $.modals.typing = false;
  
                              modal.append($.modals.continueIcon);
  
                              clearInterval(interval);
  
                              return;
                          }
                      }, 50);
  
                  $.modals.typing = true;
  
                  modal.html('');
  
                  //if (dialogue.action) {
                  //    dialogue.action(modal.data('modal')['npc']);
                  //}
  
                  break;
  
              // Notification
              case 'notification':
                  modal.append(dialogue.text);
  
                  setTimeout(function () {
                      modal.modal('destroy', null);
                  }, 10000);
  
                  break;
  
          }
      }
  }
  
  $.fn.modal = function (option) {
      var
          element     = $(this[0]),
          otherArgs   = Array.prototype.slice.call(arguments, 1);
  
      if (typeof option !== 'undefined' && otherArgs.length > 0) {
          return element.data('modal')[option].apply(this[0], [].concat(otherArgs));
      }
  
      return element.data('modal');
  }
  
  $.modals = new Modals();
  
  
  /** NPCS ******************************************************************************************/
  
  function NPC () {
      this.dialogueId     = 'd000';
      this.id             = 0;
      this.talking        = false;
      this.wanderInterval = '';
      this.wanderPause    = false;
  
      /**
       *
       */
      this.create = function (data) {
          var npc;
  
          $('#objects').append('<div id="' + data.name + '" class="npc"><div class="npc-sprite"></div></div>');
  
          npc = $('#' + data.name);
  
          npc.data('npc', new NPC());
  
          npc.data('npc')['id']           = data.name;
          npc.data('npc')['dialogueId']   = data.properties.dialogue;
  
          npc.css({
              left    : data.x + 'px',
              top     : (data.y - Game.gridCellSize) + 'px'
          });
  
          if (!Stage.npcsMap[(data.y / Game.gridCellSize) - 1]) {
              Stage.npcsMap[(data.y / Game.gridCellSize) - 1] = {};
          }
  
          Stage.npcsMap[(data.y / Game.gridCellSize) - 1][(data.x / Game.gridCellSize)] = npc;
  
          if (data.properties.wander && data.properties.wander != "false") {
              npc.npc('wander');
          }
  
          if (data.properties.active && data.properties.active == 'false') {
              $('#'+data.name).hide();
          }
  
          if (data.properties.defaultdirection) {
              $('#'+data.name).find('.npc-sprite').addClass(data.properties.defaultdirection);
          }
  
      },
  
      /**
       *
       */
      this.destroy = function () {
          var npc = $(this);
  
          clearInterval(npc.data('npc')['wanderInterval']);
  
          npc.remove();
      }
  
      /**
       *
       */
      this.destroyEmote = function () {
          var npc = $(this);
  
          npc.find('.emote').animate({
              opacity : 0,
              top     : '-48px'
          }, 100, function () {
              $(this).remove();
          });
      }
  
      /**
       *
       */
      this.emote = function (emotion) {
          var
              npc     = $(this),
              emote   = npc.find('.emote');
  
          if (emote.length === 0) {
              npc.append('<div class="emote ' + emotion + '" style="opacity: 0; top: -48px"></div>');
  
              npc.find('.emote').animate({
                  opacity : 1,
                  top     : '-32px'
              }, 100);
          } else {
              npc.find('.emote').replaceWith('<div class="emote ' + emotion + '"></div>');
          }
      },
  
      /**
       *
       */
      this.move = function (direction) {
  
          //moving single space
          var direction_single = direction;
          if (typeof direction != "string") {
              direction_single = direction.shift();
          }
  
          var
              npc         = $(this),
              collision   = Game.checkCollisions(npc, direction_single),
              npcPos      = Game.getCoordinates(npc),
              npcSprite   = npc.find('.npc-sprite');
  
          Game.currentDirection = direction_single;
  
          npcSprite.removeClass('walking up down left right').addClass('walking ' + direction_single);
  
          if (collision) {
              npcSprite.removeClass('walking');
          } else {
              Game.moveObject(npc, direction_single, function () {
                  var newPos = Game.getCoordinates(npc);
  
                  if (!Stage.npcsMap[newPos.y]) {
                      Stage.npcsMap[newPos.y] = {};
                  }
  
                  Stage.npcsMap[newPos.y][newPos.x] = npc;
  
                  delete Stage.npcsMap[npcPos.y][npcPos.x];
  
                  npcSprite.removeClass('walking');
                  if (typeof direction != "string" && direction.length > 0) {
                      Game.activeNPC.npc('move', direction);
                  }
              });
          }
  
      },
  
      /**
       *
       */
      this.talk = function (dialogue) {
          var npc = $(this);
  
          if ($.npc.talking) {
              return;
          }
  
          $.npc.talking                   = true;
          npc.data('npc')['wanderPause']  = true;
  
          Game.activeNPC = npc;
  
          $.modals.create(
              {
                  height  : 80,
                  width   : 720
              },
              {
                  left    : ($(window).width() - (720 + Game.gridCellSize)) / 2,
                  top     : 20
              },
              dialogue,
              npc
          );
      },
  
      /**
       *
       */
      this.wander = function () {
          var npc = $(this);
  
          clearInterval(npc.data('npc')['wanderInterval']);
  
          npc.data('npc')['wanderInterval'] = setInterval(function () {
              var
                  direction = Game.directions[Object.keys(Game.directions)[Math.floor(Math.random() * Object.keys(Game.directions).length)]];
  
              if (Math.random() < 0.5 || npc.data('npc')['wanderPause'] === true) {
                  return;
              }
  
              npc.npc('move', direction);
  
          }, 1800);
      }
  }
  
  $.fn.npc = function (option) {
      var
          element     = $(this[0]),
          otherArgs   = Array.prototype.slice.call(arguments, 1);
  
      if (typeof option !== 'undefined' && otherArgs.length > 0) {
          return element.data('npc')[option].apply(this[0], [].concat(otherArgs));
      } else if (typeof option !== 'undefined') {
          return element.data('npc')[option].call (this[0]);
      } else {
          return element.data('npc');
      }
  }
  
  $.npc = new NPC();
  
  
  /** PLAYER ****************************************************************************************/
  
  function Player () {
      this.allowMove          = true;
      this.allowPress         = true;
      this.direction          = null;
      this.speedMultiplier    = 1;
  
      /**
       *
       */
      this.checkButtons = function () {
          var player = $('#player');
  
          if (player.length == 0) {
              return;
          }
  
          var
              allowMove       = player.data('player')['allowMove'],
              allowPress      = $.player.allowPress;
              playerSprite    = $('#player-sprite');
  
          if (!allowPress) {
              return;
          }
  
          player.data('player')['speedMultiplier'] = 1;
  
          // Spacebar, Enter
          if (Game.pressedKeys[13] || Game.pressedKeys[32]) {
              var collision = Game.checkCollisions(player, player.data('player')['direction']);
  
              $.player.allowPress = false;
  
              if (collision) {
                  switch (true) {
                      case (collision) :
                          // Do nothing
  
                          break;
  
                      case (collision.is('.npc')) :
                          var newDialogue = Dialogue[collision.data('npc')['dialogueId']];
                          if ('triggeredText' in newDialogue) {
                              newDialogue.triggeredText(collision.data('npc'));
                          }
                          collision.npc('talk', newDialogue);
                          break;
                  }
              }
          }
  
  
          switch (true) {
  
              // Up Arrow
              case (Game.pressedKeys[38]) :
                  if (!$('.modal').length) {
                      var direction = player.data('player')['direction'] = Game.directions.up;
                      player.player('move', direction);
                  }
  
                  break;
  
              // Down Arrow
              case (Game.pressedKeys[40]) :
                  if (!$('.modal').length) {
                      var direction = player.data('player')['direction'] = Game.directions.down;
                      player.player('move', direction);
                  }
  
                  break;
  
              // Left Arrow
              case (Game.pressedKeys[37]) :
                  if (!$('.modal').length) {
                      var direction = player.data('player')['direction'] = Game.directions.left;
                      player.player('move', direction);
                  }
  
                  break;
  
              // Right Arrow
              case (Game.pressedKeys[39]) :
                  if (!$('.modal').length) {
                      var direction = player.data('player')['direction'] = Game.directions.right;
                      player.player('move', direction);
                  }
  
                  break;
  
              default:
                  playerSprite.removeClass('walking');
  
                  break;
          }
      }
  
      /**
       *
       */
      this.create = function (data) {
          var player;
  
          if (data.properties.prevArea === Game.prevArea) {
              $('#objects').append('<div id="player" style="left: ' + data.x + 'px; top: ' + (data.y - 32) + 'px;"><div id="player-sprite" class="' + Game.currentDirection + '"></div></div>');
  
              player = $('#player');
  
              player.data('player', new Player());
  
              if (!Stage.playersMap[(data.y / Game.gridCellSize) - 1]) {
                  Stage.playersMap[(data.y / Game.gridCellSize) - 1] = {};
              }
  
              Stage.playersMap[(data.y / Game.gridCellSize) - 1][(data.x / Game.gridCellSize)] = player;
          }
      }
  
      /**
       *
       */
      this.destroy = function () {
          var player = $(this);
  
          player.remove();
      }
  
      /**
       *
       */
      this.move = function (direction, callback) {
          var
              player          = $(this),
              playerPos       = Game.getCoordinates(player);
              playerSprite    = $('#player-sprite');
  
          if (player.data('player')['allowMove']) {
              var collision = Game.checkCollisions(player, direction);
  
              Game.currentDirection = direction;
  
              playerSprite.removeClass().addClass('walking ' + direction);
  
              if (collision) {
                  switch (true) {
  
                      // Not Doorway or Stairs
                      case (collision) :
                          playerSprite.removeClass('walking');
  
                          Sounds.fx.bump.play();
  
                          break;
  
                      // Doorway
                      case (collision.is('.doorway')) :
                          player.data('player')['allowMove'] = false;
  
                          Sounds.fx.door.play();
  
                          Game.moveObject(player, direction, function () {
                              var newPos = Game.getCoordinates(player);
  
                              if (!Stage.playersMap[newPos.y]) {
                                  Stage.playersMap[newPos.y] = {};
                              }
  
                              Stage.playersMap[newPos.y][newPos.x] = player;
  
                              Sounds.fx.enter.play();
  
                              delete Stage.playersMap[playerPos.y][playerPos.x];
  
                              Stage.init(collision.attr('data-area'));
                          });
  
                          break;
  
                      // Stairs
                      case (collision.is('.stairs')) :
                          player.data('player')['allowMove'] = false;
  
                          Sounds.fx.enter.play();
  
                          player.player('useStairs', collision.attr('data-direction'), function () {
                              Stage.init(collision.attr('data-area'));
                          });
  
                          break;
                  }
              } else {
                  player.data('player')['allowMove'] = false;
  
                  Game.moveObject(player, direction, function () {
                      var newPos = Game.getCoordinates(player);
  
                      player.data('player')['allowMove'] = true;
  
                      if (!Stage.playersMap[newPos.y]) {
                          Stage.playersMap[newPos.y] = {};
                      }
  
                      Stage.playersMap[newPos.y][newPos.x] = player;
  
                      delete Stage.playersMap[playerPos.y][playerPos.x];
                  });
  
                  Stage.scrollStage(direction);
              }
          }
      }
  
      /**
       *
       */
      this.useStairs = function (direction, callback) {
          var
              player      = $(this),
              playerPos   = player.position(),
              offsetL     = 0,
              offsetT     = 0;
  
          switch (direction) {
  
              // Down & Left
              case 'dl':
                  offsetL = -32;
                  offsetT = 0; // Player clips through the stairs if set to 32
  
                  break;
  
              // Down & Right
              case 'dr':
                  offsetL = 32;
                  offsetT = 32;
  
                  break;
  
              // Up & Left
              case 'ul':
                  offsetL = -32;
                  offsetT = -32;
  
                  break;
  
              // Up & Right
              case 'ur':
                  offsetL = 32;
                  offsetT = -32;
  
                  break;
          }
  
          player.addClass('walking').stop().animate({
              left    : playerPos.left + offsetL,
              top     : playerPos.top + offsetT
          }, 180, 'linear', function () {
              if (callback) {
                  callback();
              }
          });
      }
  }
  
  $.fn.player = function (option) {
      var
          element     = $(this[0]),
          otherArgs   = Array.prototype.slice.call(arguments, 1);
  
      if (typeof option !== 'undefined' && otherArgs.length > 0) {
          return element.data('player')[option].apply(this[0], [].concat(otherArgs));
      } else if (typeof option !== 'undefined') {
          return element.data('player')[option].call (this[0]);
      } else {
          return element.data('player');
      }
  }
  
  $.player = new Player();
  
  
  /** SOUNDS ****************************************************************************************/
  
  Sounds = {
      currentMusic   : '',
      currentMusicId : '',
      fx             : {
          bip     : new Audio(Game.domain + '/sounds/bip.wav'),
          bump    : new Audio(Game.domain + '/sounds/bump.wav'),
          door    : new Audio(Game.domain + '/sounds/door.wav'),
          enter   : new Audio(Game.domain + '/sounds/enter.wav'),
          error   : new Audio(Game.domain + '/sounds/error.wav')
      },
      music          : {
          //shadesOfRed : Game.domain + '/sounds/Pokemon_Red_Version_Shades_of_Red_OC_ReMix.mp3',
          //wetDreams   : Game.domain + '/sounds/Pokemon_Blue_Version_Wet_Dreams_OC_ReMix.mp3'
      },
  
      changeMusic : function (newMusic) {
          if (Sounds.currentMusic) {
              Sounds.fade(Sounds.currentMusic, 0, function () {
                  Sounds.currentMusic.pause();
              });
          }
  
          Game.loading = true;
  
          Sounds.currentMusic   = new Audio(newMusic);
  
          Sounds.currentMusic.canplaythrough = Sounds.playMusic();
      },
  
      fade : function (sound, volume, callback) {
          var
              offset  = 0.0001,
              steps   = Math.abs(sound.volume - volume) / 0.0001;
  
          if (sound.volume > volume) {
              offset = -0.0001;
          }
  
          for (var i = 1; i <= steps; i++) {
              sound.volume += offset;
          }
  
          if (callback) {
              callback();
          }
      },
  
      playMusic : function () {
          Game.loading = false;
  
          Sounds.currentMusic.loop = true;
  
          Sounds.currentMusic.play();
  
          $.modals.create(
              {
                  height  : 16,
                  width   : '25%'
              },
              {
                  left    : 20 + 3, // The 3 is half the border-image-outset value
                  top     : $(window).height() - 68 - 3 // The 3 is half the border-image-outset value
              },
              Dialogue[Sounds.currentMusicId]
          );
      }
  }
  
  
  /** STAGE *****************************************************************************************/
  
  Stage = {
      collisionsMap  : [],
      height         : 0,
      npcsMap        : [],
      playersMap     : [],
      portalsMap     : [],
      tileMap        : {
          height  : 50,
          width   : 50
      },
      width          : 0,
  
      /**
       *
       */
      center : function () {
          var
              left        = 0,
              player      = $('#player'),
              playerPos   = player.position(),
              stage       = $('#stage'),
              top         = 0,
              windowH     = $(window).height(),
              windowW     = $(window).width();
  
          left    = (windowW / 2) - (playerPos.left + (player.width() / 2));
          top     = (windowH / 2) - (playerPos.top + ((player.height() + 8)/ 2));
  
          if (Stage.width <= windowW && Stage.height <= windowH) {
              left    = (windowW - Stage.width) / 2;
              top     = (windowH - Stage.height) / 2;
          }
  
          if (Stage.width > windowW && stage.offset().left > 0 && playerPos.left < windowW
              && Stage.height > windowH && stage.offset().top > 0 && playerPos.top < windowH) {
              left    = 0;
              top     = 0;
          }
  
          stage.css({
              height  : Stage.height,
              left    : left + 'px',
              top     : top + 'px',
              width   : Stage.width
          });
      },
  
      /**
       *
       */
      checkButtons : function () {
          // Shift
          if (Game.pressedKeys[16]) {
  
          }
  
          // Spacebar, Enter
          if (Game.pressedKeys[13] || Game.pressedKeys[32]) {
  
          }
  
          switch (true) {
  
              // W, Up Arrow
              case ((Game.pressedKeys[87] || Game.pressedKeys[38])) :
  
                  break;
  
              // S, Down Arrow
              case ((Game.pressedKeys[83] || Game.pressedKeys[40])) :
  
                  break;
  
              // A, Left Arrow
              case ((Game.pressedKeys[65] || Game.pressedKeys[37])) :
  
                  break;
  
              // D, Right Arrow
              case ((Game.pressedKeys[68] || Game.pressedKeys[39])) :
  
                  break;
  
              default:
  
                  break;
          }
      },
  
      /**
       *
       */
      cleanup : function () {
          Stage.collisionsMap   = [];
          Stage.npcsMap         = [];
          Stage.playersMap      = [];
          Stage.portalsMap      = [];
  
          $('.npc').each(function (index, element) {
              $(element).npc('destroy');
          });
  
          $('#player').each(function (index, element) {
              $(element).player('destroy');
          });
  
          $('#collisions').html('');
          // $('#modals').html('');
          $('#objects').html('');
          $('#tiles').html('');
  
          $(document).trigger('keyup');
      },
  
      /**
       *
       */
      drawCollisions : function (collisions) {
          var
              counter = 0,
              height  = collisions.height,
              row     = 0,
              width   = collisions.width;
  
          $.each(collisions.data, function (index, value) {
              if (!Stage.collisionsMap[row]) {
                  Stage.collisionsMap[row] = {};
              }
  
              Stage.collisionsMap[row][counter] = (value === 2);
  
              if (value !== 0) { // 0 is empty, 2 is a collision
                  $('#collisions').append(
                      '<div class="collision" '
                      + 'style="left: ' + (counter * Game.gridCellSize) + 'px; top: ' + (row * Game.gridCellSize) + 'px">'
                      + '</div>'
                  );
              }
  
              counter += (index + 1) % width === 0 ? -counter : 1;
              row     += (index + 1) % width === 0 ? 1 : 0;
          });
      },
  
      /**
       *
       */
      drawObjects : function (objects) {
          $.each(objects.objects, function (index, value) {
              switch (value.type) {
                  case 'player':
                      $.player.create(value);
  
                      break;
  
                  case 'npc':
                      $.npc.create(value);
  
                      break;
  
                  case 'doorway':
                      $('#objects').append(
                          '<div id="' + value.name + '" '
                          + 'class="object doorway" '
                          + 'data-area="' + value.properties.area + '" '
                          + 'style="left: ' + value.x + 'px; top: ' + (value.y - Game.gridCellSize) + 'px">'
                          + '</div>'
                      );
  
                      if (!Stage.portalsMap[(value.y / Game.gridCellSize) - 1]) {
                          Stage.portalsMap[(value.y / Game.gridCellSize) - 1] = {};
                      }
  
                      Stage.portalsMap[(value.y / Game.gridCellSize) - 1][(value.x / Game.gridCellSize)] = $('#' + value.name);
  
                      break;
  
                  case 'stairs':
                      $('#objects').append(
                          '<div id="' + value.name + '" '
                          + 'class="object stairs" data-area="' + value.properties.area + '" '
                          + 'data-direction="' + value.properties.direction + '" '
                          + 'style="left: ' + value.x + 'px; top: ' + (value.y - Game.gridCellSize) + 'px">'
                          + '</div>'
                      );
  
                      if (!Stage.portalsMap[(value.y / Game.gridCellSize) - 1]) {
                          Stage.portalsMap[(value.y / Game.gridCellSize) - 1] = {};
                      }
  
                      Stage.portalsMap[(value.y / Game.gridCellSize) - 1][(value.x / Game.gridCellSize)] = $('#' + value.name);
  
                      break;
              }
          });
      },
  
      /**
       *
       */
      drawTiles : function (tiles) {
          var
              counter = 0,
              height  = tiles.height,
              row     = 0,
              width   = tiles.width;
  
          var yellow_brick_counter = 0;
  
          $.each(tiles.data, function (index, value) {
              var
                  y   = Math.ceil(value / Stage.tileMap.width),
                  x   = (value - ((y - 1) * Stage.tileMap.width));
  
              if (value !== 0) { // 0 is empty, therefore don't draw it
                  var yellow_brick_road = "";
                  var yellow_brick_counter_display = "";
                  if (tiles.name == "tiles2" && value == 4) {
                      yellow_brick_road = " display:none;";
                      yellow_brick_counter += 1;
                      yellow_brick_counter_display = " yb"+yellow_brick_counter;
                  }
                  $('#tiles').append(
                      '<div class="tile t' + value + ' ' + tiles.name + yellow_brick_counter_display + '" '
                      + 'style="background-position: -' + ((x * Game.gridCellSize) - Game.gridCellSize) + 'px -' + ((y * Game.gridCellSize) - Game.gridCellSize) + 'px; '+ yellow_brick_road
                      + 'left: ' + (counter * Game.gridCellSize) + 'px; top: ' + (row * Game.gridCellSize) + 'px">'
                      + '</div>'
                  );
              }
  
              counter += (index + 1) % width === 0 ? -counter : 1;
              row     += (index + 1) % width === 0 ? 1 : 0;
          });
      },
  
      /**
       *
       */
      init : function (stage) {
          var transition = $('#transition');
  
          transition.animate({
              opacity: 1
          }, 180, function () {
              Stage.cleanup();
  
              Game.loading = true;
  
              $.ajax({
                  // cache       : false, // For development purposes
                  dataType    : 'json',
                  type        : 'GET',
                  url         : '/static/pub/json/'+ stage +'.json',
              }).done(function (data, textStatus, jqXHR) {
                  Game.prevArea     = Game.currentArea;
                  Game.currentArea  = stage;
                  Stage.height      = data.height * Game.gridCellSize;
                  Stage.width       = data.width * Game.gridCellSize;
  
                  $.each(data.layers, function (index, value) {
                      var layer = value;
  
                      switch (true) {
  
                          // Collisions
                          case (layer.name == 'collisions') :
                              Stage.drawCollisions(layer);
  
                              break;
  
                          // Objects
                          case (layer.type == 'objectgroup') :
                              Stage.drawObjects(layer);
  
                              break;
  
                          // Tile Layer
                          default :
                              Stage.drawTiles(layer);
  
                              break;
                      }
                  });
  
                  for (i = 0; i < $('#player, .npc, .object, .tiles3').length; i++) {
                      Game.calculateZindex($('#player, .npc, .object, .tiles3').eq(i));
                  }
  
                  Stage.center();
  
                  setTimeout(function(){
                      trigger_beginning()
                  }, 2500);
  
                  Game.loading = false;
  
                  if (data.properties.music && Sounds.currentMusic.src != Sounds.music[data.properties.music]) {
                      Sounds.currentMusicId = data.properties.music;
  
                      Sounds.changeMusic(Sounds.music[data.properties.music]);
                  }
  
                  Sounds.fade(Sounds.currentMusic, data.properties.musicVol);
  
                  transition.animate({
                      opacity: 0
                  }, 180);
              }).fail(function (jqXHR, textStatus, errorThrown) {
                  // Do nothing
              }).always(function (data, textStatus, jqXHR) {
                  // Do nothing
              });
          });
      },
  
      scrollStage : function (direction) {
          var
              player      = $('#player'),
              playerOff   = player.offset(),
              offset      = 0,
              scrollArea  = $('#scroll-area'),
              stage       = $('#stage'),
              stagePos    = stage.position(),
              stageL      = stagePos.left,
              stageT      = stagePos.top,
              windowH     = $(window).height(),
              windowW     = $(window).width();
  
          if ((Stage.width > windowW || Stage.height > windowH)) {
              switch (direction) {
  
                  // Up
                  case Game.directions.up:
                      if ((playerOff.top + (Game.gridCellSize / 2)) < (windowH / 2) && stageT < 0) {
                          stage.stop().animate({
                              top: stageT + Game.gridCellSize + offset
                          }, 180, 'linear');
                      }
  
                      break;
  
                  // Down
                  case Game.directions.down:
                      if ((playerOff.top + (Game.gridCellSize / 2)) > (windowH / 2)
                          && Math.abs(stageT - windowH) < Stage.height) {
                          stage.stop().animate({
                              top: stageT - Game.gridCellSize + offset
                          }, 180, 'linear');
                      }
  
                      break;
  
                  // Left
                  case Game.directions.left:
                      if ((playerOff.left + (Game.gridCellSize / 2)) < (windowW / 2) && stageL < 0) {
                          stage.stop().animate({
                              left: stageL + Game.gridCellSize + offset
                          }, 180, 'linear');
                      }
  
                      break;
  
                  // Right
                  case Game.directions.right:
                      if ((playerOff.left + (Game.gridCellSize / 2)) > (windowW / 2)
                          && Math.abs(stageL - windowW) < Stage.width) {
                          stage.stop().animate({
                              left: stageL - Game.gridCellSize + offset
                          }, 180, 'linear');
                      }
  
                      break;
              }
          }
      }
  }
  