Source: playerClass.js

1
/**
 * @public
 * @class
 * @name Player
 * @constructor
 * @param {String} nick The nickname for the player
 * @prop {String} nickname - The nickname of the player
 * @prop {Number} id - The id of the player
 * @prop {Integer} HP - The current number of health points the player has.
 * @prop {integer} maxHP - The maximum number of health points the player can have.
 * @prop {BlockPosition} position - The current {@link BlockPosition} of the player. This is the {@link BlockPosition} of the player's feet.
 * @prop {Number} xSpeed - The speed at which the player changes their block x position per tick when moving.
 * @prop {Number} ySpeed - The speed at which the player changes their block y position per tick when moving.
 */
function Player(nick) {
    this.nickname = nick;
    this.id = 0;
    this.HP = 10;
    this.maxHP = 10;
    this.noClipMode = false; // If set to true, the player ignores gravity, fluids, explosions, solid blocks. Player can move freely without limits
    this.gravity = true; // If set to true, player will fall at twice ySpeed until they land on a block
    this.canJump = true; // Player can only jump if not already in the air or jumping
	this.jumpingTicks = 0; // Number of ticks the player still has left in their jump. If 0, then the player is not jumping at all

    this.position = new BlockPosition(0, 0, 50, 50); // This block position is the position of the bottom-most, left corner block of the player entity. (their feet block)
    
    this.update = function () {
		if (!this.noClipMode) {
			if (this.gravity) {
				if (this.jumpingTicks > 0) {
					if (!game.physics.belowSolidBlock(game.map.getPositionRelativeTo(this.position, 0, -2 + (-3*this.speed.yBlockSpeed)))) {
						this.position.localY -= 3*this.speed.yBlockSpeed;
						this.jumpingTicks--;
					}
					else {
						this.position.localY = Math.floor(this.position.localY); // Player is hitting their head on a block above them, so make sure the player actually reaches the block and doesnt cut short (mining stairs logic)
						this.jumpingTicks = 0;					
					}
				}
				else {
					/*if (!game.physics.aboveSolidBlock(this.position)) {
						this.position.localY += 2*this.speed.yBlockSpeed;
						this.canJump = false;
					}*/
					
					if (!game.physics.playerAboveSolidBlock(this)) {
					   this.position.localY += 2*this.speed.yBlockSpeed;
					   this.canJump = false;
					}
					else {
						this.canJump = true; // Player is on top of a solid block, so allow the player to jump now
						if (this.position.localY > Math.floor(this.position.localY)) { //Player landed on a solid block, so lets ensure that the player is directly on top of the block and not floating on top of the block
							this.position.localY = Math.ceil(this.position.localY);
						}
					}
				}
			}
		}    	
    };
    
    
    /**
      * @function
      * @memberof Player
      * @param {Object} entity - The entity that is being tested to see if testPosition is within its hitbox
      * @param {BlockPosition} testPosition - The {@link BlockPosition} that represents the position that is being tested to see if it is within the entity's hitbox
      * @returns {Boolean} True if the BlockPosition is within the target entity's hitbox. False otherwise.
      * @description This function tests to see if the given BlockPosition is within the target entity's hitbox. Takes into account sectors!
      *
      */
    this.positionInHitBox = function (entity, testPosition) {
    	
		if (entity.position.sectorX == testPosition.sectorY && entity.position.sectorY == testPosition.sectorY) {
			//Great, they're in the same sector! Let's test it.
			if (entity.position.localX <= testPosition.localX && entity.position.localX + entity.dimensions.bWidth >= testPosition.localX) {
				//Ok, within the needed X range, lets test Y
				if (entity.position.localY >= testPosition.localY && entity.position.localY - entity.dimensions.bHeight <= testPosition.localY) {
					return true;
				}
			}
			return false;
		}    	
    	else {
    	//First let's make sure that this is even remotely near the entity by checking how many sectors its away
    	if (Math.sqrt(Math.pow(testPosition.sectorX - entity.position.sectorX, 2) + Math.pow(testPosition.sectorY - entity.position.sectorY, 2)) <= 1) {
        		//Ok fine, it's close enough to waste the CPU time to check if its in the hitbox
    	    	var leftBound = new BlockPosition(entity.position.sectorX, entity.position.sectorY, entity.position.localX, entity.position.localY); //Left bound is the sectorX and localX, needs no modification
    	    	var rightBound = game.map.getPositionRelativeTo(new BlockPosition(entity.position.sectorX, entity.position.sectorY, entity.position.localX, entity.position.localY), entity.dimensions.bWidth, 0); //Right bound is the sectorX and localX plus the width of the entity
    	    	var topBound = game.map.getPositionRelativeTo(new BlockPosition(entity.position.sectorX, entity.position.sectorY, entity.position.localX, entity.position.localY), 0, -1 * entity.dimensions.bHeight); //Top bound is the sectorY and localY minus the height of the entity
    	    	var bottomBound = new BlockPosition(entity.position.sectorX, entity.position.sectorY, entity.position.localX, entity.position.localY); //Bottom bound is the sectorY and localY, needs no modification
    
        		//Test each bound to see if the testPosition violates any requirements of the position. If it doesn't violate any requirements, then it must be in the hitbox, so return true.
        		if (leftBound.sectorX > testPosition.sectorX) {
        			return false;	
        		}
        		else if (leftBound.sectorX == testPosition.sectorX) {
        			if (leftBound.localX > testPosition.localX) {
        				return false;
        			}
        		}
        		
    			if (rightBound.sectorX < testPosition.sectorX) {
    				return false;
    			}
    			else if (rightBound.sectorX == testPosition.sectorX) {
    				if (rightBound.localX < testPosition.sectorX) {
    					return false;
    				}
    			}    		
        		
    			if (topBound.sectorY > testPosition.sectorY) {
    				return false;
    			}    		
    			else if (topBound.sectorY == testPosition.sectorY) {
    				if (topBound.localY > testPosition.localY) {
    					return false;
    				}
    			}
    			
    			if (bottomBound.sectorY < testPosition.sectorY) {
    				return false;
    			}
    			else if (bottomBound.sectorY == testPosition.sectorY) {
    				if (bottomBound.localY < testPosition.sectorY) {
    					return false;
    				}
    			}    
    			
        		return true;
        	}
        	else {
        		return false;
        	}
        }
    };
    

    this.dimensions = {
        bWidth: 0.5,
        bHeight: 3
    };

    this.speed = {
        xBlockSpeed: 0.125,
        yBlockSpeed: 0.125,
        xSpeed: this.xBlockSpeed * game.blockSize,
        ySpeed: this.yBlockSpeed * game.blockSize
    };
    
    this.updateSectorPosition = function() {
        if (this.position.localY < 0) { // If we go above the our previous sector, decrement the sectorY and calculate the new localY
            this.position.sectorY--;
            this.position.localY = game.generation.SECTOR_SIZE + this.position.localY;
        } else if (this.position.localY > game.generation.SECTOR_SIZE) { // If we go below the our previous sector, increment the sectorY and calculate the new localY
            this.position.sectorY++;
            this.position.localY -= game.generation.SECTOR_SIZE;
        }

        if (this.position.localX < 0) { // If we go left of the previous sector, decrement the sectorX and calculate the new localX
            this.position.sectorX--;
            this.position.localX = game.generation.SECTOR_SIZE + this.position.localX;
        } else if (this.position.localX > game.generation.SECTOR_SIZE) { // If we go right of the previous sector, increment the sectorX and calculate the new localX
            this.position.sectorX++;
            this.position.localX -= game.generation.SECTOR_SIZE;
        }
    };

    this.render = function(x, y, g) {
        g.can.fillStyle = "#FF0";
        g.can.fillRect(x, y, this.dimensions.bWidth * g.blockSize, g.blockSize * this.dimensions.bHeight);
        g.can.fillStyle = "#F00";
        g.can.beginPath();
        g.can.arc(x,y,4,0,2 * Math.PI);
        g.can.fill();
    };

    this.goRight = function() {
		if (this.noClipMode || (
!game.blockList[game.map.getBlockIdAt(game.map.getPositionRelativeTo(this.position, this.dimensions.bWidth + this.speed.xBlockSpeed/2, 0))].solid && 
		!game.blockList[game.map.getBlockIdAt(game.map.getPositionRelativeTo(this.position, this.dimensions.bWidth + this.speed.xBlockSpeed/2, -1))].solid &&
 !game.blockList[game.map.getBlockIdAt(game.map.getPositionRelativeTo(this.position, this.dimensions.bWidth + this.speed.xBlockSpeed/2, -2))].solid
)) {
			
    	
        this.position.localX += this.speed.xBlockSpeed;
        this.updateSectorPosition();
 	   }
    };
    
   this.goLeft = function() {
		if (this.noClipMode || (
!game.blockList[game.map.getBlockIdAt(game.map.getPositionRelativeTo(this.position, -this.speed.xBlockSpeed, 0))].solid
 && !game.blockList[game.map.getBlockIdAt(game.map.getPositionRelativeTo(this.position, -this.speed.xBlockSpeed, -1))].solid 
&& !game.blockList[game.map.getBlockIdAt(game.map.getPositionRelativeTo(this.position, -this.speed.xBlockSpeed, -2))].solid
)) {
			
    	
        this.position.localX -= this.speed.xBlockSpeed;
        this.updateSectorPosition();
 	   }
    };
    
    this.goUp = function() {
        this.position.localY -= this.speed.yBlockSpeed;
        this.updateSectorPosition();
    };
    
    this.goDown = function() {
        this.position.localY += this.speed.yBlockSpeed;
        this.updateSectorPosition();
    };
    
    this.jump = function () {
    	if (this.canJump) {
	    	//this.position.localY -= 24*this.speed.yBlockSpeed;
	    	//this.updateSectorPosition();
	    	this.canJump = false;
	    	this.jumpingTicks = 6;
	    }
    };
}