Code viewer for World: Nightmare Simulator
//Michael Regan 22112111
//ca318 Practicle - impressive usecase: Nightmare Simulator


let sprites = [];                                                                           //storing sprites
let urls = [];                                                                              //storing image urls
let responces = [];                                                                         //storing ai generated text responces


// movement booleans
let movingUp = false;
let movingDown = false;
let movingLeft = false;
let movingRight = false;


let apikey = "";                                                    

 AB.world.newRun = function()
{
	AB.runReady = false;                                                                      //Not running setup() as to take users input
	ABWorld.init (  'white'  );                                                               //Backgound colour
    AB.headerRHS();                                                                           //Moving header the RHS
    AB.newSplash();                                                                           //Creating HTML
	AB.splashHtml ( " <h1> Welcome to Nightmare Simulator (Michael Regan ca318) </h1> " +
                        " <p>API keys can be sourced form: https://platform.openai.com/api-keys </p> " +
	   	               " <div>Enter API Key: <input    style='width:25vw;'    maxlength='2000'  id=apikey NAME=apikey   VALUE='' > </div>" +
	   	               " <div>Describe yourself:<input    style='width:25vw;'    maxlength='2000'  id=player NAME=player   VALUE='' ></div>  " +
	   	               " <div>Who/What are you most affraid of: <input    style='width:25vw;'    maxlength='2000'  id=enemy NAME=enemy   VALUE='' > </div>" +
	   	               " <button onclick='intialSetup();'  class=ab-normbutton >Start game</button> " +
	   	               " <div id=errordiv name=errordiv> </div> " );
};
   
function intialSetup() {
    apikey =  jQuery("input#apikey").val();
    var  player =  jQuery("input#player").val();
    var  enemy =  jQuery("input#enemy").val();
    AB.removeSplash();                                                                          //Removing HTML
    console.log(player);  
    console.log(enemy);
    
    //Asynchronously generatng sprite images and text
    getImages(player, () => {
        getImages(enemy, () => {
            getText(enemy, player);
            createSprites(urls);
        });
    
    })
    AB.runReady = true;
} 

//Generate image using DALL-E
const getImages = async (promptValue, callback) => {
const options = {
    method: "POST",                                                                              //Post request
    headers: {
        "Authorization": `Bearer ${apikey}`,                                                     //API key
        'Content-Type': "application/json"                                                       //Conetent type JSON
        },
    body: JSON.stringify({                                                                       //Turn body into JSON
        "prompt" : "pixel art of a personified " + promptValue + "(whole body)",                 //Modified prompt for pixel art generation
        "n": 1,                                                                                  //1 image
        "size": "256x256"                                                                        //Image size 256x256
    })
    }
    try {
        const responce = await fetch('https://api.openai.com/v1/images/generations', options)     //Asynchronously fetching from OpenAI
        const data = await responce.json()                                                        //Turning responce into json
        console.log(data.data[0].url);
        urls.push(data.data[0].url);                                                               //Storing url in list
        if(callback) callback();                                                                   //Asynchronous functionailty
    } catch (error){                                                                               //Error handling
        console.error(error)                                                                       //Print error
    }
}

const getText = async (promptValue1, promptValue2, callback) => {
    const options = {
        method: "POST",                                                                             //Post request
        headers: {
            "Authorization":  `Bearer ${apikey}`,                                                    //API key
            'Content-Type': "application/json"                                                       //Conetent type JSON
        },
        body: JSON.stringify({
            "model": "gpt-3.5-turbo",                                                                //Model
            "messages" : [{role: "user", content: `think of soemthing a ${promptValue1} would say to ${promptValue2} while chasing them (funny)`}],   //Prompt 
            "max_tokens": 100                                                                       //max tokens
        })
    }
    try {
        const responce = await fetch('https://api.openai.com/v1/chat/completions', options)         //Asynchronously fetching to chatgpt
        const data = await responce.json()
        responces.push(data.choices[0].message.content);                                            //Push message to list
        if(callback) callback();                                                                    //Asynchronous functionailty
    } catch (error){                                                                                //Error handling
        console.error(error)                                                                        //Print error
    }
}


//create user's sprite
function createCustomSprite(x, y, img) {
  return {
    x: x,
    y: y,
    img: img,
    speed: 5,
    display: function() {
      image(this.img, this.x, this.y);
    },
   
    //moving up,down,left,right   
    move: function() {
      if (movingUp) {
    this.y -= this.speed;
  }
  if (movingDown) {
    this.y += this.speed;
  }
  if (movingLeft) {
    this.x -= this.speed;
  }
  if (movingRight) {
    this.x += this.speed;
      }
    }
  };
}

//creating enemy sprite
function createEnemySprite(x, y, img, sprite) {
  return {
    x: x,
    y: y,
    img: img,
    speed: 1, 
    display: function() {
      image(this.img, this.x, this.y);
    },
    move: function() {
      // Move horizontally towards player
      if (sprite.x > this.x) {
        this.x += this.speed;
      } else if (sprite.x < this.x) {
        this.x -= this.speed;
      }

      // Move vertically towards player
      if (sprite.y > this.y) {
        this.y += this.speed;
      } else if (sprite.y < this.y) {
        this.y -= this.speed;
      }
    }
  };
}

//Create sprites from list of image URLs
function createSprites(imageUrls) {
    const proxyUrl = 'https://corsproxy.io/?';                                                         //CORS error handling
    proxiedUrl = proxyUrl + encodeURIComponent(imageUrls[0]);                                          //New CORs safe URL
    
    // Load Image into sprite
    loadImage(proxiedUrl , img => {
        let sprite = createCustomSprite(random(width), random(height), img);                           //First URL will be the player
        sprites.push(sprite);                                                                          //Push sprite to list
        
        if (imageUrls.length > 1) {                                                        
            proxiedUrl = proxyUrl + encodeURIComponent(imageUrls[1]);                                  //New CORS safe URL
            
            //load image into sprite
            loadImage(proxiedUrl, img => {
                let enemySprite = createEnemySprite(random(width), random(height), img, sprites[0]);  //Second URL will be enemy
                sprites.push(enemySprite);                                                            //Push sprite to list
            }, err => {                                                                               //Error handling
                console.error(err);                                                                   //Print error 
            });
        }
    }, err => {                                                                                       //Error handling
        console.error(err);                                                                           //Print error
    });
}

//Keyboard input for moving player
function keyPressed() {
  //Move up
  if (key === 'W' || key === 'w') {
    movingUp = true;
  }
  //Move down
  if (key === 'S' || key === 's') {
    movingDown = true;
  }
  //Move left
  if (key === 'A' || key === 'a') {
    movingLeft = true;
  }
  
  //Move Right
  if (key === 'D' || key === 'd') {
    movingRight = true;
  }
}

// Stop player moving when key is released
function keyReleased() {
  //Stop moving up
  if (key === 'W' || key === 'w') {
    movingUp = false;
  }
  //Stop moving down
  if (key === 'S' || key === 's') {
    movingDown = false;
  }
  //Stop moving left
  if (key === 'A' || key === 'a') {
    movingLeft = false;
  }
  //Stop moving right
  if (key === 'D' || key === 'd') {
    movingRight = false;
  }
}
    

// Dynamically get text size
function calculateMaxFontSize(textContent, boxWidth, boxHeight) {
  let fontSize = 1;                                                                              //Starting text size
  textSize(fontSize);                                                                           
  while (textWidth(textContent) < boxWidth && textAscent() + textDescent() < boxHeight) {       //While text widthis less then box with and texthight is less the box heigh
    fontSize++;                                                                                 //Increment text size
    textSize(fontSize);                                                                         //Adjust text size
  }
  return fontSize - 1;                                                                          //Return -1 because while loop will go one to far
};


//---- draw ---------------------------

function draw() {
    background(" white");                                                                      //Images need to be removed when they move and replaced at new coordinates
    
    //Text box details
    let textBoxX = 10;
    let textBoxY = height - 100;
    let textBoxWidth = width - 20;
    let textBoxHeight = 100;
   
    //Draw TextBox
    rect(textBoxX, textBoxY, textBoxWidth, textBoxHeight);                                    //Draw TextBox
    let textContent = responces[0];                                                           //Set text box's content to chatGPT's responce
    let fontSize = calculateMaxFontSize(textContent, textBoxWidth, textBoxHeight);            //Dynamicly fill the box with text
    textSize(fontSize);                                                                       //Set text size
    text(textContent, textBoxX + 10, textBoxY + textBoxHeight / 2 + 5);                       //Show Text
   
    // For the player an enemy
    for (let sprite of sprites) {
        sprite.move();                                                                        //move sprite to new coordinates
        sprite.display();                                                                     //Display sprite
    }
}