xref: /titanic_50/usr/src/lib/libshell/common/scripts/gnaw.sh (revision 3e14f97f673e8a630f076077de35afdd43dc1587)
17c2fbfb3SApril Chin#!/usr/bin/ksh93
27c2fbfb3SApril Chin
37c2fbfb3SApril Chin#
47c2fbfb3SApril Chin# CDDL HEADER START
57c2fbfb3SApril Chin#
67c2fbfb3SApril Chin# The contents of this file are subject to the terms of the
77c2fbfb3SApril Chin# Common Development and Distribution License (the "License").
87c2fbfb3SApril Chin# You may not use this file except in compliance with the License.
97c2fbfb3SApril Chin#
107c2fbfb3SApril Chin# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
117c2fbfb3SApril Chin# or http://www.opensolaris.org/os/licensing.
127c2fbfb3SApril Chin# See the License for the specific language governing permissions
137c2fbfb3SApril Chin# and limitations under the License.
147c2fbfb3SApril Chin#
157c2fbfb3SApril Chin# When distributing Covered Code, include this CDDL HEADER in each
167c2fbfb3SApril Chin# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
177c2fbfb3SApril Chin# If applicable, add the following below this CDDL HEADER, with the
187c2fbfb3SApril Chin# fields enclosed by brackets "[]" replaced with your own identifying
197c2fbfb3SApril Chin# information: Portions Copyright [yyyy] [name of copyright owner]
207c2fbfb3SApril Chin#
217c2fbfb3SApril Chin# CDDL HEADER END
227c2fbfb3SApril Chin#
237c2fbfb3SApril Chin
247c2fbfb3SApril Chin#
25*3e14f97fSRoger A. Faulkner# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
267c2fbfb3SApril Chin#
277c2fbfb3SApril Chin
287c2fbfb3SApril Chin#
297c2fbfb3SApril Chin# gnaw - a simple ksh93 technology demo
307c2fbfb3SApril Chin#
317c2fbfb3SApril Chin# Note that this script has been written with the main idea to show
327c2fbfb3SApril Chin# many of ksh93's new features (comparing to ksh88/bash) and not
337c2fbfb3SApril Chin# as an example of efficient&&clean script code (much of the code
347c2fbfb3SApril Chin# could be done more efficient using compound variables, this script
357c2fbfb3SApril Chin# focus is the usage of associative arrays).
367c2fbfb3SApril Chin#
377c2fbfb3SApril Chin
387c2fbfb3SApril Chin# Solaris needs /usr/xpg6/bin:/usr/xpg4/bin because the tools in /usr/bin are not POSIX-conformant
397c2fbfb3SApril Chinexport PATH=/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin
407c2fbfb3SApril Chin
417c2fbfb3SApril Chin# Make sure all math stuff runs in the "C" locale to avoid problems
427c2fbfb3SApril Chin# with alternative # radix point representations (e.g. ',' instead of
437c2fbfb3SApril Chin# '.' in de_DE.*-locales). This needs to be set _before_ any
447c2fbfb3SApril Chin# floating-point constants are defined in this script).
457c2fbfb3SApril Chinif [[ "${LC_ALL}" != "" ]] ; then
467c2fbfb3SApril Chin    export \
477c2fbfb3SApril Chin        LC_MONETARY="${LC_ALL}" \
487c2fbfb3SApril Chin        LC_MESSAGES="${LC_ALL}" \
497c2fbfb3SApril Chin        LC_COLLATE="${LC_ALL}" \
507c2fbfb3SApril Chin        LC_CTYPE="${LC_ALL}"
517c2fbfb3SApril Chin        unset LC_ALL
527c2fbfb3SApril Chinfi
537c2fbfb3SApril Chinexport LC_NUMERIC=C
547c2fbfb3SApril Chin
557c2fbfb3SApril Chinfunction print_setcursorpos
567c2fbfb3SApril Chin{
577c2fbfb3SApril Chin    print -n -- "${vtcode[cup_${1}_${2}]}"
587c2fbfb3SApril Chin}
597c2fbfb3SApril Chin
607c2fbfb3SApril Chinfunction beep
617c2fbfb3SApril Chin{
627c2fbfb3SApril Chin    ${quiet} || print -n -- "${vtcode["bel"]}"
637c2fbfb3SApril Chin}
647c2fbfb3SApril Chin
657c2fbfb3SApril Chinfunction fatal_error
667c2fbfb3SApril Chin{
677c2fbfb3SApril Chin    print -u2 "${progname}: $*"
687c2fbfb3SApril Chin    exit 1
697c2fbfb3SApril Chin}
707c2fbfb3SApril Chin
717c2fbfb3SApril Chin# Get terminal size and put values into a compound variable with the integer
727c2fbfb3SApril Chin# members "columns" and "lines"
737c2fbfb3SApril Chinfunction get_term_size
747c2fbfb3SApril Chin{
757c2fbfb3SApril Chin	nameref rect=$1
767c2fbfb3SApril Chin
777c2fbfb3SApril Chin	rect.columns=${ tput cols ; } || return 1
787c2fbfb3SApril Chin	rect.lines=${ tput lines ; }  || return 1
797c2fbfb3SApril Chin
807c2fbfb3SApril Chin	return 0
817c2fbfb3SApril Chin}
827c2fbfb3SApril Chin
837c2fbfb3SApril Chinfunction print_levelmap
847c2fbfb3SApril Chin{
857c2fbfb3SApril Chin    integer screen_y_offset=$1
867c2fbfb3SApril Chin    integer start_y_pos=$2 # start at this line in the map
877c2fbfb3SApril Chin    integer max_numlines=$3 # maximum lines we're allowed to render
887c2fbfb3SApril Chin    integer x
897c2fbfb3SApril Chin    integer y
907c2fbfb3SApril Chin    typeset line=""
917c2fbfb3SApril Chin
927c2fbfb3SApril Chin    print_setcursorpos 0 ${screen_y_offset}
937c2fbfb3SApril Chin
947c2fbfb3SApril Chin    for (( y=start_y_pos; (y-start_y_pos) < max_numlines && y < levelmap["max_y"] ; y++ )) ; do
957c2fbfb3SApril Chin        line=""
967c2fbfb3SApril Chin        for (( x=0 ; x < levelmap["max_x"] ; x++ )) ; do
977c2fbfb3SApril Chin            line+="${levelmap["${x}_${y}"]}"
987c2fbfb3SApril Chin        done
997c2fbfb3SApril Chin
1007c2fbfb3SApril Chin        print -- "${line} "
1017c2fbfb3SApril Chin    done
1027c2fbfb3SApril Chin
1037c2fbfb3SApril Chin    # print lines filled with spaces for each line not filled
1047c2fbfb3SApril Chin    # by the level map
1057c2fbfb3SApril Chin    line="${vtcode["spaceline"]:0:${levelmap["max_x"]}}"
1067c2fbfb3SApril Chin    for (( ; (y-start_y_pos) < max_numlines ; y++ )) ; do
1077c2fbfb3SApril Chin        print -- "${line} "
1087c2fbfb3SApril Chin    done
1097c2fbfb3SApril Chin    return 0
1107c2fbfb3SApril Chin}
1117c2fbfb3SApril Chin
1127c2fbfb3SApril Chinfunction level_completed
1137c2fbfb3SApril Chin{
1147c2fbfb3SApril Chin    integer i
1157c2fbfb3SApril Chin    typeset dummy
1167c2fbfb3SApril Chin    typeset render_buffer="$(
1177c2fbfb3SApril Chin    print -n -- "${vtcode["clear"]}"
1187c2fbfb3SApril Chin    cat <<ENDOFTEXT
1197c2fbfb3SApril Chin
1207c2fbfb3SApril Chin #       ######  #    #  ######  #
1217c2fbfb3SApril Chin #       #       #    #  #       #
1227c2fbfb3SApril Chin #       #####   #    #  #####   #
1237c2fbfb3SApril Chin #       #       #    #  #       #
1247c2fbfb3SApril Chin #       #        #  #   #       #
1257c2fbfb3SApril Chin ######  ######    ##    ######  ######
1267c2fbfb3SApril Chin
1277c2fbfb3SApril Chin             (Good job)
1287c2fbfb3SApril Chin
1297c2fbfb3SApril Chin     #####    ####   #    #  ######
1307c2fbfb3SApril Chin     #    #  #    #  ##   #  #
1317c2fbfb3SApril Chin     #    #  #    #  # #  #  #####
1327c2fbfb3SApril Chin     #    #  #    #  #  # #  #
1337c2fbfb3SApril Chin     #    #  #    #  #   ##  #
1347c2fbfb3SApril Chin     #####    ####   #    #  ######
1357c2fbfb3SApril Chin
1367c2fbfb3SApril Chin
1377c2fbfb3SApril ChinENDOFTEXT
1387c2fbfb3SApril Chin
1397c2fbfb3SApril Chin    printf "    SCORE: --> %s <--\n" "${player["score"]}"
1407c2fbfb3SApril Chin    printf "    LIVES: --> %s <--\n" "${player["lives"]}"
1417c2fbfb3SApril Chin    )"
1427c2fbfb3SApril Chin    print -- "${render_buffer}${end_of_frame}"
1437c2fbfb3SApril Chin
1447c2fbfb3SApril Chin    # wait five seconds and swallow any user input
1457c2fbfb3SApril Chin    for (( i=0 ; i < 50 ; i++ )) ; do
1467c2fbfb3SApril Chin        read -r -t 0.1 -n 1 dummy
1477c2fbfb3SApril Chin    done
1487c2fbfb3SApril Chin
1497c2fbfb3SApril Chin    print "Press any key to continue...${end_of_frame}"
1507c2fbfb3SApril Chin    # wait five secs or for a key
1517c2fbfb3SApril Chin    read -r -t 5 -n 1 dummy
1527c2fbfb3SApril Chin    return 0
1537c2fbfb3SApril Chin}
1547c2fbfb3SApril Chin
1557c2fbfb3SApril Chinfunction game_over
1567c2fbfb3SApril Chin{
1577c2fbfb3SApril Chin    typeset dummy
1587c2fbfb3SApril Chin    typeset render_buffer="$(
1597c2fbfb3SApril Chin    print -n -- "${vtcode["clear"]}"
1607c2fbfb3SApril Chin    cat <<ENDOFTEXT
1617c2fbfb3SApril Chin
1627c2fbfb3SApril Chin  ####     ##    #    #  ######
1637c2fbfb3SApril Chin #    #   #  #   ##  ##  #
1647c2fbfb3SApril Chin #       #    #  # ## #  #####
1657c2fbfb3SApril Chin #  ###  ######  #    #  #
1667c2fbfb3SApril Chin #    #  #    #  #    #  #
1677c2fbfb3SApril Chin  ####   #    #  #    #  ######
1687c2fbfb3SApril Chin
1697c2fbfb3SApril Chin            (LOSER!)
1707c2fbfb3SApril Chin
1717c2fbfb3SApril Chin  ####   #    #  ######  #####
1727c2fbfb3SApril Chin #    #  #    #  #       #    #
1737c2fbfb3SApril Chin #    #  #    #  #####   #    #
1747c2fbfb3SApril Chin #    #  #    #  #       #####
1757c2fbfb3SApril Chin #    #   #  #   #       #   #
1767c2fbfb3SApril Chin  ####     ##    ######  #    #
1777c2fbfb3SApril Chin
1787c2fbfb3SApril ChinENDOFTEXT
1797c2fbfb3SApril Chin
1807c2fbfb3SApril Chin    printf "\n    SCORE: --> %s <--\n" "${player["score"]}"
1817c2fbfb3SApril Chin    )"
1827c2fbfb3SApril Chin    print -r -- "${render_buffer}${end_of_frame}"
1837c2fbfb3SApril Chin
1847c2fbfb3SApril Chin    # wait five seconds and swallow any user input
1857c2fbfb3SApril Chin    for (( i=0 ; i < 50 ; i++ )) ; do
1867c2fbfb3SApril Chin        read -r -t 0.1 -n 1 dummy
1877c2fbfb3SApril Chin    done
1887c2fbfb3SApril Chin
1897c2fbfb3SApril Chin    print "Press any key to continue...${end_of_frame}"
1907c2fbfb3SApril Chin    # wait five secs or for a key
1917c2fbfb3SApril Chin    read -r -t 5 -n 1 dummy
1927c2fbfb3SApril Chin    return 0
1937c2fbfb3SApril Chin}
1947c2fbfb3SApril Chin
1957c2fbfb3SApril Chinfunction run_logo
1967c2fbfb3SApril Chin{
1977c2fbfb3SApril Chin    typeset render_buffer="$(
1987c2fbfb3SApril Chin    cat <<ENDOFTEXT
1997c2fbfb3SApril Chin
2007c2fbfb3SApril Chin #####  #     #    #    #     #   ###
2017c2fbfb3SApril Chin#     # ##    #   # #   #  #  #   ###
2027c2fbfb3SApril Chin#       # #   #  #   #  #  #  #   ###
2037c2fbfb3SApril Chin#  #### #  #  # #     # #  #  #    #
2047c2fbfb3SApril Chin#     # #   # # ####### #  #  #
2057c2fbfb3SApril Chin#     # #    ## #     # #  #  #   ###
2067c2fbfb3SApril Chin #####  #     # #     #  ## ##    ###
2077c2fbfb3SApril ChinENDOFTEXT
2087c2fbfb3SApril Chin    )"
2097c2fbfb3SApril Chin    print -- "${vtcode["clear"]}${render_buffer}"
2107c2fbfb3SApril Chin
2117c2fbfb3SApril Chin    # wait two seconds and swallow any user input
2127c2fbfb3SApril Chin    for (( i=0 ; i < 20 ; i++ )) ; do
2137c2fbfb3SApril Chin        read -r -t 0.1 -n 1 dummy
2147c2fbfb3SApril Chin    done
2157c2fbfb3SApril Chin
2167c2fbfb3SApril Chin    print "\n   (The KornShell 93 maze game)"
2177c2fbfb3SApril Chin
2187c2fbfb3SApril Chin    attract_mode
2197c2fbfb3SApril Chin    return 0
2207c2fbfb3SApril Chin}
2217c2fbfb3SApril Chin
2227c2fbfb3SApril Chinfunction attract_mode
2237c2fbfb3SApril Chin{
2247c2fbfb3SApril Chin(
2257c2fbfb3SApril Chin    # Now present some info, line-by-line in an endless loop
2267c2fbfb3SApril Chin    # until the user presses a key (we turn the "magic" return
2277c2fbfb3SApril Chin    # code for that)
2287c2fbfb3SApril Chin    integer -r magic_return_code=69
2297c2fbfb3SApril Chin    typeset line
2307c2fbfb3SApril Chin    IFS='' ; # Make sure we do not swallow whitespaces
2317c2fbfb3SApril Chin
2327c2fbfb3SApril Chin    while true ; do
2337c2fbfb3SApril Chin        (
2347c2fbfb3SApril Chin            redirect 5<&0
2357c2fbfb3SApril Chin
2367c2fbfb3SApril Chin        (cat <<ENDOFTEXT
2377c2fbfb3SApril Chin
2387c2fbfb3SApril Chin
2397c2fbfb3SApril Chin
2407c2fbfb3SApril Chin
2417c2fbfb3SApril Chin
2427c2fbfb3SApril Chin         ################
2437c2fbfb3SApril Chin     ########################
2447c2fbfb3SApril Chin   ############################
2457c2fbfb3SApril Chin  #######     ######     #######
2467c2fbfb3SApril Chin  ######     ######     ########
2477c2fbfb3SApril Chin  #######     ######     #######
2487c2fbfb3SApril Chin  ##############################
2497c2fbfb3SApril Chin  ##############################
2507c2fbfb3SApril Chin  ##############################
2517c2fbfb3SApril Chin  ##############################
2527c2fbfb3SApril Chin  ##############################
2537c2fbfb3SApril Chin  #########  ########  #########
2547c2fbfb3SApril Chin  #  ####      ####      ####  #
2557c2fbfb3SApril Chin
2567c2fbfb3SApril Chin
2577c2fbfb3SApril Chin
2587c2fbfb3SApril Chin
2597c2fbfb3SApril Chin
2607c2fbfb3SApril Chin
2617c2fbfb3SApril Chin           Written by
2627c2fbfb3SApril Chin
2637c2fbfb3SApril Chin          Roland Mainz
2647c2fbfb3SApril Chin    (roland.mainz@nrubsig.org)
2657c2fbfb3SApril Chin
2667c2fbfb3SApril Chin
2677c2fbfb3SApril Chin
2687c2fbfb3SApril Chin
2697c2fbfb3SApril Chin
2707c2fbfb3SApril Chin
2717c2fbfb3SApril Chin           ##############         
2727c2fbfb3SApril Chin      ########################    
2737c2fbfb3SApril Chin   #################**############
2747c2fbfb3SApril Chin  ################################
2757c2fbfb3SApril Chin ############################     
2767c2fbfb3SApril Chin ######################           
2777c2fbfb3SApril Chin ################                 
2787c2fbfb3SApril Chin ######################           
2797c2fbfb3SApril Chin ############################     
2807c2fbfb3SApril Chin  ################################
2817c2fbfb3SApril Chin   ############################## 
2827c2fbfb3SApril Chin      ########################    
2837c2fbfb3SApril Chin           ##############    
2847c2fbfb3SApril Chin
2857c2fbfb3SApril Chin
2867c2fbfb3SApril Chin
2877c2fbfb3SApril Chin
2887c2fbfb3SApril Chin
2897c2fbfb3SApril Chin
2907c2fbfb3SApril Chin
2917c2fbfb3SApril Chin             High scores:
2927c2fbfb3SApril Chin  
2937c2fbfb3SApril Chin        * 'chin'      8200 pt
2947c2fbfb3SApril Chin        * 'gisburn'   7900 pt
2957c2fbfb3SApril Chin        * 'tpenta'    5520 pt
2967c2fbfb3SApril Chin        * 'kupfer'    5510 pt
2977c2fbfb3SApril Chin        * 'noname'    5000 pt
2987c2fbfb3SApril Chin        * 'noname'    4000 pt
2997c2fbfb3SApril Chin        * 'livad'     3120 pt
3007c2fbfb3SApril Chin        * 'noname'    3000 pt
3017c2fbfb3SApril Chin        * 'noname'    2000 pt
3027c2fbfb3SApril Chin        * 'noname'    1000 pt
3037c2fbfb3SApril Chin  
3047c2fbfb3SApril ChinENDOFTEXT
3057c2fbfb3SApril Chin
3067c2fbfb3SApril Chin        # clear screen, line-by-line
3077c2fbfb3SApril Chin        for (( i=0 ; i < termsize.lines ; i++ )) ; do print "" ; done
3087c2fbfb3SApril Chin        ) | (while read -r line ; do
3097c2fbfb3SApril Chin                read -r -t 0.3 -n 1 c <&5
3107c2fbfb3SApril Chin                [[ "$c" != "" ]] && exit ${magic_return_code}
3117c2fbfb3SApril Chin                print -- "${line}"
3127c2fbfb3SApril Chin            done)
3137c2fbfb3SApril Chin        (( $? == magic_return_code )) && exit ${magic_return_code}
3147c2fbfb3SApril Chin        )
3157c2fbfb3SApril Chin        (( $? == magic_return_code )) && return 0
3167c2fbfb3SApril Chin
3177c2fbfb3SApril Chin        sleep 2
3187c2fbfb3SApril Chin    done
3197c2fbfb3SApril Chin)
3207c2fbfb3SApril Chin}
3217c2fbfb3SApril Chin
3227c2fbfb3SApril Chinfunction run_menu
3237c2fbfb3SApril Chin{
3247c2fbfb3SApril Chin    integer numlevels=0
3257c2fbfb3SApril Chin    integer selected_level=0
3267c2fbfb3SApril Chin    typeset l
3277c2fbfb3SApril Chin
3287c2fbfb3SApril Chin    # built list of available levels based on the "function levelmap_.*"
3297c2fbfb3SApril Chin    # built into this script
3307c2fbfb3SApril Chin    typeset -f | egrep "^function.*levelmap_.*" | sed 's/^function //' |
3317c2fbfb3SApril Chin    while read -r l ; do
3327c2fbfb3SApril Chin        levellist[numlevels]="$l"
3337c2fbfb3SApril Chin        numlevels+=1
3347c2fbfb3SApril Chin    done
3357c2fbfb3SApril Chin
3367c2fbfb3SApril Chin    # swallow any queued user input (e.g. drain stdin)
3377c2fbfb3SApril Chin    read -r -t 0.1 -n 100 dummy
3387c2fbfb3SApril Chin
3397c2fbfb3SApril Chin    while true ; do
3407c2fbfb3SApril Chin        # menu loop with timeout (which switches to "attract mode")
3417c2fbfb3SApril Chin        while true ; do
3427c2fbfb3SApril Chin            print -n -- "${vtcode["clear"]}"
3437c2fbfb3SApril Chin
3447c2fbfb3SApril Chin    cat <<ENDOFTEXT
3457c2fbfb3SApril Chin>======================================\   
3467c2fbfb3SApril Chin>  /-\     .--.                        |
3477c2fbfb3SApril Chin> | OO|   / _.-' .-.   .-.  .-.   .-.  |
3487c2fbfb3SApril Chin> |   |   \  '-. '-'   '-'  '-'   '-'  |
3497c2fbfb3SApril Chin> ^^^^^    '--'                        |
3507c2fbfb3SApril Chin>======\       /================\  .-. |
3517c2fbfb3SApril Chin>      |       |                |  '-' |
3527c2fbfb3SApril Chin ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3537c2fbfb3SApril ChinENDOFTEXT
3547c2fbfb3SApril Chin            print "    GNAW - the ksh93 maze game"
3557c2fbfb3SApril Chin            print "\n\tMenu:"
3567c2fbfb3SApril Chin
3577c2fbfb3SApril Chin            print "\t - [L]evels:"
3587c2fbfb3SApril Chin            for (( i=0 ; i < numlevels ; i++ )) ; do
3597c2fbfb3SApril Chin                printf "\t    %s %s \n" "$( (( i == selected_level )) && print -n "*" || print -n " ")" "${levellist[i]##levelmap_}"
3607c2fbfb3SApril Chin            done
3617c2fbfb3SApril Chin
3627c2fbfb3SApril Chin            print  "\t - Rendering options:"
3637c2fbfb3SApril Chin            printf "\t    [%s] Use [U]nicode\n" "$( (( game_use_unicode == 1 )) && print -n "x" || print -n "_" )"
3647c2fbfb3SApril Chin            printf "\t    [%s] Use [C]olors\n"  "$( (( game_use_colors  == 1 )) && print -n "x" || print -n "_" )"
3657c2fbfb3SApril Chin
3667c2fbfb3SApril Chin            print "\t - [S]tart - [Q]uit"
3677c2fbfb3SApril Chin
3687c2fbfb3SApril Chin            # wait 30 secs (before we switch to "attract mode")
3697c2fbfb3SApril Chin            c="" ; read -r -t 30 -n 1 c
3707c2fbfb3SApril Chin            case "$c" in
3717c2fbfb3SApril Chin                'l') (( selected_level=(selected_level+numlevels+1) % numlevels )) ;;
3727c2fbfb3SApril Chin                'L') (( selected_level=(selected_level+numlevels-1) % numlevels )) ;;
3737c2fbfb3SApril Chin                ~(Fi)s)
3747c2fbfb3SApril Chin                    (( game_use_colors == 1 )) && print -- "${vtcode["bg_black"]}"
3757c2fbfb3SApril Chin                    case "${game_use_colors}${game_use_unicode}" in
3767c2fbfb3SApril Chin                        "00") main_loop "${levellist[selected_level]}" ;;
3777c2fbfb3SApril Chin                        "01") main_loop "${levellist[selected_level]}" | map_filter 0 1 ;;
3787c2fbfb3SApril Chin                        "10") main_loop "${levellist[selected_level]}" | map_filter 1 0 ;;
3797c2fbfb3SApril Chin                        "11") main_loop "${levellist[selected_level]}" | map_filter 1 1 ;;
3807c2fbfb3SApril Chin                    esac
3817c2fbfb3SApril Chin                    print -- "${vtcode["vtreset"]}"
3827c2fbfb3SApril Chin                    ;;
3837c2fbfb3SApril Chin                ~(Fi)q | $'\E')
3847c2fbfb3SApril Chin                    # make sure we do not exit on a cursor key (e.g. <esc>[A,B,C,D)
3857c2fbfb3SApril Chin                    read -r -t 0.01 -n 1 c
3867c2fbfb3SApril Chin                    if [[ "$c" == "[" ]] ; then
3877c2fbfb3SApril Chin                        # this was a cursor key sequence, just eat the 3rd charcater
3887c2fbfb3SApril Chin                        read -r -t 0.01 -n 1 c
3897c2fbfb3SApril Chin                    else
3907c2fbfb3SApril Chin                        exit 0
3917c2fbfb3SApril Chin                    fi
3927c2fbfb3SApril Chin                    ;;
3937c2fbfb3SApril Chin                ~(Fi)u) (( game_use_unicode=(game_use_unicode+2+1) % 2)) ;;
3947c2fbfb3SApril Chin                ~(Fi)c) (( game_use_colors=(game_use_colors+2+1) % 2))   ;;
3957c2fbfb3SApril Chin                "") break ;; # timeout, switch to attract mode
3967c2fbfb3SApril Chin                *) beep ;;
3977c2fbfb3SApril Chin            esac
3987c2fbfb3SApril Chin        done
3997c2fbfb3SApril Chin
4007c2fbfb3SApril Chin        print -n -- "${vtcode["clear"]}"
4017c2fbfb3SApril Chin        attract_mode
4027c2fbfb3SApril Chin    done
4037c2fbfb3SApril Chin    return 0
4047c2fbfb3SApril Chin}
4057c2fbfb3SApril Chin
4067c2fbfb3SApril Chinfunction levelmap_stripes
4077c2fbfb3SApril Chin{
4087c2fbfb3SApril Chincat <<ENDOFLEVEL
4097c2fbfb3SApril Chin###################################
4107c2fbfb3SApril Chin#.......    ...............    P  #
4117c2fbfb3SApril Chin#########..#################..### #
4127c2fbfb3SApril Chin#########..#################..### #
4137c2fbfb3SApril Chin#.......    ..    ..............# #
4147c2fbfb3SApril Chin###############  ################ #
4157c2fbfb3SApril Chin###############  ################ #
4167c2fbfb3SApril Chin#............. M  ..............# #
4177c2fbfb3SApril Chin##..#####################  ###### #
4187c2fbfb3SApril Chin##..#####################  ###### #
4197c2fbfb3SApril Chin#.......  ...........    .......# #
4207c2fbfb3SApril Chin########  ############  ######### #
4217c2fbfb3SApril Chin#   ####  ############  ######### #
4227c2fbfb3SApril Chin# #..................     ......# #
4237c2fbfb3SApril Chin# ############################### # 
4247c2fbfb3SApril Chin#                                 #
4257c2fbfb3SApril Chin###################################
4267c2fbfb3SApril ChinENDOFLEVEL
4277c2fbfb3SApril Chin    return 0
4287c2fbfb3SApril Chin}
4297c2fbfb3SApril Chin
4307c2fbfb3SApril Chinfunction levelmap_livad
4317c2fbfb3SApril Chin{
4327c2fbfb3SApril Chincat <<ENDOFLEVEL
4337c2fbfb3SApril Chin#####################################################
4347c2fbfb3SApril Chin#                                                   #
4357c2fbfb3SApril Chin# ##############  ###############  ################ #
4367c2fbfb3SApril Chin# #............       P             ..............# #
4377c2fbfb3SApril Chin#  .#############################################.# #
4387c2fbfb3SApril Chin# #.#..........                     ............#.  #
4397c2fbfb3SApril Chin# #.#.##########  ###############  ############.#.# #
4407c2fbfb3SApril Chin# #...#........                     ..........#...# #
4417c2fbfb3SApril Chin# #...#.#####################################.#.#.# #
4427c2fbfb3SApril Chin# #...#.#......                     ........#...#.# #
4437c2fbfb3SApril Chin# #.#.#...######  #########################.#.#.#.# #
4447c2fbfb3SApril Chin#  .#.....#....          M          ......#...#.#.# #
4457c2fbfb3SApril Chin# #.#.#...#######################  ########.#.#.#.# #
4467c2fbfb3SApril Chin# #...#.#......                     ........#...#.# #
4477c2fbfb3SApril Chin# #...#.########  ###############  ##########.#.#.# #
4487c2fbfb3SApril Chin# #...#........                     ..........#...# #
4497c2fbfb3SApril Chin# #.#.#########################################.#.# #
4507c2fbfb3SApril Chin# #.#..........                     ............#.  #
4517c2fbfb3SApril Chin#  .############  ###############  ##############.# #
4527c2fbfb3SApril Chin# #............                     ..............# #
4537c2fbfb3SApril Chin# ################################################# #
4547c2fbfb3SApril Chin#                                                   #
4557c2fbfb3SApril Chin#####################################################
4567c2fbfb3SApril ChinENDOFLEVEL
4577c2fbfb3SApril Chin    return 0
4587c2fbfb3SApril Chin}
4597c2fbfb3SApril Chin
4607c2fbfb3SApril Chinfunction levelmap_classic1
4617c2fbfb3SApril Chin{
4627c2fbfb3SApril Chincat <<ENDOFLEVEL
4637c2fbfb3SApril Chin#########################
4647c2fbfb3SApril Chin#.P.........#...........#
4657c2fbfb3SApril Chin#.####.####.#.####.####.#
4667c2fbfb3SApril Chin#.#  #.#  #.#.#  #.#  #.#
4677c2fbfb3SApril Chin#.#  #.#  #.#.#  #.#  #.#
4687c2fbfb3SApril Chin#.####.####.#.####.####.#
4697c2fbfb3SApril Chin#.......................#
4707c2fbfb3SApril Chin#.####.#.#######.#.####.#
4717c2fbfb3SApril Chin#.#  #.#.#     #.#.#  #.#
4727c2fbfb3SApril Chin#.####.#.#######.#.####.#
4737c2fbfb3SApril Chin#......#....#....#......#
4747c2fbfb3SApril Chin######.####.#.####.######
4757c2fbfb3SApril Chin###### #         # ######
4767c2fbfb3SApril Chin###### # ##   ## # ######
4777c2fbfb3SApril Chin###### # #     # # ######
4787c2fbfb3SApril Chin#        #  M  #        #
4797c2fbfb3SApril Chin###### # ####### # ######
4807c2fbfb3SApril Chin###### #         # ######
4817c2fbfb3SApril Chin###### # ####### # ######
4827c2fbfb3SApril Chin###### # #     # # ######
4837c2fbfb3SApril Chin######.#.#######.#.######
4847c2fbfb3SApril Chin#...........#...........#
4857c2fbfb3SApril Chin#.###.###...#...###.###.#
4867c2fbfb3SApril Chin#...#...............#...#
4877c2fbfb3SApril Chin###.#....#######....#.###
4887c2fbfb3SApril Chin# #.#..#.#     #.#..#.# #
4897c2fbfb3SApril Chin###....#.#######.#....###
4907c2fbfb3SApril Chin#......#....#....#......#
4917c2fbfb3SApril Chin#.#########.#.#########.#
4927c2fbfb3SApril Chin#.......................#
4937c2fbfb3SApril Chin#########################
4947c2fbfb3SApril ChinENDOFLEVEL
4957c2fbfb3SApril Chin    return 0
4967c2fbfb3SApril Chin}
4977c2fbfb3SApril Chin
4987c2fbfb3SApril Chinfunction levelmap_classic2
4997c2fbfb3SApril Chin{
5007c2fbfb3SApril Chincat <<ENDOFLEVEL
5017c2fbfb3SApril Chin#######################
5027c2fbfb3SApril Chin#.P...#.........#.....#
5037c2fbfb3SApril Chin#.###.#.#######.#.###.#
5047c2fbfb3SApril Chin#.....................#
5057c2fbfb3SApril Chin###.#.####.#.####.#.###
5067c2fbfb3SApril Chin###.#......#......#.###
5077c2fbfb3SApril Chin###.###.#######.###.###
5087c2fbfb3SApril Chin###.................###
5097c2fbfb3SApril Chin###.###.### ###.###.###
5107c2fbfb3SApril Chin###.#...#M    #...#.###
5117c2fbfb3SApril Chin###.#.#.#######.#.#.###
5127c2fbfb3SApril Chin#.....#.........#.....#
5137c2fbfb3SApril Chin###.#####..#..#####.###
5147c2fbfb3SApril Chin###........#........###
5157c2fbfb3SApril Chin###.###.#######.###.###
5167c2fbfb3SApril Chin#.....................#
5177c2fbfb3SApril Chin#.###.####.#.####.###.#
5187c2fbfb3SApril Chin#.###.#....#....#.###.#
5197c2fbfb3SApril Chin#.###.#.#######.#.###.#
5207c2fbfb3SApril Chin#.....................#
5217c2fbfb3SApril Chin#######################
5227c2fbfb3SApril ChinENDOFLEVEL
5237c2fbfb3SApril Chin    return 0
5247c2fbfb3SApril Chin}
5257c2fbfb3SApril Chin
5267c2fbfb3SApril Chinfunction levelmap_easy
5277c2fbfb3SApril Chin{
5287c2fbfb3SApril Chincat <<ENDOFLEVEL
5297c2fbfb3SApril Chin##################
5307c2fbfb3SApril Chin# .............. #
5317c2fbfb3SApril Chin# . ######       #
5327c2fbfb3SApril Chin# . #  M #       #
5337c2fbfb3SApril Chin# . #    #       #
5347c2fbfb3SApril Chin# . ### ##       #
5357c2fbfb3SApril Chin# .   #          #
5367c2fbfb3SApril Chin# .   ###        #
5377c2fbfb3SApril Chin# .              #
5387c2fbfb3SApril Chin# ..........     #
5397c2fbfb3SApril Chin# .......... P   #
5407c2fbfb3SApril Chin##################
5417c2fbfb3SApril ChinENDOFLEVEL
5427c2fbfb3SApril Chin    return 0
5437c2fbfb3SApril Chin}
5447c2fbfb3SApril Chin
5457c2fbfb3SApril Chinfunction levelmap_sunsolaristext
5467c2fbfb3SApril Chin{
5477c2fbfb3SApril Chincat <<ENDOFLEVEL
5487c2fbfb3SApril Chin################################################
5497c2fbfb3SApril Chin# .####   .    #  #....#                       #
5507c2fbfb3SApril Chin# #       #    #  #....#                       #
5517c2fbfb3SApril Chin#  ####   #    #  #.#..#          M            #
5527c2fbfb3SApril Chin#      #  #    #  #..#.#                       #
5537c2fbfb3SApril Chin# #    #  #    #  #...##                       #
5547c2fbfb3SApril Chin#  ####    ####   #....#                       #
5557c2fbfb3SApril Chin#                                              #
5567c2fbfb3SApril Chin#  ####    ####  #       ##    #####  #  ####  #
5577c2fbfb3SApril Chin# #       #.  .# #      #  #   #....# # #      #
5587c2fbfb3SApril Chin#  ####   #    # #      # P #  #....# #  ####  #
5597c2fbfb3SApril Chin#      #  #    ###      #.#### #.###  #      # #
5607c2fbfb3SApril Chin# #   .#  #.  ..        #    # #...#  # #    # #
5617c2fbfb3SApril Chin#  ####    ####  ###### .    #  ....# #  ####. #
5627c2fbfb3SApril Chin################################################
5637c2fbfb3SApril ChinENDOFLEVEL
5647c2fbfb3SApril Chin    return 0
5657c2fbfb3SApril Chin}
5667c2fbfb3SApril Chin
5677c2fbfb3SApril Chinfunction read_levelmap
5687c2fbfb3SApril Chin{
5697c2fbfb3SApril Chin    typeset map="$( $1 )"
5707c2fbfb3SApril Chin
5717c2fbfb3SApril Chin    integer y=0
5727c2fbfb3SApril Chin    integer x=0
5737c2fbfb3SApril Chin    integer maxx=0
5747c2fbfb3SApril Chin    integer numdots=0
5757c2fbfb3SApril Chin    typeset line
5767c2fbfb3SApril Chin    typeset c
5777c2fbfb3SApril Chin
5787c2fbfb3SApril Chin    while read -r line ; do
5797c2fbfb3SApril Chin        for (( x=0 ; x < ${#line} ; x++ )) ; do
5807c2fbfb3SApril Chin            c="${line:x:1}"
5817c2fbfb3SApril Chin
5827c2fbfb3SApril Chin            case $c in
5837c2fbfb3SApril Chin                ".") numdots+=1 ;;
5847c2fbfb3SApril Chin                "M")
5857c2fbfb3SApril Chin		    # log start position of monsters
5867c2fbfb3SApril Chin                    levelmap["monsterstartpos_x"]="$x"
5877c2fbfb3SApril Chin                    levelmap["monsterstartpos_y"]="$y"
5887c2fbfb3SApril Chin                    c=" "
5897c2fbfb3SApril Chin                    ;;
5907c2fbfb3SApril Chin                "P")
5917c2fbfb3SApril Chin		    # log start position of player
5927c2fbfb3SApril Chin                    levelmap["playerstartpos_x"]="$x"
5937c2fbfb3SApril Chin                    levelmap["playerstartpos_y"]="$y"
5947c2fbfb3SApril Chin                    c=" "
5957c2fbfb3SApril Chin                    ;;
5967c2fbfb3SApril Chin            esac
5977c2fbfb3SApril Chin
5987c2fbfb3SApril Chin            levelmap["${x}_${y}"]="$c"
5997c2fbfb3SApril Chin        done
6007c2fbfb3SApril Chin        (( maxx=x , y++ ))
6017c2fbfb3SApril Chin    done <<<"${map}"
6027c2fbfb3SApril Chin
6037c2fbfb3SApril Chin    levelmap["max_x"]=${maxx}
6047c2fbfb3SApril Chin    levelmap["max_y"]=${y}
6057c2fbfb3SApril Chin    levelmap["numdots"]=${numdots}
6067c2fbfb3SApril Chin
6077c2fbfb3SApril Chin    # consistency checks
6087c2fbfb3SApril Chin    if [[ "${levelmap["monsterstartpos_x"]}" == "" ]] ; then
6097c2fbfb3SApril Chin        fatal_error "read_levelmap: monsterstartpos_x is empty."
6107c2fbfb3SApril Chin    fi
6117c2fbfb3SApril Chin    if [[ "${levelmap["playerstartpos_x"]}" == "" ]] ; then
6127c2fbfb3SApril Chin        fatal_error "read_levelmap: playerstartpos_x is empty."
6137c2fbfb3SApril Chin    fi
6147c2fbfb3SApril Chin
6157c2fbfb3SApril Chin    return 0
6167c2fbfb3SApril Chin}
6177c2fbfb3SApril Chin
6187c2fbfb3SApril Chinfunction player.set
6197c2fbfb3SApril Chin{
6207c2fbfb3SApril Chin    case "${.sh.subscript}" in
6217c2fbfb3SApril Chin        pos_y)
6227c2fbfb3SApril Chin            if [[ "${levelmap["${player["pos_x"]}_${.sh.value}"]}" == "#" ]] ; then
6237c2fbfb3SApril Chin                .sh.value=${player["pos_y"]}
6247c2fbfb3SApril Chin                beep
6257c2fbfb3SApril Chin            fi
6267c2fbfb3SApril Chin            ;;
6277c2fbfb3SApril Chin
6287c2fbfb3SApril Chin        pos_x)
6297c2fbfb3SApril Chin            if [[ "${levelmap["${.sh.value}_${player["pos_y"]}"]}" == "#" ]] ; then
6307c2fbfb3SApril Chin                .sh.value=${player["pos_x"]}
6317c2fbfb3SApril Chin                beep
6327c2fbfb3SApril Chin            fi
6337c2fbfb3SApril Chin            ;;
6347c2fbfb3SApril Chin    esac
6357c2fbfb3SApril Chin    return 0
6367c2fbfb3SApril Chin}
6377c2fbfb3SApril Chin
6387c2fbfb3SApril Chinfunction monster.set
6397c2fbfb3SApril Chin{
6407c2fbfb3SApril Chin    case "${.sh.subscript}" in
6417c2fbfb3SApril Chin        *_pos_y)
6427c2fbfb3SApril Chin            if [[ "${levelmap["${monster[${currmonster}_"pos_x"]}_${.sh.value}"]}" == "#" ]] ; then
6437c2fbfb3SApril Chin                .sh.value=${monster[${currmonster}_"pos_y"]}
6447c2fbfb3SApril Chin                # turn homing off when the monster hit a wall
6457c2fbfb3SApril Chin                monster[${currmonster}_"homing"]=0
6467c2fbfb3SApril Chin            fi
6477c2fbfb3SApril Chin            ;;
6487c2fbfb3SApril Chin
6497c2fbfb3SApril Chin        *_pos_x)
6507c2fbfb3SApril Chin            if [[ "${levelmap["${.sh.value}_${monster[${currmonster}_"pos_y"]}"]}" == "#" ]] ; then
6517c2fbfb3SApril Chin                .sh.value=${monster[${currmonster}_"pos_x"]}
6527c2fbfb3SApril Chin                # turn homing off when the monster hit a wall
6537c2fbfb3SApril Chin                monster[${currmonster}_"homing"]=0
6547c2fbfb3SApril Chin            fi
6557c2fbfb3SApril Chin            ;;
6567c2fbfb3SApril Chin    esac
6577c2fbfb3SApril Chin    return 0
6587c2fbfb3SApril Chin}
6597c2fbfb3SApril Chin
6607c2fbfb3SApril Chinfunction render_game
6617c2fbfb3SApril Chin{
6627c2fbfb3SApril Chin    # render_buffer is some kind of "background buffer" to "double buffer"
6637c2fbfb3SApril Chin    # all output and combine it in one write to reduce flickering in the
6647c2fbfb3SApril Chin    # terminal
6657c2fbfb3SApril Chin    typeset render_buffer="$(
6667c2fbfb3SApril Chin        integer screen_y_offset=1
6677c2fbfb3SApril Chin        integer start_y_pos=0
6687c2fbfb3SApril Chin        integer render_num_lines=${levelmap["max_y"]}
6697c2fbfb3SApril Chin
6707c2fbfb3SApril Chin        if (( (termsize.lines-3) < levelmap["max_y"] )) ; then
6717c2fbfb3SApril Chin            (( start_y_pos=player["pos_y"] / 2))
6727c2fbfb3SApril Chin            (( render_num_lines=termsize.lines-5))
6737c2fbfb3SApril Chin        fi
6747c2fbfb3SApril Chin
6757c2fbfb3SApril Chin        #print -n -- "${vtcode["clear"]}"
6767c2fbfb3SApril Chin        print_setcursorpos 0 0
6777c2fbfb3SApril Chin
6787c2fbfb3SApril Chin        # print score (note the " " around "%d" are neccesary to clean up cruft
6797c2fbfb3SApril Chin        # when we overwrite the level
6807c2fbfb3SApril Chin        printf "SCORE: %05d  DOTS: %.3d  LIVES: %2.d " "${player["score"]}" "${levelmap["numdots"]}" "${player["lives"]}"
6817c2fbfb3SApril Chin        print_levelmap ${screen_y_offset} ${start_y_pos} ${render_num_lines}
6827c2fbfb3SApril Chin
6837c2fbfb3SApril Chin        # render player
6847c2fbfb3SApril Chin        print_setcursorpos ${player["pos_x"]} $((player["pos_y"]+screen_y_offset-start_y_pos))
6857c2fbfb3SApril Chin        print -n "@"
6867c2fbfb3SApril Chin
6877c2fbfb3SApril Chin        # render monsters
6887c2fbfb3SApril Chin        for currmonster in ${monsterlist} ; do
6897c2fbfb3SApril Chin            (( m_pos_x=monster[${currmonster}_"pos_x"] ))
6907c2fbfb3SApril Chin            (( m_pos_y=monster[${currmonster}_"pos_y"]+screen_y_offset-start_y_pos ))
6917c2fbfb3SApril Chin
6927c2fbfb3SApril Chin            if (( m_pos_y >= screen_y_offset && m_pos_y < render_num_lines )) ; then
6937c2fbfb3SApril Chin                print_setcursorpos ${m_pos_x} ${m_pos_y}
6947c2fbfb3SApril Chin                print -n "x"
6957c2fbfb3SApril Chin            fi
6967c2fbfb3SApril Chin        done
6977c2fbfb3SApril Chin
6987c2fbfb3SApril Chin        # status block
6997c2fbfb3SApril Chin        print_setcursorpos 0 $((render_num_lines+screen_y_offset))
7007c2fbfb3SApril Chin        emptyline="                                                            "
7017c2fbfb3SApril Chin        print -n " >> ${player["message"]} <<${emptyline:0:${#emptyline}-${#player["message"]}}"
7027c2fbfb3SApril Chin    )"
7037c2fbfb3SApril Chin    print -r -- "${render_buffer}${end_of_frame}"
7047c2fbfb3SApril Chin#    print "renderbuffersize=$(print "${render_buffer}" | wc -c) ${end_of_frame}"
7057c2fbfb3SApril Chin    return 0
7067c2fbfb3SApril Chin}
7077c2fbfb3SApril Chin
7087c2fbfb3SApril Chinfunction main_loop
7097c2fbfb3SApril Chin{
7107c2fbfb3SApril Chin    float   sleep_per_cycle=0.2
7117c2fbfb3SApril Chin    float   seconds_before_read
7127c2fbfb3SApril Chin    integer num_cycles=0
7137c2fbfb3SApril Chin    float   rs
7147c2fbfb3SApril Chin
7157c2fbfb3SApril Chin    print -n -- "${vtcode["clear"]}"
7167c2fbfb3SApril Chin
7177c2fbfb3SApril Chin    read_levelmap "$1"
7187c2fbfb3SApril Chin
7197c2fbfb3SApril Chin    # player init
7207c2fbfb3SApril Chin    player["pos_x"]=${levelmap["playerstartpos_x"]}
7217c2fbfb3SApril Chin    player["pos_y"]=${levelmap["playerstartpos_y"]}
7227c2fbfb3SApril Chin    player["score"]=0         # player score
7237c2fbfb3SApril Chin    player["lives"]=5         # number of lives
7247c2fbfb3SApril Chin    player["invulnerable"]=10 # cycles how long the player remains invulnerable
7257c2fbfb3SApril Chin    player["message"]="Go..."
7267c2fbfb3SApril Chin
7277c2fbfb3SApril Chin    monsterlist="maw claw jitterbug tentacle grendel"
7287c2fbfb3SApril Chin
7297c2fbfb3SApril Chin    for currmonster in ${monsterlist} ; do
7307c2fbfb3SApril Chin        monster[${currmonster}_"pos_x"]=${levelmap["monsterstartpos_x"]}
7317c2fbfb3SApril Chin        monster[${currmonster}_"pos_y"]=${levelmap["monsterstartpos_y"]}
7327c2fbfb3SApril Chin        monster[${currmonster}_"xstep"]=0
7337c2fbfb3SApril Chin        monster[${currmonster}_"ystep"]=0
7347c2fbfb3SApril Chin        monster[${currmonster}_"homing"]=0
7357c2fbfb3SApril Chin    done
7367c2fbfb3SApril Chin
7377c2fbfb3SApril Chin    # main game cycle loop
7387c2fbfb3SApril Chin    while true ; do
7397c2fbfb3SApril Chin        num_cycles+=1
7407c2fbfb3SApril Chin        seconds_before_read=${SECONDS}
7417c2fbfb3SApril Chin        c="" ; read -r -t ${sleep_per_cycle} -n 1 c
7427c2fbfb3SApril Chin
7437c2fbfb3SApril Chin        if [[ "$c" != "" ]] ; then
7447c2fbfb3SApril Chin            # special case handling for cursor keys which are usually composed
7457c2fbfb3SApril Chin            # of three characters (e.g. "<ESC>[D"). If only <ESC> is hit we
7467c2fbfb3SApril Chin            # quicky exit
7477c2fbfb3SApril Chin            if [[ "$c" == $'\E' ]] ; then
7487c2fbfb3SApril Chin                read -r -t 0.1 -n 1 c
7497c2fbfb3SApril Chin                if [[ "$c" != "[" ]] ; then
7507c2fbfb3SApril Chin                    return 0
7517c2fbfb3SApril Chin                fi
7527c2fbfb3SApril Chin
7537c2fbfb3SApril Chin                # we assume the user is using the cursor keys, this |read|
7547c2fbfb3SApril Chin                # should fetch the 3rd byte of the three-character sequence
7557c2fbfb3SApril Chin                # for the cursor keys
7567c2fbfb3SApril Chin                read -r -t 0.1 -n 1 c
7577c2fbfb3SApril Chin            fi
7587c2fbfb3SApril Chin
7597c2fbfb3SApril Chin            # if the user hit a key the "read" above was interrupted
7607c2fbfb3SApril Chin            # and didn't wait exactly |sleep_per_cycle| seconds.
7617c2fbfb3SApril Chin            # We wait here some moments (|rs|="remaining seconds") to
7627c2fbfb3SApril Chin            # avoid that the game gets "faster" when more user input
7637c2fbfb3SApril Chin            # is given.
7647c2fbfb3SApril Chin            (( rs=sleep_per_cycle-(SECONDS-seconds_before_read) ))
7657c2fbfb3SApril Chin            (( rs > 0.001 )) && sleep ${rs}
7667c2fbfb3SApril Chin
7677c2fbfb3SApril Chin            player["message"]=""
7687c2fbfb3SApril Chin
7697c2fbfb3SApril Chin            case "$c" in
7707c2fbfb3SApril Chin                j|D|4) (( player["pos_x"]-=1 )) ;;
7717c2fbfb3SApril Chin                k|C|6) (( player["pos_x"]+=1 )) ;;
7727c2fbfb3SApril Chin                i|A|8) (( player["pos_y"]-=1 )) ;;
7737c2fbfb3SApril Chin                m|B|2) (( player["pos_y"]+=1 )) ;;
7747c2fbfb3SApril Chin
7757c2fbfb3SApril Chin                q) return 0 ;;
7767c2fbfb3SApril Chin            esac
7777c2fbfb3SApril Chin
7787c2fbfb3SApril Chin            if [[ "${levelmap["${player["pos_x"]}_${player["pos_y"]}"]}" == "." ]] ; then
7797c2fbfb3SApril Chin                levelmap["${player["pos_x"]}_${player["pos_y"]}"]=" "
7807c2fbfb3SApril Chin                (( levelmap["numdots"]-=1 ))
7817c2fbfb3SApril Chin
7827c2fbfb3SApril Chin                (( player["score"]+=10 ))
7837c2fbfb3SApril Chin                player["message"]='GNAW!!'
7847c2fbfb3SApril Chin
7857c2fbfb3SApril Chin                if (( levelmap["numdots"] <= 0 )) ; then
7867c2fbfb3SApril Chin                    level_completed
7877c2fbfb3SApril Chin                    return 0
7887c2fbfb3SApril Chin                fi
7897c2fbfb3SApril Chin            fi
7907c2fbfb3SApril Chin        fi
7917c2fbfb3SApril Chin
7927c2fbfb3SApril Chin        # generic player status change
7937c2fbfb3SApril Chin        if (( player["invulnerable"] > 0 )) ; then
7947c2fbfb3SApril Chin            (( player["invulnerable"]-=1 ))
7957c2fbfb3SApril Chin        fi
7967c2fbfb3SApril Chin        if (( player["lives"] <= 0 )) ; then
7977c2fbfb3SApril Chin            game_over
7987c2fbfb3SApril Chin            return 0
7997c2fbfb3SApril Chin        fi
8007c2fbfb3SApril Chin
8017c2fbfb3SApril Chin        # move monsters
8027c2fbfb3SApril Chin        for currmonster in ${monsterlist} ; do
8037c2fbfb3SApril Chin            # make monster as half as slow then the others when it is following the user
8047c2fbfb3SApril Chin            if (( monster[${currmonster}_"homing"] > 0 )) ; then
8057c2fbfb3SApril Chin                (( (num_cycles%2) > 0 )) && continue
8067c2fbfb3SApril Chin            fi
8077c2fbfb3SApril Chin
8087c2fbfb3SApril Chin            if [[ ${monster[${currmonster}_"pos_x"]} == ${player["pos_x"]} ]] ; then
8097c2fbfb3SApril Chin                if (( (monster[${currmonster}_"pos_y"]-player["pos_y"]) > 0 )) ; then
8107c2fbfb3SApril Chin                    (( monster[${currmonster}_"xstep"]=+0 , monster[${currmonster}_"ystep"]=-1 ))
8117c2fbfb3SApril Chin                else
8127c2fbfb3SApril Chin                    (( monster[${currmonster}_"xstep"]=+0 , monster[${currmonster}_"ystep"]=+1 ))
8137c2fbfb3SApril Chin                fi
8147c2fbfb3SApril Chin                monster[${currmonster}_"homing"]=1
8157c2fbfb3SApril Chin                if (( player["invulnerable"] <= 0 )) ; then
8167c2fbfb3SApril Chin                    player["message"]="Attention: ${currmonster} is chasing you"
8177c2fbfb3SApril Chin                fi
8187c2fbfb3SApril Chin            elif (( monster[${currmonster}_"pos_y"] == player["pos_y"] )) ; then
8197c2fbfb3SApril Chin                if (( (monster[${currmonster}_"pos_x"]-player["pos_x"]) > 0 )) ; then
8207c2fbfb3SApril Chin                    (( monster[${currmonster}_"xstep"]=-1 , monster[${currmonster}_"ystep"]=-0 ))
8217c2fbfb3SApril Chin                else
8227c2fbfb3SApril Chin                    (( monster[${currmonster}_"xstep"]=+1 , monster[${currmonster}_"ystep"]=+0 ))
8237c2fbfb3SApril Chin                fi
8247c2fbfb3SApril Chin                monster[${currmonster}_"homing"]=1
8257c2fbfb3SApril Chin                if (( player["invulnerable"] <= 0 )) ; then
8267c2fbfb3SApril Chin                    player["message"]="Attention: ${currmonster} is chasing you"
8277c2fbfb3SApril Chin                fi
8287c2fbfb3SApril Chin            else
8297c2fbfb3SApril Chin                if (( monster[${currmonster}_"homing"] == 0 )) ; then
8307c2fbfb3SApril Chin                    case $((SECONDS % 6 + RANDOM % 4)) in
8317c2fbfb3SApril Chin                        0) (( monster[${currmonster}_"xstep"]=+0 , monster[${currmonster}_"ystep"]=+0 )) ;;
8327c2fbfb3SApril Chin                        2) (( monster[${currmonster}_"xstep"]=+0 , monster[${currmonster}_"ystep"]=+1 )) ;;
8337c2fbfb3SApril Chin                        3) (( monster[${currmonster}_"xstep"]=+1 , monster[${currmonster}_"ystep"]=+0 )) ;;
8347c2fbfb3SApril Chin                        5) (( monster[${currmonster}_"xstep"]=+0 , monster[${currmonster}_"ystep"]=-1 )) ;;
8357c2fbfb3SApril Chin                        6) (( monster[${currmonster}_"xstep"]=-1 , monster[${currmonster}_"ystep"]=+0 )) ;;
8367c2fbfb3SApril Chin                    esac
8377c2fbfb3SApril Chin                fi
8387c2fbfb3SApril Chin            fi
8397c2fbfb3SApril Chin
8407c2fbfb3SApril Chin            (( monster[${currmonster}_"pos_x"]=monster[${currmonster}_"pos_x"]+monster[${currmonster}_"xstep"] ))
8417c2fbfb3SApril Chin            (( monster[${currmonster}_"pos_y"]=monster[${currmonster}_"pos_y"]+monster[${currmonster}_"ystep"] ))
8427c2fbfb3SApril Chin
8437c2fbfb3SApril Chin            # check if a monster hit the player
8447c2fbfb3SApril Chin            if (( player["invulnerable"] <= 0 )) ; then
8457c2fbfb3SApril Chin                if (( monster[${currmonster}_"pos_x"] == player["pos_x"] && \
8467c2fbfb3SApril Chin                      monster[${currmonster}_"pos_y"] == player["pos_y"] )) ; then
8477c2fbfb3SApril Chin                     # if player was hit by a monster take one life and
8487c2fbfb3SApril Chin                     # make him invulnerable for 10 cycles to avoid that
8497c2fbfb3SApril Chin                     # the next cycle steals more lives
8507c2fbfb3SApril Chin                     player["message"]="Ouuuchhhh"
8517c2fbfb3SApril Chin                     player["invulnerable"]=10
8527c2fbfb3SApril Chin                     (( player["lives"]-=1 ))
8537c2fbfb3SApril Chin
8547c2fbfb3SApril Chin                     beep ; beep ; sleep 0.2 ; beep ; beep
8557c2fbfb3SApril Chin                fi
8567c2fbfb3SApril Chin            fi
8577c2fbfb3SApril Chin        done
8587c2fbfb3SApril Chin
8597c2fbfb3SApril Chin        render_game
8607c2fbfb3SApril Chin    done
8617c2fbfb3SApril Chin    return 0
8627c2fbfb3SApril Chin}
8637c2fbfb3SApril Chin
8647c2fbfb3SApril Chinfunction map_filter
8657c2fbfb3SApril Chin{
8667c2fbfb3SApril Chin    typeset ch_player ch_monster ch_wall var
8677c2fbfb3SApril Chin
8687c2fbfb3SApril Chin    if (( $1 == 1 )) ; then
8697c2fbfb3SApril Chin        ch_player="${vtcode["fg_yellow"]}"
8707c2fbfb3SApril Chin        ch_monster="${vtcode["fg_red"]}"
8717c2fbfb3SApril Chin        ch_wall="${vtcode["fg_blue"]}"
8727c2fbfb3SApril Chin    else
8737c2fbfb3SApril Chin        ch_player=""
8747c2fbfb3SApril Chin        ch_monster=""
8757c2fbfb3SApril Chin        ch_wall=""
8767c2fbfb3SApril Chin    fi
8777c2fbfb3SApril Chin
8787c2fbfb3SApril Chin    if (( $2 == 1 )) ; then
8797c2fbfb3SApril Chin        # unicode map
8807c2fbfb3SApril Chin        ch_player+="$(printf '\u[24d2]')"
8817c2fbfb3SApril Chin        ch_monster+="$(printf '\u[2605]')"
8827c2fbfb3SApril Chin        ch_wall+="$(printf '\u[25a6]')"
8837c2fbfb3SApril Chin    else
8847c2fbfb3SApril Chin        # ascii map
8857c2fbfb3SApril Chin        ch_player+="@"
8867c2fbfb3SApril Chin        ch_monster+="x"
8877c2fbfb3SApril Chin        ch_wall+="#"
8887c2fbfb3SApril Chin    fi
8897c2fbfb3SApril Chin
8907c2fbfb3SApril Chin    # note that this filter currently defeats the "double-buffering"
8917c2fbfb3SApril Chin    while IFS='' read -r -d "${end_of_frame}" var ; do
8927c2fbfb3SApril Chin        var="${var// /${vtcode["fg_grey"]} }"
8937c2fbfb3SApril Chin        var="${var//\./${vtcode["fg_lightred"]}.}"
8947c2fbfb3SApril Chin        var="${var//@/${ch_player}}"
8957c2fbfb3SApril Chin        var="${var//x/${ch_monster}}"
8967c2fbfb3SApril Chin        var="${var//#/${ch_wall}}"
8977c2fbfb3SApril Chin
8987c2fbfb3SApril Chin        print -r -- "${var}"
8997c2fbfb3SApril Chin    done
9007c2fbfb3SApril Chin    return 0
9017c2fbfb3SApril Chin}
9027c2fbfb3SApril Chin
9037c2fbfb3SApril Chinfunction exit_trap
9047c2fbfb3SApril Chin{
9057c2fbfb3SApril Chin    # restore stty settings
9067c2fbfb3SApril Chin    stty ${saved_stty}
9077c2fbfb3SApril Chin
9087c2fbfb3SApril Chin    print "bye."
9097c2fbfb3SApril Chin    return 0
9107c2fbfb3SApril Chin}
9117c2fbfb3SApril Chin
9127c2fbfb3SApril Chinfunction usage
9137c2fbfb3SApril Chin{
9147c2fbfb3SApril Chin    OPTIND=0
9157c2fbfb3SApril Chin    getopts -a "${progname}" "${gnaw_usage}" OPT '-?'
9167c2fbfb3SApril Chin    exit 2
9177c2fbfb3SApril Chin}
9187c2fbfb3SApril Chin
9197c2fbfb3SApril Chin# program start
9207c2fbfb3SApril Chin# make sure we use the ksh93 "cat" builtin which supports the "-u" option
9217c2fbfb3SApril Chinbuiltin basename
9227c2fbfb3SApril Chinbuiltin cat
9237c2fbfb3SApril Chinbuiltin wc
9247c2fbfb3SApril Chin
9257c2fbfb3SApril Chintypeset progname="${ basename "${0}" ; }"
9267c2fbfb3SApril Chin
9277c2fbfb3SApril Chin# terminal size rect
92834f9b3eeSRoland Mainzcompound termsize=(
9297c2fbfb3SApril Chin    integer columns=-1
9307c2fbfb3SApril Chin    integer lines=-1
9317c2fbfb3SApril Chin)
9327c2fbfb3SApril Chin
9337c2fbfb3SApril Chin# global variables
9347c2fbfb3SApril Chintypeset quiet=false
9357c2fbfb3SApril Chin
9367c2fbfb3SApril Chintypeset -A levelmap
9377c2fbfb3SApril Chintypeset -A player
9387c2fbfb3SApril Chintypeset -A monster
9397c2fbfb3SApril Chin# global rendering options
9407c2fbfb3SApril Chininteger game_use_colors=0
9417c2fbfb3SApril Chininteger game_use_unicode=0
9427c2fbfb3SApril Chin
9437c2fbfb3SApril Chintypeset -r gnaw_usage=$'+
94434f9b3eeSRoland Mainz[-?\n@(#)\$Id: gnaw (Roland Mainz) 2009-05-09 \$\n]
9457c2fbfb3SApril Chin[-author?Roland Mainz <roland.mainz@nrubsig.org>]
9467c2fbfb3SApril Chin[+NAME?gnaw - maze game written in ksh93]
9477c2fbfb3SApril Chin[+DESCRIPTION?\bgnaw\b is a maze game.
9487c2fbfb3SApril Chin        The player maneuvers a yellow "@" sign to navigate a maze while eating
9497c2fbfb3SApril Chin        small dots. A level is finished when all the dots are eaten. Five monsters
9507c2fbfb3SApril Chin        (maw, claw, jitterbug, tentacle and grendel) also wander the maze in an attempt
9517c2fbfb3SApril Chin        to catch the "@". Each level begins with all ghosts in their home, and "@" near
9527c2fbfb3SApril Chin        the bottom of the maze. The monsters are released from the home one by one at the
9537c2fbfb3SApril Chin        start of each level and start their rentless hunt after the player.]
9547c2fbfb3SApril Chin[q:quiet?Disable use of terminal bell.]
9557c2fbfb3SApril Chin[+SEE ALSO?\bksh93\b(1)]
9567c2fbfb3SApril Chin'
9577c2fbfb3SApril Chin
9587c2fbfb3SApril Chinwhile getopts -a "${progname}" "${gnaw_usage}" OPT ; do
9597c2fbfb3SApril Chin#    printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|"
9607c2fbfb3SApril Chin    case ${OPT} in
9617c2fbfb3SApril Chin        q)    quiet=true  ;;
9627c2fbfb3SApril Chin        +q)   quiet=false ;;
9637c2fbfb3SApril Chin        *)    usage ;;
9647c2fbfb3SApril Chin    esac
9657c2fbfb3SApril Chindone
9667c2fbfb3SApril Chinshift $((OPTIND-1))
9677c2fbfb3SApril Chin
9687c2fbfb3SApril Chin# save stty values and register the exit trap which restores these values on exit
9697c2fbfb3SApril Chinsaved_stty="$(stty -g)"
9707c2fbfb3SApril Chintrap exit_trap EXIT
9717c2fbfb3SApril Chin
9727c2fbfb3SApril Chinprint "Loading..."
9737c2fbfb3SApril Chin
9747c2fbfb3SApril Chin# set stty values, "-icanon min 1 time 0 -inpck" should improve input latency,
9757c2fbfb3SApril Chin# "-echo" turns the terminal echo off
9767c2fbfb3SApril Chinstty -icanon min 1 time 0 -inpck -echo
9777c2fbfb3SApril Chin
9787c2fbfb3SApril Chinget_term_size termsize || fatal_error "Could not get terminal size."
9797c2fbfb3SApril Chin
9807c2fbfb3SApril Chin# prechecks
9817c2fbfb3SApril Chin(( termsize.columns < 60 )) && fatal_error "Terminal width must be larger than 60 columns (currently ${termsize.columns})."
9827c2fbfb3SApril Chin
9837c2fbfb3SApril Chintypeset -A vtcode
9847c2fbfb3SApril Chin# color values taken from http://frexx.de/xterm-256-notes/, other
9857c2fbfb3SApril Chin# codes from http://vt100.net/docs/vt100-tm/
9867c2fbfb3SApril Chinvtcode=(
9877c2fbfb3SApril Chin    ["bg_black"]="$(print -n "\E[40m")"
9887c2fbfb3SApril Chin    ["fg_black"]="$(print -n "\E[30m")"
9897c2fbfb3SApril Chin    ["fg_red"]="$(print -n "\E[31m")"
9907c2fbfb3SApril Chin    ["fg_lightred"]="$(print -n "\E[1;31m")"
9917c2fbfb3SApril Chin    ["fg_green"]="$(print -n "\E[32m")"
9927c2fbfb3SApril Chin    ["fg_lightgreen"]="$(print -n "\E[1;32m")"
9937c2fbfb3SApril Chin    ["fg_yellow"]="$(print -n "\E[33m")"
9947c2fbfb3SApril Chin    ["fg_lightyellow"]="$(print -n "\E[1;33m")"
9957c2fbfb3SApril Chin    ["fg_blue"]="$(print -n "\E[34m")"
9967c2fbfb3SApril Chin    ["fg_lightblue"]="$(print -n "\E[1;34m")"
9977c2fbfb3SApril Chin    ["fg_grey"]="$(print -n "\E[1;37m")"
9987c2fbfb3SApril Chin    ["fg_white"]="$(print -n "\E[37m")"
9997c2fbfb3SApril Chin
10007c2fbfb3SApril Chin    # misc other vt stuff
10017c2fbfb3SApril Chin    ["vtreset"]="$(tput reset)"
10027c2fbfb3SApril Chin    ["clear"]="$(tput clear)"
10037c2fbfb3SApril Chin    ["bel"]="$(tput bel)"
10047c2fbfb3SApril Chin    ["spaceline"]="$(for (( i=0 ; i < termsize.columns ; i++ )) ; do print -n " " ; done)"
10057c2fbfb3SApril Chin)
10067c2fbfb3SApril Chin
10077c2fbfb3SApril Chin# character used to as marker that a single frame ends at this point - this
10087c2fbfb3SApril Chin# is used by the "double buffering" code to make sure the "read" builtin
10097c2fbfb3SApril Chin# can read a whole "frame" instead of reading stuff line-by-line
10107c2fbfb3SApril Chintypeset -r end_of_frame=$'\t'
10117c2fbfb3SApril Chin
10127c2fbfb3SApril Chin# get terminal sequence to move cursor to position x,y
10137c2fbfb3SApril Chin# (see http://vt100.net/docs/vt100-ug/chapter3.html#CPR)
10147c2fbfb3SApril Chincase ${TERM} in
10157c2fbfb3SApril Chin    xterm | xterm-color | vt100 | vt220 | dtterm | sun | sun-color)
10167c2fbfb3SApril Chin        cup="$(infocmp -1 | \
10177c2fbfb3SApril Chin	       egrep '^[[:space:]]*cup=' | \
10187c2fbfb3SApril Chin	       sed -e 's/.*cup=//' \
10197c2fbfb3SApril Chin	           -e 's/%[%id]*p1[%id]*/%2\\\$d/g' \
10207c2fbfb3SApril Chin		   -e 's/%[%id]*p2[%id]*/%1\\\$d/g' \
10217c2fbfb3SApril Chin		   -e 's/,$//')"
10227c2fbfb3SApril Chin        for (( x=0 ; x < termsize.columns ; x++ )) ; do
10237c2fbfb3SApril Chin            for (( y=0 ; y < termsize.lines ; y++ )) ; do
10247c2fbfb3SApril Chin                vtcode[cup_${x}_${y}]="$(printf "${cup}" $((x + 1)) $((y + 1)) )"
10257c2fbfb3SApril Chin            done
10267c2fbfb3SApril Chin        done
10277c2fbfb3SApril Chin        ;;
10287c2fbfb3SApril Chin    *)
10297c2fbfb3SApril Chin        printf "# Unrecognised terminal type '%s', fetching %dx%d items from terminfo database, please wait...\n" "${TERM}" "${termsize.columns}" "${termsize.lines}"
10307c2fbfb3SApril Chin        for (( x=0 ; x < termsize.columns ; x++ )) ; do
10317c2fbfb3SApril Chin            for (( y=0 ; y < termsize.lines ; y++ )) ; do
10327c2fbfb3SApril Chin                vtcode[cup_${x}_${y}]="$(tput cup ${y} ${x})"
10337c2fbfb3SApril Chin            done
10347c2fbfb3SApril Chin        done
10357c2fbfb3SApril Chin        ;;
10367c2fbfb3SApril Chinesac
10377c2fbfb3SApril Chin
10387c2fbfb3SApril Chinprint -- "${vtcode["vtreset"]}"
10397c2fbfb3SApril Chin
10407c2fbfb3SApril Chinrun_logo
10417c2fbfb3SApril Chinrun_menu
10427c2fbfb3SApril Chin
10437c2fbfb3SApril Chinexit 0
10447c2fbfb3SApril Chin# EOF.
1045