Subversion Repositories AndroidProjects

Rev

Blame | Last modification | View Log | RSS feed

unit app_game_main;

interface

uses sux_constant, sux_object, app_constant,
     app_game_player, resource_list, app_entity_main, app_game_highscore,
     app_particle_main, Bass;

type
  TSState = record
    state: SXState;
    timer: SXFloat;
    nextballdirection: SXFloat;
    soundtimer: SXFloat;
    nextpoweruptimer: SXFloat;
    paused: SXBool;
  end;
  TSMenu = record
    selection: SXInt;
    joypadrepresstimer: SXFloat;
  end;
  TSHelpScreen = record
    comefromstate: SXState;
  end;
  TSHighScore = record
    name: SXStringL;
    cursorpos: SXInt;
    playerindex: SXInt;
    score: SXInt;
  end;
  TSEffects = record
    pixel: SXFloat;
    flowers: SXFloat;
    slowmotion: SXFloat;
    createcrystals: SXInt;
    concave: SXFloat;
  end;
  TSMusic = record
    timer: SXFloat;
    length: SXFloat;
  end;
  TSettings = record
    player1, player2: SAGPlayer;
    game: SXState;
    state: TSState;
    menu: TSMenu;
    helpscreen: TSHelpScreen;
    highscore: TSHighScore;
    effects: TSEffects;
    music: TSMusic;
  end;
  SAGame=class(SUXObject)
    settings: TSettings;
    entities: SRList;
    particlesystems: SRList;
    highscore: SAGHighScore;

    procedure setGameState(state: SXState);
    function getGameTimer:SXFloat;

    function isGameInProgress:SXBool;
    function getPlayer1:SAGPlayer;
    function getPlayer2:SAGPlayer;
    function getPlayer(player:SXInt):SAGPlayer;

    function createEntity(typus:SXState):SAEntity;
    function getEntity(index:SXInt):SAEntity;
    procedure deleteEntity(entity:SAEntity);

    function createParticleSystem(const typus: SXState): SAParticle;
    function getParticleSystem(const index: SXInt): SAParticle;
    procedure deleteParticleSystem(const particlesystem: SAParticle);

    procedure restartGame;
    procedure playerGainsScore(player:SXInt;typeofscore:SXState);

    procedure startSingePlayerGame;
    procedure startLocalMultiPlayerGame;
    procedure startNetworkGameAsServer;
    procedure startNetworkGameAsClient;
    procedure endCurrentGame;

    procedure openHelpScreen;
    procedure openHintScreen;
    procedure openEnterHighScore;
    procedure playerEnterHighScore(const playerindex: SXInt; const score: SXInt);
    procedure getKeyPressed(const key: Char);

    procedure onTimerMusic;
    procedure onTimerSpawnPowerUp;
    procedure onTimerEffects;
    procedure onTimerEntities;
    procedure onTimerParticleSystems;
    procedure onTimerPlay;
    procedure onTimerWaitForBall;
    procedure onTimerMenu;
    procedure onTimerHelpScreen;
    procedure onTimerViewHighScore;
    procedure onTimerEnterHighScore;
    procedure onTimer;

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

implementation

uses main, app_entity_paddle, app_entity_ball, app_entity_impact,
     app_entity_trail, app_entity_warp, app_entity_crystal, app_entity_powerup,
     app_particle_ballsparks, app_particle_crystalshards,
     app_particle_powerupappears, app_particle_flowers;


// --- SAGame


procedure SAGame.setGameState(state: SXState);
begin
  if (state = SX_GAME_STATE_HELPSCREEN) or (state = SX_GAME_STATE_HINTSCREEN) then
    settings.helpscreen.comefromstate := settings.state.state;

  settings.state.state:=state;
  settings.state.timer:=0;
  settings.menu.joypadrepresstimer := 0;

  if (state = SX_GAME_STATE_MENU) then app.scene.flash.displayMenuFlash;
end;


function SAGame.getGameTimer:SXFloat;
begin
  result:=settings.state.timer;
end;




function SAGame.isGameInProgress:SXBool;
begin
  result:=(settings.game<>SX_NONE);
end;


function SAGame.getPlayer1:SAGPlayer;
begin
  result:=settings.player1;
end;


function SAGame.getPlayer2:SAGPlayer;
begin
  result:=settings.player2;
end;


function SAGame.getPlayer(player:SXInt):SAGPlayer;
begin
  case player of
    1 : result:=settings.player1;
    2 : result:=settings.player2;
  else
    result:=nil;
  end;
end;




function SAGame.createEntity(typus:SXState):SAEntity;
var entity:SAEntity;
begin
  case typus of
    SX_ENTITY_PADDLE          : entity := SAEPaddle.Create(self);
    SX_ENTITY_BALL            : entity := SAEBall.Create(self);
    SX_ENTITY_IMPACT          : entity := SAEImpact.Create(self);
    SX_ENTITY_TRAIL           : entity := SAETrail.Create(self);
    SX_ENTITY_WARP            : entity := SAEWarp.Create(self);
    SX_ENTITY_CRYSTAL         : entity := SAECrystal.Create(self);
    SX_ENTITY_POWERUP         : entity := SAEPowerUp.Create(self);
  else
    entity:=SAEntity.Create(self);
  end;

  entity.settings.typus:=typus;
  entities.addObject(entity);
  result:=entity;
end;


function SAGame.getEntity(index:SXInt):SAEntity;
begin
  result:=SAEntity(entities.getObject(index));
end;


procedure SAGame.deleteEntity(entity:SAEntity);
begin
  entities.deleteObject(entity);
end;




function SAGame.createParticleSystem(const typus: SXState): SAParticle;
var particle: SAParticle;
begin
  case typus of
    SX_PARTICLESYSTEM_BALLSPARKS          : particle := SAPBallSparks.Create(self);
    SX_PARTICLESYSTEM_CRYSTALSHARDS       : particle := SAPCrystalShards.Create(self);
    SX_PARTICLESYSTEM_POWERUPAPPEARS      : particle := SAPPowerUpAppears.Create(self);
    SX_PARTICLESYSTEM_FLOWERS             : particle := SAPFlowers.Create(self);
  else
    particle := SAParticle.Create(self);
  end;
 
  particle.settings.typus := typus;
  particlesystems.addObject(particle);
  result := particle;
end;


function SAGame.getParticleSystem(const index: SXInt): SAParticle;
begin
  result := SAParticle(particlesystems.getObject(index));
end;


procedure SAGame.deleteParticleSystem(const particlesystem: SAParticle);
begin
  particlesystems.deleteObject(particlesystem);
end;




procedure SAGame.restartGame;
var paddle:SAEPaddle;
begin
  // Delete all previous entities
  entities.deleteAllObjects;
  particlesystems.deleteAllObjects;

  // Create new entities for the players
  paddle:=SAEPaddle(createEntity(SX_ENTITY_PADDLE));
  paddle.connectToPlayer(1);

  paddle:=SAEPaddle(createEntity(SX_ENTITY_PADDLE));
  paddle.connectToPlayer(2);

  // The two players wait for a ball
  setGameState(SX_GAME_STATE_WAITFORBALL);
  settings.state.nextballdirection:=270;

  settings.effects.pixel := 0;
  settings.effects.flowers := 0;
  settings.effects.slowmotion := 0;
  settings.effects.createcrystals := 0;
  settings.effects.concave := 0;
  settings.state.paused := false;
end;


procedure SAGame.playerGainsScore(player:SXInt;typeofscore:SXState);
begin
  getPlayer(player).gainScore;
  setGameState(SX_GAME_STATE_WAITFORBALL);
  app.scene.flash.displayGainScore(typeofscore);
  inc(app.settings.score);

  if (player=1) then settings.state.nextballdirection:=90;
  if (player=2) then settings.state.nextballdirection:=270;
end;




procedure SAGame.startSingePlayerGame;
begin
  endCurrentGame;
  settings.game:=SX_GAME_SINGLEPLAYER;
  settings.player1:=SAGPlayer.Create(self, 1, SX_PLAYER_CONTROL_CPU);
  settings.player2:=SAGPlayer.Create(self, 2, SX_PLAYER_CONTROL_CPU);
  restartGame;

  settings.state.nextpoweruptimer := 0;
end;


procedure SAGame.startLocalMultiPlayerGame;
begin
  endCurrentGame;
  settings.game:=SX_GAME_LOCALMULTIPLAYER;
  settings.player1:=SAGPlayer.Create(self, 1, SX_PLAYER_CONTROL_LOCAL);
  settings.player2:=SAGPlayer.Create(self, 2, SX_PLAYER_CONTROL_LOCAL);
  restartGame;
end;


procedure SAGame.startNetworkGameAsServer;
begin
  endCurrentGame;
  settings.game:=SX_GAME_NETWORK;
  settings.player1:=SAGPlayer.Create(self, 1, SX_PLAYER_CONTROL_LOCAL);
  settings.player2:=SAGPlayer.Create(self, 2, SX_PLAYER_CONTROL_NETWORK);
  restartGame;
end;


procedure SAGame.startNetworkGameAsClient;
begin
  endCurrentGame;
  settings.game:=SX_GAME_NETWORK;
  settings.player1:=SAGPlayer.Create(self, 1, SX_PLAYER_CONTROL_NETWORK);
  settings.player2:=SAGPlayer.Create(self, 2, SX_PLAYER_CONTROL_LOCAL);
  restartGame;
end;


procedure SAGame.endCurrentGame;
begin
  entities.deleteAllObjects;
  particlesystems.deleteAllObjects;
  if (settings.player1<>nil) then settings.player1.Free;
  if (settings.player2<>nil) then settings.player2.Free;
  settings.player1:=nil;
  settings.player2:=nil;

  if (settings.game<>SX_NONE) then settings.game:=SX_NONE;
end;




procedure SAGame.openHelpScreen;
begin
  setGameState(SX_GAME_STATE_HELPSCREEN);
end;

procedure SAGame.openHintScreen;
begin
  setGameState(SX_GAME_STATE_HINTSCREEN);
end;


procedure SAGame.openEnterHighScore;
var position, score: SXInt;
begin
  if (settings.player1 <> nil) then
  if (settings.player1.isHumanPlayer) then
  begin
    score := settings.player1.settings.score;
    position := highscore.getPositionForScore(score);
    settings.player1.Free;
    settings.player1 := nil;

    if (position <> SX_NONE) then
    begin
      playerEnterHighScore(1, score);
      exit;
    end;
  end;
  if (settings.player2 <> nil) then
  if (settings.player2.isHumanPlayer) then
  begin
    score := settings.player2.settings.score;
    position := highscore.getPositionForScore(score);
    settings.player2.Free;
    settings.player2 := nil;

    if (position <> SX_NONE) then
    begin
      playerEnterHighScore(2, score);
      exit;
    end;
  end;

  if (settings.state.state <> SX_GAME_STATE_ENTERHIGHSCORE) then
  begin
    setGameState(SX_GAME_STATE_MENU);
  end else setGameState(SX_GAME_STATE_VIEWHIGHSCORE);
end;


procedure SAGame.playerEnterHighScore(const playerindex: SXInt; const score: SXInt);
begin
  settings.highscore.name := '';
  settings.highscore.cursorpos := 0;
  settings.highscore.playerindex := playerindex;
  settings.highscore.score := score;
  setGameState(SX_GAME_STATE_ENTERHIGHSCORE);
end;


procedure SAGame.getKeyPressed(const key: Char);
begin
  if (settings.state.state = SX_GAME_STATE_ENTERHIGHSCORE) then
  begin
    if (key = #13) then
    begin
      if (settings.highscore.cursorpos = 0) then settings.highscore.name := 'brak';
      highscore.addEntry(settings.highscore.name, settings.highscore.score);
      openEnterHighScore;
      exit;
    end;
    if (key = #27) then
    begin
      setGameState(SX_GAME_STATE_MENU);
      exit;
    end;

    // Backspace
    if (key = #8) then
    begin
      if (settings.highscore.cursorpos > 0) then
      begin
        settings.highscore.name := sx.math.strings.deleteFromString(settings.highscore.name,
          settings.highscore.cursorpos, settings.highscore.cursorpos);
        dec(settings.highscore.cursorpos);
      end;
      exit;
    end;

    // Add letter to name
    if (settings.highscore.cursorpos < 25) then
    begin
      inc(settings.highscore.cursorpos);
      settings.highscore.name := sx.math.strings.insertSubString(
        settings.highscore.name, key, settings.highscore.cursorpos);
    end;
  end;
end;




procedure SAGame.onTimerMusic;
var sam: BASS_SAMPLE;
    backgroundmusic: SXBASSSample;
begin
  if (not app.ini.settings.sound.backgroundmusic) then exit;

  sx.timer.incTimer(settings.music.timer);
  if (settings.music.timer > settings.music.length) then
  begin
    settings.music.timer := 0;

    backgroundmusic := app.sound.getSound('sounds\hydrotopos').sound.index;
    app.sound.playSample2D('sounds\hydrotopos');
    BASS_SampleGetInfo(backgroundmusic, sam);
    sam.volume := 50;
    BASS_SampleSetInfo(backgroundmusic, sam);

    settings.music.length := sam.length / sam.freq;
  end;
end;


procedure SAGame.onTimerSpawnPowerUp;
var e: SXInt;
    powerupspresent: SXInt;
begin
  sx.timer.incTimer(settings.state.nextpoweruptimer);
  if (settings.state.nextpoweruptimer > app.ini.settings.gameplay.poweruptimer) then
  begin
    settings.state.nextpoweruptimer := 0;

    // Not more than 3 power ups may be present at the same time.
    powerupspresent := 0;
    for e := 0 to entities.getMax do
    if (entities.getObject(e) is SAEPowerUp) then inc(powerupspresent);
    if (powerupspresent >= app.ini.settings.gameplay.maximumpowerupspresent) then exit;

    createEntity(SX_ENTITY_POWERUP);
  end;
end;


procedure SAGame.onTimerEffects;
begin
  sx.timer.decTimer(settings.effects.pixel);
  sx.timer.decTimer(settings.effects.flowers);
  sx.timer.decTimer(settings.effects.slowmotion);
  sx.timer.decTimer(settings.effects.concave);
end;


procedure SAGame.onTimerEntities;
var e:SXInt;
begin
  for e := entities.getMax downto 0 do
  if (e <= entities.getMax) then
  begin
    getEntity(e).onTimer;
  end;
end;


procedure SAGame.onTimerParticleSystems;
var p: SXInt;
begin
  for p := particlesystems.getMax downto 0 do
  if (p <= particlesystems.getMax) then
  begin
    getParticleSystem(p).onTimer;
  end;
end;


procedure SAGame.onTimerPlay;
begin
  if (not settings.state.paused) then
  begin
    if (settings.effects.slowmotion > 0) then sx.timer.setGameSpeed(0.5);

    onTimerSpawnPowerUp;
    onTimerEffects;
    onTimerEntities;
    onTimerParticleSystems;
  end;

  if (app.ini.settings.menu.candisplaymenu) then
  begin
    if (app.input.isPressedNow('Pause','Menu')) then settings.state.paused := not settings.state.paused;

    if (app.input.isPressedNow('Quit','Menu')) then
    begin
      if (app.ini.settings.menu.canquit) then settings.menu.selection := SX_MENU_SELECTION_QUIT
        else settings.menu.selection := SX_MENU_SELECTION_STARTGAME;
      openEnterHighScore;
    end;

    // Enter help screen
    if (app.input.isPressedNow('Help','Menu')) then openHelpScreen;
    if (app.input.isPressedNow('Hint','Menu')) then openHintScreen;
  end;
end;


procedure SAGame.onTimerWaitForBall;
var ball:SAEBall;
    warp:SAEWarp;
begin
  if (not settings.state.paused) then
  begin
    if (settings.effects.slowmotion > 0) then sx.timer.setGameSpeed(0.5);
   
    onTimerSpawnPowerUp;
    onTimerEffects;
    onTimerEntities;
    onTimerParticleSystems;

    if (getGameTimer>=2.0) then
    begin
      // Spawn a ball
      setGameState(SX_GAME_STATE_PLAY);
      ball:=createEntity(SX_ENTITY_BALL) as SAEBall;
      ball.setRotation(settings.state.nextballdirection);
      ball.setSpeed(app.ini.settings.gameplay.ballstartspeed);
      app.scene.flash.displayNewBall;

      warp:=createEntity(SX_ENTITY_WARP) as SAEWarp;
      warp.setSize(10,0.8);
      warp:=createEntity(SX_ENTITY_WARP) as SAEWarp;
      warp.setSize(20,0.6);
      warp:=createEntity(SX_ENTITY_WARP) as SAEWarp;
      warp.setSize(30,0.4);
      warp:=createEntity(SX_ENTITY_WARP) as SAEWarp;
      warp.setSize(40,0.2);
    end;
  end;

  if (app.ini.settings.menu.candisplaymenu) then
  begin
    if (app.input.isPressedNow('Pause','Menu')) then settings.state.paused := not settings.state.paused;

    if (app.input.isPressedNow('Quit','Menu')) then
    begin
      if (app.ini.settings.menu.canquit) then settings.menu.selection := SX_MENU_SELECTION_QUIT
        else settings.menu.selection := SX_MENU_SELECTION_STARTGAME;
      openEnterHighScore;
    end;

    // Enter help screen
    if (app.input.isPressedNow('Help','Menu')) then openHelpScreen;
    if (app.input.isPressedNow('Hint', 'Menu')) then openHintScreen;
  end;
end;


procedure SAGame.onTimerMenu;
var pressedup, presseddown, pressedaccept: SXBool;
    movement1, movement2: SXFloat;
    joyindex: SXInt;
begin
  sx.timer.decTimer(settings.menu.joypadrepresstimer);

  if (sx.timer.exceedTimerValue(getGameTimer,0.1)) then app.sound.playSample2D('sounds\menuopen');
  if (getGameTimer < 0.2) then exit;

  // Navigate through the menu
  movement1 := 0;
  movement2 := 0;
  pressedaccept := false;
  pressedup := false;
  presseddown := false;
  if (app.input.isRePressed('Up','Menu'))
  or (app.input.isRePressed('Up','Player1'))
  or (app.input.isRePressed('Up','Player2')) then pressedup := true;
  if (app.input.isRePressed('Down','Menu'))
  or (app.input.isRePressed('Down','Player1'))
  or (app.input.isRePressed('Down','Player2')) then presseddown := true;
  if (app.input.isPressedNow('Accept','Menu')) then pressedaccept := true;

  joyindex := app.ini.settings.player1.joypad - 1;
  if (joyindex > SX_NONE) and (app.input.joysticks.maxjoystick >= joyindex) then
  begin
    movement1 := app.input.getJoystick(joyindex).getMovementY;
    if (settings.menu.joypadrepresstimer <= 0) then
    begin
      if (movement1 < 0) then pressedup := true;
      if (movement1 > 0) then presseddown := true;
      if (movement1 <> 0) then settings.menu.joypadrepresstimer := 0.3;
    end;
    if (app.input.getJoystick(joyindex).getButtonPressed(SX_INPUT_JOYSTICK_BUTTON_0)) then pressedaccept := true;
  end;
  joyindex := app.ini.settings.player2.joypad - 1;
  if (joyindex > SX_NONE) and (app.input.joysticks.maxjoystick >= joyindex) then
  begin
    movement2 := app.input.getJoystick(joyindex).getMovementY;
    if (settings.menu.joypadrepresstimer <= 0) then
    begin
      if (movement2 < 0) then pressedup := true;
      if (movement2 > 0) then presseddown := true;
      if (movement2 <> 0) then settings.menu.joypadrepresstimer := 0.3;
    end;
    if (app.input.getJoystick(joyindex).getButtonPressed(SX_INPUT_JOYSTICK_BUTTON_0)) then pressedaccept := true;
  end;
  if (movement1 = 0) and (movement2 = 0) then settings.menu.joypadrepresstimer := 0;

  if (pressedup) then
  if (settings.menu.selection > 0) then
  begin
    app.sound.playSample2D('sounds\menumove');
    dec(settings.menu.selection);
  end;

  if (presseddown) then
  if (settings.menu.selection < 2) or ((app.ini.settings.menu.canquit) and (settings.menu.selection < 3)) then
  begin
    app.sound.playSample2D('sounds\menumove');
    inc(settings.menu.selection);
  end;

  // Enter help screen
  if (app.input.isPressedNow('Help','Menu')) then openHelpScreen;
  if (app.input.isPressedNow('Hint', 'Menu')) then openHintScreen;

  // Quit game
  if (app.ini.settings.menu.canquit) then
  if (app.input.isPressedNow('Quit','Menu')) then app.settings.close:=true;

  // Select a menu option
  if (pressedaccept) then
  begin
    app.sound.playSample2D('sounds\menuselect');
    case settings.menu.selection of
      SX_MENU_SELECTION_STARTGAME         : startSingePlayerGame;
      SX_MENU_SELECTION_HIGHSCORE         : setGameState(SX_GAME_STATE_VIEWHIGHSCORE);
      SX_MENU_SELECTION_HELP              : openHelpScreen;
      SX_MENU_SELECTION_QUIT              : app.settings.close:=true;
    end;
  end;
end;


procedure SAGame.onTimerHelpScreen;
begin
  if (sx.timer.exceedTimerValue(getGameTimer,0.1)) then app.sound.playSample2D('sounds\menuopen');
  if (getGameTimer < 0.2) then exit;

  // Go back to previous game state
  if (app.input.isPressedNow('Help','Menu')) or (app.input.isPressedNow('Hint','Menu')) then
  begin
    app.sound.playSample2D('sounds\menuselect');
    setGameState(settings.helpscreen.comefromstate);
  end;

  if (app.input.isPressedNow('Accept','Menu'))
  or (app.input.isPressedNow('Quit','Menu')) then
  begin
    app.sound.playSample2D('sounds\menuselect');
    setGameState(settings.helpscreen.comefromstate);
  end;
end;


procedure SAGame.onTimerViewHighScore;
begin
  if (sx.timer.exceedTimerValue(getGameTimer,0.1)) then app.sound.playSample2D('sounds\menuopen');
  if (getGameTimer < 0.2) then exit;

  // Go back to previous game state
  if (app.input.isPressedNow('Accept','Menu'))
  or (app.input.isPressedNow('Quit','Menu')) then
  begin
    app.sound.playSample2D('sounds\menuselect');
    setGameState(SX_GAME_STATE_MENU);
  end;
end;


procedure SAGame.onTimerEnterHighScore;
begin
  if (sx.timer.exceedTimerValue(getGameTimer,0.1)) then app.sound.playSample2D('sounds\menuopen');
end;


procedure SAGame.onTimer;
begin
  sx.timer.setGameSpeed(1);
  if (settings.state.state > SX_GAME_STATE_WAITFORBALL) or (not settings.state.paused) then
  begin
    sx.timer.incTimer(settings.state.timer);
  end;

  case settings.state.state of
    SX_GAME_STATE_PLAY              : onTimerPlay;
    SX_GAME_STATE_WAITFORBALL       : onTimerWaitForBall;
    SX_GAME_STATE_MENU              : onTimerMenu;
    SX_GAME_STATE_HELPSCREEN,
    SX_GAME_STATE_HINTSCREEN        : onTimerHelpScreen;
    SX_GAME_STATE_VIEWHIGHSCORE     : onTimerViewHighScore;
    SX_GAME_STATE_ENTERHIGHSCORE    : onTimerEnterHighScore;
  end;

  sx.timer.setGameSpeed(1);
  onTimerMusic;
end;




procedure SAGame.initialize(const startimmediately: SXBool);
begin
  highscore.initialize;

  if (startimmediately) then
  begin
    startSingePlayerGame;
  end else
  begin
    setGameState(SX_GAME_STATE_MENU);
  end;
end;


constructor SAGame.Create(parent:TObject);
begin
  inherited Create(parent,'Game');

  settings.player1:=nil;
  settings.player2:=nil;
  settings.game:=SX_NONE;
  settings.menu.selection:=0;
  particlesystems := SRList.Create(self, false, true, true);
  entities := SRList.Create(self, false, true, true);
  setGameState(SX_NONE);
  settings.state.soundtimer:=0;
  settings.state.paused := false;

  highscore := SAGHighScore.Create(self);
  settings.music.timer := 0;
  settings.music.length := -1;
end;


destructor SAGame.Destroy;
begin
  endCurrentGame;
  entities.Free;
  particlesystems.Free;
  highscore.Free;

  inherited;
end;


end.