Koalas in Space

by James Hall: L6 Age ~17

Introduction

In this complex game James uses music, sounds, a true-type font and input by both keyboard and mouse. The player (koala) and the three kinds of enemy have several properties that change during the game. The player's properties include health, level, bullet power, bullet rate, pulse power, pulse rate, chi and stamina. Use the wasd keys to navigate, a click of the left mouse button to fire a directed bullet and the space bar to fire multiple bullets radiating from the koala. The reviewer often saw the Game Over screen at an early stage and resorted to increasing the delay (by editing the code at the end of the program)! You may be able to work out from the code how to play the game well; comments are sparse but the identifiers are generally meaningful. The game is not bug-free but it is fun to play.

The following screenshot shows the program in action.

Program in action

Program in action

The Stats menu appears at intervals:

Statistics

Statistics

Download here a zip file containing the main code (koalainspace.txt), the arial true-type font, a folder of images and another folder of sound files. You should unzip the files so that the image and sound folders are in the program folder. In order to run the program you will need to use SDL files and additional libraries. (See Getting Started with SDL for download details of SDL.dll and SDL.pas).

James supplied us his program folder, which contained among its files the libraries SDL.dll, SDL_mixer.dll, SDL_ttf.dll, libfreetype-6.dll, smpeg.dll and zlib1.dll. You can put these files in the program folder or with thousands of other libraries in your system folder if they are not already in your system. See the introduction to SpaceShooter for individual download details and/or download several at once in a zip file of libraries that are required by the Elysian Pascal game development framework.

The Program

program KoalasInSpace;
{
    Copyright (c) 2011 James Hall

    Licensed under the Apache License, Version 2.0 (the "License"); you may not
    use this file except in compliance with the License, as described at
    http://www.apache.org/licenses/ and http://www.pp4s.co.uk/licenses/
}
  {$Apptype GUI}
  {$r+}
uses
  SDL, math, SDL_mixer, sdl_ttf, strutils, classes, Sysutils;

type
  Tinput = record
    left, right, up, down, space, enter, mousedown, mouseup : boolean;
  end;

  Tenemy = record
    live, dying : boolean;
    etype, health, power, diecounter, exp : integer;
    x, y, speed, angle : real;
    imrect, rect : TSDL_Rect;
  end;

  Tbullet = record
    live : boolean;
    x, y, speed, angle : real;
    power : integer;
    btype : integer;
    rect : TSDL_Rect;
  end;

  Tplayer = record
    live, hit : boolean;
    x, y, speed, angle : real;
    health, healthmax, level, bulletpower, bulletrate, pulsepower, pulserate, ppower, chi, stamina, upoints, exp: integer;
    rect : TSDL_Rect;
  end;

  Tbar = record
    imrectr, imrectg, rect : TSDL_Rect;
  end;

var
  Display, Image, BackgroundImage, bulletimage, enemyimage, barimage, introimage, gameoverimage, deadenemyimage, pausemenuimage, arrowimage, fontface, updatemenuimage, plusminusimage : PSDL_Surface;
  Filename : PChar = 'images/koala.bmp';
  Filename2 : PChar = 'images/background.bmp';
  Filename3 : PChar = 'images/bullet.bmp';
  Filename4 : PChar = 'images/enemies.bmp';
  Filename5 : PChar = 'sounds/wookie.wav';
  Filename6 : PChar = 'sounds/mariogalaxy2.mp3';
  Filename7 : PChar = 'sounds/pig.wav';
  Filename8 : PChar = 'sounds/laugh.wav';
  Filename9 : PChar = 'images/bar.bmp';
  Filename10 : PChar = 'images/intro.bmp';
  Filename11 : PChar = 'images/gameover.bmp';
  Filename12 : PChar = 'sounds/gameover.mp3';
  Filename13 : PChar = 'images/enemiesdead.bmp';
  Filename14 : PChar = 'images/pausemenu.bmp';
  Filename15 : PChar = 'images/arrow.bmp';
  Filename16 : PChar = 'images/updatemenu.bmp';
  Filename17 : PChar = 'images/plusminus.bmp';
  Fmt : PSDL_PixelFormat;
  WhiteKey : UInt32;
  MyEvent : TSDL_Event;
  gameinput : Tinput;
  player1 : Tplayer;
  mousex, mousey, numbullet, i, j, k, numenemy, ecounter, numwaves, currwave, currenemy : Uint32;
  bulletcounter, pulsecounter, bulletwait, pulsewait : Uint32;
  bullets : array[1 .. 2000] of Tbullet;
  xspeed, yspeed : real;
  enemies : array[1 .. 100] of Tenemy;
  enemyhits : array[1 .. 100] of boolean;
  enemytemplates : array[1 .. 20] of Tenemy;
  eschedule : array[1 .. 20, 1 .. 100, 1 .. 2] of integer; //enemy schedule
  screen : array[-100 .. 889, -100 .. 632] of integer;
  bars : array[1 .. 3] of Tbar;
  music : pMIX_MUSIC = nil;
  gameovermusic : pMIX_MUSIC = nil;
  swookie : pMIX_CHUNK = nil;
  spig : pMIX_CHUNK = nil;
  slaugh : pMIX_CHUNK = nil;
  soundchannel : integer;
  pause : boolean;
  fontcolour1, fontcolour2 : psdl_color;
  loadedfont : pointer;
  bulletimg : array[1 .. 3] of tsdl_rect;

const
  maxbull = 2000;
  scrx = 789;
  scry = 532;
  AUDIO_FREQUENCY : INTEGER = 22050;
  AUDIO_FORMAT : WORD = AUDIO_S16;
  AUDIO_CHANNELS : INTEGER = 2;
  AUDIO_CHUNKSIZE : INTEGER = 4096;

procedure closeall;
begin
  SDL_ShowCursor(SDL_ENABLE);
  SDL_Freesurface(Image);
  SDL_Freesurface(BackgroundImage);
  SDL_Freesurface(EnemyImage);
  SDL_Freesurface(BulletImage);
  SDL_Freesurface(barImage);
  SDL_Freesurface(introImage);
  SDL_Freesurface(gameoverImage);
  SDL_Freesurface(deadenemyimage);
  SDL_Freesurface(pausemenuimage);
  SDL_Freesurface(arrowimage);
  SDL_Freesurface(updatemenuimage);
  SDL_Freesurface(plusminusimage);
  SDL_Freesurface(fontface);
  MIX_HALTMUSIC;
  MIX_HALTCHANNEL(soundchannel);
  MIX_FREEMUSIC(music);
  MIX_FREEMUSIC(gameovermusic);
  MIX_FREECHUNK(swookie);
  MIX_FREECHUNK(spig);
  MIX_FREECHUNK(slaugh);
  MIX_CLOSEAUDIO;
  DISPOSE(fontcolour1);
  DISPOSE(fontcolour2);
  TTF_CLOSEFONT(loadedfont);
  TTF_QUIT;
  SDL_Quit;
  halt;
end;

procedure checkinput;
begin
  while SDL_PollEvent(@MyEvent) > 0 do
    begin
      if MyEvent.type_ = SDL_mousemotion then
        begin
          mousex := Myevent.motion.x;
          mousey := Myevent.motion.y;
        end;
      if MyEvent.type_ = SDL_mousebuttondown then
        begin
          gameinput.mousedown := true;
        end;
      if MyEvent.type_ = SDL_mousebuttonup then
        begin
          gameinput.mousedown := false;
        end;
      if MyEvent.type_ = SDL_KeyDown then //Trailing underscore because type is a keyword
        begin
          case MyEvent.key.keysym.sym of
            SDLK_ESCAPE : begin
                            closeall;
                          end;
            SDLK_a      : begin
                            gameinput.left := true;
                          end;
            SDLK_w      : begin
                            gameinput.up := true;
                          end;
            SDLK_s      : begin
                            gameinput.down := true;
                          end;
            SDLK_d      : begin
                            gameinput.right := true;
                          end;
            SDLK_SPACE  : begin
                            gameinput.space := true;
                          end;
            SDLK_return : begin
                            gameinput.enter := true;
                          end;
          end;
        end;
      if MyEvent.type_ = SDL_KeyUp then
        begin
          case MyEvent.key.keysym.sym of

            SDLK_a      : begin
                            gameinput.left := false;
                          end;
            SDLK_w      : begin
                            gameinput.up := false;
                          end;
            SDLK_s      : begin
                            gameinput.down := false;
                          end;
            SDLK_d      : begin
                            gameinput.right := false;
                          end;
            SDLK_SPACE  : begin
                            gameinput.space := false;
                          end;
            SDLK_return : begin
                            gameinput.enter := false;
                          end;
          end;
        end;
    end;
end;

procedure puttext(text : pchar; xx, yy : integer);
var
  fontrect : tsdl_rect;
begin
  fontface := TTF_RENDERTEXT_SOLID(loadedfont, text, fontcolour1^);
  with fontrect do
    begin
      w := fontface^.w;
      h := fontface^.h;
      x := xx - w div 2;
      y := yy - h div 2;
    end;
  SDL_BLITSURFACE(fontface, nil, display, @fontrect);
end;

procedure putim(rect : tsdl_rect; xx, yy : integer);
var
  imrect : tsdl_rect;
begin
  with imrect do
    begin
      x := xx;
      y := yy;
      w := 15;
      h := 15;
    end;
  SDL_BlitSurface(plusminusimage, @rect, Display, @imrect);
end;

procedure updatemenu;
var
  plusim, minusim, pauserect : tsdl_rect;
  maxpoints, maxlevel, m, n, lowppower, lowchi, lowstamina  : integer;
  uscreen : array[1 .. 100, 1 .. 100] of integer;
begin
  maxlevel := 10;
  maxpoints := player1.upoints;
  lowppower := player1.ppower;
  lowchi := player1.chi;
  lowstamina := player1.stamina;
  with pauserect do
    begin
      x := 250;
      y := 75;
      w := pausemenuimage^.w;
      h := pausemenuimage^.h;
    end;
  with plusim do
    begin
      x := 0;
      y := 0;
      w := 15;
      h := 15;
    end;
  with minusim do
    begin
      x := 16;
      y := 0;
      w := 15;
      h := 15;
    end;
  //font bits
  new(fontcolour1);
  new(fontcolour2);
  fontcolour1^.r := 255;
  fontcolour1^.g := 50;
  fontcolour1^.b := 50;
  fontcolour2^.r := 255;
  fontcolour2^.g := 255;
  fontcolour2^.b := 255;
  //initialise screeninput
  for m := 1 to 100 do
    for n := 1 to 100 do
      uscreen[m, n] := 0;
  for m := 440 to 465 do
    for n := 302 to 317 do
      uscreen[m - 439, n - 301] := 1;
  for m := 440 to 465 do
    for n := 320 to 335 do
      uscreen[m - 439, n - 301] := 2;
  for m := 440 to 465 do
    for n := 338 to 353 do
      uscreen[m - 439, n - 301] := 3;
  for m := 460 to 485 do
    for n := 302 to 317 do
      uscreen[m - 439, n - 301] := 4;
  for m := 460 to 485 do
    for n := 320 to 335 do
      uscreen[m - 439, n - 301] := 5;
  for m := 460 to 485 do
    for n := 338 to 353 do
      uscreen[m - 439, n - 301] := 6;
  repeat
    checkinput;
    SDL_BlitSurface(updatemenuimage, nil, Display, @pauserect);
    //do the text
    loadedfont := TTF_OPENFONT('arial.ttf', 18);
    puttext('Stats', 385, 149);
    loadedfont := TTF_OPENFONT('arial.ttf', 14);
    puttext('Bullet power:', 379, 185);
    puttext(' Bullet rate:', 382, 203);
    puttext(' Pulse power:', 378, 221);       //18 separation per line
    puttext('  Pulse rate:', 382, 239);
    puttext(pchar(inttostr(player1.bulletpower)), 430, 185);
    puttext(pchar(inttostr(player1.bulletrate)), 430, 203);
    puttext(pchar(inttostr(player1.pulsepower)), 430, 221);
    puttext(pchar(inttostr(player1.pulserate)), 430, 239);
    puttext('Press Space to confirm', 391, 419);
    puttext('Upgrade Points:', 382, 370);
    puttext(pchar(inttostr(player1.upoints)), 444, 371);
    loadedfont := TTF_OPENFONT('arial.ttf', 18);
    puttext('Power level: ', 364, 310);
    puttext('Chi level: ', 374, 328);
    puttext('Stamina level: ', 360, 346);
    puttext(pchar(inttostr(player1.ppower)), 426, 311);
    puttext(pchar(inttostr(player1.chi)), 426, 329);
    puttext(pchar(inttostr(player1.stamina)), 426, 347);
    putim(minusim, 440, 302);
    putim(minusim, 440, 320);
    putim(minusim, 440, 338);
    putim(plusim, 460, 302);
    putim(plusim, 460, 320);
    putim(plusim, 460, 338);
    if gameinput.mousedown = true then
      begin
        if (mousex > 439) and (mousex < 538) and (mousey > 301) and  (mousey < 381) then
          begin
            case uscreen[mousex - 439, mousey - 301] of
              1 : begin
                    if player1.ppower > lowppower then
                      begin
                        dec(player1.ppower);
                        inc(player1.upoints);
                      end;
                  end;
              2 : begin
                    if player1.chi > lowchi then
                      begin
                        dec(player1.chi);
                        inc(player1.upoints);
                      end;
                  end;
              3 : begin
                    if player1.stamina > lowstamina then
                      begin
                        dec(player1.stamina);
                        inc(player1.upoints);
                      end;
                  end;
              4 : begin
                    if player1.upoints > 0 then
                      if player1.ppower < maxlevel then
                        begin
                          inc(player1.ppower);
                          dec(player1.upoints);
                        end;
                  end;
              5 : begin
                    if player1.upoints > 0 then
                      if player1.chi < maxlevel then
                        begin
                          inc(player1.chi);
                          dec(player1.upoints);
                        end;
                  end;
              6 : begin
                    if player1.upoints > 0 then
                      if player1.stamina < maxlevel then
                        begin
                          inc(player1.stamina);
                          dec(player1.upoints);
                        end;
                  end;
            end;
          end;
        gameinput.mousedown := false;
        player1.bulletpower := 3 * player1.ppower;
        player1.bulletrate := player1.ppower;
        player1.pulsepower := 2 * player1.chi;
        player1.pulserate := player1.chi;
        bulletcounter := 0;
        pulsecounter := 0;
        bulletwait := 40 div player1.bulletrate;
        pulsewait := 90 div player1.pulserate;
      end;
    sdl_flip(display);
    sdl_delay(30);
  until gameinput.space = true;
  gameinput.space := false;
end;

Procedure pausemenu;
var
//screen : array[1..789,1..532] of integer;
  choice, maxchoice : integer;
  pauserect : tsdl_rect;
  arrowrects : array[1 .. 3] of tsdl_rect;
  bup, bdown, endpause : boolean;
begin
  choice := 1;
  maxchoice := 3;
  bup := false;
  bdown := false;
  endpause := false;
  with pauserect do
    begin
      x := 250;
      y := 75;
      w := pausemenuimage^.w;
      h := pausemenuimage^.h;
    end;
  with arrowrects[1] do
    begin
      x := 40 + pauserect.x;
      y := 118 + pauserect.y;
      w := arrowimage^.w;
      h := arrowimage^.h;
    end;
  with arrowrects[2] do
    begin
    x := 46 + pauserect.x;
    y := 181 + pauserect.y;
    w := arrowrects[1].w;
    h := arrowrects[1].h;
    end;
  with arrowrects[3] do
    begin
      x := 71 + pauserect.x;
      y := 254 + pauserect.y;
      w := arrowrects[1].w;
      h := arrowrects[1].h;
    end;

  repeat
    checkinput;
    if gameinput.space = true then
      begin
        case choice of
          1 :  endpause := true;
          2 :  begin
                 gameinput.space := false;
                 updatemenu;
               end;
          3 : closeall;
        end;
      end;
    if gameinput.up = true then
      bup := true;
    if gameinput.down = true then
      bdown := true;
    if bup = true then
      if gameinput.up = false then
        begin
          if choice > 1 then
            dec(choice);
          bup := false;
        end;
    if bdown = true then
      if gameinput.down = false then
        begin
          if choice < maxchoice then
            inc(choice);
          bdown := false;
        end;
    SDL_BlitSurface(pausemenuimage, nil, Display, @pauserect);
    SDL_BlitSurface(arrowimage, nil, Display, @arrowrects[choice]);
    sdl_flip(display);
    sdl_delay(30);
  until endpause = true;
  gameinput.space := false;
end;

function modang(ang : real) : real;
begin
  if ang >= 2 * pi then
    result := ang - 2 * pi
  else if ang < 0 then
    result := ang + 2 * pi
  else result := ang;
end;

function angfind(x, y : real) : real;
begin
  if y > 0 then
    result := arctan(x / y)
  else if y < 0 then
    result := arctan(x / y) + pi
  else if x >= 0 then
    result := pi / 2
  else result := 3 * pi / 2;
end;

begin
  //initialise bars
  with bars[1] do
    begin
      imrectg.x := 0;
      imrectg.y := 0;
      imrectg.w := 66;
      imrectg.h := 18;
      imrectr.x := 0;
      imrectr.y := 19;
      imrectr.w := 66;
      imrectr.h := 18;
      rect.x := 700;
      rect.y := 10;
      rect.w := 66;
      rect.h := 18;
    end;
  bars[2] := bars[1];
  bars[2].rect.y := 40;
  bars[3] := bars[1];
  bars[3].rect.x := 10;

  pause := false;
  for i := 1 to maxbull do
    bullets[i].live := false;
  numbullet := 0;
  numenemy := 0;
  Image := SDL_LoadBMP(Filename);
  BackgroundImage := SDL_LoadBMP(Filename2);
  bulletimage := SDL_LoadBMP(Filename3);
  enemyimage := SDL_LoadBMP(Filename4);
  barimage := SDL_LoadBMP(Filename9);
  introimage := SDL_LoadBMP(Filename10);
  gameoverimage := SDL_LoadBMP(Filename11);
  deadenemyimage := SDL_LoadBMP(Filename13);
  pausemenuimage := SDL_LoadBMP(Filename14);
  arrowimage := SDL_LoadBMP(Filename15);
  updatemenuimage := SDL_LoadBMP(Filename16);
  plusminusimage := SDL_LoadBMP(Filename17);
  //initialise bullets
  with bulletimg[1] do
    begin
       x := 0;
       y := 0;
       w := 9;
       h := 9;
    end;
  with bulletimg[2] do
    begin
      x := 11;
      y := 0;
      w := 9;
      h := 9;
    end;
  //initialise player
  with player1 do
    begin
      live := true;
      hit := false;
      level := 1;
      exp := 0;
      x := 400;
      y := 300;
      speed := 0;
      angle := 0;
      health := 100;
      healthmax := health;
      bulletpower := 1;
      bulletrate := 1;
      pulsepower := 1;
      pulserate := 1;
      ppower := 1;
      chi := 1;
      stamina := 1;
      upoints := 20;
      Rect.w := Image^.w;
      Rect.h := Image^.h;
      Rect.x := round(x) - Rect.w div 2;
      Rect.y := round(y) - Rect.h div 2;
    end;
  //initialise video
  SDL_Init(SDL_INIT_VIDEO);
  Display := SDL_SetVideoMode(BackgroundImage^.w, BackgroundImage^.h, 24, SDL_HWSURFACE or SDL_DOUBLEBUF);
  SDL_WM_SetCaption('Koalas in Space', nil);
  SDL_ShowCursor(SDL_ENABLE);
  //Initialise audio
  SDL_Init(SDL_INIT_AUDIO);
  if MIX_OPENAUDIO(AUDIO_FREQUENCY, AUDIO_FORMAT, AUDIO_CHANNELS, AUDIO_CHUNKSIZE) <> 0 then
    HALT;
  music := MIX_LOADMUS(filename6);
  MIX_VOLUMEMUSIC(20);
  gameovermusic := MIX_LOADMUS(filename12);
  MIX_VOLUMEMUSIC(20);
  swookie := MIX_LOADWAV(filename5);
  MIX_VOLUMECHUNK(swookie, 10);
  spig := MIX_LOADWAV(filename7);
  MIX_VOLUMECHUNK(spig, 10);
  slaugh := MIX_LOADWAV(filename8);
  MIX_VOLUMECHUNK(slaugh, 10);
  //initialise text
  if TTF_INIT = -1 then
    HALT;
  loadedfont := TTF_OPENFONT('arial.ttf', 40);
  //Calculate WhiteKey, needed to set transparent colour
  Fmt := Image^.format;
  WhiteKey:= SDL_MapRGB(Fmt, 255, 255, 255);
  //Set transparent colour
  SDL_SetColorKey(Image, SDL_SRCCOLORKEY or SDL_RLEACCEL, WhiteKey);
  SDL_SetColorKey(bulletimage, SDL_SRCCOLORKEY or SDL_RLEACCEL, WhiteKey);
  SDL_SetColorKey(enemyimage, SDL_SRCCOLORKEY or SDL_RLEACCEL, WhiteKey);
  SDL_SetColorKey(deadenemyimage, SDL_SRCCOLORKEY or SDL_RLEACCEL, WhiteKey);
  SDL_SetColorKey(pausemenuimage, SDL_SRCCOLORKEY or SDL_RLEACCEL, WhiteKey);
  SDL_SetColorKey(arrowimage, SDL_SRCCOLORKEY or SDL_RLEACCEL, WhiteKey);
  SDL_SetColorKey(updatemenuimage, SDL_SRCCOLORKEY or SDL_RLEACCEL, WhiteKey);
  //initialise enemy schedule
  numwaves := 2;
  currwave := 1;
  ecounter := 0;
  currenemy := 1;
  eschedule[1, 1, 1] := 1;
  eschedule[1, 1, 2] := 20;
  eschedule[1, 2, 1] := 1;
  eschedule[1, 2, 2] := 50;
  eschedule[1, 3, 1] := 1;
  eschedule[1, 3, 2] := 50;
  eschedule[1, 4, 1] := 2;
  eschedule[1, 4, 2] := 50;
  eschedule[1, 5, 1] := 0;
  eschedule[1, 5, 2] := 0;

  eschedule[2, 1, 1] := 1;
  eschedule[2, 1, 2] := 500;
  eschedule[2, 2, 1] := 1;
  eschedule[2, 2, 2] := 100;
  eschedule[2, 3, 1] := 2;
  eschedule[2, 3, 2] := 100;
  eschedule[2, 4, 1] := 3;
  eschedule[2, 4, 2] := 300;
  eschedule[2, 5, 1] := 0;
  eschedule[2, 5, 2] := 0;
  //initialise enemies
  with enemytemplates[1] do
    begin
     live := true;
     dying := false;
     etype := 1;
     health := 10;
     diecounter := 20;
     exp := 10;
     x := 400;
     y := 40;
     speed := 4;
     angle := 0;
     power := 1;
     imrect.x := 0;
     imrect.y := 0;
     imrect.w := 22;
     imrect.h := 23;
     rect.w := 22;
     rect.h := 23;
     rect.x := round(x) - rect.w div 2;
     rect.y := round(y) - rect.h div 2;
    end;
  with enemytemplates[2] do
    begin
      live := true;
      dying := false;
      etype := 2;
      health := 20;
      diecounter := 20;
      exp := 20;
      x := 400;
      y := 40;
      speed := 2;
      angle := 0;
      power := 3;
      imrect.x := 24;
      imrect.y := 0;
      imrect.w := 26;
      imrect.h := 23;
      rect.w := 26;
      rect.h := 23;
      rect.x := round(x) - rect.w div 2;
      rect.y := round(y) - rect.h div 2;
    end;
  with enemytemplates[3] do
    begin
      live := true;
      dying := false;
      etype := 3;
      health := 50;
      diecounter := 25;
      exp := 50;
      x := 400;
      y := 40;
      speed := 2;
      angle := 0;
      power := 10;
      imrect.x := 52;
      imrect.y := 0;
      imrect.w := 55;
      imrect.h := 68;
      rect.w := 55;
      rect.h := 68;
      rect.x := round(x) - rect.w div 2;
      rect.y := round(y) - rect.h div 2;
    end;
  randomize;
  bulletcounter := 0;
  pulsecounter := 0;
  bulletwait := 14 - 2 * player1.bulletrate;
  pulsewait := 40 - 3 * player1.pulserate;
  //intro
  repeat
    checkinput;
    SDL_BlitSurface(introimage, nil, Display, nil);
    //Update
    SDL_Flip(Display);
    //wait 30 milliseconds
    SDL_Delay(30);
  until gameinput.space = true;

  //main game
  MIX_PLAYMUSIC(music, -1); //-1 = infinite, 0 = once, 1 = twice,...   Starting the music
  repeat
    for i := 1 to scrx do
      for j := 1 to scry do
        screen[i, j] := 0;
    if bulletcounter < bulletwait then
      inc(bulletcounter);
    if pulsecounter < pulsewait then
      inc(pulsecounter);
    //Inputs
    checkinput;
    if pause = true then
      begin
        pausemenu;
        pause := false;
      end;
    if gameinput.mousedown = true then
      begin
        if bulletcounter = bulletwait then
          begin
            if numbullet < maxbull then
              begin
                i := 0;
                repeat
                  inc(i);
                until bullets[i].live = false;
                with bullets[i] do
                  begin
                    live := true;
                    btype := 1;
                    speed := 18;
                    power := player1.bulletpower;
                    x := player1.x + random(19) - 9;
                    y := player1.y + random(19) - 9;
                    Rect.w := bulletImage^.w;
                    Rect.h := bulletImage^.h;
                    Rect.x := round(x) - rect.w div 2;
                    Rect.y := round(y) - rect.h div 2;
                    angle := modang(angfind(mousex - player1.x, mousey - player1.y));
                  end;
               end;
            inc(numbullet);
            bulletcounter := 0;
          end;
      end;
    if gameinput.space = true then
      begin
        if pulsecounter = pulsewait then
          begin
            if numbullet < maxbull then
              begin
                j := 0;
                repeat
                  i := 0;
                  repeat
                    inc(i);
                  until bullets[i].live = false;
                  with bullets[i] do
                    begin
                      live := true;
                      btype := 2;
                      speed := 18;
                      power := player1.pulsepower;
                      x := player1.x;
                      y := player1.y;
                      Rect.w := bulletImage^.w;
                      Rect.h := bulletImage^.h;
                      Rect.x := round(x) - rect.w div 2;
                      Rect.y := round(y) - rect.h div 2;
                      angle := j * pi / 18;
                    end;
                  inc(numbullet);
                  inc(j)
                until (j >= 36) or (i >= maxbull);
              end;
            pulsecounter := 0;
        end;
      end;

    if gameinput.left = true then
      begin
        with player1 do
          begin
            xspeed := speed * sin(angle) - 1 / (power(speed + 0.5, 0.5));
            yspeed := speed * cos(angle);
            speed := power(xspeed * xspeed + yspeed * yspeed, 0.5);
            angle := modang(angfind(xspeed, yspeed));
          end;
      end;
    if gameinput.right = true then
      begin
        with player1 do
          begin
            xspeed := speed * sin(angle) + 1 / (power(speed + 0.5, 0.5));
            yspeed := speed * cos(angle);
            speed := power(xspeed * xspeed + yspeed * yspeed, 0.5);
            angle := modang(angfind(xspeed, yspeed));
          end;
      end;
    if gameinput.up = true then
      begin
        with player1 do
          begin
            xspeed := speed * sin(angle);
            yspeed := speed * cos(angle) - 1 / (power(speed + 0.5, 0.5));
            speed := power(xspeed * xspeed + yspeed * yspeed, 0.5);
            angle := modang(angfind(xspeed, yspeed));
          end;
      end;
    if gameinput.down = true then
      begin
        with player1 do
          begin
            xspeed := speed * sin(angle);
            yspeed := speed * cos(angle) + 1 / (power(speed + 0.5, 0.5));
            speed := power(xspeed * xspeed + yspeed * yspeed, 0.5);
            angle := modang(angfind(xspeed, yspeed));
          end;
      end;
    player1.speed := player1.speed * 0.97;

    //enemy bit
    if currwave <= numwaves then
      begin
        inc(ecounter);
          if ecounter >= eschedule[currwave, currenemy, 2] then
            begin
              enemies[currenemy] := enemytemplates[eschedule[currwave, currenemy, 1]];
              enemies[currenemy].x := 400;
              enemies[currenemy].y := 50;
              enemies[currenemy].rect.x := round(enemies[currenemy].x) - enemies[currenemy].rect.w div 2;
              enemies[currenemy].rect.y := round(enemies[currenemy].y) - enemies[currenemy].rect.h div 2;
              inc(currenemy);
              inc(numenemy);
              ecounter := 0;
              if eschedule[currwave, currenemy, 1] = 0 then
                begin
                  inc(currwave);
                  currenemy := 1;
                end;
            end;
      end;

    if numenemy > 0 then
      for i:= 1 to numenemy do
        begin
          with enemies[i] do
            begin
              if live = true then
                begin
                  case etype of
                    1 : begin
                          angle := modang(angfind(player1.x - x, player1.y - y));
                          x := x + speed * sin(angle);
                          y := y + speed * cos(angle);
                          rect.x := round(x) - rect.w div 2;
                          rect.y := round(y) - rect.h div 2;
                          if health <= 0 then
                            begin
                              live := false;
                              dying := true;
                              soundchannel := MIX_PLAYCHANNEL(-1, swookie, 0);
                            end;
                          if health > 0 then
                            for k := rect.x + 100 to rect.x + rect.w + 100 do
                              for j := rect.y + 100 to rect.y + rect.h + 100 do
                                screen[k - 100, j - 100] := i;
                        end;
                    2 : begin
                          angle := modang(angfind(player1.x - x, player1.y - y));
                          x := x + speed * sin(angle);
                          y := y + speed * cos(angle);
                          rect.x := round(x) - rect.w div 2;
                          rect.y := round(y) - rect.h div 2;
                          if health <= 0 then
                            begin
                              live := false;
                              dying := true;
                              soundchannel := MIX_PLAYCHANNEL(-1, spig, 0);
                            end;
                          if health > 0 then
                            for k := rect.x + 100 to rect.x + rect.w + 100 do
                              for j := rect.y + 100 to rect.y + rect.h + 100 do
                                screen[k - 100, j - 100] := i;
                        end;
                    3 : begin
                          angle := modang(angfind(player1.x - x, player1.y - y));
                          x := x + speed * sin(angle);
                          y := y + speed * cos(angle);
                          rect.x := round(x) - rect.w div 2;
                          rect.y := round(y) - rect.h div 2;
                          if health <= 0 then
                            begin
                              live := false;
                              dying := true;
                              soundchannel := MIX_PLAYCHANNEL(-1, slaugh, 0);
                            end;
                          if health > 0 then
                            for k := rect.x to rect.x + rect.w do
                              for j := rect.y to rect.y + rect.h do
                                screen[k, j] := i;
                        end;
                  end;
                end;
            end;
        end;
    //move bullet bit
    for i := 1 to maxbull do
      begin
        if bullets[i].live = true then
          begin
            with bullets[i] do
              begin
                x := x + speed * sin(angle);
                y := y + speed * cos(angle);
                rect.x := round(x) - rect.w div 2;
                rect.y := round(y) - rect.h div 2;
                if (rect.x >= 1) and (rect.x+rect.w <= 789) and (rect.y >= 1) and (rect.y + rect.h <= 532) then
                  begin
                    for k := rect.x to rect.x + rect.w do
                      for j := rect.y to rect.y+rect.h do
                        if live = true then
                          if screen[k, j] <> 0 then
                            begin
                              enemies[screen[k, j]].health := enemies[screen[k, j]].health - power;
                              live := false;
                            end;
                  end;
                if (x > 800) or (x < -100) or (y > 650) or (y < -100) then
                  begin
                    live := false;
                    dec(numbullet);
                  end;
              end;
          end;
      end;
    with player1 do
      begin
        y := y + speed * cos(angle);
        y := y + speed * cos(angle);
        x := x + speed * sin(angle);
        rect.x := round(x) - rect.w div 2;
        rect.y := round(y) - rect.h div 2;
        for i := 1 to numenemy do
          enemyhits[i] := false;
        if (rect.x >= 1) and (rect.x + rect.w <= 789) and (rect.y >= 1) and (rect.y + rect.h <= 532) then
          begin
            for k := rect.x to rect.x + rect.w do
              for j := rect.y to rect.y + rect.h do
                if screen[k, j] <> 0 then
                  if enemyhits[screen[k, j]] = false then
                    begin
                      with enemies[screen[k, j]] do
                        begin
                          angle := modang(angle + pi);
                          x := x + 10 * sin(angle);
                          y := y + 10 * cos(angle);
                        end;
                      health := health - enemies[screen[k, j]].power;
                      enemyhits[screen[k, j]] := true;
                      if health < 0 then
                        begin
                          live := false;
                          health := 0;
                          SDL_BlitSurface(gameoverimage, nil, Display, nil);
                          //Update
                          SDL_Flip(Display);
                          mix_haltmusic;
                          mix_playmusic(gameovermusic, -1); //Starting the ending music
                          //wait 6000 milliseconds
                          SDL_Delay(6000);
                          closeall;
                        end;
                    end;
          end;
      end;

    //copy background
    SDL_BlitSurface(BackgroundImage, nil, Display, nil);
    //put enemies
    for i := 1 to numenemy do
      begin
        if enemies[i].live then
          SDL_BlitSurface(enemyimage, @enemies[i].imrect, Display, @enemies[i].Rect);
        if enemies[i].dying then
          begin
            SDL_BlitSurface(deadenemyimage, @enemies[i].imrect, Display, @enemies[i].Rect);
            dec(enemies[i].diecounter);
            if enemies[i].diecounter < 1 then
              begin
                enemies[i].dying := false;
                player1.exp := player1.exp + enemies[i].exp;
                while player1.exp >= round(30 * power(1.2, player1.level)) do
                  begin
                    player1.exp := player1.exp - round(30 * power(1.2, player1.level));
                    inc(player1.level);
                    player1.upoints := player1.upoints + 3;
                    gameinput.space := false;
                    if player1.exp < round(30 * power(1 + player1.level / 10, player1.level)) then
                      updatemenu;
                  end;
              end;
          end;
      end;
    //put live bullets
    for i := 1 to maxbull do
      if bullets[i].live = true then
        SDL_BlitSurface(bulletimage, @bulletimg[bullets[i].btype], Display, @bullets[i].Rect);
    //copy sprite to Display
    SDL_BlitSurface(Image, nil, Display, @player1.Rect);
    //copy bars
    with bars[1] do
      begin
        imrectg.w := round((bulletcounter / bulletwait) * 66);
        SDL_BlitSurface(barimage, @imrectr, Display, @Rect);
        SDL_BlitSurface(barimage, @imrectg, Display, @Rect);
      end;
    with bars[2] do
      begin
        imrectg.w := round((pulsecounter / pulsewait) * 66);
        SDL_BlitSurface(barimage, @imrectr, Display, @Rect);
        SDL_BlitSurface(barimage, @imrectg, Display, @Rect);
      end;
    with bars[3] do
      begin
        imrectg.w := round((player1.health / player1.healthmax) * 66);
        SDL_BlitSurface(barimage, @imrectr, Display, @Rect);
        SDL_BlitSurface(barimage, @imrectg, Display, @Rect);
      end;
    //Update
    SDL_Flip(Display);
    //wait 30 milliseconds
    SDL_Delay(30);
  until 1 = 2;
end.

Remarks

Could you write your own game using SDL?

Programming - a skill for life!

Seven programs including GameOfLife, PixelSort and SuperHappyFunLand by James Hall