YAPong source

The place for any Hollywood tutorials

YAPong source

Postby lazi » Tue Jan 23, 2018 10:22 am

Hi!

Here is the source of a silly Pong remake in Hollywood.
It features:
- needs no external files
- hardware double buffer
- a self containing scroller object
- a self containing starfield object
- on the fly generated menu

Hope someone can find something useful in it. I am open to discuss questions, opinions, suggestions.
Not well commented, but feel free to ask while I am remember :)

Code: Select all
;pong by Lazi 2017
@REQUIRE "glgalore.hwp"
@DISPLAY {mode="windowed", title="YaP", borderless=True, width=640, height=400, nomodeswitch=True, scalemode=#SCALEMODE_AUTO}

;@SAMPLE 1,"pong0.wav"
;@SAMPLE 2,"pong1.wav"
;@SAMPLE 3,"pong2.wav"

Const #color_left = $211549
Const #color_right= $0D3A48
Const #color_ball = $FFFF9D
Const #color_middle = $E4D0C2
Const #color_border = $E4D0C2
Const #color_menutext = $AAF9FF
Const #color_score = $D7D7D7

res={w=640,h=400} ; host resolution to adapt for
display=1
sound=False
sample={1,2}

If Exists("pong0.wav") And Exists("pong1.wav") And Exists("pong2.wav")
   LoadSample(1,"pong0.wav")
   SetVolume(1,10)
   LoadSample(2,"pong1.wav")
   SetVolume(2,10)
   LoadSample(3,"pong2.wav")
   SetVolume(3,10)
   sound=True
EndIf

demo_scroller={}
Function demo_scroller:setup(w,y)
   self.fontinfo=0
   self.text="Nolan Bushnell in 1972 started his own company, Atari. He assigned Allan Alcorn to create a cheap ping pong arcade game as a training exercise, though he did not tell Alcorn that it was for training nor that the idea was based on the Odyssey Table Tennis game. Alcorn soon developed Pong, which Bushnell recognized as a potential hit, and it became the company's first game. The next major succes of that company was the Atari 2600 based on the works of chip designer Jay Miner, but that is another story...                                                   "
   self.text=UpperStr(self.text)
   self.width=20
   self.window=0
   self.length=w
   self.pos=0
   self.y=y
   self.br={}

   self.ascii2path = Function()
                     font_state={name=GetAttribute(#DISPLAY,0,#ATTRFONTNAME),
                              size=GetAttribute(#DISPLAY,0,#ATTRFONTSIZE)}


                       SetFillStyle(#FILLGRADIENT,#LINEAR,#WHITE,#GRAY)
                       SetFormStyle(#ANTIALIAS)
                       SetFont(#MONOSPACE,20)

                       Local f={}
                       Local tbr
                       ; 21 - 7E
                       For Local i=$21 To $ff
                          StartPath(i)
                          MoveTo(i,0,0)
                          AddTextToPath(i,Chr(i))
                          ClosePath(i)
                          ss=TextExtent(Chr(i))
                          InsertItem(f,TextExtent(Chr(i)),i)


                          tbr=CreateBrush(Nil,40,80,#WHITE,{alphachannel=True,clear=True})
                          SelectBrush(tbr)
                          DrawPath(i,#CENTER,ss.height)

                          SelectAlphaChannel(tbr)
                          SetAlphaIntensity(#VANILLACOPY)
                                  DrawPath(i,#CENTER,ss.height)

                          EndSelect
                          self.br[i]=CopyBrush(tbr,Nil,{hardware=True})
                          FreeBrush(tbr)
                        Next
                        SetFont(font_state.name,font_state.size)
                        SetFillStyle(#FILLNONE)
                        SetFormStyle(#NORMAL)

                       Return(f)
                  EndFunction

   self.fontinfo = self.ascii2path()

   self.sintable = {}

   For Local i=0 To 179 self.sintable[i]=Sin(Rad(i))+.5 Next

   self.draw = Function ()
               Local chars, charpos, c, x, y
               chars=Min(self.pos\self.width+1,self.length\self.width+1)
               charpos=self.pos\self.width

               For Local i=charpos-chars To charpos

               If i>=0
                  c=Asc(MidStr(self.text,i,1))
                  x=self.length-self.pos+i*self.width

                  If c<>32 Then DisplayBrush(self.br[c],x,self.y)

               EndIf

               Next
               self.pos=Wrap(self.pos+2,0,self.width*(StrLen(self.text)-1))


            EndFunction
EndFunction
demo_scroller:setup(640,368)


/*startfield*/
Const #hw=True

self={}
self.scale=0.1
starfield=True
Function p_init(num_stars, max_depth)

    self.num_stars=num_stars
    self.max_depth=max_depth

    ;create the starfield
    self.stars={}
    For Local i=0 To self.num_stars
        ;A star is represented as a list with this format: [X,Y,Z]
        star={ x=Rnd(50)-25, y=Rnd(50)-25 , z=Rnd(self.max_depth) }
        InsertItem(self.stars,star)
    Next
    self.k={}
    self.size={}
   self.shade={}
    For z=1 To 320
        self.k[z]={}
        self.size[z]=Int(Max(1,(1-z/self.max_depth)*5))
        self.shade[z]=Int((1-z/self.max_depth)*255)
        For x=-25 To 25
         self.k[z][x]=(2000/z)*x
        Next
    Next

   origin_x=res.w/2
   origin_y=res.h/2

EndFunction

Function p_move_and_draw_stars()

    Local i, x,y,size,shade

    For Local i=0 To self.num_stars

      self.stars[i].z=self.stars[i].z-1

        If self.stars[i].z <=0
            self.stars[i].x=Rnd(50)-25
            self.stars[i].y=Rnd(50)-25
            self.stars[i].z=self.max_depth
        EndIf

        x=self.k[self.stars[i].z][self.stars[i].x]+origin_x
        y=self.k[self.stars[i].z][self.stars[i].y]+origin_y

      If 0 <= x < res.w And 0 <= y < res.h
            size=self.size[self.stars[i].z]
            shade=self.shade[self.stars[i].z]
         Box(x,y,size,size,RGB(shade,shade,shade))
        EndIf
    Next
EndFunction
p_init(100,320)
/*endof starfield*/


ball={}
Function ball:setup()

   self.x=res.w/2-16/2
   self.y=res.h/2-16/2
   self.dir={-4,4}
   self.dx=self.dir[Rnd(2)]
   self.dy=self.dir[Rnd(2)]
   self.br=CreateBrush(Nil,8,8,#color_ball,{hardware=True})
   self.lastpong=-1

   Function self.draw()
            DisplayBrush(self.br,self.x,self.y)
   EndFunction

   Function self.move()
            Local i, count, pong
            pong=-1
            count=ListItems(field.border)
            self.x=self.x+self.dx
            For i=0 To count-1
               If Collision(#BRUSH_VS_BOX,self.br,self.x,self.y, field.border[i][0], field.border[i][1], field.border[i][2], field.border[i][3])
                  If i <> self.lastpong
                     self.dx=self.dx*-1
                     self.x=self.x+self.dx
                     If HaveItem(field.border[i],5) Then   field.border[i][5]()

                                If HaveItem(field.border[i],4)
                           Local dy=Abs( (self.y-field.border[i][1]) \ (field.border[i][3]\5)-2)
                           self.dy=IIf(dy=0,self.dy\2,Limit(self.dy*dy,-6,6))
                           If self.dy=0 Then self.dy=Rnd(4)-2
                        EndIf

                  EndIf
                  pong = i
                  If sound Then PlaySample(sample[0])
                  sample[0],sample[1]=sample[1],sample[0]
                  Break
               EndIf
            Next

            self.y=self.y+self.dy
            For i=0 To count-1
               If Collision(#BRUSH_VS_BOX,self.br,self.x,self.y, field.border[i][0], field.border[i][1], field.border[i][2], field.border[i][3])
                        If i <> self.lastpong
                     self.dy=self.dy*-1
                     self.y=self.y+self.dy
                     If HaveItem(field.border[i],5) Then   field.border[i][5]()

                  EndIf
                  pong=i
                  If sound Then PlaySample(sample[0])
                  sample[0],sample[1]=sample[1],sample[0]
                  Break
               EndIf
            Next
            self.lastpong=pong
   EndFunction

EndFunction

field={}
Function field:setup()
   self.displaylist={}

               ;x,y,w,h,action
   self.border={
               {0,0,res.w,8,Nil},         ;upper
               {0,res.h-8,res.w,8,Nil},     ;bottom
               
               {0-8,0,8,res.h,Nil},         ;left
               {res.w-8+8,0,8,res.h,Nil}      ;right
            }

   self.draw=Function()
            SetFillStyle(#FILLCOLOR)
            For Local y=4 To res.h Step 24 Box(res.w/2-8/2,y,8,8,#color_middle) Next ;middle line
            ForEach(self.border,Function(a,b) Box(b[0],b[1],b[2],b[3],#color_border) EndFunction) ;borders
            ForEach(self.displaylist,Function(a,b) b() EndFunction)
           EndFunction

EndFunction


Function p_keys()
   If IsKeyDown("UP") Then Return("UP",1)
   If IsKeyDown("DOWN") Then Return("DOWN",1)
   Return("",0)
   ;return values - string that contains UP or DOWN and the speed factor 1 is 100% speed
EndFunction


Function p_keys2()
   If IsKeyDown("Q") Then Return("UP",1)
   If IsKeyDown("A") Then Return("DOWN",1)
   Return("",0)
EndFunction

Function p_mouse(y)
   Local my=MouseY()-y
   If Abs(my)>4
   If my<0
       Return("UP",1)
   Else
      Return("DOWN",1)
   EndIf
   EndIf
   Return("",0)
EndFunction



Function p_droid(aim)
   If aim > ball.y Then Return("UP",1)
   If aim < ball.y Then Return("DOWN",1)
   Return("",0)
EndFunction

Function info() EndFunction

player={}
Function player:setup( x, c, sx, goalb )
      self.active=True
      self.points = 0
      self.speed = 4
      self.size = 40
      self.sx=sx
      self.x = x
      self.y = 200-self.size/2
      self.input = c
      self.displayscore = Function()
                         TextOut(self.sx,16,self.points)
                     EndFunction
        SetFont(#SANS,60)
      SetFontStyle(#ANTIALIAS)
      InsertItem(field.displaylist,self.displayscore)

      self.goal = Function()
                  If sound Then PlaySample(3)
                  self.points = self.points+1
                  ball:setup()
                  If self.points=10
                     p1=player1.points
                     p2=player2.points
                     p_demo()
                     player1.points=p1
                     player2.points=p2
                     menu.on=True
                  EndIf



               EndFunction
      If Not IsNil(goalb) Then field.border[goalb][5] = self.goal

      self.control = Function()
                     Switch self.input(self.y+self.size/2)
                     Case "UP":
                        self.up()
                     Case "DOWN":
                        self.down()
                     EndSwitch
                     EndFunction
      self.pos = ListItems(field.border)
      InsertItem(field.border,{self.x,self.y,8,self.size,self.y,Nil},self.pos) ;adds the paddle as a border

      self.update = Function()
                        field.border[self.pos]={self.x,self.y,8,self.size,field.border[self.pos][1],Nil} ;new x, new y, width, height, last y to calculate speed, goal function
                 EndFunction

      self.up = Function() If self.y > 8 Then self.y=self.y-self.speed self.update EndFunction
      self.down = Function() If self.y < res.h-8-self.size Then self.y=self.y+self.speed self.update EndFunction

EndFunction



menu={}
Function menu:setup()
   self.on = True
   self.highlight=0
   self.submenu=0
   self.bgbrush=0
   self.brushstack={}

   self.items={{{sel=0,br={0,0,0},brsel={0,0,0},txt={"Yet Another Pong","(c) 2017 by Lazi","www.kezdobetu.hu/software"},action={Function() self.nextstep()  EndFunction,Function() self.nextstep() EndFunction,Function() self.nextstep() EndFunction}},
            {sel=0,br={0,0,0},brsel={0,0,0},txt={"Play"},action={Function()
                                                menu.on=False
                                                field:setup()
                                                                SetVolume(1,10)
                                                SetVolume(2,10)
                                                SetVolume(3,10)

                                                Local p1c=0
                                                Local p2c=0

                                                Switch self.items[2][1].sel
                                                Case 0: ;keys
                                                   p1c=p_keys
                                                Case 1: ;mouse
                                                   p1c=p_mouse
                                                Case 2: ;keyboard
                                                   p1c=p_keys2
                                                Case 3: ;touch
                                                   p1c=p_touch
                                                 EndSwitch

                                                Switch self.items[2][2].sel
                                                Case 0: ;keys
                                                   p2c=p_keys
                                                Case 1: ;mouse
                                                   p2c=p_mouse
                                                Case 2: ;keyboard
                                                   p2c=p_keys2
                                                Case 3: ;touch
                                                   p2c=p_touch
                                                 EndSwitch



                                                Switch self.items[0][2].txt[self.items[0][2].sel]
                                                Case "Game: 1 Player":
                                                                    field.border={
                                                           {0,0,res.w,8,Nil},         ;upper
                                                           {0,res.h-8,res.w,8,Nil},     ;bottom

                                                           {0-8,0,8,res.h,Nil},         ;left
                                                           {res.w-8+8,0,8,res.h,Nil}      ;right
                                                            }
                                                   player1:setup(32,p1c,#CENTER-40,3)            ;player controlled by keyboard
                                                   player1.speed=4
                                                   player2:setup(res.w-32-8,p_droid,#CENTER+40,2)     ;NPC
                                                   player2.speed=3                           ;NPC speed is less than the ball, so it can miss

                                                Case "Game: 1 Player with wall":
                                                                    field.border={
                                                           {0,0,res.w,8,Nil},         ;upper
                                                           {0,res.h-8,res.w,8,Nil},     ;bottom

                                                           {0-8,0,8,res.h,Nil},         ;left
                                                           {res.w-8,0,8,res.h,Nil}      ;right
                                                            }
                                                   player1:setup(32,p1c,#CENTER-40,2)            ;player controlled by keyboard
                                                   player1.speed=4
                                                   player2:setup(res.w,p_droid,#CENTER+40,2)     ;NPC
                                                   player2.active=False

                                                Case "Game: 2 Players":
                                                                    field.border={
                                                           {0,0,res.w,8,Nil},         ;upper
                                                           {0,res.h-8,res.w,8,Nil},     ;bottom

                                                           {0-8,0,8,res.h,Nil},         ;left
                                                           {res.w-8+8,0,8,res.h,Nil}      ;right
                                                            }

                                                   player1:setup(32,p1c,#CENTER-40,3)            ;player controlled by keyboard
                                                   player1.speed=4
                                                   player2:setup(res.w-32-8,p2c,#CENTER+40,2)
                                                   player2.speed=4
                                                                  
                                                EndSwitch
 
                                                                ball:setup()
 
                                                EndFunction}},

            {sel=0,br={0,0,0},brsel={0,0,0},txt={"Game: 1 Player", "Game: 1 Player with wall", "Game: 2 Players" },
                                                action={
                                                Function() self.nextstep() EndFunction,
                                                Function() self.nextstep() EndFunction,
                                                Function() self.nextstep() EndFunction}},

            {sel=0,br={0},brsel={0},txt={"Controls"},action={Function() self.submenu=2 self.highlight=0 EndFunction}},
            {sel=0,br={0},brsel={0},txt={"Settings"},action={Function() self.submenu=1 self.highlight=0 EndFunction}},
            {sel=0,br={0},brsel={0},txt={"Exit"},action={Function() End EndFunction}}
                                                
                                                },
            {{sel=0,br={0},brsel={0},txt={"Settings"},action={Function() EndFunction}},
            {sel=0,br={0},brsel={0},txt={"Starfield on/off"},action={Function() starfield=Not starfield EndFunction}},
            {sel=0,br={0},brsel={0},txt={"Sound on/off"},action={Function() sound=Not sound EndFunction}},
            {sel=0,br={0},brsel={0},txt={"Display mode switch"},action={Function()
                                                EndDoubleBuffer()
                                                Local old_display=display
                                                display=CreateDisplay(Nil, {mode= IIf(GetAttribute(#DISPLAY,old_display,#ATTRMODE)=#DISPMODE_WINDOWED, "fullscreen","windowed"), title="YaP", borderless=True, width=res.w, Height=res.h, nomodeswitch=True} )
                                                CloseDisplay(old_display)
                                                OpenDisplay(display)
                                                SelectDisplay(display)
                                                FreeDisplay(old_display)
                                                demo_scroller:setup(640,368)
                                                p_init()
                                                p_demo()

                                               EndFunction}},
            {sel=0,br={0},brsel={0},txt={"Back"},action={Function() self.submenu=0 self.highlight=0 EndFunction}}
                                    },
            {{sel=0,br={0},brsel={0},txt={"Controls"},action={Function() EndFunction}},
            {sel=0,br={0},brsel={0},txt={"Player 1 - cursor keys", "Player 1 - mouse", "Player 1 - keyboard (Q,A)", "Player 1 - touch"},action={Function() self.nextstep() self.cs() EndFunction,Function() self.nextstep() self.cs() EndFunction,Function() self.nextstep() self.cs() EndFunction,Function() self.nextstep() self.cs() EndFunction}},
            {sel=1,br={0},brsel={0},txt={"Player 2 - cursor keys", "Player 2 - mouse", "Player 2 - keyboard (Q,A)", "Player 2 - touch"},action={Function() self.nextstep() self.cs() EndFunction,Function() self.nextstep() self.cs() EndFunction,Function() self.nextstep() self.cs() EndFunction,Function() self.nextstep() self.cs() EndFunction}},
            {sel=0,br={0},brsel={0},txt={"Back"},action={Function() self.submenu=0 self.highlight=0 EndFunction}},

            }
            }

   self.nextstep=Function()
               Local n=ListItems(self.items[self.submenu][self.highlight].txt)
               self.items[self.submenu][self.highlight].sel=Wrap(self.items[self.submenu][self.highlight].sel+1,0,n)
              EndFunction

   self.cs = Function()
               If self.items[2][1].sel=self.items[2][2].sel Then self.nextstep()
             EndFunction
   
   self.create=Function()
                    Local t,tb
                    /*
                    ;create half opaque canvas behind the menu
                    tb=CreateBrush (Nil,GetAttribute(#DISPLAY,0,#ATTRWIDTH),GetAttribute(#DISPLAY,0,#ATTRHEIGHT),$300060,{alphachannel=True,hardware=False})
                    SelectAlphaChannel(tb)
                    SetAlphaIntensity(100)
                    Cls
                    EndSelect
                    self.bgbrush=CopyBrush(tb,Nil,{hardware=True})
                    FreeBrush(tb)
                    InsertItem(self.brushstack,self.bgbrush)
                    */

                    SetFont(#SANS,30)
                    SetFontColor(#color_menutext)
                    SetFontStyle(#ANTIALIAS)
                    For Local s=0 To ListItems(self.items)-1
                    For Local i=0 To ListItems(self.items[s])-1
                    For Local j=0 To ListItems(self.items[s][i].txt)-1
                       SetFontStyle(#EDGE,#BLACK,5)
                       t=CreateTextObject(Nil,self.items[s][i].txt[j],{color=#color_menutext})
                       tb=ConvertToBrush(#TEXTOBJECT,t,Nil)
                         FreeTextObject(t)
                       t=CopyBrush(tb,Nil,{hardware=True})
                       self.items[s][i].br[j]=t
                       InsertItem(self.brushstack,t)
                       FreeBrush(tb)


                       SetFontStyle(#EDGE,#WHITE,5)
                       t=CreateTextObject(Nil,self.items[s][i].txt[j],{color=#BLACK})
                       tb=ConvertToBrush(#TEXTOBJECT,t,Nil)
                         FreeTextObject(t)
                       t=CopyBrush(tb,Nil,{hardware=True})
                       self.items[s][i].brsel[j]=t
                         InsertItem(self.brushstack,t)
                       FreeBrush(tb)

                    Next
                    Next
                    Next
               EndFunction

   self.display=Function()
                  ;DisplayBrush(self.bgbrush,0,0)
                  Local n
                  For Local i=0 To ListItems(self.items[self.submenu])-1
                    n=self.items[self.submenu][i].sel
                    DisplayBrush( IIf (self.highlight=i, self.items[self.submenu][i].brsel[n], self.items[self.submenu][i].br[n]) ,#CENTER,100+i*30)
                  Next
              EndFunction

   self.create()
EndFunction

Function p_mainloop()
   If starfield Then p_move_and_draw_stars
   player1.control()
   If player2.active Then player2.control()
   field.draw()
   ball.move()
   ball.draw()
EndFunction

Function p_events(msg)
   Switch msg.action
   Case "OnKeyDown":
      Switch msg.key
      Case "ESC":
         Switch menu.on
         Case False:
            menu.on=True
            p_demo()
         Case True:
            menu.on=False
         EndSwitch
      Case "RIGHT":
         If menu.on
            Local n=ListItems(menu.items[menu.submenu][menu.highlight].txt)
            menu.items[menu.submenu][menu.highlight].sel=Wrap(menu.items[menu.submenu][menu.highlight].sel+1,0,n)
            If menu.submenu=2 And (menu.highlight=1 Or menu.highlight=2) Then menu.cs()
         EndIf
      Case "LEFT":
         If menu.on
            Local n=ListItems(menu.items[menu.submenu][menu.highlight].txt)
            menu.items[menu.submenu][menu.highlight].sel=Wrap(menu.items[menu.submenu][menu.highlight].sel-1,0,n)
            If menu.submenu=2 And (menu.highlight=1 Or menu.highlight=2) Then menu.cs()
         EndIf

      Case "DOWN":
         If menu.on Then   menu.highlight=Wrap(menu.highlight+1,0,ListItems(menu.items[menu.submenu]))
      Case "UP":
         If menu.on Then   menu.highlight=Wrap(menu.highlight-1,0,ListItems(menu.items[menu.submenu]))
      Case "\n":
         If menu.on Then menu.items[menu.submenu][menu.highlight].action[menu.items[menu.submenu][menu.highlight].sel]()
      EndSwitch
   EndSwitch
EndFunction

 
Function p_demo()
   field.border={
               {0,0,res.w,8,Nil},         ;upper
               {0,res.h-8,res.w,8,Nil},     ;bottom

               {0,0,8,res.h,Nil},         ;left
               {res.w-8,0,8,res.h,Nil}      ;right
            }
   player1:setup(32,p_droid,#CENTER-40)            ;player controlled by keyboard
   player1.speed=2
   player2:setup(res.w-32-8,p_droid,#CENTER+40)     ;NPC
   player2.speed=3
   SetVolume(1,1)
   SetVolume(2,1)
   SetVolume(3,1)
EndFunction

Function p_init()
   HidePointer()
   BeginDoubleBuffer(True)
   Cls
   Flip
   Cls
   ball:setup()
   field:setup()
   menu:setup()
   InstallEventHandler ({OnKeyDown=p_events})
EndFunction

p_init()

    player1=CopyTable(player)
   player2=CopyTable(player)

p_demo()

Repeat
       CheckEvent
       DisableLineHook()
         Box (0,0,320,400,#color_left)
        Box (320,0,320,400,#color_right)
       If menu.on Then demo_scroller.draw()
       p_mainloop()
       info()
       If menu.on Then menu.display()
       EnableLineHook()
       Flip()
Forever
         
User avatar
lazi
 
Posts: 282
Joined: Fri Feb 25, 2011 12:08 am

Return to Tutorials

Who is online

Users browsing this forum: No registered users and 1 guest