Page 1 of 1

ascii hero (jump'n'run)

Posted: Sun Sep 26, 2010 6:24 pm
by #NULL
this weekend i wondered if i could make something jump'n'run-like to play within the console/terminal, and what i hacked togehter is quite funny to look at, so i will share it here.
i'm making use of the pc-speaker (beep), so if you have one and your boss is around then better set the constant '#no_sound' to 1.
for keys see the comments in the beginning of the code.

Code: Select all

; >>>>>>>  ascii hero  <<<<<<<<
;
; keys:
;          q : quit
;      cntrl : shoot
; left/right : move
;         up : jump
; items:
;   gun         : , (comma)
;   laser rifle : _ (underscore)




EnableExplicit

#no_sound = 0


#jump_init = 0.9
#jump_fall = 0.09

Structure s_hero
  x.f
  y.f
  look.l
  isjumping.l
  jump.f
  weapon.l
  energy.f
  x_prev.l
  y_prev.l
  sprite.s
EndStructure

Structure s_shot
  x.f
  y.f
  dx.f
  dy.f
  c.c
  byhero.l
  x_prev.l
  y_prev.l
EndStructure

Structure s_enemy
  x.f
  y.f
  dx.f
  dy.f
  c.c
  firewait.l
  energy.f
  x_prev.l
  y_prev.l
EndStructure

Global hero.s_hero
Global gun_x_prev
Global gun_y_prev
Global NewList enemy.s_enemy()
Global NewList shot.s_shot()
Global xmax,ymax
Global Dim levelmap.c(0,0)
Global redraw=#True
Global redraw_timer
Global quit

Declare shoot(x.f, y.f, dx.f, dy.f, c.c, byhero)

Macro r(f)
  Int(Round(f,#PB_Round_Nearest))
EndMacro

Procedure drawSprite(*dat, x, y)
  Protected c.c
  ConsoleLocate(x,y)
  Repeat
    c = PeekC(*dat)
    Select c 
      Case #LF  ::  Break
      Case 0    ::  y+1 : ConsoleLocate(x,y)
      ;Case 32   ::  x+1 : ConsoleLocate(x,y)
      Default   ::  Print(Chr(c))
    EndSelect
    *dat + SizeOf(Character)
  ForEver
EndProcedure

Procedure loadlevel(*dat)
  Protected c.c
  Protected x
  xmax = Len(PeekS(*dat))-1
  ;Debug xmax
  ;CallDebugger
  ymax = 0
  Dim levelmap(xmax,ymax)
  Repeat
    c.c = PeekC(*dat)
    Select c 
      Case #LF
        Break
      Case 0
        x=-1
        ymax+1
        ReDim levelmap(xmax,ymax)
      Case '='
        AddElement(enemy())
        enemy()\x = x
        enemy()\y = ymax
        enemy()\dx = 0.2 - 0.2*2*Random(1)
        enemy()\dy = 0.2 - 0.2*2*Random(1)
        enemy()\c = c
        enemy()\energy = 2
        levelmap(x,ymax) = 32
      Default
        levelmap(x,ymax) = c
    EndSelect
    *dat + SizeOf(Character)
    x + 1
  ForEver
EndProcedure

Procedure drawlevel()
  Protected x,y
  For x=0 To xmax
    For y=1 To ymax
      ConsoleLocate(x,y)
      Print(Chr(levelmap(x,y)))
    Next
  Next
EndProcedure

Procedure drawtxt(x,y,txt.s)
  Protected lines,n
  lines = CountString(txt,#LF$)+1
  For n=1 To lines
    ConsoleLocate(x,y)
    Print(StringField(txt,n,#LF$))
    y+1
  Next
EndProcedure

Procedure drawlevelxy(x,y)
  If x>=0 And x<= xmax And y>=0 And y<=ymax
    drawtxt(x,y,Chr(levelmap(x,y)))
  EndIf
EndProcedure

Procedure issolid(x,y)
  If x>=0 And x<=xmax And y>=0 And y<=ymax
    If Chr(levelmap(x,y)) = "#"
      ProcedureReturn #True
    EndIf
  EndIf
  ProcedureReturn #False
EndProcedure

Procedure jump()
  Protected y_prev
  If hero\isjumping
    If hero\jump > -0.5
      hero\jump - #jump_fall
    EndIf
    If hero\jump>0 And issolid(hero\x, hero\y-hero\jump)
      hero\jump = 0
    ElseIf hero\jump<0 And issolid(hero\x, hero\y+1-hero\jump)
      hero\isjumping = #False
    Else  
      y_prev = hero\y
      hero\y - hero\jump
      If r(hero\y) <> y_prev
        redraw=#True
      EndIf
    EndIf
  EndIf
EndProcedure

Procedure bullets()
  Protected x,y,cont
  ForEach shot()
    ; impacts
    If Not shot()\byhero And r(shot()\x) = r(hero\x) And (r(shot()\y) = r(hero\y) Or r(shot()\y) = r(hero\y)+1)
      Select shot()\c
        Case '.'
          hero\energy - 1
          drawlevelxy(shot()\x_prev,shot()\y_prev)
          drawlevelxy(shot()\x,shot()\y)
          DeleteElement(shot())
          Continue
      EndSelect  
    EndIf
    cont=#False
    ForEach enemy()
      If shot()\byhero And r(shot()\x) = r(enemy()\x) And r(shot()\y) = r(enemy()\y)
        Select shot()\c
          Case '.'
            enemy()\energy - 0.7
            drawlevelxy(shot()\x_prev,shot()\y_prev)
            drawlevelxy(shot()\x,shot()\y)
            DeleteElement(shot())
            cont=#True
            Break
          Case '_'
            enemy()\energy - 2
            drawlevelxy(shot()\x_prev,shot()\y_prev)
            drawlevelxy(shot()\x,shot()\y)
            DeleteElement(shot())
            cont=#True
            Break
        EndSelect  
      EndIf
    Next
    If cont
      Continue
    EndIf
    
    ; move
    x = shot()\x
    y = shot()\y
    shot()\x + shot()\dx
    shot()\y + shot()\dy
    If issolid(shot()\x, shot()\y)
      drawlevelxy(shot()\x_prev,shot()\y_prev)
      drawlevelxy(shot()\x,shot()\y)
      DeleteElement(shot())
      redraw=#True
    ElseIf shot()\x<0 Or shot()\y<0 Or shot()\x>xmax Or shot()\y>ymax
      drawlevelxy(shot()\x_prev,shot()\y_prev)
      drawlevelxy(shot()\x,shot()\y)
      DeleteElement(shot())
      redraw=#True
    ElseIf Not redraw
      If r(shot()\x) <> x  Or  r(shot()\y) <> y
        redraw=#True
      EndIf
    EndIf
    ;redraw=#True
  Next
EndProcedure

Procedure lineofsight(x,x2,y)
  If x>x2
    Swap x,x2
  EndIf
  For x=x To x2
    If issolid(x,y)
      ProcedureReturn #False
    EndIf
  Next
  ProcedureReturn #True
EndProcedure

Procedure enemies()
  Protected x,y,sight
  ForEach enemy()
    ; die
    If enemy()\energy<=0
      drawlevelxy(enemy()\x_prev,enemy()\y_prev)
      drawlevelxy(enemy()\x,enemy()\y)
      DeleteElement(enemy())
      Continue
    EndIf
    
    ; shoot
    If enemy()\firewait<1
      If (r(enemy()\y) = r(hero\y) And lineofsight(enemy()\x,hero\x,hero\y))   Or   (r(enemy()\y) = r(hero\y)+1 And lineofsight(enemy()\x,hero\x,hero\y))
        If enemy()\x > hero\x
          shoot(enemy()\x, enemy()\y, -0.9, 0, Asc("."), #False)
        ElseIf enemy()\x < hero\x
          shoot(enemy()\x, enemy()\y,  0.9, 0, Asc("."), #False)
        EndIf
        enemy()\firewait = 30
      EndIf
    EndIf
    If enemy()\firewait
      enemy()\firewait - 1
    EndIf
    
    ; move
    x = enemy()\x
    y = enemy()\y
    ;enemy()\x -0.5 +Random(100)/100.0
    ;enemy()\y -0.5 +Random(100)/100.0
    If Random(3)=0 : enemy()\x -1 +Random(2) : EndIf
    If Random(3)=0 : enemy()\y -1 +Random(2) : EndIf
    
    If issolid(enemy()\x, enemy()\y)
      enemy()\x = x
      enemy()\y = y
    ElseIf Not redraw
      If r(enemy()\x) <> x  Or  r(enemy()\y) <> y
        redraw=#True
      EndIf
    EndIf
    ;redraw=#True
  Next
EndProcedure

Procedure draw()
  ;ClearConsole()
  ;drawlevel()
  drawtxt(1,0,"HEALTH "+LSet( Left("|||||||||||||||||||||||||||",hero\energy), 10,"."))
  
  
  ; delete
  ForEach enemy()
    drawlevelxy(enemy()\x_prev, enemy()\y_prev)
  Next
  drawlevelxy(hero\x_prev,hero\y_prev)
  drawlevelxy(hero\x_prev,hero\y_prev+1)
  drawlevelxy(gun_x_prev,gun_y_prev)
  ForEach shot()
    drawlevelxy(shot()\x_prev, shot()\y_prev)
  Next
  
  
  ; redraw
  ForEach enemy()
    drawtxt(enemy()\x, enemy()\y, Chr(enemy()\c))
  Next
  drawtxt(hero\x,hero\y,hero\sprite)
  
  If Not issolid(hero\x+hero\look, hero\y)
    If hero\weapon
      drawtxt(hero\x+hero\look, hero\y, "_")
    EndIf
  EndIf
  
  ForEach shot()
    drawtxt(shot()\x, shot()\y, Chr(shot()\c))
  Next
EndProcedure

Procedure sound(nr)
  CompilerIf #no_sound
    ;
  CompilerElse
  
  Protected i.l
  
  Select nr
  Case 0; pick up
    For i=0 To 3
      Beep_(1000+i*200,8)
    Next
  Case 1; shoot rifle
    For i=0 To 8
      Beep_(1000+Random(10)*50-i*80,4)
    Next
  Case 2; shoot laser
    For i=8 To 0 Step -1
      Beep_(1000+i*120,6)
    Next
  Case 99; game over
    Beep_( 234, 200 )
    Beep_( 211, 200 )
    Beep_( 187, 200 )
    Beep_( 176, 200 )
    Beep_( 187, 400 )
    Beep_( 158, 400 )
    Beep_( 79, 400 )
  Case 100; victory
    Beep_( 396, 83 )
    Beep_( 440, 83 )
    Beep_( 469, 83 )
    Beep_( 528, 83 )
    Beep_( 594, 166 )
    Beep_( 792, 166 )
    Beep_( 594, 166 )
    Beep_( 792, 166 )
    Beep_( 1584, 249 )
  EndSelect
  
  CompilerEndIf
EndProcedure

Procedure shoot(x.f, y.f, dx.f, dy.f, c.c, byhero)
  AddElement(shot())
  shot()\x = x
  shot()\y = y
  shot()\dx = dx
  shot()\dy = dy
  shot()\c = c
  shot()\byhero = byhero
  Select Chr(c)
    Case "." :: sound(1)
    Case "_" :: sound(2)
  EndSelect
EndProcedure

Procedure getprevious()
  hero\x_prev = r(hero\x)
  hero\y_prev = r(hero\y)
  gun_x_prev = hero\x + hero\look
  gun_y_prev = hero\y
  ForEach enemy()
    enemy()\x_prev = r(enemy()\x)
    enemy()\y_prev = r(enemy()\y)
  Next
  ForEach shot()
    shot()\x_prev = r(shot()\x)
    shot()\y_prev = r(shot()\y)
  Next
EndProcedure



OpenConsole()
; If 0 ; print ascii table
;   Define i
;   For i=30 To 256 : Print(Chr(i)) : Next
;   For i=0 To 256 : PrintN(Str(i)+" "+Chr(i)) : Next
;   Input()
;   End
; EndIf
EnableGraphicalConsole(1)
ConsoleCursor(0)
loadlevel(?map01)


hero\x=4
hero\y=16
hero\isjumping=#True
hero\look=1
hero\energy=10
hero\sprite = "o"+#LF$
hero\sprite + "A"
;hero\weapon=2

ClearConsole()
drawlevel()

Repeat
  
  Inkey()
  If RawKey()
    Select RawKey()
      Case 37 ;{ left
        hero\look = -1
        If Not issolid(hero\x-1,hero\y) And Not issolid(hero\x-1,hero\y+1)
          hero\x-1
          If Not issolid(hero\x,hero\y+2)
            hero\isjumping = #True
          EndIf
          redraw=#True
        EndIf
        ;}
      Case 39 ;{ right
        hero\look = 1
        If Not issolid(hero\x+1,hero\y) And Not issolid(hero\x+1,hero\y+1)
          hero\x+1
          If Not issolid(hero\x,hero\y+2)
            hero\isjumping = #True
          EndIf
          redraw=#True
        EndIf
        ;}
      Case 38 ;{ up
        If Not hero\isjumping
          hero\isjumping = #True
          hero\jump = #jump_init
        EndIf
        ;}
      Case 17 ;{ shoot
        Select hero\weapon
          Case 1 :: shoot(hero\x+hero\look, hero\y, 0.9 * hero\look, 0, Asc("."), #True)
          Case 2 :: shoot(hero\x+hero\look, hero\y, 0.9 * hero\look, 0, Asc("_"), #True)
        EndSelect
        ;}
      Case Asc("Q") ;quit
        quit = #True
    EndSelect
  EndIf
  
  Select Chr(levelmap(Int(hero\x),Int(hero\y+1)))
    Case ","
      sound(0)
      hero\weapon = 1
      levelmap(Int(hero\x),Int(hero\y+1)) = 32
    Case "_"
      sound(0)
      hero\weapon = 2
      levelmap(Int(hero\x),Int(hero\y+1)) = 32
  EndSelect
  enemies()
  jump()
  bullets()
  draw()
  Delay(50)
  getprevious()
  
  If hero\energy<=0
    drawtxt(32,12," ----------- "+#LF$+"| GAME OVER |"+#LF$+" ----------- ")
    sound(99)
    Delay(1000)
    End
  EndIf
  
  If ListSize(enemy())=0
    drawtxt(32,12," ----------- "+#LF$+"|  VICTORY  |"+#LF$+" ----------- ")
    sound(100)
    Delay(1000)
    End
  EndIf
Until quit




DataSection
  map01:
  Data.s "                                                                                "
  Data.s " ############################################################################## "
  Data.s " #                                                                            # "
  Data.s " #                                   =                                        # "
  Data.s " #    =                                            ##         ##              # "
  Data.s " #     =                                                                      # "
  Data.s " #                                                   ##     ##     =          # "
  Data.s " #                                                    ##   ##                 # "
  Data.s " #                                                                            # "
  Data.s " #                                                       _                    # "
  Data.s " #                                                   ########    =            # "
  Data.s " #                                                                            # "
  Data.s " #                                           ######                           # "
  Data.s " #########    ################    #########                                   # "
  Data.s " #                                                                            # "
  Data.s " # =                           ##                                             # "
  Data.s " #         ##                          =                          =           # "
  Data.s " #                                                                            # "
  Data.s " #                                                                            # "
  Data.s " #                    ,       ####       =                                    # "
  Data.s " #        ####   ##########   ####                                            # "
  Data.s " #        ####                ####                                            # "
  Data.s " #        ####                ####                                            # "
  Data.s " ############################################################################## "
  Data.s #LF$
  map01end:
EndDataSection





Re: ascii hero (jump'n'run)

Posted: Sun Sep 26, 2010 6:36 pm
by Fluid Byte
Wow, oldskool! I think it's awesome :mrgreen:

Re: ascii hero (jump'n'run)

Posted: Sun Sep 26, 2010 6:46 pm
by nco2k
good old times. :)

c ya,
nco2k

Re: ascii hero (jump'n'run)

Posted: Sun Sep 26, 2010 7:52 pm
by djes
Great!!! :mrgreen:

Re: ascii hero (jump'n'run)

Posted: Sun Sep 26, 2010 8:38 pm
by idle
that's great :D

Re: ascii hero (jump'n'run)

Posted: Sun Sep 26, 2010 10:26 pm
by luis
Nice :)

I like the textures.

Re: ascii hero (jump'n'run)

Posted: Mon Sep 27, 2010 7:25 am
by Rings
awesome !!!

Re: ascii hero (jump'n'run)

Posted: Mon Sep 27, 2010 8:02 am
by #NULL
:D many thanks

i wasn't sure how it will work on other systems (Windows XP here). if the console window is too small it won't be displayed correctly for example. and if i would use very special characters, could i run into problems with different ascii-tables used on some systems?
i had a quick try on linux (gnome terminal iirc) but ClearConsole() and ConsoleLocate() seemed to work different there, so there were just characters printed out continuously making the terminal window scrolling down - looked like the matrix.

Re: ascii hero (jump'n'run)

Posted: Fri Feb 25, 2011 8:50 am
by flaith
Oldschool powaaaaa, awesome :D

Re: ascii hero (jump'n'run)

Posted: Fri Feb 25, 2011 3:49 pm
by Kwai chang caine
Very very coooooll !!!
Thanks 8)

Re: ascii hero (jump'n'run)

Posted: Fri Feb 25, 2011 8:07 pm
by Zach
Very neat :)