📜 ⬆️ ⬇️

Playfully bash'im

As I wrote the game on bash'e.

image

The impatient can watch / play by downloading the game here , and Ubuntu 18.04 users can install the game with apt:
sudo apt install -y piu-piu 

Then a short story about the process of creating and analyzing interesting (in my opinion) places.

gif with gameplay
image

The idea, as usual, came suddenly. Once I “painted” (I inserted control codes into text messages to change the color of the text / background), I repeated my own script and thought: “Hmm, but this could make a game!”
')
By the way, I use this tablet for coloring:

 #----------------------------------------------------------------------+ #Color picker, usage: printf ${BLD}${CUR}${RED}${BBLU}"Some text"${DEF}| #---------------------------+--------------------------------+---------+ # Text color | Background color | | #------------+--------------+--------------+-----------------+ | # Base |Lighter\Darker| Base | Lighter\Darker | | #------------+--------------+--------------+-----------------+ | RED='\e[31m'; LRED='\e[91m'; BRED='\e[41m'; BLRED='\e[101m' #| Red | GRN='\e[32m'; LGRN='\e[92m'; BGRN='\e[42m'; BLGRN='\e[102m' #| Green | YLW='\e[33m'; LYLW='\e[93m'; BYLW='\e[43m'; BLYLW='\e[103m' #| Yellow | BLU='\e[34m'; LBLU='\e[94m'; BBLU='\e[44m'; BLBLU='\e[104m' #| Blue | MGN='\e[35m'; LMGN='\e[95m'; BMGN='\e[45m'; BLMGN='\e[105m' #| Magenta | CYN='\e[36m'; LCYN='\e[96m'; BCYN='\e[46m'; BLCYN='\e[106m' #| Cyan | GRY='\e[37m'; DGRY='\e[90m'; BGRY='\e[47m'; BDGRY='\e[100m' #| Gray | #------------------------------------------------------------+---------+ # Effects | #----------------------------------------------------------------------+ DEF='\e[0m' # Default color and effects | BLD='\e[1m' # Bold\brighter | DIM='\e[2m' # Dim\darker | CUR='\e[3m' # Italic font | UND='\e[4m' # Underline | INV='\e[7m' # Inverted | COF='\e[?25l' # Cursor Off | CON='\e[?25h' # Cursor On | #----------------------------------------------------------------------+ # Text positioning, usage: XY 10 10 "Some text" | XY () { printf "\e[${2};${1}H${3}"; } # | #----------------------------------------------------------------------+ # Line, usage: line - 10 | line -= 20 | line "word1 word2 " 20 | line () { printf %.s"${1}" $(seq ${2}); } # | #----------------------------------------------------------------------+ 

Inserted into the script using source or just copy-paste.
Pay attention to this control code - '\ e $ {Y}; $ {X} H'
It allows you to move the cursor position, where Y is a row, X is a column, and the game itself is built on this. For convenience, I wrapped it in the XY function.

So, inspired by such console utilities as: sl , cowsay , figlet , etc. etc. I started to invent a game. I always had warm feelings for bullet scrollers, so the choice fell on this genre. In addition, I wanted to make an action game! And not some text adventure game (I have nothing against text adventure games). Action in console, on bash'e? Challenge accepted!
First of all, I drew the "sprite" of the airplane (hero):

 __ |★〵____ \_| / °)- |/ 

The "window" of the cockpit prevented to make the lower part, here the effect of underlining came in handy.
At the same time added colors:

 __ |${RED}${DEF}〵____ \_| /${UND}${BLD} °${DEF})${DGRY}-${DEF} |/ 

For drawing, I decided to use an array, break the lines into array elements:

 hero=(" " "__ " "|${RED}${DEF}〵____ " " \_| /${UND}${BLD} °${DEF})${DGRY}-${DEF}" " |/ " " ") 

And we derive this command:

 for (( i=0; i<${#hero[@]}; i++ )); do XY ${X} $(($Y + $i)) " ${hero[$i]} "; done 

Cycle by the number of elements in the array. Each element is drawn with the line offset down.
The first and last elements of the array are empty in order to erase the remnants of a plane with vertical movement. And to remove artifacts, when moving horizontally, lines are drawn in the frame of spaces - "$ {hero [$ i]}"

Let's make the airplane fly. It is necessary to somehow handle button presses (WASD).

Use the utility - read :

 while true; do read -t0.0001 -n1 input; case $input in "w") ((Y--));; "a") ((X--));; "s") ((Y++));; "d") ((X++));; esac for (( i=0; i<${#hero[@]}; i++ )); do XY ${X} $(($Y + $i)) " ${hero[$i]} " done done 

Infinite loop while . In the read loop to the input variable. Parameters read :
-t0.0001 sets the timeout - input waiting time, I set the minimum so that the game does not get up to stake
-n1 number of characters, we only need 1

Case checks what arrives at the input and, depending on this, moves the airplane (decreases / increases the X and Y coordinates). Next is the familiar for loop for drawing the airplane.

It turned out like this:

image

Great, the airplane is moving, but the cursor is flashing and input is coming out.
Turn off the cursor and hide the input. But this will “spoil” the terminal (with the input disabled, it is somewhat uncomfortable to work), so it is necessary to enable the input after exiting the game.
Create a bye function:

 function bye () { stty echo #   printf "${CON}${DEF}" #  ,     exit #    } trap bye INT #   bye   Ctrl+C printf "${COF}" #   stty -echo #   

It’s beautiful, but if the airplane “flies” off the edge of the screen, a facing occurs, the XY function stops working.

image

It is necessary to determine the size of the "screen" and not to let the plane over the edge. It is convenient to put this in a function so that the values ​​of variables change when you stretch the “screen”:

 function get_dimensions { endx=$( tput cols ) # - (X) endy=$( tput lines ) # - (Y) heroendx=$(( $endx - 12 )) #     . X heroendy=$(( $endy - 7 )) #     . Y } 

Modify the main while loop :

 while true; do get_dimensions read -t0.0001 -n1 input; case $input in "w") ((Y--)); [ $Y -lt 1 ] && Y=1;; "a") ((X--)); [ $X -lt 1 ] && X=1;; "s") ((Y++)); [ $Y -gt $heroendy ] && Y=$heroendy;; "d") ((X++)); [ $X -gt $heroendx ] && X=$heroendx;; esac for (( i=0; i<${#hero[@]}; i++ )); do XY ${X} $(($Y + $i)) " ${hero[$i]} " done done 

You can already play in moving the airplane on the screen. Let's play a little. Enough, it's time to add a bullet.

To do this, add a poll button "P" (piu), it will be responsible for the shot:

 while true; do HX=$(($X + 9)); HY=$(($Y + 3)) get_dimensions read -t0.0001 -n1 input; case $input in "w") ((Y--)); [ $Y -lt 1 ] && Y=1;; "a") ((X--)); [ $X -lt 1 ] && X=1;; "s") ((Y++)); [ $Y -gt $heroendy ] && Y=$heroendy;; "d") ((X++)); [ $X -gt $heroendx ] && X=$heroendx;; "p") PIU+=("$HY $HX");; esac for (( i=0; i<${#hero[@]}; i++ )); do XY ${X} $(($Y + $i)) " ${hero[$i]} " done done 

There can be a lot of bullets; here again an array is useful. Pressing "P" adds the entry "$ HY $ HX" to the "PIU" array.

These are the coordinates of the new bullet. And in order for the bullets to appear at the plane from the desired place, and not from the tail or wing, these coordinates are set with the offset from the coordinates of the plane - HX = $ (($ X + 9)); HY = $ (($ Y + 3)).

Small lyrical digression
Arrays are generally very useful (O_o true chtol!?)

A small example: the task is to take dumps from several postgresql servers of different colors and copy them to yourself.

The settings for connecting to servers are different, the databases are called differently, the dumps are stored in different places ...

We'll have to hardcode, the array is the best suited for this. Create an array:

 dbases=( #-------------------------------+---------------+------------+---------+ # Ssh address | Dump folder | DB name | New db | #-------------------------------+---------------+------------+---------+ '-p123 user@192.168.0.1' '/backup' 'test_db' 'db1' '-p321 looser@127.1' '/tmp' 'main_db' 'db2' 'someserver' '/tmp/backup' 'a' 'db3' ); N=${#dbases[*]}; C=4 

Values ​​we build up in columns, the convenient label turns out. It is convenient to add / subtract lines, you can insert comments.

Variable N counts the total number of values, variable C - the number of columns (manually set).

Then in the cycle for ((i = 0; i <$ {N}; i + = $ {C})); do use this construction:

 tmp=("${dbases[@]:$i:$C}") srvadr="${tmp[0]}" bkpath="${tmp[1]}" dbname="${tmp[2]}" dbtest="${tmp[3]}" 

You can use read :

 read srvadr bkpath dbname dbtest <<< "${dbases[@]:$i:$C}" 

But read it messes up with spaces. Total:

 #!/bin/bash dbases=( #-------------------------------+---------------+------------+---------+ # Ssh address | Dump folder | DB name | New db | #-------------------------------+---------------+------------+---------+ '-p123 user@192.168.0.1' '/backup' 'test_db' 'db1' '-p321 looser@127.1' '/tmp' 'main_db' 'db2' 'someserver' '/tmp/backup' 'a' 'db3' ); N=${#dbases[*]}; C=4 for ((i=0; i<${N}; i+=${C})); do tmp=("${dbases[@]:$i:$C}") srvadr="${tmp[0]}" bkpath="${tmp[1]}" dbname="${tmp[2]}" dbtest="${tmp[3]}" # copy dump scp ${srvadr}${bkpath}/${dbname}.gz . # test dump gunzip -c ${dbname}.gz | psql -v ON_ERROR_STOP=1 ${dbtest} \ || { printf "\nDB error!"; continue; } done 


Voila You can use an array for hardcode parsing without resorting to grep , sed , awk etc services:

 df=($(df -h)); echo ${df[5]} 

Etc. etc., but back to our bullets.

So, bullet is a record of the form "YX" in the array "PIU".
It is necessary to "sprite" bullets, I decided to make bullets not simple, but gold animated.

The bullet will have an animation of the flight, as if it were a mini rocket, it turned out like this:

 shoot=( " ->" "-=>" "=->" "- >") 

And in color:

 shoot=( "${RED} -${DEF}${BLD}${GRN}>${DEF}" "${BLD}${LRED}-=${DEF}${GRN}>${DEF}" "${LRED}=-${DEF}${BLD}${GRN}>${DEF}" "${RED}- ${DEF}${GRN}>${DEF}") 

In the get_dimensions function we add restrictions for the bullet:

 function get_dimensions { endx=$( tput cols ) # - (X) endy=$( tput lines ) # - (Y) heroendx=$(( $endx - 12 )) #     . X heroendy=$(( $endy - 7 )) #     . Y bullendx=$(( $endx - 4 )) #     . X } 

In the main while loop, we place this structure:

 #-------------------------------{  }--------------------------------------- #  ,  L?    ((L++)); [ $L -gt 3 ] && L=0 NP=${#PIU[@]} #  -  #    for (( t=0; t<${NP}; t++ )); do #    "YX"    # .. X  ( ) PI=(${PIU[$t]}); PY=${PI[0]}; PX=${PI[1]} #  X   ((PX++)) [ $PX -ge $bullendx ] && { #      : XY ${PX} ${PY} " " #  ( ) unset PIU[$t] #     PIU=("${PIU[@]}") #    # .. unset    ((NP--)) #  -  continue #   .  #  ,       "PIU" } || { PIU[$t]="$PY $PX"; } #  ,        XY ${PX} ${PY} " ${shoot[$L]}" done 

Piu, piu, piu!

image

But there is no one to shoot at. Add targets. Targets will be the eternal enemies of humanity, zaslantsy from space - aliens in flying saucers.

The plates, I also decided to make animated, as if the plate is spinning. Sprite:

 alien=( " ___ " "( o ) " ' `¯´ ') 

The effect of torsion is achieved by animating the inside of a flying saucer:

 small=( 'o ' ' ' ' o' ' o ') 

In color:

 small=( ${YLW}'o '${DEF} ${YLW}' '${DEF} ${YLW}' o'${DEF} ${YLW}' o '${DEF}) 

In order not to make a bunch of flying saucer frames, I decided to make a sprite generator:

 function sprites { #          "" alien=( " _${UND}${BLD}_${DEF}_ " #        small "(${small[$L]}${DEF}) " ' `¯´ ') } 

We add the sprites function to the while loop , the timing L is right here at the right time.

Add limiters for cymbals:

 enmyendx=$(( $endx - 5 )) enmyendy=$(( $endy - 7 )) 

The number of enemies is given by the variables: enumber (current quantity) and enmax (maximum quantity).

I immediately thought that I would add more objects other than flying saucers. Therefore, I created the “OBJ” array and started adding records like “XY type” there.
The plates appear at the right edge of the screen X = $ enmyendx, and the Y coordinate is set randomly Y = $ (((RANDOM% $ enmyendy) + 3)).
Add to the main while loop :

 #-----------------------------{  }-------------------------------------- [ $enumber -lt $enmax ] && { OBJ+=("$enmyendx $(( (RANDOM % $enmyendy) + 3 )) alien") ((enumber++)) } 

We draw strangers:

 NO=${#OBJ[@]} #  -  er=${#alien[@]} #  -   #    for (( i=0; i<$NO; i++ )); do #    "XY type"    OI=(${OBJ[$i]}); OX=${OI[0]}; OY=${OI[1]}; type=${OI[2]} #  X (   ) ((OX--)) #      : [ $OX -lt 1 ] && { #  ,   #    ..     for (( k=0; k<$er; k++ )); do XY ${OX} $(($OY + $k)) " " done unset OBJ[$i] #     OBJ=("${OBJ[@]}") #    # .. unset    ((NO--)) #  -  ((enumber--)) #   -  continue #   .  #  ,      "OBJ" } || { OBJ[$i]="$OX $OY $type"; } #  ,   for (( p=0; p<${er}; p++ )); do XY ${OX} $(($OY + $p)) "${alien[$p]}" done done 

Juha! Um, the aliens don't die ...

image

Implement collision checking:

  #   for (( p=0; p<${er}; p++ )); do #     for (( t=0; t<${NP}; t++ )); do #     #    "YX"    # .. X  ( ) PI=(${PIU[$t]}); PY=${PI[0]}; PX=${PI[1]} #        # <i>case</i>   <i>test([])</i> .. <i>case</i> #   ,     #   ,   case "$(($OY + 1)) $(($OX + $p))" in #        "${PIU[$t]}") #  ,   #    ..     for (( k=0; k<$er; k++ )); do XY ${OX} $(($OY + $k)) " " done unset OBJ[$i] #     OBJ=("${OBJ[@]}") #   ((NO--)) #  -  ((enumber--)) #   - #  #  ( ) XY ${PX} ${PY} " " unset PIU[$t] #     PIU=("${PIU[@]}") #   ((NP--)) #  -  break #   ;; esac done done 

Skeet.

image

It's time to add a little sense, strangers should collide with a plane and take life from the player.

To do this, you need to add collision checking with an airplane, but to avoid copy-paste, it is desirable to create functions for deleting objects and pools:

  frags=0 #  life=3 #  function remove_obj () { for (( k=0; k<$er; k++ )); do XY ${OX} $(($OY + $k)) " " done unset OBJ[$1]; OBJ=("${OBJ[@]}"); ((NO--)) } function remove_piu () { XY ${PX} ${PY} " " unset PIU[$1]; PIU=("${PIU[@]}"); ((NP--)) } 

We rewrite the "bullets" and "aliens" using functions and add a check of the coliseum with the airplane:

 #-------------------------------{  }--------------------------------------- #  ,  L?    ((L++)); [ $L -gt 3 ] && L=0 NP=${#PIU[@]} #  -  #    for (( t=0; t<${NP}; t++ )); do #    "YX"    # .. X  ( ) PI=(${PIU[$t]}); PY=${PI[0]}; PX=${PI[1]} #  X   ((PX++)) [ $PX -ge $bullendx ] && { #      : remove_piu ${t} #   ,   #     "PIU" continue #   .  #  ,       "PIU" } || { PIU[$t]="$PY $PX"; } #  ,        XY ${PX} ${PY} " ${shoot[$L]}" done #------------------------------{  }------------------------------------- [ $enumber -lt $enmax ] && { OBJ+=("$enmyendx $(( (RANDOM % $enmyendy) + 3 )) alien") ((enumber++)) } NO=${#OBJ[@]} #  -  er=${#alien[@]} #  -   #    for (( i=0; i<$NO; i++ )); do #    "XY type"    OI=(${OBJ[$i]}); OX=${OI[0]}; OY=${OI[1]}; type=${OI[2]} #  X (   ) ((OX--)) [ $OX -lt 1 ] && { #      : remove_obj ${i} #  , ,   #     "OBJ" ((enumber--)) #   -  continue #   .  #  ,      "OBJ" } || { OBJ[$i]="$OX $OY $type"; } #  ,   for (( p=0; p<${er}; p++ )); do XY ${OX} $(($OY + $p)) "${alien[$p]}" done #   for (( p=0; p<${er}; p++ )); do #     for (( t=0; t<${NP}; t++ )); do #     #    "YX"    # .. X  ( ) PI=(${PIU[$t]}); PY=${PI[0]}; PX=${PI[1]} #       # <i>case</i>   <i>test([])</i> .. <i>case</i> #   ,     #   ,   case "$(($OY + 1)) $(($OX + $p))" in #     #    "${PIU[$t]}") remove_obj ${i} #  , , #     #   "OBJ" ((enumber--)) #   - #  ((frags++)) #   remove_piu ${t} #   , #     #   "PIU" break #   ;; esac done #   ,  #    case "$(($OY + 1)) $(($OX + $p))" in #     #    "$HY $HX") remove_obj ${i} #  , ,   #     "OBJ" ((enumber--)) #   -  ((life--)) #    ((frags++)) #   break #   ;; esac done done 

Add the output of game information and check the number of lives:

 #----------------------------{   }------------------------------ XY 0 0 "${BLD}killed aliens: ${DEF}${CYN}${frags}${DEF} ${BLD}Life: ${DEF}${CYN}${life}${DEF} " [ $life -le 0 ] && { clear; echo 'Game over man!'; bye; } 

The game is dead, my friend!

image

Subtotal
 #!/bin/bash #----------------------------------------------------------------------+ #Color picker, usage: printf ${BLD}${CUR}${RED}${BBLU}"Some text"${DEF}| #---------------------------+--------------------------------+---------+ # Text color | Background color | | #------------+--------------+--------------+-----------------+ | # Base |Lighter\Darker| Base | Lighter\Darker | | #------------+--------------+--------------+-----------------+ | RED='\e[31m'; LRED='\e[91m'; BRED='\e[41m'; BLRED='\e[101m' #| Red | GRN='\e[32m'; LGRN='\e[92m'; BGRN='\e[42m'; BLGRN='\e[102m' #| Green | YLW='\e[33m'; LYLW='\e[93m'; BYLW='\e[43m'; BLYLW='\e[103m' #| Yellow | BLU='\e[34m'; LBLU='\e[94m'; BBLU='\e[44m'; BLBLU='\e[104m' #| Blue | MGN='\e[35m'; LMGN='\e[95m'; BMGN='\e[45m'; BLMGN='\e[105m' #| Magenta | CYN='\e[36m'; LCYN='\e[96m'; BCYN='\e[46m'; BLCYN='\e[106m' #| Cyan | GRY='\e[37m'; DGRY='\e[90m'; BGRY='\e[47m'; BDGRY='\e[100m' #| Gray | #------------------------------------------------------------+---------+ # Effects | #----------------------------------------------------------------------+ DEF='\e[0m' # Default color and effects | BLD='\e[1m' # Bold\brighter | DIM='\e[2m' # Dim\darker | CUR='\e[3m' # Italic font | UND='\e[4m' # Underline | INV='\e[7m' # Inverted | COF='\e[?25l' # Cursor Off | CON='\e[?25h' # Cursor On | #----------------------------------------------------------------------+ # Text positioning, usage: XY 10 10 "Some text" | XY () { printf "\e[${2};${1}H${3}"; } # | #----------------------------------------------------------------------+ # Line, usage: line - 10 | line -= 20 | line "word1 word2 " 20 | line () { printf %.s"${1}" $(seq ${2}); } # | #----------------------------------------------------------------------+ small=( ${YLW}'o '${DEF} ${YLW}' '${DEF} ${YLW}' o'${DEF} ${YLW}' o '${DEF}) hero=(" " "__ " "|${RED}★${DEF}〵____ " " \_| /${UND}${BLD} °${DEF})${DGRY}-${DEF}" " |/ " " ") shoot=( "${RED} -${DEF}${BLD}${GRN}>${DEF}" "${BLD}${LRED}-=${DEF}${GRN}>${DEF}" "${LRED}=-${DEF}${BLD}${GRN}>${DEF}" "${RED}- ${DEF}${GRN}>${DEF}") X=1; Y=1 #    enumber=0 #    enmax=10 #    frags=0 #  life=3 #  #-----------------------------{  }------------------------------------- function sprites { alien=( " _${UND}${BLD}_${DEF}_ " "(${small[$L]}${DEF}) " ' `¯´ ') } function remove_obj () { for (( k=0; k<$er; k++ )); do XY ${OX} $(($OY + $k)) " " done unset OBJ[$1]; OBJ=("${OBJ[@]}"); ((NO--)) } function remove_piu () { XY ${PX} ${PY} " " unset PIU[$1]; PIU=("${PIU[@]}"); ((NP--)) } function bye () { stty echo printf "${CON}${DEF}" exit } function get_dimensions { endx=$( tput cols ) endy=$( tput lines ) heroendx=$(( $endx - 12 )) heroendy=$(( $endy - 7 )) bullendx=$(( $endx - 4 )) enmyendx=$(( $endx - 5 )) enmyendy=$(( $endy - 7 )) } #----------------------------------------------------------------------------- trap bye INT printf "${COF}" stty -echo clear #---------------------------{   }--------------------------------- while true; do HX=$(($X + 9)); HY=$(($Y + 3)) get_dimensions; sprites read -t0.0001 -n1 input; case $input in "w") ((Y--)); [ $Y -lt 1 ] && Y=1;; "a") ((X--)); [ $X -lt 1 ] && X=1;; "s") ((Y++)); [ $Y -gt $heroendy ] && Y=$heroendy;; "d") ((X++)); [ $X -gt $heroendx ] && X=$heroendx;; "p") PIU+=("$HY $HX");; esac for (( i=0; i<${#hero[@]}; i++ )); do XY ${X} $(($Y + $i)) " ${hero[$i]} " done #--------------------------{  }--------------------------------- ((L++)); [ $L -gt 3 ] && L=0 NP=${#PIU[@]} #   for (( t=0; t<${NP}; t++ )); do PI=(${PIU[$t]}); PY=${PI[0]}; PX=${PI[1]}; ((PX++)) [ $PX -ge $bullendx ] && { remove_piu ${t} continue } || { PIU[$t]="$PY $PX"; } XY ${PX} ${PY} " ${shoot[$L]}" done #-------------------------{  }------------------------------- [ $enumber -lt $enmax ] && { OBJ+=("$enmyendx $(( (RANDOM % $enmyendy) + 3 )) alien") ((enumber++)) } NO=${#OBJ[@]} #  -  er=${#alien[@]} #  -   #   for (( i=0; i<$NO; i++ )); do OI=(${OBJ[$i]}); OX=${OI[0]}; OY=${OI[1]}; ((OX--)) [ $OX -lt 1 ] && { remove_obj ${i} ((enumber--)) continue } || { OBJ[$i]="$OX $OY $type"; } #   for (( p=0; p<${er}; p++ )); do XY ${OX} $(($OY + $p)) "${alien[$p]}" done #   for (( p=0; p<${er}; p++ )); do for (( t=0; t<${NP}; t++ )); do PI=(${PIU[$t]}); PY=${PI[0]}; PX=${PI[1]} case "$(($OY + 1)) $(($OX + $p))" in "${PIU[$t]}") remove_obj ${i} ((enumber--)) ((frags++)) remove_piu ${t} break ;; esac done #    case "$(($OY + 1)) $(($OX + $p))" in "$HY $HX") remove_obj ${i} ((enumber--)) ((life--)) ((frags++)) break ;; esac done done #-----------------------{   }------------------------ XY 0 0 "${BLD}killed aliens: ${DEF}${CYN}${frags}${DEF} ${BLD}Life: ${DEF}${CYN}${life}${DEF} " [ $life -le 0 ] && { clear; echo 'Game over man!'; bye; } done 

The main points from which the movement is built are dismantled. The following story goes in the style of "how to draw an owl."

I already mentioned that I wanted to add more objects besides flying saucers. As a result, I added bonuses that fall out of dead enemies.

Life - adds life, cartridges - adds cartridges (yes, cartridges end) and a booster amplifier - shooter x2 and x3, but the consumption of cartridges is appropriate.

He also added background elements: trees, clouds, sun and explosions of destroyed enemies.
Trees and clouds are divided into 3 “bitplanes” and appear randomly. I had to completely redo the cycle of objects, moving all the logic into the mover function:

 function mover () { er=${#sprite[@]} #  [ ${1} = 0 ] && { ((OX--)) [ $OX -lt 0 ] && OX=0 OBJ[$i]="$OX $OY $type" } #   [ $OX -lt 1 ] && { remove_obj ${i} case ${type} in "alien") ((enumber--));; esac continue } #         for (( p=0; p<${er}; p++ )); do #  XY ${OX} $(($OY + $p)) "${sprite[$p]}" #   case ${type} in "life" ) #   case "$(($OY + $p)) $OX" in "$HY $HX") ((life++)) remove_obj ${i} break;; esac;; "ammo" ) #   case "$(($OY + $p)) $OX" in "$HY $HX") ((ammo+=100)) remove_obj ${i} break;; esac;; "gunup" ) #    case "$(($OY + $p)) $OX" in "$HY $HX") ((G++)) remove_obj ${i} break;; esac;; "bfire" ) #    case "$OY $OX" in "$HY $HX") ((life--)) remove_obj ${i} break;; esac;; "alien" ) for (( t=0; t<${NP}; t++ )); do #     case "$(($OY + 1)) $(($OX + $p))" in "${PIU[$t]}") #      [ $((RANDOM % $rnd)) -eq 0 ] && { OBJ+=("$OX $OY \ ${bonuses[$((RANDOM % ${#bonuses[@]}))]}") } ((frags++)) ((enumber--)) remove_obj ${i} remove_piu ${t} #   OBJ+=("${OX} ${OY} boom") break;; esac done #    case "$(($OY + 1)) $(($OX + $p))" in "$HY $HX") ((life--)) ((frags++)) ((enumber--)) remove_obj ${i} OBJ+=("${OX} ${OY} boom") break;; esac;; esac done } 

And the cycle of objects began to look like this:

 # \\  ,    |------------------ NO=${#OBJ[@]} for (( i=0; i<$NO; i++ )); do OI=(${OBJ[$i]}); OX=${OI[0]}; OY=${OI[1]}; type=${OI[2]} case $type in #      1  "boom" ) er=${boomC} for part in "${boom[@]:$B:$boomC}"; do XY ${OX} ${OY} " ${part}" ((OY++)) done [ ${E} = 0 ] && { ((B+=${boomC})) [ $B -gt ${boomN} ] && { B=0 remove_obj ${i} } };; #      sprite #   mover    #       # (  ) "alien" ) sprite=("${alien[@]}"); mover 0;; "bfire" ) sprite=("${bfire[@]}"); mover 0;; "ammo" ) sprite=("${ammob[@]}"); mover 0;; "life" ) sprite=("${lifep[@]}"); mover 0;; "gunup" ) sprite=("${gunup[@]}"); mover 0;; "tree1" ) sprite=("${tree1[@]}"); mover ${Q};; "tree2" ) sprite=("${tree2[@]}"); mover ${W};; "tree3" ) sprite=("${tree3[@]}"); mover ${E};; "cloud1") sprite=("${cloud1[@]}"); mover ${Q};; "cloud2") sprite=("${cloud2[@]}"); mover ${W};; "cloud3") sprite=("${cloud3[@]}"); mover ${E};; esac done 

So what kind of shooter without a boss? As soon as the number of dead aliens reaches 100, an evil papa will appear. With the advent of the big plate, the player understands where these endless aliens come from, from here! From this big plate, you need to dunk it!

Boss:

 #  |----------------------------------------------------------------- if [ $frags -ge $tillboss ]; then #    bar=; hp=$(( $bosshbar * $bhealth / 100 )); hm=$(( $endx - 10 )) for (( i=0 ; i<${hp}; i++ )); do bar="▒${bar}"; done for (( i=$hp; i<${hm}; i++ )); do bar="${bar} "; done XY 1 $(($endy - 1)) " ${BLD}BOSS: |${RED}${bar}${DEF}${BLD}|${DEF}" #  [ $BY -lt $Y ] && { ((BY++)) } [ $BY -gt $Y ] && { ((BY--)) } [ $BX -gt $(($endx / 2)) -a "$goback" == "false" ] && { ((BX--)) } || goback=true [ $BX -lt $bossendx -a "$goback" == "true" ] { && ((BX++)) } || goback=false #  for (( i=0; i<${#boss[@]}; i++ )); do XY ${BX} $(($BY + $i)) " ${boss[$i]} " done #  [ $BY -eq $Y -a $K -eq 0 ] && { OBJ+=("$(($BX - 4)) $(($BY + 3)) bfire") } #     [ $enumber -lt $enmax ] && { ((enumber++)) OBJ+=("$(($BX + 2)) $(($BY + 3)) alien") } else [ $enumber -lt $enmax ] && { ((enumber++)) OBJ+=("$enmyendx $(( (RANDOM % $enmyendy) + 3 )) alien") } fi 

image

That was such a game. What I want to do more: try to realize the appearance of objects character by character.

There will have to either abandon the color / effects, or seriously squat. There is an idea to add additional fields to the sprite arrays with control codes for each character.
Maybe I’ll come up with some storyline and who knows what else, but that's another story.

Continued I. BASH'im further

Piu, piu, piu!)

Source: https://habr.com/ru/post/335960/


All Articles