http://t3x.org/xt3x/invaders.html (light|dark)
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