############################################################################
#
#	File:     missile.icn
#
#	Subject:  Program to play missile command game
#
#	Author:   Chris Tenaglia
#
#	Date:     June 14, 1994
#
############################################################################
#
#   This file is in the public domain.
#
############################################################################
#
#  Here is a cheap attempt at a Missile Command game.
#
#  I've run it under Icon V8.7 under VMS, Unix, and V8.5 under MS-DOS.
#  
#  Here are some things you'll need to know. There is a delay() procedure
#  that keeps the game running at a steady pace. delay() is built into
#  V8.7 on VMS and unix. Under DOS you'll need to roll your own.
#  The program also uses ansi escape sequences. Also to play use 7, 8, and 9
#  to launch a # missile. 7 is leftward, 8 is straight, and 9 is right. A bug
#  in the Ultrix version (kbhit() and getch()) requires double pressing the
#  buttons. I think q will quit the game early.
#  
#  Have Fun!
#  
############################################################################
#
#  Links:  random
#
############################################################################

link random

global bonus,             # bonus missile threshhold
       score,             # number of missiles shot down
       munitions,         # munitions supply (# of defensive missiles)
       missilef,          # whether enemy missile is launched flag
       missilex,          # x position of enemy missile
       missiley,          # y position of enemy missile
       incm,              # x increment of enemy missile
       abmf,              # whether defensive missile fired flag
       abmx,              # x position of defensive missile
       abmy,              # y position of defensive missile
       abmix              # x increment of defensive missle

procedure main()
  infrastructure()        # set up defaults, globals, and munitions
  banner()                # output initial banner
  repeat
    {
    draw_base()           # initially draw base
    repeat
      {
      enemy_launch()      # possible enemy attack
      friendly_fire()     # possible defensive attack
      animate()           # draw action if any
      sense_status()      # sense status
      delay(1000)         # pace the game
      }
    }
  stop("\7\e[0m",at(12,24),"Game Over. \e[5mInsert another quarter.\e[0m\e[?25h\e=")
  end

#
# set up all the initial defaults
#
procedure infrastructure()
  bonus    := 22
  missilef := 0
  missilex := 0
  missiley := 0
  incm     := 0
  abmf     := 0
  abmx     := 0
  abmy     := 0
  score    := 0
  randomize()
  munitions:= 10 + ?5
  end

#
# draw the initial environment
#
procedure draw_base()
  write("\e[?25l\e>\e[?5l\e[0;1;33;44m\e[2J\e[H                 S.D.I. OUTPOST        [TACTICAL SITUATION DISPLAY]")
  writes(at(23,1),repl("#",79))
  writes(at(24,1),repl("=",79))
  writes(at(24,39),"/ \\",at(23,40),"^")
  writes(at(24,5)," Missiles Left : ",munitions," ")
  writes(at(24,60)," Score : ",score," ")
  end

#
# check and occasionally launch a missile
#
procedure enemy_launch()
  (?50 = 33) | fail
  if missilef = 1 then fail
  missilex := 1
  missiley := 1 + ?10
  missilef := 1
  incm     := ?3                                                  
  end

#
# coordinate launch of defensive missiles
#
procedure friendly_fire()
  local ambf, press

  kbhit() | fail
  press := getch()
  if abmf = 1 then
    {
    case press of
      {
      "1" | "4" | "7" | "l" | "L" : abmix := -2
      "2" | "5" | "8" | "s" | "S" : abmix :=  0
      "3" | "6" | "9" | "r" | "R" : abmix :=  2
      "q" | "Q" | "\e"                  : stop("\e[2J\e[H")
      default : writes("\7")
      }
    } else {
    ambf :=  1
    abmx := 40
    abmy := 22
    case press of
      {
      "1" | "4" | "7" | "l" | "L" : abmix := -2
      "2" | "5" | "8" | "s" | "S" : abmix :=  0
      "3" | "6" | "9" | "r" | "R" : abmix :=  2
      "q" | "Q" | "\e": stop("\e[2J\e[H",at(12,24),"Game Over. \e[5mInsert another quarter.\e[0m\e[?25h\e=")
      default : {
                writes("\7")
                fail
                }
      }
    if munitions <= 0 then
      stop(at(12,24),"Game Over. \e[5mInsert Another Quarter!\e[0m\e=\e[?25h")
    munitions -:= 1
    abmf       := 1
    writes(at(24,5)," Missiles Left : ",munitions," ")
    }
  end

#
# fly the missiles
#
procedure animate()
  local old_missilez

  static  old_abmx,
          old_abmy,
          old_missilex,
          old_missiley

  initial {
          old_abmx     := 0
          old_abmy     := 0
          old_missilez := 0
          old_missiley := 0
          }

  #
  # move the defensive missile if launched
  #
  if abmf = 1 then
    {
    writes(at(abmy,abmx),"*",at(old_abmy,old_abmx)," ")
    old_abmx := abmx
    old_abmy := abmy
    abmx    +:= abmix
    abmy    -:= 1
    if abmy < 2 then
      {
      writes(at(old_abmy,old_abmx)," ")
      abmf := 0
      abmx := 0
      abmy := 0
      }
    }

  #
  # move the offensive missile if launched
  #
  if missilef = 1 then
    {
    writes(at(missiley,missilex),"   =>")
    missilex +:= incm
    if missilex > 76 then
      {
      writes(at(missiley,76),"\e[K")
      missilef := 0                                 
      missilex := 0
      missiley := 0
      incm     := 0
      }
    }
  end

#
# sense for hits and handle explosions
#
procedure sense_status()
  local j
  static  junk
  initial junk := ["=%!*@",
                   "%^&(!",
                   "(@^$^",
                   "*)@%$",
                   "@&%^(#"]
  if missilef=1 & abmf=1 then
    {
    if abmy=missiley & (missilex < abmx < missilex+6) then
      {
      every 1 to 3 do
        {
        writes(at(abmy,abmx-4),"\e[?5h<<<<>>>>")  ; delay(2000)  # reverse screen
        writes(at(abmy,abmx-4),"\e[?5l>>>><<<<")  ; delay(2000)  # normal  screen
        }
      every j := abmy to 22 do
        {
        writes(at(j,abmx-3),?junk)
        delay(1000)
        }
      if abmx > 67 then abmx := 67   # handle edge of screen problem
      writes(at(23,abmx-3),"********")              ; delay(1000)
      writes(at(22,abmx-3),"\e[?5h||||||||")        ; delay(1000)
      writes(at(21,abmx-5),"\e[?5l. . . . . . .")   ; delay(1000)
      every j := 20 to abmy by -1 do writes(at(j,abmx-6),"\e[K")
      wait(2)
      score   +:= incm * (15 - missiley)
      if score > bonus then
        {
        writes(at(12,30),"\7\e[5mBONUS MISSILE EARNED!\e[0m")
        bonus     +:= 33
        munitions +:= 1
        delay(30000)
        }
      draw_base()
      abmf     := 0
      abmx     := 0
      abmy     := 0
      missilef := 0
      missilex := 0
      missiley := 0
      }
    }
  end
                    
#
# output initial banner for this game
#             
procedure banner()
  write("\e[0;1;33;44m\e[2J\e[H                                                                 ")
  write("                                                                 ")
  write("###############################################################################")
  write("                                                                 ")
  write("             ***   *   *  *****  ****    ***    ****  *****      ")
  write("           *   *  *   *    *    *   *  *   *  *        *         ")
  write("          *   *  *   *    *    ****   *   *   ***     *          ")
  write("         *   *  *   *    *    *      *   *      *    *           ")
  write("         ***    ***     *    *       ***   ****     *            ")
  write("                                                                 ")
  write("                ****          ****          ***                  ")
  write("              *              *   *          *                    ")
  write("              ****          *   *          *                     ")
  write("                 *         *   *          *                      ")
  write("            ****   **     ****   **     ***  **                  ")
  write("                                                                 ")
  write("                                                                 ")
  write("###############################################################################")
  wait(3)
  end

#
# move cursor to specified screen position
#
procedure at(row,column)
  return "\e[" || row || ";" || column || "f"
  end

#
# procedure to wait n seconds
#
procedure wait(n)
  delay(n * 10000)
  return
##  secs := &clock[-2:0] + n
##  if secs > 58 then secs -:= 60
##  repeat
##    {
##    now := &clock[-2:0]
##    if now > secs then break
##    }
##  return
  end
 
############################################################################
#                                                                #
# This procedure pulls all the elements (tokens) out of a line   #
# buffer and returns them in a list. a variable named 'chars'    #
# can be statically defined here or global. It is a cset that    #
# contains the valid characters that can compose the elements    #
# one wishes to extract.                                         #
#                                                                #
############################################################################
procedure parse(line,delims)
  local tokens
  static chars
  chars  := &cset -- delims
  tokens := []
  line ? while tab(upto(chars)) do put(tokens,tab(many(chars)))
  return tokens
  end

############################################################################
#                                                                #
# This procedure is terribly handy in prompting and getting      #
# an input string                                                #
#                                                                #
############################################################################
procedure input(prompt)
  writes(prompt)
  return read()
  end
