18462SApril.Chin@Sun.COM#!/usr/bin/ksh93 28462SApril.Chin@Sun.COM 38462SApril.Chin@Sun.COM# 48462SApril.Chin@Sun.COM# CDDL HEADER START 58462SApril.Chin@Sun.COM# 68462SApril.Chin@Sun.COM# The contents of this file are subject to the terms of the 78462SApril.Chin@Sun.COM# Common Development and Distribution License (the "License"). 88462SApril.Chin@Sun.COM# You may not use this file except in compliance with the License. 98462SApril.Chin@Sun.COM# 108462SApril.Chin@Sun.COM# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 118462SApril.Chin@Sun.COM# or http://www.opensolaris.org/os/licensing. 128462SApril.Chin@Sun.COM# See the License for the specific language governing permissions 138462SApril.Chin@Sun.COM# and limitations under the License. 148462SApril.Chin@Sun.COM# 158462SApril.Chin@Sun.COM# When distributing Covered Code, include this CDDL HEADER in each 168462SApril.Chin@Sun.COM# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 178462SApril.Chin@Sun.COM# If applicable, add the following below this CDDL HEADER, with the 188462SApril.Chin@Sun.COM# fields enclosed by brackets "[]" replaced with your own identifying 198462SApril.Chin@Sun.COM# information: Portions Copyright [yyyy] [name of copyright owner] 208462SApril.Chin@Sun.COM# 218462SApril.Chin@Sun.COM# CDDL HEADER END 228462SApril.Chin@Sun.COM# 238462SApril.Chin@Sun.COM 248462SApril.Chin@Sun.COM# 25*10898Sroland.mainz@nrubsig.org# Copyright 2009 Sun Microsystems, Inc. All rights reserved. 268462SApril.Chin@Sun.COM# Use is subject to license terms. 278462SApril.Chin@Sun.COM# 288462SApril.Chin@Sun.COM 298462SApril.Chin@Sun.COM# 308462SApril.Chin@Sun.COM# gnaw - a simple ksh93 technology demo 318462SApril.Chin@Sun.COM# 328462SApril.Chin@Sun.COM# Note that this script has been written with the main idea to show 338462SApril.Chin@Sun.COM# many of ksh93's new features (comparing to ksh88/bash) and not 348462SApril.Chin@Sun.COM# as an example of efficient&&clean script code (much of the code 358462SApril.Chin@Sun.COM# could be done more efficient using compound variables, this script 368462SApril.Chin@Sun.COM# focus is the usage of associative arrays). 378462SApril.Chin@Sun.COM# 388462SApril.Chin@Sun.COM 398462SApril.Chin@Sun.COM# Solaris needs /usr/xpg6/bin:/usr/xpg4/bin because the tools in /usr/bin are not POSIX-conformant 408462SApril.Chin@Sun.COMexport PATH=/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin 418462SApril.Chin@Sun.COM 428462SApril.Chin@Sun.COM# Make sure all math stuff runs in the "C" locale to avoid problems 438462SApril.Chin@Sun.COM# with alternative # radix point representations (e.g. ',' instead of 448462SApril.Chin@Sun.COM# '.' in de_DE.*-locales). This needs to be set _before_ any 458462SApril.Chin@Sun.COM# floating-point constants are defined in this script). 468462SApril.Chin@Sun.COMif [[ "${LC_ALL}" != "" ]] ; then 478462SApril.Chin@Sun.COM export \ 488462SApril.Chin@Sun.COM LC_MONETARY="${LC_ALL}" \ 498462SApril.Chin@Sun.COM LC_MESSAGES="${LC_ALL}" \ 508462SApril.Chin@Sun.COM LC_COLLATE="${LC_ALL}" \ 518462SApril.Chin@Sun.COM LC_CTYPE="${LC_ALL}" 528462SApril.Chin@Sun.COM unset LC_ALL 538462SApril.Chin@Sun.COMfi 548462SApril.Chin@Sun.COMexport LC_NUMERIC=C 558462SApril.Chin@Sun.COM 568462SApril.Chin@Sun.COMfunction print_setcursorpos 578462SApril.Chin@Sun.COM{ 588462SApril.Chin@Sun.COM print -n -- "${vtcode[cup_${1}_${2}]}" 598462SApril.Chin@Sun.COM} 608462SApril.Chin@Sun.COM 618462SApril.Chin@Sun.COMfunction beep 628462SApril.Chin@Sun.COM{ 638462SApril.Chin@Sun.COM ${quiet} || print -n -- "${vtcode["bel"]}" 648462SApril.Chin@Sun.COM} 658462SApril.Chin@Sun.COM 668462SApril.Chin@Sun.COMfunction fatal_error 678462SApril.Chin@Sun.COM{ 688462SApril.Chin@Sun.COM print -u2 "${progname}: $*" 698462SApril.Chin@Sun.COM exit 1 708462SApril.Chin@Sun.COM} 718462SApril.Chin@Sun.COM 728462SApril.Chin@Sun.COM# Get terminal size and put values into a compound variable with the integer 738462SApril.Chin@Sun.COM# members "columns" and "lines" 748462SApril.Chin@Sun.COMfunction get_term_size 758462SApril.Chin@Sun.COM{ 768462SApril.Chin@Sun.COM nameref rect=$1 778462SApril.Chin@Sun.COM 788462SApril.Chin@Sun.COM rect.columns=${ tput cols ; } || return 1 798462SApril.Chin@Sun.COM rect.lines=${ tput lines ; } || return 1 808462SApril.Chin@Sun.COM 818462SApril.Chin@Sun.COM return 0 828462SApril.Chin@Sun.COM} 838462SApril.Chin@Sun.COM 848462SApril.Chin@Sun.COMfunction print_levelmap 858462SApril.Chin@Sun.COM{ 868462SApril.Chin@Sun.COM integer screen_y_offset=$1 878462SApril.Chin@Sun.COM integer start_y_pos=$2 # start at this line in the map 888462SApril.Chin@Sun.COM integer max_numlines=$3 # maximum lines we're allowed to render 898462SApril.Chin@Sun.COM integer x 908462SApril.Chin@Sun.COM integer y 918462SApril.Chin@Sun.COM typeset line="" 928462SApril.Chin@Sun.COM 938462SApril.Chin@Sun.COM print_setcursorpos 0 ${screen_y_offset} 948462SApril.Chin@Sun.COM 958462SApril.Chin@Sun.COM for (( y=start_y_pos; (y-start_y_pos) < max_numlines && y < levelmap["max_y"] ; y++ )) ; do 968462SApril.Chin@Sun.COM line="" 978462SApril.Chin@Sun.COM for (( x=0 ; x < levelmap["max_x"] ; x++ )) ; do 988462SApril.Chin@Sun.COM line+="${levelmap["${x}_${y}"]}" 998462SApril.Chin@Sun.COM done 1008462SApril.Chin@Sun.COM 1018462SApril.Chin@Sun.COM print -- "${line} " 1028462SApril.Chin@Sun.COM done 1038462SApril.Chin@Sun.COM 1048462SApril.Chin@Sun.COM # print lines filled with spaces for each line not filled 1058462SApril.Chin@Sun.COM # by the level map 1068462SApril.Chin@Sun.COM line="${vtcode["spaceline"]:0:${levelmap["max_x"]}}" 1078462SApril.Chin@Sun.COM for (( ; (y-start_y_pos) < max_numlines ; y++ )) ; do 1088462SApril.Chin@Sun.COM print -- "${line} " 1098462SApril.Chin@Sun.COM done 1108462SApril.Chin@Sun.COM return 0 1118462SApril.Chin@Sun.COM} 1128462SApril.Chin@Sun.COM 1138462SApril.Chin@Sun.COMfunction level_completed 1148462SApril.Chin@Sun.COM{ 1158462SApril.Chin@Sun.COM integer i 1168462SApril.Chin@Sun.COM typeset dummy 1178462SApril.Chin@Sun.COM typeset render_buffer="$( 1188462SApril.Chin@Sun.COM print -n -- "${vtcode["clear"]}" 1198462SApril.Chin@Sun.COM cat <<ENDOFTEXT 1208462SApril.Chin@Sun.COM 1218462SApril.Chin@Sun.COM # ###### # # ###### # 1228462SApril.Chin@Sun.COM # # # # # # 1238462SApril.Chin@Sun.COM # ##### # # ##### # 1248462SApril.Chin@Sun.COM # # # # # # 1258462SApril.Chin@Sun.COM # # # # # # 1268462SApril.Chin@Sun.COM ###### ###### ## ###### ###### 1278462SApril.Chin@Sun.COM 1288462SApril.Chin@Sun.COM (Good job) 1298462SApril.Chin@Sun.COM 1308462SApril.Chin@Sun.COM ##### #### # # ###### 1318462SApril.Chin@Sun.COM # # # # ## # # 1328462SApril.Chin@Sun.COM # # # # # # # ##### 1338462SApril.Chin@Sun.COM # # # # # # # # 1348462SApril.Chin@Sun.COM # # # # # ## # 1358462SApril.Chin@Sun.COM ##### #### # # ###### 1368462SApril.Chin@Sun.COM 1378462SApril.Chin@Sun.COM 1388462SApril.Chin@Sun.COMENDOFTEXT 1398462SApril.Chin@Sun.COM 1408462SApril.Chin@Sun.COM printf " SCORE: --> %s <--\n" "${player["score"]}" 1418462SApril.Chin@Sun.COM printf " LIVES: --> %s <--\n" "${player["lives"]}" 1428462SApril.Chin@Sun.COM )" 1438462SApril.Chin@Sun.COM print -- "${render_buffer}${end_of_frame}" 1448462SApril.Chin@Sun.COM 1458462SApril.Chin@Sun.COM # wait five seconds and swallow any user input 1468462SApril.Chin@Sun.COM for (( i=0 ; i < 50 ; i++ )) ; do 1478462SApril.Chin@Sun.COM read -r -t 0.1 -n 1 dummy 1488462SApril.Chin@Sun.COM done 1498462SApril.Chin@Sun.COM 1508462SApril.Chin@Sun.COM print "Press any key to continue...${end_of_frame}" 1518462SApril.Chin@Sun.COM # wait five secs or for a key 1528462SApril.Chin@Sun.COM read -r -t 5 -n 1 dummy 1538462SApril.Chin@Sun.COM return 0 1548462SApril.Chin@Sun.COM} 1558462SApril.Chin@Sun.COM 1568462SApril.Chin@Sun.COMfunction game_over 1578462SApril.Chin@Sun.COM{ 1588462SApril.Chin@Sun.COM typeset dummy 1598462SApril.Chin@Sun.COM typeset render_buffer="$( 1608462SApril.Chin@Sun.COM print -n -- "${vtcode["clear"]}" 1618462SApril.Chin@Sun.COM cat <<ENDOFTEXT 1628462SApril.Chin@Sun.COM 1638462SApril.Chin@Sun.COM #### ## # # ###### 1648462SApril.Chin@Sun.COM # # # # ## ## # 1658462SApril.Chin@Sun.COM # # # # ## # ##### 1668462SApril.Chin@Sun.COM # ### ###### # # # 1678462SApril.Chin@Sun.COM # # # # # # # 1688462SApril.Chin@Sun.COM #### # # # # ###### 1698462SApril.Chin@Sun.COM 1708462SApril.Chin@Sun.COM (LOSER!) 1718462SApril.Chin@Sun.COM 1728462SApril.Chin@Sun.COM #### # # ###### ##### 1738462SApril.Chin@Sun.COM # # # # # # # 1748462SApril.Chin@Sun.COM # # # # ##### # # 1758462SApril.Chin@Sun.COM # # # # # ##### 1768462SApril.Chin@Sun.COM # # # # # # # 1778462SApril.Chin@Sun.COM #### ## ###### # # 1788462SApril.Chin@Sun.COM 1798462SApril.Chin@Sun.COMENDOFTEXT 1808462SApril.Chin@Sun.COM 1818462SApril.Chin@Sun.COM printf "\n SCORE: --> %s <--\n" "${player["score"]}" 1828462SApril.Chin@Sun.COM )" 1838462SApril.Chin@Sun.COM print -r -- "${render_buffer}${end_of_frame}" 1848462SApril.Chin@Sun.COM 1858462SApril.Chin@Sun.COM # wait five seconds and swallow any user input 1868462SApril.Chin@Sun.COM for (( i=0 ; i < 50 ; i++ )) ; do 1878462SApril.Chin@Sun.COM read -r -t 0.1 -n 1 dummy 1888462SApril.Chin@Sun.COM done 1898462SApril.Chin@Sun.COM 1908462SApril.Chin@Sun.COM print "Press any key to continue...${end_of_frame}" 1918462SApril.Chin@Sun.COM # wait five secs or for a key 1928462SApril.Chin@Sun.COM read -r -t 5 -n 1 dummy 1938462SApril.Chin@Sun.COM return 0 1948462SApril.Chin@Sun.COM} 1958462SApril.Chin@Sun.COM 1968462SApril.Chin@Sun.COMfunction run_logo 1978462SApril.Chin@Sun.COM{ 1988462SApril.Chin@Sun.COM typeset render_buffer="$( 1998462SApril.Chin@Sun.COM cat <<ENDOFTEXT 2008462SApril.Chin@Sun.COM 2018462SApril.Chin@Sun.COM ##### # # # # # ### 2028462SApril.Chin@Sun.COM# # ## # # # # # # ### 2038462SApril.Chin@Sun.COM# # # # # # # # # ### 2048462SApril.Chin@Sun.COM# #### # # # # # # # # # 2058462SApril.Chin@Sun.COM# # # # # ####### # # # 2068462SApril.Chin@Sun.COM# # # ## # # # # # ### 2078462SApril.Chin@Sun.COM ##### # # # # ## ## ### 2088462SApril.Chin@Sun.COMENDOFTEXT 2098462SApril.Chin@Sun.COM )" 2108462SApril.Chin@Sun.COM print -- "${vtcode["clear"]}${render_buffer}" 2118462SApril.Chin@Sun.COM 2128462SApril.Chin@Sun.COM # wait two seconds and swallow any user input 2138462SApril.Chin@Sun.COM for (( i=0 ; i < 20 ; i++ )) ; do 2148462SApril.Chin@Sun.COM read -r -t 0.1 -n 1 dummy 2158462SApril.Chin@Sun.COM done 2168462SApril.Chin@Sun.COM 2178462SApril.Chin@Sun.COM print "\n (The KornShell 93 maze game)" 2188462SApril.Chin@Sun.COM 2198462SApril.Chin@Sun.COM attract_mode 2208462SApril.Chin@Sun.COM return 0 2218462SApril.Chin@Sun.COM} 2228462SApril.Chin@Sun.COM 2238462SApril.Chin@Sun.COMfunction attract_mode 2248462SApril.Chin@Sun.COM{ 2258462SApril.Chin@Sun.COM( 2268462SApril.Chin@Sun.COM # Now present some info, line-by-line in an endless loop 2278462SApril.Chin@Sun.COM # until the user presses a key (we turn the "magic" return 2288462SApril.Chin@Sun.COM # code for that) 2298462SApril.Chin@Sun.COM integer -r magic_return_code=69 2308462SApril.Chin@Sun.COM typeset line 2318462SApril.Chin@Sun.COM IFS='' ; # Make sure we do not swallow whitespaces 2328462SApril.Chin@Sun.COM 2338462SApril.Chin@Sun.COM while true ; do 2348462SApril.Chin@Sun.COM ( 2358462SApril.Chin@Sun.COM redirect 5<&0 2368462SApril.Chin@Sun.COM 2378462SApril.Chin@Sun.COM (cat <<ENDOFTEXT 2388462SApril.Chin@Sun.COM 2398462SApril.Chin@Sun.COM 2408462SApril.Chin@Sun.COM 2418462SApril.Chin@Sun.COM 2428462SApril.Chin@Sun.COM 2438462SApril.Chin@Sun.COM ################ 2448462SApril.Chin@Sun.COM ######################## 2458462SApril.Chin@Sun.COM ############################ 2468462SApril.Chin@Sun.COM ####### ###### ####### 2478462SApril.Chin@Sun.COM ###### ###### ######## 2488462SApril.Chin@Sun.COM ####### ###### ####### 2498462SApril.Chin@Sun.COM ############################## 2508462SApril.Chin@Sun.COM ############################## 2518462SApril.Chin@Sun.COM ############################## 2528462SApril.Chin@Sun.COM ############################## 2538462SApril.Chin@Sun.COM ############################## 2548462SApril.Chin@Sun.COM ######### ######## ######### 2558462SApril.Chin@Sun.COM # #### #### #### # 2568462SApril.Chin@Sun.COM 2578462SApril.Chin@Sun.COM 2588462SApril.Chin@Sun.COM 2598462SApril.Chin@Sun.COM 2608462SApril.Chin@Sun.COM 2618462SApril.Chin@Sun.COM 2628462SApril.Chin@Sun.COM Written by 2638462SApril.Chin@Sun.COM 2648462SApril.Chin@Sun.COM Roland Mainz 2658462SApril.Chin@Sun.COM (roland.mainz@nrubsig.org) 2668462SApril.Chin@Sun.COM 2678462SApril.Chin@Sun.COM 2688462SApril.Chin@Sun.COM 2698462SApril.Chin@Sun.COM 2708462SApril.Chin@Sun.COM 2718462SApril.Chin@Sun.COM 2728462SApril.Chin@Sun.COM ############## 2738462SApril.Chin@Sun.COM ######################## 2748462SApril.Chin@Sun.COM #################**############ 2758462SApril.Chin@Sun.COM ################################ 2768462SApril.Chin@Sun.COM ############################ 2778462SApril.Chin@Sun.COM ###################### 2788462SApril.Chin@Sun.COM ################ 2798462SApril.Chin@Sun.COM ###################### 2808462SApril.Chin@Sun.COM ############################ 2818462SApril.Chin@Sun.COM ################################ 2828462SApril.Chin@Sun.COM ############################## 2838462SApril.Chin@Sun.COM ######################## 2848462SApril.Chin@Sun.COM ############## 2858462SApril.Chin@Sun.COM 2868462SApril.Chin@Sun.COM 2878462SApril.Chin@Sun.COM 2888462SApril.Chin@Sun.COM 2898462SApril.Chin@Sun.COM 2908462SApril.Chin@Sun.COM 2918462SApril.Chin@Sun.COM 2928462SApril.Chin@Sun.COM High scores: 2938462SApril.Chin@Sun.COM 2948462SApril.Chin@Sun.COM * 'chin' 8200 pt 2958462SApril.Chin@Sun.COM * 'gisburn' 7900 pt 2968462SApril.Chin@Sun.COM * 'tpenta' 5520 pt 2978462SApril.Chin@Sun.COM * 'kupfer' 5510 pt 2988462SApril.Chin@Sun.COM * 'noname' 5000 pt 2998462SApril.Chin@Sun.COM * 'noname' 4000 pt 3008462SApril.Chin@Sun.COM * 'livad' 3120 pt 3018462SApril.Chin@Sun.COM * 'noname' 3000 pt 3028462SApril.Chin@Sun.COM * 'noname' 2000 pt 3038462SApril.Chin@Sun.COM * 'noname' 1000 pt 3048462SApril.Chin@Sun.COM 3058462SApril.Chin@Sun.COMENDOFTEXT 3068462SApril.Chin@Sun.COM 3078462SApril.Chin@Sun.COM # clear screen, line-by-line 3088462SApril.Chin@Sun.COM for (( i=0 ; i < termsize.lines ; i++ )) ; do print "" ; done 3098462SApril.Chin@Sun.COM ) | (while read -r line ; do 3108462SApril.Chin@Sun.COM read -r -t 0.3 -n 1 c <&5 3118462SApril.Chin@Sun.COM [[ "$c" != "" ]] && exit ${magic_return_code} 3128462SApril.Chin@Sun.COM print -- "${line}" 3138462SApril.Chin@Sun.COM done) 3148462SApril.Chin@Sun.COM (( $? == magic_return_code )) && exit ${magic_return_code} 3158462SApril.Chin@Sun.COM ) 3168462SApril.Chin@Sun.COM (( $? == magic_return_code )) && return 0 3178462SApril.Chin@Sun.COM 3188462SApril.Chin@Sun.COM sleep 2 3198462SApril.Chin@Sun.COM done 3208462SApril.Chin@Sun.COM) 3218462SApril.Chin@Sun.COM} 3228462SApril.Chin@Sun.COM 3238462SApril.Chin@Sun.COMfunction run_menu 3248462SApril.Chin@Sun.COM{ 3258462SApril.Chin@Sun.COM integer numlevels=0 3268462SApril.Chin@Sun.COM integer selected_level=0 3278462SApril.Chin@Sun.COM typeset l 3288462SApril.Chin@Sun.COM 3298462SApril.Chin@Sun.COM # built list of available levels based on the "function levelmap_.*" 3308462SApril.Chin@Sun.COM # built into this script 3318462SApril.Chin@Sun.COM typeset -f | egrep "^function.*levelmap_.*" | sed 's/^function //' | 3328462SApril.Chin@Sun.COM while read -r l ; do 3338462SApril.Chin@Sun.COM levellist[numlevels]="$l" 3348462SApril.Chin@Sun.COM numlevels+=1 3358462SApril.Chin@Sun.COM done 3368462SApril.Chin@Sun.COM 3378462SApril.Chin@Sun.COM # swallow any queued user input (e.g. drain stdin) 3388462SApril.Chin@Sun.COM read -r -t 0.1 -n 100 dummy 3398462SApril.Chin@Sun.COM 3408462SApril.Chin@Sun.COM while true ; do 3418462SApril.Chin@Sun.COM # menu loop with timeout (which switches to "attract mode") 3428462SApril.Chin@Sun.COM while true ; do 3438462SApril.Chin@Sun.COM print -n -- "${vtcode["clear"]}" 3448462SApril.Chin@Sun.COM 3458462SApril.Chin@Sun.COM cat <<ENDOFTEXT 3468462SApril.Chin@Sun.COM>======================================\ 3478462SApril.Chin@Sun.COM> /-\ .--. | 3488462SApril.Chin@Sun.COM> | OO| / _.-' .-. .-. .-. .-. | 3498462SApril.Chin@Sun.COM> | | \ '-. '-' '-' '-' '-' | 3508462SApril.Chin@Sun.COM> ^^^^^ '--' | 3518462SApril.Chin@Sun.COM>======\ /================\ .-. | 3528462SApril.Chin@Sun.COM> | | | '-' | 3538462SApril.Chin@Sun.COM ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 3548462SApril.Chin@Sun.COMENDOFTEXT 3558462SApril.Chin@Sun.COM print " GNAW - the ksh93 maze game" 3568462SApril.Chin@Sun.COM print "\n\tMenu:" 3578462SApril.Chin@Sun.COM 3588462SApril.Chin@Sun.COM print "\t - [L]evels:" 3598462SApril.Chin@Sun.COM for (( i=0 ; i < numlevels ; i++ )) ; do 3608462SApril.Chin@Sun.COM printf "\t %s %s \n" "$( (( i == selected_level )) && print -n "*" || print -n " ")" "${levellist[i]##levelmap_}" 3618462SApril.Chin@Sun.COM done 3628462SApril.Chin@Sun.COM 3638462SApril.Chin@Sun.COM print "\t - Rendering options:" 3648462SApril.Chin@Sun.COM printf "\t [%s] Use [U]nicode\n" "$( (( game_use_unicode == 1 )) && print -n "x" || print -n "_" )" 3658462SApril.Chin@Sun.COM printf "\t [%s] Use [C]olors\n" "$( (( game_use_colors == 1 )) && print -n "x" || print -n "_" )" 3668462SApril.Chin@Sun.COM 3678462SApril.Chin@Sun.COM print "\t - [S]tart - [Q]uit" 3688462SApril.Chin@Sun.COM 3698462SApril.Chin@Sun.COM # wait 30 secs (before we switch to "attract mode") 3708462SApril.Chin@Sun.COM c="" ; read -r -t 30 -n 1 c 3718462SApril.Chin@Sun.COM case "$c" in 3728462SApril.Chin@Sun.COM 'l') (( selected_level=(selected_level+numlevels+1) % numlevels )) ;; 3738462SApril.Chin@Sun.COM 'L') (( selected_level=(selected_level+numlevels-1) % numlevels )) ;; 3748462SApril.Chin@Sun.COM ~(Fi)s) 3758462SApril.Chin@Sun.COM (( game_use_colors == 1 )) && print -- "${vtcode["bg_black"]}" 3768462SApril.Chin@Sun.COM case "${game_use_colors}${game_use_unicode}" in 3778462SApril.Chin@Sun.COM "00") main_loop "${levellist[selected_level]}" ;; 3788462SApril.Chin@Sun.COM "01") main_loop "${levellist[selected_level]}" | map_filter 0 1 ;; 3798462SApril.Chin@Sun.COM "10") main_loop "${levellist[selected_level]}" | map_filter 1 0 ;; 3808462SApril.Chin@Sun.COM "11") main_loop "${levellist[selected_level]}" | map_filter 1 1 ;; 3818462SApril.Chin@Sun.COM esac 3828462SApril.Chin@Sun.COM print -- "${vtcode["vtreset"]}" 3838462SApril.Chin@Sun.COM ;; 3848462SApril.Chin@Sun.COM ~(Fi)q | $'\E') 3858462SApril.Chin@Sun.COM # make sure we do not exit on a cursor key (e.g. <esc>[A,B,C,D) 3868462SApril.Chin@Sun.COM read -r -t 0.01 -n 1 c 3878462SApril.Chin@Sun.COM if [[ "$c" == "[" ]] ; then 3888462SApril.Chin@Sun.COM # this was a cursor key sequence, just eat the 3rd charcater 3898462SApril.Chin@Sun.COM read -r -t 0.01 -n 1 c 3908462SApril.Chin@Sun.COM else 3918462SApril.Chin@Sun.COM exit 0 3928462SApril.Chin@Sun.COM fi 3938462SApril.Chin@Sun.COM ;; 3948462SApril.Chin@Sun.COM ~(Fi)u) (( game_use_unicode=(game_use_unicode+2+1) % 2)) ;; 3958462SApril.Chin@Sun.COM ~(Fi)c) (( game_use_colors=(game_use_colors+2+1) % 2)) ;; 3968462SApril.Chin@Sun.COM "") break ;; # timeout, switch to attract mode 3978462SApril.Chin@Sun.COM *) beep ;; 3988462SApril.Chin@Sun.COM esac 3998462SApril.Chin@Sun.COM done 4008462SApril.Chin@Sun.COM 4018462SApril.Chin@Sun.COM print -n -- "${vtcode["clear"]}" 4028462SApril.Chin@Sun.COM attract_mode 4038462SApril.Chin@Sun.COM done 4048462SApril.Chin@Sun.COM return 0 4058462SApril.Chin@Sun.COM} 4068462SApril.Chin@Sun.COM 4078462SApril.Chin@Sun.COMfunction levelmap_stripes 4088462SApril.Chin@Sun.COM{ 4098462SApril.Chin@Sun.COMcat <<ENDOFLEVEL 4108462SApril.Chin@Sun.COM################################### 4118462SApril.Chin@Sun.COM#....... ............... P # 4128462SApril.Chin@Sun.COM#########..#################..### # 4138462SApril.Chin@Sun.COM#########..#################..### # 4148462SApril.Chin@Sun.COM#....... .. ..............# # 4158462SApril.Chin@Sun.COM############### ################ # 4168462SApril.Chin@Sun.COM############### ################ # 4178462SApril.Chin@Sun.COM#............. M ..............# # 4188462SApril.Chin@Sun.COM##..##################### ###### # 4198462SApril.Chin@Sun.COM##..##################### ###### # 4208462SApril.Chin@Sun.COM#....... ........... .......# # 4218462SApril.Chin@Sun.COM######## ############ ######### # 4228462SApril.Chin@Sun.COM# #### ############ ######### # 4238462SApril.Chin@Sun.COM# #.................. ......# # 4248462SApril.Chin@Sun.COM# ############################### # 4258462SApril.Chin@Sun.COM# # 4268462SApril.Chin@Sun.COM################################### 4278462SApril.Chin@Sun.COMENDOFLEVEL 4288462SApril.Chin@Sun.COM return 0 4298462SApril.Chin@Sun.COM} 4308462SApril.Chin@Sun.COM 4318462SApril.Chin@Sun.COMfunction levelmap_livad 4328462SApril.Chin@Sun.COM{ 4338462SApril.Chin@Sun.COMcat <<ENDOFLEVEL 4348462SApril.Chin@Sun.COM##################################################### 4358462SApril.Chin@Sun.COM# # 4368462SApril.Chin@Sun.COM# ############## ############### ################ # 4378462SApril.Chin@Sun.COM# #............ P ..............# # 4388462SApril.Chin@Sun.COM# .#############################################.# # 4398462SApril.Chin@Sun.COM# #.#.......... ............#. # 4408462SApril.Chin@Sun.COM# #.#.########## ############### ############.#.# # 4418462SApril.Chin@Sun.COM# #...#........ ..........#...# # 4428462SApril.Chin@Sun.COM# #...#.#####################################.#.#.# # 4438462SApril.Chin@Sun.COM# #...#.#...... ........#...#.# # 4448462SApril.Chin@Sun.COM# #.#.#...###### #########################.#.#.#.# # 4458462SApril.Chin@Sun.COM# .#.....#.... M ......#...#.#.# # 4468462SApril.Chin@Sun.COM# #.#.#...####################### ########.#.#.#.# # 4478462SApril.Chin@Sun.COM# #...#.#...... ........#...#.# # 4488462SApril.Chin@Sun.COM# #...#.######## ############### ##########.#.#.# # 4498462SApril.Chin@Sun.COM# #...#........ ..........#...# # 4508462SApril.Chin@Sun.COM# #.#.#########################################.#.# # 4518462SApril.Chin@Sun.COM# #.#.......... ............#. # 4528462SApril.Chin@Sun.COM# .############ ############### ##############.# # 4538462SApril.Chin@Sun.COM# #............ ..............# # 4548462SApril.Chin@Sun.COM# ################################################# # 4558462SApril.Chin@Sun.COM# # 4568462SApril.Chin@Sun.COM##################################################### 4578462SApril.Chin@Sun.COMENDOFLEVEL 4588462SApril.Chin@Sun.COM return 0 4598462SApril.Chin@Sun.COM} 4608462SApril.Chin@Sun.COM 4618462SApril.Chin@Sun.COMfunction levelmap_classic1 4628462SApril.Chin@Sun.COM{ 4638462SApril.Chin@Sun.COMcat <<ENDOFLEVEL 4648462SApril.Chin@Sun.COM######################### 4658462SApril.Chin@Sun.COM#.P.........#...........# 4668462SApril.Chin@Sun.COM#.####.####.#.####.####.# 4678462SApril.Chin@Sun.COM#.# #.# #.#.# #.# #.# 4688462SApril.Chin@Sun.COM#.# #.# #.#.# #.# #.# 4698462SApril.Chin@Sun.COM#.####.####.#.####.####.# 4708462SApril.Chin@Sun.COM#.......................# 4718462SApril.Chin@Sun.COM#.####.#.#######.#.####.# 4728462SApril.Chin@Sun.COM#.# #.#.# #.#.# #.# 4738462SApril.Chin@Sun.COM#.####.#.#######.#.####.# 4748462SApril.Chin@Sun.COM#......#....#....#......# 4758462SApril.Chin@Sun.COM######.####.#.####.###### 4768462SApril.Chin@Sun.COM###### # # ###### 4778462SApril.Chin@Sun.COM###### # ## ## # ###### 4788462SApril.Chin@Sun.COM###### # # # # ###### 4798462SApril.Chin@Sun.COM# # M # # 4808462SApril.Chin@Sun.COM###### # ####### # ###### 4818462SApril.Chin@Sun.COM###### # # ###### 4828462SApril.Chin@Sun.COM###### # ####### # ###### 4838462SApril.Chin@Sun.COM###### # # # # ###### 4848462SApril.Chin@Sun.COM######.#.#######.#.###### 4858462SApril.Chin@Sun.COM#...........#...........# 4868462SApril.Chin@Sun.COM#.###.###...#...###.###.# 4878462SApril.Chin@Sun.COM#...#...............#...# 4888462SApril.Chin@Sun.COM###.#....#######....#.### 4898462SApril.Chin@Sun.COM# #.#..#.# #.#..#.# # 4908462SApril.Chin@Sun.COM###....#.#######.#....### 4918462SApril.Chin@Sun.COM#......#....#....#......# 4928462SApril.Chin@Sun.COM#.#########.#.#########.# 4938462SApril.Chin@Sun.COM#.......................# 4948462SApril.Chin@Sun.COM######################### 4958462SApril.Chin@Sun.COMENDOFLEVEL 4968462SApril.Chin@Sun.COM return 0 4978462SApril.Chin@Sun.COM} 4988462SApril.Chin@Sun.COM 4998462SApril.Chin@Sun.COMfunction levelmap_classic2 5008462SApril.Chin@Sun.COM{ 5018462SApril.Chin@Sun.COMcat <<ENDOFLEVEL 5028462SApril.Chin@Sun.COM####################### 5038462SApril.Chin@Sun.COM#.P...#.........#.....# 5048462SApril.Chin@Sun.COM#.###.#.#######.#.###.# 5058462SApril.Chin@Sun.COM#.....................# 5068462SApril.Chin@Sun.COM###.#.####.#.####.#.### 5078462SApril.Chin@Sun.COM###.#......#......#.### 5088462SApril.Chin@Sun.COM###.###.#######.###.### 5098462SApril.Chin@Sun.COM###.................### 5108462SApril.Chin@Sun.COM###.###.### ###.###.### 5118462SApril.Chin@Sun.COM###.#...#M #...#.### 5128462SApril.Chin@Sun.COM###.#.#.#######.#.#.### 5138462SApril.Chin@Sun.COM#.....#.........#.....# 5148462SApril.Chin@Sun.COM###.#####..#..#####.### 5158462SApril.Chin@Sun.COM###........#........### 5168462SApril.Chin@Sun.COM###.###.#######.###.### 5178462SApril.Chin@Sun.COM#.....................# 5188462SApril.Chin@Sun.COM#.###.####.#.####.###.# 5198462SApril.Chin@Sun.COM#.###.#....#....#.###.# 5208462SApril.Chin@Sun.COM#.###.#.#######.#.###.# 5218462SApril.Chin@Sun.COM#.....................# 5228462SApril.Chin@Sun.COM####################### 5238462SApril.Chin@Sun.COMENDOFLEVEL 5248462SApril.Chin@Sun.COM return 0 5258462SApril.Chin@Sun.COM} 5268462SApril.Chin@Sun.COM 5278462SApril.Chin@Sun.COMfunction levelmap_easy 5288462SApril.Chin@Sun.COM{ 5298462SApril.Chin@Sun.COMcat <<ENDOFLEVEL 5308462SApril.Chin@Sun.COM################## 5318462SApril.Chin@Sun.COM# .............. # 5328462SApril.Chin@Sun.COM# . ###### # 5338462SApril.Chin@Sun.COM# . # M # # 5348462SApril.Chin@Sun.COM# . # # # 5358462SApril.Chin@Sun.COM# . ### ## # 5368462SApril.Chin@Sun.COM# . # # 5378462SApril.Chin@Sun.COM# . ### # 5388462SApril.Chin@Sun.COM# . # 5398462SApril.Chin@Sun.COM# .......... # 5408462SApril.Chin@Sun.COM# .......... P # 5418462SApril.Chin@Sun.COM################## 5428462SApril.Chin@Sun.COMENDOFLEVEL 5438462SApril.Chin@Sun.COM return 0 5448462SApril.Chin@Sun.COM} 5458462SApril.Chin@Sun.COM 5468462SApril.Chin@Sun.COMfunction levelmap_sunsolaristext 5478462SApril.Chin@Sun.COM{ 5488462SApril.Chin@Sun.COMcat <<ENDOFLEVEL 5498462SApril.Chin@Sun.COM################################################ 5508462SApril.Chin@Sun.COM# .#### . # #....# # 5518462SApril.Chin@Sun.COM# # # # #....# # 5528462SApril.Chin@Sun.COM# #### # # #.#..# M # 5538462SApril.Chin@Sun.COM# # # # #..#.# # 5548462SApril.Chin@Sun.COM# # # # # #...## # 5558462SApril.Chin@Sun.COM# #### #### #....# # 5568462SApril.Chin@Sun.COM# # 5578462SApril.Chin@Sun.COM# #### #### # ## ##### # #### # 5588462SApril.Chin@Sun.COM# # #. .# # # # #....# # # # 5598462SApril.Chin@Sun.COM# #### # # # # P # #....# # #### # 5608462SApril.Chin@Sun.COM# # # ### #.#### #.### # # # 5618462SApril.Chin@Sun.COM# # .# #. .. # # #...# # # # # 5628462SApril.Chin@Sun.COM# #### #### ###### . # ....# # ####. # 5638462SApril.Chin@Sun.COM################################################ 5648462SApril.Chin@Sun.COMENDOFLEVEL 5658462SApril.Chin@Sun.COM return 0 5668462SApril.Chin@Sun.COM} 5678462SApril.Chin@Sun.COM 5688462SApril.Chin@Sun.COMfunction read_levelmap 5698462SApril.Chin@Sun.COM{ 5708462SApril.Chin@Sun.COM typeset map="$( $1 )" 5718462SApril.Chin@Sun.COM 5728462SApril.Chin@Sun.COM integer y=0 5738462SApril.Chin@Sun.COM integer x=0 5748462SApril.Chin@Sun.COM integer maxx=0 5758462SApril.Chin@Sun.COM integer numdots=0 5768462SApril.Chin@Sun.COM typeset line 5778462SApril.Chin@Sun.COM typeset c 5788462SApril.Chin@Sun.COM 5798462SApril.Chin@Sun.COM while read -r line ; do 5808462SApril.Chin@Sun.COM for (( x=0 ; x < ${#line} ; x++ )) ; do 5818462SApril.Chin@Sun.COM c="${line:x:1}" 5828462SApril.Chin@Sun.COM 5838462SApril.Chin@Sun.COM case $c in 5848462SApril.Chin@Sun.COM ".") numdots+=1 ;; 5858462SApril.Chin@Sun.COM "M") 5868462SApril.Chin@Sun.COM # log start position of monsters 5878462SApril.Chin@Sun.COM levelmap["monsterstartpos_x"]="$x" 5888462SApril.Chin@Sun.COM levelmap["monsterstartpos_y"]="$y" 5898462SApril.Chin@Sun.COM c=" " 5908462SApril.Chin@Sun.COM ;; 5918462SApril.Chin@Sun.COM "P") 5928462SApril.Chin@Sun.COM # log start position of player 5938462SApril.Chin@Sun.COM levelmap["playerstartpos_x"]="$x" 5948462SApril.Chin@Sun.COM levelmap["playerstartpos_y"]="$y" 5958462SApril.Chin@Sun.COM c=" " 5968462SApril.Chin@Sun.COM ;; 5978462SApril.Chin@Sun.COM esac 5988462SApril.Chin@Sun.COM 5998462SApril.Chin@Sun.COM levelmap["${x}_${y}"]="$c" 6008462SApril.Chin@Sun.COM done 6018462SApril.Chin@Sun.COM (( maxx=x , y++ )) 6028462SApril.Chin@Sun.COM done <<<"${map}" 6038462SApril.Chin@Sun.COM 6048462SApril.Chin@Sun.COM levelmap["max_x"]=${maxx} 6058462SApril.Chin@Sun.COM levelmap["max_y"]=${y} 6068462SApril.Chin@Sun.COM levelmap["numdots"]=${numdots} 6078462SApril.Chin@Sun.COM 6088462SApril.Chin@Sun.COM # consistency checks 6098462SApril.Chin@Sun.COM if [[ "${levelmap["monsterstartpos_x"]}" == "" ]] ; then 6108462SApril.Chin@Sun.COM fatal_error "read_levelmap: monsterstartpos_x is empty." 6118462SApril.Chin@Sun.COM fi 6128462SApril.Chin@Sun.COM if [[ "${levelmap["playerstartpos_x"]}" == "" ]] ; then 6138462SApril.Chin@Sun.COM fatal_error "read_levelmap: playerstartpos_x is empty." 6148462SApril.Chin@Sun.COM fi 6158462SApril.Chin@Sun.COM 6168462SApril.Chin@Sun.COM return 0 6178462SApril.Chin@Sun.COM} 6188462SApril.Chin@Sun.COM 6198462SApril.Chin@Sun.COMfunction player.set 6208462SApril.Chin@Sun.COM{ 6218462SApril.Chin@Sun.COM case "${.sh.subscript}" in 6228462SApril.Chin@Sun.COM pos_y) 6238462SApril.Chin@Sun.COM if [[ "${levelmap["${player["pos_x"]}_${.sh.value}"]}" == "#" ]] ; then 6248462SApril.Chin@Sun.COM .sh.value=${player["pos_y"]} 6258462SApril.Chin@Sun.COM beep 6268462SApril.Chin@Sun.COM fi 6278462SApril.Chin@Sun.COM ;; 6288462SApril.Chin@Sun.COM 6298462SApril.Chin@Sun.COM pos_x) 6308462SApril.Chin@Sun.COM if [[ "${levelmap["${.sh.value}_${player["pos_y"]}"]}" == "#" ]] ; then 6318462SApril.Chin@Sun.COM .sh.value=${player["pos_x"]} 6328462SApril.Chin@Sun.COM beep 6338462SApril.Chin@Sun.COM fi 6348462SApril.Chin@Sun.COM ;; 6358462SApril.Chin@Sun.COM esac 6368462SApril.Chin@Sun.COM return 0 6378462SApril.Chin@Sun.COM} 6388462SApril.Chin@Sun.COM 6398462SApril.Chin@Sun.COMfunction monster.set 6408462SApril.Chin@Sun.COM{ 6418462SApril.Chin@Sun.COM case "${.sh.subscript}" in 6428462SApril.Chin@Sun.COM *_pos_y) 6438462SApril.Chin@Sun.COM if [[ "${levelmap["${monster[${currmonster}_"pos_x"]}_${.sh.value}"]}" == "#" ]] ; then 6448462SApril.Chin@Sun.COM .sh.value=${monster[${currmonster}_"pos_y"]} 6458462SApril.Chin@Sun.COM # turn homing off when the monster hit a wall 6468462SApril.Chin@Sun.COM monster[${currmonster}_"homing"]=0 6478462SApril.Chin@Sun.COM fi 6488462SApril.Chin@Sun.COM ;; 6498462SApril.Chin@Sun.COM 6508462SApril.Chin@Sun.COM *_pos_x) 6518462SApril.Chin@Sun.COM if [[ "${levelmap["${.sh.value}_${monster[${currmonster}_"pos_y"]}"]}" == "#" ]] ; then 6528462SApril.Chin@Sun.COM .sh.value=${monster[${currmonster}_"pos_x"]} 6538462SApril.Chin@Sun.COM # turn homing off when the monster hit a wall 6548462SApril.Chin@Sun.COM monster[${currmonster}_"homing"]=0 6558462SApril.Chin@Sun.COM fi 6568462SApril.Chin@Sun.COM ;; 6578462SApril.Chin@Sun.COM esac 6588462SApril.Chin@Sun.COM return 0 6598462SApril.Chin@Sun.COM} 6608462SApril.Chin@Sun.COM 6618462SApril.Chin@Sun.COMfunction render_game 6628462SApril.Chin@Sun.COM{ 6638462SApril.Chin@Sun.COM # render_buffer is some kind of "background buffer" to "double buffer" 6648462SApril.Chin@Sun.COM # all output and combine it in one write to reduce flickering in the 6658462SApril.Chin@Sun.COM # terminal 6668462SApril.Chin@Sun.COM typeset render_buffer="$( 6678462SApril.Chin@Sun.COM integer screen_y_offset=1 6688462SApril.Chin@Sun.COM integer start_y_pos=0 6698462SApril.Chin@Sun.COM integer render_num_lines=${levelmap["max_y"]} 6708462SApril.Chin@Sun.COM 6718462SApril.Chin@Sun.COM if (( (termsize.lines-3) < levelmap["max_y"] )) ; then 6728462SApril.Chin@Sun.COM (( start_y_pos=player["pos_y"] / 2)) 6738462SApril.Chin@Sun.COM (( render_num_lines=termsize.lines-5)) 6748462SApril.Chin@Sun.COM fi 6758462SApril.Chin@Sun.COM 6768462SApril.Chin@Sun.COM #print -n -- "${vtcode["clear"]}" 6778462SApril.Chin@Sun.COM print_setcursorpos 0 0 6788462SApril.Chin@Sun.COM 6798462SApril.Chin@Sun.COM # print score (note the " " around "%d" are neccesary to clean up cruft 6808462SApril.Chin@Sun.COM # when we overwrite the level 6818462SApril.Chin@Sun.COM printf "SCORE: %05d DOTS: %.3d LIVES: %2.d " "${player["score"]}" "${levelmap["numdots"]}" "${player["lives"]}" 6828462SApril.Chin@Sun.COM print_levelmap ${screen_y_offset} ${start_y_pos} ${render_num_lines} 6838462SApril.Chin@Sun.COM 6848462SApril.Chin@Sun.COM # render player 6858462SApril.Chin@Sun.COM print_setcursorpos ${player["pos_x"]} $((player["pos_y"]+screen_y_offset-start_y_pos)) 6868462SApril.Chin@Sun.COM print -n "@" 6878462SApril.Chin@Sun.COM 6888462SApril.Chin@Sun.COM # render monsters 6898462SApril.Chin@Sun.COM for currmonster in ${monsterlist} ; do 6908462SApril.Chin@Sun.COM (( m_pos_x=monster[${currmonster}_"pos_x"] )) 6918462SApril.Chin@Sun.COM (( m_pos_y=monster[${currmonster}_"pos_y"]+screen_y_offset-start_y_pos )) 6928462SApril.Chin@Sun.COM 6938462SApril.Chin@Sun.COM if (( m_pos_y >= screen_y_offset && m_pos_y < render_num_lines )) ; then 6948462SApril.Chin@Sun.COM print_setcursorpos ${m_pos_x} ${m_pos_y} 6958462SApril.Chin@Sun.COM print -n "x" 6968462SApril.Chin@Sun.COM fi 6978462SApril.Chin@Sun.COM done 6988462SApril.Chin@Sun.COM 6998462SApril.Chin@Sun.COM # status block 7008462SApril.Chin@Sun.COM print_setcursorpos 0 $((render_num_lines+screen_y_offset)) 7018462SApril.Chin@Sun.COM emptyline=" " 7028462SApril.Chin@Sun.COM print -n " >> ${player["message"]} <<${emptyline:0:${#emptyline}-${#player["message"]}}" 7038462SApril.Chin@Sun.COM )" 7048462SApril.Chin@Sun.COM print -r -- "${render_buffer}${end_of_frame}" 7058462SApril.Chin@Sun.COM# print "renderbuffersize=$(print "${render_buffer}" | wc -c) ${end_of_frame}" 7068462SApril.Chin@Sun.COM return 0 7078462SApril.Chin@Sun.COM} 7088462SApril.Chin@Sun.COM 7098462SApril.Chin@Sun.COMfunction main_loop 7108462SApril.Chin@Sun.COM{ 7118462SApril.Chin@Sun.COM float sleep_per_cycle=0.2 7128462SApril.Chin@Sun.COM float seconds_before_read 7138462SApril.Chin@Sun.COM integer num_cycles=0 7148462SApril.Chin@Sun.COM float rs 7158462SApril.Chin@Sun.COM 7168462SApril.Chin@Sun.COM print -n -- "${vtcode["clear"]}" 7178462SApril.Chin@Sun.COM 7188462SApril.Chin@Sun.COM read_levelmap "$1" 7198462SApril.Chin@Sun.COM 7208462SApril.Chin@Sun.COM # player init 7218462SApril.Chin@Sun.COM player["pos_x"]=${levelmap["playerstartpos_x"]} 7228462SApril.Chin@Sun.COM player["pos_y"]=${levelmap["playerstartpos_y"]} 7238462SApril.Chin@Sun.COM player["score"]=0 # player score 7248462SApril.Chin@Sun.COM player["lives"]=5 # number of lives 7258462SApril.Chin@Sun.COM player["invulnerable"]=10 # cycles how long the player remains invulnerable 7268462SApril.Chin@Sun.COM player["message"]="Go..." 7278462SApril.Chin@Sun.COM 7288462SApril.Chin@Sun.COM monsterlist="maw claw jitterbug tentacle grendel" 7298462SApril.Chin@Sun.COM 7308462SApril.Chin@Sun.COM for currmonster in ${monsterlist} ; do 7318462SApril.Chin@Sun.COM monster[${currmonster}_"pos_x"]=${levelmap["monsterstartpos_x"]} 7328462SApril.Chin@Sun.COM monster[${currmonster}_"pos_y"]=${levelmap["monsterstartpos_y"]} 7338462SApril.Chin@Sun.COM monster[${currmonster}_"xstep"]=0 7348462SApril.Chin@Sun.COM monster[${currmonster}_"ystep"]=0 7358462SApril.Chin@Sun.COM monster[${currmonster}_"homing"]=0 7368462SApril.Chin@Sun.COM done 7378462SApril.Chin@Sun.COM 7388462SApril.Chin@Sun.COM # main game cycle loop 7398462SApril.Chin@Sun.COM while true ; do 7408462SApril.Chin@Sun.COM num_cycles+=1 7418462SApril.Chin@Sun.COM seconds_before_read=${SECONDS} 7428462SApril.Chin@Sun.COM c="" ; read -r -t ${sleep_per_cycle} -n 1 c 7438462SApril.Chin@Sun.COM 7448462SApril.Chin@Sun.COM if [[ "$c" != "" ]] ; then 7458462SApril.Chin@Sun.COM # special case handling for cursor keys which are usually composed 7468462SApril.Chin@Sun.COM # of three characters (e.g. "<ESC>[D"). If only <ESC> is hit we 7478462SApril.Chin@Sun.COM # quicky exit 7488462SApril.Chin@Sun.COM if [[ "$c" == $'\E' ]] ; then 7498462SApril.Chin@Sun.COM read -r -t 0.1 -n 1 c 7508462SApril.Chin@Sun.COM if [[ "$c" != "[" ]] ; then 7518462SApril.Chin@Sun.COM return 0 7528462SApril.Chin@Sun.COM fi 7538462SApril.Chin@Sun.COM 7548462SApril.Chin@Sun.COM # we assume the user is using the cursor keys, this |read| 7558462SApril.Chin@Sun.COM # should fetch the 3rd byte of the three-character sequence 7568462SApril.Chin@Sun.COM # for the cursor keys 7578462SApril.Chin@Sun.COM read -r -t 0.1 -n 1 c 7588462SApril.Chin@Sun.COM fi 7598462SApril.Chin@Sun.COM 7608462SApril.Chin@Sun.COM # if the user hit a key the "read" above was interrupted 7618462SApril.Chin@Sun.COM # and didn't wait exactly |sleep_per_cycle| seconds. 7628462SApril.Chin@Sun.COM # We wait here some moments (|rs|="remaining seconds") to 7638462SApril.Chin@Sun.COM # avoid that the game gets "faster" when more user input 7648462SApril.Chin@Sun.COM # is given. 7658462SApril.Chin@Sun.COM (( rs=sleep_per_cycle-(SECONDS-seconds_before_read) )) 7668462SApril.Chin@Sun.COM (( rs > 0.001 )) && sleep ${rs} 7678462SApril.Chin@Sun.COM 7688462SApril.Chin@Sun.COM player["message"]="" 7698462SApril.Chin@Sun.COM 7708462SApril.Chin@Sun.COM case "$c" in 7718462SApril.Chin@Sun.COM j|D|4) (( player["pos_x"]-=1 )) ;; 7728462SApril.Chin@Sun.COM k|C|6) (( player["pos_x"]+=1 )) ;; 7738462SApril.Chin@Sun.COM i|A|8) (( player["pos_y"]-=1 )) ;; 7748462SApril.Chin@Sun.COM m|B|2) (( player["pos_y"]+=1 )) ;; 7758462SApril.Chin@Sun.COM 7768462SApril.Chin@Sun.COM q) return 0 ;; 7778462SApril.Chin@Sun.COM esac 7788462SApril.Chin@Sun.COM 7798462SApril.Chin@Sun.COM if [[ "${levelmap["${player["pos_x"]}_${player["pos_y"]}"]}" == "." ]] ; then 7808462SApril.Chin@Sun.COM levelmap["${player["pos_x"]}_${player["pos_y"]}"]=" " 7818462SApril.Chin@Sun.COM (( levelmap["numdots"]-=1 )) 7828462SApril.Chin@Sun.COM 7838462SApril.Chin@Sun.COM (( player["score"]+=10 )) 7848462SApril.Chin@Sun.COM player["message"]='GNAW!!' 7858462SApril.Chin@Sun.COM 7868462SApril.Chin@Sun.COM if (( levelmap["numdots"] <= 0 )) ; then 7878462SApril.Chin@Sun.COM level_completed 7888462SApril.Chin@Sun.COM return 0 7898462SApril.Chin@Sun.COM fi 7908462SApril.Chin@Sun.COM fi 7918462SApril.Chin@Sun.COM fi 7928462SApril.Chin@Sun.COM 7938462SApril.Chin@Sun.COM # generic player status change 7948462SApril.Chin@Sun.COM if (( player["invulnerable"] > 0 )) ; then 7958462SApril.Chin@Sun.COM (( player["invulnerable"]-=1 )) 7968462SApril.Chin@Sun.COM fi 7978462SApril.Chin@Sun.COM if (( player["lives"] <= 0 )) ; then 7988462SApril.Chin@Sun.COM game_over 7998462SApril.Chin@Sun.COM return 0 8008462SApril.Chin@Sun.COM fi 8018462SApril.Chin@Sun.COM 8028462SApril.Chin@Sun.COM # move monsters 8038462SApril.Chin@Sun.COM for currmonster in ${monsterlist} ; do 8048462SApril.Chin@Sun.COM # make monster as half as slow then the others when it is following the user 8058462SApril.Chin@Sun.COM if (( monster[${currmonster}_"homing"] > 0 )) ; then 8068462SApril.Chin@Sun.COM (( (num_cycles%2) > 0 )) && continue 8078462SApril.Chin@Sun.COM fi 8088462SApril.Chin@Sun.COM 8098462SApril.Chin@Sun.COM if [[ ${monster[${currmonster}_"pos_x"]} == ${player["pos_x"]} ]] ; then 8108462SApril.Chin@Sun.COM if (( (monster[${currmonster}_"pos_y"]-player["pos_y"]) > 0 )) ; then 8118462SApril.Chin@Sun.COM (( monster[${currmonster}_"xstep"]=+0 , monster[${currmonster}_"ystep"]=-1 )) 8128462SApril.Chin@Sun.COM else 8138462SApril.Chin@Sun.COM (( monster[${currmonster}_"xstep"]=+0 , monster[${currmonster}_"ystep"]=+1 )) 8148462SApril.Chin@Sun.COM fi 8158462SApril.Chin@Sun.COM monster[${currmonster}_"homing"]=1 8168462SApril.Chin@Sun.COM if (( player["invulnerable"] <= 0 )) ; then 8178462SApril.Chin@Sun.COM player["message"]="Attention: ${currmonster} is chasing you" 8188462SApril.Chin@Sun.COM fi 8198462SApril.Chin@Sun.COM elif (( monster[${currmonster}_"pos_y"] == player["pos_y"] )) ; then 8208462SApril.Chin@Sun.COM if (( (monster[${currmonster}_"pos_x"]-player["pos_x"]) > 0 )) ; then 8218462SApril.Chin@Sun.COM (( monster[${currmonster}_"xstep"]=-1 , monster[${currmonster}_"ystep"]=-0 )) 8228462SApril.Chin@Sun.COM else 8238462SApril.Chin@Sun.COM (( monster[${currmonster}_"xstep"]=+1 , monster[${currmonster}_"ystep"]=+0 )) 8248462SApril.Chin@Sun.COM fi 8258462SApril.Chin@Sun.COM monster[${currmonster}_"homing"]=1 8268462SApril.Chin@Sun.COM if (( player["invulnerable"] <= 0 )) ; then 8278462SApril.Chin@Sun.COM player["message"]="Attention: ${currmonster} is chasing you" 8288462SApril.Chin@Sun.COM fi 8298462SApril.Chin@Sun.COM else 8308462SApril.Chin@Sun.COM if (( monster[${currmonster}_"homing"] == 0 )) ; then 8318462SApril.Chin@Sun.COM case $((SECONDS % 6 + RANDOM % 4)) in 8328462SApril.Chin@Sun.COM 0) (( monster[${currmonster}_"xstep"]=+0 , monster[${currmonster}_"ystep"]=+0 )) ;; 8338462SApril.Chin@Sun.COM 2) (( monster[${currmonster}_"xstep"]=+0 , monster[${currmonster}_"ystep"]=+1 )) ;; 8348462SApril.Chin@Sun.COM 3) (( monster[${currmonster}_"xstep"]=+1 , monster[${currmonster}_"ystep"]=+0 )) ;; 8358462SApril.Chin@Sun.COM 5) (( monster[${currmonster}_"xstep"]=+0 , monster[${currmonster}_"ystep"]=-1 )) ;; 8368462SApril.Chin@Sun.COM 6) (( monster[${currmonster}_"xstep"]=-1 , monster[${currmonster}_"ystep"]=+0 )) ;; 8378462SApril.Chin@Sun.COM esac 8388462SApril.Chin@Sun.COM fi 8398462SApril.Chin@Sun.COM fi 8408462SApril.Chin@Sun.COM 8418462SApril.Chin@Sun.COM (( monster[${currmonster}_"pos_x"]=monster[${currmonster}_"pos_x"]+monster[${currmonster}_"xstep"] )) 8428462SApril.Chin@Sun.COM (( monster[${currmonster}_"pos_y"]=monster[${currmonster}_"pos_y"]+monster[${currmonster}_"ystep"] )) 8438462SApril.Chin@Sun.COM 8448462SApril.Chin@Sun.COM # check if a monster hit the player 8458462SApril.Chin@Sun.COM if (( player["invulnerable"] <= 0 )) ; then 8468462SApril.Chin@Sun.COM if (( monster[${currmonster}_"pos_x"] == player["pos_x"] && \ 8478462SApril.Chin@Sun.COM monster[${currmonster}_"pos_y"] == player["pos_y"] )) ; then 8488462SApril.Chin@Sun.COM # if player was hit by a monster take one life and 8498462SApril.Chin@Sun.COM # make him invulnerable for 10 cycles to avoid that 8508462SApril.Chin@Sun.COM # the next cycle steals more lives 8518462SApril.Chin@Sun.COM player["message"]="Ouuuchhhh" 8528462SApril.Chin@Sun.COM player["invulnerable"]=10 8538462SApril.Chin@Sun.COM (( player["lives"]-=1 )) 8548462SApril.Chin@Sun.COM 8558462SApril.Chin@Sun.COM beep ; beep ; sleep 0.2 ; beep ; beep 8568462SApril.Chin@Sun.COM fi 8578462SApril.Chin@Sun.COM fi 8588462SApril.Chin@Sun.COM done 8598462SApril.Chin@Sun.COM 8608462SApril.Chin@Sun.COM render_game 8618462SApril.Chin@Sun.COM done 8628462SApril.Chin@Sun.COM return 0 8638462SApril.Chin@Sun.COM} 8648462SApril.Chin@Sun.COM 8658462SApril.Chin@Sun.COMfunction map_filter 8668462SApril.Chin@Sun.COM{ 8678462SApril.Chin@Sun.COM typeset ch_player ch_monster ch_wall var 8688462SApril.Chin@Sun.COM 8698462SApril.Chin@Sun.COM if (( $1 == 1 )) ; then 8708462SApril.Chin@Sun.COM ch_player="${vtcode["fg_yellow"]}" 8718462SApril.Chin@Sun.COM ch_monster="${vtcode["fg_red"]}" 8728462SApril.Chin@Sun.COM ch_wall="${vtcode["fg_blue"]}" 8738462SApril.Chin@Sun.COM else 8748462SApril.Chin@Sun.COM ch_player="" 8758462SApril.Chin@Sun.COM ch_monster="" 8768462SApril.Chin@Sun.COM ch_wall="" 8778462SApril.Chin@Sun.COM fi 8788462SApril.Chin@Sun.COM 8798462SApril.Chin@Sun.COM if (( $2 == 1 )) ; then 8808462SApril.Chin@Sun.COM # unicode map 8818462SApril.Chin@Sun.COM ch_player+="$(printf '\u[24d2]')" 8828462SApril.Chin@Sun.COM ch_monster+="$(printf '\u[2605]')" 8838462SApril.Chin@Sun.COM ch_wall+="$(printf '\u[25a6]')" 8848462SApril.Chin@Sun.COM else 8858462SApril.Chin@Sun.COM # ascii map 8868462SApril.Chin@Sun.COM ch_player+="@" 8878462SApril.Chin@Sun.COM ch_monster+="x" 8888462SApril.Chin@Sun.COM ch_wall+="#" 8898462SApril.Chin@Sun.COM fi 8908462SApril.Chin@Sun.COM 8918462SApril.Chin@Sun.COM # note that this filter currently defeats the "double-buffering" 8928462SApril.Chin@Sun.COM while IFS='' read -r -d "${end_of_frame}" var ; do 8938462SApril.Chin@Sun.COM var="${var// /${vtcode["fg_grey"]} }" 8948462SApril.Chin@Sun.COM var="${var//\./${vtcode["fg_lightred"]}.}" 8958462SApril.Chin@Sun.COM var="${var//@/${ch_player}}" 8968462SApril.Chin@Sun.COM var="${var//x/${ch_monster}}" 8978462SApril.Chin@Sun.COM var="${var//#/${ch_wall}}" 8988462SApril.Chin@Sun.COM 8998462SApril.Chin@Sun.COM print -r -- "${var}" 9008462SApril.Chin@Sun.COM done 9018462SApril.Chin@Sun.COM return 0 9028462SApril.Chin@Sun.COM} 9038462SApril.Chin@Sun.COM 9048462SApril.Chin@Sun.COMfunction exit_trap 9058462SApril.Chin@Sun.COM{ 9068462SApril.Chin@Sun.COM # restore stty settings 9078462SApril.Chin@Sun.COM stty ${saved_stty} 9088462SApril.Chin@Sun.COM 9098462SApril.Chin@Sun.COM print "bye." 9108462SApril.Chin@Sun.COM return 0 9118462SApril.Chin@Sun.COM} 9128462SApril.Chin@Sun.COM 9138462SApril.Chin@Sun.COMfunction usage 9148462SApril.Chin@Sun.COM{ 9158462SApril.Chin@Sun.COM OPTIND=0 9168462SApril.Chin@Sun.COM getopts -a "${progname}" "${gnaw_usage}" OPT '-?' 9178462SApril.Chin@Sun.COM exit 2 9188462SApril.Chin@Sun.COM} 9198462SApril.Chin@Sun.COM 9208462SApril.Chin@Sun.COM# program start 9218462SApril.Chin@Sun.COM# make sure we use the ksh93 "cat" builtin which supports the "-u" option 9228462SApril.Chin@Sun.COMbuiltin basename 9238462SApril.Chin@Sun.COMbuiltin cat 9248462SApril.Chin@Sun.COMbuiltin wc 9258462SApril.Chin@Sun.COM 9268462SApril.Chin@Sun.COMtypeset progname="${ basename "${0}" ; }" 9278462SApril.Chin@Sun.COM 9288462SApril.Chin@Sun.COM# terminal size rect 929*10898Sroland.mainz@nrubsig.orgcompound termsize=( 9308462SApril.Chin@Sun.COM integer columns=-1 9318462SApril.Chin@Sun.COM integer lines=-1 9328462SApril.Chin@Sun.COM) 9338462SApril.Chin@Sun.COM 9348462SApril.Chin@Sun.COM# global variables 9358462SApril.Chin@Sun.COMtypeset quiet=false 9368462SApril.Chin@Sun.COM 9378462SApril.Chin@Sun.COMtypeset -A levelmap 9388462SApril.Chin@Sun.COMtypeset -A player 9398462SApril.Chin@Sun.COMtypeset -A monster 9408462SApril.Chin@Sun.COM# global rendering options 9418462SApril.Chin@Sun.COMinteger game_use_colors=0 9428462SApril.Chin@Sun.COMinteger game_use_unicode=0 9438462SApril.Chin@Sun.COM 9448462SApril.Chin@Sun.COMtypeset -r gnaw_usage=$'+ 945*10898Sroland.mainz@nrubsig.org[-?\n@(#)\$Id: gnaw (Roland Mainz) 2009-05-09 \$\n] 9468462SApril.Chin@Sun.COM[-author?Roland Mainz <roland.mainz@nrubsig.org>] 9478462SApril.Chin@Sun.COM[+NAME?gnaw - maze game written in ksh93] 9488462SApril.Chin@Sun.COM[+DESCRIPTION?\bgnaw\b is a maze game. 9498462SApril.Chin@Sun.COM The player maneuvers a yellow "@" sign to navigate a maze while eating 9508462SApril.Chin@Sun.COM small dots. A level is finished when all the dots are eaten. Five monsters 9518462SApril.Chin@Sun.COM (maw, claw, jitterbug, tentacle and grendel) also wander the maze in an attempt 9528462SApril.Chin@Sun.COM to catch the "@". Each level begins with all ghosts in their home, and "@" near 9538462SApril.Chin@Sun.COM the bottom of the maze. The monsters are released from the home one by one at the 9548462SApril.Chin@Sun.COM start of each level and start their rentless hunt after the player.] 9558462SApril.Chin@Sun.COM[q:quiet?Disable use of terminal bell.] 9568462SApril.Chin@Sun.COM[+SEE ALSO?\bksh93\b(1)] 9578462SApril.Chin@Sun.COM' 9588462SApril.Chin@Sun.COM 9598462SApril.Chin@Sun.COMwhile getopts -a "${progname}" "${gnaw_usage}" OPT ; do 9608462SApril.Chin@Sun.COM# printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|" 9618462SApril.Chin@Sun.COM case ${OPT} in 9628462SApril.Chin@Sun.COM q) quiet=true ;; 9638462SApril.Chin@Sun.COM +q) quiet=false ;; 9648462SApril.Chin@Sun.COM *) usage ;; 9658462SApril.Chin@Sun.COM esac 9668462SApril.Chin@Sun.COMdone 9678462SApril.Chin@Sun.COMshift $((OPTIND-1)) 9688462SApril.Chin@Sun.COM 9698462SApril.Chin@Sun.COM# save stty values and register the exit trap which restores these values on exit 9708462SApril.Chin@Sun.COMsaved_stty="$(stty -g)" 9718462SApril.Chin@Sun.COMtrap exit_trap EXIT 9728462SApril.Chin@Sun.COM 9738462SApril.Chin@Sun.COMprint "Loading..." 9748462SApril.Chin@Sun.COM 9758462SApril.Chin@Sun.COM# set stty values, "-icanon min 1 time 0 -inpck" should improve input latency, 9768462SApril.Chin@Sun.COM# "-echo" turns the terminal echo off 9778462SApril.Chin@Sun.COMstty -icanon min 1 time 0 -inpck -echo 9788462SApril.Chin@Sun.COM 9798462SApril.Chin@Sun.COMget_term_size termsize || fatal_error "Could not get terminal size." 9808462SApril.Chin@Sun.COM 9818462SApril.Chin@Sun.COM# prechecks 9828462SApril.Chin@Sun.COM(( termsize.columns < 60 )) && fatal_error "Terminal width must be larger than 60 columns (currently ${termsize.columns})." 9838462SApril.Chin@Sun.COM 9848462SApril.Chin@Sun.COMtypeset -A vtcode 9858462SApril.Chin@Sun.COM# color values taken from http://frexx.de/xterm-256-notes/, other 9868462SApril.Chin@Sun.COM# codes from http://vt100.net/docs/vt100-tm/ 9878462SApril.Chin@Sun.COMvtcode=( 9888462SApril.Chin@Sun.COM ["bg_black"]="$(print -n "\E[40m")" 9898462SApril.Chin@Sun.COM ["fg_black"]="$(print -n "\E[30m")" 9908462SApril.Chin@Sun.COM ["fg_red"]="$(print -n "\E[31m")" 9918462SApril.Chin@Sun.COM ["fg_lightred"]="$(print -n "\E[1;31m")" 9928462SApril.Chin@Sun.COM ["fg_green"]="$(print -n "\E[32m")" 9938462SApril.Chin@Sun.COM ["fg_lightgreen"]="$(print -n "\E[1;32m")" 9948462SApril.Chin@Sun.COM ["fg_yellow"]="$(print -n "\E[33m")" 9958462SApril.Chin@Sun.COM ["fg_lightyellow"]="$(print -n "\E[1;33m")" 9968462SApril.Chin@Sun.COM ["fg_blue"]="$(print -n "\E[34m")" 9978462SApril.Chin@Sun.COM ["fg_lightblue"]="$(print -n "\E[1;34m")" 9988462SApril.Chin@Sun.COM ["fg_grey"]="$(print -n "\E[1;37m")" 9998462SApril.Chin@Sun.COM ["fg_white"]="$(print -n "\E[37m")" 10008462SApril.Chin@Sun.COM 10018462SApril.Chin@Sun.COM # misc other vt stuff 10028462SApril.Chin@Sun.COM ["vtreset"]="$(tput reset)" 10038462SApril.Chin@Sun.COM ["clear"]="$(tput clear)" 10048462SApril.Chin@Sun.COM ["bel"]="$(tput bel)" 10058462SApril.Chin@Sun.COM ["spaceline"]="$(for (( i=0 ; i < termsize.columns ; i++ )) ; do print -n " " ; done)" 10068462SApril.Chin@Sun.COM) 10078462SApril.Chin@Sun.COM 10088462SApril.Chin@Sun.COM# character used to as marker that a single frame ends at this point - this 10098462SApril.Chin@Sun.COM# is used by the "double buffering" code to make sure the "read" builtin 10108462SApril.Chin@Sun.COM# can read a whole "frame" instead of reading stuff line-by-line 10118462SApril.Chin@Sun.COMtypeset -r end_of_frame=$'\t' 10128462SApril.Chin@Sun.COM 10138462SApril.Chin@Sun.COM# get terminal sequence to move cursor to position x,y 10148462SApril.Chin@Sun.COM# (see http://vt100.net/docs/vt100-ug/chapter3.html#CPR) 10158462SApril.Chin@Sun.COMcase ${TERM} in 10168462SApril.Chin@Sun.COM xterm | xterm-color | vt100 | vt220 | dtterm | sun | sun-color) 10178462SApril.Chin@Sun.COM cup="$(infocmp -1 | \ 10188462SApril.Chin@Sun.COM egrep '^[[:space:]]*cup=' | \ 10198462SApril.Chin@Sun.COM sed -e 's/.*cup=//' \ 10208462SApril.Chin@Sun.COM -e 's/%[%id]*p1[%id]*/%2\\\$d/g' \ 10218462SApril.Chin@Sun.COM -e 's/%[%id]*p2[%id]*/%1\\\$d/g' \ 10228462SApril.Chin@Sun.COM -e 's/,$//')" 10238462SApril.Chin@Sun.COM for (( x=0 ; x < termsize.columns ; x++ )) ; do 10248462SApril.Chin@Sun.COM for (( y=0 ; y < termsize.lines ; y++ )) ; do 10258462SApril.Chin@Sun.COM vtcode[cup_${x}_${y}]="$(printf "${cup}" $((x + 1)) $((y + 1)) )" 10268462SApril.Chin@Sun.COM done 10278462SApril.Chin@Sun.COM done 10288462SApril.Chin@Sun.COM ;; 10298462SApril.Chin@Sun.COM *) 10308462SApril.Chin@Sun.COM printf "# Unrecognised terminal type '%s', fetching %dx%d items from terminfo database, please wait...\n" "${TERM}" "${termsize.columns}" "${termsize.lines}" 10318462SApril.Chin@Sun.COM for (( x=0 ; x < termsize.columns ; x++ )) ; do 10328462SApril.Chin@Sun.COM for (( y=0 ; y < termsize.lines ; y++ )) ; do 10338462SApril.Chin@Sun.COM vtcode[cup_${x}_${y}]="$(tput cup ${y} ${x})" 10348462SApril.Chin@Sun.COM done 10358462SApril.Chin@Sun.COM done 10368462SApril.Chin@Sun.COM ;; 10378462SApril.Chin@Sun.COMesac 10388462SApril.Chin@Sun.COM 10398462SApril.Chin@Sun.COMprint -- "${vtcode["vtreset"]}" 10408462SApril.Chin@Sun.COM 10418462SApril.Chin@Sun.COMrun_logo 10428462SApril.Chin@Sun.COMrun_menu 10438462SApril.Chin@Sun.COM 10448462SApril.Chin@Sun.COMexit 0 10458462SApril.Chin@Sun.COM# EOF. 1046