//player.js

/** The player character, with stats and movement.
  * @param name: Player name. Useful if the game ever gets multiplayer capability.
**/
function Player(name)
{
  CreatePerson(name, name + ".rss", false);
  CreatePerson("bubble", "bubbleprotect.rss", false);

  this.init = function()
  {
    this.health = 2;
    this.maxhealth = 2;
    this.score = 0;
    this.deaths = 0; //Death count
    this.powerups = [];
    this.powerups.longer_float = 0;  //For each longer float powerup, you can float 10 frames longer.
    this.powerups.bubble = false;  //Defense bubble
    this.powerups.high_float = false;
    this.powerups.highfloattime = 0;
    this.powerups.maxhighfloattime = 90;  //About 3 high floats.
    this.powerups.got = [];  //Levels you got powerups from.
    
    //Firing projectiles.
    this.maxprojectiles = 3;  //Determines how many weapons you have (and thus can shoot out at once)
    this.projectiledistance = 100; //Max distance they can be thrown.
    this.projectiles = []; //Projectiles fired by this person.
    this.projectilewait = 0;
    this.projectilewaitmax = 25; //1 second
    this.canthrow = true;
    
    this.endtimes = [];

    //Navigational properties
    this.name = name;
    this.x = GetPersonX(name); //In tiles
    this.y = GetPersonY(name);
    this.xspeed = 0;
    this.yspeed = 0;
    this.xdir = 1;
    this.gravity = undefined;  //Gravity = jump speed.

    this.floating = false;
    this.floated_this_jump = false;
    this.floattime = 0;   //Amount of time already floated.
    this.jumped_prev_frame = false;  //Did the player hold the jump button in the previous frame?
    this.floorwait = 0;  //Wait for a set amount of time when on the floor.

    //Configurables
    this.fwmax = 12;     //Wait this amount of frames.
    this.max_floattime = 30;  //In frames
    this.max_friction = 8;  //For xspeed.
    this.maxfallspeed = 20;

    //Debugging
    //SetRenderScript("PLAYER.debug()");
  }
  this.init();


  var updatemovementthistime = true;
  var ignorelist;
  /** Update player. **/
  this.update = function()
  {
    SetPersonIgnoreList(this.name, GetPersonList());
    //updatemovementthistime = !updatemovementthistime;
    if (updatemovementthistime) this.updateMovement();

    ignorelist = ["bubble"];
    if (this.alreadyhurt) ignorelist = ignorelist.concat(ENEMIES_NAMES);

    SetPersonIgnoreList(this.name, ignorelist);
    this.checkOverlap();
    
    //Projectile handling
    if (!this.canthrow)
      this.projectilewait++;
    if (this.projectilewait > this.projectilewaitmax || !INPUT.pressed("attack"))
    {
      this.canthrow = true;
      this.projectilewait = 0;
    }
  }


  /** Done every time before a new map starts. **/
  this.resetMovement = function()
  {
    this.x = GetPersonX(name);
    this.y = GetPersonY(name);
    this.xspeed = 0;
    this.yspeed = 0;
    this.xdir = 1;
    this.gravity = 0.5;

    this.floating = false;
    this.floated_this_jump = false;
    this.floattime = 0;
    this.jumped_prev_frame = false;
    this.floorwait = 0;

    this.hurtcounter = 0;
    this.alreadyhurt = false;

    SetPersonVisible(this.name, true);
    this.health = this.maxhealth;

    SetPersonIgnoreList("bubble", GetPersonList());
    SetPersonX("bubble", -32);
    SetPersonY("bubble", -32);
  }


  /** Movement system for player. **/
  this.updateMovement = function()
  {
    //GRAVITY AND JUMPING
    //Also floating, and floating powerups.
    this.ydir = 1;
    if (this.yspeed > 0) this.ydir = 1;
    else if (this.yspeed < 0) this.ydir = -1;

    this.inair = (!IsPersonObstructed(this.name, this.x, this.y+1));
    if (this.inair)
    {
			//TODO: fix gravity to work better.
      if (this.jumped_prev_frame && INPUT.pressed("jump")) this.yspeed -= this.gravity*0.5;
      else if (!INPUT.pressed("jump")) this.jumped_prev_frame = false;
      if (this.ydir == 1) this.jumped_prev_frame = false;
      if (this.yspeed < this.maxfallspeed) this.yspeed += this.gravity;

      //Floating mechanism//
      if (INPUT.pressed("fly") && !this.floated_this_jump
          && this.yspeed > 1 && this.yspeed < 11)
      {
        this.floating = true;
        if (this.powerups.high_float) this.yspeed = this.gravity/2;
        else this.yspeed = 1;
        this.floattime++;
        if (this.powerups.high_float) this.powerups.highfloattime++;
      }

      //Float once, then finish.
      if (this.floating && !INPUT.pressed("fly"))
        this.floattime = this.max_floattime+(this.powerups.longer_float*10);

      if (this.powerups.high_float && this.powerups.highfloattime > this.powerups.maxhighfloattime)
      {
        this.powerups.highfloattime = 0;
        this.powerups.high_float = false;
      }

      //End floating.
      if (this.floattime >= this.max_floattime+(this.powerups.longer_float*10))
      {
        this.floating = false;
        this.floated_this_jump = true;
      }

      //Animation//
      if (this.floating)
        SetPersonFrame(this.name, 4+(this.floattime/this.max_floattime)*16%3);  //Frames 4 to 7 are fly
      else
        SetPersonFrame(this.name, 3);
    }

    else //On the ground
    {
      this.floorwait++;
      this.floated_this_jump = false;
      this.floattime = 0;

      if (this.floorwait >= this.fwmax)
      {
        this.floorwait = 0;
        this.jumped_prev_frame = INPUT.pressed("jump");
        if (this.yspeed <= this.maxfallspeed*0.5)
          this.yspeed = -this.gravity*8;
        else if (this.yspeed > this.maxfallspeed*0.5 && this.jumped_prev_frame)
          this.yspeed = -this.gravity*10;
        else
        {
          this.yspeed = -this.gravity*14;
          this.jumped_prev_frame = false;
        }
      }

      //Animation//
      if (this.floorwait < this.fwmax*0.5)
        SetPersonFrame(this.name, 1);
      else
        SetPersonFrame(this.name, 2);
    }

    //Collision detection//
    for (var i = 0; i != Math.floor(this.yspeed); i+=this.ydir)
    {
      if (IsPersonObstructed(this.name, this.x, this.y+this.ydir) && this.ydir == -1) this.yspeed = 1; //Touching the ceiling
      if (IsPersonObstructed(this.name, this.x, this.y+this.ydir)) break;
      this.y+=this.ydir;
      if (this.jumped_prev_frame) this.y += this.ydir;
    }


    //HORIZONTAL MOVEMENT
    //It's affected by the jumps, so calculated after the jumps.
    if (this.inair)
    {
      if (INPUT.pressed("left") && this.xspeed > -this.max_friction)
        this.xspeed--;
      else if (INPUT.pressed("right") && this.xspeed < this.max_friction)
        this.xspeed++;

      //Nothing pressed
      else if (!INPUT.pressed("left") && !INPUT.pressed("right") && !this.floating)
      {
        if (this.xspeed > 0) this.xspeed--;
        else if (this.xspeed < 0) this.xspeed++;
      }

      if (this.xspeed > 0) this.xdir = 1;
      else if (this.xspeed < 0) this.xdir = -1;


      //Collission detection//
      for (var i = 0; i != this.xspeed; i+=this.xdir)
      {
        if (IsPersonObstructed(this.name, this.x+this.xdir, this.y)
            && GetObstructingPerson("player", GetPersonX("player"), GetPersonY("player")) == "")
				{
					this.xspeed = 0;
					break;
				}
        this.x+=this.xdir/2;
      }

      //Animation//
      if (this.xdir == 1) SetPersonDirection(this.name, "east");
      else SetPersonDirection(this.name, "west");


      //REPOSITION THE CHARACTER FOR REALS
      SetPersonX(this.name, this.x);
      SetPersonY(this.name, this.y);

      //Bubble powerup.
      if (this.powerups.bubble)
      {
        SetPersonX("bubble", this.x);
        SetPersonY("bubble", this.y);
      }
    }
    
    //ATTACKING
    if (INPUT.pressed("attack"))
    {
      this.fireProjectile();
    }
  }



  /** Check if the player is touching anything and respond accordingly. **/
  this.checkOverlap = function()
  {
    //Gets to the finish?
    if (this.x > GetLayerWidth(0)*16 || this.x < 0)
    {
      LEVEL.win();
      return;
    }

    //First, check the tiles.
    if (this.x >= 0 && this.x < GetLayerWidth(0)*16 && this.y >= 0 && this.y < GetLayerHeight(0)*16)
    {
      var tilename = GetTileName(GetTile(Math.floor(this.x/16), Math.floor(this.y/16), GetPersonLayer("player")));
			switch (tilename)
			{
			  case "": break;
			  
				case "hazard":
					if (this.powerups.bubble)
					{
						if (GAME.sound) res.load("explode01.ogg").play(false);
						this.hurt();
						this.enableBubble(false);
						this.x -= this.xdir*5;
						break;
					}
					if (GAME.sound) res.load("explode02.ogg").play(false);
					this.health = 0;
					this.die();
					break;
				
				case "cupcake":
					SetTile(Math.floor(this.x/16), Math.floor(this.y/16), 0, 0)
					this.score++;
					LEVEL.gotcupcakes++;
					res.load("blip02.ogg").stop();
					if (GAME.sound) res.load("blip02.ogg").play(false);
					break;
			}
			
			//Check top layer for hidden tiles:
			if (GetNumLayers() > 2)
			{
				lyr = GetNumLayers()-1;
				tilename = GetTileName(GetTile(Math.floor(this.x/16), Math.floor(this.y/16), lyr));
				SetLayerVisible(lyr, true);
				if (tilename == "hide")
					SetLayerVisible(lyr, false);
			}
			
    }

    //Falls outside the level?
    else if (this.y > GetLayerHeight(0)*16+32)
    {
      if (GAME.sound) res.load("fall01.ogg").play(false);
      this.health = 0;
      this.die();
    }

    //Then, check the sprites.
    if (GetObstructingPerson(this.name, this.x, this.y) != "")
    {
      var otherguy = GetObstructingPerson(this.name, this.x, this.y);
      var otherguytype = GetPersonValue(otherguy, "type");

      if (otherguytype == "enemy" && !GetPersonValue(otherguy, "new"))
      {
        if (this.powerups.bubble)
        {
          this.enableBubble(false);
          this.updateMovement();
          this.xspeed = -this.xdir*10;
          this.yspeed = -6
          this.y -= 5;
        }
        else
          this.hurt();
      }

      else if (otherguytype == "lethal-enemy" && !GetPersonValue(otherguy, "new"))
      {
        if (this.powerups.bubble)
        {
          if (GAME.sound) res.load("explode01.ogg").play(false);
          this.hurt();
          this.enableBubble(false);
        }
        else
        {
          if (GAME.sound) res.load("explode02.ogg").play(false);
          this.die();
        }
      }

      else if (otherguytype == "platform")
      {
        this.yspeed = -6;
        if (INPUT.pressed("jump")) this.yspeed = -10;
        this.floattime = 0;
        this.floated_this_jump = false;
      }

      else if (otherguytype == "nojumpkey")  //Disallows high jumping.
      {
        this.yspeed = -6;
        this.floattime = 0;
        this.floated_this_jump = false;
      }

      else if (otherguytype == "powerup")
      {
        if (GAME.music) LEVEL.powerupmusic.play(false);
        
        if (otherguy.substring(0, 6) != "bubble")
          PLAYER.powerups.got.push(LEVEL.mapfile);

        if (otherguy.substring(0, 5) == "hplus")
        {
          this.maxhealth++;
          this.health = this.maxhealth;
        }

        else if (otherguy.substring(0, 7) == "bubble_")  //Avoid finding "bubbleprotect".
          this.enableBubble(true);

        else if (otherguy.substring(0, 7) == "feather")
          this.powerups.longer_float++;

        else if (otherguy.substring(0, 6) == "hfloat")
          this.powerups.high_float = true;
        
        DestroyPerson(otherguy);
      }

      else if (otherguy.substring(0, 7) == "cupcake")
      {
        this.score++;
        LEVEL.gotcupcakes++;
        DestroyPerson(otherguy);
        res.load("blip02.ogg").stop();
        if (GAME.sound) res.load("blip02.ogg").play(false);
      }

      else if (otherguy.substring(0, 5) == "heart")
      {
        res.load("jump02.ogg").stop();
        if (GAME.sound) res.load("jump02.ogg").play(false);
        
        if (this.health < this.maxhealth)
          this.health+=2;
        
        if (this.health > this.maxhealth)
          this.health = this.maxhealth;
        
        DestroyPerson(otherguy);
      }
      
      else if (otherguy.substring(0, 8) == "warp-pad")
      {
				var values = otherguy.split(":");  //Get the coordinates out of the person's name.
				if (values.length > 2) PLAYER.warp(values[1], values[2]);
      }
    }

    //Last, check the zones to set the help text to nothing if there is none.
    if (AreZonesAt(this.x, this.y, GetPersonLayer("player")))
      ExecuteZones(this.x, this.y, GetPersonLayer("player"));
    else if (LEVEL.helptext != "")
      LEVEL.helptext = "";
  }


  /** Hurt the player. **/
  this.hurt = function()
  {
    if (!this.alreadyhurt)
    {
      if (GAME.sound) res.load("explode04.ogg").play(false);
      this.health--;
      this.alreadyhurt = true;
    }

    if (this.health < 0) this.health = 0;
    if (this.health == 0)
    {
      if (GAME.sound) res.load("explode02.ogg").play(false);
      this.alreadyhurt = false;
      this.die();
    }
    else
    {
      SetRenderScript("LEVEL.renderHUD(); PLAYER.hurtUpdate();");
      this.xspeed = -this.xspeed*2;
      
      if (this.yspeed >= 0)
      {
        this.yspeed = -6;
        this.y -= 5;
      }
      else
      {
        this.yspeed = 1;
        this.y += 5;
      }
      
      this.updateMovement();
    }
  }


  /** UpdateScript for player hurtin'. **/
  this.hurtUpdate = function()
  {
    if (!this.hurtcounter) this.hurtcounter = 0;
    this.hurtcounter++;
    SetPersonFrame(this.name, 8);
    SetPersonVisible(this.name, this.hurtcounter%2);

    if (this.hurtcounter > 45)
    {
      SetRenderScript("LEVEL.renderHUD();");
      SetPersonVisible(this.name, true);
      this.hurtcounter = 0;
      this.alreadyhurt = false;
    }
  }


  /** Kill the player. **/
  this.die = function()
  {
    this.deaths++;
    LEVEL.music.stop();
		MUSIC.play("music/death.ogg", false);
		
    SetPersonVisible(this.name, false);
    SetRenderScript("LEVEL.renderHUD(); PLAYER.dieUpdate();");
    SetUpdateScript("");
    SetPersonFrame(this.name, 8);
  }


  /** UpdateScript for player killin'. **/
  this.dieUpdate = function()
  {
    if (!this.diecounter) this.diecounter = 0;
    this.diecounter++;
    SetPersonVisible(this.name, this.diecounter%2);
    
    if (this.diecounter > 68)
    {
      this.diecounter = 0;
      SetPersonVisible(this.name, false);
      SetRenderScript("LEVEL.renderHUD();");
      LEVEL.dieScreen();
      this.resetMovement();
    }
  }

  
  /** Show the protection bubble around the player. **/
  this.enableBubble = function(enable)
  {
    this.powerups.bubble = enable;
    SetPersonVisible("bubble", enable);
  }
  
  
  
  /** Warp the player to a different location **/
  this.warp = function(x, y)
  {
    if (GAME.sound) res.load("explode06.ogg").play(false);
    var base = GetPersonBase("player");
    var oldx = PLAYER.x;
    var oldy = PLAYER.y;
    PLAYER.x = x*GetTileWidth()+base.x1;
    PLAYER.y = y*GetTileHeight()+base.y1;
    SetPersonX("player",PLAYER.x);
    SetPersonY("player",PLAYER.y);
    
    //New position is far: do an instant camera move. Gives a nice effect to the close warps.
    if (oldy-PLAYER.y > 230 || oldy-PLAYER.y < -230 || oldx-PLAYER.x > 310 || oldx-PLAYER.x < -310)
			CAMERA.fixToPoint(Math.floor(PLAYER.x/SW)*SW+SW/2, Math.floor(PLAYER.y/SH)*SH+SH/2);
  }
  
  
  /** Fire a projectile. **/
  this.fireProjectile = function()
  {
    if (this.canthrow && this.projectiles.length < this.maxprojectiles)
    {
			res.load("swish.ogg").play(false);
			var projectile = new Projectile(this.name, this.xdir);
			this.canthrow = false;
		}
  }


  /** Take a wild guess. **/
  this.debug = function()
  {
    LEVEL.font.drawTextBox(10, 10, 300, 200, 0,
      "x: " + this.x + "; xspeed: " + this.xspeed + "; xdir: " + this.xdir +
      "\ny: " + this.y + "; yspeed: " + this.yspeed + "; ydir: " + this.ydir +
      "\ninair: " + this.inair + "; jumped_prev_frame: " + this.jumped_prev_frame +
      "\nfloating: " + this.floating + "; floated_this_jump: " + this.floated_this_jump + "; floattime: " + this.floattime
      );
  }
}
