Logo Search packages:      
Sourcecode: ballz version File versions  Download package

ball.cpp

/*
 * Copyright (c) 2007, Olof Naessen and Per Larsson
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 *
 *    * Redistributions of source code must retain the above copyright notice, 
 *      this list of conditions and the following disclaimer.
 *    * Redistributions in binary form must reproduce the above copyright notice, 
 *      this list of conditions and the following disclaimer in the documentation 
 *      and/or other materials provided with the distribution.
 *    * Neither the name of the Darkbits nor the names of its contributors may be 
 *      used to endorse or promote products derived from this software without 
 *      specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <string>
#include <allegro.h>
#include <iostream>

#include "ball.hpp"
#include "levelhandler.hpp"
#include "resourcehandler.hpp"

const int JUMP_STRENGTH = 13;
const int METAL_JUMP_STRENGTH = 7;
const int MAX_AIR_SPEED = 7;
const int MAX_WATER_SPEED = 4;
const int AIR_CONTROL = 4; // Lower = more control

const int STARS_TO_GET_METAL = 52;
const int METALBALL_COOLDOWN = 400;
const int METALBALL_TIME = 300;

const int STARS_TO_GET_RUSH = 17;
const int RUSH_POWER = 10;

Ball::Ball(int x, int y) {
      
    sprite = ResourceHandler::getInstance()->getBitmap("ball.bmp");
      spriteBlink = ResourceHandler::getInstance()->getBitmap("ballblink.bmp");
    spriteIron = ResourceHandler::getInstance()->getBitmap("balliron.bmp");
      spriteBlinkIron = ResourceHandler::getInstance()->getBitmap("ballblinkiron.bmp");
    spriteCoolDown = ResourceHandler::getInstance()->getBitmap("ballcooldown.bmp");
    spriteBlinkCoolDown = ResourceHandler::getInstance()->getBitmap("ballcooldown.bmp");

      bounceSample = ResourceHandler::getInstance()->getSample("bounce.wav");
      rushSample = ResourceHandler::getInstance()->getSample("rush.wav");
      jumpSample = ResourceHandler::getInstance()->getSample("jump.wav");

      this->x = x;
      this->y = y;
      dy = 0;
      dx = 2;
      setState(AIR_DOWN);
      lastKeyUp = false;
      bouncing = false;
      metalBallCounter = 0;
      speedCooldown = 0;
    blinkCounter = 0;
      freezeTime = 0;
    mImortal = false;
}

void Ball::logic(TileMap *tileMap) {

    if (key[KEY_F1])
    {
        mImortal = true;
    }

    if (key[KEY_F2])
    {
        mImortal = false;
    }

      if (state == DEAD) {
            return;
      }

      if (freezeTime > 0) {
            freezeTime--;
            return;
      }

      blinkCounter++;

      if (dx < -2) {
            dx++;
            dy = 0;
      }

      if (dx > 2) {
            dx--;
            dy = 0;
      }

      if (metalBallCounter < 0) {
            metalBallCounter++;
      }

      if (metalBallCounter > 0) {
            metalBallCounter--;
            if (metalBallCounter == 0) {
                  metalBallCounter = -METALBALL_COOLDOWN;
            }
      }     

      if ((key[KEY_LSHIFT] || key[KEY_RSHIFT]) && metalBallCounter == 0 && LevelHandler::getInstance()->getStarsCollected() >= STARS_TO_GET_METAL) {
            metalBallCounter = METALBALL_TIME;
            play_sample(rushSample, 150, 128, 400, 0);
      }


      bool keyUp = key[KEY_UP];

      if (tileMap->getTileAtPixel(getCenterX(), getCenterY())->isWater() && !isMetal()) {
            logicWater(tileMap, keyUp);
            lastKeyUp = keyUp;
            upHeldDuringJump = keyUp;
            return;
      }

      bool keyCtrl = key[KEY_LCONTROL] || key[KEY_RCONTROL];
      bool canRush = LevelHandler::getInstance()->getStarsCollected() >= STARS_TO_GET_RUSH;

      // Rushing
      if (keyCtrl && !lastKeyCtrl && dx < 0 && speedCooldown == 0 && canRush && state != ROLL && !bouncing) {
            dx = -RUSH_POWER;
            dy = 0;
            speedCooldown = 1;
            play_sample(rushSample, 100, 128, 1000, 0);
      }

      if (keyCtrl && !lastKeyCtrl && dx > 0 && speedCooldown == 0 && canRush && state != ROLL && !bouncing) {
            dx = RUSH_POWER;
            dy = 0;
            speedCooldown = 1;
            play_sample(rushSample, 100, 128, 1000, 0);
      }

      lastKeyCtrl = keyCtrl;

      // Interrupting jumping
      if (!keyUp && state == AIR_UP && dy > AIR_CONTROL) {
            dy = AIR_CONTROL;       
      }

      if (!keyUp && (state == AIR_UP || state == AIR_DOWN)) {
            upHeldDuringJump = false;
      }

      // Stop bouncing if fall of ledge
      if (y > bounceY) {
            bouncing = false;
      }

      // Jumping
      if (!lastKeyUp && keyUp && (state == ROLL || bouncing)) {
            dy = isMetal() ? METAL_JUMP_STRENGTH : JUMP_STRENGTH;
            setState(AIR_UP);
            upHeldDuringJump = true;
            // Align to tile
            if (bouncing) {
                  y = bounceY;
            }
            play_sample(jumpSample, 100, 128, 1000, 0);
            bouncing = false;       
      }
      lastKeyUp = keyUp;

      // Sideways movement
      x += dx;

      // Left Bounce
      if (dx < 0 && (tileMap->getTileAtPixel(x, y)->isSolid() || tileMap->getTileAtPixel(x, y + 15)->isSolid())) {            
            // Align to tile
            x = x - (x % 16) + 16;
            // Bounce unless in narrow vertical well
            if (!(tileMap->getTileAtPixel(x + 16, y)->isSolid() || tileMap->getTileAtPixel(x + 16, y + 15)->isSolid())) {
                  dx = -dx;
            }
      }

      // Right Bounce
      if (dx > 0 && (tileMap->getTileAtPixel(x + 15, y)->isSolid() || tileMap->getTileAtPixel(x + 15, y + 15)->isSolid())) {
            // Align to tile
            x = x - (x % 16);
            // Bounce unless in narrow vertical well
            if (!(tileMap->getTileAtPixel(x - 1, y)->isSolid() || tileMap->getTileAtPixel(x - 1, y + 15)->isSolid())) {
                  dx = -dx;
            }
      }

      // Falling, raising, movement
      if (state == AIR_DOWN) {
            y += dy > MAX_AIR_SPEED ? MAX_AIR_SPEED : dy;
      } else if (state == AIR_UP) {
            y -= dy > MAX_AIR_SPEED ? MAX_AIR_SPEED : dy;
      }

      // Falling, acceleration
      if (state == AIR_DOWN) {
            dy += 1;
            if (dy > 1 && dy < 7 && tileMap->getTileAtPixel(getCenterX(), getCenterY() + 8)->isWater() && !isMetal()) {
                  dy = 1;
            }
      }

      // Raising, deacceleration
      if (state == AIR_UP) {
            dy--;
            if (dy < 0) {
                  setState(AIR_DOWN);
                  dy = 0;
            }
      }

      // Ceiling bouncing
      if (state == AIR_UP && (tileMap->getTileAtPixel(x+2, y)->isSolid() || tileMap->getTileAtPixel(x + 13, y)->isSolid())) {
            setState(AIR_DOWN);
            // Align to tile
            y = y - (y % 16) + 16;
      }

      // Ground bouncing / landing
      bool onSolidGround = tileMap->getTileAtPixel(x + 2, y + 16)->isSolid() || tileMap->getTileAtPixel(x + 13, y + 16)->isSolid();
      if (state == AIR_DOWN && onSolidGround) {
            if (keyUp && !upHeldDuringJump) {
                  // Rejump
                  dy = isMetal() ? METAL_JUMP_STRENGTH : JUMP_STRENGTH;
                  setState(AIR_UP);
                  upHeldDuringJump = true;
                  play_sample(jumpSample, 100, 128, 1000, 0);
            } else {
                  if (dy > AIR_CONTROL && !isMetal()) {
                        // Bounce
                        dy = dy < (AIR_CONTROL * 2) ? dy / 2 : AIR_CONTROL;
                        setState(AIR_UP);
                        bouncing = true;
                  } 
                  else {
                        // Land
                        setState(ROLL);
                        dy = 0;
                        bouncing = false;                   
                  }
                  play_sample(bounceSample, 80, 128, 1000, 0);
            }
            // Align to tile
            y = y - (y % 16);
            bounceY = y;
            speedCooldown = 0;            
      }

      // Falling from rolling
      if (state == ROLL && !onSolidGround) {
            setState(AIR_DOWN);
            dy = 0;
      }

      // Fall off screen
      if (y > 380) {
            kill();
      }
}

void Ball::logicWater(TileMap *tileMap, bool keyUp) {
/*
      // Interrupting jumping
      if (!keyUp && state == AIR_UP && dy > AIR_CONTROL) {
            dy = AIR_CONTROL;       
      }

      if (!keyUp && (state == AIR_UP || state == AIR_DOWN)) {
            upHeldDuringJump = false;
      }
*/
      // Stop bouncing if fall of ledge
      if (y > bounceY) {
            bouncing = false;
      }
/*
      // Jumping
      if (!lastKeyUp && keyUp && (state == ROLL || bouncing)) {
            dy = JUMP_STRENGTH;
            setState(AIR_UP);
            upHeldDuringJump = true;
            // Align to tile
            if (bouncing) {
                  y = bounceY;
            }
            bouncing = false;
      }
      lastKeyUp = keyUp;
*/

      // Sideways movement
      x += dx;

      // Left Bounce
      if (dx < 0 && (tileMap->getTileAtPixel(x, y)->isSolid() || tileMap->getTileAtPixel(x, y + 15)->isSolid())) {            
            // Align to tile
            x = x - (x % 16) + 16;
            // Bounce unless in narrow vertical well
            if (!(tileMap->getTileAtPixel(x + 16, y)->isSolid() || tileMap->getTileAtPixel(x + 16, y + 15)->isSolid())) {
                  dx = -dx;
            }
      }

      // Right Bounce
      if (dx > 0 && (tileMap->getTileAtPixel(x + 15, y)->isSolid() || tileMap->getTileAtPixel(x + 15, y + 15)->isSolid())) {
            // Align to tile
            x = x - (x % 16);
            // Bounce unless in narrow vertical well
            if (!(tileMap->getTileAtPixel(x - 1, y)->isSolid() || tileMap->getTileAtPixel(x - 1, y + 15)->isSolid())) {
                  dx = -dx;
            }
      }

      // Falling, raising, movement
      if (state == AIR_DOWN) {
            y += dy > MAX_WATER_SPEED ? MAX_WATER_SPEED : dy;
      } else if (state == AIR_UP) {
            y -= dy > MAX_WATER_SPEED ? MAX_WATER_SPEED : dy;
      }


      // Floating, acceleration
      if (state == AIR_UP) {
            dy += 1;
            if (keyUp) {
                  if (dy < 4) {
                        dy = 4;
                  }
                  if (dy > 16) {
                        dy = 16;
                  }
            } else {
                  if (dy > 1) {
                        dy = 1;
                  }
            }
      }

      // Falling, deacceleration
      if (state == AIR_DOWN) {
            if (keyUp) {
                  dy-=2;
            } else {
                  dy--;
            }
            if (dy < 0) {
                  setState(AIR_UP);
                  dy = 0;
            }
      }

      // Ground bouncing
      if (state == AIR_DOWN && (tileMap->getTileAtPixel(x+2, y+16)->isSolid() || tileMap->getTileAtPixel(x + 13, y+16)->isSolid())) {
            setState(AIR_UP);
            // Align to tile
            y = y - (y % 16);
      }

      // Ceiling bouncing / landing
      bool onSolidGround = tileMap->getTileAtPixel(x + 2, y)->isSolid() || tileMap->getTileAtPixel(x + 13, y)->isSolid();
      if (state == AIR_UP && onSolidGround) {
            /*if (keyUp && !upHeldDuringJump) {
                  // Rejump
                  dy = JUMP_STRENGTH;
                  setState(AIR_UP);
                  upHeldDuringJump = true;
            } else */{
                  if (dy > AIR_CONTROL) {
                        // Bounce
                        dy = dy < (AIR_CONTROL * 2) ? dy / 2 : AIR_CONTROL;
                        setState(AIR_DOWN);
                        bouncing = true;
                  } 
                  else {
                        // Land
                        setState(ROLL);
                        dy = 0;
                        bouncing = false;
                  }
            }
            // Align to tile
            y = y - (y % 16) + 16;
            bounceY = y;
      }

      // Falling from rolling
      if (state == ROLL && !onSolidGround) {
            setState(AIR_UP);
            dy = 0;
      }
}

void Ball::draw(BITMAP *dest, int scroll) {
      if (dx > 2 || dx < -2) {
            int px;
            for (px = 0; px < 8; px++) {
                  if ((blinkCounter + px * 2) % 4 == 0) {
                        drawBall(dest, x - (dx * px) / 2, y, scroll);
                  }
            }
      }
      drawBall(dest, x, y, scroll);
}

void Ball::drawBall(BITMAP *dest, int x, int y, int scroll) {
      
    int frame = (x / 8) % (sprite->w / 16);
    
    if (blinkCounter % 300 >= 260)
    {
        if (isMetal())
        {
            masked_blit(spriteBlinkIron, dest, frame * 16, 0, x - scroll, y, 16, 16);
        }
        else if (metalBallCounter < 0) 
        {
            if (blinkCounter % 4 < 2)
            {
                masked_blit(spriteBlinkCoolDown, dest, frame * 16, 0, x - scroll, y, 16, 16);
            }
            else
            {
                 masked_blit(sprite, dest, frame * 16, 0, x - scroll, y, 16, 16);
            }
        }
        else
        {
             masked_blit(spriteBlink, dest, frame * 16, 0, x - scroll, y, 16, 16);
        }
    }
    else
    {
        if (isMetal())
        {
            masked_blit(spriteIron, dest, frame * 16, 0, x - scroll, y, 16, 16);
        }
        else if (metalBallCounter < 0) 
        {
            if (blinkCounter % 4 < 2)
            {
                masked_blit(spriteCoolDown, dest, frame * 16, 0, x - scroll, y, 16, 16);
            }
            else
            {
                 masked_blit(sprite, dest, frame * 16, 0, x - scroll, y, 16, 16);
            }
        }
        else
        {
            masked_blit(sprite, dest, frame * 16, 0, x - scroll, y, 16, 16);    
        }
    }
}

int Ball::getCenterX() {
      return x + 8;
}

int Ball::getCenterY() {
      return y + 8;
}

void Ball::setState(State newState) {
      state = newState;
}

Ball::State Ball::getState() {
      return state;
}

void Ball::setPosition(int x, int y) {
      this->x = x;
      this->y = y;
      bouncing = false;
      dy = 0;
      setState(AIR_DOWN);
}

int Ball::getMetalBarPosition() {
      if (metalBallCounter > 0) {
            return 100 - (metalBallCounter * 100) / METALBALL_TIME;
      } else {
            return -(metalBallCounter * 100) / METALBALL_COOLDOWN;
      }
}

void Ball::kill() {
    if (!mImortal)
    {
          setState(DEAD);
    }
}

bool Ball::isMetal() {
      return metalBallCounter > 0;
}

void Ball::freeze(int time) {
       freezeTime = time;
}

Generated by  Doxygen 1.6.0   Back to index