This is the source code to a space shooter inspired by the Space Invaders arcade game of the 1970's. The code is in XT3X.
! Generic Invaders ! By Nils M Holm, 2018 ! In the public domain ! https://creativecommons.org/publicdomain/zero/1.0/ str.length(s) return t.memscan(s, 0, 32767); str.copy(sd, ss) t.memcopy(ss, sd, str.length(ss)+1); str.append(sd, ss) t.memcopy(ss, @sd::str.length(sd), str.length(ss)+1); str.equal(s1, s2) return t.memcomp(s1, s2, str.length(s1)+1) = 0; writes(s) t.write(1, s, str.length(s)); var ntoa_buf::100; fixed_ntoa(x, len) do var i, k, n; i := 99; ntoa_buf::i := 0; k := x<0-> -x: x; n := 0; while (k > 0 \/ i = 99) do n := n+1; i := i-1; ntoa_buf::i := '0' + k mod 10; k := k/10; end if (x < 0) n := n+1; while (n < len) do n := n+1; i := i-1; ntoa_buf::i := '0'; end if (x < 0) do i := i-1; ntoa_buf::i := '-'; end return @ntoa_buf::i; end ntoa(x) return fixed_ntoa(x, 0); aton(s) do var v, i; i := 0; v := 0; while ('0' <= s::i /\ s::i <= '9') do v := v*10 + s::i - '0'; i := i+1; end return v; end const SPEED = 20; const SPEEDUP = 3; const PAUSE = 50; const BMBSPEED = 2; const MAXSPEED = 2; const LASTSPEED = 1; const ROWS = 4; const COLS = 6; var Fort1, Fort2, Fort3, Inv1, Inv2, Rocket, Boom, Bomb, Exp1, Exp2; var Invmap, Kills, Score, Quit, Logo, Gameover, Level; var Xfort, Hit, Hits, Shield, Gamespeed, Hiscores::91; var Xinv, Yinv, Invdir, Invspeed, Invclk, Invstate; var Xrkt, Yrkt, Xbmb, Ybmb, Bmbclk; init() do x.init(); x.wave(1); Fort1 := x.bitmap(8, 8, packed [ 0x00, 0x00, 0x00, 0x01, 0x07, 0x1f, 0x3f, 0x3f ]); Fort2 := x.bitmap(8, 8, packed [ 0x00, 0x18, 0x18, 0x18, 0x3c, 0xff, 0xff, 0xff ]); Fort3 := x.bitmap(8, 8, packed [ 0x00, 0x00, 0x00, 0x80, 0xe0, 0xf8, 0xfc, 0xfc ]); Inv1 := x.bitmap(16, 16, packed [ 0, 0, 0, 0, 0x30, 0x0c, 0x08, 0x10, 0x04, 0x20, 0x1f, 0xf8, 0x3f, 0xfc, 0x73, 0xce, 0xf3, 0xcf, 0xff, 0xff, 0xbf, 0xfd, 0xa0, 0x05, 0x10, 0x08, 0x0c, 0x30, 0, 0, 0, 0 ]); Inv2 := x.bitmap(16, 16, packed [ 0, 0, 0, 0, 0x0c, 0x30, 0x08, 0x10, 0x04, 0x20, 0x1f, 0xf8, 0x3f, 0xfc, 0x73, 0xce, 0xf3, 0xcf, 0xff, 0xff, 0xbf, 0xfd, 0x88, 0x11, 0x08, 0x10, 0x30, 0x0c, 0, 0, 0, 0 ]); Rocket := x.bitmap(8, 8, packed [ 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x24, 0x00 ]); Boom := x.bitmap(16, 16, packed [ 0x00, 0x00, 0x44, 0x22, 0x24, 0x24, 0x12, 0x48, 0xc8, 0x13, 0x20, 0x04, 0x00, 0x00, 0xf8, 0x1f, 0x00, 0x00, 0x10, 0x08, 0x64, 0x46, 0x88, 0x11, 0x12, 0x48, 0x24, 0x24, 0x24, 0x22, 0x00, 0x00 ]); Bomb := x.bitmap(8, 8, packed [ 0x00, 0x1c, 0x38, 0x1c, 0x38, 0x1c, 0x38, 0x00 ]); Exp1 := x.bitmap(16, 16, packed [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x03, 0xcf, 0x0f, 0xdf, 0x19, 0xdf, 0x33, 0xf9, 0x67, 0xfd, 0x6f, 0xfd, 0x5f, 0xff, 0xdd, 0xdf, 0xfd, 0xdf, 0xfd, 0xff, 0xff, 0xff ]); Exp2 := x.bitmap(8, 16, packed [ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xf0, 0xf8, 0x9c, 0x9e, 0xce, 0xee, 0xef, 0xff, 0xff, 0xff ]); Logo := [ "...#####.#####.#...#.#####.#####.###.#####....", "...#.....#.....##..#.#.....#...#..#..#........", "...#.###.#####.#.# #.#####.#####..#..#........", "...#...#.#.....#..##.#.....#..#...#..#........", "...#####.#####.#...#.#####.#...#.###.#####....", "..............................................", "###.#...#.#...#.#####.####..#####.#####.#####.", ".#..##..#.#...#.#...#.#...#.#.....#...#.#.....", ".#..#.#.#.#...#.#####.#...#.#####.#####.#####.", ".#..#..##..#.#..#...#.#...#.#.....#..#......#.", "###.#...#...#...#...#.####..#####.#...#.#####.", ".............................................." ]; Gameover := [" #### ### # # #####", "# # # # ## ## # ", "# # # # # # # ", "# #### ##### # # #### ", "# # # # # # # ", "# # # # # # # ", " #### # # # # #####", " ", " #### # # ##### #### ", "# # # # # # #", "# # # # # # #", "# # # # #### #### ", "# # # # # # # ", "# # # # # # # ", " #### # ##### # #"]; x.bg(0); x.clrscr(); end initgame() do Level := 0; Invstate := 1; Gamespeed := SPEED; Shield := 5; Score := 0; Quit := 0; end initround(spd) do var i, j; Xfort := 14; Invmap := [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]; for (j=0, ROWS) for (i=0, COLS) Invmap[j][i] := 1; Xinv := 4; Yinv := 1; Invdir := 1; Invspeed := spd/SPEEDUP; if (Invspeed < MAXSPEED) Invspeed := MAXSPEED; Invclk := Invspeed; Xrkt := 0; Xbmb := 0; Kills := 0; Hits := 0; Level := Level + 1; end center(y, m) x.print(15-str.length(m)/2, y, m); print(x, y, m) do var i, j, k; k := str.length(m); j := 0; for (i=0, k) ie (m::i = '[') do x.bg(14); x.fg(0); end else ie (m::i = ']') do x.bg(0); x.fg(14); end else do x.blit(x+j, y, m::i); j := j+1; end x.bg(0); end post(m) do var i; x.fg(4); x.bg(14); for (i=0,32) x.blit(i, 0, '\s'); center(0, m); x.bg(0); x.redraw(); end ask(q) do var k, b::32; str.copy(b, q); str.append(b, "? y/n"); post(b); while (1) do k := x.getkey(); if (k = 'y') return %1; if (k = 'n') return 0; end end invader_sound(on) do var i; ie (on) do for (i=660, 440, %55) do x.beep(440, 2); x.beep(i, 2); end end else do for (i=440, 660, 55) do x.beep(440, 2); x.beep(i, 2); end end end inv_expl_sound() do var i; for (i=0, 20) x.beep(x.rnd(300)+200-i*10, 10); end fort_expl_sound(i) do x.beep(x.rnd(300)+200-i, 10); end hit_sound() do var i; for (i=0, 20) do x.beep(400-i*10, 5); x.beep(400+i*10, 5); end end launch_sound() do var i; for (i=400, 550, 4) x.beep(i, 2); end victory_sound(bon) do var i, j; for (i=0, 160+bon*40, 40) for (j=400, 660, 55) x.beep(i+j, 50); end draw_fort(x) do if (Shield < 0) return; x.fg(Hit-> 11: 14); if (Hit) Hit := Hit-1; x.blit(x-1, 21, Fort1); x.blit(x, 21, Fort2); x.blit(x+1, 21, Fort3); end draw_invaders() do var i, j; x.fg(13); for (j=0, ROWS) do for (i=0, COLS) do if (Invmap[j][i]) x.blit(i*4+Xinv, j*3+Yinv, Invstate-> Inv1: Inv2); end end end drop_bomb() do var i, j; if (Xbmb) return; Xbmb := x.rnd(COLS); for (i=0, COLS) do for (j=ROWS-1, %1, %1) if (Invmap[j][Xbmb] \= 0) do Ybmb := j; leave; end if (j > %1) leave; Xbmb := Xbmb + 1; if (Xbmb > COLS-1) Xbmb := 0; end Xbmb := Xbmb*4+Xinv + x.rnd(2); Ybmb := 2+Ybmb*3+Yinv; Bmbclk := Bmbspeed; end move_invaders() do var i, j, x0, x1; x0 := %1; x1 := 0; for (i=0, COLS) do for (j=0, ROWS) do if (Invmap[j][i]) do if (x0 = %1) x0 := i; x1 := i; end end end if (Invclk = 0) do Xinv := Xinv + Invdir; if (Xinv > 29-x1*4) do Invdir := %1; Xinv := 29-x1*4; Yinv := Yinv + 1; end if (Xinv < 1-x0*4) do Invdir := 1; Xinv := 1-x0*4; Yinv := Yinv + 1; end Invclk := Invspeed; invader_sound(Invstate); drop_bomb(); Invstate := \Invstate; end end explode_invader(x, y) do var xi, yi, i; xi := (x - Xinv) / 4; yi := (y - Yinv) / 3; Invmap[yi][xi] := 0; x.fg(15); x.blit(xi*4+Xinv, yi*3+Yinv, Boom); inv_expl_sound(); Xrkt := 0; Kills := Kills + 1; Score := Score + 100; if (Kills mod 6 = 0) do Invspeed := Invspeed - 1; if (Invspeed < MAXSPEED /\ Kills < 23) Invspeed := MAXSPEED; end if (Kills = 23) Invspeed := LASTSPEED; end move_rocket() do Yrkt := Yrkt-1; if (x.screen(Xrkt, Yrkt)) explode_invader(Xrkt, Yrkt); if (Yrkt < 3) Xrkt := 0; x.fg(15); if (Xrkt \= 0) x.blit(Xrkt, Yrkt, Rocket); end explode_fort() do var i; Shield := %1; for (i=0, 200) do x.fg(9+x.rnd(7)); x.blit(Xfort-1, 20, Exp1); x.blit(Xfort+1, 20, Exp2); fort_expl_sound(i); x.redraw(); end x.delay(100); end hit_fortress() do Hit := 10; Hits := Hits + 1; Shield := Shield - 1; if (Shield < 0) explode_fort(); end move_bomb() do Bmbclk := Bmbclk-1; if (Bmbclk = 0) do Bmbclk := Bmbspeed; Ybmb := Ybmb+1; if (Ybmb > 21) do if (Xbmb >= Xfort-1 /\ Xbmb <= Xfort+1) hit_fortress(); Xbmb := 0; end end x.fg(11); if (Xbmb \= 0) x.blit(Xbmb, Ybmb, Bomb); end draw_status() do var i; x.fg(13); x.line(0, 182, 255, 182); x.fg(11); x.print(0, 23, "Level:"); x.print(6, 23, fixed_ntoa(Level, 2)); x.print(9, 23, "Shld:"); for (i=0, 5) x.blit(i+14, 23, 14); for (i=0, Shield) x.blit(i+14, 23, 16); x.print(20, 23, "Score:"); x.print(26, 23, fixed_ntoa(Score, 6)); end really_quit() do Quit := ask("really quit"); return Quit; end get_ready() do var i; for (i=0, 5) do x.fg(0); x.bg(14); x.print(10, 16, "get ready"); x.redraw(); x.delay(250); x.fg(14); x.bg(0); x.print(10, 16, "get ready"); x.redraw(); x.delay(250); end return %1; end round(spd) do var i, ready;; ready := 0; initround(spd); x.clrscr(); while (1) do draw_fort(Xfort); move_invaders(); draw_invaders(); draw_status(); if (Xrkt \= 0) move_rocket(); if (Xbmb \= 0) move_bomb(); if (Quit /\ really_quit()) return; if (Shield < 0) return; if (\ready) ready := get_ready(); if (Kills > 23) return; x.redraw(); for (i=0, 32, 2) if (x.screen(i, 21) = Inv1 \/ x.screen(i, 21) = Inv2) do explode_fort(); return; end x.delay(PAUSE); if (Hit = 10 /\ Shield >= 0) hit_sound(); ie (x.keydown(2) /\ Xfort < 30) do Xfort := Xfort+1; end else ie (x.keydown(4) /\ Xfort > 1) do Xfort := Xfort-1; end else ie (x.keydown('a') /\ Xrkt = 0) do launch_sound(); Xrkt := Xfort; Yrkt := 21; end else ie (x.keydown('p') \/ x.keydown('\s')) do post("paused"); x.getkey(); end else if (x.keydown(27)) do Quit := %1; end Invclk := Invclk-1; x.clrscr(); end end list_scores(w) do var i, j, b::10, s; for (j=0, 22) for (i=0, 32) x.blit(i, j, '\s'); x.fg(14); x.print(10, 5, "HIGH SCORES"); for (i=0, 10) do s := fixed_ntoa(i+1, 2); if (s::0 = '0') s::0 := '\s'; x.print(8, 7+i, s); x.print(10, 7+i, "."); t.memcopy(@Hiscores::(9*i), b, 3); b::3 := 0; x.print(12, 7+i, b); t.memcopy(@Hiscores::(3+9*i), b, 6); b::6 := 0; x.print(17, 7+i, b); end if (w) do x.redraw(); x.getkey(); end end loadhi() do var f, i; f := t.open("invaders.hi", 0); if (f < 0) do for (i=0, 10) t.memcopy("...000000", @Hiscores::(9*i), 9); Hiscores::90 := '\n'; return; end t.read(f, Hiscores, 91); t.close(f); end savehi() do var f; f := t.create("invaders.hi"); if (f < 0) return; t.write(f, Hiscores, 91); t.close(f); end printhi() do var i, j, n::4, p; n::3 := 0; p := 0; for (i=0, 2) do for (j=0, 5) do x.print(i*6+20, j+12, ntoa((p+1) mod 10)); t.memcopy(@Hiscores::(9*p), n, 3); x.print(i*6+22, j+12, n); p := p+1; end end end enter_hiscore() do var i, b::10, j, k, p; for (p=0, 10) do if (Score > aton(@Hiscores::(3+9*p))) leave; end if (p = 10) return; t.memcopy(@Hiscores::(9*p), @Hiscores::(9+9*p), 81-9*p); t.memcopy(fixed_ntoa(Score, 6), @Hiscores::(9*p+3), 6); t.memcopy("...", @Hiscores::(9*p), 3); list_scores(0); str.copy(b, "..."); j := 0; while (1) do x.fg(0); x.bg(14); x.blit(12+j, 7+p, b::j); x.fg(14); x.bg(0); x.redraw(); k := x.getkey(); ie ('a' <= k /\ k <= 'z') do b::j := k; x.blit(12+j, 7+p, k); j := j+1; if (j > 2) j := 0; end else ie (k = 8) do x.blit(12+j, 7+p, b::j); j := j-1; if (j < 0) j := 2; end else if (k = 13) do leave; end end t.memcopy(b, @Hiscores::(9*p), 3); savehi(); end game_over() do var i, j, k, n, x; k := str.length(Gameover[0]); x := 16-k/2; for (n=0, 6) do x.fg(0); for (j=0, 15) do for (i=0, k) x.blit(i+x, 3+j, 0); end x.redraw(); x.delay(167); x.fg(14); for (j=0, 15) do for (i=0, k) x.blit(i+x, 3+j, Gameover[j]::i='#'-> 16: 12); end x.redraw(); x.delay(167); end x.delay(1000); enter_hiscore(); end play_again() return ask("play again"); next_round() do var i, j, y, b::32, bon; y := 7; x.fg(14); bon := 0; ie (Hits = 0) do center(y, "perfect defense: +500"); Score := Score + 500; bon := bon + 1; end else if (Hits < 4) do str.copy(b, " defense bonus: +"); str.append(b, ntoa(400-Hits*100)); center(y, b); Score := Score + 400-Hits*100; bon := bon + 1; end y := y+2; ie (Shield = 5) do center(y, "full shields: +500"); Score := Score + 1000; y := y + 2; bon := bon + 1; end else do Shield := Shield + 1; end x.fg(0); x.bg(14); str.copy(b, "score: "); str.append(b, ntoa(Score)); center(y, b); x.fg(14); x.bg(0); y := y+2; Gamespeed := Gamespeed - 1; if (Gamespeed < 1) Gamespeed := 1; print(4, y, "press [enter] to continue"); x.redraw(); victory_sound(bon); x.delay(2000); while (x.getkey() \= 13) ; end play() do initgame(); while (1) do round(Gamespeed); if (Quit) leave; ie (Shield < 0) do game_over(); if (\play_again()) leave; initgame(); end else do next_round(); end end end intro() do var i, j, k, n, v, m, b::30; loadhi(); m := [ 00,23,24,19,22,18,30,25,21,29,20,26,17,28,27,16 ]; n := str.length(Logo[0]); while (1) do x.bg(0); for (k=0, 50) do x.clrscr(); x.fg(k=49-> 14: x.rnd(7)+9); for (j=0, 12, 2) do for (i=0, n, 2) do v := (Logo[j]::i = '#'-> 8: 0) + (Logo[j]::(i+1) = '#'-> 4: 0) + (Logo[j+1]::(i) = '#'-> 2: 0) + (Logo[j+1]::(i+1) = '#'-> 1: 0); x.blit(5+i/2, 3+j/2, m[v]); end end x.fg(14); str.copy(b, "high score: ["); t.memcopy(@Hiscores::3, @b::13, 6); t.memcopy("]", b::19, 2); print(7, 10, b); print(1, 12, "[<-] [->] move turret"); print(1, 14, "[a] fire [p] pause"); print(1, 16, " [esc] quit game"); print(6, 20, "press [enter] to start"); printhi(); x.delay(20); x.redraw(); end k := x.getkey(); if (k = 13) play(); if (k = 27) leave; if (k = 's') list_scores(1); end end do init(); intro(); x.fini(); end