javascriptandroidgame-engineandroid-scripting

Organize code in AndroidScript?


I'm working on a fully-featured, basic classic RPG (JRPG) using the Android "Javascript" IDE--AndroidScript (which is great!). However, this program is getting a little large for one file (700 lines). What can I do to better organize or separate my code? Can you import other files in AndroidScript? I'm used to keeping separate classes in Java and am just now getting into Javascript.

Here's the code thus far:

/*
 * My first JavaScript RPG
 * @author I don't care...please steal this.
 */

var enemyArray = [];

function Enemy( type, level, str, hp, totalhp, x, y, speed )
{
  this.type = type; 
  this.level = level;
  this.str = str;
  this.hp = hp;
  this.totalhp = totalhp;
  this.exp = level * 3;
  this.gold = level;
  this.x = x;
  this.y = y;
  this.isAlive = true;
  this.speed = speed;
}

var STATE = {
  MAINMENU : {value: 0, name: "Main Menu", code: "M"}, 
  WORLDMAP : {value: 1, name: "World Map", code: "W"}, 
  BATTLE : {value: 2, name: "Battle", code: "B"},
  STATUS : {value: 3, name: "Status", code: "S"},
  INVENTORY : {value: 4, name: "Inventory", code: "I"},
  DEAD : {value: 5, name: "Dead", code: "D"},
};

var player = {
    yourTurn: true,
    name : "",
    x : 0.5,
    y : 0.9,
    level : 1,
    strength : 2,
    intel : 2,
    totalhp : 10,
    hp : 10,
    totalmp : 3,
    mp : 3,
    level : 1,
    exp : 0,
    gold : 0,
    inventory : [],
    speed : 0.002,
    playerSpeedMultiplier : 20 // it's low so that the run decision uses even sides
}

var game = {
    timer : null,
    isPaused : false,
    quit : false,
    state : STATE.MAINMENU,
    level : 0,
    enemies : enemyArray,
    currentEnemy : null
}

var ui =
{
    battleText : "",
    init : function(){
         //Lock screen orientation to Portrait.
        app.SetOrientation( "Portrait" );

        //Stop screen turning off.
        app.PreventScreenLock( true );

        //Create a layout with objects vertically centered.
        lay = app.CreateLayout( "linear", "Vertical,FillXY" );    
        lay.SetBackColor( "#000000" );

        //Set canvas size
        var canvasHeight = 0.75;
        var canvasWidth = 1.0;

        //Create an blank image to act as our drawing 'canvas'.
        //(For performance reasons, we limit the internal bitmap to 
        //480x800 and set screen updating to manual mode).    
        canvas = app.CreateImage( null, canvasWidth, canvasHeight, "fix", 480, 800 );
        canvas.SetAutoUpdate( false );
        lay.AddChild( canvas );

        //Create menu button.
        btnStart = app.CreateButton( "Start", 0.4 );
        btnStart.SetMargins( 0, 0.02, 0, 0 );
        btnStart.SetOnTouch( btn_start );
        lay.AddChild( btnStart );

        moveButtons = app.CreateLayout( "Linear", "Horizontal" );
        lay.AddChild( moveButtons );

        btnLeft = app.CreateButton( "Left", 0.2, 0.16 );
        btnLeft.SetOnTouch( btn_left );
        moveButtons.AddChild( btnLeft );

        layVert = app.CreateLayout( "Linear", "Vertical" );

        btnUp = app.CreateButton( "Up", 0.3, 0.08 );
        btnUp.SetOnTouch( btn_up );
        layVert.AddChild( btnUp );
        btnDown = app.CreateButton( "Down", 0.3, 0.08 );
        btnDown.SetOnTouch( btn_down );
        layVert.AddChild( btnDown );

        moveButtons.AddChild( layVert );

        btnRight = app.CreateButton( "Right", 0.2, 0.16 );
        btnRight.SetOnTouch( btn_right );
        moveButtons.AddChild( btnRight );

        btnLeft.SetVisibility( "hide" );
        btnUp.SetVisibility( "hide" );
        btnDown.SetVisibility( "hide" );
        btnRight.SetVisibility( "hide" ); 

        battleButtons = app.CreateLayout( "Linear", "Horizontal" );
        lay.AddChild( battleButtons );

        btnAttack = app.CreateButton( "Attack", 0.2, 0.16 );
        btnAttack.SetOnTouch( btn_attack );
        battleButtons.AddChild( btnAttack );

        layMid = app.CreateLayout( "Linear", "Vertical" );

        btnItem = app.CreateButton( "Item", 0.3, 0.08 );
        btnItem.SetOnTouch( btn_item );
        layMid.AddChild( btnItem );
        btnRun = app.CreateButton( "Run", 0.3, 0.08 );
        btnRun.SetOnTouch( btn_run );
        layMid.AddChild( btnRun );

        battleButtons.AddChild( layMid );

        btnMagic = app.CreateButton( "Magic", 0.2, 0.16 );
        btnMagic.SetOnTouch( btn_magic );
        battleButtons.AddChild( btnMagic );

           //Status button
          btnStatus = app.CreateButton( "Status", 0.9, 0.025, "blue" ); 
          btnStatus.SetOnTouch( btn_status ); 
          lay.AddChild( btnStatus );

        btnAttack.SetVisibility( "gone" );
        btnItem.SetVisibility( "gone" );
        btnRun.SetVisibility( "gone" );
        btnMagic.SetVisibility( "gone" );


         //Status slider
         //Create a layout we can slide over the main layout. 
         laySlide = app.CreateLayout( "Linear", "FillXY" ); 
         laySlide.SetPadding( 0, 0.1, 0, 0 );  
         laySlide.SetBackground( "/Sys/Img/GreenBack.png" ); 
         laySlide.SetVisibility( "Hide" ); 

         //Create tabs.
         var tabs = app.CreateTabs( "STATUS,EQUIP,INVENTORY", 0.8, 0.8, "VCenter" );
         tabs.SetOnChange( tabs_OnChange ); 
         laySlide.AddChild( tabs ); 

         //Create button and add to main layout. 
         btnBack = app.CreateButton( "Back", 0.3, 0.06, "gray" ); 
         btnBack.SetOnTouch( btnBack_OnTouch ); 
         laySlide.AddChild( btnBack ); 

         btnStatus.SetVisibility( "gone" );

        //Add layouts to app.    
        app.AddLayout( lay );
        app.AddLayout( laySlide );

        //Switch off debugging for max speed.
        app.SetDebugEnabled( false );
    },
    write : function(){},
    update: function(){}
}


//STATUS SLIDER
//Called when user touches our 'slide' button. 
function btn_status() 
{ 
    laySlide.Animate( "SlideFromRight" ); 
    game.isPaused = true;
} 

//Called when user touches our 'back' button. 
function btnBack_OnTouch() 
{ 
    laySlide.Animate( "SlideToLeft" );
    game.isPaused = false;     
} 

//Handle tab selection.
function tabs_OnChange( name )
{
    app.ShowPopup( name );
}



//Called when application is started.
function OnStart()
{    
    ui.init();

    //Call DrawFrame function 30x a second.
    game.timer = setInterval( drawFrame, 1000/30 ); // 30 default    
}

//Update and redraw all game graphics.
function drawFrame()
{
    //Clear the canvas.
    canvas.Clear();

    if(game.state == STATE.MAINMENU) {
        drawMenu();
    }
    if(game.state == STATE.WORLDMAP) {
        drawWorld();
        drawChar();
        drawEnemies();
        areAllEnemiesDead();
    }
    if(game.state == STATE.BATTLE) {
        drawBattleScreen();
    }
    if(game.state == STATE.STATUS) {
        drawStatusScreen();
    }
    if(game.state == STATE.INVENTORY) {
        drawInventory();
    }
    if(game.state == STATE.DEAD) {
        drawDead();
    }

    //Update the canvas.
    canvas.Update();

    //Quit game if required.
    if( game.quit ) return;
}

function goToNextLevel(){
    game.level += 1;

    player.hp = player.totalhp;
    player.mp = player.totalmp;

    app.ShowPopup( "Level " + game.level );
    btnStart.SetVisibility("gone");
    btnLeft.SetVisibility( "show" );
    btnUp.SetVisibility( "show" );
    btnDown.SetVisibility( "show" );
    btnRight.SetVisibility( "show" );
    btnAttack.SetVisibility( "gone" );
    btnItem.SetVisibility( "gone" );
    btnRun.SetVisibility( "gone" );
    btnMagic.SetVisibility( "gone" );
    canvas.SetBackColor( "#008200"  );

   for (var i = 0; i < game.enemies.length; i++) {
       game.enemies.pop();
    }
    player.x = 0.5;
    player.y = 0.9;
    createEnemies();
    game.state=STATE.WORLDMAP;
}

//Draw the Menu
function drawMenu()
{
    //Draw header
    canvas.SetTextSize( 14 );
    canvas.SetPaintColor( "#ff0088ff"  );
    canvas.DrawText( "Super Best RPG,", 0.1, 0.1 );

    //Draw info
    canvas.SetTextSize( 16 );
    canvas.SetPaintColor( "#ffff0000"  );
    canvas.DrawText( "Battle great moon", 0.1, 0.3 );
}

//Called when user presses 'Start' button.
function btn_start()
{
    goToNextLevel();
    btnStatus.SetVisibility( "show" );
}

function createEnemies(){
 app.ShowPopup( "Level " + game.level );
    for(var i = 0; i < game.level; i++) {
        game.enemies.push(
            new Enemy("Goblin", 
                                  1,
                                  Math.pow(1.2, game.level),
                                  5,
                                  game.level * 5,
                                  (Math.random() * 0.58) + 0.22,
                                  (Math.random() * 0.89),
                                  0.002
                                  )
        );
    }

}

//Called when user presses 'up'
function btn_up()
{
    if(player.y > 0.06) {
        player.y -= player.speed * player.playerSpeedMultiplier;
        app.Vibrate( "0,30" );
    }
}

//Called when user presses 'down'
function btn_down()
{
    if(player.y < 0.90) {
        player.y += player.speed * player.playerSpeedMultiplier;
        app.Vibrate( "0,30" );
    }
}

//Called when user presses 'left'
function btn_left()
{
    if(player.x > 0.23) {
        player.x -= player.speed * player.playerSpeedMultiplier;
        app.Vibrate( "0,30" );
    }
}

//Called when user presses 'right'
function btn_right()
{
    if(player.x < 0.77) {
        player.x += player.speed * player.playerSpeedMultiplier;
        app.Vibrate( "0,30" );
    }
}

//Called when user presses 'Attack'
function btn_attack()
{
    ui.battleText = "";
    if(player.speed >= game.currentEnemy.speed) {
        youAttack();
        player.yourTurn = false;
        enemyAttack();
        player.yourTurn = true;
    }
    else {
        player.yourTurn = false;
        enemyAttack();
        player.yourTurn = true;
        youAttack();
    }
}

//Called when user presses 'Item'
function btn_item()
{
    ui.battleText = "";
    if(player.yourTurn) {

    }
}

//Called when user presses 'Run'
function btn_run()
{
    ui.battleText = "";
    if(player.yourTurn) {
        if(player.speed >= game.currentEnemy.speed) {
            game.currentEnemy.isAlive = false;
            battleExit();
            game.state = STATE.WORLDMAP;
        }
        else {
            player.yourTurn = false;
            enemyAttack();
            player.yourTurn = true;
        }
    }
}

//Called when user presses 'Magic'
function btn_magic()
{
    ui.battleText = "";
    if(player.yourTurn) {

    }
}

function drawWorld() 
{
    canvas.SetPaintStyle( "Fill" ); 
    canvas.SetPaintColor( "#555500" );
    canvas.DrawRectangle(0.2, 0.0, 0.8, 1.0);   

    //HUD
    //level
    canvas.SetTextSize( 24 );
    canvas.SetPaintColor( "#ff22aaaa"  );
    canvas.DrawText( "" + game.level, 0.05, 0.1 );

    //health/magic text
    canvas.SetTextSize( 6 );
    canvas.SetPaintColor( "#ff000000"  );
    canvas.DrawText( "HP: " + player.hp + "\\" + player.totalhp, 0.01, 0.95 );
    canvas.DrawText( "MP: " + player.mp + "\\" + player.totalmp, 0.82, 0.95 );

    //health/magic bars
    canvas.SetPaintColor( "#ffff0000"  );    
    var hpsize = (player.hp/player.totalhp) * 0.6;
    canvas.DrawRectangle( 0.066, 0.9 - hpsize, 0.133 , 0.9);

    canvas.SetPaintColor( "#ff0000ff"  );
    var mpsize = (player.mp/player.totalmp) * 0.6;
    canvas.DrawRectangle( 0.866, 0.9 - mpsize, 0.933 , 0.9);
}



function drawChar() 
{   
    //Stick man
    //Head 
    canvas.SetPaintStyle( "Line" ); 
    canvas.SetPaintColor( "#ff000000" ); 
    canvas.DrawCircle( player.x, player.y, 0.02 );

    //Body
    canvas.SetLineWidth( 4.0 ); 
    canvas.DrawLine( player.x, player.y + 0.01, player.x, player.y + 0.05 ); 

    //Legs
    canvas.SetLineWidth( 2.5 ); 
    canvas.DrawLine( player.x, player.y + 0.05, player.x - 0.03, player.y + 0.08 ); 
    canvas.DrawLine( player.x, player.y + 0.05, player.x + 0.03, player.y + 0.08 ); 

    //Arms
    canvas.DrawLine( player.x - 0.04, player.y + 0.025, player.x + 0.04, player.y + 0.025 );
}



function drawEnemies(){
    canvas.SetPaintStyle( "Line" ); 
    canvas.SetPaintColor( "#000000" ); 
    for (var i = 0; i < game.enemies.length; i++) {
       if(game.enemies[i].isAlive){
           moveEnemyTowardsPlayer(game.enemies[i]);
           checkEnemy(game.enemies[i]);
           canvas.DrawCircle(game.enemies[i].x ,game.enemies[i].y ,0.01);     
       }
    }
}


function moveEnemyTowardsPlayer(enemy)
{
       if (game.isPaused) return;
       if (enemy.x < player.x) {
           enemy.x += enemy.speed;
       }
       if (enemy.x > player.x) {
           enemy.x -= enemy.speed;
       }
       if (enemy.y < player.y) {
           enemy.y += enemy.speed;
       }
       if (enemy.y > player.y) {
           enemy.y -= enemy.speed;
       }
}

function checkEnemy(enemy) 
{
       if (enemy.hp <= 0) return;
       if (enemy.x > player.x - 0.03 && enemy.x < player.x + 0.03 &&
           enemy.y > player.y - 0.02 && enemy.y < player.y + 0.08) {
               enemy.speed = 0;
               game.currentEnemy = enemy;     
               app.ShowPopup( game.currentEnemy.type + " Attacks!");
               if(Math.random() > 0.5) yourTurn = false;
               battleSetup();
               game.state = STATE.BATTLE;
       }
}







//BATTLE

//Hide moveButtons
//Show battleButtons
function battleSetup() {
                btnLeft.SetVisibility( "gone" );
                btnUp.SetVisibility( "gone" );
                btnDown.SetVisibility( "gone" );
                btnRight.SetVisibility( "gone" );
                btnAttack.SetVisibility( "show" );
                btnItem.SetVisibility( "show" );
                btnRun.SetVisibility( "show" );
                btnMagic.SetVisibility( "show" );
                canvas.SetLineWidth(10);
                canvas.SetPaintColor( "#ff777777"  );
                canvas.DrawLine(0, 1, 1, 1);
}

//Show moveButtons
//Hide battleButtons
function battleExit() {
               btnLeft.SetVisibility( "show" );
               btnUp.SetVisibility( "show" );
               btnDown.SetVisibility( "show" );
               btnRight.SetVisibility( "show" );
               btnAttack.SetVisibility( "gone" );
               btnItem.SetVisibility( "gone" );
               btnRun.SetVisibility( "gone" );
               btnMagic.SetVisibility( "gone" );
}

//Draw the Battle Screen
function drawBattleScreen()
{
    //Draw Enemy Stats
    canvas.SetLineWidth(1);
    canvas.SetTextSize( 9 );
    canvas.SetPaintColor( "#ff000000"  );
    canvas.DrawText( "" + game.currentEnemy.type + 
        "  HP: " + game.currentEnemy.hp + "\\" + game.currentEnemy.totalhp, 0.04, 0.07 );
    //Lines under enemy stats
    canvas.SetLineWidth(2.5);
    canvas.SetPaintColor( "#ff000000"  );
    canvas.DrawLine(0, 0.09, 0.25, 0.09);
    canvas.SetLineWidth(2.0);
    canvas.DrawLine(0, 0.10, 0.20, 0.10);
    canvas.SetLineWidth(1.5);
    canvas.DrawLine(0, 0.11, 0.15, 0.11);
    canvas.SetLineWidth(1.0);
    canvas.DrawLine(0, 0.12, 0.10, 0.12);
    canvas.SetLineWidth(0.5);
    canvas.DrawLine(0, 0.13, 0.05, 0.13);

    //Draw Player Stats
    canvas.SetLineWidth(1);
    canvas.SetTextSize( 9 );
    canvas.SetPaintColor( "#ff000000"  );
    canvas.DrawText( "HP: " + player.hp + "\\" + player.totalhp, 0.7, 0.87 );
    canvas.DrawText( "MP: " + player.mp + "\\" + player.totalmp, 0.7, 0.93 );

    //Draw Enemy
    //Head
    canvas.SetLineWidth(5);
    canvas.SetPaintColor( "#ff884400"  );
    canvas.DrawCircle( 0.7, 0.25, 0.2 );
    //Eyes
    canvas.SetPaintColor( "#ff222200"  );
    canvas.SetLineWidth(7);
    canvas.DrawLine( 0.55, 0.2, 0.64, 0.24);
    canvas.DrawLine( 0.79, 0.2, 0.70, 0.24);
    //Mouth
    canvas.SetPaintColor( "#ff000000"  );
    canvas.SetLineWidth(7);
    canvas.DrawLine( 0.59, 0.31, 0.75, 0.31);
    canvas.DrawLine( 0.61, 0.32, 0.73, 0.32);

    //Draw Player
    //Head
    canvas.SetLineWidth(10);
    canvas.SetPaintColor( "#ff000000"  );
    canvas.DrawCircle( 0, 0.5, 0.25 );
    //Body
    canvas.SetLineWidth(20);
    canvas.DrawLine(0, 0.65, 0, 1);
    //Arm
    canvas.DrawLine(0, 0.8, 0.4, 0.8);
    //Sword
    canvas.SetLineWidth(12);
    canvas.DrawLine(0.35, 0.88, 0.5, 0.5);
    canvas.SetLineWidth(7);
    canvas.DrawLine(0.3, 0.73, 0.5, 0.78);

    //BattleText
    canvas.SetLineWidth(1);
    canvas.DrawText(ui.battleText, 0.6, 0.7);

    showOrHideButtons();
}

function showOrHideButtons() {
    if(player.yourTurn) {
        btnAttack.SetVisibility("Show");
        btnItem.SetVisibility("Show");
        btnRun.SetVisibility("Show");
        btnMagic.SetVisibility("Show");
    }
    else {
        btnAttack.SetVisibility("Hide");
        btnItem.SetVisibility("Hide");
        btnRun.SetVisibility("Hide");
        btnMagic.SetVisibility("Hide");
    }
}



function youAttack(){
  if(player.hp <= 0) return;
  var tempRandom = (Math.random() > 0.3);
  app.ShowPopup( "str:" + player.strength + tempRandom);
  if(tempRandom) {  
      game.currentEnemy.hp -= player.strength;
      ui.battleText += "Hit for " + player.strength;      
   } else {
      ui.battleText += "You missed";      
   }
   checkEnemyDeath();
}

function enemyAttack(){ 
   if(game.currentEnemy.hp <= 0) return;
   if(Math.random() > 0.5) {  
      player.hp -= Math.floor(game.currentEnemy.str);
   } 
   checkPlayerDeath();

}

function checkEnemyDeath() {
    if(game.currentEnemy.hp <= 0) {
        game.currentEnemy.isAlive == false;
        player.exp += game.currentEnemy.exp;
        ui.battleText = "You defeated the " + game.currentEnemy.type + "! " +
                      "You gained " + game.currentEnemy.exp + " exp.";
        player.y += 0.01;
        battleExit();
        game.currentEnemy.x = 1000;
        game.state = STATE.WORLDMAP;
        areAllEnemiesDead();
    }
}

function areAllEnemiesDead(){
   var allDead = true;
   for (var i = 0; i < game.enemies.length; i++) {
       if(game.enemies[i].hp > 0){
           allDead = false;     
       }
    }
    if (allDead) {
       goToNextLevel();
    }
}

function checkPlayerDeath() {
    if(player.hp <= 0) {
        player.isAlive == false;
        player.hp = 0;
        app.ShowPopup( "Died on level " + game.level );
        btnStart.SetVisibility("gone");
        btnLeft.SetVisibility( "gone" );
        btnUp.SetVisibility( "gone" );
        btnDown.SetVisibility( "gone" );
        btnRight.SetVisibility( "gone" );
        btnAttack.SetVisibility( "gone" );
        btnItem.SetVisibility( "gone" );
        btnRun.SetVisibility( "gone" );
        btnMagic.SetVisibility( "gone" );
        btnStatus.SetVisibility( "gone" );
        canvas.SetBackColor( "#000000"  );
        drawFrame();
    }
}

Thanks!


Solution

  • You can try using the (currently undocumented) app.LoadScript() method. You can use it at the top of your script like this:-

    app.LoadScript( "External.js" );
    
    function OnStart()
    {
      :
      :
    }
    

    I suggest you keep the core functionality of your App in the main file and move trustworthy utils and helper functions out to external files. This is because the WiFi IDE does not currently load external scripts when you hit errors (although the log should show which file the error was in).

    Note: The Loadscript method also has a second optional 'callback' parameter was added so you can find out when your script has completed loading. You could call LoadScript in any part of your program and might need to know when the functions it contains are available since it loads asynchronously. But if you use LoadScript at the top of your script then you should not need to worry about using the callback.