Subversion Repositories AndroidProjects

Rev

Blame | Last modification | View Log | RSS feed

unit app_entity_ball;

interface

uses sux_constant, sux_object, app_constant, app_entity_main,
     app_entity_trail, app_entity_paddle,
     app_particle_ballsparks, app_particle_flowers;

type
  TBall=record
    rot:SXFloat;
    speed:SXFloat;
    speeduptimer:SXFloat;
    angularspeed:SXFloat;
    lastpos:SXVertex2D;
    lasthitplayer:SXInt;
    sparktimer: SXFloat;
  end;
  SAEBall=class(SAEntity)
    ball:TBall;
    trail:SAETrail;
    sparks: SAPBallSparks;
    flowers: SAPFlowers;

    procedure setRotation(rot:SXFloat);
    procedure setSpeed(speed:SXFloat);
    procedure setAngularVelocity(angularspeed:SXFloat);

    function getHitZoneAngleDeviation:SXFloat;
    function getTopSpinIntensity:SXFloat;
    function getBallStartSpeed:SXFloat;
    function getBallSpeedUpTimer:SXFloat;
    function getBallSpeedUpIncrease:SXFloat;
    function getBallSteepestAngleReflectFromBoard:SXFloat;
    function getBallSteepestAngleHitPaddle:SXFloat;
    function getBallMaximumAngularSpeed:SXFloat;
    function getBallAngularSpeedDecrease:SXFloat;

    procedure playerGainsScore(player:SXInt);
    procedure checkPlayerGainsScore;

    procedure generateTrail;
    procedure updateTrail;
    procedure generateImpact(pos:SXVertex2D;direction:SXFloat;mode:SXState);
    procedure reflectFromBoard;
    procedure spawnCrystal;

    procedure generateFlowers;
    procedure updateFlowers;
    procedure generateBallSparks;
    procedure updateBallSparks;
    procedure deleteBallSparks;

    procedure collectPowerUps;
    procedure reflectFromCrystals;
    procedure reflectAlongNormal(rot:SXFloat);
    procedure ballHitsPaddle(paddle:SAEPaddle);
    procedure reflectFromPaddle(paddle:SAEPaddle);
    procedure reflectFromEntities;

    procedure clampToAngle(const steepestangle: SXFloat);
    procedure speedUp;
    procedure moveForward;
    procedure processAngularVelocity;
    procedure onTimer; override;

    procedure initialize; override;
    constructor Create(parent:TObject);
    destructor Destroy; override;
  end;

implementation

uses main, app_entity_impact, Math, app_entity_crystal, app_entity_powerup;


// --- SAEBall


procedure SAEBall.setRotation(rot:SXFloat);
begin
  ball.rot:=rot;
end;


procedure SAEBall.setSpeed(speed:SXFloat);
begin
  ball.speed:=speed;
end;


procedure SAEBall.setAngularVelocity(angularspeed:SXFloat);
begin
  sx.math.clampToRange(angularspeed,-getBallMaximumAngularSpeed,getBallMaximumAngularSpeed);
  ball.angularspeed:=angularspeed;
end;




function SAEBall.getHitZoneAngleDeviation:SXFloat;
begin
  result:=app.ini.settings.gameplay.hitzoneangledeviation;
end;


function SAEBall.getTopSpinIntensity:SXFloat;
begin
  result:=app.ini.settings.gameplay.topspinintensity;
end;


function SAEBall.getBallStartSpeed:SXFloat;
begin
  result:=app.ini.settings.gameplay.ballstartspeed;
end;


function SAEBall.getBallSpeedUpTimer:SXFloat;
begin
  result:=app.ini.settings.gameplay.ballspeeduptimer;
end;


function SAEBall.getBallSpeedUpIncrease:SXFloat;
begin
  result:=app.ini.settings.gameplay.ballspeedupincrease;
end;


function SAEBall.getBallSteepestAngleReflectFromBoard:SXFloat;
begin
  result:=app.ini.settings.gameplay.ballsteepestanglereflectfromboard;
end;


function SAEBall.getBallSteepestAngleHitPaddle:SXFloat;
begin
  result:=app.ini.settings.gameplay.ballsteepestanglehitpaddle;
end;


function SAEBall.getBallMaximumAngularSpeed:SXFloat;
begin
  result:=app.ini.settings.gameplay.ballmaximumangularspeed;
end;


function SAEBall.getBallAngularSpeedDecrease:SXFloat;
begin
  result:=app.ini.settings.gameplay.ballangularspeeddecrease;
end;




procedure SAEBall.playerGainsScore(player:SXInt);
var typeofscore:SXState;
begin
  typeofscore:=SX_SCORE_TYPE_NORMAL;
  if (ball.lasthitplayer<>player) then typeofscore:=SX_SCORE_TYPE_OWNGOAL;

  app.game.playerGainsScore(player,typeofscore);
 
  delete;
end;


procedure SAEBall.checkPlayerGainsScore;
begin
  if (settings.pos.x<-270) then
  begin
    playerGainsScore(2);
    exit;
  end;
  if (settings.pos.x>270) then
  begin
    playerGainsScore(1);
    exit;
  end;
end;




procedure SAEBall.generateTrail;
begin
  if (trail=nil) then trail:=app.game.createEntity(SX_ENTITY_TRAIL) as SAETrail;
end;


procedure SAEBall.updateTrail;
begin
  if (trail=nil) then generateTrail;

  // Update the ball's trail
  trail.addElement(settings.pos);
end;


procedure SAEBall.generateImpact(pos:SXVertex2D;direction:SXFloat;mode:SXState);
var impact:SAEImpact;
begin
  if (app.game.settings.effects.concave > 0) and (mode = SX_EIMPACT_MODE_PADDLE) then
  begin
    direction := 180 - direction;
    if (direction = 180) then pos.x := pos.x + 25 else pos.x := pos.x - 25;
  end;
 
  impact:=app.game.createEntity(SX_ENTITY_IMPACT) as SAEImpact;
  impact.setPosition(pos);
  impact.setRotation(direction);
  impact.setMode(mode);
  impact.setInverseMovement(app.game.settings.effects.concave > 0);

  case mode of
    SX_EIMPACT_MODE_WALL      : app.sound.playSample2D('sounds\wallimpact');
    SX_EIMPACT_MODE_PADDLE    : app.sound.playSample2D('sounds\paddleimpact');
  end;

  if (mode = SX_EIMPACT_MODE_WALL) then
  if (app.game.settings.effects.createcrystals > 0) then
  if (not isOutOfBoard) then
  begin
    dec(app.game.settings.effects.createcrystals);
    spawnCrystal;
  end;

  updateTrail;
end;


procedure SAEBall.reflectFromBoard;
var impactdirection:SXFloat;
begin
  impactdirection := -1;
  if (settings.pos.y < -185) then
  begin
    reflectAlongNormal(0);
    impactdirection := 0;
    clampToAngle(getBallSteepestAngleReflectFromBoard);
  end;
  if (settings.pos.y > 185) then
  begin
    reflectAlongNormal(180);
    impactdirection := 180;
    clampToAngle(getBallSteepestAngleReflectFromBoard);
  end;

  sx.math.clampToRange(settings.pos.y, -185, 185);

  // Generate an impact on the board
  if (impactdirection >- 1) then generateImpact(settings.pos,impactdirection,SX_EIMPACT_MODE_WALL);
end;


procedure SAEBall.spawnCrystal;
var crystal: SAECrystal;
begin
  crystal := SAECrystal(app.game.createEntity(SX_ENTITY_CRYSTAL));
  crystal.setPosition(getPos);
  crystal.setRotation(sx.math.random.getRandomFloat(0, 360));
  crystal.setSpawnParameters(4, 15, 5.0);

  app.sound.playSample2D('sounds\crystal1');
end;




procedure SAEBall.generateFlowers;
begin
  if (flowers = nil) then
  begin
    flowers := SAPFlowers(app.game.createParticleSystem(SX_PARTICLESYSTEM_FLOWERS));
    flowers.addFlowers(getPos);
  end;
end;


procedure SAEBall.updateFlowers;
begin
  generateFlowers;

  sx.timer.incTimer(ball.sparktimer, 1/SX_EBALL_ITERATIONS);
  while (ball.sparktimer > 0.02) do
  begin
    ball.sparktimer := ball.sparktimer - 0.02;
    flowers.addFlowers(getPos);
  end;
end;


procedure SAEBall.generateBallSparks;
begin
  if (sparks = nil) then
  begin
    sparks := SAPBallSparks(app.game.createParticleSystem(SX_PARTICLESYSTEM_BALLSPARKS));
  end;
end;


procedure SAEBall.updateBallSparks;
begin
  if (app.game.settings.effects.flowers <= 0) then
  begin
    generateBallSparks;
    flowers := nil;

    sx.timer.incTimer(ball.sparktimer, 1/SX_EBALL_ITERATIONS);
    while (ball.sparktimer > 0.02) do
    begin
      ball.sparktimer := ball.sparktimer - 0.02;
      sparks.addSparks(getPos, ball.angularspeed);
    end;
  end else
  begin
    updateFlowers;
  end;
end;


procedure SAEBall.deleteBallSparks;
begin
  if (sparks <> nil) then app.game.deleteParticleSystem(sparks);
  sparks := nil;
end;




procedure SAEBall.collectPowerUps;
var e: SXInt;
    other: SAEntity;
begin
  for e := app.game.entities.getMax downto 0 do
  begin
    other := SAEntity(app.game.entities.getObject(e));
    if (self <> other) and (other is SAEPowerUp) then
    if (doBoundingCirclesIntersect(other)) then
    begin
      SAEPowerUp(other).collect;
      app.game.deleteEntity(other);
      exit;
    end;
  end;
end;


procedure SAEBall.reflectFromCrystals;
var e: SXInt;
    other: SAEntity;
    normal: SXVertex2D;
    rot: SXFloat;
    distance: SXVertex2D;
begin
  for e := app.game.entities.getMax downto 0 do
  begin
    other := SAEntity(app.game.entities.getObject(e));
    if (self <> other) and (other.settings.solid) then
    if (other is SAECrystal) and (other.settings.size > 5) then
    begin

      distance.x := settings.pos.x - other.settings.pos.x;
      distance.y := settings.pos.y - other.settings.pos.y;
      if ((sqr(distance.x) + sqr(distance.y)) < sqr(other.settings.size)) then
      begin
        // Reflect from this entity.
        normal := sx.math.getPointVector(other.getPos, ball.lastpos);
        normal := sx.math.normalizeVector(normal);

        rot := radtodeg(arccos(normal.x));
        if normal.y > 0 then rot := 180 - rot + 90 else rot := rot - 90;
        rot := 180+rot;
       
        reflectAlongNormal(rot);

        app.sound.playSample2D('sounds\crystal2');

        SAECrystal(other).generateShards;
        app.game.deleteEntity(other);
        exit;
      end;

    end;
  end;
end;


procedure SAEBall.reflectAlongNormal(rot:SXFloat);
var difference:SXFloat;
begin
  ball.rot:=ball.rot+180;
  sx.math.stayInDegrees0to360(ball.rot);

  {angle:=radtodeg(arccos(vector.x));
  if vector.z>0 then angle:=180-angle+90 else angle:=angle-90;}


  difference:=rot-ball.rot;

  ball.rot:=rot+difference;
  sx.math.stayInDegrees0to360(ball.rot);

  if (sx.math.turnDegrees(ball.rot,rot)>=90) then
  begin
    ball.rot:=360-ball.rot;
    sx.math.stayInDegrees0to360(ball.rot);
  end;

  {normal:=sx.math.normalizeVector(normal);
  ballvector:=sx.math.normalizeVector(ball.linear.velocity);

  ball.linear.velocity:=sx.math.subtractVertices(ballvector,sx.math.multiplyVertex(normal,2*sx.math.getDotProduct(ballvector,normal)));}

end;


procedure SAEBall.ballHitsPaddle(paddle:SAEPaddle);
var newx:SXFloat;
    direction,ydifference,angularspeed:SXFloat;
    paddledirection:SXFloat;
    invertplayer2paddle: SXInt;
begin
  newx:=paddle.settings.pos.x;
  ydifference:=(settings.pos.y-paddle.settings.pos.y)*getHitZoneAngleDeviation;

  // Invert y-difference if the paddles are concave.
  if (app.game.settings.effects.concave > 0) then ydifference := -ydifference;

  if (paddle.paddle.belongtoplayer = 1) then
  begin
    paddledirection := 0;
    direction:= 90 - ydifference;
    newx := newx + 6;
    invertplayer2paddle := 1;
  end else
  begin
    paddledirection := 180;
    direction:= 270 + ydifference;
    newx := newx - 6;
    invertplayer2paddle := -1;
  end;

  ball.lasthitplayer:=paddle.paddle.belongtoplayer;
  settings.pos.x := newx;
  reflectAlongNormal(direction);

  generateImpact(paddle.settings.pos,paddledirection, SX_EIMPACT_MODE_PADDLE);

  angularspeed := paddle.getMovementSpeed * getTopSpinIntensity * invertplayer2paddle;
  setAngularVelocity(ball.angularspeed + angularspeed);
  clampToAngle(getBallSteepestAngleHitPaddle);
end;


procedure SAEBall.reflectFromPaddle(paddle:SAEPaddle);
var paddlex:SXFloat;
begin
  if (abs(settings.pos.y - paddle.settings.pos.y) < 35) then
  begin
    paddlex := paddle.settings.pos.x;
    if (paddlex < 0) then
    begin
      paddlex := paddlex + 5;
      if (settings.pos.x <= paddlex) and (ball.lastpos.x > paddlex) then ballHitsPaddle(paddle);
    end else
    begin
      paddlex := paddlex - 5;
      if (settings.pos.x >= paddlex) and (ball.lastpos.x < paddlex) then ballHitsPaddle(paddle);
    end;
  end;
end;


procedure SAEBall.reflectFromEntities;
var e:SXInt;
    entity:SAEntity;
begin
  for e:=0 to app.game.entities.getMax do
  begin
    entity:=SAEntity(app.game.entities.getObject(e));
    if (entity is SAEPaddle) then reflectFromPaddle(SAEPaddle(entity));
  end;
end;




procedure SAEBall.clampToAngle(const steepestangle: SXFloat);
begin
  with ball do
  begin
    sx.math.stayInDegrees0to360(rot);
    if (steepestangle <= 0) then exit;

    if (rot < steepestangle) then rot := steepestangle;
    if (rot > 360-steepestangle) then rot := 360 - steepestangle;
    if (rot > 180-steepestangle) and (rot <= 180) then rot := 180 - steepestangle;
    if (rot < 180+steepestangle) and (rot >= 180) then rot := 180 + steepestangle;
  end;
end;


procedure SAEBall.speedUp;
begin
  with ball do
  begin
    // Speed of linear velocity increases with time
    sx.timer.incTimer(speeduptimer);
    if (speeduptimer>=getBallSpeedUpTimer) then
    begin
      speeduptimer:=speeduptimer-getBallSpeedUpTimer;
      speed:=speed+getBallSpeedUpIncrease;
      //app.sound.playSample2D('sounds\speedup');
    end;

    // Decrease angular velocity
    if (angularspeed>0) then
    begin
      sx.timer.decTimer(angularspeed,getBallAngularSpeedDecrease);
      if (angularspeed<0) then angularspeed:=0;
    end;
    if (angularspeed<0) then
    begin
      sx.timer.incTimer(angularspeed,getBallAngularSpeedDecrease);
      if (angularspeed>0) then angularspeed:=0;
    end;
  end;
end;


procedure SAEBall.moveForward;
var vector:SXVertex2D;
    intensity:SXFloat;
begin
  with ball do
  begin
    vector.x:=sx.math._sin(rot);
    vector.y:=sx.math._cos(rot);
    vector:=sx.math.setVectorLength(vector,sx.timer.getTimer(speed/SX_EBALL_ITERATIONS));
    settings.pos:=sx.math.addVertices(settings.pos,vector);

    intensity:=(speed-getBallStartSpeed)*0.002;
    sx.math.clampToRange(intensity,0,1);
    app.scene.blur.setBlurIntensity(intensity);
  end;
end;


procedure SAEBall.processAngularVelocity;
begin
  // Influence the velocity vector
  with ball do
  begin
    sx.timer.incTimer(rot, angularspeed / SX_EBALL_ITERATIONS);
  end;
end;


procedure SAEBall.onTimer;
var i:SXInt;
begin
  speedUp;
  for i:=0 to SX_EBALL_ITERATIONS-1 do
  begin
    ball.lastpos:=getPos;
    processAngularVelocity;
    moveForward;
    collectPowerUps;
    reflectFromCrystals;
    reflectFromBoard;
    reflectFromEntities;
    updateBallSparks;
  end;
  updateTrail;
  checkPlayerGainsScore;

  app.scene.board.setMonkeyRotationSpeed(ball.angularspeed);
end;




procedure SAEBall.initialize;
begin
end;


constructor SAEBall.Create(parent:TObject);
begin
  inherited Create(parent);

  setupAppearance(app.scene.board.settings.ball,5);
  setRotation(90);
  setSpeed(300);
  setAngularVelocity(0);
  ball.speeduptimer:=0;
  ball.lasthitplayer:=SX_NONE;
  ball.sparktimer := 0;
end;


destructor SAEBall.Destroy;
begin
  deleteBallSparks;
 
  inherited;
end;


end.