1 /**
  2     A class to represent the player on the screen
  3     @author <a href="mailto:matthewcasperson@gmail.com">Matthew Casperson</a>
  4     @class
  5 */
  6 function Player()
  7 {
  8     /** The maximum height of the jump
  9         @type Number
 10      */
 11     this.jumpHeight = 64;
 12     /** The constant or half PI
 13         @type Number
 14      */
 15     this.halfPI = Math.PI / 2;
 16     /** The amount of time to spend in the air when jumping
 17         @type Number
 18      */
 19     this.jumpHangTime = 0.5;
 20     /** The speed to progress alone the sine wave that defines
 21         the jumping arc
 22         @type Number
 23      */
 24     this.jumpSinWaveSpeed = this.halfPI / this.jumpHangTime;
 25     /** The current position on the sine wave that defines the jump arc
 26         @type Number
 27      */
 28     this.jumpSinWavePos = 0;
 29     /** The rate to fall at
 30         @type Number
 31      */
 32     this.fallMultiplyer = 1.5;
 33     /** True when the player is on the ground, false otherwise
 34         @type Boolean
 35      */
 36     this.grounded = true;
 37     /** the players running speed
 38         @type Number
 39      */
 40     this.speed = 75;
 41     /** True if the player is moving left, false otherwise
 42         @type Boolean
 43      */
 44     this.left = false;
 45     /** True if the player is moving right, false otherwise
 46         @type Boolean
 47      */
 48     this.right = false;
 49     /** A reference to the level object
 50         @type Level
 51     */
 52     this.level = null;
 53     /** The distance between the player and the edge of the screen
 54         @type Number
 55      */
 56     this.screenBorder = 20;
 57 
 58     /**
 59         Initialises this object
 60     */
 61     this.startupPlayer = function(level)
 62     {
 63         this.startupAnimatedGameObject(g_idle_left, 300, 400 - 48 - 48, 4, 6, 20);
 64         this.level = level;
 65         return this;
 66     }
 67 
 68     /**
 69         Called when a key is pressed
 70         @param event Event Object
 71     */
 72     this.keyDown = function(event)
 73     {
 74         var updateRequired = false;
 75 
 76         // left
 77         if (event.keyCode == 37 && !this.left)
 78         {
 79             this.left = true;
 80             updateRequired = true;
 81         }
 82         // right
 83         if (event.keyCode == 39 && !this.right)
 84         {
 85             this.right = true;
 86             updateRequired = true;
 87         }
 88         if (event.keyCode == 32 && this.grounded)
 89         {
 90             this.grounded = false;
 91             this.jumpSinWavePos = 0;
 92         }
 93 
 94         if (updateRequired)
 95             this.updateAnimation();
 96 
 97     }
 98 
 99     /**
100         Called when a key is pressed
101         @param event Event Object
102     */
103     this.keyUp = function(event)
104     {
105         // left
106         if (event.keyCode == 37)
107         {
108             this.left = false;
109             this.setAnimation(g_idle_left, 6, 20);
110         }
111         // right
112         if (event.keyCode == 39)
113         {
114             this.right = false;
115             this.setAnimation(g_idle_right, 6, 20);
116         }
117 
118         this.updateAnimation();
119     }
120 
121     /**
122         Updates the current animation depending on the movement
123         of the player. This accounts for the fact that both
124         the left and right arrow keys can be pressed at the
125         same time.
126     */
127     this.updateAnimation = function()
128     {
129        if (this.right && this.left)
130             this.setAnimation(g_idle_left, 6, 20);
131         else if (this.right)
132             this.setAnimation(g_run_right, 12, 20);
133         else if (this.left)
134             this.setAnimation(g_run_left, 12, 20);
135     }
136 
137     /**
138         Updates the object
139         @param dt The time since the last frame in seconds
140         @param context The drawing context
141         @param xScroll The global scrolling value of the x axis
142         @param yScroll The global scrolling value of the y axis
143     */
144 	this.update = function (/**Number*/ dt, /**CanvasRenderingContext2D*/context, /**Number*/ xScroll, /**Number*/ yScroll)
145     {
146         if (this.left)
147             this.x -= this.speed * dt;
148         if (this.right)
149             this.x += this.speed * dt;
150 
151         // XOR operation (JavaScript does not have a native XOR operator)
152         // only test for a collision if the player is moving left or right (and not trying to do both at
153         // the same time)
154         if ((this.right || this.left) && !(this.left && this.right))
155         {
156             // this will be true until the player is no longer colliding
157             var collision = false;
158             // the player may have to be pushed back through several block stacks (especially if the
159             // frame rate is very slow)
160             do
161             {
162                 // the current position of the player (test the left side if running left
163                 // and the right side if running right)
164                 var xPos = this.left ? this.x : this.x + this.frameWidth;
165                 // the index of stack of blocks that the player is standing on/in
166                 var currentBlock = this.level.currentBlock(xPos);
167                 // the height of the stack of blocks that the player is standing on/in
168                 var groundHeight = this.level.groundHeight(currentBlock);
169                 // the height of the player (we need the height from the ground up,
170                 // whereas the this.y value represents the position of the player
171                 // from the "sky" down).
172                 var playerHeight = context.canvas.height - (this.y + this.image.height);
173                 // if the player is not higher than the stack of blocks, it must be colliding
174                 if (playerHeight  < groundHeight)
175                 {
176                     collision = true;
177                     // we are moving right, so push the player left
178                     if (this.right)
179                         this.x = this.level.blockWidth * currentBlock - this.frameWidth - 1;
180                     // we are moving left, push the player right
181                     else
182                         this.x = this.level.blockWidth * (currentBlock + 1);
183                 }
184                 else
185                 {
186                     collision = false;
187                 }
188             }  while (collision)
189         }
190 
191         // keep the player bound to the level
192         if (this.x > this.level.blocks.length * this.level.blockWidth - this.frameWidth - 1)
193             this.x = this.level.blocks.length * this.level.blockWidth - this.frameWidth - 1;
194         if (this.x > context.canvas.width - this.frameWidth + xScroll -  this.screenBorder)
195             g_GameObjectManager.xScroll = this.x - (context.canvas.width - this.frameWidth -  this.screenBorder);
196         // modify the xScroll value to keep the player on the screen
197         if (this.x < 0)
198             this.x = 0;
199         if (this.x -  this.screenBorder < xScroll)
200             g_GameObjectManager.xScroll = this.x - this.screenBorder;
201 
202         // if the player is jumping or falling, move along the sine wave
203         if (!this.grounded)
204         {
205             // the last position on the sine wave
206             var lastHeight = this.jumpSinWavePos;
207             // the new position on the sine wave
208             this.jumpSinWavePos += this.jumpSinWaveSpeed * dt;
209 
210             // we have fallen off the bottom of the sine wave, so continue falling
211             // at a predetermined speed
212             if (this.jumpSinWavePos >= Math.PI)
213                  this.y += this.jumpHeight / this.jumpHangTime * this.fallMultiplyer * dt;
214             // otherwise move along the sine wave
215             else
216                 this.y -= (Math.sin(this.jumpSinWavePos) - Math.sin(lastHeight)) * this.jumpHeight;
217         }
218 
219         // now that the player has had it's y position changed we need to check for a collision
220         // with the ground below the player. we have to check both the players left and right sides
221         // for a collision with the ground
222 
223         // left side
224         var currentBlock1 = this.level.currentBlock(this.x);
225         // right side
226         var currentBlock2 = this.level.currentBlock(this.x + this.frameWidth);
227         // ground height below the left side
228         var groundHeight1 = this.level.groundHeight(currentBlock1);
229         // ground height below the right side
230         var groundHeight2 = this.level.groundHeight(currentBlock2);
231         // the heighest point under the player
232         var maxGroundHeight = groundHeight1 > groundHeight2 ? groundHeight1 : groundHeight2;
233         // the players height (relaitive to the bottom of the screen)
234         var playerHeight = context.canvas.height - (this.y + this.image.height);
235 
236         // we have hit the ground
237         if (maxGroundHeight >= playerHeight)
238         {
239             this.y = context.canvas.height - maxGroundHeight - this.image.height;
240             this.grounded = true;
241             this.jumpSinWavePos = 0;
242         }
243         // otherwise we are falling
244         else if (this.grounded)
245         {
246             this.grounded = false;
247             // starting falling down the sine wave (i.e. from the top)
248             this.jumpSinWavePos = this.halfPI;
249         }
250     }
251 }
252 
253 Player.prototype = new AnimatedGameObject;