YAPong source
Posted: Tue Jan 23, 2018 9: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
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