Back to the minefield

Description of the page content

A tribute and a remake of ZX Spectrum's Mined-Out.

Tags:

A small and slow project, a tribute to and a reinterpretation of Ian Andrew's Mined-Out (1983); and a benchwork for my Vimclair BASIC.

Screenshots

Images taken during the development. The aspect is not definitive.

Game scene of Back to the minefieldMenu of Back to the minefield

Source code

The program is not finished, but here you are its code at the moment this page was updated:

rem Back to the minefield

//  Written in Vimclair BASIC for ZX Spectrum 128.

rem Author: Marcos Cruz (programandala.net), 2016, 2017, 2018, 2019, 2020.

rem Credit: A remake of Ian Andrew's Mined-Out (1983), partly based on his code and graphics.

// You may do whatever you want with this work, so long as you
// retain every copyright, credit and authorship notice, and this
// license.  There is no warranty.

// ==============================================================
// Start {{{1

clear 65535-21*8*3-8-1:\

let version$="0.109.1+202005100021":\
// Note: version number after Semantic Versioning: http://semver.org

goto @init

// ==============================================================
// Functions {{{1

// A random row of the field:
deffn random_row()=int(rnd*mined_rows)+top_mined_row

// A random column of the field:
deffn random_col()=int(rnd*30)+1

// Text number n in the current language:
deffn txt$(n)=text$(lang,n,1 to text_len(lang,n))

// Current count of the system frames counter, in seconds:
deffn seconds()=int((peek 23672+256*peek 23637+65536*peek 23674)/50)

// Score in a 5-character string, with zeros at the left:
# XXX OLD
# deffn score$(n)="00000"(to 5-len str$ n)+str$ n

// ==============================================================
// Start a new game {{{1

@new_game:

let level=first_level:\
let score=0

// ==============================================================
// Enter a new level {{{1

@new_level:

randomize udg1:\
gosub @select_udg_set

let surrounding_mines=0:\
let door_closed=0

// coordinates
let row=bottom_fence_row:\
let col=start_col:\
let old_row=row:\
let old_col=col

// list of coordinates, stored as chars
let trail$=chr$ row+chr$ col

// counter
let enter_time=fn seconds()

let protagonist_frame=0:\

# let walking_mine_step=-1:\
# let walking_mine_row=row:\
# let walking_mine_col=col:\

let mines=32+level*4

border border_color:\
paper ground_color:\
ink black:\
cls:\
gosub @no_message:\
load!"fence.scr"code

let message$=fn txt$(putting_mines_txt):\
gosub @message

print paper transparent;bright 1;\
  at top_fence_row+1,1;fn txt$(safe_zone_txt);\
  at bottom_fence_row-1,1;fn txt$(safe_zone_txt)

for w=1 to mines:\
  print \
    at fn random_row(),fn random_col();\
    ink ground_color;mine_udg$:\
  beep .0015,35:\
next w

print paper transparent;\
  at top_fence_row+1,1;blank_field_row$;\
  at bottom_fence_row-1,1;blank_field_row$

gosub @no_message:\
gosub @new_status_bar

if level=last_level then

  let things=0:\
  gosub @last_level

else

  let things=int(rnd*(level+3)+level+1)
  if things then
    dim thing_row(things):\
    dim thing_col(things):\
    dim thing_udg$(things):\
    dim thing_ink(things):\
    for i=1 to things:\
      let thing_row(i)=fn random_row():\
      let thing_col(i)=fn random_col()
      if int(rnd*level+1) then
        let thing_udg$(i)=can_udg$:\
        let thing_ink(i)=can_color
      elseif int(rnd*level+1) then
        let thing_udg$(i)=bottle_udg$:\
        let thing_ink(i)=bottle_color
      elseif int(rnd*(last_level-level)+1) then
        let thing_udg$(i)=flower_udg$:\
        let thing_ink(i)=flower_color
      endif
      print \
        at thing_row(i),thing_col(i);\
        paper transparent;ink thing_ink(i);\
        thing_udg$(i):\
    next i:\
    ink contrast
  endif

endif

if level=(last_level-1) then \
  gosub @close_the_door

beep .0875,10:\

for n=1 to 20:\
  beep .002,n+20:\
next n:\

goto @one_step

// ==============================================================
// Walking loop {{{1

@walking_loop

let old_row=row:\
let old_col=col

do

  if int (rnd*(surrounding_mines+1.2)) then:\
    print at row,col;\
      paper path_color;\
      protagonist$(protagonist_frame+1):\
      let protagonist_frame=not protagonist_frame:\
      beep .003,4-8*protagonist_frame

  let i=peek 23560:\
  poke 23560,0:\
  let row=row+(i=10)-(i=11):\
  let row=row-(row=(bottom_fence_row+1)):\
  let col=col+(i=9)-(i=8)

  // XXX OLD -- improve or remove
  # if level>=4 then \
  #   if time>(260*ground_color+70) then \
  #     if int(time/(3*ground_color+1))=(time/(3*ground_color+1)) then \
  #       gosub @walking_mine

loop while old_row=row and old_col=col

beep .003,4-8*protagonist_frame

@one_step

print at old_row,old_col; paper path_color;" "
let trail$=trail$+chr$ row+chr$ col

// XXX TODO -- move down to save the print
if door_closed then \
    if row=key_row then \
      if col=key_col then \
        print at row,col; paper path_color;protagonist$(protagonist_frame+1):\
        let protagonist_frame=not protagonist_frame:\
        gosub @open_the_door:\
        goto @step_done

// make the first UDG bank the current font, in order to detect
// the graphics with `screen$()`:
poke 23606,udg1_font_low:poke 23607,udg1_font_high:\

// XXX REMAK -- There's a bug in Sinclar BASIC: the following
// calculations return a wrong non-integer value. That's why every
// value is calculated apart.

# let surrounding_mines=int(\
#   (screen$(row-1,col)<>" ")+\
#   (screen$(row+1,col)<>" ")+\
#   (screen$(row,col-1)<>" ")+\
#   (screen$(row,col+1)<>" "))

# let surrounding_mines=(screen$(row-1,col)<>" ")
# let surrounding_mines=surrounding_mines+(screen$(row+1,col)<>" ")
# let surrounding_mines=surrounding_mines+(screen$(row,col-1)<>" ")
# let surrounding_mines=surrounding_mines+(screen$(row,col+1)<>" ")

let found_char=code screen$(row,col):\
let front_surrounding_mines=screen$(row-1,col)=mine_char$:\
let back_surrounding_mines=screen$(row+1,col)=mine_char$:\
let left_surrounding_mines=screen$(row,col-1)=mine_char$:\
let right_surrounding_mines=screen$(row,col+1)=mine_char$:\

// restore the default font:
poke 23606,0:poke 23607,60

if found_char=mine_char then \
  goto @explosion

if found_char=fence_char then \
  goto @explosion

if found_char=flower_char then
  let score=score-10:\
  if score<0 then let score=0
  gosub @update_status_bar
elseif found_char=can_char or found_char=bottle_char then
  let score=score+10:\
  gosub @update_status_bar
elseif found_char=bill_char then
  gosub @rescue
endif

print at row,col;\
  paper path_color;\
  protagonist$(protagonist_frame+1):\
let protagonist_frame=not protagonist_frame

@step_done:

let surrounding_mines=\
  front_surrounding_mines+\
  back_surrounding_mines+\
  left_surrounding_mines+\
  right_surrounding_mines:\
gosub @print_surrounding_mines:\
beep .04*sgn surrounding_mines,surrounding_mines*10

if row=top_fence_row then \
  goto @level_passed

goto @walking_loop

// ==============================================================
// subroutine: close the door {{{1

@close_the_door:

let key_row=fn random_row():\
let key_col=fn random_col():\
print at key_row,key_col;key_udg$:\

let message$=fn txt$(you_need_key_txt):\
gosub @message

for i=60 to 10 step -5:\
  print at top_fence_row,door_col;"   ":\
  beep .125,i:\
  print at top_fence_row,door_col;fence$(1 to 3):\
  for j=1 to 7: next j:\
next i:\
let door_closed=1:\
goto @no_message // then return

// ==============================================================
// subroutine: open the door {{{1

@open_the_door:

let message$=fn txt$(open_door_txt):\
gosub @message:\
for i=10 to 60 step 5:\
  print at top_fence_row,door_col;fence$(1 to 3):\
  beep .125,i:\
  print at top_fence_row,door_col;"   ":\
  for j=1 to 7: next j:\
next i:\
let door_closed=0:\
goto @no_message // then return

// ==============================================================
// subroutine: status bar {{{1

@new_status_bar:

input ;:\
gosub @select_udg_chars_set:\
print #1;paper black;ink white;\
  at 0, 0;fn txt$(level_txt);inverse 1;\
  at 1, 0;"     ";inverse 0;\
  at 0, 8;fn txt$(near_mines_txt);\
  at 0,16;fn txt$(points_txt);inverse 1;\
  at 1,16;" 00000 ";inverse 0;\
  at 0,25;fn txt$(record_txt);inverse 1;\
  at 1,25;" 00000 ";:\
randomize udg1:\
gosub @select_udg_set

@update_status_bar:

// XXX FIXME -- A system bug?: only the first item is affected by
// `paper` and `ink`, the rest is printed with the current colors. The
// problem is solved by removing `str$` in the expression. In fact,
// using `str$` to calculate the column of the first expression, makes
// this one be printed with default colors too.
//
// This problem happens on the Fuse emulator and on the ZX Spectrum
// Next.

# print #1;paper red;ink black;\
#   at 1,2;level;\
#   at 1,22-len str$ score;score;\
#   at 1,31-len str$ record;record

// XXX FIXME -- This alternative prints score and record with inverse
// colors! `str$` is used in the function that calculates the string.

# print #1;paper white; ink black;\
#   at 1,2;level;\
#   at 1,17;fn score$(score);\
#   at 1,26;fn score$(record)

# XXX FIXME -- This makes no difference:

# print #1;paper white;ink black;\
#   at 1,2;level;\
#   at 1,17;paper white;ink black;fn score$(score);\
#   at 1,26;paper white;ink black;fn score$(record)

# XXX FIXME -- This makes no difference:

# print #1;paper transparent; ink transparent;\
#   at 1,2;level;\
#   at 1,17;fn score$(score);\
#   at 1,26;fn score$(record)

# XXX REMARK -- This works fine:

print #1;paper white; ink black;\
  at 1,2;level;\
  at 1,17+(score<10000)+(score<1000)+(score<100)+(score<10);score;\
  at 1,26+(record<10000)+(record<1000)+(record<100)+(record<10);record

@print_surrounding_mines:

print #1; paper detector_color(surrounding_mines+1);ink contrast;\
  at 1,8;"  ";surrounding_mines;"  ":\
return

// ==============================================================
// subroutine: walking mine {{{1

// XXX TODO -- improve or remove

# @walking_mine:

# // XXX TODO -- try, after the latest changes (2016-06-12)

# print at walking_mine_row,walking_mine_col; paper path_color;" "

# let walking_mine_step=walking_mine_step+2:\
# beep .0018,60
# let walking_mine_row=code trail$(walking_mine_step):\
# let walking_mine_col=code trail$(walking_mine_step+1):\
# if screen$(walking_mine_row,walking_mine_col)<>" " then \
#   goto @explosion

# // XXX TODO -- improve:
# print at walking_mine_row,walking_mine_col; paper path_color;"\h":\
# return

// ==============================================================
// explosion {{{1

@explosion:

// XXX TODO -- remove mine count from the status bar

for i=20 to 1 step -1:\
  beep .003,i: print at row,col;"\c":\
  beep .002,10: print at row,col;dead_protagonist_udg$:\
next i
beep 1.6,-35
if found_char=mine_char then
  print at row,col;mine_udg$
else
  print at row,col;fence_udg$
endif
gosub @replay

if not quit_replay then:\
  print at row,col;dead_protagonist_udg$:\
  beep 1,-35

if score>record then \
  gosub @new_record
gosub @update_status_bar

let message$=fn txt$(press_key_txt):\
gosub @message:\

pause 0:\
goto @menu

// ==============================================================
// subroutine: message {{{1

@message:

gosub @no_message:\
gosub @select_udg_chars_set:\
print bright 1; ink contrast; paper border_color;\
  at message_row,(32-len message$)/2;message$;:\
randomize udg1:\
goto @select_udg_set // then return

@no_message:

print at message_row,0;paper border_color;blank_row$;:\
return

// ==============================================================
// Rescue {{{1

@rescue:

# XXX OLD
# gosub @the_nest
# let dx=30
# for u=.155 to .005 step -.01
#   print at 0,dx; ink black; paper white;"\n "
#   let dx=dx-1
#   beep u,8: beep u,12: beep u,16
#   border rnd*7
# next u
# for n=1 to 16
#   print at 0,dx; ink black; paper white;"\n "
#   let dx=dx-1+(dx=0)
#   beep u+.005,8
#   border rnd*7
#   beep u+.005,12: beep u,16
# next n
# print at 0,0; paper white;" "
# for t=0 to 2
#   for l=0 to 7 step 7
#     ink l
#     // XXX TODO -- simpler
#     for n=1 to 5:\
#       print at row+n,col+n;"*":\
#       print at row-n,col+n;"*":\
#       print at row+n,col-n;"*":\
#       print at row-n,col-n;"*":\
#     next n
#     for n=1 to 5:\
#       print at row,col+n;">":\
#       print at row,col-n;"<":\
#       print at row+n,col;"#":\
#       print at row-n,col;"#":\
#     next n
#   next l
# next t
# ink black
# gosub @the_nest

print at 0,0; ink white; paper black;\
text$(lang,two_thousand_txt);

let score=score+2000:\
gosub @update_status_bar

border black: paper black: cls

if lang=spanish then

#  <------------------------------>
print at 3,8;\
  "¡Felicidades!"
#  <------------------------------>
print '\
  "Has rescatado a Bill. El mundo"'\
  "te está agradecido."
#  <------------------------------>
print '\
  "Puntuación: ";score;"."
#  <------------------------------>
print '\
  "¿Por qué no vuelves a jugar y"'\
  "tratas de mejorarla?"
#  <------------------------------>

elseif lang=english then

#  <------------------------------>
print at 3,6;\
  "Congratulations!"
#  <------------------------------>
print '\
  "You have rescued Bill. The"'\
  "world thanks you."
#  <------------------------------>
print '\
  "Score: ";score;"."
#  <------------------------------>
print '\
  "Why don't you play again and"'\
  "try to do it even better?"
#  <------------------------------>

elseif lang=interlingue then

// XXX TODO --

#  <------------------------------>
print at 3,6;\
  "Gratulationes!"
#  <------------------------------>
print '\
  "Vu hat salvat Bill. Li munde"'\
  "mersia Vos."
#  <------------------------------>
print '\
  "Pontes: ";score;"."
#  <------------------------------>
print '\
  "Mey Vu luder ancor e provar far"'\
  "it mem plu bon?"
#  <------------------------------>

else // Esperanto

#  <------------------------------>
print at 3,6;\
  "Gratulon!"
#  <------------------------------>
print '\
  "Vi savis Bill. La mondo dankas"'\
  "vin."
#  <------------------------------>
print '\
  "Poentoj: ";score;"."
#  <------------------------------>
print '\
  "Kial ne ludi plian fojon kaj"'\
  "klopodi fari eĉ pli bone?"
#  <------------------------------>

endif

// XXX FIXME -- This is not the best place for this, given the
// previous messages:
if score>record then \
  gosub @new_record

pause 0:\
goto @menu

// ==============================================================
// subroutine: replay {{{1

@replay:

let quit_replay=0 // exit flag

// XXX FIXME -- remove the surrounding mines
// XXX FIXME -- when the replay is ended, the explosion is shown in the
// current position!

// XXX TODO --
# print at walking_mine_row,walking_mine_col;" ";\
#       at row,col;" "

// Make all mines visible and hide the path:
load! "groundattr" code

// Restore all the things:
for i=1 to things:\
  print at thing_row(i),thing_col(i);\
    paper transparent;ink thing_ink(i);\
    thing_udg$(i):\
next i

gosub @select_udg_chars_set

let paused_replay=0:\
gosub @show_replay_bar

randomize udg1:\
gosub @select_udg_set

for n=1 to 100: next n

let row=code trail$(1):\
let col=code trail$(2)

for t=1 to len trail$ step 2

@replay_control:

  // XXX REMARK -- At the moment the control keys must be the same in
  // all languages (P to pause and F to finish).

  let i$=inkey$
  if paused_replay then
    let paused_replay=(i$=""):\
    beep .1*not paused_replay,0:\
    if paused_replay then \
      goto @replay_control
    gosub @show_replay_bar
  endif
  if i$="f" or i$="F" then \
    let quit_replay=1:\
    goto @quit_replay
  if i$="p" or i$="P" then \
    let paused_replay=1:\
    beep .1,10:\
    gosub @show_replay_bar

  print at row,col; paper white;" "
  let row=code trail$(t):\
  let col=code trail$(t+1):\
  print at row,col; paper white;protagonist$(protagonist_frame+1):\
  let protagonist_frame=not protagonist_frame
  beep .005,5+(t*40/(len trail$))

next t

@quit_replay:

goto @no_message // then return

@show_replay_bar:

let message$=fn txt$(replay_controls_1_txt+paused_replay):\
goto @message // then return

// ==============================================================
// subroutine: level passed {{{1

@level_passed:

let surrounding_mines=0:\
gosub @print_surrounding_mines

// XXX TODO -- improve time score with the frames system variable

let exit_time=fn seconds():\
let time_score=600-(exit_time-enter_time):\
let time_score=time_score*(time_score>0)*level

// XXX TODO --
# restore @applause_sound:\
# gosub @sound

paper path_color
poke 23560,0:\
for i=1 to 8:\
  print at row,col;"\i":\
  if peek 23560 then goto @level_replay
  beep .1,0:\
  print at row,col;"\j":\
  beep .1,10:\
  if peek 23560 then goto @level_replay
next i

@level_replay:

// XXX TODO --
# restore @no_sound:\
# gosub @sound

print at row,col;" "

gosub @replay

// XXX TODO -- variable to flash the score
let score=score+time_score+mines:\
gosub @update_status_bar

let level=level+1:\
goto @new_level

// ==============================================================
// subroutine: new record {{{1

@new_record:

// XXX TMP --
let record=score:return

// XXX TODO -- rewrite:

for n=1 to 50
  border blue: border red: border magenta: border green
next n
for n=1 to 50
  border yellow: border red: beep .002,40+(n/10): border yellow
next n
gosub @select_udg_chars_set
for m=1 to 4
  for n=7 to 0 step -1
    print at 10,7; ink n;fn txt$(new_record_txt)
    beep .004,50-n
  next n
next m
print at 10,7; ink constrast;fn txt$(new_record_txt)
randomize udg1:\
gosub @select_udg_set

// XXX TODO -- adapt

print at 21,0;\
  flash 1; paper blue; ink white;\
  fn txt$(enter_initials_txt); flash 0;"         "

let record_player$="   "
for n=1 to 3
  do
    let record_player$(n)=inkey$
  loop while record_player$(n)=" "
  if record_player$(n)>="a" and record_player$(n)<="z" then \
    let record_player$(n)=chr$((code record_player$(n))-32)
  print at 21,24+n; ink white; paper blue;record_player$(n)
  beep .12,(n*5)+20
  for m=1 to 4: next m
next n
let record=score
for n=1 to 12
  beep .0045,-10: border blue: border red: border yellow: border green
next n
border border_color
beep .1,8
for n=1 to 4: next n
beep .1,8: beep .1,8: beep .1,20: beep .1,24: beep .1,18: beep .15,29
for n=1 to 7: next n
beep .12,22
for n=1 to 3: next n
beep .07,19: beep .08,17
for n=1 to 3: next n
beep .1,14: beep .1,12
print at 10,7;"                  "
return

// ==============================================================
// init {{{1

@init:

// ---------------------------------------------
// Screen {{{2

border black:\
paper black:\
ink black:\
flash 0:\
inverse 0:\
bright 0:\
cls

// ---------------------------------------------
// OS variables {{{2

// Set the REPDEL OS variable (at memory address 23561) to zero, for
// speed.
//
// REPDEL contains the time (in 50ths of a second) that a key must be
// held down before it repeats.  Its default value is 35.

poke 23561,0

// ---------------------------------------------
// Graphics {{{2

// There are three UDG banks:
//
// udg1: graphics
// udg2: the Spanish and Interlingue characters
// udg3: the Esperanto characters

let udg1=65535-21*8*3-1:\
let udg2=udg1+21*8:\
let udg3=udg2+21*8:\
let udg_chars=udg2:\
let udg1_font=udg1-256-8:\
let udg1_font_high=int(udg1_font/256):\
let udg1_font_low=udg1_font-(udg1_font_high*256)

// ---------------------------------------------
// Constants {{{2

let first_level=1 // XXX TMP -- change for debugging -- default=1
let last_level=7

let message_row=0:\
let top_fence_row=1:\
let bottom_fence_row=21:\
let top_safe_row=top_fence_row+1:\
let top_mined_row=top_safe_row+1:\
let bottom_safe_row=bottom_fence_row-1:\
let mined_rows=bottom_safe_row-top_safe_row-1:\
let door_col=14:\
let start_col=15

let blank_row$="                                "

let protagonist$="\d\e":\
let dead_protagonist_udg$="\l":\

let bill_char=46:\
let bill_udg$="\n":\
let bottle_char=51:\
let bottle_udg$="\s":\
let can_char=50:\
let can_udg$="\r":\
let fence_char=38:\
let fence_udg$="\f":\
let flower_char=49:\
let flower_udg$="\q":\
let key_char=48:\ // XXX TODO -- Not used.
let key_udg$="\p":\
let mine_char=47:\
let mine_char$="/":\
let mine_udg$="\o"

let fence$="\f\f\f\f\f\f\f\f\f\f\f\f\f\f   \f\f\f\f\f\f\f\f\f\f\f\f\f\f\f"

let blank_field_row$="                              "

dim detector_color(4):\
let detector_color(1)=4:\
let detector_color(2)=5:\
let detector_color(3)=6:\
let detector_color(4)=2

// ---------------------------------------------
// Variables {{{2

let record_player$="IAN":\
let record=250

// ---------------------------------------------
// Languages {{{2

// Defined in alphabetical order of their original names:

let english=1:\     // English
let spanish=2:\     // Español
let esperanto=3:\   // Esperanto
let interlingue=4:\ // Interlingue

let langs=4:\
let max_lang_key=ASCII_0+langs

// ---------------------------------------------
// Multilingual texts {{{2

dim text$(langs,texts,32):\
dim text_len(langs,texts):\
dim yes$(langs):\
dim no$(langs)

gosub @texts:\
let lang=english

// ---------------------------------------------
// Files {{{2

if not peek 65535 then // file operations not done yet?

// .............................
// UDG graphics {{{2

// All UDG banks are loaded from one single tape file.

load "UDG.BIN" code udg1-8:\

// .............................
// Fence {{{3

// Draw the fence and save the display (without attributes) into a RAM
// disk file, which which will used to update the display
// instantaneously.

randomize udg1:\
gosub @select_udg_set:\
print at top_fence_row,0;fence$:\
for n=top_safe_row to bottom_safe_row:\
  border white:\
  print at n,0;\
    fence_udg$+"                              "+fence_udg$:\
  border black:\
next n:\
print at bottom_fence_row,0;fence$:\
save!"fence.scr"code 16384,6144

// .............................
// Arena attributes {{{3

// Save the attributes of the field ground (rows from 1 to 21) into a
// RAM disk file, which will be used to restore the field before the
// replay.

paper ground_color:\
ink black:\
border ground_color:\
cls:\
save!"groundattr" code 22560,672

// .............................
// Text attributes {{{3

// Save the text attributes used in the menu, the credits and the
// instructions.  They are used to reveal the texts instantaneously.

paper blue:ink white:border blue:\
cls:\
save!"txtattr"code 22528,768

// .............................

// Set a marker to know the necessary files were already loaded from
// tape and created into the RAM disk, in case the program is
// reentered with `run`:

poke 65535,1

endif

// ==============================================================
// menu {{{1

@menu:

border blue: paper blue: ink blue:\
cls

print \
  at 3,4;"{(C)} 2016-2020 Marcos Cruz";\
  at 4,6;"(programandala.net)"

@change_menu_language:

gosub @select_udg_chars_set:\

print \

at 0,0;text$(lang,title_txt);\

at 10,8;inverse 1;text$(lang,instructions_txt,1);\
  inverse 0;text$(lang,instructions_txt,2 to 13);\
at 12,8;inverse 1;text$(lang,start_txt,1);\
  inverse 0;text$(lang,start_txt,2 to 5);\
at 14,8;inverse 1;text$(lang,credits_txt,1);\
  inverse 0;text$(lang,credits_txt,2 to 19)

gosub @lang_footer_menu

load!"txtattr"code:\
ink white

do

  // Wait for a key and convert it to uppercase
  // if needed:
  poke 23560,0:\
  pause 0:\
  let i=peek 23560:\
  let i=i-32*(i>90) // convert to uppercase

  // Instructions?
  if i=code text$(lang,instructions_txt,1) then \
    gosub @instructions:\
    goto @menu

  // Play?
  if i=code text$(lang,start_txt,1) then \
    goto @new_game

  // Credits?
  if i=code text$(lang,credits_txt,1) then \
    gosub @credits:\
    goto @menu

  // Change lang?
  gosub @check_new_lang:\
  if new_lang then \
    goto @change_menu_language

  // Invalid key
  beep .1 ,23

loop

// ==============================================================
// subroutine: language footer menu {{{1

@lang_footer_menu

// Load the attributes to reveal the texts:
load!"txtattr"code:\

// Select the UDG set containing the Spanish chars,
// no matter the current language, because only
// that set is needed to print the list of languages:
randomize udg2:\
gosub @select_udg_set:\

print #1;at 0,0;\
  inverse lang<>english;english;inverse 0;" ";\
  inverse lang=english;"English";inverse 0,\
  inverse lang<>esperanto;esperanto;inverse 0;" ";\
  inverse lang=esperanto;"Esperanto"'\
  inverse lang<>spanish;spanish;inverse 0;" ";\
  inverse lang=spanish;"Español";inverse 0,\
  inverse lang<>interlingue;interlingue;inverse 0;" ";\
  inverse lang=interlingue;"Interlingue":\
randomize udg_chars:\
goto @select_udg_set // then return

// ==============================================================
// subroutine: credits {{{1

@credits:

paper blue:ink blue:cls:\
print text$(lang,title_txt)''

if lang=spanish then

#      <------------------------------>
print "Versión:"''
print "  ";version$
print
print "Autor:"'
print "  Marcos Cruz"
print "  (programandala.net), 2016-2019"
print
print "Jugador de pruebas:"'
print "  Irene Albert."
print
print "Un homenaje a y una reinterpre-"
print "tación de:"'
print "  Mined-Out (Ian Andrew, 1983)."
print
print "Escrito en Vimclair BASIC para"
print "ZX Spectrum 128."
#      <------------------------------>

elseif lang=english then

#      <------------------------------>
print "Version:"'
print "  ";version$
print
print "Author:"'
print "  Marcos Cruz"
print "  (programandala.net), 2016-2019"
print
print "Test player:"'
print "  Irene Albert."
print
print "A tribute to and a reinterpre-"
print "tation of:"'
print "  Mined-Out (Ian Andrew, 1983)."
print
print "Written in Vimclair BASIC for"
print "ZX Spectrum 128."
#      <------------------------------>

elseif lang=interlingue then

#      <------------------------------>
print "Version:"'
print "  ";version$
print
print "Autor:"'
print "  Marcos Cruz"
print "  (programandala.net), 2016-2019"
print
print "Lusor de provas:"'
print "  Irene Albert."
print
#      <------------------------------>
print "Un tribute e un reinterpreta-"
print "tion ye:"'
print "  Mined-Out (Ian Andrew, 1983)."
print
print "Scrit in Vimclair BASIC por ZX"
print "Spectrum 128."
#      <------------------------------>

elseif lang=esperanto then

#      <------------------------------>
print "Versio:"'
print "  ";version$
print
print "Aŭtoro:"'
print "  Marcos Cruz"
print "  (programandala.net), 2016-2019"
print
print "Provludanto:"'
print "  Irene Albert."
print
print "Omaĝo kaj reinterpreto je:"'
print "  Mined-Out (Ian Andrew, 1983)."
print
print "Verkita en Vimclair BASIC por ZX"
print "Spectrum 128."
#      <------------------------------>

endif

gosub @lang_footer_menu

do
  poke 23560,0:\
  pause 0:\
  let i=peek 23560:\
  gosub @check_new_lang:\
  if new_lang then :\
    goto @credits
loop while lang_key

randomize udg1:\
goto @select_udg_set // then return

// ==============================================================
// subroutine: icon {{{1

// Input:
// i$ = UDG string

@icon:
randomize udg1:\
gosub @select_udg_set:\
print 'i$;

// Run into `@select_udg_chars_set` and return:

// ==============================================================
// subroutine: select current lang characters {{{1

@select_udg_chars_set:

randomize udg_chars

// Run into `@select_udg_set` and return:

// ==============================================================
// subroutine: select user defined characters {{{1

// Input: UDG set address stored in OS variable 23670.

@select_udg_set:

poke 23675,peek 23670:poke 23676,peek 23671:\
randomize:\
return

// ==============================================================
// subroutine: instructions {{{1

@instructions:

paper blue:ink blue:cls:\
print text$(lang,title_txt)

if lang=spanish then

let i$=protagonist$(1):gosub @icon
#      <------------------------------>
print  " Tú, la heroica damisela. Atra-";
print "  viesa los campos de minas,"
print "  usando el cursor."
let i$=bill_udg$:gosub @icon
#      <------------------------------>
print  " Bill el gusano: Está en el úl-";
print "  timo campo. Rescátalo."
let i$=mine_udg$:gosub @icon
#      <------------------------------>
print  " Minas: En la parte inferior"
print "  verás cuántas tienes cerca."
let i$=fence_udg$:gosub @icon
#      <------------------------------>
print  " Verja electrificada, para"
print "  burros."

elseif lang=english then

let i$=protagonist$(1):gosub @icon
#      <------------------------------>
print  " You, the heroic damsel. Cross"
print "  the minefields using the"
print "  the cursor keys."
let i$=bill_udg$:gosub @icon
#      <------------------------------>
print  " Bill the worm: It's on the"
print "  last field. Rescue it."
let i$=mine_udg$:gosub @icon
#      <------------------------------>
print  " Mines: On the status bar you"
print "  will see how many of them are"
print "  near you."
let i$=fence_udg$:gosub @icon
#      <------------------------------>
print  " Electrified fence, for asses."

elseif lang=interlingue then

let i$=protagonist$(1):gosub @icon
#      <------------------------------>
print  " Vu, li heroic senioretta."
print "  Transea li campes de mines"
print "  usante li fleche-claves."
let i$=bill_udg$:gosub @icon
#      <------------------------------>
print  " Bill li verme: It es in li"
print "  ultim camp. Salva it."
let i$=mine_udg$:gosub @icon
#      <------------------------------>
print  " Mines: Sur li statu-barre Vu"
print "  va vider quantes es apu Vos."
let i$=fence_udg$:gosub @icon
#      <------------------------------>
print  " Palissade electrificat, por"
print "  ásines."

else // Esperanto

let i$=protagonist$(1):gosub @icon
#      <------------------------------>
print  " Vi, la heroa fraŭlino."
print "  Trairu la min-kampojn"
print "  uzante la sago-klavojn."
let i$=bill_udg$:gosub @icon
#      <------------------------------>
print  " Bill la vermo: Ĝi estas en la"
print "  lasta kampo. Savu ĝin."
let i$=mine_udg$:gosub @icon
#      <------------------------------>
print  " Minoj: Sube de la ekrano vi"
print "  vidos kiomaj estas apud vi."
let i$=fence_udg$:gosub @icon
#      <------------------------------>
print  " Elektrigita barilo, por"
print "  azenoj."

endif

gosub @lang_footer_menu

do
  poke 23560,0:\
  pause 0:\
  let i=peek 23560:\
  gosub @check_new_lang:\
  if new_lang then :\
    goto @instructions
loop while lang_key

randomize udg1:\
goto @select_udg_set // then return

// ==============================================================
// subrutine: texts {{{1

// Set the text translations

@texts

for i=1 to langs:\
  read lang,yes$(lang),no$(lang):\
next i

for i=1 to langs
  read lang
  do
    border black
    read t
    if not t then exit do
    read t$:\
    let text$(lang,t)=t$:\
    let text_len(lang,t)=len(t$):\
    border white
  loop
next i

return

data spanish,"S","N"
data english,"Y","N"
data interlingue,"Y","N"
data esperanto,"J","N"

data spanish

data title_txt,"   REGRESO AL CAMPO DE MINAS"
data instructions_txt,"Instrucciones"
data start_txt,"Jugar"
data credits_txt,"Sobre este programa"
data level_txt,"Campo"
data near_mines_txt,"Minas"
data points_txt,"Puntos"
data record_txt,"Récor"
data safe_zone_txt,"<<<<<<<< ZONA SEGURA >>>>>>>>>"
data putting_mines_txt,"Poniendo minas..."
data replay_controls_1_txt,"REPETICIÓN: [P]ausa [F]in"
data replay_controls_2_txt,"Pulsa una tecla para seguir"
data you_need_key_txt,"Necesitas la llave para salir"
data open_door_txt,"Puerta abierta"
data new_record_txt,"¡Un nuevo récord!"
data enter_initials_txt,"Introduce tus iniciales"
data press_key_txt,"Pulsa una tecla para ir al menú"
data two_thousand_txt,"    ¡Dos mil puntos extra!"
data 0 // end of data

data english

data title_txt,"      BACK TO THE MINEFIELD"
data instructions_txt,"Instructions"
data start_txt,"Play"
data credits_txt,"About this program"
data level_txt,"Field"
data near_mines_txt,"Mines"
data points_txt,"Score"
data record_txt,"High"
data safe_zone_txt,"<<<<<<<<< SAFE ZONE >>>>>>>>>>"
data putting_mines_txt,"Putting mines..."
data replay_controls_1_txt,"REPETITION: [P]ause [F]inish"
data replay_controls_2_txt,"Press any key to continue"
data you_need_key_txt,"Yo need the key to exit"
data open_door_txt,"Open door"
data new_record_txt,"A new record!"
data enter_initials_txt,"Enter your initials"
data press_key_txt,"Press any key to go to the menu"
data two_thousand_txt,"   Two thousand extra points!"
data 0 // end of data

data interlingue

data title_txt,"   REVENIDA AL CAMP DE MINES"
data instructions_txt,"Instructiones"
data start_txt,"Luder"
data credits_txt,"Pri ti programa"
data level_txt,"Camp"
data near_mines_txt,"Mines"
data points_txt,"Puntes"
data record_txt,"Record"
data safe_zone_txt,"<<<<<<<< ZONA SECUR >>>>>>>>>>"
data putting_mines_txt,"Colocante mines..."
data replay_controls_1_txt,"REPETITION: [P]ause [F]ine"
data replay_controls_2_txt,"Presse un clave por continuar"
data you_need_key_txt,"Vu besona li clave por exear"
data open_door_txt,"Porta apert"
data new_record_txt,"Un nov record!"
data enter_initials_txt,"Inscri vu initiales"
data press_key_txt,"Presse un clave por ear al menú"
data two_thousand_txt,"     Du mil extra puntes!"
data 0 // end of data

data esperanto

data title_txt,"     REVENO AL LA MIN-KAMPO"
data instructions_txt,"Instrukcioj"
data start_txt,"Ludi"
data credits_txt,"Pri ĉi programo"
data level_txt,"Kampo"
data near_mines_txt,"Minoj"
data points_txt,"Poentoj"
data record_txt,"Rikordo"
data safe_zone_txt,"<<<<<<<< SEKURA ZONO >>>>>>>>>"
data putting_mines_txt,"Metante minojn..."
data replay_controls_1_txt,"RIPETO: [P]aŭzi [F]ini"
data replay_controls_2_txt,"Premu klavon por plui"
data you_need_key_txt,"Vi bezonas la ŝlosilon por eliri"
data open_door_txt,"Pordo malfermita"
data new_record_txt,"Nova rikordo!"
data enter_initials_txt,"Tajpu viajn inicialojn"
data press_key_txt,"Premu klavon por iri al la menuo"
data two_thousand_txt,"    Du mil kromaj poentoj!"
data 0 // end of data

// ==============================================================
// subroutine: the_nest {{{1

@the_nest:

print at nest_row,nest_col+3;fence_udg$;\
      at nest_row,nest_col+5;fence_udg$;\
      at nest_row+1,nest_col+3;fence_udg$;\
      at nest_row+1,nest_col+5;fence_udg$;\
      at nest_row+2,nest_col+3;fence_udg$;\
      at nest_row+2,nest_col+5;fence_udg$;\
      at nest_row+3,nest_col;fence$(1 to 4);\
      at nest_row+3,nest_col+5;fence$(1 to 4);\
      at nest_row+5,nest_col;fence$(1 to 4);\
      at nest_row+5,nest_col+5;fence$(1 to 4);\
      at nest_row+6,nest_col+3;fence_udg$;\
      at nest_row+6,nest_col+5;fence_udg$;\
      at nest_row+7,nest_col+3;fence_udg$;\
      at nest_row+7,nest_col+5;fence_udg$;\
      at nest_row+8,nest_col+3;fence_udg$;\
      at nest_row+8,nest_col+5;fence_udg$:\
return

// ==============================================================
// subroutine: last level {{{1

@last_level:

let nest_width=9:\
let nest_height=9:\ // XXX TODO -- Not used.
let nest_row=int(rnd*7)+3:\
let nest_col=int(rnd*20)+2

beep .3,-12

print at top_fence_row,door_col;fence$(1 to 3)

gosub @the_nest

// XXX TODO --
# print at 11,nest_col+5; ink ground_color; paper ground_color;mine_udg$

print \
  at nest_row+int(nest_width/2),nest_col+int(nest_width/2);\
  bill_udg$

return

// ==============================================================
// subroutine: check new language {{{1

// Check if a given key is the key of language in a menu. If so,
// change the corresponding variables.

// Input:
//  i = key ASCII code ("1"=en; "2"=es; "3"=eo; "4"=ie; other)
// Output:
//  new_lang = 0 : no language change
//  new_lang = 1 : language change
//  lang_key = 0 : i is not a language key code
//  lang_key = 1 : i is a language key code
//  lang = current or new language

@check_new_lang

let new_lang=0:\
let lang_key=i>ASCII_0 and i<=max_lang_key:\
if lang_key then:\
  if lang<>i then:\
    let new_lang=1:\
    let lang=i-ASCII_0:\
    let udg_chars=udg3*(lang=esperanto)+\
                  udg2*(lang<>esperanto):\
    randomize udg_chars:\
    goto @select_udg_set // then return

return

// ==============================================================
// Sounds {{{1

// XXX TODO --

@sound:
for i=0 to 13:\
  read d: out 65333,i: out 49149,d:\
next i:\
return

@applause_sound:
data 0,0,0,0,0,0,30,64,15,16,15,0,7,24

@no_sound:
data 0,0,0,0,0,0,0,0,0,0,0,0,0,0

// ==============================================================
// Debugging tests {{{1

# rem --------------------------------
# paper red
# ink white
# print at 15,15;mine_udg$;" (mine)=";
# gosub @graphics_font
# let tmp= code screen$(15,15)
# gosub @default_font
# print tmp

# print at 16,15;"  (space)=";
# gosub @graphics_font
# let tmp= code screen$(16,15)
# gosub @default_font
# print tmp

# gosub @last_level
# for n=1 to langs
#   let lang=n
#   gosub @rescue
#   pause 0
# next n

# stop

// ==============================================================
// Vimclair BASIC directives {{{1

# #tapmaker zmakebas
#tapmaker bas2tap

#filename DISK
#run 1

// ---------------------------------------------
// Convert UTF-8 characters to UDG codes {{{2

# UDG set 2: Spanish:
#vim %substitute,á,\\A,gIe
#vim %substitute,Á,\\B,gIe
#vim %substitute,é,\\C,gIe
#vim %substitute,É,\\D,gIe
#vim %substitute,í,\\E,gIe
#vim %substitute,Í,\\F,gIe
#vim %substitute,ó,\\G,gIe
#vim %substitute,Ó,\\H,gIe
#vim %substitute,ú,\\I,gI
#vim %substitute,Ú,\\J,gIe
#vim %substitute,ñ,\\K,gIe
#vim %substitute,Ñ,\\L,gIe
#vim %substitute,ü,\\M,gIe
#vim %substitute,Ü,\\N,gIe
#vim %substitute,¿,\\O,gIe
#vim %substitute,¡,\\P,gIe
# %substitute,º,\\Q,gIe
#vim %substitute,«,\\R,gIe
#vim %substitute,»,\\S,gIe

# UDG set 3: Esperanto
#vim %substitute,Ĉ,\\A,gIe
#vim %substitute,ĉ,\\B,gIe
#vim %substitute,Ĝ,\\C,gIe
#vim %substitute,ĝ,\\D,gIe
#vim %substitute,Ĥ,\\E,gIe
#vim %substitute,ĥ,\\F,gIe
#vim %substitute,Ĵ,\\G,gIe
#vim %substitute,ĵ,\\H,gIe
#vim %substitute,Ŝ,\\I,gIe
#vim %substitute,ŝ,\\J,gIe
#vim %substitute,Ŭ,\\K,gIe
#vim %substitute,ŭ,\\L,gIe

// ---------------------------------------------
// Shorten string variable names {{{2

#vim %substitute,\<mine_udg\$,a$,gIe
#vim %substitute,\<blank_row\$,b$,gIe
#vim %substitute,\<bill_udg\$,c$,gIe
#vim %substitute,\<mine_char\$,d$,gIe
#vim %substitute,\<bottle_udg\$,e$,gIe
#vim %substitute,\<fence\$,f$,gIe
#vim %substitute,\<flower_udg\$,g$,gIe
#vim %substitute,\<record_player\$,h$,gIe
#vim %substitute,\<fence_udg\$,j$,gIe
#vim %substitute,\<can_udg\$,k$,gIe
#vim %substitute,\<text\$,l$,gIe
#vim %substitute,\<replay_controls\$,m$,gIe
#vim %substitute,\<no\$,n$,gIe
#vim %substitute,\<key_udg\$,o$,gIe
#vim %substitute,\<protagonist\$,p$,gIe
#vim %substitute,\<dead_protagonist_udg\$,q$,gIe
#vim %substitute,\<trail\$,t$,gIe
#vim %substitute,\<blank_field_row\$,u$,gIe
#vim %substitute,\<version\$,v$,gIe
#vim %substitute,\<message\$,w$,gIe
#vim %substitute,\<thing_udg\$,x$,gIe
#vim %substitute,\<yes\$,y$,gIe

// ---------------------------------------------
//
// Shorten numeric array names {{{2

#vim %substitute,\<thing_col\>,c,gIe
#vim %substitute,\<detector_color\>,d,gIe
#vim %substitute,\<thing_ink\>,i,gIe
#vim %substitute,\<thing_row\>,r,gIe
#vim %substitute,\<text_len\>,l,gIe

// ---------------------------------------------
// Replace numeric constants {{{2

// Generic colors
#vim %substitute,\<black\>,0,gIe
#vim %substitute,\<blue\>,1,gIe
#vim %substitute,\<red\>,2,gIe
#vim %substitute,\<magenta\>,3,gIe
#vim %substitute,\<green\>,4,gIe
#vim %substitute,\<cyan\>,6,gIe
#vim %substitute,\<yellow\>,6,gIe
#vim %substitute,\<white\>,7,gIe
#vim %substitute,\<transparent\>,8,gIe
#vim %substitute,\<contrast\>,9,gIe

// Style colors
#vim %substitute,\<border_color\>,0,gIe // black
#vim %substitute,\<bottle_color\>,4,gIe // green
#vim %substitute,\<can_color\>,0,gIe // black
#vim %substitute,\<ground_color\>,6,gIe // yelow
#vim %substitute,\<flower_color\>,2,gIe // red
#vim %substitute,\<path_color\>,7,gIe // white

// Text identifiers
#vim %substitute,\<press_key_txt\>,1,gIe
#vim %substitute,\<credits_txt\>,3,gIe
#vim %substitute,\<enter_initials_txt\>,4,gIe
#vim %substitute,\<instructions_txt\>,5,gIe
#vim %substitute,\<level_txt\>,6,gIe
#vim %substitute,\<near_mines_txt\>,7,gIe
#vim %substitute,\<new_record_txt\>,8,gIe
#vim %substitute,\<open_door_txt\>,9,gIe
#vim %substitute,\<points_txt\>,10,gIe
#vim %substitute,\<putting_mines_txt\>,11,gIe
#vim %substitute,\<record_txt\>,12,gIe
#vim %substitute,\<replay_controls_1_txt\>,13,gIe
#vim %substitute,\<replay_controls_2_txt\>,14,gIe
#vim %substitute,\<safe_zone_txt\>,15,gIe
#vim %substitute,\<start_txt\>,16,gIe
#vim %substitute,\<title_txt\>,17,gIe
#vim %substitute,\<you_need_key_txt\>,18,gIe
#vim %substitute,\<two_thousand_txt\>,19,gIe

// Number of texts
#vim %substitute,\<texts\>,19,gIe

// Other
#vim %substitute,\<ASCII_0\>,48,gIe

// ---------------------------------------------
// Shorten numeric variable names {{{2

#vim %substitute,\<bottom_fence_row\>,bfr,gIe
#vim %substitute,\<bill_char\>,bic,gIe
#vim %substitute,\<bottle_char\>,boc,gIe
#vim %substitute,\<back_surrounding_mines\>,bsm,gIe
#vim %substitute,\<bottom_safe_row\>,bsr,gIe
#vim %substitute,\<can_char\>,cac,gIe
#vim %substitute,\<door_closed\>,dcl,gIe
#vim %substitute,\<door_col\>,dco,gIe
#vim %substitute,\<english\>,en,gIe
#vim %substitute,\<enter_time\>,ent,gIe
#vim %substitute,\<esperanto\>,eo,gIe
#vim %substitute,\<spanish\>,es,gIe
#vim %substitute,\<exit_time\>,ext,gIe
#vim %substitute,\<fence_char\>,fec,gIe
#vim %substitute,\<first_level\>,fl,gIe
#vim %substitute,\<flower_char\>,flc,gIe
#vim %substitute,\<found_char\>,foc,gIe
#vim %substitute,\<front_surrounding_mines\>,fsm,gIe
#vim %substitute,\<interlingue\>,ie,gIe
#vim %substitute,\<key_col\>,kc,gIe
#vim %substitute,\<key_char\>,kec,gIe
#vim %substitute,\<key_row\>,kr,gIe
#vim %substitute,\<lang_key\>,lk,gIe
#vim %substitute,\<last_level\>,ll,gIe
#vim %substitute,\<left_surrounding_mines\>,lsm,gIe
#vim %substitute,\<mine_char\>,mic,gIe
#vim %substitute,\<max_lang_key\>,ml,gIe
#vim %substitute,\<message_row\>,mr0,gIe
#vim %substitute,\<mine_row\>,mr1,gIe
#vim %substitute,\<mined_rows\>,mr2,gIe
#vim %substitute,\<nest_col\>,nc,gIe
#vim %substitute,\<nest_height\>,nh,gIe
#vim %substitute,\<new_lang\>,nl,gIe
#vim %substitute,\<nest_row\>,nr,gIe
#vim %substitute,\<nest_width\>,nw,gIe
#vim %substitute,\<next_text\>,nt,gIe
#vim %substitute,\<old_col\>,ocol,gIe
#vim %substitute,\<old_row\>,orow,gIe
#vim %substitute,\<protagonist_frame\>,pf,gIe
#vim %substitute,\<paused_replay\>,pr,gIe
#vim %substitute,\<quit_replay\>,qr,gIe
#vim %substitute,\<record\>,r,gIe
#vim %substitute,\<right_surrounding_mines\>,rsm,gIe
#vim %substitute,\<score\>,s,gIe
#vim %substitute,\<start_col\>,sc,gIe
#vim %substitute,\<surrounding_mines\>,sm,gIe
#vim %substitute,\<things\>,th,gIe
#vim %substitute,\<top_fence_row\>,tfr,gIe
#vim %substitute,\<top_mined_row\>,tmr,gIe
#vim %substitute,\<time_score\>,ts,gIe
#vim %substitute,\<top_safe_row\>,tsr,gIe
#vim %substitute,\<texts\>,tx,gIe
#vim %substitute,\<udg1\>,u1,gIe
#vim %substitute,\<udg2\>,u2,gIe
#vim %substitute,\<udg3\>,u3,gIe
#vim %substitute,\<udg_chars\>,uc,gIe
#vim %substitute,\<udg1_font\>,uf,gIe
#vim %substitute,\<udg1_font_high\>,uh,gIe
#vim %substitute,\<udg1_font_low\>,ul,gIe
# #vim %substitute,\<walking_mine_col\>,wmc,gIe
# #vim %substitute,\<walking_mine_row\>,wmr,gIe
# #vim %substitute,\<walking_mine_step\>,wms,gIe

// ---------------------------------------------
// Shorten function names {{{2

#vim %substitute,\<\(\(def\)\?\s*fn \)\+random_row\>,\1 r,gIe
#vim %substitute,\<\(\(def\)\?\s*fn \)\+random_col\>,\1 c,gIe
#vim %substitute,\<\(\(def\)\?\s*fn \)\+score\$\>,\1 z$,gIe
#vim %substitute,\<\(\(def\)\?\s*fn \)\+seconds\>,\1 s,gIe
#vim %substitute,\<\(\(def\)\?\s*fn \)\+txt\$,\1 t$,gIe

// vim: filetype=vimclairbasic:fileencoding=utf8

; udg.z80s
;
; This file is part of
; Back to the minefield
; By Marcos Cruz (programandala.net)
;
; User Defined Graphics

; Last modified 202005092223
; See change log at the end of the file

; ==============================================================
; UDG bank 1: game graphics

  org 0

; fake space, needed when the UDG set is used as font by `screen$()`:

defs 8,0

bank1 equ $

; A -- protagonist: XXX OLD
defb %00011000
defb %00011000
defb %00100100
defb %11000011
defb %11000011
defb %00100100
defb %00011000
defb %00011000

; B -- protagonist carrying a damsel: XXX OLD
defb %01011010
defb %01011010
defb %00100100
defb %11011011
defb %11011011
defb %00100100
defb %01011010
defb %10011001

; C -- exploding mine:
defb %10100101
defb %10100101
defb %01000010
defb %01011010
defb %00101000
defb %10111101
defb %01111110
defb %10000001

; D -- walking damsel 1:
defb %00001000
defb %01010100
defb %01001001
defb %00111110
defb %00011000
defb %00111100
defb %01111110
defb %01111110

; E -- walking damsel 2:
defb %00010000
defb %00101010
defb %10010010
defb %01111100
defb %00011000
defb %00111100
defb %01111110
defb %01111110

; F -- fence:
defb %00001000
defb %00010000
defb %00001000
defb %10111010
defb %01011101
defb %00010000
defb %00001000
defb %00010000

; G -- protagonist carrying a damsel: XXX OLD
defb %01011010
defb %01011010
defb %00100100
defb %11011011
defb %11011011
defb %00100100
defb %01011010
defb %10011001

; H -- walking mine: XXX OLD
defb %00000000
defb %00000000
defb %00000000
defb %00000000
defb %00111100
defb %01111110
defb %10100101
defb %10100101

; I -- saluting damsel 1
defb %00001000
defb %01010100
defb %01001000
defb %00111100
defb %00011010
defb %00111100
defb %01111110
defb %01111110

; J -- saluting damsel 2
defb %00001000
defb %10010100
defb %01001000
defb %00111100
defb %00011010
defb %00111100
defb %01111110
defb %01111110

; K -- fence door?: XXX OLD
defb %00000000
defb %00000000
defb %00000000
defb %00000000
defb %00000000
defb %00000000
defb %01010101
defb %10101010

; L -- dead damsel

if 0

defb %00010000
defb %00101010
defb %00010010
defb %01111100
defb %10010000
defb %00111000
defb %10100101
defb %01100110

else

defb %01100110
defb %10100101
defb %00111000
defb %10010000
defb %01111100
defb %00010010
defb %00101010
defb %00010000

endif

; M -- miner: XXX OLD
defb %11000000
defb %00100000
defb %00010010
defb %00001101
defb %00111111
defb %00010000
defb %00100000
defb %11000000

; N -- Bill:
defb %00000000
defb %00000000
defb %11000000
defb %11000000
defb %01001110
defb %01001010
defb %01011011
defb %01110000

; O -- mine:
defb %00000000
defb %00000000
defb %00000000
defb %00000000
defb %00111100
defb %01111110
defb %00000000
defb %00000000

; P -- key:
defb %00111000
defb %00011000
defb %00111000
defb %00001000
defb %00001000
defb %00011100
defb %00100010
defb %00011100

; Q -- flower:
defb %00110110
defb %01001001
defb %00110110
defb %01011100
defb %00101000
defb %00001010
defb %00001100
defb %00001000

; R -- can:
defb %00111100
defb %01100110
defb %01011010
defb %01000010
defb %01000010
defb %01000010
defb %00111100
defb %00000000

; S -- bottle:
defb %00011000
defb %00011000
defb %00111100
defb %00111100
defb %00111100
defb %00111100
defb %00111100
defb %00111100

; ==============================================================
; UDG bank 2: Spanish letters and symbols
; (vowels with accute are used by Interlingue too)

  org bank1+21*8

bank2 equ $

; A -- a accute:
defb %00001000
defb %00010000
defb %00111000
defb %00000100
defb %00111100
defb %01000100
defb %00111100
defb %00000000
; B -- A accute:
defb %00000100
defb %00001000
defb %00111100
defb %01000010
defb %01111110
defb %01000010
defb %01000010
defb %00000000
; C -- e acute:
defb %00001000
defb %00010000
defb %00111000
defb %01000100
defb %01111000
defb %01000000
defb %00111100
defb %00000000
; D -- E accute:
defb %00000100
defb %00001000
defb %01111110
defb %01000000
defb %01111100
defb %01000000
defb %01111110
defb %00000000
; E -- i accute:
defb %00001000
defb %00010000
defb %00000000
defb %00110000
defb %00010000
defb %00010000
defb %00111000
defb %00000000
; F -- I accute:
defb %00000100
defb %00001000
defb %00111110
defb %00001000
defb %00001000
defb %00001000
defb %00111110
defb %00000000
; G -- o accute:
defb %00001000
defb %00010000
defb %00111000
defb %01000100
defb %01000100
defb %01000100
defb %00111000
defb %00000000
; H -- O accute:
defb %00001000
defb %00010000
defb %00111100
defb %01000010
defb %01000010
defb %01000010
defb %00111100
defb %00000000
; I -- u accute:
defb %00001000
defb %00010000
defb %01000100
defb %01000100
defb %01000100
defb %01000100
defb %00111000
defb %00000000
; J -- U accute:
defb %00000100
defb %01001010
defb %01000010
defb %01000010
defb %01000010
defb %01000010
defb %00111100
defb %00000000
; K -- n tilde:
defb %00000000
defb %01111000
defb %00000000
defb %01111000
defb %01000100
defb %01000100
defb %01000100
defb %00000000
; L -- N tilde:
defb %00111100
defb %00000000
defb %01100010
defb %01010010
defb %01001010
defb %01000110
defb %01000010
defb %00000000
; M -- u umlaut:
defb %01000100
defb %00000000
defb %01000100
defb %01000100
defb %01000100
defb %01000100
defb %00111000
defb %00000000
; N -- U umlaut:
defb %01000010
defb %00000000
defb %01000010
defb %01000010
defb %01000010
defb %01000010
defb %00111100
defb %00000000
; O -- inversed question mark:
defb %00000000
defb %00010000
defb %00000000
defb %00010000
defb %00100000
defb %01000010
defb %00111100
defb %00000000
; P -- inversed exclamation mark:
defb %00000000
defb %00001000
defb %00000000
defb %00001000
defb %00001000
defb %00001000
defb %00001000
defb %00000000
; Q -- º:
defb %00011000
defb %00100100
defb %00011000
defb %00000000
defb %00111100
defb %00000000
defb %00000000
defb %00000000
; R -- «
defb %00000000
defb %00000000
defb %00010010
defb %00100100
defb %01001000
defb %00100100
defb %00010010
defb %00000000
; S -- »:
defb %00000000
defb %00000000
defb %01001000
defb %00100100
defb %00010010
defb %00100100
defb %01001000
defb %00000000

; ==============================================================
; UDG bank 3: Esperanto letters

  org bank2+21*8

bank3 equ $

; A -- Ĉ
defb %00001000
defb %00010100
defb %00111100
defb %01000010
defb %01000000
defb %01000010
defb %00111100
defb %00000000

; B -- ĉ
defb %00001000
defb %00010100
defb %00011100
defb %00100000
defb %00100000
defb %00100000
defb %00011100
defb %00000000

; C -- Ĝ
defb %00001000
defb %00010100
defb %00111110
defb %01000000
defb %01001110
defb %01000010
defb %00111100
defb %00000000

; D -- ĝ
defb %00010000
defb %00101000
defb %00111100
defb %01000100
defb %01000100
defb %00111100
defb %00000100
defb %00111000

; E -- Ĥ
defb %00001000
defb %00010100
defb %01000010
defb %01000010
defb %01111110
defb %01000010
defb %01000010
defb %00000000

; F -- ĥ
defb %00001000
defb %01010100
defb %01000000
defb %01111000
defb %01000100
defb %01000100
defb %01000100
defb %00000000

; G -- Ĵ
defb %00000100
defb %00001010
defb %00000000
defb %00000010
defb %01000010
defb %01000010
defb %00111100
defb %00000000

; H -- ĵ
defb %00000100
defb %00001010
defb %00000000
defb %00000100
defb %00000100
defb %00000100
defb %00100100
defb %00011000

; I -- Ŝ
defb %00001000
defb %00010100
defb %00111110
defb %01000000
defb %00111100
defb %00000010
defb %01111100
defb %00000000

; J -- ŝ
defb %00010000
defb %00101000
defb %00111000
defb %01000000
defb %00111000
defb %00000100
defb %01111000
defb %00000000

; K -- Ŭ
defb %00010100
defb %00001000
defb %01000010
defb %01000010
defb %01000010
defb %01000010
defb %00111100
defb %00000000

; L -- ŭ
defb %00101000
defb %00010000
defb %01000100
defb %01000100
defb %01000100
defb %01000100
defb %00111000
defb %00000000

; ==============================================================
; Change log

; 2016-06-09: Start, converted to Z80 from the BASIC source.
;
; 2016-06-15: Add the graphic of the key.
;
; 2016-06-16: Add the graphic of the flower.
;
; 2016-06-17: Add fake space; add the graphic of the dead damsel.
;
; 2016-06-18: Modify the dead damsel.
;
; 2019-03-10: Add a third UDG set with the Esperanto letters.
;
; 2019-03-12: Modify the arms of the walking damsel.
;
; 2019-03-25: Improve the dress of the walking damsel. Add a graphic of a
; saluting damsel, for the end of the level.
;
; 2020-05-09: Convert to binary patterns the definitions that still were
; decimal data.

# Makefile
#
# This file is part of
# Back to the minefield
# By Marcos Cruz (programandala.net)

# Last modified 20230405T1037+0200
# See change log at the end of the file

# ==============================================================
# Requirements

# The following programs must be installed in your system:

# Asciidoctor (by Dan Allen, Sarah White et al.)
# http://asciidoctor.org

# Pasmo, by Julián Albo
# http://pasmo.speccy.org/

# Vimclair BASIC, by Marcos Cruz (programandala.net)
# http://programandala.net/en.program.vimclair_basic.html

# ==============================================================
# Main {{{1

.PHONY: all
all: target/back_to_the_minefield.tap

clean:
    rm -f target/* tmp/*

# ==============================================================
# Create a TAP file {{{1

tmp/udg.tap: src/udg.z80s
    pasmo --name UDG.BIN --tap $< $@ 

tmp/main.tap: src/back_to_the_minefield.vbas
    vbas2tap $< ;\
    mv src/back_to_the_minefield.bas tmp/main.bas;\
    mv src/back_to_the_minefield.tap $@

target/back_to_the_minefield.tap: tmp/main.tap tmp/udg.tap
    cat $^ > $@

# ==============================================================
# Change log

# 2016-06-04: Start.
#
# 2016-06-06: Update.
#
# 2016-06-07: Update.
#
# 2016-06-09: Improve with UDG defined in Z80 source. Create also a DSK disk
# image for +3.
#
# 2016-06-13: Use start line 1 and increment 1.
#
# 2016-06-19: Update: the source has been converted to Vimclair BASIC.
#
# 2017-12-30: Update layout.
#
# 2019-03-10: Update after the renaming of the project.
#
# 2019-03-12: Make the TAP in the target directory.
#
# 2020-12-24: Online documentation.
#
# 2023-04-05: Remove online documentation, after converting the repo from
# Fossil to Mercurial.

Download

The current development version of the project can be downloaded from: